summaryrefslogtreecommitdiffstats
path: root/comm/suite
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/suite
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/suite')
-rw-r--r--comm/suite/Makefile.in25
-rw-r--r--comm/suite/README10
-rw-r--r--comm/suite/app.mozbuild13
-rw-r--r--comm/suite/app/Makefile.in143
-rw-r--r--comm/suite/app/blocklist.xml5225
-rw-r--r--comm/suite/app/icons/moz.build21
-rw-r--r--comm/suite/app/icons/windows/calendar-alarm-dialog.icobin0 -> 7886 bytes
-rw-r--r--comm/suite/app/icons/windows/calendar-event-dialog.icobin0 -> 7886 bytes
-rw-r--r--comm/suite/app/icons/windows/calendar-event-summary-dialog.icobin0 -> 7886 bytes
-rw-r--r--comm/suite/app/icons/windows/calendar-task-dialog.icobin0 -> 7886 bytes
-rw-r--r--comm/suite/app/icons/windows/calendar-task-summary-dialog.icobin0 -> 7886 bytes
-rw-r--r--comm/suite/app/macbuild/Contents/Info.plist.in275
-rw-r--r--comm/suite/app/macbuild/Contents/MacOS-files-copy.in11
-rw-r--r--comm/suite/app/macbuild/Contents/MacOS-files.in8
-rw-r--r--comm/suite/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in7
-rw-r--r--comm/suite/app/module.ver8
-rw-r--r--comm/suite/app/moz.build129
-rw-r--r--comm/suite/app/nsSuiteApp.cpp335
-rw-r--r--comm/suite/app/permissions10
-rw-r--r--comm/suite/app/profile/channel-prefs.js5
-rw-r--r--comm/suite/app/profile/suite-prefs.js1500
-rw-r--r--comm/suite/app/seamonkey.exe.manifest46
-rw-r--r--comm/suite/app/seamonkey.man.in160
-rw-r--r--comm/suite/app/splash.rc23
-rw-r--r--comm/suite/base/content/about.js46
-rw-r--r--comm/suite/base/content/about.xhtml43
-rw-r--r--comm/suite/base/content/aboutLife.xhtml72
-rw-r--r--comm/suite/base/content/aboutPrivateBrowsing.css12
-rw-r--r--comm/suite/base/content/aboutPrivateBrowsing.js33
-rw-r--r--comm/suite/base/content/aboutPrivateBrowsing.xul62
-rw-r--r--comm/suite/base/content/askViewZoom.js43
-rw-r--r--comm/suite/base/content/askViewZoom.xul27
-rw-r--r--comm/suite/base/content/blockedSite.js126
-rw-r--r--comm/suite/base/content/blockedSite.xhtml77
-rw-r--r--comm/suite/base/content/certError.css7
-rw-r--r--comm/suite/base/content/certError.js181
-rw-r--r--comm/suite/base/content/certError.xhtml164
-rw-r--r--comm/suite/base/content/certError.xml18
-rw-r--r--comm/suite/base/content/charsetOverlay.xul24
-rw-r--r--comm/suite/base/content/communicator.css329
-rw-r--r--comm/suite/base/content/contentAreaClick.js260
-rw-r--r--comm/suite/base/content/contentAreaContextOverlay.xul401
-rw-r--r--comm/suite/base/content/defaultClientDialog.js58
-rw-r--r--comm/suite/base/content/defaultClientDialog.xul37
-rw-r--r--comm/suite/base/content/extensionsOverlay.css7
-rw-r--r--comm/suite/base/content/findUtils.js141
-rw-r--r--comm/suite/base/content/fullscreen-video.xhtml156
-rw-r--r--comm/suite/base/content/gopherAddon.xhtml55
-rw-r--r--comm/suite/base/content/helpEditorOverlay.xul54
-rw-r--r--comm/suite/base/content/helpMessengerOverlay.xul57
-rw-r--r--comm/suite/base/content/helpSecurityOverlay.xul137
-rw-r--r--comm/suite/base/content/nsContextMenu.js1676
-rw-r--r--comm/suite/base/content/openLocation.js125
-rw-r--r--comm/suite/base/content/openLocation.xul74
-rw-r--r--comm/suite/base/content/overrides/app-license.html8
-rw-r--r--comm/suite/base/content/safeMode.js92
-rw-r--r--comm/suite/base/content/safeMode.xul58
-rw-r--r--comm/suite/base/content/tasksOverlay.js276
-rw-r--r--comm/suite/base/content/tasksOverlay.xul124
-rw-r--r--comm/suite/base/content/utilityOverlay.js1969
-rw-r--r--comm/suite/base/content/utilityOverlay.xul719
-rw-r--r--comm/suite/base/content/viewApplyThemeOverlay.js170
-rw-r--r--comm/suite/base/content/viewApplyThemeOverlay.xul34
-rw-r--r--comm/suite/base/content/viewSourceOverlay.js32
-rw-r--r--comm/suite/base/content/viewSourceOverlay.xul81
-rw-r--r--comm/suite/base/content/viewZoomOverlay.js479
-rw-r--r--comm/suite/base/content/viewZoomOverlay.xul55
-rw-r--r--comm/suite/base/jar.mn96
-rw-r--r--comm/suite/base/moz.build17
-rw-r--r--comm/suite/branding/branding-common.mozbuild70
-rw-r--r--comm/suite/branding/seamonkey/background.pngbin0 -> 24741 bytes
-rw-r--r--comm/suite/branding/seamonkey/branding.nsi15
-rw-r--r--comm/suite/branding/seamonkey/content/about.pngbin0 -> 31651 bytes
-rw-r--r--comm/suite/branding/seamonkey/content/aboutRights.xhtml107
-rw-r--r--comm/suite/branding/seamonkey/content/jar.mn16
-rw-r--r--comm/suite/branding/seamonkey/content/messenger-start-bg.pngbin0 -> 206758 bytes
-rw-r--r--comm/suite/branding/seamonkey/content/messenger-start-hdr.pngbin0 -> 1357 bytes
-rw-r--r--comm/suite/branding/seamonkey/content/moz.build7
-rw-r--r--comm/suite/branding/seamonkey/default128.pngbin0 -> 13891 bytes
-rw-r--r--comm/suite/branding/seamonkey/default16.pngbin0 -> 810 bytes
-rw-r--r--comm/suite/branding/seamonkey/default22.pngbin0 -> 1380 bytes
-rw-r--r--comm/suite/branding/seamonkey/default24.pngbin0 -> 1561 bytes
-rw-r--r--comm/suite/branding/seamonkey/default256.pngbin0 -> 33361 bytes
-rw-r--r--comm/suite/branding/seamonkey/default32.pngbin0 -> 2035 bytes
-rw-r--r--comm/suite/branding/seamonkey/default48.pngbin0 -> 4335 bytes
-rw-r--r--comm/suite/branding/seamonkey/default64.pngbin0 -> 7583 bytes
-rw-r--r--comm/suite/branding/seamonkey/disk.icnsbin0 -> 180808 bytes
-rw-r--r--comm/suite/branding/seamonkey/document.icnsbin0 -> 200361 bytes
-rw-r--r--comm/suite/branding/seamonkey/dsstorebin0 -> 15364 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow.pngbin0 -> 1951 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow16.pngbin0 -> 884 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow48.pngbin0 -> 3103 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/abcardWindow.pngbin0 -> 1885 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/abcardWindow16.pngbin0 -> 858 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/abcardWindow48.pngbin0 -> 3010 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/ablistWindow.pngbin0 -> 1865 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/ablistWindow16.pngbin0 -> 843 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/ablistWindow48.pngbin0 -> 2688 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/addressbookWindow.pngbin0 -> 1724 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/addressbookWindow16.pngbin0 -> 837 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/addressbookWindow48.pngbin0 -> 2657 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties.pngbin0 -> 2104 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties16.pngbin0 -> 838 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties48.pngbin0 -> 3236 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/chatzilla-window.pngbin0 -> 1129 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/chatzilla-window16.pngbin0 -> 870 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/chatzilla-window48.pngbin0 -> 896 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/downloadManager.pngbin0 -> 1649 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/downloadManager16.pngbin0 -> 846 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/downloadManager48.pngbin0 -> 2525 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/editorWindow.pngbin0 -> 2018 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/editorWindow16.pngbin0 -> 870 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/editorWindow48.pngbin0 -> 3180 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow.pngbin0 -> 2135 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow16.pngbin0 -> 914 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow48.pngbin0 -> 3305 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow.pngbin0 -> 2083 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow16.pngbin0 -> 877 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow48.pngbin0 -> 3378 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/history-window.pngbin0 -> 2070 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/history-window16.pngbin0 -> 897 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/history-window48.pngbin0 -> 3454 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/messengerWindow.pngbin0 -> 1958 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/messengerWindow16.pngbin0 -> 854 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/messengerWindow48.pngbin0 -> 3207 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow.pngbin0 -> 1825 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow16.pngbin0 -> 843 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow48.pngbin0 -> 2944 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/places.pngbin0 -> 1849 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/places16.pngbin0 -> 848 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/gtk/places48.pngbin0 -> 2728 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/JSConsoleWindow.svg221
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/abcardWindow.svg278
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/ablistWindow.svg1590
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/addressbookWindow.svg358
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/bookmarkproperties.svg255
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/downloadManager.svg313
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/editorWindow.svg214
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/findBookmarkWindow.svg211
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/findHistoryWindow.svg396
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/history-window.svg403
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/messengerWindow.svg196
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/msgcomposeWindow.svg185
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/places.svg193
-rw-r--r--comm/suite/branding/seamonkey/icons/svg/seamonkey.svg529
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/JSConsoleWindow.svg1297
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/README15
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/abcardWindow.svg723
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/ablistWindow.svg4504
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/addressbookWindow.svg1968
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/bookmarkproperties.svg536
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/downloadManager.svg775
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/editorWindow.svg614
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/findBookmarkWindow.svg1769
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/findHistoryWindow.svg2325
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/history-window.svg2294
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/messengerWindow.svg1116
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/msgcomposeWindow.svg676
-rw-r--r--comm/suite/branding/seamonkey/icons/template/svg/places.svg1562
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/JSConsoleWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/abcardWindow.icobin0 -> 135438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/ablistWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/addressbookWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/bookmarkproperties.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/chatzilla-window.icobin0 -> 50534 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/downloadManager.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/editorWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/findBookmarkWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/findHistoryWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/gif-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/history-window.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/html-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/image-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/jpeg-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/main-window.icobin0 -> 59025 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/messengerWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/msgcomposeWindow.icobin0 -> 127438 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/newmail.icobin0 -> 318 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/places.icobin0 -> 127438 bytes
-rwxr-xr-xcomm/suite/branding/seamonkey/icons/windows/readme.txt50
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/script-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/template-file.icobin0 -> 25214 bytes
-rwxr-xr-xcomm/suite/branding/seamonkey/icons/windows/template-window.icobin0 -> 29310 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/xml-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/icons/windows/xul-file.icobin0 -> 25214 bytes
-rw-r--r--comm/suite/branding/seamonkey/locales/Makefile.in8
-rw-r--r--comm/suite/branding/seamonkey/locales/en-US/brand.dtd8
-rw-r--r--comm/suite/branding/seamonkey/locales/en-US/brand.properties31
-rw-r--r--comm/suite/branding/seamonkey/locales/jar.mn6
-rw-r--r--comm/suite/branding/seamonkey/locales/moz.build6
-rw-r--r--comm/suite/branding/seamonkey/moz.build9
-rw-r--r--comm/suite/branding/seamonkey/seamonkey-branding.js1
-rw-r--r--comm/suite/branding/seamonkey/seamonkey.icnsbin0 -> 471865 bytes
-rw-r--r--comm/suite/branding/seamonkey/wizHeader.bmpbin0 -> 25818 bytes
-rw-r--r--comm/suite/branding/seamonkey/wizHeaderRTL.bmpbin0 -> 25818 bytes
-rw-r--r--comm/suite/branding/seamonkey/wizWatermark.bmpbin0 -> 154542 bytes
-rw-r--r--comm/suite/browser/SuiteBrowser.manifest23
-rw-r--r--comm/suite/browser/browser-places.js983
-rw-r--r--comm/suite/browser/content.js901
-rw-r--r--comm/suite/browser/fullScreen.js107
-rw-r--r--comm/suite/browser/hiddenWindow.xul55
-rw-r--r--comm/suite/browser/jar.mn41
-rw-r--r--comm/suite/browser/linkToolbarHandler.js295
-rw-r--r--comm/suite/browser/linkToolbarItem.js215
-rw-r--r--comm/suite/browser/linkToolbarOverlay.js213
-rw-r--r--comm/suite/browser/linkToolbarOverlay.xul165
-rw-r--r--comm/suite/browser/mailNavigatorOverlay.js176
-rw-r--r--comm/suite/browser/mailNavigatorOverlay.xul96
-rw-r--r--comm/suite/browser/metadata.js526
-rw-r--r--comm/suite/browser/metadata.xul192
-rw-r--r--comm/suite/browser/moz.build22
-rw-r--r--comm/suite/browser/navigator.css135
-rw-r--r--comm/suite/browser/navigator.js3311
-rw-r--r--comm/suite/browser/navigator.xul571
-rw-r--r--comm/suite/browser/navigatorDD.js121
-rw-r--r--comm/suite/browser/navigatorOverlay.xul716
-rw-r--r--comm/suite/browser/nsBrowserContentHandler.js634
-rw-r--r--comm/suite/browser/nsBrowserContentListener.js138
-rw-r--r--comm/suite/browser/nsBrowserStatusHandler.js473
-rw-r--r--comm/suite/browser/nsTypeAheadFind.js416
-rw-r--r--comm/suite/browser/pageinfo/feeds.js31
-rw-r--r--comm/suite/browser/pageinfo/feeds.xml40
-rw-r--r--comm/suite/browser/pageinfo/pageInfo.css18
-rw-r--r--comm/suite/browser/pageinfo/pageInfo.js1177
-rw-r--r--comm/suite/browser/pageinfo/pageInfo.xul531
-rw-r--r--comm/suite/browser/pageinfo/permissions.js204
-rw-r--r--comm/suite/browser/pageinfo/security.js354
-rw-r--r--comm/suite/browser/safeBrowsingOverlay.js75
-rw-r--r--comm/suite/browser/safeBrowsingOverlay.xul32
-rw-r--r--comm/suite/browser/sessionHistoryUI.js172
-rw-r--r--comm/suite/browser/tabbrowser.xml3707
-rw-r--r--comm/suite/browser/test/browser/alltabslistener.html8
-rw-r--r--comm/suite/browser/test/browser/authenticate.sjs210
-rw-r--r--comm/suite/browser/test/browser/browser.ini38
-rw-r--r--comm/suite/browser/test/browser/browser_alltabslistener.js201
-rw-r--r--comm/suite/browser/test/browser/browser_bug329212.js42
-rw-r--r--comm/suite/browser/test/browser/browser_bug409624.js57
-rw-r--r--comm/suite/browser/test/browser/browser_bug413915.js70
-rw-r--r--comm/suite/browser/test/browser/browser_bug427559.js39
-rw-r--r--comm/suite/browser/test/browser/browser_bug435325.js56
-rw-r--r--comm/suite/browser/test/browser/browser_bug462289.js87
-rw-r--r--comm/suite/browser/test/browser/browser_bug519216.js50
-rw-r--r--comm/suite/browser/test/browser/browser_bug561636.js459
-rw-r--r--comm/suite/browser/test/browser/browser_bug562649.js26
-rw-r--r--comm/suite/browser/test/browser/browser_bug581947.js95
-rw-r--r--comm/suite/browser/test/browser/browser_bug585511.js24
-rw-r--r--comm/suite/browser/test/browser/browser_bug595507.js39
-rw-r--r--comm/suite/browser/test/browser/browser_bug623155.js136
-rw-r--r--comm/suite/browser/test/browser/browser_ctrlTab.js197
-rw-r--r--comm/suite/browser/test/browser/browser_fayt.js25
-rw-r--r--comm/suite/browser/test/browser/browser_notification_tab_switching.js95
-rw-r--r--comm/suite/browser/test/browser/browser_pageInfo.js38
-rw-r--r--comm/suite/browser/test/browser/browser_page_style_menu.js67
-rw-r--r--comm/suite/browser/test/browser/browser_popupNotification.js782
-rw-r--r--comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js65
-rw-r--r--comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html13
-rw-r--r--comm/suite/browser/test/browser/browser_relatedTabs.js52
-rw-r--r--comm/suite/browser/test/browser/browser_scope.js4
-rw-r--r--comm/suite/browser/test/browser/browser_selectTabAtIndex.js22
-rw-r--r--comm/suite/browser/test/browser/browser_urlbarCopying.js204
-rw-r--r--comm/suite/browser/test/browser/feed_tab.html17
-rw-r--r--comm/suite/browser/test/browser/file_dom_notifications.html40
-rw-r--r--comm/suite/browser/test/browser/head.js63
-rw-r--r--comm/suite/browser/test/browser/page_style_sample.html31
-rw-r--r--comm/suite/browser/test/browser/redirect_bug623155.sjs16
-rw-r--r--comm/suite/browser/test/browser/title_test.svg59
-rw-r--r--comm/suite/browser/test/chrome/chrome.ini3
-rw-r--r--comm/suite/browser/test/chrome/test_maxSniffing.html37
-rw-r--r--comm/suite/browser/test/mochitest/audio.oggbin0 -> 47411 bytes
-rw-r--r--comm/suite/browser/test/mochitest/bug364677-data.xml5
-rw-r--r--comm/suite/browser/test/mochitest/bug364677-data.xml^headers^1
-rw-r--r--comm/suite/browser/test/mochitest/bug395533-data.txt6
-rw-r--r--comm/suite/browser/test/mochitest/bug436801-data.xml44
-rw-r--r--comm/suite/browser/test/mochitest/ctxmenu-image.pngbin0 -> 5401 bytes
-rw-r--r--comm/suite/browser/test/mochitest/feed_discovery.html112
-rw-r--r--comm/suite/browser/test/mochitest/mochitest.ini17
-rw-r--r--comm/suite/browser/test/mochitest/subtst_contextmenu.html70
-rw-r--r--comm/suite/browser/test/mochitest/test_bug364677.html32
-rw-r--r--comm/suite/browser/test/mochitest/test_bug395533.html39
-rw-r--r--comm/suite/browser/test/mochitest/test_bug436801.html118
-rw-r--r--comm/suite/browser/test/mochitest/test_contextmenu.html931
-rw-r--r--comm/suite/browser/test/mochitest/test_feed_discovery.html56
-rw-r--r--comm/suite/browser/test/mochitest/test_registerHandler.html85
-rw-r--r--comm/suite/browser/test/mochitest/valid-feed.xml23
-rw-r--r--comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml32
-rw-r--r--comm/suite/browser/test/mochitest/video.oggbin0 -> 285310 bytes
-rw-r--r--comm/suite/browser/urlbarBindings.xml694
-rw-r--r--comm/suite/browser/webDeveloperOverlay.js197
-rw-r--r--comm/suite/browser/webDeveloperOverlay.xul156
-rw-r--r--comm/suite/build.mk40
-rw-r--r--comm/suite/chatzilla/ChangeLog432
-rw-r--r--comm/suite/chatzilla/DYK97
-rw-r--r--comm/suite/chatzilla/Makefile.in6
-rw-r--r--comm/suite/chatzilla/README70
-rw-r--r--comm/suite/chatzilla/TODO8
-rw-r--r--comm/suite/chatzilla/jar.mn103
-rw-r--r--comm/suite/chatzilla/js/lib/chatzilla-protocol-script.js10
-rw-r--r--comm/suite/chatzilla/js/lib/chatzilla-service.js311
-rw-r--r--comm/suite/chatzilla/js/lib/chatzilla-service.manifest6
-rw-r--r--comm/suite/chatzilla/js/lib/command-manager.js952
-rw-r--r--comm/suite/chatzilla/js/lib/connection-xpcom.js703
-rw-r--r--comm/suite/chatzilla/js/lib/dcc.js1198
-rw-r--r--comm/suite/chatzilla/js/lib/events.js365
-rw-r--r--comm/suite/chatzilla/js/lib/file-utils.js430
-rw-r--r--comm/suite/chatzilla/js/lib/http.js177
-rw-r--r--comm/suite/chatzilla/js/lib/ident.js203
-rw-r--r--comm/suite/chatzilla/js/lib/irc-debug.js84
-rw-r--r--comm/suite/chatzilla/js/lib/irc.js4518
-rw-r--r--comm/suite/chatzilla/js/lib/json-serializer.js103
-rw-r--r--comm/suite/chatzilla/js/lib/menu-manager.js848
-rw-r--r--comm/suite/chatzilla/js/lib/message-manager.js356
-rw-r--r--comm/suite/chatzilla/js/lib/pref-manager.js443
-rw-r--r--comm/suite/chatzilla/js/lib/protocol-handlers.jsm250
-rw-r--r--comm/suite/chatzilla/js/lib/sts.js210
-rw-r--r--comm/suite/chatzilla/js/lib/text-logger.js134
-rw-r--r--comm/suite/chatzilla/js/lib/text-serializer.js348
-rw-r--r--comm/suite/chatzilla/js/lib/utils.js1490
-rw-r--r--comm/suite/chatzilla/js/tests/DP.js612
-rw-r--r--comm/suite/chatzilla/js/tests/ircbot.js407
-rw-r--r--comm/suite/chatzilla/js/tests/mingus.js362
-rw-r--r--comm/suite/chatzilla/js/tests/mybot.js21
-rw-r--r--comm/suite/chatzilla/js/tests/test_matchobject.js41
-rw-r--r--comm/suite/chatzilla/js/tests/toys.js35
-rw-r--r--comm/suite/chatzilla/locales/Makefile.in6
-rw-r--r--comm/suite/chatzilla/locales/all-locales23
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/about.dtd17
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/browserOverlay.dtd5
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/channels.dtd33
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/chatzilla.dtd19
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/chatzilla.properties1674
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/chatzillaOverlay.dtd7
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/config.dtd40
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/install-plugin.dtd17
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/networks.dtd57
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/networks.properties28
-rw-r--r--comm/suite/chatzilla/locales/en-US/chrome/pref-irc.dtd12
-rw-r--r--comm/suite/chatzilla/locales/en-US/defines.inc11
-rw-r--r--comm/suite/chatzilla/locales/jar.mn20
-rw-r--r--comm/suite/chatzilla/locales/l10n.ini10
-rw-r--r--comm/suite/chatzilla/locales/l10n.toml44
-rw-r--r--comm/suite/chatzilla/locales/moz.build6
-rw-r--r--comm/suite/chatzilla/moz.build15
-rw-r--r--comm/suite/chatzilla/xul/content/about/about.js112
-rw-r--r--comm/suite/chatzilla/xul/content/about/about.xul57
-rw-r--r--comm/suite/chatzilla/xul/content/browserOverlay.xul24
-rw-r--r--comm/suite/chatzilla/xul/content/channels.js875
-rw-r--r--comm/suite/chatzilla/xul/content/channels.xul108
-rw-r--r--comm/suite/chatzilla/xul/content/chatzilla.xul141
-rw-r--r--comm/suite/chatzilla/xul/content/chatzillaOverlay.js11
-rw-r--r--comm/suite/chatzilla/xul/content/chatzillaOverlay.xul42
-rw-r--r--comm/suite/chatzilla/xul/content/commands.js4760
-rw-r--r--comm/suite/chatzilla/xul/content/config-add.js55
-rw-r--r--comm/suite/chatzilla/xul/content/config-add.xul55
-rw-r--r--comm/suite/chatzilla/xul/content/config.css33
-rw-r--r--comm/suite/chatzilla/xul/content/config.js1775
-rw-r--r--comm/suite/chatzilla/xul/content/config.xul82
-rw-r--r--comm/suite/chatzilla/xul/content/dynamic.css7
-rw-r--r--comm/suite/chatzilla/xul/content/handlers.js3960
-rw-r--r--comm/suite/chatzilla/xul/content/install-plugin/install-plugin.js97
-rw-r--r--comm/suite/chatzilla/xul/content/install-plugin/install-plugin.xul43
-rw-r--r--comm/suite/chatzilla/xul/content/menus.js513
-rw-r--r--comm/suite/chatzilla/xul/content/menus.xul97
-rw-r--r--comm/suite/chatzilla/xul/content/messages.js104
-rw-r--r--comm/suite/chatzilla/xul/content/mungers.js904
-rw-r--r--comm/suite/chatzilla/xul/content/networks-edit.css15
-rw-r--r--comm/suite/chatzilla/xul/content/networks-edit.js390
-rw-r--r--comm/suite/chatzilla/xul/content/networks-edit.xul155
-rw-r--r--comm/suite/chatzilla/xul/content/networks-server.js94
-rw-r--r--comm/suite/chatzilla/xul/content/networks-server.xul84
-rw-r--r--comm/suite/chatzilla/xul/content/networks.js228
-rw-r--r--comm/suite/chatzilla/xul/content/output-base.css528
-rw-r--r--comm/suite/chatzilla/xul/content/output-window.html209
-rw-r--r--comm/suite/chatzilla/xul/content/output-window.js588
-rw-r--r--comm/suite/chatzilla/xul/content/popups.xul124
-rw-r--r--comm/suite/chatzilla/xul/content/pref-irc-toolkit.xul24
-rw-r--r--comm/suite/chatzilla/xul/content/prefs.js1213
-rw-r--r--comm/suite/chatzilla/xul/content/prefsOverlay.xul31
-rw-r--r--comm/suite/chatzilla/xul/content/scripts.xul55
-rw-r--r--comm/suite/chatzilla/xul/content/static.js5639
-rw-r--r--comm/suite/chatzilla/xul/lib/munger.js245
-rw-r--r--comm/suite/chatzilla/xul/lib/tree-utils.js1716
-rw-r--r--comm/suite/chatzilla/xul/skin/about.css44
-rw-r--r--comm/suite/chatzilla/xul/skin/browserOverlay.css7
-rw-r--r--comm/suite/chatzilla/xul/skin/channels.css24
-rw-r--r--comm/suite/chatzilla/xul/skin/chatzilla.css305
-rw-r--r--comm/suite/chatzilla/xul/skin/chatzillaOverlay.css7
-rw-r--r--comm/suite/chatzilla/xul/skin/images/admin-graphic.pngbin0 -> 566 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/admin-symbol.pngbin0 -> 461 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/arrow-down.pngbin0 -> 192 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/chatzilla-16.pngbin0 -> 261 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/drop-indicator-bottom.pngbin0 -> 416 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/founder-graphic.pngbin0 -> 582 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/founder-symbol.pngbin0 -> 325 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/halfop-graphic.pngbin0 -> 588 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/halfop-symbol.pngbin0 -> 481 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/input-send.pngbin0 -> 290 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/logging-off.pngbin0 -> 289 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/logging-on.pngbin0 -> 308 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/logo.pngbin0 -> 1499 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/multiline-contract.pngbin0 -> 255 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/multiline-expand.pngbin0 -> 255 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/no-graphic.pngbin0 -> 606 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/no-symbol.pngbin0 -> 154 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/op-graphic.pngbin0 -> 599 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/op-symbol.pngbin0 -> 600 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/source_png/spbubble-off.pngbin0 -> 26256 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/source_png/spbubble-on.pngbin0 -> 31259 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/source_svg/logging.svg61
-rw-r--r--comm/suite/chatzilla/xul/skin/images/source_svg/userlist_icons.svg636
-rw-r--r--comm/suite/chatzilla/xul/skin/images/spbubble-off.pngbin0 -> 710 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/spbubble-on.pngbin0 -> 663 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/voice-graphic.pngbin0 -> 378 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/images/voice-symbol.pngbin0 -> 325 bytes
-rw-r--r--comm/suite/chatzilla/xul/skin/install-plugin.css9
-rw-r--r--comm/suite/chatzilla/xul/skin/networks-edit.css7
-rw-r--r--comm/suite/chatzilla/xul/skin/output-dark.css226
-rw-r--r--comm/suite/chatzilla/xul/skin/output-default.css67
-rw-r--r--comm/suite/chatzilla/xul/skin/output-light.css217
-rw-r--r--comm/suite/chatzilla/xul/skin/output-loud.css202
-rw-r--r--comm/suite/chatzilla/xul/skin/output-marble.css148
-rw-r--r--comm/suite/components/SuiteComponents.manifest18
-rw-r--r--comm/suite/components/autocomplete/content/autocomplete.css46
-rw-r--r--comm/suite/components/autocomplete/content/autocomplete.xml1641
-rw-r--r--comm/suite/components/autocomplete/jar.mn9
-rw-r--r--comm/suite/components/autocomplete/moz.build7
-rw-r--r--comm/suite/components/bindings/datetimepicker.xml1316
-rw-r--r--comm/suite/components/bindings/findbar.xml162
-rw-r--r--comm/suite/components/bindings/general.xml37
-rw-r--r--comm/suite/components/bindings/generalBindings.xml31
-rw-r--r--comm/suite/components/bindings/jar.mn20
-rw-r--r--comm/suite/components/bindings/moz.build7
-rw-r--r--comm/suite/components/bindings/notification.xml2423
-rw-r--r--comm/suite/components/bindings/numberbox.xml217
-rw-r--r--comm/suite/components/bindings/preferences.xml817
-rw-r--r--comm/suite/components/bindings/prefwindow.xml548
-rw-r--r--comm/suite/components/bindings/spinbuttons.xml92
-rw-r--r--comm/suite/components/bindings/textbox.xml251
-rw-r--r--comm/suite/components/bindings/toolbar-xpfe.xml333
-rw-r--r--comm/suite/components/bindings/toolbar.xml579
-rw-r--r--comm/suite/components/build/Makefile.in8
-rw-r--r--comm/suite/components/build/moz.build23
-rw-r--r--comm/suite/components/build/nsSuiteCID.h24
-rw-r--r--comm/suite/components/build/nsSuiteModule.cpp87
-rw-r--r--comm/suite/components/console/content/console.css74
-rw-r--r--comm/suite/components/console/content/console.js111
-rw-r--r--comm/suite/components/console/content/console.xul208
-rw-r--r--comm/suite/components/console/content/consoleBindings.xml543
-rw-r--r--comm/suite/components/console/jar.mn9
-rw-r--r--comm/suite/components/console/jsconsole-clhandler.js34
-rw-r--r--comm/suite/components/console/jsconsole-clhandler.manifest3
-rw-r--r--comm/suite/components/console/moz.build12
-rw-r--r--comm/suite/components/customizeToolbar.css107
-rw-r--r--comm/suite/components/customizeToolbar.js855
-rw-r--r--comm/suite/components/customizeToolbar.xhtml110
-rw-r--r--comm/suite/components/dataman/content/dataman.css45
-rw-r--r--comm/suite/components/dataman/content/dataman.js3270
-rw-r--r--comm/suite/components/dataman/content/dataman.xml249
-rw-r--r--comm/suite/components/dataman/content/dataman.xul571
-rw-r--r--comm/suite/components/dataman/jar.mn9
-rw-r--r--comm/suite/components/dataman/moz.build11
-rw-r--r--comm/suite/components/dataman/tests/browser.ini8
-rw-r--r--comm/suite/components/dataman/tests/browser_dataman_basics.js831
-rw-r--r--comm/suite/components/dataman/tests/browser_dataman_callviews.js213
-rw-r--r--comm/suite/components/dataman/tests/dataman_storage.appcache5
-rw-r--r--comm/suite/components/dataman/tests/dataman_storage.appcache^headers^2
-rw-r--r--comm/suite/components/dataman/tests/dataman_storage.html40
-rw-r--r--comm/suite/components/downloads/DownloadsCommon.jsm800
-rw-r--r--comm/suite/components/downloads/DownloadsTaskbar.jsm182
-rw-r--r--comm/suite/components/downloads/content/DownloadProgressListener.js30
-rw-r--r--comm/suite/components/downloads/content/downloadmanager.js634
-rw-r--r--comm/suite/components/downloads/content/downloadmanager.xul452
-rw-r--r--comm/suite/components/downloads/content/progressDialog.js240
-rw-r--r--comm/suite/components/downloads/content/progressDialog.xul108
-rw-r--r--comm/suite/components/downloads/content/treeView.js483
-rw-r--r--comm/suite/components/downloads/content/uploadProgress.js189
-rw-r--r--comm/suite/components/downloads/content/uploadProgress.xul33
-rw-r--r--comm/suite/components/downloads/jar.mn14
-rw-r--r--comm/suite/components/downloads/moz.build17
-rw-r--r--comm/suite/components/downloads/tests/chrome/chrome.ini21
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul376
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul281
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul172
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul201
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul117
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul200
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul198
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_drag.xul201
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul243
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_multi_select.xul204
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_multiword_search.xul173
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_open_properties.xul197
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul150
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul168
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_search_keys.xul128
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_select_all.xul145
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul221
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul198
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul115
-rw-r--r--comm/suite/components/feeds/FeedConverter.js461
-rw-r--r--comm/suite/components/feeds/FeedWriter.js1211
-rw-r--r--comm/suite/components/feeds/SuiteFeeds.manifest11
-rw-r--r--comm/suite/components/feeds/WebContentConverter.js818
-rw-r--r--comm/suite/components/feeds/content/subscribe.css7
-rw-r--r--comm/suite/components/feeds/content/subscribe.xhtml59
-rw-r--r--comm/suite/components/feeds/content/subscribe.xml42
-rw-r--r--comm/suite/components/feeds/jar.mn8
-rw-r--r--comm/suite/components/feeds/moz.build27
-rw-r--r--comm/suite/components/feeds/nsFeedSniffer.cpp356
-rw-r--r--comm/suite/components/feeds/nsFeedSniffer.h35
-rw-r--r--comm/suite/components/feeds/nsIFeedResultService.idl66
-rw-r--r--comm/suite/components/feeds/nsIWebContentConverterRegistrar.idl116
-rw-r--r--comm/suite/components/helpviewer/content/contextHelp.js68
-rw-r--r--comm/suite/components/helpviewer/content/help.js856
-rw-r--r--comm/suite/components/helpviewer/content/help.xul284
-rw-r--r--comm/suite/components/helpviewer/content/helpContextOverlay.xul58
-rw-r--r--comm/suite/components/helpviewer/content/platformClasses.css13
-rw-r--r--comm/suite/components/helpviewer/jar.mn11
-rw-r--r--comm/suite/components/helpviewer/moz.build7
-rw-r--r--comm/suite/components/migration/SuiteProfileMigrator.js149
-rw-r--r--comm/suite/components/migration/SuiteProfileMigrator.manifest2
-rw-r--r--comm/suite/components/migration/content/migration.js413
-rw-r--r--comm/suite/components/migration/content/migration.xul95
-rw-r--r--comm/suite/components/migration/jar.mn7
-rw-r--r--comm/suite/components/migration/moz.build19
-rw-r--r--comm/suite/components/migration/public/moz.build15
-rw-r--r--comm/suite/components/migration/public/nsISuiteProfileMigrator.idl76
-rw-r--r--comm/suite/components/migration/public/nsSuiteMigrationCID.h8
-rw-r--r--comm/suite/components/migration/src/moz.build13
-rw-r--r--comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp844
-rw-r--r--comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h147
-rw-r--r--comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp66
-rw-r--r--comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h60
-rw-r--r--comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp571
-rw-r--r--comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h44
-rw-r--r--comm/suite/components/moz.build52
-rw-r--r--comm/suite/components/nsAbout.js76
-rw-r--r--comm/suite/components/nsGopherProtocolStubHandler.js67
-rw-r--r--comm/suite/components/nsISuiteGlue.idl42
-rw-r--r--comm/suite/components/nsSuiteGlue.js1676
-rw-r--r--comm/suite/components/permissions/content/cookieViewer.js531
-rw-r--r--comm/suite/components/permissions/content/cookieViewer.xul225
-rw-r--r--comm/suite/components/permissions/content/permissionsManager.js287
-rw-r--r--comm/suite/components/permissions/content/permissionsManager.xul81
-rw-r--r--comm/suite/components/permissions/content/permissionsUtils.js130
-rw-r--r--comm/suite/components/permissions/jar.mn10
-rw-r--r--comm/suite/components/permissions/moz.build7
-rw-r--r--comm/suite/components/places/PlacesUIUtils.jsm1499
-rw-r--r--comm/suite/components/places/content/bookmarkProperties.js524
-rw-r--r--comm/suite/components/places/content/bookmarkProperties.xul41
-rw-r--r--comm/suite/components/places/content/bookmarksPanel.js24
-rw-r--r--comm/suite/components/places/content/bookmarksPanel.xul54
-rw-r--r--comm/suite/components/places/content/browserPlacesViews.js2287
-rw-r--r--comm/suite/components/places/content/controller.js1442
-rw-r--r--comm/suite/components/places/content/editBookmarkOverlay.js1129
-rw-r--r--comm/suite/components/places/content/editBookmarkOverlay.xul191
-rw-r--r--comm/suite/components/places/content/history-panel.js86
-rw-r--r--comm/suite/components/places/content/history-panel.xul95
-rw-r--r--comm/suite/components/places/content/menu.xml624
-rw-r--r--comm/suite/components/places/content/organizer.css7
-rw-r--r--comm/suite/components/places/content/places.css37
-rw-r--r--comm/suite/components/places/content/places.js1366
-rw-r--r--comm/suite/components/places/content/places.xul337
-rw-r--r--comm/suite/components/places/content/placesOverlay.xul228
-rw-r--r--comm/suite/components/places/content/sidebarUtils.js105
-rw-r--r--comm/suite/components/places/content/tree.xml812
-rw-r--r--comm/suite/components/places/content/treeView.js1823
-rw-r--r--comm/suite/components/places/jar.mn30
-rw-r--r--comm/suite/components/places/moz.build28
-rw-r--r--comm/suite/components/places/nsPlacesAutoComplete.js1323
-rw-r--r--comm/suite/components/places/nsPlacesAutoComplete.manifest3
-rw-r--r--comm/suite/components/places/tests/autocomplete/head_autocomplete.js307
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_416211.js30
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_416214.js38
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_417798.js36
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_418257.js43
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_422277.js25
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_autocomplete_on_value_removed_479089.js54
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_download_embed_bookmarks.js53
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_empty_search.js69
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_enabled.js69
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_escape_self.js30
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_ignore_protocol.js27
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_keyword_search.js73
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_match_beginning.js45
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_multi_word_search.js49
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_special_search.js183
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_swap_protocol.js63
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_tabmatches.js97
-rw-r--r--comm/suite/components/places/tests/autocomplete/test_word_boundary_search.js105
-rw-r--r--comm/suite/components/places/tests/autocomplete/xpcshell.ini29
-rw-r--r--comm/suite/components/places/tests/browser/browser.ini12
-rw-r--r--comm/suite/components/places/tests/browser/browser_0_library_left_pane_migration.js93
-rw-r--r--comm/suite/components/places/tests/browser/browser_425884.js103
-rw-r--r--comm/suite/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js233
-rw-r--r--comm/suite/components/places/tests/browser/browser_library_infoBox.js171
-rw-r--r--comm/suite/components/places/tests/browser/browser_library_left_pane_commands.js100
-rw-r--r--comm/suite/components/places/tests/browser/browser_library_left_pane_fixnames.js92
-rw-r--r--comm/suite/components/places/tests/browser/browser_library_open_leak.js23
-rw-r--r--comm/suite/components/places/tests/browser/browser_library_views_liveupdate.js303
-rw-r--r--comm/suite/components/places/tests/browser/browser_sort_in_library.js254
-rw-r--r--comm/suite/components/places/tests/browser/head.js95
-rw-r--r--comm/suite/components/places/tests/chrome/chrome.ini10
-rw-r--r--comm/suite/components/places/tests/chrome/head.js55
-rw-r--r--comm/suite/components/places/tests/chrome/test_0_bug510634.xul87
-rw-r--r--comm/suite/components/places/tests/chrome/test_0_multiple_left_pane.xul82
-rw-r--r--comm/suite/components/places/tests/chrome/test_bug427633_no_newfolder_if_noip.xul83
-rw-r--r--comm/suite/components/places/tests/chrome/test_bug485100-change-case-loses-tag.xul82
-rw-r--r--comm/suite/components/places/tests/chrome/test_bug549192.xul118
-rw-r--r--comm/suite/components/places/tests/chrome/test_bug549491.xul100
-rw-r--r--comm/suite/components/places/tests/chrome/test_treeview_date.xul179
-rw-r--r--comm/suite/components/places/tests/head_common.js868
-rw-r--r--comm/suite/components/places/tests/unit/bookmarks.glue.html16
-rw-r--r--comm/suite/components/places/tests/unit/bookmarks.glue.json1
-rw-r--r--comm/suite/components/places/tests/unit/corruptDB.sqlitebin0 -> 32772 bytes
-rw-r--r--comm/suite/components/places/tests/unit/distribution.ini21
-rw-r--r--comm/suite/components/places/tests/unit/head_bookmarks.js46
-rw-r--r--comm/suite/components/places/tests/unit/test_421483.js84
-rw-r--r--comm/suite/components/places/tests/unit/test_PUIU_makeTransaction.js355
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_corrupt.js85
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js81
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js80
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_distribution.js124
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_migrate.js89
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_prefs.js272
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_restore.js81
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_shutdown.js152
-rw-r--r--comm/suite/components/places/tests/unit/test_browserGlue_smartBookmarks.js351
-rw-r--r--comm/suite/components/places/tests/unit/test_clearHistory_shutdown.js181
-rw-r--r--comm/suite/components/places/tests/unit/test_leftpane_corruption_handling.js189
-rw-r--r--comm/suite/components/places/tests/unit/xpcshell.ini19
-rw-r--r--comm/suite/components/pref/content/pref-advanced.js90
-rw-r--r--comm/suite/components/pref/content/pref-advanced.xul174
-rw-r--r--comm/suite/components/pref/content/pref-appearance.js102
-rw-r--r--comm/suite/components/pref/content/pref-appearance.xul103
-rw-r--r--comm/suite/components/pref/content/pref-applicationManager.js100
-rw-r--r--comm/suite/components/pref/content/pref-applicationManager.xul56
-rw-r--r--comm/suite/components/pref/content/pref-applications.js1606
-rw-r--r--comm/suite/components/pref/content/pref-applications.xul113
-rw-r--r--comm/suite/components/pref/content/pref-cache.js113
-rw-r--r--comm/suite/components/pref/content/pref-cache.xul142
-rw-r--r--comm/suite/components/pref/content/pref-colors.js26
-rw-r--r--comm/suite/components/pref/content/pref-colors.xul131
-rw-r--r--comm/suite/components/pref/content/pref-content.js141
-rw-r--r--comm/suite/components/pref/content/pref-content.xul131
-rw-r--r--comm/suite/components/pref/content/pref-cookies.js34
-rw-r--r--comm/suite/components/pref/content/pref-cookies.xul89
-rw-r--r--comm/suite/components/pref/content/pref-debugging.js15
-rw-r--r--comm/suite/components/pref/content/pref-debugging.xul120
-rw-r--r--comm/suite/components/pref/content/pref-download.js197
-rw-r--r--comm/suite/components/pref/content/pref-download.xul120
-rw-r--r--comm/suite/components/pref/content/pref-findasyoutype.js15
-rw-r--r--comm/suite/components/pref/content/pref-findasyoutype.xul70
-rw-r--r--comm/suite/components/pref/content/pref-fonts.js220
-rw-r--r--comm/suite/components/pref/content/pref-fonts.xul260
-rw-r--r--comm/suite/components/pref/content/pref-history.js55
-rw-r--r--comm/suite/components/pref/content/pref-history.xul99
-rw-r--r--comm/suite/components/pref/content/pref-http.js42
-rw-r--r--comm/suite/components/pref/content/pref-http.xul82
-rw-r--r--comm/suite/components/pref/content/pref-images.xul47
-rw-r--r--comm/suite/components/pref/content/pref-keynav.js54
-rw-r--r--comm/suite/components/pref/content/pref-keynav.xul104
-rw-r--r--comm/suite/components/pref/content/pref-languages-add.js147
-rw-r--r--comm/suite/components/pref/content/pref-languages-add.xul54
-rw-r--r--comm/suite/components/pref/content/pref-languages.js200
-rw-r--r--comm/suite/components/pref/content/pref-languages.xul124
-rw-r--r--comm/suite/components/pref/content/pref-links.js15
-rw-r--r--comm/suite/components/pref/content/pref-links.xul78
-rw-r--r--comm/suite/components/pref/content/pref-locationbar.js42
-rw-r--r--comm/suite/components/pref/content/pref-locationbar.xul127
-rw-r--r--comm/suite/components/pref/content/pref-media.xul60
-rw-r--r--comm/suite/components/pref/content/pref-mousewheel.js45
-rw-r--r--comm/suite/components/pref/content/pref-mousewheel.xul298
-rw-r--r--comm/suite/components/pref/content/pref-navigator.js262
-rw-r--r--comm/suite/components/pref/content/pref-navigator.xul188
-rw-r--r--comm/suite/components/pref/content/pref-offlineapps.js178
-rw-r--r--comm/suite/components/pref/content/pref-offlineapps.xul81
-rw-r--r--comm/suite/components/pref/content/pref-popups.js95
-rw-r--r--comm/suite/components/pref/content/pref-popups.xul132
-rw-r--r--comm/suite/components/pref/content/pref-privatedata.js30
-rw-r--r--comm/suite/components/pref/content/pref-privatedata.xul181
-rw-r--r--comm/suite/components/pref/content/pref-proxies-advanced.xul194
-rw-r--r--comm/suite/components/pref/content/pref-proxies.js188
-rw-r--r--comm/suite/components/pref/content/pref-proxies.xul156
-rw-r--r--comm/suite/components/pref/content/pref-scripts.js29
-rw-r--r--comm/suite/components/pref/content/pref-scripts.xul92
-rwxr-xr-xcomm/suite/components/pref/content/pref-search.js60
-rwxr-xr-xcomm/suite/components/pref/content/pref-search.xul50
-rw-r--r--comm/suite/components/pref/content/pref-security.js15
-rw-r--r--comm/suite/components/pref/content/pref-security.xul108
-rw-r--r--comm/suite/components/pref/content/pref-smartupdate.js87
-rw-r--r--comm/suite/components/pref/content/pref-smartupdate.xul139
-rw-r--r--comm/suite/components/pref/content/pref-spelling.js119
-rw-r--r--comm/suite/components/pref/content/pref-spelling.xul80
-rw-r--r--comm/suite/components/pref/content/pref-sync.js143
-rw-r--r--comm/suite/components/pref/content/pref-sync.xul158
-rw-r--r--comm/suite/components/pref/content/pref-tabs.xul113
-rw-r--r--comm/suite/components/pref/content/preferences.js99
-rw-r--r--comm/suite/components/pref/content/preferences.xul264
-rwxr-xr-xcomm/suite/components/pref/content/prefpanels.css33
-rw-r--r--comm/suite/components/pref/content/prefpanels.xml59
-rw-r--r--comm/suite/components/pref/jar.mn73
-rw-r--r--comm/suite/components/pref/moz.build11
-rw-r--r--comm/suite/components/pref/tests/browser/browser.ini3
-rw-r--r--comm/suite/components/pref/tests/browser/browser_bug410900.js55
-rw-r--r--comm/suite/components/profile/content/profileSelection.js344
-rw-r--r--comm/suite/components/profile/content/profileSelection.xul85
-rw-r--r--comm/suite/components/profile/jar.mn8
-rw-r--r--comm/suite/components/profile/moz.build13
-rwxr-xr-xcomm/suite/components/profile/nsSuiteDirectoryProvider.cpp248
-rw-r--r--comm/suite/components/profile/nsSuiteDirectoryProvider.h58
-rw-r--r--comm/suite/components/sanitize/Sanitizer.jsm947
-rw-r--r--comm/suite/components/sanitize/content/sanitizeDialog.js111
-rw-r--r--comm/suite/components/sanitize/content/sanitizeDialog.xul154
-rw-r--r--comm/suite/components/sanitize/jar.mn8
-rw-r--r--comm/suite/components/sanitize/moz.build14
-rw-r--r--comm/suite/components/search/content/engineManager.js508
-rw-r--r--comm/suite/components/search/content/engineManager.xul98
-rw-r--r--comm/suite/components/search/content/search-panel.js86
-rw-r--r--comm/suite/components/search/content/search-panel.xul48
-rw-r--r--comm/suite/components/search/content/search.xml739
-rw-r--r--comm/suite/components/search/content/searchbarBindings.css21
-rw-r--r--comm/suite/components/search/jar.mn16
-rw-r--r--comm/suite/components/search/moz.build7
-rw-r--r--comm/suite/components/search/searchplugins/allegro-pl.xml18
-rw-r--r--comm/suite/components/search/searchplugins/amazon-br.xml20
-rw-r--r--comm/suite/components/search/searchplugins/amazon-de.xml20
-rw-r--r--comm/suite/components/search/searchplugins/amazon-en-GB.xml20
-rw-r--r--comm/suite/components/search/searchplugins/amazon-es.xml20
-rw-r--r--comm/suite/components/search/searchplugins/amazon-fr.xml20
-rw-r--r--comm/suite/components/search/searchplugins/amazon-it.xml20
-rw-r--r--comm/suite/components/search/searchplugins/amazon-jp.xml32
-rw-r--r--comm/suite/components/search/searchplugins/amazon-zh-CN.xml23
-rw-r--r--comm/suite/components/search/searchplugins/amazon.xml27
-rw-r--r--comm/suite/components/search/searchplugins/atlas-sk.xml14
-rw-r--r--comm/suite/components/search/searchplugins/azet-sk.xml15
-rw-r--r--comm/suite/components/search/searchplugins/bing.xml22
-rw-r--r--comm/suite/components/search/searchplugins/bolcom-nl.xml16
-rw-r--r--comm/suite/components/search/searchplugins/chambers-en-GB.xml19
-rw-r--r--comm/suite/components/search/searchplugins/cnrtl-tlfi-fr.xml20
-rw-r--r--comm/suite/components/search/searchplugins/drae.xml16
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-cs-CZ.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-de-DE.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-el-GR.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-en-GB.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-en-US.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-es-AR.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-es-ES.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-fi-FI.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-fr-FR.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-hu-HU.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-it-IT.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-ja-JP.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-nb-NO.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-nl-NL.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-pl-PL.xml27
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-pt-BR.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-pt-PT.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-ru-RU.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-sk-SK.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-sv-SE.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-zh-CN.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo-zh-TW.xml25
-rw-r--r--comm/suite/components/search/searchplugins/duckduckgo.xml23
-rw-r--r--comm/suite/components/search/searchplugins/ebay-de.xml20
-rw-r--r--comm/suite/components/search/searchplugins/ebay-en-GB.xml20
-rw-r--r--comm/suite/components/search/searchplugins/ebay-es.xml20
-rw-r--r--comm/suite/components/search/searchplugins/ebay-fr.xml20
-rw-r--r--comm/suite/components/search/searchplugins/ebay-it.xml20
-rw-r--r--comm/suite/components/search/searchplugins/ebay-nl.xml20
-rw-r--r--comm/suite/components/search/searchplugins/ebay.xml20
-rw-r--r--comm/suite/components/search/searchplugins/google-jp.xml31
-rw-r--r--comm/suite/components/search/searchplugins/google.xml24
-rw-r--r--comm/suite/components/search/searchplugins/heureka-cz.xml22
-rw-r--r--comm/suite/components/search/searchplugins/hoepli.xml20
-rw-r--r--comm/suite/components/search/searchplugins/huuto-fi.xml24
-rw-r--r--comm/suite/components/search/searchplugins/images/amazon.icobin0 -> 1407 bytes
-rw-r--r--comm/suite/components/search/searchplugins/images/duckduckgo.icobin0 -> 5430 bytes
-rw-r--r--comm/suite/components/search/searchplugins/images/ebay.icobin0 -> 1455 bytes
-rw-r--r--comm/suite/components/search/searchplugins/images/google.icobin0 -> 5430 bytes
-rw-r--r--comm/suite/components/search/searchplugins/images/startpage.icobin0 -> 1150 bytes
-rw-r--r--comm/suite/components/search/searchplugins/images/wikipedia.icobin0 -> 884 bytes
-rw-r--r--comm/suite/components/search/searchplugins/images/yahoo.icobin0 -> 5430 bytes
-rw-r--r--comm/suite/components/search/searchplugins/list.json217
-rw-r--r--comm/suite/components/search/searchplugins/mapy-cz.xml18
-rw-r--r--comm/suite/components/search/searchplugins/marktplaats-nl.xml17
-rw-r--r--comm/suite/components/search/searchplugins/priberam.xml16
-rw-r--r--comm/suite/components/search/searchplugins/prisjakt-sv-SE.xml25
-rw-r--r--comm/suite/components/search/searchplugins/pwn-pl.xml14
-rw-r--r--comm/suite/components/search/searchplugins/sapo.xml22
-rw-r--r--comm/suite/components/search/searchplugins/seznam-cz.xml22
-rw-r--r--comm/suite/components/search/searchplugins/startpage-pl.xml17
-rw-r--r--comm/suite/components/search/searchplugins/startpage.xml17
-rw-r--r--comm/suite/components/search/searchplugins/tyda-sv-SE.xml17
-rw-r--r--comm/suite/components/search/searchplugins/vatera.xml18
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-NO.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-cz.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-de.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-el.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-es.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-fi.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-fr.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-hu.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-it.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-ja.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-ka.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-nl.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-pl.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-pt.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-ru.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-sk.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-sv-SE.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-zh-CN.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia-zh-TW.xml25
-rw-r--r--comm/suite/components/search/searchplugins/wikipedia.xml24
-rw-r--r--comm/suite/components/search/searchplugins/wolnelektury-pl.xml22
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-NO.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-ar.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-bid-zh-TW.xml18
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-br.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-de.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-en-GB.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-es.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-fi.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-fr.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-it.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-jp.xml18
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-nl.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-sv-SE.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-zh-CN.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo-zh-TW.xml25
-rw-r--r--comm/suite/components/search/searchplugins/yahoo.xml25
-rw-r--r--comm/suite/components/search/searchplugins/zoznam-sk.xml13
-rw-r--r--comm/suite/components/security/content/prefs/pref-certs.js32
-rw-r--r--comm/suite/components/security/content/prefs/pref-certs.xul100
-rw-r--r--comm/suite/components/security/content/prefs/pref-passwords.js31
-rw-r--r--comm/suite/components/security/content/prefs/pref-passwords.xul82
-rw-r--r--comm/suite/components/security/content/prefs/pref-ssl.js82
-rw-r--r--comm/suite/components/security/content/prefs/pref-ssl.xul120
-rw-r--r--comm/suite/components/security/jar.mn11
-rw-r--r--comm/suite/components/security/moz.build6
-rw-r--r--comm/suite/components/sessionstore/XPathGenerator.jsm97
-rw-r--r--comm/suite/components/sessionstore/content/aboutSessionRestore.js291
-rw-r--r--comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml84
-rw-r--r--comm/suite/components/sessionstore/jar.mn7
-rw-r--r--comm/suite/components/sessionstore/moz.build24
-rw-r--r--comm/suite/components/sessionstore/nsISessionStartup.idl39
-rw-r--r--comm/suite/components/sessionstore/nsISessionStore.idl216
-rw-r--r--comm/suite/components/sessionstore/nsSessionStartup.js223
-rw-r--r--comm/suite/components/sessionstore/nsSessionStartup.manifest11
-rw-r--r--comm/suite/components/sessionstore/nsSessionStore.js4174
-rw-r--r--comm/suite/components/shell/ShellService.jsm110
-rw-r--r--comm/suite/components/shell/content/setDesktopBackground.js78
-rw-r--r--comm/suite/components/shell/content/setDesktopBackground.xul49
-rw-r--r--comm/suite/components/shell/jar.mn7
-rw-r--r--comm/suite/components/shell/moz.build49
-rw-r--r--comm/suite/components/shell/nsGNOMEShellService.cpp463
-rw-r--r--comm/suite/components/shell/nsGNOMEShellService.h37
-rw-r--r--comm/suite/components/shell/nsIGNOMEShellService.idl18
-rw-r--r--comm/suite/components/shell/nsIMacShellService.idl14
-rw-r--r--comm/suite/components/shell/nsIShellService.idl98
-rw-r--r--comm/suite/components/shell/nsMacShellService.cpp398
-rw-r--r--comm/suite/components/shell/nsMacShellService.h36
-rw-r--r--comm/suite/components/shell/nsSetDefault.js53
-rw-r--r--comm/suite/components/shell/nsSetDefault.manifest3
-rw-r--r--comm/suite/components/shell/nsShellService.h11
-rw-r--r--comm/suite/components/shell/nsWindowsShellService.cpp793
-rw-r--r--comm/suite/components/shell/nsWindowsShellService.h41
-rw-r--r--comm/suite/components/sidebar/SuiteSidebar.manifest4
-rw-r--r--comm/suite/components/sidebar/content/PageNotFound.xul13
-rw-r--r--comm/suite/components/sidebar/content/customize-panel.js43
-rw-r--r--comm/suite/components/sidebar/content/customize-panel.xul23
-rw-r--r--comm/suite/components/sidebar/content/customize.js692
-rw-r--r--comm/suite/components/sidebar/content/customize.xul137
-rw-r--r--comm/suite/components/sidebar/content/preview.js15
-rw-r--r--comm/suite/components/sidebar/content/preview.xul30
-rw-r--r--comm/suite/components/sidebar/content/sidebarBindings.xml34
-rw-r--r--comm/suite/components/sidebar/content/sidebarOverlay.css78
-rw-r--r--comm/suite/components/sidebar/content/sidebarOverlay.js1704
-rw-r--r--comm/suite/components/sidebar/content/sidebarOverlay.xul247
-rw-r--r--comm/suite/components/sidebar/jar.mn16
-rw-r--r--comm/suite/components/sidebar/moz.build18
-rw-r--r--comm/suite/components/sidebar/nsISidebar.idl26
-rw-r--r--comm/suite/components/sidebar/nsSidebar.js348
-rw-r--r--comm/suite/components/sync/content/aboutSyncTabs-bindings.xml46
-rw-r--r--comm/suite/components/sync/content/aboutSyncTabs.css11
-rw-r--r--comm/suite/components/sync/content/aboutSyncTabs.js293
-rw-r--r--comm/suite/components/sync/content/aboutSyncTabs.xul69
-rw-r--r--comm/suite/components/sync/content/syncAddDevice.js142
-rw-r--r--comm/suite/components/sync/content/syncAddDevice.xul128
-rw-r--r--comm/suite/components/sync/content/syncGenericChange.js232
-rw-r--r--comm/suite/components/sync/content/syncGenericChange.xul116
-rw-r--r--comm/suite/components/sync/content/syncKey.xhtml49
-rw-r--r--comm/suite/components/sync/content/syncNotification.xml93
-rw-r--r--comm/suite/components/sync/content/syncQuota.js252
-rw-r--r--comm/suite/components/sync/content/syncQuota.xul62
-rw-r--r--comm/suite/components/sync/content/syncSetup.js961
-rw-r--r--comm/suite/components/sync/content/syncSetup.xul482
-rw-r--r--comm/suite/components/sync/content/syncUI.js454
-rw-r--r--comm/suite/components/sync/content/syncUtils.js224
-rw-r--r--comm/suite/components/sync/jar.mn21
-rw-r--r--comm/suite/components/sync/moz.build7
-rw-r--r--comm/suite/components/tests/browser/browser.ini72
-rw-r--r--comm/suite/components/tests/browser/browser_339445.js34
-rw-r--r--comm/suite/components/tests/browser/browser_339445_sample.html17
-rw-r--r--comm/suite/components/tests/browser/browser_345898.js45
-rw-r--r--comm/suite/components/tests/browser/browser_346337.js122
-rw-r--r--comm/suite/components/tests/browser/browser_346337_sample.html37
-rw-r--r--comm/suite/components/tests/browser/browser_350525.js100
-rw-r--r--comm/suite/components/tests/browser/browser_354894.js459
-rw-r--r--comm/suite/components/tests/browser/browser_367052.js38
-rw-r--r--comm/suite/components/tests/browser/browser_393716.js74
-rw-r--r--comm/suite/components/tests/browser/browser_394759_basic.js77
-rw-r--r--comm/suite/components/tests/browser/browser_394759_behavior.js66
-rw-r--r--comm/suite/components/tests/browser/browser_408470.js57
-rw-r--r--comm/suite/components/tests/browser/browser_408470_sample.html19
-rw-r--r--comm/suite/components/tests/browser/browser_423132.js86
-rw-r--r--comm/suite/components/tests/browser/browser_423132_sample.html13
-rw-r--r--comm/suite/components/tests/browser/browser_447951.js50
-rw-r--r--comm/suite/components/tests/browser/browser_447951_sample.html4
-rw-r--r--comm/suite/components/tests/browser/browser_448741.js62
-rw-r--r--comm/suite/components/tests/browser/browser_454908.js52
-rw-r--r--comm/suite/components/tests/browser/browser_454908_sample.html8
-rw-r--r--comm/suite/components/tests/browser/browser_456342.js47
-rw-r--r--comm/suite/components/tests/browser/browser_456342_sample.xhtml28
-rw-r--r--comm/suite/components/tests/browser/browser_461634.js88
-rw-r--r--comm/suite/components/tests/browser/browser_463206.js64
-rw-r--r--comm/suite/components/tests/browser/browser_463206_sample.html10
-rw-r--r--comm/suite/components/tests/browser/browser_465215.js39
-rw-r--r--comm/suite/components/tests/browser/browser_465223.js60
-rw-r--r--comm/suite/components/tests/browser/browser_466937.js43
-rw-r--r--comm/suite/components/tests/browser/browser_466937_sample.html21
-rw-r--r--comm/suite/components/tests/browser/browser_477657.js85
-rw-r--r--comm/suite/components/tests/browser/browser_480893.js58
-rw-r--r--comm/suite/components/tests/browser/browser_483330.js37
-rw-r--r--comm/suite/components/tests/browser/browser_485482.js36
-rw-r--r--comm/suite/components/tests/browser/browser_485482_sample.html12
-rw-r--r--comm/suite/components/tests/browser/browser_490040.js139
-rw-r--r--comm/suite/components/tests/browser/browser_491168.js64
-rw-r--r--comm/suite/components/tests/browser/browser_491577.js119
-rw-r--r--comm/suite/components/tests/browser/browser_493467.js48
-rw-r--r--comm/suite/components/tests/browser/browser_500328.js115
-rw-r--r--comm/suite/components/tests/browser/browser_514751.js59
-rw-r--r--comm/suite/components/tests/browser/browser_522545.js280
-rw-r--r--comm/suite/components/tests/browser/browser_524745.js59
-rw-r--r--comm/suite/components/tests/browser/browser_526613.js71
-rw-r--r--comm/suite/components/tests/browser/browser_528776.js29
-rw-r--r--comm/suite/components/tests/browser/browser_581937.js38
-rw-r--r--comm/suite/components/tests/browser/browser_586068-cascaded_restore.js730
-rw-r--r--comm/suite/components/tests/browser/browser_597315.js64
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_a.html5
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_b.html10
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_c.html5
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_c1.html5
-rwxr-xr-xcomm/suite/components/tests/browser/browser_597315_c2.html5
-rw-r--r--comm/suite/components/tests/browser/browser_597315_index.html10
-rw-r--r--comm/suite/components/tests/browser/browser_607016.js120
-rw-r--r--comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js362
-rw-r--r--comm/suite/components/tests/browser/browser_625257.js87
-rw-r--r--comm/suite/components/tests/browser/browser_636279.js102
-rw-r--r--comm/suite/components/tests/browser/browser_637020.js65
-rw-r--r--comm/suite/components/tests/browser/browser_637020_slow.sjs18
-rw-r--r--comm/suite/components/tests/browser/browser_645428.js22
-rw-r--r--comm/suite/components/tests/browser/browser_665702-state_session.js25
-rw-r--r--comm/suite/components/tests/browser/browser_687710.js44
-rw-r--r--comm/suite/components/tests/browser/browser_687710_2.js64
-rw-r--r--comm/suite/components/tests/browser/browser_694378.js33
-rw-r--r--comm/suite/components/tests/browser/browser_bug431826.js42
-rw-r--r--comm/suite/components/tests/browser/browser_isempty.js28
-rw-r--r--comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js87
-rw-r--r--comm/suite/components/tests/browser/frameLeft.html8
-rw-r--r--comm/suite/components/tests/browser/frameRight.html8
-rw-r--r--comm/suite/components/tests/browser/framedPage.html9
-rw-r--r--comm/suite/components/tests/browser/head.js151
-rw-r--r--comm/suite/components/tests/chrome/chrome.ini4
-rw-r--r--comm/suite/components/tests/chrome/test_idcheck.xul302
-rw-r--r--comm/suite/config/mozconfigs/common27
-rw-r--r--comm/suite/config/mozconfigs/linux32/debug21
-rw-r--r--comm/suite/config/mozconfigs/linux32/l10n-mozconfig4
-rw-r--r--comm/suite/config/mozconfigs/linux32/nightly25
-rw-r--r--comm/suite/config/mozconfigs/linux32/release25
-rw-r--r--comm/suite/config/mozconfigs/linux32/release-l10n4
-rw-r--r--comm/suite/config/mozconfigs/linux64/debug21
-rw-r--r--comm/suite/config/mozconfigs/linux64/l10n-mozconfig4
-rw-r--r--comm/suite/config/mozconfigs/linux64/nightly22
-rw-r--r--comm/suite/config/mozconfigs/linux64/release22
-rw-r--r--comm/suite/config/mozconfigs/linux64/release-l10n4
-rw-r--r--comm/suite/config/mozconfigs/linux64/source7
-rw-r--r--comm/suite/config/mozconfigs/macosx64/debug17
-rw-r--r--comm/suite/config/mozconfigs/macosx64/l10n-mozconfig16
-rw-r--r--comm/suite/config/mozconfigs/macosx64/nightly20
-rw-r--r--comm/suite/config/mozconfigs/macosx64/release17
-rw-r--r--comm/suite/config/mozconfigs/macosx64/release-l10n16
-rw-r--r--comm/suite/config/mozconfigs/mozconfig.gtk22
-rw-r--r--comm/suite/config/mozconfigs/mozconfig.linux.common14
-rw-r--r--comm/suite/config/mozconfigs/mozconfig.linux.l10n.common20
-rw-r--r--comm/suite/config/mozconfigs/mozconfig.macosx.common10
-rw-r--r--comm/suite/config/mozconfigs/mozconfig.win.common8
-rw-r--r--comm/suite/config/mozconfigs/win32/debug21
-rw-r--r--comm/suite/config/mozconfigs/win32/l10n-mozconfig20
-rw-r--r--comm/suite/config/mozconfigs/win32/nightly26
-rw-r--r--comm/suite/config/mozconfigs/win32/release24
-rw-r--r--comm/suite/config/mozconfigs/win32/release-l10n21
-rw-r--r--comm/suite/config/mozconfigs/win64/debug28
-rw-r--r--comm/suite/config/mozconfigs/win64/l10n-mozconfig26
-rw-r--r--comm/suite/config/mozconfigs/win64/nightly30
-rw-r--r--comm/suite/config/mozconfigs/win64/release30
-rw-r--r--comm/suite/config/mozconfigs/win64/release-l10n27
-rw-r--r--comm/suite/config/version.txt1
-rw-r--r--comm/suite/config/version_display.txt1
-rwxr-xr-xcomm/suite/confvars.sh30
-rw-r--r--comm/suite/editor/base/content/ComposerCommands.js4051
-rw-r--r--comm/suite/editor/base/content/EditorAllTags.css802
-rw-r--r--comm/suite/editor/base/content/EditorContent.css62
-rw-r--r--comm/suite/editor/base/content/EditorContextMenu.js122
-rw-r--r--comm/suite/editor/base/content/EditorContextMenuOverlay.xhtml171
-rw-r--r--comm/suite/editor/base/content/StructBarContextMenu.js179
-rw-r--r--comm/suite/editor/base/content/composerOverlay.xhtml28
-rw-r--r--comm/suite/editor/base/content/editingOverlay.js387
-rw-r--r--comm/suite/editor/base/content/editingOverlay.xhtml247
-rw-r--r--comm/suite/editor/base/content/editor.js3383
-rw-r--r--comm/suite/editor/base/content/editor.xhtml402
-rw-r--r--comm/suite/editor/base/content/editorApplicationOverlay.js161
-rw-r--r--comm/suite/editor/base/content/editorOverlay.xhtml1504
-rw-r--r--comm/suite/editor/base/content/editorTasksOverlay.xhtml31
-rw-r--r--comm/suite/editor/base/content/editorUtilities.js1014
-rw-r--r--comm/suite/editor/base/content/images/bringtofront-disabled.pngbin0 -> 155 bytes
-rw-r--r--comm/suite/editor/base/content/images/bringtofront.pngbin0 -> 155 bytes
-rw-r--r--comm/suite/editor/base/content/images/sendtoback-disabled.pngbin0 -> 156 bytes
-rw-r--r--comm/suite/editor/base/content/images/sendtoback.pngbin0 -> 156 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-a.pngbin0 -> 185 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-abr.pngbin0 -> 243 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-acr.pngbin0 -> 290 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-adr.pngbin0 -> 259 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-anchor.pngbin0 -> 171 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-app.pngbin0 -> 251 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ara.pngbin0 -> 246 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-b.pngbin0 -> 177 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-bas.pngbin0 -> 240 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-bdo.pngbin0 -> 211 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-big.pngbin0 -> 213 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-blq.pngbin0 -> 294 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-body.pngbin0 -> 247 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-br.pngbin0 -> 204 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-bsf.pngbin0 -> 274 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-btn.pngbin0 -> 259 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-cit.pngbin0 -> 214 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-clg.pngbin0 -> 261 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-cod.pngbin0 -> 214 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-col.pngbin0 -> 201 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-cpt.pngbin0 -> 273 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ctr.pngbin0 -> 249 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-dd.pngbin0 -> 188 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-del.pngbin0 -> 191 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-dfn.pngbin0 -> 210 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-dir.pngbin0 -> 200 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-div.pngbin0 -> 218 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-dl.pngbin0 -> 185 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-dt.pngbin0 -> 187 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-em.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-fld.pngbin0 -> 239 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-fnt.pngbin0 -> 228 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-for.pngbin0 -> 237 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-frm.pngbin0 -> 240 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-fst.pngbin0 -> 261 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-h1.pngbin0 -> 184 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-h2.pngbin0 -> 194 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-h3.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-h4.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-h5.pngbin0 -> 197 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-h6.pngbin0 -> 195 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-hed.pngbin0 -> 241 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-hr.pngbin0 -> 194 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-html.pngbin0 -> 222 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-i.pngbin0 -> 154 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ifr.pngbin0 -> 255 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-img.pngbin0 -> 214 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-inp.pngbin0 -> 235 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ins.pngbin0 -> 207 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-isx.pngbin0 -> 278 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-kbd.pngbin0 -> 223 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-lbl.pngbin0 -> 238 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-lgn.pngbin0 -> 253 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-li.pngbin0 -> 167 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-lnk.pngbin0 -> 219 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-lst.pngbin0 -> 263 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-map.pngbin0 -> 228 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-men.pngbin0 -> 236 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-met.pngbin0 -> 230 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-nbr.pngbin0 -> 301 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-nfr.pngbin0 -> 297 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-nsc.pngbin0 -> 253 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-obj.pngbin0 -> 253 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ol.pngbin0 -> 197 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-opg.pngbin0 -> 266 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-opt.pngbin0 -> 247 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-p.pngbin0 -> 170 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-pln.pngbin0 -> 219 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-pre.pngbin0 -> 211 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-prm.pngbin0 -> 248 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-q.pngbin0 -> 185 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-s.pngbin0 -> 181 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-scr.pngbin0 -> 257 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-slc.pngbin0 -> 249 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-sml.pngbin0 -> 245 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-smp.pngbin0 -> 246 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-spn.pngbin0 -> 246 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-stk.pngbin0 -> 261 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-stl.pngbin0 -> 245 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-stn.pngbin0 -> 277 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-sub.pngbin0 -> 221 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-sup.pngbin0 -> 218 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-tbd.pngbin0 -> 258 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-tbl.pngbin0 -> 240 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-td.pngbin0 -> 194 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-tft.pngbin0 -> 219 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-th.pngbin0 -> 189 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-thd.pngbin0 -> 253 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-tr.pngbin0 -> 197 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-tt.pngbin0 -> 179 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ttl.pngbin0 -> 218 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-txt.pngbin0 -> 289 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-u.pngbin0 -> 164 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-ul.pngbin0 -> 182 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-userdefined.pngbin0 -> 178 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-var.pngbin0 -> 230 bytes
-rw-r--r--comm/suite/editor/base/content/images/tag-xmp.pngbin0 -> 223 bytes
-rw-r--r--comm/suite/editor/base/content/publishprefs.js867
-rw-r--r--comm/suite/editor/base/jar.mn124
-rw-r--r--comm/suite/editor/base/moz.build6
-rw-r--r--comm/suite/editor/components/dialogs/content/EdAEAttributes.js973
-rw-r--r--comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js146
-rw-r--r--comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js367
-rw-r--r--comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js200
-rw-r--r--comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js342
-rw-r--r--comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml182
-rw-r--r--comm/suite/editor/components/dialogs/content/EdButtonProps.js146
-rw-r--r--comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml92
-rw-r--r--comm/suite/editor/components/dialogs/content/EdColorPicker.js297
-rw-r--r--comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml56
-rw-r--r--comm/suite/editor/components/dialogs/content/EdColorProps.js476
-rw-r--r--comm/suite/editor/components/dialogs/content/EdColorProps.xhtml134
-rw-r--r--comm/suite/editor/components/dialogs/content/EdConvertToTable.js326
-rw-r--r--comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml43
-rw-r--r--comm/suite/editor/components/dialogs/content/EdDialogCommon.js1038
-rw-r--r--comm/suite/editor/components/dialogs/content/EdDialogTemplate.js45
-rw-r--r--comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml23
-rw-r--r--comm/suite/editor/components/dialogs/content/EdDictionary.js164
-rw-r--r--comm/suite/editor/components/dialogs/content/EdDictionary.xhtml59
-rw-r--r--comm/suite/editor/components/dialogs/content/EdFieldSetProps.js196
-rw-r--r--comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml67
-rw-r--r--comm/suite/editor/components/dialogs/content/EdFormProps.js136
-rw-r--r--comm/suite/editor/components/dialogs/content/EdFormProps.xhtml98
-rw-r--r--comm/suite/editor/components/dialogs/content/EdHLineProps.js227
-rw-r--r--comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml80
-rw-r--r--comm/suite/editor/components/dialogs/content/EdImageDialog.js661
-rwxr-xr-xcomm/suite/editor/components/dialogs/content/EdImageLinkLoader.js145
-rw-r--r--comm/suite/editor/components/dialogs/content/EdImageProps.js293
-rw-r--r--comm/suite/editor/components/dialogs/content/EdImageProps.xhtml116
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInputImage.js189
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInputImage.xhtml104
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInputProps.js345
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInputProps.xhtml135
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsSrc.js160
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml42
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertChars.js409
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml55
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertMath.js330
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml60
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertTOC.js378
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml225
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertTable.js254
-rw-r--r--comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml82
-rw-r--r--comm/suite/editor/components/dialogs/content/EdLabelProps.js118
-rw-r--r--comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml66
-rw-r--r--comm/suite/editor/components/dialogs/content/EdLinkProps.js331
-rw-r--r--comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml79
-rw-r--r--comm/suite/editor/components/dialogs/content/EdListProps.js455
-rw-r--r--comm/suite/editor/components/dialogs/content/EdListProps.xhtml73
-rw-r--r--comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js159
-rw-r--r--comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml43
-rw-r--r--comm/suite/editor/components/dialogs/content/EdPageProps.js159
-rw-r--r--comm/suite/editor/components/dialogs/content/EdPageProps.xhtml50
-rw-r--r--comm/suite/editor/components/dialogs/content/EdReplace.js382
-rw-r--r--comm/suite/editor/components/dialogs/content/EdReplace.xhtml65
-rw-r--r--comm/suite/editor/components/dialogs/content/EdSelectProps.js770
-rw-r--r--comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml143
-rw-r--r--comm/suite/editor/components/dialogs/content/EdSnapToGrid.js62
-rw-r--r--comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml47
-rw-r--r--comm/suite/editor/components/dialogs/content/EdSpellCheck.js495
-rw-r--r--comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml113
-rw-r--r--comm/suite/editor/components/dialogs/content/EdTableProps.js1439
-rw-r--r--comm/suite/editor/components/dialogs/content/EdTableProps.xhtml287
-rw-r--r--comm/suite/editor/components/dialogs/content/EdTextAreaProps.js171
-rw-r--r--comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml115
-rw-r--r--comm/suite/editor/components/dialogs/content/EditConflict.js42
-rw-r--r--comm/suite/editor/components/dialogs/content/EditConflict.xhtml40
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublish.js558
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublish.xhtml132
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml66
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublishProgress.js391
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml66
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublishSettings.js343
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml50
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js155
-rw-r--r--comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml46
-rw-r--r--comm/suite/editor/components/dialogs/content/edImage.inc.xhtml248
-rw-r--r--comm/suite/editor/components/dialogs/jar.mn82
-rw-r--r--comm/suite/editor/components/dialogs/moz.build6
-rw-r--r--comm/suite/editor/components/moz.build10
-rw-r--r--comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml50
-rw-r--r--comm/suite/editor/components/prefs/content/pref-composer.xhtml84
-rw-r--r--comm/suite/editor/components/prefs/content/pref-editing.js187
-rw-r--r--comm/suite/editor/components/prefs/content/pref-editing.xhtml181
-rw-r--r--comm/suite/editor/components/prefs/jar.mn11
-rw-r--r--comm/suite/editor/components/prefs/moz.build6
-rw-r--r--comm/suite/editor/components/texzilla/content/TeXZilla.js339
-rw-r--r--comm/suite/editor/components/texzilla/jar.mn6
-rw-r--r--comm/suite/editor/components/texzilla/moz.build6
-rw-r--r--comm/suite/editor/modules/editorUtilities.jsm12
-rw-r--r--comm/suite/editor/moz.build22
-rw-r--r--comm/suite/editor/nsComposerCmdLineHandler.js64
-rw-r--r--comm/suite/editor/nsComposerCmdLineHandler.manifest3
-rw-r--r--comm/suite/editor/profile/composer.js70
-rw-r--r--comm/suite/extensions/built_in_addons.json1
-rw-r--r--comm/suite/extensions/debugQA/content/EditorInitPage.html159
-rw-r--r--comm/suite/extensions/debugQA/content/debugQAEditorOverlay.js207
-rw-r--r--comm/suite/extensions/debugQA/content/debugQAEditorOverlay.xul45
-rw-r--r--comm/suite/extensions/debugQA/content/debugQAMenuOverlay.js51
-rw-r--r--comm/suite/extensions/debugQA/content/debugQAMenuOverlay.xul150
-rw-r--r--comm/suite/extensions/debugQA/content/debugQANavigatorOverlay.xul54
-rw-r--r--comm/suite/extensions/debugQA/content/debugQATextEditorShell.xul189
-rw-r--r--comm/suite/extensions/debugQA/install.rdf.in38
-rw-r--r--comm/suite/extensions/debugQA/jar.mn16
-rw-r--r--comm/suite/extensions/debugQA/locales/en-US/debugQAEditorOverlay.dtd27
-rw-r--r--comm/suite/extensions/debugQA/locales/en-US/debugQANavigatorOverlay.properties6
-rw-r--r--comm/suite/extensions/debugQA/locales/jar.mn10
-rw-r--r--comm/suite/extensions/debugQA/locales/moz.build6
-rw-r--r--comm/suite/extensions/debugQA/moz.build15
-rw-r--r--comm/suite/extensions/jar.mn7
-rw-r--r--comm/suite/extensions/moz.build11
-rw-r--r--comm/suite/installer/Makefile.in196
-rw-r--r--comm/suite/installer/allowed-dupes.mn349
-rw-r--r--comm/suite/installer/license.txt373
-rw-r--r--comm/suite/installer/moz.build4
-rw-r--r--comm/suite/installer/package-manifest.in515
-rw-r--r--comm/suite/installer/removed-files.in290
-rw-r--r--comm/suite/installer/windows/Makefile.in55
-rw-r--r--comm/suite/installer/windows/app.tag4
-rw-r--r--comm/suite/installer/windows/moz.build10
-rw-r--r--comm/suite/installer/windows/nsis/custom.nsi68
-rw-r--r--comm/suite/installer/windows/nsis/defines.nsi.in64
-rw-r--r--comm/suite/installer/windows/nsis/installer.nsi942
-rw-r--r--comm/suite/installer/windows/nsis/shared.nsh1069
-rw-r--r--comm/suite/installer/windows/nsis/uninstaller.nsi580
-rw-r--r--comm/suite/installer/windows/nsis/updater_append.ini16
-rw-r--r--comm/suite/locales/Makefile.in165
-rw-r--r--comm/suite/locales/all-locales30
-rw-r--r--comm/suite/locales/en-US/chrome/branding/aboutRights.dtd82
-rw-r--r--comm/suite/locales/en-US/chrome/branding/aboutRights.properties9
-rw-r--r--comm/suite/locales/en-US/chrome/browser/linkToolbar.dtd48
-rw-r--r--comm/suite/locales/en-US/chrome/browser/mailNavigatorOverlay.dtd30
-rw-r--r--comm/suite/locales/en-US/chrome/browser/metadata.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/browser/metadata.properties19
-rw-r--r--comm/suite/locales/en-US/chrome/browser/navigator.dtd87
-rw-r--r--comm/suite/locales/en-US/chrome/browser/navigator.properties79
-rw-r--r--comm/suite/locales/en-US/chrome/browser/navigatorOverlay.dtd157
-rw-r--r--comm/suite/locales/en-US/chrome/browser/pageInfo.dtd111
-rw-r--r--comm/suite/locales/en-US/chrome/browser/pageInfo.properties77
-rw-r--r--comm/suite/locales/en-US/chrome/browser/region.properties26
-rw-r--r--comm/suite/locales/en-US/chrome/browser/tabbrowser.dtd27
-rw-r--r--comm/suite/locales/en-US/chrome/browser/tabbrowser.properties32
-rw-r--r--comm/suite/locales/en-US/chrome/browser/taskbar.properties21
-rw-r--r--comm/suite/locales/en-US/chrome/browser/webDeveloper.dtd72
-rw-r--r--comm/suite/locales/en-US/chrome/common/about.dtd70
-rw-r--r--comm/suite/locales/en-US/chrome/common/aboutPrivateBrowsing.dtd26
-rw-r--r--comm/suite/locales/en-US/chrome/common/aboutSessionRestore.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/common/aboutSyncTabs.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/common/askViewZoom.dtd6
-rw-r--r--comm/suite/locales/en-US/chrome/common/certError.dtd44
-rw-r--r--comm/suite/locales/en-US/chrome/common/console/console.dtd47
-rw-r--r--comm/suite/locales/en-US/chrome/common/console/console.properties17
-rw-r--r--comm/suite/locales/en-US/chrome/common/contentAreaCommands.dtd166
-rw-r--r--comm/suite/locales/en-US/chrome/common/contentAreaCommands.properties15
-rw-r--r--comm/suite/locales/en-US/chrome/common/customizeToolbar.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/common/customizeToolbar.properties13
-rw-r--r--comm/suite/locales/en-US/chrome/common/dataman/dataman.dtd156
-rw-r--r--comm/suite/locales/en-US/chrome/common/dataman/dataman.properties73
-rw-r--r--comm/suite/locales/en-US/chrome/common/datetimepicker.dtd7
-rw-r--r--comm/suite/locales/en-US/chrome/common/defaultClientDialog.dtd14
-rw-r--r--comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.dtd95
-rw-r--r--comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.properties146
-rw-r--r--comm/suite/locales/en-US/chrome/common/downloads/progressDialog.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/common/feeds/subscribe.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/common/feeds/subscribe.properties52
-rw-r--r--comm/suite/locales/en-US/chrome/common/gopherAddon.dtd9
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/cert_dialog_help.xhtml491
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/certs_help.xhtml366
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/certs_prefs_help.xhtml128
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/composer_help.xhtml2624
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_advanced.xhtml751
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_appearance.xhtml369
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_navigator.xhtml716
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/cs_priv_prefs_popup.xhtml149
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/customize_help.xhtml1451
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/developer_tools.xhtml50
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/glossary.xhtml912
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/help/help-glossary.rdf161
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/help-index1.rdf2247
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/help-indexAZ.rdf41
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/help-win.rdf52
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/helpFileLayout.css70
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/help_help.xhtml118
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/help/images/anchor-in-doc.gifbin0 -> 126 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/broken.gifbin0 -> 203 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/bullets.gifbin0 -> 154 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/columns.pngbin0 -> 139 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/composer_icon.pngbin0 -> 2124 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/help_nav.pngbin0 -> 2885 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/help_print.gifbin0 -> 723 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/image.gifbin0 -> 489 bytes
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/help/images/link.gifbin0 -> 419 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/locationbar.pngbin0 -> 2622 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/locationbar_search.pngbin0 -> 773 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_flag.pngbin0 -> 171 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_flag_column.pngbin0 -> 204 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_junk_column.pngbin0 -> 571 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_alert.pngbin0 -> 7661 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_balloon.pngbin0 -> 2123 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_trayicon.pngbin0 -> 222 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_quicksearch.pngbin0 -> 531 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_read.pngbin0 -> 110 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_read_column.pngbin0 -> 185 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/mail_unread.pngbin0 -> 144 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/menubar.pngbin0 -> 1130 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/numbers.gifbin0 -> 181 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/offline.pngbin0 -> 184 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/online.pngbin0 -> 180 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/personalbar.pngbin0 -> 7045 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/reload.gifbin0 -> 2860 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/search_navigation_toolbar.pngbin0 -> 817 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/sidebar.pngbin0 -> 11615 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/tabbed_browsing_bar.pngbin0 -> 4278 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/table.gifbin0 -> 512 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/task_mail.pngbin0 -> 2079 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/task_newmail.pngbin0 -> 2184 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/taskbar-ab.pngbin0 -> 2130 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/taskbar.pngbin0 -> 2349 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/threadbutton.pngbin0 -> 589 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/images/web-links.pngbin0 -> 226 bytes
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_account_settings.xhtml1225
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_addressbooks.xhtml572
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_blogs_and_feeds.xhtml388
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_getting_started.xhtml390
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_newsgroups.xhtml203
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_offline.xhtml504
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_organizing.xhtml848
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_preferences.xhtml792
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_security.xhtml463
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/mailnews_using_mail.xhtml1188
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/nav_help.xhtml1439
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/page_info_help.xhtml214
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/passwords_help.xhtml407
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/privacy_help.xhtml343
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/privsec_help.xhtml252
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/profiles_help.xhtml113
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/shortcuts.xhtml524
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/shortcuts_composer.xhtml184
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/shortcuts_mailnews.xhtml365
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/shortcuts_navigator.xhtml541
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/ssl_help.xhtml226
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/suite-toc.rdf1203
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/help/suitehelp.rdf58
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/using_certs_help.xhtml598
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/using_priv_help.xhtml1134
-rw-r--r--comm/suite/locales/en-US/chrome/common/help/welcome_help.xhtml96
-rw-r--r--comm/suite/locales/en-US/chrome/common/helpviewer/help.dtd56
-rw-r--r--comm/suite/locales/en-US/chrome/common/helpviewer/help.properties5
-rw-r--r--comm/suite/locales/en-US/chrome/common/migration/migration.dtd28
-rw-r--r--comm/suite/locales/en-US/chrome/common/migration/migration.properties52
-rw-r--r--comm/suite/locales/en-US/chrome/common/notification.dtd15
-rw-r--r--comm/suite/locales/en-US/chrome/common/notification.properties162
-rw-r--r--comm/suite/locales/en-US/chrome/common/openLocation.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/common/openLocation.properties11
-rw-r--r--comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.dtd46
-rw-r--r--comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.properties30
-rw-r--r--comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.dtd19
-rw-r--r--comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.properties23
-rw-r--r--comm/suite/locales/en-US/chrome/common/places/bookmarkProperties.properties19
-rw-r--r--comm/suite/locales/en-US/chrome/common/places/editBookmarkOverlay.dtd28
-rw-r--r--comm/suite/locales/en-US/chrome/common/places/places.dtd106
-rw-r--r--comm/suite/locales/en-US/chrome/common/places/places.properties100
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-advanced.dtd44
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-appearance.dtd37
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.properties10
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-applications.dtd17
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-applications.properties34
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-cache.dtd37
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/pref/pref-certs.dtd23
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-colors.dtd35
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-content.dtd39
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-cookies.dtd39
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-debugging.dtd40
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-download.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-findasyoutype.dtd21
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-fonts.dtd79
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-history.dtd25
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-http.dtd24
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-images.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-keynav.dtd27
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-languages.dtd30
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-languages.properties13
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-links.dtd29
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-locationbar.dtd42
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/pref/pref-masterpass.dtd13
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-media.dtd23
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-mousewheel.dtd36
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-navigator.dtd48
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-offlineapps.dtd24
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-passwords.dtd13
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-popups.dtd34
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-privatedata.dtd33
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-proxies-advanced.dtd32
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-proxies.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-scripts.dtd29
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/pref/pref-search.dtd21
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-security.dtd42
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-smartupdate.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-spelling.dtd20
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/pref/pref-ssl.dtd44
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-sync.dtd56
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/pref-tabs.dtd35
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/preferences.dtd58
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/prefutilities.dtd43
-rw-r--r--comm/suite/locales/en-US/chrome/common/pref/prefutilities.properties45
-rw-r--r--comm/suite/locales/en-US/chrome/common/printPreview.dtd39
-rw-r--r--comm/suite/locales/en-US/chrome/common/profile/profileSelection.dtd35
-rw-r--r--comm/suite/locales/en-US/chrome/common/profile/profileSelection.properties22
-rw-r--r--comm/suite/locales/en-US/chrome/common/quitDialog.properties29
-rw-r--r--comm/suite/locales/en-US/chrome/common/safeBrowsing.dtd33
-rw-r--r--comm/suite/locales/en-US/chrome/common/safeMode.dtd27
-rw-r--r--comm/suite/locales/en-US/chrome/common/sanitize.dtd66
-rw-r--r--comm/suite/locales/en-US/chrome/common/sanitize.properties9
-rw-r--r--comm/suite/locales/en-US/chrome/common/search/engineManager.dtd29
-rw-r--r--comm/suite/locales/en-US/chrome/common/search/engineManager.properties9
-rwxr-xr-xcomm/suite/locales/en-US/chrome/common/search/search-panel.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/common/search/search.properties6
-rw-r--r--comm/suite/locales/en-US/chrome/common/search/searchbar.dtd6
-rw-r--r--comm/suite/locales/en-US/chrome/common/setDesktopBackground.dtd19
-rw-r--r--comm/suite/locales/en-US/chrome/common/shellservice.properties7
-rw-r--r--comm/suite/locales/en-US/chrome/common/sidebar/customize.dtd25
-rw-r--r--comm/suite/locales/en-US/chrome/common/sidebar/preview.dtd5
-rw-r--r--comm/suite/locales/en-US/chrome/common/sidebar/sidebar.properties10
-rw-r--r--comm/suite/locales/en-US/chrome/common/sidebar/sidebarOverlay.dtd38
-rw-r--r--comm/suite/locales/en-US/chrome/common/sitePermissions.properties43
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncBrand.dtd6
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncGenericChange.properties37
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncKey.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncQuota.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncQuota.properties42
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncSetup.dtd116
-rw-r--r--comm/suite/locales/en-US/chrome/common/sync/syncSetup.properties50
-rw-r--r--comm/suite/locales/en-US/chrome/common/tasksOverlay.dtd64
-rw-r--r--comm/suite/locales/en-US/chrome/common/typeaheadfind.properties17
-rw-r--r--comm/suite/locales/en-US/chrome/common/utilityOverlay.dtd206
-rw-r--r--comm/suite/locales/en-US/chrome/common/utilityOverlay.properties35
-rw-r--r--comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.dtd10
-rw-r--r--comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.properties8
-rw-r--r--comm/suite/locales/en-US/chrome/common/viewZoomOverlay.dtd15
-rw-r--r--comm/suite/locales/en-US/chrome/common/viewZoomOverlay.properties32
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EdAdvancedEdit.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EdColorPicker.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EdConvertToTable.dtd15
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EdDialogOverlay.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EdNamedAnchorProperties.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditConflict.dtd10
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorButtonProperties.dtd27
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorColorProperties.dtd29
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorFieldSetProperties.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorFormProperties.dtd21
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorHLineProperties.dtd27
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorImageProperties.dtd79
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorInputProperties.dtd50
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertChars.dtd19
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertMath.dtd21
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertSource.dtd15
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTOC.dtd16
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTable.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorLabelProperties.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorLinkProperties.dtd6
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorListProperties.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorPageProperties.dtd17
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorPersonalDictionary.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublish.dtd65
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublishProgress.dtd16
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorReplace.dtd27
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorSaveAsCharset.dtd14
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorSelectProperties.dtd48
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorSnapToGrid.dtd15
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorSpellCheck.dtd38
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorTableProperties.dtd75
-rw-r--r--comm/suite/locales/en-US/chrome/editor/dialogs/EditorTextAreaProperties.dtd33
-rw-r--r--comm/suite/locales/en-US/chrome/editor/editingOverlay.dtd62
-rw-r--r--comm/suite/locales/en-US/chrome/editor/editor.dtd67
-rw-r--r--comm/suite/locales/en-US/chrome/editor/editor.properties208
-rw-r--r--comm/suite/locales/en-US/chrome/editor/editorOverlay.dtd368
-rw-r--r--comm/suite/locales/en-US/chrome/editor/editorSmileyOverlay.dtd58
-rw-r--r--comm/suite/locales/en-US/chrome/editor/prefs/editorPrefsOverlay.dtd12
-rw-r--r--comm/suite/locales/en-US/chrome/editor/prefs/pref-composer.dtd29
-rw-r--r--comm/suite/locales/en-US/chrome/editor/prefs/pref-editing.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/CustomHeaders.dtd11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/FilterEditor.dtd67
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/FilterListDialog.dtd37
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/SearchDialog.dtd38
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abAddressBookNameDialog.dtd5
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd152
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abMailListDialog.dtd19
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abMainWindow.dtd118
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abNewCardDialog.dtd7
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abResultsPaneOverlay.dtd52
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/abSelectAddressesDialog.dtd30
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties264
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/addressbook/ldapAutoCompErrs.properties104
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/appleMailImportMsgs.properties20
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/beckyImportMsgs.properties19
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/charsetTitles.properties80
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/addressingWidgetOverlay.dtd12
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.properties8
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties318
-rwxr-xr-xcomm/suite/locales/en-US/chrome/mailnews/compose/mailComposeEditorOverlay.dtd9
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/messengercompose.dtd137
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.properties21
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/custom.properties5
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/downloadheaders.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/fieldMapImport.dtd19
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/filter.properties111
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/folderProps.dtd70
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/folderWidgets.properties12
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/folderpane.dtd11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/gloda.properties104
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/imapMsgs.properties271
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/importDialog.dtd43
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/importMsgs.properties306
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/junkLog.dtd10
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/junkMailInfo.dtd11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/localMsgs.properties136
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailEditorOverlay.dtd7
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailKeysOverlay.dtd30
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailOverlay.dtd11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailTasksOverlay.dtd23
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailViewList.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailViewSetup.dtd10
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mailviews.properties13
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mapi/mapi.properties13
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/markByDate.dtd9
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/messenger.dtd564
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/messenger.properties521
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mime.properties156
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/mimeheader.properties39
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgAccountCentral.dtd24
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgHdrViewOverlay.dtd48
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgHdrViewPopup.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgPrintEngine.dtd11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgSynchronize.dtd23
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgViewPickerOverlay.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/msgmdn.properties18
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/newFolderDialog.dtd16
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/news.properties56
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/newsError.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/newsblog/am-newsblog.dtd17
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd55
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties93
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/offline.properties51
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/offlineStartup.properties8
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/outlookImportMsgs.properties72
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pgpmime.properties11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/AccountManager.dtd26
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/AccountWizard.dtd124
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-addressing.dtd49
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-advanced.dtd25
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-archiveoptions.dtd23
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-copies.dtd50
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-identities-list.dtd15
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-identity-edit.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-junk.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-main.dtd46
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.dtd33
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.properties6
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-offline.dtd57
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-server-advanced.dtd31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-server-top.dtd89
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/am-serverwithnoidentities.dtd6
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/mailPrefsOverlay.dtd21
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-addressing.dtd22
-rwxr-xr-xcomm/suite/locales/en-US/chrome/mailnews/pref/pref-character_encoding.dtd17
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-composing_messages.dtd56
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd45
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory.dtd17
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-formatting.dtd42
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-junk.dtd40
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-mailnews.dtd33
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-notifications.dtd46
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-offline.dtd38
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-receipts.dtd28
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-tags.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/pref-viewing_messages.dtd47
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/prefs.properties90
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.properties5
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/replicationProgress.properties20
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/pref/smtpEditOverlay.dtd24
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/region.properties31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/renameFolderDialog.dtd9
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/search-attributes.properties45
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/search-operators.properties31
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/search.properties28
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/searchTermOverlay.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/shutdownWindow.properties10
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime.properties11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.dtd37
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.properties41
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/certFetchingStatus.dtd9
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.dtd20
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.properties6
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.dtd18
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.properties13
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.properties11
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSecurityInfo.dtd14
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/smime/msgSecurityInfo.properties44
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/start.dtd34
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/subscribe.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/subscribe.properties14
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/tabmail.properties13
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/textImportMsgs.properties43
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/threadpane.dtd45
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/vCardImportMsgs.properties26
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/viewLog.dtd12
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/virtualFolderListDialog.dtd8
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/virtualFolderProperties.dtd22
-rw-r--r--comm/suite/locales/en-US/chrome/mailnews/wmImportMsgs.properties76
-rw-r--r--comm/suite/locales/en-US/chrome/mozldap/ldap.properties261
-rw-r--r--comm/suite/locales/en-US/crashreporter/crashreporter-override.ini9
-rw-r--r--comm/suite/locales/en-US/defines.inc9
-rw-r--r--comm/suite/locales/en-US/installer/windows/custom.properties86
-rw-r--r--comm/suite/locales/en-US/installer/windows/mui.properties64
-rw-r--r--comm/suite/locales/en-US/installer/windows/override.properties86
-rw-r--r--comm/suite/locales/en-US/profile/bookmarks.extra18
-rw-r--r--comm/suite/locales/en-US/profile/bookmarks.inc69
-rw-r--r--comm/suite/locales/en-US/profile/chrome/userChrome-example.css58
-rw-r--r--comm/suite/locales/en-US/profile/chrome/userContent-example.css47
-rw-r--r--comm/suite/locales/en-US/profile/panels.extra7
-rw-r--r--comm/suite/locales/en-US/suite-l10n.js8
-rw-r--r--comm/suite/locales/en-US/updater/updater.ini8
-rw-r--r--comm/suite/locales/filter.py72
-rw-r--r--comm/suite/locales/generic/install.rdf30
-rw-r--r--comm/suite/locales/generic/profile/bookmarks.html.in43
-rw-r--r--comm/suite/locales/generic/profile/mimeTypes.rdf86
-rw-r--r--comm/suite/locales/generic/profile/panels.rdf.in19
-rw-r--r--comm/suite/locales/jar.mn396
-rw-r--r--comm/suite/locales/l10n-beta.ini36
-rw-r--r--comm/suite/locales/l10n-central.ini36
-rw-r--r--comm/suite/locales/l10n.ini20
-rw-r--r--comm/suite/locales/l10n.toml126
-rw-r--r--comm/suite/locales/moz.build18
-rw-r--r--comm/suite/locales/shipped-locales25
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abCardOverlay.js1397
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abCardOverlay.xul515
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abCardViewOverlay.js527
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abCommon.js1185
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abEditCardDialog.xul18
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abEditListDialog.xul22
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abListOverlay.xul86
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abMailListDialog.xul34
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abNewCardDialog.xul35
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abResultsPaneOverlay.xul94
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.js399
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.xul92
-rw-r--r--comm/suite/mailnews/components/addrbook/content/abTrees.js332
-rw-r--r--comm/suite/mailnews/components/addrbook/content/addressbook-panel.js119
-rw-r--r--comm/suite/mailnews/components/addrbook/content/addressbook-panel.xul96
-rw-r--r--comm/suite/mailnews/components/addrbook/content/addressbook.js581
-rw-r--r--comm/suite/mailnews/components/addrbook/content/addressbook.xul722
-rw-r--r--comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.js24
-rw-r--r--comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.xul92
-rw-r--r--comm/suite/mailnews/components/addrbook/jar.mn24
-rw-r--r--comm/suite/mailnews/components/addrbook/moz.build8
-rw-r--r--comm/suite/mailnews/components/calendar/content/suite-overlay-addons.xhtml39
-rw-r--r--comm/suite/mailnews/components/calendar/content/suite-overlay-preferences.xhtml66
-rw-r--r--comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.js47
-rw-r--r--comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.xhtml39
-rw-r--r--comm/suite/mailnews/components/compose/content/MsgComposeCommands.js3936
-rw-r--r--comm/suite/mailnews/components/compose/content/addressingWidgetOverlay.js1167
-rw-r--r--comm/suite/mailnews/components/compose/content/mailComposeOverlay.xul16
-rw-r--r--comm/suite/mailnews/components/compose/content/messengercompose.xul720
-rw-r--r--comm/suite/mailnews/components/compose/content/msgComposeContextOverlay.xul23
-rw-r--r--comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.js30
-rw-r--r--comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.xul212
-rw-r--r--comm/suite/mailnews/components/compose/content/prefs/pref-formatting.js151
-rw-r--r--comm/suite/mailnews/components/compose/content/prefs/pref-formatting.xul120
-rw-r--r--comm/suite/mailnews/components/compose/jar.mn14
-rw-r--r--comm/suite/mailnews/components/compose/moz.build6
-rw-r--r--comm/suite/mailnews/components/moz.build11
-rw-r--r--comm/suite/mailnews/components/prefs/content/mailPrefsOverlay.xul102
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-character_encoding.js41
-rwxr-xr-xcomm/suite/mailnews/components/prefs/content/pref-character_encoding.xul111
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-junk.js45
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-junk.xul134
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-mailnews.js25
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-mailnews.xul141
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-notifications.js91
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-notifications.xul187
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-offline.js19
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-offline.xul121
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-receipts.js28
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-receipts.xul146
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-tags.js478
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-tags.xul83
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-viewing_messages.js26
-rw-r--r--comm/suite/mailnews/components/prefs/content/pref-viewing_messages.xul174
-rw-r--r--comm/suite/mailnews/components/prefs/jar.mn23
-rw-r--r--comm/suite/mailnews/components/prefs/moz.build6
-rw-r--r--comm/suite/mailnews/components/smime/content/msgCompSMIMEOverlay.js354
-rw-r--r--comm/suite/mailnews/components/smime/content/msgHdrViewSMIMEOverlay.js258
-rw-r--r--comm/suite/mailnews/components/smime/jar.mn15
-rw-r--r--comm/suite/mailnews/components/smime/moz.build6
-rw-r--r--comm/suite/mailnews/content/ABSearchDialog.js327
-rw-r--r--comm/suite/mailnews/content/ABSearchDialog.xul99
-rw-r--r--comm/suite/mailnews/content/FilterListDialog.js1037
-rw-r--r--comm/suite/mailnews/content/FilterListDialog.xul197
-rw-r--r--comm/suite/mailnews/content/SearchDialog.js729
-rw-r--r--comm/suite/mailnews/content/SearchDialog.xul178
-rw-r--r--comm/suite/mailnews/content/browserRequest.js107
-rw-r--r--comm/suite/mailnews/content/browserRequest.xul34
-rw-r--r--comm/suite/mailnews/content/commandglue.js989
-rw-r--r--comm/suite/mailnews/content/folderDisplay.js142
-rw-r--r--comm/suite/mailnews/content/folderPane.js2221
-rw-r--r--comm/suite/mailnews/content/folderPane.xul169
-rw-r--r--comm/suite/mailnews/content/mail-offline.js164
-rw-r--r--comm/suite/mailnews/content/mail3PaneWindowCommands.js1057
-rw-r--r--comm/suite/mailnews/content/mailCommands.js415
-rw-r--r--comm/suite/mailnews/content/mailContextMenus.js828
-rw-r--r--comm/suite/mailnews/content/mailEditorOverlay.xul61
-rw-r--r--comm/suite/mailnews/content/mailKeysOverlay.xul64
-rw-r--r--comm/suite/mailnews/content/mailOverlay.js30
-rw-r--r--comm/suite/mailnews/content/mailOverlay.xul29
-rw-r--r--comm/suite/mailnews/content/mailTasksOverlay.js250
-rw-r--r--comm/suite/mailnews/content/mailTasksOverlay.xul64
-rw-r--r--comm/suite/mailnews/content/mailViewList.js161
-rw-r--r--comm/suite/mailnews/content/mailViewList.xul79
-rw-r--r--comm/suite/mailnews/content/mailViewSetup.js119
-rw-r--r--comm/suite/mailnews/content/mailViewSetup.xul51
-rw-r--r--comm/suite/mailnews/content/mailWidgets.xml1946
-rw-r--r--comm/suite/mailnews/content/mailWindow.js593
-rw-r--r--comm/suite/mailnews/content/mailWindowOverlay.js2695
-rw-r--r--comm/suite/mailnews/content/mailWindowOverlay.xul1929
-rw-r--r--comm/suite/mailnews/content/messageWindow.js1044
-rw-r--r--comm/suite/mailnews/content/messageWindow.xul141
-rw-r--r--comm/suite/mailnews/content/messenger.css236
-rw-r--r--comm/suite/mailnews/content/messenger.xul275
-rw-r--r--comm/suite/mailnews/content/msgFolderPickerOverlay.js100
-rw-r--r--comm/suite/mailnews/content/msgHdrViewOverlay.js1971
-rw-r--r--comm/suite/mailnews/content/msgHdrViewOverlay.xul273
-rw-r--r--comm/suite/mailnews/content/msgMail3PaneWindow.js1265
-rw-r--r--comm/suite/mailnews/content/msgViewNavigation.js243
-rw-r--r--comm/suite/mailnews/content/msgViewPickerOverlay.js413
-rw-r--r--comm/suite/mailnews/content/nsDragAndDrop.js595
-rw-r--r--comm/suite/mailnews/content/phishingDetector.js173
-rw-r--r--comm/suite/mailnews/content/searchBar.js432
-rw-r--r--comm/suite/mailnews/content/searchTermOverlay.xul64
-rw-r--r--comm/suite/mailnews/content/start.xhtml69
-rw-r--r--comm/suite/mailnews/content/tabmail.js969
-rw-r--r--comm/suite/mailnews/content/tabmail.xml1583
-rw-r--r--comm/suite/mailnews/content/threadPane.js598
-rw-r--r--comm/suite/mailnews/content/threadPane.xul91
-rw-r--r--comm/suite/mailnews/jar.mn70
-rw-r--r--comm/suite/mailnews/modules/MailUtils.js100
-rw-r--r--comm/suite/mailnews/modules/moz.build8
-rw-r--r--comm/suite/mailnews/moz.build11
-rw-r--r--comm/suite/modules/Feeds.jsm50
-rw-r--r--comm/suite/modules/OfflineAppCacheHelper.jsm16
-rw-r--r--comm/suite/modules/OpenInTabsUtils.jsm83
-rw-r--r--comm/suite/modules/PermissionUI.jsm612
-rw-r--r--comm/suite/modules/RecentWindow.jsm66
-rw-r--r--comm/suite/modules/SitePermissions.jsm796
-rw-r--r--comm/suite/modules/ThemeVariableMap.jsm29
-rw-r--r--comm/suite/modules/WindowsJumpLists.jsm604
-rw-r--r--comm/suite/modules/WindowsPreviewPerTab.jsm872
-rw-r--r--comm/suite/modules/moz.build20
-rw-r--r--comm/suite/modules/test/unit/head.js135
-rw-r--r--comm/suite/modules/test/unit/test_browser_sanitizer.js339
-rw-r--r--comm/suite/modules/test/unit/xpcshell.ini6
-rw-r--r--comm/suite/moz.build33
-rw-r--r--comm/suite/moz.configure117
-rw-r--r--comm/suite/themes/classic/Makefile.in9
-rw-r--r--comm/suite/themes/classic/README19
-rw-r--r--comm/suite/themes/classic/communicator/about.css58
-rw-r--r--comm/suite/themes/classic/communicator/aboutPrivateBrowsing.css86
-rw-r--r--comm/suite/themes/classic/communicator/aboutSessionRestore.css52
-rw-r--r--comm/suite/themes/classic/communicator/aboutSupport.css139
-rw-r--r--comm/suite/themes/classic/communicator/aboutSyncTabs.css88
-rw-r--r--comm/suite/themes/classic/communicator/blockedSite.css20
-rw-r--r--comm/suite/themes/classic/communicator/brand.css59
-rw-r--r--comm/suite/themes/classic/communicator/brand/throbber-anim.pngbin0 -> 81284 bytes
-rw-r--r--comm/suite/themes/classic/communicator/brand/throbber-single.pngbin0 -> 2174 bytes
-rw-r--r--comm/suite/themes/classic/communicator/brand/throbber16-anim.pngbin0 -> 28831 bytes
-rw-r--r--comm/suite/themes/classic/communicator/brand/throbber16-single.pngbin0 -> 909 bytes
-rw-r--r--comm/suite/themes/classic/communicator/button.css235
-rw-r--r--comm/suite/themes/classic/communicator/certError.css81
-rw-r--r--comm/suite/themes/classic/communicator/common.css66
-rw-r--r--comm/suite/themes/classic/communicator/communicator.css321
-rw-r--r--comm/suite/themes/classic/communicator/communicatorBindings.xml22
-rw-r--r--comm/suite/themes/classic/communicator/config.css66
-rw-r--r--comm/suite/themes/classic/communicator/console/console-error-caret.pngbin0 -> 103 bytes
-rw-r--r--comm/suite/themes/classic/communicator/console/console-error-dash.pngbin0 -> 77 bytes
-rw-r--r--comm/suite/themes/classic/communicator/console/console-toolbar.pngbin0 -> 5787 bytes
-rw-r--r--comm/suite/themes/classic/communicator/console/console.css208
-rw-r--r--comm/suite/themes/classic/communicator/console/itemSelected.pngbin0 -> 459 bytes
-rw-r--r--comm/suite/themes/classic/communicator/customizeToolbar.css23
-rw-r--r--comm/suite/themes/classic/communicator/dataman/dataman.css31
-rw-r--r--comm/suite/themes/classic/communicator/dataman/datamanIcon-16.pngbin0 -> 318 bytes
-rw-r--r--comm/suite/themes/classic/communicator/datetimepicker.css97
-rw-r--r--comm/suite/themes/classic/communicator/dialogs.css11
-rw-r--r--comm/suite/themes/classic/communicator/downloads/dl-remove.pngbin0 -> 385 bytes
-rw-r--r--comm/suite/themes/classic/communicator/downloads/downloadButtons.pngbin0 -> 952 bytes
-rw-r--r--comm/suite/themes/classic/communicator/downloads/downloadmanager.css95
-rw-r--r--comm/suite/themes/classic/communicator/feed-subscribe-ui.css20
-rw-r--r--comm/suite/themes/classic/communicator/feed-subscribe.css148
-rw-r--r--comm/suite/themes/classic/communicator/fullscreen-video.css33
-rw-r--r--comm/suite/themes/classic/communicator/helpviewer/Toolbar-rtl.pngbin0 -> 12032 bytes
-rw-r--r--comm/suite/themes/classic/communicator/helpviewer/help.css136
-rw-r--r--comm/suite/themes/classic/communicator/icons/alwaysAsk.pngbin0 -> 446 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/application.pngbin0 -> 390 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/close-button.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/closeFullScreenVideo.pngbin0 -> 373 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/communicatoricons-small.pngbin0 -> 14182 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/communicatoricons.pngbin0 -> 22603 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/feedIcon.pngbin0 -> 1790 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/feedIcon16.pngbin0 -> 699 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/geo.pngbin0 -> 918 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/geolocation-16.pngbin0 -> 704 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/geolocation-64.pngbin0 -> 8424 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/identity.pngbin0 -> 616 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/information-48.pngbin0 -> 2598 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/key-16.pngbin0 -> 773 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/key-64.pngbin0 -> 6136 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/loading.pngbin0 -> 558 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/lock-broken-16.pngbin0 -> 408 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/lock-broken.pngbin0 -> 357 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/lock-insecure-16.pngbin0 -> 379 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/lock-insecure.pngbin0 -> 369 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/lock-secure-16.pngbin0 -> 372 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/lock-secure.pngbin0 -> 367 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/notification-16.pngbin0 -> 524 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/notification-64.pngbin0 -> 3373 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/notification-icons.svg104
-rw-r--r--comm/suite/themes/classic/communicator/icons/offline.pngbin0 -> 413 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/online.pngbin0 -> 403 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/question-48.pngbin0 -> 2849 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/save.pngbin0 -> 923 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/search.pngbin0 -> 1393 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-cool.pngbin0 -> 976 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-cry.pngbin0 -> 1025 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-embarrassed.pngbin0 -> 939 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-foot.pngbin0 -> 932 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-frown.pngbin0 -> 992 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-innocent.pngbin0 -> 941 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-kiss.pngbin0 -> 943 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-laughing.pngbin0 -> 1016 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-money.pngbin0 -> 967 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-sealed.pngbin0 -> 916 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-smile.pngbin0 -> 986 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-surprise.pngbin0 -> 984 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-tongue.pngbin0 -> 936 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-undecided.pngbin0 -> 951 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-wink.pngbin0 -> 983 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/smileys/smiley-yell.pngbin0 -> 942 bytes
-rw-r--r--comm/suite/themes/classic/communicator/icons/warning-24.pngbin0 -> 914 bytes
-rw-r--r--comm/suite/themes/classic/communicator/numberbox.css23
-rw-r--r--comm/suite/themes/classic/communicator/places/allBookmarks.pngbin0 -> 565 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark-folder-closed.pngbin0 -> 506 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark-folder-dis.pngbin0 -> 351 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark-folder-open.pngbin0 -> 596 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark-item-dis.pngbin0 -> 405 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark-item-updated.pngbin0 -> 287 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark-item.svg13
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmark.pngbin0 -> 2171 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmarks.css131
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmarksMenu.pngbin0 -> 371 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmarksToolbar.css107
-rw-r--r--comm/suite/themes/classic/communicator/places/bookmarksToolbar.pngbin0 -> 272 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/calendar.pngbin0 -> 604 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/editBookmarkOverlay.css84
-rw-r--r--comm/suite/themes/classic/communicator/places/history.pngbin0 -> 821 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/livemark-folder.pngbin0 -> 566 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/organizer.css17
-rw-r--r--comm/suite/themes/classic/communicator/places/query.pngbin0 -> 509 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/tag.pngbin0 -> 517 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/toolbarDropMarker.pngbin0 -> 468 bytes
-rw-r--r--comm/suite/themes/classic/communicator/places/unsortedBookmarks.pngbin0 -> 515 bytes
-rw-r--r--comm/suite/themes/classic/communicator/preferences.css80
-rw-r--r--comm/suite/themes/classic/communicator/prefpanels.css84
-rw-r--r--comm/suite/themes/classic/communicator/profile/migrate.pngbin0 -> 197 bytes
-rw-r--r--comm/suite/themes/classic/communicator/profile/profile.css65
-rw-r--r--comm/suite/themes/classic/communicator/profile/profileManager.css18
-rw-r--r--comm/suite/themes/classic/communicator/profile/profileicon-large.pngbin0 -> 105 bytes
-rw-r--r--comm/suite/themes/classic/communicator/search/engineManager.css14
-rw-r--r--comm/suite/themes/classic/communicator/search/mainwindow-dropdown-arrow.pngbin0 -> 450 bytes
-rw-r--r--comm/suite/themes/classic/communicator/search/search-glass.pngbin0 -> 1350 bytes
-rw-r--r--comm/suite/themes/classic/communicator/search/search.css18
-rw-r--r--comm/suite/themes/classic/communicator/search/searchbar.css74
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/customize.css39
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/preview.css21
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/sbtab-twisty-open.pngbin0 -> 114 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/sbtab-twisty.pngbin0 -> 114 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/sidebar.css173
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/sidebarBindings.xml21
-rw-r--r--comm/suite/themes/classic/communicator/sidebar/sidebarListView.css17
-rw-r--r--comm/suite/themes/classic/communicator/smileys.css132
-rw-r--r--comm/suite/themes/classic/communicator/spinbuttons.css24
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-16-throbber.pngbin0 -> 6227 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-16.pngbin0 -> 1789 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-32-throbber.pngbin0 -> 25548 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-32.pngbin0 -> 3365 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-bg.pngbin0 -> 21309 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-desktopIcon.pngbin0 -> 260 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/sync-mobileIcon.pngbin0 -> 301 bytes
-rw-r--r--comm/suite/themes/classic/communicator/sync/syncCommon.css45
-rw-r--r--comm/suite/themes/classic/communicator/sync/syncQuota.css26
-rw-r--r--comm/suite/themes/classic/communicator/sync/syncSetup.css129
-rwxr-xr-xcomm/suite/themes/classic/communicator/taskbar/taskbar.pngbin0 -> 8169 bytes
-rw-r--r--comm/suite/themes/classic/communicator/taskbar/taskmenu-abook.pngbin0 -> 835 bytes
-rw-r--r--comm/suite/themes/classic/communicator/taskbar/taskmenu-browser.pngbin0 -> 682 bytes
-rw-r--r--comm/suite/themes/classic/communicator/taskbar/taskmenu-composer.pngbin0 -> 870 bytes
-rw-r--r--comm/suite/themes/classic/communicator/taskbar/taskmenu-mailnews.pngbin0 -> 707 bytes
-rw-r--r--comm/suite/themes/classic/communicator/tasksOverlay.css98
-rw-r--r--comm/suite/themes/classic/communicator/toolbar.css118
-rw-r--r--comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow-clps.pngbin0 -> 102 bytes
-rw-r--r--comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow.pngbin0 -> 105 bytes
-rw-r--r--comm/suite/themes/classic/communicator/toolbar/tbgrip-texture.pngbin0 -> 85 bytes
-rw-r--r--comm/suite/themes/classic/communicator/viewSourceOverlay.css18
-rw-r--r--comm/suite/themes/classic/communicator/xpinstall/xpinstall.css32
-rw-r--r--comm/suite/themes/classic/editor/EditorDialog.css267
-rw-r--r--comm/suite/themes/classic/editor/editor.css85
-rw-r--r--comm/suite/themes/classic/editor/editorFormatToolbar.css574
-rw-r--r--comm/suite/themes/classic/editor/editorModeToolbar.css39
-rw-r--r--comm/suite/themes/classic/editor/editorPrimaryToolbar.css569
-rw-r--r--comm/suite/themes/classic/editor/icons/btn2.pngbin0 -> 1939 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/editmode-html.pngbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/editmode-normal.pngbin0 -> 150 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/editmode-preview.pngbin0 -> 172 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/editmode-tags.pngbin0 -> 173 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/editoricons-small.pngbin0 -> 48863 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/editoricons.pngbin0 -> 85605 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/img-align-bottom.pngbin0 -> 219 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/img-align-left.pngbin0 -> 205 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/img-align-middle.pngbin0 -> 216 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/img-align-right.pngbin0 -> 197 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/img-align-top.pngbin0 -> 213 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/multicolor.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/progress-busy.pngbin0 -> 1439 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/progress-done.pngbin0 -> 161 bytes
-rw-r--r--comm/suite/themes/classic/editor/icons/progress-failed.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/themes/classic/icon.pngbin0 -> 1321 bytes
-rw-r--r--comm/suite/themes/classic/install.rdf48
-rw-r--r--comm/suite/themes/classic/jar.mn520
-rw-r--r--comm/suite/themes/classic/linux/communicator/communicator.css331
-rw-r--r--comm/suite/themes/classic/linux/communicator/feed-subscribe.css149
-rw-r--r--comm/suite/themes/classic/linux/communicator/numberbox.css29
-rw-r--r--comm/suite/themes/classic/linux/communicator/preferences.css66
-rw-r--r--comm/suite/themes/classic/linux/communicator/sanitizeDialog.css49
-rw-r--r--comm/suite/themes/classic/linux/communicator/spinbuttons.css24
-rw-r--r--comm/suite/themes/classic/mac/communicator/aboutPrivateBrowsing.css85
-rw-r--r--comm/suite/themes/classic/mac/communicator/aboutSessionRestore.css40
-rw-r--r--comm/suite/themes/classic/mac/communicator/button.css65
-rw-r--r--comm/suite/themes/classic/mac/communicator/communicator.css285
-rw-r--r--comm/suite/themes/classic/mac/communicator/config.css79
-rw-r--r--comm/suite/themes/classic/mac/communicator/console/console-error-caret.pngbin0 -> 103 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/console/console-error-dash.pngbin0 -> 77 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/console/console.css164
-rw-r--r--comm/suite/themes/classic/mac/communicator/customizeToolbar.css38
-rw-r--r--comm/suite/themes/classic/mac/communicator/datetimepicker.css94
-rw-r--r--comm/suite/themes/classic/mac/communicator/downloads/downloadButtons.pngbin0 -> 762 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/downloads/downloadmanager.css142
-rw-r--r--comm/suite/themes/classic/mac/communicator/downloads/progressBg.pngbin0 -> 114 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/helpviewer/dropmark-nav.pngbin0 -> 1258 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/helpviewer/help.css136
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/autocomplete-dropmarker.pngbin0 -> 234 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/communicatoricons-small.pngbin0 -> 10236 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/communicatoricons.pngbin0 -> 14396 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/feedIcon16-disabled.pngbin0 -> 680 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/geolocation-16.pngbin0 -> 416 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/geolocation-64.pngbin0 -> 8490 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/item.pngbin0 -> 403 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/key-16.pngbin0 -> 244 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/key-16@2x.pngbin0 -> 515 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/key-64.pngbin0 -> 4905 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/key.pngbin0 -> 360 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/loading.pngbin0 -> 8944 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/panebutton-active.pngbin0 -> 276 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/panebutton-inactive.pngbin0 -> 220 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/icons/warning-24.pngbin0 -> 1034 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/numberbox.css25
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/bookmarks.css108
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/bookmarksMenu.pngbin0 -> 169 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.css77
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.pngbin0 -> 303 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/editBookmarkOverlay.css75
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/filters.svg14
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/livemark-folder.pngbin0 -> 570 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/organizer.css185
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/query.pngbin0 -> 474 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/tag.pngbin0 -> 789 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/places/toolbarDropMarker.pngbin0 -> 237 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/preferences.css77
-rw-r--r--comm/suite/themes/classic/mac/communicator/profile/profile.css51
-rw-r--r--comm/suite/themes/classic/mac/communicator/sanitizeDialog.css45
-rw-r--r--comm/suite/themes/classic/mac/communicator/search/searchbar-dropmarker.pngbin0 -> 131 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/search/searchbar-search.pngbin0 -> 234 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/search/searchbar.css69
-rw-r--r--comm/suite/themes/classic/mac/communicator/sidebar/sidebar.css164
-rw-r--r--comm/suite/themes/classic/mac/communicator/spinbuttons.css31
-rw-r--r--comm/suite/themes/classic/mac/communicator/sync/syncSetup.css127
-rw-r--r--comm/suite/themes/classic/mac/communicator/toolbar.css57
-rw-r--r--comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient22.pngbin0 -> 345 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient34.pngbin0 -> 490 bytes
-rw-r--r--comm/suite/themes/classic/mac/communicator/viewSourceOverlay.css14
-rw-r--r--comm/suite/themes/classic/mac/editor/editor.css84
-rw-r--r--comm/suite/themes/classic/mac/editor/editorFormatToolbar.css538
-rw-r--r--comm/suite/themes/classic/mac/editor/editorModeToolbar.css76
-rw-r--r--comm/suite/themes/classic/mac/editor/editorPrimaryToolbar.css441
-rw-r--r--comm/suite/themes/classic/mac/editor/icons/editoricons-small.pngbin0 -> 46272 bytes
-rw-r--r--comm/suite/themes/classic/mac/editor/icons/editoricons.pngbin0 -> 57196 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/accountManage.css57
-rw-r--r--comm/suite/themes/classic/mac/messenger/addressbook/addressbook.css334
-rw-r--r--comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons-small.pngbin0 -> 10738 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons.pngbin0 -> 15259 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/browserRequest.css59
-rw-r--r--comm/suite/themes/classic/mac/messenger/filterDialog.css108
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/junk.pngbin0 -> 1096 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/messengericons-small.pngbin0 -> 30676 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/messengericons.pngbin0 -> 44917 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/phishing.pngbin0 -> 766 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/spin-buttons-active.pngbin0 -> 517 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/spin-buttons.pngbin0 -> 847 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/tab-arrow-left.pngbin0 -> 239 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/tab-arrow-right.pngbin0 -> 239 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/twisty-clsd.pngbin0 -> 176 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/icons/twisty-open.pngbin0 -> 204 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/mailWindow1.css266
-rw-r--r--comm/suite/themes/classic/mac/messenger/messageHeader.css178
-rw-r--r--comm/suite/themes/classic/mac/messenger/messageQuotes.css43
-rw-r--r--comm/suite/themes/classic/mac/messenger/messengercompose/messengercompose.css249
-rw-r--r--comm/suite/themes/classic/mac/messenger/primaryToolbar.css490
-rw-r--r--comm/suite/themes/classic/mac/messenger/searchDialog.css90
-rw-r--r--comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons-small.pngbin0 -> 10762 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons.pngbin0 -> 16459 bytes
-rw-r--r--comm/suite/themes/classic/mac/messenger/smime/msgCompSMIMEOverlay.css68
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/close.pngbin0 -> 1240 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/identity.pngbin0 -> 9959 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/navigatoricons-small.pngbin0 -> 2030 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/navigatoricons.pngbin0 -> 2668 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/panel-expander-closed.pngbin0 -> 155 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/panel-expander-open.pngbin0 -> 155 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/panel-plus-sign.pngbin0 -> 154 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/icons/restore.pngbin0 -> 457 bytes
-rw-r--r--comm/suite/themes/classic/mac/navigator/linkToolbar.css59
-rw-r--r--comm/suite/themes/classic/mac/navigator/navigator.css956
-rw-r--r--comm/suite/themes/classic/mac/navigator/pageInfo.css183
-rw-r--r--comm/suite/themes/classic/mac/navigator/tabbrowser.css238
-rw-r--r--comm/suite/themes/classic/mac/navigator/webDeveloper.css197
-rw-r--r--comm/suite/themes/classic/messenger/accountCentral.css120
-rw-r--r--comm/suite/themes/classic/messenger/accountManage.css61
-rw-r--r--comm/suite/themes/classic/messenger/accountWizard.css26
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/abResultsPane.css16
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/addressPanes.css36
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/addressbook.css317
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/cardDialog.css72
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/abcard.pngbin0 -> 163 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/ablist.pngbin0 -> 174 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/addrbook.pngbin0 -> 191 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons-small.pngbin0 -> 16089 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons.pngbin0 -> 24739 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/contact-generic-tiny.pngbin0 -> 651 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/contact-generic.pngbin0 -> 4495 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook-error.pngbin0 -> 195 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook.pngbin0 -> 187 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/icons/secure-remote-addrbook.pngbin0 -> 201 bytes
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/selectAddressesDialog.css48
-rw-r--r--comm/suite/themes/classic/messenger/addressbook/sidebarPanel.css12
-rw-r--r--comm/suite/themes/classic/messenger/addressingWidget.css41
-rw-r--r--comm/suite/themes/classic/messenger/browserRequest.css40
-rw-r--r--comm/suite/themes/classic/messenger/dialogs.css14
-rw-r--r--comm/suite/themes/classic/messenger/filterDialog.css67
-rw-r--r--comm/suite/themes/classic/messenger/folderMenus.css124
-rw-r--r--comm/suite/themes/classic/messenger/folderPane.css251
-rw-r--r--comm/suite/themes/classic/messenger/folderPaneExtras.css7
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-compose.pngbin0 -> 1786 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-filters.pngbin0 -> 1163 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-newaccount.pngbin0 -> 1241 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-prefs.pngbin0 -> 818 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-read.pngbin0 -> 1780 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-search.pngbin0 -> 1384 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/acct-subscribe.pngbin0 -> 1993 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/attach.pngbin0 -> 385 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/attachment-col.pngbin0 -> 985 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/attachment-selected.pngbin0 -> 1042 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/attachment.pngbin0 -> 1042 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/check.pngbin0 -> 367 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/dot.pngbin0 -> 332 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/flagcol.pngbin0 -> 399 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-closed.pngbin0 -> 368 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-draft-open.pngbin0 -> 584 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-draft-share-open.pngbin0 -> 598 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-draft-share.pngbin0 -> 569 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-draft.pngbin0 -> 553 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-inbox-new.pngbin0 -> 558 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-inbox-open.pngbin0 -> 509 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-inbox-share-open.pngbin0 -> 534 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-inbox-share.pngbin0 -> 471 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-inbox.pngbin0 -> 509 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-junk-open.pngbin0 -> 386 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-junk.pngbin0 -> 667 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-new-open.pngbin0 -> 614 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-new.pngbin0 -> 528 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-newsgroup-new.pngbin0 -> 449 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-newsgroup.pngbin0 -> 415 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-open.pngbin0 -> 473 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-outbox-open.pngbin0 -> 379 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-outbox.pngbin0 -> 354 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-search.pngbin0 -> 362 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-sent-open.pngbin0 -> 571 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-sent-share-open.pngbin0 -> 596 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-sent-share.pngbin0 -> 525 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-sent.pngbin0 -> 491 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-share-open.pngbin0 -> 625 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-share.pngbin0 -> 511 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-template-open.pngbin0 -> 538 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-template-share-open.pngbin0 -> 550 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-template-share.pngbin0 -> 470 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-template.pngbin0 -> 458 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-trash-open.pngbin0 -> 457 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-trash-share-open.pngbin0 -> 471 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-trash-share.pngbin0 -> 407 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/folder-trash.pngbin0 -> 385 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/info.pngbin0 -> 900 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/insecure.pngbin0 -> 559 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/junk.pngbin0 -> 1096 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/junkBar.pngbin0 -> 2013 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/junkcol.pngbin0 -> 323 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/loading.pngbin0 -> 10727 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/local-mailhost.pngbin0 -> 281 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-del.pngbin0 -> 857 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl-reply.pngbin0 -> 282 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl.pngbin0 -> 267 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-reply.pngbin0 -> 281 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd.pngbin0 -> 260 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-offl-reply.pngbin0 -> 279 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-offl.pngbin0 -> 430 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach-reply.pngbin0 -> 280 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-attach.pngbin0 -> 422 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-delete-offl.pngbin0 -> 441 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl-reply.pngbin0 -> 276 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl.pngbin0 -> 258 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-fwd-reply.pngbin0 -> 276 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-fwd.pngbin0 -> 262 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-imapdelete.pngbin0 -> 456 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-new-offl.pngbin0 -> 472 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-new.pngbin0 -> 479 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-offl-reply.pngbin0 -> 273 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-offl.pngbin0 -> 408 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail-reply.pngbin0 -> 269 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-mail.pngbin0 -> 412 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-attach-kill-offl.pngbin0 -> 496 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-attach-kill.pngbin0 -> 500 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-attach-offl.pngbin0 -> 354 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-attach.pngbin0 -> 361 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-kill-offl.pngbin0 -> 488 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-kill.pngbin0 -> 488 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-new-attach-off.pngbin0 -> 394 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-new-attach.pngbin0 -> 392 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-new-offl.pngbin0 -> 395 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-new.pngbin0 -> 396 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news-offl.pngbin0 -> 335 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/message-news.pngbin0 -> 341 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/messengericons-small.pngbin0 -> 38661 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/messengericons.pngbin0 -> 69425 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/new-mail-alert.pngbin0 -> 1686 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/phishing.pngbin0 -> 766 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/readcol.pngbin0 -> 288 bytes
-rwxr-xr-xcomm/suite/themes/classic/messenger/icons/remote-blocked.pngbin0 -> 2205 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/secure.pngbin0 -> 595 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-local-new.pngbin0 -> 333 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-local.pngbin0 -> 242 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-mail-new.pngbin0 -> 382 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-mail.pngbin0 -> 331 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-news-lock.pngbin0 -> 330 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-news-new.pngbin0 -> 300 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-news.pngbin0 -> 244 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-remote-lock-new.pngbin0 -> 390 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/server-remote-lock.pngbin0 -> 368 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-closed-eye.pngbin0 -> 878 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-closed-kill.pngbin0 -> 635 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-closed-offl-eye.pngbin0 -> 876 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-closed-offl-kill.pngbin0 -> 635 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-closed.pngbin0 -> 397 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-new-closed-eye.pngbin0 -> 900 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-new-closed-kill.pngbin0 -> 659 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-eye.pngbin0 -> 898 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-kill.pngbin0 -> 659 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/thread-new-closed.pngbin0 -> 443 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/threadcol-threaded.pngbin0 -> 261 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/threadcol-unthreaded.pngbin0 -> 230 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/twisty-clsd.pngbin0 -> 207 bytes
-rw-r--r--comm/suite/themes/classic/messenger/icons/twisty-open.pngbin0 -> 203 bytes
-rw-r--r--comm/suite/themes/classic/messenger/mailWindow1.css156
-rw-r--r--comm/suite/themes/classic/messenger/messageBody.css186
-rw-r--r--comm/suite/themes/classic/messenger/messageHeader.css185
-rw-r--r--comm/suite/themes/classic/messenger/messageKeywords.css8
-rw-r--r--comm/suite/themes/classic/messenger/messageQuotes.css59
-rw-r--r--comm/suite/themes/classic/messenger/messageWindow.css19
-rw-r--r--comm/suite/themes/classic/messenger/messenger.css20
-rw-r--r--comm/suite/themes/classic/messenger/messengercompose/messengercompose.css333
-rw-r--r--comm/suite/themes/classic/messenger/msgSelectOffline.css32
-rw-r--r--comm/suite/themes/classic/messenger/newmailalert.css67
-rw-r--r--comm/suite/themes/classic/messenger/newsblog/feed-subscriptions.css30
-rw-r--r--comm/suite/themes/classic/messenger/newsblog/rss-feed.pngbin0 -> 879 bytes
-rw-r--r--comm/suite/themes/classic/messenger/prefPanels.css21
-rw-r--r--comm/suite/themes/classic/messenger/primaryToolbar.css572
-rw-r--r--comm/suite/themes/classic/messenger/searchDialog.css57
-rw-r--r--comm/suite/themes/classic/messenger/smime/certFetchingStatus.css7
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/hdrCryptoNotOk.pngbin0 -> 231 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/hdrCryptoOk.pngbin0 -> 220 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/hdrSignNotOk.pngbin0 -> 226 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/hdrSignOk.pngbin0 -> 202 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/hdrSignUnknown.pngbin0 -> 251 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/sbCryptoNotOk.pngbin0 -> 173 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/sbCryptoOk.pngbin0 -> 162 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/sbSignNotOk.pngbin0 -> 177 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/sbSignOk.pngbin0 -> 175 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/sbSignUnknown.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/smimeicons-small.pngbin0 -> 15620 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/icons/smimeicons.pngbin0 -> 25677 bytes
-rw-r--r--comm/suite/themes/classic/messenger/smime/msgCompSMIMEOverlay.css75
-rw-r--r--comm/suite/themes/classic/messenger/smime/msgCompSecurityInfo.css11
-rw-r--r--comm/suite/themes/classic/messenger/smime/msgHdrViewSMIMEOverlay.css45
-rw-r--r--comm/suite/themes/classic/messenger/smime/msgReadSMIMEOverlay.css37
-rw-r--r--comm/suite/themes/classic/messenger/smime/msgReadSecurityInfo.css27
-rw-r--r--comm/suite/themes/classic/messenger/start.css40
-rw-r--r--comm/suite/themes/classic/messenger/subscribe.css70
-rw-r--r--comm/suite/themes/classic/messenger/threadPane.css352
-rw-r--r--comm/suite/themes/classic/messenger/threadPaneExtras.css7
-rw-r--r--comm/suite/themes/classic/messenger/threadPaneLabels.css527
-rw-r--r--comm/suite/themes/classic/moz.build15
-rw-r--r--comm/suite/themes/classic/navigator/btn1/feeds.pngbin0 -> 2841 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/first-disabled.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/first-hover.pngbin0 -> 169 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/first.pngbin0 -> 157 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/last-disabled.pngbin0 -> 141 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/last-hover.pngbin0 -> 170 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/last.pngbin0 -> 159 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/next-disabled.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/next-hover.pngbin0 -> 151 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/next.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/previous-disabled.pngbin0 -> 119 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/previous-hover.pngbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/previous.pngbin0 -> 145 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/top-disabled.pngbin0 -> 130 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/top-hover.pngbin0 -> 158 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/top.pngbin0 -> 150 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/up-disabled.pngbin0 -> 109 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/up-hover.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/classic/navigator/btn1/up.pngbin0 -> 136 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/chevron.pngbin0 -> 82 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/close.pngbin0 -> 101 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/identity.pngbin0 -> 10386 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/minimize.pngbin0 -> 80 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/navigatoricons-small.pngbin0 -> 2634 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/navigatoricons.pngbin0 -> 4225 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/popup-blocked.pngbin0 -> 729 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/restore.pngbin0 -> 99 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/tab-arrow-left.pngbin0 -> 138 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/tab-arrow-right.pngbin0 -> 128 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/tab-drag-indicator.pngbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/classic/navigator/icons/tab-new.pngbin0 -> 635 bytes
-rw-r--r--comm/suite/themes/classic/navigator/linkToolbar.css125
-rw-r--r--comm/suite/themes/classic/navigator/navigator.css745
-rw-r--r--comm/suite/themes/classic/navigator/pageInfo.css126
-rw-r--r--comm/suite/themes/classic/navigator/tabbrowser.css198
-rw-r--r--comm/suite/themes/classic/navigator/webDeveloper.css205
-rw-r--r--comm/suite/themes/classic/preview.pngbin0 -> 22895 bytes
-rw-r--r--comm/suite/themes/classic/windows/communicator/sanitizeDialog.css56
-rw-r--r--comm/suite/themes/modern/README6
-rw-r--r--comm/suite/themes/modern/communicator/aboutPrivateBrowsing.css82
-rw-r--r--comm/suite/themes/modern/communicator/aboutSessionRestore.css45
-rw-r--r--comm/suite/themes/modern/communicator/aboutSyncTabs.css83
-rw-r--r--comm/suite/themes/modern/communicator/blockedSite.css19
-rw-r--r--comm/suite/themes/modern/communicator/brand.css60
-rw-r--r--comm/suite/themes/modern/communicator/brand/throbber-anim.pngbin0 -> 81284 bytes
-rw-r--r--comm/suite/themes/modern/communicator/brand/throbber-single.pngbin0 -> 2174 bytes
-rw-r--r--comm/suite/themes/modern/communicator/brand/throbber16-anim.pngbin0 -> 28831 bytes
-rw-r--r--comm/suite/themes/modern/communicator/brand/throbber16-single.pngbin0 -> 909 bytes
-rw-r--r--comm/suite/themes/modern/communicator/button.css115
-rw-r--r--comm/suite/themes/modern/communicator/certError.css81
-rw-r--r--comm/suite/themes/modern/communicator/communicator.css264
-rw-r--r--comm/suite/themes/modern/communicator/console/console.css106
-rw-r--r--comm/suite/themes/modern/communicator/console/error-caret.pngbin0 -> 103 bytes
-rw-r--r--comm/suite/themes/modern/communicator/console/error-dash.pngbin0 -> 77 bytes
-rw-r--r--comm/suite/themes/modern/communicator/customizeToolbar.css20
-rw-r--r--comm/suite/themes/modern/communicator/dataman/dataman.css31
-rw-r--r--comm/suite/themes/modern/communicator/dataman/datamanIcon-16.pngbin0 -> 318 bytes
-rw-r--r--comm/suite/themes/modern/communicator/datetimepicker.css129
-rw-r--r--comm/suite/themes/modern/communicator/dialogs.css11
-rw-r--r--comm/suite/themes/modern/communicator/downloads/dl-remove.pngbin0 -> 302 bytes
-rw-r--r--comm/suite/themes/modern/communicator/downloads/downloadButtons.pngbin0 -> 952 bytes
-rw-r--r--comm/suite/themes/modern/communicator/downloads/downloadmanager.css97
-rw-r--r--comm/suite/themes/modern/communicator/feed-subscribe-ui.css11
-rw-r--r--comm/suite/themes/modern/communicator/feed-subscribe.css150
-rw-r--r--comm/suite/themes/modern/communicator/fullscreen-video.css33
-rw-r--r--comm/suite/themes/modern/communicator/helpviewer/help.css144
-rw-r--r--comm/suite/themes/modern/communicator/icons/alwaysAsk.pngbin0 -> 423 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/application.pngbin0 -> 376 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/btn1.pngbin0 -> 7657 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/closeFullScreenVideo.pngbin0 -> 373 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/common-small.pngbin0 -> 10519 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/common.pngbin0 -> 35929 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/feedIcon.pngbin0 -> 1790 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/feedIcon16.pngbin0 -> 699 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/geo.pngbin0 -> 918 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/geolocation-16.pngbin0 -> 606 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/geolocation-64.pngbin0 -> 8056 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/identity.pngbin0 -> 616 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/loading.pngbin0 -> 576 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/lock-broken-16.pngbin0 -> 408 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/lock-broken.pngbin0 -> 357 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/lock-insecure-16.pngbin0 -> 379 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/lock-insecure.pngbin0 -> 369 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/lock-secure-16.pngbin0 -> 372 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/lock-secure.pngbin0 -> 367 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/notification-16.pngbin0 -> 524 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/notification-64.pngbin0 -> 3373 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/notification-icons.svg104
-rw-r--r--comm/suite/themes/modern/communicator/icons/offline.pngbin0 -> 206 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/online.pngbin0 -> 198 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/save.pngbin0 -> 923 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-cool.pngbin0 -> 976 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-cry.pngbin0 -> 1025 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-embarrassed.pngbin0 -> 939 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-foot.pngbin0 -> 932 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-frown.pngbin0 -> 992 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-innocent.pngbin0 -> 941 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-kiss.pngbin0 -> 943 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-laughing.pngbin0 -> 1016 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-money.pngbin0 -> 967 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-sealed.pngbin0 -> 916 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-smile.pngbin0 -> 986 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-surprise.pngbin0 -> 984 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-tongue.pngbin0 -> 936 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-undecided.pngbin0 -> 951 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-wink.pngbin0 -> 983 bytes
-rw-r--r--comm/suite/themes/modern/communicator/icons/smileys/smiley-yell.pngbin0 -> 942 bytes
-rw-r--r--comm/suite/themes/modern/communicator/numberbox.css25
-rw-r--r--comm/suite/themes/modern/communicator/places/allBookmarks.pngbin0 -> 572 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark-folder-closed.pngbin0 -> 314 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark-folder-dis.pngbin0 -> 300 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark-folder-open.pngbin0 -> 396 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark-item-dis.pngbin0 -> 178 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark-item-updated.pngbin0 -> 239 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark-item.svg10
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmark.pngbin0 -> 2179 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmarks.css123
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmarksMenu.pngbin0 -> 326 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmarksToolbar.css103
-rw-r--r--comm/suite/themes/modern/communicator/places/bookmarksToolbar.pngbin0 -> 255 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/calendar.pngbin0 -> 604 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/editBookmarkOverlay.css75
-rw-r--r--comm/suite/themes/modern/communicator/places/history.pngbin0 -> 821 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/livemark-folder.pngbin0 -> 594 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/organizer.css23
-rw-r--r--comm/suite/themes/modern/communicator/places/query.pngbin0 -> 620 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/tag.pngbin0 -> 453 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/toolbarDropMarker.pngbin0 -> 449 bytes
-rw-r--r--comm/suite/themes/modern/communicator/places/unsortedBookmarks.pngbin0 -> 516 bytes
-rw-r--r--comm/suite/themes/modern/communicator/preferences.css18
-rw-r--r--comm/suite/themes/modern/communicator/prefpanels.css117
-rw-r--r--comm/suite/themes/modern/communicator/profile/migrate.pngbin0 -> 299 bytes
-rw-r--r--comm/suite/themes/modern/communicator/profile/profile.css68
-rw-r--r--comm/suite/themes/modern/communicator/profile/profile.pngbin0 -> 261 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sanitizeDialog.css56
-rw-r--r--comm/suite/themes/modern/communicator/search/engineManager.css14
-rw-r--r--comm/suite/themes/modern/communicator/search/mainwindow-dropdown-arrow.pngbin0 -> 470 bytes
-rwxr-xr-xcomm/suite/themes/modern/communicator/search/search.css18
-rw-r--r--comm/suite/themes/modern/communicator/search/searchbar.css62
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/customize.css7
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/preview.css19
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbar-top-tabopen.pngbin0 -> 845 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbar-top.pngbin0 -> 814 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbpicker-arrow.pngbin0 -> 91 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-lft-act.pngbin0 -> 216 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-lft-sel.pngbin0 -> 207 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-lft.pngbin0 -> 207 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-mid-act.pngbin0 -> 108 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-mid-sel.pngbin0 -> 101 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-mid.pngbin0 -> 102 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-act.pngbin0 -> 131 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-hov.pngbin0 -> 151 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-act.pngbin0 -> 766 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-hov.pngbin0 -> 673 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-sel.pngbin0 -> 676 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top.pngbin0 -> 537 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sidebar.css247
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sidebarBindings.xml69
-rw-r--r--comm/suite/themes/modern/communicator/sidebar/sidebarListView.css17
-rw-r--r--comm/suite/themes/modern/communicator/smileys.css132
-rw-r--r--comm/suite/themes/modern/communicator/spinbuttons.css49
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-16-throbber.pngbin0 -> 6227 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-16.pngbin0 -> 1789 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-32-throbber.pngbin0 -> 25548 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-32.pngbin0 -> 3365 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-bg.pngbin0 -> 21309 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-desktopIcon.pngbin0 -> 260 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/sync-mobileIcon.pngbin0 -> 301 bytes
-rw-r--r--comm/suite/themes/modern/communicator/sync/syncCommon.css41
-rw-r--r--comm/suite/themes/modern/communicator/sync/syncQuota.css16
-rw-r--r--comm/suite/themes/modern/communicator/sync/syncSetup.css113
-rw-r--r--comm/suite/themes/modern/communicator/taskbar/taskbar.pngbin0 -> 4936 bytes
-rw-r--r--comm/suite/themes/modern/communicator/taskbar/taskmenu-abook.pngbin0 -> 621 bytes
-rw-r--r--comm/suite/themes/modern/communicator/taskbar/taskmenu-browser.pngbin0 -> 771 bytes
-rw-r--r--comm/suite/themes/modern/communicator/taskbar/taskmenu-composer.pngbin0 -> 694 bytes
-rw-r--r--comm/suite/themes/modern/communicator/taskbar/taskmenu-mailnews.pngbin0 -> 739 bytes
-rw-r--r--comm/suite/themes/modern/communicator/tasksOverlay.css87
-rw-r--r--comm/suite/themes/modern/communicator/toolbar.css106
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-bg-line.pngbin0 -> 130 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-bg-noline.pngbin0 -> 120 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm-act.pngbin0 -> 150 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm.pngbin0 -> 141 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid-act.pngbin0 -> 84 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid.pngbin0 -> 82 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-grip-top-act.pngbin0 -> 320 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/prtb-grip-top.pngbin0 -> 238 bytes
-rw-r--r--comm/suite/themes/modern/communicator/toolbar/toolbarBindings.xml34
-rw-r--r--comm/suite/themes/modern/communicator/viewSourceOverlay.css8
-rw-r--r--comm/suite/themes/modern/communicator/xpinstall/xpinstall.css37
-rw-r--r--comm/suite/themes/modern/editor/EditorDialog.css289
-rw-r--r--comm/suite/themes/modern/editor/editor.css80
-rw-r--r--comm/suite/themes/modern/editor/editorFormatToolbar.css591
-rw-r--r--comm/suite/themes/modern/editor/editorModeToolbar.css29
-rw-r--r--comm/suite/themes/modern/editor/editorPrimaryToolbar.css311
-rw-r--r--comm/suite/themes/modern/editor/icons/btn1.pngbin0 -> 94999 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/btn2.pngbin0 -> 11759 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/editmode-html.pngbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/editmode-normal.pngbin0 -> 150 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/editmode-preview.pngbin0 -> 172 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/editmode-tags.pngbin0 -> 173 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-btm-sel.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-btm.pngbin0 -> 213 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-lft.pngbin0 -> 205 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-mid-sel.pngbin0 -> 198 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-mid.pngbin0 -> 210 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-rit.pngbin0 -> 197 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-top-sel.pngbin0 -> 199 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/img-align-top.pngbin0 -> 207 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/mast-editor.pngbin0 -> 3513 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/multicolor.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/progress-busy.pngbin0 -> 1439 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/progress-done.pngbin0 -> 161 bytes
-rw-r--r--comm/suite/themes/modern/editor/icons/progress-failed.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/themes/modern/global/about.css65
-rw-r--r--comm/suite/themes/modern/global/aboutCache.css50
-rw-r--r--comm/suite/themes/modern/global/aboutCacheEntry.css27
-rw-r--r--comm/suite/themes/modern/global/aboutMemory.css26
-rw-r--r--comm/suite/themes/modern/global/aboutSupport.css108
-rw-r--r--comm/suite/themes/modern/global/alerts/alert.css113
-rw-r--r--comm/suite/themes/modern/global/alerts/notification-48.pngbin0 -> 2517 bytes
-rw-r--r--comm/suite/themes/modern/global/appPicker.css26
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-dn-dis.gifbin0 -> 51 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-dn-dis.pngbin0 -> 99 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-dn.gifbin0 -> 60 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-dn.pngbin0 -> 113 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft-dis.gifbin0 -> 53 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft-dis.pngbin0 -> 96 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.gifbin0 -> 56 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.pngbin0 -> 94 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft-sharp.gifbin0 -> 53 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft-sharp.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft.gifbin0 -> 60 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-lft.pngbin0 -> 114 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit-dis.gifbin0 -> 52 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit-dis.pngbin0 -> 89 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.gifbin0 -> 56 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.pngbin0 -> 92 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit-sharp.gifbin0 -> 53 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit-sharp.pngbin0 -> 94 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit.gifbin0 -> 62 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-rit.pngbin0 -> 117 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-up-dis.gifbin0 -> 50 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-up-dis.pngbin0 -> 91 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-up.gifbin0 -> 59 bytes
-rw-r--r--comm/suite/themes/modern/global/arrow/arrow-up.pngbin0 -> 117 bytes
-rw-r--r--comm/suite/themes/modern/global/autocomplete.css105
-rw-r--r--comm/suite/themes/modern/global/button.css127
-rw-r--r--comm/suite/themes/modern/global/button/tbmbtn-arrow-act.gifbin0 -> 558 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmbtn-arrow-act.pngbin0 -> 506 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.gifbin0 -> 556 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.pngbin0 -> 505 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmbtn-arrow.gifbin0 -> 553 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmbtn-arrow.pngbin0 -> 507 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmenu-arrow-act.gifbin0 -> 47 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmenu-arrow-act.pngbin0 -> 86 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmenu-arrow-dis.gifbin0 -> 47 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmenu-arrow-dis.pngbin0 -> 92 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmenu-arrow.gifbin0 -> 47 bytes
-rw-r--r--comm/suite/themes/modern/global/button/tbmenu-arrow.pngbin0 -> 92 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox.css69
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-act-check.gifbin0 -> 138 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-act-check.pngbin0 -> 179 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-act.gifbin0 -> 99 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-act.pngbin0 -> 143 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-check.gifbin0 -> 111 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-check.pngbin0 -> 176 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-dis-check.gifbin0 -> 138 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-dis-check.pngbin0 -> 179 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-dis.gifbin0 -> 99 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox-dis.pngbin0 -> 143 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox.gifbin0 -> 99 bytes
-rw-r--r--comm/suite/themes/modern/global/checkbox/cbox.pngbin0 -> 143 bytes
-rw-r--r--comm/suite/themes/modern/global/colorpicker.css57
-rw-r--r--comm/suite/themes/modern/global/config.css72
-rw-r--r--comm/suite/themes/modern/global/dialog.css16
-rw-r--r--comm/suite/themes/modern/global/dirListing/dirListing.css107
-rw-r--r--comm/suite/themes/modern/global/dropmarker.css24
-rw-r--r--comm/suite/themes/modern/global/filefield.css37
-rw-r--r--comm/suite/themes/modern/global/filepicker.css59
-rw-r--r--comm/suite/themes/modern/global/filepicker/blank.gifbin0 -> 47 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/blank.pngbin0 -> 71 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/dir-closed.gifbin0 -> 228 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/dir-closed.pngbin0 -> 211 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/dir-open.gifbin0 -> 227 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/dir-open.pngbin0 -> 219 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/folder-home.gifbin0 -> 406 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/folder-home.pngbin0 -> 461 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/folder-new.gifbin0 -> 377 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/folder-new.pngbin0 -> 369 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/folder-up.gifbin0 -> 405 bytes
-rw-r--r--comm/suite/themes/modern/global/filepicker/folder-up.pngbin0 -> 408 bytes
-rw-r--r--comm/suite/themes/modern/global/findBar.css98
-rw-r--r--comm/suite/themes/modern/global/global.css309
-rw-r--r--comm/suite/themes/modern/global/globalBindings.xml70
-rw-r--r--comm/suite/themes/modern/global/groupbox.css27
-rw-r--r--comm/suite/themes/modern/global/icons/Error.pngbin0 -> 2047 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/Question.pngbin0 -> 2013 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/authentication-48.pngbin0 -> 2977 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/autoscroll.pngbin0 -> 3275 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/blacklist_favicon.pngbin0 -> 591 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/blacklist_large.pngbin0 -> 3587 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close-act.gifbin0 -> 1016 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close-act.pngbin0 -> 571 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close-dis.gifbin0 -> 1037 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close-dis.pngbin0 -> 562 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close-hov.gifbin0 -> 1022 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close-hov.pngbin0 -> 640 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close.gifbin0 -> 1037 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/close.pngbin0 -> 616 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/closebox.gifbin0 -> 67 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/closebox.pngbin0 -> 98 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/error-16.pngbin0 -> 823 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/error-24.pngbin0 -> 1180 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/error-48.pngbin0 -> 2467 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/error-64.pngbin0 -> 3061 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/find.pngbin0 -> 403 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/information-16.pngbin0 -> 638 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/information-24.pngbin0 -> 1117 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/information-32.pngbin0 -> 1703 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/information-48.pngbin0 -> 2526 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/information-64.pngbin0 -> 3443 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/notfound.pngbin0 -> 638 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-landscape-small.gifbin0 -> 151 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-landscape-small.pngbin0 -> 156 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-landscape.gifbin0 -> 311 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-landscape.pngbin0 -> 236 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-portrait-small.gifbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-portrait-small.pngbin0 -> 163 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-portrait.gifbin0 -> 318 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/pg-portrait.pngbin0 -> 237 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/question-16.pngbin0 -> 730 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/question-24.pngbin0 -> 1186 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/question-48.pngbin0 -> 2760 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/question-64.pngbin0 -> 3744 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/resizer-rtl.pngbin0 -> 188 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/resizer.pngbin0 -> 160 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/search.gifbin0 -> 562 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/search.pngbin0 -> 552 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/sslWarning.pngbin0 -> 3860 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/warning-16.pngbin0 -> 603 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/warning-24.pngbin0 -> 963 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/warning-32.pngbin0 -> 1671 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/warning-48.pngbin0 -> 1972 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/warning-64.pngbin0 -> 2878 bytes
-rw-r--r--comm/suite/themes/modern/global/icons/wrap.pngbin0 -> 358 bytes
-rw-r--r--comm/suite/themes/modern/global/listbox.css137
-rw-r--r--comm/suite/themes/modern/global/media/TopLevelImageDocument.css17
-rw-r--r--comm/suite/themes/modern/global/media/TopLevelVideoDocument.css12
-rw-r--r--comm/suite/themes/modern/global/media/closedCaptionButton.svg55
-rw-r--r--comm/suite/themes/modern/global/media/error.pngbin0 -> 19817 bytes
-rw-r--r--comm/suite/themes/modern/global/media/fullscreenButton.svg47
-rw-r--r--comm/suite/themes/modern/global/media/imagedoc-darknoise.pngbin0 -> 3050 bytes
-rw-r--r--comm/suite/themes/modern/global/media/imagedoc-lightnoise.pngbin0 -> 4025 bytes
-rw-r--r--comm/suite/themes/modern/global/media/muteButton.svg56
-rw-r--r--comm/suite/themes/modern/global/media/pauseButton.svg6
-rw-r--r--comm/suite/themes/modern/global/media/playButton.svg5
-rw-r--r--comm/suite/themes/modern/global/media/stalled.pngbin0 -> 20763 bytes
-rw-r--r--comm/suite/themes/modern/global/media/throbber.pngbin0 -> 30503 bytes
-rw-r--r--comm/suite/themes/modern/global/media/videocontrols.css482
-rw-r--r--comm/suite/themes/modern/global/menu.css233
-rw-r--r--comm/suite/themes/modern/global/menu/menu-arrow-dis.gifbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-arrow-dis.pngbin0 -> 96 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-arrow-hov.gifbin0 -> 49 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-arrow-hov.pngbin0 -> 92 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-arrow.gifbin0 -> 49 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-arrow.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-check-dis.gifbin0 -> 111 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-check-dis.pngbin0 -> 102 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-check-hov.gifbin0 -> 66 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-check-hov.pngbin0 -> 108 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-check.gifbin0 -> 111 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-check.pngbin0 -> 102 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-radio-dis.gifbin0 -> 68 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-radio-dis.pngbin0 -> 90 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-radio-hov.gifbin0 -> 68 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-radio-hov.pngbin0 -> 97 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-radio.gifbin0 -> 68 bytes
-rw-r--r--comm/suite/themes/modern/global/menu/menu-radio.pngbin0 -> 90 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist.css180
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-act-arrow.gifbin0 -> 51 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-act-arrow.pngbin0 -> 95 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-arrow.gifbin0 -> 51 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-arrow.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-compact-arrow.gifbin0 -> 53 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-compact-arrow.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-dis-arrow.gifbin0 -> 51 bytes
-rw-r--r--comm/suite/themes/modern/global/menulist/mlist-dis-arrow.pngbin0 -> 108 bytes
-rw-r--r--comm/suite/themes/modern/global/netError.css137
-rw-r--r--comm/suite/themes/modern/global/notification.css129
-rw-r--r--comm/suite/themes/modern/global/plugins.css101
-rw-r--r--comm/suite/themes/modern/global/popup.css104
-rw-r--r--comm/suite/themes/modern/global/preferences.css65
-rw-r--r--comm/suite/themes/modern/global/printPageSetup.css11
-rw-r--r--comm/suite/themes/modern/global/printPreview.css37
-rw-r--r--comm/suite/themes/modern/global/progressmeter.css47
-rw-r--r--comm/suite/themes/modern/global/progressmeter/progress-busy.gifbin0 -> 1047 bytes
-rw-r--r--comm/suite/themes/modern/global/progressmeter/progress-busy.pngbin0 -> 1382 bytes
-rw-r--r--comm/suite/themes/modern/global/radio.css72
-rw-r--r--comm/suite/themes/modern/global/radio/radio-act-check.gifbin0 -> 346 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-act-check.pngbin0 -> 343 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-act.gifbin0 -> 336 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-act.pngbin0 -> 324 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-check.gifbin0 -> 346 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-check.pngbin0 -> 350 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-dis-check.gifbin0 -> 346 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-dis-check.pngbin0 -> 333 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-dis.gifbin0 -> 336 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio-dis.pngbin0 -> 312 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio.gifbin0 -> 336 bytes
-rw-r--r--comm/suite/themes/modern/global/radio/radio.pngbin0 -> 328 bytes
-rw-r--r--comm/suite/themes/modern/global/resizer.css49
-rw-r--r--comm/suite/themes/modern/global/richlistbox.css51
-rw-r--r--comm/suite/themes/modern/global/scale.css51
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-dn.gifbin0 -> 51 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-dn.pngbin0 -> 91 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-lft.gifbin0 -> 53 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-lft.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-rit.gifbin0 -> 53 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-rit.pngbin0 -> 94 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-up.gifbin0 -> 52 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/btn-up.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-dn.gifbin0 -> 47 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-dn.pngbin0 -> 84 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-lft.gifbin0 -> 48 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-lft.pngbin0 -> 85 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-rit.gifbin0 -> 48 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-rit.pngbin0 -> 87 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-up.gifbin0 -> 48 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-btn-up.pngbin0 -> 85 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.gifbin0 -> 57 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.pngbin0 -> 93 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.gifbin0 -> 57 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.pngbin0 -> 86 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.gifbin0 -> 87 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.pngbin0 -> 125 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.gifbin0 -> 88 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.pngbin0 -> 120 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/slider-hrz.gifbin0 -> 103 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/slider-hrz.pngbin0 -> 107 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/slider-vrt.gifbin0 -> 65 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/slider-vrt.pngbin0 -> 86 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.gifbin0 -> 95 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.pngbin0 -> 124 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.gifbin0 -> 99 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.pngbin0 -> 125 bytes
-rw-r--r--comm/suite/themes/modern/global/scrollbars-mini.css108
-rw-r--r--comm/suite/themes/modern/global/scrollbars.css192
-rw-r--r--comm/suite/themes/modern/global/scrollbox.css95
-rw-r--r--comm/suite/themes/modern/global/splitter.css182
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-after-act.gifbin0 -> 104 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-after-act.pngbin0 -> 137 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-after.gifbin0 -> 134 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-after.pngbin0 -> 160 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-before-act.gifbin0 -> 104 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-before-act.pngbin0 -> 142 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-before.gifbin0 -> 135 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-hrz-before.pngbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-after-act.gifbin0 -> 114 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-after-act.pngbin0 -> 130 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-after.gifbin0 -> 143 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-after.pngbin0 -> 149 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-before-act.gifbin0 -> 116 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-before-act.pngbin0 -> 131 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-before.gifbin0 -> 147 bytes
-rw-r--r--comm/suite/themes/modern/global/splitter/grip-vrt-before.pngbin0 -> 163 bytes
-rw-r--r--comm/suite/themes/modern/global/tabbox.css123
-rw-r--r--comm/suite/themes/modern/global/textbox.css120
-rw-r--r--comm/suite/themes/modern/global/toolbar.css186
-rw-r--r--comm/suite/themes/modern/global/toolbar/chevron.gifbin0 -> 51 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/chevron.pngbin0 -> 87 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/mbgrip-arrow.gifbin0 -> 57 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/mbgrip-arrow.pngbin0 -> 105 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/spring.pngbin0 -> 149 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tb-mid.gifbin0 -> 146 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tb-mid.pngbin0 -> 97 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.gifbin0 -> 57 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.pngbin0 -> 105 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.gifbin0 -> 59 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.pngbin0 -> 110 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.gifbin0 -> 59 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.pngbin0 -> 110 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow.gifbin0 -> 57 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbar/tbgrip-arrow.pngbin0 -> 105 bytes
-rw-r--r--comm/suite/themes/modern/global/toolbarbutton.css146
-rw-r--r--comm/suite/themes/modern/global/tree.css278
-rw-r--r--comm/suite/themes/modern/global/tree/checkbox-checked.gifbin0 -> 180 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/checkbox-checked.pngbin0 -> 165 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/checkbox.gifbin0 -> 160 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/checkbox.pngbin0 -> 139 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/columnpicker.gifbin0 -> 97 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/columnpicker.pngbin0 -> 155 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/sort-asc.gifbin0 -> 64 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/sort-asc.pngbin0 -> 104 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/sort-dsc.gifbin0 -> 63 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/sort-dsc.pngbin0 -> 105 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/twisty-clsd.gifbin0 -> 87 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/twisty-clsd.pngbin0 -> 134 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/twisty-open.gifbin0 -> 85 bytes
-rw-r--r--comm/suite/themes/modern/global/tree/twisty-open.pngbin0 -> 137 bytes
-rw-r--r--comm/suite/themes/modern/global/wizard.css58
-rw-r--r--comm/suite/themes/modern/icon.pngbin0 -> 1754 bytes
-rw-r--r--comm/suite/themes/modern/install.rdf47
-rw-r--r--comm/suite/themes/modern/jar.mn766
-rw-r--r--comm/suite/themes/modern/messenger/accountCentral.css117
-rw-r--r--comm/suite/themes/modern/messenger/accountManage.css61
-rw-r--r--comm/suite/themes/modern/messenger/accountWizard.css26
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/abResultsPane.css15
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/addressPanes.css36
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/addressbook.css200
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/cardDialog.css72
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/contact-generic-tiny.pngbin0 -> 651 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/contact-generic.pngbin0 -> 4495 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/directory-down.pngbin0 -> 331 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/directory.pngbin0 -> 313 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/list.pngbin0 -> 368 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/mast-ab.pngbin0 -> 3559 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/myaddrbk.pngbin0 -> 312 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/person.pngbin0 -> 409 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/icons/secure-directory.pngbin0 -> 353 bytes
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/selectAddressesDialog.css49
-rw-r--r--comm/suite/themes/modern/messenger/addressbook/sidebarPanel.css12
-rw-r--r--comm/suite/themes/modern/messenger/addressingWidget.css43
-rw-r--r--comm/suite/themes/modern/messenger/browserRequest.css40
-rw-r--r--comm/suite/themes/modern/messenger/dialogs.css32
-rw-r--r--comm/suite/themes/modern/messenger/filterDialog.css67
-rw-r--r--comm/suite/themes/modern/messenger/folderMenus.css120
-rw-r--r--comm/suite/themes/modern/messenger/folderPane.css237
-rw-r--r--comm/suite/themes/modern/messenger/folderPaneExtras.css7
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-compose.pngbin0 -> 1409 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-filters.pngbin0 -> 1085 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-newaccount.pngbin0 -> 1059 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-prefs.pngbin0 -> 715 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-read.pngbin0 -> 1412 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-search.pngbin0 -> 1262 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/acct-subscribe.pngbin0 -> 1144 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/attach.pngbin0 -> 174 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/attachment-col.pngbin0 -> 258 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/attachment-selected.pngbin0 -> 572 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/attachment.pngbin0 -> 565 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/btn1.pngbin0 -> 47788 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/check.pngbin0 -> 165 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/dot.pngbin0 -> 139 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/flagcol-flagged.pngbin0 -> 187 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-closed.pngbin0 -> 318 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-draft-open.pngbin0 -> 384 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-draft-share-open.pngbin0 -> 397 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-draft-share.pngbin0 -> 362 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-draft.pngbin0 -> 349 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-inbox-new.pngbin0 -> 368 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-inbox-open.pngbin0 -> 316 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-inbox-share-open.pngbin0 -> 338 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-inbox-share.pngbin0 -> 234 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-inbox.pngbin0 -> 316 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-junk-open.pngbin0 -> 308 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-junk.pngbin0 -> 560 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-new-open.pngbin0 -> 418 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-new.pngbin0 -> 333 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-newsgroup-new.pngbin0 -> 244 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-newsgroup.pngbin0 -> 191 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-open.pngbin0 -> 417 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-outbox-open.pngbin0 -> 329 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-outbox.pngbin0 -> 309 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-search.pngbin0 -> 306 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-sent-open.pngbin0 -> 380 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-sent-share-open.pngbin0 -> 403 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-sent-share.pngbin0 -> 324 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-sent.pngbin0 -> 293 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-share-open.pngbin0 -> 423 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-share.pngbin0 -> 318 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-template-open.pngbin0 -> 340 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-template-share-open.pngbin0 -> 355 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-template-share.pngbin0 -> 282 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-template.pngbin0 -> 263 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-trash-open.pngbin0 -> 196 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-trash-share-open.pngbin0 -> 222 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-trash-share.pngbin0 -> 189 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/folder-trash.pngbin0 -> 161 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/info.pngbin0 -> 900 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/insecure.pngbin0 -> 559 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/junk.pngbin0 -> 1096 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/junkBar.pngbin0 -> 1680 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/loading.pngbin0 -> 13906 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/local-mailhost.pngbin0 -> 209 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/mast-mail.pngbin0 -> 2944 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-junk-other.pngbin0 -> 255 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-del.pngbin0 -> 778 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl-reply.pngbin0 -> 290 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl.pngbin0 -> 267 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-reply.pngbin0 -> 298 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd.pngbin0 -> 262 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-offl-reply.pngbin0 -> 286 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-offl.pngbin0 -> 254 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach-reply.pngbin0 -> 271 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-attach.pngbin0 -> 252 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-delete-offl.pngbin0 -> 272 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl-reply.pngbin0 -> 277 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl.pngbin0 -> 257 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-fwd-reply.pngbin0 -> 283 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-fwd.pngbin0 -> 250 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-imapdelete.pngbin0 -> 283 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-new-offl.pngbin0 -> 298 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-new.pngbin0 -> 307 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-offl-reply.pngbin0 -> 273 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-offl.pngbin0 -> 245 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail-reply.pngbin0 -> 260 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-mail.pngbin0 -> 249 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-attach-kill-offl.pngbin0 -> 496 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-attach-kill.pngbin0 -> 500 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-attach-offl.pngbin0 -> 180 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-attach.pngbin0 -> 183 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-kill-offl.pngbin0 -> 488 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-kill.pngbin0 -> 488 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-new-attach-off.pngbin0 -> 211 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-new-attach.pngbin0 -> 214 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-new-offl.pngbin0 -> 222 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-new.pngbin0 -> 222 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news-offl.pngbin0 -> 168 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/message-news.pngbin0 -> 169 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/new-mail-alert.pngbin0 -> 1686 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/phishing.pngbin0 -> 766 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/readcol-read.pngbin0 -> 123 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/readcol-unread.pngbin0 -> 147 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/remote-blocked.pngbin0 -> 2162 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/secure.pngbin0 -> 595 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-local-new.pngbin0 -> 250 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-local.pngbin0 -> 209 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-mail-new.pngbin0 -> 325 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-mail.pngbin0 -> 273 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-news-lock.pngbin0 -> 250 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-news-new.pngbin0 -> 225 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-news.pngbin0 -> 179 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-remote-lock-new.pngbin0 -> 325 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/server-remote-lock.pngbin0 -> 311 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-closed-eye.pngbin0 -> 443 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-closed-kill.pngbin0 -> 342 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-closed-offl-eye.pngbin0 -> 442 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-closed-offl-kill.pngbin0 -> 342 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-closed.pngbin0 -> 194 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-new-closed-eye.pngbin0 -> 463 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-new-closed-kill.pngbin0 -> 377 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-eye.pngbin0 -> 464 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-kill.pngbin0 -> 377 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/thread-new-closed.pngbin0 -> 234 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/threadcol-threaded.pngbin0 -> 190 bytes
-rw-r--r--comm/suite/themes/modern/messenger/icons/threadcol-unthreaded.pngbin0 -> 164 bytes
-rw-r--r--comm/suite/themes/modern/messenger/mailWindow1.css143
-rw-r--r--comm/suite/themes/modern/messenger/messageBody.css190
-rw-r--r--comm/suite/themes/modern/messenger/messageHeader.css189
-rw-r--r--comm/suite/themes/modern/messenger/messageKeywords.css8
-rw-r--r--comm/suite/themes/modern/messenger/messageQuotes.css58
-rw-r--r--comm/suite/themes/modern/messenger/messageWindow.css13
-rw-r--r--comm/suite/themes/modern/messenger/messenger.css23
-rw-r--r--comm/suite/themes/modern/messenger/messengercompose/icons/mast-msgcomp.pngbin0 -> 3873 bytes
-rw-r--r--comm/suite/themes/modern/messenger/messengercompose/messengercompose.css211
-rw-r--r--comm/suite/themes/modern/messenger/msgSelectOffline.css33
-rw-r--r--comm/suite/themes/modern/messenger/newmailalert.css67
-rw-r--r--comm/suite/themes/modern/messenger/newsblog/feed-subscriptions.css30
-rw-r--r--comm/suite/themes/modern/messenger/newsblog/rss-feed.pngbin0 -> 879 bytes
-rw-r--r--comm/suite/themes/modern/messenger/prefPanels.css21
-rw-r--r--comm/suite/themes/modern/messenger/primaryToolbar.css385
-rw-r--r--comm/suite/themes/modern/messenger/searchDialog.css59
-rw-r--r--comm/suite/themes/modern/messenger/smime/certFetchingStatus.css7
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/hdrCryptoNotOk.pngbin0 -> 1116 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/hdrCryptoOk.pngbin0 -> 983 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/hdrSignNotOk.pngbin0 -> 871 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/hdrSignOk.pngbin0 -> 1136 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/hdrSignUnknown.pngbin0 -> 860 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/sbCryptoNotOk.pngbin0 -> 738 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/sbCryptoOk.pngbin0 -> 500 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/sbSignNotOk.pngbin0 -> 533 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/sbSignOk.pngbin0 -> 487 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/sbSignUnknown.pngbin0 -> 483 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/icons/smbtn1.pngbin0 -> 21765 bytes
-rw-r--r--comm/suite/themes/modern/messenger/smime/msgCompSMIMEOverlay.css58
-rw-r--r--comm/suite/themes/modern/messenger/smime/msgCompSecurityInfo.css11
-rw-r--r--comm/suite/themes/modern/messenger/smime/msgHdrViewSMIMEOverlay.css45
-rw-r--r--comm/suite/themes/modern/messenger/smime/msgReadSMIMEOverlay.css37
-rw-r--r--comm/suite/themes/modern/messenger/smime/msgReadSecurityInfo.css27
-rw-r--r--comm/suite/themes/modern/messenger/start.css40
-rw-r--r--comm/suite/themes/modern/messenger/subscribe.css78
-rw-r--r--comm/suite/themes/modern/messenger/threadPane.css351
-rw-r--r--comm/suite/themes/modern/messenger/threadPaneExtras.css7
-rw-r--r--comm/suite/themes/modern/messenger/threadPaneLabels.css527
-rw-r--r--comm/suite/themes/modern/moz.build19
-rw-r--r--comm/suite/themes/modern/mozapps/aboutNetworking.css149
-rw-r--r--comm/suite/themes/modern/mozapps/downloads/downloadIcon.pngbin0 -> 857 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/downloads/downloads.css102
-rw-r--r--comm/suite/themes/modern/mozapps/downloads/unknownContentType.css27
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/about.css79
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/alerticon-error.pngbin0 -> 3402 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/alerticon-info-negative.pngbin0 -> 1564 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/alerticon-info-positive.pngbin0 -> 1338 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/alerticon-warning.pngbin0 -> 1567 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/blocklist.css20
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/cancel.pngbin0 -> 115 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-available.pngbin0 -> 1671 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-dictionaries.pngbin0 -> 1665 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-discover.pngbin0 -> 1324 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-extensions.pngbin0 -> 1302 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-languages.pngbin0 -> 2410 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-plugins.pngbin0 -> 886 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-recent.pngbin0 -> 1642 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-search.pngbin0 -> 2536 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-searchengines.pngbin0 -> 2814 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/category-themes.pngbin0 -> 2185 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric-16.pngbin0 -> 733 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric.pngbin0 -> 1665 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/discover-logo.pngbin0 -> 12007 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/eula.css43
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/extensionGeneric-16.pngbin0 -> 369 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/extensionGeneric.pngbin0 -> 1302 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/extensions.css1077
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/extensions.svg10
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/heart.pngbin0 -> 2949 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/localeGeneric.pngbin0 -> 2410 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/navigation.pngbin0 -> 1056 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/newaddon.css110
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/rating-not-won.pngbin0 -> 1559 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/rating-won.pngbin0 -> 1662 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/stripes-error.pngbin0 -> 1557 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/stripes-info-negative.pngbin0 -> 1703 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/stripes-info-positive.pngbin0 -> 1673 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/stripes-warning.pngbin0 -> 1880 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/themeGeneric-16.pngbin0 -> 842 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/themeGeneric.pngbin0 -> 1828 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/update.css26
-rw-r--r--comm/suite/themes/modern/mozapps/extensions/utilities.pngbin0 -> 848 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/handling/handling.css34
-rw-r--r--comm/suite/themes/modern/mozapps/icons/itemDisabledFader.pngbin0 -> 601 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/icons/itemEnabledFader.pngbin0 -> 600 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/passwordmgr/key-16.pngbin0 -> 771 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/passwordmgr/key-64.pngbin0 -> 6136 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/passwordmgr/key.pngbin0 -> 658 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/plugins/pluginBlocked-16.pngbin0 -> 563 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/plugins/pluginBlocked.pngbin0 -> 879 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/plugins/pluginGeneric-16.pngbin0 -> 563 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/plugins/pluginGeneric.pngbin0 -> 879 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/plugins/pluginInstallerWizard.css10
-rw-r--r--comm/suite/themes/modern/mozapps/update/update.pngbin0 -> 368 bytes
-rw-r--r--comm/suite/themes/modern/mozapps/update/updates.css168
-rw-r--r--comm/suite/themes/modern/mozapps/viewsource/viewsource.css5
-rw-r--r--comm/suite/themes/modern/mozapps/xpinstall/xpinstallConfirm.css91
-rw-r--r--comm/suite/themes/modern/navigator/btn1/feeds.pngbin0 -> 2841 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/first-dis.pngbin0 -> 110 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/first-hov.pngbin0 -> 119 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/first.pngbin0 -> 108 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/last-dis.pngbin0 -> 115 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/last-hov.pngbin0 -> 119 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/last.pngbin0 -> 111 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/next-dis.pngbin0 -> 110 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/next-hov.pngbin0 -> 119 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/next.pngbin0 -> 109 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/previous-dis.pngbin0 -> 113 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/previous-hov.pngbin0 -> 120 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/previous.pngbin0 -> 110 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/top-dis.pngbin0 -> 112 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/top-hov.pngbin0 -> 121 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/top.pngbin0 -> 107 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/up-dis.pngbin0 -> 107 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/up-hov.pngbin0 -> 118 bytes
-rw-r--r--comm/suite/themes/modern/navigator/btn1/up.pngbin0 -> 104 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/browser-small.pngbin0 -> 2379 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/browser.pngbin0 -> 8500 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/identity.pngbin0 -> 10386 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/popup-blocked.pngbin0 -> 657 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/tab-drag-indicator.pngbin0 -> 152 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/tab-new-act.pngbin0 -> 887 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/tab-new-hov.pngbin0 -> 845 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/tab-new.pngbin0 -> 901 bytes
-rw-r--r--comm/suite/themes/modern/navigator/icons/windowcontrols.pngbin0 -> 2338 bytes
-rw-r--r--comm/suite/themes/modern/navigator/linkToolbar.css112
-rw-r--r--comm/suite/themes/modern/navigator/navigator.css936
-rw-r--r--comm/suite/themes/modern/navigator/pageInfo.css122
-rw-r--r--comm/suite/themes/modern/navigator/tabbrowser.css155
-rw-r--r--comm/suite/themes/modern/navigator/toolbar/ubhist-arrow-act.pngbin0 -> 87 bytes
-rw-r--r--comm/suite/themes/modern/navigator/toolbar/ubhist-arrow.pngbin0 -> 108 bytes
-rw-r--r--comm/suite/themes/modern/navigator/webDeveloper.css205
-rw-r--r--comm/suite/themes/modern/preview.pngbin0 -> 10147 bytes
-rw-r--r--comm/suite/themes/modern/shared/datetimeinputpickers.css438
-rw-r--r--comm/suite/themes/modern/shared/datetimepopup.css11
-rw-r--r--comm/suite/themes/modern/shared/icons/calendar-arrow-left.svg7
-rw-r--r--comm/suite/themes/modern/shared/icons/calendar-arrow-right.svg7
-rw-r--r--comm/suite/themes/modern/shared/icons/input-clear.svg16
-rw-r--r--comm/suite/themes/modern/shared/icons/spinner-arrow-down.svg7
-rw-r--r--comm/suite/themes/modern/shared/icons/spinner-arrow-up.svg7
3041 files changed, 363164 insertions, 0 deletions
diff --git a/comm/suite/Makefile.in b/comm/suite/Makefile.in
new file mode 100644
index 0000000000..634037b6ec
--- /dev/null
+++ b/comm/suite/Makefile.in
@@ -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/.
+
+include $(topsrcdir)/config/rules.mk
+
+ifdef MAKENSISU
+# For Windows build the uninstaller during the application build since the
+# uninstaller is included with the application for mar file generation.
+libs::
+ $(MAKE) -C installer/windows uninstaller
+endif
+
+
+# As a fallout from bug 1247162, the sourcestamp in application.ini and
+# platform.ini are the same, which isn't a problem for Firefox, but
+# it's not right for anything else. So we correct platform.ini here.
+
+MOZ_REV=$(shell hg -R "$(topsrcdir)" parent --template="{node}" 2>/dev/null)
+
+libs:: $(DIST)/bin/platform.ini
+ sed -e "s/^\(SourceStamp=\).*/\1$(MOZ_REV)/" $(DIST)/bin/platform.ini \
+ > $(DIST)/bin/platform.ini~
+ mv -f $(DIST)/bin/platform.ini~ $(DIST)/bin/platform.ini
diff --git a/comm/suite/README b/comm/suite/README
new file mode 100644
index 0000000000..87b53e5a81
--- /dev/null
+++ b/comm/suite/README
@@ -0,0 +1,10 @@
+mozilla/suite
+=============
+
+This module contains files for the SeaMonkey suite.
+
+Documentation about the directory structure within this module can be found at
+http://wiki.mozilla.org/SeaMonkey:Suite_Directory_Layout
+
+Consult http://www.seamonkey-project.org/dev/review-and-flags
+for review rules on stuff in this directory, please.
diff --git a/comm/suite/app.mozbuild b/comm/suite/app.mozbuild
new file mode 100644
index 0000000000..f6a8e486ca
--- /dev/null
+++ b/comm/suite/app.mozbuild
@@ -0,0 +1,13 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include('/toolkit/toolkit.mozbuild')
+
+DIRS += [
+ '/comm/mailnews',
+ '/%s' % CONFIG['MOZ_BRANDING_DIRECTORY'],
+ '/comm/calendar',
+ '/comm/suite',
+]
diff --git a/comm/suite/app/Makefile.in b/comm/suite/app/Makefile.in
new file mode 100644
index 0000000000..acb1322ffe
--- /dev/null
+++ b/comm/suite/app/Makefile.in
@@ -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/.
+
+dist_dest = $(DIST)/$(MOZ_MACBUNDLE_NAME)
+
+AB_CD = $(MOZ_UI_LOCALE)
+
+LICENSE_TXT_FILE = $(topsrcdir)/comm/suite/installer/license.txt
+
+# Build a binary bootstrapping with XRE_main
+
+ifndef MOZ_WINCONSOLE
+ifdef MOZ_DEBUG
+MOZ_WINCONSOLE = 1
+else
+MOZ_WINCONSOLE = 0
+endif
+endif
+
+include $(topsrcdir)/config/config.mk
+
+# If we are trying to show an error dialog about the lack of SSE2 support,
+# make sure that code itself doesn't use SSE2.
+ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
+CXX := $(filter-out -march=% -msse -msse2 -mfpmath=sse,$(CXX))
+CXX += -march=pentiumpro
+endif
+
+ifeq ($(OS_ARCH),WINNT)
+# Rebuild seamonkey.exe if the manifest changes - it's included by splash.rc.
+# (this dependency should really be just for seamonkey.exe, not other targets)
+# Note the manifest file exists in the tree, so we use the explicit filename
+# here.
+EXTRA_DEPS += $(srcdir)/seamonkey.exe.manifest
+endif
+
+include $(topsrcdir)/config/rules.mk
+
+# channel-prefs.js is handled separate from other prefs due to bug 756325.
+libs:: $(srcdir)/profile/channel-prefs.js
+ $(NSINSTALL) -D $(DIST)/bin/defaults/pref
+ $(call py_action,preprocessor,-Fsubstitution $(PREF_PPFLAGS) $(ACDEFINES) $^ -o $(DIST)/bin/defaults/pref/channel-prefs.js)
+
+libs:: $(LICENSE_TXT_FILE)
+ifeq ($(OS_ARCH),WINNT)
+ $(EXIT_ON_ERROR) \
+ perl -pe 's/(?<!\r)\n/\r\n/g;' < $^ > $(DIST)/bin/license.txt
+
+else
+ $(INSTALL) $(IFLAGS1) $^ $(DIST)/bin/
+endif
+
+ifneq ($(OS_ARCH),WINNT)
+
+ifdef COMPILE_ENVIRONMENT
+libs::
+ cp -p $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/bin/$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
+endif
+
+endif
+
+ifneq (,$(filter-out WINNT Darwin,$(OS_ARCH)))
+$(MOZ_APP_NAME).1: $(srcdir)/seamonkey.man.in $(GLOBAL_DEPS) $(DEPTH)/config/autoconf.mk
+ @sed -e "s|\@bindir\@|$(bindir)|g" -e "s|\@mozappdir\@|$(mozappdir)|g" \
+ -e "s|\@MOZ_APP_DISPLAYNAME\@|$(MOZ_APP_DISPLAYNAME)|g" \
+ -e "s|\@MOZ_APP_NAME\@|$(MOZ_APP_NAME)|g" \
+ -e "s|\@MOZ_APP_VERSION\@|${MOZ_APP_VERSION}|g" < $< > $@
+
+libs:: $(MOZ_APP_NAME).1
+ $(INSTALL) $< $(DIST)/man/man1
+
+GARBAGE += $(MOZ_APP_NAME).1
+GARBAGE += $(addprefix $(FINAL_TARGET)/defaults/pref/, all.js mailnews.js suite-prefs.js)
+endif
+
+# Make extensions end up as XPIs instead of flat chrome when doing omni.jar.
+# Extensions that appear in dist/bin/extensions will get bundled with the
+# application.
+# NOTE: This is a hack to run this at the end of compilation, would be nicer
+# if this was done right away for built-in extensions in omnijar mode.
+
+ABS_STAGE = $(abspath $(STAGEDIST))
+
+define _PACKAGE_EXTENSIONS
+@echo "Packaging $(dir)..."
+if test -d "$(ABS_STAGE)/$(dir)"; then \
+cd $(ABS_STAGE)/$(dir)/; \
+$(ZIP) -r9mX ../$(dir).xpi * -x \*/.mkdir.done; \
+cd ..; rm -rf $(ABS_STAGE)/$(dir); \
+fi
+
+endef # do not remove the blank line!
+
+# GUIDs
+# Unused for now. All extensions are being packed.
+DONOTPACK = {9999999%
+
+pack-ext: $(STAGEDIST)
+ @echo "Packaging extensions..."
+ $(foreach dir,$(filter-out $(DONOTPACK),$(subst $(STAGEDIST)/,,$(wildcard $(STAGEDIST)/*))),$(_PACKAGE_EXTENSIONS))
+
+tools::
+ @$(MAKE) pack-ext STAGEDIST='$(DIST)/bin/extensions'
+
+# Additional macOS build steps.
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+
+MAC_APP_NAME = $(MOZ_APP_DISPLAYNAME)
+
+ifdef MOZ_DEBUG
+MAC_APP_NAME := $(MAC_APP_NAME)Debug
+endif
+
+ifeq (zh-TW,$(AB_CD))
+LPROJ_ROOT := $(subst -,_,$(AB_CD))
+else
+LPROJ_ROOT := $(firstword $(subst -, ,$(AB_CD)))
+endif
+LPROJ := Contents/Resources/$(LPROJ_ROOT).lproj
+
+clean clobber repackage::
+ $(RM) -r '$(dist_dest)'
+
+tools:: $(DIST)/bin/$(MOZ_APP_NAME)
+ rm -rf $(dist_dest)
+ mkdir -p '$(dist_dest)/Contents/MacOS'
+ rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents '$(dist_dest)' --exclude English.lproj
+ mkdir -p '$(dist_dest)/$(LPROJ)'
+ rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents/Resources/English.lproj/ '$(dist_dest)/$(LPROJ)'
+ sed -e 's/\@MOZ_APP_NAME\@/$(MOZ_APP_NAME)/' -e 's/\@MAC_APP_NAME\@/$(MAC_APP_NAME)/' -e 's/\@MOZ_APP_DISPLAYNAME\@/$(MOZ_APP_DISPLAYNAME)/' -e 's/\@MOZ_APP_VERSION\@/$(MOZ_APP_VERSION)/' -e 's/\@MOZ_MACBUNDLE_ID\@/$(MOZ_MACBUNDLE_ID)/' -e 's|\@MOZ_DEVELOPER_REPO_PATH\@|$(topsrcdir)|' -e 's|\@MOZ_DEVELOPER_OBJ_PATH\@|$(topobjdir)|' $(srcdir)/macbuild/Contents/Info.plist.in > '$(dist_dest)/Contents/Info.plist'
+ sed -e "s/\@MAC_APP_NAME\@/$(MAC_APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > '$(dist_dest)/$(LPROJ)/InfoPlist.strings'
+ rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ '$(dist_dest)/Contents/Resources'
+ rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS'
+ # MacOS-files-copy.in is a list of files that should be copies rather
+ # than symlinks and placed in .app/Contents/MacOS.
+ rsync -aL --include-from='$(srcdir)/macbuild/Contents/MacOS-files-copy.in' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS'
+ rm -f '$(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)'
+ rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS'
+ cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/seamonkey.icns '$(dist_dest)/Contents/Resources/seamonkey.icns'
+ cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/document.icns '$(dist_dest)/Contents/Resources/document.icns'
+ printf APPLMOZZ > '$(dist_dest)/Contents/PkgInfo'
+endif
diff --git a/comm/suite/app/blocklist.xml b/comm/suite/app/blocklist.xml
new file mode 100644
index 0000000000..da13d0d40e
--- /dev/null
+++ b/comm/suite/app/blocklist.xml
@@ -0,0 +1,5225 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<blocklist lastupdate="1536186868443" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1211" id="flvto@hotger.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i576" id="newmoz@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i326" id="/^((support2_en@adobe14\.com)|(XN4Xgjw7n4@yUWgc\.com)|(C7yFVpIP@WeolS3acxgS\.com)|(Kbeu4h0z@yNb7QAz7jrYKiiTQ3\.com)|(aWQzX@a6z4gWdPu8FF\.com)|(CBSoqAJLYpCbjTP90@JoV0VMywCjsm75Y0toAd\.com)|(zZ2jWZ1H22Jb5NdELHS@o0jQVWZkY1gx1\.com))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i258" id="helperbar@helperbar.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.0" severity="1"/>
+ </emItem>
+ <emItem blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1229" id="/^(.*@(unblocker\.yt|sparpilot\.com))|(axtara@axtara\.com)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i218" id="ffxtlbr@claro.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1137" id="/^({d50bfa5f-291d-48a8-909c-5f1a77b31948}|{d54bc985-6e7b-46cd-ad72-a4a266ad879e}|{d89e5de3-5543-4363-b320-a98cf150f86a}|{f3465017-6f51-4980-84a5-7bee2f961eba}|{fae25f38-ff55-46ea-888f-03b49aaf8812})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i515" id="/^({bf9194c2-b86d-4ebc-9b53-1c08b6ff779e}|{61a83e16-7198-49c6-8874-3e4e8faeb4f3}|{f0af464e-5167-45cf-9cf0-66b396d1918c}|{5d9968c3-101c-4944-ba71-72d77393322d}|{01e86e69-a2f8-48a0-b068-83869bdba3d0})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i433" id="{c95a4e8e-816d-4655-8c79-d736da1adb6d}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i816" id="noOpus@outlook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1228" id="unblocker30__web@unblocker.yt">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i818" id="contentarget@maildrop.cc">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i590" id="{94cd2cc3-083f-49ba-a218-4cda4b4829fd}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i746" id="{58d2a791-6199-482f-a9aa-9b725ec61362}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1414" id="/^new@kuot\.pro|{13ec6687-0b15-4f01-a5a0-7a891c18e4ee}|rebeccahoppkins(ty(tr)?)?@gmail\.com|{501815af-725e-45be-b0f2-8f36f5617afc}|{9bdb5f1f-b1e1-4a75-be31-bdcaace20a99}|{e9d93e1d-792f-4f95-b738-7adb0e853b7b}|dojadewaskurwa@gmail\.com$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i690" id="{55dce8ba-9dec-4013-937e-adbf9317d990">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i840" id="{4889ddce-7a83-45e6-afc9-1e4f1149fff4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1223" id="tmbepff@trendmicro.com">
+ <prefs/>
+ <versionRange minVersion="9.2" maxVersion="9.2.0.1023" severity="1"/>
+ <versionRange minVersion="0" maxVersion="9.1.0.1035" severity="1"/>
+ </emItem>
+ <emItem blockID="i1278" id="/^(ff\-)?dodate(kKKK|XkKKK|k|kk|kkx|kR)@(firefox|flash(1)?)\.pl|dode(ee)?k@firefoxnet\.pl|(addon|1)@upsolutions\.pl$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i168" id="flashX@adobe.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1012" id="wxtui502n2xce9j@no14">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i516" id="/^({3f3cddf8-f74d-430c-bd19-d2c9147aed3d}|{515b2424-5911-40bd-8a2c-bdb20286d8f5}|{17464f93-137e-4646-a0c6-0dc13faf0113}|{d1b5aad5-d1ae-4b20-88b1-feeaeb4c1ebc}|{aad50c91-b136-49d9-8b30-0e8d3ead63d0})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i491" id="{515b2424-5911-40bd-8a2c-bdb20286d8f5}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i136" id="Adobe@flash.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i504" id="aytac@abc.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i848" id="bcVX5@nQm9l.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i680" id="jid1-bKSXgRwy1UQeRA@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i582" id="discoverypro@discoverypro.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i69" id="{977f3b97-5461-4346-92c8-a14c749b77c9}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i846" id="PDVDZDW52397720@XDDWJXW57740856.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i142" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
+ <prefs/>
+ <versionRange minVersion="4.2" maxVersion="4.2" severity="3"/>
+ <versionRange minVersion="2.0.3" maxVersion="2.0.3" severity="3"/>
+ </emItem>
+ <emItem blockID="i578" id="jid1-XLjasWL55iEE1Q@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i535" id="/^ext@WebexpEnhancedV1alpha[0-9]+\.net$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1077" id="helper@vidscrab.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i566" id="{77BEC163-D389-42c1-91A4-C758846296A5}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i778" id="{f2456568-e603-43db-8838-ffa7c4a685c7}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1024" id="{458fb825-2370-4973-bf66-9d7142141847}">
+ <prefs>
+ <pref>app.update.auto</pref>
+ <pref>app.update.enabled</pref>
+ <pref>app.update.interval</pref>
+ <pref>app.update.url</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i856" id="/^({94d62e35-4b43-494c-bf52-ba5935df36ef}|firefox@advanceelite\.com|{bb7b7a60-f574-47c2-8a0b-4c56f2da9802})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i596" id="{b99c8534-7800-48fa-bd71-519a46cdc7e1}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i672" id="/^(saamazon@mybrowserbar\.com)|(saebay@mybrowserbar\.com)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i228" id="crossriderapp5060@crossrider.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1034" id="a88a77ahjjfjakckmmabsy278djasi@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i378" id="{a7aae4f0-bc2e-a0dd-fb8d-68ce32c9261f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i854" id="/^(7tG@zEb\.net|ru@gfK0J\.edu)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i372" id="5nc3QHFgcb@r06Ws9gvNNVRfH.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i548" id="/^firefox@(jumpflip|webconnect|browsesmart|mybuzzsearch|outobox|greygray|lemurleap|divapton|secretsauce|batbrowse|whilokii|linkswift|qualitink|browsefox|kozaka|diamondata|glindorus|saltarsmart|bizzybolt|websparkle)\.(com?|net|org|info|biz)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1016" id="jid1-uabu5A9hduqzCw@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i630" id="webbooster@iminent.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1213" id="unblocker20__web@unblocker.yt">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i788" id="{729c9605-0626-4792-9584-4cbe65b243e6}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i618" id="toolbar@ask.com">
+ <prefs/>
+ <versionRange minVersion="3.15.5" maxVersion="3.15.5.*" severity="1"/>
+ <versionRange minVersion="3.15.31" maxVersion="3.15.31.*" severity="1"/>
+ <versionRange minVersion="3.15.24" maxVersion="3.15.24.*" severity="1"/>
+ <versionRange minVersion="3.15.18" maxVersion="3.15.20.*" severity="1"/>
+ <versionRange minVersion="3.15.22" maxVersion="3.15.22.*" severity="1"/>
+ <versionRange minVersion="3.15.13" maxVersion="3.15.13.*" severity="1"/>
+ <versionRange minVersion="3.15.10" maxVersion="3.15.11.*" severity="1"/>
+ <versionRange minVersion="3.15.26" maxVersion="3.15.26.*" severity="1"/>
+ <versionRange minVersion="3.15.28" maxVersion="3.15.28.*" severity="1"/>
+ <versionRange minVersion="3.15.8" maxVersion="3.15.8.*" severity="1"/>
+ </emItem>
+ <emItem blockID="i256" id="/^[0-9a-f]+@[0-9a-f]+\.info/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i700" id="2bbadf1f-a5af-499f-9642-9942fcdb7c76@f05a14cc-8842-4eee-be17-744677a917ed.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i501" id="xivars@aol.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i440" id="{2d069a16-fca1-4e81-81ea-5d5086dcbd0c}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1279" id="dodatek@flash2.pl">
+ <prefs/>
+ <versionRange minVersion="1.3" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i584" id="{52b0f3db-f988-4788-b9dc-861d016f4487}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="0.1.9999999" severity="1"/>
+ </emItem>
+ <emItem blockID="i804" id="{ad7ce998-a77b-4062-9ffb-1d0b7cb23183}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i446" id="{E90FA778-C2B7-41D0-9FA9-3FEC1CA54D66}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i88" id="anttoolbar@ant.com">
+ <prefs/>
+ <versionRange minVersion="2.4.6.4" maxVersion="2.4.6.4" severity="1"/>
+ </emItem>
+ <emItem blockID="i442" id="pennerdu@faceobooks.ws">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i696" id="/^({fa95f577-07cb-4470-ac90-e843f5f83c52}|ffxtlbr@speedial\.com)$/">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i622" id="/^({ebd898f8-fcf6-4694-bc3b-eabc7271eeb1}|{46008e0d-47ac-4daa-a02a-5eb69044431a}|{213c8ed6-1d78-4d8f-8729-25006aa86a76}|{fa23121f-ee7c-4bd8-8c06-123d087282c5}|{19803860-b306-423c-bbb5-f60a7d82cde5})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i542" id="/^({bf67a47c-ea97-4caf-a5e3-feeba5331231}|{24a0cfe1-f479-4b19-b627-a96bf1ea3a56})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i838" id="{87b5a11e-3b54-42d2-9102-0a7cb1f79ebf}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i950" id="foxyproxy@eric.h.jung">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="4.5.5" severity="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.35"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem blockID="i524" id="/^({4e988b08-8c51-45c1-8d74-73e0c8724579}|{93ec97bf-fe43-4bca-a735-5c5d6a0a40c4}|{aed63b38-7428-4003-a052-ca6834d8bad3}|{0b5130a9-cc50-4ced-99d5-cda8cc12ae48}|{C4CFC0DE-134F-4466-B2A2-FF7C59A8BFAD})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i487" id="{df6bb2ec-333b-4267-8c4f-3f27dc8c6e07}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i660" id="youplayer@addons.mozilla.org">
+ <prefs/>
+ <versionRange minVersion="79.9.8" maxVersion="208.0.1" severity="3"/>
+ </emItem>
+ <emItem blockID="i1214" id="firefoxdav@icloud.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.4.22" severity="1"/>
+ </emItem>
+ <emItem blockID="i664" id="123456789@offeringmedia.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i497" id="{872b5b88-9db5-4310-bdd0-ac189557e5f5}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i734" id="profsites@pr.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i792" id="{8f894ed3-0bf2-498e-a103-27ef6e88899f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1263" id="axtara__web@axtara.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.1.1" severity="3"/>
+ </emItem>
+ <emItem blockID="i324" id="/^((34qEOefiyYtRJT@IM5Munavn\.com)|(Mro5Fm1Qgrmq7B@ByrE69VQfZvZdeg\.com)|(KtoY3KGxrCe5ie@yITPUzbBtsHWeCdPmGe\.com)|(9NgIdLK5Dq4ZMwmRo6zk@FNt2GCCLGyUuOD\.com)|(NNux7bWWW@RBWyXdnl6VGls3WAwi\.com)|(E3wI2n@PEHTuuNVu\.com)|(2d3VuWrG6JHBXbQdbr@3BmSnQL\.com))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1119" id="/^(test3@test.org|test2@test.org|test@test.org|support@mozilla.org)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i754" id="{bb7b7a60-f574-47c2-8a0b-4c56f2da9802}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i886" id="searchengine@gmail.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i441" id="{49c53dce-afa0-49a1-a08b-2eb8e8444128}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i774" id="x77IjS@xU.net">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i537" id="rally_toolbar_ff@bulletmedia.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i682" id="f6682b47-e12f-400b-9bc0-43b3ccae69d1@39d6f481-b198-4349-9ebe-9a93a86f9267.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
+ <prefs/>
+ <versionRange minVersion="0.1" maxVersion="5.2.0.7164" severity="1"/>
+ </emItem>
+ <emItem blockID="i1227" id="{A34CAF42-A3E3-11E5-945F-18C31D5D46B0}">
+ <prefs>
+ <pref>security.csp.enable</pref>
+ <pref>security.fileuri.strict_origin_policy</pref>
+ <pref>security.mixed_content.block_active_content</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i474" id="{906000a4-88d9-4d52-b209-7a772970d91f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i716" id="{cc6cc772-f121-49e0-b1f0-c26583cb0c5e}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i431" id="chinaescapeone@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i748" id="{32da2f20-827d-40aa-a3b4-2fc4a294352e}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i732" id="{e935dd68-f90d-46a6-b89e-c4657534b353}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i473" id="{81b13b5d-fba1-49fd-9a6b-189483ac548a}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i402" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i432" id="lugcla21@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1210" id="auto-plugin-checker@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i471" id="firefox@luckyleap.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1040" id="frhegnejkgner@grhjgewfewf.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i980" id="wHO@W9.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i544" id="/^(93abedcf-8e3a-4d02-b761-d1441e437c09@243f129d-aee2-42c2-bcd1-48858e1c22fd\.com|9acfc440-ac2d-417a-a64c-f6f14653b712@09f9a966-9258-4b12-af32-da29bdcc28c5\.com|58ad0086-1cfb-48bb-8ad2-33a8905572bc@5715d2be-69b9-4930-8f7e-64bdeb961cfd\.com)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i91" id="crossriderapp4926@crossrider.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="0.81.43" severity="1"/>
+ </emItem>
+ <emItem blockID="i740" id="ascsurfingprotection@iobit.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i444" id="fplayer@adobe.flash">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i550" id="colmer@yopmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i822" id="{6af08a71-380e-42dd-9312-0111d2bc0630}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1036" id="HxLVJK1ioigz9WEWo8QgCs3evE7uW6LEExAniBGG@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i678" id="{C4A4F5A0-4B89-4392-AFAC-D58010E349AF}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i467" id="plugin@analytic-s.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i742" id="{f894a29a-f065-40c3-bb19-da6057778493}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i624" id="/^({b95faac1-a3d7-4d69-8943-ddd5a487d966}|{ecce0073-a837-45a2-95b9-600420505f7e}|{2713b394-286f-4d7c-89ea-4174eeab9f5a}|{da7a20cf-bef4-4342-ad78-0240fdf87055})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i439" id="{d2cf9842-af95-48cd-b873-bfbb48cd7f5e}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i658" id="low_quality_flash@pie2k.com">
+ <prefs/>
+ <versionRange minVersion="46.2" maxVersion="47.1" severity="3"/>
+ </emItem>
+ <emItem blockID="i1231" id="youtube@downloader.yt">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i465" id="trtv3@trtv.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i286" id="{58bd07eb-0ee0-4df0-8121-dc9b693373df}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i167" id="{b64982b1-d112-42b5-b1e4-d3867c4533f8}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1128" id="youtubeunblocker@unblocker.yt">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i814" id="liiros@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1136" id="/^({1f43c8af-e9e4-4e5a-b77a-f51c7a916324}|{3a3bd700-322e-440a-8a6a-37243d5c7f92}|{6a5b9fc2-733a-4964-a96a-958dd3f3878e}|{7b5d6334-8bc7-4bca-a13e-ff218d5a3f17}|{b87bca5b-2b5d-4ae8-ad53-997aa2e238d4}|{bf8e032b-150f-4656-8f2d-6b5c4a646e0d})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1030" id="support@todoist.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="3.9" severity="1"/>
+ </emItem>
+ <emItem blockID="i982" id="odtffplugin@ibm.com">
+ <prefs/>
+ <versionRange minVersion="9.0.1.1" maxVersion="9.0.1.100" severity="1"/>
+ </emItem>
+ <emItem blockID="i670" id="/^({ad9a41d2-9a49-4fa6-a79e-71a0785364c8})|(ffxtlbr@mysearchdial\.com)$/">
+ <prefs>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i684" id="{9edd0ea8-2819-47c2-8320-b007d5996f8a}">
+ <prefs>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i39" id="{c2d64ff7-0ab8-4263-89c9-ea3b0f8f050c}">
+ <prefs/>
+ <versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1"/>
+ </emItem>
+ <emItem blockID="i988" id="{b12785f5-d8d0-4530-a3ea-5c4263b85bef}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i724" id="{1cdbda58-45f8-4d91-b566-8edce18f8d0a}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i646" id="{e1aaa9f8-4500-47f1-9a0a-b02bd60e4076}">
+ <prefs/>
+ <versionRange minVersion="178.7.0" maxVersion="178.7.0" severity="3"/>
+ </emItem>
+ <emItem blockID="i438" id="{02edb56b-9b33-435b-b7df-b2843273a694}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i868" id="{6e7f6f9f-8ce6-4611-add2-05f0f7049ee6}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i598" id="{29b136c9-938d-4d3d-8df8-d649d9b74d02}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i541" id="/^({988919ff-0cd8-4d0c-bc7e-60d55a49eb64}|{494b9726-9084-415c-a499-68c07e187244}|{55b95864-3251-45e9-bb30-1a82589aaff1}|{eef3855c-fc2d-41e6-8d91-d368f51b3055}|{90a1b331-c2b4-4933-9f63-ba7b84d60d58}|{d2cf9842-af95-48cd-b873-bfbb48cd7f5e})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i262" id="{167d9323-f7cc-48f5-948a-6f012831a69f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i800" id="{424b0d11-e7fe-4a04-b7df-8f2c77f58aaf}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i117" id="{ce7e73df-6a44-4028-8079-5927a588c948}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.0.8" severity="1"/>
+ </emItem>
+ <emItem blockID="i406" id="{bf7380fa-e3b4-4db2-af3e-9d8783a45bfc}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i996" id="9598582LLKmjasieijkaslesae@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i338" id="{1FD91A9C-410C-4090-BBCC-55D3450EF433}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i620" id="{21EAF666-26B3-4A3C-ABD0-CA2F5A326744}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i451" id="{e44a1809-4d10-4ab8-b343-3326b64c7cdd}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1050" id="87aukfkausiopoawjsuifhasefgased278djasi@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i570" id="jid1-vW9nopuIAJiRHw@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i688" id="firefox-extension@mozilla.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i165" id="{EEF73632-A085-4fd3-A778-ECD82C8CB297}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i668" id="/^(matchersite(pro(srcs?)?)?\@matchersite(pro(srcs?)?)?\.com)|((pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\@(pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\.com)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i588" id="quick_start@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1018" id="grjkntbhr@hgergerherg.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i708" id="{849ded12-59e9-4dae-8f86-918b70d213dc}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1032" id="KSqOiTeSJEDZtTGuvc18PdPmYodROmYzfpoyiCr2@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i768" id="{f2548724-373f-45fe-be6a-3a85e87b7711}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i526" id="/^({83a8ce1b-683c-4784-b86d-9eb601b59f38}|{ef1feedd-d8da-4930-96f1-0a1a598375c6}|{79ff1aae-701f-4ca5-aea3-74b3eac6f01b}|{8a184644-a171-4b05-bc9a-28d75ffc9505}|{bc09c55d-0375-4dcc-836e-0e3c8addfbda}|{cef81415-2059-4dd5-9829-1aef3cf27f4f})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i666" id="wecarereminder@bryan">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i509" id="contato@facefollow.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1265" id="@video_downloader_pro">
+ <prefs/>
+ <versionRange minVersion="1.2.1" maxVersion="1.2.5" severity="1"/>
+ </emItem>
+ <emItem blockID="i656" id="hdv@vovcacik.addons.mozilla.org">
+ <prefs/>
+ <versionRange minVersion="102.0" maxVersion="102.0" severity="3"/>
+ </emItem>
+ <emItem blockID="i507" id="4zffxtbr-bs@VideoDownloadConverter_4z.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="5.75.3.25126" severity="1"/>
+ </emItem>
+ <emItem blockID="i650" id="jid1-qj0w91o64N7Eeg@jetpack">
+ <prefs/>
+ <versionRange minVersion="39.5.1" maxVersion="47.0.4" severity="3"/>
+ </emItem>
+ <emItem blockID="i870" id="M1uwW0@47z8gRpK8sULXXLivB.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i318" id="ffxtlbr@incredibar.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i776" id="g@uzcERQ6ko.net">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i394" id="{7D4F1959-3F72-49d5-8E59-F02F8AA6815D}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i362" id="addon@defaulttab.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.4.4" severity="1"/>
+ </emItem>
+ <emItem blockID="i648" id="firefoxaddon@youtubeenhancer.com">
+ <prefs/>
+ <versionRange minVersion="199.7.0" maxVersion="208.7.0" severity="3"/>
+ <versionRange minVersion="199.7.0" maxVersion="199.7.0" severity="3"/>
+ <versionRange minVersion="208.7.0" maxVersion="208.7.0" severity="3"/>
+ </emItem>
+ <emItem blockID="i429" id="{B40794A0-7477-4335-95C5-8CB9BBC5C4A5}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i706" id="thefoxonlybetter@quicksaver">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="0.*" severity="3"/>
+ <versionRange minVersion="1.6.160" maxVersion="1.6.160" severity="3"/>
+ <versionRange minVersion="1.10" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i532" id="249911bc-d1bd-4d66-8c17-df533609e6d8@c76f3de9-939e-4922-b73c-5d7a3139375d.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i529" id="/^(torntv@torntv\.com|trtv3@trtv\.com|torntv2@torntv\.com|e2fd07a6-e282-4f2e-8965-85565fcb6384@b69158e6-3c3b-476c-9d98-ae5838c5b707\.com)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i714" id="{25dd52dc-89a8-469d-9e8f-8d483095d1e8}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i528" id="008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1261" id="support@lastpass.com">
+ <prefs/>
+ <versionRange minVersion="4.0.0a" maxVersion="4.1.20a" severity="1"/>
+ </emItem>
+ <emItem blockID="i495" id="kallow@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i547" id="{87934c42-161d-45bc-8cef-ef18abe2a30c}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="3.7.9999999999" severity="1"/>
+ </emItem>
+ <emItem blockID="i720" id="FXqG@xeeR.net">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i514" id="/^(67314b39-24e6-4f05-99f3-3f88c7cddd17@6c5fa560-13a3-4d42-8e90-53d9930111f9\.com|ffxtlbr@visualbee\.com|{7aeae561-714b-45f6-ace3-4a8aed6e227b}|{7093ee04-f2e4-4637-a667-0f730797b3a0}|{53c4024f-5a2e-4f2a-b33e-e8784d730938})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i884" id="detgdp@gmail.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i306" id="{ADFA33FD-16F5-4355-8504-DF4D664CFE10}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i437" id="{4933189D-C7F7-4C6E-834B-A29F087BFD23}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i398" id="{377e5d4d-77e5-476a-8716-7e70a9272da0}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i472" id="linksicle@linksicle.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i850" id="P2@D.edu">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i404" id="{a9bb9fa0-4122-4c75-bd9a-bc27db3f9155}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i634" id="jid1-4vUehhSALFNqCw@jetpack">
+ <prefs/>
+ <versionRange minVersion="100.7" maxVersion="100.7" severity="3"/>
+ <versionRange minVersion="99.7" maxVersion="99.7" severity="3"/>
+ </emItem>
+ <emItem blockID="i496" id="{ACAA314B-EEBA-48e4-AD47-84E31C44796C}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i882" id="69ffxtbr@PackageTracer_69.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i564" id="/^(firefox@vebergreat\.net|EFGLQA@78ETGYN-0W7FN789T87\.COM)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i448" id="{0134af61-7a0c-4649-aeca-90d776060cb3}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i322" id="jid0-Y6TVIzs0r7r4xkOogmJPNAGFGBw@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i864" id="{0A92F062-6AC6-8180-5881-B6E0C0DC2CC5}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i488" id="jid1-4P0kohSJxU1qGg@jetpack">
+ <prefs/>
+ <versionRange minVersion="1.2.50" maxVersion="1.2.50" severity="1"/>
+ </emItem>
+ <emItem blockID="i500" id="{2aab351c-ad56-444c-b935-38bffe18ad26}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1245" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="3.9.9" severity="1"/>
+ </emItem>
+ <emItem blockID="i510" id="{3c9a72a0-b849-40f3-8c84-219109c27554}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i222" id="dealcabby@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i452" id="{77beece6-3997-403a-92fa-0055bfcf88e5}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1078" id="/^(jid1-W4CLFIRExukJIFW@jetpack|jid1-W4CLFIRExukJIFW@jetpack_1|jid1-W3CLwrP[a-z]+@jetpack)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i766" id="/^[a-z0-9]+@foxysecure[a-z0-9]*\.com$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i503" id="{9CE11043-9A15-4207-A565-0C94C42D590D}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i466" id="afext@anchorfree.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i531" id="/^(4cb61367-efbf-4aa1-8e3a-7f776c9d5763@cdece6e9-b2ef-40a9-b178-291da9870c59\.com|0efc9c38-1ec7-49ed-8915-53a48b6b7600@e7f17679-2a42-4659-83c5-7ba961fdf75a\.com|6be3335b-ef79-4b0b-a0ba-b87afbc6f4ad@6bbb4d2e-e33e-4fa5-9b37-934f4fb50182\.com)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i392" id="{EEE6C361-6118-11DC-9C72-001320C79847}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i174" id="info@thebflix.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i994" id="addonhack@mozilla.kewis.ch">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i434" id="afurladvisor@anchorfree.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i676" id="SpecialSavings@SpecialSavings.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i460" id="{845cab51-d8d2-472f-8bd9-2b44642d97c2}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i640" id="jid0-l9BxpNUhx1UUgRfKigWzSfrZqAc@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i810" id="{41339ee8-61ed-489d-b049-01e41fd5d7e0}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i736" id="{c5e48979-bd7f-4cf7-9b73-2482a67a4f37}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i626" id="{20AD702C-661E-4534-8CE9-BA4EC9AD6ECC}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i493" id="12x3q@3244516.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i450" id="{dff137ae-1ffd-11e3-8277-b8ac6f996f26}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i172" id="info@bflix.info">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i461" id="{8E9E3331-D360-4f87-8803-52DE43566502}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i538" id="{354dbb0a-71d5-4e9f-9c02-6c88b9d387ba}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i520" id="/^({7316e43a-3ebd-4bb4-95c1-9caf6756c97f}|{0cc09160-108c-4759-bab1-5c12c216e005}|{ef03e721-f564-4333-a331-d4062cee6f2b}|{465fcfbb-47a4-4866-a5d5-d12f9a77da00}|{7557724b-30a9-42a4-98eb-77fcb0fd1be3}|{b7c7d4b0-7a84-4b73-a7ef-48ef59a52c3b})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i484" id="plugin@getwebcake.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i358" id="lfind@nijadsoft.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i836" id="hansin@topvest.id">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i540" id="/^(ffxtlbr@mixidj\.com|{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}|{67097627-fd8e-4f6b-af4b-ecb65e50112e}|{f6f0f973-a4a3-48cf-9a7a-b7a69c30d71a}|{a3d0e35f-f1da-4ccb-ae77-e9d27777e68d}|{1122b43d-30ee-403f-9bfa-3cc99b0caddd})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i314" id="crossriderapp8812@crossrider.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i447" id="{B18B1E5C-4D81-11E1-9C00-AFEB4824019B}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i70" id="psid-vhvxQHMZBOzUZA@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i812" id="{1e4ea5fc-09e5-4f45-a43b-c048304899fc}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1129" id="youtubeunblocker__web@unblocker.yt">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i752" id="savingsslider@mybrowserbar.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i770" id="{8dc5c42e-9204-2a64-8b97-fa94ff8a241f}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i998" id="meOYKQEbBBjH5Ml91z0p9Aosgus8P55bjTa4KPfl@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1038" id="344141-fasf9jas08hasoiesj9ia8ws@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i435" id="pluggets@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i674" id="crossriderapp12555@crossrider.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i320" id="torntv@torntv.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i820" id="{aab02ab1-33cf-4dfa-8a9f-f4e60e976d27}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i786" id="{63eb5ed4-e1b3-47ec-a253-f8462f205350}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i340" id="chiang@programmer.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i282" id="{33e0daa6-3af3-d8b5-6752-10e949c61516}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.1.999" severity="1"/>
+ </emItem>
+ <emItem blockID="i1079" id="/^(@9338379C-DD5C-4A45-9A36-9733DC806FAE|9338379C-DD5C-4A45-9A36-9733DC806FAE|@EBC7B466-8A28-4061-81B5-10ACC05FFE53|@bd6a97c0-4b18-40ed-bce7-3b7d3309e3c4222|@bd6a97c0-4b18-40ed-bce7-3b7d3309e3c4|@b2d6a97c0-4b18-40ed-bce7-3b7d3309e3c4222)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1042" id="gjhrjenrengoe@jfdnkwelfwkm.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i42" id="{D19CA586-DD6C-4a0a-96F8-14644F340D60}">
+ <prefs/>
+ <versionRange minVersion="0.1" maxVersion="14.4.0" severity="1"/>
+ </emItem>
+ <emItem blockID="i1212" id="unblocker20@unblocker.yt">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="2.0.0" severity="3"/>
+ </emItem>
+ <emItem blockID="i539" id="ScorpionSaver@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i712" id="{a2bfe612-4cf5-48ea-907c-f3fb25bc9d6b}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i580" id="{51c77233-c0ad-4220-8388-47c11c18b355}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="0.1.9999999" severity="1"/>
+ </emItem>
+ <emItem blockID="i842" id="{746505DC-0E21-4667-97F8-72EA6BCF5EEF}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i505" id="extacylife@a.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1022" id="g99hiaoekjoasiijdkoleabsy278djasi@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i115" id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i530" id="{739df940-c5ee-4bab-9d7e-270894ae687a}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i477" id="mbrnovone@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i858" id="fftoolbar2014@etech.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i490" id="now.msn.com@services.mozilla.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i163" id="info@allpremiumplay.info">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i638" id="{7b1bf0b6-a1b9-42b0-b75d-252036438bdc}">
+ <prefs/>
+ <versionRange minVersion="27.8" maxVersion="27.9" severity="3"/>
+ </emItem>
+ <emItem blockID="i426" id="addlyrics@addlyrics.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i642" id="{bee6eb20-01e0-ebd1-da83-080329fb9a3a}">
+ <prefs/>
+ <versionRange minVersion="40.10.1" maxVersion="44.10.1" severity="3"/>
+ </emItem>
+ <emItem blockID="i710" id="{e0352044-1439-48ba-99b6-b05ed1a4d2de}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i523" id="/^({7e8a1050-cf67-4575-92df-dcc60e7d952d}|{b3420a9c-a397-4409-b90d-bcf22da1a08a}|{eca6641f-2176-42ba-bdbe-f3e327f8e0af}|{707dca12-3f99-4d94-afea-06dcc0ae0108}|{aea20431-87fc-40be-bc5b-18066fe2819c}|{30ee6676-1ba6-455a-a7e8-298fa863a546})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i756" id="{5eeb83d0-96ea-4249-942c-beead6847053}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i508" id="advance@windowsclient.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i498" id="hoverst@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i360" id="ytd@mybrowserbar.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i220" id="pricepeep@getpricepeep.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="2.1.0.19.99" severity="1"/>
+ </emItem>
+ <emItem blockID="i568" id="thunder@xunlei.com" os="Darwin">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="2.0.6" severity="1"/>
+ </emItem>
+ <emItem blockID="i730" id="25p@9eAkaLq.net">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i400" id="{dd6b651f-dfb9-4142-b0bd-09912ad22674}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i521" id="/^({66b103a7-d772-4fcd-ace4-16f79a9056e0}|{6926c7f7-6006-42d1-b046-eba1b3010315}|{72cabc40-64b2-46ed-8648-26d831761150}|{73ee2cf2-7b76-4c49-b659-c3d8cf30825d}|{ca6446a5-73d5-4c35-8aa1-c71dc1024a18}|{5373a31d-9410-45e2-b299-4f61428f0be4})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i478" id="{7e8a1050-cf67-4575-92df-dcc60e7d952d}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i970" id="hha8771ui3-Fo9j9h7aH98jsdfa8sda@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i772" id="{72b98dbc-939a-4e0e-b5a9-9fdbf75963ef}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i356" id="{341f4dac-1966-47ff-aacf-0ce175f1498a}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i344" id="lrcsTube@hansanddeta.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i430" id="1chtw@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i336" id="CortonExt@ext.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i506" id="/^ext@bettersurfplus/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i554" id="lightningnewtab@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i808" id="{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i469" id="OKitSpace@OKitSpace.es">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i726" id="{d87d56b2-1379-49f4-b081-af2850c79d8e}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1230" id="addon@gemaoff">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i543" id="{badea1ae-72ed-4f6a-8c37-4db9a4ac7bc9}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i216" id="fdm_ffext@freedownloadmanager.org">
+ <prefs/>
+ <versionRange minVersion="1.5.7.5" maxVersion="1.5.7.5" severity="1"/>
+ </emItem>
+ <emItem blockID="i536" id="{25D77636-38B1-1260-887C-2D4AFA92D6A4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i874" id="/^toolbar[0-9]*@findwide\.com$/">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i764" id="prositez@prz.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i455" id="7d51fb17-b199-4d8f-894e-decaff4fc36a@a298838b-7f50-4c7c-9277-df6abbd42a0c.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i968" id="{184AA5E6-741D-464a-820E-94B3ABC2F3B4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i844" id="e9d197d59f2f45f382b1aa5c14d82@8706aaed9b904554b5cb7984e9.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i449" id="gystqfr@ylgga.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i888" id="istart_ffnt@gmail.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i790" id="JMLv@njMaHh.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i750" id="{46eddf51-a4f6-4476-8d6c-31c5187b2a2f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1000" id="jufa098j-LKooapd9jasJ9jliJsd@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i308" id="9518042e-7ad6-4dac-b377-056e28d00c8f@f1cc0a13-4df1-4d66-938f-088db8838882.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i652" id="garg_sms@yahoo.in">
+ <prefs/>
+ <versionRange minVersion="67.9" maxVersion="67.9" severity="3"/>
+ </emItem>
+ <emItem blockID="i346" id="{a6e67e6f-8615-4fe0-a599-34a73fc3fba5}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i348" id="{13c9f1f9-2322-4d5c-81df-6d4bf8476ba4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i662" id="imbaty@taringamp3.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i519" id="703db0db-5fe9-44b6-9f53-c6a91a0ad5bd@7314bc82-969e-4d2a-921b-e5edd0b02cf1.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1266" id="@stopad">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="0.0.4" severity="1"/>
+ </emItem>
+ <emItem blockID="i533" id="extension@Fast_Free_Converter.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i718" id="G4Ce4@w.net">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i224" id="{336D0C35-8A85-403a-B9D2-65C292C39087}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1264" id="suchpony@suchpony.de">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.6.7" severity="3"/>
+ </emItem>
+ <emItem blockID="i560" id="adsremoval@adsremoval.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i525" id="/^({65f9f6b7-2dae-46fc-bfaf-f88e4af1beca}|{9ed31f84-c8b3-4926-b950-dff74047ff79}|{0134af61-7a0c-4649-aeca-90d776060cb3}|{02edb56b-9b33-435b-b7df-b2843273a694}|{da51d4f6-3e7e-4ef8-b400-9198e0874606}|{b24577db-155e-4077-bb37-3fdd3c302bb5})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i698" id="{6b2a75c8-6e2e-4267-b955-43e25b54e575}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i728" id="l@AdLJ7uz.net">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i111" id="{C3949AC2-4B17-43ee-B4F1-D26B9D42404D}" os="WINNT">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="15.0.5" severity="1"/>
+ </emItem>
+ <emItem blockID="i196" id="info@wxdownloadmanager.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i780" id="{b6ef1336-69bb-45b6-8cba-e578fc0e4433}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i802" id="{18d5a8fe-5428-485b-968f-b97b05a92b54}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i342" id="lbmsrvfvxcblvpane@lpaezhjez.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i972" id="831778-poidjao88DASfsAnindsd@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i744" id="{84a93d51-b7a9-431e-8ff8-d60e5d7f5df1}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i492" id="{af95cc15-3b9b-45ae-8d9b-98d08eda3111}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i782" id="safebrowse@safebrowse.co">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i517" id="/^({16e193c8-1706-40bf-b6f3-91403a9a22be}|{284fed43-2e13-4afe-8aeb-50827d510e20}|{5e3cc5d8-ed11-4bed-bc47-35b4c4bc1033}|{7429e64a-1fd4-4112-a186-2b5630816b91}|{8c9980d7-0f09-4459-9197-99b3e559660c}|{8f1d9545-0bb9-4583-bb3c-5e1ac1e2920c})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i586" id="jid1-0xtMKhXFEs4jIg@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i468" id="05dd836e-2cbd-4204-9ff3-2f8a8665967d@a8876730-fb0c-4057-a2fc-f9c09d438e81.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i866" id="faststartff@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i382" id="{6926c7f7-6006-42d1-b046-eba1b3010315}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i546" id="firefox@browsefox.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i226" id="{462be121-2b54-4218-bf00-b9bf8135b23f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i762" id="/^({2d7886a0-85bb-4bf2-b684-ba92b4b21d23}|{2fab2e94-d6f9-42de-8839-3510cef6424b}|{c02397f7-75b0-446e-a8fa-6ef70cfbf12b}|{8b337819-d1e8-48d3-8178-168ae8c99c36}|firefox@neurowise.info|firefox@allgenius.info)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i784" id="{41e5ef7a-171d-4ab5-8351-951c65a29908}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
+ <prefs/>
+ <versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1"/>
+ </emItem>
+ <emItem blockID="i628" id="ffxtlbr@iminent.com">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1126" id="{bbea93c6-64a3-4a5a-854a-9cc61c8d309e}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i489" id="astrovia@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i140" id="mozillahmpg@mozilla.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i376" id="{9e09ac65-43c0-4b9d-970f-11e2e9616c55}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i562" id="iobitapps@mybrowserbar.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i350" id="sqlmoz@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i374" id="update@firefox.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i862" id="{CA8C84C6-3918-41b1-BE77-049B2BDD887C}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i686" id="{a7f2cb14-0472-42a1-915a-8adca2280a2c}">
+ <prefs>
+ <pref>browser.startup.homepage</pref>
+ <pref>browser.search.defaultenginename</pref>
+ </prefs>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i100" id="{394DCBA4-1F92-4f8e-8EC9-8D2CB90CB69B}">
+ <prefs/>
+ <versionRange minVersion="2.5.0" maxVersion="2.5.0" severity="1"/>
+ </emItem>
+ <emItem blockID="i852" id="6lIy@T.edu">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1058" id="amo-validator-bypass@example.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i436" id="/(\{7aeae561-714b-45f6-ace3-4a8aed6e227b\})|(\{01e86e69-a2f8-48a0-b068-83869bdba3d0\})|(\{77f5fe49-12e3-4cf5-abb4-d993a0164d9e\})/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i107" id="{ABDE892B-13A8-4d1b-88E6-365A6E755758}" os="WINNT">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="15.0.5" severity="1"/>
+ </emItem>
+ <emItem blockID="i872" id="search-snacks@search-snacks.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i370" id="happylyrics@hpyproductions.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i482" id="brasilescapeeight@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i483" id="brasilescapefive@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i552" id="jid0-O6MIff3eO5dIGf5Tcv8RsJDKxrs@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i522" id="/^({976cd962-e0ca-4337-aea7-d93fae63a79c}|{525ba996-1ce4-4677-91c5-9fc4ead2d245}|{91659dab-9117-42d1-a09f-13ec28037717}|{c1211069-1163-4ba8-b8b3-32fc724766be})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i162" id="{EB7508CA-C7B2-46E0-8C04-3E94A035BD49}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i494" id="/^({e9df9360-97f8-4690-afe6-996c80790da4}|{687578b9-7132-4a7a-80e4-30ee31099e03}|{46a3135d-3683-48cf-b94c-82655cbc0e8a}|{49c795c2-604a-4d18-aeb1-b3eba27e5ea2}|{7473b6bd-4691-4744-a82b-7854eb3d70b6}|{96f454ea-9d38-474f-b504-56193e00c1a5})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i476" id="mbroctone@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i952" id="foxyproxy-basic@eric.h.jung">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="3.5.5" severity="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.35"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem blockID="i453" id="/^brasilescape.*\@facebook\.com$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i479" id="mbrsepone@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i545" id="superlrcs@svenyor.net">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i966" id="{5C655500-E712-41e7-9349-CE462F844B19}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.0.1-signed" severity="1"/>
+ </emItem>
+ <emItem blockID="i486" id="xz123@ya456.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i304" id="{f0e59437-6148-4a98-b0a6-60d557ef57f4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1262" id="my7thfakeid@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i396" id="/@(ft|putlocker|clickmovie|m2k|sharerepo|smarter-?)downloader\.com$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i549" id="/^firefox@(albrechto|swiftbrowse|springsmart|storimbo|squirrelweb|betterbrowse|lizardlink|rolimno|browsebeyond|clingclang|weblayers|kasimos|higher-aurum|xaven|bomlabio)\.(com?|net|org|info|biz)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i352" id="vpyekkifgv@vpyekkifgv.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i364" id="{FE1DEEEA-DB6D-44b8-83F0-34FC0F9D1052}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i312" id="extension21804@extension21804.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i246" id="support@vide1flash2.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i527" id="/^({bfec236d-e122-4102-864f-f5f19d897f5e}|{3f842035-47f4-4f10-846b-6199b07f09b8}|{92ed4bbd-83f2-4c70-bb4e-f8d3716143fe})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i694" id="59D317DB041748fdB89B47E6F96058F3@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i470" id="extension@FastFreeConverter.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i380" id="{cc8f597b-0765-404e-a575-82aefbd81daf}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1425" id="/^(pdftoword@addingapps.com|jid0-EYTXLS0GyfQME5irGbnD4HksnbQ@jetpack|jid1-ZjJ7t75BAcbGCX@jetpack)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i1423" id="/^(@pluginscribens_firefox|extension@vidscrab.com|firefox@jjj.ee|firefox@shop-reward.de|FxExtPasteNGoHtk@github.lostdj|himanshudotrai@gmail.com|jid0-bigoD0uivzAMmt07zrf3OHqa418@jetpack|jid0-iXbAR01tjT2BsbApyS6XWnjDhy8@jetpack)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i99" id="pfzPXmnzQRXX6@2iABkVe.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i101" id="{3a12052a-66ef-49db-8c39-e5b0bd5c83fa}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i98" id="youtubeeing@youtuberie.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i78" id="socialnetworktools@mozilla.doslash.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i108" id="{28bfb930-7620-11e1-b0c4-0800200c9a66}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i64" id="royal@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i5" id="support@daemon-tools.cc">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.0.0.5" severity="1"/>
+ </emItem>
+ <emItem blockID="i16" id="{27182e60-b5f3-411c-b545-b44205977502}">
+ <prefs/>
+ <versionRange minVersion="1.0" maxVersion="1.0" severity="1"/>
+ </emItem>
+ <emItem blockID="i22" id="ShopperReports@ShopperReports.com">
+ <prefs/>
+ <versionRange minVersion="3.1.22.0" maxVersion="3.1.22.0" severity="1"/>
+ </emItem>
+ <emItem blockID="i52" id="ff-ext@youtube">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i79" id="GifBlock@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i127" id="plugin@youtubeplayer.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i68" id="flashupdate@adobe.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i86" id="{45147e67-4020-47e2-8f7a-55464fb535aa}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i76" id="crossriderapp3924@crossrider.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i55" id="youtube@youtube7.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
+ <prefs/>
+ <versionRange minVersion="2.0" maxVersion="2.0" severity="1"/>
+ </emItem>
+ <emItem blockID="i6" id="{3f963a5b-e555-4543-90e2-c3908898db71}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="8.5" severity="1"/>
+ </emItem>
+ <emItem blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i65" id="activity@facebook.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i54" id="applebeegifts@mozilla.doslash.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i93" id="{68b8676b-99a5-46d1-b390-22411d8bcd61}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
+ <prefs/>
+ <versionRange minVersion="2.2" maxVersion="2.2" severity="1"/>
+ </emItem>
+ <emItem blockID="i96" id="youtubeee@youtuber3.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i51" id="admin@youtubeplayer.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i12" id="masterfiler@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i67" id="youtube2@youtube2.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i103" id="kdrgun@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1900" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
+ <prefs/>
+ <versionRange minVersion="1.1b1" maxVersion="1.1b1" severity="1"/>
+ <versionRange minVersion="3.0.0" maxVersion="3.1.1" severity="1"/>
+ </emItem>
+ <emItem blockID="i59" id="ghostviewer@youtube2.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i73" id="a1g0a9g219d@a1.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i97" id="support3_en@adobe122.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i92" id="play5@vide04flash.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i58" id="webmaster@buzzzzvideos.info">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i71" id="youtube@2youtube.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i83" id="flash@adobee.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i63" id="youtube@youtuber.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i66" id="youtubeer@youtuber.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i56" id="flash@adobe.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i104" id="yasd@youasdr3.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i18" id="msntoolbar@msn.com">
+ <prefs/>
+ <versionRange minVersion=" 0" maxVersion="6.*" severity="1"/>
+ </emItem>
+ <emItem blockID="i109" id="{392e123b-b691-4a5e-b52f-c4c1027e749c}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i57" id="youtube@youtube3.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i105" id="{95ff02bc-ffc6-45f0-a5c8-619b8226a9de}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1233" id="cloudmask@cloudmask.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="2.0.788" severity="1"/>
+ </emItem>
+ <emItem blockID="i82" id="{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i60" id="youtb3@youtb3.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i90" id="videoplugin@player.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i84" id="pink@rosaplugin.info">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1424" id="/^(jid0-S9kkzfTvEmC985BVmf8ZOzA5nLM@jetpack|jid1-qps14pkDB6UDvA@jetpack|jid1-Tsr09YnAqIWL0Q@jetpack|shole@ats.ext|{38a64ef0-7181-11e3-981f-0800200c9a66}|eochoa@ualberta.ca)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i44" id="sigma@labs.mozilla">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i48" id="admin@youtubespeedup.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i47" id="youtube@youtube2.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i43" id="supportaccessplugin@gmail.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i21" id="support@update-firefox.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="i1492" id="googlotim@gmail.com">
+ <prefs/>
+ <versionRange minVersion="1.3.2" maxVersion="1.3.2" severity="1"/>
+ </emItem>
+ <emItem blockID="i1493" id="{de71f09a-3342-48c5-95c1-4b0f17567554}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.3.9" severity="3"/>
+ </emItem>
+ <emItem blockID="i1522" id="/^(ciscowebexstart1@cisco\.com|ciscowebexstart_test@cisco\.com|ciscowebexstart@cisco\.com|ciscowebexgpc@cisco\.com)$/">
+ <prefs/>
+ <versionRange minVersion="1.0.0" maxVersion="1.0.1" severity="1"/>
+ </emItem>
+ <emItem blockID="i1523" id="{a0d7ccb3-214d-498b-b4aa-0e8fda9a7bf7}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="20170120" severity="1"/>
+ </emItem>
+ <emItem blockID="i1524" id="ext@alibonus.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="1.20.9" severity="1"/>
+ </emItem>
+ <emItem blockID="04b25e3d-a725-493e-be07-cbd74fb37ea7" id="{95E84BD3-3604-4AAC-B2CA-D9AC3E55B64B}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="d6425f24-8c9e-4c0a-89b4-6890fc68d5c9" id="/^\{(9321F452-96D5-11E6-BC3E-3769C7AD2208)|({18ED1ECA-96D3-11E6-A373-BD66C7AD2208})\}$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="0f8344d0-8211-49a1-81be-c0084b3da9b1" id="fr@fbt.ovh">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="c142360c-4f93-467e-9717-b638aa085d95" id="/^(\{11112503-5e91-4299-bf4b-f8c07811aa50\})|(\{501815af-725e-45be-b0f2-8f36f5617afc\})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="edad04eb-ea16-42f3-a4a7-20dded33cc37" id="@safesearchscoutee">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="3fd71895-7fc6-4f3f-aa22-1cbb0c5fd922" id="/^({95E84BD3-3604-4AAC-B2CA-D9AC3E55B64B}|{E3605470-291B-44EB-8648-745EE356599A}|{95E5E0AD-65F9-4FFC-A2A2-0008DCF6ED25}|{FF20459C-DA6E-41A7-80BC-8F4FEFD9C575}|{6E727987-C8EA-44DA-8749-310C0FBE3C3E}|{12E8A6C2-B125-479F-AB3C-13B8757C7F04}|{EB6628CF-0675-4DAE-95CE-EFFA23169743})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="c806b01c-3352-4083-afd9-9a8ab6e00b19" id="html5@encoding">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="e16408c3-4e08-47fd-85a9-3cbbce534e95" id="WebProtection@360safe.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="5.0.0.1009" severity="3"/>
+ </emItem>
+ <emItem blockID="510bbd9b-b883-4837-90ab-8e353e27e1be" id="{3B4DE07A-DE43-4DBC-873F-05835FF67DCE}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="28736359-700e-4b61-9c50-0b533a6bac55" id="xdict@www.iciba.com">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="2.3.1" severity="1"/>
+ </emItem>
+ <emItem blockID="0a47a2f7-f07c-489b-bd39-88122a2dfe6a" id="@DA3566E2-F709-11E5-8E87-A604BC8E7F8B">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="5df16afc-c804-43c9-9de5-f1835403e5fb" id="@H99KV4DO-UCCF-9PFO-9ZLK-8RRP4FVOKD9O">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="d83011de-67a4-479b-a778-916a7232095b" id="{efda3854-2bd9-45a1-9766-49d7ff18931d}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="0.8.17.2" severity="1"/>
+ </emItem>
+ <emItem blockID="595e0e53-b76b-4188-a160-66f29c636094" id="@68eba425-7a05-4d62-82b1-1d6d5a51716b">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="9abc7502-bd6f-40d7-b035-abe721345360" id="{368eb817-31b4-4be9-a761-b67598faf9fa}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="ccebab59-7190-4258-8faa-a0b752dd5301" id="{8ab60777-e899-475d-9a4f-5f2ee02c7ea4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="84dd8a02-c879-4477-8ea7-bf2f225b0940" id="{87010166-e3d0-4db5-a394-0517917201df}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="28c805a9-e692-4ef8-b3ae-14e085c19ecd" id="{3602008d-8195-4860-965a-d01ac4f9ca96}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="f7569261-f575-4719-8202-552b20d013b0" id="{7e907a15-0a4c-4ff4-b64f-5eeb8f841349}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="10d9ce89-b8d4-4b53-b3d7-ecd192681f4e" id="{d03b6b0f-4d44-4666-a6d6-f16ad9483593}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="455772a3-8360-4f5a-9a5f-a45b904d0b51" id="{dfa727cb-0246-4c5a-843a-e4a8592cc7b9}">
+ <prefs/>
+ <versionRange minVersion="2.0.0" maxVersion="2.0.0" severity="1"/>
+ </emItem>
+ <emItem blockID="674b6e19-f087-4706-a91d-1e723ed6f79e" id="{1490068c-d8b7-4bd2-9621-a648942b312c}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="8088b39a-3e6d-4a17-a22f-3f95c0464bd6" id="{5b0f6d3c-10fd-414c-a135-dffd26d7de0f}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="4ca8206f-bc2a-4428-9439-7f3142dc08db" id="/^(\{ac06c6b2-3fd6-45ee-9237-6235aa347215\})|(\{d461cc1b-8a36-4ff0-b330-1824c148f326\})|(\{d1ab5ebd-9505-481d-a6cd-6b9db8d65977\})|(\{07953f60-447e-4f53-a5ef-ed060487f616\})|(\{2d3c5a5a-8e6f-4762-8aff-b24953fe1cc9\})|(\{f82b3ad5-e590-4286-891f-05adf5028d2f\})|(\{f96245ad-3bb0-46c5-8ca9-2917d69aa6ca\})|(\{2f53e091-4b16-4b60-9cae-69d0c55b2e78\})|(\{18868c3a-a209-41a6-855d-f99f782d1606\})|(\{47352fbf-80d9-4b70-9398-fb7bffa3da53\})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="5bf72f70-a611-4845-af3f-d4dabe8862b6" id="/^(\{1490068c-d8b7-4bd2-9621-a648942b312c\})|(\{d47ebc8a-c1ea-4a42-9ca3-f723fff034bd\})|(\{83d6f65c-7fc0-47d0-9864-a488bfcaa376\})|(\{e804fa4c-08e0-4dae-a237-8680074eba07\})|(\{ea618d26-780e-4f0f-91fd-2a6911064204\})|(\{ce93dcc7-f911-4098-8238-7f023dcdfd0d\})|(\{7eaf96aa-d4e7-41b0-9f12-775c2ac7f7c0\})|(\{b019c485-2a48-4f5b-be13-a7af94bc1a3e\})|(\{9b8a3057-8bf4-4a9e-b94b-867e4e71a50c\})|(\{eb3ebb14-6ced-4f60-9800-85c3de3680a4\})|(\{01f409a5-d617-47be-a574-d54325fe05d1\})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="9dfeee42-e6a8-49e0-8979-0648f7368239" id="/^({fce89242-66d3-4946-9ed0-e66078f172fc})|({0c4df994-4f4a-4646-ae5d-8936be8a4188})|({6cee30bc-a27c-43ea-ac72-302862db62b2})|({e08ebf0b-431d-4ed1-88bb-02e5db8b9443})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="5c092b0d-7205-43a1-aa75-b7a42372fb52" id="/^({2bb68b03-b528-4133-9fc4-4980fbb4e449}|{231e58ac-0f3c-460b-bb08-0e589360bec7}|{a506c5af-0f95-4107-86f8-3de05e2794c9}|{8886a262-1c25-490b-b797-2e750dd9f36b}|{65072bef-041f-492e-8a51-acca2aaeac70}|{6fa41039-572b-44a4-acd4-01fdaebf608d}|{87ba49bd-daba-4071-aedf-4f32a7e63dbe}|{95d58338-ba6a-40c8-93fd-05a34731dc0e}|{4cbef3f0-4205-4165-8871-2844f9737602}|{1855d130-4893-4c79-b4aa-cbdf6fee86d3}|{87dcb9bf-3a3e-4b93-9c85-ba750a55831a})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="a29aed6f-6546-4fa2-8131-df5c9a5427af" id="/^({d03b6b0f-4d44-4666-a6d6-f16ad9483593}|{767d394a-aa77-40c9-9365-c1916b4a2f84}|{a0ce2605-b5fc-4265-aa65-863354e85058}|{b7f366fa-6c66-46bf-8df2-797c5e52859f}|{4ad16913-e5cb-4292-974c-d557ef5ec5bb}|{3c3ef2a3-0440-4e77-9e3c-1ca8d48f895c}|{543f7503-3620-4f41-8f9e-c258fdff07e9}|{98363f8b-d070-47b6-acc6-65b80acac4f3}|{5af74f5a-652b-4b83-a2a9-f3d21c3c0010}|{484e0ba4-a20b-4404-bb1b-b93473782ae0}|{b99847d6-c932-4b52-9650-af83c9dae649})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="750aa293-3742-46b5-8761-51536afecaef" id="/^({30972e0a-f613-4c46-8c87-2e59878e7180}|{0599211f-6314-4bf9-854b-84cb18da97f8}|{4414af84-1e1f-449b-ac85-b79f812eb69b}|{2a8bec00-0ab0-4b4d-bd3d-4f59eada8fd8}|{bea8866f-01f8-49e9-92cd-61e96c05d288}|{046258c9-75c5-429d-8d5b-386cfbadc39d}|{c5d359ff-ae01-4f67-a4f7-bf234b5afd6e}|{fdc0601f-1fbb-40a5-84e1-8bbe96b22502}|{85349ea6-2b5d-496a-9379-d4be82c2c13d}|{640c40e5-a881-4d16-a4d0-6aa788399dd2}|{d42328e1-9749-46ba-b35c-cce85ddd4ace})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="aae78cd5-6b26-472e-ab2d-db4105911250" id="/^({f6df4ef7-14bd-43b5-90c9-7bd02943789c}|{ccb7b5d6-a567-40a2-9686-a097a8b583dd}|{9b8df895-fcdd-452a-8c46-da5be345b5bc}|{5cf77367-b141-4ba4-ac2a-5b2ca3728e81}|{ada56fe6-f6df-4517-9ed0-b301686a34cc}|{95c7ae97-c87e-4827-a2b7-7b9934d7d642}|{e7b978ae-ffc2-4998-a99d-0f4e2f24da82}|{115a8321-4414-4f4c-aee6-9f812121b446}|{bf153de7-cdf2-4554-af46-29dabfb2aa2d}|{179710ba-0561-4551-8e8d-1809422cb09f}|{9d592fd5-e655-461a-9b28-9eba85d4c97f})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="9a3fd797-0ab8-4286-9a1b-2b6c97f9075b" id="/^({4dac7c77-e117-4cae-a9f0-6bd89e9e26ab}|{cc689da4-203f-4a0c-a7a6-a00a5abe74c5}|{0eb4672d-58a6-4230-b74c-50ca3716c4b0}|{06a71249-ef35-4f61-b2c8-85c3c6ee5617}|{5280684d-f769-43c9-8eaa-fb04f7de9199}|{c2341a34-a3a0-4234-90cf-74df1db0aa49}|{85e31e7e-3e3a-42d3-9b7b-0a2ff1818b33}|{b5a35d05-fa28-41b5-ae22-db1665f93f6b}|{1bd8ba17-b3ed-412e-88db-35bc4d8771d7}|{a18087bb-4980-4349-898c-ca1b7a0e59cd}|{488e190b-d1f6-4de8-bffb-0c90cc805b62})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="2d4fe65b-6c02-4461-baa8-dda52e688cf6" id="/^({618baeb9-e694-4c7b-9328-69f35b6a8839}|{b91fcda4-88b0-4a10-9015-9365e5340563}|{04150f98-2d7c-4ae2-8979-f5baa198a577}|{4b1050c6-9139-4126-9331-30a836e75db9}|{1e6f5a54-2c4f-4597-aa9e-3e278c617d38}|{e73854da-9503-423b-ab27-fafea2fbf443}|{a2427e23-d349-4b25-b5b8-46960b218079}|{f92c1155-97b3-40f4-9d5b-7efa897524bb}|{c8e14311-4b2d-4eb0-9a6b-062c6912f50e}|{45621564-b408-4c29-8515-4cf1f26e4bc3}|{27380afd-f42a-4c25-b57d-b9012e0d5d48})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="c92f2a05-73eb-454e-9583-f6d2382d8bca" id="/^({abec23c3-478f-4a5b-8a38-68ccd500ec42}|{a83c1cbb-7a41-41e7-a2ae-58efcb4dc2e4}|{62237447-e365-487e-8fc3-64ddf37bdaed}|{b12cfdc7-3c69-43cb-a3fb-38981b68a087}|{1a927d5b-42e7-4407-828a-fdc441d0daae}|{dd1cb0ec-be2a-432b-9c90-d64c824ac371}|{82c8ced2-e08c-4d6c-a12b-3e8227d7fc2a}|{87c552f9-7dbb-421b-8deb-571d4a2d7a21})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="805ee80e-0929-4c92-93ed-062b98053f28" id="/^({d78d27f4-9716-4f13-a8b6-842c455d6a46})|({bd5ba448-b096-4bd0-9582-eb7a5c9c0948})|({0b24cf69-02b8-407d-83db-e7af04fc1f3e})|({e08d85c5-4c0f-4ce3-9194-760187ce93ba})|({1c7d6d9e-325a-4260-8213-82d51277fc31})|({8a0699a0-09c3-4cf1-b38d-fec25441650c})|({1e68848a-2bb7-425c-81a2-524ab93763eb})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="70f37cc7-9f8a-4d0f-a881-f0c56934fa75" id="/^({be5d0c88-571b-4d01-a27a-cc2d2b75868c})|({3908d078-e1db-40bf-9567-5845aa77b833})|({5b620343-cd69-49b8-a7ba-f9d499ee5d3d})|({6eee2d17-f932-4a43-a254-9e2223be8f32})|({e05ba06a-6d6a-4c51-b8fc-60b461ffecaf})|({a5808da1-5b4f-42f2-b030-161fd11a36f7})|({d355bee9-07f0-47d3-8de6-59b8eecba57b})|({a1f8e136-bce5-4fd3-9ed1-f260703a5582})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="32ffc62d-40c4-43ac-aa3f-7240978d0ad0" id="/^(\{0b24cf69-02b8-407d-83db-e7af04fc1f3e\})|(\{6feed48d-41d4-49b8-b7d6-ef78cc7a7cd7\})| (\{8a0699a0-09c3-4cf1-b38d-fec25441650c\})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="2104a522-bb2f-4b04-ad0d-b0c571644552" id="{ed352072-ddf0-4cb4-9cb6-d8aa3741c2de}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="646e2384-f894-41bf-b7fc-8879e0095109" id="/^(https|youtube)@vietbacsecurity\.com$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="cc5848e8-23d5-4655-b45c-dc239839b74e" id="/^(\{01c9a4a4-06dd-426b-9500-2ea6fe841b88\}|{5e024309-042c-4b9d-a634-5d92cf9c7514\}|{f4262989-6de0-4604-918f-663b85fad605\}|{e341ed12-a703-47fe-b8dd-5948c38070e4\}|{cd89045b-2e06-46bb-9e34-48e8799e5ef2\}|{ac296b47-7c03-486f-a1d6-c48b24419749\}|{5da81d3d-5db1-432a-affc-4a2fe9a70749\}|{df09f268-3c92-49db-8c31-6a25a6643896\}|{81ac42f3-3d17-4cff-85af-8b7f89c8826b\}|{09c8fa16-4eec-4f78-b19d-9b24b1b57e1e\}|{71639610-9cc3-47e0-86ed-d5b99eaa41d5\}|{83d38ac3-121b-4f28-bf9c-1220bd3c643b\}|{7f8bc48d-1c7c-41a0-8534-54adc079338f\})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="4e28ba5c-af62-4e53-a7a1-d33334571cf8" id="/^(\{fd0c36fa-6a29-4246-810b-0bb4800019cb\}|\{b9c1e5bf-6585-4766-93fc-26313ac59999\}|\{3de25fff-25e8-40e9-9ad9-fdb3b38bb2f4\})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="adfd98ef-cebc-406b-b1e0-61bd4c71e4b1" id="{f3c31b34-862c-4bc8-a98f-910cc6314a86}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="2447476f-043b-4d0b-9d3c-8e859c97d950" id="{44e4b2cf-77ba-4f76-aca7-f3fcbc2dda2f} ">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="48b14881-5f6b-4e48-afc5-3d9a7fae26a3" id="/{0c9970a2-6874-483b-a486-2296cfe251c2}|{01c9a4a4-06dd-426b-9500-2ea6fe841b88}|{1c981c7c-30e0-4ed2-955d-6b370e0a9d19}|{2aa275f8-fabc-4766-95b2-ecfc73db310b}|{2cac0be1-10a2-4a0d-b8c5-787837ea5955}|{2eb66f6c-94b3-44f5-9de2-22371236ec99}|{2f8aade6-8717-4277-b8b1-55172d364903}|{3c27c34f-8775-491a-a1c9-fcb15beb26d3}|{3f4dea3e-dbfc-428f-a88b-36908c459e20}|{3f4191fa-8f16-47d2-9414-36bfc9e0c2bf}|{4c140bc5-c2ad-41c3-a407-749473530904}|{05a21129-af2a-464c-809f-f2df4addf209}|{5da81d3d-5db1-432a-affc-4a2fe9a70749}|{5f4e63e4-351f-4a21-a8e5-e50dc72b5566}|{7c1df23b-1fd8-42b9-8752-71fff2b979de}|{7d5e24a1-7bef-4d09-a952-b9519ec00d20}|{7d932012-b4dd-42cc-8a78-b15ca82d0e61}|{7f8bc48d-1c7c-41a0-8534-54adc079338f}|{8a61507d-dc2f-4507-a9b7-7e33b8cbc31b}|{09c8fa16-4eec-4f78-b19d-9b24b1b57e1e}|{9ce2a636-0e49-4b8e-ad17-d0c156c963b0}|{11df9391-dba5-4fe2-bd48-37a9182b796d}|{23c65153-c21e-430a-a2dc-0793410a870d}|{36a4269e-4eef-4538-baea-9dafbf6a8e2f}|{37f8e483-c782-40ed-82e9-36f101b9e41f}|{63df223d-51cf-4f76-aad8-bbc94c895ed2}|{72c1ca96-c05d-46a7-bce1-c507ec3db4ea}|{76ce213c-8e57-4a14-b60a-67a5519bd7a7}|{79db6c96-d65a-4a64-a892-3d26bd02d2d9}|{81ac42f3-3d17-4cff-85af-8b7f89c8826b}|{83d38ac3-121b-4f28-bf9c-1220bd3c643b}|{86d98522-5d42-41d5-83c2-fc57f260a3d9}|{0111c475-01e6-42ea-a9b4-27bed9eb6092}|{214cb48a-ce31-4e48-82cf-a55061f1b766}|{216e0bcc-8a23-4069-8b63-d9528b437258}|{226b0fe6-f80f-48f1-9d8d-0b7a1a04e537}|{302ef84b-2feb-460e-85ca-f5397a77aa6a}|{408a506b-2336-4671-a490-83a1094b4097}|{419be4e9-c981-478e-baa0-937cf1eea1e8}|{0432b92a-bfcf-41b9-b5f0-df9629feece1}|{449e185a-dd91-4f7b-a23a-bbf6c1ca9435}|{591d1b73-5eae-47f4-a41f-8081d58d49bf}|{869b5825-e344-4375-839b-085d3c09ab9f}|{919fed43-3961-48d9-b0ef-893054f4f6f1}|{01166e60-d740-440c-b640-6bf964504b3c}|{2134e327-8060-441c-ba68-b167b82ff5bc}|{02328ee7-a82b-4983-a5f7-d0fc353698f0}|{6072a2a8-f1bc-4c9c-b836-7ac53e3f51e4}|{28044ca8-8e90-435e-bc63-a757af2fb6be}|{28092fa3-9c52-4a41-996d-c43e249c5f08}|{31680d42-c80d-4f8a-86d3-cd4930620369}|{92111c8d-0850-4606-904a-783d273a2059}|{446122cd-cd92-4d0c-9426-4ee0d28f6dca}|{829827cd-03be-4fed-af96-dd5997806fb4}|{4479446e-40f3-48af-ab85-7e3bb4468227}|{9263519f-ca57-4178-b743-2553a40a4bf1}|{71639610-9cc3-47e0-86ed-d5b99eaa41d5}|{84406197-6d37-437c-8d82-ae624b857355}|{93017064-dfd4-425e-a700-353f332ede37}|{a0ab16af-3384-4dbe-8722-476ce3947873}|{a0c54bd8-7817-4a40-b657-6dc7d59bd961}|{a2de96bc-e77f-4805-92c0-95c9a2023c6a}|{a3fbc8be-dac2-4971-b76a-908464cfa0e0}|{a42e5d48-6175-49e3-9e40-0188cde9c5c6}|{a893296e-5f54-43f9-a849-f12dcdee2c98}|{ac296b47-7c03-486f-a1d6-c48b24419749}|{b26bf964-7aa6-44f4-a2a9-d55af4b4eec0}|{be981b5e-1d9d-40dc-bd4f-47a7a027611c}|{be37931c-af60-4337-8708-63889f36445d}|{bfd92dfd-b293-4828-90c1-66af2ac688e6}|{c5cf4d08-0a33-4aa3-a40d-d4911bcc1da7}|{c488a8f5-ea3d-408d-809e-44e82c06ad9d}|{c661c2dc-00f9-4dc1-a9f6-bb2b7e1a4f8d}|{cd28aa38-d2f1-45a3-96c3-6cfd4702ef51}|{cd89045b-2e06-46bb-9e34-48e8799e5ef2}|{cf9d96ff-5997-439a-b32b-98214c621eee}|{d14acee6-f32b-4aa3-a802-6616003fc6a8}|{d97223b8-44e5-46c7-8ab5-e1d8986daf44}|{ddae89bd-6793-45d8-8ec9-7f4fb7212378}|{de3b1909-d4da-45e9-8da5-7d36a30e2fc6}|{df09f268-3c92-49db-8c31-6a25a6643896}|{e5bc3951-c837-4c98-9643-3c113fc8cf5e}|{e9ccb1f2-a8ba-4346-b43b-0d5582bce414}|{e341ed12-a703-47fe-b8dd-5948c38070e4}|{e2139287-2b0d-4f54-b3b1-c9a06c597223}|{ed352072-ddf0-4cb4-9cb6-d8aa3741c2de}|{f0b809eb-be22-432f-b26f-b1cadd1755b9}|{f1bce8e4-9936-495b-bf48-52850c7250ab}|{f01c3add-dc6d-4f35-a498-6b4279aa2ffa}|{f9e1ad25-5961-4cc5-8d66-5496c438a125}|{f4262989-6de0-4604-918f-663b85fad605}|{fc0d55bd-3c50-4139-9409-7df7c1114a9d}/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="0cd723fe-d33d-43a0-b84f-7a3cad253212" id="{42baa93e-0cff-4289-b79e-6ae88df668c4}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="f58729ec-f93c-41d9-870d-dd9c9fd811b6" id="/^(addon@fasterweb\.com|\{5f398d3f-25db-47f5-b422-aa2364ff6c0b\}|addon@fasterp\.com|addon@calculator)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="3d55fab0-ec1a-4bca-84c9-3b74f5d01509" id="/^.*extension.*@asdf\.pl$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="547037f2-97ae-435a-863c-efd7532668cd" id="{44685ba6-68b3-4895-879e-4efa29dfb578}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="baf7f735-d6b6-410a-8cc8-25c60f7c57e2" id="adbeaver@adbeaver.org">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="36f97298-8bef-4372-a548-eb829413bee9" id="/(__TEMPLATE__APPLICATION__@ruta-mapa\.com)|(application-3@findizer\.fr)|(application2@allo-pages\.fr)|(application2@bilan-imc\.fr)|(application2@lettres\.net)|(application2@search-maps-finder\.com)|(application-imcpeso@imc-peso\.com)|(application-meuimc@meu-imc\.com)|(application-us2@factorlove)|(application-us@misterdirections)|(application-us@yummmi\.es)|(application@amiouze\.fr)|(application@astrolignes\.com)|(application@blotyn\.com)|(application@bmi-result\.com)|(application@bmi-tw\.com)|(application@calcolo-bmi\.com)|(application@cartes-itineraires\.com)|(application@convertisseur\.pro)|(application@de-findizer\.fr)|(application@de-super-rezepte\.com)|(application@dermabeauty\.fr)|(application@dev\.squel\.v2)|(application@eu-my-drivingdirections\.com)|(application@fr-allo-pages\.fr)|(application@fr-catizz\.com)|(application@fr-mr-traduction\.com)|(application@good-recettes\.com)|(application@horaires\.voyage)|(application@imc-calcular\.com)|(application@imc-peso\.com)|(application@it-mio-percorso\.com)|(application@iti-maps\.fr)|(application@itineraire\.info)|(application@lbc-search\.com)|(application@les-pages\.com)|(application@lovincalculator\.com)|(application@lovintest\.com)|(application@masowe\.com)|(application@matchs\.direct)|(application@mein-bmi\.com)|(application@mes-resultats\.com)|(application@mestaf\.com)|(application@meu-imc\.com)|(application@mon-calcul-imc\.fr)|(application@mon-juste-poids\.com)|(application@mon-trajet\.com)|(application@my-drivingdirections\.com)|(application@people-show\.com)|(application@plans-reduc\.fr)|(application@point-meteo\.fr)|(application@poulixo\.com)|(application@quipage\.fr)|(application@quizdeamor\.com)|(application@quizdoamor\.com)|(application@quotient-retraite\.fr)|(application@recettes\.net)|(application@routenplaner-karten\.com)|(application@ruta-mapa\.com)|(application@satellite\.dev\.squel\.v2)|(application@search-bilan-imc\.fr)|(application@search-maps-finder\.com)|(application@slimness\.fr)|(application@start-bmi\.com)|(application@tests-moi\.com)|(application@tousmesjeux\.fr)|(application@toutlannuaire\.fr)|(application@tuto-diy\.com)|(application@ubersetzung-app\.com)|(application@uk-cookyummy\.com)|(application@uk-howlogin\.me)|(application@uk-myloap\.com)|(application@voyagevoyage\.co)|(application@wikimot\.fr)|(application@www\.plans-reduc\.fr)|(application@yummmi\.es)|(application@yummmies\.be)|(application@yummmies\.ch)|(application@yummmies\.fr)|(application@yummmies\.lu)|(application@zikplay\.fr)|(applicationY@search-maps-finder\.com)|(cmesapps@findizer\.fr)|(findizer-shopping@jetpack)|(\{8aaebb36-1488-4022-b7ec-29b790d12c17\})/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="1e5f5cb2-346c-422a-9aaa-29d8760949d2" id="{872f20ea-196e-4d11-8835-1cc4c877b1b8}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="feb2d0d7-1b76-4dba-bf84-42873a92af5f" id="/^({6ecb9f49-90f0-43a1-8f8a-e809ea4f732b})|(@googledashboard)|(@smashdashboard)|(@smash_tv)|(@smash_mov)|(@smashmovs)|(@smashtvs)|(@FirefoxUpdate)|({92b9e511-ac81-4d47-9b8f-f92dc872447e})|({3c841114-da8c-44ea-8303-78264edfe60b})|({116a0754-20eb-4fe5-bd35-575867a0b89e})|({6e6ff0fd-4ae4-49ae-ac0c-e2527e12359b})|({f992ac88-79d3-4960-870e-92c342ed3491})|({6ecb9f49-90f0-43a1-8f8a-e809ea4f732b})|({a512297e-4d3a-468c-bd1a-f77bd093f925})|({08c28c16-9fb6-4b32-9868-db37c1668f94})|({b4ab1a1d-e137-4c59-94d5-4f509358a81d})|({feedf4f8-08c1-451f-a717-f08233a64ec9})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="96b137e6-8cb5-44d6-9a34-4a4a76fb5e38" id="/^({b99ae7b1-aabb-4674-ba8f-14ed32d04e76})|({dfa77d38-f67b-4c41-80d5-96470d804d09})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="3ab9f100-e253-4080-b3e5-652f842ddb7a" id="/((@extcorp\.[a-z]+)|(@brcorporation\.com)|(@brmodcorp\.com)|(@teset\.com)|(@modext\.tech)|(@ext?mod\.net)|(@browcorporation\.org)|(@omegacorporation\.org)|(@browmodule\.com)|(@corpext\.net)|({6b50ddac-f5e0-4d9e-945b-e4165bfea5d6})|({fab6484f-b8a7-4ba9-a041-0f948518b80c})|({b797035a-7f29-4ff5-bd19-77f1b5e464b1})|({0f612416-5c5a-4ec8-b482-eb546af9cac4}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="3a123214-b4b6-410c-a061-bbaf0d168d31" id="/^(({41c14ab8-9958-44bf-b74e-af54c1f169a6})|({78054cb2-e3e8-4070-a8ad-3fd69c8e4707})|({0089b179-8f3d-44d9-bb18-582843b0757a})|({f44ddcb4-4cc0-4866-92fa-eefda60c6720})|({1893d673-7953-4870-8069-baac49ce3335})|({fb28cac0-c2aa-4e0c-a614-cf3641196237})|({d7dee150-da14-45ba-afca-02c7a79ad805})|(RandomNameTest@RandomNameTest\.com )|(corpsearchengine@mail\.ru)|(support@work\.org))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="cbfa5303-c1bf-49c8-87d8-259738a20064" id="@vkmad">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="0f0764d5-a290-428b-a5b2-3767e1d72c71" id="{38363d75-6591-4e8b-bf01-0270623d1b6c}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="5afea853-d029-43f3-a387-64ce9980742a" id="/^(contactus@unzipper.com|{72dcff4e-48ce-41d8-a807-823adadbe0c9}|{dc7d2ecc-9cc3-40d7-93ed-ef6f3219bd6f}|{994db3d3-ccfe-449a-81e4-f95e2da76843}|{25aef460-43d5-4bd0-aa3d-0a46a41400e6}|{178e750c-ae27-4868-a229-04951dac57f7})$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="ae8ae617-590d-430b-86d4-16364372b67f" id="^((@mixclouddownloader)|(all-down@james\.burrow)|(d\.lehr@chello\.at)|(easy-video-downloader@addonsmash)|(easy-youtube-mp3@james\.burrow)|(gid@addonsmash)|(gmail_panel@addon_clone)|(guid-reused-by-pk-907175)|(idm@addonsmash)|(image-picka@addonsmash)|(instant-idm@addon\.host)|(jdm@awesome\.addons)|(open-in-idm@addonsmash)|(open-in-idm@james\.burrow)|(open-in-vlc@awesome\.addons)|(saveimage@addonsmash)|(thundercross@addonsmash)|(vk-download@addon\.host)|(vk-music-downloader@addonsmash)|(whatsapp_popup@addons\.clone)|(ytb-down@james\.burrow)|(ytb-mp3-downloader@james\.burrow)|(\{0df8d631-7d88-401e-ba7e-af1425dded8a\})|(\{3c74e141-1993-4c04-b755-a66dd491bb47\})|(\{5cdd95c7-5d92-40c5-8e2a-8c52c90191d9\})|(\{40efedc0-8e48-404a-a779-f4016b25c0e6\})|(\{53d605ce-599b-4352-8a06-5e594b3d1822\})|(\{3697c1e8-27d7-4c63-a27e-ac16191a1545\})|(\{170503FA-3349-4F17-BC86-001888A5C8E2\})|(\{649558df-9461-4824-ad18-f2d4d4845ac8\})|(\{27875553-afd5-4365-86dc-019bcd60594c\})|(\{27875553-afd5-4365-86dc-019bcd60594c\})|(\{6e7624fa-7f70-4417-93db-1ec29c023275\})|(\{b1aea1f1-6bed-41ef-9679-1dfbd7b2554f\})|(\{b9acc029-d62b-4d23-b921-8e7aea34266a\})|(\{b9b59e13-4ac5-4eff-8dbe-c345b7619b3c\})|(\{b0186d2d-3126-4537-9186-a6f198547901\})|(\{b3e8fde8-6d97-4ac3-95e0-57b797f4c56b\})|(\{e6a9a96e-4a08-4719-b9bd-0e91c35aaabc\})|(\{e69a36e6-ee12-4fe6-87ca-66b77fc0ffbf\})|(\{ee3601f1-78ab-48bf-89ae-0cfe4aed1f2e\})|(\{f4ce48b3-ad14-4900-86cb-4604474c5b08\})|(\{f5c1262d-b1e8-44a4-b820-a834f0f6d605\}))$">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="1"/>
+ </emItem>
+ <emItem blockID="c7d7515d-563f-459f-821c-27d4cf825dbf" id="/^((@FirefoxUpdate)|(@googledashboard)|(@smash_mov)|(@smash_tv)|(@smashdashboard)|(@smashmovs)|(@smashtvs)|(\{0be01832-7cce-4457-b8ad-73b743914085\})|(\{0e1c683e-9f34-45f1-b365-a283befb471a\})|(\{0c72a72d-6b2e-4a0e-8a31-16581176052d\})|(\{0ccfc208-8441-4c27-b1cb-799accb04908\})|(\{0ede8d39-26f2-49c4-8014-dfc484f54a65\})|(\{1fc1f8e6-3575-4a6f-a4d1-c4ca1c36bd2a\})|(\{3a1d6607-e6a8-4012-9506-f14cd157c171\})|(\{03b3ac4d-59a3-4cc6-aa4d-9b39dd8b3196\})|(\{3bb6e889-ac7a-46ca-8eed-45ba4fbe75b5\})|(\{3c841114-da8c-44ea-8303-78264edfe60b\})|(\{3f3bcb3e-dd73-4410-b102-60a87fcb8323\})|(\{3f951165-fd85-42ae-96ef-6ff589a1fe72\})|(\{04c86cb3-5f52-4083-9e9a-e322dd02181a\})|(\{4d8b44ef-9b8b-4d82-b668-a49648d2749d\})|(\{4d25d2b4-6ae7-4a66-abc0-c3fca4cdddf6\})|(\{5c9a2eca-2126-4a84-82c0-efbf3d989371\})|(\{6ecb9f49-90f0-43a1-8f8a-e809ea4f732b\})|(\{6fb8289d-c6c8-4fe5-9a92-7dc6cbf35349\})|(\{7fea697d-327c-4d20-80d5-813a6fb26d86\})|(\{08a3e913-0bbc-42ba-96d7-3fa16aceccbf\})|(\{8b04086b-94a5-4161-910b-59e3e31e4364\})|(\{08c28c16-9fb6-4b32-9868-db37c1668f94\})|(\{8cd69708-2f5e-4282-a94f-3feebc4bce35\})|(\{8dc21e24-3883-4d01-b486-ef1d1106fa3d\})|(\{8f8cc21a-2097-488f-a213-f5786a2ccbbf\})|(\{9c8b93f7-3bf8-4762-b221-40c912268f96\})|(\{9ce66491-ef06-4da6-b602-98c2451f6395\})|(\{1e1acc1c-8daa-4c2e-ad05-5ef01ae65f1e\})|(\{10b0f607-1efa-4762-82a0-e0d9bbae4e48\})|(\{24f338d7-b539-49f1-b276-c9edc367a32d\})|(\{40c9030f-7a2f-4a58-9d0a-edccd8063218\})|(\{41f97b71-c7c6-40b8-83b1-a4dbff76f73d\})|(\{42f3034a-0c4a-4f68-a8fd-8a2440e3f011\})|(\{52d456e5-245a-4319-b8d2-c14fbc9755f0\})|(\{57ea692b-f9fe-42df-bf5e-af6953fba05a\})|(\{060c61d8-b48f-465d-aa4b-23325ea757c3\})|(\{65c1967c-6a5c-44dd-9637-0d4d8b4c339b\})|(\{65d40b64-b52a-46d8-b146-580ff91889cb\})|(\{75b7af0d-b4ed-4320-95c8-7ffd8dd2cb7c\})|(\{77fe9731-b683-4599-9b06-a5dcea63d432\})|(\{84b20d0c-9c87-4340-b4f8-1912df2ae70d\})|(\{92b9e511-ac81-4d47-9b8f-f92dc872447e\})|(\{95afafef-b580-4f66-a0fe-7f3e74be7507\})|(\{116a0754-20eb-4fe5-bd35-575867a0b89e\})|(\{118bf5f6-98b1-4543-b133-42fdaf3cbade\})|(\{248eacc4-195f-43b2-956c-b9ad1ae67529\})|(\{328f931d-83c1-4876-953c-ddc9f63fe3b4\})|(\{447fa5d3-1c27-4502-9e13-84452d833b89\})|(\{476a1fa9-bce8-4cb4-beff-cb31980cc521\})|(\{507a5b13-a8a3-4653-a4a7-9a03099acf48\})|(\{531bf931-a8c6-407b-a48f-8a53f43cd461\})|(\{544c7f83-ef54-4d17-aa91-274fa27514ef\})|(\{546ea388-2839-4215-af49-d7289514a7b1\})|(\{635cb424-0cd5-4446-afaf-6265c4b711b5\})|(\{654b21c7-6a70-446c-b9ac-8cac9592f4a9\})|(\{0668b0a7-7578-4fb3-a4bd-39344222daa3\})|(\{944ed336-d750-48f1-b0b5-3c516bfb551c\})|(\{1882a9ce-c0e3-4476-8185-f387fe269852\})|(\{5571a054-225d-4b65-97f7-3511936b3429\})|(\{5921be85-cddd-4aff-9b83-0b317db03fa3\})|(\{7082ba5c-f55e-4cd8-88d6-8bc479d3749e\})|(\{7322a4cb-641c-4ca2-9d83-8701a639e17a\})|(\{90741f13-ab72-443f-a558-167721f64883\})|(\{198627a5-4a7b-4857-b074-3040bc8effb8\})|(\{5e5b9f44-2416-4669-8362-42a0b3f97868\})|(\{824985b9-df2a-401c-9168-749960596007\})|(\{4853541f-c9d7-42c5-880f-fd460dbb5d5f\})|(\{6e6ff0fd-4ae4-49ae-ac0c-e2527e12359b\})|(\{90e8aa72-a7eb-4337-81d4-538b0b09c653\})|(\{02e3137a-96a4-433d-bfb2-0aa1cd4aed08\})|(\{9e734c09-fcb1-4e3f-acab-04d03625301c\})|(\{a6ad792c-69a8-4608-90f0-ff7c958ce508\})|(\{a512297e-4d3a-468c-bd1a-f77bd093f925\})|(\{a71b10ae-b044-4bf0-877e-c8aa9ad47b42\})|(\{a33358ad-a3fa-4ca1-9a49-612d99539263\})|(\{a7775382-4399-49bf-9287-11dbdff8f85f\})|(\{afa64d19-ddba-4bd5-9d2a-c0ba4b912173\})|(\{b4ab1a1d-e137-4c59-94d5-4f509358a81d\})|(\{b4ec2f8e-57fd-4607-bf4f-bc159ca87b26\})|(\{b06bfc96-c042-4b34-944c-8eb67f35630a\})|(\{b9dcdfb0-3420-4616-a4cb-d41b5192ba0c\})|(\{b8467ec4-ff65-45f4-b7c5-f58763bf9c94\})|(\{b48e4a17-0655-4e8e-a5e2-3040a3d87e55\})|(\{b6166509-5fe0-4efd-906e-1e412ff07a04\})|(\{bd1f666e-d473-4d13-bc4d-10dde895717e\})|(\{be572ad4-5dd7-4b6b-8204-5d655efaf3b3\})|(\{bf2a3e58-2536-44d4-b87f-62633256cf65\})|(\{bfc5ac5f-80bd-43e5-9acb-f6d447e0d2ce\})|(\{bfe3f6c1-c5fe-44af-93b3-576812cb6f1b\})|(\{c0b8009b-57dc-45bc-9239-74721640881d\})|(\{c1cf1f13-b257-4271-b922-4c57c6b6e047\})|(\{c3d61029-c52f-45df-8ec5-a654b228cd48\})|(\{c39e7c0b-79d5-4137-bef0-57cdf85c920f\})|(\{ce043eac-df8a-48d0-a739-ef7ed9bdf2b5\})|(\{cf62e95a-8ded-4c74-b3ac-f5c037880027\})|(\{cff02c70-7f07-4592-986f-7748a2abd9e1\})|(\{d1b87087-09c5-4e58-b01d-a49d714da2a2\})|(\{d14adc78-36bf-4cf0-9679-439e8371d090\})|(\{d64c923e-8819-488c-947f-716473d381b2\})|(\{d734e7e3-1b8e-42a7-a9b3-11b16c362790\})|(\{d147e8c6-c36e-46b1-b567-63a492390f07\})|(\{db1a103d-d1bb-4224-a5e1-8d0ec37cff70\})|(\{dec15b3e-1d12-4442-930e-3364e206c3c2\})|(\{dfa4b2e3-9e07-45a4-a152-cde1e790511d\})|(\{dfcda377-b965-4622-a89b-1a243c1cbcaf\})|(\{e4c5d262-8ee4-47d3-b096-42b8b04f590d\})|(\{e82c0f73-e42c-41dd-a686-0eb4b65b411c\})|(\{e60616a9-9b50-49d8-b1e9-cecc10a8f927\})|(\{e517649a-ffd7-4b49-81e0-872431898712\})|(\{e771e094-3b67-4c33-8647-7b20c87c2183\})|(\{eff5951b-b6d4-48f5-94c3-1b0e178dcca5\})|(\{f26a8da3-8634-4086-872e-e589cbf03375\})|(\{f992ac88-79d3-4960-870e-92c342ed3491\})|(\{f4e4fc03-be50-4257-ae99-5cd0bd4ce6d5\})|(\{f73636fb-c322-40e1-82fb-e3d7d06d9606\})|(\{f5128739-78d5-4ad7-bac7-bd1af1cfb6d1\})|(\{fc11e7f0-1c31-4214-a88f-6497c27b6be9\})|(\{feedf4f8-08c1-451f-a717-f08233a64ec9\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="52842139-3d11-41ac-9d7f-8e51122a3141" id="/^((\{ac296b47-7c03-486f-a1d6-c48b24419749\})|(\{1cab8ccf-deff-4743-925d-a47cbd0a6b56\})|(\{5da81d3d-5db1-432a-affc-4a2fe9a70749\})|(\{071b9878-a7d3-4ae3-8ef0-2eaee1923403\})|(\{261476ea-bd0e-477c-abd7-33cdf626f81f\})|(\{224e66d0-6b11-4c4b-9bcf-41180889898a\})|(\{1e90cf52-c67c-4bd9-80c3-a2bf521fc981\})|(\{09c4799c-00f1-439e-9e60-3827c589b372\})|(\{d3d2095a-9faa-466f-82ae-3114179b34d6\})|(\{70389ea5-7e4d-4515-835c-fbd047f229dd\})|(\{2e8083a5-cd88-4aaa-bb8b-e54e9753f280\})|(\{fbf2480b-5c19-478e-bfd0-192ad9f84dc9\})|(\{6c7dc694-89f8-477e-88d5-c55af4d6a846\})|(\{915c12c6-901a-490d-9bfc-20f00d1ad31d\})|(\{d3a4aa3e-f74c-4382-876d-825f592f2976\})|(\{0ad91ec1-f7c4-4a39-9244-3310e9fdd169\})|(\{9c17aa27-63c5-470a-a678-dc899ab67ed3\})|(\{c65efef2-9988-48db-9e0a-9ff8164182b6\})|(\{d54c5d25-2d51-446d-8d14-18d859e3e89a\})|(\{e458f1f1-a331-4486-b157-81cba19f0993\})|(\{d2de7e1f-6e51-41d6-ba8a-937f8a5c92ff\})|(\{2b08a649-9bea-4dd4-91c8-f53a84d38e19\})|(\{312dd57e-a590-4e19-9b26-90e308cfb103\})|(\{82ce595a-f9b6-4db8-9c97-b1f1c933418b\})|(\{0a2e64f0-ea5a-4fff-902d-530732308d8e\})|(\{5fbdc975-17ab-4b4e-90d7-9a64fd832a08\})|(\{28820707-54d8-41f0-93e9-a36ffb2a1da6\})|(\{64a2aed1-5dcf-4f2b-aad6-9717d23779ec\})|(\{ee54794f-cd16-4f7d-a7dd-515a36086f60\})|(\{4d381160-b2d5-4718-9a05-fc54d4b307e7\})|(\{60393e0e-f039-4b80-bad4-10189053c2ab\})|(\{0997b7b2-52d7-4d14-9aa6-d820b2e26310\})|(\{8214cbd6-d008-4d16-9381-3ef1e1415665\})|(\{6dec3d8d-0527-49a3-8f12-b05f2a8b95b2\})|(\{0c0d8d8f-3ae0-4c98-81ac-06453a316d16\})|(\{84d5ef02-a283-484a-80da-7087836c74aa\})|(\{24413756-2c44-47c5-8bbf-160cb37776d8\})|(\{cf6ac458-06e8-45d0-9cbf-ec7fc0eb1710\})|(\{263a5792-933a-4de1-820a-d04198e17120\})|(\{b5fd7f37-190d-4c0a-b8dd-8b4850c986ac\})|(\{cb5ef07b-c2e7-47a6-be81-2ceff8df4dd5\})|(\{311b20bc-b498-493c-a5e1-22ec32b0e83c\})|(\{b308aead-8bc1-4f37-9324-834b49903df7\})|(\{3a26e767-b781-4e21-aaf8-ac813d9edc9f\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="c37c7c24-e738-4d06-888c-108b4d63b428" id="/^((\{39bd8607-0af4-4d6b-bd69-9a63c1825d3c\})|(\{273f0bce-33f4-45f6-ae03-df67df3864c2\})|(\{a77fc9b9-6ebb-418d-b0b6-86311c191158\})|(\{c6c4a718-cf91-4648-aa9b-170d66163cf2\})|(\{d371abec-84bb-481b-acbf-235639451127\})|(\{e63b262a-f9b8-4496-9c4b-9d3cbd6aea90\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="f0713a5e-7208-484e-b3a0-4e6dc6a195be" id="{bee8b1f2-823a-424c-959c-f8f76c8b2306}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="4.0.7.3" severity="1"/>
+ </emItem>
+ <emItem blockID="e04f98b5-4480-43a3-881d-e509e4e28cdc" id="{dd3d7613-0246-469d-bc65-2a3cc1668adc}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="4.0.3" severity="1"/>
+ </emItem>
+ <emItem blockID="d664412d-ed08-4892-b247-b007a70856ff" id="/^((@asdfjhsdfuhw)|(@asdfsdfwe)|(@asdieieuss)|(@dghfghfgh)|(@difherk)|(@dsfgtftgjhrdf4)|(@fidfueir)|(@fsgergsdqtyy)|(@hjconsnfes)|(@isdifvdkf)|(@iweruewir)|(@oiboijdjfj)|(@safesearchavs)|(@safesearchavsext)|(@safesearchincognito)|(@safesearchscoutee)|(@sdfykhhhfg)|(@sdiosuff)|(@sdklsajd)|(@sduixcjksd)|(@sicognitores)|(@simtabtest)|(@sodiasudi)|(@test13)|(@test131)|(@test131ver)|(@test132)|(@test13s)|(@testmptys)|(\{ac4e5b0c-13c4-4bfd-a0c3-1e73c81e8bac\})|(\{e78785c3-ec49-44d2-8aac-9ec7293f4a8f\})|(general@filecheckerapp\.com)|(general@safesearch\.net))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="d0a401cb-0c70-4784-8288-b06a88b2ae8a" id="{5834f62d-6164-4cdd-a0a3-c00c66ec9d13}">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="cae5d906-0b1d-4d1c-b83f-f9727b8c4a29" id="/^((search-unlisted2@mozilla\.com)|(search-unlisted3@mozilla\.com)|(search-unlisted4@mozilla\.com)|(search-unlisted5@mozilla\.com)|(search-unlisted11@mozilla\.com)|(search-unlisted12@mozilla\.com)|(search-unlisted55@mozilla\.com)|(search-unlisted111@mozilla\.com)|(search-unlisted400@mozilla\.com)|(search-unlisted40110@mozilla\.com)|(search-unlisted17441000051@mozilla\.com)|(search-unlisted174410000522777441@mozilla\.com)|(search-unlisted@mozilla\.com)|({0a054930-63d7-46f4-937a-de80eab21da4})|({0b24cf69-02b8-407d-83db-e7af04fc1f3e})|({0c4df994-4f4a-4646-ae5d-8936be8a4188})|({0d50d8aa-d1ed-4930-b0a0-f3340d2f510e})|({0eb4672d-58a6-4230-b74c-50ca3716c4b0})|({0f9e469e-4245-43f8-a7a8-7e730f80d284})|({0fc9fcc7-2f47-4fd1-a811-6bd4d611294b})|({4479446e-40f3-48af-ab85-7e3bb4468227})|({1a927d5b-42e7-4407-828a-fdc441d0daae})|({1a760841-50c3-4143-9f7e-3c8f04e8f9d1})|({1bd8ba17-b3ed-412e-88db-35bc4d8771d7})|({1c7d6d9e-325a-4260-8213-82d51277fc31})|({01c9a4a4-06dd-426b-9500-2ea6fe841b88})|({1cab8ccf-deff-4743-925d-a47cbd0a6b56})|({1cb0652a-4645-412d-b7e8-0b9e9a83242f})|({1d6634ca-dd37-4a31-aad1-321f05aa2bb3})|({1d9997b2-f61e-429a-8591-999a6d62becc})|({1ed2af70-9e89-42db-a9e8-17ae594003ac})|({01f409a5-d617-47be-a574-d54325fe05d1})|({2a8bec00-0ab0-4b4d-bd3d-4f59eada8fd8})|({2aeb1f92-6ddc-49f5-b7b3-3872d7e019a9})|({2bb68b03-b528-4133-9fc4-4980fbb4e449})|({2cac0be1-10a2-4a0d-b8c5-787837ea5955})|({2d3c5a5a-8e6f-4762-8aff-b24953fe1cc9})|({2ee125f1-5a32-4f8e-b135-6e2a5a51f598})|({2f53e091-4b16-4b60-9cae-69d0c55b2e78})|({3a65e87c-7ffc-408d-927e-ebf1784efd6d})|({3a26e767-b781-4e21-aaf8-ac813d9edc9f})|({3c3ef2a3-0440-4e77-9e3c-1ca8d48f895c})|({3dca6517-0d75-42d2-b966-20467f82dca1})|({3f4191fa-8f16-47d2-9414-36bfc9e0c2bf})|({3f49e12b-bb58-4797-982c-4364030d96d9})|({4aa2f47a-0bae-4a47-8a1b-1b93313a2938})|({04abafc7-7a65-401d-97f3-af2853854373})|({4ad16913-e5cb-4292-974c-d557ef5ec5bb})|({4b1050c6-9139-4126-9331-30a836e75db9})|({4b1777ec-6fe4-4572-9a29-5af206e003bf})|({4beacbbb-1691-40e7-8c1e-4853ce2e2dee})|({4c140bc5-c2ad-41c3-a407-749473530904})|({4cbef3f0-4205-4165-8871-2844f9737602})|({4dac7c77-e117-4cae-a9f0-6bd89e9e26ab})|({04ed02dc-0cb0-40c2-8bc8-6f20843024b8})|({4f6b6aaf-c5a1-4fac-8228-ead4d359dc6d})|({4f8a15fb-45c2-4d3b-afb1-c0c8813a4a5a})|({5af74f5a-652b-4b83-a2a9-f3d21c3c0010})|({5b0f6d3c-10fd-414c-a135-dffd26d7de0f})|({5b421f02-e55e-4b63-b90e-aa0cfea01f53})|({5b620343-cd69-49b8-a7ba-f9d499ee5d3d})|({5c5cf69b-ed92-4429-8d26-ff3bb6c37269})|({5cf77367-b141-4ba4-ac2a-5b2ca3728e81})|({5da81d3d-5db1-432a-affc-4a2fe9a70749})|({5eac1066-90c3-4ba0-b361-e6315dcd6828})|({5ec4c837-59b9-496d-96e2-ff3fa74ca01f})|({5efd8c7a-ff37-41ac-a55c-af4170453fdf})|({5f4e63e4-351f-4a21-a8e5-e50dc72b5566})|({6a934ff5-e41d-43a2-baf5-2d215a869674})|({06a71249-ef35-4f61-b2c8-85c3c6ee5617})|({6ad26473-5822-4142-8881-0c56a8ebc8c0})|({6cee30bc-a27c-43ea-ac72-302862db62b2})|({6ed852d5-a72e-4f26-863f-f660e79a2ebb})|({6eee2d17-f932-4a43-a254-9e2223be8f32})|({6f13489d-b274-45b6-80fa-e9daa140e1a4})|({6fa41039-572b-44a4-acd4-01fdaebf608d})|({7ae85eef-49cf-440d-8d13-2bebf32f14cf})|({7b3c1e86-2599-4e1a-ad98-767ae38286c8})|({7b23c0de-aa3d-447f-9435-1e8eba216f09})|({7b71d75e-51f5-4a71-9207-7acb58827420})|({7c6bf09e-5526-4bce-9548-7458ec56cded})|({7ca54c8d-d515-4f2a-a21f-3d32951491a6})|({7d932012-b4dd-42cc-8a78-b15ca82d0e61})|({7d5e24a1-7bef-4d09-a952-b9519ec00d20})|({7eabad73-919d-4890-b737-8d409c719547})|({7eaf96aa-d4e7-41b0-9f12-775c2ac7f7c0})|({7f8bc48d-1c7c-41a0-8534-54adc079338f})|({7f84c4d8-bdf5-4110-a10d-fa2a6e80ef6a})|({8a6bda75-4668-4489-8869-a6f9ccbfeb84})|({8a0699a0-09c3-4cf1-b38d-fec25441650c})|({8ab8c1a2-70d4-41a8-bf78-0d0df77ac47f})|({8b4cb418-027e-4213-927a-868b33a88b4f})|({8fcfe2b3-598e-4861-a5d4-0d77993f984b})|({9a941038-82fa-4ae4-ba98-f2eb2d195345})|({9b8a3057-8bf4-4a9e-b94b-867e4e71a50c})|({9b8df895-fcdd-452a-8c46-da5be345b5bc})|({09c8fa16-4eec-4f78-b19d-9b24b1b57e1e})|({09cbfddf-5e55-4676-920d-5a16cb9e4cb5})|({9cf8d28f-f546-4871-ac4d-5faff8b5bde3})|({9d592fd5-e655-461a-9b28-9eba85d4c97f})|({9fc6e583-78a5-4a2b-8569-4297bb8b3300})|({014d98ce-dab9-4c1d-8643-166e75d7cb4d})|({18c64b09-4ccb-4c21-ba6f-ebd4a1efa034})|({21d83d85-a636-4b18-955d-376a6b19bd19})|({22ecf14b-ead6-4684-a498-7b2b839a4c97})|({23c65153-c21e-430a-a2dc-0793410a870d})|({29c69b12-8208-457e-92f4-e663b00a1f10})|({30a8d6f1-0401-4327-8c46-2e1ab45dfe77})|({30d63f93-1446-43b3-8219-deefec9c81ce})|({32cb52f8-c78a-423d-b378-0abec72304a6})|({35bfa8c0-68c1-41f8-a5dd-7f3b3c956da9})|({36a4269e-4eef-4538-baea-9dafbf6a8e2f})|({37f8e483-c782-40ed-82e9-36f101b9e41f})|({42a512a8-37e0-4e07-a1db-5b4651d75048})|({43ae5745-c40a-45ab-9c11-74316c0e9fd2})|({53fa8e1c-112b-4013-b582-0d9e8c51ca75})|({56effac7-3ae9-41e3-9b46-51469f67b3b2})|({61a486c0-ce3d-4bf1-b4f2-e186a2adecf1})|({62b55928-80cc-49f7-8a4b-ec06030d6601})|({63df223d-51cf-4f76-aad8-bbc94c895ed2})|({064d8320-e0f3-411f-9ed1-8c1349279d20})|({071b9878-a7d3-4ae3-8ef0-2eaee1923403})|({72c1ca96-c05d-46a7-bce1-c507ec3db4ea})|({76ce213c-8e57-4a14-b60a-67a5519bd7a7})|({78c2f6a0-3b54-4a21-bf25-a3348278c327})|({0079b71b-89c9-4d82-aea3-120ee12d9890})|({81ac42f3-3d17-4cff-85af-8b7f89c8826b})|({81dc4f0e-9dab-4bd2-ab9d-d9365fbf676f})|({82c8ced2-e08c-4d6c-a12b-3e8227d7fc2a})|({83d6f65c-7fc0-47d0-9864-a488bfcaa376})|({83d38ac3-121b-4f28-bf9c-1220bd3c643b})|({84b9121e-55c9-409a-9b28-c588b5096222})|({87ba49bd-daba-4071-aedf-4f32a7e63dbe})|({87c552f9-7dbb-421b-8deb-571d4a2d7a21})|({87dcb9bf-3a3e-4b93-9c85-ba750a55831a})|({89a4f24d-37d5-46e7-9d30-ba4778da1aaa})|({93c524c4-2e92-4dd7-8b37-31a69bc579e8})|({94df38fc-2dbe-4056-9b35-d9858d0264d3})|({95c7ae97-c87e-4827-a2b7-7b9934d7d642})|({95d58338-ba6a-40c8-93fd-05a34731dc0e})|({97c436a9-7232-4495-bf34-17e782d6232c})|({97fca2cd-545f-42ef-ae93-dc13b046bd3b})|({0111c475-01e6-42ea-a9b4-27bed9eb6092})|({115a8321-4414-4f4c-aee6-9f812121b446})|({158a5a56-aca0-418f-bec0-5b3bda6e9d4c})|({243a0246-cbab-4b46-93fb-249039f68d84})|({283d4f2a-bab1-43ce-90be-5129741ac988})|({408a506b-2336-4671-a490-83a1094b4097})|({0432b92a-bfcf-41b9-b5f0-df9629feece1})|({484e0ba4-a20b-4404-bb1b-b93473782ae0})|({486ecaf1-1080-48c1-8973-549bc731ccf9})|({495a84bd-5a0c-4c74-8a50-88a4ba9d74ba})|({520f2c78-7804-4f59-ae74-a192476055ed})|({543f7503-3620-4f41-8f9e-c258fdff07e9})|({0573bea9-7368-49cd-ba10-600be3535a0b})|({605a0c42-86af-40c4-bf39-f14060f316aa})|({618baeb9-e694-4c7b-9328-69f35b6a8839})|({640c40e5-a881-4d16-a4d0-6aa788399dd2})|({713d4902-ae7b-4a9a-bcf5-47f39a73aed0})|({767d394a-aa77-40c9-9365-c1916b4a2f84})|({832ffcf9-55e9-4fd1-b2eb-f19e1fac5089})|({866a0745-8b91-4199-820a-ec17de52b5f2})|({869b5825-e344-4375-839b-085d3c09ab9f})|({919fed43-3961-48d9-b0ef-893054f4f6f1})|({971d6ef0-a085-4a04-83d8-6e489907d926})|({1855d130-4893-4c79-b4aa-cbdf6fee86d3})|({02328ee7-a82b-4983-a5f7-d0fc353698f0})|({2897c767-03aa-4c2f-910a-6d0c0b9b9315})|({3908d078-e1db-40bf-9567-5845aa77b833})|({04150f98-2d7c-4ae2-8979-f5baa198a577})|({4253db7f-5136-42c3-b09d-cf38344d1e16})|({4414af84-1e1f-449b-ac85-b79f812eb69b})|({4739f233-57c1-4466-ad51-224558cf375d})|({5066a3b2-f848-4a59-a297-f268bc3a08b6})|({6072a2a8-f1bc-4c9c-b836-7ac53e3f51e4})|({7854ee87-079f-4a25-8e57-050d131404fe})|({07953f60-447e-4f53-a5ef-ed060487f616})|({8886a262-1c25-490b-b797-2e750dd9f36b})|({12473a49-06df-4770-9c47-a871e1f63aea})|({15508c91-aa0a-4b75-81a2-13055c96281d})|({18868c3a-a209-41a6-855d-f99f782d1606})|({24997a0a-9d9b-4c87-a076-766d44e1f6fd})|({27380afd-f42a-4c25-b57d-b9012e0d5d48})|({28044ca8-8e90-435e-bc63-a757af2fb6be})|({30972e0a-f613-4c46-8c87-2e59878e7180})|({31680d42-c80d-4f8a-86d3-cd4930620369})|({44685ba6-68b3-4895-879e-4efa29dfb578})|({046258c9-75c5-429d-8d5b-386cfbadc39d})|({47352fbf-80d9-4b70-9398-fb7bffa3da53})|({56316a2b-ef89-4366-b4aa-9121a2bb6dea})|({65072bef-041f-492e-8a51-acca2aaeac70})|({677e2d00-264c-4f62-a4e8-2d971349c440})|({72056a58-91a5-4de5-b831-a1fa51f0411a})|({85349ea6-2b5d-496a-9379-d4be82c2c13d})|({98363f8b-d070-47b6-acc6-65b80acac4f3})|({179710ba-0561-4551-8e8d-1809422cb09f})|({207435d0-201d-43f9-bb0f-381efe97501d})|({313e3aef-bdc9-4768-8f1f-b3beb175d781})|({387092cb-d2dc-4da5-9389-4a766c604ec2})|({0599211f-6314-4bf9-854b-84cb18da97f8})|({829827cd-03be-4fed-af96-dd5997806fb4})|({856862a5-8109-47eb-b815-a94059570888})|({1e6f5a54-2c4f-4597-aa9e-3e278c617d38})|({1490068c-d8b7-4bd2-9621-a648942b312c})|({18e5e07b-0cfa-4990-a67b-4512ecbae04b})|({3584581e-c01a-4f53-aec8-ca3293bb550d})|({5280684d-f769-43c9-8eaa-fb04f7de9199})|({5766852a-b384-4276-ad06-70c2283b4792})|({34364255-2a81-4d6e-9760-85fe616abe80})|({45621564-b408-4c29-8515-4cf1f26e4bc3})|({62237447-e365-487e-8fc3-64ddf37bdaed})|({7e7aa524-a8af-4880-8106-102a35cfbf42})|({71639610-9cc3-47e0-86ed-d5b99eaa41d5})|({78550476-29ff-4b7e-b437-195024e7e54e})|({85064550-57a8-4d06-bd4b-66f9c6925bf5})|({93070807-c5cd-4bde-a699-1319140a3a9c})|({11e7b9b3-a769-4d7f-b200-17cffa4f9291})|({22632e5e-95b9-4f05-b4b7-79033d50467f})|({03e10db6-b6a7-466a-a2b3-862e98960a85})|({23775e7d-dfcf-42b1-aaad-8017aa88fc59})|({85e31e7e-3e3a-42d3-9b7b-0a2ff1818b33})|({9e32ca65-4670-41e3-b6bb-8773e6b9bba8})|({6e43af8e-a78e-4beb-991f-7b015234eacc})|({57e61dc7-db04-4cf8-bbd3-62a15fc74138})|({01166e60-d740-440c-b640-6bf964504b3c})|({52e137bc-a330-4c25-a981-6c1ab9feb806})|({488e190b-d1f6-4de8-bffb-0c90cc805b62})|({5e257c96-bfed-457d-b57e-18f31f08d7bb})|({2134e327-8060-441c-ba68-b167b82ff5bc})|({1e68848a-2bb7-425c-81a2-524ab93763eb})|({8e888a6a-ec19-4f06-a77c-6800219c6daf})|({7e907a15-0a4c-4ff4-b64f-5eeb8f841349})|({a0ab16af-3384-4dbe-8722-476ce3947873})|({a0c54bd8-7817-4a40-b657-6dc7d59bd961})|({a0ce2605-b5fc-4265-aa65-863354e85058})|({a1f8e136-bce5-4fd3-9ed1-f260703a5582})|({a3fbc8be-dac2-4971-b76a-908464cfa0e0})|({a5a84c10-f12c-496e-80df-33386b7a1463})|({a5f90823-0a50-414f-ad34-de0f6f26f78e})|({a6b83c45-3f24-4913-a1f7-6f42411bbb54})|({a9eb2583-75e0-435a-bb6c-69d5d9b20e27})|({a32ebb9b-8649-493e-a9e9-f091f6ac1217})|({a83c1cbb-7a41-41e7-a2ae-58efcb4dc2e4})|({a506c5af-0f95-4107-86f8-3de05e2794c9})|({a02001ae-b7ed-45d7-baf2-c07f0a7b6f87})|({a5808da1-5b4f-42f2-b030-161fd11a36f7})|({a18087bb-4980-4349-898c-ca1b7a0e59cd})|({a345865c-44b9-4197-b418-934f191ce555})|({a7487703-02d8-4a82-a7d0-2859de96edb4})|({a2427e23-d349-4b25-b5b8-46960b218079})|({a015e172-2465-40fc-a6ce-d5a59992c56a})|({aaaffe20-3306-4c64-9fe5-66986ebb248e})|({abec23c3-478f-4a5b-8a38-68ccd500ec42})|({ac06c6b2-3fd6-45ee-9237-6235aa347215})|({ac037ad5-2b22-46c7-a2dc-052b799b22b5})|({ac296b47-7c03-486f-a1d6-c48b24419749})|({acbff78b-9765-4b55-84a8-1c6673560c08})|({acfe4807-8c3f-4ecc-85d1-aa804e971e91})|({ada56fe6-f6df-4517-9ed0-b301686a34cc})|({af44c8b4-4fd8-42c3-a18e-c5eb5bd822e2})|({b5a35d05-fa28-41b5-ae22-db1665f93f6b})|({b7b0948c-d050-4c4c-b588-b9d54f014c4d})|({b7f366fa-6c66-46bf-8df2-797c5e52859f})|({b9bb8009-3716-4d0c-bcb4-35f9874e931e})|({b12cfdc7-3c69-43cb-a3fb-38981b68a087})|({b019c485-2a48-4f5b-be13-a7af94bc1a3e})|({b91fcda4-88b0-4a10-9015-9365e5340563})|({b30591d6-ec24-4fae-9df6-2f3fe676c232})|({b99847d6-c932-4b52-9650-af83c9dae649})|({bbe79d30-e023-4e82-b35e-0bfdfe608672})|({bc3c2caf-2710-4246-bd22-b8dc5241693a})|({bc3c7922-e425-47e2-a2dd-0dbb71aa8423})|({bc763c41-09ca-459a-9b22-cf4474f51ebc})|({bd5ba448-b096-4bd0-9582-eb7a5c9c0948})|({be5d0c88-571b-4d01-a27a-cc2d2b75868c})|({be981b5e-1d9d-40dc-bd4f-47a7a027611c})|({be37931c-af60-4337-8708-63889f36445d})|({bea8866f-01f8-49e9-92cd-61e96c05d288})|({bf153de7-cdf2-4554-af46-29dabfb2aa2d})|({c3a2b953-025b-425d-9e6e-f1a26ee8d4c2})|({c3b71705-c3a6-4e32-bd5f-eb814d0e0f53})|({c5d359ff-ae01-4f67-a4f7-bf234b5afd6e})|({c6c8ea62-e0b1-4820-9b7f-827bc5b709f4})|({c8c8e8de-2989-4028-bbf2-d372e219ba71})|({c34f47d1-2302-4200-80d4-4f26e47b2980})|({c178b310-6ed5-4e04-9e71-76518dd5fb3e})|({c2341a34-a3a0-4234-90cf-74df1db0aa49})|({c8399f02-02f4-48e3-baea-586564311f95})|({c41807db-69a1-4c35-86c1-bc63044e4fcb})|({c383716f-b23f-47b2-b6bb-d7c1a7c218af})|({c3447081-f790-45cb-ae03-0d7f1764c88c})|({c445e470-9e5a-4521-8649-93c8848df377})|({c8e14311-4b2d-4eb0-9a6b-062c6912f50e})|({ca4fdfdb-e831-4e6e-aa8b-0f2e84f4ed07})|({ca6cb8b2-a223-496d-b0f6-35c31bc7ca2b})|({cba7ce11-952b-4dcb-ba85-a5b618c92420})|({cc6b2dc7-7d6f-470f-bccc-6a42907162d1})|({cc689da4-203f-4a0c-a7a6-a00a5abe74c5})|({ccb7b5d6-a567-40a2-9686-a097a8b583dd})|({cd28aa38-d2f1-45a3-96c3-6cfd4702ef51})|({cd89045b-2e06-46bb-9e34-48e8799e5ef2})|({cdda1813-51d6-4b1f-8a2f-8f9a74a28e14})|({ce0d1384-b99b-478e-850a-fa6dfbe5a2d4})|({ce93dcc7-f911-4098-8238-7f023dcdfd0d})|({cf9d96ff-5997-439a-b32b-98214c621eee})|({cfa458f9-b49b-4e09-8cb2-5e50bd8937cc})|({cfb50cdf-e371-4d6b-9ef2-fcfe6726db02})|({d1ab5ebd-9505-481d-a6cd-6b9db8d65977})|({d03b6b0f-4d44-4666-a6d6-f16ad9483593})|({d9d8cfc1-7112-40cc-a1e9-0c7b899aae98})|({d47ebc8a-c1ea-4a42-9ca3-f723fff034bd})|({d72d260f-c965-4641-bf49-af4135fc46cb})|({d78d27f4-9716-4f13-a8b6-842c455d6a46})|({d355bee9-07f0-47d3-8de6-59b8eecba57b})|({d461cc1b-8a36-4ff0-b330-1824c148f326})|({d97223b8-44e5-46c7-8ab5-e1d8986daf44})|({d42328e1-9749-46ba-b35c-cce85ddd4ace})|({da7d00bf-f3c8-4c66-8b54-351947c1ef68})|({db84feec-2e1f-48f0-9511-645fe4784feb})|({dc6256cc-b6d0-44ca-b42f-4091f11a9d29})|({dd1cb0ec-be2a-432b-9c90-d64c824ac371})|({dd95dd08-75d1-4f06-a75b-51979cbab247})|({ddae89bd-6793-45d8-8ec9-7f4fb7212378})|({de3b1909-d4da-45e9-8da5-7d36a30e2fc6})|({df09f268-3c92-49db-8c31-6a25a6643896})|({e2a4966f-919d-4afc-a94f-5bd6e0606711})|({e05ba06a-6d6a-4c51-b8fc-60b461ffecaf})|({e7b978ae-ffc2-4998-a99d-0f4e2f24da82})|({e7fb6f2f-52b6-4b02-b410-2937940f5049})|({e08d85c5-4c0f-4ce3-9194-760187ce93ba})|({e08ebf0b-431d-4ed1-88bb-02e5db8b9443})|({e9c47315-2a2b-4583-88f3-43d196fa11af})|({e341ed12-a703-47fe-b8dd-5948c38070e4})|({e804fa4c-08e0-4dae-a237-8680074eba07})|({e8982fbd-1bc2-4726-ad8d-10be90f660bd})|({e40673cd-9027-4f61-956c-2097c03ae2be})|({e72172d1-39c9-4f41-829d-a1b8d845d1ca})|({e73854da-9503-423b-ab27-fafea2fbf443})|({e81e7246-e697-4811-b336-72298d930857})|({ea618d26-780e-4f0f-91fd-2a6911064204})|({ea523075-66cd-4c03-ab04-5219b8dda753})|({eb3ebb14-6ced-4f60-9800-85c3de3680a4})|({ec8c5fee-0a49-44f5-bf55-f763c52889a6})|({eccd286a-5b1d-494d-82b0-92a12213d95a})|({ed352072-ddf0-4cb4-9cb6-d8aa3741c2de})|({edb476af-0505-42af-a7fd-ec9f454804c0})|({ee97f92d-1bfe-4e9d-816c-0dfcd63a6206})|({f0b809eb-be22-432f-b26f-b1cadd1755b9})|({f5ffa269-fbca-4598-bbd8-a8aa9479e0b3})|({f6c543bf-2222-4230-8ecb-f5446095b63d})|({f6df4ef7-14bd-43b5-90c9-7bd02943789c})|({f6f98e6b-f67d-4c53-8b76-0b5b6df79218})|({f38b61f3-3fed-4249-bb3d-e6c8625c7afb})|({f50e0a8f-8c32-4880-bcef-ca978ccd1d83})|({f59c2d3d-58da-4f74-b8c9-faf829f60180})|({f82b3ad5-e590-4286-891f-05adf5028d2f})|({f92c1155-97b3-40f4-9d5b-7efa897524bb})|({f95a3826-5c8e-4f82-b353-21b6c0ca3c58})|({f5758afc-9faf-42bb-9543-a4cfb0bfce9d})|({f447670d-64f5-418f-9b4a-5352d6c8e127})|({f4262989-6de0-4604-918f-663b85fad605})|({fa8bd609-0e06-4ba9-8e2e-5989f0b2e197})|({fa0808f6-25ab-4a8b-bd17-3b275c55ff09})|({fac5816b-fd0f-4db2-a16e-52394b6db41d})|({fc99b961-5878-46b4-b091-6d2f507bf44d})|({fce89242-66d3-4946-9ed0-e66078f172fc})|({fcf72e24-5831-439e-bb07-fd53a9e87a30})|({fdc0601f-1fbb-40a5-84e1-8bbe96b22502})|({feb3c734-4529-4d69-9f3a-2dae18f1d896}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="cee5c2ab-1059-4b15-a78c-1203116552c4" id="/^(({0f9e469e-4245-43f8-a7a8-7e730f80d284})|({117ca2f3-df4c-4e17-a5c5-b49077e9c731})|({11db147a-a1cb-43dd-9c05-0d11683483e1})|({1ed2af70-9e89-42db-a9e8-17ae594003ac})|({24ed6bdc-3085-413b-a62e-dc5dd30272f4})|({2aa19a7a-2a43-4e0d-a3dc-abb33fa7e2b6})|({3d6fbbb3-6c80-47bb-af20-56fcaebcb9ca})|({42f4c194-8929-42b9-a9a3-afa56dd0913b})|({46740fa0-896d-4f2e-a240-9478865c47c2})|({4718da68-a373-4a03-a77b-0f49b8bb40ee})|({4d41e0b8-bf7e-45ab-bd90-c426b420e3ee})|({50957a38-c15d-42da-94f5-325bc74a554c})|({5650fc63-a7c5-4627-8d0a-99b20dcbd94b})|({5c5c38ec-08bf-493a-9352-6ccf25d60c08})|({67ecb446-9ccd-4193-a27f-7bd1521bd03c})|({71f01ffe-226d-4634-9b21-968f5ce9f8f5})|({72f31855-2412-4998-a6ff-978f89bba0c3})|({7b3c1e86-2599-4e1a-ad98-767ae38286c8})|({7c37463c-001e-4f58-9e88-aaab2a624551})|({7de64f18-8e6b-4c41-9b05-d8872b418026})|({82dcf841-c7e1-4764-bb47-caa28909e447})|({872f20ea-196e-4d11-8835-1cc4c877b1b8})|({8efee317-546f-418d-82d3-60cc5187acf5})|({93deeba1-0126-43f7-a94d-4eecfce53b33})|({9cc12446-16da-4200-b284-d5fc18670825})|({9cd27996-6068-4597-8e97-bb63f783a224})|({9fdcedc7-ffde-44c3-94f6-4196b1e0d9fc})|({a191563e-ac30-4c5a-af3d-85bb9e9f9286})|({a4cb0430-c92e-44c6-9427-6a6629c4c5f6})|({a87f1b9b-8817-4bff-80fd-db96020c56c8})|({ae29a313-c6a9-48be-918d-1e4c67ba642f})|({b2cea58a-845d-4394-9b02-8a31cfbb4873})|({b420e2be-df31-4bea-83f4-103fe0aa558c})|({b77afcab-0971-4c50-9486-f6f54845a273})|({b868c6f4-5841-4c14-86ee-d60bbfd1cec1})|({b99ae7b1-aabb-4674-ba8f-14ed32d04e76})|({b9bb8009-3716-4d0c-bcb4-35f9874e931e})|({c53c4cbc-04a7-4771-9e97-c08c85871e1e})|({ce0d1384-b99b-478e-850a-fa6dfbe5a2d4})|({cf8e8789-e75d-4823-939f-c49a9ae7fba2})|({d0f67c53-42b5-4650-b343-d9664c04c838})|({dfa77d38-f67b-4c41-80d5-96470d804d09})|({e20c916e-12ea-445b-b6f6-a42ec801b9f8})|({e2a4966f-919d-4afc-a94f-5bd6e0606711})|({e7d03b09-24b3-4d99-8e1b-c510f5d13612})|({fa8141ba-fa56-414e-91c0-898135c74c9d})|({fc99b961-5878-46b4-b091-6d2f507bf44d})|(firedocs@mozilla\.com)|(firetasks@mozilla\.com)|(getta@mozilla\.com)|(javideo@mozilla\.com)|(javideo2@mozilla\.com)|(javideos@mozilla\.com)|(javideosz@mozilla\.com)|(search_free@mozilla\.com)|(search-unlisted@mozilla\.com)|(search-unlisted101125511@mozilla\.com)|(search-unlisted10155511@mozilla\.com)|(search-unlisted1025525511@mozilla\.com)|(search-unlisted1099120071@mozilla\.com)|(search-unlisted1099125511@mozilla\.com)|(search-unlisted109925511@mozilla\.com)|(search-unlisted11@mozilla\.com)|(search-unlisted111@mozilla\.com)|(search-unlisted12@mozilla\.com)|(search-unlisted14400770034@mozilla\.com)|(search-unlisted144007741154@mozilla\.com)|(search-unlisted144436110034@mozilla\.com)|(search-unlisted14454@mozilla\.com)|(search-unlisted1570124111@mozilla\.com)|(search-unlisted1570254441111@mozilla\.com)|(search-unlisted15721239034@mozilla\.com)|(search-unlisted157441@mozilla\.com)|(search-unlisted15757771@mozilla\.com)|(search-unlisted1577122001@mozilla\.com)|(search-unlisted15777441001@mozilla\.com)|(search-unlisted15788120036001@mozilla\.com)|(search-unlisted157881200361111@mozilla\.com)|(search-unlisted1578899961111@mozilla\.com)|(search-unlisted157999658@mozilla\.com)|(search-unlisted158436561@mozilla\.com)|(search-unlisted158440374111@mozilla\.com)|(search-unlisted15874111@mozilla\.com)|(search-unlisted1741395551@mozilla\.com)|(search-unlisted17441000051@mozilla\.com)|(search-unlisted174410000522777441@mozilla\.com)|(search-unlisted1768fdgfdg@mozilla\.com)|(search-unlisted180000411@mozilla\.com)|(search-unlisted18000411@mozilla\.com)|(search-unlisted1800411@mozilla\.com)|(search-unlisted18011888@mozilla\.com)|(search-unlisted1801668@mozilla\.com)|(search-unlisted18033411@mozilla\.com)|(search-unlisted180888@mozilla\.com)|(search-unlisted181438@mozilla\.com)|(search-unlisted18411@mozilla\.com)|(search-unlisted18922544@mozilla\.com)|(search-unlisted1955511@mozilla\.com)|(search-unlisted2@mozilla\.com)|(search-unlisted3@mozilla\.com)|(search-unlisted4@mozilla\.com)|(search-unlisted400@mozilla\.com)|(search-unlisted40110@mozilla\.com)|(search-unlisted5@mozilla\.com)|(search-unlisted55@mozilla\.com)|(search@mozilla\.com)|(searchazsd@mozilla\.com)|(smart246@mozilla\.com)|(smarter1@mozilla\.com)|(smarters1@mozilla\.com)|(stream@mozilla\.com)|(tahdith@mozilla\.com)|(therill@mozilla\.com)|(Updates@mozilla\.com))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="2734325e-143b-4962-98bf-4b18c77407e2" id="/^((Timemetric@tmetric)|(textMarkertool@underFlyingBirches\.org)|(youpanel@jetpack)|({6f13489d-b274-45b6-80fa-e9daa140e1a4})|({568db771-c718-4587-bcd0-e3728ee53550})|({829827cd-03be-4fed-af96-dd5997806fb4})|({9077390b-89a9-41ad-998f-ab973e37f26f})|({8e7269ac-a171-4d9f-9c0a-c504848fd52f})|({aaaffe20-3306-4c64-9fe5-66986ebb248e})|({bf153de7-cdf2-4554-af46-29dabfb2aa2d})|({c579191c-6bb8-4795-adca-d1bf180b512d})|({e2a4966f-919d-4afc-a94f-5bd6e0606711})|({ee97f92d-1bfe-4e9d-816c-0dfcd63a6206}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="d9892a76-b22e-40bd-8073-89b0f8110ec7" id="/^(({1a3fb414-0945-405c-a62a-9fe5e1a50c69})|({1a45f6aa-d80a-4317-84d2-0ce43671b08a})|({2d52a462-8bec-4708-9cd1-894b682bdc78})|({3f841cfc-de5a-421f-8bd7-2bf1d943b02a})|({5c7601bf-522b-47e5-b0f0-ea0e706af443})|({7ebe580f-71c9-4ef8-8073-f38deaeb9dfb})|({8b2188fd-1daf-4851-b387-28d964014353})|({8cee42ac-f1fe-40ae-aed6-24e3b76b2f77})|({8d13c4a9-5e8c-47a6-b583-681c83164ac9})|({9b1d775a-1877-45c9-ad48-d6fcfa4fff39})|({9efdbe5f-6e51-4a35-a41b-71dc939e6221})|({23f63efb-156e-440b-a96c-118bebc21057})|({026dfc8c-ecc8-41ba-b45f-70ffbd5cc672})|({34aa433c-27e9-4c87-a662-9f82f99eb9af})|({36f34d69-f22f-47c3-b4cd-4f37b7676107})|({39bd8607-0af4-4d6b-bd69-9a63c1825d3c})|({48c6ad6d-297c-4074-8fef-ca5f07683859})|({54aa688d-9504-481d-ba75-cfee421b98e0})|({59f59748-e6a8-4b41-87b5-9baadd75ddef})|({61d99407-1231-4edc-acc8-ab96cbbcf151})|({68ca8e3a-397a-4135-a3af-b6e4068a1eae})|({71beafd6-779b-4b7d-a78b-18a107277b59})|({83ed90f8-b07e-4c45-ba6b-ba2fe12cebb6})|({231dfb44-98e0-4bc4-b6ee-1dac4a836b08})|({273f0bce-33f4-45f6-ae03-df67df3864c2})|({392f4252-c731-4715-9f8d-d5815f766abb})|({484ec5d0-4cfd-4d96-88d0-a349bfc33780})|({569dbf47-cc10-41c4-8fd5-5f6cf4a833c7})|({578cad7a-57d5-404d-8dda-4d30de33b0c2})|({986b2c3f-e335-4b39-b3ad-46caf809d3aa})|({1091c11f-5983-410e-a715-0968754cff54})|({2330eb8a-e3fe-4b2e-9f17-9ddbfb96e6f5})|({5920b042-0af1-4658-97c1-602315d3b93d})|({6331a47f-8aae-490c-a9ad-eae786b4349f})|({6698b988-c3ef-4e1f-8740-08d52719eab5})|({30516f71-88d4-489b-a27f-d00a63ad459f})|({12089699-5570-4bf6-890f-07e7f674aa6e})|({84887738-92bf-4903-a5e8-695fd078c657})|({8562e48e-3723-412a-9ebd-b33d3d3b29dd})|({6e449795-c545-41be-92c0-5d467c147389})|({1e369c7c-6b61-436e-8978-4640687670d6})|({a03d427a-bd2e-42b6-828f-a57f38fac7b5})|({a77fc9b9-6ebb-418d-b0b6-86311c191158})|({a368025b-9828-43a1-8a5c-f6fab61c9be9})|({b1908b02-410d-4778-8856-7e259fbf471d})|({b9425ace-c2e9-4ec4-b564-4062546f4eca})|({b9845b5d-70c9-419c-a9a5-98ea8ee5cc01})|({ba99fee7-9806-4e32-8257-a33ffc3b8539})|({bdf8767d-ae4c-4d45-8f95-0ba29b910600})|({c6c4a718-cf91-4648-aa9b-170d66163cf2})|({ca0f2988-e1a8-4e83-afde-0dca56a17d5f})|({cac5db09-979b-40e3-8c8e-d96397b0eecb})|({d3b5280b-f8d8-4669-bdf6-91f23ae58042})|({d73d2f6a-ea24-4b1b-8c76-563fce9f786d})|({d77fed37-85c0-4b94-89bb-0d2849472b8d})|({d371abec-84bb-481b-acbf-235639451127})|({de47a3b4-dad1-4f4a-bdd6-8666586e29e8})|({ded6afad-2aaa-446b-b6bd-b12a8a61c945})|({e0c3a1ca-8e21-4d1b-b53b-ea115cf59172})|({e6bbf496-6489-4b48-8e5a-799aad4aa742})|({e63b262a-f9b8-4496-9c4b-9d3cbd6aea90})|({e73c1b5d-20f7-4d86-ad16-9de3c27718e2})|({eb01dc49-688f-4a21-aa8d-49bd88a8f319})|({edc9816b-60b4-493c-a090-01125e0b8018})|({effa2f97-0f07-44c8-99cb-32ac760a0621})|({f6e6fd9b-b89f-4e8d-9257-01405bc139a6})|({ff87977a-fefb-4a9d-b703-4b73dce8853d})|({ffea9e62-e516-4238-88a7-d6f9346f4955}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="df852b6a-28be-4b10-9285-869f4761f111" id="/^((@svuznnqyxinw)|(myprivacytools@besttools\.com)|(powertools@penprivacy\.com)|(privacypro@mybestprivacy\.com)|(realsecure@top10\.com)|(rlbvpdfrlbgx@scoutee\.net)|(vfjkurlfijwz@scoutee\.net))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="202fbae4-e904-430a-a244-63b0fb04385f" id="/^((de\.firefoxextension12345@asdf\.pl)|(deex1@de\.com)|(esex1@ese\.com)|(estrellach@protonmail\.com)|(fifi312@protonmail\.com)|(finex1@fin\.com)|(firefoxextension123@asdf\.pl)|(firefoxextension1234@asdf\.pl)|(firefoxextension12345@asdf\.pl)|(firefoxextension123456@asdf\.pl)|(frexff1@frexff1\.com)|(frexff2@frexff2\.com)|(frexff3@frexff3\.com)|(ind@niepodam\.pl)|(jacob4311@protonmail\.com)|(javonnu144@protonmail\.com)|(keellon33-ff@protonmail\.com)|(keellon33@protonmail\.com)|(masetoo4113@protonmail\.com)|(mikecosenti11@protonmail\.com)|(paigecho@protonmail\.com)|(salooo12@protonmail\.com)|(swex1@swe\.com)|(swex2@swe\.com)|(swex3@swe\.com)|(willburpoor@protonmail\.com)|(williamhibburn@protonmail\.com)|)$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="96b2e7d5-d4e4-425e-b275-086dc7ccd6ad" id="/^((firefox@browser-security\.de)|(firefox@smarttube\.io)|({0fde9597-0508-47ff-ad8a-793fa059c4e7})|(info@browser-privacy\.com)|({d3b98a68-fd64-4763-8b66-e15e47ef000a})|({36ea170d-2586-45fb-9f48-5f6b6fd59da7})|(youtubemp3converter@yttools\.io)|(simplysearch@dirtylittlehelpers\.com)|(extreme@smarttube\.io)|(selfdestructingcookies@dirtylittlehelpers\.com)|({27a1b6d8-c6c9-4ddd-bf20-3afa0ccf5040})|({2e9cae8b-ee3f-4762-a39e-b53d31dffd37})|(adblock@smarttube\.io)|({a659bdfa-dbbe-4e58-baf8-70a6975e47d0})|({f9455ec1-203a-4fe8-95b6-f6c54a9e56af})|({8c85526d-1be9-4b96-9462-aa48a811f4cf})|(mail@quick-buttons\.de)|(youtubeadblocker@yttools\.io)|(extension@browser-safety\.org)|(contact@web-security\.com)|(videodownloader@dirtylittlehelpers\.com)|(googlenotrack@dirtylittlehelpers\.com)|(develop@quick-amz\.com))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="01c22882-868b-43e1-bb23-29d5dc7bc11b" id="/(({a4d84dae-7906-4064-911b-3ad2b1ec178b})|({d7e388c5-1cd0-4aa6-8888-9172f90951fb})|({a67f4004-855f-4e6f-8ef0-2ac735614967})|({25230eb3-db35-4613-8c03-e9a3912b7004})|({37384122-9046-4ff9-a31f-963767d9fe33})|({f1479b0b-0762-4ba2-97fc-010ea9dd4e73})|({53804e15-69e5-4b24-8883-c8f68bd98cf6})|({0f2aec80-aade-46b8-838c-54eeb595aa96})|({b65d6378-6840-4da6-b30e-dee113f680aa})|({e8fc3f33-14b7-41aa-88a1-d0d7b5641a50})|({c49ee246-d3d2-4e88-bfdb-4a3b4de9f974}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="ee2d12a4-ea1d-4f3d-9df1-4303e8993f18" id="@testpilot-addon">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="2.0.8-dev-259fe19" severity="1"/>
+ </emItem>
+ <emItem blockID="ab029019-0e93-450a-8c11-ac31556c2a77" id="/^(({aeac6f90-5e17-46fe-8e81-9007264b907d})|({6ee25421-1bd5-4f0c-9924-79eb29a8889d})|({b317fa11-c23d-45b9-9fd8-9df41a094525})|({16ac3e8f-507a-4e04-966b-0247a196c0b4}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="46779b5a-2369-4007-bff0-857a657626ba" id="/^((fastplayer@fastsearch\.me)|(ff-search-flash-unlisted@mozilla\.com)|(inspiratiooo-unlisted@mozilla\.com)|(lite-search-ff-unlisted@mozilla\.com)|(mysearchprotect-unlisted@mozilla\.com)|(pdfconverter-unlisted@mozilla\.com)|(plugin-search-ff-unlisted@mozilla\.com)|(pro-search-ff-unlisted@mozilla\.com)|(pro-search-unlisted@mozilla\.com)|(searchincognito-unlisted@mozilla\.com)|(socopoco-search@mozilla\.com)|(socopoco-unlisted@mozilla\.com)|(\{08ea1e08-e237-42e7-ad60-811398c21d58\})|(\{0a56e2a0-a374-48b6-9afc-976680fab110\})|(\{193b040d-2a00-4406-b9ae-e0d345b53201\})|(\{1ffa2e79-7cd4-4fbf-8034-20bcb3463d20\})|(\{528cbbe2-3cde-4331-9344-e348cb310783\})|(\{6f7c2a42-515a-4797-b615-eaa9d78e8c80\})|(\{be2a3fba-7ea2-48b9-bbae-dffa7ae45ef8\})|(\{c0231a6b-c8c8-4453-abc9-c4a999a863bd\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="a0d44ee3-9492-47d7-ac1c-35f520e819ae" id="/^((Timemetric@tmetric)|(image-fastpicker@eight04.blogspot\.com)|(textMarkertool@underFlyingBirches\.org)|(youpanel@jetpack)|({0ff32ce0-dee9-4e7e-9260-65e58373e21d})|({4ca00873-7e8d-4ada-b460-96cad0eb8fa9})|({6b427f73-2ee1-4256-b69d-7dc253ebe030})|({6f13489d-b274-45b6-80fa-e9daa140e1a4})|({40a9d23b-09ef-4c82-ae1d-7fc5c067e987})|({205c2185-ebe4-4106-92ab-0ffa7c4efcbb})|({256ec7b0-57b4-416d-91c1-2bfdf01b2438})|({568db771-c718-4587-bcd0-e3728ee53550})|({5782a0f1-de26-42e5-a5b3-dae9ec05221b})|({9077390b-89a9-41ad-998f-ab973e37f26f})|({8e7269ac-a171-4d9f-9c0a-c504848fd52f})|({3e6586e2-7410-4f10-bba0-914abfc3a0b4})|({b3f06312-93c7-4a4f-a78b-f5defc185d8f})|({c1aee371-4401-4bab-937a-ceb15c2323c1})|({c579191c-6bb8-4795-adca-d1bf180b512d})|({d0aa0ad2-15ed-4415-8ef5-723f303c2a67})|({d8157e0c-bf39-42eb-a0c3-051ff9724a8c})|({e2a4966f-919d-4afc-a94f-5bd6e0606711})|({ee97f92d-1bfe-4e9d-816c-0dfcd63a6206}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="016676cc-c381-4c01-adcf-2d46f48142d0" id="/^((\{25211004-63e4-4a94-9c71-bdfeabb72bfe\})|(\{cbf23b92-ea55-4ca9-a5ae-f4197e286bc8\})|(\{7ac0550e-19cb-4d22-be12-b0b352144b33\})|(Mada111@mozilla\.com)|(\{c71709a9-af59-4958-a587-646c8c314c16\})|(\{6ac3f3b4-18db-4f69-a210-7babefd94b1e\})|(addon@fastsearch\.me)|(\{53d152fa-0ae0-47f1-97bf-c97ca3051562\})|(\{f9071611-24ee-472b-b106-f5e2f40bbe54\})|(\{972920f1-3bfd-4e99-b605-8688a94c3c85\})|(\{985afe98-fa74-4932-8026-4bdc880552ac\})|(\{d96a82f5-5d3e-46ed-945f-7c62c20b7644\})|(\{3a036dc5-c13b-499a-a62d-e18aab59d485\})|(\{49574957-56c6-4477-87f1-1ac7fa1b2299\})|(\{097006e8-9a95-4f7c-9c2f-59f20c61771c\})|(\{8619885d-0380-467a-b3fe-92a115299c32\})|(\{aa0587d6-4760-4abe-b3a1-2a5958f46775\})|(\{bdada7ae-cf89-46cf-b1fe-f3681f596278\})|(\{649bead3-df51-4023-8090-02ceb2f7095a\})|(\{097c3142-0b68-416a-9919-9dd576aedc17\})|(\{bc3cced8-51f0-4519-89ee-56706b67ea4b\})|(\{796da6e3-01c0-4c63-96dd-1737710b2ff6\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="fcd12629-43df-4751-9654-7cc008f8f7c0" id="/^((fireAnalytics\.download@mozilla\.com)|(fireabsorb@mozilla\.com)|(fireaccent@mozilla\.com)|(fireaccept@mozilla\.com)|(fireads@mozilla\.com)|(firealerts@mozilla\.com)|(fireapi@mozilla\.com)|(fireapp@mozilla\.com)|(fireattribution@mozilla\.com)|(fireauthenticator@mozilla\.com)|(firecalendar@mozilla\.com)|(firemail@mozilla\.com)|(firemarketplace@mozilla\.com)|(firequestions@mozilla\.com)|(firescript@mozilla\.com)|(firesheets@mozilla\.com)|(firespam@mozilla\.com)|(firesuite@mozilla\.com)|(\{3b6dfc8f-e8ed-4b4c-b616-bdc8c526ac1d\})|(\{834f87db-0ff7-4518-89a0-0167a963a869\})|(\{4921fe4d-fbe6-4806-8eed-346d7aff7c75\})|(\{07809949-bd7d-40a6-a17b-19807448f77d\})|(\{68968617-cc8b-4c25-9c38-34646cdbe43e\})|(\{b8b2c0e1-f85d-4acd-aeb1-b6308a473874\})|(\{bc0b3499-f772-468e-9de6-b4aaf65d2bbb\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="56bd2f99-57eb-4904-840a-23ca155d93ad" id="/^((\{35253b0b-8109-437f-b8fa-d7e690d3bde1\})|(\{0c8d774c-0447-11e7-a3b1-1b43e3911f03\})|(\{c11f85de-0bf8-11e7-9dcd-83433cae2e8e\})|(\{f9f072c8-5357-11e7-bb4c-c37ea2335fb4\})|(\{b6d09408-a35e-11e7-bc48-f3e9438e081e\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="914ec360-d35e-4420-a88f-1bad3513f054" id="/^((application2@fr-metoun\.com)|(application@br-annitop\.com)|(application@br-atoleg\.com)|(application@br-cholty\.com)|(application@br-debozoiz\.com)|(application@br-echite\.com)|(application@br-estracep\.com)|(application@br-exatrom\.com)|(application@br-iginot\.com)|(application@br-imastifi\.com)|(application@br-isobiv\.com)|(application@br-ludimaro\.com)|(application@br-pintoula\.com)|(application@br-proufta\.com)|(application@br-qhirta\.com)|(application@br-qibizar\.com)|(application@br-qopletr\.com)|(application@br-roblaprouf\.com)|(application@br-rosalop\.com)|(application@br-samalag\.com)|(application@br-sopreni\.com)|(application@br-stoumo\.com)|(application@br-villonat\.com)|(application@br-zoobre\.com)|(application@de-barbuna\.com)|(application@de-bicelou\.com)|(application@de-blabuma\.com)|(application@de-dalofir\.com)|(application@de-elplic\.com)|(application@de-erotah\.com)|(application@de-ertuck\.com)|(application@de-eurosty\.com)|(application@de-ezigat\.com)|(application@de-lorelam\.com)|(application@de-losimt\.com)|(application@de-luchil\.com)|(application@de-miligap\.com)|(application@de-open-dog\.com)|(application@de-rydima\.com)|(application@de-slapapi\.com)|(application@de-soqano\.com)|(application@de-treboola\.com)|(application@de-vasurk\.com)|(application@de-ygivas\.com)|(application@es-biloufer\.com)|(application@es-boulass\.com)|(application@es-cemaseur\.com)|(application@es-elixet\.com)|(application@es-gestona\.com)|(application@es-glicalol\.com)|(application@es-griloup\.com)|(application@es-iblep\.com)|(application@es-iglere\.com)|(application@es-jounyl\.com)|(application@es-klepst\.com)|(application@es-nofinaj\.com)|(application@es-ofarnut\.com)|(application@es-phistouquet\.com)|(application@es-pronzal\.com)|(application@es-roterf\.com)|(application@es-taapas\.com)|(application@es-tatoflex\.com)|(application@fr-acomyl\.com)|(application@fr-avortep\.com)|(application@fr-blicac\.com)|(application@fr-bloubil\.com)|(application@fr-carazouco\.com)|(application@fr-cichalou\.com)|(application@fr-consimis\.com)|(application@fr-cropam\.com)|(application@fr-deplitg\.com)|(application@fr-doadoto\.com)|(application@fr-domeoco\.com)|(application@fr-domlaji\.com)|(application@fr-eferif\.com)|(application@fr-eivlot\.com)|(application@fr-eristrass\.com)|(application@fr-ertike\.com)|(application@fr-esiliq\.com)|(application@fr-fedurol\.com)|(application@fr-grilsta\.com)|(application@fr-hyjouco\.com)|(application@fr-intramys\.com)|(application@fr-istrubil\.com)|(application@fr-javelas\.com)|(application@fr-jusftip\.com)|(application@fr-lolaji\.com)|(application@fr-macoulpa\.com)|(application@fr-mareps\.com)|(application@fr-metoun\.com)|(application@fr-metyga\.com)|(application@fr-mimaloy\.com)|(application@fr-monstegou\.com)|(application@fr-oplaff\.com)|(application@fr-ortisul\.com)|(application@fr-pastamicle\.com)|(application@fr-petrlimado\.com)|(application@fr-pinadolada\.com)|(application@fr-raepdi\.com)|(application@fr-soudamo\.com)|(application@fr-stoumo\.com)|(application@fr-stropemer\.com)|(application@fr-tlapel\.com)|(application@fr-tresdumil\.com)|(application@fr-troglit\.com)|(application@fr-troplip\.com)|(application@fr-tropset\.com)|(application@fr-vlouma)|(application@fr-yetras\.com)|(application@fr-zorbil\.com)|(application@fr-zoublet\.com)|(application@it-bipoel\.com)|(application@it-eneude\.com)|(application@it-glucmu\.com)|(application@it-greskof\.com)|(application@it-gripoal\.com)|(application@it-janomirg\.com)|(application@it-lapretofe\.com)|(application@it-oomatie\.com)|(application@it-platoks\.com)|(application@it-plopatic\.com)|(application@it-riploi\.com)|(application@it-sabuf\.com)|(application@it-selbamo\.com)|(application@it-sjilota\.com)|(application@it-stoploco\.com)|(application@it-teryom\.com)|(application@it-tyhfepa\.com)|(application@it-ujdilon\.com)|(application@it-zunelrish\.com)|(application@uk-ablapol\.com)|(application@uk-blamap\.com)|(application@uk-cepamoa\.com)|(application@uk-cloakyz\.com)|(application@uk-crisofil\.com)|(application@uk-donasip\.com)|(application@uk-fanibi\.com)|(application@uk-intramys\.com)|(application@uk-klastaf\.com)|(application@uk-liloust\.com)|(application@uk-logmati\.com)|(application@uk-manulap\.com)|(application@uk-misafou\.com)|(application@uk-nedmaf\.com)|(application@uk-optalme\.com)|(application@uk-plifacil\.com)|(application@uk-poulilax\.com)|(application@uk-rastafroc\.com)|(application@uk-ruflec\.com)|(application@uk-sabrelpt\.com)|(application@uk-sqadipt\.com)|(application@uk-tetsop\.com)|(application@uk-ustif\.com)|(application@uk-vomesq\.com)|(application@uk-vrinotd\.com)|(application@us-estuky\.com)|(application@us-lesgsyo\.com)|(applicationY@search-lesgsyo\.com)|(\{88069ce6-2762-4e02-a994-004b48bd83c1\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="43f11241-88e3-4139-9f02-ac39489a241f" id="/^((\{1cfaec8b-a1cb-4fc5-b139-897a22a71390\})|(\{2ed89659-09c1-4280-9dd7-1daf69272a86\})|(\{5c82f5cc-31f8-4316-bb7d-45a5c05227e6\})|(\{6a98a401-378c-4eac-b93c-da1036a00c6c\})|(\{6d83ebde-6396-483c-b078-57c9d445abfa\})|(\{07efb887-b09f-4028-8f7f-c0036d0485ea\})|(\{36f4882f-ff0b-4865-8674-ef02a937f7da\})|(\{61dea9e9-922d-4218-acdd-cfef0fdf85e7\})|(\{261be583-9695-48e0-bd93-a4feafaa18e6\})|(\{401ae092-6c5c-4771-9a87-a6827be80224\})|(\{534b7a84-9fc6-4d7c-9d67-e3365d2ae088\})|(\{552a949f-6d0e-402d-903d-1550075541ba\})|(\{579b8de8-c461-4301-ab09-695579f9b7c7\})|(\{754d3be3-7337-488e-a5bb-86487e495495\})|(\{2775f69b-75e4-46cb-a5aa-f819624bd9a6\})|(\{41290ec4-b3f0-45ad-b8f3-7bcbca01ed0d\})|(\{0159131f-d76f-4365-81cd-d6831549b90a\})|(\{01527332-1170-4f20-a65b-376e25438f3d\})|(\{760e6ff0-798d-4291-9d5f-12f48ef7658b\})|(\{7e31c21c-156a-4783-b1ce-df0274a89c75\})|(\{8e247308-a68a-4280-b0e2-a14c2f15180a\})|(\{b6d36fe8-eca1-4d85-859e-a4cc74debfed\})|(\{bab0e844-2979-407f-9264-c87ebe279e72\})|(\{d00f78fe-ee73-4589-b120-5723b9a64aa0\})|(\{d59a7294-6c08-4ad5-ba6d-a3bc41851de5\})|(\{d145aa5b-6e66-40cb-8a08-d55a53fc7058\})|(\{d79962e3-4511-4c44-8a40-aed6d32a53b1\})|(\{e3e2a47e-7295-426f-8517-e72c31da3f23\})|(\{e6348f01-841d-419f-8298-93d6adb0b022\})|(\{eb6f8a22-d96e-4727-9167-be68c7d0a7e9\})|(\{fdd72dfe-e10b-468b-8508-4de34f4e95e3\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="67f72634-e170-4860-a5a3-133f160ebc32" id="/^((\{f01a138a-c051-4bc7-a90a-21151ce05755\})|(\{50f78250-63ce-4191-b7c3-e0efc6309b64\})|(\{3d2b2ff4-126b-4874-a57e-ed7dac670230\})|(\{e7c1abd4-ec8e-4519-8f3a-7bd763b8a353\})|(\{4d40bf75-fbe2-45f6-a119-b191c2dd33b0\})|(\{08df7ff2-dee0-453c-b85e-f3369add18ef\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ <emItem blockID="81eb67a5-3fdb-448c-aadd-5f4d3b7cf281" id="/^((\{686fc9c5-c339-43db-b93a-5181a217f9a6\})|(\{eb4b28c8-7f2d-4327-a00c-40de4299ba44\})|(\{58d735b4-9d6c-4e37-b146-7b9f7e79e318\})|(\{ff608c10-2abc-415c-9fe8-0fdd8e988de8\})|(\{5a8145e2-6cbb-4509-a268-f3121429656c\})|(\{6d451f29-1d6b-4c34-a510-c1234488b0a3\})|(\{de71f09a-3342-48c5-95c1-4b0f17567554\})|(\{df106b04-984e-4e27-97b6-3f3150e98a9e\})|(\{70DE470A-4DC0-11E6-A074-0C08D310C1A8\})|(\{4dcde019-2a1b-499b-a5cd-322828e1279b\})|(\{1ec3563f-1567-49a6-bb5c-75d52334b01c\})|(\{c140c82e-98e6-49fd-ae17-0627e6f7c5e1\})|(\{2581c1f6-5ad9-48d4-8008-4c37dcea1984\})|(\{a2bcc6f7-14f7-4083-b4b0-c335edc68612\})|(\{4c726bb6-a2af-44ed-b498-794cfd8d8838\})|(\{fa6c39a6-cd11-477b-966d-f388f0ba4203\})|(\{26c7bd04-18d3-47f5-aeec-bb54e562acf2\})|(\{7a961c90-2071-4f94-9d9a-d4e3bbf247c0\})|(\{a0481ea2-03f0-4e56-a0e1-030908ecb43e\})|(\{c98fb54e-d25f-43f4-bd72-dfaa736391e2\})|(\{da57263d-adfc-4768-91f7-b3b076c20d63\})|(\{3abb352c-8735-4fb6-9fd6-8117aea3d705\})|(contactus@unzipper\.com)|(\{a1499769-6978-4647-ac0f-78da4652716d\})|(\{581D0A4C-1013-11E7-938B-FCD2A0406E17\})|(\{68feffe4-bfd8-4fc3-8320-8178a3b7aa67\})|(\{823489ae-1bf8-4403-acdd-ea1bdc6431da\})|(\{4c0d11c3-ee81-4f73-a63c-da23d8388abd\})|(\{dc7d2ecc-9cc3-40d7-93ed-ef6f3219bd6f\})|(\{21f29077-6271-46fc-8a79-abaeedb2002b\})|(\{55d15d4d-da76-44ab-95a3-639315be5ef8\})|(\{edfbec6b-8432-4856-930d-feb334fb69c1\})|(\{f81a3bf7-d626-48cf-bd24-64e111ddc580\})|(\{4407ab94-60ae-4526-b1ab-2521ffd285c7\})|(\{4aa2ba11-f87b-4950-8250-cd977252e556\})|(\{646b0c4d-4c6f-429d-9b09-37101b36ed1c\})|(\{1b2d76f1-4906-42d2-9643-0ce928505dab\})|(\{1869f89d-5f15-4c0d-b993-2fa8f09694fb\})|(\{7e4edd36-e3a6-4ddb-9e98-22b4e9eb4721\})|(\{e9c9ad8c-84ba-43f2-9ae2-c1448694a2a0\})|(\{6b2bb4f0-78ea-47c2-a03a-f4bf8f916eda\})|(\{539e1692-5841-4ac6-b0cd-40db15c34738\}))$/">
+ <prefs/>
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="p332">
+ <match exp="libflashplayer\.so" name="filename"/>
+ <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
+ <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.16a1"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p330">
+ <match exp="libflashplayer\.so" name="filename"/>
+ <match exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" name="description"/>
+ <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.16a1"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p1054">
+ <match exp="np32dsw_[0-9]+\.dll" name="filename"/>
+ <infoURL>https://get.adobe.com/shockwave/</infoURL>
+ <versionRange maxVersion="12.2.0.162" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+ </pluginItem>
+ <pluginItem blockID="p1055">
+ <match exp="DirectorShockwave\.plugin" name="filename"/>
+ <infoURL>https://get.adobe.com/shockwave/</infoURL>
+ <versionRange maxVersion="12.2.0.162" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+ </pluginItem>
+ <pluginItem blockID="832dc9ff-3314-4df2-abcf-7bd65a645371">
+ <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
+ <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+ <versionRange maxVersion="27.0.0.159" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+ </pluginItem>
+ <pluginItem blockID="49b843cc-a8fc-4ede-be0c-a0da56d0214f" os="Linux">
+ <match exp="libflashplayer\.so" name="filename"/>
+ <infoURL>https://get.adobe.com/flashplayer/</infoURL>
+ <versionRange maxVersion="27.0.0.159" minVersion="0" severity="0" vulnerabilitystatus="1"/>
+ </pluginItem>
+ <pluginItem blockID="p300">
+ <match exp="Java\(TM\) Platform SE 6 U(39|40|41)(\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p180">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <infoURL>https://java.com/</infoURL>
+ <versionRange maxVersion="Java 7 Update 10" minVersion="Java 7 Update 0" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p420">
+ <match exp="Java\(TM\) Platform SE 7 U(1[6-9]|2[0-4])(\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p458">
+ <match exp="Java\(TM\) Platform SE 7 U(2[5-9]|3\d|4[0-4])(\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p298">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <versionRange maxVersion="Java 6 Update 41" minVersion="Java 6 Update 39" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p296">
+ <match exp="Java\(TM\) Plug-in 1\.7\.0_1[2-5]([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p302">
+ <match exp="Java\(TM\) Plug-in 1\.6\.0_(39|40|41)([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p422">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <versionRange maxVersion="Java 7 Update 24" minVersion="Java 7 Update 16" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p182">
+ <match exp="Java\(TM\) Platform SE 7 U([0-9]|(1[0-1]))(\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p418">
+ <match exp="Java\(TM\) Plug-in 1\.7\.0_(1[6-9]|2[0-4])([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p190">
+ <match exp="Java\(TM\) Plug-in 1\.6\.0_3[1-8]([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p292">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <versionRange maxVersion="Java 7 Update 15" minVersion="Java 7 Update 12" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p188">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <versionRange maxVersion="Java 6 Update 38" minVersion="Java 6 Update 0" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p459">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <versionRange maxVersion="Java 7 Update 44" minVersion="Java 7 Update 25" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p186">
+ <match exp="Java\(TM\) Platform SE 6 U3[1-8](\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p294">
+ <match exp="Java\(TM\) Platform SE 7 U1[2-5](\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p457">
+ <match exp="Java(\(TM\))? Plug-in ((1\.7\.0_(2[5-9]|3\d|4[0-4]))|(10\.4[0-4](\.[0-9]+)?))([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p29">
+ <match exp="^Yahoo Application State Plugin$" name="name"/>
+ <match exp="npYState.dll" name="filename"/>
+ <match exp="^Yahoo Application State Plugin$" name="description"/>
+ <versionRange>
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="1.0.0.5"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p412">
+ <match exp="Java\(TM\) Plug-in 1\.6\.0_4[2-5]([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p184">
+ <match exp="Java\(TM\) Plug-in 1\.7\.0(_0?([0-9]|(1[0-1]))?)?([^\d\._]|$)" name="name"/>
+ <match exp="libnpjp2\.so" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p414">
+ <match exp="Java\(TM\) Platform SE 6 U4[2-5](\s[^\d\._U]|$)" name="name"/>
+ <match exp="npjp2\.dll" name="filename"/>
+ <versionRange severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p328">
+ <match exp="Silverlight\.plugin" name="filename"/>
+ <versionRange maxVersion="5.1.20124.9999" minVersion="5.1" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.16a1"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="p416">
+ <match exp="JavaAppletPlugin\.plugin" name="filename"/>
+ <versionRange maxVersion="Java 6 Update 45" minVersion="Java 6 Update 42" severity="0" vulnerabilitystatus="1">
+ <targetApplication id="{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}">
+ <versionRange maxVersion="*" minVersion="2.14"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ </pluginItems>
+ <gfxItems>
+ <gfxBlacklistEntry blockID="g194">
+ <os>WINNT 6.2</os>
+ <vendor>0x1022</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>9.10.8.0</driverVersion>
+ <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1072">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.1892</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g278">
+ <os>WINNT 6.1</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ <devices>
+ <device>0x9802</device>
+ <device>0x9803</device>
+ <device>0x9803</device>
+ <device>0x9804</device>
+ <device>0x9805</device>
+ <device>0x9806</device>
+ <device>0x9807</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g204">
+ <os>Darwin 10</os>
+ <vendor>0x8086</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1220">
+ <os>WINNT 6.3</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g232">
+ <os>Darwin 11</os>
+ <vendor>0x1002</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g992">
+ <os>WINNT 8.1</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>15.201.1151.0</driverVersion>
+ <driverVersionComparator>LESS_THAN</driverVersionComparator>
+ <devices>
+ <device>0x6920</device>
+ <device>0x6921</device>
+ <device>0x6928</device>
+ <device>0x6929</device>
+ <device>0x692b</device>
+ <device>0x692f</device>
+ <device>0x6930</device>
+ <device>0x6938</device>
+ <device>0x6939</device>
+ <device>0x6900</device>
+ <device>0x6901</device>
+ <device>0x6902</device>
+ <device>0x6903</device>
+ <device>0x6907</device>
+ <device>0x7300</device>
+ <device>0x9870</device>
+ <device>0x9874</device>
+ <device>0x9875</device>
+ <device>0x9876</device>
+ <device>0x9877</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g146">
+ <os>All</os>
+ <vendor>0x1022</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.982.0.0</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g37">
+ <os>WINNT 5.1</os>
+ <vendor>0x10de</vendor>
+ <feature>DIRECT3D_9_LAYERS</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>7.0.0.0</driverVersion>
+ <driverVersionComparator>GREATER_THAN_OR_EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1219">
+ <os>WINNT 6.2</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1068">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.1851</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g206">
+ <os>Darwin 11</os>
+ <vendor>0x8086</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1251">
+ <os>WINNT 5.1</os>
+ <vendor>0x8086</vendor>
+ <feature>WEBGL_ANGLE</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>6.14.10.5218</driverVersion>
+ <driverVersionComparator>LESS_THAN</driverVersionComparator>
+ <versionRange maxVersion="49.9"/>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g144">
+ <os>All</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.982.0.0</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g150">
+ <os>All</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT3D_9_LAYERS</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.982.0.0</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1071">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.1883</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g35">
+ <os>WINNT 6.1</os>
+ <vendor>0x10de</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.17.12.5896</driverVersion>
+ <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x0a6c</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g192">
+ <os>WINNT 6.2</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>9.10.8.0</driverVersion>
+ <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g148">
+ <os>All</os>
+ <vendor>0x1022</vendor>
+ <feature>DIRECT3D_9_LAYERS</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.982.0.0</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1070">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.1872</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g984">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.2413</driverVersion>
+ <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g280">
+ <os>WINNT 6.1</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT3D_9_LAYERS</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ <devices>
+ <device>0x9802</device>
+ <device>0x9803</device>
+ <device>0x9803</device>
+ <device>0x9804</device>
+ <device>0x9805</device>
+ <device>0x9806</device>
+ <device>0x9807</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g974">
+ <os>WINNT 10.0</os>
+ <vendor>0x1002</vendor>
+ <feature>DIRECT2D</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>15.201.1151.0</driverVersion>
+ <driverVersionComparator>LESS_THAN</driverVersionComparator>
+ <devices>
+ <device>0x6920</device>
+ <device>0x6921</device>
+ <device>0x6928</device>
+ <device>0x6929</device>
+ <device>0x692b</device>
+ <device>0x692f</device>
+ <device>0x6930</device>
+ <device>0x6938</device>
+ <device>0x6939</device>
+ <device>0x6900</device>
+ <device>0x6901</device>
+ <device>0x6902</device>
+ <device>0x6903</device>
+ <device>0x6907</device>
+ <device>0x7300</device>
+ <device>0x9870</device>
+ <device>0x9874</device>
+ <device>0x9875</device>
+ <device>0x9876</device>
+ <device>0x9877</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1069">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.1855</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g36">
+ <os>WINNT 6.1</os>
+ <vendor>0x10de</vendor>
+ <feature>DIRECT3D_9_LAYERS</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.17.12.5896</driverVersion>
+ <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x0a6c</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g511">
+ <os>WINNT 5.1</os>
+ <vendor>0x8086</vendor>
+ <feature>DIRECT3D_9_LAYERS</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>6.14.10.5218</driverVersion>
+ <driverVersionComparator>LESS_THAN</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g198">
+ <os>Darwin 10</os>
+ <vendor>0x10de</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1218">
+ <os>WINNT 6.1</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g230">
+ <os>Darwin 10</os>
+ <vendor>0x1002</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g234">
+ <os>Darwin 12</os>
+ <vendor>0x1002</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1215">
+ <os>WINNT 5.1</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1216">
+ <os>WINNT 5.2</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1221">
+ <os>WINNT 10.0</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1073">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.1994</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g208">
+ <os>Darwin 12</os>
+ <vendor>0x8086</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g202">
+ <os>Darwin 12</os>
+ <vendor>0x10de</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1124">
+ <os>All</os>
+ <vendor>0x8086</vendor>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>8.15.10.2086</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ <devices>
+ <device>0x2a42</device>
+ <device>0x2e22</device>
+ <device>0x2e12</device>
+ <device>0x2e32</device>
+ <device>0x0046</device>
+ </devices>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g1217">
+ <os>WINNT 6.0</os>
+ <vendor>0x8086</vendor>
+ <feature>HARDWARE_VIDEO_DECODING</feature>
+ <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus>
+ <driverVersion>10.18.10.3947</driverVersion>
+ <driverVersionComparator>EQUAL</driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g200">
+ <os>Darwin 11</os>
+ <vendor>0x10de</vendor>
+ <feature>WEBGL_MSAA</feature>
+ <featureStatus>BLOCKED_DEVICE</featureStatus>
+ </gfxBlacklistEntry>
+ </gfxItems>
+ <certItems>
+ <certItem issuerName="MGcxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpGcmF1bmhvZmVyMSEwHwYDVQQLExhGcmF1bmhvZmVyIENvcnBvcmF0ZSBQS0kxIDAeBgNVBAMTF0ZyYXVuaG9mZXIgUm9vdCBDQSAyMDA3">
+ <serialNumber>YR3YYQAAAAAABA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==">
+ <serialNumber>TXxtAQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESCEUbthDurBjJw0/h/FfuNY</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEbMBkGA1UEAxMSR2VvVHJ1c3QgRFYgU1NMIENB">
+ <serialNumber>CWhp</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMw==">
+ <serialNumber>cpqpXVWPk5AXzGw+zNIcBw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydKkg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABJQcQQN0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEfMB0GA1UEAxMWVVROLVVTRVJGaXJzdC1IYXJkd2FyZQ==">
+ <serialNumber>EEpERSryZFMagbsNw/WoWQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHsxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEsMCoGA1UEAxMjU3ltYW50ZWMgQ2xhc3MgMyBFQ0MgMjU2IGJpdCBTU0wgQ0E=">
+ <serialNumber>U3SgRR3J+D6575WuCxuXeQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>U4P1tUoxl/XkztlVHdtdgw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABAPpuVh0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=">
+ <serialNumber>U3t2Vk8pfxTcaUPpIq0seQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>CSU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>UW3oKZKTDsrPy/rfwmGNaQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABLF5/Gog=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABKUXDqA8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG1MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMS8wLQYDVQQDEyZWZXJpU2lnbiBDbGFzcyAzIFNlY3VyZSBTZXJ2ZXIgQ0EgLSBHMw==">
+ <serialNumber>QZBvapTZFvmYktEPsBYLQQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>eR1nUEz8k+nDSBD+bb5uIQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls">
+ <serialNumber>D/wZ7+m1Mv8SONSEFcs73w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>P6G7IYSL2RZxtzTh8I6qPA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>BYOGvG32ukb1Yxj2oKoFyw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESByYNtAIfizf2L3NMzCH8zZ</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>AQAAAAQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESAyW/JX3+hZIp44EAMlXU2b</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>AQAAAAM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>Cfk9qg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABHkSHjz8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABLM/7qjk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>L7tgs/W85vnhV7I7qJ6N/g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==">
+ <serialNumber>BAAAAAABL07hTcY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>DYifRdP6aQQ8MLbXZY2f5g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABHJRKNmk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>CSY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==">
+ <serialNumber>BAAAAAABCFiEp9s=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>AQAAAAU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9LtnA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxUaGF3dGUsIEluYy4xGzAZBgNVBAMTElRoYXd0ZSBTR0MgQ0EgLSBHMg==">
+ <serialNumber>e0bEFhI16xx9U1yvlI56rA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ==">
+ <serialNumber>ATFEdg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>YRJNfMoc12IpmW+Enpv3Pdo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>E77H6yvyFQjO0PcN3x0H+Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CqL7CA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>AygWP2Fgd2T+iLbmAlKT6g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>QZCrvA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEiMCAGA1UEAxMZR2VvVHJ1c3QgRFYgU1NMIFNIQTI1NiBDQQ==">
+ <serialNumber>OE4/d+p3YRzzcSl+kmZ8Mw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGQMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxOzA5BgNVBAMTMkFyaXN0b3RsZSBVbml2ZXJzaXR5IG9mIFRoZXNzYWxvbmlraSBDZW50cmFsIENBIFI0">
+ <serialNumber>EqthLKdUgwI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>PAdKZPiaac2CvPxbOrsHOw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGQMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE2MDQGA1UEAxMtQ09NT0RPIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB">
+ <serialNumber>UoRGnb96CUDTxIqVry6LBg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>DjIvBkX+ECVbB/C3i6w2Gg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CcHC1w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>Cbssdw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>A9GPKQ8jv9oIxfwiOy7qxQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABHkSHki0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>YNOos6YJoPC77qwSGCpb7w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>LTRcDHabRHU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESJJweWBPhoXAaB9c8SHwI4O</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byd5cg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABMYnGRuw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJVUzEgMB4GA1UECgwXV2VsbHMgRmFyZ28gV2VsbHNTZWN1cmUxHDAaBgNVBAsME1dlbGxzIEZhcmdvIEJhbmsgTkExNjA0BgNVBAMMLVdlbGxzU2VjdXJlIFB1YmxpYyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eQ==">
+ <serialNumber>Aw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==">
+ <serialNumber>BA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESDu2nhlLPzfx+LYgjlYFP/k</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABKB/OGqI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABHkSHlSo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABEAuMoRs=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>F5Bg+EziQQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>Cyr1PA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG1MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMS8wLQYDVQQDEyZWZXJpU2lnbiBDbGFzcyAzIFNlY3VyZSBTZXJ2ZXIgQ0EgLSBHMw==">
+ <serialNumber>OqQ2rV0ISTc308Z/oQgzFw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA3IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNA==">
+ <serialNumber>cXXMzbWDHMIdCotb3h64yw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==">
+ <serialNumber>BAAAAAABCfhiO+s=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==">
+ <serialNumber>Ermwxw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABMrS7t2g=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEkMCIGA1UEAxMbU3ltYW50ZWMgQ2xhc3MgMyBEU0EgU1NMIENB">
+ <serialNumber>AuhvPsYZfVP6UDsuyjeZ4Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byc68g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB">
+ <serialNumber>BAAAAAABI54PryQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>EDQMI0tR4kSntv1O37N10g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>UUFV3S2cUidOOv7ESN65Ng==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Ai7cBJYqBE0I9NdyoZfRrw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>HNo1DR4XCe4mS1iUMsY6Wg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>IyIVazG4RE9AERkb+ekH8w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>Cfk9oA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNQ==">
+ <serialNumber>buROL/l2GuXISv+/JVLkdA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>sPNcCSE9Nkg3jy5IN1xe2Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>JV/LVzSKI/wsDgg3UuZHlA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMg==">
+ <serialNumber>WX89jn8yGZVvoKTD9jDfRQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHjAcBgNVBAMTFXRoYXd0ZSBFViBTU0wgQ0EgLSBHMw==">
+ <serialNumber>CrTHPEE6AZSfI3jysin2bA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESISuBo/wdW2tBztKmHdFCFz</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>F5BhENPfVw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDcxJDAiBgNVBAMTG1JDUyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEPMA0GA1UEChMGSFQgc3Js">
+ <serialNumber>AN9bfYOvlR1t</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEwMC4GA1UEAxMnU3ltYW50ZWMgQ2xhc3MgMyBFQ0MgMjU2IGJpdCBFViBDQSAtIEcy">
+ <serialNumber>OhrtngFwotLcm4i+z00SjA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMg==">
+ <serialNumber>VfTSum25nb65YPlpuhJAvg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xGzAZBgNVBAMTEnRoYXd0ZSBTU0wgQ0EgLSBHMg==">
+ <serialNumber>FNISyWWTGi5Yco6fGh58/A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABM6d3Z0s=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABJ/ufQg8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>Mq0P6o03FDk0B2bnJ+mYPGo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMw==">
+ <serialNumber>bx/XHJqcwxDOptxJ2lh5vw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESDItX4ruWiLnrlz0rk4/bmz</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>FJl6tXgNpSg=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9LtnM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==">
+ <serialNumber>BAAAAAABK84ykc0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESC8DawWRiAyEMd38UXbfgPR</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfHkw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABJpQ0AbA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>Rvm2CEw2IC2Mu/ax0A46QQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABGMGjftY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABJ/v3ZwA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>Gd/pPu+qLnXUdvP9sW73CQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>bzTw0uq05TUYEGS98bh0Ww==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>UV9aaDeNRNtQuXjRYk4Skhg=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ==">
+ <serialNumber>ATFpsA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>VOcIuNbTqkpOMUyI108FOg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDI=">
+ <serialNumber>BXA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABLF5/HXY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>ATE0vw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CcHC/g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>DHmmaw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>Sx51x7V8pYe8rp7PMP/3qg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>F7PAjw2k0dTX5escPnyVOBo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==">
+ <serialNumber>BQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>45KI4WIxyXfNrdtdj7C6</serialNumber>
+ </certItem>
+ <certItem issuerName="MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xGzAZBgNVBAMTEnRoYXd0ZSBTU0wgQ0EgLSBHMg==">
+ <serialNumber>JpUvYJyWjdGmeoH7YcYunw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>fWK0j/Vi8vNWg3VAGjc02w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABIBnBjWg=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=">
+ <serialNumber>M64Z5ufZzDRVTHkJR1uXzw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>Cd/dug==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>NTgf4iwIfeyJPIomw2dwSXEwtxQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>CjM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=">
+ <serialNumber>a12RvBNhznU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CskruA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>HZyLf+K70FKc+jomm8DiDw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>F5Bg6C237Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESCis569omrbb20yySF39+aE</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>a9/VeyVWrzFD7rM2PEHwQA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxUaGF3dGUsIEluYy4xGzAZBgNVBAMTElRoYXd0ZSBTR0MgQ0EgLSBHMg==">
+ <serialNumber>cDggUYfwJ3A1YcdoeT6s4A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>VLm3Xe60+1YgPpXCGtXLng==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>ezdAeCxKH7BFs7vn3byYaw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>By7fBTreouRwX/qrpgSUsg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>O2S99lVUxErLSk56GvWRv+E=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABGMG0Gmw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESCVop+Q4/OBgtf4WJkr01Gh</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydeGg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>ANygrItIJ2rcKlyS3Lue07U=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>LdbnCbsA9sOgI4mkUpWXPw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>TqfXw+FkhxfVgE9GVMgjWQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==">
+ <serialNumber>BAAAAAABHhw1vwc=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABBHYoIFs=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABHJRKMpA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABJ/ufRdg=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDI=">
+ <serialNumber>EM8bDLBnnoYe4LnWpLIhS4esr3I=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>OYBKgxEHpW/8XGAGAlvJyMA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGQMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE2MDQGA1UEAxMtQ09NT0RPIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB">
+ <serialNumber>D9UltDPl4XVfSSqQOvdiwQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESCC9oPNcRdPOox+SjWm9dTX</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG1MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMS8wLQYDVQQDEyZWZXJpU2lnbiBDbGFzcyAzIFNlY3VyZSBTZXJ2ZXIgQ0EgLSBHMw==">
+ <serialNumber>NvEJoRYL2yvAZrAjbDIipQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==">
+ <serialNumber>Eg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESD9YhzIEOwiOT7Nwip+E1KI</serialNumber>
+ </certItem>
+ <certItem issuerName="MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDIgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAyIENB">
+ <serialNumber>BAAAAAABHkSl6Co=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=">
+ <serialNumber>Jq6jgeApiT9O4W2Tx/NTRQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>E5I2y6sIonl4a+TmlXc7fw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>XLhHIg7vP+tWfRqvuKeAxw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dD">
+ <serialNumber>Ew1ee9Jq7Q/Dig3ACF4V6Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>UMUwXwT1Z4juyQ/CNTf4mw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAMTFHRoYXd0ZSBTSEEyNTYgU1NMIENB">
+ <serialNumber>UKKK5ol/rKBZchAAOnZjaA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>TurPPI6eivtNeGYdM0ZWXQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="ME0xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIgU2VjdXJlIFNlcnZlciBDQQ==">
+ <serialNumber>Aa8e+91erglSMgsk/mtVaA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==">
+ <serialNumber>STMAeg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>VN2yeFexyXjPf34fHGmbhg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=">
+ <serialNumber>M0VSOewW3WI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>FJl6tXgNpSk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==">
+ <serialNumber>BAAAAAABK84yjs8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABAJmPjfQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Aw1SPC56593ZCZ9vCNHKwQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>AQAAAAI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>QZCrvQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bydp0g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABMxvC9bk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMg==">
+ <serialNumber>SdegFrLaFTCsoMAW5ED+zA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDMgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAzIENB">
+ <serialNumber>BAAAAAABHkSl6mw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>Cfk9lw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABJQdAjik=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>TrKEMhb2PKktH8lHg0AV5A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CdWFNw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESCyHU+xOECnh9Rf2IvgR8zS</serialNumber>
+ </certItem>
+ <certItem issuerName="MFYxCzAJBgNVBAYTAkpQMQ8wDQYDVQQKEwZKSVBERUMxGjAYBgNVBAsTEUpDQU4gU3ViIFJvb3QgQ0EwMRowGAYDVQQDExFKQ0FOIFN1YiBSb290IENBMA==">
+ <serialNumber>BAAAAAABL07hUBg=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>F5BhE0zbgQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>bAOrKSMsmA0MLJyAJ5BRsUM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==">
+ <serialNumber>STMAjg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CeFU2w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlJZGVuVHJ1c3QxIDAeBgNVBAsTF0lkZW5UcnVzdCBQdWJsaWMgU2VjdG9yMRwwGgYDVQQDExNJZGVuVHJ1c3QgQUNFUyBDQSAx">
+ <serialNumber>fwAAAQAAAUrz/HmrAAAAAg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABFqoAZoI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB">
+ <serialNumber>BAAAAAABHkSl7L4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>EAdmaA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CeagHQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Xbevr3ut3Z9m1GuXC9SonA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bydr0Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>KjoVfZ3by6+pL8fssyfM6A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>GtXUVojhwOTkaQ4bTKblEQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESBqoILo90ntDW7OTK43MS2F</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>UU3AP1SMxmyhBFq7MRFZmf0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==">
+ <serialNumber>EA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>e/fIfg2Dj2tkYIWVu2r82Cc=</serialNumber>
+ </certItem>
+ <certItem issuerName="MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=">
+ <serialNumber>Os2rnHWYhryvdOXfgan06A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>d8AtKymQwkOPDBj+hjPzFg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>fMTRbGCp280pnyE/u53zbA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9Ltm4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xvYmFsU2lnbiBQYXJ0bmVycyBDQQ==">
+ <serialNumber>BAAAAAABF2Tb8Bc=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABCUVQ9No=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESCLRVuhcUZaluIgIVlRJx+O</serialNumber>
+ </certItem>
+ <certItem issuerName="MFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBSb290IENB">
+ <serialNumber>AJiWmg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=">
+ <serialNumber>RurwlgVMxeP6Zepun0LGZA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>BYyEX2b5+K+myAIR7eXaRQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAAA+X/GIyk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>R4af5A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==">
+ <serialNumber>EQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeQ9g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABHkSl5AQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>IIxFSyNM6mWtCgTG0IL3Og==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTQ==">
+ <serialNumber>Bg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABJQcQRNU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9LtnI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGKMQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEmMCQGA1UECxMdQ29weXJpZ2h0IChjKSAyMDA1IFdJU2VLZXkgU0ExFjAUBgNVBAsTDUludGVybmF0aW9uYWwxKTAnBgNVBAMTIFdJU2VLZXkgQ2VydGlmeUlEIEFkdmFuY2VkIEcxIENB">
+ <serialNumber>WD1AyQAAAAAAJQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>COwoDFvz7GD8R2K7Lo0rYQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>GdXz4L1b6FKNCMG9Jz2tjA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp">
+ <serialNumber>TA6EVg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CcL+EA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>dItWlz2V62Philqj9m6Pbg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>CgFBQgAAAUFcf/EVAAAAAg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABIg08D3U=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABJZbEU4I=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMQ==">
+ <serialNumber>BGU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>LnfcUaXG/pxV2CpXM5+YSg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xLTArBgNVBAMMJFN0YWF0IGRlciBOZWRlcmxhbmRlbiBCdXJnZXIgQ0EgLSBHMg==">
+ <serialNumber>ATE3ew==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABFUtaxac=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RDQTEx">
+ <serialNumber>Aw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESByNJZ5TPjg9iZyL6a/h5Zx</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>BUrYjru5px1ym4QUN33TOQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>L79XLVO2ZmtAu7FAG8Wmzw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Qh/QbQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEiMCAGA1UEAxMZR2VvVHJ1c3QgRFYgU1NMIFNIQTI1NiBDQQ==">
+ <serialNumber>ZgwfEqZnBsUNvNuZ77FbQA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>OnvXX72mvUI2Id/NMzegmg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMQ==">
+ <serialNumber>BHk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>DAk9hy8DhHSo+aQetvPB/fY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>ARQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9Ltms=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzM=">
+ <serialNumber>ORFgmCj072NjcJnrxOMfQA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR8wHQYDVQQDExZHZW9UcnVzdCBTSEEyNTYgU1NMIENB">
+ <serialNumber>OUvvVscW0/NltofkmV9qmg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMR4wHAYDVQQDExV0aGF3dGUgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>CqZgEvHAsnzkT//QV9KjXw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABKUXDqxw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEQxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQDExRHZW9UcnVzdCBTU0wgQ0EgLSBHMw==">
+ <serialNumber>RUT1Gehd1KKYPfqOlgspoQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDEgQ0ExJjAkBgNVBAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAxIENB">
+ <serialNumber>BAAAAAABHkSl5ao=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>J2La+q+JOURNWkX60OP2lQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>AImQERVYPoeb</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>F5Bg/C8eXg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgQ2xhc3MgMyBFViBTU0wgQ0EgLSBHMg==">
+ <serialNumber>UVKsEezpGWOVQ4W9esstng==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==">
+ <serialNumber>BAAAAAABA/A35EU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHcxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEoMCYGA1UEAxMfU3ltYW50ZWMgQ2xhc3MgMyBFViBTU0wgQ0EgLSBHMw==">
+ <serialNumber>acI1CFIgmwSFBoU5+ahDgg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGpMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYDVQQLEy8oYykgMjAwNiB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UEAxMWdGhhd3RlIFByaW1hcnkgUm9vdCBDQQ==">
+ <serialNumber>Ikdj3zYXXGsC/Afm9Tvx+g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>AQAAAAA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG8MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTEwMTYwNAYDVQQDEy1WZXJpU2lnbiBDbGFzcyAzIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gRzM=">
+ <serialNumber>NMpMcEnex3eXx4ohk9glcQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzQ=">
+ <serialNumber>H08=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Qh/SnQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEfMB0GA1UEAxMWVVROLVVTRVJGaXJzdC1IYXJkd2FyZQ==">
+ <serialNumber>Xrr31RF0DoIzMKXS6XtD+g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>CqnbFQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bydxog==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpERk4tVmVyZWluMRAwDgYDVQQLEwdERk4tUEtJMSQwIgYDVQQDExtERk4tVmVyZWluIFBDQSBHbG9iYWwgLSBHMDE=">
+ <serialNumber>F6QlB/yX+A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGYxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEgMB4GA1UEAxMXR2VvVHJ1c3QgRFYgU1NMIENBIC0gRzI=">
+ <serialNumber>XhcFm2g619rt8Sro+a4rHA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp">
+ <serialNumber>TA6BjA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMiBQcmltYXJ5IENB">
+ <serialNumber>ESDYXNBhF+dePFjojs7u2vj1</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Gg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>Pgyeh2mqlVzqI9hFntRbUQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkpQMRMwEQYDVQQKEwpGdWppIFhlcm94MS0wKwYDVQQDEyRGdWppIFhlcm94IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IDI=">
+ <serialNumber>AUa47POQ1dN5</serialNumber>
+ </certItem>
+ <certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=">
+ <serialNumber>Ajp+</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Qh/O5w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Iw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>TAA2G+UIK6mqznQKBT77NA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx">
+ <serialNumber>U+1Y1QpJc0FOR5JdCJ01gQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDIxCzAJBgNVBAYTAkNOMQ4wDAYDVQQKEwVDTk5JQzETMBEGA1UEAxMKQ05OSUMgUk9PVA==">
+ <serialNumber>STMAFQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xCzAJBgNVBAYTAlRXMTAwLgYDVQQKDCdHb3Zlcm5tZW50IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>K1ftto7Xcb0YKwQ6uMvOIA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Fw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=">
+ <serialNumber>Ajp/</serialNumber>
+ </certItem>
+ <certItem issuerName="MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==">
+ <serialNumber>Ermw0Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xCzAJBgNVBAYTAlRXMTAwLgYDVQQKDCdHb3Zlcm5tZW50IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>APdCebq8ZyZr/T0luxlicNw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Ig==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>Nbc68Q8EHza72P/hSWcddw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFAxCzAJBgNVBAYTAkpQMRgwFgYDVQQKEw9TRUNPTSBUcnVzdC5uZXQxJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMQ==">
+ <serialNumber>Ermwtg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>MABJSw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>QDi5rw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFIxCzAJBgNVBAYTAk5MMRkwFwYDVQQKDBBEaWdpZGVudGl0eSBCLlYuMSgwJgYDVQQDDB9EaWdpZGVudGl0eSBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>DA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>VBSf+IncsTB3RZS4KFCJPQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy">
+ <serialNumber>ANX8SnNRxCmsE/GCl5hw+8A=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABElatX7I=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Ew==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>LizeWXFWP5pZPI/dLc+PVQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=">
+ <serialNumber>cJ+vg4742XhNgJW2ot9eIg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>L1fHogsVxmfMBka5q4uzaQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>VUtahOwvvmJFwlvmGDZP5w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGsxCzAJBgNVBAYTAklUMQ4wDAYDVQQHDAVNaWxhbjEjMCEGA1UECgwaQWN0YWxpcyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGljYXRpb24gUm9vdCBDQQ==">
+ <serialNumber>WJ2qHzWUqTk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>QDi5sQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABRE7wRk4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Aw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>Cj0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy">
+ <serialNumber>AIChpbGNqu4XKp9J70syKEs=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAkZSMRMwEQYDVQQKEwpDZXJ0aW5vbWlzMRcwFQYDVQQLEw4wMDAyIDQzMzk5ODkwMzEdMBsGA1UEAxMUQ2VydGlub21pcyAtIFJvb3QgQ0E=">
+ <serialNumber>J8mznxvTvOR5p4Br3a3sm5j5iM0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Qh/SqA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx">
+ <serialNumber>JD1wxDd8IgmiqX7MyPPg1g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydInw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>FQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=">
+ <serialNumber>AjqL</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABIg08FMU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=">
+ <serialNumber>LU4d0t7PAsZNgJGZcb+o/w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byeaqw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>B+U=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=">
+ <serialNumber>KuzHPJLdK5hNgJRo3R47Ag==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>BA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGCMQswCQYDVQQGEwJVUzEeMBwGA1UECxMVd3d3LnhyYW1wc2VjdXJpdHkuY29tMSQwIgYDVQQKExtYUmFtcCBTZWN1cml0eSBTZXJ2aWNlcyBJbmMxLTArBgNVBAMTJFhSYW1wIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>QDi5sA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx">
+ <serialNumber>HxT1XSjIpzjMprp9Qu1gYQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>BwImeaRkSZQLYwFREwKo3R1Jn+8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWU=">
+ <serialNumber>JLiDzgpL7oFNgJN+jIjt7w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfNeA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=">
+ <serialNumber>AjqK</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEZMBcGA1UEAxMQU2VjdXJlIEdsb2JhbCBDQQ==">
+ <serialNumber>QAAnEQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>CLc=</serialNumber>
+ </certItem>
+ <certItem issuerName="ME0xCzAJBgNVBAYTAk5MMRkwFwYDVQQKDBBEaWdpZGVudGl0eSBCLlYuMSMwIQYDVQQDDBpEaWdpZGVudGl0eSBCdXJnZXIgQ0EgLSBHMg==">
+ <serialNumber>DA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>HA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>MABJTA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGsxCzAJBgNVBAYTAklUMQ4wDAYDVQQHDAVNaWxhbjEjMCEGA1UECgwaQWN0YWxpcyBTLnAuQS4vMDMzNTg1MjA5NjcxJzAlBgNVBAMMHkFjdGFsaXMgQXV0aGVudGljYXRpb24gUm9vdCBDQQ==">
+ <serialNumber>OfJBIhFwAdQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>IA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFIxCzAJBgNVBAYTAk5MMRkwFwYDVQQKDBBEaWdpZGVudGl0eSBCLlYuMSgwJgYDVQQDDB9EaWdpZGVudGl0eSBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>Cw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAy">
+ <serialNumber>GpO48aJ8GngtwECqZhm/xA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25h">
+ <serialNumber>Bw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>eLumDUO40KwnecZLJxFM2A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydiAg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>Er0moq4zwH8ke2pYafIKdg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>SurdtfsuPcXXDpY2LkBpYO6BT7o=</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>RH7WhshwXRK6f0VfOfjXgQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByembA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfFnw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>frj5jTuqBnQ4fljPvVU3KA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>AJBQSPqrEvDE2Hz8xH39Low=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>ZECgRdZEsns=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=">
+ <serialNumber>a9rf7/BmG9JkKvRuy7J5QA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0E=">
+ <serialNumber>AjpW</serialNumber>
+ </certItem>
+ <certItem issuerName="MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx">
+ <serialNumber>ESBrHE7sFC7CQ8EM681xA3CY</serialNumber>
+ </certItem>
+ <certItem issuerName="MCgxCzAJBgNVBAYTAkJFMRkwFwYDVQQDExBCZWxnaXVtIFJvb3QgQ0Ey">
+ <serialNumber>RFlmmjulj6Ve7PfBi44nnw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx">
+ <serialNumber>ESDDtMgFFiaUfKo7HD9qImM7</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>AQw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGcxCzAJBgNVBAYTAkRFMRMwEQYDVQQKEwpGcmF1bmhvZmVyMSEwHwYDVQQLExhGcmF1bmhvZmVyIENvcnBvcmF0ZSBQS0kxIDAeBgNVBAMTF0ZyYXVuaG9mZXIgUm9vdCBDQSAyMDA3">
+ <serialNumber>YR0zGQAAAAAAAw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>e9JTGBe45yw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=">
+ <serialNumber>XJ8pGvGNM9RIcLUG9YQjLQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJGaXJzdC1PYmplY3Q=">
+ <serialNumber>CMNfzETd7XxesS9FOUj9Mg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bye2Cg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=">
+ <serialNumber>Hnms0W0OxHSYE2F0XE97sw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>AJiU+bpWh2Uc4xFRf8GM9yA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=">
+ <serialNumber>fbsHfUkagQtznc3rtY1uDg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUw=">
+ <serialNumber>CdYL9vSQCEKzBwjO10ud2w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byc85g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bydrxg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQQ==">
+ <serialNumber>e7wSpVxmgAS5/ioLi2iBIA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDErMCkGA1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>AKrMYlJmUUin8FOM/0TJrmk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQQ==">
+ <serialNumber>ALxyZmb/WL/wAuUiPK5oK/g=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==">
+ <serialNumber>RnQ3dYovwvB0D5q2YGY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==">
+ <serialNumber>RnQ3dg5KdDZs0nyFZk4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFAxJDAiBgNVBAsTG0dsb2JhbFNpZ24gRUNDIFJvb3QgQ0EgLSBSNDETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbg==">
+ <serialNumber>Hwexgn/ZCJicZPcsIyI8zxQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>Iqpyf/YoGgvHc8HiDAxAI8o=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydSYg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp">
+ <serialNumber>TA5iEg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMDkgRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIwMAYDVQQDEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMg==">
+ <serialNumber>UdNjvA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>RVWTeb5EKqE7cy7MUD2oJ3M=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>IHj3eiEK3K1Xrpu1uvtBuvE=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>YUlF+VXF2FWFqCo472HfZlw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>QM1zZ4GZ4gfwpQtUYye3Ne0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRydXN0ZWQgUm9vdCBDQSBHMg==">
+ <serialNumber>LAVIFm0MWZYH+Sv8Vf+IqkM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAlRXMQ4wDAYDVQQKEwVUYWlDQTESMBAGA1UECxMJUG9saWN5IENBMSQwIgYDVQQDExtUYWlDQSBJbmZvcm1hdGlvbiBQb2xpY3kgQ0E=">
+ <serialNumber>UbQGvw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bydvrw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfDtA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byemag==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByemaQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp">
+ <serialNumber>OGPFrg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>APt5i5rs4dIIQPwZdk9/ISc=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQKDBBWZXJpem9uIEJ1c2luZXNzMREwDwYDVQQLDAhPbW5pUm9vdDEfMB0GA1UEAwwWVmVyaXpvbiBHbG9iYWwgUm9vdCBDQQ==">
+ <serialNumber>A4w=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQKDBBWZXJpem9uIEJ1c2luZXNzMREwDwYDVQQLDAhPbW5pUm9vdDEfMB0GA1UEAwwWVmVyaXpvbiBHbG9iYWwgUm9vdCBDQQ==">
+ <serialNumber>A4g=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDMxCzAJBgNVBAYTAlBUMQ0wCwYDVQQKDARTQ0VFMRUwEwYDVQQDDAxFQ1JhaXpFc3RhZG8=">
+ <serialNumber>cx0HrIEQg8JHWTP7DzOxSQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJVUzEgMB4GA1UECgwXV2VsbHMgRmFyZ28gV2VsbHNTZWN1cmUxHDAaBgNVBAsME1dlbGxzIEZhcmdvIEJhbmsgTkExNjA0BgNVBAMMLVdlbGxzU2VjdXJlIFB1YmxpYyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eQ==">
+ <serialNumber>AZ0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJVUzEgMB4GA1UECgwXV2VsbHMgRmFyZ28gV2VsbHNTZWN1cmUxHDAaBgNVBAsME1dlbGxzIEZhcmdvIEJhbmsgTkExNjA0BgNVBAMMLVdlbGxzU2VjdXJlIFB1YmxpYyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eQ==">
+ <serialNumber>AMs=</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAlRXMRIwEAYDVQQKDAlUQUlXQU4tQ0ExEDAOBgNVBAsMB1Jvb3QgQ0ExKjAoBgNVBAMMIVRXQ0EgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>DL8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAlRXMRIwEAYDVQQKDAlUQUlXQU4tQ0ExEDAOBgNVBAsMB1Jvb3QgQ0ExKjAoBgNVBAMMIVRXQ0EgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>QAEy3RIAAAAAAAAMweH5dw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>R/j2qA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExIjAgBgNVBAMTGVRydXN0ZWQgUm9vdCBDQSBTSEEyNTYgRzI=">
+ <serialNumber>RdHgEmEIjdyRFWDRRlk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExIjAgBgNVBAMTGVRydXN0ZWQgUm9vdCBDQSBTSEEyNTYgRzI=">
+ <serialNumber>RvCM2iRdkCE82ZOO2dU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExIjAgBgNVBAMTGVRydXN0ZWQgUm9vdCBDQSBTSEEyNTYgRzI=">
+ <serialNumber>Rea7UUYH3jl33BryPIo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkZSMRMwEQYDVQQKEwpDZXJ0aW5vbWlzMRcwFQYDVQQLEw4wMDAyIDQzMzk5ODkwMzEmMCQGA1UEAwwdQ2VydGlub21pcyAtIEF1dG9yaXTDqSBSYWNpbmU=">
+ <serialNumber>HQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkZSMRMwEQYDVQQKEwpDZXJ0aW5vbWlzMRcwFQYDVQQLEw4wMDAyIDQzMzk5ODkwMzEmMCQGA1UEAwwdQ2VydGlub21pcyAtIEF1dG9yaXTDqSBSYWNpbmU=">
+ <serialNumber>Eg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkZSMRMwEQYDVQQKEwpDZXJ0aW5vbWlzMRcwFQYDVQQLEw4wMDAyIDQzMzk5ODkwMzEmMCQGA1UEAwwdQ2VydGlub21pcyAtIEF1dG9yaXTDqSBSYWNpbmU=">
+ <serialNumber>GA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydIoA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BycpYA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BycfpA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bycfmw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeekA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfJhw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byd/Ug==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byd/UA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byd/Tg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeBQg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byd/Tw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byd/UQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABL07hSVI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0E=">
+ <serialNumber>BAAAAAABL07hRxA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABL07hXdQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>BAAAAAABL07hW2M=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOjA4BgNVBAMMMVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBQZXJzb29uIENBIC0gRzM=">
+ <serialNumber>f43O9TualR8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>ATE5Ig==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>ATE6Xw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>ATE6YA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>azAcTWL+ijs=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>LYTXWk7gMu8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9LtnE=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9LtnQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9Ltm0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9LtnY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH0xCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>eohOGeS5ZHJeptyBvCu/mQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH0xCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSswKQYDVQQLEyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYDVQQDEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>fqRDfSf8haCEh2nWE6O+bA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGcxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEeMBwGA1UEAxMVU3dpc3Njb20gUm9vdCBFViBDQSAy">
+ <serialNumber>QFLH3Zrq+I5WQ6TlWzfUxA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx">
+ <serialNumber>OUOBG6TE0Lr+uYYGxeVbHg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAx">
+ <serialNumber>AI7cApIcPA3cfSpQMf40onQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGcxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEeMBwGA1UEAxMVU3dpc3Njb20gUm9vdCBFViBDQSAy">
+ <serialNumber>AL691kTvkemG9UQNa6McQg8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxITAfBgNVBAMTGFN3aXNzU2lnbiBTaWx2ZXIgQ0EgLSBHMg==">
+ <serialNumber>APiyCXmwAUq+95DYa3DmGw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=">
+ <serialNumber>AIZ6Wq/4deFQzwC6NnFpUA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxITAfBgNVBAMTGFN3aXNzU2lnbiBTaWx2ZXIgQ0EgLSBHMg==">
+ <serialNumber>aBXsv0oU3xqh2xkUPOi8</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>PmDn14AwWY28IlJeBXkDvA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>G8sz+bm+vQjTpQNBh5CfMg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>VP3bQF/UdNfxq/UOypU1zQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>A/kVDQpE7c9h+WxlWQFzSQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>OOkLFZaa4CXGyJlLTIEjUQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>GuJ0aGBYhChXAOljooJZ3A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEtMCsGA1UEAxMkVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENB">
+ <serialNumber>Cf0103tCm9oulH1QK0weTA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHsxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEyMDAGA1UEAxMpVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENBIC0gRzI=">
+ <serialNumber>dhjnNtYx6cojdAE55TgIBA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>RbG+tfPUe/vBRfTZF54i8g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHsxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEyMDAGA1UEAxMpVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENBIC0gRzI=">
+ <serialNumber>NpsJHyt3o1U47AAgw3UNXA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>GTPOETOFf5mIsbuzrojGfw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>OgxXyntHYBXnPAHDxY0OXg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHsxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEyMDAGA1UEAxMpVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENBIC0gRzI=">
+ <serialNumber>U3KGm6UTqJ/nsMyteiUa2g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHsxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEyMDAGA1UEAxMpVmVyaVNpZ24gQ2xhc3MgMyBTU1AgSW50ZXJtZWRpYXRlIENBIC0gRzI=">
+ <serialNumber>eViJ2GX26lp5HbF+XNp1kQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>MWzraR3LLhU9m/qKEhvVLQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>CgFBQQAAATjtdPY5AAAAAg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>ANUANvVYN7xqAISA9rvJPzQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGsxCzAJBgNVBAYTAlVTMQ0wCwYDVQQKEwRWSVNBMS8wLQYDVQQLEyZWaXNhIEludGVybmF0aW9uYWwgU2VydmljZSBBc3NvY2lhdGlvbjEcMBoGA1UEAxMTVmlzYSBlQ29tbWVyY2UgUm9vdA==">
+ <serialNumber>B2VhZAPxCDH3s9Mkbu3HfQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>JGKKnm00uOQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=">
+ <serialNumber>CgFBQQAAATjkOB1sAAAAAg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>Aj/CJN2QWZAF25GXPXADOA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA3IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNA==">
+ <serialNumber>RmI44ARDVCUOgXNK9ACAbg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>HVRikKXRQ1ouhOpYcOna/A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yaw==">
+ <serialNumber>B8f7CHJUqV3VareLPE+2kA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>D/VlGqmz9Nai1ywCydT/RQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yaw==">
+ <serialNumber>O2Qh+qhbBRuZA11yDhcLGQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>E/YGRk12iZqZuMfsIiVaeg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>RMgdRGEBv0KzFCjgGFp0Hg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=">
+ <serialNumber>SeEzbpTltqUtqW7UiuJ2</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNQ==">
+ <serialNumber>Xmo3AIW2VHeeJoR0o09RGQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>QOu0a5Z9rCkw6Nk7Rg1/AQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJVUzEgMB4GA1UECgwXV2VsbHMgRmFyZ28gV2VsbHNTZWN1cmUxHDAaBgNVBAsME1dlbGxzIEZhcmdvIEJhbmsgTkExNjA0BgNVBAMMLVdlbGxzU2VjdXJlIFB1YmxpYyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eQ==">
+ <serialNumber>ANU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJVUzEgMB4GA1UECgwXV2VsbHMgRmFyZ28gV2VsbHNTZWN1cmUxHDAaBgNVBAsME1dlbGxzIEZhcmdvIEJhbmsgTkExNjA0BgNVBAMMLVdlbGxzU2VjdXJlIFB1YmxpYyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eQ==">
+ <serialNumber>ATk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeLBg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMQ==">
+ <serialNumber>BUE=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxITAfBgNVBAMTGFN3aXNzU2lnbiBTaWx2ZXIgQ0EgLSBHMg==">
+ <serialNumber>AINVG9I4T2jgQgW4N9SNhw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNQ==">
+ <serialNumber>UWMOvf4tj/x5cQN2PXVSww==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BEeJFwO0nu759EPo9tKluw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BydCwg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGRMQswCQYDVQQGEwJERTEQMA4GA1UECgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaVjAxOjA4BgNVBAsMMUNvcHlyaWdodCAoQykgU2llbWVucyBBRyAyMDExIEFsbCBSaWdodHMgUmVzZXJ2ZWQxITAfBgNVBAMMGFNpZW1lbnMgSW50ZXJuZXQgQ0EgVjEuMA==">
+ <serialNumber>AaoZYg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGRMQswCQYDVQQGEwJERTEQMA4GA1UECgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaVjAxOjA4BgNVBAsMMUNvcHlyaWdodCAoQykgU2llbWVucyBBRyAyMDExIEFsbCBSaWdodHMgUmVzZXJ2ZWQxITAfBgNVBAMMGFNpZW1lbnMgSW50ZXJuZXQgQ0EgVjEuMA==">
+ <serialNumber>WW8OCQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTowOAYDVQQDEzFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiBQYXJ0bmVycyBDQSAtIFNIQTI1NiAtIEcy">
+ <serialNumber>AeNmeF8oVpDp/4GPvA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABQaHhOT4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABQaHhNLo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABQaHhPSY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>RqFXxGPuA18=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>ARU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>AwBGo0Zmp6KRryAguuMvXATI</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>AxW0+uDsfyCSfhECdsGGpVD8</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>Ax6Jm7ajV49tqHgf9nYnzRCI</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>AyYMguSo1my449OZq51C3s3Z</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>AyjNQ4dnGD3FD6WL5gYrYru7</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>AzL4tLuklekJ8lSh6VnRMSrk</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A0BOaf9UbJxzqBudSyes/cEM</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A1V4dX0tTb1rdTZxdWcuZ7YR</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A3TWA5Aylxw0x8bVvrmUSNJd</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A3UNTBOHUkbq+k999nJeSJdF</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A3WVy2V+2VFkWtMvA6HFwnhq</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A3ZQibPGSZ8nPVbuccaCvUfa</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A5oET6WBWx72ColKf0txoWyR</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A7GX+szdK8/7Kf0xUuarfyIN</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A7RCxMe1S9Hb7ENzRxl0mxGP</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A7T0V6o47rgCKl3oUb7jF2Ph</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A7uy+rmTav6tDH4dRrsnvXGH</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A8LV4zckxcwdttbQSk0EPnoA</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A8aDg1/IA4O8gjMPZHVqPI+w</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A8wZnhfuY6VIV1SwGsTGNR7L</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A9BRwOwbXRRhCe+kcmglgW3z</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A+RCQYwhofmXM+/hxdyoUzkI</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A+ly3y1rVP59k/MKfcE3DoEq</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A/7DHCczBnP5qUVh0jF2pvwB</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>A/99bZCzSpexYL5y6dSryDn3</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BDV89QWZE9MJYlCpFQUv5Y2W</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BHT6CK6B569m/dd5dEluBOEd</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BJDHnthjoDRutxFRJPFnixbU</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BKobzjrOxa/6kCR0ImKoqaQW</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BKrxi2/1iFxHEFzyZvegxq5C</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BLlQHJ611eOZuedFrFgVAfAs</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BOIIipysxAz5xHIMmFRvYchY</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BONHqLIx/ibQE08IQIyoGaXg</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BOPwjyn5eqfeoxs7Z0y3vqNN</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BOc11keA9WJ9R20XQY8hO7yi</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BOncXh7IZp1SNydhtUdyh2O2</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>BPVqx4UbKVAbJSFTKwrcFryU</serialNumber>
+ </certItem>
+ <certItem issuerName="MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQDExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMw==">
+ <serialNumber>AxPlMqxkByCn3XNuYMhYNMcp</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>DNHqTQd9QC+JnMy6AWyhkg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>cFbYT3bxd1sAAAAAUdNX8A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=">
+ <serialNumber>AIQ8dLGqNIaxxMeg31W16Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzI=">
+ <serialNumber>AIQ8dLGqNIaxxMeg31W16Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEcxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxITAfBgNVBAMTGFN3aXNzU2lnbiBTaWx2ZXIgQ0EgLSBHMg==">
+ <serialNumber>ANsAyDuSSs7Z83LfMZ+TDw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>VIFPnH3Io2OmF0J5KK8gzA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>EkoaKijVTGVYI5c604iweg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>EYfoVrySx7V3OUqs4xKvgA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>SrQ125q7UcLfxVKepx+lRg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>OIJdAvYxHmLb6YaaMmwmjg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>KNhgX8XuJduYciIyatpOQg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>UN78HLEKf7W9vQYkzYpJnw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>XOZMbPKQuJEw8Ib5neDVpQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>DoP7aSdEs/3y+o2Gj9zgWA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>ElBUYv/f+6+gnbAJ23qnAA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>d8ToN4Dfs5RqD2yfAp12yQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>WU+jmMhGAumhewqVKrZBmg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>Q1r0dRkkG9miuHj/Y52izw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>CGo/+42e75JBJ2JcOEaMFw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>X4C5SJIG0BDeJvpQq4ngCw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>UfM8pWkcmmLGRiGIVydmoA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>KvQ5AzK6tQy8eBy7NAD/lQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>XbPH0u4MjoIrWzN8QCilfg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>dUIqmrcgq/261bRbo7fM1g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>RkNUwM80Jt7beb4ek+iI8w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>ODTGURr0vY14WkIt15hHrg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>c0ENPRDbRozjU83garZrdA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>UT6GtTGbEC6SXJteWAKy2g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>FK+rVRFA0o0PnW+X6V60gQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>CuUEKEJM4xhxlFXraPcSpQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>VNb2Hjai/t7dmCtOzRXXew==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>OeKv0wi+ATDxfQ6CWir1vA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTE=">
+ <serialNumber>AOVojQRgyPca</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTI=">
+ <serialNumber>APB/jQRgyPca</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTE=">
+ <serialNumber>AOVojQRgyPcY</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGcMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0x">
+ <serialNumber>APB/jQRgyP8Q</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwOA==">
+ <serialNumber>AklaZYwhC9k=</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290">
+ <serialNumber>DA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGuMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4">
+ <serialNumber>RXJFI0h6EJY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwOA==">
+ <serialNumber>Ah69dEvrzT4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGuMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4">
+ <serialNumber>N4XreFRrqFQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9Ltm8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGVMQswCQYDVQQGEwJHUjFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTE=">
+ <serialNumber>GN2Hrh9Ltmw=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>D4dSwi4udjGtMftKLTSFyg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNQ==">
+ <serialNumber>JLLEdDl2iHqqyenVWwQ/XA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>dSBsq/te0hzZauKHgJ3EWg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>TsaDDThhoyhX10SURO3NMg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>IARKrBjlKQLyVGA4X52L7w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>KRfQOuBdOSpEmAxSpDZGZg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>YwslVqGwc9CHkaZkXNZ4xw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>QjiuX0y1agXQQqmDB2yh3w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>C2tQZWb2eoQD2XC3F5JSzg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>a9HDb1beqQYmkvFH0qExcg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>KUZMXOUj2sdY2i2Rfgp/5Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>P4sUnc++hlU/bXj0zSTlcQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>bf8hEJywo1lAp4UNcLl5Ew==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>Gz4uHrL2usrTZrPCHeuF5g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>fZ10MyCe51jAjZCsDgqaxA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>UKM/CNF2OvC4giYnAUG/Ag==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>X3iUdzxCEtOAKpiTLsqjBA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>GskXrIFkzLS+4yohQM9EUA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>LJ8wKbrQXgT8VExZ6vEfWA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>BWuckD4dPHZYW5ThBsl+aQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>LzVYePklc3vH3jkk0BZr9g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>WfPUsnnSF04ShWVYEa/KRA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>fa9agGguMHfBorMTXXMd9g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>HGD2RtvXMaPDqHIPLdXocw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBSU0EgUm9vdCAtIEcx">
+ <serialNumber>JjjcXrfGjTCi1ug/AEeYlg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>TbPyD9NnsEcxyK6LIsr78g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>EgtJ1f+/tZwlGfg0Uu7XCQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>PbXdgANzAyCOCZ5qa/7E6A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>QspbHxzWb41SX9TUhF1N1A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHYxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEnMCUGA1UEAxMeU3ltYW50ZWMgV2ViIFBLSSBFQ0MgUm9vdCAtIEcx">
+ <serialNumber>a2GKnRbYMZ0oZkRzJE8NIw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNQ==">
+ <serialNumber>em/HTY01Cvv6ITgkH+ftlg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMRUwEwYDVQQLEwxNaWNyb3NvZnQgSVQxHjAcBgNVBAMTFU1pY3Jvc29mdCBJVCBTU0wgU0hBMg==">
+ <serialNumber>WgAFElcDxFjoswSzjAABAAUSVw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMRUwEwYDVQQLEwxNaWNyb3NvZnQgSVQxHjAcBgNVBAMTFU1pY3Jvc29mdCBJVCBTU0wgU0hBMg==">
+ <serialNumber>WgAFElbyxxPA8BdM4gABAAUSVg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG0MQswCQYDVQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy">
+ <serialNumber>AOfHzdPzlvw5</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290">
+ <serialNumber>Eg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfNbw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQKDBBWZXJpem9uIEJ1c2luZXNzMREwDwYDVQQLDAhPbW5pUm9vdDEfMB0GA1UEAwwWVmVyaXpvbiBHbG9iYWwgUm9vdCBDQQ==">
+ <serialNumber>BFA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFQxCzAJBgNVBAYTAkJNMRkwFwYDVQQKDBBRdW9WYWRpcyBMaW1pdGVkMSowKAYDVQQDDCFRdW9WYWRpcyBFbnRlcnByaXNlIFRydXN0IENBIDIgRzM=">
+ <serialNumber>bqapwACCtKhVagTl7cEP7KFbM0E=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExGjAYBgNVBAoTEUludGVsIENvcnBvcmF0aW9uMSUwIwYDVQQDExxJbnRlbCBFeHRlcm5hbCBJc3N1aW5nIENBIDZC">
+ <serialNumber>HwAABsvzDP+DIzUG6QAAAAAGyw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEvMC0GA1UEAxMmU3ltYW50ZWMgQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzQ=">
+ <serialNumber>UDE/uwr4z5V8eZI4+1gkAw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGSMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE4MDYGA1UEAxMvQ09NT0RPIFJTQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2ZXIgQ0E=">
+ <serialNumber>TasC8Zd8BT8kXEE67cFQmA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJUWDEQMA4GA1UEBxMHSG91c3RvbjEVMBMGA1UEChMMY1BhbmVsLCBJbmMuMS0wKwYDVQQDEyRjUGFuZWwsIEluYy4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>NlLRZJFLco/An3cLAGjGgQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJUWDEQMA4GA1UEBxMHSG91c3RvbjEVMBMGA1UEChMMY1BhbmVsLCBJbmMuMS0wKwYDVQQDEyRjUGFuZWwsIEluYy4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>AJk3QFH13eHUHHVnsvwS0Vo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHAxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xLzAtBgNVBAMTJkRpZ2lDZXJ0IFNIQTIgSGlnaCBBc3N1cmFuY2UgU2VydmVyIENB">
+ <serialNumber>Cn+uUpLudsH09lYYIPTK5A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bye9zw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeWyA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bye9zg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGFMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSkwJwYDVQQDEyBDZXJ0dW0gRG9tYWluIFZhbGlkYXRpb24gQ0EgU0hBMg==">
+ <serialNumber>VEav0UR+l38TpKTRi7sS1g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDMxCzAJBgNVBAYTAlBUMQ0wCwYDVQQKDARTQ0VFMRUwEwYDVQQDDAxFQ1JhaXpFc3RhZG8=">
+ <serialNumber>PgImeGqCkapG6P426Ne85w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIG9MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>X22XOlwfc1Taw/ORwGOIeg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHUxCzAJBgNVBAYTAkFVMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEmMCQGA1UEAxMdU3ltYW50ZWMgQXVzdHJhbGlhIENsYXNzIDIgQ0E=">
+ <serialNumber>Nn6RHaVImMEtHLbPqlyGEA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Bye/xA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDMxCzAJBgNVBAYTAlBUMQ0wCwYDVQQKDARTQ0VFMRUwEwYDVQQDDAxFQ1JhaXpFc3RhZG8=">
+ <serialNumber>PWwhjEHh0n5G6P8b+bAkcg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHoxCzAJBgNVBAYTAkFVMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazErMCkGA1UEAxMiU3ltYW50ZWMgQXVzdHJhbGlhIENsYXNzIDIgQ0EgLSBHMg==">
+ <serialNumber>MZNwCx0BAjzTOgcvHsmdhQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byecpw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xLTArBgNVBAMMJFN0YWF0IGRlciBOZWRlcmxhbmRlbiBCdXJnZXIgQ0EgLSBHMg==">
+ <serialNumber>ATE33w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>ATEzEQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5">
+ <serialNumber>Qh/SsQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzM=">
+ <serialNumber>PybG62jqpxKYOV5MlXAGPJYDy9M=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDI=">
+ <serialNumber>WDFms0iypljMkohTzBvf4GmTOu0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>IsWDgJMv7uY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHTAbBgNVBAsTFERvbWFpbiBWYWxpZGF0ZWQgU1NMMSAwHgYDVQQDExd0aGF3dGUgRFYgU1NMIFNIQTI1NiBDQQ==">
+ <serialNumber>dqN9ZZM/PfFCXStajJdbtQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGuMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4">
+ <serialNumber>Ag==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGuMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4">
+ <serialNumber>BQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>AKbcLtOIMMwDPSOrzrclZL8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>ANjIV8rkvmb5E3Wf3aPV2ys=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>APy7Z8kyJRVBcM/oki5xZ2M=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>BlOMLY2hk1OPGflbt/pPXQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>MpG5djdbcIoI5TIkJ7vENA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IEVDQyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>UJ3tpbZLsyrhh60M9CMQaQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>dgkExobSurPQq8GYrxxluA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>A09gcxt2IBLNzwqUBAhkDg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>TpyAwu1JmlIKD9gyf+0d4w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>ZpLTr9toPH+XRF7OITitfw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>And3HzRA33dI3K772oqBCw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>T3UrJ2tKvT0lyumu37ic6g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>PxYWUib8jdriX5MSGW7Ozw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>H/Vx9uatDIulnLLrZjXEKg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>ej2u88yEVXEb8BP1K49U6Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>BtDaTXIs6tBSClhSLPXdYg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>cepjeevcJiJnbGEvdJE1jg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>Qk03giZwJwxf5QpixTKflQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>dn2My7LvPi25AtUw3aPEmQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>c5EZjjNc7LMOapbOzjEtJA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>dtULH8kD2mThpR/g1YJEtw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>G0UY3ZCa+JfTgAVgvFA8qg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>ajUDfjuO76YmIt3+fyTLXg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>TrUuR7x7VeU7Qvlwt8Sumw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>Hl7MT7aU4GbuanaMzc5eAg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>R4b2Vg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEgxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdTZWN1cmVUcnVzdCBDb3Jwb3JhdGlvbjEXMBUGA1UEAxMOU2VjdXJlVHJ1c3QgQ0E=">
+ <serialNumber>R4af5Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>WV2iHxGL6Vg=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>bYuYcMtVvjo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>UzAV4JqeuGY=</serialNumber>
+ </certItem>
+ <certItem issuerName="ME4xCzAJBgNVBAYTAk5PMR0wGwYDVQQKDBRCdXlwYXNzIEFTLTk4MzE2MzMyNzEgMB4GA1UEAwwXQnV5cGFzcyBDbGFzcyAyIFJvb3QgQ0E=">
+ <serialNumber>KA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>IL094GkEPSU+HAucglL0Ig==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UECgwyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsMH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsMLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLDCxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAwwGRUMtQUND">
+ <serialNumber>L5tOVjVGKtFP6V84tGEFPg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIIBGDELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMTQwMgYDVQQHEytQYXNzYXRnZSBkZSBsYSBDb25jZXBjaW8gMTEgMDgwMDggQmFyY2Vsb25hMS4wLAYDVQQLEyVTZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvIEVDVi0yMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJDSUMtMiAoYykwMzEfMB0GA1UECxMWVW5pdmVyc2l0YXRzIGkgUmVjZXJjYTEOMAwGA1UEAxMFRUMtVVI=">
+ <serialNumber>JSPC8hAKsUBP6Y3n9JMx8w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHpMQswCQYDVQQGEwJFUzE7MDkGA1UECgwyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxLjAsBgNVBAsMJVNlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8gRUNWLTExNjA0BgNVBAsMLVZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlckNJQy0xICAoYykwMzEhMB8GA1UECwwYR2VuZXJhbGl0YXQgZGUgQ2F0YWx1bnlhMRIwEAYDVQQDDAlFQy1HRU5DQVQ=">
+ <serialNumber>On0bAstcoxZP6WERe150Gw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UECgwyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsMH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsMLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLDCxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAwwGRUMtQUND">
+ <serialNumber>IqW4gO46S81PjTpHBA7mUQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UECgwyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsMH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsMLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLDCxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAwwGRUMtQUND">
+ <serialNumber>dfE2CNAy9IxP6VwZ2IU2cA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UECgwyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsMH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsMLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLDCxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAwwGRUMtQUND">
+ <serialNumber>W99Z2UuV5pFP6V8AYIwcVQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UECgwyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsMH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsMLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLDCxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAwwGRUMtQUND">
+ <serialNumber>P0qUU7RhznNP6V9iGYbSbA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>cEBA0P3KPBk/ojwnYepwzg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>BqVfPLKBlSg/4Enn+TGdbA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIIBGDELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMTQwMgYDVQQHEytQYXNzYXRnZSBkZSBsYSBDb25jZXBjaW8gMTEgMDgwMDggQmFyY2Vsb25hMS4wLAYDVQQLEyVTZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvIEVDVi0yMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJDSUMtMiAoYykwMzEfMB0GA1UECxMWVW5pdmVyc2l0YXRzIGkgUmVjZXJjYTEOMAwGA1UEAxMFRUMtVVI=">
+ <serialNumber>Y3QACu2RGYVJ6FAnJWZpHA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHpMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxLjAsBgNVBAsTJVNlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8gRUNWLTExNjA0BgNVBAsTLVZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlckNJQy0xICAoYykwMzEhMB8GA1UECxMYR2VuZXJhbGl0YXQgZGUgQ2F0YWx1bnlhMRIwEAYDVQQDEwlFQy1HRU5DQVQ=">
+ <serialNumber>b+8vFPRPzN8+HCEWmIwVNg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>PZfTkwQ5Yio+HE2mvtFzDg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>c+6uFePfrahUGpXs8lhiTw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>VBy0L8eIKnVUGpY97OXrkw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>JY5zdgD/mG9A4oB/uzdSwQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHwxCzAJBgNVBAYTAk5MMSIwIAYDVQQKExlLUE4gQ29ycG9yYXRlIE1hcmtldCBCLlYuMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSgwJgYDVQQDEx9LUE4gQ29ycG9yYXRlIE1hcmtldCBDbGFzcyAyIENB">
+ <serialNumber>F6sWArGVJv7AwBSxbnnqaw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGwxCzAJBgNVBAYTAk5MMRkwFwYDVQQKExBLUE4gVGVsZWNvbSBCLlYuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMSEwHwYDVQQDExhLUE4gVGVsZWNvbSBCLlYuIENBIC0gRzI=">
+ <serialNumber>W4sqXNfJgPC3aLKkcOxq9Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGwxCzAJBgNVBAYTAk5MMRkwFwYDVQQKExBLUE4gVGVsZWNvbSBCLlYuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMSEwHwYDVQQDExhLUE4gVGVsZWNvbSBCLlYuIENBIC0gRzI=">
+ <serialNumber>I1kCCASG38Q8TKOJaqQtvQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHwxCzAJBgNVBAYTAk5MMS0wKwYDVQQKEyRHZXRyb25pY3MgUGlua1JvY2NhZGUgTmVkZXJsYW5kIEIuVi4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxHTAbBgNVBAMTFEdldHJvbmljcyBDbGFzcyAyIENB">
+ <serialNumber>VhmAg9gQ0IaL5+lKzrKYPQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHwxCzAJBgNVBAYTAk5MMS0wKwYDVQQKEyRHZXRyb25pY3MgUGlua1JvY2NhZGUgTmVkZXJsYW5kIEIuVi4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxHTAbBgNVBAMTFEdldHJvbmljcyBDbGFzcyAyIENB">
+ <serialNumber>MfSUS8xHwG64IFRIU5IHpw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHwxCzAJBgNVBAYTAk5MMSIwIAYDVQQKExlLUE4gQ29ycG9yYXRlIE1hcmtldCBCLlYuMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSgwJgYDVQQDEx9LUE4gQ29ycG9yYXRlIE1hcmtldCBDbGFzcyAyIENB">
+ <serialNumber>I+zjm9Bi1ZVKLF0R96thFQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>cIHLIBl0M9N90NNjZwhwSA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>XaqJA1pYkpSOSst7Hmcxew==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yaw==">
+ <serialNumber>HkN+3VDzRBFAw/QQ6XZ2gA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGUMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxRTBDBgNVBAMTPFN5bWFudGVjIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNg==">
+ <serialNumber>VedYmG4aoUcioKT467SDcg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGUMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxRTBDBgNVBAMTPFN5bWFudGVjIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNg==">
+ <serialNumber>U51Qij2xILJB29u2m4ePyw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>bcIU/gztAKdw8elgpRh2vA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGUMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxRTBDBgNVBAMTPFN5bWFudGVjIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNg==">
+ <serialNumber>Bxt4PMyN1f5tIXW8W62DhA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGUMQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxRTBDBgNVBAMTPFN5bWFudGVjIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHNg==">
+ <serialNumber>CpSHXk6RnrLSRVVJhVZEWA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>Baw9WIPUcpFvYe8bilTVVQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>PpIe9Q1JUVu5nN/+4HWAoA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MH4xCzAJBgNVBAYTAk1ZMSQwIgYDVQQKExtNU0MgVHJ1c3RnYXRlLmNvbSBTZG4uIEJoZC4xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH01TQyBUcnVzdGdhdGUuY29tIENsYXNzIDIgQ0EtRzM=">
+ <serialNumber>Q0dKwXPiEec83XZPgsQh+g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHsxCzAJBgNVBAYTAk1ZMR8wHQYDVQQKExZNU0MgVHJ1c3RnYXRlLmNvbSBTZG4uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSowKAYDVQQDEyFNU0MgVHJ1c3RnYXRlLmNvbSBDbGFzcyAyIENBIC0gRzI=">
+ <serialNumber>L3UnLdK9iz8XVM1rbm3tTw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>CAEyq5GePgxvZbmFx5WW6A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>JFcRHv1L89Vu8gagzuR3Pg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGcxCzAJBgNVBAYTAktSMRMwEQYDVQQKEwpLRUNBLCBJbmMuMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSIwIAYDVQQDExlDcm9zc0NlcnQgQ2xhc3MgMiBDQSAtIEcz">
+ <serialNumber>fLpClvRi4IMKsokzVKT9Yg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHwxCzAJBgNVBAYTAk5MMSIwIAYDVQQKExlLUE4gQ29ycG9yYXRlIE1hcmtldCBCLlYuMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSgwJgYDVQQDEx9LUE4gQ29ycG9yYXRlIE1hcmtldCBDbGFzcyAyIENB">
+ <serialNumber>GARMIB0Iaz3xxucE70O9Qg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFIxCzAJBgNVBAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIy">
+ <serialNumber>CMUHBBak0idMAAAAAAAAAAE=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFIxCzAJBgNVBAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIx">
+ <serialNumber>B6AKfwrKX6H1AAAAAAAAAAE=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExIjAgBgNVBAMTGVRydXN0ZWQgUm9vdCBDQSBTSEEyNTYgRzI=">
+ <serialNumber>R8MQVHZjYD/8LqGrob8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFI2MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu">
+ <serialNumber>Ro51e1DpnjbH3LKdghY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGMxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVzdGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExIjAgBgNVBAMTGVRydXN0ZWQgUm9vdCBDQSBTSEEyNTYgRzI=">
+ <serialNumber>AeUotGv9K4mpvLzWxw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGAxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMTAvBgNVBAMMKFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBJbnRlcm1lZGlhaXIgQ0E=">
+ <serialNumber>a5DOAqSUlLm2s6kL0x8gkQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xLTArBgNVBAMMJFN0YWF0IGRlciBOZWRlcmxhbmRlbiBCdXJnZXIgQ0EgLSBHMg==">
+ <serialNumber>ATE7Ow==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>VOQX5SLKeMGyQdoF0X9h38gYrks=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>Z6RtH7xmDM0r66IKSlpCZNrlRfY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>bdheRp0SfvS84GGiPaBnyFhE8EY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>JJfQeI7SQbQcPQ8Wc4+X2nlpWho=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDM=">
+ <serialNumber>KZlCQ0XnAo+GY3mKKJoNoNucjT0=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>amYSY2usyXU=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>EOqAPvgqxt8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3Q=">
+ <serialNumber>eRdKqRQXNv4Vp8qfLP9FiA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHzMQswCQYDVQQGEwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND">
+ <serialNumber>Mi/Y+W40ChdUGpag8vaUjQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BycpXw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byekag==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>BycpXg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byekaw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byeekg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfJkQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>SFuFrFB7MZnZ6tsqwS47tw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>aKsZrWDpsFlVL0xkShb22A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yaw==">
+ <serialNumber>deh5gFVej9+uQBqlb1fIig==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yaw==">
+ <serialNumber>OBGSpfa3Oz6a7zeF/OywMg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yaw==">
+ <serialNumber>CHF76YGUdPMMCJ4njfsnwQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>JekvfVn3h2+OX/V8Ef6vpg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMg==">
+ <serialNumber>QHdGjRdEcAz+FjRyuIJmog==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfJkw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABSOXEgNk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byc3Cg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABKkKSw14=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABI75RcWk=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDsxGDAWBgNVBAoTD0N5YmVydHJ1c3QsIEluYzEfMB0GA1UEAxMWQ3liZXJ0cnVzdCBHbG9iYWwgUm9vdA==">
+ <serialNumber>BAAAAAABECVWTJM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>RWHsRyzP3KFyjhTLPO4FPA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>W9KDjZvaDeWwN4jQG9TO3w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMg==">
+ <serialNumber>TpTE/3d2UBJYfYHw2LSoww==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMg==">
+ <serialNumber>DmbpIZh1fhYcSThCcjaohA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHQxCzAJBgNVBAYTAkdCMScwJQYDVQQKEx5Ccml0aXNoIFRlbGVjb21tdW5pY2F0aW9ucyBwbGMxHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxGzAZBgNVBAMTEkJUIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>X407nWyYC7u8lCrBrW2cRA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGRMQswCQYDVQQGEwJOTDEiMCAGA1UEChMZS1BOIENvcnBvcmF0ZSBNYXJrZXQgQi5WLjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazE9MDsGA1UEAxM0S1BOIENvcnBvcmF0ZSBNYXJrZXQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQ==">
+ <serialNumber>M9pDPXYgyiimYdML5Wg4zQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>eNYPiDzOMtQ=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOjA4BgNVBAMMMVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBQZXJzb29uIENBIC0gRzM=">
+ <serialNumber>XJI7ULS6xv8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xLTArBgNVBAMMJFN0YWF0IGRlciBOZWRlcmxhbmRlbiBCdXJnZXIgQ0EgLSBHMw==">
+ <serialNumber>SMwb3p7dSlA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGoxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xOzA5BgNVBAMMMlN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBTZXJ2aWNlcyBDQSAtIEcz">
+ <serialNumber>Q704nTrqxVY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MGExCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xMjAwBgNVBAMMKVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPcmdhbmlzYXRpZSBDQSAtIEcy">
+ <serialNumber>ATEz9w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>ARE=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>ARA=</serialNumber>
+ </certItem>
+ <certItem issuerName="MHExCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNEZXV0c2NoZSBUZWxla29tIEFHMR8wHQYDVQQLExZULVRlbGVTZWMgVHJ1c3QgQ2VudGVyMSMwIQYDVQQDExpEZXV0c2NoZSBUZWxla29tIFJvb3QgQ0EgMg==">
+ <serialNumber>AQ8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwOA==">
+ <serialNumber>AahE5mpsDY4=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>YfJj9o3IPBI=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>ThcGy6zgwpM=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjg=">
+ <serialNumber>HvAB3BwhY8g=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByekcA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>Byc3DA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>A7XRsyidfS8L2dlFuTsfGA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>C95Fob0ttu7S7dIXnjqiBA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>MyEj11s2KJH0vdnfUfuNIw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>HxapAqDOXuN5BHzREkFFtg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>SrterKaDHA8hZ+z9gwFXnw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGUxCzAJBgNVBAYTAkdSMRQwEgYDVQQKEwtBREFDT00gUy5BLjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEfMB0GA1UEAxMWQURBQ09NIENsYXNzIDIgQ0EgLSBHNA==">
+ <serialNumber>YfdCZWyRV1sSx5XxyoXKSQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>H3xY9eXX+IpIl9ixQf3lzQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGUxCzAJBgNVBAYTAkdSMRQwEgYDVQQKEwtBREFDT00gUy5BLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEfMB0GA1UEAxMWQURBQ09NIENsYXNzIDIgQ0EgLSBHMw==">
+ <serialNumber>e9fp8poJ4jDqpHxVc+7SoA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>Jje4sy72uF/upHdwh0gBGg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGAxCzAJBgNVBAYTAkdSMRMwEQYDVQQKEwpBTFBIQSBCQU5LMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMRswGQYDVQQDExJBTFBIQSBCQU5LIENBIC0gRzI=">
+ <serialNumber>SoA0BJz+EzihvsNlkwlJTg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGAxCzAJBgNVBAYTAkdSMRMwEQYDVQQKEwpBTFBIQSBCQU5LMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMRswGQYDVQQDExJBTFBIQSBCQU5LIENBIC0gRzI=">
+ <serialNumber>I5cyb4y1eoVQS44pO3PAww==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGUxCzAJBgNVBAYTAkdSMRQwEgYDVQQKEwtBREFDT00gUy5BLjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEfMB0GA1UEAxMWQURBQ09NIENsYXNzIDIgQ0EgLSBHNA==">
+ <serialNumber>USISWFWRHGp530VQc2S1/Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGUxCzAJBgNVBAYTAkdSMRQwEgYDVQQKEwtBREFDT00gUy5BLjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEfMB0GA1UEAxMWQURBQ09NIENsYXNzIDIgQ0EgLSBHNA==">
+ <serialNumber>IW5rxECQ5LEyRGPeZE91ug==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>BILpOL1LYav6JuQxlNetFA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByekbA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByekbQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MGAxCzAJBgNVBAYTAkdSMRMwEQYDVQQKEwpBTFBIQSBCQU5LMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMRswGQYDVQQDExJBTFBIQSBCQU5LIENBIC0gRzI=">
+ <serialNumber>aqcSP+AsWAmN9xWwAseOqg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>LiJxautXUrJeoN4q4RX/Rg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>cDosFiyvqdvDoYinOV6PDg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGBMQswCQYDVQQGEwJCUjEtMCsGA1UEChMkQ2VydGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBTLkEuMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSIwIAYDVQQDExlDZXJ0aXNpZ24gQ2xhc3MgMiBDQSAtIEcz">
+ <serialNumber>FdJweu3BTeU/YzTvayJksQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>PtgfuDU/40rxF87UP4HpIw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>BnG7G8DueagyBXomN87dYA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>JMRsU4iZEfeLdmXeFIjy4w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMw==">
+ <serialNumber>U4S9ZGx1FCY3wppgMwTn0Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFwxCzAJBgNVBAYTAlVTMRkwFwYDVQQKDBBWZXJpem9uIEJ1c2luZXNzMREwDwYDVQQLDAhPbW5pUm9vdDEfMB0GA1UEAwwWVmVyaXpvbiBHbG9iYWwgUm9vdCBDQQ==">
+ <serialNumber>Beo=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>TA7JGA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>RWua3A==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>TA7JMQ==</serialNumber>
+ </certItem>
+ <certItem issuerName="MEkxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxIzAhBgNVBAMTGlN3aXNzU2lnbiBQbGF0aW51bSBDQSAtIEcy">
+ <serialNumber>SUUmDL8PIBZ0EkIfCV6N</serialNumber>
+ </certItem>
+ <certItem issuerName="MEkxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxIzAhBgNVBAMTGlN3aXNzU2lnbiBQbGF0aW51bSBDQSAtIEcy">
+ <serialNumber>AISaJcSkfQq9</serialNumber>
+ </certItem>
+ <certItem issuerName="MDcxFDASBgNVBAoMC1RlbGlhU29uZXJhMR8wHQYDVQQDDBZUZWxpYVNvbmVyYSBSb290IENBIHYx">
+ <serialNumber>A3XEm35jzkM3B/8ZhDel7w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDcxFDASBgNVBAoMC1RlbGlhU29uZXJhMR8wHQYDVQQDDBZUZWxpYVNvbmVyYSBSb290IENBIHYx">
+ <serialNumber>ANdqi8UFCQChm0RchyjMpjY=</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGWMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDE8MDoGA1UEAxMzQ09NT0RPIFJTQSBPcmdhbml6YXRpb24gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB">
+ <serialNumber>AMN6iHtOgy68QBu3kXiaFc8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJSYXBpZFNTTCBTSEEyNTYgQ0E=">
+ <serialNumber>L41amoCH4B2agSUpD8Wd2A==</serialNumber>
+ </certItem>
+ <certItem issuerName="ME0xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIgU2VjdXJlIFNlcnZlciBDQQ==">
+ <serialNumber>CR8HWlsGr6Sdlw/mzOv8gA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=">
+ <serialNumber>W2qOjVqGcY8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=">
+ <serialNumber>W2qOjVqGcY8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREU=">
+ <serialNumber>W2qOjVqGcY8=</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeO7g==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfJkA==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByeO7w==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfJkg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfTSw==</serialNumber>
+ </certItem>
+ <certItem issuerName="MFoxCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxIjAgBgNVBAMTGUJhbHRpbW9yZSBDeWJlclRydXN0IFJvb3Q=">
+ <serialNumber>ByfTSg==</serialNumber>
+ </certItem>
+ <certItem issuerName="MIGwMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBFbnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHk=">
+ <serialNumber>Qks10R3Zqs4AAAAAUdNX8Q==</serialNumber>
+ </certItem>
+ <certItem issuerName="MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=">
+ <serialNumber>IrmxST2Fhyj5</serialNumber>
+ </certItem>
+ <certItem issuerName="MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=">
+ <serialNumber>IrmxST2Fhyj5</serialNumber>
+ </certItem>
+ <certItem issuerName="MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=">
+ <serialNumber>IrmxSxsaAPaT</serialNumber>
+ </certItem>
+ <certItem issuerName="MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=">
+ <serialNumber>IrmxTNGDGgYt</serialNumber>
+ </certItem>
+ <certItem issuerName="MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=">
+ <serialNumber>IrmxSjphH9DY</serialNumber>
+ </certItem>
+ <certItem issuerName="MF0xCzAJBgNVBAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTI=">
+ <serialNumber>IrmxSDcTuZO2</serialNumber>
+ </certItem>
+ </certItems>
+</blocklist>
diff --git a/comm/suite/app/icons/moz.build b/comm/suite/app/icons/moz.build
new file mode 100644
index 0000000000..652537f71d
--- /dev/null
+++ b/comm/suite/app/icons/moz.build
@@ -0,0 +1,21 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
+ icon_suffix = ".ico"
+ icon_dir = CONFIG["MOZ_WIDGET_TOOLKIT"]
+
+ # Windows icons
+ desktop_icons = [
+ "calendar-alarm-dialog",
+ "calendar-event-dialog",
+ "calendar-event-summary-dialog",
+ "calendar-task-dialog",
+ "calendar-task-summary-dialog",
+ ]
+
+ FINAL_TARGET_FILES.chrome.icons.default += [
+ "%s/%s%s" % (icon_dir, i, icon_suffix) for i in sorted(desktop_icons)
+ ]
diff --git a/comm/suite/app/icons/windows/calendar-alarm-dialog.ico b/comm/suite/app/icons/windows/calendar-alarm-dialog.ico
new file mode 100644
index 0000000000..198cd4d7b6
--- /dev/null
+++ b/comm/suite/app/icons/windows/calendar-alarm-dialog.ico
Binary files differ
diff --git a/comm/suite/app/icons/windows/calendar-event-dialog.ico b/comm/suite/app/icons/windows/calendar-event-dialog.ico
new file mode 100644
index 0000000000..b2a9a5909c
--- /dev/null
+++ b/comm/suite/app/icons/windows/calendar-event-dialog.ico
Binary files differ
diff --git a/comm/suite/app/icons/windows/calendar-event-summary-dialog.ico b/comm/suite/app/icons/windows/calendar-event-summary-dialog.ico
new file mode 100644
index 0000000000..883036a268
--- /dev/null
+++ b/comm/suite/app/icons/windows/calendar-event-summary-dialog.ico
Binary files differ
diff --git a/comm/suite/app/icons/windows/calendar-task-dialog.ico b/comm/suite/app/icons/windows/calendar-task-dialog.ico
new file mode 100644
index 0000000000..a80ae3d2e0
--- /dev/null
+++ b/comm/suite/app/icons/windows/calendar-task-dialog.ico
Binary files differ
diff --git a/comm/suite/app/icons/windows/calendar-task-summary-dialog.ico b/comm/suite/app/icons/windows/calendar-task-summary-dialog.ico
new file mode 100644
index 0000000000..3a26809c6c
--- /dev/null
+++ b/comm/suite/app/icons/windows/calendar-task-summary-dialog.ico
Binary files differ
diff --git a/comm/suite/app/macbuild/Contents/Info.plist.in b/comm/suite/app/macbuild/Contents/Info.plist.in
new file mode 100644
index 0000000000..6dfb9177d9
--- /dev/null
+++ b/comm/suite/app/macbuild/Contents/Info.plist.in
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleExecutable</key>
+ <string>@MOZ_APP_NAME@</string>
+ <key>CFBundleIdentifier</key>
+ <string>@MOZ_MACBUNDLE_ID@</string>
+ <key>CFBundleVersion</key>
+ <string>@MOZ_APP_VERSION@</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleName</key>
+ <string>@MOZ_APP_DISPLAYNAME@</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>MOZZ</string>
+ <key>CFBundleIconFile</key>
+ <string>seamonkey.icns</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@MOZ_APP_VERSION@</string>
+ <key>CFBundleGetInfoString</key>
+ <string>@MAC_APP_NAME@ @MOZ_APP_VERSION@</string>
+ <key>NSAppleScriptEnabled</key>
+ <true/>
+ <key>LSApplicationCategoryType</key>
+ <string>public.app-category.productivity</string>
+ <key>LSEnvironment</key>
+ <dict>
+ <key>MallocNanoZone</key>
+ <string>0</string>
+ <key>SYSTEM_VERSION_COMPAT</key>
+ <string>0</string>
+ </dict>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.12.0</string>
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+ <key>NSRequiresAquaSystemAppearance</key>
+ <true/>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>Text Document</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ <string>utxt</string>
+ </array>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>text</string>
+ <string>txt</string>
+ <string>css</string>
+ <string>xul</string>
+ <string>js</string>
+ <string>rdf</string>
+ <string>log</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>HTML Document</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>HTML</string>
+ </array>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>html</string>
+ <string>htm</string>
+ <string>shtml</string>
+ <string>xml</string>
+ <string>xht</string>
+ <string>xhtml</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>JSON File</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ </array>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>json</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>application/json</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>@MOZ_APP_DISPLAYNAME@ E-mail</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ <string>utxt</string>
+ </array>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>eml</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>Image Document</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>GIFf</string>
+ <string>JPEG</string>
+ <string>PNGf</string>
+ </array>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>jpeg</string>
+ <string>jpg</string>
+ <string>jpe</string>
+ <string>png</string>
+ <string>gif</string>
+ <string>bmp</string>
+ <string>ico</string>
+ <string>jfif</string>
+ <string>jng</string>
+ <string>ppm</string>
+ <string>ppm6</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>SVG document</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeOSTypes</key>
+ <array>
+ <string>TEXT</string>
+ </array>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>svg</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>image/svg+xml</string>
+ </array>
+ <key>NSDocumentClass</key>
+ <string>BrowserDocument</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>HTML5 Audio (Ogg)</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>oga</string>
+ <string>ogg</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>audio/ogg</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>HTML5 Video (Ogg)</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>ogv</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>video/ogg</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleTypeName</key>
+ <string>HTML5 Video (WebM)</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>webm</string>
+ </array>
+ <key>CFBundleTypeIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>video/webm</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleURLIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleURLName</key>
+ <string>http URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>http</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleURLIconFile</key>
+ <string>document.icns</string>
+ <key>CFBundleURLName</key>
+ <string>https URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>https</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>ftp URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>ftp</string>
+ </array>
+ </dict>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>file URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>file</string>
+ </array>
+ </dict>
+ </array>
+ <key>NSPrincipalClass</key>
+ <string>GeckoNSApplication</string>
+ <key>MozillaDeveloperRepoPath</key>
+ <string>@MOZ_DEVELOPER_REPO_PATH@</string>
+ <key>MozillaDeveloperObjPath</key>
+ <string>@MOZ_DEVELOPER_OBJ_PATH@</string>
+ <key>NSContactsUsageDescription</key>
+ <string>Use your macOS contacts in @MAC_APP_NAME@.</string>
+</dict>
+</plist>
diff --git a/comm/suite/app/macbuild/Contents/MacOS-files-copy.in b/comm/suite/app/macbuild/Contents/MacOS-files-copy.in
new file mode 100644
index 0000000000..e9d0f0efb9
--- /dev/null
+++ b/comm/suite/app/macbuild/Contents/MacOS-files-copy.in
@@ -0,0 +1,11 @@
+# Specifies files that should be copied (via deep copy, resolving symlinks)
+# from dist/bin to the .app/Contents/MacOS directory. Linking is preferred to
+# reduce disk I/O during builds, so just include dylibs which need to be in the
+# same directory as returned by dladddr(3).
+#
+# Some of these dylibs load other dylibs which are assumed to be siblings in
+# the same directory obtained from dladdr(3). With macOS 10.15, dladdr returns
+# absolute resolved paths which breaks this assumption if symlinks are used
+# because the symlink targets are in different directories. Hence the need for
+# them to be copied to the same directory.
+/*.dylib
diff --git a/comm/suite/app/macbuild/Contents/MacOS-files.in b/comm/suite/app/macbuild/Contents/MacOS-files.in
new file mode 100644
index 0000000000..127e6f10aa
--- /dev/null
+++ b/comm/suite/app/macbuild/Contents/MacOS-files.in
@@ -0,0 +1,8 @@
+/*.app/***
+/certutil
+/seamonkey-bin
+/pingsender
+/pk12util
+/ssltunnel
+/xpcshell
+/XUL
diff --git a/comm/suite/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in b/comm/suite/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
new file mode 100644
index 0000000000..b6c99af3e0
--- /dev/null
+++ b/comm/suite/app/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* Localized versions of Info.plist keys */
+
+CFBundleName = "@MAC_APP_NAME@";
diff --git a/comm/suite/app/module.ver b/comm/suite/app/module.ver
new file mode 100644
index 0000000000..b6eb11cda3
--- /dev/null
+++ b/comm/suite/app/module.ver
@@ -0,0 +1,8 @@
+WIN32_MODULE_COMPANYNAME=mozilla.org
+WIN32_MODULE_COPYRIGHT=©Mozilla Developers, according to the MPL 1.1/GPL 2.0/LGPL 2.1 licenses, as applicable.
+WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@
+WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@
+WIN32_MODULE_TRADEMARKS=SeaMonkey and Mozilla are trademarks of The Mozilla Foundation.
+WIN32_MODULE_DESCRIPTION=@MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@
+WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@
diff --git a/comm/suite/app/moz.build b/comm/suite/app/moz.build
new file mode 100644
index 0000000000..be99a23b09
--- /dev/null
+++ b/comm/suite/app/moz.build
@@ -0,0 +1,129 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ "icons",
+]
+
+GeckoProgram(CONFIG["MOZ_APP_NAME"])
+
+SOURCES += ["nsSuiteApp.cpp"]
+
+LOCAL_INCLUDES += [
+ "!/build",
+ "/ipc/contentproc/",
+ "/toolkit/xre",
+ "/xpcom/base",
+ "/xpcom/build",
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ RCINCLUDE = "splash.rc"
+ DEFINES["MOZ_SUITE"] = True
+
+if CONFIG["LIBFUZZER"]:
+ USE_LIBS += ["fuzzer"]
+ LOCAL_INCLUDES += [
+ "/tools/fuzzing/libfuzzer",
+ ]
+
+if CONFIG["CC_TYPE"] in ("msvc", "clang-cl"):
+ # Always enter a Windows program through wmain, whether or not we're
+ # a console application.
+ WIN32_EXE_LDFLAGS += ["-ENTRY:wmainCRTStartup"]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "Darwin":
+ USE_LIBS += [
+ "mozsandbox",
+ ]
+ OS_LIBS += [
+ "-framework CoreFoundation",
+ ]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "WINNT":
+ # For sandbox includes and the include dependencies those have
+ LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+ "/security/sandbox/chromium-shim",
+ ]
+
+ OS_LIBS += [
+ "version",
+ ]
+
+ USE_LIBS += [
+ "sandbox_s",
+ ]
+
+ OS_LIBS += [
+ "advapi32",
+ "winmm",
+ "user32",
+ ]
+
+ DELAYLOAD_DLLS += [
+ "winmm.dll",
+ "user32.dll",
+ ]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ OS_LIBS += [
+ "ntdll",
+ ]
+
+ if CONFIG["CC_TYPE"] == "msvc":
+ CFLAGS += ["-guard:cf"]
+ CXXFLAGS += ["-guard:cf"]
+ LDFLAGS += ["-guard:cf"]
+
+# Control the default heap size.
+# This is the heap returned by GetProcessHeap().
+# As we use the CRT heap, the default size is too large and wastes VM.
+#
+# The default heap size is 1MB on Win32.
+# The heap will grow if need be.
+#
+# Set it to 256k. See bug 127069.
+if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["CC_TYPE"] not in ("clang", "gcc"):
+ LDFLAGS += ["/HEAP:0x40000"]
+
+DisableStlWrapping()
+
+if CONFIG["MOZ_LINKER"]:
+ OS_LIBS += CONFIG["MOZ_ZLIB_LIBS"]
+
+if CONFIG["HAVE_CLOCK_MONOTONIC"]:
+ OS_LIBS += CONFIG["REALTIME_LIBS"]
+
+if CONFIG["MOZ_LINUX_32_SSE2_STARTUP_ERROR"]:
+ DEFINES["MOZ_LINUX_32_SSE2_STARTUP_ERROR"] = True
+ COMPILE_FLAGS["OS_CXXFLAGS"] = [
+ f
+ for f in COMPILE_FLAGS.get("OS_CXXFLAGS", [])
+ if not f.startswith("-march=") and f not in ("-msse", "-msse2", "-mfpmath=sse")
+ ] + [
+ "-mno-sse",
+ "-mno-sse2",
+ "-mfpmath=387",
+ ]
+
+# For splash.rc
+DEFINES["SEAMONKEY_ICO"] = '"%s/%s/icons/windows/main-window.ico"' % (
+ TOPSRCDIR,
+ CONFIG["MOZ_BRANDING_DIRECTORY"],
+)
+DEFINES["HTML_FILE_ICO"] = '"%s/%s/icons/windows/html-file.ico"' % (
+ TOPSRCDIR,
+ CONFIG["MOZ_BRANDING_DIRECTORY"],
+)
+
+DEFINES["APP_VERSION"] = CONFIG["MOZ_APP_VERSION"]
+
+JS_PREFERENCE_PP_FILES += [
+ "profile/suite-prefs.js",
+]
+
+FINAL_TARGET_FILES += ["blocklist.xml"]
+FINAL_TARGET_FILES.defaults += ["permissions"]
diff --git a/comm/suite/app/nsSuiteApp.cpp b/comm/suite/app/nsSuiteApp.cpp
new file mode 100644
index 0000000000..7d0775cad5
--- /dev/null
+++ b/comm/suite/app/nsSuiteApp.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsXULAppAPI.h"
+#include "nsXPCOM.h"
+#include "nsISupports.h"
+#include "mozilla/Logging.h"
+#include "mozilla/XREAppData.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Vector.h"
+#include "mozilla/TimeStamp.h"
+#include "XREChildData.h"
+#include "XREShellData.h"
+#include "application.ini.h"
+#include "mozilla/Bootstrap.h"
+#if defined(XP_WIN)
+#include <windows.h>
+#include <stdlib.h>
+#elif defined(XP_UNIX)
+#include <sys/resource.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+
+#ifdef XP_WIN
+#include "mozilla/WindowsDllBlocklist.h"
+#define XRE_WANT_ENVIRON
+#define strcasecmp _stricmp
+#ifdef MOZ_SANDBOX
+#include "mozilla/sandboxing/SandboxInitialization.h"
+#endif
+#endif
+#include "BinaryPath.h"
+
+#include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL
+
+#include "mozilla/Sprintf.h"
+#include "mozilla/StartupTimeline.h"
+
+#ifdef LIBFUZZER
+#include "FuzzerDefs.h"
+#endif
+
+#ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
+#include <cpuid.h>
+#include "mozilla/Unused.h"
+
+static bool
+IsSSE2Available()
+{
+ // The rest of the app has been compiled to assume that SSE2 is present
+ // unconditionally, so we can't use the normal copy of SSE.cpp here.
+ // Since SSE.cpp caches the results and we need them only transiently,
+ // instead of #including SSE.cpp here, let's just inline the specific check
+ // that's needed.
+ unsigned int level = 1u;
+ unsigned int eax, ebx, ecx, edx;
+ unsigned int bits = (1u<<26);
+ unsigned int max = __get_cpuid_max(0, nullptr);
+ if (level > max) {
+ return false;
+ }
+ __cpuid_count(level, 0, eax, ebx, ecx, edx);
+ return (edx & bits) == bits;
+}
+
+static const char sSSE2Message[] =
+ "This SeaMonkey version requires a processor with the SSE2 instruction "
+ "set extension.\nYou may be able to obtain a version that does not "
+ "require SSE2 from your Linux distribution.\n";
+
+__attribute__((constructor))
+static void
+SSE2Check()
+{
+ if (IsSSE2Available()) {
+ return;
+ }
+ // Using write() in order to avoid jemalloc-based buffering. Ignoring return
+ // values, since there isn't much we could do on failure and there is no
+ // point in trying to recover from errors.
+ MOZ_UNUSED(write(STDERR_FILENO,
+ sSSE2Message,
+ MOZ_ARRAY_LENGTH(sSSE2Message) - 1));
+ // _exit() instead of exit() to avoid running the usual "at exit" code.
+ _exit(255);
+}
+#endif
+
+#if !defined(MOZ_WIDGET_COCOA) && !defined(MOZ_WIDGET_ANDROID)
+#define MOZ_BROWSER_CAN_BE_CONTENTPROC
+#include "plugin-container.cpp"
+#endif
+
+using namespace mozilla;
+
+#ifdef XP_MACOSX
+#define kOSXResourcesFolder "Resources"
+#endif
+#define kDesktopFolder ""
+
+static MOZ_FORMAT_PRINTF(1, 2) void Output(const char *fmt, ... )
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+#ifndef XP_WIN
+ vfprintf(stderr, fmt, ap);
+#else
+ char msg[2048];
+ vsnprintf_s(msg, _countof(msg), _TRUNCATE, fmt, ap);
+
+ wchar_t wide_msg[2048];
+ MultiByteToWideChar(CP_UTF8,
+ 0,
+ msg,
+ -1,
+ wide_msg,
+ _countof(wide_msg));
+#if MOZ_WINCONSOLE
+ fwprintf_s(stderr, wide_msg);
+#else
+ // Linking user32 at load-time interferes with the DLL blocklist (bug 932100).
+ // This is a rare codepath, so we can load user32 at run-time instead.
+ HMODULE user32 = LoadLibraryW(L"user32.dll");
+ if (user32) {
+ decltype(MessageBoxW)* messageBoxW =
+ (decltype(MessageBoxW)*) GetProcAddress(user32, "MessageBoxW");
+ if (messageBoxW) {
+ messageBoxW(nullptr, wide_msg, L"XULRunner", MB_OK
+ | MB_ICONERROR
+ | MB_SETFOREGROUND);
+ }
+ FreeLibrary(user32);
+ }
+#endif
+#endif
+
+ va_end(ap);
+}
+
+/**
+ * Return true if |arg| matches the given argument name.
+ */
+static bool IsArg(const char* arg, const char* s)
+{
+ if (*arg == '-')
+ {
+ if (*++arg == '-')
+ ++arg;
+ return !strcasecmp(arg, s);
+ }
+
+#if defined(XP_WIN)
+ if (*arg == '/')
+ return !strcasecmp(++arg, s);
+#endif
+
+ return false;
+}
+
+Bootstrap::UniquePtr gBootstrap;
+
+static int do_main(int argc, char* argv[], char* envp[])
+{
+ // Allow seamonkey.exe to launch XULRunner apps via -app <application.ini>
+ // Note that -app must be the *first* argument.
+ const char *appDataFile = getenv("XUL_APP_FILE");
+ if ((!appDataFile || !*appDataFile) &&
+ (argc > 1 && IsArg(argv[1], "app"))) {
+ if (argc == 2) {
+ Output("Incorrect number of arguments passed to -app");
+ return 255;
+ }
+ appDataFile = argv[2];
+
+ char appEnv[MAXPATHLEN];
+ SprintfLiteral(appEnv, "XUL_APP_FILE=%s", argv[2]);
+ // Bug 1271574 Purposefully leak the XUL_APP_FILE string passed to putenv.
+ if (putenv(strdup(appEnv))) {
+ Output("Couldn't set %s.\n", appEnv);
+ return 255;
+ }
+ argv[2] = argv[0];
+ argv += 2;
+ argc -= 2;
+ } else if (argc > 1 && IsArg(argv[1], "xpcshell")) {
+ for (int i = 1; i < argc; i++) {
+ argv[i] = argv[i + 1];
+ }
+
+ XREShellData shellData;
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ shellData.sandboxBrokerServices =
+ sandboxing::GetInitializedBrokerServices();
+#endif
+
+ return gBootstrap->XRE_XPCShellMain(--argc, argv, envp, &shellData);
+ }
+
+ BootstrapConfig config;
+
+ if (appDataFile && *appDataFile) {
+ config.appData = nullptr;
+ config.appDataPath = appDataFile;
+ } else {
+ // no -app flag so we use the compiled-in app data
+ config.appData = &sAppData;
+ config.appDataPath = kDesktopFolder;
+ }
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ sandbox::BrokerServices* brokerServices =
+ sandboxing::GetInitializedBrokerServices();
+#if defined(MOZ_CONTENT_SANDBOX)
+ if (!brokerServices) {
+ Output("Couldn't initialize the broker services.\n");
+ return 255;
+ }
+#endif
+ config.sandboxBrokerServices = brokerServices;
+#endif
+
+#ifdef LIBFUZZER
+ if (getenv("FUZZER"))
+ gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver);
+#endif
+
+ return gBootstrap->XRE_main(argc, argv, config);
+}
+
+static nsresult
+InitXPCOMGlue(LibLoadingStrategy aLibLoadingStrategy)
+{
+ UniqueFreePtr<char> exePath = BinaryPath::Get();
+ if (!exePath) {
+ Output("Couldn't find the application directory.\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ auto bootstrapResult =
+ mozilla::GetBootstrap(exePath.get(), aLibLoadingStrategy);
+ if (bootstrapResult.isErr()) {
+ Output("Couldn't load XPCOM.\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ gBootstrap = bootstrapResult.unwrap();
+
+ // This will set this thread as the main thread.
+ gBootstrap->NS_LogInit();
+
+ return NS_OK;
+}
+
+#ifdef HAS_DLL_BLOCKLIST
+// NB: This must be extern, as this value is checked elsewhere
+uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault;
+#endif
+
+int main(int argc, char* argv[], char* envp[])
+{
+ mozilla::TimeStamp start = mozilla::TimeStamp::Now();
+
+#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
+ // We are launching as a content process, delegate to the appropriate
+ // main
+ if (argc > 1 && IsArg(argv[1], "contentproc")) {
+#ifdef HAS_DLL_BLOCKLIST
+ DllBlocklist_Initialize(eDllBlocklistInitFlagIsChildProcess);
+#endif
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ // We need to initialize the sandbox TargetServices before InitXPCOMGlue
+ // because we might need the sandbox broker to give access to some files.
+ if (IsSandboxedProcess() && !sandboxing::GetInitializedTargetServices()) {
+ Output("Failed to initialize the sandbox target services.");
+ return 255;
+ }
+#endif
+
+ nsresult rv = InitXPCOMGlue(LibLoadingStrategy::NoReadAhead);
+ if (NS_FAILED(rv)) {
+ return 255;
+ }
+
+ int result = content_process_main(gBootstrap.get(), argc, argv);
+
+ // InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
+ gBootstrap->NS_LogTerm();
+
+ return result;
+ }
+#endif
+
+#ifdef HAS_DLL_BLOCKLIST
+ DllBlocklist_Initialize(gBlocklistInitFlags);
+#endif
+
+ nsresult rv = InitXPCOMGlue(LibLoadingStrategy::ReadAhead);
+ if (NS_FAILED(rv)) {
+ return 255;
+ }
+
+ gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
+
+#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
+ gBootstrap->XRE_EnableSameExecutableForContentProc();
+#endif
+
+ int result = do_main(argc, argv, envp);
+
+ gBootstrap->NS_LogTerm();
+
+#ifdef XP_MACOSX
+ // Allow writes again. While we would like to catch writes from static
+ // destructors to allow early exits to use _exit, we know that there is
+ // at least one such write that we don't control (see bug 826029). For
+ // now we enable writes again and early exits will have to use exit instead
+ // of _exit.
+ gBootstrap->XRE_StopLateWriteChecks();
+#endif
+
+ gBootstrap.reset();
+
+ return result;
+}
diff --git a/comm/suite/app/permissions b/comm/suite/app/permissions
new file mode 100644
index 0000000000..038695a412
--- /dev/null
+++ b/comm/suite/app/permissions
@@ -0,0 +1,10 @@
+# This file has default permissions for the permission manager.
+# The file-format is strict:
+# * matchtype \t type \t permission \t host
+# * Only "host" is supported for matchtype
+# * type is a string that identifies the type of permission (e.g. "cookie")
+# * permission is an integer between 1 and 15
+# See nsPermissionManager.cpp for more...
+
+# XPInstall
+origin install 1 https://addons.thunderbird.net
diff --git a/comm/suite/app/profile/channel-prefs.js b/comm/suite/app/profile/channel-prefs.js
new file mode 100644
index 0000000000..633c489f3c
--- /dev/null
+++ b/comm/suite/app/profile/channel-prefs.js
@@ -0,0 +1,5 @@
+/* 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/. */
+
+pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
diff --git a/comm/suite/app/profile/suite-prefs.js b/comm/suite/app/profile/suite-prefs.js
new file mode 100644
index 0000000000..e59f3a382b
--- /dev/null
+++ b/comm/suite/app/profile/suite-prefs.js
@@ -0,0 +1,1500 @@
+/* -*- Mode: javascript; 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/. */
+
+/* The prefs in this file are specific to the SeaMonkey browser.
+ * Generic default prefs are in (mozilla/)modules/libpref/src/init/all.js
+ */
+
+/* filter substitution
+ *
+ * SYNTAX HINTS:
+ *
+ * - Dashes are delimiters; use underscores instead.
+ * - The first character after a period must be alphabetic.
+ * - Computed values (e.g. 50 * 1024) don't work.
+ */
+
+pref("startup.homepage_override_url","chrome://navigator-region/locale/region.properties");
+pref("general.skins.selectedSkin", "classic/1.0");
+
+pref("browser.chromeURL","chrome://navigator/content/navigator.xul");
+pref("browser.hiddenWindowChromeURL", "chrome://navigator/content/hiddenWindow.xul");
+
+// prompt for Master Password on startup
+pref("signon.startup.prompt", true);
+
+pref("general.startup.browser", true);
+pref("general.startup.mail", false);
+pref("general.startup.news", false);
+pref("general.startup.editor", false);
+pref("general.startup.compose", false);
+pref("general.startup.addressbook", false);
+
+pref("general.open_location.last_url", "");
+pref("general.open_location.last_window_choice", 0);
+pref("browser.urlbar.historyEnabled", true);
+
+pref("general.smoothScroll", false);
+pref("general.autoScroll", true);
+
+pref("general.useragent.compatMode.firefox", true);
+// See bug 1242294 for how to make this work.
+pref("general.useragent.compatMode.strict-firefox", false);
+
+// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
+pref("browser.startup.page", 1);
+pref("browser.startup.homepage", "chrome://navigator-region/locale/region.properties");
+pref("browser.startup.homepage.count", 1);
+
+pref("browser.warnOnQuit", true);
+pref("browser.warnOnRestart", true);
+
+// disable this until it can be disabled on a per-docshell basis (see bug 319368)
+pref("browser.send_pings", false);
+
+pref("browser.chrome.site_icons", true);
+pref("browser.chrome.favicons", true);
+
+// 0 = Pictures Only, 1 = Text Only, 2 = Pictures and Text
+pref("browser.chrome.toolbar_style", 2);
+
+pref("browser.download.finished_download_sound", false);
+pref("browser.download.finished_sound_url", "");
+pref("browser.download.useDownloadDir", false);
+pref("browser.download.folderList", 1);
+
+pref("browser.download.manager.addToRecentDocs", true);
+pref("browser.download.manager.resumeOnWakeDelay", 10000);
+pref("browser.download.manager.flashCount", 2);
+pref("browser.download.manager.focusWhenStarting", false);
+pref("browser.download.progress.closeWhenDone", false);
+
+// Number of milliseconds to wait for the http headers (and thus
+// the Content-Disposition filename) before giving up and falling back to
+// picking a filename without that info in hand so that the user sees some
+// feedback from their action.
+pref("browser.download.saveLinkAsFilenameTimeout", 4000);
+
+// Output console.log/info/warn/error to the Error Console
+pref("browser.dom.window.console.enabled", false);
+
+// Use the findbar instead of the dialog box
+pref("browser.findbar.enabled", true);
+
+// Use doorhanger notifications instead of the notification bar
+pref("browser.doorhanger.enabled", true);
+
+// search engines URL
+pref("browser.search.searchEnginesURL", "https://addons.thunderbird.net/%LOCALE%/%APP%/search/?atype=4");
+
+// Disable logging for the search service by default.
+pref("browser.search.log", false);
+
+// Search (side)bar results always open in a new tab.
+pref("browser.search.openintab", false);
+
+// Open context search results in either a new window or tab.
+pref("browser.search.opentabforcontextsearch", true);
+
+// Send ping to the server to update.
+pref("browser.search.update", true);
+
+// Disable logging for the search service update system by default.
+pref("browser.search.update.log", false);
+
+// Check whether we need to perform engine updates every 6 hours
+pref("browser.search.update.interval", 21600);
+
+// enable search suggestions by default
+pref("browser.search.suggest.enabled", true);
+
+// Smart Browsing prefs
+pref("keyword.enabled", true);
+// Override the default keyword.URL. Empty value means
+// "use the search service's default engine"
+pref("keyword.URL", "");
+
+pref("browser.urlbar.autocomplete.enabled", true);
+pref("browser.urlbar.formatting.enabled", true);
+pref("browser.urlbar.highlight.secure", true);
+pref("browser.urlbar.clickSelectsAll", true);
+// when clickSelectsAll=true, does it also apply when the click is past end of text?
+pref("browser.urlbar.clickAtEndSelects", true);
+
+pref("browser.urlbar.autoFill", false);
+pref("browser.urlbar.showPopup", true);
+pref("browser.urlbar.showSearch", true);
+// 0: Match anywhere (e.g., middle of words)
+// 1: Match on word boundaries and then try matching anywhere
+// 2: Match only on word boundaries (e.g., after / or .)
+// 3: Match at the beginning of the url or title
+pref("browser.urlbar.matchBehavior", 1);
+
+pref("browser.urlbar.suggest.history", true);
+pref("browser.urlbar.suggest.bookmark", false);
+// SeaMonkey doesn't support this.
+pref("browser.urlbar.suggest.openpage", false);
+
+pref("browser.urlbar.suggest.history.onlyTyped", false);
+
+pref("browser.urlbar.filter.javascript", true);
+
+// Size of "chunks" affects the number of places to process between each search
+// timeout (ms). Too big and the UI will be unresponsive; too small and we'll
+// be waiting on the timeout too often without many results.
+pref("browser.urlbar.search.chunkSize", 1000);
+pref("browser.urlbar.search.timeout", 100);
+
+// The special characters below can be typed into the urlbar to either restrict
+// the search to visited history, bookmarked, tagged pages; or force a match on
+// just the title text or url.
+pref("browser.urlbar.restrict.history", "^");
+pref("browser.urlbar.restrict.bookmark", "*");
+pref("browser.urlbar.restrict.tag", "+");
+pref("browser.urlbar.restrict.openpage", "%");
+pref("browser.urlbar.restrict.typed", "~");
+pref("browser.urlbar.match.title", "#");
+pref("browser.urlbar.match.url", "@");
+
+pref("browser.history.last_page_visited", "about:blank");
+pref("browser.history.grouping", "day");
+pref("browser.sessionhistory.max_entries", 50);
+
+// Whether history is enabled or not.
+pref("places.history.enabled", true);
+
+// the (maximum) number of the recent visits to sample
+// when calculating frecency
+pref("places.frecency.numVisits", 10);
+
+// buckets (in days) for frecency calculation
+pref("places.frecency.firstBucketCutoff", 4);
+pref("places.frecency.secondBucketCutoff", 14);
+pref("places.frecency.thirdBucketCutoff", 31);
+pref("places.frecency.fourthBucketCutoff", 90);
+
+// weights for buckets for frecency calculations
+pref("places.frecency.firstBucketWeight", 100);
+pref("places.frecency.secondBucketWeight", 70);
+pref("places.frecency.thirdBucketWeight", 50);
+pref("places.frecency.fourthBucketWeight", 30);
+pref("places.frecency.defaultBucketWeight", 10);
+
+// bonus (in percent) for visit transition types for frecency calculations
+pref("places.frecency.embedVisitBonus", 0);
+pref("places.frecency.framedLinkVisitBonus", 0);
+pref("places.frecency.linkVisitBonus", 100);
+pref("places.frecency.typedVisitBonus", 2000);
+pref("places.frecency.bookmarkVisitBonus", 75);
+pref("places.frecency.downloadVisitBonus", 0);
+pref("places.frecency.permRedirectVisitBonus", 0);
+pref("places.frecency.tempRedirectVisitBonus", 0);
+pref("places.frecency.defaultVisitBonus", 0);
+
+// bonus (in percent) for place types for frecency calculations
+pref("places.frecency.unvisitedBookmarkBonus", 140);
+pref("places.frecency.unvisitedTypedBonus", 200);
+
+// By default, do not export HTML at shutdown.
+// If true, at shutdown the bookmarks in your menu and toolbar will
+// be exported as HTML to the bookmarks.html file.
+pref("browser.bookmarks.autoExportHTML", false);
+
+// The maximum number of daily bookmark backups to
+// keep in {PROFILEDIR}/bookmarkbackups. Special values:
+// -1: unlimited
+// 0: no backups created (and deletes all existing backups)
+pref("browser.bookmarks.max_backups", 10);
+
+// Don't try to alter this pref. It will be reset the next time you use the
+// bookmarking dialog.
+pref("browser.bookmarks.editDialog.firstEditField", "namePicker");
+
+// Tabbed browser
+pref("browser.tabs.loadDivertedInBackground", false);
+pref("browser.tabs.loadInBackground", true);
+pref("browser.tabs.opentabfor.middleclick", true);
+pref("browser.tabs.opentabfor.urlbar", true);
+pref("browser.tabs.tooltippreview.enable", true);
+pref("browser.tabs.tooltippreview.width", 300);
+pref("browser.tabs.autoHide", false);
+pref("browser.tabs.forceHide", false);
+pref("browser.tabs.closeWindowWithLastTab", true);
+pref("browser.tabs.warnOnClose", true);
+pref("browser.tabs.warnOnCloseOther", true);
+pref("browser.tabs.warnOnOpen", true);
+pref("browser.tabs.maxOpenBeforeWarn", 15);
+pref("browser.tabs.insertRelatedAfterCurrent", true);
+pref("browser.tabs.insertAllTabsAfterCurrent", false);
+pref("browser.tabs.selectOwnerOnClose", true);
+pref("browser.tabs.avoidBrowserFocus", false);
+
+// No e10s in SeaMonkey for now.
+pref("browser.tabs.remote.autostart", false);
+
+// how many browsers can be saved in the DOM (by the tabbed browser)
+pref("browser.tabs.max_tabs_undo", 3);
+// should popups by saved in the DOM (by the tabbed browser)
+pref("browser.tabs.cache_popups", false);
+
+// tab width and clipping
+pref("browser.tabs.tabMinWidth", 100);
+pref("browser.tabs.tabMaxWidth", 250);
+pref("browser.tabs.tabClipWidth", 140);
+
+// Where to show tab close buttons:
+// 0 on active tab only
+// 1 on all tabs until tabClipWidth is reached, then active tab only
+// 2 no close buttons at all
+// 3 at the end of the tabstrip
+pref("browser.tabs.closeButtons", 3);
+
+// Mouse wheel action when over the tab bar:
+// false The mouse wheel scrolls the whole tab bar like Firefox (default).
+// true The mouse wheel advances the selected tab.
+pref("browser.tabs.mouseScrollAdvancesTab", false);
+
+// lets new tab/window load something different than first window
+// -1 - use navigator startup preference
+// 0 - loads blank page
+// 1 - loads home page
+// 2 - loads last page visited
+pref("browser.tabs.loadOnNewTab", 0);
+pref("browser.windows.loadOnNewWindow", 1);
+
+// external link handling in tabbed browsers. values from nsIBrowserDOMWindow.
+// 0=default window, 1=current window/tab, 2=new window, 3=new tab in most recent window
+pref("browser.link.open_external", 3);
+// internal links handling in tabbed browsers. see .open_external for values.
+pref("browser.link.open_newwindow", 3);
+
+// 0: no restrictions - divert everything
+// 1: don't divert window.open at all
+// 2: don't divert window.open with features
+pref("browser.link.open_newwindow.restriction", 2);
+
+// Translation service
+pref("browser.translation.service", "chrome://navigator-region/locale/region.properties");
+pref("browser.translation.serviceDomain", "chrome://navigator-region/locale/region.properties");
+pref("browser.validate.html.service", "chrome://navigator-region/locale/region.properties");
+
+// 0 goes back
+// 1 act like pgup
+// 2 and other values, nothing
+pref("browser.backspace_action", 0);
+
+// Controls behavior of the "Add Exception" dialog launched from SSL error pages:
+// 0 - don't pre-populate anything.
+// 1 - pre-populate site URL, but don't fetch certificate.
+// 2 - pre-populate site URL and pre-fetch certificate.
+pref("browser.ssl_override_behavior", 2);
+
+// if true, use full page zoom instead of text zoom
+pref("browser.zoom.full", true);
+
+// Whether or not to save and restore zoom levels on a per-site basis.
+pref("browser.zoom.siteSpecific", true);
+
+// Whether or not to update background tabs to the current zoom level
+// once they come to the foreground (i.e. get activated).
+pref("browser.zoom.updateBackgroundTabs", true);
+
+// Whether to show the zoom status and controls in status panel
+pref("browser.zoom.showZoomStatusPanel", false);
+
+// Zoom levels for View > Zoom and Ctrl +/- keyboard shortcuts
+pref("toolkit.zoomManager.zoomValues", "0.2,0.3,0.5,0.67,0.8,0.9,1,1.1,1.2,1.33,1.5,1.7,2,2.4,3,4,5,6,7,8");
+
+pref("javascript.options.showInConsole", true);
+
+pref("suite.manager.addons.openAsDialog", false);
+pref("suite.manager.dataman.openAsDialog", true);
+
+pref("offline.startup_state", 0);
+pref("offline.send.unsent_messages", 0);
+pref("offline.download.download_messages", 0);
+
+// allow offline web apps to store data but ask for permission by default
+pref("offline-apps.allow_by_default", false);
+pref("browser.offline-apps.notify", true);
+
+pref("browser.formfill.expire_days", 180);
+
+// Handle mail/news URLs internally by default...
+pref("network.protocol-handler.external.mailto", false); // for mail
+pref("network.protocol-handler.external.news", false); // for news
+pref("network.protocol-handler.external.snews", false); // for secure news
+pref("network.protocol-handler.external.nntp", false); // also news
+
+// ...but still show the dialog at least the first time if switched to external
+pref("network.protocol-handler.warn-external.mailto", true);
+pref("network.protocol-handler.warn-external.news", true);
+pref("network.protocol-handler.warn-external.snews", true);
+pref("network.protocol-handler.warn-external.nntp", true);
+
+// bug 1005566 - Disable seer until properly supported
+// bug 1021370 - Rename Seer to Predictor
+pref("network.predictor.enabled", false);
+
+// To allow images to be inserted into a composition with an auth prompt, we
+// need the following two.
+pref("network.auth.subresource-img-cross-origin-http-auth-allow", true);
+// This pref is also needed for showing the caldav auth prompt.
+pref("network.auth.non-web-content-triggered-resources-http-auth-allow", true);
+
+pref("mail.biff.show_new_alert", true);
+
+// If the mail tab bar is automatically hidden when there is only one tab.
+pref("mail.tabs.autoHide", false);
+
+// If messages or folders are opened using the context menu or a middle click,
+// should we open them in the foreground or in the background?
+pref("mail.tabs.loadInBackground", true);
+
+// If messages are opened in a tab or a window on a double-click.
+pref("mail.tabs.opentabfor.doubleclick", false);
+
+// If messages or folders are opened in a tab or a window on a middle-click (or
+// equivalent).
+pref("mail.tabs.opentabfor.middleclick", true);
+
+// Force the unit shown for the size of all folders.
+// If empty, the unit is determined automatically for each folder.
+// Allowed values: KB/MB/<empty string>
+pref("mail.folderpane.sizeUnits", "");
+// Summarize messages count and size of subfolders into a collapsed parent?
+// Allowed values: true/false
+pref("mail.folderpane.sumSubfolders", true);
+
+pref("mailnews.reuse_thread_window2", true);
+
+pref("mailnews.ui.deleteMarksRead", true);
+pref("mailnews.ui.deleteAlwaysSelectedMessages", false);
+
+// The maximum amount of decoded image data we'll willingly keep around (we
+// might keep around more than this, but we'll try to get down to this value).
+// (This is intentionally on the high side; see bugs 746055 and 768015.)
+pref("image.mem.max_decoded_image_kb", 256000);
+
+pref("spellchecker.dictionary", "");
+pref("spellchecker.dictionaries.download.url", "https://addons.thunderbird.net/%LOCALE%/%APP%/dictionaries");
+
+// this will automatically enable inline spellchecking (if it is available) for
+// editable elements in HTML
+// 0 = spellcheck nothing
+// 1 = check multi-line controls [default]
+// 2 = check multi/single line controls
+pref("layout.spellcheckDefault", 1);
+
+// Blocks auto refresh if true
+pref("accessibility.blockautorefresh", false);
+
+// special TypeAheadFind settings
+
+// Use the findbar for type ahead find, instead of the XPFE implementation
+pref("accessibility.typeaheadfind.usefindbar", true);
+pref("accessibility.typeaheadfind.flashBar", 0);
+#ifndef XP_UNIX
+pref("accessibility.typeaheadfind.soundURL", "default");
+#endif
+
+#ifdef XP_WIN
+pref("browser.preferences.instantApply", false);
+#else
+pref("browser.preferences.instantApply", true);
+#endif
+#ifdef XP_MACOSX
+pref("browser.preferences.animateFadeIn", true);
+#else
+pref("browser.preferences.animateFadeIn", false);
+#endif
+
+// initial web feed readers list - add enough entries for locales to add theirs
+pref("browser.contentHandlers.types.0.title", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.0.uri", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.0.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.1.title", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.1.uri", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.1.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.2.title", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.2.uri", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.2.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.3.title", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.3.uri", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.3.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.4.title", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.4.uri", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.4.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.5.title", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.5.uri", "chrome://navigator-region/locale/region.properties");
+pref("browser.contentHandlers.types.5.type", "application/vnd.mozilla.maybe.feed");
+
+pref("browser.feeds.handler", "ask");
+pref("browser.videoFeeds.handler", "ask");
+pref("browser.audioFeeds.handler", "ask");
+
+// Overriding defaults defined in all.js (no UI yet covering these cases)
+pref("browser.safebrowsing.downloads.enabled", false);
+pref("browser.safebrowsing.downloads.remote.enabled", false);
+
+pref("urlclassifier.phishTable", "googpub-phish-proto,test-phish-simple");
+
+// Overriding defaults defined in all.js (use full version 2.x, bypassing bug 1077874) (Legacy)
+pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_SAFEBROWSING_API_KEY%");
+pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+pref("browser.safebrowsing.provider.mozilla.updateURL", "https://shavar.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://shavar.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+
+// The following prefs are for testing safebrowsing reporting only. Do not activate them in regular builds.
+// pref("browser.safebrowsing.provider.test.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
+// pref("browser.safebrowsing.provider.test.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
+
+//Theoretically the "client ID" sent in updates should be appinfo.name but
+//anything except "Firefox" or "navclient-auto-ffox" will cause safebrowsing
+//updates to fail. So we pretend to be Firefox here.
+pref("browser.safebrowsing.id", "navclient-auto-ffox");
+
+// Those are only used in our utilityOverlay.js (see bug 1270168)
+pref("browser.safebrowsing.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/phishing-protection/");
+
+pref("browser.sessionstore.resume_from_crash", true);
+pref("browser.sessionstore.resume_session_once", false);
+
+// minimal interval between two save operations in milliseconds
+pref("browser.sessionstore.interval", 15000);
+// maximum amount of POSTDATA to be saved in bytes per history entry (-1 = all of it)
+// (NB: POSTDATA will be saved either entirely or not at all)
+pref("browser.sessionstore.postdata", 0);
+// on which sites to save text data, POSTDATA and cookies
+// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
+pref("browser.sessionstore.privacy_level", 0);
+// the same as browser.sessionstore.privacy_level, but for saving deferred session data
+pref("browser.sessionstore.privacy_level_deferred", 2);
+// number of crashes that can occur before the about:sessionrestore page is displayed
+// (this pref has no effect if more than 6 hours have passed since the last crash)
+pref("browser.sessionstore.max_resumed_crashes", 1);
+// how many tabs can be reopened (per window)
+pref("browser.sessionstore.max_tabs_undo", 10);
+// how many windows can be reopened (per session) - on non-OS X platforms this
+// pref may be ignored when dealing with pop-up windows to ensure proper startup
+pref("browser.sessionstore.max_windows_undo", 3);
+// The number of tabs that can restore concurrently:
+// < 0 = All tabs can restore at the same time
+// 0 = Only the selected tab in each window will load.
+// N = N tabs should restore at the same time
+pref("browser.sessionstore.max_concurrent_tabs", 3);
+
+pref("shell.checkDefaultClient", true);
+// We want to check if we are the default client for browser and mail. See
+// suite/shell/public/nsIShellService.idl for the possible constants you can use
+pref("shell.checkDefaultApps", 3);
+
+pref("app.releaseNotesURL", "chrome://branding/locale/brand.properties");
+pref("app.troubleshootingURL", "chrome://branding/locale/brand.properties");
+pref("app.vendorURL", "chrome://branding/locale/brand.properties");
+
+// Base URL for web-based support pages.
+pref("app.support.baseURL", "chrome://branding/locale/brand.properties");
+
+// App-specific update preferences
+
+// Whether or not app updates are enabled
+pref("app.update.enabled", true);
+
+// This preference allows automatic download and install to take place.
+pref("app.update.auto", true);
+
+// If set to true, the Update Service will present no UI for any event.
+pref("app.update.silent", false);
+
+// Update service URL:
+pref("app.update.url", "https://updates.seamonkey-project.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
+// URL user can browse to manually if for some reason all update installation
+// attempts fail.
+pref("app.update.url.manual", "chrome://branding/locale/brand.properties");
+// A default value for the "More information about this update" link
+// supplied in the "An update is available" page of the update wizard.
+pref("app.update.url.details", "chrome://branding/locale/brand.properties");
+
+// User-settable override to app.update.url for testing purposes.
+//pref("app.update.url.override", "");
+
+// Enables some extra Application Update Logging (can reduce performance)
+pref("app.update.log", false);
+
+// The number of general background check failures to allow before notifying the
+// user of the failure. User initiated update checks always notify the user of
+// the failure.
+pref("app.update.backgroundMaxErrors", 10);
+
+// When |app.update.cert.requireBuiltIn| is true or not specified the
+// final certificate and all certificates the connection is redirected to before
+// the final certificate for the url specified in the |app.update.url|
+// preference must be built-in.
+pref("app.update.cert.requireBuiltIn", true);
+
+// When |app.update.cert.checkAttributes| is true or not specified the
+// certificate attributes specified in the |app.update.certs.| preference branch
+// are checked against the certificate for the url specified by the
+// |app.update.url| preference.
+pref("app.update.cert.checkAttributes", true);
+
+// The number of certificate attribute check failures to allow for background
+// update checks before notifying the user of the failure. User initiated update
+// checks always notify the user of the certificate attribute check failure.
+pref("app.update.cert.maxErrors", 5);
+
+// The |app.update.certs.| preference branch contains branches that are
+// sequentially numbered starting at 1 that contain attribute name / value
+// pairs for the certificate used by the server that hosts the update xml file
+// as specified in the |app.update.url| preference. When these preferences are
+// present the following conditions apply for a successful update check:
+// 1. the uri scheme must be https
+// 2. the preference name must exist as an attribute name on the certificate and
+// the value for the name must be the same as the value for the attribute
+// name on the certificate.
+// If these conditions aren't met it will be treated the same as when there is
+// no update available. This validation will not be performed when using the
+// |app.update.url.override| preference for update checking.
+pref("app.update.certs.1.issuerName", "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US");
+pref("app.update.certs.1.commonName", "updates.seamonkey-project.org");
+
+// Interval: Time between checks for a new version (in seconds)
+// default=1 day
+pref("app.update.interval", 86400);
+// The minimum delay in seconds for the timer to fire.
+// default=2 minutes
+pref("app.update.timerMinimumDelay", 120);
+#ifdef RELEASE_OR_BETA
+// Give the user x seconds to react before showing the big UI. default=8 days
+pref("app.update.promptWaitTime", 691200);
+#else
+// For nightly builds, before showing the big UI, default=12 hrs
+pref("app.update.promptWaitTime", 43200);
+#endif
+// Show the Update Checking/Ready UI when the user was idle for x seconds
+pref("app.update.idletime", 60);
+
+// Extension preferences
+
+// Controls enabling of the extension system logging (can reduce performance)
+pref("extensions.logging.enabled", false);
+
+// Strict compatibility makes add-ons incompatible by default.
+pref("extensions.strictCompatibility", false);
+
+// Disable add-ons installed into the shared user and shared system areas by
+// default. See the SCOPE constants in AddonManager.jsm for values to use here.
+pref("extensions.autoDisableScopes", 11);
+
+// Enable add-ons installed and owned by the application, like the default theme.
+pref("extensions.startupScanScopes", 4);
+
+// If true, unprivileged extensions may use experimental APIs on
+// nightly and developer edition.
+pref("extensions.experiments.enabled", true);
+
+// Extensions that should not be flagged as legacy in about:addons
+// {972ce4c6-7e08-4474-a285-3208198ce6fd} default theme
+// {59c81df5-4b7a-477b-912d-4e0fdf64e5f2} chatZilla
+// {e2fda1a4-762b-4020-b5ad-a41df1933103} calendar
+pref("extensions.legacy.exceptions", "{972ce4c6-7e08-4474-a285-3208198ce6fd},debugQA@mozilla.org,modern@themes.mozilla.org,{59c81df5-4b7a-477b-912d-4e0fdf64e5f2},{e2fda1a4-762b-4020-b5ad-a41df1933103}");
+
+// Preferences for AMO integration
+pref("extensions.getAddons.cache.enabled", true); // This also toggles personalized recommendations
+pref("extensions.getAddons.maxResults", 15);
+pref("extensions.getAddons.get.url", "https://live.thunderbird.net/services.addons/api/v3/addons/search/?guid=%IDS%&lang=%LOCALE%");
+pref("extensions.getAddons.compatOverides.url", "https://live.thunderbird.net/services.addons/api/v3/addons/compat-override/?guid=%IDS%&lang=%LOCALE%");
+pref("extensions.getAddons.link.url", "https://addons.thunderbird.net/%LOCALE%/%APP%/");
+pref("extensions.getAddons.recommended.url", "https://services.addons.thunderbird.net/%LOCALE%/%APP%/api/%API_VERSION%/list/recommended/all/%MAX_RESULTS%/%OS%/%VERSION%?src=seamonkey");
+pref("extensions.getAddons.search.browseURL", "https://addons.thunderbird.net/%LOCALE%/%APP%/search/?q=%TERMS%");
+pref("extensions.getAddons.search.url", "https://services.addons.thunderbird.net/%LOCALE%/%APP%/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=seamonkey");
+pref("extensions.getAddons.themes.browseURL", "https://addons.thunderbird.net/%LOCALE%/thunderbird/themes/?src=seamonkey");
+pref("extensions.getAddons.siteRegExp", "^https://.*addons\\.thunderbird\\.net");
+
+pref("extensions.webservice.discoverURL", "https://services.addons.thunderbird.net/%LOCALE%/%APP%/discovery/pane/%VERSION%/%OS%");
+
+// Blocklist preferences
+pref("extensions.blocklist.url", "https://live.thunderbird.net/blocklists.settings/v1/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
+pref("extensions.blocklist.detailsURL", "https://live.thunderbird.net/blocked.cdn/");
+pref("extensions.blocklist.itemURL", "https://live.thunderbird.net/blocked.cdn/%blockID%.html");
+
+// Update preferences for installed Extensions and Themes.
+// Symmetric (can be overridden by individual extensions),
+// e.g.
+// extensions.{GUID}.update.enabled
+// extensions.{GUID}.update.url
+// extensions.{GUID}.update.interval
+// extensions.{GUID}.update.autoUpdateDefault
+// .. etc ..
+pref("extensions.update.url", "https://versioncheck.addons.thunderbird.net/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.autoUpdateDefault", true); // Download and install automatically
+pref("extensions.update.enabled", true);
+pref("extensions.update.url", "https://versioncheck.addons.thunderbird.net/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.background.url", "https://versioncheck-bg.addons.thunderbird.net/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+// Check for updates to Extensions and Themes every day
+pref("extensions.update.interval", 86400);
+
+// getMoreThemes is used by our UI under our switch theme menu
+pref("extensions.getMoreThemesURL", "https://addons.thunderbird.net/%LOCALE%/%APP%/complete-themes/");
+pref("extensions.getPersonasURL", "https://addons.thunderbird.net/%LOCALE%/%APP%/themes/?sort=up-and-coming");
+
+// Non-dynamic switch pending after next restart.
+pref("extensions.dss.switchPending", false);
+
+pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://navigator/locale/navigator.properties");
+pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://navigator/locale/navigator.properties");
+
+pref("extensions.modern@themes.mozilla.org.name", "chrome://navigator/locale/navigator.properties");
+pref("extensions.modern@themes.mozilla.org.description", "chrome://navigator/locale/navigator.properties");
+
+pref("lightweightThemes.selectedThemeID", "");
+pref("lightweightThemes.update.enabled", true);
+
+pref("xpinstall.whitelist.add", "addons.thunderbird.net");
+pref("xpinstall.enabled", true);
+pref("xpinstall.signatures.required", false);
+
+// Built-in default permissions.
+pref("permissions.manager.defaultsUrl", "resource:///defaults/permissions");
+
+// Set default fallback values for site permissions we want
+// the user to be able to globally change.
+pref("permissions.default.camera", 0);
+pref("permissions.default.microphone", 0);
+pref("permissions.default.geo", 0);
+pref("permissions.default.desktop-notification", 0);
+
+// Customizable toolbar stuff
+pref("custtoolbar.personal_toolbar_folder", "");
+// Use a popup window for the customize toolbar UI
+pref("toolbar.customization.usesheet", false);
+
+// Show the toolbar and menu grippies.
+pref("browser.toolbars.grippyhidden", false);
+
+#ifdef XP_WIN
+pref("browser.taskbar.lists.enabled", true);
+pref("browser.taskbar.lists.frequent.enabled", true);
+pref("browser.taskbar.lists.recent.enabled", false);
+pref("browser.taskbar.lists.maxListItemCount", 7);
+pref("browser.taskbar.lists.tasks.enabled", true);
+pref("browser.taskbar.lists.refreshInSeconds", 120);
+pref("browser.taskbar.previews.enable", true);
+pref("browser.taskbar.previews.max", 20);
+pref("browser.taskbar.previews.cachetime", 5);
+#endif
+
+pref("sidebar.customize.directory.url", "https://edmullen.net/mozilla/moz_sidebar.php");
+pref("sidebar.customize.more_panels.url", "https://edmullen.net/mozilla/moz_sidebar.php");
+pref("sidebar.num_tabs_in_view", 8);
+
+// pref to control the alert notification
+pref("alerts.slideIncrement", 1);
+pref("alerts.slideIncrementTime", 10);
+pref("alerts.totalOpenTime", 10000);
+
+// 0 opens the download manager
+// 1 opens a progress dialog
+// 2 and other values, no download manager, no progress dialog.
+pref("browser.download.manager.behavior", 0);
+
+pref("privacy.popups.sound_enabled", false);
+pref("privacy.popups.sound_type", 1);
+pref("privacy.popups.sound_url", "");
+pref("privacy.popups.statusbar_icon_enabled", true);
+pref("privacy.popups.prefill_whitelist", false);
+pref("privacy.popups.remove_blacklist", true);
+pref("privacy.popups.showBrowserMessage", true);
+
+// Sanitize options
+
+// Sanitize everything
+pref("privacy.sanitize.timeSpan", 0);
+
+// Sanitize (clear private data manually) options
+pref("privacy.cpd.history", true);
+pref("privacy.cpd.urlbar", true);
+pref("privacy.cpd.formdata", true);
+pref("privacy.cpd.passwords", false);
+pref("privacy.cpd.downloads", true);
+pref("privacy.cpd.cookies", false);
+pref("privacy.cpd.cache", true);
+pref("privacy.cpd.sessions", true);
+pref("privacy.cpd.offlineApps", false);
+pref("privacy.cpd.siteSettings", false);
+
+// Sanitize (clear private data on shutdown) options
+pref("privacy.sanitize.sanitizeOnShutdown", false);
+
+pref("privacy.clearOnShutdown.history", true);
+pref("privacy.clearOnShutdown.urlbar", true);
+pref("privacy.clearOnShutdown.formdata", true);
+pref("privacy.clearOnShutdown.passwords", false);
+pref("privacy.clearOnShutdown.downloads", true);
+pref("privacy.clearOnShutdown.cookies", false);
+pref("privacy.clearOnShutdown.cache", true);
+pref("privacy.clearOnShutdown.sessions", true);
+pref("privacy.clearOnShutdown.offlineApps", false);
+pref("privacy.clearOnShutdown.siteSettings", false);
+
+pref("privacy.warn_tracking_content", true);
+
+// Switching this on will also spoof our user agent and other potentially
+// fingerprintable preferences to generic Firefox ones (see nsRFPService
+// introduced by bug 1330890 and meta-bug 1329996 dependencies).
+pref("privacy.resistFingerprinting", false);
+
+// Show XUL error pages instead of alerts for errors
+pref("browser.xul.error_pages.enabled", true);
+pref("browser.xul.error_pages.expert_bad_cert", false);
+
+// Setting this pref to |true| forces BiDi UI menu items and keyboard shortcuts
+// to be exposed. By default, only expose it for bidi-associated system locales.
+pref("bidi.browser.ui", false);
+
+// block popup windows
+pref("dom.disable_open_during_load", true);
+// prevent JS from moving/resizing existing windows
+pref("dom.disable_window_move_resize", true);
+// prevent JS from raising or lowering windows
+pref("dom.disable_window_flip", true);
+// prevent JS from disabling or replacing context menus
+pref("dom.event.contextmenu.enabled", true);
+
+pref("dom.identity.enabled", false);
+
+// Digital Rights Management, Encrypted Media Extensions
+pref("media.eme.enabled", false);
+
+// Turn off WebRTC by default (bug 1419507)
+pref("media.navigator.enabled", false);
+pref("media.peerconnection.enabled", false);
+
+#ifndef XP_MACOSX
+// Restore the spinner that was removed in bug 481359
+pref("ui.use_activity_cursor", true);
+#endif
+
+#ifdef XP_MACOSX
+// Use a sheet instead of a popup window for the customize toolbar UI
+pref("toolbar.customization.usesheet", true);
+#endif
+
+#ifndef XP_MACOSX
+#ifdef XP_UNIX
+// For the download dialog
+pref("browser.download.progressDnldDialog.enable_launch_reveal_buttons", false);
+
+// Mouse wheel action when over the tab bar:
+// false The mouse wheel scrolls the whole tab bar like Firefox.
+// true The mouse wheel advances the selected tab.
+pref("browser.tabs.mouseScrollAdvancesTab", true);
+
+pref("browser.urlbar.clickSelectsAll", false);
+
+// 0 goes back
+// 1 act like pgup
+// 2 and other values, nothing
+pref("browser.backspace_action", 2);
+
+pref("general.autoScroll", false);
+
+pref("layout.word_select.stop_at_punctuation", false);
+#endif
+#endif
+
+// The breakpad report server to link to in about:crashes
+pref("breakpad.reportURL", "https://crash-reports-test.seamonkey-project.org/report/index/");
+
+// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
+pref("security.alternate_certificate_error_page", "certerror");
+pref("security.warn_entering_secure", false);
+pref("security.warn_leaving_secure", false);
+pref("security.warn_submit_insecure", false);
+pref("security.warn_viewing_mixed", false);
+pref("security.warn_mixed_active_content", true);
+pref("security.warn_mixed_display_content", true);
+// Block insecure active content on https pages
+pref("security.mixed_content.block_active_content", true);
+// Turn on the CSP 1.0 parser for Content Security Policy headers
+pref("security.csp.speccompliant", true);
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
+pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_LOCATION_SERVICE_API_KEY%");
+
+// Some of these prefs are specified even though they may be redundant; they are given
+// here for clarity and end-user experiments with platform-provided geolocation.
+#ifdef XP_MACOSX
+pref("geo.provider.use_corelocation", false);
+#endif
+#ifdef XP_WIN
+pref("geo.provider.ms-windows-location", false);
+#endif
+#ifdef MOZ_WIDGET_GTK
+pref("geo.provider.use_gpsd", false);
+#endif
+
+// FAQ URLs
+pref("browser.geolocation.warning.infoURL", "http://www.seamonkey-project.org/doc/2.0/geolocation");
+
+pref("browser.rights.version", 1);
+pref("browser.rights.1.shown", false);
+
+#ifdef DEBUG
+// Don't show the about:rights notification in debug builds.
+pref("browser.rights.override", true);
+#elifndef OFFICIAL_BUILD
+// Don't show the about:rights notification in non-official builds.
+pref("browser.rights.override", true);
+#endif
+
+// The sync engines to use.
+pref("services.sync.registerEngines", "Bookmarks,Form,History,Password,Prefs,Tab,Addons");
+// Preferences to be synced by default
+pref("services.sync.prefs.sync.accessibility.blockautorefresh", true);
+pref("services.sync.prefs.sync.accessibility.browsewithcaret", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind.autostart", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind.linksonly", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind.usefindbar", true);
+pref("services.sync.prefs.sync.addons.ignoreUserEnabledChanges", true);
+// The addons prefs related to repository verification are intentionally
+// not synced for security reasons. If a system is compromised, a user
+// could weaken the pref locally, install an add-on from an untrusted
+// source, and this would propagate automatically to other,
+// uncompromised Sync-connected devices.
+pref("services.sync.prefs.sync.browser.download.manager.behavior", true);
+pref("services.sync.prefs.sync.browser.formfill.enable", true);
+pref("services.sync.prefs.sync.browser.link.open_external", true);
+pref("services.sync.prefs.sync.browser.link.open_newwindow", true);
+pref("services.sync.prefs.sync.browser.offline-apps.notify", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.phishing.enabled", true);
+pref("services.sync.prefs.sync.browser.search.update", true);
+pref("services.sync.prefs.sync.browser.sessionstore.max_concurrent_tabs", true);
+pref("services.sync.prefs.sync.browser.startup.homepage", true);
+pref("services.sync.prefs.sync.browser.startup.page", true);
+pref("services.sync.prefs.sync.browser.tabs.autoHide", true);
+pref("services.sync.prefs.sync.browser.tabs.closeButtons", true);
+pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnCloseOther", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
+pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
+pref("services.sync.prefs.sync.browser.urlbar.autoFill", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.history", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.bookmark", true);
+pref("services.sync.prefs.sync.browser.urlbar.suggest.history.onlyTyped", true);
+pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
+pref("services.sync.prefs.sync.dom.disable_window_flip", true);
+pref("services.sync.prefs.sync.dom.disable_window_move_resize", true);
+pref("services.sync.prefs.sync.dom.disable_window_open_feature.status", true);
+pref("services.sync.prefs.sync.dom.disable_window_status_change", true);
+pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true);
+pref("services.sync.prefs.sync.extensions.update.enabled", true);
+pref("services.sync.prefs.sync.general.smoothScroll", true);
+pref("services.sync.prefs.sync.intl.accept_languages", true);
+pref("services.sync.prefs.sync.javascript.enabled", true);
+pref("services.sync.prefs.sync.layout.spellcheckDefault", true);
+pref("services.sync.prefs.sync.lightweightThemes.isThemeSelected", true);
+pref("services.sync.prefs.sync.lightweightThemes.usedThemes", true);
+pref("services.sync.prefs.sync.mailnews.confirm.moveFoldersToTrash", true);
+pref("services.sync.prefs.sync.mailnews.customDBHeaders", true);
+pref("services.sync.prefs.sync.mailnews.customHeaders", true);
+pref("services.sync.prefs.sync.mailnews.display.date_senders_timezone", true);
+pref("services.sync.prefs.sync.mailnews.display.disable_format_flowed_support", true);
+pref("services.sync.prefs.sync.mailnews.display.disallow_mime_handlers", true);
+pref("services.sync.prefs.sync.mailnews.display.html_as", true);
+pref("services.sync.prefs.sync.mailnews.display.html_sanitizer.allowed_tags", true);
+pref("services.sync.prefs.sync.mailnews.display.prefer_plaintext", true);
+pref("services.sync.prefs.sync.mailnews.display.show_all_body_parts_menu", true);
+pref("services.sync.prefs.sync.mailnews.emptyTrash.dontAskAgain", true);
+pref("services.sync.prefs.sync.mailnews.filters.confirm_delete", true);
+pref("services.sync.prefs.sync.mailnews.forward_header_originalmessage", true);
+pref("services.sync.prefs.sync.mailnews.headers.extraExpandedHeaders", true);
+pref("services.sync.prefs.sync.mailnews.headers.showMessageId", true);
+pref("services.sync.prefs.sync.mailnews.headers.showOrganization", true);
+pref("services.sync.prefs.sync.mailnews.headers.showReferences", true);
+pref("services.sync.prefs.sync.mailnews.headers.showSender", true);
+pref("services.sync.prefs.sync.mailnews.headers.showUserAgent", true);
+pref("services.sync.prefs.sync.mailnews.localizedRe", true);
+pref("services.sync.prefs.sync.mailnews.mark_message_read.auto", true);
+pref("services.sync.prefs.sync.mailnews.mark_message_read.delay", true);
+pref("services.sync.prefs.sync.mailnews.mark_message_read.delay.interval", true);
+pref("services.sync.prefs.sync.mailnews.messageid.openInNewWindow", true);
+pref("services.sync.prefs.sync.mailnews.message_display.disable_remote_image", true);
+pref("services.sync.prefs.sync.mailnews.nav_crosses_folders", true);
+pref("services.sync.prefs.sync.mailnews.offline_sync_mail", true);
+pref("services.sync.prefs.sync.mailnews.offline_sync_news", true);
+pref("services.sync.prefs.sync.mailnews.offline_sync_send_unsent", true);
+pref("services.sync.prefs.sync.mailnews.offline_sync_work_offline", true);
+pref("services.sync.prefs.sync.mailnews.remember_selected_message", true);
+pref("services.sync.prefs.sync.mailnews.reply_header_authorwrotesingle", true);
+pref("services.sync.prefs.sync.mailnews.reply_header_ondateauthorwrote", true);
+pref("services.sync.prefs.sync.mailnews.reply_header_authorwroteondate", true);
+pref("services.sync.prefs.sync.mailnews.reply_header_locale", true);
+pref("services.sync.prefs.sync.mailnews.reply_header_originalmessage", true);
+pref("services.sync.prefs.sync.mailnews.reply_header_type", true);
+pref("services.sync.prefs.sync.mailnews.scroll_to_new_message", true);
+pref("services.sync.prefs.sync.mailnews.sendInBackground", true);
+pref("services.sync.prefs.sync.mailnews.send_default_charset", true);
+pref("services.sync.prefs.sync.mailnews.send_plaintext_flowed", true);
+pref("services.sync.prefs.sync.mailnews.show_send_progress", true);
+pref("services.sync.prefs.sync.mailnews.start_page.enabled", true);
+pref("services.sync.prefs.sync.mailnews.thread_pane_column_unthreads", true);
+pref("services.sync.prefs.sync.mailnews.ui.deleteMarksRead", true);
+pref("services.sync.prefs.sync.mailnews.ui.junk.manualMarkAsJunkMarksRead", true);
+pref("services.sync.prefs.sync.mailnews.view_default_charset", true);
+pref("services.sync.prefs.sync.mailnews.wraplength", true);
+pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true);
+pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true);
+pref("services.sync.prefs.sync.offline-apps.allow_by_default", true);
+pref("services.sync.prefs.sync.permissions.default.image", true);
+pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
+pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
+pref("services.sync.prefs.sync.privacy.sanitize.promptOnSanitize", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.cache", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.cookies", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.downloads", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.passwords", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.urlbar", true);
+pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true);
+pref("services.sync.prefs.sync.privacy.warn_tracking_content", true);
+pref("services.sync.prefs.sync.security.OCSP.enabled", true);
+pref("services.sync.prefs.sync.security.OCSP.require", true);
+pref("services.sync.prefs.sync.security.default_personal_cert", true);
+pref("services.sync.prefs.sync.security.mixed_content.block_active_content", true);
+pref("services.sync.prefs.sync.security.mixed_content.block_display_content", true);
+pref("services.sync.prefs.sync.security.tls.version.min", true);
+pref("services.sync.prefs.sync.security.tls.version.max", true);
+pref("services.sync.prefs.sync.security.warn_entering_secure", true);
+pref("services.sync.prefs.sync.security.warn_leaving_secure", true);
+pref("services.sync.prefs.sync.security.warn_mixed_active_content", true);
+pref("services.sync.prefs.sync.security.warn_mixed_display_content", true);
+pref("services.sync.prefs.sync.security.warn_submit_insecure", true);
+pref("services.sync.prefs.sync.security.warn_viewing_mixed", true);
+pref("services.sync.prefs.sync.signon.rememberSignons", true);
+pref("services.sync.prefs.sync.spellchecker.dictionary", true);
+pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
+
+// Enable the DOM fullscreen API.
+pref("full-screen-api.enabled", true);
+
+// Most DevTools prefs are set from the shared file
+// devtools/client/preferences/devtools.js, but this one is currently set
+// per-app or per-channel.
+// Number of usages of the web console or scratchpad. If this is less than 5,
+// then pasting code into the web console or scratchpad is disabled
+pref("devtools.selfxss.count", 5);
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+// When this pref is true the Windows process sandbox will set up dummy
+// interceptions and log to the browser console when calls fail in the sandboxed
+// process and also if they are subsequently allowed by the broker process.
+// This will require a restart.
+pref("security.sandbox.windows.log", false);
+
+#if defined(MOZ_CONTENT_SANDBOX)
+// This controls the strength of the Windows content process sandbox for testing
+// purposes. This will require a restart.
+// On windows these levels are:
+// See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+// SetSecurityLevelForContentProcess() for what the different settings mean.
+#if defined(NIGHTLY_BUILD)
+pref("security.sandbox.content.level", 2);
+#else
+pref("security.sandbox.content.level", 1);
+#endif
+
+#if defined(MOZ_STACKWALKING)
+// This controls the depth of stack trace that is logged when Windows sandbox
+// logging is turned on. This is only currently available for the content
+// process because the only other sandbox (for GMP) has too strict a policy to
+// allow stack tracing. This does not require a restart to take effect.
+pref("security.sandbox.windows.log.stackTraceDepth", 0);
+#endif
+#endif
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
+// This pref is discussed in bug 1083344, the naming is inspired from its
+// Windows counterpart, but on Mac it's an integer which means:
+// 0 -> "no sandbox"
+// 1 -> "preliminary content sandboxing enabled: write access to
+// home directory is prevented"
+// 2 -> "preliminary content sandboxing enabled with profile protection:
+// write access to home directory is prevented, read and write access
+// to ~/Library and profile directories are prevented (excluding
+// $PROFILE/{extensions,weave})"
+// This setting is read when the content process is started. On Mac the content
+// process is killed when all windows are closed, so a change will take effect
+// when the 1st window is opened.
+#if defined(NIGHTLY_BUILD)
+pref("security.sandbox.content.level", 2);
+#else
+pref("security.sandbox.content.level", 1);
+#endif
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
+// This pref is introduced as part of bug 742434, the naming is inspired from
+// its Windows/Mac counterpart, but on Linux it's an integer which means:
+// 0 -> "no sandbox"
+// 1 -> "content sandbox using seccomp-bpf when available"
+// 2 -> "seccomp-bpf + file broker"
+// Content sandboxing on Linux is currently in the stage of
+// 'just getting it enabled', which includes a very permissive whitelist. We
+// enable seccomp-bpf on nightly to see if everything is running, or if we need
+// to whitelist more system calls.
+//
+// So the purpose of this setting is to allow nightly users to disable the
+// sandbox while we fix their problems. This way, they won't have to wait for
+// another nightly release which disables seccomp-bpf again.
+//
+// This setting may not be required anymore once we decide to permanently
+// enable the content sandbox.
+pref("security.sandbox.content.level", 2);
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
+#if defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
+// ID (a UUID when set by gecko) that is used to form the name of a
+// sandbox-writable temporary directory to be used by content processes
+// when a temporary writable file is required in a level 1 sandbox.
+pref("security.sandbox.content.tempDirSuffix", "");
+#endif
+#endif
+
+// Url shown when you type moz://a
+pref("toolkit.mozprotocol.url", "http://www.seamonkey-project.org/");
+
+// Serif fonts look dated. Switch those language families to sans-serif where it
+// makes sense, see bug 1727982. See all.js for other font families preferences.
+pref("font.default", "sans-serif");
+pref("font.default.x-unicode", "sans-serif");
+pref("font.default.x-western", "sans-serif");
+pref("font.default.x-cyrillic", "sans-serif");
+pref("font.default.el", "sans-serif");
+
+// Toolbox preferences
+pref("devtools.toolbox.footer.height", 250);
+pref("devtools.toolbox.sidebar.width", 500);
+pref("devtools.toolbox.host", "bottom");
+pref("devtools.toolbox.previousHost", "right");
+pref("devtools.toolbox.selectedTool", "inspector");
+pref("devtools.toolbox.sideEnabled", true);
+pref("devtools.toolbox.zoomValue", "1");
+pref("devtools.toolbox.splitconsoleEnabled", false);
+pref("devtools.toolbox.splitconsoleHeight", 100);
+pref("devtools.toolbox.tabsOrder", "");
+
+// The fission pref is enabling the "Omniscient Browser Toolbox", which will
+// make it possible to debug anything in Firefox (See Bug 1570639 for more
+// information).
+// âš  This is a work in progress. Expect weirdness when the pref is enabled. âš 
+pref("devtools.browsertoolbox.fission", false);
+
+// Toolbox Button preferences
+pref("devtools.command-button-pick.enabled", true);
+pref("devtools.command-button-frames.enabled", true);
+pref("devtools.command-button-splitconsole.enabled", true);
+pref("devtools.command-button-paintflashing.enabled", false);
+pref("devtools.command-button-scratchpad.enabled", false);
+pref("devtools.command-button-responsive.enabled", true);
+pref("devtools.command-button-screenshot.enabled", false);
+pref("devtools.command-button-rulers.enabled", false);
+pref("devtools.command-button-measure.enabled", false);
+pref("devtools.command-button-noautohide.enabled", false);
+
+// Inspector preferences
+// Enable the Inspector
+pref("devtools.inspector.enabled", true);
+
+// What was the last active sidebar in the inspector
+pref("devtools.inspector.activeSidebar", "layoutview");
+pref("devtools.inspector.remote", false);
+
+// Enable the 3 pane mode in the inspector
+pref("devtools.inspector.three-pane-enabled", true);
+// Enable the 3 pane mode in the chrome inspector
+pref("devtools.inspector.chrome.three-pane-enabled", false);
+// Collapse pseudo-elements by default in the rule-view
+pref("devtools.inspector.show_pseudo_elements", false);
+// The default size for image preview tooltips in the rule-view/computed-view/markup-view
+pref("devtools.inspector.imagePreviewTooltipSize", 300);
+// Enable user agent style inspection in rule-view
+pref("devtools.inspector.showUserAgentStyles", false);
+// Show all native anonymous content
+pref("devtools.inspector.showAllAnonymousContent", false);
+// Show user agent shadow roots
+pref("devtools.inspector.showUserAgentShadowRoots", false);
+// Enable the new Rules View
+pref("devtools.inspector.new-rulesview.enabled", false);
+
+// Grid highlighter preferences
+pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
+pref("devtools.gridinspector.gridOutlineMaxRows", 50);
+pref("devtools.gridinspector.showGridAreas", false);
+pref("devtools.gridinspector.showGridLineNumbers", false);
+pref("devtools.gridinspector.showInfiniteLines", false);
+// Max number of grid highlighters that can be displayed
+pref("devtools.gridinspector.maxHighlighters", 3);
+
+// Whether or not the box model panel is opened in the layout view
+pref("devtools.layout.boxmodel.opened", true);
+// Whether or not the flexbox panel is opened in the layout view
+pref("devtools.layout.flexbox.opened", true);
+// Whether or not the grid inspector panel is opened in the layout view
+pref("devtools.layout.grid.opened", true);
+
+// Enable hovering Box Model values and jumping to their source CSS rule in the
+// rule-view.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.layout.boxmodel.highlightProperty", true);
+#else
+ pref("devtools.layout.boxmodel.highlightProperty", false);
+#endif
+
+// By how many times eyedropper will magnify pixels
+pref("devtools.eyedropper.zoom", 6);
+
+// Enable to collapse attributes that are too long.
+pref("devtools.markup.collapseAttributes", true);
+// Length to collapse attributes
+pref("devtools.markup.collapseAttributeLength", 120);
+// Whether to auto-beautify the HTML on copy.
+pref("devtools.markup.beautifyOnCopy", false);
+// Whether or not the DOM mutation breakpoints context menu are enabled in the
+// markup view.
+pref("devtools.markup.mutationBreakpoints.enabled", false);
+
+// DevTools default color unit
+pref("devtools.defaultColorUnit", "authored");
+
+// Enable the Memory tools
+pref("devtools.memory.enabled", true);
+
+pref("devtools.memory.custom-census-displays", "{}");
+pref("devtools.memory.custom-label-displays", "{}");
+pref("devtools.memory.custom-tree-map-displays", "{}");
+
+pref("devtools.memory.max-individuals", 1000);
+pref("devtools.memory.max-retaining-paths", 10);
+
+// Enable the Performance tools
+pref("devtools.performance.enabled", true);
+
+// The default Performance UI settings
+pref("devtools.performance.memory.sample-probability", "0.05");
+
+// Can't go higher than this without causing internal allocation overflows while
+// serializing the allocations data over the RDP.
+pref("devtools.performance.memory.max-log-length", 125000);
+pref("devtools.performance.timeline.hidden-markers",
+ "[\"Composite\",\"CompositeForwardTransaction\"]");
+pref("devtools.performance.profiler.buffer-size", 10000000);
+pref("devtools.performance.profiler.sample-frequency-hz", 1000);
+pref("devtools.performance.ui.invert-call-tree", true);
+pref("devtools.performance.ui.invert-flame-graph", false);
+pref("devtools.performance.ui.flatten-tree-recursion", true);
+pref("devtools.performance.ui.show-platform-data", false);
+pref("devtools.performance.ui.show-idle-blocks", true);
+pref("devtools.performance.ui.enable-memory", false);
+pref("devtools.performance.ui.enable-allocations", false);
+pref("devtools.performance.ui.enable-framerate", true);
+pref("devtools.performance.ui.show-jit-optimizations", false);
+pref("devtools.performance.ui.show-triggers-for-gc-types",
+ "TOO_MUCH_MALLOC ALLOC_TRIGGER LAST_DITCH EAGER_ALLOC_TRIGGER");
+
+// Temporary pref disabling memory flame views
+// TODO remove once we have flame charts via bug 1148663
+pref("devtools.performance.ui.enable-memory-flame", false);
+
+// Enable experimental options in the UI only in Nightly
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.performance.ui.experimental", true);
+#else
+ pref("devtools.performance.ui.experimental", false);
+#endif
+
+// Preferences for the new performance panel. This pref configures the base URL
+// for the profiler.firefox.com instance to use. This is useful so that a
+// developer can change it while working on profiler.firefox.com, or in tests.
+// This isn't exposed directly to the user.
+pref("devtools.performance.recording.ui-base-url", "https://profiler.firefox.com");
+
+// A JSON array of strings, where each string is a file path to an objdir on
+// the host machine. This is used in order to look up symbol information from
+// build artifacts of local builds.
+pref("devtools.performance.recording.objdirs", "[]");
+
+// The default cache UI setting
+pref("devtools.cache.disabled", false);
+
+// The default service workers UI setting
+pref("devtools.serviceWorkers.testing.enabled", false);
+
+// Enable the Network Monitor
+pref("devtools.netmonitor.enabled", true);
+
+// Enable Network Search
+pref("devtools.netmonitor.features.search", false);
+
+// Enable the Application panel
+pref("devtools.application.enabled", false);
+
+// The default Network Monitor UI settings
+pref("devtools.netmonitor.panes-network-details-width", 550);
+pref("devtools.netmonitor.panes-network-details-height", 450);
+pref("devtools.netmonitor.panes-search-width", 550);
+pref("devtools.netmonitor.panes-search-height", 450);
+pref("devtools.netmonitor.filters", "[\"all\"]");
+pref("devtools.netmonitor.visibleColumns",
+ "[\"status\",\"method\",\"domain\",\"file\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
+);
+pref("devtools.netmonitor.columnsData",
+ '[{"name":"status","minWidth":30,"width":5}, {"name":"method","minWidth":30,"width":5}, {"name":"domain","minWidth":30,"width":10}, {"name":"file","minWidth":30,"width":25}, {"name":"url","minWidth":30,"width":25}, {"name":"cause","minWidth":30,"width":10},{"name":"type","minWidth":30,"width":5},{"name":"transferred","minWidth":30,"width":10},{"name":"contentSize","minWidth":30,"width":5},{"name":"waterfall","minWidth":150,"width":25}]');
+pref("devtools.netmonitor.ws.payload-preview-height", 128);
+pref("devtools.netmonitor.ws.visibleColumns",
+ '["data", "time"]'
+);
+
+pref("devtools.netmonitor.ws.displayed-frames.limit", 500);
+pref("devtools.netmonitor.response.ui.limit", 10240);
+
+// Save request/response bodies yes/no.
+pref("devtools.netmonitor.saveRequestAndResponseBodies", true);
+
+// The default Network monitor HAR export setting
+pref("devtools.netmonitor.har.defaultLogDir", "");
+pref("devtools.netmonitor.har.defaultFileName", "%hostname_Archive [%date]");
+pref("devtools.netmonitor.har.jsonp", false);
+pref("devtools.netmonitor.har.jsonpCallback", "");
+pref("devtools.netmonitor.har.includeResponseBodies", true);
+pref("devtools.netmonitor.har.compress", false);
+pref("devtools.netmonitor.har.forceExport", false);
+pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
+pref("devtools.netmonitor.har.enableAutoExportToFile", false);
+
+// Enable WebSocket monitoring in Nightly builds.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.netmonitor.features.webSockets", true);
+#else
+ pref("devtools.netmonitor.features.webSockets", false);
+#endif
+
+// Scratchpad settings
+// - recentFileMax: The maximum number of recently-opened files
+// stored. Setting this preference to 0 will not
+// clear any recent files, but rather hide the
+// 'Open Recent'-menu.
+// - lineNumbers: Whether to show line numbers or not.
+// - wrapText: Whether to wrap text or not.
+// - showTrailingSpace: Whether to highlight trailing space or not.
+// - editorFontSize: Editor font size configuration.
+// - enableAutocompletion: Whether to enable JavaScript autocompletion.
+pref("devtools.scratchpad.recentFilesMax", 10);
+pref("devtools.scratchpad.lineNumbers", true);
+pref("devtools.scratchpad.wrapText", false);
+pref("devtools.scratchpad.showTrailingSpace", false);
+pref("devtools.scratchpad.editorFontSize", 12);
+pref("devtools.scratchpad.enableAutocompletion", true);
+
+// Enable the Storage Inspector
+pref("devtools.storage.enabled", true);
+
+// Enable the Style Editor.
+pref("devtools.styleeditor.enabled", true);
+pref("devtools.styleeditor.autocompletion-enabled", true);
+pref("devtools.styleeditor.showMediaSidebar", true);
+pref("devtools.styleeditor.mediaSidebarWidth", 238);
+pref("devtools.styleeditor.navSidebarWidth", 245);
+pref("devtools.styleeditor.transitions", true);
+
+// Screenshot Option Settings.
+pref("devtools.screenshot.clipboard.enabled", false);
+pref("devtools.screenshot.audio.enabled", true);
+
+// Enable Scratchpad
+pref("devtools.scratchpad.enabled", false);
+
+// Make sure the DOM panel is hidden by default
+pref("devtools.dom.enabled", false);
+
+// Enable the Accessibility panel.
+pref("devtools.accessibility.enabled", true);
+
+// Web console filters
+pref("devtools.webconsole.filter.error", true);
+pref("devtools.webconsole.filter.warn", true);
+pref("devtools.webconsole.filter.info", true);
+pref("devtools.webconsole.filter.log", true);
+pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.css", false);
+pref("devtools.webconsole.filter.net", false);
+pref("devtools.webconsole.filter.netxhr", false);
+
+// Webconsole autocomplete preference
+pref("devtools.webconsole.input.autocomplete",true);
+
+// Browser console filters
+pref("devtools.browserconsole.filter.error", true);
+pref("devtools.browserconsole.filter.warn", true);
+pref("devtools.browserconsole.filter.info", true);
+pref("devtools.browserconsole.filter.log", true);
+pref("devtools.browserconsole.filter.debug", true);
+pref("devtools.browserconsole.filter.css", false);
+pref("devtools.browserconsole.filter.net", false);
+pref("devtools.browserconsole.filter.netxhr", false);
+
+// Max number of inputs to store in web console history.
+pref("devtools.webconsole.inputHistoryCount", 300);
+
+// Persistent logging: |true| if you want the relevant tool to keep all of the
+// logged messages after reloading the page, |false| if you want the output to
+// be cleared each time page navigation happens.
+pref("devtools.webconsole.persistlog", false);
+pref("devtools.netmonitor.persistlog", false);
+
+// Web Console timestamp: |true| if you want the logs and instructions
+// in the Web Console to display a timestamp, or |false| to not display
+// any timestamps.
+pref("devtools.webconsole.timestampMessages", false);
+
+// Enable the webconsole sidebar toggle in Nightly builds.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.webconsole.sidebarToggle", true);
+#else
+ pref("devtools.webconsole.sidebarToggle", false);
+#endif
+
+// Enable editor mode in the console in Nightly builds.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.webconsole.features.editor", true);
+#else
+ pref("devtools.webconsole.features.editor", false);
+#endif
+
+// Saved editor mode state in the console.
+pref("devtools.webconsole.input.editor", false);
+
+// Editor width for webconsole and browserconsole
+pref("devtools.webconsole.input.editorWidth", 0);
+pref("devtools.browserconsole.input.editorWidth", 0);
+
+// Disable the new performance recording panel by default
+pref("devtools.performance.new-panel-enabled", false);
+
+// Enable message grouping in the console, true by default
+pref("devtools.webconsole.groupWarningMessages", true);
+
+// Saved state of the Display content messages checkbox in the browser console.
+pref("devtools.browserconsole.contentMessages", false);
+
+// Enable client-side mapping service for source maps
+pref("devtools.source-map.client-service.enabled", true);
+
+// The number of lines that are displayed in the web console.
+pref("devtools.hud.loglimit", 10000);
+
+// The developer tools editor configuration:
+// - tabsize: how many spaces to use when a Tab character is displayed.
+// - expandtab: expand Tab characters to spaces.
+// - keymap: which keymap to use (can be 'default', 'emacs' or 'vim')
+// - autoclosebrackets: whether to permit automatic bracket/quote closing.
+// - detectindentation: whether to detect the indentation from the file
+// - enableCodeFolding: Whether to enable code folding or not.
+pref("devtools.editor.tabsize", 2);
+pref("devtools.editor.expandtab", true);
+pref("devtools.editor.keymap", "default");
+pref("devtools.editor.autoclosebrackets", true);
+pref("devtools.editor.detectindentation", true);
+pref("devtools.editor.enableCodeFolding", true);
+pref("devtools.editor.autocomplete", true);
+
+// The angle of the viewport.
+pref("devtools.responsive.viewport.angle", 0);
+// The width of the viewport.
+pref("devtools.responsive.viewport.width", 320);
+// The height of the viewport.
+pref("devtools.responsive.viewport.height", 480);
+// The pixel ratio of the viewport.
+pref("devtools.responsive.viewport.pixelRatio", 0);
+// Whether or not the viewports are left aligned.
+pref("devtools.responsive.leftAlignViewport.enabled", false);
+// Whether to reload when touch simulation is toggled
+pref("devtools.responsive.reloadConditions.touchSimulation", false);
+// Whether to reload when user agent is changed
+pref("devtools.responsive.reloadConditions.userAgent", false);
+// Whether to show the notification about reloading to apply emulation
+pref("devtools.responsive.reloadNotification.enabled", true);
+// Whether or not touch simulation is enabled.
+pref("devtools.responsive.touchSimulation.enabled", false);
+// Whether or not meta viewport is enabled, if and only if touchSimulation
+// is also enabled.
+pref("devtools.responsive.metaViewport.enabled", false);
+// The user agent of the viewport.
+pref("devtools.responsive.userAgent", "");
+
+// Whether to show the settings onboarding tooltip only in release or beta
+// builds.
+#if defined(RELEASE_OR_BETA)
+ pref("devtools.responsive.show-setting-tooltip", true);
+#else
+ pref("devtools.responsive.show-setting-tooltip", false);
+#endif
+// Show the custom user agent input in Nightly builds.
+#if defined(NIGHTLY_BUILD)
+ pref("devtools.responsive.showUserAgentInput", true);
+#else
+ pref("devtools.responsive.showUserAgentInput", false);
+#endif
+
+// Show tab debug targets for This Firefox (on by default for local builds).
+#ifdef MOZILLA_OFFICIAL
+ pref("devtools.aboutdebugging.local-tab-debugging", false);
+#else
+ pref("devtools.aboutdebugging.local-tab-debugging", true);
+#endif
+
+// Show process debug targets.
+pref("devtools.aboutdebugging.process-debugging", true);
+// Stringified array of network locations that users can connect to.
+pref("devtools.aboutdebugging.network-locations", "[]");
+// Debug target pane collapse/expand settings.
+pref("devtools.aboutdebugging.collapsibilities.installedExtension", false);
+pref("devtools.aboutdebugging.collapsibilities.otherWorker", false);
+pref("devtools.aboutdebugging.collapsibilities.serviceWorker", false);
+pref("devtools.aboutdebugging.collapsibilities.sharedWorker", false);
+pref("devtools.aboutdebugging.collapsibilities.tab", false);
+pref("devtools.aboutdebugging.collapsibilities.temporaryExtension", false);
+
+// about:debugging: only show system and hidden extensions in local builds by
+// default.
+#ifdef MOZILLA_OFFICIAL
+ pref("devtools.aboutdebugging.showHiddenAddons", false);
+#else
+ pref("devtools.aboutdebugging.showHiddenAddons", true);
+#endif
+
+// Map top-level await expressions in the console
+pref("devtools.debugger.features.map-await-expression", true);
+
+// Disable autohide for DevTools popups and tooltips.
+// This is currently not exposed by any UI to avoid making
+// about:devtools-toolbox tabs unusable by mistake.
+pref("devtools.popup.disable_autohide", false);
+
+// Load the DevTools toolbox in a frame with type=content instead of type=chrome
+// See Bug 1539979 for more details.
+// We keep the option of running devtools in a chrome frame while we fix racy
+// tests that started failing when using type=content, but this ultimately
+// should be removed.
+pref("devtools.toolbox.content-frame", true);
diff --git a/comm/suite/app/seamonkey.exe.manifest b/comm/suite/app/seamonkey.exe.manifest
new file mode 100644
index 0000000000..16c190f0d3
--- /dev/null
+++ b/comm/suite/app/seamonkey.exe.manifest
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="*"
+ name="SeaMonkey"
+ type="win32"
+/>
+<description>SeaMonkey</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="*"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+ <ms_asmv3:security>
+ <ms_asmv3:requestedPrivileges>
+ <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </ms_asmv3:requestedPrivileges>
+ </ms_asmv3:security>
+</ms_asmv3:trustInfo>
+ <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+ <ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAware>true</dpiAware>
+ </ms_asmv3:windowsSettings>
+ </ms_asmv3:application>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!--This Id value indicates the application supports Windows 7 and Server 2008 R2 functionality-->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!--This Id value indicates the application supports Windows 8 and Server 2012 functionality-->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!--This Id value indicates the application supports Windows 8.1 and Server 2012 R2 functionality-->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!--This Id value indicates the application supports Windows 10 and Server 2016 functionality-->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/comm/suite/app/seamonkey.man.in b/comm/suite/app/seamonkey.man.in
new file mode 100644
index 0000000000..d5a51eb1ae
--- /dev/null
+++ b/comm/suite/app/seamonkey.man.in
@@ -0,0 +1,160 @@
+.TH @MOZ_APP_NAME@ 1 "August 05, 2020" @MOZ_APP_NAME@ "Linux User's Manual"
+.SH NAME
+@MOZ_APP_NAME@ \- an internet application suite (browser, mail, etc.)
+
+.SH SYNOPSIS
+.B @MOZ_APP_NAME@
+[\fIOPTIONS\fR] [\fIURL\fR]
+
+.SH DESCRIPTION
+\fB@MOZ_APP_DISPLAYNAME@\fR is an all-in-one Internet application suite (previously made popular by Netscape and Mozilla).
+It includes an Internet browser, advanced e-mail, newsgroup and feed client, a calendar, IRC client,
+HTML editor and a tool to inspect the DOM for web pages.
+It is derived from the application formerly known as Mozilla Application Suite.
+
+.SH OPTIONS
+A summary of the options supported by \fB@MOZ_APP_NAME@\fR is included below.
+
+.SS "X11 options"
+.TP
+.BI \-\-display= DISPLAY
+X display to use
+.TP
+.B \--sync
+Make X calls synchronous
+.TP
+.B \-\-g-fatal-warnings
+Make all warnings fatal
+
+.SS "@MOZ_APP_DISPLAYNAME@ options"
+.TP
+.B \-h, \-help
+Show summary of options.
+.TP
+.B \-v, \-version
+Print \fB@MOZ_APP_NAME@\fR version.
+.TP
+\fB\-P\fR \fIprofile\fR
+Start with \fIprofile\fR.
+.TP
+\fB\-profile\fR \fIpath\fR
+Start with profile at \fIpath\fR.
+.TP
+\fB\-CreateProfile\fR \fIprofile_name\fR
+Create a new profile in the default directory, but do not start the application.
+The profile will be named \fIprofile_name\fR in the profile manager,
+the \fIprofile_name\fR must not contain spaces. Do not run \fIprofile_name\fR while running
+an instance of the application, you can use the \fB-no-remote\fR option to avoid connecting
+to a running instance.
+.TP
+\fB\-CreateProfile\fR "\fIprofile_name profile_dir\fR"
+The same, but creates a new profile in the \fIprofile_dir\fR directory.
+Note \fIprofile_name\fR and \fIprofile_dir\fR are quoted together, and are separated
+by exactly 1 space.
+.TP
+.B \-migration
+Start with import wizard (if you have proper import sources).
+.TP
+.B \-ProfileManager
+Start with profile manager.
+.TP
+.B \-no-remote
+Do not accept or send remote commands. Implies \fB-new-instance\fR.
+.TP
+.B \-new-instance
+Open new instance, not a new window in running instance,
+which allows multiple copies of application to be open at a time.
+.TP
+.B \-safe-mode
+Launches the application with all extensions disabled, for that launch only
+(extensions are not loaded, but are not permanently disabled in the extension manager data source).
+.TP
+.B \-headless
+Runs @MOZ_APP_DISPLAYNAME@ in headless mode, which is very useful for purposes such as
+debugging and automated testing.
+.TP
+.B \-addressbook
+Open the address book at startup.
+.TP
+\fB\-compose\fR [\fIurl\fR]
+Compose a mail or news message. Options are specified as string
+\fB"option='\fIvalue,...\fB',option=\fIvalue\fR,\fI...\fB"\fR
+and include: \fIfrom\fR, \fIto\fR, \fIcc\fR, \fIbcc\fR, \fInewsgroups\fR, \fIsubject\fR, \fIbody\fR, \fImessage \fR(\fIfile\fR), \fIattachment\fR (\fIfile\fR), \fIformat\fR (\fIhtml\fR | \fItext\fR).
+Example:
+.B "to=\fIjohn@example.com\fB,subject=\fI'Dinner tonight?'\fB"
+.TP
+.B \-jsconsole
+Open the Browser Console.
+.TP
+.B \-purgecaches
+Gecko (layout engine) has a JavaScript cache, which is not reset on startup, this clears it.
+.TP
+\fB\-edit\fR [\fIurl\fR]
+Start with editor (composer) for the given \fIurl\fR (optional).
+.TP
+.B \-mail, \-news
+Open the Mail&News folder view.
+.TP
+\fB\-mail\fR \fIurl\fR
+Open the message specified by this \fIurl\fR.
+.TP
+.B \-offline
+Start with the offline mode.
+This option will only work if @MOZ_APP_DISPLAYNAME@ is set to "detect
+automatically" the online/offline status on startup. To set this, go
+to Edit -> Preferences -> Mail & Newsgroups -> Network & Storage and
+in the "Offline" group choose "Detect automatically (if available)" in
+the "When starting up:" drop-down box.
+.TP
+.B \-suiteconsole
+Open the Error Console.
+.TP
+.B \-browser
+Open a browser window.
+.TP
+.B \-private
+Open a private browsing window.
+.TP
+\fB\-new-window\fR \fIurl\fR
+Open \fIurl\fR in a new browser window.
+.TP
+\fB\-new-tab\fR \fIurl\fR
+Open \fIurl\fR in a new browser tab.
+.TP
+\fB\-url\fR \fIurl\fR
+Open the specified \fIurl\fR.
+.TP
+\fB\-chrome\fR \fIchrome_url\fR
+Load the specified chrome.
+.TP
+\fB\-register\fR \fIchrome_url\fR
+Register the specified chrome, but do not start application.
+.TP
+\fB\-search\fR \fIterm\fR
+Search \fIterm\fR with your default search engine.
+.TP
+.B \-preferences
+Open Preferences window.
+.TP
+\fB\-remote\fR \fIcommand\fR
+Execute \fIcommand\fR in an already running @MOZ_APP_DISPLAYNAME@ process. For more info,
+see: \fIhttp://www.mozilla.org/unix/remote.html\fR
+.TP
+.B \-silent
+Don't open default windows. Useful with those command-line arguments that open
+their own windows but don't already prevent default windows from opening.
+
+.SH VERSION
+@MOZ_APP_VERSION@
+
+.SH BUGS
+To report a bug, please visit \fIhttps://bugzilla.mozilla.org/\fR
+
+.SH "SEE ALSO"
+.BR firefox (1),
+.BR thunderbird (1)
+
+.SH AUTHORS
+.TP
+.B The @MOZ_APP_DISPLAYNAME@ Team.
+.I https://www.seamonkey-project.org
diff --git a/comm/suite/app/splash.rc b/comm/suite/app/splash.rc
new file mode 100644
index 0000000000..05bad2f615
--- /dev/null
+++ b/comm/suite/app/splash.rc
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <windows.h>
+#include "nsNativeAppSupportWin.h"
+
+1 24 "seamonkey.exe.manifest"
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STARTMENU_APPNAME, "@MOZ_APP_DISPLAYNAME@"
+END
+
+// Program icon.
+IDI_APPLICATION ICON SEAMONKEY_ICO
+
+// For some reason IDI_MAILBIFF needs to be larger than the value of IDI_APPLICATION for static builds
+#define IDI_MAILBIFF 32576
+IDI_MAILBIFF ICON "../branding/seamonkey/icons/windows/newmail.ico"
+
+#define IDI_HTMLFILE 32577
+IDI_HTMLFILE ICON HTML_FILE_ICO
diff --git a/comm/suite/base/content/about.js b/comm/suite/base/content/about.js
new file mode 100644
index 0000000000..15e3ebad4d
--- /dev/null
+++ b/comm/suite/base/content/about.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+window.onload = function () {
+ // get release notes URL and vendor URL from prefs
+ var releaseNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
+ if (releaseNotesURL != "about:blank") {
+ var relnotes = document.getElementById("releaseNotesURL");
+ relnotes.href = releaseNotesURL;
+ }
+
+ var vendorURL = Services.urlFormatter.formatURLPref("app.vendorURL");
+ if (vendorURL != "about:blank") {
+ var vendor = document.getElementById("vendorURL");
+ vendor.href = vendorURL;
+ }
+
+ // append the version of the XUL application (!= XULRunner platform version)
+ var versionNum = AppConstants.MOZ_APP_VERSION_DISPLAY;
+ var version = document.getElementById("version");
+ version.appendChild(document.createTextNode(versionNum));
+
+ // append user agent
+ var ua = navigator.userAgent;
+ if (ua) {
+ var uaItem = document.getElementById("userAgent");
+ uaItem.appendChild(document.createTextNode(ua));
+ uaItem.hidden = false;
+ }
+
+ // append build identifier
+ var buildId = Services.appinfo.appBuildID;
+ if (buildId) {
+ var buildItem = document.getElementById("buildID");
+ buildItem.appendChild(document.createTextNode(buildId));
+ buildItem.hidden = false;
+ }
+
+ // Determine and display current channel.
+ document.getElementById("currentChannel").textContent =
+ Services.prefs.getDefaultBranch("").getCharPref("app.update.channel");
+}
diff --git a/comm/suite/base/content/about.xhtml b/comm/suite/base/content/about.xhtml
new file mode 100644
index 0000000000..7303f040a8
--- /dev/null
+++ b/comm/suite/base/content/about.xhtml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+%globalDTD;
+<!ENTITY % suiteAboutDTD SYSTEM "chrome://communicator/locale/about.dtd" >
+%suiteAboutDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>About:</title>
+ <link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
+ <script src="chrome://communicator/content/about.js"/>
+</head>
+
+<body dir="&locale.dir;">
+ <div id="aboutLogoContainer">
+ <a id="vendorURL" href="http://www.seamonkey-project.org/">
+ <img src="about:logo" alt="&brandShortName;"/>
+ <p id="version">&about.version; </p>
+ </a>
+ </div>
+
+ <ul id="aboutPageList">
+ <li>&channel.description.start;<strong id="currentChannel"/>&channel.description.end;</li>
+ <li>&about.credits.beforeLink;<a href="about:credits">&about.credits.linkTitle;</a>&about.credits.afterLink;</li>
+ <li>&about.license.beforeTheLink;<a href="about:license">&about.license.linkTitle;</a>&about.license.afterTheLink;</li>
+ <li>&about.relnotes.beforeTheLink;<a id="releaseNotesURL" href="">&about.relnotes.linkTitle;</a>&about.relnotes.afterTheLink;</li>
+ <li>&about.buildconfig.beforeTheLink;<a href="about:buildconfig">&about.buildconfig.linkTitle;</a>&about.buildconfig.afterTheLink;</li>
+ <li id="userAgent" hidden="true">&about.userAgent;</li>
+ <li id="buildID" hidden="true">&about.buildIdentifier;</li>
+ </ul>
+
+</body>
+</html>
diff --git a/comm/suite/base/content/aboutLife.xhtml b/comm/suite/base/content/aboutLife.xhtml
new file mode 100644
index 0000000000..bd61f40f44
--- /dev/null
+++ b/comm/suite/base/content/aboutLife.xhtml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Life, the Universe, and Everything</title>
+
+ <script>
+ function load()
+ {
+ document.getElementById("magic").textContent = Math.sqrt(1764);
+ }
+ </script>
+
+ <style>
+ html {
+ height: 100%;
+ width: 100%;
+ background-color: #B0D8FF;
+ background-image: radial-gradient(circle closest-side, #72B9FF, #B0D8FF);
+ }
+
+ body {
+ margin: 0;
+ height: 100%;
+ width: 100%;
+ display: -moz-box;
+ -moz-box-pack: center;
+ -moz-box-align: center;
+ }
+
+ p:empty {
+ font-size: 16px;
+ color: #72B9FF;
+ text-shadow: none;
+ transform: rotate(0deg);
+ }
+
+ p {
+ font-family: arial;
+ font-weight: bold;
+ margin: 0;
+ transition-property: font-size, color, text-shadow, transform;
+ transition-duration: 6s;
+ transition-timing-function: ease;
+ font-size: 200px;
+ color: #0303E4;
+ text-shadow: #333333 3px 3px 3px;
+ transform: rotate(2160deg);
+ }
+
+ p:hover {
+ transition-duration: 1s;
+ font-size: 300px;
+ color: #2727E8;
+ transform: rotate(2520deg);
+ }
+ </style>
+
+</head>
+
+<body onload="load();">
+ <p id="magic"/>
+</body>
+
+</html>
diff --git a/comm/suite/base/content/aboutPrivateBrowsing.css b/comm/suite/base/content/aboutPrivateBrowsing.css
new file mode 100644
index 0000000000..14f610a5e4
--- /dev/null
+++ b/comm/suite/base/content/aboutPrivateBrowsing.css
@@ -0,0 +1,12 @@
+/* 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/. */
+
+#warningBox:not(.private) .private,
+#warningBox:not(.normal) .normal {
+ display: none;
+}
+
+.title {
+ background-size: auto;
+}
diff --git a/comm/suite/base/content/aboutPrivateBrowsing.js b/comm/suite/base/content/aboutPrivateBrowsing.js
new file mode 100644
index 0000000000..202bc4dbc8
--- /dev/null
+++ b/comm/suite/base/content/aboutPrivateBrowsing.js
@@ -0,0 +1,33 @@
+/* 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/. */
+
+window.onload = function () {
+ if (window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext)
+ .usePrivateBrowsing) {
+ document.getElementById("warningBox").className = "private";
+ document.title = document.getElementById("privateTitle").textContent;
+ }
+ else {
+ document.getElementById("warningBox").className = "normal";
+ document.title = document.getElementById("normalTitle").textContent;
+ }
+
+ document.getElementById("learnMoreButton")
+ .addEventListener("command", function() {
+ openHelp("private-browsing",
+ "chrome://communicator/locale/help/suitehelp.rdf");
+ });
+
+ document.getElementById("closeWindowButton")
+ .addEventListener("command", function() {
+ window.close();
+ });
+
+ document.getElementById("privateWindowButton")
+ .addEventListener("command", function() {
+ openNewPrivateWith(location.href);
+ });
+}
diff --git a/comm/suite/base/content/aboutPrivateBrowsing.xul b/comm/suite/base/content/aboutPrivateBrowsing.xul
new file mode 100644
index 0000000000..30361ef38f
--- /dev/null
+++ b/comm/suite/base/content/aboutPrivateBrowsing.xul
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/aboutPrivateBrowsing.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/aboutPrivateBrowsing.css" type="text/css"?>
+
+<!DOCTYPE window [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % aboutPrivateBrowsingDTD SYSTEM "chrome://communicator/locale/aboutPrivateBrowsing.dtd" >
+ %aboutPrivateBrowsingDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="warningScreen"
+ align="center">
+
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://communicator/content/tasksOverlay.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script src="chrome://communicator/content/aboutPrivateBrowsing.js"/>
+
+ <spacer flex="1"/>
+ <hbox id="warningBox" align="start">
+ <image id="warningBoxIcon"/>
+ <vbox id="warningOuterBox">
+ <vbox id="warningTitle">
+ <label id="privateTitle" class="private">&privatebrowsingpage.title.private;</label>
+ <label id="normalTitle" class="normal">&privatebrowsingpage.title.normal;</label>
+ </vbox>
+ <vbox id="warningStatus">
+ <label class="private">&privatebrowsingpage.status.private;</label>
+ <label class="normal">&privatebrowsingpage.status.normal;</label>
+ </vbox>
+ <vbox id="warningInnerBox" align="start">
+ <description id="warningText">&privatebrowsingpage.common.description;</description>
+ <hbox id="trackWarnBox" class="private">
+ <image id="trackWarnIcon"/>
+ <description flex="1">&privatebrowsingpage.track.warn;</description>
+ </hbox>
+ <button id="learnMoreButton"
+ label="&privatebrowsingpage.learnmore.label;"
+ accesskey="&privatebrowsingpage.learnmore.accesskey;"/>
+ <description class="private">&privatebrowsingpage.close.info;</description>
+ <button id="closeWindowButton"
+ class="private"
+ label="&privatebrowsingpage.close.label;"
+ accesskey="&privatebrowsingpage.close.accesskey;"/>
+ <description class="normal">&privatebrowsingpage.start.info;</description>
+ <button id="privateWindowButton"
+ class="normal"
+ label="&privatebrowsingpage.private.label;"
+ accesskey="&privatebrowsingpage.private.accesskey;"/>
+ </vbox>
+ </vbox>
+ </hbox>
+ <spacer flex="2"/>
+</window>
diff --git a/comm/suite/base/content/askViewZoom.js b/comm/suite/base/content/askViewZoom.js
new file mode 100644
index 0000000000..6a3f903d6b
--- /dev/null
+++ b/comm/suite/base/content/askViewZoom.js
@@ -0,0 +1,43 @@
+/* -*- 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/. */
+
+var dialog;
+var args;
+
+function onLoad() {
+ args = window.arguments[0];
+ args.zoomOK = false;
+
+ dialog = {};
+ dialog.OKButton = document.documentElement.getButton("accept");
+
+ dialog.input = document.getElementById("zoomValue");
+ dialog.input.value = args.value;
+ dialog.input.select();
+ dialog.input.focus();
+
+ doEnabling();
+}
+
+function onAccept() {
+ var zoom = parseFloat(dialog.input.value);
+ if (!isNaN(zoom) && zoom >= args.zoomMin && zoom <= args.zoomMax) {
+ args.value = zoom;
+ args.zoomOK = true;
+ }
+ return args.zoomOK;
+}
+
+function doEnabling() {
+ var enable = false;
+ if (dialog.input.value) {
+ var zoom = parseFloat(dialog.input.value);
+ if (!isNaN(zoom) && zoom >= args.zoomMin && zoom <= args.zoomMax)
+ enable = true;
+ }
+
+ dialog.OKButton.disabled = !enable;
+}
diff --git a/comm/suite/base/content/askViewZoom.xul b/comm/suite/base/content/askViewZoom.xul
new file mode 100644
index 0000000000..5f38270d15
--- /dev/null
+++ b/comm/suite/base/content/askViewZoom.xul
@@ -0,0 +1,27 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/askViewZoom.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="askViewZoom"
+ title="&askViewZoom.title;"
+ ondialogaccept="return onAccept();"
+ onload="onLoad();">
+
+ <script src="chrome://communicator/content/askViewZoom.js"/>
+
+ <hbox>
+ <label value="&selectZoom.label;" control="zoomValue"/>
+ </hbox>
+
+ <hbox>
+ <textbox id="zoomValue" oninput="doEnabling();"/>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/base/content/blockedSite.js b/comm/suite/base/content/blockedSite.js
new file mode 100644
index 0000000000..4a3cf0d69e
--- /dev/null
+++ b/comm/suite/base/content/blockedSite.js
@@ -0,0 +1,126 @@
+/* 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/. */
+
+// Error url MUST be formatted like this:
+// about:blocked?e=error_code&u=url
+
+// Note that this file uses document.documentURI to get
+// the URL (with the format from above). This is because
+// document.location.href gets the current URI off the docshell,
+// which is the URL displayed in the location bar, i.e.
+// the URI that the user attempted to load.
+
+// initializing the page in this way, window.onload won't work here
+
+initPage();
+
+function getErrorCode()
+{
+ var url = document.documentURI;
+ var error = url.indexOf("e=");
+ var duffUrl = url.indexOf("&u=");
+ return url.slice(error + 2, duffUrl);
+}
+
+function getURL()
+{
+ var url = document.documentURI;
+ var match = url.match(/&u=([^&]+)&/);
+
+ // match == null if not found; if so, return an empty string
+ // instead of what would turn out to be portions of the URI
+ if (!match)
+ return "";
+
+ url = decodeURIComponent(match[1]);
+
+ // If this is a view-source page, then get then real URI of the page
+ if (url.startsWith("view-source:"))
+ url = url.slice(12);
+ return url;
+}
+
+ /**
+ * Check whether this warning page should be overridable or whether
+ * the "ignore warning" button should be hidden.
+ */
+ function getOverride()
+ {
+ var url = document.documentURI;
+ return /&o=1&/.test(url);
+ }
+
+/**
+ * Attempt to get the hostname via document.location. Fail back
+ * to getURL so that we always return something meaningful.
+ */
+function getHostString()
+{
+ try {
+ return document.location.hostname;
+ } catch (e) {
+ return getURL();
+ }
+}
+
+function deleteElement(element) {
+ var el = document.getElementById(element);
+ if (el)
+ el.remove();
+}
+
+function initPage()
+{
+ // Handoff to the appropriate initializer, based on error code
+ var error = "";
+ switch (getErrorCode()) {
+ case "malwareBlocked":
+ error = "malware";
+ break;
+ case "deceptiveBlocked":
+ error = "phishing";
+ break;
+ case "unwantedBlocked":
+ error = "unwanted";
+ break;
+ case "harmfulBlocked":
+ error = "harmful";
+ break;
+ default:
+ return;
+ }
+
+ if (error != "malware") {
+ deleteElement("errorTitleText_malware");
+ deleteElement("errorShortDescText_malware");
+ deleteElement("errorLongDescText_malware");
+ }
+
+ if (error != "phishing") {
+ deleteElement("errorTitleText_phishing");
+ deleteElement("errorShortDescText_phishing");
+ deleteElement("errorLongDescText_phishing");
+ }
+
+ if (error != "unwanted") {
+ deleteElement("errorTitleText_unwanted");
+ deleteElement("errorShortDescText_unwanted");
+ deleteElement("errorLongDescText_unwanted");
+ }
+
+ if (error != "harmful") {
+ deleteElement("errorTitleText_harmful");
+ deleteElement("errorShortDescText_harmful");
+ deleteElement("errorLongDescText_harmful");
+ }
+
+ // Set sitename
+ document.getElementById(error + "_sitename").textContent = getHostString();
+ document.title = document.getElementById("errorTitleText_" + error)
+ .innerHTML;
+
+ if (!getOverride())
+ deleteElement("ignoreWarningButton");
+}
+
diff --git a/comm/suite/base/content/blockedSite.xhtml b/comm/suite/base/content/blockedSite.xhtml
new file mode 100644
index 0000000000..40cce3eb49
--- /dev/null
+++ b/comm/suite/base/content/blockedSite.xhtml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % blockedSiteDTD SYSTEM "chrome://communicator/locale/safeBrowsing.dtd">
+ %blockedSiteDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml" class="blacklist">
+ <head>
+ <link rel="stylesheet" href="chrome://communicator/content/certError.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="chrome://communicator/skin/blockedSite.css" type="text/css" media="all" />
+ <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/blacklist_favicon.png"/>
+ </head>
+
+ <body dir="&locale.dir;">
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText_phishing">&safeb.blocked.phishingPage.title2;</h1>
+ <h1 id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1>
+ <h1 id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1>
+ <h1 id="errorTitleText_harmful">&safeb.blocked.harmfulPage.title;</h1>
+ </div>
+
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc2;</p>
+ <p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
+ <p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
+ <p id="errorShortDescText_harmful">&safeb.blocked.harmfulPage.shortDesc;</p>
+ </div>
+
+ <!-- Long Description -->
+ <div id="errorLongDesc">
+ <p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc2;</p>
+ <p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
+ <p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
+ <p id="errorLongDescText_harmful">&safeb.blocked.harmfulPage.longDesc;</p>
+ </div>
+
+ <!-- Action buttons -->
+ <div id="buttons">
+ <!-- Commands handled in utilityOverlay.js -->
+ <span id="getMeOutOfHereButton"
+ class="button"
+ label="&safeb.palm.accept.label;"/>
+ <span id="reportButton"
+ class="button"
+ label="&safeb.palm.reportPage.label;"/>
+ <span id="ignoreWarningButton"
+ class="button"
+ label="&safeb.palm.decline.label;"/>
+ </div>
+ </div>
+ </div>
+ <!--
+ - Note: It is important to run the script this way, instead of using
+ - a window.onload function. This is because error pages are loaded as
+ - LOAD_BACKGROUND, which means that onload handlers will not be executed.
+ -->
+ <script src="chrome://communicator/content/blockedSite.js"/>
+ </body>
+</html>
diff --git a/comm/suite/base/content/certError.css b/comm/suite/base/content/certError.css
new file mode 100644
index 0000000000..93dc0a4cf0
--- /dev/null
+++ b/comm/suite/base/content/certError.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+.button {
+ -moz-binding: url("chrome://communicator/content/certError.xml#button");
+}
diff --git a/comm/suite/base/content/certError.js b/comm/suite/base/content/certError.js
new file mode 100644
index 0000000000..96a89b0685
--- /dev/null
+++ b/comm/suite/base/content/certError.js
@@ -0,0 +1,181 @@
+/* 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/. */
+
+// The following parameters are parsed from the error URL:
+// e - the error code
+// s - custom CSS class to allow alternate styling/favicons
+// d - error description
+
+// Note that this file uses document.documentURI to get
+// the URL (with the format from above). This is because
+// document.location.href gets the current URI off the docshell,
+// which is the URL displayed in the location bar, i.e.
+// the URI that the user attempted to load.
+
+// setting up the event listeners and initializing the page
+// in this way given that window.onload won't work here
+
+document.getElementById("technicalContentHeading")
+ .addEventListener("click", function() { toggle("technicalContent"); });
+
+document.getElementById("expertContentHeading")
+ .addEventListener("click", function() { toggle("expertContent"); });
+
+let gSearchParams;
+
+// Set to true on init if the error code is nssBadCert.
+let gIsCertError;
+
+// Set to true on init if a neterror.
+let gIsNetError;
+
+initPage();
+
+function retryThis(buttonEl) {
+ // Note: The application may wish to handle switching off "offline mode"
+ // before this event handler runs, but using a capturing event handler.
+
+ // Session history has the URL of the page that failed
+ // to load, not the one of the error page. So, just call
+ // reload(), which will also repost POST data correctly.
+ try {
+ location.reload();
+ } catch (e) {
+ // We probably tried to reload a URI that caused an exception to
+ // occur; e.g. a nonexistent file.
+ }
+
+ buttonEl.disabled = true;
+}
+
+function initPage() {
+ gSearchParams = new URLSearchParams(document.documentURI.split("?")[1]);
+
+ let err = gSearchParams.get("e");
+ // List of neterror pages which have no error code and
+ // could have an illustration instead.
+ let illustratedErrors = [
+ "malformedURI", "dnsNotFound", "connectionFailure", "netInterrupt",
+ "netTimeout", "netReset", "netOffline",
+ ];
+ if (illustratedErrors.includes(err)) {
+ document.body.classList.add("illustrated", err);
+ }
+
+ gIsCertError = (err == "nssBadCert");
+ gIsNetError = (document.documentURI.startsWith("about:neterror"));
+
+ let pageTitle = document.getElementById("ept_" + err);
+ if (pageTitle) {
+ document.title = pageTitle.textContent;
+ }
+
+ // If it's an unknown error or there's no title or description defined,
+ // get the generic message.
+ let errTitle = document.getElementById("et_" + err);
+ let errDesc = document.getElementById("ed_" + err);
+ if (!errTitle || !errDesc) {
+ errTitle = document.getElementById("et_generic");
+ errDesc = document.getElementById("ed_generic");
+ }
+
+ let title = document.getElementById("errorTitleText");
+ if (title) {
+ title.innerHTML = errTitle.innerHTML;
+ }
+
+ let sd = document.getElementById("errorShortDescText");
+ if (sd) {
+ if (gIsCertError) {
+ sd.innerHTML = errDesc.innerHTML;
+ } else if (!err || err == "unknownProtocolFound") {
+ sd.remove();
+ }
+ }
+
+ let xd = document.getElementById("errorShortDescExtra");
+ if (xd) {
+ let errExtra = document.getElementById("ex_" + err);
+ if (gIsCertError && errExtra) {
+ xd.innerHTML = errExtra.innerHTML;
+ } else {
+ xd.remove();
+ }
+ }
+
+ let ld = document.getElementById("errorLongDesc");
+ if (ld && !gIsCertError) {
+ ld.innerHTML = errDesc.innerHTML;
+ }
+
+ // Remove undisplayed errors to avoid bug 39098.
+ let errContainer = document.getElementById("errorContainer");
+ errContainer.remove();
+
+ if (gIsCertError || err == "inadequateSecurityError") {
+ for (let host of document.querySelectorAll(".hostname")) {
+ host.textContent = location.host;
+ }
+ }
+
+ if (gIsCertError || err == "sslv3Used") {
+ document.body.classList.add("certerror");
+ }
+
+ if (gIsCertError || err == "remoteXUL" || err == "cspBlocked" ||
+ err == "inadequateSecurityError") {
+ // Remove the "Try again" button for certificate errors, remote XUL errors,
+ // CSP violations (Bug 553180) and HTTP/2 inadequate security,
+ // given that it is useless.
+ document.getElementById("netErrorButtonContainer").style.display = "none";
+ }
+
+ let className = gSearchParams.get("s");
+ if (className && className != "expertBadCert") {
+ // Associate a CSS class with the root of the page, if one was passed in,
+ // to allow custom styling.
+ // Not "expertBadCert" though, don't want to deal with the favicon
+ document.documentElement.classList.add(className);
+ }
+
+ if (className == "expertBadCert") {
+ toggle("technicalContent");
+ toggle("expertContent");
+ }
+
+ // Disallow overrides if this is a Strict-Transport-Security
+ // host and the cert is bad (STS Spec section 7.3);
+ // or if the cert error is in a frame (bug 633691).
+ if (className == "badStsCert" || window != top || !gIsCertError) {
+ let expertContent = document.getElementById("expertContent");
+ expertContent.remove();
+ }
+ if (className == "badStsCert") {
+ document.getElementById("badStsCertExplanation").removeAttribute("hidden");
+ }
+
+ // For neterrors set a suitable class.
+ if (gIsNetError) {
+ document.body.classList.add("neterror");
+ }
+
+ // For neterrors and null error codes do not show the What Should I Do and
+ // Technical Details sections.
+ if (gIsNetError || !err) {
+ let whatShould = document.getElementById("whatShouldIDoContent");
+ whatShould.remove();
+ let technicalContent = document.getElementById("technicalContent");
+ technicalContent.remove();
+ }
+ var event = new CustomEvent("AboutNetAndCertErrorLoad", {bubbles: true});
+ document.dispatchEvent(event);
+}
+
+function toggle(id) {
+ var el = document.getElementById(id);
+ if (el.hasAttribute("collapsed"))
+ el.removeAttribute("collapsed");
+ else
+ el.setAttribute("collapsed", true);
+}
diff --git a/comm/suite/base/content/certError.xhtml b/comm/suite/base/content/certError.xhtml
new file mode 100644
index 0000000000..e63f5d1844
--- /dev/null
+++ b/comm/suite/base/content/certError.xhtml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % htmlDTD
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
+ %netErrorDTD;
+ <!ENTITY % globalDTD
+ SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % certerrorDTD
+ SYSTEM "chrome://communicator/locale/certError.dtd">
+ %certerrorDTD;
+]>
+
+<!-- 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/. -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&loadError.label;</title>
+ <link rel="stylesheet" href="chrome://communicator/content/certError.css" type="text/css" media="all" />
+ <link rel="stylesheet" href="chrome://communicator/skin/certError.css" type="text/css" media="all" />
+ <link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
+ </head>
+
+ <body dir="&locale.dir;">
+
+ <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
+ <div id="errorContainer" style="display: none;">
+ <div id="errorPageTitlesContainer">
+ <span id="ept_nssBadCert">&certerror.pagetitle;</span>
+ <span id="ept_dnsNotFound">&dnsNotFound.pageTitle;</span>
+ <span id="ept_malformedURI">&malformedURI.pageTitle;</span>
+ </div>
+ <div id="errorTitlesContainer">
+ <h1 id="et_generic">&generic.title;</h1>
+ <h1 id="et_dnsNotFound">&dnsNotFound.title;</h1>
+ <h1 id="et_fileNotFound">&fileNotFound.title;</h1>
+ <h1 id="et_fileAccessDenied">&fileAccessDenied.title;</h1>
+ <h1 id="et_malformedURI">&malformedURI.title;</h1>
+ <h1 id="et_unknownProtocolFound">&unknownProtocolFound.title;</h1>
+ <h1 id="et_connectionFailure">&connectionFailure.title;</h1>
+ <h1 id="et_netTimeout">&netTimeout.title;</h1>
+ <h1 id="et_redirectLoop">&redirectLoop.title;</h1>
+ <h1 id="et_unknownSocketType">&unknownSocketType.title;</h1>
+ <h1 id="et_netReset">&netReset.title;</h1>
+ <h1 id="et_notCached">&notCached.title;</h1>
+ <h1 id="et_netOffline">&netOffline.title;</h1>
+ <h1 id="et_netInterrupt">&netInterrupt.title;</h1>
+ <h1 id="et_deniedPortAccess">&deniedPortAccess.title;</h1>
+ <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
+ <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
+ <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
+ <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
+ <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
+ <h1 id="et_nssBadCert">&certerror.longpagetitle;</h1>
+ <h1 id="et_cspBlocked">&cspBlocked.title;</h1>
+ <h1 id="et_remoteXUL">&remoteXUL.title;</h1>
+ <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
+ <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
+ </div>
+ <div id="errorDescriptionsContainer">
+ <div id="ed_generic">&generic.longDesc;</div>
+ <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
+ <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
+ <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
+ <div id="ed_malformedURI">&malformedURI.longDesc;</div>
+ <div id="ed_unknownProtocolFound">&unknownProtocolFound.longDesc;</div>
+ <div id="ed_connectionFailure">&connectionFailure.longDesc;</div>
+ <div id="ed_netTimeout">&netTimeout.longDesc;</div>
+ <div id="ed_redirectLoop">&redirectLoop.longDesc;</div>
+ <div id="ed_unknownSocketType">&unknownSocketType.longDesc;</div>
+ <div id="ed_netReset">&netReset.longDesc;</div>
+ <div id="ed_notCached">&notCached.longDesc;</div>
+ <div id="ed_netOffline">&netOffline.longDesc2;</div>
+ <div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
+ <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
+ <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
+ <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
+ <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
+ <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
+ <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
+ <div id="ed_nssBadCert">&certerror.introPara1a;</div>
+ <div id="ex_nssBadCert">&certerror.introPara2;</div>
+ <div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
+ <div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
+ <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
+ <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
+ </div>
+ </div>
+
+ <!-- PAGE CONTAINER (for styling purposes only) -->
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText"/>
+ </div>
+
+ <!-- LONG CONTENT (the section most likely to require scrolling) -->
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText"/>
+ <p id="errorShortDescExtra"/>
+ </div>
+
+ <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
+ <div id="errorLongDesc"/>
+
+ <div id="whatShouldIDoContent">
+ <h2>&certerror.whatShouldIDo.heading;</h2>
+ <div id="whatShouldIDoContentText">
+ <p>&certerror.whatShouldIDo.content;</p>
+ <p id="badStsCertExplanation"
+ hidden="true">&certerror.whatShouldIDo.badStsCertExplanation;</p>
+ <span id="getMeOutOfHereButton"
+ class="button"
+ label="&certerror.getMeOutOfHere.label;"/>
+ </div>
+ </div>
+
+ <!-- The following sections can be unhidden by default by setting the
+ "browser.xul.error_pages.expert_bad_cert" pref to true -->
+ <div id="technicalContent" collapsed="true">
+ <h2 id="technicalContentHeading">&certerror.technical.heading;</h2>
+ <p id="technicalContentText"/>
+ </div>
+
+ <div id="expertContent" collapsed="true">
+ <h2 id="expertContentHeading">&certerror.expert.heading;</h2>
+ <div>
+ <p>&certerror.expert.content;</p>
+ <p>&certerror.expert.contentPara2;</p>
+ <span id="exceptionDialogButton"
+ class="button"
+ label="&certerror.addException.label;"/>
+ </div>
+ </div>
+
+ <div id="netErrorButtonContainer" class="button-container">
+ <button id="errorTryAgain"
+ class="primary"
+ autocomplete="off"
+ onclick="retryThis(this);">&retry.label;</button>
+ </div>
+ </div>
+ </div>
+
+ <!--
+ - Note: It is important to run the script this way, instead of using
+ - a window.onload function. This is because error pages are loaded as
+ - LOAD_BACKGROUND, which means that onload handlers will not be executed.
+ -->
+ <script src="chrome://communicator/content/certError.js"/>
+
+ </body>
+</html>
diff --git a/comm/suite/base/content/certError.xml b/comm/suite/base/content/certError.xml
new file mode 100644
index 0000000000..c4fbe12f01
--- /dev/null
+++ b/comm/suite/base/content/certError.xml
@@ -0,0 +1,18 @@
+<?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/. -->
+
+<bindings id="certErrorBindings"
+ 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="button" bindToUntrustedContent="true">
+ <content>
+ <xul:button xbl:inherits="anonid=id,label"/>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/base/content/charsetOverlay.xul b/comm/suite/base/content/charsetOverlay.xul
new file mode 100644
index 0000000000..6f318a1d6b
--- /dev/null
+++ b/comm/suite/base/content/charsetOverlay.xul
@@ -0,0 +1,24 @@
+<?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 SYSTEM "chrome://global/locale/charsetMenu.dtd">
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script>
+ ChromeUtils.import("resource://gre/modules/CharsetMenu.jsm");
+
+ function UpdateCharsetMenu(aCharset, aNode)
+ {
+ var bundle = document.getElementById("charsetBundle");
+ CharsetMenu.update(aNode, bundle.getString(aCharset.toLowerCase()));
+ }
+ </script>
+
+ <menu id="charsetMenu"
+ label="&charsetMenu2.label;"
+ accesskey="&charsetMenu2.accesskey;">
+ <menupopup id="charsetPopup"
+ onpopupshowing="CharsetMenu.build(this, true, this.getAttribute('detectors') != 'false');"/>
+ </menu>
+</overlay>
diff --git a/comm/suite/base/content/communicator.css b/comm/suite/base/content/communicator.css
new file mode 100644
index 0000000000..6ec458f0df
--- /dev/null
+++ b/comm/suite/base/content/communicator.css
@@ -0,0 +1,329 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: print preview toolbar ::::: */
+
+toolbar[printpreview="true"] {
+ -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
+}
+
+/* ::::: autocomplete textbox ::::: */
+
+textbox[type="autocomplete"] {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete");
+}
+
+panel[type="autocomplete"] {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete-result-popup");
+}
+
+.autocomplete-history-popup {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete-history-popup");
+}
+
+.autocomplete-treebody {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#autocomplete-treebody");
+}
+
+panel[type="autocomplete-richlistbox"] {
+ -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup");
+}
+
+.autocomplete-richlistbox {
+ -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistbox");
+ -moz-user-focus: ignore;
+}
+
+.autocomplete-richlistbox > scrollbox {
+ overflow-x: hidden !important;
+}
+
+.autocomplete-history-dropmarker {
+ -moz-binding: url("chrome://global/content/autocomplete.xml#history-dropmarker");
+}
+
+.autocomplete-richlistitem {
+ -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-richlistitem");
+ -moz-box-orient: vertical;
+ overflow: -moz-hidden-unscrollable;
+}
+
+/* ::::: notification box ::::: */
+
+.browser-notificationbox {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#browser-notificationbox");
+}
+
+.browser-notificationbox[popupnotification="true"] {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#popup-notification");
+}
+
+notification[value="addon-install-started"] {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#addon-progress-notification");
+}
+
+/* ::::: toolbaritem ::::: */
+toolbaritem {
+ -moz-binding: url("chrome://global/content/bindings/general.xml#basecontrol");
+}
+
+/* With the move to the new toolkit, SeaMonkey needs to overwrite certain bindings
+ * if it wants to keep its distinctive likeness. The now hidden new toolkit bindings
+ * will stay accessible via a set xpfe="false" attribute, though, where necessary.
+ */
+
+/******* toolkit access layer *******/
+/* These rules reintroduce the toolkit bindings overwritten later below */
+toolbox[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#toolbox");
+}
+
+toolbox[xpfe="false"] > toolbar,
+toolbar[xpfe="false"][type="menubar"],
+toolbar[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#toolbar");
+}
+
+menubar[xpfe="false"],
+toolbar > toolbaritem > menubar,
+toolbar > menubar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#menubar");
+}
+
+toolbar > toolbarpaletteitem > toolbaritem > menubar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#menubar") !important;
+}
+
+.menubar-items {
+ -moz-box-orient: vertical; /* for flex hack */
+}
+
+.menubar-items > menubar {
+ -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
+}
+
+prefwindow,
+prefwindow:root,
+prefpane {
+ -moz-box-orient: vertical;
+}
+
+prefwindow[type="child"] > .paneDeckContainer {
+ overflow: -moz-hidden-unscrollable;
+}
+
+prefwindow[type="child"] > prefpane > .content-box {
+ -moz-box-flex: 1;
+ overflow: -moz-hidden-unscrollable;
+}
+
+preferences {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#preferences");
+ visibility: collapse;
+}
+
+preference {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#preference");
+ visibility: collapse;
+}
+
+radio[pane] {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#panebutton") !important;
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+}
+
+prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar {
+ display: none;
+}
+
+prefwindow[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#prefwindow");
+}
+
+prefpane[xpfe="false"] {
+ -moz-binding: url("chrome://communicator/content/bindings/preferences.xml#prefpane");
+}
+
+findbar[xpfe="false"] {
+ -moz-binding: url("chrome://global/content/bindings/findbar.xml#findbar");
+}
+
+prefwindow[xpfe="false"] > .paneDeckContainer,
+prefpane[xpfe="false"] > .content-box {
+ overflow: hidden;
+}
+
+/******* SeaMonkey XPFE *******/
+/* These bindings reflect SeaMonkey XPFE, modulo new toolkit features. */
+toolbox {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbox");
+}
+
+toolbar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar");
+}
+
+toolbar[type="menubar"] {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-menubar");
+}
+
+toolbargrippy {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#toolbargrippy");
+}
+
+menubar {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippymenubar");
+}
+
+prefwindow {
+ -moz-binding: url("chrome://communicator/content/bindings/prefwindow.xml#prefwindow");
+}
+
+prefpane {
+ -moz-binding: url("chrome://communicator/content/bindings/prefwindow.xml#prefpane");
+}
+
+findbar {
+ -moz-binding: url("chrome://communicator/content/bindings/findbar.xml#findbar");
+}
+
+prefwindow > .paneDeckContainer,
+prefpane > .content-box {
+ overflow: visible;
+}
+
+prefwindow[overflow="auto"] > .paneDeckContainer,
+prefwindow[overflow="auto"] prefpane > .content-box {
+ overflow: auto;
+}
+
+dialogheader {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#dialogheader");
+}
+
+%ifndef MOZ_WIDGET_GTK
+statusbar:not([nowindowdrag="true"]) {
+ -moz-window-dragging: drag;
+}
+%endif
+
+%ifdef XP_MACOSX
+.statusbar-resizerpanel {
+ display: none;
+}
+%else
+window[sizemode="maximized"] statusbarpanel.statusbar-resizerpanel {
+ visibility: collapse;
+}
+%endif
+
+statusbar {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#statusbar");
+%ifdef XP_MACOSX
+ padding-right: 14px;
+%endif
+}
+
+statusbarpanel {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#statusbarpanel");
+}
+
+.statusbarpanel-iconic {
+ -moz-binding: url("chrome://communicator/content/bindings/general.xml#statusbarpanel-iconic");
+}
+
+.statusbarpanel-iconic-text {
+ -moz-binding: url("chrome://communicator/content/bindings/general.xml#statusbarpanel-iconic-text");
+}
+
+.statusbarpanel-backgroundbox {
+ -moz-binding: url("chrome://communicator/content/bindings/general.xml#statusbarpanel-backgroundbox");
+}
+
+textbox[enablehistory="true"] > .autocomplete-history-dropmarker {
+ display: -moz-box;
+}
+
+/******* lightweight themes *******/
+:root:-moz-lwtheme {
+ color: var(--lwt-text-color) !important
+}
+
+/**
+ * [customization-lwtheme] may or may not be used yet; we leave it in
+ * in case it moves to toolkit in the future.
+ */
+:root:-moz-lwtheme:not([customization-lwtheme]) {
+ background-color: var(--lwt-accent-color) !important;
+ background-image: var(--lwt-header-image) !important;
+}
+
+window[lwtheme="true"] {
+ background-repeat: no-repeat;
+ background-position: top right;
+ background-image: var(--lwt-header-image);
+}
+
+:root[lwthemefooter="true"] #status-bar:-moz-lwtheme {
+ background-repeat: no-repeat;
+ background-position: bottom left;
+ background-color: var(--lwt-accent-color);
+ background-image: var(--lwt-header-image);
+}
+
+/******* sync *******/
+#sync-notifications {
+ -moz-binding: url("chrome://communicator/content/sync/syncNotification.xml#notificationbox");
+ overflow-y: visible !important;
+}
+
+#sync-notifications > notification {
+ -moz-binding: url("chrome://communicator/content/sync/syncNotification.xml#notification");
+}
+
+/******* autohide toolbars *******/
+
+toolbar[type="menubar"][autohide="true"]
+{
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar.xml#toolbar-menubar-autohide");
+ overflow: hidden;
+}
+
+toolbar[type="menubar"][autohide="true"][inactive="true"]
+{
+ min-height: 0px !important;
+ height: 0px !important;
+ -moz-appearance: none !important;
+ border-style: none !important;
+}
+
+/******* datepicker *******/
+datepicker {
+ -moz-binding: url("chrome://communicator/content/bindings/datetimepicker.xml#datepicker");
+}
+
+datepicker[type="popup"] {
+ -moz-binding: url("chrome://communicator/content/bindings/datetimepicker.xml#datepicker-popup");
+}
+
+datepicker[type="grid"] {
+ -moz-binding: url("chrome://communicator/content/bindings/datetimepicker.xml#datepicker-grid");
+}
+
+/******* numberbox *******/
+textbox[type="number"] {
+ -moz-binding: url("chrome://communicator/content/bindings/numberbox.xml#numberbox");
+}
+
+/******* spinbuttons *******/
+spinbuttons {
+ -moz-binding: url("chrome://communicator/content/bindings/spinbuttons.xml#spinbuttons");
+}
+
+.spinbuttons-button {
+ -moz-user-focus: ignore;
+}
diff --git a/comm/suite/base/content/contentAreaClick.js b/comm/suite/base/content/contentAreaClick.js
new file mode 100644
index 0000000000..b4c83e991e
--- /dev/null
+++ b/comm/suite/base/content/contentAreaClick.js
@@ -0,0 +1,260 @@
+// /* -*- 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/. */
+
+/*
+ * - [ Dependencies ] ---------------------------------------------------------
+ * utilityOverlay.js:
+ * - gatherTextUnder
+ */
+
+ function hrefAndLinkNodeForClickEvent(event)
+ {
+ var href = "";
+ var isKeyCommand = (event.type == "command");
+ var linkNode = isKeyCommand ? document.commandDispatcher.focusedElement
+ : event.originalTarget;
+
+ while (linkNode instanceof Element) {
+ if (linkNode instanceof HTMLAnchorElement ||
+ linkNode instanceof HTMLAreaElement ||
+ linkNode instanceof HTMLLinkElement) {
+ href = linkNode.href;
+ if (href)
+ break;
+ }
+ // Try MathML href
+ else if (linkNode.namespaceURI == "http://www.w3.org/1998/Math/MathML" &&
+ linkNode.hasAttribute("href")) {
+ href = linkNode.getAttribute("href");
+ href = makeURLAbsolute(linkNode.baseURI, href);
+ break;
+ }
+ // Try simple XLink
+ else if (linkNode.hasAttributeNS("http://www.w3.org/1999/xlink", "href")) {
+ href = linkNode.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+ href = makeURLAbsolute(linkNode.baseURI, href);
+ break;
+ }
+ linkNode = linkNode.parentNode;
+ }
+
+ return href ? {href: href, linkNode: linkNode} : null;
+ }
+
+ // Called whenever the user clicks in the content area,
+ // except when left-clicking on links (special case)
+ // should always return true for click to go through
+ function contentAreaClick(event)
+ {
+ if (!event.isTrusted || event.defaultPrevented) {
+ return true;
+ }
+
+ var isKeyCommand = (event.type == "command");
+ var ceParams = hrefAndLinkNodeForClickEvent(event);
+ if (ceParams) {
+ var href = ceParams.href;
+ if (isKeyCommand) {
+ var doc = event.target.ownerDocument;
+ urlSecurityCheck(href, doc.nodePrincipal);
+ openLinkIn(href, event && event.altKey ? "tabshifted" : "tab",
+ { charset: doc.characterSet,
+ referrerURI: doc.documentURIObject });
+ event.stopPropagation();
+ }
+ else {
+ // if in mailnews block the link left click if we determine
+ // that this URL is phishy (i.e. a potential email scam)
+ if ("gMessengerBundle" in this && event.button < 2 &&
+ isPhishingURL(ceParams.linkNode, false, href))
+ return false;
+ handleLinkClick(event, href, ceParams.linkNode);
+
+ // Mark the page as a user followed link. This is done so that history can
+ // distinguish automatic embed visits from user activated ones. For example
+ // pages loaded in frames are embed visits and lost with the session, while
+ // visits across frames should be preserved.
+ try {
+ PlacesUIUtils.markPageAsFollowedLink(href);
+ } catch (ex) { /* Skip invalid URIs. */ }
+ }
+ return true;
+ }
+
+ if (!isKeyCommand && event.button == 1 &&
+ Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
+ !Services.prefs.getBoolPref("general.autoScroll")) {
+ middleMousePaste(event);
+ }
+
+ return true;
+ }
+
+function handleLinkClick(event, href, linkNode) {
+ if (event.button == 2) // right click
+ return false;
+
+ var where = whereToOpenLink(event);
+ if (where == "current")
+ return false;
+
+ var doc = event.target.ownerDocument;
+
+ if (where == "save") {
+ saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, false,
+ true, doc.documentURIObject, doc);
+ event.preventDefault();
+ return true;
+ }
+
+ var referrerURI = doc.documentURIObject;
+ // if the mixedContentChannel is present and the referring URI passes
+ // a same origin check with the target URI, we can preserve the users
+ // decision of disabling MCB on a page for it's child tabs.
+ var persistAllowMixedContentInChildTab = false;
+
+ if (where == "tab" && getBrowser().docShell.mixedContentChannel) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ var targetURI = makeURI(href);
+ sm.checkSameOriginURI(referrerURI, targetURI, false);
+ persistAllowMixedContentInChildTab = true;
+ }
+ catch (e) { }
+ }
+
+ urlSecurityCheck(href, doc.nodePrincipal);
+ let params = {
+ charset: doc.characterSet,
+ private: gPrivate ? true : false,
+ allowMixedContent: persistAllowMixedContentInChildTab,
+ referrerURI: referrerURI,
+ noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
+ originPrincipal: doc.nodePrincipal,
+ triggeringPrincipal: doc.nodePrincipal,
+ };
+
+ // The new tab/window must use the same userContextId
+ if (doc.nodePrincipal.originAttributes.userContextId) {
+ params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
+ }
+
+ openLinkIn(href, where, params);
+ event.preventDefault();
+ return true;
+}
+
+ function middleMousePaste(event) {
+
+ let clipboard = readFromClipboard();
+
+ if (!clipboard)
+ return;
+
+ // Strip embedded newlines and surrounding whitespace, to match the URL
+ // bar's behavior (stripsurroundingwhitespace).
+ clipboard = clipboard.replace(/\s*\n\s*/g, "");
+
+ clipboard = stripUnsafeProtocolOnPaste(clipboard);
+
+ // If its not the current tab, we don't need to do anything because the
+ // browser doesn't exist.
+ let where = whereToOpenLink(event, true, false);
+ let lastLocationChange;
+ if (where == "current") {
+ lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
+ }
+
+ getShortcutOrURIAndPostData(clipboard).then(data => {
+ try {
+ makeURI(data.url);
+ } catch (ex) {
+ // Not a valid URI.
+ return;
+ }
+
+ try {
+ addToUrlbarHistory(data.url);
+ } catch (ex) {
+ // Things may go wrong when adding url to session history,
+ // but don't let that interfere with the loading of the url.
+ Cu.reportError(ex);
+ }
+
+ if (where != "current" ||
+ lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
+ openUILink(data.url, event,
+ { ignoreButton: true,
+ disallowInheritPrincipal: !data.mayInheritPrincipal });
+ }
+ });
+
+ event.stopPropagation();
+ }
+
+ function stripUnsafeProtocolOnPaste(pasteData) {
+ // Don't allow pasting javascript URIs since we don't support
+ // LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL for those.
+ let changed = false;
+ let pasteDataNoJS = pasteData.replace(/\r?\n/g, "")
+ .replace(/^(?:\s*javascript:)+/i,
+ () => { changed = true;
+ return ""; });
+ return changed ? pasteDataNoJS : pasteData;
+ }
+
+ function addToUrlbarHistory(aUrlToAdd)
+ {
+ if (gPrivate)
+ return;
+
+ if (!Services.prefs.getBoolPref("browser.urlbar.historyEnabled"))
+ return;
+
+ // Remove leading and trailing spaces first.
+ aUrlToAdd = aUrlToAdd.trim();
+
+ if (!aUrlToAdd)
+ return;
+ // Don't store bad URLs.
+ if (aUrlToAdd.search(/[\x00-\x1F]/) != -1)
+ return;
+
+ getShortcutOrURIAndPostData(aUrlToAdd).then(data => {
+ var fixedUpURI = Services.uriFixup.createFixupURI(data.url, 0);
+ if (!fixedUpURI.schemeIs("data"))
+ PlacesUtils.history.markPageAsTyped(fixedUpURI);
+ }).catch(() => {});
+
+ // Open or create the urlbar history database.
+ var file = GetUrlbarHistoryFile();
+ var connection = Services.storage.openDatabase(file);
+ connection.beginTransaction();
+ if (!connection.tableExists("urlbarhistory"))
+ connection.createTable("urlbarhistory", "url TEXT");
+
+ // If the URL is already present in the database then remove it from
+ // its current position. It is then reinserted at the top of the list.
+ var statement = connection.createStatement(
+ "DELETE FROM urlbarhistory WHERE LOWER(url) = LOWER(?1)");
+ statement.bindByIndex(0, aUrlToAdd);
+ statement.execute();
+ statement.finalize();
+
+ // Put the value as it was typed by the user in to urlbar history.
+ statement = connection.createStatement(
+ "INSERT INTO urlbarhistory (url) VALUES (?1)");
+ statement.bindByIndex(0, aUrlToAdd);
+ statement.execute();
+ statement.finalize();
+
+ // Remove any expired history items so that we don't let
+ // this grow without bound.
+ connection.executeSimpleSQL(
+ "DELETE FROM urlbarhistory WHERE ROWID NOT IN " +
+ "(SELECT ROWID FROM urlbarhistory ORDER BY ROWID DESC LIMIT 30)");
+ connection.commitTransaction();
+ connection.close();
+ }
diff --git a/comm/suite/base/content/contentAreaContextOverlay.xul b/comm/suite/base/content/contentAreaContextOverlay.xul
new file mode 100644
index 0000000000..57601b7d94
--- /dev/null
+++ b/comm/suite/base/content/contentAreaContextOverlay.xul
@@ -0,0 +1,401 @@
+<?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 SYSTEM "chrome://communicator/locale/contentAreaCommands.dtd" >
+<overlay id="contentAreaContextOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- Context menu -->
+ <script>
+ // Global variable that holds the nsContextMenu instance.
+ var gContextMenu = null;
+ </script>
+ <script src="chrome://communicator/content/nsContextMenu.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+
+ <stringbundleset>
+ <stringbundle id="contentAreaCommandsBundle"
+ src="chrome://communicator/locale/contentAreaCommands.properties"/>
+ </stringbundleset>
+
+ <popupset id="contentAreaContextSet">
+ <!-- This is a generic context menu for a content area. It contains
+ each and every possible menu choice. The appropriate items are
+ hidden/shown upon display, based on what the user has clicked-on.
+ -->
+ <menupopup id="contentAreaContextMenu"
+ pagemenu="start"
+ onpopupshowing="if (event.target != this) return true; gContextMenu = new nsContextMenu(this, event.shiftKey, event); return gContextMenu.shouldDisplay;"
+ onpopuphiding="if (event.target != this) return; gContextMenu.hiding(); gContextMenu = null;">
+ <menuseparator id="page-menu-separator"/>
+
+ <menuitem id="spell-no-suggestions"
+ disabled="true"
+ label="&spellNoSuggestions.label;"/>
+ <menuseparator id="spell-add-separator"/>
+ <menuitem id="spell-add-to-dictionary"
+ label="&spellAddToDictionary.label;"
+ accesskey="&spellAddToDictionary.accesskey;"
+ oncommand="InlineSpellCheckerUI.addToDictionary();"/>
+ <menuitem id="spell-undo-add-to-dictionary"
+ label="&spellUndoAddToDictionary.label;"
+ accesskey="&spellUndoAddToDictionary.accesskey;"
+ oncommand="InlineSpellCheckerUI.undoAddToDictionary();"/>
+ <menuitem id="spell-ignore-word"
+ label="&spellIgnoreWord.label;"
+ accesskey="&spellIgnoreWord.accesskey;"
+ oncommand="InlineSpellCheckerUI.ignoreWord();"/>
+ <menuseparator id="spell-suggestions-separator"/>
+ <menuitem id="context-openlinkintab"
+ label="&openLinkCmdInTab.label;"
+ accesskey="&openLinkCmdInTab.accesskey;"
+ usercontextid="0"
+ oncommand="gContextMenu.openLinkInTab(event);"/>
+ <menuitem id="context-openlink"
+ label="&openLinkCmd.label;"
+ accesskey="&openLinkCmd.accesskey;"
+ oncommand="gContextMenu.openLinkInWindow();"/>
+ <menuitem id="context-openlinkinprivatewindow"
+ label="&openLinkCmdInPrivateWindow.label;"
+ accesskey="&openLinkCmdInPrivateWindow.accesskey;"
+ oncommand="gContextMenu.openLinkInPrivateWindow();"/>
+ <menuseparator id="context-sep-open"/>
+ <menuitem id="context-bookmarklink"
+ label="&bookmarkLinkCmd.label;"
+ accesskey="&bookmarkLinkCmd.accesskey;"
+ oncommand="gContextMenu.bookmarkLink();"/>
+ <menuitem id="context-savelink"
+ valueSaveAs="&saveLinkAsCmd.label;"
+ valueSave="&saveLinkCmd.label;"
+ accesskey="&saveLinkCmd.accesskey;"
+ oncommand="gContextMenu.saveLink();"/>
+ <menuitem id="context-copyemail"
+ label="&copyEmailCmd.label;"
+ accesskey="&copyEmailCmd.accesskey;"
+ oncommand="gContextMenu.copyEmail();"/>
+ <menuitem id="context-copylink"
+ label="&copyLinkCmd.label;"
+ accesskey="&copyLinkCmd.accesskey;"
+ command="cmd_copyLink"/>
+ <menuseparator id="context-sep-copylink"/>
+ <menuitem id="context-media-play"
+ label="&mediaPlay.label;"
+ accesskey="&mediaPlay.accesskey;"
+ oncommand="gContextMenu.mediaCommand('play');"/>
+ <menuitem id="context-media-pause"
+ label="&mediaPause.label;"
+ accesskey="&mediaPause.accesskey;"
+ oncommand="gContextMenu.mediaCommand('pause');"/>
+ <menuitem id="context-media-mute"
+ label="&mediaMute.label;"
+ accesskey="&mediaMute.accesskey;"
+ oncommand="gContextMenu.mediaCommand('mute');"/>
+ <menuitem id="context-media-unmute"
+ label="&mediaUnmute.label;"
+ accesskey="&mediaUnmute.accesskey;"
+ oncommand="gContextMenu.mediaCommand('unmute');"/>
+ <menu id="context-media-playbackrate"
+ label="&mediaPlaybackRate.label;"
+ accesskey="&mediaPlaybackRate.accesskey;">
+ <menupopup id="media-playbackrate-popup"
+ oncommand="gContextMenu.mediaCommand('playbackRate',
+ event.target.id.replace(/.*-/, '') / 100);">
+ <menuitem id="context-media-playbackrate-050"
+ label="&mediaPlaybackRate050.label;"
+ accesskey="&mediaPlaybackRate050.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-100"
+ label="&mediaPlaybackRate100.label;"
+ accesskey="&mediaPlaybackRate100.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-125"
+ label="&mediaPlaybackRate125.label;"
+ accesskey="&mediaPlaybackRate125.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-150"
+ label="&mediaPlaybackRate150.label;"
+ accesskey="&mediaPlaybackRate150.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ <menuitem id="context-media-playbackrate-200"
+ label="&mediaPlaybackRate200.label;"
+ accesskey="&mediaPlaybackRate200.accesskey;"
+ type="radio"
+ name="playbackrate"/>
+ </menupopup>
+ </menu>
+ <menuitem id="context-media-loop"
+ label="&mediaLoop.label;"
+ accesskey="&mediaLoop.accesskey;"
+ type="checkbox"
+ oncommand="gContextMenu.mediaCommand('loop');"/>
+ <menuitem id="context-media-showcontrols"
+ label="&mediaShowControls.label;"
+ accesskey="&mediaShowControls.accesskey;"
+ oncommand="gContextMenu.mediaCommand('showcontrols');"/>
+ <menuitem id="context-media-hidecontrols"
+ label="&mediaHideControls.label;"
+ accesskey="&mediaHideControls.accesskey;"
+ oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
+ <menuitem id="context-video-showstats"
+ label="&videoShowStats.label;"
+ accesskey="&videoShowStats.accesskey;"
+ oncommand="gContextMenu.mediaCommand('showstats');"/>
+ <menuitem id="context-video-hidestats"
+ label="&videoHideStats.label;"
+ accesskey="&videoHideStats.accesskey;"
+ oncommand="gContextMenu.mediaCommand('hidestats');"/>
+ <menuitem id="context-video-fullscreen"
+ label="&videoFullScreen.label;"
+ accesskey="&videoFullScreen.accesskey;"
+ oncommand="gContextMenu.fullScreenVideo();"/>
+ <menuseparator id="context-media-sep-commands"/>
+ <menuitem id="context-fitimage"
+ type="checkbox"
+ label="&fitImageCmd.label;"
+ accesskey="&fitImageCmd.accesskey;"
+ oncommand="gContextMenu.toggleImageSize();"/>
+ <menuitem id="context-reloadimage"
+ label="&reloadImageCmd.label;"
+ accesskey="&reloadImageCmd.accesskey;"
+ oncommand="gContextMenu.reloadImage();"/>
+ <menuitem id="context-viewimage"
+ label="&viewImageCmd.label;"
+ accesskey="&viewImageCmd.accesskey;"
+ oncommand="gContextMenu.viewMedia(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-copyimage"
+ label="&copyImageCmd.label;"
+ accesskey="&copyImageCmd.accesskey;"
+ command="cmd_copyImage"/>
+ <menuitem id="context-viewvideo"
+ label="&viewVideoCmd.label;"
+ accesskey="&viewVideoCmd.accesskey;"
+ oncommand="gContextMenu.viewMedia(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-copyvideourl"
+ label="&copyVideoURLCmd.label;"
+ accesskey="&copyVideoURLCmd.accesskey;"
+ oncommand="gContextMenu.copyMediaLocation();"/>
+ <menuitem id="context-copyaudiourl"
+ label="&copyAudioURLCmd.label;"
+ accesskey="&copyAudioURLCmd.accesskey;"
+ oncommand="gContextMenu.copyMediaLocation();"/>
+ <menuseparator id="context-sep-copyimage"/>
+ <menuitem id="context-blockimage"
+ oncommand="gContextMenu.toggleImageBlocking(true);"/>
+ <menuitem id="context-unblockimage"
+ oncommand="gContextMenu.toggleImageBlocking(false);"/>
+ <menuseparator id="context-sep-blockimage"/>
+ <menuitem id="context-saveimage"
+ valueSaveAs="&saveImageAsCmd.label;"
+ valueSave="&saveImageCmd.label;"
+ accesskey="&saveImageCmd.accesskey;"
+ oncommand="gContextMenu.saveMedia();"/>
+ <menuitem id="context-setDesktopBackground"
+ label="&setDesktopBackgroundCmd.label;"
+ accesskey="&setDesktopBackgroundCmd.accesskey;"
+ oncommand="gContextMenu.setDesktopBackground();"/>
+ <menuitem id="context-viewimageinfo"
+ label="&viewImageInfoCmd.label;"
+ accesskey="&viewImageInfoCmd.accesskey;"
+ oncommand="gContextMenu.viewImageInfo();"/>
+ <menuitem id="context-savevideo"
+ label="&saveVideoCmd.label;"
+ accesskey="&saveVideoCmd.accesskey;"
+ oncommand="gContextMenu.saveMedia();"/>
+ <menuitem id="context-saveaudio"
+ label="&saveAudioCmd.label;"
+ accesskey="&saveAudioCmd.accesskey;"
+ oncommand="gContextMenu.saveMedia();"/>
+ <menuitem id="context-video-saveimage"
+ label="&videoSaveImage.label;"
+ accesskey="&videoSaveImage.accesskey;"
+ oncommand="gContextMenu.saveVideoFrameAsImage();"/>
+ <menuseparator id="context-sep-image"/>
+ <menuitem id="context-back"
+ label="&goBackCmd.label;"
+ accesskey="&goBackCmd.accesskey;"
+ oncommand="BrowserBack(event)"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-forward"
+ label="&goForwardCmd.label;"
+ accesskey="&goForwardCmd.accesskey;"
+ oncommand="BrowserForward(event)"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-reload"
+ label="&reloadCmd.label;"
+ accesskey="&reloadCmd.accesskey;"
+ oncommand="BrowserReload(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-stop"
+ label="&stopCmd.label;"
+ accesskey="&stopCmd.accesskey;"
+ disabled="true"
+ oncommand="BrowserStop();"/>
+ <menuseparator id="context-sep-stop"/>
+ <menuitem id="context-bookmarkpage"
+ label="&bookmarkPageCmd.label;"
+ accesskey="&bookmarkPageCmd.accesskey;"
+ oncommand="gContextMenu.bookmarkThisPage();"/>
+ <menuitem id="context-savepage"
+ valueSaveAs="&savePageAsCmd.label;"
+ valueSave="&savePageCmd.label;"
+ accesskey="&savePageCmd.accesskey;"
+ oncommand="saveDocument(window.content.document, true);"/>
+ <menuseparator id="context-sep-viewbgimage"/>
+ <menuitem id="context-viewbgimage"
+ label="&viewBGImageCmd.label;"
+ accesskey="&viewBGImageCmd.accesskey;"
+ oncommand="gContextMenu.viewBGImage(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="context-undo"/>
+ <menuitem id="context-redo"/>
+ <menuseparator id="context-sep-undo"/>
+ <menuitem id="context-cut"/>
+ <menuitem id="context-copy"/>
+ <menuitem id="context-paste"/>
+ <menuitem id="context-delete"/>
+ <menuseparator id="context-sep-paste"/>
+ <menuitem id="context-selectall"/>
+ <menuseparator id="context-sep-selectall"/>
+ <menuitem id="context-keywordfield"
+ label="&keywordfield.label;"
+ accesskey="&keywordfield.accesskey;"
+ oncommand="AddKeywordForSearchField();"/>
+ <menuitem id="context-searchselect"
+ oncommand="BrowserSearch.loadSearch(gContextMenu.searchSelected(), true, event);"/>
+ <menuseparator id="context-sep-properties"/>
+ <menuitem id="context-viewpartialsource-selection"
+ label="&viewPartialSourceForSelectionCmd.label;"
+ accesskey="&viewPartialSourceCmd.accesskey;"
+ oncommand="gContextMenu.viewPartialSource('selection');"/>
+ <menuitem id="context-viewpartialsource-mathml"
+ label="&viewPartialSourceForMathMLCmd.label;"
+ accesskey="&viewPartialSourceCmd.accesskey;"
+ oncommand="gContextMenu.viewPartialSource('mathml');"/>
+ <menuitem id="context-viewsource"
+ label="&viewPageSourceCmd.label;"
+ accesskey="&viewPageSourceCmd.accesskey;"
+ observes="isImage"
+ oncommand="BrowserViewSource(gContextMenu.browser);"/>
+ <menuitem id="context-viewinfo"
+ label="&viewPageInfoCmd.label;"
+ accesskey="&viewPageInfoCmd.accesskey;"
+ oncommand="gContextMenu.viewInfo();"/>
+ <menuitem id="context-metadata"
+ label="&metadataCmd.label;"
+ accesskey="&metadataCmd.accesskey;"
+ oncommand="gContextMenu.showMetadata();"/>
+ <menuseparator id="frame-sep"/>
+ <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
+ <menupopup id="frame_popup">
+ <menuitem id="context-showonlythisframe"
+ label="&showOnlyThisFrameCmd.label;"
+ accesskey="&showOnlyThisFrameCmd.accesskey;"
+ oncommand="gContextMenu.showOnlyThisFrame();"/>
+ <menuitem id="context-openframeintab"
+ label="&openFrameCmdInTab.label;"
+ accesskey="&openFrameCmdInTab.accesskey;"
+ oncommand="gContextMenu.openFrameInTab(event);"/>
+ <menuitem id="context-openframe"
+ label="&openFrameCmd.label;"
+ accesskey="&openFrameCmd.accesskey;"
+ oncommand="gContextMenu.openFrame();"/>
+ <menuseparator/>
+ <menuitem id="context-reloadframe"
+ label="&reloadFrameCmd.label;"
+ accesskey="&reloadFrameCmd.accesskey;"
+ oncommand="gContextMenu.reloadFrame();"/>
+ <menuseparator/>
+ <menuitem id="context-bookmarkframe"
+ label="&bookmarkFrameCmd.label;"
+ accesskey="&bookmarkFrameCmd.accesskey;"
+ oncommand="gContextMenu.addBookmarkForFrame();"/>
+ <menuitem id="context-saveframe"
+ valueSaveAs="&saveFrameAsCmd.label;"
+ valueSave="&saveFrameCmd.label;"
+ accesskey="&saveFrameCmd.accesskey;"
+ oncommand="saveDocument(gContextMenu.target.ownerDocument, true);"/>
+ <menuseparator/>
+ <menuitem id="context-printframe"
+ label="&printFrameCmd.label;"
+ accesskey="&printFrameCmd.accesskey;"
+ oncommand="gContextMenu.printFrame();"/>
+ <menuseparator/>
+ <menuitem id="context-viewframesource"
+ label="&viewFrameSourceCmd.label;"
+ accesskey="&viewFrameSourceCmd.accesskey;"
+ oncommand="gContextMenu.viewFrameSource();"/>
+ <menuitem id="context-viewframeinfo"
+ label="&viewFrameInfoCmd.label;"
+ accesskey="&viewFrameInfoCmd.accesskey;"
+ oncommand="gContextMenu.viewFrameInfo();"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="spell-separator"/>
+ <menuitem id="spell-check-enabled"
+ label="&spellCheckEnable.label;"
+ type="checkbox"
+ accesskey="&spellCheckEnable.accesskey;"
+ oncommand="InlineSpellCheckerUI.toggleEnabled();"/>
+ <menuitem id="spell-add-dictionaries-main"
+ label="&spellAddDictionaries.label;"
+ accesskey="&spellAddDictionaries.accesskey;"
+ oncommand="openDictionaryList();"/>
+ <menu id="spell-dictionaries"
+ label="&spellDictionaries.label;"
+ accesskey="&spellDictionaries.accesskey;">
+ <menupopup id="spell-dictionaries-menu">
+ <menuseparator id="spell-language-separator"/>
+ <menuitem id="spell-add-dictionaries"
+ label="&spellAddDictionaries.label;"
+ accesskey="&spellAddDictionaries.accesskey;"
+ oncommand="openDictionaryList();"/>
+ </menupopup>
+ </menu>
+ <menuseparator hidden="true" id="context-sep-bidi"/>
+ <menuitem hidden="true" id="context-bidi-text-direction-toggle"
+ label="&bidiSwitchTextDirectionItem.label;"
+ accesskey="&bidiSwitchTextDirectionItem.accesskey;"
+ command="cmd_switchTextDirection"/>
+ <menuitem hidden="true" id="context-bidi-page-direction-toggle"
+ label="&bidiSwitchPageDirectionItem.label;"
+ accesskey="&bidiSwitchPageDirectionItem.accesskey;"
+ oncommand="SwitchDocumentDirection(window.content);"/>
+ <menuseparator id="fill-login-separator" hidden="true"/>
+ <menu id="fill-login"
+ label="&fillLoginMenu.label;"
+ label-login="&fillLoginMenu.label;"
+ label-password="&fillPasswordMenu.label;"
+ label-username="&fillUsernameMenu.label;"
+ accesskey="&fillLoginMenu.accesskey;"
+ accesskey-login="&fillLoginMenu.accesskey;"
+ accesskey-password="&fillPasswordMenu.accesskey;"
+ accesskey-username="&fillUsernameMenu.accesskey;"
+ hidden="true">
+ <menupopup id="fill-login-popup">
+ <menuitem id="fill-login-no-logins"
+ label="&noLoginSuggestions.label;"
+ disabled="true"
+ hidden="true"/>
+ <menuseparator id="saved-logins-separator"/>
+ <menuitem id="fill-login-saved-passwords"
+ label="&viewSavedLogins.label;"
+ oncommand="gContextMenu.openPasswordManager();"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="inspect-separator"
+ hidden="true"/>
+ <menuitem id="context-inspect"
+ hidden="true"
+ label="&devtoolsInspect.label;"
+ accesskey="&devtoolsInspect.accesskey;"
+ oncommand="gContextMenu.inspectNode();"/>
+ </menupopup>
+ </popupset>
+</overlay>
diff --git a/comm/suite/base/content/defaultClientDialog.js b/comm/suite/base/content/defaultClientDialog.js
new file mode 100644
index 0000000000..d44fb4f412
--- /dev/null
+++ b/comm/suite/base/content/defaultClientDialog.js
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+// this dialog can only be opened if we have a shell service
+const {ShellService} = ChromeUtils.import("resource:///modules/ShellService.jsm");
+const nsIPrefBranch = Ci.nsIPrefBranch;
+
+function onLoad()
+{
+ var defaultList = document.getElementById("defaultList");
+ var appTypes = ShellService.shouldBeDefaultClientFor;
+ /* Iterate through the list of possible default client types and check for
+ each list item if we want to be the default for that type using the AND
+ conjunction */
+ for (var i = 0; i < defaultList.getRowCount(); i++) {
+ var currentItem = defaultList.getItemAtIndex(i);
+ try {
+ if (ShellService.isDefaultClient(false, Ci.nsIShellService[currentItem.value])) {
+ currentItem.checked = true;
+ currentItem.disabled = true;
+ }
+ else if (Ci.nsIShellService[currentItem.value] & appTypes)
+ currentItem.checked = true;
+ } catch (e) {
+ currentItem.hidden = true;
+ }
+ }
+}
+
+function onAccept()
+{
+ // for each checked item, if we aren't already the default, make us the default.
+ var appTypes = 0;
+ var appTypesCheck = 0;
+ var defaultList = document.getElementById("defaultList");
+
+ for (var i = 0; i < defaultList.getRowCount(); i++) {
+ var currentItem = defaultList.getItemAtIndex(i);
+ var currentAppType = Ci.nsIShellService[currentItem.value];
+
+ if (currentItem.checked) {
+ appTypesCheck |= currentAppType;
+
+ if (!currentItem.disabled)
+ appTypes |= currentAppType;
+ }
+ }
+
+ if (appTypes)
+ ShellService.setDefaultClient(false, true, appTypes);
+
+ // Update the pref for which app types we should check if we are the default app
+ ShellService.shouldBeDefaultClientFor = appTypesCheck;
+
+ ShellService.shouldCheckDefaultClient = document.getElementById('checkOnStartup').checked;
+}
diff --git a/comm/suite/base/content/defaultClientDialog.xul b/comm/suite/base/content/defaultClientDialog.xul
new file mode 100644
index 0000000000..db075a4b88
--- /dev/null
+++ b/comm/suite/base/content/defaultClientDialog.xul
@@ -0,0 +1,37 @@
+<?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 window [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % defaultClientDTD SYSTEM "chrome://communicator/locale/defaultClientDialog.dtd" >
+ %defaultClientDTD;
+]>
+
+<dialog xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="defaultClientDialog"
+ buttons="accept,cancel"
+ onload="onLoad();"
+ ondialogaccept="return onAccept();"
+ title="&defaultClient.title;">
+
+ <script src="chrome://communicator/content/defaultClientDialog.js"/>
+
+ <description>&defaultClient.intro;</description>
+ <listbox rows="4" id="defaultList">
+ <listitem value="BROWSER" type="checkbox" label="&browser.label;"/>
+ <listitem value="MAIL" type="checkbox" label="&email.label;"/>
+ <listitem value="NEWS" type="checkbox" label="&newsgroups.label;"/>
+ <listitem value="RSS" type="checkbox" label="&feeds.label;"/>
+ </listbox>
+
+ <separator class="thin"/>
+ <checkbox id="checkOnStartup" checked="true" label="&checkOnStartup.label;" accesskey="&checkOnStartup.accesskey;"/>
+
+</dialog>
diff --git a/comm/suite/base/content/extensionsOverlay.css b/comm/suite/base/content/extensionsOverlay.css
new file mode 100644
index 0000000000..9a2b0ce535
--- /dev/null
+++ b/comm/suite/base/content/extensionsOverlay.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+.legacy-warning {
+ display: none;
+}
diff --git a/comm/suite/base/content/findUtils.js b/comm/suite/base/content/findUtils.js
new file mode 100644
index 0000000000..a894e8a4c9
--- /dev/null
+++ b/comm/suite/base/content/findUtils.js
@@ -0,0 +1,141 @@
+/* -*- 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/. */
+
+var gFindBundle;
+
+function nsFindInstData() {}
+nsFindInstData.prototype =
+{
+ // set the next three attributes on your object to override the defaults
+ browser : null,
+
+ get rootSearchWindow() { return this._root || this.window.content; },
+ set rootSearchWindow(val) { this._root = val; },
+
+ get currentSearchWindow() {
+ if (this._current)
+ return this._current;
+
+ var focusedWindow = this.window.document.commandDispatcher.focusedWindow;
+ if (!focusedWindow || focusedWindow == this.window)
+ focusedWindow = this.window.content;
+
+ return focusedWindow;
+ },
+ set currentSearchWindow(val) { this._current = val; },
+
+ get webBrowserFind() { return this.browser.webBrowserFind; },
+
+ init : function() {
+ var findInst = this.webBrowserFind;
+ // set up the find to search the focussedWindow, bounded by the content window.
+ var findInFrames = findInst.QueryInterface(Ci.nsIWebBrowserFindInFrames);
+ findInFrames.rootSearchFrame = this.rootSearchWindow;
+ findInFrames.currentSearchFrame = this.currentSearchWindow;
+
+ // always search in frames for now. We could add a checkbox to the dialog for this.
+ findInst.searchFrames = true;
+ },
+
+ window : window,
+ _root : null,
+ _current : null
+}
+
+// browser is the <browser> element
+// rootSearchWindow is the window to constrain the search to (normally window.content)
+// currentSearchWindow is the frame to start searching (can be, and normally, rootSearchWindow)
+function findInPage(findInstData)
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("browser.findbar.enabled"))
+ findbar.onFindCommand();
+ else if ("findDialog" in window && window.findDialog) // is the find dialog up already?
+ window.findDialog.focus();
+ else
+ {
+ findInstData.init();
+ window.findDialog = window.openDialog("chrome://global/content/finddialog.xul", "_blank", "chrome,resizable=no,dependent=yes", findInstData);
+ }
+}
+
+function findAgainInPage(findInstData, reverse)
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("browser.findbar.enabled"))
+ {
+ // first, look to see whether XPFE typeaheadfind wants to find next
+ var sip = Cc["@mozilla.org/supports-interface-pointer;1"]
+ .createInstance(Ci.nsISupportsInterfacePointer);
+ sip.data = content;
+ Services.obs.notifyObservers(sip, "nsWebBrowserFind_FindAgain", reverse ? "up" : "down");
+ if (sip.data) // XPFE typeahead find was not interested in this find next
+ findbar.onFindAgainCommand(reverse);
+ }
+ else
+ {
+ // get the find service, which stores global find state, and init the
+ // nsIWebBrowser find with it. We don't assume that there was a previous
+ // Find that set this up.
+ var findService = Cc["@mozilla.org/find/find_service;1"]
+ .getService(Ci.nsIFindService);
+
+ var searchString = findService.searchString;
+ if (searchString.length == 0) {
+ // no previous find text
+ findInPage(findInstData);
+ return;
+ }
+
+ findInstData.init();
+ var findInst = findInstData.webBrowserFind;
+ findInst.searchString = searchString;
+ findInst.matchCase = findService.matchCase;
+ findInst.wrapFind = findService.wrapFind;
+ findInst.entireWord = findService.entireWord;
+ findInst.findBackwards = findService.findBackwards ^ reverse;
+
+ var found = findInst.findNext();
+ if (!found) {
+ if (!gFindBundle)
+ gFindBundle = document.getElementById("findBundle");
+
+ Services.prompt.alert(window, gFindBundle.getString("notFoundTitle"), gFindBundle.getString("notFoundWarning"));
+ }
+
+ // Reset to normal value, otherwise setting can get changed in find dialog
+ findInst.findBackwards = findService.findBackwards;
+ }
+}
+
+function canFindAgainInPage()
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("browser.findbar.enabled"))
+ // The findbar will just be brought up in an error state if you cannot find text again.
+ return true;
+
+ var findService = Cc["@mozilla.org/find/find_service;1"]
+ .getService(Ci.nsIFindService);
+ return (findService.searchString.length > 0);
+}
+
+function findLinksAsYouType()
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("accessibility.typeaheadfind.usefindbar"))
+ findbar.startFastFind(findbar.FIND_LINKS);
+ else
+ goDoCommand("cmd_findTypeLinks");
+}
+
+function findTextAsYouType()
+{
+ var findbar = document.getElementById("FindToolbar");
+ if (findbar && Services.prefs.getBoolPref("accessibility.typeaheadfind.usefindbar"))
+ findbar.startFastFind(findbar.FIND_TYPEAHEAD);
+ else
+ goDoCommand("cmd_findTypeText");
+}
diff --git a/comm/suite/base/content/fullscreen-video.xhtml b/comm/suite/base/content/fullscreen-video.xhtml
new file mode 100644
index 0000000000..a56789a06a
--- /dev/null
+++ b/comm/suite/base/content/fullscreen-video.xhtml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" accelerated="11">
+<head>
+ <link href="chrome://communicator/skin/fullscreen-video.css"
+ rel="stylesheet" type="text/css"/>
+ <script>
+ <![CDATA[
+
+var contentVideo = window.arguments[0];
+var isPaused = window.arguments[1];
+var video;
+var closeIcon;
+
+var title = (contentVideo.currentSrc || contentVideo.src).replace(/^.*\//, "");
+try {
+ title = decodeURI(title);
+} catch (e) {}
+document.title = title;
+
+window.addEventListener("focus", onFocus);
+window.addEventListener("unload", onUnload);
+window.addEventListener("keypress", onKeyPress);
+
+function onFocus() {
+ window.removeEventListener("focus", onFocus);
+
+ window.fullScreen = true;
+
+ window.addEventListener("deactivate", autoClose);
+
+ video = document.querySelector("video");
+ closeIcon = document.querySelector("div");
+
+ video.addEventListener("loadeddata", onLoadedData);
+
+ // Automatically close this window when the playback ended, unless the user
+ // interacted with it.
+ video.addEventListener("ended", autoClose);
+ window.addEventListener("click", cancelAutoClose);
+ window.addEventListener("keypress", cancelAutoClose);
+
+ video.addEventListener("playing", hideUI);
+ video.addEventListener("seeked", hideUI);
+ video.addEventListener("seeking", showUI);
+ video.addEventListener("pause", showUI);
+ video.addEventListener("ended", showUI);
+
+ window.addEventListener("mousemove", onMouseMove);
+
+ video.src = contentVideo.currentSrc || contentVideo.src;
+}
+
+function onLoadedData() {
+ video.removeEventListener("loadeddata", onLoadedData);
+ video.volume = contentVideo.volume;
+ video.muted = contentVideo.muted;
+ video.poster = contentVideo.poster;
+
+ if (contentVideo.currentTime && !contentVideo.ended) {
+ video.addEventListener("seeked", playbackStarts);
+
+ video.currentTime = contentVideo.currentTime;
+ } else {
+ playbackStarts();
+ }
+
+ video.controls = true;
+
+ if (!isPaused)
+ video.play();
+}
+
+function onMouseMove() {
+ showUI();
+ resetIdleTimer();
+}
+
+function onUnload() {
+ if (video.currentSrc) {
+ contentVideo.currentTime = video.currentTime;
+ contentVideo.volume = video.volume;
+ contentVideo.muted = video.muted;
+ if (!video.paused && !video.ended) {
+ video.pause();
+ contentVideo.play();
+ }
+ }
+}
+
+function onKeyPress(event) {
+ if (event.keyCode == event.DOM_VK_ESCAPE) {
+ window.close();
+ return;
+ }
+
+ resetIdleTimer();
+
+ if (!video.controls &&
+ String.fromCharCode(event.charCode) == " ")
+ video.pause();
+}
+
+function playbackStarts() {
+ video.removeEventListener("seeked", playbackStarts);
+
+ // Loading the data from the content video may take a second or two. We hide
+ // the video during that period.
+ document.body.style.visibility = "visible";
+ video.focus();
+}
+
+function autoClose() {
+ window.close();
+}
+
+function cancelAutoClose() {
+ video.removeEventListener("ended", autoClose);
+ window.removeEventListener("click", cancelAutoClose);
+ window.removeEventListener("keypress", cancelAutoClose);
+}
+
+var idleTimer = 0;
+function resetIdleTimer() {
+ clearTimeout(idleTimer);
+ idleTimer = setTimeout(hideUI, 2000);
+}
+
+function showUI() {
+ if (!video.controls) {
+ window.setCursor("auto");
+ closeIcon.style.visibility = "visible";
+ video.controls = true;
+ }
+}
+
+function hideUI() {
+ if (!video.paused && !video.ended && !video.seeking && !video.error) {
+ window.setCursor("none");
+ closeIcon.style.visibility = "hidden";
+ video.controls = false;
+ }
+}
+
+ ]]></script>
+</head>
+<body>
+ <div onclick="window.close();"/>
+ <video/>
+</body>
+</html>
diff --git a/comm/suite/base/content/gopherAddon.xhtml b/comm/suite/base/content/gopherAddon.xhtml
new file mode 100644
index 0000000000..002e274972
--- /dev/null
+++ b/comm/suite/base/content/gopherAddon.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % gopherAddonDTD
+ SYSTEM "chrome://communicator/locale/gopherAddon.dtd">
+ %gopherAddonDTD;
+]>
+
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&loadError.label;</title>
+ <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css"/>
+ <script>
+ <![CDATA[
+
+ function goToAddOn() {
+ document.location = "https://addons.thunderbird.net/addon/7685";
+ }
+
+ ]]></script>
+ </head>
+
+ <body>
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText">&gopherAddon.title;</h1>
+ </div>
+
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText">&gopherAddon.shortDesc;</p>
+ </div>
+
+ <!-- Long Description -->
+ <div id="errorLongDesc">&gopherAddon.longDesc;</div>
+
+ </div>
+ <!-- Go To Add-On Button -->
+ <button id="errorTryAgain" onclick="goToAddOn()">&goToAddOn.label;</button>
+
+ </div>
+ </body>
+</html>
diff --git a/comm/suite/base/content/helpEditorOverlay.xul b/comm/suite/base/content/helpEditorOverlay.xul
new file mode 100644
index 0000000000..355ea6c59c
--- /dev/null
+++ b/comm/suite/base/content/helpEditorOverlay.xul
@@ -0,0 +1,54 @@
+<?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/. -->
+
+<overlay id="helpEditorOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script>
+ <![CDATA[
+ setHelpFileURI('chrome://communicator/locale/help/suitehelp.rdf');
+
+ function doPublishDialogHelpButton()
+ {
+ var selTab = document.getElementById('TabBox').selectedTab;
+
+ if (selTab.id == "PublishTab")
+ openHelp('comp-doc-publish-publishtab');
+ else
+ openHelp('comp-doc-publish-settingstab');
+ }
+ ]]>
+ </script>
+
+ <dialog id="advancedEditDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('advanced_property_editor');"/>
+
+ <dialog id="imageDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('image_properties');"/>
+
+ <dialog id="linkDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('link_properties');"/>
+
+ <dialog id="publishDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="doPublishDialogHelpButton();"/>
+
+ <dialog id="publishProgressDlg"
+ buttons="cancel,help"
+ ondialoghelp="openHelp('comp-doc-publish-troubleshooting');"/>
+
+ <dialog id="publishSettingsDlg"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('comp-doc-publish-site-settings');"/>
+
+ <dialog id="tableDlg"
+ buttons="accept,extra1,cancel,help"
+ ondialoghelp="openHelp('table_properties');"/>
+
+</overlay>
diff --git a/comm/suite/base/content/helpMessengerOverlay.xul b/comm/suite/base/content/helpMessengerOverlay.xul
new file mode 100644
index 0000000000..1672e80624
--- /dev/null
+++ b/comm/suite/base/content/helpMessengerOverlay.xul
@@ -0,0 +1,57 @@
+<?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/. -->
+
+<overlay id="helpMessengerOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script>
+ setHelpFileURI('chrome://communicator/locale/help/suitehelp.rdf');
+ </script>
+
+ <dialog id="editDirectories"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('mail-ldap-properties');"/>
+
+ <dialog id="addDirectory"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-ldap-properties');"/>
+
+ <dialog id="accountManager"
+ buttons="accept,cancel,help"
+ ondialoghelp="return doHelpButton();"/>
+
+ <dialog id="FilterEditor"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-filters');"/>
+
+ <dialog id="junkMailInfo"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('mail-junk');"/>
+
+ <dialog id="select-offline"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-offline-items');"/>
+
+ <dialog id="subscribeWindow"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('mail-subscribe');"/>
+
+ <dialog id="mailViewListDialog"
+ buttons="help"
+ ondialoghelp="return openHelp('message-views-using');"/>
+
+ <dialog id="mailViewSetupDialog"
+ buttons="accept,cancel,help"
+ ondialoghelp="return openHelp('message-views-create-new');"/>
+
+ <dialog id="msgCompSecurityInfo"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('compose_security');"/>
+
+ <dialog id="msgReadSecurityInfo"
+ buttons="accept,help"
+ ondialoghelp="return openHelp('received_security');"/>
+</overlay>
diff --git a/comm/suite/base/content/helpSecurityOverlay.xul b/comm/suite/base/content/helpSecurityOverlay.xul
new file mode 100644
index 0000000000..2291cdb284
--- /dev/null
+++ b/comm/suite/base/content/helpSecurityOverlay.xul
@@ -0,0 +1,137 @@
+<?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/. -->
+
+<overlay id="securityManagerOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script>
+ <![CDATA[
+ setHelpFileURI('chrome://communicator/locale/help/suitehelp.rdf');
+
+ function doCertManagerHelpButton()
+ {
+ var selTab = document.getElementById('certMgrTabbox').selectedItem;
+ var selTabID = selTab.getAttribute('id');
+ switch (selTabID) {
+ case 'mine_tab':
+ openHelp("my_certs");
+ break;
+ case 'others_tab':
+ openHelp("others_certs");
+ break;
+ case 'websites_tab':
+ openHelp("web_certs");
+ break;
+ case 'ca_tab':
+ openHelp("ca_certs");
+ break;
+ case 'orphan_tab':
+ openHelp("orphan_certs");
+ break;
+ }
+ }
+
+ function doDeleteCertificateHelpButton() {
+ let typeFlag = window.arguments[0];
+ switch (typeFlag) {
+ case "mine_tab":
+ openHelp("delete_my_certs");
+ break;
+ case "websites_tab":
+ openHelp("delete_web_certs");
+ break;
+ case "ca_tab":
+ openHelp("delete_ca_certs");
+ break;
+ case "others_tab":
+ openHelp("delete_email_certs");
+ break;
+ }
+ }
+ ]]>
+ </script>
+
+ <dialog id="certmanager"
+ buttons="accept,help"
+ ondialoghelp="return doCertManagerHelpButton();"/>
+
+ <dialog id="certDetails"
+ buttons="accept,help"
+ ondialoghelp="openHelp('cert_details');"/>
+
+ <dialog id="set_password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('change_pwd');"/>
+
+ <dialog id="devicemanager"
+ buttons="accept,help"
+ ondialoghelp="openHelp('sec_devices');"/>
+
+ <dialog id="ssl_warning"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('which_token');"/>
+
+ <dialog id="certAuthAsk"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('which_cert');"/>
+
+ <dialog id="crlImportSuccess"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('validation-crl-import');"/>
+
+ <dialog id="deleteCertificate"
+ buttons="accept,cancel,help"
+ ondialoghelp="doDeleteCertificateHelpButton();"/>
+
+ <dialog id="editCaCert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('edit_ca_certs');"/>
+
+ <dialog id="editEmailCert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('edit_email_certs');"/>
+
+ <dialog id="editWebsiteCert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('edit_web_certs');"/>
+
+ <dialog id="escrowWarnDialog"
+ spacerflex="1"
+ buttons="accept,cancel,help,extra2"
+ ondialoghelp="openHelp('priv_key_copy');"/>
+
+ <dialog id="getp12password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('my_certs');"/>
+
+ <dialog id="setp12password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('cert_backup_pwd');"/>
+
+ <dialog id="crlUpdatePref"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('validation-crl-auto-update-prefs');"/>
+
+ <dialog id="serverCrlNextupdate"
+ buttons="accept,help"
+ ondialoghelp="openHelp('exp_crl');"/>
+
+ <dialog id="crlviewer"
+ buttons="help"
+ ondialoghelp="openHelp('validation-crl-manage');">
+ <hbox id="dialogButtons">
+ <button dlgtype="help"/>
+ </hbox>
+ </dialog>
+
+ <dialog id="reset_password"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('reset_pwd');"/>
+
+ <dialog id="download_cert"
+ buttons="accept,cancel,help"
+ ondialoghelp="openHelp('new_ca');"/>
+</overlay>
diff --git a/comm/suite/base/content/nsContextMenu.js b/comm/suite/base/content/nsContextMenu.js
new file mode 100644
index 0000000000..26514ff39d
--- /dev/null
+++ b/comm/suite/base/content/nsContextMenu.js
@@ -0,0 +1,1676 @@
+/* -*- Mode: C; 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/. */
+
+/*------------------------------ nsContextMenu ---------------------------------
+| This JavaScript "class" is used to implement the browser's content-area |
+| context menu. |
+| |
+| For usage, see references to this class in navigator.xul. |
+| |
+| Currently, this code is relatively useless for any other purpose. In the |
+| longer term, this code will be restructured to make it more reusable. |
+------------------------------------------------------------------------------*/
+
+var {BrowserUtils} =
+ ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"
+var {LoginManagerContextMenu} =
+ ChromeUtils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", () => {
+ let { InlineSpellChecker } = ChromeUtils.import(
+ "resource://gre/modules/InlineSpellChecker.jsm"
+ );
+ return new InlineSpellChecker();
+});
+
+XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
+ let tmp = {};
+ ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp);
+ return new tmp.PageMenuParent();
+});
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
+ findCssSelector: "resource://gre/modules/css-selector.js",
+ LoginHelper: "resource://gre/modules/LoginHelper.jsm",
+ LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
+ DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.jsm",
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+ ShellService: "resource:///modules/ShellService.jsm",
+
+});
+
+var gContextMenuContentData = null;
+
+function nsContextMenu(aXulMenu, aIsShift, aEvent) {
+ this.shouldDisplay = true;
+ this.initMenu(aXulMenu, aIsShift, aEvent);
+}
+
+// Prototype for nsContextMenu "class."
+nsContextMenu.prototype = {
+ initMenu: function(aXulMenu, aIsShift, aEvent) {
+ // Get contextual info.
+ this.setTarget(document.popupNode, document.popupRangeParent,
+ document.popupRangeOffset);
+
+ if (!this.shouldDisplay)
+ return;
+
+ this.hasPageMenu = false;
+ if (!aIsShift && this.browser.docShell.allowJavascript &&
+ Services.prefs.getBoolPref("javascript.enabled"))
+ this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu);
+
+ this.isTextSelected = this.isTextSelection();
+ this.isContentSelected = this.isContentSelection();
+
+ // Initialize gContextMenuContentData.
+ if (aEvent)
+ this.initContentData(aEvent);
+ // Initialize (disable/remove) menu items.
+ this.initItems();
+ },
+
+ initContentData: function(aEvent) {
+ var addonInfo = {};
+ var subject = {
+ event: aEvent,
+ addonInfo: addonInfo,
+ };
+ subject.wrappedJSObject = subject;
+ // Notifies the Addon-SDK which then populates addonInfo.
+ Services.obs.notifyObservers(subject, "content-contextmenu");
+
+ var popupNode = this.target;
+ var doc = popupNode.ownerDocument;
+
+ var contentType = null;
+ var contentDisposition = null;
+ if (this.onImage) {
+ try {
+ let imageCache = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools)
+ .getImgCacheForDocument(doc);
+ let props = imageCache.findEntryProperties(popupNode.currentURI, doc);
+ if (props) {
+ let nsISupportsCString = Ci.nsISupportsCString;
+ contentType = props.get("type", nsISupportsCString).data;
+ try {
+ contentDisposition = props.get("content-disposition",
+ nsISupportsCString).data;
+ } catch (e) {}
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+
+ gContextMenuContentData = {
+ isRemote: false,
+ event: aEvent,
+ popupNode: popupNode,
+ browser: this.browser,
+ principal: doc.nodePrincipal,
+ addonInfo: addonInfo,
+ documentURIObject: doc.documentURIObject,
+ docLocation: doc.location.href,
+ charSet: doc.characterSet,
+ referrer: doc.referrer,
+ referrerPolicy: doc.referrerPolicy,
+ contentType: contentType,
+ contentDisposition: contentDisposition,
+ frameOuterWindowID: doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .outerWindowID,
+ loginFillInfo: LoginManagerContent.getFieldContext(popupNode),
+ };
+ },
+
+ hiding: function () {
+ gContextMenuContentData = null;
+ InlineSpellCheckerUI.clearSuggestionsFromMenu();
+ InlineSpellCheckerUI.clearDictionaryListFromMenu();
+ InlineSpellCheckerUI.uninit();
+ LoginManagerContextMenu.clearLoginsFromMenu(document);
+ },
+
+ initItems: function() {
+ this.initPageMenuSeparator();
+ this.initOpenItems();
+ this.initNavigationItems();
+ this.initViewItems();
+ this.initMiscItems();
+ this.initSpellingItems();
+ this.initSaveItems();
+ this.initClipboardItems();
+ this.initMetadataItems();
+ this.initMediaPlayerItems();
+ this.initPasswordManagerItems();
+ },
+
+ initPageMenuSeparator: function() {
+ this.showItem("page-menu-separator", this.hasPageMenu);
+ },
+
+ initOpenItems: function() {
+ var showOpen = this.onSaveableLink || (this.inDirList && this.onLink);
+ this.showItem("context-openlinkintab", showOpen);
+ this.showItem("context-openlink", showOpen && !gPrivate);
+ this.showItem("context-openlinkinprivatewindow", showOpen);
+ this.showItem("context-sep-open", showOpen);
+ },
+
+ initNavigationItems: function() {
+ // Back/Forward determined by canGoBack/canGoForward broadcasters.
+ this.setItemAttrFromNode("context-back", "disabled", "canGoBack");
+ this.setItemAttrFromNode("context-forward", "disabled", "canGoForward");
+
+ var showNav = !(this.isContentSelected || this.onLink || this.onImage ||
+ this.onCanvas || this.onVideo || this.onAudio ||
+ this.onTextInput);
+
+ this.showItem("context-back", showNav);
+ this.showItem("context-forward", showNav);
+ this.showItem("context-reload", showNav);
+ this.showItem("context-stop", showNav);
+ this.showItem("context-sep-stop", showNav);
+
+ // XXX: Stop is determined in navigator.js; the canStop broadcaster is broken
+ //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
+ },
+
+ initSaveItems: function() {
+ var showSave = !(this.inDirList || this.isContentSelected ||
+ this.onTextInput || this.onStandaloneImage ||
+ this.onCanvas || this.onVideo || this.onAudio ||
+ (this.onLink && this.onImage));
+ if (showSave)
+ goSetMenuValue("context-savepage",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+ this.showItem("context-savepage", showSave);
+
+ // Save/send link depends on whether we're in a link.
+ if (this.onSaveableLink)
+ goSetMenuValue("context-savelink",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+ this.showItem("context-savelink", this.onSaveableLink);
+ this.showItem("context-sendlink", this.onSaveableLink);
+
+ // Save image depends on having loaded its content, video and audio don't.
+ showSave = (this.onLoadedImage && this.onCompletedImage) ||
+ this.onStandaloneImage || this.onCanvas;
+ if (showSave)
+ goSetMenuValue("context-saveimage",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+ this.showItem("context-saveimage", showSave);
+ this.showItem("context-savevideo", this.onVideo);
+ this.showItem("context-saveaudio", this.onAudio);
+ this.showItem("context-video-saveimage", this.onVideo);
+ if (this.onVideo)
+ this.setItemAttr("context-savevideo", "disabled", !this.mediaURL);
+ if (this.onAudio)
+ this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL);
+
+ // Send media URL (but not for canvas, since it's a big data: URL)
+ this.showItem("context-sendimage", showSave && !this.onCanvas);
+ this.showItem("context-sendvideo", this.onVideo);
+ this.showItem("context-sendaudio", this.onAudio);
+ if (this.onVideo)
+ this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL);
+ if (this.onAudio)
+ this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL);
+ },
+
+ initViewItems: function() {
+ // View source is always OK, unless in directory listing.
+ this.showItem("context-viewpartialsource-selection",
+ this.isContentSelected && !this.onTextInput);
+ this.showItem("context-viewpartialsource-mathml",
+ this.onMathML && !this.isContentSelected);
+
+ var showView = !(this.inDirList || this.onImage || this.isContentSelected ||
+ this.onCanvas || this.onVideo || this.onAudio ||
+ this.onLink || this.onTextInput);
+
+ this.showItem("context-viewsource", showView);
+ this.showItem("context-viewinfo", showView);
+
+ var showInspect = DevToolsShim.isEnabled() &&
+ "gDevTools" in window &&
+ Services.prefs.getBoolPref("devtools.inspector.enabled", false);
+ this.showItem("inspect-separator", showInspect);
+ this.showItem("context-inspect", showInspect);
+
+ this.showItem("context-sep-properties",
+ !(this.inDirList || this.isContentSelected || this.onTextInput ||
+ this.onCanvas || this.onVideo || this.onAudio));
+ // Set Desktop Background depends on whether an image was clicked on,
+ // and requires the shell service.
+ var canSetDesktopBackground = ShellService &&
+ ShellService.canSetDesktopBackground;
+ this.showItem("context-setDesktopBackground",
+ canSetDesktopBackground && (this.onLoadedImage || this.onStandaloneImage));
+
+ this.showItem("context-sep-image",
+ this.onLoadedImage || this.onStandaloneImage);
+
+ if (canSetDesktopBackground && this.onLoadedImage)
+ // Disable the Set Desktop Background menu item if we're still trying to load the image
+ this.setItemAttr("context-setDesktopBackground", "disabled",
+ (("complete" in this.target) && !this.target.complete) ? "true" : null);
+
+ this.showItem("context-fitimage", this.onStandaloneImage &&
+ content.document.imageResizingEnabled);
+ if (this.onStandaloneImage && content.document.imageResizingEnabled) {
+ this.setItemAttr("context-fitimage", "disabled",
+ content.document.imageIsOverflowing ? null : "true");
+ this.setItemAttr("context-fitimage", "checked",
+ content.document.imageIsResized ? "true" : null);
+ }
+
+ // Reload image depends on an image that's not fully loaded
+ this.showItem("context-reloadimage", (this.onImage && !this.onCompletedImage));
+
+ // View image depends on having an image that's not standalone
+ // (or is in a frame), or a canvas.
+ this.showItem("context-viewimage",
+ (this.onImage && (!this.inSyntheticDoc || this.inFrame)) ||
+ this.onCanvas);
+
+ // View video depends on not having a standalone video.
+ this.showItem("context-viewvideo", this.onVideo &&
+ (!this.inSyntheticDoc || this.inFrame));
+ this.setItemAttr("context-viewvideo", "disabled", !this.mediaURL);
+
+ // View background image depends on whether there is one, but don't make
+ // background images of a stand-alone media document available
+ this.showItem("context-viewbgimage", showView && !this.inSyntheticDoc);
+ this.showItem("context-sep-viewbgimage", showView && !this.inSyntheticDoc);
+ this.setItemAttr("context-viewbgimage", "disabled", this.hasBGImage ? null : "true");
+
+ this.showItem("context-viewimageinfo", this.onImage);
+
+ // Hide Block and Unblock menuitems.
+ this.showItem("context-blockimage", false);
+ this.showItem("context-unblockimage", false);
+ this.showItem("context-sep-blockimage", false);
+
+ // Block image depends on whether an image was clicked on.
+ if (this.onImage) {
+ var uri = Services.io.newURI(this.mediaURL);
+ if (uri instanceof Ci.nsIURL && uri.host) {
+ var serverLabel = uri.host;
+ // Limit length to max 15 characters.
+ serverLabel = serverLabel.replace(/^www\./i, "");
+ if (serverLabel.length > 15)
+ serverLabel = serverLabel.substr(0, 15) + this.ellipsis;
+
+ // Set label and accesskey for appropriate action and unhide menuitem.
+ var id = "context-blockimage";
+ var attr = "blockImage";
+ if (Services.perms.testPermission(uri, "image") == Services.perms.DENY_ACTION) {
+ id = "context-unblockimage";
+ attr = "unblockImage";
+ }
+ const bundle = document.getElementById("contentAreaCommandsBundle");
+ this.setItemAttr(id, "label",
+ bundle.getFormattedString(attr, [serverLabel]));
+ this.setItemAttr(id, "accesskey",
+ bundle.getString(attr + ".accesskey"));
+ this.showItem(id, true);
+ this.showItem("context-sep-blockimage", true);
+ }
+ }
+ },
+
+ initMiscItems: function() {
+ // Use "Bookmark This Link" if on a link.
+ this.showItem("context-bookmarkpage",
+ !(this.isContentSelected || this.onTextInput ||
+ this.onStandaloneImage || this.onVideo || this.onAudio));
+ this.showItem("context-bookmarklink", this.onLink && !this.onMailtoLink);
+ this.showItem("context-searchselect", this.isTextSelected);
+ this.showItem("context-keywordfield", this.onTextInput && this.onKeywordField);
+ this.showItem("frame", this.inFrame);
+ this.showItem("frame-sep", this.inFrame);
+ if (this.inFrame)
+ goSetMenuValue("context-saveframe",
+ this.autoDownload ? "valueSave" : "valueSaveAs");
+
+ // BiDi UI
+ this.showItem("context-sep-bidi", !this.onNumeric && gShowBiDi);
+ this.showItem("context-bidi-text-direction-toggle",
+ this.onTextInput && !this.onNumeric && gShowBiDi);
+ this.showItem("context-bidi-page-direction-toggle",
+ !this.onTextInput && gShowBiDi);
+ },
+
+ initSpellingItems: function() {
+ var canSpell = InlineSpellCheckerUI.canSpellCheck &&
+ !InlineSpellCheckerUI.initialSpellCheckPending &&
+ this.canSpellCheck;
+ let showDictionaries = canSpell && InlineSpellCheckerUI.enabled;
+ var onMisspelling = InlineSpellCheckerUI.overMisspelling;
+ var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
+ this.showItem("spell-check-enabled", canSpell);
+ this.showItem("spell-separator", canSpell);
+ if (canSpell)
+ this.setItemAttr("spell-check-enabled", "checked", InlineSpellCheckerUI.enabled);
+ this.showItem("spell-add-to-dictionary", onMisspelling);
+ this.showItem("spell-undo-add-to-dictionary", showUndo);
+ this.showItem("spell-ignore-word", onMisspelling);
+
+ // suggestion list
+ this.showItem("spell-add-separator", onMisspelling);
+ this.showItem("spell-suggestions-separator", onMisspelling || showUndo);
+ if (onMisspelling) {
+ var suggestionsSeparator = document.getElementById("spell-add-separator");
+ var numsug = InlineSpellCheckerUI.addSuggestionsToMenu(suggestionsSeparator.parentNode, suggestionsSeparator, 5);
+ this.showItem("spell-no-suggestions", numsug == 0);
+ } else {
+ this.showItem("spell-no-suggestions", false);
+ }
+
+ // dictionary list
+ this.showItem("spell-dictionaries", showDictionaries);
+ var dictMenu = document.getElementById("spell-dictionaries-menu");
+ if (canSpell && dictMenu) {
+ var dictSep = document.getElementById("spell-language-separator");
+ let count = InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep);
+ this.showItem(dictSep, count > 0);
+ this.showItem("spell-add-dictionaries-main", false);
+ }
+ else if (this.onEditableArea) {
+ // when there is no spellchecker but we might be able to spellcheck
+ // add the add to dictionaries item. This will ensure that people
+ // with no dictionaries will be able to download them
+ this.showItem("spell-language-separator", showDictionaries);
+ this.showItem("spell-add-dictionaries-main", showDictionaries);
+ }
+ else
+ this.showItem("spell-add-dictionaries-main", false);
+ },
+
+ initClipboardItems: function() {
+ // Copy depends on whether there is selected text.
+ // Enabling this context menu item is now done through the global
+ // command updating system
+ // this.setItemAttr("context-copy", "disabled", !this.isTextSelected());
+
+ goUpdateGlobalEditMenuItems();
+
+ this.showItem("context-undo", this.onTextInput);
+ this.showItem("context-redo", this.onTextInput);
+ this.showItem("context-sep-undo", this.onTextInput);
+ this.showItem("context-cut", this.onTextInput);
+ this.showItem("context-copy", this.isContentSelected || this.onTextInput);
+ this.showItem("context-paste", this.onTextInput);
+ this.showItem("context-delete", this.onTextInput);
+ this.showItem("context-sep-paste", this.onTextInput);
+ this.showItem("context-selectall", !(this.onLink || this.onImage ||
+ this.onVideo || this.onAudio ||
+ this.inSyntheticDoc));
+ this.showItem("context-sep-selectall",
+ this.isContentSelected && !this.onTextInput);
+ // In a text area there will be nothing after select all, so we don't want a sep
+ // Otherwise, if there's text selected then there are extra menu items
+ // (search for selection and view selection source), so we do want a sep
+
+ // XXX dr
+ // ------
+ // nsDocumentViewer.cpp has code to determine whether we're
+ // on a link or an image. we really ought to be using that...
+
+ // Copy email link depends on whether we're on an email link.
+ this.showItem("context-copyemail", this.onMailtoLink);
+
+ // Copy link location depends on whether we're on a link.
+ this.showItem("context-copylink", this.onLink);
+ this.showItem("context-sep-copylink", this.onLink);
+
+ // Copy image location depends on whether we're on an image.
+ this.showItem("context-copyimage", this.onImage);
+ this.showItem("context-copyvideourl", this.onVideo);
+ this.showItem("context-copyaudiourl", this.onAudio);
+ if (this.onVideo)
+ this.setItemAttr("context-copyvideourl", "disabled", !this.mediaURL);
+ if (this.onAudio)
+ this.setItemAttr("context-copyaudiourl", "disabled", !this.mediaURL);
+ this.showItem("context-sep-copyimage",
+ this.onImage || this.onVideo || this.onAudio);
+ },
+
+ initMetadataItems: function() {
+ // Show if user clicked on something which has metadata.
+ this.showItem("context-metadata", this.onMetaDataItem);
+ },
+
+ initMediaPlayerItems: function() {
+ var onMedia = (this.onVideo || this.onAudio);
+ // Several mutually exclusive items... play/pause, mute/unmute, show/hide
+ this.showItem("context-media-play",
+ onMedia && (this.target.paused || this.target.ended));
+ this.showItem("context-media-pause",
+ onMedia && !this.target.paused && !this.target.ended);
+ this.showItem("context-media-mute", onMedia && !this.target.muted);
+ this.showItem("context-media-unmute", onMedia && this.target.muted);
+ this.showItem("context-media-playbackrate",
+ onMedia && this.target.duration != Number.POSITIVE_INFINITY);
+ this.showItem("context-media-loop", onMedia);
+ this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
+ this.showItem("context-media-hidecontrols", onMedia && this.target.controls);
+ this.showItem("context-video-fullscreen", this.onVideo &&
+ !this.target.ownerDocument.fullscreenElement);
+
+ var statsShowing = this.onVideo && this.target.mozMediaStatisticsShowing;
+ this.showItem("context-video-showstats",
+ this.onVideo && this.target.controls && !statsShowing);
+ this.showItem("context-video-hidestats",
+ this.onVideo && this.target.controls && statsShowing);
+
+ // Disable them when there isn't a valid media source loaded.
+ if (onMedia) {
+ this.setItemAttr("context-media-playbackrate-050", "checked", this.target.playbackRate == 0.5);
+ this.setItemAttr("context-media-playbackrate-100", "checked", this.target.playbackRate == 1.0);
+ this.setItemAttr("context-media-playbackrate-125", "checked", this.target.playbackRate == 1.25);
+ this.setItemAttr("context-media-playbackrate-150", "checked", this.target.playbackRate == 1.5);
+ this.setItemAttr("context-media-playbackrate-200", "checked", this.target.playbackRate == 2.0);
+ this.setItemAttr("context-media-loop", "checked", this.target.loop);
+ var hasError = this.target.error != null ||
+ this.target.networkState == this.target.NETWORK_NO_SOURCE;
+ this.setItemAttr("context-media-play", "disabled", hasError);
+ this.setItemAttr("context-media-pause", "disabled", hasError);
+ this.setItemAttr("context-media-mute", "disabled", hasError);
+ this.setItemAttr("context-media-unmute", "disabled", hasError);
+ this.setItemAttr("context-media-playbackrate", "disabled", hasError);
+ this.setItemAttr("context-media-showcontrols", "disabled", hasError);
+ this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
+ if (this.onVideo) {
+ let canSave = this.target.readyState >= this.target.HAVE_CURRENT_DATA;
+ this.setItemAttr("context-video-saveimage", "disabled", !canSave);
+ this.setItemAttr("context-video-fullscreen", "disabled", hasError);
+ this.setItemAttr("context-video-showstats", "disabled", hasError);
+ this.setItemAttr("context-video-hidestats", "disabled", hasError);
+ }
+ }
+ this.showItem("context-media-sep-commands", onMedia);
+ },
+
+ initPasswordManagerItems: function() {
+ let fillMenu = document.getElementById("fill-login");
+ // If no fill Menu, probably mailContext so nothing to set up.
+ if (!fillMenu)
+ return;
+
+ let loginFillInfo = gContextMenuContentData && gContextMenuContentData.loginFillInfo;
+
+ // If we could not find a password field we
+ // don't want to show the form fill option.
+ let showFill = loginFillInfo && loginFillInfo.passwordField.found;
+
+ // Disable the fill option if the user has set a master password
+ // or if the password field or target field are disabled.
+ let disableFill = !loginFillInfo ||
+ !Services.logins ||
+ !Services.logins.isLoggedIn ||
+ loginFillInfo.passwordField.disabled ||
+ (!this.onPassword && loginFillInfo.usernameField.disabled);
+
+ this.showItem("fill-login-separator", showFill);
+ this.showItem("fill-login", showFill);
+ this.setItemAttr("fill-login", "disabled", disableFill);
+
+ // Set the correct label for the fill menu
+ if (this.onPassword) {
+ fillMenu.setAttribute("label", fillMenu.getAttribute("label-password"));
+ fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-password"));
+ } else {
+ fillMenu.setAttribute("label", fillMenu.getAttribute("label-login"));
+ fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-login"));
+ }
+
+ if (!showFill || disableFill) {
+ return;
+ }
+ let documentURI = gContextMenuContentData.documentURIObject;
+ let fragment = LoginManagerContextMenu.addLoginsToMenu(this.target, this.browser, documentURI);
+
+ this.showItem("fill-login-no-logins", !fragment);
+
+ if (!fragment) {
+ return;
+ }
+ let popup = document.getElementById("fill-login-popup");
+ let insertBeforeElement = document.getElementById("fill-login-no-logins");
+ popup.insertBefore(fragment, insertBeforeElement);
+ },
+
+ openPasswordManager: function() {
+ // LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
+ toDataManager(gContextMenuContentData.documentURIObject.host + '|passwords');
+ },
+
+ /**
+ * Retrieve the array of CSS selectors corresponding to the provided node. The first item
+ * of the array is the selector of the node in its owner document. Additional items are
+ * used if the node is inside a frame, each representing the CSS selector for finding the
+ * frame element in its parent document.
+ *
+ * This format is expected by DevTools in order to handle the Inspect Node context menu
+ * item.
+ *
+ * @param {Node}
+ * The node for which the CSS selectors should be computed
+ * @return {Array} array of css selectors (strings).
+ */
+ getNodeSelectors: function(node) {
+ let selectors = [];
+ while (node) {
+ selectors.push(findCssSelector(node));
+ node = node.ownerGlobal.frameElement;
+ }
+
+ return selectors;
+ },
+
+ inspectNode: function() {
+ let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
+ return DevToolsShim.inspectNode(gBrowser.selectedTab,
+ this.getNodeSelectors(this.target));
+ },
+
+ // Set various context menu attributes based on the state of the world.
+ setTarget: function(aNode, aRangeParent, aRangeOffset) {
+ // Currently "isRemote" is always false.
+ //this.isRemote = gContextMenuContentData && gContextMenuContentData.isRemote;
+
+ const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ // Initialize contextual info.
+ this.onImage = false;
+ this.onLoadedImage = false;
+ this.onCompletedImage = false;
+ this.onStandaloneImage = false;
+ this.onCanvas = false;
+ this.onVideo = false;
+ this.onAudio = false;
+ this.onMetaDataItem = false;
+ this.onTextInput = false;
+ this.onNumeric = false;
+ this.onKeywordField = false;
+ this.mediaURL = "";
+ this.onLink = false;
+ this.onMailtoLink = false;
+ this.onSaveableLink = false;
+ this.inDirList = false;
+ this.link = null;
+ this.linkURL = "";
+ this.linkURI = null;
+ this.linkProtocol = "";
+ this.linkHasNoReferrer = false;
+ this.onMathML = false;
+ this.inFrame = false;
+ this.inSyntheticDoc = false;
+ this.hasBGImage = false;
+ this.bgImageURL = "";
+ this.autoDownload = false;
+ this.isTextSelected = false;
+ this.isContentSelected = false;
+ this.onEditableArea = false;
+ this.canSpellCheck = false;
+ this.onPassword = false;
+
+ // Remember the node that was clicked.
+ this.target = aNode;
+
+ if (aNode.nodeType == Node.DOCUMENT_NODE ||
+ // Not display on XUL element but relax for <label class="text-link">
+ (aNode.namespaceURI == xulNS && !isXULTextLinkLabel(aNode))) {
+ this.shouldDisplay = false;
+ return;
+ }
+
+ let editFlags = SpellCheckHelper.isEditable(this.target, window);
+ this.browser = this.target.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ this.principal = this.target.ownerDocument.nodePrincipal;
+
+ this.autoDownload = Services.prefs.getBoolPref("browser.download.useDownloadDir");
+
+ // Check if we are in a synthetic document (stand alone image, video, etc.).
+ this.inSyntheticDoc = this.target.ownerDocument.mozSyntheticDocument;
+ // First, do checks for nodes that never have children.
+ if (this.target.nodeType == Node.ELEMENT_NODE) {
+ // See if the user clicked on an image.
+ if (this.target instanceof Ci.nsIImageLoadingContent &&
+ this.target.currentURI) {
+ this.onImage = true;
+
+ var request =
+ this.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
+ this.onLoadedImage = true;
+ if (request &&
+ (request.imageStatus & request.STATUS_LOAD_COMPLETE) &&
+ !(request.imageStatus & request.STATUS_ERROR)) {
+ this.onCompletedImage = true;
+ }
+
+ this.mediaURL = this.target.currentURI.spec;
+
+ if (this.target.ownerDocument instanceof ImageDocument)
+ this.onStandaloneImage = true;
+ }
+ else if (this.target instanceof HTMLCanvasElement) {
+ this.onCanvas = true;
+ }
+ else if (this.target instanceof HTMLVideoElement) {
+ // Gecko always creates a HTMLVideoElement when loading an ogg file
+ // directly. If the media is actually audio, be smarter and provide
+ // a context menu with audio operations.
+ if (this.target.readyState >= this.target.HAVE_METADATA &&
+ (this.target.videoWidth == 0 || this.target.videoHeight == 0))
+ this.onAudio = true;
+ else
+ this.onVideo = true;
+
+ this.mediaURL = this.target.currentSrc || this.target.src;
+ }
+ else if (this.target instanceof HTMLAudioElement) {
+ this.onAudio = true;
+ this.mediaURL = this.target.currentSrc || this.target.src;
+ }
+ else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
+ this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
+ this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
+ this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
+ this.onPassword = (editFlags & SpellCheckHelper.PASSWORD) !== 0;
+ if (this.onEditableArea) {
+ InlineSpellCheckerUI.init(this.target.editor);
+ InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
+ }
+ this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
+ }
+ else if ( this.target instanceof HTMLHtmlElement ) {
+ // pages with multiple <body>s are lame. we'll teach them a lesson.
+ var bodyElt = this.target.ownerDocument.body;
+ if (bodyElt) {
+ var computedURL = this.getComputedURL(bodyElt, "background-image");
+ if (computedURL) {
+ this.hasBGImage = true;
+ this.bgImageURL = makeURLAbsolute(bodyElt.baseURI, computedURL);
+ }
+ }
+ }
+ else if ("HTTPIndex" in content &&
+ content.HTTPIndex instanceof Ci.nsIHTTPIndex) {
+ this.inDirList = true;
+ // Bubble outward till we get to an element with URL attribute
+ // (which should be the href).
+ var root = this.target;
+ while (root && !this.link) {
+ if (root.tagName == "tree") {
+ // Hit root of tree; must have clicked in empty space;
+ // thus, no link.
+ break;
+ }
+
+ if (root.getAttribute("URL")) {
+ // Build pseudo link object so link-related functions work.
+ this.onLink = true;
+ this.link = {href: root.getAttribute("URL"),
+ getAttribute: function(attr) {
+ if (attr == "title") {
+ return root.firstChild.firstChild.getAttribute("label");
+ } else {
+ return "";
+ }
+ }
+ };
+ this.linkURL = this.getLinkURL();
+ this.linkURI = this.getLinkURI();
+ this.linkProtocol = this.getLinkProtocol();
+ this.onMailtoLink = (this.linkProtocol == "mailto");
+
+ // If element is a directory, then you can't save it.
+ this.onSaveableLink = root.getAttribute("container") != "true";
+ }
+ else {
+ root = root.parentNode;
+ }
+ }
+ }
+
+ this.canSpellCheck = this._isSpellCheckEnabled(this.target);
+ }
+ else if (this.target.nodeType == Node.TEXT_NODE) {
+ // For text nodes, look at the parent node to determine the spellcheck attribute.
+ this.canSpellCheck = this.target.parentNode &&
+ this._isSpellCheckEnabled(this.target);
+ }
+
+ // We have meta data on images.
+ this.onMetaDataItem = this.onImage;
+
+ // Bubble out, looking for items of interest
+ const NS_MathML = "http://www.w3.org/1998/Math/MathML";
+ const XMLNS = "http://www.w3.org/XML/1998/namespace";
+ var elem = this.target;
+ while (elem) {
+ if (elem.nodeType == Node.ELEMENT_NODE) {
+ // Link?
+ if (!this.onLink &&
+ (isXULTextLinkLabel(elem) ||
+ (elem instanceof HTMLAnchorElement && elem.href) ||
+ (elem instanceof HTMLAreaElement && elem.href) ||
+ elem instanceof HTMLLinkElement ||
+ (elem.namespaceURI == NS_MathML && elem.hasAttribute("href")) ||
+ elem.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) {
+ // Clicked on a link.
+ this.onLink = true;
+ this.onMetaDataItem = true;
+ // Remember corresponding element.
+ this.link = elem;
+ this.linkURL = this.getLinkURL();
+ this.linkURI = this.getLinkURI();
+ this.linkProtocol = this.getLinkProtocol();
+ this.onMailtoLink = (this.linkProtocol == "mailto");
+ // Remember if it is saveable.
+ this.onSaveableLink = this.isLinkSaveable();
+ this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
+ }
+
+ // Text input?
+ if (!this.onTextInput) {
+ // Clicked on a link.
+ this.onTextInput = this.isTargetATextBox(elem);
+ }
+
+ // Metadata item?
+ if (!this.onMetaDataItem) {
+ // We currently display metadata on anything which fits
+ // the below test.
+ if ((elem instanceof HTMLQuoteElement && elem.cite) ||
+ (elem instanceof HTMLTableElement && elem.summary) ||
+ (elem instanceof HTMLModElement && (elem.cite || elem.dateTime)) ||
+ (elem instanceof HTMLElement && (elem.title || elem.lang)) ||
+ elem.getAttributeNS(XMLNS, "lang")) {
+ dump("On metadata item.\n");
+ this.onMetaDataItem = true;
+ }
+ }
+
+ // Background image? Don't bother if we've already found a
+ // background image further down the hierarchy. Otherwise,
+ // we look for the computed background-image style.
+ if (!this.hasBGImage) {
+ var bgImgUrl = this.getComputedURL(elem, "background-image");
+ if (bgImgUrl) {
+ this.hasBGImage = true;
+ this.bgImageURL = makeURLAbsolute(elem.baseURI, bgImgUrl);
+ }
+ }
+ }
+ elem = elem.parentNode;
+ }
+
+ // See if the user clicked on MathML
+ if ((this.target.nodeType == Node.TEXT_NODE &&
+ this.target.parentNode.namespaceURI == NS_MathML) ||
+ (this.target.namespaceURI == NS_MathML))
+ this.onMathML = true;
+
+ // See if the user clicked in a frame.
+ var docDefaultView = this.target.ownerDocument.defaultView;
+ if (docDefaultView != docDefaultView.top)
+ this.inFrame = true;
+
+ // if the document is editable, show context menu like in text inputs
+ if (!this.onEditableArea) {
+ if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
+ // If this.onEditableArea is false but editFlags is CONTENTEDITABLE,
+ // then the document itself must be editable.
+ this.onTextInput = true;
+ this.onKeywordField = false;
+ this.onImage = false;
+ this.onLoadedImage = false;
+ this.onCompletedImage = false;
+ this.onMathML = false;
+ this.inFrame = false;
+ this.hasBGImage = false;
+ this.onEditableArea = true;
+ var win = this.target.ownerDocument.defaultView;
+ var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIEditingSession);
+ InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
+ InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
+ var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
+ this.showItem("spell-check-enabled", canSpell);
+ this.showItem("spell-separator", canSpell);
+ }
+ }
+
+ function isXULTextLinkLabel(node) {
+ return node.namespaceURI == xulNS &&
+ node.tagName == "label" &&
+ node.classList.contains('text-link') &&
+ node.href;
+ }
+ },
+
+ _isSpellCheckEnabled: function(aNode) {
+ // We can always force-enable spellchecking on textboxes
+ if (this.isTargetATextBox(aNode)) {
+ return true;
+ }
+ // We can never spell check something which is not content editable
+ var editable = aNode.isContentEditable;
+ if (!editable && aNode.ownerDocument) {
+ editable = aNode.ownerDocument.designMode == "on";
+ }
+ if (!editable) {
+ return false;
+ }
+ // Otherwise make sure that nothing in the parent chain disables spellchecking
+ return aNode.spellcheck;
+ },
+
+ // Returns the computed style attribute for the given element.
+ getComputedStyle: function(aElem, aProp) {
+ return aElem.ownerDocument
+ .defaultView
+ .getComputedStyle(aElem, "").getPropertyValue(aProp);
+ },
+
+ // Returns a "url"-type computed style attribute value, with the url() stripped.
+ getComputedURL: function(aElem, aProp) {
+ var url = aElem.ownerDocument.defaultView
+ .getComputedStyle(aElem, "")
+ .getPropertyCSSValue(aProp);
+ if (url instanceof CSSPrimitiveValue)
+ url = [url];
+
+ for (var i = 0; i < url.length; i++)
+ if (url[i].primitiveType == CSSPrimitiveValue.CSS_URI)
+ return url[i].getStringValue();
+ return null;
+ },
+
+ // Returns true if clicked-on link targets a resource that can be saved.
+ isLinkSaveable: function() {
+ return this.linkProtocol && this.linkProtocol != "mailto" &&
+ this.linkProtocol != "javascript";
+ },
+
+ // Block/Unblock image from loading in the future.
+ toggleImageBlocking: function(aBlock) {
+ const uri = Services.io.newURI(this.mediaURL);
+ if (aBlock)
+ Services.perms.add(uri, "image", Services.perms.DENY_ACTION);
+ else
+ Services.perms.remove(uri, "image");
+ },
+
+ _openLinkInParameters : function (extra) {
+ let params = { charset: gContextMenuContentData.charSet,
+ originPrincipal: this.principal,
+ triggeringPrincipal: this.principal,
+ referrerURI: gContextMenuContentData.documentURIObject,
+ referrerPolicy: gContextMenuContentData.referrerPolicy,
+ noReferrer: this.linkHasNoReferrer || this.onPlainTextLink };
+ for (let p in extra) {
+ params[p] = extra[p];
+ }
+
+ // If we want to change userContextId, we must be sure that we don't
+ // propagate the referrer.
+ if ("userContextId" in params &&
+ params.userContextId != this.principal.originAttributes.userContextId) {
+ params.noReferrer = true;
+ }
+
+ return params;
+ },
+
+ // Open linked-to URL in a new tab.
+ openLinkInTab: function(aEvent) {
+ urlSecurityCheck(this.linkURL, this.principal);
+ let referrerURI = gContextMenuContentData.documentURIObject;
+
+ // if the mixedContentChannel is present and the referring URI passes
+ // a same origin check with the target URI, we can preserve the users
+ // decision of disabling MCB on a page for it's child tabs.
+ let persistAllowMixedContentInChildTab = false;
+
+ if (this.browser.docShell.mixedContentChannel) {
+ const sm = Services.scriptSecurityManager;
+ try {
+ let targetURI = this.linkURI;
+ sm.checkSameOriginURI(referrerURI, targetURI, false);
+ persistAllowMixedContentInChildTab = true;
+ }
+ catch (e) { }
+ }
+
+ let params = {
+ allowMixedContent: persistAllowMixedContentInChildTab,
+ userContextId: parseInt(aEvent.target.getAttribute('usercontextid')),
+ };
+
+ openLinkIn(this.linkURL,
+ aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
+ this._openLinkInParameters(params));
+ },
+
+ // Open linked-to URL in a new window.
+ openLinkInWindow: function() {
+ urlSecurityCheck(this.linkURL, this.principal);
+ openLinkIn(this.linkURL, "window", this._openLinkInParameters());
+ },
+
+ // Open linked-to URL in a private window.
+ openLinkInPrivateWindow: function() {
+ urlSecurityCheck(this.linkURL, this.principal);
+ openLinkIn(this.linkURL, "window",
+ this._openLinkInParameters({ private: true }));
+ },
+
+ // Open frame in a new tab.
+ openFrameInTab: function(aEvent) {
+ let referrer = gContextMenuContentData.referrer;
+ openLinkIn(gContextMenuContentData.docLocation,
+ aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
+ { charset: gContextMenuContentData.charSet,
+ referrerURI: referrer ? makeURI(referrer) : null });
+ },
+
+ // Reload clicked-in frame.
+ reloadFrame: function() {
+ this.target.ownerDocument.location.reload();
+ },
+
+ // Open clicked-in frame in its own window.
+ openFrame: function() {
+ let referrer = gContextMenuContentData.referrer;
+ openLinkIn(gContextMenuContentData.docLocation, "window",
+ { charset: gContextMenuContentData.charSet,
+ referrerURI: referrer ? makeURI(referrer) : null });
+ },
+
+ printFrame: function() {
+ PrintUtils.printWindow(gContextMenuContentData.frameOuterWindowID,
+ this.browser);
+ },
+
+ // Open clicked-in frame in the same window
+ showOnlyThisFrame: function() {
+ urlSecurityCheck(gContextMenuContentData.docLocation,
+ this.principal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ let referrer = gContextMenuContentData.referrer;
+ openUILinkIn(gContextMenuContentData.docLocation, "current",
+ { disallowInheritPrincipal: true,
+ referrerURI: referrer ? makeURI(referrer) : null });
+ },
+
+ // View Partial Source
+ viewPartialSource: function(aContext) {
+ var browser = getBrowser().selectedBrowser;
+ var target = aContext == "mathml" ? this.target : null;
+ gViewSourceUtils.viewPartialSourceInBrowser(browser, target, null);
+ },
+
+ // Open new "view source" window with the frame's URL.
+ viewFrameSource: function() {
+ gViewSourceUtils.viewSource({
+ browser: this.browser,
+ URL: gContextMenuContentData.docLocation,
+ outerWindowID: gContextMenuContentData.frameOuterWindowID,
+ });
+ },
+
+ viewInfo: function() {
+ BrowserPageInfo(gContextMenuContentData.docLocation, null,
+ null, null, this.browser);
+ },
+
+ viewImageInfo: function() {
+ BrowserPageInfo(gContextMenuContentData.docLocation, "mediaTab",
+ this.target, null, this.browser);
+ },
+
+ viewFrameInfo: function() {
+ BrowserPageInfo(gContextMenuContentData.docLocation, null, null,
+ gContextMenuContentData.frameOuterWindowID, this.browser);
+ },
+
+ toggleImageSize: function() {
+ content.document.toggleImageSize();
+ },
+
+ // Reload image
+ reloadImage: function() {
+ urlSecurityCheck(this.mediaURL,
+ this.target.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ if (this.target instanceof Ci.nsIImageLoadingContent)
+ this.target.forceReload();
+ },
+
+ // Change current window to the URL of the image, video, or audio.
+ viewMedia(e) {
+ let doc = this.target.ownerDocument;
+ let where = whereToOpenLink(e);
+
+ if (this.onCanvas) {
+ let systemPrincipal = Services.scriptSecurityManager
+ .getSystemPrincipal();
+ this.target.toBlob((blob) => {
+ openUILinkIn(URL.createObjectURL(blob), where,
+ { referrerURI: doc.documentURIObject,
+ triggeringPrincipal: systemPrincipal,
+ });
+ });
+ } else {
+ urlSecurityCheck(this.mediaURL,
+ this.target.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ openUILinkIn(this.mediaURL, where,
+ { referrerURI: doc.documentURIObject,
+ triggeringPrincipal: this.target.nodePrincipal,
+ });
+ }
+ },
+
+ saveVideoFrameAsImage: function () {
+ urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ var name = "snapshot.jpg";
+ try {
+ let uri = makeURI(this.mediaURL);
+ let url = uri.QueryInterface(Ci.nsIURL);
+ if (url.fileBaseName)
+ name = decodeURI(url.fileBaseName) + ".jpg";
+ } catch (e) { }
+ var video = this.target;
+ var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ canvas.width = video.videoWidth;
+ canvas.height = video.videoHeight;
+ var ctxDraw = canvas.getContext("2d");
+ ctxDraw.drawImage(video, 0, 0);
+ saveImageURL(canvas.toDataURL("image/jpeg", ""), name, "SaveImageTitle",
+ true, true,
+ this.target.ownerDocument.documentURIObject,
+ null, null, null, (gPrivate ? true : false),
+ this.principal);
+ },
+
+ // Full screen video playback
+ fullScreenVideo: function() {
+ var isPaused = this.target.paused && this.target.currentTime > 0;
+ this.target.pause();
+
+ openDialog("chrome://communicator/content/fullscreen-video.xhtml",
+ "", "chrome,centerscreen,dialog=no", this.target, isPaused);
+ },
+
+ // Change current window to the URL of the background image.
+ viewBGImage(e) {
+ urlSecurityCheck(this.bgImageURL,
+ this.target.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+
+ let doc = this.target.ownerDocument;
+ let where = whereToOpenLink(e);
+ openUILinkIn(this.bgImageURL, where,
+ { referrerURI: doc.documentURIObject,
+ triggeringPrincipal: this.target.nodePrincipal,
+ });
+ },
+
+ setDesktopBackground: function() {
+ let url = (new URL(this.target.ownerDocument.location.href)).pathname;
+ let imageName = url.substr(url.lastIndexOf("/") + 1);
+ openDialog("chrome://communicator/content/setDesktopBackground.xul",
+ "_blank", "chrome,modal,titlebar,centerscreen", this.target,
+ imageName);
+ },
+
+ // Save URL of clicked-on frame.
+ saveFrame: function() {
+ saveDocument(this.target.ownerDocument, true);
+ },
+
+ // Save URL of clicked-on link.
+ saveLink: function() {
+ var doc = this.target.ownerDocument;
+ urlSecurityCheck(this.linkURL, this.principal);
+ this.saveHelper(this.linkURL, this.linkText(), null, true, doc);
+ },
+
+ // Helper function to wait for appropriate MIME-type headers and
+ // then prompt the user with a file picker
+ saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc) {
+ // canonical def in nsURILoader.h
+ const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
+
+ // an object to proxy the data through to
+ // nsIExternalHelperAppService.doContent, which will wait for the
+ // appropriate MIME-type headers and then prompt the user with a
+ // file picker
+ function SaveAsListener() {}
+ SaveAsListener.prototype = {
+ extListener: null,
+
+ onStartRequest: function onStartRequest(aRequest, aContext) {
+ // If the timer fired, the error status will have been caused by that,
+ // and we'll be restarting in onStopRequest, so no reason to notify
+ // the user.
+ if (aRequest.status == NS_ERROR_SAVE_LINK_AS_TIMEOUT)
+ return;
+
+ clearTimeout(timer);
+
+ // some other error occured; notify the user...
+ if (!Components.isSuccessCode(aRequest.status)) {
+ try {
+ const bundle = Services.strings.createBundle(
+ "chrome://mozapps/locale/downloads/downloads.properties");
+
+ const title = bundle.GetStringFromName("downloadErrorAlertTitle");
+ const msg = bundle.GetStringFromName("downloadErrorGeneric");
+
+ Services.prompt.alert(doc.defaultView, title, msg);
+ } catch (ex) {}
+ return;
+ }
+
+ var extHelperAppSvc =
+ Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
+ .getService(Ci.nsIExternalHelperAppService);
+ var channel = aRequest.QueryInterface(Ci.nsIChannel);
+ this.extListener = extHelperAppSvc.doContent(channel.contentType, aRequest,
+ doc.defaultView, true);
+ this.extListener.onStartRequest(aRequest, aContext);
+ },
+
+ onStopRequest: function onStopRequest(aRequest, aContext, aStatusCode) {
+ if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
+ // Do it the old fashioned way, which will pick the best filename
+ // it can without waiting.
+ saveURL(linkURL, linkText, dialogTitle, bypassCache, true, doc.documentURIObject, doc);
+ }
+ if (this.extListener)
+ this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
+ },
+
+ onDataAvailable: function onDataAvailable(aRequest, aContext, aInputStream,
+ aOffset, aCount) {
+ this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
+ aOffset, aCount);
+ }
+ }
+
+ function Callbacks() {}
+ Callbacks.prototype = {
+ getInterface: function getInterface(aIID) {
+ if (aIID.equals(Ci.nsIAuthPrompt) ||
+ aIID.equals(Ci.nsIAuthPrompt2)) {
+ // If the channel demands authentication prompt, we must cancel it
+ // because the save-as-timer would expire and cancel the channel
+ // before we get credentials from user. Both authentication dialog
+ // and save as dialog would appear on the screen as we fall back to
+ // the old fashioned way after the timeout.
+ timer.cancel();
+ channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
+ }
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+ }
+
+ // If we don't have the headers after a short time the user won't have
+ // received any feedback from the click. That's bad, so we give up
+ // waiting for the filename.
+ function timerCallback() {
+ channel.cancel(NS_ERROR_SAVE_LINK_AS_TIMEOUT);
+ }
+
+ // set up a channel to do the saving
+ var channel = NetUtil.newChannel({
+ uri: makeURI(linkURL),
+ loadUsingSystemPrincipal: true,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
+ });
+
+ channel.notificationCallbacks = new Callbacks();
+
+ var flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
+
+ if (bypassCache)
+ flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+
+ if (channel instanceof Ci.nsICachingChannel)
+ flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
+
+ channel.loadFlags |= flags;
+
+ if (channel instanceof Ci.nsIPrivateBrowsingChannel)
+ channel.setPrivate(gPrivate);
+
+ if (channel instanceof Ci.nsIHttpChannel) {
+ channel.referrer = doc.documentURIObject;
+ if (channel instanceof Ci.nsIHttpChannelInternal)
+ channel.forceAllowThirdPartyCookie = true;
+ }
+
+ // fallback to the old way if we don't see the headers quickly
+ var timeToWait = Services.prefs.getIntPref("browser.download.saveLinkAsFilenameTimeout");
+ var timer = setTimeout(timerCallback, timeToWait);
+
+ // kick off the channel with our proxy object as the listener
+ channel.asyncOpen2(new SaveAsListener());
+ },
+
+ // Save URL of clicked-on image, video, or audio.
+ saveMedia: function() {
+ var doc = this.target.ownerDocument;
+ let referrerURI = doc.documentURIObject;
+
+ if (this.onCanvas)
+ // Bypass cache, since it's a data: URL.
+ saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
+ true, false, referrerURI, null, null, null,
+ (gPrivate ? true : false),
+ document.nodePrincipal /* system, because blob: */);
+ else if (this.onImage) {
+ urlSecurityCheck(this.mediaURL, this.principal);
+ saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
+ false, referrerURI, null, gContextMenuContentData.contentType,
+ gContextMenuContentData.contentDisposition,
+ (gPrivate ? true : false),
+ this.principal);
+ }
+ else if (this.onVideo || this.onAudio) {
+ urlSecurityCheck(this.mediaURL, this.principal);
+ var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
+ this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
+ }
+ },
+
+ // Backwards-compatibility wrapper
+ saveImage: function() {
+ if (this.onCanvas || this.onImage)
+ this.saveMedia();
+ },
+
+ // Generate email address.
+ getEmail: function() {
+ // Get the comma-separated list of email addresses only.
+ // There are other ways of embedding email addresses in a mailto:
+ // link, but such complex parsing is beyond us.
+ var addresses;
+ try {
+ // Let's try to unescape it using a character set
+ var characterSet = this.target.ownerDocument.characterSet;
+ const textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"]
+ .getService(Ci.nsITextToSubURI);
+ addresses = this.linkURL.match(/^mailto:([^?]+)/)[1];
+ addresses = textToSubURI.unEscapeURIForUI(characterSet, addresses);
+ }
+ catch(ex) {
+ // Do nothing.
+ }
+ return addresses;
+ },
+
+ // Copy email to clipboard
+ copyEmail: function() {
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(this.getEmail());
+ },
+
+ bookmarkThisPage : function() {
+ window.top.PlacesCommandHook.bookmarkPage(this.browser,
+ true);
+ },
+
+ bookmarkLink: function CM_bookmarkLink() {
+ window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId,
+ this.linkURL,
+ this.linkText());
+ },
+
+ addBookmarkForFrame: function() {
+ var doc = this.target.ownerDocument;
+ var uri = doc.documentURIObject;
+
+ var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
+ if (itemId == -1) {
+ var title = doc.title;
+ var description = PlacesUIUtils.getDescriptionFromDocument(doc);
+ PlacesUIUtils.showMinimalAddBookmarkUI(uri, title, description);
+ }
+ else
+ PlacesUIUtils.showItemProperties(itemId,
+ PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ },
+
+ // Open Metadata window for node
+ showMetadata: function() {
+ window.openDialog("chrome://navigator/content/metadata.xul",
+ "_blank",
+ "scrollbars,resizable,chrome,dialog=no",
+ this.target);
+ },
+
+ ///////////////
+ // Utilities //
+ ///////////////
+
+ // Show/hide one item (specified via name or the item element itself).
+ showItem: function(aItemOrId, aShow) {
+ var item = aItemOrId.constructor == String ? document.getElementById(aItemOrId) : aItemOrId;
+ if (item)
+ item.hidden = !aShow;
+ },
+
+ // Set given attribute of specified context-menu item. If the
+ // value is null, then it removes the attribute (which works
+ // nicely for the disabled attribute).
+ setItemAttr: function(aID, aAttr, aVal) {
+ var elem = document.getElementById(aID);
+ if (elem) {
+ if (aVal == null) {
+ // null indicates attr should be removed.
+ elem.removeAttribute(aAttr);
+ }
+ else {
+ // Set attr=val.
+ elem.setAttribute(aAttr, aVal);
+ }
+ }
+ },
+
+ // Set context menu attribute according to like attribute of another node
+ // (such as a broadcaster).
+ setItemAttrFromNode: function(aItem_id, aAttr, aOther_id) {
+ var elem = document.getElementById(aOther_id);
+ if (elem && elem.getAttribute(aAttr) == "true") {
+ this.setItemAttr(aItem_id, aAttr, "true");
+ }
+ else {
+ this.setItemAttr(aItem_id, aAttr, null);
+ }
+ },
+
+ // Temporary workaround for DOM api not yet implemented by XUL nodes.
+ cloneNode: function(aItem) {
+ // Create another element like the one we're cloning.
+ var node = document.createElement(aItem.tagName);
+
+ // Copy attributes from argument item to the new one.
+ var attrs = aItem.attributes;
+ for (var i = 0; i < attrs.length; i++) {
+ var attr = attrs.item(i);
+ node.setAttribute(attr.nodeName, attr.nodeValue);
+ }
+
+ // Voila!
+ return node;
+ },
+
+ // Generate fully qualified URL for clicked-on link.
+ getLinkURL: function() {
+ if (this.link.href)
+ return this.link.href;
+
+ var href;
+ if (this.link.namespaceURI == "http://www.w3.org/1998/Math/MathML")
+ href = this.link.getAttribute("href");
+
+ if (!href)
+ href = this.link.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+
+ if (!href || !href.match(/\S/)) {
+ // Without this we try to save as the current doc,
+ // for example, HTML case also throws if empty
+ throw "Empty href";
+ }
+
+ return makeURLAbsolute(this.link.baseURI, href);
+ },
+
+ getLinkURI: function() {
+ try {
+ return makeURI(this.linkURL);
+ }
+ catch (ex) {
+ // e.g. empty URL string
+ }
+
+ return null;
+ },
+
+ getLinkProtocol: function() {
+ if (this.linkURI)
+ return this.linkURI.scheme; // can be |undefined|
+
+ return null;
+ },
+
+ // Get text of link.
+ linkText: function() {
+ var text = gatherTextUnder(this.link);
+ if (text && text.match(/\S/))
+ return text;
+
+ text = this.link.getAttribute("title");
+ if (text && text.match(/\S/))
+ return text;
+
+ text = this.link.getAttribute("alt");
+ if (text && text.match(/\S/))
+ return text;
+
+ if (this.link.href)
+ return this.link.href;
+
+ if (elem.namespaceURI == "http://www.w3.org/1998/Math/MathML")
+ text = elem.getAttribute("href");
+ if (!text || !text.match(/\S/))
+ text = elem.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+ if (text && text.match(/\S/))
+ return makeURLAbsolute(this.link.baseURI, text);
+
+ return null;
+ },
+
+ /**
+ * Determines whether the focused window has selected text, and if so
+ * formats the first 15 characters for the label of the context-searchselect
+ * element according to the searchText string.
+ * @return true if there is selected text, false if not
+ */
+ isTextSelection: function() {
+ var searchSelectText = this.searchSelected(16);
+
+ if (!searchSelectText)
+ return false;
+
+ if (searchSelectText.length > 15)
+ searchSelectText = searchSelectText.substr(0, 15) + this.ellipsis;
+
+ // Use the current engine if it's a browser window and the search bar is
+ // visible, the default engine otherwise.
+ var engineName = "";
+ if (window.BrowserSearch &&
+ (isElementVisible(BrowserSearch.searchBar) ||
+ BrowserSearch.searchSidebar))
+ engineName = Services.search.currentEngine.name;
+ else
+ engineName = Services.search.defaultEngine.name;
+
+ // format "Search <engine> for <selection>" string to show in menu
+ const bundle = document.getElementById("contentAreaCommandsBundle");
+ var menuLabel = bundle.getFormattedString("searchSelected",
+ [engineName, searchSelectText]);
+ this.setItemAttr("context-searchselect", "label", menuLabel);
+ this.setItemAttr("context-searchselect", "accesskey",
+ bundle.getString("searchSelected.accesskey"));
+
+ return true;
+ },
+
+ searchSelected: function(aCharlen) {
+ var focusedWindow = document.commandDispatcher.focusedWindow;
+ var searchStr = focusedWindow.getSelection();
+ searchStr = searchStr.toString();
+
+ if (this.onTextInput) {
+ var fElem = this.target;
+ if ((fElem instanceof HTMLInputElement &&
+ fElem.mozIsTextField(true)) ||
+ fElem instanceof HTMLTextAreaElement) {
+ searchStr = fElem.value.substring(fElem.selectionStart, fElem.selectionEnd);
+ }
+ }
+
+ // searching for more than 150 chars makes no sense
+ if (!aCharlen)
+ aCharlen = 150;
+ if (aCharlen < searchStr.length) {
+ // only use the first charlen important chars. see bug 221361
+ var pattern = new RegExp("^(?:\\s*.){0," + aCharlen + "}");
+ pattern.test(searchStr);
+ searchStr = RegExp.lastMatch;
+ }
+
+ return searchStr.trim().replace(/\s+/g, " ");
+ },
+
+ // Returns true if anything is selected.
+ isContentSelection: function() {
+ return !document.commandDispatcher.focusedWindow.getSelection().isCollapsed;
+ },
+
+ // Returns true if the target is editable
+ isTargetEditable: function() {
+ if (this.target.ownerDocument.designMode == "on")
+ return true;
+
+ for (var node = this.target; node; node = node.parentNode)
+ if (node.nodeType == node.ELEMENT_NODE &&
+ node.namespaceURI == "http://www.w3.org/1999/xhtml")
+ return node.isContentEditable;
+ return false;
+ },
+
+ toString: function() {
+ return "contextMenu.target = " + this.target + "\n" +
+ "contextMenu.onImage = " + this.onImage + "\n" +
+ "contextMenu.onLink = " + this.onLink + "\n" +
+ "contextMenu.link = " + this.link + "\n" +
+ "contextMenu.inFrame = " + this.inFrame + "\n" +
+ "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
+ },
+
+ isTextBoxEnabled: function(aNode) {
+ return !aNode.ownerDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .isNodeDisabledForEvents(aNode);
+ },
+
+ isTargetATextBox: function(aNode) {
+ if (aNode instanceof HTMLInputElement)
+ return aNode.mozIsTextField(false) && this.isTextBoxEnabled(aNode);
+
+ return aNode instanceof HTMLTextAreaElement && this.isTextBoxEnabled(aNode);
+ },
+
+ /**
+ * Determine whether a separator should be shown based on whether
+ * there are any non-hidden items between it and the previous separator.
+ * @param aSeparatorID
+ * The id of the separator element
+ * @return true if the separator should be shown, false if not
+ */
+ shouldShowSeparator: function(aSeparatorID) {
+ let separator = document.getElementById(aSeparatorID);
+ if (separator) {
+ let sibling = separator.previousSibling;
+ while (sibling && sibling.localName != "menuseparator") {
+ if (sibling.getAttribute("hidden") != "true")
+ return true;
+ sibling = sibling.previousSibling;
+ }
+ }
+ return false;
+ },
+
+ mediaCommand: function(aCommand, aData) {
+ var media = this.target;
+
+ switch (aCommand) {
+ case "play":
+ media.play();
+ break;
+ case "pause":
+ media.pause();
+ break;
+ case "loop":
+ media.loop = !media.loop;
+ break;
+ case "mute":
+ media.muted = true;
+ break;
+ case "unmute":
+ media.muted = false;
+ break;
+ case "playbackRate":
+ media.playbackRate = aData;
+ break;
+ case "hidecontrols":
+ media.removeAttribute("controls");
+ break;
+ case "showcontrols":
+ media.setAttribute("controls", "true");
+ break;
+ case "showstats":
+ case "hidestats":
+ var win = media.ownerDocument.defaultView;
+ var showing = aCommand == "showstats";
+ media.dispatchEvent(new win.CustomEvent("media-showStatistics",
+ { bubbles: false, cancelable: true, detail: showing }));
+ break;
+ }
+ },
+
+ copyMediaLocation: function() {
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(this.mediaURL);
+ },
+
+ get imageURL() {
+ if (this.onImage)
+ return this.mediaURL;
+ return "";
+ }
+};
+
+XPCOMUtils.defineLazyGetter(nsContextMenu.prototype, "ellipsis", function() {
+ return Services.prefs.getComplexValue("intl.ellipsis",
+ Ci.nsIPrefLocalizedString).data;
+});
diff --git a/comm/suite/base/content/openLocation.js b/comm/suite/base/content/openLocation.js
new file mode 100644
index 0000000000..2bb9524d94
--- /dev/null
+++ b/comm/suite/base/content/openLocation.js
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gInput;
+var gAcceptButton;
+var gLastPref = "general.open_location.last_url";
+var gOpenAppList;
+var gBundle;
+var gAction;
+
+function onLoad()
+{
+ gInput = document.getElementById("dialog.input");
+ gAcceptButton = document.documentElement.getButton("accept");
+ gOpenAppList = document.getElementById("openAppList");
+ gBundle = document.getElementById("openLocationBundle");
+ gAction = window.arguments[0].action;
+ // Set arguments action to prevent problems on cancel.
+ window.arguments[0].action = "-1";
+
+ switch (gAction) {
+ case "5": // attach web page
+ document.title = gBundle.getString("attachTitle");
+ document.getElementById("enterLabel").value = gBundle.getString("attachEnterLabel");
+ document.getElementById("openWhereBox").setAttribute("hidden", true);
+
+ // Change accept button text to 'attach'.
+ gAcceptButton.label = gBundle.getString("attachButtonLabel");
+ gLastPref = "mailnews.attach_web_page.last_url";
+
+ break;
+
+ case "2": // open web page from composer
+ gOpenAppList.selectedItem = document.getElementById("editWindow");
+ var openTopWindow = document.getElementById("currentTab");
+
+ // Change string to make more sense for Composer.
+ openTopWindow.setAttribute("label",
+ gBundle.getString("existingNavigatorWindow"));
+
+ // Disable existing browser and new tab menuitems and create indicator
+ // if no browser windows found.
+ if (!Services.wm.getMostRecentWindow("navigator:browser")) {
+ openTopWindow.setAttribute("disabled", "true");
+ document.getElementById("newTab").setAttribute("disabled", "true");
+ gAction = "-1";
+ }
+ break;
+
+ default: // open web page
+ gOpenAppList.value = Services.prefs.getIntPref("general.open_location.last_window_choice");
+ }
+
+ gInput.value = Services.prefs.getStringPref(gLastPref, "");
+ if (gInput.value)
+ gInput.select(); // XXX should probably be done automatically
+
+ doEnabling();
+}
+
+function doEnabling()
+{
+ gAcceptButton.disabled = !gInput.value;
+}
+
+function accept()
+{
+ var params = window.arguments[0];
+ params.url = gInput.value;
+ params.action = gOpenAppList.value;
+ if (gAction == "4" || params.action == "4")
+ return; // private, don't set any preferences
+
+ if (gAction != "5") { // open web page
+ // If there were no browser windows open and not set to open in composer
+ // then set to open in a new window.
+ if (gAction == "-1" && params.action != "2")
+ params.action = "1";
+
+ // If open web page from navigator window, save last window choice.
+ if (gAction == "0")
+ Services.prefs.setIntPref("general.open_location.last_window_choice",
+ gOpenAppList.value);
+ }
+
+ SetStringPref(gLastPref, gInput.value);
+}
+
+function onChooseFile()
+{
+ const nsIFilePicker = Ci.nsIFilePicker;
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(nsIFilePicker);
+ fp.init(window, gBundle.getString("chooseFileDialogTitle"),
+ nsIFilePicker.modeOpen);
+ if (window.arguments[0].action != "5" && gOpenAppList.value == "2") {
+ // When loading into Composer, direct user to prefer HTML files and text
+ // files, so we call separately to control the order of the filter list.
+ fp.appendFilters(nsIFilePicker.filterHTML | nsIFilePicker.filterText);
+ fp.appendFilters(nsIFilePicker.filterAll);
+ } else {
+ fp.appendFilters(nsIFilePicker.filterHTML | nsIFilePicker.filterText |
+ nsIFilePicker.filterAll | nsIFilePicker.filterImages |
+ nsIFilePicker.filterXML);
+ }
+
+ fp.open(rv => {
+ if (rv == nsIFilePicker.returnOK && fp.fileURL.spec &&
+ fp.fileURL.spec.length > 0) {
+ gInput.value = fp.fileURL.spec;
+ }
+
+ doEnabling();
+ });
+}
+
+function useUBHistoryItem(aValue)
+{
+ gInput.value = aValue;
+ gInput.focus();
+ doEnabling();
+}
diff --git a/comm/suite/base/content/openLocation.xul b/comm/suite/base/content/openLocation.xul
new file mode 100644
index 0000000000..1116f76562
--- /dev/null
+++ b/comm/suite/base/content/openLocation.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % openDialogDTD SYSTEM "chrome://communicator/locale/openLocation.dtd" >
+ %openDialogDTD;
+]>
+
+<dialog id="openLocation"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&caption.label;"
+ onload="onLoad()"
+ ondialogaccept="return accept();"
+ buttonlabelaccept="&open.label;"
+ style="width: 40em;"
+ persist="screenX screenY"
+ screenX="24" screenY="24">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/openLocation.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://navigator/content/sessionHistoryUI.js"/>
+
+ <stringbundle id="openLocationBundle" src="chrome://communicator/locale/openLocation.properties"/>
+
+ <hbox>
+ <separator orient="vertical" class="thin"/>
+ <vbox flex="1">
+ <label id="enterLabel"
+ value="&enter.label;"
+ control="dialog.input"
+ accesskey="&enter.accesskey;"/>
+ <separator class="thin"/>
+
+ <hbox align="center">
+ <textbox id="dialog.input" flex="1" type="autocomplete"
+ autocompletesearch="history file" timeout="50" maxrows="6"
+ enablehistory="true" class="uri-element"
+ oninput="doEnabling();">
+ <menupopup id="ubhist-popup" class="autocomplete-history-popup"
+ popupalign="topleft" popupanchor="bottomleft"
+ onpopupshowing="createUBHistoryMenu(event.target);"
+ oncommand="useUBHistoryItem(event.target.label);"/>
+ </textbox>
+ <button label="&chooseFile.label;" accesskey="&chooseFile.accesskey;" oncommand="onChooseFile();"/>
+ </hbox>
+ <hbox id="openWhereBox" align="center">
+ <label value="&openWhere.label;" accesskey="&openWhere.accesskey;" control="openAppList"/>
+ <menulist id="openAppList">
+ <menupopup>
+ <menuitem value="0"
+ id="currentTab"
+ label="&currentTab.label;"
+ selected="true"/>
+ <menuitem value="3" id="newTab" label="&newTab.label;"/>
+ <menuitem value="1" id="newWindow" label="&newWindow.label;"/>
+ <menuitem value="4" id="newPrivate" label="&newPrivate.label;"/>
+ <menuseparator/>
+ <menuitem value="2" id="editWindow" label="&editNewWindow.label;"/>
+ </menupopup>
+ </menulist>
+ <spacer flex="1"/>
+ </hbox>
+ </vbox>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/base/content/overrides/app-license.html b/comm/suite/base/content/overrides/app-license.html
new file mode 100644
index 0000000000..ca69723fa8
--- /dev/null
+++ b/comm/suite/base/content/overrides/app-license.html
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+ <p><b>Binaries</b> of this product have been made available to you by the
+ <a href="http://www.seamonkey-project.org/">SeaMonkey Project</a>
+ under the Mozilla Public License 2.0 (MPL).
+ <a href="about:rights">Know your rights</a>.</p>
diff --git a/comm/suite/base/content/safeMode.js b/comm/suite/base/content/safeMode.js
new file mode 100644
index 0000000000..7572bfef8b
--- /dev/null
+++ b/comm/suite/base/content/safeMode.js
@@ -0,0 +1,92 @@
+/* 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 { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const appStartup = Services.startup;
+
+function restartApp() {
+ appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
+}
+
+function clearAllPrefs() {
+ Services.prefs.resetUserPrefs();
+
+ // Remove the pref-overrides dir, if it exists.
+ try {
+ var prefOverridesDir = Services.dirsvc.get("PrefDOverride", Ci.nsIFile);
+ prefOverridesDir.remove(true);
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+}
+
+function restoreDefaultBookmarks() {
+ Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks", true);
+}
+
+function deleteLocalstore() {
+ // Delete the xulstore file.
+ let xulstoreFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ xulstoreFile.append("xulstore.json");
+ if (xulstoreFile.exists())
+ xulstoreFile.remove(false);
+}
+
+function disableAddons() {
+ AddonManager.getAllAddons(function(aAddons) {
+ aAddons.forEach(function(aAddon) {
+ if (aAddon.type == "theme") {
+ // Setting userDisabled to false on the default theme activates it,
+ // disables all other themes and deactivates the applied persona, if
+ // any.
+ const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
+ if (aAddon.id == DEFAULT_THEME_ID)
+ aAddon.userDisabled = false;
+ }
+ else {
+ aAddon.userDisabled = true;
+ }
+ });
+
+ restartApp();
+ });
+}
+
+function onOK() {
+ try {
+ if (document.getElementById("resetUserPrefs").checked)
+ clearAllPrefs();
+ if (document.getElementById("deleteBookmarks").checked)
+ restoreDefaultBookmarks();
+ if (document.getElementById("resetToolbars").checked)
+ deleteLocalstore();
+ if (document.getElementById("restoreSearch").checked)
+ Services.search.restoreDefaultEngines();
+ if (document.getElementById("disableAddons").checked) {
+ disableAddons();
+ // disableAddons will asynchronously restart the application
+ return false;
+ }
+ } catch(e) {
+ }
+
+ restartApp();
+ return false;
+}
+
+function onCancel() {
+ appStartup.quit(appStartup.eForceQuit);
+ return false;
+}
+
+function onLoad() {
+ document.documentElement.getButton("extra1").focus();
+}
+
+function UpdateOKButtonState() {
+ document.documentElement.getButton("accept").disabled =
+ !document.getElementsByAttribute("checked", "true").item(0);
+}
diff --git a/comm/suite/base/content/safeMode.xul b/comm/suite/base/content/safeMode.xul
new file mode 100644
index 0000000000..76b874c783
--- /dev/null
+++ b/comm/suite/base/content/safeMode.xul
@@ -0,0 +1,58 @@
+<?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 [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % safeModeDTD SYSTEM "chrome://communicator/locale/safeMode.dtd">
+%safeModeDTD;
+<!ENTITY % utilityDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+%utilityDTD;
+]>
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<dialog id="safeModeDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&safeModeDialog.title;"
+ buttons="accept,cancel,extra1"
+ buttonlabelaccept="&changeAndRestartButton.label;"
+ buttonlabelcancel="&quitApplicationCmd.label;"
+ buttonlabelextra1="&continueButton.label;"
+ width="&window.width;"
+ ondialogaccept="return onOK();"
+ ondialogcancel="return onCancel();"
+ ondialogextra1="window.close();"
+ onload="onLoad();"
+ buttondisabledaccept="true">
+
+ <script src="chrome://communicator/content/safeMode.js"/>
+
+ <description>&safeModeDescription.label;</description>
+
+ <separator class="thin"/>
+
+ <label value="&safeModeDescription2.label;"/>
+ <vbox id="tasks" oncommand="UpdateOKButtonState();">
+ <checkbox id="disableAddons"
+ label="&disableAddons.label;"
+ accesskey="&disableAddons.accesskey;"/>
+ <checkbox id="resetToolbars"
+ label="&resetToolbars.label;"
+ accesskey="&resetToolbars.accesskey;"/>
+ <checkbox id="deleteBookmarks"
+ label="&deleteBookmarks.label;"
+ accesskey="&deleteBookmarks.accesskey;"/>
+ <checkbox id="resetUserPrefs"
+ label="&resetUserPrefs.label;"
+ accesskey="&resetUserPrefs.accesskey;"/>
+ <checkbox id="restoreSearch"
+ label="&restoreSearch.label;"
+ accesskey="&restoreSearch.accesskey;"/>
+ </vbox>
+
+ <separator class="thin"/>
+</dialog>
diff --git a/comm/suite/base/content/tasksOverlay.js b/comm/suite/base/content/tasksOverlay.js
new file mode 100644
index 0000000000..49ec7c03af
--- /dev/null
+++ b/comm/suite/base/content/tasksOverlay.js
@@ -0,0 +1,276 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function toNavigator()
+{
+ if (!CycleWindow("navigator:browser"))
+ OpenBrowserWindow();
+}
+
+function ExpirePassword()
+{
+ // Queries the HTTP Auth Manager and clears all sessions
+ Cc['@mozilla.org/network/http-auth-manager;1']
+ .getService(Ci.nsIHttpAuthManager)
+ .clearAll();
+
+ // Expires the master password
+ Cc["@mozilla.org/security/pk11tokendb;1"]
+ .createInstance(Ci.nsIPK11TokenDB)
+ .getInternalKeyToken()
+ .checkPassword("");
+}
+
+function toDownloadManager()
+{
+ Cc["@mozilla.org/suite/suiteglue;1"]
+ .getService(Ci.nsISuiteGlue)
+ .showDownloadManager();
+}
+
+function toDataManager(aView)
+{
+ var useDlg = Services.prefs.getBoolPref("suite.manager.dataman.openAsDialog");
+
+ if (useDlg) {
+ var url = "chrome://communicator/content/dataman/dataman.xul";
+ var win = toOpenWindowByType("data:manager", url, "", aView);
+ if (win && aView)
+ win.gDataman.loadView(aView);
+ return;
+ }
+
+ switchToTabHavingURI("about:data", true, function(browser) {
+ if (aView)
+ browser.contentWindow.wrappedJSObject.gDataman.loadView(aView);
+ });
+}
+
+function toEM(aView)
+{
+ var useDlg = Services.prefs.getBoolPref("suite.manager.addons.openAsDialog");
+
+ if (useDlg) {
+ var view = aView ? { view: aView } : null;
+ var url = "chrome://mozapps/content/extensions/extensions.xul";
+ var win = toOpenWindowByType("Addons:Manager", url, "", view);
+ if (win && aView)
+ win.loadView(aView);
+ return;
+ }
+
+ switchToTabHavingURI("about:addons", true, function(browser) {
+ if (aView)
+ browser.contentWindow.wrappedJSObject.loadView(aView);
+ });
+}
+
+function toBookmarksManager()
+{
+ toOpenWindowByType("Places:Organizer",
+ "chrome://communicator/content/places/places.xul");
+}
+
+function toJavaScriptConsole()
+{
+ toOpenWindowByType("suite:console", "chrome://communicator/content/console/console.xul");
+}
+
+function toOpenWindow( aWindow )
+{
+ try {
+ // Try to focus the previously focused window e.g. message compose body
+ aWindow.document.commandDispatcher.focusedWindow.focus();
+ } catch (e) {
+ // e.g. non-XUL document; just raise the top window
+ aWindow.focus();
+ }
+}
+
+function toOpenWindowByType(inType, uri, features, args)
+{
+ // don't do several loads in parallel
+ if (uri in window)
+ return;
+
+ var topWindow = Services.wm.getMostRecentWindow(inType);
+ if ( topWindow )
+ {
+ toOpenWindow( topWindow );
+ return topWindow;
+ }
+ else
+ {
+ // open the requested window, but block it until it's fully loaded
+ function newWindowLoaded(event)
+ {
+ // make sure that this handler is called only once
+ window.removeEventListener("unload", newWindowLoaded);
+ window[uri].removeEventListener("load", newWindowLoaded);
+ delete window[uri];
+ }
+
+ // Remember the newly loading window until it's fully loaded
+ // or until the current window passes away.
+ // Only pass args if they exist and have a value (see Bug 1279738).
+ if (typeof args != "undefined" && args) {
+ window[uri] = openDialog(uri, "",
+ features || "non-private,all,dialog=no",
+ args || null);
+ }
+ else {
+ window[uri] = openDialog(uri, "",
+ features || "non-private,all,dialog=no");
+ }
+
+ window[uri].addEventListener("load", newWindowLoaded);
+ window.addEventListener("unload", newWindowLoaded);
+ }
+ return;
+}
+
+function OpenBrowserWindow()
+{
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+ if (document.documentElement.getAttribute("windowtype") ==
+ "navigator:browser" && window.content && window.content.document)
+ {
+ // if and only if the current window is a browser window and
+ // it has a document with a character set, then extract the
+ // current charset menu setting from the current document
+ // and use it to initialize the new browser window
+ return window.openDialog(getBrowserURL(), "_blank",
+ "chrome,all,dialog=no,non-private", null,
+ "charset=" + window.content.document.characterSet);
+ }
+
+ if (win) {
+ // if a browser window already exists then set startpage to null so
+ // navigator.js can check pref for how new window should be opened
+ return win.openDialog(getBrowserURL(), "_blank",
+ "chrome,all,dialog=no,non-private", null);
+ }
+
+ // open the first browser window as if we were starting up
+ var cmdLine = {
+ handleFlagWithParam: function handleFlagWithParam(flag, caseSensitive) {
+ return flag == "remote" ? "xfeDoCommand(openBrowser)" : null;
+ },
+ handleFlag: function handleFlag(flag, caseSensitive) {
+ return false;
+ },
+ preventDefault: true
+ };
+ const clh_prefix = "@mozilla.org/commandlinehandler/general-startup;1";
+ Cc[clh_prefix + "?type=browser"]
+ .getService(Ci.nsICommandLineHandler)
+ .handle(cmdLine);
+ return null;
+}
+
+function CycleWindow(aType) {
+ let topWindowOfType = Services.wm.getMostRecentWindow(aType);
+ if (topWindowOfType == null)
+ return null;
+
+ let topWindow = Services.wm.getMostRecentWindow(null);
+ if (topWindowOfType != topWindow) {
+ toOpenWindow(topWindowOfType);
+ return topWindowOfType;
+ }
+
+ let topFound = false;
+ let enumerator = Services.wm.getEnumerator(aType);
+ let iWindow;
+ let firstWindow;
+
+ while (enumerator.hasMoreElements()) {
+ iWindow = enumerator.getNext();
+ if (!iWindow.closed) {
+ if (!firstWindow) {
+ firstWindow = iWindow;
+ }
+ if (topFound) {
+ toOpenWindow(iWindow);
+ return iWindow;
+ }
+ if (iWindow == topWindow) {
+ topFound = true;
+ }
+ }
+ }
+
+ if (firstWindow == topWindow) // Only one window
+ return null;
+
+ toOpenWindow(firstWindow);
+ return firstWindow;
+}
+
+function windowMenuDidHide()
+{
+ let sep = document.getElementById("sep-window-list");
+ // Clear old items
+ while (sep.nextElementSibling) {
+ sep.nextElementSibling.remove();
+ }
+}
+
+function checkFocusedWindow()
+{
+ let windows = Services.wm.getEnumerator("");
+ let frag = document.createDocumentFragment();
+ while (windows.hasMoreElements()) {
+ let win = windows.getNext();
+ if (win.closed || win.document.documentElement.getAttribute("inwindowmenu") == "false") {
+ continue;
+ }
+ let item = document.createElement("menuitem");
+ item.setAttribute("label", win.document.title);
+ item.setAttribute("type", "radio");
+ if (win == window) {
+ item.setAttribute("checked", "true");
+ }
+ item.addEventListener("command", () => {
+ if (win.windowState == window.STATE_MINIMIZED) {
+ win.restore();
+ }
+ win.focus();
+ });
+ frag.appendChild(item);
+ }
+ document.getElementById("windowPopup").appendChild(frag);
+}
+
+function toProfileManager()
+{
+ var promgrWin = Services.wm.getMostRecentWindow("mozilla:profileSelection");
+ if (promgrWin) {
+ promgrWin.focus();
+ } else {
+ var params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+
+ params.SetNumberStrings(1);
+ params.SetString(0, "menu");
+ window.openDialog("chrome://communicator/content/profile/profileSelection.xul",
+ "",
+ "centerscreen,chrome,titlebar,resizable",
+ params);
+ }
+ // Here, we don't care about the result code
+ // that was returned in the param block.
+}
+
+// This function is only used by macs.
+function ZoomCurrentWindow()
+{
+ if (window.windowState == STATE_NORMAL)
+ window.maximize();
+ else
+ window.restore();
+}
diff --git a/comm/suite/base/content/tasksOverlay.xul b/comm/suite/base/content/tasksOverlay.xul
new file mode 100644
index 0000000000..2835d5302e
--- /dev/null
+++ b/comm/suite/base/content/tasksOverlay.xul
@@ -0,0 +1,124 @@
+<?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://communicator/skin/tasksOverlay.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % tasksDTD SYSTEM "chrome://communicator/locale/tasksOverlay.dtd" >
+%tasksDTD;
+]>
+
+<overlay id="tasksOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://communicator/content/tasksOverlay.js"/>
+
+<keyset id="tasksKeys">
+#ifdef XP_MACOSX
+ <key id="key_minimizeWindow"
+ command="cmd_minimizeWindow"
+ key="&minimizeWindowCmd.key;"
+ modifiers="accel"/>
+#endif
+ <key id="key_navigator" key="&navigatorCmd.commandkey;" command="Tasks:Navigator" modifiers="accel"/>
+ <key id="key_downloadManager" key="&downloadManagerCmd.commandkey;"
+ command="downloadmgr" modifiers="accel"/>
+ <key id="key_addOnsManager" key="&addOnsManagerCmd.commandkey;"
+ command="addonsmgr" modifiers="accel,shift"/>
+ <key id="key_errorConsole" key="&errorConsoleCmd.commandkey2;"
+ command="Tasks:ErrorConsole" modifiers="accel,alt"/>
+</keyset>
+<commandset id="tasksCommands">
+#ifdef XP_MACOSX
+ <command id="cmd_minimizeWindow" oncommand="window.minimize();"/>
+ <command id="cmd_zoomWindow" oncommand="ZoomCurrentWindow();"/>
+#endif
+ <command id="Tasks:DataMan" oncommand="toDataManager();"/>
+ <command id="Tasks:Navigator" oncommand="toNavigator();"/>
+ <command id="Tasks:ErrorConsole" oncommand="toJavaScriptConsole();"/>
+</commandset>
+
+<broadcasterset id="mainBroadcasterSet">
+ <broadcaster id="sync-setup-state" hidden="true"/>
+ <broadcaster id="sync-syncnow-state" hidden="true"/>
+</broadcasterset>
+
+ <!-- Tasks Menu -->
+ <menu id="tasksMenu" label="&tasksMenu.label;" accesskey="&tasksMenu.accesskey;">
+ <menupopup id="taskPopup">
+ <menuitem id="tasksDataman"
+ label="&datamanCmd.label;"
+ accesskey="&datamanCmd.accesskey;"
+ command="Tasks:DataMan"/>
+ <menu id="menu_passwordManager"
+ label="&passwordManagerCmd.label;"
+ accesskey="&passwordManagerCmd.accesskey;">
+ <menupopup id="passwordPopup">
+ <menuitem label="&passwordDisplayCmd.label;"
+ accesskey="&passwordDisplayCmd.accesskey;"
+ oncommand="toDataManager('|passwords');"/>
+ <menuitem label="&passwordExpireCmd.label;"
+ accesskey="&passwordExpireCmd.accesskey;"
+ oncommand="ExpirePassword();"/>
+ </menupopup>
+ </menu>
+ <menuitem id="downloadmgr" label="&downloadManagerCmd.label;"
+ accesskey="&downloadManagerCmd.accesskey;"
+ key="key_downloadManager" oncommand="toDownloadManager();"/>
+ <menuitem id="addonsmgr" label="&addOnsManagerCmd.label;"
+ accesskey="&addOnsManagerCmd.accesskey;"
+ key="key_addOnsManager" oncommand="toEM();"/>
+ <!-- only one of sync-setup or sync-syncnowitem will be showing at once
+ <menuitem id="sync-setup"
+ label="&syncSetup.label;"
+ accesskey="&syncSetup.accesskey;"
+ observes="sync-setup-state"
+ oncommand="gSyncUI.openSetup();"
+ hidden="true"/>
+ <menuitem id="sync-syncnowitem"
+ label="&syncSyncNowItem.label;"
+ accesskey="&syncSyncNowItem.accesskey;"
+ observes="sync-syncnow-state"
+ oncommand="gSyncUI.doSync(event);"
+ hidden="true"/> -->
+ <menuseparator id="devToolsSeparator"/>
+ <menu label="&webDevelopment.label;" accesskey="&webDevelopment.accesskey;">
+ <menupopup id="toolsPopup">
+ <menuitem id="javascriptConsole" label="&errorConsoleCmd.label;"
+ accesskey="&errorConsoleCmd.accesskey;"
+ key="key_errorConsole" command="Tasks:ErrorConsole"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="sep_switchprofile"/>
+ <menuitem id="cmd_switchprofile" label="&switchProfileCmd.label;" accesskey="&switchProfileCmd.accesskey;" oncommand="toProfileManager();"/>
+ </menupopup>
+ </menu>
+
+ <menu id="windowMenu" label="&windowMenu.label;" accesskey="&windowMenu.accesskey;"
+ onpopupshowing="checkFocusedWindow();" onpopuphidden="windowMenuDidHide();">
+ <menupopup id="windowPopup">
+#ifdef XP_MACOSX
+ <menuitem command="cmd_minimizeWindow"
+ label="&minimizeWindowCmd.label;"
+ key="key_minimizeWindow"
+ position="1"/>
+ <menuitem command="cmd_zoomWindow"
+ label="&zoomWindowCmd.label;"
+ position="2"/>
+ <menuseparator position="3"/>
+#endif
+ <menuitem label="&navigatorCmd.label;" accesskey="&navigatorCmd.accesskey;" key="key_navigator" command="Tasks:Navigator" id="tasksMenuNavigator" class="menuitem-iconic icon-navigator16"/>
+
+ <!-- Overlays get stuffed in here. -->
+ <menuseparator id="sep-window-list"/>
+ </menupopup>
+ </menu>
+
+
+ <statusbarpanel id="component-bar" persist="collapsed">
+ <toolbarbutton class="taskbutton" id="mini-nav" oncommand="toNavigator();"
+ tooltiptext="&taskNavigator.tooltip;"/>
+ </statusbarpanel>
+</overlay>
diff --git a/comm/suite/base/content/utilityOverlay.js b/comm/suite/base/content/utilityOverlay.js
new file mode 100644
index 0000000000..627203d435
--- /dev/null
+++ b/comm/suite/base/content/utilityOverlay.js
@@ -0,0 +1,1969 @@
+/* -*- 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/. */
+
+/**
+ * Communicator Shared Utility Library
+ * for shared application glue for the Communicator suite of applications
+ **/
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
+ RecentWindow: "resource:///modules/RecentWindow.jsm",
+});
+
+// XPCOMUtils.defineLazyGetter(this, "Weave", function() {
+// let tmp = {};
+// ChromeUtils.import("resource://services-sync/main.js", tmp);
+// return tmp.Weave;
+// });
+
+/*
+ Note: All Editor/Composer-related methods have been moved to editorApplicationOverlay.js,
+ so app windows that require those must include editorTasksOverlay.xul
+*/
+
+/**
+ * Go into online/offline mode
+ **/
+
+const kProxyManual = ["network.proxy.ftp",
+ "network.proxy.http",
+ "network.proxy.socks",
+ "network.proxy.ssl"];
+var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
+var gShowBiDi = false;
+var gUtilityBundle = null;
+var gPrivate = null;
+
+function toggleOfflineStatus()
+{
+ var checkfunc;
+ try {
+ checkfunc = document.getElementById("offline-status").getAttribute('checkfunc');
+ }
+ catch (ex) {
+ checkfunc = null;
+ }
+
+ if (checkfunc) {
+ if (!eval(checkfunc)) {
+ // the pre-offline check function returned false, so don't go offline
+ return;
+ }
+ }
+ Services.io.manageOfflineStatus = false;
+ Services.io.offline = !Services.io.offline;
+}
+
+function setNetworkStatus(networkProxyType)
+{
+ try {
+ Services.prefs.setIntPref("network.proxy.type", networkProxyType);
+ }
+ catch (ex) {}
+}
+
+function InitProxyMenu()
+{
+ var networkProxyNo = document.getElementById("network-proxy-no");
+ var networkProxyManual = document.getElementById("network-proxy-manual");
+ var networkProxyPac = document.getElementById("network-proxy-pac");
+ var networkProxyWpad = document.getElementById("network-proxy-wpad");
+ var networkProxySystem = document.getElementById("network-proxy-system");
+
+ var proxyLocked = Services.prefs.prefIsLocked("network.proxy.type");
+ if (proxyLocked) {
+ networkProxyNo.setAttribute("disabled", "true");
+ networkProxyWpad.setAttribute("disabled", "true");
+ networkProxySystem.setAttribute("disabled", "true");
+ }
+ else {
+ networkProxyNo.removeAttribute("disabled");
+ networkProxyWpad.removeAttribute("disabled");
+ networkProxySystem.removeAttribute("disabled");
+ }
+
+ // If no proxy is configured, disable the menuitems.
+ // Checking for proxy manual settings.
+ var proxyManuallyConfigured = false;
+ for (var i = 0; i < kProxyManual.length; i++) {
+ if (Services.prefs.getStringPref(kProxyManual[i], "") != "") {
+ proxyManuallyConfigured = true;
+ break;
+ }
+ }
+
+ if (proxyManuallyConfigured && !proxyLocked) {
+ networkProxyManual.removeAttribute("disabled");
+ }
+ else {
+ networkProxyManual.setAttribute("disabled", "true");
+ }
+
+ //Checking for proxy PAC settings.
+ var proxyAutoConfigured = false;
+ if (Services.prefs.getStringPref("network.proxy.autoconfig_url", "") != "")
+ proxyAutoConfigured = true;
+
+ if (proxyAutoConfigured && !proxyLocked) {
+ networkProxyPac.removeAttribute("disabled");
+ }
+ else {
+ networkProxyPac.setAttribute("disabled", "true");
+ }
+
+ // The pref value 3 for network.proxy.type is unused to maintain
+ // backwards compatibility. Treat 3 equally to 0. See bug 115720.
+ var networkProxyStatus = [networkProxyNo, networkProxyManual, networkProxyPac,
+ networkProxyNo, networkProxyWpad,
+ networkProxySystem];
+ var networkProxyType = Services.prefs.getIntPref("network.proxy.type", 0);
+ networkProxyStatus[networkProxyType].setAttribute("checked", "true");
+}
+
+function setProxyTypeUI()
+{
+ var panel = document.getElementById("offline-status");
+ if (!panel)
+ return;
+
+ var onlineTooltip = "onlineTooltip" +
+ Services.prefs.getIntPref("network.proxy.type", 0);
+ panel.setAttribute("tooltiptext", gUtilityBundle.getString(onlineTooltip));
+}
+
+function SetStringPref(aPref, aValue)
+{
+ try {
+ Services.prefs.setStringPref(aPref, aValue);
+ } catch (e) {}
+}
+
+function GetLocalizedStringPref(aPrefName, aDefaultValue)
+{
+ try {
+ return Services.prefs.getComplexValue(aPrefName,
+ Ci.nsIPrefLocalizedString).data;
+ } catch (e) {
+ Cu.reportError("Couldn't get " + aPrefName + " pref: " + e);
+ }
+ return aDefaultValue;
+}
+
+function GetLocalFilePref(aName)
+{
+ try {
+ return Services.prefs.getComplexValue(aName,
+ Ci.nsIFile);
+ } catch (e) {}
+ return null;
+}
+
+/**
+ * Returns the Desktop folder.
+ */
+function GetDesktopFolder()
+{
+ return Services.dirsvc.get("Desk", Ci.nsIFile);
+}
+
+/**
+ * Returns the relevant nsIFile directory.
+ */
+function GetSpecialDirectory(aName)
+{
+ return Services.dirsvc.get(aName, Ci.nsIFile);
+}
+
+function GetUrlbarHistoryFile()
+{
+ var profileDir = GetSpecialDirectory("ProfD");
+ profileDir.append("urlbarhistory.sqlite");
+ return profileDir;
+}
+
+function setOfflineUI(offline)
+{
+ var broadcaster = document.getElementById("Communicator:WorkMode");
+ var panel = document.getElementById("offline-status");
+ if (!broadcaster || !panel) return;
+
+ // Checking for a preference "network.online", if it's locked, disabling
+ // network icon and menu item
+ if (Services.prefs.prefIsLocked("network.online"))
+ broadcaster.setAttribute("disabled", "true");
+
+ if (offline)
+ {
+ broadcaster.setAttribute("offline", "true");
+ broadcaster.setAttribute("checked", "true");
+ panel.removeAttribute("context");
+ panel.setAttribute("tooltiptext", gUtilityBundle.getString("offlineTooltip"));
+ }
+ else
+ {
+ broadcaster.removeAttribute("offline");
+ broadcaster.removeAttribute("checked");
+ panel.setAttribute("context", "networkProperties");
+ setProxyTypeUI();
+ }
+}
+
+function getBrowserURL() {
+
+ try {
+ var url = Services.prefs.getCharPref("browser.chromeURL");
+ if (url)
+ return url;
+ } catch(e) {
+ }
+ return "chrome://navigator/content/navigator.xul";
+}
+
+function goPreferences(paneID)
+{
+ //check for an existing pref window and focus it; it's not application modal
+ var lastPrefWindow = Services.wm.getMostRecentWindow("mozilla:preferences");
+ if (lastPrefWindow)
+ lastPrefWindow.focus();
+ else
+ openDialog("chrome://communicator/content/pref/preferences.xul",
+ "PrefWindow", "non-private,chrome,titlebar,dialog=no,resizable",
+ paneID);
+}
+
+function goToggleToolbar(id, elementID)
+{
+ var toolbar = document.getElementById(id);
+ if (!toolbar)
+ return;
+
+ var type = toolbar.getAttribute("type");
+ var toggleAttribute = type == "menubar" ? "autohide" : "hidden";
+ var hidden = toolbar.getAttribute(toggleAttribute) == "true";
+ var element = document.getElementById(elementID);
+
+ toolbar.setAttribute(toggleAttribute, !hidden);
+ if (element)
+ element.setAttribute("checked", hidden)
+
+ document.persist(id, toggleAttribute);
+ document.persist(elementID, "checked");
+
+ if (toolbar.hasAttribute("customindex"))
+ persistCustomToolbar(toolbar);
+
+}
+
+var gCustomizeSheet = false;
+
+function SuiteCustomizeToolbar(aMenuItem)
+{
+ let toolbar = aMenuItem.parentNode.triggerNode;
+ while (toolbar.localName != "toolbar") {
+ toolbar = toolbar.parentNode;
+ if (!toolbar)
+ return false;
+ }
+ return goCustomizeToolbar(toolbar.toolbox);
+}
+
+function goCustomizeToolbar(toolbox)
+{
+ /* If the toolbox has a method "customizeInit" then call it first.
+ The optional "customizeDone" method will be invoked by the callback
+ from the Customize Window so we don't need to take care of that */
+ if ("customizeInit" in toolbox)
+ toolbox.customizeInit();
+
+ var customizeURL = "chrome://communicator/content/customizeToolbar.xul";
+
+ gCustomizeSheet =
+ Services.prefs.getBoolPref("toolbar.customization.usesheet", false);
+
+ if (gCustomizeSheet) {
+ var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
+ var panel = document.getElementById("customizeToolbarSheetPopup");
+ sheetFrame.hidden = false;
+ sheetFrame.toolbox = toolbox;
+ sheetFrame.panel = panel;
+
+ // The document might not have been loaded yet, if this is the first time.
+ // If it is already loaded, reload it so that the onload initialization
+ // code re-runs.
+ if (sheetFrame.getAttribute("src") == customizeURL)
+ sheetFrame.contentWindow.location.reload();
+ else
+ sheetFrame.setAttribute("src", customizeURL);
+
+ // Open the panel, but make it invisible until the iframe has loaded so
+ // that the user doesn't see a white flash.
+ panel.style.visibility = "hidden";
+ toolbox.addEventListener("beforecustomization", function toolboxBeforeCustom() {
+ toolbox.removeEventListener("beforecustomization", toolboxBeforeCustom);
+ panel.style.removeProperty("visibility");
+ });
+ panel.openPopup(toolbox, "after_start", 0, 0);
+ return sheetFrame.contentWindow;
+ }
+ else {
+ return window.openDialog(customizeURL,
+ "",
+ "chrome,all,dependent",
+ toolbox);
+ }
+}
+
+function onViewToolbarsPopupShowing(aEvent, aInsertPoint)
+{
+ var popup = aEvent.target;
+ if (popup != aEvent.currentTarget)
+ return;
+
+ // Empty the menu
+ var deadItems = popup.getElementsByAttribute("toolbarid", "*");
+ for (let i = deadItems.length - 1; i >= 0; --i)
+ deadItems[i].remove();
+
+ // Thunderbird/Lightning function signature is:
+ // onViewToolbarsPopupShowing(aEvent, toolboxIds, aInsertPoint)
+ // where toolboxIds is either a string or an array of strings.
+ var firstMenuItem = aInsertPoint instanceof XULElement ? aInsertPoint
+ : popup.firstChild;
+
+ var toolbar = document.popupNode || popup;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+ var toolbox = toolbar.toolbox;
+ var externalToolbars = Array.from(toolbox.externalToolbars)
+ .filter(function(toolbar) {
+ return toolbar.hasAttribute("toolbarname")});
+ var toolbars = Array.from(toolbox.getElementsByAttribute("toolbarname", "*"))
+ .filter(function(toolbar) {
+ return !toolbar.hasAttribute("hideinmenu")});
+ toolbars = toolbars.concat(externalToolbars);
+ var menusep = document.getElementById("toolbarmode-sep");
+
+ var menubar = toolbox.getElementsByAttribute("type", "menubar").item(0);
+ if (!menubar || !toolbars.length) {
+ if (menusep)
+ menusep.hidden = true;
+ return;
+ }
+ if (menusep)
+ menusep.hidden = false;
+
+ toolbars.forEach(function(bar) {
+ let type = bar.getAttribute("type");
+ let toggleAttribute = type == "menubar" ? "autohide" : "hidden";
+ let isHidden = bar.getAttribute(toggleAttribute) == "true";
+ let menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("id", "toggle_" + bar.id);
+ menuItem.setAttribute("toolbarid", bar.id);
+ menuItem.setAttribute("type", "checkbox");
+ menuItem.setAttribute("label", bar.getAttribute("toolbarname"));
+ menuItem.setAttribute("accesskey", bar.getAttribute("accesskey"));
+ menuItem.setAttribute("checked", !isHidden);
+ popup.insertBefore(menuItem, firstMenuItem);
+ });
+}
+
+function onToolbarModePopupShowing(aEvent)
+{
+ var popup = aEvent.target;
+
+ var toolbar = document.popupNode;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+ var toolbox = toolbar.toolbox;
+
+ var mode = toolbar.getAttribute("mode") || "full";
+ var modePopup = document.getElementById("toolbarModePopup");
+ var radio = modePopup.getElementsByAttribute("value", mode);
+ radio[0].setAttribute("checked", "true");
+
+ var small = toolbar.getAttribute("iconsize") == "small";
+ var smallicons = document.getElementById("toolbarmode-smallicons");
+ smallicons.setAttribute("checked", small);
+ smallicons.setAttribute("disabled", mode == "text");
+
+ var end = toolbar.getAttribute("labelalign") == "end";
+ var labelalign = document.getElementById("toolbarmode-labelalign");
+ labelalign.setAttribute("checked", end);
+ labelalign.setAttribute("disabled", mode != "full");
+
+ var custommode = (toolbar.getAttribute("mode") || "full") !=
+ (toolbar.getAttribute("defaultmode") ||
+ toolbox.getAttribute("mode") ||
+ "full");
+ var customicon = (toolbar.getAttribute("iconsize") || "large") !=
+ (toolbar.getAttribute("defaulticonsize") ||
+ toolbox.getAttribute("iconsize") ||
+ "large");
+ var customalign = (toolbar.getAttribute("labelalign") || "bottom") !=
+ (toolbar.getAttribute("defaultlabelalign") ||
+ toolbox.getAttribute("labelalign") ||
+ "bottom");
+ var custom = custommode || customicon || customalign ||
+ toolbar.hasAttribute("ignoremodepref");
+
+ var defmode = document.getElementById("toolbarmode-default");
+ defmode.setAttribute("checked", !custom);
+ defmode.setAttribute("disabled", !custom);
+
+ var command = document.getElementById("cmd_CustomizeToolbars");
+ var menuitem = document.getElementById("customize_toolbars");
+ menuitem.hidden = !command;
+ menuitem.previousSibling.hidden = !command;
+}
+
+function onViewToolbarCommand(aEvent)
+{
+ var toolbar = aEvent.originalTarget.getAttribute("toolbarid");
+ if (toolbar)
+ goToggleToolbar(toolbar);
+}
+
+function goSetToolbarState(aEvent)
+{
+ aEvent.stopPropagation();
+ var toolbar = document.popupNode;
+ while (toolbar.localName != "toolbar")
+ toolbar = toolbar.parentNode;
+ var toolbox = toolbar.parentNode;
+
+ var target = aEvent.originalTarget;
+ var mode = target.value;
+ var radiogroup = target.getAttribute("name");
+ var primary = /toolbar-primary/.test(toolbar.getAttribute("class"));
+
+ switch (mode) {
+ case "smallicons":
+ var size = target.getAttribute("checked") == "true" ? "small" : "large";
+ toolbar.setAttribute("iconsize", size);
+ break;
+
+ case "end":
+ var align = target.getAttribute("checked") == "true" ? "end" : "bottom";
+ toolbar.setAttribute("labelalign", align);
+ break;
+
+ case "default":
+ toolbar.setAttribute("mode", toolbar.getAttribute("defaultmode") ||
+ toolbox.getAttribute("mode"));
+ toolbar.setAttribute("iconsize", toolbar.getAttribute("defaulticonsize") ||
+ toolbox.getAttribute("iconsize"));
+ toolbar.setAttribute("labelalign", toolbar.getAttribute("defaultlabelalign") ||
+ toolbox.getAttribute("labelalign"));
+ if (primary)
+ toolbar.removeAttribute("ignoremodepref");
+ break;
+
+ default:
+ toolbar.setAttribute("mode", mode);
+ if (primary)
+ toolbar.setAttribute("ignoremodepref", "true");
+ break;
+ }
+ document.persist(toolbar.id, "mode");
+ document.persist(toolbar.id, "iconsize");
+ document.persist(toolbar.id, "labelalign");
+ if (primary)
+ document.persist(toolbar.id, "ignoremodepref");
+ if (toolbar.hasAttribute("customindex"))
+ persistCustomToolbar(toolbar);
+}
+
+function persistCustomToolbar(toolbar)
+{
+ var toolbox = toolbar.parentNode;
+ var name = toolbar.getAttribute("toolbarname").replace(" ", "_");
+ var attrs = ["mode", "iconsize", "labelalign", "hidden"];
+ for (let i = 0; i < attrs.length; i++) {
+ let value = toolbar.getAttribute(attrs[i]);
+ let attr = name + attrs[i];
+ toolbox.toolbarset.setAttribute(attr, value);
+ document.persist(toolbox.toolbarset.id, attr);
+ }
+}
+
+/* Common Customize Toolbar code */
+
+function toolboxCustomizeInit(menubarID)
+{
+ // Disable the toolbar context menu items
+ var menubar = document.getElementById(menubarID);
+ for (let i = 0; i < menubar.childNodes.length; ++i) {
+ let item = menubar.childNodes[i];
+ if (item.getAttribute("disabled") != "true") {
+ item.setAttribute("disabled", "true");
+ item.setAttribute("saved-disabled", "false");
+ }
+ }
+
+ var cmd = document.getElementById("cmd_CustomizeToolbars");
+ cmd.setAttribute("disabled", "true");
+}
+
+function toolboxCustomizeDone(menubarID, toolbox, aToolboxChanged)
+{
+ if (gCustomizeSheet) {
+ document.getElementById("customizeToolbarSheetIFrame").hidden = true;
+ document.getElementById("customizeToolbarSheetPopup").hidePopup();
+ if (content)
+ content.focus();
+ else
+ window.focus();
+ }
+
+ // Re-enable parts of the UI we disabled during the dialog
+ var menubar = document.getElementById(menubarID);
+ for (let i = 0; i < menubar.childNodes.length; ++i) {
+ let item = menubar.childNodes[i];
+ if (item.hasAttribute("saved-disabled")) {
+ item.removeAttribute("disabled");
+ item.removeAttribute("saved-disabled");
+ }
+ }
+
+ var cmd = document.getElementById("cmd_CustomizeToolbars");
+ cmd.removeAttribute("disabled");
+
+ var toolbars = toolbox.getElementsByAttribute("customindex", "*");
+ for (let i = 0; i < toolbars.length; ++i) {
+ persistCustomToolbar(toolbars[i]);
+ }
+}
+
+function toolboxCustomizeChange(toolbox, event)
+{
+ if (event != "reset")
+ return;
+ var toolbars = toolbox.getElementsByAttribute("toolbarname", "*");
+ for (let i = 0; i < toolbars.length; ++i) {
+ let toolbar = toolbars[i];
+ toolbar.setAttribute("labelalign",
+ toolbar.getAttribute("defaultlabelalign") ||
+ toolbox.getAttribute("labelalign"));
+ document.persist(toolbar.id, "labelalign");
+ let primary = /toolbar-primary/.test(toolbar.getAttribute("class"));
+ if (primary) {
+ toolbar.removeAttribute("ignoremodepref");
+ document.persist(toolbar.id, "ignoremodepref");
+ }
+ }
+}
+
+function goClickThrobber(urlPref, aEvent)
+{
+ var url = GetLocalizedStringPref(urlPref);
+ if (url)
+ openUILinkIn(url, whereToOpenLink(aEvent, false, true, true));
+}
+
+function getTopWin(skipPopups) {
+ // If this is called in a browser window, use that window regardless of
+ // whether it's the frontmost window, since commands can be executed in
+ // background windows (bug 626148).
+ if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
+ (!skipPopups || top.toolbar.visible))
+ return top;
+
+ let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+ return RecentWindow.getMostRecentBrowserWindow({private: isPrivate,
+ allowPopups: !skipPopups});
+}
+
+function isRestricted( url )
+{
+ try {
+ let uri = Services.uriFixup
+ .createFixupURI(url, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
+ const URI_INHERITS_SECURITY_CONTEXT =
+ Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT;
+ return Services.netUtils
+ .URIChainHasFlags(uri, URI_INHERITS_SECURITY_CONTEXT);
+ } catch (e) {
+ return false;
+ }
+}
+
+function goAbout(aProtocol)
+{
+ var target;
+ var url = "about:" + (aProtocol || "");
+ var defaultAboutState = Services.prefs.getIntPref("browser.link.open_external");
+
+ switch (defaultAboutState) {
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
+ target = "window";
+ break;
+ case Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW:
+ target = "current";
+ break;
+ default:
+ target = "tabfocused";
+ }
+ openUILinkIn(url, target);
+}
+
+function goTroubleshootingPage()
+{
+ goAbout("support");
+}
+
+function goReleaseNotes()
+{
+ // get release notes URL from prefs
+ try {
+ openUILink(Services.urlFormatter.formatURLPref("app.releaseNotesURL"));
+ }
+ catch (ex) { dump(ex); }
+}
+
+function openDictionaryList()
+{
+ try {
+ openAsExternal(Services.urlFormatter.formatURLPref("spellchecker.dictionaries.download.url"));
+ }
+ catch (ex) {
+ dump(ex);
+ }
+}
+
+// Prompt user to restart the browser in safe mode
+function safeModeRestart()
+{
+ // prompt the user to confirm
+ var promptTitle = gUtilityBundle.getString("safeModeRestartPromptTitle");
+ var promptMessage = gUtilityBundle.getString("safeModeRestartPromptMessage");
+ var restartText = gUtilityBundle.getString("safeModeRestartButton");
+ var checkboxText = gUtilityBundle.getString("safeModeRestartCheckbox");
+ var checkbox = { value: true };
+ var buttonFlags = (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_0_DEFAULT;
+
+ var rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
+ buttonFlags, restartText, null, null,
+ checkboxText, checkbox);
+ if (rv == 0) {
+ if (checkbox.value)
+ Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment)
+ .set("MOZ_SAFE_MODE_RESTART", "1");
+ BrowserUtils.restartApplication();
+ }
+}
+
+function checkForUpdates()
+{
+ var um = Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager);
+ var prompter = Cc["@mozilla.org/updates/update-prompt;1"]
+ .createInstance(Ci.nsIUpdatePrompt);
+
+ // If there's an update ready to be applied, show the "Update Downloaded"
+ // UI instead and let the user know they have to restart the browser for
+ // the changes to be applied.
+ if (um.activeUpdate && um.activeUpdate.state == "pending")
+ prompter.showUpdateDownloaded(um.activeUpdate);
+ else
+ prompter.checkForUpdates();
+}
+
+function updateCheckUpdatesItem()
+{
+ var hasUpdater = "nsIApplicationUpdateService" in Ci;
+ var checkForUpdates = document.getElementById("checkForUpdates");
+
+ if (!hasUpdater)
+ {
+ var updateSeparator = document.getElementById("updateSeparator");
+
+ checkForUpdates.hidden = true;
+ updateSeparator.hidden = true;
+ return;
+ }
+
+ var updates = Cc["@mozilla.org/updates/update-service;1"]
+ .getService(Ci.nsIApplicationUpdateService);
+ var um = Cc["@mozilla.org/updates/update-manager;1"]
+ .getService(Ci.nsIUpdateManager);
+
+ // Disable the UI if the update enabled pref has been locked by the
+ // administrator or if we cannot update for some other reason.
+ var canCheckForUpdates = updates.canCheckForUpdates;
+ checkForUpdates.setAttribute("disabled", !canCheckForUpdates);
+
+ if (!canCheckForUpdates)
+ return;
+
+ // By default, show "Check for Updates..."
+ var key = "default";
+ if (um.activeUpdate) {
+ switch (um.activeUpdate.state) {
+ case "downloading":
+ // If we're downloading an update at present, show the text:
+ // "Downloading SeaMonkey x.x..." otherwise we're paused, and show
+ // "Resume Downloading SeaMonkey x.x..."
+ key = updates.isDownloading ? "downloading" : "resume";
+ break;
+ case "pending":
+ // If we're waiting for the user to restart, show: "Apply Downloaded
+ // Updates Now..."
+ key = "pending";
+ break;
+ }
+ }
+
+ // If there's an active update, substitute its name into the label
+ // we show for this item, otherwise display a generic label.
+ if (um.activeUpdate && um.activeUpdate.name)
+ checkForUpdates.label = gUtilityBundle.getFormattedString("updatesItem_" + key,
+ [um.activeUpdate.name]);
+ else
+ checkForUpdates.label = gUtilityBundle.getString("updatesItem_" + key + "Fallback");
+
+ checkForUpdates.accessKey = gUtilityBundle.getString("updatesItem_" + key + "AccessKey");
+
+ if (um.activeUpdate && updates.isDownloading)
+ checkForUpdates.setAttribute("loading", "true");
+ else
+ checkForUpdates.removeAttribute("loading");
+}
+
+// update menu items that rely on focus
+function goUpdateGlobalEditMenuItems()
+{
+ goUpdateCommand('cmd_undo');
+ goUpdateCommand('cmd_redo');
+ goUpdateCommand('cmd_cut');
+ goUpdateCommand('cmd_copy');
+ goUpdateCommand('cmd_paste');
+ goUpdateCommand('cmd_selectAll');
+ goUpdateCommand('cmd_delete');
+ if (gShowBiDi)
+ goUpdateCommand('cmd_switchTextDirection');
+}
+
+// update menu items that rely on the current selection
+function goUpdateSelectEditMenuItems()
+{
+ goUpdateCommand('cmd_cut');
+ goUpdateCommand('cmd_copy');
+ goUpdateCommand('cmd_delete');
+ goUpdateCommand('cmd_selectAll');
+}
+
+// update menu items that relate to undo/redo
+function goUpdateUndoEditMenuItems()
+{
+ goUpdateCommand('cmd_undo');
+ goUpdateCommand('cmd_redo');
+}
+
+// update menu items that depend on clipboard contents
+function goUpdatePasteMenuItems()
+{
+ goUpdateCommand('cmd_paste');
+}
+
+// update Find As You Type menu items, they rely on focus
+function goUpdateFindTypeMenuItems()
+{
+ goUpdateCommand('cmd_findTypeText');
+ goUpdateCommand('cmd_findTypeLinks');
+}
+
+// Gather all descendent text under given document node.
+function gatherTextUnder(root)
+{
+ var text = "";
+ var node = root.firstChild;
+ var depth = 1;
+ while ( node && depth > 0 ) {
+ // See if this node is text.
+ if ( node.nodeType == Node.TEXT_NODE ) {
+ // Add this text to our collection.
+ text += " " + node.data;
+ } else if ( node instanceof HTMLImageElement ) {
+ // If it has an alt= attribute, add that.
+ var altText = node.getAttribute( "alt" );
+ if ( altText && altText != "" ) {
+ text += " " + altText;
+ }
+ }
+ // Find next node to test.
+ // First, see if this node has children.
+ if ( node.hasChildNodes() ) {
+ // Go to first child.
+ node = node.firstChild;
+ depth++;
+ } else {
+ // No children, try next sibling.
+ if ( node.nextSibling ) {
+ node = node.nextSibling;
+ } else {
+ // Last resort is a sibling of an ancestor.
+ while ( node && depth > 0 ) {
+ node = node.parentNode;
+ depth--;
+ if ( node.nextSibling ) {
+ node = node.nextSibling;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Strip leading and trailing whitespaces,
+ // then compress remaining whitespaces.
+ return text.trim().replace(/\s+/g, " ");
+}
+
+var offlineObserver = {
+ observe: function(subject, topic, state) {
+ // sanity checks
+ if (topic != "network:offline-status-changed") return;
+ setOfflineUI(state == "offline");
+ }
+}
+
+var proxyTypeObserver = {
+ observe: function(subject, topic, state) {
+ // sanity checks
+ if (state == "network.proxy.type" && !Services.io.offline)
+ setProxyTypeUI();
+ }
+}
+
+function utilityOnLoad(aEvent)
+{
+ gUtilityBundle = document.getElementById("bundle_utilityOverlay");
+
+ var broadcaster = document.getElementById("Communicator:WorkMode");
+ if (!broadcaster) return;
+
+ Services.obs.addObserver(offlineObserver, "network:offline-status-changed");
+ // make sure we remove this observer later
+ Services.prefs.addObserver("network.proxy.type", proxyTypeObserver);
+
+ addEventListener("unload", utilityOnUnload, false);
+
+ // set the initial state
+ setOfflineUI(Services.io.offline);
+
+ // Check for system proxy settings class and show menuitem if present
+ if ("@mozilla.org/system-proxy-settings;1" in Cc &&
+ document.getElementById("network-proxy-system"))
+ document.getElementById("network-proxy-system").hidden = false;
+}
+
+function utilityOnUnload(aEvent)
+{
+ Services.obs.removeObserver(offlineObserver, "network:offline-status-changed");
+ Services.prefs.removeObserver("network.proxy.type", proxyTypeObserver);
+}
+
+addEventListener("load", utilityOnLoad, false);
+
+/**
+ * example use:
+ * suggestUniqueFileName("testname", ".txt", ["testname.txt", "testname(2).txt"])
+ * returns "testname(3).txt"
+ * does not check file system for existing files
+ *
+ * @param aBaseName base name for generating unique filenames.
+ *
+ * @param aExtension extension name to use for the generated filename.
+ *
+ * @param aExistingNames array of names in use.
+ *
+ * @return suggested filename as a string.
+ */
+function suggestUniqueFileName(aBaseName, aExtension, aExistingNames)
+{
+ var suffix = 1;
+ aBaseName = validateFileName(aBaseName);
+ var suggestion = aBaseName + aExtension;
+ while (aExistingNames.includes(suggestion))
+ {
+ suffix++;
+ suggestion = aBaseName + "(" + suffix + ")" + aExtension;
+ }
+ return suggestion;
+}
+
+function focusElement(aElement)
+{
+ if (isElementVisible(aElement))
+ aElement.focus();
+}
+
+function isElementVisible(aElement)
+{
+ if (!aElement)
+ return false;
+
+ // If aElement or a direct or indirect parent is hidden or collapsed,
+ // height, width or both will be 0.
+ var bo = aElement.boxObject;
+ return (bo.height > 0 && bo.width > 0);
+}
+
+function makeURLAbsolute(aBase, aUrl, aCharset)
+{
+ // Construct nsIURL.
+ return Services.io.newURI(aUrl, aCharset,
+ Services.io.newURI(aBase, aCharset)).spec;
+}
+
+/**
+ * whereToLoadExternalLink: Returns values for opening a new external link.
+ *
+ * @returns (object[]} an array of objects with the following structure:
+ * - (string) where location where to open the link.
+ * - (bool) loadInBackground load url in background.
+ * - (bool) Focus browser after load.
+ */
+function whereToLoadExternalLink() {
+ let openParms = {
+ where: null,
+ loadInBackground: false,
+ avoidBrowserFocus: false,
+ }
+
+ switch (Services.prefs.getIntPref("browser.link.open_external")) {
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW:
+ openParms.where = "window";
+ break;
+ case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB:
+ openParms.where = "tab";
+ break;
+ case Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW:
+ openParms.where = "current";
+ break;
+ default:
+ console.log("Check pref browser.link.open_external");
+ openParms.where = "current";
+ }
+ openParms.loadInBackground =
+ Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
+
+ openParms.avoidBrowserFocus =
+ Services.prefs.getBoolPref("browser.tabs.avoidBrowserFocus");
+
+ return openParms;
+}
+
+function openAsExternal(aURL) {
+ let openParms = whereToLoadExternalLink();
+
+ openNewTabWindowOrExistingWith(aURL, openParms.where, null,
+ openParms.loadInBackground);
+}
+
+/**
+ * openNewTabWith: opens a new tab with the given URL.
+ * openNewWindowWith: opens a new window with the given URL.
+ * openNewPrivateWith: opens a private window with the given URL.
+ *
+ * @param aURL
+ * The URL to open (as a string).
+ * @param aDocument
+ * The document from which the URL came, or null. This is used to set
+ * the referrer header and to do a security check of whether the
+ * document is allowed to reference the URL. If null, there will be no
+ * referrer header and no security check.
+ * @param aPostData
+ * Form POST data, or null.
+ * @param aEvent
+ * The triggering event (for the purpose of determining whether to open
+ * in the background), or null.
+ * @param aAllowThirdPartyFixup
+ * If true, then we allow the URL text to be sent to third party
+ * services (e.g., Google's I Feel Lucky) for interpretation. This
+ * parameter may be undefined in which case it is treated as false.
+ * @param [optional] aReferrer
+ * If aDocument is null, then this will be used as the referrer.
+ * There will be no security check.
+ * @param [optional] aReferrerPolicy
+ * Referrer policy - Ci.nsIHttpChannel.REFERRER_POLICY_*.
+ */
+function openNewPrivateWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy) {
+ return openNewTabWindowOrExistingWith(aURL, "private", aDocument, null,
+ aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy);
+}
+
+function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy) {
+ return openNewTabWindowOrExistingWith(aURL, "window", aDocument, null,
+ aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy);
+}
+
+function openNewTabWith(aURL, aDocument, aPostData, aEvent,
+ aAllowThirdPartyFixup, aReferrer, aReferrerPolicy) {
+ let where = aEvent && aEvent.shiftKey ? "tabshifted" : "tab";
+ return openNewTabWindowOrExistingWith(aURL, where, aDocument, null,
+ aPostData, aAllowThirdPartyFixup,
+ aReferrer, aReferrerPolicy);
+}
+
+function openNewTabWindowOrExistingWith(aURL, aWhere, aDocument,
+ aLoadInBackground, aPostData,
+ aAllowThirdPartyFixup, aReferrer,
+ aReferrerPolicy) {
+ // Make sure we are allowed to open this url
+ if (aDocument)
+ urlSecurityCheck(aURL, aDocument.nodePrincipal);
+
+ // Where appropriate we want to pass the charset of the
+ // current document over to a new tab / window.
+ var originCharset = null;
+ if (aWhere != "current") {
+ originCharset = aDocument && aDocument.characterSet;
+ if (!originCharset &&
+ document.documentElement.getAttribute("windowtype") == "navigator:browser")
+ originCharset = window.content.document.characterSet;
+ }
+
+ var isPrivate = false;
+ if (aWhere == "private") {
+ aWhere = "window";
+ isPrivate = true;
+ }
+ var referrerURI = aDocument ? aDocument.documentURIObject : aReferrer;
+ return openLinkIn(aURL, aWhere,
+ { charset: originCharset,
+ postData: aPostData,
+ inBackground: aLoadInBackground,
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ referrerURI: referrerURI,
+ referrerPolicy: aReferrerPolicy,
+ private: isPrivate, });
+}
+
+/**
+ * Handle command events bubbling up from error page content
+ * called from oncommand by <browser>s that support error pages
+ */
+function BrowserOnCommand(event)
+{
+ // Don't trust synthetic events
+ if (!event.isTrusted)
+ return;
+
+ const ot = event.originalTarget;
+ const ownerDoc = ot.ownerDocument;
+ const docURI = ownerDoc.documentURI;
+ const buttonID = ot.getAttribute("anonid");
+
+ // If the event came from an ssl error page, it is probably either the "Add
+ // Exception" or "Get Me Out Of Here" button
+ if (docURI.startsWith("about:certerror?")) {
+ if (buttonID == "exceptionDialogButton") {
+ let docshell = ownerDoc.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let securityInfo = docshell.failedChannel.securityInfo;
+ let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+ .SSLStatus;
+
+ let params = { exceptionAdded : false, sslStatus : sslStatus };
+
+ switch (Services.prefs.getIntPref("browser.ssl_override_behavior", 2)) {
+ case 2 : // Pre-fetch & pre-populate.
+ params.prefetchCert = true;
+ // Fall through.
+ case 1 : // Pre-populate.
+ params.location = ownerDoc.location.href;
+ }
+
+ window.openDialog('chrome://pippki/content/exceptionDialog.xul',
+ '', 'chrome,centerscreen,modal', params);
+
+ // If the user added the exception cert, attempt to reload the page
+ if (params.exceptionAdded)
+ ownerDoc.location.reload();
+ }
+ else if (buttonID == "getMeOutOfHereButton") {
+ // Redirect them to a known-functioning page, default start page
+ getMeOutOfHere();
+ }
+ }
+ else if (docURI.startsWith("about:blocked")) {
+ // The event came from a button on a malware/phishing block page
+ // First check whether the reason, so that we can
+ // use the right strings/links
+ let reason = "phishing";
+
+ if (/e=malwareBlocked/.test(docURI)) {
+ reason = "malware";
+ } else if (/e=unwantedBlocked/.test(docURI)) {
+ reason = "unwanted";
+ } else if (/e=harmfulBlocked/.test(docURI)) {
+ reason = "harmful";
+ }
+
+ let docShell = ownerDoc.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let blockedInfo = {};
+ if (docShell.failedChannel) {
+ let classifiedChannel = docShell.failedChannel.
+ QueryInterface(Ci.nsIClassifiedChannel);
+ if (classifiedChannel) {
+ let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
+
+ let reportUri = httpChannel.URI.clone();
+
+ // Remove the query to avoid leaking sensitive data
+ if (reportUri instanceof Ci.nsIURL) {
+ reportUri = reportUri.mutate().setQuery("").finalize();
+ }
+
+ blockedInfo = { list: classifiedChannel.matchedList,
+ provider: classifiedChannel.matchedProvider,
+ uri: reportUri.asciiSpec };
+ }
+ }
+
+ switch (buttonID) {
+ case "getMeOutOfHereButton":
+ getMeOutOfHere();
+ break;
+ case "reportButton":
+ // This is the "Why is this site blocked" button. We redirect
+ // to the generic page describing phishing/malware protection.
+ try {
+ loadURI(Services.urlFormatter.formatURLPref("browser.safebrowsing.warning.infoURL"));
+ } catch (e) {
+ Cu.reportError("Couldn't get phishing info URL: " + e);
+ }
+ break;
+ case "ignoreWarningButton":
+ if (Services.prefs.getBoolPref("browser.safebrowsing.allowOverride")) {
+ getBrowser().getNotificationBox().ignoreSafeBrowsingWarning(reason, blockedInfo);
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * Re-direct the browser to a known-safe page. This function is
+ * used when, for example, the user browses to a known malware page
+ * and is presented with about:blocked. The "Get me out of here!"
+ * button should take the user to the default start page so that even
+ * when their own homepage is infected, we can get them somewhere safe.
+ */
+function getMeOutOfHere() {
+ // Get the start page from the *default* pref branch, not the user's
+ var prefs = Services.prefs.getDefaultBranch(null);
+ var url = "about:blank";
+ try {
+ url = prefs.getComplexValue("browser.startup.homepage",
+ Ci.nsIPrefLocalizedString).data;
+ } catch(e) {}
+ loadURI(url);
+}
+
+function popupNotificationMenuShowing(event)
+{
+ var notificationbox = document.popupNode.parentNode.control;
+ var uri = notificationbox.activeBrowser.currentURI;
+ var allowPopupsForSite = document.getElementById("allowPopupsForSite");
+ allowPopupsForSite.notificationbox = notificationbox;
+ var showPopupManager = document.getElementById("showPopupManager");
+
+ // Only offer this menu item for the top window.
+ // See bug 280536 for problems with frames and iframes.
+ try {
+ // uri.host generates an exception on nsISimpleURIs.
+ var allowString = gUtilityBundle.getFormattedString("popupAllow", [uri.host || uri.spec]);
+ allowPopupsForSite.setAttribute("label", allowString);
+ showPopupManager.hostport = uri.hostPort;
+ allowPopupsForSite.hidden = gPrivate;
+ } catch (ex) {
+ allowPopupsForSite.hidden = true;
+ showPopupManager.hostport = "";
+ }
+
+ var separator = document.getElementById("popupNotificationMenuSeparator");
+ separator.hidden = !createShowPopupsMenu(event.target, notificationbox.activeBrowser);
+}
+
+function RemovePopupsItems(parent)
+{
+ while (parent.lastChild && parent.lastChild.hasAttribute("popupReportIndex"))
+ parent.lastChild.remove();
+}
+
+function createShowPopupsMenu(parent, browser)
+{
+ if (!browser)
+ return false;
+
+ if (!browser.blockedPopups ||
+ browser.blockedPopups.count == 0)
+ return false;
+
+ parent.browser = browser;
+
+ browser.retrieveListOfBlockedPopups().then(blockedPopups => {
+
+ for (var i = 0; i < blockedPopups.length; i++) {
+
+ let blockedPopup = blockedPopups[i];
+ // popupWindowURI will be null if the file picker popup is blocked.
+ if (!blockedPopup.popupWindowURIspec)
+ continue;
+
+ let str = gUtilityBundle.getFormattedString("popupMenuShow", [blockedPopup.popupWindowURIspec]);
+ // Check for duplicates in the blockedPopups list and reuse the old menuitem.
+ let menuitem = parent.getElementsByAttribute("label", str).item(0);
+ if (!menuitem) {
+ menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", str);
+ }
+ menuitem.setAttribute("popupReportIndex", i);
+ parent.appendChild(menuitem);
+ }
+ }, null);
+
+ return parent.getElementsByAttribute("popupReportIndex", "*").item(0) != null;
+}
+
+function popupBlockerMenuCommand(target)
+{
+ if (target.hasAttribute("popupReportIndex"))
+ target.parentNode.browser.unblockPopup(target.getAttribute("popupReportIndex"));
+}
+
+function hostUrl()
+{
+ var url = "";
+ try {
+ url = getBrowser().currentURI.scheme + "://" + getBrowser().currentURI.hostPort;
+ } catch (e) {}
+ return url;
+}
+
+function disablePopupBlockerNotifications()
+{
+ Services.prefs.setBoolPref("privacy.popups.showBrowserMessage", false);
+}
+
+// Used as an onclick handler for UI elements with link-like behavior.
+// e.g. onclick="checkForMiddleClick(this, event);"
+function checkForMiddleClick(node, event) {
+ // We should be using the disabled property here instead of the attribute,
+ // but some elements that this function is used with don't support it (e.g.
+ // menuitem).
+ if (node.getAttribute("disabled") == "true")
+ return; // Do nothing
+
+ if (event.button == 1) {
+ /* Execute the node's oncommand or command.
+ *
+ * XXX: we should use node.oncommand(event) once bug 246720 is fixed.
+ */
+ var target = node.hasAttribute("oncommand") ? node :
+ node.ownerDocument.getElementById(node.getAttribute("command"));
+ var fn = new Function("event", target.getAttribute("oncommand"));
+ fn.call(target, event);
+
+ // If the middle-click was on part of a menu, close the menu.
+ // (Menus close automatically with left-click but not with middle-click.)
+ closeMenus(event.target);
+ }
+}
+
+// Closes all popups that are ancestors of the node.
+function closeMenus(node) {
+ if ("tagName" in node) {
+ if (node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ && (node.tagName == "menupopup" || node.tagName == "popup"))
+ node.hidePopup();
+
+ closeMenus(node.parentNode);
+ }
+}
+
+/**
+ * Toggle a splitter to show or hide some piece of UI (e.g. the message preview
+ * pane).
+ *
+ * @param aSplitterId the splitter that should be toggled
+ */
+function togglePaneSplitter(aSplitterId)
+{
+ var splitter = document.getElementById(aSplitterId);
+ if (splitter.getAttribute("state") == "collapsed")
+ splitter.setAttribute("state", "open");
+ else
+ splitter.setAttribute("state", "collapsed");
+}
+
+/* openUILink handles clicks on UI elements that cause URLs to load.
+ *
+ * As the third argument, you may pass an object with the same properties as
+ * accepted by openUILinkIn, plus "ignoreButton" and "ignoreSave".
+ *
+ * Note: Firefox uses aIgnoreAlt while SeaMonkey uses aIgnoreSave because in
+ * SeaMonkey, Save can be Alt or Shift depending on ui.key.saveLink.shift.
+ *
+ * For API compatibility with Firefox the object version uses params.ignoreAlt
+ * although for SeaMonkey it is effectively ignoreSave.
+ */
+function openUILink(url, aEvent, aIgnoreButton, aIgnoreSave,
+ aAllowThirdPartyFixup, aPostData, aReferrerURI) {
+ var params;
+ if (aIgnoreButton && typeof aIgnoreButton == "object") {
+ params = aIgnoreButton;
+
+ // don't forward "ignoreButton" and "ignoreSave" to openUILinkIn.
+ aIgnoreButton = params.ignoreButton;
+ aIgnoreSave = params.ignoreAlt;
+ delete params.ignoreButton;
+ delete params.ignoreAlt;
+ }
+ else {
+ params = {allowThirdPartyFixup: aAllowThirdPartyFixup,
+ postData: aPostData,
+ referrerURI: aReferrerURI,
+ referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+ initiatingDoc: aEvent ? aEvent.target.ownerDocument : document,}
+ }
+
+ var where = whereToOpenLink(aEvent, aIgnoreButton, aIgnoreSave);
+ return openUILinkIn(url, where, params);
+}
+
+/* whereToOpenLink() looks at an event to decide where to open a link.
+ *
+ * The event may be a mouse event (click, double-click, middle-click) or keypress event (enter).
+ *
+ * The logic for modifiers is as following:
+ * If browser.tabs.opentabfor.middleclick is true, then Ctrl (or Meta) and middle-click
+ * open a new tab, depending on Shift, browser.tabs.loadInBackground, and
+ * ignoreBackground.
+ * Otherwise if middlemouse.openNewWindow is true, then Ctrl (or Meta) and middle-click
+ * open a new window.
+ * Otherwise if middle-click is pressed then nothing happens.
+ * Save is Alt or Shift depending on the ui.key.saveLink.shift preference.
+ * Otherwise if Alt, or Shift, or Ctrl (or Meta) is pressed then nothing happens.
+ * Otherwise the most recent browser is used for left clicks.
+ *
+ * Exceptions:
+ * - Alt is ignored for menu items selected using the keyboard so you don't accidentally save stuff.
+ * - Alt is hard to use in context menus, because pressing Alt closes the menu.
+ * - Alt can't be used on the bookmarks toolbar because Alt is used for "treat this as something draggable".
+ * - The button is ignored for the middle-click-paste-URL feature, since it's always a middle-click.
+ */
+function whereToOpenLink(e, ignoreButton, ignoreSave, ignoreBackground = false)
+{
+ // This method must treat a null event like a left click without modifier keys (i.e.
+ // e = { shiftKey:false, ctrlKey:false, metaKey:false, altKey:false, button:0 })
+ // for compatibility purposes.
+ if (!e)
+ return "current";
+
+ var shift = e.shiftKey;
+ var ctrl = e.ctrlKey;
+ var meta = e.metaKey;
+ var alt = e.altKey && !ignoreSave;
+
+ // ignoreButton allows "middle-click paste" to use function without always opening in a new window.
+ var middle = !ignoreButton && e.button == 1;
+
+ // Don't do anything special with right-mouse clicks. They're probably clicks on context menu items.
+
+ // On macOS ctrl is not evaluated.
+ var metaKey = AppConstants.platform == "macosx" ? meta : ctrl;
+
+ if (metaKey || middle) {
+ if (Services.prefs.getBoolPref("browser.tabs.opentabfor.middleclick", true))
+ return ignoreBackground ? "tabfocused" : shift ? "tabshifted" : "tab";
+ if (Services.prefs.getBoolPref("middlemouse.openNewWindow", true))
+ return "window";
+ if (middle)
+ return null;
+ }
+ if (!ignoreSave) {
+ if (Services.prefs.getBoolPref("ui.key.saveLink.shift", true) ? shift : alt)
+ return "save";
+ }
+ if (alt || shift || meta || ctrl)
+ return null;
+
+ return "current";
+}
+
+/* openUILinkIn opens a URL in a place specified by the parameter |where|.
+ *
+ * |where| can be:
+ * "current" current tab (if there aren't any browser windows, then in a new window instead)
+ * "tab" new tab (if there aren't any browser windows, then in a new window instead)
+ * "tabshifted" same as "tab" but in background if default is to select new tabs, and vice versa
+ * "tabfocused" same as "tab" but explicitly focus new tab
+ * "private" private browsing window
+ * "window" new window
+ * "save" save to disk (with no filename hint!)
+ *
+ * aAllowThirdPartyFixup controls whether third party services such as Google's
+ * I'm Feeling Lucky are allowed to interpret this URL. This parameter may be
+ * undefined, which is treated as false.
+ *
+ * Instead of aAllowThirdPartyFixup, you may also pass an object with any of
+ * these properties:
+ * allowThirdPartyFixup (boolean)
+ * postData (nsIInputStream)
+ * referrerURI (nsIURI)
+ * relatedToCurrent (boolean)
+ * initiatingDoc (document)
+ * userContextId (unsigned int)
+ */
+function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
+ var params;
+
+ if (arguments.length == 3 && typeof arguments[2] == "object") {
+ params = aAllowThirdPartyFixup;
+ } else {
+ params = {
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ postData: aPostData,
+ referrerURI: aReferrerURI,
+ referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
+ };
+ }
+
+ if (where == "private") {
+ where = "window";
+ params.private = true;
+ }
+
+ params.fromChrome = true;
+
+ return openLinkIn(url, where, params);
+}
+
+function openLinkIn(url, where, params)
+{
+ if (!where || !url)
+ return null;
+
+ var aFromChrome = params.fromChrome;
+ var aAllowThirdPartyFixup = params.allowThirdPartyFixup;
+ var aPostData = params.postData;
+ var aCharset = params.charset;
+ var aReferrerURI = params.referrerURI;
+ var aReferrerPolicy = ("referrerPolicy" in params ?
+ params.referrerPolicy : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
+ var aRelatedToCurrent = params.relatedToCurrent;
+ var aAllowMixedContent = params.allowMixedContent;
+ var aInBackground = params.inBackground;
+ var aAvoidBrowserFocus = params.avoidBrowserFocus;
+ var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
+ var aInitiatingDoc = params.initiatingDoc ? params.initiatingDoc : document;
+ var aIsPrivate = params.private;
+ var aNoReferrer = params.noReferrer;
+ var aUserContextId = params.userContextId;
+ var aPrincipal = params.originPrincipal;
+ var aTriggeringPrincipal = params.triggeringPrincipal;
+ var aForceAboutBlankViewerInCurrent =
+ params.forceAboutBlankViewerInCurrent;
+
+ if (where == "save") {
+ saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI,
+ aInitiatingDoc);
+ return null;
+ }
+
+ // Establish which window we'll load the link in.
+ var w = getTopWin();
+ // We don't want to open tabs in popups, so try to find a non-popup window in
+ // that case.
+ if ((where == "tab" || where == "tabshifted") && w && !w.toolbar.visible) {
+ w = getTopWin(true);
+ aRelatedToCurrent = false;
+ }
+
+ // Teach the principal about the right OA to use, e.g. in case when
+ // opening a link in a new private window, or in a new container tab.
+ // Please note we do not have to do that for SystemPrincipals and we
+ // can not do it for NullPrincipals since NullPrincipals are only
+ // identical if they actually are the same object (See Bug: 1346759)
+ function useOAForPrincipal(principal) {
+ if (principal && principal.isCodebasePrincipal) {
+ let attrs = {
+ userContextId: aUserContextId,
+ };
+ return Services.scriptSecurityManager.createCodebasePrincipal(principal.URI, attrs);
+ }
+ return principal;
+ }
+ aPrincipal = useOAForPrincipal(aPrincipal);
+ aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
+
+ if (!w || where == "window") {
+ let features = "chrome,dialog=no,all";
+ if (aIsPrivate) {
+ features += ",private";
+ // To prevent regular browsing data from leaking to private browsing
+ // sites, strip the referrer when opening a new private window.
+ aNoReferrer = true;
+ }
+
+ // This propagates to window.arguments.
+ var sa = Cc["@mozilla.org/array;1"].
+ createInstance(Ci.nsIMutableArray);
+
+ var wuri = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ wuri.data = url;
+
+ let charset = null;
+ if (aCharset) {
+ charset = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ charset.data = "charset=" + aCharset;
+ }
+
+ var allowThirdPartyFixupSupports = Cc["@mozilla.org/supports-PRBool;1"].
+ createInstance(Ci.nsISupportsPRBool);
+ allowThirdPartyFixupSupports.data = aAllowThirdPartyFixup;
+
+ var referrerURISupports = null;
+ if (aReferrerURI && !aNoReferrer) {
+ referrerURISupports = Cc["@mozilla.org/supports-string;1"].
+ createInstance(Ci.nsISupportsString);
+ referrerURISupports.data = aReferrerURI.spec;
+ }
+
+ var referrerPolicySupports = Cc["@mozilla.org/supports-PRUint32;1"].
+ createInstance(Ci.nsISupportsPRUint32);
+ referrerPolicySupports.data = aReferrerPolicy;
+
+ var userContextIdSupports = Cc["@mozilla.org/supports-PRUint32;1"].
+ createInstance(Ci.nsISupportsPRUint32);
+ userContextIdSupports.data = aUserContextId;
+
+ sa.appendElement(wuri);
+ sa.appendElement(charset);
+ sa.appendElement(referrerURISupports);
+ sa.appendElement(aPostData);
+ sa.appendElement(allowThirdPartyFixupSupports);
+ sa.appendElement(referrerPolicySupports);
+ sa.appendElement(userContextIdSupports);
+ sa.appendElement(aPrincipal);
+ sa.appendElement(aTriggeringPrincipal);
+
+ const sourceWindow = (w || window);
+ Services.ww.openWindow(sourceWindow, getBrowserURL(), null, features, sa);
+ return;
+ }
+
+ let loadInBackground = aInBackground;
+ if (loadInBackground == null) {
+ loadInBackground =
+ aFromChrome ? false :
+ Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+ }
+
+ if (aAvoidBrowserFocus == null) {
+ aAvoidBrowserFocus =
+ Services.prefs.getBoolPref("browser.tabs.avoidBrowserFocus", false);
+ }
+
+ // reuse the browser if its current tab is empty
+ if (isBrowserEmpty(w.getBrowser()))
+ where = "current";
+
+ switch (where) {
+ case "current":
+ let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+
+ if (aAllowThirdPartyFixup) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
+ }
+ if (aDisallowInheritPrincipal) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
+ }
+
+ if (aForceAboutBlankViewerInCurrent) {
+ w.gBrowser.selectedBrowser.createAboutBlankContentViewer(aPrincipal);
+ }
+
+ w.getBrowser().loadURIWithFlags(url, {
+ triggeringPrincipal: aTriggeringPrincipal,
+ flags,
+ referrerURI: aNoReferrer ? null : aReferrerURI,
+ referrerPolicy: aReferrerPolicy,
+ postData: aPostData,
+ userContextId: aUserContextId
+ });
+ if (!aAvoidBrowserFocus) {
+ w.content.focus();
+ }
+ break;
+
+ case "tabfocused":
+ // forces tab to be focused
+ loadInBackground = true;
+ // fall through
+ case "tabshifted":
+ loadInBackground = !loadInBackground;
+ // fall through
+ case "tab":
+ var browser = w.getBrowser();
+ var tab = browser.addTab(url, {
+ referrerURI: aReferrerURI,
+ referrerPolicy: aReferrerPolicy,
+ charset: aCharset,
+ postData: aPostData,
+ ownerTab: loadInBackground ? null : browser.selectedTab,
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ relatedToCurrent: aRelatedToCurrent,
+ allowMixedContent: aAllowMixedContent,
+ noReferrer: aNoReferrer,
+ userContextId: aUserContextId,
+ originPrincipal: aPrincipal,
+ triggeringPrincipal: aTriggeringPrincipal,
+ });
+ if (!loadInBackground) {
+ browser.selectedTab = tab;
+ }
+ if (!aAvoidBrowserFocus) {
+ w.content.focus();
+ }
+
+ break;
+ }
+
+ return w;
+}
+
+// This opens the URLs contained in the given array in new tabs
+// of the most recent window, creates a new window if necessary.
+function openUILinkArrayIn(urlArray, where, allowThirdPartyFixup)
+{
+ if (!where || !urlArray.length)
+ return null;
+
+ if (where == "save") {
+ for (var i = 0; i < urlArray.length; i++)
+ saveURL(urlArray[i], null, null, true, true, null, document);
+ return null;
+ }
+
+ var w = getTopWin();
+
+ if (!w || where == "window") {
+ return window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no",
+ urlArray.join("\n"), // Pretend that we're a home page group
+ null, null, null, allowThirdPartyFixup);
+ }
+
+ var loadInBackground =
+ Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+
+ var browser = w.getBrowser();
+ switch (where) {
+ case "current":
+ w.loadURI(urlArray[0], null, null, allowThirdPartyFixup);
+ w.content.focus();
+ break;
+ case "tabshifted":
+ loadInBackground = !loadInBackground;
+ // fall through
+ case "tab":
+ var tab = browser.addTab(urlArray[0], {allowThirdPartyFixup: allowThirdPartyFixup});
+ if (!loadInBackground) {
+ browser.selectedTab = tab;
+ w.content.focus();
+ }
+ }
+ var relatedToCurrent = where == "current";
+ for (var i = 1; i < urlArray.length; i++)
+ browser.addTab(urlArray[i], {allowThirdPartyFixup: allowThirdPartyFixup, relatedToCurrent: relatedToCurrent});
+
+ return w;
+}
+
+/**
+ * Switch to a tab that has a given URI, and focusses its browser window.
+ * If a matching tab is in this window, it will be switched to. Otherwise, other
+ * windows will be searched.
+ *
+ * @param aURI
+ * URI to search for
+ * @param aOpenNew
+ * True to open a new tab and switch to it, if no existing tab is found
+ * @param A callback to call when the tab is open, the tab's browser will be
+ * passed as an argument
+ * @return True if a tab was switched to (or opened), false otherwise
+ */
+function switchToTabHavingURI(aURI, aOpenNew, aCallback) {
+ function switchIfURIInWindow(aWindow) {
+ if (!aWindow.gBrowser)
+ return false;
+ let browsers = aWindow.gBrowser.browsers;
+ for (let i = 0; i < browsers.length; i++) {
+ let browser = browsers[i];
+ if (browser.currentURI.equals(aURI)) {
+ // Focus the matching window & tab
+ aWindow.focus();
+ aWindow.gBrowser.tabContainer.selectedIndex = i;
+ if (aCallback)
+ aCallback(browser);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // This can be passed either nsIURI or a string.
+ if (!(aURI instanceof Ci.nsIURI))
+ aURI = Services.io.newURI(aURI);
+
+ // Prioritise this window.
+ if (switchIfURIInWindow(window))
+ return true;
+
+ let winEnum = Services.wm.getEnumerator("navigator:browser");
+ while (winEnum.hasMoreElements()) {
+ let browserWin = winEnum.getNext();
+ // Skip closed (but not yet destroyed) windows,
+ // and the current window (which was checked earlier).
+ if (browserWin.closed || browserWin == window)
+ continue;
+ if (switchIfURIInWindow(browserWin))
+ return true;
+ }
+
+ // No opened tab has that url.
+ if (aOpenNew) {
+ let browserWin = openUILinkIn(aURI.spec, "tabfocused");
+ if (aCallback) {
+ browserWin.addEventListener("pageshow", function browserWinPageShow(event) {
+ if (event.target.location.href != aURI.spec)
+ return;
+ browserWin.removeEventListener("pageshow", browserWinPageShow, true);
+ aCallback(browserWin.getBrowser().selectedBrowser);
+ }, true);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+// Determines if a browser is "empty"
+function isBrowserEmpty(aBrowser) {
+ return aBrowser.sessionHistory.count < 2 &&
+ aBrowser.currentURI.spec == "about:blank" &&
+ !aBrowser.contentDocument.body.hasChildNodes();
+}
+
+function subscribeToFeed(href, event) {
+ // Just load the feed in the content area to either subscribe or show the
+ // preview UI
+ var w = getTopWin();
+ var charset;
+ if (w) {
+ var browser = w.getBrowser();
+ charset = browser.characterSet;
+ } else {
+ // When calling this function without any open navigator window
+ charset = document.characterSet;
+ }
+ let feedURI = makeURI(href, charset);
+
+ openUILink(href, event, false, true);
+}
+
+function subscribeToFeedMiddleClick(href, event) {
+ if (event.button == 1) {
+ this.subscribeToFeed(href, event);
+ // unlike for command events, we have to close the menus manually
+ closeMenus(event.target);
+ }
+}
+
+function OpenSearchEngineManager() {
+ var window = Services.wm.getMostRecentWindow("Browser:SearchManager");
+ if (window)
+ window.focus();
+ else {
+ var arg = { value: false };
+ openDialog("chrome://communicator/content/search/engineManager.xul",
+ "_blank", "chrome,dialog,modal,centerscreen,resizable", arg);
+ if (arg.value)
+ loadAddSearchEngines();
+ }
+}
+
+function loadAddSearchEngines() {
+ var newWindowPref = Services.prefs.getIntPref("browser.link.open_newwindow");
+ var where = newWindowPref == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB ? "tabfocused" : "window";
+ var searchEnginesURL = Services.urlFormatter.formatURLPref("browser.search.searchEnginesURL");
+ openUILinkIn(searchEnginesURL, where);
+}
+
+function FillInHTMLTooltip(tipElement)
+{
+ // Don't show the tooltip if the tooltip node is a document or disconnected.
+ if (!tipElement.ownerDocument ||
+ (tipElement.ownerDocument.compareDocumentPosition(tipElement) & document.DOCUMENT_POSITION_DISCONNECTED))
+ return false;
+
+ var defView = tipElement.ownerDocument.defaultView;
+ // XXX Work around bug 350679:
+ // "Tooltips can be fired in documents with no view".
+ if (!defView)
+ return false;
+
+ const XLinkNS = "http://www.w3.org/1999/xlink";
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ var titleText = null;
+ var XLinkTitleText = null;
+ var SVGTitleText = null;
+ var lookingForSVGTitle = true;
+ var direction = defView.getComputedStyle(tipElement, "")
+ .getPropertyValue("direction");
+
+ // If the element is invalid per HTML5 Forms specifications and has no title,
+ // show the constraint validation error message.
+ if ((tipElement instanceof HTMLInputElement ||
+ tipElement instanceof HTMLTextAreaElement ||
+ tipElement instanceof HTMLSelectElement ||
+ tipElement instanceof HTMLButtonElement) &&
+ !tipElement.hasAttribute("title") &&
+ (!tipElement.form || !tipElement.form.noValidate)) {
+ // If the element is barred from constraint validation or is valid,
+ // the validation message will be the empty string.
+ titleText = tipElement.validationMessage || null;
+ }
+
+ while ((titleText == null) && (XLinkTitleText == null) &&
+ (SVGTitleText == null) && tipElement) {
+ if (tipElement.nodeType == Node.ELEMENT_NODE &&
+ tipElement.namespaceURI != XULNS) {
+ titleText = tipElement.getAttribute("title");
+ if ((tipElement instanceof HTMLAnchorElement ||
+ tipElement instanceof HTMLAreaElement ||
+ tipElement instanceof HTMLLinkElement ||
+ tipElement instanceof SVGAElement) && tipElement.href) {
+ XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
+ }
+ if (lookingForSVGTitle &&
+ (!(tipElement instanceof SVGElement) ||
+ tipElement.parentNode.nodeType == Node.DOCUMENT_NODE)) {
+ lookingForSVGTitle = false;
+ }
+ if (lookingForSVGTitle) {
+ let length = tipElement.childNodes.length;
+ for (let i = 0; i < length; i++) {
+ let childNode = tipElement.childNodes[i];
+ if (childNode instanceof SVGTitleElement) {
+ SVGTitleText = childNode.textContent;
+ break;
+ }
+ }
+ }
+ direction = defView.getComputedStyle(tipElement, "")
+ .getPropertyValue("direction");
+ }
+ tipElement = tipElement.parentNode;
+ }
+
+ var tipNode = document.getElementById("aHTMLTooltip");
+ tipNode.style.direction = direction;
+
+ return [titleText, XLinkTitleText, SVGTitleText].some(function (t) {
+ if (t && /\S/.test(t)) {
+ // Make CRLF and CR render one line break each.
+ tipNode.setAttribute("label", t.replace(/\r\n?/g, "\n"));
+ return true;
+ }
+ return false;
+ });
+}
+
+function GetFileFromString(aString)
+{
+ // If empty string just return null.
+ if (!aString)
+ return null;
+
+ let commandLine = Cc["@mozilla.org/toolkit/command-line;1"]
+ .createInstance(Ci.nsICommandLine);
+ let uri = commandLine.resolveURI(aString);
+ return uri instanceof Ci.nsIFileURL ?
+ uri.file.QueryInterface(Ci.nsIFile) : null;
+}
+
+function CopyImage()
+{
+ var param = Cu.createCommandParams();
+ param.setLongValue("imageCopy",
+ Ci.nsIContentViewerEdit.COPY_IMAGE_ALL);
+ document.commandDispatcher.getControllerForCommand("cmd_copyImage")
+ .QueryInterface(Ci.nsICommandController)
+ .doCommandWithParams("cmd_copyImage", param);
+}
+
+/**
+ * Moved from toolkit/content/globalOverlay.js
+ */
+function goSetMenuValue(aCommand, aLabelAttribute) {
+ var commandNode = top.document.getElementById(aCommand);
+ if (commandNode) {
+ var label = commandNode.getAttribute(aLabelAttribute);
+ if (label)
+ commandNode.setAttribute("label", label);
+ }
+}
+
+function goSetAccessKey(aCommand, aValueAttribute) {
+ var commandNode = top.document.getElementById(aCommand);
+ if (commandNode) {
+ var value = commandNode.getAttribute(aValueAttribute);
+ if (value)
+ commandNode.setAttribute("accesskey", value);
+ }
+}
+
+// this function is used to inform all the controllers attached to a node that an event has occurred
+// (e.g. the tree controllers need to be informed of blur events so that they can change some of the
+// menu items back to their default values)
+function goOnEvent(aNode, aEvent) {
+ var numControllers = aNode.controllers.getControllerCount();
+ var controller;
+
+ for (var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++) {
+ controller = aNode.controllers.getControllerAt(controllerIndex);
+ if (controller)
+ controller.onEvent(aEvent);
+ }
+}
diff --git a/comm/suite/base/content/utilityOverlay.xul b/comm/suite/base/content/utilityOverlay.xul
new file mode 100644
index 0000000000..c5d0ea5751
--- /dev/null
+++ b/comm/suite/base/content/utilityOverlay.xul
@@ -0,0 +1,719 @@
+<?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 % utilityDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+%utilityDTD;
+<!ENTITY % customizeToolbarDTD SYSTEM "chrome://communicator/locale/customizeToolbar.dtd">
+%customizeToolbarDTD;
+
+]>
+
+<overlay id="utilityOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://communicator/content/sync/syncUI.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+
+ <stringbundleset>
+ <stringbundle id="bundle_utilityOverlay"
+ src="chrome://communicator/locale/utilityOverlay.properties"/>
+ </stringbundleset>
+
+<broadcasterset id="mainBroadcasterSet">
+ <broadcaster id="cmd_CustomizeToolbars"/>
+</broadcasterset>
+
+ <!-- online/offline status indicators -->
+ <broadcaster id="Communicator:WorkMode"
+ label="&offlineGoOfflineCmd.label;"
+ type="checkbox"
+ oncommand="toggleOfflineStatus();"/>
+
+ <menupopup id="networkProperties" onpopupshown="InitProxyMenu();">
+ <menuitem id="network-proxy-no"
+ type="radio"
+ name="status"
+ label="&direct.label;"
+ accesskey="&direct.accesskey;"
+ oncommand="setNetworkStatus('0')"/>
+ <menuitem id="network-proxy-wpad"
+ type="radio"
+ name="status"
+ label="&wpad.label;"
+ accesskey="&wpad.accesskey;"
+ oncommand="setNetworkStatus('4')"/>
+ <menuitem id="network-proxy-system"
+ type="radio"
+ name="status"
+ label="&system.label;"
+ accesskey="&system.accesskey;"
+ hidden="true"
+ oncommand="setNetworkStatus('5')"/>
+ <menuitem id="network-proxy-pac"
+ type="radio"
+ name="status"
+ label="&pac.label;"
+ accesskey="&pac.accesskey;"
+ oncommand="setNetworkStatus('2')"/>
+ <menuitem id="network-proxy-manual"
+ type="radio"
+ name="status"
+ label="&manual.label;"
+ accesskey="&manual.accesskey;"
+ oncommand="setNetworkStatus('1')"/>
+ <menuseparator/>
+ <menuitem label="&proxy.label;"
+ accesskey="&proxy.accesskey;"
+ oncommand="goPreferences('proxies_pane');"/>
+ </menupopup>
+
+ <menupopup id="toolbar-context-menu"
+ onpopupshowing="onViewToolbarsPopupShowing(event);"
+ oncommand="onViewToolbarCommand(event);">
+ <menuseparator id="toolbarmode-sep"/>
+ <menu id="toolbarmode-context-menu"
+ label="&customizeToolbar.toolbarmode.label;"
+ accesskey="&customizeToolbar.toolbarmode.accesskey;">
+ <menupopup id="toolbarModePopup"
+ onpopupshowing="onToolbarModePopupShowing(event);"
+ oncommand="goSetToolbarState(event);">
+ <menuitem type="radio" name="mode" value="icons"
+ label="&customizeToolbar.icons.label;"
+ accesskey="&customizeToolbar.icons.accesskey;"/>
+ <menuitem type="radio" name="mode" value="full"
+ label="&customizeToolbar.iconsAndText.label;"
+ accesskey="&customizeToolbar.iconsAndText.accesskey;"/>
+ <menuitem type="radio" name="mode" value="text"
+ label="&customizeToolbar.text.label;"
+ accesskey="&customizeToolbar.text.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="toolbarmode-smallicons"
+ type="checkbox" value="smallicons"
+ label="&customizeToolbar.useSmallIcons.label;"
+ accesskey="&customizeToolbar.useSmallIcons.accesskey;"/>
+ <menuitem id="toolbarmode-labelalign"
+ type="checkbox" value="end"
+ label="&customizeToolbar.labelAlignEnd.label;"
+ accesskey="&customizeToolbar.labelAlignEnd.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="toolbarmode-default"
+ type="checkbox" value="default"
+ label="&customizeToolbar.useDefault.label;"
+ accesskey="&customizeToolbar.useDefault.accesskey;"/>
+ </menupopup>
+ </menu>
+
+ <menuseparator id="toolbar-customize-sep"/>
+ <menuitem id = "customize_toolbars"
+ observes="cmd_CustomizeToolbars"
+ label="&customizeToolbarContext.label;"
+ accesskey="&customizeToolbarContext.accesskey;"
+ oncommand="return SuiteCustomizeToolbar(this);"/>
+
+ </menupopup>
+
+ <panel id="customizeToolbarSheetPopup" noautohide="true">
+ <iframe id="customizeToolbarSheetIFrame"
+ style="&dialog.dimensions;"
+ hidden="true"/>
+ </panel>
+
+ <statusbarpanel id="offline-status" context="networkProperties"
+ observes="Communicator:WorkMode"/>
+
+ <menuitem id="offlineGoOfflineCmd"
+ label="&offlineGoOfflineCmd.label;"
+ accesskey="&offlineGoOfflineCmd.accesskey;"
+ observes="Communicator:WorkMode"/>
+
+ <keyset id="tasksKeys">
+#ifndef XP_MACOSX
+ <key id="key_openHelp"
+ keycode="&openHelpCmd.key;"
+ command="cmd_openHelp"/>
+#else
+ <key id="key_openHelp"
+ key="&openHelpCmdMac.key;"
+ modifiers="&openHelpCmdMac.modifiers;"
+ command="cmd_openHelp"/>
+ <key id="key_preferencesMac"
+ key="&preferencesCmdMac.key;"
+ modifiers="&preferencesCmdMac.modifiers;"/>
+ <key id="key_hideThisApp"
+ key="&hideThisAppCmd.key;"
+ modifiers="&hideThisAppCmd.modifiers;"/>
+ <key id="key_hideOtherApps"
+ key="&hideOtherAppsCmd.key;"
+ modifiers="&hideOtherAppsCmd.modifiers;"/>
+#endif
+ <key id="key_quit"
+ key="&quitApplicationCmd.key;"
+ command="cmd_quit"
+ modifiers="accel"/>
+ </keyset>
+
+ <!-- File Menu -->
+ <menu id="menu_File"
+ label="&fileMenu.label;"
+ accesskey="&fileMenu.accesskey;"/>
+
+ <!-- New SubMenu (Under File Menu) -->
+ <command id="cmd_newNavigator"
+ oncommand="OpenBrowserWindow()"/>
+ <command id="cmd_newPrivateWindow"
+ oncommand="openNewPrivateWith('about:privatebrowsing');"/>
+ <command id="cmd_newEditor"
+ oncommand="NewEditorWindow();"/>
+
+ <!-- XXX not implemented, temporarily disabled
+ <command id="cmd_newEditorTemplate"
+ disabled="true"
+ oncommand="NewEditorFromTemplate();"/>
+ <command id="cmd_newEditorDraft"
+ disabled="true"
+ oncommand="NewEditorFromDraft();"/>
+ -->
+
+ <menuitem id="menu_newEditor"
+ label="&newBlankPageCmd.label;"
+ accesskey="&newBlankPageCmd.accesskey;"
+ key="key_newBlankPage"
+ command="cmd_newEditor"/>
+ <menuitem id="menu_newEditorTemplate"
+ label="&newPageFromTemplateCmd.label;"
+ accesskey="&newPageFromTemplateCmd.accesskey;"
+ command="cmd_newEditorTemplate"/>
+ <menuitem id="menu_newEditorDraft"
+ label="&newPageFromDraftCmd.label;"
+ accesskey="&newPageFromDraftCmd.accesskey;"
+ command="cmd_newEditorDraft"/>
+
+ <menu id="menu_New"
+ label="&newMenu.label;"
+ accesskey="&newMenu.accesskey;"/>
+
+ <menuitem id="menu_newNavigator"
+ label="&newNavigatorCmd.label;"
+ accesskey="&newNavigatorCmd.accesskey;"
+ key="key_newNavigator"
+ command="cmd_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"
+ label="&newPrivateWindowCmd.label;"
+ accesskey="&newPrivateWindowCmd.accesskey;"
+ key="key_newPrivateWindow"
+ command="cmd_newPrivateWindow"/>
+ <menuitem id="menu_close"
+ label="&closeCmd.label;"
+ accesskey="&closeCmd.accesskey;"
+ key="key_close"
+ command="cmd_close"/>
+ <menuitem id="menu_printSetup"
+ label="&printSetupCmd.label;"
+ accesskey="&printSetupCmd.accesskey;"
+ command="cmd_printSetup"/>
+ <menuitem id="menu_printPreview"
+ label="&printPreviewCmd.label;"
+ accesskey="&printPreviewCmd.accesskey;"
+#ifdef XP_MACOSX
+ hidden="true"
+#endif
+ command="cmd_printpreview"/>
+ <menuitem id="menu_print"
+ label="&printCmd.label;"
+ accesskey="&printCmd.accesskey;"
+ key="key_print"
+ command="cmd_print"/>
+
+ <key id="key_newBlankPage"
+ key="&newBlankPageCmd.key;"
+ command="cmd_newEditor"
+ modifiers="accel, shift"/>
+ <key id="key_newNavigator"
+ key="&newNavigatorCmd.key;"
+ command="cmd_newNavigator"
+ modifiers="accel"/>
+ <key id="key_newPrivateWindow"
+ key="&newPrivateWindowCmd.key;"
+ command="cmd_newPrivateWindow"
+ modifiers="accel, shift"/>
+ <key id="key_close"
+ key="&closeCmd.key;"
+ command="cmd_close"
+ modifiers="accel"/>
+ <key id="key_closeWindow"
+ key="&closeCmd.key;"
+ command="cmd_closeWindow"
+ modifiers="accel,shift"/>
+ <key id="key_print"
+ key="&printCmd.key;"
+ command="cmd_print"
+ modifiers="accel"/>
+ <key id="printKb"
+ key="&printCmd.key;"/>
+
+ <menupopup id="menu_FilePopup">
+#ifdef XP_MACOSX
+ <menuitem id="menu_mac_services"
+ label="&servicesMenu.label;"/>
+ <menuitem id="menu_mac_hide_app"
+ label="&hideThisAppCmd.label;"
+ key="key_hideThisApp"/>
+ <menuitem id="menu_mac_hide_others"
+ label="&hideOtherAppsCmd.label;"
+ key="key_hideOtherApps"/>
+ <menuitem id="menu_mac_show_all"
+ label="&showAllAppsCmd.label;"/>
+#else
+ <menuseparator id="menu_FileQuitSeparator"/>
+#endif
+ <menuitem id="menu_FileQuitItem"
+#ifdef XP_WIN
+ label="&quitApplicationCmd.label;"
+ accesskey="&quitApplicationCmd.accesskey;"
+#else
+#ifdef XP_MACOSX
+ label="&quitApplicationCmdMac.label;"
+ accesskey="&quitApplicationCmdMac.accesskey;"
+#else
+ label="&quitApplicationCmdUnix.label;"
+ accesskey="&quitApplicationCmdUnix.accesskey;"
+#endif
+#endif
+ key="key_quit"
+ command="cmd_quit"/>
+ </menupopup>
+
+ <keyset id="findKeys">
+ <key id="key_find"
+ key="&findBarCmd.key;"
+ command="cmd_find"
+ modifiers="accel"/>
+#ifndef XP_MACOSX
+#ifdef XP_UNIX
+ <key keycode="&findCmd.key2;"
+ command="cmd_find"/>
+#endif
+ <key id="key_findReplace"
+ key="&findReplaceCmd.key;"
+ modifiers="accel"
+ command="cmd_findReplace"/>
+#else
+ <key id="key_findReplace"
+ key="&findReplaceCmdMac.key;"
+ modifiers="accel,alt"
+ command="cmd_findReplace"/>
+#endif
+ <key id="key_findNext"
+ key="&findAgainCmd.key;"
+ command="cmd_findNext"
+ modifiers="accel"/>
+ <key id="key_findPrev"
+ key="&findPrevCmd.key;"
+ command="cmd_findPrev"
+ modifiers="accel, shift"/>
+ <key keycode="&findAgainCmd.key2;"
+ command="cmd_findNext"/>
+ <key keycode="&findPrevCmd.key2;"
+ command="cmd_findPrev"
+ modifiers="shift"/>
+#ifndef XP_MACOSX
+ <key id="key_findTypeText"
+ key="&findTypeTextCmd.key;"/>
+ <key id="key_findTypeLinks"
+ key="&findTypeLinksCmd.key;"/>
+#endif
+ </keyset>
+
+ <!-- Edit Menu -->
+ <menu id="menu_Edit"
+ label="&editMenu.label;"
+ accesskey="&editMenu.accesskey;"/>
+ <menuitem id="menu_undo"
+ key="key_undo"
+ accesskey="&undoCmd.accesskey;"
+ command="cmd_undo"/>
+ <menuitem id="menu_redo"
+ key="key_redo"
+ accesskey="&redoCmd.accesskey;"
+ command="cmd_redo"/>
+ <menuitem id="menu_cut"
+ key="key_cut"
+ accesskey="&cutCmd.accesskey;"
+ command="cmd_cut"/>
+ <menuitem id="menu_copy"
+ key="key_copy"
+ accesskey="&copyCmd.accesskey;"
+ command="cmd_copy"/>
+ <menuitem id="menu_paste"
+ key="key_paste"
+ accesskey="&pasteCmd.accesskey;"
+ command="cmd_paste"/>
+ <menuitem id="menu_delete"
+ key="key_delete"
+ accesskey="&deleteCmd.accesskey;"
+ command="cmd_delete"/>
+ <menuitem id="menu_selectAll"
+ label="&selectAllCmd.label;"
+ key="key_selectAll"
+ accesskey="&selectAllCmd.accesskey;"
+ command="cmd_selectAll"/>
+ <menuitem id="menu_find"
+ accesskey="&findBarCmd.accesskey;"
+ key="key_find"
+ command="cmd_find"/>
+ <menuitem id="menu_findReplace"
+ accesskey="&findReplaceCmd.accesskey;"
+ key="key_findReplace"
+ command="cmd_findReplace"/>
+ <menuitem id="menu_findNext"
+ label="&findAgainCmd.label;"
+ accesskey="&findAgainCmd.accesskey;"
+ key="key_findNext"
+ command="cmd_findNext"/>
+ <menuitem id="menu_findPrev"
+ label="&findPrevCmd.label;"
+ accesskey="&findPrevCmd.accesskey;"
+ key="key_findPrev"
+ command="cmd_findPrev"/>
+ <menuitem id="menu_findTypeText"
+ label="&findTypeTextCmd.label;"
+ key="key_findTypeText"
+ accesskey="&findTypeTextCmd.accesskey;"
+ command="cmd_findTypeText"/>
+ <menuitem id="menu_findTypeLinks"
+ label="&findTypeLinksCmd.label;"
+ key="key_findTypeLinks"
+ accesskey="&findTypeLinksCmd.accesskey;"
+ command="cmd_findTypeLinks"/>
+ <menuitem id="textfieldDirection-swap"
+ label="&bidiSwitchTextDirectionItem.label;"
+ key="key_switchTextDirection"
+ accesskey="&bidiSwitchTextDirectionItem.accesskey;"
+ command="cmd_switchTextDirection"/>
+
+ <!-- Context Menu Overlay -->
+ <menuitem id="context-undo"
+ accesskey="&undoCmd.accesskey;"
+ command="cmd_undo"/>
+ <menuitem id="context-redo"
+ accesskey="&redoCmd.accesskey;"
+ command="cmd_redo"/>
+ <menuitem id="context-cut"
+ accesskey="&cutCmd.accesskey;"
+ command="cmd_cut"/>
+ <menuitem id="context-copy"
+ accesskey="&copyCmd.accesskey;"
+ command="cmd_copy"/>
+ <menuitem id="context-paste"
+ accesskey="&pasteCmd.accesskey;"
+ command="cmd_paste"/>
+ <menuitem id="context-delete"
+ accesskey="&deleteCmd.accesskey;"
+ command="cmd_delete"/>
+ <menuitem id="context-selectall"
+ label="&selectAllCmd.label;"
+ accesskey="&selectAllCmd.accesskey;"
+ command="cmd_selectAll"/>
+
+ <!-- These key nodes are here only for show. The real bindings come from
+ XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->
+
+ <key id="key_undo"
+ key="&undoCmd.key;"
+ modifiers="accel"/>
+ <key id="key_cut"
+ key="&cutCmd.key;"
+ modifiers="accel"/>
+ <key id="key_copy"
+ key="&copyCmd.key;"
+ modifiers="accel"/>
+ <key id="key_paste"
+ key="&pasteCmd.key;"
+ modifiers="accel"/>
+ <key id="key_switchTextDirection"
+ command="cmd_switchTextDirection"
+ key="&bidiSwitchTextDirectionItem.commandkey;"
+ modifiers="accel,shift"/>
+
+#ifndef XP_MACOSX
+ <key id="key_redo"
+ key="&redoCmd.key;"
+ modifiers="accel"/>
+ <key id="key_delete"
+ keycode="VK_DELETE"
+ command="cmd_delete"/>
+#ifdef XP_WIN
+ <key id="key_selectAll"
+ key="&selectAllCmd.key;"
+ modifiers="accel"/>
+#else
+ <key id="key_selectAll"
+ key="&selectAllCmd.key;"
+ modifiers="alt"/>
+#endif
+#else
+ <key id="key_redo"
+ key="&redoCmdMac.key;"
+ modifiers="shift, accel"/>
+ <!-- not all Mac keyboards have a VK_DELETE key, so we use VK_BACK as
+ the primary and provide VK_DELETE as a secondary key definition -->
+ <key id="key_delete"
+ keycode="VK_BACK"
+ command="cmd_delete"/>
+ <key id="key_delete2"
+ keycode="VK_DELETE"
+ command="cmd_delete"/>
+ <key id="key_selectAll"
+ key="&selectAllCmd.key;"
+ modifiers="accel"/>
+#endif
+
+ <commandset id="globalEditMenuItems"
+ commandupdater="true"
+ events="focus"
+ oncommandupdate="goUpdateGlobalEditMenuItems()"/>
+ <commandset id="selectEditMenuItems"
+ commandupdater="true"
+ events="select"
+ oncommandupdate="goUpdateSelectEditMenuItems()"/>
+ <commandset id="undoEditMenuItems"
+ commandupdater="true"
+ events="undo"
+ oncommandupdate="goUpdateUndoEditMenuItems()"/>
+ <commandset id="clipboardEditMenuItems"
+ commandupdater="true"
+ events="clipboard"
+ oncommandupdate="goUpdatePasteMenuItems()"/>
+ <commandset id="findTypeMenuItems"
+ commandupdater="true"
+ events="focus"
+ oncommandupdate="goUpdateFindTypeMenuItems()"/>
+
+ <commandset id="tasksCommands">
+ <command id="cmd_quit" oncommand="goQuitApplication();"/>
+ <command id="cmd_openHelp"
+ oncommand="openHelp('welcome', 'chrome://communicator/locale/help/suitehelp.rdf');"/>
+ </commandset>
+
+ <command id="cmd_copyLink"
+ oncommand="goDoCommand('cmd_copyLink')"
+ disabled="false"/>
+ <command id="cmd_copyImage"
+ oncommand="CopyImage();"
+ disabled="false"/>
+ <command id="cmd_undo"
+ label="&undoCmd.label;"
+ oncommand="goDoCommand('cmd_undo')"
+ disabled="true"/>
+ <command id="cmd_redo"
+ label="&redoCmd.label;"
+ oncommand="goDoCommand('cmd_redo')"
+ disabled="true"/>
+ <command id="cmd_cut"
+ label="&cutCmd.label;"
+ oncommand="goDoCommand('cmd_cut')"
+ disabled="true"/>
+ <command id="cmd_copy"
+ label="&copyCmd.label;"
+ oncommand="goDoCommand('cmd_copy')"
+ disabled="true"/>
+ <command id="cmd_paste"
+ label="&pasteCmd.label;"
+ oncommand="goDoCommand('cmd_paste')"
+ disabled="true"/>
+ <command id="cmd_delete"
+ label="&deleteCmd.label;"
+ oncommand="goDoCommand('cmd_delete')"
+ valueDefault="&deleteCmd.label;"
+ valueDefaultAccessKey="&deleteCmd.accesskey;"
+ disabled="true"/>
+ <command id="cmd_selectAll"
+ oncommand="goDoCommand('cmd_selectAll')"
+ disabled="true"/>
+ <command id="cmd_switchTextDirection"
+ oncommand="goDoCommand('cmd_switchTextDirection');"/>
+ <command id="cmd_findTypeText"
+ oncommand="findTextAsYouType();"/>
+ <command id="cmd_findTypeLinks"
+ oncommand="findLinksAsYouType();"/>
+
+ <!-- Not needed yet, window will need this: -->
+ <!-- broadcaster id="cmd_preferences"/ -->
+
+#ifndef XP_MACOSX
+ <menuitem id="menu_preferences"
+ label="&preferencesCmd.label;"
+ accesskey="&preferencesCmd.accesskey;"/>
+#else
+ <menuitem id="menu_preferences"
+ label="&preferencesCmdMac.label;"
+ key="key_preferencesMac"/>
+#endif
+
+ <!-- View Menu -->
+ <menu id="menu_View"
+ label="&viewMenu.label;"
+ accesskey="&viewMenu.accesskey;"/>
+ <menu id="menu_Toolbars"
+ label="&viewToolbarsMenu.label;"
+ accesskey="&viewToolbarsMenu.accesskey;"/>
+
+ <menuitem id="menu_showTaskbar"
+ label="&showTaskbarCmd.label;"
+ accesskey="&showTaskbarCmd.accesskey;"
+ oncommand="goToggleToolbar('status-bar', 'menu_showTaskbar')"
+ type="checkbox"
+ checked="true"/>
+
+ <!-- Help Menu -->
+#ifndef XP_WIN
+ <menu id="menu_Help"
+ label="&helpMenu.label;"
+ accesskey="&helpMenu.accesskey;">
+#else
+ <menu id="menu_Help"
+ label="&helpMenuWin.label;"
+ accesskey="&helpMenuWin.accesskey;">
+#endif
+ <menupopup id="helpPopup" onpopupshowing="updateCheckUpdatesItem();">
+#ifndef XP_MACOSX
+ <menuitem label="&openHelpCmd.label;"
+ accesskey="&openHelpCmd.accesskey;"
+ id="help"
+ key="key_openHelp"
+ command="cmd_openHelp"/>
+#else
+ <menuitem label="&openHelpCmdMac.label;"
+ accesskey="&openHelpCmdMac.accesskey;"
+ id="help"
+ key="key_openHelp"
+ command="cmd_openHelp"/>
+#endif
+ <menuitem id="troubleShooting"
+ accesskey="&helpTroubleshootingInfo.accesskey;"
+ label="&helpTroubleshootingInfo.label;"
+ oncommand="goTroubleshootingPage();"/>
+ <menuitem id="releaseUrl"
+ accesskey="&releaseCmd.accesskey;"
+ label="&releaseCmd.label;"
+ oncommand="goReleaseNotes();"/>
+ <menuitem id="helpSafeMode"
+ accesskey="&helpSafeMode.accesskey;"
+ label="&helpSafeMode.label;"
+ oncommand="safeModeRestart();"/>
+
+ <menuseparator id="updateSeparator"/>
+
+ <menuitem accesskey="&updateCmd.accesskey;" label="&updateCmd.label;"
+ id="checkForUpdates" oncommand="checkForUpdates();"/>
+
+ <menuseparator id="menu_HelpAboutSeparator"/>
+
+ <menuitem class="about"
+ accesskey="&aboutBuildConfigCmd.accesskey;"
+ label="&aboutBuildConfigCmd.label;"
+ id="BuildConfigInfo"
+ oncommand="goAbout('buildconfig');"/>
+ <menuitem accesskey="&aboutCmd.accesskey;"
+ label="&aboutCmd.label;"
+ id="aboutName"
+ oncommand="goAbout();"/>
+ </menupopup>
+ </menu>
+
+ <menupopup id="popupNotificationMenu"
+ oncommand="popupBlockerMenuCommand(event.target);"
+ onpopupshowing="return popupNotificationMenuShowing(event);"
+ onpopuphiding="RemovePopupsItems(this);">
+ <menuitem id="allowPopupsForSite" hidden="true" accesskey="&allowPopups.accesskey;"
+ oncommand="this.notificationbox.allowPopupsForSite(event);"/>
+ <menuitem id="showPopupManager" label="&showPopupManager.label;"
+ accesskey="&showPopupManager.accesskey;"
+ oncommand="toDataManager(hostUrl() + '|permissions|add|popup')"/>
+ <menuitem id="dontShowMessage" label="&dontShowMessage.label;"
+ accesskey="&dontShowMessage.accesskey;"
+ oncommand="disablePopupBlockerNotifications();"/>
+ <menuseparator id="popupNotificationMenuSeparator" hidden="true"/>
+ <!-- Additional items are generated, see popupNotificationMenuShowing() -->
+ </menupopup>
+
+#ifndef XP_MACOSX
+ <toolbar id="toolbar-menubar"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="addrbook-toolbar-menubar2"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="compose-toolbar-menubar2"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="mail-toolbar-menubar2"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+
+ <toolbar id="placesToolbar"
+ toolbarname="&menubarCmd.label;"
+ accesskey="&menubarCmd.accesskey;"/>
+#else
+ <!-- Title bar elements -->
+ <vbox id="titlebar">
+ <hbox id="titlebar-content">
+ <spacer id="titlebar-spacer" flex="1"/>
+ <hbox id="titlebar-buttonbox-container">
+ <hbox id="titlebar-buttonbox"/>
+ </hbox>
+ </hbox>
+ </vbox>
+#endif
+
+ <toolbarbutton id="print-button"
+ type="menu-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ command="cmd_print">
+ <menupopup id="printMenu">
+ <menuitem id="printMenuItemToolbar"
+ label="&printCmd.label;"
+ accesskey="&printCmd.accesskey;"
+ default="true"
+ command="cmd_print"/>
+#ifndef XP_MACOSX
+ <menuitem id="printPreviewMenuItemToolbar"
+ label="&printPreviewCmd.label;"
+ accesskey="&printPreviewCmd.accesskey;"
+ command="cmd_printpreview"/>
+#endif
+ <menuitem id="printSetupToolbar"
+ label="&printSetupCmd.label;"
+ accesskey="&printSetupCmd.accesskey;"
+ command="cmd_printSetup"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbaritem id="throbber-box"
+ class="toolbaritem-noline"
+ title="&throbber.title;"
+ removable="true"
+ align="center">
+ <button id="navigator-throbber"
+ onclick="checkForMiddleClick(this, event);"
+ oncommand="goClickThrobber('app.vendorURL', event);"
+ tooltiptext="&throbber.tooltip2;"/>
+ </toolbaritem>
+
+ <!-- Sync toolbar button
+ <toolbarbutton id="sync-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&syncToolbarButton.label;"
+ oncommand="gSyncUI.handleToolbarButton();"/> -->
+</overlay>
diff --git a/comm/suite/base/content/viewApplyThemeOverlay.js b/comm/suite/base/content/viewApplyThemeOverlay.js
new file mode 100644
index 0000000000..557dc50a3b
--- /dev/null
+++ b/comm/suite/base/content/viewApplyThemeOverlay.js
@@ -0,0 +1,170 @@
+/* -*- 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/. */
+
+const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
+ "resource://gre/modules/LightweightThemeManager.jsm");
+
+var gThemes = [];
+var gApplyThemeBundle;
+var gBackgroundIsActive;
+
+function reloadThemes()
+{
+ AddonManager.getAddonsByTypes(["theme"], function(themes) {
+ gThemes = themes.sort(function(a, b) {
+ return a.name.localeCompare(b.name);
+ });
+ });
+}
+
+var gAddonListener = {
+ onEnabling: function(val) {},
+ onEnabled: function(val) {},
+ onDisabling: function(val) {},
+ onDisabled: function(val) {},
+ onInstalling: function(val) {},
+ onInstalled: reloadThemes,
+ onUninstalling: function(val) {},
+ onUninstalled: reloadThemes,
+ onOperationCancelled: reloadThemes
+};
+
+function getNewThemes()
+{
+ // get URL for more themes from prefs
+ try {
+ openURL(Services.urlFormatter.formatURLPref("extensions.getMoreThemesURL"));
+ }
+ catch (e) {
+ dump(e);
+ }
+}
+
+function getPersonas()
+{
+ // get URL for more themes from prefs
+ try {
+ openURL(Services.urlFormatter.formatURLPref("extensions.getPersonasURL"));
+ }
+ catch (e) {
+ dump(e);
+ }
+}
+
+function checkTheme(popup)
+{
+ const ID_SUFFIX = "@personas.mozilla.org";
+ gBackgroundIsActive = false;
+
+ var usedThemes = LightweightThemeManager.usedThemes;
+ usedThemes = new Map(usedThemes.map( x => [x.id + ID_SUFFIX, x] ));
+
+ while (popup.lastChild.localName != 'menuseparator')
+ popup.lastChild.remove();
+ gThemes.forEach(function(theme) {
+ var menuitem = document.createElement('menuitem');
+ menuitem.setAttribute("label", theme.name);
+ menuitem.setAttribute("type", "radio");
+ menuitem.setAttribute("name", "themeGroup");
+ if (theme.description)
+ menuitem.setAttribute("tooltiptext", theme.description);
+ if (!theme.userDisabled)
+ menuitem.setAttribute("checked", "true");
+ else if (!(theme.permissions & AddonManager.PERM_CAN_ENABLE))
+ menuitem.setAttribute("disabled", "true");
+ menuitem.theme = theme;
+
+ var persona = usedThemes.get(theme.id);
+ if (persona) {
+ menuitem.persona = persona;
+ if (theme.isActive)
+ gBackgroundIsActive = true;
+ }
+
+ popup.appendChild(menuitem);
+ });
+}
+
+function previewTheme(event)
+{
+ if (!gBackgroundIsActive || !event.target.persona)
+ return;
+
+ switch (event.type) {
+ case "DOMMenuItemActive":
+ LightweightThemeManager.previewTheme(event.target.persona);
+ break;
+ case "DOMMenuItemInactive":
+ LightweightThemeManager.resetPreview();
+ break;
+ }
+}
+
+function restartApp()
+{
+ // Notify all windows that an application quit has been requested.
+ var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+
+ // Something aborted the quit process.
+ if (cancelQuit.data)
+ return;
+
+ Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
+ const appStartup = Services.startup;
+ appStartup.quit(appStartup.eRestart | appStartup.eAttemptQuit);
+}
+
+function applyTheme(menuitem)
+{
+ if (!menuitem.theme)
+ return;
+
+ menuitem.theme.userDisabled = false;
+ if (!menuitem.theme.isActive) {
+ var promptTitle = gApplyThemeBundle.getString("switchskinstitle");
+ // gBrandBundle: bundle_brand stringbundle from overlayed XUL file
+ var brandName = gBrandBundle.getString("brandShortName");
+ var promptMsg = gApplyThemeBundle.getFormattedString("switchskins", [brandName]);
+ var promptNow = gApplyThemeBundle.getString("switchskinsnow");
+ var promptLater = gApplyThemeBundle.getString("switchskinslater");
+ var check = {value: false};
+ var flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
+ Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING;
+ var pressedVal = Services.prompt.confirmEx(window, promptTitle, promptMsg,
+ flags, promptNow, promptLater,
+ null, null, check);
+ if (pressedVal == 0)
+ restartApp();
+ }
+}
+
+function applyThemeOnLoad()
+{
+ // init globals
+ gApplyThemeBundle = document.getElementById("bundle_viewApplyTheme");
+ AddonManager.addAddonListener(gAddonListener);
+ reloadThemes();
+
+ removeEventListener("load", applyThemeOnLoad, false);
+ addEventListener("unload", applyThemeOnUnload, false);
+ var popup = document.getElementById("menu_viewApplyTheme_Popup");
+ popup.addEventListener("DOMMenuItemActive", previewTheme);
+ popup.addEventListener("DOMMenuItemInactive", previewTheme);
+}
+
+function applyThemeOnUnload()
+{
+ AddonManager.removeAddonListener(gAddonListener);
+ var popup = document.getElementById("menu_viewApplyTheme_Popup");
+ popup.removeEventListener("DOMMenuItemActive", previewTheme);
+ popup.removeEventListener("DOMMenuItemInactive", previewTheme);
+}
+
+addEventListener("load", applyThemeOnLoad, false);
diff --git a/comm/suite/base/content/viewApplyThemeOverlay.xul b/comm/suite/base/content/viewApplyThemeOverlay.xul
new file mode 100644
index 0000000000..0e0097776a
--- /dev/null
+++ b/comm/suite/base/content/viewApplyThemeOverlay.xul
@@ -0,0 +1,34 @@
+<?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 SYSTEM "chrome://communicator/locale/viewApplyThemeOverlay.dtd">
+
+<overlay id="viewApplyThemeOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://communicator/content/viewApplyThemeOverlay.js"/>
+
+ <stringbundle id="bundle_viewApplyTheme"
+ src="chrome://communicator/locale/viewApplyThemeOverlay.properties"/>
+
+ <menu id="menu_viewApplyTheme"
+ label="&applyTheme.label;"
+ accesskey="&applyTheme.accesskey;">
+ <menupopup id="menu_viewApplyTheme_Popup"
+ onpopupshowing="checkTheme(this);"
+ oncommand="applyTheme(event.target);">
+ <menuitem label="&getMoreThemesCmd.label;"
+ accesskey="&getMoreThemesCmd.accesskey;"
+ oncommand="getNewThemes();"/>
+ <menuitem label="&getBackgroundsCmd.label;"
+ accesskey="&getBackgroundsCmd.accesskey;"
+ oncommand="getPersonas();"/>
+ <menuseparator/>
+ </menupopup>
+ </menu>
+
+</overlay>
diff --git a/comm/suite/base/content/viewSourceOverlay.js b/comm/suite/base/content/viewSourceOverlay.js
new file mode 100644
index 0000000000..8bcedb2087
--- /dev/null
+++ b/comm/suite/base/content/viewSourceOverlay.js
@@ -0,0 +1,32 @@
+/* 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/. */
+
+// onload make sure we adapt what's needed for partial source
+window.addEventListener("load", onLoadViewSourceOverlay);
+
+function onLoadViewSourceOverlay() {
+ if (/viewPartialSource\.xul$/.test(document.location)) {
+ // disable menu items that don't work since the selection is munged and
+ // the editor doesn't work for MathML
+ document.getElementById('cmd_savePage').setAttribute('disabled', 'true');
+ document.getElementById('cmd_editPage').setAttribute('disabled', 'true');
+ }
+}
+
+// editPage() comes in from editorApplicationOverlay.js
+function ViewSourceEditPage() {
+ editPage(window.content.location.href);
+}
+
+// needed by findUtils.js
+var gFindInstData;
+function getFindInstData()
+{
+ if (!gFindInstData) {
+ gFindInstData = new nsFindInstData();
+ gFindInstData.browser = getBrowser();
+ // defaults for rootSearchWindow and currentSearchWindow are fine here
+ }
+ return gFindInstData;
+}
diff --git a/comm/suite/base/content/viewSourceOverlay.xul b/comm/suite/base/content/viewSourceOverlay.xul
new file mode 100644
index 0000000000..f5ede156f0
--- /dev/null
+++ b/comm/suite/base/content/viewSourceOverlay.xul
@@ -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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/viewSourceOverlay.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+
+<!DOCTYPE overlay [
+
+<!ENTITY % navDTD SYSTEM "chrome://navigator/locale/navigator.dtd">
+%navDTD;
+
+]>
+
+<overlay id="viewSourceOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://communicator/content/viewSourceOverlay.js"/>
+ <script src="chrome://communicator/content/findUtils.js"/>
+
+ <window id="viewSource">
+ <commandset id="tasksCommands"/>
+ <command id="cmd_newNavigator"/>
+ <command id="cmd_newPrivateWindow"/>
+ <command id="cmd_newEditor"/>
+ <command id="cmd_editPage" oncommand="ViewSourceEditPage();"/>
+ <command id="cmd_find"
+ oncommand="findInPage(getFindInstData());"/>
+ <command id="cmd_findAgain"
+ oncommand="findAgainInPage(getFindInstData(), false);"/>
+ <command id="cmd_findPrevious"
+ oncommand="findAgainInPage(getFindInstData(), true);"/>
+ <stringbundle id="findBundle"
+ src="chrome://global/locale/finddialog.properties"/>
+ </window>
+
+ <keyset id="viewSourceKeys">
+ <keyset id="tasksKeys"/>
+ <key id="key_newBlankPage"/>
+ <key id="key_newNavigator"/>
+ <key id="key_newPrivateWindow"/>
+ <key id="key_editPage" key="&editPageCmd.commandkey;"
+ command="Browser:EditPage" modifiers="accel"/>
+ </keyset>
+
+ <menubar id="viewSource-main-menubar"
+ class="chromeclass-menubar"
+ grippytooltiptext="&menuBar.tooltip;">
+ <menu id="menu_file">
+ <menupopup id="menu_FilePopup">
+ <menu id="menu_New" position="1">
+ <menupopup id="menu_NewPopup">
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ <menuitem id="menu_newEditor"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_editPage" insertafter="menu_savePage"
+ key="key_editPage" command="cmd_editPage"
+ label="&editPageCmd.label;"
+ accesskey="&editPageCmd.accesskey;"/>
+ <menuseparator insertbefore="menu_pageSetup"/>
+ </menupopup>
+ </menu>
+
+ <!-- tasks menu filled from tasksOverlay -->
+ <menu id="tasksMenu"/>
+
+ <!-- window menu filled from tasksOverlay -->
+ <menu id="windowMenu"/>
+
+ <!-- help menu filled from globalOverlay -->
+ <menu id="menu_Help"/>
+ </menubar>
+
+</overlay>
diff --git a/comm/suite/base/content/viewZoomOverlay.js b/comm/suite/base/content/viewZoomOverlay.js
new file mode 100644
index 0000000000..d14ef9d67d
--- /dev/null
+++ b/comm/suite/base/content/viewZoomOverlay.js
@@ -0,0 +1,479 @@
+/* -*- 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/. */
+
+// One of the possible values for the mousewheel.* preferences.
+// From nsEventStateManager.cpp.
+const MOUSE_SCROLL_ZOOM = 3;
+
+/**
+ * Controls the "full zoom" setting and its site-specific preferences.
+ */
+var FullZoom = FullZoom || {
+ // Identifies the setting in the content prefs database.
+ name: "browser.content.full-zoom",
+
+ // The global value (if any) for the setting. Asynchronously loaded from the
+ // service when first requested, then updated by the pref change listener as
+ // it changes. If there is no global value, then this should be undefined.
+ globalValue: undefined,
+
+ // browser.zoom.siteSpecific preference cache
+ _siteSpecificPref: undefined,
+
+ // browser.zoom.updateBackgroundTabs preference cache
+ updateBackgroundTabs: undefined,
+
+ get siteSpecific() {
+ return this._siteSpecificPref;
+ },
+
+ //**************************************************************************//
+ // nsISupports
+
+ QueryInterface:
+ XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
+ Ci.nsIObserver,
+ Ci.nsIContentPrefObserver,
+ Ci.nsIContentPrefCallback2,
+ Ci.nsISupportsWeakReference]),
+
+ //**************************************************************************//
+ // Initialization & Destruction
+
+ init: function FullZoom_init() {
+ // Listen for scrollwheel events so we can save scrollwheel-based changes.
+ window.addEventListener("wheel", this, true);
+
+ // Fetch the initial global value.
+ Services.contentPrefs2.getGlobal(this.name, null, this);
+
+ // Register ourselves with the service so we know when our pref changes.
+ Services.contentPrefs2.addObserverForName(this.name, this);
+
+ this._siteSpecificPref =
+ Services.prefs.getBoolPref("browser.zoom.siteSpecific");
+ this.updateBackgroundTabs =
+ Services.prefs.getBoolPref("browser.zoom.updateBackgroundTabs");
+ // Listen for changes to the browser.zoom branch so we can enable/disable
+ // updating background tabs and per-site saving and restoring of zoom levels.
+ Services.prefs.addObserver("browser.zoom.", this, true);
+ },
+
+ destroy: function FullZoom_destroy() {
+ Services.prefs.removeObserver("browser.zoom.", this);
+ Services.contentPrefs2.removeObserverForName(this.name, this);
+ window.removeEventListener("wheel", this, true);
+ },
+
+
+ //**************************************************************************//
+ // Event Handlers
+
+ // nsIDOMEventListener
+
+ handleEvent: function FullZoom_handleEvent(event) {
+ switch (event.type) {
+ case "wheel":
+ this._handleMouseScrolled(event);
+ break;
+ }
+ },
+
+ _handleMouseScrolled: function FullZoom_handleMouseScrolled(event) {
+ // Construct the "mousewheel action" pref key corresponding to this event.
+ // Based on nsEventStateManager::WheelPrefs::GetIndexFor.
+ var modifiers = {
+ Alt: "mousewheel.with_alt.action",
+ Control: "mousewheel.with_control.action",
+ Meta: "mousewheel.with_meta.action",
+ Shift: "mousewheel.with_shift.action",
+ OS: "mousewheel.with_win.action"
+ };
+ var pref = [];
+ for (var key in modifiers)
+ if (event.getModifierState(key))
+ pref.push(modifiers[key]);
+ if (pref.length == 1)
+ pref = pref[0];
+ else // Multiple or no modifiers, use default action
+ pref = "mousewheel.default.action";
+
+ // Don't do anything if this isn't a "zoom" scroll event.
+ if (Services.prefs.getIntPref(pref, 0) != MOUSE_SCROLL_ZOOM)
+ return;
+
+ // XXX Lazily cache all the possible action prefs so we don't have to get
+ // them anew from the pref service for every scroll event? We'd have to
+ // make sure to observe them so we can update the cache when they change.
+
+ // We have to call _applySettingToPref in a timeout because we handle
+ // the event before the event state manager has a chance to apply the zoom
+ // during nsEventStateManager::PostHandleEvent.
+ window.setTimeout(function (self) { self._applySettingToPref() }, 0, this);
+ },
+
+ // nsIObserver
+
+ observe: function (aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "nsPref:changed":
+ switch (aData) {
+ case "browser.zoom.siteSpecific":
+ this._siteSpecificPref =
+ Services.prefs.getBoolPref("browser.zoom.siteSpecific");
+ break;
+ case "browser.zoom.updateBackgroundTabs":
+ this.updateBackgroundTabs =
+ Services.prefs.getBoolPref("browser.zoom.updateBackgroundTabs");
+ break;
+ }
+ break;
+ }
+ },
+
+ // nsIContentPrefObserver
+
+ onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) {
+ if (aGroup == Services.contentPrefs2.extractDomain(getBrowser().currentURI.spec))
+ this._applyPrefToSetting(aValue);
+ else if (aGroup == null) {
+ this.globalValue = this._ensureValid(aValue);
+
+ // If the current page doesn't have a site-specific preference,
+ // then its zoom should be set to the new global preference now that
+ // the global preference has changed.
+ var zoomValue = Services.contentPrefs2.getCachedByDomainAndName(getBrowser().currentURI.spec, this.name, getBrowser().docShell);
+ if (zoomValue && !zoomValue.value)
+ this._applyPrefToSetting();
+ }
+ },
+
+ onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) {
+ if (aGroup == Services.contentPrefs2.extractDomain(getBrowser().currentURI.spec))
+ this._applyPrefToSetting();
+ else if (aGroup == null) {
+ this.globalValue = undefined;
+
+ // If the current page doesn't have a site-specific preference,
+ // then its zoom should be set to the default preference now that
+ // the global preference has changed.
+ var zoomValue = Services.contentPrefs2.getCachedByDomainAndName(getBrowser().currentURI.spec, this.name, getBrowser().docShell);
+ if (zoomValue && !zoomValue.value)
+ this._applyPrefToSetting();
+ }
+ },
+
+ // nsIContentPrefCallback2
+
+ handleCompletion: function(aReason) {},
+ handleError: function(aResult) {},
+ handleResult: function(aPref) {
+ this.onContentPrefSet(null, this.name, aPref.value);
+ },
+
+ // location change observer
+
+ /**
+ * Called when the location of a tab changes.
+ * When that happens, we need to update the current zoom level if appropriate.
+ *
+ * @param aURI
+ * A URI object representing the new location.
+ * @param aIsTabSwitch
+ * Whether this location change has happened because of a tab switch.
+ * @param aBrowser
+ * (optional) browser object displaying the document
+ */
+ onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
+ if (!aURI || !this.siteSpecific)
+ return;
+
+ // Avoid the cps roundtrip and apply the default/global pref.
+ if (aURI.spec == "about:blank") {
+ this._applyPrefToSetting(undefined, aBrowser);
+ return;
+ }
+
+ // Image documents should always start at 1, and are not affected by prefs.
+ if (!aIsTabSwitch && aBrowser.contentDocument.mozSyntheticDocument) {
+ ZoomManager.setZoomForBrowser(aBrowser, this._ensureValid(1));
+ return;
+ }
+
+ var loadContext = aBrowser.docShell;
+ var zoomValue = Services.contentPrefs2.getCachedByDomainAndName(aURI.spec, this.name, loadContext);
+ if (zoomValue) {
+ this._applyPrefToSetting(zoomValue.value, aBrowser);
+ } else {
+ Services.contentPrefs2.getByDomainAndName(aURI.spec, this.name, loadContext, {
+ self: this,
+ value: undefined,
+ handleCompletion: function(aReason) {
+ // Check that we're still where we expect to be in case this took a
+ // while. Null check currentURI, since the window may have been
+ // destroyed before we were called.
+ if (aBrowser.currentURI && aURI.equals(aBrowser.currentURI))
+ this.self._applyPrefToSetting(this.value, aBrowser);
+ },
+ handleError: function(aResult) {},
+ handleResult: function(aPref) {
+ this.value = aPref.value;
+ }
+ });
+ }
+ },
+
+ //**************************************************************************//
+ // Setting & Pref Manipulation
+
+ reduce: function FullZoom_reduce() {
+ ZoomManager.reduce();
+ this._applySettingToPref();
+ },
+
+ enlarge: function FullZoom_enlarge() {
+ ZoomManager.enlarge();
+ this._applySettingToPref();
+ },
+
+ zoom: function FullZoom_zoom(aZoomValue) {
+ ZoomManager.zoom = aZoomValue;
+ this._applySettingToPref();
+ },
+
+ reset: function FullZoom_reset() {
+ if (typeof this.globalValue != "undefined")
+ ZoomManager.zoom = this.globalValue;
+ else
+ ZoomManager.zoom = this._ensureValid(1);
+
+ this._removePref();
+ },
+
+ setOther: function setZoomOther() {
+ if (openZoomDialog())
+ this._applySettingToPref();
+ },
+
+ /**
+ * Set the zoom level for the current tab.
+ *
+ * Per nsPresContext::setFullZoom, we can set the zoom to its current value
+ * without significant impact on performance, as the setting is only applied
+ * if it differs from the current setting. In fact getting the zoom and then
+ * checking ourselves if it differs costs more.
+ *
+ * And perhaps we should always set the zoom even if it was more expensive,
+ * since DocumentViewerImpl::SetTextZoom claims that child documents can have
+ * a different text zoom (although it would be unusual), and it implies that
+ * those child text zooms should get updated when the parent zoom gets set,
+ * and perhaps the same is true for full zoom
+ * (although DocumentViewerImpl::SetFullZoom doesn't mention it).
+ *
+ * So when we apply new zoom values to the browser, we simply set the zoom.
+ * We don't check first to see if the new value is the same as the current
+ * one.
+ **/
+ _applyPrefToSetting: function FullZoom_applyPrefToSetting(aValue, aBrowser) {
+ var browser = aBrowser || getBrowser();
+
+ if (!this.siteSpecific || window.gInPrintPreviewMode ||
+ browser.contentDocument.mozSyntheticDocument)
+ return;
+
+ try {
+ if (typeof aValue != "undefined")
+ ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
+ else if (typeof this.globalValue != "undefined")
+ ZoomManager.setZoomForBrowser(browser, this.globalValue);
+ else
+ ZoomManager.setZoomForBrowser(browser, this._ensureValid(1));
+ }
+ catch(ex) {}
+ },
+
+ _applySettingToPref: function FullZoom_applySettingToPref() {
+ if (!this.siteSpecific || window.gInPrintPreviewMode ||
+ content.document.mozSyntheticDocument)
+ return;
+
+ var zoomLevel = ZoomManager.zoom;
+ Services.contentPrefs2.set(getBrowser().currentURI.spec, this.name, zoomLevel, getBrowser().docShell);
+ },
+
+ _removePref: function FullZoom_removePref() {
+ if (!content.document.mozSyntheticDocument)
+ Services.contentPrefs2.removeByDomainAndName(getBrowser().currentURI.spec, this.name, getBrowser().docShell);
+ },
+
+
+ //**************************************************************************//
+ // Utilities
+
+ _ensureValid: function FullZoom_ensureValid(aValue) {
+ if (isNaN(aValue))
+ aValue = 1;
+
+ if (aValue < ZoomManager.MIN)
+ return ZoomManager.MIN;
+
+ if (aValue > ZoomManager.MAX)
+ return ZoomManager.MAX;
+
+ return aValue;
+ }
+};
+
+/***** init and helper functions for viewZoomOverlay.xul *****/
+window.addEventListener("load", registerZoomManager);
+window.addEventListener("unload", unregisterZoomManager);
+
+function registerZoomManager() {
+ FullZoom.init();
+
+ var zoomBundle = document.getElementById("bundle_viewZoom");
+ var zoomMenu = document.getElementById("menu_zoom");
+ var parentMenu = zoomMenu.parentNode;
+ parentMenu.addEventListener("popupshowing", updateViewMenu);
+
+ // initialize menu from toolkit.zoomManager.zoomValues and assign accesskeys
+ var zoomFactors = ZoomManager.zoomValues;
+ var freeKeys = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
+
+ var insertBefore = document.getElementById("menu_zoomInsertBefore");
+ var popup = insertBefore.parentNode;
+ for (var i = 0; i < zoomFactors.length; ++i) {
+ var thisFactor = Math.round(zoomFactors[i] * 100);
+ var menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("type", "radio");
+ menuItem.setAttribute("name", "zoom");
+
+ var label;
+ var accessKey = "";
+ if (thisFactor == 100) {
+ label = zoomBundle.getString("zoom.100.label");
+ accessKey = zoomBundle.getString("zoom.100.accesskey");
+ menuItem.setAttribute("key", "key_zoomReset");
+ }
+ else if (thisFactor == 200) {
+ label = zoomBundle.getString("zoom.200.label");
+ accessKey = zoomBundle.getString("zoom.200.accesskey");
+ }
+ else if (thisFactor == Math.round(ZoomManager.MIN * 100)) {
+ label = zoomBundle.getString("zoom.min.label")
+ .replace(/%zoom%/, thisFactor);
+ accessKey = zoomBundle.getString("zoom.min.accesskey");
+ }
+ else if (thisFactor == Math.round(ZoomManager.MAX * 100)) {
+ label = zoomBundle.getString("zoom.max.label")
+ .replace(/%zoom%/, thisFactor);
+ accessKey = zoomBundle.getString("zoom.max.accesskey");
+ }
+ else {
+ label = zoomBundle.getString("zoom.value.label")
+ .replace(/%zoom%/, thisFactor);
+ for (var j = 0; j < label.length; ++j) {
+ var testKey = label[j];
+ var indexKey = freeKeys.indexOf(testKey);
+ if (indexKey >= 0) {
+ accessKey = testKey;
+ freeKeys.splice(indexKey, 1);
+ break;
+ }
+ }
+ }
+
+ menuItem.setAttribute("label", label);
+ if (accessKey)
+ menuItem.setAttribute("accesskey", accessKey);
+ menuItem.setAttribute("value", thisFactor);
+ popup.insertBefore(menuItem, insertBefore);
+ }
+}
+
+function unregisterZoomManager() {
+ FullZoom.destroy();
+}
+
+function updateViewMenu() {
+ var zoomBundle = document.getElementById("bundle_viewZoom");
+ var zoomMenu = document.getElementById("menu_zoom");
+ var zoomType = ZoomManager.useFullZoom ? "fullZoom" : "textZoom";
+ var menuLabel = zoomBundle.getString(zoomType + ".label")
+ .replace(/%zoom%/, Math.round(ZoomManager.zoom * 100));
+ var menuKey = zoomBundle.getString(zoomType + ".accesskey");
+ zoomMenu.setAttribute("label", menuLabel);
+ zoomMenu.setAttribute("accesskey", menuKey);
+}
+
+function updateZoomMenu() {
+ var zoomBundle = document.getElementById("bundle_viewZoom");
+ var zoomOther = document.getElementById("menu_zoomOther");
+ var label = zoomBundle.getString("zoom.other.label");
+ var accesskey = zoomBundle.getString("zoom.other.accesskey");
+ var factorOther = zoomOther.getAttribute("value") ||
+ Math.round(ZoomManager.MAX * 100);
+ zoomOther.setAttribute("label", label.replace(/%zoom%/, factorOther));
+ zoomOther.setAttribute("accesskey", accesskey);
+ zoomOther.setAttribute("value", factorOther);
+
+ var popup = document.getElementById("menu_zoomPopup");
+ var item = popup.lastChild;
+ while (item) {
+ if (item.getAttribute("name") == "zoom") {
+ if (item.getAttribute("value") == Math.round(ZoomManager.zoom * 100))
+ item.setAttribute("checked","true");
+ else
+ item.removeAttribute("checked");
+ }
+ item = item.previousSibling;
+ }
+}
+
+function openZoomDialog() {
+ var zoomOther = document.getElementById("menu_zoomOther");
+ // open dialog and ask for new value
+ var o = {value: zoomOther.getAttribute("value"),
+ zoomMin: ZoomManager.MIN * 100,
+ zoomMax: ZoomManager.MAX * 100};
+ window.openDialog("chrome://communicator/content/askViewZoom.xul",
+ "", "chrome,modal,centerscreen", o);
+ if (o.zoomOK) {
+ zoomOther.setAttribute("value", o.value);
+ ZoomManager.zoom = o.value / 100;
+ }
+ return o.zoomOK;
+}
+
+function zoomEnlarge() {
+ FullZoom.enlarge();
+ updateZoomStatus();
+}
+
+function zoomReduce() {
+ FullZoom.reduce();
+ updateZoomStatus();
+}
+
+function zoomReset() {
+ FullZoom.reset();
+ updateZoomStatus();
+}
+
+function zoomSetOther() {
+ FullZoom.setOther();
+ updateZoomStatus();
+}
+
+function zoomToggle() {
+ ZoomManager.toggleZoom();
+ updateZoomStatus();
+}
+
+function zoomSet(aValue) {
+ FullZoom.zoom(aValue)
+ updateZoomStatus();
+}
diff --git a/comm/suite/base/content/viewZoomOverlay.xul b/comm/suite/base/content/viewZoomOverlay.xul
new file mode 100644
index 0000000000..a22c853e3e
--- /dev/null
+++ b/comm/suite/base/content/viewZoomOverlay.xul
@@ -0,0 +1,55 @@
+<?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 SYSTEM "chrome://communicator/locale/viewZoomOverlay.dtd">
+
+<overlay id="viewZoomOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://global/content/viewZoomOverlay.js"/>
+ <script src="chrome://communicator/content/viewZoomOverlay.js"/>
+
+ <stringbundle id="bundle_viewZoom" src="chrome://communicator/locale/viewZoomOverlay.properties"/>
+
+ <keyset id="viewZoomKeys">
+ <key id="key_zoomReduce" key="&zoomReduceCmd.commandkey;" command="cmd_zoomReduce" modifiers="accel"/>
+ <key id="key_zoomEnlarge" key="&zoomEnlargeCmd.commandkey;" command="cmd_zoomEnlarge" modifiers="accel"/>
+ <key key="&zoomEnlargeCmd.commandkey;" command="cmd_zoomEnlarge" modifiers="accel,shift"/>
+ <key key="&zoomEnlargeCmd.commandkey2;" command="cmd_zoomEnlarge" modifiers="accel"/>
+ <key id="key_zoomReset" key="&zoomResetCmd.commandkey;" command="cmd_zoomReset" modifiers="accel"/>
+ </keyset>
+
+ <commandset id="viewZoomCommands">
+ <command id="cmd_zoomReduce" oncommand="zoomReduce();"/>
+ <command id="cmd_zoomEnlarge" oncommand="zoomEnlarge();"/>
+ <command id="cmd_zoomReset" oncommand="zoomReset();"/>
+ <command id="cmd_zoomOther" oncommand="zoomSetOther();"/>
+ <command id="cmd_fullZoomToggle" oncommand="zoomToggle();"/>
+ </commandset>
+
+ <menu id="menu_zoom">
+ <menupopup id="menu_zoomPopup" onpopupshowing="updateZoomMenu();" oncommand="zoomSet(event.target.value / 100);">
+ <menuitem id="menu_zoomReduce"
+ key="key_zoomReduce"
+ label="&zoomReduceCmd.label;"
+ accesskey="&zoomReduceCmd.accesskey;"
+ command="cmd_zoomReduce"/>
+ <menuitem id="menu_zoomEnlarge"
+ key="key_zoomEnlarge"
+ label="&zoomEnlargeCmd.label;"
+ accesskey="&zoomEnlargeCmd.accesskey;"
+ command="cmd_zoomEnlarge"/>
+ <menuseparator/>
+ <menuseparator id="menu_zoomInsertBefore"/>
+ <menuitem id="menu_zoomOther"
+ type="radio"
+ name="zoom"
+ command="cmd_zoomOther"/>
+ </menupopup>
+ </menu>
+
+</overlay>
diff --git a/comm/suite/base/jar.mn b/comm/suite/base/jar.mn
new file mode 100644
index 0000000000..910f8e2251
--- /dev/null
+++ b/comm/suite/base/jar.mn
@@ -0,0 +1,96 @@
+#filter substitution
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.comm.jar:
+
+comm.jar:
+% content communicator %content/communicator/ contentaccessible=yes
+% override chrome://browser/content/utilityOverlay.js chrome://communicator/content/utilityOverlay.js
+% override chrome://global/content/license.html chrome://communicator/content/license.html
+% override chrome://global/content/netError.xhtml chrome://communicator/content/certError.xhtml
+% overlay chrome://global/content/viewSource.xul chrome://communicator/content/viewSourceOverlay.xul
+% overlay chrome://global/content/viewPartialSource.xul chrome://communicator/content/viewSourceOverlay.xul
+% overlay chrome://editor/content/EdAdvancedEdit.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EdImageProps.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EditorPublish.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EditorPublishProgress.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EditorPublishSettings.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EdLinkProps.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://editor/content/EdTableProps.xul chrome://communicator/content/helpEditorOverlay.xul
+% overlay chrome://pippki/content/certManager.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/certViewer.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/changepassword.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/device_manager.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/choosetoken.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/clientauthask.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/crlImportDialog.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/deletecert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/editcacert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/editemailcert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/editsslcert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/escrowWarn.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/getp12password.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/setp12password.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/pref-crlupdate.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/serverCrlNextupdate.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/crlManager.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/resetpassword.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://pippki/content/downloadcert.xul chrome://communicator/content/helpSecurityOverlay.xul
+% overlay chrome://messenger/content/addressbook/pref-editdirectories.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/addressbook/pref-directory-add.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/AccountManager.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/FilterEditor.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/junkMailInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/msgSelectOfflineFolders.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/subscribe.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/mailViewList.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger/content/mailViewSetup.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger-smime/content/msgCompSecurityInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
+% overlay chrome://messenger-smime/content/msgReadSecurityInfo.xul chrome://communicator/content/helpMessengerOverlay.xul
+% style about:addons chrome://communicator/content/extensionsOverlay.css
+% style chrome://mozapps/content/extensions/extensions.xul chrome://communicator/content/extensionsOverlay.css
+ content/communicator/about.js (content/about.js)
+ content/communicator/about.xhtml (content/about.xhtml)
+ content/communicator/aboutLife.xhtml (content/aboutLife.xhtml)
+ content/communicator/aboutPrivateBrowsing.css (content/aboutPrivateBrowsing.css)
+ content/communicator/aboutPrivateBrowsing.js (content/aboutPrivateBrowsing.js)
+ content/communicator/aboutPrivateBrowsing.xul (content/aboutPrivateBrowsing.xul)
+ content/communicator/askViewZoom.xul (content/askViewZoom.xul)
+ content/communicator/askViewZoom.js (content/askViewZoom.js)
+ content/communicator/blockedSite.js (content/blockedSite.js)
+ content/communicator/blockedSite.xhtml (content/blockedSite.xhtml)
+ content/communicator/certError.css (content/certError.css)
+ content/communicator/certError.js (content/certError.js)
+ content/communicator/certError.xhtml (content/certError.xhtml)
+ content/communicator/certError.xml (content/certError.xml)
+ content/communicator/charsetOverlay.xul (content/charsetOverlay.xul)
+* content/communicator/communicator.css (content/communicator.css)
+ content/communicator/contentAreaClick.js (content/contentAreaClick.js)
+ content/communicator/contentAreaContextOverlay.xul (content/contentAreaContextOverlay.xul)
+ content/communicator/defaultClientDialog.js (content/defaultClientDialog.js)
+ content/communicator/defaultClientDialog.xul (content/defaultClientDialog.xul)
+ content/communicator/extensionsOverlay.css (content/extensionsOverlay.css)
+ content/communicator/findUtils.js (content/findUtils.js)
+ content/communicator/fullscreen-video.xhtml (content/fullscreen-video.xhtml)
+ content/communicator/gopherAddon.xhtml (content/gopherAddon.xhtml)
+ content/communicator/helpEditorOverlay.xul (content/helpEditorOverlay.xul)
+ content/communicator/helpMessengerOverlay.xul (content/helpMessengerOverlay.xul)
+ content/communicator/helpSecurityOverlay.xul (content/helpSecurityOverlay.xul)
+# the following file is a suite-specific override of the generic license.html, using suite/common/app-license.html as input:
+* content/communicator/license.html (/toolkit/content/license.html)
+ content/communicator/nsContextMenu.js (content/nsContextMenu.js)
+ content/communicator/openLocation.js (content/openLocation.js)
+ content/communicator/openLocation.xul (content/openLocation.xul)
+ content/communicator/safeMode.js (content/safeMode.js)
+ content/communicator/safeMode.xul (content/safeMode.xul)
+ content/communicator/tasksOverlay.js (content/tasksOverlay.js)
+* content/communicator/tasksOverlay.xul (content/tasksOverlay.xul)
+ content/communicator/utilityOverlay.js (content/utilityOverlay.js)
+* content/communicator/utilityOverlay.xul (content/utilityOverlay.xul)
+ content/communicator/viewApplyThemeOverlay.xul (content/viewApplyThemeOverlay.xul)
+ content/communicator/viewApplyThemeOverlay.js (content/viewApplyThemeOverlay.js)
+ content/communicator/viewSourceOverlay.js (content/viewSourceOverlay.js)
+ content/communicator/viewSourceOverlay.xul (content/viewSourceOverlay.xul)
+ content/communicator/viewZoomOverlay.xul (content/viewZoomOverlay.xul)
+ content/communicator/viewZoomOverlay.js (content/viewZoomOverlay.js)
diff --git a/comm/suite/base/moz.build b/comm/suite/base/moz.build
new file mode 100644
index 0000000000..5ca66f7aa6
--- /dev/null
+++ b/comm/suite/base/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+# DEFINES for preprocessing
+
+# Use suite/base/content/overrides/app-license.html as input when generating
+# chrome://content/communicator/license.html to override
+# chrome://global/content/license.html (about:license)
+DEFINES["APP_LICENSE_BLOCK"] = "%s/content/overrides/app-license.html" % SRCDIR
+
+for var in ("MOZ_APP_NAME", "MOZ_MACBUNDLE_NAME"):
+ DEFINES[var] = CONFIG[var]
diff --git a/comm/suite/branding/branding-common.mozbuild b/comm/suite/branding/branding-common.mozbuild
new file mode 100644
index 0000000000..7a9f005818
--- /dev/null
+++ b/comm/suite/branding/branding-common.mozbuild
@@ -0,0 +1,70 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+@template
+def SeaMonkeyBranding():
+ JS_PREFERENCE_FILES += [
+ 'seamonkey-branding.js',
+ ]
+
+ # Note: mac icons are handled in /suite/app during the final application
+ # packaging
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk'):
+ desktop_icons = [
+ 'abcardWindow',
+ 'ablistWindow',
+ 'addressbookWindow',
+ 'bookmarkproperties',
+ 'chatzilla-window',
+ 'downloadManager',
+ 'editorWindow',
+ 'findBookmarkWindow',
+ 'findHistoryWindow',
+ 'history-window',
+ 'JSConsoleWindow',
+ 'messengerWindow',
+ 'msgcomposeWindow',
+ 'places',
+ ]
+
+ desktop_icons_small = []
+ desktop_icons_large = []
+
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ icon_suffix = '.ico'
+ icon_dir = 'windows'
+ # Windows icons
+ desktop_icons += [
+ 'gif-file',
+ 'html-file',
+ 'image-file',
+ 'main-window',
+ 'jpeg-file',
+ 'script-file',
+ 'xml-file',
+ 'xul-file',
+ ]
+ else:
+ icon_suffix = '.png'
+ icon_dir = 'gtk'
+ FINAL_TARGET_FILES.chrome.icons.default += [
+ 'default128.png',
+ 'default16.png',
+ 'default22.png',
+ 'default24.png',
+ 'default256.png',
+ 'default32.png',
+ 'default48.png',
+ 'default64.png',
+ ]
+ desktop_icons_small = [ '%s16' % i for i in desktop_icons ]
+ desktop_icons_large = [ '%s48' % i for i in desktop_icons ]
+
+ FINAL_TARGET_FILES.chrome.icons.default += [
+ 'icons/%s/%s%s' % (icon_dir, i, icon_suffix) for i in sorted(
+ desktop_icons + desktop_icons_small + desktop_icons_large
+ )
+ ]
diff --git a/comm/suite/branding/seamonkey/background.png b/comm/suite/branding/seamonkey/background.png
new file mode 100644
index 0000000000..22e4754e88
--- /dev/null
+++ b/comm/suite/branding/seamonkey/background.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/branding.nsi b/comm/suite/branding/seamonkey/branding.nsi
new file mode 100644
index 0000000000..b3dcc59d6b
--- /dev/null
+++ b/comm/suite/branding/seamonkey/branding.nsi
@@ -0,0 +1,15 @@
+# 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/.
+
+#NSIS defines
+# BrandFullNameInternal is used for some registry and file system values
+# instead of BrandFullName and typically should not be modified.
+!define BrandFullNameInternal "SeaMonkey"
+!define CompanyName "mozilla.org"
+!define URLInfoAbout "http://www.mozilla.org"
+!define URLUpdateInfo "http://www.seamonkey-project.org"
+!define URLSystemRequirements "http://www.seamonkey-project.org/doc/system-requirements"
+
+# Percentage of new "Standard" installs to enable talkback for
+!define RandomPercent "100"
diff --git a/comm/suite/branding/seamonkey/content/about.png b/comm/suite/branding/seamonkey/content/about.png
new file mode 100644
index 0000000000..354e3fea92
--- /dev/null
+++ b/comm/suite/branding/seamonkey/content/about.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/content/aboutRights.xhtml b/comm/suite/branding/seamonkey/content/aboutRights.xhtml
new file mode 100644
index 0000000000..fb61a71928
--- /dev/null
+++ b/comm/suite/branding/seamonkey/content/aboutRights.xhtml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % prefsDTD SYSTEM "chrome://communicator/locale/pref/preferences.dtd">
+ %prefsDTD;
+ <!ENTITY % prefSmartUpdateDTD SYSTEM "chrome://communicator/locale/pref/pref-smartupdate.dtd">
+ %prefSmartUpdateDTD;
+ <!ENTITY % prefSecurityDTD SYSTEM "chrome://communicator/locale/pref/pref-security.dtd">
+ %prefSecurityDTD;
+ <!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
+ %extensionsDTD;
+ <!ENTITY % aboutRightsDTD SYSTEM "chrome://branding/locale/aboutRights.dtd">
+ %aboutRightsDTD;
+]>
+<!-- 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/. -->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>&rights.pagetitle;</title>
+ <link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
+</head>
+
+<body id="your-rights" dir="&rights.locale-direction;" class="aboutPageWideContainer">
+
+<h1>&rights.intro-header;</h1>
+
+<p>&rights.intro;</p>
+
+<ul>
+ <li>&rights.intro-point1a;<a href="http://www.mozilla.org/MPL/">&rights.intro-point1b;</a>&rights.intro-point1c;</li>
+<!-- Point 2aa discusses SeaMonkey trademarks, and isn't needed when the build is unbranded.
+ - Point 2dd discusses opt-in feedback mechanisms like the Crash Reporter
+ - Point 3a discusses privacy policy, unbranded builds get a placeholder (for the vendor to replace)
+ - Point 4a discusses web service terms, unbranded builds gets a placeholder (for the vendor to replace) -->
+ <li>&rights.intro-point2aa;<a href="https://www.seamonkey-project.org/legal/trademark">&rights.intro-point2b;</a>&rights.intro-point2c;</li>
+ <li>&rights.intro-point2da;</li>
+ <li>&rights.intro-point3a;<a href="http://www.seamonkey-project.org/legal/privacy">&rights.intro-point3b;</a>&rights.intro-point3c;</li>
+ <li>&rights2.intro-point4a;<a href="about:rights#webservices" onclick="showServices();">&rights.intro-point4b;</a>&rights.intro-point4c;</li>
+</ul>
+
+<div id="webservices-container">
+ <a name="webservices"/>
+ <h3>&rights.webservices-header;</h3>
+
+ <p>&rights3.webservices-aa;<a href="about:rights#disabling-webservices" onclick="showDisablingServices();">&rights3.webservices-b;</a>&rights3.webservices-c;</p>
+
+ <div id="disabling-webservices-container" style="margin-left:40px;">
+ <a name="disabling-webservices"/>
+ <p><strong>&rights.addons-a;</strong>&rights.addons-b;</p>
+ <ul>
+ <li>&rights.addons-term1;</li>
+ <li>&rights.addons-term2;</li>
+ <li>&rights.addons-term3;</li>
+ <li>&rights.addons-term4;</li>
+ </ul>
+ <p><strong>&rights.safebrowsing-a;</strong>&rights.safebrowsing-b;</p>
+ <ul>
+ <li>&rights.safebrowsing-term1;</li>
+ <li>&rights.safebrowsing-term2;</li>
+ <li>&rights.safebrowsing-term3;</li>
+ <li>&rights.safebrowsing-term4;</li>
+ </ul>
+ <p><strong>&rights.locationawarebrowsing-a;</strong>&rights.locationawarebrowsing-b;</p>
+ <ul>
+ <li>&rights.locationawarebrowsing-term1;</li>
+ <li>&rights.locationawarebrowsing-term2;</li>
+ <li>&rights.locationawarebrowsing-term3;</li>
+ <li>&rights.locationawarebrowsing-term4;</li>
+ </ul>
+ </div>
+
+ <ol>
+ <li>&rights.webservices-term0a;</li>
+ <li>&rights.webservices-term1;</li>
+ <li>&rights.webservices-term2a;</li>
+ <li><strong>&rights.webservices-term3;</strong></li>
+ <li><strong>&rights.webservices-term4;</strong></li>
+ <li>&rights.webservices-term5;</li>
+ <li>&rights.webservices-term6a;</li>
+ </ol>
+</div>
+
+<script>
+<![CDATA[
+ var servicesDiv = document.getElementById("webservices-container");
+ servicesDiv.style.display = "none";
+
+ function showServices() {
+ servicesDiv.style.display = "";
+ }
+
+ var disablingServicesDiv = document.getElementById("disabling-webservices-container");
+ disablingServicesDiv.style.display = "none";
+
+ function showDisablingServices() {
+ disablingServicesDiv.style.display = "";
+ }
+]]></script>
+
+</body>
+</html>
diff --git a/comm/suite/branding/seamonkey/content/jar.mn b/comm/suite/branding/seamonkey/content/jar.mn
new file mode 100644
index 0000000000..9724b64bf1
--- /dev/null
+++ b/comm/suite/branding/seamonkey/content/jar.mn
@@ -0,0 +1,16 @@
+# 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/.
+#filter substitution
+
+comm.jar:
+% content branding %content/branding/ contentaccessible=yes
+ content/branding/about.png (about.png)
+ content/branding/aboutRights.xhtml (aboutRights.xhtml)
+ content/branding/icon16.png (../default16.png)
+ content/branding/icon32.png (../default32.png)
+ content/branding/icon48.png (../default48.png)
+ content/branding/icon64.png (../default64.png)
+ content/branding/icon128.png (../default128.png)
+ content/branding/messenger-start-bg.png (messenger-start-bg.png)
+ content/branding/messenger-start-hdr.png (messenger-start-hdr.png)
diff --git a/comm/suite/branding/seamonkey/content/messenger-start-bg.png b/comm/suite/branding/seamonkey/content/messenger-start-bg.png
new file mode 100644
index 0000000000..a94d22df4e
--- /dev/null
+++ b/comm/suite/branding/seamonkey/content/messenger-start-bg.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/content/messenger-start-hdr.png b/comm/suite/branding/seamonkey/content/messenger-start-hdr.png
new file mode 100644
index 0000000000..e1f61f7931
--- /dev/null
+++ b/comm/suite/branding/seamonkey/content/messenger-start-hdr.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/content/moz.build b/comm/suite/branding/seamonkey/content/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/branding/seamonkey/content/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/branding/seamonkey/default128.png b/comm/suite/branding/seamonkey/default128.png
new file mode 100644
index 0000000000..f37b8341e9
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default128.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default16.png b/comm/suite/branding/seamonkey/default16.png
new file mode 100644
index 0000000000..9e0a272dfc
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default22.png b/comm/suite/branding/seamonkey/default22.png
new file mode 100644
index 0000000000..b508df3cca
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default22.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default24.png b/comm/suite/branding/seamonkey/default24.png
new file mode 100644
index 0000000000..d9742210f5
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default24.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default256.png b/comm/suite/branding/seamonkey/default256.png
new file mode 100644
index 0000000000..1ae128fae2
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default256.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default32.png b/comm/suite/branding/seamonkey/default32.png
new file mode 100644
index 0000000000..01881204bb
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default32.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default48.png b/comm/suite/branding/seamonkey/default48.png
new file mode 100644
index 0000000000..c9ff370e22
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/default64.png b/comm/suite/branding/seamonkey/default64.png
new file mode 100644
index 0000000000..f0966c5838
--- /dev/null
+++ b/comm/suite/branding/seamonkey/default64.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/disk.icns b/comm/suite/branding/seamonkey/disk.icns
new file mode 100644
index 0000000000..6d28bf28b7
--- /dev/null
+++ b/comm/suite/branding/seamonkey/disk.icns
Binary files differ
diff --git a/comm/suite/branding/seamonkey/document.icns b/comm/suite/branding/seamonkey/document.icns
new file mode 100644
index 0000000000..0c2094c302
--- /dev/null
+++ b/comm/suite/branding/seamonkey/document.icns
Binary files differ
diff --git a/comm/suite/branding/seamonkey/dsstore b/comm/suite/branding/seamonkey/dsstore
new file mode 100644
index 0000000000..955aef3cbc
--- /dev/null
+++ b/comm/suite/branding/seamonkey/dsstore
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow.png b/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow.png
new file mode 100644
index 0000000000..d23cf77ec3
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow16.png
new file mode 100644
index 0000000000..5b40e5c602
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow48.png
new file mode 100644
index 0000000000..b1b2a83aca
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/JSConsoleWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/abcardWindow.png b/comm/suite/branding/seamonkey/icons/gtk/abcardWindow.png
new file mode 100644
index 0000000000..a1637e5cc1
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/abcardWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/abcardWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/abcardWindow16.png
new file mode 100644
index 0000000000..0d5698927f
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/abcardWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/abcardWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/abcardWindow48.png
new file mode 100644
index 0000000000..77a7294105
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/abcardWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/ablistWindow.png b/comm/suite/branding/seamonkey/icons/gtk/ablistWindow.png
new file mode 100644
index 0000000000..5e57a65bfb
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/ablistWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/ablistWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/ablistWindow16.png
new file mode 100644
index 0000000000..06415b9a58
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/ablistWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/ablistWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/ablistWindow48.png
new file mode 100644
index 0000000000..5ebc64d74c
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/ablistWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow.png b/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow.png
new file mode 100644
index 0000000000..aba91e35cf
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow16.png
new file mode 100644
index 0000000000..a14700d09e
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow48.png
new file mode 100644
index 0000000000..d81247baec
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/addressbookWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties.png b/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties.png
new file mode 100644
index 0000000000..4b233162e0
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties16.png b/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties16.png
new file mode 100644
index 0000000000..c659ee4539
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties48.png b/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties48.png
new file mode 100644
index 0000000000..64d66a7425
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/bookmarkproperties48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window.png b/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window.png
new file mode 100644
index 0000000000..44c632dcbb
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window16.png b/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window16.png
new file mode 100644
index 0000000000..97d7544811
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window48.png b/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window48.png
new file mode 100644
index 0000000000..cc915835b6
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/chatzilla-window48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/downloadManager.png b/comm/suite/branding/seamonkey/icons/gtk/downloadManager.png
new file mode 100644
index 0000000000..b0037da069
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/downloadManager.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/downloadManager16.png b/comm/suite/branding/seamonkey/icons/gtk/downloadManager16.png
new file mode 100644
index 0000000000..541dffa31d
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/downloadManager16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/downloadManager48.png b/comm/suite/branding/seamonkey/icons/gtk/downloadManager48.png
new file mode 100644
index 0000000000..cf561d95d9
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/downloadManager48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/editorWindow.png b/comm/suite/branding/seamonkey/icons/gtk/editorWindow.png
new file mode 100644
index 0000000000..4961c763f1
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/editorWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/editorWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/editorWindow16.png
new file mode 100644
index 0000000000..b5f5f6ba78
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/editorWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/editorWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/editorWindow48.png
new file mode 100644
index 0000000000..0f5f6592de
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/editorWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow.png b/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow.png
new file mode 100644
index 0000000000..b3735ff518
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow16.png
new file mode 100644
index 0000000000..41c71f39c5
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow48.png
new file mode 100644
index 0000000000..31d3c63fed
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/findBookmarkWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow.png b/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow.png
new file mode 100644
index 0000000000..4eebfc723e
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow16.png
new file mode 100644
index 0000000000..a72dbc062e
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow48.png
new file mode 100644
index 0000000000..a37b30e30f
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/findHistoryWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/history-window.png b/comm/suite/branding/seamonkey/icons/gtk/history-window.png
new file mode 100644
index 0000000000..302c846664
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/history-window.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/history-window16.png b/comm/suite/branding/seamonkey/icons/gtk/history-window16.png
new file mode 100644
index 0000000000..8e9a2d0227
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/history-window16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/history-window48.png b/comm/suite/branding/seamonkey/icons/gtk/history-window48.png
new file mode 100644
index 0000000000..569acef4ca
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/history-window48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/messengerWindow.png b/comm/suite/branding/seamonkey/icons/gtk/messengerWindow.png
new file mode 100644
index 0000000000..c976d40b83
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/messengerWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/messengerWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/messengerWindow16.png
new file mode 100644
index 0000000000..8259ab67d9
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/messengerWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/messengerWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/messengerWindow48.png
new file mode 100644
index 0000000000..125711d48a
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/messengerWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow.png b/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow.png
new file mode 100644
index 0000000000..9baec7e14a
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow16.png b/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow16.png
new file mode 100644
index 0000000000..d70146cd11
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow48.png b/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow48.png
new file mode 100644
index 0000000000..461ea0a4c3
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/msgcomposeWindow48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/places.png b/comm/suite/branding/seamonkey/icons/gtk/places.png
new file mode 100644
index 0000000000..d634e8d26f
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/places.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/places16.png b/comm/suite/branding/seamonkey/icons/gtk/places16.png
new file mode 100644
index 0000000000..018853582c
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/places16.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/gtk/places48.png b/comm/suite/branding/seamonkey/icons/gtk/places48.png
new file mode 100644
index 0000000000..5802b6a507
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/gtk/places48.png
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/svg/JSConsoleWindow.svg b/comm/suite/branding/seamonkey/icons/svg/JSConsoleWindow.svg
new file mode 100644
index 0000000000..73931081c5
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/JSConsoleWindow.svg
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="JSConsoleWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs12339">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2242"
+ x1="441.50293"
+ x2="553.63983"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ y1="596.54565"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5809"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ offset="0.00000000"
+ id="stop5811"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ offset="1.0000000"
+ id="stop5813"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2239"
+ x1="331.57434"
+ x2="225.65819"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ y1="354.45773"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5074"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ offset="0.00000000"
+ id="stop5076"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ offset="1.0000000"
+ id="stop5078"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient2275"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214"
+ fy="821.82721"
+ fx="1147.2316" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3214">
+ <stop
+ offset="0"
+ id="stop3216"
+ style="stop-color:#eb0000;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3218"
+ style="stop-color:#ae0000;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2277"
+ x1="1139.5706"
+ y1="826.1059"
+ x2="1152.082"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3251"
+ y2="730.0083" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3251">
+ <stop
+ offset="0"
+ id="stop3253"
+ style="stop-color:#380000;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3255"
+ style="stop-color:#b90000;stop-opacity:1" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata12342">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-373.79172,-373.74402)"
+ id="g3501"
+ inkscape:label="Background"
+ style="display:inline">
+ <g
+ id="g2219">
+ <circle
+ id="path6573"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ cx="365.22308"
+ cy="433.07086"
+ r="395.53806" />
+ <path
+ id="path2064"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-ydpi="24.028801"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path2056"
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cssssssssssscsss"
+ inkscape:connector-curvature="0" />
+ <path
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path2058"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ inkscape:connector-curvature="0" />
+ <circle
+ id="path2052"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ cx="365.22308"
+ cy="433.07086"
+ r="395.53806" />
+ </g>
+ </g>
+ <path
+ d="m 1161.6664,817.31491 -13.6628,10.48381 -17.0742,-2.24786 -10.4838,-13.66277 2.2479,-17.07422 13.6627,-10.48381 17.0742,2.24786 10.4839,13.66278 z"
+ inkscape:flatsided="true"
+ id="path3230"
+ inkscape:rounded="0"
+ inkscape:export-ydpi="90.000084"
+ sodipodi:cx="1142.1799"
+ inkscape:export-xdpi="90.000084"
+ inkscape:randomized="0"
+ transform="matrix(1.6768645,-0.2183201,0.2183201,1.6768645,-2027.2621,-1038.2989)"
+ sodipodi:arg2="0.91629786"
+ sodipodi:arg1="0.52359878"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:r2="20.788244"
+ sodipodi:r1="22.501036"
+ sodipodi:sides="8"
+ sodipodi:type="star"
+ style="fill:url(#radialGradient2275);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2277);stroke-width:1.98091471;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cy="806.06439" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3236"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 59.3082,86 C 57.9189,86 56.7703,85.57255 55.8621,84.71769 54.9543,83.86283 54.5,82.74084 54.5,81.3517 c 0.036,-0.24889 0.054,-0.44453 0.054,-0.58693 0.1778,-1.53148 0.8194,-2.72465 1.9238,-3.57955 1.1041,-0.85486 2.4398,-1.28231 4.0058,-1.28231 0.926,0 1.8522,0.28478 2.7781,0.85433 0.9259,0.56955 1.4961,1.28177 1.7098,2.13664 0.2134,0.85489 0.3203,1.47851 0.3203,1.87089 0,1.53151 -0.6057,2.78692 -1.8164,3.76626 C 62.2644,85.51035 60.8756,86 59.3082,86 Z m 4.115,-13.51607 h -3.3127 c 0,0 -1.0556,-19.2335 2.297,-25.644649 3.3526,-6.41115 10.859,-6.41115 11.1126,0 C 73.7744,53.25043 63.4232,72.48393 63.4232,72.48393 Z"
+ style="font-style:normal;font-weight:bold;font-size:24px;font-family:'Sprint SF';writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/abcardWindow.svg b/comm/suite/branding/seamonkey/icons/svg/abcardWindow.svg
new file mode 100644
index 0000000000..6242784fd3
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/abcardWindow.svg
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="abcardWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs483">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient7470"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient7472"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ x="-0.12278381"
+ width="1.2455676"
+ y="-0.142712"
+ height="1.285424"
+ id="filter6228">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="2.9049243"
+ id="feGaussianBlur6230" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter6850">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514"
+ height="1.169028"
+ id="filter3395">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254385"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544" />
+ </filter>
+ </defs>
+ <metadata
+ id="metadata486">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g7546"
+ transform="translate(-525.69257,-1884.051)">
+ <g
+ id="g7434"
+ transform="translate(152.00085,1510.1315)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g7436" />
+ <g
+ style="display:inline"
+ id="g7438"
+ inkscape:label="Background">
+ <g
+ id="g7440">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path7442"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient7470);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path7444"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient7472);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path7446"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path7448"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path7450"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g7452" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ id="path7454"
+ d="m 575.81447,1912.2934 c -6.30783,2.9703 -9.35423,5.9425 -12.73541,15.3475 0,0 -3.34665,13.0041 1.87573,27.6069 0.83249,2.3278 4.15134,6.7969 4.19691,6.8355 2.55947,2.1686 5.20572,2.7906 7.93353,3.8699 0.11878,0.047 0.23886,0.086 0.35791,0.1323 0.96932,0.376 1.96621,0.7372 2.9527,1.0584 1.89433,0.6169 3.80918,1.1122 5.75629,1.5216 0.22803,0.048 0.45733,0.087 0.68598,0.1323 0.80983,0.1595 1.62874,0.3072 2.44568,0.43 0.15841,0.024 0.31855,0.044 0.4772,0.066 0.83959,0.1185 1.68934,0.2184 2.53515,0.2976 0.0896,0.01 0.17876,0.025 0.26843,0.033 0.0796,0.01 0.15899,-0.01 0.2386,0 0.81718,0.069 -6.20845,-3.316 -5.38665,-3.2844 0.14039,0.01 0.27704,0.029 0.41755,0.033 0.12833,0 0.25931,0 0.38773,0 0.65606,0.015 1.31023,0.01 1.96847,0 0.55566,-0.01 2.061,5.4688 2.6179,5.4438 0.0987,0 0.19952,0 0.29825,0 0.18875,-0.01 0.37783,-0.022 0.56668,-0.033 0.66699,-0.041 -6.50211,-3.549 -5.83402,-3.615 0.12869,-0.013 0.25901,-0.02 0.38773,-0.033 0.23941,-0.025 0.47631,-0.071 0.71581,-0.099 0.70747,-0.085 1.40978,-0.1515 2.11759,-0.2646 0.0403,-0.01 0.079,-0.027 0.1193,-0.033 0.17773,-0.029 0.35913,-0.069 0.53686,-0.099 0.77016,-0.133 1.55677,-0.2628 2.32637,-0.4301 0.0401,-0.01 0.0793,-0.024 0.11931,-0.033 0.0992,-0.022 0.19902,-0.044 0.29825,-0.066 0.88776,-0.2002 1.76871,-0.4149 2.65445,-0.6615 0.91489,-0.2549 1.83248,-0.5219 2.74393,-0.827 0.0687,-0.023 0.14006,-0.043 0.20878,-0.066 0.9807,-0.3324 1.94782,-0.7001 2.92288,-1.0915 0.94549,-0.3797 1.88607,-0.7979 2.80358,-1.2238 0.0288,-0.014 0.0607,-0.02 0.0895,-0.033 0.39408,-0.1838 0.77438,-0.3702 1.16319,-0.5624 0.24543,-0.1228 0.47249,-0.2707 0.71581,-0.3968 0.4098,-0.2125 0.81907,-0.4075 1.22284,-0.6285 0.64348,-0.3523 4.30377,-5.0196 5.24805,-7.1433 5.62389,-12.6479 1.33833,-26.8357 1.33833,-26.8357 -3.27093,-8.7869 -5.40799,-11.8466 -12.70558,-15.3475 -8.51035,-4.0827 -19.65748,-3.4819 -27.05156,0 z"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:0.62111798;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter6228)"
+ d="m 238.5625,1732.9688 c -6.75933,-1e-4 -12.21875,6.46 -12.21875,14.5 l -3.15625,22.6875 c 0,1.8518 0.30788,3.5942 0.84375,5.2187 2.13639,1.0456 4.31807,1.9805 6.5625,2.7813 0.12446,0.044 0.25027,0.081 0.375,0.125 1.01564,0.3552 2.06013,0.6965 3.09375,1 1.98483,0.5828 3.99113,1.0507 6.03125,1.4375 0.23893,0.045 0.47918,0.082 0.71875,0.125 0.84852,0.1507 1.70654,0.2901 2.5625,0.4062 0.16599,0.022 0.33377,0.041 0.5,0.062 0.8797,0.112 1.77004,0.2063 2.65625,0.2813 0.0939,0.01 0.1873,0.024 0.28125,0.031 0.0834,0.01 0.16658,-0.01 0.25,0 0.85622,0.065 1.70145,0.1265 2.5625,0.1563 0.14711,0.01 0.29028,0.027 0.4375,0.031 0.13447,0 0.27169,0 0.40625,0 0.68741,0.014 1.37281,0.01 2.0625,0 0.58221,-0.01 1.1665,-0.039 1.75,-0.062 0.10342,0 0.20905,0 0.3125,0 0.19777,-0.01 0.39587,-0.02 0.59375,-0.031 0.69886,-0.039 1.39375,-0.094 2.09375,-0.1563 0.13483,-0.012 0.27138,-0.018 0.40625,-0.031 0.25084,-0.024 0.49906,-0.067 0.75,-0.094 0.74126,-0.08 1.47713,-0.1431 2.21875,-0.25 0.0422,-0.01 0.0828,-0.025 0.125,-0.031 0.18621,-0.027 0.37628,-0.065 0.5625,-0.094 0.80694,-0.1256 1.63113,-0.2483 2.4375,-0.4062 0.042,-0.01 0.083,-0.023 0.125,-0.031 0.10397,-0.021 0.20853,-0.041 0.3125,-0.062 0.93016,-0.1892 1.8532,-0.392 2.78125,-0.625 0.95858,-0.2408 1.92001,-0.493 2.875,-0.7812 0.072,-0.022 0.14675,-0.041 0.21875,-0.062 1.02754,-0.3141 2.04087,-0.6615 3.0625,-1.0313 0.99065,-0.3587 1.97616,-0.7538 2.9375,-1.1562 0.0302,-0.013 0.0636,-0.019 0.0937,-0.031 0.41289,-0.1737 0.81137,-0.3498 1.21875,-0.5312 0.25714,-0.1161 0.49506,-0.2559 0.75,-0.375 0.34442,-0.161 0.69023,-0.3078 1.03125,-0.4688 0.49149,-1.5691 0.78125,-3.2494 0.78125,-5.0312 l -3.25,-23 c 0,-8.0401 -5.45942,-14.5 -12.21875,-14.5 z"
+ id="path7456"
+ transform="matrix(1,0,0,0.9389427,338.46002,336.25238)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path7458"
+ d="m 583.11571,1960.4203 c -5.55991,-10e-5 -9.9375,6.7915 -9.9375,15.4062 -5.6e-4,0.1979 -0.0101,0.3987 -0.0312,0.5938 l -3.15625,29.4375 c 0.002,0.2876 0.0194,0.5603 0.0312,0.8437 12.56066,4.2167 26.51563,4.5343 39.96875,0.031 3.7e-4,-0.031 -2.6e-4,-0.062 0,-0.094 l -3.25,-29.8125 c -0.0218,-0.2007 -0.0313,-0.3903 -0.0312,-0.5937 10e-6,-8.6147 -4.37761,-15.4375 -9.9375,-15.4375 z"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path7460"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.3953873,0,0,0.5094214,454.70787,1765.1701)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path7462"
+ d="m 598.80321,1961.9828 3.65625,46.75 c 3.01845,-0.6109 6.01815,-1.4205 9,-2.5 1.94466,-0.7041 3.8259,-1.5049 5.65625,-2.375 l -4.125,-26.0938 c -0.0309,-0.1975 -0.0626,-0.4247 -0.0625,-0.625 -1e-5,-8.4805 -6.22516,-15.1562 -14.125,-15.1562 z"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path7464"
+ d="m 580.49071,1961.6703 c -7.89984,0 -14.15625,6.7378 -14.15625,15.25 6e-5,0.201 -3.1e-4,0.3954 -0.0312,0.5937 l -4.09375,26 c 4.63591,2.279 9.55055,3.9706 14.625,5.0625 z"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(0.3953873,0,0,0.5094214,454.70787,1765.1701)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path7466"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(0.3953873,0,0,0.3980335,485.21164,1824.6047)"
+ id="path7468"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="m 574.06634,1928.9264 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.099 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.097 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ id="path7476" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path7474"
+ d="m 567.47842,1929.3122 c 0.81789,-2.8937 2.81139,-5.516 4.82203,-7.7378 1.8972,-2.0965 5.27221,-3.5114 7.63722,-5.1745 0.39883,-0.2805 0.79903,-0.559 1.19854,-0.8385 2.55981,-1.7907 4.90753,-3.4042 8.08318,-3.785 2.54762,-0.3055 4.59925,1.0236 6.88464,1.9165 2.62559,1.0257 5.11612,2.8607 7.19125,4.7433 1.89214,1.7165 3.72143,3.4395 5.40737,5.3901 1.56394,1.8094 2.71506,3.9074 3.9301,5.9411 1.01373,1.6966 2.06152,4.202 1.19854,6.0848 -1.14061,2.4885 -4.83953,-1.0294 -6.07632,-1.8686 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 -4.30796,0.7064 -6.32718,0.2875 -1.15282,-0.2391 -0.0647,-2.299 0.3066,-2.8028 0.25022,-0.3396 0.63179,-0.559 0.94769,-0.8385 z"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/ablistWindow.svg b/comm/suite/branding/seamonkey/icons/svg/ablistWindow.svg
new file mode 100644
index 0000000000..073c059365
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/ablistWindow.svg
@@ -0,0 +1,1590 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="ablistWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs2257">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3592"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3594"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter6850">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514"
+ height="1.169028"
+ id="filter3395">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254385"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter6850-7">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852-8" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514"
+ height="1.169028"
+ id="filter3395-0">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397-0" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254385"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542-9">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544-0" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter6850-0">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852-7" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514"
+ height="1.169028"
+ id="filter3395-2">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397-2" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254385"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542-4">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544-2" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter6850-6">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852-9" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514"
+ height="1.169028"
+ id="filter3395-8">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397-6" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254385"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542-1">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544-3" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter6850-2">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852-0" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514"
+ height="1.169028"
+ id="filter3395-26">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397-4" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254385"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542-97">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544-24" />
+ </filter>
+ </defs>
+ <metadata
+ id="metadata2260">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-373.79172,-373.74402)"
+ id="g3516">
+ <g
+ id="g3518"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g3520"
+ style="display:inline">
+ <g
+ id="g3522">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path3524"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path3526"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient3592);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path3528"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient3594);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path3530"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path3532"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g3534"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ id="g6337"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-128.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013"
+ transform="translate(1094.0761,-2380.7444)">
+ <g
+ id="g6205"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="Shadow"
+ id="g6207" />
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ id="g6210"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6213">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6216"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path6224"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path6226"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path6228"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6230"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="SeaMonkey"
+ id="g6232" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6234"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6236"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6238"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6240">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6243" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6248" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6251"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6253" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6255" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6257"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6259"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6261"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6263" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6265"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6267"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6269"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6271"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6273">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6275" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6277" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6279"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6281" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6283" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6285"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6287"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6289"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6291" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6293"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g6337-5"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-128.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013"
+ transform="translate(1094.0761,-2380.7444)">
+ <g
+ id="g6205-3"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="Shadow"
+ id="g6207-1" />
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ id="g6210-1"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6213-8">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6216-8"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path6224-9"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path6226-6"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path6228-8"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6230-9"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="SeaMonkey"
+ id="g6232-5" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6234-0"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6236-6"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6238-7"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6240-5">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6243-3" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6248-4" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6251-3"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-7)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6253-1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6255-9" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6257-7"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-0)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6259-4"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6261-1"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-9)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6263-8" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6265-7"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6267-8"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6269-6"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6271-0"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6273-8">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6275-6" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6277-0" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6279-5"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-7)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6281-1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6283-1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6285-9"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-0)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6287-9"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6289-2"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-9)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6291-8" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6293-7"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g6337-8"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-128.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013"
+ transform="translate(1094.0761,-2380.7444)">
+ <g
+ id="g6205-9"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="Shadow"
+ id="g6207-6" />
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ id="g6210-10"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6213-5">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6216-0"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path6224-7"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path6226-69"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path6228-4"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6230-6"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="SeaMonkey"
+ id="g6232-54" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6234-7"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6236-3"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6238-72"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6240-4">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6243-8" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6248-1" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6251-5"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-0)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6253-2" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6255-2" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6257-4"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-2)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6259-3"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6261-17"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-4)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6263-7" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6265-79"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6267-5"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6269-67"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6271-7"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6273-9">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6275-3" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6277-9" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6279-6"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-0)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6281-18" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6283-2" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6285-8"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-2)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6287-6"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6289-9"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-4)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6291-5" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6293-1"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g6337-9"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-128.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013"
+ transform="translate(1094.0761,-2380.7444)">
+ <g
+ id="g6205-5"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="Shadow"
+ id="g6207-0" />
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ id="g6210-4"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6213-6">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6216-7"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path6224-1"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path6226-68"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path6228-7"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6230-2"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="SeaMonkey"
+ id="g6232-7" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6234-6"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6236-30"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6238-71"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6240-46">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6243-6" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6248-2" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6251-4"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-6)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6253-7" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6255-95" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6257-5"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-8)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6259-8"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6261-3"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-1)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6263-1" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6265-1"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6267-9"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6269-0"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6271-6"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6273-96">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6275-5" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6277-8" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6279-8"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-6)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6281-187" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6283-5" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6285-5"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-8)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6287-5"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6289-1"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-1)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6291-82" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6293-4"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g6337-55"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-128.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013"
+ transform="translate(864.57858,-2002.3036)">
+ <g
+ id="g6205-6"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="Shadow"
+ id="g6207-7" />
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ id="g6210-6"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6213-85">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6216-75"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path6224-2"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path6226-5"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path6228-5"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6230-5"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:#ffffff;fill-opacity:0"
+ inkscape:label="SeaMonkey"
+ id="g6232-543" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6234-1"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6236-9"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6238-4"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6240-1">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6243-30" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6248-5" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6251-43"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-2)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6253-3" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6255-0" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6257-2"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-26)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6259-0"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6261-2"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-97)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6263-9" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6265-5"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6267-0"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6269-5"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6271-4"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6273-6">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ id="path6275-52" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ id="path6277-6" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6279-2"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850-2)"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ id="path6281-4" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ id="path6283-19" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6285-2"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395-26)"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ id="path6287-7"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path6289-3"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542-97)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ id="path6291-50" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6293-49"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/addressbookWindow.svg b/comm/suite/branding/seamonkey/icons/svg/addressbookWindow.svg
new file mode 100644
index 0000000000..c04b6b4a21
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/addressbookWindow.svg
@@ -0,0 +1,358 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="addressbookWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs3879">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2242"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop5811" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop5813" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2239"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop5076" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop5078" />
+ </linearGradient>
+ <filter
+ id="filter6850"
+ inkscape:collect="always">
+ <feGaussianBlur
+ id="feGaussianBlur6852"
+ stdDeviation="4.2275602"
+ inkscape:collect="always" />
+ </filter>
+ <filter
+ id="filter3395"
+ height="1.169028"
+ y="-0.084514"
+ width="1.2221982"
+ x="-0.11109908"
+ inkscape:collect="always">
+ <feGaussianBlur
+ id="feGaussianBlur3397"
+ stdDeviation="4.1250422"
+ inkscape:collect="always" />
+ </filter>
+ <filter
+ id="filter7542"
+ height="1.3432254"
+ y="-0.17161272"
+ width="1.1845088"
+ x="-0.092254385"
+ inkscape:collect="always">
+ <feGaussianBlur
+ id="feGaussianBlur7544"
+ stdDeviation="1.900534"
+ inkscape:collect="always" />
+ </filter>
+ </defs>
+ <metadata
+ id="metadata3882">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g2596"
+ transform="translate(-240,-92.000006)">
+ <g
+ transform="translate(-133.79172,-281.74402)"
+ id="g2650">
+ <g
+ id="layer3"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g2653"
+ style="display:inline">
+ <g
+ id="g2219">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path6573"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2064"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2056"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2058"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path2052"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="layer2"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 278,118 v 6 h 6 6 6 v -6 h -6 -6 z"
+ id="path2664" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 302,118 v 6 h -6 v 6 h 6 6 6 6 v -6 -6 h -6 -6 z"
+ id="path2666" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 326,118 v 6 h -6 v 6 h 6 6 6 6 v -6 -6 h -6 -6 z"
+ id="path2668" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 266,124 v 6 h 6 6 6 6 6 v -6 h -6 -6 -6 -6 z"
+ id="path2670" />
+ <rect
+ y="124"
+ x="284"
+ height="6"
+ width="6"
+ id="rect2672"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 260,130 v 6 6 6 6 6 6 6 6 6 6 h 6 6 6 6 v -6 h -6 -6 -6 v -6 -6 -6 -6 -6 -6 -6 -6 -6 z"
+ id="path2674" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 290,184 v 6 h 6 6 6 6 6 v -6 h -6 -6 -6 -6 z"
+ id="path2676" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 344,124 v 6 6 6 6 6 6 6 6 6 6 h -6 -6 -6 v 6 h 6 6 6 6 v -6 -6 -6 -6 -6 -6 -6 -6 -6 -6 -6 z"
+ id="path2678" />
+ <rect
+ style="fill:#ffffcc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2680"
+ width="78"
+ height="54"
+ x="266"
+ y="130" />
+ <g
+ id="g2682"
+ transform="matrix(6,0,0,6,350,124)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path2684"
+ d="m -6.1094906,2.2138122 c -0.4746932,0.2235287 -0.7039488,0.4472004 -0.9583981,1.1549698 0,0 -0.2518508,0.9786182 0.1411573,2.077546 0.062649,0.1751776 0.3124074,0.5114979 0.3158367,0.5144027 0.1926119,0.1631971 0.3917544,0.2100055 0.5970346,0.2912278 0.00894,0.00354 0.017975,0.00648 0.026934,0.00996 0.072946,0.028296 0.1479664,0.055478 0.2222043,0.07965 0.142557,0.046424 0.2866583,0.083698 0.4331872,0.1145074 0.01716,0.0036 0.034416,0.00656 0.051623,0.00996 0.060943,0.012003 0.1225701,0.023118 0.1840486,0.032359 0.011921,0.00178 0.023972,0.00329 0.035912,0.00498 0.063183,0.00892 0.1271306,0.016436 0.1907817,0.022396 0.00674,6.321e-4 0.013453,0.00189 0.020201,0.0025 0.00599,5.117e-4 0.011965,-5.042e-4 0.017956,0 0.061497,0.00518 -0.4672144,-0.2495442 -0.4053701,-0.2471662 0.010565,4.064e-4 0.020849,0.00216 0.031423,0.00249 0.00966,2.784e-4 0.019514,-2.183e-4 0.029178,0 0.049372,0.00115 0.098601,6.622e-4 0.1481364,0 0.041816,-5.87e-4 0.1550997,0.4115523 0.197009,0.4096709 0.00743,-3.387e-4 0.015015,3.612e-4 0.022445,0 0.014204,-7.3e-4 0.028433,-0.00163 0.042645,-0.0025 0.050194,-0.00308 -0.4893136,-0.2670785 -0.4390367,-0.2720453 0.00968,-9.557e-4 0.019492,-0.00148 0.029178,-0.00249 0.018017,-0.00191 0.035844,-0.00531 0.053868,-0.00747 0.05324,-0.00637 0.1060924,-0.011401 0.1593584,-0.019912 0.00303,-4.892e-4 0.00595,-0.00201 0.00898,-0.00249 0.013375,-0.00218 0.027026,-0.00516 0.040401,-0.00747 0.057958,-0.010009 0.1171541,-0.019777 0.17507,-0.032367 0.00301,-6.472e-4 0.00596,-0.00183 0.00898,-0.00248 0.00747,-0.00165 0.014977,-0.0033 0.022445,-0.00498 0.066808,-0.015066 0.1331036,-0.031223 0.1997596,-0.049781 0.06885,-0.019182 0.1379025,-0.039275 0.2064933,-0.062235 0.00517,-0.00172 0.01054,-0.00322 0.015712,-0.00497 0.073802,-0.025015 0.1465824,-0.052686 0.2199601,-0.08214 0.071153,-0.028574 0.1419354,-0.060046 0.2109823,-0.092097 0.00217,-0.00102 0.00456,-0.00147 0.00673,-0.00249 0.029656,-0.013832 0.058276,-0.027859 0.087535,-0.042323 0.01847,-0.00924 0.035557,-0.020371 0.053868,-0.029861 0.030839,-0.015992 0.061639,-0.030666 0.092024,-0.047297 0.048425,-0.026512 0.3238784,-0.3777479 0.3949399,-0.5375661 0.4232235,-0.9518125 0.1007154,-2.0195096 0.1007154,-2.0195096 -0.2461525,-0.6612545 -0.406976,-0.891511 -0.9561532,-1.1549698 -0.6404429,-0.3072419 -1.4793156,-0.262029 -2.035754,0 z"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2686"
+ d="m -5.5625,5.84375 c -0.418409,-7.5e-6 -0.75,0.5079543 -0.75,1.15625 -4.21e-5,0.014893 0.00159,0.016568 0,0.03125 L -6.53125,9 H -3.5625 L -3.78125,7.0625 c -0.00164,-0.015104 -3.8e-6,-0.015943 0,-0.03125 7e-7,-0.6482958 -0.3315928,-1.15625 -0.75,-1.15625 z"
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path2688"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2690"
+ d="M -4.375,5.96875 -4.125,9 H -3 L -3.3125,7.15625 c -0.00233,-0.014863 -4.5e-6,-0.047427 0,-0.0625 -7e-7,-0.6381967 -0.4680008,-1.125 -1.0625,-1.125 z"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2692"
+ d="m -5.75,5.9375 c -0.5944991,-2e-7 -1.0625,0.4844178 -1.0625,1.125 4.7e-6,0.015126 0.00233,0.047577 0,0.0625 L -7.125,9 H -6 Z"
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(0.02975471,0,0,0.0383363,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 391.46325,333.5712 a 52.411282,52.411282 0 0 1 -31.32177,67.08044 52.411282,52.411282 0 0 1 -67.15463,-31.16238 52.411282,52.411282 0 0 1 31.00282,-67.22845 52.411282,52.411282 0 0 1 67.30188,30.84308"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path2694"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(0.02975471,0,0,0.02995385,-12.927769,-4.385165)"
+ id="path2696"
+ d="m 263.78125,241.90625 c -21.73945,0.98399 -36.85128,21.73579 -41.28125,41.375 -6.49305,26.5762 2.68626,58.98264 27.71875,72.4375 17.35487,8.69641 38.91459,-0.83382 48.15625,-16.90625 18.406,-27.61355 14.12909,-69.41512 -12.5625,-90.15625 -6.4176,-4.42182 -14.18297,-7.13873 -22.03125,-6.75 z"
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(0.07525459,0,0,0.07525459,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="m 198.40449,-30.85609 c 0.81789,-2.8937 0.32081,-1.1775 2.33145,-3.3993 1.8972,-2.0965 3.79548,-3.168 6.16049,-4.8311 0.39883,-0.2805 1.17513,-0.267 1.57464,-0.5465 2.55981,-1.7907 2.27479,0.0994 5.45044,-0.2814 2.54762,-0.3055 1.03231,-1.9691 3.3177,-1.0762 2.62559,1.0257 3.73303,1.7867 5.80816,3.6693 1.89214,1.7165 2.24127,1.7607 3.92721,3.7113 1.56394,1.8094 0.82239,-0.0968 2.03743,1.9369 1.01373,1.6966 4.58051,2.5198 4.93534,4.7084 0.2575,1.5882 -0.61744,2.005 -1.85423,1.1658 -4.08444,-2.7714 -7.90988,-5.5623 -12.2084,-8.0252 -2.9166,-1.6712 -4.25342,-2.295 -5.9927,0.6947 -1.73892,2.9891 -2.98982,6.493 -4.9614,9.3428 -3.29643,4.7649 -1.4494,-4.8585 -1.4494,-6.1327 0,-3.372 -2.34116,-1.2978 -4.15309,0 -1.68966,1.2102 -4.3947,2.5101 -6.43867,2.8028 -2.05176,0.2939 0.75127,-3.2977 -1.26795,-3.7166 -1.15282,-0.2391 0.88166,1.9658 1.25293,1.462 0.25022,-0.3396 1.21415,-1.2055 1.53005,-1.485 z"
+ id="path2698" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2700"
+ d="m -6.7368167,3.494555 c 0.06155,-0.2177642 0.21157,-0.4151043 0.3628799,-0.5823049 0.142773,-0.1577713 0.396758,-0.264249 0.5747359,-0.3894049 0.030014,-0.021109 0.060131,-0.042067 0.090196,-0.063101 0.1926375,-0.1347584 0.3693142,-0.2561817 0.6082964,-0.2848386 0.1917201,-0.02299 0.3461147,0.077031 0.5181008,0.1442254 0.1975877,0.077189 0.3850115,0.2152808 0.5411745,0.3569551 0.1423923,0.1291745 0.2800547,0.2588382 0.4069295,0.4056298 0.1176936,0.1361656 0.2043207,0.2940497 0.295758,0.447095 0.076288,0.1276769 0.1551389,0.3162198 0.090196,0.4579091 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 c -0.2480714,0.3585806 -0.109074,-0.3656244 -0.109074,-0.4615138 0,-0.2537585 -0.176183,-0.097665 -0.312539,0 -0.1271547,0.091073 -0.3307214,0.1888965 -0.4845395,0.2109236 -0.1544044,0.022117 -0.3241938,0.05316 -0.4761493,0.021636 -0.086755,-0.017993 -0.00487,-0.1730103 0.023073,-0.2109236 0.01883,-0.025556 0.047545,-0.042067 0.071318,-0.063101 z"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2702"
+ width="6"
+ height="6"
+ x="284"
+ y="178" />
+ <rect
+ y="178"
+ x="320"
+ height="6"
+ width="6"
+ id="rect2704"
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 272,142 v 6 h 6 6 6 v -6 h -6 -6 z"
+ id="path2706" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 272,154 v 6 h 6 6 6 v -6 h -6 -6 z"
+ id="path2708" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 272,166 v 6 h 6 6 6 v -6 h -6 -6 z"
+ id="path2710" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2785"
+ d="m 280,142 v 6 h 6 6 6 v -6 h -6 -6 z"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2787"
+ d="m 280,154 v 6 h 6 6 6 v -6 h -6 -6 z"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path2789"
+ d="m 280,166 v 6 h 6 6 6 v -6 h -6 -6 z"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/bookmarkproperties.svg b/comm/suite/branding/seamonkey/icons/svg/bookmarkproperties.svg
new file mode 100644
index 0000000000..de21091ff4
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/bookmarkproperties.svg
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="kaka.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs14561">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3119"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata14564">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-373.79172,-373.74402)"
+ id="g4261">
+ <g
+ id="g4263"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4265"
+ style="display:inline">
+ <g
+ id="g4267">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path4269"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4271"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient3119);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4273"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient3121);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4275"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path4277"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g4279"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="matrix(1.2522169,0,0,1.1949279,323.69463,-199.8516)"
+ id="g4281">
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="80.222176"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ id="path4283"
+ d="m -227.62113,184.74074 c -4.49411,-0.0954 -8.97042,2.374 -11.191,6.2799 -2.63845,4.36896 -2.07131,10.19335 1.18251,14.09932 l 6.45195,6.95613 6.28685,6.60414 6.28203,6.77764 6.46157,6.60911 6.28684,6.60412 6.28203,6.77766 6.28686,6.60411 6.46157,6.60911 -0.43407,-9.56308 -0.25451,-9.73165 9.59088,0.96781 9.59091,0.9678 -6.28683,-6.60412 -6.46158,-6.60911 -6.28205,-6.77765 -6.28682,-6.60412 -6.46159,-6.6091 -6.28202,-6.77765 -6.28686,-6.60414 -6.46158,-6.60911 c -1.7903,-1.65835 -4.1384,-2.72262 -6.55787,-3.13885 -0.63712,-0.1224 -1.2752,-0.21464 -1.91722,-0.22827 z"
+ style="fill:#8ac692;fill-opacity:1;fill-rule:evenodd;stroke:#2d692d;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="76.790672"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 0 1 -6.9448319,4.5172983 0.78960949,0.77599555 0 0 1 -7.9565593,4.0559115 0.78960949,0.77599555 0 0 1 -7.489482,3.0605348 0.78960949,0.77599555 0 0 1 -6.4755362,3.517194"
+ sodipodi:ry="0.77599555"
+ sodipodi:rx="0.78960949"
+ sodipodi:cy="3.788281"
+ sodipodi:cx="-7.2153969"
+ id="path4285"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ transform="translate(-466.06857,-708.64199)"
+ id="g2271">
+ <g
+ style="fill:none"
+ id="g4596"
+ transform="translate(92.276855,334.89797)">
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="Shadow"
+ id="g4598" />
+ <g
+ style="display:inline;fill:none"
+ id="g4600"
+ inkscape:label="Background">
+ <g
+ style="fill:none"
+ id="g4602">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4604"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path4606"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path4608"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path4610"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4612"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="SeaMonkey"
+ id="g4614" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 560.56525,744.36691 c 3.64655,-3.77487 13.96702,6.9505 10.52124,11.54964 -11.59808,15.38019 -42.24999,47.22625 -50.85403,49.97111 -2.6299,1.25133 -6.11355,2.04244 -9.4501,-0.75714 -10.13999,-8.30435 -18.13697,-19.25869 -17.77887,-21.95705 0.45909,-2.09241 7.07553,-9.65209 11.13187,-9.8428 1.9355,0.31425 5.00638,8.89847 8.72922,11.90775 1.53232,1.61531 3.99472,1.98345 6.16212,0.12937 14.4144,-10.31745 29.02856,-26.97258 41.53855,-41.00088 z"
+ id="path4616"
+ sodipodi:nodetypes="ccccccccc" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/downloadManager.svg b/comm/suite/branding/seamonkey/icons/svg/downloadManager.svg
new file mode 100644
index 0000000000..e05666aac5
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/downloadManager.svg
@@ -0,0 +1,313 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="downloadManagerScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs4997">
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2242"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2239"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3484"
+ id="radialGradient4215"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.0948682,-2.3267471,1.1043583,0.9943003,-2088.218,2560.8382)"
+ cx="1087.3295"
+ cy="756.81622"
+ fx="1087.3295"
+ fy="756.81622"
+ r="4.13275" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3484">
+ <stop
+ style="stop-color:#00a83e;stop-opacity:1"
+ offset="0"
+ id="stop3486" />
+ <stop
+ style="stop-color:#73cf95;stop-opacity:1"
+ offset="1"
+ id="stop3488" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3458"
+ id="linearGradient4217"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-62.48626,26.463358)"
+ x1="1086.8507"
+ y1="749.88818"
+ x2="1086.8507"
+ y2="754.21796" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3458">
+ <stop
+ style="stop-color:#009b35;stop-opacity:1"
+ offset="0"
+ id="stop3460" />
+ <stop
+ style="stop-color:#00561d;stop-opacity:1"
+ offset="1"
+ id="stop3462" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3526"
+ id="linearGradient4219"
+ gradientUnits="userSpaceOnUse"
+ x1="1021.7614"
+ y1="732.21332"
+ x2="1021.7063"
+ y2="725.86243" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3526">
+ <stop
+ style="stop-color:#00561d;stop-opacity:1;"
+ offset="0"
+ id="stop3528" />
+ <stop
+ style="stop-color:#00561d;stop-opacity:0;"
+ offset="1"
+ id="stop3530" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata5000">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g4113"
+ transform="translate(-373.79172,-373.74403)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="layer3" />
+ <g
+ style="display:inline"
+ id="g4116"
+ inkscape:label="Background">
+ <g
+ id="g2219">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6573"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path2064"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path2056"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path2058"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2052"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="layer2" />
+ </g>
+ <g
+ transform="matrix(2.8571604,0,0,2.8571604,-2863.2797,-2029.3264)"
+ id="g4181">
+ <rect
+ y="722.69794"
+ x="1011.0417"
+ height="17.999828"
+ width="26.999828"
+ id="rect4183"
+ style="opacity:1;fill:#f3f4f5;fill-opacity:1;fill-rule:nonzero;stroke:#3b556e;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ y="727.71289"
+ x="1015.0414"
+ height="4.0004034"
+ width="19.000404"
+ id="rect4185"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#00561d;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc"
+ id="path4187"
+ d="m 1016.0339,727.75491 v 3.82169 z"
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 1018.028,727.7436 v 3.82169 z"
+ id="path4189"
+ sodipodi:nodetypes="ccc" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc"
+ id="path4191"
+ d="m 1020.017,727.73673 v 3.82169 z"
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 1022.0115,727.60918 v 3.82169 z"
+ id="path4193"
+ sodipodi:nodetypes="ccc" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc"
+ id="path4195"
+ d="m 1023.9948,727.6158 v 3.82169 z"
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#73c890;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4197"
+ width="19.000404"
+ height="4.0004034"
+ x="1015.0414"
+ y="727.71289" />
+ <g
+ transform="translate(0.1771012,-38.795917)"
+ id="g4199">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4201"
+ d="m 1021.0703,782.19928 0.037,-5.6165 6.5797,-0.11554 -0.01,5.86042 1.1725,0.0899 -4.4735,4.7307 -4.5408,-4.64725 z"
+ style="fill:#6ec527;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:url(#radialGradient4215);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 1021.0703,782.19928 0.037,-5.6165 6.5797,-0.11554 0.1994,5.96205 -4.1978,4.09216 -3.8509,-4.02048 z"
+ id="path4203" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4205"
+ d="m 1020.8642,781.38404 v -5.0002 h 7.0004 v 5.0002"
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4217);stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4207"
+ d="m 1027.8606,782.31893 h 1.4534 l -4.9495,4.94951 -4.9495,-4.94947 1.4469,-1e-5"
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#00561d;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <rect
+ y="727.71289"
+ x="1015.0414"
+ height="4.0004034"
+ width="19.000404"
+ id="rect4209"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4219);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/editorWindow.svg b/comm/suite/branding/seamonkey/icons/svg/editorWindow.svg
new file mode 100644
index 0000000000..7bceab2fda
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/editorWindow.svg
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="editorWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs5613">
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2242"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2239"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata5616">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-157.06859,-245.97515)"
+ id="g2435">
+ <g
+ transform="translate(-216.72313,-127.76887)"
+ id="g2405">
+ <g
+ id="layer3"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g2408"
+ style="display:inline">
+ <g
+ id="g2219">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path6573"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2064"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2056"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2058"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path2052"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="layer2"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ id="g2417"
+ transform="matrix(3.5766367,0,0,3.5766367,277.69915,379.568)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path2419"
+ d="m -25.69054,-11.447732 5.26078,-14.094435 11.986099,2.169649 -2.158268,14.9308247 z"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path2421"
+ d="m -19.730935,-21.959623 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135604 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749132,0.135603 v 0 l -0.749132,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749132,-0.135603 -0.749131,-0.135604 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 z"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="m -20.458605,-19.575327 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135604 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749132,0.135603 v 0 l -0.749132,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749132,-0.135603 -0.749131,-0.135604 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 z"
+ id="path2423"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a2c2de;fill-opacity:1;fill-rule:nonzero;stroke:#2271bc;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -8.450037,-28.245275 c -0.970358,2.006581 -2.166317,3.931798 -3.682122,5.54764 -0.509248,0.500769 -1.058333,1.066037 -1.750015,1.205493 -0.54566,0.08346 -0.674001,-0.592426 -0.618549,-1.039912 0.14582,-1.41906 0.759026,-2.759364 1.381823,-4.040375 0.969709,-1.884697 2.125466,-3.705249 3.599541,-5.209737 0.487262,-0.450156 1.023899,-0.992482 1.682798,-1.04552 0.544816,0.02662 0.56687,0.736752 0.511403,1.188274 -0.148152,1.163095 -0.610945,2.274243 -1.099996,3.341338"
+ id="path2425" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="csssccscc"
+ id="path2427"
+ d="m -12.873502,-21.508276 c -0.502412,0.518158 -1.042444,0.838596 -1.440354,1.012263 -0.16156,0.07051 -0.309507,0.278033 -0.583527,0.15493 -0.23579,-0.110547 -0.43802,-0.285099 -0.47272,-0.446272 -0.08713,-0.404734 0.05555,-1.280998 0.28909,-1.877028 0.1616,-0.405262 0.402886,-1.013441 0.669696,-1.443413 0.72003,-0.06238 1.343294,0.143983 1.518734,0.287907 0.4558,0.373918 0.798955,0.757496 0.921345,1.309524 -0.450913,0.528581 -0.644945,0.729805 -0.902264,1.002089 z"
+ style="fill:#000000;fill-opacity:0.99215686;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccssccsscc"
+ id="path2429"
+ d="m -13.278907,-20.408924 c -0.108123,0.590226 -0.29482,1.26275 -0.801275,2.021917 -0.121028,0.156629 -1.032398,-0.375324 -2.14733,1.128838 -0.284721,0.384119 1.547051,-3.350132 1.410256,-3.433094 -0.136663,-0.09258 -1.544388,3.547818 -1.514129,3.418801 0.319782,-1.363461 -0.57915,-1.934836 -0.532114,-2.159478 0.206373,-0.874762 0.607095,-1.658897 1.188459,-2.323433 0.300451,-0.326124 0.634229,-0.639981 1.127534,-0.81581 0.195489,-0.05819 0.434537,-0.117957 0.736353,0.0054 0.275031,0.126518 0.410178,0.362893 0.483058,0.544883 0.170925,0.463474 0.17481,0.968007 0.04919,1.612022 z"
+ style="fill:#c5c5c5;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path2431"
+ d="m -21.129721,-16.757843 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135604 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749132,0.135603 v 0 l -0.749132,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749132,-0.135603 -0.749131,-0.135604 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 z"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="m -22.000837,-14.036738 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135604 0.749132,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749131,0.135603 0.749132,0.135603 v 0 l -0.749132,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749132,-0.135603 -0.749131,-0.135604 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 -0.749131,-0.135603 z"
+ id="path2433"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/findBookmarkWindow.svg b/comm/suite/branding/seamonkey/icons/svg/findBookmarkWindow.svg
new file mode 100644
index 0000000000..13aa2bbaac
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/findBookmarkWindow.svg
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="findBookmarkWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs6452">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3119"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata6455">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-373.79163,-373.74402)"
+ id="g4261">
+ <g
+ id="g4263"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4265"
+ style="display:inline">
+ <g
+ id="g4267">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path4269"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4271"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient3119);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4273"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient3121);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4275"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path4277"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g4279"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="matrix(1.2522169,0,0,1.1949279,319.59463,-195.9516)"
+ id="g4281-0">
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="80.222176"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ id="path4283-0"
+ d="m -227.62113,184.74074 c -4.49411,-0.0954 -8.97042,2.374 -11.191,6.2799 -2.63845,4.36896 -2.07131,10.19335 1.18251,14.09932 l 6.45195,6.95613 6.28685,6.60414 6.28203,6.77764 6.46157,6.60911 6.28684,6.60412 6.28203,6.77766 6.28686,6.60411 6.46157,6.60911 -0.43407,-9.56308 -0.25451,-9.73165 9.59088,0.96781 9.59091,0.9678 -6.28683,-6.60412 -6.46158,-6.60911 -6.28205,-6.77765 -6.28682,-6.60412 -6.46159,-6.6091 -6.28202,-6.77765 -6.28686,-6.60414 -6.46158,-6.60911 c -1.7903,-1.65835 -4.1384,-2.72262 -6.55787,-3.13885 -0.63712,-0.1224 -1.2752,-0.21464 -1.91722,-0.22827 z"
+ style="fill:#8ac692;fill-opacity:1;fill-rule:evenodd;stroke:#2d692d;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="76.790672"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 0 1 -6.9448319,4.5172983 0.78960949,0.77599555 0 0 1 -7.9565593,4.0559115 0.78960949,0.77599555 0 0 1 -7.489482,3.0605348 0.78960949,0.77599555 0 0 1 -6.4755362,3.517194"
+ sodipodi:ry="0.77599555"
+ sodipodi:rx="0.78960949"
+ sodipodi:cy="3.788281"
+ sodipodi:cx="-7.2153969"
+ id="path4285-5"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g3129-6"
+ transform="matrix(-4.8274668,0,0,4.8274668,164.49732,-96.183626)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 22.484347,32.246116 c 3.080132,2.515494 5.769185,6.012023 6.002351,7.804753 0.233164,1.792726 -2.077419,1.206135 -5.157554,-1.30936 -3.08013,-2.515492 -5.769183,-6.012022 -6.00235,-7.804751 -0.231754,-1.781886 2.0454,-1.217025 5.104569,1.266219"
+ id="path3131-8" />
+ <path
+ transform="matrix(1.0443307,0,0,1.0442899,31.33439,24.634086)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m -10.649134,2.2285586 a 4.4278188,4.4278188 0 0 1 -2.646131,5.6671008 4.4278188,4.4278188 0 0 1 -5.673369,-2.6326657 4.4278188,4.4278188 0 0 1 2.619186,-5.67960464 4.4278188,4.4278188 0 0 1 5.685808,2.60569044"
+ sodipodi:ry="4.4278188"
+ sodipodi:rx="4.4278188"
+ sodipodi:cy="3.7358978"
+ sodipodi:cx="-14.812487"
+ id="path3133-0"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/findHistoryWindow.svg b/comm/suite/branding/seamonkey/icons/svg/findHistoryWindow.svg
new file mode 100644
index 0000000000..fe43d3fba4
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/findHistoryWindow.svg
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="findHistoryWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs7852">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3308"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter4288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="8.1055212"
+ id="feGaussianBlur4290" />
+ </filter>
+ </defs>
+ <metadata
+ id="metadata7855">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g3212"
+ transform="translate(-225.00117,-55.438896)">
+ <g
+ transform="translate(-148.79055,-318.30513)"
+ id="g4024">
+ <g
+ id="g4026"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4028"
+ style="display:inline">
+ <g
+ id="g4030">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path4032"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4034"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient3308);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4036"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient3311);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4038"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path4040"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g4042"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="translate(-153.84722,-21.867746)"
+ id="g4044">
+ <path
+ transform="matrix(0.2477023,0,0,0.2079683,648.47198,64.124139)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m -661.87975,209.38359 a 127.14286,151.42857 0 0 1 -75.98248,193.81123 127.14286,151.42857 0 0 1 -162.90828,-90.03549 127.14286,151.42857 0 0 1 75.20875,-194.23885 127.14286,151.42857 0 0 1 163.26548,89.11295"
+ sodipodi:ry="151.42857"
+ sodipodi:rx="127.14286"
+ sodipodi:cy="260.93362"
+ sodipodi:cx="-781.42859"
+ id="path4046"
+ style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <text
+ id="text4048"
+ y="93.797005"
+ x="451.29788"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="93.797005"
+ x="451.29788"
+ id="tspan4050"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">XII</tspan></text>
+ <text
+ id="text4052"
+ y="97.519417"
+ x="466.95267"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="466.95267"
+ id="tspan4054"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">I</tspan></text>
+ <text
+ id="text4056"
+ y="106.09559"
+ x="475.9686"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="106.09559"
+ x="475.9686"
+ id="tspan4058"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">II</tspan></text>
+ <text
+ id="text4060"
+ y="119.60712"
+ x="479.48703"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="119.60712"
+ x="479.48703"
+ id="tspan4062"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">III</tspan></text>
+ <text
+ id="text4064"
+ y="133.25336"
+ x="475.19901"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="133.25336"
+ x="475.19901"
+ id="tspan4066"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">IV</tspan></text>
+ <text
+ id="text4068"
+ y="142.88013"
+ x="466.40292"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="142.88013"
+ x="466.40292"
+ id="tspan4070"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">V</tspan></text>
+ <text
+ id="text4072"
+ y="146.44739"
+ x="451.66959"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="146.44739"
+ x="451.66959"
+ id="tspan4074"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">VI</tspan></text>
+ <text
+ id="text4076"
+ y="143.14886"
+ x="437.59592"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="143.14886"
+ x="437.59592"
+ id="tspan4078"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">VII</tspan></text>
+ <text
+ id="text4080"
+ y="133.09454"
+ x="428.68991"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="133.09454"
+ x="428.68991"
+ id="tspan4082"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">VIII</tspan></text>
+ <text
+ id="text4084"
+ y="119.50953"
+ x="426.38095"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="119.50953"
+ x="426.38095"
+ id="tspan4086"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">IX</tspan></text>
+ <text
+ id="text4088"
+ y="105.9856"
+ x="430.44913"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="105.9856"
+ x="430.44913"
+ id="tspan4090"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">X</tspan></text>
+ <text
+ id="text4092"
+ y="97.519417"
+ x="439.02524"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="439.02524"
+ id="tspan4094"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">XI</tspan></text>
+ <g
+ transform="matrix(0.5000001,0,0,0.5000001,64.050849,-145.21778)"
+ id="g4096">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 779.71875,502 v 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 h 4 V 525 522.125 519.25 516.375 513.5 510.625 507.75 504.875 502 Z"
+ id="path4098"
+ sodipodi:nodetypes="ccccccccccccccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m 781.71875,496.68239 2.61671,5.23343 h -5.23342 z"
+ id="path4100"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ transform="matrix(-0.4291897,-0.256508,0.256508,-0.4291897,652.78758,549.26748)"
+ id="g4102">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path4104"
+ d="m 779.71875,502 v 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 h 4 V 533.625 530.75 527.875 525 522.125 519.25 516.375 513.5 510.625 507.75 504.875 502 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc"
+ id="path4106"
+ d="m 781.71875,496.68239 2.61671,5.23343 h -5.23342 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <path
+ transform="matrix(0.5000001,0,0,0.5000001,67.279699,-148.03415)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 778.6727,531.61082 a 3.6283669,3.6283669 0 0 1 -2.16837,4.6439 3.6283669,3.6283669 0 0 1 -4.64903,-2.15734 3.6283669,3.6283669 0 0 1 2.14629,-4.65414 3.6283669,3.6283669 0 0 1 4.65922,2.13523"
+ sodipodi:ry="3.6283669"
+ sodipodi:rx="3.6283669"
+ sodipodi:cy="532.84601"
+ sodipodi:cx="775.26105"
+ id="path4108"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="3.7992489"
+ inkscape:original="M -784.625 119.21875 C -823.30724 120.2578 -857.85589 145.69751 -876.65625 178.4375 C -911.16585 236.5006 -905.33598 317.22678 -859.21875 367.5 C -835.95661 393.00489 -800.00341 407.76292 -765.34375 401.375 C -725.99105 394.88795 -694.34644 364.38152 -678.625 328.90625 C -652.69932 272.153 -661.28225 199.33022 -704.71875 153.46875 C -725.24632 131.86038 -754.52802 118.0423 -784.625 119.21875 z "
+ style="fill:#ffffff;fill-opacity:0.23602486;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4288)"
+ id="path4110"
+ d="m -784.75977,115.42188 c -40.2611,1.09547 -75.79772,27.37379 -95.17382,61.09765 -35.37906,59.54928 -29.45794,141.89836 17.9082,193.54102 0.002,0.002 0.006,0.003 0.008,0.006 3.4e-4,3.7e-4 -3.4e-4,0.002 0,0.002 24.07235,26.38799 61.18486,41.68739 97.31055,35.05078 40.90102,-6.75069 73.38739,-38.20666 89.54687,-74.6582 26.55489,-58.15536 17.83876,-132.4738 -26.80078,-179.60547 a 3.7996289,3.7996289 0 0 0 -0.004,-0.004 c -21.21,-22.32673 -51.44654,-36.65052 -82.79493,-35.42968 z"
+ transform="matrix(0.2477023,0,0,0.2079683,648.46968,64.107569)" />
+ </g>
+ <g
+ transform="matrix(4.13789,0,0,4.13789,186.93117,5.0293239)"
+ id="g4112">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4114"
+ d="m 22.484347,32.246116 c 3.080132,2.515494 5.769185,6.012023 6.002351,7.804753 0.233164,1.792726 -2.077419,1.206135 -5.157554,-1.30936 -3.08013,-2.515492 -5.769183,-6.012022 -6.00235,-7.804751 -0.231754,-1.781886 2.0454,-1.217025 5.104569,1.266219"
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4116"
+ sodipodi:cx="-14.812487"
+ sodipodi:cy="3.7358978"
+ sodipodi:rx="4.4278188"
+ sodipodi:ry="4.4278188"
+ d="m -10.649134,2.2285586 a 4.4278188,4.4278188 0 0 1 -2.646131,5.6671008 4.4278188,4.4278188 0 0 1 -5.673369,-2.6326657 4.4278188,4.4278188 0 0 1 2.619186,-5.67960464 4.4278188,4.4278188 0 0 1 5.685808,2.60569044"
+ sodipodi:start="5.9358167"
+ sodipodi:end="5.9263177"
+ sodipodi:open="true"
+ transform="matrix(1.0443307,0,0,1.0442899,31.33439,24.634086)" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/history-window.svg b/comm/suite/branding/seamonkey/icons/svg/history-window.svg
new file mode 100644
index 0000000000..fcfd2d61ef
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/history-window.svg
@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="history-windowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs9553">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter4288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="8.1055212"
+ id="feGaussianBlur4290" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3152"
+ id="linearGradient3885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.4893755,0,0,2.4893755,754.50881,-27.065893)"
+ spreadMethod="pad"
+ x1="-136.54431"
+ y1="58.386353"
+ x2="-130.13719"
+ y2="78.847557" />
+ <linearGradient
+ id="linearGradient3152"
+ inkscape:collect="always">
+ <stop
+ id="stop3154"
+ offset="0"
+ style="stop-color:#63ba0f;stop-opacity:1;" />
+ <stop
+ id="stop3156"
+ offset="1"
+ style="stop-color:#509e07;stop-opacity:1" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata9556">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g3261"
+ transform="translate(-390.91039,-54.388836)">
+ <g
+ transform="translate(17.118669,-319.35519)"
+ id="g3787">
+ <g
+ id="g3789"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g3791"
+ style="display:inline">
+ <g
+ id="g3793">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path3795"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path3797"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ style="fill:url(#linearGradient3873);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path3799"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ style="fill:url(#linearGradient3875);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path3801"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ id="path3803"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g3805"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="translate(12.062002,-22.917805)"
+ id="g3887">
+ <path
+ transform="matrix(0.2477023,0,0,0.2079683,648.47198,64.124139)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m -661.87975,209.38359 a 127.14286,151.42857 0 0 1 -75.98248,193.81123 127.14286,151.42857 0 0 1 -162.90828,-90.03549 127.14286,151.42857 0 0 1 75.20875,-194.23885 127.14286,151.42857 0 0 1 163.26548,89.11295"
+ sodipodi:ry="151.42857"
+ sodipodi:rx="127.14286"
+ sodipodi:cy="260.93362"
+ sodipodi:cx="-781.42859"
+ id="path3807"
+ style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <text
+ id="text3809"
+ y="93.797005"
+ x="451.29788"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="93.797005"
+ x="451.29788"
+ id="tspan3811"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">XII</tspan></text>
+ <text
+ id="text3813"
+ y="97.519417"
+ x="466.95267"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="466.95267"
+ id="tspan3815"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">I</tspan></text>
+ <text
+ id="text3817"
+ y="106.09559"
+ x="475.9686"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="106.09559"
+ x="475.9686"
+ id="tspan3819"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">II</tspan></text>
+ <text
+ id="text3821"
+ y="119.60712"
+ x="479.48703"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="119.60712"
+ x="479.48703"
+ id="tspan3823"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">III</tspan></text>
+ <text
+ id="text3825"
+ y="133.25336"
+ x="475.19901"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="133.25336"
+ x="475.19901"
+ id="tspan3827"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">IV</tspan></text>
+ <text
+ id="text3829"
+ y="142.88013"
+ x="466.40292"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="142.88013"
+ x="466.40292"
+ id="tspan3831"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">V</tspan></text>
+ <text
+ id="text3833"
+ y="146.44739"
+ x="451.66959"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="146.44739"
+ x="451.66959"
+ id="tspan3835"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">VI</tspan></text>
+ <text
+ id="text3837"
+ y="143.14886"
+ x="437.59592"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="143.14886"
+ x="437.59592"
+ id="tspan3839"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">VII</tspan></text>
+ <text
+ id="text3841"
+ y="133.09454"
+ x="428.68991"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="133.09454"
+ x="428.68991"
+ id="tspan3843"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">VIII</tspan></text>
+ <text
+ id="text3845"
+ y="119.50953"
+ x="426.38095"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="119.50953"
+ x="426.38095"
+ id="tspan3847"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">IX</tspan></text>
+ <text
+ id="text3849"
+ y="105.9856"
+ x="430.44913"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="105.9856"
+ x="430.44913"
+ id="tspan3851"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">X</tspan></text>
+ <text
+ id="text3853"
+ y="97.519417"
+ x="439.02524"
+ style="font-style:normal;font-weight:bold;line-height:0%;font-family:'Trebuchet MS';writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="439.02524"
+ id="tspan3855"
+ sodipodi:role="line"
+ style="font-size:5.00000095px;line-height:1.25">XI</tspan></text>
+ <g
+ transform="matrix(0.5000001,0,0,0.5000001,64.050849,-145.21778)"
+ id="g3857">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 779.71875,502 v 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 h 4 V 525 522.125 519.25 516.375 513.5 510.625 507.75 504.875 502 Z"
+ id="path3859"
+ sodipodi:nodetypes="ccccccccccccccccccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="m 781.71875,496.68239 2.61671,5.23343 h -5.23342 z"
+ id="path3861"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ transform="matrix(-0.4291897,-0.256508,0.256508,-0.4291897,652.78758,549.26748)"
+ id="g3863">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path3865"
+ d="m 779.71875,502 v 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 2.875 h 4 V 533.625 530.75 527.875 525 522.125 519.25 516.375 513.5 510.625 507.75 504.875 502 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc"
+ id="path3867"
+ d="m 781.71875,496.68239 2.61671,5.23343 h -5.23342 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <path
+ transform="matrix(0.5000001,0,0,0.5000001,67.279699,-148.03415)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="m 778.6727,531.61082 a 3.6283669,3.6283669 0 0 1 -2.16837,4.6439 3.6283669,3.6283669 0 0 1 -4.64903,-2.15734 3.6283669,3.6283669 0 0 1 2.14629,-4.65414 3.6283669,3.6283669 0 0 1 4.65922,2.13523"
+ sodipodi:ry="3.6283669"
+ sodipodi:rx="3.6283669"
+ sodipodi:cy="532.84601"
+ sodipodi:cx="775.26105"
+ id="path3869"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="3.7992489"
+ inkscape:original="M -784.625 119.21875 C -823.30724 120.2578 -857.85589 145.69751 -876.65625 178.4375 C -911.16585 236.5006 -905.33598 317.22678 -859.21875 367.5 C -835.95661 393.00489 -800.00341 407.76292 -765.34375 401.375 C -725.99105 394.88795 -694.34644 364.38152 -678.625 328.90625 C -652.69932 272.153 -661.28225 199.33022 -704.71875 153.46875 C -725.24632 131.86038 -754.52802 118.0423 -784.625 119.21875 z "
+ style="fill:#ffffff;fill-opacity:0.23602486;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4288)"
+ id="path3871"
+ d="m -784.75977,115.42188 c -40.2611,1.09547 -75.79772,27.37379 -95.17382,61.09765 -35.37906,59.54928 -29.45794,141.89836 17.9082,193.54102 0.002,0.002 0.006,0.003 0.008,0.006 3.4e-4,3.7e-4 -3.4e-4,0.002 0,0.002 24.07235,26.38799 61.18486,41.68739 97.31055,35.05078 40.90102,-6.75069 73.38739,-38.20666 89.54687,-74.6582 26.55489,-58.15536 17.83876,-132.4738 -26.80078,-179.60547 a 3.7996289,3.7996289 0 0 0 -0.004,-0.004 c -21.21,-22.32673 -51.44654,-36.65052 -82.79493,-35.42968 z"
+ transform="matrix(0.2477023,0,0,0.2079683,648.46968,64.107569)" />
+ </g>
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccccccccsc"
+ id="path3883"
+ d="m 404.19444,135.98197 24.79232,-24.32202 c 3.13106,-2.55685 11.83238,7.75331 8.38038,11.39109 l -7.10342,7.3016 c -1.18415,1.22356 -0.20921,2.58737 2.51053,2.50551 l 26.97005,0.24521 c 5.00448,0.28701 4.38868,15.11016 0.10407,15.01699 l -27.06073,-0.13418 c -2.95307,-0.0983 -3.38304,1.50804 -2.53262,2.2944 l 7.00805,7.6908 c 3.09206,2.62399 -5.82774,12.40827 -7.80957,10.55959 L 404.47801,143.9352 c -1.63098,-1.38486 -1.7996,-2.78055 -1.82857,-3.79858 -0.0268,-0.94281 0.0817,-2.60794 1.545,-4.15465 z"
+ style="fill:url(#linearGradient3885);fill-opacity:1;fill-rule:evenodd;stroke:#3f7b07;stroke-width:2.48937559px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/messengerWindow.svg b/comm/suite/branding/seamonkey/icons/svg/messengerWindow.svg
new file mode 100644
index 0000000000..e2f5a3710f
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/messengerWindow.svg
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="messengerWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs548">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-220.53892,-160.04912)"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3278">
+ <stop
+ style="stop-color:#7aa1ca;stop-opacity:1;"
+ offset="0"
+ id="stop3280" />
+ <stop
+ style="stop-color:#3c75b2;stop-opacity:1"
+ offset="1"
+ id="stop3282" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,152.57359,197.13637)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3495">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6546695"
+ id="feGaussianBlur3497" />
+ </filter>
+ </defs>
+ <metadata
+ id="metadata551">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g2381"
+ transform="translate(-153.25271,-213.69491)">
+ <circle
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6573"
+ transform="matrix(0.1596245,0,0,0.1596247,158.95416,208.5661)"
+ cx="365.22308"
+ cy="433.07086"
+ r="395.53806" />
+ <path
+ style="fill:url(#linearGradient2346);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 163.31785,310.28568 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path2064"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:url(#linearGradient2343);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 155.25682,263.1413 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path2056"
+ sodipodi:nodetypes="cssssssssssscsss"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 154.11364,269.98938 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path2058"
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ inkscape:connector-curvature="0" />
+ <circle
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2052"
+ transform="matrix(0.1596245,0,0,0.1596247,158.95416,208.5661)"
+ cx="365.22308"
+ cy="433.07086"
+ r="395.53806" />
+ <g
+ transform="translate(3.3772247,7.0388147)"
+ id="g2373">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ d="m 211.32356,128.37745 c 1.33355,-0.68632 17.24041,1.35319 17.85159,3.07786 0.87206,1.13542 1.20617,10.94739 0.41038,11.69588 -1.00211,1.03148 -22.13027,-0.99964 -23.18655,-2.87267 -0.78052,-1.12703 2.65776,-10.31084 4.92458,-11.90107 z"
+ id="path2458"
+ sodipodi:nodetypes="ccccc"
+ transform="matrix(3.3771096,0,0,3.3771096,-522.87836,-188.0747)"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2460"
+ d="m 214.85909,134.27001 c 1.33355,-0.68632 3.45183,-1.12168 4.06302,0.60299 3.22908,0.1926 11.45921,7.5297 10.66342,8.27819 -1.00211,1.03148 -22.13027,-0.99964 -23.18655,-2.87267 -0.78052,-1.12703 4.89693,-5.95035 8.46011,-6.00851 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ transform="matrix(3.3771096,0,0,3.3771096,-522.87836,-188.0747)"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2466"
+ d="m 211.32356,128.37745 c 1.33355,-0.68632 17.24041,1.35319 17.85159,3.07786 -0.4243,2.31393 -3.74358,4.22987 -5.95358,4.86051 -3.12344,0.44223 -7.98814,-0.52823 -9.28012,-1.69416 -1.25193,-0.89132 -3.9419,-4.06472 -2.61789,-6.24421 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ transform="matrix(3.3771096,0,0,3.3771096,-522.87836,-188.0747)"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2231"
+ d="m 190.78446,245.47002 c 4.50355,-2.31778 58.22276,4.56987 60.28678,10.39427 2.94504,3.83444 4.07337,36.97054 1.3859,39.49827 -3.38424,3.48342 -74.73635,-3.3759 -78.30352,-9.70132 -2.6359,-3.80611 8.97554,-34.82084 16.63084,-40.19122 z"
+ style="fill:#eee5dd;fill-opacity:1;fill-rule:evenodd;stroke:#94806f;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#f3ebe4;fill-opacity:1;fill-rule:evenodd;stroke:#937f6d;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.95686275"
+ d="m 202.72434,265.36984 c 4.50354,-2.31778 11.6572,-3.78804 13.72126,2.03636 10.90496,0.65043 38.69901,25.42863 36.01154,27.95636 -3.38424,3.48342 -74.73635,-3.3759 -78.30352,-9.70132 -2.6359,-3.80611 16.53747,-20.09499 28.57072,-20.2914 z"
+ id="path2233"
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#f7f0e9;fill-opacity:1;fill-rule:evenodd;stroke:#9e8b7a;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 190.78446,245.47002 c 4.50355,-2.31778 58.22276,4.56987 60.28678,10.39427 -1.43291,7.81439 -12.64248,14.28473 -20.10589,16.41447 -10.5482,1.49346 -26.97683,-1.78389 -31.33999,-5.72136 -4.2279,-3.01008 -13.31222,-13.727 -8.8409,-21.08738 z"
+ id="path2235"
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/msgcomposeWindow.svg b/comm/suite/branding/seamonkey/icons/svg/msgcomposeWindow.svg
new file mode 100644
index 0000000000..15f4829611
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/msgcomposeWindow.svg
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="msgcomposeWindowScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs10517">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3807"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3809"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata10520">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-23.854982,-360.7796)"
+ id="g3828">
+ <g
+ id="g3773"
+ transform="translate(-349.93665,-12.96442)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g3775" />
+ <g
+ style="display:inline"
+ id="g3777"
+ inkscape:label="Background">
+ <g
+ id="g3779">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3781"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3807);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path3783"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3809);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path3785"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path3787"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3789"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g3791" />
+ </g>
+ <g
+ transform="translate(360.48635,6.9897052)"
+ id="g3766">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#a2c2de;fill-opacity:1;fill-rule:nonzero;stroke:#2271bc;stroke-width:4.58545876;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -271.625,356.1875 c -2.90582,9.8256 -5.05638,19.92272 -6.6875,30.03125 -1.7881,12.93993 -3.32569,26.36876 -0.5625,39.15625 0.78878,4.0593 3.82336,9.59154 8.34375,7.3125 5.63431,-3.1825 8.82736,-9.65466 11.84375,-15.46875 7.87605,-16.80589 12.68773,-35.11494 15.5625,-53.59375 -8.52003,-4.56992 -18.21024,-7.23175 -28.5,-7.4375 z"
+ id="path3715" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:0.99215686;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="m -261.76711,429.68955 c -2.90735,5.94628 -6.70487,10.2756 -9.68051,12.92115 -1.20817,1.07414 -1.90814,3.30416 -4.64734,3.00991 -2.37016,-0.29349 -4.62971,-1.24045 -5.39049,-2.54708 -1.91042,-3.28116 -3.15773,-11.32704 -2.81608,-17.18785 0.25677,-3.99297 0.63141,-9.98173 1.73525,-14.48928 6.10064,-2.59108 12.12166,-2.56379 14.06052,-1.80771 5.03728,1.9643 9.11984,4.3331 11.75646,8.79829 -2.4287,5.89075 -3.54836,8.19691 -5.01781,11.30257 z"
+ id="path3717"
+ sodipodi:nodetypes="csssccscc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#c5c5c5;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.29272938;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m -262.17624,440.4275 c 0.73539,5.45364 1.01965,11.84823 -1.23774,19.9074 -0.60993,1.70975 -10.06866,-0.33721 -15.51324,15.9476 -1.39041,4.15866 3.96387,-33.60855 2.53529,-33.94296 -1.45482,-0.41869 -3.37864,35.32462 -3.48161,34.11367 -1.08835,-12.79731 -10.5507,-15.22336 -10.77928,-17.31576 -0.68771,-8.21387 0.5768,-16.1901 3.75638,-23.63716 1.69242,-3.69771 3.7103,-7.38325 7.51158,-10.31885 1.53902,-1.06317 3.45338,-2.26393 6.43551,-2.04687 2.75774,0.32116 4.60814,1.99787 5.76103,3.37746 2.80801,3.55508 4.27637,7.94311 5.01208,13.91547 z"
+ id="path3719"
+ sodipodi:nodetypes="cccssccsscc" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/places.svg b/comm/suite/branding/seamonkey/icons/svg/places.svg
new file mode 100644
index 0000000000..8f7d83db53
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/places.svg
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128px"
+ height="128px"
+ viewBox="0 0 128 128"
+ version="1.1"
+ id="SVGRoot"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+ sodipodi:docname="placesScalable.svg">
+<!--
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+-->
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="64"
+ inkscape:cy="64"
+ inkscape:document-units="px"
+ inkscape:current-layer="g3417"
+ showgrid="false"
+ inkscape:window-width="1786"
+ inkscape:window-height="1021"
+ inkscape:window-x="47"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:grid-bbox="true" />
+ <defs
+ id="defs11396">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3119"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ </defs>
+ <metadata
+ id="metadata11399">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ transform="translate(-126.00734,-307.7809)"
+ id="g3417">
+ <g
+ id="g4261"
+ transform="translate(-247.78438,-65.963114)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g4263" />
+ <g
+ style="display:inline"
+ id="g4265"
+ inkscape:label="Background">
+ <g
+ id="g4267">
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4269"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3119);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 383.85677,470.3348 c -6.32473,-12.34953 -8.05387,-20.49647 -9.52788,-32.28418 3.81257,-0.10939 4.90008,-2.24285 31.45589,3.47223 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 13.05993,-0.67788 15.04445,-1.97661 1.98739,-0.88215 15.99863,-3.10467 21.11682,-3.66075 3.18189,-0.47001 4.50887,-1.12735 7.73783,-1.15937 11.35612,-5.35068 24.3651,-3.5214 29.52452,-2.34558 -0.1077,4.34882 -0.64329,10.45278 -1.44535,13.25753 -0.83486,3.95125 -1.25505,6.04479 -3.20764,9.5936 -0.86304,4.07813 -2.79707,6.71702 -5.82995,11.62656 -3.16637,4.95692 -8.85713,10.12649 -15.35852,15.5514 -4.35459,5.8898 -19.72031,9.02775 -22.94775,10.88715 -8.55892,2.31056 -17.1425,3.16792 -32.16414,-0.86687 -16.64504,-3.63918 -29.62612,-18.61585 -36.37142,-27.7713 z"
+ id="path4271"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3121);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 375.79574,423.19042 c 0.6654,-2.66163 2.25909,-8.13069 3.89624,-10.31356 2.73624,-3.64834 6.99051,-12.21676 10.08436,-16.5017 4.52016,-6.26037 18.25337,-14.60235 24.29417,-17.41846 9.3217,-4.34562 32.45573,-6.36135 45.37965,-0.45838 5.54058,2.53065 16.26536,8.07359 20.62713,12.60546 4.06502,4.22358 10.5201,11.73492 13.7514,18.33522 1.74906,3.57267 7.16364,17.7727 6.41732,17.87684 -4.71335,0.65767 -9.56876,2.38248 -14.43898,3.89623 -3.42766,1.06538 -9.31841,3.89624 -10.08436,3.89624 -3.43786,0 -15.4907,-0.83882 -19.0228,-0.68757 -3.52211,0.15082 -8.86547,0.0959 -10.54273,0.45837 -5.14771,1.11256 -13.46546,-0.10221 -18.33522,-2.97947 -9.24476,1.9224 -12.77368,3.34755 -27.27363,-1.37513 -12.22239,-3.98088 -23.04873,-1.05625 -24.52336,-0.68758 -1.94899,0.48725 -0.57979,-5.24412 -0.22919,-6.64651 z"
+ id="path4273"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 374.65256,430.0385 c -0.46386,0.30755 -0.29852,6.66155 0.0735,9.77357 0.69771,1.34006 -0.75673,3.15055 0.0612,4.42669 1.06229,1.26575 17.96392,4.17448 30.99751,-2.71591 7.16773,3.65338 19.48816,5.76222 21.97314,5.67619 1.35759,0.0791 31.04308,-5.08128 36.16127,-5.63736 3.18189,-0.47001 6.34239,-1.12735 9.57135,-1.15937 2.18853,-0.0793 11.59494,2.01483 14.10855,3.03809 7.97815,3.2478 10.94211,1.92182 12.43548,2.40625 0.3507,-1.15174 0.96205,-3.0669 0.61838,-5.30434 -0.14728,-2.69526 0.80767,-8.85257 -0.22817,-10.57514 -0.86303,-0.73485 0.23011,-2.34712 -0.76943,-2.57144 -3.35671,0.5678 -9.25646,2.30332 -14.14181,3.90936 -4.29978,1.42531 -6.14948,2.85959 -9.02473,2.95456 -2.70396,0.83022 -9.61856,0.40802 -12.73297,0.33881 -6.61848,-1.45305 -13.54767,-4.88569 -20.73662,-6.10729 -3.89623,-0.29835 -11.99097,1.55012 -15.2184,3.40951 -5.12106,-2.50243 -16.63917,-4.01807 -20.88889,-4.38581 -15.49707,-1.34855 -27.70891,1.32477 -32.25937,2.52363 z"
+ id="path4275"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <circle
+ r="395.53806"
+ cy="433.07086"
+ cx="365.22308"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4277"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g4279" />
+ </g>
+ <g
+ transform="matrix(1.2522169,0,0,1.1949279,445.57197,111.7793)"
+ id="g4281-6">
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="80.222176"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ id="path4283-4"
+ d="m -227.62113,184.74074 c -4.49411,-0.0954 -8.97042,2.374 -11.191,6.2799 -2.63845,4.36896 -2.07131,10.19335 1.18251,14.09932 l 6.45195,6.95613 6.28685,6.60414 6.28203,6.77764 6.46157,6.60911 6.28684,6.60412 6.28203,6.77766 6.28686,6.60411 6.46157,6.60911 -0.43407,-9.56308 -0.25451,-9.73165 9.59088,0.96781 9.59091,0.9678 -6.28683,-6.60412 -6.46158,-6.60911 -6.28205,-6.77765 -6.28682,-6.60412 -6.46159,-6.6091 -6.28202,-6.77765 -6.28686,-6.60414 -6.46158,-6.60911 c -1.7903,-1.65835 -4.1384,-2.72262 -6.55787,-3.13885 -0.63712,-0.1224 -1.2752,-0.21464 -1.91722,-0.22827 z"
+ style="fill:#8ac692;fill-opacity:1;fill-rule:evenodd;stroke:#2d692d;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="76.790672"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ sodipodi:open="true"
+ sodipodi:end="5.9263177"
+ sodipodi:start="5.9358167"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 0 1 -6.9448319,4.5172983 0.78960949,0.77599555 0 0 1 -7.9565593,4.0559115 0.78960949,0.77599555 0 0 1 -7.489482,3.0605348 0.78960949,0.77599555 0 0 1 -6.4755362,3.517194"
+ sodipodi:ry="0.77599555"
+ sodipodi:rx="0.78960949"
+ sodipodi:cy="3.788281"
+ sodipodi:cx="-7.2153969"
+ id="path4285-1"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/svg/seamonkey.svg b/comm/suite/branding/seamonkey/icons/svg/seamonkey.svg
new file mode 100644
index 0000000000..d38f24e14e
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/svg/seamonkey.svg
@@ -0,0 +1,529 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ id="svg173"
+ enable-background="new 0.0 0.0 202.0 202.0"
+ viewBox="0.0 0.0 202.0 202.0"
+ overflow="visible"
+ height="202.0px"
+ width="202.0px"
+ version="1.1">
+<!-- Official SeaMonkey logo
+
+ 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 https://mozilla.org/MPL/2.0/.
+
+ Prepared from the SeaMonkey Artwork project code
+ (see https://www.seamonkey-project.org/dev/artwork for more info).
+
+-->
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+ <metadata
+ id="metadata177">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs105">
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(-0.02980042,-0.05162048,0.10150147,-0.05860901,139.75,135.45)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad1">
+ <stop
+ id="stop10"
+ stop-opacity="0.28235295"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop12"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.02276611,0.05506897,-0.10826111,0.04475403,74.85,61.55)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad2">
+ <stop
+ id="stop15"
+ stop-opacity="0.2509804"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop17"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(-0.01377869,-0.02386475,0.06390381,-0.03691101,112.85,110.8)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad3">
+ <stop
+ id="stop20"
+ stop-opacity="0.7529412"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop22"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.01478577,0.02560425,-0.06390381,0.03691101,124.65,67.85)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad4">
+ <stop
+ id="stop25"
+ stop-opacity="0.6627451"
+ stop-color="#FFFFFF"
+ offset="0.078431375" />
+ <stop
+ id="stop27"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(-0.01382446,0.05155945,-0.0725708,-0.0194397,106.45,90.35)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad5">
+ <stop
+ id="stop30"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop32"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <radialGradient
+ cy="0"
+ cx="0"
+ r="819.2"
+ gradientTransform="matrix(0.08848572,0,0,0.08848572,102.7,84.95)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad6">
+ <stop
+ id="stop35"
+ stop-color="#188BFE"
+ offset="0.0" />
+ <stop
+ id="stop37"
+ stop-opacity="0.5019608"
+ stop-color="#A6CAF0"
+ offset="1.0" />
+ </radialGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00497437,-0.00926209,0.01187134,0.00636291,147.9,87.6)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad7">
+ <stop
+ id="stop40"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop42"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00318909,-0.00860596,0.00933838,0.00344849,148.65,74.3)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad8">
+ <stop
+ id="stop45"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop47"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00167847,-0.00621033,0.00637817,0.00170898,147.45,62.35)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad9">
+ <stop
+ id="stop50"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop52"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(9.3078613e-4,-0.00343323,0.00375366,0.00100708,144.5,52.95)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad10">
+ <stop
+ id="stop55"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop57"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00770569,-0.00982666,0.01483154,0.01164246,145.05,102.45)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad11">
+ <stop
+ id="stop60"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop62"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00814819,0.00468445,-0.00396729,0.00692749,65.6,150.7)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad12">
+ <stop
+ id="stop65"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop67"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00517273,0.00241089,-0.00442505,0.00958252,79.8,146.3)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad13">
+ <stop
+ id="stop70"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop72"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.0080719,-0.00523376,0.01164246,0.01797485,138,114)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad14">
+ <stop
+ id="stop75"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop77"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00566101,-0.00151062,0.00544739,0.02049255,128.3,122.15)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad15">
+ <stop
+ id="stop80"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop82"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.004776,-5.79834e-4,0.00231934,0.01898193,118.5,128.4)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad16">
+ <stop
+ id="stop85"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop87"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00550842,0.00161743,-0.00361633,0.01231384,89.95,142.45)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad17">
+ <stop
+ id="stop90"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop92"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00509644,5.79834e-4,-0.00170898,0.01495361,100.05,138.3)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad18">
+ <stop
+ id="stop95"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop97"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ <linearGradient
+ y2="0"
+ x2="819.2"
+ y1="0"
+ x1="-819.2"
+ gradientTransform="matrix(0.00476074,-1.0681152e-4,4.4250488e-4,0.01708984,109.55,133.7)"
+ gradientUnits="userSpaceOnUse"
+ id="Grad19">
+ <stop
+ id="stop100"
+ stop-opacity="0.0"
+ stop-color="#FFFFFF"
+ offset="0.0" />
+ <stop
+ id="stop102"
+ stop-opacity="0.5019608"
+ stop-color="#FFFFFF"
+ offset="1.0" />
+ </linearGradient>
+ </defs>
+ <g
+ transform="translate(0,1.961165)"
+ id="g171">
+ <g
+ id="g169">
+ <g
+ id="g167">
+ <path
+ style="fill:#000089;stroke-linecap:round;stroke-linejoin:round"
+ id="path107"
+ d="m 171.2,169.2 c -19.33333,19.4 -42.66666,29.1 -70,29.1 -27.36667,0 -50.750003,-9.7 -70.15,-29.1 C 11.683332,149.86667 2.0166655,126.51667 2.05,99.15 2.0166667,71.78333 11.683333,48.416664 31.05,29.05 50.45,9.683332 73.83333,-9.536743e-7 101.2,0 c 27.33333,0 50.66665,9.683333 70,29.05 19.36667,19.366664 29.06666,42.73333 29.1,70.1 -0.0333,27.36666 -9.73333,50.71667 -29.1,70.05 z" />
+ <path
+ style="fill:#9293fe;stroke-linecap:round;stroke-linejoin:round"
+ id="path109"
+ d="m 164.9,93.35 3.6,-1.3 3.1,-1.2 c 8.03333,-3.26667 16.18333,-5.550005 24.45,-6.85 l 0.1,0.8 c 0.66667,4.633334 1,9.41667 1,14.35 v 0.15 c -17.30001,-2.3 -34.10001,-0.68333 -50.4,4.85 -6.8,2.7 -14.56667,5.28333 -23.3,7.75 -22.06667,5.16667 -44.98334,3.38333 -68.75,-5.35 l -3.25,-1.15 c -15.8,-5.43333 -31.233333,-7.76667 -46.3,-7 0.033333,-3.23333 0.2,-6.39999 0.5,-9.5 l 0.2,-2 c 3.699999,-1.066664 7.449999,-1.76666 11.25,-2.1 l 4.4,-0.2 c 7.566666,-0.06667 15.333332,1.36667 23.3,4.3 13.966666,5.166666 27.383334,5.7 40.25,1.6 L 86,90.85 c 27.46667,10.1 53.76666,10.93333 78.9,2.5 z m -18.15,10.8 h 0.05 z" />
+ <path
+ style="fill:#5050fe;stroke-linecap:round;stroke-linejoin:round"
+ id="path111"
+ d="m 197.15,99.3 c 0,4.26667 -0.25,8.41667 -0.75,12.45 -5.76667,-0.5 -11.65,-1.85 -17.65,-4.05 -11.03334,-4.03334 -21.68333,-5.21667 -31.95,-3.55 16.26667,-5.53333 33.04999,-7.15 50.35,-4.85 z m -50.35,4.85 h -0.05 z" />
+ <path
+ style="fill:#0101e4;stroke-linecap:round;stroke-linejoin:round"
+ id="path113"
+ d="m 196.4,111.75 c -1.56667,13 -5.63334,24.96666 -12.2,35.9 -4.1,6.83332 -9.16666,13.28332 -15.2,19.35 -18.73334,18.76666 -41.33334,28.15 -67.8,28.15 -26.533336,0 -49.183337,-9.38334 -67.95,-28.15 -6.066668,-6.06667 -11.133335,-12.51666 -15.2,-19.35 -6.9,-11.53333 -11.0333336,-24.23333 -12.4,-38.1 13.533334,3.8 26.516668,3.18333 38.95,-1.85 2.266665,-0.93333 4.549998,-1.69999 6.85,-2.3 l 3.25,1.15 c 23.76667,8.73333 46.68333,10.51667 68.75,5.35 8.73333,-2.46667 16.51667,-5.05 23.35,-7.75 10.26667,-1.66666 20.91666,-0.48333 31.95,3.55 6,2.2 11.88333,3.55 17.65,4.05 z" />
+ <path
+ style="fill:#6263fd;stroke-linecap:round;stroke-linejoin:round"
+ id="path115"
+ d="m 5.65,109.55 c -0.3333334,-3.4 -0.5,-6.86667 -0.5,-10.4 V 98.4 c 15.066667,-0.76667 30.5,1.56667 46.3,7 -2.3,0.6 -4.58333,1.36666 -6.85,2.3 -12.433336,5.03333 -25.41667,5.65 -38.95,1.85 z" />
+ <path
+ style="fill:#5c5cbe;stroke-linecap:round;stroke-linejoin:round"
+ id="path117"
+ d="m 21.5,84.6 c 20.433334,-3.96667 41.61667,-2 63.55,5.9 C 72.183334,94.6 58.766666,94.066666 44.8,88.9 36.833332,85.96667 29.066666,84.53333 21.5,84.6 Z" />
+ <path
+ style="fill:#4d4db7;stroke-linecap:round;stroke-linejoin:round"
+ id="path119"
+ d="m 85.05,90.5 0.75,-0.25 3.7,-1.35 c 14.16666,-5.73333 29.06667,-5.73333 44.7,0 7.53334,2.76667 14.91667,4.200006 22.15,4.3 h 0.25 l 8.3,0.15 c -25.13334,8.43333 -51.43334,7.6 -78.9,-2.5 z" />
+ <path
+ style="fill:url(#Grad1);stroke-linecap:round;stroke-linejoin:round"
+ id="path121"
+ d="m 169,167 c -18.73334,18.76666 -41.33334,28.15 -67.8,28.15 C 74.666664,195.15 52.016663,185.76666 33.25,167 14.516666,148.26666 5.1499996,125.64999 5.15,99.15 c 0,-26.5 9.366666,-49.133334 28.1,-67.9 18.76667,-18.733334 41.41667,-28.1000004 67.95,-28.1 26.46666,0 49.06666,9.366666 67.8,28.1 18.76666,18.76667 28.15,41.40001 28.15,67.9 0,26.49999 -9.38334,49.11666 -28.15,67.85 z" />
+ <path
+ style="fill:url(#Grad2);stroke-linecap:round;stroke-linejoin:round"
+ id="path123"
+ d="M 169.05,167.1 C 150.25,185.8 127.6,195.16667 101.1,195.2 74.6,195.16666 51.999996,185.79999 33.3,167.1 14.5,148.3 5.116667,125.65 5.15,99.15 5.116667,72.683334 14.5,50.083332 33.3,31.35 52,12.583334 74.600006,3.2000008 101.1,3.2 c 26.50001,0 49.15,9.383333 67.95,28.15 18.7,18.733336 28.06667,41.333334 28.1,67.8 -0.0333,26.49999 -9.4,49.14999 -28.1,67.95 z" />
+ <path
+ style="fill:#ffffff;stroke-linecap:round;stroke-linejoin:round"
+ id="path125"
+ d="M 166,88.95 V 89 l -1.45,10.05 c -1.93333,9 -5.85,17.08334 -11.75,24.25 -6.23333,7.46666 -14.68333,13.9 -25.35,19.3 -6.36666,3.13334 -13.49999,5.91666 -21.4,8.35 -6.56667,1.96666 -13.66667,3.71666 -21.3,5.25 -6.9,1.33333 -14.25,2.46666 -22.05,3.4 -3.9,2.66668 -7.833335,5.61667 -11.8,8.85 l -0.1,0.05 C 34.233334,181.7 18.65,189.41666 4.05,191.65 3.15,191.75 2.3333333,191.5667 1.6,191.1 L 1.5,191.05 C 0.8333333,190.58334 0.3833333,189.96667 0.15,189.2 L 0.1,189 c -0.20000002,-0.83333 -0.11666668,-1.61667 0.25,-2.35 l 0.05,-0.1 c 0.4000001,-0.8 1.0166667,-1.35 1.85,-1.65 23.066666,-8.6 44.1,-23.81666 63.1,-45.65 19,-21.86667 31.583334,-44.316666 37.75,-67.35 6.66666,-24.933333 15.13333,-39.716666 25.4,-44.35 10.83333,-5.266667 18.91666,-5.916667 24.25,-1.95 l 0.1,0.05 c 3.83333,2.466667 4.85,6.066667 3.05,10.8 -1.03333,2.9 -3.56666,6.383332 -7.6,10.45 l -0.8,0.8 1.1,0.3 c 2.6,0.766666 4.9,1.95 6.9,3.55 3.59999,2.866664 6.11665,6.483333 7.55,10.85 1.2,3.86666 2.11667,8.166666 2.75,12.9 0.53334,4.766666 0.60001,9.31666 0.2,13.65 z m -1.45,10.1 V 99.1 Z M 84.75,156.2 H 84.7 Z M 4.05,191.65 V 191.6 Z M 0.1,189 v -0.05 z m 2.15,-4.1 H 2.3 Z" />
+ <path
+ style="fill:#3399ff;stroke-linecap:round;stroke-linejoin:round"
+ id="path127"
+ d="m 143.95,45.55 -0.1,0.25 v 0.25 C 157.88333,91.149994 141.36667,121.96666 94.3,138.5 79.5,143.59999 64.183334,152.53333 48.35,165.3 34.516666,176.33334 21.4,183.40001 9,186.5 c 21.566666,-9.06667 41.35,-23.93333 59.35,-44.6 19.466666,-22.36667 32.33333,-45.35001 38.6,-68.95 6.26667,-23.333335 14.03334,-37.266666 23.3,-41.8 9.16667,-4.466666 15.96667,-5.2 20.4,-2.2 2,1.4 2.5,3.41667 1.5,6.05 -1.1,2.833332 -3.83333,6.35 -8.2,10.55 z M 48.35,165.3 H 48.4 Z M 150.65,28.95 V 29 Z" />
+ <path
+ style="fill:#0582fe;stroke-linecap:round;stroke-linejoin:round"
+ id="path129"
+ d="m 143.85,45.8 0.1,-0.25 c 4.36667,-4.2 7.1,-7.716668 8.2,-10.55 1,-2.633335 0.5,-4.633335 -1.5,-6 -4.43334,-3.033333 -11.23334,-2.316666 -20.4,2.15 -9.26667,4.533334 -17.03334,18.46667 -23.3,41.8 -6.26667,23.6 -19.133334,46.58334 -38.6,68.95 -18,20.66667 -37.783334,35.53333 -59.35,44.6 12.400002,-3.09999 25.533337,-10.16666 39.4,-21.2 15.8,-12.76667 31.1,-21.70001 45.9,-26.8 47.06667,-16.53334 63.58333,-47.350006 49.55,-92.45 z M 48.4,165.3 H 48.35 Z M 150.65,29 v -0.05 z m 0.55,-0.85 c 2.46666,1.666666 3.09999,4.083334 1.9,7.25 -1.1,2.866666 -3.83334,6.416666 -8.2,10.65 14.1,45.533336 -2.65,76.66667 -50.25,93.4 C 79.91667,144.51666 64.700005,153.4 49,166.1 32.866665,178.93333 17.73333,186.45 3.6,188.65 l -0.35,-0.05 -0.2,-0.3 v -0.35 l 0.3,-0.25 C 26.816668,178.93333 48.233337,163.45 67.6,141.25 86.933334,118.98333 99.73334,96.13333 106,72.7 c 6.33334,-23.76667 14.26667,-37.916667 23.8,-42.45 9.6,-4.666668 16.73333,-5.366669 21.4,-2.1 z" />
+ <path
+ style="fill:url(#Grad3);stroke-linecap:round;stroke-linejoin:round"
+ id="path131"
+ d="m 149.15,31.1 c 0.83332,0.700001 1.01666,1.7 0.55,3 -1.03334,2.533335 -3.55,5.700003 -7.55,9.5 -0.43334,0.433333 -0.71667,0.933333 -0.85,1.5 -0.16667,0.566668 -0.16667,1.15 0,1.75 3.3,10.566668 4.85001,20.333334 4.65,29.3 -0.63333,27.83334 -18.13333,47.78333 -52.5,59.85 -9.7,3.33333 -19.6,8.28333 -29.7,14.85 l 6.6,-7.2 c 12.73333,-14.66666 22.683325,-29.58333 29.85,-44.75 3.96666,-8.333334 7.08333,-16.75 9.35,-25.25 5.90001,-22.3 13.16667,-35.683337 21.8,-40.15 8.03333,-3.866667 13.96667,-4.666666 17.8,-2.4 z" />
+ <path
+ style="fill:url(#Grad4);stroke-linecap:round;stroke-linejoin:round"
+ id="path133"
+ d="m 149.15,31.1 c 0.83332,0.700001 1.01666,1.7 0.55,3 -1.03334,2.533335 -3.55,5.700003 -7.55,9.5 -0.43334,0.433333 -0.71667,0.933333 -0.85,1.5 -0.16667,0.566668 -0.16667,1.15 0,1.75 3.3,10.566668 4.85001,20.333334 4.65,29.3 -0.63333,27.83334 -18.13333,47.78333 -52.5,59.85 -9.7,3.33333 -19.6,8.28333 -29.7,14.85 l 6.6,-7.2 c 12.73333,-14.66666 22.683325,-29.58333 29.85,-44.75 3.96666,-8.333334 7.08333,-16.75 9.35,-25.25 5.90001,-22.3 13.16667,-35.683337 21.8,-40.15 8.03333,-3.866667 13.96667,-4.666666 17.8,-2.4 z" />
+ <path
+ style="fill:url(#Grad5);stroke-linecap:round;stroke-linejoin:round"
+ id="path135"
+ d="M 149.4,32.15 148.6,32 c -3.6,-2.033333 -9.18333,-1.216665 -16.75,2.45 -8.39999,4.4 -15.49999,17.55 -21.3,39.45 -2.26667,8.56667 -5.4,17.05 -9.4,25.45 -7.2,15.23333 -17.2,30.21667 -30,44.95 l -6.6,7.3 c -0.2,0.2 -0.45,0.31667 -0.75,0.35 -0.266667,0 -0.5,-0.10001 -0.7,-0.3 -0.233335,-0.2 -0.35,-0.45 -0.35,-0.75 -0.03333,-0.26667 0.05,-0.5 0.25,-0.7 l 6.55,-7.25 c 12.66667,-14.56667 22.56667,-29.40001 29.7,-44.5 3.93333,-8.3 7.03333,-16.65 9.3,-25.05 6,-22.733336 13.46667,-36.33334 22.4,-40.8 8.43333,-4.1 14.66667,-4.883333 18.7,-2.35 l 0.5,0.6 -0.1,0.8 c -0.16667,0.233333 -0.38334,0.4 -0.65,0.5 z" />
+ <path
+ style="fill:url(#Grad6);stroke-linecap:round;stroke-linejoin:round"
+ id="path137"
+ d="m 162.5,88.6 c -0.43333,4.066664 -0.9,7.316664 -1.4,9.75 -1.8,8.43333 -5.46667,16 -11,22.7 -5.93333,7.13333 -14.01666,13.26667 -24.25,18.4 -6.16667,3.06666 -13.11666,5.78334 -20.85,8.15 -6.46667,1.93334 -13.45,3.65 -20.95,5.15 -8.099995,1.56667 -16.8,2.83333 -26.1,3.8 14.83333,-10.26667 24.38333,-16.61668 28.65,-19.05 6.43333,-3.8 12.25,-7.71667 17.45,-11.75 5.56667,-4.4 10.38333,-8.93333 14.45,-13.6 3.66666,-4.26667 6.73333,-8.63334 9.2,-13.1 2.33333,-4.233334 4.14999,-8.6 5.45,-13.1 1.3,-4.633334 2.03333,-9.366664 2.2,-14.2 l 0.05,-1.45 c 0.0667,-5.366666 -0.51667,-11.866666 -1.75,-19.5 8.56666,-1.266667 15.11666,-0.1 19.65,3.5 3.06667,2.433334 5.2,5.483333 6.4,9.15 1.16667,3.66667 2.03334,7.75 2.6,12.25 0.53334,4.5 0.60001,8.8 0.2,12.9 z" />
+ <path
+ style="fill:url(#Grad7);stroke-linecap:round;stroke-linejoin:round"
+ id="path139"
+ d="m 162.2,90.85 c -0.23333,2.53333 -0.6,5 -1.1,7.4 -2.13334,-1.833336 -4.31667,-3.450005 -6.55,-4.85 -6.03334,-3.9 -13.05,-6.8 -21.05,-8.7 0.66667,-2.66667 1.16667,-5.366664 1.5,-8.1 7.86667,0.63334 14.86667,2.2 21,4.7 2.09999,0.83334 4.21666,2 6.35,3.5 z" />
+ <path
+ style="fill:url(#Grad8);stroke-linecap:round;stroke-linejoin:round"
+ id="path141"
+ d="m 161.35,73.45 0.95,8.8 c -1.86667,-1.200005 -3.7,-2.16667 -5.5,-2.9 -6.3,-2.56667 -13.5,-4.166666 -21.6,-4.8 l 0.15,-2.8 0.05,-1.45 -0.05,-3.8 c 9.56667,-0.73333 17.91666,0.38334 25.05,3.35 l 0.4,0.2 z m -0.95,-3.6 h -0.05 z" />
+ <path
+ style="fill:url(#Grad9);stroke-linecap:round;stroke-linejoin:round"
+ id="path143"
+ d="m 160.25,67.55 c -7.16667,-2.83333 -15.5,-3.900002 -25,-3.2 L 134.7,58 c 8.33333,-1.533333 15.8,-1.35 22.4,0.55 1.1,1.83333 1.94999,3.983333 2.55,6.45 z" />
+ <path
+ style="fill:url(#Grad10);stroke-linecap:round;stroke-linejoin:round"
+ id="path145"
+ d="m 155.1,55.95 c -6.19999,-1.4 -13.1,-1.4 -20.7,0 l -0.75,-5.15 c 8.56666,-1.266667 15.11666,-0.1 19.65,3.5 z" />
+ <path
+ style="fill:url(#Grad11);stroke-linecap:round;stroke-linejoin:round"
+ id="path147"
+ d="m 132.95,86.7 c 7.76667,1.866666 14.58334,4.68333 20.45,8.45 2.43333,1.56667 4.81666,3.38333 7.15,5.45 -1.5,5.9 -3.96667,11.38334 -7.4,16.45 -6.26667,-10.26667 -13.98333,-17.8 -23.15,-22.6 1.16667,-2.533336 2.15001,-5.11667 2.95,-7.75 z" />
+ <path
+ style="fill:url(#Grad12);stroke-linecap:round;stroke-linejoin:round"
+ id="path149"
+ d="m 57.95,156.55 c 6.8,-4.73333 12.5,-8.61667 17.1,-11.65 -0.599995,3.19999 -1.51666,6.48333 -2.75,9.85 z" />
+ <path
+ style="fill:url(#Grad13);stroke-linecap:round;stroke-linejoin:round"
+ id="path151"
+ d="m 77.45,143.3 c 3.3,-2.2 5.91667,-3.86667 7.85,-5 -0.06667,4.63333 -0.78334,9.51667 -2.15,14.65 l -8.55,1.45 c 1.333334,-3.76667 2.28333,-7.46667 2.85,-11.1 z" />
+ <path
+ style="fill:url(#Grad14);stroke-linecap:round;stroke-linejoin:round"
+ id="path153"
+ d="m 150.1,121.05 c -2.9,3.46667 -6.30001,6.7 -10.2,9.7 -3.53333,-9.43333 -8.41666,-18.6 -14.65,-27.5 l 2.45,-4.2 1.4,-2.65 c 8.86666,4.63333 16.33332,11.95 22.4,21.95 l 0.3,0.55 z" />
+ <path
+ style="fill:url(#Grad15);stroke-linecap:round;stroke-linejoin:round"
+ id="path155"
+ d="m 127,138.9 c -1.5,-9.66666 -4.13333,-18.81666 -7.9,-27.45 l 4.9,-6.35 c 6.03334,8.7 10.75,17.68334 14.15,26.95 -3.36666,2.43334 -7.08332,4.71666 -11.15,6.85 z" />
+ <path
+ style="fill:url(#Grad16);stroke-linecap:round;stroke-linejoin:round"
+ id="path157"
+ d="m 117.55,113.2 c 3.56667,8.4 6.06667,17.28334 7.5,26.65 l -8.2,3.65 c -0.63333,-8.59999 -2.18334,-16.86666 -4.65,-24.8 z" />
+ <path
+ style="fill:url(#Grad17);stroke-linecap:round;stroke-linejoin:round"
+ id="path159"
+ d="m 94.5,132.55 c 0.4,5.83333 0.13333,11.86666 -0.8,18.1 l -8.3,1.85 c 1.36666,-5.40001 2.016664,-10.55 1.95,-15.45 z" />
+ <path
+ style="fill:url(#Grad18);stroke-linecap:round;stroke-linejoin:round"
+ id="path161"
+ d="m 104.15,147.9 -8.25,2.25 c 0.933336,-6.5 1.13333,-12.80001 0.6,-18.9 l 6.5,-4.65 c 1.29999,6.73333 1.68333,13.83332 1.15,21.3 z" />
+ <path
+ style="fill:url(#Grad19);stroke-linecap:round;stroke-linejoin:round"
+ id="path163"
+ d="m 104.8,125.15 5.7,-4.85 c 2.29999,7.7 3.71666,15.7 4.25,24 l -8.5,2.95 c 0.5,-7.73334 0.0167,-15.1 -1.45,-22.1 z" />
+ <path
+ style="fill:#0582fe;stroke-linecap:round;stroke-linejoin:round"
+ id="path165"
+ d="m 160.2,63.3 c 1.16667,3.7 2.03334,7.81667 2.6,12.35 0.53334,4.533334 0.60001,8.86667 0.2,13 -0.43333,4.1 -0.9,7.36666 -1.4,9.8 -1.83332,8.53333 -5.53332,16.18333 -11.1,22.95 -6,7.16667 -14.13333,13.33334 -24.4,18.5 -6.23333,3.06666 -13.2,5.8 -20.9,8.2 -6.5,1.93334 -13.516666,3.65 -21.05,5.15 -8.1,1.56667 -16.816664,2.83333 -26.15,3.8 l -0.35,-0.05 -0.2,-0.25 v -0.35 l 0.2,-0.25 c 14.83334,-10.26667 24.4,-16.61666 28.7,-19.05 6.43333,-3.8 12.25,-7.71667 17.45,-11.75 5.53334,-4.36666 10.31667,-8.86666 14.35,-13.5 3.6,-4.2 6.61666,-8.48333 9.05,-12.85 l 0.05,-0.2 c 2.33333,-4.2 4.15,-8.53334 5.45,-13 1.26667,-4.599995 1.98335,-9.28333 2.15,-14.05 l 0.05,-1.45 c 0.0667,-5.33333 -0.51667,-11.8 -1.75,-19.4 l 0.1,-0.4 c 0.0667,-0.100002 0.18333,-0.166668 0.35,-0.2 8.7,-1.3 15.36667,-0.1 20,3.6 3.13334,2.5 5.33333,5.633333 6.6,9.4 z m -27.5,22.5 v 0.05 z m 29.1,-10.05 c -0.56666,-4.46667 -1.41667,-8.51667 -2.55,-12.15 -1.16667,-3.566667 -3.25,-6.533334 -6.25,-8.9 -4.36667,-3.433334 -10.61667,-4.583335 -18.75,-3.45 1.16667,7.433334 1.71667,13.78333 1.65,19.05 l -0.05,1.5 c -0.16667,4.866664 -0.9,9.63333 -2.2,14.3 -1.30001,4.43333 -3.10001,8.75 -5.4,12.95 l -0.05,0.25 c -2.5,4.5 -5.6,8.90001 -9.3,13.2 -4.1,4.66666 -8.93333,9.21667 -14.5,13.65 -5.23333,4.03333 -11.08333,7.96667 -17.55,11.8 -4.13333,2.33333 -13.15,8.3 -27.05,17.9 8.599994,-0.93333 16.65,-2.13333 24.15,-3.6 7.5,-1.5 14.483334,-3.2 20.95,-5.1 7.66667,-2.36666 14.58333,-5.08332 20.75,-8.15 10.13334,-5.09999 18.16668,-11.18333 24.1,-18.25 5.46666,-6.63333 9.08333,-14.13333 10.85,-22.5 0.5,-2.433334 0.96667,-5.666664 1.4,-9.7 0.40001,-4.06667 0.33334,-8.33334 -0.2,-12.8 z" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/JSConsoleWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/JSConsoleWindow.svg
new file mode 100644
index 0000000000..67828aca32
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/JSConsoleWindow.svg
@@ -0,0 +1,1297 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons"
+ sodipodi:docname="seamonkey-JSConsoleWindowSVGredo.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4601519"
+ inkscape:cx="441.74906"
+ inkscape:cy="478.51551"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ showguides="true"
+ inkscape:guide-bbox="true" />
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3771">
+ <stop
+ offset="0"
+ id="stop3773"
+ style="stop-color:#a53d00;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3775"
+ style="stop-color:#d28100;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3687">
+ <stop
+ offset="0"
+ id="stop3689"
+ style="stop-color:#f9f1a9;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3691"
+ style="stop-color:#e6bb00;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3251">
+ <stop
+ offset="0"
+ id="stop3253"
+ style="stop-color:#380000;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3255"
+ style="stop-color:#b90000;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3214">
+ <stop
+ offset="0"
+ id="stop3216"
+ style="stop-color:#eb0000;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3218"
+ style="stop-color:#ae0000;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3220">
+ <stop
+ offset="0"
+ id="stop3222"
+ style="stop-color:#2eb8f3;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3224"
+ style="stop-color:#005bc6;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient3546"
+ r="11.280758"
+ gradientTransform="matrix(0.1470135,-0.7243702,0.8344651,0.1693713,234.72682,1381.244)"
+ cx="1027.4507"
+ cy="774.97803"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3220"
+ fy="774.97803"
+ fx="1027.4507" />
+ <filter
+ inkscape:collect="always"
+ id="filter3210"
+ x="-0.21277955"
+ y="-0.27520661"
+ width="1.4255591"
+ height="1.5504132">
+ <feGaussianBlur
+ inkscape:collect="always"
+ id="feGaussianBlur3212"
+ stdDeviation="1.734375" />
+ </filter>
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient3220"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214"
+ fy="821.82721"
+ fx="1147.2316" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient3238"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214"
+ fy="821.82721"
+ fx="1147.2316" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3257"
+ x1="1139.5706"
+ y1="826.1059"
+ x2="1152.082"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3251"
+ y2="730.0083" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2242"
+ x1="441.50293"
+ x2="553.63983"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ y1="596.54565"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5809"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2239"
+ x1="331.57434"
+ x2="225.65819"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ y1="354.45773"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5074"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2234"
+ x1="331.57434"
+ x2="225.65819"
+ gradientTransform="translate(155.91995,3.7227902)"
+ y1="354.45773"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5074"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2232"
+ x1="441.50293"
+ x2="553.63983"
+ gradientTransform="translate(155.91995,3.7227902)"
+ y1="596.54565"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5809"
+ y2="786.83405" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11020"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ cy="334.10477"
+ fx="822.35791"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="334.10477"
+ cx="822.35791" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11018"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ cy="379.37366"
+ fx="834.06329"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="379.37366"
+ cx="834.06329" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11016"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ cy="416.62680"
+ fx="834.16461"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="416.62680"
+ cx="834.16461" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11014"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ cy="653.19714"
+ fx="559.93085"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="653.19714"
+ cx="559.93085" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11012"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ cy="635.96777"
+ fx="596.68011"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="635.96777"
+ cx="596.68011" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11010"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ cy="620.45239"
+ fx="637.07098"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="620.45239"
+ cx="637.07098" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11008"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ cy="613.80670"
+ fx="665.83667"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="613.80670"
+ cx="665.83667" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11006"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ cy="592.33008"
+ fx="696.78522"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="592.33008"
+ cx="696.78522" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11004"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ cy="565.76050"
+ fx="734.74274"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="565.76050"
+ cx="734.74274" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11002"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ cy="558.89948"
+ fx="793.18164"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="558.89948"
+ cx="793.18164" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient11000"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ cy="520.16986"
+ fx="799.92041"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="520.16986"
+ cx="799.92041" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient10262"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ cy="457.29218"
+ fx="825.40234"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient10990"
+ fy="457.29218"
+ cx="825.40234" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient5823"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ cy="432.99265"
+ fx="425.42249"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5817"
+ fy="432.99265"
+ cx="425.42249" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5815"
+ x1="441.50293"
+ x2="553.63983"
+ gradientTransform="translate(155.91995,3.7227902)"
+ y1="596.54565"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5809"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5080"
+ x1="331.57434"
+ x2="225.65819"
+ gradientTransform="translate(155.91995,3.7227902)"
+ y1="354.45773"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5074"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5072"
+ x1="225.43034"
+ x2="518.04419"
+ gradientTransform="translate(-478.87675,4.232021)"
+ y1="682.32623"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5066"
+ y2="294.85672" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient5002"
+ x1="544.29083"
+ x2="253.71039"
+ gradientTransform="translate(-453.48462,5.6426947)"
+ y1="213.74759"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient4996"
+ y2="653.04156" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ offset="0"
+ id="stop4998"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ offset="1"
+ id="stop5000"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ offset="0.00000000"
+ id="stop5008"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ offset="0.69999999"
+ id="stop10260"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ offset="0.80000001"
+ id="stop5016"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ offset="1.0000000"
+ id="stop5010"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ offset="0.00000000"
+ id="stop5068"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ offset="1.0000000"
+ id="stop5070"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ offset="0.00000000"
+ id="stop5076"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ offset="1.0000000"
+ id="stop5078"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ offset="0.00000000"
+ id="stop5811"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ offset="1.0000000"
+ id="stop5813"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ offset="0.00000000"
+ id="stop5819"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ offset="1.0000000"
+ id="stop5821"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ offset="0.00000000"
+ id="stop10992"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ offset="0.80357140"
+ id="stop10996"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ offset="1.0000000"
+ id="stop10998"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ offset="0"
+ id="stop4699"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ offset="0.89999998"
+ id="stop4707"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ offset="1"
+ id="stop4701"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ offset="0"
+ id="stop5600"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ offset="0.95266271"
+ id="stop5606"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ offset="1"
+ id="stop5602"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3777"
+ x1="902.51556"
+ y1="806.60181"
+ gradientTransform="translate(7.2175233,19.074883)"
+ x2="907.56848"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3771"
+ y2="782.05695" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3779"
+ x1="896.6649"
+ y1="795.09097"
+ gradientTransform="translate(7.2175233,19.074883)"
+ x2="922.22605"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3687"
+ y2="795.09097" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient2275"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214"
+ fy="821.82721"
+ fx="1147.2316" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2277"
+ x1="1139.5706"
+ y1="826.1059"
+ x2="1152.082"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3251"
+ y2="730.0083" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient3268"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214"
+ fy="821.82721"
+ fx="1147.2316" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3270"
+ x1="1139.5706"
+ y1="826.1059"
+ x2="1152.082"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3251"
+ y2="730.0083" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3337"
+ x1="896.6649"
+ y1="795.09097"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="922.22605"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3687"
+ y2="795.09097" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3339"
+ x1="902.51556"
+ y1="806.60181"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="907.56848"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3771"
+ y2="782.05695" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2340"
+ x1="441.50293"
+ y1="596.54565"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x2="553.63983"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5809"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2342"
+ x1="331.57434"
+ y1="354.45773"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x2="225.65819"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient5074"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2399"
+ x1="896.6649"
+ y1="795.09097"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="922.22605"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3687"
+ y2="795.09097" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2401"
+ x1="902.51556"
+ y1="806.60181"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="907.56848"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3771"
+ y2="782.05695" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2333"
+ x1="896.6649"
+ y1="795.09097"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="922.22605"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3687"
+ y2="795.09097" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2335"
+ x1="902.51556"
+ y1="806.60181"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="907.56848"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3771"
+ y2="782.05695" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2351"
+ x1="896.6649"
+ y1="795.09097"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="922.22605"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3687"
+ y2="795.09097" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2353"
+ x1="902.51556"
+ y1="806.60181"
+ gradientTransform="matrix(0.8511224,0,0,0.9092423,386.18996,104.23525)"
+ x2="907.56848"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3771"
+ y2="782.05695" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2358"
+ x1="896.6649"
+ y1="795.09097"
+ gradientTransform="matrix(-2.8317816,0,0,-3.0251531,3017.0995,3033.9076)"
+ x2="922.22605"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3687"
+ y2="795.09097" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2360"
+ x1="902.51556"
+ y1="806.60181"
+ gradientTransform="matrix(-2.8317816,0,0,-3.0251531,3017.0995,3033.9076)"
+ x2="907.56848"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3771"
+ y2="782.05695" />
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient3268-278"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214-560"
+ fy="821.82721"
+ fx="1147.2316" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3214-560">
+ <stop
+ offset="0"
+ id="stop3677"
+ style="stop-color:#ff0505;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3679"
+ style="stop-color:#c70000;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3270-289"
+ y2="730.0083"
+ y1="826.1059"
+ x2="1152.082"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3251-917"
+ x1="1139.5706" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3251-917">
+ <stop
+ offset="0"
+ id="stop3683"
+ style="stop-color:#510000;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop3685"
+ style="stop-color:#d20000;stop-opacity:1" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ id="radialGradient3268-278-827"
+ r="22.724789"
+ gradientTransform="matrix(0.701911,-0.2667433,0.1979628,0.5209215,179.39359,699.29387)"
+ cx="1147.2316"
+ cy="821.82721"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3214-560-300"
+ fy="821.82721"
+ fx="1147.2316" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3214-560-300">
+ <stop
+ offset="0"
+ id="stop4042"
+ style="stop-color:#ff1e1e;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop4044"
+ style="stop-color:#e00000;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3270-289-17"
+ x1="1139.5706"
+ y1="826.1059"
+ x2="1152.082"
+ gradientUnits="userSpaceOnUse"
+ xlink:href="#linearGradient3251-917-341"
+ y2="730.0083" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3251-917-341">
+ <stop
+ offset="0"
+ id="stop4048"
+ style="stop-color:#6a0000;stop-opacity:1" />
+ <stop
+ offset="1"
+ id="stop4050"
+ style="stop-color:#eb0000;stop-opacity:1" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ x="-0.17312159"
+ width="1.3462432"
+ y="-0.045451466"
+ height="1.0909029"
+ id="filter4477">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.9084375"
+ id="feGaussianBlur4479" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3687"
+ id="linearGradient4489"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-2.8317816,0,0,-3.0251531,3017.0995,3033.9076)"
+ x1="896.6649"
+ y1="795.09097"
+ x2="922.22605"
+ y2="795.09097" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3771"
+ id="linearGradient4491"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-2.8317816,0,0,-3.0251531,3017.0995,3033.9076)"
+ x1="902.51556"
+ y1="806.60181"
+ x2="907.56848"
+ y2="782.05695" />
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="Layer 1">
+ <g
+ id="layer3"
+ transform="translate(831.94743,330.68335)"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ id="layer2"
+ transform="translate(831.94743,330.68335)"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ <g
+ id="g2403"
+ transform="translate(-965.49792,-294.60277)">
+ <g
+ transform="translate(831.94743,330.68335)"
+ id="g3501"
+ inkscape:label="Background"
+ style="display:inline">
+ <g
+ id="g2219">
+ <path
+ id="path6573"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ <path
+ id="path2064"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-ydpi="24.028801"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2056"
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2058"
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ id="path2052"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ </g>
+ </g>
+ <path
+ d="M 1161.6664,817.31491 L 1148.0036,827.79872 L 1130.9294,825.55086 L 1120.4456,811.88809 L 1122.6935,794.81387 L 1136.3562,784.33006 L 1153.4304,786.57792 L 1163.9143,800.2407 L 1161.6664,817.31491 z "
+ inkscape:flatsided="true"
+ id="path3230"
+ inkscape:rounded="0"
+ inkscape:export-ydpi="90.000084"
+ sodipodi:cx="1142.1799"
+ inkscape:export-xdpi="90.000084"
+ inkscape:randomized="0"
+ transform="matrix(1.6768645,-0.2183201,0.2183201,1.6768645,-821.00426,-331.11256)"
+ sodipodi:arg2="0.91629786"
+ sodipodi:arg1="0.52359878"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:r2="20.788244"
+ sodipodi:r1="22.501036"
+ sodipodi:sides="8"
+ sodipodi:type="star"
+ style="fill:url(#radialGradient2275);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2277);stroke-width:1.98091471;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cy="806.06439" />
+ <path
+ id="path3232"
+ inkscape:export-ydpi="90.000084"
+ inkscape:radius="-0.48143509"
+ inkscape:export-xdpi="90.000084"
+ transform="matrix(2.8640361,0,0,3.0356973,-1937.9751,-1610.0856)"
+ inkscape:original="M 1115.7813 774.09375 L 1109.75 780.125 L 1109.7188 788.5625 L 1111.8125 790.65625 C 1111.9938 786.42948 1114.6239 782.49644 1118.8438 780.96875 C 1122.9474 779.48305 1127.3599 780.70786 1130.2188 783.65625 L 1130.2188 780.15625 L 1124.2188 774.125 L 1115.7813 774.09375 z "
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ d="M 1116,774.5625 L 1110.2188,780.34375 L 1110.1875,788.34375 L 1111.5313,789.6875 C 1112.049,785.64848 1114.601,782.01064 1118.6875,780.53125 C 1122.6273,779.10487 1126.7764,780.13281 1129.75,782.6875 L 1129.75,780.375 L 1124,774.59375 L 1116,774.5625 z "
+ sodipodi:type="inkscape:offset"
+ style="opacity:0.50264551;fill:#ffffff;fill-opacity:0.66459626;fill-rule:nonzero;stroke:none;stroke-width:1.98091471;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3210)" />
+ <path
+ id="path3234"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 1266.0373,792.12543 C 1264.6481,792.12543 1263.4994,791.69798 1262.5912,790.84312 C 1261.6834,789.98825 1261.2292,788.86627 1261.2292,787.47713 C 1261.265,787.22823 1261.2831,787.0326 1261.2831,786.89019 C 1261.461,785.35872 1262.1025,784.16555 1263.2069,783.31065 C 1264.3111,782.45579 1265.6467,782.02834 1267.2128,782.02834 C 1268.1387,782.02834 1269.0649,782.31311 1269.9908,782.88267 C 1270.9168,783.45222 1271.4869,784.16444 1271.7006,785.0193 C 1271.914,785.8742 1272.0209,786.49782 1272.0209,786.89019 C 1272.0209,788.4217 1271.4152,789.67712 1270.2046,790.65646 C 1268.9936,791.63577 1267.6047,792.12543 1266.0373,792.12543 L 1266.0373,792.12543 z M 1270.1523,778.60936 L 1266.8396,778.60936 C 1266.8396,778.60936 1265.784,759.37586 1269.1366,752.96471 C 1272.4892,746.55356 1279.9957,746.55356 1280.2493,752.96471 C 1280.5035,759.37586 1270.1523,778.60936 1270.1523,778.60936 z "
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#380000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF" />
+ <path
+ id="path3236"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 1264.4364,790.12436 C 1263.0471,790.12436 1261.8985,789.69691 1260.9903,788.84205 C 1260.0825,787.98719 1259.6282,786.8652 1259.6282,785.47606 C 1259.6641,785.22717 1259.6822,785.03153 1259.6822,784.88913 C 1259.86,783.35765 1260.5016,782.16448 1261.606,781.30958 C 1262.7101,780.45472 1264.0458,780.02727 1265.6118,780.02727 C 1266.5378,780.02727 1267.464,780.31205 1268.3899,780.8816 C 1269.3158,781.45115 1269.886,782.16337 1270.0997,783.01824 C 1270.3131,783.87313 1270.42,784.49675 1270.42,784.88913 C 1270.42,786.42064 1269.8143,787.67605 1268.6036,788.65539 C 1267.3926,789.63471 1266.0038,790.12436 1264.4364,790.12436 L 1264.4364,790.12436 z M 1268.5514,776.60829 L 1265.2387,776.60829 C 1265.2387,776.60829 1264.1831,757.37479 1267.5357,750.96364 C 1270.8883,744.55249 1278.3947,744.55249 1278.6483,750.96364 C 1278.9026,757.37479 1268.5514,776.60829 1268.5514,776.60829 z "
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF" />
+ </g>
+ <g
+ id="g3236"
+ inkscape:export-xdpi="90.000084"
+ transform="translate(11.4436,33.95071)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:export-ydpi="90.000084"
+ style="fill:none;fill-opacity:0.66459626">
+ <g
+ id="g3238"
+ inkscape:label="Shadow"
+ style="fill:none;fill-opacity:0.66459626;display:inline" />
+ <g
+ id="g3240"
+ inkscape:label="Background"
+ style="fill:none;fill-opacity:0.66459626;display:inline">
+ <g
+ id="g3242"
+ style="fill:none;fill-opacity:0.66459626">
+ <path
+ id="path3244"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ <path
+ id="path3246"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-ydpi="24.028801"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ id="path3248"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ id="path3250"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ id="path3252"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ </g>
+ </g>
+ <g
+ id="g3254"
+ inkscape:label="SeaMonkey"
+ style="fill:none;fill-opacity:0.66459626;display:inline" />
+ </g>
+ <g
+ id="g3575">
+ <path
+ sodipodi:sides="8"
+ id="path3256"
+ inkscape:rounded="0"
+ sodipodi:r1="22.501036"
+ sodipodi:cx="1142.1799"
+ sodipodi:cy="806.06439"
+ inkscape:randomized="0"
+ transform="matrix(1.6768645,-0.2183201,0.2183201,1.6768645,-1641.5081,-627.8452)"
+ sodipodi:arg2="0.91629786"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:flatsided="true"
+ inkscape:export-ydpi="90.000084"
+ sodipodi:arg1="0.52359878"
+ d="M 1161.6664,817.31491 L 1148.0036,827.79872 L 1130.9294,825.55086 L 1120.4456,811.88809 L 1122.6935,794.81387 L 1136.3562,784.33006 L 1153.4304,786.57792 L 1163.9143,800.2407 L 1161.6664,817.31491 z "
+ sodipodi:type="star"
+ style="fill:url(#radialGradient3268-278-827);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3270-289-17);stroke-width:1.98091471;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:r2="20.788244" />
+ <path
+ id="path3258"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:radius="-0.48143509"
+ inkscape:export-xdpi="90.000084"
+ transform="matrix(2.8640361,0,0,3.0356973,-2758.479,-1906.8183)"
+ inkscape:original="M 1115.7813 774.09375 L 1109.75 780.125 L 1109.7188 788.5625 L 1111.8125 790.65625 C 1111.9938 786.42948 1114.6239 782.49644 1118.8438 780.96875 C 1122.9474 779.48305 1127.3599 780.70786 1130.2188 783.65625 L 1130.2188 780.15625 L 1124.2188 774.125 L 1115.7813 774.09375 z "
+ inkscape:export-ydpi="90.000084"
+ d="M 1116,774.5625 L 1110.2188,780.34375 L 1110.1875,788.34375 L 1111.5313,789.6875 C 1112.049,785.64848 1114.601,782.01064 1118.6875,780.53125 C 1122.6273,779.10487 1126.7764,780.13281 1129.75,782.6875 L 1129.75,780.375 L 1124,774.59375 L 1116,774.5625 z "
+ sodipodi:type="inkscape:offset"
+ style="opacity:0.50264551;fill:#ffffff;fill-opacity:0.66459626;fill-rule:nonzero;stroke:none;stroke-width:1.98091471;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3210)" />
+ <path
+ id="path3260"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 445.5334,495.39279 C 444.1442,495.39279 442.9955,494.96534 442.0873,494.11048 C 441.1795,493.25561 440.7253,492.13363 440.7253,490.74449 C 440.7611,490.49559 440.7792,490.29996 440.7792,490.15755 C 440.9571,488.62608 441.5986,487.43291 442.703,486.57801 C 443.8072,485.72315 445.1428,485.2957 446.7089,485.2957 C 447.6348,485.2957 448.561,485.58047 449.4869,486.15003 C 450.4129,486.71958 450.983,487.4318 451.1967,488.28666 C 451.4101,489.14156 451.517,489.76518 451.517,490.15755 C 451.517,491.68906 450.9113,492.94448 449.7007,493.92382 C 448.4897,494.90313 447.1008,495.39279 445.5334,495.39279 L 445.5334,495.39279 z M 449.6484,481.87672 L 446.3357,481.87672 C 446.3357,481.87672 445.2801,462.64322 448.6327,456.23207 C 451.9853,449.82092 459.4918,449.82092 459.7454,456.23207 C 459.9996,462.64322 449.6484,481.87672 449.6484,481.87672 z "
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#6a0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF" />
+ <path
+ id="path3262"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 443.9325,493.39172 C 442.5432,493.39172 441.3946,492.96427 440.4864,492.10941 C 439.5786,491.25455 439.1243,490.13256 439.1243,488.74342 C 439.1602,488.49453 439.1783,488.29889 439.1783,488.15649 C 439.3561,486.62501 439.9977,485.43184 441.1021,484.57694 C 442.2062,483.72208 443.5419,483.29463 445.1079,483.29463 C 446.0339,483.29463 446.9601,483.57941 447.886,484.14896 C 448.8119,484.71851 449.3821,485.43073 449.5958,486.2856 C 449.8092,487.14049 449.9161,487.76411 449.9161,488.15649 C 449.9161,489.688 449.3104,490.94341 448.0997,491.92275 C 446.8887,492.90207 445.4999,493.39172 443.9325,493.39172 L 443.9325,493.39172 z M 448.0475,479.87565 L 444.7348,479.87565 C 444.7348,479.87565 443.6792,460.64215 447.0318,454.231 C 450.3844,447.81985 457.8908,447.81985 458.1444,454.231 C 458.3987,460.64215 448.0475,479.87565 448.0475,479.87565 z "
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF" />
+ </g>
+ <g
+ id="g3292"
+ inkscape:export-xdpi="90.000084"
+ transform="translate(3.9574,190.89147)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:export-ydpi="90.000084"
+ style="fill:none;fill-opacity:0.66459626">
+ <g
+ id="g3294"
+ inkscape:label="Shadow"
+ style="fill:none;fill-opacity:0.66459626;display:inline" />
+ <g
+ id="g3296"
+ inkscape:label="Background"
+ style="fill:none;fill-opacity:0.66459626;display:inline">
+ <g
+ id="g3298"
+ style="fill:none;fill-opacity:0.66459626">
+ <path
+ id="path3300"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ <path
+ id="path3302"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-ydpi="24.028801"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path3304"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path3306"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ id="path3308"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ </g>
+ </g>
+ <g
+ id="g3310"
+ inkscape:label="SeaMonkey"
+ style="fill:none;fill-opacity:0.66459626;display:inline" />
+ </g>
+ <g
+ id="g4481"
+ transform="translate(-3.2654421e-5,4.0000063)">
+ <path
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#380000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF"
+ d="M 437.04481,646.62002 C 435.65561,646.62002 434.50691,646.19262 433.59871,645.33772 C 432.69091,644.48292 432.23671,643.36092 432.23671,641.97172 C 432.27251,641.72282 432.29061,641.52722 432.29061,641.38482 C 432.46851,639.85332 433.11001,638.66022 434.21441,637.80532 C 435.31861,636.95042 436.65421,636.52292 438.22031,636.52292 C 439.14621,636.52292 440.07241,636.80772 440.99831,637.37732 C 441.92431,637.94682 442.49441,638.65902 442.70811,639.51392 C 442.92151,640.36882 443.02841,640.99242 443.02841,641.38482 C 443.02841,642.91632 442.42271,644.17172 441.21211,645.15112 C 440.00111,646.13042 438.61221,646.62002 437.04481,646.62002 L 437.04481,646.62002 z M 441.15981,633.10398 L 437.84711,633.10398 C 437.84711,633.10398 436.79151,613.87048 440.14411,607.45933 C 443.49671,601.04818 451.00321,601.04818 451.25681,607.45933 C 451.51101,613.87048 441.15981,633.10398 441.15981,633.10398 z "
+ inkscape:export-ydpi="90.000084"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:export-xdpi="90.000084"
+ id="path3316" />
+ <path
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF"
+ d="M 437.04491,646.62006 C 435.65561,646.62006 434.50701,646.19256 433.59881,645.33776 C 432.69101,644.48286 432.23671,643.36086 432.23671,641.97176 C 432.27261,641.72286 432.29071,641.52716 432.29071,641.38476 C 432.46851,639.85336 433.11011,638.66016 434.21451,637.80526 C 435.31861,636.95036 436.65431,636.52296 438.22031,636.52296 C 439.14631,636.52296 440.07251,636.80776 440.99841,637.37726 C 441.92431,637.94686 442.49451,638.65906 442.70821,639.51386 C 442.92161,640.36876 443.02851,640.99246 443.02851,641.38476 C 443.02851,642.91626 442.42281,644.17176 441.21211,645.15106 C 440.00111,646.13036 438.61231,646.62006 437.04491,646.62006 L 437.04491,646.62006 z M 441.15991,633.10395 L 437.84721,633.10395 C 437.84721,633.10395 436.79161,613.87045 440.14421,607.4593 C 443.49681,601.04815 451.00321,601.04815 451.25681,607.4593 C 451.51111,613.87045 441.15991,633.10395 441.15991,633.10395 z "
+ inkscape:export-ydpi="90.000084"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:export-xdpi="90.000084"
+ id="path3318" />
+ <path
+ sodipodi:nodetypes="cssscsscsssc"
+ style="fill:url(#linearGradient4489);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4491);stroke-width:3.32711434;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3331"
+ d="M 440.83622,663.56361 C 442.32178,663.66286 444.14138,662.75077 445.47289,660.82071 C 446.94048,658.69292 473.81258,611.17134 475.36668,607.93237 C 476.85822,604.82401 476.74776,601.65433 476.60935,599.38987 C 476.47095,597.12553 475.73166,595.52253 474.24644,594.81592 C 473.98193,594.49046 472.78717,594.20942 470.8967,593.97076 C 469.0059,593.73214 414.07358,593.59959 412.25731,593.79289 C 410.44103,593.98623 409.33277,594.20922 409.16708,594.45968 C 407.75971,594.9477 407.05403,596.39147 406.91463,598.54481 C 406.77522,600.69805 406.54831,603.83822 408.05982,606.88682 C 409.7653,610.32596 435.13521,658.43846 436.34728,660.71916 C 437.60426,663.08384 439.67539,663.54831 440.83622,663.56361 z " />
+ <path
+ transform="translate(-0.6415603,-8.0051349)"
+ d="M 442.375,608.65625 C 440.58208,608.65631 439.0046,609.25144 437.84375,610.4375 C 436.6814,611.62516 436.09374,613.23495 436.09375,615.09375 C 436.09374,617.11332 436.62483,620.16749 437.65625,624.46875 L 439.34375,631.625 C 440.18673,635.12667 440.69534,638.11425 440.84375,640.53125 C 440.88085,640.97058 441.24662,641.30925 441.6875,641.3125 L 443.125,641.3125 C 443.56588,641.30925 443.93165,640.97058 443.96875,640.53125 C 444.24976,637.07256 444.71523,634.0993 445.3125,631.625 L 447.0625,624.46875 C 448.15938,619.90569 448.68747,616.84161 448.6875,615 C 448.68748,613.23038 448.10051,611.6631 446.90625,610.46875 C 445.71376,609.27633 444.13658,608.65627 442.375,608.65625 z M 442.4375,644.3125 C 440.75619,644.31251 439.23091,644.93172 438.0625,646.125 C 436.89935,647.3129 436.31249,648.80998 436.3125,650.46875 C 436.31249,652.12328 436.91039,653.62917 438.09375,654.8125 C 439.27103,655.98979 440.77083,656.62499 442.4375,656.625 C 444.10416,656.625 445.60395,655.9898 446.78125,654.8125 C 447.95851,653.63522 448.59372,652.13542 448.59375,650.46875 C 448.59373,648.78327 447.96032,647.27288 446.78125,646.09375 C 445.5979,644.91042 444.09202,644.31251 442.4375,644.3125 z "
+ id="path4343"
+ style="font-size:67.04067993px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter4477);font-family:Times New Roman"
+ inkscape:original="M 442.375 609.5 C 440.76008 609.50005 439.4726 610.0056 438.46875 611.03125 C 437.46488 612.05698 436.93749 613.41343 436.9375 615.09375 C 436.93749 616.94875 437.47432 620.00393 438.5 624.28125 L 440.1875 631.4375 C 441.03859 634.97287 441.53474 637.98096 441.6875 640.46875 L 443.125 640.46875 C 443.40869 636.97707 443.85768 633.96898 444.46875 631.4375 L 446.21875 624.28125 C 447.30989 619.74207 447.84372 616.65861 447.84375 615 C 447.84373 613.40695 447.34907 612.09915 446.3125 611.0625 C 445.27589 610.02595 443.94625 609.50002 442.375 609.5 z M 442.4375 645.15625 C 440.95352 645.15626 439.71319 645.67123 438.6875 646.71875 C 437.66181 647.76627 437.15624 649.00661 437.15625 650.46875 C 437.15624 651.9309 437.65089 653.18217 438.6875 654.21875 C 439.72409 655.25535 440.97535 655.78124 442.4375 655.78125 C 443.89964 655.78125 445.15088 655.25537 446.1875 654.21875 C 447.22408 653.18215 447.74997 651.93091 447.75 650.46875 C 447.74998 648.98478 447.22408 647.72414 446.1875 646.6875 C 445.15089 645.65091 443.89965 645.15626 442.4375 645.15625 z "
+ inkscape:radius="0.85297155"
+ sodipodi:type="inkscape:offset" />
+ <text
+ y="646.89508"
+ x="430.57016"
+ style="font-size:67.04067993px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Times New Roman"
+ xml:space="preserve"
+ id="text3333"><tspan
+ y="646.89508"
+ x="430.57016"
+ sodipodi:role="line"
+ id="tspan3335">!</tspan></text>
+ </g>
+ <g
+ id="g2416"
+ transform="translate(-863.88271,-299.22165)">
+ <g
+ id="g2326"
+ transform="translate(722.2492,492.34391)"
+ inkscape:label="Background"
+ style="display:inline">
+ <g
+ id="g2328">
+ <path
+ id="path2330"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ <path
+ id="path2332"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-ydpi="24.028801"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient2340);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ id="path2334"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient2342);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ id="path2336"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ id="path2338"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ </g>
+ </g>
+ <g
+ id="g2363"
+ transform="translate(-487.38746,161.58191)">
+ <path
+ id="path2365"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 1643.7265,792.20405 C 1642.3373,792.20405 1641.1886,791.77665 1640.2804,790.92175 C 1639.3726,790.06695 1638.9184,788.94495 1638.9184,787.55575 C 1638.9542,787.30685 1638.9723,787.11125 1638.9723,786.96885 C 1639.1502,785.43735 1639.7917,784.24425 1640.8961,783.38935 C 1642.0003,782.53445 1643.3359,782.10695 1644.902,782.10695 C 1645.8279,782.10695 1646.7541,782.39175 1647.68,782.96135 C 1648.606,783.53085 1649.1761,784.24305 1649.3898,785.09795 C 1649.6032,785.95285 1649.7101,786.57645 1649.7101,786.96885 C 1649.7101,788.50035 1649.1044,789.75575 1647.8938,790.73515 C 1646.6828,791.71445 1645.2939,792.20405 1643.7265,792.20405 L 1643.7265,792.20405 z M 1647.8415,778.68801 L 1644.5288,778.68801 C 1644.5288,778.68801 1643.4732,759.45451 1646.8258,753.04336 C 1650.1784,746.63221 1657.6849,746.63221 1657.9385,753.04336 C 1658.1927,759.45451 1647.8415,778.68801 1647.8415,778.68801 z "
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#380000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF" />
+ <path
+ id="path2367"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ sodipodi:nodetypes="cscssssssscccczzc"
+ inkscape:export-ydpi="90.000084"
+ d="M 1642.1256,790.20305 C 1640.7363,790.20305 1639.5877,789.77555 1638.6795,788.92075 C 1637.7717,788.06585 1637.3174,786.94385 1637.3174,785.55475 C 1637.3533,785.30585 1637.3714,785.11015 1637.3714,784.96775 C 1637.5492,783.43635 1638.1908,782.24315 1639.2952,781.38825 C 1640.3993,780.53335 1641.735,780.10595 1643.301,780.10595 C 1644.227,780.10595 1645.1532,780.39075 1646.0791,780.96025 C 1647.005,781.52985 1647.5752,782.24205 1647.7889,783.09685 C 1648.0023,783.95175 1648.1092,784.57545 1648.1092,784.96775 C 1648.1092,786.49925 1647.5035,787.75475 1646.2928,788.73405 C 1645.0818,789.71335 1643.693,790.20305 1642.1256,790.20305 L 1642.1256,790.20305 z M 1646.2406,776.68694 L 1642.9279,776.68694 C 1642.9279,776.68694 1641.8723,757.45344 1645.2249,751.04229 C 1648.5775,744.63114 1656.0839,744.63114 1656.3375,751.04229 C 1656.5918,757.45344 1646.2406,776.68694 1646.2406,776.68694 z "
+ style="font-size:24px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sprint SF" />
+ <g
+ transform="translate(165.08216,-220.10955)"
+ id="g2369">
+ <g
+ id="g2371"
+ inkscape:export-xdpi="90.000084"
+ transform="translate(1044.5545,550.87155)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\inkscape images\seamonkey-console3.svg.png"
+ inkscape:export-ydpi="90.000084"
+ style="fill:none;fill-opacity:0.66459626">
+ <g
+ id="g2373"
+ inkscape:label="Shadow"
+ style="fill:none;fill-opacity:0.66459626;display:inline" />
+ <g
+ id="g2375"
+ inkscape:label="Background"
+ style="fill:none;fill-opacity:0.66459626;display:inline">
+ <g
+ id="g2377"
+ style="fill:none;fill-opacity:0.66459626">
+ <path
+ id="path2379"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ <path
+ id="path2381"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-ydpi="24.028801"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2383"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2385"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ id="path2387"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.66459626;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806" />
+ </g>
+ </g>
+ <g
+ id="g2389"
+ inkscape:label="SeaMonkey"
+ style="fill:none;fill-opacity:0.66459626;display:inline" />
+ </g>
+ <g
+ id="g2391"
+ transform="matrix(3.3271144,0,0,3.3271144,-2377.9025,-1763.4591)">
+ <path
+ id="path2393"
+ d="M 1160.5136,816.66761 C 1160.0671,816.63778 1159.5202,816.91192 1159.12,817.49202 C 1158.6789,818.13155 1150.6022,832.41467 1150.1351,833.38818 C 1149.6868,834.32243 1149.72,835.27511 1149.7616,835.95572 C 1149.8032,836.63629 1150.0254,837.11809 1150.4718,837.33047 C 1150.5513,837.42829 1150.9104,837.51276 1151.4786,837.58449 C 1152.0469,837.65621 1168.5574,837.69605 1169.1033,837.63795 C 1169.6492,837.57984 1169.9823,837.51282 1170.0321,837.43754 C 1170.4551,837.29086 1170.6672,836.85692 1170.7091,836.20971 C 1170.751,835.56253 1170.8192,834.61872 1170.3649,833.70243 C 1169.8523,832.66876 1162.2271,818.20803 1161.8628,817.52254 C 1161.485,816.81181 1160.8625,816.67221 1160.5136,816.66761 z "
+ style="opacity:1;fill:url(#linearGradient2399);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient2401);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:nodetypes="cssscsscsssc" />
+ <text
+ id="text2395"
+ style="font-size:20.14979744px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Times New Roman"
+ xml:space="preserve"
+ x="1157.0756"
+ y="835.06042"><tspan
+ y="835.06042"
+ id="tspan2397"
+ x="1157.0756"
+ sodipodi:role="line">!</tspan></text>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/README b/comm/suite/branding/seamonkey/icons/template/svg/README
new file mode 100644
index 0000000000..eab645b7f7
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/README
@@ -0,0 +1,15 @@
+suite/branding/seamonkey/icons/svg:
+This directory contains the base SVG images used for creation of the SeaMonkey
+window icons. The SVG images are not directly used in the software, but stored
+here for reference and as a base for possible later revisions or new icons
+that may be created in the future.
+
+All image files in this directory and images derived from them are licensed
+under the MPL 2 as follows:
+
+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/.
+
+Note: The images may contain parts of the SeaMonkey logo, which by itself is a
+registered trademark of the Mozilla Foundation.
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/abcardWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/abcardWindow.svg
new file mode 100644
index 0000000000..ab591e3012
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/abcardWindow.svg
@@ -0,0 +1,723 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docname="seamonkeyabcardwindow.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\abcardwindow">
+ <defs
+ id="defs4">
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514002"
+ height="1.169028"
+ id="filter3395">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397" />
+ </filter>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <filter
+ inkscape:collect="always"
+ x="-0.12278381"
+ width="1.2455676"
+ y="-0.142712"
+ height="1.285424"
+ id="filter6228">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="2.9049243"
+ id="feGaussianBlur6230" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter6850">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient7470"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient7472"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <filter
+ inkscape:collect="always"
+ x="-0.092254387"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.55089535"
+ inkscape:cx="533.72967"
+ inkscape:cy="512.21606"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ gridempspacing="10"
+ showgrid="true"
+ inkscape:grid-points="false"
+ gridspacingy="0.5px"
+ gridspacingx="0.5px"
+ inkscape:grid-bbox="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g7546"
+ transform="translate(-243.72613,-1399.3888)">
+ <g
+ id="g7434"
+ transform="translate(152.00085,1510.1315)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g7436" />
+ <g
+ style="display:inline"
+ id="g7438"
+ inkscape:label="Background">
+ <g
+ id="g7440">
+ <path
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path7442"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:url(#linearGradient7470);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path7444"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:url(#linearGradient7472);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path7446"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path7448"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path7450"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g7452" />
+ </g>
+ <path
+ id="path7454"
+ d="M 575.81447,1912.2934 C 569.50664,1915.2637 566.46024,1918.2359 563.07906,1927.6409 C 563.07906,1927.6409 559.73241,1940.645 564.95479,1955.2478 C 565.78728,1957.5756 569.10613,1962.0447 569.1517,1962.0833 C 571.71117,1964.2519 574.35742,1964.8739 577.08523,1965.9532 C 577.20401,1966.0002 577.32409,1966.0393 577.44314,1966.0855 C 578.41246,1966.4615 579.40935,1966.8227 580.39584,1967.1439 C 582.29017,1967.7608 584.20502,1968.2561 586.15213,1968.6655 C 586.38016,1968.7134 586.60946,1968.7527 586.83811,1968.7978 C 587.64794,1968.9573 588.46685,1969.105 589.28379,1969.2278 C 589.4422,1969.2515 589.60234,1969.2715 589.76099,1969.294 C 590.60058,1969.4125 591.45033,1969.5124 592.29614,1969.5916 C 592.38575,1969.6 592.4749,1969.6167 592.56457,1969.6248 C 592.64414,1969.6316 592.72356,1969.6181 592.80317,1969.6248 C 593.62035,1969.6936 586.59472,1966.3088 587.41652,1966.3404 C 587.55691,1966.3458 587.69356,1966.3691 587.83407,1966.3735 C 587.9624,1966.3772 588.09338,1966.3706 588.2218,1966.3735 C 588.87786,1966.3888 589.53203,1966.3823 590.19027,1966.3735 C 590.74593,1966.3657 592.25127,1971.8423 592.80817,1971.8173 C 592.90687,1971.8128 593.00769,1971.8221 593.10642,1971.8173 C 593.29517,1971.8076 593.48425,1971.7957 593.6731,1971.7841 C 594.34009,1971.7432 587.17099,1968.2351 587.83908,1968.1691 C 587.96777,1968.1564 588.09809,1968.1495 588.22681,1968.136 C 588.46622,1968.1106 588.70312,1968.0654 588.94262,1968.0368 C 589.65009,1967.9521 590.3524,1967.8853 591.06021,1967.7722 C 591.10052,1967.7657 591.13921,1967.7455 591.17951,1967.7391 C 591.35724,1967.7101 591.53864,1967.6705 591.71637,1967.6399 C 592.48653,1967.5069 593.27314,1967.3771 594.04274,1967.2098 C 594.0828,1967.2012 594.12199,1967.1855 594.16205,1967.1768 C 594.26129,1967.1549 594.36107,1967.133 594.4603,1967.1106 C 595.34806,1966.9104 596.22901,1966.6957 597.11475,1966.4491 C 598.02964,1966.1942 598.94723,1965.9272 599.85868,1965.6221 C 599.92742,1965.5992 599.99874,1965.5793 600.06746,1965.556 C 601.04816,1965.2236 602.01528,1964.8559 602.99034,1964.4645 C 603.93583,1964.0848 604.87641,1963.6666 605.79392,1963.2407 C 605.82275,1963.2271 605.85458,1963.2212 605.88339,1963.2076 C 606.27747,1963.0238 606.65777,1962.8374 607.04658,1962.6452 C 607.29201,1962.5224 607.51907,1962.3745 607.76239,1962.2484 C 608.17219,1962.0359 608.58146,1961.8409 608.98523,1961.6199 C 609.62871,1961.2676 613.289,1956.6003 614.23328,1954.4766 C 619.85717,1941.8287 615.57161,1927.6409 615.57161,1927.6409 C 612.30068,1918.854 610.16362,1915.7943 602.86603,1912.2934 C 594.35568,1908.2107 583.20855,1908.8115 575.81447,1912.2934 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ style="fill:#000000;fill-opacity:0.62111798;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter6228)"
+ d="M 238.5625,1732.9688 C 231.80317,1732.9687 226.34375,1739.4288 226.34375,1747.4688 L 223.1875,1770.1563 C 223.1875,1772.0081 223.49538,1773.7505 224.03125,1775.375 C 226.16764,1776.4206 228.34932,1777.3555 230.59375,1778.1563 C 230.71821,1778.2007 230.84402,1778.2376 230.96875,1778.2813 C 231.98439,1778.6365 233.02888,1778.9778 234.0625,1779.2813 C 236.04733,1779.8641 238.05363,1780.332 240.09375,1780.7188 C 240.33268,1780.7641 240.57293,1780.8012 240.8125,1780.8438 C 241.66102,1780.9945 242.51904,1781.1339 243.375,1781.25 C 243.54099,1781.2724 243.70877,1781.2913 243.875,1781.3125 C 244.7547,1781.4245 245.64504,1781.5188 246.53125,1781.5938 C 246.62515,1781.6017 246.71855,1781.6174 246.8125,1781.625 C 246.89588,1781.6315 246.97908,1781.6187 247.0625,1781.625 C 247.91872,1781.69 248.76395,1781.7515 249.625,1781.7813 C 249.77211,1781.7864 249.91528,1781.8084 250.0625,1781.8125 C 250.19697,1781.816 250.33419,1781.8097 250.46875,1781.8125 C 251.15616,1781.8269 251.84156,1781.8208 252.53125,1781.8125 C 253.11346,1781.8051 253.69775,1781.7737 254.28125,1781.75 C 254.38467,1781.7458 254.4903,1781.7546 254.59375,1781.75 C 254.79152,1781.7409 254.98962,1781.7297 255.1875,1781.7188 C 255.88636,1781.6802 256.58125,1781.6248 257.28125,1781.5625 C 257.41608,1781.5505 257.55263,1781.5441 257.6875,1781.5313 C 257.93834,1781.5073 258.18656,1781.4645 258.4375,1781.4375 C 259.17876,1781.3575 259.91463,1781.2944 260.65625,1781.1875 C 260.69847,1781.1814 260.73902,1781.1623 260.78125,1781.1563 C 260.96746,1781.129 261.15753,1781.0914 261.34375,1781.0625 C 262.15069,1780.9369 262.97488,1780.8142 263.78125,1780.6563 C 263.82321,1780.6481 263.86428,1780.6332 263.90625,1780.625 C 264.01022,1780.6043 264.11478,1780.5836 264.21875,1780.5625 C 265.14891,1780.3733 266.07195,1780.1705 267,1779.9375 C 267.95858,1779.6967 268.92001,1779.4445 269.875,1779.1563 C 269.94701,1779.1346 270.02175,1779.1157 270.09375,1779.0938 C 271.12129,1778.7797 272.13462,1778.4323 273.15625,1778.0625 C 274.1469,1777.7038 275.13241,1777.3087 276.09375,1776.9063 C 276.12395,1776.8935 276.15731,1776.8878 276.1875,1776.875 C 276.60039,1776.7013 276.99887,1776.5252 277.40625,1776.3438 C 277.66339,1776.2277 277.90131,1776.0879 278.15625,1775.9688 C 278.50067,1775.8078 278.84648,1775.661 279.1875,1775.5 C 279.67899,1773.9309 279.96875,1772.2506 279.96875,1770.4688 L 276.71875,1747.4688 C 276.71875,1739.4287 271.25933,1732.9688 264.5,1732.9688 L 238.5625,1732.9688 z "
+ id="path7456"
+ transform="matrix(1,0,0,0.9389427,338.46002,336.25238)" />
+ <path
+ id="path7458"
+ d="M 583.11571,1960.4203 C 577.5558,1960.4202 573.17821,1967.2118 573.17821,1975.8265 C 573.17765,1976.0244 573.16814,1976.2252 573.14696,1976.4203 L 569.99071,2005.8578 C 569.99288,2006.1454 570.01011,2006.4181 570.02196,2006.7015 C 582.58262,2010.9182 596.53759,2011.2358 609.99071,2006.7328 C 609.99108,2006.7015 609.99045,2006.6703 609.99071,2006.639 L 606.74071,1976.8265 C 606.71894,1976.6258 606.70941,1976.4362 606.70946,1976.2328 C 606.70947,1967.6181 602.33185,1960.7953 596.77196,1960.7953 L 583.11571,1960.4203 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path7460"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(0.3953873,0,0,0.5094214,454.70787,1765.1701)" />
+ <path
+ id="path7462"
+ d="M 598.80321,1961.9828 L 602.45946,2008.7328 C 605.47791,2008.1219 608.47761,2007.3123 611.45946,2006.2328 C 613.40412,2005.5287 615.28536,2004.7279 617.11571,2003.8578 L 612.99071,1977.764 C 612.95977,1977.5665 612.92815,1977.3393 612.92821,1977.139 C 612.9282,1968.6585 606.70305,1961.9828 598.80321,1961.9828 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path7464"
+ d="M 580.49071,1961.6703 C 572.59087,1961.6703 566.33446,1968.4081 566.33446,1976.9203 C 566.33452,1977.1213 566.33415,1977.3157 566.30321,1977.514 L 562.20946,2003.514 C 566.84537,2005.793 571.76001,2007.4846 576.83446,2008.5765 L 580.49071,1961.6703 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(0.3953873,0,0,0.5094214,454.70787,1765.1701)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path7466"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.3953873,0,0,0.3980335,485.21164,1824.6047)"
+ id="path7468"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 574.06634,1928.9264 C 574.88423,1926.0327 574.38715,1927.7489 576.39779,1925.5271 C 578.29499,1923.4306 580.19327,1922.3591 582.55828,1920.696 C 582.95711,1920.4155 583.73341,1920.429 584.13292,1920.1495 C 586.69273,1918.3588 586.40771,1920.2489 589.58336,1919.8681 C 592.13098,1919.5626 590.61567,1917.899 592.90106,1918.7919 C 595.52665,1919.8176 596.63409,1920.5786 598.70922,1922.4612 C 600.60136,1924.1777 600.95049,1924.2219 602.63643,1926.1725 C 604.20037,1927.9819 603.45882,1926.0757 604.67386,1928.1094 C 605.68759,1929.806 609.25437,1930.6292 609.6092,1932.8178 C 609.8667,1934.406 608.99176,1934.8228 607.75497,1933.9836 C 603.67053,1931.2122 599.84509,1928.4213 595.54657,1925.9584 C 592.62997,1924.2872 591.29315,1923.6634 589.55387,1926.6531 C 587.81495,1929.6422 586.56405,1933.1461 584.59247,1935.9959 C 581.29604,1940.7608 583.14307,1931.1374 583.14307,1929.8632 C 583.14307,1926.4912 580.80191,1928.5654 578.98998,1929.8632 C 577.30032,1931.0734 574.59528,1932.3733 572.55131,1932.666 C 570.49955,1932.9599 573.30258,1929.3683 571.28336,1928.9494 C 570.13054,1928.7103 572.16502,1930.9152 572.53629,1930.4114 C 572.78651,1930.0718 573.75044,1929.2059 574.06634,1928.9264 z "
+ id="path7476" />
+ <path
+ id="path7474"
+ d="M 567.47842,1929.3122 C 568.29631,1926.4185 570.28981,1923.7962 572.30045,1921.5744 C 574.19765,1919.4779 577.57266,1918.063 579.93767,1916.3999 C 580.3365,1916.1194 580.7367,1915.8409 581.13621,1915.5614 C 583.69602,1913.7707 586.04374,1912.1572 589.21939,1911.7764 C 591.76701,1911.4709 593.81864,1912.8 596.10403,1913.6929 C 598.72962,1914.7186 601.22015,1916.5536 603.29528,1918.4362 C 605.18742,1920.1527 607.01671,1921.8757 608.70265,1923.8263 C 610.26659,1925.6357 611.41771,1927.7337 612.63275,1929.7674 C 613.64648,1931.464 614.69427,1933.9694 613.83129,1935.8522 C 612.69068,1938.3407 608.99176,1934.8228 607.75497,1933.9836 C 603.67053,1931.2122 599.84509,1928.4213 595.54657,1925.9584 C 592.62997,1924.2872 591.29315,1923.6634 589.55387,1926.6531 C 587.81495,1929.6422 586.56405,1933.1461 584.59247,1935.9959 C 581.29604,1940.7608 583.14307,1931.1374 583.14307,1929.8632 C 583.14307,1926.4912 580.80191,1928.5654 578.98998,1929.8632 C 577.30032,1931.0734 574.59528,1932.3733 572.55131,1932.666 C 570.49955,1932.9599 568.24335,1933.3724 566.22413,1932.9535 C 565.07131,1932.7144 566.15946,1930.6545 566.53073,1930.1507 C 566.78095,1929.8111 567.16252,1929.5917 567.47842,1929.3122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <g
+ id="g3687">
+ <g
+ style="fill:none"
+ transform="translate(95.938042,113.29346)"
+ id="g2654">
+ <g
+ id="g2656"
+ inkscape:label="Shadow"
+ style="fill:none;display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g2658"
+ style="fill:none;display:inline">
+ <g
+ style="fill:none"
+ id="g2660">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2662"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2664"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2666"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2668"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2670"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g2672"
+ inkscape:label="SeaMonkey"
+ style="fill:none;display:inline" />
+ </g>
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 519.75166,515.45536 C 513.44383,518.42566 510.39743,521.39786 507.01625,530.80286 C 507.01625,530.80286 503.6696,543.80696 508.89198,558.40976 C 509.72447,560.73756 513.04332,565.20666 513.08889,565.24526 C 515.64836,567.41386 518.29461,568.03586 521.02242,569.11516 C 521.1412,569.16216 521.26128,569.20126 521.38033,569.24746 C 522.34965,569.62346 523.34654,569.98466 524.33303,570.30586 C 526.22736,570.92276 528.14221,571.41806 530.08932,571.82746 C 530.31735,571.87536 530.54665,571.91466 530.7753,571.95976 C 531.58513,572.11926 532.40404,572.26696 533.22098,572.38976 C 533.37939,572.41346 533.53953,572.43346 533.69818,572.45596 C 534.53777,572.57446 535.38752,572.67436 536.23333,572.75356 C 536.32294,572.76196 536.41209,572.77866 536.50176,572.78676 C 536.58133,572.79356 536.66075,572.78006 536.74036,572.78676 C 537.55754,572.85556 530.53191,569.47076 531.35371,569.50236 C 531.4941,569.50776 531.63075,569.53106 531.77126,569.53546 C 531.89959,569.53916 532.03057,569.53256 532.15899,569.53546 C 532.81505,569.55076 533.46922,569.54426 534.12746,569.53546 C 534.68312,569.52766 536.18846,575.00426 536.74536,574.97926 C 536.84406,574.97476 536.94488,574.98406 537.04361,574.97926 C 537.23236,574.96956 537.42144,574.95766 537.61029,574.94606 C 538.27728,574.90516 531.10818,571.39706 531.77627,571.33106 C 531.90496,571.31836 532.03528,571.31146 532.164,571.29796 C 532.40341,571.27256 532.64031,571.22736 532.87981,571.19876 C 533.58728,571.11406 534.28959,571.04726 534.9974,570.93416 C 535.03771,570.92766 535.0764,570.90746 535.1167,570.90106 C 535.29443,570.87206 535.47583,570.83246 535.65356,570.80186 C 536.42372,570.66886 537.21033,570.53906 537.97993,570.37176 C 538.01999,570.36316 538.05918,570.34746 538.09924,570.33876 C 538.19848,570.31686 538.29826,570.29496 538.39749,570.27256 C 539.28525,570.07236 540.1662,569.85766 541.05194,569.61106 C 541.96683,569.35616 542.88442,569.08916 543.79587,568.78406 C 543.86461,568.76116 543.93593,568.74126 544.00465,568.71796 C 544.98535,568.38556 545.95247,568.01786 546.92753,567.62646 C 547.87302,567.24676 548.8136,566.82856 549.73111,566.40266 C 549.75994,566.38906 549.79177,566.38316 549.82058,566.36956 C 550.21466,566.18576 550.59496,565.99936 550.98377,565.80716 C 551.2292,565.68436 551.45626,565.53646 551.69958,565.41036 C 552.10938,565.19786 552.51865,565.00286 552.92242,564.78186 C 553.5659,564.42956 557.22619,559.76226 558.17047,557.63856 C 563.79436,544.99066 559.5088,530.80286 559.5088,530.80286 C 556.23787,522.01596 554.10081,518.95626 546.80322,515.45536 C 538.29287,511.37266 527.14574,511.97346 519.75166,515.45536 z "
+ id="path2674" />
+ <path
+ transform="matrix(1,0,0,0.9389427,282.39721,-1060.5856)"
+ id="path2676"
+ d="M 238.5625,1732.9688 C 231.80317,1732.9687 226.34375,1739.4288 226.34375,1747.4688 L 223.1875,1770.1563 C 223.1875,1772.0081 223.49538,1773.7505 224.03125,1775.375 C 226.16764,1776.4206 228.34932,1777.3555 230.59375,1778.1563 C 230.71821,1778.2007 230.84402,1778.2376 230.96875,1778.2813 C 231.98439,1778.6365 233.02888,1778.9778 234.0625,1779.2813 C 236.04733,1779.8641 238.05363,1780.332 240.09375,1780.7188 C 240.33268,1780.7641 240.57293,1780.8012 240.8125,1780.8438 C 241.66102,1780.9945 242.51904,1781.1339 243.375,1781.25 C 243.54099,1781.2724 243.70877,1781.2913 243.875,1781.3125 C 244.7547,1781.4245 245.64504,1781.5188 246.53125,1781.5938 C 246.62515,1781.6017 246.71855,1781.6174 246.8125,1781.625 C 246.89588,1781.6315 246.97908,1781.6187 247.0625,1781.625 C 247.91872,1781.69 248.76395,1781.7515 249.625,1781.7813 C 249.77211,1781.7864 249.91528,1781.8084 250.0625,1781.8125 C 250.19697,1781.816 250.33419,1781.8097 250.46875,1781.8125 C 251.15616,1781.8269 251.84156,1781.8208 252.53125,1781.8125 C 253.11346,1781.8051 253.69775,1781.7737 254.28125,1781.75 C 254.38467,1781.7458 254.4903,1781.7546 254.59375,1781.75 C 254.79152,1781.7409 254.98962,1781.7297 255.1875,1781.7188 C 255.88636,1781.6802 256.58125,1781.6248 257.28125,1781.5625 C 257.41608,1781.5505 257.55263,1781.5441 257.6875,1781.5313 C 257.93834,1781.5073 258.18656,1781.4645 258.4375,1781.4375 C 259.17876,1781.3575 259.91463,1781.2944 260.65625,1781.1875 C 260.69847,1781.1814 260.73902,1781.1623 260.78125,1781.1563 C 260.96746,1781.129 261.15753,1781.0914 261.34375,1781.0625 C 262.15069,1780.9369 262.97488,1780.8142 263.78125,1780.6563 C 263.82321,1780.6481 263.86428,1780.6332 263.90625,1780.625 C 264.01022,1780.6043 264.11478,1780.5836 264.21875,1780.5625 C 265.14891,1780.3733 266.07195,1780.1705 267,1779.9375 C 267.95858,1779.6967 268.92001,1779.4445 269.875,1779.1563 C 269.94701,1779.1346 270.02175,1779.1157 270.09375,1779.0938 C 271.12129,1778.7797 272.13462,1778.4323 273.15625,1778.0625 C 274.1469,1777.7038 275.13241,1777.3087 276.09375,1776.9063 C 276.12395,1776.8935 276.15731,1776.8878 276.1875,1776.875 C 276.60039,1776.7013 276.99887,1776.5252 277.40625,1776.3438 C 277.66339,1776.2277 277.90131,1776.0879 278.15625,1775.9688 C 278.50067,1775.8078 278.84648,1775.661 279.1875,1775.5 C 279.67899,1773.9309 279.96875,1772.2506 279.96875,1770.4688 L 276.71875,1747.4688 C 276.71875,1739.4287 271.25933,1732.9688 264.5,1732.9688 L 238.5625,1732.9688 z "
+ style="fill:#000000;fill-opacity:0.62111798;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter6228)" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 527.0529,563.58226 C 521.49299,563.58216 517.1154,570.37376 517.1154,578.98846 C 517.11484,579.18636 517.10533,579.38716 517.08415,579.58226 L 513.9279,609.01976 C 513.93007,609.30736 513.9473,609.58006 513.95915,609.86346 C 526.51981,614.08016 540.47478,614.39776 553.9279,609.89476 C 553.92827,609.86346 553.92764,609.83226 553.9279,609.80096 L 550.6779,579.98846 C 550.65613,579.78776 550.6466,579.59816 550.64665,579.39476 C 550.64666,570.78006 546.26904,563.95726 540.70915,563.95726 L 527.0529,563.58226 z "
+ id="path2678" />
+ <path
+ transform="matrix(0.3953873,0,0,0.5094214,398.64506,368.33206)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path2680"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 542.7404,565.14476 L 546.39665,611.89476 C 549.4151,611.28386 552.4148,610.47426 555.39665,609.39476 C 557.34131,608.69066 559.22255,607.88986 561.0529,607.01976 L 556.9279,580.92596 C 556.89696,580.72846 556.86534,580.50126 556.8654,580.30096 C 556.86539,571.82046 550.64024,565.14476 542.7404,565.14476 z "
+ id="path2682" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 524.4279,564.83226 C 516.52806,564.83226 510.27165,571.57006 510.27165,580.08226 C 510.27171,580.28326 510.27134,580.47766 510.2404,580.67596 L 506.14665,606.67596 C 510.78256,608.95496 515.6972,610.64656 520.77165,611.73846 L 524.4279,564.83226 z "
+ id="path2684" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path2686"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(0.3953873,0,0,0.5094214,398.64506,368.33206)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path2688"
+ transform="matrix(0.3953873,0,0,0.3980335,429.14883,427.76666)" />
+ <path
+ id="path2690"
+ d="M 518.00353,532.08836 C 518.82142,529.19466 518.32434,530.91086 520.33498,528.68906 C 522.23218,526.59256 524.13046,525.52106 526.49547,523.85796 C 526.8943,523.57746 527.6706,523.59096 528.07011,523.31146 C 530.62992,521.52076 530.3449,523.41086 533.52055,523.03006 C 536.06817,522.72456 534.55286,521.06096 536.83825,521.95386 C 539.46384,522.97956 540.57128,523.74056 542.64641,525.62316 C 544.53855,527.33966 544.88768,527.38386 546.57362,529.33446 C 548.13756,531.14386 547.39601,529.23766 548.61105,531.27136 C 549.62478,532.96796 553.19156,533.79116 553.54639,535.97976 C 553.80389,537.56796 552.92895,537.98476 551.69216,537.14556 C 547.60772,534.37416 543.78228,531.58326 539.48376,529.12036 C 536.56716,527.44916 535.23034,526.82536 533.49106,529.81506 C 531.75214,532.80416 530.50124,536.30806 528.52966,539.15786 C 525.23323,543.92276 527.08026,534.29936 527.08026,533.02516 C 527.08026,529.65316 524.7391,531.72736 522.92717,533.02516 C 521.23751,534.23536 518.53247,535.53526 516.4885,535.82796 C 514.43674,536.12186 517.23977,532.53026 515.22055,532.11136 C 514.06773,531.87226 516.10221,534.07716 516.47348,533.57336 C 516.7237,533.23376 517.68763,532.36786 518.00353,532.08836 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 511.41561,532.47416 C 512.2335,529.58046 514.227,526.95816 516.23764,524.73636 C 518.13484,522.63986 521.50985,521.22496 523.87486,519.56186 C 524.27369,519.28136 524.67389,519.00286 525.0734,518.72336 C 527.63321,516.93266 529.98093,515.31916 533.15658,514.93836 C 535.7042,514.63286 537.75583,515.96196 540.04122,516.85486 C 542.66681,517.88056 545.15734,519.71556 547.23247,521.59816 C 549.12461,523.31466 550.9539,525.03766 552.63984,526.98826 C 554.20378,528.79766 555.3549,530.89566 556.56994,532.92936 C 557.58367,534.62596 558.63146,537.13136 557.76848,539.01416 C 556.62787,541.50266 552.92895,537.98476 551.69216,537.14556 C 547.60772,534.37416 543.78228,531.58326 539.48376,529.12036 C 536.56716,527.44916 535.23034,526.82536 533.49106,529.81506 C 531.75214,532.80416 530.50124,536.30806 528.52966,539.15786 C 525.23323,543.92276 527.08026,534.29936 527.08026,533.02516 C 527.08026,529.65316 524.7391,531.72736 522.92717,533.02516 C 521.23751,534.23536 518.53247,535.53526 516.4885,535.82796 C 514.43674,536.12186 512.18054,536.53436 510.16132,536.11546 C 509.0085,535.87636 510.09665,533.81646 510.46792,533.31266 C 510.71814,532.97306 511.09971,532.75366 511.41561,532.47416 z "
+ id="path2692" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/ablistWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/ablistWindow.svg
new file mode 100644
index 0000000000..9b05dfefce
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/ablistWindow.svg
@@ -0,0 +1,4504 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docname="maillistredo.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4">
+ <defs
+ id="defs4">
+ <filter
+ inkscape:collect="always"
+ x="-0.11109908"
+ width="1.2221982"
+ y="-0.084514002"
+ height="1.169028"
+ id="filter3395">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.1250422"
+ id="feGaussianBlur3397" />
+ </filter>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <filter
+ inkscape:collect="always"
+ id="filter6850">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="4.2275602"
+ id="feGaussianBlur6852" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ x="-0.092254387"
+ width="1.1845088"
+ y="-0.17161272"
+ height="1.3432254"
+ id="filter7542">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.900534"
+ id="feGaussianBlur7544" />
+ </filter>
+ <linearGradient
+ id="linearGradient2641">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2643" />
+ <stop
+ id="stop2645"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2647" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2633">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2635" />
+ <stop
+ id="stop2637"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2639" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2625">
+ <stop
+ id="stop2627"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2629" />
+ <stop
+ id="stop2631"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2619">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2621" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2623" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2613">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2615" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2617" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2607">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2609" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2611" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2601">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2603" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2605" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2591">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2593" />
+ <stop
+ id="stop2595"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2597"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2599" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2585">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2587" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2589" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2583"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2581"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2579"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2577"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2575"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43960"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2573"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2571"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2569"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2567"
+ cx="734.74274"
+ cy="565.76050"
+ fx="734.74274"
+ fy="565.76050"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2565"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2563"
+ cx="665.83667"
+ cy="613.80670"
+ fx="665.83667"
+ fy="613.80670"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2561"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2559"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.038260"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2557"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2555"
+ cx="834.16461"
+ cy="416.62680"
+ fx="834.16461"
+ fy="416.62680"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2553"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2551"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.656700"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2549"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2547"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient2544">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2546" />
+ <stop
+ id="stop2548"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2550" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2552">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2554" />
+ <stop
+ id="stop2556"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2558" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2560">
+ <stop
+ id="stop2562"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2564" />
+ <stop
+ id="stop2566"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2568">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2570" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2572" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2574">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2576" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2578" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2580">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2582" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2584" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2586">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2588" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2590" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2592">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2594" />
+ <stop
+ id="stop2596"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2598"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2600" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2602">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2604" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2606" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2608"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2610"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2612"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2614"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2616"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43960"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2618"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2620"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2622"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2624"
+ cx="734.74274"
+ cy="565.76050"
+ fx="734.74274"
+ fy="565.76050"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2626"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2628"
+ cx="665.83667"
+ cy="613.80670"
+ fx="665.83667"
+ fy="613.80670"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2630"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2632"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.038260"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2634"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2636"
+ cx="834.16461"
+ cy="416.62680"
+ fx="834.16461"
+ fy="416.62680"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2638"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2640"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.656700"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2642"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2644"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2646"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2648"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4859"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4857"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4611"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4613"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4615"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4617"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.6567"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient4619"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient4621"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.6268"
+ fx="834.16461"
+ cy="416.6268"
+ cx="834.16461"
+ id="radialGradient4623"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient4625"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.03826"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient4627"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient4629"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.8067"
+ fx="665.83667"
+ cy="613.8067"
+ cx="665.83667"
+ id="radialGradient4631"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient4633"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.7605"
+ fx="734.74274"
+ cy="565.7605"
+ cx="734.74274"
+ id="radialGradient4635"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient4637"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient4639"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient4641"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43961"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient4643"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient4645"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient4647"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient4649"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient4651"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4653">
+ <stop
+ id="stop4655"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop4657"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4659">
+ <stop
+ id="stop4661"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop4663" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop4665" />
+ <stop
+ id="stop4667"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4669">
+ <stop
+ id="stop4671"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop4673"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4675">
+ <stop
+ id="stop4677"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop4679"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4681">
+ <stop
+ id="stop4683"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop4685"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4687">
+ <stop
+ id="stop4689"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop4691"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4693">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop4695" />
+ <stop
+ id="stop4697"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop4700" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4702">
+ <stop
+ id="stop4704"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4706" />
+ <stop
+ id="stop4708"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4710">
+ <stop
+ id="stop4712"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop4714" />
+ <stop
+ id="stop4716"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4726"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4736"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4766"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3531"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3529"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.6567"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient3527"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient3525"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.6268"
+ fx="834.16461"
+ cy="416.6268"
+ cx="834.16461"
+ id="radialGradient3523"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient3521"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.03826"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient3519"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient3517"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.8067"
+ fx="665.83667"
+ cy="613.8067"
+ cx="665.83667"
+ id="radialGradient3515"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient3513"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.7605"
+ fx="734.74274"
+ cy="565.7605"
+ cx="734.74274"
+ id="radialGradient3511"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient3509"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient3507"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient3505"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43961"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient3503"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient3501"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient3499"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient3497"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient3495"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient3489">
+ <stop
+ id="stop3491"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop3493"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3479">
+ <stop
+ id="stop3481"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop3483" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop3485" />
+ <stop
+ id="stop3487"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3473">
+ <stop
+ id="stop3475"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop3477"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3467">
+ <stop
+ id="stop3469"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop3471"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3461">
+ <stop
+ id="stop3463"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop3465"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3455">
+ <stop
+ id="stop3457"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop3459"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3447">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop3449" />
+ <stop
+ id="stop3451"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop3453" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3439">
+ <stop
+ id="stop3441"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop3443" />
+ <stop
+ id="stop3445"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3431">
+ <stop
+ id="stop3433"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop3435" />
+ <stop
+ id="stop3437"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4123"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4125"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3427"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3425"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient5776"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient5778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient6901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient6903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient6925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient6927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient7331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient7333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3409"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3407"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2479"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2481"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ y2="18.695934"
+ x2="73.216293"
+ y1="15.536267"
+ x1="67.794327"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2409"
+ xlink:href="#linearGradient3266"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-220.53892,-160.04912)"
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2406"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,152.57359,197.13637)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2403"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(3.3771096,0,0,3.3771096,-225.9505,-202.08073)"
+ y2="144.91158"
+ x2="222.35701"
+ y1="145.44635"
+ x1="231.71468"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2386"
+ xlink:href="#linearGradient3258"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="18.695934"
+ x2="73.216293"
+ y1="15.536267"
+ x1="67.794327"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2355"
+ xlink:href="#linearGradient3266"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(3.3771096,0,0,3.3771096,-226.52402,-199.72976)"
+ y2="144.91158"
+ x2="222.35701"
+ y1="145.44635"
+ x1="231.71468"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2350"
+ xlink:href="#linearGradient3258"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2800">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2802" />
+ <stop
+ id="stop2804"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2806" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2808">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2810" />
+ <stop
+ id="stop2812"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2814" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2816">
+ <stop
+ id="stop2818"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2820" />
+ <stop
+ id="stop2822"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2824">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2826" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2828" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2830">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2832" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2834" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2836">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2838" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2840" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2842">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2844" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2846" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2848">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2850" />
+ <stop
+ id="stop2852"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2854"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2856" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2858">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2860" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2862" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2864"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2866"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2868"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2870"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2872"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43960"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2874"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2876"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2878"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2880"
+ cx="734.74274"
+ cy="565.76050"
+ fx="734.74274"
+ fy="565.76050"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2882"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2884"
+ cx="665.83667"
+ cy="613.80670"
+ fx="665.83667"
+ fy="613.80670"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2886"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2888"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.038260"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2890"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2892"
+ cx="834.16461"
+ cy="416.62680"
+ fx="834.16461"
+ fy="416.62680"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2894"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2896"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.656700"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ y2="18.695934"
+ x2="73.216293"
+ y1="15.536267"
+ x1="67.794327"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2329"
+ xlink:href="#linearGradient3266"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(3.3771096,0,0,3.3771096,-223.77696,-215.3576)"
+ y2="144.91158"
+ x2="222.35701"
+ y1="145.44635"
+ x1="231.71468"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2324"
+ xlink:href="#linearGradient3258"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="144.91158"
+ x2="222.35701"
+ y1="145.44635"
+ x1="231.71468"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2363"
+ xlink:href="#linearGradient3258"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="18.695934"
+ x2="73.216293"
+ y1="15.536267"
+ x1="67.794327"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2361"
+ xlink:href="#linearGradient3266"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4723">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop4725" />
+ <stop
+ id="stop4727"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop4729" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4715">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop4717" />
+ <stop
+ id="stop4719"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop4721" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4707">
+ <stop
+ id="stop4709"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop4711" />
+ <stop
+ id="stop4713"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4700">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop4702" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop4705" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4694">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop4696" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop4698" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4688">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop4690" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop4692" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4682">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop4684" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop4686" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4672">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop4674" />
+ <stop
+ id="stop4676"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop4678"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop4680" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4666">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop4668" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop4670" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient4664"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient4662"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4660"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4658"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient4656"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43960"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4654"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4652"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4650"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4648"
+ cx="734.74274"
+ cy="565.76050"
+ fx="734.74274"
+ fy="565.76050"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4646"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4644"
+ cx="665.83667"
+ cy="613.80670"
+ fx="665.83667"
+ fy="613.80670"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4642"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4640"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.038260"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4638"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4636"
+ cx="834.16461"
+ cy="416.62680"
+ fx="834.16461"
+ fy="416.62680"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4634"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4632"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.656700"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4630"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4628"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2443"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2262"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2219"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3219"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2210"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2199"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="18.9375"
+ x2="77.5"
+ y1="18.9375"
+ x1="63.666668"
+ id="linearGradient3284"
+ xlink:href="#linearGradient3278"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient3278"
+ inkscape:collect="always">
+ <stop
+ id="stop3280"
+ offset="0"
+ style="stop-color:#7aa1ca;stop-opacity:1;" />
+ <stop
+ id="stop3282"
+ offset="1"
+ style="stop-color:#3c75b2;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3258"
+ inkscape:collect="always">
+ <stop
+ id="stop3260"
+ offset="0"
+ style="stop-color:#9096b1;stop-opacity:1;" />
+ <stop
+ id="stop3262"
+ offset="1"
+ style="stop-color:#5e678d;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3266">
+ <stop
+ style="stop-color:#7aa1ca;stop-opacity:1;"
+ offset="0"
+ id="stop3268" />
+ <stop
+ style="stop-color:#3c75b2;stop-opacity:1"
+ offset="1"
+ id="stop3270" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter6174">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.2300004"
+ id="feGaussianBlur6176" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3592"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3594"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <filter
+ inkscape:collect="always"
+ id="filter4838">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.9376613"
+ id="feGaussianBlur4840" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter4884">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.9376617"
+ id="feGaussianBlur4886" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.9526982"
+ inkscape:cx="385.99242"
+ inkscape:cy="444.79604"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ gridempspacing="10"
+ showgrid="true"
+ inkscape:grid-points="false"
+ gridspacingy="0.5px"
+ gridspacingx="0.5px"
+ inkscape:grid-bbox="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ transform="translate(-334.75512,164.53914)"
+ id="g3516">
+ <g
+ id="g3518"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g3520"
+ style="display:inline">
+ <g
+ id="g3522">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path3524"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path3526"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient3592);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path3528"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient3594);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path3530"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path3532"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g3534"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <rect
+ y="553.78314"
+ x="64.536415"
+ height="97"
+ width="77"
+ id="rect3536"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ y="578.61383"
+ x="100.13438"
+ height="6"
+ width="34"
+ id="rect3538"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3540"
+ width="34"
+ height="6"
+ x="100.13438"
+ y="591.77423" />
+ <g
+ id="g3542"
+ transform="matrix(6,0,0,6,114.11851,545.62146)">
+ <path
+ id="path3544"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path3546"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path3548"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path3550"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path3552"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path3554"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path3556"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path3558" />
+ <path
+ id="path3560"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3562"
+ width="34"
+ height="6"
+ x="100.13438"
+ y="565.14081" />
+ <g
+ transform="translate(192.93871,392.69274)"
+ id="g3564">
+ <rect
+ y="231.42992"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect3566"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3568"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="244.59032" />
+ <g
+ id="g3570"
+ transform="matrix(6,0,0,6,-78.820202,198.43754)">
+ <path
+ id="path3572"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path3574"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path3576"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path3578"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path3580"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path3582"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path3584"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path3586" />
+ <path
+ id="path3588"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3590"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="217.95688" />
+ </g>
+ <g
+ id="g6337"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-128.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013"
+ transform="translate(1094.0761,-1456.3822)">
+ <g
+ id="g6205"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ inkscape:label="Shadow"
+ id="g6207" />
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ id="g6210"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6213">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6216"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path6224"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path6226"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path6228"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6230"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ inkscape:label="SeaMonkey"
+ id="g6232" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6234"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6236"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6238"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6240">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path6243" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path6248" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6251"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path6253" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path6255" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6257"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path6259"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path6261"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path6263" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6265"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6267"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6269"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6271"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6273">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path6275" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path6277" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6279"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path6281" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path6283" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6285"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path6287"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path6289"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path6291" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6293"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g6377"
+ transform="matrix(0.3750001,0,0,0.3750001,749.86586,-122.01496)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-48.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013">
+ <g
+ style="opacity:1;fill:#ffffff;fill-opacity:0"
+ transform="translate(-1238.3703,1628.5596)"
+ id="g6379">
+ <g
+ id="g6381"
+ inkscape:label="Shadow"
+ style="fill:#ffffff;fill-opacity:0;display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g6383"
+ style="fill:#ffffff;fill-opacity:0;display:inline">
+ <g
+ id="g6385"
+ style="fill:#ffffff;fill-opacity:0">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path6387"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path6389"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path6391"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path6393"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path6395"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g6397"
+ inkscape:label="SeaMonkey"
+ style="fill:#ffffff;fill-opacity:0;display:inline" />
+ </g>
+ <rect
+ y="2017.8035"
+ x="-839.0788"
+ height="97"
+ width="77"
+ id="rect6399"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ y="2042.6342"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6401"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6403"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2055.7947" />
+ <g
+ id="g6405"
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)">
+ <path
+ id="path6407"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path6409"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path6411"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path6413"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path6415"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6417"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path6419"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path6421" />
+ <path
+ id="path6423"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6425"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2029.1611" />
+ <g
+ transform="translate(-710.67651,1856.7132)"
+ id="g6427">
+ <rect
+ y="231.42992"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6429"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6431"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="244.59032" />
+ <g
+ id="g6433"
+ transform="matrix(6,0,0,6,-78.820202,198.43754)">
+ <path
+ id="path6435"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path6437"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path6439"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path6441"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path6443"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6445"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path6447"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path6449" />
+ <path
+ id="path6451"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6453"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="217.95688" />
+ </g>
+ </g>
+ <g
+ transform="matrix(0.25,0,0,0.25,772.07311,144.27314)"
+ id="g6456"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-32.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013">
+ <g
+ id="g6458"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ inkscape:label="Shadow"
+ id="g6460" />
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ id="g6462"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6464">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6466"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path6468"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path6470"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path6472"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6474"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ inkscape:label="SeaMonkey"
+ id="g6476" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6478"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6480"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6482"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6484">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path6486" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path6488" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6490"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path6492" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path6494" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6496"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path6498"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path6500"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path6502" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6504"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6506"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6508"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6510"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6512">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path6514" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path6516" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6518"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path6520" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path6522" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6524"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path6526"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path6528"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path6530" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6532"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g6534"
+ transform="matrix(0.1875,0,0,0.1875,816.11264,277.41704)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-24.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013">
+ <g
+ style="opacity:1;fill:#ffffff;fill-opacity:0"
+ transform="translate(-1238.3703,1628.5596)"
+ id="g6536">
+ <g
+ id="g6538"
+ inkscape:label="Shadow"
+ style="fill:#ffffff;fill-opacity:0;display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g6540"
+ style="fill:#ffffff;fill-opacity:0;display:inline">
+ <g
+ id="g6542"
+ style="fill:#ffffff;fill-opacity:0">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path6544"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path6546"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path6548"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path6550"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path6552"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g6554"
+ inkscape:label="SeaMonkey"
+ style="fill:#ffffff;fill-opacity:0;display:inline" />
+ </g>
+ <rect
+ y="2017.8035"
+ x="-839.0788"
+ height="97"
+ width="77"
+ id="rect6556"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ y="2042.6342"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6558"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6560"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2055.7947" />
+ <g
+ id="g6562"
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)">
+ <path
+ id="path6564"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path6567"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path6569"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path6571"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path6575"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6577"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path6579"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path6581" />
+ <path
+ id="path6583"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6585"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2029.1611" />
+ <g
+ transform="translate(-710.67651,1856.7132)"
+ id="g6587">
+ <rect
+ y="231.42992"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6589"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6591"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="244.59032" />
+ <g
+ id="g6593"
+ transform="matrix(6,0,0,6,-78.820202,198.43754)">
+ <path
+ id="path6595"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path6597"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path6599"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path6601"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path6603"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6605"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path6607"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path6609" />
+ <path
+ id="path6611"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6613"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="217.95688" />
+ </g>
+ </g>
+ <g
+ transform="matrix(0.125,0,0,0.125,825.02058,410.56094)"
+ id="g6615"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\mail-list-window-redo\try4-16.png"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-ydpi="90.00013">
+ <g
+ id="g6617"
+ transform="translate(-1238.3703,1628.5596)"
+ style="opacity:1;fill:#ffffff;fill-opacity:0">
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ inkscape:label="Shadow"
+ id="g6619" />
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ id="g6621"
+ inkscape:label="Background">
+ <g
+ style="fill:#ffffff;fill-opacity:0"
+ id="g6623">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6625"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path6627"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path6629"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path6631"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:0;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6633"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="fill:#ffffff;fill-opacity:0;display:inline"
+ inkscape:label="SeaMonkey"
+ id="g6635" />
+ </g>
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#b3b3b3;stroke-width:2.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6637"
+ width="77"
+ height="97"
+ x="-839.0788"
+ y="2017.8035" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6639"
+ width="34"
+ height="6"
+ x="-803.4809"
+ y="2042.6342" />
+ <rect
+ y="2055.7947"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6641"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-789.49671,2009.6419)"
+ id="g6643">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path6645" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path6647" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6649"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path6651" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path6653" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6655"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path6657"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path6659"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path6661" />
+ </g>
+ <rect
+ y="2029.1611"
+ x="-803.4809"
+ height="6"
+ width="34"
+ id="rect6663"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g6665"
+ transform="translate(-710.67651,1856.7132)">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6667"
+ width="34"
+ height="6"
+ x="-92.804382"
+ y="231.42992" />
+ <rect
+ y="244.59032"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6669"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="matrix(6,0,0,6,-78.820202,198.43754)"
+ id="g6671">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path6673" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path6675" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path6677"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path6679" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path6681" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path6683"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path6685"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path6687"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path6689" />
+ </g>
+ <rect
+ y="217.95688"
+ x="-92.804382"
+ height="6"
+ width="34"
+ id="rect6691"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/addressbookWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/addressbookWindow.svg
new file mode 100644
index 0000000000..5a59b97953
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/addressbookWindow.svg
@@ -0,0 +1,1968 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="500"
+ height="500"
+ id="svg2160"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\addressbookWindow"
+ sodipodi:docname="seamonkey-addressbookWindowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2162">
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2481"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2479"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <filter
+ id="filter7542"
+ height="1.3432254"
+ y="-0.17161272"
+ width="1.1845088"
+ x="-0.092254385"
+ inkscape:collect="always">
+ <feGaussianBlur
+ id="feGaussianBlur7544"
+ stdDeviation="1.900534"
+ inkscape:collect="always" />
+ </filter>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient7472"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient7470"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient7333"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient7331"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient6927"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient6925"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient6903"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient6901"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <filter
+ id="filter6850"
+ inkscape:collect="always">
+ <feGaussianBlur
+ id="feGaussianBlur6852"
+ stdDeviation="4.2275602"
+ inkscape:collect="always" />
+ </filter>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5778"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5776"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4358"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4356"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4218"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4216"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4125"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4123"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop5600" />
+ <stop
+ id="stop5606"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop5602" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop4699" />
+ <stop
+ id="stop4707"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop4701" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ id="stop10992"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop10996" />
+ <stop
+ id="stop10998"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop5819" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop5821" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop5811" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop5813" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop5076" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop5078" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop5068" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop5070" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop5008" />
+ <stop
+ id="stop10260"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop5016"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop5010" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop4998" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop5000" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient5002"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient5072"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient5080"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient5815"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient5823"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43961"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient10262"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11000"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11002"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11004"
+ cx="734.74274"
+ cy="565.7605"
+ fx="734.74274"
+ fy="565.7605"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11006"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11008"
+ cx="665.83667"
+ cy="613.8067"
+ fx="665.83667"
+ fy="613.8067"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11010"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11012"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.03826"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11014"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11016"
+ cx="834.16461"
+ cy="416.6268"
+ fx="834.16461"
+ fy="416.6268"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11018"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient11020"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.6567"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2232"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2239"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2242"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <filter
+ id="filter3395"
+ height="1.169028"
+ y="-0.084514"
+ width="1.2221982"
+ x="-0.11109908"
+ inkscape:collect="always">
+ <feGaussianBlur
+ id="feGaussianBlur3397"
+ stdDeviation="4.1250422"
+ inkscape:collect="always" />
+ </filter>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4792"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4790"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4784"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4782"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4780"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4778"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4772"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4770"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4768"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4766"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4752"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4750"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4736"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4734"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4732"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4730"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4728"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4726"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4710">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop4712" />
+ <stop
+ id="stop4714"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop4716" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4702">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop4704" />
+ <stop
+ id="stop4706"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop4708" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4693">
+ <stop
+ id="stop4695"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop4697" />
+ <stop
+ id="stop4700"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4687">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop4689" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop4691" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4681">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop4683" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop4685" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4675">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop4677" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop4679" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4669">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop4671" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop4673" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4659">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop4661" />
+ <stop
+ id="stop4663"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop4665"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop4667" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4653">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop4655" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop4657" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient4651"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient4649"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4647"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4645"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient4643"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43961"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4641"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4639"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4637"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4635"
+ cx="734.74274"
+ cy="565.7605"
+ fx="734.74274"
+ fy="565.7605"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4633"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4631"
+ cx="665.83667"
+ cy="613.8067"
+ fx="665.83667"
+ fy="613.8067"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4629"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4627"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.03826"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4625"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4623"
+ cx="834.16461"
+ cy="416.6268"
+ fx="834.16461"
+ fy="416.6268"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4621"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient4619"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.6567"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4617"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4615"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4613"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4611"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4857"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4859"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2648"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2646"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2644"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2642"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient2640"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient2638"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient2636"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient2634"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient2632"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient2630"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient2628"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient2626"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient2624"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient2622"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient2620"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient2618"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient2616"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient2614"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient2612"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient2610"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient2608"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2602">
+ <stop
+ id="stop2604"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2606"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2592">
+ <stop
+ id="stop2594"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop2596" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop2598" />
+ <stop
+ id="stop2600"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2586">
+ <stop
+ id="stop2588"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop2590"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2580">
+ <stop
+ id="stop2582"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2584"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2574">
+ <stop
+ id="stop2576"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2578"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2568">
+ <stop
+ id="stop2570"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2572"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2560">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop2562" />
+ <stop
+ id="stop2564"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop2566" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2552">
+ <stop
+ id="stop2554"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop2556" />
+ <stop
+ id="stop2558"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2544">
+ <stop
+ id="stop2546"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop2548" />
+ <stop
+ id="stop2550"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.9591645"
+ inkscape:cx="133.99991"
+ inkscape:cy="225.59407"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ gridtolerance="10000"
+ width="500px"
+ height="500px"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4" />
+ <metadata
+ id="metadata2165">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ id="g2596"
+ transform="translate(-140,120)">
+ <g
+ transform="translate(-133.79172,-281.74402)"
+ id="g2650">
+ <g
+ id="layer3"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g2653"
+ style="display:inline">
+ <g
+ id="g2219">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path6573"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2064"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2056"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2058"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2052"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="layer2"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <path
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 278,118 L 278,124 L 284,124 L 290,124 L 296,124 L 296,118 L 290,118 L 284,118 L 278,118 z "
+ id="path2664" />
+ <path
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 302,118 L 302,124 L 296,124 L 296,130 L 302,130 L 308,130 L 314,130 L 320,130 L 320,124 L 320,118 L 314,118 L 308,118 L 302,118 z "
+ id="path2666" />
+ <path
+ style="fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 326,118 L 326,124 L 320,124 L 320,130 L 326,130 L 332,130 L 338,130 L 344,130 L 344,124 L 344,118 L 338,118 L 332,118 L 326,118 z "
+ id="path2668" />
+ <path
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 266,124 L 266,130 L 272,130 L 278,130 L 284,130 L 290,130 L 296,130 L 296,124 L 290,124 L 284,124 L 278,124 L 272,124 L 266,124 z "
+ id="path2670" />
+ <rect
+ y="124"
+ x="284"
+ height="6"
+ width="6"
+ id="rect2672"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 260,130 L 260,136 L 260,142 L 260,148 L 260,154 L 260,160 L 260,166 L 260,172 L 260,178 L 260,184 L 260,190 L 266,190 L 272,190 L 278,190 L 284,190 L 284,184 L 278,184 L 272,184 L 266,184 L 266,178 L 266,172 L 266,166 L 266,160 L 266,154 L 266,148 L 266,142 L 266,136 L 266,130 L 260,130 z "
+ id="path2674" />
+ <path
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 290,184 L 290,190 L 296,190 L 302,190 L 308,190 L 314,190 L 320,190 L 320,184 L 314,184 L 308,184 L 302,184 L 296,184 L 290,184 z "
+ id="path2676" />
+ <path
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 344,124 L 344,130 L 344,136 L 344,142 L 344,148 L 344,154 L 344,160 L 344,166 L 344,172 L 344,178 L 344,184 L 338,184 L 332,184 L 326,184 L 326,190 L 332,190 L 338,190 L 344,190 L 350,190 L 350,184 L 350,178 L 350,172 L 350,166 L 350,160 L 350,154 L 350,148 L 350,142 L 350,136 L 350,130 L 350,124 L 344,124 z "
+ id="path2678" />
+ <rect
+ style="fill:#ffffcc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2680"
+ width="78"
+ height="54"
+ x="266"
+ y="130" />
+ <g
+ id="g2682"
+ transform="matrix(6,0,0,6,350,124)">
+ <path
+ id="path2684"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss" />
+ <path
+ id="path2686"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ id="path2688"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ id="path2690"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path2692"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path2694"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)"
+ id="path2696"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)" />
+ <path
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ id="path2698" />
+ <path
+ id="path2700"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <rect
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2702"
+ width="6"
+ height="6"
+ x="284"
+ y="178" />
+ <rect
+ y="178"
+ x="320"
+ height="6"
+ width="6"
+ id="rect2704"
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 272,142 L 272,148 L 278,148 L 284,148 L 290,148 L 290,142 L 284,142 L 278,142 L 272,142 z "
+ id="path2706" />
+ <path
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 272,154 L 272,160 L 278,160 L 284,160 L 290,160 L 290,154 L 284,154 L 278,154 L 272,154 z "
+ id="path2708" />
+ <path
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 272,166 L 272,172 L 278,172 L 284,172 L 290,172 L 290,166 L 284,166 L 278,166 L 272,166 z "
+ id="path2710" />
+ <path
+ id="path2785"
+ d="M 280,142 L 280,148 L 286,148 L 292,148 L 298,148 L 298,142 L 292,142 L 286,142 L 280,142 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ id="path2787"
+ d="M 280,154 L 280,160 L 286,160 L 292,160 L 298,160 L 298,154 L 292,154 L 286,154 L 280,154 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ id="path2789"
+ d="M 280,166 L 280,172 L 286,172 L 292,172 L 298,172 L 298,166 L 292,166 L 286,166 L 280,166 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g2635"
+ transform="translate(200,85.000002)">
+ <g
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ style="fill:none"
+ id="g2791"
+ transform="translate(-303.79172,-246.74402)">
+ <g
+ style="fill:none;display:inline"
+ inkscape:label="Shadow"
+ id="g2793" />
+ <g
+ style="fill:none;display:inline"
+ id="g2795"
+ inkscape:label="Background">
+ <g
+ style="fill:none"
+ id="g2797">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2799"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path2801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2803"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2805"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2807"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="fill:none;display:inline"
+ inkscape:label="SeaMonkey"
+ id="g2809" />
+ </g>
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2811"
+ d="M 108,153 L 108,159 L 114,159 L 120,159 L 126,159 L 126,153 L 120,153 L 114,153 L 108,153 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2813"
+ d="M 132,153 L 132,159 L 126,159 L 126,165 L 132,165 L 138,165 L 144,165 L 150,165 L 150,159 L 150,153 L 144,153 L 138,153 L 132,153 z "
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2815"
+ d="M 156,153 L 156,159 L 150,159 L 150,165 L 156,165 L 162,165 L 168,165 L 174,165 L 174,159 L 174,153 L 168,153 L 162,153 L 156,153 z "
+ style="fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2817"
+ d="M 96,159 L 96,165 L 102,165 L 108,165 L 114,165 L 120,165 L 126,165 L 126,159 L 120,159 L 114,159 L 108,159 L 102,159 L 96,159 z "
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2819"
+ width="6"
+ height="6"
+ x="114"
+ y="159" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2821"
+ d="M 90,165 L 90,171 L 90,177 L 90,183 L 90,189 L 90,195 L 90,201 L 90,207 L 90,213 L 90,219 L 90,225 L 96,225 L 102,225 L 108,225 L 114,225 L 114,219 L 108,219 L 102,219 L 96,219 L 96,213 L 96,207 L 96,201 L 96,195 L 96,189 L 96,183 L 96,177 L 96,171 L 96,165 L 90,165 z "
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2823"
+ d="M 120,219 L 120,225 L 126,225 L 132,225 L 138,225 L 144,225 L 150,225 L 150,219 L 144,219 L 138,219 L 132,219 L 126,219 L 120,219 z "
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2825"
+ d="M 174,159 L 174,165 L 174,171 L 174,177 L 174,183 L 174,189 L 174,195 L 174,201 L 174,207 L 174,213 L 174,219 L 168,219 L 162,219 L 156,219 L 156,225 L 162,225 L 168,225 L 174,225 L 180,225 L 180,219 L 180,213 L 180,207 L 180,201 L 180,195 L 180,189 L 180,183 L 180,177 L 180,171 L 180,165 L 180,159 L 174,159 z "
+ style="fill:#333366;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ y="165"
+ x="96"
+ height="54"
+ width="78"
+ id="rect2827"
+ style="fill:#ffffcc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ transform="matrix(6,0,0,6,180,159)"
+ id="g2829">
+ <path
+ sodipodi:nodetypes="ccsssssssssssssssssssssssssssssssssssssscss"
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -6.1094906,2.2138122 C -6.5841838,2.4373409 -6.8134394,2.6610126 -7.0678887,3.368782 C -7.0678887,3.368782 -7.3197395,4.3474002 -6.9267314,5.446328 C -6.8640827,5.6215056 -6.614324,5.9578259 -6.6108947,5.9607307 C -6.4182828,6.1239278 -6.2191403,6.1707362 -6.0138601,6.2519585 C -6.0049214,6.2554954 -5.9958848,6.2584379 -5.9869258,6.2619146 C -5.91398,6.2902104 -5.8389594,6.3173923 -5.7647215,6.3415641 C -5.6221645,6.3879886 -5.4780632,6.4252622 -5.3315343,6.4560715 C -5.314374,6.4596762 -5.2971181,6.4626337 -5.2799111,6.4660277 C -5.2189677,6.4780308 -5.157341,6.4891459 -5.0958625,6.4983871 C -5.0839414,6.5001707 -5.0718901,6.5016758 -5.059951,6.503369 C -4.996768,6.5122867 -4.9328204,6.5198046 -4.8691693,6.5257648 C -4.8624258,6.5263969 -4.8557168,6.5276536 -4.8489687,6.5282632 C -4.8429807,6.5287749 -4.837004,6.527759 -4.831013,6.5282632 C -4.7695164,6.5334407 -5.2982274,6.278719 -5.2363831,6.281097 C -5.2258181,6.2815034 -5.2155346,6.2832568 -5.2049606,6.283588 C -5.1953032,6.2838664 -5.1854463,6.2833697 -5.1757821,6.283588 C -5.1264106,6.2847394 -5.0771813,6.2842502 -5.0276457,6.283588 C -4.9858297,6.283001 -4.872546,6.6951403 -4.8306367,6.6932589 C -4.8232091,6.6929202 -4.8156219,6.6936201 -4.808192,6.6932589 C -4.7939877,6.6925289 -4.7797586,6.6916334 -4.7655468,6.6907604 C -4.7153527,6.6876825 -5.2548604,6.4236819 -5.2045835,6.4187151 C -5.194899,6.4177594 -5.1850919,6.4172401 -5.1754051,6.4162242 C -5.1573884,6.4143127 -5.1395606,6.4109112 -5.1215371,6.4087589 C -5.0682967,6.4023849 -5.0154447,6.3973578 -4.9621787,6.3888466 C -4.9591452,6.3883574 -4.9562336,6.3868373 -4.9532009,6.3863556 C -4.9398259,6.3841732 -4.9261747,6.3811932 -4.9127997,6.3788904 C -4.8548416,6.3688815 -4.7956456,6.3591135 -4.7377297,6.3465234 C -4.734715,6.3458762 -4.7317657,6.3446947 -4.728751,6.34404 C -4.7212828,6.3423919 -4.7137739,6.3407438 -4.7063064,6.3390581 C -4.6394983,6.3239921 -4.5732028,6.307835 -4.5065468,6.2892772 C -4.4376971,6.2700948 -4.3686443,6.2500018 -4.3000535,6.2270417 C -4.2948805,6.2253183 -4.2895133,6.2238208 -4.2843418,6.2220673 C -4.2105396,6.1970527 -4.1377594,6.1693816 -4.0643817,6.1399269 C -3.9932292,6.1113528 -3.9224463,6.0798813 -3.8533994,6.0478304 C -3.8512298,6.0468069 -3.8488345,6.0463629 -3.8466664,6.0453394 C -3.8170101,6.0315077 -3.7883907,6.0174802 -3.759131,6.0030163 C -3.7406613,5.993775 -3.723574,5.9826448 -3.705263,5.9731552 C -3.6744237,5.9571636 -3.6436242,5.942489 -3.6132387,5.9258577 C -3.5648139,5.8993455 -3.2893603,5.5481098 -3.2182988,5.3882916 C -2.7950753,4.4364791 -3.1175834,3.368782 -3.1175834,3.368782 C -3.3637359,2.7075275 -3.5245594,2.477271 -4.0737366,2.2138122 C -4.7141795,1.9065703 -5.5530522,1.9517832 -6.1094906,2.2138122 z "
+ id="path2831" />
+ <path
+ style="fill:#00c99f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.5625,5.84375 C -5.980909,5.8437425 -6.3125,6.3517043 -6.3125,7 C -6.3125421,7.0148931 -6.3109061,7.0165678 -6.3125,7.03125 L -6.53125,9 L -3.5625,9 L -3.78125,7.0625 C -3.7828883,7.0473963 -3.7812538,7.0465568 -3.78125,7.03125 C -3.7812493,6.3829542 -4.1128428,5.875 -4.53125,5.875 L -5.5625,5.84375 z "
+ id="path2833" />
+ <path
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:ry="52.411282"
+ sodipodi:rx="52.411282"
+ sodipodi:cy="351.4133"
+ sodipodi:cx="342.1824"
+ id="path2835"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter6850)"
+ sodipodi:type="arc" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -4.375,5.96875 L -4.125,9 L -3,9 L -3.3125,7.15625 C -3.3148284,7.1413874 -3.3125045,7.1088235 -3.3125,7.09375 C -3.3125007,6.4555533 -3.7805008,5.96875 -4.375,5.96875 z "
+ id="path2837" />
+ <path
+ style="fill:#01aa90;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -5.75,5.9375 C -6.3444991,5.9374998 -6.8125,6.4219178 -6.8125,7.0625 C -6.8124953,7.0776263 -6.8101716,7.110077 -6.8125,7.125 L -7.125,9 L -6,9 L -5.75,5.9375 z "
+ id="path2839" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#cfb32e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path2841"
+ sodipodi:cx="342.1824"
+ sodipodi:cy="351.4133"
+ sodipodi:rx="52.411282"
+ sodipodi:ry="52.411282"
+ d="M 391.46325,333.5712 A 52.411282,52.411282 0 1 1 391.29155,333.10389"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9754709e-2,0,0,3.8336299e-2,-15.223318,-8.8578914)" />
+ <path
+ style="fill:#fae15a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3395)"
+ d="M 263.78125,241.90625 C 242.0418,242.89024 226.92997,263.64204 222.5,283.28125 C 216.00695,309.85745 225.18626,342.26389 250.21875,355.71875 C 267.57362,364.41516 289.13334,354.88493 298.375,338.8125 C 316.781,311.19895 312.50409,269.39738 285.8125,248.65625 C 279.3949,244.23443 271.62953,241.51752 263.78125,241.90625 z "
+ id="path2843"
+ transform="matrix(2.9754709e-2,0,0,2.9953848e-2,-12.927769,-4.385165)" />
+ <path
+ id="path2845"
+ d="M 198.40449,-30.85609 C 199.22238,-33.74979 198.7253,-32.03359 200.73594,-34.25539 C 202.63314,-36.35189 204.53142,-37.42339 206.89643,-39.08649 C 207.29526,-39.36699 208.07156,-39.35349 208.47107,-39.63299 C 211.03088,-41.42369 210.74586,-39.53359 213.92151,-39.91439 C 216.46913,-40.21989 214.95382,-41.88349 217.23921,-40.99059 C 219.8648,-39.96489 220.97224,-39.20389 223.04737,-37.32129 C 224.93951,-35.60479 225.28864,-35.56059 226.97458,-33.60999 C 228.53852,-31.80059 227.79697,-33.70679 229.01201,-31.67309 C 230.02574,-29.97649 233.59252,-29.15329 233.94735,-26.96469 C 234.20485,-25.37649 233.32991,-24.95969 232.09312,-25.79889 C 228.00868,-28.57029 224.18324,-31.36119 219.88472,-33.82409 C 216.96812,-35.49529 215.6313,-36.11909 213.89202,-33.12939 C 212.1531,-30.14029 210.9022,-26.63639 208.93062,-23.78659 C 205.63419,-19.02169 207.48122,-28.64509 207.48122,-29.91929 C 207.48122,-33.29129 205.14006,-31.21709 203.32813,-29.91929 C 201.63847,-28.70909 198.93343,-27.40919 196.88946,-27.11649 C 194.8377,-26.82259 197.64073,-30.41419 195.62151,-30.83309 C 194.46869,-31.07219 196.50317,-28.86729 196.87444,-29.37109 C 197.12466,-29.71069 198.08859,-30.57659 198.40449,-30.85609 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter7542)"
+ sodipodi:nodetypes="cssssssssssssssssssc"
+ transform="matrix(7.525459e-2,0,0,7.525459e-2,-21.171894,5.7875842)" />
+ <path
+ style="fill:#a05a2c;fill-opacity:1;fill-rule:evenodd;stroke:#a05a2c;stroke-width:0.07525459px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -6.7368167,3.494555 C -6.6752667,3.2767908 -6.5252467,3.0794507 -6.3739368,2.9122501 C -6.2311638,2.7544788 -5.9771788,2.6480011 -5.7992009,2.5228452 C -5.7691871,2.5017363 -5.7390702,2.4807779 -5.7090053,2.4597442 C -5.5163678,2.3249858 -5.3396911,2.2035625 -5.1007089,2.1749056 C -4.9089888,2.1519153 -4.7545942,2.2519362 -4.5826081,2.319131 C -4.3850204,2.3963196 -4.1975966,2.5344118 -4.0414336,2.6760861 C -3.8990413,2.8052606 -3.7613789,2.9349243 -3.6345041,3.0817159 C -3.5168105,3.2178815 -3.4301834,3.3757656 -3.3387461,3.5288109 C -3.2624582,3.6564878 -3.1836072,3.8450307 -3.2485504,3.98672 C -3.3343866,4.1739911 -3.6127473,3.909253 -3.7058214,3.8460993 C -4.0131943,3.6375387 -4.3010762,3.4275107 -4.6245595,3.2421662 C -4.8440471,3.1164007 -4.9446489,3.0694569 -5.0755377,3.2944455 C -5.2063994,3.519389 -5.3005354,3.7830736 -5.4489059,3.9975341 C -5.6969773,4.3561147 -5.5579799,3.6319097 -5.5579799,3.5360203 C -5.5579799,3.2822618 -5.7341629,3.4383549 -5.8705189,3.5360203 C -5.9976736,3.6270934 -6.2012403,3.7249168 -6.3550584,3.7469439 C -6.5094628,3.7690612 -6.6792522,3.8001037 -6.8312077,3.7685796 C -6.9179627,3.7505862 -6.8360745,3.5955693 -6.8081347,3.557656 C -6.7893045,3.5320995 -6.7605896,3.5155887 -6.7368167,3.494555 z "
+ id="path2847" />
+ </g>
+ <rect
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ y="213"
+ x="114"
+ height="6"
+ width="6"
+ id="rect2849"
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2851"
+ width="6"
+ height="6"
+ x="150"
+ y="213" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2853"
+ d="M 102,177 L 102,183 L 108,183 L 114,183 L 120,183 L 120,177 L 114,177 L 108,177 L 102,177 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2855"
+ d="M 102,189 L 102,195 L 108,195 L 114,195 L 120,195 L 120,189 L 114,189 L 108,189 L 102,189 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ id="path2857"
+ d="M 102,201 L 102,207 L 108,207 L 114,207 L 120,207 L 120,201 L 114,201 L 108,201 L 102,201 z "
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 110,177 L 110,183 L 116,183 L 122,183 L 128,183 L 128,177 L 122,177 L 116,177 L 110,177 z "
+ id="path2859" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 110,189 L 110,195 L 116,195 L 122,195 L 128,195 L 128,189 L 122,189 L 116,189 L 110,189 z "
+ id="path2861" />
+ <path
+ inkscape:export-ydpi="90.00013"
+ inkscape:export-xdpi="90.00013"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\128pxaddressbook1trans.png"
+ style="fill:#6666cc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 110,201 L 110,207 L 116,207 L 122,207 L 128,207 L 128,201 L 122,201 L 116,201 L 110,201 z "
+ id="path2863" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/bookmarkproperties.svg b/comm/suite/branding/seamonkey/icons/template/svg/bookmarkproperties.svg
new file mode 100644
index 0000000000..59dedaf740
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/bookmarkproperties.svg
@@ -0,0 +1,536 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\bmPropsWindow"
+ sodipodi:docname="seamonkey-bmPropsWindowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4592"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4594"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.50266952"
+ inkscape:cx="344.19923"
+ inkscape:cy="288.48886"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g2258">
+ <g
+ transform="translate(-95.568906,331.26225)"
+ id="g4570">
+ <g
+ id="g4572"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4574"
+ style="display:inline">
+ <g
+ id="g4576">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4578"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4580"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient4592);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4582"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient4594);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4584"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4586"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g4588"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <path
+ sodipodi:nodetypes="ccccccccc"
+ id="path4590"
+ d="M 372.71949,740.73119 C 376.36604,736.95632 386.68651,747.68169 383.24073,752.28083 C 371.64265,767.66102 340.99074,799.50708 332.3867,802.25194 C 329.7568,803.50327 326.27315,804.29438 322.9366,801.4948 C 312.79661,793.19045 304.79963,782.23611 305.15773,779.53775 C 305.61682,777.44534 312.23326,769.88566 316.2896,769.69495 C 318.2251,770.0092 321.29598,778.59342 325.01882,781.6027 C 326.55114,783.21801 329.01354,783.58615 331.18094,781.73207 C 345.59534,771.41462 360.2095,754.75949 372.71949,740.73119 z "
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g2271">
+ <g
+ style="fill:none"
+ id="g4596"
+ transform="translate(92.276855,334.89797)">
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="Shadow"
+ id="g4598" />
+ <g
+ style="display:inline;fill:none"
+ id="g4600"
+ inkscape:label="Background">
+ <g
+ style="fill:none"
+ id="g4602">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4604"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path4606"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path4608"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path4610"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4612"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="SeaMonkey"
+ id="g4614" />
+ </g>
+ <path
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 560.56525,744.36691 C 564.2118,740.59204 574.53227,751.31741 571.08649,755.91655 C 559.48841,771.29674 528.8365,803.1428 520.23246,805.88766 C 517.60256,807.13899 514.11891,807.9301 510.78236,805.13052 C 500.64237,796.82617 492.64539,785.87183 493.00349,783.17347 C 493.46258,781.08106 500.07902,773.52138 504.13536,773.33067 C 506.07086,773.64492 509.14174,782.22914 512.86458,785.23842 C 514.3969,786.85373 516.8593,787.22187 519.0267,785.36779 C 533.4411,775.05034 548.05526,758.39521 560.56525,744.36691 z "
+ id="path4616"
+ sodipodi:nodetypes="ccccccccc" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/downloadManager.svg b/comm/suite/branding/seamonkey/icons/template/svg/downloadManager.svg
new file mode 100644
index 0000000000..54ddb18c6a
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/downloadManager.svg
@@ -0,0 +1,775 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\downloadManager"
+ sodipodi:docname="downloadManager.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3526">
+ <stop
+ style="stop-color:#00561d;stop-opacity:1;"
+ offset="0"
+ id="stop3528" />
+ <stop
+ style="stop-color:#00561d;stop-opacity:0;"
+ offset="1"
+ id="stop3530" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3484">
+ <stop
+ style="stop-color:#00a83e;stop-opacity:1"
+ offset="0"
+ id="stop3486" />
+ <stop
+ style="stop-color:#73cf95;stop-opacity:1"
+ offset="1"
+ id="stop3488" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3458">
+ <stop
+ style="stop-color:#009b35;stop-opacity:1"
+ offset="0"
+ id="stop3460" />
+ <stop
+ style="stop-color:#00561d;stop-opacity:1"
+ offset="1"
+ id="stop3462" />
+ </linearGradient>
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2242"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2239"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3484"
+ id="radialGradient4155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.0948682,-2.3267471,1.1043583,0.9943003,-2088.218,2560.8382)"
+ cx="1087.3295"
+ cy="756.81622"
+ fx="1087.3295"
+ fy="756.81622"
+ r="4.13275" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3458"
+ id="linearGradient4157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-62.48626,26.463358)"
+ x1="1086.8507"
+ y1="749.88818"
+ x2="1086.8507"
+ y2="754.21796" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3526"
+ id="linearGradient4159"
+ gradientUnits="userSpaceOnUse"
+ x1="1021.7614"
+ y1="732.21332"
+ x2="1021.7063"
+ y2="725.86243" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3484"
+ id="radialGradient4215"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.0948682,-2.3267471,1.1043583,0.9943003,-2088.218,2560.8382)"
+ cx="1087.3295"
+ cy="756.81622"
+ fx="1087.3295"
+ fy="756.81622"
+ r="4.13275" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3458"
+ id="linearGradient4217"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-62.48626,26.463358)"
+ x1="1086.8507"
+ y1="749.88818"
+ x2="1086.8507"
+ y2="754.21796" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3526"
+ id="linearGradient4219"
+ gradientUnits="userSpaceOnUse"
+ x1="1021.7614"
+ y1="732.21332"
+ x2="1021.7063"
+ y2="725.86243" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="50"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.265625"
+ inkscape:cx="559.37208"
+ inkscape:cy="158.84954"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="false"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="0"
+ inkscape:window-y="22" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g4113"
+ transform="translate(-111.1204,289.99552)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="layer3" />
+ <g
+ style="display:inline"
+ id="g4116"
+ inkscape:label="Background">
+ <g
+ id="g2219">
+ <path
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6573"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path2064"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2056"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2058"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2052"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="layer2" />
+ </g>
+ <g
+ id="g4125"
+ transform="matrix(2.8571604,0,0,2.8571604,-2600.6084,-1365.6782)">
+ <rect
+ style="opacity:1;fill:#f3f4f5;fill-opacity:1;fill-rule:nonzero;stroke:#3b556e;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4127"
+ width="26.999828"
+ height="17.999828"
+ x="1011.0417"
+ y="722.69794" />
+ <rect
+ style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#00561d;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4129"
+ width="19.000404"
+ height="4.0004034"
+ x="1015.0414"
+ y="727.71289" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1016.0339,727.75491 L 1016.0339,731.5766 L 1016.0339,727.75491 z "
+ id="path4131"
+ sodipodi:nodetypes="ccc" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path4133"
+ d="M 1018.028,727.7436 L 1018.028,731.56529 L 1018.028,727.7436 z "
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1020.017,727.73673 L 1020.017,731.55842 L 1020.017,727.73673 z "
+ id="path4135"
+ sodipodi:nodetypes="ccc" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path4137"
+ d="M 1022.0115,727.60918 L 1022.0115,731.43087 L 1022.0115,727.60918 z "
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1023.9948,727.6158 L 1023.9948,731.43749 L 1023.9948,727.6158 z "
+ id="path4139"
+ sodipodi:nodetypes="ccc" />
+ <rect
+ y="727.71289"
+ x="1015.0414"
+ height="4.0004034"
+ width="19.000404"
+ id="rect4141"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#73c890;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g4143"
+ transform="translate(0.1771012,-38.795917)">
+ <path
+ style="fill:#6ec527;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1021.0703,782.19928 L 1021.1076,776.58278 L 1027.6873,776.46724 L 1027.6798,782.32766 L 1028.8523,782.41752 L 1024.3788,787.14822 L 1019.838,782.50097 L 1021.0703,782.19928 z "
+ id="path4145" />
+ <path
+ id="path4147"
+ d="M 1021.0703,782.19928 L 1021.1076,776.58278 L 1027.6873,776.46724 L 1027.8867,782.42929 L 1023.6889,786.52145 L 1019.838,782.50097 L 1021.0703,782.19928 z "
+ style="fill:url(#radialGradient4155);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4157);stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 1020.8642,781.38404 L 1020.8642,776.38384 L 1027.8646,776.38384 L 1027.8646,781.38404"
+ id="path4149" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#00561d;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 1027.8606,782.31893 L 1029.314,782.31893 L 1024.3645,787.26844 L 1019.415,782.31897 L 1020.8619,782.31896"
+ id="path4151" />
+ </g>
+ <rect
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4159);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4153"
+ width="19.000404"
+ height="4.0004034"
+ x="1015.0414"
+ y="727.71289" />
+ </g>
+ <g
+ transform="translate(-106.37966,460.66219)"
+ id="g4161"
+ style="fill:none">
+ <g
+ id="g4163"
+ inkscape:label="Shadow"
+ style="fill:none;display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4165"
+ style="fill:none;display:inline">
+ <g
+ id="g4167"
+ style="fill:none">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4169"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4171"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4173"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4175"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4177"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g4179"
+ inkscape:label="SeaMonkey"
+ style="fill:none;display:inline" />
+ </g>
+ <g
+ transform="matrix(2.8571604,0,0,2.8571604,-2595.8677,-1195.0115)"
+ id="g4181">
+ <rect
+ y="722.69794"
+ x="1011.0417"
+ height="17.999828"
+ width="26.999828"
+ id="rect4183"
+ style="opacity:1;fill:#f3f4f5;fill-opacity:1;fill-rule:nonzero;stroke:#3b556e;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ y="727.71289"
+ x="1015.0414"
+ height="4.0004034"
+ width="19.000404"
+ id="rect4185"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#00561d;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path4187"
+ d="M 1016.0339,727.75491 L 1016.0339,731.5766 L 1016.0339,727.75491 z "
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1018.028,727.7436 L 1018.028,731.56529 L 1018.028,727.7436 z "
+ id="path4189"
+ sodipodi:nodetypes="ccc" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path4191"
+ d="M 1020.017,727.73673 L 1020.017,731.55842 L 1020.017,727.73673 z "
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1022.0115,727.60918 L 1022.0115,731.43087 L 1022.0115,727.60918 z "
+ id="path4193"
+ sodipodi:nodetypes="ccc" />
+ <path
+ sodipodi:nodetypes="ccc"
+ id="path4195"
+ d="M 1023.9948,727.6158 L 1023.9948,731.43749 L 1023.9948,727.6158 z "
+ style="fill:none;fill-rule:evenodd;stroke:#49b83c;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#73c890;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4197"
+ width="19.000404"
+ height="4.0004034"
+ x="1015.0414"
+ y="727.71289" />
+ <g
+ transform="translate(0.1771012,-38.795917)"
+ id="g4199">
+ <path
+ id="path4201"
+ d="M 1021.0703,782.19928 L 1021.1076,776.58278 L 1027.6873,776.46724 L 1027.6798,782.32766 L 1028.8523,782.41752 L 1024.3788,787.14822 L 1019.838,782.50097 L 1021.0703,782.19928 z "
+ style="fill:#6ec527;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ style="fill:url(#radialGradient4215);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 1021.0703,782.19928 L 1021.1076,776.58278 L 1027.6873,776.46724 L 1027.8867,782.42929 L 1023.6889,786.52145 L 1019.838,782.50097 L 1021.0703,782.19928 z "
+ id="path4203" />
+ <path
+ id="path4205"
+ d="M 1020.8642,781.38404 L 1020.8642,776.38384 L 1027.8646,776.38384 L 1027.8646,781.38404"
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4217);stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="path4207"
+ d="M 1027.8606,782.31893 L 1029.314,782.31893 L 1024.3645,787.26844 L 1019.415,782.31897 L 1020.8619,782.31896"
+ style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#00561d;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <rect
+ y="727.71289"
+ x="1015.0414"
+ height="4.0004034"
+ width="19.000404"
+ id="rect4209"
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4219);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/editorWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/editorWindow.svg
new file mode 100644
index 0000000000..641f5dc99a
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/editorWindow.svg
@@ -0,0 +1,614 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="500"
+ height="500"
+ id="svg2160"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\editorwindow"
+ sodipodi:docname="seamonkey-editorwindowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2162">
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2242"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2239"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4174786"
+ inkscape:cx="310.43662"
+ inkscape:cy="193.31735"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="false"
+ inkscape:document-units="px"
+ inkscape:object-paths="false"
+ inkscape:guide-bbox="false"
+ width="500px"
+ height="500px"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4" />
+ <metadata
+ id="metadata2165">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ id="g2435">
+ <g
+ transform="translate(-216.72313,-127.76887)"
+ id="g2405">
+ <g
+ id="layer3"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g2408"
+ style="display:inline">
+ <g
+ id="g2219">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path6573"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2064"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2056"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2058"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2052"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="layer2"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ id="g2417"
+ transform="matrix(3.5766367,0,0,3.5766367,277.69915,379.568)">
+ <path
+ id="path2419"
+ d="M -25.69054,-11.447732 L -20.42976,-25.542167 L -8.443661,-23.372518 L -10.601929,-8.4416933 L -25.69054,-11.447732 z "
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path2421"
+ d="M -19.730935,-21.959623 L -18.981803,-21.82402 L -18.232672,-21.688417 L -17.483541,-21.552814 L -16.73441,-21.417211 L -15.985279,-21.281608 L -15.236148,-21.146004 L -14.487016,-21.010401 L -13.737885,-20.874798 L -12.988754,-20.739195 L -12.239623,-20.603592 L -11.490492,-20.467989 L -10.74136,-20.332386 L -10.74136,-20.332386 L -11.490492,-20.467989 L -12.239623,-20.603592 L -12.988754,-20.739195 L -13.737885,-20.874798 L -14.487016,-21.010401 L -15.236148,-21.146004 L -15.985279,-21.281608 L -16.73441,-21.417211 L -17.483541,-21.552814 L -18.232672,-21.688417 L -18.981803,-21.82402 L -19.730935,-21.959623 L -19.730935,-21.959623 z "
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="M -20.458605,-19.575327 L -19.709473,-19.439724 L -18.960342,-19.304121 L -18.211211,-19.168518 L -17.46208,-19.032915 L -16.712949,-18.897312 L -15.963818,-18.761708 L -15.214686,-18.626105 L -14.465555,-18.490502 L -13.716424,-18.354899 L -12.967293,-18.219296 L -12.218162,-18.083693 L -11.46903,-17.94809 L -11.46903,-17.94809 L -12.218162,-18.083693 L -12.967293,-18.219296 L -13.716424,-18.354899 L -14.465555,-18.490502 L -15.214686,-18.626105 L -15.963818,-18.761708 L -16.712949,-18.897312 L -17.46208,-19.032915 L -18.211211,-19.168518 L -18.960342,-19.304121 L -19.709473,-19.439724 L -20.458605,-19.575327 L -20.458605,-19.575327 z "
+ id="path2423"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ <path
+ style="fill:#a2c2de;fill-opacity:1;fill-rule:nonzero;stroke:#2271bc;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -8.450037,-28.245275 C -9.420395,-26.238694 -10.616354,-24.313477 -12.132159,-22.697635 C -12.641407,-22.196866 -13.190492,-21.631598 -13.882174,-21.492142 C -14.427834,-21.408687 -14.556175,-22.084568 -14.500723,-22.532054 C -14.354903,-23.951114 -13.741697,-25.291418 -13.1189,-26.572429 C -12.149191,-28.457126 -10.993434,-30.277678 -9.519359,-31.782166 C -9.032097,-32.232322 -8.49546,-32.774648 -7.836561,-32.827686 C -7.291745,-32.801069 -7.269691,-32.090934 -7.325158,-31.639412 C -7.47331,-30.476317 -7.936103,-29.365169 -8.425154,-28.298074"
+ id="path2425" />
+ <path
+ sodipodi:nodetypes="csssccscc"
+ id="path2427"
+ d="M -12.873502,-21.508276 C -13.375914,-20.990118 -13.915946,-20.66968 -14.313856,-20.496013 C -14.475416,-20.425499 -14.623363,-20.21798 -14.897383,-20.341083 C -15.133173,-20.45163 -15.335403,-20.626182 -15.370103,-20.787355 C -15.457233,-21.192089 -15.314553,-22.068353 -15.081013,-22.664383 C -14.919413,-23.069645 -14.678127,-23.677824 -14.411317,-24.107796 C -13.691287,-24.17018 -13.068023,-23.963813 -12.892583,-23.819889 C -12.436783,-23.445971 -12.093628,-23.062393 -11.971238,-22.510365 C -12.422151,-21.981784 -12.616183,-21.78056 -12.873502,-21.508276 z "
+ style="fill:#000000;fill-opacity:0.99215686;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccssccsscc"
+ id="path2429"
+ d="M -13.278907,-20.408924 C -13.38703,-19.818698 -13.573727,-19.146174 -14.080182,-18.387007 C -14.20121,-18.230378 -15.11258,-18.762331 -16.227512,-17.258169 C -16.512233,-16.87405 -14.680461,-20.608301 -14.817256,-20.691263 C -14.953919,-20.783848 -16.361644,-17.143445 -16.331385,-17.272462 C -16.011603,-18.635923 -16.910535,-19.207298 -16.863499,-19.43194 C -16.657126,-20.306702 -16.256404,-21.090837 -15.67504,-21.755373 C -15.374589,-22.081497 -15.040811,-22.395354 -14.547506,-22.571183 C -14.352017,-22.629374 -14.112969,-22.68914 -13.811153,-22.565829 C -13.536122,-22.439311 -13.400975,-22.202936 -13.328095,-22.020946 C -13.15717,-21.557472 -13.153285,-21.052939 -13.278907,-20.408924 z "
+ style="fill:#c5c5c5;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path2431"
+ d="M -21.129721,-16.757843 L -20.380589,-16.62224 L -19.631458,-16.486637 L -18.882327,-16.351034 L -18.133196,-16.215431 L -17.384065,-16.079828 L -16.634934,-15.944224 L -15.885802,-15.808621 L -15.136671,-15.673018 L -14.38754,-15.537415 L -13.638409,-15.401812 L -12.889278,-15.266209 L -12.140146,-15.130606 L -12.140146,-15.130606 L -12.889278,-15.266209 L -13.638409,-15.401812 L -14.38754,-15.537415 L -15.136671,-15.673018 L -15.885802,-15.808621 L -16.634934,-15.944224 L -17.384065,-16.079828 L -18.133196,-16.215431 L -18.882327,-16.351034 L -19.631458,-16.486637 L -20.380589,-16.62224 L -21.129721,-16.757843 L -21.129721,-16.757843 z "
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="M -22.000837,-14.036738 L -21.251705,-13.901135 L -20.502574,-13.765532 L -19.753443,-13.629929 L -19.004312,-13.494326 L -18.255181,-13.358723 L -17.50605,-13.223119 L -16.756918,-13.087516 L -16.007787,-12.951913 L -15.258656,-12.81631 L -14.509525,-12.680707 L -13.760394,-12.545104 L -13.011262,-12.409501 L -13.011262,-12.409501 L -13.760394,-12.545104 L -14.509525,-12.680707 L -15.258656,-12.81631 L -16.007787,-12.951913 L -16.756918,-13.087516 L -17.50605,-13.223119 L -18.255181,-13.358723 L -19.004312,-13.494326 L -19.753443,-13.629929 L -20.502574,-13.765532 L -21.251705,-13.901135 L -22.000837,-14.036738 L -22.000837,-14.036738 z "
+ id="path2433"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ </g>
+ </g>
+ <g
+ id="g2276">
+ <g
+ style="fill:none"
+ id="g2259"
+ transform="translate(-37.98689,-134.35389)">
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="Shadow"
+ id="g2261" />
+ <g
+ style="display:inline;fill:none"
+ id="g2263"
+ inkscape:label="Background">
+ <g
+ style="fill:none"
+ id="g2265">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2267"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path2269"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2271"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2273"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2275"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="SeaMonkey"
+ id="g2277" />
+ </g>
+ <g
+ transform="matrix(3.5766367,0,0,3.5766367,456.43539,372.98298)"
+ id="g2279">
+ <path
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -25.69054,-11.447732 L -20.42976,-25.542167 L -8.443661,-23.372518 L -10.601929,-8.4416933 L -25.69054,-11.447732 z "
+ id="path2282" />
+ <path
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="M -19.730935,-21.959623 L -18.981803,-21.82402 L -18.232672,-21.688417 L -17.483541,-21.552814 L -16.73441,-21.417211 L -15.985279,-21.281608 L -15.236148,-21.146004 L -14.487016,-21.010401 L -13.737885,-20.874798 L -12.988754,-20.739195 L -12.239623,-20.603592 L -11.490492,-20.467989 L -10.74136,-20.332386 L -10.74136,-20.332386 L -11.490492,-20.467989 L -12.239623,-20.603592 L -12.988754,-20.739195 L -13.737885,-20.874798 L -14.487016,-21.010401 L -15.236148,-21.146004 L -15.985279,-21.281608 L -16.73441,-21.417211 L -17.483541,-21.552814 L -18.232672,-21.688417 L -18.981803,-21.82402 L -19.730935,-21.959623 L -19.730935,-21.959623 z "
+ id="path2284"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path2286"
+ d="M -20.458605,-19.575327 L -19.709473,-19.439724 L -18.960342,-19.304121 L -18.211211,-19.168518 L -17.46208,-19.032915 L -16.712949,-18.897312 L -15.963818,-18.761708 L -15.214686,-18.626105 L -14.465555,-18.490502 L -13.716424,-18.354899 L -12.967293,-18.219296 L -12.218162,-18.083693 L -11.46903,-17.94809 L -11.46903,-17.94809 L -12.218162,-18.083693 L -12.967293,-18.219296 L -13.716424,-18.354899 L -14.465555,-18.490502 L -15.214686,-18.626105 L -15.963818,-18.761708 L -16.712949,-18.897312 L -17.46208,-19.032915 L -18.211211,-19.168518 L -18.960342,-19.304121 L -19.709473,-19.439724 L -20.458605,-19.575327 L -20.458605,-19.575327 z "
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ id="path2288"
+ d="M -8.450037,-28.245275 C -9.420395,-26.238694 -10.616354,-24.313477 -12.132159,-22.697635 C -12.641407,-22.196866 -13.190492,-21.631598 -13.882174,-21.492142 C -14.427834,-21.408687 -14.556175,-22.084568 -14.500723,-22.532054 C -14.354903,-23.951114 -13.741697,-25.291418 -13.1189,-26.572429 C -12.149191,-28.457126 -10.993434,-30.277678 -9.519359,-31.782166 C -9.032097,-32.232322 -8.49546,-32.774648 -7.836561,-32.827686 C -7.291745,-32.801069 -7.269691,-32.090934 -7.325158,-31.639412 C -7.47331,-30.476317 -7.936103,-29.365169 -8.425154,-28.298074"
+ style="fill:#a2c2de;fill-opacity:1;fill-rule:nonzero;stroke:#2271bc;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ style="fill:#000000;fill-opacity:0.99215686;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -12.873502,-21.508276 C -13.375914,-20.990118 -13.915946,-20.66968 -14.313856,-20.496013 C -14.475416,-20.425499 -14.623363,-20.21798 -14.897383,-20.341083 C -15.133173,-20.45163 -15.335403,-20.626182 -15.370103,-20.787355 C -15.457233,-21.192089 -15.314553,-22.068353 -15.081013,-22.664383 C -14.919413,-23.069645 -14.678127,-23.677824 -14.411317,-24.107796 C -13.691287,-24.17018 -13.068023,-23.963813 -12.892583,-23.819889 C -12.436783,-23.445971 -12.093628,-23.062393 -11.971238,-22.510365 C -12.422151,-21.981784 -12.616183,-21.78056 -12.873502,-21.508276 z "
+ id="path2290"
+ sodipodi:nodetypes="csssccscc" />
+ <path
+ style="fill:#c5c5c5;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.25;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -13.278907,-20.408924 C -13.38703,-19.818698 -13.573727,-19.146174 -14.080182,-18.387007 C -14.20121,-18.230378 -15.11258,-18.762331 -16.227512,-17.258169 C -16.512233,-16.87405 -14.680461,-20.608301 -14.817256,-20.691263 C -14.953919,-20.783848 -16.361644,-17.143445 -16.331385,-17.272462 C -16.011603,-18.635923 -16.910535,-19.207298 -16.863499,-19.43194 C -16.657126,-20.306702 -16.256404,-21.090837 -15.67504,-21.755373 C -15.374589,-22.081497 -15.040811,-22.395354 -14.547506,-22.571183 C -14.352017,-22.629374 -14.112969,-22.68914 -13.811153,-22.565829 C -13.536122,-22.439311 -13.400975,-22.202936 -13.328095,-22.020946 C -13.15717,-21.557472 -13.153285,-21.052939 -13.278907,-20.408924 z "
+ id="path2292"
+ sodipodi:nodetypes="cccssccsscc" />
+ <path
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ d="M -21.129721,-16.757843 L -20.380589,-16.62224 L -19.631458,-16.486637 L -18.882327,-16.351034 L -18.133196,-16.215431 L -17.384065,-16.079828 L -16.634934,-15.944224 L -15.885802,-15.808621 L -15.136671,-15.673018 L -14.38754,-15.537415 L -13.638409,-15.401812 L -12.889278,-15.266209 L -12.140146,-15.130606 L -12.140146,-15.130606 L -12.889278,-15.266209 L -13.638409,-15.401812 L -14.38754,-15.537415 L -15.136671,-15.673018 L -15.885802,-15.808621 L -16.634934,-15.944224 L -17.384065,-16.079828 L -18.133196,-16.215431 L -18.882327,-16.351034 L -19.631458,-16.486637 L -20.380589,-16.62224 L -21.129721,-16.757843 L -21.129721,-16.757843 z "
+ id="path2294"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path2296"
+ d="M -22.000837,-14.036738 L -21.251705,-13.901135 L -20.502574,-13.765532 L -19.753443,-13.629929 L -19.004312,-13.494326 L -18.255181,-13.358723 L -17.50605,-13.223119 L -16.756918,-13.087516 L -16.007787,-12.951913 L -15.258656,-12.81631 L -14.509525,-12.680707 L -13.760394,-12.545104 L -13.011262,-12.409501 L -13.011262,-12.409501 L -13.760394,-12.545104 L -14.509525,-12.680707 L -15.258656,-12.81631 L -16.007787,-12.951913 L -16.756918,-13.087516 L -17.50605,-13.223119 L -18.255181,-13.358723 L -19.004312,-13.494326 L -19.753443,-13.629929 L -20.502574,-13.765532 L -21.251705,-13.901135 L -22.000837,-14.036738 L -22.000837,-14.036738 z "
+ style="fill:#ffffff;fill-rule:evenodd;stroke:#8d8888;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/findBookmarkWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/findBookmarkWindow.svg
new file mode 100644
index 0000000000..a275b96e5d
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/findBookmarkWindow.svg
@@ -0,0 +1,1769 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="500"
+ height="500"
+ id="svg2160"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\bmPropsWindow"
+ sodipodi:docname="seamonkey-bmPropsWindowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2162">
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2242"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2239"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient4590"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4588"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4586"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient4353"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4351"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4349"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient4305"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4303"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4301"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3160"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3099"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3097"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3056"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3054"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2967"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2965"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2756"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2758"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2760"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2762"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.6567"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient2764"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient2766"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.6268"
+ fx="834.16461"
+ cy="416.6268"
+ cx="834.16461"
+ id="radialGradient2768"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient2770"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.03826"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient2772"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient2774"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.8067"
+ fx="665.83667"
+ cy="613.8067"
+ cx="665.83667"
+ id="radialGradient2776"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient2778"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.7605"
+ fx="734.74274"
+ cy="565.7605"
+ cx="734.74274"
+ id="radialGradient2780"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient2782"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient2784"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient2786"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43961"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient2788"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient2790"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient2792"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient2794"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient2796"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2798">
+ <stop
+ id="stop2800"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2802"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2804">
+ <stop
+ id="stop2806"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop2808" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop2810" />
+ <stop
+ id="stop2812"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2814">
+ <stop
+ id="stop2816"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop2818"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2820">
+ <stop
+ id="stop2822"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2824"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2826">
+ <stop
+ id="stop2828"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2830"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2832">
+ <stop
+ id="stop2834"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2836"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2838">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop2840" />
+ <stop
+ id="stop2842"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop2844" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2846">
+ <stop
+ id="stop2848"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop2850" />
+ <stop
+ id="stop2852"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2854">
+ <stop
+ id="stop2856"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop2858" />
+ <stop
+ id="stop2860"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3776"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3841"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3843"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient2889">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2891" />
+ <stop
+ id="stop2893"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2895" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2881">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2883" />
+ <stop
+ id="stop2885"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2887" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2873">
+ <stop
+ id="stop2875"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2877" />
+ <stop
+ id="stop2879"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2867">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2869" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2871" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2861">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2863" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2865" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2855">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2857" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2859" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2849">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2851" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2853" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2839">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2841" />
+ <stop
+ id="stop2843"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2845"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2847" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2833">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2835" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2837" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2831"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2829"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2827"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2825"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2823"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43961"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2821"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2819"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2817"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2815"
+ cx="734.74274"
+ cy="565.7605"
+ fx="734.74274"
+ fy="565.7605"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2813"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2811"
+ cx="665.83667"
+ cy="613.8067"
+ fx="665.83667"
+ fy="613.8067"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2809"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2807"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.03826"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2805"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2803"
+ cx="834.16461"
+ cy="416.6268"
+ fx="834.16461"
+ fy="416.6268"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2801"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2799"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.6567"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2797"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2795"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2793"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2791"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient3199">
+ <stop
+ id="stop3201"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.5"
+ id="stop3216" />
+ <stop
+ id="stop3218"
+ offset="0.8035714"
+ style="stop-color:#ffffff;stop-opacity:0.24705882;" />
+ <stop
+ id="stop3203"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient3076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3100"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3119"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.64665836"
+ inkscape:cx="210.56928"
+ inkscape:cy="191.36374"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ width="500px"
+ height="500px"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4" />
+ <metadata
+ id="metadata2165">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <image
+ y="204.13028"
+ x="182.13039"
+ id="image2241"
+ height="24"
+ width="27"
+ sodipodi:absref="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\seamonkeydefault\preview-magnify-glass.png"
+ xlink:href="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\seamonkeydefault\preview-magnify-glass.png" />
+ <g
+ id="g3223"
+ transform="translate(238.13028,210.13029)">
+ <path
+ transform="matrix(0.9078077,-0.9078077,1.1542663,1.1542663,-23.246509,10.180673)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 9.6429272,4.4773731 A 1.7520146,4.746367 0 1 1 9.6371875,4.4350537"
+ sodipodi:ry="4.746367"
+ sodipodi:rx="1.7520146"
+ sodipodi:cy="6.093154"
+ sodipodi:cx="7.9955573"
+ id="path2244"
+ style="opacity:1;fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#662607;stroke-width:0.34538627;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.95686275"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(1.0725343,0,0,1.0724923,-0.1130571,-1.0065792)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:ry="4.4278188"
+ sodipodi:rx="4.4278188"
+ sodipodi:cy="3.7358978"
+ sodipodi:cx="-14.812487"
+ id="path2248"
+ style="opacity:1;fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.46619469;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g3236"
+ transform="translate(210.13028,210.13029)">
+ <path
+ id="path3229"
+ d="M 9.6429272,4.4773731 C 9.9721561,6.94088 9.5013347,9.6641177 8.5919865,10.556029 C 7.6826383,11.44794 6.6774163,10.172442 6.3481874,7.7089348 C 6.0189585,5.2454279 6.48978,2.5221902 7.3991281,1.6302791 C 8.3029773,0.74376171 9.3013787,1.9949757 9.6371875,4.4350537"
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.34538627;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ transform="matrix(0.9078077,-0.9078077,1.1542663,1.1542663,-18.875211,35.699686)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.46619469;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3231"
+ sodipodi:cx="-14.812487"
+ sodipodi:cy="3.7358978"
+ sodipodi:rx="4.4278188"
+ sodipodi:ry="4.4278188"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(1.0725343,0,0,1.0724923,4.2582376,24.512434)" />
+ </g>
+ <g
+ id="g3254"
+ transform="translate(210.13028,210.13029)">
+ <path
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 22.484347,32.246116 C 25.564479,34.76161 28.253532,38.258139 28.486698,40.050869 C 28.719862,41.843595 26.409279,41.257004 23.329144,38.741509 C 20.249014,36.226017 17.559961,32.729487 17.326794,30.936758 C 17.09504,29.154872 19.372194,29.719733 22.431363,32.202977"
+ id="path3242" />
+ <path
+ transform="matrix(1.0443307,0,0,1.0442899,31.33439,24.634086)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:ry="4.4278188"
+ sodipodi:rx="4.4278188"
+ sodipodi:cy="3.7358978"
+ sodipodi:cx="-14.812487"
+ id="path3244"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g2388"
+ transform="translate(-89.791305,-184.77715)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="layer3" />
+ <g
+ style="display:inline"
+ id="g2391"
+ inkscape:label="Background">
+ <g
+ id="g2219">
+ <path
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6573"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:url(#linearGradient2242);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path2064"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:url(#linearGradient2239);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2056"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2058"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2052"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="layer2" />
+ </g>
+ <g
+ transform="translate(-247.78438,-65.963114)"
+ id="g4261">
+ <g
+ id="g4263"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4265"
+ style="display:inline">
+ <g
+ id="g4267">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4269"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4271"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient3119);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4273"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient3121);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4275"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4277"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g4279"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="matrix(1.2522169,0,0,1.1949279,445.5891,111.78335)"
+ id="g4281">
+ <path
+ inkscape:export-ydpi="80.222176"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ id="path4283"
+ d="M -227.62113,184.74074 C -232.11524,184.64538 -236.59155,187.11474 -238.81213,191.02064 C -241.45058,195.3896 -240.88344,201.21399 -237.62962,205.11996 L -231.17767,212.07609 L -224.89082,218.68023 L -218.60879,225.45787 L -212.14722,232.06698 L -205.86038,238.6711 L -199.57835,245.44876 L -193.29149,252.05287 L -186.82992,258.66198 L -187.26399,249.0989 L -187.5185,239.36725 L -177.92762,240.33506 L -168.33671,241.30286 L -174.62354,234.69874 L -181.08512,228.08963 L -187.36717,221.31198 L -193.65399,214.70786 L -200.11558,208.09876 L -206.3976,201.32111 L -212.68446,194.71697 L -219.14604,188.10786 C -220.93634,186.44951 -223.28444,185.38524 -225.70391,184.96901 C -226.34103,184.84661 -226.97911,184.75437 -227.62113,184.74074 z "
+ style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000080;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="76.790672"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 1 1 -6.4755362,3.517194"
+ sodipodi:ry="0.77599555"
+ sodipodi:rx="0.78960949"
+ sodipodi:cy="3.788281"
+ sodipodi:cx="-7.2153969"
+ id="path4285"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <path
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:3.62059999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 182.01143,367.29502 C 167.1422,379.43849 154.16088,396.31787 153.03528,404.97221 C 151.90969,413.62654 163.06395,410.79479 177.9332,398.65132 C 192.80243,386.50786 205.78374,369.62848 206.90935,360.97414 C 208.02813,352.37215 197.03525,355.09899 182.26721,367.08677"
+ id="path3125" />
+ <path
+ transform="matrix(-5.0414718,0,0,5.0412748,139.28814,330.5482)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:ry="4.4278188"
+ sodipodi:rx="4.4278188"
+ sodipodi:cy="3.7358978"
+ sodipodi:cx="-14.812487"
+ id="path3127"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <g
+ id="g3129"
+ transform="matrix(-4.8274668,0,0,4.8274668,451.4283,218.35912)">
+ <path
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 22.484347,32.246116 C 25.564479,34.76161 28.253532,38.258139 28.486698,40.050869 C 28.719862,41.843595 26.409279,41.257004 23.329144,38.741509 C 20.249014,36.226017 17.559961,32.729487 17.326794,30.936758 C 17.09504,29.154872 19.372194,29.719733 22.431363,32.202977"
+ id="path3131" />
+ <path
+ transform="matrix(1.0443307,0,0,1.0442899,31.33439,24.634086)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:ry="4.4278188"
+ sodipodi:rx="4.4278188"
+ sodipodi:cy="3.7358978"
+ sodipodi:cx="-14.812487"
+ id="path3133"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g2409"
+ transform="translate(-343.79172,-234.91492)"
+ style="fill:none">
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="Shadow"
+ id="g2411" />
+ <g
+ style="display:inline;fill:none"
+ id="g2413"
+ inkscape:label="Background">
+ <g
+ id="g2415"
+ style="fill:none">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2417"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path2419"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2421"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2423"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2425"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline;fill:none"
+ inkscape:label="SeaMonkey"
+ id="g2427" />
+ </g>
+ <g
+ id="g2429"
+ transform="matrix(1.2522169,0,0,1.1949279,625.57862,-159.03968)">
+ <path
+ style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000080;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -227.62113,184.74074 C -232.11524,184.64538 -236.59155,187.11474 -238.81213,191.02064 C -241.45058,195.3896 -240.88344,201.21399 -237.62962,205.11996 L -231.17767,212.07609 L -224.89082,218.68023 L -218.60879,225.45787 L -212.14722,232.06698 L -205.86038,238.6711 L -199.57835,245.44876 L -193.29149,252.05287 L -186.82992,258.66198 L -187.26399,249.0989 L -187.5185,239.36725 L -177.92762,240.33506 L -168.33671,241.30286 L -174.62354,234.69874 L -181.08512,228.08963 L -187.36717,221.31198 L -193.65399,214.70786 L -200.11558,208.09876 L -206.3976,201.32111 L -212.68446,194.71697 L -219.14604,188.10786 C -220.93634,186.44951 -223.28444,185.38524 -225.70391,184.96901 C -226.34103,184.84661 -226.97911,184.75437 -227.62113,184.74074 z "
+ id="path2431"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-ydpi="80.222176" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path2433"
+ sodipodi:cx="-7.2153969"
+ sodipodi:cy="3.788281"
+ sodipodi:rx="0.78960949"
+ sodipodi:ry="0.77599555"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 1 1 -6.4755362,3.517194"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-ydpi="76.790672" />
+ </g>
+ <path
+ id="path2435"
+ d="M 86.00409,198.34322 C 71.13486,210.48669 58.15354,227.36607 57.02794,236.02041 C 55.90235,244.67474 67.05661,241.84299 81.92586,229.69952 C 96.79509,217.55606 109.7764,200.67668 110.90201,192.02234 C 112.02079,183.42035 101.02791,186.14719 86.25987,198.13497"
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:3.62059999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path2437"
+ sodipodi:cx="-14.812487"
+ sodipodi:cy="3.7358978"
+ sodipodi:rx="4.4278188"
+ sodipodi:ry="4.4278188"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(-5.0414718,0,0,5.0412748,43.2808,161.5964)" />
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/findHistoryWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/findHistoryWindow.svg
new file mode 100644
index 0000000000..1316015835
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/findHistoryWindow.svg
@@ -0,0 +1,2325 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\findHistorywindow"
+ sodipodi:docname="seamonkey-findHistorywindowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter4288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="8.1055212"
+ id="feGaussianBlur4290" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4612"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4614"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient3152"
+ inkscape:collect="always">
+ <stop
+ id="stop3154"
+ offset="0"
+ style="stop-color:#63ba0f;stop-opacity:1;" />
+ <stop
+ id="stop3156"
+ offset="1"
+ style="stop-color:#509e07;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3102"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3100"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3076"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3074"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3072"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3027"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3025"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient3199">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3201" />
+ <stop
+ id="stop3216"
+ offset="0.5"
+ style="stop-color:#ffffff;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.24705882;"
+ offset="0.8035714"
+ id="stop3218" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3203" />
+ </linearGradient>
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2791"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2793"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2795"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2797"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.6567"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient2799"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient2801"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.6268"
+ fx="834.16461"
+ cy="416.6268"
+ cx="834.16461"
+ id="radialGradient2803"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient2805"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.03826"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient2807"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient2809"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.8067"
+ fx="665.83667"
+ cy="613.8067"
+ cx="665.83667"
+ id="radialGradient2811"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient2813"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.7605"
+ fx="734.74274"
+ cy="565.7605"
+ cx="734.74274"
+ id="radialGradient2815"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient2817"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient2819"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient2821"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43961"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient2823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient2825"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient2827"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient2829"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient2831"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2833">
+ <stop
+ id="stop2835"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2837"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2839">
+ <stop
+ id="stop2841"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop2843" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop2845" />
+ <stop
+ id="stop2847"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2849">
+ <stop
+ id="stop2851"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop2853"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2855">
+ <stop
+ id="stop2857"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2859"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2861">
+ <stop
+ id="stop2863"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2865"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2867">
+ <stop
+ id="stop2869"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2871"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2873">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop2875" />
+ <stop
+ id="stop2877"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop2879" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2881">
+ <stop
+ id="stop2883"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop2885" />
+ <stop
+ id="stop2887"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2889">
+ <stop
+ id="stop2891"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop2893" />
+ <stop
+ id="stop2895"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3843"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3841"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3776"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3774"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2854">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2856" />
+ <stop
+ id="stop2858"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2860" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2846">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2848" />
+ <stop
+ id="stop2850"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2852" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2838">
+ <stop
+ id="stop2840"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2842" />
+ <stop
+ id="stop2844"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2832">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2834" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2836" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2826">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2828" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2830" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2820">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2822" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2824" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2814">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2816" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2818" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2804">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2806" />
+ <stop
+ id="stop2808"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2810"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2798">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2800" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2802" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2796"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2794"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2792"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2790"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2788"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43961"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2786"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2784"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2782"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2780"
+ cx="734.74274"
+ cy="565.7605"
+ fx="734.74274"
+ fy="565.7605"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2778"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2776"
+ cx="665.83667"
+ cy="613.8067"
+ fx="665.83667"
+ fy="613.8067"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2774"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2772"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.03826"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2770"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2768"
+ cx="834.16461"
+ cy="416.6268"
+ fx="834.16461"
+ fy="416.6268"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2766"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2764"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.6567"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2756"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3097"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3099"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient3160"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient4305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient4353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4586"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4588"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient4590"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ id="linearGradient3350">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop3352" />
+ <stop
+ id="stop3354"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop3356" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3342">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop3344" />
+ <stop
+ id="stop3346"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop3348" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3334">
+ <stop
+ id="stop3336"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop3338" />
+ <stop
+ id="stop3340"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3328">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop3330" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3332" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3322">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop3324" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3326" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3316">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop3318" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3320" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3310">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop3312" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop3314" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3300">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop3302" />
+ <stop
+ id="stop3304"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop3306"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop3308" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3294">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3296" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3298" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient3292"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient3290"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3288"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3286"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient3284"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43960"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3282"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3280"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3278"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3276"
+ cx="734.74274"
+ cy="565.76050"
+ fx="734.74274"
+ fy="565.76050"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3274"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3272"
+ cx="665.83667"
+ cy="613.80670"
+ fx="665.83667"
+ fy="613.80670"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3270"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3268"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.038260"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3266"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3264"
+ cx="834.16461"
+ cy="416.62680"
+ fx="834.16461"
+ fy="416.62680"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3262"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3260"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.656700"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3152"
+ id="linearGradient3885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.4893755,0,0,2.4893755,754.50881,-27.065893)"
+ spreadMethod="pad"
+ x1="-136.54431"
+ y1="58.386353"
+ x2="-130.13719"
+ y2="78.847557" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3152"
+ id="linearGradient4319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.4893755,0,0,2.4893755,811.77658,331.14465)"
+ spreadMethod="pad"
+ x1="-136.54431"
+ y1="58.386353"
+ x2="-130.13719"
+ y2="78.847557" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3308"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4340442"
+ inkscape:cx="212.32122"
+ inkscape:cy="576.0591"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="0"
+ inkscape:window-y="22" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3212"
+ transform="translate(-165.475,356.80547)">
+ <g
+ transform="translate(-148.79055,-318.30513)"
+ id="g4024">
+ <g
+ id="g4026"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g4028"
+ style="display:inline">
+ <g
+ id="g4030">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4032"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path4034"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient3308);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path4036"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient3311);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path4038"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path4040"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g4042"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="translate(-153.84722,-21.867746)"
+ id="g4044">
+ <path
+ transform="matrix(0.2477023,0,0,0.2079683,648.47198,64.124139)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -661.87975,209.38359 A 127.14286,151.42857 0 1 1 -662.29628,208.03343"
+ sodipodi:ry="151.42857"
+ sodipodi:rx="127.14286"
+ sodipodi:cy="260.93362"
+ sodipodi:cx="-781.42859"
+ id="path4046"
+ style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <text
+ id="text4048"
+ y="93.797005"
+ x="451.29788"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="93.797005"
+ x="451.29788"
+ id="tspan4050"
+ sodipodi:role="line">XII</tspan></text>
+ <text
+ id="text4052"
+ y="97.519417"
+ x="466.95267"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="466.95267"
+ id="tspan4054"
+ sodipodi:role="line">I</tspan></text>
+ <text
+ id="text4056"
+ y="106.09559"
+ x="475.9686"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="106.09559"
+ x="475.9686"
+ id="tspan4058"
+ sodipodi:role="line">II</tspan></text>
+ <text
+ id="text4060"
+ y="119.60712"
+ x="479.48703"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="119.60712"
+ x="479.48703"
+ id="tspan4062"
+ sodipodi:role="line">III</tspan></text>
+ <text
+ id="text4064"
+ y="133.25336"
+ x="475.19901"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="133.25336"
+ x="475.19901"
+ id="tspan4066"
+ sodipodi:role="line">IV</tspan></text>
+ <text
+ id="text4068"
+ y="142.88013"
+ x="466.40292"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="142.88013"
+ x="466.40292"
+ id="tspan4070"
+ sodipodi:role="line">V</tspan></text>
+ <text
+ id="text4072"
+ y="146.44739"
+ x="451.66959"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="146.44739"
+ x="451.66959"
+ id="tspan4074"
+ sodipodi:role="line">VI</tspan></text>
+ <text
+ id="text4076"
+ y="143.14886"
+ x="437.59592"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="143.14886"
+ x="437.59592"
+ id="tspan4078"
+ sodipodi:role="line">VII</tspan></text>
+ <text
+ id="text4080"
+ y="133.09454"
+ x="428.68991"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="133.09454"
+ x="428.68991"
+ id="tspan4082"
+ sodipodi:role="line">VIII</tspan></text>
+ <text
+ id="text4084"
+ y="119.50953"
+ x="426.38095"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="119.50953"
+ x="426.38095"
+ id="tspan4086"
+ sodipodi:role="line">IX</tspan></text>
+ <text
+ id="text4088"
+ y="105.9856"
+ x="430.44913"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="105.9856"
+ x="430.44913"
+ id="tspan4090"
+ sodipodi:role="line">X</tspan></text>
+ <text
+ id="text4092"
+ y="97.519417"
+ x="439.02524"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="439.02524"
+ id="tspan4094"
+ sodipodi:role="line">XI</tspan></text>
+ <g
+ transform="matrix(0.5000001,0,0,0.5000001,64.050849,-145.21778)"
+ id="g4096">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ id="path4098"
+ sodipodi:nodetypes="ccccccccccccccccccccc" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ id="path4100"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ transform="matrix(-0.4291897,-0.256508,0.256508,-0.4291897,652.78758,549.26748)"
+ id="g4102">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path4104"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 779.71875,530.75 L 779.71875,533.625 L 779.71875,536.5 L 783.71875,536.5 L 783.71875,533.625 L 783.71875,530.75 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path4106"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <path
+ transform="matrix(0.5000001,0,0,0.5000001,67.279699,-148.03415)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 778.6727,531.61082 A 3.6283669,3.6283669 0 1 1 778.66081,531.57847"
+ sodipodi:ry="3.6283669"
+ sodipodi:rx="3.6283669"
+ sodipodi:cy="532.84601"
+ sodipodi:cx="775.26105"
+ id="path4108"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="3.7992489"
+ inkscape:original="M -784.625 119.21875 C -823.30724 120.2578 -857.85589 145.69751 -876.65625 178.4375 C -911.16585 236.5006 -905.33598 317.22678 -859.21875 367.5 C -835.95661 393.00489 -800.00341 407.76292 -765.34375 401.375 C -725.99105 394.88795 -694.34644 364.38152 -678.625 328.90625 C -652.69932 272.153 -661.28225 199.33022 -704.71875 153.46875 C -725.24632 131.86038 -754.52802 118.0423 -784.625 119.21875 z "
+ style="fill:#ffffff;fill-opacity:0.23602486;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4288)"
+ id="path4110"
+ d="M -784.71875,115.40625 C -825.00423,116.48836 -860.55945,142.78523 -879.9375,176.53125 C -915.33331,236.08542 -909.41022,318.41384 -862.03125,370.0625 C -837.94741,396.46832 -800.80225,411.78686 -764.65625,405.125 C -723.73999,398.38021 -691.31594,366.90167 -675.15625,330.4375 C -648.58851,272.27873 -657.3233,197.98167 -701.96875,150.84375 C -723.18185,128.51376 -753.42796,114.21194 -784.78125,115.4375 L -784.71875,115.40625 z "
+ transform="matrix(0.2477023,0,0,0.2079683,648.46968,64.107569)" />
+ </g>
+ <g
+ transform="matrix(4.13789,0,0,4.13789,186.93117,5.0293239)"
+ id="g4112">
+ <path
+ id="path4114"
+ d="M 22.484347,32.246116 C 25.564479,34.76161 28.253532,38.258139 28.486698,40.050869 C 28.719862,41.843595 26.409279,41.257004 23.329144,38.741509 C 20.249014,36.226017 17.559961,32.729487 17.326794,30.936758 C 17.09504,29.154872 19.372194,29.719733 22.431363,32.202977"
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4116"
+ sodipodi:cx="-14.812487"
+ sodipodi:cy="3.7358978"
+ sodipodi:rx="4.4278188"
+ sodipodi:ry="4.4278188"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(1.0443307,0,0,1.0442899,31.33439,24.634086)" />
+ </g>
+ </g>
+ <g
+ id="g3116">
+ <g
+ style="fill:none"
+ id="g4126"
+ transform="translate(-136.67528,38.617765)">
+ <g
+ style="fill:none;display:inline"
+ inkscape:label="Shadow"
+ id="g4128" />
+ <g
+ style="fill:none;display:inline"
+ id="g4130"
+ inkscape:label="Background">
+ <g
+ style="fill:none"
+ id="g4132">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4134"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path4136"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path4138"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path4140"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4142"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="fill:none;display:inline"
+ inkscape:label="SeaMonkey"
+ id="g4144" />
+ </g>
+ <g
+ id="g4146"
+ transform="translate(-141.73195,335.05514)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4148"
+ sodipodi:cx="-781.42859"
+ sodipodi:cy="260.93362"
+ sodipodi:rx="127.14286"
+ sodipodi:ry="151.42857"
+ d="M -661.87975,209.38359 A 127.14286,151.42857 0 1 1 -662.29628,208.03343"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(0.2477023,0,0,0.2079683,648.47198,64.124139)" />
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="451.29788"
+ y="93.797005"
+ id="text4150"><tspan
+ sodipodi:role="line"
+ id="tspan4152"
+ x="451.29788"
+ y="93.797005">XII</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="466.95267"
+ y="97.519417"
+ id="text4154"><tspan
+ sodipodi:role="line"
+ id="tspan4156"
+ x="466.95267"
+ y="97.519417">I</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="475.9686"
+ y="106.09559"
+ id="text4158"><tspan
+ sodipodi:role="line"
+ id="tspan4160"
+ x="475.9686"
+ y="106.09559">II</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="479.48703"
+ y="119.60712"
+ id="text4162"><tspan
+ sodipodi:role="line"
+ id="tspan4164"
+ x="479.48703"
+ y="119.60712">III</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="475.19901"
+ y="133.25336"
+ id="text4166"><tspan
+ sodipodi:role="line"
+ id="tspan4168"
+ x="475.19901"
+ y="133.25336">IV</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="466.40292"
+ y="142.88013"
+ id="text4170"><tspan
+ sodipodi:role="line"
+ id="tspan4172"
+ x="466.40292"
+ y="142.88013">V</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="451.66959"
+ y="146.44739"
+ id="text4174"><tspan
+ sodipodi:role="line"
+ id="tspan4176"
+ x="451.66959"
+ y="146.44739">VI</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="437.59592"
+ y="143.14886"
+ id="text4178"><tspan
+ sodipodi:role="line"
+ id="tspan4180"
+ x="437.59592"
+ y="143.14886">VII</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="428.68991"
+ y="133.09454"
+ id="text4182"><tspan
+ sodipodi:role="line"
+ id="tspan4184"
+ x="428.68991"
+ y="133.09454">VIII</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="426.38095"
+ y="119.50953"
+ id="text4186"><tspan
+ sodipodi:role="line"
+ id="tspan4188"
+ x="426.38095"
+ y="119.50953">IX</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="430.44913"
+ y="105.9856"
+ id="text4190"><tspan
+ sodipodi:role="line"
+ id="tspan4192"
+ x="430.44913"
+ y="105.9856">X</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="439.02524"
+ y="97.519417"
+ id="text4194"><tspan
+ sodipodi:role="line"
+ id="tspan4196"
+ x="439.02524"
+ y="97.519417">XI</tspan></text>
+ <g
+ id="g4198"
+ transform="matrix(0.5000001,0,0,0.5000001,64.050849,-145.21778)">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccc"
+ id="path4200"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path4202"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <g
+ id="g4204"
+ transform="matrix(-0.4291897,-0.256508,0.256508,-0.4291897,652.78758,549.26748)">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 779.71875,530.75 L 779.71875,533.625 L 779.71875,536.5 L 783.71875,536.5 L 783.71875,533.625 L 783.71875,530.75 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ id="path4206"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ id="path4208"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <path
+ sodipodi:type="arc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4210"
+ sodipodi:cx="775.26105"
+ sodipodi:cy="532.84601"
+ sodipodi:rx="3.6283669"
+ sodipodi:ry="3.6283669"
+ d="M 778.6727,531.61082 A 3.6283669,3.6283669 0 1 1 778.66081,531.57847"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(0.5000001,0,0,0.5000001,67.279699,-148.03415)" />
+ <path
+ transform="matrix(0.2477023,0,0,0.2079683,648.46968,64.107569)"
+ d="M -784.71875,115.40625 C -825.00423,116.48836 -860.55945,142.78523 -879.9375,176.53125 C -915.33331,236.08542 -909.41022,318.41384 -862.03125,370.0625 C -837.94741,396.46832 -800.80225,411.78686 -764.65625,405.125 C -723.73999,398.38021 -691.31594,366.90167 -675.15625,330.4375 C -648.58851,272.27873 -657.3233,197.98167 -701.96875,150.84375 C -723.18185,128.51376 -753.42796,114.21194 -784.78125,115.4375 L -784.71875,115.40625 z "
+ id="path4212"
+ style="fill:#ffffff;fill-opacity:0.23602486;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4288)"
+ inkscape:original="M -784.625 119.21875 C -823.30724 120.2578 -857.85589 145.69751 -876.65625 178.4375 C -911.16585 236.5006 -905.33598 317.22678 -859.21875 367.5 C -835.95661 393.00489 -800.00341 407.76292 -765.34375 401.375 C -725.99105 394.88795 -694.34644 364.38152 -678.625 328.90625 C -652.69932 272.153 -661.28225 199.33022 -704.71875 153.46875 C -725.24632 131.86038 -754.52802 118.0423 -784.625 119.21875 z "
+ inkscape:radius="3.7992489"
+ sodipodi:type="inkscape:offset" />
+ </g>
+ <g
+ id="g4214"
+ transform="matrix(4.13789,0,0,4.13789,199.04644,361.95221)">
+ <path
+ style="fill:#91380c;fill-opacity:1;fill-rule:nonzero;stroke:#682707;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 22.484347,32.246116 C 25.564479,34.76161 28.253532,38.258139 28.486698,40.050869 C 28.719862,41.843595 26.409279,41.257004 23.329144,38.741509 C 20.249014,36.226017 17.559961,32.729487 17.326794,30.936758 C 17.09504,29.154872 19.372194,29.719733 22.431363,32.202977"
+ id="path4216" />
+ <path
+ transform="matrix(1.0443307,0,0,1.0442899,31.33439,24.634086)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -10.649134,2.2285586 A 4.4278188,4.4278188 0 1 1 -10.66364,2.1890795"
+ sodipodi:ry="4.4278188"
+ sodipodi:rx="4.4278188"
+ sodipodi:cy="3.7358978"
+ sodipodi:cx="-14.812487"
+ id="path4218"
+ style="fill:#d1d3d5;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.71817738;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/history-window.svg b/comm/suite/branding/seamonkey/icons/template/svg/history-window.svg
new file mode 100644
index 0000000000..c5e12bae50
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/history-window.svg
@@ -0,0 +1,2294 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\history-window"
+ sodipodi:docname="seamonkey-history-windowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter4288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="8.1055212"
+ id="feGaussianBlur4290" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4612"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4614"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient3152"
+ inkscape:collect="always">
+ <stop
+ id="stop3154"
+ offset="0"
+ style="stop-color:#63ba0f;stop-opacity:1;" />
+ <stop
+ id="stop3156"
+ offset="1"
+ style="stop-color:#509e07;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3102"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3100"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3076"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3074"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3072"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3027"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3025"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient3199">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3201" />
+ <stop
+ id="stop3216"
+ offset="0.5"
+ style="stop-color:#ffffff;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.24705882;"
+ offset="0.8035714"
+ id="stop3218" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3203" />
+ </linearGradient>
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2791"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2793"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2795"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2797"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.6567"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient2799"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient2801"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.6268"
+ fx="834.16461"
+ cy="416.6268"
+ cx="834.16461"
+ id="radialGradient2803"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient2805"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.03826"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient2807"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient2809"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.8067"
+ fx="665.83667"
+ cy="613.8067"
+ cx="665.83667"
+ id="radialGradient2811"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient2813"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.7605"
+ fx="734.74274"
+ cy="565.7605"
+ cx="734.74274"
+ id="radialGradient2815"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient2817"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient2819"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient2821"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43961"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient2823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient2825"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient2827"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient2829"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient2831"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2833">
+ <stop
+ id="stop2835"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2837"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2839">
+ <stop
+ id="stop2841"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop2843" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop2845" />
+ <stop
+ id="stop2847"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2849">
+ <stop
+ id="stop2851"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop2853"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2855">
+ <stop
+ id="stop2857"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2859"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2861">
+ <stop
+ id="stop2863"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2865"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2867">
+ <stop
+ id="stop2869"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2871"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2873">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop2875" />
+ <stop
+ id="stop2877"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop2879" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2881">
+ <stop
+ id="stop2883"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop2885" />
+ <stop
+ id="stop2887"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2889">
+ <stop
+ id="stop2891"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop2893" />
+ <stop
+ id="stop2895"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3843"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3841"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3776"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3774"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2854">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2856" />
+ <stop
+ id="stop2858"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2860" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2846">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2848" />
+ <stop
+ id="stop2850"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2852" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2838">
+ <stop
+ id="stop2840"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2842" />
+ <stop
+ id="stop2844"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2832">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2834" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2836" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2826">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2828" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2830" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2820">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2822" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2824" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2814">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2816" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2818" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2804">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2806" />
+ <stop
+ id="stop2808"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2810"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2798">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2800" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2802" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2796"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2794"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2792"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2790"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2788"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43961"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2786"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2784"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2782"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2780"
+ cx="734.74274"
+ cy="565.7605"
+ fx="734.74274"
+ fy="565.7605"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2778"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2776"
+ cx="665.83667"
+ cy="613.8067"
+ fx="665.83667"
+ fy="613.8067"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2774"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2772"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.03826"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2770"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2768"
+ cx="834.16461"
+ cy="416.6268"
+ fx="834.16461"
+ fy="416.6268"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2766"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2764"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.6567"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2756"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3097"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3099"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient3160"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient4305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient4353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient4586"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient4588"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient4590"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ id="linearGradient3350">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop3352" />
+ <stop
+ id="stop3354"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop3356" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3342">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop3344" />
+ <stop
+ id="stop3346"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop3348" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3334">
+ <stop
+ id="stop3336"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop3338" />
+ <stop
+ id="stop3340"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3328">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop3330" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3332" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3322">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop3324" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3326" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3316">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop3318" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3320" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3310">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop3312" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop3314" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3300">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop3302" />
+ <stop
+ id="stop3304"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop3306"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop3308" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3294">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3296" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3298" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient3292"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient3290"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3288"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3286"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient3284"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43960"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3282"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3280"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3278"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3276"
+ cx="734.74274"
+ cy="565.76050"
+ fx="734.74274"
+ fy="565.76050"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3274"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3272"
+ cx="665.83667"
+ cy="613.80670"
+ fx="665.83667"
+ fy="613.80670"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3270"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3268"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.038260"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3266"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3264"
+ cx="834.16461"
+ cy="416.62680"
+ fx="834.16461"
+ fy="416.62680"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3262"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient3260"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.656700"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3152"
+ id="linearGradient3885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.4893755,0,0,2.4893755,754.50881,-27.065893)"
+ spreadMethod="pad"
+ x1="-136.54431"
+ y1="58.386353"
+ x2="-130.13719"
+ y2="78.847557" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3152"
+ id="linearGradient4319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.4893755,0,0,2.4893755,811.77658,331.14465)"
+ spreadMethod="pad"
+ x1="-136.54431"
+ y1="58.386353"
+ x2="-130.13719"
+ y2="78.847557" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3308"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.5312926"
+ inkscape:cx="219.81841"
+ inkscape:cy="408.40479"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3261"
+ transform="translate(-320.60782,524.86602)">
+ <g
+ transform="translate(17.118669,-319.35519)"
+ id="g3787">
+ <g
+ id="g3789"
+ inkscape:label="Shadow"
+ style="display:inline" />
+ <g
+ inkscape:label="Background"
+ id="g3791"
+ style="display:inline">
+ <g
+ id="g3793">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path3795"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path3797"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:url(#linearGradient3873);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path3799"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:url(#linearGradient3875);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path3801"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path3803"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g3805"
+ inkscape:label="SeaMonkey"
+ style="display:inline" />
+ </g>
+ <g
+ transform="translate(12.062002,-22.917805)"
+ id="g3887">
+ <path
+ transform="matrix(0.2477023,0,0,0.2079683,648.47198,64.124139)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -661.87975,209.38359 A 127.14286,151.42857 0 1 1 -662.29628,208.03343"
+ sodipodi:ry="151.42857"
+ sodipodi:rx="127.14286"
+ sodipodi:cy="260.93362"
+ sodipodi:cx="-781.42859"
+ id="path3807"
+ style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <text
+ id="text3809"
+ y="93.797005"
+ x="451.29788"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="93.797005"
+ x="451.29788"
+ id="tspan3811"
+ sodipodi:role="line">XII</tspan></text>
+ <text
+ id="text3813"
+ y="97.519417"
+ x="466.95267"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="466.95267"
+ id="tspan3815"
+ sodipodi:role="line">I</tspan></text>
+ <text
+ id="text3817"
+ y="106.09559"
+ x="475.9686"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="106.09559"
+ x="475.9686"
+ id="tspan3819"
+ sodipodi:role="line">II</tspan></text>
+ <text
+ id="text3821"
+ y="119.60712"
+ x="479.48703"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="119.60712"
+ x="479.48703"
+ id="tspan3823"
+ sodipodi:role="line">III</tspan></text>
+ <text
+ id="text3825"
+ y="133.25336"
+ x="475.19901"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="133.25336"
+ x="475.19901"
+ id="tspan3827"
+ sodipodi:role="line">IV</tspan></text>
+ <text
+ id="text3829"
+ y="142.88013"
+ x="466.40292"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="142.88013"
+ x="466.40292"
+ id="tspan3831"
+ sodipodi:role="line">V</tspan></text>
+ <text
+ id="text3833"
+ y="146.44739"
+ x="451.66959"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="146.44739"
+ x="451.66959"
+ id="tspan3835"
+ sodipodi:role="line">VI</tspan></text>
+ <text
+ id="text3837"
+ y="143.14886"
+ x="437.59592"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="143.14886"
+ x="437.59592"
+ id="tspan3839"
+ sodipodi:role="line">VII</tspan></text>
+ <text
+ id="text3841"
+ y="133.09454"
+ x="428.68991"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="133.09454"
+ x="428.68991"
+ id="tspan3843"
+ sodipodi:role="line">VIII</tspan></text>
+ <text
+ id="text3845"
+ y="119.50953"
+ x="426.38095"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="119.50953"
+ x="426.38095"
+ id="tspan3847"
+ sodipodi:role="line">IX</tspan></text>
+ <text
+ id="text3849"
+ y="105.9856"
+ x="430.44913"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="105.9856"
+ x="430.44913"
+ id="tspan3851"
+ sodipodi:role="line">X</tspan></text>
+ <text
+ id="text3853"
+ y="97.519417"
+ x="439.02524"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ xml:space="preserve"><tspan
+ y="97.519417"
+ x="439.02524"
+ id="tspan3855"
+ sodipodi:role="line">XI</tspan></text>
+ <g
+ transform="matrix(0.5000001,0,0,0.5000001,64.050849,-145.21778)"
+ id="g3857">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ id="path3859"
+ sodipodi:nodetypes="ccccccccccccccccccccc" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ id="path3861"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ transform="matrix(-0.4291897,-0.256508,0.256508,-0.4291897,652.78758,549.26748)"
+ id="g3863">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc"
+ id="path3865"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 779.71875,530.75 L 779.71875,533.625 L 779.71875,536.5 L 783.71875,536.5 L 783.71875,533.625 L 783.71875,530.75 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path3867"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <path
+ transform="matrix(0.5000001,0,0,0.5000001,67.279699,-148.03415)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M 778.6727,531.61082 A 3.6283669,3.6283669 0 1 1 778.66081,531.57847"
+ sodipodi:ry="3.6283669"
+ sodipodi:rx="3.6283669"
+ sodipodi:cy="532.84601"
+ sodipodi:cx="775.26105"
+ id="path3869"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="3.7992489"
+ inkscape:original="M -784.625 119.21875 C -823.30724 120.2578 -857.85589 145.69751 -876.65625 178.4375 C -911.16585 236.5006 -905.33598 317.22678 -859.21875 367.5 C -835.95661 393.00489 -800.00341 407.76292 -765.34375 401.375 C -725.99105 394.88795 -694.34644 364.38152 -678.625 328.90625 C -652.69932 272.153 -661.28225 199.33022 -704.71875 153.46875 C -725.24632 131.86038 -754.52802 118.0423 -784.625 119.21875 z "
+ style="fill:#ffffff;fill-opacity:0.23602486;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4288)"
+ id="path3871"
+ d="M -784.71875,115.40625 C -825.00423,116.48836 -860.55945,142.78523 -879.9375,176.53125 C -915.33331,236.08542 -909.41022,318.41384 -862.03125,370.0625 C -837.94741,396.46832 -800.80225,411.78686 -764.65625,405.125 C -723.73999,398.38021 -691.31594,366.90167 -675.15625,330.4375 C -648.58851,272.27873 -657.3233,197.98167 -701.96875,150.84375 C -723.18185,128.51376 -753.42796,114.21194 -784.78125,115.4375 L -784.71875,115.40625 z "
+ transform="matrix(0.2477023,0,0,0.2079683,648.46968,64.107569)" />
+ </g>
+ <path
+ sodipodi:nodetypes="ccccccccccccsc"
+ id="path3883"
+ d="M 404.19444,135.98197 L 428.98676,111.65995 C 432.11782,109.1031 440.81914,119.41326 437.36714,123.05104 L 430.26372,130.35264 C 429.07957,131.5762 430.05451,132.94001 432.77425,132.85815 L 459.7443,133.10336 C 464.74878,133.39037 464.13298,148.21352 459.84837,148.12035 L 432.78764,147.98617 C 429.83457,147.88788 429.4046,149.49421 430.25502,150.28057 L 437.26307,157.97137 C 440.35513,160.59536 431.43533,170.37964 429.4535,168.53096 L 404.47801,143.9352 C 402.84703,142.55034 402.67841,141.15465 402.64944,140.13662 C 402.62263,139.19381 402.73119,137.52868 404.19444,135.98197 z "
+ style="fill:url(#linearGradient3885);fill-opacity:1;fill-rule:evenodd;stroke:#3f7b07;stroke-width:2.48937559px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+ <g
+ id="g3165"
+ transform="translate(-206.84375,168.06055)">
+ <g
+ style="fill:none"
+ id="g4224"
+ transform="translate(74.386447,38.855354)">
+ <g
+ style="fill:none;display:inline"
+ inkscape:label="Shadow"
+ id="g4226" />
+ <g
+ style="fill:none;display:inline"
+ id="g4228"
+ inkscape:label="Background">
+ <g
+ style="fill:none"
+ id="g4230">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4232"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path4235"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path4237"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path4239"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4241"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="fill:none;display:inline"
+ inkscape:label="SeaMonkey"
+ id="g4243" />
+ </g>
+ <g
+ id="g4245"
+ transform="translate(69.32978,335.29274)">
+ <path
+ sodipodi:type="arc"
+ style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4247"
+ sodipodi:cx="-781.42859"
+ sodipodi:cy="260.93362"
+ sodipodi:rx="127.14286"
+ sodipodi:ry="151.42857"
+ d="M -661.87975,209.38359 A 127.14286,151.42857 0 1 1 -662.29628,208.03343"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(0.2477023,0,0,0.2079683,648.47198,64.124139)" />
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="451.29788"
+ y="93.797005"
+ id="text4249"><tspan
+ sodipodi:role="line"
+ id="tspan4251"
+ x="451.29788"
+ y="93.797005">XII</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="466.95267"
+ y="97.519417"
+ id="text4253"><tspan
+ sodipodi:role="line"
+ id="tspan4255"
+ x="466.95267"
+ y="97.519417">I</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="475.9686"
+ y="106.09559"
+ id="text4257"><tspan
+ sodipodi:role="line"
+ id="tspan4259"
+ x="475.9686"
+ y="106.09559">II</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="479.48703"
+ y="119.60712"
+ id="text4261"><tspan
+ sodipodi:role="line"
+ id="tspan4263"
+ x="479.48703"
+ y="119.60712">III</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="475.19901"
+ y="133.25336"
+ id="text4265"><tspan
+ sodipodi:role="line"
+ id="tspan4267"
+ x="475.19901"
+ y="133.25336">IV</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="466.40292"
+ y="142.88013"
+ id="text4269"><tspan
+ sodipodi:role="line"
+ id="tspan4271"
+ x="466.40292"
+ y="142.88013">V</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="451.66959"
+ y="146.44739"
+ id="text4273"><tspan
+ sodipodi:role="line"
+ id="tspan4275"
+ x="451.66959"
+ y="146.44739">VI</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="437.59592"
+ y="143.14886"
+ id="text4277"><tspan
+ sodipodi:role="line"
+ id="tspan4279"
+ x="437.59592"
+ y="143.14886">VII</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="428.68991"
+ y="133.09454"
+ id="text4281"><tspan
+ sodipodi:role="line"
+ id="tspan4283"
+ x="428.68991"
+ y="133.09454">VIII</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="426.38095"
+ y="119.50953"
+ id="text4285"><tspan
+ sodipodi:role="line"
+ id="tspan4287"
+ x="426.38095"
+ y="119.50953">IX</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="430.44913"
+ y="105.9856"
+ id="text4289"><tspan
+ sodipodi:role="line"
+ id="tspan4291"
+ x="430.44913"
+ y="105.9856">X</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:5.00000095px;font-style:normal;font-weight:bold;writing-mode:lr-tb;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Trebuchet MS"
+ x="439.02524"
+ y="97.519417"
+ id="text4293"><tspan
+ sodipodi:role="line"
+ id="tspan4295"
+ x="439.02524"
+ y="97.519417">XI</tspan></text>
+ <g
+ id="g4297"
+ transform="matrix(0.5000001,0,0,0.5000001,64.050849,-145.21778)">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccc"
+ id="path4299"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path4301"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ </g>
+ <g
+ id="g4303"
+ transform="matrix(-0.4291897,-0.256508,0.256508,-0.4291897,652.78758,549.26748)">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 779.71875,502 L 779.71875,504.875 L 779.71875,507.75 L 779.71875,510.625 L 779.71875,513.5 L 779.71875,516.375 L 779.71875,519.25 L 779.71875,522.125 L 779.71875,525 L 779.71875,527.875 L 779.71875,530.75 L 779.71875,533.625 L 779.71875,536.5 L 783.71875,536.5 L 783.71875,533.625 L 783.71875,530.75 L 783.71875,527.875 L 783.71875,525 L 783.71875,522.125 L 783.71875,519.25 L 783.71875,516.375 L 783.71875,513.5 L 783.71875,510.625 L 783.71875,507.75 L 783.71875,504.875 L 783.71875,502 L 779.71875,502 z "
+ id="path4305"
+ sodipodi:nodetypes="ccccccccccccccccccccccccccc" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.76657629;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1"
+ d="M 781.71875,496.68239 L 784.33546,501.91582 L 779.10204,501.91582 L 781.71875,496.68239 z "
+ id="path4307"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <path
+ sodipodi:type="arc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4309"
+ sodipodi:cx="775.26105"
+ sodipodi:cy="532.84601"
+ sodipodi:rx="3.6283669"
+ sodipodi:ry="3.6283669"
+ d="M 778.6727,531.61082 A 3.6283669,3.6283669 0 1 1 778.66081,531.57847"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(0.5000001,0,0,0.5000001,67.279699,-148.03415)" />
+ <path
+ transform="matrix(0.2477023,0,0,0.2079683,648.46968,64.107569)"
+ d="M -784.71875,115.40625 C -825.00423,116.48836 -860.55945,142.78523 -879.9375,176.53125 C -915.33331,236.08542 -909.41022,318.41384 -862.03125,370.0625 C -837.94741,396.46832 -800.80225,411.78686 -764.65625,405.125 C -723.73999,398.38021 -691.31594,366.90167 -675.15625,330.4375 C -648.58851,272.27873 -657.3233,197.98167 -701.96875,150.84375 C -723.18185,128.51376 -753.42796,114.21194 -784.78125,115.4375 L -784.71875,115.40625 z "
+ id="path4311"
+ style="fill:#ffffff;fill-opacity:0.23602486;fill-rule:nonzero;stroke:#000000;stroke-width:4.40591908;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4288)"
+ inkscape:original="M -784.625 119.21875 C -823.30724 120.2578 -857.85589 145.69751 -876.65625 178.4375 C -911.16585 236.5006 -905.33598 317.22678 -859.21875 367.5 C -835.95661 393.00489 -800.00341 407.76292 -765.34375 401.375 C -725.99105 394.88795 -694.34644 364.38152 -678.625 328.90625 C -652.69932 272.153 -661.28225 199.33022 -704.71875 153.46875 C -725.24632 131.86038 -754.52802 118.0423 -784.625 119.21875 z "
+ inkscape:radius="3.7992489"
+ sodipodi:type="inkscape:offset" />
+ </g>
+ <path
+ style="fill:url(#linearGradient4319);fill-opacity:1;fill-rule:evenodd;stroke:#3f7b07;stroke-width:2.48937559px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
+ d="M 461.46221,494.19251 L 486.25453,469.87049 C 489.38559,467.31364 498.08691,477.6238 494.63491,481.26158 L 487.53149,488.56318 C 486.34734,489.78674 487.32228,491.15055 490.04202,491.06869 L 517.01207,491.3139 C 522.01655,491.60091 521.40075,506.42406 517.11614,506.33089 L 490.05541,506.19671 C 487.10234,506.09842 486.67237,507.70475 487.52279,508.49111 L 494.53084,516.18191 C 497.6229,518.8059 488.7031,528.59018 486.72127,526.7415 L 461.74578,502.14574 C 460.1148,500.76088 459.94618,499.36519 459.91721,498.34716 C 459.8904,497.40435 459.99896,495.73922 461.46221,494.19251 z "
+ id="path4313"
+ sodipodi:nodetypes="ccccccccccccsc" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/messengerWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/messengerWindow.svg
new file mode 100644
index 0000000000..3427184c28
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/messengerWindow.svg
@@ -0,0 +1,1116 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2226"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ width="500"
+ height="500"
+ version="1.0"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\messengerWindowversion2"
+ sodipodi:docname="seamonkeymessengerWindowSVGversion2.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <metadata
+ id="metadata2231">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs2229">
+ <linearGradient
+ id="linearGradient3266"
+ inkscape:collect="always">
+ <stop
+ id="stop3268"
+ offset="0"
+ style="stop-color:#7aa1ca;stop-opacity:1;" />
+ <stop
+ id="stop3270"
+ offset="1"
+ style="stop-color:#3c75b2;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3258">
+ <stop
+ style="stop-color:#9096b1;stop-opacity:1;"
+ offset="0"
+ id="stop3260" />
+ <stop
+ style="stop-color:#5e678d;stop-opacity:1"
+ offset="1"
+ id="stop3262" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3278">
+ <stop
+ style="stop-color:#7aa1ca;stop-opacity:1;"
+ offset="0"
+ id="stop3280" />
+ <stop
+ style="stop-color:#3c75b2;stop-opacity:1"
+ offset="1"
+ id="stop3282" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient3284"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2199"
+ gradientUnits="userSpaceOnUse"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2210"
+ gradientUnits="userSpaceOnUse"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient3219"
+ gradientUnits="userSpaceOnUse"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2219"
+ gradientUnits="userSpaceOnUse"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2262"
+ gradientUnits="userSpaceOnUse"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2443"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <filter
+ inkscape:collect="always"
+ id="filter3495">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.6546695"
+ id="feGaussianBlur3497" />
+ </filter>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3266"
+ id="linearGradient2361"
+ gradientUnits="userSpaceOnUse"
+ x1="67.794327"
+ y1="15.536267"
+ x2="73.216293"
+ y2="18.695934" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3258"
+ id="linearGradient2363"
+ gradientUnits="userSpaceOnUse"
+ x1="231.71468"
+ y1="145.44635"
+ x2="222.35701"
+ y2="144.91158" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3258"
+ id="linearGradient2324"
+ gradientUnits="userSpaceOnUse"
+ x1="231.71468"
+ y1="145.44635"
+ x2="222.35701"
+ y2="144.91158"
+ gradientTransform="matrix(3.3771096,0,0,3.3771096,-223.77696,-215.3576)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3266"
+ id="linearGradient2329"
+ gradientUnits="userSpaceOnUse"
+ x1="67.794327"
+ y1="15.536267"
+ x2="73.216293"
+ y2="18.695934" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2904"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2902"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2900"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2898"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient2896"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient2894"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient2892"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient2890"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient2888"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient2886"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient2884"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient2882"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient2880"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient2878"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient2876"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient2874"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient2872"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient2870"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient2868"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient2866"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient2864"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2858">
+ <stop
+ id="stop2860"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2862"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2848">
+ <stop
+ id="stop2850"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop2852" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop2854" />
+ <stop
+ id="stop2856"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2842">
+ <stop
+ id="stop2844"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop2846"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2836">
+ <stop
+ id="stop2838"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2840"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2830">
+ <stop
+ id="stop2832"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2834"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2824">
+ <stop
+ id="stop2826"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2828"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2816">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop2818" />
+ <stop
+ id="stop2820"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop2822" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2808">
+ <stop
+ id="stop2810"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop2812" />
+ <stop
+ id="stop2814"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2800">
+ <stop
+ id="stop2802"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop2804" />
+ <stop
+ id="stop2806"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3258"
+ id="linearGradient2350"
+ gradientUnits="userSpaceOnUse"
+ x1="231.71468"
+ y1="145.44635"
+ x2="222.35701"
+ y2="144.91158"
+ gradientTransform="matrix(3.3771096,0,0,3.3771096,-226.52402,-199.72976)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3266"
+ id="linearGradient2355"
+ gradientUnits="userSpaceOnUse"
+ x1="67.794327"
+ y1="15.536267"
+ x2="73.216293"
+ y2="18.695934" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3258"
+ id="linearGradient2386"
+ gradientUnits="userSpaceOnUse"
+ x1="231.71468"
+ y1="145.44635"
+ x2="222.35701"
+ y2="144.91158"
+ gradientTransform="matrix(3.3771096,0,0,3.3771096,-225.9505,-202.08073)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,152.57359,197.13637)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2406"
+ gradientUnits="userSpaceOnUse"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375"
+ gradientTransform="translate(-220.53892,-160.04912)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3266"
+ id="linearGradient2409"
+ gradientUnits="userSpaceOnUse"
+ x1="67.794327"
+ y1="15.536267"
+ x2="73.216293"
+ y2="18.695934" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,152.57359,197.13637)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3278"
+ id="linearGradient2346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-220.53892,-160.04912)"
+ x1="63.666668"
+ y1="18.9375"
+ x2="77.5"
+ y2="18.9375" />
+ </defs>
+ <sodipodi:namedview
+ inkscape:window-height="844"
+ inkscape:window-width="1440"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#ffffff"
+ id="base"
+ inkscape:zoom="1.18087"
+ inkscape:cx="217.25271"
+ inkscape:cy="222.30509"
+ inkscape:window-x="-4"
+ inkscape:window-y="-4"
+ inkscape:current-layer="svg2226"
+ width="500px"
+ height="500px" />
+ <g
+ id="g2381">
+ <path
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path6573"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,158.95416,208.5661)" />
+ <path
+ style="fill:url(#linearGradient2346);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 163.31785,310.28568 C 156.99312,297.93615 155.26398,289.78921 153.78997,278.0015 C 157.60254,277.89211 158.69005,275.75865 185.24586,281.47373 C 192.41359,285.12711 204.73402,287.23595 207.219,287.14992 C 208.57659,287.22902 220.27893,286.47204 222.26345,285.17331 C 224.25084,284.29116 238.26208,282.06864 243.38027,281.51256 C 246.56216,281.04255 247.88914,280.38521 251.1181,280.35319 C 262.47422,275.00251 275.4832,276.83179 280.64262,278.00761 C 280.53492,282.35643 279.99933,288.46039 279.19727,291.26514 C 278.36241,295.21639 277.94222,297.30993 275.98963,300.85874 C 275.12659,304.93687 273.19256,307.57576 270.15968,312.4853 C 266.99331,317.44222 261.30255,322.61179 254.80116,328.0367 C 250.44657,333.9265 235.08085,337.06445 231.85341,338.92385 C 223.29449,341.23441 214.71091,342.09177 199.68927,338.05698 C 183.04423,334.4178 170.06315,319.44113 163.31785,310.28568 z "
+ id="path2064"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:url(#linearGradient2343);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 155.25682,263.1413 C 155.92222,260.47967 157.51591,255.01061 159.15306,252.82774 C 161.8893,249.1794 166.14357,240.61098 169.23742,236.32604 C 173.75758,230.06567 187.49079,221.72369 193.53159,218.90758 C 202.85329,214.56196 225.98732,212.54623 238.91124,218.4492 C 244.45182,220.97985 255.1766,226.52279 259.53837,231.05466 C 263.60339,235.27824 270.05847,242.78958 273.28977,249.38988 C 275.03883,252.96255 280.45341,267.16258 279.70709,267.26672 C 274.99374,267.92439 270.13833,269.6492 265.26811,271.16295 C 261.84045,272.22833 255.9497,275.05919 255.18375,275.05919 C 251.74589,275.05919 239.69305,274.22037 236.16095,274.37162 C 232.63884,274.52244 227.29548,274.46748 225.61822,274.82999 C 220.47051,275.94255 212.15276,274.72778 207.283,271.85052 C 198.03824,273.77292 194.50932,275.19807 180.00937,270.47539 C 167.78698,266.49451 156.96064,269.41914 155.48601,269.78781 C 153.53702,270.27506 154.90622,264.54369 155.25682,263.1413 z "
+ id="path2056"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 154.11364,269.98938 C 153.64978,270.29693 153.81512,276.65093 154.18718,279.76295 C 154.88489,281.10301 153.43045,282.9135 154.24835,284.18964 C 155.31064,285.45539 172.21227,288.36412 185.24586,281.47373 C 192.41359,285.12711 204.73402,287.23595 207.219,287.14992 C 208.57659,287.22902 238.26208,282.06864 243.38027,281.51256 C 246.56216,281.04255 249.72266,280.38521 252.95162,280.35319 C 255.14015,280.27389 264.54656,282.36802 267.06017,283.39128 C 275.03832,286.63908 278.00228,285.3131 279.49565,285.79753 C 279.84635,284.64579 280.4577,282.73063 280.11403,280.49319 C 279.96675,277.79793 280.9217,271.64062 279.88586,269.91805 C 279.02283,269.1832 280.11597,267.57093 279.11643,267.34661 C 275.75972,267.91441 269.85997,269.64993 264.97462,271.25597 C 260.67484,272.68128 258.82514,274.11556 255.94989,274.21053 C 253.24593,275.04075 246.33133,274.61855 243.21692,274.54934 C 236.59844,273.09629 229.66925,269.66365 222.4803,268.44205 C 218.58407,268.1437 210.48933,269.99217 207.2619,271.85156 C 202.14084,269.34913 190.62273,267.83349 186.37301,267.46575 C 170.87594,266.1172 158.6641,268.79052 154.11364,269.98938 z "
+ id="path2058"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2052"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,158.95416,208.5661)" />
+ <g
+ transform="translate(3.3772247,7.0388147)"
+ id="g2373">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ d="M 211.32356,128.37745 C 212.65711,127.69113 228.56397,129.73064 229.17515,131.45531 C 230.04721,132.59073 230.38132,142.4027 229.58553,143.15119 C 228.58342,144.18267 207.45526,142.15155 206.39898,140.27852 C 205.61846,139.15149 209.05674,129.96768 211.32356,128.37745 z "
+ id="path2458"
+ sodipodi:nodetypes="ccccc"
+ transform="matrix(3.3771096,0,0,3.3771096,-522.87836,-188.0747)" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2460"
+ d="M 214.85909,134.27001 C 216.19264,133.58369 218.31092,133.14833 218.92211,134.873 C 222.15119,135.0656 230.38132,142.4027 229.58553,143.15119 C 228.58342,144.18267 207.45526,142.15155 206.39898,140.27852 C 205.61846,139.15149 211.29591,134.32817 214.85909,134.27001 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ transform="matrix(3.3771096,0,0,3.3771096,-522.87836,-188.0747)" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2466"
+ d="M 211.32356,128.37745 C 212.65711,127.69113 228.56397,129.73064 229.17515,131.45531 C 228.75085,133.76924 225.43157,135.68518 223.22157,136.31582 C 220.09813,136.75805 215.23343,135.78759 213.94145,134.62166 C 212.68952,133.73034 209.99955,130.55694 211.32356,128.37745 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ transform="matrix(3.3771096,0,0,3.3771096,-522.87836,-188.0747)" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2231"
+ d="M 190.78446,245.47002 C 195.28801,243.15224 249.00722,250.03989 251.07124,255.86429 C 254.01628,259.69873 255.14461,292.83483 252.45714,295.36256 C 249.0729,298.84598 177.72079,291.98666 174.15362,285.66124 C 171.51772,281.85513 183.12916,250.8404 190.78446,245.47002 z "
+ style="fill:#eee5dd;fill-opacity:1;fill-rule:evenodd;stroke:#94806f;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ style="fill:#f3ebe4;fill-opacity:1;fill-rule:evenodd;stroke:#937f6d;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.95686275"
+ d="M 202.72434,265.36984 C 207.22788,263.05206 214.38154,261.5818 216.4456,267.4062 C 227.35056,268.05663 255.14461,292.83483 252.45714,295.36256 C 249.0729,298.84598 177.72079,291.98666 174.15362,285.66124 C 171.51772,281.85513 190.69109,265.56625 202.72434,265.36984 z "
+ id="path2233"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#f7f0e9;fill-opacity:1;fill-rule:evenodd;stroke:#9e8b7a;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 190.78446,245.47002 C 195.28801,243.15224 249.00722,250.03989 251.07124,255.86429 C 249.63833,263.67868 238.42876,270.14902 230.96535,272.27876 C 220.41715,273.77222 203.98852,270.49487 199.62536,266.5574 C 195.39746,263.54732 186.31314,252.8304 190.78446,245.47002 z "
+ id="path2235"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ </g>
+ <g
+ id="g2395"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\tests\32pxmessengernoarrow.png"
+ inkscape:export-xdpi="90.000084"
+ inkscape:export-ydpi="90.000084">
+ <g
+ style="fill:none"
+ id="g2925"
+ transform="translate(-60.88344,-161.0596)">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2927"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path2929"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path2931"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path2933"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path2935"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ <g
+ transform="translate(3.3772247,7.0388147)"
+ id="g2326">
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2939"
+ d="M 211.32356,128.37745 C 212.65711,127.69113 228.56397,129.73064 229.17515,131.45531 C 230.04721,132.59073 230.38132,142.4027 229.58553,143.15119 C 228.58342,144.18267 207.45526,142.15155 206.39898,140.27852 C 205.61846,139.15149 209.05674,129.96768 211.32356,128.37745 z "
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ transform="matrix(3.3771096,0,0,3.3771096,-363.22288,-189.08518)" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ d="M 214.85909,134.27001 C 216.19264,133.58369 218.31092,133.14833 218.92211,134.873 C 222.15119,135.0656 230.38132,142.4027 229.58553,143.15119 C 228.58342,144.18267 207.45526,142.15155 206.39898,140.27852 C 205.61846,139.15149 211.29591,134.32817 214.85909,134.27001 z "
+ id="path2941"
+ sodipodi:nodetypes="ccccc"
+ transform="matrix(3.3771096,0,0,3.3771096,-363.22288,-189.08518)" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.54999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3495)"
+ d="M 211.32356,128.37745 C 212.65711,127.69113 228.56397,129.73064 229.17515,131.45531 C 228.75085,133.76924 225.43157,135.68518 223.22157,136.31582 C 220.09813,136.75805 215.23343,135.78759 213.94145,134.62166 C 212.68952,133.73034 209.99955,130.55694 211.32356,128.37745 z "
+ id="path2947"
+ sodipodi:nodetypes="ccccc"
+ transform="matrix(3.3771096,0,0,3.3771096,-363.22288,-189.08518)" />
+ <path
+ style="fill:#eee5dd;fill-opacity:1;fill-rule:evenodd;stroke:#9e8b7a;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 350.43994,244.45954 C 354.94349,242.14176 408.6627,249.02941 410.72672,254.85381 C 413.67176,258.68825 414.80009,291.82435 412.11262,294.35208 C 408.72838,297.8355 337.37627,290.97618 333.8091,284.65076 C 331.1732,280.84465 342.78464,249.82992 350.43994,244.45954 z "
+ id="path2951"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2953"
+ d="M 362.37982,264.35936 C 366.88336,262.04158 374.03702,260.57132 376.10108,266.39572 C 387.00604,267.04615 414.80009,291.82435 412.11262,294.35208 C 408.72838,297.8355 337.37627,290.97618 333.8091,284.65076 C 331.1732,280.84465 350.34657,264.55577 362.37982,264.35936 z "
+ style="fill:#f3ebe4;fill-opacity:1;fill-rule:evenodd;stroke:#978472;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ id="path2959"
+ d="M 350.43994,244.45954 C 354.94349,242.14176 408.6627,249.02941 410.72672,254.85381 C 409.29381,262.6682 398.08424,269.13854 390.62083,271.26828 C 380.07263,272.76174 363.644,269.48439 359.28084,265.54692 C 355.05294,262.53684 345.96862,251.81992 350.43994,244.45954 z "
+ style="fill:#f7f0e9;fill-opacity:1;fill-rule:evenodd;stroke:#94806f;stroke-width:5.23451948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ <image
+ y="101.4155"
+ x="-39.096359"
+ id="image2484"
+ height="126"
+ width="188"
+ sodipodi:absref="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\seamonkeydefault\preview.png"
+ xlink:href="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\seamonkey icons\seamonkeydefault\preview.png" />
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/msgcomposeWindow.svg b/comm/suite/branding/seamonkey/icons/template/svg/msgcomposeWindow.svg
new file mode 100644
index 0000000000..ccf48d6b82
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/msgcomposeWindow.svg
@@ -0,0 +1,676 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="500"
+ height="500"
+ id="svg2160"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\msgcomposeWindow"
+ sodipodi:docname="seamonkey-msgcomposeWindowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2162">
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3457"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,-480.95396,201.6476)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3460"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,-480.95396,201.6476)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3507"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3509"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3564"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3566"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3600"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3602"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3681"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3683"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3807"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3809"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4491943"
+ inkscape:cx="176.12983"
+ inkscape:cy="74.102991"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="false"
+ inkscape:document-units="px"
+ inkscape:object-paths="false"
+ inkscape:guide-bbox="false"
+ width="500px"
+ height="500px"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="0"
+ inkscape:window-y="22" />
+ <metadata
+ id="metadata2165">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g3689"
+ transform="translate(-710.70481,-18.44722)" />
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g3705"
+ transform="translate(-710.70481,-18.44722)" />
+ <g
+ id="g3828">
+ <g
+ id="g3773"
+ transform="translate(-349.93665,-12.96442)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g3775" />
+ <g
+ style="display:inline"
+ id="g3777"
+ inkscape:label="Background">
+ <g
+ id="g3779">
+ <path
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3781"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:url(#linearGradient3807);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path3783"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:url(#linearGradient3809);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path3785"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path3787"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path3789"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g3791" />
+ </g>
+ <g
+ transform="translate(360.48635,6.9897052)"
+ id="g3766">
+ <path
+ style="fill:#a2c2de;fill-opacity:1;fill-rule:nonzero;stroke:#2271bc;stroke-width:4.58545876;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -271.625,356.1875 C -274.53082,366.0131 -276.68138,376.11022 -278.3125,386.21875 C -280.1006,399.15868 -281.63819,412.58751 -278.875,425.375 C -278.08622,429.4343 -275.05164,434.96654 -270.53125,432.6875 C -264.89694,429.505 -261.70389,423.03284 -258.6875,417.21875 C -250.81145,400.41286 -245.99977,382.10381 -243.125,363.625 C -251.64503,359.05508 -261.33524,356.39325 -271.625,356.1875 z "
+ id="path3715" />
+ <path
+ style="fill:#000000;fill-opacity:0.99215686;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ d="M -261.76711,429.68955 C -264.67446,435.63583 -268.47198,439.96515 -271.44762,442.6107 C -272.65579,443.68484 -273.35576,445.91486 -276.09496,445.62061 C -278.46512,445.32712 -280.72467,444.38016 -281.48545,443.07353 C -283.39587,439.79237 -284.64318,431.74649 -284.30153,425.88568 C -284.04476,421.89271 -283.67012,415.90395 -282.56628,411.3964 C -276.46564,408.80532 -270.44462,408.83261 -268.50576,409.58869 C -263.46848,411.55299 -259.38592,413.92179 -256.7493,418.38698 C -259.178,424.27773 -260.29766,426.58389 -261.76711,429.68955 z "
+ id="path3717"
+ sodipodi:nodetypes="csssccscc" />
+ <path
+ style="fill:#c5c5c5;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.29272938;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -262.17624,440.4275 C -261.44085,445.88114 -261.15659,452.27573 -263.41398,460.3349 C -264.02391,462.04465 -273.48264,459.99769 -278.92722,476.2825 C -280.31763,480.44116 -274.96335,442.67395 -276.39193,442.33954 C -277.84675,441.92085 -279.77057,477.66416 -279.87354,476.45321 C -280.96189,463.6559 -290.42424,461.22985 -290.65282,459.13745 C -291.34053,450.92358 -290.07602,442.94735 -286.89644,435.50029 C -285.20402,431.80258 -283.18614,428.11704 -279.38486,425.18144 C -277.84584,424.11827 -275.93148,422.91751 -272.94935,423.13457 C -270.19161,423.45573 -268.34121,425.13244 -267.18832,426.51203 C -264.38031,430.06711 -262.91195,434.45514 -262.17624,440.4275 z "
+ id="path3719"
+ sodipodi:nodetypes="cccssccsscc" />
+ </g>
+ </g>
+ <g
+ id="g2384">
+ <g
+ style="fill:none"
+ transform="translate(-173.38695,-10.729614)"
+ id="g2372">
+ <g
+ id="g2374"
+ inkscape:label="Shadow"
+ style="display:inline;fill:none" />
+ <g
+ inkscape:label="Background"
+ id="g2376"
+ style="display:inline;fill:none">
+ <g
+ style="fill:none"
+ id="g2378">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2380"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2382"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2384"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2386"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2388"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g2390"
+ inkscape:label="SeaMonkey"
+ style="display:inline;fill:none" />
+ </g>
+ <g
+ id="g2392"
+ transform="translate(537.03605,9.2245115)">
+ <path
+ id="path2394"
+ d="M -271.625,356.1875 C -274.53082,366.0131 -276.68138,376.11022 -278.3125,386.21875 C -280.1006,399.15868 -281.63819,412.58751 -278.875,425.375 C -278.08622,429.4343 -275.05164,434.96654 -270.53125,432.6875 C -264.89694,429.505 -261.70389,423.03284 -258.6875,417.21875 C -250.81145,400.41286 -245.99977,382.10381 -243.125,363.625 C -251.64503,359.05508 -261.33524,356.39325 -271.625,356.1875 z "
+ style="fill:#a2c2de;fill-opacity:1;fill-rule:nonzero;stroke:#2271bc;stroke-width:4.58545876;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="csssccscc"
+ id="path2396"
+ d="M -261.76711,429.68955 C -264.67446,435.63583 -268.47198,439.96515 -271.44762,442.6107 C -272.65579,443.68484 -273.35576,445.91486 -276.09496,445.62061 C -278.46512,445.32712 -280.72467,444.38016 -281.48545,443.07353 C -283.39587,439.79237 -284.64318,431.74649 -284.30153,425.88568 C -284.04476,421.89271 -283.67012,415.90395 -282.56628,411.3964 C -276.46564,408.80532 -270.44462,408.83261 -268.50576,409.58869 C -263.46848,411.55299 -259.38592,413.92179 -256.7493,418.38698 C -259.178,424.27773 -260.29766,426.58389 -261.76711,429.68955 z "
+ style="fill:#000000;fill-opacity:0.99215686;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccssccsscc"
+ id="path2398"
+ d="M -262.17624,440.4275 C -261.44085,445.88114 -261.15659,452.27573 -263.41398,460.3349 C -264.02391,462.04465 -273.48264,459.99769 -278.92722,476.2825 C -280.31763,480.44116 -274.96335,442.67395 -276.39193,442.33954 C -277.84675,441.92085 -279.77057,477.66416 -279.87354,476.45321 C -280.96189,463.6559 -290.42424,461.22985 -290.65282,459.13745 C -291.34053,450.92358 -290.07602,442.94735 -286.89644,435.50029 C -285.20402,431.80258 -283.18614,428.11704 -279.38486,425.18144 C -277.84584,424.11827 -275.93148,422.91751 -272.94935,423.13457 C -270.19161,423.45573 -268.34121,425.13244 -267.18832,426.51203 C -264.38031,430.06711 -262.91195,434.45514 -262.17624,440.4275 z "
+ style="fill:#c5c5c5;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.29272938;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/template/svg/places.svg b/comm/suite/branding/seamonkey/icons/template/svg/places.svg
new file mode 100644
index 0000000000..e90853da5c
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/template/svg/places.svg
@@ -0,0 +1,1562 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="500"
+ height="500"
+ id="svg2160"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="C:\Documents and Settings\Owner.SPINELLOZONE\My Documents\seamonkey-icons-complete\bookmark-window"
+ sodipodi:docname="seamonkey-bookmark-windowSVG.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs2162">
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2234"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2232"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.656700"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient11020"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient11018"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.62680"
+ fx="834.16461"
+ cy="416.62680"
+ cx="834.16461"
+ id="radialGradient11016"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient11014"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.038260"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient11012"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient11010"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.80670"
+ fx="665.83667"
+ cy="613.80670"
+ cx="665.83667"
+ id="radialGradient11008"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient11006"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.76050"
+ fx="734.74274"
+ cy="565.76050"
+ cx="734.74274"
+ id="radialGradient11004"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient11002"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient11000"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient10262"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43960"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient5823"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient5815"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient5080"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient5072"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient5002"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient4996">
+ <stop
+ id="stop4998"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop5000"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5006">
+ <stop
+ id="stop5008"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop10260" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop5016" />
+ <stop
+ id="stop5010"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5066">
+ <stop
+ id="stop5068"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop5070"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5074">
+ <stop
+ id="stop5076"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5078"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5809">
+ <stop
+ id="stop5811"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5813"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5817">
+ <stop
+ id="stop5819"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop5821"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10990">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop10992" />
+ <stop
+ id="stop10996"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop10998" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4697">
+ <stop
+ id="stop4699"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop4707" />
+ <stop
+ id="stop4701"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5598">
+ <stop
+ id="stop5600"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop5606" />
+ <stop
+ id="stop5602"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient4590"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4588"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4586"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient4353"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4351"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4349"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient4305"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4303"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4301"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ r="17.2868"
+ fy="45.411678"
+ fx="45.457588"
+ cy="45.411678"
+ cx="45.457588"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3160"
+ xlink:href="#linearGradient3199"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3099"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3097"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3056"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3054"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2967"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2965"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2756"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2758"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2760"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2762"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ r="37.6567"
+ fy="334.10477"
+ fx="822.35791"
+ cy="334.10477"
+ cx="822.35791"
+ id="radialGradient2764"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ r="48.537544"
+ fy="379.37366"
+ fx="834.06329"
+ cy="379.37366"
+ cx="834.06329"
+ id="radialGradient2766"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ r="51.490337"
+ fy="416.6268"
+ fx="834.16461"
+ cy="416.6268"
+ cx="834.16461"
+ id="radialGradient2768"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ r="22.916662"
+ fy="653.19714"
+ fx="559.93085"
+ cy="653.19714"
+ cx="559.93085"
+ id="radialGradient2770"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ r="21.03826"
+ fy="635.96777"
+ fx="596.68011"
+ cy="635.96777"
+ cx="596.68011"
+ id="radialGradient2772"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ r="20.071379"
+ fy="620.45239"
+ fx="637.07098"
+ cy="620.45239"
+ cx="637.07098"
+ id="radialGradient2774"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ r="23.940041"
+ fy="613.8067"
+ fx="665.83667"
+ cy="613.8067"
+ cx="665.83667"
+ id="radialGradient2776"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ r="27.265039"
+ fy="592.33008"
+ fx="696.78522"
+ cy="592.33008"
+ cx="696.78522"
+ id="radialGradient2778"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ r="39.235065"
+ fy="565.7605"
+ fx="734.74274"
+ cy="565.7605"
+ cx="734.74274"
+ id="radialGradient2780"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ r="55.195084"
+ fy="558.89948"
+ fx="793.18164"
+ cy="558.89948"
+ cx="793.18164"
+ id="radialGradient2782"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ r="57.855091"
+ fy="520.16986"
+ fx="799.92041"
+ cy="520.16986"
+ cx="799.92041"
+ id="radialGradient2784"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ r="53.865082"
+ fy="457.29218"
+ fx="825.40234"
+ cy="457.29218"
+ cx="825.40234"
+ id="radialGradient2786"
+ xlink:href="#linearGradient10990"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ r="165.43961"
+ fy="432.99265"
+ fx="425.42249"
+ cy="432.99265"
+ cx="425.42249"
+ id="radialGradient2788"
+ xlink:href="#linearGradient5817"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="786.83405"
+ x2="553.63983"
+ y1="596.54565"
+ x1="441.50293"
+ id="linearGradient2790"
+ xlink:href="#linearGradient5809"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(155.91995,3.7227902)"
+ gradientUnits="userSpaceOnUse"
+ y2="133.54459"
+ x2="225.65819"
+ y1="354.45773"
+ x1="331.57434"
+ id="linearGradient2792"
+ xlink:href="#linearGradient5074"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-478.87675,4.232021)"
+ gradientUnits="userSpaceOnUse"
+ y2="294.85672"
+ x2="518.04419"
+ y1="682.32623"
+ x1="225.43034"
+ id="linearGradient2794"
+ xlink:href="#linearGradient5066"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="translate(-453.48462,5.6426947)"
+ gradientUnits="userSpaceOnUse"
+ y2="653.04156"
+ x2="253.71039"
+ y1="213.74759"
+ x1="544.29083"
+ id="linearGradient2796"
+ xlink:href="#linearGradient4996"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2798">
+ <stop
+ id="stop2800"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ id="stop2802"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2804">
+ <stop
+ id="stop2806"
+ offset="0.00000000"
+ style="stop-color:#dfefff;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;"
+ offset="0.69999999"
+ id="stop2808" />
+ <stop
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;"
+ offset="0.80000001"
+ id="stop2810" />
+ <stop
+ id="stop2812"
+ offset="1.0000000"
+ style="stop-color:#007dfd;stop-opacity:0.50196081;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2814">
+ <stop
+ id="stop2816"
+ offset="0.00000000"
+ style="stop-color:#ffffff;stop-opacity:0.62886596;" />
+ <stop
+ id="stop2818"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2820">
+ <stop
+ id="stop2822"
+ offset="0.00000000"
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2824"
+ offset="1.0000000"
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2826">
+ <stop
+ id="stop2828"
+ offset="0.00000000"
+ style="stop-color:#2727e8;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2830"
+ offset="1.0000000"
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2832">
+ <stop
+ id="stop2834"
+ offset="0.00000000"
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop2836"
+ offset="1.0000000"
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2838">
+ <stop
+ style="stop-color:#007ffe;stop-opacity:0.62745100;"
+ offset="0.00000000"
+ id="stop2840" />
+ <stop
+ id="stop2842"
+ offset="0.80357140"
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;"
+ offset="1.0000000"
+ id="stop2844" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2846">
+ <stop
+ id="stop2848"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.89999998"
+ id="stop2850" />
+ <stop
+ id="stop2852"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2854">
+ <stop
+ id="stop2856"
+ offset="0"
+ style="stop-color:black;stop-opacity:0.49779737;" />
+ <stop
+ style="stop-color:black;stop-opacity:0.49803922;"
+ offset="0.95266271"
+ id="stop2858" />
+ <stop
+ id="stop2860"
+ offset="1"
+ style="stop-color:black;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3776"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3841"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3843"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730936,0,0,0.1702044,373.10831,358.56126)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ id="linearGradient2889">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2891" />
+ <stop
+ id="stop2893"
+ offset="0.95266271"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2895" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2881">
+ <stop
+ style="stop-color:black;stop-opacity:0.49779737;"
+ offset="0"
+ id="stop2883" />
+ <stop
+ id="stop2885"
+ offset="0.89999998"
+ style="stop-color:black;stop-opacity:0.49803922;" />
+ <stop
+ style="stop-color:black;stop-opacity:0;"
+ offset="1"
+ id="stop2887" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2873">
+ <stop
+ id="stop2875"
+ offset="0.00000000"
+ style="stop-color:#007ffe;stop-opacity:0.62745100;" />
+ <stop
+ style="stop-color:#3d9eff;stop-opacity:0.62745100;"
+ offset="0.80357140"
+ id="stop2877" />
+ <stop
+ id="stop2879"
+ offset="1.0000000"
+ style="stop-color:#a5d2ff;stop-opacity:0.62745100;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2867">
+ <stop
+ style="stop-color:#69b4ff;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2869" />
+ <stop
+ style="stop-color:#b0d8ff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2871" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2861">
+ <stop
+ style="stop-color:#2727e8;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2863" />
+ <stop
+ style="stop-color:#5f5fee;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2865" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2855">
+ <stop
+ style="stop-color:#2b2b9c;stop-opacity:1.0000000;"
+ offset="0.00000000"
+ id="stop2857" />
+ <stop
+ style="stop-color:#5c5cb3;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2859" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2849">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.62886596;"
+ offset="0.00000000"
+ id="stop2851" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.00000000;"
+ offset="1.0000000"
+ id="stop2853" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2839">
+ <stop
+ style="stop-color:#dfefff;stop-opacity:0.50196081;"
+ offset="0.00000000"
+ id="stop2841" />
+ <stop
+ id="stop2843"
+ offset="0.69999999"
+ style="stop-color:#8ec6fe;stop-opacity:0.50196078;" />
+ <stop
+ id="stop2845"
+ offset="0.80000001"
+ style="stop-color:#3e9dfe;stop-opacity:0.50196081;" />
+ <stop
+ style="stop-color:#007dfd;stop-opacity:0.50196081;"
+ offset="1.0000000"
+ id="stop2847" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2833">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop2835" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop2837" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4996"
+ id="linearGradient2831"
+ x1="544.29083"
+ y1="213.74759"
+ x2="253.71039"
+ y2="653.04156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-453.48462,5.6426947)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5066"
+ id="linearGradient2829"
+ x1="225.43034"
+ y1="682.32623"
+ x2="518.04419"
+ y2="294.85672"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-478.87675,4.232021)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2827"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2825"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5817"
+ id="radialGradient2823"
+ cx="425.42249"
+ cy="432.99265"
+ fx="425.42249"
+ fy="432.99265"
+ r="165.43961"
+ gradientTransform="matrix(0.546319,0.218253,-0.436865,1.093538,105.62767,-147.06742)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2821"
+ cx="825.40234"
+ cy="457.29218"
+ fx="825.40234"
+ fy="457.29218"
+ r="53.865082"
+ gradientTransform="matrix(1.442415,0.58951,-0.216275,0.529183,-1132.7474,-269.06738)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2819"
+ cx="799.92041"
+ cy="520.16986"
+ fx="799.92041"
+ fy="520.16986"
+ r="57.855091"
+ gradientTransform="matrix(1.238186,1.07492,-0.532416,0.613282,-668.29405,-630.01478)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2817"
+ cx="793.18164"
+ cy="558.89948"
+ fx="793.18164"
+ fy="558.89948"
+ r="55.195084"
+ gradientTransform="matrix(1.006265,1.510239,-0.401621,0.267598,-663.16697,-833.15883)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2815"
+ cx="734.74274"
+ cy="565.7605"
+ fx="734.74274"
+ fy="565.7605"
+ r="39.235065"
+ gradientTransform="matrix(0.747853,2.224634,-0.425239,0.142953,-427.60907,-1188.9396)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2813"
+ cx="696.78522"
+ cy="592.33008"
+ fx="696.78522"
+ fy="592.33008"
+ r="27.265039"
+ gradientTransform="matrix(0.948955,4.089824,-0.993629,0.230546,-207.73447,-2450.4193)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2811"
+ cx="665.83667"
+ cy="613.8067"
+ fx="665.83667"
+ fy="613.8067"
+ r="23.940041"
+ gradientTransform="matrix(0.334297,4.019557,-0.995094,8.275962e-2,391.82207,-2240.9819)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2809"
+ cx="637.07098"
+ cy="620.45239"
+ fx="637.07098"
+ fy="620.45239"
+ r="20.071379"
+ gradientTransform="matrix(1.261524e-6,4.784578,-0.528762,1.394159e-7,258.39389,-2486.0713)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2807"
+ cx="596.68011"
+ cy="635.96777"
+ fx="596.68011"
+ fy="635.96777"
+ r="21.03826"
+ gradientTransform="matrix(-0.492499,4.368806,-0.629878,-7.100652e-2,732.99123,-2042.7743)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2805"
+ cx="559.93085"
+ cy="653.19714"
+ fx="559.93085"
+ fy="653.19714"
+ r="22.916662"
+ gradientTransform="matrix(-0.523809,2.977912,-0.573765,-0.100924,597.43787,-1122.4516)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2803"
+ cx="834.16461"
+ cy="416.6268"
+ fx="834.16461"
+ fy="416.6268"
+ r="51.490337"
+ gradientTransform="matrix(1.536957,0.35811,-0.115486,0.495648,-1220.8078,-81.019121)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2801"
+ cx="834.06329"
+ cy="379.37366"
+ fx="834.06329"
+ fy="379.37366"
+ r="48.537544"
+ gradientTransform="matrix(1.604552,0.137396,-4.558732e-2,0.532382,-1332.9848,57.130614)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10990"
+ id="radialGradient2799"
+ cx="822.35791"
+ cy="334.10477"
+ fx="822.35791"
+ fy="334.10477"
+ r="37.6567"
+ gradientTransform="matrix(1.389618,6.572876e-8,-1.395947e-8,0.295127,-1147.0601,218.83232)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2797"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2795"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(155.91995,3.7227902)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient2793"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient2791"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ id="linearGradient3199">
+ <stop
+ id="stop3201"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.5"
+ id="stop3216" />
+ <stop
+ id="stop3218"
+ offset="0.8035714"
+ style="stop-color:#ffffff;stop-opacity:0.24705882;" />
+ <stop
+ id="stop3203"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3199"
+ id="radialGradient3076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0745274,1.0832957,-0.1820881,0.1806143,4.8810925,-12.034328)"
+ cx="45.457588"
+ cy="45.411678"
+ fx="45.457588"
+ fy="45.411678"
+ r="17.2868" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3100"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5809"
+ id="linearGradient3119"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="441.50293"
+ y1="596.54565"
+ x2="553.63983"
+ y2="786.83405" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5074"
+ id="linearGradient3121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.1730824,0,0,0.1718448,373.11251,357.18549)"
+ x1="331.57434"
+ y1="354.45773"
+ x2="225.65819"
+ y2="133.54459" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4856308"
+ inkscape:cx="210.56928"
+ inkscape:cy="191.36374"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ width="500px"
+ height="500px"
+ inkscape:window-width="1440"
+ inkscape:window-height="844"
+ inkscape:window-x="0"
+ inkscape:window-y="22" />
+ <metadata
+ id="metadata2165">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ id="g3417">
+ <g
+ id="g4261"
+ transform="translate(-247.78438,-65.963114)">
+ <g
+ style="display:inline"
+ inkscape:label="Shadow"
+ id="g4263" />
+ <g
+ style="display:inline"
+ id="g4265"
+ inkscape:label="Background">
+ <g
+ id="g4267">
+ <path
+ sodipodi:type="arc"
+ style="fill:#9195e1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4269"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ <path
+ style="fill:url(#linearGradient3119);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ id="path4271"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-xdpi="24.028801"
+ inkscape:export-ydpi="24.028801" />
+ <path
+ style="fill:url(#linearGradient3121);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ id="path4273"
+ sodipodi:nodetypes="cssssssssssscsss" />
+ <path
+ style="fill:#b2b4ff;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ id="path4275"
+ sodipodi:nodetypes="cccccccsccccccccccc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="path4277"
+ sodipodi:cx="365.22308"
+ sodipodi:cy="433.07086"
+ sodipodi:rx="395.53806"
+ sodipodi:ry="395.53806"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)" />
+ </g>
+ </g>
+ <g
+ style="display:inline"
+ inkscape:label="SeaMonkey"
+ id="g4279" />
+ </g>
+ <g
+ id="g4281"
+ transform="matrix(1.2522169,0,0,1.1949279,445.5891,111.78335)">
+ <path
+ style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000080;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M -227.62113,184.74074 C -232.11524,184.64538 -236.59155,187.11474 -238.81213,191.02064 C -241.45058,195.3896 -240.88344,201.21399 -237.62962,205.11996 L -231.17767,212.07609 L -224.89082,218.68023 L -218.60879,225.45787 L -212.14722,232.06698 L -205.86038,238.6711 L -199.57835,245.44876 L -193.29149,252.05287 L -186.82992,258.66198 L -187.26399,249.0989 L -187.5185,239.36725 L -177.92762,240.33506 L -168.33671,241.30286 L -174.62354,234.69874 L -181.08512,228.08963 L -187.36717,221.31198 L -193.65399,214.70786 L -200.11558,208.09876 L -206.3976,201.32111 L -212.68446,194.71697 L -219.14604,188.10786 C -220.93634,186.44951 -223.28444,185.38524 -225.70391,184.96901 C -226.34103,184.84661 -226.97911,184.75437 -227.62113,184.74074 z "
+ id="path4283"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-ydpi="80.222176" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4285"
+ sodipodi:cx="-7.2153969"
+ sodipodi:cy="3.788281"
+ sodipodi:rx="0.78960949"
+ sodipodi:ry="0.77599555"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 1 1 -6.4755362,3.517194"
+ sodipodi:start="5.9358167"
+ sodipodi:end="12.209503"
+ sodipodi:open="true"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-ydpi="76.790672" />
+ </g>
+ </g>
+ <g
+ id="g3402">
+ <g
+ style="fill:none"
+ transform="translate(-71.791544,-65.289999)"
+ id="g2403">
+ <g
+ id="g2405"
+ inkscape:label="Shadow"
+ style="display:inline;fill:none" />
+ <g
+ inkscape:label="Background"
+ id="g2407"
+ style="display:inline;fill:none">
+ <g
+ style="fill:none"
+ id="g2409">
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2411"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ <path
+ inkscape:export-ydpi="24.028801"
+ inkscape:export-xdpi="24.028801"
+ sodipodi:nodetypes="ccccccccccccccc"
+ id="path2413"
+ d="M 383.85677,470.3348 C 377.53204,457.98527 375.8029,449.83833 374.32889,438.05062 C 378.14146,437.94123 379.22897,435.80777 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 440.81785,446.52116 442.80237,445.22243 C 444.78976,444.34028 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 468.42806,440.43433 471.65702,440.40231 C 483.01314,435.05163 496.02212,436.88091 501.18154,438.05673 C 501.07384,442.40555 500.53825,448.50951 499.73619,451.31426 C 498.90133,455.26551 498.48114,457.35905 496.52855,460.90786 C 495.66551,464.98599 493.73148,467.62488 490.6986,472.53442 C 487.53223,477.49134 481.84147,482.66091 475.34008,488.08582 C 470.98549,493.97562 455.61977,497.11357 452.39233,498.97297 C 443.83341,501.28353 435.24983,502.14089 420.22819,498.1061 C 403.58315,494.46692 390.60207,479.49025 383.85677,470.3348 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cssssssssssscsss"
+ id="path2415"
+ d="M 375.79574,423.19042 C 376.46114,420.52879 378.05483,415.05973 379.69198,412.87686 C 382.42822,409.22852 386.68249,400.6601 389.77634,396.37516 C 394.2965,390.11479 408.02971,381.77281 414.07051,378.9567 C 423.39221,374.61108 446.52624,372.59535 459.45016,378.49832 C 464.99074,381.02897 475.71552,386.57191 480.07729,391.10378 C 484.14231,395.32736 490.59739,402.8387 493.82869,409.439 C 495.57775,413.01167 500.99233,427.2117 500.24601,427.31584 C 495.53266,427.97351 490.67725,429.69832 485.80703,431.21207 C 482.37937,432.27745 476.48862,435.10831 475.72267,435.10831 C 472.28481,435.10831 460.23197,434.26949 456.69987,434.42074 C 453.17776,434.57156 447.8344,434.5166 446.15714,434.87911 C 441.00943,435.99167 432.69168,434.7769 427.82192,431.89964 C 418.57716,433.82204 415.04824,435.24719 400.54829,430.52451 C 388.3259,426.54363 377.49956,429.46826 376.02493,429.83693 C 374.07594,430.32418 375.44514,424.59281 375.79574,423.19042 z "
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cccccccsccccccccccc"
+ id="path2417"
+ d="M 374.65256,430.0385 C 374.1887,430.34605 374.35404,436.70005 374.7261,439.81207 C 375.42381,441.15213 373.96937,442.96262 374.78727,444.23876 C 375.84956,445.50451 392.75119,448.41324 405.78478,441.52285 C 412.95251,445.17623 425.27294,447.28507 427.75792,447.19904 C 429.11551,447.27814 458.801,442.11776 463.91919,441.56168 C 467.10108,441.09167 470.26158,440.43433 473.49054,440.40231 C 475.67907,440.32301 485.08548,442.41714 487.59909,443.4404 C 495.57724,446.6882 498.5412,445.36222 500.03457,445.84665 C 500.38527,444.69491 500.99662,442.77975 500.65295,440.54231 C 500.50567,437.84705 501.46062,431.68974 500.42478,429.96717 C 499.56175,429.23232 500.65489,427.62005 499.65535,427.39573 C 496.29864,427.96353 490.39889,429.69905 485.51354,431.30509 C 481.21376,432.7304 479.36406,434.16468 476.48881,434.25965 C 473.78485,435.08987 466.87025,434.66767 463.75584,434.59846 C 457.13736,433.14541 450.20817,429.71277 443.01922,428.49117 C 439.12299,428.19282 431.02825,430.04129 427.80082,431.90068 C 422.67976,429.39825 411.16165,427.88261 406.91193,427.51487 C 391.41486,426.16632 379.20302,428.83964 374.65256,430.0385 z "
+ style="fill:none;fill-opacity:0.50196078;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ transform="matrix(0.1596245,0,0,0.1596247,379.49308,368.61522)"
+ d="M 760.76114 433.07086 A 395.53806 395.53806 0 1 1 -30.314972,433.07086 A 395.53806 395.53806 0 1 1 760.76114 433.07086 z"
+ sodipodi:ry="395.53806"
+ sodipodi:rx="395.53806"
+ sodipodi:cy="433.07086"
+ sodipodi:cx="365.22308"
+ id="path2419"
+ style="fill:none;fill-opacity:0.36184208;fill-rule:evenodd;stroke:#26269a;stroke-width:10.80470085;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ <g
+ id="g2421"
+ inkscape:label="SeaMonkey"
+ style="display:inline;fill:none" />
+ </g>
+ <g
+ transform="matrix(1.2522169,0,0,1.1949279,621.58194,112.45646)"
+ id="g2423">
+ <path
+ inkscape:export-ydpi="80.222176"
+ inkscape:export-xdpi="80.222176"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk3.png"
+ sodipodi:nodetypes="ccsccccccccccccccccccccsc"
+ id="path2425"
+ d="M -227.62113,184.74074 C -232.11524,184.64538 -236.59155,187.11474 -238.81213,191.02064 C -241.45058,195.3896 -240.88344,201.21399 -237.62962,205.11996 L -231.17767,212.07609 L -224.89082,218.68023 L -218.60879,225.45787 L -212.14722,232.06698 L -205.86038,238.6711 L -199.57835,245.44876 L -193.29149,252.05287 L -186.82992,258.66198 L -187.26399,249.0989 L -187.5185,239.36725 L -177.92762,240.33506 L -168.33671,241.30286 L -174.62354,234.69874 L -181.08512,228.08963 L -187.36717,221.31198 L -193.65399,214.70786 L -200.11558,208.09876 L -206.3976,201.32111 L -212.68446,194.71697 L -219.14604,188.10786 C -220.93634,186.44951 -223.28444,185.38524 -225.70391,184.96901 C -226.34103,184.84661 -226.97911,184.75437 -227.62113,184.74074 z "
+ style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000080;stroke-width:2.79422808;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:export-ydpi="76.790672"
+ inkscape:export-xdpi="76.790672"
+ inkscape:export-filename="C:\Documents and Settings\Owner.SPINELLOZONE\Desktop\bkmk.png"
+ transform="matrix(2.9454369,2.8106115,-3.1508717,2.6846787,-194.04659,207.50232)"
+ sodipodi:open="true"
+ sodipodi:end="12.209503"
+ sodipodi:start="5.9358167"
+ d="M -6.4729494,3.5241129 A 0.78960949,0.77599555 0 1 1 -6.4755362,3.517194"
+ sodipodi:ry="0.77599555"
+ sodipodi:rx="0.78960949"
+ sodipodi:cy="3.788281"
+ sodipodi:cx="-7.2153969"
+ id="path2427"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.39147401;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/branding/seamonkey/icons/windows/JSConsoleWindow.ico b/comm/suite/branding/seamonkey/icons/windows/JSConsoleWindow.ico
new file mode 100644
index 0000000000..e2562fa2a1
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/JSConsoleWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/abcardWindow.ico b/comm/suite/branding/seamonkey/icons/windows/abcardWindow.ico
new file mode 100644
index 0000000000..3e1c0b73e4
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/abcardWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/ablistWindow.ico b/comm/suite/branding/seamonkey/icons/windows/ablistWindow.ico
new file mode 100644
index 0000000000..c720e5b6c7
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/ablistWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/addressbookWindow.ico b/comm/suite/branding/seamonkey/icons/windows/addressbookWindow.ico
new file mode 100644
index 0000000000..a942510117
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/addressbookWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/bookmarkproperties.ico b/comm/suite/branding/seamonkey/icons/windows/bookmarkproperties.ico
new file mode 100644
index 0000000000..bf7beaf288
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/bookmarkproperties.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/chatzilla-window.ico b/comm/suite/branding/seamonkey/icons/windows/chatzilla-window.ico
new file mode 100644
index 0000000000..c3beee23a5
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/chatzilla-window.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/downloadManager.ico b/comm/suite/branding/seamonkey/icons/windows/downloadManager.ico
new file mode 100644
index 0000000000..e9d413ee18
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/downloadManager.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/editorWindow.ico b/comm/suite/branding/seamonkey/icons/windows/editorWindow.ico
new file mode 100644
index 0000000000..2a2578ca31
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/editorWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/findBookmarkWindow.ico b/comm/suite/branding/seamonkey/icons/windows/findBookmarkWindow.ico
new file mode 100644
index 0000000000..d970582365
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/findBookmarkWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/findHistoryWindow.ico b/comm/suite/branding/seamonkey/icons/windows/findHistoryWindow.ico
new file mode 100644
index 0000000000..6e6c271604
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/findHistoryWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/gif-file.ico b/comm/suite/branding/seamonkey/icons/windows/gif-file.ico
new file mode 100644
index 0000000000..afe8372b10
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/gif-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/history-window.ico b/comm/suite/branding/seamonkey/icons/windows/history-window.ico
new file mode 100644
index 0000000000..0e07410670
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/history-window.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/html-file.ico b/comm/suite/branding/seamonkey/icons/windows/html-file.ico
new file mode 100644
index 0000000000..ed8fc194a2
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/html-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/image-file.ico b/comm/suite/branding/seamonkey/icons/windows/image-file.ico
new file mode 100644
index 0000000000..42ffa4120a
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/image-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/jpeg-file.ico b/comm/suite/branding/seamonkey/icons/windows/jpeg-file.ico
new file mode 100644
index 0000000000..b5d252af8f
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/jpeg-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/main-window.ico b/comm/suite/branding/seamonkey/icons/windows/main-window.ico
new file mode 100644
index 0000000000..2bf1dbeeeb
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/main-window.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/messengerWindow.ico b/comm/suite/branding/seamonkey/icons/windows/messengerWindow.ico
new file mode 100644
index 0000000000..7c59703110
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/messengerWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/msgcomposeWindow.ico b/comm/suite/branding/seamonkey/icons/windows/msgcomposeWindow.ico
new file mode 100644
index 0000000000..d54d2e4908
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/msgcomposeWindow.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/newmail.ico b/comm/suite/branding/seamonkey/icons/windows/newmail.ico
new file mode 100644
index 0000000000..301c0853ee
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/newmail.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/places.ico b/comm/suite/branding/seamonkey/icons/windows/places.ico
new file mode 100644
index 0000000000..b42924036a
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/places.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/readme.txt b/comm/suite/branding/seamonkey/icons/windows/readme.txt
new file mode 100755
index 0000000000..398a94c546
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/readme.txt
@@ -0,0 +1,50 @@
+ 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/.
+
+
+
+The icons in this directory are Win32 program icons, file association icons,
+and window icons (to appear in the upper left corner of various windows).
+
+
+
+Requirements:
+Each icon should contain the following devices images:
+48x48, 32x32, and 16x16 - 16 color
+48x48, 32x32, and 16x16 - True color
+48x48, 32x32, and 16x16 - True color XP (Contains alpha shadows)
+
+At this time, we don't think 256 color is a good idea since Windows does
+a good job dithering and some systems will use 256 color icons even when
+True Color exists.
+
+See bug http://bugzilla.mozilla.org/show_bug.cgi?id=99380 for a lot of rambling about
+icons.
+
+
+
+Window icons:
+Should be named using the following convention: [NAME]-window.ico where [NAME]
+represents the name of the window. Example:
+history-window.ico
+
+Blank template icon should be available as: template-window.ico
+
+XXXFIXME
+Some icons have been given names such as downloadManager.ico and is because
+there are two naming schemes for windows. This should be remedied.
+Bug http://bugzilla.mozilla.org/show_bug.cgi?id=199576
+XXXFIXME
+
+
+File association icons:
+Should be named using the following convention: [NAME]-file.ico where [NAME]
+represents the type of file it is. Example:
+image-file.ico
+
+Blank template icon should be available as: template-file.ico
+
+
+Program icon:
+Currently, the only available program icon is mozilla.ico
diff --git a/comm/suite/branding/seamonkey/icons/windows/script-file.ico b/comm/suite/branding/seamonkey/icons/windows/script-file.ico
new file mode 100644
index 0000000000..6a0dcda734
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/script-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/template-file.ico b/comm/suite/branding/seamonkey/icons/windows/template-file.ico
new file mode 100644
index 0000000000..a9704d6034
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/template-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/template-window.ico b/comm/suite/branding/seamonkey/icons/windows/template-window.ico
new file mode 100755
index 0000000000..16bd7e9d1c
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/template-window.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/xml-file.ico b/comm/suite/branding/seamonkey/icons/windows/xml-file.ico
new file mode 100644
index 0000000000..29d8d6c439
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/xml-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/icons/windows/xul-file.ico b/comm/suite/branding/seamonkey/icons/windows/xul-file.ico
new file mode 100644
index 0000000000..d9b3f4888b
--- /dev/null
+++ b/comm/suite/branding/seamonkey/icons/windows/xul-file.ico
Binary files differ
diff --git a/comm/suite/branding/seamonkey/locales/Makefile.in b/comm/suite/branding/seamonkey/locales/Makefile.in
new file mode 100644
index 0000000000..843bea8827
--- /dev/null
+++ b/comm/suite/branding/seamonkey/locales/Makefile.in
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+LOCALE_TOPDIR=$(commtopsrcdir)
+LOCALE_RELATIVEDIR=suite/branding/seamonkey/locales
+
+include $(topsrcdir)/config/config.mk
diff --git a/comm/suite/branding/seamonkey/locales/en-US/brand.dtd b/comm/suite/branding/seamonkey/locales/en-US/brand.dtd
new file mode 100644
index 0000000000..6b88ad7200
--- /dev/null
+++ b/comm/suite/branding/seamonkey/locales/en-US/brand.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY brandFullName "SeaMonkey">
+<!ENTITY brandShortName "SeaMonkey">
+<!ENTITY brandShorterName "SeaMonkey">
+<!ENTITY vendorShortName "SeaMonkey e.V.">
diff --git a/comm/suite/branding/seamonkey/locales/en-US/brand.properties b/comm/suite/branding/seamonkey/locales/en-US/brand.properties
new file mode 100644
index 0000000000..94acf7da68
--- /dev/null
+++ b/comm/suite/branding/seamonkey/locales/en-US/brand.properties
@@ -0,0 +1,31 @@
+# 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/.
+
+brandFullName=SeaMonkey
+brandShortName=SeaMonkey
+brandShorterName=SeaMonkey
+vendorShortName=SeaMonkey e.V.
+
+# Only change these links if you are providing a localized website including
+# release notes.
+# LOCALIZATION NOTE (app.releaseNotesURL): Only translate this string if
+# providing a localized version of the release notes.
+app.releaseNotesURL=https://www.seamonkey-project.org/releases/seamonkey%VERSION%/
+# LOCALIZATION NOTE (app.troubleshootingURL): Only translate this string if
+# providing a localized version of the release notes.
+app.troubleshootingURL=https://www.seamonkey-project.org/releases/seamonkey%VERSION%/#troubleshooting
+# LOCALIZATION NOTE (app.vendorURL): Only translate this string if
+# providing a localized version of the SeaMonkey website.
+app.vendorURL=https://www.seamonkey-project.org/
+# LOCALIZATION NOTE (app.support.baseURL): Only translate this string if
+# providing a localized version of the SeaMonkey documentation.
+app.support.baseURL=https://www.seamonkey-project.org/doc/
+# LOCALIZATION NOTE (app.update.url.details): Only translate this string if
+# providing a localized version of the release notes. More information about
+# this update link available in the update wizard.
+app.update.url.details=https://www.seamonkey-project.org/releases/
+# LOCALIZATION NOTE (app.update.url.manual): Only translate this string if
+# providing a localized version of the SeaMonkey website. Available if for some
+# reason all update installation attempts fail.
+app.update.url.manual=https://www.seamonkey-project.org/
diff --git a/comm/suite/branding/seamonkey/locales/jar.mn b/comm/suite/branding/seamonkey/locales/jar.mn
new file mode 100644
index 0000000000..708e5c91af
--- /dev/null
+++ b/comm/suite/branding/seamonkey/locales/jar.mn
@@ -0,0 +1,6 @@
+#filter substitution
+
+@AB_CD@.jar:
+% locale branding @AB_CD@ %locale/@AB_CD@/branding/
+ locale/@AB_CD@/branding/brand.dtd (%brand.dtd)
+ locale/@AB_CD@/branding/brand.properties (%brand.properties)
diff --git a/comm/suite/branding/seamonkey/locales/moz.build b/comm/suite/branding/seamonkey/locales/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/branding/seamonkey/locales/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/branding/seamonkey/moz.build b/comm/suite/branding/seamonkey/moz.build
new file mode 100644
index 0000000000..12e4990e74
--- /dev/null
+++ b/comm/suite/branding/seamonkey/moz.build
@@ -0,0 +1,9 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += ["content", "locales"]
+
+include("../../../suite/branding/branding-common.mozbuild")
+SeaMonkeyBranding()
diff --git a/comm/suite/branding/seamonkey/seamonkey-branding.js b/comm/suite/branding/seamonkey/seamonkey-branding.js
new file mode 100644
index 0000000000..c80ce30ff2
--- /dev/null
+++ b/comm/suite/branding/seamonkey/seamonkey-branding.js
@@ -0,0 +1 @@
+// For future use
diff --git a/comm/suite/branding/seamonkey/seamonkey.icns b/comm/suite/branding/seamonkey/seamonkey.icns
new file mode 100644
index 0000000000..9e241c2fb8
--- /dev/null
+++ b/comm/suite/branding/seamonkey/seamonkey.icns
Binary files differ
diff --git a/comm/suite/branding/seamonkey/wizHeader.bmp b/comm/suite/branding/seamonkey/wizHeader.bmp
new file mode 100644
index 0000000000..1c092fcbb6
--- /dev/null
+++ b/comm/suite/branding/seamonkey/wizHeader.bmp
Binary files differ
diff --git a/comm/suite/branding/seamonkey/wizHeaderRTL.bmp b/comm/suite/branding/seamonkey/wizHeaderRTL.bmp
new file mode 100644
index 0000000000..22dc90b33b
--- /dev/null
+++ b/comm/suite/branding/seamonkey/wizHeaderRTL.bmp
Binary files differ
diff --git a/comm/suite/branding/seamonkey/wizWatermark.bmp b/comm/suite/branding/seamonkey/wizWatermark.bmp
new file mode 100644
index 0000000000..2dc1c596d7
--- /dev/null
+++ b/comm/suite/branding/seamonkey/wizWatermark.bmp
Binary files differ
diff --git a/comm/suite/browser/SuiteBrowser.manifest b/comm/suite/browser/SuiteBrowser.manifest
new file mode 100644
index 0000000000..773ea9573c
--- /dev/null
+++ b/comm/suite/browser/SuiteBrowser.manifest
@@ -0,0 +1,23 @@
+component {c2343730-dc2c-11d3-98b3-001083010e9b} nsBrowserContentHandler.js
+contract @mozilla.org/uriloader/content-handler;1?type=text/html {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=application/vnd.mozilla.xul+xml {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/svg+xml {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=text/rdf {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=text/xml {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=application/xhtml+xml {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=text/css {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=text/plain {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/gif {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/jpeg {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/jpg {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/png {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/bmp {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/x-icon {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=image/vnd.microsoft.icon {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/uriloader/content-handler;1?type=application/http-index-format {c2343730-dc2c-11d3-98b3-001083010e9b}
+contract @mozilla.org/commandlinehandler/general-startup;1?type=browser {c2343730-dc2c-11d3-98b3-001083010e9b}
+category command-line-handler x-default @mozilla.org/commandlinehandler/general-startup;1?type=browser
+category command-line-validator b-default @mozilla.org/commandlinehandler/general-startup;1?type=browser
+component {45c8f75b-a299-4178-a461-f63690389055} nsTypeAheadFind.js
+contract @mozilla.org/suite/typeaheadfind;1 {45c8f75b-a299-4178-a461-f63690389055}
+category app-startup SuiteTypeAheadFind service,@mozilla.org/suite/typeaheadfind;1
diff --git a/comm/suite/browser/browser-places.js b/comm/suite/browser/browser-places.js
new file mode 100644
index 0000000000..83584456e0
--- /dev/null
+++ b/comm/suite/browser/browser-places.js
@@ -0,0 +1,983 @@
+/* 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 {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyScriptGetter(this, ["PlacesToolbar", "PlacesMenu",
+ "PlacesPanelview", "PlacesPanelMenuView"],
+ "chrome://communicator/content/places/browserPlacesViews.js");
+
+var StarUI = {
+ _itemGuids: null,
+ uri: null,
+ _batching: false,
+
+ _element: function(aID) {
+ return document.getElementById(aID);
+ },
+
+ // Edit-bookmark panel
+ get panel() {
+ delete this.panel;
+ var element = this._element("editBookmarkPanel");
+ // initially the panel is hidden
+ // to avoid impacting startup / new window performance
+ element.hidden = false;
+ element.addEventListener("popuphidden", this);
+ element.addEventListener("keypress", this);
+ element.addEventListener("keypress", this);
+ element.addEventListener("mousedown", this);
+ element.addEventListener("mouseout", this);
+ element.addEventListener("mousemove", this);
+ element.addEventListener("compositionstart", this);
+ element.addEventListener("compositionend", this);
+ element.addEventListener("input", this);
+ element.addEventListener("popuphidden", this);
+ element.addEventListener("popupshown", this);
+ return this.panel = element;
+ },
+
+ // Array of command elements to disable when the panel is opened.
+ get _blockedCommands() {
+ delete this._blockedCommands;
+ return this._blockedCommands =
+ ["cmd_close", "cmd_closeWindow"].map(id => this._element(id));
+ },
+
+ _blockCommands: function SU__blockCommands() {
+ this._blockedCommands.forEach(function (elt) {
+ // make sure not to permanently disable this item (see bug 409155)
+ if (elt.hasAttribute("wasDisabled"))
+ return;
+ if (elt.getAttribute("disabled") == "true") {
+ elt.setAttribute("wasDisabled", "true");
+ } else {
+ elt.setAttribute("wasDisabled", "false");
+ elt.setAttribute("disabled", "true");
+ }
+ });
+ },
+
+ _restoreCommandsState: function SU__restoreCommandsState() {
+ this._blockedCommands.forEach(function (elt) {
+ if (elt.getAttribute("wasDisabled") != "true")
+ elt.removeAttribute("disabled");
+ elt.removeAttribute("wasDisabled");
+ });
+ },
+
+ // nsIDOMEventListener
+ handleEvent: function SU_handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "popuphidden":
+ if (aEvent.originalTarget == this.panel) {
+ if (!this._element("editBookmarkPanelContent").hidden)
+ this.quitEditMode();
+
+ this._restoreCommandsState();
+ let guidsForRemoval = this._itemGuids;
+ this._itemGuids = null;
+
+ if (this._batching) {
+ this.endBatch();
+ }
+
+ switch (this._actionOnHide) {
+ case "cancel": {
+ PlacesTransactions.undo().catch(Cu.reportError);
+ break;
+ }
+ case "remove": {
+ PlacesTransactions.Remove(guidsForRemoval)
+ .transact().catch(Cu.reportError);
+ break;
+ }
+ }
+ this._actionOnHide = "";
+ }
+ break;
+ case "keypress":
+ if (aEvent.defaultPrevented) {
+ // The event has already been consumed inside of the panel.
+ break;
+ }
+ switch (aEvent.keyCode) {
+ case KeyEvent.DOM_VK_ESCAPE:
+ if (!this._element("editBookmarkPanelContent").hidden)
+ this.cancelButtonOnCommand();
+ break;
+ case KeyEvent.DOM_VK_RETURN:
+ if (aEvent.target.className == "expander-up" ||
+ aEvent.target.className == "expander-down" ||
+ aEvent.target.id == "editBMPanel_newFolderButton") {
+ //XXX Why is this necessary? The defaultPrevented check should
+ // be enough.
+ break;
+ }
+ this.panel.hidePopup(true);
+ break;
+ }
+ break;
+ }
+ },
+
+ _overlayLoaded: false,
+ _overlayLoading: false,
+ async showEditBookmarkPopup(aNode, aAnchorElement, aPosition, aIsNewBookmark, aUrl) {
+ // Slow double-clicks (not true double-clicks) shouldn't
+ // cause the panel to flicker.
+ if (this.panel.state == "showing" ||
+ this.panel.state == "open") {
+ return;
+ }
+
+ this._isNewBookmark = aIsNewBookmark;
+ this._uriForRemoval = "";
+ this._itemGuids = null;
+
+ // Performance: load the overlay the first time the panel is opened
+ // (see bug 392443).
+ if (this._overlayLoading)
+ return;
+
+ if (this._overlayLoaded) {
+ await this._doShowEditBookmarkPanel(aNode, aAnchorElement, aPosition, aUrl);
+ return;
+ }
+
+ this._overlayLoading = true;
+ document.loadOverlay(
+ "chrome://communicator/content/places/editBookmarkOverlay.xul",
+ (aSubject, aTopic, aData) => {
+ // Move the header (star, title, button) into the grid,
+ // so that it aligns nicely with the other items (bug 484022).
+ let header = this._element("editBookmarkPanelHeader");
+ let rows = this._element("editBookmarkPanelGrid").lastChild;
+ rows.insertBefore(header, rows.firstChild);
+ header.hidden = false;
+
+ this._overlayLoading = false;
+ this._overlayLoaded = true;
+ this._doShowEditBookmarkPanel(aNode, aAnchorElement, aPosition, aUrl);
+ }
+ );
+ },
+
+ async _doShowEditBookmarkPanel(aNode, aAnchorElement, aPosition, aUrl) {
+ if (this.panel.state != "closed")
+ return;
+
+ this._blockCommands(); // un-done in the popuphiding handler
+
+ // Set panel title:
+ // if we are batching, i.e. the bookmark has been added now,
+ // then show Page Bookmarked, else if the bookmark did already exist,
+ // we are about editing it, then use Edit This Bookmark.
+ this._element("editBookmarkPanelTitle").value =
+ this._isNewBookmark ?
+ gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
+ gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");
+
+ this._element("editBookmarkPanelBottomButtons").hidden = false;
+ this._element("editBookmarkPanelContent").hidden = false;
+
+ // The label of the remove button differs if the URI is bookmarked
+ // multiple times.
+ this._itemGuids = [];
+
+ await PlacesUtils.bookmarks.fetch({url: aUrl},
+ bookmark => this._itemGuids.push(bookmark.guid));
+
+ let forms = gNavigatorBundle.getString("editBookmark.removeBookmarks.label");
+ let bookmarksCount = this._itemGuids.length;
+ let label = PluralForm.get(bookmarksCount, forms)
+ .replace("#1", bookmarksCount);
+ this._element("editBookmarkPanelRemoveButton").label = label;
+
+ this.beginBatch();
+
+ let onPanelReady = fn => {
+ let target = this.panel;
+ if (target.parentNode) {
+ // By targeting the panel's parent and using a capturing listener, we
+ // can have our listener called before others waiting for the panel to
+ // be shown (which probably expect the panel to be fully initialized)
+ target = target.parentNode;
+ }
+ target.addEventListener("popupshown", function(event) {
+ fn();
+ }, {"capture": true, "once": true});
+ };
+ gEditItemOverlay.initPanel({ node: aNode,
+ onPanelReady,
+ hiddenRows: ["description", "location",
+ "loadInSidebar", "keyword"],
+ focusedElement: "preferred"});
+ this.panel.openPopup(aAnchorElement, aPosition);
+ },
+
+ panelShown:
+ function SU_panelShown(aEvent) {
+ if (aEvent.target == this.panel) {
+ if (!this._element("editBookmarkPanelContent").hidden) {
+ let fieldToFocus = "editBMPanel_" +
+ Services.prefs.getCharPref("browser.bookmarks.editDialog.firstEditField");
+ var elt = this._element(fieldToFocus);
+ elt.focus();
+ elt.select();
+ }
+ else {
+ // Note this isn't actually used anymore, we should remove this
+ // once we decide not to bring back the page bookmarked notification
+ this.panel.focus();
+ }
+ }
+ },
+
+ quitEditMode: function SU_quitEditMode() {
+ this._element("editBookmarkPanelContent").hidden = true;
+ this._element("editBookmarkPanelBottomButtons").hidden = true;
+ gEditItemOverlay.uninitPanel(true);
+ },
+
+ editButtonCommand: function SU_editButtonCommand() {
+ this.showEditBookmarkPopup();
+ },
+
+ cancelButtonOnCommand: function SU_cancelButtonOnCommand() {
+ this._actionOnHide = "cancel";
+ this.panel.hidePopup();
+ },
+
+ removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
+ this._removeBookmarksOnPopupHidden = true;
+ this._actionOnHide = "remove";
+ this.panel.hidePopup();
+ },
+
+ _batchBlockingDeferred: null,
+ beginBatch() {
+ if (this._batching)
+ return;
+ this._batchBlockingDeferred = PromiseUtils.defer();
+ PlacesTransactions.batch(async () => {
+ await this._batchBlockingDeferred.promise;
+ });
+ this._batching = true;
+ },
+
+ endBatch() {
+ if (!this._batching)
+ return;
+
+ this._batchBlockingDeferred.resolve();
+ this._batchBlockingDeferred = null;
+ this._batching = false;
+ },
+};
+
+var PlacesCommandHook = {
+
+ /**
+ * Adds a bookmark to the page loaded in the given browser using the
+ * properties dialog.
+ *
+ * @param aBrowser
+ * a <browser> element.
+ * @param [optional] aShowEditUI
+ * whether or not to show the edit-bookmark UI for the bookmark item
+ * @param [optional] aUrl
+ * Option to provide a URL to bookmark rather than the current page
+ * @param [optional] aTitle
+ * Option to provide a title for a bookmark to use rather than the
+ * getting the current page's title
+ */
+ async bookmarkPage(aBrowser, aShowEditUI, aUrl = null, aTitle = null) {
+ // If aUrl is provided, we want to bookmark that url rather than the
+ // the current page
+ let url = aUrl ? new URL(aUrl) : new URL(aBrowser.currentURI.spec);
+ let info = await PlacesUtils.bookmarks.fetch({ url });
+ let isNewBookmark = !info;
+ if (!info) {
+ let parentGuid = PlacesUtils.bookmarks.unfiledGuid;
+ info = { url, parentGuid };
+ let description = null;
+ let charset = null;
+
+ let docInfo = aUrl ? {} : await this._getPageDetails(aBrowser);
+
+ try {
+ if (docInfo.isErrorPage) {
+ let entry = await PlacesUtils.history.fetch(aBrowser.currentURI);
+ if (entry) {
+ info.title = entry.title;
+ }
+ } else {
+ info.title = aTitle || aBrowser.contentTitle;
+ }
+ info.title = info.title || url.href;
+ description = docInfo.description;
+ charset = aUrl ? null : aBrowser.characterSet;
+ } catch (e) {
+ Cu.reportError(e);
+ }
+
+ if (aShowEditUI && isNewBookmark) {
+ // If we bookmark the page here but open right into a cancelable
+ // state (i.e. new bookmark in Library), start batching here so
+ // all of the actions can be undone in a single undo step.
+ StarUI.beginBatch();
+ }
+
+ if (description) {
+ info.annotations = [{ name: PlacesUIUtils.DESCRIPTION_ANNO,
+ value: description }];
+ }
+
+ info.guid = await PlacesTransactions.NewBookmark(info).transact();
+
+ // Set the character-set
+ if (charset && !PrivateBrowsingUtils.isBrowserPrivate(aBrowser))
+ PlacesUtils.setCharsetForURI(makeURI(url.href), charset);
+ }
+
+ // If it was not requested to open directly in "edit" mode, we are done.
+ if (!aShowEditUI)
+ return;
+
+ let node = await PlacesUIUtils.promiseNodeLikeFromFetchInfo(info);
+
+ // Dock the panel to the star icon when possible, otherwise dock
+ // it to the content area.
+ if (aBrowser.contentWindow == window.content) {
+ let ubIcons = aBrowser.ownerDocument.getElementById("urlbar-icons");
+ if (ubIcons) {
+ await StarUI.showEditBookmarkPopup(node, ubIcons,
+ "bottomcenter topright",
+ isNewBookmark, url);
+ return;
+ }
+ }
+
+ await StarUI.showEditBookmarkPopup(node, aBrowser, "overlap",
+ isNewBookmark, url);
+ },
+
+ _getPageDetails(browser) {
+ return new Promise(resolve => {
+ let mm = browser.messageManager;
+ mm.addMessageListener("Bookmarks:GetPageDetails:Result", function listener(msg) {
+ mm.removeMessageListener("Bookmarks:GetPageDetails:Result", listener);
+ resolve(msg.data);
+ });
+
+ mm.sendAsyncMessage("Bookmarks:GetPageDetails", { });
+ });
+ },
+
+ /**
+ * Adds a bookmark to the page targeted by a link.
+ * @param parentId
+ * The folder in which to create a new bookmark if aURL isn't
+ * bookmarked.
+ * @param url (string)
+ * the address of the link target
+ * @param title
+ * The link text
+ * @param [optional] description
+ * The linked page description, if available
+ */
+ async bookmarkLink(parentId, url, title, description = "") {
+ let bm = await PlacesUtils.bookmarks.fetch({url});
+ if (bm) {
+ let node = await PlacesUIUtils.promiseNodeLikeFromFetchInfo(bm);
+ PlacesUIUtils.showBookmarkDialog({ action: "edit", node },
+ window.top);
+ return;
+ }
+
+ let parentGuid = parentId == PlacesUtils.bookmarksMenuFolderId ?
+ PlacesUtils.bookmarks.menuGuid :
+ await PlacesUtils.promiseItemGuid(parentId);
+ let defaultInsertionPoint = new PlacesInsertionPoint({ parentId, parentGuid });
+
+ PlacesUIUtils.showBookmarkDialog({ action: "add",
+ type: "bookmark",
+ uri: makeURI(url),
+ title,
+ description,
+ defaultInsertionPoint,
+ hiddenRows: [ "description",
+ "location",
+ "loadInSidebar",
+ "keyword" ]
+ }, window.top);
+ },
+
+ /**
+ * List of nsIURI objects characterizing the tabs currently open in the
+ * browser. The URIs will be in the order in which their
+ * corresponding tabs appeared and duplicates are discarded.
+ */
+ get uniqueCurrentPages() {
+ let seenURIs = {};
+ let URIs = [];
+
+ gBrowser.tabs.forEach(tab => {
+ let browser = tab.linkedBrowser;
+ let uri = browser.currentURI;
+ // contentTitle is usually empty.
+ let title = browser.contentTitle || tab.label;
+ let spec = uri.spec;
+ if (!(spec in seenURIs)) {
+ // add to the set of seen URIs
+ seenURIs[uri.spec] = null;
+ URIs.push({ uri, title });
+ }
+ });
+
+ return URIs;
+ },
+
+ /**
+ * Adds a folder with bookmarks to all of the currently open tabs in this
+ * window.
+ */
+ bookmarkCurrentPages: function PCH_bookmarkCurrentPages() {
+ let pages = this.uniqueCurrentPages;
+ if (pages.length > 1) {
+ PlacesUIUtils.showBookmarkDialog({ action: "add",
+ type: "folder",
+ URIList: pages,
+ hiddenRows: [ "description" ]
+ }, window);
+ }
+ },
+
+ /**
+ * Updates disabled state for the "Bookmark All Tabs" command.
+ */
+ updateBookmarkAllTabsCommand:
+ function PCH_updateBookmarkAllTabsCommand() {
+ // There's nothing to do in non-browser windows.
+ if (window.location.href != getBrowserURL())
+ return;
+
+ // Disable "Bookmark All Tabs" if there are less than two
+ // "unique current pages".
+ goSetCommandEnabled("Browser:BookmarkAllTabs",
+ this.uniqueCurrentPages.length >= 2);
+ },
+
+ /**
+ * Adds a Live Bookmark to a feed associated with the current page.
+ * @param url
+ * The nsIURI of the page the feed was attached to
+ * @title title
+ * The title of the feed. Optional.
+ * @subtitle subtitle
+ * A short description of the feed. Optional.
+ */
+ async addLiveBookmark(url, feedTitle, feedSubtitle) {
+ let toolbarIP = new PlacesInsertionPoint({
+ parentId: PlacesUtils.toolbarFolderId,
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid
+ });
+
+ let feedURI = makeURI(url);
+ let title = feedTitle || gBrowser.contentTitle;
+ let description = feedSubtitle;
+ if (!description) {
+ description = (await this._getPageDetails(gBrowser.selectedBrowser)).description;
+ }
+
+ PlacesUIUtils.showBookmarkDialog({ action: "add",
+ type: "livemark",
+ feedURI,
+ siteURI: gBrowser.currentURI,
+ title,
+ description,
+ defaultInsertionPoint: toolbarIP,
+ hiddenRows: [ "feedLocation",
+ "siteLocation",
+ "description" ]
+ }, window);
+ },
+
+ /**
+ * Opens the Places Organizer.
+ * @param {String} item The item to select in the organizer window,
+ * options are (case sensitive):
+ * BookmarksMenu, BookmarksToolbar, UnfiledBookmarks,
+ * AllBookmarks, History.
+ */
+ showPlacesOrganizer(item) {
+ var organizer = Services.wm.getMostRecentWindow("Places:Organizer");
+ // Due to bug 528706, getMostRecentWindow can return closed windows.
+ if (!organizer || organizer.closed) {
+ // No currently open places window, so open one with the specified mode.
+ openDialog("chrome://communicator/content/places/places.xul",
+ "", "chrome,toolbar=yes,dialog=no,resizable", item);
+ } else {
+ organizer.PlacesOrganizer.selectLeftPaneContainerByHierarchy(item);
+ organizer.focus();
+ }
+ },
+};
+
+/**
+ * Functions for handling events in the Bookmarks Toolbar and menu.
+ */
+var BookmarksEventHandler = {
+
+ onMouseUp(aEvent) {
+ // Handles left-click with modifier if not browser.bookmarks.openInTabClosesMenu.
+ if (aEvent.button != 0 || PlacesUIUtils.openInTabClosesMenu)
+ return;
+ let target = aEvent.originalTarget;
+ if (target.tagName != "menuitem")
+ return;
+ let modifKey = AppConstants.platform === "macosx" ? aEvent.metaKey
+ : aEvent.ctrlKey;
+ // Don't keep menu open for 'Open all in Tabs'.
+ if (modifKey && !target.classList.contains("openintabs-menuitem")) {
+ target.setAttribute("closemenu", "none");
+ }
+ },
+
+ /**
+ * Handler for click event for an item in the bookmarks toolbar or menu.
+ * Menus and submenus from the folder buttons bubble up to this handler.
+ * Left-click is handled in the onCommand function.
+ * When items are middle-clicked (or clicked with modifier), open in tabs.
+ * If the click came through a menu, close the menu.
+ * @param aEvent
+ * DOMEvent for the click
+ * @param aView
+ * The places view which aEvent should be associated with.
+ */
+ onClick: function BEH_onClick(aEvent, aView) {
+ // Only handle middle-click or left-click with modifiers.
+ if (aEvent.button == 2 || (aEvent.button == 0 && !aEvent.shiftKey &&
+ !aEvent.ctrlKey && !aEvent.metaKey))
+ return;
+
+ var target = aEvent.originalTarget;
+ // If this event bubbled up from a menu or menuitem,
+ // close the menus if browser.bookmarks.openInTabClosesMenu.
+ if ((PlacesUIUtils.openInTabClosesMenu && target.tagName == "menuitem") ||
+ target.tagName == "menu" ||
+ target.classList.contains("openintabs-menuitem")) {
+ closeMenus(aEvent.target);
+ }
+ // Command already precesssed so remove any closemenu attr set in onMouseUp.
+ if (aEvent.button == 0 &&
+ target.tagName == "menuitem" &&
+ target.getAttribute("closemenu") == "none") {
+ // On Mac we need to extend when we remove the flag, to avoid any pre-close
+ // animations.
+ setTimeout(() => {
+ target.removeAttribute("closemenu");
+ }, 500);
+ }
+
+ if (target._placesNode && PlacesUtils.nodeIsContainer(target._placesNode)) {
+ // Don't open the root folder in tabs when the empty area on the toolbar
+ // is middle-clicked or when a non-bookmark item except for Open in Tabs)
+ // in a bookmarks menupopup is middle-clicked.
+ if (target.localName == "menu" || target.localName == "toolbarbutton")
+ PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent, aView);
+ }
+ else if (aEvent.button == 1) {
+ // left-clicks with modifier are already served by onCommand
+ this.onCommand(aEvent);
+ }
+ },
+
+ /**
+ * Handler for command event for an item in the bookmarks toolbar.
+ * Menus and submenus from the folder buttons bubble up to this handler.
+ * Opens the item.
+ * @param aEvent
+ * DOMEvent for the command
+ */
+ onCommand: function BEH_onCommand(aEvent) {
+ var target = aEvent.originalTarget;
+ if (target._placesNode)
+ PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent);
+ },
+
+ onPopupShowing: function BEH_onPopupShowing(aEvent) {
+ var browser = getBrowser();
+ if (!aEvent.currentTarget.parentNode._placesView)
+ new PlacesMenu(aEvent, 'place:folder=BOOKMARKS_MENU');
+
+ document.getElementById("Browser:BookmarkAllTabs")
+ .setAttribute("disabled", !browser || browser.tabs.length == 1);
+ },
+
+ fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {
+ var node;
+ var cropped = false;
+ var targetURI;
+
+ if (aDocument.tooltipNode.localName == "treechildren") {
+ var tree = aDocument.tooltipNode.parentNode;
+ var tbo = tree.treeBoxObject;
+ var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
+ if (cell.row == -1)
+ return false;
+ node = tree.view.nodeForTreeIndex(cell.row);
+ cropped = tbo.isCellCropped(cell.row, cell.col);
+ }
+ else {
+ // Check whether the tooltipNode is a Places node.
+ // In such a case use it, otherwise check for targetURI attribute.
+ var tooltipNode = aDocument.tooltipNode;
+ if (tooltipNode._placesNode)
+ node = tooltipNode._placesNode;
+ else {
+ // This is a static non-Places node.
+ targetURI = tooltipNode.getAttribute("targetURI");
+ }
+ }
+
+ if (!node && !targetURI)
+ return false;
+
+ // Show node.label as tooltip's title for non-Places nodes.
+ var title = node ? node.title : tooltipNode.label;
+
+ // Show URL only for Places URI-nodes or nodes with a targetURI attribute.
+ var url;
+ if (targetURI || PlacesUtils.nodeIsURI(node))
+ url = targetURI || node.uri;
+
+ // Show tooltip for containers only if their title is cropped.
+ if (!cropped && !url)
+ return false;
+
+ var tooltipTitle = aDocument.getElementById("bhtTitleText");
+ tooltipTitle.hidden = (!title || (title == url));
+ if (!tooltipTitle.hidden)
+ tooltipTitle.textContent = title;
+
+ var tooltipUrl = aDocument.getElementById("bhtUrlText");
+ tooltipUrl.hidden = !url;
+ if (!tooltipUrl.hidden)
+ tooltipUrl.value = url;
+
+ // Show tooltip.
+ return true;
+ }
+};
+
+
+// Handles special drag and drop functionality for Places menus that are not
+// part of a Places view (e.g. the bookmarks menu in the menubar).
+var PlacesMenuDNDHandler = {
+ _springLoadDelay: 350, // milliseconds
+ _loadTimer: null,
+
+ /**
+ * Called when the user enters the <menu> element during a drag.
+ * @param event
+ * The DragEnter event that spawned the opening.
+ */
+ onDragEnter: function PMDH_onDragEnter(event) {
+ // Opening menus in a Places popup is handled by the view itself.
+ if (!this._isStaticContainer(event.target))
+ return;
+
+ this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ this._loadTimer.initWithCallback(function() {
+ PlacesMenuDNDHandler._loadTimer = null;
+ event.target.lastChild.setAttribute("autoopened", "true");
+ event.target.lastChild.showPopup(event.target.lastChild);
+ }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+ event.preventDefault();
+ event.stopPropagation();
+ },
+
+ /**
+ * Handles dragexit on the <menu> element.
+ * @returns true if the element is a container element (menu or
+ * menu-toolbarbutton), false otherwise.
+ */
+ onDragExit: function PMDH_onDragExit(event) {
+ // Closing menus in a Places popup is handled by the view itself.
+ if (!this._isStaticContainer(event.target))
+ return;
+
+ if (this._loadTimer) {
+ this._loadTimer.cancel();
+ this._loadTimer = null;
+ }
+ let closeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ closeTimer.initWithCallback(function() {
+ let node = PlacesControllerDragHelper.currentDropTarget;
+ let inHierarchy = false;
+ while (node && !inHierarchy) {
+ inHierarchy = node == event.target;
+ node = node.parentNode;
+ }
+ if (!inHierarchy && event.target.lastChild &&
+ event.target.lastChild.hasAttribute("autoopened")) {
+ event.target.lastChild.removeAttribute("autoopened");
+ event.target.lastChild.hidePopup();
+ }
+ }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+
+ /**
+ * Determines if a XUL element represents a static container.
+ * @returns true if the element is a container element (menu or
+ * menu-toolbarbutton), false otherwise.
+ */
+ _isStaticContainer: function PMDH__isContainer(node) {
+ let isMenu = node.localName == "menu" ||
+ (node.localName == "toolbarbutton" &&
+ node.getAttribute("type") == "menu");
+ let isStatic = !("_placesNode" in node) && node.lastChild &&
+ node.lastChild.hasAttribute("placespopup") &&
+ !node.parentNode.hasAttribute("placespopup");
+ return isMenu && isStatic;
+ },
+
+ /**
+ * Called when the user drags over the <menu> element.
+ * @param event
+ * The DragOver event.
+ */
+ onDragOver: function PMDH_onDragOver(event) {
+ let ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.bookmarksMenuFolderId,
+ parentGuid: PlacesUtils.bookmarks.menuGuid
+ });
+ if (ip && PlacesControllerDragHelper.canDrop(ip, event.dataTransfer))
+ event.preventDefault();
+
+ event.stopPropagation();
+ },
+
+ /**
+ * Called when the user drops on the <menu> element.
+ * @param event
+ * The Drop event.
+ */
+ onDrop: function PMDH_onDrop(event) {
+ // Put the item at the end of bookmark menu.
+ let ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.bookmarksMenuFolderId,
+ parentGuid: PlacesUtils.bookmarks.menuGuid
+ });
+ PlacesControllerDragHelper.onDrop(ip, event.dataTransfer);
+ event.stopPropagation();
+ }
+};
+
+
+var BookmarkingUI = {
+ _hasBookmarksObserver: false,
+ _itemGuids: new Set(),
+
+ uninit: function BUI_uninit()
+ {
+ if (this._hasBookmarksObserver) {
+ PlacesUtils.bookmarks.removeObserver(this);
+ }
+
+ if (this._pendingUpdate) {
+ delete this._pendingUpdate;
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsINavBookmarkObserver
+ ]),
+
+ get _starredTooltip()
+ {
+ delete this._starredTooltip;
+ return this._starredTooltip =
+ gNavigatorBundle.getString("starButtonOn.tooltip");
+ },
+
+ get _unstarredTooltip()
+ {
+ delete this._unstarredTooltip;
+ return this._unstarredTooltip =
+ gNavigatorBundle.getString("starButtonOff.tooltip");
+ },
+
+ updateStarState: function BUI_updateStarState() {
+ this._uri = gBrowser.currentURI;
+ this._itemGuids = [];
+ let aItemGuids = [];
+
+ // those objects are use to check if we are in the current iteration before
+ // returning any result.
+ let pendingUpdate = this._pendingUpdate = {};
+
+ PlacesUtils.bookmarks.fetch({url: this._uri}, b => aItemGuids.push(b.guid))
+ .catch(Cu.reportError)
+ .then(() => {
+ if (pendingUpdate != this._pendingUpdate) {
+ return;
+ }
+
+ // It's possible that onItemAdded gets called before the async statement
+ // calls back. For such an edge case, retain all unique entries from the
+ // array.
+ this._itemGuids = this._itemGuids.filter(
+ guid => !aItemGuids.includes(guid)
+ ).concat(aItemGuids);
+
+ this._updateStar();
+
+ // Start observing bookmarks if needed.
+ if (!this._hasBookmarksObserver) {
+ try {
+ PlacesUtils.bookmarks.addObserver(this);
+ this._hasBookmarksObserver = true;
+ } catch (ex) {
+ Cu.reportError("BookmarkingUI failed adding a bookmarks observer: " + ex);
+ }
+ }
+
+ delete this._pendingUpdate;
+ });
+ },
+
+ _updateStar: function BUI__updateStar()
+ {
+ let starIcon = document.getElementById("star-button");
+ if (this._itemGuids.length > 0) {
+ starIcon.setAttribute("starred", "true");
+ starIcon.setAttribute("tooltiptext", this._starredTooltip);
+ }
+ else {
+ starIcon.removeAttribute("starred");
+ starIcon.setAttribute("tooltiptext", this._unstarredTooltip);
+ }
+ },
+
+ onClick: function BUI_onClick(aEvent)
+ {
+ // Ignore clicks on the star while we update its state.
+ if (aEvent.button == 0 && !this._pendingUpdate)
+ PlacesCommandHook.bookmarkPage(gBrowser.selectedBrowser,
+ this._itemGuids.length > 0);
+
+ },
+
+ // nsINavBookmarkObserver
+ onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGuid) {
+ if (aURI && aURI.equals(this._uri)) {
+ // If a new bookmark has been added to the tracked uri, register it.
+ if (!this._itemGuids.includes(aGuid)) {
+ this._itemGuids.push(aGuid);
+ // Only need to update the UI if it wasn't marked as starred before:
+ if (this._itemGuids.length == 1) {
+ this._updateStar();
+ }
+ }
+ }
+ },
+
+ onItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGuid) {
+ let index = this._itemGuids.indexOf(aGuid);
+ // If one of the tracked bookmarks has been removed, unregister it.
+ if (index != -1) {
+ this._itemGuids.splice(index, 1);
+ // Only need to update the UI if the page is no longer starred
+ if (this._itemGuids.length == 0) {
+ this._updateStar();
+ }
+ }
+ },
+
+ onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, aLastModified,
+ aItemType, aParentId, aGuid) {
+ if (aProperty == "uri") {
+ let index = this._itemGuids.indexOf(aGuid);
+ // If the changed bookmark was tracked, check if it is now pointing to
+ // a different uri and unregister it.
+ if (index != -1 && aNewValue != this._uri.spec) {
+ this._itemGuids.splice(index, 1);
+ // Only need to update the UI if the page is no longer starred
+ if (this._itemGuids.length == 0) {
+ this._updateStar();
+ }
+ } else if (index == -1 && aNewValue == this._uri.spec) {
+ // If another bookmark is now pointing to the tracked uri, register it.
+ this._itemGuids.push(aGuid);
+ // Only need to update the UI if it wasn't marked as starred before:
+ if (this._itemGuids.length == 1) {
+ this._updateStar();
+ }
+ }
+ }
+ },
+
+ onBeginUpdateBatch: function () {},
+ onEndUpdateBatch: function () {},
+ onItemVisited: function () {},
+ onItemMoved: function () {}
+};
+
+
+// This object handles the initialization and uninitialization of the bookmarks
+// toolbar. updateStarState is called when the browser window is opened and
+// after closing the toolbar customization dialog.
+var PlacesToolbarHelper = {
+ _place: "place:folder=TOOLBAR",
+ get _viewElt() {
+ return document.getElementById("PlacesToolbar");
+ },
+
+ init: function PTH_init() {
+ let viewElt = this._viewElt;
+ if (!viewElt || viewElt._placesView)
+ return;
+
+ // There is no need to initialize the toolbar if customizing because
+ // init() will be called when the customization is done.
+ if (this._isCustomizing)
+ return;
+
+ new PlacesToolbar(this._place);
+ },
+
+ customizeStart: function PTH_customizeStart() {
+ let viewElt = this._viewElt;
+ if (viewElt && viewElt._placesView)
+ viewElt._placesView.uninit();
+
+ this._isCustomizing = true;
+ },
+
+ customizeDone: function PTH_customizeDone() {
+ this._isCustomizing = false;
+ this.init();
+ }
+};
+
+
+// Handles the bookmarks menu popup
+var BookmarksMenu = {
+ _popupInitialized: {},
+ onPopupShowing: function BM_onPopupShowing(aEvent, aPrefix) {
+ if (!(aPrefix in this._popupInitialized)) {
+ // First popupshowing event, initialize immutable attributes.
+ this._popupInitialized[aPrefix] = true;
+
+ // Need to set the label on Unsorted Bookmarks menu.
+ let unsortedBookmarksElt =
+ document.getElementById(aPrefix + "unsortedBookmarksFolderMenu");
+ unsortedBookmarksElt.label =
+ PlacesUtils.getString("OtherBookmarksFolderTitle");
+ }
+ },
+};
diff --git a/comm/suite/browser/content.js b/comm/suite/browser/content.js
new file mode 100644
index 0000000000..40dd01d7c2
--- /dev/null
+++ b/comm/suite/browser/content.js
@@ -0,0 +1,901 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This content script should work in any browser or iframe and should not
+ * depend on the frame being contained in tabbrowser. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ChromeUtils.defineModuleGetter(this, "LoginManagerContent",
+ "resource://gre/modules/LoginManagerContent.jsm");
+ChromeUtils.defineModuleGetter(this, "InsecurePasswordUtils",
+ "resource://gre/modules/InsecurePasswordUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "LoginFormFactory",
+ "resource://gre/modules/LoginManagerContent.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesUIUtils",
+ "resource:///modules/PlacesUIUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "setTimeout",
+ "resource://gre/modules/Timer.jsm");
+ChromeUtils.defineModuleGetter(this, "Feeds",
+ "resource:///modules/Feeds.jsm");
+ChromeUtils.defineModuleGetter(this, "BrowserUtils",
+ "resource://gre/modules/BrowserUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
+ return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
+});
+XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
+ return Services.strings.createBundle("chrome://pipnss/locale/nsserrors.properties");
+});
+
+addMessageListener("RemoteLogins:fillForm", message => {
+ LoginManagerContent.receiveMessage(message, content);
+});
+
+addEventListener("DOMFormHasPassword", event => {
+ LoginManagerContent.onDOMFormHasPassword(event, content);
+ let formLike = LoginFormFactory.createFromForm(event.target);
+ InsecurePasswordUtils.reportInsecurePasswords(formLike);
+});
+
+addEventListener("DOMInputPasswordAdded", event => {
+ LoginManagerContent.onDOMInputPasswordAdded(event, content);
+ let formLike = LoginFormFactory.createFromField(event.target);
+ InsecurePasswordUtils.reportInsecurePasswords(formLike);
+});
+
+addEventListener("pageshow", event => {
+ LoginManagerContent.onPageShow(event, content);
+}, true);
+
+addEventListener("DOMAutoComplete", event => {
+ LoginManagerContent.onUsernameInput(event);
+});
+
+addEventListener("blur", event => {
+ LoginManagerContent.onUsernameInput(event);
+});
+
+addMessageListener("Bookmarks:GetPageDetails", (message) => {
+ let doc = content.document;
+ let isErrorPage = /^about:(neterror|certerror|blocked)/.test(doc.documentURI);
+ sendAsyncMessage("Bookmarks:GetPageDetails:Result",
+ { isErrorPage,
+ description: PlacesUIUtils.getDescriptionFromDocument(doc) });
+});
+
+/* The following code, in particular AboutCertErrorListener and
+ * AboutNetErrorListener, is mostly copied from content browser.js and content.js.
+ * Certificate error handling should be unified to remove this duplicated code.
+ */
+
+const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
+
+const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11;
+const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
+const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20;
+const SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21;
+const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30;
+const SEC_ERROR_CA_CERT_INVALID = SEC_ERROR_BASE + 36;
+const SEC_ERROR_OCSP_FUTURE_RESPONSE = SEC_ERROR_BASE + 131;
+const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132;
+const SEC_ERROR_REUSED_ISSUER_AND_SERIAL = SEC_ERROR_BASE + 138;
+const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
+const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
+const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;
+const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = MOZILLA_PKIX_ERROR_BASE + 14;
+const MOZILLA_PKIX_ERROR_MITM_DETECTED = MOZILLA_PKIX_ERROR_BASE + 15;
+
+
+const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
+const SSL_ERROR_SSL_DISABLED = SSL_ERROR_BASE + 20;
+const SSL_ERROR_SSL2_DISABLED = SSL_ERROR_BASE + 14;
+
+var AboutNetAndCertErrorListener = {
+ init(chromeGlobal) {
+ addEventListener("AboutNetAndCertErrorLoad", this, false, true);
+ },
+
+ get isNetErrorSite() {
+ return content.document.documentURI.startsWith("about:neterror");
+ },
+
+ get isCertErrorSite() {
+ return content.document.documentURI.startsWith("about:certerror");
+ },
+
+ _getErrorMessageFromCode(securityInfo, doc) {
+ let uri = Services.io.newURI(doc.location);
+ let hostString = uri.host;
+ if (uri.port != 443 && uri.port != -1) {
+ hostString += ":" + uri.port;
+ }
+
+ let id_str = "";
+ switch (securityInfo.errorCode) {
+ case SSL_ERROR_SSL_DISABLED:
+ id_str = "PSMERR_SSL_Disabled";
+ break;
+ case SSL_ERROR_SSL2_DISABLED:
+ id_str = "PSMERR_SSL2_Disabled";
+ break;
+ case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
+ id_str = "PSMERR_HostReusedIssuerSerial";
+ break;
+ }
+ let nss_error_id_str = securityInfo.errorCodeString;
+ let msg2 = "";
+ if (id_str) {
+ msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
+ } else if (nss_error_id_str) {
+ msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
+ }
+
+ if (!msg2) {
+ // We couldn't get an error message. Use the error string.
+ // Note that this is different from before where we used PR_ErrorToString.
+ msg2 = nss_error_id_str;
+ }
+ let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
+ [hostString, msg2], 2);
+
+ if (nss_error_id_str) {
+ msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
+ [nss_error_id_str], 1);
+ }
+ let id = content.document.getElementById("errorShortDescText");
+ id.textContent = msg;
+ id.className = "wrap";
+ },
+
+ _setTechDetails(sslStatus, securityInfo, location) {
+ if (!securityInfo || !sslStatus || !location) {
+ return;
+ }
+ let validity = sslStatus.serverCert.validity;
+
+ let doc = content.document;
+ // CSS class and error code are set from nsDocShell.
+ let searchParams = new URLSearchParams(doc.documentURI.split("?")[1]);
+ let cssClass = searchParams.get("s");
+ let error = searchParams.get("e");
+ let technicalInfo = doc.getElementById("technicalContentText");
+
+ let uri = Services.io.newURI(location);
+ let hostString = uri.host;
+ if (uri.port != 443 && uri.port != -1) {
+ hostString += ":" + uri.port;
+ }
+
+ let msg = gPipNSSBundle.formatStringFromName("certErrorIntro",
+ [hostString], 1);
+ msg += "\n\n";
+
+ if (sslStatus.isUntrusted) {
+ switch (securityInfo.errorCode) {
+ case MOZILLA_PKIX_ERROR_MITM_DETECTED:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_MitM") + "\n";
+ break;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") + "\n";
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") + "\n";
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") + "\n";
+ break;
+ case SEC_ERROR_CA_CERT_INVALID:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_CaInvalid") + "\n";
+ break;
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_Issuer") + "\n";
+ break;
+ case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_SignatureAlgorithmDisabled") + "\n";
+ break;
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_ExpiredIssuer") + "\n";
+ break;
+ case MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_SelfSigned") + "\n";
+ break;
+ case SEC_ERROR_UNTRUSTED_CERT:
+ default:
+ msg += gPipNSSBundle.GetStringFromName("certErrorTrust_Untrusted") + "\n";
+ }
+ }
+
+ technicalInfo.appendChild(doc.createTextNode(msg));
+
+ if (sslStatus.isDomainMismatch) {
+ let subjectAltNamesList = sslStatus.serverCert.subjectAltNames;
+ let subjectAltNames = subjectAltNamesList.split(",");
+ let numSubjectAltNames = subjectAltNames.length;
+ if (numSubjectAltNames != 0) {
+ if (numSubjectAltNames == 1) {
+ // Let's check if we want to make this a link.
+ let okHost = subjectAltNamesList;
+ let href = "";
+ let thisHost = doc.location.hostname;
+ let proto = doc.location.protocol + "//";
+ // If okHost is a wildcard domain ("*.example.com") let's
+ // use "www" instead. "*.example.com" isn't going to
+ // get anyone anywhere useful. bug 432491
+ okHost = okHost.replace(/^\*\./, "www.");
+ /* case #1:
+ * example.com uses an invalid security certificate.
+ *
+ * The certificate is only valid for www.example.com
+ *
+ * Make sure to include the "." ahead of thisHost so that
+ * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
+ *
+ * We'd normally just use a RegExp here except that we lack a
+ * library function to escape them properly (bug 248062), and
+ * domain names are famous for having '.' characters in them,
+ * which would allow spurious and possibly hostile matches.
+ */
+ if (okHost.endsWith("." + thisHost)) {
+ href = proto + okHost;
+ }
+ /* case #2:
+ * browser.garage.maemo.org uses an invalid security certificate.
+ *
+ * The certificate is only valid for garage.maemo.org
+ */
+ if (thisHost.endsWith("." + okHost)) {
+ href = proto + okHost;
+ }
+
+ // If we set a link, meaning there's something helpful for
+ // the user here, expand the section by default
+ if (href && cssClass != "expertBadCert") {
+ doc.getElementById("technicalContentText").style.display = "block";
+ }
+
+ let msgPrefix =
+ gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
+
+ // Set the link if we want it.
+ if (href) {
+ let referrerlink = doc.createElement("a");
+ referrerlink.append(subjectAltNamesList + "\n");
+ referrerlink.title = subjectAltNamesList;
+ referrerlink.id = "cert_domain_link";
+ referrerlink.href = href;
+ msg = BrowserUtils.getLocalizedFragment(doc, msgPrefix,
+ referrerlink);
+ } else {
+ msg = BrowserUtils.getLocalizedFragment(doc, msgPrefix,
+ subjectAltNamesList);
+ }
+ } else {
+ msg = gPipNSSBundle.GetStringFromName("certErrorMismatchMultiple") + "\n";
+ for (let i = 0; i < numSubjectAltNames; i++) {
+ msg += subjectAltNames[i];
+ if (i != (numSubjectAltNames - 1)) {
+ msg += ", ";
+ }
+ }
+ }
+ } else {
+ msg = gPipNSSBundle.formatStringFromName("certErrorMismatch",
+ [hostString], 1);
+ }
+ technicalInfo.append(msg + "\n");
+ }
+
+ if (sslStatus.isNotValidAtThisTime) {
+ let nowTime = new Date().getTime() * 1000;
+ let dateOptions = { year: "numeric", month: "long", day: "numeric",
+ hour: "numeric", minute: "numeric" };
+ let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
+ if (validity.notBefore) {
+ if (nowTime > validity.notAfter) {
+ msg = gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
+ [validity.notAfterLocalTime, now], 2) + "\n";
+ } else {
+ msg = gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow",
+ [validity.notBeforeLocalTime, now], 2) + "\n";
+ }
+ } else {
+ // If something goes wrong, we assume the cert expired.
+ msg = gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
+ ["", now], 2) + "\n";
+ }
+ technicalInfo.append(msg);
+ }
+ technicalInfo.append("\n");
+
+ // Add link to certificate and error message.
+ msg = gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
+ [securityInfo.errorCodeString], 1);
+ technicalInfo.append(msg);
+ },
+
+ handleEvent(aEvent) {
+ if (!this.isNetErrorSite && !this.isCertErrorSite) {
+ return;
+ }
+
+ if (aEvent.type != "AboutNetAndCertErrorLoad") {
+ return;
+ }
+
+ if (this.isNetErrorSite) {
+ let {securityInfo} = docShell.failedChannel;
+ // We don't have a securityInfo when this is for example a DNS error.
+ if (securityInfo) {
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ this._getErrorMessageFromCode(securityInfo,
+ aEvent.originalTarget.ownerGlobal);
+ }
+ return;
+ }
+
+ let ownerDoc = aEvent.originalTarget.ownerGlobal;
+ let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
+ .QueryInterface(Ci.nsISerializable);
+ let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+ .SSLStatus;
+ this._setTechDetails(sslStatus, securityInfo, ownerDoc.location.href);
+ },
+};
+AboutNetAndCertErrorListener.init();
+
+const MathMLNS = "http://www.w3.org/1998/Math/MathML";
+const XLinkNS = "http://www.w3.org/1999/xlink";
+
+let PageInfoListener = {
+
+ init: function() {
+ addMessageListener("PageInfo:getData", this);
+ },
+
+ receiveMessage: function(message) {
+ let strings = message.data.strings;
+ let window;
+ let document;
+
+ let frameOuterWindowID = message.data.frameOuterWindowID;
+
+ // If inside frame then get the frame's window and document.
+ if (frameOuterWindowID) {
+ window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
+ document = window.document;
+ }
+ else {
+ document = content.document;
+ window = content.window;
+ }
+
+ let imageElement = message.objects.imageElement;
+
+ let pageInfoData = {metaViewRows: this.getMetaInfo(document),
+ docInfo: this.getDocumentInfo(document),
+ feeds: this.getFeedsInfo(document, strings),
+ windowInfo: this.getWindowInfo(window),
+ imageInfo: this.getImageInfo(imageElement)};
+
+ sendAsyncMessage("PageInfo:data", pageInfoData);
+
+ // Separate step so page info dialog isn't blank while waiting for this
+ // to finish.
+ this.getMediaInfo(document, window, strings);
+ },
+
+ getImageInfo: function(imageElement) {
+ let imageInfo = null;
+ if (imageElement) {
+ imageInfo = {
+ currentSrc: imageElement.currentSrc,
+ width: imageElement.width,
+ height: imageElement.height,
+ imageText: imageElement.title || imageElement.alt
+ };
+ }
+ return imageInfo;
+ },
+
+ getMetaInfo: function(document) {
+ let metaViewRows = [];
+
+ // Get the meta tags from the page.
+ let metaNodes = document.getElementsByTagName("meta");
+
+ for (let metaNode of metaNodes) {
+ metaViewRows.push([metaNode.name || metaNode.httpEquiv ||
+ metaNode.getAttribute("property"),
+ metaNode.content]);
+ }
+
+ return metaViewRows;
+ },
+
+ getWindowInfo: function(window) {
+ let windowInfo = {};
+ windowInfo.isTopWindow = window == window.top;
+
+ let hostName = null;
+ try {
+ hostName = window.location.host;
+ }
+ catch (exception) { }
+
+ windowInfo.hostName = hostName;
+ return windowInfo;
+ },
+
+ getDocumentInfo: function(document) {
+ let docInfo = {};
+ docInfo.title = document.title;
+ docInfo.location = document.location.toString();
+ docInfo.referrer = document.referrer;
+ docInfo.compatMode = document.compatMode;
+ docInfo.contentType = document.contentType;
+ docInfo.characterSet = document.characterSet;
+ docInfo.lastModified = document.lastModified;
+ docInfo.principal = document.nodePrincipal;
+
+ let documentURIObject = {};
+ documentURIObject.spec = document.documentURIObject.spec;
+ docInfo.documentURIObject = documentURIObject;
+
+ docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content);
+
+ return docInfo;
+ },
+
+ getFeedsInfo: function(document, strings) {
+ let feeds = [];
+ // Get the feeds from the page.
+ let linkNodes = document.getElementsByTagName("link");
+ let length = linkNodes.length;
+ for (let i = 0; i < length; i++) {
+ let link = linkNodes[i];
+ if (!link.href) {
+ continue;
+ }
+ let rel = link.rel && link.rel.toLowerCase();
+ let rels = {};
+
+ if (rel) {
+ for (let relVal of rel.split(/\s+/)) {
+ rels[relVal] = true;
+ }
+ }
+
+ if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
+ let type = Feeds.isValidFeed(link, document.nodePrincipal, "feed" in rels);
+ if (type) {
+ type = strings[type] || strings["application/rss+xml"];
+ feeds.push([link.title, type, link.href]);
+ }
+ }
+ }
+ return feeds;
+ },
+
+ // Only called once to get the media tab's media elements from the content
+ // page.
+ getMediaInfo: function(document, window, strings)
+ {
+ let frameList = this.goThroughFrames(document, window);
+ this.processFrames(document, frameList, strings);
+ },
+
+ goThroughFrames: function(document, window)
+ {
+ let frameList = [document];
+ if (window && window.frames.length > 0) {
+ let num = window.frames.length;
+ for (let i = 0; i < num; i++) {
+ // Recurse through the frames.
+ frameList =
+ frameList.concat(this.goThroughFrames(window.frames[i].document,
+ window.frames[i]));
+ }
+ }
+ return frameList;
+ },
+
+ async processFrames(document, frameList, strings)
+ {
+ let nodeCount = 0;
+ for (let doc of frameList) {
+ let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT);
+
+ // Goes through all the elements on the doc.
+ while (iterator.nextNode()) {
+ this.getMediaItems(document, strings, iterator.currentNode);
+
+ if (++nodeCount % 500 == 0) {
+ // setTimeout every 500 elements so we don't keep blocking the
+ // content process.
+ await new Promise(resolve => setTimeout(resolve, 10));
+ }
+ }
+ }
+ // Send that page info media fetching has finished.
+ sendAsyncMessage("PageInfo:mediaData", {isComplete: true});
+ },
+
+ getMediaItems: function(document, strings, elem)
+ {
+ // Check for images defined in CSS (e.g. background, borders).
+ let computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
+ // A node can have multiple media items associated with it - for example,
+ // multiple background images.
+ let imageItems = [];
+ let formItems = [];
+ let linkItems = [];
+
+ let addImage = (url, type, alt, elem, isBg) => {
+ let element = this.serializeElementInfo(document, url, type, alt, elem, isBg);
+ imageItems.push([url, type, alt, element, isBg]);
+ };
+
+ if (computedStyle) {
+ let addImgFunc = (label, val) => {
+ if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) {
+ addImage(val.getStringValue(), label, strings.notSet, elem, true);
+ }
+ else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) {
+ // This is for -moz-image-rect.
+ // TODO: Reimplement once bug 714757 is fixed.
+ let strVal = val.getStringValue();
+ if (strVal.search(/^.*url\(\"?/) > -1) {
+ let url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
+ addImage(url, label, strings.notSet, elem, true);
+ }
+ }
+ else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) {
+ // Recursively resolve multiple nested CSS value lists.
+ for (let i = 0; i < val.length; i++) {
+ addImgFunc(label, val.item(i));
+ }
+ }
+ };
+
+ addImgFunc(strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
+ addImgFunc(strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
+ addImgFunc(strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
+ addImgFunc(strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
+ }
+
+ let addForm = (elem) => {
+ let element = this.serializeFormInfo(document, elem, strings);
+ formItems.push([elem.name, elem.method, elem.action, element]);
+ };
+
+ // One swi^H^H^Hif-else to rule them all.
+ if (elem instanceof content.HTMLAnchorElement) {
+ linkItems.push([this.getValueText(elem), elem.href, strings.linkAnchor,
+ elem.target, elem.accessKey]);
+ }
+ else if (elem instanceof content.HTMLImageElement) {
+ addImage(elem.src, strings.mediaImg,
+ (elem.hasAttribute("alt")) ? elem.alt : strings.notSet,
+ elem, false);
+ }
+ else if (elem instanceof content.HTMLAreaElement) {
+ linkItems.push([elem.alt, elem.href, strings.linkArea, elem.target, ""]);
+ }
+ else if (elem instanceof content.HTMLVideoElement) {
+ addImage(elem.currentSrc, strings.mediaVideo, "", elem, false);
+ }
+ else if (elem instanceof content.HTMLAudioElement) {
+ addImage(elem.currentSrc, strings.mediaAudio, "", elem, false);
+ }
+ else if (elem instanceof content.HTMLLinkElement) {
+ if (elem.rel) {
+ let rel = elem.rel;
+ if (/\bicon\b/i.test(rel)) {
+ addImage(elem.href, strings.mediaLink, "", elem, false);
+ }
+ else if (/(?:^|\s)stylesheet(?:\s|$)/i.test(rel)) {
+ linkItems.push([elem.rel, elem.href, strings.linkStylesheet,
+ elem.target, ""]);
+ }
+ else {
+ linkItems.push([elem.rel, elem.href, strings.linkRel,
+ elem.target, ""]);
+ }
+ }
+ else {
+ linkItems.push([elem.rev, elem.href, strings.linkRev, elem.target, ""]);
+ }
+ }
+ else if (elem instanceof content.HTMLInputElement ||
+ elem instanceof content.HTMLButtonElement) {
+ switch (elem.type.toLowerCase()) {
+ case "image":
+ addImage(elem.src, strings.mediaInput,
+ (elem.hasAttribute("alt")) ? elem.alt : strings.notSet,
+ elem, false);
+ // Fall through, <input type="image"> submits, too
+ case "submit":
+ if ("form" in elem && elem.form) {
+ linkItems.push([elem.value || this.getValueText(elem) ||
+ strings.linkSubmit, elem.form.action,
+ strings.linkSubmission, elem.form.target, ""]);
+ }
+ else {
+ linkItems.push([elem.value || this.getValueText(elem) ||
+ strings.linkSubmit, "",
+ strings.linkSubmission, "", ""]);
+ }
+ }
+ }
+ else if (elem instanceof content.HTMLFormElement) {
+ addForm(elem);
+ }
+ else if (elem instanceof content.HTMLObjectElement) {
+ addImage(elem.data, strings.mediaObject, this.getValueText(elem),
+ elem, false);
+ }
+ else if (elem instanceof content.HTMLEmbedElement) {
+ addImage(elem.src, strings.mediaEmbed, "", elem, false);
+ }
+ else if (elem.namespaceURI == MathMLNS && elem.hasAttribute("href")) {
+ let href = elem.getAttribute("href");
+ try {
+ href = makeURLAbsolute(elem.baseURI, href,
+ elem.ownerDocument.characterSet);
+ } catch (e) {}
+ linkItems.push([this.getValueText(elem), href, strings.linkX, "", ""]);
+ }
+ else if (elem.hasAttributeNS(XLinkNS, "href")) {
+ let href = elem.getAttributeNS(XLinkNS, "href");
+ try {
+ href = makeURLAbsolute(elem.baseURI, href,
+ elem.ownerDocument.characterSet);
+ } catch (e) {}
+ // SVG images without an xlink:href attribute are ignored
+ if (elem instanceof content.SVGImageElement) {
+ addImage(href, strings.mediaImg, "", elem, false);
+ }
+ else {
+ linkItems.push([this.getValueText(elem), href, strings.linkX, "", ""]);
+ }
+ }
+ else if (elem instanceof content.HTMLScriptElement) {
+ linkItems.push([elem.type || elem.getAttribute("language") ||
+ strings.notSet, elem.src || strings.linkScriptInline,
+ strings.linkScript, "", "", ""]);
+ }
+ if (imageItems.length || formItems.length || linkItems.length) {
+ sendAsyncMessage("PageInfo:mediaData",
+ {imageItems, formItems, linkItems, isComplete: false});
+ }
+ },
+
+ /**
+ * Set up a JSON element object with all the instanceOf and other infomation
+ * that makePreview in pageInfo.js uses to figure out how to display the
+ * preview.
+ */
+
+ serializeElementInfo: function(document, url, type, alt, item, isBG)
+ {
+ let result = {};
+
+ let imageText;
+ if (!isBG &&
+ !(item instanceof content.SVGImageElement) &&
+ !(document instanceof content.ImageDocument)) {
+ imageText = item.title || item.alt;
+
+ if (!imageText && !(item instanceof content.HTMLImageElement)) {
+ imageText = this.getValueText(item);
+ }
+ }
+
+ result.imageText = imageText;
+ result.longDesc = item.longDesc;
+ result.numFrames = 1;
+
+ if (item instanceof content.HTMLObjectElement ||
+ item instanceof content.HTMLEmbedElement ||
+ item instanceof content.HTMLLinkElement) {
+ result.mimeType = item.type;
+ }
+
+ if (!result.mimeType && !isBG &&
+ item instanceof Ci.nsIImageLoadingContent) {
+ let imageRequest =
+ item.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (imageRequest) {
+ result.mimeType = imageRequest.mimeType;
+ let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) &&
+ imageRequest.image;
+ if (image) {
+ result.numFrames = image.numFrames;
+ }
+ }
+ }
+
+ // If we have a data url, get the MIME type from the url.
+ if (!result.mimeType && url.startsWith("data:")) {
+ let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
+ if (dataMimeType)
+ result.mimeType = dataMimeType[1].toLowerCase();
+ }
+
+ result.HTMLLinkElement = item instanceof content.HTMLLinkElement;
+ result.HTMLInputElement = item instanceof content.HTMLInputElement;
+ result.HTMLImageElement = item instanceof content.HTMLImageElement;
+ result.HTMLObjectElement = item instanceof content.HTMLObjectElement;
+ result.HTMLEmbedElement = item instanceof content.HTMLEmbedElement;
+ result.SVGImageElement = item instanceof content.SVGImageElement;
+ result.HTMLVideoElement = item instanceof content.HTMLVideoElement;
+ result.HTMLAudioElement = item instanceof content.HTMLAudioElement;
+
+ if (isBG) {
+ // Items that are showing this image as a background
+ // image might not necessarily have a width or height,
+ // so we'll dynamically generate an image and send up the
+ // natural dimensions.
+ let img = content.document.createElement("img");
+ img.src = url;
+ result.naturalWidth = img.naturalWidth;
+ result.naturalHeight = img.naturalHeight;
+ } else {
+ // Otherwise, we can use the current width and height
+ // of the image.
+ result.width = item.width;
+ result.height = item.height;
+ }
+
+ if (item instanceof content.SVGImageElement) {
+ result.SVGImageElementWidth = item.width.baseVal.value;
+ result.SVGImageElementHeight = item.height.baseVal.value;
+ }
+
+ result.baseURI = item.baseURI;
+
+ return result;
+ },
+
+ serializeFormInfo: function(document, form, strings)
+ {
+ let result = {};
+
+ if (form.name)
+ result.name = form.name;
+
+ result.encoding = form.encoding;
+ result.target = form.target;
+ result.formfields = [];
+
+ function findFirstControl(node, document) {
+ function FormControlFilter(node) {
+ if (node instanceof content.HTMLInputElement ||
+ node instanceof content.HTMLSelectElement ||
+ node instanceof content.HTMLButtonElement ||
+ node instanceof content.HTMLTextAreaElement ||
+ node instanceof content.HTMLObjectElement)
+ return content.NodeFilter.FILTER_ACCEPT;
+ return content.NodeFilter.FILTER_SKIP;
+ }
+
+ if (node.hasAttribute("for")) {
+ return document.getElementById(node.getAttribute("for"));
+ }
+
+ var iterator = document.createTreeWalker(node, content.NodeFilter.SHOW_ELEMENT, FormControlFilter, true);
+
+ return iterator.nextNode();
+ }
+
+ var whatfor;
+ var labels = [];
+ for (let label of form.getElementsByTagName("label")) {
+ var whatfor = findFirstControl(label, document);
+
+ if (whatfor && (whatfor.form == form)) {
+ labels.push({label: whatfor, labeltext: this.getValueText(label)});
+ }
+ }
+
+ result.formfields = [];
+
+ var val;
+ for (let formfield of form.elements) {
+ if (formfield instanceof content.HTMLButtonElement)
+ val = this.getValueText(formfield);
+ else
+ val = (/^password$/i.test(formfield.type)) ? strings.formPassword : formfield.value;
+
+ var fieldlabel = "";
+ for (let labelfor of labels) {
+ if (formfield == labelfor.label) {
+ fieldlabel = labelfor.labeltext;
+ }
+ }
+ result.formfields.push([fieldlabel, formfield.name, formfield.type, val]);
+ }
+
+ return result;
+ },
+
+ //******** Other Misc Stuff
+ // Modified from the Links Panel v2.3,
+ // http://segment7.net/mozilla/links/links.html
+ // parse a node to extract the contents of the node
+ getValueText: function(node)
+ {
+
+ let valueText = "";
+
+ // Form input elements don't generally contain information that is useful
+ // to our callers, so return nothing.
+ if (node instanceof content.HTMLInputElement ||
+ node instanceof content.HTMLSelectElement ||
+ node instanceof content.HTMLTextAreaElement) {
+ return valueText;
+ }
+
+ // Otherwise recurse for each child.
+ let length = node.childNodes.length;
+
+ for (let i = 0; i < length; i++) {
+ let childNode = node.childNodes[i];
+ let nodeType = childNode.nodeType;
+
+ // Text nodes are where the goods are.
+ if (nodeType == content.Node.TEXT_NODE) {
+ valueText += " " + childNode.nodeValue;
+ }
+ // And elements can have more text inside them.
+ else if (nodeType == content.Node.ELEMENT_NODE) {
+ // Images are special, we want to capture the alt text as if the image
+ // weren't there.
+ if (childNode instanceof content.HTMLImageElement) {
+ valueText += " " + this.getAltText(childNode);
+ }
+ else {
+ valueText += " " + this.getValueText(childNode);
+ }
+ }
+ }
+
+ return this.stripWS(valueText);
+ },
+
+ // Copied from the Links Panel v2.3,
+ // http://segment7.net/mozilla/links/links.html.
+ // Traverse the tree in search of an img or area element and grab its alt tag.
+ getAltText: function(node)
+ {
+ let altText = "";
+
+ if (node.alt) {
+ return node.alt;
+ }
+ let length = node.childNodes.length;
+ for (let i = 0; i < length; i++) {
+ if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
+ return altText;
+ }
+ }
+ return "";
+ },
+
+ // Copied from the Links Panel v2.3,
+ // http://segment7.net/mozilla/links/links.html.
+ // Strip leading and trailing whitespace, and replace multiple consecutive
+ // whitespace characters with a single space.
+ stripWS: function(text)
+ {
+ let middleRE = /\s+/g;
+ let endRE = /(^\s+)|(\s+$)/g;
+
+ text = text.replace(middleRE, " ");
+ return text.replace(endRE, "");
+ }
+};
+PageInfoListener.init();
diff --git a/comm/suite/browser/fullScreen.js b/comm/suite/browser/fullScreen.js
new file mode 100644
index 0000000000..30a666cf57
--- /dev/null
+++ b/comm/suite/browser/fullScreen.js
@@ -0,0 +1,107 @@
+/* -*- 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/. */
+
+var FullScreen =
+{
+ toggle: function()
+ {
+ var show = !window.fullScreen;
+ // show/hide all menubars, toolbars, and statusbars (except the full screen toolbar)
+ this.showXULChrome("toolbar", show);
+ this.showXULChrome("statusbar", show);
+
+ var toolbox = getNavToolbox();
+ if (show)
+ toolbox.removeAttribute("inFullscreen");
+ else
+ toolbox.setAttribute("inFullscreen", true);
+
+ var controls = document.getElementsByAttribute("fullscreencontrol", "true");
+ for (let i = 0; i < controls.length; ++i)
+ controls[i].hidden = show;
+
+ controls = document.getElementsByAttribute("domfullscreenhidden", "true");
+ if (document.mozFullScreen) {
+ for (let i = 0; i < controls.length; ++i)
+ controls[i].setAttribute("moz-collapsed", "true");
+ getBrowser().mStrip.setAttribute("moz-collapsed", "true");
+ } else {
+ for (let i = 0; i < controls.length; ++i)
+ controls[i].removeAttribute("moz-collapsed");
+ getBrowser().mStrip.removeAttribute("moz-collapsed");
+ }
+ getBrowser().getNotificationBox().notificationsHidden = document.mozFullScreen;
+ },
+
+ showXULChrome: function(aTag, aShow)
+ {
+ var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var els = document.getElementsByTagNameNS(XULNS, aTag);
+
+ var i;
+ for (i = 0; i < els.length; ++i) {
+ // XXX don't interfere with previously collapsed toolbars
+ if (els[i].getAttribute("fullscreentoolbar") == "true" &&
+ !document.mozFullScreen) {
+ if (!aShow) {
+ var toolbarMode = els[i].getAttribute("mode");
+ if (toolbarMode != "text") {
+ els[i].setAttribute("saved-mode", toolbarMode);
+ els[i].setAttribute("saved-iconsize",
+ els[i].getAttribute("iconsize"));
+ els[i].setAttribute("mode", "icons");
+ els[i].setAttribute("iconsize", "small");
+ }
+
+ // XXX See bug 202978: we disable the context menu
+ // to prevent customization while in fullscreen, which
+ // causes menu breakage.
+ els[i].setAttribute("saved-context",
+ els[i].getAttribute("context"));
+ els[i].removeAttribute("context");
+
+ // Set the inFullscreen attribute to allow specific styling
+ // in fullscreen mode
+ els[i].setAttribute("inFullscreen", true);
+ }
+ else {
+ this.restoreAttribute(els[i], "mode");
+ this.restoreAttribute(els[i], "iconsize");
+ this.restoreAttribute(els[i], "context"); // XXX see above
+
+ els[i].removeAttribute("inFullscreen");
+ els[i].removeAttribute("moz-collapsed");
+ }
+ } else if (els[i].getAttribute("type") == "menubar") {
+ if (aShow) {
+ this.restoreAttribute(els[i], "autohide");
+ }
+ else {
+ els[i].setAttribute("saved-autohide",
+ els[i].getAttribute("autohide"));
+ els[i].setAttribute("autohide", "true");
+ }
+ } else {
+ // use moz-collapsed so it doesn't persist hidden/collapsed,
+ // so that new windows don't have missing toolbars
+ if (aShow)
+ els[i].removeAttribute("moz-collapsed");
+ else
+ els[i].setAttribute("moz-collapsed", "true");
+ }
+ }
+ },
+
+ restoreAttribute: function(element, attributeName)
+ {
+ var savedAttribute = "saved-" + attributeName;
+ if (element.hasAttribute(savedAttribute)) {
+ var savedValue = element.getAttribute(savedAttribute);
+ element.setAttribute(attributeName, savedValue);
+ element.removeAttribute(savedAttribute);
+ }
+ }
+
+};
diff --git a/comm/suite/browser/hiddenWindow.xul b/comm/suite/browser/hiddenWindow.xul
new file mode 100644
index 0000000000..ac769bf12c
--- /dev/null
+++ b/comm/suite/browser/hiddenWindow.xul
@@ -0,0 +1,55 @@
+<?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/. -->
+
+<?xul-overlay href="chrome://navigator/content/navigatorOverlay.xul"?>
+
+<!-- hiddenwindow is a "minimal" XUL window intended for creating the,
+ er, hidden window. This window is never shown, but on platforms
+ which leave the app running after the last (visible) window is shut
+ down, this window does hold a browser menubar.
+ Though this window looks a lot like navigator.xul, that xul
+ is unsuitable because it's subject to the whims of its associated
+ appcore, which among other things causes it to load content documents
+ undesirable for this window.
+ Arguably a simpler menu structure could be substituted, but
+ the full one was included for now in anticipation of the whole thing
+ becoming an included file someday. -->
+
+<!-- Localizable string definitions from navigator.xul. -->
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % navigatorDTD SYSTEM "chrome://navigator/locale/navigator.dtd" >
+%navigatorDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ title="hidden"
+ titlemodifier="&mainWindow.titlemodifier;"
+ titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
+ onload="hiddenWindowStartup();"
+ onunload="Shutdown();">
+
+ <stringbundleset id="stringbundleset"/>
+
+ <!-- keys are appended from the overlay -->
+ <keyset id="navKeys"/>
+
+ <!-- commands are appended from the overlay -->
+ <commandset id="commands"/>
+
+ <broadcasterset id="navBroadcasters"/>
+
+ <!-- it's the whole navigator.xul menubar! hidden windows need to
+ have a menubar for situations where they're the only window remaining
+ on a platform that wants to leave the app running, like the Mac.
+ -->
+ <toolbox id="toolbox">
+ <menubar id="main-menubar" position="1"/>
+ </toolbox>
+
+</window>
diff --git a/comm/suite/browser/jar.mn b/comm/suite/browser/jar.mn
new file mode 100644
index 0000000000..8034462f4c
--- /dev/null
+++ b/comm/suite/browser/jar.mn
@@ -0,0 +1,41 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+% content navigator %content/navigator/
+% content navigator-region %content/navigator-region/
+ content/navigator/browser-places.js
+ content/navigator/content.js
+ content/navigator/fullScreen.js
+ content/navigator/hiddenWindow.xul
+ content/navigator/linkToolbarHandler.js
+ content/navigator/linkToolbarItem.js
+ content/navigator/linkToolbarOverlay.js
+ content/navigator/linkToolbarOverlay.xul
+ content/navigator/mailNavigatorOverlay.js
+* content/navigator/mailNavigatorOverlay.xul
+ content/navigator/metadata.js
+ content/navigator/metadata.xul
+ content/navigator/navigator.css
+ content/navigator/navigator.js
+ content/navigator/navigator.xul
+ content/navigator/navigatorDD.js
+* content/navigator/navigatorOverlay.xul
+ content/navigator/nsBrowserContentListener.js
+ content/navigator/nsBrowserStatusHandler.js
+ content/navigator/sessionHistoryUI.js
+ content/navigator/safeBrowsingOverlay.js
+ content/navigator/safeBrowsingOverlay.xul
+ content/navigator/tabbrowser.xml
+ content/navigator/urlbarBindings.xml
+ content/navigator/webDeveloperOverlay.js
+ content/navigator/webDeveloperOverlay.xul
+
+ content/navigator/pageinfo/feeds.js (pageinfo/feeds.js)
+ content/navigator/pageinfo/feeds.xml (pageinfo/feeds.xml)
+ content/navigator/pageinfo/pageInfo.css (pageinfo/pageInfo.css)
+ content/navigator/pageinfo/pageInfo.js (pageinfo/pageInfo.js)
+ content/navigator/pageinfo/pageInfo.xul (pageinfo/pageInfo.xul)
+ content/navigator/pageinfo/permissions.js (pageinfo/permissions.js)
+ content/navigator/pageinfo/security.js (pageinfo/security.js)
diff --git a/comm/suite/browser/linkToolbarHandler.js b/comm/suite/browser/linkToolbarHandler.js
new file mode 100644
index 0000000000..eb3e0cfb38
--- /dev/null
+++ b/comm/suite/browser/linkToolbarHandler.js
@@ -0,0 +1,295 @@
+/* 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/. */
+
+ChromeUtils.defineModuleGetter(this, "Feeds",
+ "resource:///modules/Feeds.jsm");
+
+/**
+ * LinkToolbarHandler is a Singleton that displays LINK elements
+ * and nodeLists of LINK elements in the Link Toolbar. It
+ * associates the LINK with a corresponding LinkToolbarItem based
+ * on it's REL attribute and the toolbar item's ID attribute.
+ * LinkToolbarHandler is also a Factory and will create
+ * LinkToolbarItems as necessary.
+ */
+function LinkToolbarHandler()
+{
+ this.items = new Array();
+ this.hasItems = false;
+}
+
+LinkToolbarHandler.prototype.handle =
+function(element)
+{
+ // XXX: if you're going to re-enable handling of anchor elements,
+ // you'll want to change this to AnchorElementDecorator
+ var linkElement = new LinkElementDecorator(element);
+
+ if (linkElement.isIgnored()) return;
+
+ for (var i = 0; i < linkElement.relValues.length; i++) {
+ // Skip "alternate" when we have e.g. "alternate XXX".
+ if (linkElement.relValues.length > 1 &&
+ linkElement.relValues[i] == "alternate")
+ continue;
+
+ var linkType = LinkToolbarHandler.getLinkType(linkElement.relValues[i], element);
+ if (linkType) {
+ if (!this.hasItems) {
+ this.hasItems = true;
+ linkToolbarUI.activate();
+ }
+ this.getItemForLinkType(linkType).displayLink(linkElement);
+ }
+ }
+}
+
+LinkToolbarHandler.getLinkType =
+function(relAttribute, element)
+{
+ var isFeed = false;
+ switch (relAttribute.toLowerCase()) {
+ case "top":
+ case "origin":
+ return "top";
+
+ case "up":
+ case "parent":
+ return "up";
+
+ case "start":
+ case "begin":
+ case "first":
+ return "first";
+
+ case "next":
+ case "child":
+ return "next";
+
+ case "prev":
+ case "previous":
+ return "prev";
+
+ case "end":
+ case "last":
+ return "last";
+
+ case "author":
+ case "made":
+ return "author";
+
+ case "contents":
+ case "toc":
+ return "toc";
+
+ case "feed":
+ isFeed = true;
+ // fall through
+ case "alternate":
+ if (Feeds.isValidFeed(element, element.nodePrincipal, isFeed)) {
+ return "feed";
+ }
+
+ if (!isFeed) {
+ return "alternate";
+ }
+ // fall through
+ case "prefetch":
+ return null;
+
+ default:
+ return relAttribute.toLowerCase();
+ }
+}
+
+LinkToolbarHandler.prototype.getItemForLinkType =
+function(linkType) {
+ if (!(linkType in this.items && this.items[linkType]))
+ this.items[linkType] = LinkToolbarHandler.createItemForLinkType(linkType);
+
+ return this.items[linkType];
+}
+
+LinkToolbarHandler.createItemForLinkType =
+function(linkType)
+{
+ if (!document.getElementById("link-" + linkType))
+ return new LinkToolbarTransientMenu(linkType);
+
+ // XXX: replace switch with polymorphism
+ var element = document.getElementById("link-" + linkType);
+ switch (element.getAttribute("type") || element.localName) {
+ case "toolbarbutton":
+ return new LinkToolbarButton(linkType);
+
+ case "menuitem":
+ return new LinkToolbarItem(linkType);
+
+ case "menu":
+ return new LinkToolbarMenu(linkType);
+
+ default:
+ return new LinkToolbarTransientMenu(linkType);
+ }
+}
+
+LinkToolbarHandler.prototype.clearAllItems =
+function()
+{
+ // Hide the 'miscellaneous' separator
+ document.getElementById("misc-separator").setAttribute("collapsed", "true");
+
+ // Disable the individual items
+ for (var linkType in this.items)
+ this.items[linkType].clear();
+
+ // Store the fact that the toolbar is empty
+ this.hasItems = false;
+}
+
+var linkToolbarHandler = new LinkToolbarHandler();
+
+function LinkElementDecorator(element) {
+ /*
+ * XXX: this is an incomplete decorator, because it doesn't implement
+ * the full Element interface. If you need to use a method
+ * or member in the Element interface, just add it here and
+ * have it delegate to this.element
+ *
+ * XXX: would rather add some methods to Element.prototype instead of
+ * using a decorator, but Element.prototype is no longer exposed
+ * since the XPCDOM landing, see bug 83433
+ */
+
+ if (!element) return; // skip the rest on foo.prototype = new ThisClass calls
+
+ this.element = element;
+
+ this.rel = LinkElementDecorator.convertRevMade(element.rel, element.rev);
+ if (this.rel)
+ this.relValues = this.rel.split(" ");
+ this.rev = element.rev;
+ this.title = element.title;
+ this.href = element.href;
+ this.hreflang = element.hreflang;
+ this.media = element.media;
+ this.longTitle = null;
+}
+
+LinkElementDecorator.prototype.isIgnored =
+function()
+{
+ if (!this.rel) return true;
+ for (var i = 0; i < this.relValues.length; i++)
+ if (/^stylesheet$|^icon$|^fontdef$|^p3pv|^schema./i.test(this.relValues[i]))
+ return true;
+ return false;
+}
+
+LinkElementDecorator.convertRevMade =
+function(rel, rev)
+{
+ if (!rel && rev && /\bmade\b/i.test(rev))
+ return rev;
+ else
+ return rel;
+}
+
+LinkElementDecorator.prototype.getTooltip =
+function()
+{
+ return this.getLongTitle() || this.href;
+}
+
+LinkElementDecorator.prototype.getLabel =
+function()
+{
+ return this.getLongTitle() || this.rel;
+}
+
+LinkElementDecorator.prototype.getLongTitle =
+function()
+{
+ if (this.longTitle == null)
+ this.longTitle = this.makeLongTitle();
+
+ return this.longTitle;
+}
+
+LinkElementDecorator.prototype.makeLongTitle =
+function()
+{
+ let prefix = "";
+
+ // XXX: lookup more meaningful and localized version of media,
+ // i.e. media="print" becomes "Printable" or some such
+ // XXX: use localized version of ":" separator
+ if (this.media && !/\ball\b|\bscreen\b/i.test(this.media))
+ prefix += this.media + ": ";
+ if (this.hreflang) {
+ try {
+ let languageBundle = document.getElementById("languageBundle");
+ let regionBundle = document.getElementById("regionBundle");
+
+ // In case hreflang contains region code.
+ let ISOcode = this.hreflang.split("-");
+
+ prefix += languageBundle.getString(ISOcode[0].toLowerCase());
+
+ // Test if region code exists.
+ if (ISOcode[1])
+ prefix += " (" + regionBundle.getString(ISOcode[1].toLowerCase()) + ")";
+ }
+ catch (e) {
+ // Return if language or region is not recognized.
+ prefix += gNavigatorBundle.getFormattedString("unknownLanguage",
+ [this.hreflang]);
+ }
+
+ prefix += ": ";
+ }
+
+ return this.title ? prefix + this.title : prefix;
+}
+
+function AnchorElementDecorator(element) {
+ this.constructor(element);
+}
+AnchorElementDecorator.prototype = new LinkElementDecorator;
+
+AnchorElementDecorator.prototype.getLongTitle =
+function()
+{
+ return this.title ? this.__proto__.getLongTitle.apply(this)
+ : getText(this.element);
+}
+
+AnchorElementDecorator.prototype.getText =
+function(element)
+{
+ return condenseWhitespace(getTextRecursive(element));
+}
+
+AnchorElementDecorator.prototype.getTextRecursive =
+function(node)
+{
+ var text = "";
+ node.normalize();
+ if (node.hasChildNodes()) {
+ for (var i = 0; i < node.childNodes.length; i++) {
+ if (node.childNodes.item(i).nodeType == Node.TEXT_NODE)
+ text += node.childNodes.item(i).nodeValue;
+ else if (node.childNodes.item(i).nodeType == Node.ELEMENT_NODE)
+ text += getTextRecursive(node.childNodes.item(i));
+ }
+ }
+
+ return text;
+}
+
+AnchorElementDecorator.prototype.condenseWhitespace =
+function(text)
+{
+ return text.replace(/\W*$/, "").replace(/^\W*/, "").replace(/\W+/g, " ");
+}
diff --git a/comm/suite/browser/linkToolbarItem.js b/comm/suite/browser/linkToolbarItem.js
new file mode 100644
index 0000000000..1ae956a64d
--- /dev/null
+++ b/comm/suite/browser/linkToolbarItem.js
@@ -0,0 +1,215 @@
+/* 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/. */
+
+/*
+ * LinkToolbarItem and its subclasses represent the buttons, menuitems,
+ * and menus that handle the various link types.
+ */
+function LinkToolbarItem (linkType) {
+ this.linkType = linkType;
+ this.xulElementId = "link-" + linkType;
+ this.xulPopupId = this.xulElementId + "-popup";
+ this.parentMenuButton = null;
+
+ this.getXULElement = function() {
+ return document.getElementById(this.xulElementId);
+ }
+
+ this.clear = function() {
+ this.disableParentMenuButton();
+ this.getXULElement().setAttribute("disabled", "true");
+ this.getXULElement().removeAttribute("href");
+ }
+
+ this.displayLink = function(linkElement) {
+ if (this.getXULElement().hasAttribute("href")) return false;
+
+ this.setItem(linkElement);
+ this.enableParentMenuButton();
+ return true;
+ }
+
+ this.setItem = function(linkElement) {
+ this.getXULElement().setAttribute("href", linkElement.href);
+ this.getXULElement().removeAttribute("disabled");
+ }
+
+ this.enableParentMenuButton = function() {
+ if(this.getParentMenuButton())
+ this.getParentMenuButton().removeAttribute("disabled");
+ }
+
+ this.disableParentMenuButton = function() {
+ if (!this.parentMenuButton) return;
+
+ this.parentMenuButton.setAttribute("disabled", "true");
+ this.parentMenuButton = null;
+ }
+
+ this.getParentMenuButton = function() {
+ if (!this.parentMenuButton)
+ this.parentMenuButton = getParentMenuButtonRecursive(
+ this.getXULElement());
+
+ return this.parentMenuButton;
+ }
+
+ function getParentMenuButtonRecursive(xulElement) {
+ if (!xulElement) return null;
+
+ if (xulElement.tagName == "toolbarbutton")
+ return xulElement;
+
+ return getParentMenuButtonRecursive(xulElement.parentNode)
+ }
+}
+
+
+function LinkToolbarButton (linkType) {
+ this.constructor(linkType);
+
+ this.clear = function() {
+ this.__proto__.clear.apply(this);
+
+ this.getXULElement().removeAttribute("tooltiptext");
+ }
+
+ this.setItem = function(linkElement) {
+ this.__proto__.setItem.apply(this, [linkElement]);
+
+ this.getXULElement().setAttribute("tooltiptext", linkElement.getTooltip());
+ }
+
+ this.enableParentMenuButton = function() { /* do nothing */ }
+ this.disableParentMenuButton = function() { /* do nothing */ }
+}
+LinkToolbarButton.prototype = new LinkToolbarItem;
+
+
+function LinkToolbarMenu (linkType) {
+ this.constructor(linkType);
+
+ this.clear = function() {
+ this.disableParentMenuButton();
+ this.getXULElement().setAttribute("disabled", "true");
+ clearPopup(this.getPopup());
+ }
+
+ function clearPopup(popup) {
+ while (popup.hasChildNodes())
+ popup.lastChild.remove();
+ }
+
+ this.getPopup = function() {
+ return document.getElementById(this.xulPopupId);
+ }
+
+ this.displayLink = function(linkElement) {
+ this.addMenuItem(linkElement);
+ this.getXULElement().removeAttribute("disabled");
+ this.enableParentMenuButton();
+ return true;
+ }
+
+ function match(first, second) {
+ if (!first && !second) return true;
+ if (!first || !second) return false;
+
+ return first == second;
+ }
+
+ this.addMenuItem = function(linkElement) {
+ this.getPopup().appendChild(this.createMenuItem(linkElement));
+ }
+
+ this.createMenuItem = function(linkElement) {
+ // XXX: clone a prototypical XUL element instead of hardcoding these
+ // attributes
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", linkElement.getLabel());
+ menuitem.setAttribute("href", linkElement.href);
+ menuitem.setAttribute("class", "menuitem-iconic bookmark-item");
+
+ return menuitem;
+ }
+}
+LinkToolbarMenu.prototype = new LinkToolbarItem;
+
+
+function LinkToolbarTransientMenu (linkType) {
+ this.constructor(linkType);
+
+ this.getXULElement = function() {
+ if (this.__proto__.getXULElement.apply(this))
+ return this.__proto__.getXULElement.apply(this);
+ else
+ return this.createXULElement();
+ }
+
+ this.createXULElement = function() {
+ // XXX: clone a prototypical XUL element instead of hardcoding these
+ // attributes
+ var menu = document.createElement("menu");
+ menu.setAttribute("id", this.xulElementId);
+ menu.setAttribute("label", this.linkType);
+ menu.setAttribute("disabled", "true");
+ menu.setAttribute("class", "menu-iconic bookmark-item");
+ menu.setAttribute("container", "true");
+
+ document.getElementById("more-menu-popup").appendChild(menu);
+
+ return menu;
+ }
+
+ this.getPopup = function() {
+ if (!this.__proto__.getPopup.apply(this))
+ this.getXULElement().appendChild(this.createPopup());
+
+ return this.__proto__.getPopup.apply(this)
+ }
+
+ this.createPopup = function() {
+ var popup = document.createElement("menupopup");
+ popup.setAttribute("id", this.xulPopupId);
+
+ return popup;
+ }
+
+ this.clear = function() {
+ this.__proto__.clear.apply(this);
+
+ // XXX: we really want to use this instead of removeXULElement
+ //this.hideXULElement();
+ this.removeXULElement();
+ }
+
+ this.hideXULElement = function() {
+ /*
+ * XXX: using "hidden" or "collapsed" leads to a crash when you
+ * open the More menu under certain circumstances. Maybe
+ * related to bug 83906. As of 0.9.2 I it doesn't seem
+ * to crash anymore.
+ */
+ this.getXULElement().setAttribute("collapsed", "true");
+ }
+
+ this.removeXULElement = function() {
+ // XXX: stop using this method once it's safe to use hideXULElement
+ if (this.__proto__.getXULElement.apply(this))
+ this.__proto__.getXULElement.apply(this).remove();
+ }
+
+ this.displayLink = function(linkElement) {
+ if(!this.__proto__.displayLink.apply(this, [linkElement])) return false;
+
+ this.getXULElement().removeAttribute("collapsed");
+
+ // Show the 'miscellaneous' separator
+ document.getElementById("misc-separator").removeAttribute("collapsed");
+ return true;
+ }
+}
+
+LinkToolbarTransientMenu.prototype = new LinkToolbarMenu;
+
diff --git a/comm/suite/browser/linkToolbarOverlay.js b/comm/suite/browser/linkToolbarOverlay.js
new file mode 100644
index 0000000000..94927ad310
--- /dev/null
+++ b/comm/suite/browser/linkToolbarOverlay.js
@@ -0,0 +1,213 @@
+/* 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 LinkToolbarUI = function()
+{
+}
+
+LinkToolbarUI.prototype.linkAdded =
+function(event)
+{
+ var element = event.originalTarget;
+
+ if (element.ownerDocument != getBrowser().contentDocument ||
+ !linkToolbarUI.isLinkToolbarEnabled() ||
+ !(ChromeUtils.getClassName(element) === "HTMLLinkElement") ||
+ !element.href || (!element.rel && !element.rev))
+ return;
+
+ linkToolbarHandler.handle(element);
+}
+
+LinkToolbarUI.prototype.isLinkToolbarEnabled =
+function()
+{
+ if (document.getElementById("linktoolbar").getAttribute("hidden") == "true")
+ return false;
+ else
+ return true;
+}
+
+LinkToolbarUI.prototype.clear =
+function(event)
+{
+ if (event.originalTarget != getBrowser().contentDocument ||
+ !linkToolbarUI.isLinkToolbarEnabled() ||
+ !linkToolbarHandler.hasItems)
+ return;
+
+ linkToolbarHandler.clearAllItems();
+}
+
+LinkToolbarUI.prototype.tabSelected =
+function(event)
+{
+ if (event.originalTarget.localName != "tabs" ||
+ !linkToolbarUI.isLinkToolbarEnabled())
+ return;
+
+ linkToolbarHandler.clearAllItems();
+ linkToolbarUI.deactivate();
+ linkToolbarUI.fullSlowRefresh();
+}
+
+LinkToolbarUI.prototype.fullSlowRefresh =
+function()
+{
+ var currentNode = getBrowser().contentDocument.documentElement;
+ if (!ChromeUtils.getClassName(currentNode) === "HTMLHtmlElement")
+ return;
+ currentNode = currentNode.firstChild;
+
+ while(currentNode)
+ {
+ if (ChromeUtils.getClassName(currentNode) === "HTMLHeadElement") {
+ currentNode = currentNode.firstChild;
+
+ while(currentNode)
+ {
+ if (ChromeUtils.getClassName(currentNode) === "HTMLLinkElement")
+ linkToolbarUI.linkAdded({originalTarget: currentNode});
+ currentNode = currentNode.nextSibling;
+ }
+ }
+ else if (currentNode.nodeType == currentNode.ELEMENT_NODE) {
+ // head is supposed to be the first element inside html.
+ // Got something else instead. returning
+ return;
+ }
+ else
+ {
+ // Got a comment node or something like that. Moving on.
+ currentNode = currentNode.nextSibling;
+ }
+ }
+}
+
+LinkToolbarUI.prototype.toolbarActive = false;
+
+LinkToolbarUI.prototype.activate =
+function()
+{
+ if (!linkToolbarUI.toolbarActive) {
+ linkToolbarUI.toolbarActive = true;
+ document.getElementById("linktoolbar").setAttribute("hasitems", "true");
+ var contentArea = document.getElementById("appcontent");
+ contentArea.addEventListener("pagehide", linkToolbarUI.clear, true);
+ contentArea.addEventListener("pageshow", linkToolbarUI.deactivate, true);
+ contentArea.addEventListener("DOMHeadLoaded", linkToolbarUI.deactivate,
+ true);
+ }
+}
+
+LinkToolbarUI.prototype.deactivate =
+function()
+{
+ // This function can never be called unless the toolbar is active, because
+ // it's a handler that's only activated in that situation, so there's no need
+ // to check toolbarActive. On the other hand, by the time this method is
+ // called the toolbar might have been populated again already, in which case
+ // we don't want to deactivate.
+ if (!linkToolbarHandler.hasItems) {
+ linkToolbarUI.toolbarActive = false;
+ document.getElementById("linktoolbar").setAttribute("hasitems", "false");
+ var contentArea = document.getElementById("appcontent");
+ contentArea.removeEventListener("pagehide", linkToolbarUI.clear, true);
+ contentArea.removeEventListener("pageshow", linkToolbarUI.deactivate, true);
+ contentArea.removeEventListener("DOMHeadLoaded", linkToolbarUI.deactivate,
+ true);
+ }
+}
+
+/* called whenever something on the toolbar gets an oncommand event */
+LinkToolbarUI.prototype.commanded =
+function(event)
+{
+ // Return if this is one of the menubuttons.
+ if (event.target.getAttribute("type") == "menu") return;
+
+ if (!event.target.getAttribute("href")) return;
+
+ var destURL = event.target.getAttribute("href");
+
+ // We have to do a security check here, because we are loading URIs given
+ // to us by a web page from chrome, which is privileged.
+ try {
+ urlSecurityCheck(destURL, content.document.nodePrincipal,
+ Ci.nsIScriptSecurityManager.STANDARD);
+ loadURI(destURL, content.document.documentURIObject);
+ } catch (e) {
+ dump("Error: it is not permitted to load this URI from a <link> element: " + e);
+ }
+}
+
+// functions for twiddling XUL elements in the toolbar
+
+LinkToolbarUI.prototype.toggleLinkToolbar =
+function(checkedItem)
+{
+ this.goToggleTristateToolbar("linktoolbar", checkedItem);
+ this.initHandlers();
+ if (this.isLinkToolbarEnabled())
+ this.fullSlowRefresh();
+ else
+ linkToolbarHandler.clearAllItems();
+}
+
+LinkToolbarUI.prototype.initLinkbarVisibilityMenu =
+function()
+{
+ var state = document.getElementById("linktoolbar").getAttribute("hidden");
+ if (!state)
+ state = "maybe";
+ var checkedItem = document.getElementById("cmd_viewlinktoolbar_" + state);
+ checkedItem.setAttribute("checked", true);
+ checkedItem.checked = true;
+}
+
+LinkToolbarUI.prototype.goToggleTristateToolbar =
+function(id, checkedItem)
+{
+ var toolbar = document.getElementById(id);
+ if (toolbar)
+ {
+ toolbar.setAttribute("hidden", checkedItem.value);
+ document.persist(id, "hidden");
+ }
+}
+
+LinkToolbarUI.prototype.addHandlerActive = false;
+
+LinkToolbarUI.prototype.initialized = false;
+
+LinkToolbarUI.prototype.initHandlers =
+function()
+{
+ var contentArea = document.getElementById("appcontent");
+ if (linkToolbarUI.isLinkToolbarEnabled())
+ {
+ if (!linkToolbarUI.addHandlerActive) {
+ contentArea.addEventListener("select", linkToolbarUI.tabSelected);
+ contentArea.addEventListener("DOMLinkAdded", linkToolbarUI.linkAdded,
+ true);
+ linkToolbarUI.addHandlerActive = true;
+ }
+ } else
+ {
+ if (linkToolbarUI.addHandlerActive) {
+ contentArea.removeEventListener("select", linkToolbarUI.tabSelected);
+ contentArea.removeEventListener("DOMLinkAdded", linkToolbarUI.linkAdded,
+ true);
+ linkToolbarUI.addHandlerActive = false;
+ }
+ }
+ if (!linkToolbarUI.initialized)
+ {
+ linkToolbarUI.initialized = true;
+ document.removeEventListener("pageshow", linkToolbarUI.initHandlers, true);
+ }
+}
+
+var linkToolbarUI = new LinkToolbarUI;
+
diff --git a/comm/suite/browser/linkToolbarOverlay.xul b/comm/suite/browser/linkToolbarOverlay.xul
new file mode 100644
index 0000000000..8d9b4c1503
--- /dev/null
+++ b/comm/suite/browser/linkToolbarOverlay.xul
@@ -0,0 +1,165 @@
+<?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://navigator/skin/linkToolbar.css" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://navigator/locale/linkToolbar.dtd">
+
+<overlay id="linkToolbarOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:rdf="http://www.mozilla.org/rdf">
+
+ <!-- classes -->
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://navigator/content/linkToolbarHandler.js" />
+ <script src="chrome://navigator/content/linkToolbarItem.js" />
+
+ <!-- functions -->
+ <script src="chrome://navigator/content/linkToolbarOverlay.js" />
+
+ <script>
+ <![CDATA[
+ document.addEventListener("pageshow", linkToolbarUI.initHandlers, true);
+ ]]>
+ </script>
+
+ <stringbundleset>
+ <stringbundle id="languageBundle"
+ src="chrome://global/locale/languageNames.properties"/>
+ <stringbundle id="regionBundle"
+ src="chrome://global/locale/regionNames.properties"/>
+ </stringbundleset>
+
+ <menupopup id="view_toolbars_popup">
+ <menu label="&linkToolbar.label;"
+ accesskey="&linkToolbar.accesskey;"
+ insertbefore="menuitem_showhide_tabbar"
+ onpopupshowing="linkToolbarUI.initLinkbarVisibilityMenu();"
+ oncommand="linkToolbarUI.toggleLinkToolbar(event.target);">
+ <menupopup>
+ <menuitem label="&linkToolbarAlways.label;"
+ accesskey="&linkToolbarAlways.accesskey;"
+ class="menuitem-iconic" type="radio" value="false"
+ name="cmd_viewlinktoolbar" id="cmd_viewlinktoolbar_false" />
+ <menuitem label="&linkToolbarAsNeeded.label;"
+ accesskey="&linkToolbarAsNeeded.accesskey;"
+ class="menuitem-iconic" type="radio" value="maybe"
+ name="cmd_viewlinktoolbar" id="cmd_viewlinktoolbar_maybe" />
+ <menuitem label="&linkToolbarNever.label;"
+ accesskey="&linkToolbarNever.accesskey;"
+ class="menuitem-iconic" type="radio" value="true"
+ name="cmd_viewlinktoolbar" id="cmd_viewlinktoolbar_true" />
+ </menupopup>
+ </menu>
+ </menupopup>
+
+ <toolbox id="navigator-toolbox">
+ <toolbar id="linktoolbar"
+ grippytooltiptext="&linkToolbar.tooltip;"
+ class="chromeclass-directories"
+ hidden="true"
+ hasitems="false"
+ nowindowdrag="true"
+ oncommand="event.stopPropagation(); return linkToolbarUI.commanded(event);">
+
+ <toolbarbutton id="link-top" class="bookmark-item"
+ label="&topButton.label;" disabled="true"/>
+
+ <toolbarbutton id="link-up" class="bookmark-item"
+ label="&upButton.label;" disabled="true"/>
+
+ <toolbarseparator />
+
+ <toolbarbutton id="link-first" class="bookmark-item"
+ label="&firstButton.label;" disabled="true"/>
+
+ <toolbarbutton id="link-prev" class="bookmark-item"
+ label="&prevButton.label;" disabled="true"/>
+
+ <toolbarbutton id="link-next" class="bookmark-item"
+ label="&nextButton.label;" disabled="true"/>
+
+ <toolbarbutton id="link-last" class="bookmark-item"
+ label="&lastButton.label;" disabled="true"/>
+
+ <toolbarseparator />
+
+ <toolbarbutton id="document-menu" class="bookmark-item"
+ type="menu"
+ container="true"
+ label="&documentButton.label;" disabled="true">
+ <menupopup id="document-menu-popup">
+
+ <menuitem id="link-toc" label="&tocButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+ <menu id="link-chapter" label="&chapterButton.label;" disabled="true"
+ class="menu-iconic bookmark-item" container="true">
+ <menupopup id="link-chapter-popup" />
+ </menu>
+ <menu id="link-section" label="&sectionButton.label;" disabled="true"
+ class="menu-iconic bookmark-item" container="true">
+ <menupopup id="link-section-popup" />
+ </menu>
+ <menu id="link-subsection" label="&subSectionButton.label;" disabled="true"
+ class="menu-iconic bookmark-item" container="true">
+ <menupopup id="link-subsection-popup" />
+ </menu>
+ <menu id="link-appendix" label="&appendixButton.label;" disabled="true"
+ class="menu-iconic bookmark-item" container="true">
+ <menupopup id="link-appendix-popup" />
+ </menu>
+ <menuitem id="link-glossary" label="&glossaryButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+ <menuitem id="link-index" label="&indexButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbarbutton id="more-menu" class="bookmark-item"
+ type="menu"
+ container="true"
+ label="&moreButton.label;" disabled="true">
+ <menupopup id="more-menu-popup">
+
+ <menuitem id="link-help" label="&helpButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+ <menuitem id="link-search" label="&searchButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+
+ <menuseparator />
+
+ <menuitem id="link-author" label="&authorButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+ <menuitem id="link-copyright" label="&copyrightButton.label;" disabled="true"
+ class="menuitem-iconic bookmark-item"/>
+
+ <menuseparator />
+
+ <menu id="link-bookmark" label="&bookmarkButton.label;" disabled="true"
+ class="menu-iconic bookmark-item" container="true">
+ <menupopup id="link-bookmark-popup" />
+ </menu>
+
+ <menuseparator />
+
+ <menu id="link-alternate" label="&alternateButton.label;" disabled="true"
+ class="menu-iconic bookmark-item" container="true">
+ <menupopup id="link-alternate-popup" />
+ </menu>
+
+ <menuseparator collapsed="true" id="misc-separator" />
+
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbarbutton id="link-feed" class="bookmark-item"
+ type="menu"
+ container="true"
+ label="&feedButton.label;" disabled="true">
+ <menupopup id="link-feed-popup"/>
+ </toolbarbutton>
+ </toolbar>
+ </toolbox>
+</overlay>
diff --git a/comm/suite/browser/mailNavigatorOverlay.js b/comm/suite/browser/mailNavigatorOverlay.js
new file mode 100644
index 0000000000..495d124eba
--- /dev/null
+++ b/comm/suite/browser/mailNavigatorOverlay.js
@@ -0,0 +1,176 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gUseExternalMailto;
+
+// attachment: 0 - link
+// 1 - page
+// 2 - image
+function openComposeWindow(url, title, attachment, charset)
+{
+ if (gUseExternalMailto)
+ {
+ openExternalMailer(url, title);
+ }
+ else
+ {
+ var params = Cc["@mozilla.org/messengercompose/composeparams;1"]
+ .createInstance(Ci.nsIMsgComposeParams);
+
+ params.composeFields = Cc["@mozilla.org/messengercompose/composefields;1"]
+ .createInstance(Ci.nsIMsgCompFields);
+ if (attachment == 0 || attachment == 1)
+ {
+ params.composeFields.body = url;
+ params.composeFields.subject = title;
+ params.bodyIsLink = true;
+ }
+
+ if (attachment == 1 || attachment == 2)
+ {
+ var attachmentData = Cc["@mozilla.org/messengercompose/attachment;1"]
+ .createInstance(Ci.nsIMsgAttachment);
+ attachmentData.url = url;
+ attachmentData.urlCharset = charset;
+ params.composeFields.addAttachment(attachmentData);
+ }
+
+ var composeService = Cc["@mozilla.org/messengercompose;1"]
+ .getService(Ci.nsIMsgComposeService);
+
+ // it is possible you won't have a default identity
+ // like if you've never launched mail before on a new profile.
+ // see bug #196073
+ try
+ {
+ params.identity = composeService.defaultIdentity;
+ }
+ catch (ex)
+ {
+ params.identity = null;
+ }
+
+ composeService.OpenComposeWindowWithParams(null, params);
+ }
+}
+
+function openExternalMailer(url, title) {
+ var extProtocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var mailto = url ? "mailto:?body=" + encodeURIComponent(url)
+ + "&subject="
+ + encodeURIComponent(title) : "mailto:";
+ var uri = Services.io.newURI(mailto);
+
+ extProtocolSvc.loadURI(uri);
+}
+
+function openNewCardDialog()
+{
+ window.openDialog("chrome://messenger/content/addressbook/abNewCardDialog.xul",
+ "", "chrome,modal,resizable=no,centerscreen");
+}
+
+function goOpenNewMessage()
+{
+ if (gUseExternalMailto)
+ {
+ openExternalMailer();
+ }
+ else if ("MsgNewMessage" in window)
+ {
+ MsgNewMessage(null);
+ }
+ else
+ {
+ var msgComposeService = Cc["@mozilla.org/messengercompose;1"]
+ .getService(Ci.nsIMsgComposeService);
+ msgComposeService.OpenComposeWindow(null, null, null,
+ Ci.nsIMsgCompType.New,
+ Ci.nsIMsgCompFormat.Default,
+ null, null, null);
+ }
+}
+
+function sendLink(aURL)
+{
+ var title = "";
+ if (!aURL)
+ {
+ aURL = window.content.document.URL;
+ title = window.content.document.title;
+ }
+ try
+ {
+ openComposeWindow(aURL, title, 0, null);
+ }
+ catch(ex)
+ {
+ dump("Cannot Send Link: " + ex + "\n");
+ }
+}
+
+function sendMedia(mediaURL)
+{
+ try
+ {
+ var charset = getCharsetforSave(null);
+ openComposeWindow(mediaURL, null, 2, charset);
+ }
+ catch(ex)
+ {
+ dump("Cannot Send Media: " + ex + "\n");
+ }
+}
+
+function sendPage(aDocument)
+{
+ if (!aDocument)
+ aDocument = window.content.document;
+
+ try
+ {
+ var charset = getCharsetforSave(aDocument);
+ openComposeWindow(aDocument.URL, aDocument.title, 1, charset);
+ }
+ catch(ex)
+ {
+ dump("Cannot Send Page: " + ex + "\n");
+ }
+}
+
+function initMailContextMenuItems(aEvent)
+{
+ var shouldShowSendPage = !(gContextMenu.onTextInput || gContextMenu.isContentSelected ||
+ gContextMenu.onVideo || gContextMenu.onAudio) &&
+ !gContextMenu.onLink &&
+ !gUseExternalMailto;
+ gContextMenu.showItem("context-sendpage", shouldShowSendPage);
+
+ gContextMenu.showItem("context-sep-apps", gContextMenu.shouldShowSeparator("context-sep-apps"));
+}
+
+function initMailContextMenuPopupListener(aEvent)
+{
+ var popup = document.getElementById("contentAreaContextMenu");
+ if (popup)
+ popup.addEventListener("popupshowing", initMailContextMenuItems);
+}
+
+function hideMenuitems() {
+ document.getElementById("menu_newCard").hidden = gUseExternalMailto;
+ var menu_sendPage = document.getElementById("menu_sendPage");
+ if (menu_sendPage)
+ menu_sendPage.hidden = gUseExternalMailto;
+}
+
+function initOverlay(aEvent) {
+ gUseExternalMailto = Services.io.getProtocolHandler("mailto") instanceof
+ Ci.nsIExternalProtocolHandler;
+ initMailContextMenuPopupListener(aEvent);
+ hideMenuitems();
+}
+
+addEventListener("load", initOverlay, false);
diff --git a/comm/suite/browser/mailNavigatorOverlay.xul b/comm/suite/browser/mailNavigatorOverlay.xul
new file mode 100644
index 0000000000..5221ce68f8
--- /dev/null
+++ b/comm/suite/browser/mailNavigatorOverlay.xul
@@ -0,0 +1,96 @@
+<?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 SYSTEM "chrome://navigator/locale/mailNavigatorOverlay.dtd" >
+
+<overlay id="mailNavigatorOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://navigator/content/mailNavigatorOverlay.js"/>
+
+ <!-- navigator specific commands -->
+ <commandset id="tasksCommands">
+ <command id="cmd_newMessage" oncommand="goOpenNewMessage();"/>
+ <command id="cmd_newCard" oncommand="openNewCardDialog()"/>
+ <command id="cmd_sendPage" oncommand="sendPage();"/>
+ <command id="Browser:SendLink" oncommand="sendLink();"/>
+ </commandset>
+ <keyset id="tasksKeys">
+#ifdef XP_MACOSX
+ <key id="key_newMessage" key="&newMessageCmd.key;"
+ modifiers="accel,shift" command="cmd_newMessage"/>
+#else
+ <key id="key_newMessage" key="&newMessageCmd.key;"
+ modifiers="accel" command="cmd_newMessage"/>
+#endif
+ </keyset>
+
+ <!-- navigator specific UI items -->
+ <menupopup id="menu_NewPopup">
+ <menuitem id="menu_newCard"
+ label="&newContactCmd.label;"
+ accesskey="&newContactCmd.accesskey;"
+ command="cmd_newCard"
+ insertafter="navBeginGlobalNewItems"/>
+ <menuitem id="menu_newMessage"
+ label="&newMessageCmd.label;"
+ accesskey="&newMessageCmd.accesskey;"
+ command="cmd_newMessage"
+ key="key_newMessage"
+ insertafter="navBeginGlobalNewItems"/>
+ </menupopup>
+
+ <menupopup id="menu_FilePopup">
+ <menuitem id="menu_sendPage"
+ label="&sendPage.label;"
+ accesskey="&sendPage.accesskey;"
+ command="cmd_sendPage"
+ position="9"/>
+ <menuitem id="menu_sendLink"
+ label="&sendLinkCmd.label;"
+ accesskey="&sendLinkCmd.accesskey;"
+ command="Browser:SendLink"
+ position="10"/>
+ </menupopup>
+
+ <menupopup id="contentAreaContextMenu">
+ <menuitem id="context-sendpage"
+ label="&contextSendThisPage.label;"
+ accesskey="&contextSendThisPage.accesskey;"
+ oncommand="sendPage();"
+ insertafter="context-savepage"/>
+ <menuitem id="context-sendimage"
+ label="&contextSendImage.label;"
+ accesskey="&contextSendImage.accesskey;"
+ oncommand="sendMedia(gContextMenu.mediaURL);"
+ insertafter="context-saveimage"/>
+ <menuitem id="context-sendvideo"
+ label="&contextSendVideo.label;"
+ accesskey="&contextSendVideo.accesskey;"
+ oncommand="sendMedia(gContextMenu.mediaURL);"
+ insertafter="context-savevideo"/>
+ <menuitem id="context-sendaudio"
+ label="&contextSendAudio.label;"
+ accesskey="&contextSendAudio.accesskey;"
+ oncommand="sendMedia(gContextMenu.mediaURL);"
+ insertafter="context-saveaudio"/>
+ <menuitem id="context-sendlink"
+ label="&contextSendThisLink.label;"
+ accesskey="&contextSendThisLink.accesskey;"
+ oncommand="sendLink(gContextMenu.linkURL);"
+ insertafter="context-savelink"/>
+ <menu id="frame">
+ <menupopup id="frame_popup">
+ <menuitem id="context-sendframe"
+ insertafter="context-saveframe"
+ label="&contextSendFrame.label;"
+ accesskey="&contextSendFrame.accesskey;"
+ oncommand="sendPage(gContextMenu.target.ownerDocument);"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+
+</overlay>
+
diff --git a/comm/suite/browser/metadata.js b/comm/suite/browser/metadata.js
new file mode 100644
index 0000000000..604ef54bb5
--- /dev/null
+++ b/comm/suite/browser/metadata.js
@@ -0,0 +1,526 @@
+/* -*- 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/. */
+
+const MathMLNS = "http://www.w3.org/1998/Math/MathML";
+const XLinkNS = "http://www.w3.org/1999/xlink";
+const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const XMLNS = "http://www.w3.org/XML/1998/namespace";
+const XHTMLNS = "http://www.w3.org/1999/xhtml";
+var gMetadataBundle;
+var gLangBundle;
+var gRegionBundle;
+var nodeView;
+var htmlMode = false;
+
+var onLink = false;
+var onImage = false;
+var onInsDel = false;
+var onQuote = false;
+var onMisc = false;
+var onTable = false;
+var onTitle = false;
+var onLang = false;
+
+function onLoad()
+{
+ gMetadataBundle = document.getElementById("bundle_metadata");
+ gLangBundle = document.getElementById("bundle_languages");
+ gRegionBundle = document.getElementById("bundle_regions");
+
+ showMetadataFor(window.arguments[0]);
+
+ nodeView = window.arguments[0].ownerDocument.defaultView;
+}
+
+function showMetadataFor(elem)
+{
+ // skip past non-element nodes
+ while (elem && elem.nodeType != Node.ELEMENT_NODE)
+ elem = elem.parentNode;
+
+ if (!elem) {
+ alert(gMetadataBundle.getString("unableToShowProps"));
+ window.close();
+ }
+
+ if (elem.ownerDocument.getElementsByName && !elem.ownerDocument.namespaceURI)
+ htmlMode = true;
+
+ // htmllocalname is "" if it's not an html tag, or the name of the tag if it is.
+ var htmllocalname = "";
+ if (isHTMLElement(elem,"")) {
+ htmllocalname = elem.localName.toLowerCase();
+ }
+
+ // We only look for images once
+ checkForImage(elem, htmllocalname);
+
+ // Walk up the tree, looking for elements of interest.
+ // Each of them could be at a different level in the tree, so they each
+ // need their own boolean to tell us to stop looking.
+ while (elem && elem.nodeType == Node.ELEMENT_NODE) {
+ htmllocalname = "";
+ if (isHTMLElement(elem,"")) {
+ htmllocalname = elem.localName.toLowerCase();
+ }
+
+ if (!onLink) checkForLink(elem, htmllocalname);
+ if (!onInsDel) checkForInsDel(elem, htmllocalname);
+ if (!onQuote) checkForQuote(elem, htmllocalname);
+ if (!onTable) checkForTable(elem, htmllocalname);
+ if (!onTitle) checkForTitle(elem, htmllocalname);
+ if (!onLang) checkForLang(elem, htmllocalname);
+
+ elem = elem.parentNode;
+ }
+
+ // Decide which sections to show
+ var onMisc = onTable || onTitle || onLang;
+ if (!onMisc) hideNode("misc-sec");
+ if (!onLink) hideNode("link-sec");
+ if (!onImage) hideNode("image-sec");
+ if (!onInsDel) hideNode("insdel-sec");
+ if (!onQuote) hideNode("quote-sec");
+
+ // Fix the Misc section visibilities
+ if (onMisc) {
+ if (!onTable) hideNode("misc-tblsummary");
+ if (!onLang) hideNode("misc-lang");
+ if (!onTitle) hideNode("misc-title");
+ }
+
+ // Get rid of the "No properties" message. This is a backstop -
+ // it should really never show, as long as nsContextMenu.js's
+ // checking doesn't get broken.
+ if (onLink || onImage || onInsDel || onQuote || onMisc)
+ hideNode("no-properties")
+}
+
+var cacheListener = {
+ onCacheEntryAvailable: function onCacheEntryAvailable(descriptor) {
+ if (descriptor) {
+ var kbSize = descriptor.dataSize / 1024;
+ kbSize = Math.round(kbSize * 100) / 100;
+ setInfo("image-filesize", gMetadataBundle.getFormattedString("imageSize",
+ [formatNumber(kbSize),
+ formatNumber(descriptor.dataSize)]));
+ } else {
+ setInfo("image-filesize", gMetadataBundle.getString("imageSizeUnknown"));
+ }
+ },
+ onCacheEntryCheck: function onCacheEntryCheck() {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ }
+};
+
+function checkForImage(elem, htmllocalname)
+{
+ var img;
+ var imgType; // "img" = <img>
+ // "object" = <object>
+ // "input" = <input type=image>
+ // "background" = css background (to be added later)
+ var ismap = false;
+
+ if (htmllocalname === "img") {
+ img = elem;
+ imgType = "img";
+
+ } else if (htmllocalname === "object" &&
+ elem.type.substring(0,6) == "image/" &&
+ elem.data) {
+ img = elem;
+ imgType = "object";
+
+ } else if (htmllocalname === "input" &&
+ elem.type.toUpperCase() == "IMAGE") {
+ img = elem;
+ imgType = "input";
+
+ } else if (htmllocalname === "area" || htmllocalname === "a") {
+
+ // Clicked in image map?
+ var map = elem;
+ ismap = true;
+ setAlt(map);
+
+ while (map && map.nodeType == Node.ELEMENT_NODE && !isHTMLElement(map,"map") )
+ map = map.parentNode;
+
+ if (map && map.nodeType == Node.ELEMENT_NODE) {
+ img = getImageForMap(map);
+ var imgLocalName = img && img.localName.toLowerCase();
+ if (imgLocalName == "img" || imgLocalName == "object") {
+ imgType = imgLocalName;
+ }
+ }
+
+ }
+
+ if (img) {
+
+ var imgURL = imgType == "object" ? img.data : img.src;
+ setInfo("image-url", imgURL);
+
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ const LoadContextInfo = Services.loadContextInfo;
+ var loadContextInfo = opener.gPrivate ? LoadContextInfo.private :
+ LoadContextInfo.default;
+ const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+ Services.cache2
+ .getService(Ci.nsICacheStorageService)
+ .diskCacheStorage(loadContextInfo, false)
+ .asyncOpenURI(NetUtil.newURI(imgURL), null,
+ Ci.nsICacheStorage.OPEN_READONLY, cacheListener);
+
+ if ("width" in img && img.width != "") {
+ setInfo("image-width", gMetadataBundle.getFormattedString("imageWidth", [formatNumber(img.width)]));
+ setInfo("image-height", gMetadataBundle.getFormattedString("imageHeight", [formatNumber(img.height)]));
+ }
+ else {
+ setInfo("image-width", "");
+ setInfo("image-height", "");
+ }
+
+ if (imgType == "img") {
+ setInfo("image-desc", img.longDesc);
+ } else {
+ setInfo("image-desc", "");
+ }
+
+ onImage = true;
+ }
+
+ if (!ismap) {
+ if (imgType == "img" || imgType == "input") {
+ setAlt(img);
+ } else {
+ hideNode("image-alt");
+ }
+ }
+}
+
+function checkForLink(elem, htmllocalname)
+{
+ if ((htmllocalname === "a" && elem.href != "") ||
+ htmllocalname === "area") {
+
+ setLink(elem.href, elem.getAttribute("type"),
+ convertLanguageCode(elem.getAttribute("hreflang")),
+ elem.getAttribute("rel"), elem.getAttribute("rev"));
+
+ var target = elem.target;
+
+ switch (target) {
+ case "_top":
+ setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
+ break;
+ case "_parent":
+ setInfo("link-target", gMetadataBundle.getString("parentFrameText"));
+ break;
+ case "_blank":
+ setInfo("link-target", gMetadataBundle.getString("newWindowText"));
+ break;
+ case "":
+ case "_self":
+ if (elem.ownerDocument.defaultView) {
+ if (elem.ownerDocument != elem.ownerDocument.defaultView.content.document)
+ setInfo("link-target", gMetadataBundle.getString("sameFrameText"));
+ else
+ setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
+ } else {
+ hideNode("link-target");
+ }
+ break;
+ default:
+ setInfo("link-target", "\"" + target + "\"");
+ }
+
+ onLink = true;
+ }
+ else if (elem.namespaceURI == MathMLNS && elem.hasAttribute("href")) {
+ setLink(makeHrefAbsolute(elem.getAttribute("href"), elem));
+
+ setInfo("link-target", "");
+
+ onLink = true;
+ }
+ else if (elem.getAttributeNS(XLinkNS, "href")) {
+ setLink(makeHrefAbsolute(elem.getAttributeNS(XLinkNS, "href"), elem));
+
+ switch (elem.getAttributeNS(XLinkNS,"show")) {
+ case "embed":
+ setInfo("link-target", gMetadataBundle.getString("embeddedText"));
+ break;
+ case "new":
+ setInfo("link-target", gMetadataBundle.getString("newWindowText"));
+ break;
+ case null:
+ case "":
+ case "replace":
+ if (elem.ownerDocument != elem.ownerDocument.defaultView.content.document)
+ setInfo("link-target", gMetadataBundle.getString("sameFrameText"));
+ else
+ setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
+ break;
+ default:
+ setInfo("link-target", "");
+ break;
+ }
+
+ onLink = true;
+ }
+}
+
+function checkForInsDel(elem, htmllocalname)
+{
+ if ((htmllocalname === "ins" || htmllocalname === "del") &&
+ (elem.cite || elem.dateTime)) {
+ setInfo("insdel-cite", elem.cite);
+ setInfo("insdel-date", elem.dateTime);
+ onInsDel = true;
+ }
+}
+
+
+function checkForQuote(elem, htmllocalname)
+{
+ if ((htmllocalname === "q" || htmllocalname === "blockquote") && elem.cite) {
+ setInfo("quote-cite", elem.cite);
+ onQuote = true;
+ }
+}
+
+function checkForTable(elem, htmllocalname)
+{
+ if (htmllocalname === "table" && elem.summary) {
+ setInfo("misc-tblsummary", elem.summary);
+ onTable = true;
+ }
+}
+
+function checkForLang(elem, htmllocalname)
+{
+ if ((htmllocalname && elem.lang) || elem.getAttributeNS(XMLNS, "lang")) {
+ var abbr;
+ if (htmllocalname && elem.lang)
+ abbr = elem.lang;
+ else
+ abbr = elem.getAttributeNS(XMLNS, "lang");
+
+ setInfo("misc-lang", convertLanguageCode(abbr));
+ onLang = true;
+ }
+}
+
+function checkForTitle(elem, htmllocalname)
+{
+ if (htmllocalname && elem.title) {
+ setInfo("misc-title", elem.title);
+ onTitle = true;
+ }
+}
+
+/*
+ * Set five link properties at once.
+ * All parameters are optional.
+ */
+function setLink(url, lang, type, rel, rev)
+{
+ setInfo("link-url", url);
+ setInfo("link-type", type);
+ setInfo("link-lang", lang);
+ setInfo("link-rel", rel);
+ setInfo("link-rev", rev);
+}
+
+/*
+ * Set text of node id to value
+ * if value="" the node with specified id is hidden.
+ * Node should be have one of these forms
+ * <xul:label id="id-text" value=""/>
+ * <xul:description id="id-text"/>
+ */
+function setInfo(id, value)
+{
+ if (!value) {
+ hideNode(id);
+ return;
+ }
+
+ var node = document.getElementById(id+"-text");
+
+ if (node.namespaceURI == XULNS && node.localName == "label" ||
+ (node.namespaceURI == XULNS && node.localName == "textbox")) {
+ node.setAttribute("value",value);
+
+ } else if (node.namespaceURI == XULNS && node.localName == "description") {
+ node.textContent = value;
+ }
+}
+
+// Hide node with specified id
+function hideNode(id)
+{
+ var style = document.getElementById(id).getAttribute("style");
+ document.getElementById(id).setAttribute("style", "display:none;" + style);
+}
+
+/*
+ * Find <img> or <object> which uses an imagemap.
+ * If more then one object is found we can't determine which one
+ * was clicked.
+ *
+ * This code has to be changed once bug 1882 is fixed.
+ * Once bug 72527 is fixed this code should use the .images collection.
+ */
+function getImageForMap(map)
+{
+ var mapuri = "#" + map.getAttribute("name");
+ var multipleFound = false;
+ var img;
+
+ var list = getHTMLElements(map.ownerDocument, "img");
+ for (var i=0; i < list.length; i++) {
+ if (list.item(i).getAttribute("usemap") == mapuri) {
+ if (img) {
+ multipleFound = true;
+ break;
+ } else {
+ img = list.item(i);
+ imgType = "img";
+ }
+ }
+ }
+
+ list = getHTMLElements(map.ownerDocument, "object");
+ for (i = 0; i < list.length; i++) {
+ if (list.item(i).getAttribute("usemap") == mapuri) {
+ if (img) {
+ multipleFound = true;
+ break;
+ } else {
+ img = list.item(i);
+ imgType = "object";
+ }
+ }
+ }
+
+ if (multipleFound)
+ img = null;
+
+ return img;
+}
+
+function getHTMLElements(node, name)
+{
+ if (htmlMode)
+ return node.getElementsByTagName(name);
+ return node.getElementsByTagNameNS(XHTMLNS, name);
+}
+
+// name should be in lower case
+function isHTMLElement(node, name)
+{
+ if (node.nodeType != Node.ELEMENT_NODE)
+ return false;
+
+ if (htmlMode)
+ return !name || node.localName.toLowerCase() == name;
+
+ return (!name || node.localName == name) && node.namespaceURI == XHTMLNS;
+}
+
+// This function coded according to the spec at:
+// http://www.bath.ac.uk/~py8ieh/internet/discussion/metadata.txt
+function convertLanguageCode(abbr)
+{
+ if (!abbr) return "";
+ var result;
+ var region = "";
+ var tokens = abbr.split("-");
+ var language = tokens.shift();
+
+ if (language == "x" || language == "i")
+ {
+ // x and i prefixes mean unofficial ones. So we proper-case the next
+ // word and leave the rest.
+ if (tokens.length > 0)
+ {
+ // Upper-case first letter
+ language = tokens[0].substr(0, 1).toUpperCase() + tokens[0].substr(1);
+ tokens.shift();
+
+ // Add on the rest as space-separated strings inside the brackets
+ region = tokens.join(" ");
+ }
+ }
+ else
+ {
+ // Otherwise we treat the first as a lang, the second as a region
+ // and the rest as strings.
+ try
+ {
+ language = gLangBundle.getString(language.toLowerCase());
+ }
+ catch (e)
+ {
+ }
+
+ if (tokens.length > 0)
+ {
+ try
+ {
+ tokens[0] = gRegionBundle.getString(tokens[0].toLowerCase());
+ }
+ catch (e)
+ {
+ }
+ region = tokens.join(" ");
+ }
+ }
+
+ if (region) {
+ result = gMetadataBundle.getFormattedString("languageRegionFormat",
+ [language, region]);
+ } else {
+ result = language;
+ }
+ return result;
+}
+
+function setAlt(elem) {
+ var altText = document.getElementById("image-alt-text");
+ if (elem.hasAttribute("alt")) {
+ if (elem.alt != "") {
+ altText.value = elem.alt;
+ altText.setAttribute("style","font-style:inherit");
+ } else {
+ altText.value = gMetadataBundle.getString("altTextBlank");
+ altText.setAttribute("style","font-style:italic");
+ }
+ } else {
+ altText.value = gMetadataBundle.getString("altTextMissing");
+ altText.setAttribute("style","font-style:italic");
+ }
+
+}
+
+function formatNumber(number)
+{
+ return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString()
+}
+
+function makeHrefAbsolute(href, elem)
+{
+ const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+ try {
+ var baseURI = NetUtil.newURI(elem.baseURI, elem.ownerDocument.characterSet);
+ href = NetUtil.newURI(href, elem.ownerDocument.characterSet, baseURI).spec;
+ } catch (e) {
+ }
+ return href;
+}
diff --git a/comm/suite/browser/metadata.xul b/comm/suite/browser/metadata.xul
new file mode 100644
index 0000000000..43d0f7b00b
--- /dev/null
+++ b/comm/suite/browser/metadata.xul
@@ -0,0 +1,192 @@
+<?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://navigator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://navigator/skin/pageInfo.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+ <!ENTITY % metadataDTD SYSTEM "chrome://navigator/locale/metadata.dtd" >
+ %metadataDTD;
+]>
+
+<dialog id="metadata"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&caption.label;"
+ onload="onLoad()"
+ minwidth="350"
+ buttons="none"
+ persist="screenX screenY"
+ screenX="24" screenY="24">
+
+ <script src="chrome://navigator/content/metadata.js"/>
+
+ <stringbundle src="chrome://navigator/locale/metadata.properties" id="bundle_metadata"/>
+ <stringbundle src="chrome://global/locale/languageNames.properties" id="bundle_languages"/>
+ <stringbundle src="chrome://global/locale/regionNames.properties" id="bundle_regions"/>
+
+ <label id="no-properties" value="&no-properties.label;"/>
+
+ <vbox id="link-sec">
+ <label value="&link-sec.label;"/>
+ <separator class="groove"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="link-url">
+ <separator orient="vertical"/>
+ <label value="&link-url.label; " control="link-url-text"/>
+ <textbox readonly="true" id="link-url-text" class="meta-properties"/>
+ </row>
+ <row id="link-target">
+ <separator orient="vertical"/>
+ <label value="&link-target.label; " control="link-target-text"/>
+ <textbox readonly="true" id="link-target-text" class="meta-properties"/>
+ </row>
+ <row id="link-type">
+ <separator orient="vertical"/>
+ <label value="&link-type.label; " control="link-type-text"/>
+ <textbox readonly="true" id="link-type-text" class="meta-properties"/>
+ </row>
+ <row id="link-lang">
+ <separator orient="vertical"/>
+ <label value="&link-lang.label; " control="link-lang-text"/>
+ <textbox readonly="true" id="link-lang-text" class="meta-properties"/>
+ </row>
+ <row id="link-rel">
+ <separator orient="vertical"/>
+ <label value="&link-rel.label; " control="link-rel-text"/>
+ <textbox readonly="true" id="link-rel-text" class="meta-properties"/>
+ </row>
+ <row id="link-rev">
+ <separator orient="vertical"/>
+ <label value="&link-rev.label; " control="link-rev-text"/>
+ <textbox readonly="true" id="link-rev-text" class="meta-properties"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ </vbox>
+ <vbox id="image-sec">
+ <label value="&image-sec.label;"/>
+ <separator class="groove"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="image-url">
+ <separator orient="vertical"/>
+ <label value="&image-url.label; " control="image-url-text"/>
+ <textbox readonly="true" id="image-url-text" class="meta-properties"/>
+ </row>
+ <row id="image-width">
+ <separator orient="vertical"/>
+ <label value="&image-width.label; " control="image-width-text"/>
+ <textbox readonly="true" id="image-width-text" class="meta-properties"/>
+ </row>
+ <row id="image-height">
+ <separator orient="vertical"/>
+ <label value="&image-height.label; " control="image-height-text"/>
+ <textbox readonly="true" id="image-height-text" class="meta-properties"/>
+ </row>
+ <row id="image-filesize">
+ <separator orient="vertical"/>
+ <label value="&image-filesize.label; " control="image-filesize-text"/>
+ <textbox readonly="true" id="image-filesize-text" value="&image-filesize.value;" class="meta-properties"/>
+ </row>
+ <row id="image-alt">
+ <separator orient="vertical"/>
+ <label value="&image-alt.label; " control="image-alt-text"/>
+ <textbox readonly="true" id="image-alt-text" class="meta-properties"/>
+ </row>
+ <row id="image-desc">
+ <separator orient="vertical"/>
+ <label value="&image-desc.label; " control="image-desc-text"/>
+ <textbox readonly="true" id="image-desc-text" class="meta-properties"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ </vbox>
+ <vbox id="insdel-sec">
+ <label value="&insdel-sec.label;"/>
+ <separator class="groove"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="insdel-cite">
+ <separator orient="vertical"/>
+ <label value="&insdel-cite.label; " control="insdel-cite-text"/>
+ <textbox readonly="true" id="insdel-cite-text" class="meta-properties"/>
+ </row>
+ <row id="insdel-date">
+ <separator orient="vertical"/>
+ <label value="&insdel-date.label; " control="insdel-date-text"/>
+ <textbox readonly="true" id="insdel-date-text" class="meta-properties"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ </vbox>
+ <vbox id="quote-sec">
+ <label value="&quote-sec.label;"/>
+ <separator class="groove"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="quote-cite">
+ <separator orient="vertical"/>
+ <label value="&quote-cite.label; " control="quote-cite-text"/>
+ <textbox readonly="true" id="quote-cite-text" class="meta-properties"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ </vbox>
+ <vbox id="misc-sec">
+ <label value="&misc-sec.label;"/>
+ <separator class="groove"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="misc-lang">
+ <separator orient="vertical"/>
+ <label value="&misc-lang.label; " control="misc-lang-text"/>
+ <textbox readonly="true" id="misc-lang-text" class="meta-properties"/>
+ </row>
+ <row id="misc-title">
+ <separator orient="vertical"/>
+ <label value="&misc-title.label; " control="misc-title-text"/>
+ <textbox readonly="true" id="misc-title-text" class="meta-properties"/>
+ </row>
+ <row id="misc-tblsummary">
+ <separator orient="vertical"/>
+ <label value="&misc-tblsummary.label; " control="misc-tblsummary-text"/>
+ <textbox readonly="true" id="misc-tblsummary-text" class="meta-properties"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ </vbox>
+ </dialog>
diff --git a/comm/suite/browser/moz.build b/comm/suite/browser/moz.build
new file mode 100644
index 0000000000..6740f038ae
--- /dev/null
+++ b/comm/suite/browser/moz.build
@@ -0,0 +1,22 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+BROWSER_CHROME_MANIFESTS += ["test/browser/browser.ini"]
+MOCHITEST_CHROME_MANIFESTS += ["test/chrome/chrome.ini"]
+MOCHITEST_MANIFESTS += ["test/mochitest/mochitest.ini"]
+
+EXTRA_COMPONENTS += [
+ "nsBrowserContentHandler.js",
+ "nsTypeAheadFind.js",
+ "SuiteBrowser.manifest",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
+
+for var in ("MOZ_APP_NAME", "MOZ_APP_DISPLAYNAME", "MOZ_APP_VERSION"):
+ DEFINES[var] = '"%s"' % CONFIG[var]
+
+if CONFIG["MOZILLA_OFFICIAL"]:
+ DEFINES["OFFICIAL_BUILD"] = 1
diff --git a/comm/suite/browser/navigator.css b/comm/suite/browser/navigator.css
new file mode 100644
index 0000000000..8b9c11ada3
--- /dev/null
+++ b/comm/suite/browser/navigator.css
@@ -0,0 +1,135 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* ::::: Hide the link toolbar if it is set to autohide and has no items. ::::: */
+
+#linktoolbar[hidden="maybe"][hasitems="false"] {
+ display: none;
+}
+
+/* ::::: tabbed browser ::::: */
+
+tabbrowser {
+ -moz-binding: url("chrome://navigator/content/tabbrowser.xml#tabbrowser");
+}
+
+.tabbrowser-tabs {
+ -moz-binding: url("chrome://navigator/content/tabbrowser.xml#tabbrowser-tabs");
+}
+
+.tabbrowser-tab:-moz-lwtheme:not([customization-lwtheme]) {
+ color: var(--lwt-text-color);
+ text-shadow: var(--lwt-accent-color);
+}
+
+.tabbrowser-arrowscrollbox {
+ -moz-binding: url("chrome://navigator/content/tabbrowser.xml#tabbrowser-arrowscrollbox");
+}
+
+.tabs-alltabs-popup {
+ -moz-binding: url("chrome://navigator/content/tabbrowser.xml#tabbrowser-alltabs-popup");
+}
+
+.tabs-closebutton-box > .tabs-closebutton {
+ -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton");
+}
+
+/* ::::: search suggestions autocomplete ::::: */
+#PopupAutoComplete > richlistbox > richlistitem > .ac-type-icon,
+#PopupAutoComplete > richlistbox > richlistitem > .ac-site-icon,
+#PopupAutoComplete > richlistbox > richlistitem > .ac-tags,
+#PopupAutoComplete > richlistbox > richlistitem > .ac-separator,
+#PopupAutoComplete > richlistbox > richlistitem > .ac-url {
+ display: none;
+}
+
+/* ::::: urlbar autocomplete ::::: */
+
+#urlbar {
+ -moz-binding: url("chrome://navigator/content/urlbarBindings.xml#urlbar");
+}
+
+.paste-and-go {
+ -moz-binding: url("chrome://navigator/content/urlbarBindings.xml#input-box-paste");
+}
+
+panel[for="urlbar"] {
+ -moz-binding: url("chrome://navigator/content/urlbarBindings.xml#autocomplete-result-popup") !important;
+}
+
+.autocomplete-search-box {
+ -moz-binding: url("chrome://navigator/content/urlbarBindings.xml#autocomplete-search-box");
+}
+
+.autocomplete-search-engine {
+ -moz-binding: url("chrome://navigator/content/urlbarBindings.xml#autocomplete-search-engine");
+ -moz-box-align: center;
+}
+
+#DateTimePickerPanel[active="true"] {
+ -moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup");
+}
+
+.popup-anchor {
+ /* should occupy space but not be visible */
+ opacity: 0;
+ pointer-events: none;
+ -moz-stack-sizing: ignore;
+}
+
+/* ::::: search bar ::::: */
+searchbar {
+ -moz-binding: url("chrome://communicator/content/search/search.xml#searchbar");
+}
+
+#wrapper-search-container > #search-container > #searchbar > .searchbar-textbox > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input {
+ visibility: hidden;
+}
+
+/* ::::: bookmarks menu ::::: */
+
+.isempty:not(:last-child) {
+ display: none;
+}
+
+/* ::::: bookmarks toolbar ::::: */
+
+#wrapper-personal-bookmarks[place="palette"] > toolbaritem > #PlacesToolbar {
+ display: none;
+}
+
+/* notification anchors should only be visible when their associated
+ notifications are */
+.notification-anchor-icon {
+ -moz-user-focus: normal;
+}
+
+.notification-anchor-icon:not([showing]) {
+ display: none;
+}
+
+popupnotification-centeritem {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#center-item");
+}
+
+#addon-install-started-notification {
+ -moz-binding: url("chrome://communicator/content/bindings/notification.xml#addon-progress-popup-notification");
+}
+
+/* ::::: Wallpaper fix for Bug 435652. Remove when Bug 204743 is fixed ::::: */
+
+.textbox-input-box {
+ overflow-x: hidden;
+}
+
+/* Developer Tools: Error counter */
+
+#developer-toolbar-toolbox-button[error-count]:before {
+ content: attr(error-count);
+ display: -moz-box;
+ -moz-box-pack: center;
+}
diff --git a/comm/suite/browser/navigator.js b/comm/suite/browser/navigator.js
new file mode 100644
index 0000000000..2321906fb9
--- /dev/null
+++ b/comm/suite/browser/navigator.js
@@ -0,0 +1,3311 @@
+/* -*- 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {AeroPeek} = ChromeUtils.import("resource:///modules/WindowsPreviewPerTab.jsm");
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+ PluralForm: "resource://gre/modules/PluralForm.jsm",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
+ PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
+ SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
+ SitePermissions: "resource:///modules/SitePermissions.jsm",
+});
+
+XPCOMUtils.defineLazyScriptGetter(this, "gEditItemOverlay",
+ "chrome://communicator/content/places/editBookmarkOverlay.js");
+
+const REMOTESERVICE_CONTRACTID = "@mozilla.org/toolkit/remote-service;1";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const nsIWebNavigation = Ci.nsIWebNavigation;
+
+var gPrintSettingsAreGlobal = true;
+var gSavePrintSettings = true;
+var gChromeState = null; // chrome state before we went into print preview
+var gInPrintPreviewMode = false;
+var gNavToolbox = null;
+var gFindInstData;
+var gURLBar = null;
+var gProxyButton = null;
+var gProxyFavIcon = null;
+var gProxyDeck = null;
+var gNavigatorBundle;
+var gBrandBundle;
+var gNavigatorRegionBundle;
+var gLastValidURLStr = "";
+var gLastValidURL = null;
+var gClickSelectsAll = false;
+var gClickAtEndSelects = false;
+var gIgnoreFocus = false;
+var gIgnoreClick = false;
+
+// Listeners for updating zoom value in status bar
+var ZoomListeners =
+{
+
+ // Identifies the setting in the content prefs database.
+ name: "browser.content.full-zoom",
+
+ QueryInterface:
+ XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIContentPrefObserver,
+ Ci.nsIContentPrefCallback2,
+ Ci.nsISupportsWeakReference,
+ Ci.nsISupports]),
+
+ init: function ()
+ {
+ Cc["@mozilla.org/content-pref/service;1"]
+ .getService(Ci.nsIContentPrefService2)
+ .addObserverForName(this.name, this);
+
+ Services.prefs.addObserver("browser.zoom.", this, true);
+ this.updateVisibility();
+ },
+
+ destroy: function ()
+ {
+ Cc["@mozilla.org/content-pref/service;1"]
+ .getService(Ci.nsIContentPrefService2)
+ .removeObserverForName(this.name, this);
+
+ Services.prefs.removeObserver("browser.zoom.", this);
+ },
+
+ observe: function (aSubject, aTopic, aData)
+ {
+ if (aTopic == "nsPref:changed"){
+ switch (aData) {
+ case "browser.zoom.siteSpecific":
+ updateZoomStatus();
+ break;
+ case "browser.zoom.showZoomStatusPanel":
+ this.updateVisibility();
+ break;
+ }
+ }
+ },
+
+ updateVisibility: function()
+ {
+ let hidden = !Services.prefs.getBoolPref("browser.zoom.showZoomStatusPanel");
+ document.getElementById("zoomOut-button").setAttribute('hidden', hidden);
+ document.getElementById("zoomIn-button").setAttribute('hidden', hidden);
+ document.getElementById("zoomLevel-display").setAttribute('hidden', hidden);
+ },
+
+ onContentPrefSet: function (aGroup, aName, aValue)
+ {
+ // Make sure zoom values loaded before updating
+ window.setTimeout(updateZoomStatus, 0);
+ },
+
+ onContentPrefRemoved: function (aGroup, aName)
+ {
+ // Make sure zoom values loaded before updating
+ window.setTimeout(updateZoomStatus, 0);
+ },
+
+ handleResult: function(aPref)
+ {
+ updateZoomStatus();
+ },
+
+ onLocationChange: function(aURI)
+ {
+ updateZoomStatus();
+ }
+};
+
+var gInitialPages = new Set([
+ "about:blank",
+ "about:privatebrowsing",
+ "about:sessionrestore"
+]);
+
+//cached elements
+var gBrowser = null;
+
+var gTabStripPrefListener =
+{
+ domain: "browser.tabs.autoHide",
+ observe: function(subject, topic, prefName)
+ {
+ // verify that we're changing the tab browser strip auto hide pref
+ if (topic != "nsPref:changed")
+ return;
+
+ if (gBrowser.tabContainer.childNodes.length == 1 && window.toolbar.visible) {
+ var stripVisibility = !Services.prefs.getBoolPref(prefName);
+ gBrowser.setStripVisibilityTo(stripVisibility);
+ Services.prefs.setBoolPref("browser.tabs.forceHide", false);
+ }
+ }
+};
+
+var gHomepagePrefListener =
+{
+ domain: "browser.startup.homepage",
+ observe: function(subject, topic, prefName)
+ {
+ // verify that we're changing the home page pref
+ if (topic != "nsPref:changed")
+ return;
+
+ updateHomeButtonTooltip();
+ }
+};
+
+var gStatusBarPopupIconPrefListener =
+{
+ domain: "privacy.popups.statusbar_icon_enabled",
+ observe: function(subject, topic, prefName)
+ {
+ if (topic != "nsPref:changed" || prefName != this.domain)
+ return;
+
+ var popupIcon = document.getElementById("popupIcon");
+ if (!Services.prefs.getBoolPref(prefName))
+ popupIcon.hidden = true;
+
+ else if (gBrowser.getNotificationBox().popupCount)
+ popupIcon.hidden = false;
+ }
+};
+
+var gFormSubmitObserver = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver,
+ Ci.nsIObserver]),
+
+ panel: null,
+
+ init: function()
+ {
+ this.panel = document.getElementById("invalid-form-popup");
+ },
+
+ notifyInvalidSubmit: function (aFormElement, aInvalidElements)
+ {
+ // We are going to handle invalid form submission attempt by focusing the
+ // first invalid element and show the corresponding validation message in a
+ // panel attached to the element.
+ if (!aInvalidElements.length) {
+ return;
+ }
+
+ // Don't show the popup if the current tab doesn't contain the invalid form.
+ if (aFormElement.ownerDocument.defaultView.top != content) {
+ return;
+ }
+
+ let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
+
+ if (!(element instanceof HTMLInputElement ||
+ element instanceof HTMLTextAreaElement ||
+ element instanceof HTMLSelectElement ||
+ element instanceof HTMLButtonElement)) {
+ return;
+ }
+
+ this.panel.firstChild.textContent = element.validationMessage;
+
+ element.focus();
+
+ // If the user interacts with the element and makes it valid or leaves it,
+ // we want to remove the popup.
+ // We could check for clicks but a click already removes the popup.
+ function blurHandler() {
+ gFormSubmitObserver.panel.hidePopup();
+ }
+ function inputHandler(e) {
+ if (e.originalTarget.validity.valid) {
+ gFormSubmitObserver.panel.hidePopup();
+ } else {
+ // If the element is now invalid for a new reason, we should update the
+ // error message.
+ if (gFormSubmitObserver.panel.firstChild.textContent !=
+ e.originalTarget.validationMessage) {
+ gFormSubmitObserver.panel.firstChild.textContent =
+ e.originalTarget.validationMessage;
+ }
+ }
+ }
+ element.addEventListener("input", inputHandler);
+ element.addEventListener("blur", blurHandler);
+
+ // One event to bring them all and in the darkness bind them.
+ this.panel.addEventListener("popuphiding", function popupHidingHandler(aEvent) {
+ aEvent.target.removeEventListener("popuphiding", popupHidingHandler);
+ element.removeEventListener("input", inputHandler);
+ element.removeEventListener("blur", blurHandler);
+ });
+
+ this.panel.hidden = false;
+
+ var win = element.ownerDocument.defaultView;
+ var style = win.getComputedStyle(element, null);
+ var scale = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .fullZoom;
+
+ var offset = style.direction == 'rtl' ? parseInt(style.paddingRight) +
+ parseInt(style.borderRightWidth) :
+ parseInt(style.paddingLeft) +
+ parseInt(style.borderLeftWidth);
+
+ offset = Math.round(offset * scale);
+ this.panel.openPopup(element, "after_start", offset, 0);
+ }
+};
+
+/**
+* Pref listener handler functions.
+* Both functions assume that observer.domain is set to
+* the pref domain we want to start/stop listening to.
+*/
+function addPrefListener(observer)
+{
+ try {
+ Services.prefs.addObserver(observer.domain, observer);
+ } catch(ex) {
+ dump("Failed to observe prefs: " + ex + "\n");
+ }
+}
+
+function removePrefListener(observer)
+{
+ try {
+ Services.prefs.removeObserver(observer.domain, observer);
+ } catch(ex) {
+ dump("Failed to remove pref observer: " + ex + "\n");
+ }
+}
+
+function addPopupPermListener(observer)
+{
+ Services.obs.addObserver(observer, "popup-perm-close");
+}
+
+function removePopupPermListener(observer)
+{
+ Services.obs.removeObserver(observer, "popup-perm-close");
+}
+
+function addFormSubmitObserver(observer)
+{
+ observer.init();
+ Services.obs.addObserver(observer, "invalidformsubmit");
+}
+
+function removeFormSubmitObserver(observer)
+{
+ Services.obs.removeObserver(observer, "invalidformsubmit");
+}
+
+/**
+* We can avoid adding multiple load event listeners and save some time by adding
+* one listener that calls all real handlers.
+*/
+
+function pageShowEventHandlers(event)
+{
+ checkForDirectoryListing();
+}
+
+/**
+ * Determine whether or not the content area is displaying a page with frames,
+ * and if so, toggle the display of the 'save frame as' menu item.
+ **/
+function getContentAreaFrameCount()
+{
+ var saveFrameItem = document.getElementById("saveframe");
+ if (!content || !content.frames.length || !isContentFrame(document.commandDispatcher.focusedWindow))
+ saveFrameItem.setAttribute("hidden", "true");
+ else {
+ var autoDownload = Services.prefs.getBoolPref("browser.download.useDownloadDir");
+ goSetMenuValue("saveframe", autoDownload ? "valueSave" : "valueSaveAs");
+ saveFrameItem.removeAttribute("hidden");
+ }
+}
+
+function saveFrameDocument()
+{
+ var focusedWindow = document.commandDispatcher.focusedWindow;
+ if (isContentFrame(focusedWindow))
+ saveDocument(focusedWindow.document, true);
+}
+
+function updateHomeButtonTooltip()
+{
+ var homePage = getHomePage();
+ var tooltip = document.getElementById("home-button-tooltip-inner");
+
+ while (tooltip.hasChildNodes())
+ tooltip.lastChild.remove();
+
+ for (var i in homePage) {
+ var label = document.createElementNS(XUL_NS, "label");
+ label.setAttribute("value", homePage[i]);
+ tooltip.appendChild(label);
+ }
+}
+function getWebNavigation()
+{
+ try {
+ return getBrowser().webNavigation;
+ } catch (e) {
+ return null;
+ }
+}
+
+function BrowserReloadWithFlags(reloadFlags)
+{
+ // Reset temporary permissions on the current tab. This is done here
+ // because we only want to reset permissions on user reload.
+ SitePermissions.clearTemporaryPermissions(gBrowser.selectedBrowser);
+
+ // First, we'll try to use the session history object to reload so
+ // that framesets are handled properly. If we're in a special
+ // window (such as view-source) that has no session history, fall
+ // back on using the web navigation's reload method.
+ let webNav = getWebNavigation();
+ try {
+ let sh = webNav.sessionHistory;
+ if (sh)
+ webNav = sh.QueryInterface(Components.interfaces.nsIWebNavigation);
+ } catch (e) {
+ }
+
+ try {
+ webNav.reload(reloadFlags);
+ } catch (e) {
+ }
+}
+
+function toggleAffectedChrome(aHide)
+{
+ // chrome to toggle includes:
+ // (*) menubar
+ // (*) navigation bar
+ // (*) personal toolbar
+ // (*) tab browser ``strip''
+ // (*) sidebar
+ // (*) statusbar
+ // (*) findbar
+
+ if (!gChromeState)
+ gChromeState = new Object;
+
+ var statusbar = document.getElementById("status-bar");
+ getNavToolbox().hidden = aHide;
+ var notificationBox = gBrowser.getNotificationBox();
+ var findbar = document.getElementById("FindToolbar")
+
+ // sidebar states map as follows:
+ // hidden => hide/show nothing
+ // collapsed => hide/show only the splitter
+ // shown => hide/show the splitter and the box
+ if (aHide)
+ {
+ // going into print preview mode
+ gChromeState.sidebar = SidebarGetState();
+ SidebarSetState("hidden");
+
+ // deal with tab browser
+ gBrowser.mStrip.setAttribute("moz-collapsed", "true");
+
+ // deal with the Status Bar
+ gChromeState.statusbarWasHidden = statusbar.hidden;
+ statusbar.hidden = true;
+
+ // deal with the notification box
+ gChromeState.notificationsWereHidden = notificationBox.notificationsHidden;
+ notificationBox.notificationsHidden = true;
+
+ if (findbar)
+ {
+ gChromeState.findbarWasHidden = findbar.hidden;
+ findbar.close();
+ }
+ else
+ {
+ gChromeState.findbarWasHidden = true;
+ }
+
+ gChromeState.syncNotificationsOpen = false;
+ var syncNotifications = document.getElementById("sync-notifications");
+ if (syncNotifications)
+ {
+ gChromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
+ syncNotifications.notificationsHidden = true;
+ }
+ }
+ else
+ {
+ // restoring normal mode (i.e., leaving print preview mode)
+ SidebarSetState(gChromeState.sidebar);
+
+ // restore tab browser
+ gBrowser.mStrip.removeAttribute("moz-collapsed");
+
+ // restore the Status Bar
+ statusbar.hidden = gChromeState.statusbarWasHidden;
+
+ // restore the notification box
+ notificationBox.notificationsHidden = gChromeState.notificationsWereHidden;
+
+ if (!gChromeState.findbarWasHidden)
+ findbar.open();
+
+ if (gChromeState.syncNotificationsOpen)
+ document.getElementById("sync-notifications").notificationsHidden = false;
+ }
+
+ // if we are unhiding and sidebar used to be there rebuild it
+ if (!aHide && gChromeState.sidebar == "visible")
+ SidebarRebuild();
+}
+
+var PrintPreviewListener = {
+ _printPreviewTab: null,
+ _tabBeforePrintPreview: null,
+
+ getPrintPreviewBrowser: function () {
+ if (!this._printPreviewTab) {
+ this._tabBeforePrintPreview = getBrowser().selectedTab;
+ this._printPreviewTab = getBrowser().addTab("about:blank");
+ getBrowser().selectedTab = this._printPreviewTab;
+ }
+ return getBrowser().getBrowserForTab(this._printPreviewTab);
+ },
+ getSourceBrowser: function () {
+ return this._tabBeforePrintPreview ?
+ getBrowser().getBrowserForTab(this._tabBeforePrintPreview) :
+ getBrowser().selectedBrowser;
+ },
+ getNavToolbox: function () {
+ return window.getNavToolbox();
+ },
+ onEnter: function () {
+ gInPrintPreviewMode = true;
+ toggleAffectedChrome(true);
+ },
+ onExit: function () {
+ getBrowser().selectedTab = this._tabBeforePrintPreview;
+ this._tabBeforePrintPreview = null;
+ gInPrintPreviewMode = false;
+ toggleAffectedChrome(false);
+ getBrowser().removeTab(this._printPreviewTab, { disableUndo: true });
+ this._printPreviewTab = null;
+ }
+};
+
+function getNavToolbox()
+{
+ if (!gNavToolbox)
+ gNavToolbox = document.getElementById("navigator-toolbox");
+ return gNavToolbox;
+}
+
+function BrowserPrintPreview()
+{
+ PrintUtils.printPreview(PrintPreviewListener);
+}
+
+function BrowserSetCharacterSet(aEvent)
+{
+ if (aEvent.target.hasAttribute("charset"))
+ getBrowser().docShell.charset = aEvent.target.getAttribute("charset");
+ BrowserCharsetReload();
+}
+
+function BrowserCharsetReload()
+{
+ BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
+}
+
+function BrowserUpdateCharsetMenu(aNode)
+{
+ var wnd = document.commandDispatcher.focusedWindow;
+ if (wnd.top != content)
+ wnd = content;
+ UpdateCharsetMenu(wnd.document.characterSet, aNode);
+}
+
+function EnableCharsetMenu()
+{
+ var menuitem = document.getElementById("charsetMenu");
+ if (getBrowser() && getBrowser().docShell &&
+ getBrowser().docShell.mayEnableCharacterEncodingMenu)
+ menuitem.removeAttribute("disabled");
+ else
+ menuitem.setAttribute("disabled", "true");
+}
+
+function getFindInstData()
+{
+ if (!gFindInstData) {
+ gFindInstData = new nsFindInstData();
+ gFindInstData.browser = getBrowser();
+ // defaults for rootSearchWindow and currentSearchWindow are fine here
+ }
+ return gFindInstData;
+}
+
+function BrowserFind()
+{
+ findInPage(getFindInstData());
+}
+
+function BrowserFindAgain(reverse)
+{
+ findAgainInPage(getFindInstData(), reverse);
+}
+
+function BrowserCanFindAgain()
+{
+ return canFindAgainInPage();
+}
+
+function getMarkupDocumentViewer()
+{
+ return getBrowser().markupDocumentViewer;
+}
+
+function getBrowser()
+{
+ if (!gBrowser)
+ gBrowser = document.getElementById("content");
+ return gBrowser;
+}
+
+function getHomePage()
+{
+ var URIs = [];
+ URIs[0] = GetLocalizedStringPref("browser.startup.homepage");
+ var count = Services.prefs.getIntPref("browser.startup.homepage.count");
+ for (var i = 1; i < count; ++i)
+ URIs[i] = GetLocalizedStringPref("browser.startup.homepage." + i);
+
+ return URIs;
+}
+
+function UpdateBackForwardButtons()
+{
+ var backBroadcaster = document.getElementById("canGoBack");
+ var forwardBroadcaster = document.getElementById("canGoForward");
+ var upBroadcaster = document.getElementById("canGoUp");
+ var browser = getBrowser();
+
+ // Avoid setting attributes on broadcasters if the value hasn't changed!
+ // Remember, guys, setting attributes on elements is expensive! They
+ // get inherited into anonymous content, broadcast to other widgets, etc.!
+ // Don't do it if the value hasn't changed! - dwh
+
+ var backDisabled = backBroadcaster.hasAttribute("disabled");
+ var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
+ var upDisabled = upBroadcaster.hasAttribute("disabled");
+ if (backDisabled == browser.canGoBack) {
+ if (backDisabled)
+ backBroadcaster.removeAttribute("disabled");
+ else
+ backBroadcaster.setAttribute("disabled", true);
+ }
+ if (forwardDisabled == browser.canGoForward) {
+ if (forwardDisabled)
+ forwardBroadcaster.removeAttribute("disabled");
+ else
+ forwardBroadcaster.setAttribute("disabled", true);
+ }
+ if (upDisabled != !browser.currentURI.spec.replace(/[#?].*$/, "").match(/\/[^\/]+\/./)) {
+ if (upDisabled)
+ upBroadcaster.removeAttribute("disabled");
+ else
+ upBroadcaster.setAttribute("disabled", true);
+ }
+}
+
+const nsIBrowserDOMWindow = Ci.nsIBrowserDOMWindow;
+const nsIInterfaceRequestor = Ci.nsIInterfaceRequestor;
+
+function nsBrowserAccess() {
+}
+
+nsBrowserAccess.prototype = {
+ createContentWindow(aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal = null) {
+ return this.getContentWindowOrOpenURI(null, aOpener, aWhere, aFlags,
+ aTriggeringPrincipal);
+ },
+
+ openURI: function (aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal = null) {
+ if (!aURI) {
+ Cu.reportError("openURI should only be called with a valid URI");
+ throw Cr.NS_ERROR_FAILURE;
+ }
+ return this.getContentWindowOrOpenURI(aURI, aOpener, aWhere, aFlags,
+ aTriggeringPrincipal);
+ },
+
+ getContentWindowOrOpenURI(aURI, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
+ var isExternal = !!(aFlags & nsIBrowserDOMWindow.OPEN_EXTERNAL);
+
+ if (aOpener && isExternal) {
+ Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " +
+ "passed if the context is OPEN_EXTERNAL.");
+ throw Cr.NS_ERROR_FAILURE;
+ }
+
+ if (aWhere == nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
+ if (isExternal)
+ aWhere = Services.prefs.getIntPref("browser.link.open_external");
+ else
+ aWhere = Services.prefs.getIntPref("browser.link.open_newwindow");
+ }
+
+ let referrer = aOpener ? aOpener.QueryInterface(nsIInterfaceRequestor)
+ .getInterface(nsIWebNavigation)
+ .currentURI : null;
+ let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET;
+ if (aOpener && aOpener.document) {
+ referrerPolicy = aOpener.document.referrerPolicy;
+ }
+ var uri = aURI ? aURI.spec : "about:blank";
+
+ switch (aWhere) {
+ case nsIBrowserDOMWindow.OPEN_NEWWINDOW:
+ return window.openDialog(getBrowserURL(), "_blank", "all,dialog=no",
+ uri, null, null);
+ case nsIBrowserDOMWindow.OPEN_NEWTAB:
+ var bgLoad = Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
+ var isRelated = referrer ? true : false;
+ // If we have an opener, that means that the caller is expecting access
+ // to the nsIDOMWindow of the opened tab right away.
+ let userContextId = aOpener && aOpener.document
+ ? aOpener.document.nodePrincipal.originAttributes.userContextId
+ : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID;
+ let openerWindow = (aFlags & nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener;
+
+ var newTab = gBrowser.loadOneTab(uri, {triggeringPrincipal: aTriggeringPrincipal,
+ referrerURI: referrer,
+ referrerPolicy,
+ inBackground: bgLoad,
+ fromExternal: isExternal,
+ relatedToCurrent: isRelated,
+ userContextId: userContextId,
+ opener: openerWindow,
+ });
+ var contentWin = gBrowser.getBrowserForTab(newTab).contentWindow;
+ if (!bgLoad)
+ contentWin.focus();
+ return contentWin;
+ default:
+ var loadflags = isExternal ?
+ nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
+ nsIWebNavigation.LOAD_FLAGS_NONE;
+
+ if (!aOpener) {
+ if (aURI) {
+ gBrowser.loadURIWithFlags(aURI.spec, {
+ flags: loadflags,
+ referrerURI: referrer,
+ referrerPolicy,
+ triggeringPrincipal: aTriggeringPrincipal,
+ });
+ }
+ return content;
+ }
+ aOpener = aOpener.top;
+ if (aURI) {
+ try {
+ aOpener.QueryInterface(nsIInterfaceRequestor)
+ .getInterface(nsIWebNavigation)
+ .loadURI(uri, loadflags, referrer, null, null,
+ aTriggeringPrincipal);
+ } catch (e) {}
+ }
+ return aOpener;
+ }
+ },
+ isTabContentWindow: function isTabContentWindow(aWindow) {
+ return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
+ }
+}
+
+function HandleAppCommandEvent(aEvent)
+{
+ aEvent.stopPropagation();
+ switch (aEvent.command) {
+ case "Back":
+ BrowserBack();
+ break;
+ case "Forward":
+ BrowserForward();
+ break;
+ case "Reload":
+ BrowserReloadSkipCache();
+ break;
+ case "Stop":
+ BrowserStop();
+ break;
+ case "Search":
+ BrowserSearch.webSearch();
+ break;
+ case "Bookmarks":
+ toBookmarksManager();
+ break;
+ case "Home":
+ BrowserHome(null);
+ break;
+ default:
+ break;
+ }
+}
+
+/* window.arguments[0]: URL(s) to load
+ (string, with one or more URLs separated by \n)
+ * [1]: character set (string)
+ * [2]: referrer (nsIURI)
+ * [3]: postData (nsIInputStream)
+ * [4]: allowThirdPartyFixup (bool)
+ */
+function Startup()
+{
+ // init globals
+ gNavigatorBundle = document.getElementById("bundle_navigator");
+ gBrandBundle = document.getElementById("bundle_brand");
+ gNavigatorRegionBundle = document.getElementById("bundle_navigator_region");
+
+ gBrowser = document.getElementById("content");
+ gURLBar = document.getElementById("urlbar");
+
+ SetPageProxyState("invalid", null);
+
+ var webNavigation;
+ try {
+ webNavigation = getWebNavigation();
+ if (!webNavigation)
+ throw "no XBL binding for browser";
+ } catch (e) {
+ alert("Error launching browser window:" + e);
+ window.close(); // Give up.
+ return;
+ }
+
+ // Do all UI building here:
+ UpdateNavBar();
+ updateWindowState();
+
+ // set home button tooltip text
+ updateHomeButtonTooltip();
+
+ var lc = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+ if (lc.usePrivateBrowsing) {
+ gPrivate = window;
+ let docElement = document.documentElement;
+ var titlemodifier = docElement.getAttribute("titlemodifier");
+ if (titlemodifier)
+ titlemodifier += " ";
+ titlemodifier += docElement.getAttribute("titleprivate");
+ docElement.setAttribute("titlemodifier", titlemodifier);
+ document.title = titlemodifier;
+ docElement.setAttribute("privatebrowsingmode",
+ PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary");
+ }
+
+ // initialize observers and listeners
+ var xw = lc.QueryInterface(Ci.nsIDocShellTreeItem)
+ .treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow);
+ xw.XULBrowserWindow = window.XULBrowserWindow = new nsBrowserStatusHandler();
+
+ if (!window.content.opener &&
+ Services.prefs.getBoolPref("browser.doorhanger.enabled")) {
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/PopupNotifications.jsm", tmp);
+ window.PopupNotifications = new tmp.PopupNotifications(
+ getBrowser(),
+ document.getElementById("notification-popup"),
+ document.getElementById("notification-popup-box"));
+ // Setting the popup notification attribute causes the XBL to bind
+ // and call the constructor again, so we have to destroy it first.
+ gBrowser.getNotificationBox().destroy();
+ gBrowser.setAttribute("popupnotification", "true");
+ // The rebind also resets popup window scrollbar visibility, so override it.
+ if (!(xw.chromeFlags & Ci.nsIWebBrowserChrome.CHROME_SCROLLBARS))
+ gBrowser.selectedBrowser.style.overflow = "hidden";
+ }
+
+ addPrefListener(gTabStripPrefListener);
+ addPrefListener(gHomepagePrefListener);
+ addPrefListener(gStatusBarPopupIconPrefListener);
+ addFormSubmitObserver(gFormSubmitObserver);
+
+ window.browserContentListener =
+ new nsBrowserContentListener(window, getBrowser());
+
+ // Add a capturing event listener to the content area
+ // (rjc note: not the entire window, otherwise we'll get sidebar pane loads too!)
+ // so we'll be notified when onloads complete.
+ var contentArea = document.getElementById("appcontent");
+ contentArea.addEventListener("pageshow", function callPageShowHandlers(aEvent) {
+ // Filter out events that are not about the document load we are interested in.
+ if (aEvent.originalTarget == content.document)
+ setTimeout(pageShowEventHandlers, 0, aEvent);
+ }, true);
+
+ // Set a sane starting width/height for all resolutions on new profiles.
+ if (!document.documentElement.hasAttribute("width")) {
+ var defaultHeight = screen.availHeight;
+ var defaultWidth= screen.availWidth;
+
+ // Create a narrower window for large or wide-aspect displays, to suggest
+ // side-by-side page view.
+ if (screen.availWidth >= 1440)
+ defaultWidth /= 2;
+
+ // Tweak sizes to be sure we don't grow outside the screen
+ defaultWidth = defaultWidth - 20;
+ defaultHeight = defaultHeight - 10;
+
+ // On X, we're not currently able to account for the size of the window
+ // border. Use 28px as a guess (titlebar + bottom window border)
+ if (navigator.appVersion.includes("X11"))
+ defaultHeight -= 28;
+
+ // On small screens, default to maximized state
+ if (defaultHeight <= 600)
+ document.documentElement.setAttribute("sizemode", "maximized");
+
+ document.documentElement.setAttribute("width", defaultWidth);
+ document.documentElement.setAttribute("height", defaultHeight);
+ // Make sure we're safe at the left/top edge of screen
+ document.documentElement.setAttribute("screenX", screen.availLeft);
+ document.documentElement.setAttribute("screenY", screen.availTop);
+ }
+
+ // hook up UI through progress listener
+ getBrowser().addProgressListener(window.XULBrowserWindow);
+ // setup the search service DOMLinkAdded listener
+ getBrowser().addEventListener("DOMLinkAdded", BrowserSearch);
+ // hook up drag'n'drop
+ getBrowser().droppedLinkHandler = handleDroppedLink;
+
+ var uriToLoad = "";
+
+ // Check window.arguments[0]. If not null then use it for uriArray
+ // otherwise the new window is being called when another browser
+ // window already exists so use the New Window pref for uriArray
+ if ("arguments" in window && window.arguments.length >= 1) {
+ var uriArray;
+ if (window.arguments[0]) {
+ uriArray = window.arguments[0].toString().split('\n'); // stringify and split
+ } else {
+ switch (Services.prefs.getIntPref("browser.windows.loadOnNewWindow", 0))
+ {
+ default:
+ uriArray = ["about:blank"];
+ break;
+ case 1:
+ uriArray = getHomePage();
+ break;
+ case 2:
+ uriArray = [Services.prefs.getStringPref("browser.history.last_page_visited", "")];
+ break;
+ }
+ }
+ uriToLoad = uriArray.splice(0, 1)[0];
+
+ if (uriArray.length > 0)
+ window.setTimeout(function(arg) { for (var i in arg) gBrowser.addTab(arg[i]); }, 0, uriArray);
+ }
+
+ if (/^\s*$/.test(uriToLoad))
+ uriToLoad = "about:blank";
+
+ var browser = getBrowser();
+
+ if (uriToLoad != "about:blank") {
+ if (!gInitialPages.has(uriToLoad)) {
+ gURLBar.value = uriToLoad;
+ browser.userTypedValue = uriToLoad;
+ }
+
+ if ("arguments" in window && window.arguments.length >= 3) {
+ // window.arguments[2]: referrer (nsIURI | string)
+ // [3]: postData (nsIInputStream)
+ // [4]: allowThirdPartyFixup (bool)
+ // [5]: referrerPolicy (int)
+ // [6]: userContextId (int)
+ // [7]: originPrincipal (nsIPrincipal)
+ // [8]: triggeringPrincipal (nsIPrincipal)
+ let referrerURI = window.arguments[2];
+ if (typeof(referrerURI) == "string") {
+ try {
+ referrerURI = makeURI(referrerURI);
+ } catch (e) {
+ referrerURI = null;
+ }
+ }
+ let referrerPolicy = (window.arguments[5] != undefined ?
+ window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
+
+ let userContextId = (window.arguments[6] != undefined ?
+ window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
+ loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
+ window.arguments[4] || false, referrerPolicy, userContextId,
+ // pass the origin principal (if any) and force its use to create
+ // an initial about:blank viewer if present:
+ window.arguments[7], !!window.arguments[7], window.arguments[8]);
+ } else {
+ // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
+ // Such callers expect that window.arguments[0] is handled as a single URI.
+ loadOneOrMoreURIs(uriToLoad, Services.scriptSecurityManager.getSystemPrincipal());
+ }
+ }
+
+ // Focus content area unless we're loading a blank or other initial
+ // page, or if we weren't passed any arguments. This "breaks" the
+ // javascript:window.open(); case where we don't get any arguments
+ // either, but we're loading about:blank, but focusing the content
+ // area is arguably correct in that case as well since the opener
+ // is very likely to put some content in the new window, and then
+ // the focus should be in the content area.
+ var navBar = document.getElementById("nav-bar");
+ if ("arguments" in window && gInitialPages.has(uriToLoad) &&
+ isElementVisible(gURLBar))
+ setTimeout(WindowFocusTimerCallback, 0, gURLBar);
+ else
+ setTimeout(WindowFocusTimerCallback, 0, content);
+
+ // hook up browser access support
+ window.browserDOMWindow = new nsBrowserAccess();
+
+ // hook up remote support
+ if (!gPrivate && REMOTESERVICE_CONTRACTID in Cc) {
+ var remoteService =
+ Cc[REMOTESERVICE_CONTRACTID]
+ .getService(Ci.nsIRemoteService);
+ remoteService.registerWindow(window);
+ }
+
+ // ensure login manager is loaded
+ Cc["@mozilla.org/login-manager;1"].getService();
+
+ // called when we go into full screen, even if it is
+ // initiated by a web page script
+ addEventListener("fullscreen", onFullScreen, true);
+
+ addEventListener("PopupCountChanged", UpdateStatusBarPopupIcon, true);
+
+ addEventListener("AppCommand", HandleAppCommandEvent, true);
+
+ addEventListener("sizemodechange", updateWindowState, false);
+
+ // does clicking on the urlbar select its contents?
+ gClickSelectsAll = Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll");
+ gClickAtEndSelects = Services.prefs.getBoolPref("browser.urlbar.clickAtEndSelects");
+
+ // BiDi UI
+ gShowBiDi = isBidiEnabled();
+ if (gShowBiDi) {
+ document.getElementById("documentDirection-swap").hidden = false;
+ document.getElementById("textfieldDirection-separator").hidden = false;
+ document.getElementById("textfieldDirection-swap").hidden = false;
+ }
+
+ // Before and after callbacks for the customizeToolbar code
+ getNavToolbox().customizeInit = BrowserToolboxCustomizeInit;
+ getNavToolbox().customizeDone = BrowserToolboxCustomizeDone;
+ getNavToolbox().customizeChange = BrowserToolboxCustomizeChange;
+
+ PlacesToolbarHelper.init();
+
+ gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
+ gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
+ gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
+
+ AeroPeek.onOpenWindow(window);
+
+ if (!gPrivate) {
+ // initialize the sync UI
+ // gSyncUI.init();
+
+ // initialize the session-restore service
+ setTimeout(InitSessionStoreCallback, 0);
+ }
+
+ ZoomListeners.init();
+ gBrowser.addTabsProgressListener(ZoomListeners);
+
+ window.addEventListener("MozAfterPaint", DelayedStartup);
+}
+
+// Minimal gBrowserInit shim to keep the Addon-SDK happy.
+var gBrowserInit = {
+ delayedStartupFinished: false,
+}
+
+function DelayedStartup() {
+ window.removeEventListener("MozAfterPaint", DelayedStartup);
+
+ // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
+ setTimeout(function() { SafeBrowsing.init(); }, 2000);
+
+ gBrowserInit.delayedStartupFinished = true;
+ Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
+}
+
+function UpdateNavBar()
+{
+ var elements = getNavToolbox().getElementsByClassName("nav-bar-class");
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.classList.remove("nav-bar-last");
+ element.classList.remove("nav-bar-first");
+ var next = element.nextSibling;
+ if (!next || !next.classList.contains("nav-bar-class"))
+ element.classList.add("nav-bar-last");
+ var previous = element.previousSibling;
+ if (!previous || !previous.classList.contains("nav-bar-class"))
+ element.classList.add("nav-bar-first");
+ }
+ UpdateUrlbarSearchSplitterState();
+}
+
+function UpdateUrlbarSearchSplitterState()
+{
+ var splitter = document.getElementById("urlbar-search-splitter");
+ var urlbar = document.getElementById("nav-bar-inner");
+ var searchbar = document.getElementById("search-container");
+
+ var ibefore = null;
+ if (isElementVisible(urlbar) && isElementVisible(searchbar)) {
+ if (searchbar.matches("#nav-bar-inner ~ #search-container"))
+ ibefore = searchbar;
+ else if (urlbar.matches("#search-container ~ #nav-bar-inner"))
+ ibefore = searchbar.nextSibling;
+ }
+
+ if (ibefore) {
+ splitter = document.createElement("splitter");
+ splitter.id = "urlbar-search-splitter";
+ splitter.setAttribute("resizebefore", "flex");
+ splitter.setAttribute("resizeafter", "flex");
+ splitter.setAttribute("skipintoolbarset", "true");
+ splitter.setAttribute("overflows", "false");
+ splitter.classList.add("chromeclass-toolbar-additional",
+ "nav-bar-class");
+ ibefore.parentNode.insertBefore(splitter, ibefore);
+ }
+}
+
+function updateWindowState()
+{
+ getBrowser().showWindowResizer =
+ window.windowState == window.STATE_NORMAL &&
+ !isElementVisible(document.getElementById("status-bar"));
+ getBrowser().docShellIsActive =
+ window.windowState != window.STATE_MINIMIZED;
+}
+
+function InitSessionStoreCallback()
+{
+ try {
+ var ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+ ss.init(window);
+
+ //Check if we have "Deferred Session Restore"
+ let restoreItem = document.getElementById("historyRestoreLastSession");
+
+ if (ss.canRestoreLastSession)
+ restoreItem.removeAttribute("disabled");
+ } catch(ex) {
+ dump("nsSessionStore could not be initialized: " + ex + "\n");
+ }
+}
+
+function WindowFocusTimerCallback(element)
+{
+ // This function is a redo of the fix for jag bug 91884.
+ // See Bug 97067 and Bug 89214 for details.
+ if (window == Services.ww.activeWindow) {
+ element.focus();
+ } else {
+ // set the element in command dispatcher so focus will restore properly
+ // when the window does become active
+
+ if (element instanceof Ci.nsIDOMWindow) {
+ document.commandDispatcher.focusedWindow = element;
+ document.commandDispatcher.focusedElement = null;
+ } else if (Element.isInstance(element)) {
+ document.commandDispatcher.focusedWindow = element.ownerDocument.defaultView;
+ document.commandDispatcher.focusedElement = element;
+ }
+ }
+}
+
+function Shutdown()
+{
+ AeroPeek.onCloseWindow(window);
+
+ BookmarkingUI.uninit();
+
+ // shut down browser access support
+ window.browserDOMWindow = null;
+
+ getBrowser().removeEventListener("DOMLinkAdded", BrowserSearch);
+
+ try {
+ getBrowser().removeProgressListener(window.XULBrowserWindow);
+ } catch (ex) {
+ // Perhaps we didn't get around to adding the progress listener
+ }
+
+ window.XULBrowserWindow.destroy();
+ window.XULBrowserWindow = null;
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow)
+ .XULBrowserWindow = null;
+
+ // unregister us as a pref listener
+ removePrefListener(gTabStripPrefListener);
+ removePrefListener(gHomepagePrefListener);
+ removePrefListener(gStatusBarPopupIconPrefListener);
+ removeFormSubmitObserver(gFormSubmitObserver);
+
+ window.browserContentListener.close();
+}
+
+function Translate()
+{
+ var service = GetLocalizedStringPref("browser.translation.service");
+ var serviceDomain = GetLocalizedStringPref("browser.translation.serviceDomain");
+ var targetURI = getWebNavigation().currentURI.spec;
+
+ // if we're already viewing a translated page, then just reload
+ if (targetURI.includes(serviceDomain))
+ BrowserReload();
+ else {
+ loadURI(encodeURI(service) + encodeURIComponent(targetURI));
+ }
+}
+
+function GetTypePermFromId(aId)
+{
+ // Get type and action from splitting id, first is type, second is action.
+ var [type, action] = aId.split("_");
+ var perm = "ACCESS_" + action.toUpperCase();
+ return [type, Ci.nsICookiePermission[perm]];
+}
+
+// Determine current state and check/uncheck the appropriate menu items.
+function CheckPermissionsMenu(aType, aNode)
+{
+ var currentPerm = Services.perms.testPermission(getBrowser().currentURI, aType);
+ var items = aNode.getElementsByAttribute("name", aType);
+ for (let item of items) {
+ // Get type and perm from id.
+ var [type, perm] = GetTypePermFromId(item.id);
+ item.setAttribute("checked", perm == currentPerm);
+ }
+}
+
+// Perform a Cookie, Image or Popup action.
+function CookieImagePopupAction(aElement)
+{
+ var uri = getBrowser().currentURI;
+ // Get type and perm from id.
+ var [type, perm] = GetTypePermFromId(aElement.id);
+ if (Services.perms.testPermission(uri, type) == perm)
+ return;
+
+ Services.perms.add(uri, type, perm);
+
+ Services.prompt.alert(window, aElement.getAttribute("title"),
+ aElement.getAttribute("msg"));
+}
+function OpenSessionHistoryIn(aWhere, aDelta, aTab)
+{
+ var win = aWhere == "window" ? null : window;
+ aTab = aTab || getBrowser().selectedTab;
+ var tab = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore)
+ .duplicateTab(win, aTab, aDelta, true);
+
+ var loadInBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+
+ switch (aWhere) {
+ case "tabfocused":
+ // forces tab to be focused
+ loadInBackground = true;
+ // fall through
+ case "tabshifted":
+ loadInBackground = !loadInBackground;
+ // fall through
+ case "tab":
+ if (!loadInBackground) {
+ getBrowser().selectedTab = tab;
+ window.content.focus();
+ }
+ }
+}
+
+/* Firefox compatibility shim *
+ * duplicateTabIn duplicates tab in a place specified by the parameter |where|.
+ *
+ * |where| can be:
+ * "tab" new tab
+ * "tabshifted" same as "tab" but in background if default is to select new
+ * tabs, and vice versa
+ * "tabfocused" same as "tab" but override any background preferences and
+ * focus the new tab
+ * "window" new window
+ *
+ * delta is the offset to the history entry that you want to load.
+ */
+function duplicateTabIn(aTab, aWhere, aDelta)
+{
+ OpenSessionHistoryIn(aWhere, aDelta, aTab);
+}
+
+function gotoHistoryIndex(aEvent)
+{
+ var index = aEvent.target.getAttribute("index");
+ if (!index)
+ return false;
+
+ var where = whereToOpenLink(aEvent);
+ if (where == "current") {
+ // Normal click. Go there in the current tab and update session history.
+ try {
+ getWebNavigation().gotoIndex(index);
+ }
+ catch(ex) {
+ return false;
+ }
+ }
+ else {
+ // Modified click. Go there in a new tab/window. Include session history.
+ var delta = index - getWebNavigation().sessionHistory.index;
+ OpenSessionHistoryIn(where, delta);
+ }
+ return true;
+}
+
+function BrowserBack(aEvent)
+{
+ var where = whereToOpenLink(aEvent, false, true);
+
+ if (where == "current") {
+ try {
+ getBrowser().goBack();
+ }
+ catch(ex) {}
+ }
+ else {
+ OpenSessionHistoryIn(where, -1);
+ }
+}
+
+function BrowserHandleBackspace()
+{
+ switch (Services.prefs.getIntPref("browser.backspace_action")) {
+ case 0:
+ BrowserBack();
+ break;
+ case 1:
+ goDoCommand("cmd_scrollPageUp");
+ break;
+ }
+}
+
+function BrowserForward(aEvent)
+{
+ var where = whereToOpenLink(aEvent, false, true);
+
+ if (where == "current") {
+ try {
+ getBrowser().goForward();
+ }
+ catch(ex) {
+ }
+ }
+ else {
+ OpenSessionHistoryIn(where, 1);
+ }
+}
+
+function BrowserUp()
+{
+ loadURI(getBrowser().currentURI.spec.replace(/[#?].*$/, "").replace(/\/[^\/]*.$/, "/"));
+}
+
+function BrowserHandleShiftBackspace()
+{
+ switch (Services.prefs.getIntPref("browser.backspace_action")) {
+ case 0:
+ BrowserForward();
+ break;
+ case 1:
+ goDoCommand("cmd_scrollPageDown");
+ break;
+ }
+}
+
+function BrowserBackMenu(event)
+{
+ return FillHistoryMenu(event.target, "back");
+}
+
+function BrowserForwardMenu(event)
+{
+ return FillHistoryMenu(event.target, "forward");
+}
+
+function BrowserStop()
+{
+ try {
+ const stopFlags = nsIWebNavigation.STOP_ALL;
+ getWebNavigation().stop(stopFlags);
+ }
+ catch(ex) {
+ }
+}
+
+function BrowserReload(aEvent)
+{
+ var where = whereToOpenLink(aEvent, false, true);
+ if (where == "current")
+ BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_NONE);
+ else if (where == null && aEvent.shiftKey)
+ BrowserReloadSkipCache();
+ else
+ OpenSessionHistoryIn(where, 0);
+}
+
+function BrowserReloadSkipCache()
+{
+ // Bypass proxy and cache.
+ const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
+ BrowserReloadWithFlags(reloadFlags);
+}
+
+function BrowserHome(aEvent)
+{
+ var homePage = getHomePage();
+ var where = whereToOpenLink(aEvent, false, true);
+ openUILinkArrayIn(homePage, where);
+}
+
+var BrowserSearch = {
+ handleEvent: function (event) { // "DOMLinkAdded" event
+ var link = event.originalTarget;
+
+ var isSearch = /(?:^|\s)search(?:\s|$)/i.test(link.rel) && link.title &&
+ /^(https?|ftp):/i.test(link.href) &&
+ /(?:^|\s)application\/opensearchdescription\+xml(?:;?.*)$/i.test(link.type);
+
+ if (isSearch) {
+ this.addEngine(link, link.ownerDocument);
+ }
+ },
+
+ addEngine: function(engine, targetDoc) {
+ if (!this.searchBar)
+ return;
+
+ var browser = getBrowser().getBrowserForDocument(targetDoc);
+ // ignore search engines from subframes (see bug 479408)
+ if (!browser)
+ return;
+
+ // Check to see whether we've already added an engine with this title
+ if (browser.engines) {
+ if (browser.engines.some(e => e.title == engine.title))
+ return;
+ }
+
+ // Append the URI and an appropriate title to the browser data.
+ // Use documentURIObject in the check so that we do the right
+ // thing with about:-style error pages. Bug 453442
+ var iconURL = null;
+ var aURI = targetDoc.documentURIObject;
+ try {
+ aURI = Services.uriFixup.createExposableURI(aURI);
+ } catch (e) {
+ }
+
+ if (aURI && ("schemeIs" in aURI) &&
+ (aURI.schemeIs("http") || aURI.schemeIs("https")))
+ iconURL = getBrowser().buildFavIconString(aURI);
+
+ var hidden = false;
+ // If this engine (identified by title) is already in the list, add it
+ // to the list of hidden engines rather than to the main list.
+ // XXX This will need to be changed when engines are identified by URL;
+ // see bug 335102.
+ if (Services.search.getEngineByName(engine.title))
+ hidden = true;
+
+ var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
+
+ engines.push({ uri: engine.href,
+ title: engine.title,
+ icon: iconURL });
+
+ if (hidden)
+ browser.hiddenEngines = engines;
+ else {
+ browser.engines = engines;
+ if (browser == getBrowser().selectedBrowser)
+ this.updateSearchButton();
+ }
+ },
+
+ /**
+ * Update the browser UI to show whether or not additional engines are
+ * available when a page is loaded or the user switches tabs to a page that
+ * has search engines.
+ */
+ updateSearchButton: function() {
+ var searchBar = this.searchBar;
+
+ // The search bar binding might not be applied even though the element is
+ // in the document (e.g. when the navigation toolbar is hidden), so check
+ // for .searchButton specifically.
+ if (!searchBar || !searchBar.searchButton)
+ return;
+
+ searchBar.updateSearchButton();
+ },
+
+ /**
+ * Gives focus to the search bar, if it is present on the toolbar, or to the
+ * search sidebar, if it is open, or loads the default engine's search form
+ * otherwise. For Mac, opens a new window or focuses an existing window, if
+ * necessary.
+ */
+ webSearch: function BrowserSearch_webSearch() {
+ if (!gBrowser) {
+ var win = getTopWin();
+ if (win) {
+ // If there's an open browser window, it should handle this command
+ win.focus();
+ win.BrowserSearch.webSearch();
+ return;
+ }
+
+ // If there are no open browser windows, open a new one
+ function webSearchCallback() {
+ // This needs to be in a timeout so that we don't end up refocused
+ // in the url bar
+ setTimeout(BrowserSearch.webSearch, 0);
+ }
+
+ win = window.openDialog(getBrowserURL(), "_blank",
+ "chrome,all,dialog=no", "about:blank");
+ win.addEventListener("load", webSearchCallback);
+ return;
+ }
+
+ if (isElementVisible(this.searchBar)) {
+ this.searchBar.select();
+ this.searchBar.focus();
+ } else if (this.searchSidebar) {
+ this.searchSidebar.focus();
+ } else {
+ loadURI(Services.search.defaultEngine.searchForm);
+ window.content.focus();
+ }
+ },
+
+ /**
+ * Loads a search results page, given a set of search terms. Uses the current
+ * engine if the search bar is visible, or the default engine otherwise.
+ *
+ * @param aSearchText
+ * The search terms to use for the search.
+ *
+ * @param [optional] aNewWindowOrTab
+ * A boolean if set causes the search to load in a new window or tab
+ * (depending on "browser.search.openintab"). Otherwise the search
+ * loads in the current tab.
+ *
+ * @param [optional] aEvent
+ * The event object passed from the caller.
+ */
+ loadSearch: function BrowserSearch_search(aSearchText, aNewWindowOrTab, aEvent) {
+ var engine;
+
+ // If the search bar is visible, use the current engine, otherwise, fall
+ // back to the default engine.
+ if (isElementVisible(this.searchBar) ||
+ this.searchSidebar)
+ engine = Services.search.currentEngine;
+ else
+ engine = Services.search.defaultEngine;
+
+ var submission = engine.getSubmission(aSearchText); // HTML response
+
+ // getSubmission can return null if the engine doesn't have a URL
+ // with a text/html response type. This is unlikely (since
+ // SearchService._addEngineToStore() should fail for such an engine),
+ // but let's be on the safe side.
+ // If you change the code here, remember to make the corresponding
+ // changes in suite/mailnews/mailWindowOverlay.js->MsgOpenSearch
+ if (!submission)
+ return;
+
+ if (aNewWindowOrTab) {
+ let newTabPref = Services.prefs.getBoolPref("browser.search.opentabforcontextsearch");
+ let where = newTabPref ? aEvent && aEvent.shiftKey ? "tabshifted" : "tab" : "window";
+ openUILinkIn(submission.uri.spec, where, null, submission.postData);
+ if (where == "window")
+ return;
+ } else {
+ loadURI(submission.uri.spec, null, submission.postData, false);
+ window.content.focus();
+ }
+ },
+
+ /**
+ * Returns the search bar element if it is present in the toolbar, null otherwise.
+ */
+ get searchBar() {
+ return document.getElementById("searchbar");
+ },
+
+ /**
+ * Returns the search sidebar textbox if the search sidebar is present in
+ * the sidebar and selected, null otherwise.
+ */
+ get searchSidebar() {
+ if (sidebarObj.never_built)
+ return null;
+ var panel = sidebarObj.panels.get_panel_from_id("urn:sidebar:panel:search");
+ return panel && isElementVisible(panel.get_iframe()) &&
+ panel.get_iframe()
+ .contentDocument.getElementById("sidebar-search-text");
+ },
+
+ loadAddEngines: function BrowserSearch_loadAddEngines() {
+ loadAddSearchEngines(); // for compatibility
+ },
+
+ /**
+ * Reveal the search sidebar panel.
+ */
+ revealSidebar: function BrowserSearch_revealSidebar() {
+ // first lets check if the search panel will be shown at all
+ // by checking the sidebar datasource to see if there is an entry
+ // for the search panel, and if it is excluded for navigator or not
+
+ var searchPanelExists = false;
+
+ var myPanel = document.getElementById("urn:sidebar:panel:search");
+ if (myPanel) {
+ var panel = sidebarObj.panels.get_panel_from_header_node(myPanel);
+ searchPanelExists = !panel.is_excluded();
+
+ } else if (sidebarObj.never_built) {
+ // XXXsearch: in theory, this should work when the sidebar isn't loaded,
+ // in practice, it fails as sidebarObj.datasource_uri isn't defined
+ try {
+ var datasource = RDF.GetDataSourceBlocking(sidebarObj.datasource_uri);
+ var aboutValue = RDF.GetResource("urn:sidebar:panel:search");
+
+ // check if the panel is even in the list by checking for its content
+ var contentProp = RDF.GetResource("http://home.netscape.com/NC-rdf#content");
+ var content = datasource.GetTarget(aboutValue, contentProp, true);
+
+ if (content instanceof Ci.nsIRDFLiteral) {
+ // the search panel entry exists, now check if it is excluded
+ // for navigator
+ var excludeProp = RDF.GetResource("http://home.netscape.com/NC-rdf#exclude");
+ var exclude = datasource.GetTarget(aboutValue, excludeProp, true);
+
+ if (exclude instanceof Ci.nsIRDFLiteral) {
+ searchPanelExists = !exclude.Value.includes("navigator:browser");
+ } else {
+ // panel exists and no exclude set
+ searchPanelExists = true;
+ }
+ }
+ } catch (e) {
+ searchPanelExists = false;
+ }
+ }
+
+ if (searchPanelExists) {
+ // make sure the sidebar is open, else SidebarSelectPanel() will fail
+ if (sidebar_is_hidden())
+ SidebarShowHide();
+
+ if (sidebar_is_collapsed())
+ SidebarExpandCollapse();
+
+ var searchPanel = document.getElementById("urn:sidebar:panel:search");
+ if (searchPanel)
+ SidebarSelectPanel(searchPanel, true, true); // lives in sidebarOverlay.js
+ }
+ }
+}
+
+function QualifySearchTerm()
+{
+ // If the text in the URL bar is the same as the currently loaded
+ // page's URL then treat this as an empty search term. This way
+ // the user is taken to the search page where s/he can enter a term.
+ if (gBrowser.userTypedValue !== null)
+ return gURLBar.value;
+ return "";
+}
+
+function BrowserOpenWindow()
+{
+ //opens a window where users can select a web location to open
+ var params = { action: gPrivate ? "4" : "0", url: "" };
+ openDialog("chrome://communicator/content/openLocation.xul", "_blank",
+ "chrome,modal,titlebar", params);
+
+ getShortcutOrURIAndPostData(params.url).then(data => {
+ switch (params.action) {
+ case "0": // current window
+ loadURI(data.url, null, data.postData, true);
+ break;
+ case "1": // new window
+ openDialog(getBrowserURL(), "_blank", "all,dialog=no", data.url, null, null,
+ data.postData, true);
+ break;
+ case "2": // edit
+ editPage(data.url);
+ break;
+ case "3": // new tab
+ gBrowser.selectedTab = gBrowser.addTab(data.url,
+ {allowThirdPartyFixup: true,
+ postData: data.postData});
+ break;
+ case "4": // private
+ openNewPrivateWith(params.url);
+ break;
+ }
+ });
+}
+
+function BrowserOpenTab()
+{
+ if (!gInPrintPreviewMode) {
+ var uriToLoad;
+ var tabPref = Services.prefs.getIntPref("browser.tabs.loadOnNewTab", 0);
+ switch (tabPref)
+ {
+ default:
+ uriToLoad = "about:blank";
+ break;
+ case 1:
+ uriToLoad = GetLocalizedStringPref("browser.startup.homepage");
+ break;
+ case 2:
+ uriToLoad = Services.prefs.getStringPref("browser.history.last_page_visited", "");
+ break;
+ }
+
+ if (!gBrowser) {
+ var win = getTopWin();
+ if (win) {
+ // If there's an open browser window, it should handle this command
+ win.focus();
+ win.BrowserOpenTab();
+ return;
+ }
+
+ // If there are no open browser windows, open a new one
+ openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", uriToLoad);
+ return;
+ }
+
+ if (tabPref == 2)
+ OpenSessionHistoryIn("tabfocused", 0);
+ else
+ gBrowser.selectedTab = gBrowser.addTab(uriToLoad);
+
+ if (uriToLoad == "about:blank" && isElementVisible(gURLBar))
+ setTimeout(WindowFocusTimerCallback, 0, gURLBar);
+ else
+ setTimeout(WindowFocusTimerCallback, 0, content);
+ }
+}
+
+function BrowserOpenSyncTabs()
+{
+ switchToTabHavingURI("about:sync-tabs", true);
+}
+// Class for saving the last directory and filter Index in the prefs.
+// Used for open file and upload file.
+class RememberLastDir {
+
+ // The pref names are constructed from the prefix parameter in the constructor.
+ // The pref names should not be changed later.
+ constructor(prefPrefix) {
+ this._prefLastDir = prefPrefix + ".lastDir";
+ this._prefFilterIndex = prefPrefix + ".filterIndex";
+ this._lastDir = null;
+ this._lastFilterIndex = null;
+ }
+
+ get path() {
+ if (!this._lastDir || !this._lastDir.exists()) {
+ try {
+ this._lastDir = Services.prefs.getComplexValue(this._prefLastDir,
+ Ci.nsIFile);
+ if (!this._lastDir.exists()) {
+ this._lastDir = null;
+ }
+ } catch (e) {}
+ }
+ return this._lastDir;
+ }
+
+ set path(val) {
+ try {
+ if (!val || !val.isDirectory()) {
+ return;
+ }
+ } catch (e) {
+ return;
+ }
+ this._lastDir = val.clone();
+
+ // Don't save the last open directory pref inside the Private Browsing mode
+ if (!gPrivate) {
+ Services.prefs.setComplexValue(this._prefLastDir,
+ Ci.nsIFile,
+ this._lastDir);
+ }
+ }
+
+ get filterIndex() {
+ if (!this._lastFilterIndex) {
+ // use a pref to remember the filterIndex selected by the user.
+ this._lastFilterIndex =
+ Services.prefs.getIntPref(this._prefFilterIndex, 0);
+ }
+ return this._lastFilterIndex;
+ }
+
+ set filterIndex(val) {
+ // If the default is picked the filter is null.
+ this._lastFilterIndex = val ? val : 0;
+
+ // Don't save the last filter index inside the Private Browsing mode
+ if (!gPrivate) {
+ Services.prefs.setIntPref(this._prefFilterIndex,
+ this._lastFilterIndex);
+ }
+ }
+
+ // This is currently not used.
+ reset() {
+ this._lastDir = null;
+ this._lastFilterIndex = null;
+ }
+}
+
+var gLastOpenDirectory;
+
+function BrowserOpenFileWindow() {
+
+ if (!gLastOpenDirectory) {
+ gLastOpenDirectory = new RememberLastDir("browser.open");
+ };
+
+ // Get filepicker component.
+ try {
+ const nsIFilePicker = Ci.nsIFilePicker;
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult == nsIFilePicker.returnOK) {
+ try {
+ // Set last path and file index only if file is ok.
+ if (fp.file) {
+ gLastOpenDirectory.filterIndex = fp.filterIndex;
+ gLastOpenDirectory.path =
+ fp.file.parent.QueryInterface(Ci.nsIFile);
+ }
+ } catch (ex) {
+ }
+ openUILinkIn(fp.fileURL.spec, "current");
+ }
+ };
+
+ fp.init(window, gNavigatorBundle.getString("openFile"),
+ nsIFilePicker.modeOpen);
+ fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
+ nsIFilePicker.filterImages | nsIFilePicker.filterXML |
+ nsIFilePicker.filterHTML);
+ fp.filterIndex = gLastOpenDirectory.filterIndex;
+ fp.displayDirectory = gLastOpenDirectory.path;
+ fp.open(fpCallback);
+ } catch (ex) {
+ }
+}
+
+function updateCloseItems()
+{
+ var browser = getBrowser();
+
+ var hideCloseWindow = Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") &&
+ (!browser || browser.tabContainer.childNodes.length <= 1);
+ document.getElementById("menu_closeWindow").hidden = hideCloseWindow;
+ var closeItem = document.getElementById("menu_close");
+ if (hideCloseWindow) {
+ closeItem.setAttribute("label", gNavigatorBundle.getString("tabs.close.label"));
+ closeItem.setAttribute("accesskey", gNavigatorBundle.getString("tabs.close.accesskey"));
+ } else {
+ closeItem.setAttribute("label", gNavigatorBundle.getString("tabs.closeTab.label"));
+ closeItem.setAttribute("accesskey", gNavigatorBundle.getString("tabs.closeTab.accesskey"));
+ }
+
+ var hideClose = !browser || !browser.getStripVisibility();
+ document.getElementById("menu_closeOtherTabs").hidden = hideClose;
+ if (!hideClose)
+ document.getElementById("cmd_closeOtherTabs").disabled = hideCloseWindow;
+
+ hideClose = !browser ||
+ (browser.getTabsToTheEndFrom(browser.mCurrentTab).length == 0);
+ document.getElementById("menu_closeTabsToTheEnd").hidden = hideClose;
+ if (!hideClose)
+ document.getElementById("cmd_closeTabsToTheEnd").disabled = hideClose;
+}
+
+function updateRecentMenuItems()
+{
+ var browser = getBrowser();
+ var ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+
+ var recentTabsItem = document.getElementById("menu_recentTabs");
+ recentTabsItem.setAttribute("disabled", !browser || browser.getUndoList().length == 0);
+ var recentWindowsItem = document.getElementById("menu_recentWindows");
+ recentWindowsItem.setAttribute("disabled", ss.getClosedWindowCount() == 0);
+}
+
+function updateRecentTabs(menupopup)
+{
+ var browser = getBrowser();
+
+ while (menupopup.hasChildNodes())
+ menupopup.lastChild.remove();
+
+ var list = browser.getUndoList();
+ for (var i = 0; i < list.length; i++) {
+ var menuitem = document.createElement("menuitem");
+ var label = list[i];
+ if (i < 9) {
+ label = gNavigatorBundle.getFormattedString("tabs.recentlyClosed.format", [i + 1, label]);
+ menuitem.setAttribute("accesskey", i + 1);
+ }
+
+ if (i == 0)
+ menuitem.setAttribute("key", "key_restoreTab");
+
+ menuitem.setAttribute("label", label);
+ menuitem.setAttribute("value", i);
+ menupopup.appendChild(menuitem);
+ }
+}
+
+function updateRecentWindows(menupopup)
+{
+ var ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+
+ while (menupopup.hasChildNodes())
+ menupopup.lastChild.remove();
+
+ var undoItems = JSON.parse(ss.getClosedWindowData());
+ for (var i = 0; i < undoItems.length; i++) {
+ var menuitem = document.createElement("menuitem");
+ var label = undoItems[i].title;
+ if (i < 9) {
+ label = gNavigatorBundle.getFormattedString("windows.recentlyClosed.format", [i + 1, label]);
+ menuitem.setAttribute("accesskey", i + 1);
+ }
+
+ if (i == 0)
+ menuitem.setAttribute("key", "key_restoreWindow");
+
+ menuitem.setAttribute("label", label);
+ menuitem.setAttribute("value", i);
+ menupopup.appendChild(menuitem);
+ }
+}
+
+function undoCloseWindow(aIndex)
+{
+ var ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+
+ return ss.undoCloseWindow(aIndex);
+}
+
+function restoreLastSession() {
+ let ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+ ss.restoreLastSession();
+}
+
+/*
+ * Determines if a tab is "empty" using isBrowserEmpty from utilityOverlay.js
+ */
+function isTabEmpty(aTab)
+{
+ return isBrowserEmpty(aTab.linkedBrowser);
+}
+
+function BrowserCloseOtherTabs()
+{
+ var browser = getBrowser();
+ browser.removeAllTabsBut(browser.mCurrentTab);
+}
+
+function BrowserCloseTabsToTheEnd()
+{
+ var browser = getBrowser();
+ browser.removeTabsToTheEndFrom(browser.mCurrentTab);
+}
+
+function BrowserCloseTabOrWindow()
+{
+ var browser = getBrowser();
+ if (browser.tabContainer.childNodes.length > 1 ||
+ !Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab")) {
+ // Just close up a tab.
+ browser.removeCurrentTab();
+ return;
+ }
+
+ BrowserCloseWindow();
+}
+
+function BrowserTryToCloseWindow()
+{
+ if (WindowIsClosing())
+ BrowserCloseWindow();
+}
+
+function BrowserCloseWindow()
+{
+ // This code replicates stuff in Shutdown(). It is here because
+ // window.screenX and window.screenY have real values. We need
+ // to fix this eventually but by replicating the code here, we
+ // provide a means of saving position (it just requires that the
+ // user close the window via File->Close (vs. close box).
+
+ // Get the current window position/size.
+ var x = window.screenX;
+ var y = window.screenY;
+ var h = window.outerHeight;
+ var w = window.outerWidth;
+
+ // Store these into the window attributes (for persistence).
+ var win = document.getElementById( "main-window" );
+ win.setAttribute( "x", x );
+ win.setAttribute( "y", y );
+ win.setAttribute( "height", h );
+ win.setAttribute( "width", w );
+
+ window.close();
+}
+
+function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
+ userContextId, originPrincipal,
+ forceAboutBlankViewerInCurrent, triggeringPrincipal) {
+ try {
+ openLinkIn(uri, "current",
+ { referrerURI: referrer,
+ referrerPolicy: referrerPolicy,
+ postData: postData,
+ allowThirdPartyFixup: allowThirdPartyFixup,
+ userContextId: userContextId,
+ originPrincipal,
+ triggeringPrincipal,
+ forceAboutBlankViewerInCurrent, });
+ } catch (e) {}
+}
+
+function loadOneOrMoreURIs(aURIString, aTriggeringPrincipal) {
+ // we're not a browser window, pass the URI string to a new browser window
+ if (window.location.href != getBrowserURL()) {
+ window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
+ return;
+ }
+
+ // This function throws for certain malformed URIs, so use exception handling
+ // so that we don't disrupt startup
+ try {
+ gBrowser.loadTabs(aURIString.split("|"), {
+ inBackground: false,
+ replace: true,
+ triggeringPrincipal: aTriggeringPrincipal,
+ });
+ } catch (e) {
+ }
+}
+
+function handleURLBarCommand(aUserAction, aTriggeringEvent)
+{
+ // Remove leading and trailing spaces first
+ var url = gURLBar.value.trim();
+ try {
+ addToUrlbarHistory(url);
+ } catch (ex) {
+ // Things may go wrong when adding url to the location bar history,
+ // but don't let that interfere with the loading of the url.
+ }
+
+ if (url.match(/^view-source:/)) {
+ gViewSourceUtils.viewSource(url.replace(/^view-source:/, ""), null, null);
+ return;
+ }
+
+ getShortcutOrURIAndPostData(url).then(data => {
+ // Check the pressed modifiers: (also see bug 97123)
+ // Modifier Mac | Modifier PC | Action
+ // -------------+-------------+-----------
+ // Command | Control | New Window/Tab
+ // Shift+Cmd | Shift+Ctrl | New Window/Tab behind current one
+ // Option | Shift | Save URL (show Filepicker)
+
+ // If false, the save modifier is Alt, which is Option on Mac.
+ var modifierIsShift = Services.prefs.getBoolPref("ui.key.saveLink.shift", true);
+
+ var shiftPressed = false;
+ var saveModifier = false; // if the save modifier was pressed
+ if (aTriggeringEvent && 'shiftKey' in aTriggeringEvent &&
+ 'altKey' in aTriggeringEvent) {
+ saveModifier = modifierIsShift ? aTriggeringEvent.shiftKey
+ : aTriggeringEvent.altKey;
+ shiftPressed = aTriggeringEvent.shiftKey;
+ }
+
+ var browser = getBrowser();
+ // Accept both Control and Meta (=Command) as New-Window-Modifiers
+ if (aTriggeringEvent &&
+ (('ctrlKey' in aTriggeringEvent && aTriggeringEvent.ctrlKey) ||
+ ('metaKey' in aTriggeringEvent && aTriggeringEvent.metaKey) ||
+ ('button' in aTriggeringEvent && aTriggeringEvent.button == 1))) {
+ // Check if user requests Tabs instead of windows
+ if (Services.prefs.getBoolPref("browser.tabs.opentabfor.urlbar", false)) {
+ // Reset url in the urlbar
+ URLBarSetURI();
+ // Open link in new tab
+ var t = browser.addTab(data.url, {
+ postData: data.postData,
+ allowThirdPartyFixup: true,
+ });
+
+ // Focus new tab unless shift is pressed
+ if (!shiftPressed)
+ browser.selectedTab = t;
+ } else {
+ // Open a new window with the URL
+ var newWin = openDialog(getBrowserURL(), "_blank", "all,dialog=no", data.url,
+ null, null, data.postData, true);
+ // Reset url in the urlbar
+ URLBarSetURI();
+
+ // Focus old window if shift was pressed, as there's no
+ // way to open a new window in the background
+ // XXX this doesn't seem to work
+ if (shiftPressed) {
+ //newWin.blur();
+ content.focus();
+ }
+ }
+ } else if (saveModifier) {
+ try {
+ // Firstly, fixup the url so that (e.g.) "www.foo.com" works
+ url = Services.uriFixup.createFixupURI(data.url, Ci.nsIURIFixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI).spec;
+ // Open filepicker to save the url
+ saveURL(url, null, null, false, true, null, document);
+ }
+ catch(ex) {
+ // XXX Do nothing for now.
+ // Do we want to put up an alert in the future? Mmm, l10n...
+ }
+ } else {
+ // No modifier was pressed, load the URL normally and
+ // focus the content area
+ loadURI(data.url, null, data.postData, true);
+ content.focus();
+ }
+ });
+}
+
+/**
+ * Given a string, will generate a more appropriate urlbar value if a Places
+ * keyword or a search alias is found at the beginning of it.
+ *
+ * @param url
+ * A string that may begin with a keyword or an alias.
+ *
+ * @return {Promise}
+ * @resolves { url, postData, mayInheritPrincipal }. If it's not possible
+ * to discern a keyword or an alias, url will be the input string.
+ */
+function getShortcutOrURIAndPostData(url) {
+ return (async function() {
+ let mayInheritPrincipal = false;
+ let postData = null;
+ // Split on the first whitespace.
+ let [keyword, param = ""] = url.trim().split(/\s(.+)/, 2);
+
+ if (!keyword) {
+ return { url, postData, mayInheritPrincipal };
+ }
+
+ let engine = Services.search.getEngineByAlias(keyword);
+ if (engine) {
+ let submission = engine.getSubmission(param, null, "keyword");
+ return { url: submission.uri.spec,
+ postData: submission.postData,
+ mayInheritPrincipal };
+ }
+
+ // A corrupt Places database could make this throw, breaking navigation
+ // from the location bar.
+ let entry = null;
+ try {
+ entry = await PlacesUtils.keywords.fetch(keyword);
+ } catch (ex) {
+ Cu.reportError(`Unable to fetch Places keyword "${keyword}": ${ex}`);
+ }
+ if (!entry || !entry.url) {
+ // This is not a Places keyword.
+ return { url, postData, mayInheritPrincipal };
+ }
+
+ try {
+ [url, postData] =
+ await BrowserUtils.parseUrlAndPostData(entry.url.href,
+ entry.postData,
+ param);
+ if (postData) {
+ postData = getPostDataStream(postData);
+ }
+
+ // Since this URL came from a bookmark, it's safe to let it inherit the
+ // current document's principal.
+ mayInheritPrincipal = true;
+ } catch (ex) {
+ // It was not possible to bind the param, just use the original url value.
+ }
+
+ return { url, postData, mayInheritPrincipal };
+ })().then(data => {
+ return data;
+ });
+}
+
+function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType)
+{
+ var dataStream = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
+ dataStream.data = aStringData;
+
+ var mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"]
+ .createInstance(Ci.nsIMIMEInputStream);
+ mimeStream.addHeader("Content-Type", aType);
+ mimeStream.setData(dataStream);
+ return mimeStream.QueryInterface(Ci.nsIInputStream);
+}
+
+// handleDroppedLink has the following 2 overloads:
+// handleDroppedLink(event, url, name, triggeringPrincipal)
+// handleDroppedLink(event, links, triggeringPrincipal)
+function handleDroppedLink(event, urlOrLinks, nameOrTriggeringPrincipal, triggeringPrincipal)
+{
+ let links;
+ if (Array.isArray(urlOrLinks)) {
+ links = urlOrLinks;
+ triggeringPrincipal = nameOrTriggeringPrincipal;
+ } else {
+ links = [{ url: urlOrLinks, nameOrTriggeringPrincipal, type: "" }];
+ }
+
+ let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
+
+ // Usually blank for SeaMonkey.
+ let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid");
+
+ // event is null if links are dropped in content process.
+ // inBackground should be false, as it's loading into current browser.
+ let inBackground = false;
+ if (event) {
+ inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+ if (event.shiftKey)
+ inBackground = !inBackground;
+ }
+
+ (async function() {
+ let urls = [];
+ let postDatas = [];
+ for (let link of links) {
+ let data = await getShortcutOrURIAndPostData(link.url);
+ urls.push(data.url);
+ postDatas.push(data.postData);
+ }
+ if (lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
+ gBrowser.loadTabs(urls, {
+ inBackground,
+ replace: true,
+ allowThirdPartyFixup: false,
+ postDatas,
+ userContextId,
+ triggeringPrincipal,
+ });
+ }
+ })();
+
+ // If links are dropped in content process, event.preventDefault() should be
+ // called in content process.
+ if (event) {
+ // Keep the event from being handled by the dragDrop listeners
+ // built-in to gecko if they happen to be above us.
+ event.preventDefault();
+ }
+}
+
+function readFromClipboard()
+{
+ var url;
+
+ try {
+ // Get the clipboard.
+ const clipboard = Services.clipboard;
+
+ // Create a transferable that will transfer the text.
+ var trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+
+ trans.init(null);
+ trans.addDataFlavor("text/unicode");
+ // If available, use the selection clipboard, otherwise use the global one.
+ if (clipboard.supportsSelectionClipboard())
+ clipboard.getData(trans, clipboard.kSelectionClipboard);
+ else
+ clipboard.getData(trans, clipboard.kGlobalClipboard);
+
+ var data = {};
+ trans.getTransferData("text/unicode", data, {});
+
+ if (data.value) {
+ data = data.value.QueryInterface(Ci.nsISupportsString);
+ url = data.data;
+ }
+ } catch (ex) {
+ }
+
+ return url;
+}
+
+/**
+ * Open the View Source dialog.
+ *
+ * @param aArgsOrDocument
+ * Either an object or a Document. Passing a Document is deprecated,
+ * and is not supported with e10s. This function will throw if
+ * aArgsOrDocument is a CPOW.
+ *
+ * If aArgsOrDocument is an object, that object can take the
+ * following properties:
+ *
+ * URL (required):
+ * A string URL for the page we'd like to view the source of.
+ * browser (optional):
+ * The browser containing the document that we would like to view the
+ * source of. This is required if outerWindowID is passed.
+ * outerWindowID (optional):
+ * The outerWindowID of the content window containing the document that
+ * we want to view the source of. You only need to provide this if you
+ * want to attempt to retrieve the document source from the network
+ * cache.
+ * lineNumber (optional):
+ * The line number to focus on once the source is loaded.
+ */
+function BrowserViewSourceOfDocument(aArgsOrDocument) {
+ if (aArgsOrDocument instanceof Document) {
+ // Deprecated API - callers should pass args object instead.
+ if (Cu.isCrossProcessWrapper(aArgsOrDocument)) {
+ throw new Error("BrowserViewSourceOfDocument cannot accept a CPOW " +
+ "as a document.");
+ }
+
+ let requestor = aArgsOrDocument.defaultView
+ .QueryInterface(Ci.nsIInterfaceRequestor);
+ let browser = requestor.getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ let outerWindowID = requestor.getInterface(Ci.nsIDOMWindowUtils)
+ .outerWindowID;
+ let URL = browser.currentURI.spec;
+ aArgsOrDocument = { browser, outerWindowID, URL };
+ }
+
+ gViewSourceUtils.viewSource(aArgsOrDocument);
+}
+
+/**
+ * Opens the View Source dialog for the source loaded in the root
+ * top-level document of the browser.
+ *
+ * @param aBrowser
+ * The browser that we want to load the source of.
+ */
+function BrowserViewSource(aBrowser) {
+ gViewSourceUtils.viewSource({
+ browser: aBrowser,
+ outerWindowID: aBrowser.outerWindowID,
+ URL: aBrowser.currentURI.spec,
+ });
+}
+
+// documentURL - URL of the document to view, or null for this window's document
+// initialTab - id of the initial tab to display, or null for the first tab
+// imageElement - image to load in the Media Tab of the Page Info window;
+// can be null/omitted
+// frameOuterWindowID - the id of the frame that the context menu opened in;
+// can be null/omitted
+// browser - the browser containing the document we're interested in inspecting;
+// can be null/omitted
+function BrowserPageInfo(documentURL, initialTab, imageElement,
+ frameOuterWindowID, browser) {
+ if (documentURL instanceof HTMLDocument) {
+ Deprecated.warning("Please pass the location URL instead of the document " +
+ "to BrowserPageInfo() as the first argument.",
+ "https://bugzilla.mozilla.org/show_bug.cgi?id=1575830");
+ documentURL = documentURL.location;
+ }
+
+ let args = { initialTab, imageElement, frameOuterWindowID, browser };
+ var windows = Services.wm.getEnumerator("Browser:page-info");
+
+ documentURL = documentURL || window.gBrowser.selectedBrowser.currentURI.spec;
+
+ // Check for windows matching the url.
+ while (windows.hasMoreElements()) {
+ let win = windows.getNext();
+ if (win.closed) {
+ continue;
+ }
+ if (win.document.documentElement
+ .getAttribute("relatedUrl") == documentURL) {
+ win.focus();
+ win.resetPageInfo(args);
+ return win;
+ }
+ }
+ // We didn't find a matching window, so open a new one.
+ return window.openDialog("chrome://navigator/content/pageinfo/pageInfo.xul",
+ "_blank",
+ "chrome,dialog=no,resizable",
+ args);
+}
+
+function hiddenWindowStartup()
+{
+ // focus the hidden window
+ window.focus();
+
+ // Disable menus which are not appropriate
+ var disabledItems = ['cmd_close', 'cmd_sendPage', 'Browser:SendLink',
+ 'Browser:EditPage', 'Browser:SavePage', 'cmd_printSetup',
+ 'cmd_print', 'canGoBack', 'canGoForward',
+ 'Browser:AddBookmark', 'Browser:AddBookmarkAs',
+ 'cmd_undo', 'cmd_redo', 'cmd_cut', 'cmd_copy',
+ 'cmd_paste', 'cmd_delete', 'cmd_selectAll',
+ 'cmd_findTypeText', 'cmd_findTypeLinks', 'cmd_find',
+ 'cmd_findNext', 'cmd_findPrev', 'menu_Toolbars',
+ 'menuitem_reload', 'menu_UseStyleSheet', 'charsetMenu',
+ 'View:PageSource', 'View:PageInfo', 'menu_translate',
+ 'cookie_deny', 'cookie_default', 'View:FullScreen',
+ 'cookie_session', 'cookie_allow', 'image_deny',
+ 'image_default', 'image_allow', 'popup_deny',
+ 'popup_default','popup_allow', 'menu_zoom',
+ 'cmd_minimizeWindow', 'cmd_zoomWindow'];
+ var broadcaster;
+
+ for (var id in disabledItems) {
+ broadcaster = document.getElementById(disabledItems[id]);
+ if (broadcaster)
+ broadcaster.setAttribute("disabled", "true");
+ }
+
+ // also hide the window list separator
+ var separator = document.getElementById("sep-window-list");
+ if (separator)
+ separator.setAttribute("hidden", "true");
+
+ // init string bundles
+ gNavigatorBundle = document.getElementById("bundle_navigator");
+ gNavigatorRegionBundle = document.getElementById("bundle_navigator_region");
+ gBrandBundle = document.getElementById("bundle_brand");
+}
+
+function checkForDirectoryListing()
+{
+ if ( "HTTPIndex" in content &&
+ content.HTTPIndex instanceof Ci.nsIHTTPIndex ) {
+ var forced = getBrowser().docShell.forcedCharset;
+ if (forced) {
+ content.defaultCharacterset = forced;
+ }
+ }
+}
+
+function URLBarSetURI(aURI, aValid) {
+ var uri = aURI || getWebNavigation().currentURI;
+ var value;
+
+ // If the url has "wyciwyg://" as the protocol, strip it off.
+ // Nobody wants to see it on the urlbar for dynamically generated pages.
+ try {
+ uri = Services.uriFixup.createExposableURI(uri);
+ } catch (ex) {}
+
+ // Replace "about:blank" and other initial pages with an empty string
+ // only if there's no opener (bug 370555).
+ if (gInitialPages.has(uri.spec))
+ value = (content.opener || getWebNavigation().canGoBack) ? uri.spec : "";
+ else
+ value = losslessDecodeURI(uri);
+
+ gURLBar.value = value;
+ // In some cases, setting the urlBar value causes userTypedValue to
+ // become set because of oninput, so reset it to null.
+ getBrowser().userTypedValue = null;
+
+ SetPageProxyState((value && (!aURI || aValid)) ? "valid" : "invalid", uri);
+}
+
+function losslessDecodeURI(aURI) {
+ var value = aURI.spec;
+ var scheme = aURI.scheme;
+
+ var decodeASCIIOnly = !["https", "http", "file", "ftp"].includes(scheme);
+ // Try to decode as UTF-8 if there's no encoding sequence that we would break.
+ if (!/%25(?:3B|2F|3F|3A|40|26|3D|2B|24|2C|23)/i.test(value)) {
+ if (decodeASCIIOnly) {
+ // This only decodes ascii characters (hex) 20-7e, except 25 (%).
+ // This avoids both cases stipulated below (%-related issues, and \r, \n
+ // and \t, which would be %0d, %0a and %09, respectively) as well as any
+ // non-US-ascii characters.
+ value = value.replace(/%(2[0-4]|2[6-9a-f]|[3-6][0-9a-f]|7[0-9a-e])/g, decodeURI);
+ } else {
+ try {
+ value = decodeURI(value)
+ // 1. decodeURI decodes %25 to %, which creates unintended
+ // encoding sequences. Re-encode it, unless it's part of
+ // a sequence that survived decodeURI, i.e. one for:
+ // ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#'
+ // (RFC 3987 section 3.2)
+ // 2. Ee-encode all adjacent whitespace, to prevent spoofing
+ // attempts where invisible characters would push part of
+ // the URL to overflow the location bar (bug 1395508).
+ .replace(/%(?!3B|2F|3F|3A|40|26|3D|2B|24|2C|23)|\s(?=\s)|\s$/ig,
+ encodeURIComponent);
+ } catch (e) {}
+ }
+ }
+
+ // Encode invisible characters (soft hyphen, zero-width space, BOM,
+ // line and paragraph separator, word joiner, invisible times,
+ // invisible separator, object replacement character,
+ // C0/C1 controls). (bug 452979, bug 909264)
+ // Encode bidirectional formatting characters.
+ // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
+ // Re-encode whitespace so that it doesn't get eaten away
+ // by the location bar (bug 410726).
+ return value.replace(/[\u0000-\u001f\u007f-\u00a0\u00ad\u034f\u061c\u115f\u1160\u17b4\u17b5\u180b-\u180d\u200b\u200e\u200f\u2028-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8\ufffc]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g, encodeURIComponent);
+}
+
+/**
+ * Use Stylesheet functions.
+ * Written by Tim Hill (bug 6782)
+ * Frameset handling by Neil Rashbrook <neil@parkwaycc.co.uk>
+ **/
+/**
+ * Adds this frame's stylesheet sets to the View > Use Style submenu
+ *
+ * If the frame has no preferred stylesheet set then the "Default style"
+ * menuitem should be shown. Note that it defaults to checked, hidden.
+ *
+ * If this frame has a selected stylesheet set then its menuitem should
+ * be checked (unless the "None" style is currently selected), and the
+ * "Default style" menuitem should to be unchecked.
+ *
+ * The stylesheet sets may match those of other frames. In that case, the
+ * checkmark should be removed from sets that are not selected in this frame.
+ *
+ * @param menuPopup The submenu's popup child
+ * @param frame The frame whose sets are to be added
+ * @param styleDisabled True if the "None" style is currently selected
+ * @param itemPersistentOnly The "Default style" menuitem element
+ */
+function stylesheetFillFrame(menuPopup, frame, styleDisabled, itemPersistentOnly)
+{
+ if (!frame.document.preferredStyleSheetSet)
+ itemPersistentOnly.hidden = false;
+
+ var title = frame.document.selectedStyleSheetSet;
+ if (title)
+ itemPersistentOnly.removeAttribute("checked");
+
+ var styleSheetSets = frame.document.styleSheetSets;
+ for (var i = 0; i < styleSheetSets.length; i++) {
+ var styleSheetSet = styleSheetSets[i];
+ var menuitem = menuPopup.getElementsByAttribute("data", styleSheetSet).item(0);
+ if (menuitem) {
+ if (styleSheetSet != title)
+ menuitem.removeAttribute("checked");
+ } else {
+ var menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("type", "radio");
+ menuItem.setAttribute("label", styleSheetSet);
+ menuItem.setAttribute("data", styleSheetSet);
+ menuItem.setAttribute("checked", styleSheetSet == title && !styleDisabled);
+ menuPopup.appendChild(menuItem);
+ }
+ }
+}
+/**
+ * Adds all available stylesheet sets to the View > Use Style submenu
+ *
+ * If all frames have preferred stylesheet sets then the "Default style"
+ * menuitem should remain hidden, otherwise it should be shown, and
+ * if some frames have a selected stylesheet then the "Default style"
+ * menuitem should be unchecked, otherwise it should remain checked.
+ *
+ * A stylesheet set's menuitem should not be checked if the "None" style
+ * is currently selected. Otherwise a stylesheet set may be available in
+ * more than one frame. In such a case the menuitem should only be checked
+ * if it is selected in all frames in which it is available.
+ *
+ * @param menuPopup The submenu's popup child
+ * @param frameset The frameset whose sets are to be added
+ * @param styleDisabled True if the "None" style is currently selected
+ * @param itemPersistentOnly The "Default style" menuitem element
+ */
+function stylesheetFillAll(menuPopup, frameset, styleDisabled, itemPersistentOnly)
+{
+ stylesheetFillFrame(menuPopup, frameset, styleDisabled, itemPersistentOnly);
+ for (var i = 0; i < frameset.frames.length; i++) {
+ stylesheetFillAll(menuPopup, frameset.frames[i], styleDisabled, itemPersistentOnly);
+ }
+}
+/**
+ * Populates the View > Use Style submenu with all available stylesheet sets
+ * @param menuPopup The submenu's popup child
+ */
+function stylesheetFillPopup(menuPopup)
+{
+ /* Clear menu */
+ var itemPersistentOnly = menuPopup.firstChild.nextSibling;
+ while (itemPersistentOnly.nextSibling)
+ itemPersistentOnly.nextSibling.remove();
+
+ /* Reset permanent items */
+ var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
+ menuPopup.firstChild.setAttribute("checked", styleDisabled);
+ itemPersistentOnly.setAttribute("checked", !styleDisabled);
+ itemPersistentOnly.hidden = true;
+
+ stylesheetFillAll(menuPopup, window.content, styleDisabled, itemPersistentOnly);
+}
+/**
+ * Switches all frames in a frameset to the same stylesheet set
+ *
+ * Only frames that support the given title will be switched
+ *
+ * @param frameset The frameset whose frames are to be switched
+ * @param title The name of the stylesheet set to switch to
+ */
+function stylesheetSwitchAll(frameset, title) {
+ if (!title || frameset.document.styleSheetSets.contains(title)) {
+ frameset.document.selectedStyleSheetSet = title;
+ }
+ for (var i = 0; i < frameset.frames.length; i++) {
+ stylesheetSwitchAll(frameset.frames[i], title);
+ }
+}
+
+function setStyleDisabled(disabled) {
+ getMarkupDocumentViewer().authorStyleDisabled = disabled;
+}
+
+function URLBarFocusHandler(aEvent)
+{
+ if (gIgnoreFocus)
+ gIgnoreFocus = false;
+ else if (gClickSelectsAll)
+ gURLBar.select();
+}
+
+function URLBarMouseDownHandler(aEvent)
+{
+ if (gURLBar.hasAttribute("focused")) {
+ gIgnoreClick = true;
+ } else {
+ gIgnoreFocus = true;
+ gIgnoreClick = false;
+ gURLBar.setSelectionRange(0, 0);
+ }
+}
+
+function URLBarClickHandler(aEvent)
+{
+ if (!gIgnoreClick && gClickSelectsAll && gURLBar.selectionStart == gURLBar.selectionEnd)
+ if (gClickAtEndSelects || gURLBar.selectionStart < gURLBar.value.length)
+ gURLBar.select();
+}
+
+function ShowAndSelectContentsOfURLBar()
+{
+ if (!isElementVisible(gURLBar)) {
+ BrowserOpenWindow();
+ return;
+ }
+
+ if (gURLBar.value)
+ gURLBar.select();
+ else
+ gURLBar.focus();
+}
+
+// If "ESC" is pressed in the url bar, we replace the urlbar's value with the url of the page
+// and highlight it, unless it is about:blank, where we reset it to "".
+function handleURLBarRevert()
+{
+ var url = getWebNavigation().currentURI.spec;
+ var throbberElement = document.getElementById("navigator-throbber");
+
+ var isScrolling = gURLBar.userAction == "scrolling";
+
+ // don't revert to last valid url unless page is NOT loading
+ // and user is NOT key-scrolling through autocomplete list
+ if (!throbberElement.hasAttribute("busy") && !isScrolling) {
+ URLBarSetURI();
+
+ // If the value isn't empty, select it.
+ if (gURLBar.value)
+ gURLBar.select();
+ }
+
+ // tell widget to revert to last typed text only if the user
+ // was scrolling when they hit escape
+ return isScrolling;
+}
+
+function UpdatePageProxyState()
+{
+ if (gURLBar.value != gLastValidURLStr)
+ SetPageProxyState("invalid", null);
+}
+
+function SetPageProxyState(aState, aURI)
+{
+ if (!gProxyButton)
+ gProxyButton = document.getElementById("page-proxy-button");
+ if (!gProxyFavIcon)
+ gProxyFavIcon = document.getElementById("page-proxy-favicon");
+ if (!gProxyDeck)
+ gProxyDeck = document.getElementById("page-proxy-deck");
+
+ gProxyButton.setAttribute("pageproxystate", aState);
+
+ if (aState == "valid") {
+ gLastValidURLStr = gURLBar.value;
+ gURLBar.addEventListener("input", UpdatePageProxyState);
+ if (gBrowser.shouldLoadFavIcon(aURI)) {
+ var favStr = gBrowser.buildFavIconString(aURI);
+ if (favStr != gProxyFavIcon.src) {
+ gBrowser.loadFavIcon(aURI, "src", gProxyFavIcon);
+ gProxyDeck.selectedIndex = 0;
+ }
+ else gProxyDeck.selectedIndex = 1;
+ }
+ else {
+ gProxyDeck.selectedIndex = 0;
+ gProxyFavIcon.removeAttribute("src");
+ }
+ } else if (aState == "invalid") {
+ gURLBar.removeEventListener("input", UpdatePageProxyState);
+ gProxyDeck.selectedIndex = 0;
+ gProxyFavIcon.removeAttribute("src");
+ }
+}
+
+function handlePageProxyClick(aEvent)
+{
+ switch (aEvent.button) {
+ case 0:
+ // bug 52784 - select location field contents
+ gURLBar.select();
+ break;
+ case 1:
+ // bug 111337 - load url/keyword from clipboard
+ middleMousePaste(aEvent);
+ break;
+ }
+}
+
+function updateComponentBarBroadcaster()
+{
+ var compBarBroadcaster = document.getElementById('cmd_viewcomponentbar');
+ var taskBarBroadcaster = document.getElementById('cmd_viewtaskbar');
+ var compBar = document.getElementById('component-bar');
+ if (taskBarBroadcaster.getAttribute('checked') == 'true') {
+ compBarBroadcaster.removeAttribute('disabled');
+ if (compBar.getAttribute('hidden') != 'true')
+ compBarBroadcaster.setAttribute('checked', 'true');
+ }
+ else {
+ compBarBroadcaster.setAttribute('disabled', 'true');
+ compBarBroadcaster.removeAttribute('checked');
+ }
+}
+
+function updateToolbarStates(aEvent)
+{
+ onViewToolbarsPopupShowing(aEvent);
+ updateComponentBarBroadcaster();
+
+ const tabbarMenuItem = document.getElementById("menuitem_showhide_tabbar");
+ // Make show/hide menu item reflect current state
+ const visibility = gBrowser.getStripVisibility();
+ tabbarMenuItem.setAttribute("checked", visibility);
+
+ // Don't allow the tab bar to be shown/hidden when more than one tab is open
+ // or when we have 1 tab and the autoHide pref is set
+ const disabled = gBrowser.browsers.length > 1 ||
+ Services.prefs.getBoolPref("browser.tabs.autoHide");
+ tabbarMenuItem.setAttribute("disabled", disabled);
+}
+
+function showHideTabbar()
+{
+ const visibility = gBrowser.getStripVisibility();
+ Services.prefs.setBoolPref("browser.tabs.forceHide", visibility);
+ gBrowser.setStripVisibilityTo(!visibility);
+}
+
+function BrowserFullScreen()
+{
+ window.fullScreen = !window.fullScreen;
+}
+
+function onFullScreen()
+{
+ FullScreen.toggle();
+}
+
+function UpdateStatusBarPopupIcon(aEvent)
+{
+ if (aEvent && aEvent.originalTarget != gBrowser.getNotificationBox())
+ return;
+
+ var showIcon = Services.prefs.getBoolPref("privacy.popups.statusbar_icon_enabled");
+ if (showIcon) {
+ var popupIcon = document.getElementById("popupIcon");
+ popupIcon.hidden = !gBrowser.getNotificationBox().popupCount;
+ }
+}
+function StatusbarViewPopupManager()
+{
+ // Open Data Manager permissions pane site and type prefilled to add.
+ toDataManager(hostUrl() + "|permissions|add|popup");
+}
+
+function popupBlockerMenuShowing(event)
+{
+ var separator = document.getElementById("popupMenuSeparator");
+
+ if (separator)
+ separator.hidden = !createShowPopupsMenu(event.target, gBrowser.selectedBrowser);
+}
+
+function WindowIsClosing()
+{
+ var browser = getBrowser();
+ var cn = browser.tabContainer.childNodes;
+ var numtabs = cn.length;
+
+ if (!gPrivate && AppConstants.platform != "macosx" && isClosingLastBrowser()) {
+ let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(closingCanceled, "browser-lastwindow-close-requested");
+ if (closingCanceled.data)
+ return false;
+
+ Services.obs.notifyObservers(null, "browser-lastwindow-close-granted");
+
+ return true;
+ }
+
+ var reallyClose = gPrivate ||
+ browser.warnAboutClosingTabs(browser.closingTabsEnum.ALL);
+
+ for (var i = 0; reallyClose && i < numtabs; ++i) {
+ var ds = browser.getBrowserForTab(cn[i]).docShell;
+
+ if (ds.contentViewer && !ds.contentViewer.permitUnload())
+ reallyClose = false;
+ }
+
+ return reallyClose;
+}
+
+/**
+ * Checks whether this is the last full *browser* window around.
+ * @returns true if closing last browser window, false if not.
+ */
+function isClosingLastBrowser() {
+ // Popups aren't considered full browser windows.
+ if (!toolbar.visible)
+ return false;
+
+ // Figure out if there's at least one other browser window around.
+ var e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ let win = e.getNext();
+ if (!win.closed && win != window && win.toolbar.visible)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * file upload support
+ */
+
+/* This function returns the URI of the currently focused content frame
+ * or frameset.
+ */
+function getCurrentURI()
+{
+ var focusedWindow = document.commandDispatcher.focusedWindow;
+ var contentFrame = isContentFrame(focusedWindow) ? focusedWindow : window.content;
+
+ var nav = contentFrame.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation);
+ return nav.currentURI;
+}
+
+var gLastOpenUploadDirectory;
+
+function BrowserUploadFile()
+{
+ if (!gLastOpenUploadDirectory) {
+ gLastOpenUploadDirectory = new RememberLastDir("browser.upload");
+ };
+
+ const nsIFilePicker = Ci.nsIFilePicker;
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ fp.init(window, gNavigatorBundle.getString("uploadFile"), nsIFilePicker.modeOpen);
+ fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
+ nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
+
+ // use a pref to remember the filterIndex selected by the user.
+ fp.filterIndex = gLastOpenUploadDirectory.filterIndex;
+
+ // Use a pref to remember the displayDirectory selected by the user.
+ fp.displayDirectory = gLastOpenUploadDirectory.path;
+
+ fp.open(rv => {
+ if (rv != nsIFilePicker.returnOK || !fp.fileURL) {
+ return;
+ }
+ gLastOpenUploadDirectory.filterIndex = fp.filterIndex;
+ gLastOpenUploadDirectory.path = fp.file.parent.QueryInterface(Ci.nsIFile);
+
+ try {
+ var targetBaseURI = getCurrentURI();
+ // Generate the target URI. We use fileURL.file.leafName to get the
+ // unicode value of the target filename w/o any URI-escaped chars.
+ // this gives the protocol handler the best chance of generating a
+ // properly formatted URI spec. we pass null for the origin charset
+ // parameter since we want the URI to inherit the origin charset
+ // property from targetBaseURI.
+ var leafName = fp.fileURL.QueryInterface(Ci.nsIFileURL).file.leafName;
+ var targetURI = Services.io.newURI(leafName, null, targetBaseURI);
+
+ // ok, start uploading...
+ openDialog("chrome://communicator/content/downloads/uploadProgress.xul", "",
+ "titlebar,centerscreen,minimizable,dialog=no", fp.fileURL, targetURI);
+ } catch (e) {}
+ });
+}
+
+/* This function is called whenever the file menu is about to be displayed.
+ * Enable the upload menu item if appropriate. */
+function updateFileUploadItem()
+{
+ var canUpload = false;
+ try {
+ canUpload = getCurrentURI().schemeIs('ftp');
+ } catch (e) {}
+
+ var item = document.getElementById('Browser:UploadFile');
+ if (canUpload)
+ item.removeAttribute('disabled');
+ else
+ item.setAttribute('disabled', 'true');
+}
+
+function isBidiEnabled()
+{
+ // first check the pref.
+ if (Services.prefs.getBoolPref("bidi.browser.ui", false)) {
+ return true;
+ }
+
+ // now see if the app locale is an RTL one.
+ const isRTL = Services.locale.isAppLocaleRTL;
+
+ if (isRTL) {
+ Services.prefs.setBoolPref("bidi.browser.ui", true);
+ }
+ return isRTL;
+}
+
+function SwitchDocumentDirection(aWindow)
+{
+ aWindow.document.dir = (aWindow.document.dir == "ltr" ? "rtl" : "ltr");
+
+ for (var run = 0; run < aWindow.frames.length; run++)
+ SwitchDocumentDirection(aWindow.frames[run]);
+}
+
+function updateSavePageItems()
+{
+ var autoDownload = Services.prefs
+ .getBoolPref("browser.download.useDownloadDir");
+ goSetMenuValue("savepage", autoDownload ? "valueSave" : "valueSaveAs");
+}
+
+function convertFromUnicode(charset, str)
+{
+ try {
+ var unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ unicodeConverter.charset = charset;
+ str = unicodeConverter.ConvertFromUnicode(str);
+ return str + unicodeConverter.Finish();
+ } catch(ex) {
+ return null;
+ }
+}
+
+function getNotificationBox(aWindow)
+{
+ return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler.parentNode.wrappedJSObject;
+}
+
+function BrowserToolboxCustomizeInit()
+{
+ SetPageProxyState("invalid", null);
+ toolboxCustomizeInit("main-menubar");
+ PlacesToolbarHelper.customizeStart();
+
+ var splitter = document.getElementById("urlbar-search-splitter");
+ if (splitter)
+ splitter.remove();
+}
+
+function BrowserToolboxCustomizeDone(aToolboxChanged)
+{
+ toolboxCustomizeDone("main-menubar", getNavToolbox(), aToolboxChanged);
+
+ UpdateNavBar();
+
+ // Update the urlbar
+ var value = gBrowser.userTypedValue;
+ if (value == null)
+ URLBarSetURI();
+ else
+ gURLBar.value = value;
+
+ PlacesToolbarHelper.customizeDone();
+}
+
+function BrowserToolboxCustomizeChange(event)
+{
+ toolboxCustomizeChange(getNavToolbox(), event);
+}
+
+var LightWeightThemeWebInstaller = {
+ handleEvent: function (event) {
+ switch (event.type) {
+ case "InstallBrowserTheme":
+ case "PreviewBrowserTheme":
+ case "ResetBrowserThemePreview":
+ // ignore requests from background tabs
+ if (event.target.ownerDocument.defaultView.top != content)
+ return;
+ }
+ switch (event.type) {
+ case "InstallBrowserTheme":
+ this._installRequest(event);
+ break;
+ case "PreviewBrowserTheme":
+ this._preview(event);
+ break;
+ case "ResetBrowserThemePreview":
+ this._resetPreview(event);
+ break;
+ case "pagehide":
+ case "TabSelect":
+ this._resetPreview();
+ break;
+ }
+ },
+
+ get _manager () {
+ delete this._manager;
+ return this._manager = LightweightThemeManager;
+ },
+
+ _installRequest: function (event) {
+ var node = event.target;
+ var data = this._getThemeFromNode(node);
+ if (!data)
+ return;
+
+ if (this._isAllowed(node)) {
+ this._install(data);
+ return;
+ }
+
+ this._removePreviousNotifications();
+ getBrowser().getNotificationBox().lwthemeInstallRequest(
+ node.ownerDocument.location.host,
+ this._install.bind(this, data));
+ },
+
+ _install: function (newTheme) {
+ this._removePreviousNotifications();
+
+ var previousTheme = this._manager.currentTheme;
+ this._manager.currentTheme = newTheme;
+ if (this._manager.currentTheme &&
+ this._manager.currentTheme.id == newTheme.id)
+ getBrowser().getNotificationBox().lwthemeInstallNotification(function() {
+ LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
+ LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
+ });
+ else
+ getBrowser().getNotificationBox().lwthemeNeedsRestart(newTheme.name);
+
+ // We've already destroyed the permission notification,
+ // so tell the former that it's closed already.
+ return true;
+ },
+
+ _removePreviousNotifications: function () {
+ getBrowser().getNotificationBox().removeNotifications(
+ ["lwtheme-install-request", "lwtheme-install-notification"]);
+ },
+
+ _previewWindow: null,
+ _preview: function (event) {
+ if (!this._isAllowed(event.target))
+ return;
+
+ var data = this._getThemeFromNode(event.target);
+ if (!data)
+ return;
+
+ this._resetPreview();
+
+ this._previewWindow = event.target.ownerDocument.defaultView;
+ this._previewWindow.addEventListener("pagehide", this, true);
+ gBrowser.tabContainer.addEventListener("TabSelect", this);
+
+ this._manager.previewTheme(data);
+ },
+
+ _resetPreview: function (event) {
+ if (!this._previewWindow ||
+ event && !this._isAllowed(event.target))
+ return;
+
+ this._previewWindow.removeEventListener("pagehide", this, true);
+ this._previewWindow = null;
+ gBrowser.tabContainer.removeEventListener("TabSelect", this);
+
+ this._manager.resetPreview();
+ },
+
+ _isAllowed: function (node) {
+ var uri = node.ownerDocument.documentURIObject;
+ return Services.perms.testPermission(uri, "install") == Services.perms.ALLOW_ACTION;
+ },
+
+ _getThemeFromNode: function (node) {
+ return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
+ node.baseURI);
+ }
+}
+
+function AddKeywordForSearchField() {
+ var node = document.popupNode;
+ var doc = node.ownerDocument;
+ var charset = doc.characterSet;
+ var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
+ [doc.title]);
+ var description = PlacesUIUtils.getDescriptionFromDocument(doc);
+ var postData = null;
+ var form = node.form;
+ var spec = form.action || doc.documentURI;
+
+ function encodeNameValuePair(aName, aValue) {
+ return encodeURIComponent(aName) + "=" + encodeURIComponent(aValue);
+ }
+
+ let el = null;
+ let type = null;
+ let formData = [];
+ for (var i = 0; i < form.elements.length; i++) {
+ el = form.elements[i];
+
+ if (!el.type) // happens with fieldsets
+ continue;
+
+ if (el == node) {
+ formData.push(encodeNameValuePair(el.name, "") + "%s");
+ continue;
+ }
+
+ type = el.type;
+
+ if (((el instanceof HTMLInputElement && el.mozIsTextField(true)) ||
+ type == "hidden" || type == "textarea") ||
+ ((type == "checkbox" || type == "radio") && el.checked)) {
+ formData.push(encodeNameValuePair(el.name, el.value));
+ } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
+ for (var j = 0; j < el.options.length; j++) {
+ if (el.options[j].selected)
+ formData.push(encodeNameValuePair(el.name, el.options[j].value));
+ }
+ }
+ }
+
+ if (form.method == "post" &&
+ form.enctype == "application/x-www-form-urlencoded") {
+ postData = formData.join("&");
+ } else { // get
+ spec += spec.includes("?") ? "&" : "?";
+ spec += formData.join("&");
+ }
+
+ PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(spec), title, description, null,
+ null, null, "", postData, charset);
+}
+
+function getCert()
+{
+ var sslStatus = getBrowser().securityUI
+ .QueryInterface(Ci.nsISSLStatusProvider)
+ .SSLStatus;
+
+ return sslStatus && sslStatus.serverCert;
+}
+
+function viewCertificate()
+{
+ var cert = getCert();
+
+ if (cert)
+ {
+ Cc["@mozilla.org/nsCertificateDialogs;1"]
+ .getService(Ci.nsICertificateDialogs)
+ .viewCert(window, cert);
+ }
+}
+
+function openCertManager()
+{
+ toOpenWindowByType("mozilla:certmanager", "chrome://pippki/content/certManager.xul",
+ "resizable,dialog=no,centerscreen");
+}
+
+function onViewSecurityContextMenu()
+{
+ document.getElementById("viewCertificate").disabled = !getCert();
+}
+
+function updateZoomStatus() {
+ let newLabel = Math.round(ZoomManager.zoom * 100) + " %";
+ let zoomStatusElement = document.getElementById("zoomLevel-display");
+ if (zoomStatusElement.getAttribute('label') != newLabel){
+ zoomStatusElement.setAttribute('label', newLabel);
+ }
+}
+
+function zoomIn() {
+ FullZoom.enlarge();
+ updateZoomStatus();
+}
+
+function zoomOut() {
+ FullZoom.reduce();
+ updateZoomStatus();
+}
+
+/**
+ * Determine whether or not a given focused DOMWindow is in the content area.
+ **/
+function isContentFrame(aFocusedWindow) {
+ if (!aFocusedWindow)
+ return false;
+
+ return (aFocusedWindow.top == window.content);
+}
+
+var browserDragAndDrop = {
+ canDropLink: aEvent => Services.droppedLinkHandler.canDropLink(aEvent, true),
+
+ dragOver(aEvent) {
+ if (this.canDropLink(aEvent)) {
+ aEvent.preventDefault();
+ }
+ },
+
+ getTriggeringPrincipal(aEvent) {
+ return Services.droppedLinkHandler.getTriggeringPrincipal(aEvent);
+ },
+
+ dropLinks(aEvent, aDisallowInherit) {
+ return Services.droppedLinkHandler.dropLinks(aEvent, aDisallowInherit);
+ }
+};
diff --git a/comm/suite/browser/navigator.xul b/comm/suite/browser/navigator.xul
new file mode 100644
index 0000000000..89a0dae076
--- /dev/null
+++ b/comm/suite/browser/navigator.xul
@@ -0,0 +1,571 @@
+<?xml version="1.0"?> <!-- -*- Mode: HTML; indent-tabs-mode: nil -*- -->
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<?xml-stylesheet href="chrome://navigator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/places/places.css" type="text/css"?>
+
+<?xul-overlay href="chrome://navigator/content/navigatorOverlay.xul"?>
+<?xul-overlay href="chrome://navigator/content/linkToolbarOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/contentAreaContextOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/sidebar/sidebarOverlay.xul"?>
+<?xul-overlay href="chrome://navigator/content/safeBrowsingOverlay.xul"?>
+<?xul-overlay href="chrome://navigator/content/webDeveloperOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % navigatorDTD SYSTEM "chrome://navigator/locale/navigator.dtd" >
+%navigatorDTD;
+]>
+
+<window id="main-window"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="Startup()" onunload="Shutdown()"
+ onclose="return WindowIsClosing();"
+ contenttitlesetting="true"
+ title="&mainWindow.title;"
+ titlemodifier="&mainWindow.titlemodifier;"
+ titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
+ titleprivate="&mainWindow.titleprivate;"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ windowtype="navigator:browser"
+ macanimationtype="document"
+ retargetdocumentfocus="urlbar"
+ drawtitle="true"
+ persist="screenX screenY width height sizemode">
+
+ <!-- Generic Utility -->
+ <script src="chrome://global/content/viewSourceUtils.js"/>
+
+ <!-- Content Area -->
+ <script src="chrome://navigator/content/nsBrowserStatusHandler.js"/>
+ <script src="chrome://navigator/content/nsBrowserContentListener.js"/>
+ <script src="chrome://communicator/content/contentAreaClick.js"/>
+ <script src="chrome://communicator/content/findUtils.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+
+ <!-- Navigator -->
+ <script src="chrome://navigator/content/fullScreen.js"/>
+ <script src="chrome://navigator/content/navigatorDD.js"/>
+ <script src="chrome://navigator/content/sessionHistoryUI.js"/>
+
+ <!-- Places Bookmarks Utilities -->
+ <script src="chrome://navigator/content/browser-places.js"/>
+
+ <!-- hook for stringbundle overlays -->
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
+ </stringbundleset>
+
+ <commandset id="commands">
+ <commandset id="findTypeMenuItems"/>
+ <command id="toggleSidebar"/>
+ </commandset>
+
+ <commandset id="mainCommandSet"/> <!-- Firefox extension compatibility -->
+
+ <!-- broadcasters are appended from the overlay -->
+ <broadcasterset id="navBroadcasters"/>
+ <broadcasterset id="mainBroadcasterSet"/> <!-- Firefox extension compatibility -->
+
+ <!-- keys are appended from the overlay -->
+ <keyset id="navKeys">
+ <key id="showHideSidebar"/>
+ </keyset>
+ <keyset id="mainKeyset"/> <!-- Firefox extension compatibility -->
+
+ <popupset id="mainPopupSet">
+ <menupopup id="backMenu"
+ position="after_start"
+ onpopupshowing="return BrowserBackMenu(event);"
+ oncommand="gotoHistoryIndex(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menupopup id="forwardMenu"
+ position="after_start"
+ onpopupshowing="return BrowserForwardMenu(event);"
+ oncommand="gotoHistoryIndex(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <tooltip id="aHTMLTooltip"
+ onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
+ <menupopup id="sidebarPopup"/>
+
+ <tooltip id="home-button-tooltip" noautohide="true">
+ <vbox id="home-button-tooltip-inner" flex="1"/>
+ </tooltip>
+
+ <menupopup id="toolbar-context-menu"/>
+
+ <menupopup id="feedsPopup" popupanchor="bottomright" popupalign="topright"
+ onpopupshowing="window.XULBrowserWindow.populateFeeds(this);"
+ oncommand="subscribeToFeed(event.target.statusText, event);"
+ onclick="checkForMiddleClick(this, event);"/>
+
+ <!-- for content formfill and password manager -->
+ <panel id="PopupAutoComplete"
+ type="autocomplete-richlistbox"
+ noautofocus="true"
+ hidden="true"/>
+
+ <!-- for date/time picker. consumeoutsideclicks is set to never, so that
+ clicks on the anchored input box are never consumed. -->
+ <panel id="DateTimePickerPanel"
+ type="arrow"
+ hidden="true"
+ orient="vertical"
+ noautofocus="true"
+ norolluponanchor="true"
+ consumeoutsideclicks="never"
+ level="parent">
+ </panel>
+
+ <!-- for invalid form error message -->
+ <panel id="invalid-form-popup" noautofocus="true" hidden="true" level="parent">
+ <description/>
+ </panel>
+
+ <panel id="editBookmarkPanel"
+ type="arrow"
+ animate="false"
+ orient="vertical"
+ ignorekeys="true"
+ hidden="true"
+ onpopupshown="StarUI.panelShown(event);"
+ aria-labelledby="editBookmarkPanelTitle">
+ <row id="editBookmarkPanelHeader" align="center" hidden="true">
+ <vbox align="center">
+ <image id="editBookmarkPanelStarIcon"/>
+ </vbox>
+ <label id="editBookmarkPanelTitle"/>
+ </row>
+ <vbox id="editBookmarkPanelContent" hidden="true"/>
+ <hbox id="editBookmarkPanelBottomButtons">
+ <button id="editBookmarkPanelRemoveButton"
+ class="editBookmarkPanelHeaderButton"
+ oncommand="StarUI.removeBookmarkButtonCommand();"
+ accesskey="&editBookmark.removeBookmark.accessKey;"/>
+ <spacer flex="1"/>
+ <button id="editBookmarkPanelDeleteButton"
+ class="editBookmarkPanelBottomButton"
+ label="&editBookmark.cancel.label;"
+ oncommand="StarUI.cancelButtonOnCommand();"/>
+ <button id="editBookmarkPanelDoneButton"
+ class="editBookmarkPanelBottomButton"
+ label="&editBookmark.done.label;"
+ default="true"
+ oncommand="StarUI.panel.hidePopup();"/>
+ </hbox>
+ </panel>
+
+ <menupopup id="placesContext"/>
+
+ <!-- Bookmarks and history tooltip -->
+ <tooltip id="bhTooltip"/>
+
+ <panel id="notification-popup"
+ type="arrow"
+ animate="false"
+ position="after_start"
+ hidden="true"
+ role="alert"/>
+
+ <menupopup id="popupBlockerMenu"
+ oncommand="popupBlockerMenuCommand(event.target);"
+ onpopupshowing="return popupBlockerMenuShowing(event);"
+ onpopuphiding="RemovePopupsItems(this);"/>
+ <!-- Items are generated, see popupBlockerMenuShowing() -->
+ <menupopup id="popupNotificationMenu"/>
+ <menupopup id="networkProperties"/>
+
+ <menupopup id="security-context-menu"
+ onpopupshowing="onViewSecurityContextMenu();">
+ <menuitem id="viewSecurityInfo"
+ default="true"
+ label="&viewSecurityInfo.label;"
+ accesskey="&viewSecurityInfo.accesskey;"
+ oncommand="BrowserPageInfo(null, 'securityTab');"/>
+ <menuitem id="viewCertificate"
+ label="&viewCertificate.label;"
+ accesskey="&viewCertificate.accesskey;"
+ oncommand="viewCertificate();"/>
+ <menuseparator/>
+ <menuitem id="viewCertManager"
+ label="&viewCertManager.label;"
+ accesskey="&viewCertManager.accesskey;"
+ oncommand="openCertManager();"/>
+ </menupopup>
+
+ <popupnotification id="password-notification" hidden="true">
+ <popupnotificationcontent orient="vertical">
+ <textbox id="password-notification-username"/>
+ <textbox id="password-notification-password" type="password" show-content=""/>
+ <checkbox id="password-notification-visibilityToggle" hidden="true"/>
+ </popupnotificationcontent>
+ </popupnotification>
+
+ </popupset>
+
+ <!-- context menu -->
+ <popupset id="contentAreaContextSet"/>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="navigator-toolbox" class="toolbox-top" deferattached="true"
+ mode="full" defaultmode="full">
+ <!-- Menu -->
+ <toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar"
+ persist="collapsed" grippytooltiptext="&menuBar.tooltip;" customizable="true"
+ defaultset="menubar-items"
+ mode="icons" iconsize="small"
+ defaultmode="icons" defaulticonsize="small"
+ context="toolbar-context-menu">
+ <toolbaritem id="menubar-items" class="menubar-items" align="center">
+ <menubar id="main-menubar"/>
+ </toolbaritem>
+ </toolbar>
+
+ <toolbar class="toolbar-primary chromeclass-toolbar" id="nav-bar" persist="collapsed"
+ grippytooltiptext="&navigationToolbar.tooltip;"
+ fullscreentoolbar="true" customizable="true"
+ toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
+ defaultset="back-button,forward-button,reload-button,stop-button,nav-bar-inner,search-button-container,print-button,throbber-box,window-controls"
+ context="toolbar-context-menu">
+
+ <hbox id="window-controls" hidden="true" fullscreencontrol="true">
+ <toolbarbutton id="minimize-button"
+ tooltiptext="&minimizeButton.tooltip;"
+ oncommand="window.minimize();"/>
+
+ <toolbarbutton id="restore-button"
+ tooltiptext="&restoreButton.tooltip;"
+ oncommand="BrowserFullScreen();"/>
+
+ <toolbarbutton id="close-button"
+ tooltiptext="&closeWindow.label;"
+ oncommand="BrowserTryToCloseWindow();"/>
+ </hbox>
+ </toolbar>
+
+ <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+
+ <toolbar id="PersonalToolbar"
+ accesskey="&bookmarksToolbarCmd.accesskey;"
+ class="chromeclass-directories"
+ persist="collapsed"
+ grippytooltiptext="&bookmarksToolbar.tooltip;"
+ toolbarname="&bookmarksToolbarCmd.label;"
+ nowindowdrag="true"
+ customizable="true"
+ defaultset="home-button,separator,bookmarks-button,personal-bookmarks"
+ mode="full"
+ iconsize="small"
+ labelalign="end"
+ defaultmode="full"
+ defaulticonsize="small"
+ defaultlabelalign="end"
+ context="toolbar-context-menu">
+ </toolbar>
+
+ <toolbarpalette id="BrowserToolbarPalette">
+
+ <!-- Nav bar buttons -->
+ <toolbarbutton id="back-button" type="menu-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&backButton.label;"
+ oncommand="if (event.target==this) BrowserBack(event); else gotoHistoryIndex(event);"
+ onclick="checkForMiddleClick(this, event);"
+ context="backMenu"
+ tooltiptext="&backButton.tooltip;">
+ <observes element="canGoBack" attribute="disabled"/>
+ <menupopup context="" onpopupshowing="BrowserBackMenu(event);"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="forward-button" type="menu-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&forwardButton.label;"
+ oncommand="if (event.target==this) BrowserForward(event); else gotoHistoryIndex(event);"
+ onclick="checkForMiddleClick(this, event);"
+ context="forwardMenu"
+ tooltiptext="&forwardButton.tooltip;">
+ <observes element="canGoForward" attribute="disabled"/>
+ <menupopup context="" onpopupshowing="BrowserForwardMenu(event);"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="reload-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&reloadButton.label;"
+ oncommand="BrowserReload(event);"
+ onclick="checkForMiddleClick(this, event);"
+ tooltiptext="&reloadButton.tooltip;"/>
+
+ <toolbarbutton id="stop-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&stopButton.label;"
+ oncommand="BrowserStop();" observes="canStop"
+ disabled="true"
+ tooltiptext="&stopButton.tooltip;">
+ </toolbarbutton>
+
+ <!-- XXXRatty ? class="toolbarbutton-1 chromeclass-toolbar-additional" ? -->
+ <toolbarbutton id="home-button"
+ class="toolbarbutton-1"
+ label="&homeButton.label;"
+ oncommand="BrowserHome(event);"
+ onclick="if (event.button == 1) BrowserHome(event);"
+ tooltip="home-button-tooltip"
+ ondragstart="homeButtonObserver.onDragStart(event);"
+ ondrop="homeButtonObserver.onDrop(event);"
+ ondragenter="event.stopPropagation();"
+ ondragexit="homeButtonObserver.onDragExit(event);"
+ ondragover="homeButtonObserver.onDragOver(event);"/>
+
+ <toolbaritem id="nav-bar-inner"
+ flex="4"
+ persist="width"
+ class="chromeclass-location nav-bar-class"
+ title="&locationBar.title;">
+ <textbox id="urlbar" class="chromeclass-location uri-element" flex="1"
+ type="autocomplete" autocompletesearch="history file"
+ timeout="50" maxrows="6"
+ enablehistory="true" accesskey="&locationBar.accesskey;"
+ defaultSearchEngine="true" tabscrolling="true"
+ showcommentcolumn="true"
+ placeholder="&locationBar.tooltip;"
+ newlines="stripsurroundingwhitespace"
+ aria-label="&locationBar.title;"
+ oninput="gBrowser.userTypedValue = this.value;"
+ ontextentered="return handleURLBarCommand(eventParam, domEvent);"
+ ontextreverted="return handleURLBarRevert();"
+ onfocus="URLBarFocusHandler(event);"
+ onmousedown="URLBarMouseDownHandler(event);"
+ onclick="URLBarClickHandler(event);">
+ <box id="notification-popup-box" hidden="true" align="center"
+ onmousedown="event.stopPropagation();"
+ onclick="event.stopPropagation();">
+ <image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
+ <image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
+ </box>
+ <deck id="page-proxy-deck"
+ class="urlbar-icons"
+ ondragstart="proxyIconDNDObserver.onDragStart(event);"
+ onclick="handlePageProxyClick(event);">
+ <image id="page-proxy-button"
+ tooltiptext="&proxyIcon.tooltip;"/>
+ <image id="page-proxy-favicon" validate="never"
+ onload="this.parentNode.selectedIndex = 1;
+ event.stopPropagation();"
+ onerror="gBrowser.addToMissedIconCache(this.src);"
+ tooltiptext="&proxyIcon.tooltip;"/>
+ </deck>
+ <hbox id="urlbar-icons" class="urlbar-icons"
+ onmousedown="event.stopPropagation();"
+ onclick="event.stopPropagation();">
+ <image id="feedsButton" hidden="true" popup="feedsPopup"/>
+ <image id="ev-button" hidden="true"
+ onclick="if (event.button == 0) BrowserPageInfo(null, 'securityTab');"/>
+ <image id="star-button"
+ onclick="BookmarkingUI.onClick(event);"/>
+ </hbox>
+ <menupopup id="ubhist-popup" class="autocomplete-history-popup"
+ popupalign="topleft" popupanchor="bottomleft"
+ onpopupshowing="createUBHistoryMenu(event.target);"
+ oncommand="executeUrlBarHistoryCommand(event.target);"/>
+ </textbox>
+ </toolbaritem>
+
+ <toolbaritem id="go-button-container"
+ class="nav-bar-class"
+ title="&goButton.label;">
+ <button id="go-button"
+ class="button-toolbar chromeclass-location"
+ label="&goButton.label;"
+ tooltiptext="&goButton.tooltip;"
+ default="true"
+ oncommand="handleURLBarCommand('none', event);"
+ onclick="checkForMiddleClick(this, event);"
+ ondragover="goButtonObserver.onDragOver(event);"
+ ondrop="goButtonObserver.onDrop(event);"/>
+ </toolbaritem>
+
+ <toolbaritem id="search-button-container"
+ class="nav-bar-class"
+ title="&searchButton.label;">
+ <button id="search-button"
+ class="button-toolbar chromeclass-location"
+ label="&searchButton.label;"
+ tooltiptext="&searchButton.tooltip;"
+ oncommand="BrowserSearch.loadSearch(QualifySearchTerm());"
+ ondragover="searchButtonObserver.onDragOver(event);"
+ ondrop="searchButtonObserver.onDrop(event);"/>
+ </toolbaritem>
+
+ <toolbaritem id="search-container" title="&searchItem.title;"
+ align="center" class="chromeclass-toolbar-additional nav-bar-class"
+ flex="1" persist="width" removable="true">
+ <searchbar id="searchbar" flex="1"/>
+ </toolbaritem>
+
+ <toolbarbutton id="print-button"
+ label="&printButton.label;"
+ tooltiptext="&printButton.tooltip;"/>
+
+ <toolbaritem id="throbber-box"/>
+
+ <!-- "Bookmarks" button on Bookmarks Toolbar -->
+ <toolbarbutton type="menu" id="bookmarks-button"
+ class="bookmark-item" container="true"
+ label="&bookmarksButton.label;"
+ ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
+ ondragover="PlacesMenuDNDHandler.onDragOver(event);"
+ ondragexit="PlacesMenuDNDHandler.onDragExit(event);"
+ ondrop="PlacesMenuDNDHandler.onDrop(event);">
+ <menupopup id="BMB_bookmarksPopup"
+ placespopup="true"
+ context="placesContext"
+ openInTabs="children"
+ oncommand="BookmarksEventHandler.onCommand(event);"
+ onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
+ onpopupshowing="BookmarksMenu.onPopupShowing(event, 'BMB_');
+ BookmarksEventHandler.onPopupShowing(event);"
+ tooltip="bhTooltip" popupsinherittooltip="true">
+ <menuitem command="Browser:AddBookmark"/>
+ <menuitem command="Browser:AddBookmarkAs"/>
+ <menuitem command="Browser:BookmarkAllTabs"/>
+ <menuitem command="Browser:ManageBookmark"/>
+ <menuseparator/>
+ <menu id="BMB_feedsMenu" class="menu-iconic feedsMenu" command="feedsMenu"
+ label="&feedsMenu.label;" accesskey="&feedsMenu.accesskey;">
+ <menupopup onpopupshowing="window.XULBrowserWindow.populateFeeds(this);"
+ oncommand="subscribeToFeed(event.target.statusText, event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ </menu>
+ <menuseparator/>
+ <menu id="BMB_bookmarksToolbarFolderMenu"
+ class="menu-iconic bookmark-item"
+ label="&bookmarksToolbarCmd.label;"
+ container="true">
+ <menupopup id="BMB_bookmarksToolbarFolderPopup"
+ placespopup="true"
+ context="placesContext"
+ onpopupshowing="if (!this.parentNode._placesView)
+ new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+ </menu>
+ <menu id="BMB_unsortedBookmarksFolderMenu"
+ class="menu-iconic bookmark-item"
+ container="true">
+ <menupopup id="BMB_unsortedBookmarksFolderPopup"
+ placespopup="true"
+ context="placesContext"
+ onpopupshowing="if (!this.parentNode._placesView)
+ new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
+ </menu>
+ <menuseparator/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbaritem flex="1" id="personal-bookmarks" title="&bookmarksToolbarItem.label;"
+ removable="true">
+ <hbox flex="1"
+ id="PlacesToolbar"
+ context="placesContext"
+ onclick="BookmarksEventHandler.onClick(event, this._placesView);"
+ oncommand="BookmarksEventHandler.onCommand(event);"
+ tooltip="bhTooltip"
+ popupsinherittooltip="true">
+ <toolbarbutton class="bookmark-item bookmarks-toolbar-customize"
+ mousethrough="never"
+ label="&bookmarksToolbarItem.label;"/>
+ <hbox flex="1">
+ <hbox align="center">
+ <image id="PlacesToolbarDropIndicator"
+ mousethrough="always"
+ collapsed="true"/>
+ </hbox>
+ <scrollbox orient="horizontal"
+ id="PlacesToolbarItems"
+ flex="1"/>
+ <toolbarbutton type="menu"
+ id="PlacesChevron"
+ class="chevron"
+ mousethrough="never"
+ collapsed="true"
+ tooltiptext="&bookmarksToolbarChevron.tooltip;"
+ onpopupshowing="this.parentNode.parentNode
+ ._placesView._onChevronPopupShowing(event);">
+ <menupopup id="PlacesChevronPopup"
+ placespopup="true"
+ tooltip="bhTooltip" popupsinherittooltip="true"
+ context="placesContext"/>
+ </toolbarbutton>
+ </hbox>
+ </hbox>
+ </toolbaritem>
+
+ <!-- see utilityOverlay.xul
+ <toolbarbutton id="sync-button"/> -->
+ </toolbarpalette>
+ </toolbox>
+
+ <hbox flex="1">
+ <vbox id="sidebar-box" class="chromeclass-extrachrome" domfullscreenhidden="true"/>
+ <splitter id="sidebar-splitter" class="chromeclass-extrachrome" domfullscreenhidden="true"/>
+
+ <vbox id="appcontent" flex="1">
+ <findbar id="FindToolbar" browserid="content" domfullscreenhidden="true"/>
+
+ <!-- this box is temporary, pending XBLified <browser> -->
+ <hbox id="browser" flex="1">
+ <tabbrowser id="content"
+ flex="1" contenttooltip="aHTMLTooltip"
+ contentcontextmenu="contentAreaContextMenu"
+ onnewtab="BrowserOpenTab();"
+ onnewtabclick="checkForMiddleClick(this, event);"
+ autocompletepopup="PopupAutoComplete"
+ datetimepicker="DateTimePickerPanel"
+ onbookmarkgroup="PlacesCommandHook.bookmarkCurrentPages();"
+ oncontentclick="return contentAreaClick(event);"
+ oncommand="BrowserOnCommand(event);"/>
+ <!-- The oncommand listener above lets us fix bugs like 401575 which
+ require error page UI to do privileged things, without letting
+ error pages have any privilege themselves. -->
+ </hbox>
+ </vbox>
+ </hbox>
+
+ <panel id="customizeToolbarSheetPopup"/>
+
+ <statusbar id="status-bar" class="chromeclass-status">
+ <statusbarpanel id="component-bar"/>
+ <statusbarpanel id="statusbar-display" label="&statusText.label;" flex="1"/>
+ <statusbarpanel class="statusbarpanel-progress" id="statusbar-progresspanel" collapsed="true">
+ <progressmeter class="progressmeter-statusbar" id="statusbar-icon" mode="normal" value="0"/>
+ </statusbarpanel>
+ <statusbarpanel class="statusbarpanel-iconic-text zoom-button-align" id="zoomOut-button" label="[-]"
+ oncommand="zoomOut();"
+ tooltiptext="&zoomOut.tooltiptext;"/>
+ <statusbarpanel class="statusbarpanel-iconic-text" id="zoomLevel-display" label="" oncommand="zoomReset();"/>
+ <statusbarpanel class="statusbarpanel-iconic-text zoom-button-align" id="zoomIn-button" label="[+]"
+ oncommand="zoomIn();"
+ tooltiptext="&zoomIn.tooltiptext;"/>
+ <statusbarpanel id="popupIcon" class="statusbarpanel-iconic" hidden="true"
+ oncommand="StatusbarViewPopupManager()"
+ tooltiptext="&popupIcon.tooltiptext;"
+ context="popupBlockerMenu"/>
+ <statusbarpanel class="statusbarpanel-iconic" id="offline-status"/>
+ <statusbarpanel class="statusbarpanel-backgroundbox"
+ id="security-button" dir="reverse"
+ context="security-context-menu"
+ oncommand="BrowserPageInfo(null, 'securityTab')"/>
+ </statusbar>
+</window>
diff --git a/comm/suite/browser/navigatorDD.js b/comm/suite/browser/navigatorDD.js
new file mode 100644
index 0000000000..19c4b8f228
--- /dev/null
+++ b/comm/suite/browser/navigatorDD.js
@@ -0,0 +1,121 @@
+/* -*- 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/. */
+
+function htmlEscape(aString)
+{
+ return aString.replace(/&/g, "&amp;")
+ .replace(/>/g, "&gt;")
+ .replace(/</g, "&lt;")
+ .replace(/"/g, "&quot;")
+ .replace(/'/g, "&apos;");
+}
+
+function BeginDragLink(aEvent, aHref, aTitle)
+{
+ var dt = aEvent.dataTransfer;
+ dt.setData("text/x-moz-url", aHref + "\n" + aTitle);
+ dt.setData("text/uri-list", aHref);
+ dt.setData("text/html", "<a href=\"" + htmlEscape(aHref) +
+ "\">" + htmlEscape(aTitle) + "</a>");
+ dt.setData("text/plain", aHref);
+}
+
+function DragLinkOver(aEvent)
+{
+ if (Services.droppedLinkHandler.canDropLink(aEvent, true))
+ aEvent.preventDefault();
+}
+
+var proxyIconDNDObserver = {
+ onDragStart: function (aEvent)
+ {
+ if (gProxyButton.getAttribute("pageproxystate") != "valid")
+ return;
+
+ BeginDragLink(aEvent, window.content.location.href,
+ window.content.document.title);
+ }
+};
+
+var homeButtonObserver = {
+ onDragStart: function (aEvent)
+ {
+ var homepage = GetLocalizedStringPref("browser.startup.homepage",
+ "about:blank");
+
+ if (homepage)
+ {
+ // XXX find a readable title string for homepage,
+ // perhaps do a history lookup.
+ BeginDragLink(aEvent, homepage, homepage);
+ }
+ },
+
+ onDrop: function (aEvent)
+ {
+ aEvent.stopPropagation();
+ // disallow setting home pages that inherit the principal
+ var url = Services.droppedLinkHandler.dropLink(aEvent, {}, true);
+ setTimeout(openHomeDialog, 0, url);
+ },
+
+ onDragOver: function (aEvent)
+ {
+ if (aEvent.target == aEvent.dataTransfer.mozSourceNode)
+ return;
+
+ DragLinkOver(aEvent);
+ aEvent.dropEffect = "link";
+ var statusTextFld = document.getElementById("statusbar-display");
+ statusTextFld.label = gNavigatorBundle.getString("droponhomebutton");
+ },
+
+ onDragExit: function (aEvent)
+ {
+ aEvent.stopPropagation();
+ document.getElementById("statusbar-display").label = "";
+ }
+};
+
+function openHomeDialog(aURL)
+{
+ var promptTitle = gNavigatorBundle.getString("droponhometitle");
+ var promptMsg = gNavigatorBundle.getString("droponhomemsg");
+ var okButton = gNavigatorBundle.getString("droponhomeokbutton");
+ if (Services.prompt.confirmEx(window, promptTitle, promptMsg,
+ (Services.prompt.BUTTON_TITLE_IS_STRING *
+ Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL *
+ Services.prompt.BUTTON_POS_1),
+ okButton, null, null, null,
+ {value: false}) == 0)
+ SetStringPref("browser.startup.homepage", aURL);
+}
+
+var goButtonObserver = {
+ onDragOver: DragLinkOver,
+
+ onDrop: function (aEvent)
+ {
+ var url = Services.droppedLinkHandler.dropLink(aEvent, {});
+
+ getShortcutOrURIAndPostData(url).then(data => {
+ if (data.url)
+ loadURI(data.url, null, data.postData, false);
+ });
+ }
+};
+
+var searchButtonObserver = {
+ onDragOver: DragLinkOver,
+
+ onDrop: function (aEvent)
+ {
+ var name = {};
+ var url = Services.droppedLinkHandler.dropLink(aEvent, name);
+ if (url)
+ BrowserSearch.loadSearch(name.value || url);
+ }
+};
diff --git a/comm/suite/browser/navigatorOverlay.xul b/comm/suite/browser/navigatorOverlay.xul
new file mode 100644
index 0000000000..45e256b123
--- /dev/null
+++ b/comm/suite/browser/navigatorOverlay.xul
@@ -0,0 +1,716 @@
+<?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/. -->
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/viewZoomOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/viewApplyThemeOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/charsetOverlay.xul"?>
+<?xul-overlay href="chrome://navigator/content/mailNavigatorOverlay.xul"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % navigatorDTD SYSTEM "chrome://navigator/locale/navigator.dtd" >
+%navigatorDTD;
+<!ENTITY % navigatorOverlayDTD SYSTEM "chrome://navigator/locale/navigatorOverlay.dtd">
+%navigatorOverlayDTD;
+<!ENTITY % contentAreaCommandsDTD SYSTEM "chrome://communicator/locale/contentAreaCommands.dtd" >
+%contentAreaCommandsDTD;
+]>
+
+<overlay id="navigatorOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- Navigator -->
+ <script src="chrome://navigator/content/navigator.js"/>
+
+ <!-- Places Bookmarks Utilities -->
+ <script src="chrome://navigator/content/browser-places.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_navigator"
+ src="chrome://navigator/locale/navigator.properties"/>
+ <stringbundle id="bundle_brand"
+ src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="bundle_navigator_region"
+ src="chrome://navigator-region/locale/region.properties"/>
+ <stringbundle id="bundle_viewZoom"/>
+ <stringbundle id="bundle_viewApplyTheme"/>
+ </stringbundleset>
+
+ <!-- Keysets -->
+ <keyset id="navKeys">
+ <!-- File Menu -->
+ <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTab"/>
+ <key id="key_newNavigator"/>
+ <key id="key_newPrivateWindow"/>
+ <key id="key_restoreTab" key="&recentTabs.commandkey;" modifiers="accel,shift" oncommand="gBrowser.undoCloseTab(0);"/>
+ <key id="key_restoreWindow" key="&recentWindows.commandkey;" modifiers="accel,shift" oncommand="undoCloseWindow();"/>
+ <key id="key_newBlankPage"/>
+ <key id="focusURLBar" key="&openCmd.commandkey;" oncommand="ShowAndSelectContentsOfURLBar();"
+ modifiers="accel"/>
+ <key id="openLocationKb" key="&openCmd.commandkey;" command="Browser:Open" modifiers="accel,shift"/>
+ <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/>
+ <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
+ <key id="key_editPage" key="&editPageCmd.commandkey;" command="Browser:EditPage" modifiers="accel"/>
+ <key id="key_print"/>
+ <key id="key_close"/>
+ <key id="key_closeWindow"/>
+
+ <!-- Edit Menu -->
+ <key id="key_undo"/>
+ <key id="key_redo"/>
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"/>
+ <key id="key_delete"/>
+ <key id="key_delete2"/>
+ <key id="key_selectAll"/>
+ <key id="key_switchTextDirection"/>
+
+ <!-- View Menu -->
+ <key id="key_reload" key="&reloadCmd.commandkey;" oncommand="BrowserReload();" modifiers="accel"/>
+ <key key="&reloadCmd.commandkey;" oncommand="BrowserReloadSkipCache();" modifiers="accel,shift"/>
+ <key id="key_viewSource" key="&pageSourceCmd.commandkey;" command="View:PageSource" modifiers="accel"/>
+ <key id="key_viewInfo" key="&pageInfoCmd.commandkey;" command="View:PageInfo" modifiers="accel"/>
+ <key id="key_viewNextSidebarPanel" keycode="VK_PAGE_DOWN" oncommand="SidebarGetRelativePanel(1);" modifiers="alt" />
+ <key id="key_viewPrevSidebarPanel" keycode="VK_PAGE_UP" oncommand="SidebarGetRelativePanel(-1);" modifiers="alt" />
+
+ <!-- Search Menu -->
+ <keyset id="findKeys"/>
+
+ <!-- Go Menu -->
+ <key keycode="VK_BACK" command="cmd_handleBackspace"/>
+ <key keycode="VK_BACK" command="cmd_handleShiftBackspace" modifiers="shift"/>
+
+ <!-- Bookmarks Menu -->
+ <key id="addBookmarkKb" key="&addCurPageAsCmd.commandkey;" command="Browser:AddBookmark" modifiers="accel,shift"/>
+ <key id="addBookmarkAsKb" key="&addCurPageAsCmd.commandkey;" command="Browser:AddBookmarkAs" modifiers="accel"/>
+ <key id="manBookmarkKb" key="&manBookmarksCmd.commandkey;" command="Browser:ManageBookmark" modifiers="accel"/>
+
+ <!-- Tools Menu -->
+ <key id="searchInternetKb" key="&searchInternet.commandKey;" modifiers="accel,shift" command="Browser:SearchInternet"/>
+
+ <!-- Misc -->
+ <!-- the amazing fishcam, suppress warning by ',' at the beginning of modifiers, see bug 496322 -->
+ <key key="f" modifiers=",control,alt" oncommand="loadURI('http://www.fishcam.com/');"/>
+ <key id="goUpKb" keycode="VK_UP" command="Browser:Up" modifiers="alt"/>
+ <key id="key_gotoHistory"
+#ifndef XP_MACOSX
+ key="&history.commandKey;"
+ modifiers="accel"
+#else
+ key="&historyCmd.key;"
+ modifiers="accel,shift"
+#endif
+ oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
+ <keyset id="viewZoomKeys"/>
+ <keyset id="navigationKeys">
+#ifndef XP_MACOSX
+ <key id="goBackKb" keycode="VK_LEFT"
+ command="Browser:Back" modifiers="alt"/>
+ <key id="goForwardKb" keycode="VK_RIGHT"
+ command="Browser:Forward" modifiers="alt"/>
+#else
+ <key id="goBackKb" keycode="VK_LEFT"
+ command="Browser:Back" modifiers="accel"/>
+ <key id="goForwardKb" keycode="VK_RIGHT"
+ command="Browser:Forward" modifiers="accel"/>
+#endif
+#ifndef XP_WIN
+ <key id="goBackKb2" key="&goBackCmd.commandKey;"
+ command="Browser:Back" modifiers="accel"/>
+ <key id="goForwardKb2" key="&goForwardCmd.commandKey;"
+ command="Browser:Forward" modifiers="accel"/>
+#endif
+ <key id="key_stop" keycode="VK_ESCAPE" oncommand="BrowserStop();"/>
+#ifdef XP_MACOSX
+ <key id="key_stop_mac" key="&stopCmd.macCommandKey;"
+ oncommand="BrowserStop();" modifiers="accel"/>
+#endif
+
+#ifndef XP_MACOSX
+ <key keycode="VK_F5" oncommand="BrowserReload();"/>
+ <key keycode="VK_F5"
+ oncommand="BrowserReloadSkipCache();" modifiers="control"/>
+ <key id="goHome" keycode="VK_HOME"
+ oncommand="BrowserHome();" modifiers="alt"/>
+#else
+ <key id="goHome" keycode="VK_HOME"
+ oncommand="BrowserHome();" modifiers="meta"/>
+#endif
+
+#ifdef XP_MACOSX
+ <key keycode="VK_F11" command="View:FullScreen"/>
+ <key id="key_fullScreen" key="&fullScreenCmd.commandKey;"
+ command="View:FullScreen" modifiers="accel,shift"/>
+#else
+ <key id="key_fullScreen" keycode="VK_F11" command="View:FullScreen"/>
+ <key id="key_newTabWithTargetBg" keycode="VK_INSERT"
+ command="cmd_newTabWithTarget"/>
+#endif
+ <key id="key_newTabWithTargetFg" keycode="VK_INSERT"
+ modifiers="alt" command="cmd_newTabWithTarget"/>
+ </keyset>
+ <keyset id="tasksKeys"/>
+ <key id="key_sanitize" keycode="VK_DELETE"
+ command="Tools:Sanitize" modifiers="accel,shift"/>
+#ifdef XP_MACOSX
+ <key id="key_sanitize_mac" keycode="VK_BACK"
+ command="Tools:Sanitize" modifiers="accel,shift"/>
+#endif
+ </keyset>
+
+ <commandset id="commands">
+ <command id="cmd_newNavigatorTab" oncommand="BrowserOpenTab();"/>
+ <command id="cmd_newNavigator"/>
+ <command id="cmd_newPrivateWindow"/>
+ <command id="cmd_newTabWithTarget" oncommand="contentAreaClick(event);"/>
+ <command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
+ <command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
+
+ <command id="cmd_newEditor"/>
+ <!-- NOT IMPLEMENTED
+ <command id="cmd_newEditorTemplate"/>
+ <command id="cmd_newEditorDraft"/> -->
+ <command id="Browser:OpenFile" oncommand="BrowserOpenFileWindow();"/>
+ <command id="Browser:SavePage" oncommand="saveDocument(window.content.document, true);"/>
+ <command id="Browser:EditPage" oncommand="editPageOrFrame();" observes="isImage"/>
+ <command id="Browser:UploadFile" oncommand="BrowserUploadFile();"/>
+ <command id="Browser:Open" oncommand="BrowserOpenWindow();"/>
+ <command id="cmd_printSetup" oncommand="PrintUtils.showPageSetup();"/>
+ <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
+ <command id="cmd_printpreview" oncommand="BrowserPrintPreview();"/>
+ <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
+ <command id="cmd_closeOtherTabs" oncommand="BrowserCloseOtherTabs()"/>
+ <command id="cmd_closeTabsToTheEnd"
+ oncommand="BrowserCloseTabsToTheEnd();"/>
+ <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
+
+ <!-- Edit Menu -->
+ <command id="cmd_undo"/>
+ <command id="cmd_redo"/>
+ <command id="cmd_cut"/>
+ <command id="cmd_copy"/>
+ <command id="cmd_paste"/>
+ <command id="cmd_delete"/>
+ <command id="cmd_selectAll" observes="isImage"/>
+ <command id="cmd_switchTextDirection"/>
+ <commandset id="globalEditMenuItems"/>
+ <commandset id="selectEditMenuItems"/>
+ <commandset id="undoEditMenuItems"/>
+ <commandset id="clipboardEditMenuItems"/>
+
+ <!-- Content area context menu -->
+ <command id="cmd_copyLink"/>
+ <command id="cmd_copyImage"/>
+
+ <!-- View Menu -->
+ <command id="View:PageSource" oncommand="BrowserViewSource(gBrowser.selectedBrowser);" observes="isImage"/>
+ <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
+ <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
+ <command id="cmd_SwitchDocumentDirection" oncommand="SwitchDocumentDirection(window.content);" />
+
+ <!-- Search Menu -->
+ <command id="cmd_find"
+ oncommand="BrowserFind();"
+ observes="isImage"/>
+ <command id="cmd_findNext"
+ oncommand="BrowserFindAgain(false);"
+ observes="isImage"/>
+ <command id="cmd_findPrev"
+ oncommand="BrowserFindAgain(true);"
+ observes="isImage"/>
+ <command id="cmd_findTypeText" observes="isImage"/>
+ <command id="cmd_findTypeLinks" observes="isImage"/>
+
+ <!-- Bookmarks Menu -->
+ <command id="Browser:AddBookmark"
+ label="&addCurPageCmd.label;" accesskey="&addCurPageCmd.accesskey;"
+ oncommand="PlacesCommandHook.bookmarkPage(gBrowser.selectedBrowser,
+ false);"/>
+ <command id="Browser:AddBookmarkAs"
+ label="&addCurPageAsCmd.label;" accesskey="&addCurPageAsCmd.accesskey;"
+ oncommand="PlacesCommandHook.bookmarkPage(gBrowser.selectedBrowser,
+ true);"/>
+ <!-- The command is disabled for the hidden window. Otherwise its enabled
+ state is handled by BookmarksEventHandler.onPopupShowing. -->
+ <command id="Browser:BookmarkAllTabs"
+ label="&addCurTabsAsCmd.label;" accesskey="&addCurTabsAsCmd.accesskey;"
+ oncommand="PlacesCommandHook.bookmarkCurrentPages();"
+ disabled="true"/>
+ <command id="Browser:ManageBookmark"
+ label="&manBookmarksCmd.label;" accesskey="&manBookmarksCmd.accesskey;"
+ oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
+ <command id="feedsMenu" disabled="true"/>
+ <commandset id="placesCommands"/>
+
+ <!-- Go Menu -->
+ <command id="Browser:Home" oncommand="BrowserHome(event);"/>
+ <command id="Browser:Back" oncommand="BrowserBack();" observes="canGoBack"/>
+ <command id="Browser:Forward" oncommand="BrowserForward();" observes="canGoForward"/>
+ <command id="Browser:Up" oncommand="BrowserUp();" observes="canGoUp"/>
+ <commandset id="viewZoomCommands"/>
+ <commandset id="tasksCommands"/>
+
+ <!-- Tools Menu -->
+ <command id="Browser:SearchInternet" oncommand="BrowserSearch.webSearch();"/>
+ <command id="Tools:Sanitize"
+ oncommand="Cc['@mozilla.org/suite/suiteglue;1'].getService(Ci.nsISuiteGlue).sanitize(window);"/>
+
+ </commandset>
+
+ <broadcasterset id="navBroadcasters">
+ <broadcaster id="canGoBack" disabled="true"/>
+ <broadcaster id="canGoForward" disabled="true"/>
+ <broadcaster id="canGoUp" disabled="true"/>
+ <broadcaster id="Communicator:WorkMode"/>
+ <broadcaster id="cmd_viewtaskbar"
+ checked="true"
+ oncommand="goToggleToolbar('status-bar', 'cmd_viewtaskbar');
+ updateWindowState();"/>
+ <broadcaster id="cmd_viewcomponentbar" oncommand="goToggleToolbar('component-bar', 'cmd_viewcomponentbar');" checked="true"/>
+ <broadcaster id="isImage"/>
+ </broadcasterset>
+
+ <!-- Menu -->
+ <menubar id="main-menubar" class="chromeclass-menubar">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup" onpopupshowing="updateCloseItems();getContentAreaFrameCount();updateSavePageItems();updateFileUploadItem();">
+ <menu id="menu_New">
+ <menupopup id="menu_NewPopup">
+ <!-- From utilityOverlay.xul -->
+ <menuitem id="menu_newNavigatorTab" command="cmd_newNavigatorTab" key="key_newNavigatorTab"
+ label="&tabCmd.label;" accesskey="&tabCmd.accesskey;"/>
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ <menuseparator id="navBeginGlobalNewItems"/>
+ <menuitem id="menu_newEditor" command="cmd_newEditor"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_openLocation"
+ label="&openCmd.label;"
+ accesskey="&openCmd.accesskey;"
+ key="openLocationKb"
+ command="Browser:Open"/>
+ <menuitem id="menu_openFile"
+ label="&openFileCmd.label;"
+ accesskey="&openFileCmd.accesskey;"
+ key="openFileKb"
+ command="Browser:OpenFile"/>
+ <menuitem id="menu_close"/>
+ <menuitem id="menu_closeOtherTabs" command="cmd_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"/>
+ <menuitem id="menu_closeTabsToTheEnd"
+ label="&closeTabsToTheEnd.label;"
+ accesskey="&closeTabsToTheEnd.accesskey;"
+ command="cmd_closeTabsToTheEnd"/>
+ <menuitem id="menu_closeWindow" hidden="true" command="cmd_closeWindow" key="key_closeWindow" label="&closeWindow.label;" accesskey="&closeWindow.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="savepage" valueSaveAs="&savePageAsCmd.label;" valueSave="&savePageCmd.label;"
+ accesskey="&savePageCmd.accesskey;" key="key_savePage" command="Browser:SavePage"/>
+ <menuitem id="saveframe" valueSaveAs="&saveFrameAsCmd.label;" valueSave="&saveFrameCmd.label;"
+ accesskey="&saveFrameCmd.accesskey;" oncommand="saveFrameDocument();" hidden="true"/>
+ <menuseparator id="saveMenuBlockEnd"/>
+ <menuitem label="&editPageCmd.label;" accesskey="&editPageCmd.accesskey;" key="key_editPage" command="Browser:EditPage" />
+ <menuseparator/>
+ <menuitem command="Browser:UploadFile" label="&uploadFile.label;" accesskey="&uploadFile.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="menu_printSetup"/>
+ <menuitem id="menu_printPreview"/>
+ <menuitem id="menu_print"/>
+ <menuseparator/>
+ <menuitem id="offlineGoOfflineCmd"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_Edit">
+ <menupopup id="menu_EditPopup">
+ <menuitem id="menu_undo"/>
+ <menuitem id="menu_redo"/>
+ <menuseparator/>
+ <menuitem id="menu_cut"/>
+ <menuitem id="menu_copy"/>
+ <menuitem id="menu_paste"/>
+ <menuitem id="menu_delete"/>
+ <menuseparator/>
+ <menuitem id="menu_selectAll"/>
+ <menuseparator/>
+ <menuitem id="menu_find" label="&findOnCmd.label;"/>
+ <menuitem id="menu_findNext"/>
+ <menuitem id="menu_findPrev"/>
+ <menuseparator/>
+ <menuitem id="menu_findTypeLinks"/>
+ <menuitem id="menu_findTypeText"/>
+
+ <menuseparator id="textfieldDirection-separator" hidden="true"/>
+ <menuitem id="textfieldDirection-swap" hidden="true"/>
+
+ <menuseparator id="menu_PrefsSeparator"/>
+ <menuitem id="menu_preferences" oncommand="goPreferences('navigator_pane')"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_View">
+ <menupopup id="menu_View_Popup" onpopupshowing="EnableCharsetMenu();">
+ <menu label="&toolbarsCmd.label;" accesskey="&toolbarsCmd.accesskey;" id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup"
+ onpopupshowing="updateToolbarStates(event);"
+ oncommand="onViewToolbarCommand(event);">
+ <menuitem id="menuitem_showhide_tabbar"
+ label="&tabbarCmd.label;"
+ accesskey="&tabbarCmd.accesskey;"
+ class="menuitem-iconic"
+ type="checkbox"
+ oncommand="showHideTabbar();"
+ checked="true"/>
+ <menuitem id="menuitem_taskbarCmd"
+ label="&taskbarCmd.label;"
+ accesskey="&taskbarCmd.accesskey;"
+ class="menuitem-iconic"
+ type="checkbox"
+ observes="cmd_viewtaskbar"/>
+ <menuitem id="menuitem_componentbarCmd"
+ label="&componentbarCmd.label;"
+ accesskey="&componentbarCmd.accesskey;"
+ class="menuitem-iconic"
+ type="checkbox"
+ observes="cmd_viewcomponentbar"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menuitem_fullScreen"
+ label="&fullScreenCmd.label;"
+ accesskey="&fullScreenCmd.accesskey;"
+ key="key_fullScreen"
+ command="View:FullScreen"/>
+ <menuseparator />
+ <menuitem id="menuitem-stop"
+ label="&stopCmd.label;"
+ accesskey="&stopCmd.accesskey;"
+ disabled="true"
+ oncommand="BrowserStop();"
+ key="key_stop"/>
+ <menuitem id="menuitem_reload"
+ label="&reloadCmd.label;"
+ accesskey="&reloadCmd.accesskey;"
+ key="key_reload"
+ oncommand="BrowserReload(event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuseparator />
+
+ <!-- overlayed from viewZoomOverlay.xul -->
+ <menu id="menu_zoom"/>
+
+ <menu id="menu_UseStyleSheet"
+ label="&useStyleSheetMenu.label;"
+ accesskey="&useStyleSheetMenu.accesskey;"
+ disabled="false"
+ observes="isImage">
+ <menupopup id="menupopup_stylesheetFill" onpopupshowing="stylesheetFillPopup(this);"
+ oncommand="stylesheetSwitchAll(window.content, event.target.getAttribute('data')); setStyleDisabled(false);" type="radio">
+ <menuitem id="menu_pageStyleNoStyle"
+ label="&useStyleSheetNone.label;"
+ accesskey="&useStyleSheetNone.accesskey;"
+ oncommand="setStyleDisabled(true); event.stopPropagation();"
+ type="radio"/>
+ <menuitem id="menu_pageStylePersistentOnly"
+ label="&useStyleSheetPersistentOnly.label;"
+ accesskey="&useStyleSheetPersistentOnly.accesskey;"
+ type="radio"/>
+ </menupopup>
+ </menu>
+ <menu id="charsetMenu"
+ onpopupshowing="BrowserUpdateCharsetMenu(this);"
+ oncommand="BrowserSetCharacterSet(event);"/>
+ <menuitem hidden="true" id="documentDirection-swap"
+ label="&bidiSwitchPageDirectionItem.label;"
+ accesskey="&bidiSwitchPageDirectionItem.accesskey;"
+ command="cmd_SwitchDocumentDirection"/>
+ <menuseparator />
+ <menuitem id="menuitem_pageSourceCmd"
+ label="&pageSourceCmd.label;"
+ accesskey="&pageSourceCmd.accesskey;"
+ key="key_viewSource"
+ command="View:PageSource"/>
+ <menuitem id="menuitem_pageInfoCmd"
+ label="&pageInfoCmd.label;"
+ accesskey="&pageInfoCmd.accesskey;"
+ key="key_viewInfo"
+ command="View:PageInfo"/>
+ <menuseparator />
+ <!-- overlayed from viewApplyThemeOverlay.xul -->
+ <menu id="menu_viewApplyTheme"/>
+ </menupopup>
+ </menu>
+
+ <menu id="history-menu"
+ label="&goMenu.label;"
+ accesskey="&goMenu.accesskey;"
+ oncommand="gotoHistoryIndex(event);"
+ onclick="checkForMiddleClick(this, event);">
+ <menupopup id="goPopup"
+ onpopupshowing="updateGoMenu(event);">
+ <menuitem id="historyMenuBack"
+ label="&goBackCmd.label;"
+ accesskey="&goBackCmd.accesskey;"
+ key="goBackKb"
+ oncommand="BrowserBack(event);"
+ onclick="checkForMiddleClick(this, event);"
+ observes="canGoBack"/>
+ <menuitem id="historyMenuForward"
+ label="&goForwardCmd.label;"
+ accesskey="&goForwardCmd.accesskey;"
+ key="goForwardKb"
+ oncommand="BrowserForward(event);"
+ onclick="checkForMiddleClick(this, event);"
+ observes="canGoForward"/>
+ <menuitem id="historyMenuUp"
+ label="&goUpCmd.label;"
+ accesskey="&goUpCmd.accesskey;"
+ key="goUpKb"
+ command="Browser:Up"/>
+ <menuitem id="historyMenuHome"
+ label="&goHomeCmd.label;"
+ accesskey="&goHomeCmd.accesskey;"
+ command="Browser:Home"
+ onclick="checkForMiddleClick(this, event);"
+ key="goHome"/>
+ <menuseparator/>
+ <menu id="menu_recentTabs"
+ label="&recentTabs.label;"
+ accesskey="&recentTabs.accesskey;">
+ <menupopup id="menu_recentTabsPopup"
+ onpopupshowing="event.stopPropagation(); updateRecentTabs(this);"
+ oncommand="gBrowser.undoCloseTab(event.target.value);"/>
+ </menu>
+ <menu id="menu_recentWindows"
+ label="&recentWindows.label;"
+ accesskey="&recentWindows.accesskey;">
+ <menupopup id="menu_recentWindowsPopup"
+ onpopupshowing="event.stopPropagation(); updateRecentWindows(this);"
+ oncommand="undoCloseWindow(event.target.value);"/>
+ </menu>
+ <menuitem id="historyRestoreLastSession"
+ label="&historyRestoreLastSession.label;"
+ accesskey="&historyRestoreLastSession.accesskey;"
+ oncommand="restoreLastSession();"
+ disabled="true"/>
+ <menuseparator/>
+ <menuitem id="menu_showAllHistory"
+ label="&historyCmd.label;"
+ accesskey="&historyCmd.accesskey;"
+ oncommand="PlacesCommandHook.showPlacesOrganizer('History');"
+ key="key_gotoHistory"/>
+ <menuseparator id="startHistorySeparator" hidden="true"/>
+ <menuseparator id="endHistorySeparator" hidden="true"/>
+ <!-- Dead for now.
+ <menuitem id="sync-tabs-menuitem"
+ label="&syncTabsMenu.label;"
+ oncommand="BrowserOpenSyncTabs();"
+ disabled="true"/> -->
+ </menupopup>
+ </menu>
+
+ <menu id="bookmarksMenu"
+ label="&bookmarksMenu.label;"
+ accesskey="&bookmarksMenu.accesskey;"
+ ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
+ ondragover="PlacesMenuDNDHandler.onDragOver(event);"
+ ondrop="PlacesMenuDNDHandler.onDrop(event);">
+ <menupopup id="bookmarksMenuPopup"
+ placespopup="true"
+ context="placesContext"
+ openInTabs="children"
+ oncommand="BookmarksEventHandler.onCommand(event);"
+ onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
+ onpopupshowing="BookmarksMenu.onPopupShowing(event, '');
+ BookmarksEventHandler.onPopupShowing(event);"
+ tooltip="bhTooltip" popupsinherittooltip="true">
+ <menuitem id="menu_bookmarkThisPage"
+ command="Browser:AddBookmark"
+ key="addBookmarkKb"/>
+ <menuitem id="menu_bookmarkThisPageAs"
+ command="Browser:AddBookmarkAs"
+ key="addBookmarkAsKb"/>
+ <menuitem id="menu_bookmarkAllTabs"
+ command="Browser:BookmarkAllTabs"/>
+ <menuitem id="menu_bookmarkManager"
+ command="Browser:ManageBookmark"
+ key="manBookmarkKb"/>
+ <menuseparator id="organizeBookmarksSeparator"/>
+ <menu id="menu_iconic_feedsMenu"
+ label="&feedsMenu.label;"
+ accesskey="&feedsMenu.accesskey;"
+ class="menu-iconic feedsMenu"
+ command="feedsMenu">
+ <menupopup onpopupshowing="window.XULBrowserWindow.populateFeeds(this);"
+ oncommand="subscribeToFeed(event.target.statusText, event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ </menu>
+ <menuseparator/>
+ <menu id="bookmarksToolbarFolderMenu"
+ class="menu-iconic bookmark-item"
+ label="&bookmarksToolbarCmd.label;"
+ container="true">
+ <menupopup id="bookmarksToolbarFolderPopup"
+ placespopup="true"
+ context="placesContext"
+ onpopupshowing="if (!this.parentNode._placesView)
+ new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+ </menu>
+ <menu id="unsortedBookmarksFolderMenu"
+ class="menu-iconic bookmark-item"
+ container="true">
+ <menupopup id="unsortedBookmarksFolderPopup"
+ placespopup="true"
+ context="placesContext"
+ onpopupshowing="if (!this.parentNode._placesView)
+ new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
+ </menu>
+ <menuseparator/>
+ </menupopup>
+ </menu>
+
+ <menu id="tasksMenu">
+ <menupopup id="taskPopup">
+ <menuitem id="menu_searchWeb"
+ label="&searchInternetCmd.label;"
+ accesskey="&searchInternetCmd.accesskey;"
+ key="searchInternetKb"
+ command="Browser:SearchInternet"/>
+ <menuitem id="menu_translate"
+ label="&translateMenu.label;"
+ accesskey="&translateMenu.accesskey;"
+ oncommand="Translate();"/>
+ <menu id="menu_cookieManager"
+ label="&cookieCookieManager.label;"
+ accesskey="&cookieCookieManager.accesskey;"
+ oncommand="if (event.target.id.startsWith('cookie_'))
+ CookieImagePopupAction(event.target);">
+ <menupopup id="menupopup_checkPermissions_cookie"
+ onpopupshowing="CheckPermissionsMenu('cookie', this);">
+ <menuitem id="cookie_deny"
+ label="&cookieBlockCookiesCmd.label;"
+ accesskey="&cookieBlockCookiesCmd.accesskey;"
+ title="&cookieMessageTitle.label;"
+ msg="&cookieBlockCookiesMsg.label;"
+ type="radio"
+ name="cookie"/>
+ <menuitem id="cookie_default"
+ label="&cookieCookiesDefaultCmd.label;"
+ accesskey="&cookieCookiesDefaultCmd.accesskey;"
+ title="&cookieMessageTitle.label;"
+ msg="&cookieCookiesDefaultMsg.label;"
+ type="radio"
+ name="cookie"/>
+ <menuitem id="cookie_session"
+ label="&cookieAllowSessionCookiesCmd.label;"
+ accesskey="&cookieAllowSessionCookiesCmd.accesskey;"
+ title="&cookieMessageTitle.label;"
+ msg="&cookieAllowSessionCookiesMsg.label;"
+ type="radio"
+ name="cookie"/>
+ <menuitem id="cookie_allow"
+ label="&cookieAllowCookiesCmd.label;"
+ accesskey="&cookieAllowCookiesCmd.accesskey;"
+ title="&cookieMessageTitle.label;"
+ msg="&cookieAllowCookiesMsg.label;"
+ type="radio"
+ name="cookie"/>
+ <menuseparator/>
+ <menuitem id="menuitem_cookieDisplay"
+ label="&cookieDisplayCookiesCmd.label;"
+ accesskey="&cookieDisplayCookiesCmd.accesskey;"
+ oncommand="toDataManager(hostUrl() + '|cookies');
+ event.stopPropagation();"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_imageManager"
+ label="&cookieImageManager.label;"
+ accesskey="&cookieImageManager.accesskey;"
+ oncommand="if (event.target.id.startsWith('image_'))
+ CookieImagePopupAction(event.target);">
+ <menupopup id="menupopup_checkPermissions_image"
+ onpopupshowing="CheckPermissionsMenu('image', this);">
+ <menuitem id="image_deny"
+ label="&cookieBlockImagesCmd.label;"
+ accesskey="&cookieBlockImagesCmd.accesskey;"
+ title="&cookieImageMessageTitle.label;"
+ msg="&cookieBlockImagesMsg.label;"
+ type="radio"
+ name="image"/>
+ <menuitem id="image_default"
+ label="&cookieImagesDefaultCmd.label;"
+ accesskey="&cookieImagesDefaultCmd.accesskey;"
+ title="&cookieImageMessageTitle.label;"
+ msg="&cookieImagesDefaultMsg.label;"
+ type="radio"
+ name="image"/>
+ <menuitem id="image_allow"
+ label="&cookieAllowImagesCmd.label;"
+ accesskey="&cookieAllowImagesCmd.accesskey;"
+ title="&cookieImageMessageTitle.label;"
+ msg="&cookieAllowImagesMsg.label;"
+ type="radio"
+ name="image"/>
+ <menuseparator/>
+ <menuitem id="menuitem_imageDisplay"
+ label="&cookieDisplayImagesCmd.label;"
+ accesskey="&cookieDisplayImagesCmd.accesskey;"
+ oncommand="toDataManager(hostUrl() + '|permissions|add|image');
+ event.stopPropagation();"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_popupManager"
+ label="&popupsManager.label;"
+ accesskey="&popupsManager.accesskey;"
+ oncommand="if (event.target.id.startsWith('popup_'))
+ CookieImagePopupAction(event.target);">
+ <menupopup id="menupopup_checkForVisibility"
+ onpopupshowing="CheckPermissionsMenu('popup', this);"
+ onpopuphiding="RemovePopupsItems(this);">
+ <menuitem id="popup_deny"
+ label="&popupBlockCmd.label;"
+ accesskey="&popupBlockCmd.accesskey;"
+ title="&popupsMessageChangeTitle.label;"
+ msg="&popupBlockMsg.label;"
+ type="radio"
+ name="popup"/>
+ <menuitem id="popup_default"
+ label="&popupDefaultCmd.label;"
+ accesskey="&popupDefaultCmd.accesskey;"
+ title="&popupsMessageChangeTitle.label;"
+ msg="&popupDefaultMsg.label;"
+ type="radio"
+ name="popup"/>
+ <menuitem id="popup_allow"
+ label="&popupAllowCmd.label;"
+ accesskey="&popupAllowCmd.accesskey;"
+ title="&popupsMessageChangeTitle.label;"
+ msg="&popupAllowMsg.label;"
+ type="radio"
+ name="popup"/>
+ <menuseparator id="popupMenuSeparator"/>
+ <menuitem id="menuitem_PopupsManage"
+ label="&popupsManage.label;"
+ accesskey="&popupsManage.accesskey;"
+ oncommand="toDataManager(hostUrl() + '|permissions|add|popup');
+ event.stopPropagation();"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="navBeginGlobalItems"/>
+ <menuitem id="sanitizeItem"
+ label="&clearPrivateDataCmd.label;"
+ accesskey="&clearPrivateDataCmd.accesskey;"
+ key="key_sanitize" command="Tools:Sanitize"/>
+ </menupopup>
+ </menu>
+
+ <menu id="windowMenu"/>
+
+ <menu id="menu_Help"/>
+ </menubar>
+
+</overlay>
diff --git a/comm/suite/browser/nsBrowserContentHandler.js b/comm/suite/browser/nsBrowserContentHandler.js
new file mode 100644
index 0000000000..5e6c37380f
--- /dev/null
+++ b/comm/suite/browser/nsBrowserContentHandler.js
@@ -0,0 +1,634 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const nsISupports = Ci.nsISupports;
+const nsIBrowserDOMWindow = Ci.nsIBrowserDOMWindow;
+const nsIBrowserHistory = Ci.nsIBrowserHistory;
+const nsIBrowserSearchService = Ci.nsIBrowserSearchService;
+const nsIChannel = Ci.nsIChannel;
+const nsICommandLine = Ci.nsICommandLine;
+const nsICommandLineHandler = Ci.nsICommandLineHandler;
+const nsICommandLineValidator = Ci.nsICommandLineValidator;
+const nsIComponentRegistrar = Ci.nsIComponentRegistrar;
+const nsIContentHandler = Ci.nsIContentHandler;
+const nsIDOMWindow = Ci.nsIDOMWindow;
+const nsIFactory = Ci.nsIFactory;
+const nsIFileURL = Ci.nsIFileURL;
+const nsIHttpProtocolHandler = Ci.nsIHttpProtocolHandler;
+const nsINetUtil = Ci.nsINetUtil;
+const nsIPrefService = Ci.nsIPrefService;
+const nsIPrefBranch = Ci.nsIPrefBranch;
+const nsIPrefLocalizedString = Ci.nsIPrefLocalizedString;
+const nsISupportsString = Ci.nsISupportsString;
+const nsIWindowMediator = Ci.nsIWindowMediator;
+const nsIWebNavigationInfo = Ci.nsIWebNavigationInfo;
+
+const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
+
+const URI_INHERITS_SECURITY_CONTEXT = nsIHttpProtocolHandler
+ .URI_INHERITS_SECURITY_CONTEXT;
+
+const NS_GENERAL_STARTUP_PREFIX = "@mozilla.org/commandlinehandler/general-startup;1?type=";
+
+function shouldLoadURI(aURI)
+{
+ if (aURI && !aURI.schemeIs("chrome"))
+ return true;
+
+ dump("*** Preventing external load of chrome: URI into browser window\n");
+ dump(" Use -chrome <uri> instead\n");
+ return false;
+}
+
+function resolveURIInternal(aCmdLine, aArgument)
+{
+ try {
+ var file = aCmdLine.resolveFile(aArgument);
+ if (file.exists()) {
+ return Services.io.newFileURI(file);
+ }
+ } catch (e) {
+ }
+
+ // We have interpreted the argument as a relative file URI, but the file
+ // doesn't exist. Try URI fixup heuristics: see bug 290782.
+
+ try {
+ return Services.uriFixup
+ .createFixupURI(aArgument,
+ Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+
+ return null;
+}
+
+function getHomePageGroup()
+{
+ var homePage = Services.prefs.getComplexValue("browser.startup.homepage",
+ nsIPrefLocalizedString).data;
+
+ var count = 0;
+ try {
+ count = Services.prefs.getIntPref("browser.startup.homepage.count");
+ } catch (e) {
+ }
+
+ for (var i = 1; i < count; ++i) {
+ try {
+ homePage += '\n' + Services.prefs.getStringPref("browser.startup.homepage." + i);
+ } catch (e) {
+ }
+ }
+ return homePage;
+}
+
+function needHomePageOverride()
+{
+ var savedmstone = null;
+ try {
+ savedmstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
+ if (savedmstone == "ignore")
+ return false;
+ } catch (e) {
+ }
+
+ var mstone = Cc["@mozilla.org/network/protocol;1?name=http"]
+ .getService(nsIHttpProtocolHandler).misc;
+
+ if (mstone == savedmstone)
+ return false;
+
+ Services.prefs.setCharPref("browser.startup.homepage_override.mstone", mstone);
+
+ return true;
+}
+
+function getURLToLoad()
+{
+ if (needHomePageOverride()) {
+ try {
+ return Services.urlFormatter.formatURLPref("startup.homepage_override_url");
+ } catch (e) {
+ }
+ }
+
+ try {
+ var ss = Cc["@mozilla.org/suite/sessionstartup;1"]
+ .getService(Ci.nsISessionStartup);
+ // return about:blank if we are restoring previous session
+ if (ss.doRestore())
+ return "about:blank";
+ } catch (e) {
+ }
+
+ try {
+ var st = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+ // return about:blank if the last window was closed and should be restored
+ if (st.doRestoreLastWindow())
+ return "about:blank";
+ } catch (e) {
+ }
+
+ try {
+ switch (Services.prefs.getIntPref("browser.startup.page")) {
+ case 1:
+ return getHomePageGroup();
+
+ case 2:
+ return Services.prefs.getStringPref("browser.history.last_page_visited");
+ }
+ } catch (e) {
+ }
+
+ return "about:blank";
+}
+
+function openWindow(parent, url, features, arg)
+{
+ var argstring = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(nsISupportsString);
+ argstring.data = arg;
+ return Services.ww.openWindow(parent, url, "", features, argstring);
+}
+
+function openPreferences()
+{
+ var win = Services.wm.getMostRecentWindow("mozilla:preferences");
+ if (win)
+ win.focus();
+ else
+ openWindow(null, "chrome://communicator/content/pref/preferences.xul",
+ "chrome,titlebar,dialog=no,resizable", "");
+}
+
+function getBrowserURL()
+{
+ try {
+ return Services.prefs.getCharPref("browser.chromeURL");
+ } catch (e) {
+ }
+ return "chrome://navigator/content/navigator.xul";
+}
+
+function handURIToExistingBrowser(aUri, aLocation, aFeatures, aTriggeringPrincipal)
+{
+ if (!shouldLoadURI(aUri))
+ return;
+
+ var navWin = Services.wm.getMostRecentWindow("navigator:browser");
+ if (!navWin) {
+ // if we couldn't load it in an existing window, open a new one
+ openWindow(null, getBrowserURL(), aFeatures, aUri.spec);
+ return;
+ }
+
+ navWin.browserDOMWindow.openURI(aUri, null, aLocation,
+ nsIBrowserDOMWindow.OPEN_EXTERNAL,
+ aTriggeringPrincipal);
+}
+
+function doSearch(aSearchTerm, aFeatures) {
+ var submission = Services.search.defaultEngine.getSubmission(aSearchTerm);
+
+ // fill our nsIMutableArray with uri-as-wstring, null, null, postData
+ var sa = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+
+ var uristring = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(nsISupportsString);
+ uristring.data = submission.uri.spec;
+
+ sa.appendElement(uristring);
+ sa.appendElement(null);
+ sa.appendElement(null);
+ sa.appendElement(submission.postData);
+
+ // XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
+ // preferences, but need nsIBrowserDOMWindow extensions
+ return Services.ww.openWindow(null, getBrowserURL(), "_blank", aFeatures,
+ sa);
+}
+
+var nsBrowserContentHandler = {
+ get wrappedJSObject() {
+ return this;
+ },
+
+ /* nsISupports */
+ QueryInterface: function QueryInterface(iid) {
+ if (iid.equals(nsISupports) ||
+ iid.equals(nsICommandLineHandler) ||
+ iid.equals(nsICommandLine) ||
+ iid.equals(nsICommandLineValidator) ||
+ iid.equals(nsIContentHandler) ||
+ iid.equals(nsIFactory))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ _handledURI: null,
+
+ /* nsICommandLineHandler */
+ handle: function handle(cmdLine) {
+ var features = "chrome,all,dialog=no";
+ try {
+ var width = cmdLine.handleFlagWithParam("width", false);
+ if (width != null)
+ features += ",width=" + width;
+ } catch (e) {
+ }
+ try {
+ var height = cmdLine.handleFlagWithParam("height", false);
+ if (height != null)
+ features += ",height=" + height;
+ } catch (e) {
+ }
+
+ try {
+ var remote = cmdLine.handleFlagWithParam("remote", true);
+ if (/^\s*(\w+)\s*\(\s*([^\s,]+)\s*,?\s*([^\s]*)\s*\)\s*$/.test(remote)) {
+ switch (RegExp.$1.toLowerCase()) {
+ case "openurl":
+ case "openfile":
+ // openURL(<url>)
+ // openURL(<url>,new-window)
+ // openURL(<url>,new-tab)
+
+ var uri = resolveURIInternal(cmdLine, RegExp.$2);
+
+ var location = nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW;
+ if (RegExp.$3 == "new-window")
+ location = nsIBrowserDOMWindow.OPEN_NEWWINDOW;
+ else if (RegExp.$3 == "new-tab")
+ location = nsIBrowserDOMWindow.OPEN_NEWTAB;
+
+ handURIToExistingBrowser(uri, location, features,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ break;
+
+ case "mailto":
+ openWindow(null, "chrome://messenger/content/messengercompose/messengercompose.xul", features, RegExp.$2);
+ break;
+
+ case "xfedocommand":
+ switch (RegExp.$2.toLowerCase()) {
+ case "openbrowser":
+ openWindow(null, getBrowserURL(), features, RegExp.$3 || getURLToLoad());
+ break;
+
+ case "openinbox":
+ openWindow(null, "chrome://messenger/content", features);
+ break;
+
+ case "composemessage":
+ openWindow(null, "chrome://messenger/content/messengercompose/messengercompose.xul", features, RegExp.$3);
+ break;
+
+ default:
+ throw Cr.NS_ERROR_ABORT;
+ }
+ break;
+
+ default:
+ // Somebody sent us a remote command we don't know how to process:
+ // just abort.
+ throw Cr.NS_ERROR_ABORT;
+ }
+
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ // If we had a -remote flag but failed to process it, throw
+ // NS_ERROR_ABORT so that the xremote code knows to return a failure
+ // back to the handling code.
+ throw Cr.NS_ERROR_ABORT;
+ }
+
+ try {
+ var browserParam = cmdLine.handleFlagWithParam("browser", false);
+ if (browserParam) {
+ openWindow(null, getBrowserURL(), features, browserParam);
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ if (cmdLine.handleFlag("browser", false)) {
+ openWindow(null, getBrowserURL(), features, getURLToLoad());
+ cmdLine.preventDefault = true;
+ }
+ }
+
+ try {
+ var privateParam = cmdLine.handleFlagWithParam("private", false);
+ if (privateParam) {
+ openWindow(null, getBrowserURL(), "private," + features, privateParam);
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ if (cmdLine.handleFlag("private", false)) {
+ openWindow(null, getBrowserURL(), "private," + features, "about:privatebrowsing");
+ cmdLine.preventDefault = true;
+ }
+ }
+
+ // If we don't have a profile selected yet (e.g. the Profile Manager is
+ // displayed) we will crash if we open an url and then select a profile. To
+ // prevent this handle all url command line flag and set the command line's
+ // preventDefault to true to prevent the display of the ui. The initial
+ // command line will be retained when nsAppRunner calls LaunchChild though
+ // urls launched after the initial launch will be lost.
+ try {
+ // This will throw when a profile has not been selected.
+ Services.dirsvc.get("ProfD", Ci.nsIFile);
+ } catch (e) {
+ cmdLine.preventDefault = true;
+ throw Cr.NS_ERROR_ABORT;
+ }
+
+ try {
+ var urlParam = cmdLine.handleFlagWithParam("url", false);
+ if (urlParam) {
+ if (this._handledURI == urlParam) {
+ this._handledURI = null;
+ } else {
+ if (cmdLine.handleFlag("requestpending", false) &&
+ cmdLine.state == nsICommandLine.STATE_INITIAL_LAUNCH) {
+ // A DDE request with the URL will follow and the DDE handling code
+ // will send it to the commandline handler via
+ // "mozilla -url http://www.foo.com". Store the URL so we can
+ // ignore this request later
+ this._handledURI = urlParam;
+ }
+
+ urlParam = resolveURIInternal(cmdLine, urlParam);
+ handURIToExistingBrowser(urlParam,
+ nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
+ features,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ }
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ }
+
+ var param;
+ try {
+ while ((param = cmdLine.handleFlagWithParam("new-window", false)) != null) {
+ var uri = resolveURIInternal(cmdLine, param);
+ handURIToExistingBrowser(uri,
+ nsIBrowserDOMWindow.OPEN_NEWWINDOW,
+ features,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ }
+
+ try {
+ while ((param = cmdLine.handleFlagWithParam("new-tab", false)) != null) {
+ var uri = resolveURIInternal(cmdLine, param);
+ handURIToExistingBrowser(uri,
+ nsIBrowserDOMWindow.OPEN_NEWTAB,
+ features,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ }
+
+ try {
+ var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
+ if (chromeParam) {
+ // only load URIs which do not inherit chrome privs
+ var uri = resolveURIInternal(cmdLine, chromeParam);
+ if (!Services.netUtils.URIChainHasFlags(uri, URI_INHERITS_SECURITY_CONTEXT)) {
+ openWindow(null, uri.spec, features);
+ cmdLine.preventDefault = true;
+ }
+ }
+ } catch (e) {
+ }
+
+ try {
+ var fileParam = cmdLine.handleFlagWithParam("file", false);
+ if (fileParam) {
+ fileParam = resolveURIInternal(cmdLine, fileParam);
+ handURIToExistingBrowser(fileParam,
+ nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
+ features,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ cmdLine.preventDefault = true;
+ }
+ } catch (e) {
+ }
+
+ var searchParam = cmdLine.handleFlagWithParam("search", false);
+ if (searchParam) {
+ doSearch(searchParam, features);
+ cmdLine.preventDefault = true;
+ }
+
+ if (cmdLine.handleFlag("preferences", false)) {
+ openPreferences();
+ cmdLine.preventDefault = true;
+ }
+
+ if (cmdLine.handleFlag("silent", false))
+ cmdLine.preventDefault = true;
+
+ if (!cmdLine.preventDefault && cmdLine.length) {
+ var arg = cmdLine.getArgument(0);
+ if (!/^-/.test(arg)) {
+ try {
+ arg = resolveURIInternal(cmdLine, arg);
+ handURIToExistingBrowser(arg,
+ nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
+ features,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ cmdLine.preventDefault = true;
+ } catch (e) {
+ }
+ }
+ }
+
+ if (!cmdLine.preventDefault) {
+ this.realCmdLine = cmdLine;
+
+ var prefBranch = Services.prefs.getBranch("general.startup.");
+
+ var startupArray = prefBranch.getChildList("");
+
+ for (var i = 0; i < startupArray.length; ++i) {
+ this.currentArgument = startupArray[i];
+ var contract = NS_GENERAL_STARTUP_PREFIX + this.currentArgument;
+ if (contract in Cc) {
+ // Ignore any exceptions - we can't do anything about them here.
+ try {
+ if (prefBranch.getBoolPref(this.currentArgument)) {
+ var handler = Cc[contract].getService(nsICommandLineHandler);
+ if (handler.wrappedJSObject)
+ handler.wrappedJSObject.handle(this);
+ else
+ handler.handle(this);
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+
+ this.realCmdLine = null;
+ }
+
+ if (!cmdLine.preventDefault) {
+ var homePage = getURLToLoad();
+ if (!/\n/.test(homePage)) {
+ try {
+ let uri = Services.uriFixup.createFixupURI(homePage, 0);
+ handURIToExistingBrowser(uri, nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, features);
+ cmdLine.preventDefault = true;
+ } catch (e) {
+ }
+ }
+
+ if (!cmdLine.preventDefault) {
+ openWindow(null, getBrowserURL(), features, homePage);
+ cmdLine.preventDefault = true;
+ }
+ }
+
+ },
+
+ /* nsICommandLineValidator */
+ validate: function validate(cmdLine) {
+ var osintFlagIdx = cmdLine.findFlag("osint", false);
+
+ // If the osint flag is not present and we are not called by DDE then we're safe
+ if (cmdLine.state != nsICommandLine.STATE_REMOTE_EXPLICIT &&
+ cmdLine.findFlag("osint", false) == -1)
+ return;
+
+ // Other handlers may use osint so only handle the osint flag if a
+ // flag is also present and the command line is valid.
+ ["url", "news", "compose"].forEach(function(value) {
+ var flagIdx = cmdLine.findFlag(value, false);
+
+ if (flagIdx > -1) {
+ var testExpr = new RegExp("seamonkey" + value + ":");
+ if (cmdLine.length != flagIdx + 2 ||
+ testExpr.test(cmdLine.getArgument(flagIdx + 1)))
+ throw Cr.NS_ERROR_ABORT;
+ cmdLine.handleFlag("osint", false);
+ }
+ });
+ },
+
+ helpInfo: " -browser <url> Open a browser window.\n" +
+ " -private <url> Open a private window.\n" +
+ " -new-window <url> Open <url> in a new browser window.\n" +
+ " -new-tab <url> Open <url> in a new browser tab.\n" +
+ " -url <url> Open the specified url.\n" +
+ " -chrome <url> Open the specified chrome.\n" +
+ " -search <term> Search <term> with your default search engine.\n" +
+ " -preferences Open Preferences dialog.\n",
+
+ /* nsICommandLine */
+ length: 1,
+
+ getArgument: function getArgument(index) {
+ if (index == 0)
+ return this.currentArgument;
+
+ throw Cr.NS_ERROR_INVALID_ARG;
+ },
+
+ findFlag: function findFlag(flag, caseSensitive) {
+ if (caseSensitive)
+ return flag == this.currentArgument ? 0 : -1;
+ return flag.toLowerCase() == this.currentArgument.toLowerCase() ? 0 : -1;
+ },
+
+ removeArguments: function removeArguments(start, end) {
+ // do nothing
+ },
+
+ handleFlag: function handleFlag(flag, caseSensitive) {
+ if (caseSensitive)
+ return flag == this.currentArgument;
+ return flag.toLowerCase() == this.currentArgument.toLowerCase();
+ },
+
+ handleFlagWithParam : function handleFlagWithParam(flag, caseSensitive) {
+ if (this.handleFlag(flag, caseSensitive))
+ throw Cr.NS_ERROR_INVALID_ARG;
+ },
+
+ get state() {
+ return this.realCmdLine.state;
+ },
+
+ get preventDefault() {
+ return this.realCmdLine.preventDefault;
+ },
+
+ set preventDefault(preventDefault) {
+ return this.realCmdLine.preventDefault = preventDefault;
+ },
+
+ get workingDirectory() {
+ return this.realCmdLine.workingDirectory;
+ },
+
+ get windowContext() {
+ return this.realCmdLine.windowContext;
+ },
+
+ resolveFile: function resolveFile(arg) {
+ return this.realCmdLine.resolveFile(arg);
+ },
+
+ resolveURI: function resolveURI(arg) {
+ return this.realCmdLine.resolveURI(arg);
+ },
+
+ /* nsIContentHandler */
+ handleContent: function handleContent(contentType, context, request) {
+ var webNavInfo = Cc["@mozilla.org/webnavigation-info;1"]
+ .getService(nsIWebNavigationInfo);
+ if (!webNavInfo.isTypeSupported(contentType, null))
+ throw NS_ERROR_WONT_HANDLE_CONTENT;
+
+ request.QueryInterface(nsIChannel);
+ handURIToExistingBrowser(request.URI,
+ nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
+ "chrome,all,dialog=no",
+ request.loadInfo.triggeringPrincipal);
+ request.cancel(Cr.NS_BINDING_ABORTED);
+ },
+
+ /* nsIFactory */
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ return this.QueryInterface(iid);
+ },
+
+ lockFactory: function lockFactory(lock) {
+ /* no-op */
+ }
+};
+
+const BROWSER_CID = Components.ID("{c2343730-dc2c-11d3-98b3-001083010e9b}");
+
+function NSGetFactory(cid) {
+ if (cid.number == BROWSER_CID)
+ return nsBrowserContentHandler;
+ throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED;
+}
diff --git a/comm/suite/browser/nsBrowserContentListener.js b/comm/suite/browser/nsBrowserContentListener.js
new file mode 100644
index 0000000000..7e618f4f85
--- /dev/null
+++ b/comm/suite/browser/nsBrowserContentListener.js
@@ -0,0 +1,138 @@
+/* -*- 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/. */
+
+const nsIWebBrowserChrome = Ci.nsIWebBrowserChrome;
+
+function nsBrowserContentListener(toplevelWindow, contentWindow)
+{
+ // this one is not as easy as you would hope.
+ // need to convert toplevelWindow to an XPConnected object, instead
+ // of a DOM-based object, to be able to QI() it to nsIXULWindow
+
+ this.init(toplevelWindow, contentWindow);
+}
+
+/* implements nsIURIContentListener */
+
+nsBrowserContentListener.prototype =
+{
+ init: function(toplevelWindow, contentWindow)
+ {
+ this.toplevelWindow = toplevelWindow;
+ this.contentWindow = contentWindow;
+
+ // hook up the whole parent chain thing
+ var windowDocShell = this.convertWindowToDocShell(toplevelWindow);
+ if (windowDocShell) {
+ windowDocshell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIURIContentListener)
+ .parentContentListener = this;
+ }
+
+ var registerWindow = false;
+ try {
+ var treeItem = contentWindow.docShell.QueryInterface(Ci.nsIDocShellTreeItem);
+ var treeOwner = treeItem.treeOwner;
+ var interfaceRequestor = treeOwner.QueryInterface(Ci.nsIInterfaceRequestor);
+ var webBrowserChrome = interfaceRequestor.getInterface(nsIWebBrowserChrome);
+ if (webBrowserChrome)
+ {
+ var chromeFlags = webBrowserChrome.chromeFlags;
+ var res = chromeFlags & nsIWebBrowserChrome.CHROME_ALL;
+ var res2 = chromeFlags & nsIWebBrowserChrome.CHROME_DEFAULT;
+ if ( res == nsIWebBrowserChrome.CHROME_ALL || res2 == nsIWebBrowserChrome.CHROME_DEFAULT)
+ {
+ registerWindow = true;
+ }
+ }
+ } catch (ex) {}
+
+ // register ourselves
+ if (registerWindow)
+ {
+ var uriLoader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);
+ uriLoader.registerContentListener(this);
+ }
+ },
+ close: function()
+ {
+ this.contentWindow = null;
+ var uriLoader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);
+
+ uriLoader.unRegisterContentListener(this);
+ },
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIURIContentListener) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ doContent: function(contentType, isContentPreferred, request, contentHandler)
+ {
+ // forward the doContent to our content area webshell
+ var docShell = this.contentWindow.docShell;
+ if (Services.prefs.getIntPref("browser.link.open_external") == nsIBrowserDOMWindow.OPEN_NEWTAB) {
+ var newTab = gBrowser.loadOneTab("about:blank", {
+ inBackground: Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground")});
+ docShell = gBrowser.getBrowserForTab(newTab).docShell;
+ }
+
+ var contentListener;
+ try {
+ contentListener =
+ docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIURIContentListener);
+ } catch (ex) {
+ dump(ex);
+ }
+
+ if (!contentListener) return false;
+
+ return contentListener.doContent(contentType, isContentPreferred, request, contentHandler);
+
+ },
+
+ isPreferred: function(contentType, desiredContentType)
+ {
+ if (Services.prefs.getIntPref("browser.link.open_external") == nsIBrowserDOMWindow.OPEN_NEWWINDOW)
+ return false;
+
+ try {
+ var webNavInfo =
+ Cc["@mozilla.org/webnavigation-info;1"]
+ .getService(Ci.nsIWebNavigationInfo);
+ return webNavInfo.isTypeSupported(contentType, null);
+ } catch (e) {
+ // XXX propagate failures other than "NS_ERROR_NOT_AVAILABLE"?
+ // This seems to never get called, so not like it matters....
+ return false;
+ }
+ },
+ canHandleContent: function(contentType, isContentPreferred, desiredContentType)
+ {
+ var docShell = this.contentWindow.docShell;
+ var contentListener;
+ try {
+ contentListener =
+ docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIURIContentListener);
+ } catch (ex) {
+ dump(ex);
+ }
+ if (!contentListener) return false;
+
+ return contentListener.canHandleContent(contentType, isContentPreferred, desiredContentType);
+ },
+ convertWindowToDocShell: function(win) {
+ // don't know how to do this
+ return null;
+ },
+ loadCookie: null,
+ parentContentListener: null
+}
diff --git a/comm/suite/browser/nsBrowserStatusHandler.js b/comm/suite/browser/nsBrowserStatusHandler.js
new file mode 100644
index 0000000000..b4699f8f9c
--- /dev/null
+++ b/comm/suite/browser/nsBrowserStatusHandler.js
@@ -0,0 +1,473 @@
+/* -*- 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/. */
+
+function nsBrowserStatusHandler()
+{
+ this.init();
+}
+
+nsBrowserStatusHandler.prototype =
+{
+ // Stored Status, Link and Loading values
+ status : "",
+ defaultStatus : "",
+ jsStatus : "",
+ jsDefaultStatus : "",
+ overLink : "",
+ feeds : [],
+
+ QueryInterface : function(aIID)
+ {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsIXULBrowserWindow) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ init : function()
+ {
+ this.urlBar = document.getElementById("urlbar");
+ this.throbberElement = document.getElementById("navigator-throbber");
+ this.statusMeter = document.getElementById("statusbar-icon");
+ this.statusPanel = document.getElementById("statusbar-progresspanel");
+ this.stopButton = document.getElementById("stop-button");
+ this.stopMenu = document.getElementById("menuitem-stop");
+ this.stopContext = document.getElementById("context-stop");
+ this.statusTextField = document.getElementById("statusbar-display");
+ this.isImage = document.getElementById("isImage");
+ this.securityButton = document.getElementById("security-button");
+ this.evButton = document.getElementById("ev-button");
+ this.feedsMenu = document.getElementById("feedsMenu");
+ this.feedsButton = document.getElementById("feedsButton");
+
+ // Initialize the security button's state and tooltip text
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ this.onSecurityChange(null, null, nsIWebProgressListener.STATE_IS_INSECURE);
+ },
+
+ destroy : function()
+ {
+ // XXXjag to avoid leaks :-/, see bug 60729
+ this.urlBar = null;
+ this.throbberElement = null;
+ this.statusMeter = null;
+ this.statusPanel = null;
+ this.stopButton = null;
+ this.stopMenu = null;
+ this.stopContext = null;
+ this.statusTextField = null;
+ this.isImage = null;
+ this.securityButton = null;
+ this.evButton = null;
+ this.feedsButton = null;
+ this.feedsMenu = null;
+ },
+
+ // nsIXULBrowserWindow
+ setJSStatus : function(status)
+ {
+ this.jsStatus = status;
+ this.updateStatusField();
+ },
+
+ // nsIXULBrowserWindow
+ setJSDefaultStatus : function(status)
+ {
+ this.jsDefaultStatus = status;
+ this.updateStatusField();
+ },
+
+ setDefaultStatus : function(status)
+ {
+ this.defaultStatus = status;
+ this.updateStatusField();
+ },
+
+ // nsIXULBrowserWindow
+ setOverLink : function(link, context)
+ {
+ this.overLink = link;
+ // clear out 'Done' (or other message) on first hover
+ if (this.defaultStatus)
+ this.defaultStatus = "";
+ this.updateStatusField();
+ if (link)
+ this.statusTextField.setAttribute('crop', 'center');
+ else
+ this.statusTextField.setAttribute('crop', 'end');
+ },
+
+ // nsIXULBrowserWindow
+ // Called before links are navigated to to allow us to retarget them if needed.
+ onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
+ return originalTarget;
+ },
+
+ updateStatusField : function()
+ {
+ var text = this.overLink || this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
+
+ // check the current value so we don't trigger an attribute change
+ // and cause needless (slow!) UI updates
+ if (this.statusTextField.label != text)
+ this.statusTextField.label = text;
+ },
+
+/**
+ * Returns true if |aMimeType| is text-based, false otherwise.
+ *
+ * @param aMimeType
+ * The MIME type to check.
+ *
+ * If adding types to this function, please also check the similar
+ * function in mozilla/toolkit/content/widgets/findbar.xml.
+ */
+ mimeTypeIsTextBased : function(contentType)
+ {
+ return /^text\/|\+xml$/.test(contentType) ||
+ contentType == "application/x-javascript" ||
+ contentType == "application/javascript" ||
+ contentType == "application/xml" ||
+ contentType == "mozilla.application/cached-xul";
+ },
+
+ populateFeeds : function(popup)
+ {
+ // First clear out any old items
+ while (popup.hasChildNodes())
+ popup.lastChild.remove();
+
+ for (var i = 0; i < this.feeds.length; i++) {
+ var link = this.feeds[i];
+ var menuitem = document.createElement("menuitem");
+ menuitem.className = "menuitem-iconic bookmark-item";
+ menuitem.statusText = link.href;
+ menuitem.setAttribute("label", link.title || link.href);
+ popup.appendChild(menuitem);
+ }
+ },
+
+ onFeedAvailable : function(aLink)
+ {
+ this.feeds.push(aLink);
+ this.feedsMenu.removeAttribute("disabled");
+ this.feedsButton.hidden = false;
+ },
+
+ onLinkIconAvailable : function(aHref)
+ {
+ if (aHref && gProxyFavIcon &&
+ Services.prefs.getBoolPref("browser.chrome.site_icons")) {
+ var browser = getBrowser();
+ if (browser.userTypedValue === null)
+ gProxyFavIcon.setAttribute("src", aHref);
+ }
+ },
+
+ onProgressChange : function (aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress)
+ {
+ if (aMaxTotalProgress > 0) {
+ // This is highly optimized. Don't touch this code unless
+ // you are intimately familiar with the cost of setting
+ // attrs on XUL elements. -- hyatt
+ var percentage = (aCurTotalProgress * 100) / aMaxTotalProgress;
+ this.statusMeter.value = percentage;
+ }
+ },
+
+ onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
+ {
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ const nsIChannel = Ci.nsIChannel;
+ var ctype;
+ if (aStateFlags & nsIWebProgressListener.STATE_START) {
+ // This (thanks to the filter) is a network start or the first
+ // stray request (the first request outside of the document load),
+ // initialize the throbber and his friends.
+
+ // Call start document load listeners (only if this is a network load)
+ if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
+ aRequest && aWebProgress.isTopLevel)
+ this.startDocumentLoad(aRequest);
+
+ if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
+ // Show the progress meter
+ this.statusPanel.collapsed = false;
+ // Turn the throbber on.
+ this.throbberElement.setAttribute("busy", "true");
+ }
+
+ // XXX: These need to be based on window activity...
+ this.stopButton.disabled = false;
+ this.stopMenu.removeAttribute('disabled');
+ this.stopContext.removeAttribute('disabled');
+ }
+ else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
+ if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+ if (aRequest) {
+ if (aWebProgress.isTopLevel)
+ this.endDocumentLoad(aRequest, aStatus);
+ }
+ }
+
+ // This (thanks to the filter) is a network stop or the last
+ // request stop outside of loading the document, stop throbbers
+ // and progress bars and such
+ if (aRequest) {
+ var msg = "";
+ // Get the channel if the request is a channel
+ if (aRequest instanceof nsIChannel) {
+ var location = aRequest.URI.spec;
+ if (location != "about:blank") {
+ switch (aStatus) {
+ case Cr.NS_BINDING_ABORTED:
+ msg = gNavigatorBundle.getString("nv_stopped");
+ break;
+ case Cr.NS_ERROR_NET_TIMEOUT:
+ msg = gNavigatorBundle.getString("nv_timeout");
+ break;
+ }
+ }
+ }
+ // If msg is false then we did not have an error (channel may have
+ // been null, in the case of a stray image load).
+ if (!msg) {
+ msg = gNavigatorBundle.getString("nv_done");
+ }
+ this.status = "";
+ this.setDefaultStatus(msg);
+
+ // Disable menu entries for images, enable otherwise
+ if (content.document && this.mimeTypeIsTextBased(content.document.contentType))
+ this.isImage.removeAttribute('disabled');
+ else
+ this.isImage.setAttribute('disabled', 'true');
+ }
+
+ // Turn the progress meter and throbber off.
+ this.statusPanel.collapsed = true;
+ this.statusMeter.value = 0; // be sure to clear the progress bar
+ this.throbberElement.removeAttribute("busy");
+
+ // XXX: These need to be based on window activity...
+ // XXXjag: <command id="cmd_stop"/> ?
+ this.stopButton.disabled = true;
+ this.stopMenu.setAttribute('disabled', 'true');
+ this.stopContext.setAttribute('disabled', 'true');
+
+ ZoomListeners.onLocationChange(getBrowser().currentURI);
+ }
+ },
+
+ onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
+ {
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ if (gContextMenu) {
+ // Optimise for the common case
+ if (aWebProgress.isTopLevel)
+ document.getElementById("contentAreaContextMenu").hidePopup();
+ else {
+ for (var contextWindow = gContextMenu.target.ownerDocument.defaultView;
+ contextWindow != contextWindow.parent;
+ contextWindow = contextWindow.parent) {
+ if (contextWindow == aWebProgress.DOMWindow) {
+ document.getElementById("contentAreaContextMenu").hidePopup();
+ break;
+ }
+ }
+ }
+ }
+
+ if (document.tooltipNode) {
+ // Optimise for the common case
+ if (aWebProgress.isTopLevel) {
+ document.getElementById("aHTMLTooltip").hidePopup();
+ document.tooltipNode = null;
+ } else {
+ for (var tooltipWindow = document.tooltipNode.ownerDocument.defaultView;
+ tooltipWindow != tooltipWindow.parent;
+ tooltipWindow = tooltipWindow.parent) {
+ if (tooltipWindow == aWebProgress.DOMWindow) {
+ document.getElementById("aHTMLTooltip").hidePopup();
+ document.tooltipNode = null;
+ break;
+ }
+ }
+ }
+ }
+
+ // Hide the form invalid popup.
+ if (gFormSubmitObserver.panel) {
+ gFormSubmitObserver.panel.hidePopup();
+ }
+
+ // XXX temporary hack for bug 104532.
+ // Depends heavily on setOverLink implementation
+ if (!aRequest)
+ this.status = this.jsStatus = this.jsDefaultStatus = "";
+
+ this.setOverLink("");
+
+ // Disable menu entries for images, enable otherwise
+ if (content.document && this.mimeTypeIsTextBased(content.document.contentType))
+ this.isImage.removeAttribute('disabled');
+ else
+ this.isImage.setAttribute('disabled', 'true');
+
+ // We should probably not do this if the value has changed since the user
+ // searched
+ // Update urlbar only if a new page was loaded on the primary content area
+ // Do not update urlbar if there was a subframe navigation
+
+ var browser = getBrowser().selectedBrowser;
+ if (aWebProgress.isTopLevel) {
+ var userTypedValue = browser.userTypedValue;
+ if (userTypedValue === null) {
+ URLBarSetURI(aLocation, true);
+ } else {
+ this.urlBar.value = userTypedValue;
+ SetPageProxyState("invalid", null);
+ }
+
+ BookmarkingUI.updateStarState();
+
+ this.feedsMenu.setAttribute("disabled", "true");
+ this.feedsButton.hidden = true;
+ this.feeds = [];
+
+ // When background tab comes into foreground or loading a new page
+ // (aRequest set), might want to update zoom.
+ if (FullZoom.updateBackgroundTabs || aRequest){
+ FullZoom.onLocationChange(getBrowser().currentURI, !aRequest, browser);
+ ZoomListeners.onLocationChange(getBrowser().currentURI);
+ }
+ }
+ UpdateBackForwardButtons();
+
+ UpdateStatusBarPopupIcon();
+
+ BrowserSearch.updateSearchButton();
+ },
+
+ onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
+ {
+ this.status = aMessage;
+ this.updateStatusField();
+ },
+
+ onSecurityChange : function(aWebProgress, aRequest, aState)
+ {
+ const wpl = Ci.nsIWebProgressListener;
+ const wpl_security_bits = wpl.STATE_IS_SECURE |
+ wpl.STATE_IS_BROKEN |
+ wpl.STATE_IS_INSECURE;
+
+ var highlightSecure =
+ Services.prefs.getBoolPref("browser.urlbar.highlight.secure");
+
+ /* aState is defined as a bitmask that may be extended in the future.
+ * We filter out any unknown bits before testing for known values.
+ */
+ switch (aState & wpl_security_bits) {
+ case wpl.STATE_IS_SECURE:
+ const nsISSLStatusProvider = Ci.nsISSLStatusProvider;
+ var cert = getBrowser().securityUI.QueryInterface(nsISSLStatusProvider)
+ .SSLStatus.serverCert;
+ var issuerName = cert.issuerOrganization ||
+ cert.issuerCommonName || cert.issuerName;
+ this.securityButton.setAttribute("tooltiptext",
+ gNavigatorBundle.getFormattedString("securityButtonTooltipSecure",
+ [issuerName]));
+ this.securityButton.setAttribute("level", "high");
+ if (highlightSecure)
+ this.urlBar.setAttribute("level", "high");
+ else
+ this.urlBar.removeAttribute("level");
+ break;
+ case wpl.STATE_IS_BROKEN:
+ this.securityButton.setAttribute("tooltiptext",
+ gNavigatorBundle.getString("securityButtonTooltipMixedContent"));
+ this.securityButton.setAttribute("level", "broken");
+ if (highlightSecure)
+ this.urlBar.setAttribute("level", "broken");
+ else
+ this.urlBar.removeAttribute("level");
+ break;
+ case wpl.STATE_IS_INSECURE:
+ default:
+ this.securityButton.setAttribute("tooltiptext",
+ gNavigatorBundle.getString("securityButtonTooltipInsecure"));
+ this.securityButton.removeAttribute("level");
+ this.urlBar.removeAttribute("level");
+ break;
+ }
+
+ if (aState & wpl.STATE_IDENTITY_EV_TOPLEVEL) {
+ var organization =
+ getBrowser().securityUI
+ .QueryInterface(Ci.nsISSLStatusProvider)
+ .SSLStatus
+ .QueryInterface(Ci.nsISSLStatus)
+ .serverCert.organization;
+ this.securityButton.setAttribute("label", organization);
+ this.evButton.setAttribute("tooltiptext", organization);
+ this.evButton.hidden = false;
+ } else {
+ this.securityButton.removeAttribute("label");
+ this.evButton.hidden = true;
+ }
+ },
+
+ startDocumentLoad : function(aRequest)
+ {
+ var uri = aRequest.QueryInterface(Ci.nsIChannel).originalURI;
+
+ // clear out search-engine data
+ getBrowser().selectedBrowser.engines = null;
+
+ // Set the URI now if it isn't already set, so that the user can tell which
+ // site is loading. Only do this if user requested the load via chrome UI,
+ // to minimise spoofing risk.
+ if (!content.opener &&
+ !gURLBar.value &&
+ getWebNavigation().currentURI.spec == "about:blank")
+ URLBarSetURI(uri);
+
+ try {
+ Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
+ } catch (e) {
+ }
+ },
+
+ endDocumentLoad : function(aRequest, aStatus)
+ {
+ const nsIChannel = Ci.nsIChannel;
+ var urlStr = aRequest.QueryInterface(nsIChannel).originalURI.spec;
+
+ if (Components.isSuccessCode(aStatus))
+ dump("Document "+urlStr+" loaded successfully\n"); // per QA request
+ else {
+ // per QA request
+ var e = new Components.Exception("", aStatus);
+ var name = e.name;
+ dump("Error loading URL "+urlStr+" : "+
+ Number(aStatus).toString(16));
+ if (name)
+ dump(" ("+name+")");
+ dump('\n');
+ }
+
+ var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
+ try {
+ Services.obs.notifyObservers(content, notification, urlStr);
+ } catch (e) {
+ }
+ }
+}
+
diff --git a/comm/suite/browser/nsTypeAheadFind.js b/comm/suite/browser/nsTypeAheadFind.js
new file mode 100644
index 0000000000..aa5e0b9fba
--- /dev/null
+++ b/comm/suite/browser/nsTypeAheadFind.js
@@ -0,0 +1,416 @@
+/* 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 kSpace = " ".charCodeAt(0);
+const kSlash = "/".charCodeAt(0);
+const kApostrophe = "'".charCodeAt(0);
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function findTypeController(aTypeAheadFind, aWindow)
+{
+ this.mTypeAheadFind = aTypeAheadFind;
+ this.mWindow = aWindow;
+}
+
+findTypeController.prototype = {
+ /* nsIController */
+ supportsCommand: function(aCommand) {
+ return aCommand == "cmd_findTypeText" || aCommand == "cmd_findTypeLinks";
+ },
+
+ isCommandEnabled: function(aCommand) {
+ // We can always find if there's a primary content window in which to find.
+ if (this.mWindow.content)
+ return true;
+
+ // We can also find if the focused window is a content window.
+ // Note: this gets called during a focus change
+ // so the new window might not have focus yet.
+ var commandDispatcher = this.mWindow.document.commandDispatcher;
+ var e = commandDispatcher.focusedElement;
+ var w = e ? e.ownerDocument.defaultView : commandDispatcher.focusedWindow;
+ return w.top != this.mWindow;
+ },
+
+ doCommand: function(aCommand) {
+ this.mTypeAheadFind.startFind(this.mWindow, aCommand != "cmd_findTypeText");
+ },
+
+ onEvent: function(aEvent) {
+ }
+}
+
+function typeAheadFind()
+{
+}
+
+typeAheadFind.prototype = {
+ /* properties required for XPCOMUtils */
+ classID: Components.ID("{45c8f75b-a299-4178-a461-f63690389055}"),
+
+ /* members */
+ mBadKeysSinceMatch: 0,
+ mBundle: null,
+ mCurrentWindow: null,
+ mEventTarget: null,
+ mFind: null,
+ mFindService: null,
+ mFound: null,
+ mLinks: false,
+ mSearchString: "",
+ mSelection: null,
+ mTimer: null,
+ mXULBrowserWindow: null,
+
+ /* nsISupports */
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsISupportsWeakReference,
+ Ci.nsIObserver,
+ Ci.nsITimerCallback,
+ Ci.nsIDOMEventListener,
+ Ci.nsISelectionListener]),
+
+ /* nsIObserver */
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == "app-startup") {
+ // It's now safe to get our pref branch.
+ this.mPrefs = Services.prefs.getBranch("accessibility.typeaheadfind.");
+ // We need to add our event listeners to all windows.
+ Services.ww.registerNotification(this);
+ // We also need to listen for find again commands
+ Services.obs.addObserver(this, "nsWebBrowserFind_FindAgain", true);
+ }
+ if (aTopic == "domwindowopened") {
+ // Add our listeners. They get automatically removed on window teardown.
+ aSubject.controllers.appendController(new findTypeController(this, aSubject));
+ Services.els.addSystemEventListener(aSubject, "keypress", this, false);
+ }
+ if (aTopic == "nsWebBrowserFind_FindAgain" &&
+ aSubject instanceof Ci.nsISupportsInterfacePointer &&
+ aSubject.data instanceof Ci.nsIDOMWindow &&
+ aSubject.data.top == this.mCurrentWindow &&
+ this.mSearchString) {
+ // It's a find again. Was it one that we just searched for?
+ var w = aSubject.data;
+ var find = w.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebBrowserFind);
+ if (find.searchString.toLowerCase() == this.mSearchString) {
+ var reverse = aData == "up";
+ this.stopFind(false);
+ var result = Ci.nsITypeAheadFind.FIND_NOTFOUND;
+ if (!this.mBadKeysSinceMatch)
+ result = this.mFind.findAgain(reverse, this.mLinks);
+ this.showStatusMatch(result, reverse ? "prevmatch" : "nextmatch");
+ // Don't let anyone else try to find again.
+ aSubject.data = null;
+ }
+ }
+ },
+
+ /* nsITimerCallback */
+ notify: function(aTimer) {
+ this.stopFind(false);
+ },
+
+ /* nsIDOMEventListener */
+ handleEvent: function(aEvent) {
+ if (!aEvent.type.startsWith("key")) {
+ this.stopFind(false);
+ return true;
+ }
+
+ // We don't care about these keys.
+ if (aEvent.altKey || aEvent.ctrlKey || aEvent.metaKey)
+ return true;
+
+ if (aEvent.type != "keypress") {
+ aEvent.stopPropagation();
+ return true;
+ }
+
+ // Are we already in a find?
+ if (aEvent.eventPhase == Ci.nsIDOMEvent.CAPTURING_PHASE)
+ return this.processKey(aEvent);
+
+ // Check whether we want to start a new find.
+ if (aEvent.defaultPrevented)
+ return true;
+
+ // We don't want to start a find on a control character.
+ // We also don't want to start on a space, since that scrolls the page.
+ if (aEvent.keyCode || aEvent.charCode <= kSpace)
+ return true;
+
+ // Don't start a find if the focus is an editable element.
+ var window = aEvent.currentTarget;
+ var element = window.document.commandDispatcher.focusedElement;
+ if (element.nodeType == element.ELEMENT_NODE &&
+ element.namespaceURI == "http://www.w3.org/1999/xhtml" &&
+ element.isContentEditable)
+ return true;
+
+ // Don't start a find if the focus is on a form element.
+ if ((element.nodeType == element.ELEMENT_NODE &&
+ element.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul") ||
+ ChromeUtils.getClassName(element) === "HTMLEmbedElement" ||
+ ChromeUtils.getClassName(element) === "HTMLObjectElement" ||
+ ChromeUtils.getClassName(element) === "HTMLSelectElement" ||
+ ChromeUtils.getClassName(element) === "HTMLTextAreaElement")
+ return true;
+
+ // Don't start a find if the focus is on an editable field
+ if (ChromeUtils.getClassName(element) === "HTMLInputElement" &&
+ element.mozIsTextField(false))
+ return true;
+
+ // Don't start a find if the focus isn't or can't be set to content
+ var w = window.document.commandDispatcher.focusedWindow;
+ if (w.top == window)
+ w = window.content;
+ if (!w)
+ return true;
+
+ var webNav = w.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation);
+ try {
+ // Don't start a find if the window is in design mode
+ if (webNav.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIEditingSession)
+ .windowIsEditable(w))
+ return true;
+ } catch (e) {
+ }
+
+ switch (aEvent.charCode) {
+ // Start finding text as you type
+ case kSlash:
+ aEvent.preventDefault();
+ this.startFind(window, false);
+ break;
+
+ // Start finding links as you type
+ case kApostrophe:
+ aEvent.preventDefault();
+ this.startFind(window, true);
+ break;
+
+ default:
+ // Don't start if typeahead find is disabled
+ if (!this.mPrefs.getBoolPref("autostart"))
+ return true;
+ // Don't start in windows that don't want autostart
+ if (webNav.QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler.getAttribute("autofind") == "false")
+ return true;
+ this.startFind(window, this.mPrefs.getBoolPref("linksonly"));
+ this.processKey(aEvent);
+ }
+ return false;
+ },
+
+ /* nsISelectionListener */
+ notifySelectionChanged: function(aDoc, aSelection, aReason) {
+ this.stopFind(false);
+ },
+
+ /* private methods */
+ showStatus: function(aText) {
+ if (this.mXULBrowserWindow)
+ this.mXULBrowserWindow.setOverLink(aText, null);
+ },
+ showStatusString: function(aString) {
+ // Set the status text from a localised string
+ this.showStatus(aString && this.mBundle.GetStringFromName(aString));
+ },
+ showStatusMatch: function(aResult, aExtra) {
+ // Set the status text from a find result
+ // link|text "..." [not] found [next|previous match] [(href)]
+ if (aExtra)
+ aExtra = " " + this.mBundle.GetStringFromName(aExtra);
+ var url = "";
+ var string = this.mLinks ? "link" : "text";
+ if (aResult == Ci.nsITypeAheadFind.FIND_NOTFOUND)
+ string += "not";
+ else if (this.mFind.foundLink && this.mFind.foundLink.href)
+ url = " " + this.mBundle.GetStringFromName("openparen") +
+ this.mFind.foundLink.href +
+ this.mBundle.GetStringFromName("closeparen");
+ string += "found";
+ this.showStatus(this.mBundle.GetStringFromName(string) +
+ this.mSearchString +
+ this.mBundle.GetStringFromName("closequote") +
+ aExtra + url);
+ },
+ startTimer: function() {
+ if (this.mPrefs.getBoolPref("enabletimeout")) {
+ if (!this.mTimer)
+ this.mTimer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ this.mTimer.initWithCallback(this,
+ this.mPrefs.getIntPref("timeout"),
+ Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ },
+ processKey: function(aEvent) {
+ // Escape always cancels the find.
+ if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ this.stopFind(false);
+ return false;
+ }
+
+ var result = Ci.nsITypeAheadFind.FIND_NOTFOUND;
+ if (aEvent.keyCode == aEvent.DOM_VK_BACK_SPACE) {
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ this.mSearchString = this.mSearchString.slice(0, -1);
+ // Backspacing past the start of the string cancels the find.
+ if (!this.mSearchString) {
+ this.stopFind(true);
+ return false;
+ }
+ this.startTimer();
+ // The find will change the selection, so stop listening for changes
+ this.mEventTarget.removeEventListener("blur", this, true);
+ if (this.mSelection)
+ this.mSelection.removeSelectionListener(this);
+ // Don't bother finding until we get back to a working string
+ if (!this.mBadKeysSinceMatch || !--this.mBadKeysSinceMatch)
+ result = this.mFind.find(this.mSearchString, this.mLinks);
+ } else {
+ // Ignore control characters.
+ if (aEvent.keyCode || aEvent.charCode < kSpace)
+ return true;
+
+ this.startTimer();
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+
+ // It looks as if the cat walked on the keyboard.
+ if (this.mBadKeysSinceMatch >= 3)
+ return false;
+
+ // The find will change the selection/focus, so stop listening for changes
+ this.mEventTarget.removeEventListener("blur", this, true);
+ if (this.mSelection)
+ this.mSelection.removeSelectionListener(this);
+ var previousString = this.mSearchString;
+ this.mSearchString += String.fromCharCode(aEvent.charCode).toLowerCase();
+ if (!this.mBadKeysSinceMatch) {
+ result = this.mFind.find(this.mSearchString, this.mLinks);
+ if (previousString &&
+ result == Ci.nsITypeAheadFind.FIND_NOTFOUND)
+ // Use a separate find instance to rehighlight the previous match
+ // until bug 463294 is fixed.
+ this.mFound.find(previousString, this.mLinks);
+ }
+ if (result == Ci.nsITypeAheadFind.FIND_NOTFOUND)
+ this.mBadKeysSinceMatch++;
+ }
+
+ // Ensure that the correct frame is focused (work around for bug 485213).
+ if (this.mFind.currentWindow)
+ this.mFind.currentWindow.focus();
+
+ this.showStatusMatch(result, "");
+ if (!this.mFindService)
+ this.mFindService = Cc["@mozilla.org/find/find_service;1"]
+ .getService(Ci.nsIFindService);
+ this.mFindService.searchString = this.mSearchString;
+ // Watch for blur changes in case the cursor leaves the current field.
+ this.mEventTarget.addEventListener("blur", this, true);
+ // Also watch for the cursor moving within the current field or window.
+ var commandDispatcher = this.mEventTarget.ownerDocument.commandDispatcher;
+ var editable = commandDispatcher.focusedElement;
+ if (editable &&
+ ["HTMLInputElement", "HTMLTextAreaElement"]
+ .includes(ChromeUtils.getClassName(editable)))
+ this.mSelection = editable.editor.selection;
+ else
+ this.mSelection = commandDispatcher.focusedWindow.getSelection();
+ this.mSelection.addSelectionListener(this);
+ return false;
+ },
+ startFind: function(aWindow, aLinks) {
+ if (this.mEventTarget)
+ this.stopFind(true);
+ // Try to get the status bar for the specified window
+ this.mXULBrowserWindow =
+ aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow)
+ .XULBrowserWindow;
+
+ // If the current window is chrome then focus content instead
+ var w = aWindow.document.commandDispatcher.focusedWindow.top;
+ if (w == aWindow)
+ (w = aWindow.content).focus();
+
+ // Get two toolkit typeaheadfind instances if we don't have them already.
+ var docShell = w.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ if (!this.mFind) {
+ this.mFind = Cc["@mozilla.org/typeaheadfind;1"]
+ .createInstance(Ci.nsITypeAheadFind);
+ this.mFind.init(docShell);
+ this.mFound = Cc["@mozilla.org/typeaheadfind;1"]
+ .createInstance(Ci.nsITypeAheadFind);
+ this.mFound.init(docShell);
+ }
+
+ // Get the string bundle if we don't have it already.
+ if (!this.mBundle)
+ this.mBundle = Services.strings
+ .createBundle("chrome://communicator/locale/typeaheadfind.properties");
+
+ // Set up all our properties
+ this.mFind.setDocShell(docShell);
+ this.mFound.setDocShell(docShell);
+ this.mEventTarget = docShell.chromeEventHandler;
+ this.mEventTarget.addEventListener("keypress", this, true);
+ this.mEventTarget.addEventListener("keydown", this, true);
+ this.mEventTarget.addEventListener("keyup", this, true);
+ this.mEventTarget.addEventListener("pagehide", this, true);
+ this.mCurrentWindow = w;
+ this.mBadKeysSinceMatch = 0;
+ this.mSearchString = "";
+ this.mLinks = aLinks;
+ this.showStatusString(this.mLinks ? "startlinkfind" : "starttextfind");
+ this.startTimer();
+ },
+ stopFind: function(aClear) {
+ if (this.mTimer)
+ this.mTimer.cancel();
+ if (this.mFind)
+ this.mFind.setSelectionModeAndRepaint(
+ Ci.nsISelectionController.SELECTION_ON);
+ if (this.mEventTarget) {
+ this.mEventTarget.removeEventListener("blur", this, true);
+ this.mEventTarget.removeEventListener("pagehide", this, true);
+ this.mEventTarget.removeEventListener("keypress", this, true);
+ this.mEventTarget.removeEventListener("keydown", this, true);
+ this.mEventTarget.removeEventListener("keyup", this, true);
+ }
+ this.mEventTarget = null;
+ if (this.mSelection)
+ this.mSelection.removeSelectionListener(this);
+ this.mSelection = null;
+ this.showStatusString(aClear ? "" : "stopfind");
+ if (aClear)
+ this.mSearchString = "";
+ if (aClear && this.mFind)
+ this.mFind.collapseSelection();
+ },
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([typeAheadFind]);
diff --git a/comm/suite/browser/pageinfo/feeds.js b/comm/suite/browser/pageinfo/feeds.js
new file mode 100644
index 0000000000..194f436d23
--- /dev/null
+++ b/comm/suite/browser/pageinfo/feeds.js
@@ -0,0 +1,31 @@
+/* -*- Mode: Javascript; 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/. */
+
+function initFeedTab(feeds)
+{
+ for (let feed of feeds) {
+ let [name, type, url] = feed;
+ addRow(name, type, url);
+ }
+
+ var feedListbox = document.getElementById("feedListbox");
+ document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
+}
+
+function onSubscribeFeed(event)
+{
+ var listbox = document.getElementById("feedListbox");
+ subscribeToFeed(listbox.selectedItem.getAttribute("feedURL"), event);
+}
+
+function addRow(name, type, url)
+{
+ var item = document.createElement("richlistitem");
+ item.setAttribute("feed", "true");
+ item.setAttribute("name", name);
+ item.setAttribute("type", type);
+ item.setAttribute("feedURL", url);
+ document.getElementById("feedListbox").appendChild(item);
+}
diff --git a/comm/suite/browser/pageinfo/feeds.xml b/comm/suite/browser/pageinfo/feeds.xml
new file mode 100644
index 0000000000..c487e30f55
--- /dev/null
+++ b/comm/suite/browser/pageinfo/feeds.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?><!-- -*- Mode: nXML; indent-tabs-mode: nil -*- -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE bindings [
+ <!ENTITY % pageInfoDTD SYSTEM "chrome://navigator/locale/pageInfo.dtd">
+ %pageInfoDTD;
+]>
+
+<bindings id="feedBindings"
+ 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="feed" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <content>
+ <xul:vbox flex="1">
+ <xul:hbox flex="1">
+ <xul:textbox flex="1" readonly="true" xbl:inherits="value=name"
+ class="feedTitle"/>
+ <xul:label xbl:inherits="value=type"/>
+ </xul:hbox>
+ <xul:vbox align="start" flex="1">
+ <xul:hbox>
+ <xul:label xbl:inherits="value=feedURL,tooltiptext=feedURL"
+ class="text-link"
+ flex="1"
+ onclick="openUILink(this.value, event);" crop="end"/>
+ </xul:hbox>
+ </xul:vbox>
+ <xul:hbox flex="1" class="feed-subscribe">
+ <xul:spacer flex="1"/>
+ <xul:button label="&feedSubscribe;" accesskey="&feedSubscribe.accesskey;"
+ oncommand="onSubscribeFeed(event)"/>
+ </xul:hbox>
+ </xul:vbox>
+ </content>
+ </binding>
+</bindings>
diff --git a/comm/suite/browser/pageinfo/pageInfo.css b/comm/suite/browser/pageinfo/pageInfo.css
new file mode 100644
index 0000000000..749c01a434
--- /dev/null
+++ b/comm/suite/browser/pageinfo/pageInfo.css
@@ -0,0 +1,18 @@
+/* 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[feed] {
+ -moz-binding: url("chrome://navigator/content/pageinfo/feeds.xml#feed");
+}
+
+#thepreviewimage {
+ display: block;
+/* This following entry can be removed when Bug 522850 is fixed. */
+ min-width: 1px;
+}
+
+.urltext:-moz-locale-dir(rtl) {
+ direction: ltr !important;
+ text-align: end !important;
+}
diff --git a/comm/suite/browser/pageinfo/pageInfo.js b/comm/suite/browser/pageinfo/pageInfo.js
new file mode 100644
index 0000000000..d4f59be55c
--- /dev/null
+++ b/comm/suite/browser/pageinfo/pageInfo.js
@@ -0,0 +1,1177 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ Downloads: "resource://gre/modules/Downloads.jsm",
+ FileUtils: "resource://gre/modules/FileUtils.jsm",
+});
+
+//******** define a js object to implement nsITreeView
+function pageInfoTreeView(treeid, copycol)
+{
+ /* copycol is the index number for the column that we want to add to
+ * the copy-n-paste buffer when the user hits accel-c.
+ */
+ this.treeid = treeid;
+ this.copycol = copycol;
+ this.rows = 0;
+ this.tree = null;
+ this.data = [ ];
+ this.selection = null;
+ this.sortcol = -1;
+ this.sortdir = false;
+}
+
+pageInfoTreeView.prototype = {
+ get rowCount() { return this.rows; },
+
+ setTree: function(tree)
+ {
+ this.tree = tree;
+ },
+
+ getCellText: function(row, column)
+ {
+ // row can be null, but js arrays are 0-indexed.
+ return this.data[row][column.index] || "";
+ },
+
+ setCellValue: function(row, column, value)
+ {
+ },
+
+ setCellText: function(row, column, value)
+ {
+ this.data[row][column.index] = value;
+ },
+
+ addRow: function(row)
+ {
+ this.rows = this.data.push(row);
+ this.rowCountChanged(this.rows - 1, 1);
+ if (this.selection.count == 0 && this.rowCount && !gImageElement) {
+ this.selection.select(0);
+ }
+ },
+
+ addRows: function(rows)
+ {
+ for (let row of rows) {
+ this.addRow(row);
+ }
+ },
+
+ rowCountChanged: function(index, count)
+ {
+ this.tree.rowCountChanged(index, count);
+ },
+
+ invalidate: function()
+ {
+ this.tree.invalidate();
+ },
+
+ clear: function()
+ {
+ if (this.tree)
+ this.tree.rowCountChanged(0, -this.rows);
+ this.rows = 0;
+ this.data = [];
+ },
+
+ cycleHeader: function cycleHeader(col)
+ {
+ this.doSort(col, col.index);
+ },
+
+ doSort: function doSort(col, index, comparator)
+ {
+ var tree = document.getElementById(this.treeid);
+ if (!comparator) {
+ comparator = function comparator(a, b) {
+ return (a || "").toLowerCase().localeCompare((b || "").toLowerCase());
+ };
+ }
+
+ this.sortdir = gTreeUtils.sort(tree, this, this.data, index,
+ comparator, this.sortcol, this.sortdir);
+
+ Array.from(this.tree.columns).forEach(function(treecol) {
+ treecol.element.removeAttribute("sortActive");
+ treecol.element.removeAttribute("sortDirection");
+ });
+ col.element.setAttribute("sortActive", true);
+ col.element.setAttribute("sortDirection", this.sortdir ?
+ "ascending" : "descending");
+
+ this.sortcol = index;
+ },
+
+ getRowProperties: function(row) { return ""; },
+ getCellProperties: function(row, column) { return ""; },
+ getColumnProperties: function(column) { return ""; },
+ isContainer: function(index) { return false; },
+ isContainerOpen: function(index) { return false; },
+ isSeparator: function(index) { return false; },
+ isSorted: function() { return this.sortcol > -1 },
+ canDrop: function(index, orientation) { return false; },
+ drop: function(row, orientation) { return false; },
+ getParentIndex: function(index) { return -1; },
+ hasNextSibling: function(index, after) { return false; },
+ getLevel: function(index) { return 0; },
+ getImageSrc: function(row, column) { },
+ getProgressMode: function(row, column) { },
+ getCellValue: function(row, column) {
+ let col = (column != null) ? column : this.copycol;
+ return (row < 0 || col < 0) ? "" : (this.data[row][col] || "");
+ },
+ toggleOpenState: function(index) { },
+ selectionChanged: function() { },
+ cycleCell: function(row, column) { },
+ isEditable: function(row, column) { return false; },
+ isSelectable: function(row, column) { return false; },
+};
+
+// mmm, yummy. global variables.
+var gDocInfo = null;
+var gImageElement = null;
+
+// column number to help using the data array
+const COL_IMAGE_ADDRESS = 0;
+const COL_IMAGE_TYPE = 1;
+const COL_IMAGE_SIZE = 2;
+const COL_IMAGE_ALT = 3;
+const COL_IMAGE_COUNT = 4;
+const COL_IMAGE_NODE = 5;
+const COL_IMAGE_BG = 6;
+const COL_IMAGE_SIZENUM = 7;
+const COL_IMAGE_PERSIST = 8;
+const COL_IMAGE_MIME = 9;
+
+// column number to copy from, second argument to pageInfoTreeView's constructor
+const COPYCOL_NONE = -1;
+const COPYCOL_META_CONTENT = 1;
+const COPYCOL_FORM_ACTION = 2;
+const COPYCOL_FIELD_VALUE = 3;
+const COPYCOL_LINK_ADDRESS = 1;
+const COPYCOL_IMAGE = COL_IMAGE_ADDRESS;
+
+// one nsITreeView for each tree in the window
+var gMetaView = new pageInfoTreeView("metatree", COPYCOL_META_CONTENT);
+var gFormView = new pageInfoTreeView("formtree", COPYCOL_FORM_ACTION);
+var gFieldView = new pageInfoTreeView("formpreview", COPYCOL_FIELD_VALUE);
+var gLinkView = new pageInfoTreeView("linktree", COPYCOL_LINK_ADDRESS);
+var gImageView = new pageInfoTreeView("imagetree", COPYCOL_IMAGE);
+
+gImageView.getCellProperties = function(row, col) {
+ var data = gImageView.data[row];
+ var item = gImageView.data[row][COL_IMAGE_NODE];
+ var properties = col.id == "image-address" ? "ltr" : "";
+ if (!checkProtocol(data) || item.HTMLEmbedElement ||
+ (item.HTMLObjectElement && !item.type.startsWith("image/")))
+ properties += " broken";
+
+ return properties;
+};
+
+gFormView.getCellProperties = function(row, col) {
+ return col.id == "form-action" ? "ltr" : "";
+};
+
+gLinkView.getCellProperties = function(row, col) {
+ return col.id == "link-address" ? "ltr" : "";
+};
+
+gImageView.cycleHeader = function(col)
+{
+ var index = col.index;
+ var comparator;
+ switch (col.index) {
+ case COL_IMAGE_SIZE:
+ index = COL_IMAGE_SIZENUM;
+ case COL_IMAGE_COUNT:
+ comparator = function numComparator(a, b) { return a - b; };
+ break;
+ }
+
+ this.doSort(col, index, comparator);
+};
+
+var gImageHash = { };
+
+// localized strings (will be filled in when the document is loaded)
+// this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
+var gStrings = { };
+var gBundle;
+
+const DRAGSERVICE_CONTRACTID = "@mozilla.org/widget/dragservice;1";
+const TRANSFERABLE_CONTRACTID = "@mozilla.org/widget/transferable;1";
+const STRING_CONTRACTID = "@mozilla.org/supports-string;1";
+
+var loadContextInfo = Services.loadContextInfo.fromLoadContext(
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext), false);
+var diskStorage = Services.cache2.diskCacheStorage(loadContextInfo, false);
+
+const nsICertificateDialogs = Ci.nsICertificateDialogs;
+const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1"
+
+/* Overlays register functions here.
+ * These arrays are used to hold callbacks that Page Info will call at
+ * various stages. Use them by simply appending a function to them.
+ * For example, add a function to onLoadRegistry by invoking
+ * "onLoadRegistry.push(XXXLoadFunc);"
+ * The XXXLoadFunc should be unique to the overlay module, and will be
+ * invoked as "XXXLoadFunc();"
+ */
+
+// These functions are called to build the data displayed in the Page
+// Info window.
+var onLoadRegistry = [ ];
+
+// These functions are called to remove old data still displayed in
+// the window when the document whose information is displayed
+// changes. For example, the list of images in the Media tab
+// is cleared.
+var onResetRegistry = [ ];
+
+// These functions are called once when all the elements in all of the target
+// document (and all of its subframes, if any) have been processed
+var onFinished = [ ];
+
+// These functions are called once when the Page Info window is closed.
+var onUnloadRegistry = [ ];
+
+/* Called when PageInfo window is loaded. Arguments are:
+ * window.arguments[0] - (optional) an object consisting of
+ * - doc: (optional) document to use for source. if not provided,
+ * the calling window's document will be used
+ * - initialTab: (optional) id of the inital tab to display
+ */
+function onLoadPageInfo()
+{
+ gBundle = document.getElementById("pageinfobundle");
+ var strNames = ["unknown", "notSet", "mediaImg", "mediaBGImg",
+ "mediaBorderImg", "mediaListImg", "mediaCursor",
+ "mediaObject", "mediaEmbed", "mediaLink", "mediaInput",
+ "mediaVideo", "mediaAudio",
+ "formTitle", "formUntitled", "formDefaultTarget",
+ "formChecked", "formUnchecked", "formPassword", "linkAnchor",
+ "linkArea", "linkSubmission", "linkSubmit", "linkRel",
+ "linkStylesheet", "linkRev", "linkX", "linkScript",
+ "linkScriptInline", "yes"];
+ strNames.forEach(function(n) { gStrings[n] = gBundle.getString(n); });
+
+ var args = "arguments" in window &&
+ window.arguments.length >= 1 &&
+ window.arguments[0];
+
+ // init views
+ function initView(treeid, view)
+ {
+ document.getElementById(treeid).view = view;
+ }
+
+ initView("imagetree", gImageView);
+ initView("formtree", gFormView);
+ initView("formpreview", gFieldView);
+ initView("linktree", gLinkView);
+ initPermission();
+
+ /* Select the requested tab, if the name is specified */
+ loadTab(args);
+ Services.obs.notifyObservers(window, "page-info-dialog-loaded");
+}
+
+function loadPageInfo(frameOuterWindowID, imageElement, browser)
+{
+ browser = browser || window.opener.gBrowser.selectedBrowser;
+ let mm = browser.messageManager;
+
+ gStrings["application/rss+xml"] = gBundle.getString("feedRss");
+ gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
+ gStrings["text/xml"] = gBundle.getString("feedXML");
+ gStrings["application/xml"] = gBundle.getString("feedXML");
+ gStrings["application/rdf+xml"] = gBundle.getString("feedXML");
+
+ // Look for pageInfoListener in content.js.
+ // Sends message to listener with arguments.
+ mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
+ frameOuterWindowID: frameOuterWindowID},
+ { imageElement });
+
+ let pageInfoData;
+
+ // Get initial pageInfoData needed to display the general, feeds, permission
+ // and security tabs.
+ mm.addMessageListener("PageInfo:data", function onmessage(message) {
+ mm.removeMessageListener("PageInfo:data", onmessage);
+ pageInfoData = message.data;
+ let docInfo = pageInfoData.docInfo;
+ let windowInfo = pageInfoData.windowInfo;
+ let uri = makeURI(docInfo.documentURIObject.spec);
+ let principal = docInfo.principal;
+ gDocInfo = docInfo;
+
+ gImageElement = pageInfoData.imageInfo;
+
+ var titleFormat = windowInfo.isTopWindow ? "pageInfo.page.title"
+ : "pageInfo.frame.title";
+ document.title = gBundle.getFormattedString(titleFormat,
+ [docInfo.location]);
+
+ document.getElementById("main-window").setAttribute("relatedUrl",
+ docInfo.location);
+
+ makeGeneralTab(pageInfoData.metaViewRows, docInfo);
+ initFeedTab(pageInfoData.feeds);
+ onLoadPermission(uri, principal);
+ securityOnLoad(uri, windowInfo);
+ });
+
+ // Get the media elements from content script to setup the media tab.
+ mm.addMessageListener("PageInfo:mediaData", function onmessage(message) {
+ // Page info window was closed.
+ if (window.closed) {
+ mm.removeMessageListener("PageInfo:mediaData", onmessage);
+ return;
+ }
+
+ // The page info media fetching has been completed.
+ if (message.data.isComplete) {
+ mm.removeMessageListener("PageInfo:mediaData", onmessage);
+ onFinished.forEach(function(func) { func(pageInfoData); });
+ return;
+ }
+
+ if (message.data.imageItems) {
+ for (let item of message.data.imageItems) {
+ addImage(item);
+ }
+ selectImage();
+ }
+
+ if (message.data.linkItems) {
+ gLinkView.addRows(message.data.linkItems);
+ }
+
+ if (message.data.formItems) {
+ gFormView.addRows(message.data.formItems);
+ }
+ });
+
+ /* Call registered overlay init functions */
+ onLoadRegistry.forEach(function(func) { func(); });
+}
+
+function resetPageInfo(args)
+{
+ /* Reset Media tab */
+ // Remove the observer, only if there is at least 1 image.
+ if (gImageView.data.length != 0) {
+ Services.obs.removeObserver(imagePermissionObserver, "perm-changed");
+ }
+
+ /* Reset tree views */
+ gMetaView.clear();
+ gFormView.clear();
+ gFieldView.clear();
+ gLinkView.clear();
+ gImageView.clear();
+ gImageHash = {};
+
+ /* Reset Feeds Tab */
+ var feedListbox = document.getElementById("feedListbox");
+ while (feedListbox.hasChildNodes())
+ feedListbox.lastChild.remove();
+
+ /* Call registered overlay reset functions */
+ onResetRegistry.forEach(function(func) { func(); });
+
+ /* Rebuild the data */
+ loadTab(args);
+
+ Services.obs.notifyObservers(window, "page-info-dialog-reset");
+}
+
+function onUnloadPageInfo()
+{
+ // Remove the observer, only if there is at least 1 image.
+ if (gImageView.data.length != 0) {
+ Services.obs.removeObserver(imagePermissionObserver, "perm-changed");
+ }
+
+ /* Call registered overlay unload functions */
+ onUnloadRegistry.forEach(function(func) { func(); });
+}
+
+function doHelpButton()
+{
+ const helpTopics = {
+ "generalTab": "pageinfo_general",
+ "mediaTab": "pageinfo_media",
+ // "feedTab": "pageinfo_feed",
+ // "permTab": "pageinfo_permissions",
+ "formsTab": "pageinfo_forms",
+ "linksTab": "pageinfo_links",
+ "securityTab": "pageinfo_security"
+ };
+
+ var tabbox = document.getElementById("tabbox");
+ var helpdoc = helpTopics[tabbox.selectedTab.id] || "nav-page-info";
+ openHelp(helpdoc, "chrome://communicator/locale/help/suitehelp.rdf");
+}
+
+function showTab(id)
+{
+ var tabbox = document.getElementById("tabbox");
+ var selectedTab = document.getElementById(id) ||
+ document.getElementById(id + "Tab") || // Firefox compatibility sillyness
+ document.getElementById("generalTab");
+ tabbox.selectedTab = selectedTab;
+ selectedTab.focus();
+}
+
+function loadTab(args)
+{
+ // If the "View Image Info" context menu item was used, the related image
+ // element is provided as an argument. This can't be a background image.
+ let imageElement = args && args.imageElement;
+ let frameOuterWindowID = args && args.frameOuterWindowID;
+ let browser = args && args.browser;
+
+ /* Load the page info */
+ loadPageInfo(frameOuterWindowID, imageElement, browser);
+
+ /* Select the requested tab, if the name is specified */
+ var initialTab = (args && args.initialTab) || "generalTab";
+ showTab(initialTab);
+}
+
+function onClickMore()
+{
+ showTab("securityTab");
+}
+
+function openCacheEntry(key, cb)
+{
+ var checkCacheListener = {
+ onCacheEntryCheck: function(entry, appCache) {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ },
+ onCacheEntryAvailable: function(entry, isNew, appCache, status) {
+ cb(entry);
+ }
+ };
+ diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "",
+ Ci.nsICacheStorage.OPEN_READONLY,
+ checkCacheListener);
+}
+
+function makeGeneralTab(metaViewRows, docInfo)
+{
+ var title = (docInfo.title) ? docInfo.title : gBundle.getString("noPageTitle");
+ document.getElementById("titletext").value = title;
+
+ var url = docInfo.location.toString();
+ setItemValue("urltext", url);
+
+ var referrer = ("referrer" in docInfo && docInfo.referrer);
+ setItemValue("refertext", referrer);
+
+ var mode = ("compatMode" in docInfo && docInfo.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
+ document.getElementById("modetext").value = gBundle.getString(mode);
+
+ // find out the mime type
+ var mimeType = docInfo.contentType;
+ setItemValue("typetext", mimeType);
+
+ // get the document characterset
+ var encoding = docInfo.characterSet;
+ document.getElementById("encodingtext").value = encoding;
+
+ var length = metaViewRows.length;
+
+ var metaGroup = document.getElementById("metaTags");
+ if (!length) {
+ metaGroup.collapsed = true;
+ }
+ else {
+ var metaTagsCaption = document.getElementById("metaTagsCaption");
+ if (length == 1)
+ metaTagsCaption.label = gBundle.getString("generalMetaTag");
+ else
+ metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
+ var metaTree = document.getElementById("metatree");
+ metaTree.view = gMetaView;
+
+ // Add the metaViewRows onto the general tab's meta info tree.
+ gMetaView.addRows(metaViewRows);
+
+ metaGroup.collapsed = false;
+ }
+
+ // get the date of last modification
+ var modifiedText = formatDate(docInfo.lastModified, gStrings.notSet);
+ document.getElementById("modifiedtext").value = modifiedText;
+
+ // get cache info
+ var cacheKey = url.replace(/#.*$/, "");
+ openCacheEntry(cacheKey, function(cacheEntry) {
+ var sizeText;
+ if (cacheEntry) {
+ var pageSize = cacheEntry.dataSize;
+ var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
+ sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
+ }
+ setItemValue("sizetext", sizeText);
+ });
+}
+
+function ensureSelection(view)
+{
+ // only select something if nothing is currently selected
+ // and if there's anything to select
+ if (view.selection && view.selection.count == 0 && view.rowCount)
+ view.selection.select(0);
+}
+
+function addImage(imageViewRow)
+{
+ let [url, type, alt, elem, isBg] = imageViewRow;
+
+ if (!url)
+ return;
+
+ if (!gImageHash.hasOwnProperty(url))
+ gImageHash[url] = { };
+ if (!gImageHash[url].hasOwnProperty(type))
+ gImageHash[url][type] = { };
+ if (!gImageHash[url][type].hasOwnProperty(alt)) {
+ gImageHash[url][type][alt] = gImageView.data.length;
+ var row = [url, type, gStrings.unknown, alt, 1, elem, isBg, -1, null, null];
+ gImageView.addRow(row);
+
+ // Fill in cache data asynchronously
+ openCacheEntry(url, function(cacheEntry) {
+ if (cacheEntry) {
+ // Update the corresponding data entries from the cache.
+ var imageSize = cacheEntry.dataSize;
+ // If it is not -1 then replace with actual value, else keep as unknown.
+ if (imageSize && imageSize != -1) {
+ var kbSize = Math.round(imageSize / 1024 * 100) / 100;
+ row[2] = gBundle.getFormattedString("mediaFileSize",
+ [formatNumber(kbSize)]);
+ row[7] = imageSize;
+ }
+ row[8] = cacheEntry.persistent;
+ row[9] = getContentTypeFromHeaders(cacheEntry);
+ // Invalidate the row to trigger a repaint.
+ gImageView.tree.invalidateRow(gImageView.data.indexOf(row));
+ }
+ });
+
+ // Add the observer, only once.
+ if (gImageView.data.length == 1) {
+ Services.obs.addObserver(imagePermissionObserver, "perm-changed");
+ }
+ }
+ else {
+ var i = gImageHash[url][type][alt];
+ gImageView.data[i][COL_IMAGE_COUNT]++;
+ // The same image can occur several times on the page at different sizes.
+ // If the "View Image Info" context menu item was used, ensure we select
+ // the correct element.
+ if (!gImageView.data[i][COL_IMAGE_BG] &&
+ gImageElement && url == gImageElement.currentSrc &&
+ gImageElement.width == elem.width &&
+ gImageElement.height == elem.height &&
+ gImageElement.imageText == elem.imageText) {
+ gImageView.data[i][COL_IMAGE_NODE] = elem;
+ }
+ }
+}
+
+//******** Form Stuff
+function onFormSelect()
+{
+ if (gFormView.selection.count == 1)
+ {
+ var formPreview = document.getElementById("formpreview");
+ gFieldView.clear();
+ formPreview.view = gFieldView;
+
+ var clickedRow = gFormView.selection.currentIndex;
+ // form-node;
+ var form = gFormView.data[clickedRow][3];
+
+ var ft = null;
+ if (form.name)
+ ft = gBundle.getFormattedString("formTitle", [form.name]);
+
+ setItemValue("formenctype", form.encoding, gStrings.default);
+ setItemValue("formtarget", form.target, gStrings.formDefaultTarget);
+ document.getElementById("formname").value = ft || gStrings.formUntitled;
+
+ gFieldView.addRows(form.formfields);
+ }
+}
+
+//******** Link Stuff
+function onBeginLinkDrag(event,urlField,descField)
+{
+ if (event.originalTarget.localName != "treechildren")
+ return;
+
+ var tree = event.target;
+ if (!("treeBoxObject" in tree))
+ tree = tree.parentNode;
+
+ var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
+ if (row == -1)
+ return;
+
+ // Adding URL flavor
+ var col = tree.columns[urlField];
+ var url = tree.view.getCellText(row, col);
+ col = tree.columns[descField];
+ var desc = tree.view.getCellText(row, col);
+
+ var dataTransfer = event.dataTransfer;
+ dataTransfer.setData("text/x-moz-url", url + "\n" + desc);
+ dataTransfer.setData("text/url-list", url);
+ dataTransfer.setData("text/plain", url);
+}
+
+//******** Image Stuff
+function getSelectedRows(tree) {
+ var start = { };
+ var end = { };
+ var numRanges = tree.view.selection.getRangeCount();
+
+ var rowArray = [ ];
+ for (var t = 0; t < numRanges; t++) {
+ tree.view.selection.getRangeAt(t, start, end);
+ for (var v = start.value; v <= end.value; v++)
+ rowArray.push(v);
+ }
+
+ return rowArray;
+}
+
+function getSelectedRow(tree) {
+ var rows = getSelectedRows(tree);
+ return (rows.length == 1) ? rows[0] : -1;
+}
+
+function selectSaveFolder(aCallback) {
+ return selectSaveFolderTask(aCallback).catch(Cu.reportError);
+}
+
+async function selectSaveFolderTask(aCallback) {
+ let titleText = gBundle.getString("mediaSelectFolder");
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+
+ fp.init(window, titleText, Ci.nsIFilePicker.modeGetFolder);
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+ try {
+ let initialDir = Services.prefs.getComplexValue("browser.download.dir",
+ Ci.nsIFile);
+ if (!initialDir) {
+ let downloadsDir = await Downloads.getSystemDownloadsDirectory();
+ initialDir = new FileUtils.File(downloadsDir);
+ }
+
+ fp.displayDirectory = initialDir;
+ } catch (ex) {
+ }
+
+ let result = await new Promise(resolve => fp.open(resolve));
+
+ if (result == Ci.nsIFilePicker.returnOK) {
+ aCallback(fp.file.QueryInterface(Ci.nsIFile));
+ } else {
+ aCallback(null);
+ }
+}
+
+function saveMedia()
+{
+ var tree = document.getElementById("imagetree");
+ var rowArray = getSelectedRows(tree);
+ if (rowArray.length == 1) {
+ let row = rowArray[0];
+ let item = gImageView.data[row][COL_IMAGE_NODE];
+ let url = gImageView.data[row][COL_IMAGE_ADDRESS];
+
+ if (url) {
+ let titleKey = "SaveImageTitle";
+
+ if (item instanceof HTMLVideoElement)
+ titleKey = "SaveVideoTitle";
+ else if (item instanceof HTMLAudioElement)
+ titleKey = "SaveAudioTitle";
+
+ saveURL(url, null, titleKey, false, false, makeURI(item.baseURI),
+ null, gDocInfo.isContentWindowPrivate,
+ gDocument.nodePrincipal);
+ }
+ } else {
+ selectSaveFolder(function(aDirectory) {
+ if (aDirectory) {
+ var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
+ uniqueFile(aChosenData.file);
+ internalSave(aURIString, null, null, null, null, false,
+ "SaveImageTitle", aChosenData, aBaseURI, null, false,
+ null, gDocInfo.isContentWindowPrivate,
+ gDocument.nodePrincipal);
+ };
+
+ for (var i = 0; i < rowArray.length; i++) {
+ let v = rowArray[i];
+ let dir = aDirectory.clone();
+ let item = gImageView.data[v][COL_IMAGE_NODE];
+ let uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
+ let uri = makeURI(uriString);
+
+ try {
+ uri.QueryInterface(Ci.nsIURL);
+ dir.append(decodeURIComponent(uri.fileName));
+ } catch (ex) {
+ // data:/blob: uris
+ // Supply a dummy filename, otherwise Download Manager
+ // will try to delete the base directory on failure.
+ dir.append(gImageView.data[v][COL_IMAGE_TYPE]);
+ }
+
+ if (i == 0) {
+ saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
+ } else {
+ // This delay is a hack which prevents the download manager
+ // from opening many times. See bug 377339.
+ setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
+ makeURI(item.baseURI));
+ }
+ }
+ }
+ });
+ }
+}
+
+function onBlockImage(aChecked)
+{
+ var uri = makeURI(document.getElementById("imageurltext").value);
+ if (aChecked)
+ Services.perms.add(uri, "image", Services.perms.DENY_ACTION);
+ else
+ Services.perms.remove(uri, "image");
+}
+
+function onImageSelect()
+{
+ var previewBox = document.getElementById("mediaPreviewBox");
+ var mediaSaveBox = document.getElementById("mediaSaveBox");
+ var mediaSaveButton = document.getElementById("imagesaveasbutton");
+ var splitter = document.getElementById("mediaSplitter");
+ var tree = document.getElementById("imagetree");
+ var count = tree.view.selection.count;
+
+ if (count == 0)
+ {
+ previewBox.collapsed = true;
+ mediaSaveBox.collapsed = true;
+ mediaSaveButton.disabled = true;
+ splitter.collapsed = true;
+ tree.flex = 1;
+ }
+ else if (count > 1)
+ {
+ previewBox.collapsed = true;
+ mediaSaveBox.collapsed = false;
+ mediaSaveButton.disabled = false;
+ splitter.collapsed = true;
+ tree.flex = 1;
+ }
+ else
+ {
+ previewBox.collapsed = false;
+ mediaSaveBox.collapsed = true;
+ mediaSaveButton.disabled = false;
+ splitter.collapsed = false;
+ tree.flex = 0;
+ makePreview(tree.view.selection.currentIndex);
+ }
+}
+
+// Makes the media preview (image, video, etc) for the selected row on
+// the media tab.
+function makePreview(row)
+{
+ var [url, type, sizeText, alt, count, item, isBG, imageSize, persistent, cachedType] = gImageView.data[row];
+ var isAudio = false;
+
+ setItemValue("imageurltext", url);
+ setItemValue("imagetext", item.imageText);
+ setItemValue("imagelongdesctext", item.longDesc);
+
+ // get cache info
+ var sourceText;
+ switch (persistent) {
+ case true:
+ sourceText = gBundle.getString("generalDiskCache");
+ break;
+ case false:
+ sourceText = gBundle.getString("generalMemoryCache");
+ break;
+ default:
+ sourceText = gBundle.getString("generalNotCached");
+ break;
+ }
+ setItemValue("imagesourcetext", sourceText);
+
+ // find out the file size
+ var sizeText;
+ if (imageSize && imageSize != -1) {
+ var kbSize = Math.round(imageSize / 1024 * 100) / 100;
+ sizeText = gBundle.getFormattedString("generalSize",
+ [formatNumber(kbSize),
+ formatNumber(imageSize)]);
+ }
+ else
+ sizeText = gBundle.getString("mediaUnknownNotCached");
+ setItemValue("imagesizetext", sizeText);
+
+ var mimeType = item.mimeType || cachedType;
+ var numFrames = item.numFrames;
+
+ var imageType;
+ if (mimeType) {
+ // We found the type, try to display it nicely
+ let imageMimeType = /^image\/(.*)/i.exec(mimeType);
+ if (imageMimeType) {
+ imageType = imageMimeType[1].toUpperCase();
+ if (numFrames > 1)
+ imageType = gBundle.getFormattedString("mediaAnimatedImageType",
+ [imageType, numFrames]);
+ else
+ imageType = gBundle.getFormattedString("mediaImageType", [imageType]);
+ }
+ else {
+ // the MIME type doesn't begin with image/, display the raw type
+ imageType = mimeType;
+ }
+ }
+ else {
+ // We couldn't find the type, fall back to the value in the treeview
+ imageType = type;
+ }
+
+ setItemValue("imagetypetext", imageType);
+
+ var imageContainer = document.getElementById("theimagecontainer");
+ var oldImage = document.getElementById("thepreviewimage");
+
+ var isProtocolAllowed = checkProtocol(gImageView.data[row]);
+ var isImageType = mimeType && mimeType.startsWith("image/");
+
+ var newImage = new Image;
+ newImage.id = "thepreviewimage";
+ var physWidth = 0, physHeight = 0;
+ var width = 0, height = 0;
+
+ if ((item.HTMLLinkElement || item.HTMLInputElement ||
+ item.HTMLImageElement || item.SVGImageElement ||
+ (item.HTMLObjectElement && isImageType) ||
+ (item.HTMLEmbedElement && isImageType) ||
+ isBG) && isProtocolAllowed) {
+ // We need to wait for the image to finish loading before
+ // using width & height.
+ newImage.addEventListener("loadend", function() {
+ physWidth = newImage.width || 0;
+ physHeight = newImage.height || 0;
+
+ // "width" and "height" attributes must be set to newImage,
+ // even if there is no "width" or "height attribute in item;
+ // otherwise, the preview image cannot be displayed correctly.
+ // Since the image might have been loaded out-of-process, we expect
+ // the item to tell us its width / height dimensions. Failing that
+ // the item should tell us the natural dimensions of the image. Finally
+ // failing that, we'll assume that the image was never loaded in the
+ // other process (this can be true for favicons, for example), and so
+ // we'll assume that we can use the natural dimensions of the newImage
+ // we just created. If the natural dimensions of newImage are not known
+ // then the image is probably broken.
+ if (!isBG) {
+ newImage.width = ("width" in item && item.width) ||
+ newImage.naturalWidth;
+ newImage.height = ("height" in item && item.height) ||
+ newImage.naturalHeight;
+ }
+ else {
+ // The width and height of an HTML tag should not be used for its
+ // background image (for example, "table" can have "width" or "height"
+ // attributes).
+ newImage.width = item.naturalWidth || newImage.naturalWidth;
+ newImage.height = item.naturalHeight || newImage.naturalHeight;
+ }
+
+ if (item.SVGImageElement) {
+ newImage.width = item.SVGImageElementWidth;
+ newImage.height = item.SVGImageElementHeight;
+ }
+
+ width = newImage.width;
+ height = newImage.height;
+
+ document.getElementById("theimagecontainer").collapsed = false
+ document.getElementById("brokenimagecontainer").collapsed = true;
+
+ let imageSize = "";
+ if (url) {
+ if (width != physWidth || height != physHeight) {
+ imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
+ [formatNumber(physWidth),
+ formatNumber(physHeight),
+ formatNumber(width),
+ formatNumber(height)]);
+ } else {
+ imageSize = gBundle.getFormattedString("mediaDimensions",
+ [formatNumber(width),
+ formatNumber(height)]);
+ }
+ }
+ setItemValue("imagedimensiontext", imageSize);
+ }, {once: true});
+
+ newImage.setAttribute("src", url);
+ }
+ else {
+ // Handle the case where newImage is not used for width & height.
+ if (item.HTMLVideoElement && isProtocolAllowed) {
+ newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
+ newImage.id = "thepreviewimage";
+ newImage.src = url;
+ newImage.controls = true;
+ width = physWidth = item.videoWidth;
+ height = physHeight = item.videoHeight;
+
+ document.getElementById("theimagecontainer").collapsed = false
+ document.getElementById("brokenimagecontainer").collapsed = true;
+ }
+ else if (item.HTMLAudioElement && isProtocolAllowed) {
+ newImage = new Audio;
+ newImage.id = "thepreviewimage";
+ newImage.src = url;
+ newImage.controls = true;
+ newImage.preload = "metadata";
+ isAudio = true;
+
+ document.getElementById("theimagecontainer").collapsed = false
+ document.getElementById("brokenimagecontainer").collapsed = true;
+ }
+ else {
+ // fallback image for protocols not allowed (e.g., javascript:)
+ // or elements not [yet] handled (e.g., object, embed).
+ document.getElementById("brokenimagecontainer").collapsed = false;
+ document.getElementById("theimagecontainer").collapsed = true;
+ }
+
+ let imageSize = "";
+ if (url && !isAudio) {
+ imageSize = gBundle.getFormattedString("mediaDimensions",
+ [formatNumber(width),
+ formatNumber(height)]);
+ }
+ setItemValue("imagedimensiontext", imageSize);
+ }
+
+ makeBlockImage(url);
+
+ oldImage.remove();
+ imageContainer.appendChild(newImage);
+}
+
+function makeBlockImage(url)
+{
+ var checkbox = document.getElementById("blockImage");
+ var imagePref = Services.prefs.getIntPref("permissions.default.image");
+ if (!(/^https?:/.test(url)) || imagePref == 2)
+ // We can't block the images from this host because either is is not
+ // for http(s) or we don't load images at all
+ checkbox.hidden = true;
+ else {
+ var uri = makeURI(url);
+ if (uri.host) {
+ checkbox.hidden = false;
+ checkbox.label = gBundle.getFormattedString("mediaBlockImage", [uri.host]);
+ var perm = Services.perms.testPermission(uri, "image");
+ checkbox.checked = perm == Services.perms.DENY_ACTION;
+ }
+ else
+ checkbox.hidden = true;
+ }
+}
+
+var imagePermissionObserver = {
+ observe: function (aSubject, aTopic, aData)
+ {
+ if (document.getElementById("mediaPreviewBox").collapsed)
+ return;
+
+ if (aTopic == "perm-changed") {
+ var permission = aSubject.QueryInterface(Ci.nsIPermission);
+ if (permission.type == "image") {
+ var imageTree = document.getElementById("imagetree");
+ var row = imageTree.currentIndex;
+ var item = gImageView.data[row][COL_IMAGE_NODE];
+ var url = gImageView.data[row][COL_IMAGE_ADDRESS];
+ if (permission.matchesURI(makeURI(url), true))
+ makeBlockImage(url);
+ }
+ }
+ }
+}
+
+function getContentTypeFromHeaders(cacheEntryDescriptor)
+{
+ if (!cacheEntryDescriptor)
+ return null;
+
+ let headers = cacheEntryDescriptor.getMetaDataElement("response-head");
+ let type = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
+ return type && type[1];
+}
+
+function setItemValue(id, value, defaultString = gStrings.notSet)
+{
+ var item = document.getElementById(id);
+ if (value) {
+ item.disabled = false;
+ item.value = value;
+ }
+ else
+ {
+ item.value = defaultString;
+ item.disabled = true;
+ }
+}
+
+function formatNumber(number)
+{
+ return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString()
+}
+
+function formatDate(datestr, unknown)
+{
+ var date = new Date(datestr);
+ if (!date.valueOf())
+ return unknown;
+
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "full", timeStyle: "long"});
+ return dateTimeFormatter.format(date);
+}
+
+function getSelectedItems(linksMode)
+{
+ // linksMode is a boolean that is used to determine
+ // whether the getSelectedItems() function needs to
+ // run with urlSecurityCheck() or not.
+
+ var elem = document.commandDispatcher.focusedElement;
+
+ var view = elem.view;
+ var selection = view.selection;
+ var text = [], tmp = '';
+ var min = {}, max = {};
+
+ var count = selection.getRangeCount();
+
+ for (var i = 0; i < count; i++) {
+ selection.getRangeAt(i, min, max);
+
+ for (var row = min.value; row <= max.value; row++) {
+ tmp = view.getCellValue(row, null);
+ if (tmp)
+ {
+ try {
+ if (linksMode)
+ urlSecurityCheck(tmp, gDocInfo.principal);
+ text.push(tmp);
+ }
+ catch (e) {
+ }
+ }
+ }
+ }
+
+ return text;
+}
+
+function doCopy(isLinkMode)
+{
+ var text = getSelectedItems(isLinkMode);
+
+ Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(text.join("\n"));
+}
+
+function doSelectAllMedia()
+{
+ var tree = document.getElementById("imagetree");
+
+ if (tree)
+ tree.view.selection.selectAll();
+}
+
+function doSelectAll()
+{
+ var elem = document.commandDispatcher.focusedElement;
+
+ if (elem && "treeBoxObject" in elem)
+ elem.view.selection.selectAll();
+}
+
+function selectImage() {
+ if (!gImageElement)
+ return;
+
+ var tree = document.getElementById("imagetree");
+ for (var i = 0; i < tree.view.rowCount; i++) {
+ // If the image row element is the image selected from
+ // the "View Image Info" context menu item.
+ let image = gImageView.data[i][COL_IMAGE_NODE];
+ if (!gImageView.data[i][COL_IMAGE_BG] &&
+ gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
+ gImageElement.width == image.width &&
+ gImageElement.height == image.height &&
+ gImageElement.imageText == image.imageText) {
+ tree.view.selection.select(i);
+ tree.treeBoxObject.ensureRowIsVisible(i);
+ tree.focus();
+ return;
+ }
+ }
+}
+
+function checkProtocol(img)
+{
+ var url = img[COL_IMAGE_ADDRESS];
+ return /^data:image\//i.test(url) ||
+ /^(https?|ftp|file|about|chrome|resource):/.test(url);
+}
+
+function onOpenIn(mode)
+{
+ var linkList = getSelectedItems(true);
+
+ if (linkList.length)
+ openUILinkArrayIn(linkList, mode);
+}
diff --git a/comm/suite/browser/pageinfo/pageInfo.xul b/comm/suite/browser/pageinfo/pageInfo.xul
new file mode 100644
index 0000000000..8f4dc2ae96
--- /dev/null
+++ b/comm/suite/browser/pageinfo/pageInfo.xul
@@ -0,0 +1,531 @@
+<?xml version="1.0"?><!-- -*- Mode: nXML; indent-tabs-mode: nil -*- -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://navigator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://navigator/content/pageinfo/pageInfo.css" type="text/css"?>
+<?xml-stylesheet href="chrome://navigator/skin/pageInfo.css" type="text/css"?>
+
+<!DOCTYPE window [
+ <!ENTITY % pageInfoDTD SYSTEM "chrome://navigator/locale/pageInfo.dtd">
+ %pageInfoDTD;
+]>
+
+<window id="main-window"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="Browser:page-info"
+ onload="onLoadPageInfo()"
+ onunload="onUnloadPageInfo()"
+ align="stretch" style="&pageInfoWindow.dimensions;"
+ persist="screenX screenY width height sizemode">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+ <script src="chrome://global/content/treeUtils.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://navigator/content/pageinfo/pageInfo.js"/>
+ <script src="chrome://navigator/content/pageinfo/feeds.js"/>
+ <script src="chrome://navigator/content/pageinfo/permissions.js"/>
+ <script src="chrome://navigator/content/pageinfo/security.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+ <script src="chrome://communicator/content/tasksOverlay.js"/>
+
+ <stringbundleset id="pageinfobundleset">
+ <stringbundle id="pageinfobundle" src="chrome://navigator/locale/pageInfo.properties"/>
+ <stringbundle id="pkiBundle" src="chrome://pippki/locale/pippki.properties"/>
+ </stringbundleset>
+
+ <commandset id="pageInfoCommandSet">
+ <command id="cmd_close" oncommand="window.close();"/>
+ <command id="cmd_help" oncommand="doHelpButton();"/>
+ <command id="cmd_copy" oncommand="doCopy(false);"/>
+ <command id="cmd_selectall" oncommand="doSelectAll();"/>
+
+ <!-- links tab -->
+ <command id="cmd_openInNewTab" oncommand="onOpenIn('tab');"/>
+ <command id="cmd_openInNewWindow" oncommand="onOpenIn('window');"/>
+ <command id="cmd_copyLinks" oncommand="doCopy(true);"/>
+ </commandset>
+
+ <keyset>
+ <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/>
+ <key keycode="VK_ESCAPE" command="cmd_close"/>
+ <key key="." modifiers="meta" command="cmd_close"/>
+ <key keycode="VK_F1" command="cmd_help"/>
+ <key key="&openHelpMac.key;" modifiers="meta" command="cmd_help"/>
+ <key key="&copy.key;" modifiers="accel" command="cmd_copy"/>
+ <key key="&selectall.key;" modifiers="accel" command="cmd_selectall"/>
+ <key key="&selectall.key;" modifiers="alt" command="cmd_selectall"/>
+ </keyset>
+
+ <menupopup id="picontext">
+ <menuitem id="menu_selectall" label="&selectall.label;" command="cmd_selectall" accesskey="&selectall.accesskey;"/>
+ <menuitem id="menu_copy" label="&copy.label;" command="cmd_copy" accesskey="&copy.accesskey;"/>
+ </menupopup>
+
+ <menupopup id="piLinksContext">
+ <menuitem id="menu_openInNewTab"
+ label="&openInNewTab.label;"
+ command="cmd_openInNewTab"
+ accesskey="&openInNewTab.accesskey;"/>
+ <menuitem id="menu_openInNewWindow"
+ label="&openInNewWindow.label;"
+ command="cmd_openInNewWindow"
+ accesskey="&openInNewWindow.accesskey;"/>
+ <menuitem id="menu_selectall_links"
+ label="&selectall.label;"
+ command="cmd_selectall"
+ accesskey="&selectall.accesskey;"/>
+ <menuitem id="menu_copyLinks"
+ label="&copyLinks.label;"
+ command="cmd_copyLinks"
+ accesskey="&copyLinks.accesskey;"/>
+ </menupopup>
+
+ <tabbox id="tabbox" flex="1">
+ <vbox id="dragbox">
+ <tabs id="tabs"
+ onselect="[gImageView, gFormView, gLinkView].forEach(ensureSelection);">
+ <tab id="generalTab"
+ label="&generalTab;"
+ accesskey="&generalTab.accesskey;"/>
+ <tab id="mediaTab"
+ label="&mediaTab;"
+ accesskey="&mediaTab.accesskey;"/>
+ <tab id="feedTab"
+ label="&feedTab;"
+ accesskey="&feedTab.accesskey;"/>
+ <tab id="permTab"
+ label="&permTab;"
+ accesskey="&permTab.accesskey;"/>
+ <tab id="formsTab"
+ label="&formsTab;"
+ accesskey="&formsTab.accesskey;"/>
+ <tab id="linksTab"
+ label="&linksTab;"
+ accesskey="&linksTab.accesskey;"/>
+ <tab id="securityTab"
+ label="&securityTab;"
+ accesskey="&securityTab.accesskey;"/>
+ <!-- Others added by overlay -->
+ </tabs>
+ </vbox>
+
+ <tabpanels id="mainDeck" flex="1">
+ <!-- General page information -->
+ <vbox id="generalPanel">
+ <grid>
+ <columns>
+ <column/>
+ <column class="gridSeparator"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="generalTitle">
+ <label control="titletext" value="&generalTitle;"/>
+ <separator/>
+ <textbox readonly="true" id="titletext"/>
+ </row>
+ <row>
+ <label control="urltext" value="&generalURL;"/>
+ <separator/>
+ <textbox readonly="true" id="urltext" class="urltext"/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ </row>
+ <row>
+ <label control="typetext" value="&generalType;"/>
+ <separator/>
+ <textbox readonly="true" id="typetext"/>
+ </row>
+ <row>
+ <label control="modetext" value="&generalMode;"/>
+ <separator/>
+ <textbox readonly="true" crop="end" id="modetext"/>
+ </row>
+ <row>
+ <label control="encodingtext" value="&generalEncoding2;"/>
+ <separator/>
+ <textbox readonly="true" id="encodingtext"/>
+ </row>
+ <row>
+ <label control="sizetext" value="&generalSize;"/>
+ <separator/>
+ <textbox readonly="true" id="sizetext"/>
+ </row>
+ <row>
+ <label control="refertext" value="&generalReferrer;"/>
+ <separator/>
+ <textbox readonly="true" id="refertext" class="urltext"/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ </row>
+ <row>
+ <label control="modifiedtext" value="&generalModified;"/>
+ <separator/>
+ <textbox readonly="true" id="modifiedtext"/>
+ </row>
+ </rows>
+ </grid>
+ <separator class="thin"/>
+ <groupbox id="metaTags" flex="1">
+ <caption id="metaTagsCaption"/>
+ <tree id="metatree" flex="1" hidecolumnpicker="true" contextmenu="picontext">
+ <treecols>
+ <treecol id="meta-name" label="&generalMetaName;"
+ persist="width" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="meta-content" label="&generalMetaContent;"
+ persist="width" flex="4"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ </groupbox>
+ <groupbox id="securityBox">
+ <caption id="securityBoxCaption" label="&securityHeader;"/>
+ <description id="general-security-identity" class="indent header"/>
+ <description id="general-security-privacy" class="indent header"/>
+ <hbox align="right">
+ <button id="security-view-details" label="&generalSecurityDetails;"
+ accesskey="&generalSecurityDetails.accesskey;"
+ oncommand="onClickMore();"/>
+ </hbox>
+ </groupbox>
+ </vbox>
+
+ <!-- Media information -->
+ <vbox id="mediaPanel">
+ <tree id="imagetree" onselect="onImageSelect();" contextmenu="picontext"
+ ondragstart="onBeginLinkDrag(event,'image-address','image-alt')">
+ <treecols>
+ <treecol sortSeparators="true" primary="true" persist="width" flex="10"
+ width="10" id="image-address" label="&mediaAddress;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="2"
+ width="2" id="image-type" label="&mediaType;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="2"
+ width="2" id="image-size" label="&mediaSize;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="4"
+ width="4" id="image-alt" label="&mediaAltHeader;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="1"
+ width="1" id="image-count" label="&mediaCount;"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ <splitter orient="vertical" id="mediaSplitter"/>
+ <vbox flex="1" id="mediaPreviewBox">
+ <grid id="mediaGrid">
+ <columns>
+ <column id="mediaLabelColumn"/>
+ <column class="gridSeparator"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <label control="imageurltext" value="&mediaLocation;"/>
+ <separator/>
+ <textbox readonly="true" id="imageurltext" class="urltext"/>
+ </row>
+ <row>
+ <label control="imagetypetext" value="&generalType;"/>
+ <separator/>
+ <textbox readonly="true" id="imagetypetext"/>
+ </row>
+ <row>
+ <label control="imagesourcetext" value="&generalSource;"/>
+ <separator/>
+ <textbox readonly="true" id="imagesourcetext"/>
+ </row>
+ <row>
+ <label control="imagesizetext" value="&generalSize;"/>
+ <separator/>
+ <textbox readonly="true" id="imagesizetext"/>
+ </row>
+ <row>
+ <label control="imagedimensiontext" value="&mediaDimension;"/>
+ <separator/>
+ <textbox readonly="true" id="imagedimensiontext"/>
+ </row>
+ <row>
+ <label control="imagetext" value="&mediaText;"/>
+ <separator/>
+ <textbox readonly="true" id="imagetext"/>
+ </row>
+ <row>
+ <label control="imagelongdesctext" value="&mediaLongdesc;"/>
+ <separator/>
+ <textbox readonly="true" id="imagelongdesctext"/>
+ </row>
+ </rows>
+ </grid>
+ <hbox align="end">
+ <vbox>
+ <checkbox id="blockImage"
+ hidden="true"
+ oncommand="onBlockImage(this.checked);"
+ accesskey="&mediaBlockImage.accesskey;"/>
+ <label control="thepreviewimage" value="&mediaPreview;" class="header"/>
+ </vbox>
+ <spacer flex="1"/>
+ <button label="&selectall.label;" accesskey="&selectall.accesskey;"
+ id="selectallbutton"
+ oncommand="doSelectAllMedia();"/>
+ <button label="&mediaSaveAs;" accesskey="&mediaSaveAs.accesskey;"
+ icon="save" id="imagesaveasbutton" disabled="true"
+ oncommand="saveMedia();"/>
+ </hbox>
+ <vbox class="inset iframe" flex="1" pack="center">
+ <hbox id="theimagecontainer" pack="center">
+ <image id="thepreviewimage"/>
+ </hbox>
+ <hbox id="brokenimagecontainer" pack="center" collapsed="true">
+ <image id="brokenimage" src="resource://gre-resources/broken-image.png"/>
+ </hbox>
+ </vbox>
+ </vbox>
+ <hbox id="mediaSaveBox" collapsed="true">
+ <spacer flex="1"/>
+ <button label="&mediaSaveAs;" accesskey="&mediaSaveAs2.accesskey;"
+ icon="save" oncommand="saveMedia();"/>
+ </hbox>
+ </vbox>
+
+ <!-- Feeds -->
+ <vbox id="feedPanel">
+ <richlistbox id="feedListbox" flex="1"/>
+ </vbox>
+
+ <!-- Permissions -->
+ <vbox id="permPanel">
+ <hbox>
+ <label control="hostText" value="&permissionsFor;"/>
+ <textbox id="hostText" class="header" readonly="true"
+ crop="end" flex="1"/>
+ </hbox>
+
+ <!--
+ The richlist below is generated by permissions.js.
+ The labels point to the radio groups to give the radio buttons
+ an accessible context. The accessible context for the preceeding
+ checkbox is already taken care of through the richlistitem grouping.
+ -->
+ <richlistbox id="permList" flex="1"/>
+ </vbox>
+
+ <!-- Form information -->
+ <vbox>
+ <tree id="formtree" class="fixedsize" onselect="onFormSelect();" contextmenu="picontext">
+ <treecols>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" primary="true" persist="width" flex="1"
+ width="1" id="form-name" label="&formName;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="3"
+ width="3" id="form-method" label="&formMethod;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="2"
+ width="2" id="form-action" label="&formAction;"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ <splitter orient="vertical"/>
+ <vbox flex="1">
+ <textbox readonly="true" class="header" id="formname"/>
+ <grid>
+ <columns>
+ <column/>
+ <column style="width: .5em;"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <label control="formenctype" value="&formEncoding;"/>
+ <separator/>
+ <textbox readonly="true" id="formenctype"/>
+ </row>
+ <row>
+ <label control="formtarget" value="&formTarget;"/>
+ <separator/>
+ <textbox readonly="true" class="label" id="formtarget"/>
+ </row>
+ </rows>
+ </grid>
+ <label control="formpreview" class="header" value="&formFields;"/>
+ <tree id="formpreview" flex="1" contextmenu="picontext">
+ <treecols>
+ <treecol sortSeparators="true" primary="true" persist="width" flex="3"
+ width="3" id="field-label" label="&formLabel;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="3"
+ width="3" id="field-field" label="&formFName;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="1"
+ width="1" id="field-type" label="&formType;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="3"
+ width="3" id="field-value" label="&formCValue;"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ </vbox>
+ </vbox>
+
+ <!-- Link info -->
+ <vbox>
+ <tree id="linktree"
+ flex="1"
+ ondragstart="onBeginLinkDrag(event,'link-address','link-name')"
+ contextmenu="piLinksContext">
+ <treecols>
+ <treecol sortSeparators="true" primary="true" persist="width" flex="5"
+ width="5" id="link-name" label="&linkName;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="7"
+ width="7" id="link-address" label="&linkAddress;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="2"
+ width="2" id="link-type" label="&linkType;"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="2"
+ width="2" id="link-target" label="&linkTarget;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol sortSeparators="true" persist="hidden width" flex="1"
+ width="1" id="link-accesskey" label="&linkAccessKey;" hidden="true"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ </vbox>
+
+ <!-- Security & Privacy -->
+ <vbox id="securityPanel">
+ <!-- Identity Section -->
+ <groupbox id="security-identity-groupbox" flex="1">
+ <caption id="security-identity" label="&securityView.identity.header;"/>
+ <hbox>
+ <image id="identity-icon"/>
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row><!-- Domain -->
+ <label control="security-identity-domain-value"
+ id="security-identity-domain-label"
+ class="fieldLabel"
+ value="&securityView.identity.domain;"/>
+ <textbox id="security-identity-domain-value"
+ class="fieldValue" readonly="true"/>
+ </row>
+ <row><!-- Owner -->
+ <label control="security-identity-owner-value"
+ id="security-identity-owner-label"
+ class="fieldLabel"
+ value="&securityView.identity.owner;"/>
+ <textbox id="security-identity-owner-value"
+ class="fieldValue" readonly="true"/>
+ </row>
+ <row><!-- Verifier -->
+ <label control="security-identity-verifier-value"
+ id="security-identity-verifier-label"
+ class="fieldLabel"
+ value="&securityView.identity.verifier;"/>
+ <textbox id="security-identity-verifier-value"
+ class="fieldValue" readonly="true"/>
+ </row>
+ <!-- Certificate Validity -->
+ <row id="security-identity-validity-row">
+ <label id="security-identity-validity-label"
+ class="fieldLabel"
+ value="&securityView.identity.validity;"
+ control="security-identity-validity-value"/>
+ <textbox id="security-identity-validity-value"
+ class="fieldValue" readonly="true" />
+ </row>
+ </rows>
+ </grid>
+ </hbox>
+ <spacer flex="1"/>
+ <hbox pack="end"><!-- Cert button -->
+ <button id="security-view-cert" label="&securityView.certView;"
+ accesskey="&securityView.accesskey;"
+ oncommand="security.viewCert();"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Privacy & History section -->
+ <groupbox id="security-privacy-groupbox" flex="1">
+ <caption id="security-privacy" label="&securityView.privacy.header;" />
+ <grid>
+ <columns>
+ <column flex="1"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center"><!-- History -->
+ <label id="security-privacy-history-label"
+ control="security-privacy-history-value"
+ class="fieldLabel">&securityView.privacy.history;</label>
+ <textbox id="security-privacy-history-value"
+ class="fieldValue"
+ value="&securityView.unknown;"
+ readonly="true"/>
+ </row>
+ <row align="center"><!-- Cookies -->
+ <label id="security-privacy-cookies-label"
+ control="security-privacy-cookies-value"
+ class="fieldLabel">&securityView.privacy.cookies;</label>
+ <hbox align="center">
+ <textbox id="security-privacy-cookies-value"
+ class="fieldValue"
+ value="&securityView.unknown;"
+ flex="1"
+ readonly="true"/>
+ <button id="security-view-cookies"
+ label="&securityView.privacy.viewCookies;"
+ accesskey="&securityView.privacy.viewCookies.accessKey;"
+ oncommand="security.viewCookies();"/>
+ </hbox>
+ </row>
+ <row align="center"><!-- Passwords -->
+ <label id="security-privacy-passwords-label"
+ control="security-privacy-passwords-value"
+ class="fieldLabel">&securityView.privacy.passwords;</label>
+ <hbox align="center">
+ <textbox id="security-privacy-passwords-value"
+ class="fieldValue"
+ value="&securityView.unknown;"
+ flex="1"
+ readonly="true"/>
+ <button id="security-view-password"
+ label="&securityView.privacy.viewPasswords;"
+ accesskey="&securityView.privacy.viewPasswords.accessKey;"
+ oncommand="security.viewPasswords();"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- Technical Details section -->
+ <groupbox id="security-technical-groupbox" flex="1">
+ <caption id="security-technical" label="&securityView.technical.header;" />
+ <vbox flex="1">
+ <label id="security-technical-shortform" class="fieldValue"/>
+ <description id="security-technical-longform1" class="fieldLabel"/>
+ <description id="security-technical-longform2" class="fieldLabel"/>
+ <description id="security-technical-certificate-transparency" class="fieldLabel"/>
+ </vbox>
+ </groupbox>
+ </vbox>
+
+ <!-- Others added by overlay -->
+ </tabpanels>
+ </tabbox>
+</window>
diff --git a/comm/suite/browser/pageinfo/permissions.js b/comm/suite/browser/pageinfo/permissions.js
new file mode 100644
index 0000000000..7d126d6ea7
--- /dev/null
+++ b/comm/suite/browser/pageinfo/permissions.js
@@ -0,0 +1,204 @@
+/* -*- Mode: Javascript; 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/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+const { SitePermissions } = ChromeUtils.import("resource:///modules/SitePermissions.jsm");
+const { BrowserUtils } = ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm"");
+
+var gPermPrincipal;
+
+// Array of permissionIDs sorted alphabetically by label.
+var gPermissions = SitePermissions.listPermissions().sort((a, b) => {
+ let firstLabel = SitePermissions.getPermissionLabel(a);
+ let secondLabel = SitePermissions.getPermissionLabel(b);
+ return firstLabel.localeCompare(secondLabel);
+});
+
+var permissionObserver = {
+ observe: function (aSubject, aTopic, aData)
+ {
+ if (aTopic == "perm-changed") {
+ var permission = aSubject.QueryInterface(Ci.nsIPermission);
+ if (permission.matches(gPermPrincipal, true) &&
+ gPermissions.includes(permission.type)) {
+ initRow(permission.type);
+ }
+ }
+ }
+};
+
+function initPermission()
+{
+ onUnloadRegistry.push(onUnloadPermission);
+ onResetRegistry.push(onUnloadPermission);
+}
+
+function onLoadPermission(uri, principal)
+{
+ var permTab = document.getElementById("permTab");
+ if (!SitePermissions.isSupportedPrincipal(principal)) {
+ permTab.hidden = true;
+ return;
+ }
+
+ gPermPrincipal = principal;
+ if (gPermPrincipal && !gPermPrincipal.isSystemPrincipal) {
+ var hostText = document.getElementById("hostText");
+ hostText.value = gPermPrincipal.origin;
+ Services.obs.addObserver(permissionObserver, "perm-changed");
+ }
+ for (var i of gPermissions)
+ initRow(i);
+ permTab.hidden = false;
+}
+
+function onUnloadPermission()
+{
+ if (gPermPrincipal && !gPermPrincipal.isSystemPrincipal) {
+ Services.obs.removeObserver(permissionObserver, "perm-changed");
+ }
+}
+
+function initRow(aPartId)
+{
+ createRow(aPartId);
+
+ var checkbox = document.getElementById(aPartId + "Def");
+ var command = document.getElementById("cmd_" + aPartId + "Toggle");
+ if (gPermPrincipal && gPermPrincipal.isSystemPrincipal) {
+ checkbox.checked = false;
+ checkbox.setAttribute("disabled", "true");
+ command.setAttribute("disabled", "true");
+ document.getElementById(aPartId + "RadioGroup").selectedItem = null;
+ return;
+ }
+ checkbox.removeAttribute("disabled");
+ var {state} = SitePermissions.getForPrincipal(gPermPrincipal, aPartId);
+ let defaultState = SitePermissions.getDefault(aPartId);
+
+ // Since cookies preferences have many different possible configuration states
+ // we don't consider any permission except "no permission" to be default.
+ if (aPartId == "cookie") {
+ state = Services.perms.testPermissionFromPrincipal(gPermPrincipal, "cookie");
+
+ if (state == SitePermissions.UNKNOWN) {
+ checkbox.checked = true;
+ command.setAttribute("disabled", "true");
+ // Don't select any item in the radio group, as we can't
+ // confidently say that all cookies on the site will be allowed.
+ let radioGroup = document.getElementById("cookieRadioGroup");
+ radioGroup.selectedItem = null;
+ } else {
+ checkbox.checked = false;
+ command.removeAttribute("disabled");
+ }
+
+ setRadioState(aPartId, state);
+ return;
+ }
+
+ if (state != defaultState) {
+ checkbox.checked = false;
+ command.removeAttribute("disabled");
+ }
+ else {
+ checkbox.checked = true;
+ command.setAttribute("disabled", "true");
+ }
+ setRadioState(aPartId, state);
+}
+
+function createRow(aPartId) {
+ let rowId = "perm-" + aPartId + "-row";
+ if (document.getElementById(rowId))
+ return;
+
+ let commandId = "cmd_" + aPartId + "Toggle";
+ let labelId = "perm-" + aPartId + "-label";
+ let radiogroupId = aPartId + "RadioGroup";
+
+ let command = document.createElement("command");
+ command.setAttribute("id", commandId);
+ command.setAttribute("oncommand", "onRadioClick('" + aPartId + "');");
+ document.getElementById("pageInfoCommandSet").appendChild(command);
+
+ let row = document.createElement("richlistitem");
+ row.setAttribute("id", rowId);
+ row.setAttribute("class", "permission");
+ row.setAttribute("orient", "vertical");
+
+ let label = document.createElement("label");
+ label.setAttribute("id", labelId);
+ label.setAttribute("control", radiogroupId);
+ label.setAttribute("value", SitePermissions.getPermissionLabel(aPartId));
+ label.setAttribute("class", "permissionLabel");
+ row.appendChild(label);
+
+ let controls = document.createElement("hbox");
+ controls.setAttribute("role", "group");
+ controls.setAttribute("aria-labelledby", labelId);
+
+ let checkbox = document.createElement("checkbox");
+ checkbox.setAttribute("id", aPartId + "Def");
+ checkbox.setAttribute("oncommand", "onCheckboxClick('" + aPartId + "');");
+ checkbox.setAttribute("label", gBundle.getString("permissions.useDefault"));
+ controls.appendChild(checkbox);
+
+ let spacer = document.createElement("spacer");
+ spacer.setAttribute("flex", "1");
+ controls.appendChild(spacer);
+
+ let radiogroup = document.createElement("radiogroup");
+ radiogroup.setAttribute("id", radiogroupId);
+ radiogroup.setAttribute("orient", "horizontal");
+ for (let state of SitePermissions.getAvailableStates(aPartId)) {
+ let radio = document.createElement("radio");
+ radio.setAttribute("id", aPartId + "#" + state);
+ radio.setAttribute("label", SitePermissions.getMultichoiceStateLabel(aPartId, state));
+ radio.setAttribute("command", commandId);
+ radiogroup.appendChild(radio);
+ }
+ controls.appendChild(radiogroup);
+
+ row.appendChild(controls);
+
+ document.getElementById("permList").appendChild(row);
+}
+
+function onCheckboxClick(aPartId)
+{
+ var command = document.getElementById("cmd_" + aPartId + "Toggle");
+ var checkbox = document.getElementById(aPartId + "Def");
+ if (checkbox.checked) {
+ SitePermissions.removeFromPrincipal(gPermPrincipal, aPartId);
+ command.setAttribute("disabled", "true");
+ }
+ else {
+ onRadioClick(aPartId);
+ command.removeAttribute("disabled");
+ }
+}
+
+function onRadioClick(aPartId)
+{
+ var radioGroup = document.getElementById(aPartId + "RadioGroup");
+ let permission;
+ if (radioGroup.selectedItem) {
+ permission = parseInt(radioGroup.selectedItem.id.split("#")[1]);
+ } else {
+ permission = SitePermissions.getDefault(aPartId);
+ }
+ SitePermissions.setForPrincipal(gPermPrincipal, aPartId, permission);
+}
+
+function setRadioState(aPartId, aValue)
+{
+ var radio = document.getElementById(aPartId + "#" + aValue);
+ if (radio) {
+ radio.radioGroup.selectedItem = radio;
+ }
+}
diff --git a/comm/suite/browser/pageinfo/security.js b/comm/suite/browser/pageinfo/security.js
new file mode 100644
index 0000000000..f6357c9ac4
--- /dev/null
+++ b/comm/suite/browser/pageinfo/security.js
@@ -0,0 +1,354 @@
+/* -*- Mode: Javascript; 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/. */
+
+ChromeUtils.defineModuleGetter(this, "PluralForm",
+ "resource://gre/modules/PluralForm.jsm");
+
+var security = {
+ init: function(uri, windowInfo) {
+ this.uri = uri;
+ this.windowInfo = windowInfo;
+ },
+
+ // Display the server certificate (static)
+ viewCert : function() {
+ var cert = security._cert;
+ viewCertHelper(window, cert);
+ },
+
+ _getSecurityInfo : function() {
+ // We don't have separate info for a frame, return null until further notice
+ // (see bug 138479)
+ if (!this.windowInfo.isTopWindow)
+ return null;
+
+ var hostName = this.windowInfo.hostName;
+
+ var ui = security._getSecurityUI();
+ if (!ui)
+ return null;
+
+ var isBroken =
+ (ui.state & Ci.nsIWebProgressListener.STATE_IS_BROKEN);
+ var isMixed =
+ (ui.state & (Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT |
+ Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT));
+ var isInsecure =
+ (ui.state & Ci.nsIWebProgressListener.STATE_IS_INSECURE);
+ var isEV =
+ (ui.state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL);
+ var status = ui.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+
+ if (!isInsecure && status) {
+ status.QueryInterface(Ci.nsISSLStatus);
+ var cert = status.serverCert;
+ var issuerName = cert.issuerOrganization || cert.issuerName;
+
+ var retval = {
+ hostName : hostName,
+ cAName : issuerName,
+ encryptionAlgorithm : undefined,
+ encryptionStrength : undefined,
+ version: undefined,
+ isBroken : isBroken,
+ isMixed : isMixed,
+ isEV : isEV,
+ cert : cert,
+ certificateTransparency : undefined,
+ };
+
+ var version;
+ try {
+ retval.encryptionAlgorithm = status.cipherName;
+ retval.encryptionStrength = status.secretKeyLength;
+ version = status.protocolVersion;
+ }
+ catch (e) {
+ }
+
+ switch (version) {
+ case Ci.nsISSLStatus.SSL_VERSION_3:
+ retval.version = "SSL 3";
+ break;
+ case Ci.nsISSLStatus.TLS_VERSION_1:
+ retval.version = "TLS 1.0";
+ break;
+ case Ci.nsISSLStatus.TLS_VERSION_1_1:
+ retval.version = "TLS 1.1";
+ break;
+ case Ci.nsISSLStatus.TLS_VERSION_1_2:
+ retval.version = "TLS 1.2";
+ break;
+ case Ci.nsISSLStatus.TLS_VERSION_1_3:
+ retval.version = "TLS 1.3";
+ break;
+ }
+
+ // Select the status text to display for Certificate Transparency.
+ // Since we do not yet enforce the CT Policy on secure connections,
+ // we must not complain on policy discompliance (it might be viewed
+ // as a security issue by the user).
+ switch (status.certificateTransparencyStatus) {
+ case Ci.nsISSLStatus.CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE:
+ case Ci.nsISSLStatus.CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS:
+ case Ci.nsISSLStatus.CERTIFICATE_TRANSPARENCY_POLICY_NOT_DIVERSE_SCTS:
+ retval.certificateTransparency = null;
+ break;
+ case Ci.nsISSLStatus.CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT:
+ retval.certificateTransparency = "Compliant";
+ break;
+ }
+
+ return retval;
+ }
+ return {
+ hostName : hostName,
+ cAName : "",
+ encryptionAlgorithm : "",
+ encryptionStrength : 0,
+ version: "",
+ isBroken : isBroken,
+ isMixed : isMixed,
+ isEV : isEV,
+ cert : null,
+ certificateTransparency : null,
+ };
+ },
+
+ // Find the secureBrowserUI object (if present)
+ _getSecurityUI : function() {
+ if (window.opener.gBrowser)
+ return window.opener.gBrowser.securityUI;
+ return null;
+ },
+
+ /**
+ * Open the cookie manager window
+ */
+ viewCookies : function()
+ {
+ var eTLD;
+ try {
+ eTLD = Services.eTLD.getBaseDomain(this.uri);
+ } catch (e) {
+ // getBaseDomain will fail if the host is an IP address or is empty
+ eTLD = this.uri.asciiHost;
+ }
+
+ toDataManager(eTLD + '|cookies');
+ },
+
+ /**
+ * Open the login manager window
+ */
+ viewPasswords : function()
+ {
+ toDataManager(this._getSecurityInfo().hostName + '|passwords');
+ },
+
+ _cert : null
+};
+
+function securityOnLoad(uri, windowInfo) {
+ security.init(uri, windowInfo);
+
+ var info = security._getSecurityInfo();
+ if (!info || uri.scheme === "about") {
+ document.getElementById("securityTab").hidden = true;
+ document.getElementById("securityBox").hidden = true;
+ return;
+ }
+
+ document.getElementById("securityTab").hidden = false;
+ document.getElementById("securityBox").hidden = false;
+
+ const pageInfoBundle = document.getElementById("pageinfobundle");
+
+ /* Set Identity section text */
+ setText("security-identity-domain-value", info.hostName);
+
+ var owner;
+ var verifier;
+ var generalPageIdentityString;
+ var identityClass;
+ var validity;
+ if (info.cert && !info.isBroken) {
+ validity = info.cert.validity.notAfterLocalDay;
+
+ // Try to pull out meaningful values. Technically these fields are optional
+ // so we'll employ fallbacks where appropriate. The EV spec states that Org
+ // fields must be specified for subject and issuer so that case is simpler.
+ if (info.isEV) {
+ owner = info.cert.organization;
+ verifier = info.cAName;
+ generalPageIdentityString =
+ pageInfoBundle.getFormattedString("generalSiteIdentity",
+ [owner, verifier]);
+ identityClass = "verifiedIdentity";
+ } else {
+ // Technically, a non-EV cert might specify an owner in the O field or
+ // not, depending on the CA's issuing policies. However we don't have any
+ // programmatic way to tell those apart, and no policy way to establish
+ // which organization vetting standards are good enough (that's what EV is
+ // for) so we default to treating these certs as domain-validated only.
+ owner = pageInfoBundle.getString("securityNoOwner");
+ verifier = info.cAName || info.cert.issuerCommonName || info.cert.issuerName;
+ generalPageIdentityString = owner;
+ identityClass = "verifiedDomain";
+ }
+ } else {
+ // We don't have valid identity credentials.
+ owner = pageInfoBundle.getString("securityNoOwner");
+ verifier = pageInfoBundle.getString("notSet");
+ generalPageIdentityString = owner;
+ identityClass = "";
+ }
+
+ setText("security-identity-owner-value", owner);
+ setText("security-identity-verifier-value", verifier);
+ setText("general-security-identity", generalPageIdentityString);
+ document.getElementById("identity-icon").className = identityClass;
+ if (validity) {
+ setText("security-identity-validity-value", validity);
+ } else {
+ document.getElementById("security-identity-validity-row").hidden = true;
+ }
+
+ /* Manage the View Cert button*/
+ if (info.cert)
+ security._cert = info.cert;
+ document.getElementById("security-view-cert").collapsed = !info.cert;
+
+ /* Set Privacy & History section text */
+ var yesStr = pageInfoBundle.getString("yes");
+ var noStr = pageInfoBundle.getString("no");
+
+ var hasCookies = hostHasCookies(uri);
+ setText("security-privacy-cookies-value", hasCookies ? yesStr : noStr);
+ document.getElementById("security-view-cookies").disabled = !hasCookies;
+ var hasPasswords = realmHasPasswords(uri);
+ setText("security-privacy-passwords-value", hasPasswords ? yesStr : noStr);
+ document.getElementById("security-view-password").disabled = !hasPasswords;
+
+ var visitCount = previousVisitCount(info.hostName);
+ let visitCountStr = visitCount > 0
+ ? PluralForm.get(visitCount, pageInfoBundle.getString("securityVisitsNumber"))
+ .replace("#1", visitCount.toLocaleString())
+ : pageInfoBundle.getString("securityNoVisits");
+ setText("security-privacy-history-value", visitCountStr);
+
+ /* Set the Technical Detail section messages */
+ const pkiBundle = document.getElementById("pkiBundle");
+ var hdr;
+ var msg1;
+ var msg2;
+
+ if (info.isBroken) {
+ if (info.isMixed) {
+ hdr = pkiBundle.getString("pageInfo_MixedContent");
+ msg1 = pkiBundle.getString("pageInfo_MixedContent2");
+ } else {
+ hdr = pkiBundle.getFormattedString("pageInfo_BrokenEncryption",
+ [info.encryptionAlgorithm,
+ info.encryptionStrength + "",
+ info.version]);
+ msg1 = pkiBundle.getString("pageInfo_WeakCipher");
+ }
+ msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
+ } else if (info.encryptionStrength > 0) {
+ hdr = pkiBundle.getFormattedString("pageInfo_EncryptionWithBitsAndProtocol",
+ [info.encryptionAlgorithm,
+ info.encryptionStrength + "",
+ info.version]);
+ msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
+ msg2 = pkiBundle.getString("pageInfo_Privacy_Encrypted2");
+ security._cert = info.cert;
+ } else {
+ hdr = pkiBundle.getString("pageInfo_NoEncryption");
+ if (info.hostName != null) {
+ msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
+ } else {
+ msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
+ }
+ msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
+ }
+ setText("security-technical-shortform", hdr);
+ setText("security-technical-longform1", msg1);
+ setText("security-technical-longform2", msg2);
+ setText("general-security-privacy", hdr);
+
+ const ctStatus =
+ document.getElementById("security-technical-certificate-transparency");
+ if (info.certificateTransparency) {
+ ctStatus.hidden = false;
+ ctStatus.value = pkiBundle.getString(
+ "pageInfo_CertificateTransparency_" + info.certificateTransparency);
+ } else {
+ ctStatus.hidden = true;
+ }
+}
+
+function setText(id, value)
+{
+ var element = document.getElementById(id);
+ if (!element)
+ return;
+ if (element.localName == "textbox" || element.localName == "label")
+ element.value = value;
+ else
+ element.textContent = value;
+}
+
+function viewCertHelper(parent, cert)
+{
+ if (!cert)
+ return;
+
+ var cd = Cc[CERTIFICATEDIALOGS_CONTRACTID].getService(nsICertificateDialogs);
+ cd.viewCert(parent, cert);
+}
+
+/**
+ * Return true iff we have cookies for uri.
+ */
+function hostHasCookies(aUri) {
+ return Services.cookies.countCookiesFromHost(aUri.asciiHost) > 0;
+}
+
+/**
+ * Return true iff realm (proto://host:port) (extracted from uri) has
+ * saved passwords
+ */
+function realmHasPasswords(aUri) {
+ return Services.logins.countLogins(aUri.prePath, "", "") > 0;
+}
+
+/**
+ * Return the number of previous visits recorded for host before today.
+ *
+ * @param host - the domain name to look for in history
+ */
+function previousVisitCount(host, endTimeReference) {
+ if (!host)
+ return false;
+
+ var historyService = Cc["@mozilla.org/browser/nav-history-service;1"]
+ .getService(Ci.nsINavHistoryService);
+
+ var options = historyService.getNewQueryOptions();
+ options.resultType = options.RESULTS_AS_VISIT;
+
+ // Search for visits to this host before today
+ var query = historyService.getNewQuery();
+ query.endTimeReference = query.TIME_RELATIVE_TODAY;
+ query.endTime = 0;
+ query.domain = host;
+
+ var result = historyService.executeQuery(query, options);
+ result.root.containerOpen = true;
+ var cc = result.root.childCount;
+ result.root.containerOpen = false;
+ return cc;
+}
diff --git a/comm/suite/browser/safeBrowsingOverlay.js b/comm/suite/browser/safeBrowsingOverlay.js
new file mode 100644
index 0000000000..3e92234472
--- /dev/null
+++ b/comm/suite/browser/safeBrowsingOverlay.js
@@ -0,0 +1,75 @@
+/* -*- 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/. */
+
+var gSafeBrowsing = {
+ initMenuItems: function initMenuItems() {
+ // A blocked page will have a specific about:blocked content documentURI.
+ var docURI = content.document.documentURI;
+
+ // "reason" isn't currently used but it's here to make porting
+ // from Firefox easier and may also be useful in the future
+ // for further testing and setting menu items.
+ let reason;
+
+ // Show/hide the appropriate menu item.
+ // Initially allow report url and disallow reporting phishing error.
+ document.getElementById("reportPhishing").hidden = false;
+ document.getElementById("reportPhishingError").hidden = true;
+
+ if (docURI.startsWith("about:blocked")) {
+ // It's blocked so don't allow reporting again.
+ document.getElementById("reportPhishing").hidden = true;
+ // Test for blocked page.
+ if (/e=malwareBlocked/.test(docURI)) {
+ reason = "malware";
+ } else if (/e=unwantedBlocked/.test(docURI)) {
+ reason = "unwanted";
+ } else if (/e=deceptiveBlocked/.test(docURI)) {
+ reason = "phishing";
+ document.getElementById("reportPhishingError").hidden = false;
+ }
+ }
+
+ var broadcaster = document.getElementById("safeBrowsingBroadcaster");
+ var uri = getBrowser().currentURI;
+ if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
+ broadcaster.removeAttribute("disabled");
+ else
+ broadcaster.setAttribute("disabled", true);
+ },
+
+ /**
+ * Used to report a phishing page or a false positive
+ *
+ * @param name
+ * String One of "PhishMistake", "MalwareMistake", or "Phish"
+ * @param info
+ * Information about the reasons for blocking the resource.
+ * In the case false positive, it may contain SafeBrowsing
+ * matching list and provider of the list
+ * @return String the report phishing URL.
+ */
+ getReportURL(name, info) {
+ let reportInfo = info;
+ if (!reportInfo) {
+ let pageUri = getBrowser().currentURI.clone();
+
+ // Remove the query to avoid including potentially sensitive data
+ if (pageUri instanceof Ci.nsIURL) {
+ pageUri = pageUri.mutate().setQuery("").finalize();
+ }
+
+ reportInfo = { uri: pageUri.asciiSpec };
+ }
+ return SafeBrowsing.getReportURL(name, reportInfo);
+ },
+
+ initOverlay: function initOverlay(aEvent) {
+ var popup = document.getElementById("helpPopup");
+ popup.addEventListener("popupshowing", gSafeBrowsing.initMenuItems);
+ }
+}
+
+window.addEventListener("load", gSafeBrowsing.initOverlay);
diff --git a/comm/suite/browser/safeBrowsingOverlay.xul b/comm/suite/browser/safeBrowsingOverlay.xul
new file mode 100644
index 0000000000..e034fd5891
--- /dev/null
+++ b/comm/suite/browser/safeBrowsingOverlay.xul
@@ -0,0 +1,32 @@
+<?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 SYSTEM "chrome://communicator/locale/safeBrowsing.dtd">
+
+<overlay id="safeBrowsingOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://navigator/content/safeBrowsingOverlay.js"/>
+
+ <broadcasterset id="navBroadcasters">
+ <broadcaster id="safeBrowsingBroadcaster" disabled="true"/>
+ </broadcasterset>
+
+ <menupopup id="helpPopup">
+ <menuitem id="reportPhishing"
+ label="&reportDeceptiveSite.label;"
+ accesskey="&reportDeceptiveSite.accesskey;"
+ insertbefore="menu_HelpAboutSeparator"
+ observes="safeBrowsingBroadcaster"
+ oncommand="openUILink(gSafeBrowsing.getReportURL('Phish'), event);"
+ onclick="checkForMiddleClick(this, event);"/>
+ <menuitem id="reportPhishingError"
+ label="&notADeceptiveSite.label;"
+ accesskey="&notADeceptiveSite.accesskey;"
+ insertbefore="menu_HelpAboutSeparator"
+ observes="safeBrowsingBroadcaster"
+ oncommand="openUILinkIn(gSafeBrowsing.getReportURL('PhishMistake'), 'tabfocused');"/>
+ </menupopup>
+</overlay>
diff --git a/comm/suite/browser/sessionHistoryUI.js b/comm/suite/browser/sessionHistoryUI.js
new file mode 100644
index 0000000000..f8fdcc79aa
--- /dev/null
+++ b/comm/suite/browser/sessionHistoryUI.js
@@ -0,0 +1,172 @@
+/* -*- 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/. */
+
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+function toggleTabsFromOtherComputers()
+ {
+ // enable/disable the Tabs From Other Computers menu
+ let menuitem = document.getElementById("sync-tabs-menuitem");
+
+ // If Sync isn't configured yet, then don't show the menuitem.
+ if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
+ Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
+ menuitem.hidden = true;
+ return;
+ }
+
+ // The tabs engine might never be inited (if services.sync.registerEngines
+ // is modified), so make sure we avoid undefined errors.
+ let enabled = Weave.Service.isLoggedIn &&
+ Weave.Service.engineManager.get("tabs") &&
+ Weave.Service.engineManager.get("tabs").enabled;
+ menuitem.setAttribute("disabled", !enabled);
+ menuitem.hidden = false;
+ }
+
+function FillHistoryMenu(aParent, aMenu)
+{
+ // Remove old entries if any
+ deleteHistoryItems(aParent);
+
+ var sessionHistory = getWebNavigation().sessionHistory;
+
+ var count = sessionHistory.count;
+ var index = sessionHistory.index;
+ var end;
+ const MAX_HISTORY_MENU_ITEMS = 15;
+
+ switch (aMenu)
+ {
+ case "back":
+ end = index > MAX_HISTORY_MENU_ITEMS ? index - MAX_HISTORY_MENU_ITEMS
+ : 0;
+ if (index <= end)
+ return false;
+ for (let j = index - 1; j >= end; j--)
+ {
+ let entry = sessionHistory.getEntryAtIndex(j);
+ if (entry)
+ createHistoryMenuItem(aParent, j, entry);
+ }
+ break;
+ case "forward":
+ end = count - index > MAX_HISTORY_MENU_ITEMS ? index + MAX_HISTORY_MENU_ITEMS
+ : count - 1;
+ if (index >= end)
+ return false;
+ for (let j = index + 1; j <= end; j++)
+ {
+ let entry = sessionHistory.getEntryAtIndex(j);
+ if (entry)
+ createHistoryMenuItem(aParent, j, entry);
+ }
+ break;
+ case "go":
+ var startHistory = document.getElementById("startHistorySeparator");
+ var endHistory = document.getElementById("endHistorySeparator");
+ // var syncMenuItem = document.getElementById("sync-tabs-menuitem");
+ startHistory.hidden = (count == 0);
+ end = count > MAX_HISTORY_MENU_ITEMS ? count - MAX_HISTORY_MENU_ITEMS
+ : 0;
+ for (let j = count - 1; j >= end; j--)
+ {
+ let entry = sessionHistory.getEntryAtIndex(j);
+ if (entry)
+ createHistoryMenuItem(aParent, j, entry, endHistory, j == index);
+ }
+ // toggleTabsFromOtherComputers();
+ // endHistory.hidden = (endHistory == aParent.lastChild || syncMenuItem.hidden);
+ endHistory.hidden = (endHistory == aParent.lastChild);
+ break;
+ }
+ return true;
+}
+
+function executeUrlBarHistoryCommand( aTarget )
+ {
+ var index = aTarget.getAttribute("index");
+ var label = aTarget.getAttribute("label");
+ if (index != "nothing_available" && label)
+ {
+ gURLBar.value = label;
+ UpdatePageProxyState();
+ handleURLBarCommand();
+ }
+ }
+
+function createUBHistoryMenu( aParent )
+ {
+ while (aParent.hasChildNodes())
+ aParent.lastChild.remove();
+
+ var file = GetUrlbarHistoryFile();
+ if (file.exists()) {
+ var connection = Services.storage.openDatabase(file);
+ try {
+ if (connection.tableExists("urlbarhistory")) {
+ var statement = connection.createStatement(
+ "SELECT url FROM urlbarhistory ORDER BY ROWID DESC");
+ while (statement.executeStep())
+ aParent.appendChild(document.createElement("menuitem"))
+ .setAttribute("label", statement.getString(0));
+ statement.reset();
+ statement.finalize();
+ return;
+ }
+ } finally {
+ connection.close();
+ }
+ }
+ //Create the "Nothing Available" Menu item and disable it.
+ var na = aParent.appendChild(document.createElement("menuitem"));
+ na.setAttribute("label", gNavigatorBundle.getString("nothingAvailable"));
+ na.setAttribute("disabled", "true");
+ }
+
+function createHistoryMenuItem(aParent, aIndex, aEntry, aAnchor, aChecked)
+{
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", aEntry.title);
+ menuitem.setAttribute("index", aIndex);
+ if (aChecked)
+ {
+ menuitem.setAttribute("type", "radio");
+ menuitem.setAttribute("checked", "true");
+ }
+
+ if (!aChecked || AppConstants.platform == "macosx")
+ {
+ menuitem.className = "menuitem-iconic bookmark-item menuitem-with-favicon";
+ PlacesUtils.favicons.getFaviconURLForPage(aEntry.URI,
+ function faviconURLCallback(aURI) {
+ if (aURI) {
+ menuitem.setAttribute("image",
+ PlacesUtils.favicons
+ .getFaviconLinkForIcon(aURI).spec);
+ }
+ }
+ );
+ }
+ aParent.insertBefore(menuitem, aAnchor);
+}
+
+function deleteHistoryItems(aParent)
+{
+ var children = aParent.childNodes;
+ for (let i = children.length - 1; i >= 0; --i)
+ {
+ if (children[i].hasAttribute("index"))
+ children[i].remove();
+ }
+}
+
+function updateGoMenu(event)
+ {
+ FillHistoryMenu(event.target, "go");
+ updateRecentMenuItems();
+ }
diff --git a/comm/suite/browser/tabbrowser.xml b/comm/suite/browser/tabbrowser.xml
new file mode 100644
index 0000000000..390fbaa0cf
--- /dev/null
+++ b/comm/suite/browser/tabbrowser.xml
@@ -0,0 +1,3707 @@
+<?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 bindings [
+<!ENTITY % tabBrowserDTD SYSTEM "chrome://navigator/locale/tabbrowser.dtd" >
+%tabBrowserDTD;
+]>
+
+<bindings id="tabBrowserBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="tabbrowser">
+ <resources>
+ <stylesheet src="chrome://navigator/skin/tabbrowser.css"/>
+ </resources>
+
+ <content>
+ <xul:stringbundle anonid="tbstringbundle" src="chrome://navigator/locale/tabbrowser.properties"/>
+ <xul:tabbox anonid="tabbox" flex="1" eventnode="document">
+ <xul:hbox class="tab-drop-indicator-bar" collapsed="true">
+ <xul:image class="tab-drop-indicator" mousethrough="always"/>
+ </xul:hbox>
+ <xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
+ anonid="strip"
+ ondragstart="this.parentNode.parentNode._onDragStart(event);"
+ ondragover="this.parentNode.parentNode._onDragOver(event);"
+ ondrop="this.parentNode.parentNode._onDrop(event);"
+ ondragleave="this.parentNode.parentNode._onDragLeave(event);">
+ <xul:tooltip onpopupshowing="event.stopPropagation(); return this.parentNode.parentNode.parentNode.doPreview(this);"
+ onpopuphiding="this.parentNode.parentNode.parentNode.resetPreview(this);" orient="vertical">
+ <xul:label class="tooltip-label" crop="right"/>
+ <xul:label class="tooltip-label" hidden="true"><html:canvas class="tab-tooltip-canvas"/></xul:label>
+ </xul:tooltip>
+ <xul:menupopup anonid="tabContextMenu" onpopupshowing="return document.getBindingParent(this).updatePopupMenu(this);">
+ <xul:menuitem label="&closeTab.label;" accesskey="&closeTab.accesskey;"
+ tbattr="tabbrowser-tab"
+ oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
+ tabbrowser.removeTab(tabbrowser.mContextTab);"/>
+ <xul:menuitem label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
+ tbattr="tabbrowser-multiple tabbrowser-tab"
+ oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
+ tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
+ <xul:menuitem label="&closeTabsToTheEnd.label;"
+ accesskey="&closeTabsToTheEnd.accesskey;"
+ tbattr="tabbrowser-totheend tabbrowser-tab"
+ oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
+ tabbrowser.removeTabsToTheEndFrom(tabbrowser.mContextTab);"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;"
+ xbl:inherits="oncommand=onnewtab"/>
+ <xul:menuitem label="&undoCloseTab.label;" accesskey="&undoCloseTab.accesskey;"
+ tbattr="tabbrowser-undoclosetab"
+ oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
+ tabbrowser.undoCloseTab(0);"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&bookmarkGroup.label;" accesskey="&bookmarkGroup.accesskey;"
+ tbattr="tabbrowser-multiple"
+ xbl:inherits="oncommand=onbookmarkgroup"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
+ tbattr="tabbrowser-tab"
+ oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
+ tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
+ <xul:menuitem label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
+ tbattr="tabbrowser-multiple"
+ oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
+ tabbrowser.reloadAllTabs();"/>
+ </xul:menupopup>
+
+ <xul:tabs class="tabbrowser-tabs" flex="1"
+ anonid="tabcontainer"
+ tooltiptextnew="&newTabButton.tooltip;"
+ tooltiptextclose="&closeTabButton.tooltip;"
+ tooltiptextalltabs="&listAllTabs.tooltip;"
+ setfocus="false"
+ onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
+ xbl:inherits="onnewtab,onnewtabclick"
+ onclosetab="var node = this.parentNode;
+ while (node.localName != 'tabbrowser')
+ node = node.parentNode;
+ node.removeCurrentTab();">
+ <xul:tab selected="true" validate="never"
+ onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image'));
+ this.removeAttribute('image');"
+ width="0" flex="100"
+ class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
+ </xul:tabs>
+ </xul:hbox>
+ <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
+ <xul:notificationbox class="browser-notificationbox" xbl:inherits="popupnotification">
+ <xul:stack flex="1" anonid="browserStack">
+ <xul:browser flex="1" type="content" primary="true"
+ xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,datetimepicker"/>
+ </xul:stack>
+ </xul:notificationbox>
+ </xul:tabpanels>
+ </xul:tabbox>
+ <children/>
+ </content>
+ <implementation implements="nsIDOMEventListener, nsIObserver">
+ <field name="closingTabsEnum" readonly="true">
+ ({ ALL: 0, OTHER: 1, TO_END: 2 });
+ </field>
+ <field name="mSessionStore" readonly="true">
+ Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.nsISessionStore);
+ </field>
+ <field name="mTabBox" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
+ </field>
+ <field name="mStrip" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "strip");
+ </field>
+ <field name="tabContainer" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
+ </field>
+ <field name="mPanelContainer" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
+ </field>
+ <field name="tabs" readonly="true">
+ this.tabContainer.childNodes
+ </field>
+ <field name="mStringBundle">
+ document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
+ </field>
+ <field name="mCurrentTab">
+ null
+ </field>
+ <field name="mPreviousTab">
+ null
+ </field>
+ <field name="mCurrentBrowser">
+ null
+ </field>
+ <field name="mProgressListeners">
+ []
+ </field>
+ <field name="mTabsProgressListeners">
+ []
+ </field>
+ <field name="mTabListeners">
+ new Array()
+ </field>
+ <field name="mTabFilters">
+ new Array()
+ </field>
+ <field name="mLastRelatedIndex">
+ 0
+ </field>
+ <field name="usePrivateBrowsing" readonly="true">
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext)
+ .usePrivateBrowsing;
+ </field>
+ <field name="mContextTab">
+ null
+ </field>
+
+ <method name="_handleKeyEvent">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ if (!aEvent.isTrusted) {
+ // Don't let untrusted events mess with tabs.
+ return;
+ }
+
+ if (aEvent.altKey)
+ return;
+
+ if (AppConstants.platform == "macosx") {
+ if (!aEvent.metaKey)
+ return;
+
+ var offset = 1;
+ switch (aEvent.charCode) {
+ case '}'.charCodeAt(0):
+ offset = -1;
+ case '{'.charCodeAt(0):
+ if (window.getComputedStyle(this, null).direction == "ltr")
+ offset *= -1;
+ this.tabContainer.advanceSelectedTab(offset, true);
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ return;
+ }
+ } else {
+ if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
+ aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
+ this.getStripVisibility()) {
+ this.removeCurrentTab();
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ return;
+ }
+
+ if (aEvent.target == this) {
+ switch (aEvent.keyCode) {
+ case KeyEvent.DOM_VK_UP:
+ this.moveTabBackward();
+ break;
+ case KeyEvent.DOM_VK_DOWN:
+ this.moveTabForward();
+ break;
+ case KeyEvent.DOM_VK_RIGHT:
+ case KeyEvent.DOM_VK_LEFT:
+ this.moveTabOver(aEvent);
+ break;
+ case KeyEvent.DOM_VK_HOME:
+ this.moveTabToStart();
+ break;
+ case KeyEvent.DOM_VK_END:
+ this.moveTabToEnd();
+ break;
+ default:
+ // Stop the keypress event for the above keyboard
+ // shortcuts only.
+ return;
+ }
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ }
+ }
+ ]]></body>
+ </method>
+
+ <field name="arrowKeysShouldWrap">
+ null
+ </field>
+ <field name="nextTabNumber">
+ 0
+ </field>
+ <field name="_browsers">
+ null
+ </field>
+ <field name="savedBrowsers">
+ new Array()
+ </field>
+ <field name="referenceTab">
+ null
+ </field>
+
+ <method name="doPreview">
+ <parameter name="aPopup"/>
+ <body>
+ <![CDATA[
+ var tab = document.tooltipNode;
+ if (tab.localName != "tab")
+ return false;
+ var b = tab.linkedBrowser;
+ if (!b)
+ return false;
+
+ var label = aPopup.firstChild;
+ label.setAttribute("value", tab.getAttribute("label"));
+
+ var canvas = aPopup.lastChild.firstChild;
+ canvas.parentNode.hidden = true;
+
+ var win = b.contentWindow;
+ var w = win.innerWidth;
+ var h = win.innerHeight;
+
+ if (tab == this.mCurrentTab || h == 0 ||
+ !Services.prefs.getBoolPref("browser.tabs.tooltippreview.enable")) {
+ return true;
+ }
+
+ var ctx;
+ try {
+ ctx = canvas.getContext("2d");
+ } catch (e) {
+ return true;
+ }
+
+ label.width = 0;
+ aPopup.setAttribute("tabpreview", "true");
+
+ var canvasW = Services.prefs.getIntPref("browser.tabs.tooltippreview.width");
+ var canvasH = Math.round(canvasW * h / w);
+ canvas.width = canvasW;
+ canvas.height = canvasH;
+ canvas.parentNode.hidden = false;
+
+ var bgColor = Services.prefs.getBoolPref("browser.display.use_system_colors") ?
+ "Window" :
+ Services.prefs.getCharPref("browser.display.background_color");
+ if (b.contentDocument instanceof ImageDocument &&
+ !(b.contentDocument.imageRequest.imageStatus &
+ Ci.imgIRequest.STATUS_ERROR)) {
+ ctx.fillStyle = bgColor;
+ ctx.fillRect(0, 0, canvasW, canvasH);
+ var img = b.contentDocument.body.firstChild;
+ var ratio = img.naturalHeight / img.naturalWidth;
+ if (img.naturalHeight <= canvasH && img.naturalWidth <= canvasW) {
+ ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
+ }
+ else if (ratio * canvasW > canvasH) {
+ ctx.drawImage(img, 0, 0, canvasH / ratio, canvasH);
+ }
+ else {
+ ctx.drawImage(img, 0, 0, canvasW, ratio * canvasW);
+ }
+ }
+ else {
+ ctx.save();
+ ctx.scale(canvasW / w, canvasH / h);
+ ctx.drawWindow(win, win.pageXOffset, win.pageYOffset, w, h, bgColor);
+ ctx.restore();
+ }
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <!-- XXXcst This should not be needed, but it seems that the tooltip
+ sizing is happening too early when we want to stop showing the
+ preview. This clears the label's width early (i.e. when the
+ previous preview disappears) so that when the next tooltip appears,
+ it doesn't start with a bad size. For now, I blame Gecko. -->
+ <method name="resetPreview">
+ <parameter name="aPopup"/>
+ <body>
+ <![CDATA[
+ var label = aPopup.firstChild;
+ // If this function is removed, these two lines need to be restored
+ // to the non-preview codepath above.
+ label.removeAttribute("width");
+ aPopup.removeAttribute("tabpreview");
+ ]]>
+ </body>
+ </method>
+
+ <method name="previewTab">
+ <parameter name="aTab"/>
+ <parameter name="aCallback"/>
+ <body>
+ <![CDATA[
+ let currentTab = this.selectedTab;
+ try {
+ // Suppress focus, ownership and selected tab changes.
+ this._previewMode = true;
+ this.selectedTab = aTab;
+ aCallback();
+ } finally {
+ this.selectedTab = currentTab;
+ this._previewMode = false;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserAtIndex">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ return this.browsers[aIndex];
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserIndexForDocument">
+ <parameter name="aDocument"/>
+ <body>
+ <![CDATA[
+ var browsers = this.browsers;
+ for (var i = 0; i < browsers.length; i++)
+ if (browsers[i].contentDocument == aDocument)
+ return i;
+ return -1;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserForDocument">
+ <parameter name="aDocument"/>
+ <body>
+ <![CDATA[
+ var browsers = this.browsers;
+ for (var i = 0; i < browsers.length; i++)
+ if (browsers[i].contentDocument == aDocument)
+ return browsers[i];
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserForContentWindow">
+ <parameter name="aWindow"/>
+ <body>
+ <![CDATA[
+ const browsers = this.browsers;
+ for (let browser of browsers) {
+ if (browser.contentWindow == aWindow)
+ return browser;
+ }
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getNotificationBox">
+ <parameter name="aBrowser"/>
+ <body>
+ <![CDATA[
+ return aBrowser ? aBrowser.parentNode.parentNode
+ : this.mCurrentBrowser.parentNode.parentNode;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_callProgressListeners">
+ <parameter name="aBrowser"/>
+ <parameter name="aMethod"/>
+ <parameter name="aArguments"/>
+ <parameter name="aCallGlobalListeners"/>
+ <parameter name="aCallTabsListeners"/>
+ <body><![CDATA[
+ if (!aBrowser)
+ aBrowser = this.mCurrentBrowser;
+
+ if (aCallGlobalListeners != false &&
+ aBrowser == this.mCurrentBrowser) {
+ this.mProgressListeners.forEach(function (p) {
+ if (aMethod in p) {
+ try {
+ p[aMethod].apply(p, aArguments);
+ } catch (e) {
+ // don't inhibit other listeners
+ Cu.reportError(e);
+ }
+ }
+ });
+ }
+
+ if (aCallTabsListeners != false) {
+ aArguments.unshift(aBrowser);
+
+ this.mTabsProgressListeners.forEach(function (p) {
+ if (aMethod in p) {
+ try {
+ p[aMethod].apply(p, aArguments);
+ } catch (e) {
+ // don't inhibit other listeners
+ Cu.reportError(e);
+ }
+ }
+ });
+ }
+ ]]></body>
+ </method>
+
+ <!-- A web progress listener object definition for a given tab. -->
+ <method name="mTabProgressListener">
+ <parameter name="aTab"/>
+ <parameter name="aBrowser"/>
+ <parameter name="aStartsBlank"/>
+ <body>
+ <![CDATA[
+ return ({
+ mTabBrowser: this,
+ mTab: aTab,
+ mBrowser: aBrowser,
+ mBlank: aStartsBlank,
+ mFeeds: [],
+ mRequest: null,
+ mStateFlags: 0,
+ mStatus: 0,
+ mMessage: "",
+
+ // cache flags for correct status UI update after tab switching
+ mTotalProgress: 0,
+
+ // count of open requests (should always be 0 or 1)
+ mRequestCount: 0,
+
+ _callProgressListeners: function () {
+ Array.prototype.unshift.call(arguments, this.mBrowser);
+ return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
+ },
+
+ _shouldShowProgress: function (aRequest) {
+ if (this.mBlank)
+ return false;
+
+ // Don't show progress indicators in tabs for about: URIs
+ // pointing to local resources.
+ if ((aRequest instanceof Ci.nsIChannel) &&
+ aRequest.originalURI.schemeIs("about") &&
+ (aRequest.URI.schemeIs("jar") || aRequest.URI.schemeIs("file")))
+ return false;
+
+ return true;
+ },
+
+ onProgressChange: function (aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
+
+ if (!this._shouldShowProgress(aRequest))
+ return;
+
+ if (this.mTotalProgress && this.mTab.hasAttribute("busy"))
+ this.mTab.setAttribute("progress", "true");
+
+ this._callProgressListeners("onProgressChange",
+ [aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress]);
+ },
+
+ onProgressChange64: function (aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ return this.onProgressChange(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
+ aMaxTotalProgress);
+ },
+
+ onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (!aRequest)
+ return;
+
+ var oldBlank = this.mBlank;
+
+ let location;
+ let originalLocation;
+ try {
+ aRequest.QueryInterface(Ci.nsIChannel)
+ location = aRequest.URI;
+ originalLocation = aRequest.originalURI;
+ } catch (ex) {}
+
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
+ this.mRequestCount++;
+ }
+ else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ const NS_ERROR_UNKNOWN_HOST = 2152398878;
+ if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
+ // to prevent bug 235825: wait for the request handled
+ // by the automatic keyword resolver
+ return;
+ }
+ // since we (try to) only handle STATE_STOP of the last request,
+ // the count of open requests should now be 0
+ this.mRequestCount = 0;
+ }
+
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
+ if (aWebProgress.isTopLevel) {
+ this.mFeeds = [];
+ // Need to use originalLocation rather than location because things
+ // like about:privatebrowsing arrive with nsIRequest pointing to
+ // their resolved jar: or file: URIs.
+ if (!(originalLocation && gInitialPages.has(originalLocation.spec) &&
+ originalLocation != "about:blank" &&
+ this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
+ // This will trigger clearing the location bar. Don't do it if
+ // we loaded off a blank browser and this is an initial page load
+ // (e.g. about:privatebrowsing, etc.) so we avoid clearing the
+ // location bar in case the user is typing in it.
+ // Loading about:blank shouldn't trigger this, either, because its
+ // loads are "special".
+ this.mBrowser.urlbarChangeTracker.startedLoad();
+ }
+ // If the browser is loading it must not be crashed anymore.
+ this.mTab.removeAttribute("crashed");
+ }
+
+ if (this._shouldShowProgress(aRequest)) {
+ if (!(aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) &&
+ aWebProgress && aWebProgress.isTopLevel) {
+ this.mTab.setAttribute("busy", "true");
+
+ // Do the following only for the top frame not any subframes.
+ // Remove favicon. This shows busy and progress indicators even during a reload.
+ this.mTab.removeAttribute("image");
+
+ if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
+ this.mTabBrowser.setTabTitleLoading(this.mTab);
+ }
+
+ if (this.mTab.selected)
+ this.mTabBrowser.mIsBusy = true;
+ }
+ }
+ else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
+
+ if (this.mTab.hasAttribute("busy")) {
+ this.mTab.removeAttribute("busy");
+ this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
+ if (!this.mTab.selected)
+ this.mTab.setAttribute("unread", "true");
+ }
+ this.mTab.removeAttribute("progress");
+
+ if (aWebProgress.isTopLevel) {
+ let isSuccessful = Components.isSuccessCode(aStatus);
+ if (!isSuccessful && !isTabEmpty(this.mTab)) {
+ // Restore the current document's location in case the
+ // request was stopped (possibly from a content script)
+ // before the location changed.
+ this.mBrowser.userTypedValue = null;
+
+ let inLoadURI = this.mBrowser.inLoadURI;
+ if (this.mTab.selected && gURLBar && !inLoadURI)
+ URLBarSetURI();
+ } else if (isSuccessful) {
+ this.mBrowser.urlbarChangeTracker.finishedLoad();
+ }
+
+ if (!this.mBrowser.mIconURL)
+ this.mTabBrowser.useDefaultIcon(this.mTab);
+ }
+
+ if (this.mBlank)
+ this.mBlank = false;
+
+ // For keyword URIs clear the user typed value since they will be changed into real URIs.
+ if (location && location.scheme == "keyword")
+ this.mBrowser.userTypedValue = null;
+
+ if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
+ this.mTabBrowser.setTabTitle(this.mTab);
+
+ if (this.mTab.selected)
+ this.mTabBrowser.mIsBusy = false;
+ }
+
+ if (oldBlank) {
+ this._callProgressListeners("onUpdateCurrentBrowser",
+ [aStateFlags, aStatus, "", 0],
+ true, false);
+ } else {
+ this._callProgressListeners("onStateChange",
+ [aWebProgress, aRequest, aStateFlags, aStatus],
+ true, false);
+ }
+
+ this._callProgressListeners("onStateChange",
+ [aWebProgress, aRequest, aStateFlags, aStatus],
+ false);
+
+ if (aStateFlags & (Ci.nsIWebProgressListener.STATE_START |
+ Ci.nsIWebProgressListener.STATE_STOP)) {
+ // reset cached temporary values at beginning and end
+ this.mMessage = "";
+ this.mTotalProgress = 0;
+ }
+ this.mStateFlags = aStateFlags;
+ this.mStatus = aStatus;
+ },
+
+ onLocationChange: function (aWebProgress, aRequest, aLocation, aFlags) {
+ // OnLocationChange is called for both the top-level content
+ // and the subframes.
+ let topLevel = aWebProgress.isTopLevel;
+
+ if (topLevel) {
+ let isSameDocument =
+ !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
+ // We need to clear the typed value
+ // if the document failed to load, to make sure the urlbar reflects the
+ // failed URI (particularly for SSL errors). However, don't clear the value
+ // if the error page's URI is about:blank, because that causes complete
+ // loss of urlbar contents for invalid URI errors (see bug 867957).
+ // Another reason to clear the userTypedValue is if this was an anchor
+ // navigation initiated by the user.
+ if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
+ ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
+ aLocation.spec != "about:blank") ||
+ (isSameDocument && this.mBrowser.inLoadURI))
+ this.mBrowser.userTypedValue = null;
+
+ // Don't clear the favicon if this onLocationChange was
+ // triggered by a pushState or a replaceState (bug 550565) or
+ // a hash change (bug 408415).
+ if (aWebProgress.isLoadingDocument && !isSameDocument)
+ this.mBrowser.mIconURL = null;
+ }
+
+ if (!this.mBlank)
+ this._callProgressListeners("onLocationChange",
+ [aWebProgress, aRequest, aLocation,
+ aFlags]);
+
+ if (topLevel) {
+ this.mBrowser.lastURI = aLocation;
+ this.mBrowser.lastLocationChange = Date.now();
+ }
+ },
+
+ onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {
+ if (this.mBlank)
+ return;
+
+ this.mMessage = aMessage;
+
+ this.mTabBrowser._callProgressListeners(this.mBrowser, "onStatusChange",
+ [aWebProgress, aRequest, aStatus, aMessage]);
+ },
+
+ onSecurityChange: function (aWebProgress, aRequest, aState) {
+ this.mTabBrowser._callProgressListeners(this.mBrowser, "onSecurityChange",
+ [aWebProgress, aRequest, aState]);
+ },
+
+ onRefreshAttempted: function(aWebProgress, aURI, aDelay, aSameURI)
+ {
+ var allowRefresh = true;
+ if (this.mTabBrowser.mCurrentTab == this.mTab) {
+ this.mTabBrowser.mProgressListeners.forEach(
+ function notifyRefreshAttempted(element) {
+ if (element && "onRefreshAttempted" in element) {
+ try {
+ if (!element.onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI))
+ allowRefresh = false;
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+ );
+ }
+
+ this.mTabBrowser.mTabsProgressListeners.forEach(
+ function notifyRefreshAttempted(element) {
+ if (element && "onRefreshAttempted" in element) {
+ try {
+ if (!element.onRefreshAttempted(this.mBrowser, aWebProgress, aURI, aDelay, aSameURI))
+ allowRefresh = false;
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+ , this);
+ return allowRefresh;
+ },
+
+ addFeed: function(aLink)
+ {
+ this.mFeeds.push(aLink);
+ },
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsIWebProgressListener2) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ }
+ });
+ ]]>
+ </body>
+ </method>
+
+ <method name="mInstallSH">
+ <parameter name="aBrowser"/>
+ <parameter name="aSH"/>
+ <body>
+ <![CDATA[
+ return ({
+ mBrowser: aBrowser,
+ mSH: aSH,
+
+ onProgressChange : function (aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress)
+ {
+ },
+
+ onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
+ {
+ if ((aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) &&
+ (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)) {
+ function refresh(closure) {
+ closure.mBrowser.webNavigation.sessionHistory = closure.mSH;
+ closure.mBrowser.webProgress.removeProgressListener(closure);
+ delete closure.mBrowser._SHListener;
+ closure.mSH.QueryInterface(Ci.nsIWebNavigation)
+ .gotoIndex(closure.mSH.index);
+ }
+ setTimeout(refresh, 0, this);
+ }
+ },
+
+ onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags)
+ {
+ },
+
+ onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
+ {
+ },
+
+ onSecurityChange : function(aWebProgress, aRequest, aState)
+ {
+ },
+
+ QueryInterface : function(aIID)
+ {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ }
+ });
+ ]]>
+ </body>
+ </method>
+
+ <method name="setIcon">
+ <parameter name="aTab"/>
+ <parameter name="aURI"/>
+ <parameter name="aLoadingPrincipal"/>
+ <body>
+ <![CDATA[
+ let browser = this.getBrowserForTab(aTab);
+ browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+
+ if (aURI) {
+ if (!(aURI instanceof Ci.nsIURI)) {
+ aURI = makeURI(aURI);
+ }
+
+ // We do not serialize the principal from within nsSessionStore.js,
+ // hence if aLoadingPrincipal is null we default to the
+ // systemPrincipal which will allow the favicon to load.
+ let loadingPrincipal = aLoadingPrincipal ||
+ Services.scriptSecurityManager.getSystemPrincipal();
+
+ PlacesUIUtils.loadFavicon(browser, loadingPrincipal, aURI);
+ }
+
+ let sizedIconUrl = browser.mIconURL || "";
+ if (sizedIconUrl != aTab.getAttribute("image")) {
+ if (sizedIconUrl)
+ aTab.setAttribute("image", sizedIconUrl);
+ else
+ aTab.removeAttribute("image");
+ this._tabAttrModified(aTab, ["image"]);
+ }
+
+ this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
+ ]]>
+ </body>
+ </method>
+
+ <method name="getIcon">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ let browser = aTab ? aTab.linkedBrowser : this.selectedBrowser;
+ return browser.mIconURL;
+ ]]>
+ </body>
+ </method>
+
+ <method name="buildFavIconString">
+ <parameter name="aURI"/>
+ <body>
+ <![CDATA[
+ try {
+ aURI = Services.uriFixup.createExposableURI(aURI);
+ } catch (e) {
+ }
+ return aURI.resolve("/favicon.ico");
+ ]]>
+ </body>
+ </method>
+
+ <method name="shouldLoadFavIcon">
+ <parameter name="aURI"/>
+ <body>
+ <![CDATA[
+ try {
+ aURI = Services.uriFixup.createExposableURI(aURI);
+ } catch (e) {
+ }
+ return (aURI && Services.prefs.getBoolPref("browser.chrome.site_icons") &&
+ Services.prefs.getBoolPref("browser.chrome.favicons") &&
+ ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
+ ]]>
+ </body>
+ </method>
+
+ <method name="useDefaultIcon">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ var browser = this.getBrowserForTab(aTab);
+ var documentURI = browser.documentURI;
+ var icon = null;
+
+ if (browser.imageDocument) {
+ if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
+ let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
+ if (browser.imageDocument.width <= sz &&
+ browser.imageDocument.height <= sz) {
+ icon = browser.currentURI;
+ }
+ }
+ }
+
+ // Use documentURIObject in the check for shouldLoadFavIcon so that we
+ // do the right thing with about:-style error pages. Bug 453442
+ if (!icon && this.shouldLoadFavIcon(documentURI)) {
+ let url = documentURI.prePath + "/favicon.ico";
+ if (!this.isFailedIcon(url))
+ icon = url;
+ }
+ this.setIcon(aTab, icon, browser.contentPrincipal);
+ ]]>
+ </body>
+ </method>
+
+ <method name="isFailedIcon">
+ <parameter name="aURI"/>
+ <body>
+ <![CDATA[
+ if (!(aURI instanceof Ci.nsIURI))
+ aURI = makeURI(aURI);
+ return PlacesUtils.favicons.isFailedFavicon(aURI);
+ ]]>
+ </body>
+ </method>
+
+ <method name="loadFavIcon">
+ <parameter name="aURI"/>
+ <parameter name="aAttr"/>
+ <parameter name="aElt"/>
+ <parameter name="aLoadingPrincipal"/>
+ <body>
+ <![CDATA[
+ let iconURL = this.buildFavIconString(aURI);
+ let iconURI = Services.io.newURI(iconURL);
+ let faviconFlags = this.usePrivateBrowsing ?
+ PlacesUtils.favicons.FAVICON_LOAD_PRIVATE
+ : PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE;
+ let loadingPrincipal = aLoadingPrincipal ||
+ Services.scriptSecurityManager.getSystemPrincipal();
+
+ PlacesUtils.favicons
+ .setAndFetchFaviconForPage(aURI, iconURI, false, faviconFlags,
+ null,
+ loadingPrincipal);
+ if (PlacesUtils.favicons.isFailedFavicon(aURI)) {
+ return;
+ }
+
+ aElt.setAttribute(aAttr, iconURL);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addToMissedIconCache">
+ <parameter name="aURI"/>
+ <body>
+ <![CDATA[
+ let uri = Services.io.newURI(aURI);
+ PlacesUtils.favicons.addFailedFavicon(uri);
+ ]]>
+ </body>
+ </method>
+
+ <method name="getTitleForURI">
+ <parameter name="aURI"/>
+ <body>
+ <![CDATA[
+ try {
+ aURI = Services.uriFixup.createExposableURI(aURI).spec;
+ } catch (e) {
+ aURI = aURI.spec;
+ }
+
+ if (aURI == "about:blank")
+ return "";
+
+ // We have a URI. Let's try to unescape it using a character set
+ // in case the URI is not ASCII.
+ try {
+ let characterSet = this.mCurrentBrowser.contentDocument.characterSet;
+ let textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"]
+ .getService(Ci.nsITextToSubURI);
+ aURI = textToSubURI.unEscapeNonAsciiURI(characterSet, aURI);
+ } catch (e) {
+ // Do nothing.
+ }
+ return aURI;
+ ]]>
+ </body>
+ </method>
+
+ <method name="updateUrlBar">
+ <parameter name="aWebProgress"/>
+ <parameter name="aRequest"/>
+ <parameter name="aLocation"/>
+ <parameter name="aFlags"/>
+ <parameter name="aSecurityUI"/>
+ <parameter name="aBrowser"/>
+ <parameter name="aFeeds"/>
+ <body>
+ <![CDATA[
+ this.mProgressListeners.forEach(
+ function notifyUrlBar(element) {
+ try {
+ if ("onLocationChange" in element)
+ element.onLocationChange(aWebProgress, aRequest, aLocation, aFlags);
+ // If switching tabs, the security may have changed.
+ if (aSecurityUI && "onSecurityChange" in element)
+ element.onSecurityChange(aWebProgress, null, aSecurityUI.state);
+ // If the document already exists, just resend cached data.
+ if (!aRequest && aWebProgress.isTopLevel) {
+ if (aBrowser.mIconURL && "onLinkIconAvailable" in element)
+ element.onLinkIconAvailable(aBrowser.mIconURL);
+ if ("onFeedAvailable" in element) {
+ aFeeds.forEach(
+ function notifyFeedAvailable(feed) {
+ element.onFeedAvailable(feed);
+ }
+ );
+ }
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ );
+ ]]>
+ </body>
+ </method>
+
+ <method name="getWindowTitleForBrowser">
+ <parameter name="aBrowser"/>
+ <body>
+ <![CDATA[
+ var newTitle = "";
+ var docTitle;
+ var docElement = this.ownerDocument.documentElement;
+ var sep = docElement.getAttribute("titlemenuseparator");
+ var modifier = docElement.getAttribute("titlemodifier");
+
+ // Strip out any null bytes in the content title, since the
+ // underlying widget implementations of nsWindow::SetTitle pass
+ // null-terminated strings to system APIs.
+ if (aBrowser.docShell.contentViewer)
+ docTitle = aBrowser.contentTitle.replace(/\0/g, "");
+
+ if (!docTitle && !modifier) {
+ docTitle = this.getTitleForURI(aBrowser.currentURI);
+ if (!docTitle) {
+ // Here we actually override contenttitlesetting, because we
+ // don't want the titledefault value.
+ docTitle = this.mStringBundle.getString("tabs.untitled");
+ }
+ }
+
+ if (docTitle) {
+ newTitle += docElement.getAttribute("titlepreface") + docTitle;
+ if (modifier)
+ newTitle += sep;
+ }
+ newTitle += modifier;
+
+ // If location bar is hidden and the URL type supports a host,
+ // add the scheme and host to the title to prevent spoofing.
+ // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
+ // (only for schemes that support a host)
+ try {
+ if (docElement.getAttribute("chromehidden").includes("location")) {
+ let uri = Services.uriFixup.createExposableURI(
+ aBrowser.currentURI);
+ if (uri.schemeIs("about"))
+ newTitle = uri.spec + sep + newTitle;
+ else if (uri.host)
+ newTitle = uri.prePath + sep + newTitle;
+ }
+ } catch (e) {
+ }
+
+ return newTitle;
+ ]]>
+ </body>
+ </method>
+
+ <method name="updateTitlebar">
+ <body>
+ <![CDATA[
+ var newTitle = this.getWindowTitleForBrowser(this.mCurrentBrowser);
+ document.title = newTitle;
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIBaseWindow).title = newTitle;
+ ]]>
+ </body>
+ </method>
+
+ <method name="updatePopupMenu">
+ <parameter name="aPopupMenu"/>
+ <body>
+ <![CDATA[
+ this.mContextTab = aPopupMenu.triggerNode;
+ // The user might right-click on a tab or an empty part of the tabbar.
+ var isTab = this.mContextTab.localName == "tab";
+ var isMultiple = this.tabs.length > 1;
+ var isAtEnd = this.getTabsToTheEndFrom(this.mContextTab).length == 0;
+ var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "*");
+ for (let menuitem of menuItems) {
+ let tbattr = menuitem.getAttribute("tbattr");
+
+ if (tbattr.includes("tabbrowser-undoclosetab")) {
+ menuitem.disabled = (this.usePrivateBrowsing ?
+ this.savedBrowsers.length :
+ this.mSessionStore.getClosedTabCount(window)) == 0;
+ menuitem.hidden = (this.usePrivateBrowsing ||
+ Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo") <= 0) &&
+ Services.prefs.getIntPref("browser.tabs.max_tabs_undo") <= 0;
+ }
+ else
+ menuitem.disabled =
+ (tbattr.includes("tabbrowser-totheend") && isAtEnd) ||
+ (tbattr.includes("tabbrowser-multiple") && !isMultiple) ||
+ (tbattr.includes("tabbrowser-tab") && !isTab);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="mAeroPeek">false</field>
+
+ <method name="updateCurrentBrowser">
+ <body>
+ <![CDATA[
+ var newBrowser = this.mPanelContainer.selectedPanel.firstChild.firstChild;
+ var oldBrowser = this.mCurrentBrowser;
+
+ // Transfer the dropped link handler to the new browser.
+ // Note: closing the current tab sets mCurrentBrowser to null
+ // so we use mCurrentTab.linkedBrowser instead.
+ newBrowser.droppedLinkHandler = this.mCurrentTab.linkedBrowser.droppedLinkHandler;
+ newBrowser.showWindowResizer = this.mCurrentTab.linkedBrowser.showWindowResizer;
+ newBrowser.docShellIsActive = this.mCurrentTab.linkedBrowser.docShellIsActive;
+ if (this.mCurrentBrowser) {
+ this.mCurrentBrowser.droppedLinkHandler = null;
+ this.mCurrentBrowser.docShellIsActive = false;
+ this.mCurrentBrowser.removeAttribute("primary");
+ this.finder.mListeners.forEach(l => this.mCurrentBrowser.finder.removeResultListener(l));
+ }
+
+ let oldTab = this.mCurrentTab;
+
+ // Preview mode should not reset the owner.
+ if (!this._previewMode && !oldTab.selected)
+ oldTab.owner = null;
+
+ let lastRelatedTab = this.mLastRelatedIndex ? this.tabs[this.mLastRelatedIndex] : null;
+ if (lastRelatedTab && !lastRelatedTab.selected) {
+ lastRelatedTab.owner = null;
+ }
+
+ newBrowser.setAttribute("primary", "true");
+ this.mCurrentBrowser = newBrowser;
+ this.mCurrentTab = this.selectedTab;
+ this.mCurrentTab.removeAttribute("unread");
+ this.finder.mListeners.forEach(l => this.mCurrentBrowser.finder.addResultListener(l));
+
+ var tabListener = this.mTabListeners[this.tabContainer.selectedIndex];
+
+ if (!oldBrowser ||
+ (!oldBrowser.blockedPopups != !newBrowser.blockedPopups))
+ this.mCurrentBrowser.updateBlockedPopups();
+
+ // Update the URL bar.
+ this.updateUrlBar(newBrowser.webProgress,
+ null,
+ newBrowser.currentURI,
+ 0,
+ newBrowser.securityUI,
+ newBrowser,
+ tabListener.mFeeds);
+
+ // Send the state, status and progress to all progress listeners.
+ var flags = tabListener.mStateFlags &
+ (Ci.nsIWebProgressListener.STATE_START |
+ Ci.nsIWebProgressListener.STATE_STOP);
+ this._callProgressListeners(null, "onStateChange",
+ [this.mCurrentBrowser.webProgress,
+ tabListener.mRequest,
+ flags,
+ tabListener.mStatus],
+ true, false);
+
+ this._callProgressListeners(null, "onStatusChange",
+ [this.mCurrentBrowser.webProgress,
+ tabListener.mRequest,
+ tabListener.mStatus,
+ tabListener.mMessage],
+ true, false);
+
+ // Also send the onUpdateCurrentBrowser event for compatibility
+ this._callProgressListeners(null, "onUpdateCurrentBrowser",
+ [tabListener.mStateFlags,
+ tabListener.mStatus,
+ tabListener.mMessage,
+ tabListener.mTotalProgress],
+ true, false);
+
+ if (this.mAeroPeek)
+ return;
+
+ // we only want to return to the parent tab if no other
+ // tabs have been opened and the user hasn't switched tabs
+ this.mPreviousTab = null;
+ this.mLastRelatedIndex = 0;
+
+ // Update the window title.
+ this.updateTitlebar();
+
+ // FAYT
+ this.fastFind.setDocShell(this.mCurrentBrowser.docShell);
+
+ // We've selected the new tab, so go ahead and notify listeners
+ this.mCurrentTab.dispatchEvent(new Event("TabSelect",
+ { bubbles: true, cancelable: false }));
+
+ if (!document.commandDispatcher.focusedElement ||
+ document.commandDispatcher.focusedElement.parentNode !=
+ this.mCurrentTab.parentNode) {
+ // The focus was not on one of our tabs, so focus the new browser.
+ newBrowser.focus();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="onTabClick">
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ // A middle mouse button click on a tab is a short cut for
+ // closing that tab.
+ if (event.button != 1 || event.target.localName != 'tab')
+ return;
+
+ this.removeTab(event.target);
+ event.stopPropagation();
+ event.preventDefault();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onLinkEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ var link = aEvent.originalTarget;
+ var href = link.href;
+ if (!href)
+ return;
+
+ var targetDoc = link.ownerDocument;
+ var index = this.getBrowserIndexForDocument(targetDoc);
+ if (index < 0)
+ return;
+
+ var rel = link.rel;
+ var type = link.type;
+ var isIcon = /(?:^|\s)icon(?:\s|$)/i.test(rel) &&
+ Services.prefs.getBoolPref("browser.chrome.site_icons");
+ if (isIcon) {
+ var iconUri = this.getLinkIconURI(link);
+ if (iconUri)
+ this.setIcon(this.tabs[index], iconUri,
+ link.nodePrincipal);
+ return;
+ }
+
+ if (aEvent.type == "DOMLinkChanged")
+ return;
+
+ var isFeed = /(?:^|\s)feed(?:\s|$)/i.test(rel) ||
+ (/(?:^|\s)alternate(?:\s|$)/i.test(rel) &&
+ !/(?:^|\s)stylesheet(?:\s|$)/i.test(rel) &&
+ /^\s*application\/(?:atom|rss)\+xml\s*$/i.test(type));
+
+ if (!isFeed)
+ return;
+
+ try {
+ let feedURI = Services.io.newURI(href, targetDoc.characterSet);
+ if (!/^https?$/.test(feedURI.scheme)) {
+ return;
+ }
+ urlSecurityCheck(feedURI, targetDoc.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+ } catch(e) {
+ return;
+ }
+
+ this.mTabListeners[index].addFeed(link);
+ if (this.browsers[index] == this.mCurrentBrowser) {
+ this.mProgressListeners.forEach(
+ function notifyFeedAvailable(element) {
+ if ("onFeedAvailable" in element) {
+ try {
+ element.onFeedAvailable(link);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+ );
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="getLinkIconURI">
+ <parameter name="aLink"/>
+ <body><![CDATA[
+ var targetDoc = aLink.ownerDocument;
+ // Make a URI out of our href.
+ var uri = Services.io.newURI(aLink.href, targetDoc.characterSet);
+
+ // Verify that the load of this icon is legal.
+ // Some error or special pages can load their favicon.
+ // To be on the safe side, only allow chrome:// favicons.
+ const re = /^about:(neterror|certerror|blocked)\?/;
+ var isAllowedPage = re.test(targetDoc.documentURI);
+
+ if (!isAllowedPage || !uri.schemeIs("chrome")) {
+ try {
+ urlSecurityCheck(uri,targetDoc.nodePrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ } catch(e) {
+ return null;
+ }
+ }
+
+ // Security says okay, now ask content policy
+ try {
+ var contentPolicy =
+ Cc['@mozilla.org/layout/content-policy;1']
+ .getService(Ci.nsIContentPolicy);
+ } catch (e) {
+ return null; // Refuse to load if we can't do a security check.
+ }
+
+ if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE,
+ uri, targetDoc.documentURIObject,
+ aLink, aLink.type,
+ null) != Ci.nsIContentPolicy.ACCEPT) {
+ return null;
+ }
+ return uri;
+ ]]></body>
+ </method>
+
+ <method name="_tabAttrModified">
+ <parameter name="aTab"/>
+ <parameter name="aChanged"/>
+ <body><![CDATA[
+ if (aTab.closing)
+ return;
+
+ let event = new CustomEvent("TabAttrModified", {
+ bubbles: true,
+ cancelable: false,
+ detail: {
+ changed: aChanged,
+ }
+ });
+ aTab.dispatchEvent(event);
+ ]]></body>
+ </method>
+
+ <method name="setTabTitleLoading">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ aTab.label = this.mStringBundle.getString("tabs.loading");
+ aTab.crop = "end";
+ this._tabAttrModified(aTab, ["label", "crop"]);
+ ]]>
+ </body>
+ </method>
+
+ <method name="setTabTitle">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ var browser = aTab.linkedBrowser;
+ var title = browser.contentTitle;
+ var crop = "end";
+
+ if (!title) {
+ title = this.getTitleForURI(browser.currentURI);
+
+ if (title)
+ crop = "center";
+ else
+ title = this.mStringBundle.getString("tabs.untitled");
+ }
+ aTab.label = title;
+ aTab.crop = crop;
+ ]]>
+ </body>
+ </method>
+
+ <method name="setStripVisibilityTo">
+ <parameter name="aShow"/>
+ <body>
+ <![CDATA[
+ this.mStrip.collapsed = !aShow;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getStripVisibility">
+ <body>
+ return !this.mStrip.collapsed;
+ </body>
+ </method>
+
+ <method name="loadOneTab">
+ <parameter name="aURI"/>
+ <parameter name="aReferrerURI"/>
+ <parameter name="aCharset"/>
+ <parameter name="aPostData"/>
+ <parameter name="aLoadInBackground"/>
+ <parameter name="aAllowThirdPartyFixup"/>
+ <body>
+ <![CDATA[
+ var params = aReferrerURI;
+ if (!params || params instanceof Ci.nsIURI) {
+ params = {
+ triggeringPrincipal: Services.scriptSecurityManager
+ .getSystemPrincipal(),
+ referrerURI: aReferrerURI,
+ charset: aCharset,
+ postData: aPostData,
+ inBackground: aLoadInBackground,
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ allowMixedContent: false,
+ userContextId: null,
+ opener: null,
+ };
+ }
+
+ params.focusNewTab = params.inBackground != null ?
+ !params.inBackground :
+ !Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+
+ if (params.focusNewTab)
+ params.ownerTab = this.selectedTab;
+
+ return this.addTab(aURI, params);
+ ]]>
+ </body>
+ </method>
+
+ <method name="loadTabs">
+ <parameter name="aURIs"/>
+ <parameter name="aLoadInBackground"/>
+ <parameter name="aReplace"/>
+ <body><![CDATA[
+ let aAllowThirdPartyFixup;
+ let aPostDatas = [];
+ let aUserContextId;
+ let aTriggeringPrincipal;
+
+ // Additional parameters are in a params object.
+ // Firefox uses additional parameters not supported here.
+ if (arguments.length == 2 &&
+ typeof arguments[1] == "object") {
+ let params = arguments[1];
+ aLoadInBackground = params.inBackground;
+ aReplace = params.replace;
+ aAllowThirdPartyFixup = params.allowThirdPartyFixup;
+ aPostDatas = params.postDatas || aPostDatas;
+ aUserContextId = params.userContextId;
+ aTriggeringPrincipal = params.triggeringPrincipal;
+ }
+
+ if (!aURIs.length)
+ return;
+
+ // The tab selected after this new tab is closed (i.e. the new tab's
+ // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
+ // when several urls are opened here (i.e. closing the first should select
+ // the next of many URLs opened) or if the pref to have UI links opened in
+ // the background is set (i.e. the link is not being opened modally)
+ //
+ // i.e.
+ // Number of URLs Load UI Links in BG Focus Last Viewed?
+ // == 1 false YES
+ // == 1 true NO
+ // > 1 false/true NO
+ var multiple = aURIs.length > 1;
+ var owner = multiple || aLoadInBackground ? null : this.selectedTab;
+ var firstTabAdded = null;
+ var targetTabIndex = -1;
+
+ if (aReplace) {
+ let browser;
+ browser = this.mCurrentBrowser;
+ targetTabIndex = this.tabContainer.selectedIndex;
+
+ let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+ if (aAllowThirdPartyFixup) {
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
+ Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
+ }
+ try {
+ browser.loadURIWithFlags(aURIs[0], {
+ flags,
+ postData: aPostDatas[0],
+ triggeringPrincipal : aTriggeringPrincipal,
+ });
+ } catch (e) {
+ // Ignore failure in case a URI is wrong, so we can continue
+ // opening the next ones.
+ }
+ } else {
+ firstTabAdded = this.addTab(aURIs[0], {
+ ownerTab: owner,
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ postData: aPostDatas[0],
+ userContextId: aUserContextId,
+ triggeringPrincipal: aTriggeringPrincipal,
+ });
+ }
+
+ let tabNum = targetTabIndex;
+ for (let i = 1; i < aURIs.length; ++i) {
+ let tab = this.addTab(aURIs[i], {
+ allowThirdPartyFixup: aAllowThirdPartyFixup,
+ postData: aPostDatas[i],
+ userContextId: aUserContextId,
+ triggeringPrincipal: aTriggeringPrincipal,
+ });
+ if (targetTabIndex !== -1)
+ this.moveTabTo(tab, ++tabNum);
+ }
+
+ if (!aLoadInBackground) {
+ if (firstTabAdded) {
+ // .selectedTab setter focuses the content area
+ this.selectedTab = firstTabAdded;
+ } else
+ this.selectedBrowser.focus();
+ }
+ ]]></body>
+ </method>
+
+ <method name="addTab">
+ <parameter name="aURI"/>
+ <parameter name="aReferrerURI"/>
+ <parameter name="aCharset"/>
+ <parameter name="aPostData"/>
+ <parameter name="aFocusNewTab"/>
+ <parameter name="aAllowThirdPartyFixup"/>
+ <body>
+ <![CDATA[
+ var aTriggeringPrincipal;
+ var aReferrerPolicy;
+ var aFromExternal;
+ var aOwner;
+ var aRelatedToCurrent;
+ var aAllowMixedContent;
+ var aNoReferrer;
+ var aUserContextId;
+ var aOriginPrincipal;
+ var aOpener;
+ if (arguments.length == 2 &&
+ arguments[1] != null &&
+ typeof arguments[1] == "object" &&
+ !(arguments[1] instanceof Ci.nsIURI)) {
+ let params = arguments[1];
+ aTriggeringPrincipal = params.triggeringPrincipal;
+ aReferrerURI = params.referrerURI;
+ aReferrerPolicy = params.referrerPolicy;
+ aCharset = params.charset;
+ aPostData = params.postData;
+ aOwner = params.ownerTab;
+ aFocusNewTab = params.focusNewTab;
+ aAllowThirdPartyFixup = params.allowThirdPartyFixup;
+ aFromExternal = params.fromExternal;
+ aRelatedToCurrent = params.relatedToCurrent;
+ aAllowMixedContent = params.allowMixedContent;
+ aNoReferrer = params.noReferrer;
+ aUserContextId = params.userContextId;
+ aOriginPrincipal = params.originPrincipal;
+ aOpener = params.opener;
+ }
+
+ // If we're adding tabs, we're past interrupt mode, ditch the owner.
+ if (this.mCurrentTab.owner)
+ this.mCurrentTab.owner = null;
+
+ this._browsers = null; // invalidate cache
+
+ var t = this.referenceTab.cloneNode(true);
+
+ var blank = !aURI || aURI == "about:blank";
+
+ if (!blank)
+ t.setAttribute("label", aURI);
+
+ this.tabContainer.appendChild(t);
+
+ var b = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "browser");
+ b.setAttribute("type", "content");
+ b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
+ b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
+ b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
+ if (this.hasAttribute("datetimepicker")) {
+ b.setAttribute("datetimepicker", this.getAttribute("datetimepicker"));
+ }
+
+ // Check if we have a "parent" window which we need to set as our opener
+ if (aOpener) {
+ b.presetOpenerWindow(aOpener);
+ }
+
+ // Create the browserStack container
+ var stack = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "stack");
+ stack.setAttribute("anonid", "browserStack");
+ stack.appendChild(b);
+ stack.setAttribute("flex", "1");
+
+ // Add the Message and the Browser to the box
+ var n = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "notificationbox");
+ n.setAttribute("class", "browser-notificationbox");
+ n.setAttribute("popupnotification", this.getAttribute("popupnotification"));
+ n.appendChild(stack);
+
+ var uniqueId = "panel" + this.nextTabNumber++;
+ n.id = uniqueId;
+ t.linkedPanel = uniqueId;
+ t.linkedBrowser = b;
+ if (t.previousSibling.selected)
+ t.setAttribute("afterselected", true);
+
+ // Prevent the superfluous initial load of a blank document
+ // if we're going to load something other than about:blank.
+ if (!blank)
+ b.setAttribute("nodefaultsrc", "true");
+
+ // NB: this appendChild call causes us to run constructors for the
+ // browser element, which fires off a bunch of notifications. Some
+ // of those notifications can cause code to run that inspects our
+ // state, so it is important that the tab element is fully
+ // initialized by this point.
+ this.mPanelContainer.appendChild(n);
+
+ // We start our browsers out as inactive.
+ b.docShellIsActive = false;
+
+ this.mStrip.collapsed = false;
+
+ Services.prefs.setBoolPref("browser.tabs.forceHide", false);
+
+ // If this new tab is owned by another, assert that relationship.
+ if (aOwner)
+ t.owner = aOwner;
+
+ // wire up a progress listener for the new browser object.
+ var position = this.tabs.length - 1;
+ var tabListener = this.mTabProgressListener(t, b, blank);
+ const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
+ .createInstance(Ci.nsIWebProgress);
+ filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
+ b.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
+ this.mTabListeners[position] = tabListener;
+ this.mTabFilters[position] = filter;
+
+ if (!blank) {
+ // pretend the user typed this so it'll be available till
+ // the document successfully loads
+ b.userTypedValue = aURI;
+
+ let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+ if (aAllowThirdPartyFixup)
+ flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
+ Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
+ if (aFromExternal)
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
+ if (aAllowMixedContent)
+ flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
+ try {
+ b.loadURIWithFlags(aURI, {
+ flags,
+ triggeringPrincipal : aTriggeringPrincipal,
+ referrerURI: aNoReferrer ? null : aReferrerURI,
+ charset: aCharset,
+ referrerPolicy: aReferrerPolicy,
+ postData: aPostData,
+ });
+ }
+ catch (ex) { }
+ }
+
+ t.dispatchEvent(new Event("TabOpen",
+ { bubbles: true, cancelable: false }));
+
+ // Check if we're opening a tab related to the current tab and
+ // move it to after the current tab.
+ // aReferrerURI is null or undefined if the tab is opened from
+ // an external application or bookmark, i.e. somewhere other
+ // than the current tab.
+ if ((aRelatedToCurrent || aReferrerURI ||
+ Services.prefs.getBoolPref("browser.tabs.insertAllTabsAfterCurrent")) &&
+ Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
+ var lastRelatedIndex = this.mLastRelatedIndex ||
+ this.tabContainer.selectedIndex;
+ if (this.mLastRelatedIndex)
+ this.tabs[this.mLastRelatedIndex].owner = null;
+ else
+ t.owner = this.selectedTab;
+ this.moveTabTo(t, ++lastRelatedIndex);
+ this.mLastRelatedIndex = lastRelatedIndex;
+ }
+
+ if (aFocusNewTab) {
+ var parentTab = this.selectedTab;
+ this.selectedTab = t;
+ this.mPreviousTab = parentTab;
+ }
+ else {
+ // The user opened a background tab, so updateCurrentBrowser
+ // won't be called. Explicitly clear the previous tab.
+ this.mPreviousTab = null;
+ }
+ this.tabContainer._handleNewTab(t);
+
+ return t;
+ ]]>
+ </body>
+ </method>
+
+ <method name="warnAboutClosingTabs">
+ <parameter name="aCloseTabs"/>
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ var tabsToClose;
+ switch (aCloseTabs) {
+ case this.closingTabsEnum.ALL:
+ tabsToClose = this.tabs.length;
+ break;
+ case this.closingTabsEnum.OTHER:
+ tabsToClose = this.tabs.length - 1;
+ break;
+ case this.closingTabsEnum.TO_END:
+ if (!aTab)
+ throw new Error("Required argument missing: aTab");
+
+ tabsToClose = this.getTabsToTheEndFrom(aTab).length;
+ break;
+ default:
+ throw new Error("Invalid argument: " + aCloseTabs);
+ }
+
+ if (tabsToClose <= 1)
+ return true;
+
+ const pref = aCloseTabs == this.closingTabsEnum.ALL ?
+ "browser.tabs.warnOnClose" :
+ "browser.tabs.warnOnCloseOther";
+ if (!Services.prefs.getBoolPref(pref))
+ return true;
+
+ //default to true: if it were false, we wouldn't get this far
+ var warnOnClose = { value:true };
+ var bundle = this.mStringBundle;
+
+ // Focus the window before prompting. This will raise any minimized
+ // window, which will make it obvious which window the prompt is
+ // for and will solve the problem of windows "obscuring" the
+ // prompt. See bug #350299 for more details.
+ window.focus();
+ var warningTitle;
+ var warningMessage;
+ var closeButton;
+ var promptMessage;
+ switch (aCloseTabs) {
+ case this.closingTabsEnum.ALL:
+ warningTitle = "tabs.closeWarningTitleAll";
+ warningMessage =
+ PluralForm.get(tabsToClose,
+ bundle.getString("tabs.closeWarningAll"));
+ closeButton = "tabs.closeButtonAll";
+ promptMessage = "tabs.closeWarningPromptMeAll";
+ break;
+ case this.closingTabsEnum.OTHER:
+ // fall through
+ case this.closingTabsEnum.TO_END:
+ // fall through
+ default:
+ warningTitle = "tabs.closeWarningTitle";
+ warningMessage =
+ PluralForm.get(tabsToClose,
+ bundle.getString("tabs.closeWarningOther"));
+ closeButton = "tabs.closeButton";
+ promptMessage = "tabs.closeWarningPromptMe";
+ break;
+ }
+
+ var ps = Services.prompt;
+ var buttonPressed =
+ ps.confirmEx(window,
+ bundle.getString(warningTitle),
+ warningMessage.replace("#1", tabsToClose),
+ (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0)
+ + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
+ bundle.getString(closeButton),
+ null, null,
+ bundle.getString(promptMessage),
+ warnOnClose);
+ var reallyClose = (buttonPressed == 0);
+ // Don't set the pref unless they press OK and it's false
+ if (reallyClose && !warnOnClose.value)
+ Services.prefs.setBoolPref(pref, false);
+
+ return reallyClose;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getTabsToTheEndFrom">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ let tabsToEnd = [];
+ let tabs = this.tabs;
+ for (let i = tabs.length - 1; tabs[i] != aTab && i >= 0; --i) {
+ tabsToEnd.push(tabs[i]);
+ }
+ return tabsToEnd.reverse();
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeTabsToTheEndFrom">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) {
+ let tabs = this.getTabsToTheEndFrom(aTab);
+ for (let i = tabs.length - 1; i >= 0; --i) {
+ this.removeTab(tabs[i]);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeAllTabsBut">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ if (this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
+ this.selectedTab = aTab;
+
+ for (let i = this.tabs.length - 1; i >= 0; --i) {
+ if (this.tabs[i] != aTab)
+ this.removeTab(this.tabs[i]);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeCurrentTab">
+ <parameter name="aParams"/>
+ <body>
+ <![CDATA[
+ return this.removeTab(this.mCurrentTab, aParams);
+ ]]>
+ </body>
+ </method>
+
+ <method name="isBrowserEmpty">
+ <parameter name="aBrowser"/>
+ <body>
+ <![CDATA[
+ return aBrowser.sessionHistory.count < 2 &&
+ aBrowser.currentURI.spec == "about:blank" &&
+ !aBrowser.contentDocument.body.hasChildNodes();
+ ]]>
+ </body>
+ </method>
+
+ <method name="getUndoList">
+ <body>
+ <![CDATA[
+ var tabData = this.usePrivateBrowsing ? this.savedBrowsers :
+ JSON.parse(this.mSessionStore.getClosedTabData(window));
+ return tabData.map(function(aTabData) { return aTabData.title; });
+ ]]>
+ </body>
+ </method>
+
+ <method name="undoCloseTab">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ if (this.usePrivateBrowsing)
+ return this.savedBrowsers.length ? this.restoreTab(aIndex) : null;
+
+ return this.mSessionStore.getClosedTabCount(window) ?
+ this.mSessionStore.undoCloseTab(window, aIndex) : null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="restoreTab">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ if (aIndex >= this.savedBrowsers.length || aIndex < 0)
+ return null;
+
+ this._browsers = null;
+
+ var savedData = this.savedBrowsers.splice(aIndex, 1)[0];
+ var t = savedData.browserData.tab;
+ var b = savedData.browserData.browser;
+ var hist = savedData.browserData.history;
+
+ this.tabContainer.appendChild(t);
+ if (t.previousSibling.selected)
+ t.setAttribute("afterselected", true);
+
+ // navigate back to the proper page from the light page
+ b.stop();
+ b.webNavigation.gotoIndex(0);
+
+ // reattach the old history
+ b.webNavigation.sessionHistory = hist;
+
+ // add back the filters, security first (bug 313335)
+ var secFlags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
+ Ci.nsIWebProgress.NOTIFY_LOCATION |
+ Ci.nsIWebProgress.NOTIFY_SECURITY;
+ b.webProgress.addProgressListener(b.securityUI, secFlags);
+
+ var position = this.tabs.length - 1;
+ var tabListener = this.mTabProgressListener(t, b, false);
+ const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
+ .createInstance(Ci.nsIWebProgress);
+ filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
+ b.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
+ this.mTabListeners[position] = tabListener;
+ this.mTabFilters[position] = filter;
+
+ t.dispatchEvent(new Event("TabOpen",
+ { bubbles: true, cancelable: false }));
+
+ if (savedData.pos < position)
+ this.moveTabTo(t, savedData.pos);
+
+ if (this.tabs.length == 2 && this.isBrowserEmpty(this))
+ this.removeCurrentTab({ disableUndo: true });
+ else {
+ this.selectedTab = t;
+ this.mStrip.collapsed = false;
+ }
+ this.tabContainer._handleNewTab(t);
+
+ return t;
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeBrowser">
+ <parameter name="aBrowser"/>
+ <body>
+ <![CDATA[
+ let panel = this.getNotificationBox(aBrowser);
+ panel.destroy();
+ aBrowser.destroy();
+
+ // The pagehide event that this removal triggers is safe
+ // because the browser is no longer current at this point.
+ panel.remove();
+
+ // Fix up the selected panel.
+ panel = this.getNotificationBox(this.selectedTab.linkedBrowser);
+ this.mTabBox.selectedPanel = panel;
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeTab">
+ <parameter name="aTab"/>
+ <parameter name="aParams"/>
+ <body>
+ <![CDATA[
+ this.mLastRelatedIndex = 0;
+
+ if (!aParams) {
+ aParams = {
+ animate: false,
+ disableUndo: false
+ };
+ }
+
+ var oldBrowser = aTab.linkedBrowser;
+
+ var ds = oldBrowser.docShell;
+
+ if (ds.contentViewer && !ds.contentViewer.permitUnload())
+ return;
+
+ // We're committed to closing the tab now.
+ var l = this.tabs.length;
+ switch (l) {
+ case 1:
+ // add a new blank tab to replace the one we're about to close
+ // (this ensures that the remaining tab is as good as new)
+ this.addTab("about:blank");
+ l++;
+ // fall through
+ case 2:
+ if (Services.prefs.getBoolPref("browser.tabs.autoHide"))
+ this.mStrip.collapsed = true;
+ }
+
+ // Dispatch a notification.
+ // We dispatch it before any teardown so that event listeners can
+ // inspect the tab that's about to close.
+ aTab.dispatchEvent(new UIEvent("TabClose",
+ { bubbles: true, cancelable: false, view: window,
+ detail: !!aParams.disableUndo }));
+ var tabData = aTab.tabData || {};
+ tabData.pos = this.getTabIndex(aTab);
+ tabData.panel = this.getNotificationBox(oldBrowser).id;
+ tabData.title = oldBrowser.contentDocument.title ||
+ this.getTitleForURI(oldBrowser.currentURI) ||
+ this.mStringBundle.getString("tabs.untitled");
+
+ var index = this.getTabIndex(aTab);
+
+ // Remove SSL listener
+ oldBrowser.webProgress.removeProgressListener(oldBrowser.securityUI);
+
+ // Remove the tab's filter and progress listener.
+ const filter = this.mTabFilters[index];
+ oldBrowser.webProgress.removeProgressListener(filter);
+ filter.removeProgressListener(this.mTabListeners[index]);
+ this.mTabFilters.splice(index, 1);
+ this.mTabListeners.splice(index, 1);
+
+ // We are no longer the primary content area
+ oldBrowser.removeAttribute("primary");
+
+ // Remove this tab as the owner of any other tabs, since it's going away.
+ for (let tab of this.tabs) {
+ if ("owner" in tab && tab.owner == aTab)
+ // |tab| is a child of the tab we're removing, make it an orphan.
+ tab.owner = null;
+ }
+
+ // Now select the new tab before nuking the old one.
+ var currentIndex = this.tabContainer.selectedIndex;
+
+ var newIndex = -1;
+ if (currentIndex > index)
+ newIndex = currentIndex - 1;
+ else if (currentIndex < index)
+ newIndex = currentIndex;
+ else if (index == l - 1)
+ newIndex = index - 1;
+ else
+ newIndex = index;
+
+ if (oldBrowser == this.mCurrentBrowser)
+ this.mCurrentBrowser = null;
+
+ // Invalidate browsers cache, as the tab is removed from the
+ // tab container.
+ this._browsers = null;
+
+ let owner = ("owner" in aTab) ? aTab.owner : null;
+
+ // Clean up before/after selected attributes before removing the
+ // tab.
+ aTab._selected = false;
+ aTab.remove();
+
+ // When the current tab is removed select a new tab
+ // and fire select events on tabpanels and tabs
+ if (owner && !owner.hidden && !owner.closing &&
+ Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
+ this.selectedTab = owner;
+ }
+ else if (this.mPreviousTab && (aTab == this.mCurrentTab))
+ this.selectedTab = this.mPreviousTab;
+ else {
+ this.tabContainer.selectedIndex = newIndex;
+
+ // We need to explicitly clear this, because updateCurrentBrowser
+ // doesn't get called for a background tab
+ this.mPreviousTab = null;
+ }
+
+ // Save the tab for undo.
+ // Even though we navigate to about:blank, it costs more RAM than
+ // really closing the tab. The pref controls how far you can undo
+ var maxUndoDepth = Services.prefs.getIntPref("browser.tabs.max_tabs_undo");
+ var oldSH = oldBrowser.webNavigation.sessionHistory;
+ var inOnLoad = oldBrowser.docShell.isExecutingOnLoadHandler;
+ var isPopup = oldBrowser.contentWindow.opener &&
+ !Services.prefs.getBoolPref("browser.tabs.cache_popups");
+ if (maxUndoDepth <= 0 || aParams.disableUndo || inOnLoad || isPopup || this.isBrowserEmpty(oldBrowser)) {
+ // Undo is disabled/tab is blank. Kill the browser for real.
+ // Because of the way XBL works (fields just set JS
+ // properties on the element) and the code we have in place
+ // to preserve the JS objects for any elements that have
+ // JS properties set on them, the browser element won't be
+ // destroyed until the document goes away. So we force a
+ // cleanup ourselves. Also fix up the selected panel in the case
+ // the removed browser was to the left of the current browser.
+ this.removeBrowser(oldBrowser);
+ return;
+ }
+
+ // preserve a pointer to the browser for undoing the close
+ // 1. save a copy of the session history (oldSH)
+ // 2. hook up a new history
+ // 3. add the last history entry from the old history the new
+ // history so we'll be able to go back from about:blank
+ // 4. load a light URL in the browser, pushing the current page
+ // into bfcache - allows for saving of JS modifications
+ // and also saves RAM by allowing bfcache to evict the full page
+
+ tabData.browserData = {
+ tab: aTab,
+ browser: oldBrowser,
+ history: oldSH,
+ toJSON: function() {} // hides this object from JSON.stringify
+ };
+ this.savedBrowsers.unshift(tabData);
+
+ var newSH = Cc["@mozilla.org/browser/shistory;1"]
+ .createInstance(Ci.nsISHistory);
+ oldBrowser.webNavigation.sessionHistory = newSH;
+ var entry = oldSH.getEntryAtIndex(oldSH.index)
+ .QueryInterface(Ci.nsISHEntry)
+ .clone();
+ // The bfcache entry is tightly coupled to the original shistory it
+ // belongs to, better to drop it.
+ entry.abandonBFCacheEntry();
+ // don't try to repost data when restoring the tab
+ entry.postData = null;
+ newSH.addEntry(entry, true);
+
+ // about:blank is light
+ oldBrowser.loadURI("about:blank");
+
+ // remove overflow from the undo stack
+ if (this.savedBrowsers.length > maxUndoDepth) {
+ tabData = this.savedBrowsers.pop();
+ var deadBrowser = tabData.browserData.browser;
+ delete tabData.browserData;
+ this.removeBrowser(deadBrowser);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="forgetSavedBrowser">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ if (aIndex >= this.savedBrowsers.length || aIndex < 0)
+ return false;
+
+ var tabData = this.savedBrowsers.splice(aIndex, 1)[0];
+ var deadBrowser = tabData.browserData.browser;
+ delete tabData.browserData;
+ this.removeBrowser(deadBrowser);
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="reloadAllTabs">
+ <body>
+ <![CDATA[
+ var l = this.tabs.length;
+ for (var i = 0; i < l; i++) {
+ try {
+ this.tabs[i].linkedBrowser.reload();
+ } catch (e) {
+ // ignore failure to reload so others will be reloaded
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="reloadTab">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ this.getBrowserForTab(aTab).reload();
+ ]]>
+ </body>
+ </method>
+
+ <method name="addProgressListener">
+ <parameter name="aListener"/>
+ <body>
+ <![CDATA[
+ if (!aListener)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ if (this.mProgressListeners.includes(aListener))
+ throw Cr.NS_ERROR_FAILURE;
+
+ // push() does not disturb possibly ongoing iterations.
+ this.mProgressListeners.push(aListener);
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeProgressListener">
+ <parameter name="aListener"/>
+ <body>
+ <![CDATA[
+ if (!this.mProgressListeners.includes(aListener))
+ throw Cr.NS_ERROR_FAILURE;
+
+ // Create a new array, not to disturb possibly ongoing iterations.
+ this.mProgressListeners =
+ this.mProgressListeners.filter(
+ function removeListener(element) {
+ return element != aListener;
+ }
+ );
+ ]]>
+ </body>
+ </method>
+
+ <method name="addTabsProgressListener">
+ <parameter name="aListener"/>
+ <body>
+ <![CDATA[
+ if (!aListener)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ if (this.mTabsProgressListeners.includes(aListener))
+ throw Cr.NS_ERROR_FAILURE;
+
+ // push() does not disturb possibly ongoing iterations.
+ this.mTabsProgressListeners.push(aListener);
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeTabsProgressListener">
+ <parameter name="aListener"/>
+ <body>
+ <![CDATA[
+ if (!this.mTabsProgressListeners.includes(aListener))
+ throw Cr.NS_ERROR_FAILURE;
+
+ // Create a new array, not to disturb possibly ongoing iterations.
+ this.mTabsProgressListeners =
+ this.mTabsProgressListeners.filter(
+ function removeListener(element) {
+ return element != aListener;
+ }
+ );
+ ]]>
+ </body>
+ </method>
+
+ <method name="_getTabForContentWindow">
+ <parameter name="aWindow"/>
+ <body>
+ <![CDATA[
+ const browsers = this.browsers;
+ for (var i = 0; i < browsers.length; ++i)
+ if (browsers[i].contentWindow == aWindow)
+ return this.tabs[i];
+
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getTabForBrowser">
+ <parameter name="aBrowser"/>
+ <body>
+ <![CDATA[
+ for (var tab of this.tabs)
+ if (tab.linkedBrowser == aBrowser)
+ return tab;
+
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserForTab">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ return aTab.linkedBrowser;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserForOuterWindowID">
+ <parameter name="aID"/>
+ <body>
+ <![CDATA[
+ for (var browser of this.browsers)
+ if (browser.outerWindowID == aID)
+ return browser;
+
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getTabIndex">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ for (var i = 0; i < this.tabs.length; ++i)
+ if (this.tabs[i] == aTab)
+ return i;
+
+ throw Cr.NS_ERROR_ILLEGAL_VALUE;
+ ]]>
+ </body>
+ </method>
+
+ <property name="popupAnchor" readonly="true">
+ <getter><![CDATA[
+ if (this.mCurrentTab._popupAnchor) {
+ return this.mCurrentTab._popupAnchor;
+ }
+ // Actually the notificationbox not the browserstack.
+ let stack = this.mCurrentBrowser.parentNode;
+ // Create an anchor for the popup
+ const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ let popupAnchor = document.createElementNS(NS_XUL, "hbox");
+ popupAnchor.className = "popup-anchor";
+ popupAnchor.hidden = true;
+ stack.appendChild(popupAnchor);
+ return this.mCurrentTab._popupAnchor = popupAnchor;
+ ]]></getter>
+ </property>
+
+ <method name="selectTabAtIndex">
+ <parameter name="aIndex"/>
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ // count backwards for aIndex < 0
+ if (aIndex < 0)
+ aIndex += this.tabs.length;
+
+ if (aIndex >= 0 &&
+ aIndex < this.tabs.length &&
+ aIndex != this.tabContainer.selectedIndex)
+ this.selectedTab = this.tabs[aIndex];
+
+ if (aEvent) {
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="selectedTab">
+ <getter>
+ return this.mTabBox.selectedTab;
+ </getter>
+ <setter>
+ <![CDATA[
+ // Update the tab
+ this.mTabBox.selectedTab = val;
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="selectedBrowser"
+ onget="return this.mCurrentBrowser;"
+ readonly="true"/>
+
+ <property name="browsers" readonly="true">
+ <getter>
+ <![CDATA[
+ return this._browsers ||
+ (this._browsers = Array.from(this.tabs, tab => tab.linkedBrowser));
+ ]]>
+ </getter>
+ </property>
+
+ <!-- Drag and drop observer API -->
+ <method name="_onDragStart">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ var target = aEvent.target;
+ if (target.localName == "tab") {
+ var URI = target.linkedBrowser.currentURI;
+ var spec = URI.spec;
+ var title = target.linkedBrowser.contentTitle || spec;
+ var dt = aEvent.dataTransfer;
+ dt.mozSetDataAt("text/x-moz-url", spec + "\n" + title, 0);
+ dt.mozSetDataAt("text/uri-list", spec, 0);
+ dt.mozSetDataAt("text/plain", spec, 0);
+ dt.mozSetDataAt("text/html", '<a href="' + spec + '">' + title + '</a>', 0);
+ }
+ aEvent.stopPropagation();
+ ]]>
+ </body>
+ </method>
+
+ <method name="_onDragOver">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+
+ var ib = document.getAnonymousElementByAttribute(this, "class", "tab-drop-indicator-bar");
+
+ // autoscroll the tab strip if we drag over the scroll buttons,
+ // even if we aren't dragging a tab
+ var pixelsToScroll = 0;
+ var tabStrip = this.tabContainer.arrowScrollbox;
+ var ltr = window.getComputedStyle(this, null).direction == "ltr";
+ if (this.tabContainer.getAttribute("overflow") == "true") {
+ var targetAnonid = aEvent.originalTarget.getAttribute("anonid");
+ switch (targetAnonid) {
+ case "scrollbutton-up":
+ pixelsToScroll = -tabStrip.scrollIncrement;
+ break;
+ case "scrollbutton-down":
+ case "alltabs-button":
+ pixelsToScroll = tabStrip.scrollIncrement;
+ break;
+ }
+ if (pixelsToScroll)
+ tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
+ }
+
+ var ind = document.getAnonymousElementByAttribute(this, "class", "tab-drop-indicator");
+
+ var draggedTab = aEvent.dataTransfer.mozSourceNode;
+ var within = draggedTab &&
+ draggedTab.parentNode == this.tabContainer;
+ var newIndexOn = within ? -1 : this.getDropOnIndex(aEvent);
+
+ var ltr = window.getComputedStyle(this, null).direction == "ltr";
+ var arrowX, tabBoxObject;
+ if (newIndexOn != -1) {
+ tabBoxObject = this.tabs[newIndexOn].boxObject;
+ arrowX = tabBoxObject.screenX + tabBoxObject.width / 2;
+ }
+ else {
+ var newIndexBetween = this.getDropIndex(aEvent);
+ if (within) {
+ var tabIndex = this.getTabIndex(draggedTab);
+ if (newIndexBetween == tabIndex ||
+ newIndexBetween == tabIndex + 1) {
+ ib.collapsed = true;
+ return;
+ }
+ }
+
+ if (newIndexBetween == this.tabs.length) {
+ tabBoxObject = this.tabs[this.tabs.length - 1].boxObject;
+ arrowX = tabBoxObject.x;
+ arrowX = tabBoxObject.screenX;
+ if (ltr) // for LTR "after" is on the right-hand side of the tab
+ arrowX += tabBoxObject.width;
+ }
+ else {
+ tabBoxObject = this.tabs[newIndexBetween].boxObject;
+ arrowX = tabBoxObject.screenX;
+ if (!ltr) // for RTL "before" is on the right-hand side of the tab
+ arrowX += tabBoxObject.width;
+ }
+ }
+
+ var boxObject = tabStrip.scrollBoxObject;
+ // Check pixelsToScroll as well to prevent noticable judder.
+ if (pixelsToScroll > 0 || arrowX >= boxObject.screenX + boxObject.width)
+ arrowX = boxObject.screenX + boxObject.width;
+ else if (pixelsToScroll < 0 || arrowX < boxObject.screenX)
+ arrowX = boxObject.screenX;
+
+ if (ltr)
+ ind.style.marginLeft = (arrowX - this.boxObject.screenX) + "px";
+ else
+ ind.style.marginRight = (this.boxObject.screenX + this.boxObject.width - arrowX) + "px";
+
+ ib.collapsed = false;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_onDrop">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ document.getAnonymousElementByAttribute(this, "class",
+ "tab-drop-indicator-bar")
+ .collapsed = true;
+ aEvent.stopPropagation();
+
+ var newIndex = this.getDropIndex(aEvent);
+ var dt = aEvent.dataTransfer;
+ var draggedTab = dt.mozSourceNode;
+ if (draggedTab && draggedTab.parentNode == this.tabContainer) {
+ if (newIndex > this.getTabIndex(draggedTab))
+ newIndex--;
+ this.moveTabTo(draggedTab, newIndex);
+ return;
+ }
+
+ var url;
+ try {
+ // Pass true to disallow dropping javascript: or data: urls.
+ url = Services.droppedLinkHandler.dropLink(aEvent, {}, true);
+ } catch (ex) {}
+
+ // Valid urls don't contain spaces ' '; if we have a space
+ // it isn't a valid url.
+ if (!url || url.includes(" "))
+ return;
+
+ getShortcutOrURIAndPostData(url).then(data => {
+ var bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
+ if (aEvent.shiftKey)
+ bgLoad = !bgLoad;
+
+ let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent);
+
+ var tab = null;
+ tabIndex = this.getDropOnIndex(aEvent);
+ if (tabIndex != -1) {
+ // Load in an existing tab.
+ tab = this.tabs[tabIndex];
+ tab.linkedBrowser.loadURI(data.url, {
+ allowThirdPartyFixup: true,
+ triggeringPrincipal,
+ });
+ if (this.mCurrentTab != tab && !bgLoad)
+ this.selectedTab = tab;
+ }
+ else if (dt.mozSourceDocument &&
+ dt.mozSourceDocument.defaultView.top == content) {
+ // We're adding a new tab, and we may want parent-tab tracking.
+
+ tab = this.loadOneTab(data.url, {
+ inBackground: bgLoad,
+ allowThirdPartyFixup: true,
+ triggeringPrincipal,
+ });
+
+ this.moveTabTo(tab, newIndex);
+ }
+ else {
+ // We're adding a new tab, but do not want parent-tab tracking.
+ tab = this.addTab(data.url, {
+ allowThirdPartyFixup: true,
+ triggeringPrincipal,
+ });
+
+ this.moveTabTo(tab, newIndex);
+ if (this.mCurrentTab != tab && !bgLoad)
+ this.selectedTab = tab;
+ }
+ });
+ ]]>
+ </body>
+ </method>
+
+ <method name="_onDragLeave">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ var target = aEvent.relatedTarget;
+ while (target && target != this.mStrip)
+ target = target.parentNode;
+
+ if (target)
+ return;
+
+ document.getAnonymousElementByAttribute(this, "class",
+ "tab-drop-indicator-bar")
+ .collapsed = true;
+ aEvent.stopPropagation();
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabTo">
+ <parameter name="aTab"/>
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ let oldPosition;
+ // for compatibility with extensions
+ if (typeof(aTab) == "number") {
+ oldPosition = aTab;
+ aTab = this.tabs[oldPosition];
+ } else {
+ oldPosition = this.getTabIndex(aTab);
+ }
+
+ if (oldPosition == aIndex)
+ return;
+
+ this.mLastRelatedIndex = 0;
+
+ this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(oldPosition, 1)[0]);
+ this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(oldPosition, 1)[0]);
+
+ let wasFocused = (document.activeElement == this.mCurrentTab);
+
+ if (aIndex >= oldPosition)
+ ++aIndex;
+ this.mCurrentTab._selected = false;
+
+ // invalidate cache
+ this._browsers = null;
+
+ // Use .item() instead of [] because dragging to the end of the
+ // strip goes out of bounds: .item() returns null (so it acts like
+ // appendChild), but [] throws.
+ var tab = this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
+
+ this.mCurrentTab._selected = true;
+
+ if (wasFocused)
+ this.mCurrentTab.focus();
+
+ this.tabContainer._handleTabSelect(false);
+
+ tab.dispatchEvent(new UIEvent("TabMove",
+ { bubbles: true, cancelable: false, view: window,
+ detail: oldPosition }));
+ ]]>
+ </body>
+ </method>
+
+ <method name="getDropIndex">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ for (var i = 0; i < this.tabs.length; ++i) {
+ var coord = this.tabs[i].boxObject.screenX +
+ this.tabs[i].boxObject.width / 2;
+ if (window.getComputedStyle(this, null).direction == "ltr") {
+ if (aEvent.screenX < coord)
+ return i;
+ } else {
+ if (aEvent.screenX > coord)
+ return i;
+ }
+ }
+
+ return this.tabs.length;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getDropOnIndex">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ for (var i = 0; i < this.tabs.length; ++i) {
+ var tabBoxObject = this.tabs[i].boxObject;
+ if (aEvent.screenX > tabBoxObject.screenX + tabBoxObject.width * .25 &&
+ aEvent.screenX < tabBoxObject.screenX + tabBoxObject.width * .75)
+ return i;
+ }
+
+ return -1;
+ ]]>
+ </body>
+ </method>
+
+ <!-- moveTabLeft and moveTabRight methods have been kept for backwards
+ compatibility for extensions. Internally moveTabOver is used. -->
+ <method name="moveTabLeft">
+ <body>
+ <![CDATA[
+ if (window.getComputedStyle(this, null).direction == "ltr")
+ this.moveTabBackward();
+ else
+ this.moveTabForward();
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabRight">
+ <body>
+ <![CDATA[
+ if (window.getComputedStyle(this, null).direction == "ltr")
+ this.moveTabForward();
+ else
+ this.moveTabBackward();
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabForward">
+ <body>
+ <![CDATA[
+ var tabPos = this.tabContainer.selectedIndex;
+ if (tabPos < this.browsers.length - 1) {
+ this.moveTabTo(this.mCurrentTab, tabPos + 1);
+ }
+ else if (this.arrowKeysShouldWrap)
+ this.moveTabToStart();
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabBackward">
+ <body>
+ <![CDATA[
+ var tabPos = this.tabContainer.selectedIndex;
+ if (tabPos > 0) {
+ this.moveTabTo(this.mCurrentTab, tabPos - 1);
+ }
+ else if (this.arrowKeysShouldWrap)
+ this.moveTabToEnd();
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabToStart">
+ <body>
+ <![CDATA[
+ if (this.tabContainer.selectedIndex > 0) {
+ this.moveTabTo(this.mCurrentTab, 0);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabToEnd">
+ <body>
+ <![CDATA[
+ if (this.tabContainer.selectedIndex < this.browsers.length - 1) {
+ this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveTabOver">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ var direction = window.getComputedStyle(this, null).direction;
+ var keyCode = aEvent.keyCode;
+ if ((direction == "ltr" && keyCode == KeyEvent.DOM_VK_RIGHT) ||
+ (direction == "rtl" && keyCode == KeyEvent.DOM_VK_LEFT))
+ this.moveTabForward();
+ else
+ this.moveTabBackward();
+ ]]>
+ </body>
+ </method>
+
+ <!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
+ MAKE SURE TO ADD IT HERE AS WELL. -->
+ <property name="canGoBack"
+ onget="return this.mCurrentBrowser.canGoBack;"
+ readonly="true"/>
+
+ <property name="canGoForward"
+ onget="return this.mCurrentBrowser.canGoForward;"
+ readonly="true"/>
+
+ <method name="goBack">
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.goBack();
+ ]]>
+ </body>
+ </method>
+
+ <method name="goForward">
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.goForward();
+ ]]>
+ </body>
+ </method>
+
+ <method name="reload">
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.reload();
+ ]]>
+ </body>
+ </method>
+
+ <method name="reloadWithFlags">
+ <parameter name="aFlags"/>
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.reloadWithFlags(aFlags);
+ ]]>
+ </body>
+ </method>
+
+ <method name="stop">
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.stop();
+ ]]>
+ </body>
+ </method>
+
+ <!-- throws exception for unknown schemes -->
+ <method name="loadURI">
+ <parameter name="aURI"/>
+ <parameter name="aReferrerURI"/>
+ <parameter name="aCharset"/>
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
+ ]]>
+ </body>
+ </method>
+
+ <!-- throws exception for unknown schemes -->
+ <method name="loadURIWithFlags">
+ <parameter name="aURI"/>
+ <parameter name="aFlags"/>
+ <parameter name="aReferrerURI"/>
+ <parameter name="aCharset"/>
+ <parameter name="aPostData"/>
+ <body>
+ <![CDATA[
+ // Note - the callee understands both:
+ // (a) loadURIWithFlags(aURI, aFlags, ...)
+ // (b) loadURIWithFlags(aURI, { flags: aFlags, ... })
+ // Forwarding it as (a) here actually supports both (a) and (b),
+ // so you can call us either way too.
+ return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
+ ]]>
+ </body>
+ </method>
+
+ <method name="goHome">
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.goHome();
+ ]]>
+ </body>
+ </method>
+
+ <property name="homePage">
+ <getter>
+ <![CDATA[
+ return this.mCurrentBrowser.homePage;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ this.mCurrentBrowser.homePage = val;
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <method name="gotoIndex">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ return this.mCurrentBrowser.gotoIndex(aIndex);
+ ]]>
+ </body>
+ </method>
+
+ <property name="currentURI"
+ onget="return this.mCurrentBrowser.currentURI;"
+ readonly="true"/>
+
+ <field name="finder"><![CDATA[
+ ({
+ mTabBrowser: this,
+ mListeners: new Set(),
+ get finder() {
+ return this.mTabBrowser.mCurrentBrowser.finder;
+ },
+ addResultListener: function(aListener) {
+ this.mListeners.add(aListener);
+ this.finder.addResultListener(aListener);
+ },
+ removeResultListener: function(aListener) {
+ this.mListeners.delete(aListener);
+ this.finder.removeResultListener(aListener);
+ },
+ get searchString() {
+ return this.finder.searchString;
+ },
+ get clipboardSearchString() {
+ return this.finder.clipboardSearchString;
+ },
+ set clipboardSearchString(val) {
+ return this.finder.clipboardSearchString = val;
+ },
+ set caseSensitive(val) {
+ return this.finder.caseSensitive = val;
+ },
+ set entireWord(val) {
+ return this.finder.entireWord = val;
+ },
+ get highlighter() {
+ return this.finder.highlighter;
+ },
+ get matchesCountLimit() {
+ return this.finder.matchesCountLimit;
+ },
+ fastFind: function(aSearchString, aLinksOnly, aDrawOutline) {
+ this.finder.fastFind(aSearchString, aLinksOnly, aDrawOutline);
+ },
+ findAgain: function(aFindBackwards, aLinksOnly, aDrawOutline) {
+ this.finder.findAgain(aFindBackwards, aLinksOnly, aDrawOutline);
+ },
+ setSearchStringToSelection: function() {
+ return this.finder.setSearchStringToSelection();
+ },
+ highlight: function(...args) {
+ this.finder.highlight(...args);
+ },
+ getInitialSelection: function() {
+ this.finder.getInitialSelection();
+ },
+ getActiveSelectionText: function() {
+ return this.finder.getActiveSelectionText();
+ },
+ enableSelection: function() {
+ this.finder.enableSelection();
+ },
+ removeSelection: function() {
+ this.finder.removeSelection();
+ },
+ focusContent: function() {
+ this.finder.focusContent();
+ },
+ onFindbarClose: function() {
+ this.finder.onFindbarClose();
+ },
+ onFindbarOpen: function() {
+ this.finder.onFindbarOpen();
+ },
+ onModalHighlightChange: function(...args) {
+ return this.finder.onModalHighlightChange(...args);
+ },
+ onHighlightAllChange: function(...args) {
+ return this.finder.onHighlightAllChange(...args);
+ },
+ keyPress: function(aEvent) {
+ this.finder.keyPress(aEvent);
+ },
+ requestMatchesCount: function(...args) {
+ this.finder.requestMatchesCount(...args);
+ }
+ })
+ ]]></field>
+
+ <property name="docShell"
+ onget="return this.mCurrentBrowser.docShell"
+ readonly="true"/>
+
+ <property name="webNavigation"
+ onget="return this.mCurrentBrowser.webNavigation"
+ readonly="true"/>
+
+ <property name="webBrowserFind"
+ readonly="true"
+ onget="return this.mCurrentBrowser.webBrowserFind"/>
+
+ <property name="webProgress"
+ readonly="true"
+ onget="return this.mCurrentBrowser.webProgress"/>
+
+ <property name="contentWindow"
+ readonly="true"
+ onget="return this.mCurrentBrowser.contentWindow"/>
+
+ <property name="contentWindowAsCPOW"
+ readonly="true"
+ onget="return this.mCurrentBrowser.contentWindowAsCPOW"/>
+
+ <property name="sessionHistory"
+ onget="return this.mCurrentBrowser.sessionHistory;"
+ readonly="true"/>
+
+ <property name="markupDocumentViewer"
+ onget="return this.mCurrentBrowser.markupDocumentViewer;"
+ readonly="true"/>
+
+ <property name="contentDocument"
+ onget="return this.mCurrentBrowser.contentDocument;"
+ readonly="true"/>
+
+ <property name="contentTitle"
+ onget="return this.mCurrentBrowser.contentTitle;"
+ readonly="true"/>
+
+ <property name="securityUI"
+ onget="return this.mCurrentBrowser.securityUI;"
+ readonly="true"/>
+
+ <property name="userTypedValue"
+ onget="return this.mCurrentBrowser.userTypedValue;"
+ onset="return this.mCurrentBrowser.userTypedValue = val;"/>
+
+ <property name="droppedLinkHandler"
+ onget="return this.mCurrentBrowser.droppedLinkHandler;"
+ onset="return this.mCurrentBrowser.droppedLinkHandler = val;"/>
+
+ <property name="showWindowResizer"
+ onget="return this.mCurrentBrowser.showWindowResizer;"
+ onset="return this.mCurrentBrowser.showWindowResizer = val;"/>
+
+ <property name="docShellIsActive"
+ onget="return this.mCurrentBrowser.docShellIsActive;"
+ onset="return this.mCurrentBrowser.docShellIsActive = val;"/>
+
+ <property name="fullZoom"
+ onget="return this.mCurrentBrowser.fullZoom;"
+ onset="return this.mCurrentBrowser.fullZoom = val;"/>
+
+ <property name="textZoom"
+ onget="return this.mCurrentBrowser.textZoom;"
+ onset="return this.mCurrentBrowser.textZoom = val;"/>
+
+ <property name="isSyntheticDocument"
+ onget="return this.mCurrentBrowser.isSyntheticDocument;"
+ readonly="true"/>
+
+ <property name="messageManager"
+ onget="return window.messageManager;"
+ readonly="true"/>
+
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ var maxUndoDepth = 0;
+ switch (aTopic) {
+ case "browser:purge-session-history":
+ break;
+
+ case "nsPref:changed":
+ if (aData == "browser.tabs.max_tabs_undo") {
+ maxUndoDepth = Math.max(0, Services.prefs.getIntPref(aData));
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ // Wipe out savedBrowsers since history is gone
+ while (this.savedBrowsers.length > maxUndoDepth) {
+ var tabData = this.savedBrowsers.pop();
+ var deadBrowser = tabData.browserData.browser;
+ delete tabData.browserData;
+ this.removeBrowser(deadBrowser);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="_fastFind">null</field>
+ <property name="fastFind" readonly="true">
+ <getter>
+ <![CDATA[
+ if (!this._fastFind) {
+ this._fastFind = Cc["@mozilla.org/typeaheadfind;1"]
+ .createInstance(Ci.nsITypeAheadFind);
+ this._fastFind.init(this.docShell);
+ }
+ return this._fastFind;
+ ]]>
+ </getter>
+ </property>
+
+ <field name="_lastSearchString">null</field>
+ <field name="_lastSearchHighlight">false</field>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ switch (aEvent.type) {
+ case "keypress":
+ this._handleKeyEvent(aEvent);
+ break;
+ }
+ ]]></body>
+ </method>
+
+ <constructor>
+ <![CDATA[
+ document.addEventListener("keypress", this);
+ this.arrowKeysShouldWrap = AppConstants.platform == "macosx";
+ // Bail out early if we are in tabmail. See Bug 521803.
+ if (!this.mPanelContainer)
+ return;
+
+ this.mCurrentBrowser = this.mPanelContainer.firstChild.firstChild.firstChild;
+ this.mCurrentTab = this.tabContainer.firstChild;
+
+ var uniqueId = "panel" + this.nextTabNumber++;
+ this.mPanelContainer.childNodes[0].id = uniqueId;
+ this.tabs[0].linkedPanel = uniqueId;
+ this.tabs[0].linkedBrowser = this.mCurrentBrowser;
+
+ // Ensure the browser's session history and security UI are wired up
+ // note that toolkit browser automatically inits its security UI
+ // when you get it but for xpfe you need to init it explicitly
+ if (!this.mCurrentBrowser.securityUI)
+ this.mCurrentBrowser.init();
+
+ // Wire up the tab's progress listener and filter.
+ var tabListener = this.mTabProgressListener(this.mCurrentTab,
+ this.mCurrentBrowser,
+ false);
+ var filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
+ .createInstance(Ci.nsIWebProgress);
+ filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
+ this.webProgress.addProgressListener(filter,
+ Ci.nsIWebProgress.NOTIFY_ALL);
+ this.mTabListeners[0] = tabListener;
+ this.mTabFilters[0] = filter;
+
+ if (!Services.prefs.getBoolPref("browser.tabs.autoHide") &&
+ !Services.prefs.getBoolPref("browser.tabs.forceHide") &&
+ window.toolbar.visible)
+ this.mStrip.collapsed = false;
+
+ var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "tab");
+ t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
+ t.setAttribute("crop", "end");
+ t.className = "tabbrowser-tab";
+ t.style.maxWidth = Services.prefs.getIntPref("browser.tabs.tabMaxWidth") + "px";
+ t.style.minWidth = Services.prefs.getIntPref("browser.tabs.tabMinWidth") + "px";
+ t.width = 0;
+ t.flex = 100;
+ t.setAttribute("validate", "never");
+ t.setAttribute("onerror", "this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); this.removeAttribute('image');");
+ this.referenceTab = t;
+
+ Services.obs.addObserver(this, "browser:purge-session-history");
+ Services.prefs.addObserver("browser.tabs.max_tabs_undo", this);
+
+ var onclick = this.getAttribute("oncontentclick");
+ if (onclick)
+ this.onContentClick = new Function("event", onclick);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ document.removeEventListener("keypress", this);
+ // Bail out early if we are in tabmail. See Bug 521803.
+ if (!this.mPanelContainer)
+ return;
+
+ for (var i = 0; i < this.mTabListeners.length; ++i) {
+ this.browsers[i].webProgress.removeProgressListener(this.mTabFilters[i]);
+ this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
+ this.mTabFilters[i] = null;
+ this.mTabListeners[i] = null;
+ }
+ Services.obs.removeObserver(this, "browser:purge-session-history");
+ Services.prefs.removeObserver("browser.tabs.max_tabs_undo", this);
+ this.savedBrowsers.forEach(function(aTabData) {
+ delete aTabData.browserData;
+ });
+ ]]>
+ </destructor>
+
+ <!-- Deprecated stuff, implemented for backwards compatibility. -->
+ <property name="mTabContainer" readonly="true"
+ onget="return this.tabContainer;"/>
+ <property name="mTabs" readonly="true"
+ onget="return this.tabs;"/>
+ </implementation>
+
+ <handlers>
+ <handler event="select" action="if (event.originalTarget == this.mPanelContainer) this.updateCurrentBrowser();"/>
+
+ <handler event="DOMLinkAdded" phase="capturing" action="this.onLinkEvent(event);"/>
+ <handler event="DOMLinkChanged" phase="capturing" action="this.onLinkEvent(event);"/>
+
+ <handler event="DOMWindowClose" phase="capturing">
+ <![CDATA[
+ if (!event.isTrusted)
+ return;
+
+ if (this.tabs.length == 1)
+ return;
+
+ this.removeTab(this._getTabForContentWindow(event.target));
+ event.preventDefault();
+ ]]>
+ </handler>
+
+ <handler event="DOMWebNotificationClicked" phase="capturing">
+ <![CDATA[
+ if (!event.isTrusted)
+ return;
+
+ // The user clicked a desktop notification; make sure its
+ // tab is brought to the front and then raise the window.
+ this.selectedTab = this._getTabForContentWindow(event.target.top);
+ window.focus();
+ ]]>
+ </handler>
+
+ <handler event="DOMWillOpenModalDialog" phase="capturing">
+ <![CDATA[
+ if (!event.isTrusted)
+ return;
+
+ // We're about to open a modal dialog, make sure the opening
+ // tab is brought to the front.
+ this.selectedTab = this._getTabForContentWindow(event.target.top);
+ ]]>
+ </handler>
+
+ <handler event="DOMTitleChanged">
+ <![CDATA[
+ if (!event.isTrusted)
+ return;
+
+ var contentWin = event.target.defaultView;
+ if (contentWin != contentWin.top)
+ return;
+
+ var tab = this._getTabForContentWindow(contentWin);
+ if (!tab)
+ return;
+
+ this.setTabTitle(tab);
+ if (tab == this.mCurrentTab)
+ this.updateTitlebar();
+ ]]>
+ </handler>
+
+ <handler event="click" phase="capturing" group="system">
+ <![CDATA[
+ if (this.onContentClick)
+ this.onContentClick(event);
+ ]]>
+ </handler>
+
+ </handlers>
+ </binding>
+
+ <binding id="tabbrowser-arrowscrollbox"
+ extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
+ <implementation>
+ <!-- Override scrollbox.xml method, since our scrollbox's children are
+ inherited from the binding parent -->
+ <method name="_getScrollableElements">
+ <body>
+ <![CDATA[
+ return Array.from(document.getBindingParent(this).childNodes)
+ .filter(this._canScrollToElement,
+ this);
+ ]]>
+ </body>
+ </method>
+ <method name="_canScrollToElement">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ return !aTab.pinned && !aTab.hidden;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="underflow">
+ <![CDATA[
+ if (event.detail == 0)
+ return; // Ignore vertical events
+
+ var tabs = document.getBindingParent(this);
+ tabs.removeAttribute("overflow");
+ ]]>
+ </handler>
+ <handler event="overflow">
+ <![CDATA[
+ if (event.detail == 0)
+ return; // Ignore vertical events
+
+ var tabs = document.getBindingParent(this);
+ tabs.setAttribute("overflow", true);
+ tabs._handleTabSelect(false);
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="tabbrowser-tabs"
+ extends="chrome://global/content/bindings/tabbox.xml#tabs">
+ <content>
+ <xul:stack flex="1" class="tabs-stack">
+ <xul:vbox>
+ <xul:spacer flex="1"/>
+ <xul:hbox class="tabs-bottom" align="center"/>
+ </xul:vbox>
+ <xul:vbox>
+ <xul:hbox>
+ <xul:stack>
+ <xul:spacer class="tabs-left"/>
+ <xul:toolbarbutton class="tabs-newbutton" context=""
+ anonid="tabstrip-newbutton"
+ xbl:inherits="oncommand=onnewtab,onclick=onnewtabclick,tooltiptext=tooltiptextnew"/>
+ </xul:stack>
+ <xul:arrowscrollbox anonid="arrowscrollbox"
+ class="tabbrowser-arrowscrollbox"
+ flex="1"
+ xbl:inherits="smoothscroll"
+ orient="horizontal"
+ style="min-width: 1px;">
+ <children includes="tab"/>
+ <xul:spacer class="tabs-right" flex="1"/>
+ </xul:arrowscrollbox>
+ <children/>
+ <xul:stack>
+ <xul:spacer class="tabs-right"/>
+ <xul:hbox class="tabs-closebutton-box" align="stretch" pack="end">
+ <xul:toolbarbutton class="tabs-alltabs-button" context=""
+ anonid="alltabs-button"
+ type="menu"
+ xbl:inherits="tooltiptext=tooltiptextalltabs">
+ <xul:menupopup class="tabs-alltabs-popup"
+ anonid="alltabs-popup"
+ position="after_end"/>
+ </xul:toolbarbutton>
+ <xul:hbox align="center">
+ <xul:toolbarbutton class="tabs-closebutton close-button" context=""
+ anonid="tabstrip-closebutton"
+ xbl:inherits="disabled=disableclose,oncommand=onclosetab,tooltiptext=tooltiptextclose"/>
+ </xul:hbox>
+ </xul:hbox>
+ </xul:stack>
+ </xul:hbox>
+ <xul:spacer class="tabs-bottom-spacer"/>
+ </xul:vbox>
+ </xul:stack>
+ </content>
+
+ <implementation implements="nsIDOMEventListener">
+ <constructor>
+ <![CDATA[
+ var tab = this.firstChild;
+ // set the tabstrip's minWidth too, otherwise it immediately overflows
+ this.arrowScrollbox.style.minWidth =
+ tab.style.minWidth = Services.prefs.getIntPref("browser.tabs.tabMinWidth") + "px";
+ tab.style.maxWidth = Services.prefs.getIntPref("browser.tabs.tabMaxWidth") + "px";
+ window.addEventListener("resize", this);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ window.removeEventListener("resize", this);
+ ]]>
+ </destructor>
+
+ <field name="arrowScrollboxWidth">0</field>
+
+ <field name="arrowScrollbox">
+ document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
+ </field>
+
+ <method name="_handleTabSelect">
+ <parameter name="aSmoothScroll"/>
+ <body>
+ <![CDATA[
+ if (this.getAttribute("overflow") == "true")
+ this.arrowScrollbox.ensureElementIsVisible(this.selectedItem,
+ aSmoothScroll);
+ ]]>
+ </body>
+ </method>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ switch (aEvent.type)
+ {
+ case "resize":
+ if (aEvent.target != window)
+ break;
+ var width = this.arrowScrollbox.boxObject.width;
+ if (width != this.arrowScrollboxWidth)
+ {
+ this._handleTabSelect(false);
+ this.arrowScrollboxWidth = width;
+ }
+ break;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="mAllTabsPopup">
+ document.getAnonymousElementByAttribute(this, "anonid", "alltabs-popup");
+ </field>
+
+ <field name="_animateElement">
+ this.arrowScrollbox._scrollButtonDown;
+ </field>
+
+ <method name="_notifyBackgroundTab">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ var scrollRect = this.arrowScrollbox.scrollClientRect;
+ var tab = aTab.getBoundingClientRect();
+
+ // Is the new tab already completely visible?
+ if (scrollRect.left <= tab.left && tab.right <= scrollRect.right)
+ return;
+
+ if (this.arrowScrollbox.smoothScroll) {
+ let selected = this.selectedItem.getBoundingClientRect();
+
+ /* Can we make both the new tab and the selected tab completely
+ visible? */
+ if (!selected ||
+ Math.max(tab.right - selected.left, selected.right - tab.left) <= scrollRect.width) {
+ this.arrowScrollbox.ensureElementIsVisible(aTab);
+ return;
+ }
+
+ this.arrowScrollbox.scrollByPixels(this.arrowScrollbox._isRTLScrollbox ?
+ selected.right - scrollRect.right :
+ selected.left - scrollRect.left);
+ }
+
+ if (!this._animateElement.hasAttribute("notifybgtab")) {
+ this._animateElement.setAttribute("notifybgtab", "true");
+ setTimeout(function(ele) {
+ ele.removeAttribute("notifybgtab");
+ }, 150, this._animateElement);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_handleNewTab">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ if (aTab.parentNode != this)
+ return;
+
+ if (aTab.getAttribute("selected") == "true") {
+ this._handleTabSelect();
+ } else {
+ this._notifyBackgroundTab(aTab);
+ }
+
+ /* XXXmano: this is a temporary workaround for bug 345399
+ * We need to manually update the scroll buttons disabled state
+ * if a tab was inserted to the overflow area or removed from it
+ * without any scrolling and when the tabbar has already
+ * overflowed.
+ */
+ this.arrowScrollbox._updateScrollButtonsDisabledState();
+ ]]>
+ </body>
+ </method>
+
+ <method name="_handleMouseScroll">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ // Javascript does not have a logical XOR operator.
+ if (aEvent.shiftKey != Services.prefs.getBoolPref("browser.tabs.mouseScrollAdvancesTab")) {
+ this.advanceSelectedTab(aEvent.detail < 0 ? -1 : 1);
+ aEvent.stopPropagation();
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="TabSelect" action="this._handleTabSelect();"/>
+
+ <handler event="transitionend">
+ <![CDATA[
+ if (event.propertyName == "max-width")
+ this._handleNewTab(event.target);
+ ]]>
+ </handler>
+
+ <handler event="DOMMouseScroll" phase="capturing">
+ <![CDATA[
+ this._handleMouseScroll(event);
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="tabbrowser-alltabs-popup"
+ extends="chrome://global/content/bindings/popup.xml#popup">
+ <implementation implements="nsIDOMEventListener">
+ <method name="_tabOnTabClose">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ let menuItem = aEvent.target.mCorrespondingMenuitem;
+ if (menuItem)
+ menuItem.remove();
+ ]]>
+ </body>
+ </method>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ switch (aEvent.type)
+ {
+ case "TabClose":
+ this._tabOnTabClose(aEvent);
+ break;
+ case "TabOpen":
+ this._createTabMenuItem(aEvent.originalTarget);
+ break;
+ case "scroll":
+ this._updateTabsVisibilityStatus();
+ break;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_updateTabsVisibilityStatus">
+ <body>
+ <![CDATA[
+ let tabContainer = document.getBindingParent(this);
+ let tabstripBO = tabContainer.arrowScrollbox.scrollBoxObject;
+
+ for (let i = 0; i < this.childNodes.length; i++)
+ {
+ let curTabBO = this.childNodes[i].tab.boxObject;
+ if (curTabBO.screenX >= tabstripBO.screenX &&
+ curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
+ this.childNodes[i].removeAttribute("tabIsScrolled");
+ else
+ this.childNodes[i].setAttribute("tabIsScrolled", "true");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_createTabMenuItem">
+ <parameter name="aTabNode"/>
+ <body>
+ <![CDATA[
+ let menuItem = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "menuitem");
+ menuItem.setAttribute("class", "menuitem-iconic alltabs-item icon-holder");
+ menuItem.setAttribute("label", aTabNode.label);
+ menuItem.setAttribute("crop", aTabNode.getAttribute("crop"));
+
+ ["busy", "image", "selected"].forEach(
+ function(attribute)
+ {
+ if (aTabNode.hasAttribute(attribute))
+ {
+ menuItem.setAttribute(attribute, aTabNode.getAttribute(attribute));
+ }
+ }
+ );
+
+ // Keep some attributes of the menuitem in sync with its
+ // corresponding tab (e.g. the tab label)
+ aTabNode.mCorrespondingMenuitem = menuItem;
+ document.addBroadcastListenerFor(aTabNode, menuItem, "label");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "crop");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "image");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "busy");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "selected");
+ aTabNode.addEventListener("TabClose", this);
+ menuItem.tab = aTabNode;
+ this.appendChild(menuItem);
+ return menuItem;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popupshowing">
+ <![CDATA[
+ // set up the menu popup
+ let tabcontainer = document.getBindingParent(this);
+ let tabs = tabcontainer.childNodes;
+
+ // Listen for changes in the tab bar.
+ let tabbrowser = document.getBindingParent(tabcontainer);
+ tabbrowser.addEventListener("TabOpen", this);
+ tabcontainer.arrowScrollbox.addEventListener("scroll", this);
+
+ for (let i = 0; i < tabs.length; i++)
+ this._createTabMenuItem(tabs[i]);
+ this._updateTabsVisibilityStatus();
+ ]]>
+ </handler>
+
+ <handler event="popuphiding">
+ <![CDATA[
+ // clear out the menu popup and remove the listeners
+ while (this.hasChildNodes())
+ {
+ let menuItem = this.lastChild;
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "label");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "crop");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "image");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "busy");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "selected");
+ menuItem.tab.removeEventListener("TabClose", this);
+ menuItem.tab.mCorrespondingMenuitem = null;
+ menuItem.tab = null;
+ menuItem.remove();
+ }
+ let tabcontainer = document.getBindingParent(this);
+ tabcontainer.arrowScrollbox.removeEventListener("scroll", this);
+ document.getBindingParent(tabcontainer).removeEventListener("TabOpen", this);
+ ]]>
+ </handler>
+
+ <handler event="command">
+ <![CDATA[
+ let tabcontainer = document.getBindingParent(this);
+ let tab = event.target.tab;
+ if (tabcontainer.selectedItem == tab)
+ tabcontainer._handleTabSelect();
+ else
+ tabcontainer.selectedItem = tab;
+ ]]>
+ </handler>
+
+ <handler event="DOMMenuItemActive">
+ <![CDATA[
+ var tab = event.target.tab;
+ if (tab) {
+ let overLink = tab.linkedBrowser.currentURI.spec;
+ if (overLink == "about:blank")
+ overLink = "";
+ XULBrowserWindow.setOverLink(overLink, null);
+ }
+ ]]></handler>
+
+ <handler event="DOMMenuItemInactive">
+ <![CDATA[
+ XULBrowserWindow.setOverLink("", null);
+ ]]></handler>
+
+ </handlers>
+ </binding>
+</bindings>
diff --git a/comm/suite/browser/test/browser/alltabslistener.html b/comm/suite/browser/test/browser/alltabslistener.html
new file mode 100644
index 0000000000..166c31037a
--- /dev/null
+++ b/comm/suite/browser/test/browser/alltabslistener.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Test page for bug 463387</title>
+</head>
+<body>
+<p>Test page for bug 463387</p>
+</body>
+</html>
diff --git a/comm/suite/browser/test/browser/authenticate.sjs b/comm/suite/browser/test/browser/authenticate.sjs
new file mode 100644
index 0000000000..2f8c85adc1
--- /dev/null
+++ b/comm/suite/browser/test/browser/authenticate.sjs
@@ -0,0 +1,210 @@
+function handleRequest(request, response)
+{
+ try {
+ reallyHandleRequest(request, response);
+ } catch (e) {
+ response.setStatusLine("1.0", 200, "AlmostOK");
+ response.write("Error handling request: " + e);
+ }
+}
+
+
+function reallyHandleRequest(request, response) {
+ var match;
+ var requestAuth = true, requestProxyAuth = true;
+
+ // Allow the caller to drive how authentication is processed via the query.
+ // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
+ // The extra ? allows the user/pass/realm checks to succeed if the name is
+ // at the beginning of the query string.
+ var query = "?" + request.queryString;
+
+ var expected_user = "", expected_pass = "", realm = "mochitest";
+ var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy";
+ var huge = false, anonymous = false;
+ var authHeaderCount = 1;
+ // user=xxx
+ match = /[^_]user=([^&]*)/.exec(query);
+ if (match)
+ expected_user = match[1];
+
+ // pass=xxx
+ match = /[^_]pass=([^&]*)/.exec(query);
+ if (match)
+ expected_pass = match[1];
+
+ // realm=xxx
+ match = /[^_]realm=([^&]*)/.exec(query);
+ if (match)
+ realm = match[1];
+
+ // proxy_user=xxx
+ match = /proxy_user=([^&]*)/.exec(query);
+ if (match)
+ proxy_expected_user = match[1];
+
+ // proxy_pass=xxx
+ match = /proxy_pass=([^&]*)/.exec(query);
+ if (match)
+ proxy_expected_pass = match[1];
+
+ // proxy_realm=xxx
+ match = /proxy_realm=([^&]*)/.exec(query);
+ if (match)
+ proxy_realm = match[1];
+
+ // huge=1
+ match = /huge=1/.exec(query);
+ if (match)
+ huge = true;
+
+ // multiple=1
+ match = /multiple=([^&]*)/.exec(query);
+ if (match)
+ authHeaderCount = match[1]+0;
+
+ // anonymous=1
+ match = /anonymous=1/.exec(query);
+ if (match)
+ anonymous = true;
+
+ // Look for an authentication header, if any, in the request.
+ //
+ // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+ //
+ // This test only supports Basic auth. The value sent by the client is
+ // "username:password", obscured with base64 encoding.
+
+ var actual_user = "", actual_pass = "", authHeader, authPresent = false;
+ if (request.hasHeader("Authorization")) {
+ authPresent = true;
+ authHeader = request.getHeader("Authorization");
+ match = /Basic (.+)/.exec(authHeader);
+ if (match.length != 2)
+ throw "Couldn't parse auth header: " + authHeader;
+
+ var userpass = base64ToString(match[1]); // no atob() :-(
+ match = /(.*):(.*)/.exec(userpass);
+ if (match.length != 3)
+ throw "Couldn't decode auth header: " + userpass;
+ actual_user = match[1];
+ actual_pass = match[2];
+ }
+
+ var proxy_actual_user = "", proxy_actual_pass = "";
+ if (request.hasHeader("Proxy-Authorization")) {
+ authHeader = request.getHeader("Proxy-Authorization");
+ match = /Basic (.+)/.exec(authHeader);
+ if (match.length != 2)
+ throw "Couldn't parse auth header: " + authHeader;
+
+ var userpass = base64ToString(match[1]); // no atob() :-(
+ match = /(.*):(.*)/.exec(userpass);
+ if (match.length != 3)
+ throw "Couldn't decode auth header: " + userpass;
+ proxy_actual_user = match[1];
+ proxy_actual_pass = match[2];
+ }
+
+ // Don't request authentication if the credentials we got were what we
+ // expected.
+ if (expected_user == actual_user &&
+ expected_pass == actual_pass) {
+ requestAuth = false;
+ }
+ if (proxy_expected_user == proxy_actual_user &&
+ proxy_expected_pass == proxy_actual_pass) {
+ requestProxyAuth = false;
+ }
+
+ if (anonymous) {
+ if (authPresent) {
+ response.setStatusLine("1.0", 400, "Unexpected authorization header found");
+ } else {
+ response.setStatusLine("1.0", 200, "Authorization header not found");
+ }
+ } else {
+ if (requestProxyAuth) {
+ response.setStatusLine("1.0", 407, "Proxy authentication required");
+ for (i = 0; i < authHeaderCount; ++i)
+ response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
+ } else if (requestAuth) {
+ response.setStatusLine("1.0", 401, "Authentication required");
+ for (i = 0; i < authHeaderCount; ++i)
+ response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
+ } else {
+ response.setStatusLine("1.0", 200, "OK");
+ }
+ }
+
+ response.setHeader("Content-Type", "application/xhtml+xml", false);
+ response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
+ response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
+ response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n");
+ response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
+ response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
+ response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n");
+
+ if (huge) {
+ response.write("<div style='display: none'>");
+ for (i = 0; i < 100000; i++) {
+ response.write("123456789\n");
+ }
+ response.write("</div>");
+ response.write("<span id='footnote'>This is a footnote after the huge content fill</span>");
+ }
+
+ response.write("</html>");
+}
+
+
+// base64 decoder
+//
+// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa()
+// doesn't seem to exist. :-(
+/* Convert Base64 data to a string */
+const toBinaryTable = [
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+ 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+ -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+];
+const base64Pad = '=';
+
+function base64ToString(data) {
+
+ var result = '';
+ var leftbits = 0; // number of bits decoded, but yet to be appended
+ var leftdata = 0; // bits decoded, but yet to be appended
+
+ // Convert one by one.
+ for (var i = 0; i < data.length; i++) {
+ var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
+ var padding = (data[i] == base64Pad);
+ // Skip illegal characters and whitespace
+ if (c == -1) continue;
+
+ // Collect data into leftdata, update bitcount
+ leftdata = (leftdata << 6) | c;
+ leftbits += 6;
+
+ // If we have 8 or more bits, append 8 bits to the result
+ if (leftbits >= 8) {
+ leftbits -= 8;
+ // Append if not padding.
+ if (!padding)
+ result += String.fromCharCode((leftdata >> leftbits) & 0xff);
+ leftdata &= (1 << leftbits) - 1;
+ }
+ }
+
+ // If there are any bits left, the base64 string was corrupted
+ if (leftbits)
+ throw Components.Exception('Corrupted base64 string');
+
+ return result;
+}
diff --git a/comm/suite/browser/test/browser/browser.ini b/comm/suite/browser/test/browser/browser.ini
new file mode 100644
index 0000000000..a6b00157c6
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser.ini
@@ -0,0 +1,38 @@
+[DEFAULT]
+support-files =
+ head.js
+
+[browser_alltabslistener.js]
+support-files = alltabslistener.html
+[browser_bug329212.js]
+support-files = title_test.svg
+[browser_bug409624.js]
+[browser_bug413915.js]
+[browser_bug427559.js]
+[browser_bug435325.js]
+[browser_bug462289.js]
+skip-if = toolkit == "cocoa"
+[browser_bug519216.js]
+[browser_bug561636.js]
+[browser_bug562649.js]
+[browser_bug581947.js]
+[browser_bug585511.js]
+[browser_bug595507.js]
+[browser_bug623155.js]
+support-files = redirect_bug623155.sjs
+[browser_fayt.js]
+[browser_page_style_menu.js]
+support-files = page_style_sample.html
+[browser_notification_tab_switching.js]
+support-files = file_dom_notifications.html
+[browser_pageInfo.js]
+support-files = feed_tab.html
+[browser_popupNotification.js]
+[browser_privatebrowsing_protocolhandler.js]
+support-files = browser_privatebrowsing_protocolhandler_page.html
+[browser_relatedTabs.js]
+[browser_scope.js]
+[browser_selectTabAtIndex.js]
+[browser_urlbarCopying.js]
+support-files =
+ authenticate.sjs
diff --git a/comm/suite/browser/test/browser/browser_alltabslistener.js b/comm/suite/browser/test/browser/browser_alltabslistener.js
new file mode 100644
index 0000000000..609a4e66e8
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_alltabslistener.js
@@ -0,0 +1,201 @@
+const gCompleteState = Ci.nsIWebProgressListener.STATE_STOP +
+ Ci.nsIWebProgressListener.STATE_IS_NETWORK;
+
+var gFrontProgressListener = {
+ onProgressChange: function (aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ },
+
+ onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
+ var state = "onStateChange";
+ info("FrontProgress: " + state + " 0x" + aStateFlags.toString(16));
+ ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
+ is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
+ gFrontNotificationsPos++;
+ },
+
+ onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
+ var state = "onLocationChange";
+ info("FrontProgress: " + state + " " + aLocationURI.spec);
+ ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
+ is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
+ gFrontNotificationsPos++;
+ },
+
+ onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
+ },
+
+ onSecurityChange: function (aWebProgress, aRequest, aState) {
+ var state = "onSecurityChange";
+ info("FrontProgress: " + state + " 0x" + aState.toString(16));
+ ok(gFrontNotificationsPos < gFrontNotifications.length, "Got an expected notification for the front notifications listener");
+ is(state, gFrontNotifications[gFrontNotificationsPos], "Got a notification for the front notifications listener");
+ gFrontNotificationsPos++;
+ }
+}
+
+var gAllProgressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ var state = "onStateChange";
+ info("AllProgress: " + state + " 0x" + aStateFlags.toString(16));
+ is(aBrowser, gTestBrowser, state + " notification came from the correct browser");
+ ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
+ is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
+ gAllNotificationsPos++;
+
+ if ((aStateFlags & gCompleteState) == gCompleteState) {
+ is(gAllNotificationsPos, gAllNotifications.length, "Saw the expected number of notifications");
+ is(gFrontNotificationsPos, gFrontNotifications.length, "Saw the expected number of frontnotifications");
+ executeSoon(gNextTest);
+ }
+ },
+
+ onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI, aFlags) {
+ var state = "onLocationChange";
+ info("AllProgress: " + state + " " + aLocationURI.spec);
+ is(aBrowser, gTestBrowser, state + " notification came from the correct browser");
+ ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
+ is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
+ gAllNotificationsPos++;
+ },
+
+ onStatusChange: function (aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
+ var state = "onStatusChange";
+ is(aBrowser, gTestBrowser, state + " notification came from the correct browser");
+ },
+
+ onSecurityChange: function (aBrowser, aWebProgress, aRequest, aState) {
+ var state = "onSecurityChange";
+ info("AllProgress: " + state + " 0x" + aState.toString(16));
+ is(aBrowser, gTestBrowser, state + " notification came from the correct browser");
+ ok(gAllNotificationsPos < gAllNotifications.length, "Got an expected notification for the all notifications listener");
+ is(state, gAllNotifications[gAllNotificationsPos], "Got a notification for the all notifications listener");
+ gAllNotificationsPos++;
+ }
+}
+
+var gFrontNotifications, gAllNotifications, gFrontNotificationsPos, gAllNotificationsPos;
+var gBackgroundTab, gForegroundTab, gBackgroundBrowser, gForegroundBrowser, gTestBrowser;
+var gTestPage = "/browser/suite/browser/test/browser/alltabslistener.html";
+var gNextTest;
+
+function test() {
+ waitForExplicitFinish();
+
+ gBackgroundTab = getBrowser().addTab("about:blank");
+ gForegroundTab = getBrowser().addTab("about:blank");
+ gBackgroundBrowser = getBrowser().getBrowserForTab(gBackgroundTab);
+ gForegroundBrowser = getBrowser().getBrowserForTab(gForegroundTab);
+ getBrowser().selectedTab = gForegroundTab;
+
+ // We must wait until the about:blank page has completed loading before
+ // starting tests or we get notifications from that
+ gForegroundBrowser.addEventListener("load", startTests, true);
+}
+
+function runTest(browser, url, next) {
+ gFrontNotificationsPos = 0;
+ gAllNotificationsPos = 0;
+ gNextTest = next;
+ gTestBrowser = browser;
+ browser.loadURI(url);
+}
+
+function startTests() {
+ gForegroundBrowser.removeEventListener("load", startTests, true);
+ executeSoon(startTest1);
+}
+
+function startTest1() {
+ info("\nTest 1");
+ getBrowser().addProgressListener(gFrontProgressListener);
+ getBrowser().addTabsProgressListener(gAllProgressListener);
+
+ gAllNotifications = [
+ "onStateChange",
+ "onLocationChange",
+ "onSecurityChange",
+ "onStateChange"
+ ];
+ gFrontNotifications = gAllNotifications;
+ runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest2);
+}
+
+function startTest2() {
+ info("\nTest 2");
+ gAllNotifications = [
+ "onStateChange",
+ "onLocationChange",
+ "onSecurityChange",
+ "onSecurityChange",
+ "onStateChange"
+ ];
+ gFrontNotifications = gAllNotifications;
+ runTest(gForegroundBrowser, "https://example.com" + gTestPage, startTest3);
+}
+
+function startTest3() {
+ info("\nTest 3");
+ gAllNotifications = [
+ "onStateChange",
+ "onLocationChange",
+ "onSecurityChange",
+ "onStateChange"
+ ];
+ gFrontNotifications = [];
+ runTest(gBackgroundBrowser, "http://example.org" + gTestPage, startTest4);
+}
+
+function startTest4() {
+ info("\nTest 4");
+ gAllNotifications = [
+ "onStateChange",
+ "onLocationChange",
+ "onSecurityChange",
+ "onSecurityChange",
+ "onStateChange"
+ ];
+ gFrontNotifications = [];
+ runTest(gBackgroundBrowser, "https://example.com" + gTestPage, startTest5);
+}
+
+function startTest5() {
+ info("\nTest 5");
+ // Switch the foreground browser
+ [gForegroundBrowser, gBackgroundBrowser] = [gBackgroundBrowser, gForegroundBrowser];
+ [gForegroundTab, gBackgroundTab] = [gBackgroundTab, gForegroundTab];
+ // Avoid the onLocationChange this will fire
+ getBrowser().removeProgressListener(gFrontProgressListener);
+ getBrowser().selectedTab = gForegroundTab;
+ getBrowser().addProgressListener(gFrontProgressListener);
+
+ gAllNotifications = [
+ "onStateChange",
+ "onLocationChange",
+ "onSecurityChange",
+ "onStateChange"
+ ];
+ gFrontNotifications = gAllNotifications;
+ runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest6);
+}
+
+function startTest6() {
+ info("\nTest 6");
+ gAllNotifications = [
+ "onStateChange",
+ "onLocationChange",
+ "onSecurityChange",
+ "onStateChange"
+ ];
+ gFrontNotifications = [];
+ runTest(gBackgroundBrowser, "http://example.org" + gTestPage, finishTest);
+}
+
+function finishTest() {
+ getBrowser().removeProgressListener(gFrontProgressListener);
+ getBrowser().removeTabsProgressListener(gAllProgressListener);
+ getBrowser().removeTab(gBackgroundTab);
+ getBrowser().removeTab(gForegroundTab);
+ finish();
+}
diff --git a/comm/suite/browser/test/browser/browser_bug329212.js b/comm/suite/browser/test/browser/browser_bug329212.js
new file mode 100644
index 0000000000..86925a4cb8
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug329212.js
@@ -0,0 +1,42 @@
+function test() {
+ waitForExplicitFinish();
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+
+ let doc = gBrowser.contentDocument;
+ let tooltip = document.getElementById("aHTMLTooltip");
+
+ ok(FillInHTMLTooltip(doc.getElementById("svg1")), "should get title");
+ is(tooltip.getAttribute("label"), "This is a non-root SVG element title");
+
+ ok(FillInHTMLTooltip(doc.getElementById("text1")), "should get title");
+ is(tooltip.getAttribute("label"), "\n\n\n This is a title\n\n ");
+
+ ok(!FillInHTMLTooltip(doc.getElementById("text2")), "should not get title");
+
+ ok(!FillInHTMLTooltip(doc.getElementById("text3")), "should not get title");
+
+ ok(FillInHTMLTooltip(doc.getElementById("link1")), "should get title");
+ is(tooltip.getAttribute("label"), "\n This is a title\n ");
+ ok(FillInHTMLTooltip(doc.getElementById("text4")), "should get title");
+ is(tooltip.getAttribute("label"), "\n This is a title\n ");
+
+ ok(!FillInHTMLTooltip(doc.getElementById("link2")), "should not get title");
+
+ ok(FillInHTMLTooltip(doc.getElementById("link3")), "should get title");
+ isnot(tooltip.getAttribute("label"), "");
+
+ ok(FillInHTMLTooltip(doc.getElementById("link4")), "should get title");
+ is(tooltip.getAttribute("label"), "This is an xlink:title attribute");
+
+ ok(!FillInHTMLTooltip(doc.getElementById("text5")), "should not get title");
+
+ gBrowser.removeCurrentTab();
+ finish();
+ }, true);
+
+ content.location =
+ "http://mochi.test:8888/browser/suite/browser/test/browser/title_test.svg";
+}
+
diff --git a/comm/suite/browser/test/browser/browser_bug409624.js b/comm/suite/browser/test/browser/browser_bug409624.js
new file mode 100644
index 0000000000..c3a800dffa
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug409624.js
@@ -0,0 +1,57 @@
+/* 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/. */
+ChromeUtils.defineModuleGetter(this, "FormHistory",
+ "resource://gre/modules/FormHistory.jsm");
+
+function test() {
+ waitForExplicitFinish();
+
+ // This test relies on the form history being empty to start with delete
+ // all the items first.
+ FormHistory.update({ op: "remove" },
+ { handleError: function (error) {
+ do_throw("Error occurred updating form history: " + error);
+ },
+ handleCompletion: function (reason) { if (!reason) test2(); },
+ });
+}
+
+function test2()
+{
+ let prefService = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+
+ let findBar = document.getElementById("FindToolbar");
+ let textbox = findBar.getElement("findbar-textbox");
+
+ let temp = {};
+ ChromeUtils.import("resource:///modules/Sanitizer.jsm", temp);
+ let s = temp.Sanitizer;
+ let prefBranch = prefService.getBranch("privacy.clearOnShutdown.");
+
+ prefBranch.setBoolPref("cache", false);
+ prefBranch.setBoolPref("cookies", false);
+ prefBranch.setBoolPref("downloads", false);
+ prefBranch.setBoolPref("formdata", true);
+ prefBranch.setBoolPref("history", false);
+ prefBranch.setBoolPref("offlineApps", false);
+ prefBranch.setBoolPref("passwords", false);
+ prefBranch.setBoolPref("sessions", false);
+ prefBranch.setBoolPref("siteSettings", false);
+
+ prefService.setBoolPref("privacy.sanitize.promptOnSanitize", false);
+
+ // Sanitize now so we can test the baseline point.
+ s.sanitize();
+ ok(!gFindBar.hasTransactions, "pre-test baseline for sanitizer");
+
+ gFindBar.getElement("findbar-textbox").value = "m";
+ ok(gFindBar.hasTransactions, "formdata can be cleared after input");
+
+ s.sanitize();
+ is(gFindBar.getElement("findbar-textbox").value, "", "findBar textbox should be empty after sanitize");
+ ok(!gFindBar.hasTransactions, "No transactions after sanitize");
+
+ finish();
+}
diff --git a/comm/suite/browser/test/browser/browser_bug413915.js b/comm/suite/browser/test/browser/browser_bug413915.js
new file mode 100644
index 0000000000..98b968a1b3
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug413915.js
@@ -0,0 +1,70 @@
+ChromeUtils.defineModuleGetter(this, "Feeds",
+ "resource:///modules/Feeds.jsm");
+
+function test() {
+ var exampleUri = Services.io.newURI("http://example.com/");
+ var secman = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager);
+ var principal = secman.createCodebasePrincipal(exampleUri, {});
+
+ function testIsFeed(aTitle, aHref, aType, aKnown) {
+ var link = {
+ title: aTitle,
+ href: aHref,
+ type: aType,
+ ownerDocument: {
+ characterSet: "UTF-8"
+ }
+ };
+ return Feeds.isValidFeed(link, principal, aKnown);
+ }
+
+ var href = "http://example.com/feed/";
+ var atomType = "application/atom+xml";
+ var funkyAtomType = " aPPLICAtion/Atom+XML ";
+ var rssType = "application/rss+xml";
+ var funkyRssType = " Application/RSS+XML ";
+ var rdfType = "application/rdf+xml";
+ var texmlType = "text/xml";
+ var appxmlType = "application/xml";
+ var noRss = "Foo";
+ var rss = "RSS";
+
+ // things that should be valid
+ ok(testIsFeed(noRss, href, atomType, false) == atomType,
+ "detect Atom feed");
+ ok(testIsFeed(noRss, href, funkyAtomType, false) == atomType,
+ "clean up and detect Atom feed");
+ ok(testIsFeed(noRss, href, rssType, false) == rssType,
+ "detect RSS feed");
+ ok(testIsFeed(noRss, href, funkyRssType, false) == rssType,
+ "clean up and detect RSS feed");
+
+ // things that should not be feeds
+ ok(testIsFeed(noRss, href, rdfType, false) == null,
+ "should not detect RDF non-feed");
+ ok(testIsFeed(rss, href, rdfType, false) == null,
+ "should not detect RDF feed from type and title");
+ ok(testIsFeed(noRss, href, texmlType, false) == null,
+ "should not detect text/xml non-feed");
+ ok(testIsFeed(rss, href, texmlType, false) == null,
+ "should not detect text/xml feed from type and title");
+ ok(testIsFeed(noRss, href, appxmlType, false) == null,
+ "should not detect application/xml non-feed");
+ ok(testIsFeed(rss, href, appxmlType, false) == null,
+ "should not detect application/xml feed from type and title");
+
+ // security check only, returns cleaned up type or "application/rss+xml"
+ ok(testIsFeed(noRss, href, atomType, true) == atomType,
+ "feed security check should return Atom type");
+ ok(testIsFeed(noRss, href, funkyAtomType, true) == atomType,
+ "feed security check should return cleaned up Atom type");
+ ok(testIsFeed(noRss, href, rssType, true) == rssType,
+ "feed security check should return RSS type");
+ ok(testIsFeed(noRss, href, funkyRssType, true) == rssType,
+ "feed security check should return cleaned up RSS type");
+ ok(testIsFeed(noRss, href, "", true) == rssType,
+ "feed security check without type should return RSS type");
+ ok(testIsFeed(noRss, href, "garbage", true) == "garbage",
+ "feed security check with garbage type should return garbage");
+}
diff --git a/comm/suite/browser/test/browser/browser_bug427559.js b/comm/suite/browser/test/browser/browser_bug427559.js
new file mode 100644
index 0000000000..bd34d89f5c
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug427559.js
@@ -0,0 +1,39 @@
+/* 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/. */
+
+/*
+ * Test bug 427559 to make sure focused elements that are no longer on the page
+ * will have focus transferred to the window when changing tabs back to that
+ * tab with the now-gone element.
+ */
+
+// Default focus on a button and have it kill itself on blur
+var testPage = 'data:text/html,<body><button onblur="this.remove();"><script>document.body.firstChild.focus();</script></body>';
+
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+
+ gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+ executeSoon(function () {
+
+ // The test page loaded, so open an empty tab, select it, then restore
+ // the test tab. This causes the test page's focused element to be removed
+ // from its document.
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.removeCurrentTab();
+
+ // Make sure focus is given to the window because the element is now gone.
+ is(document.commandDispatcher.focusedWindow, window.content,
+ "content window is focused");
+
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+ }, true);
+
+ content.location = testPage;
+}
diff --git a/comm/suite/browser/test/browser/browser_bug435325.js b/comm/suite/browser/test/browser/browser_bug435325.js
new file mode 100644
index 0000000000..dca45a6001
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug435325.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* Ensure that clicking the button in the Offline mode neterror page makes the browser go online. See bug 435325. */
+
+var proxyPrefValue;
+
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ window.addEventListener("DOMContentLoaded", checkPage);
+
+
+ // Tests always connect to localhost, and per bug 87717, localhost is now
+ // reachable in offline mode. To avoid this, disable any proxy.
+ proxyPrefValue = Services.prefs.getIntPref("network.proxy.type");
+ Services.prefs.setIntPref("network.proxy.type", 0);
+
+ // Go offline and disable the proxy and cache, then try to load the test URL.
+ Services.io.offline = true;
+ Services.prefs.setBoolPref("browser.cache.disk.enable", false);
+ Services.prefs.setBoolPref("browser.cache.memory.enable", false);
+ content.location = "http://example.com/";
+}
+
+function checkPage() {
+ if(content.location == "about:blank") {
+ info("got about:blank, which is expected once, so return");
+ return;
+ }
+
+ window.removeEventListener("DOMContentLoaded", checkPage);
+
+ ok(Services.io.offline, "Setting Services.io.offline to true.");
+ is(gBrowser.contentDocument.documentURI.substring(0,27),
+ "about:neterror?e=netOffline", "Loading the Offline mode neterror page.");
+
+ // Now press the "Try Again" button
+ ok(gBrowser.contentDocument.getElementById("errorTryAgain"),
+ "The error page has got a #errorTryAgain element");
+ gBrowser.contentDocument.getElementById("errorTryAgain").click();
+
+ ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
+ "online.");
+
+ finish();
+}
+
+registerCleanupFunction(function() {
+ Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
+ Services.prefs.setBoolPref("browser.cache.disk.enable", true);
+ Services.prefs.setBoolPref("browser.cache.memory.enable", true);
+ Services.io.offline = false;
+ gBrowser.removeCurrentTab();
+});
diff --git a/comm/suite/browser/test/browser/browser_bug462289.js b/comm/suite/browser/test/browser/browser_bug462289.js
new file mode 100644
index 0000000000..c61b9a0403
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug462289.js
@@ -0,0 +1,87 @@
+// Wanted delay (in ms) to let UI fully update.
+// 375: hopefully enough (on slow test environments).
+var gDelay = 375;
+
+var tab1, tab2;
+
+function focus_in_navbar()
+{
+ var parent = document.activeElement.parentNode;
+ while (parent && parent.id != "nav-bar")
+ parent = parent.parentNode;
+
+ return parent != null;
+}
+
+function test()
+{
+ waitForExplicitFinish();
+
+ // Ftr, SeaMonkey doesn't support animation (yet).
+ tab1 = gBrowser.addTab("about:blank");
+ tab2 = gBrowser.addTab("about:blank");
+
+ EventUtils.synthesizeMouseAtCenter(tab1, {});
+ setTimeout(step2, gDelay);
+}
+
+function step2()
+{
+ is(gBrowser.selectedTab, tab1, "1st click on tab1 selects tab");
+ isnot(document.activeElement, tab1, "1st click on tab1 does not activate tab");
+
+ EventUtils.synthesizeMouseAtCenter(tab1, {});
+ setTimeout(step3, gDelay);
+}
+
+function step3()
+{
+ is(gBrowser.selectedTab, tab1, "2nd click on selected tab1 keeps tab selected");
+ // SeaMonkey differs from Firefox.
+ is(document.activeElement, tab1, "2nd click on selected tab1 activates tab");
+
+ // Ftr, SeaMonkey doesn't support tabsontop (yet).
+ ok(true, "focusing URLBar then sending Tab(s) until out of nav-bar.");
+ document.getElementById("urlbar").focus();
+ while (focus_in_navbar())
+ EventUtils.synthesizeKey("VK_TAB", { });
+ is(gBrowser.selectedTab, tab1, "tab key to selected tab1 keeps tab selected");
+ is(document.activeElement, tab1, "tab key to selected tab1 activates tab");
+
+ EventUtils.synthesizeMouseAtCenter(tab1, {});
+ setTimeout(step4, gDelay);
+}
+
+function step4()
+{
+ is(gBrowser.selectedTab, tab1, "3rd click on activated tab1 keeps tab selected");
+ is(document.activeElement, tab1, "3rd click on activated tab1 keeps tab activated");
+
+ EventUtils.synthesizeMouseAtCenter(tab2, {});
+ setTimeout(step5, gDelay);
+}
+
+function step5()
+{
+ // The tabbox selects a tab within a setTimeout in a bubbling mousedown event
+ // listener, and focuses the current tab if another tab previously had focus.
+ is(gBrowser.selectedTab, tab2, "click on tab2 while tab1 is activated selects tab");
+ is(document.activeElement, tab2, "click on tab2 while tab1 is activated activates tab");
+
+ ok(true, "focusing content then sending middle-button mousedown to tab2.");
+ content.focus();
+ EventUtils.synthesizeMouseAtCenter(tab2, {button: 1, type: "mousedown"});
+ setTimeout(step6, gDelay);
+}
+
+function step6()
+{
+ is(gBrowser.selectedTab, tab2, "middle-button mousedown on selected tab2 keeps tab selected");
+ // SeaMonkey differs from Firefox.
+ is(document.activeElement, tab2, "middle-button mousedown on selected tab2 activates tab");
+
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab1);
+
+ finish();
+}
diff --git a/comm/suite/browser/test/browser/browser_bug519216.js b/comm/suite/browser/test/browser/browser_bug519216.js
new file mode 100644
index 0000000000..a924f7a091
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug519216.js
@@ -0,0 +1,50 @@
+function test() {
+ waitForExplicitFinish();
+ gBrowser.stop();
+ gBrowser.addProgressListener(progressListener1);
+ gBrowser.addProgressListener(progressListener2);
+ gBrowser.addProgressListener(progressListener3);
+ gBrowser.loadURI("data:text/plain,bug519216");
+}
+
+var calledListener1 = false;
+var progressListener1 = {
+ onLocationChange: function onLocationChange() {
+ calledListener1 = true;
+ gBrowser.removeProgressListener(this);
+ }
+};
+
+var calledListener2 = false;
+var progressListener2 = {
+ onLocationChange: function onLocationChange() {
+ ok(calledListener1, "called progressListener1 before progressListener2");
+ calledListener2 = true;
+ gBrowser.removeProgressListener(this);
+ }
+};
+
+var progressListener3 = {
+ onLocationChange: function onLocationChange() {
+ ok(calledListener2, "called progressListener2 before progressListener3");
+ gBrowser.removeProgressListener(this);
+ gBrowser.addProgressListener(progressListener4);
+ executeSoon(function () {
+ expectListener4 = true;
+ gBrowser.reload();
+ });
+ }
+};
+
+var expectListener4 = false;
+var progressListener4 = {
+ onLocationChange: function onLocationChange() {
+ ok(expectListener4, "didn't call progressListener4 for the first location change");
+ gBrowser.removeProgressListener(this);
+ executeSoon(function () {
+ gBrowser.addTab();
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+ }
+};
diff --git a/comm/suite/browser/test/browser/browser_bug561636.js b/comm/suite/browser/test/browser/browser_bug561636.js
new file mode 100644
index 0000000000..8f41629253
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug561636.js
@@ -0,0 +1,459 @@
+var gInvalidFormPopup = document.getElementById('invalid-form-popup');
+ok(gInvalidFormPopup,
+ "The browser should have a popup to show when a form is invalid");
+
+function checkPopupShow()
+{
+ ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open',
+ "The invalid form popup should be shown");
+}
+
+function checkPopupHide()
+{
+ ok(gInvalidFormPopup.state != 'showing' && gInvalidFormPopup.state != 'open',
+ "The invalid form popup should not be shown");
+}
+
+function checkPopupMessage(doc)
+{
+ is(gInvalidFormPopup.firstChild.textContent,
+ doc.getElementById('i').validationMessage,
+ "The panel should show the message from validationMessage");
+}
+
+var gObserver = {
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
+
+ notifyInvalidSubmit : function (aFormElement, aInvalidElements)
+ {
+ }
+};
+
+function test()
+{
+ waitForExplicitFinish();
+
+ test1();
+}
+
+/**
+ * In this test, we check that no popup appears if the form is valid.
+ */
+function test1() {
+ let uri = "data:text/html,<html><body><iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='s' type='submit'></form></body></html>";
+ let tab = gBrowser.addTab();
+
+ tab.linkedBrowser.addEventListener("load", function test1TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test1TabLBLoad, true);
+ let doc = gBrowser.contentDocument;
+
+ doc.getElementById('s').click();
+
+ executeSoon(function() {
+ checkPopupHide();
+
+ // Clean-up
+ gBrowser.removeTab(gBrowser.selectedTab);
+
+ // Next test
+ executeSoon(test2);
+ });
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that, when an invalid form is submitted,
+ * the invalid element is focused and a popup appears.
+ */
+function test2()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test2gIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test2gIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test3);
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test2TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test2TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that, when an invalid form is submitted,
+ * the first invalid element is focused and a popup appears.
+ */
+function test3()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test3gIPopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test3gIPopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test4a);
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test3TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test3TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that, we hide the popup by interacting with the
+ * invalid element if the element becomes valid.
+ */
+function test4a()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test4agIPopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test4agIPopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ EventUtils.synthesizeKey("a", {});
+
+ executeSoon(function () {
+ checkPopupHide();
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test4b);
+ });
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test4aTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test4aTabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that, we don't hide the popup by interacting with the
+ * invalid element if the element is still invalid.
+ */
+function test4b()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test4bgIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test4bgIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ EventUtils.synthesizeKey("a", {});
+
+ executeSoon(function () {
+ checkPopupShow();
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test5);
+ });
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test4bTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test4bTabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that we can hide the popup by blurring the invalid
+ * element.
+ */
+function test5()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test5gIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test5gIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ doc.getElementById('i').blur();
+
+ executeSoon(function () {
+ checkPopupHide();
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test6);
+ });
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test5TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test5TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that we can hide the popup by pressing TAB.
+ */
+function test6()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test6gIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test6gIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ EventUtils.synthesizeKey("VK_TAB", {});
+
+ executeSoon(function () {
+ checkPopupHide();
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test7);
+ });
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test6TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test6TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that the popup will hide if we move to another tab.
+ */
+function test7()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test7gIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test7gIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+ checkPopupMessage(doc);
+
+ // Create a new tab and move to it.
+ // Ftr, SeaMonkey doesn't support animation (yet).
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+ executeSoon(function() {
+ checkPopupHide();
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test8);
+ });
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test7TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test7TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that nothing happen (no focus nor popup) if the
+ * invalid form is submitted in another tab than the current focused one
+ * (submitted in background).
+ */
+function test8()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gObserver.notifyInvalidSubmit = function() {
+ executeSoon(function() {
+ let doc = tab.linkedBrowser.contentDocument;
+ isnot(doc.activeElement, doc.getElementById('i'),
+ "We should not focus the invalid element when the form is submitted in background");
+
+ checkPopupHide();
+
+ // Clean-up
+ Services.obs.removeObserver(gObserver, "invalidformsubmit");
+ gObserver.notifyInvalidSubmit = function () {};
+ gBrowser.removeTab(tab);
+
+ // Next test
+ executeSoon(test9);
+ });
+ };
+
+ Services.obs.addObserver(gObserver, "invalidformsubmit");
+
+ tab.linkedBrowser.addEventListener("load", function test8TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test8TabLBLoad, true);
+
+ isnot(gBrowser.selectedTab, tab,
+ "This tab should have been loaded in background");
+
+ tab.linkedBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ tab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that the author defined error message is shown.
+ */
+function test9()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test9gIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test9gIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ checkPopupShow();
+
+ is(gInvalidFormPopup.firstChild.textContent, "foo",
+ "The panel should show the author defined error message");
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(test10);
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test9TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test9TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
+
+/**
+ * In this test, we check that the message is correctly updated when it changes.
+ */
+function test10()
+{
+ let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function test10gIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", test10gIpopupShown);
+
+ let doc = gBrowser.contentDocument;
+ let input = doc.getElementById('i');
+ is(doc.activeElement, input, "First invalid element should be focused");
+
+ checkPopupShow();
+
+ is(gInvalidFormPopup.firstChild.textContent, input.validationMessage,
+ "The panel should show the current validation message");
+
+ input.addEventListener('input', function test10InputInput() {
+ input.removeEventListener('input', test10InputInput);
+
+ executeSoon(function() {
+ // Now, the element suffers from another error, the message should have
+ // been updated.
+ is(gInvalidFormPopup.firstChild.textContent, input.validationMessage,
+ "The panel should show the current validation message");
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab);
+ executeSoon(finish);
+ });
+ });
+
+ EventUtils.synthesizeKey('f', {});
+ });
+
+ tab.linkedBrowser.addEventListener("load", function test10TabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", test10TabLBLoad, true);
+
+ gBrowser.contentDocument.getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
diff --git a/comm/suite/browser/test/browser/browser_bug562649.js b/comm/suite/browser/test/browser/browser_bug562649.js
new file mode 100644
index 0000000000..57524a4b82
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug562649.js
@@ -0,0 +1,26 @@
+function test() {
+ const URI = "data:text/plain,bug562649";
+ browserDOMWindow.openURI(makeURI(URI),
+ null,
+ Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
+ Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+
+ is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI");
+ is(gURLBar.value, URI, "location bar value matches test URI");
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.removeCurrentTab();
+ is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs");
+ is(gURLBar.value, URI, "location bar value matches test URI after switching tabs");
+
+ waitForExplicitFinish();
+ gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+
+ is(gBrowser.userTypedValue, null, "userTypedValue is null as the page has loaded");
+ is(gURLBar.value, URI, "location bar value matches test URI as the page has loaded");
+
+ gBrowser.removeCurrentTab();
+ finish();
+ }, true);
+}
diff --git a/comm/suite/browser/test/browser/browser_bug581947.js b/comm/suite/browser/test/browser/browser_bug581947.js
new file mode 100644
index 0000000000..5ac81b1218
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug581947.js
@@ -0,0 +1,95 @@
+function check(aElementName, aBarred, aType) {
+ let doc = gBrowser.contentDocument;
+ let tooltip = document.getElementById("aHTMLTooltip");
+ let content = doc.getElementById('content');
+
+ let e = doc.createElement(aElementName);
+ content.appendChild(e);
+
+ if (aType) {
+ e.type = aType;
+ }
+
+ ok(!FillInHTMLTooltip(e),
+ "No tooltip should be shown when the element is valid");
+
+ e.setCustomValidity('foo');
+ if (aBarred) {
+ ok(!FillInHTMLTooltip(e),
+ "No tooltip should be shown when the element is barred from constraint validation");
+ } else {
+ ok(FillInHTMLTooltip(e),
+ e.tagName + " " +"A tooltip should be shown when the element isn't valid");
+ }
+
+ e.setAttribute('title', '');
+ ok (!FillInHTMLTooltip(e),
+ "No tooltip should be shown if the title attribute is set");
+
+ e.removeAttribute('title');
+ content.setAttribute('novalidate', '');
+ ok (!FillInHTMLTooltip(e),
+ "No tooltip should be shown if the novalidate attribute is set on the form owner");
+ content.removeAttribute('novalidate');
+
+ e.remove();
+}
+
+function todo_check(aElementName, aBarred) {
+ let doc = gBrowser.contentDocument;
+ let tooltip = document.getElementById("aHTMLTooltip");
+ let content = doc.getElementById('content');
+
+ let e = doc.createElement(aElementName);
+ content.appendChild(e);
+
+ let caught = false;
+ try {
+ e.setCustomValidity('foo');
+ } catch (e) {
+ caught = true;
+ }
+
+ todo(!caught, "setCustomValidity should exist for " + aElementName);
+
+ e.remove();
+}
+
+function test() {
+ waitForExplicitFinish();
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+
+ let testData = [
+ /* element name, barred */
+ [ 'input', false, null],
+ [ 'textarea', false, null],
+ [ 'button', true, 'button'],
+ [ 'button', false, 'submit'],
+ [ 'select', false, null],
+ [ 'output', true, null],
+ [ 'fieldset', true, null],
+ [ 'object', true, null]
+ ];
+
+ for (let data of testData) {
+ check(data[0], data[1], data[2]);
+ }
+
+ let todo_testData = [
+ [ 'keygen', 'false' ]
+ ];
+
+ for (let data of todo_testData) {
+ todo_check(data[0], data[1]);
+ }
+
+ gBrowser.removeCurrentTab();
+ finish();
+ }, true);
+
+ content.location =
+ "data:text/html,<!DOCTYPE html><html><body><form id='content'></form></body></html>";
+}
+
diff --git a/comm/suite/browser/test/browser/browser_bug585511.js b/comm/suite/browser/test/browser/browser_bug585511.js
new file mode 100644
index 0000000000..f6513cac5a
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug585511.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+ is(getBrowser().tabs.length, 1, "one tab is open initially");
+ is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync");
+
+ // Add several new tabs
+ let tab1 = getBrowser().addTab("http://mochi.test:8888/#1");
+ let tab2 = getBrowser().addTab("http://mochi.test:8888/#2");
+ let tab3 = getBrowser().addTab("http://mochi.test:8888/#3");
+ is(getBrowser().tabs.length, 4, "four tabs are open");
+ is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync");
+ getBrowser().removeTab(tab2);
+ is(getBrowser().tabs.length, 3, "three tabs are open");
+ is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync");
+ getBrowser().removeTab(tab1);
+ is(getBrowser().tabs.length, 2, "two tabs are open");
+ is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync");
+ getBrowser().removeTab(tab3);
+ is(getBrowser().tabs.length, 1, "we've closed all our tabs");
+ is(getBrowser().browsers.length, getBrowser().tabs.length, ".browsers is in sync");
+}
diff --git a/comm/suite/browser/test/browser/browser_bug595507.js b/comm/suite/browser/test/browser/browser_bug595507.js
new file mode 100644
index 0000000000..0bea8cabc6
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug595507.js
@@ -0,0 +1,39 @@
+var gInvalidFormPopup = document.getElementById('invalid-form-popup');
+ok(gInvalidFormPopup,
+ "The browser should have a popup to show when a form is invalid");
+
+/**
+ * Make sure that the form validation error message shows even if the form is in an iframe.
+ */
+function test()
+{
+ waitForExplicitFinish();
+
+ let uri = "data:text/html,<iframe src=\"data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>\"</iframe>";
+ let tab = gBrowser.addTab();
+
+ gInvalidFormPopup.addEventListener("popupshown", function testgIpopupShown() {
+ gInvalidFormPopup.removeEventListener("popupshown", testgIpopupShown);
+
+ let doc = gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument;
+ is(doc.activeElement, doc.getElementById('i'),
+ "First invalid element should be focused");
+
+ ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open',
+ "The invalid form popup should be shown");
+
+ // Clean-up and next test.
+ gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
+ executeSoon(finish);
+ });
+
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+
+ gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument
+ .getElementById('s').click();
+ }, true);
+
+ gBrowser.selectedTab = tab;
+ gBrowser.selectedTab.linkedBrowser.loadURI(uri);
+}
diff --git a/comm/suite/browser/test/browser/browser_bug623155.js b/comm/suite/browser/test/browser/browser_bug623155.js
new file mode 100644
index 0000000000..df8197a565
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_bug623155.js
@@ -0,0 +1,136 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const REDIRECT_FROM = "https://example.com/browser/suite/browser/test/browser/" +
+ "redirect_bug623155.sjs";
+
+const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host.
+
+function isRedirectedURISpec(aURISpec) {
+ return isRedirectedURI(Services.io.newURI(aURISpec));
+}
+
+function isRedirectedURI(aURI) {
+ // Compare only their before-hash portion.
+ return Services.io.newURI(REDIRECT_TO)
+ .equalsExceptRef(aURI);
+}
+
+/*
+ Test.
+
+1. Load
+https://example.com/browser/browser/base/content/test/redirect_bug623155.sjs#BG
+ in a background tab.
+
+2. The redirected URI is <https://www.bank1.com/#BG>, which displayes a cert
+ error page.
+
+3. Switch the tab to foreground.
+
+4. Check the URLbar's value, expecting <https://www.bank1.com/#BG>
+
+5. Load
+https://example.com/browser/browser/base/content/test/redirect_bug623155.sjs#FG
+ in the foreground tab.
+
+6. The redirected URI is <https://www.bank1.com/#FG>. And this is also
+ a cert-error page.
+
+7. Check the URLbar's value, expecting <https://www.bank1.com/#FG>
+
+8. End.
+
+ */
+
+var gNewTab;
+
+function test() {
+ waitForExplicitFinish();
+
+ // Load a URI in the background.
+ gNewTab = gBrowser.addTab(REDIRECT_FROM + "#BG");
+ gBrowser.getBrowserForTab(gNewTab)
+ .webProgress
+ .addProgressListener(gWebProgressListener,
+ Ci.nsIWebProgress
+ .NOTIFY_LOCATION);
+}
+
+var gWebProgressListener = {
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ // ---------------------------------------------------------------------------
+ // NOTIFY_LOCATION mode should work fine without these methods.
+ //
+ //onStateChange: function() {},
+ //onStatusChange: function() {},
+ //onProgressChange: function() {},
+ //onSecurityChange: function() {},
+ //----------------------------------------------------------------------------
+
+ onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
+ if (!aRequest) {
+ // This is bug 673752, or maybe initial "about:blank".
+ return;
+ }
+
+ ok(gNewTab, "There is a new tab.");
+ ok(isRedirectedURI(aLocation),
+ "onLocationChange catches only redirected URI.");
+
+ if (aLocation.ref == "BG") {
+ // This is background tab's request.
+ isnot(gNewTab, gBrowser.selectedTab, "This is a background tab.");
+ } else if (aLocation.ref == "FG") {
+ // This is foreground tab's request.
+ is(gNewTab, gBrowser.selectedTab, "This is a foreground tab.");
+ }
+ else {
+ // We shonuld not reach here.
+ ok(false, "This URI hash is not expected:" + aLocation.ref);
+ }
+
+ let isSelectedTab = (gNewTab == gBrowser.selectedTab);
+ setTimeout(delayed, 0, isSelectedTab);
+ }
+};
+
+function delayed(aIsSelectedTab) {
+ // Switch tab and confirm URL bar.
+ if (!aIsSelectedTab) {
+ gBrowser.selectedTab = gNewTab;
+ }
+
+ ok(isRedirectedURISpec(content.location.href),
+ "The content area is redirected. aIsSelectedTab:" + aIsSelectedTab);
+ is(gURLBar.value, content.location.href,
+ "The URL bar shows the content URI. aIsSelectedTab:" + aIsSelectedTab);
+
+ if (!aIsSelectedTab) {
+ // If this was a background request, go on a foreground request.
+ content.location = REDIRECT_FROM + "#FG";
+ }
+ else {
+ // Othrewise, nothing to do remains.
+ finish();
+ }
+}
+
+/* Cleanup */
+registerCleanupFunction(function() {
+ if (gNewTab) {
+ gBrowser.getBrowserForTab(gNewTab)
+ .webProgress
+ .removeProgressListener(gWebProgressListener);
+
+ gBrowser.removeTab(gNewTab);
+ }
+ gNewTab = null;
+});
diff --git a/comm/suite/browser/test/browser/browser_ctrlTab.js b/comm/suite/browser/test/browser/browser_ctrlTab.js
new file mode 100644
index 0000000000..aea07917a3
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_ctrlTab.js
@@ -0,0 +1,197 @@
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.addTab();
+ gBrowser.addTab();
+ gBrowser.addTab();
+
+ assertTabs(4);
+
+ ctrlTabTest([2] , 1, 0);
+ ctrlTabTest([2, 3, 1], 2, 2);
+ ctrlTabTest([] , 4, 2);
+
+ {
+ let selectedIndex = gBrowser.tabContainer.selectedIndex;
+ pressCtrlTab();
+ pressCtrlTab(true);
+ releaseCtrl();
+ is(gBrowser.tabContainer.selectedIndex, selectedIndex,
+ "Ctrl+Tab -> Ctrl+Shift+Tab keeps the selected tab");
+ }
+
+ { // test for bug 445369
+ let tabs = gBrowser.tabs.length;
+ pressCtrlTab();
+ EventUtils.synthesizeKey("w", { ctrlKey: true });
+ is(gBrowser.tabs.length, tabs - 1, "Ctrl+Tab -> Ctrl+W removes one tab");
+ releaseCtrl();
+ }
+ assertTabs(3);
+
+ ctrlTabTest([2, 1, 0], 7, 1);
+
+ { // test for bug 445369
+ selectTabs([1, 2, 0]);
+
+ let selectedTab = gBrowser.selectedTab;
+ let tabToRemove = gBrowser.tabs[1];
+
+ pressCtrlTab();
+ pressCtrlTab();
+ EventUtils.synthesizeKey("w", { ctrlKey: true });
+ ok(!tabToRemove.parentNode,
+ "Ctrl+Tab*2 -> Ctrl+W removes the second most recently selected tab");
+
+ pressCtrlTab(true);
+ pressCtrlTab(true);
+ releaseCtrl();
+ ok(gBrowser.selectedTab == selectedTab,
+ "Ctrl+Tab*2 -> Ctrl+W -> Ctrl+Shift+Tab*2 keeps the selected tab");
+ }
+ assertTabs(2);
+
+ ctrlTabTest([1], 1, 0);
+
+ gBrowser.removeTab(gBrowser.tabContainer.lastChild);
+
+ assertTabs(1);
+
+ { // test for bug 445768
+ let focusedWindow = document.commandDispatcher.focusedWindow;
+ let eventConsumed = true;
+ let detectKeyEvent = function (event) {
+ eventConsumed = event.defaultPrevented;
+ };
+ document.addEventListener("keypress", detectKeyEvent);
+ pressCtrlTab();
+ document.removeEventListener("keypress", detectKeyEvent);
+ ok(eventConsumed, "Ctrl+Tab consumed by the tabbed browser if one tab is open");
+ is(focusedWindow.location, document.commandDispatcher.focusedWindow.location,
+ "Ctrl+Tab doesn't change focus if one tab is open");
+ }
+
+ gBrowser.addTab();
+ gBrowser.addTab();
+ gBrowser.addTab();
+
+ assertTabs(4);
+ selectTabs([0, 1, 2, 3]);
+ pressCtrlTab();
+ ctrlTab.panel.addEventListener("popupshown", stickyTests);
+
+ function stickyTests() {
+ ctrlTab.panel.removeEventListener("popupshown", stickyTests);
+
+ EventUtils.synthesizeKey("f", { ctrlKey: true });
+ is(document.activeElement, ctrlTab.searchField.inputField,
+ "Ctrl+Tab -> Ctrl+F focuses the panel's search field");
+
+ releaseCtrl();
+ ok(isOpen(),
+ "panel is sticky after focusing the search field and releasing the Ctrl key");
+
+ EventUtils.synthesizeKey("f", {});
+ EventUtils.synthesizeKey("o", {});
+ EventUtils.synthesizeKey("o", {});
+ is(ctrlTab.searchField.value, "foo",
+ "text entered into search field");
+
+ EventUtils.synthesizeKey("VK_RETURN", {});
+ ok(isOpen(),
+ "Enter key kicks pending search off; the panel stays open as there's no match");
+ is(ctrlTab.searchField.value, "foo",
+ "search field value persists after Enter pressed");
+
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ is(ctrlTab.searchField.value, "",
+ "ESC key clears the search field");
+ ok(isOpen(),
+ "Clearing the search field with ESC keeps the panel open");
+
+ // blur the search field
+ EventUtils.synthesizeKey("VK_TAB", {});
+ isnot(document.activeElement, ctrlTab.searchField.inputField,
+ "Tab key blurs the panel's search field");
+
+ // advance selection and close panel
+ EventUtils.synthesizeKey("VK_TAB", {});
+ EventUtils.synthesizeKey("VK_TAB", {});
+ EventUtils.synthesizeKey("VK_RETURN", {});
+ ok(!isOpen(),
+ "Enter key closes the panel");
+ is(gBrowser.tabContainer.selectedIndex, 1,
+ "Tab key advances the selection while the panel is sticky");
+
+ gBrowser.removeCurrentTab();
+ gBrowser.removeCurrentTab();
+ gBrowser.removeCurrentTab();
+ assertTabs(1);
+ finish();
+ }
+
+
+ /* private utility functions */
+
+ function pressCtrlTab(aShiftKey) {
+ EventUtils.synthesizeKey("VK_TAB", { ctrlKey: true, shiftKey: !!aShiftKey });
+ }
+
+ function releaseCtrl() {
+ EventUtils.synthesizeKey("VK_CONTROL", { type: "keyup" });
+ }
+
+ function isOpen() {
+ return ctrlTab.panel.state == "showing" || ctrlTab.panel.state == "open";
+ }
+
+ function assertTabs(aTabs) {
+ var tabs = gBrowser.tabs.length;
+ if (tabs != aTabs) {
+ while (gBrowser.tabs.length > 1)
+ gBrowser.removeCurrentTab();
+ throw "expected " + aTabs + " open tabs, got " + tabs;
+ }
+ }
+
+ function selectTabs(tabs) {
+ tabs.forEach(function (index) {
+ gBrowser.selectedTab = gBrowser.tabs[index];
+ });
+ }
+
+ function ctrlTabTest(tabsToSelect, tabTimes, expectedIndex) {
+ selectTabs(tabsToSelect);
+
+ var indexStart = gBrowser.tabContainer.selectedIndex;
+ var tabCount = gBrowser.tabs.length;
+ var normalized = tabTimes % tabCount;
+ var where = normalized == 1 ? "back to the previously selected tab" :
+ normalized + " tabs back in most-recently-selected order";
+
+ for (let i = 0; i < tabTimes; i++) {
+ pressCtrlTab();
+
+ if (tabCount > 2)
+ is(gBrowser.tabContainer.selectedIndex, indexStart,
+ "Selected tab doesn't change while tabbing");
+ }
+
+ if (tabCount > 2) {
+ ok(isOpen(),
+ "With " + tabCount + " tabs open, Ctrl+Tab opens the preview panel");
+
+ releaseCtrl();
+
+ ok(!isOpen(),
+ "Releasing Ctrl closes the preview panel");
+ } else {
+ ok(!isOpen(),
+ "With " + tabCount + " tabs open, Ctrl+Tab doesn't open the preview panel");
+ }
+
+ is(gBrowser.tabContainer.selectedIndex, expectedIndex,
+ "With "+ tabCount +" tabs open and tab " + indexStart
+ + " selected, Ctrl+Tab*" + tabTimes + " goes " + where);
+ }
+}
diff --git a/comm/suite/browser/test/browser/browser_fayt.js b/comm/suite/browser/test/browser/browser_fayt.js
new file mode 100644
index 0000000000..8522bc366b
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_fayt.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+ var tab1 = gBrowser.addTab("data:text/html;charset=utf-8,<p>this is some dummy text</p>");
+ var tab2 = gBrowser.addTab("data:text/html;charset=utf-8,<p>this is some random text</p>");
+
+ gBrowser.getBrowserForTab(tab2).addEventListener("load", runTest, true);
+ waitForExplicitFinish();
+
+ function runTest() {
+ gBrowser.getBrowserForTab(tab2).removeEventListener("load", runTest, true);
+
+ gBrowser.selectedTab = tab2;
+ is(gBrowser.fastFind.find("random", false), Ci.nsITypeAheadFind.FIND_FOUND, "FAYT found the random text");
+ gBrowser.selectedTab = tab1;
+ is(gBrowser.fastFind.find("dummy", false), Ci.nsITypeAheadFind.FIND_FOUND, "FAYT found the dummy text");
+
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab1);
+ finish();
+ }
+}
+
diff --git a/comm/suite/browser/test/browser/browser_notification_tab_switching.js b/comm/suite/browser/test/browser/browser_notification_tab_switching.js
new file mode 100644
index 0000000000..2cbdaa3147
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_notification_tab_switching.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+var tab;
+var notification;
+var notificationURL = "http://example.org/browser/suite/browser/test/browser/file_dom_notifications.html";
+var newWindowOpenedFromTab;
+
+function test () {
+ waitForExplicitFinish();
+
+ let pm = Services.perms;
+ registerCleanupFunction(function() {
+ pm.remove(makeURI(notificationURL), "desktop-notification");
+ gBrowser.removeTab(tab);
+ window.restore();
+ });
+
+ pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
+
+ tab = gBrowser.addTab(notificationURL);
+ tab.linkedBrowser.addEventListener("load", onLoad, true);
+}
+
+function onLoad() {
+ isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+ tab.linkedBrowser.removeEventListener("load", onLoad, true);
+ let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+ win.newWindow = win.open("about:blank", "", "height=100,width=100");
+ newWindowOpenedFromTab = win.newWindow;
+ win.newWindow.addEventListener("load", function() {
+ info("new window loaded");
+ win.newWindow.addEventListener("blur", function b() {
+ info("new window got blur");
+ win.newWindow.removeEventListener("blur", b);
+ notification = win.showNotification1();
+ win.newWindow.addEventListener("focus", onNewWindowFocused);
+ notification.addEventListener("show", onAlertShowing);
+ });
+
+ function waitUntilNewWindowHasFocus() {
+ if (!win.newWindow.document.hasFocus()) {
+ setTimeout(waitUntilNewWindowHasFocus, 50);
+ } else {
+ // Focus another window so that new window gets blur event.
+ gBrowser.selectedBrowser.contentWindow.focus();
+ }
+ }
+ win.newWindow.focus();
+ waitUntilNewWindowHasFocus();
+ });
+}
+
+function onAlertShowing() {
+ info("Notification alert showing");
+ notification.removeEventListener("show", onAlertShowing);
+
+ let alertWindow = findChromeWindowByURI("chrome://global/content/alerts/alert.xul");
+ if (!alertWindow) {
+ todo(false, "Notifications don't use XUL windows on all platforms.");
+ notification.close();
+ newWindowOpenedFromTab.close();
+ finish();
+ return;
+ }
+ gBrowser.tabContainer.addEventListener("TabSelect", onTabSelect);
+ EventUtils.synthesizeMouseAtCenter(alertWindow.document.getElementById("alertTitleLabel"), {}, alertWindow);
+ info("Clicked on notification");
+ alertWindow.close();
+}
+
+function onNewWindowFocused(event) {
+ event.target.close();
+ isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+ // Using timeout to test that something do *not* happen!
+ setTimeout(openSecondNotification, 50);
+}
+
+function openSecondNotification() {
+ isnot(gBrowser.selectedTab, tab, "Notification page loaded as a background tab");
+ let win = tab.linkedBrowser.contentWindow.wrappedJSObject;
+ notification = win.showNotification2();
+ notification.addEventListener("show", onAlertShowing);
+}
+
+function onTabSelect() {
+ gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect);
+ is(gBrowser.selectedBrowser.contentWindow.location.href, notificationURL,
+ "Notification tab should be selected.");
+
+ finish();
+}
diff --git a/comm/suite/browser/test/browser/browser_pageInfo.js b/comm/suite/browser/test/browser/browser_pageInfo.js
new file mode 100644
index 0000000000..399301a784
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_pageInfo.js
@@ -0,0 +1,38 @@
+function test() {
+ waitForExplicitFinish();
+
+ var pageInfo;
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
+ gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
+
+ Services.obs.addObserver(observer, "page-info-dialog-loaded");
+ pageInfo = BrowserPageInfo();
+ }, true);
+ content.location =
+ "https://example.com/browser/suite/browser/test/browser/feed_tab.html";
+
+ function observer(win, topic, data) {
+ Services.obs.removeObserver(observer, "page-info-dialog-loaded");
+ handlePageInfo();
+ }
+
+ function handlePageInfo() {
+ ok(pageInfo.document.getElementById("feedPanel"), "Feed panel");
+ let feedListbox = pageInfo.document.getElementById("feedListbox");
+ ok(feedListbox, "Feed list");
+
+ var feedRowsNum = feedListbox.getRowCount();
+ is(feedRowsNum, 3, "Number of feeds listed");
+
+ for (var i = 0; i < feedRowsNum; i++) {
+ let feedItem = feedListbox.getItemAtIndex(i);
+ is(feedItem.getAttribute("name"), i + 1, "Feed name");
+ }
+
+ pageInfo.close();
+ gBrowser.removeCurrentTab();
+ finish();
+ }
+}
diff --git a/comm/suite/browser/test/browser/browser_page_style_menu.js b/comm/suite/browser/test/browser/browser_page_style_menu.js
new file mode 100644
index 0000000000..88ce97da0b
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_page_style_menu.js
@@ -0,0 +1,67 @@
+function test() {
+ waitForExplicitFinish();
+
+ var tab = gBrowser.addTab();
+ gBrowser.selectedTab = tab;
+ tab.linkedBrowser.addEventListener("load", function loadListener() {
+ tab.linkedBrowser.removeEventListener("load", loadListener, true);
+ checkPageStyleMenu();
+ }, true);
+ let rootDir = getRootDirectory(gTestPath);
+ content.location = rootDir + "page_style_sample.html";
+}
+
+function checkPageStyleMenu() {
+ var menupopup = document.getElementById("menu_UseStyleSheet")
+ .getElementsByTagName("menupopup")[0];
+ stylesheetFillPopup(menupopup);
+
+ var items = [];
+ var current = menupopup.getElementsByTagName("menuitem")[1];
+ while (current.nextSibling) {
+ current = current.nextSibling;
+ items.push(current);
+ }
+
+ var validLinks = 0;
+ Array.from(content.document.getElementsByTagName("link")).forEach(function (link) {
+ var title = link.getAttribute("title");
+ var rel = link.getAttribute("rel");
+ var media = link.getAttribute("media");
+ var idstring = "link " + (title ? title : "without title and") +
+ " with rel=\"" + rel + "\"" +
+ (media ? " and media=\"" + media + "\"" : "");
+
+ var item = items.filter(item => item.getAttribute("label") == title);
+ var found = item.length == 1;
+ var checked = found && (item[0].getAttribute("checked") == "true");
+
+ switch (link.getAttribute("data-state")) {
+ case "0":
+ ok(!found, idstring + " does not show up in page style menu");
+ break;
+ case "0-todo":
+ validLinks++;
+ todo(!found, idstring + " should not show up in page style menu");
+ ok(!checked, idstring + " is not selected");
+ break;
+ case "1":
+ validLinks++;
+ ok(found, idstring + " shows up in page style menu");
+ ok(!checked, idstring + " is not selected");
+ break;
+ case "2":
+ validLinks++;
+ ok(found, idstring + " shows up in page style menu");
+ ok(checked, idstring + " is selected");
+ break;
+ default:
+ throw "data-state attribute is missing or has invalid value";
+ }
+ });
+
+ is(validLinks, items.length, "all valid links found");
+
+ gBrowser.removeCurrentTab();
+ finish();
+}
diff --git a/comm/suite/browser/test/browser/browser_popupNotification.js b/comm/suite/browser/test/browser/browser_popupNotification.js
new file mode 100644
index 0000000000..da88243e66
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_popupNotification.js
@@ -0,0 +1,782 @@
+/* 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/. */
+
+function test() {
+ waitForExplicitFinish();
+
+ ok(PopupNotifications, "PopupNotifications object exists");
+ ok(PopupNotifications.panel, "PopupNotifications panel exists");
+
+ registerCleanupFunction(cleanUp);
+
+ runNextTest();
+}
+
+function cleanUp() {
+ for (var topic in gActiveObservers)
+ Services.obs.removeObserver(gActiveObservers[topic], topic);
+ for (var eventName in gActiveListeners)
+ PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName]);
+}
+
+var gActiveListeners = {};
+var gActiveObservers = {};
+var gShownState = {};
+
+function runNextTest() {
+ let nextTest = tests[gTestIndex];
+
+ function goNext() {
+ if (++gTestIndex == tests.length)
+ executeSoon(finish);
+ else
+ executeSoon(runNextTest);
+ }
+
+ function addObserver(topic) {
+ function observer() {
+ Services.obs.removeObserver(observer, "PopupNotifications-" + topic);
+ delete gActiveObservers["PopupNotifications-" + topic];
+
+ info("[Test #" + gTestIndex + "] observer for " + topic + " called");
+ nextTest[topic]();
+ goNext();
+ }
+ Services.obs.addObserver(observer, "PopupNotifications-" + topic);
+ gActiveObservers["PopupNotifications-" + topic] = observer;
+ }
+
+ if (nextTest.backgroundShow) {
+ addObserver("backgroundShow");
+ } else if (nextTest.updateNotShowing) {
+ addObserver("updateNotShowing");
+ } else {
+ doOnPopupEvent("popupshowing", function () {
+ info("[Test #" + gTestIndex + "] popup showing");
+ });
+ doOnPopupEvent("popupshown", function () {
+ gShownState[gTestIndex] = true;
+ info("[Test #" + gTestIndex + "] popup shown");
+ nextTest.onShown(this);
+ });
+
+ // We allow multiple onHidden functions to be defined in an array. They're
+ // called in the order they appear.
+ let onHiddenArray = nextTest.onHidden instanceof Array ?
+ nextTest.onHidden :
+ [nextTest.onHidden];
+ doOnPopupEvent("popuphidden", function () {
+ if (!gShownState[gTestIndex]) {
+ // This is expected to happen for test 9, so let's not treat it as a failure.
+ info("Popup from test " + gTestIndex + " was hidden before its popupshown fired");
+ }
+
+ let onHidden = onHiddenArray.shift();
+ info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)");
+ executeSoon(function () {
+ onHidden.call(nextTest, this);
+ if (!onHiddenArray.length)
+ goNext();
+ });
+ }, onHiddenArray.length);
+ info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen);
+ }
+
+ info("[Test #" + gTestIndex + "] running test");
+ nextTest.run();
+}
+
+function doOnPopupEvent(eventName, callback, numExpected) {
+ gActiveListeners[eventName] = function (event) {
+ if (event.target != PopupNotifications.panel)
+ return;
+ if (typeof(numExpected) === "number")
+ numExpected--;
+ if (!numExpected) {
+ PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName]);
+ delete gActiveListeners[eventName];
+ }
+
+ callback.call(PopupNotifications.panel);
+ }
+ PopupNotifications.panel.addEventListener(eventName, gActiveListeners[eventName]);
+}
+
+var gTestIndex = 0;
+var gNewTab;
+
+function basicNotification() {
+ var self = this;
+ this.browser = gBrowser.selectedBrowser;
+ this.id = "test-notification-" + gTestIndex;
+ this.message = "This is popup notification " + this.id + " from test " + gTestIndex;
+ this.anchorID = null;
+ this.mainAction = {
+ label: "Main Action",
+ accessKey: "M",
+ callback: function () {
+ self.mainActionClicked = true;
+ }
+ };
+ this.secondaryActions = [
+ {
+ label: "Secondary Action",
+ accessKey: "S",
+ callback: function () {
+ self.secondaryActionClicked = true;
+ }
+ }
+ ];
+ this.options = {
+ eventCallback: function (eventName) {
+ switch (eventName) {
+ case "dismissed":
+ self.dismissalCallbackTriggered = true;
+ break;
+ case "shown":
+ self.shownCallbackTriggered = true;
+ break;
+ case "removed":
+ self.removedCallbackTriggered = true;
+ break;
+ }
+ }
+ };
+ this.addOptions = function(options) {
+ for (let [name, value] of Object.entries(options))
+ self.options[name] = value;
+ }
+}
+
+var wrongBrowserNotificationObject = new basicNotification();
+var wrongBrowserNotification;
+
+var tests = [
+ { // Test #0
+ run: function () {
+ this.notifyObj = new basicNotification();
+ showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ triggerMainCommand(popup);
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.mainActionClicked, "mainAction was clicked");
+ ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ { // Test #1
+ run: function () {
+ this.notifyObj = new basicNotification();
+ showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ triggerSecondaryCommand(popup, 0);
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.secondaryActionClicked, "secondaryAction was clicked");
+ ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ { // Test #2
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ dismissNotification(popup);
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
+ this.notification.remove();
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // test opening a notification for a background browser
+ { // Test #3
+ run: function () {
+ gNewTab = gBrowser.addTab("about:blank");
+ isnot(gBrowser.selectedTab, gNewTab, "new tab isn't selected");
+ wrongBrowserNotificationObject.browser = gBrowser.getBrowserForTab(gNewTab);
+ wrongBrowserNotification = showNotification(wrongBrowserNotificationObject);
+ },
+ backgroundShow: function () {
+ is(PopupNotifications.isPanelOpen, false, "panel isn't open");
+ ok(!wrongBrowserNotificationObject.mainActionClicked, "main action wasn't clicked");
+ ok(!wrongBrowserNotificationObject.secondaryActionClicked, "secondary action wasn't clicked");
+ ok(!wrongBrowserNotificationObject.dismissalCallbackTriggered, "dismissal callback wasn't called");
+ }
+ },
+ // now select that browser and test to see that the notification appeared
+ { // Test #4
+ run: function () {
+ this.oldSelectedTab = gBrowser.selectedTab;
+ gBrowser.selectedTab = gNewTab;
+ },
+ onShown: function (popup) {
+ checkPopup(popup, wrongBrowserNotificationObject);
+ is(PopupNotifications.isPanelOpen, true, "isPanelOpen getter doesn't lie");
+
+ // switch back to the old browser
+ gBrowser.selectedTab = this.oldSelectedTab;
+ },
+ onHidden: function (popup) {
+ // actually remove the notification to prevent it from reappearing
+ ok(wrongBrowserNotificationObject.dismissalCallbackTriggered, "dismissal callback triggered due to tab switch");
+ wrongBrowserNotification.remove();
+ ok(wrongBrowserNotificationObject.removedCallbackTriggered, "removed callback triggered");
+ wrongBrowserNotification = null;
+ }
+ },
+ // test that the removed notification isn't shown on browser re-select
+ { // Test #5
+ run: function () {
+ gBrowser.selectedTab = gNewTab;
+ },
+ updateNotShowing: function () {
+ is(PopupNotifications.isPanelOpen, false, "panel isn't open");
+ gBrowser.removeTab(gNewTab);
+ }
+ },
+ // Test that two notifications with the same ID result in a single displayed
+ // notification.
+ { // Test #6
+ run: function () {
+ this.notifyObj = new basicNotification();
+ // Show the same notification twice
+ this.notification1 = showNotification(this.notifyObj);
+ this.notification2 = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ this.notification2.remove();
+ },
+ onHidden: function (popup) {
+ ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // Test that two notifications with different IDs are displayed
+ { // Test #7
+ run: function () {
+ this.testNotif1 = new basicNotification();
+ this.testNotif1.message += " 1";
+ showNotification(this.testNotif1);
+ this.testNotif2 = new basicNotification();
+ this.testNotif2.message += " 2";
+ this.testNotif2.id += "-2";
+ showNotification(this.testNotif2);
+ },
+ onShown: function (popup) {
+ is(popup.childNodes.length, 2, "two notifications are shown");
+ // Trigger the main command for the first notification, and the secondary
+ // for the second. Need to do mainCommand first since the secondaryCommand
+ // triggering is async.
+ triggerMainCommand(popup);
+ is(popup.childNodes.length, 1, "only one notification left");
+ triggerSecondaryCommand(popup, 0);
+ },
+ onHidden: function (popup) {
+ ok(this.testNotif1.mainActionClicked, "main action #1 was clicked");
+ ok(!this.testNotif1.secondaryActionClicked, "secondary action #1 wasn't clicked");
+ ok(!this.testNotif1.dismissalCallbackTriggered, "dismissal callback #1 wasn't called");
+
+ ok(!this.testNotif2.mainActionClicked, "main action #2 wasn't clicked");
+ ok(this.testNotif2.secondaryActionClicked, "secondary action #2 was clicked");
+ ok(!this.testNotif2.dismissalCallbackTriggered, "dismissal callback #2 wasn't called");
+ }
+ },
+ // Test notification without mainAction
+ { // Test #8
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notifyObj.mainAction = null;
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ dismissNotification(popup);
+ },
+ onHidden: function (popup) {
+ this.notification.remove();
+ }
+ },
+ // Test two notifications with different anchors
+ { // Test #9
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.firstNotification = showNotification(this.notifyObj);
+ this.notifyObj2 = new basicNotification();
+ this.notifyObj2.id += "-2";
+ this.notifyObj2.anchorID = "addons-notification-icon";
+ // Second showNotification() overrides the first
+ this.secondNotification = showNotification(this.notifyObj2);
+ },
+ onShown: function (popup) {
+ // This also checks that only one element is shown.
+ checkPopup(popup, this.notifyObj2);
+ is(document.getElementById("geo-notification-icon").boxObject.width, 0,
+ "geo anchor shouldn't be visible");
+ dismissNotification(popup);
+ },
+ onHidden: [
+ // The second showing triggers a popuphidden event that we should ignore.
+ function (popup) {},
+ function (popup) {
+ // Remove the notifications
+ this.firstNotification.remove();
+ this.secondNotification.remove();
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered");
+ }
+ ]
+ },
+ // Test optional params
+ { // Test #10
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notifyObj.secondaryActions = undefined;
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ dismissNotification(popup);
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
+ this.notification.remove();
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // Test that icons appear
+ { // Test #11
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notifyObj.id = "geolocation";
+ this.notifyObj.anchorID = "geo-notification-icon";
+ // Fake these options (with arbitrary values),
+ // to avoid strict warnings in SeaMonkey notification constructor.
+ this.notifyObj.addOptions({
+ value: "Learn More...",
+ href: "http://www.seamonkey-project.org/doc/2.0/geolocation"
+ });
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ isnot(document.getElementById("geo-notification-icon").boxObject.width, 0,
+ "geo anchor should be visible");
+ dismissNotification(popup);
+ },
+ onHidden: function (popup) {
+ let icon = document.getElementById("geo-notification-icon");
+ isnot(icon.boxObject.width, 0,
+ "geo anchor should be visible after dismissal");
+ this.notification.remove();
+ is(icon.boxObject.width, 0,
+ "geo anchor should not be visible after removal");
+ }
+ },
+ // Test that persistence allows the notification to persist across reloads
+ { // Test #12
+ run: function () {
+ this.oldSelectedTab = gBrowser.selectedTab;
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+ let self = this;
+ loadURI("http://example.com/", function() {
+ self.notifyObj = new basicNotification();
+ self.notifyObj.addOptions({
+ persistence: 2
+ });
+ self.notification = showNotification(self.notifyObj);
+ });
+ },
+ onShown: function (popup) {
+ this.complete = false;
+
+ let self = this;
+ loadURI("http://example.org/", function() {
+ loadURI("http://example.com/", function() {
+
+ // Next load will remove the notification
+ self.complete = true;
+
+ loadURI("http://example.org/");
+ });
+ });
+ },
+ onHidden: function (popup) {
+ ok(this.complete, "Should only have hidden the notification after 3 page loads");
+ ok(this.notifyObj.removedCallbackTriggered, "removal callback triggered");
+ gBrowser.removeTab(gBrowser.selectedTab);
+ gBrowser.selectedTab = this.oldSelectedTab;
+ }
+ },
+ // Test that a timeout allows the notification to persist across reloads
+ { // Test #13
+ run: function () {
+ this.oldSelectedTab = gBrowser.selectedTab;
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+ let self = this;
+ loadURI("http://example.com/", function() {
+ self.notifyObj = new basicNotification();
+ // Set a timeout of 10 minutes that should never be hit
+ self.notifyObj.addOptions({
+ timeout: Date.now() + 600000
+ });
+ self.notification = showNotification(self.notifyObj);
+ });
+ },
+ onShown: function (popup) {
+ this.complete = false;
+
+ let self = this;
+ loadURI("http://example.org/", function() {
+ loadURI("http://example.com/", function() {
+
+ // Next load will hide the notification
+ self.notification.options.timeout = Date.now() - 1;
+ self.complete = true;
+
+ loadURI("http://example.org/");
+ });
+ });
+ },
+ onHidden: function (popup) {
+ ok(this.complete, "Should only have hidden the notification after the timeout was passed");
+ this.notification.remove();
+ gBrowser.removeTab(gBrowser.selectedTab);
+ gBrowser.selectedTab = this.oldSelectedTab;
+ }
+ },
+ // Test that setting persistWhileVisible allows a visible notification to
+ // persist across location changes
+ { // Test #14
+ run: function () {
+ this.oldSelectedTab = gBrowser.selectedTab;
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+ let self = this;
+ loadURI("http://example.com/", function() {
+ self.notifyObj = new basicNotification();
+ self.notifyObj.addOptions({
+ persistWhileVisible: true
+ });
+ self.notification = showNotification(self.notifyObj);
+ });
+ },
+ onShown: function (popup) {
+ this.complete = false;
+
+ let self = this;
+ loadURI("http://example.org/", function() {
+ loadURI("http://example.com/", function() {
+
+ // Notification should persist across location changes
+ self.complete = true;
+ dismissNotification(popup);
+ });
+ });
+ },
+ onHidden: function (popup) {
+ ok(this.complete, "Should only have hidden the notification after it was dismissed");
+ this.notification.remove();
+ gBrowser.removeTab(gBrowser.selectedTab);
+ gBrowser.selectedTab = this.oldSelectedTab;
+ }
+ },
+ // Test that nested icon nodes correctly activate popups
+ { // Test #15
+ run: function() {
+ // Add a temporary box as the anchor with a button
+ this.box = document.createElement("box");
+ PopupNotifications.iconBox.appendChild(this.box);
+
+ let button = document.createElement("button");
+ button.setAttribute("label", "Please click me!");
+ this.box.appendChild(button);
+
+ // The notification should open up on the box
+ this.notifyObj = new basicNotification();
+ this.notifyObj.anchorID = this.box.id = "nested-box";
+ this.notifyObj.addOptions({dismissed: true});
+ this.notification = showNotification(this.notifyObj);
+
+ EventUtils.synthesizeMouse(button, 1, 1, {});
+ },
+ onShown: function(popup) {
+ checkPopup(popup, this.notifyObj);
+ dismissNotification(popup);
+ },
+ onHidden: function(popup) {
+ this.notification.remove();
+ this.box.remove();
+ }
+ },
+ // Test that popupnotifications without popups have anchor icons shown
+ { // Test #16
+ run: function() {
+ let notifyObj = new basicNotification();
+ notifyObj.anchorID = "geo-notification-icon";
+ notifyObj.addOptions({neverShow: true});
+ showNotification(notifyObj);
+ },
+ updateNotShowing: function() {
+ isnot(document.getElementById("geo-notification-icon").boxObject.width, 0,
+ "geo anchor should be visible");
+ }
+ },
+ // Test notification "Not Now" menu item
+ { // Test #17
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ triggerSecondaryCommand(popup, 1);
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
+ this.notification.remove();
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // Test notification close button
+ { // Test #18
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ let notification = popup.childNodes[0];
+ EventUtils.synthesizeMouseAtCenter(notification.closebutton, {});
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
+ this.notification.remove();
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // Test notification when chrome is hidden
+ { // Test #19
+ run: function () {
+ window.locationbar.visible = false;
+ this.notifyObj = new basicNotification();
+ this.notification = showNotification(this.notifyObj);
+ window.locationbar.visible = true;
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ is(popup.anchorNode.className, "tabbrowser-tab", "notification anchored to tab");
+ dismissNotification(popup);
+ },
+ onHidden: function (popup) {
+ ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
+ this.notification.remove();
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // Test notification is removed when dismissed if removeOnDismissal is true
+ { // Test #20
+ run: function () {
+ this.notifyObj = new basicNotification();
+ this.notifyObj.addOptions({
+ removeOnDismissal: true
+ });
+ this.notification = showNotification(this.notifyObj);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj);
+ dismissNotification(popup);
+ },
+ onHidden: function (popup) {
+ ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
+ ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+ }
+ },
+ // Test multiple notification icons are shown
+ { // Test #21
+ run: function () {
+ this.notifyObj1 = new basicNotification();
+ this.notifyObj1.id += "_1";
+ this.notifyObj1.anchorID = "default-notification-icon";
+ this.notification1 = showNotification(this.notifyObj1);
+
+ this.notifyObj2 = new basicNotification();
+ this.notifyObj2.id += "_2";
+ this.notifyObj2.anchorID = "geo-notification-icon";
+ this.notification2 = showNotification(this.notifyObj2);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObj2);
+
+ // check notifyObj1 anchor icon is showing
+ isnot(document.getElementById("default-notification-icon").boxObject.width, 0,
+ "default anchor should be visible");
+ // check notifyObj2 anchor icon is showing
+ isnot(document.getElementById("geo-notification-icon").boxObject.width, 0,
+ "geo anchor should be visible");
+
+ dismissNotification(popup);
+ },
+ onHidden: [
+ function (popup) {
+ },
+ function (popup) {
+ this.notification1.remove();
+ ok(this.notifyObj1.removedCallbackTriggered, "removed callback triggered");
+
+ this.notification2.remove();
+ ok(this.notifyObj2.removedCallbackTriggered, "removed callback triggered");
+ }
+ ]
+ },
+ // Test that multiple notification icons are removed when switching tabs
+ { // Test #22
+ run: function () {
+ // show the notification on old tab.
+ this.notifyObjOld = new basicNotification();
+ this.notifyObjOld.anchorID = "default-notification-icon";
+ this.notificationOld = showNotification(this.notifyObjOld);
+
+ // switch tab
+ this.oldSelectedTab = gBrowser.selectedTab;
+ gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+ // show the notification on new tab.
+ this.notifyObjNew = new basicNotification();
+ this.notifyObjNew.anchorID = "geo-notification-icon";
+ this.notificationNew = showNotification(this.notifyObjNew);
+ },
+ onShown: function (popup) {
+ checkPopup(popup, this.notifyObjNew);
+
+ // check notifyObjOld anchor icon is removed
+ is(document.getElementById("default-notification-icon").boxObject.width, 0,
+ "default anchor shouldn't be visible");
+ // check notifyObjNew anchor icon is showing
+ isnot(document.getElementById("geo-notification-icon").boxObject.width, 0,
+ "geo anchor should be visible");
+
+ dismissNotification(popup);
+ },
+ onHidden: [
+ function (popup) {
+ },
+ function (popup) {
+ this.notificationNew.remove();
+ gBrowser.removeTab(gBrowser.selectedTab);
+
+ gBrowser.selectedTab = this.oldSelectedTab;
+ this.notificationOld.remove();
+ }
+ ]
+ }
+];
+
+function showNotification(notifyObj) {
+ return PopupNotifications.show(notifyObj.browser,
+ notifyObj.id,
+ notifyObj.message,
+ notifyObj.anchorID,
+ notifyObj.mainAction,
+ notifyObj.secondaryActions,
+ notifyObj.options);
+}
+
+function checkPopup(popup, notificationObj) {
+ info("[Test #" + gTestIndex + "] checking popup");
+
+ ok(notificationObj.shownCallbackTriggered, "shown callback was triggered");
+
+ let notifications = popup.childNodes;
+ is(notifications.length, 1, "only one notification displayed");
+ let notification = notifications[0];
+ let icon = document.getAnonymousElementByAttribute(notification, "class", "popup-notification-icon");
+ if (notificationObj.id == "geolocation") {
+ isnot(icon.boxObject.width, 0, "icon for geo displayed");
+ is(popup.anchorNode.className, "notification-anchor-icon", "notification anchored to icon");
+ }
+ is(notification.getAttribute("label"), notificationObj.message, "message matches");
+ is(notification.id, notificationObj.id + "-notification", "id matches");
+ if (notificationObj.mainAction) {
+ is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches");
+ is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches");
+ }
+ let actualSecondaryActions = notification.childNodes;
+ let secondaryActions = notificationObj.secondaryActions || [];
+ let actualSecondaryActionsCount = actualSecondaryActions.length;
+ if (secondaryActions.length) {
+ let lastChild = actualSecondaryActions.item(actualSecondaryActions.length - 1);
+ is(lastChild.tagName, "menuseparator", "menuseparator exists");
+ actualSecondaryActionsCount--;
+ }
+ is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions");
+ secondaryActions.forEach(function (a, i) {
+ is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches");
+ is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches");
+ });
+}
+
+function triggerMainCommand(popup) {
+ info("[Test #" + gTestIndex + "] triggering main command");
+ let notifications = popup.childNodes;
+ ok(notifications.length > 0, "at least one notification displayed");
+ let notification = notifications[0];
+ EventUtils.synthesizeMouseAtCenter(notification.button, {});
+}
+
+function triggerSecondaryCommand(popup, index) {
+ info("[Test #" + gTestIndex + "] triggering secondary command");
+ let notifications = popup.childNodes;
+ ok(notifications.length > 0, "at least one notification displayed");
+ let notification = notifications[0];
+ info("Triggering secondary command for notification " + notification.id);
+ notification.button.nextSibling.nextSibling.focus();
+
+ popup.addEventListener("popupshown", function triggerPopupShown() {
+ popup.removeEventListener("popupshown", triggerPopupShown);
+
+ // Press down until the desired command is selected
+ for (let i = 0; i <= index; i++)
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ // Activate
+ EventUtils.synthesizeKey("VK_RETURN", {});
+ });
+
+ // One down event to open the popup
+ EventUtils.synthesizeKey("VK_DOWN", { altKey: AppConstants.platform != "macosx" });
+}
+
+function loadURI(uri, callback) {
+ if (callback) {
+ gBrowser.addEventListener("load", function loadURIgBLoad() {
+ // Ignore the about:blank load
+ if (gBrowser.currentURI.spec != uri)
+ return;
+
+ gBrowser.removeEventListener("load", loadURIgBLoad, true);
+
+ callback();
+ }, true);
+ }
+ gBrowser.loadURI(uri);
+}
+
+function dismissNotification(popup) {
+ info("[Test #" + gTestIndex + "] dismissing notification");
+ executeSoon(function () {
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ });
+}
diff --git a/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js
new file mode 100644
index 0000000000..2fc5392dca
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler.js
@@ -0,0 +1,65 @@
+/* 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/. */
+
+// This test makes sure that the web pages can't register protocol handlers
+// inside the private browsing mode.
+
+function test() {
+ // initialization
+ waitForExplicitFinish();
+ let windowsToClose = [];
+ let notificationValue = "Protocol Registration: testprotocol";
+ let testURI = "http://example.com/browser/" +
+ "suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html";
+
+ function doTest(aIsPrivateMode, aWindow, aCallback) {
+ aWindow.getBrowser().selectedBrowser.addEventListener("load", function onLoad() {
+ aWindow.getBrowser().selectedBrowser.removeEventListener("load", onLoad, true);
+
+ setTimeout(function() {
+ let notificationBox = aWindow.getBrowser().getNotificationBox();
+ let notification = notificationBox.getNotificationWithValue(notificationValue);
+
+ if (aIsPrivateMode) {
+ // Make sure the notification is correctly displayed without a remember control
+ ok(!notification, "Notification box should not be displayed inside of private browsing mode");
+ } else {
+ // Make sure the notification is correctly displayed with a remember control
+ ok(notification, "Notification box should be displaying outside of private browsing mode");
+ }
+
+ aCallback();
+ }, 100); // remember control is added in a setTimeout(0) call
+ }, true);
+
+ aWindow.getBrowser().selectedBrowser.loadURI(testURI);
+ }
+
+ function testOnWindow(aOptions, aCallback) {
+ whenNewWindowLoaded(aOptions, function(aWin) {
+ windowsToClose.push(aWin);
+ // execute should only be called when need, like when you are opening
+ // web pages on the test. If calling executeSoon() is not necesary, then
+ // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+ executeSoon(() => aCallback(aWin));
+ });
+ };
+
+ // this function is called after calling finish() on the test.
+ registerCleanupFunction(function() {
+ windowsToClose.forEach(function(aWin) {
+ aWin.close();
+ });
+ });
+
+ // test first when not on private mode
+ testOnWindow({}, function(aWin) {
+ doTest(false, aWin, function() {
+ // then test when on private mode
+ testOnWindow({private: true}, function(aWin) {
+ doTest(true, aWin, finish);
+ });
+ });
+ });
+}
diff --git a/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html
new file mode 100644
index 0000000000..800476fd03
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_privatebrowsing_protocolhandler_page.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>Protocol registrar page</title>
+ </head>
+ <body>
+ <script>
+ navigator.registerProtocolHandler("testprotocol",
+ "https://example.com/foobar?uri=%s",
+ "Test Protocol");
+ </script>
+ </body>
+</html>
diff --git a/comm/suite/browser/test/browser/browser_relatedTabs.js b/comm/suite/browser/test/browser/browser_relatedTabs.js
new file mode 100644
index 0000000000..f93e212391
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_relatedTabs.js
@@ -0,0 +1,52 @@
+/* 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/. */
+
+function test() {
+ is(getBrowser().tabs.length, 1, "one tab is open initially");
+
+ // Add several new tabs in sequence, interrupted by selecting a
+ // different tab, moving a tab around and closing a tab,
+ // returning a list of opened tabs for verifying the expected order.
+ // The new tab behaviour is documented in bug 465673
+ let tabs = [];
+ function addTab(aURL, aReferrer) {
+ tabs.push(getBrowser().addTab(aURL, {referrerURI: aReferrer}));
+ }
+
+ addTab("http://mochi.test:8888/#0");
+ getBrowser().selectedTab = tabs[0];
+ addTab("http://mochi.test:8888/#1");
+ addTab("http://mochi.test:8888/#2", getBrowser().currentURI);
+ addTab("http://mochi.test:8888/#3", getBrowser().currentURI);
+ getBrowser().selectedTab = tabs[tabs.length - 1];
+ getBrowser().selectedTab = tabs[0];
+ addTab("http://mochi.test:8888/#4", getBrowser().currentURI);
+ getBrowser().selectedTab = tabs[3];
+ addTab("http://mochi.test:8888/#5", getBrowser().currentURI);
+ getBrowser().removeTab(tabs.pop());
+ addTab("about:blank", getBrowser().currentURI);
+ getBrowser().moveTabTo(getBrowser().selectedTab, 1);
+ addTab("http://mochi.test:8888/#6", getBrowser().currentURI);
+ addTab();
+ addTab("http://mochi.test:8888/#7");
+
+ function testPosition(tabNum, expectedPosition, msg) {
+ is(Array.from(getBrowser().tabs)
+ .indexOf(tabs[tabNum]),
+ expectedPosition,
+ msg);
+ }
+
+ testPosition(0, 3, "tab without referrer was opened to the far right");
+ testPosition(1, 7, "tab without referrer was opened to the far right");
+ testPosition(2, 5, "tab with referrer opened immediately to the right");
+ testPosition(3, 1, "next tab with referrer opened further to the right");
+ testPosition(4, 4, "tab selection changed, tab opens immediately to the right");
+ testPosition(5, 6, "blank tab with referrer opens to the right of 3rd original tab where removed tab was");
+ testPosition(6, 2, "tab has moved, new tab opens immediately to the right");
+ testPosition(7, 8, "blank tab without referrer opens at the end");
+ testPosition(8, 9, "tab without referrer opens at the end");
+
+ tabs.forEach(getBrowser().removeTab, getBrowser());
+}
diff --git a/comm/suite/browser/test/browser/browser_scope.js b/comm/suite/browser/test/browser/browser_scope.js
new file mode 100644
index 0000000000..e4edac1e04
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_scope.js
@@ -0,0 +1,4 @@
+function test() {
+ ok(!!gBrowser, "gBrowser exists");
+ is(gBrowser, getBrowser(), "both ways of getting tabbrowser work");
+}
diff --git a/comm/suite/browser/test/browser/browser_selectTabAtIndex.js b/comm/suite/browser/test/browser/browser_selectTabAtIndex.js
new file mode 100644
index 0000000000..9da6c8d489
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_selectTabAtIndex.js
@@ -0,0 +1,22 @@
+function test() {
+ for (let i = 0; i < 9; i++)
+ gBrowser.addTab();
+
+/* This part tests accel keys, which are not implemented in Seamonkey yet.
+ * Commenting out for now ...
+ * var isLinux = AppConstants.platform == "linux";
+ * for (let i = 9; i >= 1; i--) {
+ * EventUtils.synthesizeKey(i.toString(), { altKey: isLinux, accelKey: !isLinux });
+ *
+ * is(gBrowser.tabContainer.selectedIndex, (i == 9 ? gBrowser.tabs.length : i) - 1,
+ * (isLinux ? "Alt" : "Accel") + "+" + i + " selects expected tab");
+ * }
+ */
+
+ gBrowser.selectTabAtIndex(-3);
+ is(gBrowser.tabContainer.selectedIndex, gBrowser.tabs.length - 3,
+ "gBrowser.selectTabAtIndex(-3) selects expected tab");
+
+ for (let i = 0; i < 9; i++)
+ gBrowser.removeCurrentTab();
+}
diff --git a/comm/suite/browser/test/browser/browser_urlbarCopying.js b/comm/suite/browser/test/browser/browser_urlbarCopying.js
new file mode 100644
index 0000000000..cc51832996
--- /dev/null
+++ b/comm/suite/browser/test/browser/browser_urlbarCopying.js
@@ -0,0 +1,204 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const trimPref = "browser.urlbar.trimURLs";
+const phishyUserPassPref = "network.http.phishy-userpass-length";
+
+function test() {
+
+ let tab = gBrowser.selectedTab = gBrowser.addTab();
+
+ registerCleanupFunction(function () {
+ gBrowser.removeTab(tab);
+ Services.prefs.clearUserPref(trimPref);
+ Services.prefs.clearUserPref(phishyUserPassPref);
+ URLBarSetURI();
+ });
+
+ Services.prefs.setBoolPref(trimPref, true);
+ Services.prefs.setIntPref(phishyUserPassPref, 32); // avoid prompting about phishing
+
+ waitForExplicitFinish();
+
+ nextTest();
+}
+
+var tests = [
+ // pageproxystate="invalid"
+ {
+ setURL: "http://example.com/",
+ expectedURL: "example.com",
+ copyExpected: "example.com"
+ },
+ {
+ copyVal: "<e>xample.com",
+ copyExpected: "e"
+ },
+
+ // pageproxystate="valid" from this point on (due to the load)
+ {
+ loadURL: "http://example.com/",
+ expectedURL: "example.com",
+ copyExpected: "http://example.com/"
+ },
+ {
+ copyVal: "<example.co>m",
+ copyExpected: "example.co"
+ },
+ {
+ copyVal: "e<x>ample.com",
+ copyExpected: "x"
+ },
+ {
+ copyVal: "<e>xample.com",
+ copyExpected: "e"
+ },
+
+ {
+ loadURL: "http://example.com/foo",
+ expectedURL: "example.com/foo",
+ copyExpected: "http://example.com/foo"
+ },
+ {
+ copyVal: "<example.com>/foo",
+ copyExpected: "http://example.com"
+ },
+ {
+ copyVal: "<example>.com/foo",
+ copyExpected: "example"
+ },
+
+ // Test that userPass is stripped out
+ {
+ loadURL: "http://user:pass@mochi.test:8888/browser/browser/base/content/test/general/authenticate.sjs?user=user&pass=pass",
+ expectedURL: "mochi.test:8888/browser/browser/base/content/test/general/authenticate.sjs?user=user&pass=pass",
+ copyExpected: "http://mochi.test:8888/browser/browser/base/content/test/general/authenticate.sjs?user=user&pass=pass"
+ },
+
+ // Test escaping
+ {
+ loadURL: "http://example.com/()%28%29%C3%A9",
+ expectedURL: "example.com/()()\xe9",
+ copyExpected: "http://example.com/()%28%29%C3%A9"
+ },
+ {
+ copyVal: "<example.com/(>)()\xe9",
+ copyExpected: "http://example.com/("
+ },
+ {
+ copyVal: "e<xample.com/(>)()\xe9",
+ copyExpected: "xample.com/("
+ },
+
+ {
+ loadURL: "http://example.com/%C3%A9%C3%A9",
+ expectedURL: "example.com/\xe9\xe9",
+ copyExpected: "http://example.com/%C3%A9%C3%A9"
+ },
+ {
+ copyVal: "e<xample.com/\xe9>\xe9",
+ copyExpected: "xample.com/\xe9"
+ },
+ {
+ copyVal: "<example.com/\xe9>\xe9",
+ copyExpected: "http://example.com/\xe9"
+ },
+
+ {
+ loadURL: "http://example.com/?%C3%B7%C3%B7",
+ expectedURL: "example.com/?\xf7\xf7",
+ copyExpected: "http://example.com/?%C3%B7%C3%B7"
+ },
+ {
+ copyVal: "e<xample.com/?\xf7>\xf7",
+ copyExpected: "xample.com/?\xf7"
+ },
+ {
+ copyVal: "<example.com/?\xf7>\xf7",
+ copyExpected: "http://example.com/?\xf7"
+ },
+
+ // data: and javsacript: URIs shouldn't be encoded
+ {
+ loadURL: "javascript:('%C3%A9%20%25%50')",
+ expectedURL: "javascript:('%C3%A9 %25P')",
+ copyExpected: "javascript:('%C3%A9 %25P')"
+ },
+ {
+ copyVal: "<javascript:(>'%C3%A9 %25P')",
+ copyExpected: "javascript:("
+ },
+
+ {
+ loadURL: "data:text/html,(%C3%A9%20%25%50)",
+ expectedURL: "data:text/html,(%C3%A9 %25P)",
+ copyExpected: "data:text/html,(%C3%A9 %25P)",
+ },
+ {
+ copyVal: "<data:text/html,(>%C3%A9 %25P)",
+ copyExpected: "data:text/html,("
+ },
+ {
+ copyVal: "<data:text/html,(%C3%A9 %25P>)",
+ copyExpected: "data:text/html,(%C3%A9 %25P",
+ }
+];
+
+function nextTest() {
+ let test = tests.shift();
+ if (tests.length == 0)
+ runTest(test, finish);
+ else
+ runTest(test, nextTest);
+}
+
+function runTest(test, cb) {
+ function doCheck() {
+ if (test.setURL || test.loadURL) {
+ gURLBar.valueIsTyped = !!test.setURL;
+ is(gURLBar.textValue, test.expectedURL, "url bar value set");
+ }
+
+ testCopy(test.copyVal, test.copyExpected, cb);
+ }
+
+ if (test.loadURL) {
+ loadURL(test.loadURL, doCheck);
+ } else {
+ if (test.setURL)
+ gURLBar.value = test.setURL;
+ doCheck();
+ }
+}
+
+function testCopy(copyVal, targetValue, cb) {
+ info("Expecting copy of: " + targetValue);
+ waitForClipboard(targetValue, function () {
+ gURLBar.focus();
+ if (copyVal) {
+ let startBracket = copyVal.indexOf("<");
+ let endBracket = copyVal.indexOf(">");
+ if (startBracket == -1 || endBracket == -1 ||
+ startBracket > endBracket ||
+ copyVal.replace("<", "").replace(">", "") != gURLBar.textValue) {
+ ok(false, "invalid copyVal: " + copyVal);
+ }
+ gURLBar.selectionStart = startBracket;
+ gURLBar.selectionEnd = endBracket - 1;
+ } else {
+ gURLBar.select();
+ }
+
+ goDoCommand("cmd_copy");
+ }, cb, cb);
+}
+
+function loadURL(aURL, aCB) {
+ gBrowser.selectedBrowser.addEventListener("load", function () {
+ gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+ is(gBrowser.currentURI.spec, aURL, "loaded expected URL");
+ aCB();
+ }, true);
+
+ gBrowser.loadURI(aURL);
+}
diff --git a/comm/suite/browser/test/browser/feed_tab.html b/comm/suite/browser/test/browser/feed_tab.html
new file mode 100644
index 0000000000..50903f48b6
--- /dev/null
+++ b/comm/suite/browser/test/browser/feed_tab.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=458579
+-->
+ <head>
+ <title>Test for page info feeds tab</title>
+
+ <!-- Straight up standard -->
+ <link rel="alternate" type="application/atom+xml" title="1" href="/1.atom" />
+ <link rel="alternate" type="application/rss+xml" title="2" href="/2.rss" />
+ <link rel="feed" title="3" href="/3.xml" />
+
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/comm/suite/browser/test/browser/file_dom_notifications.html b/comm/suite/browser/test/browser/file_dom_notifications.html
new file mode 100644
index 0000000000..078c94a42d
--- /dev/null
+++ b/comm/suite/browser/test/browser/file_dom_notifications.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+<script>
+"use strict";
+
+function showNotification1() {
+ var options = {
+ dir: undefined,
+ lang: undefined,
+ body: "Test body",
+ tag: "Test tag",
+ icon: undefined,
+ };
+ var n = new Notification("Test title", options);
+ n.addEventListener("click", function(event) {
+ event.preventDefault();
+ dump("Should focus new window.");
+ newWindow.focus();
+ });
+ return n;
+}
+
+function showNotification2() {
+ var options = {
+ dir: undefined,
+ lang: undefined,
+ body: "Test body",
+ tag: "Test tag",
+ icon: undefined,
+ };
+ return new Notification("Test title", options);
+}
+</script>
+</head>
+<body>
+<form id="notificationForm" onsubmit="showNotification();">
+ <input type="submit" value="Show notification" id="submit"/>
+</form>
+</body>
+</html>
diff --git a/comm/suite/browser/test/browser/head.js b/comm/suite/browser/test/browser/head.js
new file mode 100644
index 0000000000..9a065930a2
--- /dev/null
+++ b/comm/suite/browser/test/browser/head.js
@@ -0,0 +1,63 @@
+/* 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/. */
+
+function findChromeWindowByURI(aURI) {
+ let windows = Services.wm.getEnumerator(null);
+ while (windows.hasMoreElements()) {
+ let win = windows.getNext();
+ if (win.location.href == aURI)
+ return win;
+ }
+ return null;
+}
+
+function waitForCondition(condition, nextTest, errorMsg) {
+ var tries = 0;
+ var interval = setInterval(function() {
+ if (tries >= 30) {
+ ok(false, errorMsg);
+ moveOn();
+ }
+ if (condition()) {
+ moveOn();
+ }
+ tries++;
+ }, 100);
+ var moveOn = function() { clearInterval(interval); nextTest(); };
+}
+
+function whenNewWindowLoaded(aOptions, aCallback) {
+ var { private } = aOptions;
+ var features = private ? "private,chrome,all,dialog=no" :
+ "non-private,chrome,all,dialog=no";
+ var win = window.openDialog(getBrowserURL(), "_blank", features,
+ "about:blank");
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad);
+ aCallback(win);
+ });
+}
+
+function updateBlocklist(aCallback) {
+ var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsITimerCallback);
+ var observer = function() {
+ Services.obs.removeObserver(observer, "blocklist-updated");
+ SimpleTest.executeSoon(aCallback);
+ };
+ Services.obs.addObserver(observer, "blocklist-updated");
+ blocklistNotifier.notify(null);
+}
+
+var _originalTestBlocklistURL = null;
+function setAndUpdateBlocklist(aURL, aCallback) {
+ if (!_originalTestBlocklistURL)
+ _originalTestBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
+ Services.prefs.setCharPref("extensions.blocklist.url", aURL);
+ updateBlocklist(aCallback);
+}
+
+function resetBlocklist() {
+ Services.prefs.setCharPref("extensions.blocklist.url", _originalTestBlocklistURL);
+}
diff --git a/comm/suite/browser/test/browser/page_style_sample.html b/comm/suite/browser/test/browser/page_style_sample.html
new file mode 100644
index 0000000000..633956c4c7
--- /dev/null
+++ b/comm/suite/browser/test/browser/page_style_sample.html
@@ -0,0 +1,31 @@
+<html>
+ <head>
+ <title>Test for page style menu</title>
+ <!-- data-state values:
+ 0: should not appear in the page style menu
+ 0-todo: should not appear in the page style menu, but does
+ 1: should appear in the page style menu
+ 2: should appear in the page style menu as the selected stylesheet -->
+ <link data-state="1" href="404.css" title="1" rel="alternate stylesheet">
+ <link data-state="0" title="2" rel="alternate stylesheet">
+ <link data-state="0" href="404.css" rel="alternate stylesheet">
+ <link data-state="0" href="404.css" title="" rel="alternate stylesheet">
+ <link data-state="1" href="404.css" title="3" rel="stylesheet alternate">
+ <link data-state="1" href="404.css" title="4" rel=" alternate stylesheet ">
+ <link data-state="1" href="404.css" title="5" rel="alternate stylesheet">
+ <link data-state="2" href="404.css" title="6" rel="stylesheet">
+ <link data-state="1" href="404.css" title="7" rel="foo stylesheet">
+ <link data-state="0" href="404.css" title="8" rel="alternate">
+ <link data-state="1" href="404.css" title="9" rel="alternate STYLEsheet">
+ <link data-state="1" href="404.css" title="10" rel="alternate stylesheet" media="">
+ <link data-state="1" href="404.css" title="11" rel="alternate stylesheet" media="all">
+ <link data-state="1" href="404.css" title="12" rel="alternate stylesheet" media="ALL ">
+ <link data-state="1" href="404.css" title="13" rel="alternate stylesheet" media="screen">
+ <link data-state="1" href="404.css" title="14" rel="alternate stylesheet" media=" Screen">
+ <link data-state="1" href="404.css" title="15" rel="alternate stylesheet" media="screen foo">
+ <link data-state="1" href="404.css" title="16" rel="alternate stylesheet" media="all screen">
+ <link data-state="0-todo" href="404.css" title="17" rel="alternate stylesheet" media="allscreen">
+ <link data-state="0-todo" href="404.css" title="18" rel="alternate stylesheet" media="_all">
+ </head>
+ <body></body>
+</html>
diff --git a/comm/suite/browser/test/browser/redirect_bug623155.sjs b/comm/suite/browser/test/browser/redirect_bug623155.sjs
new file mode 100644
index 0000000000..2e4681f240
--- /dev/null
+++ b/comm/suite/browser/test/browser/redirect_bug623155.sjs
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const REDIRECT_TO = "https://www.bank1.com/"; // Bad-cert host.
+
+function handleRequest(aRequest, aResponse) {
+ // Set HTTP Status
+ aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently");
+
+ // Set redirect URI, mirroring the hash value.
+ let hash = (/\#.+/.test(aRequest.pathQueryRef))?
+ "#" + aRequest.pathQueryRef.split("#")[1]:
+ "";
+ aResponse.setHeader("Location", REDIRECT_TO + hash);
+}
diff --git a/comm/suite/browser/test/browser/title_test.svg b/comm/suite/browser/test/browser/title_test.svg
new file mode 100644
index 0000000000..6ab5b2f5c2
--- /dev/null
+++ b/comm/suite/browser/test/browser/title_test.svg
@@ -0,0 +1,59 @@
+<svg width="640px" height="480px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0">
+ <title>This is a root SVG element's title</title>
+ <foreignObject>
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <body>
+ <svg xmlns="http://www.w3.org/2000/svg" id="svg1">
+ <title>This is a non-root SVG element title</title>
+ </svg>
+ </body>
+ </html>
+ </foreignObject>
+ <text id="text1" x="10px" y="32px" font-size="24px">
+ This contains only &lt;title&gt;
+ <title>
+
+
+ This is a title
+
+ </title>
+ </text>
+ <text id="text2" x="10px" y="96px" font-size="24px">
+ This contains only &lt;desc&gt;
+ <desc>This is a desc</desc>
+ </text>
+ <text id="text3" x="10px" y="128px" font-size="24px">
+ This contains nothing.
+ </text>
+ <a id="link1" xlink:href="#">
+ This link contains &lt;title&gt;
+ <title>
+ This is a title
+ </title>
+ <text id="text4" x="10px" y="192px" font-size="24px">
+ </text>
+ </a>
+ <a id="link2" xlink:href="#">
+ <text x="10px" y="192px" font-size="24px">
+ This text contains &lt;title&gt;
+ <title>
+ This is a title
+ </title>
+ </text>
+ </a>
+ <a id="link3" xlink:href="#" xlink:title="This is an xlink:title attribute">
+ <text x="10px" y="224px" font-size="24px">
+ This link contains &lt;title&gt; &amp; xlink:title attr.
+ <title>This is a title</title>
+ </text>
+ </a>
+ <a id="link4" xlink:href="#" xlink:title="This is an xlink:title attribute">
+ <text x="10px" y="256px" font-size="24px">
+ This link contains xlink:title attr.
+ </text>
+ </a>
+ <text id="text5" x="10px" y="160px" font-size="24px"
+ xlink:title="This is an xlink:title attribute but it isn't on a link" >
+ This contains nothing.
+ </text>
+</svg>
diff --git a/comm/suite/browser/test/chrome/chrome.ini b/comm/suite/browser/test/chrome/chrome.ini
new file mode 100644
index 0000000000..05c31c161a
--- /dev/null
+++ b/comm/suite/browser/test/chrome/chrome.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_maxSniffing.html]
diff --git a/comm/suite/browser/test/chrome/test_maxSniffing.html b/comm/suite/browser/test/chrome/test_maxSniffing.html
new file mode 100644
index 0000000000..8fb333e523
--- /dev/null
+++ b/comm/suite/browser/test/chrome/test_maxSniffing.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=739040
+-->
+<head>
+ <title>Test that we only sniff 512 bytes</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=739040">Mozilla Bug 739040</a>
+<p id="display">
+ <iframe id="validTestFrame" src="http://mochi.test:8888/tests/suite/browser/test/mochitest/valid-feed.xml"></iframe>
+ <iframe id="unsniffableTestFrame" src="http://mochi.test:8888/tests/suite/browser/test/mochitest/valid-unsniffable-feed.xml"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody">
+
+/** Test for Bug 739040 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ is($("validTestFrame").contentDocument.documentElement.id, "feedHandler",
+ "valid feed should be sniffed");
+ isnot($("unsniffableTestFrame").contentDocument.documentElement.id, "feedHandler",
+ "unsniffable feed should not be sniffed");
+});
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/audio.ogg b/comm/suite/browser/test/mochitest/audio.ogg
new file mode 100644
index 0000000000..7e6ef77ec4
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/audio.ogg
Binary files differ
diff --git a/comm/suite/browser/test/mochitest/bug364677-data.xml b/comm/suite/browser/test/mochitest/bug364677-data.xml
new file mode 100644
index 0000000000..b48915c050
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/bug364677-data.xml
@@ -0,0 +1,5 @@
+<rss version="2.0">
+ <channel>
+ <title>t</title>
+ </channel>
+</rss>
diff --git a/comm/suite/browser/test/mochitest/bug364677-data.xml^headers^ b/comm/suite/browser/test/mochitest/bug364677-data.xml^headers^
new file mode 100644
index 0000000000..f203c6368e
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/bug364677-data.xml^headers^
@@ -0,0 +1 @@
+Content-Type: text/xml
diff --git a/comm/suite/browser/test/mochitest/bug395533-data.txt b/comm/suite/browser/test/mochitest/bug395533-data.txt
new file mode 100644
index 0000000000..e0ed39850f
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/bug395533-data.txt
@@ -0,0 +1,6 @@
+<rss version="2.0">
+ <channel>
+ <link>http://example.org/</link>
+ <title>t</title>
+ </channel>
+</rss>
diff --git a/comm/suite/browser/test/mochitest/bug436801-data.xml b/comm/suite/browser/test/mochitest/bug436801-data.xml
new file mode 100644
index 0000000000..0e45c7ed8e
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/bug436801-data.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://www.example.com/">
+
+ <title type="xhtml" xml:base="/foo/bar/">
+ <div xmlns="http://www.w3.org/1999/xhtml">Example of a <em>special</em> feed (<img height="20px" src="baz.png" alt="base test sprite"/>)</div>
+ </title>
+
+ <subtitle type="html" xml:base="/foo/bar/">
+ <![CDATA[
+ With a <em>special</em> subtitle (<img height="20px" src="baz.png" alt="base test sprite"/>)
+ ]]>
+ </subtitle>
+
+ <link href="http://example.org/"/>
+
+ <updated>2010-09-02T18:30:02Z</updated>
+
+ <author>
+ <name>John Doe</name>
+ </author>
+
+ <id>urn:uuid:22906062-ecbd-46e2-b6a7-3039506a398f</id>
+
+ <entry>
+ <title type="xhtml" xml:base="/foo/bar/">
+ <div xmlns="http://www.w3.org/1999/xhtml">Some <abbr title="Extensible Hyper-text Mark-up Language">XHTML</abbr> examples (<img height="20px" src="baz.png" alt="base test sprite"/>)</div>
+ </title>
+ <id>urn:uuid:b48083a7-71a7-4c9c-8515-b7c0d22955e7</id>
+ <updated>2010-09-02T18:30:02Z</updated>
+ <summary>Some text.</summary>
+ </entry>
+
+ <entry>
+ <title type="html" xml:base="/foo/bar/">
+ <![CDATA[
+ Some <abbr title="Hyper-text Mark-up Language">HTML</abbr> examples (<img height="20px" src="baz.png" alt="base test sprite"/>)
+ ]]>
+ </title>
+ <id>urn:uuid:1424967a-280a-414d-b0ab-8b11c4ac1bb7</id>
+ <updated>2010-09-02T18:30:02Z</updated>
+ <summary>Some text.</summary>
+ </entry>
+
+</feed>
diff --git a/comm/suite/browser/test/mochitest/ctxmenu-image.png b/comm/suite/browser/test/mochitest/ctxmenu-image.png
new file mode 100644
index 0000000000..4c3be50847
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/ctxmenu-image.png
Binary files differ
diff --git a/comm/suite/browser/test/mochitest/feed_discovery.html b/comm/suite/browser/test/mochitest/feed_discovery.html
new file mode 100644
index 0000000000..80c35d19ab
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/feed_discovery.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=377611
+-->
+ <head>
+ <title>Test for feed discovery</title>
+
+ <!-- Straight up standard -->
+ <link rel="alternate" type="application/atom+xml" title="1" href="/1.atom" />
+ <link rel="alternate" type="application/rss+xml" title="2" href="/2.rss" />
+ <link rel="feed" title="3" href="/3.xml" />
+
+ <!-- rel is a space-separated list -->
+ <link rel=" alternate " type="application/atom+xml" title="4" href="/4.atom" />
+ <link rel="foo alternate" type="application/atom+xml" title="5" href="/5.atom" />
+ <link rel="alternate foo" type="application/atom+xml" title="6" href="/6.atom" />
+ <link rel="foo alternate foo" type="application/atom+xml" title="7" href="/7.atom" />
+ <link rel="meat feed cake" title="8" href="/8.atom" />
+
+ <!-- rel is case-insensitive -->
+ <link rel="ALTERNate" type="application/atom+xml" title="9" href="/9.atom" />
+ <link rel="fEEd" title="10" href="/10.atom" />
+
+ <!-- type can have leading and trailing whitespace -->
+ <link rel="alternate" type=" application/atom+xml " title="11" href="/11.atom" />
+
+ <!-- type is case-insensitive -->
+ <link rel="alternate" type="aPPliCAtion/ATom+xML" title="12" href="/12.atom" />
+
+ <!-- "feed stylesheet" is a feed, though "alternate stylesheet" isn't -->
+ <link rel="feed stylesheet" title="13" href="/13.atom" />
+
+ <!-- hyphens or letters around rel not allowed -->
+ <link rel="disabled-alternate" type="application/atom+xml" title="Bogus1" href="/Bogus1" />
+ <link rel="alternates" type="application/atom+xml" title="Bogus2" href="/Bogus2" />
+ <link rel=" alternate-like" type="application/atom+xml" title="Bogus3" href="/Bogus3" />
+
+ <!-- don't tolerate text/xml if title includes 'rss' not as a word -->
+ <link rel="alternate" type="text/xml" title="Bogus4 scissorsshaped" href="/Bogus4" />
+
+ <!-- don't tolerate application/xml if title includes 'rss' not as a word -->
+ <link rel="alternate" type="application/xml" title="Bogus5 scissorsshaped" href="/Bogus5" />
+
+ <!-- don't tolerate application/rdf+xml if title includes 'rss' not as a word -->
+ <link rel="alternate" type="application/rdf+xml" title="Bogus6 scissorsshaped" href="/Bogus6" />
+
+ <!-- don't tolerate random types -->
+ <link rel="alternate" type="text/plain" title="Bogus7 rss" href="/Bogus7" />
+
+ <!-- don't find Atom by title -->
+ <link rel="foopy" type="application/atom+xml" title="Bogus8 Atom and RSS" href="/Bogus8" />
+
+ <!-- don't find application/rss+xml by title -->
+ <link rel="goats" type="application/rss+xml" title="Bogus9 RSS and Atom" href="/Bogus9" />
+
+ <!-- don't find application/rdf+xml by title -->
+ <link rel="alternate" type="application/rdf+xml" title="Bogus10 RSS and Atom" href="/Bogus10" />
+
+ <!-- don't find application/xml by title -->
+ <link rel="alternate" type="application/xml" title="Bogus11 RSS and Atom" href="/Bogus11" />
+
+ <!-- don't find text/xml by title -->
+ <link rel="alternate" type="text/xml" title="Bogus12 RSS and Atom" href="/Bogus12" />
+
+ <!-- alternate and stylesheet isn't a feed -->
+ <link rel="alternate stylesheet" type="application/rss+xml" title="Bogus13 RSS" href="/Bogus13" />
+ </head>
+ <body>
+ <script>
+ window.onload = function() {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+ var tests = new Array();
+
+ var currentWindow =
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ var browserSH = currentWindow.XULBrowserWindow;
+
+ var discovered = browserSH.feeds;
+ tests.push({ check: discovered.length > 0,
+ message: "some feeds should be discovered" });
+
+ var feeds = [];
+
+ for (var aFeed of discovered) {
+ feeds[aFeed.href] = true;
+ }
+
+ for (var aLink of document.getElementsByTagName("link")) {
+ // ignore real stylesheets, and anything without an href property
+ if (aLink.type != "text/css" && aLink.href) {
+ if (/bogus/i.test(aLink.title)) {
+ tests.push({ check: !feeds[aLink.href], todo: /todo/i.test(aLink.title),
+ message: "don't discover " + aLink.href });
+ } else {
+ tests.push({ check: feeds[aLink.href], todo: /todo/i.test(aLink.title),
+ message: "should discover " + aLink.href });
+ }
+ }
+ }
+ window.arguments[0].tests = tests;
+ window.close();
+ }
+ </script>
+ </body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/mochitest.ini b/comm/suite/browser/test/mochitest/mochitest.ini
new file mode 100644
index 0000000000..99b25bba9d
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/mochitest.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+support-files =
+ valid-feed.xml
+ valid-unsniffable-feed.xml
+
+[test_bug364677.html]
+support-files = bug364677-data.xml bug364677-data.xml^headers^
+[test_bug395533.html]
+support-files = bug395533-data.txt
+[test_bug436801.html]
+support-files = bug436801-data.xml
+[test_contextmenu.html]
+support-files = audio.ogg ctxmenu-image.png subtst_contextmenu.html video.ogg
+skip-if = os != "win" # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304
+[test_feed_discovery.html]
+support-files = feed_discovery.html
+[test_registerHandler.html]
diff --git a/comm/suite/browser/test/mochitest/subtst_contextmenu.html b/comm/suite/browser/test/mochitest/subtst_contextmenu.html
new file mode 100644
index 0000000000..63cc70c84e
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/subtst_contextmenu.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Subtest for browser context menu</title>
+</head>
+<body>
+Browser context menu subtest.
+
+<div id="test-text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</div>
+<a id="test-link" href="http://mozilla.com">Click the monkey!</a>
+<a id="test-mailto" href="mailto:codemonkey@mozilla.com">Mail the monkey!</a><br>
+<input id="test-input"><br>
+<input id="test-input-select"><br>
+<img id="test-image" src="ctxmenu-image.png">
+<canvas id="test-canvas" width="100" height="100" style="background-color: blue"></canvas>
+<video controls id="test-video-ok" src="video.ogg" width="100" height="100" style="background-color: green"></video>
+<video id="test-audio-in-video" src="audio.ogg" width="100" height="100" style="background-color: red"></video>
+<video controls id="test-video-bad" src="bogus.duh" width="100" height="100" style="background-color: orange"></video>
+<video controls id="test-video-bad2" width="100" height="100" style="background-color: yellow">
+ <source src="bogus.duh" type="video/durrrr;">
+</video>
+<iframe id="test-iframe" width="98" height="98" style="border: 1px solid black"></iframe>
+<iframe id="test-video-in-iframe" src="video.ogg" width="98" height="98" style="border: 1px solid black"></iframe>
+<iframe id="test-image-in-iframe" src="ctxmenu-image.png" width="98" height="98" style="border: 1px solid black"></iframe>
+<textarea id="test-textarea">chssseesbbbie</textarea> <!-- a weird word which generates only one suggestion -->
+<textarea id="test-textarea-sel">test</textarea>
+<div id="test-contenteditable" contenteditable="true">chssseefsbbbie</div> <!-- a more weird word which generates no suggestions -->
+<input id="test-input-spellcheck" type="text" spellcheck="true" autofocus value="prodkjfgigrty"> <!-- this one also generates one suggestion -->
+<div contextmenu="myMenu">
+ <p id="test-pagemenu" hopeless="true">I've got a context menu!</p>
+ <menu id="myMenu" type="context">
+ <menuitem label="Plain item" onclick="document.getElementById('test-pagemenu').removeAttribute('hopeless');"></menuitem>
+ <menuitem label="Disabled item" disabled></menuitem>
+ <menuitem> Item w/ textContent</menuitem>
+ <menu>
+ <menuitem type="checkbox" label="Checkbox" checked></menuitem>
+ </menu>
+ <menu>
+ <menuitem type="radio" label="Radio1" checked></menuitem>
+ <menuitem type="radio" label="Radio2"></menuitem>
+ <menuitem type="radio" label="Radio3"></menuitem>
+ </menu>
+ <menu>
+ <menuitem label="Item w/ icon" icon="favicon.ico"></menuitem>
+ <menuitem label="Item w/ bad icon" icon="data://www.mozilla.org/favicon.ico"></menuitem>
+ </menu>
+ <menu label="Submenu">
+ <menuitem type="radio" label="Radio1" radiogroup="rg"></menuitem>
+ <menuitem type="radio" label="Radio2" checked radiogroup="rg"></menuitem>
+ <menuitem type="radio" label="Radio3" radiogroup="rg"></menuitem>
+ <menu>
+ <menuitem type="checkbox" label="Checkbox"></menuitem>
+ </menu>
+ </menu>
+ <menu hidden>
+ <menuitem label="Bogus item"></menuitem>
+ </menu>
+ <menu>
+ </menu>
+ <menuitem label="Hidden item" hidden></menuitem>
+ <menuitem></menuitem>
+ </menu>
+</div>
+
+<!-- SeaMonkey specific elements -->
+<a href="http://mozilla.com"><img id="test-image-link" alt="Click the monkey!" src="ctxmenu-image.png"></a>
+<a href="mailto:codemonkey@mozilla.com"><img id="test-image-mailto" alt="Mail the monkey!" src="ctxmenu-image.png"></a><br>
+
+</body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/test_bug364677.html b/comm/suite/browser/test/mochitest/test_bug364677.html
new file mode 100644
index 0000000000..e08b1403f6
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/test_bug364677.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=364677
+-->
+<head>
+ <title>Test for Bug 364677</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=364677">Mozilla Bug 364677</a>
+<p id="display"><iframe id="testFrame" src="bug364677-data.xml"></iframe></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody">
+
+/** Test for Bug 364677 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ is(SpecialPowers.wrap($("testFrame")).contentDocument.documentElement.id, "feedHandler",
+ "Feed served as text/xml without a channel/link should have been sniffed");
+});
+addLoadEvent(SimpleTest.finish);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/comm/suite/browser/test/mochitest/test_bug395533.html b/comm/suite/browser/test/mochitest/test_bug395533.html
new file mode 100644
index 0000000000..5d3cfa0121
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/test_bug395533.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=395533
+-->
+<head>
+ <title>Test for Bug 395533</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=395533">Mozilla Bug 395533</a>
+<p id="display"><iframe id="testFrame" src="bug395533-data.txt"></iframe></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody">
+
+/** Test for Bug 395533 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ // Need privs because the feed seems to have an about:feeds principal or some
+ // such. It's not same-origin with us in any case.
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ is($("testFrame").contentDocument.documentElement.id, "",
+ "Text got sniffed as a feed?");
+});
+addLoadEvent(SimpleTest.finish);
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/comm/suite/browser/test/mochitest/test_bug436801.html b/comm/suite/browser/test/mochitest/test_bug436801.html
new file mode 100644
index 0000000000..6ca1b83cf5
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/test_bug436801.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=436801
+-->
+<head>
+ <title>Test feed preview subscribe UI</title>
+ <script src="/MochiKit/packed.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436801">Mozilla Bug 436801</a>
+<p id="display"><iframe id="testFrame" src="bug436801-data.xml"></iframe></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody">
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function () {
+ var doc = SpecialPowers.wrap($("testFrame")).contentDocument;
+
+ checkNode(doc.getElementById("feedTitleText"), [
+ "ELEMENT", "h1", { "xml:base": "http://www.example.com/foo/bar/" }, [
+ ["TEXT", "Example of a "],
+ ["ELEMENT", "em", [
+ ["TEXT", "special"],
+ ]],
+ ["TEXT", " feed ("],
+ ["ELEMENT", "img", { "src": "baz.png" }],
+ ["TEXT", ")"],
+ ]
+ ]);
+
+ checkNode(doc.getElementById("feedSubtitleText"), [
+ "ELEMENT", "h2", { "xml:base": "http://www.example.com/foo/bar/" }, [
+ ["TEXT", "With a "],
+ ["ELEMENT", "em", [
+ ["TEXT", "special"],
+ ]],
+ ["TEXT", " subtitle ("],
+ ["ELEMENT", "img", { "src": "baz.png" }],
+ ["TEXT", ")"],
+ ]
+ ]);
+
+ checkNode(doc.querySelector(".entry").firstChild.firstChild.firstChild, [
+ "ELEMENT", "span", { "xml:base": "http://www.example.com/foo/bar/" }, [
+ ["TEXT", "Some "],
+ ["ELEMENT", "abbr", { title: "Extensible Hyper-text Mark-up Language" }, [
+ ["TEXT", "XHTML"],
+ ]],
+ ["TEXT", " examples ("],
+ ["ELEMENT", "img", { "src": "baz.png" }],
+ ["TEXT", ")"],
+ ]
+ ]);
+
+ checkNode(doc.querySelectorAll(".entry")[1].firstChild.firstChild.firstChild, [
+ "ELEMENT", "span", { "xml:base": "http://www.example.com/foo/bar/" }, [
+ ["TEXT", "Some "],
+ ["ELEMENT", "abbr", { title: "Hyper-text Mark-up Language" }, [
+ ["TEXT", "HTML"],
+ ]],
+ ["TEXT", " examples ("],
+ ["ELEMENT", "img", { "src": "baz.png" }],
+ ["TEXT", ")"],
+ ]
+ ]);
+});
+
+addLoadEvent(SimpleTest.finish);
+
+function checkNode(node, schema) {
+ var typeName = schema.shift() + "_NODE";
+ var type = Node[typeName];
+ is(node.nodeType, type, "Node should be expected type " + typeName);
+ if (type == Node.TEXT_NODE) {
+ var text = schema.shift();
+ is(node.data, text, "Text should match");
+ return;
+ }
+ // type == Node.ELEMENT_NODE
+ var tag = schema.shift();
+ is(node.localName, tag, "Element should have expected tag");
+ while (schema.length) {
+ var val = schema.shift();
+ if (Array.isArray(val))
+ var childSchema = val;
+ else
+ var attrSchema = val;
+ }
+ if (attrSchema) {
+ var nsTable = {
+ xml: "http://www.w3.org/XML/1998/namespace",
+ };
+ for (var name in attrSchema) {
+ var [ns, nsName] = name.split(":");
+ var val = nsName ? node.getAttributeNS(nsTable[ns], nsName) :
+ node.getAttribute(name);
+ is(val, attrSchema[name], "Attribute " + name + " should match");
+ }
+ }
+ if (childSchema) {
+ var numChildren = node.childNodes.length;
+ is(childSchema.length, numChildren,
+ "Element should have expected number of children");
+ for (var i = 0; i < numChildren; i++)
+ checkNode(node.childNodes[i], childSchema[i]);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/test_contextmenu.html b/comm/suite/browser/test/mochitest/test_contextmenu.html
new file mode 100644
index 0000000000..0dea49c925
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/test_contextmenu.html
@@ -0,0 +1,931 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Tests for browser context menu</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Browser context menu tests.
+<p id="display"></p>
+
+<div id="content">
+</div>
+
+<pre id="test">
+<script class="testbody">
+
+/** Test for Login Manager: multiple login autocomplete. **/
+
+SpecialPowers.ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", window);
+SpecialPowers.ChromeUtils.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", window);
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+function openContextMenuFor(element, shiftkey, waitForSpellCheck) {
+ // Context menu should be closed before we open it again.
+ is(SpecialPowers.wrap(contextMenu).state, "closed", "checking if popup is closed");
+
+ if (lastElement)
+ lastElement.blur();
+ element.focus();
+
+ // Some elements need time to focus and spellcheck before any tests are
+ // run on them.
+ function actuallyOpenContextMenuFor() {
+ lastElement = element;
+ var eventDetails = { type : "contextmenu", button : 2, shiftKey : shiftkey };
+ synthesizeMouse(element, 2, 2, eventDetails, element.ownerDocument.defaultView);
+ }
+
+ if (waitForSpellCheck)
+ onSpellCheck(element, actuallyOpenContextMenuFor);
+ else
+ actuallyOpenContextMenuFor();
+}
+
+function closeContextMenu() {
+ contextMenu.hidePopup();
+}
+
+function executeCopyCommand(command, expectedValue)
+{
+ // Just execute the command directly rather than simulating a context menu
+ // press to avoid having to deal with its asynchronous nature
+ SpecialPowers.wrap(subwindow).controllers.getControllerForCommand(command).doCommand(command);
+
+ // The easiest way to check the clipboard is to paste the contents into a
+ // textbox
+ input.focus();
+ input.value = "";
+ SpecialPowers.wrap(input).controllers.getControllerForCommand("cmd_paste").doCommand("cmd_paste");
+ is(input.value, expectedValue, "paste for command " + command);
+}
+
+function invokeItemAction(generatedItemId)
+{
+ var item = contextMenu.getElementsByAttribute("generateditemid",
+ generatedItemId)[0];
+ ok(item, "Got generated XUL menu item");
+ item.doCommand();
+ is(pagemenu.hasAttribute("hopeless"), false, "attribute got removed");
+}
+
+function getVisibleMenuItems(aMenu, aData) {
+ var items = [];
+ var accessKeys = {};
+ for (var i = 0; i < aMenu.childNodes.length; i++) {
+ var item = aMenu.childNodes[i];
+ if (item.hidden)
+ continue;
+
+ var key = item.accessKey;
+ if (key)
+ key = key.toLowerCase();
+
+ var isGenerated = item.hasAttribute("generateditemid");
+
+ if (item.nodeName == "menuitem") {
+ var isSpellSuggestion = item.className == "spell-suggestion";
+ if (isSpellSuggestion) {
+ is(item.id, "", "child menuitem #" + i + " is a spelling suggestion");
+ } else if (isGenerated) {
+ is(item.id, "", "child menuitem #" + i + " is a generated item");
+ } else {
+ ok(item.id, "child menuitem #" + i + " has an ID");
+ }
+ var label = item.getAttribute("label");
+ ok(label.length, "menuitem " + item.id + " has a label");
+ if (isSpellSuggestion) {
+ is(key, "", "Spell suggestions shouldn't have an access key");
+ items.push("*" + label);
+ } else if (isGenerated) {
+ items.push("+" + label);
+ } else if (item.id.indexOf("spell-check-dictionary-") != 0 &&
+ item.id != "spell-no-suggestions") {
+ ok(key, "menuitem " + item.id + " has an access key");
+ if (accessKeys[key])
+ ok(false, "menuitem " + item.id + " has same accesskey as " + accessKeys[key]);
+ else
+ accessKeys[key] = item.id;
+ }
+ if (!isSpellSuggestion && !isGenerated) {
+ items.push(item.id);
+ }
+ if (isGenerated) {
+ var p = {};
+ p.type = item.getAttribute("type");
+ p.icon = item.getAttribute("image");
+ p.checked = item.hasAttribute("checked");
+ p.disabled = item.hasAttribute("disabled");
+ items.push(p);
+ } else {
+ items.push(!item.disabled);
+ }
+ } else if (item.nodeName == "menuseparator") {
+ ok(true, "--- seperator id is " + item.id);
+ items.push("---");
+ items.push(null);
+ } else if (item.nodeName == "menu") {
+ if (isGenerated) {
+ item.id = "generated-submenu-" + aData.generatedSubmenuId++;
+ }
+ ok(item.id, "child menu #" + i + " has an ID");
+ if (!isGenerated) {
+ ok(key, "menu has an access key");
+ if (accessKeys[key])
+ ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]);
+ else
+ accessKeys[key] = item.id;
+ }
+ items.push(item.id);
+ items.push(!item.disabled);
+ // Add a dummy item to that the indexes in checkMenu are the same
+ // for expectedItems and actualItems.
+ items.push([]);
+ items.push(null);
+ } else {
+ ok(false, "child #" + i + " of menu ID " + aMenu.id +
+ " has an unknown type (" + item.nodeName + ")");
+ }
+ }
+ return items;
+}
+
+function checkContextMenu(expectedItems) {
+ is(contextMenu.state, "open", "checking if popup is open");
+ var data = { generatedSubmenuId: 1 };
+ checkMenu(contextMenu, expectedItems, data);
+}
+
+/*
+ * checkMenu - checks to see if the specified <menupopup> contains the
+ * expected items and state.
+ * expectedItems is a array of (1) item IDs and (2) a boolean specifying if
+ * the item is enabled or not (or null to ignore it). Submenus can be checked
+ * by providing a nested array entry after the expected <menu> ID.
+ * For example: ["blah", true, // item enabled
+ * "submenu", null, // submenu
+ * ["sub1", true, // submenu contents
+ * "sub2", false], null, // submenu contents
+ * "lol", false] // item disabled
+ *
+ */
+function checkMenu(menu, expectedItems, data) {
+ var actualItems = getVisibleMenuItems(menu, data);
+ //ok(false, "Items are: " + actualItems);
+ for (var i = 0; i < expectedItems.length; i+=2) {
+ var actualItem = actualItems[i];
+ var actualEnabled = actualItems[i + 1];
+ var expectedItem = expectedItems[i];
+ var expectedEnabled = expectedItems[i + 1];
+ if (expectedItem instanceof Array) {
+ ok(true, "Checking submenu...");
+ var menuID = expectedItems[i - 2]; // The last item was the menu ID.
+ var submenu = menu.getElementsByAttribute("id", menuID)[0];
+ ok(submenu && submenu.nodeName == "menu", "got expected submenu element");
+ checkMenu(submenu.menupopup, expectedItem, data);
+ } else {
+ is(actualItem, expectedItem,
+ "checking item #" + i/2 + " (" + expectedItem + ") name");
+
+ if (typeof expectedEnabled == "object" && expectedEnabled != null ||
+ typeof actualEnabled == "object" && actualEnabled != null) {
+
+ ok(!(actualEnabled == null), "actualEnabled is not null");
+ ok(!(expectedEnabled == null), "expectedEnabled is not null");
+ is(typeof actualEnabled, typeof expectedEnabled, "checking types");
+
+ if (typeof actualEnabled != typeof expectedEnabled ||
+ actualEnabled == null || expectedEnabled == null)
+ continue;
+
+ is(actualEnabled.type, expectedEnabled.type,
+ "checking item #" + i/2 + " (" + expectedItem + ") type attr value");
+ var icon = actualEnabled.icon;
+ if (icon) {
+ var tmp = "";
+ var j = icon.length - 1;
+ while (j && icon[j] != "/") {
+ tmp = icon[j--] + tmp;
+ }
+ icon = tmp;
+ }
+ is(icon, expectedEnabled.icon,
+ "checking item #" + i/2 + " (" + expectedItem + ") icon attr value");
+ is(actualEnabled.checked, expectedEnabled.checked,
+ "checking item #" + i/2 + " (" + expectedItem + ") has checked attr");
+ is(actualEnabled.disabled, expectedEnabled.disabled,
+ "checking item #" + i/2 + " (" + expectedItem + ") has disabled attr");
+ } else if (expectedEnabled != null)
+ is(actualEnabled, expectedEnabled,
+ "checking item #" + i/2 + " (" + expectedItem + ") enabled state");
+ }
+ }
+ // Could find unexpected extra items at the end...
+ is(actualItems.length, expectedItems.length, "checking expected number of menu entries");
+}
+
+/*
+ * runTest
+ *
+ * Called by a popupshowing event handler. Each test checks for expected menu
+ * contents, closes the popup, and finally triggers the popup on a new element
+ * (thus kicking off another cycle).
+ *
+ */
+function runTest(testNum) {
+ ok(true, "Starting test #" + testNum);
+
+ switch (testNum) {
+ case 1:
+ // Invoke context menu for next test.
+ openContextMenuFor(text);
+ break;
+
+ case 2:
+ // Context menu for plain text
+ plainTextItems = ["context-back", false,
+ "context-forward", false,
+ "context-reload", true,
+ "context-stop", false,
+ "---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "context-sendpage", true,
+ "---", null,
+ "context-viewbgimage", false,
+ "context-selectall", true,
+ "---", null,
+ "context-viewsource", true,
+ "context-viewinfo", true];
+ checkContextMenu(plainTextItems);
+ closeContextMenu();
+ openContextMenuFor(link); // Invoke context menu for next test.
+ break;
+
+ case 3:
+ // Context menu for text link
+ checkContextMenu(["context-openlinkintab", true,
+ "context-openlink", true,
+ "context-openlinkinprivatewindow", true,
+ "---", null,
+ "context-bookmarklink", true,
+ "context-savelink", true,
+ "context-sendlink", true,
+ "context-copylink", true,
+ "---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "---", null,
+ "context-metadata", true]);
+ closeContextMenu();
+ openContextMenuFor(mailto); // Invoke context menu for next test.
+ break;
+
+ case 4:
+ // Context menu for text mailto-link
+ checkContextMenu(["context-copyemail", true,
+ "context-copylink", true,
+ "---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "---", null,
+ "context-metadata", true]);
+ closeContextMenu();
+ openContextMenuFor(input); // Invoke context menu for next test.
+ break;
+
+ case 5:
+ // Context menu for text input field
+ checkContextMenu(["context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", false,
+ "context-copy", false,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", false,
+ "---", null,
+ "context-selectall", false,
+ "---", null,
+ "spell-check-enabled", true]);
+ closeContextMenu();
+ input_sel.value = "test";
+ input_sel.select();
+ openContextMenuFor(input_sel); // Invoke context menu for next test.
+ break;
+
+ case 6:
+ // Context menu for text input field with text
+ checkContextMenu(["context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", true,
+ "context-copy", true,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", true,
+ "---", null,
+ "context-selectall", true,
+ "context-searchselect", true,
+ "---", null,
+ "spell-check-enabled", true]);
+ closeContextMenu();
+ openContextMenuFor(img); // Invoke context menu for next test.
+ break;
+
+ case 7:
+ // Context menu for an image
+ checkContextMenu(["context-reloadimage", true,
+ "context-viewimage", true,
+ "context-blockimage", true,
+ "context-copyimage", true,
+ "---", null,
+ "context-saveimage", true,
+ "context-sendimage", true].concat(
+ ("@mozilla.org/suite/shell-service;1" in Cc) ?
+ ["context-setDesktopBackground", true] : []).concat(
+ ["---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "context-sendpage", true,
+ "---", null,
+ "context-metadata", true]));
+ closeContextMenu();
+ openContextMenuFor(canvas); // Invoke context menu for next test.
+ break;
+
+ case 8:
+ // Context menu for a canvas
+ checkContextMenu(["context-viewimage", true,
+ "context-saveimage", true,
+ "context-bookmarkpage", true,
+ "context-sendpage", true,
+ "context-selectall", true]);
+ closeContextMenu();
+ openContextMenuFor(video_ok); // Invoke context menu for next test.
+ break;
+
+ case 9:
+ // Context menu for a video (with a VALID media source)
+ checkContextMenu(["context-media-play", true,
+ "context-media-mute", true,
+ "context-media-playbackrate", true,
+ ["context-media-playbackrate-050", true,
+ "context-media-playbackrate-100", true,
+ "context-media-playbackrate-150", true,
+ "context-media-playbackrate-200", true], null,
+ "context-media-hidecontrols", true,
+ "context-video-showstats", true,
+ "context-video-fullscreen", true,
+ "---", null,
+ "context-viewvideo", true,
+ "context-copyvideourl", true,
+ "---", null,
+ "context-savevideo", true,
+ "context-sendvideo", true,
+ "context-video-saveimage", true]);
+ closeContextMenu();
+ openContextMenuFor(audio_in_video); // Invoke context menu for next test.
+ break;
+
+ case 10:
+ // Context menu for a video (with an audio-only file)
+ checkContextMenu(["context-media-play", true,
+ "context-media-mute", true,
+ "context-media-playbackrate", true,
+ ["context-media-playbackrate-050", true,
+ "context-media-playbackrate-100", true,
+ "context-media-playbackrate-150", true,
+ "context-media-playbackrate-200", true], null,
+ "context-media-showcontrols", true,
+ "---", null,
+ "context-copyaudiourl", true,
+ "---", null,
+ "context-saveaudio", true,
+ "context-sendaudio", true]);
+ closeContextMenu();
+ openContextMenuFor(video_bad); // Invoke context menu for next test.
+ break;
+
+ case 11:
+ // Context menu for a video (with an INVALID media source)
+ checkContextMenu(["context-media-play", false,
+ "context-media-mute", false,
+ "context-media-playbackrate", false,
+ ["context-media-playbackrate-050", null,
+ "context-media-playbackrate-100", null,
+ "context-media-playbackrate-150", null,
+ "context-media-playbackrate-200", null], null,
+ "context-media-hidecontrols", false,
+ "context-video-showstats", false,
+ "context-video-fullscreen", false,
+ "---", null,
+ "context-viewvideo", true,
+ "context-copyvideourl", true,
+ "---", null,
+ "context-savevideo", true,
+ "context-sendvideo", true,
+ "context-video-saveimage", false]);
+ closeContextMenu();
+ openContextMenuFor(video_bad2); // Invoke context menu for next test.
+ break;
+
+ case 12:
+ // Context menu for a video (with an INVALID media source)
+ checkContextMenu(["context-media-play", false,
+ "context-media-mute", false,
+ "context-media-playbackrate", false,
+ ["context-media-playbackrate-050", null,
+ "context-media-playbackrate-100", null,
+ "context-media-playbackrate-150", null,
+ "context-media-playbackrate-200", null], null,
+ "context-media-hidecontrols", false,
+ "context-video-showstats", false,
+ "context-video-fullscreen", false,
+ "---", null,
+ "context-viewvideo", false,
+ "context-copyvideourl", false,
+ "---", null,
+ "context-savevideo", false,
+ "context-sendvideo", false,
+ "context-video-saveimage", false]);
+ closeContextMenu();
+ openContextMenuFor(iframe); // Invoke context menu for next test.
+ break;
+
+ case 13:
+ // Context menu for an iframe
+ checkContextMenu(["context-back", false,
+ "context-forward", false,
+ "context-reload", true,
+ "context-stop", false,
+ "---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "context-sendpage", true,
+ "---", null,
+ "context-viewbgimage", false,
+ "context-selectall", true,
+ "---", null,
+ "context-viewsource", true,
+ "context-viewinfo", true,
+ "---", null,
+ "frame", null,
+ ["context-showonlythisframe", true,
+ "context-openframeintab", true,
+ "context-openframe", true,
+ "---", null,
+ "context-reloadframe", true,
+ "---", null,
+ "context-bookmarkframe", true,
+ "context-saveframe", true,
+ "context-sendframe", true,
+ "---", null,
+ "context-viewframesource", true,
+ "context-viewframeinfo", true], null]);
+ closeContextMenu();
+ openContextMenuFor(video_in_iframe); // Invoke context menu for next test.
+ break;
+
+ case 14:
+ // Context menu for a video in an iframe
+ checkContextMenu(["context-media-play", true,
+ "context-media-mute", true,
+ "context-media-playbackrate", true,
+ ["context-media-playbackrate-050", true,
+ "context-media-playbackrate-100", true,
+ "context-media-playbackrate-150", true,
+ "context-media-playbackrate-200", true], null,
+ "context-media-hidecontrols", true,
+ "context-video-showstats", true,
+ "context-video-fullscreen", true,
+ "---", null,
+ "context-viewvideo", true,
+ "context-copyvideourl", true,
+ "---", null,
+ "context-savevideo", true,
+ "context-sendvideo", true,
+ "context-video-saveimage", true,
+ "---", null,
+ "frame", null,
+ ["context-showonlythisframe", true,
+ "context-openframeintab", true,
+ "context-openframe", true,
+ "---", null,
+ "context-reloadframe", true,
+ "---", null,
+ "context-bookmarkframe", true,
+ "context-saveframe", true,
+ "context-sendframe", true,
+ "---", null,
+ "context-viewframesource", true,
+ "context-viewframeinfo", true], null]);
+ closeContextMenu();
+ openContextMenuFor(image_in_iframe); // Invoke context menu for next test.
+ break;
+
+ case 15:
+ // Context menu for an image in an iframe
+ checkContextMenu(["context-reloadimage", true,
+ "context-viewimage", true,
+ "context-blockimage", true,
+ "context-copyimage", true,
+ "---", null,
+ "context-saveimage", true,
+ "context-sendimage", true,
+ ].concat(
+ ("@mozilla.org/suite/shell-service;1" in Cc) ?
+ ["context-setDesktopBackground", true] : [])
+ .concat(
+ ["---", null,
+ "context-sendpage", true,
+ "---", null,
+ "context-metadata", true,
+ "---", null,
+ "frame", null,
+ ["context-showonlythisframe", true,
+ "context-openframeintab", true,
+ "context-openframe", true,
+ "---", null,
+ "context-reloadframe", true,
+ "---", null,
+ "context-bookmarkframe", true,
+ "context-saveframe", true,
+ "context-sendframe", true,
+ "---", null,
+ "context-viewframesource", true,
+ "context-viewframeinfo", true], null]));
+ closeContextMenu();
+ openContextMenuFor(text); // Invoke context menu for next test.
+ break;
+
+ case 16:
+ // Re-check context menu for plain text to make sure it hasn't changed
+ checkContextMenu(plainTextItems);
+ closeContextMenu();
+ textarea_sel.select();
+ openContextMenuFor(textarea_sel, false, true);
+ break;
+
+ case 17:
+ // search for text with text area's selected value
+ checkContextMenu(["context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", true,
+ "context-copy", true,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", true,
+ "---", null,
+ "context-selectall", true,
+ "context-searchselect", true,
+ "---", null,
+ "spell-check-enabled", true,
+ "spell-dictionaries", true,
+ ["spell-check-dictionary-en-US", true,
+ "---", null,
+ "spell-add-dictionaries", true], null]);
+ closeContextMenu();
+ openContextMenuFor(textarea, false, true); // Invoke context menu for next test, but wait for the spellcheck.
+ break;
+
+ case 18:
+ // Context menu for textarea
+ checkContextMenu(["*chubbiness", true, // spelling suggestion
+ "---", null,
+ "spell-add-to-dictionary", true,
+ "spell-ignore-word", true,
+ "---", null,
+ "context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", false,
+ "context-copy", false,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", false,
+ "---", null,
+ "context-selectall", true,
+ "---", null,
+ "spell-check-enabled", true,
+ "spell-dictionaries", true,
+ ["spell-check-dictionary-en-US", true,
+ "---", null,
+ "spell-add-dictionaries", true], null]);
+
+ contextMenu.ownerDocument.getElementById("spell-add-to-dictionary").doCommand(); // Add to dictionary
+ closeContextMenu();
+ openContextMenuFor(textarea, false, true); // Invoke context menu for next test.
+ break;
+
+ case 19:
+ // Context menu for textarea after a word has been added
+ // to the dictionary
+ checkContextMenu(["spell-undo-add-to-dictionary", true,
+ "---", null,
+ "context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", false,
+ "context-copy", false,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", false,
+ "---", null,
+ "context-selectall", true,
+ "---", null,
+ "spell-check-enabled", true,
+ "spell-dictionaries", true,
+ ["spell-check-dictionary-en-US", true,
+ "---", null,
+ "spell-add-dictionaries", true], null,
+ ]);
+
+ contextMenu.ownerDocument.getElementById("spell-undo-add-to-dictionary").doCommand(); // Undo add to dictionary
+ closeContextMenu();
+ openContextMenuFor(contenteditable, false, true);
+ break;
+
+ case 20:
+ // Context menu for contenteditable
+ checkContextMenu(["spell-no-suggestions", false,
+ "---", null,
+ "spell-add-to-dictionary", true,
+ "spell-ignore-word", true,
+ "---", null,
+ "context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", false,
+ "context-copy", false,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", false,
+ "---", null,
+ "context-selectall", true,
+ "---", null,
+ "spell-check-enabled", true,
+ "spell-dictionaries", true,
+ ["spell-check-dictionary-en-US", true,
+ "---", null,
+ "spell-add-dictionaries", true], null]);
+
+ closeContextMenu();
+ openContextMenuFor(inputspell, false, true); // Invoke context menu for next test.
+ break;
+
+ case 21:
+ // Context menu for spell-check input
+ checkContextMenu(["*prodigality", true, // spelling suggestion
+ "---", null,
+ "spell-add-to-dictionary", true,
+ "spell-ignore-word", true,
+ "---", null,
+ "context-undo", false,
+ "context-redo", false,
+ "---", null,
+ "context-cut", false,
+ "context-copy", false,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", false,
+ "---", null,
+ "context-selectall", true,
+ "---", null,
+ "spell-check-enabled", true,
+ "spell-dictionaries", true,
+ ["spell-check-dictionary-en-US", true,
+ "---", null,
+ "spell-add-dictionaries", true], null]);
+
+ closeContextMenu();
+ openContextMenuFor(link); // Invoke context menu for next test.
+ break;
+
+ case 22:
+ executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
+ closeContextMenu();
+ openContextMenuFor(pagemenu); // Invoke context menu for next test.
+ break;
+
+ case 23:
+ // Context menu for element with assigned content context menu
+ checkContextMenu(["+Plain item", {type: "", icon: "", checked: false, disabled: false},
+ "+Disabled item", {type: "", icon: "", checked: false, disabled: true},
+ "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false},
+ "---", null,
+ "+Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false},
+ "---", null,
+ "+Radio1", {type: "checkbox", icon: "", checked: true, disabled: false},
+ "+Radio2", {type: "checkbox", icon: "", checked: false, disabled: false},
+ "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false},
+ "---", null,
+ "+Item w/ icon", {type: "", icon: "favicon.ico", checked: false, disabled: false},
+ "+Item w/ bad icon", {type: "", icon: "", checked: false, disabled: false},
+ "---", null,
+ "generated-submenu-1", true,
+ ["+Radio1", {type: "checkbox", icon: "", checked: false, disabled: false},
+ "+Radio2", {type: "checkbox", icon: "", checked: true, disabled: false},
+ "+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false},
+ "---", null,
+ "+Checkbox", {type: "checkbox", icon: "", checked: false, disabled: false}], null,
+ "---", null,
+ "context-back", false,
+ "context-forward", false,
+ "context-reload", true,
+ "context-stop", false,
+ "---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "context-sendpage", true,
+ "---", null,
+ "context-viewbgimage", false,
+ "context-selectall", true,
+ "---", null,
+ "context-viewsource", true,
+ "context-viewinfo", true]);
+
+ invokeItemAction("0");
+ closeContextMenu();
+ openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
+ break;
+
+ case 24:
+ // Context menu for element with assigned content context menu
+ // The shift key should bypass content context menu processing
+ checkContextMenu(["context-back", false,
+ "context-forward", false,
+ "context-reload", true,
+ "context-stop", false,
+ "---", null,
+ "context-bookmarkpage", true,
+ "context-savepage", true,
+ "context-sendpage", true,
+ "---", null,
+ "context-viewbgimage", false,
+ "context-selectall", true,
+ "---", null,
+ "context-viewsource", true,
+ "context-viewinfo", true]);
+ closeContextMenu();
+
+ // Continue with SeaMonkey specific cases.
+ openContextMenuFor(img_link); // Invoke context menu for next test.
+ break;
+
+ case 25:
+ // Context menu for an image with a link
+ checkContextMenu(["context-openlinkintab", true,
+ "context-openlink", true,
+ "context-openlinkinprivatewindow", true,
+ "---", null,
+ "context-bookmarklink", true,
+ "context-savelink", true,
+ "context-sendlink", true,
+ "context-copylink", true,
+ "---", null,
+ "context-reloadimage", true,
+ "context-viewimage", true,
+ "context-blockimage", true,
+ "context-copyimage", true,
+ "---", null,
+ "context-saveimage", true,
+ "context-sendimage", true].concat(
+ ("@mozilla.org/suite/shell-service;1" in Cc) ?
+ ["context-setDesktopBackground", true] : []).concat(
+ ["---", null,
+ "context-bookmarkpage", true,
+ "---", null,
+ "context-metadata", true]));
+ closeContextMenu();
+ openContextMenuFor(img_mailto); // Invoke context menu for next test.
+ break;
+
+ case 26:
+ // Context menu for an image with a mailto: link
+ checkContextMenu(["context-copyemail", true,
+ "context-copylink", true,
+ "---", null,
+ "context-reloadimage", true,
+ "context-viewimage", true,
+ "context-blockimage", true,
+ "context-copyimage", true,
+ "---", null,
+ "context-saveimage", true,
+ "context-sendimage", true].concat(
+ ("@mozilla.org/suite/shell-service;1" in Cc) ?
+ ["context-setDesktopBackground", true] : []).concat(
+ ["---", null,
+ "context-bookmarkpage", true,
+ "---", null,
+ "context-metadata", true]));
+ closeContextMenu();
+
+ subwindow.close();
+ SimpleTest.finish();
+ return;
+
+ /*
+ * Other things that would be nice to test:
+ * - selected text
+ * - spelling / misspelled word (in text input?)
+ * - check state of disabled items
+ * - test execution of menu items (maybe as a separate test?)
+ */
+
+ default:
+ ok(false, "Unexpected invocation of test #" + testNum);
+ subwindow.close();
+ SimpleTest.finish();
+ return;
+ }
+
+}
+
+
+var testNum = 1;
+var subwindow, chromeWin, contextMenu, lastElement;
+var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2,
+ iframe, video_in_iframe, image_in_iframe, textarea, contenteditable,
+ inputspell, pagemenu, audio_in_video;
+
+// SeaMonkey specific variables.
+var img_link, img_mailto;
+
+function startTest() {
+ chromeWin = SpecialPowers.wrap(subwindow)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow)
+ .QueryInterface(Ci.nsIDOMChromeWindow);
+ contextMenu = chromeWin.document.getElementById("contentAreaContextMenu");
+ ok(contextMenu, "Got context menu XUL");
+
+ if (chromeWin.document.getElementById("context-stop").getAttribute("disabled") != "true") {
+ isnot(false, "Document still loading");
+ SimpleTest.executeSoon(startTest);
+ return;
+ }
+
+ lastElement = null;
+
+ text = subwindow.document.getElementById("test-text");
+ link = subwindow.document.getElementById("test-link");
+ mailto = subwindow.document.getElementById("test-mailto");
+ input = subwindow.document.getElementById("test-input");
+ input_sel = subwindow.document.getElementById("test-input-select");
+ img = subwindow.document.getElementById("test-image");
+ canvas = subwindow.document.getElementById("test-canvas");
+ video_ok = subwindow.document.getElementById("test-video-ok");
+ audio_in_video = subwindow.document.getElementById("test-audio-in-video");
+ video_bad = subwindow.document.getElementById("test-video-bad");
+ video_bad2 = subwindow.document.getElementById("test-video-bad2");
+ iframe = subwindow.document.getElementById("test-iframe");
+ video_in_iframe = subwindow.document.getElementById("test-video-in-iframe").contentDocument.getElementsByTagName("video")[0];
+ // Ensure that contextmenu has 'context-media-play' item when check runs.
+ video_in_iframe.pause();
+ image_in_iframe = subwindow.document.getElementById("test-image-in-iframe").contentDocument.getElementsByTagName("img")[0];
+ textarea = subwindow.document.getElementById("test-textarea");
+ textarea_sel = subwindow.document.getElementById("test-textarea-sel");
+ contenteditable = subwindow.document.getElementById("test-contenteditable");
+ contenteditable.focus(); // content editable needs to be focused to enable spellcheck
+ inputspell = subwindow.document.getElementById("test-input-spellcheck");
+ pagemenu = subwindow.document.getElementById("test-pagemenu");
+
+ // SeaMonkey specific elements.
+ img_link = subwindow.document.getElementById("test-image-link");
+ img_mailto = subwindow.document.getElementById("test-image-mailto");
+
+ contextMenu.addEventListener("popupshown", function() { runTest(++testNum); });
+ runTest(1);
+}
+
+// We open this in a separate window, because the Mochitests run inside a frame.
+// The frame causes an extra menu item, and prevents running the test
+// standalone (ie, clicking the test name in the Mochitest window) to see
+// success/failure messages.
+var painted = false, loaded = false;
+
+function waitForEvents(event)
+{
+ if (event.type == "MozAfterPaint")
+ painted = true;
+ else if (event.type == "load")
+ loaded = true;
+ if (painted && loaded) {
+ subwindow.removeEventListener("MozAfterPaint", waitForEvents);
+ subwindow.onload = null;
+ startTest();
+ }
+}
+
+var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=700");
+subwindow.addEventListener("MozAfterPaint", waitForEvents);
+subwindow.onload = waitForEvents;
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/test_feed_discovery.html b/comm/suite/browser/test/mochitest/test_feed_discovery.html
new file mode 100644
index 0000000000..2f2a0a459e
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/test_feed_discovery.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=240393
+-->
+<head>
+ <title>Test for feed discovery</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377611">Mozilla Bug 377611</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody">
+
+/** Tests for bug 240393 (from bug 377611 on Firefox side) **/
+
+var rv = { tests: null };
+var testCheckInterval = null;
+
+function startTest() {
+ var url = window.location.href.replace(/test_feed_discovery\.html/,
+ 'feed_discovery.html');
+ SpecialPowers.openDialog(window, [url, '', 'dialog=no,width=10,height=10', rv]);
+ testCheckInterval = window.setInterval(tryIfTestIsFinished, 500);
+}
+
+function tryIfTestIsFinished() {
+ if (rv.tests) {
+ window.clearInterval(testCheckInterval);
+ checkTest();
+ }
+}
+
+function checkTest() {
+ for (var i = 0; i < rv.tests.length; ++ i) {
+ var test = rv.tests[i];
+ if (test.todo)
+ todo(test.check, test.message);
+ else
+ ok(test.check, test.message);
+ }
+ SimpleTest.finish();
+}
+
+window.onload = startTest;
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/test_registerHandler.html b/comm/suite/browser/test/mochitest/test_registerHandler.html
new file mode 100644
index 0000000000..20952ca9ee
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/test_registerHandler.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=402788
+-->
+<head>
+ <title>Test for Bug 402788</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=402788">Mozilla Bug 402788</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody">
+
+/** Test for Bug 402788 **/
+
+ // return false if an exception has been catched, true otherwise
+ function testRegisterHandler(aIsProtocol, aTxt, aUri, aTitle)
+ {
+ try {
+ if (aIsProtocol)
+ navigator.registerProtocolHandler(aTxt, aUri, aTitle);
+ else
+ navigator.registerContentHandler(aTxt, aUri, aTitle);
+ }
+ catch(e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ ok(navigator.registerProtocolHandler, "navigator.registerProtocolHandler should be defined");
+ ok(navigator.registerContentHandler, "navigator.registerContentHandler should be defined");
+
+ // testing a generic case
+ is(true, testRegisterHandler(true, "foo", "http://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler should work");
+ is(true, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler should work");
+
+ // testing with wrong uris
+ is(false, testRegisterHandler(true, "foo", "http://mochi.test:8888/", "Foo handler"), "a protocol handler uri should contain %s");
+ is(false, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/", "Foo handler"), "a content handler uri should contain %s");
+
+ // the spec says we should not throw here, but it probably needs to be changed
+ is(false, testRegisterHandler(true, "foo", "foo/%s", "Foo handler"), "a protocol handler uri should be valid");
+ is(false, testRegisterHandler(false, "application/rss+xml", "foo/%s", "Foo handler"), "a content handler uri should be valid");
+
+ // we should only accept to register when the handler has the same host as the current page (bug 402287)
+ is(false, testRegisterHandler(true, "foo", "http://remotehost:8888/%s", "Foo handler"), "registering a foo protocol handler with a different host should not work");
+ is(false, testRegisterHandler(false, "application/rss+xml", "http://remotehost:8888/%s", "Foo handler"), "registering a foo content handler with a different host should not work");
+
+ // restriction to http(s) for the uri of the handler (bug 401343)
+ // https should work (http already tested in the generic case)
+ is(true, testRegisterHandler(true, "foo", "https://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with https scheme should work");
+ is(true, testRegisterHandler(false, "application/rss+xml", "https://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with https scheme should work");
+ // ftp should not work
+ is(false, testRegisterHandler(true, "foo", "ftp://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with ftp scheme should not work");
+ is(false, testRegisterHandler(false, "application/rss+xml", "ftp://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with ftp scheme should not work");
+ // chrome should not work
+ is(false, testRegisterHandler(true, "foo", "chrome://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with chrome scheme should not work");
+ is(false, testRegisterHandler(false, "application/rss+xml", "chrome://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with chrome scheme should not work");
+ // foo should not work
+ is(false, testRegisterHandler(true, "foo", "foo://mochi.test:8888/%s", "Foo handler"), "registering a foo protocol handler with foo scheme should not work");
+ is(false, testRegisterHandler(false, "application/rss+xml", "foo://mochi.test:8888/%s", "Foo handler"), "registering a foo content handler with foo scheme should not work");
+
+ // for security reasons, protocol handlers should never be registered for some schemes (chrome, vbscript, ...) (bug 402788)
+ is(false, testRegisterHandler(true, "chrome", "http://mochi.test:8888/%s", "chrome handler"), "registering a chrome protocol handler should not work");
+ is(false, testRegisterHandler(true, "vbscript", "http://mochi.test:8888/%s", "vbscript handler"), "registering a vbscript protocol handler should not work");
+ is(false, testRegisterHandler(true, "javascript", "http://mochi.test:8888/%s", "javascript handler"), "registering a javascript protocol handler should not work");
+ is(false, testRegisterHandler(true, "moz-icon", "http://mochi.test:8888/%s", "moz-icon handler"), "registering a moz-icon protocol handler should not work");
+
+ // for security reasons, content handlers should never be registered for some types (html, ...)
+ is(true, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering rss content handlers should work");
+ is(true, testRegisterHandler(false, "application/atom+xml", "http://mochi.test:8888/%s", "Foo handler"), "registering atom content handlers should work");
+ todo(false, testRegisterHandler(false, "text/html", "http://mochi.test:8888/%s", "Foo handler"), "registering html content handlers should not work"); // bug 403798
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/comm/suite/browser/test/mochitest/valid-feed.xml b/comm/suite/browser/test/mochitest/valid-feed.xml
new file mode 100644
index 0000000000..0e700b6d8d
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/valid-feed.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+
+ <title>Example Feed</title>
+ <link href="http://example.org/"/>
+ <updated>2010-08-22T18:30:02Z</updated>
+
+ <author>
+ <name>John Doe</name>
+ </author>
+ <id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id>
+
+ <entry>
+
+ <title>Item</title>
+ <link href="http://example.org/first"/>
+ <id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id>
+ <updated>2010-08-22T18:30:02Z</updated>
+
+ <summary>Some text.</summary>
+ </entry>
+
+</feed>
diff --git a/comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml b/comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml
new file mode 100644
index 0000000000..e753157395
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/valid-unsniffable-feed.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 512 bytes!
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ -->
+<feed xmlns="http://www.w3.org/2005/Atom">
+
+ <title>Example Feed</title>
+ <link href="http://example.org/"/>
+ <updated>2010-08-22T18:30:02Z</updated>
+
+ <author>
+ <name>John Doe</name>
+ </author>
+ <id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id>
+
+ <entry>
+
+ <title>Item</title>
+ <link href="http://example.org/first"/>
+ <id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id>
+ <updated>2010-08-22T18:30:02Z</updated>
+
+ <summary>Some text.</summary>
+ </entry>
+
+</feed>
diff --git a/comm/suite/browser/test/mochitest/video.ogg b/comm/suite/browser/test/mochitest/video.ogg
new file mode 100644
index 0000000000..ac7ece3519
--- /dev/null
+++ b/comm/suite/browser/test/mochitest/video.ogg
Binary files differ
diff --git a/comm/suite/browser/urlbarBindings.xml b/comm/suite/browser/urlbarBindings.xml
new file mode 100644
index 0000000000..2026724e5a
--- /dev/null
+++ b/comm/suite/browser/urlbarBindings.xml
@@ -0,0 +1,694 @@
+<?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 bindings [
+ <!ENTITY % textcontextDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+ %textcontextDTD;
+ <!ENTITY % navigatorDTD SYSTEM "chrome://navigator/locale/navigator.dtd">
+ %navigatorDTD;
+]>
+
+<bindings id="urlbarBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="urlbar" extends="chrome://global/content/autocomplete.xml#autocomplete">
+ <content sizetopopup="pref">
+ <xul:hbox class="autocomplete-textbox-container" flex="1">
+ <xul:hbox class="urlbar-security-level" flex="1" align="center" xbl:inherits="level">
+ <children includes="image|deck|stack|box">
+ <xul:image class="autocomplete-icon" allowevents="true"/>
+ </children>
+
+ <xul:hbox class="textbox-input-box paste-and-go" flex="1" tooltip="_child" xbl:inherits="context">
+ <children/>
+ <html:input anonid="input" class="autocomplete-textbox textbox-input"
+ allowevents="true"
+ xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,userAction"/>
+ <xul:tooltip anonid="tooltip"
+ onpopupshowing="document.getBindingParent(this)._showTooltip(event);"/>
+ </xul:hbox>
+ <children includes="hbox"/>
+ </xul:hbox>
+ </xul:hbox>
+
+ <xul:dropmarker class="autocomplete-history-dropmarker" allowevents="true"
+ xbl:inherits="open" anonid="historydropmarker"/>
+
+ <xul:popupset>
+ <xul:panel type="autocomplete" anonid="popup"
+ ignorekeys="true" noautofocus="true" level="top"
+ xbl:inherits="for=id,nomatch"/>
+ </xul:popupset>
+
+ <children includes="menupopup"/>
+ </content>
+
+ <implementation implements="nsIObserver, nsIDOMEventListener">
+ <constructor><![CDATA[
+ this._prefs = Services.prefs.getBranch("browser.urlbar.");
+ this._prefs.addObserver("", this);
+
+ this.updatePref("showPopup");
+ this.updatePref("autoFill");
+ this.updatePref("showSearch");
+ this.updatePref("formatting.enabled");
+ this.inputField.controllers.insertControllerAt(0, this._editItemsController);
+ this.inputField.addEventListener("overflow", this);
+ this.inputField.addEventListener("underflow", this);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ // Somehow, it's possible for the XBL destructor to fire without the
+ // constructor ever having fired. Fix:
+ if (!this._prefs) {
+ return;
+ }
+ this._prefs.removeObserver("", this);
+ this._prefs = null;
+ this.inputField.removeEventListener("underflow", this);
+ this.inputField.removeEventListener("overflow", this);
+ this.inputField.controllers.removeController(this._editItemsController);
+ ]]></destructor>
+
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body><![CDATA[
+ if (aTopic == "nsPref:changed")
+ this.updatePref(aData);
+ ]]></body>
+ </method>
+
+ <method name="updatePref">
+ <parameter name="aPref"/>
+ <body><![CDATA[
+ if (aPref == "showPopup") {
+ this.showPopup = this._prefs.getBoolPref(aPref);
+ } else if (aPref == "autoFill") {
+ this.autoFill = this._prefs.getBoolPref(aPref);
+ } else if (aPref == "showSearch") {
+ this.minResultsForPopup = this._prefs.getBoolPref(aPref) ? 0 : 1;
+ } else if (aPref == "formatting.enabled") {
+ this._formattingEnabled = this._prefs.getBoolPref(aPref);
+ this._formatValue(this._formattingEnabled && !this.focused);
+ }
+ ]]></body>
+ </method>
+
+ <field name="_overflowing">false</field>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ switch (aEvent.type) {
+ case "overflow":
+ this._overflowing = true;
+ break;
+ case "underflow":
+ this._overflowing = false;
+ break;
+ }
+ ]]></body>
+ </method>
+
+ <method name="_showTooltip">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var tooltip = aEvent.target;
+ if (this._overflowing)
+ tooltip.label = this.value;
+ else if (this.value)
+ tooltip.label = this.placeholder;
+ else
+ aEvent.preventDefault();
+ ]]></body>
+ </method>
+
+ <field name="_formattingEnabled">true</field>
+
+ <method name="_formatValue">
+ <parameter name="formattingEnabled"/>
+ <body><![CDATA[
+ if (!this.editor)
+ return;
+
+ var controller = this.editor.selectionController;
+ var selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
+ selection.removeAllRanges();
+ if (!formattingEnabled)
+ return;
+
+ var textNode = this.editor.rootElement.firstChild;
+ var value = textNode.textContent;
+
+ var protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
+ if (protocol && !/^https?:|ftp:/.test(protocol[0]))
+ return;
+ var matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
+ if (!matchedURL)
+ return;
+
+ var [, preDomain, domain] = matchedURL;
+ var subDomain = "";
+ // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
+ if (domain[0] != "[") {
+ try {
+ var baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
+ if (!domain.endsWith(baseDomain)) {
+ // getBaseDomainFromHost converts its resultant to ACE.
+ let IDNService = Cc["@mozilla.org/network/idn-service;1"]
+ .getService(Ci.nsIIDNService);
+ baseDomain = IDNService.convertACEtoUTF8(baseDomain);
+ }
+ if (baseDomain != domain) {
+ subDomain = domain.slice(0, -baseDomain.length);
+ }
+ } catch (e) {}
+ }
+
+ var startLength = preDomain.length + subDomain.length;
+ if (startLength) {
+ var startRange = document.createRange();
+ startRange.setStart(textNode, 0);
+ startRange.setEnd(textNode, startLength);
+ selection.addRange(startRange);
+ }
+
+ var endLength = preDomain.length + domain.length;
+ if (endLength < value.length) {
+ var endRange = document.createRange();
+ endRange.setStart(textNode, endLength);
+ endRange.setEnd(textNode, value.length);
+ selection.addRange(endRange);
+ }
+ ]]></body>
+ </method>
+
+ <method name="autoFillInput">
+ <parameter name="aSessionName"/>
+ <parameter name="aResults"/>
+ <parameter name="aUseFirstMatchIfNoDefault"/>
+ <body><![CDATA[
+ if (this.mInputElt.selectionEnd < this.currentSearchString.length ||
+ this.mDefaultMatchFilled)
+ return;
+
+ if (!this.mFinishAfterSearch && this.autoFill &&
+ this.mLastKeyCode != KeyEvent.DOM_VK_BACK_SPACE &&
+ this.mLastKeyCode != KeyEvent.DOM_VK_DELETE) {
+ var indexToUse = aResults.defaultItemIndex;
+ if (aUseFirstMatchIfNoDefault && indexToUse == -1)
+ indexToUse = 0;
+
+ if (indexToUse != -1) {
+ var result = this.getSessionValueAt(aSessionName, indexToUse);
+ var entry = this.value;
+ var suffix = "";
+ if (/^ftp:\/\/ftp\b/.test(result) &&
+ result.lastIndexOf("ftp://" + entry, 0) == 0)
+ suffix = result.slice(entry.length + 6);
+ else if (!/^http:\/\/ftp\b/.test(result) &&
+ result.lastIndexOf("http://" + entry, 0) == 0)
+ suffix = result.slice(entry.length + 7);
+ else if (result.lastIndexOf(entry, 0) == 0)
+ suffix = result.slice(entry.length);
+
+ if (suffix) {
+ this.setTextValue(this.value + suffix);
+ this.mInputElt.setSelectionRange(entry.length, this.value.length);
+ this.mDefaultMatchFilled = true;
+ }
+ this.mNeedToComplete = true;
+ }
+ }
+ ]]></body>
+ </method>
+
+ <method name="_getSelectedValueForClipboard">
+ <body>
+ <![CDATA[
+ var inputVal = this.inputField.value;
+ var val = inputVal.substring(this.selectionStart, this.selectionEnd);
+
+ /* If the entire value is selected and it's a valid non-javascript,
+ non-data URI, encode it. */
+ if (val == inputVal &&
+ gProxyButton.getAttribute("pageproxystate") == "valid") {
+ var uri;
+ try {
+ uri = makeURI(val);
+ } catch (e) {}
+
+ if (uri && !uri.schemeIs("javascript") && !uri.schemeIs("data")) {
+ val = uri.spec;
+
+ // Parentheses are known to confuse third-party applications (bug 458565).
+ val = val.replace(/[()]/g, c => escape(c));
+ }
+ }
+
+ return val;
+ ]]>
+ </body>
+ </method>
+
+ <field name="_editItemsController"><![CDATA[
+ ({
+ supportsCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_copy":
+ case "cmd_cut":
+ case "cmd_pasteAndGo":
+ return true;
+ }
+ return false;
+ },
+ isCommandEnabled: function(aCommand) {
+ var hasSelection = this.selectionStart < this.selectionEnd;
+ switch (aCommand) {
+ case "cmd_copy":
+ return hasSelection;
+ case "cmd_cut":
+ return !this.readOnly && hasSelection;
+ case "cmd_pasteAndGo":
+ return document.commandDispatcher
+ .getControllerForCommand("cmd_paste")
+ .isCommandEnabled("cmd_paste");
+ }
+ return false;
+ }.bind(this),
+ doCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_copy":
+ case "cmd_cut":
+ var val = this._getSelectedValueForClipboard();
+ var controller = this._editItemsController;
+ if (!val || !controller.isCommandEnabled(aCommand))
+ return;
+
+ Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(val);
+
+ if (aCommand == "cmd_cut")
+ goDoCommand("cmd_delete");
+ break;
+
+ case "cmd_pasteAndGo":
+ this.select();
+ goDoCommand("cmd_paste");
+ this._fireEvent("textentered", "pasting");
+ break;
+ }
+ }.bind(this),
+ onEvent: function(aEventName) {}
+ })
+ ]]></field>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress"
+ key="&locationBar.accesskey;"
+ modifiers="access"
+ action="this.select();"/>
+
+ <handler event="ValueChange"><![CDATA[
+ if (this._formattingEnabled && !this.focused)
+ this._formatValue(true);
+ ]]></handler>
+
+ <handler event="blur"><![CDATA[
+ if (this._formattingEnabled)
+ this._formatValue(true);
+ ]]></handler>
+
+ <handler event="focus"><![CDATA[
+ this._formatValue(false);
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-result-popup" extends="chrome://global/content/autocomplete.xml#autocomplete-result-popup">
+ <content>
+ <xul:tree anonid="tree" class="autocomplete-tree plain" flex="1">
+ <xul:treecols anonid="treecols">
+ <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteValue" flex="2"/>
+ <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteComment" flex="1" hidden="true"/>
+ </xul:treecols>
+ <xul:treechildren anonid="treebody" class="autocomplete-treebody" flex="1"/>
+ </xul:tree>
+ <xul:box role="search-box" class="autocomplete-search-box"/>
+ </content>
+
+ <implementation implements="nsIObserver, nsIBrowserSearchInitObserver">
+ <constructor><![CDATA[
+ // listen for changes to default search engine
+ Services.prefs.addObserver("browser.search", this);
+ Services.prefs.addObserver("browser.urlbar", this);
+ Services.obs.addObserver(this, "browser-search-engine-modified");
+ this._initialized = true;
+ Services.search.init(this);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ Services.prefs.removeObserver("browser.search", this);
+ Services.prefs.removeObserver("browser.urlbar", this);
+ if (this._initialized) {
+ this._initialized = false;
+ Services.obs.removeObserver(this, "browser-search-engine-modified");
+ }
+ ]]></destructor>
+
+ <property name="showSearch" onget="return this.mShowSearch;">
+ <setter><![CDATA[
+ this.mShowSearch = val;
+ if (val) {
+ this.updateEngines();
+ this.mSearchBox.removeAttribute("hidden");
+ } else {
+ this.clearEngines();
+ this.mSearchBox.setAttribute("hidden", "true");
+ }
+ ]]></setter>
+ </property>
+
+ <property name="defaultSearchEngine"
+ onget="return this.textbox.getAttribute('defaultSearchEngine') == 'true';"
+ onset="this.textbox.setAttribute('defaultSearchEngine', val); return val;"/>
+
+ <field name="mSearchBox">
+ document.getAnonymousElementByAttribute(this, "role", "search-box");
+ </field>
+
+ <field name="mInputListener"><![CDATA[
+ (function(aEvent) {
+ // "this" is the textbox, not the popup
+ if (this.mSearchInputTO)
+ window.clearTimeout(this.mSearchInputTO);
+ this.mSearchInputTO = window.setTimeout(this.popup.mInputTimeout, this.timeout, this);
+ });
+ ]]></field>
+
+ <field name="mInputTimeout"><![CDATA[
+ (function(me) {
+ me.popup.mSearchBox.searchValue = me.value;
+ me.mSearchInputTO = 0;
+ });
+ ]]></field>
+
+ <field name="_initialized">false</field>
+ <field name="mEnginesReady">false</field>
+
+ <property name="overrideValue" readonly="true">
+ <getter><![CDATA[
+ if (this.mSearchBox.selectedIndex != -1) {
+ return this.mSearchBox.overrideValue;
+ }
+ return null;
+ ]]></getter>
+ </property>
+
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body><![CDATA[
+ switch (aTopic) {
+ case "browser-search-engine-modified":
+ if (aData == "engine-current") {
+ this.updateEngines();
+ }
+ break;
+ case "nsPref:changed":
+ if (/^browser\.search\./.test(aData))
+ Services.search.init(this);
+ else if (aData == "browser.urlbar.showSearch")
+ this.updateShowSearch();
+ break;
+ default:
+ }
+ ]]></body>
+ </method>
+
+ <method name="updateShowSearch">
+ <body><![CDATA[
+ this.showSearch = Services.prefs.getBoolPref("browser.urlbar.showSearch");
+ ]]></body>
+ </method>
+
+ <method name="onInitComplete">
+ <parameter name="aStatus"/>
+ <body><![CDATA[
+ if (!this._initialized)
+ return;
+ if (Components.isSuccessCode(aStatus)) {
+ // Refresh the engines.
+ this.updateEngines();
+ } else {
+ Cu.reportError("Cannot initialize search service, bailing out: " + aStatus);
+ }
+ ]]></body>
+ </method>
+
+ <method name="addEngine">
+ <parameter name="aName"/>
+ <parameter name="aIcon"/>
+ <parameter name="aSearchBarUrl"/>
+ <body><![CDATA[
+ var box = document.createElement("box");
+ box.setAttribute("class", "autocomplete-search-engine");
+ box.setAttribute("name", aName);
+ if (aIcon)
+ box.setAttribute("icon", aIcon.spec);
+ box.setAttribute("searchBarUrl", aSearchBarUrl);
+ box.setAttribute("engineIndex", this.childNodes.length);
+ this.mSearchBox.appendChild(box);
+ ]]></body>
+ </method>
+
+ <method name="clearEngines">
+ <body><![CDATA[
+ while (this.mSearchBox.hasChildNodes())
+ this.mSearchBox.lastChild.remove();
+ ]]></body>
+ </method>
+
+ <method name="updateEngines">
+ <body><![CDATA[
+ var defaultEngine = Services.search.defaultEngine;
+ if (defaultEngine) {
+ this.clearEngines();
+ this.addEngine(defaultEngine.name, defaultEngine.iconURI,
+ defaultEngine.searchForm);
+ }
+
+ this.mEnginesReady = true;
+ ]]></body>
+ </method>
+
+ <method name="clearSelection">
+ <body>
+ this.view.selection.clearSelection();
+ this.mSearchBox.selectedIndex = -1;
+ </body>
+ </method>
+
+ <method name="selectBy">
+ <parameter name="aReverse"/>
+ <parameter name="aPage"/>
+ <body><![CDATA[
+ var sel;
+ if (this.selectedIndex == -1 && aReverse ||
+ this.mSearchBox.selectedIndex != -1) {
+ sel = this.mSearchBox.selectBy(aReverse, aPage);
+ if (sel != -1 || !aReverse)
+ return -1;
+ }
+
+ sel = this.getNextIndex(aReverse, aPage, this.selectedIndex, this.view.rowCount - 1);
+ this.selectedIndex = sel;
+ if (sel == -1 && !aReverse)
+ this.mSearchBox.selectBy(aReverse, aPage);
+ else if (this.mSearchBox.selectedIndex != -1)
+ this.mSearchBox.selectedIndex = -1;
+ return sel;
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popupshowing"><![CDATA[
+ // sync up with prefs about showing search in the URL bar if
+ // the URL bar hasn't finished initializing the default engine info
+ // -or-
+ // the search sidebar tab was displayed already triggering a change
+ // notification to the browser.search pref branch listener here which
+ // calls updateEngines but doesn't cause the ``Search for ...''
+ // display string to be updated
+ if ( (!this.mEnginesReady && this.defaultSearchEngine) ||
+ !("mShowSearch" in this) )
+ this.updateShowSearch();
+
+ if (this.mShowSearch) {
+ this.textbox.mSearchInputTO = 0;
+ this.textbox.addEventListener("input", this.mInputListener);
+ if ("searchValue" in this.mSearchBox)
+ this.mSearchBox.searchValue = this.textbox.currentSearchString;
+ else
+ this.mSearchBox.setAttribute("searchvalue", this.textbox.currentSearchString);
+ }
+ ]]></handler>
+
+ <handler event="popuphiding"><![CDATA[
+ if (this.mShowSearch)
+ this.textbox.removeEventListener("input", this.mInputListener);
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-search-box">
+ <content orient="vertical"/>
+
+ <implementation>
+ <constructor><![CDATA[
+ this.navigatorBundle = Services.strings
+ .createBundle("chrome://navigator/locale/navigator.properties");
+
+ var text = this.getAttribute("searchvalue");
+ if (text)
+ this.searchValue = text;
+ ]]></constructor>
+
+ <field name="mSelectedIndex">
+ -1
+ </field>
+
+ <property name="activeChild"
+ onget="return this.childNodes[this.mSelectedIndex]"/>
+
+ <property name="selectedIndex">
+ <getter>return this.mSelectedIndex;</getter>
+
+ <setter><![CDATA[
+ if (this.mSelectedIndex != -1)
+ this.activeChild.removeAttribute("menuactive");
+
+ this.mSelectedIndex = val;
+
+ if (val != -1)
+ this.activeChild.setAttribute("menuactive", "true");
+ return val;
+ ]]></setter>
+
+ </property>
+
+ <property name="searchValue">
+ <getter><![CDATA[
+ return this.mSearchValue;
+ ]]></getter>
+ <setter><![CDATA[
+ this.mSearchValue = val;
+
+ const kids = this.childNodes;
+ for (var i = 0; i < kids.length; ++i) {
+ kids[i].setAttribute("label",
+ this.navigatorBundle.formatStringFromName(
+ "searchFor", [kids[i].getAttribute("name"), val], 2));
+ }
+ ]]></setter>
+ </property>
+
+ <method name="selectBy">
+ <parameter name="aReverse"/>
+ <parameter name="aPage"/>
+ <body><![CDATA[
+ return this.selectedIndex = this.parentNode.getNextIndex(aReverse, aPage, this.selectedIndex, this.childNodes.length - 1);
+ ]]></body>
+ </method>
+
+ <property name="overrideValue" readonly="true">
+ <getter><![CDATA[
+ var item = this.activeChild;
+ if (item) {
+ // XXXsearch: using default engine for now, this ignores the following values:
+ // item.getAttribute("searchEngine") and item.getAttribute("searchBarUrl")
+ var engine = Services.search.defaultEngine;
+
+ var submission = engine.getSubmission(this.mSearchValue); // HTML response
+
+ // getSubmission can return null if the engine doesn't have a URL
+ // with a text/html response type. This is unlikely (since
+ // SearchService._addEngineToStore() should fail for such an engine),
+ // but let's be on the safe side.
+ if (!submission)
+ return null;
+
+ // XXXsearch: the submission object may have postData but .overrideValue can't deal with that
+ // see http://mxr.mozilla.org/comm-central/source/mozilla/netwerk/base/public/nsIBrowserSearchService.idl#47
+ // we need to figure out what to do with that case here
+
+ return submission.uri.spec;
+ }
+ return null;
+ ]]></getter>
+ </property>
+
+ </implementation>
+
+ <handlers>
+ <handler event="mouseup">
+ this.parentNode.textbox.onResultClick();
+ </handler>
+ </handlers>
+
+ </binding>
+
+ <binding id="autocomplete-search-engine">
+ <content>
+ <xul:image class="autocomplete-search-engine-img" xbl:inherits="src=icon"/>
+ <xul:label class="autocomplete-search-engine-text" xbl:inherits="value=label" crop="right" flex="1"/>
+ </content>
+
+ <handlers>
+ <handler event="mousemove">
+ this.parentNode.selectedIndex = Number(this.getAttribute("engineIndex"));
+ </handler>
+
+ <handler event="mouseout">
+ this.parentNode.selectedIndex = -1;
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="input-box-paste" extends="chrome://global/content/bindings/textbox.xml#input-box">
+ <content context="_child">
+ <children/>
+ <xul:menupopup anonid="input-box-contextmenu"
+ class="textbox-contextmenu"
+ onpopupshowing="var input =
+ this.parentNode.getElementsByAttribute('anonid', 'input')[0];
+ if (document.commandDispatcher.focusedElement != input)
+ input.focus();
+ this.parentNode._doPopupItemEnabling(this);"
+ oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
+ <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
+ <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
+ <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
+ <xul:menuitem label="&pasteGoCmd.label;" accesskey="&pasteGoCmd.accesskey;" cmd="cmd_pasteAndGo"/>
+ <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
+ </xul:menupopup>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/browser/webDeveloperOverlay.js b/comm/suite/browser/webDeveloperOverlay.js
new file mode 100644
index 0000000000..41277681f6
--- /dev/null
+++ b/comm/suite/browser/webDeveloperOverlay.js
@@ -0,0 +1,197 @@
+/* -*- 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/. */
+
+ChromeUtils.defineModuleGetter(this, "BrowserToolboxProcess",
+ "resource://devtools/client/framework/ToolboxProcess.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "DeveloperToolbar", function() {
+ var tmp = {};
+ ChromeUtils.import("resource://devtools/shared/Loader.jsm", tmp);
+ var DeveloperToolbar = tmp.require("devtools/client/shared/developer-toolbar").DeveloperToolbar;
+ return new DeveloperToolbar(window);
+});
+
+var ResponsiveUI = {
+ toggle() {
+ this.ResponsiveUIManager.toggle(window, getBrowser().selectedTab);
+ }
+};
+
+XPCOMUtils.defineLazyGetter(ResponsiveUI, "ResponsiveUIManager", function() {
+ var tmp = {};
+ ChromeUtils.import("resource://devtools/client/responsivedesign/responsivedesign.jsm", tmp);
+ return tmp.ResponsiveUIManager;
+});
+
+var Scratchpad = {
+ openScratchpad() {
+ return this.ScratchpadManager.openScratchpad();
+ }
+};
+
+XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
+ var tmp = {};
+ ChromeUtils.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", tmp);
+ return tmp.ScratchpadManager;
+});
+
+ChromeUtils.defineModuleGetter(this, "gDevTools",
+ "resource://devtools/client/framework/gDevTools.jsm");
+
+ChromeUtils.defineModuleGetter(this, "gDevToolsBrowser",
+ "resource://devtools/client/framework/gDevTools.jsm");
+
+function openEyedropper() {
+ var eyedropper = new this.Eyedropper(this, { context: "menu",
+ copyOnSelect: true });
+ eyedropper.open();
+}
+
+Object.defineProperty(this, "Eyedropper", {
+ get() {
+ var tmp = {};
+ ChromeUtils.import("resource://devtools/shared/Loader.jsm", tmp);
+ return tmp.require("devtools/client/eyedropper/eyedropper").Eyedropper;
+ },
+ configurable: true,
+ enumerable: true
+});
+
+Object.defineProperty(this, "HUDService", {
+ get() {
+ var tmp = {};
+ ChromeUtils.import("resource://devtools/shared/Loader.jsm", tmp);
+ return tmp.require("devtools/client/webconsole/hudservice").HUDService;
+ },
+ configurable: true,
+ enumerable: true
+});
+
+var gWebDeveloper = {
+ validateThisPage: function() {
+ var service = GetLocalizedStringPref("browser.validate.html.service");
+ var uri = getBrowser().currentURI;
+ var checkURL = service + encodeURIComponent(uri.spec);
+ var opentab = Services.prefs.getBoolPref("browser.tabs.opentabfor.middleclick");
+ openUILinkIn(checkURL, opentab ? "tabfocused" : "window",
+ { referrerURI: uri, relatedToCurrent: true });
+ },
+
+ enableDebugger: function(aItem) {
+ var shouldEnable = aItem.getAttribute("checked") == "true";
+ Services.prefs.setBoolPref("devtools.debugger.remote-enabled", shouldEnable);
+ },
+
+ handleEvent: function(aEvent) {
+ switch (aEvent.type) {
+ case "load":
+ window.removeEventListener("load", gWebDeveloper);
+ window.addEventListener("unload", gWebDeveloper);
+ var popup = document.getElementById("toolsPopup");
+ popup.addEventListener("popupshowing", gWebDeveloper);
+ // Don't use gDevToolsBrowser.updateCommandAvailability() at the moment
+ // because some tools aren't working.
+ if (!gDevToolsBrowser._old_updateCommandAvailability) {
+ gDevToolsBrowser._old_updateCommandAvailability = gDevToolsBrowser.updateCommandAvailability;
+ gDevToolsBrowser.updateCommandAvailability = this.updateCommandAvailability;
+ }
+ // Add Devtools menuitems, observers, and listeners
+ gDevToolsBrowser.registerBrowserWindow(window);
+ Services.prefs.addObserver(this.devtoolsThemePref, this);
+ this.updateDevtoolsThemeAttribute();
+ break;
+
+ case "unload":
+ window.removeEventListener("unload", gWebDeveloper);
+ gDevToolsBrowser.forgetBrowserWindow(window);
+ Services.prefs.removeObserver(this.devtoolsThemePref, this);
+
+ var desc = Object.getOwnPropertyDescriptor(window, "DeveloperToolbar");
+ if (desc && !desc.get)
+ DeveloperToolbar.destroy();
+ break;
+
+ case "popupshowing":
+ this.initMenuItems();
+ this.updateCommandAvailability(window);
+ break;
+ }
+ },
+
+ initMenuItems: function() {
+ var menuitem = document.getElementById("validatePage");
+ var uri = getBrowser().currentURI;
+ if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
+ menuitem.removeAttribute("disabled");
+ else
+ menuitem.setAttribute("disabled", true);
+
+ var enabled = Services.prefs
+ .getBoolPref("devtools.debugger.remote-enabled");
+ document.getElementById("devtoolsDebugger")
+ .setAttribute("checked", enabled);
+ },
+
+ devtoolsThemePref: "devtools.theme",
+
+ observe: function (subject, topic, data) {
+ if (topic == "nsPref:changed" && data == this.devtoolsThemePref) {
+ this.updateDevtoolsThemeAttribute();
+ }
+ },
+
+ updateDevtoolsThemeAttribute: function() {
+ // Set an attribute on root element to make it possible
+ // to change colors based on the selected devtools theme.
+ var devtoolsTheme = Services.prefs.getCharPref(this.devtoolsThemePref);
+ // Bug 1096469 - Make devedition theme work with custom devtools themes.
+ if (devtoolsTheme != "dark")
+ devtoolsTheme = "light";
+
+ document.documentElement.setAttribute("devtoolstheme", devtoolsTheme);
+ // document.getElementById("developer-toolbar").setAttribute("devtoolstheme", devtoolsTheme);
+ },
+
+ updateCommandAvailability: function(win) {
+ var doc = win.document;
+
+ function toggleCmd(id, isEnabled) {
+ var cmd = doc.getElementById(id);
+ if (isEnabled) {
+ cmd.removeAttribute("disabled");
+ cmd.removeAttribute("hidden");
+ } else {
+ cmd.setAttribute("disabled", "true");
+ cmd.setAttribute("hidden", "true");
+ }
+ };
+
+ // Enable developer toolbar?
+ var devToolbarEnabled = Services.prefs.getBoolPref("devtools.toolbar.enabled");
+ toggleCmd("Tools:DevToolbar", devToolbarEnabled);
+ var focusEl = doc.getElementById("Tools:DevToolbarFocus");
+ if (devToolbarEnabled)
+ focusEl.removeAttribute("disabled");
+ else
+ focusEl.setAttribute("disabled", "true");
+
+ if (devToolbarEnabled && Services.prefs.getBoolPref("devtools.toolbar.visible"))
+ win.DeveloperToolbar.show(false).catch(console.error);
+
+ // Enable Browser Toolbox?
+ var chromeEnabled = Services.prefs.getBoolPref("devtools.chrome.enabled");
+ var devtoolsRemoteEnabled = Services.prefs.getBoolPref("devtools.debugger.remote-enabled");
+ var remoteEnabled = chromeEnabled && devtoolsRemoteEnabled;
+ toggleCmd("Tools:BrowserToolbox", remoteEnabled);
+ // Currently "gMultiProcessBrowser" is always falsey.
+ //toggleCmd("Tools:BrowserContentToolbox", remoteEnabled && win.gMultiProcessBrowser);
+ toggleCmd("Tools:BrowserContentToolbox", false);
+
+ // Enable DevTools connection screen, if the preference allows this.
+ toggleCmd("Tools:DevToolsConnect", devtoolsRemoteEnabled);
+ },
+}
+
+window.addEventListener("load", gWebDeveloper);
diff --git a/comm/suite/browser/webDeveloperOverlay.xul b/comm/suite/browser/webDeveloperOverlay.xul
new file mode 100644
index 0000000000..09780ee6f0
--- /dev/null
+++ b/comm/suite/browser/webDeveloperOverlay.xul
@@ -0,0 +1,156 @@
+<?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://devtools/skin/devtools-browser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://navigator/skin/webDeveloper.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://navigator/locale/webDeveloper.dtd">
+
+<overlay id="webDeveloperOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://navigator/content/webDeveloperOverlay.js"/>
+
+ <menupopup id="toolsPopup">
+ <menuitem id="validatePage"
+ label="&validatePage.label;"
+ accesskey="&validatePage.accesskey;"
+ oncommand="gWebDeveloper.validateThisPage(); event.stopPropagation();"/>
+ <menuseparator id="devToolsStartSeparator"/>
+ <menuitem id="menu_devToolbox"
+ observes="devtoolsMenuBroadcaster_DevToolbox"
+ label="&devToolboxMenuItem.label;"
+ accesskey="&devToolboxMenuItem.accesskey;"/>
+ <menuseparator id="menu_devtools_separator"/>
+ <menuitem id="menu_devToolbar"
+ observes="devtoolsMenuBroadcaster_DevToolbar"
+ type="checkbox" autocheck="false"
+ label="&devToolbarMenu.label;"
+ accesskey="&devToolbarMenu.accesskey;"/>
+ <menuitem id="menu_browserToolbox"
+ observes="devtoolsMenuBroadcaster_BrowserToolbox"
+ label="&browserToolboxMenu.label;"
+ accesskey="&browserToolboxMenu.accesskey;"/>
+ <menuitem id="menu_browserContentToolbox"
+ observes="devtoolsMenuBroadcaster_BrowserContentToolbox"
+ label="&browserContentToolboxMenu.label;"
+ accesskey="&browserContentToolboxMenu.accesskey;" />
+ <menuitem id="menu_browserConsole"
+ observes="devtoolsMenuBroadcaster_BrowserConsole"
+ label="&browserConsoleCmd.label;"
+ accesskey="&browserConsoleCmd.accesskey;"/>
+ <menuitem id="menu_responsiveUI"
+ observes="devtoolsMenuBroadcaster_ResponsiveUI"
+ type="checkbox" autocheck="false"
+ label="&responsiveDesignTool.label;"
+ accesskey="&responsiveDesignTool.accesskey;"/>
+ <menuitem id="menu_eyedropper"
+ observes="devtoolsMenuBroadcaster_Eyedropper"
+ type="checkbox" autocheck="false"
+ label="&eyedropper.label;"
+ accesskey="&eyedropper.accesskey;"/>
+ <menuitem id="menu_scratchpad"
+ observes="devtoolsMenuBroadcaster_Scratchpad"
+ label="&scratchpad.label;"
+ accesskey="&scratchpad.accesskey;"/>
+ <menuitem id="devtoolsDebugger"
+ type="checkbox"
+ label="&allowRemoteDebugging.label;"
+ accesskey="&allowRemoteDebugging.accesskey;"
+ oncommand="gWebDeveloper.enableDebugger(this);"/>
+ <menuitem id="menu_devtools_connect"
+ observes="devtoolsMenuBroadcaster_connect"
+ label="&devtoolsConnect.label;"
+ accesskey="&devtoolsConnect.accesskey;"/>
+ <menuseparator id="devToolsEndSeparator"/>
+ <menuitem id="getMoreDevtools"
+ observes="devtoolsMenuBroadcaster_GetMoreTools"
+ label="&getMoreDevtoolsCmd.label;"
+ accesskey="&getMoreDevtoolsCmd.accesskey;"/>
+ </menupopup>
+
+ <commandset id="mainCommandSet">
+ <command id="Tools:DevToolbox" oncommand="gDevToolsBrowser.toggleToolboxCommand(gBrowser);"/>
+ <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
+ <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
+ <command id="Tools:BrowserToolbox" oncommand="BrowserToolboxProcess.init();" disabled="true" hidden="true"/>
+ <command id="Tools:BrowserContentToolbox" oncommand="gDevToolsBrowser.openContentProcessToolbox();" disabled="true" hidden="true"/>
+ <command id="Tools:BrowserConsole" oncommand="HUDService.openBrowserConsoleOrFocus();"/>
+ <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
+ <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
+ <command id="Tools:Eyedropper" oncommand="openEyedropper();"/>
+ <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
+ </commandset>
+
+ <keyset id="mainKeyset">
+ <key id="key_devToolboxMenuItemF12" keycode="&devToolsCmd.keycode;" keytext="&devToolsCmd.keytext;"
+ command="Tools:DevToolbox"/>
+ <key id="key_browserConsole" key="&browserConsoleCmd.commandkey;" modifiers="accel,shift"
+ command="Tools:BrowserConsole"/>
+ <key id="key_browserToolbox" key="&browserToolboxCmd.commandkey;" modifiers="accel,alt,shift"
+ command="Tools:BrowserToolbox"/>
+ <key id="key_devToolbar" keycode="&devToolbar.keycode;" modifiers="shift" keytext="&devToolbar.keytext;"
+ command="Tools:DevToolbarFocus"/>
+ <key id="key_devToolboxMenuItem" keytext="&devToolboxMenuItem.keytext;" key="&devToolboxMenuItem.keytext;" modifiers="accel,shift"
+ command="Tools:DevToolbox"/>
+ <key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift" keytext="&scratchpad.keytext;"
+ command="Tools:Scratchpad"/>
+ </keyset>
+
+ <broadcasterset id="mainBroadcasterSet">
+ <!-- DevTools broadcasters -->
+ <broadcaster id="devtoolsMenuBroadcaster_DevToolbox"
+ type="checkbox" autocheck="false"
+ command="Tools:DevToolbox"
+ key="key_devToolboxMenuItem"/>
+ <broadcaster id="devtoolsMenuBroadcaster_DevToolbar"
+ command="Tools:DevToolbar"
+ key="key_devToolbar"/>
+ <broadcaster id="devtoolsMenuBroadcaster_BrowserToolbox"
+ key="key_browserToolbox"
+ command="Tools:BrowserToolbox"/>
+ <broadcaster id="devtoolsMenuBroadcaster_BrowserContentToolbox"
+ command="Tools:BrowserContentToolbox"/>
+ <broadcaster id="devtoolsMenuBroadcaster_BrowserConsole"
+ key="key_browserConsole"
+ command="Tools:BrowserConsole"/>
+ <broadcaster id="devtoolsMenuBroadcaster_Scratchpad"
+ command="Tools:Scratchpad"
+ key="key_scratchpad"/>
+ <broadcaster id="devtoolsMenuBroadcaster_ResponsiveUI"
+ command="Tools:ResponsiveUI"/>
+ <broadcaster id="devtoolsMenuBroadcaster_Eyedropper"
+ command="Tools:Eyedropper"/>
+ <broadcaster id="devtoolsMenuBroadcaster_PageSource"
+ key="key_viewSource"
+ command="View:PageSource"/>
+ <broadcaster id="devtoolsMenuBroadcaster_GetMoreTools"
+ oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
+ <broadcaster id="devtoolsMenuBroadcaster_connect"
+ command="Tools:DevToolsConnect"/>
+ </broadcasterset>
+
+ <window id="main-window">
+ <toolbar id="developer-toolbar" xpfe="false" hidden="true"
+ insertbefore="status-bar">
+ <observes element="main-window" attribute="devtoolstheme"/>
+ <stack class="gclitoolbar-stack-node" flex="1">
+ <textbox class="gclitoolbar-input-node" rows="1"/>
+ <hbox class="gclitoolbar-complete-node"/>
+ </stack>
+ <toolbarbutton id="developer-toolbar-toolbox-button"
+ class="developer-toolbar-button"
+ label="&devToolboxMenuItem.label;"
+ observes="devtoolsMenuBroadcaster_DevToolbox"
+ tooltiptext="&devToolbarToolsButton.tooltip;"
+ _defaultTooltipText="&devToolbarToolsButton.tooltip;"/>
+
+ <toolbarbutton id="developer-toolbar-closebutton"
+ class="close-icon"
+ oncommand="DeveloperToolbar.hide();"
+ tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
+ </toolbar>
+ </window>
+</overlay>
diff --git a/comm/suite/build.mk b/comm/suite/build.mk
new file mode 100644
index 0000000000..85fc6c9984
--- /dev/null
+++ b/comm/suite/build.mk
@@ -0,0 +1,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/.
+
+package:
+ @$(MAKE) -C comm/suite/installer
+
+package-compare:
+ @$(MAKE) -C comm/suite/installer package-compare
+
+install::
+ @$(MAKE) -C comm/suite/installer install
+
+source-package::
+ @$(MAKE) -C comm/suite/installer source-package
+
+upload::
+ @$(MAKE) -C comm/suite/installer upload
+
+source-upload::
+ @$(MAKE) -C comm/suite/installer source-upload
+
+# make -j1 because dependencies in l10n build targets don't work
+# with parallel builds
+merge-% installers-% langpack-% chrome-% clobber-%:
+ $(MAKE) -j1 -C comm/suite/locales $@
+
+# mochitests need to be run from the Mozilla build system
+ifdef ENABLE_TESTS
+# Backend is implemented in mozilla/testing/testsuite-targets.mk.
+# This part is copied from mozilla/browser/build.mk.
+
+mochitest-browser-chrome:
+ $(RUN_MOCHITEST) --browser-chrome
+ $(CHECK_TEST_ERROR)
+
+mochitest:: mochitest-browser-chrome
+
+.PHONY: mochitest-browser-chrome
+endif
diff --git a/comm/suite/chatzilla/ChangeLog b/comm/suite/chatzilla/ChangeLog
new file mode 100644
index 0000000000..ad095e6a0a
--- /dev/null
+++ b/comm/suite/chatzilla/ChangeLog
@@ -0,0 +1,432 @@
+#################################################################################
+# 12/03/01, ChatZilla 0.8.5, <rginda@netscape.com>
+
+Shipped with 0.9.7. Again, thanks to Samuel Sieb <samuel@sieb.net> for
+contributing many of the fixes (the most obvious of which is mIRC color code
+support), reviews, and driving the release candidate process. Thanks to Casey
+Hutchinson <nnooiissee@yahoo.com> for mac accelerator cleanup, help and edit
+menus, xul refactoring work, statusbar, and motivation. Thanks to
+KOIKE Kazuhiko, Katsuhiko Momoi, Shoji Matsumoto, and others who helped out in
+bug 41564 to get charsets working.
+
+* strict warning cleanup.
+* add input/output charset support. use /charset <charsetname> to
+ change the current charset, settings will be saved.
+* add "open at startup" item to view menus.
+* implement urls for /query views (isnick urls.)
+* make nicknames in output window isnick urls.
+* add mozilla statusbar.
+* add chatzilla icon to mozilla statusbar.
+* move "statusbar" to top of message window, rename to "header".
+* add mirc color code handling.
+* show long topics in multiple lines in header, instead of cropping.
+* add bugzilla munger rule, links "bug 12345" and "bug #12345" to appropriate
+ bugzilla bugs.
+* add channel munger rule to link #channelname to the channel.
+* add edit and help menus.
+* make /attach accept urls and url "fragments",
+ /attach moznet, /attach moznet/chatzilla, and /attach irc://moznet/chatzilla
+ are now all valid commands.
+* add prefs for when to make noise; new msg, msg in existing query view, and/or
+ /stalk matches.
+* allow for space separated list of sounds to play, like "beep beep", or
+ "file:/foo.wav"
+* fix bogus "connection refused" error when disconnecting.
+* add editable topic in header area.
+* implement server passowrds (they were way broke.)
+* add ctrl-tab and ctrl-shift-tab handlers to switch views.
+* fix tab complete behavior, so it works for words in the middle of the line.
+* add /about command to get version info.
+* add "collapse consecutive messages" option, useful for motifs with faces.
+* mac s/ctrl/meta/ fixes.
+* add default completions per view, as described in bug 63621.
+* add whowas help info.
+* format responses from WHO queries.
+* add network view maxlines pref.
+* only print "attampting to connect" message once per network.
+* add target in Kick item of message pane context menu.
+* remove timestamp tooltips, use status text instead.
+* show message source in status bar, after timestamp.
+* remove usage warning from /quit command.
+* change max connect attempts from 20 to 5.
+* remove unused listbox.js file from chatzilla.jar.
+* add help menu.
+* allow params in /ctcp commands.
+
+
+=================================================================================
+= Bugs fixed
+
+41564,63621,71549,86032,88438,91399,92080,95913,97288,98244,99612,100159,100704,101942,102377,103104,103598,104114,105343,105604,105881,105882,106349,107148,108061,110006,111546,111565
+
+http://bugzilla.mozilla.org/buglist.cgi?bug_id=41564%2C63621%2C71549%2C86032%2C88438%2C91399%2C92080%2C95913%2C97288%2C98244%2C99612%2C100159%2C100704%2C101942%2C102377%2C103104%2C103598%2C104114%2C105343%2C105604%2C105881%2C105882%2C106349%2C107148%2C108061%2C110006%2C111546%2C111565
+
+28 bugs found.
+ID Opened Summary
+63621 2000-12-22 [RFE] Shortcut to reply to the last IRC message
+98244 2001-09-04 Add "Leave this channel" to channel popup menu
+101942 2001-09-27 whowas command said unknown even though it exists
+92080 2001-07-24 Enter key to navigate menus also sends commmand
+105604 2001-10-18 chatzilla should have a status bar (and component bar)
+99612 2001-09-14 Chatzilla window id should not be "main-window"
+102377 2001-09-29 Chatzilla does not handle server passwords
+103104 2001-10-04 implement isnick urls
+103598 2001-10-07 JavaScript strict mode patch for Chatzilla
+91399 2001-07-18 Missing "screen buffer length" pref setting in prefs.js
+104114 2001-10-10 Can't localize some strings in Chatzilla.
+100704 2001-09-20 chatzilla needs an easy way to edit the topic
+105882 2001-10-21 Chatzilla keyboard shortcuts use wrong modifier key
+100159 2001-09-17 hiding a minimized userlist expands it instead
+105343 2001-10-17 chatzilla should have /about command
+106349 2001-10-23 [rfe]show a check beside selected motif in the view menu.
+107148 2001-10-27 chatzilla menus should move to an overlay
+105881 2001-10-21 `Edit' menu is missing in Chatzilla
+88438 2001-06-29 2xAlt-Tab will bring up unexpected "user-match"-message
+110006 2001-11-13 view/hide status bar is not saved
+71549 2001-03-09 [RFE] add mIRC and IrcII color code support
+86032 2001-06-14 [RFE] Ctrl+[shift]+tab and [shift]+F6 to switch channel view
+95913 2001-08-18 Include Chatzilla icon in Component Bar
+111565 2001-11-23 /query command no longer sends second argument
+111546 2001-11-22 User list doesn't repopulate correctly after rejoining chann
+108061 2001-11-01 /stalk won't stalk nicknames
+41564 2000-06-05 Internationalize ChatZilla to handle different language scri
+97288 2001-08-28 Chatzilla gets disconnected w/o indicating anything is wrong
+
+#################################################################################
+# 8/23/01, ChatZilla 0.8.4, <rginda@netscape.com>
+
+Shipped with Mozilla 0.9.4, 0.9.5, and 0.9.6, introduced motifs, channel and
+mailto links, and other things that were only availble in the 0.8.1a/b releases,
+which were not checked in. Thanks to Samuel Sieb <samuel@sieb.net> for
+contributing many of the fixes, and reviewing the rest.
+
+=================================================================================
+= Bugs fixed
+
+superbug: 103386
+
+41337,44447,69296,72272,72702,73652,73767,74408,79322,85573,89692,89717,92327,92403,95781,96567
+
+http://bugzilla.mozilla.org/buglist.cgi?bugidtype=include&bug_id=41337%2C44447%2C69296%2C72272%2C72702%2C73652%2C73767%2C74408%2C79322%2C85573%2C89692%2C89717%2C92327%2C92403%2C95781%2C96567
+
+SuperBug: http://bugzilla.mozilla.org/show_bug.cgi?id=89713
+
+16 bugs found.
+ID Opened Summary
+92403 2001-07-26 mozilla -help displays >>>>>>>>>>>> chatzilla-service loaded
+96567 2001-08-22 long nicks cause extra-high lines
+95781 2001-08-17 Chatzilla text is black-on-black
+74408 2001-04-02 Typing in Chatzilla should give Textbox at bottom focus
+ automatically.
+89692 2001-07-06 /away command only uses first word for reason
+85573 2001-06-12 [RFE] support for mailto:
+72272 2001-03-16 irc urls don't detect server/network names correctly
+72702 2001-03-20 need UI for adding irc:// bookmarks
+89717 2001-07-06 header text not visible for op and voice
+73652 2001-03-27 Missing support for !-Channels
+41337 2000-06-02 /join should join the current window if it is a channel and you
+ are not currently in it
+79322 2001-05-07 [RFE] Make chatzilla user scriptable
+73767 2001-03-28 Confusing behaviour if nick already in use
+44447 2000-07-02 no ctcp ping results
+69296 2001-02-18 irc URL with # doesn't work
+92327 2001-07-25 [RFE] Have ops' names in red
+
+#################################################################################
+# 07/01/01, ChatZilla 0.8.3, <rginda@netscape.com>
+
+Revision 0.8.3 removed hardcoded strings, in favor of the Mozilla i18n/l10n api.
+RTL languages may still have some issues. Many thanks to Chiaki Koufugata
+<chiaki@mozilla.gr.jp> for doing the heavy lifting.
+
+=================================================================================
+= Bugs fixed
+
+http://bugzilla.mozilla.org/show_bug.cgi?id=27805
+Bug 27805, "ChatZilla needs i18n"
+
+#################################################################################
+# 06/03/01, ChatZilla 0.8.2, <rginda@netscape.com>
+
+=================================================================================
+= Bugs fixed
+79311, 75226, 80993
+
+=================================================================================
+= Changes by file name...
+chatzilla-service.js:
+- Add stub allowPort method.
+- Fix signature for handleContent method, bug 80993
+
+connection-xpcom.js:
+- factor chatzilla specific code out of this file. Callbacks into chatzilla specific code are now used, making this file more generic.
+- according to darinf (the current necko guy), using openOutputStream with asyncRead is a bad thing. Most of the changes in this file involve migrating from usage of openOutputStream to asyncWrite.
+- Changes also include fixing the function declaration syntax to match the rest of the code (two lines, named functions.)
+
+irc-debug.js:
+- check nextLine before using it.
+
+irc.js:
+- changes to work with new socket interface.
+- correct isOp detection in setTopic
+- remove checks for undefined exceptions
+- route data-available immediately. inserting a data-available event to be routed later caused disconnect events to be received out of order.
+
+utils.js:
+- fix HAS_XPCOM test (XPCDOM broke it.)
+- add jsenv.HAS_NSPR_EVENTQ
+
+mybot.js:
+- not built -
+- add dummy escape/unescape if it isn't there
+
+handlers.js:
+- return false the first time through onClose(), and disconnect from all servers. This makes sure we keep the window around long enough to send the QUIT messages.
+- close window if client.userClose is set and we disconnected from the last server.
+
+chatzilla.xul:
+- hook up onclose event.
+- apply patch from 75226, fixes initial splitter position.
+
+static.js:
+- copy client.userAgent code from chatzilla 0.8.1x
+- implement getConnectionCount()
+
+#################################################################################
+# ChatZilla 0.8.1a and 0.8.1b released only in XPI format, changes to be merged
+# in later.
+#################################################################################
+
+#################################################################################
+# 03/08/01, ChatZilla 0.8, <rginda@netscape.com>
+
+=================================================================================
+= Bugs fixed
+
+The superbug for ChatZilla 0.8 is 71468, which fixes the following bugs:
+22890, 40959, 41798, 42905, 43850, 44077, 54143, 56312, 59036, 59374, 65633, 65634, 65861, 66040, 71310, and 71378.
+
+Also, a workaround for bug 70335 is included, and bug 45576 *may* be fixed, we'll have to wait and see
+
+Link to superbug: http://bugzilla.mozilla.org/show_bug.cgi?id=71468
+Link to list of bugs fixed: http://bugzilla.mozilla.org/buglist.cgi?bug_id=22890%2C+40959%2C+41798%2C+42905%2C+43850%2C+44077%2C+54143%2C+56312%2C+59036%2C+59374%2C+65633%2C+65634%2C+65861%2C+66040%2C+71310%2C+71378
+
+=================================================================================
+= Changes by file name...
+
+These changes are listed as they appear, when reading a diff between what's in cvs now, and what's in my local tree. Before checking in, I'll tag the current chatzilla code as chatzilla0.7, and after, chatzilla0.8. Just diff those two tags to get something to look at alongside this ChangeLog. Or see the superbug for ChatZilla 0.8, 71468, which has a copy of the diff attached to it.
+
+---------------------------------------------------------------------------------
+- NEW FILES:
+
+outputwindow.html:
+- used as the base html for the output window, allowed bug 41798, bug 54143, and bug 59374 to be fixed.
+
+ChangeLog:
+- this file, tracks changes between ChatZilla versions. I added the comments from the previous large checkin (retro-branded as ChatZilla 0.7) as well.
+
+TODO:
+- List of things to do in the future.
+
+---------------------------------------------------------------------------------
+- MODIFIED FILES:
+
+README:
+- Started user guide, only the TOC at the moment.
+
+connection-xpcom.js:
+- Don't call close on output streams, it throws a NS_ERROR_NOT_IMPLEMENTED. dougt says just let the refcount go to 0 and it'll be fine.
+- Tell the IRCServer object it has lost it's connection when the stream gets an onStopRequest (bug 22890 and 42905)
+
+events.js:
+- dump exception on error (to get the filename and linenumber info, if it's there)
+
+irc-debug.js:
+- show data for new "info" type of event.
+
+irc.js:
+- add a servers collection to the IRCNetwork. I'm not exactly sure why I didn't do this from the beginning. It allows us to recover and reuse server objects in the event of a disconnect/reconnect. (bug 22890 and 66040)
+- set default number of reconnect attempts to something more tolerable. (bug 22890 and 66040)
+- move connection attempt/ next host to attempt information from the event object onto the actual network object. The new reconnect logic needs it in places where the event is not available. (bug 22890 and 66040)
+- spew out info event when max connect attempts are exhausted. (bug 22890 and 66040)
+- spew out info event when a connection attempt starts. (bug 22890 and 66040)
+- add try/catch around server creation, to be safe.
+- spew info event when moving on to the next connection attempt. (bug 22890 and 66040)
+- modify CIRCServer connection to check for a dupe server in it's parent.servers collection, re-init/return that object if it exists, otherwise make a new one.
+- try to reconnect if the connection was broken before we got a 001 message from the server, regardless of whether ot not the parent IRCNetwork has it's "stayingPower" flag set.
+- forward the disconnect event from the server to the network, to keep the network properly informed.
+- if an unknown message arrives at the server, send it to the server's onUnknown handler if it exists. If the server has no onUnknown handler, send it to the parent network's handler for that type of message, if it has one. If the parent network has no handler for this message, send it to the network's onUnknown handler as a last ditch. (the event code will fail silently if the network has no onUnknown handler.)
+- when we get an 001, reset connection attempt cound, and record the successful connection.
+- if the channel's users collection is considered "stable" when a new names list begins to arrive, mark it as unstable before wiping it out. the list will be marked stable again when the end of names (code 366) message arrives.
+- refer to this.foo instead of e.server.foo. they are the same object, but this.foo is one less lookup. this change should happen to the rest of this file eventually.
+- Tell the network when the user changes their nick. This is so nick changes that occur when you're not on any channel don't go unnoticed.
+- round off excess decimals in the server lag.
+- add opCount and voiceCount vars to the IRCChannel during a channel.getUsersCount() call.
+
+utils.js:
+- make keys() return an array instead of a string. this function was not called by anything before this change.
+- add formatDateOffset() function to the result of a Date subtraction into english.
+- add arraySpeak() function to join an array into an english list.
+- add arrayContains() function to search an array for an element.
+- add arrayIndexOf() function to get the index of an element within the array.
+- modify hyphenateWord() function to search for a goo place to break the word with a -/+ 5 character fuzz factor.
+- add splitLongWord() function, similar to hyphenateWord, except returns an array.
+- fix stoopid paren bug in roundTo() function.
+
+chatzilla.xul:
+- add broadcasters for the file menu items.
+- add keys for the file menu items.
+- add file menu. (bug 43850)
+- move options menu under file menu.
+- remove old view toolbar.
+- add crop="right" attribute to the userlist table to prevent horizontal scrollbars. (bug 56312)
+- whitespace fixes.
+- add input splitter for multiline-input mode.
+- add multiline-input control for multiline-input mode.
+- add crop="right" to statusbar elements (bug 59036 maybe 45576)
+
+commands.js:
+- alphabetize commands.
+- add client, cancel, disconnect, infobar, list, networks, notify, query, and status commands. (bug 44077)
+
+handlers.js:
+- don't call setOutputStyle() onLoad (it's been deleted.)
+- we can now call setClientOutput() and initStatic() onLoad, because the output window is a separate .html file. This means the the xul onLoad will not be called until the .html file is loaded, and so we can be sure that the content model for the output window will be stable.
+- global change from 'quotes like this' to ``quotes like this''.
+- set client.debugMode variable when debug messages are on. This flag is used to determine how we display irc message codes.
+- remove onToggleToolbarIcons() because we don't have a toolbar anymore.
+- don't allow the last view to be deleted.
+- move to new sort-service, but leave the old one in for now. This keeps us from breaking 0.8 users.
+- add keyUp handler for the multiline-input control.
+- clear the input control's value in the singleline input control's keyUp handler, instead of onInputCompleteLine(), because we don't want to clear the multiline control.
+- switch to multiline mode if the singleline control hears a ctl+up.
+- don't spew an error message if autocomplete can't locate a match on [tab]
+- remove old (and broken) multiline code.
+- add onNotifyTimeout() handler to take care of sending ISON messages. ISON is used to ask the server if certain users are on, used for the /notify command.
+- add onInputCancel() handler (for the /cancel command) to cancel and pending /attach command.
+- send unknown / commands directly to the server, (after warning the user that the command was unknown.)
+- add onInputSimpleCommand(). Any / commands that can be sent directly to the server can use this function, instead of creating a new one for every "simple" command.
+- add onInputStatus() (for the /status command.) Prints connection status, including connection duration, lag, and channels joined.
+- revamp onInputTestdisplay(). It now tests all types of messages, and all munger rules, from multiple sources, to multiple targets. Much more useful than it used to be.
+- add onInputNetworks() (for /networks.) Lists all networks as a set of irc:// links.
+- show an error message if you attempt to /attach to a network you are already attached to.
+- fail a /delete if the user provided a parameter. /delete deletes the *current* view, failing if a param is given prevents accidental deletion.
+- Change onInputNames() from a function that toggles the infobar (userlist) visibility to a function that sends a NAMES command to the server.
+- add onInputInfobar() to do what onInputName() used to do.
+- add extra help message to onInputCommands().
+- add status messages to onInputAttach().
+- fix onInputMe() so it works on /query views as well as channels.
+- add onInputQuery() function (for /query.)
+- fix onInputMsg() so it does not force a new query window to open.
+- change parameter of temporary doEval() function form s to __s, so as not to interfere with eval'd expressions.
+- split EVAL message types into EVAL-IN and EVAL-OUT, so you can see was was eval()d, in the event of an error.
+- change onInputTopic() from a function that would consult the irc library for topic information to a function send asks the server to update the topic information in the irc library. the code that handles TOPIC replies now tales care of printing this information out.
+- add onInputNotify() function (for /notify.)
+- add onInfo handler to networks.
+- add onUnknown handler to networks.
+- add 005 to the list of initial server messages that are *always* displayed on the network view.
+- force handlers attached to the my_showtonet() function to display on the network view (ignoring the client.SLOPPY_NETWORKS setting.)
+- allow NOTICE messages to be affected by client.SPLOPPY_NETWORKS.
+- add an on303 (ISON reply) handler to networks.
+- add an on322 (LIST reply) handler to networks.
+- use the new arraySpeak() and formatDateOffset() functions when printing WHOIS information.
+- print error message when a network gets disconnected. (bug 42905)
+- show nick change information on the network if it is the current view.
+- display "end of NAMES" message if the channel's pendingNamesReply flag is set, then clear pendingNamesReply. channel.pendingNamesReply is set by the /names command (and cleared in on366) to control whether or not NAMES information (353 and 366 messages) should actually be displayed. (the irc library will request this information when a new channels is joined, and we don't want to display it in that case.) (bug 40959)
+- print topic information on a 332.
+- print topic setter information on a 333.
+- print NAMES information on 353 if the channel's pendingNamesReply flag is set. (bug 40959)
+- global fixes to .display callsites. new syntax is obj.display (<msg>, <type>, <source>, <dest>);
+- beep when a message from a user is received.
+
+readprefs.js:
+- remove toolbar.icons pref, we don't have a toolbar anymore.
+- set client.debugMode based on debug messages pref.
+
+static.js:
+- removed some unused client.PREFERENCE type variables.
+- added client.SLOPPY_NETWORKS switch (see comment.)
+- added client.HIDE_CODES switch (see comment.)
+- added a message code -> display glyph map for use when client.HIDE_CODES is true.
+- remove the isPermanent flag from the client view, allows users to delete the client view.
+- set client.IGNORE_MOTD to false.
+- bump MAX_MESSAGES up a few notches for various view types.
+- set onresize handler for the window. (bug 41798)
+- create an nsISound in the client object, so we can beep for /msgs.
+- hook up multiline-input control keyUp handler.
+- remove icons-in-toolbar related code.
+- remove network list from the hello message, but...
+- call onInputNetworks() during init.
+- start the notify interval.
+- global change from 'quotes like this' to ``quotes like this''.
+- remove dead servers from the efnet server list.
+- add opennet network. (bug 65633)
+- removed the "you-talking-to-me" munger rule, this is now done in display().
+- made bold, underline, teletype, italic regexps more better.
+- remove matchMyNick function, this is now done in display().
+- add a "chatzilla-link" class to all links.
+- style rheet matches as chatzilla-rheet and chatzilla-link.
+- keep smileys that start with the number 8 from matching. (bug 65861)
+- fix insertHyphenatedWord() to use splitLongWord() and empty <img> tags, instead of using hyphenateWord() to insert spaces in long words. the empty <img> lets the layout engine break long words, without adding spaces.
+- added skin switching voodoo to mainStep, it's a dumb ass hack, read the comment. (bug 59374)
+- removed setOutputStyle() function, we can't do this now that we load the basic output document as a .html file.
+- simplified setClientOutput() function. all the extra work is not needed now that we load the basic output document as a .html file.
+- global fixes to .display callsites. new syntax is obj.display (<msg>, <type>, <source>, <dest>);
+- added multilineLineInputMode() function, to set the input mode state.
+- added stringToMsg() function. takes a string and converts it into munged DOM nodes ready to be added to the message window.
+- set the "selected" attribute on the current view tab button.
+- add scrollDown() function, it's now used from addHistory and onresize.
+- put a <tbody> in the <table> before adding content. (bug 70335)
+- create <xul:tab>s instead of <xul:menubutton>s for the views.
+- add client.load() function to load js subscripts (won't work until bug 48974 is fixed.)
+- combined client, network, channel, and user.display() implementations into a single function.
+
+munger.js:
+- rename third parameter to .munge() method.
+- break long words found inside text that matched other munger rules.
+
+chatzilla.css:
+- remove unused ids and classes.
+- made the status data color darker. (bug 65634)
+
+output-default.css:
+- removed unused classes.
+- added huge comment (read it.)
+- changed color scheme.
+
+face-*.gif:
+- made the smiley faces smaller so they fit better with the small font.
+
+#################################################################################
+# 10/23/00, ChatZilla 0.7, <rginda@netscape.com>
+
+=================================================================================
+= Bugs fixed
+
+The superbug for ChatZilla 0.7 is 57633, which fixes the following bugs:
+40636, 41343, 51352, 54145, 56708, 57104, and 57138
+
+Link to superbug: http://bugzilla.mozilla.org/show_bug.cgi?id=57633
+List of bugs dependent on the superbug: http://bugzilla.mozilla.org/buglist.cgi?field0-0-0=dependson&type0-0-0=equals&value0-0-0=57633&cmdtype=doit
+
+=================================================================================
+= Changes ...
+
+= fixed 41343, tab complete still works after part.
+= fixed 56708, link to rheet.wav when someone says rheet.
+= fixed 40636, delete text when simleys are matched
+= fixed 57138, toolbars not collapsing correctly.
+= fixed 57104, link regex was kinda lame.
+= fixed 54145, link completion char should be a pref.
+= fixed 51352, chatzilla should remember your nick.
+= cleaned up strict mode warnings.
+= adjusted menu layout to make it easier to grok (i hope.)
+= added "save settings now" and "save settings on exit" menu items.
+= fixed invalid xul tags (<label/>) to avoid nasty warnings.
+= implemented save settings functions.
+= added prefs for default network, startup urls, nickname autocomplete suffix,
+= delete view after part, save settings on exit, stalk list, smiley text deletion.
+= removed pref fot startup network/channel (replaced by startup urls.)
+= added graphic for scc's ear emoticon (*
+= made ui state (toolbar, userlist, statusbar visiblity) persist.
+
diff --git a/comm/suite/chatzilla/DYK b/comm/suite/chatzilla/DYK
new file mode 100644
index 0000000000..eb96dc228f
--- /dev/null
+++ b/comm/suite/chatzilla/DYK
@@ -0,0 +1,97 @@
+
+
+ ChatZilla: Did You Know?
+
+
+
+
+
+The basics...
+
++ The networks listed in the *client* view on startup are clickable links.
+ Instead of typing /attach <network-name>, you can just click on one of the
+ links.
+
++ The view tabs are color coded, green means that something has happened in that
+ view since you last looked at it. Red means that someone has said something
+ to you.
+
++ ChatZilla will automatically highlight phrases that contain your nickname.
+
++ You can add to the list of words that cause messages to be highlighted with
+ the /stalk command. You can remove words from the list with the /unstalk
+ command.
+
++ You can quickly switch to another view using the function keys. F1 will
+ switch to the first view, F2 to the second, etc.
+
++ You can turn off the userlist, statusbar, and/or view tabs using the
+ File->Options menu.
+
++ You can see a list of commands by typing /commands.
+
++ You can get help on any command by typing /help <command-name>.
+
++ You can delete any view with the /delete command. Once deleted, the text from
+ that view will be lost. Deleteing a channel view is not the same as leaving
+ the channel. You will still be a member of the channel if you only /delete it.
+ It will reappear (without the previous contents) the next time activity occurs.
+
++ You can remove a channel or query view from the tab list without dropping its
+ contents. The /hide command will remove the view's tab, but keep the contents.
+ The next time something happens on that view, it will re-appear with the
+ previous text still intact.
+
++ You can recover deleted or hidden views. To recover a deleted *client* view,
+ use /client. To recover a network view use the /attach command, to recover
+ channel view use /join, and to recover a query view use /query.
+
+The cool commands...
+
++ /networks will display the list of networks as a list of clickable links.
+
++ /status will show your connection status. If you use /status from a channel,
+ you'll get information about the network and server you are connected to,
+ including connection duration, and last ping and lag (if available.) You will
+ also get status on the channel you are viewing, including number of operators,
+ number voiced, and topic.
+
+ Using /status from a network view will give you information on every channel
+ you are connected to.
+
+ Using /status from the *client* view will give you information on every network
+ you are connected to, as well as every channel on that network.
+
++ /disconnect can be used to disconnect from a network without quitting
+ ChatZilla. This can be useful if you are attached to more than one network
+ but want to leave only one of them.
+
+The more advanced stuff...
+
++ The input area can be switched to multiline mode by pressing [CTRL]+[UP].
+ Press [CTRL]+[ENTER] to sent the text to the server. In multiline mode,
+ the text you type not scanned for commands, so stray / characters will not
+ affect your post. To return to single line mode, press [CRTL]+[DOWN].
+
++ The place where messages appear can be styled to your own taste, if you
+ know a little css <http://www.htmlhelp.org/reference/css/>. See the comment
+ at the top of <http://lxr.mozilla.org/mozilla/source/extensions/irc/xul/skin/output-default.css>
+ for details.
+
++ You can manually bookmark an irc channel by creating a new irc:// bookmark.
+ From the "Manage bookmarks" window, create a new bookmark and enter
+ "irc://moznet/chatzilla", to bookmark the #chatzilla channel, for example.
+ You can also use a server name, as in "irc://irc.mcs.com/javascript".
+
++ irc:// urls work from normal web pages as well. You can link to your
+ irc channel from you home page. See
+ <http://www.w3.org/Addressing/draft-mirashi-url-irc-01.txt> for a description
+ of irc:// urls.
+
++ Turning off File->Options->Enable Smileys actually turns off all of the text
+ matching rules, including link detection, and bold/underline/italic. It will
+ also disable the part of ChatZilla that makes it possible to break long words
+ (ie, you'll end up with a horizontal scrollbar eventually.) This
+ option was added back when a few bugs in the JavaScript engine caused the
+ text matching rules to do Bad Things. You probably don't really want to turn
+ this off.
diff --git a/comm/suite/chatzilla/Makefile.in b/comm/suite/chatzilla/Makefile.in
new file mode 100644
index 0000000000..ca4424f50f
--- /dev/null
+++ b/comm/suite/chatzilla/Makefile.in
@@ -0,0 +1,6 @@
+# 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/.
+
+LOCALE_TOPDIR=$(commtopsrcdir)
+LOCALE_RELATIVEDIR=suite/chatzilla
diff --git a/comm/suite/chatzilla/README b/comm/suite/chatzilla/README
new file mode 100644
index 0000000000..9a809aae9a
--- /dev/null
+++ b/comm/suite/chatzilla/README
@@ -0,0 +1,70 @@
+ Using ChatZilla
+
+
+
+
+
+
+
+
+
+
+
+Table Of Contents
+
+Part 1: Introduction to ChatZilla.
+
+ 1.1: Introduction
+ 1.1.1: What is IRC?
+ 1.1.2: What is ChatZilla?
+ 1.1.3: Where do I get more information on IRC?
+
+ 1.2: The User Interface.
+ 1.2.1: Menu structure.
+ 1.2.2: User list.
+ 1.2.3: Output area.
+ 1.2.4: View tabs.
+ 1.2.5: Status bar.
+
+ 1.3: Features of the input area.
+ 1.3.1: Responding to the last person who spoke to you.
+ 1.3.2: Autocompleting nicknames.
+ 1.3.3: Autocompleting commands.
+ 1.3.4: Multiline input.
+
+Part 2: Navigating IRC with ChatZilla.
+
+ 2.1: Connecting to an IRC network.
+ 2.1.1: Listing available networks.
+ 2.1.2: Attaching to a network.
+ 2.1.3: Canceling an attach that isn't going well.
+ 2.1.4: Connecting to a specific server.
+ 2.1.5: Disconnecting when you're finished.
+
+ 2.2: Channels.
+ 2.2.1: Finding a channel.
+ 2.2.2: Joining.
+ 2.2.3: Modes, Topics, and Kicks.
+ 2.2.4: Parting.
+
+ 2.3: Private messages.
+ 2.3.1: Receiving messages.
+ 2.3.2: Sending messages.
+ 2.3.3: Query windows.
+
+Part 3: Scripting ChatZilla.
+
+ 3.1: Scripting basics.
+ 3.1.1: The /eval command.
+ 3.1.2: Writing text to the output window.
+ 3.1.3: Writing DOM nodes to the output window.
+ 3.1.1: Exploring objects with dumpObjectTree();
+
+ 3.2: More Scripting.
+ 3.2.1: External scripts.
+ 3.2.2: Hooking into IRC events.
+
+Appendix A:
+ A: Styling the output window.
+ (copy of the comment in output-window.css)
+
diff --git a/comm/suite/chatzilla/TODO b/comm/suite/chatzilla/TODO
new file mode 100644
index 0000000000..9ebedc7556
--- /dev/null
+++ b/comm/suite/chatzilla/TODO
@@ -0,0 +1,8 @@
+ChatZilla 0.8.1
+smarter [tab] code (tab on empty line responds to last /msg or yournick:)
+flubbed /msg detection.
+optional time stamps
+isnick irc:// urls
+
+ChatZilla 0.9
+Lots of prefs work, including a pref panel (finally!) \ No newline at end of file
diff --git a/comm/suite/chatzilla/jar.mn b/comm/suite/chatzilla/jar.mn
new file mode 100644
index 0000000000..a9ae89fa42
--- /dev/null
+++ b/comm/suite/chatzilla/jar.mn
@@ -0,0 +1,103 @@
+# 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/.
+
+chatzilla.jar:
+% content chatzilla %content/chatzilla/
+% skin chatzilla modern/1.0 %skin/modern/chatzilla/
+% overlay chrome://navigator/content/navigator.xul chrome://chatzilla/content/browserOverlay.xul
+% overlay chrome://communicator/content/tasksOverlay.xul chrome://chatzilla/content/chatzillaOverlay.xul
+% overlay chrome://communicator/content/pref/preferences.xul chrome://chatzilla/content/prefsOverlay.xul
+% overlay chrome://communicator/content/pref/pref-appearance.xul chrome://chatzilla/content/prefsOverlay.xul
+% overlay chrome://chatzilla/content/chatzilla.xul chrome://communicator/content/utilityOverlay.xul
+% overlay chrome://chatzilla/content/menus.xul chrome://communicator/content/tasksOverlay.xul
+% style chrome://communicator/content/customizeToolbar.xul chrome://chatzilla/skin/browserOverlay.css
+ content/chatzilla/lib/js/utils.js (js/lib/utils.js)
+ content/chatzilla/lib/js/events.js (js/lib/events.js)
+ content/chatzilla/lib/js/connection-xpcom.js (js/lib/connection-xpcom.js)
+ content/chatzilla/lib/js/command-manager.js (js/lib/command-manager.js)
+ content/chatzilla/lib/js/pref-manager.js (js/lib/pref-manager.js)
+ content/chatzilla/lib/js/message-manager.js (js/lib/message-manager.js)
+ content/chatzilla/lib/js/menu-manager.js (js/lib/menu-manager.js)
+ content/chatzilla/lib/js/ident.js (js/lib/ident.js)
+ content/chatzilla/lib/js/irc.js (js/lib/irc.js)
+ content/chatzilla/lib/js/irc-debug.js (js/lib/irc-debug.js)
+ content/chatzilla/lib/js/file-utils.js (js/lib/file-utils.js)
+ content/chatzilla/lib/js/dcc.js (js/lib/dcc.js)
+ content/chatzilla/lib/js/json-serializer.js (js/lib/json-serializer.js)
+ content/chatzilla/lib/js/sts.js (js/lib/sts.js)
+ content/chatzilla/lib/js/text-serializer.js (js/lib/text-serializer.js)
+ content/chatzilla/lib/js/text-logger.js (js/lib/text-logger.js)
+ content/chatzilla/lib/js/chatzilla-protocol-script.js (js/lib/chatzilla-protocol-script.js)
+ content/chatzilla/lib/js/protocol-handlers.jsm (js/lib/protocol-handlers.jsm)
+ content/chatzilla/lib/xul/munger.js (xul/lib/munger.js)
+ content/chatzilla/lib/xul/tree-utils.js (xul/lib/tree-utils.js)
+ content/chatzilla/chatzilla.xul (xul/content/chatzilla.xul)
+ content/chatzilla/scripts.xul (xul/content/scripts.xul)
+ content/chatzilla/menus.xul (xul/content/menus.xul)
+ content/chatzilla/popups.xul (xul/content/popups.xul)
+ content/chatzilla/channels.xul (xul/content/channels.xul)
+ content/chatzilla/channels.js (xul/content/channels.js)
+ content/chatzilla/output-window.html (xul/content/output-window.html)
+ content/chatzilla/output-window.js (xul/content/output-window.js)
+ content/chatzilla/commands.js (xul/content/commands.js)
+ content/chatzilla/handlers.js (xul/content/handlers.js)
+ content/chatzilla/prefs.js (xul/content/prefs.js)
+ content/chatzilla/messages.js (xul/content/messages.js)
+ content/chatzilla/menus.js (xul/content/menus.js)
+ content/chatzilla/mungers.js (xul/content/mungers.js)
+* content/chatzilla/static.js (xul/content/static.js)
+ content/chatzilla/networks.js (xul/content/networks.js)
+ content/chatzilla/networks-edit.css (xul/content/networks-edit.css)
+ content/chatzilla/networks-edit.js (xul/content/networks-edit.js)
+ content/chatzilla/networks-edit.xul (xul/content/networks-edit.xul)
+ content/chatzilla/networks-server.js (xul/content/networks-server.js)
+ content/chatzilla/networks-server.xul (xul/content/networks-server.xul)
+ content/chatzilla/dynamic.css (xul/content/dynamic.css)
+ content/chatzilla/output-base.css (xul/content/output-base.css)
+ content/chatzilla/chatzillaOverlay.xul (xul/content/chatzillaOverlay.xul)
+ content/chatzilla/chatzillaOverlay.js (xul/content/chatzillaOverlay.js)
+ content/chatzilla/browserOverlay.xul (xul/content/browserOverlay.xul)
+ content/chatzilla/prefsOverlay.xul (xul/content/prefsOverlay.xul)
+ content/chatzilla/pref-irc-toolkit.xul (xul/content/pref-irc-toolkit.xul)
+ content/chatzilla/config.xul (xul/content/config.xul)
+ content/chatzilla/config-add.xul (xul/content/config-add.xul)
+ content/chatzilla/config.js (xul/content/config.js)
+ content/chatzilla/config-add.js (xul/content/config-add.js)
+ content/chatzilla/config.css (xul/content/config.css)
+ content/chatzilla/install-plugin/install-plugin.js (xul/content/install-plugin/install-plugin.js)
+ content/chatzilla/install-plugin/install-plugin.xul (xul/content/install-plugin/install-plugin.xul)
+ content/chatzilla/about/about.xul (xul/content/about/about.xul)
+ content/chatzilla/about/about.js (xul/content/about/about.js)
+ skin/modern/chatzilla/chatzilla.css (xul/skin/chatzilla.css)
+ skin/modern/chatzilla/chatzillaOverlay.css (xul/skin/chatzillaOverlay.css)
+ skin/modern/chatzilla/browserOverlay.css (xul/skin/browserOverlay.css)
+ skin/modern/chatzilla/channels.css (xul/skin/channels.css)
+ skin/modern/chatzilla/install-plugin.css (xul/skin/install-plugin.css)
+ skin/modern/chatzilla/networks-edit.css (xul/skin/networks-edit.css)
+ skin/modern/chatzilla/about.css (xul/skin/about.css)
+ skin/modern/chatzilla/output-default.css (xul/skin/output-default.css)
+ skin/modern/chatzilla/output-light.css (xul/skin/output-light.css)
+ skin/modern/chatzilla/output-dark.css (xul/skin/output-dark.css)
+ skin/modern/chatzilla/images/logo.png (xul/skin/images/logo.png)
+ skin/modern/chatzilla/images/no-symbol.png (xul/skin/images/no-symbol.png)
+ skin/modern/chatzilla/images/no-graphic.png (xul/skin/images/no-graphic.png)
+ skin/modern/chatzilla/images/founder-symbol.png (xul/skin/images/founder-symbol.png)
+ skin/modern/chatzilla/images/founder-graphic.png (xul/skin/images/founder-graphic.png)
+ skin/modern/chatzilla/images/admin-symbol.png (xul/skin/images/admin-symbol.png)
+ skin/modern/chatzilla/images/admin-graphic.png (xul/skin/images/admin-graphic.png)
+ skin/modern/chatzilla/images/op-symbol.png (xul/skin/images/op-symbol.png)
+ skin/modern/chatzilla/images/op-graphic.png (xul/skin/images/op-graphic.png)
+ skin/modern/chatzilla/images/halfop-symbol.png (xul/skin/images/halfop-symbol.png)
+ skin/modern/chatzilla/images/halfop-graphic.png (xul/skin/images/halfop-graphic.png)
+ skin/modern/chatzilla/images/voice-symbol.png (xul/skin/images/voice-symbol.png)
+ skin/modern/chatzilla/images/voice-graphic.png (xul/skin/images/voice-graphic.png)
+ skin/modern/chatzilla/images/chatzilla-16.png (xul/skin/images/chatzilla-16.png)
+ skin/modern/chatzilla/images/multiline-expand.png (xul/skin/images/multiline-expand.png)
+ skin/modern/chatzilla/images/multiline-contract.png (xul/skin/images/multiline-contract.png)
+ skin/modern/chatzilla/images/input-send.png (xul/skin/images/input-send.png)
+ skin/modern/chatzilla/images/drop-indicator-bottom.png (xul/skin/images/drop-indicator-bottom.png)
+ skin/modern/chatzilla/images/logging-on.png (xul/skin/images/logging-on.png)
+ skin/modern/chatzilla/images/logging-off.png (xul/skin/images/logging-off.png)
+ skin/modern/chatzilla/images/spbubble-on.png (xul/skin/images/spbubble-on.png)
+ skin/modern/chatzilla/images/spbubble-off.png (xul/skin/images/spbubble-off.png)
diff --git a/comm/suite/chatzilla/js/lib/chatzilla-protocol-script.js b/comm/suite/chatzilla/js/lib/chatzilla-protocol-script.js
new file mode 100644
index 0000000000..e8cb72d259
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/chatzilla-protocol-script.js
@@ -0,0 +1,10 @@
+/* -*- Mode: C++; 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/. */
+
+let { ChatZillaProtocols } = ChromeUtils.import(
+ "chrome://chatzilla/content/lib/js/protocol-handlers.jsm"
+);
+
+ChatZillaProtocols.init();
diff --git a/comm/suite/chatzilla/js/lib/chatzilla-service.js b/comm/suite/chatzilla/js/lib/chatzilla-service.js
new file mode 100644
index 0000000000..8772f3a782
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/chatzilla-service.js
@@ -0,0 +1,311 @@
+/* -*- Mode: C++; 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/. */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+const MEDIATOR_CONTRACTID =
+ "@mozilla.org/appshell/window-mediator;1";
+const ASS_CONTRACTID =
+ "@mozilla.org/appshell/appShellService;1";
+const RDFS_CONTRACTID =
+ "@mozilla.org/rdf/rdf-service;1";
+const CATMAN_CONTRACTID =
+ "@mozilla.org/categorymanager;1";
+const PPMM_CONTRACTID =
+ "@mozilla.org/parentprocessmessagemanager;1";
+
+const CLINE_SERVICE_CONTRACTID =
+ "@mozilla.org/commandlinehandler/general-startup;1?type=chat";
+const CLINE_SERVICE_CID =
+ Components.ID("{38a95514-1dd2-11b2-97e7-9da958640f2c}");
+const STARTUP_CID =
+ Components.ID("{ae6ad015-433b-42ab-9afc-1636af5a7fc4}");
+
+
+var {
+ ChatZillaProtocols,
+ IRCProtocolHandlerFactory,
+ IRCSProtocolHandlerFactory,
+ IRCPROT_HANDLER_CID,
+ IRCSPROT_HANDLER_CID,
+} = ChromeUtils.import(
+ "chrome://chatzilla/content/lib/js/protocol-handlers.jsm"
+);
+
+function spawnChatZilla(uri, count)
+{
+ const wm = Cc[MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator);
+ const ass = Cc[ASS_CONTRACTID].getService(Ci.nsIAppShellService);
+ const hiddenWin = ass.hiddenDOMWindow;
+
+ // Ok, not starting currently, so check if we've got existing windows.
+ const w = wm.getMostRecentWindow("irc:chatzilla");
+
+ // Claiming that a ChatZilla window is loading.
+ if ("ChatZillaStarting" in hiddenWin)
+ {
+ dump("cz-service: ChatZilla claiming to be starting.\n");
+ if (w && ("client" in w) && ("initialized" in w.client) &&
+ w.client.initialized)
+ {
+ dump("cz-service: It lied. It's finished starting.\n");
+ // It's actually loaded ok.
+ delete hiddenWin.ChatZillaStarting;
+ }
+ }
+
+ if ("ChatZillaStarting" in hiddenWin)
+ {
+ count = count || 0;
+
+ if ((new Date() - hiddenWin.ChatZillaStarting) > 10000)
+ {
+ dump("cz-service: Continuing to be unable to talk to existing window!\n");
+ }
+ else
+ {
+ // We have a ChatZilla window, but we're still loading.
+ hiddenWin.setTimeout(function wrapper(count) {
+ spawnChatZilla(uri, count + 1);
+ }, 250, count);
+ return true;
+ }
+ }
+
+ // We have a window.
+ if (w)
+ {
+ dump("cz-service: Existing, fully loaded window. Using.\n");
+ // Window is working and initialized ok. Use it.
+ w.focus();
+ if (uri)
+ w.gotoIRCURL(uri);
+ return true;
+ }
+
+ dump("cz-service: No windows, starting new one.\n");
+ // Ok, no available window, loading or otherwise, so start ChatZilla.
+ const args = new Object();
+ if (uri)
+ args.url = uri;
+
+ hiddenWin.ChatZillaStarting = new Date();
+ hiddenWin.openDialog("chrome://chatzilla/content/chatzilla.xul", "_blank",
+ "chrome,menubar,toolbar,status,resizable,dialog=no",
+ args);
+
+ return true;
+}
+
+
+function CommandLineService()
+{
+}
+
+CommandLineService.prototype =
+{
+ /* nsISupports */
+ QueryInterface(iid)
+ {
+ if (iid.equals(Ci.nsISupports))
+ return this;
+
+ if (Ci.nsICommandLineHandler && iid.equals(Ci.nsICommandLineHandler))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ /* nsICommandLineHandler */
+ handle(cmdLine)
+ {
+ var uri;
+ try
+ {
+ uri = cmdLine.handleFlagWithParam("chat", false);
+ }
+ catch (e)
+ {
+ }
+
+ if (uri || cmdLine.handleFlag("chat", false))
+ {
+ spawnChatZilla(uri || null)
+ cmdLine.preventDefault = true;
+ }
+ },
+
+ helpInfo: "-chat [<ircurl>] Start with an IRC chat client.\n",
+};
+
+
+/* factory for command line handler service (CommandLineService) */
+const CommandLineFactory =
+{
+ createInstance(outer, iid)
+ {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ return new CommandLineService().QueryInterface(iid);
+ },
+};
+
+
+function ProcessHandler()
+{
+}
+
+ProcessHandler.prototype =
+{
+ /* nsISupports */
+ QueryInterface(iid)
+ {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIObserver) ||
+ iid.equals(Ci.nsIMessageListener))
+ {
+ return this;
+ }
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ /* nsIObserver */
+ observe(subject, topic, data)
+ {
+ if (topic !== "profile-after-change")
+ return;
+
+ var ppmm;
+ // Ci.nsIMessageBroadcaster went in Gecko 61.
+ if (Ci.nsIMessageBroadcaster)
+ {
+ ppmm = Cc[PPMM_CONTRACTID].getService(Ci.nsIMessageBroadcaster);
+ }
+ else
+ {
+ ppmm = Cc[PPMM_CONTRACTID].getService();
+ }
+ ppmm.loadProcessScript("chrome://chatzilla/content/lib/js/chatzilla-protocol-script.js", true);
+ ppmm.addMessageListener("ChatZilla:SpawnChatZilla", this);
+ },
+
+ /* nsIMessageListener */
+ receiveMessage(msg)
+ {
+ if (msg.name !== "ChatZilla:SpawnChatZilla")
+ return;
+
+ spawnChatZilla(msg.data.uri);
+ },
+};
+
+
+const StartupFactory =
+{
+ createInstance(outer, iid)
+ {
+ if (outer)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ if (!iid.equals(Ci.nsISupports))
+ throw Cr.NS_ERROR_NO_INTERFACE;
+
+ // startup:
+ return new ProcessHandler();
+ },
+};
+
+
+const ChatZillaModule =
+{
+ registerSelf(compMgr, fileSpec, location, type)
+ {
+ compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
+ const catman = Cc[CATMAN_CONTRACTID].getService(Ci.nsICategoryManager);
+
+ debug("*** Registering -chat handler.\n");
+ compMgr.registerFactoryLocation(CLINE_SERVICE_CID,
+ "ChatZilla CommandLine Service",
+ CLINE_SERVICE_CONTRACTID,
+ fileSpec, location, type);
+ catman.addCategoryEntry("command-line-argument-handlers",
+ "chatzilla command line handler",
+ CLINE_SERVICE_CONTRACTID, true, true);
+ catman.addCategoryEntry("command-line-handler",
+ "m-irc",
+ CLINE_SERVICE_CONTRACTID, true, true);
+
+ debug("*** Registering irc protocol handler.\n");
+ ChatZillaProtocols.initObsolete(compMgr, fileSpec, location, type);
+
+ debug("*** Registering done.\n");
+ },
+
+ unregisterSelf(compMgr, fileSpec, location)
+ {
+ compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
+
+ const catman = Cc[CATMAN_CONTRACTID].getService(Ci.nsICategoryManager);
+ catman.deleteCategoryEntry("command-line-argument-handlers",
+ "chatzilla command line handler", true);
+ catman.deleteCategoryEntry("command-line-handler",
+ "m-irc", true);
+ },
+
+ getClassObject(compMgr, cid, iid)
+ {
+ // Checking if we're disabled in the Chrome Registry.
+ var rv;
+ try
+ {
+ const rdfSvc = Cc[RDFS_CONTRACTID].getService(Ci.nsIRDFService);
+ const rdfDS = rdfSvc.GetDataSource("rdf:chrome");
+ const resSelf = rdfSvc.GetResource("urn:mozilla:package:chatzilla");
+ const resDisabled = rdfSvc.GetResource("http://www.mozilla.org/rdf/chrome#disabled");
+ rv = rdfDS.GetTarget(resSelf, resDisabled, true);
+ }
+ catch (e)
+ {
+ }
+ if (rv)
+ throw Cr.NS_ERROR_NO_INTERFACE;
+
+ if (cid.equals(CLINE_SERVICE_CID))
+ return CommandLineFactory;
+
+ if (cid.equals(IRCPROT_HANDLER_CID))
+ return IRCProtocolHandlerFactory;
+
+ if (cid.equals(IRCSPROT_HANDLER_CID))
+ return IRCSProtocolHandlerFactory;
+
+ if (cid.equals(STARTUP_CID))
+ return StartupFactory;
+
+ if (!iid.equals(Ci.nsIFactory))
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ canUnload(compMgr)
+ {
+ return true;
+ },
+};
+
+
+/* entrypoint */
+function NSGetModule(compMgr, fileSpec)
+{
+ return ChatZillaModule;
+}
+
+function NSGetFactory(cid)
+{
+ return ChatZillaModule.getClassObject(null, cid, null);
+}
diff --git a/comm/suite/chatzilla/js/lib/chatzilla-service.manifest b/comm/suite/chatzilla/js/lib/chatzilla-service.manifest
new file mode 100644
index 0000000000..28a0d84d8f
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/chatzilla-service.manifest
@@ -0,0 +1,6 @@
+component {38a95514-1dd2-11b2-97e7-9da958640f2c} chatzilla-service.js
+contract @mozilla.org/commandlinehandler/general-startup;1?type=chat {38a95514-1dd2-11b2-97e7-9da958640f2c}
+category command-line-handler m-irc @mozilla.org/commandlinehandler/general-startup;1?type=chat
+component {ae6ad015-433b-42ab-9afc-1636af5a7fc4} chatzilla-service.js
+contract @chatzilla.mozilla.org/startup;1 {ae6ad015-433b-42ab-9afc-1636af5a7fc4}
+category profile-after-change chatzilla-startup @chatzilla.mozilla.org/startup;1
diff --git a/comm/suite/chatzilla/js/lib/command-manager.js b/comm/suite/chatzilla/js/lib/command-manager.js
new file mode 100644
index 0000000000..76d2818c3c
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/command-manager.js
@@ -0,0 +1,952 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// @internal
+function getAccessKey(str)
+{
+ var i = str.indexOf("&");
+ if (i == -1)
+ return "";
+ return str[i + 1];
+}
+
+// @internal
+function CommandRecord(name, func, usage, help, label, accesskey, flags,
+ keystr, tip, format, helpUsage)
+{
+ this.name = name;
+ this.func = func;
+ this._usage = usage;
+ this.scanUsage();
+ this.help = help;
+ this.label = label ? label : name;
+ this.accesskey = accesskey ? accesskey : "";
+ this.format = format;
+ this.helpUsage = helpUsage;
+ this.labelstr = label.replace ("&", "");
+ this.tip = tip;
+ this.flags = flags;
+ this._enabled = true;
+ this.keyNodes = new Array();
+ this.keystr = keystr;
+ this.uiElements = new Array();
+}
+
+CommandRecord.prototype.__defineGetter__ ("enabled", cr_getenable);
+function cr_getenable ()
+{
+ return this._enabled;
+}
+
+CommandRecord.prototype.__defineSetter__ ("enabled", cr_setenable);
+function cr_setenable (state)
+{
+ for (var i = 0; i < this.uiElements.length; ++i)
+ {
+ if (state)
+ this.uiElements[i].removeAttribute ("disabled");
+ else
+ this.uiElements[i].setAttribute ("disabled", "true");
+ }
+ return (this._enabled = state);
+}
+
+CommandRecord.prototype.__defineSetter__ ("usage", cr_setusage);
+function cr_setusage (usage)
+{
+ this._usage = usage;
+ this.scanUsage();
+}
+
+CommandRecord.prototype.__defineGetter__ ("usage", cr_getusage);
+function cr_getusage()
+{
+ return this._usage;
+}
+
+/**
+ * @internal
+ *
+ * Scans the argument spec, in the format "<a1> <a2> [<o1> <o2>]", into an
+ * array of strings.
+ */
+CommandRecord.prototype.scanUsage =
+function cr_scanusage()
+{
+ var spec = this._usage;
+ var currentName = "";
+ var inName = false;
+ var len = spec.length;
+ var capNext = false;
+
+ this._usage = spec;
+ this.argNames = new Array();
+
+ for (var i = 0; i < len; ++i)
+ {
+ switch (spec[i])
+ {
+ case '[':
+ this.argNames.push (":");
+ break;
+
+ case '<':
+ inName = true;
+ break;
+
+ case '-':
+ capNext = true;
+ break;
+
+ case '>':
+ inName = false;
+ this.argNames.push (currentName);
+ currentName = "";
+ capNext = false;
+ break;
+
+ default:
+ if (inName)
+ currentName += capNext ? spec[i].toUpperCase() : spec[i];
+ capNext = false;
+ break;
+ }
+ }
+}
+
+/**
+ * Manages commands, with accelerator keys, help text and argument processing.
+ *
+ * You should never need to create an instance of this prototype; access the
+ * command manager through |client.commandManager|.
+ *
+ * @param defaultBundle An |nsIStringBundle| object to load command parameters,
+ * labels a help text from.
+ */
+function CommandManager(defaultBundle)
+{
+ this.commands = new Object();
+ this.commandHistory = new Object();
+ this.defaultBundle = defaultBundle;
+ this.currentDispatchDepth = 0;
+ this.maxDispatchDepth = 10;
+ this.dispatchUnwinding = false;
+}
+
+// @undocumented
+CommandManager.prototype.defaultFlags = 0;
+
+/**
+ * Adds multiple commands in a single call.
+ *
+ * @param cmdary |Array| containing commands to define; each item in the |Array|
+ * is also an |Array|, with either 3 or 4 items - corresponding to
+ * the first three or four arguments of |defineCommand|. An extra
+ * property, |stringBundle|, may be set on the |cmdary| |Array|
+ * to override the |defaultBundle| for all the commands.
+ */
+CommandManager.prototype.defineCommands =
+function cmgr_defcmds(cmdary)
+{
+ var len = cmdary.length;
+ var commands = new Object();
+ var bundle = "stringBundle" in cmdary ? cmdary.stringBundle : null;
+
+ for (var i = 0; i < len; ++i)
+ {
+ let name = cmdary[i][0];
+ let func = cmdary[i][1];
+ let flags = cmdary[i][2];
+ let usage = (3 in cmdary[i]) ? cmdary[i][3] : "";
+ commands[name] = this.defineCommand(name, func, flags, usage, bundle);
+ }
+
+ return commands;
+}
+
+/**
+ * Adds a single command.
+ *
+ * @param name The |String| name of the command to define.
+ * @param func A |Function| to call to handle dispatch of the new command.
+ * @param flags Optional. A |Number| indicating any special requirements for the
+ * command.
+ * @param usage Optional. A |String| specifying the arguments to the command. If
+ * not specified, then it is assumed there are none.
+ * @param bundle Optional. An |nsIStringBundle| to fetch parameters, labels,
+ * accelerator keys and help from. If not specified, the
+ * |defaultBundle| is used.
+ */
+CommandManager.prototype.defineCommand =
+function cmdmgr_defcmd(name, func, flags, usage, bundle)
+{
+ if (!bundle)
+ bundle = this.defaultBundle;
+
+ var helpDefault = MSG_NO_HELP;
+ var labelDefault = name;
+ var aliasFor;
+
+ if (typeof flags != "number")
+ flags = this.defaultFlags;
+
+ if (typeof usage != "string")
+ usage = "";
+
+ if (typeof func == "string")
+ {
+ var ary = func.match(/(\S+)/);
+ if (ary)
+ aliasFor = ary[1];
+ else
+ aliasFor = null;
+ helpDefault = getMsg (MSG_DEFAULT_ALIAS_HELP, func);
+ if (aliasFor)
+ labelDefault = getMsgFrom (bundle, "cmd." + aliasFor + ".label",
+ null, name);
+ }
+
+ var label = getMsgFrom(bundle, "cmd." + name + ".label", null,
+ labelDefault);
+ var accesskey = getMsgFrom(bundle, "cmd." + name + ".accesskey", null,
+ getAccessKey(label));
+ var help = helpDefault;
+ var helpUsage = "";
+ // Help is only shown for commands that available from the console.
+ if (flags & CMD_CONSOLE)
+ {
+ help = getMsgFrom(bundle, "cmd." + name + ".help", null, helpDefault);
+ // Only need to lookup localized helpUsage for commands that have them.
+ if (usage)
+ {
+ helpUsage = getMsgFrom(bundle, "cmd." + name + ".helpUsage", null,
+ "");
+ }
+ }
+ var keystr = getMsgFrom (bundle, "cmd." + name + ".key", null, "");
+ var format = getMsgFrom (bundle, "cmd." + name + ".format", null, null);
+ var tip = getMsgFrom (bundle, "cmd." + name + ".tip", null, "");
+ var command = new CommandRecord(name, func, usage, help, label, accesskey,
+ flags, keystr, tip, format, helpUsage);
+ this.addCommand(command);
+ if (aliasFor)
+ command.aliasFor = aliasFor;
+
+ return command;
+}
+
+/**
+ * Installs accelerator keys for commands into an existing document.
+ *
+ * @internal
+ * @param document An |XULDocument| within which to install the accelerator
+ * keys. Each command's key is installed by |installKey|.
+ * @param commands Optional. An |Array| or |Object| continaing |CommandRecord|
+ * objects. If not specified, all commands in the
+ * |CommandManager| are installed.
+ */
+CommandManager.prototype.installKeys =
+function cmgr_instkeys(document, commands)
+{
+ var parentElem = document.getElementById("dynamic-keys");
+ if (!parentElem)
+ {
+ parentElem = document.createElement("keyset");
+ parentElem.setAttribute("id", "dynamic-keys");
+ document.documentElement.appendChild(parentElem);
+ }
+
+ if (!commands)
+ commands = this.commands;
+
+ for (var c in commands)
+ this.installKey (parentElem, commands[c]);
+}
+
+/**
+ * Installs the accelerator key for a single command.
+ *
+ * This creates a <key> XUL element inside |parentElem|. It should usually be
+ * called once per command, per document, so that accelerator keys work in all
+ * application windows.
+ *
+ * @internal
+ * @param parentElem An |XULElement| to add the <key> too.
+ * @param command The |CommandRecord| to install.
+ */
+CommandManager.prototype.installKey =
+function cmgr_instkey(parentElem, command)
+{
+ if (!command.keystr)
+ return;
+
+ var ary = command.keystr.match (/(.*\s)?([\S]+)$/);
+ if (!ASSERT(ary, "couldn't parse key string ``" + command.keystr +
+ "'' for command ``" + command.name + "''"))
+ {
+ return;
+ }
+
+ var key = document.createElement ("key");
+ key.setAttribute ("id", "key:" + command.name);
+ key.setAttribute ("oncommand", "dispatch('" + command.name +
+ "', {isInteractive: true, source: 'keyboard'});");
+
+ if (ary[1])
+ key.setAttribute ("modifiers", ary[1]);
+
+ if (ary[2].indexOf("VK_") == 0)
+ key.setAttribute ("keycode", ary[2]);
+ else
+ key.setAttribute ("key", ary[2]);
+
+ parentElem.appendChild(key);
+ command.keyNodes.push(key);
+}
+
+/**
+ * Uninstalls accelerator keys for commands from a document.
+ *
+ * @internal
+ * @param commands Optional. An |Array| or |Object| continaing |CommandRecord|
+ * objects. If not specified, all commands in the
+ * |CommandManager| are uninstalled.
+ */
+CommandManager.prototype.uninstallKeys =
+function cmgr_uninstkeys(commands)
+{
+ if (!commands)
+ commands = this.commands;
+
+ for (var c in commands)
+ this.uninstallKey (commands[c]);
+}
+
+/**
+ * Uninstalls the accelerator key for a single command.
+ *
+ * @internal
+ * @param command The |CommandRecord| to uninstall.
+ */
+CommandManager.prototype.uninstallKey =
+function cmgr_uninstkey(command)
+{
+ for (var i in command.keyNodes)
+ {
+ try
+ {
+ /* document may no longer exist in a useful state. */
+ command.keyNodes[i].parentNode.removeChild(command.keyNodes[i]);
+ }
+ catch (ex)
+ {
+ dd ("*** caught exception uninstalling key node: " + ex);
+ }
+ }
+}
+
+/**
+ * Use |defineCommand|.
+ *
+ * @internal
+ * @param command The |CommandRecord| to add to the |CommandManager|.
+ */
+CommandManager.prototype.addCommand =
+function cmgr_add(command)
+{
+ if (objectContains(this.commands, command.name))
+ {
+ /* We've already got a command with this name - invoke the history
+ * storage so that we can undo this back to its original state.
+ */
+ if (!objectContains(this.commandHistory, command.name))
+ this.commandHistory[command.name] = new Array();
+ this.commandHistory[command.name].push(this.commands[command.name]);
+ }
+ this.commands[command.name] = command;
+}
+
+/**
+ * Removes multiple commands in a single call.
+ *
+ * @param cmdary An |Array| or |Object| containing |CommandRecord| objects.
+ * Ideally use the value returned from |defineCommands|.
+ */
+CommandManager.prototype.removeCommands =
+function cmgr_removes(cmdary)
+{
+ for (var i in cmdary)
+ {
+ var command = isinstance(cmdary[i], Array) ?
+ {name: cmdary[i][0]} : cmdary[i];
+ this.removeCommand(command);
+ }
+}
+
+/**
+ * Removes a single command.
+ *
+ * @param command The |CommandRecord| to remove from the |CommandManager|.
+ * Ideally use the value returned from |defineCommand|.
+ */
+CommandManager.prototype.removeCommand =
+function cmgr_remove(command)
+{
+ delete this.commands[command.name];
+ if (objectContains(this.commandHistory, command.name))
+ {
+ /* There was a previous command with this name - restore the most
+ * recent from the history, returning the command to its former glory.
+ */
+ this.commands[command.name] = this.commandHistory[command.name].pop();
+ if (this.commandHistory[command.name].length == 0)
+ delete this.commandHistory[command.name];
+ }
+}
+
+/**
+ * Registers a hook for a particular command.
+ *
+ * A command hook is uniquely identified by the pair |id|, |before|; only a
+ * single hook may exist for a given pair of |id| and |before| values. It is
+ * wise to use a unique |id|; plugins should construct an |id| using
+ * |plugin.id|, e.g. |plugin.id + "-my-hook-1"|.
+ *
+ * @param commandName A |String| command name to hook. The command named must
+ * already exist in the |CommandManager|; if it does not, no
+ * hook is added.
+ * @param func A |Function| to handle the hook.
+ * @param id A |String| identifier for the hook.
+ * @param before A |Boolean| indicating whether the hook wishes to be
+ * called before or after the command executes.
+ */
+CommandManager.prototype.addHook =
+function cmgr_hook (commandName, func, id, before)
+{
+ if (!ASSERT(objectContains(this.commands, commandName),
+ "Unknown command '" + commandName + "'"))
+ {
+ return;
+ }
+
+ var command = this.commands[commandName];
+
+ if (before)
+ {
+ if (!("beforeHooks" in command))
+ command.beforeHooks = new Object();
+ command.beforeHooks[id] = func;
+ }
+ else
+ {
+ if (!("afterHooks" in command))
+ command.afterHooks = new Object();
+ command.afterHooks[id] = func;
+ }
+}
+
+/**
+ * Registers multiple hooks for commands.
+ *
+ * @param hooks An |Object| containing |Function| objects to call for each
+ * hook; the key of each item is the name of the command it
+ * wishes to hook. Optionally, the |_before| property can be
+ * added to a |function| to override the default |before| value
+ * of |false|.
+ * @param prefix Optional. A |String| prefix to apply to each hook's command
+ * name to compute an |id| for it.
+ */
+CommandManager.prototype.addHooks =
+function cmgr_hooks (hooks, prefix)
+{
+ if (!prefix)
+ prefix = "";
+
+ for (var h in hooks)
+ {
+ this.addHook(h, hooks[h], prefix + ":" + h,
+ ("_before" in hooks[h]) ? hooks[h]._before : false);
+ }
+}
+
+/**
+ * Unregisters multiple hooks for commands.
+ *
+ * @param hooks An |Object| identical to the one passed to |addHooks|.
+ * @param prefix Optional. A |String| identical to the one passed to |addHooks|.
+ */
+CommandManager.prototype.removeHooks =
+function cmgr_remhooks (hooks, prefix)
+{
+ if (!prefix)
+ prefix = "";
+
+ for (var h in hooks)
+ {
+ this.removeHook(h, prefix + ":" + h,
+ ("before" in hooks[h]) ? hooks[h].before : false);
+ }
+}
+
+/**
+ * Unregisters a hook for a particular command.
+ *
+ * The arguments to |removeHook| are the same as |addHook|, but without the
+ * hook function itself.
+ *
+ * @param commandName The |String| command name to unhook.
+ * @param id The |String| identifier for the hook.
+ * @param before A |Boolean| indicating whether the hook was to be
+ * called before or after the command executed.
+ */
+CommandManager.prototype.removeHook =
+function cmgr_unhook (commandName, id, before)
+{
+ var command = this.commands[commandName];
+
+ if (before)
+ delete command.beforeHooks[id];
+ else
+ delete command.afterHooks[id];
+}
+
+/**
+ * Gets a sorted |Array| of |CommandRecord| objects which match.
+ *
+ * After filtering by |flags| (if specified), if an exact match for
+ * |partialName| is found, only that is returned; otherwise, all commands
+ * starting with |partialName| are returned in alphabetical order by |label|.
+ *
+ * @param partialName Optional. A |String| prefix to search for.
+ * @param flags Optional. Flags to logically AND with commands.
+ */
+CommandManager.prototype.list =
+function cmgr_list(partialName, flags, exact)
+{
+ /* returns array of command objects which look like |partialName|, or
+ * all commands if |partialName| is not specified */
+ function compare (a, b)
+ {
+ a = a.labelstr.toLowerCase();
+ b = b.labelstr.toLowerCase();
+
+ if (a == b)
+ return 0;
+
+ if (a > b)
+ return 1;
+
+ return -1;
+ }
+
+ var ary = new Array();
+ var commandNames = keys(this.commands);
+
+ for (var name of commandNames)
+ {
+ let command = this.commands[name];
+ if ((!flags || (command.flags & flags)) &&
+ (!partialName || command.name.startsWith(partialName)))
+ {
+ if (exact && partialName &&
+ partialName.length == command.name.length)
+ {
+ /* exact match */
+ return [command];
+ }
+ ary.push(command);
+ }
+ }
+
+ ary.sort(compare);
+ return ary;
+}
+
+/**
+ * Gets a sorted |Array| of command names which match.
+ *
+ * |listNames| operates identically to |list|, except that only command names
+ * are returned, not |CommandRecord| objects.
+ */
+CommandManager.prototype.listNames =
+function cmgr_listnames (partialName, flags)
+{
+ var cmds = this.list(partialName, flags, false);
+ var cmdNames = new Array();
+
+ for (var c in cmds)
+ cmdNames.push (cmds[c].name);
+
+ cmdNames.sort();
+ return cmdNames;
+}
+
+/**
+ * Internal use only.
+ *
+ * Called to parse the arguments stored in |e.inputData|, as properties of |e|,
+ * for the CommandRecord stored on |e.command|.
+ *
+ * @params e Event object to be processed.
+ */
+// @undocumented
+CommandManager.prototype.parseArguments =
+function cmgr_parseargs (e)
+{
+ var rv = this.parseArgumentsRaw(e);
+ //dd("parseArguments '" + e.command.usage + "' " +
+ // (rv ? "passed" : "failed") + "\n" + dumpObjectTree(e));
+ delete e.currentArgIndex;
+ return rv;
+}
+
+/**
+ * Internal use only.
+ *
+ * Don't call parseArgumentsRaw directly, use parseArguments instead.
+ *
+ * Parses the arguments stored in the |inputData| property of the event object,
+ * according to the format specified by the |command| property.
+ *
+ * On success this method returns true, and propery names corresponding to the
+ * argument names used in the format spec will be created on the event object.
+ * All optional parameters will be initialized to |null| if not already present
+ * on the event.
+ *
+ * On failure this method returns false and a description of the problem
+ * will be stored in the |parseError| property of the event.
+ *
+ * For example...
+ * Given the argument spec "<int> <word> [ <word2> <word3> ]", and given the
+ * input string "411 foo", stored as |e.command.usage| and |e.inputData|
+ * respectively, this method would add the following propertys to the event
+ * object...
+ * -name---value--notes-
+ * e.int 411 Parsed as an integer
+ * e.word foo Parsed as a string
+ * e.word2 null Optional parameters not specified will be set to null.
+ * e.word3 null If word2 had been provided, word3 would be required too.
+ *
+ * Each parameter is parsed by calling the function with the same name, located
+ * in this.argTypes. The first parameter is parsed by calling the function
+ * this.argTypes["int"], for example. This function is expected to act on
+ * e.unparsedData, taking it's chunk, and leaving the rest of the string.
+ * The default parse functions are...
+ * <word> parses contiguous non-space characters.
+ * <int> parses as an int.
+ * <rest> parses to the end of input data.
+ * <state> parses yes, on, true, 1, 0, false, off, no as a boolean.
+ * <toggle> parses like a <state>, except allows "toggle" as well.
+ * <...> parses according to the parameter type before it, until the end
+ * of the input data. Results are stored in an array named
+ * paramnameList, where paramname is the name of the parameter
+ * before <...>. The value of the parameter before this will be
+ * paramnameList[0].
+ *
+ * If there is no parse function for an argument type, "word" will be used by
+ * default. You can alias argument types with code like...
+ * commandManager.argTypes["my-integer-name"] = commandManager.argTypes["int"];
+ */
+// @undocumented
+CommandManager.prototype.parseArgumentsRaw =
+function parse_parseargsraw (e)
+{
+ var argc = e.command.argNames.length;
+
+ function initOptionals()
+ {
+ for (var i = 0; i < argc; ++i)
+ {
+ if (e.command.argNames[i] != ":" &&
+ e.command.argNames[i] != "..." &&
+ !(e.command.argNames[i] in e))
+ {
+ e[e.command.argNames[i]] = null;
+ }
+
+ if (e.command.argNames[i] == "...")
+ {
+ var paramName = e.command.argNames[i - 1];
+ if (paramName == ":")
+ paramName = e.command.argNames[i - 2];
+ var listName = paramName + "List";
+ if (!(listName in e))
+ e[listName] = [ e[paramName] ];
+ }
+ }
+ }
+
+ if ("inputData" in e && e.inputData)
+ {
+ /* if data has been provided, parse it */
+ e.unparsedData = e.inputData;
+ var parseResult;
+ var currentArg;
+ e.currentArgIndex = 0;
+
+ if (argc)
+ {
+ currentArg = e.command.argNames[e.currentArgIndex];
+
+ while (e.unparsedData)
+ {
+ if (currentArg != ":")
+ {
+ if (!this.parseArgument (e, currentArg))
+ return false;
+ }
+ if (++e.currentArgIndex < argc)
+ currentArg = e.command.argNames[e.currentArgIndex];
+ else
+ break;
+ }
+
+ if (e.currentArgIndex < argc && currentArg != ":")
+ {
+ /* parse loop completed because it ran out of data. We haven't
+ * parsed all of the declared arguments, and we're not stopped
+ * at an optional marker, so we must be missing something
+ * required... */
+ e.parseError = getMsg(MSG_ERR_REQUIRED_PARAM,
+ e.command.argNames[e.currentArgIndex]);
+ return false;
+ }
+ }
+
+ if (e.unparsedData)
+ {
+ /* parse loop completed with unparsed data, which means we've
+ * successfully parsed all arguments declared. Whine about the
+ * extra data... */
+ display (getMsg(MSG_EXTRA_PARAMS, e.unparsedData), MT_WARN);
+ }
+ }
+
+ var rv = this.isCommandSatisfied(e);
+ if (rv)
+ initOptionals();
+ return rv;
+}
+
+/**
+ * Returns true if |e| has the properties required to call the command
+ * |command|.
+ *
+ * If |command| is not provided, |e.command| is used instead.
+ *
+ * @param e Event object to test against the command.
+ * @param command Command to test.
+ */
+// @undocumented
+CommandManager.prototype.isCommandSatisfied =
+function cmgr_isok (e, command)
+{
+ if (typeof command == "undefined")
+ command = e.command;
+ else if (typeof command == "string")
+ command = this.commands[command];
+
+ if (!command.enabled)
+ return false;
+
+ for (var i = 0; i < command.argNames.length; ++i)
+ {
+ if (command.argNames[i] == ":")
+ return true;
+
+ if (!(command.argNames[i] in e))
+ {
+ e.parseError = getMsg(MSG_ERR_REQUIRED_PARAM, command.argNames[i]);
+ //dd("command '" + command.name + "' unsatisfied: " + e.parseError);
+ return false;
+ }
+ }
+
+ //dd ("command '" + command.name + "' satisfied.");
+ return true;
+}
+
+/**
+ * Internal use only.
+ * See parseArguments above and the |argTypes| object below.
+ *
+ * Parses the next argument by calling an appropriate parser function, or the
+ * generic "word" parser if none other is found.
+ *
+ * @param e event object.
+ * @param name property name to use for the parse result.
+ */
+// @undocumented
+CommandManager.prototype.parseArgument =
+function cmgr_parsearg (e, name)
+{
+ var parseResult;
+
+ if (name in this.argTypes)
+ parseResult = this.argTypes[name](e, name, this);
+ else
+ parseResult = this.argTypes["word"](e, name, this);
+
+ if (!parseResult)
+ e.parseError = getMsg(MSG_ERR_INVALID_PARAM,
+ [name, e.unparsedData]);
+
+ return parseResult;
+}
+
+// @undocumented
+CommandManager.prototype.argTypes = new Object();
+
+/**
+ * Convenience function used to map a list of new types to an existing parse
+ * function.
+ */
+// @undocumented
+CommandManager.prototype.argTypes.__aliasTypes__ =
+function at_alias (list, type)
+{
+ for (var i in list)
+ {
+ this[list[i]] = this[type];
+ }
+}
+
+/**
+ * Internal use only.
+ *
+ * Parses an integer, stores result in |e[name]|.
+ */
+// @undocumented
+CommandManager.prototype.argTypes["int"] =
+function parse_int (e, name)
+{
+ var ary = e.unparsedData.match (/(\d+)(?:\s+(.*))?$/);
+ if (!ary)
+ return false;
+ e[name] = Number(ary[1]);
+ e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
+ return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * Parses a word, which is defined as a list of nonspace characters.
+ *
+ * Stores result in |e[name]|.
+ */
+// @undocumented
+CommandManager.prototype.argTypes["word"] =
+function parse_word (e, name)
+{
+ var ary = e.unparsedData.match (/(\S+)(?:\s+(.*))?$/);
+ if (!ary)
+ return false;
+ e[name] = ary[1];
+ e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
+ return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * Parses a "state" which can be "true", "on", "yes", or 1 to indicate |true|,
+ * or "false", "off", "no", or 0 to indicate |false|.
+ *
+ * Stores result in |e[name]|.
+ */
+// @undocumented
+CommandManager.prototype.argTypes["state"] =
+function parse_state (e, name)
+{
+ var ary =
+ e.unparsedData.match (/(true|on|yes|1|false|off|no|0)(?:\s+(.*))?$/i);
+ if (!ary)
+ return false;
+ if (ary[1].search(/true|on|yes|1/i) != -1)
+ e[name] = true;
+ else
+ e[name] = false;
+ e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
+ return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * Parses a "toggle" which can be "true", "on", "yes", or 1 to indicate |true|,
+ * or "false", "off", "no", or 0 to indicate |false|. In addition, the string
+ * "toggle" is accepted, in which case |e[name]| will be the string "toggle".
+ *
+ * Stores result in |e[name]|.
+ */
+// @undocumented
+CommandManager.prototype.argTypes["toggle"] =
+function parse_toggle (e, name)
+{
+ var ary = e.unparsedData.match
+ (/(toggle|true|on|yes|1|false|off|no|0)(?:\s+(.*))?$/i);
+
+ if (!ary)
+ return false;
+ if (ary[1].search(/toggle/i) != -1)
+ e[name] = "toggle";
+ else if (ary[1].search(/true|on|yes|1/i) != -1)
+ e[name] = true;
+ else
+ e[name] = false;
+ e.unparsedData = arrayHasElementAt(ary, 2) ? ary[2] : "";
+ return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * Returns all unparsed data to the end of the line.
+ *
+ * Stores result in |e[name]|.
+ */
+// @undocumented
+CommandManager.prototype.argTypes["rest"] =
+function parse_rest (e, name)
+{
+ e[name] = e.unparsedData;
+ e.unparsedData = "";
+ return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * Parses the rest of the unparsed data the same way the previous argument was
+ * parsed. Can't be used as the first parameter. if |name| is "..." then the
+ * name of the previous argument, plus the suffix "List" will be used instead.
+ *
+ * Stores result in |e[name]| or |e[lastName + "List"]|.
+ */
+// @undocumented
+CommandManager.prototype.argTypes["..."] =
+function parse_repeat (e, name, cm)
+{
+ ASSERT (e.currentArgIndex > 0, "<...> can't be the first argument.");
+
+ var lastArg = e.command.argNames[e.currentArgIndex - 1];
+ if (lastArg == ":")
+ lastArg = e.command.argNames[e.currentArgIndex - 2];
+
+ var listName = lastArg + "List";
+ e[listName] = [ e[lastArg] ];
+
+ while (e.unparsedData)
+ {
+ if (!cm.parseArgument(e, lastArg))
+ return false;
+ e[listName].push(e[lastArg]);
+ }
+
+ e[lastArg] = e[listName][0];
+ return true;
+}
diff --git a/comm/suite/chatzilla/js/lib/connection-xpcom.js b/comm/suite/chatzilla/js/lib/connection-xpcom.js
new file mode 100644
index 0000000000..031689d74d
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/connection-xpcom.js
@@ -0,0 +1,703 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const NS_ERROR_MODULE_NETWORK = 2152398848;
+
+const NS_ERROR_UNKNOWN_HOST = NS_ERROR_MODULE_NETWORK + 30;
+const NS_ERROR_CONNECTION_REFUSED = NS_ERROR_MODULE_NETWORK + 13;
+const NS_ERROR_NET_TIMEOUT = NS_ERROR_MODULE_NETWORK + 14;
+const NS_ERROR_OFFLINE = NS_ERROR_MODULE_NETWORK + 16;
+const NS_ERROR_NET_RESET = NS_ERROR_MODULE_NETWORK + 20;
+const NS_ERROR_UNKNOWN_PROXY_HOST = NS_ERROR_MODULE_NETWORK + 42;
+const NS_ERROR_NET_INTERRUPT = NS_ERROR_MODULE_NETWORK + 71;
+const NS_ERROR_PROXY_CONNECTION_REFUSED = NS_ERROR_MODULE_NETWORK + 72;
+
+// Offline error constants:
+const NS_ERROR_BINDING_ABORTED = NS_ERROR_MODULE_NETWORK + 2;
+const NS_ERROR_ABORT = 0x80004004;
+
+const NS_NET_STATUS_RESOLVING_HOST = NS_ERROR_MODULE_NETWORK + 3;
+const NS_NET_STATUS_CONNECTED_TO = NS_ERROR_MODULE_NETWORK + 4;
+const NS_NET_STATUS_SENDING_TO = NS_ERROR_MODULE_NETWORK + 5;
+const NS_NET_STATUS_RECEIVING_FROM = NS_ERROR_MODULE_NETWORK + 6;
+const NS_NET_STATUS_CONNECTING_TO = NS_ERROR_MODULE_NETWORK + 7;
+
+// Security error class constants:
+const ERROR_CLASS_SSL_PROTOCOL = 1;
+const ERROR_CLASS_BAD_CERT = 2;
+
+// Security Constants.
+const STATE_IS_BROKEN = 1;
+const STATE_IS_SECURE = 2;
+const STATE_IS_INSECURE = 3;
+
+const nsIScriptableInputStream = Components.interfaces.nsIScriptableInputStream;
+
+const nsIBinaryInputStream = Components.interfaces.nsIBinaryInputStream;
+const nsIBinaryOutputStream = Components.interfaces.nsIBinaryOutputStream;
+
+function toSInputStream(stream, binary)
+{
+ var sstream;
+
+ if (binary)
+ {
+ sstream = Components.classes["@mozilla.org/binaryinputstream;1"];
+ sstream = sstream.createInstance(nsIBinaryInputStream);
+ sstream.setInputStream(stream);
+ }
+ else
+ {
+ sstream = Components.classes["@mozilla.org/scriptableinputstream;1"];
+ sstream = sstream.createInstance(nsIScriptableInputStream);
+ sstream.init(stream);
+ }
+
+ return sstream;
+}
+
+function toSOutputStream(stream, binary)
+{
+ var sstream;
+
+ if (binary)
+ {
+ sstream = Components.classes["@mozilla.org/binaryoutputstream;1"];
+ sstream = sstream.createInstance(Components.interfaces.nsIBinaryOutputStream);
+ sstream.setOutputStream(stream);
+ }
+ else
+ {
+ sstream = stream;
+ }
+
+ return sstream;
+}
+
+/* This object implements nsIBadCertListener2
+ * The idea is to suppress the default UI's alert box
+ * and allow the exception to propagate normally
+ */
+function BadCertHandler()
+{
+}
+
+BadCertHandler.prototype.getInterface =
+function badcert_getinterface(aIID)
+{
+ return this.QueryInterface(aIID);
+}
+
+BadCertHandler.prototype.QueryInterface =
+function badcert_queryinterface(aIID)
+{
+ if (aIID.equals(Components.interfaces.nsIBadCertListener2) ||
+ aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
+ aIID.equals(Components.interfaces.nsISupports))
+ {
+ return this;
+ }
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+}
+
+/* Returning true in the following two callbacks
+ * means suppress default the error UI (modal alert).
+ */
+BadCertHandler.prototype.notifyCertProblem =
+function badcert_notifyCertProblem(socketInfo, sslStatus, targetHost)
+{
+ return true;
+}
+
+/**
+ * Wraps up various mechanics of sockets for easy consumption by other code.
+ *
+ * @param binary Provide |true| or |false| here to override the automatic
+ * selection of binary or text streams. This should only ever be
+ * specified as |true| or omitted, otherwise you will be shooting
+ * yourself in the foot on some versions - let the code handle
+ * the choice unless you know you need binary.
+ */
+function CBSConnection (binary)
+{
+ /* Since 2003-01-17 18:14, Mozilla has had this contract ID for the STS.
+ * Prior to that it didn't have one, so we also include the CID for the
+ * STS back then - DO NOT UPDATE THE ID if it changes in Mozilla.
+ */
+ const sockClassByName =
+ Components.classes["@mozilla.org/network/socket-transport-service;1"];
+ const sockClassByID =
+ Components.classesByID["{c07e81e0-ef12-11d2-92b6-00105a1b0d64}"];
+
+ var sockServiceClass = (sockClassByName || sockClassByID);
+
+ if (!sockServiceClass)
+ throw ("Couldn't get socket service class.");
+
+ var sockService = sockServiceClass.getService();
+ if (!sockService)
+ throw ("Couldn't get socket service.");
+
+ this._sockService = sockService.QueryInterface
+ (Components.interfaces.nsISocketTransportService);
+
+ /* Note: as part of the mess from bug 315288 and bug 316178, ChatZilla now
+ * uses the *binary* stream interfaces for all network
+ * communications.
+ *
+ * However, these interfaces do not exist prior to 1999-11-05. To
+ * make matters worse, an incompatible change to the "readBytes"
+ * method of this interface was made on 2003-03-13; luckly, this
+ * change also added a "readByteArray" method, which we will check
+ * for below, to determine if we can use the binary streams.
+ */
+
+ // We want to check for working binary streams only the first time.
+ if (CBSConnection.prototype.workingBinaryStreams == -1)
+ {
+ CBSConnection.prototype.workingBinaryStreams = false;
+
+ if (typeof nsIBinaryInputStream != "undefined")
+ {
+ var isCls = Components.classes["@mozilla.org/binaryinputstream;1"];
+ var inputStream = isCls.createInstance(nsIBinaryInputStream);
+ if ("readByteArray" in inputStream)
+ CBSConnection.prototype.workingBinaryStreams = true;
+ }
+ }
+
+ /*
+ * As part of the changes in Gecko 1.9, invalid SSL certificates now
+ * produce a horrible error message. We must look up the toolkit version
+ * to see if we need to catch these errors cleanly - see bug 454966.
+ */
+ if (!("strictSSL" in CBSConnection.prototype))
+ {
+ CBSConnection.prototype.strictSSL = false;
+ var app = getService("@mozilla.org/xre/app-info;1", "nsIXULAppInfo");
+ if (app && ("platformVersion" in app) &&
+ compareVersions("1.9", app.platformVersion) >= 0)
+ {
+ CBSConnection.prototype.strictSSL = true;
+ }
+ }
+
+ this.wrappedJSObject = this;
+ if (typeof binary != "undefined")
+ this.binaryMode = binary;
+ else
+ this.binaryMode = this.workingBinaryStreams;
+
+ if (!ASSERT(!this.binaryMode || this.workingBinaryStreams,
+ "Unable to use binary streams in this build."))
+ {
+ throw ("Unable to use binary streams in this build.");
+ }
+}
+
+CBSConnection.prototype.workingBinaryStreams = -1;
+
+CBSConnection.prototype.connect =
+function bc_connect(host, port, config, observer)
+{
+ this.host = host.toLowerCase();
+ this.port = port;
+
+ /* The APIs below want host:port. Later on, we also reformat the host to
+ * strip IPv6 literal brackets.
+ */
+ var hostPort = host + ":" + port;
+
+ if (!config)
+ config = {};
+
+ if (!("proxyInfo" in config))
+ {
+ // Lets get a transportInfo for this
+ var pps = getService("@mozilla.org/network/protocol-proxy-service;1",
+ "nsIProtocolProxyService");
+
+ /* Force Necko to supply the HTTP proxy info if desired. For none,
+ * force no proxy. Other values will get default treatment.
+ */
+ var uri = "irc://" + hostPort;
+ if ("proxy" in config)
+ {
+ if (config.proxy == "http")
+ uri = "http://" + hostPort;
+ else if (config.proxy == "none")
+ uri = "";
+ }
+
+ var self = this;
+ function continueWithProxy(proxyInfo)
+ {
+ config.proxyInfo = proxyInfo;
+ try
+ {
+ self.connect(host, port, config, observer);
+ }
+ catch (ex)
+ {
+ if ("onSocketConnection" in observer)
+ observer.onSocketConnection(host, port, config, ex);
+ return;
+ }
+ if ("onSocketConnection" in observer)
+ observer.onSocketConnection(host, port, config);
+ }
+
+ if (uri)
+ {
+ uri = Services.io.newURI(uri);
+ if ("asyncResolve" in pps)
+ {
+ pps.asyncResolve(uri, 0, {
+ onProxyAvailable: function(request, uri, proxyInfo, status) {
+ continueWithProxy(proxyInfo);
+ }
+ });
+ }
+ else if ("resolve" in pps)
+ {
+ continueWithProxy(pps.resolve(uri, 0));
+ }
+ else if ("examineForProxy" in pps)
+ {
+ continueWithProxy(pps.examineForProxy(uri));
+ }
+ else
+ {
+ throw "Unable to find method to resolve proxies";
+ }
+ }
+ else
+ {
+ continueWithProxy(null);
+ }
+ return true;
+ }
+
+ // Strip the IPv6 literal brackets; all the APIs below don't want them.
+ if (host[0] == '[' && host[host.length - 1] == ']')
+ host = host.substr(1, host.length - 2);
+
+ /* Since the proxy info is opaque, we need to check that we got
+ * something for our HTTP proxy - we can't just check proxyInfo.type.
+ */
+ var proxyInfo = config.proxyInfo || null;
+ var usingHTTPCONNECT = ("proxy" in config) && (config.proxy == "http")
+ && proxyInfo;
+
+ if (proxyInfo && ("type" in proxyInfo) && (proxyInfo.type == "unknown"))
+ throw JSIRC_ERR_PAC_LOADING;
+
+ /* use new necko interfaces */
+ if (("isSecure" in config) && config.isSecure)
+ {
+ this._transport = this._sockService.
+ createTransport(["ssl"], 1, host, port,
+ proxyInfo);
+
+ if (this.strictSSL)
+ this._transport.securityCallbacks = new BadCertHandler();
+ }
+ else
+ {
+ this._transport = this._sockService.
+ createTransport(["starttls"], 1, host, port, proxyInfo);
+ }
+ if (!this._transport)
+ throw ("Error creating transport.");
+
+ var openFlags = 0;
+
+ /* no limit on the output stream buffer */
+ this._outputStream =
+ this._transport.openOutputStream(openFlags, 4096, -1);
+ if (!this._outputStream)
+ throw "Error getting output stream.";
+ this._sOutputStream = toSOutputStream(this._outputStream,
+ this.binaryMode);
+
+ this._inputStream = this._transport.openInputStream(openFlags, 0, 0);
+ if (!this._inputStream)
+ throw "Error getting input stream.";
+ this._sInputStream = toSInputStream(this._inputStream,
+ this.binaryMode);
+
+ this.connectDate = new Date();
+ this.isConnected = true;
+
+ // Bootstrap the connection if we're proxying via an HTTP proxy.
+ if (usingHTTPCONNECT)
+ this.sendData("CONNECT " + hostPort + " HTTP/1.1\r\n\r\n");
+
+ return true;
+
+}
+
+CBSConnection.prototype.startTLS =
+function bc_starttls()
+{
+ if (!this.isConnected || !this._transport.securityInfo)
+ return;
+
+ var secInfo = this._transport.securityInfo;
+ var sockControl = secInfo.QueryInterface(Ci.nsITLSSocketControl);
+ sockControl.StartTLS();
+}
+
+CBSConnection.prototype.listen =
+function bc_listen(port, observer)
+{
+ var serverSockClass =
+ Components.classes["@mozilla.org/network/server-socket;1"];
+
+ if (!serverSockClass)
+ throw ("Couldn't get server socket class.");
+
+ var serverSock = serverSockClass.createInstance();
+ if (!serverSock)
+ throw ("Couldn't get server socket.");
+
+ this._serverSock = serverSock.QueryInterface
+ (Components.interfaces.nsIServerSocket);
+
+ this._serverSock.init(port, false, -1);
+
+ this._serverSockListener = new SocketListener(this, observer);
+
+ this._serverSock.asyncListen(this._serverSockListener);
+
+ this.port = this._serverSock.port;
+
+ return true;
+}
+
+CBSConnection.prototype.accept =
+function bc_accept(transport, observer)
+{
+ this._transport = transport;
+ this.host = this._transport.host.toLowerCase();
+ this.port = this._transport.port;
+
+ var openFlags = 0;
+
+ /* no limit on the output stream buffer */
+ this._outputStream =
+ this._transport.openOutputStream(openFlags, 4096, -1);
+ if (!this._outputStream)
+ throw "Error getting output stream.";
+ this._sOutputStream = toSOutputStream(this._outputStream,
+ this.binaryMode);
+
+ this._inputStream = this._transport.openInputStream(openFlags, 0, 0);
+ if (!this._inputStream)
+ throw "Error getting input stream.";
+ this._sInputStream = toSInputStream(this._inputStream,
+ this.binaryMode);
+
+ this.connectDate = new Date();
+ this.isConnected = true;
+
+ // Clean up listening socket.
+ this.close();
+
+ return this.isConnected;
+}
+
+CBSConnection.prototype.close =
+function bc_close()
+{
+ if ("_serverSock" in this && this._serverSock)
+ this._serverSock.close();
+}
+
+CBSConnection.prototype.disconnect =
+function bc_disconnect()
+{
+ if ("_inputStream" in this && this._inputStream)
+ this._inputStream.close();
+ if ("_outputStream" in this && this._outputStream)
+ this._outputStream.close();
+ this.isConnected = false;
+ /*
+ this._streamProvider.close();
+ if (this._streamProvider.isBlocked)
+ this._write_req.resume();
+ */
+}
+
+CBSConnection.prototype.sendData =
+function bc_senddata(str)
+{
+ if (!this.isConnected)
+ throw "Not Connected.";
+
+ this.sendDataNow(str);
+}
+
+CBSConnection.prototype.readData =
+function bc_readdata(timeout, count)
+{
+ if (!this.isConnected)
+ throw "Not Connected.";
+
+ var rv;
+
+ if (!("_sInputStream" in this)) {
+ this._sInputStream = toSInputStream(this._inputStream);
+ dump("OMG, setting up _sInputStream!\n");
+ }
+
+ try
+ {
+ // XPCshell h4x
+ if (typeof count == "undefined")
+ count = this._sInputStream.available();
+ if (this.binaryMode)
+ rv = this._sInputStream.readBytes(count);
+ else
+ rv = this._sInputStream.read(count);
+ }
+ catch (ex)
+ {
+ dd ("*** Caught " + ex + " while reading.");
+ this.disconnect();
+ throw (ex);
+ }
+
+ return rv;
+}
+
+CBSConnection.prototype.startAsyncRead =
+function bc_saread (observer)
+{
+ var cls = Components.classes["@mozilla.org/network/input-stream-pump;1"];
+ var pump = cls.createInstance(Components.interfaces.nsIInputStreamPump);
+ // Account for Bug 1402888 which removed the startOffset and readLimit
+ // parameters from init.
+ if (pump.init.length > 5)
+ {
+ pump.init(this._inputStream, -1, -1, 0, 0, false);
+ } else
+ {
+ pump.init(this._inputStream, 0, 0, false);
+ }
+ pump.asyncRead(new StreamListener(observer), this);
+}
+
+CBSConnection.prototype.asyncWrite =
+function bc_awrite (str)
+{
+ this._streamProvider.pendingData += str;
+ if (this._streamProvider.isBlocked)
+ {
+ this._write_req.resume();
+ this._streamProvider.isBlocked = false;
+ }
+}
+
+CBSConnection.prototype.hasPendingWrite =
+function bc_haspwrite ()
+{
+ return false; /* data already pushed to necko */
+}
+
+CBSConnection.prototype.sendDataNow =
+function bc_senddatanow(str)
+{
+ var rv = false;
+
+ try
+ {
+ if (this.binaryMode)
+ this._sOutputStream.writeBytes(str, str.length);
+ else
+ this._sOutputStream.write(str, str.length);
+ rv = true;
+ }
+ catch (ex)
+ {
+ dd ("*** Caught " + ex + " while sending.");
+ this.disconnect();
+ throw (ex);
+ }
+
+ return rv;
+}
+
+/**
+ * Gets information about the security of the connection.
+ *
+ * |STATE_IS_BROKEN| is returned if any errors occur and |STATE_IS_INSECURE| is
+ * returned for disconnected sockets.
+ *
+ * @returns A value from the |STATE_IS_*| enumeration at the top of this file.
+ */
+CBSConnection.prototype.getSecurityState =
+function bc_getsecuritystate()
+{
+ if (!this.isConnected || !this._transport.securityInfo)
+ return STATE_IS_INSECURE;
+
+ try
+ {
+ // Get the actual SSL Status
+ let sslSp = this._transport.securityInfo
+ .QueryInterface(Ci.nsISSLStatusProvider);
+ if (!sslSp.SSLStatus)
+ return STATE_IS_BROKEN;
+ let sslStatus = sslSp.SSLStatus.QueryInterface(Ci.nsISSLStatus);
+ // Store appropriate status
+ if (!("keyLength" in sslStatus) || !sslStatus.keyLength)
+ return STATE_IS_BROKEN;
+
+ return STATE_IS_SECURE;
+ }
+ catch (ex)
+ {
+ // Something goes wrong -> broken security icon
+ dd("Exception getting certificate for connection: " + ex.message);
+ return STATE_IS_BROKEN;
+ }
+}
+
+CBSConnection.prototype.getCertificate =
+function bc_getcertificate()
+{
+ if (!this.isConnected || !this._transport.securityInfo)
+ return null;
+
+ // Get the actual SSL Status
+ let sslSp = this._transport.securityInfo
+ .QueryInterface(Ci.nsISSLStatusProvider);
+ if (!sslSp.SSLStatus)
+ return null;
+ let sslStatus = sslSp.SSLStatus.QueryInterface(Ci.nsISSLStatus);
+
+ // return the certificate
+ return sslStatus.serverCert;
+}
+
+CBSConnection.prototype.asyncWrite =
+function bc_asyncwrite()
+{
+ throw "Not Implemented.";
+}
+
+function StreamProvider(observer)
+{
+ this._observer = observer;
+}
+
+StreamProvider.prototype.pendingData = "";
+StreamProvider.prototype.isBlocked = true;
+
+StreamProvider.prototype.close =
+function sp_close ()
+{
+ this.isClosed = true;
+}
+
+StreamProvider.prototype.onDataWritable =
+function sp_datawrite (request, ctxt, ostream, offset, count)
+{
+ //dd ("StreamProvider.prototype.onDataWritable");
+
+ if ("isClosed" in this && this.isClosed)
+ throw Components.results.NS_BASE_STREAM_CLOSED;
+
+ if (!this.pendingData)
+ {
+ this.isBlocked = true;
+
+ /* this is here to support pre-XPCDOM builds (0.9.0 era), which
+ * don't have this result code mapped. */
+ if (!Components.results.NS_BASE_STREAM_WOULD_BLOCK)
+ throw 2152136711;
+
+ throw Components.results.NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ var len = ostream.write (this.pendingData, this.pendingData.length);
+ this.pendingData = this.pendingData.substr (len);
+}
+
+StreamProvider.prototype.onStartRequest =
+function sp_startreq (request, ctxt)
+{
+ //dd ("StreamProvider::onStartRequest: " + request + ", " + ctxt);
+}
+
+
+StreamProvider.prototype.onStopRequest =
+function sp_stopreq (request, ctxt, status)
+{
+ //dd ("StreamProvider::onStopRequest: " + request + ", " + ctxt + ", " +
+ // status);
+ if (this._observer)
+ this._observer.onStreamClose(status);
+}
+
+function StreamListener(observer)
+{
+ this._observer = observer;
+}
+
+StreamListener.prototype.onStartRequest =
+function sl_startreq (request, ctxt)
+{
+ //dd ("StreamListener::onStartRequest: " + request + ", " + ctxt);
+}
+
+StreamListener.prototype.onStopRequest =
+function sl_stopreq (request, ctxt, status)
+{
+ //dd ("StreamListener::onStopRequest: " + request + ", " + ctxt + ", " +
+ //status);
+ if (this._observer)
+ this._observer.onStreamClose(status);
+}
+
+StreamListener.prototype.onDataAvailable =
+function sl_dataavail (request, ctxt, inStr, sourceOffset, count)
+{
+ ctxt = ctxt.wrappedJSObject;
+ if (!ctxt)
+ {
+ dd ("*** Can't get wrappedJSObject from ctxt in " +
+ "StreamListener.onDataAvailable ***");
+ return;
+ }
+
+ if (!("_sInputStream" in ctxt))
+ ctxt._sInputStream = toSInputStream(inStr, false);
+
+ if (this._observer)
+ this._observer.onStreamDataAvailable(request, inStr, sourceOffset,
+ count);
+}
+
+function SocketListener(connection, observer)
+{
+ this._connection = connection;
+ this._observer = observer;
+}
+
+SocketListener.prototype.onSocketAccepted =
+function sl_onSocketAccepted(socket, transport)
+{
+ this._observer.onSocketAccepted(socket, transport);
+}
+SocketListener.prototype.onStopListening =
+function sl_onStopListening(socket, status)
+{
+ delete this._connection._serverSockListener;
+ delete this._connection._serverSock;
+}
diff --git a/comm/suite/chatzilla/js/lib/dcc.js b/comm/suite/chatzilla/js/lib/dcc.js
new file mode 100644
index 0000000000..d12b8f1a52
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/dcc.js
@@ -0,0 +1,1198 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* We pick a random start ID, from 0 to DCC_ID_MAX inclusive, then go through
+ * the IDs one at a time, in sequence. We wrap when we get to DCC_ID_MAX. No
+ * uniqueness checking is done, but it takes DCC_ID_MAX connections before we
+ * hit the start ID again. 65,536 IDs ought to be enough for now. :)
+ */
+const DCC_ID_MAX = 0xFFFF;
+
+function CIRCDCC(parent)
+{
+ this.parent = parent;
+
+ this.users = new Object();
+ this.chats = new Array();
+ this.files = new Array();
+ this.last = null;
+ this.lastTime = null;
+
+ this.sendChunk = 4096;
+ this.maxUnAcked = 32; // 4096 * 32 == 128KiB 'in transit'.
+
+ this.requestTimeout = 3 * 60 * 1000; // 3 minutes.
+
+ // Can't do anything 'til this is set!
+ this.localIPlist = new Array();
+ this.localIP = null;
+ this._lastPort = null;
+
+ try {
+ var dnsComp = Components.classes["@mozilla.org/network/dns-service;1"];
+ this._dnsSvc = dnsComp.getService(Components.interfaces.nsIDNSService);
+
+ // Get local hostname.
+ if ("myHostName" in this._dnsSvc) {
+ // Using newer (1.7a+) version with DNS re-write.
+ this.addHost(this._dnsSvc.myHostName);
+ }
+ if ("myIPAddress" in this._dnsSvc) {
+ // Older Mozilla, have to use this method.
+ this.addIP(this._dnsSvc.myIPAddress);
+ }
+ this.addHost("localhost");
+ } catch(ex) {
+ // what to do?
+ dd("Error getting local IPs: " + ex);
+ }
+
+ this._lastID = Math.round(Math.random() * DCC_ID_MAX);
+
+ return this;
+}
+
+CIRCDCC.prototype.TYPE = "IRCDCC";
+CIRCDCC.prototype.listenPorts = [];
+
+CIRCDCC.prototype.addUser =
+function dcc_adduser(user, remoteIP)
+{
+ // user == CIRCUser object.
+ // remoteIP == remoteIP as specified in CTCP DCC message.
+ return new CIRCDCCUser(this, user, remoteIP);
+}
+
+CIRCDCC.prototype.addChat =
+function dcc_addchat(user, port)
+{
+ // user == CIRCDCCUser object.
+ // port == port as specified in CTCP DCC message.
+ return new CIRCDCCChat(this, user, port);
+}
+
+CIRCDCC.prototype.addFileTransfer =
+function dcc_addfile(user, port, file, size)
+{
+ return new CIRCDCCFileTransfer(this, user, port, file, size);
+}
+
+CIRCDCC.prototype.addHost =
+function dcc_addhost(host, auth)
+{
+ var me = this;
+ var listener = {
+ onLookupComplete: function _onLookupComplete(request, record, status) {
+ // record == null if it failed. We can't do anything with a failure.
+ if (record)
+ {
+ while (record.hasMore())
+ me.addIP(record.getNextAddrAsString(), auth);
+ }
+ }
+ };
+
+ try {
+ var th = getService("@mozilla.org/thread-manager;1").currentThread;
+ var dnsRecord = this._dnsSvc.asyncResolve(host, false, listener, th);
+ } catch (ex) {
+ dd("Error resolving host to IP: " + ex);
+ }
+}
+
+CIRCDCC.prototype.addIP =
+function dcc_addip(ip, auth)
+{
+ if (auth)
+ this.localIPlist.unshift(ip);
+ else
+ this.localIPlist.push(ip);
+
+ if (this.localIPlist.length > 0)
+ this.localIP = this.localIPlist[0];
+}
+
+CIRCDCC.prototype.getMatches =
+function dcc_getmatches(nickname, filename, types, dirs, states)
+{
+ function matchNames(name, otherName)
+ {
+ return ((name.match(new RegExp(otherName, "i"))) ||
+ (name.toLowerCase().indexOf(otherName.toLowerCase()) != -1));
+ };
+
+ var k;
+ var list = new Array();
+ if (!types)
+ types = ["chat", "file"];
+
+ var n = nickname;
+ var f = filename;
+
+ if (arrayIndexOf(types, "chat") >= 0)
+ {
+ for (k = 0; k < this.chats.length; k++)
+ {
+ if ((!nickname || matchNames(this.chats[k].user.unicodeName, n)) &&
+ (!dirs || arrayIndexOf(dirs, this.chats[k].state.dir) >= 0) &&
+ (!states || arrayIndexOf(states, this.chats[k].state.state) >= 0))
+ {
+ list.push(this.chats[k]);
+ }
+ }
+ }
+ if (arrayIndexOf(types, "file") >= 0)
+ {
+ for (k = 0; k < this.files.length; k++)
+ {
+ if ((!nickname || matchNames(this.files[k].user.unicodeName, n)) &&
+ (!filename || matchNames(this.files[k].filename, f)) &&
+ (!dirs || arrayIndexOf(dirs, this.files[k].state.dir) >= 0) &&
+ (!states || arrayIndexOf(states, this.files[k].state.state) >= 0))
+ {
+ list.push(this.files[k]);
+ }
+ }
+ }
+
+ return list;
+}
+
+CIRCDCC.prototype.getNextPort =
+function dcc_getnextport()
+{
+ var portList = this.listenPorts;
+
+ var newPort = this._lastPort;
+
+ for (var i = 0; i < portList.length; i++)
+ {
+ var m = portList[i].match(/^(\d+)(?:-(\d+))?$/);
+ if (m)
+ {
+ if (!newPort)
+ {
+ // We dodn't have any previous port, so just take the first we
+ // find.
+ return this._lastPort = Number(m[1]);
+ }
+ else if (arrayHasElementAt(m, 2))
+ {
+ // Port range. Anything before range, or in [exl. last value]
+ // is ok. Make sure first value is lowest value returned.
+ if (newPort < m[2])
+ return this._lastPort = Math.max(newPort + 1, Number(m[1]));
+ }
+ else
+ {
+ // Single port.
+ if (newPort < m[1])
+ return this._lastPort = Number(m[1]);
+ }
+ }
+ }
+
+ // No ports found, and no last port --> use OS.
+ if (newPort == null)
+ return -1;
+
+ // Didn't find anything... d'oh. Need to start from the begining again.
+ this._lastPort = null;
+ return this.getNextPort();
+}
+
+CIRCDCC.prototype.getNextID =
+function dcc_getnextid()
+{
+ this._lastID++;
+ if (this._lastID > DCC_ID_MAX)
+ this._lastID = 0;
+
+ // Format to DCC_ID_MAX's number of digits.
+ var id = this._lastID.toString(16);
+ while (id.length < DCC_ID_MAX.toString(16).length)
+ id = "0" + id;
+ return id;
+}
+
+CIRCDCC.prototype.findByID =
+function dcc_findbyid(id)
+{
+ if (typeof id != "string")
+ return null;
+
+ var i;
+ for (i = 0; i < this.chats.length; i++)
+ {
+ if (this.chats[i].id == id)
+ return this.chats[i];
+ }
+ for (i = 0; i < this.files.length; i++)
+ {
+ if (this.files[i].id == id)
+ return this.files[i];
+ }
+ return null;
+}
+
+
+// JavaScript won't let you delete things declared with "var", workaround:
+window.val = -1;
+
+const DCC_STATE_FAILED = val++; // try connect (accept), but it failed.
+const DCC_STATE_INIT = val++; // not "doing" anything
+const DCC_STATE_REQUESTED = val++; // waiting
+const DCC_STATE_ACCEPTED = val++; // accepted.
+const DCC_STATE_DECLINED = val++; // declined.
+const DCC_STATE_CONNECTED = val++; // all going ok.
+const DCC_STATE_DONE = val++; // finished ok.
+const DCC_STATE_ABORTED = val++; // send wasn't accepted in time.
+
+delete window.val;
+
+const DCC_DIR_UNKNOWN = 0;
+const DCC_DIR_SENDING = 1;
+const DCC_DIR_GETTING = 2;
+
+
+function CIRCDCCUser(parent, user, remoteIP)
+{
+ // user == CIRCUser object.
+ // remoteIP == remoteIP as specified in CTCP DCC message.
+
+ if ("dccUser" in user)
+ {
+ if (remoteIP)
+ user.dccUser.remoteIP = remoteIP;
+
+ return user.dccUser;
+ }
+
+ this.parent = parent;
+ this.netUser = user;
+ this.id = parent.getNextID();
+ this.unicodeName = user.unicodeName;
+ this.viewName = user.unicodeName;
+ this.canonicalName = user.collectionKey.substr(1);
+ this.remoteIP = remoteIP;
+
+ this.key = escape(user.collectionKey) + ":" + remoteIP;
+ user.dccUser = this;
+ this.parent.users[this.key] = this;
+
+ if ("onInit" in this)
+ this.onInit();
+
+ return this;
+}
+
+CIRCDCCUser.prototype.TYPE = "IRCDCCUser";
+
+
+// Keeps track of the state of a DCC connection.
+function CIRCDCCState(parent, owner, eventType)
+{
+ // parent == central CIRCDCC object.
+ // owner == DCC Chat or File object.
+ // eventType == "dcc-chat" or "dcc-file".
+
+ this.parent = parent;
+ this.owner = owner;
+ this.eventType = eventType;
+
+ this.eventPump = owner.eventPump;
+
+ this.state = DCC_STATE_INIT;
+ this.dir = DCC_DIR_UNKNOWN;
+
+ return this;
+}
+
+CIRCDCCState.prototype.TYPE = "IRCDCCState";
+
+CIRCDCCState.prototype.sendRequest =
+function dccstate_sendRequest()
+{
+ if (!this.parent.localIP || (this.state != DCC_STATE_INIT))
+ throw "Must have a local IP and be in INIT state.";
+
+ this.state = DCC_STATE_REQUESTED;
+ this.dir = DCC_DIR_SENDING;
+ this.requested = new Date();
+
+ this.requestTimeout = setTimeout(function (o){ o.abort(); },
+ this.parent.requestTimeout, this.owner);
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "request",
+ this.owner, "onRequest"));
+}
+
+CIRCDCCState.prototype.getRequest =
+function dccstate_getRequest()
+{
+ if (this.state != DCC_STATE_INIT)
+ throw "Must be in INIT state.";
+
+ this.state = DCC_STATE_REQUESTED;
+ this.dir = DCC_DIR_GETTING;
+ this.requested = new Date();
+
+ this.parent.last = this.owner;
+ this.parent.lastTime = new Date();
+
+ this.requestTimeout = setTimeout(function (o){ o.abort(); },
+ this.parent.requestTimeout, this.owner);
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "request",
+ this.owner, "onRequest"));
+}
+
+CIRCDCCState.prototype.sendAccept =
+function dccstate_sendAccept()
+{
+ if ((this.state != DCC_STATE_REQUESTED) || (this.dir != DCC_DIR_GETTING))
+ throw "Must be in REQUESTED state and direction GET.";
+
+ // Clear out "last" incoming request if that's us.
+ if (this.parent.last == this.owner)
+ {
+ this.parent.last = null;
+ this.parent.lastTime = null;
+ }
+
+ clearTimeout(this.requestTimeout);
+ delete this.requestTimeout;
+
+ this.state = DCC_STATE_ACCEPTED;
+ this.accepted = new Date();
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "accept",
+ this.owner, "onAccept"));
+}
+
+CIRCDCCState.prototype.getAccept =
+function dccstate_getAccept()
+{
+ if ((this.state != DCC_STATE_REQUESTED) || (this.dir != DCC_DIR_SENDING))
+ throw "Must be in REQUESTED state and direction SEND.";
+
+ clearTimeout(this.requestTimeout);
+ delete this.requestTimeout;
+
+ this.state = DCC_STATE_ACCEPTED;
+ this.accepted = new Date();
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "accept",
+ this.owner, "onAccept"));
+}
+
+CIRCDCCState.prototype.sendDecline =
+function dccstate_sendDecline()
+{
+ if ((this.state != DCC_STATE_REQUESTED) || (this.dir != DCC_DIR_GETTING))
+ throw "Must be in REQUESTED state and direction GET.";
+
+ // Clear out "last" incoming request if that's us.
+ if (this.parent.last == this.owner)
+ {
+ this.parent.last = null;
+ this.parent.lastTime = null;
+ }
+
+ clearTimeout(this.requestTimeout);
+ delete this.requestTimeout;
+
+ this.state = DCC_STATE_DECLINED;
+ this.declined = new Date();
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "decline",
+ this.owner, "onDecline"));
+}
+
+CIRCDCCState.prototype.getDecline =
+function dccstate_getDecline()
+{
+ if ((this.state != DCC_STATE_REQUESTED) || (this.dir != DCC_DIR_SENDING))
+ throw "Must be in REQUESTED state and direction SEND.";
+
+ clearTimeout(this.requestTimeout);
+ delete this.requestTimeout;
+
+ this.state = DCC_STATE_DECLINED;
+ this.declined = new Date();
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "decline",
+ this.owner, "onDecline"));
+}
+
+// The sockets connected.
+CIRCDCCState.prototype.socketConnected =
+function dccstate_socketConnected()
+{
+ if (this.state != DCC_STATE_ACCEPTED)
+ throw "Not in ACCEPTED state.";
+
+ this.state = DCC_STATE_CONNECTED;
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "connect",
+ this.owner, "onConnect"));
+}
+
+// Someone disconnected something.
+CIRCDCCState.prototype.socketDisconnected =
+function dccstate_socketDisconnected()
+{
+ if (this.state != DCC_STATE_CONNECTED)
+ throw "Not CONNECTED!";
+
+ this.state = DCC_STATE_DONE;
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "disconnect",
+ this.owner, "onDisconnect"));
+}
+
+CIRCDCCState.prototype.sendAbort =
+function dccstate_sendAbort()
+{
+ if ((this.state != DCC_STATE_REQUESTED) &&
+ (this.state != DCC_STATE_ACCEPTED) &&
+ (this.state != DCC_STATE_CONNECTED))
+ {
+ throw "Can't abort at this point.";
+ }
+
+ this.state = DCC_STATE_ABORTED;
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "abort",
+ this.owner, "onAbort"));
+}
+
+CIRCDCCState.prototype.getAbort =
+function dccstate_getAbort()
+{
+ if ((this.state != DCC_STATE_REQUESTED) &&
+ (this.state != DCC_STATE_ACCEPTED) &&
+ (this.state != DCC_STATE_CONNECTED))
+ {
+ throw "Can't abort at this point.";
+ }
+
+ this.state = DCC_STATE_ABORTED;
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "abort",
+ this.owner, "onAbort"));
+}
+
+CIRCDCCState.prototype.failed =
+function dccstate_failed()
+{
+ if ((this.state != DCC_STATE_REQUESTED) &&
+ (this.state != DCC_STATE_ACCEPTED) &&
+ (this.state != DCC_STATE_CONNECTED))
+ {
+ throw "Can't fail at this point.";
+ }
+
+ this.state = DCC_STATE_FAILED;
+
+ this.eventPump.addEvent(new CEvent(this.eventType, "fail",
+ this.owner, "onFail"));
+}
+
+
+
+
+function CIRCDCCChat(parent, user, port)
+{
+ // user == CIRCDCCUser object.
+ // port == port as specified in CTCP DCC message.
+
+ this.READ_TIMEOUT = 50;
+
+ // Link up all our data.
+ this.parent = parent;
+ this.id = parent.getNextID();
+ this.eventPump = parent.parent.eventPump;
+ this.user = user;
+ this.localIP = this.parent.localIP;
+ this.remoteIP = user.remoteIP;
+ this.port = port;
+ this.unicodeName = user.unicodeName;
+ this.viewName = "DCC: " + user.unicodeName;
+
+ // Set up the initial state.
+ this.requested = null;
+ this.connection = null;
+ this.savedLine = "";
+ this.state = new CIRCDCCState(parent, this, "dcc-chat");
+ this.parent.chats.push(this);
+
+ // Give ourselves a "me" object for the purposes of displaying stuff.
+ this.me = this.parent.addUser(this.user.netUser.parent.me, "0.0.0.0");
+
+ if ("onInit" in this)
+ this.onInit();
+
+ return this;
+}
+
+CIRCDCCChat.prototype.TYPE = "IRCDCCChat";
+
+CIRCDCCChat.prototype.getURL =
+function dchat_geturl()
+{
+ return "x-irc-dcc-chat:" + this.id;
+}
+
+CIRCDCCChat.prototype.isActive =
+function dchat_isactive()
+{
+ return (this.state.state == DCC_STATE_REQUESTED) ||
+ (this.state.state == DCC_STATE_ACCEPTED) ||
+ (this.state.state == DCC_STATE_CONNECTED);
+}
+
+// Call to make this end request DCC Chat with targeted user.
+CIRCDCCChat.prototype.request =
+function dchat_request()
+{
+ this.state.sendRequest();
+
+ this.localIP = this.parent.localIP;
+
+ this.connection = new CBSConnection();
+ if (!this.connection.listen(this.port, this))
+ {
+ this.state.failed();
+ return false;
+ }
+
+ this.port = this.connection.port;
+
+ // Send the CTCP DCC request via the net user (CIRCUser object).
+ var ipNumber;
+ var ipParts = this.localIP.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
+ if (ipParts)
+ ipNumber = Number(ipParts[1]) * 256 * 256 * 256 +
+ Number(ipParts[2]) * 256 * 256 +
+ Number(ipParts[3]) * 256 +
+ Number(ipParts[4]);
+ else
+ return false;
+ // What should we do here? Panic?
+
+ this.user.netUser.ctcp("DCC", "CHAT chat " + ipNumber + " " + this.port);
+
+ return true;
+}
+
+// Call to make this end accept DCC Chat with target user.
+CIRCDCCChat.prototype.accept =
+function dchat_accept()
+{
+ this.state.sendAccept();
+
+ this.connection = new CBSConnection();
+ this.connection.connect(this.remoteIP, this.port, null, this);
+
+ return true;
+}
+
+// This may be called synchronously or asynchronously by CBSConnection.connect.
+CIRCDCCChat.prototype.onSocketConnection =
+function dchat_onsocketconnection(host, port, config, exception)
+{
+ if (!exception)
+ {
+ this.state.socketConnected();
+
+ this.connection.startAsyncRead(this);
+ }
+ else
+ {
+ this.state.failed();
+ }
+}
+
+// Call to make this end decline DCC Chat with target user.
+CIRCDCCChat.prototype.decline =
+function dchat_decline()
+{
+ this.state.sendDecline();
+
+ // Tell the other end, if they care, that we refused.
+ this.user.netUser.ctcp("DCC", "REJECT CHAT chat");
+
+ return true;
+}
+
+// Call to close the connection.
+CIRCDCCChat.prototype.disconnect =
+function dchat_disconnect()
+{
+ this.connection.disconnect();
+
+ return true;
+}
+
+// Aborts the connection.
+CIRCDCCChat.prototype.abort =
+function dchat_abort()
+{
+ if (this.state.state == DCC_STATE_CONNECTED)
+ {
+ this.disconnect();
+ return;
+ }
+
+ this.state.sendAbort();
+
+ if (this.connection)
+ this.connection.close();
+}
+
+// Event to handle a request from the target user.
+// CIRCUser points the event here.
+CIRCDCCChat.prototype.onGotRequest =
+function dchat_onGotRequest(e)
+{
+ this.state.getRequest();
+
+ // Pass over to the base user.
+ e.destObject = this.user.netUser;
+}
+
+// Event to handle a client connecting to the listening socket.
+// CBSConnection points the event here.
+CIRCDCCChat.prototype.onSocketAccepted =
+function dchat_onSocketAccepted(socket, transport)
+{
+ this.state.getAccept();
+
+ this.connection.accept(transport, null);
+
+ this.state.socketConnected();
+
+ this.remoteIP = transport.host;
+
+ // Start the reading!
+ this.connection.startAsyncRead(this);
+}
+
+CIRCDCCChat.prototype.onStreamDataAvailable =
+function dchat_sda(request, inStream, sourceOffset, count)
+{
+ var ev = new CEvent("dcc-chat", "data-available", this, "onDataAvailable");
+ ev.line = this.connection.readData(0, count);
+ this.eventPump.routeEvent(ev);
+}
+
+CIRCDCCChat.prototype.onStreamClose =
+function dchat_sockdiscon(status)
+{
+ this.state.socketDisconnected();
+
+ //var ev = new CEvent("dcc-chat", "disconnect", this, "onDisconnect");
+ //ev.server = this;
+ //ev.disconnectStatus = status;
+ //this.eventPump.addEvent(ev);
+}
+
+CIRCDCCChat.prototype.onDataAvailable =
+function dchat_dataavailable(e)
+{
+ var line = e.line;
+
+ var incomplete = (line[line.length] != '\n');
+ var lines = line.split("\n");
+
+ if (this.savedLine)
+ {
+ lines[0] = this.savedLine + lines[0];
+ this.savedLine = "";
+ }
+
+ if (incomplete)
+ this.savedLine = lines.pop();
+
+ for (var i in lines)
+ {
+ var ev = new CEvent("dcc-chat", "rawdata", this, "onRawData");
+ ev.data = lines[i];
+ ev.replyTo = this;
+ this.eventPump.addEvent (ev);
+ }
+
+ return true;
+}
+
+// Raw data from DCC Chat stream.
+CIRCDCCChat.prototype.onRawData =
+function dchat_rawdata(e)
+{
+ e.code = "PRIVMSG";
+ e.line = e.data;
+ e.user = this.user;
+ e.type = "parseddata";
+ e.destObject = this;
+ e.destMethod = "onParsedData";
+
+ return true;
+}
+
+CIRCDCCChat.prototype.onParsedData =
+function dchat_onParsedData(e)
+{
+ e.type = e.code.toLowerCase();
+ if (!e.code[0])
+ {
+ dd (dumpObjectTree (e));
+ return false;
+ }
+
+ if (e.line.search(/\x01.*\x01/i) != -1) {
+ e.type = "ctcp";
+ e.destMethod = "onCTCP";
+ e.set = "dcc-chat";
+ e.destObject = this;
+ }
+ else
+ {
+ e.type = "privmsg";
+ e.destMethod = "onPrivmsg";
+ e.set = "dcc-chat";
+ e.destObject = this;
+ }
+
+ // Allow DCC Chat to handle it before "falling back" to DCC User.
+ if (typeof this[e.destMethod] == "function")
+ e.destObject = this;
+ else if (typeof this.user[e.destMethod] == "function")
+ e.destObject = this.user;
+ else if (typeof this["onUnknown"] == "function")
+ e.destMethod = "onUnknown";
+ else if (typeof this.parent[e.destMethod] == "function")
+ {
+ e.set = "dcc";
+ e.destObject = this.parent;
+ }
+ else
+ {
+ e.set = "dcc";
+ e.destObject = this.parent;
+ e.destMethod = "onUnknown";
+ }
+
+ return true;
+}
+
+CIRCDCCChat.prototype.onCTCP =
+function serv_ctcp(e)
+{
+ // The \x0D? is a BIG HACK to make this work with X-Chat.
+ var ary = e.line.match(/^\x01([^ ]+) ?(.*)\x01\x0D?$/i);
+ if (ary == null)
+ return false;
+
+ e.CTCPData = ary[2] ? ary[2] : "";
+ e.CTCPCode = ary[1].toLowerCase();
+ if (e.CTCPCode.search(/^reply/i) == 0)
+ {
+ dd("dropping spoofed reply.");
+ return false;
+ }
+
+ e.CTCPCode = toUnicode(e.CTCPCode, e.replyTo);
+ e.CTCPData = toUnicode(e.CTCPData, e.replyTo);
+
+ e.type = "ctcp-" + e.CTCPCode;
+ e.destMethod = "onCTCP" + ary[1][0].toUpperCase() +
+ ary[1].substr(1, ary[1].length).toLowerCase();
+
+ if (typeof this[e.destMethod] != "function")
+ {
+ e.destObject = e.user;
+ e.set = "dcc-user";
+ if (typeof e.user[e.destMethod] != "function")
+ {
+ e.type = "unk-ctcp";
+ e.destMethod = "onUnknownCTCP";
+ }
+ }
+ else
+ {
+ e.destObject = this;
+ }
+ return true;
+}
+
+CIRCDCCChat.prototype.ctcp =
+function dchat_ctcp(code, msg)
+{
+ msg = msg || "";
+
+ this.connection.sendData("\x01" + fromUnicode(code, this) + " " +
+ fromUnicode(msg, this) + "\x01\n");
+}
+
+CIRCDCCChat.prototype.say =
+function dchat_say (msg)
+{
+ this.connection.sendData(fromUnicode(msg, this) + "\n");
+}
+
+CIRCDCCChat.prototype.act =
+function dchat_act(msg)
+{
+ this.ctcp("ACTION", msg);
+}
+
+
+function CIRCDCCFileTransfer(parent, user, port, file, size)
+{
+ // user == CIRCDCCUser object.
+ // port == port as specified in CTCP DCC message.
+ // file == name of file being sent/got.
+ // size == size of said file.
+
+ this.READ_TIMEOUT = 50;
+
+ // Link up all our data.
+ this.parent = parent;
+ this.id = parent.getNextID();
+ this.eventPump = parent.parent.eventPump;
+ this.user = user;
+ this.localIP = this.parent.localIP;
+ this.remoteIP = user.remoteIP;
+ this.port = port;
+ this.filename = file;
+ this.size = size;
+ this.unicodeName = user.unicodeName;
+ this.viewName = "File: " + this.filename;
+
+ // Set up the initial state.
+ this.requested = null;
+ this.connection = null;
+ this.state = new CIRCDCCState(parent, this, "dcc-file");
+ this.parent.files.push(this);
+
+ // Give ourselves a "me" object for the purposes of displaying stuff.
+ this.me = this.parent.addUser(this.user.netUser.parent.me, "0.0.0.0");
+
+ if ("onInit" in this)
+ this.onInit();
+
+ return this;
+}
+
+CIRCDCCFileTransfer.prototype.TYPE = "IRCDCCFileTransfer";
+
+CIRCDCCFileTransfer.prototype.getURL =
+function dfile_geturl()
+{
+ return "x-irc-dcc-file:" + this.id;
+}
+
+CIRCDCCFileTransfer.prototype.isActive =
+function dfile_isactive()
+{
+ return (this.state.state == DCC_STATE_REQUESTED) ||
+ (this.state.state == DCC_STATE_ACCEPTED) ||
+ (this.state.state == DCC_STATE_CONNECTED);
+}
+
+CIRCDCCFileTransfer.prototype.dispose =
+function dfile_dispose()
+{
+ if (this.connection)
+ {
+ // close is for the server socket, disconnect for the client socket.
+ this.connection.close();
+ this.connection.disconnect();
+ }
+
+ if (this.localFile)
+ this.localFile.close();
+
+ this.connection = null;
+ this.localFile = null;
+ this.filestream = null;
+}
+
+// Call to make this end offer DCC File to targeted user.
+CIRCDCCFileTransfer.prototype.request =
+function dfile_request(localFile)
+{
+ this.state.sendRequest();
+
+ this.localFile = new LocalFile(localFile, "<");
+ this.filename = localFile.leafName;
+ this.size = localFile.fileSize;
+
+ // Update view name.
+ this.viewName = "File: " + this.filename;
+
+ // Double-quote file names with spaces.
+ // FIXME: Do we need any better checking?
+ if (this.filename.match(/ /))
+ this.filename = '"' + this.filename + '"';
+
+ this.localIP = this.parent.localIP;
+
+ this.connection = new CBSConnection(true);
+ if (!this.connection.listen(this.port, this))
+ {
+ this.state.failed();
+ this.dispose();
+ return false;
+ }
+
+ this.port = this.connection.port;
+
+ // Send the CTCP DCC request via the net user (CIRCUser object).
+ var ipNumber;
+ var ipParts = this.localIP.match(/(\d+)\.(\d+)\.(\d+)\.(\d+)/);
+ if (ipParts)
+ ipNumber = Number(ipParts[1]) * 256 * 256 * 256 +
+ Number(ipParts[2]) * 256 * 256 +
+ Number(ipParts[3]) * 256 +
+ Number(ipParts[4]);
+ else
+ return false;
+ // What should we do here? Panic?
+
+ this.user.netUser.ctcp("DCC", "SEND " + this.filename + " " +
+ ipNumber + " " + this.port + " " + this.size);
+
+ return true;
+}
+
+// Call to make this end accept DCC File from target user.
+CIRCDCCFileTransfer.prototype.accept =
+function dfile_accept(localFile)
+{
+ const nsIBinaryOutputStream = Components.interfaces.nsIBinaryOutputStream;
+
+ this.state.sendAccept();
+
+ this.localFile = new LocalFile(localFile, ">");
+ this.localPath = localFile.path;
+
+ this.filestream = Components.classes["@mozilla.org/binaryoutputstream;1"];
+ this.filestream = this.filestream.createInstance(nsIBinaryOutputStream);
+ this.filestream.setOutputStream(this.localFile.outputStream);
+
+ this.position = 0;
+ this.connection = new CBSConnection(true);
+ this.connection.connect(this.remoteIP, this.port, null, this);
+
+ return true;
+}
+
+// This may be called synchronously or asynchronously by CBSConnection.connect.
+CIRCDCCFileTransfer.prototype.onSocketConnection =
+function dfile_onsocketconnection(host, port, config, exception)
+{
+ if (!exception)
+ {
+ this.state.socketConnected();
+
+ this.connection.startAsyncRead(this);
+ }
+ else
+ {
+ this.state.failed();
+ this.dispose();
+ }
+}
+
+// Call to make this end decline DCC File from target user.
+CIRCDCCFileTransfer.prototype.decline =
+function dfile_decline()
+{
+ this.state.sendDecline();
+
+ // Tell the other end, if they care, that we refused.
+ this.user.netUser.ctcp("DCC", "REJECT FILE " + this.filename);
+
+ return true;
+}
+
+// Call to close the connection.
+CIRCDCCFileTransfer.prototype.disconnect =
+function dfile_disconnect()
+{
+ this.dispose();
+
+ return true;
+}
+
+// Aborts the connection.
+CIRCDCCFileTransfer.prototype.abort =
+function dfile_abort()
+{
+ if (this.state.state == DCC_STATE_CONNECTED)
+ {
+ this.disconnect();
+ return;
+ }
+
+ this.state.sendAbort();
+ this.dispose();
+}
+
+// Event to handle a request from the target user.
+// CIRCUser points the event here.
+CIRCDCCFileTransfer.prototype.onGotRequest =
+function dfile_onGotRequest(e)
+{
+ this.state.getRequest();
+
+ // Pass over to the base user.
+ e.destObject = this.user.netUser;
+}
+
+// Event to handle a client connecting to the listening socket.
+// CBSConnection points the event here.
+CIRCDCCFileTransfer.prototype.onSocketAccepted =
+function dfile_onSocketAccepted(socket, transport)
+{
+ this.state.getAccept();
+
+ this.connection.accept(transport, null);
+
+ this.state.socketConnected();
+
+ this.position = 0;
+ this.ackPosition = 0;
+ this.remoteIP = transport.host;
+
+ this.eventPump.addEvent(new CEvent("dcc-file", "connect",
+ this, "onConnect"));
+
+ try {
+ this.filestream = Components.classes["@mozilla.org/binaryinputstream;1"];
+ this.filestream = this.filestream.createInstance(nsIBinaryInputStream);
+ this.filestream.setInputStream(this.localFile.baseInputStream);
+
+ // Start the reading!
+ var d;
+ if (this.parent.sendChunk > this.size)
+ d = this.filestream.readBytes(this.size);
+ else
+ d = this.filestream.readBytes(this.parent.sendChunk);
+ this.position += d.length;
+ this.connection.sendData(d);
+ }
+ catch(ex)
+ {
+ dd(ex);
+ }
+
+ // Start the reading!
+ this.connection.startAsyncRead(this);
+}
+
+CIRCDCCFileTransfer.prototype.onStreamDataAvailable =
+function dfile_sda(request, inStream, sourceOffset, count)
+{
+ var ev = new CEvent("dcc-file", "data-available", this, "onDataAvailable");
+ ev.data = this.connection.readData(0, count);
+ this.eventPump.routeEvent(ev);
+}
+
+CIRCDCCFileTransfer.prototype.onStreamClose =
+function dfile_sockdiscon(status)
+{
+ if (this.position != this.size)
+ this.state.failed();
+ else
+ this.state.socketDisconnected();
+ this.dispose();
+}
+
+CIRCDCCFileTransfer.prototype.onDataAvailable =
+function dfile_dataavailable(e)
+{
+ e.type = "rawdata";
+ e.destMethod = "onRawData";
+
+ try
+ {
+ if (this.state.dir == DCC_DIR_SENDING)
+ {
+ while (e.data.length >= 4)
+ {
+ var word = e.data.substr(0, 4);
+ e.data = e.data.substr(4, e.data.length - 4);
+ var pos = word.charCodeAt(0) * 0x01000000
+ + word.charCodeAt(1) * 0x00010000
+ + word.charCodeAt(2) * 0x00000100
+ + word.charCodeAt(3) * 0x00000001;
+ this.ackPosition = pos;
+ }
+
+ while (this.position <= this.ackPosition + this.parent.maxUnAcked)
+ {
+ var d;
+ if (this.position + this.parent.sendChunk > this.size)
+ d = this.filestream.readBytes(this.size - this.position);
+ else
+ d = this.filestream.readBytes(this.parent.sendChunk);
+
+ this.position += d.length;
+ this.connection.sendData(d);
+
+ if (this.position >= this.size)
+ {
+ this.dispose();
+ break;
+ }
+ }
+ }
+ else if (this.state.dir == DCC_DIR_GETTING)
+ {
+ this.filestream.writeBytes(e.data, e.data.length);
+
+ // Send back ack data.
+ this.position += e.data.length;
+ var bytes = new Array();
+ for (var i = 0; i < 4; i++)
+ bytes.push(Math.floor(this.position / Math.pow(256, i)) & 255);
+
+ this.connection.sendData(String.fromCharCode(bytes[3], bytes[2],
+ bytes[1], bytes[0]));
+
+ if (this.size && (this.position >= this.size))
+ this.disconnect();
+ }
+ this.eventPump.addEvent(new CEvent("dcc-file", "progress",
+ this, "onProgress"));
+ }
+ catch(ex)
+ {
+ this.disconnect();
+ }
+
+ return true;
+}
+
+CIRCDCCFileTransfer.prototype.sendData =
+function dfile_say(data)
+{
+ this.connection.sendData(data);
+}
+
+CIRCDCCFileTransfer.prototype.size = 0;
+CIRCDCCFileTransfer.prototype.position = 0;
+CIRCDCCFileTransfer.prototype.__defineGetter__("progress",
+ function dfile_get_progress()
+ {
+ if (this.size > 0)
+ return Math.floor(100 * this.position / this.size);
+ return 0;
+ });
+
diff --git a/comm/suite/chatzilla/js/lib/events.js b/comm/suite/chatzilla/js/lib/events.js
new file mode 100644
index 0000000000..b48de11ede
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/events.js
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; 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/. */
+
+/**
+ * Event class for |CEventPump|.
+ */
+function CEvent (set, type, destObject, destMethod)
+{
+ this.set = set;
+ this.type = type;
+ this.destObject = destObject;
+ this.destMethod = destMethod;
+ this.hooks = new Array();
+
+}
+
+/**
+ * The event pump keeps a queue of pending events, processing them on-demand.
+ *
+ * You should never need to create an instance of this prototype; access the
+ * event pump through |client.eventPump|. Most code should only need to use the
+ * |addHook|, |getHook| and |removeHookByName| methods.
+ */
+function CEventPump (eventsPerStep)
+{
+ /* event routing stops after this many levels, safety valve */
+ this.MAX_EVENT_DEPTH = 50;
+ /* When there are this many 'used' items in a queue, always clean up. At
+ * this point it is MUCH more effecient to remove a block than a single
+ * item (i.e. removing 1000 is much much faster than removing 1 item 1000
+ * times [1]).
+ */
+ this.FORCE_CLEANUP_PTR = 1000;
+ /* If there are less than this many items in a queue, clean up. This keeps
+ * the queue empty normally, and is not that ineffecient [1].
+ */
+ this.MAX_AUTO_CLEANUP_LEN = 100;
+ this.eventsPerStep = eventsPerStep;
+ this.queue = new Array();
+ this.queuePointer = 0;
+ this.bulkQueue = new Array();
+ this.bulkQueuePointer = 0;
+ this.hooks = new Array();
+
+ /* [1] The delay when removing items from an array (with unshift or splice,
+ * and probably most operations) is NOT perportional to the number of items
+ * being removed, instead it is proportional to the number of items LEFT.
+ * Because of this, it is better to only remove small numbers of items when
+ * the queue is small (MAX_AUTO_CLEANUP_LEN), and when it is large remove
+ * only large chunks at a time (FORCE_CLEANUP_PTR), reducing the number of
+ * resizes being done.
+ */
+}
+
+CEventPump.prototype.onHook =
+function ep_hook(e, hooks)
+{
+ var h;
+
+ if (typeof hooks == "undefined")
+ hooks = this.hooks;
+
+ hook_loop:
+ for (h = hooks.length - 1; h >= 0; h--)
+ {
+ if (!hooks[h].enabled ||
+ !matchObject (e, hooks[h].pattern, hooks[h].neg))
+ continue hook_loop;
+
+ e.hooks.push(hooks[h]);
+ try
+ {
+ var rv = hooks[h].f(e);
+ }
+ catch(ex)
+ {
+ dd("hook #" + h + " '" +
+ ((typeof hooks[h].name != "undefined") ? hooks[h].name :
+ "") + "' had an error!");
+ dd(formatException(ex));
+ }
+ if ((typeof rv == "boolean") &&
+ (rv == false))
+ {
+ dd("hook #" + h + " '" +
+ ((typeof hooks[h].name != "undefined") ? hooks[h].name :
+ "") + "' stopped hook processing.");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Adds an event hook to be called when matching events are processed.
+ *
+ * All hooks should be given a meaningful name, to aid removal and debugging.
+ * For plugins, an ideal technique for the name is to use |plugin.id| as a
+ * prefix (e.g. <tt>plugin.id + "-my-super-hook"</tt>).
+ *
+ * @param f The function to call when an event matches |pattern|.
+ * @param name A unique name for the hook. Used for removing the hook and
+ * debugging.
+ * @param neg Optional. If specified with a |true| value, the hook will be
+ * called for events *not* matching |pattern|. Otherwise, the hook
+ * will be called for events matching |pattern|.
+ * @param enabled Optional. If specified, sets the initial enabled/disabled
+ * state of the hook. By default, hooks are enabled. See
+ * |getHook|.
+ * @param hooks Internal. Do not use.
+ */
+CEventPump.prototype.addHook =
+function ep_addhook(pattern, f, name, neg, enabled, hooks)
+{
+ if (typeof hooks == "undefined")
+ hooks = this.hooks;
+
+ if (typeof f != "function")
+ return false;
+
+ if (typeof enabled == "undefined")
+ enabled = true;
+ else
+ enabled = Boolean(enabled);
+
+ neg = Boolean(neg);
+
+ var hook = {
+ pattern: pattern,
+ f: f,
+ name: name,
+ neg: neg,
+ enabled: enabled
+ };
+
+ hooks.push(hook);
+
+ return hook;
+
+}
+
+/**
+ * Finds and returns data about a named event hook.
+ *
+ * You can use |getHook| to change the enabled state of an existing event hook:
+ * <tt>client.eventPump.getHook(myHookName).enabled = false;</tt>
+ * <tt>client.eventPump.getHook(myHookName).enabled = true;</tt>
+ *
+ * @param name The unique hook name to find and return data about.
+ * @param hooks Internal. Do not use.
+ * @returns If a match is found, an |Object| with properties matching the
+ * arguments to |addHook| is returned. Otherwise, |null| is returned.
+ */
+CEventPump.prototype.getHook =
+function ep_gethook(name, hooks)
+{
+ if (typeof hooks == "undefined")
+ hooks = this.hooks;
+
+ for (var h in hooks)
+ if (hooks[h].name.toLowerCase() == name.toLowerCase())
+ return hooks[h];
+
+ return null;
+
+}
+
+/**
+ * Removes an existing event hook by its name.
+ *
+ * @param name The unique hook name to find and remove.
+ * @param hooks Internal. Do not use.
+ * @returns |true| if the hook was found and removed, |false| otherwise.
+ */
+CEventPump.prototype.removeHookByName =
+function ep_remhookname(name, hooks)
+{
+ if (typeof hooks == "undefined")
+ hooks = this.hooks;
+
+ for (var h in hooks)
+ if (hooks[h].name.toLowerCase() == name.toLowerCase())
+ {
+ arrayRemoveAt (hooks, h);
+ return true;
+ }
+
+ return false;
+
+}
+
+CEventPump.prototype.removeHookByIndex =
+function ep_remhooki(idx, hooks)
+{
+ if (typeof hooks == "undefined")
+ hooks = this.hooks;
+
+ return arrayRemoveAt (hooks, idx);
+
+}
+
+CEventPump.prototype.addEvent =
+function ep_addevent (e)
+{
+ e.queuedAt = new Date();
+ this.queue.push(e);
+ return true;
+}
+
+CEventPump.prototype.addBulkEvent =
+function ep_addevent (e)
+{
+ e.queuedAt = new Date();
+ this.bulkQueue.push(e);
+ return true;
+}
+
+CEventPump.prototype.routeEvent =
+function ep_routeevent (e)
+{
+ var count = 0;
+
+ this.currentEvent = e;
+
+ e.level = 0;
+ while (e.destObject)
+ {
+ e.level++;
+ this.onHook (e);
+ var destObject = e.destObject;
+ e.currentObject = destObject;
+ e.destObject = (void 0);
+
+ switch (typeof destObject[e.destMethod])
+ {
+ case "function":
+ if (1)
+ try
+ {
+ destObject[e.destMethod] (e);
+ }
+ catch (ex)
+ {
+ if (typeof ex == "string")
+ {
+ dd ("Error routing event " + e.set + "." +
+ e.type + ": " + ex);
+ }
+ else
+ {
+ dd ("Error routing event " + e.set + "." +
+ e.type + ": " + dumpObjectTree(ex) +
+ " in " + e.destMethod + "\n" + ex);
+ if ("stack" in ex)
+ dd(ex.stack);
+ }
+ }
+ else
+ destObject[e.destMethod] (e);
+
+ if (count++ > this.MAX_EVENT_DEPTH)
+ throw "Too many events in chain";
+ break;
+
+ case "undefined":
+ //dd ("** " + e.destMethod + " does not exist.");
+ break;
+
+ default:
+ dd ("** " + e.destMethod + " is not a function.");
+ }
+
+ if ((e.type != "event-end") && (!e.destObject))
+ {
+ e.lastSet = e.set;
+ e.set = "eventpump";
+ e.lastType = e.type;
+ e.type = "event-end";
+ e.destMethod = "onEventEnd";
+ e.destObject = this;
+ }
+
+ }
+
+ delete this.currentEvent;
+
+ return true;
+
+}
+
+CEventPump.prototype.stepEvents =
+function ep_stepevents()
+{
+ var i = 0;
+ var st, en, e;
+
+ st = new Date();
+ while (i < this.eventsPerStep)
+ {
+ if (this.queuePointer >= this.queue.length)
+ break;
+
+ e = this.queue[this.queuePointer++];
+
+ if (e.type == "yield")
+ break;
+
+ this.routeEvent(e);
+ i++;
+ }
+ while (i < this.eventsPerStep)
+ {
+ if (this.bulkQueuePointer >= this.bulkQueue.length)
+ break;
+
+ e = this.bulkQueue[this.bulkQueuePointer++];
+
+ if (e.type == "yield")
+ break;
+
+ this.routeEvent(e);
+ i++;
+ }
+ en = new Date();
+
+ // i == number of items handled this time.
+ // We only want to do this if we handled at least 25% of our step-limit
+ // and if we have a sane interval between st and en (not zero).
+ if ((i * 4 >= this.eventsPerStep) && (en - st > 0))
+ {
+ // Calculate the number of events that can be processed in 400ms.
+ var newVal = (400 * i) / (en - st);
+
+ // If anything skews it majorly, limit it to a minimum value.
+ if (newVal < 10)
+ newVal = 10;
+
+ // Adjust the step-limit based on this "target" limit, but only do a
+ // 25% change (underflow filter).
+ this.eventsPerStep += Math.round((newVal - this.eventsPerStep) / 4);
+ }
+
+ // Clean up if we've handled a lot, or the queue is small.
+ if ((this.queuePointer >= this.FORCE_CLEANUP_PTR) ||
+ (this.queue.length <= this.MAX_AUTO_CLEANUP_LEN))
+ {
+ this.queue.splice(0, this.queuePointer);
+ this.queuePointer = 0;
+ }
+
+ // Clean up if we've handled a lot, or the queue is small.
+ if ((this.bulkQueuePointer >= this.FORCE_CLEANUP_PTR) ||
+ (this.bulkQueue.length <= this.MAX_AUTO_CLEANUP_LEN))
+ {
+ this.bulkQueue.splice(0, this.bulkQueuePointer);
+ this.bulkQueuePointer = 0;
+ }
+
+ return i;
+
+}
diff --git a/comm/suite/chatzilla/js/lib/file-utils.js b/comm/suite/chatzilla/js/lib/file-utils.js
new file mode 100644
index 0000000000..d85edac8b6
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/file-utils.js
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; 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/. */
+
+/* notice that these valuse are octal. */
+const PERM_IRWXU = 0o700; /* read, write, execute/search by owner */
+const PERM_IRUSR = 0o400; /* read permission, owner */
+const PERM_IWUSR = 0o200; /* write permission, owner */
+const PERM_IXUSR = 0o100; /* execute/search permission, owner */
+const PERM_IRWXG = 0o070; /* read, write, execute/search by group */
+const PERM_IRGRP = 0o040; /* read permission, group */
+const PERM_IWGRP = 0o020; /* write permission, group */
+const PERM_IXGRP = 0o010; /* execute/search permission, group */
+const PERM_IRWXO = 0o007; /* read, write, execute/search by others */
+const PERM_IROTH = 0o004; /* read permission, others */
+const PERM_IWOTH = 0o002; /* write permission, others */
+const PERM_IXOTH = 0o001; /* execute/search permission, others */
+
+const MODE_RDONLY = 0x01;
+const MODE_WRONLY = 0x02;
+const MODE_RDWR = 0x04;
+const MODE_CREATE = 0x08;
+const MODE_APPEND = 0x10;
+const MODE_TRUNCATE = 0x20;
+const MODE_SYNC = 0x40;
+const MODE_EXCL = 0x80;
+
+const PICK_OK = Components.interfaces.nsIFilePicker.returnOK;
+const PICK_CANCEL = Components.interfaces.nsIFilePicker.returnCancel;
+const PICK_REPLACE = Components.interfaces.nsIFilePicker.returnReplace;
+
+const FILTER_ALL = Components.interfaces.nsIFilePicker.filterAll;
+const FILTER_HTML = Components.interfaces.nsIFilePicker.filterHTML;
+const FILTER_TEXT = Components.interfaces.nsIFilePicker.filterText;
+const FILTER_IMAGES = Components.interfaces.nsIFilePicker.filterImages;
+const FILTER_XML = Components.interfaces.nsIFilePicker.filterXML;
+const FILTER_XUL = Components.interfaces.nsIFilePicker.filterXUL;
+
+const FTYPE_DIR = Components.interfaces.nsIFile.DIRECTORY_TYPE;
+const FTYPE_FILE = Components.interfaces.nsIFile.NORMAL_FILE_TYPE;
+
+// evald f = fopen("/home/rginda/foo.txt", MODE_WRONLY | MODE_CREATE)
+// evald f = fopen("/home/rginda/vnk.txt", MODE_RDONLY)
+
+var futils = new Object();
+
+futils.umask = PERM_IWOTH | PERM_IWGRP;
+futils.MSG_SAVE_AS = "Save As";
+futils.MSG_OPEN = "Open";
+
+/**
+ * Internal function used by |pickSaveAs|, |pickOpen| and |pickGetFolder|.
+ *
+ * @param initialPath (*defaultDir* in |pick| functions) Sets the
+ * initial directory for the dialog. The user may browse
+ * to any other directory - it does not restrict anything.
+ * @param typeList Optional. An |Array| or space-separated string of allowed
+ * file types for the dialog. An item in the array may be a
+ * string (used as title and filter) or a two-element array
+ * (title and filter, respectively); when using a string,
+ * the following standard filters may be used: |$all|, |$html|,
+ * |$text|, |$images|, |$xml|, |$xul|, |$noAll| (prevents "All
+ * Files" filter being included).
+ * @param attribs Optional. Takes an object with either or both of the
+ * properties: |defaultString| (*defaultFile* in |pick|
+ * functions) sets the initial/default filename, and
+ * |defaultExtension| XXX FIXME (this seems wrong?) XXX.
+ * @returns An |Object| with |ok| (Boolean), |file| (|nsIFile|) and
+ * |picker| (|nsIFilePicker|) properties.
+ */
+futils.getPicker =
+function futils_nosepicker(initialPath, typeList, attribs)
+{
+ const classes = Components.classes;
+ const interfaces = Components.interfaces;
+
+ const PICKER_CTRID = "@mozilla.org/filepicker;1";
+ const LOCALFILE_CTRID = "@mozilla.org/file/local;1";
+
+ const nsIFilePicker = interfaces.nsIFilePicker;
+ const nsIFile = interfaces.nsIFile;
+
+ var picker = classes[PICKER_CTRID].createInstance(nsIFilePicker);
+ if (attribs)
+ {
+ if (typeof attribs == "object")
+ {
+ for (var a in attribs)
+ picker[a] = attribs[a];
+ }
+ else
+ {
+ throw "bad type for param |attribs|";
+ }
+ }
+
+ if (initialPath)
+ {
+ var localFile;
+
+ if (typeof initialPath == "string")
+ {
+ localFile =
+ classes[LOCALFILE_CTRID].createInstance(nsIFile);
+ localFile.initWithPath(initialPath);
+ }
+ else
+ {
+ if (!isinstance(initialPath, nsIFile))
+ throw "bad type for argument |initialPath|";
+
+ localFile = initialPath;
+ }
+
+ picker.displayDirectory = localFile
+ }
+
+ var allIncluded = false;
+
+ if (typeof typeList == "string")
+ typeList = typeList.split(" ");
+
+ if (isinstance(typeList, Array))
+ {
+ for (var i in typeList)
+ {
+ switch (typeList[i])
+ {
+ case "$all":
+ allIncluded = true;
+ picker.appendFilters(FILTER_ALL);
+ break;
+
+ case "$html":
+ picker.appendFilters(FILTER_HTML);
+ break;
+
+ case "$text":
+ picker.appendFilters(FILTER_TEXT);
+ break;
+
+ case "$images":
+ picker.appendFilters(FILTER_IMAGES);
+ break;
+
+ case "$xml":
+ picker.appendFilters(FILTER_XML);
+ break;
+
+ case "$xul":
+ picker.appendFilters(FILTER_XUL);
+ break;
+
+ case "$noAll":
+ // This prevents the automatic addition of "All Files"
+ // as a file type option by pretending it is already there.
+ allIncluded = true;
+ break;
+
+ default:
+ if ((typeof typeList[i] == "object") && isinstance(typeList[i], Array))
+ picker.appendFilter(typeList[i][0], typeList[i][1]);
+ else
+ picker.appendFilter(typeList[i], typeList[i]);
+ break;
+ }
+ }
+ }
+
+ if (!allIncluded)
+ picker.appendFilters(FILTER_ALL);
+
+ return picker;
+}
+
+function getPickerChoice(picker)
+{
+ var obj = new Object();
+ obj.picker = picker;
+ obj.ok = false;
+ obj.file = null;
+
+ try
+ {
+ obj.reason = picker.show();
+ }
+ catch (ex)
+ {
+ dd ("caught exception from file picker: " + ex);
+ return obj;
+ }
+
+ if (obj.reason != PICK_CANCEL)
+ {
+ obj.file = picker.file;
+ obj.ok = true;
+ }
+
+ return obj;
+}
+
+/**
+ * Displays a standard file save dialog.
+ *
+ * @param title Optional. The title for the dialog.
+ * @param typeList Optional. See |futils.getPicker| for details.
+ * @param defaultFile Optional. See |futils.getPicker| for details.
+ * @param defaultDir Optional. See |futils.getPicker| for details.
+ * @param defaultExt Optional. See |futils.getPicker| for details.
+ * @returns An |Object| with "ok" (Boolean), "file" (|nsIFile|) and
+ * "picker" (|nsIFilePicker|) properties.
+ */
+function pickSaveAs (title, typeList, defaultFile, defaultDir, defaultExt)
+{
+ if (!defaultDir && "lastSaveAsDir" in futils)
+ defaultDir = futils.lastSaveAsDir;
+
+ var picker = futils.getPicker (defaultDir, typeList,
+ {defaultString: defaultFile,
+ defaultExtension: defaultExt});
+ picker.init (window, title ? title : futils.MSG_SAVE_AS,
+ Components.interfaces.nsIFilePicker.modeSave);
+
+ var rv = getPickerChoice(picker);
+ if (rv.ok)
+ futils.lastSaveAsDir = picker.file.parent;
+
+ return rv;
+}
+
+/**
+ * Displays a standard file open dialog.
+ *
+ * @param title Optional. The title for the dialog.
+ * @param typeList Optional. See |futils.getPicker| for details.
+ * @param defaultFile Optional. See |futils.getPicker| for details.
+ * @param defaultDir Optional. See |futils.getPicker| for details.
+ * @returns An |Object| with "ok" (Boolean), "file" (|nsIFile|) and
+ * "picker" (|nsIFilePicker|) properties.
+ */
+function pickOpen (title, typeList, defaultFile, defaultDir)
+{
+ if (!defaultDir && "lastOpenDir" in futils)
+ defaultDir = futils.lastOpenDir;
+
+ var picker = futils.getPicker (defaultDir, typeList,
+ {defaultString: defaultFile});
+ picker.init (window, title ? title : futils.MSG_OPEN,
+ Components.interfaces.nsIFilePicker.modeOpen);
+
+ var rv = getPickerChoice(picker);
+ if (rv.ok)
+ futils.lastOpenDir = picker.file.parent;
+
+ return rv;
+}
+
+/**
+ * Displays a standard directory selection dialog.
+ *
+ * @param title Optional. The title for the dialog.
+ * @param defaultDir Optional. See |futils.getPicker| for details.
+ * @returns An |Object| with "ok" (Boolean), "file" (|nsIFile|) and
+ * "picker" (|nsIFilePicker|) properties.
+ */
+function pickGetFolder(title, defaultDir)
+{
+ if (!defaultDir && "lastOpenDir" in futils)
+ defaultDir = futils.lastOpenDir;
+
+ var picker = futils.getPicker(defaultDir);
+ picker.init(window, title ? title : futils.MSG_OPEN,
+ Components.interfaces.nsIFilePicker.modeGetFolder);
+
+ var rv = getPickerChoice(picker);
+ if (rv.ok)
+ futils.lastOpenDir = picker.file;
+
+ return rv;
+}
+
+function mkdir (localFile, perms)
+{
+ if (typeof perms == "undefined")
+ perms = 0o766 & ~futils.umask;
+
+ localFile.create(FTYPE_DIR, perms);
+}
+
+function getTempFile(path, name)
+{
+ var tempFile = new nsLocalFile(path);
+ tempFile.append(name);
+ tempFile.createUnique(0, 0o600);
+ return tempFile;
+}
+
+function nsLocalFile(path)
+{
+ const LOCALFILE_CTRID = "@mozilla.org/file/local;1";
+ const nsIFile = Components.interfaces.nsIFile;
+
+ var localFile =
+ Components.classes[LOCALFILE_CTRID].createInstance(nsIFile);
+ localFile.initWithPath(path);
+ return localFile;
+}
+
+function fopen (path, mode, perms, tmp)
+{
+ return new LocalFile(path, mode, perms, tmp);
+}
+
+function LocalFile(file, mode, perms, tmp)
+{
+ const classes = Components.classes;
+ const interfaces = Components.interfaces;
+
+ const LOCALFILE_CTRID = "@mozilla.org/file/local;1";
+ const FILEIN_CTRID = "@mozilla.org/network/file-input-stream;1";
+ const FILEOUT_CTRID = "@mozilla.org/network/file-output-stream;1";
+ const SCRIPTSTREAM_CTRID = "@mozilla.org/scriptableinputstream;1";
+
+ const nsIFile = interfaces.nsIFile;
+ const nsIFileOutputStream = interfaces.nsIFileOutputStream;
+ const nsIFileInputStream = interfaces.nsIFileInputStream;
+ const nsIScriptableInputStream = interfaces.nsIScriptableInputStream;
+
+ if (typeof perms == "undefined")
+ perms = 0o666 & ~futils.umask;
+
+ if (typeof mode == "string")
+ {
+ switch (mode)
+ {
+ case ">":
+ mode = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+ break;
+ case ">>":
+ mode = MODE_WRONLY | MODE_CREATE | MODE_APPEND;
+ break;
+ case "<":
+ mode = MODE_RDONLY;
+ break;
+ default:
+ throw "Invalid mode ``" + mode + "''";
+ }
+ }
+
+ if (typeof file == "string")
+ {
+ this.localFile = new nsLocalFile(file);
+ }
+ else if (isinstance(file, nsIFile))
+ {
+ this.localFile = file;
+ }
+ else
+ {
+ throw "bad type for argument |file|.";
+ }
+
+ this.path = this.localFile.path;
+
+ if (mode & (MODE_WRONLY | MODE_RDWR))
+ {
+ this.outputStream =
+ classes[FILEOUT_CTRID].createInstance(nsIFileOutputStream);
+ this.outputStream.init(this.localFile, mode, perms, 0);
+ }
+
+ if (mode & (MODE_RDONLY | MODE_RDWR))
+ {
+ this.baseInputStream =
+ classes[FILEIN_CTRID].createInstance(nsIFileInputStream);
+ this.baseInputStream.init(this.localFile, mode, perms, tmp);
+ this.inputStream =
+ classes[SCRIPTSTREAM_CTRID].createInstance(nsIScriptableInputStream);
+ this.inputStream.init(this.baseInputStream);
+ }
+}
+
+LocalFile.prototype.write =
+function fo_write(buf)
+{
+ if (!("outputStream" in this))
+ throw "file not open for writing.";
+
+ return this.outputStream.write(buf, buf.length);
+}
+
+// Will return null if there is no more data in the file.
+// Will block until it has some data to return.
+// Will return an empty string if there is data, but it couldn't be read.
+LocalFile.prototype.read =
+function fo_read(max)
+{
+ if (!("inputStream" in this))
+ throw "file not open for reading.";
+
+ if (typeof max == "undefined")
+ max = this.inputStream.available();
+
+ try
+ {
+ var rv = this.inputStream.read(max);
+ return (rv != "") ? rv : null;
+ }
+ catch (ex)
+ {
+ return "";
+ }
+}
+
+LocalFile.prototype.close =
+function fo_close()
+{
+ if ("outputStream" in this)
+ this.outputStream.close();
+ if ("inputStream" in this)
+ this.inputStream.close();
+}
+
+LocalFile.prototype.flush =
+function fo_close()
+{
+ return this.outputStream.flush();
+}
diff --git a/comm/suite/chatzilla/js/lib/http.js b/comm/suite/chatzilla/js/lib/http.js
new file mode 100644
index 0000000000..45ec3ddbbc
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/http.js
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function CHTTPDoc (server, path)
+{
+
+ this.GET_TIMEOUT = 2 * 60; /* 2 minute timeout on gets */
+ this.state = "new";
+ this.server = server;
+ this.path = path;
+
+}
+
+CHTTPDoc.prototype.get =
+function http_get (ep)
+{
+
+ this.connection = new CBSConnection();
+ if (!this.connection.connect (this.server, 80, (void 0), true))
+ {
+ this.state = "connect-error";
+ var e = new CEvent ("httpdoc", "complete", this, "onComplete");
+ this.eventPump.addEvent (e);
+ return false;
+ }
+
+ this.eventPump = ep;
+ this.state = "opened";
+ this.data = "";
+ this.duration = 0;
+
+ var e = new CEvent ("httpdoc", "poll", this, "onPoll");
+ this.eventPump.addEvent (e);
+
+ this.connection.sendData ("GET " + this.path + "\n");
+ this.startDate = new Date();
+
+ return true;
+
+}
+
+CHTTPDoc.prototype.onPoll =
+function http_poll (e)
+{
+ var line = "";
+ var ex, c;
+ var need_more = false;
+
+ if (this.duration < this.GET_TIMEOUT)
+ try
+ {
+ line = this.connection.readData (50);
+ need_more = true;
+ }
+ catch (ex)
+ {
+ if (typeof (ex) == "undefined")
+ {
+ line = "";
+ need_more = true;
+ }
+ else
+ {
+ dd ("** Caught exception: '" + ex + "' while receiving " +
+ this.server + this.path);
+ this.state = "read-error";
+ }
+ }
+ else
+ this.state = "receive-timeout";
+
+ switch (this.state)
+ {
+ case "opened":
+ case "receive-header":
+ if (this.state == "opened")
+ this.headers = "";
+
+ c = line.search(/\<html\>/i);
+ if (c != -1)
+ {
+ this.data = line.substr (c, line.length);
+ this.state = "receive-data";
+ line = line.substr (0, c);
+
+ c = this.data.search(/\<\/html\>/i);
+ if (c != -1)
+ {
+ this.docType = stringTrim(this.docType);
+ this.state = "complete";
+ need_more = false;
+ }
+
+ }
+
+ this.headers += line;
+ c = this.headers.search (/\<\!doctype/i);
+ if (c != -1)
+ {
+ this.docType = this.headers.substr (c, line.length);
+ if (this.state == "opened")
+ this.state = "receive-doctype";
+ this.headers = this.headers.substr (0, c);
+ }
+
+ if (this.state == "opened")
+ this.state = "receive-header";
+ break;
+
+ case "receive-doctype":
+ var c = line.search (/\<html\>/i);
+ if (c != -1)
+ {
+ this.docType = stringTrim(this.docType);
+ this.data = line.substr (c, line.length);
+ this.state = "receive-data";
+ line = line.substr (0, c);
+ }
+
+ this.docType += line;
+ break;
+
+ case "receive-data":
+ this.data += line;
+ var c = this.data.search(/\<\/html\>/i);
+ if (c != -1)
+ this.state = "complete";
+ break;
+
+ case "read-error":
+ case "receive-timeout":
+ break;
+
+ default:
+ dd ("** INVALID STATE in HTTPDoc object (" + this.state + ") **");
+ need_more = false;
+ this.state = "error";
+ break;
+
+ }
+
+ if ((this.state != "complete") && (need_more))
+ var e = new CEvent ("httpdoc", "poll", this, "onPoll");
+ else
+ {
+ this.connection.disconnect();
+ if (this.data)
+ {
+ var m = this.data.match(/\<title\>(.*)\<\/title\>/i);
+ if (m != null)
+ this.title = m[1];
+ else
+ this.title = "";
+ }
+ var e = new CEvent ("httpdoc", "complete", this, "onComplete");
+ }
+
+ this.eventPump.addEvent (e);
+ this.duration = (new Date() - this.startDate) / 1000;
+
+ return true;
+
+}
+
+CHTTPDoc.prototype.onComplete =
+function http_complete(e)
+{
+
+ return true;
+
+}
+
+
+
diff --git a/comm/suite/chatzilla/js/lib/ident.js b/comm/suite/chatzilla/js/lib/ident.js
new file mode 100644
index 0000000000..5110d70165
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/ident.js
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// RFC1413-ish identification server for ChatZilla
+// One Ident Server is used for all networks. When multiple networks are in the
+// process of connecting, it won't stop listening until they're all done.
+
+function IdentServer(parent)
+{
+ this.responses = new Array();
+ this.listening = false;
+ this.dns = getService("@mozilla.org/network/dns-service;1","nsIDNSService");
+
+ this.parent = parent;
+ this.eventPump = parent.eventPump;
+}
+
+IdentServer.prototype.start =
+function ident_start()
+{
+ if (this.listening)
+ return true;
+
+ if (!this.socket)
+ this.socket = new CBSConnection();
+
+ if (!this.socket.listen(113, this))
+ return false;
+
+ this.listening = true;
+ return true;
+}
+
+IdentServer.prototype.stop =
+function ident_stop()
+{
+ if (!this.socket || !this.listening)
+ return;
+
+ // No need to destroy socket, listen() will work again.
+ this.socket.close();
+ this.listening = false;
+}
+
+IdentServer.prototype.addNetwork =
+function ident_add(net, serv)
+{
+ var addr, dnsRecord = this.dns.resolve(serv.hostname, 0);
+
+ while (dnsRecord.hasMore())
+ {
+ addr = dnsRecord.getNextAddrAsString();
+ this.responses.push({net: net, ip: addr, port: serv.port,
+ username: net.INITIAL_NAME || net.INITIAL_NICK});
+ }
+
+ if (this.responses.length == 0)
+ return false;
+
+ // Start the ident server if necessary.
+ if (!this.listening)
+ return this.start();
+
+ return true;
+}
+
+IdentServer.prototype.removeNetwork =
+function ident_remove(net)
+{
+ var newResponses = new Array();
+
+ for (var i = 0; i < this.responses.length; ++i)
+ {
+ if (this.responses[i].net != net)
+ newResponses.push(this.responses[i]);
+ }
+ this.responses = newResponses;
+
+ // Only stop listening if responses is empty - no networks need us anymore.
+ if (this.responses.length == 0)
+ this.stop();
+}
+
+IdentServer.prototype.onSocketAccepted =
+function ident_gotconn(serv, transport)
+{
+ // Using the listening CBSConnection to accept would stop it listening.
+ // A new CBSConnection does exactly what we want.
+ var connection = new CBSConnection();
+ connection.accept(transport);
+
+ connection.startAsyncRead(new IdentListener(this, connection));
+}
+
+function IdentListener(server, connection)
+{
+ this.server = server;
+ this.connection = connection;
+}
+
+IdentListener.prototype.onStreamDataAvailable =
+function ident_listener_sda(request, inStream, sourceOffset, count)
+{
+ var ev = new CEvent("ident-listener", "data-available", this,
+ "onDataAvailable");
+ ev.line = this.connection.readData(0, count);
+ this.server.eventPump.routeEvent(ev);
+}
+
+IdentListener.prototype.onStreamClose =
+function ident_listener_sclose(status)
+{
+}
+
+IdentListener.prototype.onDataAvailable =
+function ident_listener_dataavailable(e)
+{
+ var incomplete = (e.line.substr(-2) != "\r\n");
+ var lines = e.line.split(/\r\n/);
+
+ if (this.savedLine)
+ {
+ lines[0] = this.savedLine + lines[0];
+ this.savedLine = "";
+ }
+
+ if (incomplete)
+ this.savedLine = lines.pop()
+
+ for (var i in lines)
+ {
+ var ev = new CEvent("ident-listener", "rawdata", this, "onRawData");
+ ev.line = lines[i];
+ this.server.eventPump.routeEvent(ev);
+ }
+}
+
+IdentListener.prototype.onRawData =
+function ident_listener_rawdata(e)
+{
+ var ports = e.line.match(/(\d+) *, *(\d+)/);
+ // <port-on-server> , <port-on-client>
+ // (where "server" is the ident server)
+
+ if (!ports)
+ {
+ this.connection.disconnect(); // same meaning as "ERROR : UNKNOWN-ERROR"
+ return;
+ }
+
+ e.type = "parsedrequest";
+ e.destObject = this;
+ e.destMethod = "onParsedRequest";
+ e.localPort = ports[1];
+ e.remotePort = ports[2];
+}
+
+IdentListener.prototype.onParsedRequest =
+function ident_listener_request(e)
+{
+ function response(str)
+ {
+ return e.localPort + " , " + e.remotePort + " : " + str + "\r\n";
+ };
+
+ function validPort(p)
+ {
+ return (p >= 1) && (p <= 65535);
+ };
+
+ if (!validPort(e.localPort) || !validPort(e.remotePort))
+ {
+ this.connection.sendData(response("ERROR : INVALID-PORT"));
+ this.connection.disconnect();
+ return;
+ }
+
+ var found, responses = this.server.responses;
+ for (var i = 0; i < responses.length; ++i)
+ {
+ if ((e.remotePort == responses[i].port) &&
+ (this.connection._transport.host == responses[i].ip))
+ {
+ // charset defaults to US-ASCII
+ // anything except an OS username should use OTHER
+ // however, ircu sucks, so we can't do that.
+ this.connection.sendData(response("USERID : CHATZILLA :" +
+ responses[i].username));
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ this.connection.sendData(response("ERROR : NO-USER"));
+
+ // Spec gives us a choice: drop the connection, or listen for more queries.
+ // Since IRC servers will only ever want one name, we disconnect.
+ this.connection.disconnect();
+}
diff --git a/comm/suite/chatzilla/js/lib/irc-debug.js b/comm/suite/chatzilla/js/lib/irc-debug.js
new file mode 100644
index 0000000000..b0e95fee2e
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/irc-debug.js
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Hook used to trace events.
+ */
+function event_tracer (e)
+{
+ var name = "";
+ var data = ("debug" in e) ? e.debug : "";
+
+ switch (e.set)
+ {
+ case "server":
+ name = e.destObject.connection.host;
+ if (e.type == "rawdata")
+ data = "'" + e.data + "'";
+ if (e.type == "senddata")
+ {
+ var nextLine =
+ e.destObject.sendQueue[e.destObject.sendQueue.length - 1];
+ if ("logged" in nextLine)
+ return true; /* don't print again */
+
+ if (nextLine) {
+ data = "'" + nextLine.replace ("\n", "\\n") + "'";
+ nextLine.logged = true;
+ }
+ else
+ data = "!!! Nothing to send !!!";
+ }
+ break;
+
+ case "network":
+ case "channel":
+ case "user":
+ name = e.destObject.unicodeName;
+ break;
+
+ case "httpdoc":
+ name = e.destObject.server + e.destObject.path;
+ if (e.destObject.state != "complete")
+ data = "state: '" + e.destObject.state + "', received " +
+ e.destObject.data.length;
+ else
+ dd ("document done:\n" + dumpObjectTree (this));
+ break;
+
+ case "dcc-chat":
+ case "dcc-file":
+ name = e.destObject.localIP + ":" + e.destObject.port;
+ if (e.type == "rawdata")
+ data = "'" + e.data + "'";
+ break;
+
+ case "client":
+ if (e.type == "do-connect")
+ data = "attempt: " + e.attempt + "/" +
+ e.destObject.MAX_CONNECT_ATTEMPTS;
+ break;
+
+ default:
+ break;
+ }
+
+ if (name)
+ name = "[" + name + "]";
+
+ if (e.type == "info")
+ data = "'" + e.msg + "'";
+
+ var str = "Level " + e.level + ": '" + e.type + "', " +
+ e.set + name + "." + e.destMethod;
+ if (data)
+ str += "\ndata : " + data;
+
+ dd (str);
+
+ return true;
+
+}
diff --git a/comm/suite/chatzilla/js/lib/irc.js b/comm/suite/chatzilla/js/lib/irc.js
new file mode 100644
index 0000000000..5e7d210c44
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/irc.js
@@ -0,0 +1,4518 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const JSIRC_ERR_NO_SOCKET = "JSIRCE:NS";
+const JSIRC_ERR_EXHAUSTED = "JSIRCE:E";
+const JSIRC_ERR_CANCELLED = "JSIRCE:C";
+const JSIRC_ERR_NO_SECURE = "JSIRCE:NO_SECURE";
+const JSIRC_ERR_OFFLINE = "JSIRCE:OFFLINE";
+const JSIRC_ERR_PAC_LOADING = "JSIRCE:PAC_LOADING";
+
+const JSIRCV3_SUPPORTED_CAPS = [
+ "account-notify",
+ "account-tag",
+ "away-notify",
+ "batch",
+ "cap-notify",
+ "chghost",
+ "echo-message",
+ "extended-join",
+ "invite-notify",
+ //"labeled-response",
+ "message-tags",
+ //"metadata",
+ "multi-prefix",
+ "sasl",
+ "server-time",
+ "tls",
+ "userhost-in-names",
+];
+
+function userIsMe (user)
+{
+
+ switch (user.TYPE)
+ {
+ case "IRCUser":
+ return (user == user.parent.me);
+ break;
+
+ case "IRCChanUser":
+ return (user.__proto__ == user.parent.parent.me);
+ break;
+
+ default:
+ return false;
+
+ }
+
+ return false;
+}
+
+/*
+ * Attached to event objects in onRawData
+ */
+function decodeParam(number, charsetOrObject)
+{
+ if (!charsetOrObject)
+ charsetOrObject = this.currentObject;
+
+ var rv = toUnicode(this.params[number], charsetOrObject);
+
+ return rv;
+}
+
+// JavaScript won't let you delete things declared with "var", workaround:
+window.i = 1;
+
+const NET_OFFLINE = i++; // Initial, disconected.
+const NET_WAITING = i++; // Waiting before trying.
+const NET_CONNECTING = i++; // Trying a connect...
+const NET_CANCELLING = i++; // Cancelling connect.
+const NET_ONLINE = i++; // Connected ok.
+const NET_DISCONNECTING = i++; // Disconnecting.
+
+delete window.i;
+
+function CIRCNetwork (name, serverList, eventPump, temporary)
+{
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.servers = new Object();
+ this.serverList = new Array();
+ this.ignoreList = new Object();
+ this.ignoreMaskCache = new Object();
+ this.state = NET_OFFLINE;
+ this.temporary = Boolean(temporary);
+
+ for (var i = 0; i < serverList.length; ++i)
+ {
+ var server = serverList[i];
+ var password = ("password" in server) ? server.password : null;
+ var isSecure = ("isSecure" in server) ? server.isSecure : false;
+ this.serverList.push(new CIRCServer(this, server.name, server.port, isSecure,
+ password));
+ }
+
+ this.eventPump = eventPump;
+ if ("onInit" in this)
+ this.onInit();
+}
+
+/** Clients should override this stuff themselves **/
+CIRCNetwork.prototype.INITIAL_NICK = "js-irc";
+CIRCNetwork.prototype.INITIAL_NAME = "INITIAL_NAME";
+CIRCNetwork.prototype.INITIAL_DESC = "INITIAL_DESC";
+CIRCNetwork.prototype.USE_SASL = false;
+CIRCNetwork.prototype.UPGRADE_INSECURE = false;
+CIRCNetwork.prototype.STS_MODULE = null;
+/* set INITIAL_CHANNEL to "" if you don't want a primary channel */
+CIRCNetwork.prototype.INITIAL_CHANNEL = "#jsbot";
+CIRCNetwork.prototype.INITIAL_UMODE = "+iw";
+
+CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = 5;
+CIRCNetwork.prototype.PAC_RECONNECT_DELAY = 5 * 1000;
+CIRCNetwork.prototype.getReconnectDelayMs = function() { return 15000; }
+CIRCNetwork.prototype.stayingPower = false;
+
+// "http" = use HTTP proxy, "none" = none, anything else = auto.
+CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE = "";
+
+CIRCNetwork.prototype.TYPE = "IRCNetwork";
+
+/**
+ * Returns the IRC URL representation of this network.
+ *
+ * @param target A network-specific object to target the URL at. Instead of
+ * passing it in here, call the target's |getURL| method.
+ * @param flags An |Object| with flags (as properties) to be applied to the URL.
+ */
+CIRCNetwork.prototype.getURL =
+function net_geturl(target, flags)
+{
+ if (this.temporary)
+ return this.serverList[0].getURL(target, flags);
+
+ /* Determine whether to use the irc:// or ircs:// scheme */
+ var scheme = "irc";
+ if ((("primServ" in this) && this.primServ.isConnected &&
+ this.primServ.isSecure) ||
+ this.hasOnlySecureServers())
+ {
+ scheme = "ircs"
+ }
+
+ var obj = {host: this.unicodeName, scheme: scheme};
+
+ if (target)
+ obj.target = target;
+
+ if (flags)
+ {
+ for (var i = 0; i < flags.length; i++)
+ obj[flags[i]] = true;
+ }
+
+ return constructIRCURL(obj);
+}
+
+CIRCNetwork.prototype.getUser =
+function net_getuser (nick)
+{
+ if ("primServ" in this && this.primServ)
+ return this.primServ.getUser(nick);
+
+ return null;
+}
+
+CIRCNetwork.prototype.addServer =
+function net_addsrv(host, port, isSecure, password)
+{
+ this.serverList.push(new CIRCServer(this, host, port, isSecure, password));
+}
+
+/**
+ * Returns |true| iif a network has a secure server in its list.
+ */
+CIRCNetwork.prototype.hasSecureServer =
+function net_hasSecure()
+{
+ for (var i = 0; i < this.serverList.length; i++)
+ {
+ if (this.serverList[i].isSecure)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Returns |true| iif a network only has secure servers in its list.
+ */
+CIRCNetwork.prototype.hasOnlySecureServers =
+function net_hasOnlySecure()
+{
+ for (var i = 0; i < this.serverList.length; i++)
+ {
+ if (!this.serverList[i].isSecure)
+ return false;
+ }
+
+ return true;
+}
+
+CIRCNetwork.prototype.clearServerList =
+function net_clearserverlist()
+{
+ /* Note: we don't have to worry about being connected, since primServ
+ * keeps the currently connected server alive if we still need it.
+ */
+ this.servers = new Object();
+ this.serverList = new Array();
+}
+
+/**
+ * Trigger an |onDoConnect| event after a delay.
+ */
+CIRCNetwork.prototype.delayedConnect =
+function net_delayedConnect(eventProperties)
+{
+ function reconnectFn(network, eventProperties)
+ {
+ network.immediateConnect(eventProperties);
+ };
+
+ if ((-1 != this.MAX_CONNECT_ATTEMPTS) &&
+ (this.connectAttempt >= this.MAX_CONNECT_ATTEMPTS))
+ {
+ this.state = NET_OFFLINE;
+
+ var ev = new CEvent("network", "error", this, "onError");
+ ev.debug = "Connection attempts exhausted, giving up.";
+ ev.errorCode = JSIRC_ERR_EXHAUSTED;
+ this.eventPump.addEvent(ev);
+
+ return;
+ }
+
+ this.state = NET_WAITING;
+ this.reconnectTimer = setTimeout(reconnectFn,
+ this.getReconnectDelayMs(),
+ this,
+ eventProperties);
+}
+
+/**
+ * Immediately trigger an |onDoConnect| event. Use |delayedConnect| for automatic
+ * repeat attempts, instead, to throttle the attempts to a reasonable pace.
+ */
+CIRCNetwork.prototype.immediateConnect =
+function net_immediateConnect(eventProperties)
+{
+ var ev = new CEvent("network", "do-connect", this, "onDoConnect");
+
+ if (typeof eventProperties != "undefined")
+ for (var key in eventProperties)
+ ev[key] = eventProperties[key];
+
+ this.eventPump.addEvent(ev);
+}
+
+CIRCNetwork.prototype.connect =
+function net_connect(requireSecurity)
+{
+ if ("primServ" in this && this.primServ.isConnected)
+ return true;
+
+ // We need to test for secure servers in the network object here,
+ // because without them all connection attempts will fail anyway.
+ if (requireSecurity && !this.hasSecureServer())
+ {
+ // No secure server, cope.
+ ev = new CEvent ("network", "error", this, "onError");
+ ev.server = this;
+ ev.debug = "No connection attempted: no secure servers in list";
+ ev.errorCode = JSIRC_ERR_NO_SECURE;
+ this.eventPump.addEvent(ev);
+
+ return false;
+ }
+
+ this.state = NET_CONNECTING;
+ this.connectAttempt = 0; // actual connection attempts
+ this.connectCandidate = 0; // incl. requireSecurity non-attempts
+ this.nextHost = 0;
+ this.requireSecurity = requireSecurity || false;
+ this.immediateConnect({"password": null});
+ return true;
+}
+
+/**
+ * Disconnects the network with a given reason.
+ */
+CIRCNetwork.prototype.quit =
+function net_quit (reason)
+{
+ if (this.isConnected())
+ this.primServ.logout(reason);
+}
+
+/**
+ * Cancels the network's connection (whatever its current state).
+ */
+CIRCNetwork.prototype.cancel =
+function net_cancel()
+{
+ // We're online, pull the plug on the current connection, or...
+ if (this.state == NET_ONLINE)
+ {
+ this.quit();
+ }
+ // We're waiting for the 001, too late to throw a reconnect, or...
+ else if (this.state == NET_CONNECTING)
+ {
+ this.state = NET_CANCELLING;
+ if ("primServ" in this && this.primServ.isConnected)
+ {
+ this.primServ.connection.disconnect();
+
+ var ev = new CEvent("network", "error", this, "onError");
+ ev.server = this.primServ;
+ ev.debug = "Connect sequence was canceled.";
+ ev.errorCode = JSIRC_ERR_CANCELLED;
+ this.eventPump.addEvent(ev);
+ }
+ }
+ // We're waiting for onDoConnect, so try a reconnect (which will fail us)
+ else if (this.state == NET_WAITING)
+ {
+ this.state = NET_CANCELLING;
+ // onDoConnect will throw the error events for us, as it will fail
+ this.immediateConnect();
+ }
+ else
+ {
+ dd("Network cancel in odd state: " + this.state);
+ }
+}
+
+CIRCNetwork.prototype.onDoConnect =
+function net_doconnect(e)
+{
+ const NS_ERROR_OFFLINE = 0x804b0010;
+ var c;
+
+ // Clear the timer, if there is one.
+ if ("reconnectTimer" in this)
+ {
+ clearTimeout(this.reconnectTimer);
+ delete this.reconnectTimer;
+ }
+
+ var ev;
+
+ if (this.state == NET_CANCELLING)
+ {
+ if ("primServ" in this && this.primServ.isConnected)
+ this.primServ.connection.disconnect();
+ else
+ this.state = NET_OFFLINE;
+
+ ev = new CEvent("network", "error", this, "onError");
+ ev.server = this.primServ;
+ ev.debug = "Connect sequence was canceled.";
+ ev.errorCode = JSIRC_ERR_CANCELLED;
+ this.eventPump.addEvent(ev);
+
+ return false;
+ }
+
+ if ("primServ" in this && this.primServ.isConnected)
+ return true;
+
+ this.connectAttempt++;
+ this.connectCandidate++;
+
+ this.state = NET_CONNECTING; /* connection is considered "made" when server
+ * sends a 001 message (see server.on001) */
+
+ var host = this.nextHost++;
+ if (host >= this.serverList.length)
+ {
+ this.nextHost = 1;
+ host = 0;
+ }
+
+ // If STS is enabled, check the cache for a secure port to connect to.
+ if (this.STS_MODULE.ENABLED && !this.serverList[host].isSecure)
+ {
+ var newPort = this.STS_MODULE.getUpgradePolicy(this.serverList[host].hostname);
+ if (newPort)
+ {
+ // If we're a temporary network, just change the server prior to connecting.
+ if (this.temporary)
+ {
+ this.serverList[host].port = newPort;
+ this.serverList[host].isSecure = true;
+ }
+ // Otherwise, find or create a server with the specified host and port.
+ else
+ {
+ var hostname = this.serverList[host].hostname;
+ var matches = this.serverList.filter(function(s) {
+ return s.hostname == hostname && s.port == newPort;
+ });
+ if (matches.length > 0)
+ {
+ host = arrayIndexOf(this.serverList, matches[0]);
+ }
+ else
+ {
+ this.addServer(hostname, newPort, true,
+ this.serverList[host].password);
+ host = this.serverList.length - 1;
+ }
+ }
+ }
+ }
+
+ if (this.serverList[host].isSecure || !this.requireSecurity)
+ {
+ ev = new CEvent ("network", "startconnect", this, "onStartConnect");
+ ev.debug = "Connecting to " + this.serverList[host].unicodeName + ":" +
+ this.serverList[host].port + ", attempt " + this.connectAttempt +
+ " of " + this.MAX_CONNECT_ATTEMPTS + "...";
+ ev.host = this.serverList[host].hostname;
+ ev.port = this.serverList[host].port;
+ ev.server = this.serverList[host];
+ ev.connectAttempt = this.connectAttempt;
+ ev.reconnectDelayMs = this.getReconnectDelayMs();
+ this.eventPump.addEvent (ev);
+
+ try
+ {
+ this.serverList[host].connect();
+ }
+ catch(ex)
+ {
+ this.state = NET_OFFLINE;
+
+ ev = new CEvent("network", "error", this, "onError");
+ ev.server = this;
+ ev.debug = "Exception opening socket: " + ex;
+ ev.errorCode = JSIRC_ERR_NO_SOCKET;
+ if ((typeof ex == "object") && (ex.result == NS_ERROR_OFFLINE))
+ ev.errorCode = JSIRC_ERR_OFFLINE;
+ if ((typeof ex == "string") && (ex == JSIRC_ERR_PAC_LOADING))
+ {
+ ev.errorCode = JSIRC_ERR_PAC_LOADING;
+ ev.retryDelay = CIRCNetwork.prototype.PAC_RECONNECT_DELAY;
+ /* PAC loading is not a problem with any specific server. We'll
+ * retry the connection in 5 seconds.
+ */
+ this.nextHost--;
+ this.state = NET_WAITING;
+ setTimeout(function(n) { n.immediateConnect() },
+ ev.retryDelay, this);
+ }
+ this.eventPump.addEvent(ev);
+ }
+ }
+ else
+ {
+ /* Server doesn't use SSL as requested, try next one.
+ * In the meantime, correct the connection attempt counter */
+ this.connectAttempt--;
+ this.immediateConnect();
+ }
+
+ return true;
+}
+
+/**
+ * Returns |true| iff this network has a socket-level connection.
+ */
+CIRCNetwork.prototype.isConnected =
+function net_connected (e)
+{
+ return ("primServ" in this && this.primServ.isConnected);
+}
+
+
+CIRCNetwork.prototype.ignore =
+function net_ignore (hostmask)
+{
+ var input = getHostmaskParts(hostmask);
+
+ if (input.mask in this.ignoreList)
+ return false;
+
+ this.ignoreList[input.mask] = input;
+ this.ignoreMaskCache = new Object();
+ return true;
+}
+
+CIRCNetwork.prototype.unignore =
+function net_ignore (hostmask)
+{
+ var input = getHostmaskParts(hostmask);
+
+ if (!(input.mask in this.ignoreList))
+ return false;
+
+ delete this.ignoreList[input.mask];
+ this.ignoreMaskCache = new Object();
+ return true;
+}
+
+function CIRCServer (parent, hostname, port, isSecure, password)
+{
+ var serverName = hostname + ":" + port;
+
+ var s;
+ if (serverName in parent.servers)
+ {
+ s = parent.servers[serverName];
+ }
+ else
+ {
+ s = this;
+ s.channels = new Object();
+ s.users = new Object();
+ }
+
+ s.unicodeName = serverName;
+ s.viewName = serverName;
+ s.canonicalName = serverName;
+ s.collectionKey = ":" + serverName;
+ s.encodedName = serverName;
+ s.hostname = hostname;
+ s.port = port;
+ s.parent = parent;
+ s.isSecure = isSecure;
+ s.password = password;
+ s.connection = null;
+ s.isConnected = false;
+ s.sendQueue = new Array();
+ s.lastSend = new Date("1/1/1980");
+ s.lastPingSent = null;
+ s.lastPing = null;
+ s.savedLine = "";
+ s.lag = -1;
+ s.usersStable = true;
+ s.supports = null;
+ s.channelTypes = null;
+ s.channelModes = null;
+ s.channelCount = -1;
+ s.userModes = null;
+ s.maxLineLength = 400;
+ s.caps = new Object();
+ s.capvals = new Object();
+
+ parent.servers[s.collectionKey] = s;
+ if ("onInit" in s)
+ s.onInit();
+ return s;
+}
+
+CIRCServer.prototype.MS_BETWEEN_SENDS = 1500;
+CIRCServer.prototype.READ_TIMEOUT = 100;
+CIRCServer.prototype.VERSION_RPLY = "JS-IRC Library v0.01, " +
+ "Copyright (C) 1999 Robert Ginda; rginda@ndcico.com";
+CIRCServer.prototype.OS_RPLY = "Unknown";
+CIRCServer.prototype.HOST_RPLY = "Unknown";
+CIRCServer.prototype.DEFAULT_REASON = "no reason";
+/* true means WHO command doesn't collect hostmask, username, etc. */
+CIRCServer.prototype.LIGHTWEIGHT_WHO = false;
+/* Unique identifier for WHOX commands. */
+CIRCServer.prototype.WHOX_TYPE = "314";
+/* -1 == never, 0 == prune onQuit, >0 == prune when >X ms old */
+CIRCServer.prototype.PRUNE_OLD_USERS = -1;
+
+CIRCServer.prototype.TYPE = "IRCServer";
+
+// Define functions to set modes so they're easily readable.
+// name is the name used on the CIRCChanMode object
+// getValue is a function returning the value the canonicalmode should be set to
+// given a certain modifier and appropriate data.
+CIRCServer.prototype.canonicalChanModes = {
+ i: {
+ name: "invite",
+ getValue: function (modifier) { return (modifier == "+"); }
+ },
+ m: {
+ name: "moderated",
+ getValue: function (modifier) { return (modifier == "+"); }
+ },
+ n: {
+ name: "publicMessages",
+ getValue: function (modifier) { return (modifier == "-"); }
+ },
+ t: {
+ name: "publicTopic",
+ getValue: function (modifier) { return (modifier == "-"); }
+ },
+ s: {
+ name: "secret",
+ getValue: function (modifier) { return (modifier == "+"); }
+ },
+ p: {
+ name: "pvt",
+ getValue: function (modifier) { return (modifier == "+"); }
+ },
+ k: {
+ name: "key",
+ getValue: function (modifier, data)
+ {
+ if (modifier == "+")
+ return data;
+ else
+ return "";
+ }
+ },
+ l: {
+ name: "limit",
+ getValue: function (modifier, data)
+ {
+ // limit is special - we return -1 if there is no limit.
+ if (modifier == "-")
+ return -1;
+ else
+ return data;
+ }
+ }
+};
+
+CIRCServer.prototype.toLowerCase =
+function serv_tolowercase(str)
+{
+ /* This is an implementation that lower-cases strings according to the
+ * prevailing CASEMAPPING setting for the server. Values for this are:
+ *
+ * o "ascii": The ASCII characters 97 to 122 (decimal) are defined as
+ * the lower-case characters of ASCII 65 to 90 (decimal). No other
+ * character equivalency is defined.
+ * o "strict-rfc1459": The ASCII characters 97 to 125 (decimal) are
+ * defined as the lower-case characters of ASCII 65 to 93 (decimal).
+ * No other character equivalency is defined.
+ * o "rfc1459": The ASCII characters 97 to 126 (decimal) are defined as
+ * the lower-case characters of ASCII 65 to 94 (decimal). No other
+ * character equivalency is defined.
+ *
+ */
+
+ function replaceFunction(chr)
+ {
+ return String.fromCharCode(chr.charCodeAt(0) + 32);
+ }
+
+ var mapping = "rfc1459";
+ if (this.supports)
+ mapping = this.supports.casemapping;
+
+ /* NOTE: There are NO breaks in this switch. This is CORRECT.
+ * Each mapping listed is a super-set of those below, thus we only
+ * transform the extra characters, and then fall through.
+ */
+ switch (mapping)
+ {
+ case "rfc1459":
+ str = str.replace(/\^/g, replaceFunction);
+ case "strict-rfc1459":
+ str = str.replace(/[\[\\\]]/g, replaceFunction);
+ case "ascii":
+ str = str.replace(/[A-Z]/g, replaceFunction);
+ }
+ return str;
+}
+
+// Iterates through the keys in an object and, if specified, the keys of
+// child objects.
+CIRCServer.prototype.renameProperties =
+function serv_renameproperties(obj, child)
+{
+ for (let key in obj)
+ {
+ let item = obj[key];
+ item.canonicalName = this.toLowerCase(item.encodedName);
+ item.collectionKey = ":" + item.canonicalName;
+ renameProperty(obj, key, item.collectionKey);
+ if (child && (child in item))
+ this.renameProperties(item[child], null);
+ }
+}
+
+// Encodes tag data to send.
+CIRCServer.prototype.encodeTagData =
+function serv_encodetagdata(obj)
+{
+ var dict = new Object();
+ dict[";"] = ":";
+ dict[" "] = "s";
+ dict["\\"] = "\\";
+ dict["\r"] = "r";
+ dict["\n"] = "n";
+
+ // Function for escaping key values.
+ function escapeTagValue(data)
+ {
+ var rv = "";
+ for (var i = 0; i < data.length; i++)
+ {
+ var ci = data[i];
+ var co = dict[data[i]];
+ if (co)
+ rv += "\\" + co;
+ else
+ rv += ci;
+ }
+
+ return rv;
+ }
+
+ var str = "";
+
+ for(var key in obj)
+ {
+ var val = obj[key];
+ str += key;
+ if (val)
+ {
+ str += "=";
+ str += escapeTagValue(val);
+ }
+ str += ";";
+ }
+
+ // Remove any trailing semicolons.
+ if (str[str.length - 1] == ";")
+ str = str.substring(0, str.length - 1);
+
+ return str;
+}
+
+// Decodes received tag data.
+CIRCServer.prototype.decodeTagData =
+function serv_decodetagdata(str)
+{
+ // Remove the leading '@' if we have one.
+ if (str[0] == "@")
+ str = str.substring(1);
+
+ var dict = new Object();
+ dict[":"] = ";";
+ dict["s"] = " ";
+ dict["\\"] = "\\";
+ dict["r"] = "\r";
+ dict["n"] = "\n";
+
+ // Function for unescaping key values.
+ function unescapeTagValue(data)
+ {
+ var rv = "";
+ for (let j = 0; j < data.length; j++)
+ {
+ let currentItem = data[j];
+ if (currentItem == "\\" && j < data.length - 1)
+ {
+ let nextItem = data[j + 1];
+ if (nextItem in dict)
+ rv += dict[nextItem];
+ else
+ rv += nextItem;
+ j++
+ }
+ else if (currentItem != "\\")
+ rv += currentItem;
+ }
+
+ return rv;
+ }
+
+ var obj = Object();
+
+ var tags = str.split(";");
+ for (var i = 0; i < tags.length; i++)
+ {
+ var [key, val] = tags[i].split("=");
+ val = unescapeTagValue(val);
+ obj[key] = val;
+ }
+
+ return obj;
+}
+
+// Returns the IRC URL representation of this server.
+CIRCServer.prototype.getURL =
+function serv_geturl(target, flags)
+{
+ var scheme = (this.isSecure ? "ircs" : "irc");
+ var obj = {host: this.hostname, scheme: scheme, isserver: true,
+ port: this.port, needpass: Boolean(this.password)};
+
+ if (target)
+ obj.target = target;
+
+ if (flags)
+ {
+ for (var i = 0; i < flags.length; i++)
+ obj[flags[i]] = true;
+ }
+
+ return constructIRCURL(obj);
+}
+
+CIRCServer.prototype.getUser =
+function chan_getuser(nick)
+{
+ var tnick = ":" + this.toLowerCase(nick);
+
+ if (tnick in this.users)
+ return this.users[tnick];
+
+ tnick = ":" + this.toLowerCase(fromUnicode(nick, this));
+
+ if (tnick in this.users)
+ return this.users[tnick];
+
+ return null;
+}
+
+CIRCServer.prototype.getChannel =
+function chan_getchannel(name)
+{
+ var tname = ":" + this.toLowerCase(name);
+
+ if (tname in this.channels)
+ return this.channels[tname];
+
+ tname = ":" + this.toLowerCase(fromUnicode(name, this));
+
+ if (tname in this.channels)
+ return this.channels[tname];
+
+ return null;
+}
+
+CIRCServer.prototype.connect =
+function serv_connect()
+{
+ if (this.connection != null)
+ throw "Server already has a connection pending or established";
+
+ var config = { isSecure: this.isSecure };
+ if (this.parent.PROXY_TYPE_OVERRIDE)
+ config.proxy = this.parent.PROXY_TYPE_OVERRIDE;
+
+ this.connection = new CBSConnection();
+ this.connection.connect(this.hostname, this.port, config, this);
+}
+
+// This may be called synchronously or asynchronously by CBSConnection.connect.
+CIRCServer.prototype.onSocketConnection =
+function serv_onsocketconnection(host, port, config, exception)
+{
+ if (this.parent.state == NET_CANCELLING)
+ {
+ this.connection.disconnect();
+ this.connection = null;
+ this.parent.state = NET_OFFLINE;
+
+ var ev = new CEvent("network", "error", this.parent, "onError");
+ ev.server = this;
+ ev.debug = "Connect sequence was canceled.";
+ ev.errorCode = JSIRC_ERR_CANCELLED;
+ this.parent.eventPump.addEvent(ev);
+ }
+ else if (!exception)
+ {
+ var ev = new CEvent("server", "connect", this, "onConnect");
+ ev.server = this;
+ this.parent.eventPump.addEvent(ev);
+ this.isConnected = true;
+
+ this.connection.startAsyncRead(this);
+ }
+ else
+ {
+ var ev = new CEvent("server", "disconnect", this, "onDisconnect");
+ ev.server = this;
+ ev.reason = "error";
+ ev.exception = exception;
+ ev.disconnectStatus = NS_ERROR_ABORT;
+ this.parent.eventPump.addEvent(ev);
+ }
+}
+
+/*
+ * What to do when the client connects to it's primary server
+ */
+CIRCServer.prototype.onConnect =
+function serv_onconnect (e)
+{
+ this.parent.primServ = e.server;
+
+ this.sendData("CAP LS 302\n");
+ this.pendingCapNegotiation = true;
+
+ this.caps = new Object();
+ this.capvals = new Object();
+
+ this.login(this.parent.INITIAL_NICK, this.parent.INITIAL_NAME,
+ this.parent.INITIAL_DESC);
+ return true;
+}
+
+CIRCServer.prototype.onStreamDataAvailable =
+function serv_sda (request, inStream, sourceOffset, count)
+{
+ var ev = new CEvent ("server", "data-available", this,
+ "onDataAvailable");
+
+ ev.line = this.connection.readData(0, count);
+
+ /* route data-available as we get it. the data-available handler does
+ * not do much, so we can probably get away with this without starving
+ * the UI even under heavy input traffic.
+ */
+ this.parent.eventPump.routeEvent(ev);
+}
+
+CIRCServer.prototype.onStreamClose =
+function serv_sockdiscon(status)
+{
+ var ev = new CEvent ("server", "disconnect", this, "onDisconnect");
+ ev.server = this;
+ ev.disconnectStatus = status;
+ if (ev.disconnectStatus == NS_ERROR_BINDING_ABORTED)
+ ev.disconnectStatus = NS_ERROR_ABORT;
+
+ this.parent.eventPump.addEvent (ev);
+}
+
+
+CIRCServer.prototype.flushSendQueue =
+function serv_flush()
+{
+ this.sendQueue.length = 0;
+ dd("sendQueue flushed.");
+
+ return true;
+}
+
+CIRCServer.prototype.login =
+function serv_login(nick, name, desc)
+{
+ nick = nick.replace(/ /g, "_");
+ name = name.replace(/ /g, "_");
+
+ if (!nick)
+ nick = "nick";
+
+ if (!name)
+ name = nick;
+
+ if (!desc)
+ desc = nick;
+
+ this.me = new CIRCUser(this, nick, null, name);
+ if (this.password)
+ this.sendData("PASS " + this.password + "\n");
+ this.changeNick(this.me.unicodeName);
+ this.sendData("USER " + name + " * * :" +
+ fromUnicode(desc, this) + "\n");
+}
+
+CIRCServer.prototype.logout =
+function serv_logout(reason)
+{
+ if (reason == null || typeof reason == "undefined")
+ reason = this.DEFAULT_REASON;
+
+ this.quitting = true;
+
+ this.connection.sendData("QUIT :" +
+ fromUnicode(reason, this.parent) + "\n");
+ this.connection.disconnect();
+}
+
+CIRCServer.prototype.sendAuthResponse =
+function serv_authresponse(resp)
+{
+ // Encode the response and break into 400-byte parts.
+ var resp = btoa(resp);
+ var part = null;
+ var n = 0;
+ do
+ {
+ part = resp.substring(0, 400);
+ n = part.length;
+ resp = resp.substring(400);
+
+ this.sendData("AUTHENTICATE " + part + '\n');
+ }
+ while (resp.length > 0);
+
+ // Send empty auth response if last part was exactly 400 bytes long.
+ if (n == 400)
+ {
+ this.sendData("AUTHENTICATE +\n");
+ }
+}
+
+CIRCServer.prototype.sendAuthAbort =
+function serv_authabort()
+{
+ // Abort an in-progress SASL authentication.
+ this.sendData("AUTHENTICATE *\n");
+}
+
+CIRCServer.prototype.sendMonitorList =
+function serv_monitorlist(nicks, isAdd)
+{
+ if (!nicks.length)
+ return;
+
+ var prefix;
+ if (isAdd)
+ prefix = "MONITOR + ";
+ else
+ prefix = "MONITOR - ";
+
+ /* Send monitor list updates in chunks less than
+ maxLineLength in size. */
+ var nicks_string = nicks.join(",");
+ while (nicks_string.length > this.maxLineLength)
+ {
+ var nicks_part = nicks_string.substring(0, this.maxLineLength);
+ var i = nicks_part.lastIndexOf(",");
+ nicks_part = nicks_string.substring(0, i);
+ nicks_string = nicks_string.substring(i + 1);
+ this.sendData(prefix + nicks_part + "\n");
+ }
+ this.sendData(prefix + nicks_string + "\n");
+}
+
+CIRCServer.prototype.addTarget =
+function serv_addtarget(name)
+{
+ if (arrayIndexOf(this.channelTypes, name[0]) != -1) {
+ return this.addChannel(name);
+ } else {
+ return this.addUser(name);
+ }
+}
+
+CIRCServer.prototype.addChannel =
+function serv_addchan(unicodeName, charset)
+{
+ return new CIRCChannel(this, unicodeName, fromUnicode(unicodeName, charset));
+}
+
+CIRCServer.prototype.addUser =
+function serv_addusr(unicodeName, name, host)
+{
+ return new CIRCUser(this, unicodeName, null, name, host);
+}
+
+CIRCServer.prototype.getChannelsLength =
+function serv_chanlen()
+{
+ var i = 0;
+
+ for (var p in this.channels)
+ i++;
+
+ return i;
+}
+
+CIRCServer.prototype.getUsersLength =
+function serv_chanlen()
+{
+ var i = 0;
+
+ for (var p in this.users)
+ i++;
+
+ return i;
+}
+
+CIRCServer.prototype.sendData =
+function serv_senddata (msg)
+{
+ this.queuedSendData (msg);
+}
+
+CIRCServer.prototype.queuedSendData =
+function serv_senddata (msg)
+{
+ if (this.sendQueue.length == 0)
+ this.parent.eventPump.addEvent (new CEvent ("server", "senddata",
+ this, "onSendData"));
+ arrayInsertAt (this.sendQueue, 0, new String(msg));
+}
+
+// Utility method for splitting large lines prior to sending.
+CIRCServer.prototype.splitLinesForSending =
+function serv_splitlines(line, prettyWrap)
+{
+ let lines = String(line).split("\n");
+ let realLines = [];
+ for (let i = 0; i < lines.length; i++)
+ {
+ if (lines[i])
+ {
+ while (lines[i].length > this.maxLineLength)
+ {
+ var extraLine = lines[i].substr(0, this.maxLineLength - 5);
+ var pos = extraLine.lastIndexOf(" ");
+
+ if ((pos >= 0) && (pos >= this.maxLineLength - 15))
+ {
+ // Smart-split.
+ extraLine = lines[i].substr(0, pos);
+ lines[i] = lines[i].substr(extraLine.length + 1);
+ if (prettyWrap)
+ {
+ extraLine += "...";
+ lines[i] = "..." + lines[i];
+ }
+ }
+ else
+ {
+ // Dumb-split.
+ extraLine = lines[i].substr(0, this.maxLineLength);
+ lines[i] = lines[i].substr(extraLine.length);
+ }
+ realLines.push(extraLine);
+ }
+ realLines.push(lines[i]);
+ }
+ }
+ return realLines;
+}
+
+CIRCServer.prototype.messageTo =
+function serv_messto(code, target, msg, ctcpCode)
+{
+ let lines = this.splitLinesForSending(msg, true);
+
+ let i = 0;
+ let pfx = "";
+ let sfx = "";
+
+ if (ctcpCode)
+ {
+ pfx = "\01" + ctcpCode;
+ sfx = "\01";
+ }
+
+ // We may have no message at all with CTCP commands.
+ if (!lines.length && ctcpCode)
+ lines.push("");
+
+ for (i in lines)
+ {
+ if ((lines[i] != "") || ctcpCode)
+ {
+ var line = code + " " + target + " :" + pfx;
+ if (lines[i] != "")
+ {
+ if (ctcpCode)
+ line += " ";
+ line += lines[i] + sfx;
+ }
+ else
+ line += sfx;
+ //dd ("-*- irc sending '" + line + "'");
+ this.sendData(line + "\n");
+ }
+ }
+}
+
+CIRCServer.prototype.sayTo =
+function serv_sayto (target, msg)
+{
+ this.messageTo("PRIVMSG", target, msg);
+}
+
+CIRCServer.prototype.noticeTo =
+function serv_noticeto (target, msg)
+{
+ this.messageTo("NOTICE", target, msg);
+}
+
+CIRCServer.prototype.actTo =
+function serv_actto (target, msg)
+{
+ this.messageTo("PRIVMSG", target, msg, "ACTION");
+}
+
+CIRCServer.prototype.ctcpTo =
+function serv_ctcpto (target, code, msg, method)
+{
+ msg = msg || "";
+ method = method || "PRIVMSG";
+
+ code = code.toUpperCase();
+ if (code == "PING" && !msg)
+ msg = Number(new Date());
+ this.messageTo(method, target, msg, code);
+}
+
+CIRCServer.prototype.changeNick =
+function serv_changenick(newNick)
+{
+ this.sendData("NICK " + fromUnicode(newNick, this) + "\n");
+}
+
+CIRCServer.prototype.updateLagTimer =
+function serv_uptimer()
+{
+ this.connection.sendData("PING :LAGTIMER\n");
+ this.lastPing = this.lastPingSent = new Date();
+}
+
+CIRCServer.prototype.userhost =
+function serv_userhost(target)
+{
+ this.sendData("USERHOST " + fromUnicode(target, this) + "\n");
+}
+
+CIRCServer.prototype.userip =
+function serv_userip(target)
+{
+ this.sendData("USERIP " + fromUnicode(target, this) + "\n");
+}
+
+CIRCServer.prototype.who =
+function serv_who(target)
+{
+ this.sendData("WHO " + fromUnicode(target, this) + "\n");
+}
+
+/**
+ * Abstracts the whois command.
+ *
+ * @param target intended user(s).
+ */
+CIRCServer.prototype.whois =
+function serv_whois (target)
+{
+ this.sendData("WHOIS " + fromUnicode(target, this) + "\n");
+}
+
+CIRCServer.prototype.whowas =
+function serv_whowas(target, limit)
+{
+ if (typeof limit == "undefined")
+ limit = 1;
+ else if (limit == 0)
+ limit = "";
+
+ this.sendData("WHOWAS " + fromUnicode(target, this) + " " + limit + "\n");
+}
+
+CIRCServer.prototype.onDisconnect =
+function serv_disconnect(e)
+{
+ function stateChangeFn(network, state) {
+ network.state = state;
+ };
+
+ function delayedConnectFn(network) {
+ network.delayedConnect();
+ };
+
+ /* If we're not connected and get this, it means we have almost certainly
+ * encountered a read or write error on the socket post-disconnect. There's
+ * no point propagating this any further, as we've already notified the
+ * user of the disconnect (with the right error).
+ */
+ if (!this.isConnected)
+ return;
+
+ // Don't reconnect from a certificate error.
+ var certError = (getNSSErrorClass(e.disconnectStatus) == ERROR_CLASS_BAD_CERT);
+
+ // Don't reconnect if our connection was aborted.
+ var wasAborted = (e.disconnectStatus == NS_ERROR_ABORT);
+ var dontReconnect = certError || wasAborted;
+
+ if (((this.parent.state == NET_CONNECTING) && !dontReconnect) ||
+ /* fell off while connecting, try again */
+ (this.parent.primServ == this) && (this.parent.state == NET_ONLINE) &&
+ (!("quitting" in this) && this.parent.stayingPower && !dontReconnect))
+ { /* fell off primary server, reconnect to any host in the serverList */
+ setTimeout(delayedConnectFn, 0, this.parent);
+ }
+ else
+ {
+ setTimeout(stateChangeFn, 0, this.parent, NET_OFFLINE);
+ }
+
+ e.server = this;
+ e.set = "network";
+ e.destObject = this.parent;
+
+ e.quitting = this.quitting;
+
+ for (var c in this.channels)
+ {
+ this.channels[c].users = new Object();
+ this.channels[c].active = false;
+ }
+
+ if (this.isStartTLS)
+ {
+ this.isSecure = false;
+ delete this.isStartTLS;
+ }
+
+ delete this.batches;
+
+ this.connection = null;
+ this.isConnected = false;
+
+ delete this.quitting;
+}
+
+CIRCServer.prototype.onSendData =
+function serv_onsenddata (e)
+{
+ if (!this.isConnected || (this.parent.state == NET_CANCELLING))
+ {
+ dd ("Can't send to disconnected socket");
+ this.flushSendQueue();
+ return false;
+ }
+
+ var d = new Date();
+
+ // Wheee, some sanity checking! (there's been at least one case of lastSend
+ // ending up in the *future* at this point, which kinda busts things)
+ if (this.lastSend > d)
+ this.lastSend = 0;
+
+ if (((d - this.lastSend) >= this.MS_BETWEEN_SENDS) &&
+ this.sendQueue.length > 0)
+ {
+ var s = this.sendQueue.pop();
+
+ if (s)
+ {
+ try
+ {
+ this.connection.sendData(s);
+ }
+ catch(ex)
+ {
+ dd("Exception in queued send: " + ex);
+ this.flushSendQueue();
+
+ var ev = new CEvent("server", "disconnect",
+ this, "onDisconnect");
+ ev.server = this;
+ ev.reason = "error";
+ ev.exception = ex;
+ ev.disconnectStatus = NS_ERROR_ABORT;
+ this.parent.eventPump.addEvent(ev);
+
+ return false;
+ }
+ this.lastSend = d;
+ }
+
+ }
+ else
+ {
+ this.parent.eventPump.addEvent(new CEvent("event-pump", "yield",
+ null, ""));
+ }
+
+ if (this.sendQueue.length > 0)
+ this.parent.eventPump.addEvent(new CEvent("server", "senddata",
+ this, "onSendData"));
+ return true;
+}
+
+CIRCServer.prototype.onPoll =
+function serv_poll(e)
+{
+ var lines;
+ var ex;
+ var ev;
+
+ try
+ {
+ if (this.parent.state != NET_CANCELLING)
+ line = this.connection.readData(this.READ_TIMEOUT);
+ }
+ catch (ex)
+ {
+ dd ("*** Caught exception " + ex + " reading from server " +
+ this.hostname);
+ ev = new CEvent ("server", "disconnect", this, "onDisconnect");
+ ev.server = this;
+ ev.reason = "error";
+ ev.exception = ex;
+ ev.disconnectStatus = NS_ERROR_ABORT;
+ this.parent.eventPump.addEvent (ev);
+ return false;
+ }
+
+ this.parent.eventPump.addEvent (new CEvent ("server", "poll", this,
+ "onPoll"));
+
+ if (line)
+ {
+ ev = new CEvent ("server", "data-available", this, "onDataAvailable");
+ ev.line = line;
+ this.parent.eventPump.routeEvent(ev);
+ }
+
+ return true;
+}
+
+CIRCServer.prototype.onDataAvailable =
+function serv_ppline(e)
+{
+ var line = e.line;
+
+ if (line == "")
+ return false;
+
+ var incomplete = (line[line.length - 1] != '\n');
+ var lines = line.split("\n");
+
+ if (this.savedLine)
+ {
+ lines[0] = this.savedLine + lines[0];
+ this.savedLine = "";
+ }
+
+ if (incomplete)
+ this.savedLine = lines.pop();
+
+ for (var i in lines)
+ {
+ var ev = new CEvent("server", "rawdata", this, "onRawData");
+ ev.data = lines[i].replace(/\r/g, "");
+ if (ev.data)
+ {
+ if (ev.data.match(/^(?::[^ ]+ )?(?:32[123]|352|354|315) /i))
+ this.parent.eventPump.addBulkEvent(ev);
+ else
+ this.parent.eventPump.addEvent(ev);
+ }
+ }
+
+ return true;
+}
+
+/*
+ * onRawData begins shaping the event by parsing the IRC message at it's
+ * simplest level. After onRawData, the event will have the following
+ * properties:
+ * name value
+ *
+ * set............"server"
+ * type..........."parsedata"
+ * destMethod....."onParsedData"
+ * destObject.....server (this)
+ * server.........server (this)
+ * connection.....CBSConnection (this.connection)
+ * source.........the <prefix> of the message (if it exists)
+ * user...........user object initialized with data from the message <prefix>
+ * params.........array containing the parameters of the message
+ * code...........the first parameter (most messages have this)
+ *
+ * See Section 2.3.1 of RFC 1459 for details on <prefix>, <middle> and
+ * <trailing> tokens.
+ */
+CIRCServer.prototype.onRawData =
+function serv_onRawData(e)
+{
+ var ary;
+ var l = e.data;
+
+ if (l.length == 0)
+ {
+ dd ("empty line on onRawData?");
+ return false;
+ }
+
+ if (l[0] == "@")
+ {
+ e.tagdata = l.substring(0, l.indexOf(" "));
+ e.tags = this.decodeTagData(e.tagdata);
+ l = l.substring(l.indexOf(" ") + 1);
+ }
+ else
+ {
+ e.tagdata = new Object();
+ e.tags = new Object();
+ }
+
+ if (l[0] == ":")
+ {
+ // Must split only on REAL spaces here, not just any old whitespace.
+ ary = l.match(/:([^ ]+) +(.*)/);
+ e.source = ary[1];
+ l = ary[2];
+ ary = e.source.match(/([^ ]+)!([^ ]+)@(.*)/);
+ if (ary)
+ {
+ e.user = new CIRCUser(this, null, ary[1], ary[2], ary[3]);
+ }
+ else
+ {
+ ary = e.source.match(/([^ ]+)@(.*)/);
+ if (ary)
+ {
+ e.user = new CIRCUser(this, null, ary[1], null, ary[2]);
+ }
+ else
+ {
+ ary = e.source.match(/([^ ]+)!(.*)/);
+ if (ary)
+ e.user = new CIRCUser(this, null, ary[1], ary[2], null);
+ }
+ }
+ }
+
+ if (("user" in e) && e.user && e.tags.account)
+ {
+ e.user.account = e.tags.account;
+ }
+
+ e.ignored = false;
+ if (("user" in e) && e.user && ("ignoreList" in this.parent))
+ {
+ // Assumption: if "ignoreList" is in this.parent, we assume that:
+ // a) it's an array.
+ // b) ignoreMaskCache also exists, and
+ // c) it's an array too.
+
+ if (!(e.source in this.parent.ignoreMaskCache))
+ {
+ for (var m in this.parent.ignoreList)
+ {
+ if (hostmaskMatches(e.user, this.parent.ignoreList[m]))
+ {
+ e.ignored = true;
+ break;
+ }
+ }
+ /* Save this exact source in the cache, with results of tests. */
+ this.parent.ignoreMaskCache[e.source] = e.ignored;
+ }
+ else
+ {
+ e.ignored = this.parent.ignoreMaskCache[e.source];
+ }
+ }
+
+ e.server = this;
+
+ var sep = l.indexOf(" :");
+
+ if (sep != -1) /* <trailing> param, if there is one */
+ {
+ var trail = l.substr (sep + 2, l.length);
+ e.params = l.substr(0, sep).split(/ +/);
+ e.params[e.params.length] = trail;
+ }
+ else
+ {
+ e.params = l.split(/ +/);
+ }
+
+ e.decodeParam = decodeParam;
+ e.code = e.params[0].toUpperCase();
+
+ // Ignore all private (inc. channel) messages, notices and invites here.
+ if (e.ignored && ((e.code == "PRIVMSG") || (e.code == "NOTICE") ||
+ (e.code == "INVITE") || (e.code == "TAGMSG")))
+ return true;
+
+ // If the message is part of a batch, store it for later.
+ if (this.batches && e.tags["batch"] && e.code != "BATCH")
+ {
+ var reftag = e.tags["batch"];
+ // Check if the batch is already open.
+ // If not, ignore the incoming message.
+ if (this.batches[reftag])
+ this.batches[reftag].messages.push(e);
+ return false;
+ }
+
+ e.type = "parseddata";
+ e.destObject = this;
+ e.destMethod = "onParsedData";
+
+ return true;
+}
+
+/*
+ * onParsedData forwards to next event, based on |e.code|
+ */
+CIRCServer.prototype.onParsedData =
+function serv_onParsedData(e)
+{
+ e.type = this.toLowerCase(e.code);
+ if (!e.code[0])
+ {
+ dd (dumpObjectTree (e));
+ return false;
+ }
+
+ e.destMethod = "on" + e.code[0].toUpperCase() +
+ e.code.substr (1, e.code.length).toLowerCase();
+
+ if (typeof this[e.destMethod] == "function")
+ e.destObject = this;
+ else if (typeof this["onUnknown"] == "function")
+ e.destMethod = "onUnknown";
+ else if (typeof this.parent[e.destMethod] == "function")
+ {
+ e.set = "network";
+ e.destObject = this.parent;
+ }
+ else
+ {
+ e.set = "network";
+ e.destObject = this.parent;
+ e.destMethod = "onUnknown";
+ }
+
+ return true;
+}
+
+/* User changed topic */
+CIRCServer.prototype.onTopic =
+function serv_topic (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[1]);
+ e.channel.topicBy = e.user.unicodeName;
+ e.channel.topicDate = new Date();
+ e.channel.topic = toUnicode(e.params[2], e.channel);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+/* Successful login */
+CIRCServer.prototype.on001 =
+function serv_001 (e)
+{
+ this.parent.connectAttempt = 0;
+ this.parent.connectCandidate = 0;
+ //Mark capability negotiation as finished, if we haven't already.
+ delete this.parent.pendingCapNegotiation;
+ this.parent.state = NET_ONLINE;
+ // nextHost is incremented after picking a server. Push it back here.
+ this.parent.nextHost--;
+
+ /* servers won't send a nick change notification if user was forced
+ * to change nick while logging in (eg. nick already in use.) We need
+ * to verify here that what the server thinks our name is, matches what
+ * we think it is. If not, the server wins.
+ */
+ if (e.params[1] != e.server.me.encodedName)
+ {
+ renameProperty(e.server.users, e.server.me.collectionKey,
+ ":" + this.toLowerCase(e.params[1]));
+ e.server.me.changeNick(toUnicode(e.params[1], this));
+ }
+
+ /* Set up supports defaults here.
+ * This is so that we don't waste /huge/ amounts of RAM for the network's
+ * servers just because we know about them. Until we connect, that is.
+ * These defaults are taken from the draft 005 RPL_ISUPPORTS here:
+ * http://www.ietf.org/internet-drafts/draft-brocklesby-irc-isupport-02.txt
+ */
+ this.supports = new Object();
+ this.supports.modes = 3;
+ this.supports.maxchannels = 10;
+ this.supports.nicklen = 9;
+ this.supports.casemapping = "rfc1459";
+ this.supports.channellen = 200;
+ this.supports.chidlen = 5;
+ /* Make sure it's possible to tell if we've actually got a 005 message. */
+ this.supports.rpl_isupport = false;
+ this.channelTypes = [ '#', '&' ];
+ /* This next one isn't in the isupport draft, but instead is defaulting to
+ * the codes we understand. It should be noted, some servers include the
+ * mode characters (o, h, v) in the 'a' list, although the draft spec says
+ * they should be treated as type 'b'. Luckly, in practise this doesn't
+ * matter, since both 'a' and 'b' types always take a parameter in the
+ * MODE message, and parsing is not affected. */
+ this.channelModes = {
+ a: ['b'],
+ b: ['k'],
+ c: ['l'],
+ d: ['i', 'm', 'n', 'p', 's', 't']
+ };
+ // Default to support of v/+ and o/@ only.
+ this.userModes = [
+ { mode: 'o', symbol: '@' },
+ { mode: 'v', symbol: '+' }
+ ];
+ // Assume the server supports no extra interesting commands.
+ this.servCmds = {};
+
+ if (this.parent.INITIAL_UMODE)
+ {
+ e.server.sendData("MODE " + e.server.me.encodedName + " :" +
+ this.parent.INITIAL_UMODE + "\n");
+ }
+
+ this.parent.users = this.users;
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* server features */
+CIRCServer.prototype.on005 =
+function serv_005 (e)
+{
+ var oldCaseMapping = this.supports["casemapping"];
+ /* Drop params 0 and 1. */
+ for (var i = 2; i < e.params.length; i++) {
+ var itemStr = e.params[i];
+ /* Items may be of the forms:
+ * NAME
+ * -NAME
+ * NAME=value
+ * Value may be empty on occasion.
+ * No value allowed for -NAME items.
+ */
+ var item = itemStr.match(/^(-?)([A-Z]+)(=(.*))?$/i);
+ if (! item)
+ continue;
+
+ var name = item[2].toLowerCase();
+ if (("3" in item) && item[3])
+ {
+ // And other items are stored as-is, though numeric items
+ // get special treatment to make our life easier later.
+ if (("4" in item) && item[4].match(/^\d+$/))
+ this.supports[name] = Number(item[4]);
+ else
+ this.supports[name] = item[4];
+ }
+ else
+ {
+ // Boolean-type items stored as 'true'.
+ this.supports[name] = !(("1" in item) && item[1] == "-");
+ }
+ }
+ // Update all users and channels if the casemapping changed.
+ if (this.supports["casemapping"] != oldCaseMapping)
+ {
+ this.renameProperties(this.users, null);
+ this.renameProperties(this.channels, "users");
+ }
+
+ // Supported 'special' items:
+ // CHANTYPES (--> channelTypes{}),
+ // PREFIX (--> userModes[{mode,symbol}]),
+ // CHANMODES (--> channelModes{a:[], b:[], c:[], d:[]}).
+
+ var m;
+ if ("chantypes" in this.supports)
+ {
+ this.channelTypes = [];
+ for (m = 0; m < this.supports.chantypes.length; m++)
+ this.channelTypes.push( this.supports.chantypes[m] );
+ }
+
+ if ("prefix" in this.supports)
+ {
+ var mlist = this.supports.prefix.match(/^\((.*)\)(.*)$/i);
+ if ((! mlist) || (mlist[1].length != mlist[2].length))
+ {
+ dd ("** Malformed PREFIX entry in 005 SUPPORTS message **");
+ }
+ else
+ {
+ this.userModes = [];
+ for (m = 0; m < mlist[1].length; m++)
+ this.userModes.push( { mode: mlist[1][m],
+ symbol: mlist[2][m] } );
+ }
+ }
+
+ if ("chanmodes" in this.supports)
+ {
+ var cmlist = this.supports.chanmodes.split(/,/);
+ if ((!cmlist) || (cmlist.length < 4))
+ {
+ dd ("** Malformed CHANMODES entry in 005 SUPPORTS message **");
+ }
+ else
+ {
+ // 4 types - list, set-unset-param, set-only-param, flag.
+ this.channelModes = {
+ a: cmlist[0].split(''),
+ b: cmlist[1].split(''),
+ c: cmlist[2].split(''),
+ d: cmlist[3].split('')
+ };
+ }
+ }
+
+ if ("cmds" in this.supports)
+ {
+ // Map this.supports.cmds [comma-list] into this.servCmds [props].
+ var cmdlist = this.supports.cmds.split(/,/);
+ for (var i = 0; i < cmdlist.length; i++)
+ this.servCmds[cmdlist[i].toLowerCase()] = true;
+ }
+
+ this.supports.rpl_isupport = true;
+
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+/* users */
+CIRCServer.prototype.on251 =
+function serv_251(e)
+{
+ // 251 is the first message we get after 005, so it's now safe to do
+ // things that might depend upon server features.
+
+ if (("namesx" in this.supports) && this.supports.namesx)
+ {
+ // "multi-prefix" is the same as "namesx" but PROTOCTL doesn't reply.
+ this.caps["multi-prefix"] = true;
+ this.sendData("PROTOCTL NAMESX\n");
+ }
+
+ if (this.parent.INITIAL_CHANNEL)
+ {
+ this.parent.primChan = this.addChannel(this.parent.INITIAL_CHANNEL);
+ this.parent.primChan.join();
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* channels */
+CIRCServer.prototype.on254 =
+function serv_254(e)
+{
+ this.channelCount = e.params[2];
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* user away message */
+CIRCServer.prototype.on301 =
+function serv_301(e)
+{
+ e.user = new CIRCUser(this, null, e.params[2]);
+ e.user.awayMessage = e.decodeParam(3, e.user);
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* whois name */
+CIRCServer.prototype.on311 =
+function serv_311 (e)
+{
+ e.user = new CIRCUser(this, null, e.params[2], e.params[3], e.params[4]);
+ e.user.desc = e.decodeParam(6, e.user);
+ e.destObject = this.parent;
+ e.set = "network";
+
+ this.pendingWhoisLines = e.user;
+}
+
+/* whois server */
+CIRCServer.prototype.on312 =
+function serv_312 (e)
+{
+ e.user = new CIRCUser(this, null, e.params[2]);
+ e.user.connectionHost = e.params[3];
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* whois idle time */
+CIRCServer.prototype.on317 =
+function serv_317 (e)
+{
+ e.user = new CIRCUser(this, null, e.params[2]);
+ e.user.idleSeconds = e.params[3];
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* whois channel list */
+CIRCServer.prototype.on319 =
+function serv_319(e)
+{
+ e.user = new CIRCUser(this, null, e.params[2]);
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* end of whois */
+CIRCServer.prototype.on318 =
+function serv_318(e)
+{
+ e.user = new CIRCUser(this, null, e.params[2]);
+
+ if ("pendingWhoisLines" in this)
+ delete this.pendingWhoisLines;
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* ircu's 330 numeric ("X is logged in as Y") */
+CIRCServer.prototype.on330 =
+function serv_330(e)
+{
+ e.user = new CIRCUser(this, null, e.params[2]);
+ var account = (e.params[3] == "*" ? null : e.params[3]);
+ this.users[e.user.collectionKey].account = account;
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* TOPIC reply - no topic set */
+CIRCServer.prototype.on331 =
+function serv_331 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.channel.topic = "";
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+/* TOPIC reply - topic set */
+CIRCServer.prototype.on332 =
+function serv_332 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.channel.topic = toUnicode(e.params[3], e.channel);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+/* topic information */
+CIRCServer.prototype.on333 =
+function serv_333 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.channel.topicBy = toUnicode(e.params[3], this);
+ e.channel.topicDate = new Date(Number(e.params[4]) * 1000);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+/* who reply */
+CIRCServer.prototype.on352 =
+function serv_352 (e)
+{
+ e.userHasChanges = false;
+ if (this.LIGHTWEIGHT_WHO)
+ {
+ e.user = new CIRCUser(this, null, e.params[6]);
+ }
+ else
+ {
+ e.user = new CIRCUser(this, null, e.params[6], e.params[3], e.params[4]);
+ e.user.connectionHost = e.params[5];
+ if (8 in e.params)
+ {
+ var ary = e.params[8].match(/(?:(\d+)\s)?(.*)/);
+ e.user.hops = ary[1];
+ var desc = fromUnicode(ary[2], e.user);
+ if (e.user.desc != desc)
+ {
+ e.userHasChanges = true;
+ e.user.desc = desc;
+ }
+ }
+ }
+ var away = (e.params[7][0] == "G");
+ if (e.user.isAway != away)
+ {
+ e.userHasChanges = true;
+ e.user.isAway = away;
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+/* extended who reply */
+CIRCServer.prototype.on354 =
+function serv_354(e)
+{
+ // Discard if the type is not ours.
+ if (e.params[2] != this.WHOX_TYPE)
+ return;
+
+ e.userHasChanges = false;
+ if (this.LIGHTWEIGHT_WHO)
+ {
+ e.user = new CIRCUser(this, null, e.params[7]);
+ }
+ else
+ {
+ e.user = new CIRCUser(this, null, e.params[7], e.params[4], e.params[5]);
+ e.user.connectionHost = e.params[6];
+ // Hops is a separate parameter in WHOX.
+ e.user.hops = e.params[9];
+ var account = (e.params[10] == "0" ? null : e.params[10]);
+ e.user.account = account;
+ if (11 in e.params)
+ {
+ var desc = e.decodeParam(11, e.user);
+ if (e.user.desc != desc)
+ {
+ e.userHasChanges = true;
+ e.user.desc = desc;
+ }
+ }
+ }
+ var away = (e.params[8][0] == "G");
+ if (e.user.isAway != away)
+ {
+ e.userHasChanges = true;
+ e.user.isAway = away;
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+/* end of who */
+CIRCServer.prototype.on315 =
+function serv_315 (e)
+{
+ e.user = new CIRCUser(this, null, e.params[1]);
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+/* names reply */
+CIRCServer.prototype.on353 =
+function serv_353 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[3]);
+ if (e.channel.usersStable)
+ {
+ e.channel.users = new Object();
+ e.channel.usersStable = false;
+ }
+
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ var nicks = e.params[4].split (" ");
+ var mList = this.userModes;
+
+ for (var n in nicks)
+ {
+ var nick = nicks[n];
+ if (nick == "")
+ break;
+
+ var modes = new Array();
+ var multiPrefix = (("namesx" in this.supports) && this.supports.namesx)
+ || (("multi-prefix" in this.caps)
+ && this.caps["multi-prefix"]);
+ do
+ {
+ var found = false;
+ for (var m in mList)
+ {
+ if (nick[0] == mList[m].symbol)
+ {
+ nick = nick.substr(1);
+ modes.push(mList[m].mode);
+ found = true;
+ break;
+ }
+ }
+ } while (found && multiPrefix);
+
+ var ary = nick.match(/([^ ]+)!([^ ]+)@(.*)/);
+ var user = null;
+ var host = null;
+
+ if (this.caps["userhost-in-names"] && ary)
+ {
+ nick = ary[1];
+ user = ary[2];
+ host = ary[3];
+ }
+
+ new CIRCChanUser(e.channel, null, nick, modes, true, user, host);
+ }
+
+ return true;
+}
+
+/* end of names */
+CIRCServer.prototype.on366 =
+function serv_366 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+ e.channel.usersStable = true;
+
+ return true;
+}
+
+/* channel time stamp? */
+CIRCServer.prototype.on329 =
+function serv_329 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+ e.channel.timeStamp = new Date (Number(e.params[3]) * 1000);
+
+ return true;
+}
+
+/* channel mode reply */
+CIRCServer.prototype.on324 =
+function serv_324 (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = this;
+ e.type = "chanmode";
+ e.destMethod = "onChanMode";
+
+ return true;
+}
+
+/* channel ban entry */
+CIRCServer.prototype.on367 =
+function serv_367(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+ e.ban = e.params[3];
+ e.user = new CIRCUser(this, null, e.params[4]);
+ e.banTime = new Date (Number(e.params[5]) * 1000);
+
+ if (typeof e.channel.bans[e.ban] == "undefined")
+ {
+ e.channel.bans[e.ban] = {host: e.ban, user: e.user, time: e.banTime };
+ var ban_evt = new CEvent("channel", "ban", e.channel, "onBan");
+ ban_evt.tags = e.tags;
+ ban_evt.channel = e.channel;
+ ban_evt.ban = e.ban;
+ ban_evt.source = e.user;
+ this.parent.eventPump.addEvent(ban_evt);
+ }
+
+ return true;
+}
+
+/* channel ban list end */
+CIRCServer.prototype.on368 =
+function serv_368(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ /* This flag is cleared in a timeout (which occurs right after the current
+ * message has been processed) so that the new event target (the channel)
+ * will still have the flag set when it executes.
+ */
+ if ("pendingBanList" in e.channel)
+ setTimeout(function() { delete e.channel.pendingBanList; }, 0);
+
+ return true;
+}
+
+/* channel except entry */
+CIRCServer.prototype.on348 =
+function serv_348(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+ e.except = e.params[3];
+ e.user = new CIRCUser(this, null, e.params[4]);
+ e.exceptTime = new Date (Number(e.params[5]) * 1000);
+
+ if (typeof e.channel.excepts[e.except] == "undefined")
+ {
+ e.channel.excepts[e.except] = {host: e.except, user: e.user,
+ time: e.exceptTime };
+ }
+
+ return true;
+}
+
+/* channel except list end */
+CIRCServer.prototype.on349 =
+function serv_349(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ if ("pendingExceptList" in e.channel)
+ setTimeout(function (){ delete e.channel.pendingExceptList; }, 0);
+
+ return true;
+}
+
+/* don't have operator perms */
+CIRCServer.prototype.on482 =
+function serv_482(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ /* Some servers (e.g. Hybrid) don't let you get the except list without ops,
+ * so we might be waiting for this list forever otherwise.
+ */
+ if ("pendingExceptList" in e.channel)
+ setTimeout(function (){ delete e.channel.pendingExceptList; }, 0);
+
+ return true;
+}
+
+/* userhost reply */
+CIRCServer.prototype.on302 =
+function serv_302(e)
+{
+ var list = e.params[2].split(/\s+/);
+
+ for (var i = 0; i < list.length; i++)
+ {
+ // <reply> ::= <nick>['*'] '=' <'+'|'-'><hostname>
+ // '*' == IRCop. '+' == here, '-' == away.
+ var data = list[i].match(/^(.*)(\*?)=([-+])(.*)@(.*)$/);
+ if (data)
+ this.addUser(data[1], data[4], data[5]);
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+/* CAP response */
+CIRCServer.prototype.onCap =
+function my_cap (e)
+{
+ // We expect some sort of identifier.
+ if (e.params.length < 2)
+ return;
+
+ if (e.params[2] == "LS")
+ {
+ /* We're getting a list of all server capabilities. Set them all to
+ * null (if they don't exist) to indicate we don't know if they're
+ * enabled or not (but this will evaluate to false which matches that
+ * capabilities are only enabled on request).
+ */
+ var caps = e.params[3].split(/\s+/);
+ var multiline = (e.params[3] == "*");
+ if (multiline)
+ caps = e.params[4].split(/\s+/);
+
+ for (var i = 0; i < caps.length; i++)
+ {
+ var [cap, value] = caps[i].split(/=(.+)/);
+ cap = cap.replace(/^-/, "").trim();
+ if (!(cap in this.caps))
+ this.caps[cap] = null;
+ if (value)
+ this.capvals[cap] = value;
+ }
+
+ // Don't do anything until the end of the response.
+ if (multiline)
+ return true;
+
+ //Only request capabilities we support if we are connecting.
+ if (this.pendingCapNegotiation)
+ {
+ // If we have an STS upgrade policy, immediately disconnect
+ // and reconnect on the secure port.
+ if (this.parent.STS_MODULE.ENABLED && ("sts" in this.caps) && !this.isSecure)
+ {
+ var policy = this.parent.STS_MODULE.parseParameters(this.capvals["sts"]);
+ if (policy && policy.port)
+ {
+ e.stsUpgradePort = policy.port;
+ e.destObject = this.parent;
+ e.set = "network";
+ return false;
+ }
+ }
+
+ // Request STARTTLS if we are configured to do so.
+ if (!this.isSecure && ("tls" in this.caps) && this.parent.UPGRADE_INSECURE)
+ this.sendData("STARTTLS\n");
+
+ var caps_req = JSIRCV3_SUPPORTED_CAPS.filter(i => (i in this.caps));
+
+ // Don't send requests for these caps.
+ let caps_noreq = ["tls", "sts", "echo-message"];
+
+ if (!this.parent.USE_SASL)
+ caps_noreq.push("sasl");
+
+ caps_req = caps_req.filter(i => caps_noreq.indexOf(i) === -1);
+
+ if (caps_req.length > 0)
+ {
+ caps_req = caps_req.join(" ");
+ e.server.sendData("CAP REQ :" + caps_req + "\n");
+ }
+ else
+ {
+ e.server.sendData("CAP END\n");
+ delete this.pendingCapNegotiation;
+ }
+ }
+ }
+ else if (e.params[2] == "LIST")
+ {
+ /* Received list of enabled capabilities. Just use this as a sanity
+ * check. */
+ var caps = e.params[3].trim().split(/\s+/);
+ var multiline = (e.params[3] == "*");
+ if (multiline)
+ caps = e.params[4].trim().split(/\s+/);
+
+ for (var i = 0; i < caps.length; i++)
+ {
+ this.caps[caps[i]] = true;
+ }
+
+ // Don't do anything until the end of the response.
+ if (multiline)
+ return true;
+ }
+ else if (e.params[2] == "ACK")
+ {
+ /* One or more capability changes have been successfully applied. An enabled
+ * capability is just "cap" whilst a disabled capability is "-cap".
+ */
+ var caps = e.params[3].trim().split(/\s+/);
+ e.capsOn = new Array();
+ e.capsOff = new Array();
+ for (var i = 0; i < caps.length; i++)
+ {
+ var cap = caps[i].replace(/^-/,"").trim();
+ var enabled = caps[i][0] != "-";
+ if (enabled)
+ e.capsOn.push(cap);
+ else
+ e.capsOff.push(cap);
+ this.caps[cap] = enabled;
+ }
+
+ // Try SASL authentication if we are configured to do so.
+ if (caps.indexOf("sasl") != -1)
+ {
+ var ev = new CEvent("server", "sasl-start", this, "onSASLStart");
+ ev.server = this;
+ if (this.capvals["sasl"])
+ ev.mechs = this.capvals["sasl"].toLowerCase().split(/,/);
+ ev.destObject = this.parent;
+ this.parent.eventPump.routeEvent(ev);
+
+ if (this.pendingCapNegotiation)
+ return true;
+ }
+
+ if (this.pendingCapNegotiation)
+ {
+ e.server.sendData("CAP END\n");
+ delete this.pendingCapNegotiation;
+
+ //Don't show the raw message while connecting.
+ return true;
+ }
+ }
+ else if (e.params[2] == "NAK")
+ {
+ // A capability change has failed.
+ var caps = e.params[3].trim().split(/\s+/);
+ e.caps = new Array();
+ for (var i = 0; i < caps.length; i++)
+ {
+ var cap = caps[i].replace(/^-/, "").trim();
+ e.caps.push(cap);
+ }
+
+ if (this.pendingCapNegotiation)
+ {
+ e.server.sendData("CAP END\n");
+ delete this.pendingCapNegotiation;
+
+ //Don't show the raw message while connecting.
+ return true;
+ }
+ }
+ else if (e.params[2] == "NEW")
+ {
+ // A capability is now available, so request it if we can.
+ var caps = e.params[3].split(/\s+/);
+ e.newcaps = [];
+ for (var i = 0; i < caps.length; i++)
+ {
+ var [cap, value] = caps[i].split(/=(.+)/);
+ cap = cap.trim();
+ this.caps[cap] = null;
+ e.newcaps.push(cap);
+ if (value)
+ this.capvals[cap] = value;
+ }
+
+ var caps_req = JSIRCV3_SUPPORTED_CAPS.filter(i => (i in e.newcaps));
+
+ // Don't send requests for these caps.
+ caps_noreq = ["tls", "sts", "sasl", "echo-message"];
+ caps_req = caps_req.filter(i => caps_noreq.indexOf(i) === -1);
+
+ if (caps_req.length > 0)
+ {
+ caps_req = caps_req.join(" ");
+ e.server.sendData("CAP REQ :" + caps_req + "\n");
+ }
+ }
+ else if (e.params[2] == "DEL")
+ {
+ // A capability is no longer available.
+ var caps = e.params[3].split(/\s+/);
+ var caps_nodel = ["sts"];
+ for (var i = 0; i < caps.length; i++)
+ {
+ var cap = caps[i].split(/=(.+)/)[0];
+ cap = cap.trim();
+
+ if (arrayContains(caps_nodel, cap))
+ continue;
+
+ this.caps[cap] = null;
+ }
+ }
+ else
+ {
+ dd("Unknown CAP reply " + e.params[2]);
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* BATCH start or end */
+CIRCServer.prototype.onBatch =
+function serv_batch(e)
+{
+ // We should at least get a ref tag.
+ if (e.params.length < 2)
+ return false;
+
+ e.reftag = e.params[1].substring(1);
+ switch (e.params[1][0])
+ {
+ case "+":
+ e.starting = true;
+ break;
+ case "-":
+ e.starting = false;
+ break;
+ default:
+ // Invalid reference tag.
+ return false;
+ }
+ var isPlayback = (this.batches && this.batches[e.reftag] &&
+ this.batches[e.reftag].playback);
+
+ if (!isPlayback)
+ {
+ if (e.starting)
+ {
+ // We're starting a batch, so we also need a type.
+ if (e.params.length < 3)
+ return false;
+
+ if (!this.batches)
+ this.batches = new Object();
+ // The batch object holds the messages queued up as part
+ // of this batch, and a boolean value indicating whether
+ // it is being played back.
+ var newBatch = new Object();
+ newBatch.messages = [e];
+ newBatch.type = e.params[2].toUpperCase();
+ if (e.params[3] && (e.params[3] in this.channels))
+ {
+ newBatch.destObject = this.channels[e.params[3]];
+ }
+ else if (e.params[3] && (e.params[3] in this.users))
+ {
+ newBatch.destObject = this.users[e.params[3]];
+ }
+ else
+ {
+ newBatch.destObject = this.parent;
+ }
+ newBatch.playback = false;
+ this.batches[e.reftag] = newBatch;
+ }
+ else
+ {
+ if (!this.batches[e.reftag])
+ {
+ // Got a close tag without an open tag, so ignore it.
+ return false;
+ }
+
+ var batch = this.batches[e.reftag];
+
+ // Closing the batch, prepare for playback.
+ batch.messages.push(e);
+ batch.playback = true;
+ if (e.tags["batch"])
+ {
+ // We are an inner batch. Append the message queue
+ // to the outer batch's message queue.
+ var parentRef = e.tags["batch"];
+ var parentMsgs = this.batches[parentRef].messages;
+ parentMsgs = parentMsgs.concat(batch.messages);
+ }
+ else
+ {
+ // We are an outer batch. Playback!
+ for (var i = 0; i < batch.messages.length; i++)
+ {
+ var ev = batch.messages[i];
+ ev.type = "parseddata";
+ ev.destObject = this;
+ ev.destMethod = "onParsedData";
+ this.parent.eventPump.routeEvent(ev);
+ }
+ }
+ }
+ return false;
+ }
+ else
+ {
+ // Batch command is ready for handling.
+ e.batchtype = this.batches[e.reftag].type;
+ e.destObject = this.batches[e.reftag].destObject;
+ if (e.destObject.TYPE == "CIRCChannel")
+ {
+ e.set = "channel";
+ }
+ else
+ {
+ e.set = "network";
+ }
+
+ if (!e.starting)
+ {
+ // If we've reached the end of a batch in playback,
+ // do some cleanup.
+ delete this.batches[e.reftag];
+ if (Object.entries(this.batches).length == 0)
+ delete this.batches;
+ }
+
+ // Massage the batchtype into a method name for handlers:
+ // netsplit - onNetsplitBatch
+ // some-batch-type - onSomeBatchTypeBatch
+ // example.com/example - onExampleComExampleBatch
+ var batchCode = e.batchtype.split(/[\.\/-]/).map(function(s)
+ {
+ return s[0].toUpperCase() + s.substr(1).toLowerCase();
+ }).join("");
+ e.destMethod = "on" + batchCode + "Batch";
+
+ if (!e.destObject[e.destMethod])
+ e.destMethod = "onUnknownBatch";
+ }
+}
+
+/* SASL authentication responses */
+CIRCServer.prototype.on902 = /* Nick locked */
+CIRCServer.prototype.on903 = /* Auth success */
+CIRCServer.prototype.on904 = /* Auth failed */
+CIRCServer.prototype.on905 = /* Command too long */
+CIRCServer.prototype.on906 = /* Aborted */
+CIRCServer.prototype.on907 = /* Already authenticated */
+CIRCServer.prototype.on908 = /* Mechanisms */
+function cap_on900(e)
+{
+ if (this.pendingCapNegotiation)
+ {
+ delete this.pendingCapNegotiation;
+ this.sendData("CAP END\n");
+ }
+
+ if (e.code == "908")
+ {
+ // Update our list of SASL mechanics.
+ this.capvals["sasl"] = e.params[2];
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* STARTTLS responses */
+CIRCServer.prototype.on670 = /* Success */
+function cap_on670(e)
+{
+ this.caps["tls"] = true;
+ e.server.connection.startTLS();
+ e.server.isSecure = true;
+ e.server.isStartTLS = true;
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+CIRCServer.prototype.on691 = /* Failure */
+function cap_on691(e)
+{
+ this.caps["tls"] = false;
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* User away status changed */
+CIRCServer.prototype.onAway =
+function serv_away(e)
+{
+ e.user.isAway = e.params[1] ? true : false;
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* User host changed */
+CIRCServer.prototype.onChghost =
+function serv_chghost(e)
+{
+ this.users[e.user.collectionKey].name = e.params[1];
+ this.users[e.user.collectionKey].host = e.params[2];
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+/* user changed the mode */
+CIRCServer.prototype.onMode =
+function serv_mode (e)
+{
+ e.destObject = this;
+ /* modes are not allowed in +channels -> no need to test that here.. */
+ if (arrayIndexOf(this.channelTypes, e.params[1][0]) != -1)
+ {
+ e.channel = new CIRCChannel(this, null, e.params[1]);
+ if ("user" in e && e.user)
+ e.user = new CIRCChanUser(e.channel, e.user.unicodeName);
+ e.type = "chanmode";
+ e.destMethod = "onChanMode";
+ }
+ else
+ {
+ e.type = "usermode";
+ e.destMethod = "onUserMode";
+ }
+
+ return true;
+}
+
+CIRCServer.prototype.onUserMode =
+function serv_usermode (e)
+{
+ e.user = new CIRCUser(this, null, e.params[1])
+ e.user.modestr = e.params[2];
+ e.destObject = this.parent;
+ e.set = "network";
+
+ // usermode usually happens on connect, after the MOTD, so it's a good
+ // place to kick off the lag timer.
+ this.updateLagTimer();
+
+ return true;
+}
+
+CIRCServer.prototype.onChanMode =
+function serv_chanmode (e)
+{
+ var modifier = "";
+ var params_eaten = 0;
+ var BASE_PARAM;
+
+ if (e.code.toUpperCase() == "MODE")
+ BASE_PARAM = 2;
+ else
+ if (e.code == "324")
+ BASE_PARAM = 3;
+ else
+ {
+ dd ("** INVALID CODE in ChanMode event **");
+ return false;
+ }
+
+ var mode_str = e.params[BASE_PARAM];
+ params_eaten++;
+
+ e.modeStr = mode_str;
+ e.usersAffected = new Array();
+
+ var nick;
+ var user;
+ var umList = this.userModes;
+ var cmList = this.channelModes;
+ var modeMap = this.canonicalChanModes;
+ var canonicalModeValue;
+
+ for (var i = 0; i < mode_str.length ; i++)
+ {
+ /* Take care of modifier first. */
+ if ((mode_str[i] == '+') || (mode_str[i] == '-'))
+ {
+ modifier = mode_str[i];
+ continue;
+ }
+
+ var done = false;
+ for (var m in umList)
+ {
+ if ((mode_str[i] == umList[m].mode) && (modifier != ""))
+ {
+ nick = e.params[BASE_PARAM + params_eaten];
+ user = new CIRCChanUser(e.channel, null, nick,
+ [ modifier + umList[m].mode ]);
+ params_eaten++;
+ e.usersAffected.push (user);
+ done = true;
+ break;
+ }
+ }
+ if (done)
+ continue;
+
+ // Update legacy canonical modes if necessary.
+ if (mode_str[i] in modeMap)
+ {
+ // Get the data in case we need it, but don't increment the counter.
+ var datacounter = BASE_PARAM + params_eaten;
+ var data = (datacounter in e.params) ? e.params[datacounter] : null;
+ canonicalModeValue = modeMap[mode_str[i]].getValue(modifier, data);
+ e.channel.mode[modeMap[mode_str[i]].name] = canonicalModeValue;
+ }
+
+ if (arrayContains(cmList.a, mode_str[i]))
+ {
+ var data = e.params[BASE_PARAM + params_eaten++];
+ if (modifier == "+")
+ {
+ e.channel.mode.modeA[data] = true;
+ }
+ else
+ {
+ if (data in e.channel.mode.modeA)
+ {
+ delete e.channel.mode.modeA[data];
+ }
+ else
+ {
+ dd("** Trying to remove channel mode '" + mode_str[i] +
+ "'/'" + data + "' which does not exist in list.");
+ }
+ }
+ }
+ else if (arrayContains(cmList.b, mode_str[i]))
+ {
+ var data = e.params[BASE_PARAM + params_eaten++];
+ if (modifier == "+")
+ {
+ e.channel.mode.modeB[mode_str[i]] = data;
+ }
+ else
+ {
+ // Save 'null' even though we have some data.
+ e.channel.mode.modeB[mode_str[i]] = null;
+ }
+ }
+ else if (arrayContains(cmList.c, mode_str[i]))
+ {
+ if (modifier == "+")
+ {
+ var data = e.params[BASE_PARAM + params_eaten++];
+ e.channel.mode.modeC[mode_str[i]] = data;
+ }
+ else
+ {
+ e.channel.mode.modeC[mode_str[i]] = null;
+ }
+ }
+ else if (arrayContains(cmList.d, mode_str[i]))
+ {
+ e.channel.mode.modeD[mode_str[i]] = (modifier == "+");
+ }
+ else
+ {
+ dd("** UNKNOWN mode symbol '" + mode_str[i] + "' in ChanMode event **");
+ }
+ }
+
+ e.destObject = e.channel;
+ e.set = "channel";
+ return true;
+}
+
+CIRCServer.prototype.onNick =
+function serv_nick (e)
+{
+ var newNick = e.params[1];
+ var newKey = ":" + this.toLowerCase(newNick);
+ var oldKey = e.user.collectionKey;
+ var ev;
+
+ renameProperty (this.users, oldKey, newKey);
+ e.oldNick = e.user.unicodeName;
+ e.user.changeNick(toUnicode(newNick, this));
+
+ for (var c in this.channels)
+ {
+ if (this.channels[c].active &&
+ ((oldKey in this.channels[c].users) || e.user == this.me))
+ {
+ var cuser = this.channels[c].users[oldKey];
+ renameProperty (this.channels[c].users, oldKey, newKey);
+
+ // User must be a channel user, update sort name for userlist,
+ // before we route the event further:
+ cuser.updateSortName();
+
+ ev = new CEvent ("channel", "nick", this.channels[c], "onNick");
+ ev.tags = e.tags;
+ ev.channel = this.channels[c];
+ ev.user = cuser;
+ ev.server = this;
+ ev.oldNick = e.oldNick;
+ this.parent.eventPump.routeEvent(ev);
+ }
+ }
+
+ if (e.user == this.me)
+ {
+ /* if it was me, tell the network about the nick change as well */
+ ev = new CEvent ("network", "nick", this.parent, "onNick");
+ ev.tags = e.tags;
+ ev.user = e.user;
+ ev.server = this;
+ ev.oldNick = e.oldNick;
+ this.parent.eventPump.routeEvent(ev);
+ }
+
+ e.destObject = e.user;
+ e.set = "user";
+
+ return true;
+}
+
+CIRCServer.prototype.onQuit =
+function serv_quit (e)
+{
+ var reason = e.decodeParam(1);
+
+ for (var c in e.server.channels)
+ {
+ if (e.server.channels[c].active &&
+ e.user.collectionKey in e.server.channels[c].users)
+ {
+ var ev = new CEvent ("channel", "quit", e.server.channels[c],
+ "onQuit");
+ ev.tags = e.tags;
+ ev.user = e.server.channels[c].users[e.user.collectionKey];
+ ev.channel = e.server.channels[c];
+ ev.server = ev.channel.parent;
+ ev.reason = reason;
+ this.parent.eventPump.routeEvent(ev);
+ delete e.server.channels[c].users[e.user.collectionKey];
+ }
+ }
+
+ this.users[e.user.collectionKey].lastQuitMessage = reason;
+ this.users[e.user.collectionKey].lastQuitDate = new Date();
+
+ // 0 == prune onQuit.
+ if (this.PRUNE_OLD_USERS == 0)
+ delete this.users[e.user.collectionKey];
+
+ e.reason = reason;
+ e.destObject = e.user;
+ e.set = "user";
+
+ return true;
+}
+
+CIRCServer.prototype.onPart =
+function serv_part (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[1]);
+ e.reason = (e.params.length > 2) ? e.decodeParam(2, e.channel) : "";
+ e.user = new CIRCChanUser(e.channel, e.user.unicodeName);
+ if (userIsMe(e.user))
+ {
+ e.channel.active = false;
+ e.channel.joined = false;
+ }
+ e.channel.removeUser(e.user.encodedName);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+CIRCServer.prototype.onKick =
+function serv_kick (e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[1]);
+ e.lamer = new CIRCChanUser(e.channel, null, e.params[2]);
+ delete e.channel.users[e.lamer.collectionKey];
+ if (userIsMe(e.lamer))
+ {
+ e.channel.active = false;
+ e.channel.joined = false;
+ }
+ e.reason = e.decodeParam(3, e.channel);
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+CIRCServer.prototype.onJoin =
+function serv_join(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[1]);
+ // Passing undefined here because CIRCChanUser doesn't like "null"
+ e.user = new CIRCChanUser(e.channel, e.user.unicodeName, null,
+ undefined, true);
+
+ if (e.params[2] && e.params[3])
+ {
+ var account = (e.params[2] == "*" ? null : e.params[2]);
+ var desc = e.decodeParam([3], e.user);
+ this.users[e.user.collectionKey].account = account;
+ this.users[e.user.collectionKey].desc = desc;
+ }
+
+ if (userIsMe(e.user))
+ {
+ var delayFn1 = function(t) {
+ if (!e.channel.active)
+ return;
+
+ // Give us the channel mode!
+ e.server.sendData("MODE " + e.channel.encodedName + "\n");
+ };
+ // Between 1s - 3s.
+ setTimeout(delayFn1, 1000 + 2000 * Math.random(), this);
+
+ var delayFn2 = function(t) {
+ if (!e.channel.active)
+ return;
+
+ // Get a full list of bans and exceptions, if supported.
+ if (arrayContains(t.channelModes.a, "b"))
+ {
+ e.server.sendData("MODE " + e.channel.encodedName + " +b\n");
+ e.channel.pendingBanList = true;
+ }
+ if (arrayContains(t.channelModes.a, "e"))
+ {
+ e.server.sendData("MODE " + e.channel.encodedName + " +e\n");
+ e.channel.pendingExceptList = true;
+ }
+
+ //If away-notify is active, query the list of users for away status.
+ if (e.server.caps["away-notify"])
+ {
+ // If the server supports extended who, use it.
+ // This lets us initialize the account property.
+ if (e.server.supports["whox"])
+ e.server.who(e.channel.unicodeName + " %acdfhnrstu," + e.server.WHOX_TYPE);
+ else
+ e.server.who(e.channel.unicodeName);
+ }
+ };
+ // Between 10s - 20s.
+ setTimeout(delayFn2, 10000 + 10000 * Math.random(), this);
+
+ /* Clean up the topic, since servers don't always send RPL_NOTOPIC
+ * (no topic set) when joining a channel without a topic. In fact,
+ * the RFC even fails to mention sending a RPL_NOTOPIC after a join!
+ */
+ e.channel.topic = "";
+ e.channel.topicBy = null;
+ e.channel.topicDate = null;
+
+ // And we're in!
+ e.channel.active = true;
+ e.channel.joined = true;
+ }
+
+ e.destObject = e.channel;
+ e.set = "channel";
+
+ return true;
+}
+
+CIRCServer.prototype.onAccount =
+function serv_acct(e)
+{
+ var account = (e.params[1] == "*" ? null : e.params[1]);
+ this.users[e.user.collectionKey].account = account;
+
+ return true;
+}
+
+CIRCServer.prototype.onPing =
+function serv_ping (e)
+{
+ /* non-queued send, so we can calcualte lag */
+ this.connection.sendData("PONG :" + e.params[1] + "\n");
+ this.updateLagTimer();
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+CIRCServer.prototype.onPong =
+function serv_pong (e)
+{
+ if (e.params[2] != "LAGTIMER")
+ return true;
+
+ if (this.lastPingSent)
+ this.lag = (new Date() - this.lastPingSent) / 1000;
+
+ this.lastPingSent = null;
+
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+CIRCServer.prototype.onInvite =
+function serv_invite(e)
+{
+ e.channel = new CIRCChannel(this, null, e.params[2]);
+
+ e.destObject = this.parent;
+ e.set = "network";
+}
+
+CIRCServer.prototype.onNotice =
+CIRCServer.prototype.onPrivmsg =
+CIRCServer.prototype.onTagmsg =
+function serv_notice_privmsg (e)
+{
+ var targetName = e.params[1];
+
+ if (this.userModes)
+ {
+ // Strip off one (and only one) user mode prefix.
+ for (var i = 0; i < this.userModes.length; i++)
+ {
+ if (targetName[0] == this.userModes[i].symbol)
+ {
+ e.msgPrefix = this.userModes[i];
+ targetName = targetName.substr(1);
+ break;
+ }
+ }
+ }
+
+ /* setting replyTo provides a standard place to find the target for */
+ /* replies associated with this event. */
+ if (arrayIndexOf(this.channelTypes, targetName[0]) != -1)
+ {
+ e.channel = new CIRCChannel(this, null, targetName);
+ if ("user" in e)
+ e.user = new CIRCChanUser(e.channel, e.user.unicodeName);
+ e.replyTo = e.channel;
+ e.set = "channel";
+ }
+ else if (!("user" in e))
+ {
+ e.set = "network";
+ e.destObject = this.parent;
+ return true;
+ }
+ else
+ {
+ e.set = "user";
+ e.replyTo = e.user; /* send replies to the user who sent the message */
+ }
+
+ /* The capability identify-msg adds a + or - in front the message to
+ * indicate their network registration status.
+ */
+ if (("identify-msg" in this.caps) && this.caps["identify-msg"])
+ {
+ e.identifyMsg = false;
+ var flag = e.params[2].substring(0,1);
+ if (flag == "+")
+ {
+ e.identifyMsg = true;
+ e.params[2] = e.params[2].substring(1);
+ }
+ else if (flag == "-")
+ {
+ e.params[2] = e.params[2].substring(1);
+ }
+ else
+ {
+ // Just print to console on failure - or we'd spam the user
+ dd("Warning: IDENTIFY-MSG is on, but there's no message flags");
+ }
+ }
+
+ // TAGMSG doesn't have a message parameter, so just pass it on.
+ if (e.code == "TAGMSG")
+ {
+ e.destObject = e.replyTo;
+ return true;
+ }
+
+ if (e.params[2].search (/^\x01[^ ]+.*\x01$/) != -1)
+ {
+ if (e.code == "NOTICE")
+ {
+ e.type = "ctcp-reply";
+ e.destMethod = "onCTCPReply";
+ }
+ else // e.code == "PRIVMSG"
+ {
+ e.type = "ctcp";
+ e.destMethod = "onCTCP";
+ }
+ e.set = "server";
+ e.destObject = this;
+ }
+ else
+ {
+ e.msg = e.decodeParam(2, e.replyTo);
+ e.destObject = e.replyTo;
+ }
+
+ return true;
+}
+
+CIRCServer.prototype.onWallops =
+function serv_wallops(e)
+{
+ if (("user" in e) && e.user)
+ {
+ e.msg = e.decodeParam(1, e.user);
+ e.replyTo = e.user;
+ }
+ else
+ {
+ e.msg = e.decodeParam(1);
+ e.replyTo = this;
+ }
+
+ e.destObject = this.parent;
+ e.set = "network";
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPReply =
+function serv_ctcpr (e)
+{
+ var ary = e.params[2].match (/^\x01([^ ]+) ?(.*)\x01$/i);
+
+ if (ary == null)
+ return false;
+
+ e.CTCPData = ary[2] ? ary[2] : "";
+
+ e.CTCPCode = ary[1].toLowerCase();
+ e.type = "ctcp-reply-" + e.CTCPCode;
+ e.destMethod = "onCTCPReply" + ary[1][0].toUpperCase() +
+ ary[1].substr (1, ary[1].length).toLowerCase();
+
+ if (typeof this[e.destMethod] != "function")
+ { /* if there's no place to land the event here, try to forward it */
+ e.destObject = this.parent;
+ e.set = "network";
+
+ if (typeof e.destObject[e.destMethod] != "function")
+ { /* if there's no place to forward it, send it to unknownCTCP */
+ e.type = "unk-ctcp-reply";
+ e.destMethod = "onUnknownCTCPReply";
+ if (e.destMethod in this)
+ {
+ e.set = "server";
+ e.destObject = this;
+ }
+ else
+ {
+ e.set = "network";
+ e.destObject = this.parent;
+ }
+ }
+ }
+ else
+ e.destObject = this;
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCP =
+function serv_ctcp (e)
+{
+ var ary = e.params[2].match (/^\x01([^ ]+) ?(.*)\x01$/i);
+
+ if (ary == null)
+ return false;
+
+ e.CTCPData = ary[2] ? ary[2] : "";
+
+ e.CTCPCode = ary[1].toLowerCase();
+ if (e.CTCPCode.search (/^reply/i) == 0)
+ {
+ dd ("dropping spoofed reply.");
+ return false;
+ }
+
+ e.CTCPCode = toUnicode(e.CTCPCode, e.replyTo);
+ e.CTCPData = toUnicode(e.CTCPData, e.replyTo);
+
+ e.type = "ctcp-" + e.CTCPCode;
+ e.destMethod = "onCTCP" + ary[1][0].toUpperCase() +
+ ary[1].substr (1, ary[1].length).toLowerCase();
+
+ if (typeof this[e.destMethod] != "function")
+ { /* if there's no place to land the event here, try to forward it */
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+
+ if (typeof e.replyTo[e.destMethod] != "function")
+ { /* if there's no place to forward it, send it to unknownCTCP */
+ e.type = "unk-ctcp";
+ e.destMethod = "onUnknownCTCP";
+ }
+ }
+ else
+ e.destObject = this;
+
+ var ev = new CEvent("server", "ctcp-receive", this, "onReceiveCTCP");
+ ev.tags = e.tags;
+ ev.server = this;
+ ev.CTCPCode = e.CTCPCode;
+ ev.CTCPData = e.CTCPData;
+ ev.type = e.type;
+ ev.user = e.user;
+ ev.destObject = this.parent;
+ this.parent.eventPump.addEvent(ev);
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPClientinfo =
+function serv_ccinfo (e)
+{
+ var clientinfo = new Array();
+
+ if (e.CTCPData)
+ {
+ var cmdName = "onCTCP" + e.CTCPData[0].toUpperCase() +
+ e.CTCPData.substr (1, e.CTCPData.length).toLowerCase();
+ var helpName = cmdName.replace(/^onCTCP/, "CTCPHelp");
+
+ // Check we support the command.
+ if (cmdName in this)
+ {
+ // Do we have help for it?
+ if (helpName in this)
+ {
+ var msg;
+ if (typeof this[helpName] == "function")
+ msg = this[helpName]();
+ else
+ msg = this[helpName];
+
+ e.user.ctcp("CLIENTINFO", msg, "NOTICE");
+ }
+ else
+ {
+ e.user.ctcp("CLIENTINFO",
+ getMsg(MSG_ERR_NO_CTCP_HELP, e.CTCPData), "NOTICE");
+ }
+ }
+ else
+ {
+ e.user.ctcp("CLIENTINFO",
+ getMsg(MSG_ERR_NO_CTCP_CMD, e.CTCPData), "NOTICE");
+ }
+ return true;
+ }
+
+ for (var fname in this)
+ {
+ var ary = fname.match(/^onCTCP(.+)/);
+ if (ary && ary[1].search(/^Reply/) == -1)
+ clientinfo.push (ary[1].toUpperCase());
+ }
+
+ e.user.ctcp("CLIENTINFO", clientinfo.join(" "), "NOTICE");
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPAction =
+function serv_cact (e)
+{
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+}
+
+CIRCServer.prototype.onCTCPFinger =
+function serv_cfinger (e)
+{
+ e.user.ctcp("FINGER", this.parent.INITIAL_DESC, "NOTICE");
+ return true;
+}
+
+CIRCServer.prototype.onCTCPTime =
+function serv_cping (e)
+{
+ e.user.ctcp("TIME", new Date(), "NOTICE");
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPVersion =
+function serv_cver (e)
+{
+ var lines = e.server.VERSION_RPLY.split ("\n");
+
+ for (var i in lines)
+ e.user.ctcp("VERSION", lines[i], "NOTICE");
+
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPSource =
+function serv_csrc (e)
+{
+ e.user.ctcp("SOURCE", this.SOURCE_RPLY, "NOTICE");
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPOs =
+function serv_os(e)
+{
+ e.user.ctcp("OS", this.OS_RPLY, "NOTICE");
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPHost =
+function serv_host(e)
+{
+ e.user.ctcp("HOST", this.HOST_RPLY, "NOTICE");
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPPing =
+function serv_cping (e)
+{
+ /* non-queued send */
+ this.connection.sendData("NOTICE " + e.user.encodedName + " :\01PING " +
+ e.CTCPData + "\01\n");
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+
+ return true;
+}
+
+CIRCServer.prototype.onCTCPDcc =
+function serv_dcc (e)
+{
+ var ary = e.CTCPData.match (/([^ ]+)? ?(.*)/);
+
+ e.DCCData = ary[2];
+ e.type = "dcc-" + ary[1].toLowerCase();
+ e.destMethod = "onDCC" + ary[1][0].toUpperCase() +
+ ary[1].substr (1, ary[1].length).toLowerCase();
+
+ if (typeof this[e.destMethod] != "function")
+ { /* if there's no place to land the event here, try to forward it */
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+ }
+ else
+ e.destObject = this;
+
+ return true;
+}
+
+CIRCServer.prototype.onDCCChat =
+function serv_dccchat (e)
+{
+ var ary = e.DCCData.match (/(chat) (\d+) (\d+)/i);
+
+ if (ary == null)
+ return false;
+
+ e.id = ary[2];
+ // Longword --> dotted IP conversion.
+ var host = Number(e.id);
+ e.host = ((host >> 24) & 0xFF) + "." +
+ ((host >> 16) & 0xFF) + "." +
+ ((host >> 8) & 0xFF) + "." +
+ (host & 0xFF);
+ e.port = Number(ary[3]);
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+
+ return true;
+}
+
+CIRCServer.prototype.onDCCSend =
+function serv_dccsend (e)
+{
+ var ary = e.DCCData.match(/([^ ]+) (\d+) (\d+) (\d+)/);
+
+ /* Just for mIRC: filenames with spaces may be enclosed in double-quotes.
+ * (though by default it replaces spaces with underscores, but we might as
+ * well cope). */
+ if ((ary[1][0] == '"') || (ary[1][ary[1].length - 1] == '"'))
+ ary = e.DCCData.match(/"(.+)" (\d+) (\d+) (\d+)/);
+
+ if (ary == null)
+ return false;
+
+ e.file = ary[1];
+ e.id = ary[2];
+ // Longword --> dotted IP conversion.
+ var host = Number(e.id);
+ e.host = ((host >> 24) & 0xFF) + "." +
+ ((host >> 16) & 0xFF) + "." +
+ ((host >> 8) & 0xFF) + "." +
+ (host & 0xFF);
+ e.port = Number(ary[3]);
+ e.size = Number(ary[4]);
+ e.destObject = e.replyTo;
+ e.set = (e.replyTo == e.user) ? "user" : "channel";
+
+ return true;
+}
+
+function CIRCChannel(parent, unicodeName, encodedName)
+{
+ // Both unicodeName and encodedName are optional, but at least one must be
+ // present.
+
+ if (!encodedName && !unicodeName)
+ throw "Hey! Come on, I need either an encoded or a Unicode name.";
+ if (!encodedName)
+ encodedName = fromUnicode(unicodeName, parent);
+
+ let collectionKey = ":" + parent.toLowerCase(encodedName);
+ if (collectionKey in parent.channels)
+ return parent.channels[collectionKey];
+
+ this.parent = parent;
+ this.encodedName = encodedName;
+ this.canonicalName = collectionKey.substr(1);
+ this.collectionKey = collectionKey;
+ this.unicodeName = unicodeName || toUnicode(encodedName, this);
+ this.viewName = this.unicodeName;
+
+ this.users = new Object();
+ this.bans = new Object();
+ this.excepts = new Object();
+ this.mode = new CIRCChanMode(this);
+ this.usersStable = true;
+ /* These next two flags represent a subtle difference in state:
+ * active - in the channel, from the server's point of view.
+ * joined - in the channel, from the user's point of view.
+ * e.g. parting the channel clears both, but being disconnected only
+ * clears |active| - the user still wants to be in the channel, even
+ * though they aren't physically able to until we've reconnected.
+ */
+ this.active = false;
+ this.joined = false;
+
+ this.parent.channels[this.collectionKey] = this;
+ if ("onInit" in this)
+ this.onInit();
+
+ return this;
+}
+
+CIRCChannel.prototype.TYPE = "IRCChannel";
+CIRCChannel.prototype.topic = "";
+
+// Returns the IRC URL representation of this channel.
+CIRCChannel.prototype.getURL =
+function chan_geturl()
+{
+ var target = this.encodedName;
+ var flags = this.mode.key ? ["needkey"] : [];
+
+ if ((target[0] == "#") && (target.length > 1) &&
+ arrayIndexOf(this.parent.channelTypes, target[1]) == -1)
+ {
+ /* First character is "#" (which we're allowed to omit), and the
+ * following character is NOT a valid prefix, so it's safe to remove.
+ */
+ target = target.substr(1);
+ }
+ return this.parent.parent.getURL(target, flags);
+}
+
+CIRCChannel.prototype.rehome =
+function chan_rehome(newParent)
+{
+ delete this.parent.channels[this.collectionKey];
+ this.parent = newParent;
+ this.parent.channels[this.collectionKey] = this;
+}
+
+CIRCChannel.prototype.addUser =
+function chan_adduser (unicodeName, modes)
+{
+ return new CIRCChanUser(this, unicodeName, null, modes);
+}
+
+CIRCChannel.prototype.getUser =
+function chan_getuser(nick)
+{
+ // Try assuming it's an encodedName first.
+ let tnick = ":" + this.parent.toLowerCase(nick);
+ if (tnick in this.users)
+ return this.users[tnick];
+
+ // Ok, failed, so try assuming it's a unicodeName.
+ tnick = ":" + this.parent.toLowerCase(fromUnicode(nick, this.parent));
+ if (tnick in this.users)
+ return this.users[tnick];
+
+ return null;
+}
+
+CIRCChannel.prototype.removeUser =
+function chan_removeuser(nick)
+{
+ // Try assuming it's an encodedName first.
+ let key = ":" + this.parent.toLowerCase(nick);
+ if (key in this.users)
+ delete this.users[key]; // see ya
+
+ // Ok, failed, so try assuming it's a unicodeName.
+ key = ":" + this.parent.toLowerCase(fromUnicode(nick, this.parent));
+ if (key in this.users)
+ delete this.users[key];
+}
+
+CIRCChannel.prototype.getUsersLength =
+function chan_userslen (mode)
+{
+ var i = 0;
+ var p;
+ this.opCount = 0;
+ this.halfopCount = 0;
+ this.voiceCount = 0;
+
+ if (typeof mode == "undefined")
+ {
+ for (p in this.users)
+ {
+ if (this.users[p].isOp)
+ this.opCount++;
+ if (this.users[p].isHalfOp)
+ this.halfopCount++;
+ if (this.users[p].isVoice)
+ this.voiceCount++;
+ i++;
+ }
+ }
+ else
+ {
+ for (p in this.users)
+ if (arrayContains(this.users[p].modes, mode))
+ i++;
+ }
+
+ return i;
+}
+
+CIRCChannel.prototype.iAmOp =
+function chan_amop()
+{
+ return this.active && this.users[this.parent.me.collectionKey].isOp;
+}
+
+CIRCChannel.prototype.iAmHalfOp =
+function chan_amhalfop()
+{
+ return this.active && this.users[this.parent.me.collectionKey].isHalfOp;
+}
+
+CIRCChannel.prototype.iAmVoice =
+function chan_amvoice()
+{
+ return this.active && this.users[this.parent.me.collectionKey].isVoice;
+}
+
+CIRCChannel.prototype.setTopic =
+function chan_topic (str)
+{
+ this.parent.sendData ("TOPIC " + this.encodedName + " :" +
+ fromUnicode(str, this) + "\n");
+}
+
+CIRCChannel.prototype.say =
+function chan_say (msg)
+{
+ this.parent.sayTo(this.encodedName, fromUnicode(msg, this));
+}
+
+CIRCChannel.prototype.act =
+function chan_say (msg)
+{
+ this.parent.actTo(this.encodedName, fromUnicode(msg, this));
+}
+
+CIRCChannel.prototype.notice =
+function chan_notice (msg)
+{
+ this.parent.noticeTo(this.encodedName, fromUnicode(msg, this));
+}
+
+CIRCChannel.prototype.ctcp =
+function chan_ctcpto (code, msg, type)
+{
+ msg = msg || "";
+ type = type || "PRIVMSG";
+
+ this.parent.ctcpTo(this.encodedName, fromUnicode(code, this),
+ fromUnicode(msg, this), type);
+}
+
+CIRCChannel.prototype.join =
+function chan_join (key)
+{
+ if (!key)
+ key = "";
+
+ this.parent.sendData ("JOIN " + this.encodedName + " " + key + "\n");
+ return true;
+}
+
+CIRCChannel.prototype.part =
+function chan_part (reason)
+{
+ if (!reason)
+ reason = "";
+ this.parent.sendData ("PART " + this.encodedName + " :" +
+ fromUnicode(reason, this) + "\n");
+ this.users = new Object();
+ return true;
+}
+
+/**
+ * Invites a user to a channel.
+ *
+ * @param nick the user name to invite.
+ */
+CIRCChannel.prototype.invite =
+function chan_inviteuser (nick)
+{
+ var rawNick = fromUnicode(nick, this.parent);
+ this.parent.sendData("INVITE " + rawNick + " " + this.encodedName + "\n");
+ return true;
+}
+
+CIRCChannel.prototype.findUsers =
+function chan_findUsers(mask)
+{
+ var ary = [];
+ var unchecked = 0;
+ mask = getHostmaskParts(mask);
+ for (var nick in this.users)
+ {
+ var user = this.users[nick];
+ if (!user.host || !user.name)
+ unchecked++;
+ else if (hostmaskMatches(user, mask))
+ ary.push(user);
+ }
+ return { users: ary, unchecked: unchecked };
+}
+
+/**
+ * Stores a channel's current mode settings.
+ *
+ * You should never need to create an instance of this prototype; access the
+ * channel mode information through the |CIRCChannel.mode| property.
+ *
+ * @param parent The |CIRCChannel| to which this mode belongs.
+ */
+function CIRCChanMode (parent)
+{
+ this.parent = parent;
+
+ this.modeA = new Object();
+ this.modeB = new Object();
+ this.modeC = new Object();
+ this.modeD = new Object();
+
+ this.invite = false;
+ this.moderated = false;
+ this.publicMessages = true;
+ this.publicTopic = true;
+ this.secret = false;
+ this.pvt = false;
+ this.key = "";
+ this.limit = -1;
+}
+
+CIRCChanMode.prototype.TYPE = "IRCChanMode";
+
+// Returns the complete mode string, as constructed from its component parts.
+CIRCChanMode.prototype.getModeStr =
+function chan_modestr (f)
+{
+ var str = "";
+ var modeCparams = "";
+
+ /* modeA are 'list' ones, and so should not be shown.
+ * modeB are 'param' ones, like +k key, so we wont show them either.
+ * modeC are 'on-param' ones, like +l limit, which we will show.
+ * modeD are 'boolean' ones, which we will definitely show.
+ */
+
+ // Add modeD:
+ for (var m in this.modeD)
+ {
+ if (this.modeD[m])
+ str += m;
+ }
+
+ // Add modeC, save parameters for adding all the way at the end:
+ for (var m in this.modeC)
+ {
+ if (this.modeC[m])
+ {
+ str += m;
+ modeCparams += " " + this.modeC[m];
+ }
+ }
+
+ // Add parameters:
+ if (str)
+ str = "+" + str + modeCparams;
+
+ return str;
+}
+
+// Sends the given mode string to the server with the channel pre-filled.
+CIRCChanMode.prototype.setMode =
+function chanm_mode (modestr)
+{
+ this.parent.parent.sendData ("MODE " + this.parent.encodedName + " " +
+ modestr + "\n");
+
+ return true;
+}
+
+// Sets (|n| > 0) or clears (|n| <= 0) the user count limit.
+CIRCChanMode.prototype.setLimit =
+function chanm_limit (n)
+{
+ if ((typeof n == "undefined") || (n <= 0))
+ {
+ this.parent.parent.sendData("MODE " + this.parent.encodedName +
+ " -l\n");
+ }
+ else
+ {
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " +l " +
+ Number(n) + "\n");
+ }
+
+ return true;
+}
+
+// Locks the channel with a given key.
+CIRCChanMode.prototype.lock =
+function chanm_lock (k)
+{
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " +k " +
+ k + "\n");
+ return true;
+}
+
+// Unlocks the channel with a given key.
+CIRCChanMode.prototype.unlock =
+function chan_unlock (k)
+{
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " -k " +
+ k + "\n");
+ return true;
+}
+
+// Sets or clears the moderation mode.
+CIRCChanMode.prototype.setModerated =
+function chan_moderate (f)
+{
+ var modifier = (f) ? "+" : "-";
+
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " " +
+ modifier + "m\n");
+ return true;
+}
+
+// Sets or clears the allow public messages mode.
+CIRCChanMode.prototype.setPublicMessages =
+function chan_pmessages (f)
+{
+ var modifier = (f) ? "-" : "+";
+
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " " +
+ modifier + "n\n");
+ return true;
+}
+
+// Sets or clears the public topic mode.
+CIRCChanMode.prototype.setPublicTopic =
+function chan_ptopic (f)
+{
+ var modifier = (f) ? "-" : "+";
+
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " " +
+ modifier + "t\n");
+ return true;
+}
+
+// Sets or clears the invite-only mode.
+CIRCChanMode.prototype.setInvite =
+function chan_invite (f)
+{
+ var modifier = (f) ? "+" : "-";
+
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " " +
+ modifier + "i\n");
+ return true;
+}
+
+// Sets or clears the private channel mode.
+CIRCChanMode.prototype.setPvt =
+function chan_pvt (f)
+{
+ var modifier = (f) ? "+" : "-";
+
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " " +
+ modifier + "p\n");
+ return true;
+}
+
+// Sets or clears the secret channel mode.
+CIRCChanMode.prototype.setSecret =
+function chan_secret (f)
+{
+ var modifier = (f) ? "+" : "-";
+
+ this.parent.parent.sendData("MODE " + this.parent.encodedName + " " +
+ modifier + "s\n");
+ return true;
+}
+
+function CIRCUser(parent, unicodeName, encodedName, name, host)
+{
+ // Both unicodeName and encodedName are optional, but at least one must be
+ // present.
+
+ if (!encodedName && !unicodeName)
+ throw "Hey! Come on, I need either an encoded or a Unicode name.";
+ if (!encodedName)
+ encodedName = fromUnicode(unicodeName, parent);
+
+ let collectionKey = ":" + parent.toLowerCase(encodedName);
+ if (collectionKey in parent.users)
+ {
+ let existingUser = parent.users[collectionKey];
+ if (name)
+ existingUser.name = name;
+ if (host)
+ existingUser.host = host;
+ return existingUser;
+ }
+
+ this.parent = parent;
+ this.encodedName = encodedName;
+ this.canonicalName = collectionKey.substr(1);
+ this.collectionKey = collectionKey;
+ this.unicodeName = unicodeName || toUnicode(encodedName, this.parent);
+ this.viewName = this.unicodeName;
+
+ this.name = name;
+ this.host = host;
+ this.desc = "";
+ this.account = null;
+ this.connectionHost = null;
+ this.isAway = false;
+ this.modestr = this.parent.parent.INITIAL_UMODE;
+
+ this.parent.users[this.collectionKey] = this;
+ if ("onInit" in this)
+ this.onInit();
+
+ return this;
+}
+
+CIRCUser.prototype.TYPE = "IRCUser";
+
+// Returns the IRC URL representation of this user.
+CIRCUser.prototype.getURL =
+function usr_geturl()
+{
+ return this.parent.parent.getURL(this.encodedName, ["isnick"]);
+}
+
+CIRCUser.prototype.rehome =
+function usr_rehome(newParent)
+{
+ delete this.parent.users[this.collectionKey];
+ this.parent = newParent;
+ this.parent.users[this.collectionKey] = this;
+}
+
+CIRCUser.prototype.changeNick =
+function usr_changenick(unicodeName)
+{
+ this.unicodeName = unicodeName;
+ this.viewName = this.unicodeName;
+ this.encodedName = fromUnicode(this.unicodeName, this.parent);
+ this.canonicalName = this.parent.toLowerCase(this.encodedName);
+ this.collectionKey = ":" + this.canonicalName;
+}
+
+CIRCUser.prototype.getHostMask =
+function usr_hostmask (pfx)
+{
+ pfx = (typeof pfx != "undefined") ? pfx : "*!" + this.name + "@*.";
+ var idx = this.host.indexOf(".");
+ if (idx == -1)
+ return pfx + this.host;
+
+ return (pfx + this.host.substr(idx + 1, this.host.length));
+}
+
+CIRCUser.prototype.getBanMask =
+function usr_banmask()
+{
+ if (!this.host)
+ return this.unicodeName + "!*@*";
+
+ return "*!*@" + this.host;
+}
+
+CIRCUser.prototype.say =
+function usr_say (msg)
+{
+ this.parent.sayTo(this.encodedName, fromUnicode(msg, this));
+}
+
+CIRCUser.prototype.notice =
+function usr_notice (msg)
+{
+ this.parent.noticeTo(this.encodedName, fromUnicode(msg, this));
+}
+
+CIRCUser.prototype.act =
+function usr_act (msg)
+{
+ this.parent.actTo(this.encodedName, fromUnicode(msg, this));
+}
+
+CIRCUser.prototype.ctcp =
+function usr_ctcp (code, msg, type)
+{
+ msg = msg || "";
+ type = type || "PRIVMSG";
+
+ this.parent.ctcpTo(this.encodedName, fromUnicode(code, this),
+ fromUnicode(msg, this), type);
+}
+
+CIRCUser.prototype.whois =
+function usr_whois ()
+{
+ this.parent.whois(this.unicodeName);
+}
+
+/*
+ * channel user
+ */
+function CIRCChanUser(parent, unicodeName, encodedName, modes, userInChannel, name, host)
+{
+ // Both unicodeName and encodedName are optional, but at least one must be
+ // present.
+
+ if (!encodedName && !unicodeName)
+ throw "Hey! Come on, I need either an encoded or a Unicode name.";
+ else if (encodedName && !unicodeName)
+ unicodeName = toUnicode(encodedName, parent);
+ else if (!encodedName && unicodeName)
+ encodedName = fromUnicode(unicodeName, parent);
+
+ // We should have both unicode and encoded names by now.
+ let collectionKey = ":" + parent.parent.toLowerCase(encodedName);
+
+ if (collectionKey in parent.users)
+ {
+ let existingUser = parent.users[collectionKey];
+ if (modes)
+ {
+ // If we start with a single character mode, assume we're replacing
+ // the list. (i.e. the list is either all +/- modes, or all normal)
+ if ((modes.length >= 1) && (modes[0].search(/^[-+]/) == -1))
+ {
+ // Modes, but no +/- prefixes, so *replace* mode list.
+ existingUser.modes = modes;
+ }
+ else
+ {
+ // We have a +/- mode list, so carefully update the mode list.
+ for (var m in modes)
+ {
+ // This will remove '-' modes, and all other modes will be
+ // added.
+ var mode = modes[m][1];
+ if (modes[m][0] == "-")
+ {
+ if (arrayContains(existingUser.modes, mode))
+ {
+ var i = arrayIndexOf(existingUser.modes, mode);
+ arrayRemoveAt(existingUser.modes, i);
+ }
+ }
+ else
+ {
+ if (!arrayContains(existingUser.modes, mode))
+ existingUser.modes.push(mode);
+ }
+ }
+ }
+ }
+ existingUser.isFounder = (arrayContains(existingUser.modes, "q")) ?
+ true : false;
+ existingUser.isAdmin = (arrayContains(existingUser.modes, "a")) ?
+ true : false;
+ existingUser.isOp = (arrayContains(existingUser.modes, "o")) ?
+ true : false;
+ existingUser.isHalfOp = (arrayContains(existingUser.modes, "h")) ?
+ true : false;
+ existingUser.isVoice = (arrayContains(existingUser.modes, "v")) ?
+ true : false;
+ existingUser.updateSortName();
+ return existingUser;
+ }
+
+ var protoUser = new CIRCUser(parent.parent, unicodeName, encodedName, name, host);
+
+ this.__proto__ = protoUser;
+ this.getURL = cusr_geturl;
+ this.setOp = cusr_setop;
+ this.setHalfOp = cusr_sethalfop;
+ this.setVoice = cusr_setvoice;
+ this.setBan = cusr_setban;
+ this.kick = cusr_kick;
+ this.kickBan = cusr_kban;
+ this.say = cusr_say;
+ this.notice = cusr_notice;
+ this.act = cusr_act;
+ this.whois = cusr_whois;
+ this.updateSortName = cusr_updatesortname;
+ this.parent = parent;
+ this.TYPE = "IRCChanUser";
+
+ this.modes = new Array();
+ if (typeof modes != "undefined")
+ this.modes = modes;
+ this.isFounder = (arrayContains(this.modes, "q")) ? true : false;
+ this.isAdmin = (arrayContains(this.modes, "a")) ? true : false;
+ this.isOp = (arrayContains(this.modes, "o")) ? true : false;
+ this.isHalfOp = (arrayContains(this.modes, "h")) ? true : false;
+ this.isVoice = (arrayContains(this.modes, "v")) ? true : false;
+ this.updateSortName();
+
+ if (userInChannel)
+ parent.users[this.collectionKey] = this;
+
+ return this;
+}
+
+function cusr_updatesortname()
+{
+ // Check for the highest mode the user has (for sorting the userlist)
+ const userModes = this.parent.parent.userModes;
+ var modeLevel = 0;
+ var mode;
+ for (var i = 0; i < this.modes.length; i++)
+ {
+ for (var j = 0; j < userModes.length; j++)
+ {
+ if (userModes[j].mode == this.modes[i])
+ {
+ if (userModes.length - j > modeLevel)
+ {
+ modeLevel = userModes.length - j;
+ mode = userModes[j];
+ }
+ break;
+ }
+ }
+ }
+ // Counts numerically down from 9.
+ this.sortName = (9 - modeLevel) + "-" + this.unicodeName;
+}
+
+function cusr_geturl()
+{
+ // Don't ask.
+ return this.parent.parent.parent.getURL(this.encodedName, ["isnick"]);
+}
+
+function cusr_setop(f)
+{
+ var server = this.parent.parent;
+ var me = server.me;
+
+ var modifier = (f) ? " +o " : " -o ";
+ server.sendData("MODE " + this.parent.encodedName + modifier + this.encodedName + "\n");
+
+ return true;
+}
+
+function cusr_sethalfop (f)
+{
+ var server = this.parent.parent;
+ var me = server.me;
+
+ var modifier = (f) ? " +h " : " -h ";
+ server.sendData("MODE " + this.parent.encodedName + modifier + this.encodedName + "\n");
+
+ return true;
+}
+
+function cusr_setvoice (f)
+{
+ var server = this.parent.parent;
+ var me = server.me;
+
+ var modifier = (f) ? " +v " : " -v ";
+ server.sendData("MODE " + this.parent.encodedName + modifier + this.encodedName + "\n");
+
+ return true;
+}
+
+function cusr_kick (reason)
+{
+ var server = this.parent.parent;
+ var me = server.me;
+
+ reason = typeof reason == "string" ? reason : "";
+
+ server.sendData("KICK " + this.parent.encodedName + " " + this.encodedName + " :" +
+ fromUnicode(reason, this) + "\n");
+
+ return true;
+}
+
+function cusr_setban (f)
+{
+ var server = this.parent.parent;
+ var me = server.me;
+
+ if (!this.host)
+ return false;
+
+ var modifier = (f) ? " +b " : " -b ";
+ modifier += fromUnicode(this.getBanMask(), server) + " ";
+
+ server.sendData("MODE " + this.parent.encodedName + modifier + "\n");
+
+ return true;
+}
+
+function cusr_kban (reason)
+{
+ var server = this.parent.parent;
+ var me = server.me;
+
+ if (!this.host)
+ return false;
+
+ reason = (typeof reason != "undefined") ? reason : this.encodedName;
+ var modifier = " -o+b " + this.encodedName + " " +
+ fromUnicode(this.getBanMask(), server) + " ";
+
+ server.sendData("MODE " + this.parent.encodedName + modifier + "\n" +
+ "KICK " + this.parent.encodedName + " " +
+ this.encodedName + " :" + reason + "\n");
+
+ return true;
+}
+
+function cusr_say (msg)
+{
+ this.__proto__.say (msg);
+}
+
+function cusr_notice (msg)
+{
+ this.__proto__.notice (msg);
+}
+
+function cusr_act (msg)
+{
+ this.__proto__.act (msg);
+}
+
+function cusr_whois ()
+{
+ this.__proto__.whois ();
+}
+
+
+// IRC URL parsing and generating
+
+function parseIRCURL(url)
+{
+ var specifiedHost = "";
+
+ var rv = new Object();
+ rv.spec = url;
+ rv.scheme = url.split(":")[0];
+ rv.host = null;
+ rv.target = "";
+ rv.port = (rv.scheme == "ircs" ? 6697 : 6667);
+ rv.msg = "";
+ rv.pass = null;
+ rv.key = null;
+ rv.charset = null;
+ rv.needpass = false;
+ rv.needkey = false;
+ rv.isnick = false;
+ rv.isserver = false;
+
+ if (url.search(/^(ircs?:\/?\/?)$/i) != -1)
+ return rv;
+
+ /* split url into <host>/<everything-else> pieces */
+ var ary = url.match(/^ircs?:\/\/([^\/\s]+)?(\/[^\s]*)?$/i);
+ if (!ary || !ary[1])
+ {
+ dd("parseIRCURL: initial split failed");
+ return null;
+ }
+ var host = ary[1];
+ var rest = arrayHasElementAt(ary, 2) ? ary[2] : "";
+
+ /* split <host> into server (or network) / port */
+ ary = host.match(/^([^\:]+|\[[^\]]+\])(\:\d+)?$/i);
+ if (!ary)
+ {
+ dd("parseIRCURL: host/port split failed");
+ return null;
+ }
+
+ // 1 = hostname or IPv4 address, 2 = port.
+ specifiedHost = rv.host = ary[1].toLowerCase();
+ rv.isserver = arrayHasElementAt(ary, 2) || /\.|:/.test(specifiedHost);
+ if (arrayHasElementAt(ary, 2))
+ rv.port = parseInt(ary[2].substr(1));
+
+ if (rest)
+ {
+ ary = rest.match(/^\/([^\?\s\/,]*)?\/?(,[^\?]*)?(\?.*)?$/);
+ if (!ary)
+ {
+ dd("parseIRCURL: rest split failed ``" + rest + "''");
+ return null;
+ }
+
+ rv.target = arrayHasElementAt(ary, 1) ? ecmaUnescape(ary[1]) : "";
+
+ if (rv.target.search(/[\x07,\s]/) != -1)
+ {
+ dd("parseIRCURL: invalid characters in channel name");
+ return null;
+ }
+
+ var params = arrayHasElementAt(ary, 2) ? ary[2].toLowerCase() : "";
+ var query = arrayHasElementAt(ary, 3) ? ary[3] : "";
+
+ if (params)
+ {
+ params = params.split(",");
+ while (params.length)
+ {
+ var param = params.pop();
+ // split doesn't take out empty bits:
+ if (param == "")
+ continue;
+ switch (param)
+ {
+ case "isnick":
+ rv.isnick = true;
+ if (!rv.target)
+ {
+ dd("parseIRCURL: isnick w/o target");
+ /* isnick w/o a target is bogus */
+ return null;
+ }
+ break;
+
+ case "isserver":
+ rv.isserver = true;
+ if (!specifiedHost)
+ {
+ dd("parseIRCURL: isserver w/o host");
+ /* isserver w/o a host is bogus */
+ return null;
+ }
+ break;
+
+ case "needpass":
+ case "needkey":
+ rv[param] = true;
+ break;
+
+ default:
+ /* If we didn't understand it, ignore but warn: */
+ dd("parseIRCURL: Unrecognized param '" + param +
+ "' in URL!");
+ }
+ }
+ }
+
+ if (query)
+ {
+ ary = query.substr(1).split("&");
+ while (ary.length)
+ {
+ var arg = ary.pop().split("=");
+ /*
+ * we don't want to accept *any* query, or folks could
+ * say things like "target=foo", and overwrite what we've
+ * already parsed, so we only use query args we know about.
+ */
+ switch (arg[0].toLowerCase())
+ {
+ case "msg":
+ rv.msg = ecmaUnescape(arg[1]).replace("\n", "\\n");
+ break;
+
+ case "pass":
+ rv.needpass = true;
+ rv.pass = ecmaUnescape(arg[1]).replace("\n", "\\n");
+ break;
+
+ case "key":
+ rv.needkey = true;
+ rv.key = ecmaUnescape(arg[1]).replace("\n", "\\n");
+ break;
+
+ case "charset":
+ rv.charset = ecmaUnescape(arg[1]).replace("\n", "\\n");
+ break;
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+function constructIRCURL(obj)
+{
+ function parseQuery(obj)
+ {
+ var rv = new Array();
+ if ("msg" in obj)
+ rv.push("msg=" + ecmaEscape(obj.msg.replace("\\n", "\n")));
+ if ("pass" in obj)
+ rv.push("pass=" + ecmaEscape(obj.pass.replace("\\n", "\n")));
+ if ("key" in obj)
+ rv.push("key=" + ecmaEscape(obj.key.replace("\\n", "\n")));
+ if ("charset" in obj)
+ rv.push("charset=" + ecmaEscape(obj.charset.replace("\\n", "\n")));
+
+ return rv.length ? "?" + rv.join("&") : "";
+ };
+ function parseFlags(obj)
+ {
+ var rv = new Array();
+ var haveTarget = ("target" in obj) && obj.target;
+ if (("needpass" in obj) && obj.needpass)
+ rv.push(",needpass");
+ if (("needkey" in obj) && obj.needkey && haveTarget)
+ rv.push(",needkey");
+ if (("isnick" in obj) && obj.isnick && haveTarget)
+ rv.push(",isnick");
+
+ return rv.join("");
+ };
+
+ var flags = "";
+ var scheme = ("scheme" in obj) ? obj.scheme : "irc";
+ if (!("host" in obj) || !obj.host)
+ return scheme + "://";
+
+ var url = scheme + "://" + obj.host;
+
+ // Add port if non-standard:
+ if (("port" in obj) && (((scheme == "ircs") && (obj.port != 6697)) ||
+ ((scheme == "irc") && (obj.port != 6667))))
+ {
+ url += ":" + obj.port;
+ }
+ // Need to add ",isserver" if there's no port and no dots in the hostname:
+ else if (("isserver" in obj) && obj.isserver &&
+ (obj.host.indexOf(".") == -1))
+ {
+ flags += ",isserver";
+ }
+ url += "/";
+
+ if (("target" in obj) && obj.target)
+ {
+ if (obj.target.search(/[\x07,\s]/) != -1)
+ {
+ dd("parseIRCObject: invalid characters in channel/nick name");
+ return null;
+ }
+ url += ecmaEscape(obj.target).replace(/\//g, "%2f");
+ }
+
+ return url + flags + parseFlags(obj) + parseQuery(obj);
+}
+
+/* Canonicalizing an IRC URL removes all items which aren't necessary to
+ * identify the target. For example, an IRC URL with ?pass=password and one
+ * without (but otherwise identical) are refering to the same target, so
+ * ?pass= is removed.
+ */
+function makeCanonicalIRCURL(url)
+{
+ var canonicalProps = { scheme: true, host: true, port: true,
+ target: true, isserver: true, isnick: true };
+
+ var urlObject = parseIRCURL(url);
+ if (!urlObject)
+ return ""; // Input wasn't a valid IRC URL.
+ for (var prop in urlObject)
+ {
+ if (!(prop in canonicalProps))
+ delete urlObject[prop];
+ }
+ return constructIRCURL(urlObject);
+}
diff --git a/comm/suite/chatzilla/js/lib/json-serializer.js b/comm/suite/chatzilla/js/lib/json-serializer.js
new file mode 100644
index 0000000000..93333f2bb1
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/json-serializer.js
@@ -0,0 +1,103 @@
+/* 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/. */
+
+/* This is a simple set of functions for serializing and parsing JS objects
+ * to and from files.
+ */
+
+function JSONSerializer(file) {
+ if (typeof file == "string")
+ this._file = new nsLocalFile(file);
+ else
+ this._file = file;
+ this._open = false;
+}
+
+JSONSerializer.prototype = {
+ /*
+ * Opens the serializer on the file specified when created, in either the read
+ * ("<") or write (">") directions. When the file is open, only the
+ * appropriate direction of serialization/deserialization may be performed.
+ *
+ * Note: serialize and deserialize automatically open the file if it is not
+ * open.
+ *
+ * @param dir The string representing the direction of serialization.
+ * @returns Value indicating whether the file was opened successfully.
+ */
+ open: function(dir) {
+ if (!ASSERT((dir == ">") || (dir == "<"), "Bad serialization direction!")) {
+ return false;
+ }
+ if (this._open) {
+ return false;
+ }
+
+ this._fileStream = new LocalFile(this._file, dir);
+ if ((typeof this._fileStream == "object") && this._fileStream) {
+ this._open = true;
+ }
+
+ return this._open;
+ },
+
+ /*
+ * Closes the file stream and ends reading or writing.
+ *
+ * @returns Value indicating whether the file was closed successfully.
+ */
+ close: function() {
+ if (this._open) {
+ this._fileStream.close();
+ delete this._fileStream;
+ this._open = false;
+ }
+ return true;
+ },
+
+ /*
+ * Serializes a single object into the file stream. All properties of the
+ * object are stored in the stream, including properties that contain other
+ * objects.
+ *
+ * @param obj JS object to serialize to the file.
+ */
+ serialize: function(obj) {
+ if (!this._open) {
+ this.open(">");
+ }
+ if (!ASSERT(this._open, "Unable to open the file for writing!")) {
+ return;
+ }
+
+ this._fileStream.write(JSON.stringify(obj, null, 2));
+ },
+
+ /*
+ * Reads in enough of the file to deserialize (realize) a single object. The
+ * object deserialized is returned; all sub-properties of the object are
+ * deserialized with it.
+ *
+ * @returns JS object parsed from the file.
+ */
+ deserialize: function() {
+ if (!this._open) {
+ this.open("<");
+ }
+ if (!ASSERT(this._open, "Unable to open the file for reading!"))
+ return false;
+
+ let rv = null;
+ try {
+ rv = JSON.parse(this._fileStream.read());
+ }
+ catch(ex) {
+ dd("Syntax error while deserializing file!");
+ dd(ex.message);
+ dd(ex.stack);
+ }
+
+ return rv;
+ },
+};
diff --git a/comm/suite/chatzilla/js/lib/menu-manager.js b/comm/suite/chatzilla/js/lib/menu-manager.js
new file mode 100644
index 0000000000..6fd0686833
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/menu-manager.js
@@ -0,0 +1,848 @@
+/* -*- Mode: C++; 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/. */
+
+//
+function MenuManager(commandManager, menuSpecs, contextFunction, commandStr)
+{
+ var menuManager = this;
+
+ this.commandManager = commandManager;
+ this.menuSpecs = menuSpecs;
+ this.contextFunction = contextFunction;
+ this.commandStr = commandStr;
+ this.repeatId = 0;
+ this.cxStore = new Object();
+
+ this.onPopupShowing =
+ function mmgr_onshow(event) { return menuManager.showPopup(event); };
+ this.onPopupHiding =
+ function mmgr_onhide(event) { return menuManager.hidePopup(event); };
+ this.onMenuCommand =
+ function mmgr_oncmd(event) { return menuManager.menuCommand(event); };
+
+ /* The code using us may override these with functions which will be called
+ * after all our internal processing is done. Both are called with the
+ * arguments 'event' (DOM), 'cx' (JS), 'popup' (DOM).
+ */
+ this.onCallbackPopupShowing = null;
+ this.onCallbackPopupHiding = null;
+}
+
+MenuManager.prototype.appendMenuItems =
+function mmgr_append(menuId, items)
+{
+ for (var i = 0; i < items.length; ++i)
+ this.menuSpecs[menuId].items.push(items[i]);
+}
+
+MenuManager.prototype.createContextMenus =
+function mmgr_initcxs (document)
+{
+ for (var id in this.menuSpecs)
+ {
+ if (id.indexOf("context:") == 0)
+ this.createContextMenu(document, id);
+ }
+}
+
+MenuManager.prototype.createContextMenu =
+function mmgr_initcx (document, id)
+{
+ if (!document.getElementById(id))
+ {
+ if (!ASSERT(id in this.menuSpecs, "unknown context menu " + id))
+ return;
+
+ var dp = document.getElementById("dynamic-popups");
+ var popup = this.appendPopupMenu (dp, null, id, id);
+ var items = this.menuSpecs[id].items;
+ this.createMenuItems (popup, null, items);
+
+ if (!("uiElements" in this.menuSpecs[id]))
+ this.menuSpecs[id].uiElements = [popup];
+ else if (!arrayContains(this.menuSpecs[id].uiElements, popup))
+ this.menuSpecs[id].uiElements.push(popup);
+ }
+}
+
+
+MenuManager.prototype.createMenus =
+function mmgr_createtb(document, menuid)
+{
+ var menu = document.getElementById(menuid);
+ for (var id in this.menuSpecs)
+ {
+ var domID;
+ if ("domID" in this.menuSpecs[id])
+ domID = this.menuSpecs[id].domID;
+ else
+ domID = id;
+
+ if (id.indexOf(menuid + ":") == 0)
+ this.createMenu(menu, null, id, domID);
+ }
+}
+
+MenuManager.prototype.createMainToolbar =
+function mmgr_createtb(document, id)
+{
+ var toolbar = document.getElementById(id);
+ var spec = this.menuSpecs[id];
+ for (var i in spec.items)
+ {
+ this.appendToolbarItem (toolbar, null, spec.items[i]);
+ }
+
+ toolbar.className = "toolbar-primary chromeclass-toolbar";
+}
+
+MenuManager.prototype.updateMenus =
+function mmgr_updatemenus(document, menus)
+{
+ // Cope with one string (update just the one menu)...
+ if (isinstance(menus, String))
+ {
+ menus = [menus];
+ }
+ // Or nothing/nonsense (update everything).
+ else if ((typeof menus != "object") || !isinstance(menus, Array))
+ {
+ menus = [];
+ for (var k in this.menuSpecs)
+ {
+ if ((/^(mainmenu|context)/).test(k))
+ menus.push(k);
+ }
+ }
+
+ var menuBar = document.getElementById("mainmenu");
+
+ // Loop through this array and update everything we need to.
+ for (var i = 0; i < menus.length; i++)
+ {
+ var id = menus[i];
+ if (!(id in this.menuSpecs))
+ continue;
+ var menu = this.menuSpecs[id];
+ var domID;
+ if ("domID" in this.menuSpecs[id])
+ domID = this.menuSpecs[id].domID;
+ else
+ domID = id;
+
+ // Context menus need to be deleted in order to be regenerated...
+ if ((/^context/).test(id))
+ {
+ var cxMenuNode;
+ if ((cxMenuNode = document.getElementById(id)))
+ cxMenuNode.parentNode.removeChild(cxMenuNode);
+ this.createContextMenu(document, id);
+ }
+ else if ((/^mainmenu/).test(id) &&
+ !("uiElements" in this.menuSpecs[id]))
+ {
+ this.createMenu(menuBar, null, id, domID);
+ continue;
+ }
+ else if ((/^(mainmenu|popup)/).test(id) &&
+ ("uiElements" in this.menuSpecs[id]))
+ {
+ for (var j = 0; j < menu.uiElements.length; j++)
+ {
+ var node = menu.uiElements[j];
+ domID = node.parentNode.id;
+ // Clear the menu node.
+ while (node.lastChild)
+ node.removeChild(node.lastChild);
+
+ this.createMenu(node.parentNode.parentNode,
+ node.parentNode.nextSibling,
+ id, domID);
+ }
+ }
+
+
+ }
+}
+
+
+/**
+ * Internal use only.
+ *
+ * Registers event handlers on a given menu.
+ */
+MenuManager.prototype.hookPopup =
+function mmgr_hookpop (node)
+{
+ node.addEventListener ("popupshowing", this.onPopupShowing, false);
+ node.addEventListener ("popuphiding", this.onPopupHiding, false);
+}
+
+/**
+ * Internal use only.
+ *
+ * |showPopup| is called from the "onpopupshowing" event of menus managed
+ * by the CommandManager. If a command is disabled, represents a command
+ * that cannot be "satisfied" by the current command context |cx|, or has an
+ * "enabledif" attribute that eval()s to false, then the menuitem is disabled.
+ * In addition "checkedif" and "visibleif" attributes are eval()d and
+ * acted upon accordingly.
+ */
+MenuManager.prototype.showPopup =
+function mmgr_showpop (event)
+{
+ /* returns true if the command context has the properties required to
+ * execute the command associated with |menuitem|.
+ */
+ function satisfied()
+ {
+ if (menuitem.hasAttribute("isSeparator") ||
+ !menuitem.hasAttribute("commandname"))
+ {
+ return true;
+ }
+
+ if (menuitem.hasAttribute("repeatfor"))
+ return false;
+
+ if (!("menuManager" in cx))
+ {
+ dd ("no menuManager in cx");
+ return false;
+ }
+
+ var name = menuitem.getAttribute("commandname");
+ var commandManager = cx.menuManager.commandManager;
+ var commands = commandManager.commands;
+
+ if (!ASSERT (name in commands,
+ "menu contains unknown command '" + name + "'"))
+ {
+ return false;
+ }
+
+ var rv = commandManager.isCommandSatisfied(cx, commands[name]);
+ delete cx.parseError;
+ return rv;
+ };
+
+ /* Convenience function for "enabledif", etc, attributes. */
+ function has (prop)
+ {
+ return (prop in cx);
+ };
+
+ /* evals the attribute named |attr| on the node |node|. */
+ function evalIfAttribute (node, attr)
+ {
+ var ex;
+ var expr = node.getAttribute(attr);
+ if (!expr)
+ return true;
+
+ expr = expr.replace (/\Wand\W/gi, " && ");
+ expr = expr.replace (/\Wor\W/gi, " || ");
+
+ try
+ {
+ return eval("(" + expr + ")");
+ }
+ catch (ex)
+ {
+ dd ("caught exception evaling '" + node.getAttribute("id") + "'.'" +
+ attr + "': '" + expr + "'\n" + ex);
+ }
+ return true;
+ };
+
+ /* evals the attribute named |attr| on the node |node|. */
+ function evalAttribute(node, attr)
+ {
+ var ex;
+ var expr = node.getAttribute(attr);
+ if (!expr)
+ return null;
+
+ try
+ {
+ return eval(expr);
+ }
+ catch (ex)
+ {
+ dd ("caught exception evaling '" + node.getAttribute("id") + "'.'" +
+ attr + "': '" + expr + "'\n" + ex);
+ }
+ return null;
+ };
+
+ var cx;
+ var popup = event.originalTarget;
+ var menuName = popup.getAttribute("menuName");
+
+ /* If the host provided a |contextFunction|, use it now. Remember the
+ * return result as this.cx for use if something from this menu is actually
+ * dispatched. */
+ if (typeof this.contextFunction == "function")
+ {
+ cx = this.cx = this.contextFunction(menuName, event);
+ }
+ else
+ {
+ cx = this.cx = { menuManager: this, originalEvent: event };
+ }
+
+ // Keep the context around by menu name. Removed in hidePopup.
+ this.cxStore[menuName] = cx;
+
+ var menuitem = popup.firstChild;
+ do
+ {
+ if (!menuitem.hasAttribute("repeatfor"))
+ continue;
+
+ // Remove auto-generated items (located prior to real item).
+ while (menuitem.previousSibling &&
+ menuitem.previousSibling.hasAttribute("repeatgenerated"))
+ {
+ menuitem.parentNode.removeChild(menuitem.previousSibling);
+ }
+
+ if (!("repeatList" in cx))
+ cx.repeatList = new Object();
+
+ /* Get the array of new items to add by evaluating "repeatfor" with
+ * "cx" in scope. Usually will return an already-calculated Array
+ * either from "cx" or somewhere in the object model.
+ */
+ var ary = evalAttribute(menuitem, "repeatfor");
+
+ if ((typeof ary != "object") || !isinstance(ary, Array))
+ ary = [];
+
+ /* The item itself should only be shown if there's no items in the
+ * array - this base item is always disabled.
+ */
+ if (ary.length > 0)
+ menuitem.setAttribute("hidden", "true");
+ else
+ menuitem.removeAttribute("hidden");
+
+ // Save the array in the context object.
+ cx.repeatList[menuitem.getAttribute("repeatid")] = ary;
+
+ /* Get the maximum number of items we're allowed to show from |ary| by
+ * evaluating "repeatlimit" with "cx" in scope. This could be a fixed
+ * limit or dynamically calculated (e.g. from prefs).
+ */
+ var limit = evalAttribute(menuitem, "repeatlimit");
+ // Make sure we've got a number at all...
+ if (typeof limit != "number")
+ limit = ary.length;
+ // ...and make sure it's no higher than |ary.length|.
+ limit = Math.min(ary.length, limit);
+
+ var cmd = menuitem.getAttribute("commandname");
+ var props = { repeatgenerated: true, repeatindex: -1,
+ repeatid: menuitem.getAttribute("repeatid"),
+ repeatmap: menuitem.getAttribute("repeatmap") };
+
+ /* Clone non-repeat attributes. All attributes except those starting
+ * with 'repeat', and those matching 'hidden' or 'disabled' are saved
+ * to |props|, which is then supplied to |appendMenuItem| later.
+ */
+ for (var i = 0; i < menuitem.attributes.length; i++)
+ {
+ var name = menuitem.attributes[i].nodeName;
+ if (!name.match(/^(repeat|(hidden|disabled)$)/))
+ props[name] = menuitem.getAttribute(name);
+ }
+
+ var lastGroup = "";
+ for (i = 0; i < limit; i++)
+ {
+ /* Check for groupings. For each item we add, if "repeatgroup" gives
+ * a different value, we insert a separator.
+ */
+ if (menuitem.getAttribute("repeatgroup"))
+ {
+ cx.index = i;
+ ary = cx.repeatList[menuitem.getAttribute("repeatid")];
+ var item = ary[i];
+ /* Apply any updates to "cx" for this item by evaluating
+ * "repeatmap" with "cx" and "item" in scope. This may just
+ * copy some attributes from "item" to "cx" or it may do more.
+ */
+ evalAttribute(menuitem, "repeatmap");
+ /* Get the item's group by evaluating "repeatgroup" with "cx"
+ * and "item" in scope. Usually will return an appropriate
+ * property from "item".
+ */
+ var group = evalAttribute(menuitem, "repeatgroup");
+
+ if ((i > 0) && (lastGroup != group))
+ this.appendMenuSeparator(popup, menuitem, props);
+
+ lastGroup = group;
+ }
+
+ props.repeatindex = i;
+ this.appendMenuItem(popup, menuitem, cmd, props);
+ }
+ } while ((menuitem = menuitem.nextSibling));
+
+ menuitem = popup.firstChild;
+ do
+ {
+ if (menuitem.hasAttribute("repeatgenerated") &&
+ menuitem.hasAttribute("repeatmap"))
+ {
+ cx.index = menuitem.getAttribute("repeatindex");
+ ary = cx.repeatList[menuitem.getAttribute("repeatid")];
+ var item = ary[cx.index];
+ /* Apply any updates to "cx" for this item by evaluating
+ * "repeatmap" with "cx" and "item" in scope. This may just
+ * copy some attributes from "item" to "cx" or it may do more.
+ */
+ evalAttribute(menuitem, "repeatmap");
+ }
+
+ /* should it be visible? */
+ if (menuitem.hasAttribute("visibleif"))
+ {
+ if (evalIfAttribute(menuitem, "visibleif"))
+ menuitem.removeAttribute ("hidden");
+ else
+ {
+ menuitem.setAttribute ("hidden", "true");
+ continue;
+ }
+ }
+
+ /* it's visible, maybe it has a dynamic label? */
+ if (menuitem.hasAttribute("format"))
+ {
+ var label = replaceVars(menuitem.getAttribute("format"), cx);
+ if (label.indexOf("\$") != -1)
+ label = menuitem.getAttribute("backupLabel");
+ menuitem.setAttribute("label", label);
+ }
+
+ /* ok, it's visible, maybe it should be disabled? */
+ if (satisfied())
+ {
+ if (menuitem.hasAttribute("enabledif"))
+ {
+ if (evalIfAttribute(menuitem, "enabledif"))
+ menuitem.removeAttribute ("disabled");
+ else
+ menuitem.setAttribute ("disabled", "true");
+ }
+ else
+ menuitem.removeAttribute ("disabled");
+ }
+ else
+ {
+ menuitem.setAttribute ("disabled", "true");
+ }
+
+ /* should it have a check? */
+ if (menuitem.hasAttribute("checkedif"))
+ {
+ if (evalIfAttribute(menuitem, "checkedif"))
+ menuitem.setAttribute ("checked", "true");
+ else
+ menuitem.removeAttribute ("checked");
+ }
+ } while ((menuitem = menuitem.nextSibling));
+
+ if (typeof this.onCallbackPopupShowing == "function")
+ this.onCallbackPopupShowing(event, cx, popup);
+
+ return true;
+}
+
+/**
+ * Internal use only.
+ *
+ * |hidePopup| is called from the "onpopuphiding" event of menus
+ * managed by the CommandManager. Clean up this.cxStore, but
+ * not this.cx because that messes up nested menus.
+ */
+MenuManager.prototype.hidePopup =
+function mmgr_hidepop(event)
+{
+ var popup = event.originalTarget;
+ var menuName = popup.getAttribute("menuName");
+
+ if (typeof this.onCallbackPopupHiding == "function")
+ this.onCallbackPopupHiding(event, this.cxStore[menuName], popup);
+
+ delete this.cxStore[menuName];
+
+ return true;
+}
+
+MenuManager.prototype.menuCommand =
+function mmgr_menucmd(event)
+{
+ /* evals the attribute named |attr| on the node |node|. */
+ function evalAttribute(node, attr)
+ {
+ var ex;
+ var expr = node.getAttribute(attr);
+ if (!expr)
+ return null;
+
+ try
+ {
+ return eval(expr);
+ }
+ catch (ex)
+ {
+ dd ("caught exception evaling '" + node.getAttribute("id") + "'.'" +
+ attr + "': '" + expr + "'\n" + ex);
+ }
+ return null;
+ };
+
+ var menuitem = event.originalTarget;
+ var cx = this.cx;
+ /* We need to re-run the repeat-map if the user has selected a special
+ * repeat-generated menu item, so that the context object is correct.
+ */
+ if (menuitem.hasAttribute("repeatgenerated") &&
+ menuitem.hasAttribute("repeatmap"))
+ {
+ cx.index = menuitem.getAttribute("repeatindex");
+ var ary = cx.repeatList[menuitem.getAttribute("repeatid")];
+ var item = ary[cx.index];
+ /* Apply any updates to "cx" for this item by evaluating
+ * "repeatmap" with "cx" and "item" in scope. This may just
+ * copy some attributes from "item" to "cx" or it may do more.
+ */
+ evalAttribute(menuitem, "repeatmap");
+ }
+
+ eval(this.commandStr);
+};
+
+
+/**
+ * Appends a sub-menu to an existing menu.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param domId ID of the sub-menu to add.
+ * @param label Text to use for this sub-menu.
+ * @param accesskey Accesskey to use for the sub-menu.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendSubMenu =
+function mmgr_addsmenu(parentNode, beforeNode, menuName, domId, label,
+ accesskey, attribs)
+{
+ var document = parentNode.ownerDocument;
+
+ /* sometimes the menu is already there, for overlay purposes. */
+ var menu = document.getElementById(domId);
+
+ if (!menu)
+ {
+ menu = document.createElement ("menu");
+ menu.setAttribute ("id", domId);
+ }
+
+ var menupopup = menu.firstChild;
+
+ if (!menupopup)
+ {
+ menupopup = document.createElement ("menupopup");
+ menupopup.setAttribute ("id", domId + "-popup");
+ menu.appendChild(menupopup);
+ menupopup = menu.firstChild;
+ }
+
+ menupopup.setAttribute ("menuName", menuName);
+
+ menu.setAttribute("accesskey", accesskey);
+ label = label.replace("&", "");
+ menu.setAttribute ("label", label);
+ menu.setAttribute ("isSeparator", true);
+
+ // Only attach the menu if it's not there already. This can't be in the
+ // if (!menu) block because the updateMenus code clears toplevel menus,
+ // orphaning the submenus, to (parts of?) which we keep handles in the
+ // uiElements array. See the updateMenus code.
+ if (!menu.parentNode)
+ parentNode.insertBefore(menu, beforeNode);
+
+ if (typeof attribs == "object")
+ {
+ for (var p in attribs)
+ menu.setAttribute (p, attribs[p]);
+ }
+
+ this.hookPopup (menupopup);
+
+ return menupopup;
+}
+
+/**
+ * Appends a popup to an existing popupset.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param id ID of the popup to add.
+ * @param label Text to use for this popup. Popup menus don't normally have
+ * labels, but we set a "label" attribute anyway, in case
+ * the host wants it for some reason. Any "&" characters will
+ * be stripped.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendPopupMenu =
+function mmgr_addpmenu (parentNode, beforeNode, menuName, id, label, attribs)
+{
+ var document = parentNode.ownerDocument;
+ var popup = document.createElement ("menupopup");
+ popup.setAttribute ("id", id);
+ if (label)
+ popup.setAttribute ("label", label.replace("&", ""));
+ if (typeof attribs == "object")
+ {
+ for (var p in attribs)
+ popup.setAttribute (p, attribs[p]);
+ }
+
+ popup.setAttribute ("menuName", menuName);
+
+ parentNode.insertBefore(popup, beforeNode);
+ this.hookPopup (popup);
+
+ return popup;
+}
+
+/**
+ * Appends a menuitem to an existing menu or popup.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param command A reference to the CommandRecord this menu item will represent.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendMenuItem =
+function mmgr_addmenu (parentNode, beforeNode, commandName, attribs)
+{
+ var menuManager = this;
+
+ var document = parentNode.ownerDocument;
+ if (commandName == "-")
+ return this.appendMenuSeparator(parentNode, beforeNode, attribs);
+
+ var parentId = parentNode.getAttribute("id");
+
+ if (!ASSERT(commandName in this.commandManager.commands,
+ "unknown command " + commandName + " targeted for " +
+ parentId))
+ {
+ return null;
+ }
+
+ var command = this.commandManager.commands[commandName];
+ var menuitem = document.createElement ("menuitem");
+ menuitem.setAttribute ("id", parentId + ":" + commandName);
+ menuitem.setAttribute ("commandname", command.name);
+ // Add keys if this isn't a context menu:
+ if (parentId.indexOf("context") != 0)
+ menuitem.setAttribute("key", "key:" + command.name);
+ menuitem.setAttribute("accesskey", command.accesskey);
+ var label = command.label.replace("&", "");
+ menuitem.setAttribute ("label", label);
+ if (command.format)
+ {
+ menuitem.setAttribute("format", command.format);
+ menuitem.setAttribute("backupLabel", label);
+ }
+
+ if ((typeof attribs == "object") && attribs)
+ {
+ for (var p in attribs)
+ menuitem.setAttribute (p, attribs[p]);
+ if ("repeatfor" in attribs)
+ menuitem.setAttribute("repeatid", this.repeatId++);
+ }
+
+ command.uiElements.push(menuitem);
+ parentNode.insertBefore (menuitem, beforeNode);
+ /* It seems, bob only knows why, that this must be done AFTER the node is
+ * added to the document.
+ */
+ menuitem.addEventListener("command", this.onMenuCommand, false);
+
+ return menuitem;
+}
+
+/**
+ * Appends a menuseparator to an existing menu or popup.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendMenuSeparator =
+function mmgr_addsep (parentNode, beforeNode, attribs)
+{
+ var document = parentNode.ownerDocument;
+ var menuitem = document.createElement ("menuseparator");
+ menuitem.setAttribute ("isSeparator", true);
+ if (typeof attribs == "object")
+ {
+ for (var p in attribs)
+ menuitem.setAttribute (p, attribs[p]);
+ }
+ parentNode.insertBefore (menuitem, beforeNode);
+
+ return menuitem;
+}
+
+/**
+ * Appends a toolbaritem to an existing box element.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param command A reference to the CommandRecord this toolbaritem will
+ * represent.
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendToolbarItem =
+function mmgr_addtb (parentNode, beforeNode, commandName, attribs)
+{
+ if (commandName == "-")
+ return this.appendToolbarSeparator(parentNode, beforeNode, attribs);
+
+ var parentId = parentNode.getAttribute("id");
+
+ if (!ASSERT(commandName in this.commandManager.commands,
+ "unknown command " + commandName + " targeted for " +
+ parentId))
+ {
+ return null;
+ }
+
+ var command = this.commandManager.commands[commandName];
+ var document = parentNode.ownerDocument;
+ var tbitem = document.createElement ("toolbarbutton");
+
+ var id = parentNode.getAttribute("id") + ":" + commandName;
+ tbitem.setAttribute ("id", id);
+ tbitem.setAttribute ("class", "toolbarbutton-1");
+ if (command.tip)
+ tbitem.setAttribute ("tooltiptext", command.tip);
+ tbitem.setAttribute ("label", command.label.replace("&", ""));
+ tbitem.setAttribute ("oncommand",
+ "dispatch('" + commandName + "');");
+ if (typeof attribs == "object")
+ {
+ for (var p in attribs)
+ tbitem.setAttribute (p, attribs[p]);
+ }
+
+ command.uiElements.push(tbitem);
+ parentNode.insertBefore (tbitem, beforeNode);
+
+ return tbitem;
+}
+
+/**
+ * Appends a toolbarseparator to an existing box.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param attribs Object containing CSS attributes to set on the element.
+ */
+MenuManager.prototype.appendToolbarSeparator =
+function mmgr_addmenu (parentNode, beforeNode, attribs)
+{
+ var document = parentNode.ownerDocument;
+ var tbitem = document.createElement ("toolbarseparator");
+ tbitem.setAttribute ("isSeparator", true);
+ if (typeof attribs == "object")
+ {
+ for (var p in attribs)
+ tbitem.setAttribute (p, attribs[p]);
+ }
+ parentNode.appendChild (tbitem);
+
+ return tbitem;
+}
+
+/**
+ * Creates menu DOM nodes from a menu specification.
+ * @param parentNode DOM Node to insert into
+ * @param beforeNode DOM Node already contained by parentNode, to insert before
+ * @param menuSpec array of menu items
+ */
+MenuManager.prototype.createMenu =
+function mmgr_newmenu (parentNode, beforeNode, menuName, domId, attribs)
+{
+ if (typeof domId == "undefined")
+ domId = menuName;
+
+ if (!ASSERT(menuName in this.menuSpecs, "unknown menu name " + menuName))
+ return null;
+
+ var menuSpec = this.menuSpecs[menuName];
+ if (!("accesskey" in menuSpec))
+ menuSpec.accesskey = getAccessKey(menuSpec.label);
+
+ var subMenu = this.appendSubMenu(parentNode, beforeNode, menuName, domId,
+ menuSpec.label, menuSpec.accesskey,
+ attribs);
+
+ // Keep track where we're adding popup nodes derived from some menuSpec
+ if (!("uiElements" in this.menuSpecs[menuName]))
+ this.menuSpecs[menuName].uiElements = [subMenu];
+ else if (!arrayContains(this.menuSpecs[menuName].uiElements, subMenu))
+ this.menuSpecs[menuName].uiElements.push(subMenu);
+
+ this.createMenuItems (subMenu, null, menuSpec.items);
+ return subMenu;
+}
+
+MenuManager.prototype.createMenuItems =
+function mmgr_newitems (parentNode, beforeNode, menuItems)
+{
+ function itemAttribs()
+ {
+ return (1 in menuItems[i]) ? menuItems[i][1] : null;
+ };
+
+ var parentId = parentNode.getAttribute("id");
+
+ for (var i in menuItems)
+ {
+ var itemName = menuItems[i][0];
+ if (itemName[0] == ">")
+ {
+ itemName = itemName.substr(1);
+ if (!ASSERT(itemName in this.menuSpecs,
+ "unknown submenu " + itemName + " referenced in " +
+ parentId))
+ {
+ continue;
+ }
+ this.createMenu (parentNode, beforeNode, itemName,
+ parentId + ":" + itemName, itemAttribs());
+ }
+ else if (itemName in this.commandManager.commands)
+ {
+ this.appendMenuItem (parentNode, beforeNode, itemName,
+ itemAttribs());
+ }
+ else if (itemName == "-")
+ {
+ this.appendMenuSeparator (parentNode, beforeNode, itemAttribs());
+ }
+ else
+ {
+ dd ("unknown command " + itemName + " referenced in " + parentId);
+ }
+ }
+}
+
diff --git a/comm/suite/chatzilla/js/lib/message-manager.js b/comm/suite/chatzilla/js/lib/message-manager.js
new file mode 100644
index 0000000000..56020d48f6
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/message-manager.js
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; 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/. */
+
+//
+function MessageManager(entities)
+{
+ const UC_CTRID = "@mozilla.org/intl/scriptableunicodeconverter";
+ const nsIUnicodeConverter =
+ Components.interfaces.nsIScriptableUnicodeConverter;
+
+ this.ucConverter =
+ Components.classes[UC_CTRID].getService(nsIUnicodeConverter);
+ this.defaultBundle = null;
+ this.bundleList = new Array();
+ // Provide a fallback so we don't break getMsg and related constants later.
+ this.entities = entities || {};
+}
+
+// ISO-2022-JP (often used for Japanese on IRC) doesn't contain any support
+// for hankaku kana (half-width katakana), so we support the option to convert
+// it to zenkaku kana (full-width katakana). This does not affect any other
+// encoding at this time.
+MessageManager.prototype.enableHankakuToZenkaku = false;
+
+MessageManager.prototype.loadBrands =
+function mm_loadbrands()
+{
+ var entities = this.entities;
+ var app = getService("@mozilla.org/xre/app-info;1", "nsIXULAppInfo");
+ if (app)
+ {
+ // Use App info if possible
+ entities.brandShortName = app.name;
+ entities.brandFullName = app.name + " " + app.version;
+ entities.brandVendorName = app.vendor;
+ return;
+ }
+
+ var brandBundle;
+ var path = "chrome://branding/locale/brand.properties";
+ try
+ {
+ brandBundle = this.addBundle(path);
+ }
+ catch (exception)
+ {
+ // May be an older mozilla version, try another location.
+ path = "chrome://global/locale/brand.properties";
+ brandBundle = this.addBundle(path);
+ }
+
+ entities.brandShortName = brandBundle.GetStringFromName("brandShortName");
+ entities.brandVendorName = brandBundle.GetStringFromName("vendorShortName");
+ // Not all versions of Suite / Fx have this defined; Cope:
+ try
+ {
+ entities.brandFullName = brandBundle.GetStringFromName("brandFullName");
+ }
+ catch(exception)
+ {
+ entities.brandFullName = entities.brandShortName;
+ }
+
+ // Remove all of this junk, or it will be the default bundle for getMsg...
+ this.bundleList.pop();
+}
+
+MessageManager.prototype.addBundle =
+function mm_addbundle(bundlePath, targetWindow)
+{
+ var bundle = srGetStrBundle(bundlePath);
+ this.bundleList.push(bundle);
+
+ // The bundle will load if the file doesn't exist. This will fail though.
+ // We want to be clean and remove the bundle again.
+ try
+ {
+ this.importBundle(bundle, targetWindow, this.bundleList.length - 1);
+ }
+ catch (exception)
+ {
+ // Clean up and return the exception.
+ this.bundleList.pop();
+ throw exception;
+ }
+ return bundle;
+}
+
+MessageManager.prototype.importBundle =
+function mm_importbundle(bundle, targetWindow, index)
+{
+ var me = this;
+ function replaceEntities(matched, entity)
+ {
+ if (entity in me.entities)
+ return me.entities[entity];
+
+ return matched;
+ };
+ const nsIPropertyElement = Components.interfaces.nsIPropertyElement;
+
+ if (!targetWindow)
+ targetWindow = window;
+
+ if (typeof index == "undefined")
+ index = arrayIndexOf(this.bundleList, bundle);
+
+ var pfx;
+ if (index == 0)
+ pfx = "";
+ else
+ pfx = index + ":";
+
+ var enumer = bundle.getSimpleEnumeration();
+
+ while (enumer.hasMoreElements())
+ {
+ var prop = enumer.getNext().QueryInterface(nsIPropertyElement);
+ var ary = prop.key.match (/^(msg|msn)/);
+ if (ary)
+ {
+ var constValue;
+ var constName = prop.key.toUpperCase().replace (/\./g, "_");
+ if (ary[1] == "msn" || prop.value.search(/%(\d+\$)?s/i) != -1)
+ constValue = pfx + prop.key;
+ else
+ constValue = prop.value.replace (/^\"/, "").replace (/\"$/, "");
+
+ constValue = constValue.replace(/\&(\w+)\;/g, replaceEntities);
+ targetWindow[constName] = constValue;
+ }
+ }
+
+ if (this.bundleList.length == 1)
+ this.defaultBundle = bundle;
+}
+
+MessageManager.prototype.convertHankakuToZenkaku =
+function mm_converthankakutozenkaku(msg)
+{
+ const basicMapping = [
+ /* 0xFF60 */ 0xFF60,0x3002,0x300C,0x300D,0x3001,0x30FB,0x30F2,0x30A1,
+ /* 0xFF68 */ 0x30A3,0x30A5,0x30A7,0x30A9,0x30E3,0x30E5,0x30E7,0x30C3,
+ /* 0xFF70 */ 0x30FC,0x30A2,0x30A4,0x30A6,0x30A8,0x30AA,0x30AB,0x30AD,
+ /* 0xFF78 */ 0x30AF,0x30B1,0x30B3,0x30B5,0x30B7,0x30B9,0x30BB,0x30BD,
+ /* 0xFF80 */ 0x30BF,0x30C1,0x30C4,0x30C6,0x30C8,0x30CA,0x30CB,0x30CC,
+ /* 0xFF88 */ 0x30CD,0x30CE,0x30CF,0x30D2,0x30D5,0x30D8,0x30DB,0x30DE,
+ /* 0xFF90 */ 0x30DF,0x30E0,0x30E1,0x30E2,0x30E4,0x30E6,0x30E8,0x30E9,
+ /* 0xFF98 */ 0x30EA,0x30EB,0x30EC,0x30ED,0x30EF,0x30F3,0x309B,0x309C
+ ];
+
+ const HANKAKU_BASE1 = 0xFF60;
+ const HANKAKU_BASE2 = 0xFF80;
+ const HANKAKU_MASK = 0xFFE0;
+
+ const MOD_NIGORI = 0xFF9E;
+ const NIGORI_MIN1 = 0xFF76;
+ const NIGORI_MAX1 = 0xFF84;
+ const NIGORI_MIN2 = 0xFF8A;
+ const NIGORI_MAX2 = 0xFF8E;
+ const NIGORI_MODIFIER = 1;
+
+ const MOD_MARU = 0xFF9F;
+ const MARU_MIN = 0xFF8A;
+ const MARU_MAX = 0xFF8E;
+ const MARU_MODIFIER = 2;
+
+ var i, src, srcMod, dest;
+ var rv = "";
+
+ for (i = 0; i < msg.length; i++)
+ {
+ // Get both this character and the next one, which could be a modifier.
+ src = msg.charCodeAt(i);
+ if (i < msg.length - 1)
+ srcMod = msg.charCodeAt(i + 1);
+
+ // Is the source characher hankaku?
+ if ((HANKAKU_BASE1 == (src & HANKAKU_MASK)) ||
+ (HANKAKU_BASE2 == (src & HANKAKU_MASK)))
+ {
+ // Do the basic character mapping first.
+ dest = basicMapping[src - HANKAKU_BASE1];
+
+ // If the source character is in the nigori or maru ranges and
+ // the following character is the associated modifier, we apply
+ // the modification and skip over the modifier.
+ if (i < msg.length - 1)
+ {
+ if ((MOD_NIGORI == srcMod) &&
+ (((src >= NIGORI_MIN1) && (src <= NIGORI_MAX1)) ||
+ ((src >= NIGORI_MIN2) && (src <= NIGORI_MAX2))))
+ {
+ dest += NIGORI_MODIFIER;
+ i++;
+ }
+ else if ((MOD_MARU == srcMod) &&
+ (src >= MARU_MIN) && (src <= MARU_MAX))
+ {
+ dest += MARU_MODIFIER;
+ i++;
+ }
+ }
+
+ rv += String.fromCharCode(dest);
+ }
+ else
+ {
+ rv += msg[i];
+ }
+ }
+
+ return rv;
+}
+
+MessageManager.prototype.checkCharset =
+function mm_checkset(charset)
+{
+ try
+ {
+ this.ucConverter.charset = charset;
+ }
+ catch (ex)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+MessageManager.prototype.toUnicode =
+function mm_tounicode(msg, charset)
+{
+ if (!charset)
+ return msg;
+
+ try
+ {
+ this.ucConverter.charset = charset;
+ msg = this.ucConverter.ConvertToUnicode(msg);
+ }
+ catch (ex)
+ {
+ //dd ("caught exception " + ex + " converting " + msg + " to charset " +
+ // charset);
+ }
+
+ return msg;
+}
+
+MessageManager.prototype.fromUnicode =
+function mm_fromunicode(msg, charset)
+{
+ if (!charset)
+ return msg;
+
+ if (this.enableHankakuToZenkaku && (charset.toLowerCase() == "iso-2022-jp"))
+ msg = this.convertHankakuToZenkaku(msg);
+
+ try
+ {
+ // This can actually fail in bizare cases. Cope.
+ if (charset != this.ucConverter.charset)
+ this.ucConverter.charset = charset;
+
+ if ("Finish" in this.ucConverter)
+ {
+ msg = this.ucConverter.ConvertFromUnicode(msg) +
+ this.ucConverter.Finish();
+ }
+ else
+ {
+ msg = this.ucConverter.ConvertFromUnicode(msg + " ");
+ msg = msg.substr(0, msg.length - 1);
+ }
+ }
+ catch (ex)
+ {
+ //dd ("caught exception " + ex + " converting " + msg + " to charset " +
+ // charset);
+ }
+
+ return msg;
+}
+
+MessageManager.prototype.getMsg =
+function mm_getmsg (msgName, params, deflt)
+{
+ try
+ {
+ var bundle;
+ var ary = msgName.match (/(\d+):(.+)/);
+ if (ary)
+ {
+ return (this.getMsgFrom(this.bundleList[ary[1]], ary[2], params,
+ deflt));
+ }
+
+ return this.getMsgFrom(this.bundleList[0], msgName, params, deflt);
+ }
+ catch (ex)
+ {
+ ASSERT (0, "Caught exception getting message: " + msgName + "/" +
+ params);
+ return deflt ? deflt : msgName;
+ }
+}
+
+MessageManager.prototype.getMsgFrom =
+function mm_getfrom (bundle, msgName, params, deflt)
+{
+ var me = this;
+ function replaceEntities(matched, entity)
+ {
+ if (entity in me.entities)
+ return me.entities[entity];
+
+ return matched;
+ };
+
+ try
+ {
+ var rv;
+
+ if (params && isinstance(params, Array))
+ rv = bundle.formatStringFromName (msgName, params, params.length);
+ else if (params || params == 0)
+ rv = bundle.formatStringFromName (msgName, [params], 1);
+ else
+ rv = bundle.GetStringFromName (msgName);
+
+ /* strip leading and trailing quote characters, see comment at the
+ * top of venkman.properties.
+ */
+ rv = rv.replace(/^\"/, "");
+ rv = rv.replace(/\"$/, "");
+ rv = rv.replace(/\&(\w+)\;/g, replaceEntities);
+
+ return rv;
+ }
+ catch (ex)
+ {
+ if (typeof deflt == "undefined")
+ {
+ ASSERT (0, "caught exception getting value for ``" + msgName +
+ "''\n" + ex + "\n");
+ return msgName;
+ }
+ return deflt;
+ }
+
+ return null;
+}
diff --git a/comm/suite/chatzilla/js/lib/pref-manager.js b/comm/suite/chatzilla/js/lib/pref-manager.js
new file mode 100644
index 0000000000..3d0b09ff86
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/pref-manager.js
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; 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/. */
+
+const PREF_RELOAD = true;
+const PREF_WRITETHROUGH = true;
+const PREF_CHARSET = "utf-8"; // string prefs stored in this charset
+
+function PrefRecord(name, defaultValue, label, help, group)
+{
+ this.name = name;
+ this.defaultValue = defaultValue;
+ this.help = help;
+ this.label = label ? label : name;
+ this.group = group ? group : "";
+ // Prepend the group 'general' if there isn't one already.
+ if (this.group.match(/^(\.|$)/))
+ this.group = "general" + this.group;
+ this.realValue = null;
+}
+
+function PrefManager (branchName, defaultBundle)
+{
+ var prefManager = this;
+
+ function pm_observe (prefService, topic, prefName)
+ {
+ prefManager.onPrefChanged(prefName);
+ };
+
+ const PREF_CTRID = "@mozilla.org/preferences-service;1";
+ const nsIPrefService = Components.interfaces.nsIPrefService;
+ const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
+
+ this.prefService =
+ Components.classes[PREF_CTRID].getService(nsIPrefService);
+ this.prefBranch = this.prefService.getBranch(branchName);
+ this.prefSaveTime = 0;
+ this.prefSaveTimer = 0;
+ this.branchName = branchName;
+ this.defaultValues = new Object();
+ this.prefs = new Object();
+ this.prefNames = new Array();
+ this.prefRecords = new Object();
+ this.observer = { observe: pm_observe, branch: branchName };
+ this.observers = new Array();
+
+ this.nsIPrefBranch =
+ this.prefBranch.QueryInterface(nsIPrefBranch);
+ this.nsIPrefBranch.addObserver("", this.observer, false);
+
+ this.defaultBundle = defaultBundle;
+
+ this.valid = true;
+}
+
+// Delay between change and save.
+PrefManager.prototype.PREF_SAVE_DELAY = 5000; // 5 seconds.
+/* The timer is reset for each change. Only reset if it hasn't been delayed by
+ * this much already, or we could put off a save indefinitely.
+ */
+PrefManager.prototype.PREF_MAX_DELAY = 15000; // 15 seconds.
+
+//
+PrefManager.prototype.destroy =
+function pm_destroy()
+{
+ if (this.valid)
+ {
+ this.nsIPrefBranch.removeObserver("", this.observer);
+ this.valid = false;
+ }
+}
+
+PrefManager.prototype.getBranch =
+function pm_getbranch(suffix)
+{
+ return this.prefService.getBranch(this.prefBranch.root + suffix);
+}
+
+PrefManager.prototype.getBranchManager =
+function pm_getbranchmgr(suffix)
+{
+ return new PrefManager(this.prefBranch.root + suffix);
+}
+
+PrefManager.prototype.addObserver =
+function pm_addobserver(observer)
+{
+ if (!("onPrefChanged" in observer))
+ throw "Bad observer!";
+
+ this.observers.push(observer);
+}
+
+PrefManager.prototype.removeObserver =
+function pm_removeobserver(observer)
+{
+ for (var i = 0; i < this.observers.length; i++)
+ {
+ if (this.observers[i] == observer)
+ {
+ arrayRemoveAt(this.observers, i);
+ break;
+ }
+ }
+}
+
+PrefManager.prototype.delayedSave =
+function pm_delayedsave()
+{
+ // this.prefSaveTimer
+ var now = Number(new Date());
+
+ /* If the time == 0, there is no delayed save in progress, and we should
+ * start one. If it isn't 0, check the delayed save was started within the
+ * allowed time - this means that if we keep putting off a save, it will
+ * go through eventually, as we will stop resetting it.
+ */
+ if ((this.prefSaveTime == 0) ||
+ (now - this.prefSaveTime < this.PREF_MAX_DELAY))
+ {
+ if (this.prefSaveTime == 0)
+ this.prefSaveTime = now;
+ if (this.prefSaveTimer != 0)
+ clearTimeout(this.prefSaveTimer);
+ this.prefSaveTimer = setTimeout(function(o) { o.forceSave() },
+ this.PREF_SAVE_DELAY, this);
+ }
+}
+
+PrefManager.prototype.forceSave =
+function pm_forcesave()
+{
+ this.prefSaveTime = 0;
+ this.prefSaveTimer = 0;
+ try {
+ this.prefService.savePrefFile(null);
+ } catch(ex) {
+ dd("Exception saving preferences: " + formatException(ex));
+ }
+}
+
+PrefManager.prototype.onPrefChanged =
+function pm_prefchanged(prefName, realValue, oldValue)
+{
+ var r, oldValue;
+ // We're only interested in prefs we actually know about.
+ if (!(prefName in this.prefRecords) || !(r = this.prefRecords[prefName]))
+ return;
+
+ if (r.realValue != null)
+ oldValue = r.realValue;
+ else if (typeof r.defaultValue == "function")
+ oldValue = r.defaultValue(prefName);
+ else
+ oldValue = r.defaultValue;
+
+ var realValue = this.getPref(prefName, PREF_RELOAD);
+
+ for (var i = 0; i < this.observers.length; i++)
+ this.observers[i].onPrefChanged(prefName, realValue, oldValue);
+}
+
+PrefManager.prototype.listPrefs =
+function pm_listprefs (prefix)
+{
+ var list = new Array();
+ var names = this.prefNames;
+ for (var i = 0; i < names.length; ++i)
+ {
+ if (!prefix || names[i].indexOf(prefix) == 0)
+ list.push (names[i]);
+ }
+
+ return list;
+}
+
+PrefManager.prototype.readPrefs =
+function pm_readprefs ()
+{
+ const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
+
+ var list = this.prefBranch.getChildList("", {});
+ for (var i = 0; i < list.length; ++i)
+ {
+ if (!(list[i] in this))
+ {
+ var type = this.prefBranch.getPrefType (list[i]);
+ var defaultValue;
+
+ switch (type)
+ {
+ case nsIPrefBranch.PREF_INT:
+ defaultValue = 0;
+ break;
+
+ case nsIPrefBranch.PREF_BOOL:
+ defaultValue = false;
+ break;
+
+ default:
+ defaultValue = "";
+ }
+
+ this.addPref(list[i], defaultValue);
+ }
+ }
+}
+
+PrefManager.prototype.isKnownPref =
+function pm_ispref(prefName)
+{
+ return (prefName in this.prefRecords);
+}
+
+PrefManager.prototype.addPrefs =
+function pm_addprefs(prefSpecs)
+{
+ var bundle = "stringBundle" in prefSpecs ? prefSpecs.stringBundle : null;
+ for (var i = 0; i < prefSpecs.length; ++i)
+ {
+ this.addPref(prefSpecs[i][0], prefSpecs[i][1],
+ 3 in prefSpecs[i] ? prefSpecs[i][3] : null, bundle,
+ 2 in prefSpecs[i] ? prefSpecs[i][2] : null);
+ }
+}
+
+PrefManager.prototype.updateArrayPref =
+function pm_arrayupdate(prefName)
+{
+ var record = this.prefRecords[prefName];
+ if (!ASSERT(record, "Unknown pref: " + prefName))
+ return;
+
+ if (record.realValue == null)
+ record.realValue = record.defaultValue;
+
+ if (!ASSERT(isinstance(record.realValue, Array), "Pref is not an array"))
+ return;
+
+ this.prefBranch.setCharPref(prefName, this.arrayToString(record.realValue));
+ this.delayedSave();
+}
+
+PrefManager.prototype.stringToArray =
+function pm_s2a(string)
+{
+ if (string.search(/\S/) == -1)
+ return [];
+
+ var ary = string.split(/\s*;\s*/);
+ for (var i = 0; i < ary.length; ++i)
+ ary[i] = toUnicode(unescape(ary[i]), PREF_CHARSET);
+
+ return ary;
+}
+
+PrefManager.prototype.arrayToString =
+function pm_a2s(ary)
+{
+ var escapedAry = new Array()
+ for (var i = 0; i < ary.length; ++i)
+ escapedAry[i] = escape(fromUnicode(ary[i], PREF_CHARSET));
+
+ return escapedAry.join("; ");
+}
+
+PrefManager.prototype.getPref =
+function pm_getpref(prefName, reload)
+{
+ var prefManager = this;
+
+ function updateArrayPref() { prefManager.updateArrayPref(prefName); };
+
+ var record = this.prefRecords[prefName];
+ if (!ASSERT(record, "Unknown pref: " + prefName))
+ return null;
+
+ var defaultValue;
+
+ if (typeof record.defaultValue == "function")
+ {
+ // deferred pref, call the getter, and don't cache the result.
+ defaultValue = record.defaultValue(prefName);
+ }
+ else
+ {
+ if (!reload && record.realValue != null)
+ return record.realValue;
+
+ defaultValue = record.defaultValue;
+ }
+
+ var realValue = defaultValue;
+
+ try
+ {
+ if (typeof defaultValue == "boolean")
+ {
+ realValue = this.prefBranch.getBoolPref(prefName);
+ }
+ else if (typeof defaultValue == "number")
+ {
+ realValue = this.prefBranch.getIntPref(prefName);
+ }
+ else if (isinstance(defaultValue, Array))
+ {
+ realValue = this.prefBranch.getCharPref(prefName);
+ realValue = this.stringToArray(realValue);
+ realValue.update = updateArrayPref;
+ }
+ else if (typeof defaultValue == "string" ||
+ defaultValue == null)
+ {
+ realValue = toUnicode(this.prefBranch.getCharPref(prefName),
+ PREF_CHARSET);
+ }
+ }
+ catch (ex)
+ {
+ // if the pref doesn't exist, ignore the exception.
+ }
+
+ record.realValue = realValue;
+ return realValue;
+}
+
+PrefManager.prototype.setPref =
+function pm_setpref(prefName, value)
+{
+ var prefManager = this;
+
+ function updateArrayPref() { prefManager.updateArrayPref(prefName); };
+
+ var record = this.prefRecords[prefName];
+ if (!ASSERT(record, "Unknown pref: " + prefName))
+ return null;
+
+ var defaultValue = record.defaultValue;
+
+ if (typeof defaultValue == "function")
+ defaultValue = defaultValue(prefName);
+
+ if ((record.realValue == null && value == defaultValue) ||
+ record.realValue == value)
+ {
+ // no realvalue, and value is the same as default value ... OR ...
+ // no change at all. just bail.
+ return record.realValue;
+ }
+
+ if (value == defaultValue)
+ {
+ this.clearPref(prefName);
+ return value;
+ }
+
+ if (typeof defaultValue == "boolean")
+ {
+ this.prefBranch.setBoolPref(prefName, value);
+ }
+ else if (typeof defaultValue == "number")
+ {
+ this.prefBranch.setIntPref(prefName, value);
+ }
+ else if (isinstance(defaultValue, Array))
+ {
+ var str = this.arrayToString(value);
+ this.prefBranch.setCharPref(prefName, str);
+ value.update = updateArrayPref;
+ }
+ else
+ {
+ this.prefBranch.setCharPref(prefName, fromUnicode(value, PREF_CHARSET));
+ }
+ this.delayedSave();
+
+ // Always update this after changing the preference.
+ record.realValue = value;
+
+ return value;
+}
+
+PrefManager.prototype.clearPref =
+function pm_reset(prefName)
+{
+ try {
+ this.prefBranch.clearUserPref(prefName);
+ } catch(ex) {
+ // Do nothing, the pref didn't exist.
+ }
+ this.delayedSave();
+
+ // Always update this after changing the preference.
+ this.prefRecords[prefName].realValue = null;
+}
+
+PrefManager.prototype.addPref =
+function pm_addpref(prefName, defaultValue, setter, bundle, group)
+{
+ var prefManager = this;
+ if (!bundle)
+ bundle = this.defaultBundle;
+
+ function updateArrayPref() { prefManager.updateArrayPref(prefName); };
+ function prefGetter() { return prefManager.getPref(prefName); };
+ function prefSetter(value) { return prefManager.setPref(prefName, value); };
+
+ if (!ASSERT(!(prefName in this.defaultValues),
+ "Preference already exists: " + prefName))
+ {
+ return;
+ }
+
+ if (!setter)
+ setter = prefSetter;
+
+ if (isinstance(defaultValue, Array))
+ defaultValue.update = updateArrayPref;
+
+ var label = getMsgFrom(bundle, "pref." + prefName + ".label", null, prefName);
+ var help = getMsgFrom(bundle, "pref." + prefName + ".help", null,
+ MSG_NO_HELP);
+ if (group != "hidden")
+ {
+ if (label == prefName)
+ dd("WARNING: !!! Preference without label: " + prefName);
+ if (help == MSG_NO_HELP)
+ dd("WARNING: Preference without help text: " + prefName);
+ }
+
+ this.prefRecords[prefName] = new PrefRecord (prefName, defaultValue,
+ label, help, group);
+
+ this.prefNames.push(prefName);
+ this.prefNames.sort();
+
+ this.prefs.__defineGetter__(prefName, prefGetter);
+ this.prefs.__defineSetter__(prefName, setter);
+}
diff --git a/comm/suite/chatzilla/js/lib/protocol-handlers.jsm b/comm/suite/chatzilla/js/lib/protocol-handlers.jsm
new file mode 100644
index 0000000000..a24ca1a0de
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/protocol-handlers.jsm
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; 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/. */
+
+const EXPORTED_SYMBOLS = [
+ "ChatZillaProtocols",
+ "IRCProtocolHandlerFactory",
+ "IRCSProtocolHandlerFactory",
+ "IRCPROT_HANDLER_CID",
+ "IRCSPROT_HANDLER_CID"
+];
+
+const { classes: Cc, interfaces: Ci, results: Cr } = Components;
+
+const STANDARDURL_CONTRACTID =
+ "@mozilla.org/network/standard-url;1";
+const IOSERVICE_CONTRACTID =
+ "@mozilla.org/network/io-service;1";
+
+const IRCPROT_HANDLER_CONTRACTID =
+ "@mozilla.org/network/protocol;1?name=irc";
+const IRCSPROT_HANDLER_CONTRACTID =
+ "@mozilla.org/network/protocol;1?name=ircs";
+this.IRCPROT_HANDLER_CID =
+ Components.ID("{f21c35f4-1dd1-11b2-a503-9bf8a539ea39}");
+this.IRCSPROT_HANDLER_CID =
+ Components.ID("{f21c35f4-1dd1-11b2-a503-9bf8a539ea3a}");
+
+const IRC_MIMETYPE = "application/x-irc";
+const IRCS_MIMETYPE = "application/x-ircs";
+
+//XXXgijs: Because necko is annoying and doesn't expose this error flag, we
+// define our own constant for it. Throwing something else will show
+// ugly errors instead of seeminly doing nothing.
+const NS_ERROR_MODULE_NETWORK_BASE = 0x804b0000;
+const NS_ERROR_NO_CONTENT = NS_ERROR_MODULE_NETWORK_BASE + 17;
+
+
+function spawnChatZilla(uri) {
+ var cpmm;
+ // Ci.nsISyncMessageSender went in Gecko 61.
+ if (Ci.nsISyncMessageSender) {
+ cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
+ .getService(Ci.nsISyncMessageSender);
+ } else {
+ cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService();
+ }
+ cpmm.sendAsyncMessage("ChatZilla:SpawnChatZilla", { uri });
+}
+
+
+function IRCProtocolHandler(isSecure)
+{
+ this.isSecure = isSecure;
+}
+
+var protocolFlags = Ci.nsIProtocolHandler.URI_NORELATIVE |
+ Ci.nsIProtocolHandler.ALLOWS_PROXY;
+if ("URI_DANGEROUS_TO_LOAD" in Ci.nsIProtocolHandler) {
+ protocolFlags |= Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE;
+}
+if ("URI_NON_PERSISTABLE" in Ci.nsIProtocolHandler) {
+ protocolFlags |= Ci.nsIProtocolHandler.URI_NON_PERSISTABLE;
+}
+if ("URI_DOES_NOT_RETURN_DATA" in Ci.nsIProtocolHandler) {
+ protocolFlags |= Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA;
+}
+
+IRCProtocolHandler.prototype =
+{
+ protocolFlags: protocolFlags,
+
+ allowPort(port, scheme)
+ {
+ // Allow all ports to connect, so long as they are irc: or ircs:
+ return (scheme === 'irc' || scheme === 'ircs');
+ },
+
+ newURI(spec, charset, baseURI)
+ {
+ const port = this.isSecure ? 6697 : 6667;
+
+ if (!Cc.hasOwnProperty("@mozilla.org/network/standard-url-mutator;1")) {
+ const cls = Cc[STANDARDURL_CONTRACTID];
+ const url = cls.createInstance(Ci.nsIStandardURL);
+
+ url.init(Ci.nsIStandardURL.URLTYPE_STANDARD, port, spec, charset, baseURI);
+
+ return url.QueryInterface(Ci.nsIURI);
+ }
+ return Cc["@mozilla.org/network/standard-url-mutator;1"]
+ .createInstance(Ci.nsIStandardURLMutator)
+ .init(Ci.nsIStandardURL.URLTYPE_STANDARD, port, spec, charset, baseURI)
+ .finalize();
+ },
+
+ newChannel(URI)
+ {
+ const ios = Cc[IOSERVICE_CONTRACTID].getService(Ci.nsIIOService);
+ if (!ios.allowPort(URI.port, URI.scheme))
+ throw Cr.NS_ERROR_FAILURE;
+
+ return new BogusChannel(URI, this.isSecure);
+ },
+};
+
+
+this.IRCProtocolHandlerFactory =
+{
+ createInstance(outer, iid)
+ {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ if (!iid.equals(Ci.nsIProtocolHandler) && !iid.equals(Ci.nsISupports))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ const protHandler = new IRCProtocolHandler(false);
+ protHandler.scheme = "irc";
+ protHandler.defaultPort = 6667;
+ return protHandler;
+ },
+};
+
+
+this.IRCSProtocolHandlerFactory =
+{
+ createInstance(outer, iid)
+ {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ if (!iid.equals(Ci.nsIProtocolHandler) && !iid.equals(Ci.nsISupports))
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ const protHandler = new IRCProtocolHandler(true);
+ protHandler.scheme = "ircs";
+ protHandler.defaultPort = 6697;
+ return protHandler;
+ },
+};
+
+
+/* Bogus IRC channel used by the IRCProtocolHandler */
+function BogusChannel(URI, isSecure)
+{
+ this.URI = URI;
+ this.originalURI = URI;
+ this.isSecure = isSecure;
+ this.contentType = this.isSecure ? IRCS_MIMETYPE : IRC_MIMETYPE;
+}
+
+BogusChannel.prototype =
+{
+ /* nsISupports */
+ QueryInterface(iid)
+ {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIChannel) ||
+ iid.equals(Ci.nsIRequest))
+ {
+ return this;
+ }
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ /* nsIChannel */
+ loadAttributes: null,
+ contentLength: 0,
+ owner: null,
+ loadGroup: null,
+ notificationCallbacks: null,
+ securityInfo: null,
+
+ open(observer, context)
+ {
+ spawnChatZilla(this.URI.spec);
+ // We don't throw this (a number, not a real 'resultcode') because it
+ // upsets xpconnect if we do (error in the js console).
+ Components.returnCode = NS_ERROR_NO_CONTENT;
+ },
+
+ asyncOpen(observer, context)
+ {
+ spawnChatZilla(this.URI.spec);
+ // We don't throw this (a number, not a real 'resultcode') because it
+ // upsets xpconnect if we do (error in the js console).
+ Components.returnCode = NS_ERROR_NO_CONTENT;
+ },
+
+ asyncRead(listener, context)
+ {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ /* nsIRequest */
+ isPending()
+ {
+ return true;
+ },
+
+ status: Cr.NS_OK,
+
+ cancel(status)
+ {
+ this.status = status;
+ },
+
+ suspend()
+ {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ resume()
+ {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+};
+
+
+this.ChatZillaProtocols =
+{
+ init()
+ {
+ const compMgr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.registerFactory(IRCPROT_HANDLER_CID,
+ "IRC protocol handler",
+ IRCPROT_HANDLER_CONTRACTID,
+ IRCProtocolHandlerFactory);
+ compMgr.registerFactory(IRCSPROT_HANDLER_CID,
+ "IRC protocol handler",
+ IRCSPROT_HANDLER_CONTRACTID,
+ IRCSProtocolHandlerFactory);
+ },
+
+ initObsolete(compMgr, fileSpec, location, type)
+ {
+ compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
+ compMgr.registerFactoryLocation(IRCPROT_HANDLER_CID,
+ "IRC protocol handler",
+ IRCPROT_HANDLER_CONTRACTID,
+ fileSpec, location, type);
+ compMgr.registerFactoryLocation(IRCSPROT_HANDLER_CID,
+ "IRCS protocol handler",
+ IRCSPROT_HANDLER_CONTRACTID,
+ fileSpec, location, type);
+ },
+};
diff --git a/comm/suite/chatzilla/js/lib/sts.js b/comm/suite/chatzilla/js/lib/sts.js
new file mode 100644
index 0000000000..25052b0985
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/sts.js
@@ -0,0 +1,210 @@
+/* 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/. */
+
+/**
+ * The base CIRCSTS object.
+ */
+function CIRCSTS()
+{
+ this.policyList = new Object();
+ this.preloadList = new Object();
+}
+
+CIRCSTS.prototype.ENABLED = false;
+CIRCSTS.prototype.USE_PRELOAD_LIST = true;
+
+/**
+ * Initializes the STS module.
+ * @param policyFile |nsIFile| for storing the STS cache.
+ */
+CIRCSTS.prototype.init = function(policyFile)
+{
+ this.policyFile = policyFile;
+ this.readCacheFromFile();
+}
+
+/**
+ * Reads the policy cache from disk.
+ */
+CIRCSTS.prototype.readCacheFromFile = function()
+{
+ if (!this.policyFile.exists())
+ return;
+
+ cacheReader = new JSONSerializer(this.policyFile);
+ this.policyList = cacheReader.deserialize();
+ cacheReader.close();
+}
+
+/**
+ * Writes the policy cache to disk.
+ */
+CIRCSTS.prototype.writeCacheToFile = function()
+{
+ cacheWriter = new JSONSerializer(this.policyFile);
+ cacheWriter.serialize(this.policyList);
+ cacheWriter.close();
+}
+
+/**
+ * Utility method for determining if an expiration time has passed.
+ * An expire time of zero is never considered to be expired (as is
+ * the case for knockout entries).
+ *
+ * @param expireTime the time to evaluate, in seconds since the UNIX
+ * epoch.
+ * @return boolean value indicating whether the expiration
+ * time has passed.
+ */
+CIRCSTS.prototype.isPolicyExpired = function(expireTime)
+{
+ if (!expireTime)
+ {
+ return false;
+ }
+
+ if (Date.now() > expireTime)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Utility method for parsing the raw CAP value for an STS policy, typically
+ * received via CAP LS or CAP NEW.
+ *
+ * @param params the comma-separated list of parameters in
+ * key[=value] format.
+ * @return the received parameters in JSON.
+ *
+ */
+CIRCSTS.prototype.parseParameters = function(params)
+{
+ var rv = new Object();
+ var keys = params.toLowerCase().split(",");
+ for (var i = 0; i < keys.length; i++)
+ {
+ var [key, value] = keys[i].split("=");
+ rv[key] = value;
+ }
+
+ return rv;
+}
+
+/**
+ * Remove a policy from the cache.
+ *
+ * @param host the host to remove a policy for.
+ */
+CIRCSTS.prototype.removePolicy = function(host)
+{
+ // If this host is in the preload list, we have to store a knockout entry.
+ // To do this, we set the port and expiration time to zero.
+ if (this.getPreloadPolicy(host))
+ {
+ this.policyList[host] = {
+ port: 0,
+ expireTime: 0
+ };
+ }
+ else
+ {
+ delete this.policyList[host];
+ }
+
+ this.writeCacheToFile();
+}
+
+/**
+ * Retrieves a policy from the preload list for the specified host.
+ *
+ * @param host the host to retrieve a policy for.
+ * @return the policy from the preload list, if any.
+ */
+CIRCSTS.prototype.getPreloadPolicy = function(host)
+{
+ if (this.USE_PRELOAD_LIST && (host in this.preloadList))
+ {
+ return this.preloadList[host];
+ }
+
+ return null;
+}
+
+/**
+ * Checks whether there is an upgrade policy in place for a given host
+ * and, if so, returns the port to connect to.
+ *
+ * @param host the host to query an upgrade policy for.
+ * @return the secure port to connect to, if any.
+ */
+CIRCSTS.prototype.getUpgradePolicy = function(host)
+{
+ if (!this.ENABLED)
+ return null;
+
+ var cdata = this.policyList[host];
+ var pdata = this.getPreloadPolicy(host);
+
+ if (cdata)
+ {
+ // We have a cached policy.
+ if (!this.isPolicyExpired(cdata.expireTime))
+ {
+ // Return null if it's a knockout entry.
+ return cdata.port || null;
+ }
+ else if (!pdata)
+ {
+ // Remove the policy if it is expired and not in the preload list.
+ this.removePolicy(host);
+ }
+ }
+
+ if (pdata)
+ {
+ return pdata.port;
+ }
+
+ return null;
+}
+
+/**
+ * Processes a given policy and caches it. This may also be used to renew a
+ * persistance policy. This should ONLY be called if we are using a secure
+ * connection. Insecure connections MUST switch to a secure port first.
+ *
+ * @param host the host to store an STS policy for.
+ * @param port the currently connected secure port.
+ * @param duration the duration of the new policy.
+ * @param duration the duration of the new policy, in seconds. This may be
+ * zero if the policy needs to be removed.
+ */
+CIRCSTS.prototype.setPolicy = function(host, port, duration)
+{
+ if (!this.ENABLED)
+ return;
+
+ port = Number(port);
+ duration = Number(duration);
+
+ // If duration is zero, that's an indication to immediately remove the
+ // policy, so here's a shortcut.
+ if (!duration)
+ {
+ this.removePolicy(host);
+ return;
+ }
+
+ var expireTime = Date.now() + duration*1000;
+
+ this.policyList[host] = {
+ port: port,
+ expireTime: expireTime
+ };
+
+ this.writeCacheToFile();
+}
diff --git a/comm/suite/chatzilla/js/lib/text-logger.js b/comm/suite/chatzilla/js/lib/text-logger.js
new file mode 100644
index 0000000000..731dbd2eaf
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/text-logger.js
@@ -0,0 +1,134 @@
+/* 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/. */
+
+
+/**
+ * Serializer for lists of data that can be printed line-by-line.
+ * If you pass an autoLimit, it will automatically call limit() once the number
+ * of appended items exceeds the limit (so the number of items will never
+ * exceed limit*2).
+ */
+
+function TextLogger(path, autoLimit)
+{
+ // Check if we can open the path. This will throw if it doesn't work
+ var f = fopen(path, ">>");
+ f.close();
+ this.path = path;
+
+ this.appended = 0;
+ if (typeof autoLimit == "number")
+ this.autoLimit = autoLimit;
+ else
+ this.autoLimit = -1;
+
+ // Limit the amount of data in the file when constructing, when asked to.
+ if (this.autoLimit != -1)
+ this.limit();
+}
+
+/**
+ * Append data (an array or single item) to the file
+ */
+TextLogger.prototype.append =
+function tl_append(data)
+{
+ if (!isinstance(data, Array))
+ data = [data];
+
+ // If we go over the limit, don't write everything twice:
+ if ((this.autoLimit != -1) &&
+ (data.length + this.appended > this.autoLimit))
+ {
+ // Collect complete set of data instead:
+ var dataInFile = this.read();
+ var newData = dataInFile.concat(data);
+ // Get the last |autoLimit| items: yay JS negative indexing!
+ newData = newData.slice(-this.autoLimit);
+ this.limit(newData);
+ return true;
+ }
+
+ var file = fopen(this.path, ">>");
+ for (var i = 0; i < data.length; i++)
+ file.write(ecmaEscape(data[i]) + "\n");
+ file.close();
+ this.appended += data.length;
+
+ return true;
+}
+
+/**
+ * Limit the data already in the file to the data provided, or the count given.
+ */
+TextLogger.prototype.limit =
+function tl_limit(dataOrCount)
+{
+ // Find data and count:
+ var data = null, count = -1;
+ if (isinstance(dataOrCount, Array))
+ {
+ data = dataOrCount;
+ count = data.length;
+ }
+ else if (typeof dataOrCount == "number")
+ {
+ count = dataOrCount;
+ data = this.read();
+ }
+ else if (this.autoLimit != -1)
+ {
+ count = this.autoLimit;
+ data = this.read();
+ }
+ else
+ {
+ throw "Can't limit the length of the file without a limit..."
+ }
+
+ // Write the right data out. Note that we use the back of the array, not
+ // the front (start from data.length - count), without dropping below 0:
+ var start = Math.max(data.length - count, 0);
+ var file = fopen(this.path, ">");
+ for (var i = start; i < data.length; i++)
+ file.write(ecmaEscape(data[i]) + "\n");
+ file.close();
+ this.appended = 0;
+
+ return true;
+}
+
+/**
+ * Reads out the data currently in the file, and returns an array.
+ */
+TextLogger.prototype.read =
+function tl_read()
+{
+ var rv = new Array(), parsedLines = new Array(), buffer = "";
+ var file = fopen(this.path, "<");
+ while (true)
+ {
+ var newData = file.read();
+ if (newData)
+ buffer += newData;
+ else if (buffer.length == 0)
+ break;
+
+ // Got more data in the buffer, so split into lines. Unless we're
+ // done, the last one might not be complete yet, so save that one.
+ // We split rather strictly on line ends, because empty lines should
+ // be preserved.
+ var lines = buffer.split(/\r?\n/);
+ if (!newData)
+ buffer = "";
+ else
+ buffer = lines.pop();
+
+ rv = rv.concat(lines);
+ }
+ // Unescape here...
+ for (var i = 0; i < rv.length; i++)
+ rv[i] = ecmaUnescape(rv[i]);
+ return rv;
+}
diff --git a/comm/suite/chatzilla/js/lib/text-serializer.js b/comm/suite/chatzilla/js/lib/text-serializer.js
new file mode 100644
index 0000000000..d9d2a3c7af
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/text-serializer.js
@@ -0,0 +1,348 @@
+/* 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/. */
+
+/* The serialized file format is pretty generic... each line (using any line
+ * separator, so we don't mind being moved between platforms) consists of
+ * a command name, and some parameters (optionally). The commands 'start'
+ * and 'end' mark the chunks of properties for each object - in this case
+ * motifs. Every command inside a start/end block is considered a property
+ * for the object. There are some rules, but we are generally pretty flexible.
+ *
+ * Example file:
+ * START <Array>
+ * START 0
+ * "message" "Food%3a%20Mmm...%20food..."
+ * END
+ * START 1
+ * "message" "Busy%3a%20Working."
+ * END
+ * START 2
+ * "message" "Not%20here."
+ * END
+ * END
+ *
+ * The whitespace at the start of the inner lines is generated by the
+ * serialisation process, but is ignored when parsing - it is only to make
+ * the file more readable.
+ *
+ * The START command may be followed by one or both of a class name (enclosed
+ * in angle brackets, as above) and a property name (the first non-<>-enclosed
+ * word). Top-level START commands must not have a property name, although a
+ * class name is fine. Only the following class names are supported:
+ * - Object (the default)
+ * - Array
+ *
+ * For arrays, there are some limitations; saving an array cannot save any
+ * properties that are not numerics, due to limitations in JS' for...in
+ * enumeration. Thus, for loading, only items with numeric property names are
+ * allowed. If an item is STARTed inside an array, and specifies no property
+ * name, it will be push()ed into the array instead.
+ */
+
+function TextSerializer(file)
+{
+ this._initialized = false;
+ if (typeof file == "string")
+ this._file = new nsLocalFile(file);
+ else
+ this._file = file;
+ this._open = false;
+ this._buffer = "";
+ this._lines = [];
+ this.lineEnd = "\n";
+ this._initialized = true;
+}
+
+/* open(direction)
+ *
+ * Opens the serializer on the file specified when created, in either the read
+ * ("<") or write (">") directions. When the file is open, only the appropriate
+ * direction of serialization/deserialization may be performed.
+ *
+ * Note: serialize and deserialize automatically open the file if it is not
+ * open.
+ */
+TextSerializer.prototype.open =
+function ts_open(dir)
+{
+ if (!ASSERT((dir == ">") || (dir == "<"), "Bad serialization direction!"))
+ return false;
+ if (this._open)
+ return false;
+
+ this._fileStream = new LocalFile(this._file, dir);
+ if ((typeof this._fileStream == "object") && this._fileStream)
+ this._open = true;
+
+ return this._open;
+}
+
+/* close()
+ *
+ * Closes the file stream and ends reading or writing.
+ */
+TextSerializer.prototype.close =
+function ts_close()
+{
+ if (this._open)
+ {
+ this._fileStream.close();
+ delete this._fileStream;
+ this._open = false;
+ }
+ return true;
+}
+
+/* serialize(object)
+ *
+ * Serializes a single object into the file stream. All properties of the object
+ * are stored in the stream, including properties that contain other objects.
+ */
+TextSerializer.prototype.serialize =
+function ts_serialize(obj)
+{
+ if (!this._open)
+ this.open(">");
+ if (!ASSERT(this._open, "Unable to open the file for writing!"))
+ return;
+
+ var me = this;
+
+ function writeObjProps(o, indent)
+ {
+ function writeProp(name, val)
+ {
+ me._fileStream.write(indent + "\"" + ecmaEscape(name) + "\" " + val +
+ me.lineEnd);
+ };
+
+ for (var p in o)
+ {
+ switch (typeof o[p])
+ {
+ case "string":
+ writeProp(p, '"' + ecmaEscape(o[p]) + '"');
+ break;
+
+ case "number":
+ case "boolean":
+ case "null": // (just in case)
+ case "undefined":
+ // These all serialise to what we want.
+ writeProp(p, o[p]);
+ break;
+
+ case "function":
+ if (isinstance(o[p], RegExp))
+ writeProp(p, ecmaEscape("" + o[p]));
+ // Can't serialize non-RegExp functions (yet).
+ break;
+
+ case "object":
+ if (o[p] == null)
+ {
+ // typeof null == "object", just to catch us out.
+ writeProp(p, "null");
+ }
+ else
+ {
+ var className = "";
+ if (isinstance(o[p], Array))
+ className = "<Array> ";
+
+ me._fileStream.write(indent + "START " + className +
+ ecmaEscape(p) + me.lineEnd);
+ writeObjProps(o[p], indent + " ");
+ me._fileStream.write(indent + "END" + me.lineEnd);
+ }
+ break;
+
+ default:
+ // Can't handle anything else!
+ }
+ }
+ };
+
+ if (isinstance(obj, Array))
+ this._fileStream.write("START <Array>" + this.lineEnd);
+ else
+ this._fileStream.write("START" + this.lineEnd);
+ writeObjProps(obj, " ");
+ this._fileStream.write("END" + this.lineEnd);
+}
+
+/* deserialize()
+ *
+ * Reads in enough of the file to deserialize (realize) a single object. The
+ * object deserialized is returned; all sub-properties of the object are
+ * deserialized with it.
+ */
+TextSerializer.prototype.deserialize =
+function ts_deserialize()
+{
+ if (!this._open)
+ this.open("<");
+ if (!ASSERT(this._open, "Unable to open the file for reading!"))
+ return false;
+
+ var obj = null;
+ var rv = null;
+ var objs = new Array();
+
+ while (true)
+ {
+ if (this._lines.length == 0)
+ {
+ var newData = this._fileStream.read();
+ if (newData)
+ this._buffer += newData;
+ else if (this._buffer.length == 0)
+ break;
+
+ // Got more data in the buffer, so split into lines. Unless we're
+ // done, the last one might not be complete yet, so save that one.
+ var lines = this._buffer.split(/[\r\n]+/);
+ if (!newData)
+ this._buffer = "";
+ else
+ this._buffer = lines.pop();
+
+ this._lines = this._lines.concat(lines);
+ if (this._lines.length == 0)
+ break;
+ }
+
+ // Split each line into "command params...".
+ var parts = this._lines[0].match(/^\s*(\S+)(?:\s+(.*))?$/);
+ var command = parts[1];
+ var params = parts[2];
+
+ // 'start' and 'end' commands are special.
+ switch (command.toLowerCase())
+ {
+ case "start":
+ var paramList = new Array();
+ if (params)
+ paramList = params.split(/\s+/g);
+
+ var className = "";
+ if ((paramList.length > 0) && /^<\w+>$/i.test(paramList[0]))
+ {
+ className = paramList[0].substr(1, paramList[0].length - 2);
+ paramList.shift();
+ }
+
+ if (!rv)
+ {
+ /* The top-level objects are not allowed a property name
+ * in their START command (it is meaningless).
+ */
+ ASSERT(paramList.length == 0, "Base object with name!");
+ // Construct the top-level object.
+ if (className)
+ rv = obj = new window[className]();
+ else
+ rv = obj = new Object();
+ }
+ else
+ {
+ var n;
+ if (paramList.length == 0)
+ {
+ /* Create a new object level, but with no name. This is
+ * only valid if the parent level is an array.
+ */
+ if (!ASSERT(isinstance(obj, Array), "Parent not Array!"))
+ return null;
+ if (className)
+ n = new window[className]();
+ else
+ n = new Object();
+ objs.push(obj);
+ obj.push(n);
+ obj = n;
+ }
+ else
+ {
+ /* Create a new object level, store the reference on the
+ * parent, and set the new object as the current.
+ */
+ if (className)
+ n = new window[className]();
+ else
+ n = new Object();
+ objs.push(obj);
+ obj[ecmaUnescape(paramList[0])] = n;
+ obj = n;
+ }
+ }
+
+ this._lines.shift();
+ break;
+
+ case "end":
+ this._lines.shift();
+ if (rv && (objs.length == 0))
+ {
+ // We're done for the day.
+ return rv;
+ }
+ // Return to the previous object level.
+ obj = objs.pop();
+ if (!ASSERT(obj, "Waaa! no object level to return to!"))
+ return rv;
+ break;
+
+ default:
+ this._lines.shift();
+ // The property name may be enclosed in quotes.
+ if (command[0] == '"')
+ command = command.substr(1, command.length - 2);
+ // But it is always escaped.
+ command = ecmaUnescape(command);
+
+ if (!obj)
+ {
+ /* If we find a line that is NOT starting a new object, and
+ * we don't have a current object, we just assume the START
+ * command was missed.
+ */
+ rv = obj = new Object();
+ }
+ if (params[0] == '"') // String
+ {
+ // Remove quotes, then unescape.
+ params = params.substr(1, params.length - 2);
+ obj[command] = ecmaUnescape(params);
+ }
+ else if (params[0] == "/") // RegExp
+ {
+ var p = params.match(/^\/(.*)\/(\w*)$/);
+ if (ASSERT(p, "RepExp entry malformed, ignored!"))
+ {
+ var re = new RegExp(ecmaUnescape(p[1]), p[2]);
+ obj[command] = re;
+ }
+ }
+ else if (params == "null") // null
+ {
+ obj[command] = null;
+ }
+ else if (params == "undefined") // undefined
+ {
+ obj[command] = undefined;
+ }
+ else if ((params == "true") || (params == "false")) // boolean
+ {
+ obj[command] = (params == "true");
+ }
+ else // Number
+ {
+ obj[command] = Number(params);
+ }
+ break;
+ }
+ }
+ return null;
+}
diff --git a/comm/suite/chatzilla/js/lib/utils.js b/comm/suite/chatzilla/js/lib/utils.js
new file mode 100644
index 0000000000..d8aa88baef
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/utils.js
@@ -0,0 +1,1490 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Namespaces we happen to need:
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+var utils = new Object();
+
+var DEBUG = true;
+var dd, warn, TEST, ASSERT;
+
+if (DEBUG) {
+ var _dd_pfx = "";
+ var _dd_singleIndent = " ";
+ var _dd_indentLength = _dd_singleIndent.length;
+ var _dd_currentIndent = "";
+ var _dd_lastDumpWasOpen = false;
+ var _dd_timeStack = new Array();
+ var _dd_disableDepth = Number.MAX_VALUE;
+ var _dd_currentDepth = 0;
+ dd = function _dd(str) {
+ if (typeof str != "string") {
+ dump(str + "\n");
+ } else if (str == "") {
+ dump("<empty-string>\n");
+ } else if (str[str.length - 1] == "{") {
+ ++_dd_currentDepth;
+ if (_dd_currentDepth >= _dd_disableDepth)
+ return;
+ if (str.indexOf("OFF") == 0)
+ _dd_disableDepth = _dd_currentDepth;
+ _dd_timeStack.push (new Date());
+ if (_dd_lastDumpWasOpen)
+ dump("\n");
+ dump (_dd_pfx + _dd_currentIndent + str);
+ _dd_currentIndent += _dd_singleIndent;
+ _dd_lastDumpWasOpen = true;
+ } else if (str[0] == "}") {
+ if (--_dd_currentDepth >= _dd_disableDepth)
+ return;
+ _dd_disableDepth = Number.MAX_VALUE;
+ var sufx = (new Date() - _dd_timeStack.pop()) / 1000 + " sec";
+ _dd_currentIndent =
+ _dd_currentIndent.substr(0, _dd_currentIndent.length -
+ _dd_indentLength);
+ if (_dd_lastDumpWasOpen)
+ dump(str + " " + sufx + "\n");
+ else
+ dump(_dd_pfx + _dd_currentIndent + str + " " +
+ sufx + "\n");
+ _dd_lastDumpWasOpen = false;
+ } else {
+ if (_dd_currentDepth >= _dd_disableDepth)
+ return;
+ if (_dd_lastDumpWasOpen)
+ dump("\n");
+ dump(_dd_pfx + _dd_currentIndent + str + "\n");
+ _dd_lastDumpWasOpen = false;
+ }
+ }
+ warn = function (msg) { dd("** WARNING " + msg + " **"); }
+ TEST = ASSERT = function _assert(expr, msg) {
+ if (!expr) {
+ var m = "** ASSERTION FAILED: " + msg + " **\n" +
+ getStackTrace();
+ try {
+ alert(m);
+ } catch(ex) {}
+ dd(m);
+ return false;
+ } else {
+ return true;
+ }
+ }
+} else {
+ dd = warn = TEST = ASSERT = function (){};
+}
+
+function dumpObject (o, pfx, sep)
+{
+ var p;
+ var s = "";
+
+ sep = (typeof sep == "undefined") ? " = " : sep;
+ pfx = (typeof pfx == "undefined") ? "" : pfx;
+
+ for (p in o)
+ {
+ if (typeof (o[p]) != "function")
+ s += pfx + p + sep + o[p] + "\n";
+ else
+ s += pfx + p + sep + "function\n";
+ }
+
+ return s;
+
+}
+
+/* Dumps an object in tree format, recurse specifiec the the number of objects
+ * to recurse, compress is a boolean that can uncompress (true) the output
+ * format, and level is the number of levels to intitialy indent (only useful
+ * internally.) A sample dumpObjectTree (o, 1) is shown below.
+ *
+ * + parent (object)
+ * + users (object)
+ * | + jsbot (object)
+ * | + mrjs (object)
+ * | + nakkezzzz (object)
+ * | *
+ * + bans (object)
+ * | *
+ * + topic (string) 'ircclient.js:59: nothing is not defined'
+ * + getUsersLength (function) 9 lines
+ * *
+ */
+function dumpObjectTree (o, recurse, compress, level)
+{
+ var s = "";
+ var pfx = "";
+
+ if (typeof recurse == "undefined")
+ recurse = 0;
+ if (typeof level == "undefined")
+ level = 0;
+ if (typeof compress == "undefined")
+ compress = true;
+
+ for (var i = 0; i < level; i++)
+ pfx += (compress) ? "| " : "| ";
+
+ var tee = (compress) ? "+ " : "+- ";
+
+ for (i in o)
+ {
+ var t, ex;
+
+ try
+ {
+ t = typeof o[i];
+ }
+ catch (ex)
+ {
+ t = "ERROR";
+ }
+
+ switch (t)
+ {
+ case "function":
+ var sfunc = String(o[i]).split("\n");
+ if (sfunc[2] == " [native code]")
+ sfunc = "[native code]";
+ else
+ if (sfunc.length == 1)
+ sfunc = String(sfunc);
+ else
+ sfunc = sfunc.length + " lines";
+ s += pfx + tee + i + " (function) " + sfunc + "\n";
+ break;
+
+ case "object":
+ s += pfx + tee + i + " (object)";
+ if (o[i] == null)
+ {
+ s += " null\n";
+ break;
+ }
+
+ s += "\n";
+
+ if (!compress)
+ s += pfx + "|\n";
+ if ((i != "parent") && (recurse))
+ s += dumpObjectTree (o[i], recurse - 1,
+ compress, level + 1);
+ break;
+
+ case "string":
+ if (o[i].length > 200)
+ s += pfx + tee + i + " (" + t + ") " +
+ o[i].length + " chars\n";
+ else
+ s += pfx + tee + i + " (" + t + ") '" + o[i] + "'\n";
+ break;
+
+ case "ERROR":
+ s += pfx + tee + i + " (" + t + ") ?\n";
+ break;
+
+ default:
+ s += pfx + tee + i + " (" + t + ") " + o[i] + "\n";
+
+ }
+
+ if (!compress)
+ s += pfx + "|\n";
+
+ }
+
+ s += pfx + "*\n";
+
+ return s;
+
+}
+
+function ecmaEscape(str)
+{
+ function replaceNonPrintables(ch)
+ {
+ var rv = ch.charCodeAt().toString(16);
+ if (rv.length == 1)
+ rv = "0" + rv;
+ else if (rv.length == 3)
+ rv = "u0" + rv;
+ else if (rv.length == 4)
+ rv = "u" + rv;
+
+ return "%" + rv;
+ };
+
+ // Replace any character that is not in the 69 character set
+ // [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./]
+ // with an escape sequence. Two digit sequences in the form %XX are used
+ // for characters whose codepoint is less than 255, %uXXXX for all others.
+ // See section B.2.1 of ECMA-262 rev3 for more information.
+ return str.replace(/[^A-Za-z0-9@*_+.\-\/]/g, replaceNonPrintables);
+}
+
+function ecmaUnescape(str)
+{
+ function replaceEscapes(seq)
+ {
+ var ary = seq.match(/([\da-f]{1,2})(.*)|u([\da-f]{1,4})/i);
+ if (!ary)
+ return "<ERROR>";
+
+ var rv;
+ if (ary[1])
+ {
+ // two digit escape, possibly with cruft after
+ rv = String.fromCharCode(parseInt(ary[1], 16)) + ary[2];
+ }
+ else
+ {
+ // four digits, no cruft
+ rv = String.fromCharCode(parseInt(ary[3], 16));
+ }
+
+ return rv;
+ };
+
+ // Replace the escape sequences %X, %XX, %uX, %uXX, %uXXX, and %uXXXX with
+ // the characters they represent, where X is a hexadecimal digit.
+ // See section B.2.2 of ECMA-262 rev3 for more information.
+ return str.replace(/%u?([\da-f]{1,4})/ig, replaceEscapes);
+}
+
+function encodeForXMLAttribute(value) {
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;").replace(/"/g, "&quot;")
+ .replace(/'/g, "&apos;");
+}
+
+function replaceVars(str, vars)
+{
+ // replace "string $with a $variable", with
+ // "string " + vars["with"] + " with a " + vars["variable"]
+
+ function doReplace(symbol)
+ {
+ var name = symbol.substr(1);
+ if (name in vars)
+ return vars[name];
+
+ return "$" + name;
+ };
+
+ return str.replace(/(\$\w[\w\d\-]+)/g, doReplace);
+}
+
+function formatException(ex)
+{
+ if (isinstance(ex, Error))
+ {
+ return getMsg(MSG_FMT_JSEXCEPTION, [ex.name, ex.message, ex.fileName,
+ ex.lineNumber]);
+ }
+ if ((typeof ex == "object") && ("filename" in ex))
+ {
+ return getMsg(MSG_FMT_JSEXCEPTION, [ex.name, ex.message, ex.filename,
+ ex.lineNumber]);
+ }
+
+ return String(ex);
+}
+
+/*
+ * Clones an existing object (Only the enumerable properties
+ * of course.) use as a function..
+ * var c = Clone (obj);
+ * or a constructor...
+ * var c = new Clone (obj);
+ */
+function Clone (obj)
+{
+ var robj = new Object();
+
+ if ("__proto__" in obj)
+ {
+ // Special clone for Spidermonkey.
+ for (var p in obj)
+ {
+ if (obj.hasOwnProperty(p))
+ robj[p] = obj[p];
+ }
+ robj.__proto__ = obj.__proto__;
+ }
+ else
+ {
+ for (var p in obj)
+ robj[p] = obj[p];
+ }
+
+ return robj;
+
+}
+
+function Copy(source, dest, overwrite)
+{
+ if (!dest)
+ dest = new Object();
+
+ for (var p in source)
+ {
+ if (overwrite || !(p in dest))
+ dest[p] = source[p];
+ }
+
+ return dest;
+}
+
+/*
+ * matches a real object against one or more pattern objects.
+ * if you pass an array of pattern objects, |negate| controls whether to check
+ * if the object matches ANY of the patterns, or NONE of the patterns.
+ */
+function matchObject (o, pattern, negate)
+{
+ negate = Boolean(negate);
+
+ function _match (o, pattern)
+ {
+ if (isinstance(pattern, Function))
+ return pattern(o);
+
+ for (var p in pattern)
+ {
+ var val;
+ /* nice to have, but slow as molases, allows you to match
+ * properties of objects with obj$prop: "foo" syntax */
+ /*
+ if (p[0] == "$")
+ val = eval ("o." +
+ p.substr(1,p.length).replace (/\$/g, "."));
+ else
+ */
+ val = o[p];
+
+ if (isinstance(pattern[p], Function))
+ {
+ if (!pattern[p](val))
+ return false;
+ }
+ else
+ {
+ var ary = (new String(val)).match(pattern[p]);
+ if (ary == null)
+ return false;
+ else
+ o.matchresult = ary;
+ }
+ }
+
+ return true;
+
+ }
+
+ if (!isinstance(pattern, Array))
+ return Boolean (negate ^ _match(o, pattern));
+
+ for (var i in pattern)
+ if (_match (o, pattern[i]))
+ return !negate;
+
+ return negate;
+
+}
+
+function equalsObject(o1, o2)
+{
+ for (var p in o1)
+ {
+ if (!(p in o2) || (o1[p] != o2[p]))
+ return false;
+ }
+ for (p in o2)
+ {
+ // If the property did exist in o1, the previous loop tested it:
+ if (!(p in o1))
+ return false;
+ }
+ return true;
+}
+
+function utils_lcfn(text)
+{
+ return text.toLowerCase();
+}
+
+function matchEntry (partialName, list, lcFn)
+{
+
+ if ((typeof partialName == "undefined") ||
+ (String(partialName) == ""))
+ {
+ var ary = new Array();
+ for (var i in list)
+ ary.push(i);
+ return ary;
+ }
+
+ if (typeof lcFn != "function")
+ lcFn = utils_lcfn;
+
+ ary = new Array();
+
+ for (i in list)
+ {
+ if (lcFn(list[i]).indexOf(lcFn(partialName)) == 0)
+ ary.push(i);
+ }
+
+ return ary;
+
+}
+
+function encodeChar(ch)
+{
+ return "%" + ch.charCodeAt(0).toString(16);
+}
+
+function escapeFileName(fileName)
+{
+ // Escape / \ : * ? " < > | so they don't cause trouble.
+ return fileName.replace(/[\/\\\:\*\?"<>\|]/g, encodeChar);
+}
+
+function getCommonPfx (list, lcFn)
+{
+ var pfx = list[0];
+ var l = list.length;
+
+ if (typeof lcFn != "function")
+ lcFn = utils_lcfn;
+
+ for (var i = 0; i < l; i++)
+ {
+ for (var c = 0; c < pfx.length; ++c)
+ {
+ if (c >= list[i].length)
+ {
+ pfx = pfx.substr(0, c);
+ break;
+ }
+ else
+ {
+ if (lcFn(pfx[c]) != lcFn(list[i][c]))
+ pfx = pfx.substr(0, c);
+ }
+ }
+ }
+
+ return pfx;
+
+}
+
+function openTopWin (url)
+{
+ return openDialog (getBrowserURL(), "_blank", "chrome,all,dialog=no", url);
+}
+
+function getWindowByType (windowType)
+{
+ const MEDIATOR_CONTRACTID =
+ "@mozilla.org/appshell/window-mediator;1";
+ const nsIWindowMediator = Components.interfaces.nsIWindowMediator;
+
+ var windowManager =
+ Components.classes[MEDIATOR_CONTRACTID].getService(nsIWindowMediator);
+
+ return windowManager.getMostRecentWindow(windowType);
+}
+
+function toOpenWindowByType(inType, url, features)
+{
+ var topWindow = getWindowByType(inType);
+
+ if (typeof features == "undefined")
+ features = "chrome,extrachrome,menubar,resizable," +
+ "scrollbars,status,toolbar";
+
+ if (topWindow)
+ topWindow.focus();
+ else
+ window.open(url, "_blank", features);
+}
+
+function renameProperty (obj, oldname, newname)
+{
+
+ if (oldname == newname)
+ return;
+
+ obj[newname] = obj[oldname];
+ delete obj[oldname];
+
+}
+
+function newObject(contractID, iface)
+{
+ var rv;
+ var cls = Components.classes[contractID];
+
+ if (!cls)
+ return null;
+
+ switch (typeof iface)
+ {
+ case "undefined":
+ rv = cls.createInstance();
+ break;
+
+ case "string":
+ rv = cls.createInstance(Components.interfaces[iface]);
+ break;
+
+ case "object":
+ rv = cls.createInstance(iface);
+ break;
+
+ default:
+ rv = null;
+ break;
+ }
+
+ return rv;
+
+}
+
+function getService(contractID, iface)
+{
+ var rv;
+ var cls = Components.classes[contractID];
+
+ if (!cls)
+ return null;
+
+ switch (typeof iface)
+ {
+ case "undefined":
+ rv = cls.getService();
+ break;
+
+ case "string":
+ rv = cls.getService(Components.interfaces[iface]);
+ break;
+
+ case "object":
+ rv = cls.getService(iface);
+ break;
+
+ default:
+ rv = null;
+ break;
+ }
+
+ return rv;
+
+}
+
+function getNSSErrorClass(errorCode)
+{
+ var nssErrSvc = getService("@mozilla.org/nss_errors_service;1", "nsINSSErrorsService");
+
+ try
+ {
+ return nssErrSvc.getErrorClass(errorCode);
+ }
+ catch
+ {
+ return 0;
+ }
+}
+
+function getContentWindow(frame)
+{
+ try
+ {
+ if (!frame || !("contentWindow" in frame))
+ return false;
+
+ // The "in" operator does not detect wrappedJSObject, so don't bother.
+ if (frame.contentWindow.wrappedJSObject)
+ return frame.contentWindow.wrappedJSObject;
+ return frame.contentWindow;
+ }
+ catch (ex)
+ {
+ // throws exception is contentWindow is gone
+ return null;
+ }
+}
+
+function getContentDocument(frame)
+{
+ try
+ {
+ if (!frame || !("contentDocument" in frame))
+ return false;
+
+ // The "in" operator does not detect wrappedJSObject, so don't bother.
+ if (frame.contentDocument.wrappedJSObject)
+ return frame.contentDocument.wrappedJSObject;
+ return frame.contentDocument;
+ }
+ catch (ex)
+ {
+ // throws exception is contentDocument is gone
+ return null;
+ }
+}
+
+function getPriv (priv)
+{
+ var rv = true;
+
+ try
+ {
+ netscape.security.PrivilegeManager.enablePrivilege(priv);
+ }
+ catch (e)
+ {
+ dd ("getPriv: unable to get privlege '" + priv + "': " + e);
+ rv = false;
+ }
+
+ return rv;
+
+}
+
+function len(o)
+{
+ var l = 0;
+
+ for (var p in o)
+ ++l;
+
+ return l;
+}
+
+function keys (o)
+{
+ var rv = new Array();
+
+ for (var p in o)
+ rv.push(p);
+
+ return rv;
+
+}
+
+function stringTrim (s)
+{
+ if (!s)
+ return "";
+ s = s.replace (/^\s+/, "");
+ return s.replace (/\s+$/, "");
+
+}
+
+/* the offset should be in seconds, it will be rounded to 2 decimal places */
+function formatDateOffset (offset, format)
+{
+ var seconds = roundTo(offset % 60, 2);
+ var minutes = Math.floor(offset / 60);
+ var hours = Math.floor(minutes / 60);
+ minutes = minutes % 60;
+ var days = Math.floor(hours / 24);
+ hours = hours % 24;
+
+ if (!format)
+ {
+ var ary = new Array();
+
+ if (days == 1)
+ ary.push(MSG_DAY);
+ else if (days > 0)
+ ary.push(getMsg(MSG_DAYS, days));
+
+ if (hours == 1)
+ ary.push(MSG_HOUR);
+ else if (hours > 0)
+ ary.push(getMsg(MSG_HOURS, hours));
+
+ if (minutes == 1)
+ ary.push(MSG_MINUTE);
+ else if (minutes > 0)
+ ary.push(getMsg(MSG_MINUTES, minutes));
+
+ if (seconds == 1)
+ ary.push(MSG_SECOND);
+ else if (seconds > 0 || offset == 0)
+ ary.push(getMsg(MSG_SECONDS, seconds));
+
+ format = ary.join(", ");
+ }
+ else
+ {
+ format = format.replace ("%d", days);
+ format = format.replace ("%h", hours);
+ format = format.replace ("%m", minutes);
+ format = format.replace ("%s", seconds);
+ }
+
+ return format;
+}
+
+function arrayHasElementAt(ary, i)
+{
+ return typeof ary[i] != "undefined";
+}
+
+function arrayContains (ary, elem)
+{
+ return (arrayIndexOf (ary, elem) != -1);
+}
+
+function arrayIndexOf (ary, elem)
+{
+ for (var i in ary)
+ if (ary[i] == elem)
+ return i;
+
+ return -1;
+}
+
+function arrayInsertAt (ary, i, o)
+{
+ ary.splice (i, 0, o);
+}
+
+function arrayRemoveAt (ary, i)
+{
+ ary.splice (i, 1);
+}
+
+function objectContains(o, p)
+{
+ return Object.hasOwnProperty.call(o, p);
+}
+
+/* length should be an even number >= 6 */
+function abbreviateWord (str, length)
+{
+ if (str.length <= length || length < 6)
+ return str;
+
+ var left = str.substr (0, (length / 2) - 1);
+ var right = str.substr (str.length - (length / 2) + 1);
+
+ return left + "..." + right;
+}
+
+/*
+ * Inserts the string |hyphen| into string |str| every |pos| characters.
+ * If there are any wordbreaking characters in |str| within -/+5 characters of
+ * of a |pos| then the hyphen is inserted there instead, in order to produce a
+ * "cleaner" break.
+ */
+function hyphenateWord (str, pos, hyphen)
+{
+ if (str.length <= pos)
+ return str;
+ if (typeof hyphen == "undefined")
+ hyphen = " ";
+
+ /* search for a nice place to break the word, fuzzfactor of +/-5, centered
+ * around |pos| */
+ var splitPos =
+ str.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
+
+ splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
+ var left = str.substr (0, splitPos);
+ var right = hyphenateWord(str.substr (splitPos), pos, hyphen);
+
+ return left + hyphen + right;
+}
+
+/*
+ * Like hyphenateWord, except individual chunks of the word are returned as
+ * elements of an array.
+ */
+function splitLongWord (str, pos)
+{
+ if (str.length <= pos)
+ return [str];
+
+ var ary = new Array();
+ var right = str;
+
+ while (right.length > pos)
+ {
+ /* search for a nice place to break the word, fuzzfactor of +/-5,
+ * centered around |pos| */
+ var splitPos =
+ right.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
+
+ splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
+ ary.push(right.substr (0, splitPos));
+ right = right.substr (splitPos);
+ }
+
+ ary.push (right);
+
+ return ary;
+}
+
+function getRandomElement (ary)
+{
+
+ return ary[Math.floor(Math.random() * ary.length)];
+
+}
+
+function roundTo (num, prec)
+{
+
+ return Math.round(num * Math.pow (10, prec)) / Math.pow (10, prec);
+
+}
+
+function randomRange (min, max)
+{
+
+ if (typeof min == "undefined")
+ min = 0;
+
+ if (typeof max == "undefined")
+ max = 1;
+
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+
+}
+
+// Creates a random string of |len| characters from a-z, A-Z, 0-9.
+function randomString(len) {
+ var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ var rv = "";
+
+ for (var i = 0; i < len; i++)
+ rv += chars.substr(Math.floor(Math.random() * chars.length), 1);
+
+ return rv;
+}
+
+function getStackTrace ()
+{
+ var frame = Components.stack.caller;
+ var str = "<top>";
+
+ while (frame)
+ {
+ var name = frame.name ? frame.name : "[anonymous]";
+ str += "\n" + name + "@" + frame.lineNumber;
+ frame = frame.caller;
+ }
+
+ return str;
+
+}
+
+function getInterfaces (cls)
+{
+ var rv = new Object();
+ var e;
+
+ for (var i in Components.interfaces)
+ {
+ try
+ {
+ var ifc = Components.interfaces[i];
+ cls.QueryInterface(ifc);
+ rv[i] = ifc;
+ }
+ catch (e)
+ {
+ /* nada */
+ }
+ }
+
+ return rv;
+
+}
+
+/**
+ * Calls a named function for each element in an array, sending
+ * the same parameter each call.
+ *
+ * @param ary an array of objects
+ * @param func_name string name of function to call.
+ * @param data data object to pass to each object.
+ */
+function mapObjFunc(ary, func_name, data)
+{
+ /*
+ * WARNING: Caller assumes resonsibility to verify ary
+ * and func_name
+ */
+
+ for (var i in ary)
+ ary[i][func_name](data);
+}
+
+/**
+ * Passes each element of an array to a given function object.
+ *
+ * @param func a function object.
+ * @param ary an array of values.
+ */
+function map(func, ary) {
+
+ /*
+ * WARNING: Caller assumnes responsibility to verify
+ * func and ary.
+ */
+
+ for (var i in ary)
+ func(ary[i]);
+
+}
+
+function getSpecialDirectory(name)
+{
+ if (!("directoryService" in utils))
+ {
+ const DS_CTR = "@mozilla.org/file/directory_service;1";
+ const nsIProperties = Components.interfaces.nsIProperties;
+
+ utils.directoryService =
+ Components.classes[DS_CTR].getService(nsIProperties);
+ }
+
+ return utils.directoryService.get(name, Components.interfaces.nsIFile);
+}
+
+function getFileFromURLSpec(url)
+{
+ var handler = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ return handler.getFileFromURLSpec(url);
+}
+
+function getURLSpecFromFile(file)
+{
+ if (!file)
+ return null;
+
+ if (typeof file == "string")
+ {
+ let fileObj = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsIFile);
+ fileObj.initWithPath(file);
+ file = fileObj;
+ }
+
+ var fileHandler = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ return fileHandler.getURLSpecFromFile(file);
+}
+
+function alert(msg, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_ALERT;
+ ps.alert (parent, title, msg);
+}
+
+function confirm(msg, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_CONFIRM;
+ return ps.confirm (parent, title, msg);
+}
+
+function confirmEx(msg, buttons, defaultButton, checkText,
+ checkVal, parent, title)
+{
+ /* Note that on versions before Mozilla 0.9, using 3 buttons,
+ * the revert or dontsave button, or custom button titles will NOT work.
+ *
+ * The buttons should be listed in the 'accept', 'cancel' and 'extra' order,
+ * and the exact button order is host app- and platform-dependant.
+ * For example, on Windows this is usually [button 1] [button 3] [button 2],
+ * and on Linux [button 3] [button 2] [button 1].
+ */
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+
+ var buttonConstants = {
+ ok: ps.BUTTON_TITLE_OK,
+ cancel: ps.BUTTON_TITLE_CANCEL,
+ yes: ps.BUTTON_TITLE_YES,
+ no: ps.BUTTON_TITLE_NO,
+ save: ps.BUTTON_TITLE_SAVE,
+ revert: ps.BUTTON_TITLE_REVERT,
+ dontsave: ps.BUTTON_TITLE_DONT_SAVE
+ };
+ var buttonFlags = 0;
+ var buttonText = [null, null, null];
+
+ if (!isinstance(buttons, Array))
+ throw "buttons parameter must be an Array";
+ if ((buttons.length < 1) || (buttons.length > 3))
+ throw "the buttons array must have 1, 2 or 3 elements";
+
+ for (var i = 0; i < buttons.length; i++)
+ {
+ var buttonFlag = ps.BUTTON_TITLE_IS_STRING;
+ if ((buttons[i][0] == "!") && (buttons[i].substr(1) in buttonConstants))
+ buttonFlag = buttonConstants[buttons[i].substr(1)];
+ else
+ buttonText[i] = buttons[i];
+
+ buttonFlags += ps["BUTTON_POS_" + i] * buttonFlag;
+ }
+
+ // ignore anything but a proper number
+ var defaultIsNumber = (typeof defaultButton == "number");
+ if (defaultIsNumber && arrayHasElementAt(buttons, defaultButton))
+ buttonFlags += ps["BUTTON_POS_" + defaultButton + "_DEFAULT"];
+
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_CONFIRM;
+ if (!checkVal)
+ checkVal = new Object();
+
+ var rv = ps.confirmEx(parent, title, msg, buttonFlags, buttonText[0],
+ buttonText[1], buttonText[2], checkText, checkVal);
+ return rv;
+}
+
+function prompt(msg, initial, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_PROMPT;
+ var rv = { value: initial };
+
+ if (!ps.prompt (parent, title, msg, rv, null, {value: null}))
+ return null;
+
+ return rv.value;
+}
+
+function promptPassword(msg, initial, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_PROMPT;
+ var rv = { value: initial };
+
+ if (!ps.promptPassword (parent, title, msg, rv, null, {value: null}))
+ return null;
+
+ return rv.value;
+}
+
+function viewCert(cert, parent)
+{
+ var cd = getService("@mozilla.org/nsCertificateDialogs;1",
+ "nsICertificateDialogs");
+ if (!parent)
+ parent = window;
+ cd.viewCert(parent, cert);
+}
+
+function addOrUpdateLogin(url, type, username, password)
+{
+ username = username.toLowerCase();
+ var newinfo = newObject("@mozilla.org/login-manager/loginInfo;1",
+ "nsILoginInfo");
+ newinfo.init(url, null, type, username, password, "", "");
+ var oldinfo = getLogin(url, type, username);
+
+ if (oldinfo) {
+ Services.logins.modifyLogin(oldinfo, newinfo);
+ return true; //updated
+ }
+
+ Services.logins.addLogin(newinfo);
+ return false; //added
+}
+
+function getLogin(url, realm, username)
+{
+ username = username.toLowerCase();
+
+ let logins = Services.logins.findLogins({}, url, null, realm);
+ for (let login of logins) {
+ if (login.username == username) {
+ return login;
+ }
+ }
+
+ return null;
+}
+
+function getHostmaskParts(hostmask)
+{
+ var rv;
+ // A bit cheeky this, we try the matches here, and then branch
+ // according to the ones we like.
+ var ary1 = hostmask.match(/([^ ]*)!([^ ]*)@(.*)/);
+ var ary2 = hostmask.match(/([^ ]*)@(.*)/);
+ var ary3 = hostmask.match(/([^ ]*)!(.*)/);
+ if (ary1)
+ rv = { nick: ary1[1], user: ary1[2], host: ary1[3] };
+ else if (ary2)
+ rv = { nick: "*", user: ary2[1], host: ary2[2] };
+ else if (ary3)
+ rv = { nick: ary3[1], user: ary3[2], host: "*" };
+ else
+ rv = { nick: hostmask, user: "*", host: "*" };
+ // Make sure we got something for all fields.
+ if (!rv.nick)
+ rv.nick = "*";
+ if (!rv.user)
+ rv.user = "*";
+ if (!rv.host)
+ rv.host = "*";
+ // And re-construct the 'parsed' hostmask.
+ rv.mask = rv.nick + "!" + rv.user + "@" + rv.host;
+ return rv;
+}
+
+function makeMaskRegExp(text)
+{
+ function escapeChars(c)
+ {
+ if (c == "*")
+ return ".*";
+ if (c == "?")
+ return ".";
+ return "\\" + c;
+ }
+ // Anything that's not alpha-numeric gets escaped.
+ // "*" and "?" are 'escaped' to ".*" and ".".
+ // Optimisation; * translates as 'match all'.
+ return new RegExp("^" + text.replace(/[^\w\d]/g, escapeChars) + "$", "i");
+}
+
+function hostmaskMatches(user, mask)
+{
+ // Need to match .nick, .user, and .host.
+ if (!("nickRE" in mask))
+ {
+ // We cache all the regexp objects, but use null if the term is
+ // just "*", so we can skip having the object *and* the .match
+ // later on.
+ if (mask.nick == "*")
+ mask.nickRE = null;
+ else
+ mask.nickRE = makeMaskRegExp(mask.nick);
+
+ if (mask.user == "*")
+ mask.userRE = null;
+ else
+ mask.userRE = makeMaskRegExp(mask.user);
+
+ if (mask.host == "*")
+ mask.hostRE = null;
+ else
+ mask.hostRE = makeMaskRegExp(mask.host);
+ }
+
+ var lowerNick;
+ if (user.TYPE == "IRCChanUser")
+ lowerNick = user.parent.parent.toLowerCase(user.unicodeName);
+ else
+ lowerNick = user.parent.toLowerCase(user.unicodeName);
+
+ if ((!mask.nickRE || lowerNick.match(mask.nickRE)) &&
+ (!mask.userRE || user.name.match(mask.userRE)) &&
+ (!mask.hostRE || user.host.match(mask.hostRE)))
+ return true;
+ return false;
+}
+
+function isinstance(inst, base)
+{
+ /* Returns |true| if |inst| was constructed by |base|. Not 100% accurate,
+ * but plenty good enough for us. This is to work around the fix for bug
+ * 254067 which makes instanceof fail if the two sides are 'from'
+ * different windows (something we don't care about).
+ */
+ return (inst && base &&
+ ((inst instanceof base) ||
+ (inst.constructor && (inst.constructor.name == base.name))));
+}
+
+function isDefaultPrevented(ev)
+{
+ if ("defaultPrevented" in ev)
+ return ev.defaultPrevented;
+ return ev.getPreventDefault();
+}
+
+function scaleNumberBy1024(number)
+{
+ var scale = 0;
+ while ((number >= 1000) && (scale < 6))
+ {
+ scale++;
+ number /= 1024;
+ }
+
+ return [scale, number];
+}
+
+function getSISize(size)
+{
+ var data = scaleNumberBy1024(size);
+
+ if (data[1] < 10)
+ data[1] = data[1].toFixed(2);
+ else if (data[1] < 100)
+ data[1] = data[1].toFixed(1);
+ else
+ data[1] = data[1].toFixed(0);
+
+ return getMsg(MSG_SI_SIZE, [data[1], getMsg("msg.si.size." + data[0])]);
+}
+
+function getSISpeed(speed)
+{
+ var data = scaleNumberBy1024(speed);
+
+ if (data[1] < 10)
+ data[1] = data[1].toFixed(2);
+ else if (data[1] < 100)
+ data[1] = data[1].toFixed(1);
+ else
+ data[1] = data[1].toFixed(0);
+
+ return getMsg(MSG_SI_SPEED, [data[1], getMsg("msg.si.speed." + data[0])]);
+}
+
+// Returns -1 if version 1 is newer, +1 if version 2 is newer, and 0 for same.
+function compareVersions(ver1, ver2)
+{
+ var ver1parts = ver1.split(".");
+ var ver2parts = ver2.split(".");
+
+ while ((ver1parts.length > 0) && (ver2parts.length > 0))
+ {
+ if (ver1parts[0] < ver2parts[0])
+ return 1;
+ if (ver1parts[0] > ver2parts[0])
+ return -1;
+ ver1parts.shift();
+ ver2parts.shift();
+ }
+ if (ver1parts.length > 0)
+ return -1;
+ if (ver2parts.length > 0)
+ return 1;
+ return 0;
+}
+
+// Zero-pad Numbers (or pad with something else if you wish)
+function padNumber(num, digits, pad)
+{
+ pad = pad || "0";
+ var rv = num.toString();
+ while (rv.length < digits)
+ rv = pad + rv;
+ return rv;
+}
+
+const timestr = {
+ A: { method: "getDay" },
+ a: { method: "getDay" },
+ B: { method: "getMonth" },
+ b: { method: "getMonth" },
+ c: { replace: null },
+ D: { replace: "%m/%d/%y" },
+ d: { method: "getDate", pad: 2 },
+ e: { method: "getDate", pad: 2, padwith: " " },
+ F: { replace: "%Y-%m-%d" },
+ h: { replace: "%b" },
+ H: { method: "getHours", pad: 2 },
+ k: { method: "getHours", pad: 2, padwith: " " },
+ M: { method: "getMinutes", pad: 2 },
+ p: { AM: null, PM: null },
+ P: { AM: null, PM: null },
+ r: { replace: null },
+ R: { replace: "%H:%M" },
+ S: { method: "getSeconds", pad: 2 },
+ T: { replace: "%H:%M:%S" },
+ w: { method: "getDay" },
+ x: { replace: null },
+ X: { replace: null },
+ Y: { method: "getFullYear" },
+ initialized: false
+}
+
+function strftime(format, time)
+{
+ /* Javascript implementation of standard C strftime */
+
+ if (!timestr.initialized)
+ {
+ timestr.A.values = getMsg("datetime.day.long").split("^");
+ timestr.a.values = getMsg("datetime.day.short").split("^");
+ timestr.B.values = getMsg("datetime.month.long").split("^");
+ timestr.b.values = getMsg("datetime.month.short").split("^");
+ // Just make sure the locale isn't playing silly with us.
+ ASSERT(timestr.A.values.length == 7, "datetime.day.long bad!");
+ ASSERT(timestr.a.values.length == 7, "datetime.day.short bad!");
+ ASSERT(timestr.B.values.length == 12, "datetime.month.long bad!");
+ ASSERT(timestr.b.values.length == 12, "datetime.month.short bad!");
+
+ timestr.p.AM = getMsg("datetime.uam");
+ timestr.p.PM = getMsg("datetime.upm");
+ timestr.P.AM = getMsg("datetime.lam");
+ timestr.P.PM = getMsg("datetime.lpm");
+
+ timestr.c.replace = getMsg("datetime.presets.lc");
+ timestr.r.replace = getMsg("datetime.presets.lr");
+ timestr.x.replace = getMsg("datetime.presets.lx");
+ timestr.X.replace = getMsg("datetime.presets.ux");
+
+ timestr.initialized = true;
+ }
+
+
+ function getDayOfYear(dateobj)
+ {
+ var yearobj = new Date(dateobj.getFullYear(), 0, 1, 0, 0, 0, 0);
+ return Math.floor((dateobj - yearobj) / 86400000) + 1;
+ };
+
+ time = time || new Date();
+ if (!isinstance(time, Date))
+ throw "Expected date object";
+
+ var ary;
+ while ((ary = format.match(/(^|[^%])%(\w)/)))
+ {
+ var start = ary[1] ? (ary.index + 1) : ary.index;
+ var rpl = "";
+ if (ary[2] in timestr)
+ {
+ var tbranch = timestr[ary[2]];
+ if (("method" in tbranch) && ("values" in tbranch))
+ rpl = tbranch.values[time[tbranch.method]()];
+ else if ("method" in tbranch)
+ rpl = time[tbranch.method]().toString();
+ else if ("replace" in tbranch)
+ rpl = tbranch.replace;
+
+ if ("pad" in tbranch)
+ {
+ var padwith = ("padwith" in tbranch) ? tbranch.padwith : "0";
+ rpl = padNumber(rpl, tbranch.pad, padwith);
+ }
+ }
+ if (!rpl)
+ {
+ switch (ary[2])
+ {
+ case "C":
+ var century = Math.floor(time.getFullYear() / 100);
+ rpl = padNumber(century, 2);
+ break;
+ case "I":
+ case "l":
+ var hour = (time.getHours() + 11) % 12 + 1;
+ var padwith = (ary[2] == "I") ? "0" : " ";
+ rpl = padNumber(hour, 2, padwith);
+ break;
+ case "j":
+ rpl = padNumber(getDayOfYear(time), 3);
+ break;
+ case "m":
+ rpl = padNumber(time.getMonth() + 1, 2);
+ break;
+ case "p":
+ case "P":
+ var bit = (time.getHours() < 12) ? "AM" : "PM";
+ rpl = timestr[ary[2]][bit];
+ break;
+ case "s":
+ rpl = Math.round(time.getTime() / 1000);
+ break;
+ case "u":
+ rpl = (time.getDay() + 6) % 7 + 1;
+ break;
+ case "y":
+ rpl = time.getFullYear().toString().substr(2);
+ break;
+ case "z":
+ var mins = time.getTimezoneOffset();
+ rpl = (mins > 0) ? "-" : "+";
+ mins = Math.abs(mins);
+ var hours = Math.floor(mins / 60);
+ rpl += padNumber(hours, 2) + padNumber(mins - (hours * 60), 2);
+ break;
+ }
+ }
+ if (!rpl)
+ rpl = "%%" + ary[2];
+ format = format.substr(0, start) + rpl + format.substr(start + 2);
+ }
+ return format.replace(/%%/, "%");
+}
+
+// This used to be strres.js, copied here to help remove that...
+var strBundleService = null;
+function srGetStrBundle(path)
+{
+ const STRBSCID = "@mozilla.org/intl/stringbundle;1";
+ const STRBSIF = "nsIStringBundleService";
+ var strBundle = null;
+ if (!strBundleService)
+ {
+ try
+ {
+ strBundleService = getService(STRBSCID, STRBSIF);
+ }
+ catch (ex)
+ {
+ dump("\n--** strBundleService failed: " + ex + "\n");
+ return null;
+ }
+ }
+
+ strBundle = strBundleService.createBundle(path);
+ if (!strBundle)
+ dump("\n--** strBundle createInstance failed **--\n");
+
+ return strBundle;
+}
+
+// No-op window.getAttention if it's not found, this is for in-a-tab mode.
+if (typeof getAttention == "undefined")
+ getAttention = function() {};
diff --git a/comm/suite/chatzilla/js/tests/DP.js b/comm/suite/chatzilla/js/tests/DP.js
new file mode 100644
index 0000000000..5320b38c35
--- /dev/null
+++ b/comm/suite/chatzilla/js/tests/DP.js
@@ -0,0 +1,612 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Dissociated Press javascript for the jsbot
+ * see: http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?query=dissociated%20press
+ */
+
+DP_DEBUG = false;
+
+if (DP_DEBUG)
+ dpprint = dd;
+else
+ dpprint = (function () {});
+
+function CDPressMachine()
+{
+
+ this.wordPivots = new Object();
+ this.cleanCounter = 0;
+ this.cleanCount = 0;
+
+}
+
+CDPressMachine.CLEAN_CYCLE = 1000; // list will be trimmed after this many
+ // or never if < 1 addPhrase()s
+CDPressMachine.CLEAN_THRESHOLD = 2; // anything <= this will be trimmed
+CDPressMachine.RANDOMIZE_DEPTH = 10; // not used yet
+CDPressMachine.MIN_PHRASE_LENGTH = 3; // requested minimum phrase length
+CDPressMachine.MAX_PHRASE_LENGTH = 8; // requested maximum phrase length
+CDPressMachine.LENGTH_RETRIES = 3 // number of retries per word
+ // (to reach maxlen)
+CDPressMachine.WORD_PATTERN = /[\x21-\x7e]+/; // pattern for words
+
+/**
+ * Adds a phrase to the engine
+ */
+CDPressMachine.prototype.addPhrase =
+function DPM_addPhrase (strPhrase, weight)
+{
+ if (strPhrase == "")
+ return;
+
+ this.cleanCounter++;
+ if ((CDPressMachine.CLEAN_CYCLE >= 1) &&
+ (this.cleanCounter >= CDPressMachine.CLEAN_CYCLE))
+ {
+ dpprint ("** cleaning list");
+
+ this.cleanCounter = 0;
+ this.trimList (CDPressMachine.CLEAN_THRESHOLD);
+ this.cleancount++;
+ }
+
+ strPhrase = strPhrase.toLowerCase();
+
+ /* split the phrase */
+ var aryWordMatches = strPhrase.split (" ");
+ var previousWord = aryWordMatches[aryWordMatches.length - 1];
+ previousWord = previousWord.match(CDPressMachine.WORD_PATTERN);
+ var nextWord = "";
+
+ /* loop through each word */
+ for (var i=-1; i < aryWordMatches.length; i++)
+ {
+ var currentWord = nextWord;
+ var currentWordPivot = this.wordPivots[currentWord];
+
+ if (typeof currentWordPivot == "undefined")
+ currentWordPivot =
+ (this.wordPivots[currentWord] = new CWordPivot (currentWord));
+
+ currentWordPivot.previousList.addLink (previousWord, weight);
+
+ if (i < aryWordMatches.length - 1)
+ {
+ nextWord = aryWordMatches[i + 1];
+ if (nextWord == (String.fromCharCode(1) + "action"))
+ nextWord = escape(nextWord.toUpperCase());
+ else
+ nextWord = nextWord.match(CDPressMachine.WORD_PATTERN);
+
+ if (nextWord == null)
+ nextWord = ""; //this is weak
+
+ currentWordPivot.nextList.addLink (nextWord, weight);
+ }
+ else
+ currentWordPivot.nextList.addLink ("");
+
+ previousWord = currentWord;
+
+ }
+
+}
+
+CDPressMachine.prototype.addPhrases =
+function DPM_addPhrases(phrases)
+{
+
+ for (var i in phrases)
+ this.addPhrase (phrases[i]);
+
+}
+
+/**
+ * Gets a phrase from the engine, starting from seedWord.
+ * if dir is greater than 0, then seedWord will be the first in
+ * the phrase, otherwise it will be the last
+ */
+CDPressMachine.prototype.getPhraseDirected =
+function DPM_getPhraseDirected(seedWord, dir)
+{
+ var word = (typeof seedWord != "undefined") ? seedWord : "";
+ var tempword = word;
+ var rval = "";
+ var c = 0, retry = 0;
+
+ dpprint ("DPM_getPhraseDirected: '" + word + "' " + dir);
+
+ if (typeof this.wordPivots[word] == "undefined")
+ return;
+
+ do
+ {
+ if (typeof this.wordPivots[word] == "undefined")
+ {
+ dd ("** DP Error: Word '" + word + "' is not a pivot **");
+ return;
+ }
+
+ if (dir > 0) // pick a word
+ word= this.wordPivots[word].nextList.getRandomLink().link;
+ else
+ word= this.wordPivots[word].previousList.getRandomLink().link;
+
+ if (word != "") // if it isn't blank
+ {
+ dpprint ("DPM_getPhraseDirected: got word '" + word + "'");
+
+ if (c < CDPressMachine.MIN_PHRASE_LENGTH)
+ retry = 0;
+
+ if (c > CDPressMachine.MAX_PHRASE_LENGTH)
+ if (((dir > 0) && (this.wordPivots[word].nextList.list[""])) ||
+ ((dir <= 0) &&
+ (this.wordPivots[word].previousList.list[""])))
+ {
+ dpprint ("DPM_getPhraseDirected: forcing last word");
+ word="";
+ rval = rval.substring (0, rval.length - 1);
+ break;
+ }
+
+ if (dir > 0)
+ rval += word + " "; // put it in the rslt
+ else
+ rval = word + " " + rval;
+
+ c++; // count the word
+ }
+ else // otherwise
+ {
+ dpprint ("DPM_getPhraseDirected: last word");
+ // if it's too short
+ // and were not out of retrys
+ if ((c < CDPressMachine.MIN_PHRASE_LENGTH) &&
+ (retry++ < CDPressMachine.LENGTH_RETRIES))
+ word = tempword; // try again
+ else
+ // otherwise, we're done
+ rval = rval.substring (0, rval.length - 1);
+ }
+
+ tempword = word;
+
+ } while (word != "");
+
+ rval = unescape (rval);
+
+ return rval;
+
+}
+
+CDPressMachine.prototype.getPhraseForward =
+function DPM_getPhraseForward(firstWord)
+{
+ return this.getPhraseDirected (firstWord, 1)
+}
+
+CDPressMachine.prototype.getPhraseReverse =
+function DPM_getPhraseReverse(lastWord)
+{
+ return this.getPhraseDirected (lastWord, -1)
+}
+
+/**
+ * locates a random pivot by following CDPressMachine.RANDOMIZE_DEPTH
+ * links from |word|.
+ */
+CDPressMachine.prototype.getRandomPivot =
+function DPM_getRandomPivot (word)
+{
+
+ /**
+ * XXXrgg: erm, this is currently pointless, but could be neat later
+ * if max phrase length's were implemented.
+ */
+ if (false)
+ {
+ var depth = parseInt (Math.round
+ (CDPressMachine.RANDOMIZE_DEPTH * Math.random()));
+ word = "";
+ for (var i = 0;
+ i < depth, word =
+ this.wordPivots[word].nextList.getRandomLink().link;
+ i++); /* empty loop */
+
+ }
+
+}
+
+CDPressMachine.prototype.getPhrase =
+function DPM_getPhrase(word)
+{
+ var rval = this.getPhraseContaining (word);
+
+ return rval;
+
+}
+
+/**
+ * Returns a phrase with |word| somewhere in it.
+ */
+CDPressMachine.prototype.getPhraseContaining =
+function DPM_getPhraseContaining(word)
+{
+ if (typeof word == "undefined")
+ word = "";
+ else
+ word = word.toString();
+
+ dpprint ("* DPM_getPhraseContaining: '" + word + "'");
+
+ var rval, spc;
+ var post, pre = this.getPhraseReverse (word);
+ if (word != "")
+ var post = this.getPhraseForward (word);
+
+ dpprint ("* DPM_getPhraseContaining: pre = '" + pre + "' post = '" +
+ post + "'");
+ dpprint ("* DPM_getPhraseContaining: " + (post == "" && pre == ""));
+
+ if (word)
+ {
+ word = unescape (word);
+ spc = " ";
+ }
+ else
+ spc = "";
+
+ if (pre)
+ {
+ if (post)
+ rval = pre + spc + word + spc + post;
+ else
+ rval = pre + spc + word;
+ }
+ else
+ {
+ if (post)
+ rval = word + spc + post;
+ else
+ if (post == "" && pre == "")
+ rval = word;
+ }
+
+ if (rval && (rval.charCodeAt(0) == 1))
+ rval += String.fromCharCode(1);
+
+ dpprint ("* DPM_getPhraseContaining: returning '" + rval + "'");
+
+ return rval;
+
+}
+
+CDPressMachine.prototype.getPhraseWeight =
+function DPM_getPhraseWeight (phrase)
+{
+ var ary = this.getPhraseWeights (phrase);
+ var w = 0;
+
+ while (ary.length > 0)
+ w += ary.pop();
+
+ return w;
+}
+
+CDPressMachine.prototype.getPhraseWeights =
+function DPM_getPhraseWeights (phrase)
+{
+ var words, ary = new Array();
+ var lastword = "";
+ var link, pivot;
+
+ if (!phrase)
+ return ary;
+
+ words = phrase.split (" ");
+
+ for (var i = 0; i < words.length; i++)
+ {
+
+ if (i == 0)
+ {
+ lastWord = "";
+ nextWord = words[i + 1];
+ }
+ else if (i == words.length - 1)
+ {
+ lastWord = words[i - 1];
+ nextWord = "";
+ }
+ else
+ {
+ lastWord = words[i - 1];
+ nextWord = words[i + 1];
+ }
+
+ pivot = this.wordPivots[words[i]];
+
+ if (pivot)
+ {
+ link = pivot.previousList.list[lastWord];
+
+ if (link)
+ ary.push(link.weight);
+ else
+ ary.push(0);
+
+ link = pivot.nextList.list[nextWord];
+
+ if (link)
+ ary.push(link.weight);
+ else
+ ary.push(0);
+ }
+ else
+ {
+ ary.push(0);
+ ary.push(0);
+ }
+
+ }
+
+ return ary;
+
+}
+
+CDPressMachine.prototype.getPivot =
+function DPM_getPivot(word)
+{
+
+ return this.wordPivots[word];
+
+}
+
+CDPressMachine.prototype.trimList =
+function DPM_trimList(threshold)
+{
+ var el;
+ var c;
+
+ for (el in this.wordPivots)
+ {
+ c = this.wordPivots[el].nextList.trimList (threshold);
+ if (c == 0)
+ delete this.wordPivots[el];
+ else
+ {
+ c = this.wordPivots[el].previousList.trimList (threshold);
+ if (c == 0)
+ delete this.wordPivots[el];
+ }
+
+ }
+
+}
+
+CDPressMachine.prototype.getMachineStatus =
+function DPM_getMachineStatus()
+{
+ var o = new Object();
+
+ o.pivotcount = 0;
+ o.linkcount = 0;
+ o.linksperpivot = 0;
+ o.maxweight = 0;
+ o.minweight = Number.MAX_VALUE;
+ o.averageweight = 0;
+ o.cleancounter = this.cleanCounter;
+ o.cleancount = this.cleanCount;
+
+ for (var pivot in this.wordPivots)
+ {
+ o.pivotcount++;
+
+ for (var link in this.wordPivots[pivot].previousList.list)
+ {
+ var l = this.wordPivots[pivot].previousList.list[link];
+
+ o.linkcount++;
+
+ o.maxweight = Math.max (o.maxweight, l.weight);
+ o.minweight = Math.min (o.minweight, l.weight);
+
+ (o.averageweight == 0) ?
+ o.averageweight = l.weight :
+ o.averageweight = (l.weight + o.averageweight) / 2;
+
+ }
+ }
+
+ o.linksperpivot = o.linkcount / o.pivotcount;
+
+ return o;
+
+}
+
+////////////////////////
+function CWordPivot (word)
+{
+
+ dpprint ("* new pivot : '" + word + "'");
+ this.word = word;
+ this.nextList = new CPhraseLinkList(word, "next");
+ this.previousList = new CPhraseLinkList(word, "previous");
+
+}
+
+///////////////////////
+
+function CPhraseLinkList (parentWord, listID)
+{
+
+ if (DP_DEBUG)
+ {
+ this.parentWord = parentWord;
+ this.listID = listID;
+ }
+
+ this.list = new Object();
+
+}
+
+CPhraseLinkList.prototype.addLink =
+function PLL_addLink (link, weight)
+{
+ var existingLink = this.list[link];
+
+ dpprint ("* adding link to '" + link + "' from '" + this.parentWord +
+ "' in list '" + this.listID + "'");
+
+ if (typeof weight == "undefined")
+ weight = 1;
+
+ if (typeof existingLink == "undefined")
+ this.list[link] = new CPhraseLink (link, weight);
+ else
+ if (!(typeof existingLink.adjust == "function"))
+ dd("existingLink.adjust is a '" + existingLink.adjust + "' " +
+ "not a function! link is '" + link +"'");
+ else
+ existingLink.adjust (weight);
+
+}
+
+CPhraseLinkList.prototype.getRandomLink =
+function PLL_getRandomLink ()
+{
+ var tot = 0;
+ var lastMatch = "";
+ var aryChoices = new Array();
+ var fDone = false;
+
+ dpprint ("* PLL_getRandomLink: from '" + this.parentWord + "'");
+
+ for (el in this.list)
+ {
+ tot += this.list[el].weight;
+
+ for (var i = 0; i< aryChoices.length; i++)
+ if (this.list[el].weight <= aryChoices[i].weight)
+ break;
+
+ arrayInsertAt (aryChoices, i, this.list[el]);
+ }
+
+ if (DP_DEBUG)
+ for (var i = 0; i < aryChoices.length; i++)
+ dpprint ("** potential word '" + aryChoices[i].link + "', weight " +
+ aryChoices[i].weight);
+
+ var choice = parseInt (Math.round(((tot - 1) * Math.random()) + 1));
+
+ dpprint ("* PLL_getRandomLink: tot = " + tot + ", choice = " + choice);
+
+ tot = 0;
+ for (i = 0; i < aryChoices.length; i++)
+ {
+ if ((tot += aryChoices[i].weight) >= choice)
+ {
+ lastMatch = aryChoices[i];
+ break;
+ }
+ }
+
+ if (lastMatch == "")
+ lastMatch = aryChoices[aryChoices.length - 1];
+
+ if (!lastMatch)
+ lastMatch = {link: ""}
+
+ dpprint ("* PLL_getRandomLink: returning: " + lastMatch);
+
+ return lastMatch;
+
+}
+
+CPhraseLinkList.prototype.getListWeights =
+function PLL_getListWeights ()
+{
+ var ary = new Array();
+
+ for (var el in this.list)
+ ary.push (this.list[el].weight);
+
+ return ary;
+
+}
+
+CPhraseLinkList.prototype.getListLinks =
+function PLL_getListLinks ()
+{
+ var ary = new Array();
+
+ for (var el in this.list)
+ ary.push (this.list[el].link);
+
+ return ary;
+
+}
+
+CPhraseLinkList.prototype.trimList =
+function PLL_trimList (threshold)
+{
+ var el;
+ var c;
+
+ dpprint ("trimming '" + this.parentWord + "'s list to " + threshold);
+
+ for (el in this.list)
+ {
+ c++;
+
+ if (this.list[el].weight <= threshold)
+ {
+ dpprint ("removing '" + el + "' from '" + this.parentWord + "'s '" +
+ this.listID + "' list, because it's weight is " +
+ this.list[el].weight);
+
+ delete this.list[el];
+ c--;
+ }
+ }
+
+ return c;
+
+}
+
+////////////////////////
+
+function CPhraseLink (link, weight)
+{
+ if (typeof weight == "undefined")
+ this.weight = 1;
+ else
+ this.weight = weight;
+
+ this.link = link;
+
+}
+
+CPhraseLink.prototype.adjust =
+function PL_adjust(weight)
+{
+
+ if ((this.weight += weight) < 1)
+ this.weight = 1;
+
+}
+
+CPhraseLink.prototype.weight =
+function PL_weight ()
+{
+
+ return this.weight;
+
+}
diff --git a/comm/suite/chatzilla/js/tests/ircbot.js b/comm/suite/chatzilla/js/tests/ircbot.js
new file mode 100644
index 0000000000..e3683d034c
--- /dev/null
+++ b/comm/suite/chatzilla/js/tests/ircbot.js
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var LIB_PATH = "../lib/";
+
+bot = new Object();
+bot.ownerPatterns = new Array();
+bot.personality = new Object();
+bot.personality.hooks = new Array();
+bot.prefix = "!js ";
+
+function loadDeps()
+{
+ load(LIB_PATH + "utils.js");
+ load(LIB_PATH + "events.js");
+ load(LIB_PATH + "connection.js");
+ load(LIB_PATH + "http.js");
+ load(LIB_PATH + "dcc.js");
+ load(LIB_PATH + "irc.js");
+ load(LIB_PATH + "irc-debug.js");
+ load(LIB_PATH + "message-manager.js");
+
+ bot.messageManager = new MessageManager();
+
+ load(LIB_PATH + "connection-xpcom.js");
+
+ return true;
+}
+
+// FIXME: Find somewhere better for these guys. //
+function toUnicode(msg, charsetOrView)
+{
+ if (!msg)
+ return msg;
+
+ var charset;
+ if (typeof charsetOrView == "string")
+ charset = charsetOrView;
+ else
+ return msg;
+
+ return bot.messageManager.toUnicode(msg, charset);
+}
+
+function fromUnicode(msg, charsetOrView)
+{
+ if (!msg)
+ return msg;
+
+ var charset;
+ if (typeof charsetOrView == "string")
+ charset = charsetOrView;
+ else
+ return msg;
+
+ return bot.messageManager.fromUnicode(msg, charset);
+}
+// FIXME: END //
+
+
+function initStatic()
+{
+ CIRCNetwork.prototype.INITIAL_NICK = "jsbot";
+ CIRCNetwork.prototype.INITIAL_NAME = "XPJSBot";
+ CIRCNetwork.prototype.INITIAL_DESC = "XPCOM Javascript bot";
+ CIRCNetwork.prototype.INITIAL_CHANNEL = "#jsbot";
+
+ CIRCNetwork.prototype.stayingPower = true;
+ CIRCNetwork.prototype.on433 = my_433;
+ CIRCChannel.prototype.onPrivmsg = my_chan_privmsg;
+ CIRCUser.prototype.onDCCChat = my_user_dccchat;
+ CIRCDCCChat.prototype.onRawData = my_dccchat_rawdata;
+}
+
+/*
+ * One time initilization stuff
+ */
+function init(obj)
+{
+ obj.eventPump = new CEventPump(100);
+
+ obj.networks = new Object();
+ obj.networks["libera.chat"] =
+ new CIRCNetwork("libera.chat", [{name: "libera.chat", port: 6667}],
+ obj.eventPump);
+
+ obj.networks["efnet"] =
+ new CIRCNetwork ("efnet", [{name: "irc.mcs.net", port: 6667},
+ {name: "irc.cs.cmu.edu", port: 6667}],
+ obj.eventPump);
+
+ obj.primNet = obj.networks["efnet"];
+}
+
+/*
+ * Kick off the mainloop for the first time
+ */
+function go()
+{
+ if (!loadDeps())
+ return false;
+
+ // The utils.js formatException relies on localization, we can't. Fix:
+ formatException = function formatException(ex)
+ {
+ if (isinstance(ex, Error) ||
+ ((typeof ex == "object") && ("filename" in ex)))
+ {
+ return [ex.name, ex.message, ex.fileName, ex.lineNumber].join(", ");
+ }
+
+ return String(ex);
+ };
+
+ initStatic();
+ init(bot);
+ if (DEBUG)
+ {
+ /* hook all events EXCEPT server.poll and *.event-end types
+ * (the 4th param inverts the match) */
+ bot.eventPump.addHook([{type: "poll", set: /^(server|dcc-chat)$/},
+ {type: "event-end"}], event_tracer,
+ "event-tracer", true /* negate */);
+ }
+
+ if (typeof initPersonality == "function")
+ initPersonality();
+
+ bot.primNet.connect();
+ rego();
+
+ return true;
+}
+
+/*
+ * If you didn't compile libjs with JS_HAS_ERROR_EXCEPTIONS, any error the
+ * bot encounters will exit the mainloop and drop you back to a shell ("js>")
+ * prompt. You can continue the mainloop by executing this function.
+ */
+function rego()
+{
+ /* mainloop */
+ while (bot.eventPump.queue.length > 0)
+ {
+ bot.eventPump.stepEvents();
+ if (typeof gc == "function")
+ {
+ if ((typeof bot.lastGc == "undefined") ||
+ (Number(new Date()) - bot.lastGc > 60000))
+ {
+ gc();
+ bot.lastGc = Number(new Date());
+ }
+ }
+ }
+ dd("No events to process.");
+
+ return true;
+}
+
+function addOwner(pattern)
+{
+ bot.ownerPatterns.push(pattern);
+}
+
+function userIsOwner(user)
+{
+ if (!user.host)
+ {
+ /* we havn't got any information on this user yet. They havn't spoken
+ * yet, and we havn't /whoi's them yet. Say no for now, but do the
+ * /whois so we'll know for sure next time.
+ */
+ if (user.TYPE == "IRCChanUser")
+ user.parent.parent.sendData("WHOIS " + user.unicodeName + "\n");
+ else
+ user.parent.sendData("WHOIS " + user.unicodeName + "\n");
+ return false;
+ }
+
+ var userString = user.unicodeName + "!" + user.name + "@" + user.host;
+ dd("userIsOwner: checking userString `" + userString + "' against:");
+
+ for (var p in bot.ownerPatterns)
+ {
+ if (userString.search(bot.ownerPatterns[p]) != -1)
+ {
+ dd(String(bot.ownerPatterns[p]) + " passed.");
+ return true;
+ }
+ else
+ {
+ dd(String(bot.ownerPatterns[p]) + " fails.");
+ }
+ }
+
+ return false;
+}
+
+function psn_isAddressedToMe(e)
+{
+ if (!e.server)
+ return false;
+
+ if ((e.type.search(/privmsg|ctcp-action/)) || (e.set != "channel"))
+ return false;
+
+ var msg = e.decodeParam(2);
+
+ if (msg.indexOf(bot.prefix) == 0)
+ return false;
+
+ /*
+ dd ("-*- checking to see if message '" + msg + "' is addressed to me.");
+ */
+
+ var regex = new RegExp("^\\s*" + e.server.me.unicodeName + "\\W+(.*)", "i");
+ var ary = msg.match(regex);
+
+ //dd ("address match: " + ary);
+
+ if (ary != null)
+ {
+ e.statement = ary[1];
+ return true;
+ }
+
+ //XXXgijs: Shouldn't this be in mingus.js?
+ bot.personality.dp.addPhrase(msg);
+ return false;
+}
+
+function psn_onAddressedMsg(e)
+{
+
+ bot.eventPump.onHook(e, bot.personality.hooks);
+ return false;
+}
+
+bot.personality.addHook =
+function psn_addhook(pattern, f, name, neg, enabled)
+{
+ if (pattern instanceof RegExp)
+ pattern = {statement: pattern};
+
+ return bot.eventPump.addHook(pattern, f, name, neg, enabled,
+ bot.personality.hooks);
+}
+
+function bot_eval(e, script)
+{
+ try
+ {
+ var v = eval(script);
+ }
+ catch (ex)
+ {
+ e.replyTo.say(e.user.unicodeName + ": " + String(ex));
+ return false;
+ }
+
+ if (typeof v != "undefined")
+ {
+ if (v != null)
+ v = String(v);
+ else
+ v = "null";
+
+ var rsp = e.user.unicodeName + ", your result is,";
+
+ if (v.indexOf("\n") != -1)
+ rsp += "\n";
+ else
+ rsp += " ";
+
+ e.replyTo.say(rsp + v);
+ }
+}
+
+/*
+ * The following my_* are attached to their proper objects in the init()
+ * function. This is because the CIRC* objects are not defined at load time
+ * (they get defined when loadDeps() loads the irc library) and so connecting
+ * them here would cause an error.
+ */
+
+/*
+ * What to do when a privmsg is received on a channel
+ */
+function my_chan_privmsg(e)
+{
+ var user = e.user;
+ var msg = e.decodeParam(2);
+ if ((msg.indexOf(bot.prefix) == 0) && userIsOwner(user))
+ {
+ /* if last char is a continuation character, then... */
+ if (msg[msg.length - 1] == "\\")
+ {
+ user.accumulatedScript = msg.substring(bot.prefix.length,
+ msg.length - 1);
+ return false; // prevent other hooks from processing this...
+ }
+ else
+ {
+ return bot_eval(e, msg.substring(bot.prefix.length,
+ msg.length));
+ }
+ }
+ else if ((typeof(user.accumulatedScript) != "undefined") &&
+ userIsOwner(user))
+ /* if we were accumulating a message, add here,
+ * and finish if not ends with '\'. */
+ {
+ var lastLine = (msg[msg.length - 1] != "\\");
+ var line = msg.substring(0, msg.length - (lastLine ? 0 : 1));
+ user.accumulatedScript += line;
+ if (lastLine)
+ {
+ var script = user.accumulatedScript;
+ delete user.accumulatedScript;
+ return bot_eval(e, script);
+ }
+ }
+}
+
+/*
+ * What to do when a dcc chat request reaches a user object
+ */
+function my_user_dccchat(e)
+{
+ if (!e.user.canDCC)
+ {
+ e.user.notice("\01DCC REJECT CHAT chat\01");
+ return false;
+ }
+
+ var c = new CIRCDCCChat(bot.eventPump);
+
+ if (!c.connect(e.user.host, e.port))
+ {
+ e.user.notice("\01DCC REJECT CHAT chat\01");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * What to do when our requested nickname is in use
+ */
+function my_433(e)
+{
+ if (e.params[2] != CIRCNetwork.prototype.INITIAL_NICK)
+ {
+ /* server didn't like the last nick we tried, probably too long.
+ * not much more we can do, bail out. */
+ e.server.disconnect();
+ }
+
+ CIRCNetwork.prototype.INITIAL_NICK += "_";
+ e.server.sendData("nick " + CIRCNetwork.prototype.INITIAL_NICK + "\n");
+}
+
+/*
+ * What to do when raw data is received on a dcc chat connection
+ */
+function my_dccchat_rawdata(e)
+{
+ try
+ {
+ var v = eval(e.data);
+ }
+ catch (ex)
+ {
+ this.say(String(ex));
+ return false;
+ }
+
+ if (typeof v != "undefined")
+ {
+ if (v != null)
+ v = String(v);
+ else
+ v = "null";
+
+ this.say(v);
+ }
+}
+
+/*
+ * Wrapper around CHTTPDoc to make is simpler to use
+ */
+function loadHTTP(host, path, onComplete)
+{
+ var htdoc = new CHTTPDoc(host, path);
+
+ htdoc.onComplete = onComplete;
+ htdoc.get(bot.eventPump);
+
+ return htdoc;
+}
+
+
+
diff --git a/comm/suite/chatzilla/js/tests/mingus.js b/comm/suite/chatzilla/js/tests/mingus.js
new file mode 100644
index 0000000000..2590ed919e
--- /dev/null
+++ b/comm/suite/chatzilla/js/tests/mingus.js
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+bot.personality.guessPrefixes = ["I guess ", "maybe ", "probably ", "I think ",
+ "could be that ", "", ""];
+bot.personality.guessActionPrefixes = ["guesses ", "postulates ", "figures ",
+ "tries ","pretends ", "", ""];
+
+function initMingus()
+{
+ //XXX You should add the owner(s) of the bot here. You can do so with a
+ // regular expression which matches their hostmask, like so:
+ // addOwner(/rginda.*!.*@.*netscape\.com$/i);
+
+ bot.primNet = bot.networks["libera.chat"];
+
+ load("DP.js");
+ CIRCNetwork.prototype.INITIAL_NICK = "mingus";
+ CIRCNetwork.prototype.INITIAL_NAME = "mingus";
+ CIRCNetwork.prototype.INITIAL_DESC = "real men do it with prototypes";
+ CIRCNetwork.prototype.INITIAL_CHANNEL = "#chatzilla";
+
+ CIRCChannel.prototype.onJoin =
+ function my_chan_join(e)
+ {
+ if (userIsOwner(e.user))
+ e.user.setOp(true);
+ };
+
+ bot.eventPump.addHook(psn_isAddressedToMe, psn_onAddressedMsg,
+ "addressed-to-me-hook");
+ bot.personality.dp = new CDPressMachine();
+ /*
+ bot.personality.dp.addPhrase ("I am " +
+ CIRCNetwork.prototype.INITIAL_NICK +
+ ", hear me roar.");
+ */
+ bot.personality.dp.addPhrase("\01ACTION is back.");
+
+ /* dp hooks start */
+
+ var f = function(e)
+ {
+ var catchall = (e.hooks[e.hooks.length - 1].name == "catchall");
+ var answer = "";
+
+ if (catchall)
+ {
+ var ary = e.statement.split(" ");
+ for (var i = 0; i < 3; i++)
+ {
+ var randomElem = getRandomElement(ary);
+ answer = bot.personality.dp.getPhraseContaining(randomElem);
+ if (answer)
+ break;
+ }
+ }
+
+ if (!answer)
+ answer = bot.personality.dp.getPhrase();
+
+ if (answer[answer.length - 1] == "\01")
+ {
+ if (answer[0] != "\01")
+ {
+ if (catchall)
+ {
+ var prefes = bot.personality.guessActionPrefes;
+ answer = "\01ACTION " + getRandomElement(prefes) + answer;
+ }
+ else
+ {
+ answer = "\01ACTION " + answer;
+ }
+ }
+ }
+ else
+ {
+ if (!answer)
+ answer = "I don't know anything";
+
+ if (catchall)
+ {
+ answer = getRandomElement(bot.personality.guessPrefixes) +
+ answer;
+ }
+ }
+
+ if (answer[0] != "\01")
+ e.replyTo.say(e.user.unicodeName + ", " + answer);
+ else
+ e.replyTo.say(answer);
+
+ return false;
+ };
+
+/* first hook added is last checked */
+ bot.personality.addHook(/.*/i, f, "catchall");
+ bot.personality.addHook(/speak$/i, f, "speak");
+ bot.personality.addHook(/talk$/i, f, "hook");
+ bot.personality.addHook(/say something$/i, f, "say-something");
+
+ f = function(e)
+ {
+ var subject = e.matchresult[1].match(CDPressMachine.WORD_PATTERN);
+ if (subject == null)
+ subject = "";
+ else
+ subject = subject.toString();
+
+ var escapedSubject = escape(subject.toLowerCase());
+ var answer = bot.personality.dp.getPhraseContaining(escapedSubject);
+
+ if (!answer)
+ answer = "I don't know anything about " + e.matchresult[1];
+
+ if (answer.charCodeAt(0) != 1)
+ e.replyTo.say(e.user.unicodeName + ", " + answer);
+ else
+ e.replyTo.say(answer);
+
+ return false;
+ };
+
+ bot.personality.addHook(/speak about (\S+)/i, f);
+ bot.personality.addHook(/talk about (\S+)/i, f);
+ bot.personality.addHook(/say something about (\S+)/i, f);
+
+ f = function(e)
+ {
+ var answer = bot.personality.dp.getPhraseContaining("%01ACTION");
+
+ if (!answer)
+ answer = "I can't do a thing.";
+
+ e.replyTo.say(answer);
+
+ return false;
+ };
+
+ bot.personality.addHook(/do something/i, f);
+
+ f = function(e)
+ {
+ var ary = bot.personality.dp.getPhraseWeights(e.matchresult[1]);
+ var c = bot.personality.dp.getPhraseWeight(e.matchresult[1]);
+
+ e.replyTo.say(e.user.unicodeName + ", that phrase weighs " +
+ c + ": " + ary);
+
+ return false;
+ };
+
+ bot.personality.addHook(/weigh (.+)/i, f);
+
+ f = function(e)
+ {
+ var word = e.matchresult[1].toLowerCase();
+ var pivot = bot.personality.dp.getPivot(word);
+ var result = "";
+
+ if (pivot)
+ {
+ var list, w, l;
+
+ list = pivot.previousList;
+
+ w = list.getListWeights();
+ l = list.getListLinks();
+
+ if (w.length != l.length)
+ e.replyTo.say("warning: previous list mismatched.");
+
+ for (var i = 0; i < Math.max(w.length, l.length); i++)
+ result += ("`" + l[i] + "'" + w[i] + " ");
+
+ if (result.length > 250)
+ result += "\n";
+
+ result += ( "[" + word + "]" );
+
+ if (result.length > 250)
+ result += "\n";
+
+ list = pivot.nextList;
+
+ w = list.getListWeights();
+ l = list.getListLinks();
+
+ if (w.length != l.length)
+ e.replyTo.say("warning: next list mismatched.");
+
+ for (var i = 0; i < Math.max(w.length, l.length); i++)
+ result += (" `" + l[i] + "'" + w[i]);
+ }
+ else
+ {
+ result = "- [" + word + "] -";
+ }
+
+ e.replyTo.say(result);
+
+ return false;
+ };
+
+ bot.personality.addHook(/pivot (.*)/i, f);
+
+/* dp hooks end */
+
+ f = function(e)
+ {
+ print("I can hear you.");
+ e.replyTo.say(e.user.unicodeName + ", yes, I am.");
+
+ return false;
+ };
+
+ bot.personality.addHook(/are you alive(\?)?/i, f);
+
+
+ f = function(e)
+ {
+ if (!userIsOwner(e.user))
+ {
+ e.replyTo.say("nope.");
+ return;
+ }
+
+ chan = e.matchresult[1];
+
+ if (chan.charAt(0) != "#")
+ chan = "#" + chan;
+
+ e.server.sendData("join " + chan + "\n");
+
+ return false;
+ };
+
+ bot.personality.addHook(/join\s+(\S+)\.*/i, f);
+
+ f = function(e)
+ {
+ if (!userIsOwner(e.user))
+ {
+ e.channel.say("nope.");
+ return false;
+ }
+
+ chan = e.matchresult[1];
+
+ if (chan.charAt(0) != "#")
+ chan = "#" + chan;
+
+ e.server.sendData("part " + chan + "\n");
+
+ return false;
+ };
+
+ bot.personality.addHook(/part\s+(\S+)\.*/i, f);
+ bot.personality.addHook(/leave\s+(\S+)\.*/i, f);
+
+ f = function (e)
+ {
+ e.replyTo.say("mmmmmmm. Thanks " + e.user.unicodeName + ".");
+ return false;
+ };
+
+ bot.personality.addHook(/botsnack/i, f);
+
+ f = function(e)
+ {
+ e.replyTo.act("blushes");
+ return false;
+ };
+
+ bot.personality.addHook(/you rock/i, f);
+
+ f = function(e)
+ {
+ if (e.matchresult[1] == "me")
+ e.replyTo.act("hugs " + e.user.unicodeName);
+ else
+ e.replyTo.act("hugs " + e.matchresult[1]);
+ return false;
+ };
+
+ bot.personality.addHook(/hug (.*)/i, f);
+
+ f = function(e)
+ {
+ if (e.matchresult[1] == "me")
+ e.replyTo.say(e.user.unicodeName + ", :*");
+ else
+ e.replyTo.say(e.matchresult[1] + ", :*");
+ return false;
+ };
+
+ bot.personality.addHook(/kiss (\w+)/, f);
+
+ f = function (e)
+ {
+ e.replyTo.say(e.user.unicodeName + ", I'll try :(");
+ return false;
+ };
+
+ bot.personality.addHook
+ (/(shut up)|(shaddup)|(be quiet)|(keep quiet)|(sssh)|(stfu)/i, f);
+
+ f = function(e)
+ {
+ if (!userIsOwner(e.user))
+ {
+ e.replyTo.say("No.");
+ }
+ else
+ {
+ for (var n in bot.networks)
+ bot.networks[n].quit("Goodnight.");
+ }
+ return false;
+ };
+
+ bot.personality.addHook(/(go to bed)|(go to sleep)|(sleep)/i, f);
+
+ f = function(e)
+ {
+ e.replyTo.say(":)");
+ return false;
+ };
+
+ bot.personality.addHook
+ (/(smile)|(rotfl)|(lmao)|(rotflmao)|(look happy)|(you(.)?re smart)/i, f);
+/* (/(smile)|(rotfl)|(lmao)|(rotflmao)|(you(.)?re funny)|(look happy)|(you(.)?re smart)/i, f); */
+
+ f = function(e)
+ {
+ e.replyTo.say(":(");
+ return false;
+ };
+
+ bot.personality.addHook(/(frown)|(don(.)?t like you)|(look sad)/i, f);
+
+ f = function(e)
+ {
+ e.replyTo.say(">:|");
+ return false;
+ };
+
+ bot.personality.addHook(/(look mad)|(beat you up)/i, f);
+
+ f = function(e)
+ {
+ e.replyTo.say(":/");
+ return false;
+ };
+
+ bot.personality.addHook(/(look confused)|(i like windows)/i, f);
+}
+
diff --git a/comm/suite/chatzilla/js/tests/mybot.js b/comm/suite/chatzilla/js/tests/mybot.js
new file mode 100644
index 0000000000..260943b3c1
--- /dev/null
+++ b/comm/suite/chatzilla/js/tests/mybot.js
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+if (typeof escape == "undefined")
+ escape = unescape = function (s) {return s;}
+
+
+function runMyBot()
+{
+ load ("ircbot.js");
+ go();
+}
+
+function initPersonality()
+{
+ load ("mingus.js");
+ initMingus();
+}
diff --git a/comm/suite/chatzilla/js/tests/test_matchobject.js b/comm/suite/chatzilla/js/tests/test_matchobject.js
new file mode 100644
index 0000000000..c44380a366
--- /dev/null
+++ b/comm/suite/chatzilla/js/tests/test_matchobject.js
@@ -0,0 +1,41 @@
+
+function test_matchObject()
+{
+ var f = true;
+
+ obj1 = {foo:"hey", bar:"ho"}
+ obj2 = {a:"1", b:"2"}
+
+ p1 = {foo:"hey"}
+ p2 = {bar:"ho"}
+ p3 = {a:"1"}
+ p4 = {b:"2"}
+
+ /* true, single pattern, and it matches */
+ f &= matchObject (obj1, p1);
+ /* false, single pattern matches, negated */
+ f &= !matchObject (obj1, p1, true);
+
+ /* false, single pattern doesn't match */
+ f &= !matchObject (obj1, p3);
+ /* true, single pattern doesn't match, negated */
+ f &= matchObject (obj1, p3, true);
+
+ /* true, p1 matches */
+ f &= matchObject (obj1, [p1, p3]);
+ /* false, p1 matches, negated */
+ f &= !matchObject (obj1, [p1, p3], true);
+
+ /* true, both paterns match */
+ f &= matchObject (obj2, [p3, p4]);
+ /* false, both patterns match, negated */
+ f &= !matchObject (obj2, [p3, p4], true);
+
+ /* false, neither pattern matches */
+ f &= !matchObject (obj1, [p3, p4]);
+ /* true, neither pattern matches, negated */
+ f &= matchObject (obj1, [p3, p4], true);
+
+ return Boolean(f); /* you've got to find any problems by hand :) */
+
+} \ No newline at end of file
diff --git a/comm/suite/chatzilla/js/tests/toys.js b/comm/suite/chatzilla/js/tests/toys.js
new file mode 100644
index 0000000000..a9fd7f7e9c
--- /dev/null
+++ b/comm/suite/chatzilla/js/tests/toys.js
@@ -0,0 +1,35 @@
+
+function rainbow(str)
+{
+ str = String(str);
+ var c = str.length;
+ var rv = "";
+
+ for (var i = 0; i < c; i++)
+ {
+ var color = randomRange (2, 6);
+ rv += unescape ("%03" + color + str[i]);
+ }
+
+ return rv;
+
+}
+
+function fade(str)
+{
+ var colors = new Array(1, 14, 10, 15, 0);
+ var cIndex = 0;
+ var msg = "";
+ for (var i = 0; i < str.length; i++)
+ {
+ msg += "%03" + colors[cIndex] + str[i];
+ if ((++cIndex) == 5)
+ {
+ cIndex = 0;
+ }
+ }
+
+ return unescape(msg);
+
+ }
+
diff --git a/comm/suite/chatzilla/locales/Makefile.in b/comm/suite/chatzilla/locales/Makefile.in
new file mode 100644
index 0000000000..6b58d5d8b2
--- /dev/null
+++ b/comm/suite/chatzilla/locales/Makefile.in
@@ -0,0 +1,6 @@
+# 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/.
+
+LOCALE_TOPDIR=$(commtopsrcdir)
+LOCALE_RELATIVEDIR=suite/chatzilla/locales
diff --git a/comm/suite/chatzilla/locales/all-locales b/comm/suite/chatzilla/locales/all-locales
new file mode 100644
index 0000000000..e6d17c0b74
--- /dev/null
+++ b/comm/suite/chatzilla/locales/all-locales
@@ -0,0 +1,23 @@
+cs
+de
+el
+en-GB
+es-AR
+es-ES
+fi
+fr
+hu
+it
+ja
+ja-JP-mac
+ka
+nb-NO
+nl
+pl
+pt-BR
+pt-PT
+ru
+sk
+sv-SE
+zh-CN
+zh-TW
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/about.dtd b/comm/suite/chatzilla/locales/en-US/chrome/about.dtd
new file mode 100644
index 0000000000..981817cda7
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/about.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!ENTITY window.title "About ChatZilla">
+
+<!ENTITY chatzilla.label "ChatZilla">
+<!ENTITY version.unknown.label "Version unknown">
+<!ENTITY version.known.label "Version &#37;S">
+<!ENTITY description.label "A clean, easy to use and highly extensible Internet Relay Chat (IRC) client.">
+
+<!ENTITY homepage.label "Visit Home Page">
+<!ENTITY copyversion.label "Copy Version Details">
+
+<!ENTITY section.core.label "Core Development Team:">
+<!ENTITY section.locale.label "Localization:">
+<!ENTITY section.contrib.label "Contributors:">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/browserOverlay.dtd b/comm/suite/chatzilla/locales/en-US/chrome/browserOverlay.dtd
new file mode 100644
index 0000000000..5e774cc75c
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/browserOverlay.dtd
@@ -0,0 +1,5 @@
+<!-- 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/. -->
+
+<!ENTITY czButton.label "ChatZilla">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/channels.dtd b/comm/suite/chatzilla/locales/en-US/chrome/channels.dtd
new file mode 100644
index 0000000000..4cbc892041
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/channels.dtd
@@ -0,0 +1,33 @@
+<!-- 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/. -->
+
+<!ENTITY window.title "Join Channel">
+
+<!ENTITY network.label "Network:">
+<!ENTITY network.accesskey "N">
+
+<!ENTITY channel.label "Channel:">
+<!ENTITY channel.accesskey "C">
+
+<!ENTITY topics.label "Search topics as well as channel names">
+<!ENTITY topics.accesskey "t">
+
+<!ENTITY join.label "Join">
+<!ENTITY join.accesskey "J">
+
+<!ENTITY minusers.label "Min users:">
+<!ENTITY minusers.accesskey "M">
+
+<!ENTITY maxusers.label "Max users:">
+<!ENTITY maxusers.accesskey "x">
+
+<!ENTITY refreshNow.label "Refresh Now">
+<!ENTITY refreshNow.accesskey "R">
+
+<!ENTITY network.hint.label "Enter any network or server name (you can include a port) and a channel to join.">
+
+<!ENTITY col.network "Network">
+<!ENTITY col.name "Name">
+<!ENTITY col.users "Users">
+<!ENTITY col.topic "Topic">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/chatzilla.dtd b/comm/suite/chatzilla/locales/en-US/chrome/chatzilla.dtd
new file mode 100644
index 0000000000..19e74be9c3
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/chatzilla.dtd
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<!ENTITY Menubar.tooltip "Main Menu">
+<!ENTITY Toolbar.tooltip "Main Toolbar">
+
+
+<!ENTITY multiline-expand.tooltip "Switch to multi-line input (Ctrl+Up)">
+<!ENTITY multiline-contract.tooltip "Switch to single-line input (Ctrl+Down)">
+<!ENTITY multiline-send.tooltip "Send this text (Ctrl+Enter)">
+<!ENTITY server-nick.tooltip "Change nickname or set away state. To focus the input box, press Escape.">
+
+<!ENTITY Underline.label "Underline">
+<!ENTITY Bold.label "Bold">
+<!ENTITY Reverse.label "Reverse video">
+<!ENTITY Normal.label "Normal">
+<!ENTITY Color.label "Color">
+<!ENTITY ForeBack.label "xx=Fore yy=Back">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/chatzilla.properties b/comm/suite/chatzilla/locales/en-US/chrome/chatzilla.properties
new file mode 100644
index 0000000000..bfec752d34
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/chatzilla.properties
@@ -0,0 +1,1674 @@
+# 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/.
+
+locale.authors = XXX REPLACE THIS VALUE WITH A SEMICOLON-SEPARATED LIST OF NAMES FOR YOUR LOCALIZATION TEAM XXX
+
+# Misc
+
+unknown=<unknown>
+none=<none>
+na=<n/a>
+
+# util.js
+
+msg.alert = Alert
+msg.prompt = Prompt
+msg.confirm = Confirm
+
+# command.js
+
+### Notes for localizers ###
+#
+# ChatZilla uses cmd.<command name>.* to construct the command's help,
+# help usage and any UI labels.
+#
+# Note also that, for every command, an accesskey may be specified:
+# EITHER by prefixing the desired accesskey with "&" in the .label string,
+# OR by specifying a .accesskey string, which is useful if the desired
+# accesskey does not occur in the label.
+#
+# The following are therefore equivalent:
+# cmd.foo.label = &Foo
+# and
+# cmd.foo.label = Foo
+# cmd.foo.accesskey = F
+#
+#
+# All localised strings may contain certain entities for branding purposes.
+# The three standard brand entities (brandShortName, brandFullName, vendorName)
+# can all be used like this:
+# foo.bar = Some text used in &brandFullName;!
+#
+### End of notes ###
+
+cmd.about.label = About ChatZilla
+cmd.about.help = Display information about this version of ChatZilla.
+
+cmd.alias.helpUsage = [<alias-name> [<command-list>]]
+cmd.alias.help = Defines <alias-name> as an alias for the semicolon (';') delimited list of commands specified by <command-list>. If <command-list> is a minus ('-') character, the alias will be removed; if omitted, the alias will be displayed. If <alias-name> is not provided, all aliases will be listed.
+
+cmd.attach.helpUsage = <irc-url>
+cmd.attach.help = Attaches to the IRC URL specified by <irc-url>. If you are already attached, the view for <irc-url> is made current. If that view has been deleted, it is recreated. You may omit the irc:// portion of the <irc-url>. Examples are; /attach libera.chat, /attach libera.chat/firefox and /attach libera.chat/SeaMonkey,isnick.
+
+cmd.away.label = Away (default)
+# LOCALIZATION NOTE (cmd.away.format):
+# Do not localize $reason
+cmd.away.format = Away ($reason)
+cmd.away.helpUsage = [<reason>]
+cmd.away.help = If <reason> is specified, sets you away with that message. Used without <reason>, you are marked away with a default message.
+
+cmd.back.label = Back
+cmd.back.help = Marks you as no longer away.
+
+cmd.ban.label = Ban
+# LOCALIZATION NOTE (cmd.ban.format):
+# Do not localize $channelName
+cmd.ban.format = Ban from $channelName
+cmd.ban.helpUsage = [<nickname>]
+cmd.ban.help = Bans a single user, or mask of users, from the current channel. A user's nickname may be specified, or a proper host mask can be used. Used without a nickname or mask, shows the list of bans currently in effect.
+
+cmd.cancel.help = Cancels an /attach or /server command, or a file transfer. Use /cancel on a network view when ChatZilla is repeatedly trying to attach to a network that is not responding, to tell ChatZilla to give up before the normal number of retries. Use /cancel on a file transfer view to stop the transfer.
+
+cmd.charset.helpUsage = [<new-charset>]
+cmd.charset.help = Sets the character encoding mode for the current view to <new-charset>, or displays the current character encoding mode if <new-charset> is not provided.
+
+cmd.channel-motif.helpUsage = [<motif> [<channel>]]
+cmd.channel-motif.help = Sets the CSS file used for the message tab for this specific channel. <motif> can be a URL to a .css file, or the shortcut "dark" or "light". If <motif> is a minus ('-') character, the motif will revert to the network motif. If <channel> is not provided, the current channel will be assumed. See the ChatZilla homepage at <http://www.mozilla.org/projects/rt-messaging/chatzilla/> for more information on how to style ChatZilla. See also |motif|.
+
+cmd.channel-pref.helpUsage = [<pref-name> [<pref-value>]]
+cmd.channel-pref.help = Sets the value of the preference named <pref-name> to the value of <pref-value> on the current channel. If <pref-value> is not provided, the current value of <pref-name> will be displayed. If both <pref-name> and <pref-value> are omitted, all preferences will be displayed. If <pref-value> is a minus ('-') character, then the preference will revert back to its default value.
+
+cmd.clear-view.label = Cl&ear Tab
+cmd.clear-view.helpUsage = [<view>]
+cmd.clear-view.help = Clear the current view, discarding *all* content.
+cmd.clear-view.key = accel L
+
+cmd.client.help = Make the ``*client*'' view current. If the ``*client*'' view has been deleted, it will be recreated.
+
+cmd.cmd-undo.label = &Undo
+cmd.cmd-undo.key = accel Z
+cmd.cmd-redo.label = &Redo
+cmd.cmd-redo.key = accel Y
+cmd.cmd-cut.label = Cu&t
+cmd.cmd-cut.key = accel X
+cmd.cmd-copy.label = &Copy
+cmd.cmd-copy.key = accel C
+cmd.cmd-paste.label = &Paste
+cmd.cmd-paste.key = accel V
+cmd.cmd-delete.label = &Delete
+cmd.cmd-delete.key = VK_DELETE
+cmd.cmd-selectall.label = Select &All
+cmd.cmd-selectall.key = accel A
+cmd.cmd-copy-link-url.label = Copy Link Location
+
+cmd.cmd-mozilla-prefs.label = &&brandShortName; Preferences…
+cmd.cmd-prefs.label = Pr&eferences…
+cmd.cmd-chatzilla-prefs.label = ChatZilla Pr&eferences…
+cmd.cmd-chatzilla-opts.label = &Options…
+
+cmd.commands.helpUsage = [<pattern>]
+cmd.commands.help = Lists all command names matching <pattern>, or all command names if pattern is not specified.
+
+cmd.custom-away.label = Away (custom)…
+
+cmd.ctcp.helpUsage = <target> <code> [<params>]
+cmd.ctcp.help = Sends the CTCP code <code> to the target (user or channel) <target>. If <params> are specified they are sent along as well.
+
+cmd.default-charset.helpUsage = [<new-charset>]
+cmd.default-charset.help = Sets the global default character encoding mode to <new-charset>, or displays the current global default character encoding mode if <new-charset> is not provided.
+
+cmd.delayed.helpUsage = <delay> <rest>
+cmd.delayed.help = After |delay| seconds, run the command specified in |rest|.
+
+cmd.describe.helpUsage = <target> <action>
+cmd.describe.help = Performs an 'action' at the |target|, either a channel or a user.
+
+cmd.dcc-accept.helpUsage = [<nickname> [<type> [<file>]]]
+cmd.dcc-accept.help = Accepts an incoming DCC Chat or Send offer. If a |nickname| is not specified, the last offer that arrived will be accepted (for security reasons, this will not work in the first 10 seconds after an offer is received). You can also use a regular expression for either <nickname> or <file>.
+
+cmd.dcc-accept-list.help = Displays the DCC auto-accept list for the current network.
+
+cmd.dcc-accept-list-add.helpUsage = <nickname>
+cmd.dcc-accept-list-add.help = Add someone to your DCC auto-accept list for the current network.
+
+cmd.dcc-accept-list-remove.helpUsage = <nickname>
+cmd.dcc-accept-list-remove.help = Remove someone from your DCC auto-accept list for the current network.
+
+cmd.dcc-chat.helpUsage = [<nickname>]
+cmd.dcc-chat.help = Sends a DCC Chat offer to |nickname| on the current server. On a query view, |nickname| may be omitted to send the offer to the query view's user.
+cmd.dcc-chat.label = Direct Chat
+
+# LOCALIZATION NOTE (cmd.dcc-close.format):
+# Do not localize $userName
+cmd.dcc-close.format = Disconnect From $userName
+cmd.dcc-close.label = &Disconnect
+cmd.dcc-close.helpUsage = [<nickname> [<type> [<file>]]]
+cmd.dcc-close.help = Closes an existing DCC connection. |nickname| may be omitted if run from a DCC view, in which case the DCC connection for that view will be closed. |type| and |file| may be needed to identify the connection. You can also use a regular expression for either <nickname> or <file>.
+
+cmd.dcc-decline.helpUsage = [<nickname>]
+cmd.dcc-decline.help = Declines an incoming DCC Chat or Send offer. If a |nickname| is not specified, the last offer that arrived will be declined. You can also use a regular expression for <nickname>.
+
+cmd.dcc-list.helpUsage = [<type>]
+cmd.dcc-list.help = Lists the currently known about DCC offers and connections. This may be limited to just "chat" or "send" using the |type| parameter.
+
+cmd.dcc-send.helpUsage = [<nickname> [<file>]]
+cmd.dcc-send.help = Offers a file to |nickname|. On a query view, |nickname| may be omitted to send the offer to the query view's user. A file may be specified directly by passing |file| or, if omitted, selected from a browse dialog.
+cmd.dcc-send.label = Send File…
+
+cmd.dcc-show-file.helpUsage = <file>
+cmd.dcc-show-file.help = Opens the folder containing the file you downloaded.
+
+cmd.delete-view.key = accel W
+cmd.delete-view.label = &Close Tab
+cmd.delete-view.helpUsage = [<view>]
+cmd.delete-view.help = Clear the current view, discarding *all* content, and drop its icon from the tab strip. If a channel view is deleted this way, you also leave the channel.
+
+cmd.dehop.label = Remove Half-operator Status
+cmd.dehop.helpUsage = <nickname> [<...>]
+cmd.dehop.help = Removes half-operator status from <nickname> on current channel. Requires operator status.
+
+cmd.deop.label = Remove Operator Status
+cmd.deop.helpUsage = <nickname> [<...>]
+cmd.deop.help = Removes operator status from <nickname> on current channel. Requires operator status.
+
+cmd.desc.helpUsage = [<description>]
+cmd.desc.help = Changes the 'ircname' line returned when someone performs a /whois on you. You must specify this *before* connecting to the network. If you omit <description>, the current description is shown.
+
+cmd.devoice.label = Remove Voice Status
+cmd.devoice.helpUsage = <nickname> [<...>]
+cmd.devoice.help = Removes voice status from <nickname> on current channel. Requires operator (or half-operator) status.
+
+# LOCALIZATION NOTE (cmd.disconnect.format):
+# Do not localize $networkName
+cmd.disconnect.format = Disconnect From $networkName
+cmd.disconnect.label = &Disconnect
+cmd.disconnect.helpUsage = [<reason>]
+cmd.disconnect.help = Disconnects from the server represented by the active view when the command is executed providing the reason <reason> or the default reason if <reason> is not specified.
+
+cmd.disconnect-all.label = &Disconnect From All Networks
+cmd.disconnect-all.helpUsage = [<reason>]
+cmd.disconnect-all.key = accel D
+cmd.disconnect-all.help = Disconnects from all networks providing the reason <reason> or the default reason if <reason> is not specified.
+
+cmd.echo.helpUsage = <message>
+cmd.echo.help = Displays <message> in the current view, but does not send it to the server.
+
+cmd.edit-networks.label = &Networks…
+cmd.edit-networks.help = Opens the network editor, where you can edit the list of available networks and servers.
+
+cmd.enable-plugin.helpUsage = <plugin>
+cmd.enable-plugin.help = Meant to be used to re-enable a plugin after calling |disable-plugin|, this command calls the plugin's enablePlugin function. There are no guarantees that the plugin will properly enable itself.
+
+cmd.eval.helpUsage = <expression>
+cmd.eval.help = Evaluates <expression> as JavaScript code. Not for the faint of heart.
+
+cmd.evalsilent.helpUsage = <expression>
+cmd.evalsilent.help = Identical to the /eval command, except the [EVAL-IN] and [EVAL-OUT] lines are not displayed.
+
+cmd.except.helpUsage = [<nickname>]
+cmd.except.help = Excepts a user from channel bans. A user's nickname may be specified, or a proper host mask can be used. Used without a nickname or mask, shows the list of exceptions currently in effect.
+
+cmd.exit.label = E&xit ChatZilla
+cmd.exit.helpUsage = [<reason>]
+cmd.exit.help = Disconnects from all active servers and networks, providing the reason <reason>, or the default reason if <reason> is not specified. Exits ChatZilla after disconnecting.
+
+cmd.faq.label = ChatZilla FAQ
+
+cmd.find.label = &Find…
+cmd.find.key = accel F
+cmd.find-again.label = Find A&gain
+cmd.find-again.key = accel G
+
+cmd.focus-input.key = VK_ESCAPE
+
+cmd.font-family.helpUsage = [<font>]
+cmd.font-family.help = Sets or views the font family being used on the current view. Omit <font> to see the current font family. The value |default| will use your global font family, |serif|, |sans-serif| and |monospace| will use your global font settings, other values will set a font directly.
+cmd.font-family-default.label = Default &Font
+cmd.font-family-serif.label = Se&rif
+cmd.font-family-sans-serif.label = S&ans Serif
+cmd.font-family-monospace.label = Mo&nospace
+# LOCALIZATION NOTE (cmd.font-family.format):
+# Do not localize $fontFamily
+cmd.font-family-other.format = Other ($fontFamily)…
+cmd.font-family-other.label = O&ther…
+
+cmd.font-size.helpUsage = [<font-size>]
+cmd.font-size.help = Sets or views the font size being used on the current view. Omit <font-size> to see the current font size. The size value is specified in points (pt). The value |default| will use your global font size, and the values |bigger| and |smaller| increase or reduce the size by a fixed amount each time.
+cmd.font-size-bigger.label = Make Text &Bigger
+cmd.font-size-bigger.key = accel +
+cmd.font-size-bigger2.key = accel =
+cmd.font-size-smaller.label = Make Text &Smaller
+cmd.font-size-smaller.key = accel -
+cmd.font-size-default.label = Default Si&ze
+cmd.font-size-small.label = Sma&ll
+cmd.font-size-medium.label = &Medium
+cmd.font-size-large.label = Lar&ge
+# LOCALIZATION NOTE (cmd.font-size-other.format):
+# Do not localize $fontSize
+cmd.font-size-other.format = Other ($fontSize pt)…
+cmd.font-size-other.label = &Other…
+
+cmd.goto-startup.label = Open Auto-connect
+cmd.goto-startup.help = Open all of your configured auto-connect URLs.
+
+cmd.goto-url.label = Open Link
+# LOCALIZATION NOTE (cmd.goto-url.format):
+# Do not localize $label
+cmd.goto-url.format = $label
+cmd.goto-url-newwin.label = Open Link in New Window
+cmd.goto-url-newtab.label = Open Link in New Tab
+
+cmd.header.label = Header
+cmd.header.key = accel shift H
+cmd.header.help = Toggles visibility of the header bar.
+
+cmd.help.helpUsage = [<pattern>]
+cmd.help.help = Displays help on all commands matching <pattern>, if <pattern> is not given, displays help on all commands.
+
+cmd.hide-view.label = &Hide Tab
+cmd.hide-view.helpUsage = [<view>]
+cmd.hide-view.help = Drop the current view's icon from the tab strip, but save its contents. The icon will reappear the next time there is activity on the view.
+
+cmd.homepage.label = ChatZilla Homepage
+
+cmd.hop.label = Give Half-operator Status
+cmd.hop.helpUsage = <nickname> [<...>]
+cmd.hop.help = Gives half-operator status to <nickname> on current channel. Requires operator status.
+
+# LOCALIZATION NOTE (cmd.reconnect.format):
+# Do not localize $networkName
+cmd.reconnect.format = Reconnect To $networkName
+cmd.reconnect.label = &Reconnect
+cmd.reconnect.helpUsage = [<reason>]
+cmd.reconnect.help = Reconnects to the network represented by the active view when the command is executed providing the reason <reason> when disconnecting, or the default reason if <reason> is not specified.
+
+cmd.reconnect-all.label = &Reconnect To All Networks
+cmd.reconnect-all.helpUsage = [<reason>]
+cmd.reconnect-all.help = Reconnects to all networks providing the reason <reason> when disconnecting, or the default reason if <reason> is not specified.
+
+cmd.toggle-ui.helpUsage = <thing>
+cmd.toggle-ui.help = Toggles the visibility of various pieces of the user interface. <thing> must be one of: tabstrip, userlist, header, status.
+
+cmd.rtl.help = Switches text direction to Right-to-Left.
+cmd.ltr.help = Switches text direction to Left-to-Right.
+cmd.irtl.help = Switches input area direction to Right-to-Left.
+cmd.iltr.help = Switches input area direction to Left-to-Right.
+
+cmd.toggle-text-dir.label = S&witch Text Direction
+cmd.toggle-text-dir.key = accel shift X
+
+cmd.toggle-usort.label = Sort Users By Mode
+cmd.toggle-ccm.label = Collapse Co&nsecutive Messages
+cmd.toggle-copy.label = Copy &Important Messages
+cmd.toggle-umode.label = Show Mode as Symbol
+cmd.toggle-timestamps.label = Show &Timestamps
+
+cmd.unban.label = Un-ban
+# LOCALIZATION NOTE (cmd.unban.format):
+# Do not localize $channelName
+cmd.unban.format = Un-ban from $channelName
+cmd.unban.helpUsage = <nickname>
+cmd.unban.help = Removes the ban on a single user, or removes a specific ban mask from the channel's ban list.
+
+cmd.unexcept.helpUsage = <nickname>
+cmd.unexcept.help = Removes a channel ban exception.
+
+cmd.user.helpUsage = [<username> <description>]
+cmd.user.help = Sets your username to <username> and your description (``Real Name'') to <description>. Equivalent to using the |name| and |desc| command. The new name and description will be used the next time you connect to the network. You can use this command without parameters to show the current username and description.
+
+cmd.userlist.label = User List
+cmd.userlist.key = accel shift L
+cmd.userlist.help = Toggles the visibility of the user list.
+
+cmd.identify.helpUsage = [<password>]
+cmd.identify.help = Identify with nickname services on the current server. If <password> is not provided, you will be asked to enter the password in a prompt with a masked textfield (so nobody will be able to read it when you type it).
+
+cmd.ignore.helpUsage = [<mask>]
+cmd.ignore.help = Add someone to your ignore list for the current network. A nickname will suffice for <mask>, but you can also use a hostmask. With no parameters, it shows a list of all currently ignored users.
+
+cmd.install-plugin.helpUsage = [<url> [<name>]]
+cmd.install-plugin.help = Installs a ChatZilla plugin for you.
+cmd.install-plugin.label = &Install Plugin…
+
+cmd.invite.helpUsage = <nickname> [<channel-name>]
+cmd.invite.help = Invites <nickname> to <channel-name> or current channel if not supplied. Requires operator status if +i is set.
+
+cmd.j.helpUsage = [<channel-name> [<key>]]
+cmd.j.help = This command is an alias for /join.
+
+cmd.join.label = &Join Channel…
+cmd.join.key = accel J
+cmd.join.helpUsage = [<channel-name> [<key>]]
+cmd.join.help = Joins the global (name starts with #), local (name starts with &), or modeless (name starts with a +) channel named <channel-name>. If no prefix is given, # is assumed. Provides the key <key> if specified.
+
+cmd.join-charset.helpUsage = [<channel-name> <charset> [<key>]]
+cmd.join-charset.help = Joins the global (name starts with #), local (name starts with &), or modeless (name starts with a +) channel named <channel-name>. Messages will be encoded and decoded according to the character encoding specified by <charset>. The <charset> parameter is independent of the default character encoding, which can be selected with the /charset command. If no prefix is given, # is assumed. Provides the key <key> if specified.
+
+# LOCALIZATION NOTE (cmd.kick.format):
+# Do not localize $channelName
+cmd.kick.format = Kick from $channelName
+cmd.kick.label = Kick
+cmd.kick.helpUsage = <nickname> [<reason>]
+cmd.kick.help = Kicks <nickname> off the current channel. Requires operator status.
+
+# LOCALIZATION NOTE (cmd.kick-ban.format):
+# Do not localize $channelName
+cmd.kick-ban.format = Kickban from $channelName
+cmd.kick-ban.label = Kickban
+cmd.kick-ban.helpUsage = <nickname> [<reason>]
+cmd.kick-ban.help = Bans *!username@hostmask from the current channel, then kicks them off. Requires operator status.
+
+cmd.knock.helpUsage = <channel-name> [<reason>]
+cmd.knock.help = Requests an invitation from the specified channel with optional reason. This command is not supported by all servers.
+
+# LOCALIZATION NOTE (cmd.label-user.format):
+# Do not localize $nickname
+cmd.label-user.format = «$nickname»
+cmd.label-user.label = <unknown>
+
+# LOCALIZATION NOTE (cmd.label-user-multi.format):
+# Do not localize $userCount
+cmd.label-user-multi.format = «$userCount users»
+cmd.label-user-multi.label = <unknown>
+
+# LOCALIZATION NOTE (cmd.leave.format):
+# Do not localize $channelName
+cmd.leave.format = Leave $channelName
+cmd.leave.label = &Leave
+cmd.leave.helpUsage = [<channel-name>] [<reason>]
+cmd.leave.help = Leaves the current channel. Use /delete to force the view to go away, losing its contents, or /hide to temporarily hide it, preserving its contents. Many servers do not support the optional <reason> parameter. Your preferences are used to determine whether to delete the tab. If you are dispatching this command from a script, you may override this behaviour with the <delete-when-done> parameter.
+
+cmd.marker.help = Scrolls to the last read message marker. If the marker is not visible, this is equivalent to using ``marker-set''.
+cmd.marker-clear.help = Hides the last read message marker.
+cmd.marker-set.help = Moves the last read message marker to the last line of the view and scrolls to it.
+
+cmd.links.help = Displays the "links" to the current server. This is a list of the other servers in the network which are directly connected to the one you are connected to.
+
+cmd.list.helpUsage = [<channel-name>]
+cmd.list.help = Lists channel name, user count, and topic information for the network/server you are attached to. If you omit the optional channel argument, all channels will be listed. On large networks, the server may disconnect you for asking for a complete list.
+
+cmd.list-plugins.helpUsage = [<plugin>]
+cmd.list-plugins.help = If <plugin> is not provided, this command lists information on all loaded plugins. If <plugin> is provided, only its information will be displayed. If this command is dispatched from the console, you may specify <plugin> by either the plugin id, or index.
+
+cmd.load.helpUsage = <url>
+cmd.load.help = Executes the contents of the url specified by <url>. See also: The |initialScripts| pref.
+
+cmd.reload-plugin.helpUsage = <plugin>
+cmd.reload-plugin.help = Reloads the plugin from the same url it was loaded from last time. This will only work if the currently loaded version of the plugin can be disabled.
+
+cmd.log.helpUsage = [<state>]
+cmd.log.help = Turns logging on or off for the current channel. If <state> is provided and is |true|, |on|, |yes|, or |1|, logging will be turned on. Values |false|, |off|, |no| and |0| will turn logging off. Omit <state> to see the current logging state. The state will be saved in prefs, so that if logging is on when you close ChatZilla, it will resume logging the next time you join the channel.
+
+cmd.rlist.helpUsage = <regexp>
+cmd.rlist.help = Lists channel name, user count, and topic information for the network/server you are attached to, filtered by the regular expression.
+
+cmd.map.help = Similar to /links, but provides a graphical "Network Map" of the IRC network. Mainly used for routing purposes.
+
+cmd.match-users.helpUsage = <mask>
+cmd.match-users.help = Shows a list of all users whose hostmask matches <mask>.
+
+cmd.me.helpUsage = <action>
+cmd.me.help = Sends the text <action> to the channel as a statement in the third person. Try it and see!
+
+cmd.motd.help = Displays the "Message of the Day", which usually contains information about the network and current server, as well as any usage policies.
+
+cmd.mode.helpUsage = [<target>] [<modestr> [<param> [<...>]]]
+cmd.mode.help = Changes the channel or user mode of <target> using <modestr> and any subsequent <param> if added. When used from a channel view, <target> may be omitted. For a list of modes you may use, see http://irchelp.org.
+
+cmd.motif.helpUsage = [<motif>]
+cmd.motif.help = Sets the default CSS file used for the message tabs. <motif> can be a URL to a .css file, or the shortcut "dark" or "light". See the ChatZilla homepage at <http://www.mozilla.org/projects/rt-messaging/chatzilla/> for more information on how to style ChatZilla. See also |network-motif|, |channel-motif|, |user-motif|.
+
+cmd.motif-dark.label = Dar&k Motif
+cmd.motif-light.label = &Light Motif
+
+cmd.msg.helpUsage = <nickname> <message>
+cmd.msg.help = Sends the private message <message> to <nickname>.
+
+cmd.name.helpUsage = [<username>]
+cmd.name.help = Changes the username displayed before your hostmask if the server you're connecting to allows it. Some servers will only trust the username reply from the ident service. You must specify this *before* connecting to the network. If you omit <username>, the current username will be shown.
+
+cmd.names.helpUsage = [<channel-name>]
+cmd.names.help = Lists the users in a channel.
+
+cmd.network.helpUsage = <network-name>
+cmd.network.help = Sets the current network to <network-name>
+
+cmd.networks.help = Lists all available networks as clickable links.
+
+cmd.network-motif.helpUsage = [<motif> [<network>]]
+cmd.network-motif.help = Sets the CSS file used for the message tab for the network <network>. <motif> can be a URL to a .css file, or the shortcut "dark" or "light". If <motif> is a minus ('-') character, the motif will revert to the global motif. If <network> is not provided, the current network is assumed. See the ChatZilla homepage at <http://www.mozilla.org/projects/rt-messaging/chatzilla/> for more information on how to style ChatZilla. See also |motif|.
+
+cmd.network-pref.helpUsage = [<pref-name> [<pref-value>]]
+cmd.network-pref.help = Sets the value of the preference named <pref-name> to the value of <pref-value> on the current network. If <pref-value> is not provided, the current value of <pref-name> will be displayed. If both <pref-name> and <pref-value> are omitted, all preferences will be displayed. If <pref-value> is a minus ('-') character, then the preference will revert back to its default value.
+
+cmd.nick.label = Change nickname…
+cmd.nick.helpUsage = [<nickname>]
+cmd.nick.help = Changes your nickname. If |nickname| is omited, a prompt is shown.
+
+cmd.notify.helpUsage = [<nickname> [<...>]]
+cmd.notify.help = With no parameters, /notify shows you the online/offline status of all the users on your notify list. If one or more <nickname> parameters are supplied, the nickname(s) will be added to your notify list if they are not yet on it, or removed from it if they are.
+
+cmd.notice.helpUsage = <nickname> <message>
+cmd.notice.help = Sends the notice <message> to <nickname>.
+
+cmd.op.label = Give Operator Status
+cmd.op.helpUsage = <nickname> [<...>]
+cmd.op.help = Gives operator status to <nickname> on current channel. Requires operator status.
+
+cmd.open-at-startup.helpUsage = [<toggle>]
+cmd.open-at-startup.help = Used to add the current view to the list of views that will be automatically opened at startup. If <toggle> is not provided, the status of the current view will be displayed. <toggle> can be one of: yes, on, true, 1, no, off, false, 0, or toggle, to toggle the current state.
+
+cmd.oper.helpUsage = <opername> [<password>]
+cmd.oper.help = Requests IRC Operator status from the current server. If <password> is not provided, you will be asked to enter the password in a prompt with a masked textfield (so nobody will be able to read it when you type it).
+
+cmd.print.label = &Print…
+cmd.print.key = accel P
+cmd.print.help = Opens the print dialog for the current view.
+
+cmd.save.label = Save View &As…
+cmd.save.key = accel S
+cmd.save.helpUsage = [<filename> [<savetype>]]
+cmd.save.help = Save the current view as file <filename>. If <filename> is omitted, a Save As… dialog will be shown. <savetype> can be either |complete|, |htmlonly| or |text|. If it is omitted, it is deduced from the file extension. Files with the extension .html, .xhtml, .xhtm or .htm will be saved as complete views, .txt files as text files. Any other extensions will throw an error if <savetype> is not provided.
+
+cmd.say.helpUsage = <message>
+cmd.say.help = Sends a message to the current view. This command is used automatically by ChatZilla when you type text that does not begin with the "/" character.
+
+cmd.stats.helpUsage = [<params>]
+cmd.stats.help = Request server statistics. Use this command with no parameters to get a server-specific list of available parameters for use with this command.
+
+cmd.time.helpUsage = [<nickname>]
+cmd.time.help = Asks <nickname> what time it is on their machine. Their IRC client may or may not show them that you've asked for this information. ChatZilla currently does not. If you do not specify <nickname>, ChatZilla will ask the server for the time it is on the server.
+cmd.time.label = Get Local Time
+
+cmd.timestamps.helpUsage = [<toggle>]
+cmd.timestamps.help = Sets the visibility of timestamps in the current view. If <toggle> is provided and is |true|, |on|, |yes|, or |1|, timestamps will be turned on. Values |false|, |off|, |no| and |0| will turn timestamps off, and |toggle| will toggle the state. Omit <toggle> to see the current state.
+
+# LOCALIZATION NOTE (cmd.toggle-oas.format):
+# Do not localize $viewType
+cmd.toggle-oas.format = Open This $viewType at Startup
+cmd.toggle-oas.label = Open at &Startup
+
+cmd.pass.helpUsage = <password>
+cmd.pass.help = Sends a password to the server for use when connecting to password-protected servers.
+
+cmd.ping.helpUsage = <nickname>
+cmd.ping.help = Ping takes its name from the technique of measuring distance with sonar. In IRC, it is used to measure the time it takes to send a message to someone, and receive a response. Specify a channel to ping everyone in that channel. Some IRC clients will display ping requests to the user. ChatZilla does not.
+cmd.ping.label = Ping User
+
+cmd.plugin-pref.helpUsage = <plugin> [<pref-name> [<pref-value>]]
+cmd.plugin-pref.help = Sets the value of the plugin's preference named <pref-name> to the value of <pref-value>. If <pref-value> is not provided, the current value of <pref-name> will be displayed. If both <pref-name> and <pref-value> are omitted, all preferences for <plugin> will be displayed. If <pref-value> is a minus ('-') character, then the preference will revert back to its default value.
+
+cmd.pref.helpUsage = [<pref-name> [<pref-value>]]
+cmd.pref.help = Sets the value of the preference named <pref-name> to the value of <pref-value>. If <pref-value> is not provided, the current value of <pref-name> will be displayed. If both <pref-name> and <pref-value> are omitted, all preferences will be displayed. If <pref-value> is a minus ('-') character, then the preference will revert back to its default value.
+
+cmd.query.label = Open Private Chat
+cmd.query.helpUsage = <nickname> [<message>]
+cmd.query.help = Opens a one-on-one chat with <nickname>. If <message> is supplied, it is sent as the initial private message to <nickname>.
+
+cmd.quit.label = &Quit ChatZilla
+cmd.quit.helpUsage = [<reason>]
+cmd.quit.help = Quit ChatZilla.
+
+cmd.quote.helpUsage = <irc-command>
+cmd.quote.help = Sends a raw command to the IRC server, not a good idea if you don't know what you're doing. see IRC RFC1459 <http://www.irchelp.org/irchelp/rfc1459.html> for complete details.
+
+cmd.rejoin.helpUsage = [<reason>]
+cmd.rejoin.help = Rejoins the channel displayed in the current view. Only works from a channel view.
+# LOCALIZATION NOTE (cmd.rejoin.format):
+# Do not localize $channelName
+cmd.rejoin.format = Rejoin $channelName
+cmd.rejoin.label = Rejoin
+
+cmd.rename.helpUsage = [<label>]
+cmd.rename.help = Change the label of the current tab to <label>.
+cmd.rename.label = Rename Tab…
+
+cmd.server.helpUsage = <hostname> [<port> [<password>]]
+cmd.server.help = Connects to server <hostname> on <port>, or 6667 if <port> is not specified. Provides the password <password> if specified. If you are already connected, the view for <hostname> is made current. If that view has been deleted, it is recreated.
+
+cmd.sslserver.helpUsage = <hostname> [<port> [<password>]]
+cmd.sslserver.help = Connects to server using SSL <hostname> on <port>, or 6697 if <port> is not specified. Provides the password <password> if specified. If you are already connected, the view for <hostname> is made current. If that view has been deleted, it is recreated.
+
+cmd.squery.helpUsage = <service> [<commands>]
+cmd.squery.help = Sends the commands <commands> to the service <service>.
+
+cmd.stalk.helpUsage = [<text>]
+cmd.stalk.help = Add <text> to list of words for which you would like to see alerts. Whenever a person with a nickname matching <text> speaks, or someone says a phrase containing <text>, your ChatZilla window will become active (on some operating systems) and its taskbar icon will flash (on some operating systems.) If <text> is omitted the list of stalk words is displayed.
+
+cmd.status.help = Shows status information for the current view.
+
+cmd.statusbar.label = Status Bar
+cmd.statusbar.key = accel shift S
+cmd.statusbar.help = Toggles the visibility of the status bar.
+
+cmd.supports.help = Lists the capabilities of the current server, as reported by the 005 numeric.
+
+cmd.testdisplay.help = Displays a sample text. Used to preview styles.
+
+cmd.topic.helpUsage = [<new-topic>]
+cmd.topic.help = If <new-topic> is specified and you are a chanop, or the channel is not in 'private topic' mode (+t), the topic will be changed to <new-topic>. If <new-topic> is *not* specified, the current topic will be displayed.
+
+cmd.tabstrip.label = Tab Strip
+cmd.tabstrip.key = accel shift S
+cmd.tabstrip.help = Toggles the visibility of the channel tab strip.
+
+cmd.unalias.helpUsage = <alias-name>
+cmd.unalias.help = Removes the named alias.
+
+cmd.unignore.helpUsage = <mask>
+cmd.unignore.help = Removes someone from your ignore list for the current network. A nickname will suffice for <mask>, but you can also use a hostmask.
+
+cmd.uninstall-plugin.helpUsage = <plugin>
+cmd.uninstall-plugin.help = Uninstalls a ChatZilla plugin for you.
+
+cmd.unstalk.helpUsage = <text>
+cmd.unstalk.help = Remove word from list of terms for which you would like to see alerts.
+
+cmd.urls.helpUsage = [<number>]
+cmd.urls.help = Displays the last few URLs seen by ChatZilla. Specify <number> to change how many it displays, or omit to display the default 10.
+
+cmd.userhost.helpUsage = <nickname> [<...>]
+cmd.userhost.help = Requests the hostmask of every <nickname> given.
+
+cmd.userip.helpUsage = <nickname> [<...>]
+cmd.userip.help = Requests the IP-address of every <nickname> given.
+
+cmd.disable-plugin.helpUsage = <plugin>
+cmd.disable-plugin.help = This command calls the plugin's disablePlugin function, if it exists. There are no guarantees that the plugin will properly disable itself.
+
+cmd.usermode.helpUsage = [<new-mode>]
+cmd.usermode.help = Changes or displays the current user mode.
+
+cmd.user-motif.helpUsage = [<motif> [<user>]]
+cmd.user-motif.help = Sets the CSS file used for the message tab for the user <user>. <motif> can be a URL to a .css file, or the shortcut "dark" or "light". If <motif> is a minus ('-') character, the motif will revert to the network motif. If <user> is not provided, the current user is assumed. See the ChatZilla homepage at <http://www.mozilla.org/projects/rt-messaging/chatzilla/> for more information on how to style ChatZilla. See also |motif|.
+
+cmd.user-pref.helpUsage = [<pref-name> [<pref-value>]]
+cmd.user-pref.help = Sets the value of the preference named <pref-name> to the value of <pref-value> on the current user. If <pref-value> is not provided, the current value of <pref-name> will be displayed. If both <pref-name> and <pref-value> are omitted, all preferences will be displayed. If <pref-value> is a minus ('-') character, then the preference will revert back to its default value.
+
+cmd.websearch.help = Runs a web search for the currently-selected text.
+cmd.websearch.helpUsage = <selected-text>
+# LOCALIZATION NOTE (cmd.websearch.format):
+# Do not localize $selectedText
+cmd.websearch.format = Search the web for "$selectedText""
+cmd.websearch.label = Search the web
+
+cmd.version.label = Get Version Information
+cmd.version.helpUsage = [<nickname>]
+cmd.version.help = Asks <nickname> what irc client they're running. Their IRC client may or may not show them that you've asked for this information. ChatZilla currently does not. If you do not specify <nickname>, ChatZilla will ask the server for the version of the IRCserver software it is running.
+
+cmd.voice.label = Give Voice Status
+cmd.voice.helpUsage = <nickname> [<...>]
+cmd.voice.help = Gives voice status to <nickname> on current channel. Requires operator (or half-operator) status.
+
+cmd.who.helpUsage = <rest>
+cmd.who.help = List users who have name, host, or description information matching <rest>.
+
+cmd.whois.label = Who is
+cmd.whois.helpUsage = <nickname> [<...>]
+cmd.whois.help = Displays information about the user <nickname>, including 'real name', server connected to, idle time, and signon time. Note that some servers will lie about the idle time. The correct idle time can usually be obtained by using |wii| instead of |whois|.
+
+cmd.wii.helpUsage = <nickname> [<...>]
+cmd.wii.help = Displays the same information as |whois|, but asks the server to include the user's real idle time.
+
+cmd.whowas.label = Who was
+cmd.whowas.helpUsage = <nickname> [<limit>]
+cmd.whowas.help = Displays the last known information about the user <nickname>, including 'real name', for a user that has left the server.
+
+## dispatch-related error messages ##
+msg.err.internal.dispatch = Internal error dispatching command ``%1$S''.
+msg.err.internal.hook = Internal error processing hook ``%1$S''.
+msg.err.invalid.param = Invalid value for parameter %1$S (%2$S).
+msg.err.disabled = Sorry, ``%1$S'' is currently disabled.
+msg.err.notimplemented = Sorry, ``%1$S'' has not been implemented.
+msg.err.required.param = Missing required parameter %1$S.
+msg.err.ambigcommand = Ambiguous command, ``%1$S'', %2$S commands match [%3$S].
+msg.err.required.nr.param = Missing %1$S parameters. This alias requires at least %2$S parameters.
+msg.err.max.dispatch.depth = Reached max dispatch depth while attempting to dispatch ``%1$S''.
+
+## ChatZilla error messages ##
+msg.err.invalid.regex = Invalid Regular Expression. For help with regular expressions, see http://en.wikipedia.org/wiki/Regular_expression#Syntax.
+msg.err.invalid.pref = Invalid value for preference %1$S (%2$S).
+msg.err.invalid.file = Invalid file <%1$S> renamed to <%2$S>.
+msg.err.failure = Operation Failed: %1$S.
+msg.err.scriptload = Error loading subscript from <%1$S>.
+msg.err.pluginapi.noid = Plugin <%1$S> does not have an id.
+msg.err.pluginapi.faultyid = Plugin <%1$S> does not have a valid id. Plugin ids may only contain alphanumeric characters, underscores (_) and dashes (-).
+msg.err.pluginapi.noenable = Plugin <%1$S> does not have an enable() method.
+msg.err.pluginapi.nodisable = Plugin <%1$S> does not have a disable() method.
+msg.err.invalid.scheme = Invalid scheme in url <%1$S>.
+msg.err.item.not.found = Startup script item <%1$S> does not exist or is inaccessible.
+msg.err.unknown.pref = The preference ``%1$S'' is not known to ChatZilla.
+msg.err.unknown.network = The network ``%S'' is not known to ChatZilla.
+msg.err.unknown.channel = The channel ``%S'' is not known to ChatZilla.
+msg.err.unknown.user = The user ``%S'' is not known to ChatZilla.
+msg.err.unknown.command = The command ``%S'' is not known to ChatZilla.
+msg.err.unknown.stalk = Not stalking %S.
+msg.err.unknown.motif = The motif ``%S'' is not known to ChatZilla.
+msg.err.invalid.charset = Invalid character encoding mode ``%S''.
+msg.err.improper.view = ``%S'' cannot be used from this view.
+msg.err.not.connected = Not connected.
+msg.err.last.view = Cannot delete last view.
+msg.err.last.view.hide = Cannot hide last view.
+msg.err.bad.ircurl = Invalid IRC URL ``%S''.
+msg.err.need.network = Command ``%1$S'' must be run in the context of a network.
+msg.err.need.server = Command ``%1$S'' must be run in the context of an attached server.
+msg.err.need.channel = Command ``%1$S'' must be run in the context of a channel.
+msg.err.need.user = Command ``%1$S'' must be run in the context of a user.
+msg.err.need.recip = Command ``%1$S'' must be run in the context of a user or a channel.
+msg.err.no.default = Please do not just type into this tab, use an actual command instead.
+msg.err.no.match = No match for ``%S''.
+msg.err.no.socket = Error creating socket.
+msg.err.no.secure = The network ``%S'' has no secure servers defined.
+msg.err.cancelled = Connection process canceled.
+msg.err.offline = &brandShortName; is in ``offline mode''. No network connections can be made in this mode.
+msg.err.badalias = Malformed alias: %S"
+msg.err.no.ctcp.cmd = %S is not a valid CTCP function for this client
+msg.err.no.ctcp.help = %S does not have any help information
+msg.err.unable.to.print = The current view does not support printing.
+msg.err.unsupported.command = The server does not support the ``%S'' command.
+msg.err.invalid.mode = The mode string you entered (``%S'') is invalid. A valid mode string consists of one or more sequences of a + or - followed by one or more alphabetical characters.
+msg.err.away.save = Saving the list of away messages failed (%S).
+msg.err.inputhistory.not.writable = Unable to save input history to ``%S''.
+msg.err.urls.not.writable = Unable to save URL log to ``%S''.
+msg.err.invalid.url = ``%S'' is not a valid url nor an alias for a url, and therefore could not be loaded.
+msg.err.no.channel = When running the ``%S'' command, you should either provide a channel name, or run the command in the context of a channel.
+msg.err.no.idleservice = ChatZilla can't determine when you're away in your version of &brandShortName;. The auto-away feature will now be disabled.
+
+msg.warn.pac.loading = The automatic proxy configuration file has not loaded yet; ChatZilla will retry shortly.
+
+# Ask for nick pass if not explicitly given in the command:
+msg.need.identify.password = Please enter the Nickname Services password for this nickname.
+
+# Ask for oper pass if not explicitly given in the command:
+msg.need.oper.password = Please enter a password for obtaining IRC Operator privileges.
+
+# Better IRC error messages
+msg.irc.381 = You are now an IRC Operator.
+msg.irc.401 = The nickname ``%S'' does not exist.
+msg.irc.402 = The server ``%S'' does not exist.
+msg.irc.403 = The channel ``%S'' does not exist.
+msg.irc.421 = The command ``%S'' is not known to the server.
+msg.irc.464 = Incorrect password, please try again with the correct password.
+msg.irc.464.login = Please specify your password using the "/pass" command to continue connecting.
+msg.irc.471 = This channel has reached its set capacity; you cannot join it.
+msg.irc.473 = This channel is invite-only. You must have an invite from an existing member of the channel to join.
+msg.irc.474 = You are banned from this channel.
+msg.irc.475 = This channel needs a key. You must provide the correct key to join the channel. See "/help join" for details on joining a channel with a key.
+msg.irc.476 = You provided a channel mask which the server considers to be invalid.
+msg.irc.477 = This channel requires that you have registered and identified yourself with the network's nickname registration services (e.g. NickServ). Please see the documentation of this network's nickname registration services that should be found in the MOTD (/motd to display it).
+msg.irc.491 = Only few of mere mortals may try to enter the twilight zone (your host did not match any configured 'O-lines').
+
+# This is an extended version that is only used if the server support /knock.
+msg.irc.471.knock = %S You might be able to use "/knock %S" to ask the channel operators to invite you in. [[Knock][Asks the channel operators to let you in][%S]]
+msg.irc.473.knock = %S Use "/knock %S" to ask the channel operators to invite you in. [[Knock][Asks the channel operators to let you in][%S]]
+msg.irc.475.knock = %S You might be able to use "/knock %S" to ask the channel operators to invite you in. [[Knock][Asks the channel operators to let you in][%S]]
+
+msg.val.on = on
+msg.val.off = off
+
+msg.plugin.enabled = Plugin ``%S'' is now enabled.
+msg.plugin.disabled = Plugin ``%S'' is now disabled.
+msg.plugin.uninstalled = Plugin ``%S'' has been uninstalled.
+
+msg.leave.inputbox = There is nothing to tab-complete. Use F6 to cycle through the user list, input box and the chat output.
+
+## formatting ##
+msg.fmt.usage = "%1$S %2$S"
+msg.fmt.jsexception = "%1$S: %2$S @ <%3$S> %4$S"
+# 1: error number, 2: error text, 3: file name, 4: line number, 5: function name
+# 1: pref name 2: value
+msg.fmt.pref = Preference ``%1$S'' is ``%2$S''.
+msg.fmt.netpref = Network preference ``%1$S'' is ``%2$S''.
+msg.fmt.chanpref = Channel preference ``%1$S'' is ``%2$S''.
+msg.fmt.userpref = User preference ``%1$S'' is ``%2$S''.
+msg.fmt.pluginpref = Plugin preference ``%1$S'' is ``%2$S''.
+
+msg.fmt.plugin1 = Plugin at index %S, loaded from <%S>.
+msg.fmt.plugin2 = id: %S, version: %S, enabled: %S, status: %S.
+msg.fmt.plugin3 = Description: %S.
+
+msg.fmt.usercount = "%S, %S@, %S%%, %S+"
+msg.fmt.alias = "%S = %S"
+msg.fmt.seconds = "%S seconds
+
+msg.fmt.matchlist = "%S matches for ``%S'': [%S]
+msg.fmt.ctcpreply = CTCP %S reply ``%S'' from %S"
+msg.fmt.ctcprecv = CTCP %S request (%S) from %S"
+msg.fmt.chanlist = "%S %S %S"
+msg.fmt.logged.on = "%S is logged in as %S"
+
+# 1: local short date/time, 2: nick info
+msg.fmt.status = "%S %S"
+
+msg.unknown = <unknown>
+msg.none = <none>
+msg.na = <n/a>
+
+msg.always = always
+msg.and = and
+msg.primary = primary
+msg.secondary = secondary
+msg.you = you
+msg.network = Network
+msg.server = Server
+msg.channel = Channel
+msg.user = User
+msg.client = Client
+msg.view = View
+msg.tab = Tab
+msg.loading = Loading
+msg.error = Error
+msg.here = here
+msg.gone = gone
+msg.connecting = Connecting
+msg.connected = Connected
+msg.disconnected = Disconnected
+
+msg.days = "%S days
+msg.hours = "%S hours
+msg.minutes = "%S minutes
+msg.seconds = "%S seconds
+msg.day = 1 day
+msg.hour = 1 hour
+msg.minute = 1 minute
+msg.second = 1 second
+
+
+msg.rsp.hello = [HELLO]
+msg.rsp.help = [HELP]
+msg.rsp.usage = [USAGE]
+msg.rsp.error = [ERROR]
+msg.rsp.warn = [WARNING]
+msg.rsp.info = [INFO]
+msg.rsp.evin = [EVAL-IN]
+msg.rsp.evout = [EVAL-OUT]
+msg.rsp.disconnect = [QUIT]
+
+# For these menu labels, too, an accesskey may be specified using a .accesskey
+# string, or by prefixing the desired letter with "&" in the label.
+# The accesskey string should have the form: msg.mnu.<menuname>.accesskey
+msg.mnu.chatzilla = &ChatZilla
+msg.mnu.irc = &IRC
+msg.mnu.edit = &Edit
+msg.mnu.help = &Help
+msg.mnu.view = &View
+msg.mnu.views = &Views
+msg.mnu.motifs = Co&lor Scheme
+msg.mnu.opcommands = &Operator Commands
+msg.mnu.usercommands = &User Commands
+msg.mnu.fonts = &Font Family and Size
+
+msg.client.name = *client*
+msg.cant.disable = Unable to disable plugin %S.
+msg.cant.enable = Unable to enable plugin %S.
+msg.is.disabled = Plugin %S is already disabled.
+msg.is.enabled = Plugin %S is already enabled.
+msg.no.help = Help not available.
+msg.no.cmdmatch = No commands match ``%1$S''.
+msg.no.plugins = There are no plugins loaded.
+msg.cmdmatch = Commands matching ``%1$S'' are [%2$S].
+msg.default.alias.help = This command is an alias for |%1$S|.
+msg.extra.params = Extra parameters ``%1$S'' ignored.
+msg.version.reply = ChatZilla %S [%S]
+msg.source.reply = http://chatzilla.hacksrus.com/
+msg.nothing.to.cancel = No connection or /list in progress, nothing to cancel.
+msg.cancelling = Cancelling connection to ``%S''…
+msg.cancelling.list = Cancelling /list request…
+msg.current.charset = Using ``%S'' as default character encoding.
+msg.current.charset.view = Using ``%S'' as character encoding for this view.
+msg.current.css = Using <%S> as default motif.
+msg.current.css.net = Using <%S> as default motif for this network.
+msg.current.css.chan = Using <%S> as motif for this channel.
+msg.current.css.user = Using <%S> as motif for this user.
+msg.no.dynamic.style = Sorry, but your version of &brandShortName; doesn't support styling the entire application with a motif. This functionality will now be disabled.
+msg.subscript.loaded = Subscript <%1$S> loaded with result ``%2$S''.
+msg.user.info = Default nickname, ``%S'', username ``%S'', and description ``%S''.
+msg.connection.info = "%S: User %S connected via %S:%S (%S server).
+msg.server.info = "%S: Connected for %S, last ping: %S, server roundtrip (lag): %S seconds.
+msg.connect.via = Connected via %S"
+msg.user.mode = User mode for %S is now %S"
+msg.not.connected = "%S: Not connected.
+msg.insecure.server = Your connection to the server ``%S'' is not secure.
+msg.secure.connection = Signed by %S"
+msg.security.info = Displays security information about the current connection
+msg.going.offline = &brandShortName; is trying to go into offline mode. This will disconnect you from ALL the networks and channels you're connected to.
+msg.really.go.offline = Go Offline
+msg.dont.go.offline = Don't Go Offline
+msg.offlinestate.offline = You are offline. Click the icon to go online.
+msg.offlinestate.online = You are online. Click the icon to go offline.
+msg.member = Member
+msg.operator = Operator member
+msg.voiced = Voiced member
+msg.voiceop = Operator and voiced member
+msg.no.mode = no mode
+msg.topic.info = "%S, %S: Topic, ``%S''
+msg.notopic.info = "%S, %S: No topic.
+msg.channel.info = "%S: %S of %S (%S) <%S>
+msg.channel.details = "%S/%S: %S users total, %S operators, %S voiced.
+msg.nonmember = "%S: No longer a member of %S.
+msg.end.status = End of status.
+msg.networks.heada = Available networks are [
+msg.networks.headb2 = ]. [[Edit][Edit the list of available networks.][%S]]
+msg.messages.cleared = Messages Cleared.
+msg.match.unchecked = (%S users were not checked)
+msg.matching.nicks = The following users matched your query: %S. %S
+msg.no.matching.nicks = There were no users matching your query. %S
+msg.commands.header = Type /help <command-name> for information about a specific command.
+msg.matching.commands = Currently implemented commands matching the pattern ``%S'' are [%S].\nType /help <command-name> for information about a specific command.
+msg.all.commands = Currently implemented commands are [%S].
+msg.help.intro = Help is available from many places:\n - |/commands| lists all the built-in commands in ChatZilla. Use |/help <command-name>| to get help on individual commands.\n - The IRC Help website <http://www.irchelp.org/> provides introductory material for new IRC users. \n - The ChatZilla website <http://chatzilla.hacksrus.com/> provides more information about IRC and ChatZilla, including the ChatZilla FAQ <http://chatzilla.hacksrus.com/faq>, which answers many common questions about using ChatZilla.
+msg.about.version = "%S [[Details][Opens the about dialog for more details][%S]]
+msg.about.homepage = Please visit the ChatZilla homepage at <http://chatzilla.hacksrus.com/> for more information.
+msg.newnick.you = YOU are now known as %S
+msg.newnick.notyou = "%S is now known as %S
+msg.view.hidden = "%S (hidden)
+
+msg.sts.upgrade = "Upgrade policy in effect, switching to secure port %S."
+msg.sts.upgrade.new = "A new security policy is in effect for this network. ChatZilla will automatically switch to TLS the next time you connect. [[Reconnect now][Reconnect to %S][%S]]"
+
+msg.localeurl.homepage = http://chatzilla.hacksrus.com/
+msg.localeurl.faq = http://chatzilla.hacksrus.com/faq/
+
+msg.no.notify.list = Your notify list is empty.
+msg.notify.addone = "%S has been added to your notify list.
+msg.notify.addsome = "%S have been added to your notify list.
+msg.notify.delone = "%S has been removed from your notify list.
+msg.notify.delsome = "%S have been removed from your notify list.
+msg.notify.list = You are watching %S.
+msg.notify.full = Your notify list is full.
+
+msg.not.an.alias = No such alias: %S.
+msg.alias.removed = Removed alias: %S.
+msg.alias.created = Created alias: %S = %S.
+msg.no.aliases = No aliases are defined.
+
+msg.no.stalk.list = No stalking victims.
+msg.stalk.list = Currently stalking [%S].
+msg.stalk.add = Now stalking %S.
+msg.stalk.del = No longer stalking %S.
+msg.stalking.already = Already stalking %S.
+
+msg.status = Status
+msg.title.net.on = User %S on ``%S'' (%S:%S)
+msg.title.net.off = User %S, not connected to network ``%S''
+msg.title.nonick = <unregistered-user>
+msg.title.no.topic = No Topic
+msg.title.no.mode = No Mode
+msg.title.channel = "%S on %S (%S): %S"
+msg.title.user = Conversation with %S %S"
+msg.title.dccchat = DCC Conversation with %S"
+msg.title.dccfile.send = "%S%% of ``%S'' sent to %S"
+msg.title.dccfile.get = "%S%% of ``%S'' received from %S"
+msg.title.unknown = ChatZilla!
+msg.title.activity = "%S -- Activity [%S]
+
+msg.output.url = URL
+msg.output.knownnets = Known Networks
+msg.output.connnets = Connected Networks
+msg.output.notconn = Not Connected
+msg.output.lag = Lag
+msg.output.mode = Mode
+msg.output.users = Users
+msg.output.topic = Topic
+msg.output.via = Connected via
+msg.output.to = Connected to
+msg.output.file = File
+msg.output.progress = Progress
+msg.output.cancel = Cancel
+
+msg.logging.off = Logging is off.
+msg.logging.on = Logging is on. Log output is going to file <%S>.
+msg.logfile.closed = Logfile closed.
+msg.logfile.error = Unable to open file <%S>. Logging disabled.
+msg.logfile.opened = Now logging to <%S>.
+msg.logfile.closing = Closing log file <%S>.
+msg.logfile.write.error = Unable to write to file <%S>. Logging disabled.
+msg.logging.icon.off = Logging is off. Click the icon to start logging this view.
+msg.logging.icon.on = Logging is on. Click the icon to stop logging this view.
+
+msg.alert.icon.off = Message notifications are off. Click the icon to start showing notifications for new messages.
+msg.alert.icon.on = Message notifications are on. Click the icon to stop showing notifications for new messages.
+
+msg.already.connected = You are already connected to ``%S''.
+msg.enter.nick = Please select a nickname
+msg.network.connecting = Attempting to connect to ``%S''. Use /cancel to abort.
+
+msg.jumpto.button = [[%1$S][Jump to this message in %1$S][%2$S]]
+msg.jumpto.err.nochan = ``%S'' is no longer open.
+msg.jumpto.err.noanchor = The anchor cannot be found.
+
+msg.banlist.item = "%S banned %S from %S on %S.
+msg.banlist.button = [[Remove][Remove this ban][%S]]
+msg.banlist.end = End of %S ban list.
+msg.exceptlist.item = "%S excepted %S from bans in %S on %S.
+msg.exceptlist.button = [[Remove][Remove this ban exception][%S]]
+msg.exceptlist.end = End of %S exception list.
+
+msg.batch.netsplit.start = Netsplit (%S %S)
+msg.batch.netsplit.end = End of netsplit.
+msg.batch.netjoin.start = Net reconnect (%S %S)
+msg.batch.netjoin.end = End of net reconnect.
+msg.batch.chathistory.start = Chat history for %S
+msg.batch.chathistory.end = End of chat history.
+msg.batch.unknown.start = Batch %S (%S)
+msg.batch.unknown.end = End of batch.
+
+msg.channel.needops = You need to be an operator in %S to do that.
+
+msg.ctcphelp.clientinfo = CLIENTINFO gives information on available CTCP commands
+msg.ctcphelp.action = ACTION performs an action at the user
+msg.ctcphelp.time = TIME gives the local date and time for the client
+msg.ctcphelp.version = VERSION returns the client's version
+msg.ctcphelp.source = SOURCE returns an address where you can obtain the client
+msg.ctcphelp.os = OS returns the client's host's operating system and version
+msg.ctcphelp.host = HOST returns the client's host application name and version
+msg.ctcphelp.ping = PING echos the parameter passed to the client
+msg.ctcphelp.dcc = DCC requests a direct client connection
+
+# DCC CHAT messages.
+msg.dccchat.sent.request = Sent DCC Chat offer to ``%S'' from YOU (%S:%S) %S.
+msg.dccchat.got.request = Got DCC Chat offer from ``%S'' (%S:%S) %S.
+msg.dccchat.accepting = Auto-accepting DCC Chat offer from ``%S'' (%S:%S) in %S seconds %S.
+msg.dccchat.accepting.now = Auto-accepting DCC Chat offer from ``%S'' (%S:%S).
+msg.dccchat.accepted = Accepted DCC Chat with ``%S'' (%S:%S).
+msg.dccchat.declined = Declined DCC Chat with ``%S'' (%S:%S).
+msg.dccchat.aborted = Aborted DCC Chat with ``%S'' (%S:%S).
+msg.dccchat.failed = Failed DCC Chat with ``%S'' (%S:%S).
+msg.dccchat.opened = DCC Chat with ``%S'' (%S:%S) connected.
+msg.dccchat.closed = DCC Chat with ``%S'' (%S:%S) disconnected.
+
+# DCC FILE messages.
+msg.dccfile.sent.request = Sent DCC File Transfer offer to ``%S'' from YOU (%S:%S) of ``%S'' (%S) %S.
+msg.dccfile.got.request = Got DCC File Transfer offer from ``%S'' (%S:%S) of ``%S'' (%S) %S.
+msg.dccfile.accepting = Auto-accepting DCC File Transfer offer from ``%S'' (%S:%S) of ``%S'' (%S) in %S seconds %S.
+msg.dccfile.accepting.now = Auto-accepting DCC File Transfer offer from ``%S'' (%S:%S) of ``%S'' (%S).
+# 1 = file, 2 = to/from, 3 = nick, 4 = IP, 5 = port.
+msg.dccfile.accepted = Accepted DCC File Transfer of ``%S'' %S ``%S'' (%S:%S).
+msg.dccfile.declined = Declined DCC File Transfer of ``%S'' %S ``%S'' (%S:%S).
+msg.dccfile.aborted = Aborted DCC File Transfer of ``%S'' %S ``%S'' (%S:%S).
+msg.dccfile.failed = Failed DCC File Transfer of ``%S'' %S ``%S'' (%S:%S).
+msg.dccfile.opened = DCC File Transfer of ``%S'' %S ``%S'' (%S:%S) started.
+msg.dccfile.closed.sent = DCC File Transfer of ``%S'' %S ``%S'' (%S:%S) finished.
+# 6 = path, 7 = command for opening the folder
+msg.dccfile.closed.saved = DCC File Transfer of ``%S'' %S ``%S'' (%S:%S) finished. File saved to ``%S''. [[Open Containing Folder][Open the folder containing the downloaded file][%S]]
+msg.dccfile.closed.saved.mac = DCC File Transfer of ``%S'' %S ``%S'' (%S:%S) finished. File saved to ``%S''. [[Show In Finder][Show the folder containing the file in Finder][%S]]
+
+# 1 = percent, 2 = current pos, 3 = total size, 4 = speed.
+msg.dccfile.progress = %S%% complete, %S of %S, %S.
+msg.dccfile.send = Pick file to send
+msg.dccfile.save.to = Save incoming file (%S)
+msg.dccfile.err.notfound = The file specified could not be found.
+msg.dccfile.err.notafile = The path specified is not a normal file.
+msg.dccfile.err.notreadable = The file specified cannot be read.
+
+# General DCC messages.
+msg.dcc.pending.matches = "%S pending incoming DCC offers matched.
+msg.dcc.accepted.matches = "%S DCC connections matched.
+msg.dcc.matches.help = You must specify enough of the user's nickname to uniquely identify the request, or include the request type and even the filename if necessary.
+
+msg.dcc.not.enabled = DCC is disabled. If you need DCC functionality, you may turn it on from the Preferences window.
+msg.dcc.err.nouser = Must specify |nickname| or run the command from a query view.
+msg.dcc.err.accept.time = You cannot use the short form of |/dcc-accept| within the first 10 seconds of receiving a DCC request.
+msg.dcc.err.notdcc = Must specify |nickname| or run the command from a DCC view.
+
+# /dcc-list words and phrases.
+msg.dcclist.dir.in = incoming
+msg.dcclist.dir.out = outgoing (offer)
+msg.dcclist.to = to
+msg.dcclist.from = from
+## Params: index, state, direction (incoming/outgoing), DCC type, direction (to/from), user (ip:port), commands.
+msg.dcclist.line = %S: %S %S DCC %S %S %S (%S:%S) %S
+## Params: waiting, running, done.
+msg.dcclist.summary = DCC sessions: %S pending, %S connected, %S failed.
+
+msg.dccaccept.disabled = Currently not auto-accepting DCC on this network.
+msg.dccaccept.list = Currently auto-accepting DCC on this network from [%S].
+msg.dccaccept.add = Now auto-accepting DCC on this network from %S.
+msg.dccaccept.del = No longer auto-accepting DCC on this network from %S.
+msg.dccaccept.adderr = You are already auto-accepting DCC on this network from %S.
+msg.dccaccept.delerr = %S not found on your DCC auto-accept list for this network.
+
+msg.dcc.command.accept = [[Accept][Accept this DCC offer][%S]]
+msg.dcc.command.decline = [[Decline][Decline (refuse) this DCC offer][%S]]
+msg.dcc.command.cancel = [[Cancel][Cancels this DCC offer][%S]]
+msg.dcc.command.close = [[Close][Close (disconnect) this DCC offer][%S]]
+
+# DCC state names.
+msg.dcc.state.abort = Aborted
+msg.dcc.state.request = Requested
+msg.dcc.state.accept = Accepted
+msg.dcc.state.connect = Connected
+# 1 = percent, 2 = current pos, 3 = total size, 4 = speed.
+msg.dcc.state.connectPro = Connected (%S%% complete, %S of %S, %S)
+msg.dcc.state.disconnect = Done
+msg.dcc.state.decline = Declined
+msg.dcc.state.fail = Failed
+
+# SI general format (1$ == number, 2$ == scale suffix).
+msg.si.size = %1$S %2$S
+msg.si.speed = %1$S %2$S
+
+# SI suffixes for sizes.
+msg.si.size.0 = B
+msg.si.size.1 = KiB
+msg.si.size.2 = MiB
+msg.si.size.3 = GiB
+msg.si.size.4 = TiB
+msg.si.size.5 = PiB
+msg.si.size.6 = EiB
+
+# SI suffixes for speeds.
+msg.si.speed.0 = B/s
+msg.si.speed.1 = KiB/s
+msg.si.speed.2 = MiB/s
+msg.si.speed.3 = GiB/s
+msg.si.speed.4 = TiB/s
+msg.si.speed.5 = PiB/s
+msg.si.speed.6 = EiB/s
+
+msg.ident.error = Error enabling Ident Server: %S"
+
+msg.host.password = Enter a password for the server %S:
+msg.sasl.password = Enter a password for SASL authentication with username %S:
+msg.url.key = Enter key for url %S:
+
+msg.startup.added = <%1$S> will now open at startup.
+msg.startup.removed = <%1$S> will no longer open at startup.
+msg.startup.exists = <%1$S> is currently opened at startup.
+msg.startup.notfound = <%1$S> is not currently opened at startup.
+
+msg.collapse.button = [[%S][%S][toggle-group %S]]
+msg.collapse.test = Sample collapsible message group.
+msg.collapse.show = Show
+msg.collapse.hide = Hide
+msg.collapse.showtitle = Show message group
+msg.collapse.hidetitle = Hide message group
+
+msg.test.hello = Sample HELLO message, <http://testurl.com/foo.html>.
+msg.test.info = Sample INFO message, <http://testurl.com/foo.html>.
+msg.test.error = Sample ERROR message, <http://testurl.com/foo.html>.
+msg.test.help = Sample HELP message, <http://testurl.com/foo.html>.
+msg.test.usage = Sample USAGE message, <http://testurl.com/foo.html>.
+msg.test.status = Sample STATUS message, <http://testurl.com/foo.html>.
+msg.test.privmsg = Normal message from %S to %S, <http://testurl.com/foo.html>.
+msg.test.action = Action message from %S to %S, <http://testurl.com/foo.html>.
+msg.test.notice = Notice message from %S to %S, <http://testurl.com/foo.html>.
+msg.test.url = Sample URL <http://www.mozilla.org> message.
+msg.test.styles = Sample text styles *bold*, _underline_, /italic/, |teletype| message.
+msg.test.emoticon = Sample emoticon :) :( :~( :0 :/ :P :| (* message.
+msg.test.rheet = Sample Rheeeeeeeeeet! message.
+msg.test.topic = Sample Topic message, <http://testurl.com/foo.html>.
+msg.test.join = Sample Join message, <http://testurl.com/foo.html>.
+msg.test.part = Sample Part message, <http://testurl.com/foo.html>.
+msg.test.kick = Sample Kick message, <http://testurl.com/foo.html>.
+msg.test.quit = Sample Quit message, <http://testurl.com/foo.html>.
+msg.test.stalk = "%S : Sample /stalk match, <http://testurl.com/foo.html>.
+msg.test.ctlchr = Sample control char >%01<\\1 -- >%05<\\5 -- >%10<\\10
+msg.test.color = Sample color %033c%034o%034l%033o%033r%034%20%036t%036e%032s%034t%0f message.
+msg.test.quote = Sample ``double quote'' message.
+
+msg.welcome = Welcome to ChatZilla…\nBelow is a short selection of information to help you get started using ChatZilla.
+msg.welcome.url = Because ChatZilla was launched from a URL, the target has been opened for you. You can find it on the tab bar, next to this view.
+msg.tabdnd.drop = Would you like to use the file ``%S'' as your new motif?
+msg.default.status = Welcome to ChatZilla!
+
+msg.closing = Disconnecting from IRC. Click close again to exit now.
+msg.confirm.quit = You are still connected to some networks, are you sure you want to quit ChatZilla?\nConfirming will close the window, and disconnect from all the networks and channels you're connected to.
+msg.quit.anyway = &Quit Anyway
+msg.dont.quit = &Don't Quit
+msg.warn.on.exit = Warn me when quitting while still connected
+
+msg.login.confirm = Do you want to save the password for ``%S''?
+msg.login.prompt = Prompt to save passwords
+msg.login.save = Yes
+msg.login.dont = No
+msg.login.added = Saved password for ``%S''.
+msg.login.updated = Changed password for ``%S''.
+msg.login.err.unknown.type = Unknown login type ``%S''.
+
+msg.whois.name = "%S <%S@%S> ``%S''
+msg.whois.channels = "%S: member of %S"
+msg.whois.server = "%S: attached to %S ``%S''
+msg.whois.idle = "%S: idle for %S (on since %S)
+msg.whois.away = "%S: away with message ``%S''
+msg.whois.end = End of WHOIS information for %S.
+
+msg.ignore.list.1 = Currently not ignoring anyone.
+msg.ignore.list.2 = Currently ignoring [%S].
+msg.ignore.add = You are now ignoring %S.
+msg.ignore.adderr = You are already ignoring %S.
+msg.ignore.del = You are no longer ignoring %S.
+msg.ignore.delerr = "%S not found in your ignore list.
+
+msg.you.invite = You have invited %S to %S.
+msg.invite.you = "%S (%S@%S) has invited you to [[%S][Accept invitation to channel %S][goto-url %S]].
+msg.invite.someone = "%S has invited %S to %S".
+
+msg.nick.in.use = The nickname ``%S'' is already in use, use the /nick command to pick a new one.
+msg.retry.nick = The nickname ``%S'' is already in use, trying ``%S''.
+msg.nick.prompt = Enter a nickname to use:
+
+msg.tab.name.prompt = Enter a label for this tab:
+
+msg.list.rerouted = List reply will appear on the ``%S'' view.
+msg.list.end = Displayed %S of %S channels.
+msg.list.chancount = This server has %S channels. Listing them all will probably take a long time, and may lead to ChatZilla becoming unresponsive or being disconnected by the server. [[List Channels][List all channels][%S]]
+
+msg.who.end = End of WHO results for ``%S'', %S user(s) found.
+msg.who.match = User %S, (%S@%S) ``%S'' (%S), member of %S, is connected to <irc://%S/>, %S hop(s).
+
+msg.connection.attempt = Connecting to %S (%S)… [[Cancel][Cancel connecting to %S][%S]]
+msg.connection.refused = Connection to %S (%S) refused. [[Help][Get more information about this error online][faq connection.refused]]
+msg.connection.abort.offline = The connection to %S (%S) was aborted because you went into offline mode.
+msg.connection.abort.unknown = The connection to %S (%S) was aborted with error %S.
+msg.connection.timeout = Connection to %S (%S) timed out. [[Help][Get more information about this error online][faq connection.timeout]]
+msg.unknown.host = Unknown host ``%S'' connecting to %S (%S). [[Help][Get more information about this error online][faq connection.unknown.host]]
+msg.invalid.cert = "%S has an invalid security certificate. If you trust this server, [[add an exception][Opens the dialog to add a security certificate exception][%S]].
+msg.connection.closed = Connection to %S (%S) closed. [[Help][Get more information about this error online][faq connection.closed]]
+msg.connection.reset = Connection to %S (%S) reset. [[Help][Get more information about this error online][faq connection.reset]]
+msg.connection.interrupt = Connection to %S (%S) was interrupted.
+msg.connection.quit = Disconnected from %S (%S). [[Reconnect][Reconnect to %S][%S]]
+msg.close.status = Connection to %S (%S) closed with status %S.
+
+msg.proxy.connection.refused = The proxy server you configured is refusing the connection.
+msg.unknown.proxy.host = Unknown proxy host connecting to %S (%S).
+
+# In these messages, the first replacement string is a connection error from above.
+msg.connection.exhausted = "%S Connection attempts exhausted, giving up.
+msg.reconnecting.in = "%S Reconnecting in %S. [[Cancel][Cancel reconnecting to %S][%S]]
+msg.reconnecting.in.left = "%S %S attempts left, reconnecting in %S. [[Cancel][Cancel reconnecting to %S][%S]]
+msg.reconnecting.in.left1 = "%S 1 attempt left, reconnecting in %S. [[Cancel][Cancel reconnecting to %S][%S]]
+
+msg.reconnecting = Reconnecting…
+msg.confirm.disconnect.all = Are you sure you want to disconnect from ALL networks?
+msg.no.connected.nets = You are not connected to any networks.
+msg.no.reconnectable.nets = There are no networks to reconnect to.
+
+msg.ping.reply = Ping reply from %S in %S.
+msg.ping.reply.invalid = Malformed ping reply from %S.
+msg.prefix.response = "%S, your result is,
+
+msg.topic.changed = "%S has changed the topic to ``%S''
+msg.topic = Topic for %S is ``%S''
+msg.no.topic = No topic for channel %S"
+msg.topic.date = Topic for %S was set by %S on %S"
+
+msg.you.joined = YOU (%S) have joined %S"
+msg.someone.joined = "%S (%S@%S) has joined %S"
+msg.you.left = YOU (%S) have left %S"
+msg.you.left.reason = YOU (%S) have left %S (%S)
+msg.someone.left = "%S has left %S"
+msg.someone.left.reason = "%S has left %S (%S)
+msg.youre.gone = YOU (%S) have been booted from %S by %S (%S)
+msg.someone.gone = "%S was booted from %S by %S (%S)
+
+msg.mode.all = Mode for %S is %S"
+msg.mode.changed = Mode %S by %S"
+
+msg.away.on = You are now marked as away (%S). Click the nickname button or use the |/back| command to return from being away.
+msg.idle.away.on = You have automatically been marked as away (%S) after %S minutes of inactivity.
+msg.away.off = You are no longer marked as away.
+msg.away.prompt = Enter an away message to use:
+msg.away.default = I'm not here right now.
+msg.away.idle.default = I'm not here right now.
+
+msg.you.quit = YOU (%S) have left %S (%S)
+msg.someone.quit = "%S has left %S (%S)
+
+msg.unknown.ctcp = Unknown CTCP %S (%S) from %S"
+
+msg.fonts.family.fmt = Font family is ``%S''
+msg.fonts.family.pick = Enter the font family you wish to use:
+msg.fonts.size.fmt = Font size is %Spt
+msg.fonts.size.default = Font size is default
+msg.fonts.size.pick = Enter the font size you wish to use. Note: Non-numeric inputs will result in the default size.
+
+msg.supports.chanTypes = Supported channel types: %S"
+msg.supports.chanModesA = Supported channel modes (A: lists): %S"
+msg.supports.chanModesB = Supported channel modes (B: param): %S"
+msg.supports.chanModesC = Supported channel modes (C: on-param): %S"
+msg.supports.chanModesD = Supported channel modes (D: boolean): %S"
+msg.supports.userMode = "%S (%S)
+msg.supports.userModes = Supported channel user modes: %S"
+msg.supports.flagsOn = This server DOES support: %S"
+msg.supports.flagsOff = This server DOESN'T support: %S"
+msg.supports.miscOption = "%S=%S"
+msg.supports.miscOptions = Server settings/limits: %S"
+msg.supports.caps = Supported capabilities: %S"
+msg.supports.capsOn = Enabled capabilities: %S"
+
+msg.caps.on = Capability %S enabled.
+msg.caps.off = Capability %S disabled.
+msg.caps.error = Capability %S is invalid.
+
+msg.conf.mode.on = Conference Mode has been enabled for this view; joins, leaves, quits and nickname changes will be hidden.
+msg.conf.mode.stayon = Conference Mode is enabled for this view; joins, leaves, quits and nickname changes are hidden.
+msg.conf.mode.off = Conference Mode has been disabled for this view; joins, leaves, quits and nickname changes will be shown.
+
+# Join Network/Channel dialog
+msg.cd.updated = Network's channel list cached on %S"
+msg.cd.updated.format = %e %B %Y
+msg.cd.updated.never = Network's channel list not cached
+msg.cd.create = <create new channel>
+msg.cd.filtering = Filtered %S of %S channels…
+msg.cd.showing = Showing %S of %S channels.
+msg.cd.wait.list = Waiting for current list operation to finish…
+msg.cd.fetching = Fetching channel list…
+msg.cd.fetched = Fetched %S channels…
+msg.cd.error.list = There was an error loading the channel list.
+msg.cd.loaded = Loaded %S channels…
+
+
+msg.urls.none = There are no stored URLs.
+msg.urls.header = Listing the %S most recent stored URLs (most recent first):
+msg.urls.item = URL %S: %S"
+
+msg.save.completeview = View, Complete
+msg.save.htmlonlyview = View, HTML Only
+msg.save.plaintextview = View, Plain Text
+msg.save.files.folder = %S_files
+msg.save.dialogtitle = Save View ``%S'' As…
+msg.save.err.no.ext = You must specify either a normal extension or <savetype>. Nothing was saved.
+msg.save.err.invalid.path = The path ``%S'' is not a valid path or URL to save to. Only local file paths and file:/// urls are accepted.
+msg.save.err.invalid.ext = The extension ``%S'' cannot be used without supplying a <savetype>. Use either |.xhtml|, |.xhtm|, |.html|, |.htm| or |.txt| as a file extension, or supply <savetype>.
+msg.save.err.invalid.savetype = The savetype ``%S'' is not a valid type of file to save to. Use either |complete|, |htmlonly| or |text|.
+msg.save.err.failed = Saving the view ``%1$S'' to ``%2$S'' failed:\n ``%3$S''
+msg.save.fileexists = The file ``%S'' already exists.\n Click OK to overwrite it, click Cancel to keep the original file.
+msg.save.successful = The view ``%1$S'' has been saved to <%2$S>.
+
+# Plugin installation
+msg.install.plugin.err.download = An error occurred downloading the plugin: %S"
+msg.install.plugin.err.remove.temp = An error occurred removing the temporary files: %S"
+msg.install.plugin.err.no.name = Unable to pick a plugin name from the source, please specify one instead.
+msg.install.plugin.err.protocol = Sorry, the source location has been specified with an unknown protocol. Only 'file', 'http' and 'https' are supported.
+msg.install.plugin.err.install.to = Unable to find a suitable install location (initialScripts). Please fix the initialScripts preference, for example by resetting it, using the command: |/pref initialScripts - |. Careful, this will remove any plugin you installed elsewhere from this list!
+msg.install.plugin.err.check.sd = An error occurred checking the source and destination: %S"
+msg.install.plugin.err.many.initjs = This ChatZilla plugin appears to have multiple 'init.js' files and thus cannot be installed.
+msg.install.plugin.err.mixed.base = This ChatZilla plugin has a base path for 'init.js' which is not used for all other files. This plugin will probably not function in this state.
+msg.install.plugin.err.already.inst = This ChatZilla plugin appears to already be installed.
+msg.install.plugin.err.extract = An error occurred extracting the compressed source: %S"
+msg.install.plugin.err.installing = An error occurred installing the source: %S"
+msg.install.plugin.err.format = The source specified is not a format understood by the plugin installer.
+msg.install.plugin.err.removing = An error occurred loading or enabling the plugin. Removing the plugin.
+msg.install.plugin.err.spec.name = The plugin name must be specified!
+
+msg.install.plugin.select.source = Select a script to install…
+
+msg.install.plugin.warn.name = Changed plugin name for install from '%S' to '%S' to match source code.
+msg.install.plugin.downloading = Downloading plugin from '%S'…
+msg.install.plugin.installing = Installing from '%S' to '%S'…
+msg.install.plugin.done = Done. ChatZilla plugin '%S' installed!
+
+# Munger
+munger.mailto=Mailto
+munger.link=URLs
+munger.channel-link=IRC channel
+munger.bugzilla-link=Bugzilla link
+munger.face=Face
+munger.ear=Ear
+munger.quote=Double Quotes
+munger.rheet=Rheet
+munger.bold=Bold
+munger.italic=Italic
+munger.talkback-link=Talkback link
+munger.teletype=Teletype
+munger.underline=Underline
+munger.ctrl-char=Control Chars
+
+
+# Date/Time representations for strftime
+
+datetime.day.long = Sunday^Monday^Tuesday^Wednesday^Thursday^Friday^Saturday
+datetime.day.short = Sun^Mon^Tue^Wed^Thu^Fri^Sat
+datetime.month.long = January^February^March^April^May^June^July^August^September^October^November^December
+datetime.month.short = Jan^Feb^Mar^Apr^May^Jun^Jul^Aug^Sep^Oct^Nov^Dec
+
+datetime.uam = AM
+datetime.lam = am
+datetime.upm = PM
+datetime.lpm = pm
+
+datetime.presets.lc = %Y-%m-%d %H:%M:%S
+datetime.presets.lr = %I:%M:%S %p
+datetime.presets.lx = %Y-%m-%d
+datetime.presets.ux = %H:%M:%S
+
+
+# Messages used in config.js, part of the pref window.
+
+# We only allow one pref window open at once, this occurs when a 2nd is opened.
+msg.prefs.alreadyOpen = ChatZilla's preferences are already open; you may not open a second copy.
+
+msg.prefs.err.save = An exception occurred trying to save the preferences: %S.
+
+msg.prefs.browse = Browse…
+msg.prefs.browse.title = ChatZilla Browse
+msg.prefs.move.up = Move up
+msg.prefs.move.down = Move down
+msg.prefs.add = Add…
+msg.prefs.edit = Edit
+msg.prefs.delete = Delete
+
+msg.prefs.list.add = Enter item to add:
+msg.prefs.list.edit = Edit the item as needed:
+msg.prefs.list.delete = Are you sure you want to remove the item ``%S''?
+
+msg.prefs.object.delete = Are you sure you want to remove the object ``%S'' and all its preferences?
+msg.prefs.object.reset = Are you sure you want to reset all the preferences for ``%S'' to their defaults?
+
+# First is for adding prefix/suffix to the overall header, and the next three
+# are for the different objects (first is network name, second is channel/user
+# name).
+msg.prefs.fmt.header = "%S"
+msg.prefs.fmt.display.network = Network %S"
+msg.prefs.fmt.display.channel = Network %S, channel %S"
+msg.prefs.fmt.display.user = Network %S, user %S"
+
+# Name for "global" object.
+msg.prefs.global = Global Settings
+
+# Localized names for all the prefs and tooltip "help" messages.
+# NOTE: "Bugzilla", "ChatZilla" and "mIRC" are product names.
+pref.activityFlashDelay.label = Activity flash delay
+pref.activityFlashDelay.help = When a tab that has had activity gets more activity, the tab is flashed. This preference is the length of the flash in milliseconds: 0 disables it.
+pref.alert.globalEnabled.label = Globally enabled
+pref.alert.globalEnabled.help = When enabled, all alerts configured may be shown. When disabled, no alerts will be shown. Provides nothing more than a global toggle.
+pref.alert.enabled.label = Enabled
+pref.alert.enabled.help = When enabled, popups are shown for this view.
+pref.alert.nonFocusedOnly.label = Only when window not active
+pref.alert.nonFocusedOnly.help = When enabled, all message notifications are suppressed when the window is active. Otherwise, message notifications for non-active views will be shown. Unchecking is suggested for channel moderators or for low traffic channels.
+pref.alert.channel.event.label = Alert for Channel Event
+pref.alert.channel.event.help = Shows message notifications for joins, parts, kicks, usermodes, and any other system messages. Suggested for channel moderators or for low traffic channels.
+pref.alert.channel.chat.label = Alert for Channel Chat
+pref.alert.channel.chat.help = Show message notifications for normal chat messages. It may be annoying for high traffic channels. Suggested for moderators or for low traffic channels.
+pref.alert.channel.stalk.label = Alert for Channel Stalk
+pref.alert.channel.stalk.help = Shows message notifications for messages containing stalk words.
+pref.alert.user.chat.label = Alert for User Chat
+pref.alert.user.chat.help = Shows message notifications for private messages.
+pref.aliases.label = Command aliases
+pref.aliases.help = Allows you to make shortcuts to various commands or sequences of commands. Each item is of the form "<name> = <command-list>". The command-list is a list of commands (without the leading "/") along with their parameters, each separated by ";". The name of the alias will automatically be turned into a command when ChatZilla starts.
+pref.autoAwayCap.label = Auto away-check user limit
+pref.autoAwayCap.help = ChatZilla automatically checks which users are here and which are away in each channel you are a member of, however, this causes significant lag on larger channels. Any channel bigger than this limit won't be checked.
+pref.autoAwayPeriod.label = Auto away-check period length
+pref.autoAwayPeriod.help = ChatZilla automatically checks which users are here and which are away in each channel you are a member of. This specifies how many minutes should pass between checks. Checking is automatically disabled if the server supports the "away-notify" extension.
+pref.autoMarker.label = Automatically display the last read message marker
+pref.autoMarker.help = ChatZilla can automatically mark the last read message on views you are not looking at, or if the window is in the background. If set to false the line marker can still be set manually.
+pref.autoRejoin.label = Rejoin when kicked
+pref.autoRejoin.help = If this is turned on, ChatZilla will try (only once) to rejoin a channel you got kicked from. Note, some channels dislike auto-rejoin, and will ban you, so be careful.
+pref.away.label = Away status
+pref.away.help =
+pref.awayIdleTime.label = Auto-away timeout
+pref.awayIdleTime.help = After how many minutes of inactivity ChatZilla will set your status to "away". Set to 0 to disable it.
+pref.awayIdleMsg.label = Auto-away message
+pref.awayIdleMsg.help = The away message ChatZilla will use when you go away.
+pref.awayNick.label = Nickname (away)
+pref.awayNick.help = This nickname will automatically be used when you mark yourself away, if different from 'Nickname'. You may leave this blank to not change nickname when going away.
+pref.bugKeyword.label = Bug Keywords
+pref.bugKeyword.help = You can define multiple issue tracker keywords as a regular expression perhaps by separating them with "|" e.g. bug|issue|case|ticket
+pref.bugURL.label = Bugzilla URL
+pref.bugURL.help = The URL used for links to bugs. "%s" is replaced with the bug number or alias. The text "bug " followed by a number or "#" and a 1-20 letter word (bug alias) will get turned into a link using this URL.
+pref.bugURL.comment.label = Bugzilla URL for Comments
+pref.bugURL.comment.help = The URL or suffix used for links to specific comments within bugs. With a full URL, "%1$s" is replaced with the bug number or alias and "%2$s" with the comment number, respectively. With a suffix, "%s" is replaced with the comment number. The text "bug " followed by a number or "#" and a 1-20 letter word (bug alias) followed by " comment " followed by another number will get turned into a link using this URL or suffix.
+pref.charset.label = Character encoding
+pref.charset.help = For multiple clients to correctly read messages with non-ASCII characters on IRC, they need to use the same character encoding.
+pref.collapseMsgs.label = Collapse messages
+pref.collapseMsgs.help = Makes multiple messages from one person only show their nickname against the first, which can look cleaner than having the nickname repeated.
+pref.collapseActions.label = Collapse actions when collapsing messages
+pref.collapseActions.help = Makes multiple actions from one person only show their nickname against the first, which can look cleaner than having the nickname repeated.
+pref.conference.limit.label = Conference mode limit
+pref.conference.limit.help = When the number of users in a channel sufficiently exceeds this limit, ChatZilla switches the channel into "conference mode", during which JOIN, PART, QUIT and NICK messages for other users are hidden. When the user count drops sufficiently below the limit, normal operation is resumed automatically. Setting this to 0 will never use conference mode, likewise setting this to 1 will always use it.
+pref.connectTries.label = Connection attempts
+pref.connectTries.help = The number of times ChatZilla attempts to connect to a server or network. Set to -1 for unlimited attempts.
+pref.copyMessages.label = Copy important messages
+pref.copyMessages.help = Any message marked as "important" will be copied to the network view. It allows you to quickly see messages that were addressed to you when you were away from the computer.
+pref.dcc.enabled.label = DCC Enabled
+pref.dcc.enabled.help = When disabled, no DCC-related commands will do anything, and all DCC requests from other users will be ignored.
+pref.dcc.autoAccept.list.label = Auto-accept list
+pref.dcc.autoAccept.list.help = List of nicknames to automatically accept DCC chat/file offers from. Hostmasks are also accepted, using "*" as a wildcard. If this list is empty, all DCC requests must be manually accepted or declined.
+pref.dcc.downloadsFolder.label = Downloads folder
+pref.dcc.downloadsFolder.help = Specifies the default destination for files received via DCC.
+pref.dcc.listenPorts.label = Listen Ports
+pref.dcc.listenPorts.help = List of ports that other users can connect to remotely. Each item may be a single port number, or a range specified as "lower-upper". Leave empty to use a random, OS-picked port. Each time you offer a DCC connection to someone, the next port listed is picked.
+pref.dcc.useServerIP.label = Get local IP from server
+pref.dcc.useServerIP.help = When turned on, ChatZilla will ask the server for your IP address when connecting. This allows DCC to obtain the correct IP address when operating behind a gateway or NAT-based system.
+pref.debugMode.label = Debug mode
+pref.debugMode.help = This preference is for debugging ChatZilla and can generate a lot of debug output (usually to the console). It is a list of letters, signifying what you want debug messages about. "c" for context menus (dumps data when opening a context menu), "d" for dispatch (dumps data when dispatching commands), and "t" for trace/hook (dumps data about hooks and the event queue processing) debugging.
+pref.defaultQuitMsg.label = Default quit message
+pref.defaultQuitMsg.help = Specifies a default quit message to use when one is not explicitly specified. Leave blank to use the basic ChatZilla one, which simply states what version you have.
+pref.desc.label = Description
+pref.desc.help = Sets the "description" (aka "real name") field shown in your /whois information. It is commonly used to include one's real name, but you are not required to enter anything.
+pref.deleteOnPart.label = Delete channel views on part
+pref.deleteOnPart.help = Causes /leave and /part to also close the channel view.
+pref.displayHeader.label = Show header
+pref.displayHeader.help = Display the chat header on this view. This contains information like the URL of the current view, and the topic and modes for a channel view.
+pref.font.family.label = Font Family
+pref.font.family.help = Selects the font in which ChatZilla will display messages. The value "default" will use your global font family; "serif", "sans-serif" and "monospace" will use your global font settings; other values will be treated as font names.
+pref.font.size.label = Font Size (pt)
+pref.font.size.help = Selects the integer font size you want ChatZilla to display messages with. The value 0 will use your global font size, and other integer values will be interpreted as the size in points (pt).
+pref.guessCommands.label = Guess unknown commands
+pref.guessCommands.help = If you enter a command (starts with "/") that ChatZilla doesn't understand, then it can try "guessing" by sending the command to the server. You can turn this off if you don't want ChatZilla to try this.
+pref.hasPrefs.label = Object has prefs
+pref.hasPrefs.help = Indicates the object has preferences saved. Never shown in preferences window. :)
+pref.identd.enabled.label = Enable Identification Server during connection process
+pref.identd.enabled.help = Allows ChatZilla to connect to servers that require an ident response.
+pref.initialURLs.label = Locations
+pref.initialURLs.help = A list of locations (irc: and ircs: URLs) to which ChatZilla should connect when starting. These will not be processed if ChatZilla was started by clicking on a hyperlink.
+pref.initialScripts.label = Script files
+pref.initialScripts.help = A list of script files (file: URLs) for ChatZilla to load when it starts. URLs may be relative to the profile directory. If a URL points to a directory, "init.js" from that directory and each subdirectory is loaded.
+pref.inputSpellcheck.label = Spellcheck the inputbox
+pref.inputSpellcheck.help = Whether or not the inputbox will be spellchecked. Only works on recent &brandShortName; builds.
+pref.log.label = Log this view
+pref.log.help = Makes ChatZilla log this view. The log file is usually stored in your profile, which can be overridden with "Profile path" (for the base path) or "Log file name" for a specific view's log.
+pref.logFileName.label = Log file name
+pref.logFileName.help = The log file used for this view. If the view is currently open and logging, changing this option won't take effect until the next time it starts logging.
+pref.logFile.client.label = Log file for client
+pref.logFile.client.help = Specifies the name of the log file for the client view. This is appended to the 'log folder' to create a full path.
+pref.logFile.network.label = Log file for networks
+pref.logFile.network.help = Specifies the name of the log file for network views. This is appended to the 'log folder' to create a full path.
+pref.logFile.channel.label = Log file for channels
+pref.logFile.channel.help = Specifies the name of the log file for channel views. This is appended to the 'log folder' to create a full path.
+pref.logFile.user.label = Log file for users
+pref.logFile.user.help = Specifies the name of the log file for user/query views. This is appended to the 'log folder' to create a full path.
+pref.logFile.dccuser.label = Log file for DCC
+pref.logFile.dccuser.help = Specifies the name of the log file for DCC chat/file views. This is appended to the 'log folder' to create a full path.
+pref.logFolder.label = Log folder
+pref.logFolder.help = Specifies the base location for all logs. The various "Log file for" preferences specify the exact names for the different types of log file.
+pref.login.promptToSave.label = Prompt to save passwords
+pref.login.promptToSave.help = Enable this preference if you wish to save passwords with the password manager. Passwords that can be saved include server passwords, channel keys, nickname identification passwords and oper passwords. Passwords can only be added via this prompt.
+pref.motif.dark.label = Dark motif
+pref.motif.dark.help = The dark motif selectable from the View > Color Scheme menu.
+pref.motif.light.label = Light motif
+pref.motif.light.help = The light motif selectable from the View > Color Scheme menu.
+pref.motif.current.label = Current motif
+pref.motif.current.help = The currently selected motif file. A Motif is a CSS file that describes how do display the chat view, and can be used to customize the display.
+pref.multiline.label = Multiline input mode
+pref.multiline.help = Sets whether ChatZilla is using the multiline input box or the single-line one.
+pref.munger.bold.label = Bold
+pref.munger.bold.help = Makes ChatZilla display text between astersks (e.g. *bold*) in an actually bold face.
+pref.munger.bugzilla-link.label = Bugzilla links
+pref.munger.bugzilla-link.help = Makes ChatZilla hyperlink "bug <number>" to the specified bug, using the "Bugzilla URL" as the base link.
+pref.munger.channel-link.label = Channel links
+pref.munger.channel-link.help = Makes ChatZilla convert "#channel" into a link to the channel.
+pref.munger.colorCodes.label = mIRC colors
+pref.munger.colorCodes.help = Enables the display of colors on the chat text, as well as other mIRC codes (bold and underline). When disabled, ChatZilla will simply hide mIRC codes.
+pref.munger.ctrl-char.label = Control characters
+pref.munger.ctrl-char.help = Makes ChatZilla display control characters it doesn't understand.
+pref.munger.face.label = Faces (emoticons)
+pref.munger.face.help = Makes ChatZilla display images for common smilies, such as :-) and ;-).
+pref.munger.italic.label = Italic
+pref.munger.italic.help = Makes ChatZilla italicize text between forward slashes. (e.g. /italic/)
+pref.munger.link.label = Web links
+pref.munger.link.help = Makes ChatZilla hyperlink text that looks like a URL.
+pref.munger.mailto.label = Mail links
+pref.munger.mailto.help = Makes ChatZilla hyperlink text that looks like an e-mail address.
+pref.munger.quote.label = Neater quotes
+pref.munger.quote.help = Makes ChatZilla replace `` with \u201C and '' with \u201D.
+pref.munger.rheet.label = Rheet
+pref.munger.rheet.help = Makes ChatZilla hyperlink "rheet": a very Mozilla.org-centric feature.
+pref.munger.talkback-link.label = Talkback links
+pref.munger.talkback-link.help = Makes ChatZilla hyperlink "TB<numbers><character>" to the specified talkback stack trace.
+pref.munger.teletype.label = Teletype
+pref.munger.teletype.help = Makes ChatZilla display |teletype| actually in teletype (a fixed-width font).
+pref.munger.underline.label = Underline
+pref.munger.underline.help = Makes ChatZilla underline text between underscores. (e.g. _underline_)
+pref.munger.word-hyphenator.label = Hyphenate long words
+pref.munger.word-hyphenator.help = Makes ChatZilla insert "hyphenation points" into long words and URLs so they can wrap to the screen size.
+pref.newTabLimit.label = Max auto-created views
+pref.newTabLimit.help = Sets the number of views (such as query views) that may be created automatically by ChatZilla. Once the limit is reached, private messages will show up on the current view instead. Set this to 0 for unlimited or 1 to disallow all auto-created views.
+pref.nickCompleteStr.label = Nickname completion string
+pref.nickCompleteStr.help = This string is appended to a nickname when tab-completed at the start of a line.
+pref.nickname.label = Nickname
+pref.nickname.help = This is the name seen by everyone else when on IRC. You can use anything you like, but it can't contain particularly "weird" characters, so keep to alpha-numeric characters.
+pref.nicknameList.label = Nickname List
+pref.nicknameList.help = This is a list of nicknames you want ChatZilla to try if the one you were using happens to be already in use. Your normal nickname need not be listed.
+pref.notify.aggressive.label = Aggressive notify
+pref.notify.aggressive.help = When someone sends you a private message, says your nickname, or mentions one of your "stalk words", ChatZilla considers the message to be worth getting your attention. This preference sets whether it's allowed to flash the window or bring it to the front (varies by OS) in order to get your attention.
+pref.notifyList.label = Notify list
+pref.notifyList.help = A list of nicknames to periodically check to see if they are online or not. Every 5 minutes, ChatZilla will check this list, and inform you if anyone is now online or has gone offline. If the server supports monitor lists, this list is synchronized with the server, and you will be notified immediately of any status changes.
+pref.outgoing.colorCodes.label = Enable sending color codes
+pref.outgoing.colorCodes.help = Allows you to send color and other mIRC codes, such as bold and underline, using special %-sequences. When enabled, simply type "%" to see a popup of the various choices.
+pref.outputWindowURL.label = Output Window
+pref.outputWindowURL.help = You probably don't want to change this. The chat view loads this URL to display the actual messages, header, etc., and the file must correctly define certain items or you'll get JavaScript errors and a blank chat window!
+pref.profilePath.label = Profile path
+pref.profilePath.help = This is the base location for ChatZilla-related files. By default, ChatZilla loads scripts from the "scripts" subdirectory, and stores log files in the "logs" subdirectory.
+pref.proxy.typeOverride.label = Proxy Type
+pref.proxy.typeOverride.help = Override the normal proxy choice by specifying "http" to use your browser's HTTP Proxy or "none" to force no proxy to be used (not even the SOCKS proxy). Note that this usually only works when the browser is set to use a manual proxy configuration.
+pref.reconnect.label = Reconnect when disconnected unexpectedly
+pref.reconnect.help = When your connection is lost unexpectedly, ChatZilla can automatically reconnect to the server for you.
+pref.sasl.plain.enabled.label = Use SASL authentication
+pref.sasl.plain.enabled.help = While connecting, ChatZilla can authenticate with SASL using your username and saved password.
+pref.showModeSymbols.label = Show user mode symbols
+pref.showModeSymbols.help = The userlist can either show mode symbols ("@" for op, "%" for half-op, "+" for voice), or it can show colored dots (green for op, dark blue for half-op, cyan for voice, and black for normal). Turn this preference on to show mode symbols instead of colored dots.
+pref.sortUsersByMode.label = Sort users by mode
+pref.sortUsersByMode.help = Causes the userlist to be sorted by mode, op first, then half-op (if supported on the server), then voice, followed by everyone else.
+pref.sound.enabled.label = Enabled
+pref.sound.enabled.help = Tick this preference to allow sound, or untick to turn off all sounds. Provides nothing more than a global toggle.
+pref.sound.overlapDelay.label = Overlap Delay
+pref.sound.overlapDelay.help = Sets the period of time during which the same event will not trigger the sound to be played. For example, the default value of 2000ms (2 seconds) means if two stalk matches occur within 2 seconds of each other, only the first will cause the sound to be played.
+##pref.sound.surpressActive.label = Suppress Sounds for active view
+##pref.sound.surpressActive.help = Stops sounds generated by the active view from playing if ChatZilla is the active window. Sounds from other views, or when ChatZilla is not active, will always play.
+pref.sound.channel.start.label = Sound for Channel Start
+pref.sound.channel.start.help =
+pref.sound.channel.event.label = Sound for Channel Event
+pref.sound.channel.event.help =
+pref.sound.channel.chat.label = Sound for Channel Chat
+pref.sound.channel.chat.help =
+pref.sound.channel.stalk.label = Sound for Channel Stalk
+pref.sound.channel.stalk.help =
+pref.sound.user.start.label = Sound for User Start
+pref.sound.user.start.help =
+pref.sound.user.stalk.label = Sound for User Chat
+pref.sound.user.stalk.help =
+pref.stalkWholeWords.label = Stalk whole words only
+pref.stalkWholeWords.help = This preferences toggles ChatZilla's handling of stalk words between finding matching words, or simple substrings. For example, "ChatZilla is cool" will match the stalk word "zilla" only if this preferences is off.
+pref.stalkWords.label = Stalk words
+pref.stalkWords.help = A list of words that will cause a line to be marked "important" and will try to get your attention if "Aggressive notify" is turned on.
+pref.upgrade-insecure.label = Enable opportunistic encryption
+pref.upgrade-insecure.help = When opening an unencrypted connection to a server, ChatZilla can request that the server upgrade the existing connection to use TLS if possible. Using this option is not recommended if the server supports a dedicated TLS port or strict transport security (STS).
+pref.sts.enabled.label = Enable strict transport security
+pref.sts.enabled.help = When opening an unencrypted connection to a server, ChatZilla will automatically switch to to a TLS port if the server advertises an STS policy.
+pref.urls.store.max.label = Max stored URLs
+pref.urls.store.max.help = Sets the maximum number of URLs collected and stored by ChatZilla. The "/urls" command displays the last 10 stored, or more if you do "/urls 20", for example.
+pref.userlistLeft.label = Display the userlist on the left
+pref.userlistLeft.help = Display the userlist on the left. Uncheck to display the userlist on the right instead.
+pref.username.label = Username
+pref.username.help = Your username is used to construct your "host mask", which is a string representing you. It includes your connection's host name and this username. It is sometimes used for setting auto-op, bans, and other things specific to one person.
+pref.usermode.label = Usermode
+pref.usermode.help = Your usermode is an option string that is sent to the IRC network. It is composed of a plus sign ("+") followed by one or more letters, each of which represents an option. The letter "i" represents "invisible mode". When you are invisible, your nickname will not appear in channel userlists for people who are not in the channel with you. The letter "s" allows you to see server messages like nickname collisions. For a more complete list of available options, look up usermode on www.irchelp.org.
+pref.warnOnClose.label = Warn me when quitting while still connected
+pref.warnOnClose.help = When quitting while still connected to networks, a message appears asking you if you are sure you want to quit. Uncheck this to disable it.
+
+# Preference group labels #
+
+pref.group.general.label = General
+pref.group.general.connect.label = Connection
+pref.group.general.ident.label = Identification
+pref.group.general.log.label = Logging
+pref.group.general.palert.label = Message notifications
+pref.group.global.palertconfig.label = Message notifications configuration
+pref.group.appearance.label = Appearance
+pref.group.appearance.misc.label = Miscellaneous
+pref.group.appearance.motif.label = Motifs
+pref.group.appearance.timestamps.label = Timestamps
+pref.group.appearance.timestamps.help = The Format preference uses strftime replacements. For example, "%A %l:%M:%S%P" might become "Thursday 1:37:42pm".
+pref.group.appearance.userlist.label = Userlist
+pref.group.dcc.label = DCC
+pref.group.dcc.ports.label = Ports
+pref.group.dcc.autoAccept.label = Auto-accept
+pref.group.munger.label = Formatting
+pref.group.startup.label = Startup
+pref.group.startup.initialURLs.label = Locations
+pref.group.startup.initialScripts.label = Script files
+pref.group.lists.label = Lists
+pref.group.lists.stalkWords.label = Stalk words
+pref.group.lists.aliases.label = Command aliases
+pref.group.lists.notifyList.label = Notify list
+pref.group.lists.nicknameList.label = Nickname List
+pref.group.lists.autoperform.label = Auto-perform
+pref.group.global.label = Global
+pref.group.global.header.label = Headers
+pref.group.global.header.help = Sets the default visibility for headers of views. Each view can override this default if necessary.
+pref.group.global.log.label = Log these view types
+pref.group.global.log.help = Sets the default logging state for views. Each view can override this default if necessary.
+pref.group.global.maxLines.label = Scrollback size
+pref.group.global.maxLines.help = The number of lines of text to keep in this view type. Once the limit is reached, the oldest lines are removed as new lines are added.
+pref.group.global.security.label = Security
+pref.group.global.sounds.label = Sound Configuration
+pref.group.general.sounds.help =
+pref.group.general.soundEvts.label = Sound Events
+pref.group.general.soundEvts.help = Sounds for certain client events. These preferences are a space-separated list of either "beep" or file: URLs.
+
+# These are the prefs that get grouped #
+
+pref.autoperform.label = Auto-perform
+pref.autoperform.help = Enter any commands to be run when connecting to this network/joining this channel/opening this user's private chat. The commands are run in the order listed.
+pref.autoperform.channel.label = Channel
+pref.autoperform.channel.help = Enter any commands to be run when joining any channel.
+pref.autoperform.client.label = Client
+pref.autoperform.client.help = Enter any commands to be run when starting ChatZilla.
+pref.autoperform.network.label = Network
+pref.autoperform.network.help = Enter any commands to be run when connecting to any network.
+pref.autoperform.user.label = User
+pref.autoperform.user.help = Enter any commands to be run when opening any user's private chat.
+
+pref.networkHeader.label = Networks
+pref.networkHeader.help =
+pref.channelHeader.label = Channels
+pref.channelHeader.help =
+pref.userHeader.label = Users
+pref.userHeader.help =
+pref.dccUserHeader.label = DCC
+pref.dccUserHeader.help =
+
+pref.networkLog.label = Networks
+pref.networkLog.help =
+pref.channelLog.label = Channels
+pref.channelLog.help =
+pref.userLog.label = Users
+pref.userLog.help =
+pref.dccUserLog.label = DCC
+pref.dccUserLog.help =
+
+pref.clientMaxLines.label = Client
+pref.clientMaxLines.help =
+pref.networkMaxLines.label = Networks
+pref.networkMaxLines.help =
+pref.channelMaxLines.label = Channels
+pref.channelMaxLines.help =
+pref.userMaxLines.label = Users
+pref.userMaxLines.help =
+pref.dccUserMaxLines.label = DCC
+pref.dccUserMaxLines.help =
+
+pref.timestamps.display.label = Format
+pref.timestamps.display.help =
+pref.timestamps.label = Enabled
+pref.timestamps.help =
+
+pref.msgBeep.label = New query view
+pref.msgBeep.help =
+pref.queryBeep.label = Query message
+pref.queryBeep.help =
+pref.stalkBeep.label = Important message
+pref.stalkBeep.help =
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/chatzillaOverlay.dtd b/comm/suite/chatzilla/locales/en-US/chrome/chatzillaOverlay.dtd
new file mode 100644
index 0000000000..af9e16bc10
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/chatzillaOverlay.dtd
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+
+<!ENTITY ircCmd.label "IRC Chat">
+<!ENTITY ircCmd.accesskey "i">
+<!ENTITY ircCmd.commandkey "6">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/config.dtd b/comm/suite/chatzilla/locales/en-US/chrome/config.dtd
new file mode 100644
index 0000000000..60be5d78fa
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/config.dtd
@@ -0,0 +1,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/. -->
+
+<!ENTITY window.title "ChatZilla Preferences">
+
+<!ENTITY loading.label "Please wait, loading…">
+
+<!ENTITY homepage.url "http://chatzilla.hacksrus.com/">
+<!ENTITY homepage.label "ChatZilla Homepage">
+
+<!ENTITY object.add.label "Add…">
+<!ENTITY object.add.hint "Add a new network, channel or user to set preferences on">
+<!ENTITY object.add.accesskey "A">
+
+<!ENTITY object.del.label "Remove">
+<!ENTITY object.del.hint "Remove the current object, clearing all of its preferences">
+<!ENTITY object.del.accesskey "R">
+
+<!ENTITY object.reset.label "Reset">
+<!ENTITY object.reset.hint "Reset this object's preferences to the defaults">
+<!ENTITY object.reset.accesskey "s">
+
+<!ENTITY dialog.apply "Apply">
+
+<!ENTITY network "Network">
+<!ENTITY channel "Channel">
+<!ENTITY user "User">
+
+<!ENTITY config.add.title "Add Preference Object">
+
+<!ENTITY config.type.label "Type:">
+<!ENTITY config.type.hint "Type of object to add preferences for">
+<!ENTITY config.type.accesskey "T">
+<!ENTITY config.network.label "Network:">
+<!ENTITY config.network.hint "Known name of the network, or the server name">
+<!ENTITY config.network.accesskey "N">
+<!ENTITY config.target.label "Target:">
+<!ENTITY config.target.hint "Target channel or user name">
+<!ENTITY config.target.accesskey "g">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/install-plugin.dtd b/comm/suite/chatzilla/locales/en-US/chrome/install-plugin.dtd
new file mode 100644
index 0000000000..e92b76728b
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/install-plugin.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!ENTITY windowtitle "Install Plugin…">
+
+<!ENTITY name.label "Name:">
+<!ENTITY name.accesskey "N">
+
+<!ENTITY name.autopick.label "Automatically pick a name for me">
+<!ENTITY name.autopick.accesskey "A">
+
+<!ENTITY source.label "Source:">
+<!ENTITY source.accesskey "c">
+
+<!ENTITY browse.label "Browse…">
+<!ENTITY browse.accesskey "B">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/networks.dtd b/comm/suite/chatzilla/locales/en-US/chrome/networks.dtd
new file mode 100644
index 0000000000..470efed305
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/networks.dtd
@@ -0,0 +1,57 @@
+<!-- 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/. -->
+
+<!-- Used in networks-edit dialog -->
+<!ENTITY networksEditDialog.title "Networks Editor">
+<!-- LOCALIZATION NOTE
+ - width should be 62ch + length of longest serverList*.label -->
+<!ENTITY networksEditDialog.size "width: 75ch; height: 28em;">
+
+<!ENTITY restoreButton.label "Restore Defaults">
+<!ENTITY restoreButton.accesskey "f">
+<!ENTITY connectNetwork.label "Connect to Network">
+<!ENTITY connectNetwork.accesskey "N">
+
+<!ENTITY networkListAdd.label "Add Network…">
+<!ENTITY networkListAdd.accesskey "A">
+<!ENTITY networkListAdd.tooltip "Add a new network">
+<!ENTITY networkListEdit.label "Edit Network…">
+<!ENTITY networkListEdit.accesskey "E">
+<!ENTITY networkListEdit.tooltip "Edit the selected network">
+<!ENTITY networkListRemove.label "Remove Network">
+<!ENTITY networkListRemove.accesskey "R">
+<!ENTITY networkListRemove.tooltip "Remove the selected network">
+
+<!ENTITY serverListUp.label "Move Up">
+<!ENTITY serverListUp.accesskey "U">
+<!ENTITY serverListUp.tooltip "Move the selected server up the list">
+<!ENTITY serverListDown.label "Move Down">
+<!ENTITY serverListDown.accesskey "D">
+<!ENTITY serverListDown.tooltip "Move the selected server down the list">
+<!ENTITY serverListAdd.label "Add Server…">
+<!ENTITY serverListAdd.accesskey "S">
+<!ENTITY serverListAdd.tooltip "Add a new server">
+<!ENTITY serverListEdit.label "Edit Server…">
+<!ENTITY serverListEdit.accesskey "i">
+<!ENTITY serverListEdit.tooltip "Edit the selected server">
+<!ENTITY serverListRemove.label "Remove Server">
+<!ENTITY serverListRemove.accesskey "m">
+<!ENTITY serverListRemove.tooltip "Remove the selected server">
+
+<!-- LOCALIZATION NOTE:
+ - *.label are shared between networks-edit and networks-server dialogs
+ - *.accesskey are only used in network-servers dialog -->
+<!ENTITY serverDetails.label "Details of the selected server:">
+<!ENTITY serverName.label "Server:">
+<!ENTITY serverName.accesskey "S">
+<!ENTITY serverPort.label "Port:">
+<!ENTITY serverPort.accesskey "P">
+<!ENTITY connectionSecurity.label "Connection Security:">
+<!ENTITY connectionSecurity.accesskey "n">
+
+<!-- Used in networks-server dialog -->
+<!ENTITY serverEditDialog.title "IRC Server">
+<!ENTITY settings.caption "Settings">
+<!ENTITY security.caption "Security">
+<!ENTITY serverPortDefault.label "Default:">
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/networks.properties b/comm/suite/chatzilla/locales/en-US/chrome/networks.properties
new file mode 100644
index 0000000000..a36d4879fc
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/networks.properties
@@ -0,0 +1,28 @@
+# 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/.
+
+network-headerDefault=Networks
+network-headerName=Network %S
+network-saveErrorTitle=Save Error
+network-saveError=An exception occurred trying to save the networks list: \n %S.
+
+network-addTitle=Add Network
+network-add=Enter network to add:
+network-editTitle=Edit Network
+network-edit=Network name:
+network-removeTitle=Remove Network
+network-remove=Are you sure that you want to remove the network: \n %S?
+network-nameErrorTitle=Network Exists
+network-nameError=A network %S already exists.
+
+network-confirmRestoreDefaultsTitle=Restore Default Network List
+network-confirmRestoreDefaults=Are you sure you want to restore the default network list, and overwrite any changes you have made?
+
+server-ConnectionSecurityType-0=None
+server-ConnectionSecurityType-3=SSL/TLS
+server-removeTitle=Remove Server
+server-remove=Are you sure that you want to remove the server: \n %S?
+
+invalidServerName=Invalid Server Name
+enterValidServerName=Please enter a valid server name.
diff --git a/comm/suite/chatzilla/locales/en-US/chrome/pref-irc.dtd b/comm/suite/chatzilla/locales/en-US/chrome/pref-irc.dtd
new file mode 100644
index 0000000000..6be6ff1bc8
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/chrome/pref-irc.dtd
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+
+<!ENTITY pref-irc.window.title "ChatZilla">
+
+<!ENTITY pref-irc.open.title "ChatZilla's Preferences">
+<!ENTITY pref-irc.open.desc "ChatZilla's preferences are too extensive to fit into this preference window so, instead, you can open its preferences window from here.">
+<!ENTITY pref-irc.open.label "Open ChatZilla's Preferences">
+<!ENTITY pref-irc.open.accesskey "O">
+<!ENTITY startup.chat.label "ChatZilla">
+<!ENTITY startup.chat.accesskey "Z">
diff --git a/comm/suite/chatzilla/locales/en-US/defines.inc b/comm/suite/chatzilla/locales/en-US/defines.inc
new file mode 100644
index 0000000000..1c706834f3
--- /dev/null
+++ b/comm/suite/chatzilla/locales/en-US/defines.inc
@@ -0,0 +1,11 @@
+#filter emptyLines
+
+#define MOZ_LANGPACK_CREATOR The ChatZilla Team
+
+#define MOZ_LANGPACK_HOMEPAGE http://chatzilla.hacksrus.com/
+
+# If non-English locales wish to credit multiple contributors, uncomment this
+# variable definition and use the format specified.
+# #define MOZ_LANGPACK_CONTRIBUTORS <em:contributor>Joe Solon</em:contributor> <em:contributor>Suzy Solon</em:contributor>
+
+#unfilter emptyLines
diff --git a/comm/suite/chatzilla/locales/jar.mn b/comm/suite/chatzilla/locales/jar.mn
new file mode 100644
index 0000000000..9ff9bf79b3
--- /dev/null
+++ b/comm/suite/chatzilla/locales/jar.mn
@@ -0,0 +1,20 @@
+# 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/.
+
+#filter substitution
+
+@AB_CD@.jar:
+# the "% locale" line gets put into the .manifest used by toolkit-based apps
+% locale chatzilla @AB_CD@ %locale/@AB_CD@/chatzilla/
+ locale/@AB_CD@/chatzilla/chatzillaOverlay.dtd (%chrome/chatzillaOverlay.dtd)
+ locale/@AB_CD@/chatzilla/browserOverlay.dtd (%chrome/browserOverlay.dtd)
+ locale/@AB_CD@/chatzilla/pref-irc.dtd (%chrome/pref-irc.dtd)
+ locale/@AB_CD@/chatzilla/chatzilla.dtd (%chrome/chatzilla.dtd)
+ locale/@AB_CD@/chatzilla/chatzilla.properties (%chrome/chatzilla.properties)
+ locale/@AB_CD@/chatzilla/config.dtd (%chrome/config.dtd)
+ locale/@AB_CD@/chatzilla/channels.dtd (%chrome/channels.dtd)
+ locale/@AB_CD@/chatzilla/networks.dtd (%chrome/networks.dtd)
+ locale/@AB_CD@/chatzilla/networks.properties (%chrome/networks.properties)
+ locale/@AB_CD@/chatzilla/about.dtd (%chrome/about.dtd)
+ locale/@AB_CD@/chatzilla/install-plugin.dtd (%chrome/install-plugin.dtd)
diff --git a/comm/suite/chatzilla/locales/l10n.ini b/comm/suite/chatzilla/locales/l10n.ini
new file mode 100644
index 0000000000..d4bacc82f7
--- /dev/null
+++ b/comm/suite/chatzilla/locales/l10n.ini
@@ -0,0 +1,10 @@
+; 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/.
+
+[general]
+depth = ../../..
+all = suite/chatzilla/locales/all-locales
+
+[compare]
+dirs = suite/chatzilla
diff --git a/comm/suite/chatzilla/locales/l10n.toml b/comm/suite/chatzilla/locales/l10n.toml
new file mode 100644
index 0000000000..8da66121cd
--- /dev/null
+++ b/comm/suite/chatzilla/locales/l10n.toml
@@ -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/.
+
+basepath = "../../.."
+
+locales = [
+ "cs",
+ "de",
+ "el",
+ "en-GB",
+ "es-AR",
+ "es-ES",
+ "fi",
+ "fr",
+ "hu",
+ "it",
+ "ja",
+ "ja-JP-mac",
+ "ka",
+ "nb-NO",
+ "nl",
+ "pl",
+ "pt-BR",
+ "pt-PT",
+ "ru",
+ "sk",
+ "sv-SE",
+ "zh-CN",
+ "zh-TW",
+]
+
+[env]
+ l = "{l10n_base}/{locale}/"
+
+[[paths]]
+ reference = "suite/chatzilla/locales/en-US/**"
+ l10n = "{l}suite/chatzilla/**"
+
+# ignore MOZ_LANGPACK_CONTRIBUTORS
+[[filters]]
+ path = "{l}suite/chatzilla/defines.inc"
+ key = "MOZ_LANGPACK_CONTRIBUTORS"
+ action = "ignore"
diff --git a/comm/suite/chatzilla/locales/moz.build b/comm/suite/chatzilla/locales/moz.build
new file mode 100644
index 0000000000..e0eb66aace
--- /dev/null
+++ b/comm/suite/chatzilla/locales/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn']
diff --git a/comm/suite/chatzilla/moz.build b/comm/suite/chatzilla/moz.build
new file mode 100644
index 0000000000..6e463475ee
--- /dev/null
+++ b/comm/suite/chatzilla/moz.build
@@ -0,0 +1,15 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES['CHATZILLA_VERSION'] = '0.9.97'
+
+DIRS += ['locales']
+
+JAR_MANIFESTS += ["jar.mn"]
+
+EXTRA_COMPONENTS += [
+ "js/lib/chatzilla-service.js",
+ "js/lib/chatzilla-service.manifest",
+]
diff --git a/comm/suite/chatzilla/xul/content/about/about.js b/comm/suite/chatzilla/xul/content/about/about.js
new file mode 100644
index 0000000000..b68c64aa8f
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/about/about.js
@@ -0,0 +1,112 @@
+/* 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 ownerClient = null;
+
+// To be able to load static.js, we need a few things defined first:
+function CIRCNetwork() {}
+function CIRCServer() {}
+function CIRCChannel() {}
+function CIRCUser() {}
+function CIRCChanUser() {}
+function CIRCDCCUser() {}
+function CIRCDCCChat() {}
+function CIRCDCCFile() {}
+function CIRCDCCFileTransfer() {}
+function CIRCSTS() {}
+
+// Our friend from messages.js:
+function getMsg(msgName, params, deflt)
+{
+ return client.messageManager.getMsg(msgName, params, deflt);
+}
+
+function onLoad()
+{
+ const propsPath = "chrome://chatzilla/locale/chatzilla.properties";
+
+ // Find our owner, if we have one.
+ ownerClient = window.arguments ? window.arguments[0].client : null;
+ if (ownerClient)
+ ownerClient.aboutDialog = window;
+
+ client.entities = new Object();
+ client.messageManager = new MessageManager(client.entities);
+ client.messageManager.loadBrands();
+ client.defaultBundle = client.messageManager.addBundle(propsPath);
+
+ var version = getVersionInfo();
+ client.userAgent = getMsg(MSG_VERSION_REPLY, [version.cz, version.ua]);
+
+ var verLabel = document.getElementById("version");
+ var verString = verLabel.getAttribute("format").replace("%S", version.cz);
+ verLabel.setAttribute("value", verString);
+ verLabel.setAttribute("condition", __cz_condition);
+
+ var localizers = document.getElementById("localizers");
+ var localizerNames = getMsg("locale.authors", null, "");
+ if (localizerNames && (localizerNames.substr(0, 11) != "XXX REPLACE"))
+ {
+ localizerNames = localizerNames.split(/\s*;\s*/);
+
+ for (var i = 0; i < localizerNames.length; i++) {
+ var loc = document.createElement("label");
+ loc.setAttribute("value", localizerNames[i]);
+ localizers.appendChild(loc);
+ }
+ }
+ else
+ {
+ var localizersHeader = document.getElementById("localizers-header");
+ localizersHeader.style.display = "none";
+ localizers.style.display = "none";
+ }
+
+ if (window.opener)
+ {
+ // Force the window to be the right size now, not later.
+ window.sizeToContent();
+
+ // Position it centered over, but never up or left of parent.
+ var opener = window.opener;
+ var sx = Math.max((opener.outerWidth - window.outerWidth ) / 2, 0);
+ var sy = Math.max((opener.outerHeight - window.outerHeight) / 2, 0);
+ window.moveTo(opener.screenX + sx, opener.screenY + sy);
+ }
+
+ /* Find and focus the dialog's default button (OK), otherwise the focus
+ * lands on the first focusable content - the homepage link. Links in XUL
+ * look horrible when focused.
+ */
+ var binding = document.documentElement;
+ var defaultButton = binding.getButton(binding.defaultButton);
+ if (defaultButton)
+ setTimeout(function() { defaultButton.focus() }, 0);
+}
+
+function onUnload()
+{
+ if (ownerClient)
+ delete ownerClient.aboutDialog;
+}
+
+function copyVersion()
+{
+ const cbID = Components.interfaces.nsIClipboard.kGlobalClipboard;
+ var cb = getService("@mozilla.org/widget/clipboard;1", "nsIClipboard");
+ var tr = newObject("@mozilla.org/widget/transferable;1", "nsITransferable");
+ var str = newObject("@mozilla.org/supports-string;1", "nsISupportsString");
+
+ str.data = client.userAgent;
+ tr.setTransferData("text/unicode", str, str.data.length * 2);
+ cb.setData(tr, null, cbID);
+}
+
+function openHomepage()
+{
+ if (ownerClient)
+ ownerClient.dispatch("goto-url", {url: MSG_SOURCE_REPLY});
+ else
+ window.opener.open(MSG_SOURCE_REPLY, "_blank");
+}
diff --git a/comm/suite/chatzilla/xul/content/about/about.xul b/comm/suite/chatzilla/xul/content/about/about.xul
new file mode 100644
index 0000000000..54dc7b1bd4
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/about/about.xul
@@ -0,0 +1,57 @@
+<?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 dialog SYSTEM "chrome://chatzilla/locale/about.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/skin/about.css" type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="irc:chatzilla:about"
+ buttons="accept"
+ onload="onLoad()"
+ onunload="onUnload()"
+ title="&window.title;">
+
+ <script src="chrome://chatzilla/content/lib/js/utils.js"/>
+ <script src="chrome://chatzilla/content/about/about.js"/>
+ <script src="chrome://chatzilla/content/lib/js/message-manager.js"/>
+ <script src="chrome://chatzilla/content/static.js"/>
+
+ <vbox class="box-padded" flex="1">
+ <hbox>
+ <image id="logo"/>
+ <vbox flex="1">
+ <hbox>
+ <label class="header large-text" id="name" value="&chatzilla.label;"/>
+ <spacer flex="1"/>
+ <label class="header" id="version" value="&version.unknown.label;" format="&version.known.label;"/>
+ </hbox>
+ <hbox>
+ <label class="text-link" onclick="openHomepage()" value="&homepage.label;"/>
+ <spacer flex="1"/>
+ <label class="text-link" onclick="copyVersion()" value="&copyversion.label;"/>
+ </hbox>
+ </vbox>
+ </hbox>
+ <description id="description">&description.label;</description>
+ <label class="contributors-label header" value="&section.core.label;"/>
+ <vbox class="contributors">
+ <label>Robert Ginda</label>
+ <label>Gijs Kruitbosch</label>
+ <label>James Ross</label>
+ <label>Samuel Sieb</label>
+ </vbox>
+ <label class="contributors-label header" id="localizers-header" value="&section.locale.label;"/>
+ <vbox class="contributors" id="localizers">
+ <!-- These are inserted from onLoad(), as read from locale file. -->
+ </vbox>
+ <label class="contributors-label header" value="&section.contrib.label;"/>
+ <vbox class="contributors">
+ <label>Lim Chee Aun (graphics)</label>
+ </vbox>
+ </vbox>
+ <separator id="groove" class="groove"/>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/browserOverlay.xul b/comm/suite/chatzilla/xul/content/browserOverlay.xul
new file mode 100644
index 0000000000..a6e31a3158
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/browserOverlay.xul
@@ -0,0 +1,24 @@
+<?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 SYSTEM "chrome://chatzilla/locale/browserOverlay.dtd" >
+
+<!-- This is the overlay that adds a "Chatzilla" button to the toolbar palette. -->
+
+<?xml-stylesheet href="chrome://chatzilla/skin/browserOverlay.css"
+ type="text/css"?>
+
+<overlay id="ChatzillaBrowserToolbar"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://chatzilla/content/chatzillaOverlay.js"/>
+
+<toolbarpalette id="BrowserToolbarPalette">
+ <toolbarbutton id="chatzilla-open" oncommand="toIRC()"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ label="&czButton.label;" tooltiptext="&czButton.label;"/>
+</toolbarpalette>
+</overlay>
diff --git a/comm/suite/chatzilla/xul/content/channels.js b/comm/suite/chatzilla/xul/content/channels.js
new file mode 100644
index 0000000000..912e62c14a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/channels.js
@@ -0,0 +1,875 @@
+/* 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 client;
+var network;
+var channels = new Array();
+var tree = { view: null, newItem: null, share: new Object() };
+var xul = new Object();
+
+
+// Create list of operations. These are handled by common code.
+const OPS = new Array();
+OPS.push({ key: "noop", ignore: true });
+OPS.push({ key: "list", canStop: false });
+OPS.push({ key: "load", canStop: true });
+OPS.push({ key: "filter", canStop: true });
+
+
+// Define constants for each operation.
+// JavaScript won't let you delete things declared with "var", workaround:
+// NOTE: This order MUST be the same as those above!
+window.s = 0;
+const OP_LIST = ++s; // A /list operation on the server.
+const OP_LOAD = ++s; // Loading the saved file.
+const OP_FILTER = ++s; // Filtering the loaded list.
+
+
+// Define constants for the valid states of each operation.
+// All states before STATE_START must be idle (stopped) states.
+// All states from STATE_START onwards must be busy (running) states.
+s = 0;
+const STATE_IDLE = ++s; // Not doing this operation.
+const STATE_ERROR = ++s; // Error occurred: don't try do to any more.
+const STATE_START = ++s; // Starting an operation.
+const STATE_RUN = ++s; // Running...
+const STATE_STOP = ++s; // Clean-up/ending operation.
+delete window.s;
+
+
+// Store all the operation data here.
+var data = {
+ list: { state: STATE_IDLE },
+ load: { state: STATE_IDLE },
+ filter: { state: STATE_IDLE }
+};
+
+
+// This should keep things responsive enough, for the user to click buttons and
+// edit the filter text and options, without giving up too much time to letting
+// Gecko catch up.
+const PROCESS_TIME_MAX = 200;
+const PROCESS_DELAY = 50;
+
+const colIDToSortKey = { chanColName: "name",
+ chanColUsers: "users",
+ chanColTopic: "topic" };
+const sortKeyToColID = { name: "chanColName",
+ users: "chanColUsers",
+ topic: "chanColTopic" };
+
+function onLoad()
+{
+ function ondblclick(event) { tree.view.onRouteDblClick(event); };
+ function onkeypress(event) { tree.view.onRouteKeyPress(event); };
+ function onfocus(event) { tree.view.onRouteFocus(event); };
+ function onblur(event) { tree.view.onRouteBlur(event); };
+
+ function doJoin()
+ {
+ if (joinChannel())
+ window.close();
+ };
+
+ client = window.arguments[0].client;
+ client.joinDialog = window;
+
+ window.dd = client.mainWindow.dd;
+ window.ASSERT = client.mainWindow.ASSERT;
+ window.toUnicode = client.mainWindow.toUnicode;
+ window.getMsg = client.mainWindow.getMsg;
+ window.MSG_CHANNEL_OPENED = client.mainWindow.MSG_CHANNEL_OPENED;
+ window.MSG_FMT_JSEXCEPTION = client.mainWindow.MSG_FMT_JSEXCEPTION;
+ window.MT_INFO = client.mainWindow.MT_INFO;
+
+ // Import "MSG_CD_*"...
+ for (var m in client.mainWindow)
+ {
+ if (m.substr(0, 7) == "MSG_CD_")
+ window[m] = client.mainWindow[m];
+ }
+
+ // Cache all the XUL DOM elements.
+ var elements = ["network", "networks", "channel", "includeTopic",
+ "lastUpdated", "join", "minUsers", "maxUsers", "refresh",
+ "bottomPanel", "channels", "loadContainer", "loadLabel",
+ "loadBarDeck", "loadBar"];
+ for (var i = 0; i < elements.length; i++)
+ xul[elements[i]] = document.getElementById(elements[i]);
+
+ // Set the <dialog>'s class so we can do platform-specific CSS.
+ var dialog = document.getElementById("chatzilla-window");
+ dialog.className = "platform-" + client.platform;
+
+ // Set up the channel tree view.
+ tree.view = new XULTreeView(tree.share);
+ tree.view.onRowCommand = doJoin;
+ tree.view.cycleHeader = changeSort;
+ xul.channels.treeBoxObject.view = tree.view;
+
+ // If the new "search" binding is not working (i.e. doesn't exist)...
+ if (!("searchButton" in xul.channel))
+ {
+ // ...restore the text boxes to their former selves.
+ xul.channel.setAttribute("timeout", "500");
+ xul.channel.setAttribute("type", "timed");
+ xul.minUsers.setAttribute("timeout", "500");
+ xul.minUsers.setAttribute("type", "timed");
+ xul.maxUsers.setAttribute("timeout", "500");
+ xul.maxUsers.setAttribute("type", "timed");
+ }
+
+ // Sort by user count, descending.
+ changeSort("chanColUsers");
+
+ xul.channels.addEventListener("dblclick", ondblclick, false);
+ xul.channels.addEventListener("keypress", onkeypress, false);
+ xul.channels.addEventListener("focus", onfocus, false);
+ xul.channels.addEventListener("blur", onblur, false);
+
+ tree.newItem = new ChannelEntry("", "", MSG_CD_CREATE);
+ tree.newItem.first = true;
+ tree.view.childData.appendChild(tree.newItem);
+
+ var opener = window.arguments[0].opener;
+ if (opener)
+ {
+ // Force the window to be the right size now, not later.
+ window.sizeToContent();
+
+ // Position it centered over, but never up or left of parent.
+ var sx = Math.max((opener.outerWidth - window.outerWidth ) / 2, 0);
+ var sy = Math.max((opener.outerHeight - window.outerHeight) / 2, 0);
+ window.moveTo(opener.screenX + sx, opener.screenY + sy);
+ }
+
+ setNetwork(window.arguments[0].network);
+ setTimeout(updateOperations, PROCESS_DELAY);
+ if (network)
+ xul.channel.focus();
+ else
+ xul.network.focus();
+}
+
+function onUnload()
+{
+ delete client.joinDialog;
+}
+
+function onKeyPress(event)
+{
+ if (event.keyCode == event.DOM_VK_RETURN)
+ {
+ if (joinChannel())
+ window.close();
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ else if (event.keyCode == event.DOM_VK_UP)
+ {
+ if (tree.view.selectedIndex > 0)
+ {
+ tree.view.selectedIndex = tree.view.selectedIndex - 1;
+ ensureRowIsVisible();
+ }
+ event.preventDefault();
+ }
+ else if (event.keyCode == event.DOM_VK_DOWN)
+ {
+ if (tree.view.selectedIndex < tree.view.rowCount - 1)
+ {
+ tree.view.selectedIndex = tree.view.selectedIndex + 1;
+ ensureRowIsVisible();
+ }
+ event.preventDefault();
+ }
+}
+
+function onShowingNetworks()
+{
+ while (xul.networks.lastChild)
+ xul.networks.removeChild(xul.networks.lastChild);
+
+ /* Show any network meeting at least 1 requirement:
+ * - Non-temporary (i.e. real network).
+ * - Currently connected.
+ * - Has visible tab in main window.
+ */
+ var networks = new Array();
+ for (var n in client.networks)
+ {
+ if (!client.networks[n].temporary
+ || client.networks[n].isConnected()
+ || client.mainWindow.getTabForObject(client.networks[n]))
+ {
+ networks.push(client.networks[n].unicodeName);
+ }
+ }
+ networks.sort();
+ for (var i = 0; i < networks.length; i++)
+ {
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", networks[i]);
+ xul.networks.appendChild(menuitem);
+ }
+}
+
+function onSelectionChange()
+{
+ update();
+}
+
+function onFilter()
+{
+ update();
+ if (network)
+ startOperation(OP_FILTER);
+}
+
+function setNetwork(newNetwork, noUpdate)
+{
+ xul.network.value = newNetwork ? newNetwork.unicodeName : "";
+ update();
+}
+
+function update()
+{
+ let newNetwork = client.getNetwork(xul.network.value);
+ if (network != newNetwork)
+ {
+ network = newNetwork;
+ if (network)
+ startOperation(OP_LOAD);
+ }
+
+ if (network)
+ {
+ var index = tree.view.selectedIndex;
+ var rows = tree.view.childData;
+ var row = index == -1 ? null : rows.locateChildByVisualRow(index);
+ var listFile = getListFile();
+ var listMod = 0;
+ if (listFile.exists() && (listFile.fileSize > 0))
+ listMod = listFile.lastModifiedTime;
+
+ xul.join.disabled = network.isConnected() && (!row || !row.name);
+ xul.lastUpdated.value = listMod ? getMsg(MSG_CD_UPDATED, [strftime(MSG_CD_UPDATED_FORMAT, new Date(listMod))]) : MSG_CD_UPDATED_NEVER;
+ xul.refresh.disabled = !network.isConnected() ||
+ (getOperationState(OP_LIST) == STATE_START) ||
+ (getOperationState(OP_LIST) == STATE_RUN);
+ xul.bottomPanel.selectedIndex = 1;
+ }
+ else
+ {
+ xul.join.disabled = !xul.network.value;
+ xul.lastUpdated.value = "";
+ xul.refresh.disabled = true;
+ xul.bottomPanel.selectedIndex = 0;
+ }
+}
+
+function joinChannel()
+{
+ update();
+ if (xul.join.disabled)
+ return false;
+
+ /* Calculate the row index AS IF the 'create' row is visible. We're going
+ * to use this so that the index chosen by the user is always consistent,
+ * whatever the visibility of the 'create' row - an index of 0 is ALWAYS
+ * the 'create' row, and >= 1 is ALWAYS the searched rows.
+ */
+ var index = tree.view.selectedIndex;
+ var row = tree.view.childData.locateChildByVisualRow(index);
+ var realIndex = index + (tree.newItem.isHidden ? 1 : 0);
+
+ client.dispatch("attach", { ircUrl: xul.network.value + "/" + row.name });
+
+ return true;
+}
+
+function focusSearch()
+{
+ xul.channel.focus();
+}
+
+function refreshList()
+{
+ startOperation(OP_LIST);
+}
+
+function updateProgress(label, pro)
+{
+ if (label)
+ {
+ xul.loadLabel.value = label;
+ }
+ else
+ {
+ var msg = getMsg(MSG_CD_SHOWING,
+ [(tree.view.rowCount - (tree.newItem.isHidden ? 0 : 1)),
+ channels.length]);
+ xul.loadLabel.value = msg;
+ }
+
+ xul.loadBarDeck.selectedIndex = (typeof pro == "undefined") ? 1 : 0;
+
+ if ((typeof pro == "undefined") || (pro == -1))
+ {
+ xul.loadBar.mode = "undetermined";
+ }
+ else
+ {
+ xul.loadBar.mode = "determined";
+ xul.loadBar.value = pro;
+ }
+}
+
+function changeSort(col)
+{
+ if (typeof col == "object")
+ col = col.id;
+
+ col = colIDToSortKey[col];
+ // Users default to descending, others ascending.
+ var dir = (col == "users" ? -1 : 1);
+
+ if (col == tree.share.sortColumn)
+ dir = -tree.share.sortDirection;
+
+ var colID = sortKeyToColID[tree.share.sortColumn];
+ var colNode = document.getElementById(colID);
+ if (colNode)
+ {
+ colNode.removeAttribute("sortActive");
+ colNode.removeAttribute("sortDirection");
+ }
+
+ tree.view.childData.setSortColumn(col, dir);
+
+ colID = sortKeyToColID[tree.share.sortColumn];
+ colNode = document.getElementById(colID);
+ if (colNode)
+ {
+ colNode.setAttribute("sortActive", "true");
+ var sortDir = (dir > 0 ? "ascending" : "descending");
+ colNode.setAttribute("sortDirection", sortDir);
+ }
+}
+
+
+// ***** BEGIN OPERATIONS CODE *****
+
+
+/* Return the static data about an operation (e.g. whether it can be
+ * stopped, etc.). The data returned is always the same for a given op code.
+ */
+function getOperation(op)
+{
+ ASSERT(op in OPS, "Invalid op-code: " + op);
+ return OPS[op];
+}
+
+/* Returns the live data about an operation (e.g. current state). Accepts
+ * either the op ID or the static data (as returned from getOperation(op)).
+ */
+function getOperationData(op)
+{
+ if (typeof op == "object")
+ return data[op.key];
+ return data[getOperation(op).key];
+}
+
+// Returns the current state of an operation; accepts same as getOperationData.
+function getOperationState(op)
+{
+ return getOperationData(op).state;
+}
+
+function startOperation(op)
+{
+ var ops = getOperation(op);
+ if (ops.ignore)
+ return;
+
+ var dbg = "startOperation(" + ops.key + ")";
+ var opData = getOperationData(ops);
+
+ // STATE_ERROR operations must not do anything. Assert and bail.
+ if (!ASSERT(opData.state != STATE_ERROR, dbg + " in STATE_ERROR"))
+ return;
+
+ // Check we can stop a non-idle operation.
+ if (!ASSERT((opData.state == STATE_IDLE) || ops.canStop,
+ dbg + " not in STATE_IDLE and can't stop"))
+ {
+ return;
+ }
+
+ // Stop the current operation.
+ if (opData.state != STATE_IDLE)
+ stopOperation(op);
+
+ // Begin!
+ var opData = getOperationData(op);
+ opData.state = STATE_START;
+ processOperation(op);
+ ASSERT(opData.state == STATE_RUN, dbg + " didn't enter STATE_RUN");
+}
+
+function updateOperations()
+{
+ for (var i = 1; i < OPS.length; i++)
+ {
+ var state = getOperationState(i);
+ if ((state == STATE_RUN) || (state == STATE_STOP))
+ processOperation(i);
+ }
+
+ setTimeout(updateOperations, PROCESS_DELAY);
+}
+
+function processOperation(op)
+{
+ var ops = getOperation(op);
+ if (ops.ignore)
+ return;
+
+ var dbg = "processOperation(" + ops.key + ")";
+ var opData = getOperationData(ops);
+
+ var fn = "processOp";
+ fn += ops.key[0].toUpperCase() + ops.key.substr(1);
+ if (opData.state == STATE_START)
+ fn += "Start";
+ else if (opData.state == STATE_RUN)
+ fn += "Run";
+ else if (opData.state == STATE_STOP)
+ fn += "Stop";
+ // assert and return if we're in a different state:
+ else if (!ASSERT(false, dbg + " invalid state: " + opData.state))
+ return;
+
+ try
+ {
+ var newState = window[fn](opData);
+ if (typeof newState != "undefined")
+ opData.state = newState;
+ }
+ catch(ex)
+ {
+ /* If an error has occurred, we display it (updateProgress) and then
+ * halt our operations to prevent further damage.
+ */
+ dd("Exception in channels.js: " + dbg + ": " + fn + ": " + formatException(ex));
+ updateProgress(formatException(ex));
+ opData.state = STATE_ERROR;
+ }
+}
+
+function stopOperation(op)
+{
+ var ops = getOperation(op);
+ if (ops.ignore)
+ return;
+
+ var dbg = "stopOperation(" + ops.key + ")";
+ var opData = getOperationData(ops);
+
+ // STATE_ERROR operations must not do anything. Assert and bail.
+ if (!ASSERT(opData.state != STATE_ERROR, dbg + " in STATE_ERROR"))
+ return;
+
+ // Nothing to do for STATE_IDLE. We shouldn't really be here, so assert.
+ if (!ASSERT(opData.state != STATE_IDLE, dbg + " in STATE_IDLE"))
+ return;
+
+ // Force the end and process synchronously.
+ opData.state = STATE_STOP;
+ processOperation(op);
+ ASSERT(opData.state == STATE_IDLE, dbg + " didn't enter STATE_IDLE");
+}
+
+// ***** END OPERATIONS CODE *****
+
+
+// ***** BEGIN OPERATION HANDLERS *****
+
+function processOpListStart(opData)
+{
+ ASSERT(network, "No network");
+ ASSERT(network.isConnected(), "Network is disconnected");
+
+ // Updates the refresh button.
+ update();
+
+ // Show a general message until we get some data.
+ updateProgress(MSG_CD_FETCHING, -1);
+
+ // Get the file we're going to save to, and start the /list.
+ var file = getListFile();
+ network.list("", file.path);
+
+ return STATE_RUN;
+}
+
+function processOpListRun(opData)
+{
+ // Update the progress and end if /list done for "list only" state.
+ updateProgress(getMsg(MSG_CD_FETCHED, network._list.count), -1);
+
+ // Stop if the network's /list has finished.
+ return (network._list.done ? STATE_STOP : STATE_RUN);
+}
+
+function processOpListStop(opData)
+{
+ // Updates the refresh button.
+ update();
+
+ // Check that /list finished okay if we're just doing a list.
+ if ("error" in network._list)
+ {
+ updateProgress(MSG_CD_ERROR_LIST);
+ }
+ else
+ {
+ updateProgress();
+ if (getOperationState(OP_LOAD) == STATE_IDLE)
+ startOperation(OP_LOAD);
+ }
+
+ return STATE_IDLE;
+}
+
+function processOpLoadStart(opData)
+{
+ ASSERT(network, "No network");
+
+ // Nuke contents.
+ tree.view.selectedIndex = -1;
+ if (tree.view.childData.childData.length > 1)
+ tree.view.childData.removeChildrenAtIndex(1, tree.view.childData.childData.length - 1);
+
+ var file = getListFile();
+ if (!file.exists())
+ {
+ // We tried to do a load, but the file does not exist. Start a list to
+ // fill up the file.
+ startOperation(OP_LIST);
+
+ // File still doesn't exist, just give up.
+ if (!file.exists())
+ return STATE_IDLE;
+ }
+
+ // Nuke more stuff.
+ channels = new Array();
+
+ // And... here we go.
+ opData.loadFile = new LocalFile(file, "<");
+ opData.loadPendingData = "";
+ opData.loadChunk = 10000;
+ opData.loadedSoFar = 0;
+
+ return STATE_RUN;
+}
+
+function processOpLoadRun(opData)
+{
+ // All states before STATE_START are "not running" states.
+ var opListRunning = (getOperationState(OP_LIST) >= STATE_START);
+
+ var end = Number(new Date()) + PROCESS_TIME_MAX;
+ while (Number(new Date()) < end)
+ {
+ var nlIndex = opData.loadPendingData.indexOf("\n");
+ if (nlIndex == -1)
+ {
+ opData.loadedSoFar += opData.loadChunk;
+ var newChunk = opData.loadFile.read(opData.loadChunk);
+ if (newChunk)
+ opData.loadPendingData += newChunk;
+ nlIndex = opData.loadPendingData.indexOf("\n");
+ if (nlIndex == -1)
+ break;
+ }
+
+ var line = opData.loadPendingData.substr(0, nlIndex);
+ opData.loadPendingData = opData.loadPendingData.substr(nlIndex + 1);
+
+ line = toUnicode(line, "UTF-8");
+ var ary = line.match(/^([^ ]+) ([^ ]+) (.*)$/);
+ if (ary)
+ {
+ var chan = new ChannelEntry(ary[1], ary[2], ary[3]);
+ channels.push(chan);
+ }
+ }
+
+ var dataLeft = opData.loadFile.inputStream.available();
+
+ // We shouldn't update the display when listing as well, as we're not
+ // going to show anything useful (always 100% or near to it, and
+ // replaces the 'fetching' message).
+ if (!opListRunning)
+ {
+ var pro = opData.loadedSoFar / (opData.loadedSoFar + dataLeft);
+ pro = Math.round(100 * pro);
+ updateProgress(getMsg(MSG_CD_LOADED, channels.length), pro);
+ }
+
+ // Done if there is no more data, and we're not *expecting* any more.
+ if ((dataLeft == 0) && !opListRunning)
+ return STATE_STOP;
+
+ return STATE_RUN;
+}
+
+function processOpLoadStop(opData)
+{
+ if (channels.length > 0)
+ tree.view.childData.appendChildren(channels);
+ opData.loadFile.close();
+ delete opData.loadFile;
+ delete opData.loadPendingData;
+ delete opData.loadChunk;
+ delete opData.loadedSoFar;
+ delete opData.loadNeverComplete;
+ updateProgress();
+
+ startOperation(OP_FILTER);
+
+ return STATE_IDLE;
+}
+
+function processOpFilterStart(opData)
+{
+ // Catch filtering with the same options on the same channels:
+ var newOptions = {network: xul.network.value.toLowerCase(),
+ text: xul.channel.value.toLowerCase(),
+ min: xul.minUsers.value * 1,
+ max: xul.maxUsers.value * 1,
+ listLen: channels.length,
+ searchTopics: xul.includeTopic.checked};
+
+ if (("filterOptions" in window) &&
+ equalsObject(window.filterOptions, newOptions))
+ {
+ return STATE_IDLE;
+ }
+
+ window.filterOptions = newOptions;
+
+ opData.text = newOptions.text;
+ opData.searchTopics = newOptions.searchTopics;
+ opData.minUsers = newOptions.min;
+ opData.maxUsers = newOptions.max;
+ opData.exactMatch = null;
+ opData.currentIndex = 0;
+ opData.channelText = opData.text;
+
+ // Log the filter, indicating which features the user is using.
+ var filters = new Array();
+ if (opData.channelText)
+ filters.push("name");
+ if (opData.searchTopics)
+ filters.push("topics");
+ if (opData.minUsers)
+ filters.push("min-users");
+ if (opData.maxUsers)
+ filters.push("max-users");
+
+ if (opData.channelText &&
+ (arrayIndexOf(["#", "&", "+", "!"], opData.channelText[0]) == -1))
+ {
+ opData.channelText = "#" + opData.channelText;
+ }
+ else
+ {
+ // Log that user has specified an explicit prefix.
+ filters.push("prefix");
+ }
+
+ // Update special "create channel" row, and select it.
+ tree.newItem.name = opData.channelText;
+ tree.newItem.unHide();
+
+ // Scroll to the top and select the "create channel" row.
+ tree.view.selectedIndex = 0;
+ xul.channels.treeBoxObject.invalidateRow(0);
+ xul.channels.treeBoxObject.scrollToRow(0);
+ ensureRowIsVisible();
+
+ updateProgress(getMsg(MSG_CD_FILTERING, [0, channels.length]), 0);
+
+ return STATE_RUN;
+}
+
+function processOpFilterRun(opData)
+{
+ var end = Number(new Date()) + PROCESS_TIME_MAX;
+ var more = false;
+
+ // Save selection because freeze/thaw screws it up.
+ // Note that we only save the item if it isn't the "create channel" row.
+ var index = tree.view.selectedIndex;
+ var item = null;
+ if (index > 0)
+ item = tree.view.childData.locateChildByVisualRow(index);
+
+ tree.view.freeze();
+ for (var i = opData.currentIndex; i < channels.length; i++)
+ {
+ var c = channels[i];
+
+ var match = (c.nameLC.indexOf(opData.text) != -1) ||
+ (opData.searchTopics &&
+ (c.topicLC.indexOf(opData.text) != -1));
+
+ if (opData.minUsers && (c.users < opData.minUsers))
+ match = false;
+ if (opData.maxUsers && (c.users > opData.maxUsers))
+ match = false;
+
+ if (match)
+ c.unHide();
+ else
+ c.hide();
+
+ if (match && (c.nameLC == opData.channelText))
+ opData.exactMatch = c;
+
+ opData.currentIndex = i;
+ if ((new Date()) > end)
+ {
+ more = true;
+ break;
+ }
+ }
+ tree.view.thaw();
+
+ // No item selected by user, so use our exact match instead.
+ if (!item && opData.exactMatch)
+ item = opData.exactMatch;
+
+ // Restore selected item.
+ if (item)
+ tree.view.selectedIndex = item.calculateVisualRow();
+ else
+ tree.view.selectedIndex = 0;
+
+ ensureRowIsVisible();
+
+ updateProgress(getMsg(MSG_CD_FILTERING,
+ [opData.currentIndex, channels.length]),
+ 100 * opData.currentIndex / channels.length);
+
+ return (more ? STATE_RUN : STATE_STOP);
+}
+
+function processOpFilterStop(opData)
+{
+ if (opData.exactMatch)
+ {
+ tree.newItem.hide();
+ }
+ // If nothing is selected, select the "create channel" row.
+ else if (tree.view.selectedIndex < 0)
+ {
+ tree.view.selectedIndex = 0;
+ }
+
+ ensureRowIsVisible();
+
+ delete opData.text;
+ delete opData.searchTopics;
+ delete opData.minUsers;
+ delete opData.maxUsers;
+ delete opData.exactMatch;
+ delete opData.currentIndex;
+ delete opData.channelText;
+ updateProgress();
+
+ return STATE_IDLE;
+}
+
+
+// ***** END OPERATION HANDLERS *****
+
+
+function ensureRowIsVisible()
+{
+ if (tree.view.selectedIndex >= 0)
+ xul.channels.treeBoxObject.ensureRowIsVisible(tree.view.selectedIndex);
+ else
+ xul.channels.treeBoxObject.ensureRowIsVisible(0);
+}
+
+function getListFile(temp)
+{
+ ASSERT(network, "No network");
+ var file = new LocalFile(network.prefs["logFileName"]);
+ if (temp)
+ file.localFile.leafName = "list.temp";
+ else
+ file.localFile.leafName = "list.txt";
+ return file.localFile;
+}
+
+
+// Tree ChannelEntry objects //
+function ChannelEntry(name, users, topic)
+{
+ this.setColumnPropertyName("chanColName", "name");
+ this.setColumnPropertyName("chanColUsers", "users");
+ this.setColumnPropertyName("chanColTopic", "topic");
+
+ // Nuke color codes and bold etc.
+ topic = topic.replace(/[\x1F\x02\x0F\x16]/g, "");
+ topic = topic.replace(/\x03\d{1,2}(?:,\d{1,2})?/g, "");
+
+ this.name = name;
+ this.users = users;
+ this.topic = topic;
+
+ this.nameLC = this.name.toLowerCase();
+ this.topicLC = this.topic.toLowerCase();
+}
+
+ChannelEntry.prototype = new XULTreeViewRecord(tree.share);
+
+ChannelEntry.prototype.sortCompare =
+function chanentry_sortcmp(a, b)
+{
+ var sc = a._share.sortColumn;
+ var sd = a._share.sortDirection;
+
+ // Make sure the special 'first' row is always first.
+ if ("first" in a)
+ return -1;
+ if ("first" in b)
+ return 1;
+
+ if (sc == "users")
+ {
+ // Force a numeric comparison.
+ a = 1 * a[sc];
+ b = 1 * b[sc];
+ }
+ else
+ {
+ // Case-insensitive, please.
+ a = a[sc].toLowerCase();
+ b = b[sc].toLowerCase();
+ }
+
+ if (a < b)
+ return -1 * sd;
+
+ if (a > b)
+ return 1 * sd;
+
+ return 0;
+}
diff --git a/comm/suite/chatzilla/xul/content/channels.xul b/comm/suite/chatzilla/xul/content/channels.xul
new file mode 100644
index 0000000000..7770a7a0d9
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/channels.xul
@@ -0,0 +1,108 @@
+<?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 dialog SYSTEM "chrome://chatzilla/locale/channels.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/skin/channels.css" type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ windowtype="irc:chatzilla:channels"
+ id="chatzilla-window"
+ buttons="cancel"
+ onload="onLoad()"
+ onunload="onUnload()"
+ ondialogaccept="return joinChannel()"
+ title="&window.title;">
+
+ <script src="chrome://chatzilla/content/lib/js/utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/file-utils.js"/>
+ <script src="chrome://chatzilla/content/lib/xul/tree-utils.js"/>
+ <script src="channels.js"/>
+
+ <vbox flex="1">
+ <hbox id="topPanel">
+ <grid flex="1">
+ <columns>
+ <column/><column flex="1"/><column id="rightPanel"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label value="&network.label;" accesskey="&network.accesskey;"
+ control="network"/>
+ <menulist id="network" editable="true" tabindex="1"
+ oncommand="onFilter()" onblur="onFilter()"
+ onkeypress="onKeyPress(event)" onkeyup="update()">
+ <menupopup id="networks" onpopupshowing="onShowingNetworks()">
+ </menupopup>
+ </menulist>
+ <button id="join" disabled="true" default="true" tabindex="4"
+ label="&join.label;" accesskey="&join.accesskey;"
+ oncommand="if (joinChannel()) window.close()"/>
+ </row>
+ <row align="center">
+ <label value="&channel.label;" accesskey="&channel.accesskey;"
+ control="channel"/>
+ <textbox id="channel" type="search" tabindex="2"
+ oncommand="onFilter()" onkeypress="onKeyPress(event)"/>
+ <hbox align="center">
+ <label value="&minusers.label;" accesskey="&minusers.accesskey;"
+ control="minUsers"/>
+ <textbox id="minUsers" type="search" flex="1" tabindex="5"
+ oncommand="onFilter()"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <spacer/>
+ <checkbox id="includeTopic" checked="true" tabindex="3"
+ label="&topics.label;" accesskey="&topics.accesskey;"
+ oncommand="onFilter(); focusSearch()"/>
+ <hbox align="center">
+ <label value="&maxusers.label;" accesskey="&maxusers.accesskey;"
+ control="maxUsers"/>
+ <textbox id="maxUsers" type="search" flex="1" tabindex="6"
+ oncommand="onFilter()"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <spacer/>
+ <label id="lastUpdated"/>
+ <button id="refresh"
+ tabindex="7"
+ label="&refreshNow.label;"
+ accesskey="&refreshNow.accesskey;"
+ oncommand="refreshList(); focusSearch();"/>
+ </row>
+ </rows>
+ </grid>
+ </hbox>
+ <deck id="bottomPanel" flex="1" selectedindex="0">
+ <hbox pack="center" align="center">
+ <label value="&network.hint.label;"/>
+ </hbox>
+ <vbox>
+ <tree id="channels" flex="1" hidecolumnpicker="true" seltype="single" tabindex="8"
+ onselect="onSelectionChange()">
+ <treecols>
+ <treecol label="&col.name;" width="100" id="chanColName"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.users;" width="50" id="chanColUsers"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.topic;" flex="1" id="chanColTopic"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ <hbox id="loadContainer">
+ <label id="loadLabel" flex="1" crop="right"/>
+ <deck id="loadBarDeck">
+ <progressmeter id="loadBar" mode="undetermined" />
+ <box/>
+ </deck>
+ </hbox>
+ </vbox>
+ </deck>
+ </vbox>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/chatzilla.xul b/comm/suite/chatzilla/xul/content/chatzilla.xul
new file mode 100644
index 0000000000..a543c1e6ae
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/chatzilla.xul
@@ -0,0 +1,141 @@
+<?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 window SYSTEM "chrome://chatzilla/locale/chatzilla.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/skin/chatzilla.css" type="text/css"?>
+
+<?xul-overlay href="chrome://chatzilla/content/scripts.xul"?>
+<?xul-overlay href="chrome://chatzilla/content/popups.xul"?>
+<?xul-overlay href="chrome://chatzilla/content/menus.xul"?>
+
+<window id="chatzilla-window"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:aaa="http://www.w3.org/2005/07/aaa"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ orient="vertical" onload="onLoad();" onunload="onUnload();"
+ onclose="return onClose();" onmouseover="onMouseOver(event);"
+ persist="width height screenX screenY sizemode" windowtype="irc:chatzilla">
+
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+ <script src="chrome://communicator/content/findUtils.js"/>
+
+ <html:link rel="icon" href="chrome://chatzilla/skin/images/logo.png" style="display:none"/>
+
+ <overlaytarget id="scripts-overlay-target"/>
+ <overlaytarget id="popup-overlay-target"/>
+ <overlaytarget id="menu-overlay-target"/>
+ <stringbundleset id="chatzilla-stringbundle">
+ <stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
+ </stringbundleset>
+
+ <vbox id="upper-box" flex="1">
+ <hbox id="tabpanels-contents-box" flex="1">
+ <vbox id="user-list-box" width="125" persist="collapsed width">
+
+ <tree id="user-list" flex="1" hidecolumnpicker="true"
+ ondblclick="onUserDoubleClick(event);"
+ context="context:userlist" aaa:live="off" aria-live="off"
+ aaa:relevant="additions removals text"
+ aria-relevant="additions removals text">
+ <treecols>
+ <treecol id="usercol" hideheader="true" flex="1"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ </vbox> <!-- user-list-box -->
+
+ <splitter id="main-splitter" collapse="before" persist="collapsed left">
+ <grippy/>
+ </splitter>
+
+ <vbox flex="1" id="browser-box">
+ <deck id="output-deck" flex="1"/>
+ </vbox>
+
+ </hbox> <!-- tabpanels-contents-box -->
+
+ <vbox id="tabstrip-box" flex="0" crop="right">
+ <hbox id="view-tabs" persist="collapsed" flex="1"
+ ondragover="tabsDNDObserver.onDragOver(event);"
+ ondragexit="tabsDNDObserver.onDragExit(event);"
+ ondrop="tabsDNDObserver.onDrop(event);">
+ <tabs id="views-tbar-inner" flex="1"
+ onselect="onTabSelect(event)" setfocus="false">
+ <tab hidden="true"/> <!-- dummy tab to keep the freaking xbl from
+ causing an exception -->
+ </tabs>
+ <spacer id="views-tbar-spacer"/>
+ </hbox>
+ <hbox id="tabs-drop-indicator-bar" collapsed="true">
+ <hbox id="tabs-drop-indicator" mousethrough="always"/>
+ </hbox>
+ </vbox>
+
+ </vbox> <!-- upper-box -->
+
+ <splitter id="input-splitter" orient="vertical" collapse="after"
+ collapsed="true"/>
+
+ <hbox id="input-widgets" align="center">
+ <button id="server-nick" type="menu" label="" tooltiptext="&server-nick.tooltip;"/>
+ <hbox id="multiline-box" flex="1" collapsed="true">
+ <box id="multiline-hug-box" flex="1">
+ <textbox id="multiline-input" multiline="true" flex="1" height="100px"
+ class="multiline-input-widget" onfocus="onInputFocus();"
+ tabindex="-1"/>
+ </box>
+ <vbox>
+ <toolbarbutton id="button-input" flex="1"
+ oncommand="onMultilineSend(event);"
+ tooltiptext="&multiline-send.tooltip;" />
+ <toolbarbutton id="button-multiline-contract"
+ oncommand="dispatch('pref multiline false');"
+ tooltiptext="&multiline-contract.tooltip;" />
+ </vbox>
+ </hbox>
+ <hbox id="singleline-box" flex="1" collapsed="true">
+ <box id="singleline-hug-box" flex="1">
+ <textbox id="input" class="input-widget" flex="1"
+ onfocus="onInputFocus();" tabindex="-1"/>
+ </box>
+ <toolbarbutton id="button-multiline-expand"
+ oncommand="dispatch('pref multiline true');"
+ tooltiptext="&multiline-expand.tooltip;"/>
+ </hbox>
+ </hbox>
+
+ <statusbar id="status-bar"
+ class="chromeclass-status"
+ persist="collapsed">
+ <statusbarpanel id="component-bar"/>
+ <statusbarpanel id="status-text" label="" flex="1" crop="right"/>
+ <statusbarpanel id="status-progress-panel" class="statusbarpanel-progress">
+ <progressmeter id="status-progress-bar"
+ class="progressmeter-statusbar"
+ mode="undetermined"
+ value="0"/>
+ </statusbarpanel>
+ <statusbarpanel id="security-button"
+ class="statusbarpanel-iconic-text"
+ dir="reverse"
+ label=""
+ oncommand="displayCertificateInfo();"/>
+ <statusbarpanel id="alert-status"
+ class="statusbarpanel-iconic"
+ oncommand="updateAlertIcon(true);"/>
+ <statusbarpanel id="logging-status"
+ class="statusbarpanel-iconic"
+ oncommand="onLoggingIcon();"/>
+ <statusbarpanel id="offline-status"
+ class="statusbarpanel-iconic"
+ oncommand="client.offlineObserver.toggleOffline();"/>
+ </statusbar>
+
+</window>
diff --git a/comm/suite/chatzilla/xul/content/chatzillaOverlay.js b/comm/suite/chatzilla/xul/content/chatzillaOverlay.js
new file mode 100644
index 0000000000..7841b40f20
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/chatzillaOverlay.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/. */
+
+function toIRC()
+{
+
+ toOpenWindowByType("irc:chatzilla", "chrome://chatzilla/content/chatzilla.xul");
+
+}
+
diff --git a/comm/suite/chatzilla/xul/content/chatzillaOverlay.xul b/comm/suite/chatzilla/xul/content/chatzillaOverlay.xul
new file mode 100644
index 0000000000..755e4fd813
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/chatzillaOverlay.xul
@@ -0,0 +1,42 @@
+<?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 SYSTEM "chrome://chatzilla/locale/chatzillaOverlay.dtd" >
+
+<!-- This is the overlay that addes "Chatzilla" to the (global) task menu. -->
+
+<?xml-stylesheet href="chrome://chatzilla/skin/chatzillaOverlay.css" type="text/css"?>
+
+<overlay id="ChatzillaTaskMenuID"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script type="application/x-javascript" src="chrome://chatzilla/content/chatzillaOverlay.js"/>
+
+<keyset id="tasksKeys">
+ <key id="key_irc" key="&ircCmd.commandkey;" command="Tasks:IRC" modifiers="accel"/>
+</keyset>
+
+<commandset id="tasksCommands">
+ <command id="Tasks:IRC" oncommand="toIRC();"/>
+</commandset>
+
+<menupopup id="windowPopup">
+ <menuitem
+ label="&ircCmd.label;"
+ accesskey="&ircCmd.accesskey;"
+ key="key_irc"
+ command="Tasks:IRC"
+ id="tasksMenuIRC"
+ class="menuitem-iconic"
+ insertafter="tasksMenuAddressBook,tasksMenuEditor,IMMenuItem,tasksMenuNavigator"/>
+</menupopup>
+
+<statusbarpanel id="component-bar">
+ <toolbarbutton class="taskbutton" id="mini-irc" oncommand="toIRC()"
+ insertafter="mini-addr,mini-comp,mini-aim,mini-nav" tooltiptext="&ircCmd.label;"/>
+</statusbarpanel>
+
+</overlay>
diff --git a/comm/suite/chatzilla/xul/content/commands.js b/comm/suite/chatzilla/xul/content/commands.js
new file mode 100644
index 0000000000..a99ca52c3e
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/commands.js
@@ -0,0 +1,4760 @@
+/* -*- Mode: C++; 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/. */
+
+const CMD_CONSOLE = 0x01;
+const CMD_NEED_NET = 0x02;
+const CMD_NEED_SRV = 0x04;
+const CMD_NEED_CHAN = 0x08;
+const CMD_NEED_USER = 0x10;
+
+function initCommands()
+{
+ // Keep this in sync with the command.js section in chatzilla.properties.
+ var cmdary =
+ [/* "real" commands */
+ ["about", cmdAbout, CMD_CONSOLE],
+ ["alias", cmdAlias, CMD_CONSOLE,
+ "[<alias-name> [<command-list>]]"],
+ ["attach", cmdAttach, CMD_CONSOLE,
+ "<irc-url>"],
+ ["away", cmdAway, CMD_CONSOLE,
+ "[<reason>]"],
+ ["back", cmdAway, CMD_CONSOLE],
+ ["ban", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE,
+ "[<nickname>]"],
+ ["cancel", cmdCancel, CMD_CONSOLE],
+ ["charset", cmdCharset, CMD_CONSOLE,
+ "[<new-charset>]"],
+ ["channel-motif", cmdMotif, CMD_NEED_CHAN | CMD_CONSOLE,
+ "[<motif> [<channel>]]"],
+ ["channel-pref", cmdPref, CMD_NEED_CHAN | CMD_CONSOLE,
+ "[<pref-name> [<pref-value>]]"],
+ ["cmd-undo", "cmd-docommand cmd_undo", 0],
+ ["cmd-redo", "cmd-docommand cmd_redo", 0],
+ ["cmd-cut", "cmd-docommand cmd_cut", 0],
+ ["cmd-copy", "cmd-docommand cmd_copy", 0],
+ ["cmd-paste", "cmd-docommand cmd_paste", 0],
+ ["cmd-delete", "cmd-docommand cmd_delete", 0],
+ ["cmd-selectall", "cmd-docommand cmd_selectAll", 0],
+ ["cmd-copy-link-url", "cmd-docommand cmd_copyLink", 0,
+ "<url>"],
+ ["cmd-mozilla-prefs", "cmd-docommand cmd_mozillaPrefs", 0],
+ ["cmd-prefs", "cmd-docommand cmd_chatzillaPrefs", 0],
+ ["cmd-chatzilla-prefs", "cmd-docommand cmd_chatzillaPrefs", 0],
+ ["cmd-chatzilla-opts", "cmd-docommand cmd_chatzillaPrefs", 0],
+ ["cmd-docommand", cmdDoCommand, 0,
+ "<cmd-name>"],
+ ["create-tab-for-view", cmdCreateTabForView, 0,
+ "<view>"],
+ ["custom-away", cmdAway, 0],
+ ["op", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["dcc-accept", cmdDCCAccept, CMD_CONSOLE,
+ "[<nickname> [<type> [<file>]]]"],
+ ["dcc-accept-list", cmdDCCAutoAcceptList, CMD_NEED_NET | CMD_CONSOLE],
+ ["dcc-accept-list-add", cmdDCCAutoAcceptAdd,
+ CMD_NEED_NET | CMD_CONSOLE,
+ "<nickname>"],
+ ["dcc-accept-list-remove", cmdDCCAutoAcceptDel,
+ CMD_NEED_NET | CMD_CONSOLE,
+ "<nickname>"],
+ ["dcc-chat", cmdDCCChat, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<nickname>]"],
+ ["dcc-close", cmdDCCClose, CMD_CONSOLE,
+ "[<nickname> [<type> [<file>]]]"],
+ ["dcc-decline", cmdDCCDecline, CMD_CONSOLE,
+ "[<nickname>]"],
+ ["dcc-list", cmdDCCList, CMD_CONSOLE,
+ "[<type>]"],
+ ["dcc-send", cmdDCCSend, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<nickname> [<file>]]"],
+ ["dcc-show-file", cmdDCCShowFile, CMD_CONSOLE,
+ "<file>"],
+ ["delayed", cmdDelayed, CMD_CONSOLE,
+ "<delay> <rest>"],
+ ["deop", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["describe", cmdDescribe, CMD_NEED_SRV | CMD_CONSOLE,
+ "<target> <action>"],
+ ["hop", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["dehop", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["voice", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["devoice", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["clear-view", cmdClearView, CMD_CONSOLE,
+ "[<view>]"],
+ ["client", cmdClient, CMD_CONSOLE],
+ ["commands", cmdCommands, CMD_CONSOLE,
+ "[<pattern>]"],
+ ["ctcp", cmdCTCP, CMD_NEED_SRV | CMD_CONSOLE,
+ "<target> <code> [<params>]"],
+ ["default-charset", cmdCharset, CMD_CONSOLE,
+ "[<new-charset>]"],
+ ["delete-view", cmdDeleteView, CMD_CONSOLE,
+ "[<view>]"],
+ ["desc", cmdDesc, CMD_CONSOLE,
+ "[<description>]"],
+ ["disable-plugin", cmdDisablePlugin, CMD_CONSOLE],
+ ["disconnect", cmdDisconnect, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<reason>]"],
+ ["disconnect-all", cmdDisconnectAll, CMD_CONSOLE,
+ "[<reason>]"],
+ ["echo", cmdEcho, CMD_CONSOLE,
+ "<message>"],
+ ["edit-networks", cmdEditNetworks, CMD_CONSOLE],
+ ["enable-plugin", cmdEnablePlugin, CMD_CONSOLE,
+ "<plugin>"],
+ ["eval", cmdEval, CMD_CONSOLE,
+ "<expression>"],
+ ["evalsilent", cmdEval, CMD_CONSOLE,
+ "<expression>"],
+ ["except", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE,
+ "[<nickname>]"],
+ ["find", cmdFind, 0,
+ "[<rest>]"],
+ ["find-again", cmdFindAgain, 0],
+ ["focus-input", cmdFocusInput, 0],
+ ["font-family", cmdFont, CMD_CONSOLE,
+ "[<font>]"],
+ ["font-family-other", cmdFont, 0],
+ ["font-size", cmdFont, CMD_CONSOLE,
+ "[<font-size>]"],
+ ["font-size-other", cmdFont, 0],
+ ["goto-startup", cmdGotoStartup, CMD_CONSOLE],
+ ["goto-url", cmdGotoURL, 0,
+ "<url> [<anchor>]"],
+ ["goto-url-newwin", cmdGotoURL, 0,
+ "<url> [<anchor>]"],
+ ["goto-url-newtab", cmdGotoURL, 0,
+ "<url> [<anchor>]"],
+ ["help", cmdHelp, CMD_CONSOLE,
+ "[<pattern>]"],
+ ["hide-view", cmdHideView, CMD_CONSOLE,
+ "[<view>]"],
+ ["identify", cmdIdentify, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<password>]"],
+ ["idle-away", cmdAway, 0],
+ ["idle-back", cmdAway, 0],
+ ["ignore", cmdIgnore, CMD_NEED_NET | CMD_CONSOLE,
+ "[<mask>]"],
+ ["input-text-direction", cmdInputTextDirection, 0,
+ "<dir>"],
+ ["install-plugin", cmdInstallPlugin, CMD_CONSOLE,
+ "[<url> [<name>]]"],
+ ["invite", cmdInvite, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<channel-name>]"],
+ ["join", cmdJoin, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<channel-name> [<key>]]"],
+ ["join-charset", cmdJoin, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<channel-name> <charset> [<key>]]"],
+ ["jump-to-anchor", cmdJumpToAnchor, CMD_NEED_NET,
+ "<anchor> [<channel-name>]"],
+ ["kick", cmdKick, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<reason>]"],
+ ["kick-ban", cmdKick, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname> [<reason>]"],
+ ["knock", cmdKnock, CMD_NEED_SRV | CMD_CONSOLE,
+ "<channel-name> [<reason>]"],
+ ["leave", cmdLeave, CMD_NEED_NET | CMD_CONSOLE,
+ "[<channel-name>] [<reason>]"],
+ ["links", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE],
+ ["list", cmdList, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<channel-name>]"],
+ ["list-plugins", cmdListPlugins, CMD_CONSOLE,
+ "[<plugin>]"],
+ ["load", cmdLoad, CMD_CONSOLE,
+ "<url>"],
+ ["log", cmdLog, CMD_CONSOLE,
+ "[<state>]"],
+ ["map", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE],
+ ["marker", cmdMarker, CMD_CONSOLE],
+ ["marker-clear", cmdMarker, CMD_CONSOLE],
+ ["marker-set", cmdMarker, CMD_CONSOLE],
+ ["match-users", cmdMatchUsers, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<mask>"],
+ ["me", cmdMe, CMD_CONSOLE,
+ "<action>"],
+ ["motd", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE],
+ ["mode", cmdMode, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<target>] [<modestr> [<param> [<...>]]]"],
+ ["motif", cmdMotif, CMD_CONSOLE,
+ "[<motif>]"],
+ ["msg", cmdMsg, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> <message>"],
+ ["name", cmdName, CMD_CONSOLE,
+ "[<username>]"],
+ ["names", cmdNames, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<channel-name>]"],
+ ["network", cmdNetwork, CMD_CONSOLE,
+ "<network-name>"],
+ ["network-motif", cmdMotif, CMD_NEED_NET | CMD_CONSOLE,
+ "[<motif> [<network>]]"],
+ ["network-pref", cmdPref, CMD_NEED_NET | CMD_CONSOLE,
+ "[<pref-name> [<pref-value>]]"],
+ ["networks", cmdNetworks, CMD_CONSOLE],
+ ["nick", cmdNick, CMD_CONSOLE,
+ "[<nickname>]"],
+ ["notice", cmdNotice, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> <message>"],
+ ["notify", cmdNotify, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<nickname> [<...>]]"],
+ ["open-at-startup", cmdOpenAtStartup, CMD_CONSOLE,
+ "[<toggle>]"],
+ ["oper", cmdOper, CMD_NEED_SRV | CMD_CONSOLE,
+ "<opername> [<password>]"],
+ ["ping", cmdPing, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname>"],
+ ["plugin-pref", cmdPref, CMD_CONSOLE,
+ "<plugin> [<pref-name> [<pref-value>]]"],
+ ["pref", cmdPref, CMD_CONSOLE,
+ "[<pref-name> [<pref-value>]]"],
+ ["print", cmdPrint, CMD_CONSOLE],
+ ["query", cmdQuery, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<message>]"],
+ ["quit", cmdQuit, CMD_CONSOLE,
+ "[<reason>]"],
+ ["quote", cmdQuote, CMD_NEED_NET | CMD_CONSOLE,
+ "<irc-command>"],
+ ["rename", cmdRename, CMD_CONSOLE,
+ "[<label>]"],
+ ["reload-plugin", cmdReload, CMD_CONSOLE,
+ "<plugin>"],
+ ["rlist", cmdRlist, CMD_NEED_SRV | CMD_CONSOLE,
+ "<regexp>"],
+ ["reconnect", cmdReconnect, CMD_NEED_NET | CMD_CONSOLE,
+ "[<reason>]"],
+ ["reconnect-all", cmdReconnectAll, CMD_CONSOLE,
+ "[<reason>]"],
+ ["rejoin", cmdRejoin,
+ CMD_NEED_SRV | CMD_NEED_CHAN | CMD_CONSOLE,
+ "[<reason>]"],
+ ["reload-ui", cmdReloadUI, 0],
+ ["save", cmdSave, CMD_CONSOLE,
+ "[<filename> [<savetype>]]"],
+ ["say", cmdSay, CMD_CONSOLE,
+ "<message>"],
+ ["server", cmdServer, CMD_CONSOLE,
+ "<hostname> [<port> [<password>]]"],
+ ["set-current-view", cmdSetCurrentView, 0,
+ "<view>"],
+ ["stats", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<params>]"],
+ ["squery", cmdSquery, CMD_NEED_SRV | CMD_CONSOLE,
+ "<service> [<commands>]"],
+ ["sslserver", cmdServer, CMD_CONSOLE,
+ "<hostname> [<port> [<password>]]"],
+ ["ssl-exception", cmdSSLException, 0,
+ "[<hostname> <port> [<connect>]]"],
+ ["stalk", cmdStalk, CMD_CONSOLE,
+ "[<text>]"],
+ ["supports", cmdSupports, CMD_NEED_SRV | CMD_CONSOLE],
+ ["sync-font", cmdSync, 0],
+ ["sync-header", cmdSync, 0],
+ ["sync-log", cmdSync, 0],
+ ["sync-motif", cmdSync, 0],
+ ["sync-timestamp", cmdSync, 0],
+ ["sync-window", cmdSync, 0],
+ ["testdisplay", cmdTestDisplay, CMD_CONSOLE],
+ ["text-direction", cmdTextDirection, 0,
+ "<dir>"],
+ ["time", cmdTime, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<nickname>]"],
+ ["timestamps", cmdTimestamps, CMD_CONSOLE,
+ "[<toggle>]"],
+ ["toggle-ui", cmdToggleUI, CMD_CONSOLE,
+ "<thing>"],
+ ["toggle-pref", cmdTogglePref, 0,
+ "<pref-name>"],
+ ["toggle-group", cmdToggleGroup, 0,
+ "<group-id>"],
+ ["topic", cmdTopic, CMD_NEED_CHAN | CMD_CONSOLE,
+ "[<new-topic>]"],
+ ["unalias", cmdAlias, CMD_CONSOLE,
+ "<alias-name>"],
+ ["unignore", cmdIgnore, CMD_NEED_NET | CMD_CONSOLE,
+ "<mask>"],
+ ["unban", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE,
+ "<nickname>"],
+ ["unexcept", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE],
+ ["uninstall-plugin", cmdUninstallPlugin, CMD_CONSOLE,
+ "<plugin>"],
+ ["unstalk", cmdUnstalk, CMD_CONSOLE,
+ "<text>"],
+ ["urls", cmdURLs, CMD_CONSOLE,
+ "[<number>]"],
+ ["user", cmdUser, CMD_CONSOLE,
+ "[<username> <description>]"],
+ ["userhost", cmdUserhost, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["userip", cmdUserip, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["usermode", cmdUsermode, CMD_CONSOLE,
+ "[<new-mode>]"],
+ ["user-motif", cmdMotif, CMD_NEED_USER | CMD_CONSOLE,
+ "[<motif> [<user>]]"],
+ ["user-pref", cmdPref, CMD_NEED_USER | CMD_CONSOLE,
+ "[<pref-name> [<pref-value>]]"],
+ ["version", cmdVersion, CMD_NEED_SRV | CMD_CONSOLE,
+ "[<nickname>]"],
+ ["websearch", cmdWebSearch, CMD_CONSOLE,
+ "<selected-text>"],
+ ["who", cmdWho, CMD_NEED_SRV | CMD_CONSOLE,
+ "<rest>"],
+ ["whois", cmdWhoIs, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+ ["whowas", cmdWhoWas, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<limit>]"],
+ ["wii", cmdWhoIsIdle, CMD_NEED_SRV | CMD_CONSOLE,
+ "<nickname> [<...>]"],
+
+ /* aliases */
+ ["exit", "quit", CMD_CONSOLE,
+ "[<reason>]"],
+ ["j", "join", CMD_CONSOLE,
+ "[<channel-name> [<key>]]"],
+ ["pass", "quote PASS", CMD_CONSOLE,
+ "<password>"],
+ ["part", "leave", CMD_CONSOLE],
+ ["raw", "quote", CMD_CONSOLE],
+ // Shortcuts to useful URLs:
+ ["faq", "goto-url-newtab faq", 0],
+ ["homepage", "goto-url-newtab homepage", 0],
+ // Used to display a nickname in the menu only.
+ ["label-user", "echo", 0,
+ "<unspecified>"],
+ ["label-user-multi", "echo", 0,
+ "<unspecified>"],
+ // These are all the font family/size menu commands...
+ ["font-family-default", "font-family default", 0],
+ ["font-family-serif", "font-family serif", 0],
+ ["font-family-sans-serif", "font-family sans-serif", 0],
+ ["font-family-monospace", "font-family monospace", 0],
+ ["font-size-default", "font-size default", 0],
+ ["font-size-small", "font-size small", 0],
+ ["font-size-medium", "font-size medium", 0],
+ ["font-size-large", "font-size large", 0],
+ ["font-size-bigger", "font-size bigger", 0],
+ // This next command is not visible; it maps to Ctrl-=, which is what
+ // you get when the user tries to do Ctrl-+ (previous command's key).
+ ["font-size-bigger2", "font-size bigger", 0],
+ ["font-size-smaller", "font-size smaller", 0],
+ ["toggle-oas", "open-at-startup toggle", 0],
+ ["toggle-ccm", "toggle-pref collapseMsgs", 0],
+ ["toggle-copy", "toggle-pref copyMessages", 0],
+ ["toggle-usort", "toggle-pref sortUsersByMode", 0],
+ ["toggle-umode", "toggle-pref showModeSymbols", 0],
+ ["toggle-timestamps","timestamps toggle", 0],
+ ["motif-dark", "motif dark", 0],
+ ["motif-light", "motif light", 0],
+ ["sync-output", "evalsilent syncOutputFrame(this)", 0],
+ ["userlist", "toggle-ui userlist", CMD_CONSOLE],
+ ["tabstrip", "toggle-ui tabstrip", CMD_CONSOLE],
+ ["statusbar", "toggle-ui status", CMD_CONSOLE],
+ ["header", "toggle-ui header", CMD_CONSOLE],
+
+ // text-direction aliases
+ ["rtl", "text-direction rtl", CMD_CONSOLE],
+ ["ltr", "text-direction ltr", CMD_CONSOLE],
+ ["toggle-text-dir", "text-direction toggle", 0],
+ ["irtl", "input-text-direction rtl", CMD_CONSOLE],
+ ["iltr", "input-text-direction ltr", CMD_CONSOLE],
+ // Services aliases
+ ["cs", "quote cs", 0],
+ ["ms", "quote ms", 0],
+ ["ns", "quote ns", 0]
+ ];
+
+ // set the stringbundle associated with these commands.
+ cmdary.stringBundle = client.defaultBundle;
+
+ client.commandManager = new CommandManager(client.defaultBundle);
+ client.commandManager.defaultFlags = CMD_CONSOLE;
+ client.commandManager.isCommandSatisfied = isCommandSatisfied;
+ client.commandManager.defineCommands(cmdary);
+
+ var restList = ["reason", "action", "text", "message", "params", "font",
+ "expression", "ircCommand", "prefValue", "newTopic", "file",
+ "password", "commandList", "commands", "description",
+ "selectedText"];
+ var stateList = ["connect"];
+
+ client.commandManager.argTypes.__aliasTypes__(restList, "rest");
+ client.commandManager.argTypes.__aliasTypes__(stateList, "state");
+ client.commandManager.argTypes["plugin"] = parsePlugin;
+}
+
+function isCommandSatisfied(e, command)
+{
+ if (typeof command == "undefined")
+ command = e.command;
+ else if (typeof command == "string")
+ command = this.commands[command];
+
+ if (command.flags & CMD_NEED_USER)
+ {
+ if (!("user" in e) || !e.user)
+ {
+ e.parseError = getMsg(MSG_ERR_NEED_USER, command.name);
+ return false;
+ }
+ }
+
+ if (command.flags & CMD_NEED_CHAN)
+ {
+ if (!("channel" in e) || !e.channel)
+ {
+ e.parseError = getMsg(MSG_ERR_NEED_CHANNEL, command.name);
+ return false;
+ }
+ }
+
+ if (command.flags & CMD_NEED_SRV)
+ {
+ if (!("server" in e) || !e.server)
+ {
+ e.parseError = getMsg(MSG_ERR_NEED_SERVER, command.name);
+ return false;
+ }
+
+ if (e.network.state != NET_ONLINE)
+ {
+ e.parseError = MSG_ERR_NOT_CONNECTED;
+ return false;
+ }
+ }
+
+ if (command.flags & (CMD_NEED_NET | CMD_NEED_SRV | CMD_NEED_CHAN))
+ {
+ if (!("network" in e) || !e.network)
+ {
+ e.parseError = getMsg(MSG_ERR_NEED_NETWORK, command.name);
+ return false;
+ }
+ }
+
+ return CommandManager.prototype.isCommandSatisfied(e, command);
+}
+
+CIRCChannel.prototype.dispatch =
+CIRCNetwork.prototype.dispatch =
+CIRCUser.prototype.dispatch =
+CIRCDCCChat.prototype.dispatch =
+CIRCDCCFileTransfer.prototype.dispatch =
+client.dispatch =
+function this_dispatch(text, e, isInteractive, flags)
+{
+ e = getObjectDetails(this, e);
+ return dispatch(text, e, isInteractive, flags);
+}
+
+function dispatch(text, e, isInteractive, flags)
+{
+ if (typeof isInteractive == "undefined")
+ isInteractive = false;
+
+ if (!e)
+ e = new Object();
+
+ if (!("sourceObject" in e))
+ e.__proto__ = getObjectDetails(client.currentObject);
+
+ if (!("isInteractive" in e))
+ e.isInteractive = isInteractive;
+
+ if (!("inputData" in e))
+ e.inputData = "";
+
+ /* split command from arguments */
+ var ary = text.match(/(\S+) ?(.*)/);
+ if (!ary)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_COMMAND, ""));
+ return null;
+ }
+
+ e.commandText = ary[1];
+ if (ary[2])
+ e.inputData = stringTrim(ary[2]);
+
+ /* list matching commands */
+ ary = client.commandManager.list(e.commandText, flags, true);
+ var rv = null;
+ var i;
+
+ switch (ary.length)
+ {
+ case 0:
+ /* no match, try again */
+ if (e.server && e.server.isConnected &&
+ client.prefs["guessCommands"])
+ {
+ /* Want to keep the source details. */
+ var e2 = getObjectDetails(e.sourceObject);
+ e2.inputData = e.commandText + " " + e.inputData;
+ return dispatch("quote", e2);
+ }
+
+ display(getMsg(MSG_ERR_UNKNOWN_COMMAND, e.commandText), MT_ERROR);
+ break;
+
+ case 1:
+ /* one match, good for you */
+ var cm = client.commandManager;
+
+ if (cm.currentDispatchDepth >= cm.maxDispatchDepth)
+ {
+ /* We've reatched the max dispatch depth, so we need to unwind
+ * the entire stack of commands.
+ */
+ cm.dispatchUnwinding = true;
+ }
+ // Don't start any new commands while unwinding.
+ if (cm.dispatchUnwinding)
+ break;
+
+ cm.currentDispatchDepth++;
+
+ var ex;
+ try
+ {
+ rv = dispatchCommand(ary[0], e, flags);
+ }
+ catch (ex)
+ {
+ display(getMsg(MSG_ERR_INTERNAL_DISPATCH, ary[0].name),
+ MT_ERROR);
+ display(formatException(ex), MT_ERROR);
+ if (typeof ex == "object" && "stack" in ex)
+ dd(formatException(ex) + "\n" + ex.stack);
+ else
+ dd(formatException(ex), MT_ERROR);
+ }
+
+ cm.currentDispatchDepth--;
+ if (cm.dispatchUnwinding && (cm.currentDispatchDepth == 0))
+ {
+ /* Last level to unwind, and this is where we display the
+ * message. We need to leave it until here because displaying
+ * a message invokes a couple of commands itself, and we need
+ * to not be right on the dispatch limit for that.
+ */
+ cm.dispatchUnwinding = false;
+ display(getMsg(MSG_ERR_MAX_DISPATCH_DEPTH, ary[0].name),
+ MT_ERROR);
+ }
+ break;
+
+ default:
+ /* more than one match, show the list */
+ var str = "";
+ for (i in ary)
+ str += (str) ? ", " + ary[i].name : ary[i].name;
+ display(getMsg(MSG_ERR_AMBIGCOMMAND,
+ [e.commandText, ary.length, str]), MT_ERROR);
+ }
+
+ return rv;
+}
+
+function dispatchCommand (command, e, flags)
+{
+ function displayUsageError (e, details)
+ {
+ if (!("isInteractive" in e) || !e.isInteractive)
+ {
+ var caller = Components.stack.caller.caller;
+ if (caller.name == "dispatch")
+ caller = caller.caller;
+ var error = new Error (details);
+ error.fileName = caller.filename;
+ error.lineNumber = caller.lineNumber;
+ error.name = caller.name;
+ display (formatException(error), MT_ERROR);
+ }
+ else
+ {
+ display (details, MT_ERROR);
+ }
+
+ //display (getMsg(MSG_FMT_USAGE, [e.command.name, e.command.usage]),
+ // MT_USAGE);
+ return null;
+ };
+
+ function callHooks (command, isBefore)
+ {
+ var names, hooks;
+
+ if (isBefore)
+ hooks = command.beforeHooks;
+ else
+ hooks = command.afterHooks;
+
+ for (var h in hooks)
+ {
+ if ("dbgDispatch" in client && client.dbgDispatch)
+ {
+ dd ("calling " + (isBefore ? "before" : "after") +
+ " hook " + h);
+ }
+ try
+ {
+ hooks[h](e);
+ }
+ catch (ex)
+ {
+ if (e.command.name != "hook-session-display")
+ {
+ display(getMsg(MSG_ERR_INTERNAL_HOOK, h), MT_ERROR);
+ display(formatException(ex), MT_ERROR);
+ }
+ else
+ {
+ dd(getMsg(MSG_ERR_INTERNAL_HOOK, h));
+ }
+
+ dd("Caught exception calling " +
+ (isBefore ? "before" : "after") + " hook " + h);
+ dd(formatException(ex));
+ if (typeof ex == "object" && "stack" in ex)
+ dd(ex.stack);
+ else
+ dd(getStackTrace());
+ }
+ }
+ };
+
+ e.command = command;
+
+ if (!e.command.enabled)
+ {
+ /* disabled command */
+ display (getMsg(MSG_ERR_DISABLED, e.command.name),
+ MT_ERROR);
+ return null;
+ }
+
+ function parseAlias(aliasLine, e) {
+ /* Only 1 of these will be presented to the user. Math.max is used to
+ supply the 'worst' error */
+ const ALIAS_ERR_REQ_PRMS = 1;
+ const ALIAS_ERR_REQ_SRV = 2;
+ const ALIAS_ERR_REQ_RECIP = 3;
+
+ /* double slashes because of the string to regexp conversion, which
+ turns these into single slashes */
+ const SIMPLE_REPLACE = "\\$\\((\\d+)\\)";
+ const CUMUL_REPLACE = "\\$\\((\\d+)\\+\\)";
+ const RANGE_REPLACE = "\\$\\((\\d+)\\-(\\d+)\\)";
+ const NICK_REPLACE = "\\$\\((nick)\\)";
+ const RECIP_REPLACE = "\\$\\((recip)\\)";
+ const ALL_REPLACE = "\\$\\((all)\\)";
+ if (!aliasLine.match(/\$/))
+ {
+ if (e.inputData)
+ display(getMsg(MSG_EXTRA_PARAMS, e.inputData), MT_WARN);
+ return aliasLine;
+ }
+
+ function replaceAll(match, single, cumulative, start, end, nick, recip, all)
+ {
+ if (single)
+ {
+ // Simple 1-parameter replace
+ if (arrayHasElementAt(parameters, single - 1))
+ {
+ paramsUsed = Math.max(paramsUsed, single);
+ return parameters[single-1];
+ }
+ maxParamsAsked = Math.max(maxParamsAsked, single);
+ errorMsg = Math.max(ALIAS_ERR_REQ_PRMS, errorMsg);
+ return match;
+ }
+ if (cumulative)
+ {
+ // Cumulative Replace: parameters cumulative and up
+ if (arrayHasElementAt(parameters, cumulative - 1))
+ {
+ paramsUsed = parameters.length;
+ // there are never leftover parameters for $(somenumber+)
+ return parameters.slice(cumulative - 1).join(" ");
+ }
+ maxParamsAsked = Math.max(maxParamsAsked, cumulative);
+ errorMsg = Math.max(ALIAS_ERR_REQ_PRMS, errorMsg);
+ return match;
+ }
+ if (start && end)
+ {
+ // Ranged replace: parameters start through end
+ //'decrement to correct 0-based index.
+ if (start > end)
+ {
+ var iTemp = end;
+ end = start;
+ start = iTemp;
+ // We obviously have a very stupid user, but we're nice
+ }
+ start--;
+ if (arrayHasElementAt(parameters, start) &&
+ arrayHasElementAt(parameters, end - 1))
+ {
+ paramsUsed = Math.max(paramsUsed,end);
+ return parameters.slice(start, end).join(" ");
+ }
+ maxParamsAsked = Math.max(maxParamsAsked, end);
+ errorMsg = Math.max(ALIAS_ERR_REQ_PRMS, errorMsg);
+ return match;
+ }
+ if (nick)
+ {
+ // Replace with own nickname
+ if (e.network && e.server && e.network.state == NET_ONLINE)
+ return e.server.me.unicodeName;
+
+ errorMsg = Math.max(ALIAS_ERR_REQ_SRV, errorMsg);
+ return null;
+ }
+ if (recip)
+ {
+ // Replace with current recipient
+ if (e.channel)
+ return e.channel.unicodeName;
+
+ if (e.user)
+ return e.user.unicodeName;
+
+ errorMsg = ALIAS_ERR_REQ_RECIP;
+ return null;
+ }
+ // Replace with all parameters
+ paramsUsed = parameters.length;
+ return parameters.join(" ");
+ };
+
+ // If the replace function has a problem, this is an error constant:
+ var errorMsg = 0;
+
+ var paramsUsed = 0;
+ var maxParamsAsked = 0;
+
+ /* set parameters array and escaping \ and ; in parameters so the
+ * parameters don't get split up by the command list split later on */
+ e.inputData = e.inputData.replace(/([\\;])/g, "\\$1");
+ var parameters = e.inputData.match(/\S+/g);
+ if (!parameters)
+ parameters = [];
+
+ // replace in the command line.
+ var expr = [SIMPLE_REPLACE, CUMUL_REPLACE, RANGE_REPLACE, NICK_REPLACE,
+ RECIP_REPLACE, ALL_REPLACE].join("|");
+ aliasLine = aliasLine.replace(new RegExp(expr, "gi"), replaceAll);
+
+ if (errorMsg)
+ {
+ switch (errorMsg)
+ {
+ case ALIAS_ERR_REQ_PRMS:
+ display(getMsg(MSG_ERR_REQUIRED_NR_PARAM,
+ [maxParamsAsked - parameters.length,
+ maxParamsAsked]), MT_ERROR);
+ break;
+ case ALIAS_ERR_REQ_SRV:
+ display(getMsg(MSG_ERR_NEED_SERVER, e.command.name),
+ MT_ERROR);
+ break;
+ case ALIAS_ERR_REQ_RECIP:
+ display(getMsg(MSG_ERR_NEED_RECIP, e.command.name),
+ MT_ERROR);
+ break;
+ }
+ return null;
+ }
+
+ // return the revised command line.
+ if (paramsUsed < parameters.length)
+ {
+ var pmstring = parameters.slice(paramsUsed,
+ parameters.length).join(" ");
+ display(getMsg(MSG_EXTRA_PARAMS, pmstring), MT_WARN);
+ }
+ return aliasLine;
+ };
+
+ function callBeforeHooks()
+ {
+ if ("beforeHooks" in client.commandManager)
+ callHooks(client.commandManager, true);
+ if ("beforeHooks" in e.command)
+ callHooks(e.command, true);
+ };
+
+ function callAfterHooks()
+ {
+ if ("afterHooks" in e.command)
+ callHooks(e.command, false);
+ if ("afterHooks" in client.commandManager)
+ callHooks(client.commandManager, false);
+ };
+
+ var h, i;
+
+ if (typeof e.command.func == "function")
+ {
+ /* dispatch a real function */
+
+ client.commandManager.parseArguments(e);
+ if ("parseError" in e)
+ return displayUsageError(e, e.parseError);
+
+ if (("dbgDispatch" in client) && client.dbgDispatch)
+ {
+ var str = "";
+ for (i = 0; i < e.command.argNames.length; ++i)
+ {
+ var name = e.command.argNames[i];
+ if (name in e)
+ str += " " + name + ": " + e[name];
+ else if (name != ":")
+ str += " ?" + name;
+ }
+ dd(">>> " + e.command.name + str + " <<<");
+ }
+
+ callBeforeHooks();
+ try
+ {
+ e.returnValue = e.command.func(e);
+ }
+ finally
+ {
+ callAfterHooks();
+ /* set client.lastEvent *after* dispatching, so the dispatched
+ * function actually get's a chance to see the last event. */
+ if (("dbgDispatch" in client) && client.dbgDispatch)
+ client.lastEvent = e;
+ }
+ }
+ else if (typeof e.command.func == "string")
+ {
+ /* dispatch an alias (semicolon delimited list of subcommands) */
+
+ var commandList;
+ //Don't make use of e.inputData if we have multiple commands in 1 alias
+ if (e.command.func.match(/\$\(.*\)|(?:^|[^\\])(?:\\\\)*;/))
+ commandList = parseAlias(e.command.func, e);
+ else
+ commandList = e.command.func + " " + e.inputData;
+
+ if (commandList == null)
+ return null;
+ commandList = commandList.split(";");
+
+ i = 0;
+ while (i < commandList.length) {
+ if (commandList[i].match(/(?:^|[^\\])(?:\\\\)*$/) ||
+ (i == commandList.length - 1))
+ {
+ commandList[i] = commandList[i].replace(/\\(.)/g, "$1");
+ i++;
+ }
+ else
+ {
+ commandList[i] = commandList[i] + ";" + commandList[i + 1];
+ commandList.splice(i + 1, 1);
+ }
+ }
+
+ callBeforeHooks();
+ try
+ {
+ for (i = 0; i < commandList.length; ++i)
+ {
+ var newEvent = Clone(e);
+ delete newEvent.command;
+ commandList[i] = stringTrim(commandList[i]);
+ dispatch(commandList[i], newEvent, flags);
+ }
+ }
+ finally
+ {
+ callAfterHooks();
+ }
+ }
+ else
+ {
+ display(getMsg(MSG_ERR_NOTIMPLEMENTED, e.command.name), MT_ERROR);
+ return null;
+ }
+
+ return ("returnValue" in e) ? e.returnValue : null;
+}
+
+/* parse function for <plugin> parameters */
+function parsePlugin(e, name)
+{
+ var ary = e.unparsedData.match(/(?:(\S+))(?:\s+(.*))?$/);
+ if (!ary)
+ return false;
+
+ var plugin;
+
+ if (ary[1])
+ {
+ plugin = getPluginById(ary[1]);
+ if (!plugin)
+ return false;
+
+ }
+
+ e.unparsedData = ary[2] || "";
+ e[name] = plugin;
+ return true;
+}
+
+function getToggle (toggle, currentState)
+{
+ if (toggle == "toggle")
+ toggle = !currentState;
+
+ return toggle;
+}
+
+/******************************************************************************
+ * command definitions from here on down.
+ */
+
+function cmdDisablePlugin(e)
+{
+ disablePlugin(e.plugin, false);
+}
+
+function cmdEnablePlugin(e)
+{
+ if (e.plugin.enabled)
+ {
+ display(getMsg(MSG_IS_ENABLED, e.plugin.id));
+ return;
+ }
+
+ if (e.plugin.API > 0)
+ {
+ if (!e.plugin.enable())
+ {
+ display(getMsg(MSG_CANT_ENABLE, e.plugin.id));
+ e.plugin.prefs["enabled"] = false;
+ return;
+ }
+ e.plugin.prefs["enabled"] = true;
+ }
+ else if (!("enablePlugin" in e.plugin.scope))
+ {
+ display(getMsg(MSG_CANT_ENABLE, e.plugin.id));
+ return;
+ }
+ else
+ {
+ e.plugin.scope.enablePlugin();
+ }
+
+ display(getMsg(MSG_PLUGIN_ENABLED, e.plugin.id));
+ e.plugin.enabled = true;
+}
+
+function cmdBanOrExcept(e)
+{
+ var modestr;
+ switch (e.command.name)
+ {
+ case "ban":
+ modestr = "+bbbb";
+ break;
+
+ case "unban":
+ modestr = "-bbbb";
+ break;
+
+ case "except":
+ modestr = "+eeee";
+ break;
+
+ case "unexcept":
+ modestr = "-eeee";
+ break;
+
+ default:
+ ASSERT(0, "Dispatch from unknown name " + e.command.name);
+ return;
+ }
+
+ /* If we're unbanning, or banning in odd cases, we may actually be talking
+ * about a user who is not in the channel, so we need to check the server
+ * for information as well.
+ */
+ if (!e.user && e.nickname)
+ e.user = e.channel.getUser(e.nickname);
+ if (!e.user && e.nickname)
+ e.user = e.server.getUser(e.nickname);
+
+ var masks = new Array();
+
+ if (e.userList)
+ {
+ for (var i = 0; i < e.userList.length; i++)
+ masks.push(fromUnicode(e.userList[i].getBanMask(), e.server));
+ }
+ else if (e.user)
+ {
+ // We have a real user object, so get their proper 'ban mask'.
+ masks = [fromUnicode(e.user.getBanMask(), e.server)];
+ }
+ else if (e.nickname)
+ {
+ /* If we have either ! or @ in the nickname assume the user has given
+ * us a complete mask and pass it directly, otherwise assume it is
+ * only the nickname and use * for username/host.
+ */
+ masks = [fromUnicode(e.nickname, e.server)];
+ if (!/[!@]/.test(e.nickname))
+ masks[0] = masks[0] + "!*@*";
+ }
+ else
+ {
+ // Nothing specified, so we want to list the bans/excepts.
+ masks = [""];
+ }
+
+ // Collapses into groups we can do individually.
+ masks = combineNicks(masks);
+
+ for (var i = 0; i < masks.length; i++)
+ {
+ e.server.sendData("MODE " + e.channel.encodedName + " " +
+ modestr.substr(0, masks[i].count + 1) +
+ " " + masks[i] + "\n");
+ }
+}
+
+function cmdCancel(e)
+{
+ if (e.network && e.network.isRunningList())
+ {
+ // We're running a /list, terminate the output so we return to sanity.
+ display(MSG_CANCELLING_LIST);
+ return e.network.abortList();
+ }
+
+ if (e.network && ((e.network.state == NET_CONNECTING) ||
+ (e.network.state == NET_WAITING)))
+ {
+ // We're trying to connect to a network, and want to cancel. Do so:
+ if (e.deleteWhenDone)
+ e.network.deleteWhenDone = true;
+
+ display(getMsg(MSG_CANCELLING, e.network.unicodeName));
+ return e.network.cancel();
+ }
+
+ // If we're transferring a file, abort it.
+ var source = e.sourceObject;
+ if ((source.TYPE == "IRCDCCFileTransfer") && source.isActive())
+ return source.abort();
+
+ display(MSG_NOTHING_TO_CANCEL, MT_ERROR);
+}
+
+function cmdChanUserMode(e)
+{
+ var modestr;
+ switch (e.command.name)
+ {
+ case "op":
+ modestr = "+oooo";
+ break;
+
+ case "deop":
+ modestr = "-oooo";
+ break;
+
+ case "hop":
+ modestr = "+hhhh";
+ break;
+
+ case "dehop":
+ modestr = "-hhhh";
+ break;
+
+ case "voice":
+ modestr = "+vvvv";
+ break;
+
+ case "devoice":
+ modestr = "-vvvv";
+ break;
+
+ default:
+ ASSERT(0, "Dispatch from unknown name " + e.command.name);
+ return;
+ }
+
+ var nicks;
+ var user;
+ var nickList = new Array();
+ // Prefer pre-canonicalised list, then a * passed to the command directly,
+ // then a normal list, then finally a singular item (canon. or otherwise).
+ if (e.canonNickList)
+ {
+ nicks = combineNicks(e.canonNickList);
+ }
+ else if (e.nickname && (e.nickname == "*"))
+ {
+ var me = e.server.me;
+ var mode = modestr.substr(1, 1);
+ var adding = modestr[0] == "+";
+ for (userKey in e.channel.users)
+ {
+ var user = e.channel.users[userKey];
+ /* Never change our own mode and avoid trying to change someone
+ * else in a no-op manner (e.g. voicing an already voiced user).
+ */
+ if ((user.encodedName != me.encodedName) &&
+ (arrayContains(user.modes, mode) ^ adding))
+ {
+ nickList.push(user.encodedName);
+ }
+ }
+ nicks = combineNicks(nickList);
+ }
+ else if (e.nicknameList)
+ {
+ for (var i = 0; i < e.nicknameList.length; i++)
+ {
+ user = e.channel.getUser(e.nicknameList[i]);
+ if (!user)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_USER, e.nicknameList[i]), MT_ERROR);
+ return;
+ }
+ nickList.push(user.encodedName);
+ }
+ nicks = combineNicks(nickList);
+ }
+ else if (e.nickname)
+ {
+ user = e.channel.getUser(e.nickname);
+ if (!user)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_USER, e.nickname), MT_ERROR);
+ return;
+ }
+ var str = new String(user.encodedName);
+ str.count = 1;
+ nicks = [str];
+ }
+ else
+ {
+ // Panic?
+ dd("Help! Channel user mode command with no users...?");
+ }
+
+ for (var i = 0; i < nicks.length; ++i)
+ {
+ e.server.sendData("MODE " + e.channel.encodedName + " " +
+ modestr.substr(0, nicks[i].count + 1) +
+ " " + nicks[i] + "\n");
+ }
+}
+
+function cmdCharset(e)
+{
+ var pm;
+
+ if (e.command.name == "default-charset")
+ {
+ pm = client.prefManager;
+ msg = MSG_CURRENT_CHARSET;
+ }
+ else
+ {
+ pm = e.sourceObject.prefManager;
+ msg = MSG_CURRENT_CHARSET_VIEW;
+ }
+
+ if (e.newCharset)
+ {
+ if (e.newCharset == "-")
+ {
+ pm.clearPref("charset");
+ }
+ else
+ {
+ if(!checkCharset(e.newCharset))
+ {
+ display(getMsg(MSG_ERR_INVALID_CHARSET, e.newCharset),
+ MT_ERROR);
+ return;
+ }
+ pm.prefs["charset"] = e.newCharset;
+ }
+ }
+
+ display(getMsg(msg, pm.prefs["charset"]));
+
+ // If we're on a channel, get the topic again so it can be re-decoded.
+ if (e.newCharset && e.server && e.channel)
+ e.server.sendData("TOPIC " + e.channel.encodedName + "\n");
+}
+
+function cmdCreateTabForView(e)
+{
+ return getTabForObject(e.view, true);
+}
+
+function cmdDelayed(e)
+{
+ function _dispatch()
+ {
+ // Clear inputData so that commands without arguments work properly
+ e.inputData = "";
+ dispatch(e.rest, e, e.isInteractive);
+ }
+ setTimeout(_dispatch, e.delay * 1000);
+}
+
+function cmdSync(e)
+{
+ var fun;
+
+ switch (e.command.name)
+ {
+ case "sync-font":
+ fun = function ()
+ {
+ if (view.prefs["displayHeader"])
+ view.setHeaderState(false);
+ view.changeCSS(view.getFontCSS("data"), "cz-fonts");
+ if (view.prefs["displayHeader"])
+ view.setHeaderState(true);
+ };
+ break;
+
+ case "sync-header":
+ fun = function ()
+ {
+ view.setHeaderState(view.prefs["displayHeader"]);
+ };
+ break;
+
+ case "sync-motif":
+ fun = function ()
+ {
+ view.changeCSS(view.prefs["motif.current"]);
+ updateAppMotif(view.prefs["motif.current"]);
+ // Refresh the motif settings.
+ view.updateMotifSettings();
+ };
+ break;
+
+ case "sync-timestamp":
+ fun = function ()
+ {
+ updateTimestamps(view);
+ };
+ break;
+
+ case "sync-window":
+ fun = function ()
+ {
+ if (window && window.location &&
+ window.location.href != view.prefs["outputWindowURL"])
+ {
+ syncOutputFrame(view);
+ }
+ };
+ break;
+
+ case "sync-log":
+ fun = function ()
+ {
+ if (view.prefs["log"] ^ Boolean(view.logFile))
+ {
+ if (view.prefs["log"])
+ client.openLogFile(view, true);
+ else
+ client.closeLogFile(view, true);
+ updateLoggingIcon();
+ }
+ };
+ break;
+ }
+
+ var view = e.sourceObject;
+ var window;
+ if (("frame" in view) && view.frame)
+ window = getContentWindow(view.frame);
+
+ try
+ {
+ fun();
+ }
+ catch(ex)
+ {
+ dd("Exception in " + e.command.name + " for " + e.sourceObject.unicodeName + ": " + ex);
+ }
+}
+
+function cmdSimpleCommand(e)
+{
+ e.server.sendData(e.command.name + " " + e.inputData + "\n");
+}
+
+function cmdSquery(e)
+{
+ var data;
+
+ if (e.commands)
+ data = "SQUERY " + e.service + " :" + e.commands + "\n";
+ else
+ data = "SQUERY " + e.service + "\n";
+
+ e.server.sendData(data);
+}
+
+function cmdHelp(e)
+{
+ if (!e.pattern)
+ {
+ if ("hello" in e)
+ display(MSG_HELP_INTRO, "HELLO");
+ else
+ display(MSG_HELP_INTRO);
+ return;
+ }
+
+ var ary = client.commandManager.list(e.pattern, CMD_CONSOLE, true);
+
+ if (ary.length == 0)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_COMMAND, e.pattern), MT_ERROR);
+ return;
+ }
+
+ for (var i in ary)
+ {
+ display(getMsg(MSG_FMT_USAGE, [ary[i].name, ary[i].helpUsage]),
+ MT_USAGE);
+ display(ary[i].help, MT_HELP);
+ }
+
+ return;
+}
+
+function cmdTestDisplay(e)
+{
+ startMsgGroup("testdisplay", MSG_COLLAPSE_TEST);
+ display(MSG_TEST_HELLO, MT_HELLO);
+ display(MSG_TEST_INFO, MT_INFO);
+ display(MSG_TEST_ERROR, MT_ERROR);
+ display(MSG_TEST_HELP, MT_HELP);
+ display(MSG_TEST_USAGE, MT_USAGE);
+ display(MSG_TEST_STATUS, MT_STATUS);
+
+ if (e.server && e.server.me)
+ {
+ var me = e.server.me;
+ var sampleUser = {TYPE: "IRCUser",
+ encodedName: "ircmonkey", collectionKey: ":ircmonkey",
+ unicodeName: "IRCMonkey", viewName: "IRCMonkey",
+ host: "", name: "IRCMonkey"};
+ var sampleChannel = {TYPE: "IRCChannel",
+ encodedName: "#mojo", collectionKey: ":#mojo",
+ unicodeName: "#Mojo", viewName: "#Mojo",
+ name: "#Mojo"};
+
+ function test (from, to)
+ {
+ var fromText = (from != me) ? from.TYPE + " ``" + from.name + "''" :
+ MSG_YOU;
+ var toText = (to != me) ? to.TYPE + " ``" + to.name + "''" :
+ MSG_YOU;
+
+ display (getMsg(MSG_TEST_PRIVMSG, [fromText, toText]),
+ "PRIVMSG", from, to);
+ display (getMsg(MSG_TEST_ACTION, [fromText, toText]),
+ "ACTION", from, to);
+ display (getMsg(MSG_TEST_NOTICE, [fromText, toText]),
+ "NOTICE", from, to);
+ }
+
+ test (sampleUser, me); /* from user to me */
+ test (me, sampleUser); /* me to user */
+
+ display(MSG_TEST_URL, "PRIVMSG", sampleUser, me);
+ display(MSG_TEST_STYLES, "PRIVMSG", sampleUser, me);
+ display(MSG_TEST_EMOTICON, "PRIVMSG", sampleUser, me);
+ display(MSG_TEST_RHEET, "PRIVMSG", sampleUser, me);
+ display(unescape(MSG_TEST_CTLCHR), "PRIVMSG", sampleUser, me);
+ display(unescape(MSG_TEST_COLOR), "PRIVMSG", sampleUser, me);
+ display(MSG_TEST_QUOTE, "PRIVMSG", sampleUser, me);
+
+ if (e.channel)
+ {
+ test (sampleUser, sampleChannel); /* user to channel */
+ test (me, sampleChannel); /* me to channel */
+ display(MSG_TEST_TOPIC, "TOPIC", sampleUser, sampleChannel);
+ display(MSG_TEST_JOIN, "JOIN", sampleUser, sampleChannel);
+ display(MSG_TEST_PART, "PART", sampleUser, sampleChannel);
+ display(MSG_TEST_KICK, "KICK", sampleUser, sampleChannel);
+ display(MSG_TEST_QUIT, "QUIT", sampleUser, sampleChannel);
+ display(getMsg(MSG_TEST_STALK, me.unicodeName),
+ "PRIVMSG", sampleUser, sampleChannel);
+ display(MSG_TEST_STYLES, "PRIVMSG", me, sampleChannel);
+ }
+ }
+ endMsgGroup();
+}
+
+function cmdNetwork(e)
+{
+ let network = client.getNetwork(e.networkName);
+
+ if (!network)
+ {
+ display (getMsg(MSG_ERR_UNKNOWN_NETWORK, e.networkName), MT_ERROR);
+ return;
+ }
+
+ dispatch("create-tab-for-view", { view: network });
+ dispatch("set-current-view", { view: network });
+}
+
+function cmdNetworks(e)
+{
+ var wrapper = newInlineText(MSG_NETWORKS_HEADA);
+
+ var netnames = keys(client.networks).sort();
+
+ for (let i = 0; i < netnames.length; i++)
+ {
+ let net = client.networks[netnames[i]];
+ let hasSecure = networkHasSecure(net.serverList);
+
+ var linkData = {
+ "data": net.unicodeName,
+ "href": (hasSecure ? "ircs://" : "irc://") + net.canonicalName
+ };
+ wrapper.appendChild(newInlineText(linkData, "chatzilla-link", "a"));
+
+ if (i < netnames.length - 1)
+ wrapper.appendChild(document.createTextNode(", "));
+ }
+
+ // Display an "Edit" link.
+ var spanb = document.createElementNS(XHTML_NS, "html:span");
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ var msg = getMsg(MSG_NETWORKS_HEADB2, "edit-networks");
+ client.munger.munge(msg, spanb, getObjectDetails(client.currentObject));
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ wrapper.appendChild(spanb);
+ display(wrapper, MT_INFO);
+}
+
+function cmdEditNetworks(e)
+{
+ toOpenWindowByType("irc:chatzilla:networks",
+ "chrome://chatzilla/content/networks-edit.xul",
+ "chrome,resizable,dialog", client);
+}
+
+function cmdServer(e)
+{
+ let scheme = (e.command.name == "sslserver") ? "ircs" : "irc";
+
+ var ary = e.hostname.match(/^(.*):(\d+)$/);
+ if (ary)
+ {
+ // Foolish user obviously hasn't read the instructions, but we're nice.
+ e.password = e.port;
+ e.port = ary[2];
+ e.hostname = ary[1];
+ }
+
+ gotoIRCURL({scheme: scheme, host: e.hostname, port: e.port,
+ pass: e.password, isserver: true});
+}
+
+function cmdSSLException(e)
+{
+ var opts = "chrome,centerscreen,modal";
+ var location = e.hostname ? e.hostname + ':' + e.port : undefined;
+ var args = {location: location, prefetchCert: true};
+
+ window.openDialog("chrome://pippki/content/exceptionDialog.xul",
+ "", opts, args);
+
+ if (!args.exceptionAdded)
+ return;
+
+ if (e.connect)
+ {
+ // When we come via the inline button, we just want to reconnect
+ if (e.source == "mouse")
+ dispatch("reconnect");
+ else
+ dispatch("sslserver " + e.hostname + " " + e.port);
+ }
+}
+
+
+function cmdQuit(e)
+{
+ // if we're not connected to anything, just close the window
+ if (!("getConnectionCount" in client) || (client.getConnectionCount() == 0))
+ {
+ client.userClose = true;
+ window.close();
+ return;
+ }
+
+ // Otherwise, try to close gracefully:
+ client.wantToQuit(e.reason, true);
+}
+
+function cmdDisconnect(e)
+{
+ if ((typeof e.reason != "string") || !e.reason)
+ e.reason = e.network.prefs["defaultQuitMsg"];
+ if (!e.reason)
+ e.reason = client.userAgent;
+
+ e.network.quit(e.reason);
+}
+
+function cmdDisconnectAll(e)
+{
+ var netReason;
+ if (confirmEx(MSG_CONFIRM_DISCONNECT_ALL, ["!yes", "!no"]) != 0)
+ return;
+
+ var conNetworks = client.getConnectedNetworks();
+ if (conNetworks.length <= 0)
+ {
+ display(MSG_NO_CONNECTED_NETS, MT_ERROR);
+ return;
+ }
+
+ for (var i = 0; i < conNetworks.length; i++)
+ {
+ netReason = e.reason;
+ if ((typeof netReason != "string") || !netReason)
+ netReason = conNetworks[i].prefs["defaultQuitMsg"];
+ netReason = (netReason ? netReason : client.userAgent);
+ conNetworks[i].quit(netReason);
+ }
+}
+
+function cmdDeleteView(e)
+{
+ if (!e.view)
+ e.view = e.sourceObject;
+
+ if (("lockView" in e.view) && e.view.lockView)
+ {
+ setTabState(e.view, "attention");
+ return;
+ }
+
+ if (e.view.TYPE == "IRCChannel" && e.view.joined)
+ {
+ e.view.dispatch("part", { deleteWhenDone: true });
+ return;
+ }
+
+ if (e.view.TYPE.substr(0, 6) == "IRCDCC")
+ {
+ if (e.view.isActive())
+ e.view.abort();
+ // abort() calls disconnect() if it is appropriate.
+ // Fall through: we don't delete on disconnect.
+ }
+
+ if (e.view.TYPE == "IRCNetwork" && (e.view.state == NET_CONNECTING ||
+ e.view.state == NET_WAITING))
+ {
+ e.view.dispatch("cancel", { deleteWhenDone: true });
+ return;
+ }
+
+ if (client.viewsArray.length < 2)
+ {
+ display(MSG_ERR_LAST_VIEW, MT_ERROR);
+ return;
+ }
+
+ var tb = getTabForObject(e.view);
+ if (tb)
+ {
+ var i = deleteTab (tb);
+ if (i != -1)
+ {
+ if (e.view.logFile)
+ {
+ e.view.logFile.close();
+ e.view.logFile = null;
+ }
+ delete e.view.messageCount;
+ delete e.view.messages;
+ deleteFrame(e.view);
+
+ var oldView = client.currentObject;
+ if (client.currentObject == e.view)
+ {
+ if (i >= client.viewsArray.length)
+ i = client.viewsArray.length - 1;
+ oldView = client.viewsArray[i].source
+ }
+ client.currentObject = null;
+ oldView.dispatch("set-current-view", { view: oldView });
+ }
+ }
+}
+
+function cmdHideView(e)
+{
+ if (!e.view)
+ e.view = e.sourceObject;
+
+ if (client.viewsArray.length < 2)
+ {
+ display(MSG_ERR_LAST_VIEW_HIDE, MT_ERROR);
+ return;
+ }
+
+ if ("messages" in e.view)
+ {
+ // Detach messages from output window content.
+ if (e.view.messages.parentNode)
+ e.view.messages.parentNode.removeChild(e.view.messages);
+
+ /* XXX Bug 335998: Adopt the messages into our own internal document
+ * so that when the real one the messages were in gets incorrectly
+ * GC-collected (see bug) the nodes still have an ownerDocument.
+ */
+ client.adoptNode(e.view.messages, client.hiddenDocument);
+ }
+
+ var tb = getTabForObject(e.view);
+
+ if (tb)
+ {
+ var i = deleteTab (tb);
+ if (i != -1)
+ {
+ deleteFrame(e.view);
+
+ var oldView = client.currentObject;
+ if (client.currentObject == e.view)
+ {
+ if (i >= client.viewsArray.length)
+ i = client.viewsArray.length - 1;
+ oldView = client.viewsArray[i].source
+ }
+ client.currentObject = null;
+ oldView.dispatch("set-current-view", { view: oldView });
+ }
+ }
+}
+
+function cmdClearView(e)
+{
+ if (!e.view)
+ e.view = e.sourceObject;
+
+ e.view.messages = null;
+ e.view.messageCount = 0;
+
+ e.view.displayHere(MSG_MESSAGES_CLEARED);
+
+ syncOutputFrame(e.view);
+}
+
+function cmdDesc(e)
+{
+ if (e.network != null) // somewhere on a network
+ {
+ dispatch("network-pref", {prefValue: e.description, prefName: "desc",
+ network: e.network,
+ isInteractive: e.isInteractive});
+ }
+ else // no network, change the general pref
+ {
+ dispatch("pref", {prefName: "desc", prefValue: e.description,
+ isInteractive: e.isInteractive});
+ }
+}
+
+function cmdName(e)
+{
+ if (e.network != null) // somewhere on a network
+ {
+ dispatch("network-pref", {prefName: "username", prefValue: e.username,
+ network: e.network,
+ isInteractive: e.isInteractive});
+ }
+ else // no network, change the general pref
+ {
+ dispatch("pref", {prefName: "username", prefValue: e.username,
+ isInteractive: e.isInteractive});
+ }
+}
+
+function cmdNames(e)
+{
+ if (e.hasOwnProperty("channelName"))
+ {
+ e.channel = new CIRCChannel(e.server, e.channelName);
+ }
+ else
+ {
+ if (!e.channel)
+ {
+ display(getMsg(MSG_ERR_REQUIRED_PARAM, "channel-name"), MT_ERROR);
+ return;
+ }
+ }
+
+ e.channel.pendingNamesReply = true;
+ e.server.sendData("NAMES " + e.channel.encodedName + "\n");
+}
+
+function cmdReconnect(e)
+{
+ if (e.network.isConnected())
+ {
+ // Set reconnect flag
+ e.network.reconnect = true;
+ if (typeof e.reason != "string")
+ e.reason = MSG_RECONNECTING;
+ // Now we disconnect.
+ e.network.quit(e.reason);
+ }
+ else
+ {
+ e.network.connect(e.network.requireSecurity);
+ }
+}
+
+function cmdReconnectAll(e)
+{
+ var reconnected = false;
+ for (var net in client.networks)
+ {
+ if (client.networks[net].isConnected() ||
+ ("messages" in client.networks[net]))
+ {
+ client.networks[net].dispatch("reconnect", { reason: e.reason });
+ reconnected = true;
+ }
+ }
+ if (!reconnected)
+ display(MSG_NO_RECONNECTABLE_NETS, MT_ERROR);
+}
+
+function cmdRejoin(e)
+{
+ if (e.channel.joined)
+ {
+ if (!e.reason)
+ e.reason = "";
+ e.channel.dispatch("part", { reason: e.reason, deleteWhenDone: false });
+ }
+
+ e.channel.join(e.channel.mode.key);
+}
+
+function cmdRename(e)
+{
+ var tab = getTabForObject(e.sourceObject);
+ if (!tab)
+ {
+ feedback(e, getMsg(MSG_ERR_INTERNAL_DISPATCH, "rename"));
+ return;
+ }
+ var label = e.label || prompt(MSG_TAB_NAME_PROMPT, tab.label);
+ if (!label)
+ {
+ return;
+ }
+ e.sourceObject.prefs["tabLabel"] = label;
+}
+
+
+function cmdTogglePref (e)
+{
+ var state = !client.prefs[e.prefName];
+ client.prefs[e.prefName] = state;
+ feedback(e, getMsg (MSG_FMT_PREF,
+ [e.prefName, state ? MSG_VAL_ON : MSG_VAL_OFF]));
+}
+
+function cmdToggleGroup(e)
+{
+ var document = getContentDocument(e.sourceObject.frame);
+ var msgs = document.querySelectorAll("[msg-groups*=\"" + e.groupId + "\"]");
+ if (!msgs.length)
+ return;
+
+ var isHidden = (msgs[0].style.display == "none");
+ for (i = 0; i < msgs.length; i++)
+ {
+ if (isHidden)
+ msgs[i].style.display = "";
+ else
+ msgs[i].style.display = "none";
+ }
+
+ var els = msgs[0].previousSibling.querySelectorAll(".chatzilla-link");
+ var button = els[els.length - 1];
+ if (button.text == MSG_COLLAPSE_HIDE)
+ {
+ button.text = MSG_COLLAPSE_SHOW;
+ button.title = MSG_COLLAPSE_SHOWTITLE;
+ }
+ else
+ {
+ button.text = MSG_COLLAPSE_HIDE;
+ button.title = MSG_COLLAPSE_HIDETITLE;
+ }
+}
+
+function cmdToggleUI(e)
+{
+ var ids = new Array();
+
+ switch (e.thing)
+ {
+ case "tabstrip":
+ ids = ["view-tabs"];
+ break;
+
+ case "userlist":
+ ids = ["main-splitter", "user-list-box"];
+ break;
+
+ case "header":
+ client.currentObject.prefs["displayHeader"] =
+ !client.currentObject.prefs["displayHeader"];
+ return;
+
+ case "status":
+ ids = ["status-bar"];
+ break;
+
+ default:
+ ASSERT (0,"Unknown element ``" + e.thing +
+ "'' passed to onToggleVisibility.");
+ return;
+ }
+
+ var newState;
+ var elem = document.getElementById(ids[0]);
+ var sourceObject = e.sourceObject;
+ var newState = !elem.collapsed;
+
+ for (var i in ids)
+ {
+ elem = document.getElementById(ids[i]);
+ elem.collapsed = newState;
+ }
+
+ updateTitle();
+ dispatch("focus-input");
+}
+
+function cmdCommands(e)
+{
+ display(MSG_COMMANDS_HEADER);
+
+ var matchResult = client.commandManager.listNames(e.pattern, CMD_CONSOLE);
+ matchResult = matchResult.join(", ");
+
+ if (e.pattern)
+ display(getMsg(MSG_MATCHING_COMMANDS, [e.pattern, matchResult]));
+ else
+ display(getMsg(MSG_ALL_COMMANDS, matchResult));
+}
+
+function cmdAttach(e)
+{
+ if (e.ircUrl.search(/ircs?:\/\//i) != 0)
+ e.ircUrl = "irc://" + e.ircUrl;
+
+ var parsedURL = parseIRCURL(e.ircUrl);
+ if (!parsedURL)
+ {
+ display(getMsg(MSG_ERR_BAD_IRCURL, e.ircUrl), MT_ERROR);
+ return;
+ }
+
+ gotoIRCURL(e.ircUrl);
+}
+
+function cmdMatchUsers(e)
+{
+ var matches = e.channel.findUsers(e.mask);
+ var uc = matches.unchecked;
+ var msgNotChecked = "";
+
+ // Get a pretty list of nicknames:
+ var nicknames = [];
+ for (var i = 0; i < matches.users.length; i++)
+ nicknames.push(matches.users[i].unicodeName);
+
+ var nicknameStr = arraySpeak(nicknames);
+
+ // Were we unable to check one or more of the users?
+ if (uc != 0)
+ msgNotChecked = getMsg(MSG_MATCH_UNCHECKED, uc);
+
+ if (matches.users.length == 0)
+ display(getMsg(MSG_NO_MATCHING_NICKS, msgNotChecked));
+ else
+ display(getMsg(MSG_MATCHING_NICKS, [nicknameStr, msgNotChecked]));
+}
+
+function cmdMe(e)
+{
+ if (!("act" in e.sourceObject))
+ {
+ display(getMsg(MSG_ERR_IMPROPER_VIEW, "me"), MT_ERROR);
+ return;
+ }
+ _sendMsgTo(e.action, "ACTION", e.sourceObject);
+}
+
+function cmdDescribe(e)
+{
+ var target = e.server.addTarget(e.target);
+ _sendMsgTo(e.action, "ACTION", target, e.sourceObject);
+}
+
+function cmdMode(e)
+{
+ var chan;
+
+ // Make sure the user can leave the channel name out from a channel view.
+ if ((!e.target || /^[\+\-].+/.test(e.target)) &&
+ !(chan && e.server.getChannel(chan)))
+ {
+ if (e.channel)
+ {
+ chan = e.channel.canonicalName;
+ if (e.param && e.modestr)
+ {
+ e.paramList.unshift(e.modestr);
+ }
+ else if (e.modestr)
+ {
+ e.paramList = [e.modestr];
+ e.param = e.modestr;
+ }
+ e.modestr = e.target;
+ }
+ else
+ {
+ display(getMsg(MSG_ERR_REQUIRED_PARAM, "target"), MT_ERROR);
+ return;
+ }
+ }
+ else
+ {
+ chan = fromUnicode(e.target, e.server);
+ }
+
+ // Check whether our mode string makes sense
+ if (!e.modestr)
+ {
+ e.modestr = "";
+ if (!e.channel && arrayContains(e.server.channelTypes, chan[0]))
+ e.channel = new CIRCChannel(e.server, null, chan);
+ if (e.channel)
+ e.channel.pendingModeReply = true;
+ }
+ else if (!(/^([+-][a-z]+)+$/i).test(e.modestr))
+ {
+ display(getMsg(MSG_ERR_INVALID_MODE, e.modestr), MT_ERROR);
+ return;
+ }
+
+ var params = (e.param) ? " " + e.paramList.join(" ") : "";
+ e.server.sendData("MODE " + chan + " " + fromUnicode(e.modestr, e.server) +
+ params + "\n");
+}
+
+function cmdMotif(e)
+{
+ var pm;
+ var msg;
+
+ if (e.command.name == "channel-motif")
+ {
+ pm = e.channel.prefManager;
+ msg = MSG_CURRENT_CSS_CHAN;
+ }
+ else if (e.command.name == "network-motif")
+ {
+ pm = e.network.prefManager;
+ msg = MSG_CURRENT_CSS_NET;
+ }
+ else if (e.command.name == "user-motif")
+ {
+ pm = e.user.prefManager;
+ msg = MSG_CURRENT_CSS_USER;
+ }
+ else
+ {
+ pm = client.prefManager;
+ msg = MSG_CURRENT_CSS;
+ }
+
+ if (e.motif)
+ {
+ if (e.motif == "-")
+ {
+ // delete local motif in favor of default
+ pm.clearPref("motif.current");
+ e.motif = pm.prefs["motif.current"];
+ }
+ else if (e.motif.search(/^(file|https?|ftp):/i) != -1)
+ {
+ // specific css file
+ pm.prefs["motif.current"] = e.motif;
+ }
+ else
+ {
+ // motif alias
+ var prefName = "motif." + e.motif;
+ if (client.prefManager.isKnownPref(prefName))
+ {
+ e.motif = client.prefManager.prefs[prefName];
+ }
+ else
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_MOTIF, e.motif), MT_ERROR);
+ return;
+ }
+
+ pm.prefs["motif.current"] = e.motif;
+ }
+
+ }
+
+ display (getMsg(msg, pm.prefs["motif.current"]));
+}
+
+function cmdList(e)
+{
+ if (!e.channelName)
+ {
+ e.channelName = "";
+ var c = e.server.channelCount;
+ if ((c > client.SAFE_LIST_COUNT) && !("listWarned" in e.network))
+ {
+ client.munger.getRule(".inline-buttons").enabled = true;
+ display(getMsg(MSG_LIST_CHANCOUNT, [c, "list"]), MT_WARN);
+ client.munger.getRule(".inline-buttons").enabled = false;
+ e.network.listWarned = true;
+ return;
+ }
+ }
+
+ e.network.list(e.channelName);
+}
+
+function cmdListPlugins(e)
+{
+ function listPlugin(plugin, i)
+ {
+ var enabled;
+ if ((plugin.API > 0) || ("disablePlugin" in plugin.scope))
+ enabled = plugin.enabled;
+ else
+ enabled = MSG_ALWAYS;
+
+ display(getMsg(MSG_FMT_PLUGIN1, [i, plugin.url]));
+ display(getMsg(MSG_FMT_PLUGIN2,
+ [plugin.id, plugin.version, enabled, plugin.status]));
+ display(getMsg(MSG_FMT_PLUGIN3, plugin.description));
+ }
+
+ if (e.plugin)
+ {
+ listPlugin(e.plugin, 0);
+ return;
+ }
+
+ var i = 0;
+ for (var k in client.plugins)
+ listPlugin(client.plugins[k], i++);
+
+ if (i == 0)
+ display(MSG_NO_PLUGINS);
+}
+
+function cmdRlist(e)
+{
+ try
+ {
+ var re = new RegExp(e.regexp, "i");
+ }
+ catch (ex)
+ {
+ display(MSG_ERR_INVALID_REGEX, MT_ERROR);
+ return;
+ }
+
+ var c = e.server.channelCount;
+ if ((c > client.SAFE_LIST_COUNT) && !("listWarned" in e.network))
+ {
+ client.munger.getRule(".inline-buttons").enabled = true;
+ display(getMsg(MSG_LIST_CHANCOUNT, [c, "rlist " + e.regexp]), MT_WARN);
+ client.munger.getRule(".inline-buttons").enabled = false;
+ e.network.listWarned = true;
+ return;
+ }
+ e.network.list(re);
+}
+
+function cmdReloadUI(e)
+{
+ if (!("getConnectionCount" in client) ||
+ client.getConnectionCount() == 0)
+ {
+ window.location.href = window.location.href;
+ }
+}
+
+function cmdQuery(e)
+{
+ // We'd rather *not* trigger the user.start event this time.
+ blockEventSounds("user", "start");
+ var user = openQueryTab(e.server, e.nickname);
+ dispatch("set-current-view", { view: user });
+
+ if (e.message)
+ _sendMsgTo(e.message, "PRIVMSG", user);
+
+ return user;
+}
+
+function cmdSay(e)
+{
+ if (!("say" in e.sourceObject))
+ {
+ display(getMsg(MSG_ERR_IMPROPER_VIEW, "say"), MT_ERROR);
+ return;
+ }
+
+ _sendMsgTo(e.message, "PRIVMSG", e.sourceObject)
+}
+
+function cmdMsg(e)
+{
+ var target = e.server.addTarget(e.nickname);
+ _sendMsgTo(e.message, "PRIVMSG", target, e.sourceObject);
+}
+
+function _sendMsgTo(message, msgType, target, displayObj)
+{
+ if (!displayObj)
+ displayObj = target;
+
+
+ var msg = filterOutput(message, msgType, target);
+
+ var o = getObjectDetails(target);
+ var lines = o.server ? o.server.splitLinesForSending(msg, true) : [msg];
+
+ for (var i = 0; i < lines.length; i++)
+ {
+ msg = lines[i];
+ if (!(o.server && o.server.caps["echo-message"]))
+ {
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ displayObj.display(msg, msgType, "ME!", target);
+ client.munger.getRule(".mailto").enabled = false;
+ }
+ if (msgType == "PRIVMSG")
+ target.say(msg);
+ else if (msgType == "NOTICE")
+ target.notice(msg);
+ else if (msgType == "ACTION")
+ target.act(msg);
+ }
+}
+
+function cmdNick(e)
+{
+ if (!e.nickname)
+ {
+ var curNick;
+ if (e.server && e.server.isConnected)
+ curNick = e.server.me.unicodeName;
+ else if (e.network)
+ curNick = e.network.prefs["nickname"];
+ else
+ curNick = client.prefs["nickname"];
+
+ e.nickname = prompt(MSG_NICK_PROMPT, curNick);
+ if (e.nickname == null)
+ return;
+ e.nickname = e.nickname.replace(/ /g, "_");
+ }
+
+ if (e.server && e.server.isConnected)
+ e.server.changeNick(e.nickname);
+
+ if (e.network)
+ {
+ /* We want to save in all non-online cases, including NET_CONNECTING,
+ * as we will only get a NICK reply if we are completely connected.
+ */
+ if (e.network.state == NET_ONLINE)
+ {
+ e.network.pendingNickChange = e.nickname;
+ }
+ else
+ {
+ e.network.prefs["nickname"] = e.nickname;
+ e.network.preferredNick = e.nickname;
+ }
+ }
+ else
+ {
+ client.prefs["nickname"] = e.nickname;
+ updateTitle(client);
+ }
+}
+
+function cmdNotice(e)
+{
+ var target = e.server.addTarget(e.nickname);
+ _sendMsgTo(e.message, "NOTICE", target, e.sourceObject);
+}
+
+function cmdQuote(e)
+{
+ /* Check we are connected, or at least pretending to be connected, so this
+ * can actually send something. The only thing that's allowed to send
+ * before the 001 is PASS, so if the command is not that and the net is not
+ * online, we stop too.
+ */
+ if ((e.network.state != NET_ONLINE) &&
+ (!e.server.isConnected || !e.ircCommand.match(/^\s*PASS/i)))
+ {
+ feedback(e, MSG_ERR_NOT_CONNECTED);
+ return;
+ }
+ e.server.sendData(fromUnicode(e.ircCommand) + "\n", e.sourceObject);
+}
+
+function cmdEval(e)
+{
+ var sourceObject = e.sourceObject;
+
+ try
+ {
+ sourceObject.doEval = function (__s) { return eval(__s); }
+ if (e.command.name == "eval")
+ sourceObject.display(e.expression, MT_EVALIN);
+ var rv = String(sourceObject.doEval (e.expression));
+ if (e.command.name == "eval")
+ sourceObject.display(rv, MT_EVALOUT);
+
+ }
+ catch (ex)
+ {
+ sourceObject.display(String(ex), MT_ERROR);
+ }
+}
+
+function cmdFocusInput(e)
+{
+ const WWATCHER_CTRID = "@mozilla.org/embedcomp/window-watcher;1";
+ const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
+
+ var watcher =
+ Components.classes[WWATCHER_CTRID].getService(nsIWindowWatcher);
+ if (watcher.activeWindow == window)
+ client.input.focus();
+ else
+ document.commandDispatcher.focusedElement = client.input;
+}
+
+function cmdGotoStartup(e)
+{
+ openStartupURLs();
+}
+
+function cmdGotoURL(e)
+{
+ if (/^ircs?:/.test(e.url))
+ {
+ gotoIRCURL(e.url);
+ return;
+ }
+
+ if (/^x-irc-dcc-(chat|file):[0-9a-fA-F]+$/.test(e.url))
+ {
+ var view = client.dcc.findByID(e.url.substr(15));
+ if (view)
+ dispatch("set-current-view", {view: view});
+ return;
+ }
+
+ if (/^x-cz-command:/.test(e.url))
+ {
+ var ary = e.url.match(/^x-cz-command:(.*)$/i);
+ e.sourceObject.dispatch(decodeURI(ary[1]),
+ {isInteractive: true, source: e.source});
+ return;
+ }
+
+ try
+ {
+ var uri = Services.io.newURI(e.url, "UTF-8");
+ }
+ catch (ex)
+ {
+ // Given "goto-url faq bar", expand to "http://.../faq/#bar"
+ var localeURLKey = "msg.localeurl." + e.url;
+ var hash = (("anchor" in e) && e.anchor) ? "#" + e.anchor : "";
+ if (localeURLKey != getMsg(localeURLKey))
+ dispatch(e.command.name + " " + getMsg(localeURLKey) + hash);
+ else
+ display(getMsg(MSG_ERR_INVALID_URL, e.url), MT_ERROR);
+
+ dispatch("focus-input");
+ return;
+ }
+
+ var browserWin = getWindowByType("navigator:browser");
+ var location = browserWin ? browserWin.gBrowser.currentURI.spec : null;
+ var action = e.command.name;
+ let where = "current";
+
+ // We don't want to replace ChatZilla running in a tab.
+ if ((action == "goto-url-newwin") ||
+ ((action == "goto-url") && location &&
+ location.startsWith("chrome://chatzilla/content/")))
+ {
+ where = "window";
+ }
+
+ if (action == "goto-url-newtab")
+ {
+ where = e.shiftKey ? "tabshifted" : "tab";
+ }
+
+ try
+ {
+ let loadInBackground =
+ Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground");
+ openLinkIn(e.url, where, { inBackground: loadInBackground });
+ }
+ catch (ex)
+ {
+ dd(formatException(ex));
+ }
+ dispatch("focus-input");
+}
+
+function cmdCTCP(e)
+{
+ var obj = e.server.addTarget(e.target);
+ obj.ctcp(e.code, e.params);
+}
+
+function cmdJoin(e)
+{
+ /* This check makes sure we only check if the *user* entered anything, and
+ * ignore any contextual information, like the channel the command was
+ * run on.
+ */
+ if ((!e.hasOwnProperty("channelName") || !e.channelName) &&
+ !e.channelToJoin)
+ {
+ if (client.joinDialog)
+ {
+ client.joinDialog.setNetwork(e.network);
+ client.joinDialog.focus();
+ return;
+ }
+
+ window.openDialog("chrome://chatzilla/content/channels.xul", "",
+ "resizable=yes",
+ { client: client, network: e.network || null,
+ opener: window });
+ return null;
+ }
+
+ var chan;
+ if (!e.channelToJoin)
+ {
+ if (!("charset" in e))
+ {
+ e.charset = null;
+ }
+ else if (e.charset && !checkCharset(e.charset))
+ {
+ display (getMsg(MSG_ERR_INVALID_CHARSET, e.charset), MT_ERROR);
+ return null;
+ }
+
+ if (e.channelName.search(",") != -1)
+ {
+ // We can join multiple channels! Woo!
+ var chans = e.channelName.split(",");
+ var keys = [];
+ if (e.key)
+ keys = e.key.split(",");
+ for (var c in chans)
+ {
+ chan = dispatch("join", { network: e.network,
+ server: e.server,
+ charset: e.charset,
+ channelName: chans[c],
+ key: keys.shift() });
+ }
+ return chan;
+ }
+
+ if ((arrayIndexOf(["#", "&", "+", "!"], e.channelName[0]) == -1) &&
+ (arrayIndexOf(e.server.channelTypes, e.channelName[0]) == -1))
+ {
+ e.channelName = e.server.channelTypes[0] + e.channelName;
+ }
+
+ var charset = e.charset ? e.charset : e.network.prefs["charset"];
+ chan = e.server.addChannel(e.channelName, charset);
+ if (e.charset)
+ chan.prefs["charset"] = e.charset;
+ }
+ else
+ {
+ chan = e.channelToJoin;
+ }
+
+ e.key = client.tryToGetLogin(chan.getURL(), "chan", "*", e.key, false, "");
+ chan.join(e.key);
+
+ /* !-channels are "safe" channels, and get a server-generated prefix. For
+ * this reason, we shouldn't do anything client-side until the server
+ * replies (since the reply will have the appropriate prefix). */
+ if (chan.unicodeName[0] != "!")
+ {
+ dispatch("create-tab-for-view", { view: chan });
+ dispatch("set-current-view", { view: chan });
+ }
+
+ return chan;
+}
+
+function cmdLeave(e)
+{
+ function leaveChannel(channelName)
+ {
+ var channelToLeave;
+ // This function will return true if we should continue processing
+ // channel names. If we discover that we were passed an invalid channel
+ // name, but have a channel on the event, we'll just leave that channel
+ // with the full message (including what we thought was a channel name)
+ // and return false in order to not process the rest of what we thought
+ // was a channel name. If there's a genuine error, e.g. because the user
+ // specified a non-existing channel and isn't in a channel either, we
+ // will also return a falsy value
+ var shouldContinue = true;
+ if (arrayIndexOf(e.server.channelTypes, channelName[0]) == -1)
+ {
+ // No valid prefix character. Check they really meant a channel...
+ var valid = false;
+ for (var i = 0; i < e.server.channelTypes.length; i++)
+ {
+ // Hmm, not ideal...
+ var chan = e.server.getChannel(e.server.channelTypes[i] +
+ channelName);
+ if (chan)
+ {
+ // Yes! They just missed that single character.
+ channelToLeave = chan;
+ valid = true;
+ break;
+ }
+ }
+
+ // We can only let them get away here if we've got a channel.
+ if (!valid)
+ {
+ if (e.channel)
+ {
+ /* Their channel name was invalid, but we have a channel
+ * view, so we'll assume they did "/leave part msg".
+ * NB: we use e.channelName here to get the full channel
+ * name before we (may have) split it.
+ */
+ e.reason = e.channelName + (e.reason ? " " + e.reason : "");
+ channelToLeave = e.channel;
+ shouldContinue = false;
+ }
+ else
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_CHANNEL, channelName),
+ MT_ERROR);
+ return;
+ }
+ }
+ }
+ else
+ {
+ // Valid prefix, so get real channel (if it exists...).
+ channelToLeave = e.server.getChannel(channelName);
+ if (!channelToLeave)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_CHANNEL, channelName),
+ MT_ERROR);
+ return;
+ }
+ }
+
+ if (!("deleteWhenDone" in e))
+ e.deleteWhenDone = client.prefs["deleteOnPart"];
+
+ /* If it's not active, we're not actually in it, even though the view is
+ * still here.
+ */
+ if (channelToLeave.active)
+ {
+ channelToLeave.deleteWhenDone = e.deleteWhenDone;
+
+ if (!e.reason)
+ e.reason = "";
+
+ e.server.sendData("PART " + channelToLeave.encodedName + " :" +
+ fromUnicode(e.reason, channelToLeave) + "\n");
+ }
+ else
+ {
+ /* We can leave the channel when not active
+ * this will close the view and prevent rejoin after a reconnect
+ */
+ if (channelToLeave.joined)
+ channelToLeave.joined = false;
+
+ if (e.deleteWhenDone)
+ channelToLeave.dispatch("delete-view");
+ }
+
+ return shouldContinue;
+ };
+
+ if (!e.server)
+ {
+ display(getMsg(MSG_ERR_IMPROPER_VIEW, e.command.name), MT_ERROR);
+ return;
+ }
+
+ if (!e.hasOwnProperty("channelName") && e.channel)
+ e.channelName = e.channel.unicodeName;
+
+ if (e.hasOwnProperty("channelName"))
+ {
+ if (!e.channelName)
+ {
+ // No channel specified and command not sent from a channel view
+ display(getMsg(MSG_ERR_NEED_CHANNEL, e.command.name), MT_ERROR);
+ return;
+ }
+
+
+ var channels = e.channelName.split(",");
+ for (var i = 0; i < channels.length; i++)
+ {
+ // Skip empty channel names:
+ if (!channels[i])
+ continue;
+
+ // If we didn't successfully leave, stop processing the
+ // rest of the channels:
+ if (!leaveChannel(channels[i]))
+ break;
+ }
+ }
+}
+
+function cmdMarker(e)
+{
+ if (!client.initialized)
+ return;
+
+ var view = e.sourceObject;
+ if (!("setActivityMarker" in e.sourceObject))
+ return;
+
+ var marker = e.sourceObject.getActivityMarker();
+ if ((e.command.name == "marker") && (marker == null))
+ {
+ // Marker is not currently set but user wants to scroll to it,
+ // so we just call set like normal.
+ e.command.name = "marker-set";
+ }
+
+ switch(e.command.name)
+ {
+ case "marker": /* Scroll to the marker. */
+ e.sourceObject.scrollToElement("marker", "center");
+ break;
+ case "marker-set": /* Set (or reset) the marker. */
+ e.sourceObject.setActivityMarker(true);
+ e.sourceObject.scrollToElement("marker", "center");
+ break;
+ case "marker-clear": /* Clear the marker. */
+ e.sourceObject.setActivityMarker(false);
+ break;
+ default:
+ view.display(MSG_ERR_UNKNOWN_COMMAND, e.command.name);
+ }
+}
+
+function cmdReload(e)
+{
+ dispatch("load " + e.plugin.url);
+}
+
+function cmdLoad(e)
+{
+ if (!e.scope)
+ e.scope = new Object();
+
+ if (!("plugin" in e.scope))
+ {
+ e.scope.plugin = { url: e.url, id: MSG_UNKNOWN, version: -1,
+ description: "", status: MSG_LOADING, enabled: false,
+ PluginAPI: 1, cwd: e.url.match(/^(.*?)[^\/]+$/)[1]};
+
+ }
+
+ var plugin = e.scope.plugin;
+ plugin.scope = e.scope;
+
+ try
+ {
+ var rvStr;
+ var rv = rvStr = client.load(e.url, e.scope);
+ let oldPlugin = getPluginByURL(e.url);
+ if (oldPlugin && !disablePlugin(oldPlugin, true))
+ {
+ display(getMsg(MSG_ERR_SCRIPTLOAD, e.url));
+ return null;
+ }
+
+ if ("init" in plugin)
+ {
+ // Sanity check plugin's methods and properties:
+ var okay = false;
+ if (!("id" in plugin) || (plugin.id == MSG_UNKNOWN))
+ display(getMsg(MSG_ERR_PLUGINAPI_NOID, e.url));
+ else if (!(plugin.id.match(/^[A-Za-z0-9-_]+$/)))
+ display(getMsg(MSG_ERR_PLUGINAPI_FAULTYID, e.url));
+ else if (!("enable" in plugin))
+ display(getMsg(MSG_ERR_PLUGINAPI_NOENABLE, e.url));
+ else if (!("disable" in plugin))
+ display(getMsg(MSG_ERR_PLUGINAPI_NODISABLE, e.url));
+ else
+ okay = true;
+
+ if (!okay)
+ {
+ display (getMsg(MSG_ERR_SCRIPTLOAD, e.url));
+ return null;
+ }
+
+ plugin.API = 1;
+ plugin.prefary = [["enabled", true, "hidden"]];
+ rv = rvStr = plugin.init(e.scope);
+
+ var branch = "extensions.irc.plugins." + plugin.id + ".";
+ var prefManager = new PrefManager(branch, client.defaultBundle);
+ prefManager.addPrefs(plugin.prefary);
+ plugin.prefManager = prefManager;
+ plugin.prefs = prefManager.prefs;
+ if ("onPrefChanged" in plugin)
+ prefManager.addObserver(plugin);
+ client.prefManager.addObserver(prefManager);
+ client.prefManagers.push(prefManager);
+ }
+ else
+ {
+ plugin.API = 0;
+ if ("initPlugin" in e.scope)
+ rv = rvStr = e.scope.initPlugin(e.scope);
+ plugin.enabled = true;
+ }
+ plugin.status = "loaded";
+
+ if (typeof rv == "function")
+ rvStr = "function";
+
+ if (!plugin.id)
+ plugin.id = 'plugin' + randomString(8);
+
+ client.plugins[plugin.id] = plugin;
+
+ feedback(e, getMsg(MSG_SUBSCRIPT_LOADED, [e.url, rvStr]), MT_INFO);
+
+ if ((plugin.API > 0) && plugin.prefs["enabled"])
+ dispatch("enable-plugin " + plugin.id);
+ return {rv: rv};
+ }
+ catch (ex)
+ {
+ display (getMsg(MSG_ERR_SCRIPTLOAD, e.url));
+ display (formatException(ex), MT_ERROR);
+ }
+
+ return null;
+}
+
+function cmdWho(e)
+{
+ e.network.pendingWhoReply = true;
+ e.server.LIGHTWEIGHT_WHO = false;
+ e.server.who(e.rest);
+}
+
+function cmdWhoIs(e)
+{
+ if (!isinstance(e.network.whoisList, Object))
+ e.network.whoisList = {};
+
+ for (var i = 0; i < e.nicknameList.length; i++)
+ {
+ if ((i < e.nicknameList.length - 1) &&
+ (e.server.toLowerCase(e.nicknameList[i]) ==
+ e.server.toLowerCase(e.nicknameList[i + 1])))
+ {
+ e.server.whois(e.nicknameList[i] + " " + e.nicknameList[i]);
+ i++;
+ }
+ else
+ {
+ e.server.whois(e.nicknameList[i]);
+ }
+ e.network.whoisList[e.server.toLowerCase(e.nicknameList[i])] = null;
+ }
+}
+
+function cmdWhoIsIdle(e)
+{
+ for (var i = 0; i < e.nicknameList.length; i++)
+ e.server.whois(e.nicknameList[i] + " " + e.nicknameList[i]);
+}
+
+function cmdWhoWas(e)
+{
+ e.server.whowas(e.nickname, e.limit);
+}
+
+function cmdTopic(e)
+{
+ if (!e.newTopic)
+ e.server.sendData("TOPIC " + e.channel.encodedName + "\n");
+ else
+ e.channel.setTopic(e.newTopic);
+}
+
+function cmdAbout(e)
+{
+ if (e.source)
+ {
+ if ("aboutDialog" in client)
+ return client.aboutDialog.focus();
+
+ window.openDialog("chrome://chatzilla/content/about/about.xul", "",
+ "chrome,dialog", { client: client });
+ }
+ else
+ {
+ var ver = CIRCServer.prototype.VERSION_RPLY;
+ client.munger.getRule(".inline-buttons").enabled = true;
+ display(getMsg(MSG_ABOUT_VERSION, [ver, "about"]));
+ display(MSG_ABOUT_HOMEPAGE);
+ client.munger.getRule(".inline-buttons").enabled = false;
+ }
+}
+
+function cmdAlias(e)
+{
+ var aliasDefs = client.prefs["aliases"];
+ function getAlias(commandName)
+ {
+ for (var i = 0; i < aliasDefs.length; ++i)
+ {
+ var ary = aliasDefs[i].match(/^(.*?)\s*=\s*(.*)$/);
+ if (ary[1] == commandName)
+ return [i, ary[2]];
+ }
+
+ return null;
+ };
+
+ var ary;
+
+ if ((e.commandList == "-") || (e.command.name == "unalias"))
+ {
+ /* remove alias */
+ ary = getAlias(e.aliasName);
+ if (!ary)
+ {
+ display(getMsg(MSG_NOT_AN_ALIAS, e.aliasName), MT_ERROR);
+ return;
+ }
+
+ // Command Manager is updated when the preference changes.
+ arrayRemoveAt(aliasDefs, ary[0]);
+ aliasDefs.update();
+
+ feedback(e, getMsg(MSG_ALIAS_REMOVED, e.aliasName));
+ }
+ else if (e.aliasName && e.commandList)
+ {
+ /* add/change alias */
+ ary = getAlias(e.aliasName);
+ if (ary)
+ aliasDefs[ary[0]] = e.aliasName + " = " + e.commandList;
+ else
+ aliasDefs.push(e.aliasName + " = " + e.commandList);
+
+ // Command Manager is updated when the preference changes.
+ aliasDefs.update();
+
+ feedback(e, getMsg(MSG_ALIAS_CREATED, [e.aliasName, e.commandList]));
+ }
+ else if (e.aliasName)
+ {
+ /* display alias */
+ ary = getAlias(e.aliasName);
+ if (!ary)
+ display(getMsg(MSG_NOT_AN_ALIAS, e.aliasName), MT_ERROR);
+ else
+ display(getMsg(MSG_FMT_ALIAS, [e.aliasName, ary[1]]));
+ }
+ else
+ {
+ /* list aliases */
+ if (aliasDefs.length == 0)
+ {
+ display(MSG_NO_ALIASES);
+ }
+ else
+ {
+ for (var i = 0; i < aliasDefs.length; ++i)
+ {
+ ary = aliasDefs[i].match(/^(.*?)\s*=\s*(.*)$/);
+ if (ary)
+ display(getMsg(MSG_FMT_ALIAS, [ary[1], ary[2]]));
+ else
+ display(getMsg(MSG_ERR_BADALIAS, aliasDefs[i]));
+ }
+ }
+ }
+}
+
+function cmdAway(e)
+{
+ function sendToAllNetworks(command, reason)
+ {
+ for (var n in client.networks)
+ {
+ var net = client.networks[n];
+ if (net.primServ && (net.state == NET_ONLINE))
+ {
+ // If we can override the network's away state, or they are
+ // already idly-away, or they're not away to begin with:
+ if (overrideAway || net.isIdleAway || !net.prefs["away"])
+ {
+ net.dispatch(command, {reason: reason });
+ net.isIdleAway = (e.command.name == "idle-away");
+ }
+ }
+ }
+ };
+
+ // Idle away shouldn't override away state set by the user.
+ var overrideAway = (e.command.name.indexOf("idle") != 0);
+
+ if ((e.command.name == "away") || (e.command.name == "custom-away") ||
+ (e.command.name == "idle-away"))
+ {
+ /* going away */
+ if (e.command.name == "custom-away")
+ {
+ e.reason = prompt(MSG_AWAY_PROMPT);
+ // prompt() returns null for cancelling, a string otherwise (even if empty).
+ if (e.reason == null)
+ return;
+ }
+ // No parameter, or user entered nothing in the prompt.
+ if (!e.reason)
+ e.reason = MSG_AWAY_DEFAULT;
+
+ // Update away list (remove from current location).
+ for (var i = 0; i < client.awayMsgs.length; i++)
+ {
+ if (client.awayMsgs[i].message == e.reason)
+ {
+ client.awayMsgs.splice(i, 1);
+ break;
+ }
+ }
+ // Always put new item at start.
+ var newMsg = { message: e.reason };
+ client.awayMsgs.unshift(newMsg);
+ // Make sure we've not exceeded the limit set.
+ if (client.awayMsgs.length > client.awayMsgCount)
+ client.awayMsgs.splice(client.awayMsgCount);
+ // And now, to save the list!
+ try
+ {
+ var awayFile = new nsLocalFile(client.prefs["profilePath"]);
+ awayFile.append("awayMsgs.txt");
+ var awayLoader = new TextSerializer(awayFile);
+ if (awayLoader.open(">"))
+ {
+ awayLoader.serialize(client.awayMsgs);
+ awayLoader.close();
+ }
+ }
+ catch(ex)
+ {
+ display(getMsg(MSG_ERR_AWAY_SAVE, formatException(ex)), MT_ERROR);
+ }
+
+ // Actually do away stuff, is this on a specific network?
+ if (e.server)
+ {
+ var normalNick = e.network.prefs["nickname"];
+ var awayNick = e.network.prefs["awayNick"];
+ if (e.network.state == NET_ONLINE)
+ {
+ // Postulate that if normal nick and away nick are the same,
+ // user doesn't want to change nicks:
+ if (awayNick && (normalNick != awayNick))
+ e.server.changeNick(awayNick);
+ e.server.sendData("AWAY :" + fromUnicode(e.reason, e.network) +
+ "\n");
+ }
+ if (awayNick && (normalNick != awayNick))
+ e.network.preferredNick = awayNick;
+ e.network.prefs["away"] = e.reason;
+ }
+ else
+ {
+ // Client view, do command for all networks.
+ sendToAllNetworks("away", e.reason);
+ client.prefs["away"] = e.reason;
+
+ // Don't tell people how to get back if they're idle:
+ var idleMsgParams = [e.reason, client.prefs["awayIdleTime"]];
+ if (e.command.name == "idle-away")
+ var msg = getMsg(MSG_IDLE_AWAY_ON, idleMsgParams);
+ else
+ msg = getMsg(MSG_AWAY_ON, e.reason);
+
+ // Display on the *client* tab, or on the current tab iff
+ // there's nowhere else they'll hear about it:
+ if (("frame" in client) && client.frame)
+ client.display(msg);
+ else if (!client.getConnectedNetworks())
+ display(msg);
+ }
+ }
+ else
+ {
+ /* returning */
+ if (e.server)
+ {
+ if (e.network.state == NET_ONLINE)
+ {
+ var curNick = e.server.me.unicodeName;
+ var awayNick = e.network.prefs["awayNick"];
+ if (awayNick && (curNick == awayNick))
+ e.server.changeNick(e.network.prefs["nickname"]);
+ e.server.sendData("AWAY\n");
+ }
+ // Go back to old nick, even if not connected:
+ if (awayNick && (curNick == awayNick))
+ e.network.preferredNick = e.network.prefs["nickname"];
+ e.network.prefs["away"] = "";
+ }
+ else
+ {
+ client.prefs["away"] = "";
+ // Client view, do command for all networks.
+ sendToAllNetworks("back");
+ if (("frame" in client) && client.frame)
+ client.display(MSG_AWAY_OFF);
+ else if (!client.getConnectedNetworks())
+ display(MSG_AWAY_OFF);
+ }
+ }
+}
+
+function cmdOpenAtStartup(e)
+{
+ var origURL = e.sourceObject.getURL();
+ var url = makeCanonicalIRCURL(origURL);
+ var list = client.prefs["initialURLs"];
+ ensureCachedCanonicalURLs(list);
+ var index = arrayIndexOf(list.canonicalURLs, url);
+
+ if (e.toggle == null)
+ {
+ if (index == -1)
+ display(getMsg(MSG_STARTUP_NOTFOUND, url));
+ else
+ display(getMsg(MSG_STARTUP_EXISTS, url));
+ return;
+ }
+
+ e.toggle = getToggle(e.toggle, (index != -1));
+
+ if (e.toggle)
+ {
+ // yes, please open at startup
+ if (index == -1)
+ {
+ list.push(origURL);
+ list.update();
+ display(getMsg(MSG_STARTUP_ADDED, url));
+ }
+ else
+ {
+ display(getMsg(MSG_STARTUP_EXISTS, url));
+ }
+ }
+ else
+ {
+ // no, please don't open at startup
+ if (index != -1)
+ {
+ arrayRemoveAt(list, index);
+ list.update();
+ display(getMsg(MSG_STARTUP_REMOVED, url));
+ }
+ else
+ {
+ display(getMsg(MSG_STARTUP_NOTFOUND, url));
+ }
+ }
+}
+
+function cmdOper(e)
+{
+ e.password = client.tryToGetLogin(e.server.getURL(), "oper", e.opername,
+ e.password, true, MSG_NEED_OPER_PASSWORD);
+
+ if (!e.password)
+ return;
+
+ e.server.sendData("OPER " + fromUnicode(e.opername, e.server) + " " +
+ fromUnicode(e.password, e.server) + "\n");
+}
+
+function cmdPing (e)
+{
+ e.network.dispatch("ctcp", { target: e.nickname, code: "PING" });
+}
+
+function cmdPref (e)
+{
+ var msg;
+ var pm;
+
+ if (e.command.name == "network-pref")
+ {
+ pm = e.network.prefManager;
+ msg = MSG_FMT_NETPREF;
+ }
+ else if (e.command.name == "channel-pref")
+ {
+ pm = e.channel.prefManager;
+ msg = MSG_FMT_CHANPREF;
+ }
+ else if (e.command.name == "plugin-pref")
+ {
+ pm = e.plugin.prefManager;
+ msg = MSG_FMT_PLUGINPREF;
+ }
+ else if (e.command.name == "user-pref")
+ {
+ pm = e.user.prefManager;
+ msg = MSG_FMT_USERPREF;
+ }
+ else
+ {
+ pm = client.prefManager;
+ msg = MSG_FMT_PREF;
+ }
+
+ var ary = pm.listPrefs(e.prefName);
+ if (ary.length == 0)
+ {
+ display (getMsg(MSG_ERR_UNKNOWN_PREF, [e.prefName]),
+ MT_ERROR);
+ return false;
+ }
+
+ if (e.prefValue == "-")
+ e.deletePref = true;
+
+ if (e.deletePref)
+ {
+ if (!(e.prefName in pm.prefRecords))
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_PREF, [e.prefName]), MT_ERROR);
+ return false;
+ }
+
+ try
+ {
+ pm.clearPref(e.prefName);
+ }
+ catch (ex)
+ {
+ // ignore exception generated by clear of nonexistant pref
+ if (!("result" in ex) ||
+ ex.result != Components.results.NS_ERROR_UNEXPECTED)
+ {
+ throw ex;
+ }
+ }
+
+ var prefValue = pm.prefs[e.prefName];
+ feedback (e, getMsg(msg, [e.prefName, pm.prefs[e.prefName]]));
+ return true;
+ }
+
+ if (e.prefValue)
+ {
+ if (!(e.prefName in pm.prefRecords))
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_PREF, [e.prefName]), MT_ERROR);
+ return false;
+ }
+
+ var r = pm.prefRecords[e.prefName];
+ var def, type;
+
+ if (typeof r.defaultValue == "function")
+ def = r.defaultValue(e.prefName);
+ else
+ def = r.defaultValue;
+
+ type = typeof def;
+
+ switch (type)
+ {
+ case "number":
+ e.prefValue = Number(e.prefValue);
+ break;
+ case "boolean":
+ e.prefValue = (e.prefValue.toLowerCase() == "true");
+ break;
+ case "string":
+ break;
+ default:
+ if (isinstance(e.prefValue, Array))
+ e.prefValue = e.prefValue.join("; ");
+ if (isinstance(def, Array))
+ e.prefValue = pm.stringToArray(e.prefValue);
+ break;
+ }
+
+ pm.prefs[e.prefName] = e.prefValue;
+ if (isinstance(e.prefValue, Array))
+ e.prefValue = e.prefValue.join("; ");
+ feedback (e, getMsg(msg, [e.prefName, e.prefValue]));
+ }
+ else
+ {
+ for (var i = 0; i < ary.length; ++i)
+ {
+ var value;
+ if (isinstance(pm.prefs[ary[i]], Array))
+ value = pm.prefs[ary[i]].join("; ");
+ else
+ value = pm.prefs[ary[i]];
+
+ feedback(e, getMsg(msg, [ary[i], value]));
+ }
+ }
+
+ return true;
+}
+
+function cmdPrint(e)
+{
+ if (("frame" in e.sourceObject) && e.sourceObject.frame &&
+ getContentWindow(e.sourceObject.frame))
+ {
+ getContentWindow(e.sourceObject.frame).print();
+ }
+ else
+ {
+ display(MSG_ERR_UNABLE_TO_PRINT);
+ }
+}
+
+function cmdVersion(e)
+{
+ if (e.nickname)
+ e.network.dispatch("ctcp", { target: e.nickname, code: "VERSION"});
+ else
+ e.server.sendData(fromUnicode("VERSION") + "\n", e.sourceObject);
+}
+
+function cmdEcho(e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ display(e.message);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+function cmdInvite(e)
+{
+ var channel;
+
+ if (e.channelName)
+ {
+ channel = e.server.getChannel(e.channelName);
+ if (!channel)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_CHANNEL, e.channelName), MT_ERROR);
+ return;
+ }
+ }
+ else if (e.channel)
+ {
+ channel = e.channel;
+ }
+ else
+ {
+ display(getMsg(MSG_ERR_NO_CHANNEL, e.command.name), MT_ERROR);
+ return;
+ }
+
+ channel.invite(e.nickname);
+}
+
+function cmdKick(e)
+{
+ if (e.userList)
+ {
+ if (e.command.name == "kick-ban")
+ {
+ e.sourceObject.dispatch("ban", { userList: e.userList,
+ canonNickList: e.canonNickList,
+ user: e.user,
+ nickname: e.user.encodedName });
+ }
+
+ /* Note that we always do /kick below; the /ban is covered above.
+ * Also note that we are required to pass the nickname, to satisfy
+ * the dispatching of the command (which is defined with a required
+ * <nickname> parameter). It's not required for /ban, above, but it
+ * seems prudent to include it anyway.
+ */
+ for (var i = 0; i < e.userList.length; i++)
+ {
+ var e2 = { user: e.userList[i],
+ nickname: e.userList[i].encodedName };
+ e.sourceObject.dispatch("kick", e2);
+ }
+ return;
+ }
+
+ if (!e.user)
+ e.user = e.channel.getUser(e.nickname);
+
+ if (!e.user)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_USER, e.nickname), MT_ERROR);
+ return;
+ }
+
+ if (e.command.name == "kick-ban")
+ e.sourceObject.dispatch("ban", { nickname: e.user.encodedName });
+
+ e.user.kick(e.reason);
+}
+
+function cmdKnock(e)
+{
+ var rest = (e.reason ? " :" + fromUnicode(e.reason, e.server) : "") + "\n";
+ e.server.sendData("KNOCK " + fromUnicode(e.channelName, e.server) + rest);
+}
+
+function cmdClient(e)
+{
+ if (!("messages" in client))
+ {
+ client.display(MSG_WELCOME, "HELLO");
+ dispatch("set-current-view", { view: client });
+ dispatch("help", { hello: true });
+ dispatch("networks");
+ }
+ else
+ {
+ dispatch("set-current-view", { view: client });
+ }
+}
+
+function cmdNotify(e)
+{
+ var net = e.network;
+ var supports_monitor = net.primServ.supports["monitor"];
+
+ if (!e.nickname)
+ {
+ if (net.prefs["notifyList"].length > 0)
+ {
+ if (supports_monitor)
+ {
+ // Just get the status of the monitor list from the server.
+ net.primServ.sendData("MONITOR S\n");
+ }
+ else
+ {
+ /* delete the lists and force a ISON check, this will
+ * print the current online/offline status when the server
+ * responds */
+ delete net.onList;
+ delete net.offList;
+ onNotifyTimeout();
+ }
+ }
+ else
+ {
+ display(MSG_NO_NOTIFY_LIST);
+ }
+ }
+ else
+ {
+ var adds = new Array();
+ var subs = new Array();
+
+ for (var i in e.nicknameList)
+ {
+ var nickname = e.server.toLowerCase(e.nicknameList[i]);
+ var list = net.prefs["notifyList"];
+ list = e.server.toLowerCase(list.join(";")).split(";");
+ var idx = arrayIndexOf (list, nickname);
+ if (idx == -1)
+ {
+ net.prefs["notifyList"].push (nickname);
+ adds.push(nickname);
+ }
+ else
+ {
+ arrayRemoveAt (net.prefs["notifyList"], idx);
+ subs.push(nickname);
+ }
+ }
+ net.prefs["notifyList"].update();
+
+ var msgname;
+
+ if (adds.length > 0)
+ {
+ if (supports_monitor)
+ net.primServ.sendMonitorList(adds, true);
+
+ msgname = (adds.length == 1) ? MSG_NOTIFY_ADDONE :
+ MSG_NOTIFY_ADDSOME;
+ display(getMsg(msgname, arraySpeak(adds)));
+ }
+
+ if (subs.length > 0)
+ {
+ if (supports_monitor)
+ net.primServ.sendMonitorList(subs, false);
+
+ msgname = (subs.length == 1) ? MSG_NOTIFY_DELONE :
+ MSG_NOTIFY_DELSOME;
+ display(getMsg(msgname, arraySpeak(subs)));
+ }
+
+ delete net.onList;
+ delete net.offList;
+ if (!supports_monitor)
+ onNotifyTimeout();
+ }
+}
+
+function cmdStalk(e)
+{
+ var list = client.prefs["stalkWords"];
+
+ if (!e.text)
+ {
+ if (list.length == 0)
+ display(MSG_NO_STALK_LIST);
+ else
+ {
+ function alphabetize(a, b)
+ {
+ var A = a.toLowerCase();
+ var B = b.toLowerCase();
+ if (A < B) return -1;
+ if (B < A) return 1;
+ return 0;
+ }
+
+ list.sort(alphabetize);
+ display(getMsg(MSG_STALK_LIST, list.join(", ")));
+ }
+ return;
+ }
+
+ var notStalkingWord = true;
+ var loweredText = e.text.toLowerCase();
+
+ for (var i = 0; i < list.length; ++i)
+ if (list[i].toLowerCase() == loweredText)
+ notStalkingWord = false;
+
+ if (notStalkingWord)
+ {
+ list.push(e.text);
+ list.update();
+ display(getMsg(MSG_STALK_ADD, e.text));
+ }
+ else
+ {
+ display(getMsg(MSG_STALKING_ALREADY, e.text));
+ }
+}
+
+function cmdUnstalk(e)
+{
+ e.text = e.text.toLowerCase();
+ var list = client.prefs["stalkWords"];
+
+ for (var i = 0; i < list.length; ++i)
+ {
+ if (list[i].toLowerCase() == e.text)
+ {
+ list.splice(i, 1);
+ list.update();
+ display(getMsg(MSG_STALK_DEL, e.text));
+ return;
+ }
+ }
+
+ display(getMsg(MSG_ERR_UNKNOWN_STALK, e.text), MT_ERROR);
+}
+
+function cmdUser(e)
+{
+ dispatch("name", {username: e.username, network: e.network,
+ isInteractive: e.isInteractive});
+ dispatch("desc", {description: e.description, network: e.network,
+ isInteractive: e.isInteractive});
+}
+
+function cmdUserhost(e)
+{
+ var nickList = combineNicks(e.nicknameList, 5);
+ for (var i = 0; i < nickList.length; i++)
+ {
+ e.server.userhost(nickList[i]);
+ }
+}
+
+function cmdUserip(e)
+{
+ // Check if the server supports this
+ if (!e.server.servCmds.userip)
+ {
+ display(getMsg(MSG_ERR_UNSUPPORTED_COMMAND, "USERIP"), MT_ERROR);
+ return;
+ }
+ var nickList = combineNicks(e.nicknameList, 5);
+ for (var i = 0; i < nickList.length; i++)
+ e.server.userip(nickList[i]);
+}
+
+function cmdUsermode(e)
+{
+ if (e.newMode)
+ {
+ if (e.sourceObject.network)
+ e.sourceObject.network.prefs["usermode"] = e.newMode;
+ else
+ client.prefs["usermode"] = e.newMode;
+ }
+ else
+ {
+ if (e.server && e.server.isConnected)
+ {
+ e.server.sendData("mode " + e.server.me.encodedName + "\n");
+ }
+ else
+ {
+ var prefs;
+
+ if (e.network)
+ prefs = e.network.prefs;
+ else
+ prefs = client.prefs;
+
+ display(getMsg(MSG_USER_MODE,
+ [prefs["nickname"], prefs["usermode"]]),
+ MT_MODE);
+ }
+ }
+}
+
+function cmdLog(e)
+{
+ var view = e.sourceObject;
+
+ if (e.state != null)
+ {
+ e.state = getToggle(e.state, view.prefs["log"])
+ view.prefs["log"] = e.state;
+ }
+ else
+ {
+ if (view.prefs["log"])
+ display(getMsg(MSG_LOGGING_ON, getLogPath(view)));
+ else
+ display(MSG_LOGGING_OFF);
+ }
+}
+
+function cmdSave(e)
+{
+ var OutputProgressListener =
+ {
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus)
+ {
+ // Use this to access onStateChange flags
+ var requestSpec;
+ try
+ {
+ var channel = aRequest.QueryInterface(nsIChannel);
+ requestSpec = channel.URI.spec;
+ }
+ catch (ex) { }
+
+ // Detect end of file saving of any file:
+ if (aStateFlags & nsIWebProgressListener.STATE_STOP)
+ {
+ if (aStatus == kErrorBindingAborted)
+ aStatus = 0;
+
+ // We abort saving for all errors except if image src file is
+ // not found
+ var abortSaving = (aStatus != 0 && aStatus != kFileNotFound);
+ if (abortSaving)
+ {
+ // Cancel saving
+ wbp.cancelSave();
+ display(getMsg(MSG_SAVE_ERR_FAILED, aMessage), MT_ERROR);
+ return;
+ }
+
+ if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK
+ && wbp.currentState == nsIWBP.PERSIST_STATE_FINISHED)
+ {
+ // Let the user know:
+ pm = [e.sourceObject.viewName, getURLSpecFromFile(file)];
+ display(getMsg(MSG_SAVE_SUCCESSFUL, pm), MT_INFO);
+ }
+ /* Check if we've finished. WebBrowserPersist screws up when we
+ * don't save additional files. Cope when saving html only or
+ * text.
+ */
+ else if (!requestSpec && saveType > 0)
+ {
+ if (wbp)
+ wbp.progressListener = null;
+ pm = [e.sourceObject.viewName, getURLSpecFromFile(file)];
+ display(getMsg(MSG_SAVE_SUCCESSFUL, pm), MT_INFO);
+ }
+ }
+ },
+
+ onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress,
+ aMaxSelfProgress, aCurTotalProgress,
+ aMaxTotalProgress) {},
+ onLocationChange: function(aWebProgress, aRequest, aLocation) {},
+ onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {},
+ onSecurityChange: function(aWebProgress, aRequest, state) {},
+
+ QueryInterface: function(aIID)
+ {
+ if (aIID.equals(Components.interfaces.nsIWebProgressListener)
+ || aIID.equals(Components.interfaces.nsISupports)
+ || aIID.equals(Components.interfaces.nsISupportsWeakReference))
+ {
+ return this;
+ }
+
+ throw Components.results.NS_NOINTERFACE;
+ }
+ };
+
+ const kFileNotFound = 2152857618;
+ const kErrorBindingAborted = 2152398850;
+
+ const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
+ const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
+ const nsIChannel = Components.interfaces.nsIChannel;
+
+ var wbp = newObject("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
+ nsIWBP);
+ wbp.progressListener = OutputProgressListener;
+
+ var file, saveType, saveFolder, docToBeSaved, title;
+ var flags, fileType, charLimit;
+ var dialogTitle, rv, pm;
+
+ // We want proper descriptions and no "All Files" option.
+ const TYPELIST = [[MSG_SAVE_COMPLETEVIEW,"*.htm;*.html"],
+ [MSG_SAVE_HTMLONLYVIEW,"*.htm;*.html"],
+ [MSG_SAVE_PLAINTEXTVIEW,"*.txt"], "$noAll"];
+ // constants and variables for the wbp.saveDocument call
+ var saveTypes =
+ {
+ complete: 0,
+ htmlonly: 1,
+ text: 2
+ };
+
+ if (!e.filename)
+ {
+ dialogTitle = getMsg(MSG_SAVE_DIALOGTITLE, e.sourceObject.viewName);
+ rv = pickSaveAs(dialogTitle, TYPELIST, e.sourceObject.viewName +
+ ".html");
+ if (!rv.ok)
+ return;
+ saveType = rv.picker.filterIndex;
+ file = rv.file;
+ e.filename = rv.file.path;
+ }
+ else
+ {
+ try
+ {
+ // Try to use this as a path
+ file = nsLocalFile(e.filename);
+ }
+ catch (ex)
+ {
+ // try to use it as a URL
+ try
+ {
+ file = getFileFromURLSpec(e.filename);
+ }
+ catch(ex)
+ {
+ // What is the user thinking? It's not rocket science...
+ display(getMsg(MSG_SAVE_ERR_INVALID_PATH, e.filename),
+ MT_ERROR);
+ return;
+ }
+ }
+
+ // Get extension and determine savetype
+ if (!e.savetype)
+ {
+ var extension = file.path.substr(file.path.lastIndexOf("."));
+ if (extension == ".txt")
+ {
+ saveType = saveTypes["text"];
+ }
+ else if (extension.match(/\.x?html?$/))
+ {
+ saveType = saveTypes["complete"];
+ }
+ else
+ {
+ // No saveType and no decent extension --> out!
+ var errMsg;
+ if (extension.indexOf(".") < 0)
+ errMsg = MSG_SAVE_ERR_NO_EXT;
+ else
+ errMsg = getMsg(MSG_SAVE_ERR_INVALID_EXT, extension);
+ display(errMsg, MT_ERROR);
+ return;
+ }
+ }
+ else
+ {
+ if (!(e.savetype in saveTypes))
+ {
+ // no valid saveType
+ display(getMsg(MSG_SAVE_ERR_INVALID_SAVETYPE, e.savetype),
+ MT_ERROR);
+ return;
+ }
+ saveType = saveTypes[e.savetype];
+ }
+
+ var askforreplace = (e.isInteractive && file.exists());
+ if (askforreplace && !confirm(getMsg(MSG_SAVE_FILEEXISTS, e.filename)))
+ return;
+ }
+
+ // We don't want to convert anything, leave everything as is and replace
+ // old files, as the user has been prompted about that already.
+ wbp.persistFlags |= nsIWBP.PERSIST_FLAGS_NO_CONVERSION
+ | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES
+ | nsIWBP.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS
+ | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES
+ | nsIWBP.PERSIST_FLAGS_DONT_FIXUP_LINKS
+ | nsIWBP.PERSIST_FLAGS_DONT_CHANGE_FILENAMES;
+
+ // Set the document from the current view, and set a usable title
+ docToBeSaved = getContentDocument(e.sourceObject.frame);
+ var headElement = docToBeSaved.getElementsByTagName("HEAD")[0];
+ var titleElements = docToBeSaved.getElementsByTagName("title");
+ // Remove an existing title, there shouldn't be more than one.
+ if (titleElements.length > 0)
+ titleElements[0].parentNode.removeChild(titleElements[0]);
+ title = docToBeSaved.createElement("title");
+ title.appendChild(docToBeSaved.createTextNode(document.title +
+ " (" + new Date() + ")"));
+ headElement.appendChild(title);
+ // Set standard flags, file type, saveFolder and character limit
+ flags = nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
+ fileType = "text/html";
+ saveFolder = null;
+ charLimit = 0;
+
+ // Do saveType specific stuff
+ switch (saveType)
+ {
+ case saveTypes["complete"]:
+ // Get the directory into which to save associated files.
+ saveFolder = file.clone();
+ var baseName = saveFolder.leafName;
+ baseName = baseName.substring(0, baseName.lastIndexOf("."));
+ saveFolder.leafName = getMsg(MSG_SAVE_FILES_FOLDER, baseName);
+ break;
+ // html only does not need any additional configuration
+ case saveTypes["text"]:
+ // set flags for Plain Text
+ flags = nsIWBP.ENCODE_FLAGS_FORMATTED;
+ flags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
+ flags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;
+ // set the file type and set character limit to 80
+ fileType = "text/plain";
+ charLimit = 80;
+ break;
+ }
+
+ try
+ {
+ wbp.saveDocument(docToBeSaved, file, saveFolder, fileType, flags,
+ charLimit);
+ }
+ catch (ex)
+ {
+ pm = [e.sourceObject.viewName, e.filename, ex.message];
+ display(getMsg(MSG_SAVE_ERR_FAILED, pm), MT_ERROR);
+ }
+ // Error handling and finishing message is done by the listener
+}
+
+function cmdSupports(e)
+{
+ var server = e.server;
+ var data = server.supports;
+
+ if ("channelTypes" in server)
+ display(getMsg(MSG_SUPPORTS_CHANTYPES,
+ server.channelTypes.join(", ")));
+ if ("channelModes" in server)
+ {
+ display(getMsg(MSG_SUPPORTS_CHANMODESA,
+ server.channelModes.a.join(", ")));
+ display(getMsg(MSG_SUPPORTS_CHANMODESB,
+ server.channelModes.b.join(", ")));
+ display(getMsg(MSG_SUPPORTS_CHANMODESC,
+ server.channelModes.c.join(", ")));
+ display(getMsg(MSG_SUPPORTS_CHANMODESD,
+ server.channelModes.d.join(", ")));
+ }
+
+ if ("userModes" in server)
+ {
+ var list = new Array();
+ for (var m in server.userModes)
+ {
+ list.push(getMsg(MSG_SUPPORTS_USERMODE, [
+ server.userModes[m].mode,
+ server.userModes[m].symbol
+ ]));
+ }
+ display(getMsg(MSG_SUPPORTS_USERMODES, list.join(", ")));
+ }
+
+ var listB1 = new Array();
+ var listB2 = new Array();
+ var listN = new Array();
+ for (var k in data)
+ {
+ if (typeof data[k] == "boolean")
+ {
+ if (data[k])
+ listB1.push(k);
+ else
+ listB2.push(k);
+ }
+ else
+ {
+ listN.push(getMsg(MSG_SUPPORTS_MISCOPTION, [ k, data[k] ] ));
+ }
+ }
+ listB1.sort();
+ listB2.sort();
+ listN.sort();
+ display(getMsg(MSG_SUPPORTS_FLAGSON, listB1.join(", ")));
+ display(getMsg(MSG_SUPPORTS_FLAGSOFF, listB2.join(", ")));
+ display(getMsg(MSG_SUPPORTS_MISCOPTIONS, listN.join(", ")));
+
+ var listCaps = new Array();
+ var listCapsEnabled = new Array();
+ for (var cap in server.caps)
+ {
+ listCaps.push(cap);
+ if (server.caps[cap])
+ listCapsEnabled.push(cap);
+ }
+ if (listCaps.length > 0)
+ {
+ listCaps.sort();
+ listCapsEnabled.sort();
+ display(getMsg(MSG_SUPPORTS_CAPS, listCaps.join(", ")));
+ display(getMsg(MSG_SUPPORTS_CAPSON, listCapsEnabled.join(", ")));
+ }
+}
+
+function cmdDoCommand(e)
+{
+ if (e.cmdName == "cmd_mozillaPrefs")
+ {
+ // Open SeaMonkey preferences.
+ goPreferences("navigator_pane");
+ }
+ else if (e.cmdName == "cmd_chatzillaPrefs")
+ {
+ var prefWin = getWindowByType("irc:chatzilla:config");
+ if (!prefWin)
+ {
+ window.openDialog('chrome://chatzilla/content/config.xul', '',
+ 'chrome,resizable,dialog=no', window);
+ }
+ else
+ {
+ prefWin.focus();
+ }
+ }
+ else if (e.cmdName == "cmd_selectAll")
+ {
+ var userList = document.getElementById("user-list");
+ var elemFocused = document.commandDispatcher.focusedElement;
+
+ if (userList.view && (elemFocused == userList))
+ userList.view.selection.selectAll();
+ else
+ doCommand("cmd_selectAll");
+ }
+ else
+ {
+ doCommand(e.cmdName);
+ }
+}
+
+function cmdTime(e)
+{
+ if (e.nickname)
+ e.network.dispatch("ctcp", { target: e.nickname, code: "TIME"});
+ else
+ e.server.sendData(fromUnicode("TIME") + "\n", e.sourceObject);
+}
+
+function cmdTimestamps(e)
+{
+ var view = e.sourceObject;
+
+ if (e.toggle != null)
+ {
+ e.toggle = getToggle(e.toggle, view.prefs["timestamps"])
+ view.prefs["timestamps"] = e.toggle;
+ }
+ else
+ {
+ display(getMsg(MSG_FMT_PREF, ["timestamps",
+ view.prefs["timestamps"]]));
+ }
+}
+
+function cmdSetCurrentView(e)
+{
+ if ("lockView" in e.view)
+ delete e.view.lockView;
+
+ setCurrentObject(e.view);
+}
+
+function cmdJumpToAnchor(e)
+{
+ if (e.hasOwnProperty("channelName"))
+ {
+ e.channel = new CIRCChannel(e.server, e.channelName);
+ }
+ else if (!e.channel)
+ {
+ display(getMsg(MSG_ERR_REQUIRED_PARAM, "channel-name"), MT_ERROR);
+ return;
+ }
+ if (!e.channel.frame)
+ {
+ display(getMsg(MSG_JUMPTO_ERR_NOCHAN, e.channel.unicodeName), MT_ERROR);
+ return;
+ }
+
+ var document = getContentDocument(e.channel.frame);
+ var row = document.getElementById(e.anchor);
+
+ if (!row)
+ {
+ display(getMsg(MSG_JUMPTO_ERR_NOANCHOR), MT_ERROR);
+ return;
+ }
+
+ dispatch("set-current-view", {view: e.channel});
+ e.channel.scrollToElement(row, "center");
+}
+
+function cmdIdentify(e)
+{
+ e.password = client.tryToGetLogin(e.server.parent.getURL(), "nick",
+ e.server.me.name, e.password, true,
+ MSG_NEED_IDENTIFY_PASSWORD);
+ if (!e.password)
+ return;
+
+ e.server.sendData("NS IDENTIFY " + fromUnicode(e.password, e.server) +
+ "\n");
+}
+
+function cmdIgnore(e)
+{
+ if (("mask" in e) && e.mask)
+ {
+ e.mask = e.server.toLowerCase(e.mask);
+
+ if (e.command.name == "ignore")
+ {
+ if (e.network.ignore(e.mask))
+ display(getMsg(MSG_IGNORE_ADD, e.mask));
+ else
+ display(getMsg(MSG_IGNORE_ADDERR, e.mask));
+ }
+ else
+ {
+ if (e.network.unignore(e.mask))
+ display(getMsg(MSG_IGNORE_DEL, e.mask));
+ else
+ display(getMsg(MSG_IGNORE_DELERR, e.mask));
+ }
+ // Update pref:
+ var ignoreList = keys(e.network.ignoreList);
+ e.network.prefs["ignoreList"] = ignoreList;
+ e.network.prefs["ignoreList"].update();
+ }
+ else
+ {
+ var list = new Array();
+ for (var m in e.network.ignoreList)
+ list.push(m);
+ if (list.length == 0)
+ display(MSG_IGNORE_LIST_1);
+ else
+ display(getMsg(MSG_IGNORE_LIST_2, arraySpeak(list)));
+ }
+}
+
+function cmdFont(e)
+{
+ var view = client;
+ var pref, val, pVal;
+
+ if (e.command.name == "font-family")
+ {
+ pref = "font.family";
+ val = e.font;
+
+ // Save new value, then display pref value.
+ if (val)
+ view.prefs[pref] = val;
+
+ display(getMsg(MSG_FONTS_FAMILY_FMT, view.prefs[pref]));
+ }
+ else if (e.command.name == "font-size")
+ {
+ pref = "font.size";
+ val = e.fontSize;
+
+ // Ok, we've got an input.
+ if (val)
+ {
+ // Get the current value, use user's default if needed.
+ pVal = view.prefs[pref];
+ if (pVal == 0)
+ pVal = getDefaultFontSize();
+
+ // Handle user's input...
+ switch(val) {
+ case "default":
+ val = 0;
+ break;
+
+ case "small":
+ val = getDefaultFontSize() - 2;
+ break;
+
+ case "medium":
+ val = getDefaultFontSize();
+ break;
+
+ case "large":
+ val = getDefaultFontSize() + 2;
+ break;
+
+ case "smaller":
+ val = pVal - 2;
+ break;
+
+ case "bigger":
+ val = pVal + 2;
+ break;
+
+ default:
+ if (isNaN(val))
+ val = 0;
+ else
+ val = Number(val);
+ }
+ // Save the new value.
+ view.prefs[pref] = val;
+ }
+
+ // Show the user what the pref is set to.
+ if (view.prefs[pref] == 0)
+ display(MSG_FONTS_SIZE_DEFAULT);
+ else
+ display(getMsg(MSG_FONTS_SIZE_FMT, view.prefs[pref]));
+ }
+ else if (e.command.name == "font-family-other")
+ {
+ val = prompt(MSG_FONTS_FAMILY_PICK, view.prefs["font.family"]);
+ if (!val)
+ return;
+
+ dispatch("font-family", { font: val });
+ }
+ else if (e.command.name == "font-size-other")
+ {
+ pVal = view.prefs["font.size"];
+ if (pVal == 0)
+ pVal = getDefaultFontSize();
+
+ val = prompt(MSG_FONTS_SIZE_PICK, pVal);
+ if (!val)
+ return;
+
+ dispatch("font-size", { fontSize: val });
+ }
+}
+
+function cmdDCCChat(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ if (!e.nickname && !e.user)
+ return display(MSG_DCC_ERR_NOUSER);
+
+ var user;
+ if (e.nickname)
+ user = e.server.addUser(e.nickname);
+ else
+ user = e.server.addUser(e.user.unicodeName);
+
+ var u = client.dcc.addUser(user);
+ var c = client.dcc.addChat(u, client.dcc.getNextPort());
+ c.request();
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ var cmd = getMsg(MSG_DCC_COMMAND_CANCEL, "dcc-close " + c.id);
+ display(getMsg(MSG_DCCCHAT_SENT_REQUEST, c._getParams().concat(cmd)),
+ "DCC-CHAT");
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ return true;
+}
+
+function cmdDCCClose(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ // If there is no nickname specified, use current view.
+ if (!e.nickname)
+ {
+ // Both DCC chat and file transfers can be aborted like this.
+ if (e.sourceObject.TYPE.substr(0, 6) == "IRCDCC")
+ {
+ if (e.sourceObject.isActive())
+ return e.sourceObject.abort();
+ return true;
+ }
+ // ...if there is one.
+ return display(MSG_DCC_ERR_NOTDCC);
+ }
+
+ var o = client.dcc.findByID(e.nickname);
+ if (o)
+ // Direct ID --> object request.
+ return o.abort();
+
+ if (e.type)
+ e.type = [e.type.toLowerCase()];
+ else
+ e.type = ["chat", "file"];
+
+ // Go ask the DCC code for some matching requets.
+ var list = client.dcc.getMatches
+ (e.nickname, e.file, e.type, [DCC_DIR_GETTING, DCC_DIR_SENDING],
+ [DCC_STATE_REQUESTED, DCC_STATE_ACCEPTED, DCC_STATE_CONNECTED]);
+
+ // Disconnect if only one match.
+ if (list.length == 1)
+ return list[0].abort();
+
+ // Oops, couldn't figure the user's requets out, so give them some help.
+ display(getMsg(MSG_DCC_ACCEPTED_MATCHES, [list.length]));
+ display(MSG_DCC_MATCHES_HELP);
+ return true;
+}
+
+function cmdDCCSend(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ const DIRSVC_CID = "@mozilla.org/file/directory_service;1";
+ const nsIProperties = Components.interfaces.nsIProperties;
+
+ if (!e.nickname && !e.user)
+ return display(MSG_DCC_ERR_NOUSER);
+
+ // Accept the request passed in...
+ var file;
+ if (!e.file)
+ {
+ var pickerRv = pickOpen(MSG_DCCFILE_SEND);
+ if (!pickerRv.ok)
+ return false;
+ file = pickerRv.file;
+ }
+ else
+ {
+ // Wrap in try/catch because nsIFile creation throws a freaking
+ // error if it doesn't get a FULL path.
+ try
+ {
+ file = nsLocalFile(e.file);
+ }
+ catch(ex)
+ {
+ // Ok, try user's home directory.
+ var fl = Components.classes[DIRSVC_CID].getService(nsIProperties);
+ file = fl.get("Home", Components.interfaces.nsIFile);
+
+ // Another freaking try/catch wrapper.
+ try
+ {
+ // NOTE: This is so pathetic it can't cope with any path
+ // separators in it, so don't even THINK about lobing a
+ // relative path at it.
+ file.append(e.file);
+
+ // Wow. We survived.
+ }
+ catch (ex)
+ {
+ return display(MSG_DCCFILE_ERR_NOTFOUND);
+ }
+ }
+ }
+ if (!file.exists())
+ return display(MSG_DCCFILE_ERR_NOTFOUND);
+ if (!file.isFile())
+ return display(MSG_DCCFILE_ERR_NOTAFILE);
+ if (!file.isReadable())
+ return display(MSG_DCCFILE_ERR_NOTREADABLE);
+
+ var user;
+ if (e.nickname)
+ user = e.server.addUser(e.nickname);
+ else
+ user = e.server.addUser(e.user.unicodeName);
+
+ var u = client.dcc.addUser(user);
+ var c = client.dcc.addFileTransfer(u, client.dcc.getNextPort());
+ c.request(file);
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ var cmd = getMsg(MSG_DCC_COMMAND_CANCEL, "dcc-close " + c.id);
+ display(getMsg(MSG_DCCFILE_SENT_REQUEST, [c.user.unicodeName, c.localIP,
+ c.port, c.filename,
+ getSISize(c.size), cmd]),
+ "DCC-FILE");
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ return true;
+}
+
+function cmdDCCList(e) {
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ var counts = { pending: 0, connected: 0, failed: 0 };
+ var k;
+
+ // Get all the DCC sessions.
+ var list = client.dcc.getMatches();
+
+ for (k = 0; k < list.length; k++) {
+ var c = list[k];
+ var type = c.TYPE.substr(6, c.TYPE.length - 6);
+
+ var dir = MSG_UNKNOWN;
+ var tf = MSG_UNKNOWN;
+ if (c.state.dir == DCC_DIR_SENDING)
+ {
+ dir = MSG_DCCLIST_DIR_OUT;
+ tf = MSG_DCCLIST_TO;
+ }
+ else if (c.state.dir == DCC_DIR_GETTING)
+ {
+ dir = MSG_DCCLIST_DIR_IN;
+ tf = MSG_DCCLIST_FROM;
+ }
+
+ var state = MSG_UNKNOWN;
+ var cmds = "";
+ switch (c.state.state)
+ {
+ case DCC_STATE_REQUESTED:
+ state = MSG_DCC_STATE_REQUEST;
+ if (c.state.dir == DCC_DIR_GETTING)
+ {
+ cmds = getMsg(MSG_DCC_COMMAND_ACCEPT, "dcc-accept " + c.id) + " " +
+ getMsg(MSG_DCC_COMMAND_DECLINE, "dcc-decline " + c.id);
+ }
+ else
+ {
+ cmds = getMsg(MSG_DCC_COMMAND_CANCEL, "dcc-close " + c.id);
+ }
+ counts.pending++;
+ break;
+ case DCC_STATE_ACCEPTED:
+ state = MSG_DCC_STATE_ACCEPT;
+ counts.connected++;
+ break;
+ case DCC_STATE_DECLINED:
+ state = MSG_DCC_STATE_DECLINE;
+ break;
+ case DCC_STATE_CONNECTED:
+ state = MSG_DCC_STATE_CONNECT;
+ cmds = getMsg(MSG_DCC_COMMAND_CLOSE, "dcc-close " + c.id);
+ if (c.TYPE == "IRCDCCFileTransfer")
+ {
+ state = getMsg(MSG_DCC_STATE_CONNECTPRO,
+ [c.progress,
+ getSISize(c.position), getSISize(c.size),
+ getSISpeed(c.speed)]);
+ }
+ counts.connected++;
+ break;
+ case DCC_STATE_DONE:
+ state = MSG_DCC_STATE_DISCONNECT;
+ break;
+ case DCC_STATE_ABORTED:
+ state = MSG_DCC_STATE_ABORT;
+ counts.failed++;
+ break;
+ case DCC_STATE_FAILED:
+ state = MSG_DCC_STATE_FAIL;
+ counts.failed++;
+ break;
+ }
+ client.munger.getRule(".inline-buttons").enabled = true;
+ display(getMsg(MSG_DCCLIST_LINE, [k + 1, state, dir, type, tf,
+ c.unicodeName, c.remoteIP, c.port,
+ cmds]));
+ client.munger.getRule(".inline-buttons").enabled = false;
+ }
+ display(getMsg(MSG_DCCLIST_SUMMARY, [counts.pending, counts.connected,
+ counts.failed]));
+ return true;
+}
+
+function cmdDCCAutoAcceptList(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ var list = e.network.prefs["dcc.autoAccept.list"];
+
+ if (list.length == 0)
+ display(MSG_DCCACCEPT_DISABLED);
+ else
+ display(getMsg(MSG_DCCACCEPT_LIST, arraySpeak(list)));
+
+ return true;
+}
+
+function cmdDCCAutoAcceptAdd(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ var list = e.network.prefs["dcc.autoAccept.list"];
+
+ if (!e.user && e.server)
+ e.user = e.server.getUser(e.nickname);
+
+ var mask = e.user ? "*!" + e.user.name + "@" + e.user.host : e.nickname;
+ var index = arrayIndexOf(list, mask);
+ if (index == -1)
+ {
+ list.push(mask);
+ list.update();
+ display(getMsg(MSG_DCCACCEPT_ADD, mask));
+ }
+ else
+ {
+ display(getMsg(MSG_DCCACCEPT_ADDERR,
+ e.user ? e.user.unicodeName : e.nickname));
+ }
+ return true;
+}
+
+function cmdDCCAutoAcceptDel(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ var list = e.network.prefs["dcc.autoAccept.list"];
+
+ if (!e.user && e.server)
+ e.user = e.server.getUser(e.nickname);
+
+ var maskObj, newList = new Array();
+ for (var m = 0; m < list.length; ++m)
+ {
+ maskObj = getHostmaskParts(list[m]);
+ if (e.nickname == list[m] ||
+ (e.user && hostmaskMatches(e.user, maskObj, e.server)))
+ {
+ display(getMsg(MSG_DCCACCEPT_DEL, list[m]));
+ }
+ else
+ {
+ newList.push(list[m]);
+ }
+ }
+
+ if (list.length > newList.length)
+ e.network.prefs["dcc.autoAccept.list"] = newList;
+ else
+ display(getMsg(MSG_DCCACCEPT_DELERR,
+ e.user ? e.user.unicodeName : e.nickname));
+
+ return true;
+}
+
+function cmdDCCAccept(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ function accept(c)
+ {
+ if (c.TYPE == "IRCDCCChat")
+ {
+ if (!c.accept())
+ return false;
+
+ display(getMsg(MSG_DCCCHAT_ACCEPTED, c._getParams()), "DCC-CHAT");
+ return true;
+ }
+
+ // Accept the request passed in...
+ var filename = c.filename;
+ var ext = "*";
+ var m = filename.match(/...\.([a-z]+)$/i);
+ if (m)
+ ext = "*." + m[1];
+
+ var pickerRv = pickSaveAs(getMsg(MSG_DCCFILE_SAVE_TO, filename),
+ ["$all", ext], filename);
+ if (!pickerRv.ok)
+ return false;
+
+ if (!c.accept(pickerRv.file))
+ return false;
+
+ display(getMsg(MSG_DCCFILE_ACCEPTED, c._getParams()), "DCC-FILE");
+ return true;
+ };
+
+ // If there is no nickname specified, use the "last" item.
+ // This is the last DCC request that arrvied.
+ if (!e.nickname && client.dcc.last)
+ {
+ if ((new Date() - client.dcc.lastTime) >= 10000)
+ return accept(client.dcc.last);
+ return display(MSG_DCC_ERR_ACCEPT_TIME);
+ }
+
+ var o = client.dcc.findByID(e.nickname);
+ if (o)
+ // Direct ID --> object request.
+ return accept(o);
+
+ if (e.type)
+ e.type = [e.type.toLowerCase()];
+ else
+ e.type = ["chat", "file"];
+
+ // Go ask the DCC code for some matching requets.
+ var list = client.dcc.getMatches(e.nickname, e.file, e.type,
+ [DCC_DIR_GETTING], [DCC_STATE_REQUESTED]);
+ // Accept if only one match.
+ if (list.length == 1)
+ return accept(list[0]);
+
+ // Oops, couldn't figure the user's request out, so give them some help.
+ display(getMsg(MSG_DCC_PENDING_MATCHES, [list.length]));
+ display(MSG_DCC_MATCHES_HELP);
+ return true;
+}
+
+function cmdDCCDecline(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return display(MSG_DCC_NOT_ENABLED);
+
+ function decline(c)
+ {
+ // Decline the request passed in...
+ c.decline();
+ if (c.TYPE == "IRCDCCChat")
+ display(getMsg(MSG_DCCCHAT_DECLINED, c._getParams()), "DCC-CHAT");
+ else
+ display(getMsg(MSG_DCCFILE_DECLINED, c._getParams()), "DCC-FILE");
+ };
+
+ // If there is no nickname specified, use the "last" item.
+ // This is the last DCC request that arrvied.
+ if (!e.nickname && client.dcc.last)
+ return decline(client.dcc.last);
+
+ var o = client.dcc.findByID(e.nickname);
+ if (o)
+ // Direct ID --> object request.
+ return decline(o);
+
+ if (!e.type)
+ e.type = ["chat", "file"];
+
+ // Go ask the DCC code for some matching requets.
+ var list = client.dcc.getMatches(e.nickname, e.file, e.type,
+ [DCC_DIR_GETTING], [DCC_STATE_REQUESTED]);
+ // Decline if only one match.
+ if (list.length == 1)
+ return decline(list[0]);
+
+ // Oops, couldn't figure the user's requets out, so give them some help.
+ display(getMsg(MSG_DCC_PENDING_MATCHES, [list.length]));
+ display(MSG_DCC_MATCHES_HELP);
+ return true;
+}
+
+function cmdDCCShowFile(e)
+{
+ var f = getFileFromURLSpec(e.file);
+ if (f)
+ f = nsLocalFile(f.path);
+ if (f && f.parent && f.parent.exists())
+ {
+ try
+ {
+ f.reveal();
+ }
+ catch (ex)
+ {
+ dd(formatException(ex));
+ }
+ }
+}
+
+function cmdTextDirection(e)
+{
+ var direction;
+ var sourceObject = getContentDocument(e.sourceObject.frame).body;
+
+ switch (e.dir)
+ {
+ case "toggle":
+ if (sourceObject.getAttribute("dir") == "rtl")
+ direction = 'ltr';
+ else
+ direction = 'rtl';
+ break;
+ case "rtl":
+ direction = 'rtl';
+ break;
+ default:
+ // that is "case "ltr":",
+ // but even if !e.dir OR e.dir is an invalid value -> set to
+ // default direction
+ direction = 'ltr';
+ }
+ client.input.setAttribute("dir", direction);
+ sourceObject.setAttribute("dir", direction);
+
+ return true;
+}
+
+function cmdInputTextDirection(e)
+{
+ var direction;
+
+ switch (e.dir)
+ {
+ case "rtl":
+ client.input.setAttribute("dir", "rtl");
+ break
+ default:
+ // that is "case "ltr":", but even if !e.dir OR e.dir is an
+ //invalid value -> set to default direction
+ client.input.setAttribute("dir", "ltr");
+ }
+
+ return true;
+}
+
+function cmdInstallPlugin(e)
+{
+ var ipURL = "chrome://chatzilla/content/install-plugin/install-plugin.xul";
+ var ctx = {};
+ var pluginDownloader =
+ {
+ onStartRequest: function _onStartRequest(request, context)
+ {
+ var tempName = "plugin-install.temp";
+ if (urlMatches)
+ tempName += urlMatches[2];
+
+ ctx.outFile = getTempFile(client.prefs["profilePath"], tempName);
+ ctx.outFileH = fopen(ctx.outFile, ">");
+ },
+ onDataAvailable: function _onDataAvailable(request, context, stream,
+ offset, count)
+ {
+ if (!ctx.inputStream)
+ ctx.inputStream = toSInputStream(stream, true);
+
+ ctx.outFileH.write(ctx.inputStream.readBytes(count));
+ },
+ onStopRequest: function _onStopRequest(request, context, statusCode)
+ {
+ ctx.outFileH.close();
+
+ if (statusCode == 0)
+ {
+ client.installPlugin(e.name, ctx.outFile);
+ }
+ else
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_DOWNLOAD, statusCode),
+ MT_ERROR);
+ }
+
+ try
+ {
+ ctx.outFile.remove(false);
+ }
+ catch (ex)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_REMOVE_TEMP, ex),
+ MT_ERROR);
+ }
+ }
+ };
+
+ if (!e.url)
+ {
+ if ("installPluginDialog" in client)
+ return client.installPluginDialog.focus();
+
+ window.openDialog(ipURL, "", "chrome,dialog", client);
+ return;
+ }
+
+ var urlMatches = e.url.match(/([^\/]+?)((\..{0,3}){0,2})$/);
+ if (!e.name)
+ {
+ if (urlMatches)
+ {
+ e.name = urlMatches[1];
+ }
+ else
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_NO_NAME, MT_ERROR);
+ return;
+ }
+ }
+
+ // Do real install here.
+ switch (e.url.match(/^[^:]+/)[0])
+ {
+ case "file":
+ client.installPlugin(e.name, e.url);
+ break;
+
+ case "http":
+ case "https":
+ try
+ {
+ var channel = Services.io.newChannel(
+ e.url, "UTF-8", null, null,
+ Services.scriptSecurityManager.getSystemPrincipal(), null,
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ display(getMsg(MSG_INSTALL_PLUGIN_DOWNLOADING, e.url),
+ MT_INFO);
+ channel.asyncOpen(pluginDownloader, { e: e });
+ }
+ catch (ex)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_DOWNLOAD, ex), MT_ERROR);
+ return;
+ }
+ break;
+
+ default:
+ display(MSG_INSTALL_PLUGIN_ERR_PROTOCOL, MT_ERROR);
+ }
+}
+
+function cmdUninstallPlugin(e)
+{
+ if (e.plugin)
+ {
+ client.uninstallPlugin(e.plugin);
+ }
+}
+
+function cmdFind(e)
+{
+ if (!e.rest)
+ {
+ findInPage(getFindData(e));
+ return;
+ }
+
+ // Used from the inputbox, set the search string and find the first
+ // occurrence using find-again.
+ const FINDSVC_ID = "@mozilla.org/find/find_service;1";
+ var findService = getService(FINDSVC_ID, "nsIFindService");
+ // Make sure it searches the entire document, but don't lose the old setting
+ var oldWrap = findService.wrapFind;
+ findService.wrapFind = true;
+ findService.searchString = e.rest;
+ findAgainInPage(getFindData(e));
+ // Restore wrap setting:
+ findService.wrapFind = oldWrap;
+}
+
+function cmdFindAgain(e)
+{
+ if (canFindAgainInPage())
+ findAgainInPage(getFindData(e));
+}
+
+function cmdURLs(e)
+{
+ var urls = client.urlLogger.read().reverse();
+
+ if (urls.length == 0)
+ {
+ display(MSG_URLS_NONE);
+ }
+ else
+ {
+ /* Temporarily remove the URL logger to avoid changing the list when
+ * displaying it.
+ */
+ var logger = client.urlLogger;
+ delete client.urlLogger;
+
+ var num = e.number || client.prefs["urls.display"];
+ if (num > urls.length)
+ num = urls.length;
+ display(getMsg(MSG_URLS_HEADER, num));
+
+ for (var i = 0; i < num; i++)
+ display(getMsg(MSG_URLS_ITEM, [i + 1, urls[i]]));
+
+ client.urlLogger = logger;
+ }
+}
+
+function cmdWebSearch(e)
+{
+ let submission = Services.search.currentEngine
+ .getSubmission(e.selectedText);
+ let newTabPref = Services.prefs.getBoolPref("browser.search.opentabforcontextsearch");
+ dispatch(newTabPref ? "goto-url-newtab" : "goto-url-newwin",
+ {url: submission.uri.asciiSpec,
+ shiftKey: e.shiftKey});
+}
diff --git a/comm/suite/chatzilla/xul/content/config-add.js b/comm/suite/chatzilla/xul/content/config-add.js
new file mode 100644
index 0000000000..81034e2071
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/config-add.js
@@ -0,0 +1,55 @@
+/* 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 rv, rad, box1, box2;
+
+function changeType()
+{
+ box2.disabled = (rad.value == "net");
+}
+
+function onOK()
+{
+ rv.ok = true;
+
+ rv.type = rad.value;
+ rv.net = box1.value;
+ rv.chan = box2.value;
+
+ return true;
+}
+
+function onCancel()
+{
+ rv.ok = false;
+
+ return true;
+}
+
+function onLoad()
+{
+ rad = document.getElementById("prefType");
+ box1 = document.getElementById("prefName1");
+ box2 = document.getElementById("prefName2");
+
+ rv = window.arguments[0];
+
+ if (!("type" in rv))
+ rv.type = "";
+ if (!("net" in rv))
+ rv.net = "";
+ if (!("chan" in rv))
+ rv.chan = "";
+ rv.ok = false;
+
+ if (rv.type == "net")
+ rad.selectedIndex = 0;
+ if (rv.type == "chan")
+ rad.selectedIndex = 1;
+ if (rv.type == "user")
+ rad.selectedIndex = 2;
+
+ box1.value = rv.net || "";
+ box2.value = rv.chan || "";
+}
diff --git a/comm/suite/chatzilla/xul/content/config-add.xul b/comm/suite/chatzilla/xul/content/config-add.xul
new file mode 100644
index 0000000000..84834ca5a7
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/config-add.xul
@@ -0,0 +1,55 @@
+<?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 dialog SYSTEM "chrome://chatzilla/locale/config.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="config.css" type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ windowtype="irc:chatzilla:config:add"
+ buttons="accept,cancel"
+ ondialogaccept="onOK();"
+ ondialogcancel="onCancel();"
+ onload="onLoad();"
+ title="&config.add.title;">
+
+ <script src="config-add.js"/>
+
+ <vbox>
+ <hbox align="center">
+ <label value="&config.type.label;" accesskey="&config.type.accesskey;"
+ tooltiptext="&config.type.hint;" control="prefType"/>
+ <!-- Beware the hacks, number 264: add |value| attribute to make .value
+ work when the dialog loads (|selected| selects an item, but doesn't
+ set .value). -->
+ <radiogroup orient="horizontal" id="prefType" value="chan"
+ onselect="changeType();">
+ <radio value="net" label="&network;"/>
+ <radio value="chan" label="&channel;" selected="true"/>
+ <radio value="user" label="&user;"/>
+ </radiogroup>
+ </hbox>
+ <separator class="groove"/>
+ <grid>
+ <columns><column/><column flex="1"/></columns>
+ <rows>
+ <row align="center">
+ <label value="&config.network.label;" control="prefName1"
+ tooltiptext="&config.network.hint;"
+ accesskey="&config.network.accesskey;"/>
+ <textbox id="prefName1"/>
+ </row>
+ <row align="center">
+ <label value="&config.target.label;" control="prefName2"
+ tooltiptext="&config.target.hint;"
+ accesskey="&config.target.accesskey;"/>
+ <textbox id="prefName2"/>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/config.css b/comm/suite/chatzilla/xul/content/config.css
new file mode 100644
index 0000000000..dfbd77fa5b
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/config.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* OSX uses lotsa padding on tabs. Need to allow window to expand. */
+
+/* Instead of setting on dialog, we're going to set it on the two main
+ * components of the window, so that large changes in the size of bits of the
+ * dialog don't upset things.
+ */
+
+/* Set min-width on the left-side tree. */
+#pref-objects {
+ min-width: 25ex;
+}
+
+/* Set min-width and min-height on tabs container. */
+#pref-object-deck {
+ min-width: 65ex;
+ min-height: 32em;
+ width: 65ex;
+ height: 32em;
+}
+
+scroller {
+ overflow: auto;
+}
+
+listbox {
+ min-height: 7em;
+}
diff --git a/comm/suite/chatzilla/xul/content/config.js b/comm/suite/chatzilla/xul/content/config.js
new file mode 100644
index 0000000000..12eefa9da3
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/config.js
@@ -0,0 +1,1775 @@
+/* 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 MEDIATOR_CONTRACTID = "@mozilla.org/appshell/window-mediator;1";
+
+const nsIWindowMediator = Components.interfaces.nsIWindowMediator;
+
+const CONFIG_WINDOWTYPE = "irc:chatzilla:config";
+
+/* Now we create and set up some required items from other Chatzilla JS files
+ * that we really have no reason to load, but the ones we do need won't work
+ * without these...
+ */
+var ASSERT = function(cond, msg) { if (!cond) { alert(msg); } return cond; }
+var client;
+
+function CIRCNetwork() {}
+function CIRCServer() {}
+function CIRCChannel() {}
+function CIRCChanUser() {}
+function CIRCUser() {}
+function CIRCDCC() {}
+function CIRCDCCUser() {}
+function CIRCDCCChat() {}
+function CIRCDCCFileTransfer() {}
+function CIRCSTS() {}
+
+function getObjectDetails(obj)
+{
+ var rv = new Object();
+ rv.sourceObject = obj;
+ rv.TYPE = obj.TYPE;
+ rv.parent = ("parent" in obj) ? obj.parent : null;
+ rv.user = null;
+ rv.channel = null;
+ rv.server = null;
+ rv.network = null;
+
+ switch (obj.TYPE)
+ {
+ case "PrefNetwork":
+ rv.network = obj;
+ if ("primServ" in rv.network)
+ rv.server = rv.network.primServ;
+ else
+ rv.server = null;
+ break;
+
+ case "PrefChannel":
+ rv.channel = obj;
+ rv.server = rv.channel.parent;
+ rv.network = rv.server.parent;
+ break;
+
+ case "PrefUser":
+ rv.user = obj;
+ rv.server = rv.user.parent;
+ rv.network = rv.server.parent;
+ break;
+ }
+
+ return rv;
+}
+
+/* Global object for the prefs. The 'root' of all the objects to do with the
+ * prefs.
+ */
+function PrefGlobal()
+{
+ this.networks = new Object();
+ this.commandManager = new Object();
+ this.commandManager.defineCommand = function() {};
+ this.commandManager.removeCommand = function() {};
+ this.entities = new Object();
+ this.hostCompat = new Object();
+}
+PrefGlobal.prototype.TYPE = "PrefGlobal";
+
+/* Represents a single network in the hierarchy.
+ *
+ * |force| - If true, sets a pref on this object. This makes sure the object
+ * is "known" next time we load up (since we look for any prefs).
+ *
+ * |show| - If true, the object still exists even if the magic pref is not set.
+ * Thus, allows an object to exist without any prefs set.
+ */
+function PrefNetwork(parent, name, force, show)
+{
+ if (":" + name in parent.networks)
+ return parent.networks[":" + name];
+
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = getMsg(MSG_PREFS_FMT_DISPLAY_NETWORK, this.unicodeName);
+ this.servers = new Object();
+ this.primServ = new PrefServer(this, "dummy server");
+ this.channels = this.primServ.channels;
+ this.users = this.primServ.users;
+ this.prefManager = getNetworkPrefManager(this);
+ this.prefs = this.prefManager.prefs;
+ this.prefManager.onPrefChanged = function(){};
+
+ if (force)
+ this.prefs["hasPrefs"] = true;
+
+ if (this.prefs["hasPrefs"] || show)
+ this.parent.networks[this.collectionKey] = this;
+
+ return this;
+};
+PrefNetwork.prototype.TYPE = "PrefNetwork";
+
+/* Cleans up the mess. */
+PrefNetwork.prototype.clear =
+function pnet_clear()
+{
+ this.prefs["hasPrefs"] = false;
+ delete this.parent.networks[this.collectionKey];
+}
+
+/* A middle-management object.
+ *
+ * Exists only to satisfy the IRC library pref functions that expect this
+ * particular hierarchy.
+ */
+function PrefServer(parent, name)
+{
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = this.unicodeName; // Not used, thus not localised.
+ this.channels = new Object();
+ this.users = new Object();
+ this.parent.servers[this.collectionKey] = this;
+ return this;
+};
+PrefServer.prototype.TYPE = "PrefServer";
+
+/* Represents a single channel in the hierarchy.
+ *
+ * |force| and |show| the same as PrefNetwork.
+ */
+function PrefChannel(parent, name, force, show)
+{
+ if (":" + name in parent.channels)
+ return parent.channels[":" + name];
+
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = getMsg(MSG_PREFS_FMT_DISPLAY_CHANNEL,
+ [this.parent.parent.unicodeName, this.unicodeName]);
+ this.prefManager = getChannelPrefManager(this);
+ this.prefs = this.prefManager.prefs;
+ this.prefManager.onPrefChanged = function(){};
+
+ if (force)
+ this.prefs["hasPrefs"] = true;
+
+ if (this.prefs["hasPrefs"] || show)
+ this.parent.channels[this.collectionKey] = this;
+
+ return this;
+};
+PrefChannel.prototype.TYPE = "PrefChannel";
+
+/* Cleans up the mess. */
+PrefChannel.prototype.clear =
+function pchan_clear()
+{
+ this.prefs["hasPrefs"] = false;
+ delete this.parent.channels[this.collectionKey];
+}
+
+/* Represents a single user in the hierarchy.
+ *
+ * |force| and |show| the same as PrefNetwork.
+ */
+function PrefUser(parent, name, force, show)
+{
+ if (":" + name in parent.users)
+ return parent.users[":" + name];
+
+ this.parent = parent;
+ this.unicodeName = name;
+ this.viewName = name;
+ this.canonicalName = name;
+ this.collectionKey = ":" + name;
+ this.encodedName = name;
+ this.prettyName = getMsg(MSG_PREFS_FMT_DISPLAY_USER,
+ [this.parent.parent.unicodeName, this.unicodeName]);
+ this.prefManager = getUserPrefManager(this);
+ this.prefs = this.prefManager.prefs;
+ this.prefManager.onPrefChanged = function(){};
+
+ if (force)
+ this.prefs["hasPrefs"] = true;
+
+ if (this.prefs["hasPrefs"] || show)
+ this.parent.users[this.collectionKey] = this;
+
+ return this;
+};
+PrefUser.prototype.TYPE = "PrefUser";
+
+/* Cleans up the mess. */
+PrefUser.prototype.clear =
+function puser_clear()
+{
+ this.prefs["hasPrefs"] = false;
+ delete this.parent.users[this.collectionKey];
+}
+
+// Stores a list of |PrefObject|s.
+function PrefObjectList()
+{
+ this.objects = new Array();
+
+ return this;
+}
+
+// Add an object, and init it's private data.
+PrefObjectList.prototype.addObject =
+function polist_addObject(pObject)
+{
+ this.objects.push(pObject);
+ return pObject.privateData = new ObjectPrivateData(pObject, this.objects.length - 1);
+}
+
+/* Removes an object, without changing the index. */
+PrefObjectList.prototype.deleteObject =
+function polist_addObject(index)
+{
+ this.objects[index].privateData.clear();
+ this.objects[index].clear();
+ this.objects[index] = { privateData: null };
+}
+
+// Get a specific object.
+PrefObjectList.prototype.getObject =
+function polist_getObject(index)
+{
+ return this.objects[index].privateData;
+}
+
+// Gets the private data for an object.
+PrefObjectList.getPrivateData =
+function polist_getPrivateData(object)
+{
+ return object.privateData;
+}
+
+// Stores the pref object's private data.
+function ObjectPrivateData(parent, index)
+{
+ this.parent = parent; // Real pref object.
+ this.prefs = new Object();
+ this.groups = new Object();
+
+ this.arrayIndex = index;
+ this.deckIndex = -1;
+ this.dataLoaded = false;
+
+ var treeObj = document.getElementById("pref-tree-object");
+ this.tree = document.getElementById("pref-tree");
+ this.treeContainer = document.createElement("treeitem");
+ this.treeNode = document.createElement("treerow");
+ this.treeCell = document.createElement("treecell");
+
+ this.treeContainer.setAttribute("prefobjectindex", this.arrayIndex);
+ this.treeCell.setAttribute("label", this.parent.unicodeName);
+
+ switch (this.parent.TYPE)
+ {
+ case "PrefChannel":
+ case "PrefUser":
+ var p = this.parent.parent.parent; // Network object.
+ var pData = PrefObjectList.getPrivateData(p);
+
+ if (!("treeChildren" in pData) || !pData.treeChildren)
+ {
+ pData.treeChildren = document.createElement("treechildren");
+ pData.treeContainer.appendChild(pData.treeChildren);
+ treeObj.view.toggleOpenState(treeObj.view.rowCount - 1);
+ }
+ pData.treeContainer.setAttribute("container", "true");
+ pData.treeChildren.appendChild(this.treeContainer);
+ break;
+
+ default:
+ this.tree.appendChild(this.treeContainer);
+ break;
+ }
+
+ this.treeContainer.appendChild(this.treeNode);
+ this.treeNode.appendChild(this.treeCell);
+
+ return this;
+}
+
+// Creates all the XUL elements needed to show this pref object.
+ObjectPrivateData.prototype.loadXUL =
+function opdata_loadXUL(tabOrder)
+{
+ var t = this;
+
+ /* Function that sorts the preferences by their label, else they look
+ * fairly random in order.
+ *
+ * Sort keys: not grouped, sub-group name, boolean, pref label.
+ */
+ function sortByLabel(a, b) {
+ if (t.prefs[a].subGroup || t.prefs[b].subGroup)
+ {
+ // Non-grouped go first.
+ if (!t.prefs[a].subGroup)
+ return -1;
+ if (!t.prefs[b].subGroup)
+ return 1;
+
+ // Sub-group names.
+ if (t.prefs[a].subGroup < t.prefs[b].subGroup)
+ return -1;
+ if (t.prefs[a].subGroup > t.prefs[b].subGroup)
+ return 1;
+ }
+
+ // Booleans go first.
+ if ((t.prefs[a].type == "boolean") && (t.prefs[b].type != "boolean"))
+ return -1;
+ if ((t.prefs[a].type != "boolean") && (t.prefs[b].type == "boolean"))
+ return 1;
+
+ // ...then label.
+ if (t.prefs[a].label < t.prefs[b].label)
+ return -1;
+ if (t.prefs[a].label > t.prefs[b].label)
+ return 1;
+ return 0;
+ };
+
+ if (this.deckIndex >= 0)
+ return;
+
+ this.deck = document.getElementById("pref-object-deck");
+ this.tabbox = document.createElement("tabbox");
+ this.tabs = document.createElement("tabs");
+ this.tabPanels = document.createElement("tabpanels");
+
+ this.tabbox.setAttribute("flex", 1);
+ this.tabPanels.setAttribute("flex", 1);
+
+ this.tabbox.appendChild(this.tabs);
+ this.tabbox.appendChild(this.tabPanels);
+ this.deck.appendChild(this.tabbox);
+
+ this.deckIndex = this.deck.childNodes.length - 1;
+
+ this.loadData();
+
+ var prefList = keys(this.prefs);
+ prefList.sort(sortByLabel);
+
+ for (var i = 0; i < tabOrder.length; i++)
+ {
+ var pto = tabOrder[i];
+ var needTab = pto.fixed;
+ if (!needTab)
+ {
+ // Not a "always visible" tab, check we need it.
+ for (var j = 0; j < prefList.length; j++)
+ {
+ if (this.prefs[prefList[j]].mainGroup == pto.name)
+ {
+ needTab = true;
+ break;
+ }
+ }
+ }
+ if (needTab)
+ this.addGroup(pto.name);
+ }
+
+ for (i = 0; i < prefList.length; i++)
+ this.prefs[prefList[i]].loadXUL();
+
+ if (this.tabs.childNodes.length > 0)
+ this.tabbox.selectedIndex = 0;
+}
+
+// Loads all the prefs.
+ObjectPrivateData.prototype.loadData =
+function opdata_loadData()
+{
+ if (this.dataLoaded)
+ return;
+
+ this.dataLoaded = true;
+
+ // Now get the list of pref names, and add them...
+ var prefList = this.parent.prefManager.prefNames;
+
+ for (var i in prefList)
+ this.addPref(prefList[i]);
+}
+
+// Clears up all the XUL objects and data.
+ObjectPrivateData.prototype.clear =
+function opdata_clear()
+{
+ //dd("Removing prefs for " + this.parent.displayName + " {");
+ if (!this.dataLoaded)
+ this.loadData();
+ for (var i in this.prefs)
+ this.prefs[i].clear();
+ //dd("}");
+
+ if (this.deckIndex >= 0)
+ {
+ this.deck.removeChild(this.tabbox);
+ this.treeContainer.removeAttribute("container");
+ this.treeContainer.parentNode.removeChild(this.treeContainer);
+ }
+}
+
+// Resets all the prefs to their original values.
+ObjectPrivateData.prototype.reset =
+function opdata_reset()
+{
+ for (var i in this.prefs)
+ if (this.prefs[i].type != "hidden")
+ this.prefs[i].reset();
+}
+
+// Adds a pref to the internal data structures.
+ObjectPrivateData.prototype.addPref =
+function opdata_addPref(name)
+{
+ return this.prefs[name] = new PrefData(this, name);
+}
+
+// Adds a group to a pref object's data.
+ObjectPrivateData.prototype.addGroup =
+function opdata_addPref(name)
+{
+ // Special group for prefs we don't want shown (nothing sinister here).
+ if (name == "hidden")
+ return null;
+
+ if (!(name in this.groups))
+ this.groups[name] = new PrefMainGroup(this, name);
+
+ return this.groups[name];
+}
+
+// Represents a single pref on a single object within the pref window.
+function PrefData(parent, name)
+{
+ // We want to keep all this "worked out" info, so make a hash of all
+ // the prefs on the pwData [Pref Window Data] property of the object.
+
+ // First, lets find out what kind of pref we've got:
+ this.parent = parent; // Private data for pref object.
+ this.name = name;
+ this.manager = this.parent.parent.prefManager; // PrefManager.
+ this.record = this.manager.prefRecords[name]; // PrefRecord.
+ this.def = this.record.defaultValue; // Default value.
+ this.type = typeof this.def; // Pref type.
+ this.val = this.manager.prefs[name]; // Current value.
+ this.startVal = this.val; // Start value.
+ this.label = this.record.label; // Display name.
+ this.help = this.record.help; // Help text.
+ this.group = this.record.group; // Group identifier.
+ this.labelFor = "none"; // Auto-grouped label.
+
+ // Handle defered prefs (call defer function, and use resulting
+ // value/type instead).
+ if (this.type == "function")
+ this.def = this.def(this.name);
+ this.type = typeof this.def;
+
+ // And those arrays... this just makes our life easier later by having
+ // a particular name for array prefs.
+ if (isinstance(this.def, Array))
+ this.type = "array";
+
+ if (this.group == "hidden")
+ this.type = "hidden";
+
+ // Convert "a.b" into sub-properties...
+ var m = this.group.match(/^([^.]*)(\.(.*))?$/)
+ ASSERT(m, "Failed group match!");
+ this.mainGroup = m[1];
+ this.subGroup = m[3];
+
+ return this;
+}
+
+/* Creates all the XUL elements to display this one pref. */
+PrefData.prototype.loadXUL =
+function pdata_loadXUL()
+{
+ if (this.type == "hidden")
+ return;
+
+ // Create the base box for the pref.
+ this.box = document.createElement("box");
+ this.box.orient = "horizontal";
+ this.box.setAttribute("align", "center");
+
+ switch (this.type)
+ {
+ case "string":
+ label = document.createElement("label");
+ label.setAttribute("value", this.label);
+ label.width = 100;
+ label.flex = 1;
+ this.box.appendChild(label);
+
+ this.edit = document.createElement("textbox");
+ // We choose the size based on the length of the default.
+ if (this.def.length < 8)
+ this.edit.setAttribute("size", "10");
+ else if (this.def.length < 20)
+ this.edit.setAttribute("size", "25");
+ else
+ this.edit.flex = 1;
+
+ var editCont = document.createElement("hbox");
+ editCont.flex = 1000;
+ editCont.appendChild(this.edit);
+ this.box.appendChild(editCont);
+
+ // But if it's a file/URL...
+ if (this.def.match(/^([a-z]+:\/|[a-z]:\\)/i))
+ {
+ // ...we make it as big as possible.
+ this.edit.removeAttribute("size");
+ this.edit.flex = 1;
+
+ if (!this.name.match(/path$/i) &&
+ (this.def.match(/^(file|chrome):\//i) ||
+ this.name.match(/filename$/i)))
+ {
+ // So long as the pref name doesn't end in "path", and
+ // it's chrome:, file: or a local file, we add the button.
+ var ext = "";
+ var m = this.def.match(/\.([a-z0-9]+)$/);
+ if (m)
+ ext = "*." + m[1];
+
+ // We're cheating again here, if it ends "filename" it's
+ // a local file path.
+ var type = (this.name.match(/filename$/i) ? "file" : "fileurl");
+ type = (this.name.match(/folder$/i) ? "folder" : type);
+ appendButton(this.box, "onPrefBrowse",
+ { label: MSG_PREFS_BROWSE, spec: ext,
+ kind: type });
+ }
+ }
+ break;
+
+ case "number":
+ label = document.createElement("label");
+ label.setAttribute("value", this.label);
+ label.width = 100;
+ label.flex = 1;
+ this.box.appendChild(label);
+
+ this.edit = document.createElement("textbox");
+ this.edit.setAttribute("size", "5");
+ this.edit.setAttribute("type", "number");
+ this.edit.setAttribute("min", "-1");
+
+ editCont = document.createElement("hbox");
+ editCont.flex = 1000;
+ editCont.appendChild(this.edit);
+ this.box.appendChild(editCont);
+ break;
+
+ case "boolean":
+ this.edit = document.createElement("checkbox");
+ this.edit.setAttribute("label", this.label);
+ this.box.appendChild(this.edit);
+ break;
+
+ case "array":
+ this.box.removeAttribute("align");
+
+ var oBox = document.createElement("box");
+ oBox.orient = "vertical";
+ oBox.flex = 1;
+ this.box.appendChild(oBox);
+
+ if (this.help)
+ {
+ label = document.createElement("label");
+ label.appendChild(document.createTextNode(this.help));
+ oBox.appendChild(label);
+ }
+
+ this.edit = document.createElement("listbox");
+ this.edit.flex = 1;
+ this.edit.setAttribute("style", "height: 1em;");
+ this.edit.setAttribute("kind", "url");
+ if (this.def.length > 0 && this.def[0].match(/^file:\//))
+ this.edit.setAttribute("kind", "fileurl");
+ this.edit.setAttribute("onselect", "gPrefWindow.onPrefListSelect(this);");
+ this.edit.setAttribute("ondblclick", "gPrefWindow.onPrefListEdit(this);");
+ oBox.appendChild(this.edit);
+
+ var box = document.createElement("box");
+ box.orient = "vertical";
+ this.box.appendChild(box);
+
+ // NOTE: This order is important - getRelatedItem needs to be
+ // kept in sync with this order. Perhaps a better way is needed...
+ appendButton(box, "onPrefListUp", { label: MSG_PREFS_MOVE_UP,
+ "class": "up" });
+ appendButton(box, "onPrefListDown", { label: MSG_PREFS_MOVE_DOWN,
+ "class": "down" });
+ appendSeparator(box);
+ appendButton(box, "onPrefListAdd", { label: MSG_PREFS_ADD });
+ appendButton(box, "onPrefListEdit", { label: MSG_PREFS_EDIT });
+ appendButton(box, "onPrefListDelete", { label: MSG_PREFS_DELETE });
+ break;
+
+ default:
+ // This is really more of an error case, since we really should
+ // know about all the valid pref types.
+ var label = document.createElement("label");
+ label.setAttribute("value", "[not editable] " + this.type);
+ this.box.appendChild(label);
+ }
+
+ this.loadData();
+
+ if (this.edit)
+ {
+ this.edit.setAttribute("prefobjectindex", this.parent.arrayIndex);
+ this.edit.setAttribute("prefname", this.name);
+ // Associate textbox with label for accessibility.
+ if (label)
+ {
+ this.edit.id = this.manager.branchName + this.name;
+ label.setAttribute("control", this.edit.id);
+ }
+ }
+
+ if (!ASSERT("groups" in this.parent, "Must have called " +
+ "[ObjectPrivateData].loadXUL before trying to display prefs."))
+ return;
+
+ this.parent.addGroup(this.mainGroup);
+ if (this.subGroup)
+ this.parent.groups[this.mainGroup].addGroup(this.subGroup);
+
+ if (!this.subGroup)
+ this.parent.groups[this.mainGroup].box.appendChild(this.box);
+ else
+ this.parent.groups[this.mainGroup].groups[this.subGroup].box.appendChild(this.box);
+
+ // Setup tooltip stuff...
+ if (this.help && (this.type != "array"))
+ {
+ this.box.setAttribute("tooltiptitle", this.label);
+ this.box.setAttribute("tooltipcontent", this.help);
+ this.box.setAttribute("onmouseover", "gPrefWindow.onPrefMouseOver(this);");
+ this.box.setAttribute("onmousemove", "gPrefWindow.onPrefMouseMove(this);");
+ this.box.setAttribute("onmouseout", "gPrefWindow.onPrefMouseOut(this);");
+ }
+}
+
+/* Loads the pref's data into the edit component. */
+PrefData.prototype.loadData =
+function pdata_loadData()
+{
+ /* Note about .value and .setAttribute as used here:
+ *
+ * XBL doesn't kick in until CSS is calculated on a node, so the code makes
+ * a compromise and uses these two methods as appropriate. Initally this
+ * is called is before the node has been placed in the document DOM tree,
+ * and thus hasn't been "magiced" by XBL and so .value is meaningless to
+ * it. After initally being set as an attribute, it's added to the DOM,
+ * XBL kicks in, and after that .value is the only way to change the value.
+ */
+ switch (this.type)
+ {
+ case "string":
+ if (this.edit.hasAttribute("value"))
+ this.edit.value = this.val;
+ else
+ this.edit.setAttribute("value", this.val);
+ break;
+
+ case "number":
+ if (this.edit.hasAttribute("value"))
+ this.edit.value = this.val;
+ else
+ this.edit.setAttribute("value", this.val);
+ break;
+
+ case "boolean":
+ if (this.edit.hasAttribute("checked"))
+ this.edit.checked = this.val;
+ else
+ this.edit.setAttribute("checked", this.val);
+ break;
+
+ case "array":
+ // Remove old entires.
+ while (this.edit.firstChild)
+ this.edit.removeChild(this.edit.firstChild);
+
+ // Add new ones.
+ for (var i = 0; i < this.val.length; i++)
+ {
+ var item = document.createElement("listitem");
+ item.value = this.val[i];
+ item.crop = "center";
+ item.setAttribute("label", this.val[i]);
+ this.edit.appendChild(item);
+ }
+
+ // Make sure buttons are up-to-date.
+ gPrefWindow.onPrefListSelect(this.edit);
+ break;
+
+ default:
+ }
+}
+
+/* Cleans up the mess. */
+PrefData.prototype.clear =
+function pdata_clear()
+{
+ //dd("Clearing pref " + this.name);
+ if (("box" in this) && this.box)
+ {
+ this.box.parentNode.removeChild(this.box);
+ delete this.box;
+ }
+ try {
+ this.manager.clearPref(this.name);
+ } catch(ex) {}
+}
+
+/* Resets the pref to it's default. */
+PrefData.prototype.reset =
+function pdata_reset()
+{
+ //try {
+ // this.manager.clearPref(this.name);
+ //} catch(ex) {}
+ this.val = this.def;
+ this.loadData();
+}
+
+/* Saves the pref... or would do. */
+PrefData.prototype.save =
+function pdata_save()
+{
+ //FIXME//
+}
+
+// Represents a "main group", i.e. a tab for a single pref object.
+function PrefMainGroup(parent, name)
+{
+ // Init this group's object.
+ this.parent = parent; // Private data for pref object.
+ this.name = name;
+ this.groups = new Object();
+ this.label = getMsg("pref.group." + this.name + ".label", null, this.name);
+ this.tab = document.createElement("tab");
+ this.tabPanel = document.createElement("tabpanel");
+ this.box = this.sb = document.createElement("scroller");
+
+ this.tab.setAttribute("label", this.label);
+ this.tabPanel.setAttribute("orient", "vertical");
+ this.sb.setAttribute("orient", "vertical");
+ this.sb.setAttribute("flex", 1);
+
+ this.parent.tabs.appendChild(this.tab);
+ this.parent.tabPanels.appendChild(this.tabPanel);
+ this.tabPanel.appendChild(this.sb);
+
+ return this;
+}
+// Adds a sub group to this main group.
+PrefMainGroup.prototype.addGroup =
+function pmgroup_addGroup(name)
+{
+ // If the sub group doesn't exist, make it exist.
+ if (!(name in this.groups))
+ this.groups[name] = new PrefSubGroup(this, name);
+
+ return this.groups[name];
+}
+
+// Represents a "sub group", i.e. a groupbox on a tab, for a single main group.
+function PrefSubGroup(parent, name)
+{
+ this.parent = parent; // Main group.
+ this.name = name;
+ this.fullGroup = this.parent.name + "." + this.name;
+ this.label = getMsg("pref.group." + this.fullGroup + ".label", null, this.name);
+ this.help = getMsg("pref.group." + this.fullGroup + ".help", null, "");
+ this.gb = document.createElement("groupbox");
+ this.cap = document.createElement("caption");
+ this.box = document.createElement("box");
+
+ this.cap.setAttribute("label", this.label);
+ this.gb.appendChild(this.cap);
+ this.box.orient = "vertical";
+
+ // If there's some help text, we place it as the first thing inside
+ // the <groupbox>, as an explanation for the entire subGroup.
+ if (this.help)
+ {
+ this.helpLabel = document.createElement("label");
+ this.helpLabel.appendChild(document.createTextNode(this.help));
+ this.gb.appendChild(this.helpLabel);
+ }
+ this.gb.appendChild(this.box);
+ this.parent.box.appendChild(this.gb);
+
+ return this;
+}
+
+// Actual pref window itself.
+function PrefWindow()
+{
+ // Not loaded until the pref list and objects have been created in |onLoad|.
+ this.loaded = false;
+
+ /* PREF TAB ORDER: Determins the order, and fixed tabs, found on the UI.
+ *
+ * It is an array of mainGroup names, and a flag indicating if the tab
+ * should be created even when there's no prefs for it.
+ *
+ * This is for consistency, although none of the ChatZilla built-in pref
+ * objects leave fixed tabs empty currently.
+ */
+ this.prefTabOrder = [{ fixed: true, name: "general"},
+ { fixed: true, name: "appearance"},
+ { fixed: false, name: "lists"},
+ { fixed: false, name: "dcc"},
+ { fixed: false, name: "startup"},
+ { fixed: false, name: "global"},
+ { fixed: false, name: "munger"}
+ ];
+
+ /* PREF OBJECTS: Stores all the objects we've using that have prefs.
+ *
+ * Each object gets a "privateData" object, which is then used by the pref
+ * window code for storing all of it's data on the object.
+ */
+ this.prefObjects = null;
+
+ /* TOOLTIPS: Special tooltips for preference items.
+ *
+ * Timer: return value from |setTimeout| whenever used. There is only
+ * ever one timer going for the tooltips.
+ * Showing: stores visibility of the tooltip.
+ * ShowDelay: ms delay which them mouse must be still to show tooltips.
+ * HideDelay: ms delay before the tooltips hide themselves.
+ */
+ this.tooltipTimer = 0;
+ this.tooltipShowing = false;
+ this.tooltipShowDelay = 1000;
+ this.tooltipHideDelay = 20000;
+ this.tooltipBug418798 = false;
+}
+PrefWindow.prototype.TYPE = "PrefWindow";
+
+/* Updates the tooltip state, either showing or hiding it. */
+PrefWindow.prototype.setTooltipState =
+function pwin_setTooltipState(visible)
+{
+ // Shortcut: if we're already in the right state, don't bother.
+ if (this.tooltipShowing == visible)
+ return;
+
+ var tt = document.getElementById("czPrefTip");
+
+ // If we're trying to show it, and we have a reference object
+ // (this.tooltipObject), we are ok to go.
+ if (visible && this.tooltipObject)
+ {
+ /* Get the boxObject for the reference object, as we're going to
+ * place to tooltip explicitly based on this.
+ */
+ var tipobjBox = this.tooltipObject.boxObject;
+
+ // Adjust the width to that of the reference box.
+ tt.sizeTo(tipobjBox.width, tt.boxObject.height);
+ /* show tooltip using the reference object, and it's screen
+ * position. NOTE: Most of these parameters are supposed to affect
+ * things, and they do seem to matter, but don't work anything like
+ * the documentation. Be careful changing them.
+ */
+ tt.showPopup(this.tooltipObject, -1, -1, "tooltip", "bottomleft", "topleft");
+
+ // Set the timer to hide the tooltip some time later...
+ // (note the fun inline function)
+ this.tooltipTimer = setTimeout(setTooltipState, this.tooltipHideDelay,
+ this, false);
+ this.tooltipShowing = true;
+ }
+ else
+ {
+ /* We're here because either we are meant to be hiding the tooltip,
+ * or we lacked the information needed to show one. So hide it.
+ */
+ tt.hidePopup();
+ this.tooltipShowing = false;
+ }
+}
+
+/** Window event handlers **/
+
+/* Initalises, and loads all the data/utilities and prefs. */
+PrefWindow.prototype.onLoad =
+function pwin_onLoad()
+{
+ // Get ourselves a base object for the object hierarchy.
+ client = new PrefGlobal();
+
+ // Kick off the localisation load.
+ initMessages();
+
+ // Use localised name.
+ client.viewName = MSG_PREFS_GLOBAL;
+ client.unicodeName = client.viewName;
+ client.prettyName = client.viewName;
+
+ // Use the window mediator service to prevent mutliple instances.
+ var windowMediator = Components.classes[MEDIATOR_CONTRACTID];
+ var windowManager = windowMediator.getService(nsIWindowMediator);
+ var enumerator = windowManager.getEnumerator(CONFIG_WINDOWTYPE);
+
+ // We only want one open at a time because don't (currently) cope with
+ // pref-change notifications. In fact, it's not easy to cope with.
+ // Save it for some time later. :)
+
+ enumerator.getNext();
+ if (enumerator.hasMoreElements())
+ {
+ alert(MSG_PREFS_ALREADYOPEN);
+ window.close();
+ return;
+ }
+
+ // Make sure we know what host we're on.
+ initApplicationCompatibility();
+
+ // Kick off the core pref initalisation code.
+ initPrefs();
+
+ // Turn off all notifications, or it'll get confused when any pref
+ // does change.
+ client.prefManager.onPrefChanged = function(){};
+
+ // The list of objects we're tacking the prefs of.
+ this.prefObjects = new PrefObjectList();
+
+ /* Oh, this is such an odd way to do this. But hey, it works. :)
+ * What we do is ask the pref branch for the client object to give us
+ * a list of all the preferences under it. This just gets us all the
+ * Chatzilla prefs. Then, we filter them so that we only deal with
+ * ones for networks, channels and users. This means, even if only
+ * one pref is set for a channel, we get it's network and channel
+ * object created here.
+ */
+ var prefRE = new RegExp("^networks.([^.]+)" +
+ "(?:\\.(users|channels)?\\.([^.]+))?\\.");
+ var rv = new Object();
+ var netList = client.prefManager.prefBranch.getChildList("networks.", rv);
+ for (var i in netList)
+ {
+ var m = netList[i].match(prefRE);
+ if (!m)
+ continue;
+
+ var netName = unMungeNetworkName(m[1]);
+ /* We're forcing the network into existance (3rd param) if the
+ * pref is actually set, as opposed to just being known about (the
+ * pref branch will list all *known* prefs, not just those set).
+ *
+ * NOTE: |force| will, if |true|, set a property on the object so it
+ * will still exist next time we're here. If |false|, the
+ * the object will only come into existance if this magical
+ * [hidden] pref is set.
+ */
+ var force = client.prefManager.prefBranch.prefHasUserValue(netList[i]);
+
+ // Don't bother with the new if it's already there (time saving).
+ if (!(":" + netName in client.networks))
+ new PrefNetwork(client, netName, force);
+
+ if ((2 in m) && (3 in m) && (":" + netName in client.networks))
+ {
+ let net = client.networks[":" + netName];
+
+ // Create a channel object if appropriate.
+ if (m[2] == "channels")
+ new PrefChannel(net.primServ, unMungeNetworkName(m[3]), force);
+
+ // Create a user object if appropriate.
+ if (m[2] == "users")
+ new PrefUser(net.primServ, unMungeNetworkName(m[3]), force);
+ }
+ }
+
+ // Store out object that represents the current view.
+ var currentView = null;
+
+ // Enumerate source window's tab list...
+ if (("arguments" in window) && (0 in window.arguments) && ("client" in window.arguments[0]))
+ {
+ /* Make sure we survive this, external data could be bad. :) */
+ try
+ {
+ var czWin = window.arguments[0];
+ var s;
+ var n, c, u;
+ this.ownerClient = czWin.client;
+ this.ownerClient.configWindow = window;
+
+ /* Go nick the source window's view list. We can then show items in
+ * the tree for the currently connected/shown networks, channels
+ * and users even if they don't have any known prefs yet.
+ *
+ * NOTE: the |false, true| params tell the objects to not force
+ * any prefs into existance, but still show in the tree.
+ */
+ for (i = 0; i < czWin.client.viewsArray.length; i++)
+ {
+ var view = czWin.client.viewsArray[i].source;
+
+ // Network view...
+ if (view.TYPE == "IRCNetwork")
+ {
+ n = new PrefNetwork(client, view.unicodeName, false, true);
+ if (view == czWin.client.currentObject)
+ currentView = n;
+ }
+
+ if (view.TYPE.match(/^IRC(Channel|User)$/))
+ {
+ n = new PrefNetwork(client, view.parent.parent.unicodeName,
+ false, true);
+ s = n.primServ;
+ }
+
+ // Channel view...
+ if (view.TYPE == "IRCChannel")
+ {
+ c = new PrefChannel(s, view.unicodeName, false, true);
+ if (view == czWin.client.currentObject)
+ currentView = c;
+ }
+
+ // User view...
+ if (view.TYPE == "IRCUser")
+ {
+ u = new PrefUser(s, view.unicodeName, false, true);
+ if (view == czWin.client.currentObject)
+ currentView = u;
+ }
+ }
+ }
+ catch(ex)
+ {}
+ }
+
+ // Add the client object...
+ this.prefObjects.addObject(client);
+ // ...and everyone else.
+ var i, j;
+ /* We sort the keys (property names, i.e. network names). This means the UI
+ * will show them in lexographical order, which is good.
+ */
+ var sortedNets = keys(client.networks).sort();
+ for (i = 0; i < sortedNets.length; i++) {
+ net = client.networks[sortedNets[i]];
+ this.prefObjects.addObject(net);
+
+ var sortedChans = keys(net.channels).sort();
+ for (j = 0; j < sortedChans.length; j++)
+ this.prefObjects.addObject(net.channels[sortedChans[j]]);
+
+ var sortedUsers = keys(net.users).sort();
+ for (j = 0; j < sortedUsers.length; j++)
+ this.prefObjects.addObject(net.users[sortedUsers[j]]);
+ }
+
+ // Select the first item in the list.
+ var prefTree = document.getElementById("pref-tree-object");
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.select(0);
+ else
+ prefTree.view.selection.select(0);
+
+ // Find and select the current view.
+ for (i = 0; i < this.prefObjects.objects.length; i++)
+ {
+ if (this.prefObjects.objects[i] == currentView)
+ {
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.select(i);
+ else
+ prefTree.view.selection.select(i);
+ }
+ }
+
+ this.onSelectObject();
+
+ // We're done, without error, so it's safe to show the stuff.
+ document.getElementById("loadDeck").selectedIndex = 1;
+ // This allows [OK] to actually save, without this it'll just close.
+ this.loaded = true;
+
+ // Force the window to be the right size now, not later.
+ window.sizeToContent();
+ // XXX: If we're on mac, make it wider because the default theme's
+ // tabs are huge:
+ if (client.platform == "Mac")
+ window.resizeBy(140, 0);
+
+ // Center window.
+ if (("arguments" in window) && (0 in window.arguments))
+ {
+ var ow = window.arguments[0];
+
+ window.moveTo(ow.screenX + Math.max((ow.outerWidth - window.outerWidth ) / 2, 0),
+ ow.screenY + Math.max((ow.outerHeight - window.outerHeight) / 2, 0));
+ }
+}
+
+/* Closing the window. Clean up. */
+PrefWindow.prototype.onClose =
+function pwin_onClose()
+{
+ if (this.ownerClient)
+ delete this.ownerClient.configWindow;
+ if (this.loaded)
+ destroyPrefs();
+}
+
+/* OK button. */
+PrefWindow.prototype.onOK =
+function pwin_onOK()
+{
+ if (this.onApply())
+ window.close();
+ return true;
+}
+
+/* Apply button. */
+PrefWindow.prototype.onApply =
+function pwin_onApply()
+{
+ // If the load failed, better not to try to save.
+ if (!this.loaded)
+ return false;
+
+ try {
+ // Get an array of all the (XUL) items we have to save.
+ var list = getPrefTags();
+
+ //if (!confirm("There are " + list.length + " pref tags to save. OK?")) return false;
+
+ for (var i = 0; i < list.length; i++)
+ {
+ // Save this one pref...
+ var index = list[i].getAttribute("prefobjectindex");
+ var name = list[i].getAttribute("prefname");
+ // Get the private data for the object out, since everything is
+ // based on this.
+ var po = this.prefObjects.getObject(index);
+ var pref = po.prefs[name];
+
+ var value;
+ // We just need to force the value from the DOMString form to
+ // the right form, so we don't get anything silly happening.
+ switch (pref.type)
+ {
+ case "string":
+ value = list[i].value;
+ break;
+ case "number":
+ value = 1 * list[i].value;
+ break;
+ case "boolean":
+ value = list[i].checked;
+ break;
+ case "array":
+ var l = new Array();
+ for (var j = 0; j < list[i].childNodes.length; j++)
+ l.push(list[i].childNodes[j].value);
+ value = l;
+ break;
+ default:
+ throw "Unknown pref type: " + pref.type + "!";
+ }
+ /* Fun stuff. We don't want to save if the user hasn't changed
+ * it. This is so that if the default is defered, and changed,
+ * but this hasn't, we keep the defered default. Which is a
+ * Good Thing. :)
+ */
+ if (((pref.type != "array") && (value != pref.startVal)) ||
+ ((pref.type == "array") &&
+ (value.join(";") != pref.startVal.join(";"))))
+ {
+ po.parent.prefs[pref.name] = value;
+ }
+ // Update our saved value, so the above check works the 2nd
+ // time the user clicks Apply.
+ pref.val = value;
+ pref.startVal = pref.val;
+ }
+
+ return true;
+ }
+ catch (e)
+ {
+ alert(getMsg(MSG_PREFS_ERR_SAVE, e));
+ return false;
+ }
+}
+
+/* Cancel button. */
+PrefWindow.prototype.onCancel =
+function pwin_onCancel()
+{
+ window.close();
+ return true;
+}
+
+/** Tooltips' event handlers **/
+
+PrefWindow.prototype.onPrefMouseOver =
+function pwin_onPrefMouseOver(object)
+{
+ this.tooltipObject = object;
+ this.tooltipTitle = object.getAttribute("tooltiptitle");
+ this.tooltipText = object.getAttribute("tooltipcontent");
+ // Reset the timer now we're over a new pref.
+ clearTimeout(this.tooltipTimer);
+ this.tooltipTimer = setTimeout(setTooltipState, this.tooltipShowDelay,
+ this, true);
+}
+
+PrefWindow.prototype.onPrefMouseMove =
+function pwin_onPrefMouseMove(object)
+{
+ // If the tooltip isn't showing, we need to reset the timer.
+ if (!this.tooltipShowing)
+ {
+ clearTimeout(this.tooltipTimer);
+ this.tooltipTimer = setTimeout(setTooltipState, this.tooltipShowDelay,
+ this, true);
+ }
+}
+
+PrefWindow.prototype.onPrefMouseOut =
+function pwin_onPrefMouseOut(object)
+{
+ // Left the pref! Hide tooltip, and clear timer.
+ this.setTooltipState(false);
+ clearTimeout(this.tooltipTimer);
+}
+
+PrefWindow.prototype.onTooltipPopupShowing =
+function pwin_onTooltipPopupShowing(popup)
+{
+ if (!this.tooltipText)
+ return false;
+
+ var fChild = popup.firstChild;
+ var diff = popup.boxObject.height - fChild.boxObject.height;
+
+ // Setup the labels...
+ var ttt = document.getElementById("czPrefTipTitle");
+ ttt.firstChild.nodeValue = this.tooltipTitle;
+ var ttl = document.getElementById("czPrefTipLabel");
+ ttl.firstChild.nodeValue = this.tooltipText;
+
+ /* In Gecko 1.9, the popup has done no layout at this point, unlike in
+ * earlier versions. As a result, the box object of all the elements
+ * within it are 0x0. It also means the height of the labels isn't
+ * updated. To deal with this, we avoid calling sizeTo with the box
+ * object (as it's 0) and instead just force the popup height to 0 -
+ * otherwise it will only ever get bigger each time, never smaller.
+ */
+ if (popup.boxObject.width == 0)
+ this.tooltipBug418798 = true;
+
+ if (this.tooltipBug418798)
+ popup.height = 0;
+ else
+ popup.sizeTo(popup.boxObject.width, fChild.boxObject.height + diff);
+
+ return true;
+}
+
+/** Components' event handlers **/
+
+// Selected an item in the tree.
+PrefWindow.prototype.onSelectObject =
+function pwin_onSelectObject()
+{
+ var prefTree = document.getElementById("pref-tree-object");
+ var rv = new Object();
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.getRangeAt(0, rv, {});
+ else
+ prefTree.view.selection.getRangeAt(0, rv, {});
+ var prefTreeIndex = rv.value;
+
+ var delButton = document.getElementById("object-delete");
+ if (prefTreeIndex > 0)
+ delButton.removeAttribute("disabled");
+ else
+ delButton.setAttribute("disabled", "true");
+
+ var prefItem = prefTree.contentView.getItemAtIndex(prefTreeIndex);
+
+ var pObjectIndex = prefItem.getAttribute("prefobjectindex");
+ var pObject = this.prefObjects.getObject(pObjectIndex);
+
+ if (!ASSERT(pObject, "Object not found for index! " +
+ prefItem.getAttribute("prefobjectindex")))
+ return;
+
+ pObject.loadXUL(this.prefTabOrder);
+
+ var header = document.getElementById("pref-header");
+ header.setAttribute("title", getMsg(MSG_PREFS_FMT_HEADER,
+ pObject.parent.prettyName));
+
+ var deck = document.getElementById("pref-object-deck");
+ deck.selectedIndex = pObject.deckIndex;
+
+ this.currentObject = pObject;
+}
+
+// Browse button for file prefs.
+PrefWindow.prototype.onPrefBrowse =
+function pwin_onPrefBrowse(button)
+{
+ var spec = "$all";
+ if (button.hasAttribute("spec"))
+ spec = button.getAttribute("spec") + " " + spec;
+
+ var type = button.getAttribute("kind");
+ var edit = button.previousSibling.lastChild;
+
+ var rv;
+ if (type == "folder")
+ {
+ try
+ {
+ // if the user set the pref to an invalid folder, this will throw:
+ var current = getFileFromURLSpec(edit.value);
+ }
+ catch (ex)
+ {
+ // Just setting it to null will work:
+ current = null;
+ }
+ rv = pickGetFolder(MSG_PREFS_BROWSE_TITLE, current);
+ }
+ else
+ {
+ rv = pickOpen(MSG_PREFS_BROWSE_TITLE, spec);
+ }
+
+ if (!rv.ok)
+ return;
+
+ edit.value = (type == "file") ? rv.file.path : rv.picker.fileURL.spec;
+},
+
+// Selection changed on listbox.
+PrefWindow.prototype.onPrefListSelect =
+function pwin_onPrefListSelect(object)
+{
+ var list = getRelatedItem(object, "list");
+ var buttons = new Object();
+ buttons.up = getRelatedItem(object, "button-up");
+ buttons.down = getRelatedItem(object, "button-down");
+ buttons.add = getRelatedItem(object, "button-add");
+ buttons.edit = getRelatedItem(object, "button-edit");
+ buttons.del = getRelatedItem(object, "button-delete");
+
+ if (("selectedItems" in list) && list.selectedItems &&
+ list.selectedItems.length)
+ {
+ buttons.edit.removeAttribute("disabled");
+ buttons.del.removeAttribute("disabled");
+ }
+ else
+ {
+ buttons.edit.setAttribute("disabled", "true");
+ buttons.del.setAttribute("disabled", "true");
+ }
+
+ if (!("selectedItems" in list) || !list.selectedItems ||
+ list.selectedItems.length == 0 || list.selectedIndex == 0)
+ {
+ buttons.up.setAttribute("disabled", "true");
+ }
+ else
+ {
+ buttons.up.removeAttribute("disabled");
+ }
+
+ if (!("selectedItems" in list) || !list.selectedItems ||
+ list.selectedItems.length == 0 ||
+ list.selectedIndex == list.childNodes.length - 1)
+ {
+ buttons.down.setAttribute("disabled", "true");
+ }
+ else
+ {
+ buttons.down.removeAttribute("disabled");
+ }
+}
+
+// Up button for lists.
+PrefWindow.prototype.onPrefListUp =
+function pwin_onPrefListUp(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ var selected = list.selectedItems[0];
+ var before = selected.previousSibling;
+ if (before)
+ {
+ before.parentNode.insertBefore(selected, before);
+ list.selectItem(selected);
+ list.ensureElementIsVisible(selected);
+ }
+}
+
+// Down button for lists.
+PrefWindow.prototype.onPrefListDown =
+function pwin_onPrefListDown(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ var selected = list.selectedItems[0];
+ if (selected.nextSibling)
+ {
+ if (selected.nextSibling.nextSibling)
+ list.insertBefore(selected, selected.nextSibling.nextSibling);
+ else
+ list.appendChild(selected);
+
+ list.selectItem(selected);
+ }
+}
+
+// Add button for lists.
+PrefWindow.prototype.onPrefListAdd =
+function pwin_onPrefListAdd(object)
+{
+ var list = getRelatedItem(object, "list");
+ var newItem;
+
+ switch (list.getAttribute("kind"))
+ {
+ case "url":
+ var item = prompt(MSG_PREFS_LIST_ADD);
+ if (item)
+ {
+ newItem = document.createElement("listitem");
+ newItem.setAttribute("label", item);
+ newItem.value = item;
+ list.appendChild(newItem);
+ this.onPrefListSelect(object);
+ }
+ break;
+ case "file":
+ case "fileurl":
+ var spec = "$all";
+
+ var rv = pickOpen(MSG_PREFS_BROWSE_TITLE, spec);
+ if (rv.ok)
+ {
+ var data = { file: rv.file.path, fileurl: rv.picker.fileURL.spec };
+ var kind = list.getAttribute("kind");
+
+ newItem = document.createElement("listitem");
+ newItem.setAttribute("label", data[kind]);
+ newItem.value = data[kind];
+ list.appendChild(newItem);
+ this.onPrefListSelect(object);
+ }
+
+ break;
+ }
+}
+
+// Edit button for lists.
+PrefWindow.prototype.onPrefListEdit =
+function pwin_onPrefListEdit(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ switch (list.getAttribute("kind"))
+ {
+ case "url":
+ case "file":
+ case "fileurl":
+ // We're letting the user edit file types here, since it saves us
+ // a lot of work, and we can't let them pick a file OR a directory,
+ // so they pick a file and can edit it off to use a directory.
+ var listItem = list.selectedItems[0];
+ var newValue = prompt(MSG_PREFS_LIST_EDIT, listItem.value);
+ if (newValue)
+ {
+ listItem.setAttribute("label", newValue);
+ listItem.value = newValue;
+ }
+ break;
+ }
+}
+
+// Delete button for lists.
+PrefWindow.prototype.onPrefListDelete =
+function pwin_onPrefListDelete(object)
+{
+ var list = getRelatedItem(object, "list");
+
+ var listItem = list.selectedItems[0];
+ if (confirm(getMsg(MSG_PREFS_LIST_DELETE, listItem.value)))
+ list.removeChild(listItem);
+}
+
+/* Add... button. */
+PrefWindow.prototype.onAddObject =
+function pwin_onAddObject()
+{
+ var rv = new Object();
+
+ /* Try to nobble the current selection and pre-fill as needed. */
+ switch (this.currentObject.parent.TYPE)
+ {
+ case "PrefNetwork":
+ rv.type = "net";
+ rv.net = this.currentObject.parent.unicodeName;
+ break;
+ case "PrefChannel":
+ rv.type = "chan";
+ rv.net = this.currentObject.parent.parent.parent.unicodeName;
+ rv.chan = this.currentObject.parent.unicodeName;
+ break;
+ case "PrefUser":
+ rv.type = "user";
+ rv.net = this.currentObject.parent.parent.parent.unicodeName;
+ rv.chan = this.currentObject.parent.unicodeName;
+ break;
+ }
+
+ // Show add dialog, passing the data object along.
+ window.openDialog("config-add.xul", "cz-config-add", "chrome,dialog,modal", rv);
+
+ if (!rv.ok)
+ return;
+
+ /* Ok, so what type did they want again?
+ *
+ * NOTE: The param |true| in the object creation calls is for |force|. It
+ * causes the hidden pref to be set for the objects so they are shown
+ * every time this window opens, until the user deletes them.
+ */
+ switch (rv.type)
+ {
+ case "net":
+ this.prefObjects.addObject(new PrefNetwork(client, rv.net, true));
+ break;
+ case "chan":
+ if (!(":" + rv.net in client.networks))
+ this.prefObjects.addObject(new PrefNetwork(client, rv.net, true));
+ this.prefObjects.addObject(new PrefChannel(client.networks[":" + rv.net].primServ, rv.chan, true));
+ break;
+ case "user":
+ if (!(":" + rv.net in client.networks))
+ this.prefObjects.addObject(new PrefNetwork(client, rv.net, true));
+ this.prefObjects.addObject(new PrefUser(client.networks[":" + rv.net].primServ, rv.chan, true));
+ break;
+ default:
+ // Oops. Not good, if we got here.
+ alert("Unknown pref type: " + rv.type);
+ }
+}
+
+/* Delete button. */
+PrefWindow.prototype.onDeleteObject =
+function pwin_onDeleteObject()
+{
+ // Save current node before we re-select.
+ var sel = this.currentObject;
+
+ // Check they want to go ahead.
+ if (!confirm(getMsg(MSG_PREFS_OBJECT_DELETE, sel.parent.unicodeName)))
+ return;
+
+ // Select a new item BEFORE removing the current item, so the <tree>
+ // doesn't freak out on us.
+ var prefTree = document.getElementById("pref-tree-object");
+ if ("selection" in prefTree.treeBoxObject)
+ prefTree.treeBoxObject.selection.select(0);
+ else
+ prefTree.view.selection.select(0);
+
+ // If it's a network, nuke all the channels and users too.
+ if (sel.parent.TYPE == "PrefNetwork")
+ {
+ var chans = sel.parent.channels;
+ for (k in chans)
+ PrefObjectList.getPrivateData(chans[k]).clear();
+
+ var users = sel.parent.users;
+ for (k in users)
+ PrefObjectList.getPrivateData(users[k]).clear();
+ }
+ sel.clear();
+
+ this.onSelectObject();
+}
+
+/* Reset button. */
+PrefWindow.prototype.onResetObject =
+function pwin_onResetObject()
+{
+ // Save current node before we re-select.
+ var sel = this.currentObject;
+
+ // Check they want to go ahead.
+ if (!confirm(getMsg(MSG_PREFS_OBJECT_RESET, sel.parent.unicodeName)))
+ return;
+
+ // Reset the prefs.
+ sel.reset();
+}
+
+// End of PrefWindow. //
+
+/*** Base functions... ***/
+
+/* Gets a "related" items, such as the buttons associated with a list. */
+function getRelatedItem(object, thing)
+{
+ switch (object.nodeName)
+ {
+ case "listbox":
+ switch (thing) {
+ case "list":
+ return object;
+ case "button-up":
+ return object.parentNode.nextSibling.childNodes[0];
+ case "button-down":
+ return object.parentNode.nextSibling.childNodes[1];
+ case "button-add":
+ return object.parentNode.nextSibling.childNodes[3];
+ case "button-edit":
+ return object.parentNode.nextSibling.childNodes[4];
+ case "button-delete":
+ return object.parentNode.nextSibling.childNodes[5];
+ }
+ break;
+ case "button":
+ var n = object.parentNode.previousSibling.lastChild;
+ if (n)
+ return getRelatedItem(n, thing);
+ break;
+ }
+ return null;
+}
+
+// Wrap this call so we have the right |this|.
+function setTooltipState(w, s)
+{
+ w.setTooltipState(s);
+}
+
+// Reverses the Pref Manager's munging of network names.
+function unMungeNetworkName(name)
+{
+ name = ecmaUnescape(name);
+ return name.replace(/_/g, ":").replace(/-/g, ".");
+}
+
+// Adds a button to a container, setting up the command in a simple way.
+function appendButton(cont, oncommand, attr)
+{
+ var btn = document.createElement("button");
+ if (attr)
+ for (var a in attr)
+ btn.setAttribute(a, attr[a]);
+ if (oncommand)
+ btn.setAttribute("oncommand", "gPrefWindow." + oncommand + "(this);");
+ else
+ btn.setAttribute("disabled", "true");
+ cont.appendChild(btn);
+}
+
+// Like appendButton, but just drops in a separator.
+function appendSeparator(cont, attr)
+{
+ var spacer = document.createElement("separator");
+ if (attr)
+ for (var a in attr)
+ spacer.setAttribute(a, attr[a]);
+ cont.appendChild(spacer);
+}
+
+/* This simply collects together all the <textbox>, <checkbox> and <listbox>
+ * elements that have the attribute "prefname". Thus, we generate a list of
+ * all elements that are for prefs.
+ */
+function getPrefTags()
+{
+ var rv = new Array();
+ var i, list;
+
+ list = document.getElementsByTagName("textbox");
+ for (i = 0; i < list.length; i++)
+ {
+ if (list[i].hasAttribute("prefname"))
+ rv.push(list[i]);
+ }
+ list = document.getElementsByTagName("checkbox");
+ for (i = 0; i < list.length; i++)
+ {
+ if (list[i].hasAttribute("prefname"))
+ rv.push(list[i]);
+ }
+ list = document.getElementsByTagName("listbox");
+ for (i = 0; i < list.length; i++)
+ {
+ if (list[i].hasAttribute("prefname"))
+ rv.push(list[i]);
+ }
+
+ return rv;
+}
+
+// Sets up the "extra1" button (Apply).
+function setupButtons()
+{
+ // Hacky-hacky-hack. Looks like the new toolkit does provide a solution,
+ // but we need to support SeaMonkey too. :)
+
+ var dialog = document.documentElement;
+ dialog.getButton("extra1").label = dialog.getAttribute("extra1Label");
+}
+
+// And finally, we want one of these.
+var gPrefWindow = new PrefWindow();
diff --git a/comm/suite/chatzilla/xul/content/config.xul b/comm/suite/chatzilla/xul/content/config.xul
new file mode 100644
index 0000000000..47cacbfd3d
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/config.xul
@@ -0,0 +1,82 @@
+<?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 dialog SYSTEM "chrome://chatzilla/locale/config.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="config.css" type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ windowtype="irc:chatzilla:config"
+ id="chatzilla-window"
+ buttons="accept,cancel,extra1"
+ extra1Label="&dialog.apply;"
+ ondialogaccept="gPrefWindow.onOK();"
+ ondialogcancel="gPrefWindow.onCancel();"
+ ondialogextra1="gPrefWindow.onApply();"
+ onload="setupButtons(); gPrefWindow.onLoad();"
+ onunload="gPrefWindow.onClose();"
+ title="&window.title;">
+
+ <script src="chrome://chatzilla/content/lib/js/utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/file-utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/pref-manager.js"/>
+ <script src="chrome://chatzilla/content/lib/js/message-manager.js"/>
+ <script src="chrome://chatzilla/content/prefs.js"/>
+ <script src="chrome://chatzilla/content/messages.js"/>
+ <script src="config.js"/>
+ <script src="chrome://chatzilla/content/static.js"/>
+
+ <tooltip id="czPrefTip" orient="vertical"
+ onpopupshowing="return gPrefWindow.onTooltipPopupShowing(this);">
+ <vbox>
+ <label id="czPrefTipTitle" class="header">.</label>
+ <label id="czPrefTipLabel">.</label>
+ </vbox>
+ <spacer flex="1"/>
+ </tooltip>
+
+ <deck id="loadDeck" flex="1">
+ <vbox flex="1" align="center" pack="center">
+ <label class="loading" value="&loading.label;"/>
+ </vbox>
+ <hbox flex="1">
+ <vbox id="pref-objects">
+ <tree id="pref-tree-object" flex="1" seltype="single"
+ hidecolumnpicker="true" onselect="gPrefWindow.onSelectObject();">
+ <treecols>
+ <treecol id="pref-col-name" primary="true" flex="1"
+ hideheader="true"/>
+ </treecols>
+ <treechildren id="pref-tree"/>
+ </tree>
+ <hbox>
+ <!--
+ <button label="&object.add.label;" tooltiptext="&object.add.hint;"
+ accesskey="&object.add.accesskey;" flex="1"
+ oncommand="gPrefWindow.onAddObject();"/>
+ -->
+ <button label="&object.del.label;" tooltiptext="&object.del.hint;"
+ accesskey="&object.del.accesskey;" flex="1"
+ oncommand="gPrefWindow.onDeleteObject();" id="object-delete"/>
+ </hbox>
+ </vbox>
+ <vbox flex="1">
+ <dialogheader id="pref-header" title=""/>
+ <deck flex="1" id="pref-object-deck"/>
+ <hbox align="center">
+ <button label="&object.reset.label;" tooltiptext="&object.reset.hint;"
+ accesskey="&object.reset.accesskey;"
+ oncommand="gPrefWindow.onResetObject();"/>
+ <spacer flex="1"/>
+ <html:a onclick="" target="_blank" href="&homepage.url;"
+ style="display: block; color: blue; text-decoration:
+ underline;">&homepage.label;</html:a>
+ </hbox>
+ </vbox>
+ </hbox>
+ </deck>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/dynamic.css b/comm/suite/chatzilla/xul/content/dynamic.css
new file mode 100644
index 0000000000..d585c07ab2
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/dynamic.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* empty css file. rules are appended to this dynamically */
+
+
diff --git a/comm/suite/chatzilla/xul/content/handlers.js b/comm/suite/chatzilla/xul/content/handlers.js
new file mode 100644
index 0000000000..74e0f1c856
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/handlers.js
@@ -0,0 +1,3960 @@
+/* -*- Mode: C++; 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/. */
+
+window.onresize =
+function onresize()
+{
+ for (var i = 0; i < client.deck.childNodes.length; i++)
+ scrollDown(client.deck.childNodes[i], true);
+}
+
+function onInputFocus()
+{
+}
+
+function showErrorDlg(message)
+{
+ const XUL_MIME = "application/vnd.mozilla.xul+xml";
+ const XUL_KEY = "http://www.mozilla.org/keymaster/" +
+ "gatekeeper/there.is.only.xul";
+
+ const TITLE = "ChatZilla run-time error";
+ const HEADER = "There was a run-time error with ChatZilla. " +
+ "Please report the following information:";
+
+ const OL_JS = "document.getElementById('tb').value = '%S';";
+ const TB_STYLE = ' multiline="true" readonly="true"' +
+ ' style="width: 60ex; height: 20em;"';
+
+ const ERROR_DLG = '<?xml version="1.0"?>' +
+ '<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>' +
+ '<dialog xmlns="' + XUL_KEY + '" buttons="accept" ' +
+ 'title="' + TITLE + '" onload="' + OL_JS + '">' +
+ '<label>' + HEADER + '</label><textbox' + TB_STYLE + ' id="tb"/>' +
+ '</dialog>';
+
+ var content = message.replace(/\n/g, "\\n");
+ content = content.replace(/'/g, "\\'").replace(/"/g, "&quot;");
+ content = content.replace(/</g, "&lt;").replace(/>/g, "&gt;");
+ content = ERROR_DLG.replace("%S", content);
+ content = encodeURIComponent(content);
+ content = "data:" + XUL_MIME + "," + content;
+
+ setTimeout(function() {
+ window.openDialog(content, "_blank", "chrome,modal");
+ }, 100);
+}
+
+function onLoad()
+{
+ dd ("Initializing ChatZilla {");
+ try
+ {
+ init();
+ }
+ catch (ex)
+ {
+ dd("caught exception while initializing:\n" + dumpObjectTree(ex));
+ var exception = formatException(ex) + (ex.stack && "\n" + ex.stack);
+ showErrorDlg(exception + "\n" + dumpObjectTree(ex));
+ }
+
+ dd("}");
+ mainStep();
+}
+
+function initHandlers()
+{
+ var node;
+ node = document.getElementById("input");
+ node.addEventListener("keypress", onInputKeyPress, false);
+ node = document.getElementById("multiline-input");
+ node.addEventListener("keypress", onMultilineInputKeyPress, false);
+ node.active = false;
+
+ window.onkeypress = onWindowKeyPress;
+
+ window.isFocused = false;
+ window.addEventListener("focus", onWindowFocus, true);
+ window.addEventListener("blur", onWindowBlue, true);
+
+ client.inputPopup = null;
+
+ // Should fail silently pre-moz1.4
+ doCommandWithParams("cmd_clipboardDragDropHook",
+ {addhook: CopyPasteHandler});
+}
+
+function onClose()
+{
+ // Assume close needs authorization from user.
+ var close = false;
+
+ // Close has already been authorized.
+ if ("userClose" in client && client.userClose)
+ close = true;
+
+ // Not connected, no need for authorization.
+ if (!("getConnectionCount" in client) || (client.getConnectionCount() == 0))
+ close = true;
+
+ if (!close)
+ {
+ // Authorization needed from user.
+ client.wantToQuit();
+ return false;
+ }
+
+ return true;
+}
+
+function onUnload()
+{
+ dd("Shutting down ChatZilla.");
+
+ /* Disable every loaded & enabled plugin to give them all a chance to
+ * clean up anything beyond the ChatZilla window (e.g. libraries). All
+ * errors are disregarded as there's nothing we can do at this point.
+ * Wipe the plugin list afterwards for safety.
+ */
+ for (var k in client.plugins)
+ {
+ if ((client.plugins[k].API > 0) && client.plugins[k].enabled)
+ {
+ try
+ {
+ client.plugins[k].disable();
+ }
+ catch(ex) {}
+ }
+ }
+ client.plugins = new Object();
+
+ // Close all dialogs.
+ if ("joinDialog" in client)
+ client.joinDialog.close();
+ if ("configWindow" in client)
+ client.configWindow.close();
+ if ("installPluginDialog" in client)
+ client.installPluginDialog.close();
+ if ("aboutDialog" in client)
+ client.aboutDialog.close();
+
+ // We don't trust anybody.
+ client.hiddenDocument = null;
+ uninitOfflineIcon();
+ uninitIdleAutoAway(client.prefs["awayIdleTime"]);
+ destroy();
+}
+
+function onNotImplemented()
+{
+ alert (getMsg("onNotImplementedMsg"));
+}
+
+/* tab click */
+function onTabClick(e, id)
+{
+ if (e.which != 2)
+ return;
+
+ var tbi = document.getElementById(id);
+ var view = client.viewsArray[tbi.getAttribute("viewKey")];
+
+ if (e.which == 2)
+ {
+ dispatch("hide", { view: view.source, source: "mouse" });
+ return;
+ }
+}
+
+function onTabSelect(e)
+{
+ var tabs = e.target;
+
+ /* Hackaround, bug 314230. XBL likes focusing a tab before onload has fired.
+ * That means the tab we're trying to select here will be the hidden one,
+ * which doesn't have a viewKey. We catch that case.
+ */
+ if (!tabs.selectedItem.hasAttribute("viewKey"))
+ return;
+
+ var key = tabs.selectedItem.getAttribute("viewKey");
+ var view = client.viewsArray[key];
+ dispatch("set-current-view", {view:view.source});
+}
+
+function onMessageViewClick(e)
+{
+ if ((e.which != 1) && (e.which != 2))
+ return true;
+
+ var cx = getMessagesContext(null, e.target);
+ cx.source = "mouse";
+ cx.shiftKey = e.shiftKey;
+ var command = getEventCommand(e);
+ if (!client.commandManager.isCommandSatisfied(cx, command))
+ return false;
+
+ dispatch(command, cx);
+ dispatch("focus-input");
+ e.preventDefault();
+ return true;
+}
+
+function onMessageViewMouseDown(e)
+{
+ if ((typeof startScrolling != "function") ||
+ ((e.which != 1) && (e.which != 2)))
+ {
+ return true;
+ }
+
+ var cx = getMessagesContext(null, e.target);
+ var command = getEventCommand(e);
+ if (!client.commandManager.isCommandSatisfied(cx, command))
+ startScrolling(e);
+ return true;
+}
+
+function onMessageViewContextMenu(e)
+{
+ var elem = e.target;
+ var menu = document.getElementById("context:messages");
+ while (elem)
+ {
+ if (elem.localName && elem.localName.toLowerCase() == "input")
+ {
+ menu = document.getElementById("context:edit");
+ break;
+ }
+ elem = elem.parentNode;
+ }
+ document.popupNode = e.target;
+ if ("openPopupAtScreen" in menu)
+ menu.openPopupAtScreen(e.screenX, e.screenY, true);
+ else
+ menu.showPopup(null, e.screenX + 2, e.screenY + 2, "context", "", "");
+ e.stopPropagation();
+ e.preventDefault();
+}
+
+function getEventCommand(e)
+{
+ let where = Services.prefs.getIntPref("browser.link.open_newwindow");
+ if ((where != 3) && ((e.which == 2) || e.ctrlKey) &&
+ Services.prefs.getBoolPref("browser.tabs.opentabfor.middleclick", true))
+ where = 3;
+
+ if (where == 2)
+ return "goto-url-newwin";
+ if (where == 3)
+ return "goto-url-newtab";
+ return "goto-url";
+}
+
+function onMouseOver (e)
+{
+ var i = 0;
+ var target = e.target;
+ var status = "";
+ while (!status && target && i < 5)
+ {
+ if ("getAttribute" in target)
+ {
+ status = target.getAttribute("href");
+ if (!status)
+ status = target.getAttribute("status-text");
+ }
+ ++i;
+ target = target.parentNode;
+ }
+
+ // Setting client.status to "" will revert it to the default automatically.
+ client.status = status;
+}
+
+function onMultilineInputKeyPress (e)
+{
+ if ((e.ctrlKey || e.metaKey) && e.keyCode == 13)
+ {
+ /* meta-enter, execute buffer */
+ onMultilineSend(e);
+ }
+ else
+ {
+ if ((e.ctrlKey || e.metaKey) && e.keyCode == 40)
+ {
+ /* ctrl/meta-down, switch to single line mode */
+ dispatch ("pref multiline false");
+ }
+ }
+}
+
+function onMultilineSend(e)
+{
+ var multiline = document.getElementById("multiline-input");
+ e.line = multiline.value;
+ if (e.line.search(/\S/) == -1)
+ return;
+ onInputCompleteLine (e);
+ multiline.value = "";
+ if (("multiLineForPaste" in client) && client.multiLineForPaste)
+ client.prefs["multiline"] = false;
+}
+
+function onTooltip(event)
+{
+ const XLinkNS = "http://www.w3.org/1999/xlink";
+
+ var tipNode = event.originalTarget;
+ var titleText = null;
+ var XLinkTitleText = null;
+
+ var element = document.tooltipNode;
+ while (element && (element != document.documentElement))
+ {
+ if (element.nodeType == Node.ELEMENT_NODE)
+ {
+ var text;
+ if (element.hasAttribute("title"))
+ text = element.getAttribute("title");
+ else if (element.hasAttributeNS(XLinkNS, "title"))
+ text = element.getAttributeNS(XLinkNS, "title");
+
+ if (text)
+ {
+ tipNode.setAttribute("label", text);
+ return true;
+ }
+ }
+
+ element = element.parentNode;
+ }
+
+ return false;
+}
+
+function onInputKeyPress (e)
+{
+ if (client.prefs["outgoing.colorCodes"])
+ setTimeout(onInputKeyPressCallback, 100, e.target);
+
+ switch (e.keyCode)
+ {
+ case 9: /* tab */
+ if (!e.ctrlKey && !e.metaKey)
+ {
+ onTabCompleteRequest(e);
+ e.preventDefault();
+ }
+ return;
+
+ case 77: /* Hackaround for carbon on mac sending us this instead of 13
+ * for ctrl+enter. 77 = "M", and ctrl+M was originally used
+ * to send a CR in a terminal. */
+ // Fallthrough if ctrl was pressed, otherwise break out to default:
+ if (!e.ctrlKey)
+ break;
+
+ case 13: /* CR */
+ e.line = e.target.value;
+ e.target.value = "";
+ if (e.line.search(/\S/) == -1)
+ return;
+ if (e.ctrlKey)
+ e.line = client.COMMAND_CHAR + "say " + e.line;
+ onInputCompleteLine (e);
+ return;
+
+ case 37: /* left */
+ if (e.altKey && e.metaKey)
+ cycleView(-1);
+ return;
+
+ case 38: /* up */
+ if (e.ctrlKey || e.metaKey)
+ {
+ /* ctrl/meta-up, switch to multi line mode */
+ dispatch ("pref multiline true");
+ }
+ else
+ {
+ if (client.lastHistoryReferenced == -2)
+ {
+ client.lastHistoryReferenced = -1;
+ e.target.value = client.incompleteLine;
+ }
+ else if (client.lastHistoryReferenced <
+ client.inputHistory.length - 1)
+ {
+ e.target.value =
+ client.inputHistory[++client.lastHistoryReferenced];
+ }
+ }
+ e.preventDefault();
+ return;
+
+ case 39: /* right */
+ if (e.altKey && e.metaKey)
+ cycleView(+1);
+ return;
+
+ case 40: /* down */
+ if (client.lastHistoryReferenced > 0)
+ e.target.value =
+ client.inputHistory[--client.lastHistoryReferenced];
+ else if (client.lastHistoryReferenced == -1)
+ {
+ e.target.value = "";
+ client.lastHistoryReferenced = -2;
+ }
+ else
+ {
+ client.lastHistoryReferenced = -1;
+ e.target.value = client.incompleteLine;
+ }
+ e.preventDefault();
+ return;
+ }
+ client.lastHistoryReferenced = -1;
+ client.incompleteLine = e.target.value;
+}
+
+function onTabCompleteRequest (e)
+{
+ var elem = document.commandDispatcher.focusedElement;
+ var singleInput = document.getElementById("input");
+ if (document.getBindingParent(elem) != singleInput)
+ return;
+
+ var selStart = singleInput.selectionStart;
+ var selEnd = singleInput.selectionEnd;
+ var line = singleInput.value;
+
+ if (!line)
+ {
+ if ("defaultCompletion" in client.currentObject)
+ singleInput.value = client.currentObject.defaultCompletion;
+ // If there was nothing to complete, help the user:
+ if (!singleInput.value)
+ display(MSG_LEAVE_INPUTBOX, MT_INFO);
+ return;
+ }
+
+ if (selStart != selEnd)
+ {
+ /* text is highlighted, just move caret to end and exit */
+ singleInput.selectionStart = singleInput.selectionEnd = line.length;
+ return;
+ }
+
+ var wordStart = line.substr(0, selStart).search(/\s\S*$/);
+ if (wordStart == -1)
+ wordStart = 0;
+ else
+ ++wordStart;
+
+ var wordEnd = line.substr(selStart).search(/\s/);
+ if (wordEnd == -1)
+ wordEnd = line.length;
+ else
+ wordEnd += selStart;
+
+ // Double tab on nothing, inform user how to get out of the input box
+ if (wordEnd == wordStart)
+ {
+ display(MSG_LEAVE_INPUTBOX, MT_INFO);
+ return;
+ }
+
+ if ("performTabMatch" in client.currentObject)
+ {
+ var word = line.substring(wordStart, wordEnd);
+ var wordLower = word.toLowerCase();
+ var d = getObjectDetails(client.currentObject);
+ if (d.server)
+ wordLower = d.server.toLowerCase(word);
+
+ var co = client.currentObject;
+
+ // We need some special knowledge of how to lower-case strings.
+ var lcFn;
+ if ("getLCFunction" in co)
+ lcFn = co.getLCFunction();
+
+ var matches = co.performTabMatch(line, wordStart, wordEnd, wordLower,
+ selStart, lcFn);
+ /* if we get null back, we're supposed to fail silently */
+ if (!matches)
+ return;
+
+ var doubleTab = false;
+ var date = new Date();
+ if ((date - client.lastTabUp) <= client.DOUBLETAB_TIME)
+ doubleTab = true;
+ else
+ client.lastTabUp = date;
+
+ if (doubleTab)
+ {
+ /* if the user hit tab twice quickly, */
+ if (matches.length > 0)
+ {
+ /* then list possible completions, */
+ display(getMsg(MSG_FMT_MATCHLIST,
+ [matches.length, word,
+ matches.sort().join(", ")]));
+ }
+ else
+ {
+ /* or display an error if there are none. */
+ display(getMsg(MSG_ERR_NO_MATCH, word), MT_ERROR);
+ }
+ }
+ else if (matches.length >= 1)
+ {
+ var match;
+ if (matches.length == 1)
+ match = matches[0];
+ else
+ match = getCommonPfx(matches, lcFn);
+ singleInput.value = line.substr(0, wordStart) + match +
+ line.substr(wordEnd);
+ if (wordEnd < line.length)
+ {
+ /* if the word we completed was in the middle if the line
+ * then move the cursor to the end of the completed word. */
+ var newpos = wordStart + match.length;
+ if (matches.length == 1)
+ {
+ /* word was fully completed, move one additional space */
+ ++newpos;
+ }
+ singleInput.selectionEnd = e.target.selectionStart = newpos;
+ }
+ }
+ }
+
+}
+
+function onWindowKeyPress(e)
+{
+ var code = Number(e.keyCode);
+ var w;
+ var newOfs;
+ var userList = document.getElementById("user-list");
+ var elemFocused = document.commandDispatcher.focusedElement;
+
+ const isMac = client.platform == "Mac";
+ const isLinux = client.platform == "Linux";
+ const isWindows = client.platform == "Windows";
+ const isOS2 = client.platform == "OS/2";
+ const isUnknown = !(isMac || isLinux || isWindows || isOS2);
+
+ switch (code)
+ {
+ case 9: /* Tab */
+ // Control-Tab => next tab (all platforms)
+ // Control-Shift-Tab => previous tab (all platforms)
+ if (e.ctrlKey && !e.altKey && !e.metaKey)
+ {
+ cycleView(e.shiftKey ? -1: 1);
+ e.preventDefault();
+ }
+ break;
+
+ case 33: /* Page Up */
+ case 34: /* Page Down */
+ // Control-Page Up => previous tab (all platforms)
+ // Control-Page Down => next tab (all platforms)
+ if ((e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey) ||
+ (e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey))
+ {
+ cycleView(2 * code - 67);
+ e.preventDefault();
+ break;
+ }
+
+ if (!e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey &&
+ (elemFocused != userList))
+ {
+ w = client.currentFrame;
+ newOfs = w.pageYOffset + (w.innerHeight * 0.75) *
+ (2 * code - 67);
+ if (newOfs > 0)
+ w.scrollTo(w.pageXOffset, newOfs);
+ else
+ w.scrollTo(w.pageXOffset, 0);
+ e.preventDefault();
+ }
+ break;
+
+ case 37: /* Left Arrow */
+ case 39: /* Right Arrow */
+ // Command-Alt-Left Arrow => previous tab (Mac only)
+ // Command-Alt-Right Arrow => next tab (Mac only)
+ if (isMac && e.metaKey && e.altKey && !e.ctrlKey && !e.shiftKey)
+ {
+ cycleView(code - 38);
+ e.preventDefault();
+ }
+ break;
+
+ case 219: /* [ */
+ case 221: /* ] */
+ // Command-Shift-[ => previous tab (Mac only)
+ // Command-Shift-] => next tab (Mac only)
+ if (isMac && e.metaKey && e.shiftKey && !e.altKey && !e.ctrlKey)
+ {
+ cycleView(code - 220);
+ e.preventDefault();
+ }
+ break;
+
+ case 117: /* F6 */
+ // F6 => focus next (all platforms)
+ // Shift-F6 => focus previous (all platforms)
+ if (!e.altKey && !e.ctrlKey && !e.metaKey)
+ {
+ advanceKeyboardFocus(e.shiftKey ? -1 : 1);
+ e.preventDefault();
+ }
+ break;
+ }
+
+ // Code is zero if we have a typeable character triggering the event.
+ if (code != 0)
+ return;
+
+ // OS X only: Command-{ and Command-}
+ // Newer geckos seem to only provide these keys in charCode, not keyCode
+ if (isMac && e.metaKey && e.shiftKey && !e.altKey && !e.ctrlKey)
+ {
+ if (e.charCode == 123 || e.charCode == 125)
+ {
+ cycleView(e.charCode - 124);
+ e.preventDefault();
+ return;
+ }
+ }
+
+ // Numeric shortcuts
+
+ // The following code is copied from:
+ // /mozilla/browser/base/content/browser.js
+ // Revision: 1.748
+ // Lines: 1397-1421
+
+ // \d in a RegExp will find any Unicode character with the "decimal digit"
+ // property (Nd)
+ var regExp = /\d/;
+ if (!regExp.test(String.fromCharCode(e.charCode)))
+ return;
+
+ // Some Unicode decimal digits are in the range U+xxx0 - U+xxx9 and some are
+ // in the range U+xxx6 - U+xxxF. Find the digit 1 corresponding to our
+ // character.
+ var digit1 = (e.charCode & 0xFFF0) | 1;
+ if (!regExp.test(String.fromCharCode(digit1)))
+ digit1 += 6;
+
+ var idx = e.charCode - digit1;
+
+ if ((0 <= idx) && (idx <= 8))
+ {
+ var modifier = (e.altKey ? 0x1 : 0) |
+ (e.ctrlKey ? 0x2 : 0) |
+ (e.shiftKey ? 0x4 : 0) |
+ (e.metaKey ? 0x8 : 0);
+
+ var modifierMask;
+ if (client.prefs["tabGotoKeyModifiers"])
+ modifierMask = client.prefs["tabGotoKeyModifiers"];
+ else
+ modifierMask = 0x1; // alt
+
+ if ((modifier & modifierMask) == modifierMask)
+ {
+ // Pressing 1-8 takes you to that tab, while pressing 9 takes you
+ // to the last tab always.
+ if (idx == 8)
+ idx = client.viewsArray.length - 1;
+
+ if ((idx in client.viewsArray) && client.viewsArray[idx].source)
+ {
+ var newView = client.viewsArray[idx].source;
+ dispatch("set-current-view", { view: newView });
+ }
+ e.preventDefault();
+ return;
+ }
+ }
+}
+
+function onWindowFocus(e)
+{
+ window.isFocused = true;
+}
+
+function onWindowBlue(e)
+{
+ window.isFocused = false;
+
+ // If we're tracking last read lines, set a mark on the current view
+ // when losing focus.
+ if (client.currentObject && client.currentObject.prefs["autoMarker"])
+ client.currentObject.dispatch("marker-set");
+}
+
+function onInputCompleteLine(e)
+{
+ if (!client.inputHistory.length || client.inputHistory[0] != e.line)
+ {
+ client.inputHistory.unshift(e.line);
+ if (client.inputHistoryLogger)
+ client.inputHistoryLogger.append(e.line);
+ }
+
+ if (client.inputHistory.length > client.MAX_HISTORY)
+ client.inputHistory.pop();
+
+ client.lastHistoryReferenced = -1;
+ client.incompleteLine = "";
+
+ if (e.line[0] == client.COMMAND_CHAR)
+ {
+ if (client.prefs["outgoing.colorCodes"])
+ e.line = replaceColorCodes(e.line);
+ dispatch(e.line.substr(1), null, true);
+ }
+ else /* plain text */
+ {
+ /* color codes */
+ if (client.prefs["outgoing.colorCodes"])
+ e.line = replaceColorCodes(e.line);
+ client.sayToCurrentTarget(e.line, true);
+ }
+}
+
+function onNotifyTimeout()
+{
+ for (var n in client.networks)
+ {
+ var net = client.networks[n];
+ if (net.isConnected()) {
+ if ((net.prefs["notifyList"].length > 0) &&
+ (!net.primServ.supports["monitor"])) {
+ let isonList = net.prefs["notifyList"];
+ net.primServ.sendData ("ISON " + isonList.join(" ") + "\n");
+ } else {
+ /* Just send a ping to see if we're alive. */
+ net.primServ.sendData ("PING :ALIVECHECK\n");
+ }
+ }
+ }
+}
+
+function onWhoTimeout()
+{
+ function checkWho()
+ {
+ var checkNext = (net.lastWhoCheckChannel == null);
+ for (var c in net.primServ.channels)
+ {
+ var chan = net.primServ.channels[c];
+
+ if (checkNext && chan.active &&
+ chan.getUsersLength() < client.prefs["autoAwayCap"])
+ {
+ net.primServ.LIGHTWEIGHT_WHO = true;
+ net.primServ.who(chan.unicodeName);
+ net.lastWhoCheckChannel = chan;
+ net.lastWhoCheckTime = Number(new Date());
+ return;
+ }
+
+ if (chan == net.lastWhoCheckChannel)
+ checkNext = true;
+ }
+ if (net.lastWhoCheckChannel)
+ {
+ net.lastWhoCheckChannel = null;
+ checkWho();
+ }
+ };
+
+ for (var n in client.networks)
+ {
+ var net = client.networks[n];
+ var period = net.prefs["autoAwayPeriod"];
+ // The time since the last check, with a 5s error margin to
+ // stop us from not checking because the timer fired a tad early:
+ var waited = Number(new Date()) - net.lastWhoCheckTime + 5000;
+ if (net.isConnected() && (period != 0) && (period * 60000 < waited) &&
+ !net.primServ.caps["away-notify"])
+ checkWho();
+ }
+}
+
+function onInputKeyPressCallback (el)
+{
+ function doPopup(popup)
+ {
+ if (client.inputPopup && client.inputPopup != popup)
+ client.inputPopup.hidePopup();
+
+ client.inputPopup = popup;
+ if (popup)
+ {
+ if (el.nodeName == "textbox")
+ {
+ popup.showPopup(el, -1, -1, "tooltip", "topleft", "bottomleft");
+ }
+ else
+ {
+ var box = el.ownerDocument.getBoxObjectFor(el);
+ var pos = { x: client.mainWindow.screenX + box.screenX + 5,
+ y: client.mainWindow.screenY + box.screenY + box.height + 25 };
+ popup.moveTo(pos.x, pos.y);
+ popup.showPopup(el, 0, 0, "tooltip");
+ }
+ }
+ }
+
+ var text = " " + el.value.substr(0, el.selectionStart);
+ if (el.selectionStart != el.selectionEnd)
+ text = "";
+
+ if (text.match(/[^%]%C[0-9]{0,2},?[0-9]{0,2}$/))
+ doPopup(document.getElementById("colorTooltip"));
+ else if (text.match(/[^%]%$/))
+ doPopup(document.getElementById("percentTooltip"));
+ else
+ doPopup(null);
+}
+
+function onUserDoubleClick(event)
+{
+ if ((event.button != 0) ||
+ event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
+ {
+ return;
+ }
+ var userList = document.getElementById("user-list");
+ if (!userList.view || !userList.view.selection)
+ return;
+ var currentIndex = userList.view.selection.currentIndex;
+ if (currentIndex < 0)
+ return;
+ var nickname = getNicknameForUserlistRow(currentIndex);
+ dispatch("query", {nickname: nickname, source: "mouse"});
+}
+
+client.onFindEnd =
+CIRCNetwork.prototype.onFindEnd =
+CIRCChannel.prototype.onFindEnd =
+CIRCUser.prototype.onFindEnd =
+CIRCDCCChat.prototype.onFindEnd =
+CIRCDCCFileTransfer.prototype.onFindEnd =
+function this_onfindend(e)
+{
+ this.scrollToElement("selection", "inview");
+}
+
+CIRCChannel.prototype._updateConferenceMode =
+function my_updateconfmode()
+{
+ const minDiff = client.CONFERENCE_LOW_PASS;
+
+ var enabled = this.prefs["conference.enabled"];
+ var userLimit = this.prefs["conference.limit"];
+ var userCount = this.getUsersLength();
+
+ if (userLimit == 0)
+ {
+ // userLimit == 0 --> always off.
+ if (enabled)
+ this.prefs["conference.enabled"] = false;
+ }
+ else if (userLimit == 1)
+ {
+ // userLimit == 1 --> always on.
+ if (!enabled)
+ this.prefs["conference.enabled"] = true;
+ }
+ else if (enabled && (userCount < userLimit - minDiff))
+ {
+ this.prefs["conference.enabled"] = false;
+ }
+ else if (!enabled && (userCount > userLimit + minDiff))
+ {
+ this.prefs["conference.enabled"] = true;
+ }
+}
+
+CIRCServer.prototype.CTCPHelpClientinfo =
+function serv_ccinfohelp()
+{
+ return MSG_CTCPHELP_CLIENTINFO;
+}
+
+CIRCServer.prototype.CTCPHelpAction =
+function serv_ccinfohelp()
+{
+ return MSG_CTCPHELP_ACTION;
+}
+
+CIRCServer.prototype.CTCPHelpTime =
+function serv_ccinfohelp()
+{
+ return MSG_CTCPHELP_TIME;
+}
+
+CIRCServer.prototype.CTCPHelpVersion =
+function serv_ccinfohelp()
+{
+ return MSG_CTCPHELP_VERSION;
+}
+
+CIRCServer.prototype.CTCPHelpSource =
+function serv_csrchelp()
+{
+ return MSG_CTCPHELP_SOURCE;
+}
+
+CIRCServer.prototype.CTCPHelpOs =
+function serv_oshelp()
+{
+ return MSG_CTCPHELP_OS;
+}
+
+CIRCServer.prototype.CTCPHelpHost =
+function serv_hosthelp()
+{
+ return MSG_CTCPHELP_HOST;
+}
+
+CIRCServer.prototype.CTCPHelpPing =
+function serv_ccinfohelp()
+{
+ return MSG_CTCPHELP_PING;
+}
+
+CIRCServer.prototype.CTCPHelpDcc =
+function serv_ccinfohelp()
+{
+ return MSG_CTCPHELP_DCC;
+}
+
+/**
+ * Calculates delay before the next automatic connection attempt.
+ *
+ * If the number of connection attempts is limited, use fixed interval
+ * MIN_RECONNECT_MS. For unlimited attempts (-1), use exponential backoff: the
+ * interval between connection attempts to the network (not individual
+ * servers) is doubled after each attempt, up to MAX_RECONNECT_MS.
+ */
+CIRCNetwork.prototype.getReconnectDelayMs =
+function my_getReconnectDelayMs()
+{
+ var nServers = this.serverList.length;
+
+ if ((-1 != this.MAX_CONNECT_ATTEMPTS) ||
+ (0 != this.connectCandidate % nServers))
+ {
+ return this.MIN_RECONNECT_MS;
+ }
+
+ var networkRound = Math.ceil(this.connectCandidate / nServers);
+
+ var rv = this.MIN_RECONNECT_MS * Math.pow(2, networkRound - 1);
+
+ // clamp rv between MIN/MAX_RECONNECT_MS
+ rv = Math.min(Math.max(rv, this.MIN_RECONNECT_MS), this.MAX_RECONNECT_MS);
+
+ return rv;
+}
+
+CIRCNetwork.prototype.onInit =
+function net_oninit ()
+{
+ this.logFile = null;
+ this.lastServer = null;
+}
+
+CIRCNetwork.prototype.onInfo =
+function my_netinfo (e)
+{
+ this.display(e.msg, "INFO", undefined, undefined, e.tags);
+}
+
+CIRCNetwork.prototype.onUnknown =
+function my_unknown (e)
+{
+ if ("pendingWhoisLines" in e.server)
+ {
+ /* whois lines always have the nick in param 2 */
+ e.user = new CIRCUser(e.server, null, e.params[2]);
+
+ e.destMethod = "onUnknownWhois";
+ e.destObject = this;
+ return;
+ }
+
+ e.params.shift(); /* remove the code */
+ e.params.shift(); /* and the dest. nick (always me) */
+
+ // Handle random IRC numerics automatically.
+ var msg = getMsg("msg.irc." + e.code, null, "");
+ if (msg)
+ {
+ if (arrayIndexOf(e.server.channelTypes, e.params[0][0]) != -1)
+ {
+ // Message about a channel (e.g. join failed).
+ e.channel = new CIRCChannel(e.server, null, e.params[0]);
+ }
+
+ var targetDisplayObj = this;
+ if (e.channel && ("messages" in e.channel))
+ targetDisplayObj = e.channel;
+
+ // Check for /knock support for the +i message.
+ if (((e.code == 471) || (e.code == 473) || (e.code == 475)) &&
+ ("knock" in e.server.servCmds))
+ {
+ var args = [msg, e.channel.unicodeName,
+ "knock " + e.channel.unicodeName];
+ msg = getMsg("msg.irc." + e.code + ".knock", args, "");
+ client.munger.getRule(".inline-buttons").enabled = true;
+ targetDisplayObj.display(msg, undefined, undefined, undefined,
+ e.tags);
+ client.munger.getRule(".inline-buttons").enabled = false;
+ }
+ else
+ {
+ targetDisplayObj.display(msg, undefined, undefined, undefined,
+ e.tags);
+ }
+
+ if (e.channel)
+ {
+ if (e.channel.busy)
+ {
+ e.channel.busy = false;
+ updateProgress();
+ }
+ }
+ else
+ {
+ // Network type error?
+ if (this.busy)
+ {
+ this.busy = false;
+ updateProgress();
+ }
+ }
+ return;
+ }
+
+ /* if it looks like some kind of "end of foo" code, and we don't
+ * already have a mapping for it, make one up */
+ var length = e.params.length;
+ if (!(e.code in client.responseCodeMap) &&
+ (e.params[length - 1].search (/^end of/i) != -1))
+ {
+ client.responseCodeMap[e.code] = "---";
+ }
+
+ this.display(toUnicode(e.params.join(" "), this), e.code.toUpperCase(),
+ undefined, undefined, e.tags);
+}
+
+CIRCNetwork.prototype.lastWhoCheckChannel = null;
+CIRCNetwork.prototype.lastWhoCheckTime = 0;
+CIRCNetwork.prototype.on001 = /* Welcome! */
+CIRCNetwork.prototype.on002 = /* your host is */
+CIRCNetwork.prototype.on003 = /* server born-on date */
+CIRCNetwork.prototype.on004 = /* server id */
+CIRCNetwork.prototype.on005 = /* server features */
+CIRCNetwork.prototype.on250 = /* highest connection count */
+CIRCNetwork.prototype.on251 = /* users */
+CIRCNetwork.prototype.on252 = /* opers online (in params[2]) */
+CIRCNetwork.prototype.on254 = /* channels found (in params[2]) */
+CIRCNetwork.prototype.on255 = /* link info */
+CIRCNetwork.prototype.on265 = /* local user details */
+CIRCNetwork.prototype.on266 = /* global user details */
+CIRCNetwork.prototype.on375 = /* start of MOTD */
+CIRCNetwork.prototype.on372 = /* MOTD line */
+CIRCNetwork.prototype.on376 = /* end of MOTD */
+CIRCNetwork.prototype.on422 = /* no MOTD */
+CIRCNetwork.prototype.on670 = /* STARTTLS Success */
+CIRCNetwork.prototype.on691 = /* STARTTLS Failure */
+CIRCNetwork.prototype.on902 = /* SASL Nick locked */
+CIRCNetwork.prototype.on903 = /* SASL Auth success */
+CIRCNetwork.prototype.on904 = /* SASL Auth failed */
+CIRCNetwork.prototype.on905 = /* SASL Command too long */
+CIRCNetwork.prototype.on906 = /* SASL Aborted */
+CIRCNetwork.prototype.on907 = /* SASL Already authenticated */
+CIRCNetwork.prototype.on908 = /* SASL Mechanisms */
+function my_showtonet (e)
+{
+ var p = (3 in e.params) ? e.params[2] + " " : "";
+ var str = "";
+
+ switch (e.code)
+ {
+ case "004":
+ case "005":
+ str = e.params.slice(3).join(" ");
+ break;
+
+ case "001":
+ // Code moved to lower down to speed this bit up. :)
+ var c, u;
+ // If we've switched servers, *first* we must rehome our objects.
+ if (this.lastServer && (this.lastServer != this.primServ))
+ {
+ for (c in this.lastServer.channels)
+ this.lastServer.channels[c].rehome(this.primServ);
+ for (u in this.lastServer.users)
+ this.lastServer.users[u].rehome(this.primServ);
+
+ // This makes sure we have the *right* me object.
+ this.primServ.me.rehome(this.primServ);
+ }
+
+ // Update the list of ignored users from the prefs:
+ var ignoreAry = this.prefs["ignoreList"];
+ for (var j = 0; j < ignoreAry.length; ++j)
+ this.ignoreList[ignoreAry[j]] = getHostmaskParts(ignoreAry[j]);
+
+ // Update everything.
+ // Welcome to history.
+ addURLToHistory(this.getURL());
+ updateTitle(this);
+ this.updateHeader();
+ client.updateHeader();
+ updateSecurityIcon();
+ updateStalkExpression(this);
+
+ client.ident.removeNetwork(this);
+
+ // Figure out what nick we *really* want:
+ if (this.prefs["away"] && this.prefs["awayNick"])
+ this.preferredNick = this.prefs["awayNick"];
+ else
+ this.preferredNick = this.prefs["nickname"];
+
+ // Pretend this never happened.
+ delete this.pendingNickChange;
+
+ str = e.decodeParam(2);
+
+ break;
+
+ case "251": /* users */
+ this.doAutoPerform();
+
+ // Set our initial monitor list
+ if ((this.primServ.supports["monitor"]) &&
+ (this.prefs["notifyList"].length > 0))
+ {
+ this.primServ.sendMonitorList(this.prefs["notifyList"], true);
+ }
+
+ this.isIdleAway = client.isIdleAway;
+ if (this.prefs["away"])
+ this.dispatch("away", { reason: this.prefs["away"] });
+
+ if (this.lastServer)
+ {
+ // Re-join channels from previous connection.
+ for (c in this.primServ.channels)
+ {
+ var chan = this.primServ.channels[c];
+ if (chan.joined)
+ chan.join(chan.mode.key);
+ }
+ }
+ this.lastServer = this.primServ;
+
+ if ("pendingURLs" in this)
+ {
+ var target = this.pendingURLs.pop();
+ while (target)
+ {
+ gotoIRCURL(target.url, target.e);
+ target = this.pendingURLs.pop();
+ }
+ delete this.pendingURLs;
+ }
+
+ // Do this after the JOINs, so they are quicker.
+ // This is not time-critical code.
+ if (client.prefs["dcc.enabled"] && this.prefs["dcc.useServerIP"])
+ {
+ var delayFn = function(t) {
+ // This is the quickest way to get out host/IP.
+ t.pendingUserhostReply = true;
+ t.primServ.sendData("USERHOST " +
+ t.primServ.me.encodedName + "\n");
+ };
+ setTimeout(delayFn, 1000 * Math.random(), this);
+ }
+
+ // Had some collision during connect.
+ if (this.primServ.me.unicodeName != this.preferredNick)
+ {
+ this.reclaimLeft = this.RECLAIM_TIMEOUT;
+ this.reclaimName();
+ }
+
+ if ("onLogin" in this)
+ {
+ ev = new CEvent("network", "login", this, "onLogin");
+ client.eventPump.addEvent(ev);
+ }
+
+ str = e.decodeParam(e.params.length - 1);
+ break;
+
+ case "376": /* end of MOTD */
+ case "422": /* no MOTD */
+ this.busy = false;
+ updateProgress();
+
+ /* Some servers (wrongly) dont send 251, so try
+ auto-perform after the MOTD as well */
+ this.doAutoPerform();
+ /* no break */
+
+ case "372":
+ case "375":
+ case "376":
+ if (this.IGNORE_MOTD)
+ return;
+ /* no break */
+
+ default:
+ str = e.decodeParam(e.params.length - 1);
+ break;
+ }
+
+ this.displayHere(p + str, e.code.toUpperCase(), undefined, undefined,
+ e.tags);
+}
+
+CIRCNetwork.prototype.onUnknownCTCPReply =
+function my_ctcprunk (e)
+{
+ this.display(getMsg(MSG_FMT_CTCPREPLY,
+ [toUnicode(e.CTCPCode, this),
+ toUnicode(e.CTCPData, this), e.user.unicodeName]),
+ "CTCP_REPLY", e.user, e.server.me, e.tags);
+}
+
+CIRCNetwork.prototype.onNotice =
+function my_notice(e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.display(e.decodeParam(2), "NOTICE", this, e.server.me, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCNetwork.prototype.onPrivmsg =
+function my_privmsg(e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.display(e.decodeParam(2), "PRIVMSG", this, e.server.me, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+/* userhost reply */
+CIRCNetwork.prototype.on302 =
+function my_302(e)
+{
+ if (client.prefs["dcc.enabled"] && this.prefs["dcc.useServerIP"] &&
+ ("pendingUserhostReply" in this))
+ {
+ var me = new RegExp("^" + this.primServ.me.encodedName + "\\*?=", "i");
+ if (e.params[2].match(me))
+ client.dcc.addHost(this.primServ.me.host, true);
+
+ delete this.pendingUserhostReply;
+ return true;
+ }
+
+ e.destMethod = "onUnknown";
+ e.destObject = this;
+
+ return true;
+}
+
+CIRCNetwork.prototype.on303 = /* ISON (aka notify) reply */
+function my_303 (e)
+{
+ function lower(text)
+ {
+ return e.server.toLowerCase(text);
+ };
+
+ var onList = new Array();
+ // split() gives an array of one item ("") when splitting "", which we
+ // don't want, so only do the split if there's something to split.
+ if (e.params[2])
+ onList = stringTrim(e.server.toLowerCase(e.params[2])).split(/\s+/);
+ var offList = new Array();
+ var newArrivals = new Array();
+ var newDepartures = new Array();
+ var o = getObjectDetails(client.currentObject);
+ var displayTab;
+ var i;
+
+ if ("network" in o && o.network == this && client.currentObject != this)
+ displayTab = client.currentObject;
+
+ for (i = 0; i < this.prefs["notifyList"].length; i++)
+ {
+ if (!arrayContains(onList, lower(this.prefs["notifyList"][i])))
+ /* user is not on */
+ offList.push(lower(this.prefs["notifyList"][i]));
+ }
+
+ if ("onList" in this)
+ {
+ for (i in onList)
+ if (!arrayContains(this.onList, onList[i]))
+ /* we didn't know this person was on */
+ newArrivals.push(onList[i]);
+ }
+ else
+ this.onList = newArrivals = onList;
+
+ if ("offList" in this)
+ {
+ for (i in offList)
+ if (!arrayContains(this.offList, offList[i]))
+ /* we didn't know this person was off */
+ newDepartures.push(offList[i]);
+ }
+ else
+ this.offList = newDepartures = offList;
+
+ if (newArrivals.length > 0)
+ {
+ this.displayHere (arraySpeak (newArrivals, "is", "are") +
+ " online.", "NOTIFY-ON", undefined, undefined,
+ e.tags);
+ if (displayTab)
+ displayTab.displayHere (arraySpeak (newArrivals, "is", "are") +
+ " online.", "NOTIFY-ON", undefined,
+ undefined, e.tags);
+ }
+
+ if (newDepartures.length > 0)
+ {
+ this.displayHere (arraySpeak (newDepartures, "is", "are") +
+ " offline.", "NOTIFY-OFF", undefined, undefined,
+ e.tags);
+ if (displayTab)
+ displayTab.displayHere (arraySpeak (newDepartures, "is", "are") +
+ " offline.", "NOTIFY-OFF", undefined,
+ undefined, e.tags);
+ }
+
+ this.onList = onList;
+ this.offList = offList;
+
+}
+
+CIRCNetwork.prototype.on730 = /* RPL_MONONLINE */
+CIRCNetwork.prototype.on731 = /* RPL_MONOFFLINE */
+function my_monnotice(e)
+{
+ var userList = e.params[2].split(",");
+ var nickList = [];
+ var o = getObjectDetails(client.currentObject);
+ var displayTab;
+ var i;
+ var msg;
+
+ if ("network" in o && o.network == this && client.currentObject != this)
+ displayTab = client.currentObject;
+
+ for (i = 0; i < userList.length; i++)
+ {
+ var nick = e.server.toLowerCase(userList[i].split("!")[0]);
+
+ // Make sure this nick is in the notify list.
+ if (this.prefs["notifyList"].indexOf(nick) < 0)
+ {
+ this.prefs["notifyList"].push(nick);
+ this.prefs["notifyList"].update();
+ }
+ nickList.push(nick);
+ }
+
+ if (e.code == "730") // RPL_MONONLINE
+ msg = arraySpeak (nickList, "is", "are") + " online.";
+ else // RPL_MONOFFLINE
+ msg = arraySpeak (nickList, "is", "are") + " offline.";
+ this.displayHere(msg, e.code, undefined, undefined, e.tags);
+ if (displayTab)
+ displayTab.displayHere(msg, e.code, undefined, undefined, e.tags);
+}
+
+CIRCNetwork.prototype.on732 = /* RPL_MONLIST */
+function my_732(e)
+{
+ if (!this.pendingNotifyList)
+ this.pendingNotifyList = [];
+ var nickList = e.server.toLowerCase(e.params[2]).split(",")
+ this.pendingNotifyList = this.pendingNotifyList.concat(nickList);
+}
+
+CIRCNetwork.prototype.on733 = /* RPL_ENDOFMONLIST */
+function my_733(e)
+{
+ if (this.pendingNotifyList)
+ {
+ this.prefs["notifyList"] = this.pendingNotifyList;
+ this.prefs["notifyList"].update();
+ this.display(getMsg(MSG_NOTIFY_LIST, arraySpeak(this.pendingNotifyList)));
+ delete this.pendingNotifyList;
+ if (e.params[2])
+ this.display(e.params[2], e.code, undefined, undefined, e.tags);
+ }
+ else
+ {
+ this.prefs["notifyList"] = [];
+ this.prefs["notifyList"].update();
+ display(MSG_NO_NOTIFY_LIST, e.code, undefined, undefined, e.tags);
+ }
+}
+
+CIRCNetwork.prototype.on734 = /* ERR_MONLISTFULL */
+function my_734(e)
+{
+ var nickList = e.server.toLowerCase(e.params[3]).split(",")
+ var i;
+ var msgname;
+
+ for (i = 0; i < nickList.length; i++)
+ {
+ var j = this.prefs["notifyList"].indexOf(nickList[i]);
+ if (j >= 0)
+ arrayRemoveAt(this.prefs["notifyList"], j);
+ }
+ this.prefs["notifyList"].update();
+
+ if (e.params[4])
+ this.display(e.params[4], e.code, undefined, undefined, e.tags)
+ else
+ this.display(MSG_NOTIFY_FULL);
+
+ msgname = (nickList.length == 1) ? MSG_NOTIFY_DELONE :
+ MSG_NOTIFY_DELSOME;
+ this.display(getMsg(msgname, arraySpeak(nickList)));
+}
+
+/* away off reply */
+CIRCNetwork.prototype.on305 =
+function my_305(e)
+{
+ this.display(MSG_AWAY_OFF, e.code, undefined, undefined, e.tags);
+
+ return true;
+}
+
+/* away on reply */
+CIRCNetwork.prototype.on306 =
+function my_306(e)
+{
+ var idleMsgParams = [this.prefs["away"], client.prefs["awayIdleTime"]];
+ if (!this.isIdleAway)
+ this.display(getMsg(MSG_AWAY_ON, this.prefs["away"]), e.code,
+ undefined, undefined, e.tags);
+ else
+ this.display(getMsg(MSG_IDLE_AWAY_ON, idleMsgParams), e.code,
+ undefined, undefined, e.tags);
+
+ return true;
+}
+
+
+CIRCNetwork.prototype.on263 = /* 'try again' */
+function my_263 (e)
+{
+ /* Urgh, this one's a pain. We need to abort whatever we tried, and start
+ * it again if appropriate.
+ *
+ * Known causes of this message:
+ * - LIST, with or without a parameter.
+ */
+
+ if (("_list" in this) && !this._list.done && (this._list.count == 0))
+ {
+ // We attempted a LIST, and we think it failed. :)
+ this._list.done = true;
+ this._list.error = e.decodeParam(2);
+ // Return early for this one if we're saving it.
+ if ("file" in this._list)
+ return true;
+ }
+
+ e.destMethod = "onUnknown";
+ e.destObject = this;
+ return true;
+}
+
+CIRCNetwork.prototype.isRunningList =
+function my_running_list()
+{
+ /* The list is considered "running" when a cancel is effective. This means
+ * that even if _list.done is true (finished recieving data), we will still
+ * be "running" whilst we have undisplayed items.
+ */
+ return (("_list" in this) &&
+ (!this._list.done || (this._list.length > this._list.displayed)) &&
+ !this._list.cancelled);
+}
+
+CIRCNetwork.prototype.list =
+function my_list(word, file)
+{
+ const NORMAL_FILE_TYPE = Components.interfaces.nsIFile.NORMAL_FILE_TYPE;
+
+ if (("_list" in this) && !this._list.done)
+ return false;
+
+ this._list = new Array();
+ this._list.string = word;
+ this._list.regexp = null;
+ this._list.done = false;
+ this._list.count = 0;
+ if (file)
+ {
+ var lfile = new LocalFile(file);
+ if (!lfile.localFile.exists())
+ {
+ // futils.umask may be 0022. Result is 0644.
+ lfile.localFile.create(NORMAL_FILE_TYPE, 0o666 & ~futils.umask);
+ }
+ this._list.file = new LocalFile(lfile.localFile, ">");
+ }
+
+ if (isinstance(word, RegExp))
+ {
+ this._list.regexp = word;
+ this._list.string = "";
+ word = "";
+ }
+
+ if (word)
+ this.primServ.sendData("LIST " + fromUnicode(word, this) + "\n");
+ else
+ this.primServ.sendData("LIST\n");
+
+ return true;
+}
+
+CIRCNetwork.prototype.listInit =
+function my_list_init ()
+{
+ function checkEndList (network)
+ {
+ if (network._list.count == network._list.lastLength)
+ {
+ network.on323();
+ }
+ else
+ {
+ network._list.lastLength = network._list.count;
+ network._list.endTimeout =
+ setTimeout(checkEndList, 5000, network);
+ }
+ }
+
+ function outputList (network)
+ {
+ const CHUNK_SIZE = 5;
+ var list = network._list;
+ if (list.cancelled)
+ {
+ if (list.done)
+ {
+ /* The server is no longer throwing stuff at us, so now
+ * we can safely kill the list.
+ */
+ network.display(getMsg(MSG_LIST_END,
+ [list.displayed, list.count]));
+ delete network._list;
+ }
+ else
+ {
+ /* We cancelled the list, but we're still getting data.
+ * Handle that data, but don't display, and do it more
+ * slowly, so we cause less lag.
+ */
+ setTimeout(outputList, 1000, network);
+ }
+ return;
+ }
+ if (list.length > list.displayed)
+ {
+ var start = list.displayed;
+ var end = list.length;
+ if (end - start > CHUNK_SIZE)
+ end = start + CHUNK_SIZE;
+ for (var i = start; i < end; ++i)
+ network.displayHere(getMsg(MSG_FMT_CHANLIST, list[i]), "322",
+ undefined, undefined, list[i][3]);
+ list.displayed = end;
+ }
+ if (list.done && (list.displayed == list.length))
+ {
+ if (list.event323)
+ {
+ var length = list.event323.params.length;
+ network.displayHere(list.event323.params[length - 1], "323",
+ undefined, undefined, list.event323.tags);
+ }
+ network.displayHere(getMsg(MSG_LIST_END,
+ [list.displayed, list.count]));
+ }
+ else
+ {
+ setTimeout(outputList, 250, network);
+ }
+ }
+
+ if (!("_list" in this))
+ {
+ this._list = new Array();
+ this._list.string = MSG_UNKNOWN;
+ this._list.regexp = null;
+ this._list.done = false;
+ this._list.count = 0;
+ }
+
+ if (!("file" in this._list))
+ {
+ this._list.displayed = 0;
+ if (client.currentObject != this)
+ display (getMsg(MSG_LIST_REROUTED, this.unicodeName));
+ setTimeout(outputList, 250, this);
+ }
+ this._list.lastLength = 0;
+ this._list.endTimeout = setTimeout(checkEndList, 5000, this);
+}
+
+CIRCNetwork.prototype.abortList =
+function my_abortList()
+{
+ this._list.cancelled = true;
+}
+
+CIRCNetwork.prototype.on321 = /* LIST reply header */
+function my_321 (e)
+{
+ this.listInit();
+
+ if (!("file" in this._list))
+ this.displayHere (e.params[2] + " " + e.params[3], "321");
+}
+
+CIRCNetwork.prototype.on323 = /* end of LIST reply */
+function my_323 (e)
+{
+ if (this._list.endTimeout)
+ {
+ clearTimeout(this._list.endTimeout);
+ delete this._list.endTimeout;
+ }
+ if (("file" in this._list))
+ this._list.file.close();
+
+ this._list.done = true;
+ this._list.event323 = e;
+}
+
+CIRCNetwork.prototype.on322 = /* LIST reply */
+function my_listrply (e)
+{
+ if (!("_list" in this) || !("lastLength" in this._list))
+ this.listInit();
+
+ ++this._list.count;
+
+ /* If the list has been cancelled, don't bother adding all this info
+ * anymore. Do increase the count (above), otherwise we never truly notice
+ * the list being finished.
+ */
+ if (this._list.cancelled)
+ return;
+
+ var chanName = e.decodeParam(2);
+ var topic = e.decodeParam(4);
+ if (!this._list.regexp || chanName.match(this._list.regexp) ||
+ topic.match(this._list.regexp))
+ {
+ if (!("file" in this._list))
+ {
+ this._list.push([chanName, e.params[3], topic, e.tags]);
+ }
+ else
+ {
+ this._list.file.write(fromUnicode(chanName, "UTF-8") + " " +
+ e.params[3] + " " +
+ fromUnicode(topic, "UTF-8") + "\n");
+ }
+ }
+}
+
+CIRCNetwork.prototype.on401 = /* ERR_NOSUCHNICK */
+CIRCNetwork.prototype.on402 = /* ERR_NOSUCHSERVER */
+CIRCNetwork.prototype.on403 = /* ERR_NOSUCHCHANNEL */
+function my_401(e)
+{
+ var server, channel, user;
+
+ /* Note that servers generally only send 401 and 402, sharing the former
+ * between nicknames and channels, but we're ready for anything.
+ */
+ if (e.code == 402)
+ server = e.decodeParam(2);
+ else if (arrayIndexOf(e.server.channelTypes, e.params[2][0]) != -1)
+ channel = new CIRCChannel(e.server, null, e.params[2]);
+ else
+ user = new CIRCUser(e.server, null, e.params[2]);
+
+ if (user && this.whoisList && (user.collectionKey in this.whoisList))
+ {
+ // If this is from a /whois, send a /whowas and don't display anything.
+ this.primServ.whowas(user.unicodeName, 1);
+ this.whoisList[user.collectionKey] = false;
+ return;
+ }
+
+ if (user)
+ user.display(getMsg(MSG_IRC_401, [user.unicodeName]), e.code,
+ undefined, undefined, e.tags);
+ else if (server)
+ this.display(getMsg(MSG_IRC_402, [server]), e.code,
+ undefined, undefined, e.tags);
+ else if (channel)
+ channel.display(getMsg(MSG_IRC_403, [channel.unicodeName]), e.code,
+ undefined, undefined, e.tags);
+ else
+ dd("on401: unreachable code.");
+}
+
+/* 464; "invalid or missing password", occurs as a reply to both OPER and
+ * sometimes initially during user registration. */
+CIRCNetwork.prototype.on464 =
+function my_464(e)
+{
+ if (this.state == NET_CONNECTING)
+ {
+ // If we are in the process of connecting we are needing a login
+ // password, subtly different from after user registration.
+ this.display(MSG_IRC_464_LOGIN, e.code, undefined, undefined, e.tags);
+ }
+ else
+ {
+ e.destMethod = "onUnknown";
+ e.destObject = this;
+ }
+}
+
+/* end of WHO */
+CIRCNetwork.prototype.on315 =
+function my_315 (e)
+{
+ var matches;
+ if ("whoMatches" in this)
+ matches = this.whoMatches;
+ else
+ matches = 0;
+
+ if ("pendingWhoReply" in this)
+ this.display(getMsg(MSG_WHO_END, [e.params[2], matches]), e.code,
+ undefined, undefined, e.tags);
+
+ if ("whoUpdates" in this)
+ {
+ var userlist = document.getElementById("user-list");
+ for (var c in this.whoUpdates)
+ {
+ for (var i = 0; i < this.whoUpdates[c].length; i++)
+ {
+ var index = this.whoUpdates[c][i].chanListEntry.childIndex;
+ userlist.treeBoxObject.invalidateRow(index);
+ }
+ this.primServ.channels[c].updateUsers(this.whoUpdates[c]);
+ }
+ delete this.whoUpdates;
+ }
+
+ delete this.pendingWhoReply;
+ delete this.whoMatches;
+}
+
+CIRCNetwork.prototype.on352 =
+function my_352 (e)
+{
+ //0-352 1-sender 2-channel 3-ident 4-host
+ //5-server 6-nick 7-H/G 8-hops and realname
+ if ("pendingWhoReply" in this)
+ {
+ var status;
+ if (e.user.isAway)
+ status = MSG_GONE;
+ else
+ status = MSG_HERE;
+
+ this.display(getMsg(MSG_WHO_MATCH,
+ [e.params[6], e.params[3], e.params[4],
+ e.user.desc, status, e.decodeParam(2),
+ e.params[5], e.user.hops]), e.code, e.user,
+ undefined, e.tags);
+ }
+
+ updateTitle(e.user);
+ if ("whoMatches" in this)
+ ++this.whoMatches;
+ else
+ this.whoMatches = 1;
+
+ if (!("whoUpdates" in this))
+ this.whoUpdates = new Object();
+
+ if (e.userHasChanges)
+ {
+ for (var c in e.server.channels)
+ {
+ var chan = e.server.channels[c];
+ if (chan.active && (e.user.collectionKey in chan.users))
+ {
+ if (!(c in this.whoUpdates))
+ this.whoUpdates[c] = new Array();
+ this.whoUpdates[c].push(chan.users[e.user.collectionKey]);
+ }
+ }
+ }
+}
+
+CIRCNetwork.prototype.on354 =
+function my_354(e)
+{
+ //0-352 1-sender 2-type 3-channel 4-ident 5-host
+ //6-server 7-nick 8-H/G 9-hops 10-account 11-realname
+ if ("pendingWhoReply" in this)
+ {
+ var status;
+ if (e.user.isAway)
+ status = MSG_GONE;
+ else
+ status = MSG_HERE;
+
+ this.display(getMsg(MSG_WHO_MATCH,
+ [e.params[7], e.params[4], e.params[5],
+ e.user.desc, status, e.decodeParam(3),
+ e.params[6], e.user.hops]), e.code, e.user,
+ undefined, e.tags);
+ }
+
+ updateTitle(e.user);
+ if ("whoMatches" in this)
+ ++this.whoMatches;
+ else
+ this.whoMatches = 1;
+
+ if (!("whoUpdates" in this))
+ this.whoUpdates = new Object();
+
+ if (e.userHasChanges)
+ {
+ for (var c in e.server.channels)
+ {
+ var chan = e.server.channels[c];
+ if (chan.active && (e.user.collectionKey in chan.users))
+ {
+ if (!(c in this.whoUpdates))
+ this.whoUpdates[c] = new Array();
+ this.whoUpdates[c].push(chan.users[e.user.collectionKey]);
+ }
+ }
+ }
+}
+
+CIRCNetwork.prototype.on301 = /* user away message */
+function my_301(e)
+{
+ if (e.user.awayMessage != e.user.lastShownAwayMessage)
+ {
+ var params = [e.user.unicodeName, e.user.awayMessage];
+ e.user.display(getMsg(MSG_WHOIS_AWAY, params), e.code,
+ undefined, undefined, e.tags);
+ e.user.lastShownAwayMessage = e.user.awayMessage;
+ }
+}
+
+CIRCNetwork.prototype.on311 = /* whois name */
+CIRCNetwork.prototype.on319 = /* whois channels */
+CIRCNetwork.prototype.on312 = /* whois server */
+CIRCNetwork.prototype.on317 = /* whois idle time */
+CIRCNetwork.prototype.on318 = /* whois end of whois*/
+CIRCNetwork.prototype.on330 = /* ircu's 330 numeric ("X is logged in as Y") */
+CIRCNetwork.prototype.onUnknownWhois = /* misc whois line */
+function my_whoisreply (e)
+{
+ var text = "egads!";
+ var nick = e.params[2];
+ var lowerNick = this.primServ.toLowerCase(nick);
+ var user;
+
+ if (this.whoisList && (e.code != 318) && (lowerNick in this.whoisList))
+ this.whoisList[lowerNick] = true;
+
+ if (e.user)
+ {
+ user = e.user;
+ nick = user.unicodeName;
+ }
+
+ switch (Number(e.code))
+ {
+ case 311:
+ // Clear saved away message so it appears and can be reset.
+ if (e.user)
+ e.user.lastShownAwayMessage = "";
+
+ text = getMsg(MSG_WHOIS_NAME,
+ [nick, e.params[3], e.params[4],
+ e.decodeParam(6)]);
+ break;
+
+ case 319:
+ var ary = stringTrim(e.decodeParam(3)).split(" ");
+ text = getMsg(MSG_WHOIS_CHANNELS, [nick, arraySpeak(ary)]);
+ break;
+
+ case 312:
+ text = getMsg(MSG_WHOIS_SERVER,
+ [nick, e.params[3], e.params[4]]);
+ break;
+
+ case 317:
+ text = getMsg(MSG_WHOIS_IDLE,
+ [nick, formatDateOffset(Number(e.params[3])),
+ new Date(Number(e.params[4]) * 1000)]);
+ break;
+
+ case 318:
+ // If the user isn't here, then we sent a whowas in on401.
+ // Don't display the "end of whois" message.
+ if (this.whoisList && (lowerNick in this.whoisList) &&
+ !this.whoisList[lowerNick])
+ {
+ delete this.whoisList[lowerNick];
+ return;
+ }
+ if (this.whoisList)
+ delete this.whoisList[lowerNick];
+
+ text = getMsg(MSG_WHOIS_END, nick);
+ if (user)
+ user.updateHeader();
+ break;
+
+ case 330:
+ text = getMsg(MSG_FMT_LOGGED_ON, [e.decodeParam(2), e.params[3]]);
+ break;
+
+ default:
+ text = toUnicode(e.params.splice(2, e.params.length).join(" "),
+ this);
+ }
+
+ if (e.user)
+ e.user.display(text, e.code, undefined, undefined, e.tags);
+ else
+ this.display(text, e.code, undefined, undefined, e.tags);
+}
+
+CIRCNetwork.prototype.on341 = /* invite reply */
+function my_341 (e)
+{
+ this.display (getMsg(MSG_YOU_INVITE, [e.decodeParam(2), e.decodeParam(3)]),
+ "341", undefined, undefined, e.tags);
+}
+
+CIRCNetwork.prototype.onInvite = /* invite message */
+function my_invite (e)
+{
+ var invitee = e.params[1];
+ if (invitee == e.server.me.unicodeName)
+ {
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.display(getMsg(MSG_INVITE_YOU, [e.user.unicodeName, e.user.name,
+ e.user.host,
+ e.channel.unicodeName,
+ e.channel.unicodeName,
+ e.channel.getURL()]),
+ "INVITE", undefined, undefined, e.tags);
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ if ("messages" in e.channel)
+ e.channel.join();
+ }
+ else
+ {
+ this.display(getMsg(MSG_INVITE_SOMEONE, [e.user.unicodeName,
+ invitee,
+ e.channel.unicodeName]),
+ "INVITE", undefined, undefined, e.tags);
+ }
+}
+
+CIRCNetwork.prototype.on433 = /* nickname in use */
+function my_433 (e)
+{
+ var nick = toUnicode(e.params[2], this);
+
+ if ("pendingReclaimCheck" in this)
+ {
+ delete this.pendingReclaimCheck;
+ return;
+ }
+
+ if (this.state == NET_CONNECTING)
+ {
+ // Force a number, thanks.
+ var nickIndex = 1 * arrayIndexOf(this.prefs["nicknameList"], nick);
+ var newnick = null;
+
+ dd("433: failed with " + nick + " (" + nickIndex + ")");
+
+ var tryList = true;
+
+ if ((("_firstNick" in this) && (this._firstNick == -1)) ||
+ (this.prefs["nicknameList"].length == 0) ||
+ ((nickIndex != -1) && (this.prefs["nicknameList"].length < 2)))
+ {
+ tryList = false;
+ }
+
+ if (tryList)
+ {
+ nickIndex = (nickIndex + 1) % this.prefs["nicknameList"].length;
+
+ if (("_firstNick" in this) && (this._firstNick == nickIndex))
+ {
+ // We're back where we started. Give up with this method.
+ this._firstNick = -1;
+ tryList = false;
+ }
+ }
+
+ if (tryList)
+ {
+ newnick = this.prefs["nicknameList"][nickIndex];
+ dd(" trying " + newnick + " (" + nickIndex + ")");
+
+ // Save first index we've tried.
+ if (!("_firstNick" in this))
+ this._firstNick = nickIndex;
+ }
+ else if (this.NICK_RETRIES > 0)
+ {
+ newnick = this.INITIAL_NICK + "_";
+ this.NICK_RETRIES--;
+ dd(" trying " + newnick);
+ }
+
+ if (newnick)
+ {
+ this.INITIAL_NICK = newnick;
+ this.display(getMsg(MSG_RETRY_NICK, [nick, newnick]), "433",
+ undefined, undefined, e.tags);
+ this.primServ.changeNick(newnick);
+ }
+ else
+ {
+ this.display(getMsg(MSG_NICK_IN_USE, nick), "433",
+ undefined, undefined, e.tags);
+ }
+ }
+ else
+ {
+ this.display(getMsg(MSG_NICK_IN_USE, nick), "433",
+ undefined, undefined, e.tags);
+ }
+}
+
+CIRCNetwork.prototype.onStartConnect =
+function my_sconnect (e)
+{
+ this.busy = true;
+ updateProgress();
+ if ("_firstNick" in this)
+ delete this._firstNick;
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.display(getMsg(MSG_CONNECTION_ATTEMPT,
+ [this.getURL(), e.server.getURL(), this.unicodeName,
+ "cancel"]), "INFO");
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ if (this.prefs["identd.enabled"])
+ {
+ try
+ {
+ client.ident.addNetwork(this, e.server);
+ }
+ catch (ex)
+ {
+ display(getMsg(MSG_IDENT_ERROR, formatException(ex)), MT_ERROR);
+ }
+ }
+
+ this.NICK_RETRIES = this.prefs["nicknameList"].length + 3;
+
+ // When connection begins, autoperform has not been sent
+ this.autoPerformSent = false;
+}
+
+CIRCNetwork.prototype.onError =
+function my_neterror (e)
+{
+ var msg;
+ var type = MT_ERROR;
+
+ if (typeof e.errorCode != "undefined")
+ {
+ switch (e.errorCode)
+ {
+ case JSIRC_ERR_NO_SOCKET:
+ msg = MSG_ERR_NO_SOCKET;
+ break;
+
+ case JSIRC_ERR_EXHAUSTED:
+ // error already displayed in onDisconnect
+ break;
+
+ case JSIRC_ERR_OFFLINE:
+ msg = MSG_ERR_OFFLINE;
+ break;
+
+ case JSIRC_ERR_NO_SECURE:
+ msg = getMsg(MSG_ERR_NO_SECURE, this.unicodeName);
+ break;
+
+ case JSIRC_ERR_CANCELLED:
+ msg = MSG_ERR_CANCELLED;
+ type = MT_INFO;
+ break;
+
+ case JSIRC_ERR_PAC_LOADING:
+ msg = MSG_WARN_PAC_LOADING;
+ type = MT_WARN;
+ break;
+ }
+ }
+ else
+ {
+ msg = e.params[e.params.length - 1];
+ }
+
+ dispatch("sync-header");
+ updateTitle();
+
+ if (this.state == NET_OFFLINE)
+ {
+ this.busy = false;
+ updateProgress();
+ }
+
+ client.ident.removeNetwork(this);
+
+ if (msg)
+ this.display(msg, type);
+
+ if (e.errorCode == JSIRC_ERR_PAC_LOADING)
+ return;
+
+ if (this.deleteWhenDone)
+ this.dispatch("delete-view");
+
+ delete this.deleteWhenDone;
+}
+
+
+CIRCNetwork.prototype.onDisconnect =
+function my_netdisconnect (e)
+{
+ var msg, msgNetwork;
+ var msgType = MT_ERROR;
+ var retrying = true;
+
+ if (typeof e.disconnectStatus != "undefined")
+ {
+ switch (e.disconnectStatus)
+ {
+ case 0:
+ msg = getMsg(MSG_CONNECTION_CLOSED,
+ [this.getURL(), e.server.getURL()]);
+ break;
+
+ case NS_ERROR_CONNECTION_REFUSED:
+ msg = getMsg(MSG_CONNECTION_REFUSED,
+ [this.getURL(), e.server.getURL()]);
+ break;
+
+ case NS_ERROR_NET_TIMEOUT:
+ msg = getMsg(MSG_CONNECTION_TIMEOUT,
+ [this.getURL(), e.server.getURL()]);
+ break;
+
+ case NS_ERROR_NET_RESET:
+ msg = getMsg(MSG_CONNECTION_RESET,
+ [this.getURL(), e.server.getURL()]);
+ break;
+
+ case NS_ERROR_NET_INTERRUPT:
+ msg = getMsg(MSG_CONNECTION_INTERRUPT,
+ [this.getURL(), e.server.getURL()]);
+ break;
+
+ case NS_ERROR_UNKNOWN_HOST:
+ msg = getMsg(MSG_UNKNOWN_HOST,
+ [e.server.hostname, this.getURL(),
+ e.server.getURL()]);
+ break;
+
+ case NS_ERROR_UNKNOWN_PROXY_HOST:
+ msg = getMsg(MSG_UNKNOWN_PROXY_HOST,
+ [this.getURL(), e.server.getURL()]);
+ break;
+
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ msg = MSG_PROXY_CONNECTION_REFUSED;
+ break;
+
+ case NS_ERROR_OFFLINE:
+ msg = MSG_ERR_OFFLINE;
+ retrying = false;
+ break;
+
+ case NS_ERROR_ABORT:
+ if (Services.io.offline)
+ {
+ msg = getMsg(MSG_CONNECTION_ABORT_OFFLINE,
+ [this.getURL(), e.server.getURL()]);
+ }
+ else
+ {
+ msg = getMsg(MSG_CONNECTION_ABORT_UNKNOWN,
+ [this.getURL(), e.server.getURL(),
+ formatException(e.exception)]);
+ }
+ retrying = false;
+ break;
+
+ default:
+ var errClass = getNSSErrorClass(e.disconnectStatus);
+ // Check here if it's a cert error.
+ // The exception adding dialog will explain the reasons.
+ if (errClass == ERROR_CLASS_BAD_CERT)
+ {
+ var cmd = "ssl-exception";
+ cmd += " " + e.server.hostname + " " + e.server.port;
+ cmd += " true";
+ msg = getMsg(MSG_INVALID_CERT, [this.getURL(), cmd]);
+ retrying = false;
+ break;
+ }
+
+ // If it's a protocol error, we can still display a useful message.
+ var statusMsg = e.disconnectStatus;
+ if (errClass == ERROR_CLASS_SSL_PROTOCOL)
+ {
+ var nssErrSvc = getService("@mozilla.org/nss_errors_service;1",
+ "nsINSSErrorsService");
+ var errMsg = nssErrSvc.getErrorMessage(e.disconnectStatus);
+ errMsg = errMsg.replace(/\.$/, "");
+ statusMsg += " (" + errMsg + ")";
+ }
+
+ msg = getMsg(MSG_CLOSE_STATUS,
+ [this.getURL(), e.server.getURL(),
+ statusMsg]);
+ break;
+ }
+ }
+ else
+ {
+ msg = getMsg(MSG_CONNECTION_CLOSED,
+ [this.getURL(), e.server.getURL()]);
+ }
+
+ // e.quitting signals the disconnect was intended: don't use "ERROR".
+ if (e.quitting)
+ {
+ msgType = "DISCONNECT";
+ msg = getMsg(MSG_CONNECTION_QUIT,
+ [this.getURL(), e.server.getURL(), this.unicodeName,
+ "reconnect"]);
+ msgNetwork = msg;
+ }
+ // We won't reconnect if the error was really bad, or if the user doesn't
+ // want us to do so.
+ else if (!retrying || !this.stayingPower)
+ {
+ msgNetwork = msg;
+ }
+ else
+ {
+ var delayStr = formatDateOffset(this.getReconnectDelayMs() / 1000);
+ if (this.MAX_CONNECT_ATTEMPTS == -1)
+ {
+ msgNetwork = getMsg(MSG_RECONNECTING_IN,
+ [msg, delayStr, this.unicodeName, "cancel"]);
+ }
+ else if (this.connectAttempt < this.MAX_CONNECT_ATTEMPTS)
+ {
+ var left = this.MAX_CONNECT_ATTEMPTS - this.connectAttempt;
+ if (left == 1)
+ {
+ msgNetwork = getMsg(MSG_RECONNECTING_IN_LEFT1,
+ [msg, delayStr, this.unicodeName,
+ "cancel"]);
+ }
+ else
+ {
+ msgNetwork = getMsg(MSG_RECONNECTING_IN_LEFT,
+ [msg, left, delayStr, this.unicodeName,
+ "cancel"]);
+ }
+ }
+ else
+ {
+ msgNetwork = getMsg(MSG_CONNECTION_EXHAUSTED, msg);
+ }
+ }
+
+ /* If we were connected ok, put an error on all tabs. If we were only
+ * /trying/ to connect, and failed, just put it on the network tab.
+ */
+ client.munger.getRule(".inline-buttons").enabled = true;
+ if (this.state == NET_ONLINE)
+ {
+ for (var v in client.viewsArray)
+ {
+ var obj = client.viewsArray[v].source;
+ if (obj == this)
+ {
+ obj.displayHere(msgNetwork, msgType);
+ }
+ else if (obj != client)
+ {
+ var details = getObjectDetails(obj);
+ if ("server" in details && details.server == e.server)
+ obj.displayHere(msg, msgType);
+ }
+ }
+ }
+ else
+ {
+ this.busy = false;
+ updateProgress();
+
+ // Don't do anything if we're cancelling.
+ if (this.state != NET_CANCELLING)
+ {
+ this.displayHere(msgNetwork, msgType);
+ }
+ }
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ for (var c in this.primServ.channels)
+ {
+ var channel = this.primServ.channels[c];
+ channel._clearUserList();
+ }
+
+ dispatch("sync-header");
+ updateTitle();
+ updateProgress();
+ updateSecurityIcon();
+
+ client.ident.removeNetwork(this);
+
+ if ("userClose" in client && client.userClose &&
+ client.getConnectionCount() == 0)
+ window.close();
+
+ // Renew the STS policy.
+ if (e.server.isSecure && ("sts" in e.server.caps) && client.sts.ENABLED)
+ {
+ var policy = client.sts.parseParameters(e.server.capvals["sts"]);
+ client.sts.setPolicy(e.server.hostname, e.server.port, policy.duration);
+ }
+
+ if (("reconnect" in this) && this.reconnect)
+ {
+ if ("stsUpgradePort" in this)
+ {
+ e.server.port = this.stsUpgradePort;
+ e.server.isSecure = true;
+ delete this.stsUpgradePort;
+ }
+ this.connect(this.requireSecurity);
+ delete this.reconnect;
+ }
+}
+
+CIRCNetwork.prototype.onCTCPReplyPing =
+function my_replyping (e)
+{
+ // see bug 326523
+ if (stringTrim(e.CTCPData).length != 13)
+ {
+ this.display(getMsg(MSG_PING_REPLY_INVALID, e.user.unicodeName),
+ "INFO", e.user, "ME!", e.tags);
+ return;
+ }
+
+ var delay = formatDateOffset((new Date() - new Date(Number(e.CTCPData))) /
+ 1000);
+ this.display(getMsg(MSG_PING_REPLY, [e.user.unicodeName, delay]), "INFO",
+ e.user, "ME!", e.tags);
+}
+
+CIRCNetwork.prototype.on221 =
+CIRCNetwork.prototype.onUserMode =
+function my_umode (e)
+{
+ if ("user" in e && e.user)
+ {
+ e.user.updateHeader();
+ this.display(getMsg(MSG_USER_MODE, [e.user.unicodeName, e.params[2]]),
+ MT_MODE, undefined, undefined, e.tags);
+ }
+ else
+ {
+ this.display(getMsg(MSG_USER_MODE, [e.params[1], e.params[2]]),
+ MT_MODE, undefined, undefined, e.tags);
+ }
+}
+
+CIRCNetwork.prototype.onNick =
+function my_cnick (e)
+{
+ if (!ASSERT(userIsMe(e.user), "network nick event for third party"))
+ return;
+
+ if (("pendingNickChange" in this) &&
+ (this.pendingNickChange == e.user.unicodeName))
+ {
+ this.prefs["nickname"] = e.user.unicodeName;
+ this.preferredNick = e.user.unicodeName;
+ delete this.pendingNickChange;
+ }
+
+ if (getTabForObject(this))
+ {
+ this.displayHere(getMsg(MSG_NEWNICK_YOU, e.user.unicodeName),
+ "NICK", "ME!", e.user, e.tags);
+ }
+
+ this.updateHeader();
+ updateStalkExpression(this);
+}
+
+CIRCNetwork.prototype.onPing =
+function my_netping (e)
+{
+ this.updateHeader(this);
+}
+
+CIRCNetwork.prototype.onPong =
+function my_netpong (e)
+{
+ this.updateHeader(this);
+}
+
+CIRCNetwork.prototype.onWallops =
+function my_netwallops(e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ if (e.user)
+ this.display(e.msg, "WALLOPS/WALLOPS", e.user, this, e.tags);
+ else
+ this.display(e.msg, "WALLOPS/WALLOPS", undefined, this, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+/* unknown command reply */
+CIRCNetwork.prototype.on421 =
+function my_421(e)
+{
+ this.display(getMsg(MSG_IRC_421, e.decodeParam(2)), MT_ERROR, undefined,
+ undefined, e.tags);
+ return true;
+}
+
+/* cap reply */
+CIRCNetwork.prototype.onCap =
+function my_cap(e)
+{
+ if (e.params[2] == "LS")
+ {
+ // Handle the STS upgrade policy if we have one.
+ if (e.server.pendingCapNegotiation && e.stsUpgradePort)
+ {
+ this.display(getMsg(MSG_STS_UPGRADE, e.stsUpgradePort));
+ this.reconnect = true;
+ this.stsUpgradePort = e.stsUpgradePort;
+ this.quit(MSG_RECONNECTING);
+ return true;
+ }
+
+ // Don't show the raw message until we've registered.
+ if (this.state == NET_ONLINE)
+ {
+
+ var listCaps = new Array();
+ for (var cap in e.server.caps)
+ {
+ var value = e.server.capvals[cap];
+ if (value)
+ cap += "=" + value;
+ listCaps.push(cap);
+ }
+ if (listCaps.length > 0)
+ {
+ listCaps.sort();
+ this.display(getMsg(MSG_SUPPORTS_CAPS, listCaps.join(", ")));
+ }
+ }
+
+ // Update the STS duration policy.
+ if (e.server.isSecure && ("sts" in e.server.caps) && client.sts.ENABLED)
+ {
+ var policy = client.sts.parseParameters(e.server.capvals["sts"]);
+ client.sts.setPolicy(e.server.hostname, e.server.port, policy.duration);
+ }
+ }
+ else if (e.params[2] == "LIST")
+ {
+ var listCapsEnabled = new Array();
+ for (var cap in e.server.caps)
+ {
+ if (e.server.caps[cap])
+ {
+ listCapsEnabled.push(cap);
+ }
+ }
+ if (listCapsEnabled.length > 0)
+ {
+ listCapsEnabled.sort();
+ this.display(getMsg(MSG_SUPPORTS_CAPSON,
+ listCapsEnabled.join(", ")));
+ }
+ }
+ else if (e.params[2] == "ACK")
+ {
+ if (e.capsOn.length)
+ this.display(getMsg(MSG_CAPS_ON, e.capsOn.join(", ")));
+ if (e.capsOff.length)
+ this.display(getMsg(MSG_CAPS_OFF, e.capsOff.join(", ")));
+ }
+ else if (e.params[2] == "NAK")
+ {
+ this.display(getMsg(MSG_CAPS_ERROR, e.caps.join(", ")));
+ }
+ else if (e.params[2] == "NEW")
+ {
+ // Handle a new STS policy
+ if (client.sts.ENABLED && (arrayContains(e.newcaps, "sts")))
+ {
+ var policy = client.sts.parseParameters(e.server.capvals["sts"]);
+ if (!e.server.isSecure && policy.port)
+ {
+ // Inform the user of the new upgrade policy and
+ // offer an option to reconnect.
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.display(getMsg(MSG_STS_UPGRADE_NEW, [this.unicodeName, "reconnect"]));
+ client.munger.getRule(".inline-buttons").enabled = false;
+ }
+ else if (e.server.isSecure && policy.duration)
+ {
+ // Renew the policy's duration.
+ client.sts.setPolicy(e.server.hostname, e.server.port, policy.duration);
+ }
+ }
+ }
+ return true;
+}
+
+// Notify the user of received CTCP requests.
+CIRCNetwork.prototype.onReceiveCTCP =
+function my_ccrecv(e)
+{
+ // Do nothing if we receive these.
+ if ((e.type == "ctcp-action") ||
+ (e.type == "ctcp-dcc") ||
+ (e.type == "unk-ctcp"))
+ return true;
+
+ this.display(getMsg(MSG_FMT_CTCPRECV,
+ [toUnicode(e.CTCPCode, this),
+ toUnicode(e.CTCPData, this), e.user.unicodeName]),
+ "CTCP_REQUEST", e.user, e.server.me, e.tags);
+
+ return true;
+}
+
+/* SASL authentication start */
+CIRCNetwork.prototype.onSASLStart =
+function my_saslstart(e)
+{
+ if (!e.mechs || e.mechs.indexOf("plain") !== -1)
+ e.server.sendData("AUTHENTICATE PLAIN\n");
+}
+
+/* SASL authentication response */
+CIRCNetwork.prototype.onAuthenticate =
+function my_auth(e)
+{
+ if (e.params[1] !== "+")
+ return;
+
+ var username = e.server.me.encodedName;
+ var password = client.tryToGetLogin(e.server.parent.getURL(), "sasl",
+ e.server.me.name, null, true,
+ getMsg(MSG_SASL_PASSWORD, username));
+ if (!password)
+ {
+ // Abort authentication.
+ e.server.sendAuthAbort();
+ return;
+ }
+
+ var auth = username + '\0' + username + '\0' + password;
+ e.server.sendAuthResponse(auth);
+}
+
+CIRCNetwork.prototype.onNetsplitBatch =
+function my_netsplit_batch(e)
+{
+ for (var c in this.primServ.channels)
+ {
+ if (e.starting)
+ {
+ this.startMsgGroup(e.reftag, getMsg(MSG_BATCH_NETSPLIT_START,
+ [e.params[3],
+ e.params[4]]),
+ e.batchtype);
+ }
+ else
+ {
+ this.display(MSG_BATCH_NETSPLIT_END, e.batchtype);
+ this.endMsgGroup();
+ }
+ }
+}
+
+CIRCNetwork.prototype.onNetjoinBatch =
+function my_netjoin_batch(e)
+{
+ for (var c in this.primServ.channels)
+ {
+ if (e.starting)
+ {
+ this.startMsgGroup(e.reftag, getMsg(MSG_BATCH_NETJOIN_START,
+ [e.params[3],
+ e.params[4]]),
+ e.batchtype);
+ }
+ else
+ {
+ this.display(MSG_BATCH_NETJOIN_END, e.batchtype);
+ this.endMsgGroup();
+ }
+ }
+}
+
+CIRCChannel.prototype.onChathistoryBatch =
+function my_chathistory_batch(e)
+{
+ if (e.starting)
+ {
+ this.startMsgGroup(e.reftag, getMsg(MSG_BATCH_CHATHISTORY_START,
+ [e.params[3]]),
+ e.batchtype);
+ }
+ else
+ {
+ this.display(MSG_BATCH_CHATHISTORY_END, e.batchtype);
+ this.endMsgGroup();
+ }
+}
+
+CIRCNetwork.prototype.onUnknownBatch =
+CIRCChannel.prototype.onUnknownBatch =
+CIRCUser.prototype.onUnknownBatch =
+function my_unknown_batch(e)
+{
+ if (e.starting)
+ {
+ this.startMsgGroup(e.reftag, getMsg(MSG_BATCH_UNKNOWN,
+ [e.batchtype,
+ e.params.slice(3)]),
+ "BATCH");
+ }
+ else
+ {
+ this.display(MSG_BATCH_UNKNOWN_END, e.batchtype);
+ this.endMsgGroup();
+ }
+}
+
+/* user away status */
+CIRCNetwork.prototype.onAway =
+function my_away(e)
+{
+ var userlist = document.getElementById("user-list");
+ for (var c in e.server.channels)
+ {
+ var chan = e.server.channels[c];
+ if (chan.active && (e.user.collectionKey in chan.users))
+ {
+ let index = chan.users[e.user.collectionKey].chanListEntry.childIndex;
+ userlist.treeBoxObject.invalidateRow(index);
+ e.server.channels[c].updateUsers([e.user.collectionKey]);
+ }
+ }
+}
+
+/* user host changed */
+CIRCNetwork.prototype.onChghost =
+function my_chghost(e)
+{
+ e.user.updateHeader();
+}
+
+CIRCNetwork.prototype.reclaimName =
+function my_reclaimname()
+{
+ var network = this;
+
+ function callback() {
+ network.reclaimName();
+ };
+
+ if ("pendingReclaimCheck" in this)
+ delete this.pendingReclaimCheck;
+
+ // Function to attempt to get back the nickname the user wants.
+ if ((this.state != NET_ONLINE) || !this.primServ)
+ return false;
+
+ if (this.primServ.me.unicodeName == this.preferredNick)
+ return false;
+
+ this.reclaimLeft -= this.RECLAIM_WAIT;
+
+ if (this.reclaimLeft <= 0)
+ return false;
+
+ this.pendingReclaimCheck = true;
+ this.INITIAL_NICK = this.preferredNick;
+ this.primServ.changeNick(this.preferredNick);
+
+ setTimeout(callback, this.RECLAIM_WAIT);
+
+ return true;
+}
+
+CIRCNetwork.prototype.doAutoPerform =
+function net_autoperform()
+{
+ if (("autoPerformSent" in this) && (this.autoPerformSent == false))
+ {
+ var cmdary = client.prefs["autoperform.network"].concat(this.prefs["autoperform"]);
+ for (var i = 0; i < cmdary.length; ++i)
+ {
+ if (cmdary[i][0] == "/")
+ this.dispatch(cmdary[i].substr(1));
+ else
+ this.dispatch(cmdary[i]);
+ }
+ this.autoPerformSent = true;
+ }
+}
+
+
+/* We want to override the base implementations. */
+CIRCChannel.prototype._join = CIRCChannel.prototype.join;
+CIRCChannel.prototype._part = CIRCChannel.prototype.part;
+
+CIRCChannel.prototype.join =
+function chan_join(key)
+{
+ var joinFailedFn = function _joinFailedFn(t)
+ {
+ delete t.joinTimer;
+ t.busy = false;
+ updateProgress();
+ }
+ if (!this.joined)
+ {
+ this.joinTimer = setTimeout(joinFailedFn, 30000, this);
+ this.busy = true;
+ updateProgress();
+ }
+ this._join(key);
+}
+
+CIRCChannel.prototype.part =
+function chan_part(reason)
+{
+ var partFailedFn = function _partFailedFn(t)
+ {
+ delete t.partTimer;
+ t.busy = false;
+ updateProgress();
+ }
+ this.partTimer = setTimeout(partFailedFn, 30000, this);
+ this.busy = true;
+ updateProgress();
+ this._part(reason);
+}
+
+client.setActivityMarker =
+CIRCNetwork.prototype.setActivityMarker =
+CIRCChannel.prototype.setActivityMarker =
+CIRCUser.prototype.setActivityMarker =
+CIRCDCCChat.prototype.setActivityMarker =
+CIRCDCCFileTransfer.prototype.setActivityMarker =
+function view_setactivitymarker(state)
+{
+ if (!client.initialized)
+ return;
+
+ // Always clear the activity marker first.
+ var markedRow = this.getActivityMarker();
+ if (markedRow)
+ {
+ markedRow.classList.remove("chatzilla-line-marker");
+ }
+
+ if (state)
+ {
+ // Mark the last row.
+ var target = this.messages.firstChild.lastChild;
+ if (!target)
+ return;
+ target.classList.add("chatzilla-line-marker");
+ }
+}
+
+client.getActivityMarker =
+CIRCNetwork.prototype.getActivityMarker =
+CIRCChannel.prototype.getActivityMarker =
+CIRCUser.prototype.getActivityMarker =
+CIRCDCCChat.prototype.getActivityMarker =
+CIRCDCCFileTransfer.prototype.getActivityMarker =
+function view_getactivitymarker()
+{
+ return this.messages.querySelector(".chatzilla-line-marker");
+}
+CIRCChannel.prototype.onInit =
+function chan_oninit ()
+{
+ this.logFile = null;
+ this.pendingNamesReply = false;
+ this.importantMessages = 0;
+}
+
+CIRCChannel.prototype.onPrivmsg =
+function my_cprivmsg (e)
+{
+ var msg = e.decodeParam(2);
+ var msgtype = "PRIVMSG";
+ if ("msgPrefix" in e)
+ msgtype += "/" + e.msgPrefix.symbol;
+
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.display(msg, msgtype, e.user, this, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+/* end of names */
+CIRCChannel.prototype.on366 =
+function my_366 (e)
+{
+ // First clear up old users:
+ var removals = new Array();
+ while (this.userList.childData.childData.length > 0)
+ {
+ var userToRemove = this.userList.childData.childData[0]._userObj;
+ this.removeFromList(userToRemove);
+ removals.push(userToRemove);
+ }
+ this.removeUsers(removals);
+
+ var entries = new Array(), updates = new Array();
+ for (var u in this.users)
+ {
+ entries.push(new UserEntry(this.users[u], this.userListShare));
+ updates.push(this.users[u]);
+ }
+ this.addUsers(updates);
+
+ this.userList.childData.appendChildren(entries);
+
+ if (this.pendingNamesReply)
+ {
+ this.parent.parent.display (e.channel.unicodeName + ": " +
+ e.params[3], "366", undefined, undefined,
+ e.tags);
+ }
+ this.pendingNamesReply = false;
+
+ // Update conference mode now we have a complete user list.
+ this._updateConferenceMode();
+}
+
+CIRCChannel.prototype.onTopic = /* user changed topic */
+CIRCChannel.prototype.on332 = /* TOPIC reply */
+function my_topic (e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ if (e.code == "TOPIC")
+ this.display (getMsg(MSG_TOPIC_CHANGED, [this.topicBy, this.topic]),
+ "TOPIC", undefined, undefined, e.tags);
+
+ if (e.code == "332")
+ {
+ if (this.topic)
+ {
+ this.display (getMsg(MSG_TOPIC,
+ [this.unicodeName, this.topic]),
+ "TOPIC", undefined, undefined, e.tags);
+ }
+ else
+ {
+ this.display(getMsg(MSG_NO_TOPIC, this.unicodeName), "TOPIC",
+ undefined, undefined, e.tags);
+ }
+ }
+
+ this.updateHeader();
+ updateTitle(this);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCChannel.prototype.on333 = /* Topic setter information */
+function my_topicinfo (e)
+{
+ this.display (getMsg(MSG_TOPIC_DATE, [this.unicodeName, this.topicBy,
+ this.topicDate]), "TOPIC",
+ undefined, undefined, e.tags);
+}
+
+CIRCChannel.prototype.on353 = /* names reply */
+function my_topic (e)
+{
+ if (this.pendingNamesReply)
+ {
+ this.parent.parent.display (e.channel.unicodeName + ": " +
+ e.params[4], "NAMES", undefined, undefined,
+ e.tags);
+ }
+}
+
+CIRCChannel.prototype.on367 = /* channel ban stuff */
+function my_bans(e)
+{
+ if ("pendingBanList" in this)
+ return;
+
+ var msg = getMsg(MSG_BANLIST_ITEM,
+ [e.user.unicodeName, e.ban, this.unicodeName, e.banTime]);
+ if (this.iAmHalfOp() || this.iAmOp())
+ msg += " " + getMsg(MSG_BANLIST_BUTTON, "mode -b " + e.ban);
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.display(msg, "BAN", undefined, undefined, e.tags);
+ client.munger.getRule(".inline-buttons").enabled = false;
+}
+
+CIRCChannel.prototype.on368 =
+function my_endofbans(e)
+{
+ if ("pendingBanList" in this)
+ return;
+
+ this.display(getMsg(MSG_BANLIST_END, this.unicodeName), "BAN", undefined,
+ undefined, e.tags);
+}
+
+CIRCChannel.prototype.on348 = /* channel except stuff */
+function my_excepts(e)
+{
+ if ("pendingExceptList" in this)
+ return;
+
+ var msg = getMsg(MSG_EXCEPTLIST_ITEM, [e.user.unicodeName, e.except,
+ this.unicodeName, e.exceptTime]);
+ if (this.iAmHalfOp() || this.iAmOp())
+ msg += " " + getMsg(MSG_EXCEPTLIST_BUTTON, "mode -e " + e.except);
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.display(msg, "EXCEPT", undefined, undefined, e.tags);
+ client.munger.getRule(".inline-buttons").enabled = false;
+}
+
+CIRCChannel.prototype.on349 =
+function my_endofexcepts(e)
+{
+ if ("pendingExceptList" in this)
+ return;
+
+ this.display(getMsg(MSG_EXCEPTLIST_END, this.unicodeName), "EXCEPT",
+ undefined, undefined, e.tags);
+}
+
+CIRCChannel.prototype.on482 =
+function my_needops(e)
+{
+ if ("pendingExceptList" in this)
+ return;
+
+ this.display(getMsg(MSG_CHANNEL_NEEDOPS, this.unicodeName), MT_ERROR,
+ undefined, undefined, e.tags);
+}
+
+CIRCChannel.prototype.onNotice =
+function my_notice (e)
+{
+ var msgtype = "NOTICE";
+ if ("msgPrefix" in e)
+ msgtype += "/" + e.msgPrefix.symbol;
+
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.display(e.decodeParam(2), msgtype, e.user, this, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCChannel.prototype.onCTCPAction =
+function my_caction (e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.display(e.CTCPData, "ACTION", e.user, this, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCChannel.prototype.onUnknownCTCP =
+function my_unkctcp (e)
+{
+ this.display (getMsg(MSG_UNKNOWN_CTCP, [e.CTCPCode, e.CTCPData,
+ e.user.unicodeName]),
+ "BAD-CTCP", e.user, this, e.tags);
+}
+
+CIRCChannel.prototype.onJoin =
+function my_cjoin (e)
+{
+ dispatch("create-tab-for-view", { view: e.channel });
+
+ if (userIsMe(e.user))
+ {
+ var params = [e.user.unicodeName, e.channel.unicodeName];
+ this.display(getMsg(MSG_YOU_JOINED, params), "JOIN",
+ e.server.me, this, e.tags);
+ /* Tell the user that conference mode is on, lest they forget (if it
+ * subsequently turns itself off, they'll get a message anyway).
+ */
+ if (this.prefs["conference.enabled"])
+ this.display(MSG_CONF_MODE_STAYON);
+ addURLToHistory(this.getURL());
+
+ if ("joinTimer" in this)
+ {
+ clearTimeout(this.joinTimer);
+ delete this.joinTimer;
+ this.busy = false;
+ updateProgress();
+ }
+
+ /* !-channels are "safe" channels, and get a server-generated prefix.
+ * For this reason, creating the channel is delayed until this point.
+ */
+ if (e.channel.unicodeName[0] == "!")
+ dispatch("set-current-view", { view: e.channel });
+
+ this.doAutoPerform();
+ }
+ else
+ {
+ if (!this.prefs["conference.enabled"])
+ {
+ this.display(getMsg(MSG_SOMEONE_JOINED,
+ [e.user.unicodeName, e.user.name, e.user.host,
+ e.channel.unicodeName]),
+ "JOIN", e.user, this, e.tags);
+ }
+
+ /* Only do this for non-me joins so us joining doesn't reset it (when
+ * we join the usercount is always 1). Also, do this after displaying
+ * the join message so we don't get cryptic effects such as a user
+ * joining causes *only* a "Conference mode enabled" message.
+ */
+ this._updateConferenceMode();
+ }
+
+ /* We don't want to add ourself here, since the names reply we'll be
+ * getting right after the join will include us as well! (FIXME)
+ */
+ if (!userIsMe(e.user))
+ {
+ this.addUsers([e.user]);
+ var entry = new UserEntry(e.user, this.userListShare);
+ this.userList.childData.appendChild(entry);
+ this.userList.childData.reSort();
+ }
+ this.updateHeader();
+}
+
+CIRCChannel.prototype.onPart =
+function my_cpart(e)
+{
+ this.removeUsers([e.user]);
+ this.updateHeader();
+
+ if (userIsMe(e.user))
+ {
+ var msg = e.reason ? MSG_YOU_LEFT_REASON : MSG_YOU_LEFT;
+ var params = [e.user.unicodeName, e.channel.unicodeName, e.reason];
+ this.display(getMsg(msg, params), "PART", e.user, this, e.tags);
+ this._clearUserList();
+
+ if ("partTimer" in this)
+ {
+ clearTimeout(this.partTimer);
+ delete this.partTimer;
+ this.busy = false;
+ updateProgress();
+ }
+
+ if (this.deleteWhenDone)
+ this.dispatch("delete-view");
+
+ delete this.deleteWhenDone;
+ }
+ else
+ {
+ /* We're ok to update this before the message, because the only thing
+ * that can happen is *disabling* of conference mode.
+ */
+ this._updateConferenceMode();
+
+ if (!this.prefs["conference.enabled"])
+ {
+ var msg = e.reason ? MSG_SOMEONE_LEFT_REASON : MSG_SOMEONE_LEFT;
+ var params = [e.user.unicodeName, e.channel.unicodeName, e.reason];
+ this.display(getMsg(msg, params), "PART", e.user, this, e.tags);
+ }
+
+ this.removeFromList(e.user);
+ }
+}
+
+CIRCChannel.prototype.onKick =
+function my_ckick (e)
+{
+ if (userIsMe (e.lamer))
+ {
+ if (e.user)
+ {
+ this.display (getMsg(MSG_YOURE_GONE,
+ [e.lamer.unicodeName, e.channel.unicodeName,
+ e.user.unicodeName, e.reason]),
+ "KICK", e.user, this, e.tags);
+ }
+ else
+ {
+ this.display (getMsg(MSG_YOURE_GONE,
+ [e.lamer.unicodeName, e.channel.unicodeName,
+ MSG_SERVER, e.reason]),
+ "KICK", (void 0), this, e.tags);
+ }
+
+ this._clearUserList();
+ /* Try 1 re-join attempt if allowed. */
+ if (this.prefs["autoRejoin"])
+ this.join(this.mode.key);
+ }
+ else
+ {
+ var enforcerProper, enforcerNick;
+ if (e.user && userIsMe(e.user))
+ {
+ enforcerProper = "YOU";
+ enforcerNick = "ME!";
+ }
+ else if (e.user)
+ {
+ enforcerProper = e.user.unicodeName;
+ enforcerNick = e.user.encodedName;
+ }
+ else
+ {
+ enforcerProper = MSG_SERVER;
+ enforcerNick = MSG_SERVER;
+ }
+
+ this.display(getMsg(MSG_SOMEONE_GONE,
+ [e.lamer.unicodeName, e.channel.unicodeName,
+ enforcerProper, e.reason]),
+ "KICK", e.user, this, e.tags);
+
+ this.removeFromList(e.lamer);
+ }
+
+ this.removeUsers([e.lamer]);
+ this.updateHeader();
+}
+
+CIRCChannel.prototype.removeFromList =
+function my_removeFromList(user)
+{
+ // Remove the user from the list and 'disconnect' the user from their entry:
+ var idx = user.chanListEntry.childIndex;
+ this.userList.childData.removeChildAtIndex(idx);
+
+ delete user.chanListEntry._userObj;
+ delete user.chanListEntry;
+}
+
+CIRCChannel.prototype.onChanMode =
+function my_cmode (e)
+{
+ if (e.code == "MODE")
+ {
+ var msg = e.decodeParam(1);
+ for (var i = 2; i < e.params.length; i++)
+ msg += " " + e.decodeParam(i);
+
+ var source = e.user ? e.user.unicodeName : e.source;
+ this.display(getMsg(MSG_MODE_CHANGED, [msg, source]),
+ "MODE", (e.user || null), this, e.tags);
+ }
+ else if ("pendingModeReply" in this)
+ {
+ var msg = e.decodeParam(3);
+ for (var i = 4; i < e.params.length; i++)
+ msg += " " + e.decodeParam(i);
+
+ var view = ("messages" in this && this.messages) ? this : e.network;
+ view.display(getMsg(MSG_MODE_ALL, [this.unicodeName, msg]), "MODE",
+ undefined, undefined, e.tags);
+ delete this.pendingModeReply;
+ }
+ var updates = new Array();
+ for (var u in e.usersAffected)
+ updates.push(e.usersAffected[u]);
+ this.updateUsers(updates);
+
+ this.updateHeader();
+ updateTitle(this);
+ if (client.currentObject == this)
+ updateUserList();
+}
+
+CIRCChannel.prototype.onNick =
+function my_cnick (e)
+{
+ if (userIsMe (e.user))
+ {
+ if (getTabForObject(this))
+ {
+ this.displayHere(getMsg(MSG_NEWNICK_YOU, e.user.unicodeName),
+ "NICK", "ME!", e.user, e.tags);
+ }
+ this.parent.parent.updateHeader();
+ }
+ else if (!this.prefs["conference.enabled"])
+ {
+ this.display(getMsg(MSG_NEWNICK_NOTYOU, [e.oldNick,
+ e.user.unicodeName]),
+ "NICK", e.user, this, e.tags);
+ }
+
+ this.updateUsers([e.user]);
+ if (client.currentObject == this)
+ updateUserList();
+}
+
+CIRCChannel.prototype.onQuit =
+function my_cquit (e)
+{
+ if (userIsMe(e.user))
+ {
+ /* I dont think this can happen */
+ var pms = [e.user.unicodeName, e.server.parent.unicodeName, e.reason];
+ this.display(getMsg(MSG_YOU_QUIT, pms),"QUIT", e.user, this, e.tags);
+ this._clearUserList();
+ }
+ else
+ {
+ // See onPart for why this is ok before the message.
+ this._updateConferenceMode();
+
+ if (!this.prefs["conference.enabled"])
+ {
+ this.display(getMsg(MSG_SOMEONE_QUIT,
+ [e.user.unicodeName,
+ e.server.parent.unicodeName, e.reason]),
+ "QUIT", e.user, this, e.tags);
+ }
+ }
+
+ this.removeUsers([e.user]);
+ this.removeFromList(e.user);
+
+ this.updateHeader();
+}
+
+CIRCChannel.prototype.doAutoPerform =
+function my_cautoperform()
+{
+ var cmdary = client.prefs["autoperform.channel"].concat(this.prefs["autoperform"]);
+ for (var i = 0; i < cmdary.length; ++i)
+ {
+ if (cmdary[i][0] == "/")
+ this.dispatch(cmdary[i].substr(1));
+ else
+ this.dispatch(cmdary[i]);
+ }
+}
+
+CIRCChannel.prototype._clearUserList =
+function _my_clearuserlist()
+{
+ if (this.userList && this.userList.childData &&
+ this.userList.childData.childData)
+ {
+ this.userList.freeze();
+ var len = this.userList.childData.childData.length;
+ while (len > 0)
+ {
+ var entry = this.userList.childData.childData[--len];
+ this.userList.childData.removeChildAtIndex(len);
+ delete entry._userObj.chanListEntry;
+ delete entry._userObj;
+ }
+ this.userList.thaw();
+ }
+}
+
+CIRCUser.prototype.onInit =
+function user_oninit ()
+{
+ this.logFile = null;
+ this.lastShownAwayMessage = "";
+}
+
+CIRCUser.prototype.onPrivmsg =
+function my_cprivmsg(e)
+{
+ var sourceObj = e.user;
+ var destObj = e.server.me;
+ var displayObj = this;
+
+ if (!("messages" in this))
+ {
+ var limit = client.prefs["newTabLimit"];
+ if (limit == 0 || client.viewsArray.length < limit)
+ {
+ if (e.user != e.server.me)
+ {
+ openQueryTab(e.server, e.user.unicodeName);
+ }
+ else
+ {
+ // This is a self-message, i.e. we received a message that
+ // looks like it came from us. Display it accordingly.
+ sourceObj = e.server.me;
+ destObj = openQueryTab(e.server, e.params[1]);
+ displayObj = destObj;
+ }
+ }
+ }
+
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ displayObj.display(e.decodeParam(2), "PRIVMSG", sourceObj, destObj, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCUser.prototype.onNick =
+function my_unick (e)
+{
+ if (userIsMe(e.user))
+ {
+ this.parent.parent.updateHeader();
+ updateTitle();
+ }
+ else if ("messages" in this && this.messages)
+ {
+ this.display(getMsg(MSG_NEWNICK_NOTYOU, [e.oldNick, e.user.unicodeName]),
+ "NICK", e.user, this, e.tags);
+ }
+
+ this.updateHeader();
+ var tab = getTabForObject(this);
+ if (tab)
+ tab.setAttribute("label", this.unicodeName);
+}
+
+CIRCUser.prototype.onNotice =
+function my_notice (e)
+{
+ var msg = e.decodeParam(2);
+ var displayMailto = client.prefs["munger.mailto"];
+
+ var ary = msg.match(/^\[([^ ]+)\]\s+/);
+ if (ary)
+ {
+ var channel = e.server.getChannel(ary[1]);
+ if (channel)
+ {
+ client.munger.getRule(".mailto").enabled = displayMailto;
+ channel.display(msg, "NOTICE", this, e.server.me, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+ return;
+ }
+ }
+
+ var sourceObj = this;
+ var destObj = e.server.me;
+ var displayObj = this;
+
+ if (e.user == e.server.me)
+ {
+ // This is a self-message, i.e. we received a message that
+ // looks like it came from us. Display it accordingly.
+ var sourceObj = e.server.me;
+ var destObj = e.server.addTarget(e.params[1]);
+ var displayObj = e.server.parent;
+ }
+
+ client.munger.getRule(".mailto").enabled = displayMailto;
+ displayObj.display(msg, "NOTICE", sourceObj, destObj, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCUser.prototype.onCTCPAction =
+function my_uaction(e)
+{
+ if (!("messages" in this))
+ {
+ var limit = client.prefs["newTabLimit"];
+ if (limit == 0 || client.viewsArray.length < limit)
+ openQueryTab(e.server, e.user.unicodeName);
+ }
+
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.display(e.CTCPData, "ACTION", this, e.server.me, e.tags);
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCUser.prototype.onUnknownCTCP =
+function my_unkctcp (e)
+{
+ this.parent.parent.display (getMsg(MSG_UNKNOWN_CTCP,
+ [e.CTCPCode, e.CTCPData,
+ e.user.unicodeName]),
+ "BAD-CTCP", this, e.server.me, e.tags);
+}
+
+function onDCCAutoAcceptTimeout(o, folder)
+{
+ // user may have already accepted or declined
+ if (o.state.state != DCC_STATE_REQUESTED)
+ return;
+
+ if (o.TYPE == "IRCDCCChat")
+ {
+ o.accept();
+ display(getMsg(MSG_DCCCHAT_ACCEPTED, o._getParams()), "DCC-CHAT");
+ }
+ else
+ {
+ var dest, leaf, tries = 0;
+ while (true)
+ {
+ leaf = escapeFileName(o.filename);
+ if (++tries > 1)
+ {
+ // A file with the same name as the offered file already exists
+ // in the user's download folder. Add [x] before the extension.
+ // The extension is the last dot to the end of the string,
+ // unless it is one of the special-cased compression extensions,
+ // in which case the second to last dot is used. The second
+ // extension can only contain letters, to avoid mistakes like
+ // "patch-version1[2].0.gz". If no file extension is present,
+ // the [x] is just appended to the filename.
+ leaf = leaf.replace(/(\.[a-z]*\.(gz|bz2|z)|\.[^\.]*|)$/i,
+ "[" + tries + "]$&");
+ }
+
+ dest = getFileFromURLSpec(folder);
+ dest.append(leaf);
+ if (!dest.exists())
+ break;
+ }
+ o.accept(dest);
+ display(getMsg(MSG_DCCFILE_ACCEPTED, o._getParams()), "DCC-FILE");
+ }
+}
+
+CIRCUser.prototype.onDCCChat =
+function my_dccchat(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return;
+
+ var u = client.dcc.addUser(e.user, e.host);
+ var c = client.dcc.addChat(u, e.port);
+
+ var str = MSG_DCCCHAT_GOT_REQUEST;
+ var cmds = getMsg(MSG_DCC_COMMAND_ACCEPT, "dcc-accept " + c.id) + " " +
+ getMsg(MSG_DCC_COMMAND_DECLINE, "dcc-decline " + c.id);
+
+ var allowList = this.parent.parent.prefs["dcc.autoAccept.list"];
+ for (var m = 0; m < allowList.length; ++m)
+ {
+ if (hostmaskMatches(e.user, getHostmaskParts(allowList[m])))
+ {
+ var acceptDelay = client.prefs["dcc.autoAccept.delay"];
+ if (acceptDelay == 0)
+ {
+ str = MSG_DCCCHAT_ACCEPTING_NOW;
+ }
+ else
+ {
+ str = MSG_DCCCHAT_ACCEPTING;
+ cmds = [(acceptDelay / 1000), cmds];
+ }
+ setTimeout(onDCCAutoAcceptTimeout, acceptDelay, c);
+ break;
+ }
+ }
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.parent.parent.display(getMsg(str, c._getParams().concat(cmds)),
+ "DCC-CHAT", undefined, undefined, e.tags);
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ // Pass the event over to the DCC Chat object.
+ e.set = "dcc-chat";
+ e.destObject = c;
+ e.destMethod = "onGotRequest";
+}
+
+CIRCUser.prototype.onDCCSend =
+function my_dccsend(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return;
+
+ var u = client.dcc.addUser(e.user, e.host);
+ var f = client.dcc.addFileTransfer(u, e.port, e.file, e.size);
+
+ var str = MSG_DCCFILE_GOT_REQUEST;
+ var cmds = getMsg(MSG_DCC_COMMAND_ACCEPT, "dcc-accept " + f.id) + " " +
+ getMsg(MSG_DCC_COMMAND_DECLINE, "dcc-decline " + f.id);
+
+ var allowList = this.parent.parent.prefs["dcc.autoAccept.list"];
+ for (var m = 0; m < allowList.length; ++m)
+ {
+ if (hostmaskMatches(e.user, getHostmaskParts(allowList[m]),
+ this.parent))
+ {
+ var acceptDelay = client.prefs["dcc.autoAccept.delay"];
+ if (acceptDelay == 0)
+ {
+ str = MSG_DCCFILE_ACCEPTING_NOW;
+ }
+ else
+ {
+ str = MSG_DCCFILE_ACCEPTING;
+ cmds = [(acceptDelay / 1000), cmds];
+ }
+ setTimeout(onDCCAutoAcceptTimeout, acceptDelay,
+ f, this.parent.parent.prefs["dcc.downloadsFolder"]);
+ break;
+ }
+ }
+
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.parent.parent.display(getMsg(str,[e.user.unicodeName,
+ e.host, e.port, e.file,
+ getSISize(e.size)].concat(cmds)),
+ "DCC-FILE", undefined, undefined, e.tags);
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ // Pass the event over to the DCC File object.
+ e.set = "dcc-file";
+ e.destObject = f;
+ e.destMethod = "onGotRequest";
+}
+
+CIRCUser.prototype.onDCCReject =
+function my_dccreject(e)
+{
+ if (!client.prefs["dcc.enabled"])
+ return;
+
+ //FIXME: Uh... cope. //
+
+ // Pass the event over to the DCC Chat object.
+ //e.set = "dcc-file";
+ //e.destObject = f;
+ //e.destMethod = "onGotReject";
+}
+
+CIRCUser.prototype.doAutoPerform =
+function my_autoperform()
+{
+ var cmdary = client.prefs["autoperform.user"].concat(this.prefs["autoperform"]);
+ for (var i = 0; i < cmdary.length; ++i)
+ {
+ if (cmdary[i][0] == "/")
+ this.dispatch(cmdary[i].substr(1));
+ else
+ this.dispatch(cmdary[i]);
+ }
+}
+
+CIRCDCCChat.prototype.onInit =
+function my_dccinit(e)
+{
+}
+
+CIRCDCCChat.prototype._getParams =
+function my_dccgetparams()
+{
+ return [this.unicodeName, this.remoteIP, this.port];
+}
+
+CIRCDCCChat.prototype.onPrivmsg =
+function my_dccprivmsg(e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.displayHere(toUnicode(e.line, this), "PRIVMSG", e.user, "ME!");
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCDCCChat.prototype.onCTCPAction =
+function my_uaction(e)
+{
+ client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"];
+ this.displayHere(e.CTCPData, "ACTION", e.user, "ME!");
+ client.munger.getRule(".mailto").enabled = false;
+}
+
+CIRCDCCChat.prototype.onUnknownCTCP =
+function my_unkctcp(e)
+{
+ this.displayHere(getMsg(MSG_UNKNOWN_CTCP, [e.CTCPCode, e.CTCPData,
+ e.user.unicodeName]),
+ "BAD-CTCP", e.user, "ME!");
+}
+
+CIRCDCCChat.prototype.onConnect =
+function my_dccconnect(e)
+{
+ playEventSounds("dccchat", "connect");
+ this.displayHere(getMsg(MSG_DCCCHAT_OPENED, this._getParams()), "DCC-CHAT");
+}
+
+CIRCDCCChat.prototype.onAbort =
+function my_dccabort(e)
+{
+ this.display(getMsg(MSG_DCCCHAT_ABORTED, this._getParams()), "DCC-CHAT");
+}
+
+CIRCDCCChat.prototype.onFail =
+function my_dccfail(e)
+{
+ this.display(getMsg(MSG_DCCCHAT_FAILED, this._getParams()), "DCC-CHAT");
+}
+
+CIRCDCCChat.prototype.onDisconnect =
+function my_dccdisconnect(e)
+{
+ playEventSounds("dccchat", "disconnect");
+ this.display(getMsg(MSG_DCCCHAT_CLOSED, this._getParams()), "DCC-CHAT");
+}
+
+
+CIRCDCCFileTransfer.prototype.onInit =
+function my_dccfileinit(e)
+{
+ this.busy = false;
+ updateProgress();
+}
+
+CIRCDCCFileTransfer.prototype._getParams =
+function my_dccfilegetparams()
+{
+ var dir = MSG_UNKNOWN;
+
+ if (this.state.dir == DCC_DIR_GETTING)
+ dir = MSG_DCCLIST_FROM;
+
+ if (this.state.dir == DCC_DIR_SENDING)
+ dir = MSG_DCCLIST_TO;
+
+ return [this.filename, dir, this.unicodeName,
+ this.remoteIP, this.port];
+}
+
+CIRCDCCFileTransfer.prototype.onConnect =
+function my_dccfileconnect(e)
+{
+ this.displayHere(getMsg(MSG_DCCFILE_OPENED, this._getParams()), "DCC-FILE");
+ this.busy = true;
+ this.speed = 0;
+ updateProgress();
+ this._lastUpdate = new Date();
+ this._lastPosition = 0;
+ this._lastSpeedTime = new Date();
+}
+
+CIRCDCCFileTransfer.prototype.onProgress =
+function my_dccfileprogress(e)
+{
+ var now = new Date();
+ var pcent = this.progress;
+
+ var tab = getTabForObject(this);
+
+ // If we've moved 100KiB or waited 10s, update the progress bar.
+ if ((this.position > this._lastPosition + 102400) ||
+ (now - this._lastUpdate > 10000))
+ {
+ updateProgress();
+ updateTitle();
+
+ if (tab)
+ tab.setAttribute("label", this.viewName + " (" + pcent + "%)");
+
+ var change = (this.position - this._lastPosition);
+ var speed = change / ((now - this._lastSpeedTime) / 1000); // B/s
+ this._lastSpeedTime = now;
+
+ /* Use an average of the last speed, and this speed, so we get a little
+ * smoothing to it.
+ */
+ this.speed = (this.speed + speed) / 2;
+ this.updateHeader();
+ this._lastPosition = this.position;
+ }
+
+ // If it's also been 10s or more since we last displayed a msg...
+ if (now - this._lastUpdate > 10000)
+ {
+ this._lastUpdate = now;
+
+ var args = [pcent, getSISize(this.position), getSISize(this.size),
+ getSISpeed(this.speed)];
+
+ // We supress this message if the view is hidden.
+ if (tab)
+ this.displayHere(getMsg(MSG_DCCFILE_PROGRESS, args), "DCC-FILE");
+ }
+}
+
+CIRCDCCFileTransfer.prototype.onAbort =
+function my_dccfileabort(e)
+{
+ this.busy = false;
+ updateProgress();
+ updateTitle();
+ this.display(getMsg(MSG_DCCFILE_ABORTED, this._getParams()), "DCC-FILE");
+}
+
+CIRCDCCFileTransfer.prototype.onFail =
+function my_dccfilefail(e)
+{
+ this.busy = false;
+ updateProgress();
+ updateTitle();
+ this.display(getMsg(MSG_DCCFILE_FAILED, this._getParams()), "DCC-FILE");
+}
+
+CIRCDCCFileTransfer.prototype.onDisconnect =
+function my_dccfiledisconnect(e)
+{
+ this.busy = false;
+ updateProgress();
+ this.updateHeader();
+ updateTitle();
+
+ var msg, tab = getTabForObject(this);
+ if (tab)
+ tab.setAttribute("label", this.viewName + " (DONE)");
+
+ if (this.state.dir == DCC_DIR_GETTING)
+ {
+ var localURL = getURLSpecFromFile(this.localPath);
+ var cmd = "dcc-show-file " + localURL;
+ var msgId = (client.platform == "Mac") ? MSG_DCCFILE_CLOSED_SAVED_MAC :
+ MSG_DCCFILE_CLOSED_SAVED;
+ msg = getMsg(msgId, this._getParams().concat(localURL, cmd));
+ }
+ else
+ {
+ msg = getMsg(MSG_DCCFILE_CLOSED_SENT, this._getParams());
+ }
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.display(msg, "DCC-FILE");
+ client.munger.getRule(".inline-buttons").enabled = false;
+}
+
+var CopyPasteHandler = new Object();
+
+CopyPasteHandler.allowDrop =
+CopyPasteHandler.allowStartDrag =
+CopyPasteHandler.onCopyOrDrag =
+function phand_bogus()
+{
+ return true;
+}
+
+CopyPasteHandler.onPasteOrDrop =
+function phand_onpaste(e, data)
+{
+ // XXXbug 329487: The effect of onPasteOrDrop's return value is actually the
+ // exact opposite of the definition in the IDL.
+
+ // Don't mess with the multiline box at all.
+ if (client.prefs["multiline"])
+ return true;
+
+ var str = new Object();
+ var strlen = new Object();
+ data.getTransferData("text/unicode", str, strlen);
+ str.value.QueryInterface(Components.interfaces.nsISupportsString);
+ str.value.data = str.value.data.replace(/(^\s*[\r\n]+|[\r\n]+\s*$)/g, "");
+
+ // XXX part of what follows is a very ugly hack to make links (with a title)
+ // not open the multiline box. We 'should' be able to ask the transferable
+ // what flavours it supports, but testing showed that by the time we can ask
+ // for that info, it's forgotten about everything apart from text/unicode.
+ var lines = str.value.data.split("\n");
+ var m = lines[0].match(client.linkRE);
+
+ if ((str.value.data.indexOf("\n") == -1) ||
+ (m && (m[0] == lines[0]) && (lines.length == 2)))
+ {
+ // If, after stripping leading/trailing empty lines, the string is a
+ // single line, or it's a link with a title, put it back in
+ // the transferable and return.
+ data.setTransferData("text/unicode", str.value,
+ str.value.data.length * 2);
+ return true;
+ }
+
+ // If it's a drop, move the text cursor to the mouse position.
+ if (e && ("rangeOffset" in e))
+ client.input.setSelectionRange(e.rangeOffset, e.rangeOffset);
+
+ str = client.input.value.substr(0, client.input.selectionStart) +
+ str.value.data + client.input.value.substr(client.input.selectionEnd);
+ client.prefs["multiline"] = true;
+ // We want to auto-collapse after send, so the user is not thrown off by the
+ // "strange" input box if they didn't specifically ask for it:
+ client.multiLineForPaste = true;
+ client.input.value = str;
+ return false;
+}
+
+CopyPasteHandler.QueryInterface =
+function phand_qi(iid)
+{
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsIClipboardDragDropHooks))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+}
+
+function UserEntry(userObj, channelListShare)
+{
+ var self = this;
+ function getUName()
+ {
+ return userObj.unicodeName;
+ };
+ function getSortFn()
+ {
+ if (client.prefs["sortUsersByMode"])
+ return ule_sortByMode;
+ return ule_sortByName;
+ };
+
+ // This object is used to represent a user in the userlist. To work with our
+ // JS tree view, it needs a bunch of stuff that is set through the
+ // constructor and the prototype (see also a couple of lines down). Here we
+ // call the original constructor to do some work for us:
+ XULTreeViewRecord.call(this, channelListShare);
+
+ // This magic function means the unicodeName is used for display:
+ this.setColumnPropertyName("usercol", getUName);
+
+ // We need this for sorting by mode (op, hop, voice, etc.)
+ this._userObj = userObj;
+
+ // When the user leaves, we need to have the entry so we can remove it:
+ userObj.chanListEntry = this;
+
+ // Gross hack: we set up the sort function by getter so we don't have to go
+ // back (array sort -> xpc -> our pref lib -> xpc -> pref interfaces) for
+ // every bloody compare. Now it will be a function that doesn't need prefs
+ // after being retrieved, which is much much faster.
+ this.__defineGetter__("sortCompare", getSortFn);
+}
+
+// See explanation in the constructor.
+UserEntry.prototype = XULTreeViewRecord.prototype;
+
+function ule_sortByName(a, b)
+{
+ if (a._userObj.unicodeName == b._userObj.unicodeName)
+ return 0;
+ var aName = a._userObj.unicodeName.toLowerCase();
+ var bName = b._userObj.unicodeName.toLowerCase();
+ return (aName < bName ? -1 : 1);
+}
+
+function ule_sortByMode(a, b)
+{
+ if (a._userObj.sortName == b._userObj.sortName)
+ return 0;
+ var aName = a._userObj.sortName.toLowerCase();
+ var bName = b._userObj.sortName.toLowerCase();
+ return (aName < bName ? -1 : 1);
+}
diff --git a/comm/suite/chatzilla/xul/content/install-plugin/install-plugin.js b/comm/suite/chatzilla/xul/content/install-plugin/install-plugin.js
new file mode 100644
index 0000000000..3116d9a2f3
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/install-plugin/install-plugin.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 client;
+var plugin;
+
+function onLoad()
+{
+ client = window.arguments[0];
+ client.installPluginDialog = window;
+ window.getMsg = client.messageManager.getMsg;
+ window.MSG_ALERT = client.mainWindow.MSG_ALERT;
+
+ hookEvent("chk-name-auto", "command", changeAutoName);
+ hookEvent("txt-source", "input", sourceChange);
+ hookEvent("btn-browse", "command", browseForSource);
+
+ // Center on CZ:
+ var ow = client.mainWindow;
+ window.sizeToContent();
+ window.moveTo(ow.screenX + Math.max((ow.outerWidth - window.outerWidth ) / 2, 0),
+ ow.screenY + Math.max((ow.outerHeight - window.outerHeight) / 2, 0));
+}
+
+function changeAutoName(event)
+{
+ var useAutoName = document.getElementById("chk-name-auto");
+ var pluginName = document.getElementById("txt-name");
+ if (useAutoName.checked)
+ {
+ pluginName.setAttribute("disabled", "true");
+ sourceChange(null);
+ }
+ else
+ {
+ pluginName.removeAttribute("disabled");
+ }
+}
+
+function sourceChange(event)
+{
+ var useAutoName = document.getElementById("chk-name-auto");
+ var pluginName = document.getElementById("txt-name");
+ var sourceLoc = document.getElementById("txt-source");
+
+ if (useAutoName.checked)
+ {
+ var ary = sourceLoc.value.match(/([^\/]+?)(\..{0,3}){0,2}$/);
+ pluginName.value = (ary ? ary[1] : sourceLoc.value);
+ }
+}
+
+function browseForSource(event)
+{
+ var rv = pickOpen(client.mainWindow.MSG_INSTALL_PLUGIN_SELECT_SOURCE,
+ "*.js;*.zip;*.jar");
+
+ if (("file" in rv) && rv.file)
+ {
+ rv.path = rv.file.path;
+ rv.spec = rv.picker.fileURL.spec;
+ }
+
+ if (rv.reason == 0)
+ {
+ var sourceLoc = document.getElementById("txt-source");
+ sourceLoc.value = rv.spec;
+ sourceChange(null);
+ }
+}
+
+function doOK()
+{
+ var pluginName = document.getElementById("txt-name");
+ var pluginSource = document.getElementById("txt-source");
+ if (!pluginName.value)
+ {
+ alert(client.mainWindow.MSG_INSTALL_PLUGIN_ERR_SPEC_NAME);
+ return false;
+ }
+
+ client.dispatch("install-plugin", {name: pluginName.value,
+ url: pluginSource.value});
+ delete client.installPluginDialog;
+}
+
+function doCancel()
+{
+ delete client.installPluginDialog;
+}
+
+function hookEvent(id, event, handler)
+{
+ var item = document.getElementById(id);
+ item.addEventListener(event, handler, false);
+}
diff --git a/comm/suite/chatzilla/xul/content/install-plugin/install-plugin.xul b/comm/suite/chatzilla/xul/content/install-plugin/install-plugin.xul
new file mode 100644
index 0000000000..90de4f6105
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/install-plugin/install-plugin.xul
@@ -0,0 +1,43 @@
+<?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 window SYSTEM "chrome://chatzilla/locale/install-plugin.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/skin/install-plugin.css" type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml" onload="onLoad();"
+ title="&windowtitle;"
+ ondialogaccept="return doOK()"
+ ondialogcancel="doCancel()">
+ <script src="install-plugin.js"/>
+ <script src="../lib/js/file-utils.js"/>
+ <script src="../lib/js/utils.js"/>
+ <grid>
+ <columns><column/><column flex="1"/></columns>
+ <rows>
+ <row align="center">
+ <label id="lbl-source" accesskey="&source.accesskey;"
+ control="txt-source" value="&source.label;"/>
+ <textbox id="txt-source"/>
+ <button id="btn-browse" label="&browse.label;"
+ accesskey="&browse.accesskey;"/>
+ </row>
+ <row align="center">
+ <label id="lbl-name" control="txt-name"
+ value="&name.label;" accesskey="&name.accesskey;"/>
+ <textbox id="txt-name" disabled="true"/>
+ </row>
+ <row align="center">
+ <spacer/>
+ <checkbox id="chk-name-auto" checked="true"
+ label="&name.autopick.label;"
+ accesskey="&name.autopick.accesskey;"/>
+ </row>
+ </rows>
+ </grid>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/menus.js b/comm/suite/chatzilla/xul/content/menus.js
new file mode 100644
index 0000000000..1fecac268d
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/menus.js
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; 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/. */
+
+function initMenus()
+{
+ function isMotif(name)
+ {
+ return "client.prefs['motif.current'] == " +
+ "client.prefs['motif." + name + "']";
+ };
+
+ function isFontFamily(name)
+ {
+ return "cx.sourceObject.prefs['font.family'] == '" + name + "'";
+ };
+
+ function isFontFamilyCustom()
+ {
+ return "!cx.sourceObject.prefs['font.family']." +
+ "match(/^(default|(sans-)?serif|monospace)$/)";
+ };
+
+ function isFontSize(size)
+ {
+ return "cx.fontSize == cx.fontSizeDefault + " + size;
+ };
+
+ function isFontSizeCustom()
+ {
+ // It's "custom" if it's set (non-zero/not default), not the default
+ // size (medium) and not +/-2 (small/large).
+ return "'fontSize' in cx && cx.fontSize != 0 && " +
+ "cx.fontSizeDefault != cx.fontSize && " +
+ "Math.abs((cx.fontSizeDefault - cx.fontSize) / 2) != 1";
+ };
+
+ function onMenuCommand(event, window)
+ {
+ var commandName = event.originalTarget.getAttribute("commandname");
+ var params = new Object();
+ if ("cx" in client.menuManager && client.menuManager.cx)
+ params = client.menuManager.cx;
+ params.sourceWindow = window;
+ params.source = "menu";
+ params.shiftKey = event.shiftKey;
+
+ dispatch(commandName, params, true);
+
+ delete client.menuManager.cx;
+ };
+
+ client.onMenuCommand = onMenuCommand;
+ client.menuSpecs = new Object();
+ var menuManager = new MenuManager(client.commandManager,
+ client.menuSpecs,
+ getCommandContext,
+ "client.onMenuCommand(event, window);");
+ client.menuManager = menuManager;
+
+ client.menuSpecs["maintoolbar"] = {
+ items:
+ [
+ ["disconnect"],
+ ["quit"],
+ ["part"]
+ ]
+ };
+
+ // OS values
+ var Win = "(client.platform == 'Windows')";
+ var NotWin = "(client.platform != 'Windows')";
+ var Linux = "(client.platform == 'Linux')";
+ var NotLinux = "(client.platform != 'Linux')";
+ var Mac = "(client.platform == 'Mac')";
+ var NotMac = "(client.platform != 'Mac')";
+
+ // IRC specific values
+ var ViewClient = "(cx.TYPE == 'IRCClient')";
+ var ViewNetwork = "(cx.TYPE == 'IRCNetwork')";
+ var ViewChannel = "(cx.TYPE == 'IRCChannel')";
+ var ViewUser = "(cx.TYPE == 'IRCUser')";
+ var ViewDCC = "(cx.TYPE.substr(0, 6) == 'IRCDCC')";
+
+ // IRC specific combinations
+ var ChannelActive = "(" + ViewChannel + " and cx.channel.active)";
+ var ChannelInactive = "(" + ViewChannel + " and !cx.channel.active)";
+ var DCCActive = "(" + ViewDCC + " and cx.sourceObject.isActive())";
+ var NetConnected = "(cx.network and cx.network.isConnected())";
+ var NetDisconnected = "(cx.network and !cx.network.isConnected())";
+
+ client.menuSpecs["mainmenu:chatzilla"] = {
+ label: MSG_MNU_CHATZILLA,
+ accesskey: getAccessKeyForMenu('MSG_MNU_CHATZILLA'),
+ getContext: getDefaultContext,
+ items:
+ [
+ ["cmd-prefs"],
+ ["install-plugin"],
+ ["goto-startup"],
+ ["-"],
+ ["print"],
+ ["save"],
+ ["-", {visibleif: NotMac}],
+ ["exit", {visibleif: Win}],
+ ["quit", {visibleif: NotMac + " and " + NotWin}]
+ ]
+ };
+
+ client.menuSpecs["mainmenu:irc"] = {
+ label: MSG_MNU_IRC,
+ accesskey: getAccessKeyForMenu('MSG_MNU_IRC'),
+ getContext: getDefaultContext,
+ items:
+ [
+ ["join"],
+ ["-"],
+ ["edit-networks"],
+ ["-"],
+ [">popup:views"],
+ [">popup:nickname"],
+ ["-"],
+ ["clear-view"],
+ ["hide-view", {enabledif: "client.viewsArray.length > 1"}],
+ ["toggle-oas",
+ {type: "checkbox",
+ checkedif: "isStartupURL(cx.sourceObject.getURL())"}],
+ ["-"],
+ ["leave", {visibleif: ChannelActive}],
+ ["rejoin", {visibleif: ChannelInactive}],
+ ["dcc-close", {visibleif: DCCActive}],
+ ["delete-view", {visibleif: "!" + ChannelActive + " and !" + DCCActive}],
+ ["disconnect", {visibleif: NetConnected}],
+ ["reconnect", {visibleif: NetDisconnected}],
+ ["-"],
+ ["toggle-text-dir"]
+ ]
+ };
+
+ client.menuSpecs["popup:views"] = {
+ label: MSG_MNU_VIEWS,
+ accesskey: getAccessKeyForMenu('MSG_MNU_VIEWS'),
+ getContext: getViewsContext,
+ items:
+ [
+ ["goto-url", {type: "radio",
+ checkedif: "cx.url == cx.sourceObject.getURL()",
+ repeatfor: "cx.views",
+ repeatgroup: "item.group",
+ repeatmap: "cx.url = item.url; cx.label = item.label"}]
+ ]
+ };
+
+ client.menuSpecs["mainmenu:edit"] = {
+ label: MSG_MNU_EDIT,
+ accesskey: getAccessKeyForMenu('MSG_MNU_EDIT'),
+ getContext: getDefaultContext,
+ items:
+ [
+ ["cmd-undo", {enabledif: "getCommandEnabled('cmd_undo')"}],
+ ["cmd-redo", {enabledif: "getCommandEnabled('cmd_redo')"}],
+ ["-"],
+ ["cmd-cut", {enabledif: "getCommandEnabled('cmd_cut')"}],
+ ["cmd-copy", {enabledif: "getCommandEnabled('cmd_copy')"}],
+ ["cmd-paste", {enabledif: "getCommandEnabled('cmd_paste')"}],
+ ["cmd-delete", {enabledif: "getCommandEnabled('cmd_delete')"}],
+ ["-"],
+ ["cmd-selectall", {enabledif: "getCommandEnabled('cmd_selectAll')"}],
+ ["-"],
+ ["find"],
+ ["find-again", {enabledif: "canFindAgainInPage()"}],
+ ["-"],
+ ["cmd-mozilla-prefs"]
+ ]
+ };
+
+ client.menuSpecs["popup:motifs"] = {
+ label: MSG_MNU_MOTIFS,
+ accesskey: getAccessKeyForMenu('MSG_MNU_MOTIFS'),
+ items:
+ [
+ ["motif-dark",
+ {type: "checkbox",
+ checkedif: isMotif("dark")}],
+ ["motif-light",
+ {type: "checkbox",
+ checkedif: isMotif("light")}],
+ ]
+ };
+
+ client.menuSpecs["mainmenu:view"] = {
+ label: MSG_MNU_VIEW,
+ accesskey: getAccessKeyForMenu('MSG_MNU_VIEW'),
+ getContext: getDefaultContext,
+ items:
+ [
+ ["tabstrip",
+ {type: "checkbox",
+ checkedif: "isVisible('view-tabs')"}],
+ ["header",
+ {type: "checkbox",
+ checkedif: "cx.sourceObject.prefs['displayHeader']"}],
+ ["userlist",
+ {type: "checkbox",
+ checkedif: "isVisible('user-list-box')"}],
+ ["statusbar",
+ {type: "checkbox",
+ checkedif: "isVisible('status-bar')"}],
+ ["-"],
+ [">popup:motifs"],
+ [">popup:fonts"],
+ ["-"],
+ ["toggle-ccm",
+ {type: "checkbox",
+ checkedif: "client.prefs['collapseMsgs']"}],
+ ["toggle-copy",
+ {type: "checkbox",
+ checkedif: "client.prefs['copyMessages']"}],
+ ["toggle-timestamps",
+ {type: "checkbox",
+ checkedif: "cx.sourceObject.prefs['timestamps']"}]
+ ]
+ };
+
+ /* Mac expects a help menu with this ID, and there is nothing we can do
+ * about it. */
+ client.menuSpecs["mainmenu:help"] = {
+ label: MSG_MNU_HELP,
+ accesskey: getAccessKeyForMenu('MSG_MNU_HELP'),
+ domID: "menu_Help",
+ items:
+ [
+ ["-"],
+ ["homepage"],
+ ["faq"],
+ ["-"],
+ ["about", {id: "aboutName"}]
+ ]
+ };
+
+ client.menuSpecs["popup:fonts"] = {
+ label: MSG_MNU_FONTS,
+ accesskey: getAccessKeyForMenu('MSG_MNU_FONTS'),
+ getContext: getFontContext,
+ items:
+ [
+ ["font-size-bigger", {}],
+ ["font-size-smaller", {}],
+ ["-"],
+ ["font-size-default",
+ {type: "checkbox", checkedif: "!cx.fontSize"}],
+ ["font-size-small",
+ {type: "checkbox", checkedif: isFontSize(-2)}],
+ ["font-size-medium",
+ {type: "checkbox", checkedif: isFontSize(0)}],
+ ["font-size-large",
+ {type: "checkbox", checkedif: isFontSize(+2)}],
+ ["font-size-other",
+ {type: "checkbox", checkedif: isFontSizeCustom()}],
+ ["-"],
+ ["font-family-default",
+ {type: "checkbox", checkedif: isFontFamily("default")}],
+ ["font-family-serif",
+ {type: "checkbox", checkedif: isFontFamily("serif")}],
+ ["font-family-sans-serif",
+ {type: "checkbox", checkedif: isFontFamily("sans-serif")}],
+ ["font-family-monospace",
+ {type: "checkbox", checkedif: isFontFamily("monospace")}],
+ ["font-family-other",
+ {type: "checkbox", checkedif: isFontFamilyCustom()}]
+ ]
+ };
+
+ // Me is op.
+ var isop = "(cx.channel.iAmOp()) && ";
+ // Me is op or half-op.
+ var isopish = "(cx.channel.iAmOp() || cx.channel.iAmHalfOp()) && ";
+ // Server has half-ops.
+ var shop = "(cx.server.supports.prefix.indexOf('h') > 0) && ";
+ // User is Me or Me is op.
+ var isoporme = "((cx.user == cx.server.me) || cx.channel.iAmOp()) && ";
+
+ client.menuSpecs["popup:opcommands"] = {
+ label: MSG_MNU_OPCOMMANDS,
+ accesskey: getAccessKeyForMenu('MSG_MNU_OPCOMMANDS'),
+ items:
+ [
+ ["op", {visibleif: isop + "!cx.user.isOp"}],
+ ["deop", {visibleif: isop + "cx.user.isOp"}],
+ ["hop", {visibleif: isop + "!cx.user.isHalfOp"}],
+ ["dehop", {visibleif: isoporme + "cx.user.isHalfOp"}],
+ ["voice", {visibleif: isopish + "!cx.user.isVoice"}],
+ ["devoice", {visibleif: isopish + "cx.user.isVoice"}],
+ ["-"],
+ ["ban", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}],
+ ["unban", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}],
+ ["kick", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}],
+ ["kick-ban", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}]
+ ]
+ };
+
+
+ client.menuSpecs["popup:usercommands"] = {
+ label: MSG_MNU_USERCOMMANDS,
+ accesskey: getAccessKeyForMenu('MSG_MNU_USERCOMMANDS'),
+ items:
+ [
+ ["query", {visibleif: "cx.channel && cx.user"}],
+ ["whois", {visibleif: "cx.user"}],
+ ["whowas", {visibleif: "cx.nickname && !cx.user"}],
+ ["ping", {visibleif: "cx.user"}],
+ ["time", {visibleif: "cx.user"}],
+ ["version", {visibleif: "cx.user"}],
+ ["-", {visibleif: "cx.user"}],
+ ["dcc-chat", {visibleif: "cx.user"}],
+ ["dcc-send", {visibleif: "cx.user"}],
+ ]
+ };
+
+
+ client.menuSpecs["context:userlist"] = {
+ getContext: getUserlistContext,
+ items:
+ [
+ ["toggle-usort", {type: "checkbox",
+ checkedif: "client.prefs['sortUsersByMode']"}],
+ ["toggle-umode", {type: "checkbox",
+ checkedif: "client.prefs['showModeSymbols']"}],
+ ["-", {visibleif: "cx.nickname"}],
+ ["label-user", {visibleif: "cx.nickname && (cx.userCount == 1)",
+ header: true}],
+ ["label-user-multi", {visibleif: "cx.nickname && (cx.userCount != 1)",
+ header: true}],
+ [">popup:opcommands", {visibleif: "cx.nickname",
+ enabledif: isopish + "true"}],
+ [">popup:usercommands", {visibleif: "cx.nickname",
+ enabledif: "cx.userCount == 1"}],
+ ]
+ };
+
+ var urlenabled = "has('url')";
+ var urlexternal = "has('url') && cx.url.search(/^ircs?:/i) == -1";
+ var textselected = "getCommandEnabled('cmd_copy')";
+
+ client.menuSpecs["context:messages"] = {
+ getContext: getMessagesContext,
+ items:
+ [
+ ["goto-url", {visibleif: urlenabled}],
+ ["goto-url-newwin", {visibleif: urlexternal}],
+ ["goto-url-newtab", {visibleif: urlexternal}],
+ ["cmd-copy-link-url", {visibleif: urlenabled}],
+ ["cmd-copy", {visibleif: "!" + urlenabled, enabledif: textselected }],
+ ["cmd-selectall", {visibleif: "!" + urlenabled }],
+ ["websearch", {visibleif: textselected}],
+ ["-", {visibleif: "cx.nickname"}],
+ ["label-user", {visibleif: "cx.nickname", header: true}],
+ [">popup:opcommands", {visibleif: "cx.channel && cx.nickname",
+ enabledif: isopish + "cx.user"}],
+ [">popup:usercommands", {visibleif: "cx.nickname"}],
+ ["-"],
+ ["clear-view"],
+ ["hide-view", {enabledif: "client.viewsArray.length > 1"}],
+ ["toggle-oas",
+ {type: "checkbox",
+ checkedif: "isStartupURL(cx.sourceObject.getURL())"}],
+ ["-"],
+ ["leave", {visibleif: ChannelActive}],
+ ["rejoin", {visibleif: ChannelInactive}],
+ ["dcc-close", {visibleif: DCCActive}],
+ ["delete-view", {visibleif: "!" + ChannelActive + " and !" + DCCActive}],
+ ["disconnect", {visibleif: NetConnected}],
+ ["reconnect", {visibleif: NetDisconnected}],
+ ["-"],
+ ["toggle-text-dir"]
+ ]
+ };
+
+ client.menuSpecs["context:tab"] = {
+ getContext: getTabContext,
+ items:
+ [
+ ["clear-view"],
+ ["hide-view", {enabledif: "client.viewsArray.length > 1"}],
+ ["toggle-oas",
+ {type: "checkbox",
+ checkedif: "isStartupURL(cx.sourceObject.getURL())"}],
+ ["-"],
+ ["leave", {visibleif: ChannelActive}],
+ ["rejoin", {visibleif: ChannelInactive}],
+ ["dcc-close", {visibleif: DCCActive}],
+ ["delete-view", {visibleif: "!" + ChannelActive + " and !" + DCCActive}],
+ ["disconnect", {visibleif: NetConnected}],
+ ["reconnect", {visibleif: NetDisconnected}],
+ ["-"],
+ ["rename"],
+ ["-"],
+ ["toggle-text-dir"]
+ ]
+ };
+
+ client.menuSpecs["context:edit"] = {
+ getContext: getDefaultContext,
+ items:
+ [
+ ["cmd-undo", {enabledif: "getCommandEnabled('cmd_undo')"}],
+ ["-"],
+ ["cmd-cut", {enabledif: "getCommandEnabled('cmd_cut')"}],
+ ["cmd-copy", {enabledif: "getCommandEnabled('cmd_copy')"}],
+ ["cmd-paste", {enabledif: "getCommandEnabled('cmd_paste')"}],
+ ["cmd-delete", {enabledif: "getCommandEnabled('cmd_delete')"}],
+ ["-"],
+ ["cmd-selectall", {enabledif: "getCommandEnabled('cmd_selectAll')"}]
+ ]
+ }
+
+ // Gross hacks to figure out if we're away:
+ var netAway = "cx.network.prefs['away']";
+ var cliAway = "client.prefs['away']";
+ var awayCheckNet = "(cx.network and (" + netAway + " == item.message))";
+ var awayCheckCli = "(!cx.network and (" + cliAway + " == item.message))";
+ var awayChecked = awayCheckNet + " or " + awayCheckCli;
+ var areBack = "(cx.network and !" + netAway + ") or " +
+ "(!cx.network and !" + cliAway + ")";
+
+ client.menuSpecs["mainmenu:nickname"] = {
+ label: client.prefs["nickname"],
+ domID: "server-nick",
+ getContext: getDefaultContext,
+ items:
+ [
+ ["nick"],
+ ["-"],
+ ["back", {type: "checkbox", checkedif: areBack}],
+ ["away", {type: "checkbox",
+ checkedif: awayChecked,
+ repeatfor: "client.awayMsgs",
+ repeatmap: "cx.reason = item.message" }],
+ ["-"],
+ ["custom-away"]
+ ]
+ };
+
+ client.menuSpecs["popup:nickname"] = {
+ label: MSG_STATUS,
+ accesskey: getAccessKeyForMenu('MSG_STATUS'),
+ getContext: getDefaultContext,
+ items: client.menuSpecs["mainmenu:nickname"].items
+ };
+
+}
+
+function createMenus()
+{
+ client.menuManager.createMenus(document, "mainmenu");
+ client.menuManager.createContextMenus(document);
+}
+
+function getCommandContext (id, event)
+{
+ var cx = { originalEvent: event };
+
+ if (id in client.menuSpecs)
+ {
+ if ("getContext" in client.menuSpecs[id])
+ cx = client.menuSpecs[id].getContext(cx);
+ else if ("cx" in client.menuManager)
+ {
+ //dd ("using existing context");
+ cx = client.menuManager.cx;
+ }
+ else
+ {
+ //no context.
+ }
+ }
+ else
+ {
+ dd ("getCommandContext: unknown menu id " + id);
+ }
+
+ if (typeof cx == "object")
+ {
+ if (!("menuManager" in cx))
+ cx.menuManager = client.menuManager;
+ if (!("contextSource" in cx))
+ cx.contextSource = id;
+ if ("dbgContexts" in client && client.dbgContexts)
+ dd ("context '" + id + "'\n" + dumpObjectTree(cx));
+ }
+
+ return cx;
+}
+
+/**
+ * Gets an accesskey for the menu with label string ID labelString.
+ * At first, we attempt to extract it from the label string, otherwise
+ * we fall back to using a separate string.
+ *
+ * @param labelString the id for the locale string corresponding to the label
+ * @return the accesskey for the menu.
+ */
+function getAccessKeyForMenu(labelString)
+{
+ var rv = getAccessKey(window[labelString]);
+ if (!rv)
+ rv = window[labelString + "_ACCESSKEY"] || "";
+ return rv;
+}
+
+
diff --git a/comm/suite/chatzilla/xul/content/menus.xul b/comm/suite/chatzilla/xul/content/menus.xul
new file mode 100644
index 0000000000..3cf2e34d13
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/menus.xul
@@ -0,0 +1,97 @@
+<?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 SYSTEM "chrome://chatzilla/locale/chatzilla.dtd" >
+
+<overlay id="chatzilla-menu-overlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <overlaytarget id="menu-overlay-target">
+
+ <!-- parents for the command manager-managed objects -->
+ <keyset id="dynamic-keys"/>
+ <popupset id="dynamic-popups"/>
+
+ <!-- tooltip thingy -->
+ <tooltip id="html-tooltip-node" onpopupshowing="return onTooltip(event);"/>
+ <tooltip id="xul-tooltip-node" onpopupshowing="return onTooltip(event);"/>
+
+ <!-- Commands -->
+ <commandset id="chatzilla-commands">
+
+ <!-- Edit commands -->
+ <commandset id="selectEditMenuItems"/>
+ <commandset id="globalEditMenuItems"/>
+ <commandset id="undoEditMenuItems"/>
+ <commandset id="clipboardEditMenuItems"/>
+ <command id="cmd_undo"/>
+ <command id="cmd_redo"/>
+ <command id="cmd_cut"/>
+ <command id="cmd_copy"/>
+ <command id="cmd_paste"/>
+ <command id="cmd_delete"/>
+ <command id="cmd_selectAll"/>
+
+ <!-- Tasks commands, from overlay -->
+ <commandset id="tasksCommands"/>
+ </commandset>
+
+ <!-- Keys -->
+
+ <keyset id="chatzillaKeys">
+ <key id="key:reloadui" modifiers="accel alt" key="R"
+ oncommand="if (typeof cmdReloadUI =='function') cmdReloadUI(); else window.location.href = window.location.href;"/>
+
+ <!-- Edit keys -->
+ <key id="key_undo"/>
+ <key id="key_redo"/>
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"/>
+ <key id="key_delete"/>
+ <key id="key_selectAll"/>
+
+ <!-- Tasks keys, from overlay -->
+ <keyset id="tasksKeys"/>
+ </keyset>
+
+ <!-- Main menu bar -->
+ <toolbox flex="1" id="main-toolbox">
+ <menubar id="mainmenu" persist="collapsed"
+ grippytooltiptext="&Menubar.tooltip;">
+
+ <!-- ChatZilla menu placeholder, see menus.js -->
+ <menu id="mainmenu:chatzilla"><menupopup/></menu>
+
+ <!-- IRC menu placeholder, see menus.js -->
+ <menu id="mainmenu:irc"><menupopup/></menu>
+
+ <!-- Edit menu placeholder, see menus.js -->
+ <menu id="mainmenu:edit"><menupopup/></menu>
+
+ <!-- View menu placeholder, see menus.js -->
+ <menu id="mainmenu:view"><menupopup/></menu>
+
+ <!-- Tasks menu -->
+ <menu id="tasksMenu"/>
+
+ <!-- Window menu -->
+ <menu id="windowMenu"/>
+
+ <!-- Help menu -->
+ <!-- Mac expects a help menu with this ID, and there is nothing we can
+ do about it. -->
+ <menu id="menu_Help"/>
+ </menubar>
+
+ </toolbox>
+
+ </overlaytarget>
+
+</overlay>
+
diff --git a/comm/suite/chatzilla/xul/content/messages.js b/comm/suite/chatzilla/xul/content/messages.js
new file mode 100644
index 0000000000..b0eb1822ee
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/messages.js
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; 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/. */
+
+function initMessages()
+{
+ var path = "chrome://chatzilla/locale/chatzilla.properties";
+
+ client.messageManager = new MessageManager(client.entities);
+ client.messageManager.enableHankakuToZenkaku = true;
+ client.messageManager.loadBrands();
+ client.defaultBundle = client.messageManager.addBundle(path);
+
+ client.viewName = client.unicodeName = MSG_CLIENT_NAME;
+ client.responseCodeMap =
+ {
+ "HELLO": MSG_RSP_HELLO,
+ "HELP" : MSG_RSP_HELP,
+ "USAGE": MSG_RSP_USAGE,
+ "ERROR": MSG_RSP_ERROR,
+ "WARNING": MSG_RSP_WARN,
+ "INFO": MSG_RSP_INFO,
+ "EVAL-IN": MSG_RSP_EVIN,
+ "EVAL-OUT": MSG_RSP_EVOUT,
+ "DISCONNECT": MSG_RSP_DISCONNECT,
+ "JOIN": "-->|",
+ "PART": "<--|",
+ "QUIT": "|<--",
+ "NICK": "=-=",
+ "TOPIC": "=-=",
+ "KICK": "=-=",
+ "MODE": "=-=",
+ "END_STATUS": "---",
+ "DCC-CHAT": "[DCC]",
+ "DCC-FILE": "[DCC]",
+ "315": "---", /* end of WHO */
+ "318": "---", /* end of WHOIS */
+ "366": "---", /* end of NAMES */
+ "376": "---" /* end of MOTD */
+ };
+}
+
+function checkCharset(charset)
+{
+ return client.messageManager.checkCharset(charset);
+}
+
+function toUnicode (msg, charsetOrView)
+{
+ if (!msg)
+ return msg;
+
+ var charset;
+ if (typeof charsetOrView == "object")
+ charset = charsetOrView.prefs["charset"];
+ else if (typeof charsetOrView == "string")
+ charset = charsetOrView;
+ else
+ charset = client.currentObject.prefs["charset"];
+
+ return client.messageManager.toUnicode(msg, charset);
+}
+
+function fromUnicode (msg, charsetOrView)
+{
+ if (!msg)
+ return msg;
+
+ var charset;
+ if (typeof charsetOrView == "object")
+ charset = charsetOrView.prefs["charset"];
+ else if (typeof charsetOrView == "string")
+ charset = charsetOrView;
+ else
+ charset = client.currentObject.prefs["charset"];
+
+ return client.messageManager.fromUnicode(msg, charset);
+}
+
+function getMsg(msgName, params, deflt)
+{
+ return client.messageManager.getMsg(msgName, params, deflt);
+}
+
+function getMsgFrom(bundle, msgName, params, deflt)
+{
+ return client.messageManager.getMsgFrom(bundle, msgName, params, deflt);
+}
+
+/* message types, don't localize */
+const MT_ATTENTION = "ATTENTION";
+const MT_ERROR = "ERROR";
+const MT_HELLO = "HELLO";
+const MT_HELP = "HELP";
+const MT_MODE = "MODE";
+const MT_WARN = "WARNING";
+const MT_INFO = "INFO";
+const MT_USAGE = "USAGE";
+const MT_STATUS = "STATUS";
+const MT_EVALIN = "EVAL-IN";
+const MT_EVALOUT = "EVAL-OUT";
+
diff --git a/comm/suite/chatzilla/xul/content/mungers.js b/comm/suite/chatzilla/xul/content/mungers.js
new file mode 100644
index 0000000000..dce8e9980f
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/mungers.js
@@ -0,0 +1,904 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This file contains the munger functions and rules used by ChatZilla.
+ * It's generally a bad idea to call munger functions inside ChatZilla for
+ * anything but munging (chat) output.
+ */
+
+function initMunger()
+{
+ /* linkRE: the general URL linkifier regular expression:
+ *
+ * - start with whitespace, non-word, or begining-of-line
+ * - then match:
+ * - EITHER scheme (word + hyphen), colon, then lots of non-whitespace
+ * - OR "www" followed by at least 2 sets of:
+ * - "." plus some non-whitespace, non-"." characters
+ * - must end match with a word-break
+ * - include a "/" or "=" beyond break if present
+ * - end with whitespace, non-word, or end-of-line
+ */
+ client.linkRE =
+ /(?:\W|^)((?:(\w[\w-]+):[^\s]+|www(\.[^.\s]+){2,})\b[\/=\)]?)(?=\s|\W|$)/;
+
+ // Colours: \x03, with optional foreground and background colours
+ client.colorRE = /(\x03((\d{1,2})(,\d{1,2}|)|))/;
+
+ client.whitespaceRE = new RegExp("(\\S{" + client.MAX_WORD_DISPLAY + ",})");
+
+ const LOW_PRIORITY = 5;
+ const NORMAL_PRIORITY = 10;
+ const HIGH_PRIORITY = 15;
+ const HIGHER_PRIORITY = 20;
+
+ var munger = client.munger = new CMunger(insertText);
+ // Special internal munger!
+ munger.addRule(".inline-buttons", /(\[\[.*?\]\])/, insertInlineButton,
+ HIGH_PRIORITY, LOW_PRIORITY, false);
+ munger.addRule("quote", /(``|'')/, insertQuote,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("bold", /(?:[\s(\[]|^)(\*[^*()]*\*)(?:[\s\]).,;!\?]|$)/,
+ "chatzilla-bold", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("underline", /(?:[\s(\[]|^)(\_[^_()]*\_)(?:[\s\]).,;!\?]|$)/,
+ "chatzilla-underline", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("italic", /(?:\s|^)(\/[^\/()]*\/)(?:[\s.,]|$)/,
+ "chatzilla-italic", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ /* allow () chars inside |code()| blocks */
+ munger.addRule("teletype", /(?:\s|^)(\|[^|]*\|)(?:[\s.,]|$)/,
+ "chatzilla-teletype", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-colors", client.colorRE, mircChangeColor,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-bold", /(\x02)/, mircToggleBold,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-underline", /(\x1f)/, mircToggleUnder,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-color-reset", /(\x0f)/, mircResetColor,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-reverse", /(\x16)/, mircReverseColor,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".ansi-escape-sgr", /(\x1b\[([\d;]*)m)/,
+ ansiEscapeSGR, NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("ctrl-char", /([\x01-\x1f])/, showCtrlChar,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("link", client.linkRE, insertLink, NORMAL_PRIORITY, HIGH_PRIORITY);
+
+ // This has a higher starting priority so as to get it to match before the
+ // normal link, which won't know about mailto and then fail.
+ munger.addRule(".mailto",
+ /(?:\W|^)((mailto:)?[^:;\\<>\[\]()\'\"\s\u201d]+@[^.<>\[\]()\'\"\s\u201d]+\.[^<>\[\]()\'\"\s\u201d]+)/i,
+ insertMailToLink, NORMAL_PRIORITY, HIGHER_PRIORITY, false);
+
+ addBugzillaLinkMungerRule(client.prefs["bugKeyword"], NORMAL_PRIORITY, NORMAL_PRIORITY);
+
+ munger.addRule("channel-link",
+ /(?:[^\w#]|^)[@%+]?(#[^<>,\[\](){}\"\s\u201d]*[^:,.<>\[\](){}\'\"\s\u201d])/i,
+ insertChannelLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("talkback-link", /(?:\W|^)(TB\d{8,}[A-Z]?)(?:\W|$)/,
+ insertTalkbackLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
+
+ munger.addRule("face",
+ /((^|\s)(?:[>O]?[B8=:;(xX%][~']?[-^v"]?(?:[)|(PpSs0oO#\?\*\[\]\/\\]|D+)|>[-^v]?\)|[oO9][._][oO9])(\s|$))/,
+ insertSmiley, NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("rheet", /(?:\W|^)(rhee+t\!*)(?:\s|$)/i, insertRheet, 10, 10);
+ munger.addRule("word-hyphenator", client.whitespaceRE,
+ insertHyphenatedWord, LOW_PRIORITY, NORMAL_PRIORITY);
+
+ client.enableColors = client.prefs["munger.colorCodes"];
+ var branch = client.prefManager.prefBranch;
+ for (var entry in munger.entries)
+ {
+ if (!isinstance(munger.entries[entry], Object))
+ continue;
+
+ for (var rule in munger.entries[entry])
+ {
+ if (rule[0] == ".")
+ continue;
+
+ try
+ {
+ munger.entries[entry][rule].enabled =
+ branch.getBoolPref("munger." + rule);
+ }
+ catch (ex)
+ {
+ // nada
+ }
+ }
+ }
+}
+
+function addBugzillaLinkMungerRule(keywords, priority, startPriority)
+{
+ client.munger.addRule("bugzilla-link",
+ new RegExp("(?:\\W|^)(("+keywords+")\\s+(?:#?\\d+|#[^\\s,]{1,20})(?:\\s+comment\\s+#?\\d+)?)","i"),
+ insertBugzillaLink, priority, startPriority);
+
+}
+
+function insertLink(matchText, containerTag, data, mungerEntry)
+{
+ var href;
+ var linkText;
+
+ var trailing;
+ ary = matchText.match(/([.,?\)]+)$/);
+ if (ary)
+ {
+ linkText = RegExp.leftContext;
+ trailing = ary[1];
+
+ // We special-case links that end with (something), often found on wikis
+ // if "trailing" starts with ) and there's an unclosed ( in the
+ // "linkText"; then we put the final ) back in
+ if ((trailing.indexOf(")") == 0) && (linkText.match(/\([^\)]*$/)))
+ {
+
+ linkText += ")";
+ trailing = trailing.substr(1);
+ }
+ }
+ else
+ {
+ linkText = matchText;
+ }
+
+ var ary = linkText.match(/^(\w[\w-]+):/);
+ if (ary)
+ {
+ if (!client.checkURLScheme(ary[1]))
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, data);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ href = linkText;
+ }
+ else
+ {
+ href = "http://" + linkText;
+ }
+
+ /* This gives callers to the munger control over URLs being logged; the
+ * channel topic munger uses this, as well as the "is important" checker.
+ * If either of |dontLogURLs| or |noStateChange| is present and true, we
+ * don't log.
+ */
+ if ((!("dontLogURLs" in data) || !data.dontLogURLs) &&
+ (!("noStateChange" in data) || !data.noStateChange) &&
+ client.urlLogger)
+ {
+ client.urlLogger.append(href);
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
+ anchor.setAttribute("href", href.replace(mircRE, ""));
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(data);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ anchor.setAttribute("target", "_content");
+ mungerEntry.enabled = false;
+ data.inLink = true;
+ client.munger.munge(linkText, anchor, data);
+ mungerEntry.enabled = true;
+ delete data.inLink;
+ containerTag.appendChild(anchor);
+ if (trailing)
+ insertText(trailing, containerTag, data);
+
+}
+
+function insertMailToLink(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var href;
+
+ if (matchText.toLowerCase().indexOf("mailto:") != 0)
+ href = "mailto:" + matchText;
+ else
+ href = matchText;
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
+ anchor.setAttribute("href", href.replace(mircRE, ""));
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ //anchor.setAttribute ("target", "_content");
+ mungerEntry.enabled = false;
+ eventData.inLink = true;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ delete eventData.inLink;
+ containerTag.appendChild(anchor);
+
+}
+
+function insertChannelLink(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var bogusChannels =
+ /^#(include|error|define|if|ifdef|else|elsif|endif)$/i;
+
+ if (!("network" in eventData) || !eventData.network ||
+ matchText.search(bogusChannels) != -1)
+ {
+ containerTag.appendChild(document.createTextNode(matchText));
+ return;
+ }
+
+ var linkText = removeColorCodes(matchText);
+ var encodedLinkText = fromUnicode(linkText, eventData.sourceObject);
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ anchor.setAttribute("href", eventData.network.getURL(encodedLinkText));
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ mungerEntry.enabled = false;
+ eventData.inLink = true;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ delete eventData.inLink;
+ containerTag.appendChild(anchor);
+}
+
+function insertTalkbackLink(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+
+ anchor.setAttribute("href", "http://talkback-public.mozilla.org/" +
+ "search/start.jsp?search=2&type=iid&id=" + matchText);
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ containerTag.appendChild(anchor);
+}
+
+function insertBugzillaLink (matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var prefs = client.prefs;
+ if (eventData.channel)
+ prefs = eventData.channel.prefs;
+ else if (eventData.network)
+ prefs = eventData.network.prefs;
+
+ var bugURL = prefs["bugURL"];
+ var bugURLcomment = prefs["bugURL.comment"];
+
+ if (bugURL.length > 0)
+ {
+ var idOrAlias = matchText.match(new RegExp("(?:"+client.prefs["bugKeyword"]+")\\s+#?(\\d+|[^\\s,]{1,20})","i"))[1];
+ bugURL = bugURL.replace("%s", idOrAlias);
+
+ var commentNum = matchText.match(/comment\s+#?(\d+)/i);
+ if (commentNum)
+ {
+ /* If the comment is a complete URL, use only that, replacing %1$s
+ * and %2$s with the bug number and comment number, respectively.
+ * Otherwise, append the comment preference to the main one,
+ * replacing just %s in each.
+ */
+ if (bugURLcomment.match(/^\w+:/))
+ {
+ bugURL = bugURLcomment;
+ bugURL = bugURL.replace("%1$s", idOrAlias);
+ bugURL = bugURL.replace("%2$s", commentNum[1]);
+ }
+ else
+ {
+ bugURL += bugURLcomment.replace("%s", commentNum[1]);
+ }
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ anchor.setAttribute("href", bugURL);
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ anchor.setAttribute("target", "_content");
+ mungerEntry.enabled = false;
+ eventData.inLink = true;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ delete eventData.inLink;
+ containerTag.appendChild(anchor);
+ }
+ else
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ }
+}
+
+function insertRheet(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ anchor.setAttribute("href",
+ "http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/bonus-tracks/rheet.wav");
+ anchor.setAttribute("class", "chatzilla-rheet chatzilla-link");
+ //anchor.setAttribute ("target", "_content");
+ insertText(matchText, anchor, eventData);
+ containerTag.appendChild(anchor);
+}
+
+function insertQuote (matchText, containerTag)
+{
+ if (matchText == "``")
+ containerTag.appendChild(document.createTextNode("\u201c"));
+ else
+ containerTag.appendChild(document.createTextNode("\u201d"));
+ containerTag.appendChild(document.createElementNS(XHTML_NS, "html:wbr"));
+}
+
+function insertSmiley(emoticon, containerTag, eventData, mungerEntry)
+{
+ let smilies = {
+ "face-alien": "\uD83D\uDC7D",
+ "face-lol": "\uD83D\uDE02",
+ "face-laugh": "\uD83D\uDE04",
+ "face-sweat_smile": "\uD83D\uDE05",
+ "face-innocent": "\uD83D\uDE07",
+ "face-evil": "\uD83D\uDE08",
+ "face-wink": "\uD83D\uDE09",
+ "face-smile": "\uD83D\uDE0A",
+ "face-cool": "\uD83D\uDE0E",
+ "face-neutral": "\uD83D\uDE10",
+ "face-thinking": "\uD83D\uDE14",
+ "face-confused": "\uD83D\uDE15",
+ "face-kissing": "\uD83D\uDE17",
+ "face-tongue": "\uD83D\uDE1B",
+ "face-worried": "\uD83D\uDE1F",
+ "face-angry": "\uD83D\uDE20",
+ "face-cry": "\uD83D\uDE22",
+ "face-surprised": "\uD83D\uDE2D",
+ "face-eek": "\uD83D\uDE31",
+ "face-red": "\uD83D\uDE33",
+ "face-dizzy": "\uD83D\uDE35",
+ "face-sad": "\uD83D\uDE41",
+ "face-rolleyes": "\uD83D\uDE44",
+ "face-zipped": "\uD83E\uDD10",
+ "face-rofl": "\uD83E\uDD23",
+ "face-woozy": "\uD83E\uDD74",
+ };
+
+ let type;
+
+ if (emoticon.search(/\>[-^v]?\)/) != -1)
+ type = "face-alien";
+ else if (emoticon.search(/\>[=:;][-^v]?[(|]|[Xx][-^v]?[(\[]/) != -1)
+ type = "face-angry";
+ else if (emoticon.search(/[=:;][-^v]?[Ss]/) != -1)
+ type = "face-confused";
+ else if (emoticon.search(/[B8][-^v]?[)\]]/) != -1)
+ type = "face-cool";
+ else if (emoticon.search(/[=:;][~'][-^v]?\(/) != -1)
+ type = "face-cry";
+ else if (emoticon.search(/o[._]O|O[._]o/) != -1)
+ type = "face-dizzy";
+ else if (emoticon.search(/o[._]o|O[._]O/) != -1)
+ type = "face-eek";
+ else if (emoticon.search(/\>[=:;][-^v]?D/) != -1)
+ type = "face-evil";
+ else if (emoticon.search(/O[=:][-^v]?[)]/) != -1)
+ type = "face-innocent";
+ else if (emoticon.search(/[=:;][-^v]?[*]/) != -1)
+ type = "face-kissing";
+ else if (emoticon.search(/[=:;][-^v]?DD/) != -1)
+ type = "face-lol";
+ else if (emoticon.search(/[=:;][-^v]?D/) != -1)
+ type = "face-laugh";
+ else if (emoticon.search(/\([-^v]?D|[xX][-^v]?D/) != -1)
+ type = "face-rofl";
+ else if (emoticon.search(/[=:;][-^v]?\|/) != -1)
+ type = "face-neutral";
+ else if (emoticon.search(/[=:;][-^v]?\?/) != -1)
+ type = "face-thinking";
+ else if (emoticon.search(/[=:;]"[)\]]/) != -1)
+ type = "face-red";
+ else if (emoticon.search(/9[._]9/) != -1)
+ type = "face-rolleyes";
+ else if (emoticon.search(/[=:;][-^v]?[(\[]/) != -1)
+ type = "face-sad";
+ else if (emoticon.search(/[=:][-^v]?[)]/) != -1)
+ type = "face-smile";
+ else if (emoticon.search(/[=:;][-^v]?[0oO]/) != -1)
+ type = "face-surprised";
+ else if (emoticon.search(/[=:][-^v]?[\]]/) != -1)
+ type = "face-sweat_smile";
+ else if (emoticon.search(/[=:;][-^v]?[pP]/) != -1)
+ type = "face-tongue";
+ else if (emoticon.search(/;[-^v]?[)\]]/) != -1)
+ type = "face-wink";
+ else if (emoticon.search(/%[-^v][)\]]/) != -1)
+ type = "face-woozy";
+ else if (emoticon.search(/[=:;][-^v]?[\/\\]/) != -1)
+ type = "face-worried";
+ else if (emoticon.search(/[=:;][-^v]?[#]/) != -1)
+ type = "face-zipped";
+
+ let glyph = smilies[type];
+ if (!glyph) {
+ // We didn't actually match anything, so it'll be a too-generic match
+ // from the munger RegExp.
+ mungerEntry.enabled = false;
+ client.munger.munge(emoticon, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ // Add spaces to beginning / end where appropriate.
+ if (emoticon.search(/^\s/) != -1)
+ glyph = " " + glyph;
+ if (emoticon.search(/\s$/) != -1)
+ glyph = glyph + " ";
+
+ // Create a span to hold the emoticon.
+ let span = document.createElementNS(XHTML_NS, "html:span");
+ span.appendChild(document.createTextNode(glyph));
+ span.setAttribute("class", "chatzilla-emote-txt");
+ // Add the title attribute (to show the original text in a tooltip) in case
+ // the replacement was done incorrectly.
+ span.setAttribute("title", emoticon);
+ span.setAttribute("type", type);
+ containerTag.appendChild(span);
+}
+
+function mircChangeColor (colorInfo, containerTag, data)
+{
+ /* If colors are disabled, the caller doesn't want colors specifically, or
+ * the caller doesn't want any state-changing effects, we drop out.
+ */
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ // Entry 0 will contain all colors specified,
+ // entry 1 will have any specified foreground color or be undefined,
+ // entry 2 will have any specified background color or be undefined.
+ // Valid color codes are 0-99 with 99 having special meaning.
+ let ary = colorInfo.match(/^\x03(?:(\d\d?)(?:,(\d\d?))?)?/);
+
+ // If no foreground color specified or somehow the array does not have 3
+ // entries then it has invalid syntax.
+ if (ary.length != 3 || !ary[1]) {
+ delete data.currFgColor;
+ delete data.currBgColor;
+ return;
+ }
+
+ let fgColor = Number(ary[1]);
+
+ if (fgColor != 99) {
+ data.currFgColor = (fgColor % 16).toString().padStart(2, "0");
+ } else {
+ delete data.currFgColor;
+ }
+
+ // If no background color then default to 99.
+ let bgColor = Number(ary[2] || "99");
+
+ if (bgColor != 99) {
+ data.currBgColor = (bgColor % 16).toString().padStart(2, "0");
+ } else {
+ delete data.currBgColor;
+ }
+
+ // Only set hasColorInfo if we have something set.
+ if (fgColor != 99 || bgColor != 99) {
+ data.hasColorInfo = true;
+ }
+}
+
+function mircToggleBold (colorInfo, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ if ("isBold" in data)
+ delete data.isBold;
+ else
+ data.isBold = true;
+ data.hasColorInfo = true;
+}
+
+function mircToggleUnder (colorInfo, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ if ("isUnderline" in data)
+ delete data.isUnderline;
+ else
+ data.isUnderline = true;
+ data.hasColorInfo = true;
+}
+
+function mircResetColor (text, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange) ||
+ !("hasColorInfo" in data))
+ {
+ return;
+ }
+
+ removeColorInfo(data);
+}
+
+function mircReverseColor (text, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ var tempColor = ("currFgColor" in data ? data.currFgColor : "");
+
+ if ("currBgColor" in data)
+ data.currFgColor = data.currBgColor;
+ else
+ delete data.currFgColor;
+ if (tempColor)
+ data.currBgColor = tempColor;
+ else
+ delete data.currBgColor;
+ data.hasColorInfo = true;
+}
+
+function ansiEscapeSGR(text, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noANSIColors" in data) && data.noANSIColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ /* ANSI SGR (Select Graphic Rendition) escape support. Matched text may
+ * have any number of effects, each a number separated by a semicolon. If
+ * there are no effects listed, it is treated as effect "0" (reset/normal).
+ */
+
+ text = text.substr(2, text.length - 3) || "0";
+
+ const ansiToMircColor = [
+ "01", "05", "03", "07", "02", "06", "10", "15",
+ "14", "04", "09", "08", "12", "13", "11", "00"
+ ];
+
+ var effects = text.split(";");
+ for (var i = 0; i < effects.length; i++)
+ {
+ data.hasColorInfo = true;
+
+ switch (Number(effects[i]))
+ {
+ case 0: // Reset/normal.
+ removeColorInfo(data);
+ break;
+
+ case 1: // Intensity: bold.
+ data.isBold = true;
+ break;
+
+ case 3: // Italic: on.
+ data.isItalic = true;
+ break;
+
+ case 4: // Underline: single.
+ data.isUnderline = true;
+ break;
+
+ case 9: // Strikethrough: on.
+ data.isStrikethrough = true;
+ break;
+
+ case 22: // Intensity: normal.
+ delete data.isBold;
+ break;
+
+ case 23: // Italic: off.
+ delete data.isItalic;
+ break;
+
+ case 24: // Underline: off.
+ delete data.isUnderline;
+ break;
+
+ case 29: // Strikethrough: off.
+ delete data.isStrikethrough;
+ break;
+
+ case 53: // Overline: on.
+ data.isOverline = true;
+ break;
+
+ case 55: // Overline: off.
+ delete data.isOverline;
+ break;
+
+ case 30: // FG: Black.
+ case 31: // FG: Red.
+ case 32: // FG: Green.
+ case 33: // FG: Yellow.
+ case 34: // FG: Blue.
+ case 35: // FG: Magenta.
+ case 36: // FG: Cyan.
+ case 37: // FG: While (light grey).
+ data.currFgColor = ansiToMircColor[effects[i] - 30];
+ break;
+
+ case 39: // FG: default.
+ delete data.currFgColor;
+ break;
+
+ case 40: // BG: Black.
+ case 41: // BG: Red.
+ case 42: // BG: Green.
+ case 43: // BG: Yellow.
+ case 44: // BG: Blue.
+ case 45: // BG: Magenta.
+ case 46: // BG: Cyan.
+ case 47: // BG: While (light grey).
+ data.currBgColor = ansiToMircColor[effects[i] - 40];
+ break;
+
+ case 49: // BG: default.
+ delete data.currBgColor;
+ break;
+
+ case 90: // FG: Bright Black (dark grey).
+ case 91: // FG: Bright Red.
+ case 92: // FG: Bright Green.
+ case 93: // FG: Bright Yellow.
+ case 94: // FG: Bright Blue.
+ case 95: // FG: Bright Magenta.
+ case 96: // FG: Bright Cyan.
+ case 97: // FG: Bright While.
+ data.currFgColor = ansiToMircColor[effects[i] - 90 + 8];
+ break;
+
+ case 100: // BG: Bright Black (dark grey).
+ case 101: // BG: Bright Red.
+ case 102: // BG: Bright Green.
+ case 103: // BG: Bright Yellow.
+ case 104: // BG: Bright Blue.
+ case 105: // BG: Bright Magenta.
+ case 106: // BG: Bright Cyan.
+ case 107: // BG: Bright While.
+ data.currBgColor = ansiToMircColor[effects[i] - 100 + 8];
+ break;
+ }
+ }
+}
+
+function removeColorInfo(data)
+{
+ delete data.currFgColor;
+ delete data.currBgColor;
+ delete data.isBold;
+ delete data.isItalic;
+ delete data.isOverline;
+ delete data.isStrikethrough;
+ delete data.isUnderline;
+ delete data.hasColorInfo;
+}
+
+function showCtrlChar(c, containerTag)
+{
+ var span = document.createElementNS(XHTML_NS, "html:span");
+ span.setAttribute("class", "chatzilla-control-char");
+ if (c == "\t")
+ {
+ containerTag.appendChild(document.createTextNode(c));
+ return;
+ }
+
+ var ctrlStr = c.charCodeAt(0).toString(16);
+ if (ctrlStr.length < 2)
+ ctrlStr = "0" + ctrlStr;
+ span.appendChild(document.createTextNode("0x" + ctrlStr));
+ containerTag.appendChild(span);
+ containerTag.appendChild(document.createElementNS(XHTML_NS, "html:wbr"));
+}
+
+function insertText(text, containerTag, data)
+{
+ var newClass = "";
+ if (data && ("hasColorInfo" in data))
+ newClass = calcClass(data);
+ if (!newClass)
+ delete data.hasColorInfo;
+
+ if (newClass)
+ {
+ var spanTag = document.createElementNS(XHTML_NS, "html:span");
+ spanTag.setAttribute("class", newClass);
+ containerTag.appendChild(spanTag);
+ containerTag = spanTag;
+ }
+
+ var arg;
+ while ((arg = text.match(client.whitespaceRE)))
+ {
+ // Find the start of the match so we can insert the preceding text.
+ var start = text.indexOf(arg[0]);
+ if (start > 0)
+ containerTag.appendChild(document.createTextNode(text.substr(0, start)));
+
+ // Process the long word itself.
+ insertHyphenatedWord(arg[1], containerTag, { dontStyleText: true });
+
+ // Continue with the rest of the text.
+ text = text.substr(start + arg[0].length);
+ }
+
+ // Insert any left-over text on the end.
+ if (text)
+ containerTag.appendChild(document.createTextNode(text));
+}
+
+function insertHyphenatedWord(longWord, containerTag, data)
+{
+ var wordParts = splitLongWord(longWord, client.MAX_WORD_DISPLAY);
+
+ if (!data || !("dontStyleText" in data))
+ {
+ var newClass = "";
+ if (data && ("hasColorInfo" in data))
+ newClass = calcClass(data);
+ if (!newClass)
+ delete data.hasColorInfo;
+
+ if (newClass)
+ {
+ var spanTag = document.createElementNS(XHTML_NS, "html:span");
+ spanTag.setAttribute("class", newClass);
+ containerTag.appendChild(spanTag);
+ containerTag = spanTag;
+ }
+ }
+
+ var wbr = document.createElementNS(XHTML_NS, "html:wbr");
+ for (var i = 0; i < wordParts.length; ++i)
+ {
+ containerTag.appendChild(document.createTextNode(wordParts[i]));
+ containerTag.appendChild(wbr.cloneNode(true));
+ }
+}
+
+function insertInlineButton(text, containerTag, data)
+{
+ var ary = text.match(/\[\[([^\]]+)\]\[([^\]]+)\]\[([^\]]+)\]\]/);
+
+ if (!ary)
+ {
+ containerTag.appendChild(document.createTextNode(text));
+ return;
+ }
+
+ var label = ary[1];
+ var title = ary[2];
+ var command = ary[3];
+
+ var link = document.createElementNS(XHTML_NS, "html:a");
+ link.setAttribute("href", "x-cz-command:" + encodeURI(command));
+ link.setAttribute("title", title);
+ link.setAttribute("class", "chatzilla-link");
+ link.appendChild(document.createTextNode(label));
+
+ containerTag.appendChild(document.createTextNode("["));
+ containerTag.appendChild(link);
+ containerTag.appendChild(document.createTextNode("]"));
+}
+
+function calcClass(data)
+{
+ var className = "";
+ if ("hasColorInfo" in data)
+ {
+ if ("currFgColor" in data)
+ className += " chatzilla-fg" + data.currFgColor;
+ if ("currBgColor" in data)
+ className += " chatzilla-bg" + data.currBgColor;
+ if ("isBold" in data)
+ className += " chatzilla-bold";
+ if ("isItalic" in data)
+ className += " chatzilla-italic";
+ if ("isOverline" in data)
+ className += " chatzilla-overline";
+ if ("isStrikethrough" in data)
+ className += " chatzilla-strikethrough";
+ if ("isUnderline" in data)
+ className += " chatzilla-underline";
+ }
+ return className;
+}
+
diff --git a/comm/suite/chatzilla/xul/content/networks-edit.css b/comm/suite/chatzilla/xul/content/networks-edit.css
new file mode 100644
index 0000000000..f1f36e47e1
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/networks-edit.css
@@ -0,0 +1,15 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Set min-width on the network list. */
+#networkList {
+ min-width: 26ch;
+}
+
+/* Set min-width on the server list. */
+#serverList {
+ min-width: 28ch;
+}
diff --git a/comm/suite/chatzilla/xul/content/networks-edit.js b/comm/suite/chatzilla/xul/content/networks-edit.js
new file mode 100644
index 0000000000..4952783cc9
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/networks-edit.js
@@ -0,0 +1,390 @@
+/* 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 ASSERT = function(cond, msg) { if (!cond) { alert(msg); } return cond; }
+var client;
+
+// To be able to load static.js, we need a few things defined first:
+function CIRCNetwork() {}
+function CIRCServer() {}
+function CIRCChannel() {}
+function CIRCUser() {}
+function CIRCChanUser() {}
+function CIRCDCCUser() {}
+function CIRCDCCChat() {}
+function CIRCDCCFile() {}
+function CIRCDCCFileTransfer() {}
+function CIRCSTS() {}
+
+// Actual network window itself.
+var gNetworkWindow = {
+ mBundle: null,
+ mServerList: null,
+ mNetworkList: null,
+
+ /* Stores all the network and server objects we're using.
+ */
+ networkList: null,
+
+ alert: function(aSubject, aVar) {
+ let title = this.mBundle.getString(aSubject + "Title");
+ let msg = this.mBundle.getFormattedString(aSubject, [aVar]);
+ Services.prompt.alert(window, title, msg);
+ },
+
+ confirmEx: function(aSubject, aVar) {
+ let title = this.mBundle.getString(aSubject + "Title");
+ let msg = aVar ? this.mBundle.getFormattedString(aSubject, [aVar])
+ : this.mBundle.getString(aSubject);
+ return Services.prompt.confirmEx(window, title, msg,
+ Services.prompt.STD_YES_NO_BUTTONS, null,
+ null, null, null, { });
+ },
+
+ prompt: function(aSubject, aInitial) {
+ let title = this.mBundle.getString(aSubject + "Title");
+ let msg = this.mBundle.getString(aSubject);
+ let rv = { value: aInitial };
+
+ if (!Services.prompt.prompt(window, title, msg, rv, null, {value: null})) {
+ return null;
+ }
+
+ return rv.value.toLowerCase().trim();
+ },
+
+ refreshNetworks: function(aNetwork) {
+ // Remove all children.
+ while (this.mNetworkList.hasChildNodes()) {
+ this.mNetworkList.lastChild.remove();
+ }
+
+ let hasChildren = false;
+ let network;
+ // Populate the network item list.
+ for (let name in this.networkList) {
+ let label = document.createElement("label");
+ label.setAttribute("value", name);
+ let listitem = document.createElement("listitem");
+ listitem.appendChild(label);
+ listitem.id = name;
+ if (aNetwork && (aNetwork == name)) {
+ network = listitem;
+ }
+ this.mNetworkList.appendChild(listitem);
+ hasChildren = true;
+ }
+
+ if (hasChildren) {
+ // If a network name was given and found select it,
+ // otherwise select the first item.
+ this.mNetworkList.selectItem(network || this.mNetworkList.firstChild);
+ } else {
+ this.onSelectNetwork();
+ }
+ this.updateNetworkButtons(hasChildren);
+ },
+
+ updateNetworkButtons: function(aSelected) {
+ let editButton = document.getElementById("networkListEditButton");
+ let removeButton = document.getElementById("networkListRemoveButton");
+ if (!aSelected) {
+ editButton.setAttribute("disabled", "true");
+ removeButton.setAttribute("disabled", "true");
+ } else {
+ editButton.removeAttribute("disabled");
+ removeButton.removeAttribute("disabled");
+ }
+ },
+
+ // Loads the networks list.
+ onLoad: function() {
+ client = window.arguments[0];
+
+ // Needed for ASSERT.
+ initMessages();
+
+ this.mBundle = document.getElementById("bundle_networks");
+ this.mServerList = document.getElementById("serverList");
+ this.mNetworkList = document.getElementById("networkList");
+
+ // The list of objects we're tracking.
+ this.networkList = networksToNetworkList();
+ this.refreshNetworks();
+
+ // Force the window to be the right size now, not later.
+ window.sizeToContent();
+ },
+
+ // Closing the window. Clean up.
+ onClose: function() {
+ },
+
+ // OK button.
+ onOK: function() {
+ // Save the list and update client.networks
+ try {
+ networksSaveList(this.networkList);
+ }
+ catch (e) {
+ this.alert("network-saveError", e);
+ return false;
+ }
+
+ networksSyncFromList(this.networkList);
+ window.close();
+ client.updateHeader();
+ client.dispatch("networks");
+ return true;
+ },
+
+ // Cancel button.
+ onCancel: function() {
+ window.close();
+ return true;
+ },
+
+ // Restore Defaults button.
+ onRestore: function() {
+ // Ask for confirmation.
+ if (this.confirmEx("network-confirmRestoreDefaults") != 0) {
+ return;
+ }
+
+ // Repopulate the network list.
+ this.networkList = networksGetDefaults();
+ this.refreshNetworks();
+ },
+
+ // Connect to Network button.
+ onConnect: function() {
+ let selection = this.mNetworkList.selectedItem;
+ if (!selection)
+ return;
+
+ let network = this.networkList[selection.id];
+ if (this.onOK()) {
+ if (networkHasSecure(network.servers)) {
+ client.dispatch("sslserver " + network.name);
+ } else {
+ client.dispatch("server " + network.name);
+ }
+ }
+ },
+
+ // Select a network listitem.
+ onSelectNetwork: function(aId = 0) {
+ let header = document.getElementById("network-header");
+
+ // Remove all children.
+ while (this.mServerList.hasChildNodes()) {
+ this.mServerList.lastChild.remove();
+ }
+
+ let selection = this.mNetworkList.selectedItem;
+ if (!selection) {
+ header.setAttribute("title",
+ this.mBundle.getString("network-headerDefault"));
+ this.updateServerButtons(null, true);
+ return;
+ }
+
+ // Make sure selected network item is visible.
+ this.mNetworkList.ensureElementIsVisible(selection);
+
+ let hasChildren = false;
+ let network = this.networkList[selection.id];
+ for (let i = 0; i < network.servers.length; i++) {
+ let server = network.servers[i];
+ let label = document.createElement("label");
+ label.setAttribute("value", server.hostname + ":" + server.port);
+ let listitem = document.createElement("listitem");
+ listitem.appendChild(label);
+ listitem.setAttribute("server_id", i);
+ listitem.id = network.name + "-" + i;
+ this.mServerList.appendChild(listitem);
+ hasChildren = true;
+ }
+
+ if (hasChildren) {
+ // Select the given id if it exists otherwise the first item.
+ this.mServerList.selectedIndex = aId;
+ } else {
+ this.onSelectServer();
+ }
+
+ header.setAttribute("title",
+ this.mBundle.getFormattedString("network-headerName",
+ [network.name]));
+ },
+
+ // Network Add button.
+ onAddNetwork: function() {
+ let name = this.prompt("network-add");
+ if (!name) {
+ return;
+ }
+
+ if (name in this.networkList) {
+ this.alert("network-nameError", name);
+ return;
+ }
+
+ // Create new network entry.
+ this.networkList[name] = { name: name, displayName: name, servers: [] };
+
+ this.refreshNetworks(name);
+ },
+
+ // Network Edit button.
+ onEditNetwork: function() {
+ let oldName = this.mNetworkList.selectedItem.id;
+ let name = this.prompt("network-edit", oldName);
+ if (!name || (name == oldName)) {
+ return;
+ }
+
+ if (name in this.networkList) {
+ this.alert("network-nameError", name);
+ return;
+ }
+
+ // Create new network entry.
+ this.networkList[name] = { name: name, displayName: name,
+ servers: this.networkList[oldName].servers };
+ // Remove old network entry.
+ delete this.networkList[oldName];
+
+ this.refreshNetworks(name);
+ },
+
+ // Network Remove button.
+ onRemoveNetwork: function() {
+ let selected = this.mNetworkList.selectedItem;
+
+ // Confirm definitely want to remove this network.
+ if (this.confirmEx("network-remove", selected.id) != 0) {
+ return;
+ }
+
+ // Remove network entry.
+ delete this.networkList[selected.id];
+
+ this.refreshNetworks();
+ },
+
+ // Move up / down buttons.
+ onMoveServer: function(aDir) {
+ let item = this.mServerList.selectedItem;
+ let network = this.mNetworkList.selectedItem.id;
+ let id = parseInt(item.getAttribute("server_id"));
+ let server = this.networkList[network].servers[id];
+ this.networkList[network].servers.splice(id, 1);
+ this.networkList[network].servers.splice(id + aDir, 0, server);
+
+ // Refresh the server list and select the server that has been moved.
+ this.onSelectNetwork(id + aDir);
+ },
+
+ // Add Server button.
+ onAddServer: function() {
+ this.openServerEditor(null);
+ },
+
+ // Edit Server button.
+ onEditServer: function() {
+ let item = this.mServerList.selectedItem;
+ if (!item) {
+ return;
+ }
+ this.openServerEditor(item);
+ },
+
+ // Remove Server button.
+ onRemoveServer: function() {
+ let item = this.mServerList.selectedItem;
+ let network = this.mNetworkList.selectedItem.id;
+ let id = item.getAttribute("server_id");
+ let server = this.networkList[network].servers[id];
+ let name = server.hostname + ":" + server.port;
+
+ // Confirm definitely want to remove this network.
+ if (this.confirmEx("server-remove", name) != 0) {
+ return;
+ }
+
+ this.networkList[network].servers.splice(id, 1);
+ this.onSelectNetwork();
+ },
+
+ onSelectServer: function() {
+ let server = this.mServerList.selectedItem;
+ this.updateServerButtons(server, false);
+ this.updateServerInfoBox(server);
+ },
+
+ openServerEditor: function(aItem) {
+ let network = this.mNetworkList.selectedItem.id;
+ let id;
+ let server;
+ if (aItem) {
+ id = aItem.getAttribute("server_id");
+ server = this.networkList[network].servers[id];
+ }
+
+ let args = { server: server, result: false };
+ window.openDialog("chrome://chatzilla/content/networks-server.xul",
+ "serverEdit", "chrome,titlebar,modal,centerscreen", args);
+ // Now update the server which was just added / edited and select it.
+ if (args.result) {
+ if (server) {
+ this.networkList[network].servers[id] = args.server;
+ } else {
+ id = this.networkList[network].servers.length;
+ this.networkList[network].servers.push(args.server);
+ }
+ this.refreshNetworks(network);
+ this.mServerList.selectedIndex = id;
+ }
+ },
+
+ updateServerButtons: function(aServer, aNone) {
+ this.disableButton("serverListUpButton", aNone || !aServer ||
+ !aServer.previousSibling);
+ this.disableButton("serverListDownButton", aNone || !aServer ||
+ !aServer.nextSibling);
+ this.disableButton("serverListAddButton", aNone);
+ this.disableButton("serverListEditButton", aNone || !aServer);
+ this.disableButton("serverListRemoveButton", aNone || !aServer);
+ },
+
+ disableButton: function(aButtonId, aDisable) {
+ let button = document.getElementById(aButtonId);
+ if (aDisable) {
+ button.setAttribute("disabled", "true");
+ } else {
+ button.removeAttribute("disabled");
+ }
+ },
+
+ updateServerInfoBox: function(aServer) {
+ let name = document.getElementById("nameValue");
+ let port = document.getElementById("portValue");
+ let connection = document.getElementById("connectionSecurityValue");
+ if (!aServer) {
+ name.value = "";
+ port.value = "";
+ connection.value = "";
+ return;
+ }
+
+ let network = this.mNetworkList.selectedItem.id;
+ let id = aServer.getAttribute("server_id");
+ let server = this.networkList[network].servers[id];
+ let type = "server-ConnectionSecurityType-" + (server.isSecure ? "3" : "0");
+ name.value = server.hostname;
+ port.value = server.port;
+ connection.value = this.mBundle.getString(type);
+ },
+};
diff --git a/comm/suite/chatzilla/xul/content/networks-edit.xul b/comm/suite/chatzilla/xul/content/networks-edit.xul
new file mode 100644
index 0000000000..97483230ba
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/networks-edit.xul
@@ -0,0 +1,155 @@
+<?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 dialog SYSTEM "chrome://chatzilla/locale/networks.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/skin/networks-edit.css"
+ type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/content/networks-edit.css"
+ type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ id="chatzilla-window"
+ title="&networksEditDialog.title;"
+ size="&networksEditDialog.size;"
+ windowtype="irc:chatzilla:networks"
+ onload="gNetworkWindow.onLoad();"
+ onunload="gNetworkWindow.onClose();"
+ buttons="accept,cancel,extra1,extra2"
+ buttonlabelextra1="&connectNetwork.label;"
+ buttonaccesskeyextra1="&connectNetwork.accesskey;"
+ buttonlabelextra2="&restoreButton.label;"
+ buttonaccesskeyextra2="&restoreButton.accesskey;"
+ ondialogaccept="return gNetworkWindow.onOK();"
+ ondialogcancel="return gNetworkWindow.onCancel();"
+ ondialogextra1="gNetworkWindow.onConnect();"
+ ondialogextra2="gNetworkWindow.onRestore();"
+ persist="screenX screenY width height">
+
+ <script src="chrome://chatzilla/content/lib/js/utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/file-utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/json-serializer.js"/>
+ <script src="chrome://chatzilla/content/lib/js/message-manager.js"/>
+ <script src="chrome://chatzilla/content/lib/js/text-serializer.js"/>
+ <script src="chrome://chatzilla/content/messages.js"/>
+ <script src="chrome://chatzilla/content/networks.js"/>
+ <script src="chrome://chatzilla/content/networks-edit.js"/>
+ <script src="chrome://chatzilla/content/static.js"/>
+
+ <stringbundle id="bundle_networks"
+ src="chrome://chatzilla/locale/networks.properties"/>
+
+ <hbox flex="1">
+ <vbox id="networkListBox">
+ <listbox id="networkList"
+ onselect="gNetworkWindow.onSelectNetwork();"
+ ondblclick="gNetworkWindow.onEditNetwork();"
+ seltype="single"
+ flex="1"/>
+ <button id="networkListAddButton"
+ label="&networkListAdd.label;"
+ accesskey="&networkListAdd.accesskey;"
+ tooltiptext="&networkListAdd.tooltip;"
+ oncommand="gNetworkWindow.onAddNetwork();"/>
+ <button id="networkListEditButton"
+ label="&networkListEdit.label;"
+ accesskey="&networkListEdit.accesskey;"
+ tooltiptext="&networkListEdit.tooltip;"
+ oncommand="gNetworkWindow.onEditNetwork();"/>
+ <button id="networkListRemoveButton"
+ label="&networkListRemove.label;"
+ accesskey="&networkListRemove.accesskey;"
+ tooltiptext="&networkListRemove.tooltip;"
+ oncommand="gNetworkWindow.onRemoveNetwork();"/>
+ </vbox>
+ <vbox id="serverListBox" flex="1">
+ <dialogheader id="network-header" title=""/>
+ <hbox flex="1">
+ <listbox id="serverList"
+ onselect="gNetworkWindow.onSelectServer();"
+ ondblclick="gNetworkWindow.onEditServer();"
+ seltype="single"
+ flex="1"/>
+ <vbox id="serverListButtons">
+ <button id="serverListUpButton"
+ disabled="true"
+ label="&serverListUp.label;"
+ accesskey="&serverListUp.accesskey;"
+ tooltiptext="&serverListUp.tooltip;"
+ oncommand="gNetworkWindow.onMoveServer(-1);"/>
+ <button id="serverListDownButton"
+ disabled="true"
+ label="&serverListDown.label;"
+ accesskey="&serverListDown.accesskey;"
+ tooltiptext="&serverListDown.tooltip;"
+ oncommand="gNetworkWindow.onMoveServer(1);"/>
+ <spacer flex="1"/>
+ <button id="serverListAddButton"
+ label="&serverListAdd.label;"
+ accesskey="&serverListAdd.accesskey;"
+ tooltiptext="&serverListAdd.tooltip;"
+ oncommand="gNetworkWindow.onAddServer();"/>
+ <button id="serverListEditButton"
+ label="&serverListEdit.label;"
+ accesskey="&serverListEdit.accesskey;"
+ tooltiptext="&serverListEdit.tooltip;"
+ oncommand="gNetworkWindow.onEditServer();"/>
+ <separator/>
+ <button id="serverListRemoveButton"
+ disabled="true"
+ label="&serverListRemove.label;"
+ accesskey="&serverListRemove.accesskey;"
+ tooltiptext="&serverListRemove.tooltip;"
+ oncommand="gNetworkWindow.onRemoveServer();"/>
+ </vbox>
+ </hbox>
+
+ <separator/>
+
+ <label class="header">&serverDetails.label;</label>
+ <hbox id="serverInfoBox">
+ <stack flex="1" class="inset">
+ <spacer id="backgroundBox"/>
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <hbox pack="end">
+ <label id="nameLabel"
+ value="&serverName.label;"
+ control="nameValue"/>
+ </hbox>
+ <textbox id="nameValue" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end">
+ <label id="portLabel"
+ value="&serverPort.label;"
+ control="portValue"/>
+ </hbox>
+ <textbox id="portValue" readonly="true" class="plain"/>
+ </row>
+ <row align="center">
+ <hbox pack="end">
+ <label id="connectionSecurityLabel"
+ value="&connectionSecurity.label;"
+ control="connectionSecurityValue"/>
+ </hbox>
+ <textbox id="connectionSecurityValue"
+ readonly="true"
+ class="plain"/>
+ </row>
+ </rows>
+ </grid>
+ </stack>
+ </hbox>
+ </vbox>
+ </hbox>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/networks-server.js b/comm/suite/chatzilla/xul/content/networks-server.js
new file mode 100644
index 0000000000..fde299bf89
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/networks-server.js
@@ -0,0 +1,94 @@
+/* -*- 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/. */
+
+var { Services } =
+ ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { isLegalHostNameOrIP =
+ ChromeUtils.import("resource:///modules/hostnameUtils.jsm");
+
+var gNetworkServer;
+var gNetworksBundle;
+var gNameValue;
+var gPortValue;
+var gDefaultPort;
+var gSocketType;
+
+function onLoad(aEvent) {
+ gNetworkServer = window.arguments[0].server;
+
+ gNetworksBundle = document.getElementById("bundle_networks");
+ gNameValue = document.getElementById("nameValue");
+ gPortValue = document.getElementById("portValue");
+ gDefaultPort = document.getElementById("defaultPort");
+ gSocketType = document.getElementById("socketType");
+
+ // Set labels on socketType menuitems.
+ document.getElementById("socketSecurityType-0").label =
+ gNetworksBundle.getString("server-ConnectionSecurityType-0");
+ document.getElementById("socketSecurityType-3").label =
+ gNetworksBundle.getString("server-ConnectionSecurityType-3");
+
+ if (gNetworkServer) {
+ gNameValue.value = gNetworkServer.hostname;
+ gPortValue.value = gNetworkServer.port;
+ gSocketType.value = gNetworkServer.isSecure ? 3 : 0;
+ }
+ sslChanged(false);
+}
+
+function onAccept() {
+ let hostname = cleanUpHostName(gNameValue.value.toLowerCase());
+ if (!isLegalHostNameOrIP(hostname)) {
+ let alertTitle = gNetworksBundle.getString("invalidServerName");
+ let alertMsg = gNetworksBundle.getString("enterValidServerName");
+ Services.prompt.alert(window, alertTitle, alertMsg);
+
+ window.arguments[0].result = false;
+ return false;
+ }
+
+ // If we didn't have a server to initialize with, we must create one.
+ if (!gNetworkServer) {
+ gNetworkServer = {};
+ }
+
+ gNetworkServer.hostname = hostname;
+ gNetworkServer.port = gPortValue.value;
+ gNetworkServer.isSecure = gSocketType.value == 3;
+
+ window.arguments[0].server = gNetworkServer;
+ window.arguments[0].result = true;
+ return true;
+}
+
+/**
+ * Resets the default port to IRC or IRCS, dependending on the |gSocketType|
+ * value, and sets the port to use to this default, if that's appropriate.
+ *
+ * @param aUserAction false for dialog initialization,
+ * true for user action.
+ */
+function sslChanged(aUserAction) {
+ const DEFAULT_IRC_PORT = "6667";
+ const DEFAULT_IRCS_PORT = "6697";
+ let otherDefaultPort;
+ let prevDefaultPort = gDefaultPort.value;
+
+ if (gSocketType.value == 3) {
+ gDefaultPort.value = DEFAULT_IRCS_PORT;
+ otherDefaultPort = DEFAULT_IRC_PORT;
+ } else {
+ gDefaultPort.value = DEFAULT_IRC_PORT;
+ otherDefaultPort = DEFAULT_IRCS_PORT;
+ }
+
+ // If the port is not set, or the user is causing the default port to change,
+ // and the port is set to the default for the other protocol,
+ // then set the port to the default for the new protocol.
+ if ((gPortValue.value == 0) ||
+ (aUserAction && (gDefaultPort.value != prevDefaultPort) &&
+ (gPortValue.value == otherDefaultPort)))
+ gPortValue.value = gDefaultPort.value;
+}
diff --git a/comm/suite/chatzilla/xul/content/networks-server.xul b/comm/suite/chatzilla/xul/content/networks-server.xul
new file mode 100644
index 0000000000..af2341dae0
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/networks-server.xul
@@ -0,0 +1,84 @@
+<?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 dialog SYSTEM "chrome://chatzilla/locale/networks.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://chatzilla/content/" type="text/css"?>
+
+<dialog title="&serverEditDialog.title;"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad();"
+ ondialogaccept="return onAccept();">
+
+ <script src="chrome://chatzilla/content/networks-server.js"/>
+
+ <stringbundle id="bundle_networks"
+ src="chrome://chatzilla/locale/networks.properties"/>
+
+ <vbox id="serverEditor">
+ <groupbox>
+ <caption label="&settings.caption;"/>
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label value="&serverName.label;"
+ accesskey="&serverName.accesskey;"
+ control="nameValue"/>
+ <textbox id="nameValue"
+ flex="1"
+ class="uri-element"/>
+ </row>
+ <row align="center">
+ <label value="&serverPort.label;"
+ accesskey="&serverPort.accesskey;"
+ control="portValue"/>
+ <hbox align="center">
+ <textbox id="portValue"
+ type="number"
+ min="0"
+ max="65535"
+ size="5"/>
+ <label value="&serverPortDefault.label;"/>
+ <label id="defaultPort"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <separator class="thin"/>
+
+ <groupbox>
+ <caption label="&security.caption;"/>
+
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label value="&connectionSecurity.label;"
+ accesskey="&connectionSecurity.accesskey;"
+ control="socketType"/>
+ <menulist id="socketType" oncommand="sslChanged(true);">
+ <menupopup id="socketTypePopup">
+ <menuitem id="socketSecurityType-0" value="0"/>
+ <menuitem id="socketSecurityType-3" value="3"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ </vbox>
+</dialog>
diff --git a/comm/suite/chatzilla/xul/content/networks.js b/comm/suite/chatzilla/xul/content/networks.js
new file mode 100644
index 0000000000..bffeab7c4a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/networks.js
@@ -0,0 +1,228 @@
+/* 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/. */
+
+function initNetworks()
+{
+ let migrated = Services.prefs.getBoolPref("extensions.irc.network_migrated",
+ false);
+ let networksFile = new nsLocalFile(client.prefs["profilePath"]);
+ networksFile.append("networks." + (migrated ? "json" : "txt"));
+
+ let createDefault = !networksFile.exists();
+ let networkList = {};
+ // Populate networkList with defaults if no file exists or migrating from
+ // previous networks.txt file usage.
+ if (createDefault || !migrated)
+ {
+ networkList = networksGetDefaults();
+ }
+
+ if (!createDefault)
+ {
+ let userNetworkList = [];
+
+ let networksLoader = migrated ? new JSONSerializer(networksFile)
+ : new TextSerializer(networksFile);
+ if (networksLoader.open("<"))
+ {
+ let item = networksLoader.deserialize();
+ if (isinstance(item, Array))
+ userNetworkList = item;
+ else
+ dd("Malformed networks file!");
+ networksLoader.close();
+ }
+
+ // When migrating this merges the user's network list with the default
+ // ones otherwise this populates the empty networkList.
+ for (let network of userNetworkList)
+ {
+ let lowerNetName = network.name.toLowerCase();
+ if ((lowerNetName in networkList) && ("isDeleted" in network))
+ {
+ delete networkList[lowerNetName];
+ }
+ else if (!("isDeleted" in network))
+ {
+ networkList[lowerNetName] = network;
+ networkList[lowerNetName].name = lowerNetName;
+ }
+ }
+ }
+
+
+ if (!migrated)
+ {
+ Services.prefs.setBoolPref("extensions.irc.network_migrated", true);
+ }
+
+ // Sync to client.networks.
+ networksSyncFromList(networkList);
+
+ // If we created a new file with the defaults, save it.
+ if (createDefault || !migrated)
+ networksSaveList(networkList);
+}
+
+function networksGetDefaults()
+{
+ var networks = new Object();
+
+ // Set up default network list.
+ networks["libera.chat"] = {
+ displayName: "libera.chat",
+ servers: [{hostname: "irc.libera.chat", port:6697, isSecure: true},
+ {hostname: "irc.libera.chat", port:6667}]};
+ networks["slashnet"] = {
+ displayName: "slashnet",
+ servers: [{hostname: "irc.slashnet.org", port:6667}]};
+ networks["dalnet"] = {
+ displayName: "dalnet",
+ servers: [{hostname: "irc.dal.net", port:6667},
+ {hostname: "irc.dal.net", port:6697, isSecure: true},
+ {hostname: "irc.au.dal.net", port:6667},
+ {hostname: "irc.eu.dal.net", port:6667},
+ {hostname: "irc.us.dal.net", port:6667}]};
+ networks["undernet"] = {
+ displayName: "undernet",
+ servers: [{hostname: "irc.undernet.org", port:6667},
+ {hostname: "eu.undernet.org", port:6667},
+ {hostname: "us.undernet.org", port:6667}]};
+ networks["webbnet"] = {
+ displayName: "webbnet",
+ servers: [{hostname: "irc.webbnet.info", port:6667}]};
+ networks["quakenet"] = {
+ displayName: "quakenet",
+ servers: [{hostname: "irc.quakenet.org", port:6667},
+ {hostname: "se.quakenet.org", port:6667},
+ {hostname: "uk.quakenet.org", port:6667},
+ {hostname: "us.quakenet.org", port:6667}]};
+ networks["ircnet"] = {
+ displayName: "ircnet",
+ servers: [{hostname: "open.ircnet.net", port:6667},
+ {hostname: "au.ircnet.org", port:6667},
+ {hostname: "eu.ircnet.org", port:6667},
+ {hostname: "us.ircnet.org", port:6667}]};
+ networks["efnet"] = {
+ displayName: "efnet",
+ servers: [{hostname: "irc.efnet.org", port: 6667}]};
+ networks["hispano"] = {
+ displayName: "hispano",
+ servers: [{hostname: "irc.irc-hispano.org", port: 6667}]};
+ networks["freenode"] = {
+ displayName: "freenode",
+ servers: [{hostname: "chat.freenode.net", port:6697, isSecure: true},
+ {hostname: "chat.freenode.net", port:7000, isSecure: true},
+ {hostname: "chat.freenode.net", port:6667}]};
+
+ for (var name in networks)
+ networks[name].name = name;
+
+ return networks;
+}
+
+function networksToNetworkList()
+{
+ var networkList = {};
+
+ // Create a networkList from client.networks.
+ for (let name in client.networks)
+ {
+ let net = client.networks[name];
+ // Skip temporary networks, as they're created to wrap standalone
+ // servers only.
+ if (net.temporary)
+ continue;
+
+ let listNet = { name: net.canonicalName, displayName: net.unicodeName,
+ servers: [] };
+
+ // Populate server list (no merging here).
+ for (let i = 0; i < net.serverList.length; i++)
+ {
+ let serv = net.serverList[i];
+ let listServ = { hostname: serv.hostname, port: serv.port,
+ isSecure: serv.isSecure };
+ listNet.servers.push(listServ);
+ }
+ networkList[net.canonicalName] = listNet;
+ }
+
+ return networkList;
+}
+
+function networksSyncFromList(networkList)
+{
+ // Copy to and update client.networks from networkList.
+ for (let name in networkList)
+ {
+ let listNet = networkList[name];
+
+ // Create new network object if necessary.
+ if (!client.getNetwork(name))
+ client.addNetwork(name, []);
+
+ // Get network object and make sure server list is empty.
+ let net = client.getNetwork(name);
+ net.clearServerList();
+
+ // Update server list.
+ for (let listServ of listNet.servers)
+ {
+ // Make sure these exist.
+ if (!("isSecure" in listServ))
+ listServ.isSecure = false;
+
+ // NOTE: this must match the name given by CIRCServer.
+ let servName = ":" + listServ.hostname + ":" + listServ.port;
+
+ if (!(servName in net.servers))
+ {
+ net.addServer(listServ.hostname, listServ.port,
+ listServ.isSecure);
+ }
+ let serv = net.servers[servName];
+
+ serv.isSecure = listServ.isSecure;
+ }
+ }
+
+ // Remove network objects that aren't in networkList.
+ for (let name in client.networks)
+ {
+ // Skip temporary networks, as they don't matter.
+ let net = client.networks[name];
+ if (net.temporary)
+ continue;
+ if (!(net.canonicalName in networkList))
+ client.removeNetwork(net.canonicalName);
+ }
+}
+
+function networksSaveList(networkList)
+{
+ var networksFile = new nsLocalFile(client.prefs["profilePath"]);
+ networksFile.append("networks.json");
+ var networksLoader = new JSONSerializer(networksFile);
+ if (networksLoader.open(">"))
+ {
+ networksLoader.serialize(Object.values(networkList));
+ networksLoader.close();
+ }
+}
+
+function networkHasSecure(serverList)
+{
+ // Test to see if the network has a secure server.
+ let hasSecure = false;
+ for (let s in serverList)
+ {
+ if (serverList[s].isSecure)
+ {
+ hasSecure = true;
+ break;
+ }
+ }
+ return hasSecure;
+}
diff --git a/comm/suite/chatzilla/xul/content/output-base.css b/comm/suite/chatzilla/xul/content/output-base.css
new file mode 100644
index 0000000000..bdf9db3f2f
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/output-base.css
@@ -0,0 +1,528 @@
+/* -*- Mode: Text; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file contains the CSS rules for the output window in ChatZilla.
+ * The output window is layed out as a table with two columns. The first
+ * column holds a message type or a nickname, depending on what view the
+ * message is contained by, and what kind of message it is. The second column
+ * contains the text of the message. For most message types, ChatZilla displays
+ * ascii-art instead of the actual code. For example, messages of type "JOIN"
+ * are displayed as "-->|", and most unclassified message types are displayed
+ * as "===". If you turn on debug messages (using the options->debug messages
+ * menuitem) ChatZilla will always show the actual message type. This can be
+ * helpful when styling a particular response from the IRC server. See the
+ * description of the msg-type attribute below.
+ *
+ * You can modify these styles on your local system by placing your desired
+ * styles in a file called chatzilla.css in your <profile>/chrome directory.
+ * (the file won't be there already, you have to create it.) Add the line
+ *
+ * @import url(chatzilla.css);
+ *
+ * to your userContent.css (also in your <profile>/chrome directory, and
+ * also not there unless you created it already.) End all CSS rules in your
+ * new chatzilla.css with !important to override any styles declared here.
+ * For example, on a Linux system, you would create a file called
+ * /home/<username>/.mozilla/<username>/chrome/userContent.css (if it
+ * doesn't already exist), and add the line @import url(chatzilla.css) to it.
+ * Next, create /home/<username>/.mozilla/<username>/chrome/chatzilla.css, and
+ * add the text:
+ *
+ * .msg {
+ * font-size: 14pt !important;
+ * }
+ *
+ * .chatzilla-body {
+ * background: green !important;
+ * }
+ *
+ * Close your browser and restart. When you bring up ChatZilla, it should have
+ * a 14pt font and a green background.
+ *
+ * To learn how to make more useful changes to the ChatZilla output style, read
+ * on.
+ *
+ * All of the output in the message window is contained in an html <TABLE>.
+ * New messages are composed of <TR> and <TD> elements inside this <TABLE>.
+ * The message table and it's children have the following CSS classes assigned
+ * to them:
+ *
+ * + .msg-table is used as the class for the surrounding <TABLE>.
+ * Styles applied to this class will affect all parts of all messages.
+ *
+ * + .msg-nested-table is used as the class for the surrounding <TABLE> for
+ * messages sent from users with long nicknames. A new table is created, and
+ * nested inside a <TR colspan="2"> of the .msg-table. The rows of this
+ * table have their classes set as if they were direct children of the
+ * .msg-table. Placing messages from users with long nicknames in a nested
+ * table keeps the nickname column from getting too wide.
+ *
+ * + .msg is used as the class for the surrounding <TR>. This means that
+ * any styles applied here will affect the entire message.
+ *
+ * + .msg-timestamp is used as the class for the <TD> that has all the time
+ * information on it. Styles on this class will affect the time stamps
+ * against messages (but not the format of the time).
+ *
+ * + .msg-type is used as the class for the <TD> surrounding the message type
+ * portion of messages. Styles applied here will only affect message
+ * types. ie. "-->|", or "[HELP]".
+ *
+ * + .msg-user is used as the class for the <TD> surrounding the nickname
+ * portion of messages. ChatZilla makes use of the :before and :after
+ * pseudoclasses to decorate nicknames with different characters depending
+ * on their message type. For example, when a user performs a /me, their
+ * nickname may be surrounded by asterisks.
+ *
+ * + .msg-data is used as the class for the <TD> surrounding the actual text
+ * of the message.
+ *
+ * In addition to CSS class properties, portions of a message may have one
+ * or mode of the following attributes set:
+ *
+ * + view-type is the type of view the message is contained in. The types
+ * are:
+ * "IRCClient" for the *client* view
+ * "IRCNetwork" for network and server views
+ * "IRCChannel" for channel views
+ * "IRCUser" for query views
+ *
+ * + msg-type is the message type described above. There are too many types
+ * to list here. Turn on debug messages to force message types to print
+ * in the left column of the output.
+ *
+ * + msg-user is the nickname (in lowercase) of the nickname who sent the
+ * message. If you sent the message, msg-user will be set to "ME!", so
+ * that you can style your messages regardless of your nickname.
+ *
+ * + msg-dest is the name of the object that the message is directed to. It
+ * could be a channel name, or a nickname (both in lowercase.)
+ *
+ * + dest-type is the type of object the message is directed to. The types
+ * are:
+ * "IRCChannel" for messages sent to a channel.
+ * "IRCUser" for messages sent directly to another user, including
+ * private messages that appear in a network or channel view (when
+ * a dedicated query view does not exist.)
+ *
+ * + mark is either the text "even" or "odd". When the first user speaks on
+ * a channel, that message is marked as "even". Messages will continue to
+ * be marked "even" until a different user speaks, when the mark switches
+ * to "odd". Each view maintains it's own mark state. An example of how
+ * ChatZilla marks messages would be:
+ *
+ * EVEN: <moe> this deep fat fry-o-later is great.
+ * EVEN: <moe> It'll deep fat fry a whole buffalo in 30 seconds.
+ * ODD: <homer> but I'm hungry *now*!
+ *
+ * + important is either the text "true", or it is not set at all. If
+ * important is true, then the message triggered ChatZilla /stalk function.
+ * This occurs when someone with a nickname matching a pattern in your
+ * /stalk list speaks, when someone says a word that matches a pattern in
+ * your /stalk list, or when someone says your nickname.
+ */
+
+#splash-wrapper {
+ display: flex;
+ height: 100vh;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+}
+
+/******************************************************************************
+ * basic classes *
+ ******************************************************************************/
+
+.chatzilla-body { /* The topmost container in the ChatZilla */
+ margin: 0px 0px 0px 0px; /* output window. */
+ background: #FFFFFF;
+ color: #000000;
+}
+
+a:link {
+ color: #0000EE;
+}
+a:active {
+ color: #EE0000;
+}
+a:visited {
+ color: #551A8B;
+}
+
+/* links */
+a.chatzilla-link {
+ text-decoration: none;
+ direction: ltr;
+ unicode-bidi: embed;
+}
+
+/* link hover effect */
+a.chatzilla-link:hover {
+ text-decoration: underline;
+}
+
+/* line marker */
+.chatzilla-line-marker {
+ box-shadow: 0px 2px black;
+}
+
+/* basic styles */
+.chatzilla-highlight[name="Large"] {
+ font-size: larger;
+}
+
+.chatzilla-highlight[name="Small"] {
+ font-size: smaller;
+}
+
+.chatzilla-highlight[name="Bold text"],
+.chatzilla-bold, a.chatzilla-bold.chatzilla-link {
+ font-weight: bold;
+}
+
+.chatzilla-italic {
+ font-style: italic;
+}
+
+/* In CSS, text-decoration is a list of decorations to apply to the text.
+ * However, as it is just one property, there is no way to apply it additively;
+ * instead, we're forced to have all the 7 combinations (of 8 - one is none).
+ */
+
+.chatzilla-overline {
+ text-decoration: overline;
+}
+
+.chatzilla-strikethrough {
+ text-decoration: line-through;
+}
+
+.chatzilla-underline,
+a.chatzilla-underline.chatzilla-link {
+ text-decoration: underline;
+}
+
+.chatzilla-overline.chatzilla-strikethrough {
+ text-decoration: overline line-through;
+}
+
+.chatzilla-overline.chatzilla-underline,
+a.chatzilla-overline.chatzilla-underline.chatzilla-link {
+ text-decoration: overline underline;
+}
+
+.chatzilla-strikethrough.chatzilla-underline,
+a.chatzilla-strikethrough.chatzilla-underline.chatzilla-link {
+ text-decoration: line-through underline;
+}
+
+.chatzilla-overline.chatzilla-strikethrough.chatzilla-underline,
+a.chatzilla-overline.chatzilla-strikethrough.chatzilla-underline.chatzilla-link {
+ text-decoration: overline line-through underline;
+}
+
+.chatzilla-teletype {
+ font-family: monospace;
+}
+
+.chatzilla-rheet {
+ font-weight: bold;
+}
+
+.chatzilla-decor {
+ display: none;
+}
+
+/* mirc colors */
+.chatzilla-fg00, a.chatzilla-fg00.chatzilla-link {
+ color: #FFFFFF;
+}
+
+.chatzilla-fg01, a.chatzilla-fg01.chatzilla-link {
+ color: #000000;
+}
+
+.chatzilla-fg02, a.chatzilla-fg02.chatzilla-link {
+ color: #00007F;
+}
+
+.chatzilla-fg03, a.chatzilla-fg03.chatzilla-link {
+ color: #009300;
+}
+
+.chatzilla-fg04, a.chatzilla-fg04.chatzilla-link {
+ color: #FF0000;
+}
+
+.chatzilla-fg05, a.chatzilla-fg05.chatzilla-link {
+ color: #7F0000;
+}
+
+.chatzilla-fg06, a.chatzilla-fg06.chatzilla-link {
+ color: #9C009C;
+}
+
+.chatzilla-fg07, a.chatzilla-fg07.chatzilla-link {
+ color: #FC7F00;
+}
+
+.chatzilla-fg08, a.chatzilla-fg08.chatzilla-link {
+ color: #FFFF00;
+}
+
+.chatzilla-fg09, a.chatzilla-fg09.chatzilla-link {
+ color: #00FC00;
+}
+
+.chatzilla-fg10, a.chatzilla-fg10.chatzilla-link {
+ color: #009393;
+}
+
+.chatzilla-fg11, a.chatzilla-fg11.chatzilla-link {
+ color: #00FFFF;
+}
+
+.chatzilla-fg12, a.chatzilla-fg12.chatzilla-link {
+ color: #0000FC;
+}
+
+.chatzilla-fg13, a.chatzilla-fg13.chatzilla-link {
+ color: #FF00FF;
+}
+
+.chatzilla-fg14, a.chatzilla-fg14.chatzilla-link {
+ color: #7F7F7F;
+}
+
+.chatzilla-fg15, a.chatzilla-fg15.chatzilla-link {
+ color: #D2D2D2;
+}
+
+.chatzilla-bg00, a.chatzilla-bg00.chatzilla-link {
+ background-color: #FFFFFF;
+}
+
+.chatzilla-bg01, a.chatzilla-bg01.chatzilla-link {
+ background-color: #000000;
+}
+
+.chatzilla-bg02, a.chatzilla-bg02.chatzilla-link {
+ background-color: #00007F;
+}
+
+.chatzilla-bg03, a.chatzilla-bg03.chatzilla-link {
+ background-color: #009300;
+}
+
+.chatzilla-bg04, a.chatzilla-bg04.chatzilla-link {
+ background-color: #FF0000;
+}
+
+.chatzilla-bg05, a.chatzilla-bg05.chatzilla-link {
+ background-color: #7F0000;
+}
+
+.chatzilla-bg06, a.chatzilla-bg06.chatzilla-link {
+ background-color: #9C009C;
+}
+
+.chatzilla-bg07, a.chatzilla-bg07.chatzilla-link {
+ background-color: #FC7F00;
+}
+
+.chatzilla-bg08, a.chatzilla-bg08.chatzilla-link {
+ background-color: #FFFF00;
+}
+
+.chatzilla-bg09, a.chatzilla-bg09.chatzilla-link {
+ background-color: #00FC00;
+}
+
+.chatzilla-bg10, a.chatzilla-bg10.chatzilla-link {
+ background-color: #009393;
+}
+
+.chatzilla-bg11, a.chatzilla-bg11.chatzilla-link {
+ background-color: #00FFFF;
+}
+
+.chatzilla-bg12, a.chatzilla-bg12.chatzilla-link {
+ background-color: #0000FC;
+}
+
+.chatzilla-bg13, a.chatzilla-bg13.chatzilla-link {
+ background-color: #FF00FF;
+}
+
+.chatzilla-bg14, a.chatzilla-bg14.chatzilla-link {
+ background-color: #7F7F7F;
+}
+
+.chatzilla-bg15, a.chatzilla-bg15.chatzilla-link {
+ background-color: #D2D2D2;
+}
+
+.chatzilla-control-char:before {
+ content: "[\\";
+}
+
+.chatzilla-control-char:after {
+ content: "]";
+}
+
+/* smiley faces */
+.chatzilla-emote-txt { /* emoticon text inside */
+ font-size: larger;
+}
+
+/******************************************************************************
+ * message class base definitions *
+ ******************************************************************************/
+
+.msg-table { /* <TABLE> containing all of the */
+ width: 100%; /* messages. */
+}
+
+.msg-nested-table { /* <TABLE> nested inside */
+ width: 100%; /* .msg-table for users with long */
+ margin: 0px; /* nicknames. */
+ border: 0px;
+ border-spacing: 0px;
+ padding: 0px;
+}
+
+.msg { /* .msg = a single message in the */
+ width: 100%; /* output window */
+}
+
+.msg-timestamp { /* .msg-timestamp = timestamp for */
+ font-style: normal !important; /* the message, done using */
+ vertical-align: top; /* :before and content. */
+ white-space: nowrap;
+}
+
+.msg-type { /* .msg-type = message type */
+ font-variant: small-caps; /* indicator */
+ font-size: 90%;
+ padding-right: 10px;
+ text-align: right;
+ vertical-align: top;
+ white-space: nowrap;
+}
+
+.msg-user { /* msg-user = nickname portion of */
+ text-align: right; /* a message (channel and query */
+ vertical-align: top; /* views) */
+ white-space: nowrap;
+}
+
+.msg-data { /* .msg-data = the text portion */
+ padding: 1px 1px 1px 3px; /* of a message */
+ width: 100%;
+ white-space: pre-wrap;
+}
+
+
+/******************************************************************************
+ * message class specific definitions *
+ ******************************************************************************/
+
+/* msg-user is the nickname of the person who spoke, or "ME!" if you said it.
+ * msg-type is the type of the message, taken from the irc message. If you
+ * turn on debug messages (options->debug messages), the msg-types will be
+ * displayed to the left of the messages for all messages except:
+ * PRIVMSG: when a user sends you, or a channel you are on a message.
+ * ACTION: when a user performs a /me.
+ * NOTIFY: when a server or user sends you a notification.
+ */
+.msg[msg-user="|"] .msg-data, /* messages from common "bulk */
+.msg[msg-user="||"] .msg-data, /* paste" nicks */
+.msg[msg-user="|||"] .msg-data,
+.msg[msg-user="]"] .msg-data,
+.msg[msg-user="["] .msg-data,
+.msg[msg-type="372"] .msg-data, /* MOTD */
+.msg[msg-type="EVAL-IN"] .msg-data, /* /eval results */
+.msg[msg-type="EVAL-OUT"] .msg-data {
+ font-size: 90%;
+ font-family: monospace;
+}
+
+.msg[msg-type="USAGE"] .msg-data {
+ font-style: italic;
+}
+
+.msg[msg-type="HELP"] .msg-data {
+ font-weight: normal;
+}
+
+.msg[msg-type="ACTION"] .msg-user {
+ font-style: italic;
+}
+
+.msg[important="true"] .msg-user {
+ font-weight: bold;
+}
+
+/******************************************************************************
+ * nickname decorations *
+ ******************************************************************************/
+
+/* :before and :after pseudoclasses form the decorations around nicknames */
+.msg-user:before {
+ content: "<";
+}
+.msg-user:after {
+ content: ">";
+}
+.msg[important="true"] .msg-user:before {
+ font-weight: bold;
+}
+.msg[important="true"] .msg-user:after {
+ font-weight: bold;
+}
+.msg[msg-user$="ME!"] .msg-user:before {
+ content: "<";
+}
+.msg[msg-user$="ME!"] .msg-user:after {
+ content: ">";
+}
+.msg[msg-type="ACTION"] .msg-user:before,
+.msg[msg-type="ACTION"] .msg-user:after {
+ content: "";
+}
+.msg[msg-type="NOTICE"] .msg-user:before {
+ content: "[";
+}
+.msg[msg-type="NOTICE"] .msg-user:after {
+ content: "]";
+}
+
+/* private messages in a query window */
+.msg[view-type="IRCUser"] .msg-user:before {
+ content: "{";
+}
+.msg[view-type="IRCUser"] .msg-user:after {
+ content: "}";
+}
+.msg[view-type="IRCUser"][msg-dest$="ME!"] .msg-user:before {
+ content: "{";
+}
+.msg[view-type="IRCUser"][msg-dest$="ME!"] .msg-user:after {
+ content: "}";
+}
+
+/* messages 'to' or 'from' somewhere other than where displayed */
+.msg[to-other] .msg-user:before {
+ content: "to(";
+}
+.msg[from-other] .msg-user:before {
+ content: "from(";
+}
+.msg[to-other] .msg-user:after,
+.msg[from-other] .msg-user:after {
+ content: ")";
+}
diff --git a/comm/suite/chatzilla/xul/content/output-window.html b/comm/suite/chatzilla/xul/content/output-window.html
new file mode 100644
index 0000000000..2155c282c3
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/output-window.html
@@ -0,0 +1,209 @@
+<!-- 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 html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style type="text/css">
+ [hidden="true"] {
+ display: none;
+ }
+
+ .header-outer {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ margin: 0px;
+ padding: 0px;
+ }
+
+ .header {
+ background-color: white;
+ color: black;
+ margin: 2px;
+ border: 1px black solid;
+ }
+
+ .h-table,
+ #net-url,
+ #ch-url,
+ #dcc-chat-title {
+ width: 100%;
+ }
+
+ #splash {
+ font-size: 24pt;
+ font-weight: bold;
+ text-align: center;
+ }
+
+ #cli-version-container {
+ text-align: center;
+ width: 100%;
+ }
+
+ #usr-descnodes,
+ #ch-topicnodes {
+ line-height: 110%;
+ }
+
+ #ch-usercount,
+ #ch-modestr,
+ #net-lag,
+ #dcc-file-progress {
+ white-space: nowrap;
+ }
+
+ .label {
+ font-weight: bold;
+ text-align: right;
+ vertical-align: top;
+ white-space: nowrap;
+ padding-right: 0.5em;
+ }
+
+ .value {
+ vertical-align: top;
+ padding-right: 1em;
+ }
+
+ #usr-title,
+ #usr-descnodes {
+ text-align: center;
+ }
+ </style>
+
+ <script type="application/x-javascript" src="chrome://chatzilla/content/output-window.js"></script>
+ </head>
+
+ <body class="chatzilla-body">
+
+ <div class="header-outer">
+
+ <div class="header" id="cli-container" hidden="true">
+ <table class="h-table">
+ <tbody>
+ <tr>
+ <td class="label" localize="output.knownnets"></td>
+ <td class="value" id="cli-netcount"></td>
+ <td class="label" id="cli-version-container"
+ condition="yellow">ChatZilla <span id="cli-version">error</span></td>
+ <td class="label" localize="output.connnets"></td>
+ <td class="value" id="cli-connectcount" localize="none"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="header" id="net-container" hidden="true">
+ <table class="h-table">
+ <tbody>
+ <tr>
+ <td class="label" id="net-url-l" localize="output.url"></td>
+ <td class="value crop-right" id="net-url">
+ <a id="net-url-anchor" class="chatzilla-link"
+ href="irc://foo/bar">irc://foo/bar</a>
+ </td>
+ <td class="value" id="net-status"
+ condition="red" localize="output.notconn"></td>
+ <td class="label" id="net-lag-l" localize="output.lag"></td>
+ <td class="value" id="net-lag" localize="unknown"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="header" id="ch-container" hidden="true">
+ <table class="h-table">
+ <tbody>
+ <tr>
+ <td class="label" id="ch-url-l" localize="output.url"></td>
+ <td class="value crop-right" id="ch-url">
+ <a id="ch-url-anchor" class="chatzilla-link"
+ href="irc://foo/bar">irc://foo/bar</a>
+ </td>
+ <td class="label" id="ch-modestr-l" localize="output.mode"></td>
+ <td class="value" id="ch-modestr" localize="none"></td>
+ <td class="label" id="ch-usercount-l" localize="output.users"></td>
+ <td class="value" id="ch-usercount" localize="none"></td>
+ </tr>
+ <tr onclick="onTopicNodesClick(event);" style="cursor:default">
+ <td class="label" id="ch-topicnodes-l" localize="output.topic"></td>
+ <td class="value" colspan="6">
+ <span id="ch-topicnodes" localize="none"></span>
+ <input hidden="true" id="ch-topicinput" style="width:90%"
+ onblur="cancelTopicEdit();"
+ onkeypress="onTopicKeypress(event);"/>
+ <input type="button" hidden="true" id="ch-topiccancel"
+ onclick="setTimeout(cancelTopicEdit, 0, true);"
+ localize="output.cancel"/>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="header" id="usr-container" hidden="true">
+ <table class="h-table">
+ <tbody>
+ <tr>
+ <td class="label" localize="output.url"></td>
+ <td class="value crop-right" width="100%">
+ <a id="usr-url-anchor" class="chatzilla-link"
+ href="irc://foo/bar">irc://foo/bar</a>
+ </td>
+ <td class="label" id="usr-serverstr-l" localize="output.via"></td>
+ <td class="value" id="usr-serverstr" localize="none"></td>
+ </tr>
+ <tr>
+ <td id="usr-title" colspan="4" localize="none"></td>
+ </tr>
+ <tr>
+ <td id="usr-descnodes" colspan="4" localize="none"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="header" id="dcc-chat-container" hidden="true">
+ <table class="h-table">
+ <tbody>
+ <tr>
+ <td id="dcc-chat-title" localize="none"></td>
+ <td class="label" id="dcc-chat-remotestr-l" localize="output.to"></td>
+ <td class="value" id="dcc-chat-remotestr" localize="none"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="header" id="dcc-file-container" hidden="true">
+ <table class="h-table">
+ <tbody>
+ <tr>
+ <td class="label" localize="output.file"></td>
+ <td class="value crop-right" id="dcc-file-file" width="100%"></td>
+ <td class="label" localize="output.progress"></td>
+ <td class="value" id="dcc-file-progress" localize="unknown"></td>
+ </tr>
+ <tr>
+ <td colspan="4" class="progress-bg">
+ <table id="dcc-file-progressbar" width="0%"><tbody><tr>
+ <td class="progress-fg">&nbsp;</td>
+ </tr></tbody></table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div id="messages-outer" hidden="true">
+ <div id="splash-wrapper"><div id="splash"></div></div>
+ <div id="output"></div>
+ </div>
+ </body>
+</html>
diff --git a/comm/suite/chatzilla/xul/content/output-window.js b/comm/suite/chatzilla/xul/content/output-window.js
new file mode 100644
index 0000000000..8f670ec3ec
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/output-window.js
@@ -0,0 +1,588 @@
+/* -*- Mode: C++; 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/. */
+
+var initialized = false;
+
+var view;
+var client;
+var mainWindow;
+var clickHandler;
+
+var dd;
+var getMsg;
+var getObjectDetails;
+
+var header = null;
+var headers = {
+ IRCClient: {
+ prefix: "cli-",
+ fields: ["container", "netcount", "version-container", "version",
+ "connectcount"],
+ update: updateClient
+ },
+
+ IRCNetwork: {
+ prefix: "net-",
+ fields: ["container", "url-anchor", "status", "lag"],
+ update: updateNetwork
+ },
+
+ IRCChannel: {
+ prefix: "ch-",
+ fields: ["container", "url-anchor", "modestr", "usercount",
+ "topicnodes", "topicinput", "topiccancel"],
+ update: updateChannel
+ },
+
+ IRCUser: {
+ prefix: "usr-",
+ fields: ["container", "url-anchor", "serverstr", "title",
+ "descnodes"],
+ update: updateUser
+ },
+
+ IRCDCCChat: {
+ prefix: "dcc-chat-",
+ fields: ["container", "remotestr", "title"],
+ update: updateDCCChat
+ },
+
+ IRCDCCFileTransfer: {
+ prefix: "dcc-file-",
+ fields: ["container", "file", "progress", "progressbar"],
+ update: updateDCCFile
+ }
+};
+
+var initOutputWindow = stock_initOutputWindow;
+
+function stock_initOutputWindow(newClient, newView, newClickHandler)
+{
+ function initHeader()
+ {
+ /* it's better if we wait a half a second before poking at these
+ * dom nodes. */
+ setHeaderState(view.prefs["displayHeader"]);
+ updateHeader();
+ var div = document.getElementById("messages-outer");
+ div.removeAttribute("hidden");
+ window.scrollTo(0, window.document.body.clientHeight);
+ };
+
+ client = newClient;
+ view = newView;
+ clickHandler = newClickHandler;
+ mainWindow = client.mainWindow;
+
+ client.messageManager.importBundle(client.defaultBundle, window);
+
+ getMsg = mainWindow.getMsg;
+ getObjectDetails = mainWindow.getObjectDetails;
+ dd = mainWindow.dd;
+
+ // Wheee... localize stuff!
+ //var nodes = document.getElementsByAttribute("localize", "*");
+ var nodes = document.getElementsByTagName("*");
+ for (var i = 0; i < nodes.length; i++)
+ {
+ if (nodes[i].hasAttribute("localize"))
+ {
+ var msg = nodes[i].getAttribute("localize");
+ msg = getMsg("msg." + msg);
+ if (nodes[i].nodeName.toLowerCase() == "input")
+ nodes[i].value = msg;
+ else
+ nodes[i].appendChild(document.createTextNode(msg));
+ }
+ }
+
+ changeCSS("chrome://chatzilla/content/output-base.css", "cz-css-base");
+ changeCSS(view.prefs["motif.current"]);
+ updateMotifSettings();
+
+ var output = document.getElementById("output");
+ output.appendChild(adoptNode(view.messages));
+
+ if (view.TYPE in headers)
+ {
+ header = cacheNodes(headers[view.TYPE].prefix,
+ headers[view.TYPE].fields);
+ // Turn off accessibility announcements: they're useless as all these
+ // changes are in the "log" as well, normally.
+ // We're setting the attribute here instead of in the HTML to cope with
+ // custom output windows and so we set it only on the Right header
+ // for this view.
+ header["container"].setAttribute("aria-live", "off");
+ header.update = headers[view.TYPE].update;
+ }
+
+ var name;
+ if ("unicodeName" in view)
+ name = view.unicodeName;
+ else
+ name = view.name;
+ updateSplash(name);
+
+ setTimeout(initHeader, 500);
+
+ initialized = true;
+}
+
+function onTopicNodesClick(e)
+{
+ if (!clickHandler(e))
+ {
+ if (e.which != 1)
+ return;
+
+ startTopicEdit();
+ }
+
+ e.stopPropagation();
+}
+
+function onTopicKeypress(e)
+{
+ switch (e.keyCode)
+ {
+ case 13: /* enter */
+ var topic = header["topicinput"].value;
+ topic = mainWindow.replaceColorCodes(topic);
+ view.setTopic(topic);
+ cancelTopicEdit(true);
+ view.dispatch("focus-input");
+ break;
+
+ case 27: /* esc */
+ cancelTopicEdit(true);
+ view.dispatch("focus-input");
+ break;
+
+ default:
+ client.mainWindow.onInputKeyPress(e);
+ }
+}
+
+function startTopicEdit()
+{
+ var me = view.getUser(view.parent.me.unicodeName);
+ if (!me || (!view.mode.publicTopic && !me.isOp && !me.isHalfOp) ||
+ !hasAttribute("topicinput", "hidden"))
+ {
+ return;
+ }
+
+ header["topicinput"].value = mainWindow.decodeColorCodes(view.topic);
+
+ header["topicnodes"].setAttribute("hidden", "true")
+ header["topicinput"].removeAttribute("hidden");
+ header["topiccancel"].removeAttribute("hidden");
+ header["topicinput"].focus();
+ header["topicinput"].selectionStart = 0;
+}
+
+function cancelTopicEdit(force)
+{
+ var originalTopic = mainWindow.decodeColorCodes(view.topic);
+ if (!hasAttribute("topicnodes", "hidden") ||
+ (!force && (header["topicinput"].value != originalTopic)))
+ {
+ return;
+ }
+
+ header["topicinput"].setAttribute("hidden", "true");
+ header["topiccancel"].setAttribute("hidden", "true");
+ header["topicnodes"].removeAttribute("hidden");
+}
+
+function cacheNodes(pfx, ary, nodes)
+{
+ if (!nodes)
+ nodes = new Object();
+
+ for (var i = 0; i < ary.length; ++i)
+ nodes[ary[i]] = document.getElementById(pfx + ary[i]);
+
+ return nodes;
+}
+
+function changeCSS(url, id)
+{
+ if (!id)
+ id = "main-css";
+
+ var node = document.getElementById(id);
+
+ if (!node)
+ {
+ node = document.createElement("link");
+ node.setAttribute("id", id);
+ node.setAttribute("rel", "stylesheet");
+ node.setAttribute("type", "text/css");
+ var head = document.getElementsByTagName("head")[0];
+ head.appendChild(node);
+ }
+ else
+ {
+ if (node.getAttribute("href") == url)
+ return;
+ }
+
+ node.setAttribute("href", url);
+ window.scrollTo(0, window.document.body.clientHeight);
+}
+
+function scrollToElement(element, position)
+{
+ /* The following values can be used for element:
+ * selection - current selected text.
+ * marker - the activity marker.
+ * [any DOM node] - anything :)
+ *
+ * The following values can be used for position:
+ * top - scroll so it is at the top.
+ * center - scroll so it is in the middle.
+ * bottom - scroll so it is at the bottom.
+ * inview - scroll so it is in view.
+ */
+ switch (element)
+ {
+ case "selection":
+ var sel = window.getSelection();
+ if (sel)
+ element = sel.anchorNode;
+ else
+ element = null;
+ break;
+
+ case "marker":
+ if ("getActivityMarker" in view)
+ element = view.getActivityMarker();
+ else
+ element = null;
+ break;
+ }
+ if (!element)
+ return;
+
+ // Calculate element's position in document.
+ var pos = { top: 0, center: 0, bottom: 0 };
+ // Find first parent with offset data.
+ while (element && !("offsetParent" in element))
+ element = element.parentNode;
+ var elt = element;
+ // Calc total offset data.
+ while (elt)
+ {
+ pos.top += 0 + elt.offsetTop;
+ elt = elt.offsetParent;
+ }
+ pos.center = pos.top + element.offsetHeight / 2;
+ pos.bottom = pos.top + element.offsetHeight;
+
+ // Store the positions to align the element with.
+ var cont = { top: 0, center: window.innerHeight / 2,
+ bottom: window.innerHeight };
+ if (!hasAttribute("container", "hidden"))
+ {
+ /* Offset height doesn't include the margins, so we get to do that
+ * ourselves via getComputedStyle(). We're assuming that will return
+ * a px value, which is all but guaranteed.
+ */
+ var headerHeight = header["container"].offsetHeight;
+ var css = getComputedStyle(header["container"], null);
+ headerHeight += parseInt(css.marginTop) + parseInt(css.marginBottom);
+ cont.top += headerHeight;
+ cont.center += headerHeight / 2;
+ }
+
+ // Pick between 'top' and 'bottom' for 'inview' position.
+ if (position == "inview")
+ {
+ if (pos.top - window.scrollY < cont.top)
+ position = "top";
+ else if (pos.bottom - window.scrollY > cont.bottom)
+ position = "bottom";
+ else
+ return;
+ }
+
+ window.scrollTo(0, pos[position] - cont[position]);
+}
+
+function updateMotifSettings(existingTimeout)
+{
+ // Try... catch with a repeat to cope with the style sheet not being loaded
+ const TIMEOUT = 100;
+ try
+ {
+ existingTimeout += TIMEOUT;
+ view.motifSettings = getMotifSettings();
+ }
+ catch(ex)
+ {
+ if (existingTimeout >= 30000) // Stop after trying for 30 seconds
+ return;
+ if (ex.name == "NS_ERROR_DOM_INVALID_ACCESS_ERR") //not ready, try again
+ setTimeout(updateMotifSettings, TIMEOUT, existingTimeout);
+ else // something else, panic!
+ dd(ex);
+ }
+}
+
+function getMotifSettings()
+{
+ var re = new RegExp("czsettings\\.(\\w*)", "i");
+ var rules = document.getElementById("main-css").sheet.cssRules;
+ var rv = new Object();
+ var ary;
+ // Copy any settings, which are available in the motif using the
+ // "CZSETTINGS" selector. We only store the regexp match after checking
+ // the rule type because selectorText is not defined on other rule types.
+ for (var i = 0; i < rules.length; i++)
+ {
+ if ((rules[i].type == CSSRule.STYLE_RULE) &&
+ ((ary = rules[i].selectorText.match(re)) != null))
+ {
+ rv[ary[1]] = true;
+ }
+ }
+ return rv;
+}
+
+function adoptNode(node)
+{
+ return client.adoptNode(node, document);
+}
+
+function setText(field, text, checkCondition)
+{
+ if (!header[field].firstChild)
+ header[field].appendChild(document.createTextNode(""));
+
+ if (typeof text != "string")
+ {
+ text = MSG_UNKNOWN;
+ if (checkCondition)
+ setAttribute(field, "condition", "red");
+ }
+ else if (checkCondition)
+ {
+ setAttribute(field, "condition", "green");
+ }
+
+ header[field].firstChild.data = text;
+}
+
+function setAttribute(field, name, value)
+{
+ if (!value)
+ value = "true";
+
+ header[field].setAttribute(name, value);
+}
+
+function removeAttribute(field, name)
+{
+ header[field].removeAttribute(name);
+}
+
+function hasAttribute(field, name)
+{
+ return header[field].hasAttribute(name);
+}
+
+function setHeaderState(state)
+{
+ if (header)
+ {
+ if (state)
+ {
+ removeAttribute("container", "hidden");
+ updateHeader();
+ }
+ else
+ {
+ setAttribute("container", "hidden");
+ }
+ }
+}
+
+function updateHeader()
+{
+ document.title = view.getURL();
+
+ if (!header || hasAttribute("container", "hidden"))
+ return;
+
+ for (var id in header)
+ {
+ var value;
+
+ if (id == "url-anchor")
+ {
+ value = view.getURL();
+ setAttribute("url-anchor", "href", value);
+ setText("url-anchor", value);
+ }
+ else if (id in view)
+ {
+ setText(id, view[id]);
+ }
+ }
+
+ if (header.update)
+ header.update();
+}
+
+function updateClient()
+{
+ var n = 0, c = 0;
+ for (name in client.networks)
+ {
+ ++n;
+ if (client.networks[name].isConnected())
+ ++c;
+ }
+
+ setAttribute("version-container", "title", client.userAgent);
+ setAttribute("version-container", "condition", mainWindow.__cz_condition);
+ setText("version", mainWindow.__cz_version);
+ setText("netcount", String(n));
+ setText("connectcount", String(c));
+}
+
+function updateNetwork()
+{
+ if (view.state == mainWindow.NET_CONNECTING)
+ {
+ setText("status", MSG_CONNECTING);
+ setAttribute("status","condition", "yellow");
+ removeAttribute("status", "title");
+ setText("lag", MSG_UNKNOWN);
+ }
+ else if (view.isConnected())
+ {
+ setText("status", MSG_CONNECTED);
+ setAttribute("status","condition", "green");
+ setAttribute("status", "title",
+ getMsg(MSG_CONNECT_VIA, view.primServ.unicodeName));
+ var lag = view.primServ.lag;
+ if (lag != -1)
+ setText("lag", getMsg(MSG_FMT_SECONDS, lag.toFixed(2)));
+ else
+ setText("lag", MSG_UNKNOWN);
+
+ }
+ else
+ {
+ setText("status", MSG_DISCONNECTED);
+ setAttribute("status","condition", "red");
+ removeAttribute("status", "title");
+ setText("lag", MSG_UNKNOWN);
+ }
+}
+
+function updateChannel()
+{
+ header["topicnodes"].removeChild(header["topicnodes"].firstChild);
+
+ if (view.active)
+ {
+ var str = view.mode.getModeStr();
+ if (!str)
+ str = MSG_NO_MODE;
+ setText("modestr", str);
+ setAttribute("modestr", "condition", "green");
+
+ setText("usercount", getMsg(MSG_FMT_USERCOUNT,
+ [view.getUsersLength(), view.opCount,
+ view.halfopCount, view.voiceCount]));
+ setAttribute("usercount", "condition", "green");
+
+ if (view.topic)
+ {
+ var data = getObjectDetails(view);
+ data.dontLogURLs = true;
+ var mailto = client.prefs["munger.mailto"];
+ client.munger.getRule(".mailto").enabled = mailto;
+ var nodes = client.munger.munge(view.topic, null, data);
+ client.munger.getRule(".mailto").enabled = false;
+ header["topicnodes"].appendChild(adoptNode(nodes));
+ }
+ else
+ {
+ setText("topicnodes", MSG_NONE);
+ }
+ }
+ else
+ {
+ setText("modestr", MSG_UNKNOWN);
+ setAttribute("modestr", "condition", "red");
+ setText("usercount", MSG_UNKNOWN);
+ setAttribute("usercount", "condition", "red");
+ setText("topicnodes", MSG_UNKNOWN);
+ }
+
+}
+
+function updateUser()
+{
+ var source;
+ if (view.name)
+ source = "<" + view.name + "@" + view.host + ">";
+ else
+ source = MSG_UNKNOWN;
+
+ if (view.parent.isConnected)
+ setText("serverstr", view.connectionHost, true);
+ else
+ setText("serverstr", null, true);
+
+ setText("title", getMsg(MSG_TITLE_USER, [view.unicodeName, source]));
+
+ header["descnodes"].removeChild(header["descnodes"].firstChild);
+ if (typeof view.desc != "undefined")
+ {
+ var data = getObjectDetails(view);
+ data.dontLogURLs = true;
+ var nodes = client.munger.munge(view.desc, null, data);
+ header["descnodes"].appendChild(adoptNode(nodes));
+ }
+ else
+ {
+ setText("descnodes", "");
+ }
+}
+
+function updateDCCChat()
+{
+ if (view.state.state == 4)
+ setText("remotestr", view.remoteIP + ":" + view.port, true);
+ else
+ setText("remotestr", null, true);
+
+ setText("title", getMsg(MSG_TITLE_DCCCHAT, view.user.unicodeName));
+}
+
+function updateDCCFile()
+{
+ var pcent = view.progress;
+
+ setText("file", view.filename);
+ setText("progress", getMsg(MSG_DCCFILE_PROGRESS,
+ [pcent, mainWindow.getSISize(view.position),
+ mainWindow.getSISize(view.size),
+ mainWindow.getSISpeed(view.speed)]));
+
+ setAttribute("progressbar", "width", pcent + "%");
+}
+
+function updateSplash(content)
+{
+ var splash = document.getElementById("splash");
+ splash.appendChild(document.createTextNode(content));
+} \ No newline at end of file
diff --git a/comm/suite/chatzilla/xul/content/popups.xul b/comm/suite/chatzilla/xul/content/popups.xul
new file mode 100644
index 0000000000..aed0200cdf
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/popups.xul
@@ -0,0 +1,124 @@
+<?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 SYSTEM "chrome://chatzilla/locale/chatzilla.dtd" >
+
+<?xml-stylesheet href="chrome://chatzilla/content/output-base.css" type="text/css"?>
+
+<overlay id="chatzilla-popup-overlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <overlaytarget id="popup-overlay-target">
+
+ <tooltip id="percentTooltip">
+ <grid>
+ <columns>
+ <column />
+ <column />
+ </columns>
+ <rows>
+ <row>
+ <label value="%U"/>
+ <label value="&Underline.label;"/>
+ </row>
+ <row>
+ <label value="%B"/>
+ <label value="&Bold.label;"/>
+ </row>
+ <row>
+ <label value="%R"/>
+ <label value="&Reverse.label;"/>
+ </row>
+ <row>
+ <label value="%O"/>
+ <label value="&Normal.label;"/>
+ </row>
+ <row>
+ <label value="%C"/>
+ <label value="&Color.label;"/>
+ </row>
+ <row>
+ <label value="%%C"/>
+ <label value="%C"/>
+ </row>
+ </rows>
+ </grid>
+ </tooltip>
+ <tooltip id="colorTooltip" orient="vertical">
+ <label value="%Cxx[,yy] &ForeBack.label;"/>
+ <grid>
+ <columns>
+ <column />
+ <column />
+ <column />
+ <column />
+ <column />
+ <column />
+ <column />
+ <column />
+ </columns>
+ <rows>
+ <row>
+ <box class="colorGrid chatzilla-bg00 chatzilla-fg01">
+ <label value="0"/>
+ </box>
+ <box class="colorGrid chatzilla-bg01 chatzilla-fg00">
+ <label value="1"/>
+ </box>
+ <box class="colorGrid chatzilla-bg02 chatzilla-fg00">
+ <label value="2"/>
+ </box>
+ <box class="colorGrid chatzilla-bg03 chatzilla-fg00">
+ <label value="3"/>
+ </box>
+ <box class="colorGrid chatzilla-bg04 chatzilla-fg00">
+ <label value="4"/>
+ </box>
+ <box class="colorGrid chatzilla-bg05 chatzilla-fg00">
+ <label value="5"/>
+ </box>
+ <box class="colorGrid chatzilla-bg06 chatzilla-fg00">
+ <label value="6"/>
+ </box>
+ <box class="colorGrid chatzilla-bg07 chatzilla-fg00">
+ <label value="7"/>
+ </box>
+ </row>
+ <row>
+ <box class="colorGrid chatzilla-bg08 chatzilla-fg01">
+ <label value="8"/>
+ </box>
+ <box class="colorGrid chatzilla-bg09 chatzilla-fg01">
+ <label value="9"/>
+ </box>
+ <box class="colorGrid chatzilla-bg10 chatzilla-fg01">
+ <label value="10"/>
+ </box>
+ <box class="colorGrid chatzilla-bg11 chatzilla-fg01">
+ <label value="11"/>
+ </box>
+ <box class="colorGrid chatzilla-bg12 chatzilla-fg01">
+ <label value="12"/>
+ </box>
+ <box class="colorGrid chatzilla-bg13 chatzilla-fg01">
+ <label value="13"/>
+ </box>
+ <box class="colorGrid chatzilla-bg14 chatzilla-fg01">
+ <label value="14"/>
+ </box>
+ <box class="colorGrid chatzilla-bg15 chatzilla-fg01">
+ <label value="15"/>
+ </box>
+ </row>
+ </rows>
+ </grid>
+ </tooltip>
+
+ </overlaytarget>
+
+</overlay>
diff --git a/comm/suite/chatzilla/xul/content/pref-irc-toolkit.xul b/comm/suite/chatzilla/xul/content/pref-irc-toolkit.xul
new file mode 100644
index 0000000000..e69fb1a165
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/pref-irc-toolkit.xul
@@ -0,0 +1,24 @@
+<?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://communicator/skin/" type="text/css"?>
+<!DOCTYPE overlay SYSTEM "chrome://chatzilla/locale/pref-irc.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="chatzilla_pane"
+ label="&pref-irc.window.title;">
+
+ <label>&pref-irc.open.desc;</label>
+ <separator/>
+ <hbox pack="center">
+ <button label="&pref-irc.open.label;" accesskey="&pref-irc.open.accesskey;"
+ oncommand="this.disabled = true;
+ window.openDialog('chrome://chatzilla/content/config.xul',
+ '', 'chrome,modal,resizable');
+ this.disabled = false;"/>
+ </hbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/chatzilla/xul/content/prefs.js b/comm/suite/chatzilla/xul/content/prefs.js
new file mode 100644
index 0000000000..f4ef4fc7de
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/prefs.js
@@ -0,0 +1,1213 @@
+/* -*- Mode: C++; 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/. */
+
+const DEFAULT_NICK = "IRCMonkey"
+
+function initPrefs()
+{
+ function makeLogNameClient()
+ {
+ return makeLogName(client, "client");
+ };
+
+ client.prefManager = new PrefManager("extensions.irc.",
+ client.defaultBundle);
+ client.prefManagers = [client.prefManager];
+
+ client.prefs = client.prefManager.prefs;
+
+ var profilePath = getSpecialDirectory("ProfD");
+ profilePath.append("chatzilla");
+
+ client.prefManager.addPref("profilePath", profilePath.path, null, null,
+ "hidden");
+
+ profilePath = new nsLocalFile(client.prefs["profilePath"]);
+
+ if (!profilePath.exists())
+ mkdir(profilePath);
+
+ client.prefManager.profilePath = profilePath;
+
+ var scriptPath = profilePath.clone();
+ scriptPath.append("scripts");
+ if (!scriptPath.exists())
+ mkdir(scriptPath);
+
+ var logPath = profilePath.clone();
+ logPath.append("logs");
+ if (!logPath.exists())
+ mkdir(logPath);
+ client.prefManager.logPath = logPath;
+
+ var downloadsPath = profilePath.clone();
+ downloadsPath.append("downloads");
+ if (!downloadsPath.exists())
+ mkdir(downloadsPath);
+
+ var logDefault = client.prefManager.logPath.clone();
+ logDefault.append(escapeFileName("client.log"));
+
+ // Set up default nickname, if possible.
+ var defaultNick = DEFAULT_NICK;
+ var en = getService("@mozilla.org/process/environment;1", "nsIEnvironment");
+ if (en)
+ {
+ /* Get the enviroment variables used by various OSes:
+ * USER - Linux, macOS and other *nix-types.
+ * USERNAME - Windows.
+ * LOGNAME - *nix again.
+ */
+ const vars = ["USER", "USERNAME", "LOGNAME"];
+
+ for (var i = 0; i < vars.length; i++)
+ {
+ var nick = en.get(vars[i]);
+ if (nick)
+ {
+ defaultNick = nick.replace(/ /g, "_");
+ break;
+ }
+ }
+ }
+
+ // Set a property so network ident prefs get the same group later:
+ client.prefManager.identGroup = ".connect";
+ // Linux and OS X won't let non-root listen on port 113.
+ if ((client.platform == "Linux") || (client.platform == "Mac"))
+ client.prefManager.identGroup = "hidden";
+
+ var prefs =
+ [
+ ["activityFlashDelay", 200, "hidden"],
+ ["alert.overlapDelay", 50, "hidden"],
+ ["alert.floodDensity", 290, "hidden"],
+ ["alert.floodDispersion", 200, "hidden"],
+ ["alert.enabled", true, ".palert"],
+ ["alert.globalEnabled", true, "global.palertconfig"],
+ ["alert.clickable", true, "hidden"],
+ ["alert.nonFocusedOnly", true, "global.palertconfig"],
+ ["alert.channel.event", false, ".palert"],
+ ["alert.channel.chat", false, ".palert"],
+ ["alert.channel.stalk", true, ".palert"],
+ ["alert.user.chat", true, ".palert"],
+ ["aliases", [], "lists.aliases"],
+ ["autoAwayCap", 300, "global"],
+ ["autoAwayPeriod", 2, "appearance.misc"],
+ ["autoMarker", false, "appearance.misc"],
+ ["autoperform.channel", [], "lists.autoperform"],
+ ["autoperform.client", [], "lists.autoperform"],
+ ["autoperform.network", [], "lists.autoperform"],
+ ["autoperform.user", ["whois"], "lists.autoperform"],
+ ["autoRejoin", false, ".connect"],
+ ["away", "", "hidden"],
+ ["awayIdleMsg", "", ".ident"],
+ ["awayIdleTime", 0, ".ident"],
+ ["awayNick", "", ".ident"],
+ ["bugKeyword", "bug", "appearance.misc"],
+ ["bugURL", "https://bugzilla.mozilla.org/show_bug.cgi?id=%s",
+ "appearance.misc"],
+ ["bugURL.comment", "#c%s", "appearance.misc"],
+ ["channelHeader", true, "global.header"],
+ ["channelLog", false, "global.log"],
+ ["channelMaxLines", 500, "global.maxLines"],
+ ["charset", "utf-8", ".connect"],
+ ["clientMaxLines", 200, "global.maxLines"],
+ ["collapseActions", true, "appearance.misc"],
+ ["collapseMsgs", false, "appearance.misc"],
+ ["conference.limit", 150, "appearance.misc"],
+ ["connectTries", -1, ".connect"],
+ ["copyMessages", true, "global"],
+ ["dcc.autoAccept.delay", 10000, "hidden"],
+ ["dcc.downloadsFolder", getURLSpecFromFile(downloadsPath.path),
+ "dcc"],
+ ["dcc.enabled", true, "dcc"],
+ ["dcc.listenPorts", [], "dcc.ports"],
+ ["dcc.useServerIP", true, "dcc"],
+ ["dccUserHeader", true, "global.header"],
+ ["dccUserLog", false, "global.log"],
+ ["dccUserMaxLines", 500, "global.maxLines"],
+ ["debugMode", "", "hidden"],
+ ["defaultQuitMsg", "", ".connect"],
+ ["deleteOnPart", true, "global"],
+ ["desc", "New Now Know How", ".ident"],
+ ["displayHeader", true, "appearance.misc"],
+ ["font.family", "default", "appearance.misc"],
+ ["font.size", 0, "appearance.misc"],
+ ["guessCommands", true, "hidden"],
+ ["hasPrefs", false, "hidden"],
+ ["identd.enabled", false, client.prefManager.identGroup],
+ ["initialScripts", ["scripts/"], "startup.initialScripts"],
+ ["initialURLs", [], "startup.initialURLs"],
+ ["inputSpellcheck", true, "global"],
+ ["log", false,
+ ".log"],
+ ["logFile.channel", "$(network)/channels/$(channel).$y-$m-$d.log",
+ "hidden"],
+ ["logFile.client", "client.$y-$m-$d.log",
+ "hidden"],
+ ["logFile.dccuser", "dcc/$(user)/$(user).$y-$m-$d.log",
+ "hidden"],
+ ["logFile.network", "$(network)/$(network).$y-$m-$d.log",
+ "hidden"],
+ ["logFile.user", "$(network)/users/$(user).$y-$m-$d.log",
+ "hidden"],
+ ["logFileName", makeLogNameClient,
+ "hidden"],
+ ["logFolder", getURLSpecFromFile(logPath.path), ".log"],
+ ["login.promptToSave", true, "global.security"],
+ ["motif.current", "chrome://chatzilla/skin/output-light.css",
+ "appearance.motif"],
+ ["motif.dark", "chrome://chatzilla/skin/output-dark.css",
+ "appearance.motif"],
+ ["motif.light", "chrome://chatzilla/skin/output-light.css",
+ "appearance.motif"],
+ ["multiline", false, "hidden"],
+ ["munger.bold", true, "munger"],
+ ["munger.bugzilla-link", true, "munger"],
+ ["munger.channel-link",true, "munger"],
+ ["munger.colorCodes", true, "munger"],
+ ["munger.ctrl-char", true, "munger"],
+ ["munger.face", true, "munger"],
+ ["munger.italic", true, "munger"],
+ ["munger.link", true, "munger"],
+ ["munger.mailto", true, "munger"],
+ ["munger.quote", true, "munger"],
+ ["munger.rheet", true, "munger"],
+ ["munger.talkback-link", true, "munger"],
+ ["munger.teletype", true, "munger"],
+ ["munger.underline", true, "munger"],
+ ["munger.word-hyphenator", true, "munger"],
+ ["networkHeader", true, "global.header"],
+ ["networkLog", false, "global.log"],
+ ["networkMaxLines", 200, "global.maxLines"],
+ ["newTabLimit", 30, "global"],
+ ["nickCompleteStr", ":", "global"],
+ ["nickname", defaultNick, ".ident"],
+ ["nicknameList", [], "lists.nicknameList"],
+ ["notify.aggressive", true, "global"],
+ ["outgoing.colorCodes", true, "global"],
+ ["outputWindowURL", "chrome://chatzilla/content/output-window.html",
+ "hidden"],
+ ["proxy.typeOverride", "", ".connect"],
+ ["reconnect", true, ".connect"],
+ ["sasl.plain.enabled", false, ".ident"],
+ ["showModeSymbols", false, "appearance.userlist"],
+ ["sortUsersByMode", true, "appearance.userlist"],
+ // Chat == "Activity" activity.
+ // Event == "Superfluous" activity.
+ // Stalk == "Attention" activity.
+ // Start == When view it opened.
+ ["sound.channel.chat", "", ".soundEvts"],
+ ["sound.channel.event", "", ".soundEvts"],
+ ["sound.channel.stalk", "beep", ".soundEvts"],
+ ["sound.channel.start", "", ".soundEvts"],
+ ["sound.enabled", true, "global.sounds"],
+ ["sound.overlapDelay", 2000, "global.sounds"],
+ ["sound.user.stalk", "beep", ".soundEvts"],
+ ["sound.user.start", "beep beep", ".soundEvts"],
+ ["stalkWholeWords", true, "lists.stalkWords"],
+ ["stalkWords", [], "lists.stalkWords"],
+ ["sts.enabled", true, ".connect"],
+ ["tabLabel", "", "hidden"],
+ ["tabGotoKeyModifiers", 0, "hidden"],
+ ["timestamps", false, "appearance.timestamps"],
+ ["timestamps.display", "[%H:%M]", "appearance.timestamps"],
+ ["timestamps.log", "[%Y-%m-%d %H:%M:%S]", "hidden"],
+ ["upgrade-insecure", false, ".connect"],
+ ["urls.display", 10, "hidden"],
+ ["urls.store.max", 100, "global"],
+ ["userHeader", true, "global.header"],
+ ["userlistLeft", true, "appearance.userlist"],
+ ["userLog", false, "global.log"],
+ ["userMaxLines", 200, "global.maxLines"],
+ ["usermode", "+i", ".ident"],
+ ["username", "chatzilla", ".ident"],
+ ["warnOnClose", true, "global"]
+ ];
+
+ client.prefManager.addPrefs(prefs);
+ client.prefManager.addObserver({ onPrefChanged: onPrefChanged });
+
+ CIRCNetwork.prototype.stayingPower = client.prefs["reconnect"];
+ CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = client.prefs["connectTries"];
+ CIRCNetwork.prototype.INITIAL_NICK = client.prefs["nickname"];
+ CIRCNetwork.prototype.INITIAL_NAME = client.prefs["username"];
+ CIRCNetwork.prototype.INITIAL_DESC = client.prefs["desc"];
+ CIRCNetwork.prototype.INITIAL_UMODE = client.prefs["usermode"];
+ CIRCNetwork.prototype.MAX_MESSAGES = client.prefs["networkMaxLines"];
+ CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE = client.prefs["proxy.typeOverride"];
+ CIRCNetwork.prototype.USE_SASL = client.prefs["sasl.plain.enabled"];
+ CIRCNetwork.prototype.UPGRADE_INSECURE = client.prefs["upgrade-insecure"];
+ CIRCNetwork.prototype.STS_MODULE.ENABLED = client.prefs["sts.enabled"];
+ CIRCChannel.prototype.MAX_MESSAGES = client.prefs["channelMaxLines"];
+ CIRCUser.prototype.MAX_MESSAGES = client.prefs["userMaxLines"];
+ CIRCDCCChat.prototype.MAX_MESSAGES = client.prefs["dccUserMaxLines"];
+ CIRCDCCFileTransfer.prototype.MAX_MESSAGES = client.prefs["dccUserMaxLines"];
+ CIRCDCC.prototype.listenPorts = client.prefs["dcc.listenPorts"];
+ client.MAX_MESSAGES = client.prefs["clientMaxLines"];
+ client.charset = client.prefs["charset"];
+
+ initAliases();
+}
+
+function makeLogName(obj, type)
+{
+ function replaceNonPrintables(ch) {
+ var rv = ch.charCodeAt().toString(16);
+ if (rv.length == 1)
+ rv = "0" + rv;
+ else if (rv.length == 3)
+ rv = "u0" + rv;
+ else if (rv.length == 4)
+ rv = "u" + rv;
+
+ return "%" + rv;
+ };
+
+ function encode(text)
+ {
+ text = text.replace(/[^-A-Z0-9_#!.,'@~\[\]{}()%$"]/gi, replaceNonPrintables);
+
+ return encodeURIComponent(text);
+ };
+
+ /* /\$\(([^)]+)\)|\$(\w)/g *
+ * <-----> <--> *
+ * longName shortName *
+ */
+ function replaceParam(match, longName, shortName)
+ {
+ if (typeof longName != "undefined" && longName)
+ {
+ // Remember to encode these, don't want some dodgy # breaking stuff.
+ if (longName in longCodes)
+ return encode(longCodes[longName]);
+
+ dd("Unknown long code: " + longName);
+ }
+ else if (typeof shortName != "undefined" && shortName)
+ {
+ if (shortName in shortCodes)
+ return encode(shortCodes[shortName]);
+
+ dd("Unknown short code: " + shortName);
+ }
+ else
+ {
+ dd("Unknown match: " + match);
+ }
+
+ return match;
+ };
+
+ var base = client.prefs["logFolder"];
+ var specific = client.prefs["logFile." + type];
+
+ // Make sure we got ourselves a slash, or we'll be in trouble with the
+ // concatenation.
+ if (!base.match(/\/$/))
+ base = base + "/";
+ var file = base + specific;
+
+ // Get details for $-replacement variables.
+ var info = getObjectDetails(obj);
+
+ // Store the most specific time short code on the object.
+ obj.smallestLogInterval = "";
+ if (file.indexOf("$y") != -1)
+ obj.smallestLogInterval = "y";
+ if (file.indexOf("$m") != -1)
+ obj.smallestLogInterval = "m";
+ if (file.indexOf("$d") != -1)
+ obj.smallestLogInterval = "d";
+ if (file.indexOf("$h") != -1)
+ obj.smallestLogInterval = "h";
+
+ // Three longs codes: $(network), $(channel) and $(user).
+ // Each is available only if appropriate for the object.
+ var longCodes = new Object();
+ if (info.network)
+ longCodes["network"] = info.network.unicodeName;
+ if (info.channel)
+ longCodes["channel"] = info.channel.unicodeName;
+ if (info.user)
+ longCodes["user"] = info.user.unicodeName;
+
+ // 4 short codes: $y, $m, $d, $h.
+ // These are time codes, each replaced with a fixed-length number.
+ var d = new Date();
+ var shortCodes = { y: padNumber(d.getFullYear(), 4),
+ m: padNumber(d.getMonth() + 1, 2),
+ d: padNumber(d.getDate(), 2),
+ h: padNumber(d.getHours(), 2)
+ };
+
+ // Replace all $-variables in one go.
+ file = file.replace(/\$\(([^)]+)\)|\$(\w)/g, replaceParam);
+
+ // Convert from file: URL to local OS format.
+ try
+ {
+ file = getFileFromURLSpec(file).path;
+ }
+ catch(ex)
+ {
+ dd("Error converting '" + base + specific + "' to a local file path.");
+ }
+
+ return file;
+}
+
+function pref_mungeName(name)
+{
+ var safeName = name.replace(/\./g, "-").replace(/:/g, "_").toLowerCase();
+ return ecmaEscape(safeName);
+}
+
+function getNetworkPrefManager(network)
+{
+ function defer(prefName)
+ {
+ return client.prefs[prefName];
+ };
+
+ function makeLogNameNetwork()
+ {
+ return makeLogName(network, "network");
+ };
+
+ function onPrefChanged(prefName, newValue, oldValue)
+ {
+ onNetworkPrefChanged (network, prefName, newValue, oldValue);
+ };
+
+ var logDefault = client.prefManager.logPath.clone();
+ logDefault.append(escapeFileName(pref_mungeName(network.encodedName)) + ".log");
+
+ var prefs =
+ [
+ ["alert.enabled", defer, ".palert"],
+ ["alert.channel.event",defer, ".palert"],
+ ["alert.channel.chat", defer, ".palert"],
+ ["alert.channel.stalk",defer, ".palert"],
+ ["alert.user.chat", defer, ".palert"],
+ ["autoAwayPeriod", defer, "appearance.misc"],
+ ["autoMarker", defer, "appearance.misc"],
+ ["autoperform", [], "lists.autoperform"],
+ ["autoRejoin", defer, ".connect"],
+ ["away", defer, "hidden"],
+ ["awayNick", defer, ".ident"],
+ ["bugURL", defer, "appearance.misc"],
+ ["bugURL.comment", defer, "appearance.misc"],
+ ["charset", defer, ".connect"],
+ ["collapseActions", defer, "appearance.misc"],
+ ["collapseMsgs", defer, "appearance.misc"],
+ ["conference.limit", defer, "appearance.misc"],
+ ["connectTries", defer, ".connect"],
+ ["dcc.autoAccept.list", [], "dcc.autoAccept"],
+ ["dcc.downloadsFolder", defer, "dcc"],
+ ["dcc.useServerIP", defer, "dcc"],
+ ["defaultQuitMsg", defer, ".connect"],
+ ["desc", defer, ".ident"],
+ ["displayHeader", client.prefs["networkHeader"], "appearance.misc"],
+ ["font.family", defer, "appearance.misc"],
+ ["font.size", defer, "appearance.misc"],
+ ["hasPrefs", false, "hidden"],
+ ["identd.enabled", defer, client.prefManager.identGroup],
+ ["ignoreList", [], "hidden"],
+ ["log", client.prefs["networkLog"], ".log"],
+ ["logFileName", makeLogNameNetwork, "hidden"],
+ ["motif.current", defer, "appearance.motif"],
+ ["nickname", defer, ".ident"],
+ ["nicknameList", defer, "lists.nicknameList"],
+ ["notifyList", [], "lists.notifyList"],
+ ["outputWindowURL", defer, "hidden"],
+ ["proxy.typeOverride", defer, ".connect"],
+ ["reconnect", defer, ".connect"],
+ ["sasl.plain.enabled", defer, ".ident"],
+ ["sound.channel.chat", defer, ".soundEvts"],
+ ["sound.channel.event", defer, ".soundEvts"],
+ ["sound.channel.stalk", defer, ".soundEvts"],
+ ["sound.channel.start", defer, ".soundEvts"],
+ ["sound.user.stalk", defer, ".soundEvts"],
+ ["sound.user.start", defer, ".soundEvts"],
+ ["tabLabel", "", "hidden"],
+ ["timestamps", defer, "appearance.timestamps"],
+ ["timestamps.display", defer, "appearance.timestamps"],
+ ["timestamps.log", defer, "hidden"],
+ ["upgrade-insecure", defer, ".connect"],
+ ["usermode", defer, ".ident"],
+ ["username", defer, ".ident"]
+ ];
+
+ var branch = "extensions.irc.networks." + pref_mungeName(network.encodedName) +
+ ".";
+ var prefManager = new PrefManager(branch, client.defaultBundle);
+ prefManager.addPrefs(prefs);
+ prefManager.addObserver({ onPrefChanged: onPrefChanged });
+ client.prefManager.addObserver(prefManager);
+
+ var value = prefManager.prefs["nickname"];
+ if (value != CIRCNetwork.prototype.INITIAL_NICK)
+ network.INITIAL_NICK = value;
+
+ value = prefManager.prefs["username"];
+ if (value != CIRCNetwork.prototype.INITIAL_NAME)
+ network.INITIAL_NAME = value;
+
+ value = prefManager.prefs["desc"];
+ if (value != CIRCNetwork.prototype.INITIAL_DESC)
+ network.INITIAL_DESC = value;
+
+ value = prefManager.prefs["usermode"];
+ if (value != CIRCNetwork.prototype.INITIAL_UMODE)
+ network.INITIAL_UMODE = value;
+
+ value = prefManager.prefs["proxy.typeOverride"];
+ if (value != CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE)
+ network.PROXY_TYPE_OVERRIDE = value;
+
+ value = prefManager.prefs["sasl.plain.enabled"];
+ if (value != CIRCNetwork.prototype.USE_SASL)
+ network.USE_SASL = value;
+
+ value = prefManager.prefs["upgrade-insecure"];
+ if (value != CIRCNetwork.prototype.UPGRADE_INSECURE)
+ network.UPGRADE_INSECURE = value;
+
+ network.stayingPower = prefManager.prefs["reconnect"];
+ network.MAX_CONNECT_ATTEMPTS = prefManager.prefs["connectTries"];
+
+ client.prefManagers.push(prefManager);
+
+ return prefManager;
+}
+
+function getChannelPrefManager(channel)
+{
+ var network = channel.parent.parent;
+
+ function defer(prefName)
+ {
+ return network.prefs[prefName];
+ };
+
+ function makeLogNameChannel()
+ {
+ return makeLogName(channel, "channel");
+ };
+
+ function onPrefChanged(prefName, newValue, oldValue)
+ {
+ onChannelPrefChanged (channel, prefName, newValue, oldValue);
+ };
+
+ var logDefault = client.prefManager.logPath.clone();
+ var filename = pref_mungeName(network.encodedName) + "," +
+ pref_mungeName(channel.encodedName);
+
+ logDefault.append(escapeFileName(filename) + ".log");
+
+ var prefs =
+ [
+ ["alert.enabled", defer, ".palert"],
+ ["alert.channel.event",defer, ".palert"],
+ ["alert.channel.chat", defer, ".palert"],
+ ["alert.channel.stalk",defer, ".palert"],
+ ["autoperform", [], "lists.autoperform"],
+ ["autoRejoin", defer, ".connect"],
+ ["autoMarker", defer, "appearance.misc"],
+ ["bugURL", defer, "appearance.misc"],
+ ["bugURL.comment", defer, "appearance.misc"],
+ ["charset", defer, ".connect"],
+ ["collapseActions", defer, "appearance.misc"],
+ ["collapseMsgs", defer, "appearance.misc"],
+ ["conference.enabled", false, "hidden"],
+ ["conference.limit", defer, "appearance.misc"],
+ ["displayHeader", client.prefs["channelHeader"], "appearance.misc"],
+ ["font.family", defer, "appearance.misc"],
+ ["font.size", defer, "appearance.misc"],
+ ["hasPrefs", false, "hidden"],
+ ["log", client.prefs["channelLog"], ".log"],
+ ["logFileName", makeLogNameChannel, "hidden"],
+ ["motif.current", defer, "appearance.motif"],
+ ["outputWindowURL", defer, "hidden"],
+ ["sound.channel.chat", defer, ".soundEvts"],
+ ["sound.channel.event", defer, ".soundEvts"],
+ ["sound.channel.stalk", defer, ".soundEvts"],
+ ["sound.channel.start", defer, ".soundEvts"],
+ ["tabLabel", "", "hidden"],
+ ["timestamps", defer, "appearance.timestamps"],
+ ["timestamps.display", defer, "appearance.timestamps"],
+ ["timestamps.log", defer, "hidden"]
+ ];
+
+ var branch = "extensions.irc.networks." + pref_mungeName(network.encodedName) +
+ ".channels." + pref_mungeName(channel.encodedName) + "."
+ var prefManager = new PrefManager(branch, client.defaultBundle);
+ prefManager.addPrefs(prefs);
+ prefManager.addObserver({ onPrefChanged: onPrefChanged });
+ network.prefManager.addObserver(prefManager);
+
+ client.prefManagers.push(prefManager);
+
+ return prefManager;
+}
+
+function getUserPrefManager(user)
+{
+ var network = user.parent.parent;
+
+ function defer(prefName)
+ {
+ return network.prefs[prefName];
+ };
+
+ function makeLogNameUser()
+ {
+ return makeLogName(user, "user");
+ };
+
+ function onPrefChanged(prefName, newValue, oldValue)
+ {
+ onUserPrefChanged (user, prefName, newValue, oldValue);
+ };
+
+ var logDefault = client.prefManager.logPath.clone();
+ var filename = pref_mungeName(network.encodedName);
+ filename += "," + pref_mungeName(user.encodedName);
+ logDefault.append(escapeFileName(filename) + ".log");
+
+ var prefs =
+ [
+ ["alert.enabled", defer, ".palert"],
+ ["alert.user.chat", defer, ".palert"],
+ ["autoperform", [], "lists.autoperform"],
+ ["charset", defer, ".connect"],
+ ["collapseActions", defer, "appearance.misc"],
+ ["collapseMsgs", defer, "appearance.misc"],
+ ["displayHeader", client.prefs["userHeader"], "appearance.misc"],
+ ["font.family", defer, "appearance.misc"],
+ ["font.size", defer, "appearance.misc"],
+ ["hasPrefs", false, "hidden"],
+ ["log", client.prefs["userLog"], ".log"],
+ ["logFileName", makeLogNameUser, "hidden"],
+ ["motif.current", defer, "appearance.motif"],
+ ["outputWindowURL", defer, "hidden"],
+ ["sound.user.stalk", defer, ".soundEvts"],
+ ["sound.user.start", defer, ".soundEvts"],
+ ["tabLabel", "", "hidden"],
+ ["timestamps", defer, "appearance.timestamps"],
+ ["timestamps.display", defer, "appearance.timestamps"],
+ ["timestamps.log", defer, "hidden"]
+ ];
+
+ var branch = "extensions.irc.networks." + pref_mungeName(network.encodedName) +
+ ".users." + pref_mungeName(user.encodedName) + ".";
+ var prefManager = new PrefManager(branch, client.defaultBundle);
+ prefManager.addPrefs(prefs);
+ prefManager.addObserver({ onPrefChanged: onPrefChanged });
+ network.prefManager.addObserver(prefManager);
+
+ client.prefManagers.push(prefManager);
+
+ return prefManager;
+}
+
+function getDCCUserPrefManager(user)
+{
+ function defer(prefName)
+ {
+ return client.prefs[prefName];
+ };
+
+ function makeLogNameUser()
+ {
+ return makeLogName(user, "dccuser");
+ };
+
+ function onPrefChanged(prefName, newValue, oldValue)
+ {
+ onDCCUserPrefChanged(user, prefName, newValue, oldValue);
+ };
+
+ var prefs =
+ [
+ ["alert.enabled", defer, ".palert"],
+ ["alert.user.chat", defer, ".palert"],
+ ["charset", defer, ".connect"],
+ ["collapseMsgs", defer, "appearance.misc"],
+ ["displayHeader", client.prefs["dccUserHeader"], "appearance.misc"],
+ ["font.family", defer, "appearance.misc"],
+ ["font.size", defer, "appearance.misc"],
+ ["hasPrefs", false, "hidden"],
+ ["log", client.prefs["dccUserLog"], ".log"],
+ ["logFileName", makeLogNameUser, "hidden"],
+ ["motif.current", defer, "appearance.motif"],
+ ["outputWindowURL", defer, "hidden"],
+ ["tabLabel", "", "hidden"],
+ ["timestamps", defer, "appearance.timestamps"],
+ ["timestamps.display", defer, "appearance.timestamps"],
+ ["timestamps.log", defer, "hidden"]
+ ];
+
+ var branch = "extensions.irc.dcc.users." +
+ pref_mungeName(user.canonicalName) + ".";
+ var prefManager = new PrefManager(branch, client.defaultBundle);
+ prefManager.addPrefs(prefs);
+ prefManager.addObserver({ onPrefChanged: onPrefChanged });
+ client.prefManager.addObserver(prefManager);
+
+ client.prefManagers.push(prefManager);
+
+ return prefManager;
+}
+
+function destroyPrefs()
+{
+ if ("prefManagers" in client)
+ {
+ for (var i = 0; i < client.prefManagers.length; ++i)
+ client.prefManagers[i].destroy();
+ client.prefManagers = [];
+ }
+}
+
+function onPrefChanged(prefName, newValue, oldValue)
+{
+ if (newValue == oldValue)
+ return;
+
+ switch (prefName)
+ {
+ case "awayIdleTime":
+ uninitIdleAutoAway(oldValue);
+ initIdleAutoAway(newValue);
+ break;
+
+ case "bugKeyword":
+ client.munger.delRule("bugzilla-link");
+ addBugzillaLinkMungerRule(newValue, 10, 10);
+ break;
+
+ case "channelMaxLines":
+ CIRCChannel.prototype.MAX_MESSAGES = newValue;
+ break;
+
+ case "charset":
+ client.charset = newValue;
+ break;
+
+ case "clientMaxLines":
+ client.MAX_MESSAGES = newValue;
+ break;
+
+ case "connectTries":
+ CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = newValue;
+ break;
+
+ case "dcc.listenPorts":
+ CIRCDCC.prototype.listenPorts = newValue;
+ break;
+
+ case "dccUserMaxLines":
+ CIRCDCCFileTransfer.prototype.MAX_MESSAGES = newValue;
+ CIRCDCCChat.prototype.MAX_MESSAGES = newValue;
+ break;
+
+ case "font.family":
+ case "font.size":
+ client.dispatch("sync-font");
+ break;
+
+ case "proxy.typeOverride":
+ CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE = newValue;
+ break;
+
+ case "reconnect":
+ CIRCNetwork.prototype.stayingPower = newValue;
+ break;
+
+ case "showModeSymbols":
+ if (newValue)
+ setListMode("symbol");
+ else
+ setListMode("graphic");
+ break;
+
+ case "sasl.plain.enabled":
+ CIRCNetwork.prototype.USE_SASL = newValue;
+ break;
+
+ case "upgrade-insecure":
+ CIRCNetwork.prototype.UPGRADE_INSECURE = newValue;
+ break;
+
+ case "sts.enabled":
+ CIRCNetwork.prototype.STS_MODULE.ENABLED = newValue;
+ break;
+
+ case "nickname":
+ CIRCNetwork.prototype.INITIAL_NICK = newValue;
+ break;
+
+ case "username":
+ CIRCNetwork.prototype.INITIAL_NAME = newValue;
+ break;
+
+ case "usermode":
+ CIRCNetwork.prototype.INITIAL_UMODE = newValue;
+ break;
+
+ case "userMaxLines":
+ CIRCUser.prototype.MAX_MESSAGES = newValue;
+ break;
+
+ case "userlistLeft":
+ updateUserlistSide(newValue);
+ break;
+
+ case "debugMode":
+ setDebugMode(newValue);
+ break;
+
+ case "desc":
+ CIRCNetwork.prototype.INITIAL_DESC = newValue;
+ break;
+
+ case "stalkWholeWords":
+ case "stalkWords":
+ updateAllStalkExpressions();
+ break;
+
+ case "sortUsersByMode":
+ if (client.currentObject.TYPE == "IRCChannel")
+ updateUserList();
+
+ case "motif.current":
+ client.dispatch("sync-motif");
+ break;
+
+ case "multiline":
+ multilineInputMode(newValue);
+ delete client.multiLineForPaste;
+ break;
+
+ case "munger.colorCodes":
+ client.enableColors = newValue;
+ break;
+
+ case "networkMaxLines":
+ CIRCNetwork.prototype.MAX_MESSAGES = newValue;
+ break;
+
+ case "outputWindowURL":
+ client.dispatch("sync-window");
+ break;
+
+ case "displayHeader":
+ client.dispatch("sync-header");
+ break;
+
+ case "tabLabel":
+ onTabLabelUpdate(client, newValue);
+ break;
+
+ case "timestamps":
+ case "timestamps.display":
+ case "collapseActions":
+ case "collapseMsgs":
+ client.dispatch("sync-timestamp");
+ break;
+
+ case "log":
+ client.dispatch("sync-log");
+ break;
+
+ case "alert.globalEnabled":
+ updateAlertIcon(false);
+ break;
+
+ case "alert.floodDensity":
+ if (client.alert && client.alert.floodProtector)
+ client.alert.floodProtector.floodDensity = newValue;
+ break;
+
+ case "alert.floodDispersion":
+ if (client.alert && client.alert.floodProtector)
+ client.alert.floodProtector.floodDispersion = newValue;
+ break;
+
+ case "aliases":
+ updateAliases();
+ break;
+
+ case "inputSpellcheck":
+ updateSpellcheck(newValue);
+ break;
+
+ case "urls.store.max":
+ if (client.urlLogger)
+ {
+ client.urlLogger.autoLimit = newValue;
+ client.urlLogger.limit(newValue);
+ }
+ break;
+
+ default:
+ // Make munger prefs apply without a restart
+ var m, rule;
+ if ((m = prefName.match(/^munger\.(\S+)$/)) &&
+ (rule = client.munger.getRule(m[1])))
+ {
+ rule.enabled = newValue;
+ }
+ }
+}
+
+function onNetworkPrefChanged(network, prefName, newValue, oldValue)
+{
+ if (network != client.networks[network.collectionKey])
+ {
+ /* this is a stale observer, remove it */
+ network.prefManager.destroy();
+ return;
+ }
+
+ if (newValue == oldValue)
+ return;
+
+ network.updateHeader();
+
+ switch (prefName)
+ {
+ case "nickname":
+ network.INITIAL_NICK = newValue;
+ break;
+
+ case "username":
+ network.INITIAL_NAME = newValue;
+ break;
+
+ case "usermode":
+ network.INITIAL_UMODE = newValue;
+ if (network.isConnected())
+ {
+ network.primServ.sendData("mode " + network.server.me + " :" +
+ newValue + "\n");
+ }
+ break;
+
+ case "desc":
+ network.INITIAL_DESC = newValue;
+ break;
+
+ case "proxy.typeOverride":
+ network.PROXY_TYPE_OVERRIDE = newValue;
+ break;
+
+ case "reconnect":
+ network.stayingPower = newValue;
+ break;
+
+ case "font.family":
+ case "font.size":
+ network.dispatch("sync-font");
+ break;
+
+ case "motif.current":
+ network.dispatch("sync-motif");
+ break;
+
+ case "notifyList":
+ if (!network.primServ.supports["monitor"])
+ break;
+ var adds = newValue.filter((el) =>
+ { return oldValue.indexOf(el) < 0; });
+ var subs = oldValue.filter((el) =>
+ { return newValue.indexOf(el) < 0; });
+ if (adds.length > 0)
+ network.primServ.sendMonitorList(adds, true);
+ if (subs.length > 0)
+ network.primServ.sendMonitorList(subs, false);
+ break;
+
+ case "outputWindowURL":
+ network.dispatch("sync-window");
+ break;
+
+ case "displayHeader":
+ network.dispatch("sync-header");
+ break;
+
+ case "tabLabel":
+ onTabLabelUpdate(network, newValue);
+ break;
+
+ case "timestamps":
+ case "timestamps.display":
+ case "collapseActions":
+ case "collapseMsgs":
+ network.dispatch("sync-timestamp");
+ break;
+
+ case "log":
+ network.dispatch("sync-log");
+ break;
+
+ case "connectTries":
+ network.MAX_CONNECT_ATTEMPTS = newValue;
+ break;
+
+ case "sasl.plain.enabled":
+ network.USE_SASL = newValue;
+ break;
+
+ case "upgrade-insecure":
+ network.UPGRADE_INSECURE = newValue;
+ break;
+ }
+}
+
+function onChannelPrefChanged(channel, prefName, newValue, oldValue)
+{
+ var network = channel.parent.parent;
+
+ if (network != client.networks[network.collectionKey] ||
+ channel.parent != network.primServ ||
+ channel != network.primServ.channels[channel.collectionKey])
+ {
+ /* this is a stale observer, remove it */
+ channel.prefManager.destroy();
+ return;
+ }
+
+ if (newValue == oldValue)
+ return;
+
+ channel.updateHeader();
+
+ switch (prefName)
+ {
+ case "conference.enabled":
+ // Wouldn't want to display a message to a hidden view.
+ if ("messages" in channel)
+ {
+ if (newValue)
+ channel.display(MSG_CONF_MODE_ON);
+ else
+ channel.display(MSG_CONF_MODE_OFF);
+ }
+ break;
+
+ case "conference.limit":
+ channel._updateConferenceMode();
+ break;
+
+ case "font.family":
+ case "font.size":
+ channel.dispatch("sync-font");
+ break;
+
+ case "motif.current":
+ channel.dispatch("sync-motif");
+ break;
+
+ case "outputWindowURL":
+ channel.dispatch("sync-window");
+ break;
+
+ case "displayHeader":
+ channel.dispatch("sync-header");
+ break;
+
+ case "tabLabel":
+ onTabLabelUpdate(channel, newValue);
+ break;
+
+ case "timestamps":
+ case "timestamps.display":
+ case "collapseActions":
+ case "collapseMsgs":
+ channel.dispatch("sync-timestamp");
+ break;
+
+ case "log":
+ channel.dispatch("sync-log");
+ break;
+ }
+}
+
+function onUserPrefChanged(user, prefName, newValue, oldValue)
+{
+ var network = user.parent.parent;
+
+ if (network != client.networks[network.collectionKey] ||
+ user.parent != network.primServ ||
+ user != network.primServ.users[user.collectionKey])
+ {
+ /* this is a stale observer, remove it */
+ user.prefManager.destroy();
+ return;
+ }
+
+ if (newValue == oldValue)
+ return;
+
+ user.updateHeader();
+
+ switch (prefName)
+ {
+ case "font.family":
+ case "font.size":
+ user.dispatch("sync-font");
+ break;
+
+ case "motif.current":
+ user.dispatch("sync-motif");
+ break;
+
+ case "outputWindowURL":
+ user.dispatch("sync-window");
+ break;
+
+ case "displayHeader":
+ user.dispatch("sync-header");
+ break;
+
+ case "tabLabel":
+ onTabLabelUpdate(user, newValue);
+ break;
+
+ case "timestamps":
+ case "timestamps.display":
+ case "collapseActions":
+ case "collapseMsgs":
+ user.dispatch("sync-timestamp");
+ break;
+
+ case "log":
+ user.dispatch("sync-log");
+ break;
+ }
+}
+
+function onDCCUserPrefChanged(user, prefName, newValue, oldValue)
+{
+ if (client.dcc.users[user.key] != user)
+ {
+ /* this is a stale observer, remove it */
+ user.prefManager.destroy();
+ return;
+ }
+
+ if (newValue == oldValue)
+ return;
+
+ // DCC Users are a pain, they can have multiple views!
+ function updateDCCView(view)
+ {
+ switch (prefName)
+ {
+ case "font.family":
+ case "font.size":
+ view.dispatch("sync-font");
+ break;
+
+ case "motif.current":
+ view.dispatch("sync-motif");
+ break;
+
+ case "outputWindowURL":
+ view.dispatch("sync-window");
+ break;
+
+ case "displayHeader":
+ view.dispatch("sync-header");
+ break;
+
+ case "tabLabel":
+ onTabLabelUpdate(user, newValue);
+ break;
+
+ case "timestamps":
+ case "timestamps.display":
+ case "collapseActions":
+ case "collapseMsgs":
+ view.dispatch("sync-timestamp");
+ break;
+
+ case "log":
+ view.dispatch("sync-log");
+ break;
+ }
+ };
+
+ for (var i = 0; client.dcc.chats.length; i++)
+ {
+ var chat = client.dcc.chats[i];
+ if (chat.user == user)
+ updateDCCView(chat);
+ }
+}
+
+function initAliases()
+{
+ client.commandManager.aliasList = new Object();
+ updateAliases();
+}
+
+function updateAliases()
+{
+ var aliasDefs = client.prefs["aliases"];
+
+ // Flag all aliases as 'removed' first.
+ for (var name in client.commandManager.aliasList)
+ client.commandManager.aliasList[name] = false;
+
+ for (var i = 0; i < aliasDefs.length; ++i)
+ {
+ var ary = aliasDefs[i].match(/^(.*?)\s*=\s*(.*)$/);
+ if (ary)
+ {
+ var name = ary[1];
+ var list = ary[2];
+
+ // Remove the alias, if it exists, or we'll keep stacking them.
+ if (name in client.commandManager.aliasList)
+ client.commandManager.removeCommand({name: name});
+ client.commandManager.defineCommand(name, list);
+ // Flag this alias as 'used'.
+ client.commandManager.aliasList[name] = true;
+ }
+ else
+ {
+ dd("Malformed alias: " + aliasDefs[i]);
+ }
+ }
+
+ // Purge any aliases that were defined but are no longer in the pref.
+ for (var name in client.commandManager.aliasList)
+ {
+ if (!client.commandManager.aliasList[name])
+ {
+ client.commandManager.removeCommand({name: name});
+ delete client.commandManager.aliasList[name];
+ }
+ }
+}
+
+function onTabLabelUpdate(sourceObject, newValue)
+{
+ var tab = getTabForObject(sourceObject);
+ if (tab)
+ {
+ tab.label = newValue || sourceObject.viewName;
+ tab.setAttribute("tooltiptext", sourceObject.viewName);
+ }
+}
+
diff --git a/comm/suite/chatzilla/xul/content/prefsOverlay.xul b/comm/suite/chatzilla/xul/content/prefsOverlay.xul
new file mode 100644
index 0000000000..946299216f
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/prefsOverlay.xul
@@ -0,0 +1,31 @@
+<?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 SYSTEM "chrome://chatzilla/locale/pref-irc.dtd">
+
+<overlay id="ovCZPrefs"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <treechildren id="prefsPanelChildren">
+ <treeitem id="chatzillaItem"
+ insertbefore="securityItem"
+ label="&pref-irc.window.title;"
+ prefpane="chatzilla_pane"
+ url="chrome://chatzilla/content/pref-irc-toolkit.xul"/>
+ </treechildren>
+
+ <preferences id="appearance_preferences">
+ <preference id="general.startup.chat"
+ name="general.startup.chat"
+ type="bool"/>
+ </preferences>
+ <!-- Startup checkbox -->
+ <groupbox id="generalStartupPreferences">
+ <checkbox id="generalStartupChat" label="&startup.chat.label;"
+ accesskey="&startup.chat.accesskey;" pref="true" preftype="bool"
+ prefstring="general.startup.chat" prefattribute="checked"
+ wsm_persist="true" preference="general.startup.chat"/>
+ </groupbox>
+</overlay>
diff --git a/comm/suite/chatzilla/xul/content/scripts.xul b/comm/suite/chatzilla/xul/content/scripts.xul
new file mode 100644
index 0000000000..6207970a07
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/scripts.xul
@@ -0,0 +1,55 @@
+<?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/. -->
+
+<overlay id="chatzilla-scripts-overlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <overlaytarget id="scripts-overlay-target">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+
+ <script><![CDATA[
+ /* utils.js overloads the standard JS messages with prompt service
+ * calls, which require the locale support to have loaded. This next
+ * line is needed so that the onLoad function in handlers.js can
+ * display the "error loading ChatZilla" message even if the locale
+ * support is what failed to load.
+ */
+ window.baseAlert = window.alert;
+ ]]></script>
+ <script src="chrome://chatzilla/content/lib/js/utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/connection-xpcom.js"/>
+ <script src="chrome://chatzilla/content/lib/js/events.js"/>
+ <script src="chrome://chatzilla/content/lib/js/command-manager.js"/>
+ <script src="chrome://chatzilla/content/lib/js/pref-manager.js"/>
+ <script src="chrome://chatzilla/content/lib/js/message-manager.js"/>
+ <script src="chrome://chatzilla/content/lib/js/menu-manager.js"/>
+ <script src="chrome://chatzilla/content/lib/js/irc.js"/>
+ <script src="chrome://chatzilla/content/lib/js/irc-debug.js"/>
+ <script src="chrome://chatzilla/content/lib/js/file-utils.js"/>
+ <script src="chrome://chatzilla/content/lib/js/dcc.js"/>
+ <script src="chrome://chatzilla/content/lib/js/ident.js"/>
+ <script src="chrome://chatzilla/content/lib/js/json-serializer.js"/>
+ <script src="chrome://chatzilla/content/lib/js/sts.js"/>
+ <script src="chrome://chatzilla/content/lib/js/text-serializer.js"/>
+ <script src="chrome://chatzilla/content/lib/js/text-logger.js"/>
+ <script src="chrome://chatzilla/content/lib/xul/munger.js"/>
+ <script src="chrome://chatzilla/content/lib/xul/tree-utils.js"/>
+
+ <script src="chrome://chatzilla/content/static.js"/>
+ <script src="chrome://chatzilla/content/commands.js"/>
+ <script src="chrome://chatzilla/content/menus.js"/>
+ <script src="chrome://chatzilla/content/prefs.js"/>
+ <script src="chrome://chatzilla/content/messages.js"/>
+ <script src="chrome://chatzilla/content/mungers.js"/>
+ <script src="chrome://chatzilla/content/handlers.js"/>
+ <script src="chrome://chatzilla/content/networks.js"/>
+
+ </overlaytarget>
+
+</overlay>
diff --git a/comm/suite/chatzilla/xul/content/static.js b/comm/suite/chatzilla/xul/content/static.js
new file mode 100644
index 0000000000..8ee7753210
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/static.js
@@ -0,0 +1,5639 @@
+/* -*- Mode: C++; 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/. */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+ "resource://gre/modules/AppConstants.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+
+#expand const __cz_version = "__CHATZILLA_VERSION__";
+const __cz_condition = "green";
+
+var warn;
+var ASSERT;
+var TEST;
+
+if (DEBUG)
+{
+ _dd_pfx = "cz: ";
+ warn = function (msg) { dumpln ("** WARNING " + msg + " **"); }
+ TEST = ASSERT = function _assert(expr, msg) {
+ if (!expr) {
+ dd("** ASSERTION FAILED: " + msg + " **\n" +
+ getStackTrace() + "\n");
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+else
+ dd = warn = TEST = ASSERT = function (){};
+
+var client = new Object();
+
+client.TYPE = "IRCClient";
+client.COMMAND_CHAR = "/";
+client.STEP_TIMEOUT = 500;
+client.MAX_MESSAGES = 200;
+client.MAX_HISTORY = 50;
+/* longest nick to show in display before forcing the message to a block level
+ * element */
+client.MAX_NICK_DISPLAY = 14;
+/* longest word to show in display before abbreviating */
+client.MAX_WORD_DISPLAY = 20;
+
+client.NOTIFY_TIMEOUT = 5 * 60 * 1000; /* update notify list every 5 minutes */
+
+// Check every minute which networks have away statuses that need an update.
+client.AWAY_TIMEOUT = 60 * 1000;
+
+client.SLOPPY_NETWORKS = true; /* true if msgs from a network can be displayed
+ * on the current object if it is related to
+ * the network (ie, /whois results will appear
+ * on the channel you're viewing, if that channel
+ * is on the network that the results came from)
+ */
+client.DOUBLETAB_TIME = 500;
+client.HIDE_CODES = true; /* true if you'd prefer to show numeric response
+ * codes as some default value (ie, "===") */
+client.DEFAULT_RESPONSE_CODE = "===";
+
+/* Maximum number of channels we'll try to list without complaining */
+client.SAFE_LIST_COUNT = 500;
+
+/* Minimum number of users above or below the conference limit the user count
+ * must go, before it is changed. This allows the user count to fluctuate
+ * around the limit without continously going on and off.
+ */
+client.CONFERENCE_LOW_PASS = 10;
+
+client.viewsArray = new Array();
+client.activityList = new Object();
+client.hostCompat = new Object();
+client.inputHistory = new Array();
+client.lastHistoryReferenced = -1;
+client.incompleteLine = "";
+client.lastTabUp = new Date();
+client.awayMsgs = new Array();
+client.awayMsgCount = 5;
+client.statusMessages = new Array();
+
+CIRCNetwork.prototype.INITIAL_CHANNEL = "";
+CIRCNetwork.prototype.STS_MODULE = new CIRCSTS();
+CIRCNetwork.prototype.MAX_MESSAGES = 100;
+CIRCNetwork.prototype.IGNORE_MOTD = false;
+CIRCNetwork.prototype.RECLAIM_WAIT = 15000;
+CIRCNetwork.prototype.RECLAIM_TIMEOUT = 400000;
+CIRCNetwork.prototype.MIN_RECONNECT_MS = 15 * 1000; // 15s
+CIRCNetwork.prototype.MAX_RECONNECT_MS = 2 * 60 * 60 * 1000; // 2h
+
+CIRCServer.prototype.READ_TIMEOUT = 0;
+CIRCServer.prototype.PRUNE_OLD_USERS = 0; // prune on user quit.
+
+CIRCUser.prototype.MAX_MESSAGES = 200;
+
+CIRCChannel.prototype.MAX_MESSAGES = 300;
+
+function init()
+{
+ if (("initialized" in client) && client.initialized)
+ return;
+
+ client.initialized = false;
+
+ client.networks = new Object();
+ client.entities = new Object();
+ client.eventPump = new CEventPump (200);
+
+ if (DEBUG)
+ {
+ /* hook all events EXCEPT server.poll and *.event-end types
+ * (the 4th param inverts the match) */
+ client.debugHook =
+ client.eventPump.addHook([{type: "poll", set:/^(server|dcc-chat)$/},
+ {type: "event-end"}], event_tracer,
+ "event-tracer", true /* negate */,
+ false /* disable */);
+ }
+
+ initApplicationCompatibility();
+ initMessages();
+
+ initCommands();
+ initPrefs();
+ initMunger();
+ initNetworks();
+ initMenus();
+ initStatic();
+ initHandlers();
+
+ // Create DCC handler.
+ client.dcc = new CIRCDCC(client);
+
+ client.ident = new IdentServer(client);
+
+ // Initialize the STS module.
+ var stsFile = new nsLocalFile(client.prefs["profilePath"]);
+ stsFile.append("sts.json");
+
+ client.sts = CIRCNetwork.prototype.STS_MODULE;
+ client.sts.init(stsFile);
+ client.sts.ENABLED = client.prefs["sts.enabled"];
+
+ // Start log rotation checking first. This will schedule the next check.
+ checkLogFiles();
+ // Start logging. Nothing should call display() before this point.
+ if (client.prefs["log"])
+ client.openLogFile(client);
+
+ // Make sure the userlist is on the correct side.
+ updateUserlistSide(client.prefs["userlistLeft"]);
+
+ client.display(MSG_WELCOME, "HELLO");
+ client.dispatch("set-current-view", { view: client });
+
+ /*
+ * Due to Firefox 44 changes regarding ES6 lexical scope, these 'const'
+ * items are no longer accessible from the global object ('window') but
+ * are required by the output window. The compromise is to copy them on
+ * to the global object so they can be used.
+ */
+ window.__cz_version = __cz_version;
+ window.__cz_condition = __cz_condition;
+ window.NET_CONNECTING = NET_CONNECTING;
+
+ importFromFrame("updateHeader");
+ importFromFrame("setHeaderState");
+ importFromFrame("changeCSS");
+ importFromFrame("scrollToElement");
+ importFromFrame("updateMotifSettings");
+ importFromFrame("addUsers");
+ importFromFrame("updateUsers");
+ importFromFrame("removeUsers");
+
+ processStartupScripts();
+
+ client.commandManager.installKeys(document);
+ createMenus();
+
+ client.busy = false;
+ updateProgress();
+ initOfflineIcon();
+ updateAlertIcon(false);
+ client.isIdleAway = false;
+ initIdleAutoAway(client.prefs["awayIdleTime"]);
+
+ client.initialized = true;
+
+ dispatch("help", { hello: true });
+ dispatch("networks");
+
+ setTimeout(function() {
+ dispatch("focus-input");
+ }, 0);
+ setTimeout(processStartupAutoperform, 0);
+ setTimeout(processStartupURLs, 0);
+}
+
+function initStatic()
+{
+ client.mainWindow = window;
+
+ try
+ {
+ const nsISound = Components.interfaces.nsISound;
+ client.sound =
+ Components.classes["@mozilla.org/sound;1"].createInstance(nsISound);
+
+ client.soundList = new Object();
+ }
+ catch (ex)
+ {
+ dd("Sound failed to initialize: " + ex);
+ }
+
+ try
+ {
+ const nsIAlertsService = Components.interfaces.nsIAlertsService;
+ client.alert = new Object();
+ client.alert.service =
+ Components.classes["@mozilla.org/alerts-service;1"].getService(nsIAlertsService);
+ client.alert.alertList = new Object();
+ client.alert.floodProtector = new FloodProtector(
+ client.prefs['alert.floodDensity'],
+ client.prefs['alert.floodDispersion']);
+ }
+ catch (ex)
+ {
+ dd("Alert service failed to initialize: " + ex);
+ client.alert = null;
+ }
+
+ try
+ {
+ // Mmmm, fun. This ONLY affects the ChatZilla window, don't worry!
+ Date.prototype.toStringInt = Date.prototype.toString;
+ Date.prototype.toString = function() {
+ let dtf = new Services.intl.DateTimeFormat(undefined,
+ { dateStyle: "full",
+ timeStyle: "long" });
+ return dtf.format(this);
+ }
+ }
+ catch (ex)
+ {
+ dd("Locale-correct date formatting failed to initialize: " + ex);
+ }
+
+ // XXX Bug 335998: See cmdHideView for usage of this.
+ client.hiddenDocument = document.implementation.createDocument(null, null, null);
+
+ multilineInputMode(client.prefs["multiline"]);
+ updateSpellcheck(client.prefs["inputSpellcheck"]);
+
+ // Initialize userlist stuff
+ if (client.prefs["showModeSymbols"])
+ setListMode("symbol");
+ else
+ setListMode("graphic");
+
+ var tree = document.getElementById('user-list');
+ tree.setAttribute("ondragstart", "userlistDNDObserver.onDragStart(event);");
+
+ setDebugMode(client.prefs["debugMode"]);
+
+ var version = getVersionInfo();
+ client.userAgent = getMsg(MSG_VERSION_REPLY, [version.cz, version.ua]);
+ CIRCServer.prototype.VERSION_RPLY = client.userAgent;
+ CIRCServer.prototype.HOST_RPLY = version.host;
+ CIRCServer.prototype.SOURCE_RPLY = MSG_SOURCE_REPLY;
+
+ client.statusBar = new Object();
+
+ client.statusBar["server-nick"] = document.getElementById("server-nick");
+
+ client.tabs = document.getElementById("views-tbar-inner");
+ client.tabDragBar = document.getElementById("tabs-drop-indicator-bar");
+ client.tabDragMarker = document.getElementById("tabs-drop-indicator");
+
+ client.statusElement = document.getElementById("status-text");
+ client.currentStatus = "";
+ client.defaultStatus = MSG_DEFAULT_STATUS;
+
+ client.progressPanel = document.getElementById("status-progress-panel");
+ client.progressBar = document.getElementById("status-progress-bar");
+
+ client.logFile = null;
+ setInterval(onNotifyTimeout, client.NOTIFY_TIMEOUT);
+ // Call every minute, will check only the networks necessary.
+ setInterval(onWhoTimeout, client.AWAY_TIMEOUT);
+
+ client.awayMsgs = [{ message: MSG_AWAY_DEFAULT }];
+ var awayFile = new nsLocalFile(client.prefs["profilePath"]);
+ awayFile.append("awayMsgs.txt");
+ if (awayFile.exists())
+ {
+ var awayLoader = new TextSerializer(awayFile);
+ if (awayLoader.open("<"))
+ {
+ // Load the first item from the file.
+ var item = awayLoader.deserialize();
+ if (isinstance(item, Array))
+ {
+ // If the first item is an array, it is the entire thing.
+ client.awayMsgs = item;
+ }
+ else if (item != null)
+ {
+ /* Not an array, so we have the old format of a single object
+ * per entry.
+ */
+ client.awayMsgs = [item];
+ while ((item = awayLoader.deserialize()))
+ client.awayMsgs.push(item);
+ }
+ awayLoader.close();
+
+ /* we have to close the file before we can move it,
+ * hence the second if statement */
+ if (item == null)
+ {
+ var invalidFile = new nsLocalFile(client.prefs["profilePath"]);
+ invalidFile.append("awayMsgs.invalid");
+ invalidFile.createUnique(FTYPE_FILE, 0o600);
+ var msg = getMsg(MSG_ERR_INVALID_FILE,
+ [awayFile.leafName, invalidFile.leafName]);
+ setTimeout(function() {
+ client.display(msg, MT_WARN);
+ }, 0);
+ awayFile.moveTo(null, invalidFile.leafName);
+ }
+ }
+ }
+
+ // Get back input history from previous session:
+ var inputHistoryFile = new nsLocalFile(client.prefs["profilePath"]);
+ inputHistoryFile.append("inputHistory.txt");
+ try
+ {
+ client.inputHistoryLogger = new TextLogger(inputHistoryFile.path,
+ client.MAX_HISTORY);
+ }
+ catch (ex)
+ {
+ msg = getMsg(MSG_ERR_INPUTHISTORY_NOT_WRITABLE, inputHistoryFile.path);
+ setTimeout(function() {
+ client.display(msg, MT_ERROR);
+ }, 0);
+ dd(formatException(ex));
+ client.inputHistoryLogger = null;
+ }
+ if (client.inputHistoryLogger)
+ client.inputHistory = client.inputHistoryLogger.read().reverse();
+
+ // Set up URL collector.
+ var urlsFile = new nsLocalFile(client.prefs["profilePath"]);
+ urlsFile.append("urls.txt");
+ try
+ {
+ client.urlLogger = new TextLogger(urlsFile.path,
+ client.prefs["urls.store.max"]);
+ }
+ catch (ex)
+ {
+ msg = getMsg(MSG_ERR_URLS_NOT_WRITABLE, urlsFile.path);
+ setTimeout(function() {
+ client.display(msg, MT_ERROR);
+ }, 0);
+ dd(formatException(ex));
+ client.urlLogger = null;
+ }
+
+ // Migrate old list preference to file.
+ try
+ {
+ // Throws if the preference doesn't exist.
+ if (client.urlLogger)
+ var urls = client.prefManager.prefBranch.getCharPref("urls.list");
+ }
+ catch (ex)
+ {
+ }
+ if (urls)
+ {
+ // Add the old URLs to the new file.
+ urls = client.prefManager.stringToArray(urls);
+ for (var i = 0; i < urls.length; i++)
+ client.urlLogger.append(urls[i]);
+ // Completely purge the old preference.
+ client.prefManager.prefBranch.clearUserPref("urls.list");
+ }
+
+ client.defaultCompletion = client.COMMAND_CHAR + "help ";
+
+ client.deck = document.getElementById('output-deck');
+}
+
+function getVersionInfo()
+{
+ var version = new Object();
+ version.cz = __cz_version;
+
+ var app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
+ version.hostName = app.vendor + " " + app.name;
+ version.hostVersion = app.version;
+ version.host = version.hostName + " " + version.hostVersion + ", " +
+ client.platform;
+ version.hostBuildID = app.platformBuildID;
+ version.ua = app.name + " " + app.version + "/" + version.hostBuildID;
+
+ return version;
+}
+
+function initApplicationCompatibility()
+{
+ // This function does nothing more than tweak the UI based on the platform.
+
+ client.lineEnd = "\n";
+
+ // Set up simple platform information.
+ switch (AppConstants.platform) {
+ case "linux":
+ client.platform = "Linux";
+ break;
+ case "macosx":
+ client.platform = "Mac";
+ break;
+ case "win":
+ client.platform = "Windows";
+ // Windows likes \r\n line endings, as notepad can't cope with just
+ // \n logs.
+ client.lineEnd = "\r\n";
+ break;
+ default:
+ client.platform = "Unknown";
+ }
+
+ CIRCServer.prototype.OS_RPLY = navigator.oscpu + " (" +
+ navigator.platform + ")";
+}
+
+function getFindData(e)
+{
+ // findNext() wrapper to add our findStart/findEnd events.
+ function _cz_findNext() {
+ // Send start notification.
+ var ev = new CEvent("find", "findStart", e.sourceObject, "onFindStart");
+ client.eventPump.routeEvent(ev);
+
+ // Call the original findNext() and keep the result for later.
+ var rv = this.__proto__.findNext();
+
+ // Send end notification with result code.
+ var ev = new CEvent("find", "findEnd", e.sourceObject, "onFindEnd");
+ ev.findResult = rv;
+ client.eventPump.routeEvent(ev);
+
+ // Return the original findNext()'s result to keep up appearances.
+ return rv;
+ };
+
+ // Getter for webBrowserFind property.
+ function _cz_webBrowserFind() {
+ return this._cz_wbf;
+ };
+
+ var findData = new nsFindInstData();
+ findData.browser = e.sourceObject.frame;
+ findData.rootSearchWindow = getContentWindow(e.sourceObject.frame);
+ findData.currentSearchWindow = getContentWindow(e.sourceObject.frame);
+
+ /* Wrap up the webBrowserFind object so we get called for findNext(). Use
+ * __proto__ so that everything else is exactly like the original object.
+ */
+ findData._cz_wbf = { findNext: _cz_findNext };
+ findData._cz_wbf.__proto__ = findData.webBrowserFind;
+
+ /* Replace the nsFindInstData getter for webBrowserFind to call our
+ * function which in turn returns our object (_cz_wbf).
+ */
+ findData.__defineGetter__("webBrowserFind", _cz_webBrowserFind);
+
+ /* Yay, evil hacks! findData.init doesn't care about the findService, it
+ * gets option settings from webBrowserFind. As we want the wrap option *on*
+ * when we use /find foo, we set it on the findService there. However,
+ * restoring the original value afterwards doesn't help, because init() here
+ * overrides that value. Unless we make .init do something else, of course:
+ */
+ findData._init = findData.init;
+ findData.init =
+ function init()
+ {
+ this._init();
+ const FINDSVC_ID = "@mozilla.org/find/find_service;1";
+ var findService = getService(FINDSVC_ID, "nsIFindService");
+ this.webBrowserFind.wrapFind = findService.wrapFind;
+ };
+
+ return findData;
+}
+
+function importFromFrame(method)
+{
+ client.__defineGetter__(method, import_wrapper);
+ CIRCNetwork.prototype.__defineGetter__(method, import_wrapper);
+ CIRCChannel.prototype.__defineGetter__(method, import_wrapper);
+ CIRCUser.prototype.__defineGetter__(method, import_wrapper);
+ CIRCDCCChat.prototype.__defineGetter__(method, import_wrapper);
+ CIRCDCCFileTransfer.prototype.__defineGetter__(method, import_wrapper);
+
+ function import_wrapper()
+ {
+ var dummy = function(){};
+
+ if (!("frame" in this))
+ return dummy;
+
+ try
+ {
+ var window = getContentWindow(this.frame);
+ if (window && "initialized" in window && window.initialized &&
+ method in window)
+ {
+ return function import_wrapper_apply()
+ {
+ window[method].apply(this, arguments);
+ };
+ }
+ }
+ catch (ex)
+ {
+ ASSERT(0, "Caught exception calling: " + method + "\n" + ex);
+ }
+
+ return dummy;
+ };
+}
+
+function processStartupScripts()
+{
+ client.plugins = new Object();
+ var scripts = client.prefs["initialScripts"];
+ var basePath = getURLSpecFromFile(client.prefs["profilePath"]);
+ var baseURL = Services.io.newURI(basePath);
+ for (var i = 0; i < scripts.length; ++i)
+ {
+ try
+ {
+ var url = Services.io.newURI(scripts[i], null, baseURL);
+ var path = getFileFromURLSpec(url.spec);
+ }
+ catch(ex)
+ {
+ var params = ["initialScripts", scripts[i]];
+ display(getMsg(MSG_ERR_INVALID_PREF, params), MT_ERROR);
+ dd(formatException(ex));
+ continue;
+ }
+
+ if (url.scheme != "file" && url.scheme != "chrome")
+ {
+ display(getMsg(MSG_ERR_INVALID_SCHEME, scripts[i]), MT_ERROR);
+ continue;
+ }
+
+ if (!path.exists())
+ {
+ display(getMsg(MSG_ERR_ITEM_NOT_FOUND, url.spec), MT_WARN);
+ continue;
+ }
+
+ if (path.isDirectory())
+ loadPluginDirectory(path);
+ else
+ loadLocalFile(path);
+ }
+}
+
+function loadPluginDirectory(localPath, recurse)
+{
+ if (typeof recurse == "undefined")
+ recurse = 1;
+
+ var initPath = localPath.clone();
+ initPath.append("init.js");
+ if (initPath.exists())
+ loadLocalFile(initPath);
+
+ if (recurse < 1)
+ return;
+
+ var enumer = localPath.directoryEntries;
+ while (enumer.hasMoreElements())
+ {
+ var entry = enumer.getNext();
+ entry = entry.QueryInterface(Components.interfaces.nsIFile);
+ if (entry.isDirectory())
+ loadPluginDirectory(entry, recurse - 1);
+ }
+}
+
+function loadLocalFile(localFile)
+{
+ var url = getURLSpecFromFile(localFile);
+ var glob = new Object();
+ dispatch("load", {url: url, scope: glob});
+}
+
+function getPluginById(id)
+{
+ return client.plugins[id] || null;
+}
+
+function getPluginByURL(url)
+{
+ for (var k in client.plugins)
+ {
+ if (client.plugins[k].url == url)
+ return client.plugins[k];
+
+ }
+
+ return null;
+}
+
+function disablePlugin(plugin, destroy)
+{
+ if (!plugin.enabled)
+ {
+ display(getMsg(MSG_IS_DISABLED, plugin.id));
+ return true;
+ }
+
+ if (plugin.API > 0)
+ {
+ if (!plugin.disable())
+ {
+ display(getMsg(MSG_CANT_DISABLE, plugin.id));
+ return false;
+ }
+
+ if (destroy)
+ {
+ client.prefManager.removeObserver(plugin.prefManager);
+ plugin.prefManager.destroy();
+ }
+ else
+ {
+ plugin.prefs["enabled"] = false;
+ }
+ }
+ else if ("disablePlugin" in plugin.scope)
+ {
+ plugin.scope.disablePlugin();
+ }
+ else
+ {
+ display(getMsg(MSG_CANT_DISABLE, plugin.id));
+ return false;
+ }
+
+ display(getMsg(MSG_PLUGIN_DISABLED, plugin.id));
+ if (!destroy)
+ {
+ plugin.enabled = false;
+ }
+ return true;
+}
+
+function processStartupAutoperform()
+{
+ var cmdary = client.prefs["autoperform.client"];
+ for (var i = 0; i < cmdary.length; ++i)
+ {
+ if (cmdary[i][0] == "/")
+ client.dispatch(cmdary[i].substr(1));
+ else
+ client.dispatch(cmdary[i]);
+ }
+}
+
+function processStartupURLs()
+{
+ var wentSomewhere = false;
+
+ if ("arguments" in window &&
+ 0 in window.arguments && typeof window.arguments[0] == "object" &&
+ "url" in window.arguments[0])
+ {
+ var url = window.arguments[0].url;
+ if (url.search(/^ircs?:\/?\/?\/?$/i) == -1)
+ {
+ /* if the url is not irc: irc:/, irc://, or ircs equiv then go to it. */
+ gotoIRCURL(url);
+ wentSomewhere = true;
+ }
+ }
+ /* check to see whether the URL has been passed via the command line
+ instead. */
+ else if ("arguments" in window &&
+ 0 in window.arguments && typeof window.arguments[0] == "string")
+ {
+ var url = window.arguments[0]
+ var urlMatches = url.match(/^ircs?:\/\/\/?(.*)$/)
+ if (urlMatches)
+ {
+ if (urlMatches[1])
+ {
+ /* if the url is not "irc://", "irc:///" or an ircs equiv then
+ go to it. */
+ gotoIRCURL(url);
+ wentSomewhere = true;
+ }
+ }
+ else if (url)
+ {
+ /* URL parameter is not blank, but does not not conform to the
+ irc[s] scheme. */
+ display(getMsg(MSG_ERR_INVALID_SCHEME, url), MT_ERROR);
+ }
+ }
+
+ /* if we had nowhere else to go, connect to any default urls */
+ if (!wentSomewhere)
+ openStartupURLs();
+
+ if (client.viewsArray.length > 1 && !isStartupURL("irc://"))
+ dispatch("delete-view", { view: client });
+
+ /* XXX: If we have the "stop XBL breaking" hidden tab, remove it, to
+ * stop XBL breaking later. Oh, the irony.
+ */
+ if (client.tabs.firstChild.hidden)
+ {
+ client.tabs.removeChild(client.tabs.firstChild);
+ updateTabAttributes();
+ }
+}
+
+function openStartupURLs()
+{
+ var ary = client.prefs["initialURLs"];
+ for (var i = 0; i < ary.length; ++i)
+ {
+ if (ary[i] && ary[i] == "irc:///")
+ {
+ // Clean out "default network" entries, which we don't
+ // support any more; replace with the harmless irc:// URL.
+ ary[i] = "irc://";
+ client.prefs["initialURLs"].update();
+ }
+ if (ary[i] && ary[i] != "irc://")
+ gotoIRCURL(ary[i]);
+ }
+}
+
+function destroy()
+{
+ destroyPrefs();
+}
+
+function addURLToHistory(url) {
+ url = Services.io.newURI(url, "UTF-8");
+ PlacesUtils.history.insert({
+ url,
+ visits: [{
+ date: new Date(),
+ transition: PlacesUtils.history.TRANSITIONS.TYPED,
+ }],
+ });
+}
+
+function addStatusMessage(message)
+{
+ const DELAY_SCALE = 100;
+ const DELAY_MINIMUM = 5000;
+
+ var delay = message.length * DELAY_SCALE;
+ if (delay < DELAY_MINIMUM)
+ delay = DELAY_MINIMUM;
+
+ client.statusMessages.push({ message: message, delay: delay });
+ updateStatusMessages();
+}
+
+function updateStatusMessages()
+{
+ if (client.statusMessages.length == 0)
+ {
+ var status = client.currentStatus || client.defaultStatus;
+ client.statusElement.setAttribute("label", status);
+ client.statusElement.removeAttribute("notice");
+ return;
+ }
+
+ var now = Number(new Date());
+ var currentMsg = client.statusMessages[0];
+ if ("expires" in currentMsg)
+ {
+ if (now >= currentMsg.expires)
+ {
+ client.statusMessages.shift();
+ setTimeout(updateStatusMessages, 0);
+ }
+ else
+ {
+ setTimeout(updateStatusMessages, 1000);
+ }
+ }
+ else
+ {
+ currentMsg.expires = now + currentMsg.delay;
+ client.statusElement.setAttribute("label", currentMsg.message);
+ client.statusElement.setAttribute("notice", "true");
+ setTimeout(updateStatusMessages, currentMsg.delay);
+ }
+}
+
+
+function setStatus(str)
+{
+ client.currentStatus = str;
+ updateStatusMessages();
+ return str;
+}
+
+client.__defineSetter__("status", setStatus);
+
+function getStatus()
+{
+ return client.currentStatus;
+}
+
+client.__defineGetter__("status", getStatus);
+
+function isVisible (id)
+{
+ var e = document.getElementById(id);
+
+ if (!ASSERT(e,"Bogus id ``" + id + "'' passed to isVisible() **"))
+ return false;
+
+ return (e.getAttribute ("collapsed") != "true");
+}
+
+client.getConnectedNetworks =
+function getConnectedNetworks()
+{
+ var rv = [];
+ for (var n in client.networks)
+ {
+ if (client.networks[n].isConnected())
+ rv.push(client.networks[n]);
+ }
+ return rv;
+}
+
+function combineNicks(nickList, max)
+{
+ if (!max)
+ max = 4;
+
+ var combinedList = [];
+
+ for (var i = 0; i < nickList.length; i += max)
+ {
+ count = Math.min(max, nickList.length - i);
+ var nicks = nickList.slice(i, i + count);
+ var str = new String(nicks.join(" "));
+ str.count = count;
+ combinedList.push(str);
+ }
+
+ return combinedList;
+}
+
+function updateAllStalkExpressions()
+{
+ var list = client.prefs["stalkWords"];
+
+ for (var name in client.networks)
+ {
+ if ("stalkExpression" in client.networks[name])
+ updateStalkExpression(client.networks[name], list);
+ }
+}
+
+function updateStalkExpression(network)
+{
+ function escapeChar(ch)
+ {
+ return "\\" + ch;
+ };
+
+ var list = client.prefs["stalkWords"];
+
+ var ary = new Array();
+
+ ary.push(network.primServ.me.unicodeName.replace(/[^\w\d]/g, escapeChar));
+
+ for (var i = 0; i < list.length; ++i)
+ ary.push(list[i].replace(/[^\w\d]/g, escapeChar));
+
+ var re;
+ if (client.prefs["stalkWholeWords"])
+ re = "(^|[\\W\\s])((" + ary.join(")|(") + "))([\\W\\s]|$)";
+ else
+ re = "(" + ary.join(")|(") + ")";
+
+ network.stalkExpression = new RegExp(re, "i");
+}
+
+function getDefaultFontSize()
+{
+ const PREF_CTRID = "@mozilla.org/preferences-service;1";
+ const nsIPrefService = Components.interfaces.nsIPrefService;
+ const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
+
+ var prefSvc = Components.classes[PREF_CTRID].getService(nsIPrefService);
+ var prefBranch = prefSvc.getBranch(null);
+
+ // PX size pref: font.size.variable.x-western
+ var pxSize = 16;
+ try
+ {
+ pxSize = prefBranch.getIntPref("font.size.variable.x-western");
+ }
+ catch(ex) { }
+
+ var dpi = 96;
+ try
+ {
+ // Get the DPI the fun way (make Mozilla do the work).
+ var b = document.createElement("box");
+ b.style.width = "1in";
+ dpi = window.getComputedStyle(b, null).width.match(/^\d+/);
+ }
+ catch(ex)
+ {
+ try
+ {
+ // Get the DPI the fun way (make Mozilla do the work).
+ b = document.createElementNS("box", XHTML_NS);
+ b.style.width = "1in";
+ dpi = window.getComputedStyle(b, null).width.match(/^\d+/);
+ }
+ catch(ex) { }
+ }
+
+ return Math.round((pxSize / dpi) * 72);
+}
+
+function getDefaultContext(cx)
+{
+ if (!cx)
+ cx = new Object();
+ /* Use __proto__ here and in all other get*Context so that the command can
+ * tell the difference between getObjectDetails and actual parameters. See
+ * cmdJoin for more details.
+ */
+ cx.__proto__ = getObjectDetails(client.currentObject);
+ return cx;
+}
+
+function getMessagesContext(cx, element)
+{
+ if (!cx)
+ cx = new Object();
+ cx.__proto__ = getObjectDetails(client.currentObject);
+ if (!element)
+ element = document.popupNode;
+
+ while (element)
+ {
+ switch (element.localName)
+ {
+ case "a":
+ var href = element.getAttribute("href");
+ cx.url = href;
+ break;
+
+ case "tr":
+ // NOTE: msg-user is the canonicalName.
+ cx.canonNick = element.getAttribute("msg-user");
+ if (!cx.canonNick)
+ break;
+
+ // Strip out a potential ME! suffix.
+ var ary = cx.canonNick.match(/([^ ]+)/);
+ cx.canonNick = ary[1];
+
+ if (!cx.network)
+ break;
+
+ if (cx.channel)
+ cx.user = cx.channel.getUser(cx.canonNick);
+ else
+ cx.user = cx.network.getUser(cx.canonNick);
+
+ if (cx.user)
+ cx.nickname = cx.user.unicodeName;
+ else
+ cx.nickname = toUnicode(cx.canonNick, cx.network);
+ break;
+ }
+
+ element = element.parentNode;
+ }
+
+ return cx;
+}
+
+function getTabContext(cx, element)
+{
+ if (!cx)
+ cx = new Object();
+ if (!element)
+ element = document.popupNode;
+
+ while (element)
+ {
+ if (element.localName == "tab")
+ {
+ cx.__proto__ = getObjectDetails(element.view);
+ return cx;
+ }
+ element = element.parentNode;
+ }
+
+ return cx;
+}
+
+function getUserlistContext(cx)
+{
+ if (!cx)
+ cx = new Object();
+ cx.__proto__ = getObjectDetails(client.currentObject);
+ if (!cx.channel)
+ return cx;
+
+ var user, tree = document.getElementById("user-list");
+ cx.userList = new Array();
+ cx.canonNickList = new Array();
+ cx.nicknameList = getSelectedNicknames(tree);
+
+ for (var i = 0; i < cx.nicknameList.length; ++i)
+ {
+ user = cx.channel.getUser(cx.nicknameList[i])
+ cx.userList.push(user);
+ cx.canonNickList.push(user.canonicalName);
+ if (i == 0)
+ {
+ cx.user = user;
+ cx.nickname = user.unicodeName;
+ cx.canonNick = user.canonicalName;
+ }
+ }
+ cx.userCount = cx.userList.length;
+
+ return cx;
+}
+
+function getViewsContext(cx)
+{
+ function addView(view)
+ {
+ // We only need the view to have messages, so we accept hidden views.
+ if (!("messages" in view))
+ return;
+
+ var url = view.getURL();
+ if (url in urls)
+ return;
+
+ var label = view.viewName;
+ if (!getTabForObject(view))
+ label = getMsg(MSG_VIEW_HIDDEN, [label]);
+
+ var types = ["IRCClient", "IRCNetwork", "IRCDCCChat",
+ "IRCDCCFileTransfer"];
+ var typesNetwork = ["IRCNetwork", "IRCChannel", "IRCUser"];
+ var group = String(arrayIndexOf(types, view.TYPE));
+ if (arrayIndexOf(typesNetwork, view.TYPE) != -1)
+ group = "1-" + getObjectDetails(view).network.viewName;
+
+ var sort = group + "-" + view.viewName;
+ if (view.TYPE == "IRCNetwork")
+ sort = group;
+
+ cx.views.push({url: url, label: label, group: group, sort: sort});
+ urls[url] = true
+ };
+
+ function sortViews(a, b)
+ {
+ if (a.sort < b.sort)
+ return -1;
+ if (a.sort > b.sort)
+ return 1;
+ return 0;
+ };
+
+ if (!cx)
+ cx = new Object();
+ cx.__proto__ = getObjectDetails(client.currentObject);
+
+ cx.views = new Array();
+ var urls = new Object();
+
+ /* XXX The code here works its way through all the open views *and* any
+ * possibly visible objects in the object model. This is necessary because
+ * occasionally objects get removed from the object model while still
+ * having a view open. See bug 459318 for one such case. Note that we
+ * won't be able to correctly switch to the "lost" view but showing it is
+ * less confusing than not.
+ */
+
+ for (var i in client.viewsArray)
+ addView(client.viewsArray[i].source);
+
+ addView(client);
+ for (var n in client.networks)
+ {
+ addView(client.networks[n]);
+ for (var s in client.networks[n].servers) {
+ var server = client.networks[n].servers[s];
+ for (var c in server.channels)
+ addView(server.channels[c]);
+ for (var u in server.users)
+ addView(server.users[u]);
+ }
+ }
+
+ for (var u in client.dcc.users)
+ addView(client.dcc.users[u]);
+ for (var i = 0; i < client.dcc.chats.length; i++)
+ addView(client.dcc.chats[i]);
+ for (var i = 0; i < client.dcc.files.length; i++)
+ addView(client.dcc.files[i]);
+
+ cx.views.sort(sortViews);
+
+ return cx;
+}
+
+function getSelectedNicknames(tree)
+{
+ var rv = [];
+ if (!tree || !tree.view || !tree.view.selection)
+ return rv;
+ var rangeCount = tree.view.selection.getRangeCount();
+
+ // Loop through the selection ranges.
+ for (var i = 0; i < rangeCount; ++i)
+ {
+ var start = {}, end = {};
+ tree.view.selection.getRangeAt(i, start, end);
+
+ // If they == -1, we've got no selection, so bail.
+ if ((start.value == -1) && (end.value == -1))
+ continue;
+ /* Workaround: Because we use select(-1) instead of clearSelection()
+ * (see bug 197667) the tree will then give us selection ranges
+ * starting from -1 instead of 0! (See bug 319066.)
+ */
+ if (start.value == -1)
+ start.value = 0;
+
+ // Loop through the contents of the current selection range.
+ for (var k = start.value; k <= end.value; ++k)
+ rv.push(getNicknameForUserlistRow(k));
+ }
+ return rv;
+}
+
+function getFontContext(cx)
+{
+ if (!cx)
+ cx = new Object();
+ cx.__proto__ = getObjectDetails(client.currentObject);
+ cx.fontSizeDefault = getDefaultFontSize();
+ var view = client;
+
+ if ("prefs" in cx.sourceObject)
+ {
+ cx.fontFamily = view.prefs["font.family"];
+ if (cx.fontFamily.match(/^(default|(sans-)?serif|monospace)$/))
+ delete cx.fontFamily;
+
+ cx.fontSize = view.prefs["font.size"];
+ if (cx.fontSize == 0)
+ delete cx.fontSize;
+ }
+
+ return cx;
+}
+
+function msgIsImportant(msg, sourceNick, network)
+{
+ var plainMsg = removeColorCodes(msg);
+
+ var re = network.stalkExpression;
+ if (plainMsg.search(re) != -1 || sourceNick && sourceNick.search(re) == 0)
+ return true;
+
+ return false;
+}
+
+function ensureCachedCanonicalURLs(array)
+{
+ if ("canonicalURLs" in array)
+ return;
+
+ /* Caching this on the array is safe because the PrefManager constructs
+ * a new array if the preference changes, but otherwise keeps the same
+ * one around.
+ */
+ array.canonicalURLs = new Array();
+ for (var i = 0; i < array.length; i++)
+ array.canonicalURLs.push(makeCanonicalIRCURL(array[i]));
+}
+
+function isStartupURL(url)
+{
+ // We canonicalize all URLs before we do the (string) comparison.
+ url = makeCanonicalIRCURL(url);
+ var list = client.prefs["initialURLs"];
+ ensureCachedCanonicalURLs(list);
+ return arrayContains(list.canonicalURLs, url);
+}
+
+function cycleView(amount)
+{
+ var len = client.viewsArray.length;
+ if (len <= 1)
+ return;
+
+ var tb = getTabForObject (client.currentObject);
+ if (!tb)
+ return;
+
+ var vk = Number(tb.getAttribute("viewKey"));
+ var destKey = (vk + amount) % len; /* wrap around */
+ if (destKey < 0)
+ destKey += len;
+
+ dispatch("set-current-view", { view: client.viewsArray[destKey].source });
+}
+
+// Plays the sound for a particular event on a type of object.
+function playEventSounds(type, event, source)
+{
+ if (!client.sound || !client.prefs["sound.enabled"])
+ return;
+
+ // Converts .TYPE values into the event object names.
+ // IRCChannel => channel, IRCUser => user, etc.
+ if (type.match(/^IRC/))
+ type = type.substr(3, type.length).toLowerCase();
+
+ // DCC Chat sessions should act just like user views.
+ if (type == "dccchat")
+ type = "user";
+
+ var ev = type + "." + event;
+
+ if (ev in client.soundList)
+ return;
+
+ var src = source ? source : client;
+
+ if (!(("sound." + ev) in src.prefs))
+ return;
+
+ var s = src.prefs["sound." + ev];
+
+ if (!s)
+ return;
+
+ if (client.prefs["sound.overlapDelay"] > 0)
+ {
+ client.soundList[ev] = true;
+ setTimeout(function() {
+ delete client.soundList[ev];
+ }, client.prefs["sound.overlapDelay"]);
+ }
+
+ if (event == "start")
+ {
+ blockEventSounds(type, "event");
+ blockEventSounds(type, "chat");
+ blockEventSounds(type, "stalk");
+ }
+
+ playSounds(s);
+}
+
+// Blocks a particular type of event sound occuring.
+function blockEventSounds(type, event)
+{
+ if (!client.sound || !client.prefs["sound.enabled"])
+ return;
+
+ // Converts .TYPE values into the event object names.
+ // IRCChannel => channel, IRCUser => user, etc.
+ if (type.match(/^IRC/))
+ type = type.substr(3, type.length).toLowerCase();
+
+ var ev = type + "." + event;
+
+ if (client.prefs["sound.overlapDelay"] > 0)
+ {
+ client.soundList[ev] = true;
+ setTimeout(function() {
+ delete client.soundList[ev];
+ }, client.prefs["sound.overlapDelay"]);
+ }
+}
+
+function playSounds(list)
+{
+ var ary = list.split (" ");
+ if (ary.length == 0)
+ return;
+
+ playSound(ary[0]);
+ for (var i = 1; i < ary.length; ++i)
+ setTimeout(playSound, 250 * i, ary[i]);
+}
+
+function playSound(file)
+{
+ if (!client.sound || !client.prefs["sound.enabled"] || !file)
+ return;
+
+ if (file == "beep")
+ {
+ client.sound.beep();
+ }
+ else
+ {
+ try
+ {
+ client.sound.play(Services.io.newURI(file));
+ }
+ catch (ex)
+ {
+ // ignore exceptions from this pile of code.
+ }
+ }
+}
+
+/* timer-based mainloop */
+function mainStep()
+{
+ try
+ {
+ var count = client.eventPump.stepEvents();
+ if (count > 0)
+ setTimeout(mainStep, client.STEP_TIMEOUT);
+ else
+ setTimeout(mainStep, client.STEP_TIMEOUT / 5);
+ }
+ catch(ex)
+ {
+ dd("Exception in mainStep!");
+ dd(formatException(ex));
+ setTimeout(mainStep, client.STEP_TIMEOUT);
+ }
+}
+
+function openQueryTab(server, nick)
+{
+ var user = server.addUser(nick);
+ addURLToHistory(user.getURL());
+ if (!("messages" in user))
+ {
+ var value = "";
+ var same = true;
+ for (var c in server.channels)
+ {
+ var chan = server.channels[c];
+ if (!(user.collectionKey in chan.users))
+ continue;
+ /* This takes a boolean value for each channel (true - channel has
+ * same value as first), and &&-s them all together. Thus, |same|
+ * will tell us, at the end, if all the channels found have the
+ * same value for charset.
+ */
+ if (value)
+ same = same && (value == chan.prefs["charset"]);
+ else
+ value = chan.prefs["charset"];
+ }
+ /* If we've got a value, and it's the same accross all channels,
+ * we use it as the *default* for the charset pref. If not, it'll
+ * just keep the "defer" default which pulls it off the network.
+ */
+ if (value && same)
+ {
+ user.prefManager.prefRecords["charset"].defaultValue = value;
+ }
+
+ dispatch("create-tab-for-view", { view: user });
+
+ user.doAutoPerform();
+ }
+ return user;
+}
+
+function arraySpeak (ary, single, plural)
+{
+ var rv = "";
+ var and = MSG_AND;
+
+ switch (ary.length)
+ {
+ case 0:
+ break;
+
+ case 1:
+ rv = ary[0];
+ if (single)
+ rv += " " + single;
+ break;
+
+ case 2:
+ rv = ary[0] + " " + and + " " + ary[1];
+ if (plural)
+ rv += " " + plural;
+ break;
+
+ default:
+ for (var i = 0; i < ary.length - 1; ++i)
+ rv += ary[i] + ", ";
+ rv += and + " " + ary[ary.length - 1];
+ if (plural)
+ rv += " " + plural;
+ break;
+ }
+
+ return rv;
+
+}
+
+function getObjectDetails (obj, rv)
+{
+ if (!rv)
+ rv = new Object();
+
+ if (!ASSERT(obj && typeof obj == "object",
+ "INVALID OBJECT passed to getObjectDetails (" + obj + "). **"))
+ {
+ return rv;
+ }
+
+ rv.sourceObject = obj;
+ rv.TYPE = obj.TYPE;
+ rv.parent = ("parent" in obj) ? obj.parent : null;
+ rv.user = null;
+ rv.channel = null;
+ rv.server = null;
+ rv.network = null;
+ if (window && window.content && window.content.getSelection() != "")
+ rv.selectedText = window.content.getSelection();
+
+ switch (obj.TYPE)
+ {
+ case "IRCChannel":
+ rv.viewType = MSG_CHANNEL;
+ rv.channel = obj;
+ rv.channelName = obj.unicodeName;
+ rv.server = rv.channel.parent;
+ rv.network = rv.server.parent;
+ break;
+
+ case "IRCUser":
+ rv.viewType = MSG_USER;
+ rv.user = obj;
+ rv.userName = rv.nickname = obj.unicodeName;
+ rv.server = rv.user.parent;
+ rv.network = rv.server.parent;
+ break;
+
+ case "IRCChanUser":
+ rv.viewType = MSG_USER;
+ rv.user = obj;
+ rv.userName = rv.nickname = obj.unicodeName;
+ rv.channel = rv.user.parent;
+ rv.server = rv.channel.parent;
+ rv.network = rv.server.parent;
+ break;
+
+ case "IRCNetwork":
+ rv.network = obj;
+ rv.viewType = MSG_NETWORK;
+ if ("primServ" in rv.network)
+ rv.server = rv.network.primServ;
+ else
+ rv.server = null;
+ break;
+
+ case "IRCClient":
+ rv.viewType = MSG_TAB;
+ break;
+
+ case "IRCDCCUser":
+ //rv.viewType = MSG_USER;
+ rv.user = obj;
+ rv.userName = obj.unicodeName;
+ break;
+
+ case "IRCDCCChat":
+ //rv.viewType = MSG_USER;
+ rv.chat = obj;
+ rv.user = obj.user;
+ rv.userName = obj.unicodeName;
+ break;
+
+ case "IRCDCCFileTransfer":
+ //rv.viewType = MSG_USER;
+ rv.file = obj;
+ rv.user = obj.user;
+ rv.userName = obj.unicodeName;
+ rv.fileName = obj.filename;
+ break;
+
+ default:
+ /* no setup for unknown object */
+ break;
+ }
+
+ if (rv.network)
+ rv.networkName = rv.network.unicodeName;
+
+ return rv;
+
+}
+
+function findDynamicRule (selector)
+{
+ var rules = frames[0].document.styleSheets[1].cssRules;
+
+ if (isinstance(selector, RegExp))
+ fun = "search";
+ else
+ fun = "indexOf";
+
+ for (var i = 0; i < rules.length; ++i)
+ {
+ var rule = rules.item(i);
+ if (rule.selectorText && rule.selectorText[fun](selector) == 0)
+ return {sheet: frames[0].document.styleSheets[1], rule: rule,
+ index: i};
+ }
+
+ return null;
+}
+
+function addDynamicRule (rule)
+{
+ var rules = frames[0].document.styleSheets[1];
+
+ var pos = rules.cssRules.length;
+ rules.insertRule (rule, pos);
+}
+
+function getCommandEnabled(command)
+{
+ try {
+ var dispatcher = document.commandDispatcher;
+ var controller = dispatcher.getControllerForCommand(command);
+
+ return controller.isCommandEnabled(command);
+ }
+ catch (e)
+ {
+ return false;
+ }
+}
+
+function doCommand(command)
+{
+ try {
+ var dispatcher = document.commandDispatcher;
+ var controller = dispatcher.getControllerForCommand(command);
+ if (controller && controller.isCommandEnabled(command))
+ controller.doCommand(command);
+ }
+ catch (e)
+ {
+ }
+}
+
+function doCommandWithParams(command, params)
+{
+ try {
+ var dispatcher = document.commandDispatcher;
+ var controller = dispatcher.getControllerForCommand(command);
+ controller.QueryInterface(Components.interfaces.nsICommandController);
+
+ if (!controller || !controller.isCommandEnabled(command))
+ return;
+
+ var cmdparams = newObject("@mozilla.org/embedcomp/command-params;1",
+ "nsICommandParams");
+ for (var i in params)
+ cmdparams.setISupportsValue(i, params[i]);
+
+ controller.doCommandWithParams(command, cmdparams);
+ }
+ catch (e)
+ {
+ }
+}
+
+var testURLs = [
+ "irc:",
+ "irc://",
+ "irc://foo",
+ "irc://foo/",
+ "irc://foo/,isserver",
+ "irc://foo/chatzilla",
+ "irc://foo/chatzilla/",
+ "irc://foo:6666",
+ "irc://foo:6666/",
+ "irc://irc.foo.org",
+ "irc://irc.foo.org/",
+ "irc://irc.foo.org/,needpass",
+ "irc://irc.foo.org/?msg=hello%20there",
+ "irc://irc.foo.org/?msg=hello%20there&ignorethis",
+ "irc://irc.foo.org/%23mozilla,needkey?msg=hello%20there&ignorethis",
+ "irc://libera.chat/",
+ "irc://libera.chat/,isserver",
+ "irc://[fe80::5d49:767b:4b68:1b17]",
+ "irc://[fe80::5d49:767b:4b68:1b17]/",
+ "irc://[fe80::5d49:767b:4b68:1b17]:6666",
+ "irc://[fe80::5d49:767b:4b68:1b17]:6666/"
+];
+
+var testFailURLs = [
+ "irc:///",
+ "irc:///help",
+ "irc:///help,needkey",
+ "irc://irc.foo.org/,isnick",
+ "invalids"
+];
+
+function doURLTest()
+{
+ var passed = 0, total = testURLs.length + testFailURLs.length;
+ for (var i = 0; i < testURLs.length; i++)
+ {
+ var o = parseIRCURL(testURLs[i]);
+ if (!o)
+ display("Parse of '" + testURLs[i] + "' failed.", MT_ERROR);
+ else
+ passed++;
+ }
+ for (var i = 0; i < testFailURLs.length; i++)
+ {
+ var o = parseIRCURL(testFailURLs[i]);
+ if (o)
+ display("Parse of '" + testFailURLs[i] + "' unexpectedly succeeded.", MT_ERROR);
+ else
+ passed++;
+ }
+ display("Passed " + passed + " out of " + total + " tests (" +
+ passed / total * 100 + "%).", MT_INFO);
+}
+
+var testIRCURLObjects = [
+ [{}, "irc://"],
+ [{host: "undernet"}, "irc://undernet/"],
+ [{host: "irc.undernet.org"}, "irc://irc.undernet.org/"],
+ [{host: "irc.undernet.org", isserver: true}, "irc://irc.undernet.org/"],
+ [{host: "undernet", isserver: true}, "irc://undernet/,isserver"],
+ [{host: "irc.undernet.org", port: 6667}, "irc://irc.undernet.org/"],
+ [{host: "irc.undernet.org", port: 1}, "irc://irc.undernet.org:1/"],
+ [{host: "irc.undernet.org", port: 1, scheme: "ircs"},
+ "ircs://irc.undernet.org:1/"],
+ [{host: "irc.undernet.org", port: 6697, scheme: "ircs"},
+ "ircs://irc.undernet.org/"],
+ [{host: "undernet", needpass: true}, "irc://undernet/,needpass"],
+ [{host: "undernet", pass: "cz"}, "irc://undernet/?pass=cz"],
+ [{host: "undernet", charset: "utf-8"}, "irc://undernet/?charset=utf-8"],
+ [{host: "undernet", target: "#foo"}, "irc://undernet/%23foo"],
+ [{host: "undernet", target: "#foo", needkey: true},
+ "irc://undernet/%23foo,needkey"],
+ [{host: "undernet", target: "John", isnick: true},
+ "irc://undernet/John,isnick"],
+ [{host: "undernet", target: "#foo", key: "cz"},
+ "irc://undernet/%23foo?key=cz"],
+ [{host: "undernet", charset: "utf-8"}, "irc://undernet/?charset=utf-8"],
+ [{host: "undernet", target: "John", msg: "spam!"},
+ "irc://undernet/John?msg=spam%21"],
+ [{host: "undernet", target: "foo", isnick: true, msg: "spam!", pass: "cz"},
+ "irc://undernet/foo,isnick?msg=spam%21&pass=cz"]
+];
+
+function doObjectURLtest()
+{
+ var passed = 0, total = testIRCURLObjects.length;
+ for (var i = 0; i < total; i++)
+ {
+ var obj = testIRCURLObjects[i][0];
+ var url = testIRCURLObjects[i][1];
+ var parsedURL = constructIRCURL(obj)
+ if (url != parsedURL)
+ {
+ display("Parsed IRC Object incorrectly! Expected '" + url +
+ "', got '" + parsedURL, MT_ERROR);
+ }
+ else
+ {
+ passed++;
+ }
+ }
+ display("Passed " + passed + " out of " + total + " tests (" +
+ passed / total * 100 + "%).", MT_INFO);
+}
+
+
+function gotoIRCURL(url, e)
+{
+ var urlspec = url;
+ if (typeof url == "string")
+ url = parseIRCURL(url);
+
+ if (!url)
+ {
+ window.alert(getMsg(MSG_ERR_BAD_IRCURL, urlspec));
+ return;
+ }
+
+ if (!url.host)
+ {
+ /* focus the *client* view for irc:, irc:/, and irc:// (the only irc
+ * urls that don't have a host. (irc:/// implies a connect to the
+ * default network.)
+ */
+ client.pendingViewContext = e;
+ dispatch("client");
+ delete client.pendingViewContext;
+ return;
+ }
+
+ let isSecure = url.scheme == "ircs";
+ let network;
+ // Make sure host is in lower case.
+ url.host = url.host.toLowerCase();
+
+ // Convert a request for a server to a network if we know it.
+ if (url.isserver)
+ {
+ for (var n in client.networks)
+ {
+ network = client.networks[n];
+ for (var s in network.servers)
+ {
+ let server = network.servers[s];
+ if ((server.hostname == url.host) &&
+ (server.isSecure == isSecure) &&
+ (!url.port || (server.port == url.port)))
+ {
+ url.isserver = false;
+ url.host = network.canonicalName;
+ if (!url.port)
+ url.port = server.port;
+ break;
+ }
+ }
+ if (!url.isserver)
+ break;
+ }
+ }
+
+ let name = url.host;
+ network = client.getNetwork(name);
+
+ if (url.isserver)
+ {
+ let found = false;
+ if (network) {
+ for (let s in network.servers) {
+ let server = network.servers[s];
+ if ((server.isSecure == isSecure) &&
+ (!url.port || (server.port == url.port))) {
+ found = true;
+ if (!url.port)
+ url.port = server.port;
+ break;
+ }
+ }
+ }
+
+ // If still no port set, use the default.
+ if (!url.port)
+ url.port = isSecure ? 6697 : 6667;
+
+ if (!found) {
+ name += ":" + url.port;
+
+ // If there is no temporary network for this server:port, create one.
+ if (!client.getNetwork(name)) {
+ let server = {name: url.host, port: url.port, isSecure: isSecure};
+ client.addNetwork(name, [server], true);
+ }
+ network = client.getNetwork(name);
+ }
+ }
+ else
+ {
+ // There is no network called this, sorry.
+ if (!network)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_NETWORK, name));
+ return;
+ }
+ }
+
+ // We should only prompt for a password if we're not connected.
+ if (network.state == NET_OFFLINE)
+ {
+ // Check for a network password.
+ url.pass = client.tryToGetLogin(network.getURL(), "serv", "*",
+ url.pass, url.needpass,
+ getMsg(MSG_HOST_PASSWORD,
+ network.getURL()));
+ }
+
+ // Adjust secure setting for temporary networks (so user can override).
+ if (network.temporary)
+ network.serverList[0].isSecure = url.scheme == "ircs";
+
+ // Adjust password for all servers (so user can override).
+ if (url.pass)
+ {
+ for (var s in network.servers)
+ network.servers[s].password = url.pass;
+ }
+
+ // Start the connection and pend anything else if we're not ready.
+ if (network.state != NET_ONLINE)
+ {
+ client.pendingViewContext = e;
+ if (!network.isConnected())
+ {
+ client.connectToNetwork(network, url.scheme == "ircs");
+ }
+ else
+ {
+ dispatch("create-tab-for-view", { view: network });
+ dispatch("set-current-view", { view: network });
+ }
+ delete client.pendingViewContext;
+
+ if (!url.target)
+ return;
+
+ // We're not completely online, so everything else is pending.
+ if (!("pendingURLs" in network))
+ network.pendingURLs = new Array();
+ network.pendingURLs.unshift({ url: url, e: e });
+ return;
+ }
+
+ // We're connected now, process the target.
+ if (url.target)
+ {
+ var targetObject;
+ var ev;
+ if (url.isnick)
+ {
+ /* url points to a person. */
+ var nick = url.target;
+ var ary = url.target.split("!");
+ if (ary)
+ nick = ary[0];
+
+ client.pendingViewContext = e;
+ targetObject = network.dispatch("query", {nickname: nick});
+ delete client.pendingViewContext;
+ }
+ else
+ {
+ /* url points to a channel */
+ var key;
+ var serv = network.primServ;
+ var target = url.target;
+ if (url.charset)
+ {
+ var chan = new CIRCChannel(serv, target, fromUnicode(target, url.charset));
+ chan.prefs["charset"] = url.charset;
+ }
+ else
+ {
+ // Must do this the hard way... we have the server's format
+ // for the channel name here, and all our commands only work
+ // with the Unicode forms.
+
+ /* If we don't have a valid prefix, stick a "#" on it.
+ * NOTE: This is always a "#" so that URLs may be compared
+ * properly without involving the server (e.g. off-line).
+ */
+ if ((arrayIndexOf(["#", "&", "+", "!"], target[0]) == -1) &&
+ (arrayIndexOf(serv.channelTypes, target[0]) == -1))
+ {
+ target = "#" + target;
+ }
+
+ var chan = new CIRCChannel(serv, null, target);
+ }
+
+ if (url.needkey && !chan.joined)
+ {
+ if (url.key)
+ key = url.key;
+ else
+ key = window.promptPassword(getMsg(MSG_URL_KEY, url.spec));
+ }
+ client.pendingViewContext = e;
+ d = {channelToJoin: chan, key: key};
+ targetObject = network.dispatch("join", d);
+ delete client.pendingViewContext;
+
+ if (!targetObject)
+ return;
+ }
+
+ if (url.msg)
+ {
+ client.pendingViewContext = e;
+ var msg;
+ if (url.msg.indexOf("\01ACTION") == 0)
+ {
+ msg = filterOutput(url.msg, "ACTION", targetObject);
+ targetObject.display(msg, "ACTION", "ME!",
+ client.currentObject);
+ }
+ else
+ {
+ msg = filterOutput(url.msg, "PRIVMSG", targetObject);
+ targetObject.display(msg, "PRIVMSG", "ME!",
+ client.currentObject);
+ }
+ targetObject.say(msg);
+ dispatch("set-current-view", { view: targetObject });
+ delete client.pendingViewContext;
+ }
+ }
+ else
+ {
+ client.pendingViewContext = e;
+ dispatch("create-tab-for-view", { view: network });
+ dispatch("set-current-view", { view: network });
+ delete client.pendingViewContext;
+ }
+}
+
+function updateProgress()
+{
+ var busy;
+ var progress = -1;
+
+ if ("busy" in client.currentObject)
+ busy = client.currentObject.busy;
+
+ if ("progress" in client.currentObject)
+ progress = client.currentObject.progress;
+
+ if (!busy)
+ progress = 0;
+
+ client.progressPanel.collapsed = !busy;
+ client.progressBar.mode = (progress < 0 ? "undetermined" : "determined");
+ if (progress >= 0)
+ client.progressBar.value = progress;
+}
+
+function updateSecurityIcon()
+{
+ var o = getObjectDetails(client.currentObject);
+ var securityButton = window.document.getElementById("security-button");
+ securityButton.label = "";
+ securityButton.removeAttribute("level");
+ securityButton.removeAttribute("tooltiptext");
+ if (!o.server || !o.server.isConnected) // No server or connection?
+ {
+ securityButton.setAttribute("tooltiptext", MSG_SECURITY_INFO);
+ return;
+ }
+
+ let tooltiptext = MSG_SECURITY_INFO;
+ switch (o.server.connection.getSecurityState()) {
+ case STATE_IS_SECURE:
+ securityButton.setAttribute("level", "high");
+
+ // Update the tooltip.
+ var issuer = o.server.connection.getCertificate().issuerOrganization;
+ tooltiptext = getMsg(MSG_SECURE_CONNECTION, issuer);
+ break;
+ case STATE_IS_BROKEN:
+ securityButton.setAttribute("level", "broken");
+ break;
+ case STATE_IS_INSECURE:
+ default:
+ securityButton.setAttribute("level", "none");
+ }
+ securityButton.label = o.server.hostname;
+ securityButton.setAttribute("tooltiptext", tooltiptext);
+}
+
+function updateLoggingIcon()
+{
+ var state = client.currentObject.prefs["log"] ? "on" : "off";
+ var icon = window.document.getElementById("logging-status");
+ icon.setAttribute("loggingstate", state);
+ icon.setAttribute("tooltiptext", getMsg("msg.logging.icon." + state));
+}
+
+function updateAlertIcon(aToggle) {
+ let alertState = client.prefs["alert.globalEnabled"];
+ if (aToggle) {
+ alertState = !alertState;
+ client.prefs["alert.globalEnabled"] = alertState;
+ }
+ let state = alertState ? "on" : "off";
+ let icon = window.document.getElementById("alert-status");
+ icon.setAttribute("alertstate", state);
+ icon.setAttribute("tooltiptext", getMsg("msg.alert.icon." + state));
+}
+
+function initOfflineIcon()
+{
+ const PRBool_CID = "@mozilla.org/supports-PRBool;1";
+ const OS_CID = "@mozilla.org/observer-service;1";
+ const nsISupportsPRBool = Components.interfaces.nsISupportsPRBool;
+
+ client.offlineObserver = {
+ _element: document.getElementById("offline-status"),
+ state: function offline_state()
+ {
+ return (Services.io.offline ? "offline" : "online");
+ },
+ observe: function offline_observe(subject, topic, state)
+ {
+ if ((topic == "offline-requested") &&
+ (client.getConnectionCount() > 0))
+ {
+ var buttonAry = [MSG_REALLY_GO_OFFLINE, MSG_DONT_GO_OFFLINE];
+ var rv = confirmEx(MSG_GOING_OFFLINE, buttonAry);
+ if (rv == 1) // Don't go offline, please!
+ {
+ subject.QueryInterface(nsISupportsPRBool);
+ subject.data = true;
+ }
+ }
+ else if (topic == "network:offline-status-changed")
+ {
+ this.updateOfflineUI();
+ }
+ },
+ updateOfflineUI: function offline_uiUpdate()
+ {
+ this._element.setAttribute("offline", Services.io.offline);
+ var tooltipMsgId = "MSG_OFFLINESTATE_" + this.state().toUpperCase();
+ this._element.setAttribute("tooltiptext", window[tooltipMsgId]);
+ },
+ toggleOffline: function offline_toggle()
+ {
+ // Check whether people are OK with us going offline:
+ if (!Services.io.offline && !this.canGoOffline())
+ return;
+
+ // Stop automatic management of the offline status, if existing.
+ Services.io.manageOfflineStatus = false;
+
+ // Actually change the offline state.
+ Services.io.offline = !Services.io.offline;
+ },
+ canGoOffline: function offline_check()
+ {
+ try
+ {
+ var canGoOffline = newObject(PRBool_CID, "nsISupportsPRBool");
+ Services.obs.notifyObservers(canGoOffline, "offline-requested");
+ // Someone called for a halt
+ if (canGoOffline.data)
+ return false;
+ }
+ catch (ex)
+ {
+ dd("Exception when trying to ask if we could go offline:" + ex);
+ }
+ return true;
+ }
+ };
+
+ Services.obs.addObserver(client.offlineObserver, "offline-requested");
+ Services.obs.addObserver(client.offlineObserver,
+ "network:offline-status-changed");
+ client.offlineObserver.updateOfflineUI();
+}
+
+function uninitOfflineIcon()
+{
+ Services.obs.removeObserver(client.offlineObserver, "offline-requested");
+ Services.obs.removeObserver(client.offlineObserver,
+ "network:offline-status-changed");
+}
+
+client.idleObserver = {
+ QueryInterface: function io_qi(iid)
+ {
+ if (!iid || (!iid.equals(Components.interfaces.nsIObserver) &&
+ !iid.equals(Components.interfaces.nsISupports)))
+ {
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ return this;
+ },
+ observe: function io_observe(subject, topic, data)
+ {
+ if ((topic == "idle") && !client.prefs["away"])
+ {
+ if (!client.prefs["awayIdleMsg"])
+ client.prefs["awayIdleMsg"] = MSG_AWAY_IDLE_DEFAULT;
+ client.dispatch("idle-away", {reason: client.prefs["awayIdleMsg"]});
+ client.isIdleAway = true;
+ }
+ else if ((topic == "back" || topic == "active") && client.isIdleAway)
+ {
+ client.dispatch("idle-back");
+ client.isIdleAway = false;
+ }
+ }
+};
+
+function initIdleAutoAway(timeout)
+{
+ // Don't try to do anything if we are disabled
+ if (!timeout)
+ return;
+
+ var is = getService("@mozilla.org/widget/idleservice;1", "nsIIdleService");
+ if (!is)
+ {
+ display(MSG_ERR_NO_IDLESERVICE, MT_WARN);
+ client.prefs["autoIdleTime"] = 0;
+ return;
+ }
+
+ try
+ {
+ is.addIdleObserver(client.idleObserver, timeout * 60);
+ }
+ catch (ex)
+ {
+ display(formatException(ex), MT_ERROR);
+ }
+}
+
+function uninitIdleAutoAway(timeout)
+{
+ // Don't try to do anything if we were disabled before
+ if (!timeout)
+ return;
+
+ var is = getService("@mozilla.org/widget/idleservice;1", "nsIIdleService");
+ if (!is)
+ return;
+
+ try
+ {
+ is.removeIdleObserver(client.idleObserver, timeout * 60);
+ }
+ catch (ex)
+ {
+ display(formatException(ex), MT_ERROR);
+ }
+}
+
+function updateAppMotif(motifURL)
+{
+ var node = document.firstChild;
+ while (node && ((node.nodeType != node.PROCESSING_INSTRUCTION_NODE) ||
+ !(/name="dyn-motif"/).test(node.data)))
+ {
+ node = node.nextSibling;
+ }
+
+ motifURL = motifURL.replace(/"/g, "%22");
+ var dataStr = "href=\"" + motifURL + "\" name=\"dyn-motif\"";
+ try
+ {
+ // No dynamic style node yet.
+ if (!node)
+ {
+ node = document.createProcessingInstruction("xml-stylesheet", dataStr);
+ document.insertBefore(node, document.firstChild);
+ }
+ else if (node.data != dataStr)
+ {
+ node.data = dataStr;
+ document.insertBefore(node, node.nextSibling);
+ }
+ }
+ catch (ex)
+ {
+ dd(formatException(ex));
+ var err = ex.name;
+ // Mozilla 1.0 doesn't like document.insertBefore(...,
+ // document.firstChild); though it has a prototype for it -
+ // check for the right error:
+ if (err == "NS_ERROR_NOT_IMPLEMENTED")
+ {
+ display(MSG_NO_DYNAMIC_STYLE, MT_INFO);
+ updateAppMotif = function() {};
+ }
+ }
+}
+
+function updateSpellcheck(value)
+{
+ value = value.toString();
+ document.getElementById("input").setAttribute("spellcheck", value);
+ document.getElementById("multiline-input").setAttribute("spellcheck",
+ value);
+}
+
+function updateNetwork()
+{
+ var o = getObjectDetails (client.currentObject);
+
+ var lag = MSG_UNKNOWN;
+ var nick = "";
+ if (o.server)
+ {
+ if (o.server.me)
+ nick = o.server.me.unicodeName;
+ lag = (o.server.lag != -1) ? o.server.lag.toFixed(2) : MSG_UNKNOWN;
+ }
+ client.statusBar["header-url"].setAttribute("value",
+ client.currentObject.getURL());
+ client.statusBar["header-url"].setAttribute("href",
+ client.currentObject.getURL());
+ client.statusBar["header-url"].setAttribute("name",
+ client.currentObject.unicodeName);
+}
+
+function updateTitle (obj)
+{
+ if (!(("currentObject" in client) && client.currentObject) ||
+ (obj && obj != client.currentObject))
+ return;
+
+ var tstring = MSG_TITLE_UNKNOWN;
+ var o = getObjectDetails(client.currentObject);
+ var net = o.network ? o.network.unicodeName : "";
+ var nick = "";
+ client.statusBar["server-nick"].disabled = false;
+
+ switch (client.currentObject.TYPE)
+ {
+ case "IRCNetwork":
+ var serv = "", port = "";
+ if (client.currentObject.isConnected())
+ {
+ serv = o.server.hostname;
+ port = o.server.port;
+ if (o.server.me)
+ nick = o.server.me.unicodeName;
+ tstring = getMsg(MSG_TITLE_NET_ON, [nick, net, serv, port]);
+ }
+ else
+ {
+ nick = client.currentObject.INITIAL_NICK;
+ tstring = getMsg(MSG_TITLE_NET_OFF, [nick, net]);
+ }
+ break;
+
+ case "IRCChannel":
+ var chan = "", mode = "", topic = "";
+ if ("me" in o.parent)
+ {
+ nick = o.parent.me.unicodeName;
+ if (o.parent.me.collectionKey in client.currentObject.users)
+ {
+ let cuser = client.currentObject.users[o.parent.me.collectionKey];
+ if (cuser.isFounder)
+ nick = "~" + nick;
+ else if (cuser.isAdmin)
+ nick = "&" + nick;
+ else if (cuser.isOp)
+ nick = "@" + nick;
+ else if (cuser.isHalfOp)
+ nick = "%" + nick;
+ else if (cuser.isVoice)
+ nick = "+" + nick;
+ }
+ }
+ else
+ {
+ nick = MSG_TITLE_NONICK;
+ }
+ chan = o.channel.unicodeName;
+ mode = o.channel.mode.getModeStr();
+ if (!mode)
+ mode = MSG_TITLE_NO_MODE;
+ topic = o.channel.topic ? o.channel.topic : MSG_TITLE_NO_TOPIC;
+ var re = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
+ topic = topic.replace(re, "");
+
+ tstring = getMsg(MSG_TITLE_CHANNEL, [nick, chan, mode, topic]);
+ break;
+
+ case "IRCUser":
+ nick = client.currentObject.unicodeName;
+ var source = "";
+ if (client.currentObject.name)
+ {
+ source = "<" + client.currentObject.name + "@" +
+ client.currentObject.host +">";
+ }
+ tstring = getMsg(MSG_TITLE_USER, [nick, source]);
+ nick = "me" in o.parent ? o.parent.me.unicodeName : MSG_TITLE_NONICK;
+ break;
+
+ case "IRCClient":
+ nick = client.prefs["nickname"];
+ break;
+
+ case "IRCDCCChat":
+ client.statusBar["server-nick"].disabled = true;
+ nick = o.chat.me.unicodeName;
+ tstring = getMsg(MSG_TITLE_DCCCHAT, o.userName);
+ break;
+
+ case "IRCDCCFileTransfer":
+ client.statusBar["server-nick"].disabled = true;
+ nick = o.file.me.unicodeName;
+ var data = [o.file.progress, o.file.filename, o.userName];
+ if (o.file.state.dir == 1)
+ tstring = getMsg(MSG_TITLE_DCCFILE_SEND, data);
+ else
+ tstring = getMsg(MSG_TITLE_DCCFILE_GET, data);
+ break;
+ }
+
+ if (0 && !client.uiState["tabstrip"])
+ {
+ var actl = new Array();
+ for (var i in client.activityList)
+ actl.push ((client.activityList[i] == "!") ?
+ (Number(i) + 1) + "!" : (Number(i) + 1));
+ if (actl.length > 0)
+ tstring = getMsg(MSG_TITLE_ACTIVITY,
+ [tstring, actl.join (", ")]);
+ }
+
+ document.title = tstring;
+ client.statusBar["server-nick"].setAttribute("label", nick);
+}
+
+// Where 'right' is orientation, not wrong/right:
+function updateUserlistSide(shouldBeLeft)
+{
+ var listParent = document.getElementById("tabpanels-contents-box");
+ var isLeft = (listParent.childNodes[0].id == "user-list-box");
+ if (isLeft == shouldBeLeft)
+ return;
+ if (shouldBeLeft) // Move from right to left.
+ {
+ listParent.insertBefore(listParent.childNodes[1], listParent.childNodes[0]);
+ listParent.insertBefore(listParent.childNodes[2], listParent.childNodes[0]);
+ listParent.childNodes[1].setAttribute("collapse", "before");
+ }
+ else // Move from left to right.
+ {
+ listParent.appendChild(listParent.childNodes[1]);
+ listParent.appendChild(listParent.childNodes[0]);
+ listParent.childNodes[1].setAttribute("collapse", "after");
+ }
+ var userlist = document.getElementById("user-list")
+ if (client.currentObject && (client.currentObject.TYPE == "IRCChannel"))
+ userlist.view = client.currentObject.userList;
+}
+
+function multilineInputMode (state)
+{
+ var multiInput = document.getElementById("multiline-input");
+ var multiInputBox = document.getElementById("multiline-box");
+ var singleInput = document.getElementById("input");
+ var singleInputBox = document.getElementById("singleline-box");
+ var splitter = document.getElementById("input-splitter");
+ var iw = document.getElementById("input-widgets");
+ var h;
+
+ client._mlMode = state;
+
+ if (state) /* turn on multiline input mode */
+ {
+
+ h = iw.getAttribute ("lastHeight");
+ if (h)
+ iw.setAttribute ("height", h); /* restore the slider position */
+
+ singleInputBox.setAttribute ("collapsed", "true");
+ splitter.setAttribute ("collapsed", "false");
+ multiInputBox.setAttribute ("collapsed", "false");
+ // multiInput should have the same direction as singleInput
+ multiInput.setAttribute("dir", singleInput.getAttribute("dir"));
+ multiInput.value = (client.input ? client.input.value : "");
+ client.input = multiInput;
+ }
+ else /* turn off multiline input mode */
+ {
+ h = iw.getAttribute ("height");
+ iw.setAttribute ("lastHeight", h); /* save the slider position */
+ iw.removeAttribute ("height"); /* let the slider drop */
+
+ splitter.setAttribute ("collapsed", "true");
+ multiInputBox.setAttribute ("collapsed", "true");
+ singleInputBox.setAttribute ("collapsed", "false");
+ // singleInput should have the same direction as multiInput
+ singleInput.setAttribute("dir", multiInput.getAttribute("dir"));
+ singleInput.value = (client.input ? client.input.value : "");
+ client.input = singleInput;
+ }
+
+ client.input.focus();
+}
+
+function displayCertificateInfo()
+{
+ var o = getObjectDetails(client.currentObject);
+ if (!o.server)
+ return;
+
+ if (!o.server.isSecure)
+ {
+ alert(getMsg(MSG_INSECURE_SERVER, o.server.hostname));
+ return;
+ }
+
+ viewCert(o.server.connection.getCertificate());
+}
+
+function onLoggingIcon() {
+ client.currentObject.dispatch("log", { state: "toggle" });
+}
+
+function newInlineText (data, className, tagName)
+{
+ if (typeof tagName == "undefined")
+ tagName = "html:span";
+
+ var a = document.createElementNS(XHTML_NS, tagName);
+ if (className)
+ a.setAttribute ("class", className);
+
+ switch (typeof data)
+ {
+ case "string":
+ a.appendChild (document.createTextNode (data));
+ break;
+
+ case "object":
+ for (var p in data)
+ if (p != "data")
+ a.setAttribute (p, data[p]);
+ else
+ a.appendChild (document.createTextNode (data[p]));
+ break;
+
+ case "undefined":
+ break;
+
+ default:
+ ASSERT(0, "INVALID TYPE ('" + typeof data + "') passed to " +
+ "newInlineText.");
+ break;
+
+ }
+
+ return a;
+
+}
+
+function stringToMsg (message, obj)
+{
+ var ary = message.split ("\n");
+ var span = document.createElementNS(XHTML_NS, "html:span");
+ var data = getObjectDetails(obj);
+
+ if (ary.length == 1)
+ client.munger.munge(ary[0], span, data);
+ else
+ {
+ for (var l = 0; l < ary.length - 1; ++l)
+ {
+ client.munger.munge(ary[l], span, data);
+ span.appendChild(document.createElementNS(XHTML_NS, "html:br"));
+ }
+ client.munger.munge(ary[l], span, data);
+ }
+
+ return span;
+}
+
+function getFrame()
+{
+ if (client.deck.childNodes.length == 0)
+ return undefined;
+ var panel = client.deck.selectedPanel;
+ return getContentWindow(panel);
+}
+
+client.__defineGetter__ ("currentFrame", getFrame);
+
+function setCurrentObject (obj)
+{
+ if (!ASSERT(obj.messages, "INVALID OBJECT passed to setCurrentObject **"))
+ return;
+
+ if ("currentObject" in client && client.currentObject == obj)
+ {
+ if (typeof client.pendingViewContext == "object")
+ dispatch("create-tab-for-view", { view: obj });
+ return;
+ }
+
+ // Set window.content to make screenreader apps find the chat content.
+ if (obj.frame && getContentWindow(obj.frame))
+ window.content = getContentWindow(obj.frame);
+
+ var tb, userList;
+ userList = document.getElementById("user-list");
+
+ if ("currentObject" in client && client.currentObject)
+ tb = getTabForObject(client.currentObject);
+ if (tb)
+ tb.setAttribute("state", "normal");
+
+ // If we're tracking last read lines, set a mark on the current view
+ // before switching to the new one.
+ if (tb && client.currentObject.prefs["autoMarker"])
+ client.currentObject.dispatch("marker-set");
+
+ client.currentObject = obj;
+
+ // Update userlist:
+ userList.view = null;
+ if (obj.TYPE == "IRCChannel")
+ {
+ userList.view = obj.userList;
+ updateUserList();
+ }
+
+ tb = dispatch("create-tab-for-view", { view: obj });
+ if (tb)
+ {
+ tb.parentNode.selectedItem = tb;
+ tb.setAttribute("state", "current");
+ }
+
+ var vk = Number(tb.getAttribute("viewKey"));
+ delete client.activityList[vk];
+ client.deck.selectedPanel = obj.frame;
+
+ // Style userlist and the like:
+ updateAppMotif(obj.prefs["motif.current"]);
+
+ updateTitle();
+ updateProgress();
+ updateSecurityIcon();
+ updateLoggingIcon();
+
+ scrollDown(obj.frame, false);
+
+ // Input area should have the same direction as the output area
+ if (("frame" in client.currentObject) &&
+ client.currentObject.frame &&
+ getContentDocument(client.currentObject.frame) &&
+ ("body" in getContentDocument(client.currentObject.frame)) &&
+ getContentDocument(client.currentObject.frame).body)
+ {
+ var contentArea = getContentDocument(client.currentObject.frame).body;
+ client.input.setAttribute("dir", contentArea.getAttribute("dir"));
+ }
+ client.input.focus();
+}
+
+function checkScroll(frame)
+{
+ var window = getContentWindow(frame);
+ if (!window || !window.document || !window.document.body)
+ return false;
+
+ return (window.document.body.clientHeight - window.innerHeight -
+ window.pageYOffset) < 160;
+}
+
+function scrollDown(frame, force)
+{
+ var window = getContentWindow(frame);
+ if (!window || !window.document || !window.document.body)
+ return;
+
+ if (force || checkScroll(frame))
+ window.scrollTo(0, window.document.body.clientHeight);
+}
+
+function advanceKeyboardFocus(amount)
+{
+ var contentWin = getContentWindow(client.currentObject.frame);
+ var contentDoc = getContentDocument(client.currentObject.frame);
+ var userList = document.getElementById("user-list");
+
+ // Focus userlist, inputbox and outputwindow in turn:
+ var focusableElems = [userList, client.input.inputField, contentWin];
+
+ var elem = document.commandDispatcher.focusedElement;
+ // Finding focus in the content window is "hard". It's going to be null
+ // if the window itself is focused, and "some element" inside of it if the
+ // user starts tabbing through.
+ if (!elem || (elem.ownerDocument == contentDoc))
+ elem = contentWin;
+
+ var newIndex = (arrayIndexOf(focusableElems, elem) * 1 + 3 + amount) % 3;
+ focusableElems[newIndex].focus();
+
+ // Make it obvious this element now has focus.
+ var outlinedElem;
+ if (focusableElems[newIndex] == client.input.inputField)
+ outlinedElem = client.input.parentNode.id;
+ else if (focusableElems[newIndex] == userList)
+ outlinedElem = "user-list-box"
+ else
+ outlinedElem = "browser-box";
+
+ // Do magic, and make sure it gets undone at the right time:
+ if (("focusedElemTimeout" in client) && client.focusedElemTimeout)
+ clearTimeout(client.focusedElemTimeout);
+ outlineFocusedElem(outlinedElem);
+ client.focusedElemTimeout = setTimeout(outlineFocusedElem, 1000, "");
+}
+
+function outlineFocusedElem(id)
+{
+ var outlinedElements = ["user-list-box", "browser-box", "multiline-hug-box",
+ "singleline-hug-box"];
+ for (var i = 0; i < outlinedElements.length; i++)
+ {
+ var elem = document.getElementById(outlinedElements[i]);
+ if (outlinedElements[i] == id)
+ elem.setAttribute("focusobvious", "true");
+ else
+ elem.removeAttribute("focusobvious");
+ }
+ client.focusedElemTimeout = 0;
+}
+
+/* valid values for |what| are "superfluous", "activity", and "attention".
+ * final value for state is dependant on priority of the current state, and the
+ * new state. the priority is: normal < superfluous < activity < attention.
+ */
+function setTabState(source, what, callback)
+{
+ if (typeof source != "object")
+ {
+ if (!ASSERT(source in client.viewsArray,
+ "INVALID SOURCE passed to setTabState"))
+ return;
+ source = client.viewsArray[source].source;
+ }
+
+ callback = callback || false;
+
+ var tb = source.dispatch("create-tab-for-view", { view: source });
+ var vk = Number(tb.getAttribute("viewKey"));
+
+ var current = ("currentObject" in client && client.currentObject == source);
+
+ /* We want to play sounds if they're from a non-current view, or we don't
+ * have focus at all. Also make sure stalk matches always play sounds.
+ * Also make sure we don't play on the 2nd half of the flash (Callback).
+ */
+ if (!callback && (!window.isFocused || !current || (what == "attention")))
+ {
+ if (what == "attention")
+ playEventSounds(source.TYPE, "stalk", source);
+ else if (what == "activity")
+ playEventSounds(source.TYPE, "chat", source);
+ else if (what == "superfluous")
+ playEventSounds(source.TYPE, "event", source);
+ }
+
+ // Only change the tab's colour if it's not the active view.
+ if (!current)
+ {
+ var state = tb.getAttribute("state");
+ if (state == what)
+ {
+ /* if the tab state has an equal priority to what we are setting
+ * then blink it */
+ if (client.prefs["activityFlashDelay"] > 0)
+ {
+ tb.setAttribute("state", "normal");
+ setTimeout(setTabState, client.prefs["activityFlashDelay"],
+ vk, what, true);
+ }
+ }
+ else
+ {
+ if (state == "normal" || state == "superfluous" ||
+ (state == "activity" && what == "attention"))
+ {
+ /* if the tab state has a lower priority than what we are
+ * setting, change it to the new state */
+ tb.setAttribute("state", what);
+ /* we only change the activity list if priority has increased */
+ if (what == "attention")
+ client.activityList[vk] = "!";
+ else if (what == "activity")
+ client.activityList[vk] = "+";
+ else
+ {
+ /* this is functionally equivalent to "+" for now */
+ client.activityList[vk] = "-";
+ }
+ updateTitle();
+ }
+ else
+ {
+ /* the current state of the tab has a higher priority than the
+ * new state.
+ * blink the new lower state quickly, then back to the old */
+ if (client.prefs["activityFlashDelay"] > 0)
+ {
+ tb.setAttribute("state", what);
+ setTimeout(setTabState,
+ client.prefs["activityFlashDelay"], vk,
+ state, true);
+ }
+ }
+ }
+ }
+}
+
+function notifyAttention (source)
+{
+ if (typeof source != "object")
+ source = client.viewsArray[source].source;
+
+ if (client.currentObject != source)
+ {
+ var tb = dispatch("create-tab-for-view", { view: source });
+ var vk = Number(tb.getAttribute("viewKey"));
+
+ tb.setAttribute ("state", "attention");
+ client.activityList[vk] = "!";
+ updateTitle();
+ }
+
+ if (client.prefs["notify.aggressive"])
+ window.getAttention();
+
+}
+
+function setDebugMode(mode)
+{
+ if (mode.indexOf("t") != -1)
+ client.debugHook.enabled = true;
+ else
+ client.debugHook.enabled = false;
+
+ if (mode.indexOf("c") != -1)
+ client.dbgContexts = true;
+ else
+ delete client.dbgContexts;
+
+ if (mode.indexOf("d") != -1)
+ client.dbgDispatch = true;
+ else
+ delete client.dbgDispatch;
+}
+
+function setListMode(mode)
+{
+ var elem = document.getElementById("user-list");
+ if (mode)
+ elem.setAttribute("mode", mode);
+ else
+ elem.removeAttribute("mode");
+ if (elem && elem.view && elem.treeBoxObject)
+ {
+ elem.treeBoxObject.clearStyleAndImageCaches();
+ elem.treeBoxObject.invalidate();
+ }
+}
+
+function updateUserList()
+{
+ var node, chan;
+
+ node = document.getElementById("user-list");
+ if (!node.view)
+ return;
+
+ if (("currentObject" in client) && client.currentObject &&
+ client.currentObject.TYPE == "IRCChannel")
+ {
+ reSortUserlist(client.currentObject);
+ }
+}
+
+function reSortUserlist(channel)
+{
+ if (!channel || !channel.userList)
+ return;
+ channel.userList.childData.reSort();
+}
+
+function getNicknameForUserlistRow(index)
+{
+ // This wouldn't be so hard if APIs didn't change so much... see bug 221619
+ var userlist = document.getElementById("user-list");
+ if (userlist.columns)
+ var col = userlist.columns.getNamedColumn("usercol");
+ else
+ col = "usercol";
+ return userlist.view.getCellText(index, col);
+}
+
+function getFrameForDOMWindow(window)
+{
+ var frame;
+ for (var i = 0; i < client.deck.childNodes.length; i++)
+ {
+ frame = client.deck.childNodes[i];
+ if (frame.contentWindow == window)
+ return frame;
+ }
+ return undefined;
+}
+
+function replaceColorCodes(msg)
+{
+ // Find things that look like URLs and escape the color code inside of those
+ // to prevent munging the URLs resulting in broken links. Leave codes at
+ // the start of the URL alone.
+ msg = msg.replace(new RegExp(client.linkRE.source, "g"), function(url, _foo, scheme) {
+ if (scheme && !client.checkURLScheme(scheme))
+ return url;
+ return url.replace(/%[BC][0-9A-Fa-f]/g, function(hex, index) {
+ // as JS does not support lookbehind and we don't want to consume the
+ // preceding character, we test for an existing %% manually
+ var needPercent = ("%" == url.substr(index - 1, 1)) || (index == 0);
+ return (needPercent ? "" : "%") + hex;
+ });
+ });
+
+ // mIRC codes: underline, bold, Original (reset), colors, reverse colors.
+ msg = msg.replace(/(^|[^%])%U/g, "$1\x1f");
+ msg = msg.replace(/(^|[^%])%B/g, "$1\x02");
+ msg = msg.replace(/(^|[^%])%O/g, "$1\x0f");
+ msg = msg.replace(/(^|[^%])%C/g, "$1\x03");
+ msg = msg.replace(/(^|[^%])%R/g, "$1\x16");
+ // %%[UBOCR] --> %[UBOCR].
+ msg = msg.replace(/%(%[UBOCR])/g, "$1");
+
+ return msg;
+}
+
+function decodeColorCodes(msg)
+{
+ // %[UBOCR] --> %%[UBOCR].
+ msg = msg.replace(/(%[UBOCR])/g, "%$1");
+ // Put %-codes back in place of special character codes.
+ msg = msg.replace(/\x1f/g, "%U");
+ msg = msg.replace(/\x02/g, "%B");
+ msg = msg.replace(/\x0f/g, "%O");
+ msg = msg.replace(/\x03/g, "%C");
+ msg = msg.replace(/\x16/g, "%R");
+
+ return msg;
+}
+
+function removeColorCodes(msg)
+{
+ msg = msg.replace(/[\x1f\x02\x0f\x16]/g, "");
+ // We need this to be global:
+ msg = msg.replace(new RegExp(client.colorRE.source, "g"), "");
+ return msg;
+}
+
+client.progressListener = new Object();
+
+client.progressListener.QueryInterface =
+function qi(iid)
+{
+ return this;
+}
+
+client.progressListener.onStateChange =
+function client_statechange (webProgress, request, stateFlags, status)
+{
+ const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
+ const START = nsIWebProgressListener.STATE_START;
+ const STOP = nsIWebProgressListener.STATE_STOP;
+ const IS_NETWORK = nsIWebProgressListener.STATE_IS_NETWORK;
+ const IS_DOCUMENT = nsIWebProgressListener.STATE_IS_DOCUMENT;
+ const IS_REQUEST = nsIWebProgressListener.STATE_IS_REQUEST;
+
+ var frame;
+ //dd("progressListener.onStateChange(" + stateFlags.toString(16) + ")");
+
+ // We only care about the initial start of loading, not the loading of
+ // and page sub-components (IS_DOCUMENT, etc.).
+ if ((stateFlags & START) && (stateFlags & IS_NETWORK) &&
+ (stateFlags & IS_DOCUMENT))
+ {
+ frame = getFrameForDOMWindow(webProgress.DOMWindow);
+ if (!frame)
+ {
+ dd("can't find frame for window (start)");
+ try
+ {
+ webProgress.removeProgressListener(this);
+ }
+ catch(ex)
+ {
+ dd("Exception removing progress listener (start): " + ex);
+ }
+ }
+ }
+ // We only want to know when the *network* stops, not the page's
+ // individual components (STATE_IS_REQUEST/STATE_IS_DOCUMENT/somesuch).
+ else if ((stateFlags & STOP) && (stateFlags & IS_NETWORK))
+ {
+ frame = getFrameForDOMWindow(webProgress.DOMWindow);
+ if (!frame)
+ {
+ dd("can't find frame for window (stop)");
+ try
+ {
+ webProgress.removeProgressListener(this);
+ }
+ catch(ex)
+ {
+ dd("Exception removing progress listener (stop): " + ex);
+ }
+ }
+ else
+ {
+ var cwin = getContentWindow(frame);
+ if (cwin && "initOutputWindow" in cwin)
+ {
+ if (!("_called_initOutputWindow" in cwin))
+ {
+ cwin._called_initOutputWindow = true;
+ cwin.initOutputWindow(client, frame.source, onMessageViewClick);
+ cwin.changeCSS(frame.source.getFontCSS("data"), "cz-fonts");
+ scrollDown(frame, true);
+ //dd("initOutputWindow(" + frame.source.getURL() + ")");
+ }
+ }
+ // XXX: For about:blank it won't find initOutputWindow. Cope.
+ else if (!cwin || !cwin.location ||
+ (cwin.location.href != "about:blank"))
+ {
+ // This should totally never ever happen. It will if we get in a
+ // fight with xpcnativewrappers, though. Oops:
+ dd("Couldn't find a content window or its initOutputWindow " +
+ "function. This is BAD!");
+ }
+ }
+ }
+ // Requests stopping are either the page, or its components loading. We're
+ // interested in its components.
+ else if ((stateFlags & STOP) && (stateFlags & IS_REQUEST))
+ {
+ frame = getFrameForDOMWindow(webProgress.DOMWindow);
+ if (frame)
+ {
+ var cwin = getContentWindow(frame);
+ if (cwin && ("_called_initOutputWindow" in cwin))
+ {
+ scrollDown(frame, false);
+ //dd("scrollDown(" + frame.source.getURL() + ")");
+ }
+ }
+
+ }
+}
+
+client.progressListener.onProgressChange =
+function client_progresschange (webProgress, request, currentSelf, totalSelf,
+ currentMax, selfMax)
+{
+}
+
+client.progressListener.onLocationChange =
+function client_locationchange (webProgress, request, uri)
+{
+}
+
+client.progressListener.onStatusChange =
+function client_statuschange (webProgress, request, status, message)
+{
+}
+
+client.progressListener.onSecurityChange =
+function client_securitychange (webProgress, request, state)
+{
+}
+
+client.installPlugin =
+function cli_installPlugin(name, source)
+{
+ function checkPluginInstalled(name, path)
+ {
+ var installed = path.exists();
+ installed |= (name in client.plugins);
+
+ if (installed)
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_ALREADY_INST, MT_ERROR);
+ throw CZ_PI_ABORT;
+ }
+ };
+ function getZipEntry(reader, entryEnum)
+ {
+ // nsIZipReader was rewritten...
+ var itemName = entryEnum.getNext();
+ if (typeof itemName != "string")
+ name = itemName.QueryInterface(nsIZipEntry).name;
+ return itemName;
+ };
+ function checkZipMore(items)
+ {
+ return (("hasMoreElements" in items) && items.hasMoreElements()) ||
+ (("hasMore" in items) && items.hasMore());
+ };
+
+ const DIRECTORY_TYPE = Components.interfaces.nsIFile.DIRECTORY_TYPE;
+ const CZ_PI_ABORT = "CZ_PI_ABORT";
+ const nsIZipEntry = Components.interfaces.nsIZipEntry;
+
+ var dest;
+ // Find a suitable location if there was none specified.
+ var destList = client.prefs["initialScripts"];
+ if ((destList.length == 0) ||
+ ((destList.length == 1) && /^\s*$/.test(destList[0])))
+ {
+ // Reset to default because it is empty.
+ try
+ {
+ client.prefManager.clearPref("initialScripts");
+ }
+ catch(ex) {/* If this really fails, we're mostly screwed anyway */}
+ destList = client.prefs["initialScripts"];
+ }
+
+ // URLs for initialScripts can be relative (the default is):
+ var profilePath = getURLSpecFromFile(client.prefs["profilePath"]);
+ profilePath = Services.io.newURI(profilePath);
+ for (var i = 0; i < destList.length; i++)
+ {
+ var destURL = Services.io.newURI(destList[i], null, profilePath);
+ var file = new nsLocalFile(getFileFromURLSpec(destURL.spec).path);
+ if (file.exists() && file.isDirectory()) {
+ // A directory that exists! We'll take it!
+ dest = file.clone();
+ break;
+ }
+ }
+ if (!dest) {
+ display(MSG_INSTALL_PLUGIN_ERR_INSTALL_TO, MT_ERROR);
+ return;
+ }
+
+ try {
+ if (typeof source == "string")
+ source = getFileFromURLSpec(source);
+ }
+ catch (ex)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_CHECK_SD, ex), MT_ERROR);
+ return;
+ }
+
+ display(getMsg(MSG_INSTALL_PLUGIN_INSTALLING, [source.path, dest.path]),
+ MT_INFO);
+
+ var ary;
+ if (source.path.match(/\.(jar|zip)$/i))
+ {
+ try
+ {
+ var zipReader = newObject("@mozilla.org/libjar/zip-reader;1",
+ "nsIZipReader");
+ zipReader.open(source);
+
+ // This is set to the base path found on ALL items in the zip file.
+ // when we extract, this WILL BE REMOVED from all paths.
+ var zipPathBase = "";
+ // This always points to init.js, even if we're messing with paths.
+ var initPath = "init.js";
+
+ // Look for init.js within a directory...
+ var items = zipReader.findEntries("*/init.js");
+ while (checkZipMore(items))
+ {
+ var itemName = getZipEntry(zipReader, items);
+ // Do we already have one?
+ if (zipPathBase)
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_MANY_INITJS, MT_WARN);
+ throw CZ_PI_ABORT;
+ }
+ zipPathBase = itemName.match(/^(.*\/)init.js$/)[1];
+ initPath = itemName;
+ }
+
+ if (zipPathBase)
+ {
+ // We have a base for init.js, assert that all files are inside
+ // it. If not, we drop the path and install exactly as-is
+ // instead (which will probably cause it to not work because the
+ // init.js isn't in the right place).
+ items = zipReader.findEntries("*");
+ while (checkZipMore(items))
+ {
+ itemName = getZipEntry(zipReader, items);
+ if (itemName.substr(0, zipPathBase.length) != zipPathBase)
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_MIXED_BASE, MT_WARN);
+ zipPathBase = "";
+ break;
+ }
+ }
+ }
+
+ // Test init.js for a plugin ID.
+ var initJSFile = getTempFile(client.prefs["profilePath"],
+ "install-plugin.temp");
+ zipReader.extract(initPath, initJSFile);
+ initJSFile.permissions = 438; // 0666
+ var initJSFileH = fopen(initJSFile, "<");
+ var initJSData = initJSFileH.read();
+ initJSFileH.close();
+ initJSFile.remove(false);
+
+ //XXXgijs: this is fragile. Anyone got a better idea?
+ ary = initJSData.match(/plugin\.id\s*=\s*(['"])(.*?)(\1)/);
+ if (ary && (name != ary[2]))
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_WARN_NAME, [name, ary[2]]),
+ MT_WARN);
+ name = ary[2];
+ }
+
+ dest.append(name);
+ checkPluginInstalled(name, dest);
+
+ dest.create(DIRECTORY_TYPE, 0o700);
+
+ // Actually extract files...
+ var destInit;
+ items = zipReader.findEntries("*");
+ while (checkZipMore(items))
+ {
+ itemName = getZipEntry(zipReader, items);
+ if (!itemName.match(/\/$/))
+ {
+ var dirs = itemName;
+ // Remove common path if there is one.
+ if (zipPathBase)
+ dirs = dirs.substring(zipPathBase.length);
+ dirs = dirs.split("/");
+
+ // Construct the full path for the extracted file...
+ var zipFile = dest.clone();
+ for (var i = 0; i < dirs.length - 1; i++)
+ {
+ zipFile.append(dirs[i]);
+ if (!zipFile.exists())
+ zipFile.create(DIRECTORY_TYPE, 0o700);
+ }
+ zipFile.append(dirs[dirs.length - 1]);
+
+ if (zipFile.leafName == "init.js")
+ destInit = zipFile;
+
+ zipReader.extract(itemName, zipFile);
+ zipFile.permissions = 438; // 0666
+ }
+ }
+
+ var rv = dispatch("load ", {url: getURLSpecFromFile(destInit)});
+ if (rv)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_DONE, name));
+ }
+ else
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_REMOVING, MT_ERROR);
+ dest.remove(true);
+ }
+ }
+ catch (ex)
+ {
+ if (ex != CZ_PI_ABORT)
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_EXTRACT, ex), MT_ERROR);
+ zipReader.close();
+ return;
+ }
+ try
+ {
+ zipReader.close();
+ }
+ catch (ex)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_EXTRACT, ex), MT_ERROR);
+ }
+ }
+ else if (source.path.match(/\.(js)$/i))
+ {
+ try
+ {
+ // Test init.js for a plugin ID.
+ var initJSFileH = fopen(source, "<");
+ var initJSData = initJSFileH.read();
+ initJSFileH.close();
+
+ ary = initJSData.match(/plugin\.id\s*=\s*(['"])(.*?)(\1)/);
+ if (ary && (name != ary[2]))
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_WARN_NAME, [name, ary[2]]),
+ MT_WARN);
+ name = ary[2];
+ }
+
+ dest.append(name);
+ checkPluginInstalled(name, dest);
+
+ dest.create(DIRECTORY_TYPE, 0o700);
+
+ dest.append("init.js");
+
+ var destFile = fopen(dest, ">");
+ destFile.write(initJSData);
+ destFile.close();
+
+ var rv = dispatch("load", {url: getURLSpecFromFile(dest)});
+ if (rv)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_DONE, name));
+ }
+ else
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_REMOVING, MT_ERROR);
+ // We've appended "init.js" before, so go back up one level:
+ dest.parent.remove(true);
+ }
+ }
+ catch(ex)
+ {
+ if (ex != CZ_PI_ABORT)
+ {
+ display(getMsg(MSG_INSTALL_PLUGIN_ERR_INSTALLING, ex),
+ MT_ERROR);
+ }
+ }
+ }
+ else
+ {
+ display(MSG_INSTALL_PLUGIN_ERR_FORMAT, MT_ERROR);
+ }
+}
+
+client.uninstallPlugin =
+function cli_uninstallPlugin(plugin)
+{
+ if (!disablePlugin(plugin, true))
+ return;
+ delete client.plugins[plugin.id];
+ let file = getFileFromURLSpec(plugin.cwd);
+ if (file.exists() && file.isDirectory())
+ {
+ // Delete the directory and contents.
+ file.remove(true);
+ }
+ display(getMsg(MSG_PLUGIN_UNINSTALLED, plugin.id));
+}
+
+function syncOutputFrame(obj, nesting)
+{
+ const nsIWebProgress = Components.interfaces.nsIWebProgress;
+ const WINDOW = nsIWebProgress.NOTIFY_STATE_WINDOW;
+ const NETWORK = nsIWebProgress.NOTIFY_STATE_NETWORK;
+ const ALL = nsIWebProgress.NOTIFY_ALL;
+
+ var iframe = obj.frame;
+
+ function tryAgain(nLevel)
+ {
+ syncOutputFrame(obj, nLevel);
+ };
+
+ if (typeof nesting != "number")
+ nesting = 0;
+
+ if (nesting > 10)
+ {
+ dd("Aborting syncOutputFrame, taken too many tries.");
+ return;
+ }
+
+ /* We leave the progress listener attached so try to remove it first,
+ * should we be called on an already-set-up view.
+ */
+ try
+ {
+ iframe.removeProgressListener(client.progressListener, ALL);
+ }
+ catch (ex)
+ {
+ }
+
+ try
+ {
+ if (getContentDocument(iframe) && ("webProgress" in iframe))
+ {
+ var url = obj.prefs["outputWindowURL"];
+ iframe.addProgressListener(client.progressListener, ALL);
+ iframe.loadURI(url);
+ }
+ else
+ {
+ setTimeout(tryAgain, 500, nesting + 1);
+ }
+ }
+ catch (ex)
+ {
+ dd("caught exception showing session view, will try again later.");
+ dd(dumpObjectTree(ex) + "\n");
+ setTimeout(tryAgain, 500, nesting + 1);
+ }
+}
+
+function createMessages(source)
+{
+ playEventSounds(source.TYPE, "start", source);
+
+ source.messages = document.createElementNS(XHTML_NS, "html:table");
+ source.messages.setAttribute("class", "msg-table");
+ source.messages.setAttribute("view-type", source.TYPE);
+ source.messages.setAttribute("role", "log");
+ source.messages.setAttribute("aria-live", "polite");
+
+ var tbody = document.createElementNS(XHTML_NS, "html:tbody");
+ source.messages.appendChild (tbody);
+ source.messageCount = 0;
+}
+
+/* Gets the <tab> element associated with a view object.
+ * If |create| is present, and true, tab is created if not found.
+ */
+function getTabForObject(source, create)
+{
+ var name;
+
+ if (!ASSERT(source, "UNDEFINED passed to getTabForObject"))
+ return null;
+
+ if (!ASSERT("viewName" in source, "INVALID OBJECT in getTabForObject"))
+ return null;
+
+ name = source.viewName;
+
+ var tb, id = "tb[" + name + "]";
+ var matches = 1;
+
+ for (var i in client.viewsArray)
+ {
+ if (client.viewsArray[i].source == source)
+ {
+ tb = client.viewsArray[i].tb;
+ break;
+ }
+ else
+ if (client.viewsArray[i].tb.getAttribute("id") == id)
+ id = "tb[" + name + "<" + (++matches) + ">]";
+ }
+
+ /* If we found a <tab>, are allowed to create it, and have a pending view
+ * context, then we're expected to move the existing tab according to said
+ * context. We do that by removing the tab here, and below the creation
+ * code (which is not run) we readd it in the correct location.
+ */
+ if (tb && create && (typeof client.pendingViewContext == "object"))
+ {
+ /* If we're supposed to insert before ourselves, or the next <tab>,
+ * then bail out (since it's a no-op).
+ */
+ var tabBefore = client.pendingViewContext.tabInsertBefore;
+ if (tabBefore)
+ {
+ var tbAfter = tb.nextSibling;
+ while (tbAfter && tbAfter.collapsed && tbAfter.hidden)
+ tbAfter = tbAfter.nextSibling;
+ if ((tabBefore == tb) || (tabBefore == tbAfter))
+ return tb;
+ }
+
+ var viewKey = Number(tb.getAttribute("viewKey"));
+ arrayRemoveAt(client.viewsArray, viewKey);
+ for (i = viewKey; i < client.viewsArray.length; i++)
+ client.viewsArray[i].tb.setAttribute("viewKey", i);
+ client.tabs.removeChild(tb);
+ }
+ else if (tb || (!tb && !create))
+ {
+ /* Either: we have a tab and don't want it moved, or didn't find one
+ * and don't wish to create one.
+ */
+ return tb;
+ }
+
+ // Didn't found a <tab>, but we're allowed to create one.
+ if (!tb && create)
+ {
+ if (!("messages" in source) || source.messages == null)
+ createMessages(source);
+
+ tb = document.createElement("tab");
+ tb.setAttribute("ondragstart", "tabsDNDObserver.onDragStart(event);");
+ tb.setAttribute("href", source.getURL());
+ tb.setAttribute("name", source.unicodeName);
+ tb.setAttribute("onclick", "onTabClick(event, this.id);");
+ // This wouldn't be here if there was a supported CSS property for it.
+ tb.setAttribute("crop", "center");
+ tb.setAttribute("context", "context:tab");
+ tb.setAttribute("class", "tab-bottom view-button");
+ tb.setAttribute("id", id);
+ tb.setAttribute("state", "normal");
+ name = source.prefs["tabLabel"] || name;
+ tb.setAttribute("label", name + (matches > 1 ? "<" + matches + ">" : ""));
+ tb.setAttribute("tooltiptext", source.viewName);
+ tb.view = source;
+
+ var browser = document.createElement("browser");
+ browser.setAttribute("class", "output-container");
+ browser.setAttribute("type", "content");
+ browser.setAttribute("flex", "1");
+ browser.setAttribute("tooltip", "html-tooltip-node");
+ browser.setAttribute("onclick",
+ "return onMessageViewClick(event)");
+ browser.setAttribute("onmousedown",
+ "return onMessageViewMouseDown(event)");
+ browser.setAttribute("oncontextmenu",
+ "return onMessageViewContextMenu(event)");
+ browser.setAttribute("ondragover",
+ "contentDNDObserver.onDragOver(event);");
+ browser.setAttribute("ondrop", "contentDNDObserver.onDrop(event);");
+ browser.source = source;
+ source.frame = browser;
+ ASSERT(client.deck, "no deck?");
+ client.deck.appendChild(browser);
+ syncOutputFrame(source);
+
+ if (!("userList" in source) && (source.TYPE == "IRCChannel"))
+ {
+ source.userListShare = new Object();
+ source.userList = new XULTreeView(source.userListShare);
+ source.userList.getRowProperties = ul_getrowprops;
+ source.userList.getCellProperties = ul_getcellprops;
+ source.userList.childData.setSortDirection(1);
+ }
+ }
+
+ var beforeTab = null;
+ if (typeof client.pendingViewContext == "object")
+ {
+ var c = client.pendingViewContext;
+ /* If we have a <tab> to insert before, and it is still in the tabs,
+ * move the newly-created <tab> into the right place.
+ */
+ if (c.tabInsertBefore && (c.tabInsertBefore.parentNode == client.tabs))
+ beforeTab = c.tabInsertBefore;
+ }
+
+ if (beforeTab)
+ {
+ var viewKey = beforeTab.getAttribute("viewKey");
+ arrayInsertAt(client.viewsArray, viewKey, {source: source, tb: tb});
+ for (i = viewKey; i < client.viewsArray.length; i++)
+ client.viewsArray[i].tb.setAttribute("viewKey", i);
+ client.tabs.insertBefore(tb, beforeTab);
+ }
+ else
+ {
+ client.viewsArray.push({source: source, tb: tb});
+ tb.setAttribute("viewKey", client.viewsArray.length - 1);
+ client.tabs.appendChild(tb);
+ }
+
+ updateTabAttributes();
+
+ return tb;
+}
+
+function updateTabAttributes()
+{
+ /* XXX: Workaround for Gecko bugs 272646 and 261826. Note that this breaks
+ * the location of the spacers before and after the tabs but, due to our
+ * own <spacer>, their flex was not being utilised anyway.
+ */
+ var tabOrdinal = 0;
+ for (var tab = client.tabs.firstChild; tab; tab = tab.nextSibling)
+ tab.ordinal = tabOrdinal++;
+
+ /* XXX: Workaround for tabbox.xml not coping with updating attributes when
+ * tabs are moved. We correct the "first-tab", "last-tab", "beforeselected"
+ * and "afterselected" attributes.
+ *
+ * "last-tab" and "beforeselected" are updated on each valid (non-collapsed
+ * and non-hidden) tab found, to avoid having to work backwards as well as
+ * forwards. "first-tab" and "afterselected" are just set the once each.
+ * |foundSelected| tracks where we are in relation to the selected tab.
+ */
+ var tabAttrs = {
+ "first-tab": null,
+ "last-tab": null,
+ "beforeselected": null,
+ "afterselected": null
+ };
+ var foundSelected = "before";
+ for (tab = client.tabs.firstChild; tab; tab = tab.nextSibling)
+ {
+ if (tab.collapsed || tab.hidden)
+ continue;
+
+ if (!tabAttrs["first-tab"])
+ tabAttrs["first-tab"] = tab;
+ tabAttrs["last-tab"] = tab;
+
+ if ((foundSelected == "before") && tab.selected)
+ foundSelected = "on";
+ else if (foundSelected == "on")
+ foundSelected = "after";
+
+ if (foundSelected == "before")
+ tabAttrs["beforeselected"] = tab;
+ if ((foundSelected == "after") && !tabAttrs["afterselected"])
+ tabAttrs["afterselected"] = tab;
+ }
+
+ // After picking a tab for each attribute, apply them to the tabs.
+ for (tab = client.tabs.firstChild; tab; tab = tab.nextSibling)
+ {
+ for (var attr in tabAttrs)
+ {
+ if (tabAttrs[attr] == tab)
+ tab.setAttribute(attr, "true");
+ else
+ tab.removeAttribute(attr);
+ }
+ }
+}
+
+// Properties getter for user list tree view
+function ul_getrowprops(index)
+{
+ if ((index < 0) || (index >= this.childData.childData.length))
+ {
+ return "";
+ }
+
+ // See bug 432482 - work around Gecko deficiency.
+ if (!this.selection.isSelected(index))
+ {
+ return "unselected";
+ }
+
+ return "";
+}
+
+// Properties getter for user list tree view
+function ul_getcellprops(index, column)
+{
+ if ((index < 0) || (index >= this.childData.childData.length))
+ {
+ return "";
+ }
+
+ var resultProps = [];
+
+ // See bug 432482 - work around Gecko deficiency.
+ if (!this.selection.isSelected(index))
+ resultProps.push("unselected");
+
+ var userObj = this.childData.childData[index]._userObj;
+
+ resultProps.push("voice-" + userObj.isVoice);
+ resultProps.push("op-" + userObj.isOp);
+ resultProps.push("halfop-" + userObj.isHalfOp);
+ resultProps.push("admin-" + userObj.isAdmin);
+ resultProps.push("founder-" + userObj.isFounder);
+ resultProps.push("away-" + userObj.isAway);
+
+ return resultProps.join(" ");
+}
+
+var contentDNDObserver = {
+ onDragOver(aEvent) {
+ if (aEvent.target == aEvent.dataTransfer.mozSourceNode)
+ return;
+
+ if (Services.droppedLinkHandler.canDropLink(aEvent, true))
+ aEvent.preventDefault();
+ },
+
+ onDrop(aEvent) {
+ aEvent.stopPropagation();
+
+ var url = Services.droppedLinkHandler.dropLink(aEvent, {});
+ if (!url || url.search(client.linkRE) == -1)
+ return;
+
+ if (url.search(/\.css$/i) != -1 && confirm(getMsg(MSG_TABDND_DROP, url)))
+ dispatch("motif", {"motif": url});
+ else if (url.search(/^ircs?:\/\//i) != -1)
+ dispatch("goto-url", {"url": url});
+ },
+}
+
+var tabsDNDObserver = {
+ onDragOver(aEvent) {
+ if (aEvent.target == aEvent.dataTransfer.mozSourceNode)
+ return;
+
+ // If we're not accepting the drag, don't show the marker either.
+ if (!Services.droppedLinkHandler.canDropLink(aEvent, true)) {
+ client.tabDragBar.collapsed = true;
+ return;
+ }
+
+ aEvent.preventDefault();
+
+ /* Locate the tab we're about to drop onto. We split tabs in half, dropping
+ * on the side closest to the mouse, or after the last tab if the mouse is
+ * somewhere beyond all the tabs.
+ */
+ var ltr = (window.getComputedStyle(client.tabs, null).direction == "ltr");
+ var newPosition = client.tabs.firstChild.boxObject.x;
+ for (var dropTab = client.tabs.firstChild; dropTab;
+ dropTab = dropTab.nextSibling)
+ {
+ if (dropTab.collapsed || dropTab.hidden)
+ continue;
+ var bo = dropTab.boxObject;
+ if ((ltr && (aEvent.screenX < bo.screenX + bo.width / 2)) ||
+ (!ltr && (aEvent.screenX > bo.screenX + bo.width / 2)))
+ {
+ break;
+ }
+ newPosition = bo.x + bo.width;
+ }
+
+ // Reposition the drop marker and show it. In that order.
+ client.tabDragMarker.style.MozMarginStart = newPosition + "px";
+ client.tabDragBar.collapsed = false;
+ },
+
+ onDragExit(aEvent) {
+ aEvent.stopPropagation();
+
+ /* We've either stopped being part of a drag operation, or the dragging is
+ * somewhere away from us.
+ */
+ client.tabDragBar.collapsed = true;
+ },
+
+ onDrop(aEvent) {
+ aEvent.stopPropagation();
+
+ // Dragging has finished.
+ client.tabDragBar.collapsed = true;
+
+ // See comment above |var tabsDropObserver|.
+ var url = Services.droppedLinkHandler.dropLink(aEvent, {});
+ if (!url || !(url.match(/^ircs?:/) || url.match(/^x-irc-dcc-(chat|file):/)))
+ return;
+
+ // Find the tab to insertBefore() the new one.
+ var ltr = (window.getComputedStyle(client.tabs, null).direction == "ltr");
+ for (var dropTab = client.tabs.firstChild; dropTab;
+ dropTab = dropTab.nextSibling)
+ {
+ if (dropTab.collapsed || dropTab.hidden)
+ continue;
+ var bo = dropTab.boxObject;
+ if ((ltr && (aEvent.screenX < bo.screenX + bo.width / 2)) ||
+ (!ltr && (aEvent.screenX > bo.screenX + bo.width / 2)))
+ {
+ break;
+ }
+ }
+
+ // Check if the URL is already in the views.
+ for (var i = 0; i < client.viewsArray.length; i++)
+ {
+ var view = client.viewsArray[i].source;
+ if (view.getURL() == url)
+ {
+ client.pendingViewContext = { tabInsertBefore: dropTab };
+ dispatch("create-tab-for-view", { view: view });
+ delete client.pendingViewContext;
+ return;
+ }
+ }
+
+ // URL not found in tabs, so force it into life - this may connect/rejoin.
+ if (url.substring(0, 3) == "irc")
+ gotoIRCURL(url, { tabInsertBefore: dropTab });
+ },
+
+ onDragStart(aEvent) {
+ var tb = aEvent.currentTarget;
+ var href = tb.getAttribute("href");
+ var name = tb.getAttribute("name");
+
+ /* x-moz-url has the format "<url>\n<name>", goodie */
+ aEvent.dataTransfer.setData("text/x-moz-url", href + "\n" + name);
+ aEvent.dataTransfer.setData("text/unicode", href);
+ aEvent.dataTransfer.setData("text/plain", href);
+ aEvent.dataTransfer.setData("text/html", "<a href='" + href + "'>" +
+ name + "</a>");
+ },
+}
+
+var userlistDNDObserver = {
+ onDragStart(aEvent) {
+ var col = {};
+ var row = {};
+ var cell = {};
+ var tree = document.getElementById('user-list');
+ tree.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY,
+ row, col, cell);
+ // Check whether we're actually on a normal row and cell
+ if (!cell.value || (row.value == -1))
+ return;
+
+ var nickname = getNicknameForUserlistRow(row.value);
+ aEvent.dataTransfer.setData("text/unicode", nickname);
+ aEvent.dataTransfer.setData("text/plain", nickname);
+ },
+}
+
+function deleteTab(tb)
+{
+ if (!ASSERT(tb.hasAttribute("viewKey"),
+ "INVALID OBJECT passed to deleteTab (" + tb + ")"))
+ {
+ return null;
+ }
+
+ var key = Number(tb.getAttribute("viewKey"));
+
+ // Re-index higher tabs.
+ for (var i = key + 1; i < client.viewsArray.length; i++)
+ client.viewsArray[i].tb.setAttribute("viewKey", i - 1);
+ arrayRemoveAt(client.viewsArray, key);
+ client.tabs.removeChild(tb);
+ setTimeout(updateTabAttributes, 0);
+
+ return key;
+}
+
+function deleteFrame(view)
+{
+ const nsIWebProgress = Components.interfaces.nsIWebProgress;
+ const ALL = nsIWebProgress.NOTIFY_ALL;
+
+ // We leave the progress listener attached so try to remove it.
+ try
+ {
+ view.frame.removeProgressListener(client.progressListener, ALL);
+ }
+ catch (ex)
+ {
+ dd(formatException(ex));
+ }
+
+ client.deck.removeChild(view.frame);
+ delete view.frame;
+}
+
+function filterOutput(msg, msgtype, dest)
+{
+ if ("outputFilters" in client)
+ {
+ for (var f in client.outputFilters)
+ {
+ if (client.outputFilters[f].enabled)
+ msg = client.outputFilters[f].func(msg, msgtype, dest);
+ }
+ }
+
+ return msg;
+}
+
+function updateTimestamps(view)
+{
+ if (!("messages" in view))
+ return;
+
+ view._timestampLast = "";
+ var node = view.messages.firstChild.firstChild;
+ var nested;
+ while (node)
+ {
+ if(node.className == "msg-nested-tr")
+ {
+ nested = node.firstChild.firstChild.firstChild.firstChild;
+ while (nested)
+ {
+ updateTimestampFor(view, nested);
+ nested = nested.nextSibling;
+ }
+ }
+ else
+ {
+ updateTimestampFor(view, node);
+ }
+ node = node.nextSibling;
+ }
+}
+
+function updateTimestampFor(view, displayRow, forceOldStamp)
+{
+ var time = new Date(1 * displayRow.getAttribute("timestamp"));
+ var tsCell = displayRow.firstChild;
+ if (!tsCell)
+ return;
+
+ var fmt;
+ if (view.prefs["timestamps"])
+ fmt = strftime(view.prefs["timestamps.display"], time);
+
+ while (tsCell.lastChild)
+ tsCell.removeChild(tsCell.lastChild);
+
+ var needStamp = fmt && (forceOldStamp || !view.prefs["collapseMsgs"] ||
+ (fmt != view._timestampLast));
+ if (needStamp)
+ tsCell.appendChild(document.createTextNode(fmt));
+ if (!forceOldStamp)
+ view._timestampLast = fmt;
+}
+
+client.updateMenus =
+function c_updatemenus(menus)
+{
+ // Don't bother if the menus aren't even created yet.
+ if (!client.initialized)
+ return null;
+
+ return this.menuManager.updateMenus(document, menus);
+}
+
+client.checkURLScheme =
+function c_checkURLScheme(url)
+{
+ if (!("schemes" in client))
+ {
+ var pfx = "@mozilla.org/network/protocol;1?name=";
+ var len = pfx.length;
+
+ client.schemes = new Object();
+ for (var c in Components.classes)
+ {
+ if (c.indexOf(pfx) == 0)
+ client.schemes[c.substr(len)] = true;
+ }
+ }
+ return (url.toLowerCase() in client.schemes);
+}
+
+client.adoptNode =
+function cli_adoptnode(node, doc)
+{
+ try
+ {
+ doc.adoptNode(node);
+ }
+ catch(ex)
+ {
+ dd(formatException(ex));
+ var err = ex.name;
+ // TypeError from before adoptNode was added; NOT_IMPL after.
+ if ((err == "TypeError") || (err == "NS_ERROR_NOT_IMPLEMENTED"))
+ client.adoptNode = cli_adoptnode_noop;
+ }
+ return node;
+}
+
+function cli_adoptnode_noop(node, doc)
+{
+ return node;
+}
+
+client.addNetwork =
+function cli_addnet(name, serverList, temporary)
+{
+ let net = new CIRCNetwork(name, serverList, client.eventPump, temporary);
+ client.networks[net.collectionKey] = net;
+}
+
+client.getNetwork =
+function cli_getnet(name)
+{
+ return client.networks[":" + name] || null;
+}
+
+client.removeNetwork =
+function cli_removenet(name)
+{
+ let net = client.getNetwork(name);
+
+ // Allow network a chance to clean up any mess.
+ if (typeof net.destroy == "function")
+ net.destroy();
+
+ delete client.networks[net.collectionKey];
+}
+
+client.connectToNetwork =
+function cli_connect(networkOrName, requireSecurity)
+{
+ var network;
+ var name;
+
+
+ if (isinstance(networkOrName, CIRCNetwork))
+ {
+ network = networkOrName;
+ }
+ else
+ {
+ name = networkOrName;
+ network = client.getNetwork(name);
+
+ if (!network)
+ {
+ display(getMsg(MSG_ERR_UNKNOWN_NETWORK, name), MT_ERROR);
+ return null;
+ }
+ }
+ name = network.unicodeName;
+
+ dispatch("create-tab-for-view", { view: network });
+ dispatch("set-current-view", { view: network });
+
+ if (network.isConnected())
+ {
+ network.display(getMsg(MSG_ALREADY_CONNECTED, name));
+ return network;
+ }
+
+ if (network.state != NET_OFFLINE)
+ return network;
+
+ if (network.prefs["nickname"] == DEFAULT_NICK)
+ network.prefs["nickname"] = prompt(MSG_ENTER_NICK, DEFAULT_NICK);
+
+ if (!("connecting" in network))
+ network.display(getMsg(MSG_NETWORK_CONNECTING, name));
+
+ network.connect(requireSecurity);
+
+ network.updateHeader();
+ client.updateHeader();
+ updateTitle();
+
+ return network;
+}
+
+
+client.getURL =
+function cli_geturl ()
+{
+ return "irc://";
+}
+
+client.load =
+function cli_load(url, scope)
+{
+ if (!("_loader" in client))
+ {
+ const LOADER_CTRID = "@mozilla.org/moz/jssubscript-loader;1";
+ const mozIJSSubScriptLoader =
+ Components.interfaces.mozIJSSubScriptLoader;
+
+ var cls;
+ if ((cls = Components.classes[LOADER_CTRID]))
+ client._loader = cls.getService(mozIJSSubScriptLoader);
+ }
+
+ if (client._loader.loadSubScriptWithOptions)
+ {
+ var opts = {target: scope, ignoreCache: true};
+ return client._loader.loadSubScriptWithOptions(url, opts);
+ }
+
+ return client._loader.loadSubScript(url, scope);
+}
+
+client.sayToCurrentTarget =
+function cli_say(msg, isInteractive)
+{
+ if ("say" in client.currentObject)
+ {
+ client.currentObject.dispatch("say", {message: msg}, isInteractive);
+ return;
+ }
+
+ switch (client.currentObject.TYPE)
+ {
+ case "IRCClient":
+ dispatch("eval", {expression: msg}, isInteractive);
+ break;
+
+ default:
+ if (msg != "")
+ display(MSG_ERR_NO_DEFAULT, MT_ERROR);
+ break;
+ }
+}
+
+CIRCNetwork.prototype.__defineGetter__("prefs", net_getprefs);
+function net_getprefs()
+{
+ if (!("_prefs" in this))
+ {
+ this._prefManager = getNetworkPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefs;
+}
+
+CIRCNetwork.prototype.__defineGetter__("prefManager", net_getprefmgr);
+function net_getprefmgr()
+{
+ if (!("_prefManager" in this))
+ {
+ this._prefManager = getNetworkPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefManager;
+}
+
+CIRCServer.prototype.__defineGetter__("prefs", srv_getprefs);
+function srv_getprefs()
+{
+ return this.parent.prefs;
+}
+
+CIRCServer.prototype.__defineGetter__("prefManager", srv_getprefmgr);
+function srv_getprefmgr()
+{
+ return this.parent.prefManager;
+}
+
+CIRCChannel.prototype.__defineGetter__("prefs", chan_getprefs);
+function chan_getprefs()
+{
+ if (!("_prefs" in this))
+ {
+ this._prefManager = getChannelPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefs;
+}
+
+CIRCChannel.prototype.__defineGetter__("prefManager", chan_getprefmgr);
+function chan_getprefmgr()
+{
+ if (!("_prefManager" in this))
+ {
+ this._prefManager = getChannelPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefManager;
+}
+
+CIRCUser.prototype.__defineGetter__("prefs", usr_getprefs);
+function usr_getprefs()
+{
+ if (!("_prefs" in this))
+ {
+ this._prefManager = getUserPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefs;
+}
+
+CIRCUser.prototype.__defineGetter__("prefManager", usr_getprefmgr);
+function usr_getprefmgr()
+{
+ if (!("_prefManager" in this))
+ {
+ this._prefManager = getUserPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefManager;
+}
+
+CIRCDCCUser.prototype.__defineGetter__("prefs", dccusr_getprefs);
+function dccusr_getprefs()
+{
+ if (!("_prefs" in this))
+ {
+ this._prefManager = getDCCUserPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefs;
+}
+
+CIRCDCCUser.prototype.__defineGetter__("prefManager", dccusr_getprefmgr);
+function dccusr_getprefmgr()
+{
+ if (!("_prefManager" in this))
+ {
+ this._prefManager = getDCCUserPrefManager(this);
+ this._prefs = this._prefManager.prefs;
+ }
+
+ return this._prefManager;
+}
+
+CIRCDCCChat.prototype.__defineGetter__("prefs", dccchat_getprefs);
+function dccchat_getprefs()
+{
+ return this.user.prefs;
+}
+
+CIRCDCCChat.prototype.__defineGetter__("prefManager", dccchat_getprefmgr);
+function dccchat_getprefmgr()
+{
+ return this.user.prefManager;
+}
+
+CIRCDCCFileTransfer.prototype.__defineGetter__("prefs", dccfile_getprefs);
+function dccfile_getprefs()
+{
+ return this.user.prefs;
+}
+
+CIRCDCCFileTransfer.prototype.__defineGetter__("prefManager", dccfile_getprefmgr);
+function dccfile_getprefmgr()
+{
+ return this.user.prefManager;
+}
+
+/* Displays a network-centric message on the most appropriate view.
+ *
+ * When |client.SLOPPY_NETWORKS| is |true|, messages will be displayed on the
+ * *current* view instead of the network view, if the current view is part of
+ * the same network.
+ */
+CIRCNetwork.prototype.display =
+function net_display(message, msgtype, sourceObj, destObj, tags)
+{
+ var o = getObjectDetails(client.currentObject);
+ if (client.SLOPPY_NETWORKS && client.currentObject != this &&
+ o.network == this && o.server && o.server.isConnected)
+ {
+ client.currentObject.display(message, msgtype, sourceObj, destObj,
+ tags);
+ }
+ else
+ {
+ this.displayHere(message, msgtype, sourceObj, destObj, tags);
+ }
+}
+
+/* Displays a channel-centric message on the most appropriate view.
+ *
+ * If the channel view already exists (visible or hidden), messages are added
+ * to it; otherwise, messages go to the *network* view.
+ */
+CIRCChannel.prototype.display =
+function chan_display(message, msgtype, sourceObj, destObj, tags)
+{
+ if ("messages" in this)
+ this.displayHere(message, msgtype, sourceObj, destObj, tags);
+ else
+ this.parent.parent.displayHere(message, msgtype, sourceObj, destObj,
+ tags);
+}
+
+/* Displays a user-centric message on the most appropriate view.
+ *
+ * If the user view already exists (visible or hidden), messages are added to
+ * it; otherwise, it goes to the *current* view if the current view is part of
+ * the same network, or the *network* view if not.
+ */
+CIRCUser.prototype.display =
+function usr_display(message, msgtype, sourceObj, destObj, tags)
+{
+ if ("messages" in this)
+ {
+ this.displayHere(message, msgtype, sourceObj, destObj, tags);
+ }
+ else
+ {
+ var o = getObjectDetails(client.currentObject);
+ if (o.server && o.server.isConnected &&
+ o.network == this.parent.parent &&
+ client.currentObject.TYPE != "IRCUser")
+ client.currentObject.display(message, msgtype, sourceObj, destObj,
+ tags);
+ else
+ this.parent.parent.displayHere(message, msgtype, sourceObj,
+ destObj, tags);
+ }
+}
+
+/* Displays a DCC user/file transfer-centric message on the most appropriate view.
+ *
+ * If the DCC user/file transfer view already exists (visible or hidden),
+ * messages are added to it; otherwise, messages go to the *current* view.
+ */
+CIRCDCCChat.prototype.display =
+CIRCDCCFileTransfer.prototype.display =
+function dcc_display(message, msgtype, sourceObj, destObj)
+{
+ if ("messages" in this)
+ this.displayHere(message, msgtype, sourceObj, destObj);
+ else
+ client.currentObject.display(message, msgtype, sourceObj, destObj);
+}
+
+function feedback(e, message, msgtype, sourceObj, destObj)
+{
+ if ("isInteractive" in e && e.isInteractive)
+ display(message, msgtype, sourceObj, destObj);
+}
+
+CIRCChannel.prototype.feedback =
+CIRCNetwork.prototype.feedback =
+CIRCUser.prototype.feedback =
+CIRCDCCChat.prototype.feedback =
+CIRCDCCFileTransfer.prototype.feedback =
+client.feedback =
+function this_feedback(e, message, msgtype, sourceObj, destObj)
+{
+ if ("isInteractive" in e && e.isInteractive)
+ this.displayHere(message, msgtype, sourceObj, destObj);
+}
+
+function display (message, msgtype, sourceObj, destObj, tags)
+{
+ client.currentObject.display (message, msgtype, sourceObj, destObj, tags);
+}
+
+client.getFontCSS =
+CIRCNetwork.prototype.getFontCSS =
+CIRCChannel.prototype.getFontCSS =
+CIRCUser.prototype.getFontCSS =
+CIRCDCCChat.prototype.getFontCSS =
+CIRCDCCFileTransfer.prototype.getFontCSS =
+function this_getFontCSS(format)
+{
+ /* Wow, this is cool. We just put together a CSS-rule string based on the
+ * font preferences. *This* is what CSS is all about. :)
+ * We also provide a "data: URL" format, to simplify other code.
+ */
+ var css;
+ var fs;
+ var fn;
+
+ if (this.prefs["font.family"] != "default")
+ fn = "font-family: " + this.prefs["font.family"] + ";";
+ else
+ fn = "font-family: inherit;";
+ if (this.prefs["font.size"] != 0)
+ fs = "font-size: " + this.prefs["font.size"] + "pt;";
+ else
+ fs = "font-size: medium;";
+
+ css = ".chatzilla-body { " + fs + fn + " }";
+
+ if (format == "data")
+ return "data:text/css," + encodeURIComponent(css);
+ return css;
+}
+
+client.startMsgGroup =
+CIRCNetwork.prototype.startMsgGroup =
+CIRCChannel.prototype.startMsgGroup =
+CIRCUser.prototype.startMsgGroup =
+CIRCDCCChat.prototype.startMsgGroup =
+CIRCDCCFileTransfer.prototype.startMsgGroup =
+function __startMsgGroup(id, groupMsg, msgtype)
+{
+ // The given ID may not be unique, so append a timestamp to ensure it is.
+ var groupId = id + "-" + Date.now();
+
+ // Add the button to the end of the message.
+ var headerMsg = groupMsg + " " + getMsg(MSG_COLLAPSE_BUTTON,
+ [MSG_COLLAPSE_HIDE,
+ MSG_COLLAPSE_HIDETITLE,
+ groupId]);
+
+ // Show the group header message.
+ client.munger.getRule(".inline-buttons").enabled = true;
+ this.displayHere(headerMsg, msgtype);
+ client.munger.getRule(".inline-buttons").enabled = false;
+
+ // Add the group to a list of active message groups.
+ if (!this.msgGroups)
+ this.msgGroups = [];
+ this.msgGroups.push(groupId);
+
+ // Return the actual ID in case the caller wants to use it later.
+ return groupId;
+}
+
+function startMsgGroup(groupId, headerMsg, msgtype)
+{
+ client.currentObject.startMsgGroup(groupId, headerMsg, msgtype);
+}
+
+client.endMsgGroup =
+CIRCNetwork.prototype.endMsgGroup =
+CIRCChannel.prototype.endMsgGroup =
+CIRCUser.prototype.endMsgGroup =
+CIRCDCCChat.prototype.endMsgGroup =
+CIRCDCCFileTransfer.prototype.endMsgGroup =
+function __endMsgGroup(groupId, message)
+{
+ if (!this.msgGroups)
+ return;
+
+ // Remove the group from the list of active message groups.
+ this.msgGroups.pop();
+ if (this.msgGroups.length == 0)
+ delete this.msgGroups;
+}
+
+function endMsgGroup()
+{
+ client.currentObject.endMsgGroup();
+}
+
+client.display =
+client.displayHere =
+CIRCNetwork.prototype.displayHere =
+CIRCChannel.prototype.displayHere =
+CIRCUser.prototype.displayHere =
+CIRCDCCChat.prototype.displayHere =
+CIRCDCCFileTransfer.prototype.displayHere =
+function __display(message, msgtype, sourceObj, destObj, tags)
+{
+ // We need a message type, assume "INFO".
+ if (!msgtype)
+ msgtype = MT_INFO;
+
+ var msgprefix = "";
+ if (msgtype.indexOf("/") != -1)
+ {
+ var ary = msgtype.match(/^(.*)\/(.*)$/);
+ msgtype = ary[1];
+ msgprefix = ary[2];
+ }
+
+ var blockLevel = false; /* true if this row should be rendered at block
+ * level, (like, if it has a really long nickname
+ * that might disturb the rest of the layout) */
+ var o = getObjectDetails(this); /* get the skinny on |this| */
+
+ // Get the 'me' object, so we can be sure to get the attributes right.
+ var me;
+ if ("me" in this)
+ me = this.me;
+ else if (o.server && "me" in o.server)
+ me = o.server.me;
+
+ /* Allow for matching (but not identical) user objects here. This tends to
+ * happen with bouncers and proxies, when they send channel messages
+ * pretending to be from the user; the sourceObj is a CIRCChanUser
+ * instead of a CIRCUser so doesn't == 'me'.
+ */
+ if (me)
+ {
+ if (sourceObj && (sourceObj.canonicalName == me.canonicalName))
+ sourceObj = me;
+ if (destObj && (destObj.canonicalName == me.canonicalName))
+ destObj = me;
+ }
+
+ // Let callers get away with "ME!" and we have to substitute here.
+ if (sourceObj == "ME!")
+ sourceObj = me;
+ if (destObj == "ME!")
+ destObj = me;
+
+ // Get the TYPE of the source object.
+ var fromType = (sourceObj && sourceObj.TYPE) ? sourceObj.TYPE : "unk";
+ // Is the source a user?
+ var fromUser = (fromType.search(/IRC.*User/) != -1);
+ // Get some sort of "name" for the source.
+ var fromAttr = "";
+ if (sourceObj)
+ {
+ if ("canonicalName" in sourceObj)
+ fromAttr = sourceObj.canonicalName;
+ else if ("name" in sourceObj)
+ fromAttr = sourceObj.name;
+ else
+ fromAttr = sourceObj.viewName;
+ }
+
+ // Get the dest TYPE too...
+ var toType = (destObj) ? destObj.TYPE : "unk";
+ // Is the dest a user?
+ var toUser = (toType.search(/IRC.*User/) != -1);
+ // Get a dest name too...
+ var toAttr = "";
+ if (destObj)
+ {
+ if ("canonicalName" in destObj)
+ toAttr = destObj.canonicalName;
+ else if ("name" in destObj)
+ toAttr = destObj.name;
+ else
+ toAttr = destObj.viewName;
+ }
+
+ // Is the message 'to' or 'from' somewhere other than this view
+ var toOther = ((sourceObj == me) && destObj && (destObj != this));
+ var fromOther = (toUser && (destObj == me) && (sourceObj != this) &&
+ // Need extra check for DCC users:
+ !((this.TYPE == "IRCDCCChat") && (this.user == sourceObj)));
+
+ // Attach "ME!" if appropriate, so motifs can style differently.
+ if ((sourceObj == me) && !toOther)
+ fromAttr = fromAttr + " ME!";
+ if (destObj && destObj == me)
+ toAttr = me.canonicalName + " ME!";
+
+ /* isImportant means to style the messages as important, and flash the
+ * window, getAttention means just flash the window. */
+ var isImportant = false, getAttention = false, isSuperfluous = false;
+ var viewType = this.TYPE;
+ var code;
+ var time;
+ if (tags && ("time" in tags))
+ time = new Date(tags.time);
+ else
+ time = new Date();
+
+ var timeStamp = strftime(this.prefs["timestamps.log"], time);
+
+ // Statusbar text, and the line that gets saved to the log.
+ var statusString;
+ var logStringPfx = timeStamp + " ";
+ var logStrings = new Array();
+
+ if (fromUser)
+ {
+ statusString = getMsg(MSG_FMT_STATUS,
+ [timeStamp,
+ sourceObj.unicodeName + "!" +
+ sourceObj.name + "@" + sourceObj.host]);
+ }
+ else
+ {
+ var name;
+ if (sourceObj)
+ name = sourceObj.viewName;
+ else
+ name = this.viewName;
+
+ statusString = getMsg(MSG_FMT_STATUS,
+ [timeStamp, name]);
+ }
+
+ // The table row, and it's attributes.
+ var msgRow = document.createElementNS(XHTML_NS, "html:tr");
+ msgRow.setAttribute("class", "msg");
+ if (this.msgGroups)
+ msgRow.setAttribute("msg-groups", this.msgGroups.join(', '));
+ msgRow.setAttribute("msg-type", msgtype);
+ msgRow.setAttribute("msg-prefix", msgprefix);
+ msgRow.setAttribute("msg-dest", toAttr);
+ msgRow.setAttribute("dest-type", toType);
+ msgRow.setAttribute("view-type", viewType);
+ msgRow.setAttribute("status-text", statusString);
+ msgRow.setAttribute("timestamp", Number(time));
+ if (fromAttr)
+ {
+ if (fromUser)
+ {
+ msgRow.setAttribute("msg-user", fromAttr);
+ // Set some mode information for channel users
+ if (fromType == 'IRCChanUser')
+ msgRow.setAttribute("msg-user-mode", sourceObj.modes.join(" "));
+ }
+ else
+ {
+ msgRow.setAttribute("msg-source", fromAttr);
+ }
+ }
+ if (toOther)
+ msgRow.setAttribute("to-other", toOther);
+ if (fromOther)
+ msgRow.setAttribute("from-other", fromOther);
+
+ // Timestamp cell.
+ var msgRowTimestamp = document.createElementNS(XHTML_NS, "html:td");
+ msgRowTimestamp.setAttribute("class", "msg-timestamp");
+
+ var canMergeData;
+ var msgRowSource, msgRowType, msgRowData;
+ if (fromUser && msgtype.match(/^(PRIVMSG|ACTION|NOTICE|WALLOPS)$/))
+ {
+ var nick = sourceObj.unicodeName;
+ var decorSt = "";
+ var decorEn = "";
+
+ // Set default decorations.
+ if (msgtype == "ACTION")
+ {
+ decorSt = "* ";
+ }
+ else
+ {
+ decorSt = "<";
+ decorEn = ">";
+ }
+
+ var nickURL;
+ if ((sourceObj != me) && ("getURL" in sourceObj))
+ nickURL = sourceObj.getURL();
+ if (toOther && ("getURL" in destObj))
+ nickURL = destObj.getURL();
+
+ if (sourceObj != me)
+ {
+ // Not from us...
+ if (destObj == me)
+ {
+ // ...but to us. Messages from someone else to us.
+
+ getAttention = true;
+ this.defaultCompletion = "/msg " + nick + " ";
+
+ // If this is a private message, and it's not in a query view,
+ // use *nick* instead of <nick>.
+ if ((msgtype != "ACTION") && (this.TYPE != "IRCUser"))
+ {
+ decorSt = "*";
+ decorEn = "*";
+ }
+ }
+ else
+ {
+ // ...or to us. Messages from someone else to channel or similar.
+
+ if ((typeof message == "string") && me)
+ isImportant = msgIsImportant(message, nick, o.network);
+ else if (message.hasAttribute("isImportant") && me)
+ isImportant = true;
+
+ if (isImportant)
+ {
+ this.defaultCompletion = nick +
+ client.prefs["nickCompleteStr"] + " ";
+ }
+ }
+ }
+ else
+ {
+ // Messages from us, to somewhere other than this view
+ if (toOther)
+ {
+ nick = destObj.unicodeName;
+ decorSt = ">";
+ decorEn = "<";
+ }
+ }
+
+ // Log the nickname in the same format as we'll let the user copy.
+ // If the message has a prefix, show it after a "/".
+ if (msgprefix)
+ logStringPfx += decorSt + nick + "/" + msgprefix + decorEn + " ";
+ else
+ logStringPfx += decorSt + nick + decorEn + " ";
+
+ if (!("lastNickDisplayed" in this) || this.lastNickDisplayed != nick)
+ {
+ this.lastNickDisplayed = nick;
+ this.mark = (("mark" in this) && this.mark == "even") ? "odd" : "even";
+ }
+
+ msgRowSource = document.createElementNS(XHTML_NS, "html:td");
+ msgRowSource.setAttribute("class", "msg-user");
+
+ // Make excessive nicks get shunted.
+ if (nick && (nick.length > client.MAX_NICK_DISPLAY))
+ blockLevel = true;
+
+ if (decorSt)
+ msgRowSource.appendChild(newInlineText(decorSt, "chatzilla-decor"));
+ if (nickURL)
+ {
+ var nick_anchor = document.createElementNS(XHTML_NS, "html:a");
+ nick_anchor.setAttribute("class", "chatzilla-link");
+ nick_anchor.setAttribute("href", nickURL);
+ nick_anchor.appendChild(newInlineText(nick));
+ msgRowSource.appendChild(nick_anchor);
+ }
+ else
+ {
+ msgRowSource.appendChild(newInlineText(nick));
+ }
+ if (msgprefix)
+ {
+ /* We don't style the "/" with chatzilla-decor because the one
+ * thing we don't want is it disappearing!
+ */
+ msgRowSource.appendChild(newInlineText("/", ""));
+ msgRowSource.appendChild(newInlineText(msgprefix,
+ "chatzilla-prefix"));
+ }
+ if (decorEn)
+ msgRowSource.appendChild(newInlineText(decorEn, "chatzilla-decor"));
+ canMergeData = this.prefs["collapseMsgs"];
+ }
+ else if (msgprefix)
+ {
+ decorSt = "<";
+ decorEn = ">";
+
+ logStringPfx += decorSt + "/" + msgprefix + decorEn + " ";
+
+ msgRowSource = document.createElementNS(XHTML_NS, "html:td");
+ msgRowSource.setAttribute("class", "msg-user");
+
+ msgRowSource.appendChild(newInlineText(decorSt, "chatzilla-decor"));
+ msgRowSource.appendChild(newInlineText("/", ""));
+ msgRowSource.appendChild(newInlineText(msgprefix, "chatzilla-prefix"));
+ msgRowSource.appendChild(newInlineText(decorEn, "chatzilla-decor"));
+ canMergeData = this.prefs["collapseMsgs"];
+ }
+ else
+ {
+ isSuperfluous = true;
+ if (!client.debugHook.enabled && msgtype in client.responseCodeMap)
+ {
+ code = client.responseCodeMap[msgtype];
+ }
+ else
+ {
+ if (!client.debugHook.enabled && client.HIDE_CODES)
+ code = client.DEFAULT_RESPONSE_CODE;
+ else
+ code = "[" + msgtype + "]";
+ }
+
+ /* Display the message code */
+ msgRowType = document.createElementNS(XHTML_NS, "html:td");
+ msgRowType.setAttribute("class", "msg-type");
+
+ msgRowType.appendChild(newInlineText(code));
+ logStringPfx += code + " ";
+ }
+
+ if (message)
+ {
+ msgRowData = document.createElementNS(XHTML_NS, "html:td");
+ msgRowData.setAttribute("class", "msg-data");
+
+ var tmpMsgs = message;
+ if (typeof message == "string")
+ {
+ msgRowData.appendChild(stringToMsg(message, this));
+ }
+ else
+ {
+ msgRowData.appendChild(message);
+ tmpMsgs = tmpMsgs.innerHTML.replace(/<[^<]*>/g, "");
+ }
+ tmpMsgs = tmpMsgs.split(/\r?\n/);
+ for (var l = 0; l < tmpMsgs.length; l++)
+ logStrings[l] = logStringPfx + tmpMsgs[l];
+ }
+
+ if ("mark" in this)
+ msgRow.setAttribute("mark", this.mark);
+
+ if (isImportant)
+ {
+ if ("importantMessages" in this)
+ {
+ var importantId = "important" + (this.importantMessages++);
+ msgRow.setAttribute("id", importantId);
+ }
+ msgRow.setAttribute("important", "true");
+ msgRow.setAttribute("aria-live", "assertive");
+ }
+
+ // Timestamps first...
+ msgRow.appendChild(msgRowTimestamp);
+ // Now do the rest of the row, after block-level stuff.
+ if (msgRowSource)
+ msgRow.appendChild(msgRowSource);
+ else
+ msgRow.appendChild(msgRowType);
+ if (msgRowData)
+ msgRow.appendChild(msgRowData);
+ updateTimestampFor(this, msgRow);
+
+ if (blockLevel)
+ {
+ /* putting a div here crashes mozilla, so fake it with nested tables
+ * for now */
+ var tr = document.createElementNS(XHTML_NS, "html:tr");
+ tr.setAttribute ("class", "msg-nested-tr");
+ var td = document.createElementNS(XHTML_NS, "html:td");
+ td.setAttribute ("class", "msg-nested-td");
+ td.setAttribute ("colspan", "3");
+
+ tr.appendChild(td);
+ var table = document.createElementNS(XHTML_NS, "html:table");
+ table.setAttribute ("class", "msg-nested-table");
+ table.setAttribute("role", "presentation");
+
+ td.appendChild (table);
+ var tbody = document.createElementNS(XHTML_NS, "html:tbody");
+
+ tbody.appendChild(msgRow);
+ table.appendChild(tbody);
+ msgRow = tr;
+ }
+
+ // Actually add the item.
+ addHistory (this, msgRow, canMergeData);
+
+ // Update attention states...
+ if (isImportant || getAttention)
+ {
+ setTabState(this, "attention");
+ if (client.prefs["notify.aggressive"])
+ window.getAttention();
+ }
+ else
+ {
+ if (isSuperfluous)
+ {
+ setTabState(this, "superfluous");
+ }
+ else
+ {
+ setTabState(this, "activity");
+ }
+ }
+
+ // Copy Important Messages [to network view].
+ if (isImportant && client.prefs["copyMessages"] && (o.network != this))
+ {
+ if (importantId)
+ {
+ // Create the linked inline button
+ var msgspan = document.createElementNS(XHTML_NS, "html:span");
+ msgspan.setAttribute("isImportant", "true");
+
+ var cmd = "jump-to-anchor " + importantId + " " + this.unicodeName;
+ var prefix = getMsg(MSG_JUMPTO_BUTTON, [this.unicodeName, cmd]);
+
+ // Munge prefix as a button
+ client.munger.getRule(".inline-buttons").enabled = true;
+ client.munger.munge(prefix + " ", msgspan, o);
+
+ // Munge rest of message normally
+ client.munger.getRule(".inline-buttons").enabled = false;
+ client.munger.munge(message, msgspan, o);
+
+ o.network.displayHere(msgspan, msgtype, sourceObj, destObj);
+ }
+ else
+ {
+ o.network.displayHere(message, msgtype, sourceObj, destObj);
+ }
+ }
+
+ // Log file time!
+ if (this.prefs["log"])
+ {
+ if (!this.logFile)
+ client.openLogFile(this);
+
+ try
+ {
+ var LE = client.lineEnd;
+ for (var l = 0; l < logStrings.length; l++)
+ this.logFile.write(fromUnicode(logStrings[l] + LE, "utf-8"));
+ }
+ catch (ex)
+ {
+ // Stop logging before showing any messages!
+ this.prefs["log"] = false;
+ dd("Log file write error: " + formatException(ex));
+ this.displayHere(getMsg(MSG_LOGFILE_WRITE_ERROR, getLogPath(this)),
+ "ERROR");
+ }
+ }
+
+ /* We want to show alerts if they're from a non-current view (optional),
+ * or we don't have focus at all.
+ */
+ if (client.prefs["alert.globalEnabled"]
+ && this.prefs["alert.enabled"] && client.alert &&
+ (!window.isFocused
+ || (!client.prefs['alert.nonFocusedOnly'] &&
+ !("currentObject" in client && client.currentObject == this)
+ )
+ )
+ )
+ {
+ if (isImportant)
+ {
+ showEventAlerts(this.TYPE, "stalk", message, nick, o, this, msgtype);
+ }
+ else if (isSuperfluous)
+ {
+ showEventAlerts(this.TYPE, "event", message, nick, o, this, msgtype);
+ }
+ else
+ {
+ showEventAlerts(this.TYPE, "chat" , message, nick, o, this, msgtype);
+ }
+ }
+
+}
+
+function addHistory (source, obj, mergeData)
+{
+ if (!("messages" in source) || (source.messages == null))
+ createMessages(source);
+
+ var tbody = source.messages.firstChild;
+ var appendTo = tbody;
+
+ var needScroll = false;
+
+ if (mergeData)
+ {
+ var inobj = obj;
+ // This gives us the non-nested row when there is nesting.
+ if (inobj.className == "msg-nested-tr")
+ inobj = inobj.firstChild.firstChild.firstChild.firstChild;
+
+ var thisUserCol = inobj.firstChild;
+ while (thisUserCol && !thisUserCol.className.match(/^(msg-user|msg-type)$/))
+ thisUserCol = thisUserCol.nextSibling;
+
+ var thisMessageCol = inobj.firstChild;
+ while (thisMessageCol && !(thisMessageCol.className == "msg-data"))
+ thisMessageCol = thisMessageCol.nextSibling;
+
+ let columnInfo = findPreviousColumnInfo(source.messages);
+ let nickColumns = columnInfo.nickColumns;
+ let rowExtents = columnInfo.extents;
+ let nickColumnCount = nickColumns.length;
+
+ let lastRowSpan = 0;
+ let sameNick = false;
+ let samePrefix = false;
+ let sameDest = false;
+ let haveSameType = false;
+ let isAction = false;
+ let collapseActions;
+ let needSameType = false;
+ // 1 or messages, check for doubles.
+ if (nickColumnCount > 0)
+ {
+ var lastRow = nickColumns[nickColumnCount - 1].parentNode;
+ // What was the span last time?
+ lastRowSpan = Number(nickColumns[0].getAttribute("rowspan"));
+ // Are we the same user as last time?
+ sameNick = (lastRow.getAttribute("msg-user") ==
+ inobj.getAttribute("msg-user"));
+ // Do we have the same prefix as last time?
+ samePrefix = (lastRow.getAttribute("msg-prefix") ==
+ inobj.getAttribute("msg-prefix"));
+ // Do we have the same destination as last time?
+ sameDest = (lastRow.getAttribute("msg-dest") ==
+ inobj.getAttribute("msg-dest"));
+ // Is this message the same type as the last one?
+ haveSameType = (lastRow.getAttribute("msg-type") ==
+ inobj.getAttribute("msg-type"));
+ // Is either of the messages an action? We may not want to collapse
+ // depending on the collapseActions pref
+ isAction = ((inobj.getAttribute("msg-type") == "ACTION") ||
+ (lastRow.getAttribute("msg-type") == "ACTION"));
+ // Do we collapse actions?
+ collapseActions = source.prefs["collapseActions"];
+
+ // Does the motif collapse everything, regardless of type?
+ // NOTE: the collapseActions pref can override this for actions
+ needSameType = !(("motifSettings" in source) &&
+ source.motifSettings &&
+ ("collapsemore" in source.motifSettings));
+ }
+
+ if (sameNick && samePrefix && sameDest &&
+ (haveSameType || !needSameType) &&
+ (!isAction || collapseActions))
+ {
+ obj = inobj;
+ if (columnInfo.nested)
+ appendTo = source.messages.firstChild.lastChild.firstChild.firstChild.firstChild;
+
+ if (obj.getAttribute("important"))
+ {
+ nickColumns[nickColumnCount - 1].setAttribute("important",
+ true);
+ }
+
+ // Remove nickname column from new row.
+ obj.removeChild(thisUserCol);
+
+ // Expand previous grouping's nickname cell(s) to fill-in the gap.
+ for (var i = 0; i < nickColumns.length; ++i)
+ nickColumns[i].setAttribute("rowspan", rowExtents.length + 1);
+ }
+ }
+
+ if ("frame" in source)
+ needScroll = checkScroll(source.frame);
+ if (obj)
+ appendTo.appendChild(client.adoptNode(obj, appendTo.ownerDocument));
+
+ if (source.MAX_MESSAGES)
+ {
+ if (typeof source.messageCount != "number")
+ source.messageCount = 1;
+ else
+ source.messageCount++;
+
+ if (source.messageCount > source.MAX_MESSAGES)
+ removeExcessMessages(source);
+ }
+
+ if (needScroll)
+ scrollDown(source.frame, true);
+}
+
+function removeExcessMessages(source)
+{
+ var window = getContentWindow(source.frame);
+ var rows = source.messages.rows;
+ var lastItemOffset = rows[rows.length - 1].offsetTop;
+ var tbody = source.messages.firstChild;
+ while (source.messageCount > source.MAX_MESSAGES)
+ {
+ if (tbody.firstChild.className == "msg-nested-tr")
+ {
+ var table = tbody.firstChild.firstChild.firstChild;
+ var toBeRemoved = source.messageCount - source.MAX_MESSAGES;
+ // If we can remove the entire table, do that...
+ if (table.rows.length <= toBeRemoved)
+ {
+ tbody.removeChild(tbody.firstChild);
+ source.messageCount -= table.rows.length;
+ table = null; // Don't hang onto this.
+ continue;
+ }
+ // Otherwise, remove rows from this table instead:
+ tbody = table.firstChild;
+ }
+ var nextLastNode = tbody.firstChild.nextSibling;
+ // If the next node has only 2 childNodes,
+ // assume we're dealing with collapsed msgs,
+ // and move the nickname element:
+ if (nextLastNode.childNodes.length == 2)
+ {
+ var nickElem = tbody.firstChild.childNodes[1];
+ var rowspan = nickElem.getAttribute("rowspan") - 1;
+ tbody.firstChild.removeChild(nickElem);
+ nickElem.setAttribute("rowspan", rowspan);
+ nextLastNode.insertBefore(nickElem, nextLastNode.lastChild);
+ }
+ tbody.removeChild(tbody.firstChild);
+ --source.messageCount;
+ }
+ var oldestItem = rows[0];
+ if (oldestItem.className == "msg-nested-tr")
+ oldestItem = rows[0].firstChild.firstChild.firstChild.firstChild;
+ updateTimestampFor(source, oldestItem, true);
+
+ // Scroll by as much as the lowest item has moved up:
+ lastItemOffset -= rows[rows.length - 1].offsetTop;
+ var y = window.pageYOffset;
+ if (!checkScroll(source.frame) && (y > lastItemOffset))
+ window.scrollBy(0, -lastItemOffset);
+}
+
+function findPreviousColumnInfo(table)
+{
+ // All the rows in the grouping (for merged rows).
+ var extents = new Array();
+ // Get the last row in the table.
+ var tr = table.firstChild.lastChild;
+ // Bail if there's no rows.
+ if (!tr)
+ return {extents: [], nickColumns: [], nested: false};
+ // Get message type.
+ if (tr.className == "msg-nested-tr")
+ {
+ var rv = findPreviousColumnInfo(tr.firstChild.firstChild);
+ rv.nested = true;
+ return rv;
+ }
+ // Now get the read one...
+ var className = (tr && tr.childNodes[1]) ? tr.childNodes[1].getAttribute("class") : "";
+ // Keep going up rows until you find the first in a group.
+ // This will go up until it hits the top of a multiline/merged block.
+ while (tr && tr.childNodes[1] && className.search(/msg-user|msg-type/) == -1)
+ {
+ extents.push(tr);
+ tr = tr.previousSibling;
+ if (tr && tr.childNodes[1])
+ className = tr.childNodes[1].getAttribute("class");
+ }
+
+ // If we ran out of rows, or it's not a talking line, we're outta here.
+ if (!tr || className != "msg-user")
+ return {extents: [], nickColumns: [], nested: false};
+
+ extents.push(tr);
+
+ // Time to collect the nick data...
+ var nickCol = tr.firstChild;
+ // All the cells that contain nickname info.
+ var nickCols = new Array();
+ while (nickCol)
+ {
+ // Just collect nickname column cells.
+ if (nickCol.getAttribute("class") == "msg-user")
+ nickCols.push(nickCol);
+ nickCol = nickCol.nextSibling;
+ }
+
+ // And we're done.
+ return {extents: extents, nickColumns: nickCols, nested: false};
+}
+
+function getLogPath(obj)
+{
+ // If we're logging, return the currently-used URL.
+ if (obj.logFile)
+ return getURLSpecFromFile(obj.logFile.path);
+ // If not, return the ideal URL.
+ return getURLSpecFromFile(obj.prefs["logFileName"]);
+}
+
+client.getConnectionCount =
+function cli_gccount ()
+{
+ var count = 0;
+
+ for (var n in client.networks)
+ {
+ if (client.networks[n].isConnected())
+ ++count;
+ }
+
+ return count;
+}
+
+client.quit =
+function cli_quit (reason)
+{
+ var net, netReason;
+ for (var n in client.networks)
+ {
+ net = client.networks[n];
+ if (net.isConnected())
+ {
+ netReason = (reason ? reason : net.prefs["defaultQuitMsg"]);
+ netReason = (netReason ? netReason : client.userAgent);
+ net.quit(netReason);
+ }
+ }
+}
+
+client.wantToQuit =
+function cli_wantToQuit(reason, deliberate)
+{
+
+ var close = true;
+ if (client.prefs["warnOnClose"] && !deliberate)
+ {
+ const buttons = [MSG_QUIT_ANYWAY, MSG_DONT_QUIT];
+ var checkState = { value: true };
+ var rv = confirmEx(MSG_CONFIRM_QUIT, buttons, 0, MSG_WARN_ON_EXIT,
+ checkState);
+ close = (rv == 0);
+ client.prefs["warnOnClose"] = checkState.value;
+ }
+
+ if (close)
+ {
+ client.userClose = true;
+ display(MSG_CLOSING);
+ client.quit(reason);
+ }
+}
+
+client.promptToSaveLogin =
+function cli_promptToSaveLogin(url, type, username, password)
+{
+ var name = "";
+ switch (type)
+ {
+ case "nick":
+ case "oper":
+ case "sasl":
+ name = username;
+ break;
+ case "serv":
+ case "chan":
+ name = url;
+ username = "*";
+ break;
+ default:
+ display(getMsg(MSG_LOGIN_ERR_UNKNOWN_TYPE, type), MT_ERROR);
+ return;
+ }
+
+ const buttons = [MSG_LOGIN_SAVE, MSG_LOGIN_DONT];
+ var checkState = { value: true };
+ var rv = confirmEx(getMsg(MSG_LOGIN_CONFIRM, name), buttons, 0,
+ MSG_LOGIN_PROMPT, checkState);
+ if (rv == 0)
+ {
+ client.prefs["login.promptToSave"] = checkState.value;
+
+ var updated = addOrUpdateLogin(url, type, username, password);
+ if (updated) {
+ display(getMsg(MSG_LOGIN_UPDATED, name), MT_INFO);
+ } else {
+ display(getMsg(MSG_LOGIN_ADDED, name), MT_INFO);
+ }
+ }
+}
+
+client.tryToGetLogin =
+function cli_tryToGetLogin(url, type, username, existing, needpass,
+ promptstring)
+{
+ // Password is optional. If it is not given, we look for a saved password
+ // first. If there isn't one, we potentially use a safe prompt.
+ var info = getLogin(url, type, username);
+ var stored = (info && info.password) ? info.password : "";
+ var promptToSave = false;
+ if (!existing && stored) {
+ existing = stored;
+ } else if (!existing && needpass) {
+ existing = promptPassword(promptstring, "");
+ if (existing)
+ promptToSave = true;
+ } else if (existing && stored != existing) {
+ promptToSave = true;
+ }
+
+ if (promptToSave && client.prefs["login.promptToSave"])
+ client.promptToSaveLogin(url, type, username, existing);
+
+ return existing;
+}
+
+/* gets a tab-complete match for the line of text specified by |line|.
+ * wordStart is the position within |line| that starts the word being matched,
+ * wordEnd marks the end position. |cursorPos| marks the position of the caret
+ * in the textbox.
+ */
+client.performTabMatch =
+function gettabmatch_usr (line, wordStart, wordEnd, word, cursorPos)
+{
+ if (wordStart != 0 || line[0] != client.COMMAND_CHAR)
+ return null;
+
+ var matches = client.commandManager.listNames(word.substr(1), CMD_CONSOLE);
+ if (matches.length == 1 && wordEnd == line.length)
+ {
+ matches[0] = client.COMMAND_CHAR + matches[0] + " ";
+ }
+ else
+ {
+ for (var i in matches)
+ matches[i] = client.COMMAND_CHAR + matches[i];
+ }
+
+ return matches;
+}
+
+client.openLogFile =
+function cli_startlog(view, showMessage)
+{
+ function getNextLogFileDate()
+ {
+ var d = new Date();
+ d.setMilliseconds(0);
+ d.setSeconds(0);
+ d.setMinutes(0);
+ switch (view.smallestLogInterval)
+ {
+ case "h":
+ return d.setHours(d.getHours() + 1);
+ case "d":
+ d.setHours(0);
+ return d.setDate(d.getDate() + 1);
+ case "m":
+ d.setHours(0);
+ d.setDate(1);
+ return d.setMonth(d.getMonth() + 1);
+ case "y":
+ d.setHours(0);
+ d.setDate(1);
+ d.setMonth(0);
+ return d.setFullYear(d.getFullYear() + 1);
+ }
+ //XXXhack: This should work...
+ return Infinity;
+ };
+
+ const NORMAL_FILE_TYPE = Components.interfaces.nsIFile.NORMAL_FILE_TYPE;
+
+ try
+ {
+ var file = new LocalFile(view.prefs["logFileName"]);
+ if (!file.localFile.exists())
+ {
+ // futils.umask may be 0022. Result is 0644.
+ file.localFile.create(NORMAL_FILE_TYPE, 0o666 & ~futils.umask);
+ }
+ view.logFile = fopen(file.localFile, ">>");
+ // If we're here, it's safe to say when we should re-open:
+ view.nextLogFileDate = getNextLogFileDate();
+ }
+ catch (ex)
+ {
+ view.prefs["log"] = false;
+ dd("Log file open error: " + formatException(ex));
+ view.displayHere(getMsg(MSG_LOGFILE_ERROR, getLogPath(view)), MT_ERROR);
+ return;
+ }
+
+ if (showMessage)
+ view.displayHere(getMsg(MSG_LOGFILE_OPENED, getLogPath(view)));
+}
+
+client.closeLogFile =
+function cli_stoplog(view, showMessage)
+{
+ if (showMessage)
+ view.displayHere(getMsg(MSG_LOGFILE_CLOSING, getLogPath(view)));
+
+ if (view.logFile)
+ {
+ view.logFile.close();
+ view.logFile = null;
+ }
+}
+
+function checkLogFiles()
+{
+ // For every view that has a logfile, check if we need a different file
+ // based on the current date and the logfile preference. We close the
+ // current logfile, and display will open the new one based on the pref
+ // when it's needed.
+
+ var d = new Date();
+ for (var n in client.networks)
+ {
+ var net = client.networks[n];
+ if (net.logFile && (d > net.nextLogFileDate))
+ client.closeLogFile(net);
+ if (("primServ" in net) && net.primServ && ("channels" in net.primServ))
+ {
+ for (var c in net.primServ.channels)
+ {
+ var chan = net.primServ.channels[c];
+ if (chan.logFile && (d > chan.nextLogFileDate))
+ client.closeLogFile(chan);
+ }
+ }
+ if ("users" in net)
+ {
+ for (var u in net.users)
+ {
+ var user = net.users[u];
+ if (user.logFile && (d > user.nextLogFileDate))
+ client.closeLogFile(user);
+ }
+ }
+ }
+
+ for (var dc in client.dcc.chats)
+ {
+ var dccChat = client.dcc.chats[dc];
+ if (dccChat.logFile && (d > dccChat.nextLogFileDate))
+ client.closeLogFile(dccChat);
+ }
+ for (var df in client.dcc.files)
+ {
+ var dccFile = client.dcc.files[df];
+ if (dccFile.logFile && (d > dccFile.nextLogFileDate))
+ client.closeLogFile(dccFile);
+ }
+
+ // Don't forget about the client tab:
+ if (client.logFile && (d > client.nextLogFileDate))
+ client.closeLogFile(client);
+
+ /* We need to calculate the correct time for the next check. This is
+ * attempting to hit 2 seconds past the hour. We need the timezone offset
+ * here for when it is not a whole number of hours from UTC.
+ */
+ var shiftedDate = d.getTime() + d.getTimezoneOffset() * 60000;
+ setTimeout(checkLogFiles, 3602000 - (shiftedDate % 3600000));
+}
+
+CIRCChannel.prototype.getLCFunction =
+CIRCNetwork.prototype.getLCFunction =
+CIRCUser.prototype.getLCFunction =
+CIRCDCCChat.prototype.getLCFunction =
+CIRCDCCFileTransfer.prototype.getLCFunction =
+function getlcfn()
+{
+ var details = getObjectDetails(this);
+ var lcFn;
+
+ if (details.server)
+ {
+ lcFn = function(text)
+ {
+ return details.server.toLowerCase(text);
+ }
+ }
+
+ return lcFn;
+}
+
+CIRCChannel.prototype.performTabMatch =
+CIRCNetwork.prototype.performTabMatch =
+CIRCUser.prototype.performTabMatch =
+CIRCDCCChat.prototype.performTabMatch =
+CIRCDCCFileTransfer.prototype.performTabMatch =
+function gettabmatch_other (line, wordStart, wordEnd, word, cursorpos, lcFn)
+{
+ if (wordStart == 0 && line[0] == client.COMMAND_CHAR)
+ {
+ return client.performTabMatch(line, wordStart, wordEnd, word,
+ cursorpos);
+ }
+
+ var matchList = new Array();
+ var users;
+ var channels;
+ var userIndex = -1;
+
+ var details = getObjectDetails(this);
+
+ if (details.channel && word == details.channel.unicodeName[0])
+ {
+ /* When we have #<tab>, we just want the current channel,
+ if possible. */
+ matchList.push(details.channel.unicodeName);
+ }
+ else
+ {
+ /* Ok, not #<tab> or no current channel, so get the full list. */
+
+ if (details.channel)
+ users = details.channel.users;
+
+ if (details.server)
+ {
+ channels = details.server.channels;
+ for (var c in channels)
+ matchList.push(channels[c].unicodeName);
+ if (!users)
+ users = details.server.users;
+ }
+
+ if (users)
+ {
+ userIndex = matchList.length;
+ for (var n in users)
+ matchList.push(users[n].unicodeName);
+ }
+ }
+
+ var matches = matchEntry(word, matchList, lcFn);
+
+ var list = new Array();
+ for (var i = 0; i < matches.length; i++)
+ list.push(matchList[matches[i]]);
+
+ if (list.length == 1)
+ {
+ if (users && (userIndex >= 0) && (matches[0] >= userIndex))
+ {
+ if (wordStart == 0)
+ list[0] += client.prefs["nickCompleteStr"];
+ }
+
+ if (wordEnd == line.length)
+ {
+ /* add a space if the word is at the end of the line. */
+ list[0] += " ";
+ }
+ }
+
+ return list;
+}
+
+/*
+ * 290miliseconds for 1st derive is allowing about 3-4 events per
+ * second. 200ms for 2nd derivative allows max 200ms difference of
+ * frequency. This means when the flood is massive, this value is
+ * very closed to zero. But runtime issues should cause some delay
+ * in the core js, so zero value is not too good. We need increase
+ * this with a small, to make more strict. And when flood is done,
+ * we need detect it - based on arithmetic medium. When doesn't happen
+ * anything for a long time, perhaps for 2seconds the
+ * value - based on last 10 events - the 2nd value goes
+ * over 200ms average, so event should start again.
+ */
+
+function FloodProtector (density, dispersion)
+{
+ this.lastHit = Number(new Date());
+
+ if (density)
+ this.floodDensity = density;
+
+ if (dispersion)
+ this.floodDispersion = dispersion;
+}
+
+FloodProtector.prototype.requestedTotal = 0;
+FloodProtector.prototype.acceptedTotal = 0;
+FloodProtector.prototype.firedTotal = 0;
+FloodProtector.prototype.lastHit = 0;
+FloodProtector.prototype.derivative1 = 100;
+FloodProtector.prototype.derivative1Count = 100;
+FloodProtector.prototype.derivative2 = 0;
+FloodProtector.prototype.derivative2Count = 0;
+FloodProtector.prototype.floodDensity = 290;
+FloodProtector.prototype.floodDispersion = 200;
+
+FloodProtector.prototype.request = function ()
+{
+ this.requestedTotal++;
+ var current = Number(new Date());
+ var oldDerivative1 = this.derivative1;
+ this.derivative1 = current - this.lastHit;
+ this.derivative1Count = ((this.derivative1Count * 9) + this.derivative1) / 10;
+ this.derivative2 = Math.abs(this.derivative1 - oldDerivative1);
+ this.derivative2Count = ((this.derivative2Count * 9) + this.derivative2) / 10;
+ this.lastHit = current;
+}
+
+FloodProtector.prototype.accept = function ()
+{
+ this.acceptedTotal++;
+}
+
+FloodProtector.prototype.fire = function ()
+{
+ // There is no activity for 10 seconds - flood is possibly done.
+ // No need more recall. In other way the first normal activity
+ // overwrites it automatically earlier, if nessesary.
+ if ((Number(new Date()) - this.lastHit) > 10000)
+ return false;
+
+ // The activity is not too frequent or not massive so should not be fire.
+ if ((this.derivative1Count > this.floodDensity)
+ || (this.derivative2Count > this.floodDispersion))
+ {
+ return false;
+ }
+
+ this.firedTotal++;
+ return true;
+
+}
+
+
+function toasterPopupOverlapDelayReset (eventType)
+{
+ // it smells like a flood attack so rather wait more...
+ if (client.alert.floodProtector.fire())
+ {
+ setTimeout(
+ toasterPopupOverlapDelayReset,
+ client.prefs['alert.overlapDelay'], eventType);
+ }
+ else
+ {
+ delete client.alert.alertList[eventType];
+ }
+}
+
+var alertClickerObserver = {
+ observe: function(subject, topic, data)
+ {
+ if (topic == "alertclickcallback")
+ {
+ var tb = document.getElementById(data);
+ if (tb && tb.view)
+ {
+ tb.view.dispatch("set-current-view", {view: tb.view});
+ window.focus();
+ }
+ }
+ },
+
+ // Gecko 1.7.* rulez
+ onAlertClickCallback: function(data)
+ {
+ var tb = document.getElementById(data);
+ if (tb && tb.view)
+ {
+ tb.view.dispatch("set-current-view", {view: tb.view});
+ window.focus();
+ }
+ },
+
+ onAlertFinished: function(data)
+ {
+ }
+};
+
+
+// Show the alert for a particular event on a type of object.
+function showEventAlerts (type, event, message, nick, o, thisp, msgtype)
+{
+
+ // Converts .TYPE values into the event object names.
+ // IRCChannel => channel, IRCUser => user, etc.
+ type = type.replace(/^IRC/i,'').toLowerCase();
+
+ var source = type;
+ // DCC Chat sessions should act just like user views.
+ if (type == "dccchat") type = "user";
+
+ var ev = type + "." + event;
+ if (!(("alert."+ev) in thisp.prefs))
+ return;
+ if (!thisp.prefs["alert."+ev])
+ return;
+
+ client.alert.floodProtector.request();
+ if (ev in client.alert.alertList)
+ return;
+
+ client.alert.floodProtector.accept();
+ if(client.prefs['alert.overlapDelay'] > 0)
+ {
+ client.alert.alertList[ev] = true;
+ setTimeout(toasterPopupOverlapDelayReset,
+ client.prefs['alert.overlapDelay'], ev);
+ }
+
+ var clickable = client.prefs['alert.clickable'];
+ var tabId = clickable ? getTabForObject(thisp,false).id : "";
+ var listener = clickable ? alertClickerObserver : null;
+
+ message = removeColorCodes(message);
+ if (nick)
+ {
+ if (msgtype == "ACTION")
+ {
+ message = "* " + nick + " " + message;
+ }
+ else
+ {
+ message = "<" + nick + "> " + message;
+ }
+ }
+
+ if ((source == "channel") && o.channel)
+ {
+ source = o.channel.viewName;
+ }
+ else if ((source == "user") && o.network)
+ {
+ source = o.network.viewName;
+ }
+
+ // We can't be sure if it is a macOS and Growl is now turned off or not
+ try
+ {
+ client.alert.service.showAlertNotification(
+ "chrome://chatzilla/skin/images/logo.png",
+ "ChatZilla - " + source + " - " + event,
+ message, clickable, tabId, listener);
+ }
+ catch(ex)
+ {
+ // yup. it is probably a MAC or NsIAlertsService is not initialized
+ }
+}
diff --git a/comm/suite/chatzilla/xul/lib/munger.js b/comm/suite/chatzilla/xul/lib/munger.js
new file mode 100644
index 0000000000..cdbda15c54
--- /dev/null
+++ b/comm/suite/chatzilla/xul/lib/munger.js
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Constructs a new munger entry, using a regexp or lambda match function, and
+ * a class name (to be applied by the munger itself) or lambda replace
+ * function, and the default enabled state and a start priority (used if two
+ * rules match at the same index), as well as a default tag (when the munger
+ * adds it based on the class name) name.
+ *
+ * Regular Expressions for matching should ensure that the first capturing
+ * group is the one that contains the matched text. Non-capturing groups, of
+ * zero-width or otherwise can be used before and after, to ensure the right
+ * things are matched (e.g. to ensure whitespace before something).
+ *
+ * Note that for RegExp matching, the munger will search for the matched text
+ * (from the first capturing group) from the leftmost point of the entire
+ * match. This means that if the text that matched the first group occurs in
+ * any part of the match before the group, the munger will apply to the wrong
+ * bit. This is not usually a problem, but if it is you should use a
+ * lambdaMatch function and be sure to return the new style return value,
+ * which specifically indicates the start.
+ *
+ * The lambda match and lambda replace functions have this signature:
+ * lambdaMatch(text, containerTag, data, mungerEntry)
+ * lambdaReplace(text, containerTag, data, mungerEntry)
+ * - text is the entire text to find a match in/that has matched
+ * - containerTag is the element containing the text (not useful?)
+ * - data is a generic object containing properties kept throughout
+ * - mungerEntry is the CMungerEntry object for the munger itself
+ *
+ * The lambdaReplace function is expected to do everything needed to put
+ * |text| into |containerTab| ready for display.
+ *
+ * The return value for lambda match functions should be either:
+ * - (old style) just the text that matched
+ * (the munger will search for this text, and uses the first match)
+ * - (new style) an object with properties:
+ * - start (start index, 0 = first character)
+ * - text (matched text)
+ * (note that |text| must start at index |start|)
+ *
+ * The return value for lambda replace functions are not used.
+ *
+ */
+
+function CMungerEntry(name, regex, className, priority, startPriority,
+ enable, tagName)
+{
+ this.name = name;
+ if (name[0] != ".")
+ this.description = getMsg("munger." + name, null, null);
+ this.enabled = (typeof enable == "undefined" ? true : enable);
+ this.enabledDefault = this.enabled;
+ this.startPriority = (startPriority) ? startPriority : 0;
+ this.priority = priority;
+ this.tagName = (tagName) ? tagName : "html:span";
+
+ if (isinstance(regex, RegExp))
+ this.regex = regex;
+ else
+ this.lambdaMatch = regex;
+
+ if (typeof className == "function")
+ this.lambdaReplace = className;
+ else
+ this.className = className;
+}
+
+function CMunger(textMunger)
+{
+ this.entries = new Array();
+ this.tagName = "html:span";
+ this.enabled = true;
+ if (textMunger)
+ this.insertPlainText = textMunger;
+}
+
+CMunger.prototype.enabled = true;
+CMunger.prototype.insertPlainText = insertText;
+
+CMunger.prototype.getRule =
+function mng_getrule(name)
+{
+ for (var p in this.entries)
+ {
+ if (isinstance(this.entries[p], Object))
+ {
+ if (name in this.entries[p])
+ return this.entries[p][name];
+ }
+ }
+ return null;
+}
+
+CMunger.prototype.addRule =
+function mng_addrule(name, regex, className, priority, startPriority, enable)
+{
+ if (typeof this.entries[priority] != "object")
+ this.entries[priority] = new Object();
+ var entry = new CMungerEntry(name, regex, className, priority,
+ startPriority, enable);
+ this.entries[priority][name] = entry;
+}
+
+CMunger.prototype.delRule =
+function mng_delrule(name)
+{
+ for (var i in this.entries)
+ {
+ if (typeof this.entries[i] == "object")
+ {
+ if (name in this.entries[i])
+ delete this.entries[i][name];
+ }
+ }
+}
+
+CMunger.prototype.munge =
+function mng_munge(text, containerTag, data)
+{
+
+ if (!containerTag)
+ containerTag = document.createElementNS(XHTML_NS, this.tagName);
+
+ // Starting from the top, for each valid priority, check all the rules,
+ // return as soon as something matches.
+ if (this.enabled)
+ {
+ for (var i = this.entries.length - 1; i >= 0; i--)
+ {
+ if (i in this.entries)
+ {
+ if (this.mungePriority(i, text, containerTag, data))
+ return containerTag;
+ }
+ }
+ }
+
+ // If nothing matched, we don't have to do anything,
+ // just insert text (if any).
+ if (text)
+ this.insertPlainText(text, containerTag, data);
+ return containerTag;
+}
+
+CMunger.prototype.mungePriority =
+function mng_mungePriority(priority, text, containerTag, data)
+{
+ var matches = new Object();
+ var entry;
+ // Find all the matches in this priority
+ for (entry in this.entries[priority])
+ {
+ var munger = this.entries[priority][entry];
+ if (!munger.enabled)
+ continue;
+
+ var match = null;
+ if (typeof munger.lambdaMatch == "function")
+ {
+ var rval = munger.lambdaMatch(text, containerTag, data, munger);
+ if (typeof rval == "string")
+ match = { start: text.indexOf(rval), text: rval };
+ else if (typeof rval == "object")
+ match = rval;
+ }
+ else
+ {
+ var ary = text.match(munger.regex);
+ if ((ary != null) && (ary[1]))
+ match = { start: text.indexOf(ary[1]), text: ary[1] };
+ }
+
+ if (match && (match.start >= 0))
+ {
+ match.munger = munger;
+ matches[entry] = match;
+ }
+ }
+
+ // Find the first matching entry...
+ var firstMatch = { start: text.length, munger: null };
+ var firstPriority = 0;
+ for (entry in matches)
+ {
+ // If it matches before the existing first, or at the same spot but
+ // with a higher start-priority, this is a better match.
+ if (matches[entry].start < firstMatch.start ||
+ ((matches[entry].start == firstMatch.start) &&
+ (this.entries[priority][entry].startPriority > firstPriority)))
+ {
+ firstMatch = matches[entry];
+ firstPriority = this.entries[priority][entry].startPriority;
+ }
+ }
+
+ // Replace it.
+ if (firstMatch.munger)
+ {
+ var munger = firstMatch.munger;
+ firstMatch.end = firstMatch.start + firstMatch.text.length;
+
+ // Need to deal with the text before the match, if there is any.
+ var beforeText = text.substr(0, firstMatch.start);
+ if (firstMatch.start > 0)
+ this.munge(beforeText, containerTag, data);
+
+ if (typeof munger.lambdaReplace == "function")
+ {
+ // The munger rule itself should take care of munging the 'inside'
+ // of the match.
+ munger.lambdaReplace(firstMatch.text, containerTag, data, munger);
+ this.munge(text.substr(firstMatch.end), containerTag, data);
+
+ return containerTag;
+ }
+ else
+ {
+ var tag = document.createElementNS(XHTML_NS, munger.tagName);
+ tag.setAttribute("class", munger.className + calcClass(data));
+
+ // Don't let this rule match again when we recurse.
+ munger.enabled = false;
+ this.munge(firstMatch.text, tag, data);
+ munger.enabled = true;
+
+ containerTag.appendChild(tag);
+
+ this.munge(text.substr(firstMatch.end), containerTag, data);
+
+ return containerTag;
+ }
+ }
+ return null;
+}
+
+function insertText(text, containerTag, data)
+{
+ var textNode = document.createTextNode(text);
+ containerTag.appendChild(textNode);
+}
+
diff --git a/comm/suite/chatzilla/xul/lib/tree-utils.js b/comm/suite/chatzilla/xul/lib/tree-utils.js
new file mode 100644
index 0000000000..1e47aebee6
--- /dev/null
+++ b/comm/suite/chatzilla/xul/lib/tree-utils.js
@@ -0,0 +1,1716 @@
+/* -*- Mode: C++; 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/. */
+
+/**
+ * An implemention of |nsITreeView| for a tree whose elements have no children.
+ *
+ * Code using BasicOView can override |getRowProperties|, |getColumnProperties|,
+ * |getCellProperties|, etc., as needed.
+ *
+ * Code using |BasicOView| will need to make the appropriate |myTree.tree
+ * .invalidate| calls when |myTree.data| changes.
+ *
+ * @syntax
+ * var myTree = new BasicOView()
+ * myTree.setColumnNames(["col 1", "col 2"]);
+ * myTree.data = [["row 1, col 1", "row 1, col 2"],
+ * ["row 2, col 1", "row 2, col 2"]];
+ * treeBoxObject.view = myTree;
+ */
+function BasicOView()
+{
+ this.tree = null;
+}
+
+/* functions *you* should call to initialize and maintain the tree state */
+
+/* scroll the line specified by |line| to the center of the tree */
+BasicOView.prototype.centerLine =
+function bov_ctrln (line)
+{
+ var first = this.tree.getFirstVisibleRow();
+ var last = this.tree.getLastVisibleRow();
+ this.scrollToRow(line - (last - first + 1) / 2);
+}
+
+/* call this to set the association between column names and data columns */
+BasicOView.prototype.setColumnNames =
+function bov_setcn (aryNames)
+{
+ this.columnNames = new Object();
+ for (var i = 0; i < aryNames.length; ++i)
+ this.columnNames[aryNames[i]] = i;
+}
+
+/*
+ * scroll the source so |line| is at either the top, center, or bottom
+ * of the view, depending on the value of |align|.
+ *
+ * line is the one based target line.
+ * if align is negative, the line will be scrolled to the top, if align is
+ * zero the line will be centered, and if align is greater than 0 the line
+ * will be scrolled to the bottom. 0 is the default.
+ */
+BasicOView.prototype.scrollTo =
+function bov_scrollto (line, align)
+{
+ if (!this.tree)
+ return;
+
+ var headerRows = 1;
+
+ var first = this.tree.getFirstVisibleRow();
+ var last = this.tree.getLastVisibleRow();
+ var viz = last - first + 1 - headerRows; /* total number of visible rows */
+
+ /* all rows are visible, nothing to scroll */
+ if (first == 0 && last >= this.rowCount)
+ return;
+
+ /* tree lines are 0 based, we accept one based lines, deal with it */
+ --line;
+
+ /* safety clamp */
+ if (line < 0)
+ line = 0;
+ if (line >= this.rowCount)
+ line = this.rowCount - 1;
+
+ if (align < 0)
+ {
+ if (line > this.rowCount - viz) /* overscroll, can't put a row from */
+ line = this.rowCount - viz; /* last page at the top. */
+ this.tree.scrollToRow(line);
+ }
+ else if (align > 0)
+ {
+ if (line < viz) /* underscroll, can't put a row from the first page */
+ line = 0; /* at the bottom. */
+ else
+ line = line - viz + headerRows;
+
+ this.tree.scrollToRow(line);
+ }
+ else
+ {
+ var half_viz = viz / 2;
+ /* lines past this line can't be centered without causing the tree
+ * to show more rows than we have. */
+ var lastCenterable = this.rowCount - half_viz;
+ if (line > half_viz)
+ line = lastCenterable;
+ /* lines before this can't be centered without causing the tree
+ * to attempt to display negative rows. */
+ else if (line < half_viz)
+ line = half_viz;
+ else
+ /* round the vizible rows down to a whole number, or we try to end up
+ * on a N + 0.5 row! */
+ half_viz = Math.floor(half_viz);
+
+ this.tree.scrollToRow(line - half_viz);
+ }
+}
+
+BasicOView.prototype.__defineGetter__("selectedIndex", bov_getsel);
+function bov_getsel()
+{
+ if (!this.tree || this.tree.view.selection.getRangeCount() < 1)
+ return -1;
+
+ var min = new Object();
+ this.tree.view.selection.getRangeAt(0, min, {});
+ return min.value;
+}
+
+BasicOView.prototype.__defineSetter__("selectedIndex", bov_setsel);
+function bov_setsel(i)
+{
+ if (i == -1)
+ this.tree.view.selection.clearSelection();
+ else
+ this.tree.view.selection.timedSelect (i, 500);
+ return i;
+}
+
+/*
+ * functions the tree will call to retrieve the list state (nsITreeView.)
+ */
+
+BasicOView.prototype.rowCount = 0;
+
+BasicOView.prototype.getCellProperties =
+function bov_cellprops (row, col, properties)
+{
+ return "";
+}
+
+BasicOView.prototype.getColumnProperties =
+function bov_colprops (col, properties)
+{
+ return "";
+}
+
+BasicOView.prototype.getRowProperties =
+function bov_rowprops (index, properties)
+{
+ return "";
+}
+
+BasicOView.prototype.isContainer =
+function bov_isctr (index)
+{
+ return false;
+}
+
+BasicOView.prototype.isContainerOpen =
+function bov_isctropen (index)
+{
+ return false;
+}
+
+BasicOView.prototype.isContainerEmpty =
+function bov_isctrempt (index)
+{
+ return false;
+}
+
+BasicOView.prototype.isSeparator =
+function bov_isseparator (index)
+{
+ return false;
+}
+
+BasicOView.prototype.isSorted =
+function bov_issorted (index)
+{
+ return false;
+}
+
+BasicOView.prototype.canDrop =
+function bov_drop (index, orientation)
+{
+ return false;
+}
+
+BasicOView.prototype.drop =
+function bov_drop (index, orientation)
+{
+ return false;
+}
+
+BasicOView.prototype.getParentIndex =
+function bov_getpi (index)
+{
+ if (index < 0)
+ return -1;
+
+ return 0;
+}
+
+BasicOView.prototype.hasNextSibling =
+function bov_hasnxtsib (rowIndex, afterIndex)
+{
+ return (afterIndex < (this.rowCount - 1));
+}
+
+BasicOView.prototype.getLevel =
+function bov_getlvl (index)
+{
+ return 0;
+}
+
+BasicOView.prototype.getImageSrc =
+function bov_getimgsrc (row, col)
+{
+}
+
+BasicOView.prototype.getProgressMode =
+function bov_getprgmode (row, col)
+{
+}
+
+BasicOView.prototype.getCellValue =
+function bov_getcellval (row, col)
+{
+}
+
+BasicOView.prototype.getCellText =
+function bov_getcelltxt (row, col)
+{
+ if (!this.columnNames)
+ return "";
+
+ if (typeof col == "object")
+ col = col.id;
+
+ var ary = col.match (/:(.*)/);
+ if (ary)
+ col = ary[1];
+
+ var colName = this.columnNames[col];
+
+ if (typeof colName == "undefined")
+ return "";
+
+ return this.data[row][colName];
+}
+
+BasicOView.prototype.setTree =
+function bov_seto (tree)
+{
+ this.tree = tree;
+}
+
+BasicOView.prototype.toggleOpenState =
+function bov_toggleopen (index)
+{
+}
+
+BasicOView.prototype.cycleHeader =
+function bov_cyclehdr (col)
+{
+}
+
+BasicOView.prototype.selectionChanged =
+function bov_selchg ()
+{
+}
+
+BasicOView.prototype.cycleCell =
+function bov_cyclecell (row, col)
+{
+}
+
+BasicOView.prototype.isEditable =
+function bov_isedit (row, col)
+{
+ return false;
+}
+
+BasicOView.prototype.isSelectable =
+function bov_isselect (row, col)
+{
+ return false;
+}
+
+BasicOView.prototype.setCellValue =
+function bov_setct (row, col, value)
+{
+}
+
+BasicOView.prototype.setCellText =
+function bov_setct (row, col, value)
+{
+}
+
+BasicOView.prototype.onRouteFocus =
+function bov_rfocus (event)
+{
+ if ("onFocus" in this)
+ this.onFocus(event);
+}
+
+BasicOView.prototype.onRouteBlur =
+function bov_rblur (event)
+{
+ if ("onBlur" in this)
+ this.onBlur(event);
+}
+
+BasicOView.prototype.onRouteDblClick =
+function bov_rdblclick (event)
+{
+ if (!("onRowCommand" in this) || event.target.localName != "treechildren")
+ return;
+
+ var rowIndex = this.tree.view.selection.currentIndex;
+ if (rowIndex == -1 || rowIndex > this.rowCount)
+ return;
+ var rec = this.childData.locateChildByVisualRow(rowIndex);
+ if (!rec)
+ {
+ ASSERT (0, "bogus row index " + rowIndex);
+ return;
+ }
+
+ this.onRowCommand(rec, event);
+}
+
+BasicOView.prototype.onRouteKeyPress =
+function bov_rkeypress (event)
+{
+ var rec;
+ var rowIndex;
+
+ if ("onRowCommand" in this && (event.keyCode == 13 || event.charCode == 32))
+ {
+ if (!this.selection)
+ return;
+
+ rowIndex = this.tree.view.selection.currentIndex;
+ if (rowIndex == -1 || rowIndex > this.rowCount)
+ return;
+ rec = this.childData.locateChildByVisualRow(rowIndex);
+ if (!rec)
+ {
+ ASSERT (0, "bogus row index " + rowIndex);
+ return;
+ }
+
+ this.onRowCommand(rec, event);
+ }
+ else if ("onKeyPress" in this)
+ {
+ rowIndex = this.tree.view.selection.currentIndex;
+ if (rowIndex != -1 && rowIndex < this.rowCount)
+ {
+ rec = this.childData.locateChildByVisualRow(rowIndex);
+ if (!rec)
+ {
+ ASSERT (0, "bogus row index " + rowIndex);
+ return;
+ }
+ }
+ else
+ {
+ rec = null;
+ }
+
+ this.onKeyPress(rec, event);
+ }
+}
+
+BasicOView.prototype.performAction =
+function bov_pact (action)
+{
+}
+
+BasicOView.prototype.performActionOnRow =
+function bov_pactrow (action)
+{
+}
+
+BasicOView.prototype.performActionOnCell =
+function bov_pactcell (action)
+{
+}
+
+/**
+ * A single entry in an |XULTreeView|.
+ *
+ * These things take care of keeping the |XULTreeView| properly informed of
+ * changes in value and child count. You shouldn't have to maintain tree state
+ * at all - just update the |XULTreeViewRecord| objects.
+ *
+ * @param share An otherwise empty object to store cache data. You should use
+ * the same object as the |share| for the |XULTreeView| that you
+ * indend to contain these records.
+ *
+ */
+function XULTreeViewRecord(share)
+{
+ this._share = share;
+ this.visualFootprint = 1;
+ this.isHidden = true; /* records are considered hidden until they are
+ * inserted into a live tree */
+}
+
+XULTreeViewRecord.prototype.isContainerOpen = false;
+
+/*
+ * walk the parent tree to find our tree container. return null if there is
+ * none
+ */
+XULTreeViewRecord.prototype.findContainerTree =
+function xtvr_gettree ()
+{
+ if (!("parentRecord" in this))
+ return null;
+ var parent = this.parentRecord;
+
+ while (parent)
+ {
+ if ("_treeView" in parent)
+ return parent._treeView;
+ if ("parentRecord" in parent)
+ parent = parent.parentRecord;
+ else
+ parent = null;
+ }
+
+ return null;
+}
+
+XULTreeViewRecord.prototype.__defineGetter__("childIndex", xtvr_getChildIndex);
+function xtvr_getChildIndex ()
+{
+ //dd ("getChildIndex {");
+
+ if (!("parentRecord" in this))
+ {
+ delete this._childIndex;
+ //dd ("} -1");
+ return -1;
+ }
+
+ if ("_childIndex" in this)
+ {
+ if ("childData" in this && this._childIndex in this.childData &&
+ this.childData[this._childIndex] == this)
+ {
+ //dd ("} " + this._childIndex);
+ return this._childIndex;
+ }
+ }
+
+ var childData = this.parentRecord.childData;
+ var len = childData.length;
+ for (var i = 0; i < len; ++i)
+ {
+ if (childData[i] == this)
+ {
+ this._childIndex = i;
+ //dd ("} " + this._childIndex);
+ return i;
+ }
+ }
+
+ delete this._childIndex;
+ //dd ("} -1");
+ return -1;
+}
+
+XULTreeViewRecord.prototype.__defineSetter__("childIndex", xtvr_setChildIndex);
+function xtvr_setChildIndex ()
+{
+ dd("xtvr: childIndex is read only, ignore attempt to write to it\n");
+ if (typeof getStackTrace == "function")
+ dd(getStackTrace());
+}
+
+/* count the number of parents, not including the root node */
+XULTreeViewRecord.prototype.__defineGetter__("level", xtvr_getLevel);
+function xtvr_getLevel ()
+{
+ if (!("parentRecord" in this))
+ return -1;
+
+ var rv = 0;
+ var parentRecord = this.parentRecord;
+ while ("parentRecord" in parentRecord &&
+ (parentRecord = parentRecord.parentRecord)) ++rv;
+ return rv;
+}
+
+/*
+ * associates a property name on this record, with a column in the tree. This
+ * method will set up a get/set pair for the property name you specify which
+ * will take care of updating the tree when the value changes. DO NOT try
+ * to change your mind later. Do not attach a different name to the same colID,
+ * and do not rename the colID. You have been warned.
+ */
+XULTreeViewRecord.prototype.setColumnPropertyName =
+function xtvr_setcol (colID, propertyName)
+{
+ function xtvr_getValueShim ()
+ {
+ return this._colValues[colID];
+ }
+ function xtvr_setValueShim (newValue)
+ {
+ this._colValues[colID] = newValue;
+ return newValue;
+ }
+
+ if (!("_colValues" in this))
+ this._colValues = new Object();
+
+ if (typeof propertyName == "function")
+ {
+ this._colValues.__defineGetter__(colID, propertyName);
+ }
+ else
+ {
+ this.__defineGetter__(propertyName, xtvr_getValueShim);
+ this.__defineSetter__(propertyName, xtvr_setValueShim);
+ }
+}
+
+XULTreeViewRecord.prototype.setColumnPropertyValue =
+function xtvr_setcolv (colID, value)
+{
+ this._colValues[colID] = value;
+}
+
+/*
+ * set the default sort column and reSort.
+ */
+XULTreeViewRecord.prototype.setSortColumn =
+function xtvr_setcol (colID, dir)
+{
+ //dd ("setting sort column to " + colID);
+ this._share.sortColumn = colID;
+ this._share.sortDirection = (typeof dir == "undefined") ? 1 : dir;
+ this.reSort();
+}
+
+/*
+ * set the default sort direction. 1 is ascending, -1 is descending, 0 is no
+ * sort. setting this to 0 will *not* recover the natural insertion order,
+ * it will only affect newly added items.
+ */
+XULTreeViewRecord.prototype.setSortDirection =
+function xtvr_setdir (dir)
+{
+ this._share.sortDirection = dir;
+}
+
+/*
+ * invalidate this row in the tree
+ */
+XULTreeViewRecord.prototype.invalidate =
+function xtvr_invalidate()
+{
+ var tree = this.findContainerTree();
+ if (tree)
+ {
+ var row = this.calculateVisualRow();
+ if (row != -1)
+ tree.tree.invalidateRow(row);
+ }
+}
+
+/*
+ * invalidate any data in the cache.
+ */
+XULTreeViewRecord.prototype.invalidateCache =
+function xtvr_killcache()
+{
+ this._share.rowCache = new Object();
+ this._share.lastComputedIndex = -1;
+ this._share.lastIndexOwner = null;
+}
+
+/*
+ * default comparator function for sorts. if you want a custom sort, override
+ * this method. We declare xtvr_sortcmp as a top level function, instead of
+ * a function expression so we can refer to it later.
+ */
+XULTreeViewRecord.prototype.sortCompare = xtvr_sortcmp;
+function xtvr_sortcmp (a, b)
+{
+ var sc = a._share.sortColumn;
+ var sd = a._share.sortDirection;
+
+ a = a[sc];
+ b = b[sc];
+
+ if (a < b)
+ return -1 * sd;
+
+ if (a > b)
+ return 1 * sd;
+
+ return 0;
+}
+
+/*
+ * this method will cause all child records to be reSorted. any records
+ * with the default sortCompare method will be sorted by the colID passed to
+ * setSortColumn.
+ *
+ * the local parameter is used internally to control whether or not the
+ * sorted rows are invalidated. don't use it yourself.
+ */
+XULTreeViewRecord.prototype.reSort =
+function xtvr_resort (leafSort)
+{
+ if (!("childData" in this) || this.childData.length < 1 ||
+ (this.childData[0].sortCompare == xtvr_sortcmp &&
+ !("sortColumn" in this._share) || this._share.sortDirection == 0))
+ {
+ /* if we have no children, or we have the default sort compare and no
+ * sort flags, then just exit */
+ return;
+ }
+
+ this.childData.sort(this.childData[0].sortCompare);
+
+ for (var i = 0; i < this.childData.length; ++i)
+ {
+ if ("isContainerOpen" in this.childData[i] &&
+ this.childData[i].isContainerOpen)
+ this.childData[i].reSort(true);
+ else
+ this.childData[i].sortIsInvalid = true;
+ }
+
+ if (!leafSort)
+ {
+ this.invalidateCache();
+ var tree = this.findContainerTree();
+ if (tree && tree.tree)
+ {
+ var rowIndex = this.calculateVisualRow();
+ /*
+ dd ("invalidating " + rowIndex + " - " +
+ (rowIndex + this.visualFootprint - 1));
+ */
+ tree.tree.invalidateRange (rowIndex,
+ rowIndex + this.visualFootprint - 1);
+ }
+ }
+ delete this.sortIsInvalid;
+}
+
+/*
+ * call this to indicate that this node may have children at one point. make
+ * sure to call it before adding your first child.
+ */
+XULTreeViewRecord.prototype.reserveChildren =
+function xtvr_rkids (always)
+{
+ if (!("childData" in this))
+ this.childData = new Array();
+ if (!("isContainerOpen" in this))
+ this.isContainerOpen = false;
+ if (always)
+ this.alwaysHasChildren = true;
+ else
+ delete this.alwaysHasChildren;
+}
+
+/*
+ * add a child to the end of the child list for this record. takes care of
+ * updating the tree as well.
+ */
+XULTreeViewRecord.prototype.appendChild =
+function xtvr_appchild (child)
+{
+ if (!isinstance(child, XULTreeViewRecord))
+ throw Components.results.NS_ERROR_INVALID_ARG;
+
+ child.isHidden = false;
+ child.parentRecord = this;
+ this.childData.push(child);
+
+ if ("isContainerOpen" in this && this.isContainerOpen)
+ {
+ //dd ("appendChild: " + xtv_formatRecord(child, ""));
+ if (this.calculateVisualRow() >= 0)
+ {
+ var tree = this.findContainerTree();
+ if (tree && tree.frozen)
+ this.needsReSort = true;
+ else
+ this.reSort(true); /* reSort, don't invalidate. we're going
+ * to do that in the
+ * onVisualFootprintChanged call. */
+ }
+ this.onVisualFootprintChanged(child.calculateVisualRow(),
+ child.visualFootprint);
+ }
+}
+
+/*
+ * add a list of children to the end of the child list for this record.
+ * faster than multiple appendChild() calls.
+ */
+XULTreeViewRecord.prototype.appendChildren =
+function xtvr_appchild (children)
+{
+ var delta = 0;
+ for (var i = 0; i < children.length; ++i)
+ {
+ var child = children[i];
+ child.isHidden = false;
+ child.parentRecord = this;
+ this.childData.push(child);
+ delta += child.visualFootprint;
+ }
+
+ if ("isContainerOpen" in this && this.isContainerOpen)
+ {
+ if (this.calculateVisualRow() >= 0)
+ {
+ this.reSort(true); /* reSort, don't invalidate. we're going to do
+ * that in the onVisualFootprintChanged call. */
+ }
+ this.onVisualFootprintChanged(this.childData[0].calculateVisualRow(),
+ delta);
+ }
+}
+
+/*
+ * Removes a single child from this record by index.
+ * @param index Index of the child record to remove.
+ */
+XULTreeViewRecord.prototype.removeChildAtIndex =
+function xtvr_remchild(index)
+{
+ var len = this.childData.length;
+ if (!ASSERT(index >= 0 && index < len, "index out of bounds"))
+ return;
+
+ var orphan = this.childData[index];
+ var delta = -orphan.visualFootprint;
+ var changeStart = orphan.calculateVisualRow();
+ delete orphan.parentRecord;
+ arrayRemoveAt(this.childData, index);
+
+ if (!orphan.isHidden && "isContainerOpen" in this && this.isContainerOpen)
+ this.onVisualFootprintChanged(changeStart, delta);
+}
+
+/*
+ * Removes a range of children from this record by index. Faster than multiple
+ * removeChildAtIndex() calls.
+ * @param index Index of the first child record to remove.
+ * @param count Number of child records to remove.
+ */
+XULTreeViewRecord.prototype.removeChildrenAtIndex =
+function xtvr_remchildren(index, count)
+{
+ var len = this.childData.length;
+ if (!ASSERT(index >= 0 && index < len, "index out of bounds"))
+ return;
+ if (!ASSERT(count > 0 && index + count <= len, "count out of bounds"))
+ return;
+
+ var delta = 0;
+ var changeStart = this.childData[index].calculateVisualRow();
+ for (var i = 0; i < count; ++i)
+ {
+ var orphan = this.childData[index + i];
+ if (!orphan.isHidden)
+ delta -= orphan.visualFootprint;
+ delete orphan.parentRecord;
+ }
+ this.childData.splice(index, count);
+
+ if ("isContainerOpen" in this && this.isContainerOpen)
+ this.onVisualFootprintChanged(changeStart, delta);
+}
+
+/*
+ * hide this record and all descendants.
+ */
+XULTreeViewRecord.prototype.hide =
+function xtvr_hide()
+{
+ if (this.isHidden)
+ return;
+
+ var tree = this.findContainerTree();
+ if (tree && tree.frozen)
+ {
+ this.isHidden = true;
+ if ("parentRecord" in this)
+ this.parentRecord.onVisualFootprintChanged(0, -this.visualFootprint);
+ return;
+ }
+
+ /* get the row before hiding */
+ var row = this.calculateVisualRow();
+ this.invalidateCache();
+ this.isHidden = true;
+ /* go right to the parent so we don't muck with our own visualFootprint
+ * record, we'll need it to be correct if we're ever unHidden. */
+ if ("parentRecord" in this)
+ this.parentRecord.onVisualFootprintChanged(row, -this.visualFootprint);
+}
+
+/*
+ * unhide this record and all descendants.
+ */
+XULTreeViewRecord.prototype.unHide =
+function xtvr_uhide()
+{
+ if (!this.isHidden)
+ return;
+
+ var tree = this.findContainerTree();
+ if (tree && tree.frozen)
+ {
+ this.isHidden = false;
+ if ("parentRecord" in this)
+ this.parentRecord.onVisualFootprintChanged(0, this.visualFootprint);
+ return;
+ }
+
+ this.isHidden = false;
+ this.invalidateCache();
+ var row = this.calculateVisualRow();
+ if (this.parentRecord)
+ this.parentRecord.onVisualFootprintChanged(row, this.visualFootprint);
+}
+
+/*
+ * open this record, exposing it's children. DONT call this method if the
+ * record has no children.
+ */
+XULTreeViewRecord.prototype.open =
+function xtvr_open ()
+{
+ if (this.isContainerOpen)
+ return;
+
+ if ("onPreOpen" in this)
+ this.onPreOpen();
+
+ this.isContainerOpen = true;
+ var delta = 0;
+ for (var i = 0; i < this.childData.length; ++i)
+ {
+ if (!this.childData[i].isHidden)
+ delta += this.childData[i].visualFootprint;
+ }
+
+ /* this reSort should only happen if the sort column changed */
+ this.reSort(true);
+ this.visualFootprint += delta;
+ if ("parentRecord" in this)
+ {
+ this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
+ 0);
+ this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow() +
+ 1, delta);
+ }
+}
+
+/*
+ * close this record, hiding it's children. DONT call this method if the record
+ * has no children, or if it is already closed.
+ */
+XULTreeViewRecord.prototype.close =
+function xtvr_close ()
+{
+ if (!this.isContainerOpen)
+ return;
+
+ this.isContainerOpen = false;
+ var delta = 1 - this.visualFootprint;
+ this.visualFootprint += delta;
+ if ("parentRecord" in this)
+ {
+ this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
+ 0);
+ this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow() +
+ 1, delta);
+ }
+
+ if ("onPostClose" in this)
+ this.onPostClose();
+}
+
+/*
+ * called when a node above this one grows or shrinks. we need to adjust
+ * our own visualFootprint to match the change, and pass the message on.
+ */
+XULTreeViewRecord.prototype.onVisualFootprintChanged =
+function xtvr_vpchange (start, amount)
+{
+ /* if we're not hidden, but this notification came from a hidden node
+ * (start == -1), ignore it, it doesn't affect us. */
+ if (start == -1 && !this.isHidden)
+ {
+
+ //dd ("vfp change (" + amount + ") from hidden node ignored.");
+ return;
+ }
+
+ this.visualFootprint += amount;
+
+ if ("parentRecord" in this)
+ this.parentRecord.onVisualFootprintChanged(start, amount);
+}
+
+/*
+ * calculate the "visual" row for this record. If the record isn't actually
+ * visible return -1.
+ * eg.
+ * Name Visual Row
+ * node1 0
+ * node11 1
+ * node12 2
+ * node2 3
+ * node21 4
+ * node3 5
+ */
+XULTreeViewRecord.prototype.calculateVisualRow =
+function xtvr_calcrow ()
+{
+ /* if this is the second time in a row that someone asked us, fetch the last
+ * result from the cache. */
+ if (this._share.lastIndexOwner == this)
+ return this._share.lastComputedIndex;
+
+ var vrow;
+
+ /* if this is an uninserted or hidden node, or... */
+ if (!("parentRecord" in this) || (this.isHidden) ||
+ /* if parent isn't open, or... */
+ (!this.parentRecord.isContainerOpen) ||
+ /* parent isn't visible */
+ ((vrow = this.parentRecord.calculateVisualRow()) == -1))
+ {
+ /* then we're not visible, return -1 */
+ //dd ("cvr: returning -1");
+ return -1;
+ }
+
+ /* parent is the root node XXX parent is not visible */
+ if (vrow == null)
+ vrow = 0;
+ else
+ /* parent is not the root node, add one for the space they take up. */
+ ++vrow;
+
+ /* add in the footprint for all of the earlier siblings */
+ var ci = this.childIndex;
+ for (var i = 0; i < ci; ++i)
+ {
+ if (!this.parentRecord.childData[i].isHidden)
+ vrow += this.parentRecord.childData[i].visualFootprint;
+ }
+
+ /* save this calculation to the cache. */
+ this._share.lastIndexOwner = this;
+ this._share.lastComputedIndex = vrow;
+
+ return vrow;
+}
+
+/*
+ * locates the child record for the visible row |targetRow|. DO NOT call this
+ * with a targetRow less than this record's visual row, or greater than this
+ * record's visual row + the number of visible children it has.
+ */
+XULTreeViewRecord.prototype.locateChildByVisualRow =
+function xtvr_find (targetRow, myRow)
+{
+ if (targetRow in this._share.rowCache)
+ return this._share.rowCache[targetRow];
+
+ /* if this is true, we *are* the index */
+ if (targetRow == myRow)
+ return (this._share.rowCache[targetRow] = this);
+
+ /* otherwise, we've got to search the kids */
+ var childStart = myRow; /* childStart represents the starting visual row
+ * for the child we're examining. */
+ for (var i = 0; i < this.childData.length; ++i)
+ {
+ var child = this.childData[i];
+ /* ignore hidden children */
+ if (child.isHidden)
+ continue;
+ /* if this kid is the targetRow, we're done */
+ if (childStart == targetRow)
+ return (this._share.rowCache[targetRow] = child);
+ /* if this kid contains the index, ask *it* to find the record */
+ else if (targetRow <= childStart + child.visualFootprint) {
+ /* this *has* to succeed */
+ var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
+ //XXXASSERT (rv, "Can't find a row that *has* to be there.");
+ /* don't cache this, the previous call to locateChildByVisualRow
+ * just did. */
+ return rv;
+ }
+
+ /* otherwise, get ready to ask the next kid */
+ childStart += child.visualFootprint;
+ }
+
+ return null;
+}
+
+/**
+ * Used to drop a label into an arbitrary place in an arbitrary tree.
+ *
+ * Normally, specializations of |XULTreeViewRecord| are tied to a specific
+ * tree because of implementation details. |XTLabelRecords| are specially
+ * designed (err, hacked) to work around these details - this makes them
+ * slower, but more generic.
+ *
+ * We set up a getter for |_share| that defers to the parent object. This lets
+ * |XTLabelRecords| work in any tree.
+ */
+function XTLabelRecord (columnName, label, blankCols)
+{
+ this.setColumnPropertyName (columnName, "label");
+ this.label = label;
+ this.property = null;
+
+ if (typeof blankCols == "object")
+ {
+ for (var i in blankCols)
+ this._colValues[blankCols[i]] = "";
+ }
+}
+
+XTLabelRecord.prototype = new XULTreeViewRecord (null);
+
+XTLabelRecord.prototype.__defineGetter__("_share", tolr_getshare);
+function tolr_getshare()
+{
+ if ("parentRecord" in this)
+ return this.parentRecord._share;
+
+ ASSERT (0, "XTLabelRecord cannot be the root of a visible tree.");
+ return null;
+}
+
+// @internal
+function XTRootRecord (tree, share)
+{
+ this._share = share;
+ this._treeView = tree;
+ this.visualFootprint = 0;
+ this.isHidden = false;
+ this.reserveChildren();
+ this.isContainerOpen = true;
+}
+
+/* no cache passed in here, we set it in the XTRootRecord contructor instead. */
+XTRootRecord.prototype = new XULTreeViewRecord (null);
+
+XTRootRecord.prototype.open =
+XTRootRecord.prototype.close =
+function torr_notimplemented()
+{
+ /* don't do this on a root node */
+}
+
+XTRootRecord.prototype.calculateVisualRow =
+function torr_calcrow ()
+{
+ return null;
+}
+
+XTRootRecord.prototype.reSort =
+function torr_resort ()
+{
+ if ("_treeView" in this && this._treeView.frozen)
+ {
+ this._treeView.needsReSort = true;
+ return;
+ }
+
+ if (!("childData" in this) || this.childData.length < 1 ||
+ (this.childData[0].sortCompare == xtvr_sortcmp &&
+ !("sortColumn" in this._share) || this._share.sortDirection == 0))
+ {
+ /* if we have no children, or we have the default sort compare but we're
+ * missing a sort flag, then just exit */
+ return;
+ }
+
+ this.childData.sort(this.childData[0].sortCompare);
+
+ for (var i = 0; i < this.childData.length; ++i)
+ {
+ if ("isContainerOpen" in this.childData[i] &&
+ this.childData[i].isContainerOpen)
+ this.childData[i].reSort(true);
+ else
+ this.childData[i].sortIsInvalid = true;
+ }
+
+ if ("_treeView" in this && this._treeView.tree)
+ {
+ /*
+ dd ("root node: invalidating 0 - " + this.visualFootprint +
+ " for sort");
+ */
+ this.invalidateCache();
+ this._treeView.tree.invalidateRange (0, this.visualFootprint);
+ }
+}
+
+XTRootRecord.prototype.locateChildByVisualRow =
+function torr_find (targetRow)
+{
+ if (targetRow in this._share.rowCache)
+ return this._share.rowCache[targetRow];
+
+ var childStart = -1; /* childStart represents the starting visual row
+ * for the child we're examining. */
+ for (var i = 0; i < this.childData.length; ++i)
+ {
+ var child = this.childData[i];
+ /* ignore hidden children */
+ if (child.isHidden)
+ continue;
+ /* if this kid is the targetRow, we're done */
+ if (childStart == targetRow)
+ return (this._share.rowCache[targetRow] = child);
+ /* if this kid contains the index, ask *it* to find the record */
+ else if (targetRow <= childStart + child.visualFootprint) {
+ /* this *has* to succeed */
+ var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
+ //XXXASSERT (rv, "Can't find a row that *has* to be there.");
+ /* don't cache this, the previous call to locateChildByVisualRow
+ * just did. */
+ return rv;
+ }
+
+ /* otherwise, get ready to ask the next kid */
+ childStart += child.visualFootprint;
+ }
+
+ return null;
+}
+
+XTRootRecord.prototype.onVisualFootprintChanged =
+function torr_vfpchange (start, amount)
+{
+ if (!this._treeView.frozen)
+ {
+ this.invalidateCache();
+ this.visualFootprint += amount;
+ if ("_treeView" in this && "tree" in this._treeView &&
+ this._treeView.tree)
+ {
+ if (amount != 0)
+ this._treeView.tree.rowCountChanged (start, amount);
+ else
+ this._treeView.tree.invalidateRow (start);
+ }
+ }
+ else
+ {
+ if ("changeAmount" in this._treeView)
+ this._treeView.changeAmount += amount;
+ else
+ this._treeView.changeAmount = amount;
+ if ("changeStart" in this._treeView)
+ this._treeView.changeStart =
+ Math.min (start, this._treeView.changeStart);
+ else
+ this._treeView.changeStart = start;
+ }
+}
+
+/**
+ * An implemention of |nsITreeView| for a tree whose elements have multiple
+ * levels of children.
+ *
+ * Code using |XULTreeView| can override |getRowProperties|, |getColumnProperties|,
+ * |getCellProperties|, etc., as needed.
+ *
+ * @param share An otherwise empty object to store cache data.
+ */
+function XULTreeView(share)
+{
+ if (!share)
+ share = new Object();
+ this.childData = new XTRootRecord(this, share);
+ this.childData.invalidateCache();
+ this.tree = null;
+ this.share = share;
+ this.frozen = 0;
+}
+
+/* functions *you* should call to initialize and maintain the tree state */
+
+/*
+ * Changes to the tree contents will not cause the tree to be invalidated
+ * until |thaw()| is called. All changes will be pooled into a single invalidate
+ * call.
+ *
+ * Freeze/thaws are nestable, the tree will not update until the number of
+ * |thaw()| calls matches the number of freeze() calls.
+ */
+XULTreeView.prototype.freeze =
+function xtv_freeze ()
+{
+ if (++this.frozen == 1)
+ {
+ this.changeStart = 0;
+ this.changeAmount = 0;
+ }
+}
+
+/*
+ * Reflect any changes to the tree content since the last freeze.
+ */
+XULTreeView.prototype.thaw =
+function xtv_thaw ()
+{
+ if (this.frozen == 0)
+ {
+ ASSERT (0, "not frozen");
+ return;
+ }
+
+ if (--this.frozen == 0 && "changeStart" in this)
+ {
+ this.childData.onVisualFootprintChanged(this.changeStart,
+ this.changeAmount);
+ }
+
+ if ("needsReSort" in this) {
+ this.childData.reSort();
+ delete this.needsReSort;
+ }
+
+
+ delete this.changeStart;
+ delete this.changeAmount;
+}
+
+XULTreeView.prototype.saveBranchState =
+function xtv_savebranch (target, source, recurse)
+{
+ var len = source.length;
+ for (var i = 0; i < len; ++i)
+ {
+ if (source[i].isContainerOpen)
+ {
+ target[i] = new Object();
+ target[i].name = source[i]._colValues["col-0"];
+ if (recurse)
+ this.saveBranchState (target[i], source[i].childData, true);
+ }
+ }
+}
+
+XULTreeView.prototype.restoreBranchState =
+function xtv_restorebranch (target, source, recurse)
+{
+ for (var i in source)
+ {
+ if (typeof source[i] == "object")
+ {
+ var name = source[i].name;
+ var len = target.length;
+ for (var j = 0; j < len; ++j)
+ {
+ if (target[j]._colValues["col-0"] == name &&
+ "childData" in target[j])
+ {
+ //dd ("opening " + name);
+ target[j].open();
+ if (recurse)
+ {
+ this.restoreBranchState (target[j].childData,
+ source[i], true);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* scroll the line specified by |line| to the center of the tree */
+XULTreeView.prototype.centerLine =
+function xtv_ctrln (line)
+{
+ var first = this.tree.getFirstVisibleRow();
+ var last = this.tree.getLastVisibleRow();
+ this.scrollToRow(line - (last - first + 1) / 2);
+}
+
+/*
+ * functions the tree will call to retrieve the list state (nsITreeView.)
+ */
+
+// @internal
+XULTreeView.prototype.__defineGetter__("rowCount", xtv_getRowCount);
+function xtv_getRowCount ()
+{
+ if (!this.childData)
+ return 0;
+
+ return this.childData.visualFootprint;
+}
+
+// @internal
+XULTreeView.prototype.isContainer =
+function xtv_isctr (index)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+
+ return Boolean(row && ("alwaysHasChildren" in row || "childData" in row));
+}
+
+// @internal
+XULTreeView.prototype.__defineGetter__("selectedIndex", xtv_getsel);
+function xtv_getsel()
+{
+ if (!this.tree || this.tree.view.selection.getRangeCount() < 1)
+ return -1;
+
+ var min = new Object();
+ this.tree.view.selection.getRangeAt(0, min, {});
+ return min.value;
+}
+
+// @internal
+XULTreeView.prototype.__defineSetter__("selectedIndex", xtv_setsel);
+function xtv_setsel(i)
+{
+ this.tree.view.selection.clearSelection();
+ if (i != -1)
+ this.tree.view.selection.timedSelect (i, 500);
+ return i;
+}
+
+// @internal
+XULTreeView.prototype.scrollTo = BasicOView.prototype.scrollTo;
+
+// @internal
+XULTreeView.prototype.isContainerOpen =
+function xtv_isctropen (index)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ return row && row.isContainerOpen;
+}
+
+// @internal
+XULTreeView.prototype.toggleOpenState =
+function xtv_toggleopen (index)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ //ASSERT(row, "bogus row");
+ if (row)
+ {
+ if (row.isContainerOpen)
+ row.close();
+ else
+ row.open();
+ }
+}
+
+// @internal
+XULTreeView.prototype.isContainerEmpty =
+function xtv_isctrempt (index)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ if ("alwaysHasChildren" in row)
+ return false;
+
+ if (!row || !("childData" in row))
+ return true;
+
+ return !row.childData.length;
+}
+
+// @internal
+XULTreeView.prototype.isSeparator =
+function xtv_isseparator (index)
+{
+ return false;
+}
+
+// @internal
+XULTreeView.prototype.getParentIndex =
+function xtv_getpi (index)
+{
+ if (index < 0)
+ return -1;
+
+ var row = this.childData.locateChildByVisualRow (index);
+
+ var rv = row.parentRecord.calculateVisualRow();
+ //dd ("getParentIndex: row " + index + " returning " + rv);
+ return (rv != null) ? rv : -1;
+}
+
+// @internal
+XULTreeView.prototype.hasNextSibling =
+function xtv_hasnxtsib (rowIndex, afterIndex)
+{
+ var row = this.childData.locateChildByVisualRow (rowIndex);
+ return row.childIndex < row.parentRecord.childData.length - 1;
+}
+
+// @internal
+XULTreeView.prototype.getLevel =
+function xtv_getlvl (index)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ if (!row)
+ return 0;
+
+ return row.level;
+}
+
+// @internal
+XULTreeView.prototype.getImageSrc =
+function xtv_getimgsrc (index, col)
+{
+}
+
+// @internal
+XULTreeView.prototype.getProgressMode =
+function xtv_getprgmode (index, col)
+{
+}
+
+// @internal
+XULTreeView.prototype.getCellValue =
+function xtv_getcellval (index, col)
+{
+}
+
+// @internal
+XULTreeView.prototype.getCellText =
+function xtv_getcelltxt (index, col)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ //ASSERT(row, "bogus row " + index);
+
+ if (typeof col == "object")
+ col = col.id;
+
+ var ary = col.match (/:(.*)/);
+ if (ary)
+ col = ary[1];
+
+ if (row && row._colValues && col in row._colValues)
+ return row._colValues[col];
+ else
+ return "";
+}
+
+// @internal
+XULTreeView.prototype.getCellProperties =
+function xtv_cellprops (row, col, properties)
+{
+ return "";
+}
+
+// @internal
+XULTreeView.prototype.getColumnProperties =
+function xtv_colprops (col, properties)
+{
+ return "";
+}
+
+// @internal
+XULTreeView.prototype.getRowProperties =
+function xtv_rowprops (index, properties)
+{
+ return "";
+}
+
+// @internal
+XULTreeView.prototype.isSorted =
+function xtv_issorted (index)
+{
+ return false;
+}
+
+// @internal
+XULTreeView.prototype.canDrop =
+function xtv_drop (index, orientation)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ //ASSERT(row, "bogus row " + index);
+ return (row && ("canDrop" in row) && row.canDrop(orientation));
+}
+
+// @internal
+XULTreeView.prototype.drop =
+function xtv_drop (index, orientation)
+{
+ var row = this.childData.locateChildByVisualRow (index);
+ //ASSERT(row, "bogus row " + index);
+ return (row && ("drop" in row) && row.drop(orientation));
+}
+
+// @internal
+XULTreeView.prototype.setTree =
+function xtv_seto (tree)
+{
+ this.childData.invalidateCache();
+ this.tree = tree;
+}
+
+// @internal
+XULTreeView.prototype.cycleHeader =
+function xtv_cyclehdr (col)
+{
+}
+
+// @internal
+XULTreeView.prototype.selectionChanged =
+function xtv_selchg ()
+{
+}
+
+// @internal
+XULTreeView.prototype.cycleCell =
+function xtv_cyclecell (row, col)
+{
+}
+
+// @internal
+XULTreeView.prototype.isEditable =
+function xtv_isedit (row, col)
+{
+ return false;
+}
+
+// @internal
+XULTreeView.prototype.isSelectable =
+function xtv_isselect (row, col)
+{
+ return false;
+}
+
+// @internal
+XULTreeView.prototype.setCellValue =
+function xtv_setct (row, col, value)
+{
+}
+
+// @internal
+XULTreeView.prototype.setCellText =
+function xtv_setct (row, col, value)
+{
+}
+
+// @internal
+XULTreeView.prototype.performAction =
+function xtv_pact (action)
+{
+}
+
+// @internal
+XULTreeView.prototype.performActionOnRow =
+function xtv_pactrow (action)
+{
+}
+
+// @internal
+XULTreeView.prototype.performActionOnCell =
+function xtv_pactcell (action)
+{
+}
+
+// @internal
+XULTreeView.prototype.onRouteFocus =
+function xtv_rfocus (event)
+{
+ if ("onFocus" in this)
+ this.onFocus(event);
+}
+
+// @internal
+XULTreeView.prototype.onRouteBlur =
+function xtv_rblur (event)
+{
+ if ("onBlur" in this)
+ this.onBlur(event);
+}
+
+// @internal
+XULTreeView.prototype.onRouteDblClick =
+function xtv_rdblclick (event)
+{
+ if (!("onRowCommand" in this) || event.target.localName != "treechildren")
+ return;
+
+ var rowIndex = this.tree.view.selection.currentIndex;
+ if (rowIndex == -1 || rowIndex > this.rowCount)
+ return;
+ var rec = this.childData.locateChildByVisualRow(rowIndex);
+ if (!rec)
+ {
+ ASSERT (0, "bogus row index " + rowIndex);
+ return;
+ }
+
+ this.onRowCommand(rec, event);
+}
+
+// @internal
+XULTreeView.prototype.onRouteKeyPress =
+function xtv_rkeypress (event)
+{
+ var rec;
+ var rowIndex;
+
+ if ("onRowCommand" in this && (event.keyCode == 13 || event.charCode == 32))
+ {
+ if (!this.selection)
+ return;
+
+ rowIndex = this.tree.view.selection.currentIndex;
+ if (rowIndex == -1 || rowIndex > this.rowCount)
+ return;
+ rec = this.childData.locateChildByVisualRow(rowIndex);
+ if (!rec)
+ {
+ ASSERT (0, "bogus row index " + rowIndex);
+ return;
+ }
+
+ this.onRowCommand(rec, event);
+ }
+ else if ("onKeyPress" in this)
+ {
+ rowIndex = this.tree.view.selection.currentIndex;
+ if (rowIndex != -1 && rowIndex < this.rowCount)
+ {
+ rec = this.childData.locateChildByVisualRow(rowIndex);
+ if (!rec)
+ {
+ ASSERT (0, "bogus row index " + rowIndex);
+ return;
+ }
+ }
+ else
+ {
+ rec = null;
+ }
+
+ this.onKeyPress(rec, event);
+ }
+}
+
+/******************************************************************************/
+
+function xtv_formatRecord (rec, indent)
+{
+ var str = "";
+
+ for (var i in rec._colValues)
+ str += rec._colValues[i] + ", ";
+
+ str += "[";
+
+ str += rec.calculateVisualRow() + ", ";
+ str += rec.childIndex + ", ";
+ str += rec.level + ", ";
+ str += rec.visualFootprint + ", ";
+ str += rec.isHidden + "]";
+
+ return (indent + str);
+}
+
+function xtv_formatBranch (rec, indent, recurse)
+{
+ var str = "";
+ for (var i = 0; i < rec.childData.length; ++i)
+ {
+ str += xtv_formatRecord (rec.childData[i], indent) + "\n";
+ if (recurse)
+ {
+ if ("childData" in rec.childData[i])
+ str += xtv_formatBranch(rec.childData[i], indent + " ",
+ --recurse);
+ }
+ }
+
+ return str;
+}
+
diff --git a/comm/suite/chatzilla/xul/skin/about.css b/comm/suite/chatzilla/xul/skin/about.css
new file mode 100644
index 0000000000..e4d935fa39
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/about.css
@@ -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/. */
+
+dialog {
+ padding-top: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+ width: 30em;
+}
+
+.box-padded {
+ background-color: Window;
+ color: WindowText;
+}
+
+.large-text {
+ font-size: large;
+}
+
+#logo {
+ background: url(chrome://chatzilla/skin/images/logo.png);
+ background-repeat: no-repeat;
+ width: 32px;
+ height: 32px;
+}
+
+#version {
+ color: GrayText;
+}
+
+.contributors-label {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+.contributors label {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+#groove {
+ margin-top: 0px;
+}
diff --git a/comm/suite/chatzilla/xul/skin/browserOverlay.css b/comm/suite/chatzilla/xul/skin/browserOverlay.css
new file mode 100644
index 0000000000..bd0abf011d
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/browserOverlay.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+#chatzilla-open {
+ list-style-image: url("chrome://chatzilla/skin/images/chatzilla-16.png");
+}
diff --git a/comm/suite/chatzilla/xul/skin/channels.css b/comm/suite/chatzilla/xul/skin/channels.css
new file mode 100644
index 0000000000..6fd6be8506
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/channels.css
@@ -0,0 +1,24 @@
+/* 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/. */
+
+#rightPanel {
+ width: 12em;
+}
+
+#bottomPanel {
+ width: 50em;
+ height: 20em;
+}
+
+.platform-Mac #rightPanel {
+ width: 18em;
+}
+
+#loadContainer {
+ margin: 0;
+}
+
+#loadBar {
+ -moz-appearance: none;
+}
diff --git a/comm/suite/chatzilla/xul/skin/chatzilla.css b/comm/suite/chatzilla/xul/skin/chatzilla.css
new file mode 100644
index 0000000000..63da06a05c
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/chatzilla.css
@@ -0,0 +1,305 @@
+/* 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/. */
+
+@import url(chrome://communicator/skin/);
+
+window {
+ width: 640px;
+ height: 480px;
+}
+
+#menu-view-none {
+ display: none;
+}
+
+#header-url:hover {
+ text-decoration: underline;
+}
+
+#outer-box {
+ margin: 5px;
+}
+
+.toolbarbutton-menu-dropmarker {
+ margin-left: 0.5em;
+}
+
+#input-widget,
+#input-widget-multiline {
+ border: thin silver inset;
+}
+
+#button-multiline-expand {
+ list-style-image:url("chrome://chatzilla/skin/images/multiline-expand.png");
+}
+
+#button-multiline-contract {
+ list-style-image:url("chrome://chatzilla/skin/images/multiline-contract.png");
+}
+
+#button-input {
+ list-style-image:url("chrome://chatzilla/skin/images/input-send.png");
+}
+
+.highlight-menu-item:hover {
+ color: white !important;
+ background: darkslategrey !important;
+}
+
+#user-list {
+ margin: 0;
+}
+
+#view-tabs {
+ overflow: hidden;
+}
+
+#views-tbar-spacer {
+ -moz-box-flex: 10000;
+}
+
+#tabs-drop-indicator-bar {
+ position: relative;
+ margin-top: -3px;
+ -moz-margin-start: -5px;
+ height: 3px;
+}
+
+#tabs-drop-indicator {
+ position: relative;
+ margin-right: -11px;
+ margin-bottom: -8px;
+ width: 11px;
+ height: 11px;
+ background: url(images/drop-indicator-bottom.png) 50% 50% no-repeat;
+}
+
+.view-button {
+ /* Box model is 5px 5px 5px 6px in aggregate. Extra pixel on the left is for the separator. */
+ -moz-appearance: none;
+ margin: 5px 0;
+ border: none;
+ border-left: 1px solid ButtonShadow;
+ padding: 0 5px;
+ background: Button;
+ color: ButtonText;
+ font: message-box;
+ text-shadow: none;
+ text-align: center;
+ max-width: 30ex;
+ -moz-box-flex: 1;
+ -moz-box-orient: vertical;
+ -moz-box-align: stretch;
+}
+
+.view-button:first-child {
+ border-left-color: transparent;
+}
+
+.view-button::before {
+ content: " ";
+ display: -moz-box;
+ margin: -5px -5px 0 -5px;
+ height: 4px;
+}
+
+.view-button[state="superfluous"]::before {
+ background-color: darkblue;
+}
+
+.view-button[state="activity"]::before {
+ background-color: darkgreen;
+}
+
+.view-button[state="attention"]::before {
+ background-color: red;
+}
+
+.view-button:hover,
+.view-button[state="current"] {
+ margin: 0 0 0 1px;
+ border-left: none;
+ border-radius: 0 0 6px 6px;
+ padding: 5px;
+ background: Window;
+ color: WindowText;
+}
+
+.view-button[state="current"] + .view-button,
+.view-button:hover + .view-button:not([state="current"]) {
+ border-left-color: transparent;
+}
+
+.view-button[state="current"] {
+ position: relative; /* So it visually appears above the tabs either side. */
+ border: 2px solid ButtonShadow;
+ border-top: none;
+ padding: 5px 3px 3px 3px;
+}
+
+treecol {
+ border: none;
+}
+
+/* ::::: Trees ::::: */
+
+treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+/* The userlist can be in one of two state. In "symbol", the user's
+ * channel mode is shown as a @ or + image, while in "graphic" mode, the
+ * image is one of the LED images.
+ */
+
+/* no mode */
+treechildren::-moz-tree-image {
+ list-style-image: url(chrome://chatzilla/skin/images/no-symbol.png);
+}
+
+#user-list[mode="graphic"] treechildren::-moz-tree-image {
+ list-style-image: url(chrome://chatzilla/skin/images/no-graphic.png);
+}
+
+/* voice */
+treechildren::-moz-tree-image(voice-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/voice-symbol.png);
+}
+
+#user-list[mode="graphic"] treechildren::-moz-tree-image(voice-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/voice-graphic.png);
+}
+
+/* half-chanop */
+treechildren::-moz-tree-image(halfop-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/halfop-symbol.png);
+}
+
+#user-list[mode="graphic"] treechildren::-moz-tree-image(halfop-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/halfop-graphic.png);
+}
+
+/* chanop */
+treechildren::-moz-tree-image(op-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/op-symbol.png);
+}
+
+#user-list[mode="graphic"] treechildren::-moz-tree-image(op-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/op-graphic.png);
+}
+
+/* admin */
+treechildren::-moz-tree-image(admin-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/admin-symbol.png);
+}
+
+#user-list[mode="graphic"] treechildren::-moz-tree-image(admin-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/admin-graphic.png);
+}
+
+/* founder */
+treechildren::-moz-tree-image(founder-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/founder-symbol.png);
+}
+
+#user-list[mode="graphic"] treechildren::-moz-tree-image(founder-true) {
+ list-style-image: url(chrome://chatzilla/skin/images/founder-graphic.png);
+}
+
+/* away */
+treechildren::-moz-tree-cell-text(away-true) {
+ color: GrayText;
+ font-style: italic;
+}
+
+treechildren::-moz-tree-cell-text(away-false) {
+}
+
+menuitem[header="true"] {
+ text-align: center;
+}
+
+.colorGrid
+{
+ width: 24px;
+ height: 16px;
+ border: 1px solid black;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+}
+
+#colorTooltip
+{
+ padding: 0px;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+}
+
+/* Feel the hacks. */
+progressmeter[mode="undetermined"] {
+ -moz-appearance: none;
+}
+
+[dir="ltr"] {
+ direction: ltr;
+}
+
+[dir="rtl"] {
+ direction: rtl;
+}
+
+#input-splitter {
+ margin: 4px 0 0 0;
+}
+
+#input-widgets {
+ margin: 4px 0;
+}
+
+#server-nick, #button-multiline-expand, #button-input, #button-multiline-contract {
+ margin: 0;
+}
+
+#input, #multiline-input {
+ margin: 0 4px;
+}
+
+/* Hack; Stop the status-bar from distorting without a security icon */
+#status-text {
+ min-height: 17px;
+}
+
+#status-text[notice="true"] {
+ font-weight: bold;
+}
+
+#security-button {
+ min-width: 20px;
+}
+
+#security-button:not([level="none"]):not([level="high"]):not([level="broken"]) {
+ display: none;
+}
+
+#alert-status[alertstate="off"] {
+ list-style-image: url("chrome://chatzilla/skin/images/spbubble-off.png");
+}
+
+#alert-status[alertstate="on"] {
+ list-style-image: url("chrome://chatzilla/skin/images/spbubble-on.png");
+}
+
+#logging-status[loggingstate="off"] {
+ list-style-image: url("chrome://chatzilla/skin/images/logging-off.png");
+}
+
+#logging-status[loggingstate="on"] {
+ list-style-image: url("chrome://chatzilla/skin/images/logging-on.png");
+}
+
+/* Focus styling for a11y reasons */
+#user-list-box[focusobvious="true"], #browser-box[focusobvious="true"],
+#multiline-hug-box[focusobvious="true"], #singleline-hug-box[focusobvious="true"] {
+ outline: 2px solid highlight;
+}
diff --git a/comm/suite/chatzilla/xul/skin/chatzillaOverlay.css b/comm/suite/chatzilla/xul/skin/chatzillaOverlay.css
new file mode 100644
index 0000000000..fef849df97
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/chatzillaOverlay.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+#mini-irc, #tasksMenuIRC {
+ list-style-image: url("chrome://chatzilla/skin/images/chatzilla-16.png");
+}
diff --git a/comm/suite/chatzilla/xul/skin/images/admin-graphic.png b/comm/suite/chatzilla/xul/skin/images/admin-graphic.png
new file mode 100644
index 0000000000..5f6c75ba82
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/admin-graphic.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/admin-symbol.png b/comm/suite/chatzilla/xul/skin/images/admin-symbol.png
new file mode 100644
index 0000000000..11da8ea98a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/admin-symbol.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/arrow-down.png b/comm/suite/chatzilla/xul/skin/images/arrow-down.png
new file mode 100644
index 0000000000..6910fd19e6
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/arrow-down.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/chatzilla-16.png b/comm/suite/chatzilla/xul/skin/images/chatzilla-16.png
new file mode 100644
index 0000000000..3f462580a0
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/chatzilla-16.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/drop-indicator-bottom.png b/comm/suite/chatzilla/xul/skin/images/drop-indicator-bottom.png
new file mode 100644
index 0000000000..4196235cd3
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/drop-indicator-bottom.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/founder-graphic.png b/comm/suite/chatzilla/xul/skin/images/founder-graphic.png
new file mode 100644
index 0000000000..2328611847
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/founder-graphic.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/founder-symbol.png b/comm/suite/chatzilla/xul/skin/images/founder-symbol.png
new file mode 100644
index 0000000000..842230a849
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/founder-symbol.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/halfop-graphic.png b/comm/suite/chatzilla/xul/skin/images/halfop-graphic.png
new file mode 100644
index 0000000000..bc84fe7728
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/halfop-graphic.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/halfop-symbol.png b/comm/suite/chatzilla/xul/skin/images/halfop-symbol.png
new file mode 100644
index 0000000000..ca5654cd8e
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/halfop-symbol.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/input-send.png b/comm/suite/chatzilla/xul/skin/images/input-send.png
new file mode 100644
index 0000000000..fc1213f34a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/input-send.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/logging-off.png b/comm/suite/chatzilla/xul/skin/images/logging-off.png
new file mode 100644
index 0000000000..e9f32a18ad
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/logging-off.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/logging-on.png b/comm/suite/chatzilla/xul/skin/images/logging-on.png
new file mode 100644
index 0000000000..349e23be6a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/logging-on.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/logo.png b/comm/suite/chatzilla/xul/skin/images/logo.png
new file mode 100644
index 0000000000..f963f2c46c
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/logo.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/multiline-contract.png b/comm/suite/chatzilla/xul/skin/images/multiline-contract.png
new file mode 100644
index 0000000000..7ae5acd1ec
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/multiline-contract.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/multiline-expand.png b/comm/suite/chatzilla/xul/skin/images/multiline-expand.png
new file mode 100644
index 0000000000..0ada94b4ef
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/multiline-expand.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/no-graphic.png b/comm/suite/chatzilla/xul/skin/images/no-graphic.png
new file mode 100644
index 0000000000..cb27b2df0a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/no-graphic.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/no-symbol.png b/comm/suite/chatzilla/xul/skin/images/no-symbol.png
new file mode 100644
index 0000000000..567ec2abf0
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/no-symbol.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/op-graphic.png b/comm/suite/chatzilla/xul/skin/images/op-graphic.png
new file mode 100644
index 0000000000..99a6c47ff8
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/op-graphic.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/op-symbol.png b/comm/suite/chatzilla/xul/skin/images/op-symbol.png
new file mode 100644
index 0000000000..84de14972c
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/op-symbol.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/source_png/spbubble-off.png b/comm/suite/chatzilla/xul/skin/images/source_png/spbubble-off.png
new file mode 100644
index 0000000000..0dde51eeaf
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/source_png/spbubble-off.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/source_png/spbubble-on.png b/comm/suite/chatzilla/xul/skin/images/source_png/spbubble-on.png
new file mode 100644
index 0000000000..009a3d1f7b
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/source_png/spbubble-on.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/source_svg/logging.svg b/comm/suite/chatzilla/xul/skin/images/source_svg/logging.svg
new file mode 100644
index 0000000000..b472e0bcf6
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/source_svg/logging.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="320"
+ height="160"
+ id="svg2">
+ <defs
+ id="defs4" />
+ <g
+ transform="translate(0,-892.36218)"
+ id="layer1">
+ <path
+ d="M 95.147225,909.3621 133.18775,947.33552 125,957.36218 l -40,0 0,-40 10.147225,-8.00008 z"
+ id="rect2818-1"
+ style="fill:#dedede;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 25,907.36218 60,0 c 20,0 10,10 10,40 30,0 40,-10 40,10 l 0,90.00002 -110,0 0,-140.00002 z"
+ id="rect2818"
+ style="fill:#dedede;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 45,1027.3622 70,0"
+ id="path3615"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 45,1007.3622 70,0"
+ id="path3615-7"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 45,987.36218 70,0"
+ id="path3615-0"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 45,967.36218 70,0"
+ id="path3615-4"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 45,947.36218 70,0"
+ id="path3615-4-5"
+ style="fill:none;stroke:#000000;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 45,927.36218 50,0"
+ id="path3615-4-7"
+ style="fill:none;stroke:#000000;stroke-width:8.16496563;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 255.14722,909.3621 38.04053,37.97342 -8.18775,10.02666 -40,0 0,-40 10.14722,-8.00008 z"
+ id="rect2818-1-9"
+ style="fill:#dedede;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 185,907.36218 60,0 c 20,0 10,10 10,40 30,0 40,-10 40,10 l 0,90.00002 -110,0 0,-140.00002 z"
+ id="rect2818-9"
+ style="fill:#dedede;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+</svg>
diff --git a/comm/suite/chatzilla/xul/skin/images/source_svg/userlist_icons.svg b/comm/suite/chatzilla/xul/skin/images/source_svg/userlist_icons.svg
new file mode 100644
index 0000000000..a5b507228c
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/source_svg/userlist_icons.svg
@@ -0,0 +1,636 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- 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/. -->
+
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.0"
+ width="48"
+ height="72"
+ id="svg4597">
+ <defs
+ id="defs4599">
+ <linearGradient
+ x1="3.932596"
+ y1="4.0576153"
+ x2="11.188456"
+ y2="12.377568"
+ id="linearGradient6792"
+ xlink:href="#linearGradient4491"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.8333333,0,0,0.8333333,41.811051,54.89456)" />
+ <linearGradient
+ id="linearGradient4491">
+ <stop
+ id="stop4493"
+ style="stop-color:#008000;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4495"
+ style="stop-color:#008000;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="3.932596"
+ y1="4.0576153"
+ x2="11.188456"
+ y2="12.377568"
+ id="linearGradient4678"
+ xlink:href="#linearGradient4491"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.8333333,0,0,0.8333333,41.811051,54.89456)" />
+ <filter
+ id="filter3268">
+ <feGaussianBlur
+ inkscape:collect="always"
+ id="feGaussianBlur3270"
+ stdDeviation="0.17400778" />
+ </filter>
+ <linearGradient
+ x1="5.8113861"
+ y1="12.271072"
+ x2="11.031619"
+ y2="7.3054457"
+ id="linearGradient5639"
+ xlink:href="#linearGradient5118"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient5118">
+ <stop
+ id="stop5120"
+ style="stop-color:#808080;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop5126"
+ style="stop-color:#b6b6b6;stop-opacity:1"
+ offset="1" />
+ <stop
+ id="stop5122"
+ style="stop-color:#808080;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="1.3302127"
+ y1="7.4045763"
+ x2="4.4953356"
+ y2="6.201149"
+ id="linearGradient5641"
+ xlink:href="#linearGradient3308"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3308">
+ <stop
+ id="stop3310"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3312"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <filter
+ id="filter3362">
+ <feGaussianBlur
+ inkscape:collect="always"
+ id="feGaussianBlur3364"
+ stdDeviation="0.05131204" />
+ </filter>
+ <linearGradient
+ x1="5.7836757"
+ y1="2.3257139"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient5643"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3176">
+ <stop
+ id="stop3178"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3180"
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <filter
+ id="filter3290">
+ <feGaussianBlur
+ inkscape:collect="always"
+ id="feGaussianBlur3292"
+ stdDeviation="0.040874578" />
+ </filter>
+ <linearGradient
+ x1="5.8113861"
+ y1="12.520331"
+ x2="10.529106"
+ y2="7.3054457"
+ id="linearGradient5645"
+ xlink:href="#linearGradient5042"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient5042">
+ <stop
+ id="stop5044"
+ style="stop-color:#0066ff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop5050"
+ style="stop-color:#22a0ff;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="1.3302127"
+ y1="7.4045763"
+ x2="4.4953356"
+ y2="6.201149"
+ id="linearGradient5647"
+ xlink:href="#linearGradient3308"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.7836757"
+ y1="2.3257139"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient5649"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.8113861"
+ y1="12.520331"
+ x2="11.026272"
+ y2="7.3054457"
+ id="linearGradient5651"
+ xlink:href="#linearGradient5221"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient5221">
+ <stop
+ id="stop5223"
+ style="stop-color:#00c800;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop5229"
+ style="stop-color:#2fff12;stop-opacity:1"
+ offset="1" />
+ <stop
+ id="stop5225"
+ style="stop-color:#00de00;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="1.3302127"
+ y1="7.4045763"
+ x2="4.4953356"
+ y2="6.201149"
+ id="linearGradient5653"
+ xlink:href="#linearGradient3308"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.7836757"
+ y1="2.3257139"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient5655"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="6.3511858"
+ y1="12.54519"
+ x2="11.05113"
+ y2="3.82529"
+ id="linearGradient5657"
+ xlink:href="#linearGradient4778"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient4778">
+ <stop
+ id="stop4780"
+ style="stop-color:#ff7700;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4786"
+ style="stop-color:#ff9600;stop-opacity:1"
+ offset="0.96214288" />
+ <stop
+ id="stop4782"
+ style="stop-color:#ff7700;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="1.3302127"
+ y1="7.4045763"
+ x2="4.4953356"
+ y2="6.201149"
+ id="linearGradient5659"
+ xlink:href="#linearGradient3308"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.766274"
+ y1="2.4264779"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient5661"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="6.0393691"
+ y1="12.525679"
+ x2="10.796444"
+ y2="7.3054457"
+ id="linearGradient5663"
+ xlink:href="#linearGradient3141"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3141">
+ <stop
+ id="stop3143"
+ style="stop-color:#7c0063;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3149"
+ style="stop-color:#a60084;stop-opacity:1"
+ offset="1" />
+ <stop
+ id="stop3145"
+ style="stop-color:#7c0063;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="1.3302127"
+ y1="7.4045763"
+ x2="4.4953356"
+ y2="6.201149"
+ id="linearGradient5665"
+ xlink:href="#linearGradient3308"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.7836757"
+ y1="2.3257139"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient5667"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.8113861"
+ y1="12.520331"
+ x2="11.026272"
+ y2="7.3054457"
+ id="linearGradient5669"
+ xlink:href="#linearGradient5337-52-886-410"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient5337-52-886-410">
+ <stop
+ id="stop4558"
+ style="stop-color:#005e00;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4560"
+ style="stop-color:#058c00;stop-opacity:1"
+ offset="1" />
+ <stop
+ id="stop4562"
+ style="stop-color:#005e00;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="1.3302127"
+ y1="7.4045763"
+ x2="4.4953356"
+ y2="6.201149"
+ id="linearGradient5671"
+ xlink:href="#linearGradient3308"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.7836757"
+ y1="2.3257139"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient5673"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="5.7836757"
+ y1="2.3257139"
+ x2="5.6800237"
+ y2="5.6109982"
+ id="linearGradient6033"
+ xlink:href="#linearGradient3176"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <g
+ id="layer2">
+ <rect
+ width="48"
+ height="72"
+ x="0"
+ y="1.4988011e-15"
+ id="rect4768"
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:35.43307114;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="layer1">
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHDSURB VCiRVZE9aFNhGIXPe7+bm6TXUlNM/KPUQtB2U1pw08WC+LModLBLpYuo2Tq4mHApCI7SRUGEgqmK Crp3KG6KWG0Q2iFoKhjvtalpk96f3O9+r0NISJ/Dmc573uUQM6PDq6nHxtCZkZv1NSfru37Fp+D1 jXd3bfRAncLKvfd36l+dXLjRPDk2O0Hm8EE0a7vhx+KHYuVzeXaenyoA0ABgee7t7R8vSw/t5Z+n jk6OkNIY0IDD40Oxi/evz6RPH/sGgABAnNfGE/anX4u11eoRBkCkQQURIk+C/QjGQAL9UV9mceHZ 2LnpC2/0vkFzZnNlIysgAAb+fKkg+Oci2Pa6pj0FY4suWZZ1SK+VndGWlKSD21LAdtmBZzfgOQ24 1QZ2Vh0YA2S6wJSupNqUkNgPg5suWqXfqJVs6BAIjzOUUnEt3p94EU+bXgiJtiPIrtsKEcHPMIQQ dW3ywbWqkUk+B9A96i2HiFA/EXA4Yawzc1EDgN3vW7com1iDTj3f2+W9Ycn+5aQrYmKhUCi0qHfp /JW5JfyVV9GITC0pgHQMdPbAOgl6lM/nn+xbuoNlWYNENM3Mgoh2UqnUUi6XCzr5f4P/6zdUCtoG AAAAAElFTkSuQmCC "
+ x="12"
+ y="0"
+ width="12"
+ height="12"
+ id="image4146"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGzSURB VCiRZZFPaxNRFMV/782kqWltCmmqYCwJuCqCEPoFBCtIdyKYRcEvIPhvIwgZZhVwoxZcdOWqC4UW 0V0XfgBbqGCqFarSQuMkxJjGTGY6M2+eCzsY9cDhXrj87lkcobUmUeP+XHasVKhId7cQeWHTbUav Ckuf9xmSSIDDxxdvp/XXW+lJsyjKd2BihqjXihvryy/Vxkal9EKHABKg93Th7ni4+XBUO0Vx9jIE fQgHmNOzcqZSu9o/X962bVsCGDfPONkTvbfPRlQzh45BCFA+hB4oD1Lj5LLkfuxsXZi9tPjcTEXt 62nvSxED0BoONsHrgPsdvN82ww55oz1v2/ZpU/QbJaEBBWggjqG9C71v4DrQ24fWFlk5lQGumcoP D1CAOAaS6bvg1KFVBwH9uEgcxykZyZOv3aNMgDpOSRz/2XUEjprWQFfmH7zZc4L8qo7+AYb8sVvU 9dG5HSnligSQjb0b7wbnPhwF5n+ftw9Len1kwUXIR5ZlBWKoabF678raFJ35Cf0zMxBjOPKUfp8p f5KG+aRarS7/1XSiWq2W831/0TAMqZTqSilXLMsKkvsvIAPVEw1F5a4AAAAASUVORK5CYII= "
+ x="12"
+ y="12"
+ width="12"
+ height="12"
+ id="image4189"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHUSURB VCiRdZFPaxNRFMXPezNTdHQS61jpopUUqiIoEoiCuhAUqYWCG5EiSvwI4gfIZJeFi9KlCApKF/6p G7ELkUGKElottoK1pppx1ISEahibTpjOy3vXjdG48GzugXN/i3sPIyJ0lLmZSe47kBqvmt5AK4jr wddwppQtl9EtIgIR4ax7+mrm/X5vbOM4uXKGXtNLehDekSPuiWnkYXT2QEQ4545dS63a8d76Lrq/ eZfeygX6pEpUpyq9oTk69eRYKZ/PcyKCVjtSSy4mF27/sNZsBQmNcUQsQg8zoDMdSdYLNhjbxZW5 w+NHL97Thq6ksvM7ipcZA4gBZbmKFjbQYiEkk9jOEqjon7HoLw16D7/d0iuyOtRuK4D/vetVax6e /Ah/q4eS8Q6z7ClEr2YCOK+LSFSEIACq6xUc39HAc/UML3pcGAbHnvWDUEoZ3Iqtx+ZPMxaC0I4V hFC/J/3x8aZComYTgIC7F1y/r9I3DQL+ByVm++nQWnqFcz7FAcD/4meHl4aX9VD/F4oUdhYH6OTy SMgZn3AcJ2ZdTbPR66OPGlbjTHNL07Ta27C72U/pIP1B1/TJXC53AwC6AQBAoVCwoyi6pGkal1IG nPMpx3HiTv4Lj1QMG8iNFZwAAAAASUVORK5CYII= "
+ x="12"
+ y="24"
+ width="12"
+ height="12"
+ id="image4274"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAG4SURB VCiRVZExixpRFIXPez7fDDvgIII2BhJsQgqXYOEPkPgLUmwgso2dRUJ+gNNprEIKizQJCAnEIkQW 8gNstbCLIRJtAgqKi77MjG+cuSnCDJsPLqe458C9HEZEiGk0GnalUrkiouJ+v9+sVqtvg8HgF+7A 4kCn03lp2/aLUql0v1wuwzAMbLfbaDQafR0Oh1fT6TRIAr1e75VlWa9t205Xq1Vks1lIKSGlhOd5 6Pf7P6Moeug4TpQyDMNmjH2QUuYAgHMOIgLnHIwxSClRKBRyk8nksl6vf07VarVrImrExuPxiPP5 jCiKQEQQQiAIAszn83vj8fi9cF33gWEY0Fr/e4oxrNdrKKWglMLhcMBut4NlWRe+7z8Vnuf9FkIk 5hilFLTW2Gw2yS9ElOZhGN4opXQYhtBaIwiCROM5nU5wXZcA3DIiQrPZ/FQsFp8xxiCEQDqdhpQy 0cViQaZpfuecPxYAMJvNronoMp/PPzJNMzkviiIsl0sSQvzhnL9xHEezO02zVqv1hXP+hDF2wTkH AMpkMj+EEG/b7fa7/5qO6Xa7Od/3n6dSKR6G4S3n/KPjODre/wWkwN2ZHNKyngAAAABJRU5ErkJg gg== "
+ x="12"
+ y="48"
+ width="12"
+ height="12"
+ id="image4317"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHJSURB VCiRVZG/axNxGMY/971cbNJK2oZEW6IoZhAFq1CdXKoUcaqDoBWhq1MR/4BcM6WbiJOIOEgGhzgo VBDtIDhIJO1gNcVijbGQ1Dbmp7mcd/c6mGD6wPO+L7yfZ3h5NRGhp8nJh6H41Inr391AzNquluvr xaWN7NxX+iUiiAiXbr27fTpR2DyfduXRR5GX30SefGg6U/PLGVgwepwmIszMv7+TGz25yMiQMXsK DoUgFoLxQWg7YC4ufbkQyR43TdPTS6WzoVV//PFuMBL2BDQNbA86LnQ8GPSDMRYL597mJ2Znzj31 7erD1zbV+BHdBhFYKUKtA9U2/LKgZkNDD1AbiE4nk8mDvq3WvqOOoSMdoHv/Rhm2m/CzDaUmfK6B f2A0CFz1/ak3t5zgf1i6pQ6s2ZDfAZ8fjjkNPM8z1H6n+iJQ+WE7Njg2uJ1/3bHB7dppewxbJQGq avn5lUJkJ5cRx9sL980jq69lYqycV0qlFUDhzcpcvJj5pLcqe2HLI7r2Si6Gsy2ltLumadpa36e1 yzfuP6t40emGFwoOGb85YJTkzOHyus+n30skEg8A+gMApFKpsGVZN3VdV67rVpVSadM07d7+L8s2 8Sj9Mf2NAAAAAElFTkSuQmCC "
+ x="12"
+ y="36"
+ width="12"
+ height="12"
+ id="image4360"
+ style="display:inline" />
+ <text
+ x="25.851986"
+ y="22.37845"
+ id="text6605"
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#ff7f2a;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;font-family:FranklinGotTDemCon"><tspan
+ x="25.851986"
+ y="22.37845"
+ id="tspan6607"
+ style="fill:#ff7f2a;stroke:#4d4d4d">&amp;</tspan></text>
+ <text
+ x="25.371283"
+ y="11.332599"
+ id="text6690"
+ xml:space="preserve"
+ style="font-size:15.14527416px;font-style:normal;font-weight:normal;fill:#ff00ff;fill-opacity:1;stroke:#800080;stroke-width:1.26210618px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline;font-family:FranklinGotTDemCon"><tspan
+ x="25.371283"
+ y="11.332599"
+ id="tspan6692"
+ style="stroke-width:1.26210618">~</tspan></text>
+ <text
+ x="23.484241"
+ y="32.947613"
+ id="text6738"
+ xml:space="preserve"
+ style="font-size:10.52353096px;font-style:normal;font-weight:normal;fill:#00ff00;fill-opacity:1;stroke:#4d4d4d;stroke-width:0.0876961;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;font-family:FranklinGotTDemCon"><tspan
+ x="23.484241"
+ y="32.947613"
+ id="tspan6740"
+ style="fill:#00ff00;stroke:#4d4d4d;stroke-width:0.0876961;stroke-miterlimit:4;stroke-dasharray:none">@</tspan></text>
+ <text
+ x="24.187044"
+ y="46.661697"
+ id="text6788"
+ xml:space="preserve"
+ style="font-size:12.85570812px;font-style:normal;font-weight:normal;fill:#008dff;fill-opacity:1;stroke:url(#linearGradient6792);stroke-width:0.10713092;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;font-family:FranklinGotTDemCon"><tspan
+ x="24.187044"
+ y="46.661697"
+ id="tspan6790"
+ style="fill:#008dff;fill-opacity:1;stroke:url(#linearGradient6792);stroke-width:0.10713092;stroke-miterlimit:4;stroke-dasharray:none">%</tspan></text>
+ <text
+ x="22.939159"
+ y="62.14077"
+ id="text6845"
+ xml:space="preserve"
+ style="font-size:22.86951828px;font-style:normal;font-weight:normal;fill:#00ff00;fill-opacity:1;stroke:#e3dbdb;stroke-width:0.19057931;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;font-family:FranklinGotTDemCon"><tspan
+ x="22.939159"
+ y="62.14077"
+ id="tspan6847"
+ style="fill:#808080;stroke:#e3dbdb;stroke-width:0.19057931;stroke-miterlimit:4;stroke-dasharray:none">+</tspan></text>
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADCSURB VCiRzZGhCsIAFEXPmxvDKAYtNoMg6A+YBD9C7P7GWBgD60DBYPUP/AQx2PwFlYGwNHEbY8/gBMVk ELzwyuUe7oUnqso3Mr5K/xTwxa8DmE/DFbcDTARpAQdF144650ACOyJaAHtgbpbhoYGxsbCsGjW5 cs1i4pkn3lbRNtAsgUeDIN2Cwk5JCQmpUpUGDSMhGeTkxMQA+dskQY6KjitUTjduo4xsqmhPkAsQ AKvXhl1B0XfUiUp+Wd6H5P8edwdEdUHuCYIzqwAAAABJRU5ErkJggg== "
+ x="36"
+ y="0"
+ width="12"
+ height="12"
+ id="image8696"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFKSURB VCiRY/z//z8DKYAFXSA9PZ1V89ct73u/OfdNWrLtE7o8I7INjTEuWp6in9YzMf7/wMn0X/zwO+6k jHkH9yFrYELmWAl8jmNj+v/ApO+0+bd/TEe1eX+EotuAouHzX+b7oqx/jKcnOuQKsv62ePKD9Sxe DTuf/lr8/CfLm0TZt5MOveNbFDnzyBycfuhIdFTwEPqy6utfxg+CLH+l3/9huX/iA3e9MtfPqGVv pGtWrVr1HSWUdLh+prMw/mNY9N3E2/DvbTM/0Xcb/vxjWMDL+u8dTDGKk/hY/3H9Y2D6PnPmzN8Z c/Yd3fuWb5q90BedR9/ZLmJ1Um+ii2mkxJs997+z7f7xl+mWItfP2Mff2Z9p8n7X2fqKvyRx9qHp KDYUz99zetdr3nAWRsavvCz/FY594G2zn3TCfPtr/p7f/xjvYthALAAALLyYRtlkYjwAAAAASUVO RK5CYII= "
+ x="36"
+ y="12"
+ width="12"
+ height="12"
+ id="image8739"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHVSURB VCiRY/z//z8DKYAFmSO7U9bjo8bHNMZ/jBqs31l//OH8c4/rGdeeZ9bPZsDUMMEYEqcl8j5qfOzk esm1WmGngkPu6lwT0TOik38J/PIWuyDWBlPH+P//fwbVTaqGL/VfLhA9IRr6RemL2W/e38kM/xke cb7jfMj5ivPaO513Cfw3+Kff972/kYmBgYHhvfL7CN7HvDO/y3zX+SH2o1B6n3QW2xe2Gy9NX1az fWV7y/GWY+0n5U/WcCf94fhjJHhJ8NhX6a/J3E+5J1zOunxdaaPSIo63HJ8UTikcYv7O/PU/y39+ uAaW7ywSHI847v9j/6chfE74OgMDA8Mz+2dmTL+ZLm2btO3nV+mv7pwvOE8ibOD6c+ut3VsHxl+M F19ZvArTWqpl8VbrbRPrV9a34mfFm5h+Mkml7U9bANfA/ZJ7/0fVjxEK+xWqvgt/V3xh+iJP7LzY 1N88v4X/cfzjFj4vXL3QdGE0AwMDAwM04hhFLoms5LvHt0VrsZZtaGgo2////xnMO81lZPbKpPA+ 5L0teVjS4////5BgZWBgYGBkZGSUOSgT9V7pfRz7J3ZJ5t/M/35z//7L9JvpvNhpsa7rcddvweMB G2BkZGT8j0USAHiWxx8oaaFMAAAAAElFTkSuQmCC "
+ x="36"
+ y="24"
+ width="12"
+ height="12"
+ id="image8782"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFeSURB VCiRlZDLK4RxFIaf8/1myi1JSRZyaSxFiWRDKEWyGkWSPwCFMdloTLkTwkbKhiTsrJSFRllZuqdE LCyUBSLzfcdiXGZYyLs5i3N730dUlf/IApDJmkQZ322X8VCPjHSmfTYl2JUtY+vzgkjMAvTPgZ0P z7m4Wqa/ziU0+LBSb5RvG65ISavgbakR5+GVeN+2bIjh0u/Baq7iaagYqmMtoW/XmIxk3JnpYN+q V22kzo99u6iB9cfoDJEPerKJqRhBwopzsSzDy4W4vaXc9bVDbUxo+aQkwW4PgAamLmRibxW93yd8 FcJd2gP6jIZm1O8/k59YZSxYhqlf4Km1iISVLeyDEJIUh8nLVF9Rm+sXaKnqhfNZOHKwkkoI73Rg pyRjytaisH7Mjg5WY8VnkdW0pAEN47wcQYEHU5ADr6dRWD9kynuxD2fUq3YExvEipnIYxME5H4A8 fmX4S+9JCYDPfaR61AAAAABJRU5ErkJggg== "
+ x="36"
+ y="36"
+ width="12"
+ height="12"
+ id="image8825"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADVSURB VCiRY/z//z8DCjhwgGXnly9RDAwMDO4+PksZGBj+IkszMaCBN5KSnNdu3Zpx7datac+fP2dHl8fQ QAjQXgPj+tWri1+8ehUJN4GRkeH1u3eGjAwM/4WEhC4wIAWKuLj4SpZvP37wv3z92hibaS9fvUIR 5+Xm3sV4bP9+ld+/f6vBBFlYWDiPnz27hImJ6b+psXH0v1+/fsLk2NnZbzOix8Obmzd5F2/d+pKB geFfRGSkmKSk5DeKPE2yBhZ0gR/c3H/ERUW3MzIy/v/69etfdHkAxMtOtq4bGL4AAAAASUVORK5C YII= "
+ x="36"
+ y="48"
+ width="12"
+ height="12"
+ id="image8868"
+ style="display:inline" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAXSURBVCiRY/z//z8DKYCJJNWjGkaQBgD1WAMVqVdojQAAAABJRU5ErkJggg== "
+ x="36"
+ y="60"
+ width="12"
+ height="12"
+ id="image4763" />
+ <image
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz AAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHbSURB VCiRVZGxaxNRAId/7717d00T6Uk1pInGWqSQ4iS4NIMhoOhcsFPBTf+ADqEmJhBQHN1cIrhLEQQj aEFLoZApFCXaWtGAtl4TrpdLLrnL3XsdNBK/5bd83/QjUkr8hWbvZ6/3ae8mDdHZaT5tWGbn5YeH m1WMQaSU2KpvxVZfrD7ao5+XF1PpUHYui1R0Ac12U6xX17cTrXO3K08qvwCAAmD3nt998FHs3FE0 HtK5Dk5VGPZvzEfn6crSStqKHb8rFAoXAYBtHG9c2xdfH/eFo/rCR4AAlmthEAygMAUhPokdp36m vW+GG7XGW8Uj3q2u3w0HQkBIF7sHu7AGFlpOC+bARGIqgR7vEaq5SwDWlI7TmYMgAAMkABcumkdN mH0Th/YBwpMR7FlfsCAunwKQUSIscig9CSkAgj8RAWB3bdiuDUVlCFgAXeoDIcRZSlzymgd8CB+Q Q2B8MQR8L4DW1ZBUZo8AfKO1Sq0a9aJvgP/l8eiKcxUzEzOblNJPFADiMr6cEqm6GqhyXFYdDYvd tMyczmwDeFoqlX6S0dP5fP58o91YM4bGDY94MZ3rE8lQshOPxF8JIZ6Vy+X3/54ekcvlphhjFyil lzjnzPO8H5zz78Vi0Rg5J1kK4/V0dy9HAAAAAElFTkSuQmCC "
+ x="12"
+ y="60"
+ width="12"
+ height="12"
+ id="image5815" />
+ <g
+ transform="translate(20,5e-7)"
+ id="g4788"
+ style="display:inline">
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9638661,0.1841601,-0.1841601,0.963866,-17.87869,58.359647)"
+ id="path3043"
+ style="fill:#808080;fill-opacity:1;stroke:#808080;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3268)" />
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9814535,0,0,0.9814535,-20.058185,58.569471)"
+ id="path3045"
+ style="fill:url(#linearGradient5669);fill-opacity:1;stroke:#003500;stroke-width:0.24858253;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.856926,7.3054457 a 5.0455399,5.0905943 0 1 1 -10.09107975,0 5.0455399,5.0905943 0 1 1 10.09107975,0 z"
+ transform="matrix(0.5121728,0.8599464,-0.8599464,0.5121728,-11.048722,57.000296)"
+ id="path3047"
+ style="fill:none;stroke:url(#linearGradient5671);stroke-width:0.10042476;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3362)" />
+ <path
+ d="m 3.0290077,4.1219881 c 0.0244451,-1.006404 1.4328056,-1.7962742 2.8378096,-1.7962742 1.405004,0 2.8555192,0.7621386 2.8788196,1.796285 0.0233005,1.0341464 -1.5961005,1.5010801 -2.9688013,1.4887622 -1.3046875,-0.0117076 -2.772273,-0.482369 -2.7478279,-1.488773 z"
+ transform="matrix(0.9498646,0.5484046,-0.683379,1.1836471,-16.039471,55.616584)"
+ id="path3049"
+ style="fill:url(#linearGradient6033);fill-opacity:1;stroke:none;filter:url(#filter3290)" />
+ </g>
+ <g
+ transform="matrix(0.9705075,0,0,0.9705075,-0.146091,-0.1561012)"
+ id="g4816"
+ style="display:inline">
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9876803,0.1887101,-0.1887101,0.9876802,2.38882,-1.5621111)"
+ id="path4818"
+ style="fill:#808080;fill-opacity:1;stroke:#808080;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3268)" />
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(1.0057022,0,0,1.0057022,0.1554762,-1.3471028)"
+ id="path4820"
+ style="fill:url(#linearGradient5663);fill-opacity:1;stroke:#7c0063;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.856926,7.3054457 a 5.0455399,5.0905943 0 1 1 -10.09107975,0 5.0455399,5.0905943 0 1 1 10.09107975,0 z"
+ transform="matrix(0.524827,0.881193,-0.881193,0.524827,9.3875353,-2.9550479)"
+ id="path4822"
+ style="fill:none;stroke:url(#linearGradient5665);stroke-width:0.24374942;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3362)" />
+ <path
+ d="m 3.0290077,4.1219881 c 0.0244451,-1.006404 1.4328056,-1.7962742 2.8378096,-1.7962742 1.405004,0 2.8555192,0.7621386 2.8788196,1.796285 0.0233005,1.0341464 -1.5961005,1.5010801 -2.9688013,1.4887622 -1.3046875,-0.0117076 -2.772273,-0.482369 -2.7478279,-1.488773 z"
+ transform="matrix(0.9733328,0.561954,-0.7002632,1.2128914,4.2734795,-4.3729471)"
+ id="path4824"
+ style="fill:url(#linearGradient5667);fill-opacity:1;stroke:none;filter:url(#filter3290)" />
+ </g>
+ <g
+ transform="matrix(0.9758889,0,0,0.9758889,-0.209913,11.88922)"
+ id="g4826"
+ style="display:inline">
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9876803,0.1887101,-0.1887101,0.9876802,2.3888195,-1.5621111)"
+ id="path4828"
+ style="fill:#808080;fill-opacity:1;stroke:#808080;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3268)" />
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(1.0057022,0,0,1.0057022,0.1554762,-1.3471028)"
+ id="path4830"
+ style="fill:url(#linearGradient5657);fill-opacity:1;stroke:#de5500;stroke-width:0.24858253;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.856926,7.3054457 a 5.0455399,5.0905943 0 1 1 -10.09107975,0 5.0455399,5.0905943 0 1 1 10.09107975,0 z"
+ transform="matrix(0.524827,0.881193,-0.881193,0.524827,9.3875353,-2.9550479)"
+ id="path4832"
+ style="fill:none;stroke:url(#linearGradient5659);stroke-width:0.10068177;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3362)" />
+ <path
+ d="m 3.0290077,4.1219881 c 0.0244451,-1.006404 1.4328056,-1.7962742 2.8378096,-1.7962742 1.405004,0 2.8555192,0.7621386 2.8788196,1.796285 0.0233005,1.0341464 -1.5961005,1.5010801 -2.9688013,1.4887622 -1.3046875,-0.0117076 -2.772273,-0.482369 -2.7478279,-1.488773 z"
+ transform="matrix(0.9733328,0.561954,-0.7002632,1.2128914,4.2734795,-4.3729471)"
+ id="path4834"
+ style="fill:url(#linearGradient5661);fill-opacity:1;stroke:none;filter:url(#filter3290)" />
+ </g>
+ <g
+ transform="matrix(0.9758888,0,0,0.9758888,-0.209912,23.998424)"
+ id="g4836"
+ style="display:inline">
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9876803,0.1887101,-0.1887101,0.9876802,2.3888195,-1.5621111)"
+ id="path4838"
+ style="fill:#808080;fill-opacity:1;stroke:#808080;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3268)" />
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(1.0057022,0,0,1.0057022,0.1554762,-1.3471028)"
+ id="path4840"
+ style="fill:url(#linearGradient5651);fill-opacity:1;stroke:#008000;stroke-width:0.24858253;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.856926,7.3054457 a 5.0455399,5.0905943 0 1 1 -10.09107975,0 5.0455399,5.0905943 0 1 1 10.09107975,0 z"
+ transform="matrix(0.524827,0.881193,-0.881193,0.524827,9.387535,-2.9550479)"
+ id="path4842"
+ style="fill:none;stroke:url(#linearGradient5653);stroke-width:0.10068177;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3362)" />
+ <path
+ d="m 3.0290077,4.1219881 c 0.0244451,-1.006404 1.4328056,-1.7962742 2.8378096,-1.7962742 1.405004,0 2.8555192,0.7621386 2.8788196,1.796285 0.0233005,1.0341464 -1.5961005,1.5010801 -2.9688013,1.4887622 -1.3046875,-0.0117076 -2.772273,-0.482369 -2.7478279,-1.488773 z"
+ transform="matrix(0.9733328,0.561954,-0.7002632,1.2128914,4.2734795,-4.3729471)"
+ id="path4844"
+ style="fill:url(#linearGradient5655);fill-opacity:1;stroke:none;filter:url(#filter3290)" />
+ </g>
+ <g
+ transform="matrix(0.9758888,0,0,0.9758888,-0.209913,35.930851)"
+ id="g4846"
+ style="display:inline">
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9876803,0.1887101,-0.1887101,0.9876802,2.38882,-1.5621111)"
+ id="path4848"
+ style="fill:#808080;fill-opacity:1;stroke:#808080;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3268)" />
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(1.0057022,0,0,1.0057022,0.1554762,-1.3471028)"
+ id="path4850"
+ style="fill:url(#linearGradient5645);fill-opacity:1;stroke:#000080;stroke-width:0.24858253;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.856926,7.3054457 a 5.0455399,5.0905943 0 1 1 -10.09107975,0 5.0455399,5.0905943 0 1 1 10.09107975,0 z"
+ transform="matrix(0.524827,0.881193,-0.881193,0.524827,9.3875353,-2.9550479)"
+ id="path4852"
+ style="fill:none;stroke:url(#linearGradient5647);stroke-width:0.10042476;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3362)" />
+ <path
+ d="m 3.0290077,4.1219881 c 0.0244451,-1.006404 1.4328056,-1.7962742 2.8378096,-1.7962742 1.405004,0 2.8555192,0.7621386 2.8788196,1.796285 0.0233005,1.0341464 -1.5961005,1.5010801 -2.9688013,1.4887622 -1.3046875,-0.0117076 -2.772273,-0.482369 -2.7478279,-1.488773 z"
+ transform="matrix(0.9733328,0.561954,-0.7002632,1.2128914,4.2734795,-4.3729471)"
+ id="path4854"
+ style="fill:url(#linearGradient5649);fill-opacity:1;stroke:none;filter:url(#filter3290)" />
+ </g>
+ <g
+ transform="matrix(0.9758889,0,0,0.9758889,-0.209913,47.863277)"
+ id="g4856"
+ style="display:inline">
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(0.9876803,0.1887101,-0.1887101,0.9876802,2.3888195,-1.5621111)"
+ id="path4858"
+ style="fill:#808080;fill-opacity:1;stroke:#808080;stroke-width:0.25927806;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3268)" />
+ <path
+ d="m 10.90198,7.3054457 a 5.0905943,5.0905943 0 1 1 -10.18118818,0 5.0905943,5.0905943 0 1 1 10.18118818,0 z"
+ transform="matrix(1.0057022,0,0,1.0057022,0.1554762,-1.3471028)"
+ id="path4860"
+ style="fill:url(#linearGradient5639);fill-opacity:1;stroke:#333333;stroke-width:0.24858253;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.856926,7.3054457 a 5.0455399,5.0905943 0 1 1 -10.09107975,0 5.0455399,5.0905943 0 1 1 10.09107975,0 z"
+ transform="matrix(0.524827,0.881193,-0.881193,0.524827,9.3875353,-2.9550479)"
+ id="path4862"
+ style="fill:none;stroke:url(#linearGradient5641);stroke-width:0.10068177;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3362)" />
+ <path
+ d="m 3.0290077,4.1219881 c 0.0244451,-1.006404 1.4328056,-1.7962742 2.8378096,-1.7962742 1.405004,0 2.8555192,0.7621386 2.8788196,1.796285 0.0233005,1.0341464 -1.5961005,1.5010801 -2.9688013,1.4887622 -1.3046875,-0.0117076 -2.772273,-0.482369 -2.7478279,-1.488773 z"
+ transform="matrix(0.9733328,0.561954,-0.7002632,1.2128914,4.2734795,-4.3729471)"
+ id="path4864"
+ style="fill:url(#linearGradient5643);fill-opacity:1;stroke:none;filter:url(#filter3290)" />
+ </g>
+ </g>
+</svg>
diff --git a/comm/suite/chatzilla/xul/skin/images/spbubble-off.png b/comm/suite/chatzilla/xul/skin/images/spbubble-off.png
new file mode 100644
index 0000000000..d4c3fe2b0e
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/spbubble-off.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/spbubble-on.png b/comm/suite/chatzilla/xul/skin/images/spbubble-on.png
new file mode 100644
index 0000000000..f23d76ae0a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/spbubble-on.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/voice-graphic.png b/comm/suite/chatzilla/xul/skin/images/voice-graphic.png
new file mode 100644
index 0000000000..77ee61dbad
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/voice-graphic.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/images/voice-symbol.png b/comm/suite/chatzilla/xul/skin/images/voice-symbol.png
new file mode 100644
index 0000000000..e1eb3a2481
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/images/voice-symbol.png
Binary files differ
diff --git a/comm/suite/chatzilla/xul/skin/install-plugin.css b/comm/suite/chatzilla/xul/skin/install-plugin.css
new file mode 100644
index 0000000000..d2fa232d97
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/install-plugin.css
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+window {
+ width: 35em;
+ padding: 0.5em;
+}
+
diff --git a/comm/suite/chatzilla/xul/skin/networks-edit.css b/comm/suite/chatzilla/xul/skin/networks-edit.css
new file mode 100644
index 0000000000..e60efe2ab4
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/networks-edit.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+#backgroundBox {
+ background-color: ThreeDLightShadow;
+}
diff --git a/comm/suite/chatzilla/xul/skin/output-dark.css b/comm/suite/chatzilla/xul/skin/output-dark.css
new file mode 100644
index 0000000000..39a4521e8c
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/output-dark.css
@@ -0,0 +1,226 @@
+/* -*- Mode: Text; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * a dark background/light text version of the output window.
+ * see output-base.css for details.
+ */
+
+@import url(chrome://chatzilla/content/output-base.css);
+
+.chatzilla-body { /* The topmost container in the ChatZilla */
+ background: black; /* output window. */
+ color: lightgrey;
+}
+
+a.chatzilla-link {
+ color: #fffdd6;
+}
+
+a.chatzilla-link:visited {
+ color: lightgrey;
+}
+
+.chatzilla-line-marker {
+ box-shadow: 0px 2px darkgreen !important;
+}
+
+.header-outer {
+ background-color: black;
+}
+
+.header {
+ color: lightslategrey;
+ background-color: #333333;
+ border-radius: 7px;
+}
+
+.progress-fg {
+ background-color: silver;
+}
+
+.value {
+ color: silver;
+}
+
+/* Style userlist as white-on-black. */
+treechildren {
+ background: black;
+ color: white;
+}
+
+/* For Mac, who's theme sucks. */
+treechildren::-moz-tree-row(unselected),
+treechildren::-moz-tree-cell(unselected),
+treechildren::-moz-tree-cell-text(unselected) {
+ border-color: black !important;
+ background: black !important;
+ color: white !important;
+}
+
+#splash {
+ color: #444444;
+}
+
+#usr-descnodes,
+#ch-topicnodes {
+ color: white;
+}
+
+[condition] {
+ font-weight: bold;
+}
+
+[condition="red"] {
+ color: red;
+}
+
+[condition="yellow"] {
+ color: yellow;
+}
+
+[condition="green"] {
+ color: lightgreen;
+}
+
+.msg[msg-type="JOIN"] .msg-data a.chatzilla-link,
+.msg[msg-type="PART"] .msg-data a.chatzilla-link {
+ color: lightcyan;
+}
+
+.msg[msg-type="KICK"] .msg-data a.chatzilla-link {
+ color: #ff5700;
+}
+
+.chatzilla-rheet {
+ color: magenta !important;
+}
+
+.chatzilla-highlight[name="Slate"] {
+ color: white;
+ background: #15272d;
+}
+
+.chatzilla-highlight[name="Plum"] {
+ color: white;
+ background: #442144;
+}
+
+.chatzilla-highlight[name="Brown"] {
+ color: white;
+ background: #562a14;
+}
+
+.msg-type { /* .msg-type = message type */
+ color: silver; /* indicator */
+}
+
+.msg-user a.chatzilla-link,
+.msg-user { /* msg-user = nickname portion of */
+ color: white !important; /* a message (channel and query */
+} /* views) */
+
+.msg[mark="even"] .msg-data { /* use even/odd marks to create a */
+ color: white; /* subtle brightness change when */
+} /* the speaker changes. */
+
+.msg[msg-type="JOIN"] .msg-data,
+.msg[msg-type="PART"] .msg-data {
+ color: lightblue;
+}
+
+.msg[msg-type="PART"] .msg-data {
+ color: lightblue;
+}
+
+.msg[msg-type="HELLO"] .msg-data {
+ color: yellow;
+}
+
+.msg[msg-type="ERROR"] .msg-data,
+.msg[msg-type="DISCONNECT"] .msg-data {
+ background: red;
+ color: white;
+}
+
+.msg[msg-type="USAGE"] .msg-data {
+ color: white;
+}
+
+.msg[msg-type="ACTION"] .msg-data {
+ color: #6ac9ee;
+}
+
+.msg[msg-type="NICK"] .msg-data {
+ color: #96fa94;
+}
+
+.msg[msg-type="NOTICE"] .msg-data,
+.msg[msg-type="MODE"] .msg-data {
+ color: #60e066;
+}
+
+.msg[msg-type="NOTICE"] .msg-data a.chatzilla-link,
+.msg[msg-type="MODE"] .msg-data a.chatzilla-link {
+ color: #6dff74;
+}
+
+.msg[msg-type="KICK"] .msg-data {
+ color: #d85d24;
+}
+
+.msg[msg-type="QUIT"] .msg-data {
+ color: #f7b183;
+}
+
+/* important="true" means that the message has text from your /stalk list in
+ * it, has your nickname in it, or was spoken by someone in your /stalk list.
+ */
+.msg[important="true"] .msg-user,
+.msg[important="true"] .msg-data {
+ background: #333333 !important;
+}
+
+.msg-user:before,
+.msg-user:after {
+ color: blue;
+}
+
+.msg[msg-user$="ME!"] .msg-user:before,
+.msg[msg-user$="ME!"] .msg-user:after {
+ color: #6afc73;
+}
+
+.msg[msg-type="ACTION"] .msg-user:before,
+.msg[msg-type="ACTION"] .msg-user:after {
+ color: cyan;
+}
+
+.msg[msg-type="NOTICE"] .msg-user:before,
+.msg[msg-type="NOTICE"] .msg-user:after {
+ color: #6afc73;
+}
+
+/* private messages *not* in a query window */
+.msg[dest-type="IRCUser"] .msg-user:before,
+.msg[dest-type="IRCUser"] .msg-user:after {
+ color: #6afc73;
+}
+
+.msg[msg-dest$="ME!"] .msg-user:before,
+.msg[msg-dest$="ME!"] .msg-user:after {
+ color: magenta;
+}
+
+/* private messages in a query window */
+.msg[view-type="IRCUser"] .msg-user:before,
+.msg[view-type="IRCUser"] .msg-user:after {
+ color: white;
+}
+
+.msg[view-type="IRCUser"][msg-user$="ME!"] .msg-user:before,
+.msg[view-type="IRCUser"][msg-user$="ME!"] .msg-user:after {
+ color: #6afc73;
+}
diff --git a/comm/suite/chatzilla/xul/skin/output-default.css b/comm/suite/chatzilla/xul/skin/output-default.css
new file mode 100644
index 0000000000..af5c850a12
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/output-default.css
@@ -0,0 +1,67 @@
+/* -*- Mode: Text; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * a version of the output window that uses only your default foreground and
+ * backgrond colors. Message types are distinguished by font changes.
+ * see output-base.css for details.
+ */
+
+@import url(chrome://chatzilla/content/output-base.css);
+
+.header-outer {
+ background-color: white;
+}
+
+.progress-fg {
+ background-color: grey;
+}
+
+.msg[msg-type="ACTION"] .msg-data {
+ font-style: italic;
+}
+
+.msg[msg-type="JOIN"] .msg-type,
+.msg[msg-type="PART"] .msg-type,
+.msg[msg-type="QUIT"] .msg-type {
+ font-weight: bold;
+}
+
+.msg[msg-type="QUIT"] .msg-data {
+ font-variant: small-caps;
+ font-weight: bold;
+}
+
+.msg[msg-type="JOIN"] .msg-data,
+.msg[msg-type="PART"] .msg-data {
+ font-variant: small-caps;
+}
+
+.msg[msg-type="HELLO"] .msg-data,
+.msg[msg-type="NICK"] .msg-type,
+.msg[msg-type="NOTICE"] .msg-data {
+ font-weight: bold;
+}
+
+.msg[msg-type="NICK"] .msg-data {
+ font-family: monospace;
+}
+
+/* :before and :after pseudoclasses form the decorations around nicknames. */
+.msg-user:before,
+.msg-user:after {
+ font-size: 100%;
+ font-family: monospace;
+ font-weight: bolder;
+}
+
+.msg[dest-type="IRCUser"] .msg-user,
+.msg[dest-type="IRCUser"][msg-dest$="ME!"] .msg-user {
+ font-style: italic;
+}
+
+.msg[msg-user$="ME!"] .msg-user {
+ font-weight: bold;
+}
diff --git a/comm/suite/chatzilla/xul/skin/output-light.css b/comm/suite/chatzilla/xul/skin/output-light.css
new file mode 100644
index 0000000000..c2a346333a
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/output-light.css
@@ -0,0 +1,217 @@
+/* -*- Mode: Text; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * a light background/dark text version of the output window.
+ * see output-base.css for details.
+ */
+
+@import url(chrome://chatzilla/content/output-base.css);
+
+.chatzilla-body { /* The topmost container in the ChatZilla */
+ background: white; /* output window. */
+ color: #222222;
+}
+
+a.chatzilla-link {
+ font-weight: bold;
+ color: #342ecc;
+}
+
+.chatzilla-line-marker {
+ box-shadow: 0px 2px lightgray !important;
+}
+
+.header-outer {
+ background-color: #d1d0ea;
+}
+
+.header {
+ color: darkslategrey;
+ background-color: #EEEEEE;
+ border: 1px #777499 solid;
+ border-radius: 7px;
+}
+
+#splash {
+ color: #DDDDDD;
+}
+
+#usr-descnodes,
+#ch-topicnodes {
+ color: black;
+}
+
+.progress-fg {
+ background-color: darkslategrey;
+}
+
+[condition] {
+ font-weight: bold;
+}
+
+[condition="red"] {
+ color: red;
+}
+
+[condition="yellow"] {
+ color: orange;
+}
+
+[condition="green"] {
+ color: #2ec908;
+}
+
+.msg[msg-type="PRIVMSG"] .msg-data,
+.msg[msg-type="ACTION"] .msg-data {
+ background-color: #F0F0F0;
+}
+
+.msg[msg-type="HELLO"] .msg-data a.chatzilla-link {
+ color: #d7d9dd;
+}
+
+.msg[msg-type="JOIN"] .msg-data a.chatzilla-link,
+.msg[msg-type="PART"] .msg-data a.chatzilla-link {
+ font-weight: bold;
+ color: #11c411;
+}
+
+.msg[msg-type="ERROR"] .msg-data a.chatzilla-link,
+.msg[msg-type="DISCONNECT"] .msg-data a.chatzilla-link {
+ font-weight: bold;
+ color: white;
+}
+
+.msg[msg-type="KICK"] .msg-data a.chatzilla-link {
+ color: #aa0d08;
+}
+
+.msg[msg-type="NOTICE"] .msg-data a.chatzilla-link {
+ color: #d64444;
+}
+
+.msg[msg-type="QUIT"] .msg-data a.chatzilla-link {
+ color: #c46907;
+}
+
+.chatzilla-rheet {
+ color: #e25e00 !important;
+}
+
+.chatzilla-highlight[name="Slate"] {
+ color: black;
+ background: #b8c4e0;
+}
+
+.chatzilla-highlight[name="Plum"] {
+ color: black;
+ background: #ddb8d0;
+}
+
+.chatzilla-highlight[name="Brown"] {
+ color: black;
+ background: #ffbf77;
+}
+
+.msg-type { /* .msg-type = message type */
+ color: #686699; /* indicator */
+ font-weight: bold;
+}
+
+.msg-user a.chatzilla-link,
+.msg-user { /* msg-user = nickname portion of */
+ color: black !important; /* a message (channel and query */
+ font-weight: bold; /* views) */
+}
+
+.msg[mark="even"] .msg-data { /* use even/odd marks to create a */
+ color: #555555; /* subtle brightness change when */
+} /* the speaker changes. */
+
+.msg[msg-type="JOIN"] .msg-data,
+.msg[msg-type="PART"] .msg-data {
+ color: #0e9e0e;
+ background-color: #c3f7c3;
+ font-weight: bold;
+ border-radius: 5px 5px 5px 5px;
+}
+
+.msg[msg-type="QUIT"] .msg-data {
+ background: #fff196;
+ color: #ff8d02;
+ font-weight: bold;
+ border-radius: 5px 5px 5px 5px;
+}
+
+.msg[msg-type="HELLO"] .msg-data {
+ background: #1342a5;
+ color: white;
+ border-radius: 5px 5px 5px 5px;
+ font-weight: bold;
+}
+
+.msg[msg-type="ERROR"] .msg-data,
+.msg[msg-type="DISCONNECT"] .msg-data {
+ border-radius: 5px 5px 5px 5px;
+ background: #a8221e;
+ color: white;
+}
+
+.msg[msg-type="USAGE"] .msg-data {
+ color: black;
+}
+
+.msg[msg-type="ACTION"] .msg-data {
+ color: black;
+ font-style: italic;
+}
+
+.msg[msg-type="NICK"] .msg-data {
+ color: #4e8387;
+ background-color: #d5e9ea;
+ font-weight: bold;
+}
+
+.msg[msg-type="NOTICE"] .msg-data {
+ color: #ae4141;
+ font-weight: bold;
+}
+
+.msg[msg-type="MODE"] .msg-data {
+ color: #2709ed;
+ font-weight: bold;
+}
+
+.msg[msg-type="KICK"] .msg-data {
+ color: #ff1a0a;
+ background: #ffdbcc;
+ font-weight: bold;
+ border-radius: 5px 5px 5px 5px;
+}
+
+/* important="true" means that the message has text from your /stalk list in
+ * it, has your nickname in it, or was spoken by someone in your /stalk list.
+ */
+.msg[important="true"] .msg-user {
+ background: #d4d8d4;
+ border-radius: 5px 0px 0px 5px;
+}
+
+.msg[important="true"] .msg-data {
+ background: #eaefeb;
+}
+
+
+/* :before and :after pseudoclasses form the decorations around nicknames. */
+.msg-user:before,
+.msg-user:after {
+ color: #777499;
+}
+
+.msg[msg-user$="ME!"] .msg-user:before, /* the decoration around MY */
+.msg[msg-user$="ME!"] .msg-user:after { /* nick */
+ color: #843c6c;
+}
diff --git a/comm/suite/chatzilla/xul/skin/output-loud.css b/comm/suite/chatzilla/xul/skin/output-loud.css
new file mode 100644
index 0000000000..3bf7a94416
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/output-loud.css
@@ -0,0 +1,202 @@
+/* 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/. */
+
+body {
+
+ margin: 0px 0px 0px 0px;
+ background: black;
+
+}
+
+.bold {
+
+ font-weight: bold;
+
+}
+
+.italic {
+
+ font-style: italic;
+
+}
+
+.underline {
+
+ text-decoration: underline;
+
+}
+
+.strikethrough {
+
+ text-decoration: line-through;
+
+}
+
+.teletype {
+
+ font-family: monospace;
+
+}
+
+.smallcap {
+
+ font-variant: small-caps;
+
+}
+
+.rheet {
+
+ font-size: 14pt;
+ font-weight: bold;
+ color: magenta;
+
+}
+
+/* output from a chat session (contains msgs) */
+.chat-view {
+
+ vertical-align: text-bottom;
+
+}
+
+/* common container for all portions of a message
+ * (contains msg-*s) */
+.msg {
+
+ font-family: sans-serif;
+
+}
+
+.msg[user="!ME"] {
+
+ background: lightgrey;
+
+}
+
+/* message data in output window */
+.msg-data {
+
+ font-weight: bold;
+ color: lightgrey;
+ background: #1a2a44;
+
+}
+
+/* message data in output window */
+
+.msg-data[user="!ME"]{
+
+ background: black;
+
+}
+
+.msg-data[msgtype="JOIN"],
+.msg-data[msgtype="PART"] {
+
+ width: 100%;
+ font-variant: small-caps;
+ background: lightgray;
+ color: black;
+
+}
+
+.msg-data[msgtype="HELLO"] {
+
+ background: white;
+ color: darkgreen;
+
+}
+
+.msg-data[msgtype="ERROR"],
+.msg-data[msgtype="DISCONNECT"] {
+
+ background: red;
+ color: white;
+
+}
+
+.msg-data[msgtype="USAGE"] {
+
+ font-style: italic;
+ color: white;
+
+}
+
+.msg-data[msgtype="HELP"] {
+
+ font-weight: normal;
+
+}
+
+.msg-data[msgtype="ACTION"] {
+
+ color: cyan;
+
+}
+
+.msg-data[msgtype="NOTICE"] {
+
+ color: yellow;
+
+}
+
+.msg-data[msgtype="KICK"] {
+
+ background: orange;
+ color: yellow;
+
+}
+
+.msg-data[msgtype="QUIT"] {
+
+ background: lightgrey;
+ color: brown;
+
+}
+
+/* nickname field in output */
+.msg-user {
+
+ text-align: center;
+ vertical-align: middle;
+ color: lightgrey;
+ font-weight: bold;
+ background: grey;
+
+}
+
+.msg-user[parity="odd"]{
+
+ background: black;
+
+}
+
+.msg-user[user="!ME"] {
+
+ color : white;
+
+}
+
+.msg-user[msgtype="ACTION"] {
+
+ font-style: italic;
+
+}
+
+/* Message type indicator in output window */
+.msg-type {
+
+ text-align: center;
+ vertical-align: middle;
+ color: brown;
+ font-weight: bold;
+ background: lightgrey;
+
+}
+
+.msg-type[user="!ME"] {
+
+ background: silver;
+
+}
diff --git a/comm/suite/chatzilla/xul/skin/output-marble.css b/comm/suite/chatzilla/xul/skin/output-marble.css
new file mode 100644
index 0000000000..0de0dc1409
--- /dev/null
+++ b/comm/suite/chatzilla/xul/skin/output-marble.css
@@ -0,0 +1,148 @@
+/* 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/. */
+
+body {
+
+ margin: 0px 0px 0px 0px;
+ background: url(chrome://chatzilla/skin/images/xtal.jpg);
+
+}
+
+.bold {
+
+ font-weight: bold;
+
+}
+
+.italic {
+
+ font-style: italic;
+
+}
+
+.underline {
+
+ text-decoration: underline;
+
+}
+
+.strikethrough {
+
+ text-decoration: line-through;
+
+}
+
+.teletype {
+
+ font-family: monospace;
+
+}
+
+.smallcap {
+
+ font-variant: small-caps;
+
+}
+
+.rheet {
+
+ font-size: 14pt;
+ font-weight: bold;
+ color: magenta;
+
+}
+
+/* output from a chat session (contains msgs) */
+.chat-view {
+
+ vertical-align: text-bottom;
+
+}
+
+/* common container for all portions of a message
+ * (contains msg-*s) */
+.msg {
+
+ font-family: sans-serif;
+
+}
+
+.msg[user="!ME"] {
+
+ background: lightgrey;
+
+}
+
+/* message data in output window */
+.msg-data {
+
+ font-weight: bold;
+
+}
+
+.msg-data[msgtype="JOIN"],
+.msg-data[msgtype="PART"] {
+
+ font-variant: small-caps;
+ color: darkslategrey;
+
+}
+
+.msg-data[msgtype="ACTION"] {
+
+ color: darkred;
+
+}
+
+.msg-data[msgtype="NOTICE"] {
+
+ color: green;
+
+}
+
+.msg-data[msgtype="KICK"] {
+
+ color: slategrey;
+
+}
+
+.msg-data[msgtype="QUIT"] {
+
+ color: brown;
+
+}
+
+/* nickname field in output */
+.msg-user {
+
+ text-align: center;
+ vertical-align: middle;
+ color: blue;
+ font-weight: bold;
+ background: grey;
+
+}
+
+.msg-user[user="!ME"] {
+
+ color: green;
+
+}
+
+.msg-user[msgtype="ACTION"] {
+
+ font-style: italic;
+
+}
+
+/* Message type indicator in output window */
+.msg-type {
+
+ text-align: center;
+ vertical-align: middle;
+ color: brown;
+ font-weight: bold;
+ background: lightgrey;
+
+}
diff --git a/comm/suite/components/SuiteComponents.manifest b/comm/suite/components/SuiteComponents.manifest
new file mode 100644
index 0000000000..12a0d1ad77
--- /dev/null
+++ b/comm/suite/components/SuiteComponents.manifest
@@ -0,0 +1,18 @@
+component {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f} nsAbout.js
+contract @mozilla.org/network/protocol/about;1?what= {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=blocked {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=certerror {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=data {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=feeds {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=life {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=newserror {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=privatebrowsing {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=rights {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+contract @mozilla.org/network/protocol/about;1?what=sessionrestore {d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}
+component {22042bdb-56e4-47c6-8b12-fdfa859c05a9} nsGopherProtocolStubHandler.js
+contract @mozilla.org/network/protocol;1?name=gopher {22042bdb-56e4-47c6-8b12-fdfa859c05a9}
+component {bbbbe845-5a1b-40ee-813c-f84b8faaa07c} nsSuiteGlue.js
+contract @mozilla.org/suite/suiteglue;1 {bbbbe845-5a1b-40ee-813c-f84b8faaa07c}
+category app-startup nsSuiteGlue service,@mozilla.org/suite/suiteglue;1
+component {9d4c845d-3f09-402a-b66d-50f291d7d50f} nsSuiteGlue.js
+contract @mozilla.org/content-permission/prompt;1 {9d4c845d-3f09-402a-b66d-50f291d7d50f}
diff --git a/comm/suite/components/autocomplete/content/autocomplete.css b/comm/suite/components/autocomplete/content/autocomplete.css
new file mode 100644
index 0000000000..6c67bad2ed
--- /dev/null
+++ b/comm/suite/components/autocomplete/content/autocomplete.css
@@ -0,0 +1,46 @@
+/* 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/. */
+
+
+.autocomplete-result-popupset {
+ width: 0 !important;
+}
+
+.autocomplete-result-popup {
+ display: -moz-popup !important;
+}
+
+/* the C++ implementation of widgets is too eager to make popups visible.
+ this causes problems (bug 120155 and others), thus this workaround: */
+.autocomplete-result-popup[hidden="true"] {
+ visibility: hidden;
+}
+
+.autocomplete-tree {
+ -moz-user-focus: ignore;
+}
+
+.autocomplete-history-dropmarker {
+ display: none;
+}
+
+.autocomplete-history-dropmarker[enablehistory="true"] {
+ display: -moz-box;
+}
+
+/* The following rule is here to fix bug 96899 (and now 117952).
+ Somehow trees create a situation
+ in which a popupset flows itself as if its popup child is directly within it
+ instead of the placeholder child that should actually be inside the popupset.
+ This is a stopgap measure, and it does not address the real bug. */
+popupset {
+ max-width: 0px;
+ width: 0px;
+ min-width: 0%;
+ min-height: 0%;
+}
+
+treecolpicker {
+ display: none;
+}
diff --git a/comm/suite/components/autocomplete/content/autocomplete.xml b/comm/suite/components/autocomplete/content/autocomplete.xml
new file mode 100644
index 0000000000..58b171e641
--- /dev/null
+++ b/comm/suite/components/autocomplete/content/autocomplete.xml
@@ -0,0 +1,1641 @@
+<?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/. -->
+
+
+<bindings id="autocompleteBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="autocomplete" role="xul:combobox"
+ extends="chrome://global/content/bindings/textbox.xml#textbox">
+ <resources>
+ <stylesheet src="chrome://communicator/content/autocomplete.css"/>
+ <stylesheet src="chrome://global/skin/autocomplete.css"/>
+ </resources>
+
+ <content>
+ <children includes="menupopup"/>
+
+ <xul:hbox class="autocomplete-textbox-container" flex="1" align="center">
+ <children includes="image|deck|stack|box">
+ <xul:image class="autocomplete-icon" allowevents="true"/>
+ </children>
+
+ <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,tooltiptext=inputtooltiptext">
+ <children/>
+ <html:input anonid="input" class="autocomplete-textbox textbox-input"
+ allowevents="true"
+ xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,userAction"/>
+ </xul:hbox>
+ <children includes="hbox"/>
+ </xul:hbox>
+
+ <xul:dropmarker class="autocomplete-history-dropmarker" allowevents="true"
+ xbl:inherits="open,enablehistory" anonid="historydropmarker"/>
+
+ <xul:popupset>
+ <xul:panel type="autocomplete" anonid="popup"
+ ignorekeys="true" noautofocus="true" level="top"
+ xbl:inherits="for=id,nomatch"/>
+ </xul:popupset>
+ </content>
+
+ <implementation implements="nsIDOMXULMenuListElement">
+
+ <constructor><![CDATA[
+ // XXX bug 90337 band-aid until we figure out what's going on here
+ if (this.value != this.mInputElt.value)
+ this.mInputElt.value = this.value;
+ delete this.value;
+
+ // listen for pastes
+ this.mInputElt.controllers.insertControllerAt(0, this.mPasteController);
+
+ // listen for menubar activation
+ window.top.addEventListener("DOMMenuBarActive", this.mMenuBarListener, true);
+
+ // set default property values
+ this.ifSetAttribute("timeout", 50);
+ this.ifSetAttribute("pastetimeout", 1000);
+ this.ifSetAttribute("maxrows", 5);
+ this.ifSetAttribute("showpopup", true);
+ this.ifSetAttribute("disableKeyNavigation", true);
+
+ // initialize the search sessions
+ if (this.hasAttribute("autocompletesearch"))
+ this.initAutoCompleteSearch();
+
+ // hack to work around lack of bottom-up constructor calling
+ if ("initialize" in this.popup)
+ this.popup.initialize();
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ this.clearResults(false);
+ window.top.removeEventListener("DOMMenuBarActive", this.mMenuBarListener, true);
+ this.mInputElt.controllers.removeController(this.mPasteController);
+ ]]></destructor>
+
+ <!-- =================== nsIAutoCompleteInput =================== -->
+ <!-- XXX: This implementation is currently incomplete. -->
+
+ <!-- reference to the results popup element -->
+ <field name="popup"><![CDATA[
+ document.getAnonymousElementByAttribute(this, "anonid", "popup");
+ ]]></field>
+
+ <property name="popupOpen"
+ onget="return this.mMenuOpen;"
+ onset="if (val) this.openPopup(); else this.closePopup(); return val;"/>
+
+ <!-- option to turn off autocomplete -->
+ <property name="disableAutoComplete"
+ onset="this.setAttribute('disableautocomplete', val); return val;"
+ onget="return this.getAttribute('disableautocomplete') == 'true';"/>
+
+ <!-- if the resulting match string is not at the beginning of the typed string,
+ this will optionally autofill like this "bar |>> foobar|" -->
+ <property name="completeDefaultIndex"
+ onset="this.setAttribute('completedefaultindex', val); return val;"
+ onget="return this.getAttribute('completedefaultindex') == 'true';"/>
+
+ <!-- option for completing to the default result whenever the user hits
+ enter or the textbox loses focus -->
+ <property name="forceComplete"
+ onset="this.setAttribute('forcecomplete', val); return val;"
+ onget="return this.getAttribute('forcecomplete') == 'true';"/>
+
+ <property name="minResultsForPopup"
+ onset="this.setAttribute('minresultsforpopup', val); return val;"
+ onget="var t = this.getAttribute('minresultsforpopup'); return t ? parseInt(t) : 1;"/>
+
+ <!-- maximum number of rows to display -->
+ <property name="maxRows"
+ onset="this.setAttribute('maxrows', val); return val;"
+ onget="return parseInt(this.getAttribute('maxrows')) || 0;"/>
+
+ <!-- toggles a second column in the results list which contains
+ the string in the comment field of each autocomplete result -->
+ <property name="showCommentColumn"
+ onget="return this.getAttribute('showcommentcolumn') == 'true';">
+ <setter><![CDATA[
+ this.popup.showCommentColumn = val;
+ this.setAttribute('showcommentcolumn', val);
+ return val;
+ ]]></setter>
+ </property>
+
+ <!-- number of milliseconds after a keystroke before a search begins -->
+ <property name="timeout"
+ onset="this.setAttribute('timeout', val); return val;"
+ onget="return parseInt(this.getAttribute('timeout')) || 0;"/>
+
+ <property name="searchParam"
+ onget="return this.getAttribute('autocompletesearchparam') || '';"
+ onset="this.setAttribute('autocompletesearchparam', val); return val;"/>
+
+ <property name="searchCount" readonly="true"
+ onget="return this.sessionCount;"/>
+
+ <method name="getSearchAt">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var idx = -1;
+ for (var name in this.mSessions)
+ if (++idx == aIndex)
+ return name;
+
+ return null;
+ ]]></body>
+ </method>
+
+ <property name="textValue"
+ onget="return this.value;"
+ onset="this.setTextValue(val); return val;"/>
+
+ <method name="onSearchBegin">
+ <body><![CDATA[
+ this._fireEvent("searchbegin");
+ ]]></body>
+ </method>
+
+ <method name="onSearchComplete">
+ <body><![CDATA[
+ if (this.noMatch)
+ this.setAttribute("nomatch", "true");
+ else
+ this.removeAttribute("nomatch");
+
+ this._fireEvent("searchcomplete");
+ ]]></body>
+ </method>
+
+ <method name="onTextReverted">
+ <body><![CDATA[
+ return this._fireEvent("textreverted");
+ ]]></body>
+ </method>
+
+ <!-- =================== nsIDOMXULMenuListElement =================== -->
+
+ <property name="editable" readonly="true"
+ onget="return true;" />
+
+ <property name="crop"
+ onset="this.setAttribute('crop', val); return val;"
+ onget="return this.getAttribute('crop');"/>
+
+ <property name="label" readonly="true"
+ onget="return this.mInputElt.value;"/>
+
+ <property name="open"
+ onget="return this.getAttribute('open') == 'true';">
+ <setter>
+ <![CDATA[
+ var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker");
+ if (val) {
+ this.setAttribute('open', true);
+ historyPopup.showPopup();
+ } else {
+ this.removeAttribute('open');
+ historyPopup.hidePopup();
+ }
+ ]]>
+ </setter>
+ </property>
+
+ <!-- =================== PUBLIC PROPERTIES =================== -->
+
+ <property name="value"
+ onget="return this.mInputElt.value;">
+ <setter><![CDATA[
+ this.ignoreInputEvent = true;
+ this.mInputElt.value = val;
+ this.ignoreInputEvent = false;
+ var event = document.createEvent('Events');
+ event.initEvent('ValueChange', true, true);
+ this.mInputElt.dispatchEvent(event);
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="focused"
+ onget="return this.getAttribute('focused') == 'true';"/>
+
+ <method name="initAutoCompleteSearch">
+ <body><![CDATA[
+ var list = this.getAttribute("autocompletesearch").split(" ");
+ for (var i = 0; i < list.length; i++) {
+ var name = list[i];
+ var contractid = "@mozilla.org/autocomplete/search;1?name=" + name;
+ if (contractid in Cc) {
+ try {
+ this.mSessions[name] =
+ Cc[contractid].getService(Ci.nsIAutoCompleteSearch);
+ this.mLastResults[name] = null;
+ this.mLastRows[name] = 0;
+ ++this.sessionCount;
+ } catch (e) {
+ dump("### ERROR - unable to create search \"" + name + "\".\n");
+ }
+ } else {
+ dump("search \"" + name + "\" not found - skipping.\n");
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!-- the number of sessions currently in use -->
+ <field name="sessionCount">0</field>
+
+ <!-- number of milliseconds after a paste before a search begins -->
+ <property name="pasteTimeout"
+ onset="this.setAttribute('pastetimeout', val); return val;"
+ onget="var t = parseInt(this.getAttribute('pastetimeout')); return t ? t : 0;"/>
+
+ <!-- option for filling the textbox with the best match while typing
+ and selecting the difference -->
+ <property name="autoFill"
+ onset="this.setAttribute('autofill', val); return val;"
+ onget="return this.getAttribute('autofill') == 'true';"/>
+
+ <!-- if this attribute is set, allow different style for
+ non auto-completed lines -->
+ <property name="highlightNonMatches"
+ onset="this.setAttribute('highlightnonmatches', val); return val;"
+ onget="return this.getAttribute('highlightnonmatches') == 'true';"/>
+
+ <!-- option to show the popup containing the results -->
+ <property name="showPopup"
+ onset="this.setAttribute('showpopup', val); return val;"
+ onget="return this.getAttribute('showpopup') == 'true';"/>
+
+ <!-- option to allow scrolling through the list via the tab key, rather than
+ tab moving focus out of the textbox -->
+ <property name="tabScrolling"
+ onset="this.setAttribute('tabscrolling', val); return val;"
+ onget="return this.getAttribute('tabscrolling') == 'true';"/>
+
+ <!-- option to completely ignore any blur events while
+ searches are still going on. This is useful so that nothing
+ gets autopicked if the window is required to lose focus for
+ some reason (eg in LDAP autocomplete, another window may be
+ brought up so that the user can enter a password to authenticate
+ to an LDAP server). -->
+ <property name="ignoreBlurWhileSearching"
+ onset="this.setAttribute('ignoreblurwhilesearching', val); return val;"
+ onget="return this.getAttribute('ignoreblurwhilesearching') == 'true';"/>
+
+ <!-- state which indicates the current action being performed by the user.
+ Possible values are : none, typing, scrolling -->
+ <property name="userAction"
+ onset="this.setAttribute('userAction', val); return val;"
+ onget="return this.getAttribute('userAction');"/>
+
+ <!-- state which indicates if the last search had no matches -->
+ <field name="noMatch">true</field>
+
+ <!-- state which indicates a search is currently happening -->
+ <field name="isSearching">false</field>
+
+ <!-- state which indicates a search timeout is current waiting -->
+ <property name="isWaiting"
+ onget="return this.mAutoCompleteTimer != 0;"/>
+
+ <!-- =================== PRIVATE PROPERTIES =================== -->
+
+ <field name="mSessions">({})</field>
+ <field name="mLastResults">({})</field>
+ <field name="mLastRows">({})</field>
+ <field name="mLastKeyCode">null</field>
+ <field name="mAutoCompleteTimer">0</field>
+ <field name="mMenuOpen">false</field>
+ <field name="mFireAfterSearch">false</field>
+ <field name="mFinishAfterSearch">false</field>
+ <field name="mNeedToFinish">false</field>
+ <field name="mNeedToComplete">false</field>
+ <field name="mTransientValue">false</field>
+ <field name="mView">null</field>
+ <field name="currentSearchString">""</field>
+ <field name="ignoreInputEvent">false</field>
+ <field name="oninit">null</field>
+ <field name="mDefaultMatchFilled">false</field>
+ <field name="mFirstReturn">true</field>
+ <field name="mIsPasting">false</field>
+
+ <field name="mPasteController"><![CDATA[
+ ({
+ self: this,
+ kGlobalClipboard: Ci.nsIClipboard.kGlobalClipboard,
+ supportsCommand: function(aCommand) {
+ return aCommand == "cmd_paste";
+ },
+ isCommandEnabled: function(aCommand) {
+ return aCommand == "cmd_paste" &&
+ this.self.editor.isSelectionEditable &&
+ this.self.editor.canPaste(this.kGlobalClipboard);
+ },
+ doCommand: function(aCommand) {
+ if (aCommand == "cmd_paste") {
+ this.self.mIsPasting = true;
+ this.self.editor.paste(this.kGlobalClipboard);
+ this.self.mIsPasting = false;
+ }
+ },
+ onEvent: function() {}
+ })
+ ]]></field>
+
+ <field name="mMenuBarListener"><![CDATA[
+ ({
+ self: this,
+ handleEvent: function(aEvent) {
+ try {
+ this.self.finishAutoComplete(false, false, aEvent);
+ this.self.clearTimer();
+ this.self.closePopup();
+ } catch (e) {
+ window.top.removeEventListener("DOMMenuBarActive", this, true);
+ }
+ }
+ })
+ ]]></field>
+
+ <field name="mAutoCompleteObserver"><![CDATA[
+ ({
+ self: this,
+ onSearchResult: function(aSearch, aResult) {
+ for (var name in this.self.mSessions)
+ if (this.self.mSessions[name] == aSearch)
+ this.self.processResults(name, aResult);
+ }
+ })
+ ]]></field>
+
+ <field name="mInputElt"><![CDATA[
+ document.getAnonymousElementByAttribute(this, "anonid", "input");
+ ]]></field>
+
+ <field name="mMenuAccessKey"><![CDATA[
+ Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch)
+ .getIntPref("ui.key.menuAccessKey");
+ ]]></field>
+
+ <!-- =================== PUBLIC METHODS =================== -->
+
+ <method name="getErrorAt">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var obj = aIndex < 0 ? null : this.convertIndexToSession(aIndex);
+ return obj && this.mLastResults[obj.session] &&
+ this.mLastResults[obj.session].errorDescription;
+ ]]></body>
+ </method>
+
+ <!-- get a value from the autocomplete results as a string via an absolute index-->
+ <method name="getResultValueAt">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var obj = this.convertIndexToSession(aIndex);
+ return obj ? this.getSessionValueAt(obj.session, obj.index) : null;
+ ]]></body>
+ </method>
+
+ <!-- get a value from the autocomplete results as a string from a specific session -->
+ <method name="getSessionValueAt">
+ <parameter name="aSession"/>
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ var result = this.mLastResults[aSession];
+ return result.errorDescription || result.getValueAt(aIndex);
+ ]]></body>
+ </method>
+
+ <!-- get the total number of results overall -->
+ <method name="getResultCount">
+ <body><![CDATA[
+ return this.view.rowCount;
+ ]]></body>
+ </method>
+
+ <!-- get the first session that has results -->
+ <method name="getDefaultSession">
+ <body><![CDATA[
+ for (var name in this.mLastResults) {
+ var results = this.mLastResults[name];
+ if (results && results.matchCount > 0 && !results.errorDescription)
+ return name;
+ }
+ return null;
+ ]]></body>
+ </method>
+
+ <!-- empty the cached result data and empty the results popup -->
+ <method name="clearResults">
+ <parameter name="aInvalidate"/>
+ <body><![CDATA[
+ this.clearResultData();
+ this.clearResultElements(aInvalidate);
+ ]]></body>
+ </method>
+
+ <!-- =================== PRIVATE METHODS =================== -->
+
+ <!-- ::::::::::::: session searching ::::::::::::: -->
+
+ <!-- -->
+ <method name="callListener">
+ <parameter name="me"/>
+ <parameter name="aAction"/>
+ <body><![CDATA[
+ // bail if the binding was detached or the element removed from
+ // document during the timeout
+ if (!("startLookup" in me) || !me.ownerDocument || !me.parentNode)
+ return;
+
+ me.clearTimer();
+
+ if (me.disableAutoComplete)
+ return;
+
+ switch (aAction) {
+ case "startLookup":
+ me.startLookup();
+ break;
+
+ case "stopLookup":
+ me.stopLookup();
+ break;
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="startLookup">
+ <body><![CDATA[
+ var str = this.currentSearchString;
+ if (!str) {
+ this.clearResults(false);
+ this.closePopup();
+ return;
+ }
+
+ this.isSearching = true;
+ this.mFirstReturn = true;
+ this.mSessionReturns = this.sessionCount;
+ this.mFailureItems = 0;
+ this.mDefaultMatchFilled = false; // clear out our prefill state.
+
+ // Notify the input that the search is beginning.
+ this.onSearchBegin();
+
+ // tell each session to start searching...
+ for (var name in this.mSessions)
+ try {
+ this.mSessions[name].startSearch(str, this.searchParam, this.mLastResults[name], this.mAutoCompleteObserver);
+ } catch (e) {
+ --this.mSessionReturns;
+ this.searchFailed();
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="stopLookup">
+ <body><![CDATA[
+ for (var name in this.mSessions)
+ this.mSessions[name].stopSearch();
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="processResults">
+ <parameter name="aSessionName"/>
+ <parameter name="aResults"/>
+ <body><![CDATA[
+ if (this.disableAutoComplete)
+ return;
+
+ const ACR = Ci.nsIAutoCompleteResult;
+ var status = aResults.searchResult;
+ if (status != ACR.RESULT_NOMATCH_ONGOING &&
+ status != ACR.RESULT_SUCCESS_ONGOING)
+ --this.mSessionReturns;
+
+ // check the many criteria for failure
+ if (aResults.errorDescription)
+ ++this.mFailureItems;
+ else if (status == ACR.RESULT_IGNORED ||
+ status == ACR.RESULT_FAILURE ||
+ status == ACR.RESULT_NOMATCH ||
+ status == ACR.RESULT_NOMATCH_ONGOING ||
+ aResults.matchCount == 0 ||
+ aResults.searchString != this.currentSearchString)
+ {
+ this.mLastResults[aSessionName] = null;
+ if (this.mFirstReturn)
+ this.clearResultElements(false);
+ this.mFirstReturn = false;
+ this.searchFailed();
+ return;
+ }
+
+ if (this.mFirstReturn) {
+ if (this.view.mTree)
+ this.view.mTree.beginUpdateBatch();
+ this.clearResultElements(false); // clear results, but don't repaint yet
+ }
+
+ // always call openPopup...we may not have opened it
+ // if a previous search session didn't return enough search results.
+ // it's smart and doesn't try to open itself multiple times...
+ // be sure to add our result elements before calling openPopup as we need
+ // to know the total # of results found so far.
+ this.addResultElements(aSessionName, aResults);
+
+ this.autoFillInput(aSessionName, aResults, false);
+ if (this.mFirstReturn && this.view.mTree)
+ this.view.mTree.endUpdateBatch();
+ this.openPopup();
+ this.mFirstReturn = false;
+
+ // if this is the last session to return...
+ if (this.mSessionReturns == 0)
+ this.postSearchCleanup();
+
+ if (this.mFinishAfterSearch)
+ this.finishAutoComplete(false, this.mFireAfterSearch, null);
+ ]]></body>
+ </method>
+
+ <!-- called each time a search fails, except when failure items need
+ to be displayed. If all searches have failed, clear the list
+ and close the popup -->
+ <method name="searchFailed">
+ <body><![CDATA[
+ // if all searches are done and they all failed...
+ if (this.mSessionReturns == 0 && this.getResultCount() == 0) {
+ if (this.minResultsForPopup == 0) {
+ this.clearResults(true); // clear data and repaint empty
+ this.openPopup();
+ } else {
+ this.closePopup();
+ }
+ }
+
+ // if it's the last session to return, time to clean up...
+ if (this.mSessionReturns == 0)
+ this.postSearchCleanup();
+ ]]></body>
+ </method>
+
+ <!-- does some stuff after a search is done (success or failure) -->
+ <method name="postSearchCleanup">
+ <body><![CDATA[
+ this.isSearching = false;
+
+ // figure out if there are no matches in all search sessions
+ var failed = true;
+ for (var name in this.mSessions) {
+ if (this.mLastResults[name])
+ failed = this.mLastResults[name].errorDescription ||
+ this.mLastResults[name].matchCount == 0;
+ if (!failed)
+ break;
+ }
+ this.noMatch = failed;
+
+ // if we have processed all of our searches, and none of them gave us a default index,
+ // then we should try to auto fill the input field with the first match.
+ // note: autoFillInput is smart enough to kick out if we've already prefilled something...
+ if (!this.noMatch) {
+ var defaultSession = this.getDefaultSession();
+ if (defaultSession)
+ this.autoFillInput(defaultSession, this.mLastResults[defaultSession], true);
+ }
+
+ // Notify the input that the search is complete.
+ this.onSearchComplete();
+ ]]></body>
+ </method>
+
+ <!-- when the focus exits the widget or user hits return,
+ determine what value to leave in the textbox -->
+ <method name="finishAutoComplete">
+ <parameter name="aForceComplete"/>
+ <parameter name="aFireTextCommand"/>
+ <parameter name="aTriggeringEvent"/>
+ <body><![CDATA[
+ this.mFinishAfterSearch = false;
+ this.mFireAfterSearch = false;
+ if (this.mNeedToFinish && !this.disableAutoComplete) {
+ // set textbox value to either override value, or default search result
+ var val = this.popup.overrideValue;
+ if (val) {
+ this.setTextValue(val);
+ this.mNeedToFinish = false;
+ } else if (this.mTransientValue ||
+ !(this.forceComplete ||
+ (aForceComplete &&
+ this.mDefaultMatchFilled &&
+ this.mNeedToComplete))) {
+ this.mNeedToFinish = false;
+ } else if (this.isWaiting) {
+ // if the user typed, the search results are out of date, so let
+ // the search finish, and tell it to come back here when it's done
+ this.mFinishAfterSearch = true;
+ this.mFireAfterSearch = aFireTextCommand;
+ return;
+ } else {
+ // we want to use the default item index for the first session which gave us a valid
+ // default item index...
+ for (var name in this.mLastResults) {
+ var results = this.mLastResults[name];
+ if (results && results.matchCount > 0 &&
+ !results.errorDescription && results.defaultIndex != -1)
+ {
+ val = results.getValueAt(results.defaultIndex);
+ this.setTextValue(val);
+ this.mDefaultMatchFilled = true;
+ this.mNeedToFinish = false;
+ break;
+ }
+ }
+
+ if (this.mNeedToFinish) {
+ // if a search is happening at this juncture, bail out of this function
+ // and let the search finish, and tell it to come back here when it's done
+ if (this.isSearching) {
+ this.mFinishAfterSearch = true;
+ this.mFireAfterSearch = aFireTextCommand;
+ return;
+ }
+
+ this.mNeedToFinish = false;
+ var defaultSession = this.getDefaultSession();
+ if (defaultSession)
+ {
+ // preselect the first one
+ var first = this.getSessionValueAt(defaultSession, 0);
+ this.setTextValue(first);
+ this.mDefaultMatchFilled = true;
+ }
+ }
+ }
+
+ this.stopLookup();
+
+ this.closePopup();
+ }
+
+ this.mNeedToComplete = false;
+ this.clearTimer();
+
+ if (aFireTextCommand)
+ this._fireEvent("textentered", this.userAction, aTriggeringEvent);
+ ]]></body>
+ </method>
+
+ <!-- when the user clicks an entry in the autocomplete popup -->
+ <method name="onResultClick">
+ <body><![CDATA[
+ // set textbox value to either override value, or the clicked result
+ var errItem = this.getErrorAt(this.popup.selectedIndex);
+ var val = this.popup.overrideValue;
+ if (val)
+ this.setTextValue(val);
+ else if (this.popup.selectedIndex != -1) {
+ if (errItem) {
+ this.setTextValue(this.currentSearchString);
+ this.mTransientValue = true;
+ } else {
+ this.setTextValue(this.getResultValueAt(
+ this.popup.selectedIndex));
+ }
+ }
+
+ this.mNeedToFinish = false;
+ this.mNeedToComplete = false;
+
+ this.closePopup();
+
+ this.currentSearchString = "";
+
+ if (errItem)
+ this._fireEvent("errorcommand", errItem);
+ this._fireEvent("textentered", "clicking");
+ ]]></body>
+ </method>
+
+ <!-- when the user hits escape, revert the previously typed value in the textbox -->
+ <method name="undoAutoComplete">
+ <body><![CDATA[
+ var val = this.currentSearchString;
+
+ var ok = this.onTextReverted();
+ if ((ok || ok == undefined) && val)
+ this.setTextValue(val);
+
+ this.userAction = "typing";
+
+ this.currentSearchString = this.value;
+ this.mNeedToComplete = false;
+ ]]></body>
+ </method>
+
+ <!-- convert an absolute result index into a session name/index pair -->
+ <method name="convertIndexToSession">
+ <parameter name="aIndex"/>
+ <body><![CDATA[
+ for (var name in this.mLastRows) {
+ if (aIndex < this.mLastRows[name])
+ return { session: name, index: aIndex };
+ aIndex -= this.mLastRows[name];
+ }
+ return null;
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: user input handling ::::::::::::: -->
+
+ <!-- -->
+ <method name="processInput">
+ <body><![CDATA[
+ // stop current lookup in case it's async.
+ this.stopLookup();
+ // stop the queued up lookup on a timer
+ this.clearTimer();
+
+ if (this.disableAutoComplete)
+ return;
+
+ this.userAction = "typing";
+ this.mFinishAfterSearch = false;
+ this.mNeedToFinish = true;
+ this.mTransientValue = false;
+ this.mNeedToComplete = true;
+ var str = this.value;
+ this.currentSearchString = str;
+ this.popup.clearSelection();
+
+ var timeout = this.mIsPasting ? this.pasteTimeout : this.timeout;
+ this.mAutoCompleteTimer = setTimeout(this.callListener, timeout, this, "startLookup");
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="processKeyPress">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ this.mLastKeyCode = aEvent.keyCode;
+
+ var killEvent = false;
+
+ switch (aEvent.keyCode) {
+ case KeyEvent.DOM_VK_TAB:
+ if (this.tabScrolling) {
+ // don't kill this event if alt-tab or ctrl-tab is hit
+ if (!aEvent.altKey && !aEvent.ctrlKey) {
+ killEvent = this.mMenuOpen;
+ if (killEvent)
+ this.keyNavigation(aEvent);
+ }
+ }
+ break;
+
+ case KeyEvent.DOM_VK_RETURN:
+
+ // if this is a failure item, save it for fireErrorCommand
+ var errItem = this.getErrorAt(this.popup.selectedIndex);
+
+ killEvent = this.mMenuOpen;
+ this.finishAutoComplete(true, true, aEvent);
+ this.closePopup();
+ if (errItem) {
+ this._fireEvent("errorcommand", errItem);
+ }
+ break;
+
+ case KeyEvent.DOM_VK_ESCAPE:
+ this.clearTimer();
+ killEvent = this.mMenuOpen;
+ this.undoAutoComplete();
+ this.closePopup();
+ break;
+
+ case KeyEvent.DOM_VK_LEFT:
+ case KeyEvent.DOM_VK_RIGHT:
+ case KeyEvent.DOM_VK_HOME:
+ case KeyEvent.DOM_VK_END:
+ this.finishAutoComplete(true, false, aEvent);
+ this.clearTimer();
+ this.closePopup();
+ break;
+
+ case KeyEvent.DOM_VK_DOWN:
+ if (!aEvent.altKey) {
+ this.clearTimer();
+ killEvent = this.keyNavigation(aEvent);
+ break;
+ }
+ // Alt+Down falls through to history popup toggling code
+
+ case KeyEvent.DOM_VK_F4:
+ if (!aEvent.ctrlKey && !aEvent.shiftKey && this.getAttribute("enablehistory") == "true") {
+ var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker");
+ if (historyPopup)
+ historyPopup.showPopup();
+ else
+ historyPopup.hidePopup();
+ }
+ break;
+ case KeyEvent.DOM_VK_PAGE_UP:
+ case KeyEvent.DOM_VK_PAGE_DOWN:
+ case KeyEvent.DOM_VK_UP:
+ if (!aEvent.ctrlKey && !aEvent.metaKey) {
+ this.clearTimer();
+ killEvent = this.keyNavigation(aEvent);
+ }
+ break;
+
+ case KeyEvent.DOM_VK_BACK_SPACE:
+ if (!aEvent.ctrlKey && !aEvent.altKey && !aEvent.shiftKey &&
+ this.selectionStart == this.currentSearchString.length &&
+ this.selectionEnd == this.value.length &&
+ this.mDefaultMatchFilled) {
+ this.mDefaultMatchFilled = false;
+ this.value = this.currentSearchString;
+ }
+
+ if (!/Mac/.test(navigator.platform))
+ break;
+ case KeyEvent.DOM_VK_DELETE:
+ if (/Mac/.test(navigator.platform) && !aEvent.shiftKey)
+ break;
+
+ if (this.mMenuOpen && this.popup.selectedIndex != -1) {
+ var obj = this.convertIndexToSession(this.popup.selectedIndex);
+ if (obj) {
+ var result = this.mLastResults[obj.session];
+ if (!result.errorDescription) {
+ var count = result.matchCount;
+ result.removeValueAt(obj.index, true);
+ this.view.updateResults(this.popup.selectedIndex, result.matchCount - count);
+ killEvent = true;
+ }
+ }
+ }
+ break;
+ }
+
+ if (killEvent) {
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ }
+
+ return true;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="processStartComposition">
+ <body><![CDATA[
+ this.finishAutoComplete(false, false, null);
+ this.clearTimer();
+ this.closePopup();
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="keyNavigation">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var k = aEvent.keyCode;
+ if (k == KeyEvent.DOM_VK_TAB ||
+ k == KeyEvent.DOM_VK_UP || k == KeyEvent.DOM_VK_DOWN ||
+ k == KeyEvent.DOM_VK_PAGE_UP || k == KeyEvent.DOM_VK_PAGE_DOWN)
+ {
+ if (!this.mMenuOpen) {
+ // Original xpfe style was to allow the up and down keys to have
+ // their default Mac action if the popup could not be opened.
+ // For compatibility for toolkit we now have to predict which
+ // keys have a default action that we can always allow to fire.
+ if (/Mac/.test(navigator.platform) &&
+ ((k == KeyEvent.DOM_VK_UP &&
+ (this.selectionStart != 0 ||
+ this.selectionEnd != 0)) ||
+ (k == KeyEvent.DOM_VK_DOWN &&
+ (this.selectionStart != this.value.length ||
+ this.selectionEnd != this.value.length))))
+ return false;
+ if (this.currentSearchString != this.value) {
+ this.processInput();
+ return true;
+ }
+ if (this.view.rowCount < this.minResultsForPopup)
+ return true; // used to be false, see above
+
+ this.mNeedToFinish = true;
+ this.openPopup();
+ return true;
+ }
+
+ this.userAction = "scrolling";
+ this.mNeedToComplete = false;
+
+ var reverse = k == KeyEvent.DOM_VK_TAB && aEvent.shiftKey ||
+ k == KeyEvent.DOM_VK_UP ||
+ k == KeyEvent.DOM_VK_PAGE_UP;
+ var page = k == KeyEvent.DOM_VK_PAGE_UP ||
+ k == KeyEvent.DOM_VK_PAGE_DOWN;
+ var selected = this.popup.selectBy(reverse, page);
+
+ // determine which value to place in the textbox
+ this.ignoreInputEvent = true;
+ if (selected != -1) {
+ if (this.getErrorAt(selected)) {
+ if (this.currentSearchString)
+ this.setTextValue(this.currentSearchString);
+ } else {
+ this.setTextValue(this.getResultValueAt(selected));
+ }
+ this.mTransientValue = true;
+ } else {
+ if (this.currentSearchString)
+ this.setTextValue(this.currentSearchString);
+ this.mTransientValue = false;
+ }
+
+ // move cursor to the end
+ this.mInputElt.setSelectionRange(this.value.length, this.value.length);
+ this.ignoreInputEvent = false;
+ }
+ return true;
+ ]]></body>
+ </method>
+
+ <!-- while the user is typing, fill the textbox with the "default" value
+ if one can be assumed, and select the end of the text -->
+ <method name="autoFillInput">
+ <parameter name="aSessionName"/>
+ <parameter name="aResults"/>
+ <parameter name="aUseFirstMatchIfNoDefault"/>
+ <body><![CDATA[
+ if (this.mInputElt.selectionEnd < this.currentSearchString.length ||
+ this.mDefaultMatchFilled)
+ return;
+
+ if (!this.mFinishAfterSearch &&
+ (this.autoFill || this.completeDefaultIndex) &&
+ this.mLastKeyCode != KeyEvent.DOM_VK_BACK_SPACE &&
+ this.mLastKeyCode != KeyEvent.DOM_VK_DELETE) {
+ var indexToUse = aResults.defaultIndex;
+ if (aUseFirstMatchIfNoDefault && indexToUse == -1)
+ indexToUse = 0;
+
+ if (indexToUse != -1) {
+ var resultValue = this.getSessionValueAt(aSessionName, indexToUse);
+ var match = resultValue.toLowerCase();
+ var entry = this.currentSearchString.toLowerCase();
+ this.ignoreInputEvent = true;
+ if (match.indexOf(entry) == 0) {
+ var endPoint = this.value.length;
+ this.setTextValue(this.value + resultValue.substr(endPoint));
+ this.mInputElt.setSelectionRange(endPoint, this.value.length);
+ } else {
+ if (this.completeDefaultIndex) {
+ this.setTextValue(this.value + " >> " + resultValue);
+ this.mInputElt.setSelectionRange(entry.length, this.value.length);
+ } else {
+ var postIndex = resultValue.indexOf(this.value);
+ if (postIndex >= 0) {
+ var startPt = this.value.length;
+ this.setTextValue(this.value +
+ resultValue.substr(startPt+postIndex));
+ this.mInputElt.setSelectionRange(startPt, this.value.length);
+ }
+ }
+ }
+ this.mNeedToComplete = true;
+ this.ignoreInputEvent = false;
+ this.mDefaultMatchFilled = true;
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: popup and tree ::::::::::::: -->
+
+ <!-- -->
+ <method name="openPopup">
+ <body><![CDATA[
+ if (!this.mMenuOpen && this.focused &&
+ (this.getResultCount() >= this.minResultsForPopup ||
+ this.mFailureItems)) {
+ var w = this.boxObject.width;
+ if (w != this.popup.boxObject.width)
+ this.popup.setAttribute("width", w);
+ this.popup.showPopup(this, -1, -1, "popup", "bottomleft", "topleft");
+ this.mMenuOpen = true;
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="closePopup">
+ <body><![CDATA[
+ if (this.popup && this.mMenuOpen) {
+ this.popup.hidePopup();
+ this.mMenuOpen = false;
+ }
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="addResultElements">
+ <parameter name="aSession"/>
+ <parameter name="aResults"/>
+ <body><![CDATA[
+ var count = aResults.errorDescription ? 1 : aResults.matchCount;
+ if (this.focused && this.showPopup) {
+ var row = 0;
+ for (var name in this.mSessions) {
+ row += this.mLastRows[name];
+ if (name == aSession)
+ break;
+ }
+ this.view.updateResults(row, count - this.mLastRows[name]);
+ this.popup.adjustHeight();
+ }
+ this.mLastResults[aSession] = aResults;
+ this.mLastRows[aSession] = count;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="clearResultElements">
+ <parameter name="aInvalidate"/>
+ <body><![CDATA[
+ for (var name in this.mSessions)
+ this.mLastRows[name] = 0;
+ this.view.clearResults();
+ if (aInvalidate)
+ this.popup.adjustHeight();
+
+ this.noMatch = true;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="setTextValue">
+ <parameter name="aValue"/>
+ <body><![CDATA[
+ this.value = aValue;
+
+ // Completing a result should simulate the user typing the result,
+ // so fire an input event.
+ var evt = document.createEvent("UIEvents");
+ evt.initUIEvent("input", true, false, window, 0);
+ var oldIgnoreInput = this.ignoreInputEvent;
+ this.ignoreInputEvent = true;
+ this.dispatchEvent(evt);
+ this.ignoreInputEvent = oldIgnoreInput;
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="clearResultData">
+ <body><![CDATA[
+ for (var name in this.mSessions)
+ this.mLastResults[name] = null;
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: miscellaneous ::::::::::::: -->
+
+ <!-- -->
+ <method name="ifSetAttribute">
+ <parameter name="aAttr"/>
+ <parameter name="aVal"/>
+ <body><![CDATA[
+ if (!this.hasAttribute(aAttr))
+ this.setAttribute(aAttr, aVal);
+ ]]></body>
+ </method>
+
+ <!-- -->
+ <method name="clearTimer">
+ <body><![CDATA[
+ if (this.mAutoCompleteTimer) {
+ clearTimeout(this.mAutoCompleteTimer);
+ this.mAutoCompleteTimer = 0;
+ }
+ ]]></body>
+ </method>
+
+ <!-- ::::::::::::: event dispatching ::::::::::::: -->
+
+ <method name="_fireEvent">
+ <parameter name="aEventType"/>
+ <parameter name="aEventParam"/>
+ <parameter name="aTriggeringEvent"/>
+ <body>
+ <![CDATA[
+ var noCancel = true;
+ // handle any xml attribute event handlers
+ var handler = this.getAttribute("on"+aEventType);
+ if (handler) {
+ var fn = new Function("eventParam", "domEvent", handler);
+ var returned = fn.apply(this, [aEventParam, aTriggeringEvent]);
+ if (returned == false)
+ noCancel = false;
+ }
+
+ return noCancel;
+ ]]>
+ </body>
+ </method>
+
+ <!-- =================== TREE VIEW =================== -->
+
+ <field name="view"><![CDATA[
+ ({
+ mTextbox: this,
+ mTree: null,
+ mSelection: null,
+ mRowCount: 0,
+
+ clearResults: function()
+ {
+ var oldCount = this.mRowCount;
+ this.mRowCount = 0;
+
+ if (this.mTree) {
+ this.mTree.rowCountChanged(0, -oldCount);
+ this.mTree.scrollToRow(0);
+ }
+ },
+
+ updateResults: function(aRow, aCount)
+ {
+ this.mRowCount += aCount;
+
+ if (this.mTree)
+ this.mTree.rowCountChanged(aRow, aCount);
+ },
+
+ //////////////////////////////////////////////////////////
+ // nsIAutoCompleteController interface
+
+ // this is the only method required by the treebody mouseup handler
+ handleEnter: function(aIsPopupSelection) {
+ this.mTextbox.onResultClick();
+ },
+
+ //////////////////////////////////////////////////////////
+ // nsITreeView interface
+
+ get rowCount() {
+ return this.mRowCount;
+ },
+
+ get selection() {
+ return this.mSelection;
+ },
+
+ set selection(aVal) {
+ return this.mSelection = aVal;
+ },
+
+ setTree: function(aTree)
+ {
+ this.mTree = aTree;
+ },
+
+ getCellText: function(aRow, aCol)
+ {
+ for (var name in this.mTextbox.mSessions) {
+ if (aRow < this.mTextbox.mLastRows[name]) {
+ var result = this.mTextbox.mLastResults[name];
+ switch (aCol.id) {
+ case "treecolAutoCompleteValue":
+ return result.errorDescription || result.getLabelAt(aRow);
+ case "treecolAutoCompleteComment":
+ if (!result.errorDescription)
+ return result.getCommentAt(aRow);
+ default:
+ return "";
+ }
+ }
+ aRow -= this.mTextbox.mLastRows[name];
+ }
+ return "";
+ },
+
+ getRowProperties: function(aIndex)
+ {
+ return "";
+ },
+
+ getCellProperties: function(aIndex, aCol)
+ {
+ // for the value column, append nsIAutoCompleteItem::className
+ // to the property list so that we can style this column
+ // using that property
+ if (aCol.id == "treecolAutoCompleteValue") {
+ for (var name in this.mTextbox.mSessions) {
+ if (aIndex < this.mTextbox.mLastRows[name]) {
+ var result = this.mTextbox.mLastResults[name];
+ if (result.errorDescription)
+ return "";
+ return result.getStyleAt(aIndex);
+ }
+ aIndex -= this.mTextbox.mLastRows[name];
+ }
+ }
+ return "";
+ },
+
+ getColumnProperties: function(aCol)
+ {
+ return "";
+ },
+
+ getImageSrc: function(aRow, aCol)
+ {
+ if (aCol.id == "treecolAutoCompleteValue") {
+ for (var name in this.mTextbox.mSessions) {
+ if (aRow < this.mTextbox.mLastRows[name]) {
+ var result = this.mTextbox.mLastResults[name];
+ if (result.errorDescription)
+ return "";
+ return result.getImageAt(aRow);
+ }
+ aRow -= this.mTextbox.mLastRows[name];
+ }
+ }
+ return "";
+ },
+
+ getParentIndex: function(aRowIndex) { },
+ hasNextSibling: function(aRowIndex, aAfterIndex) { },
+ getLevel: function(aIndex) {},
+ getProgressMode: function(aRow, aCol) {},
+ getCellValue: function(aRow, aCol) {},
+ isContainer: function(aIndex) {},
+ isContainerOpen: function(aIndex) {},
+ isContainerEmpty: function(aIndex) {},
+ isSeparator: function(aIndex) {},
+ isSorted: function() {},
+ toggleOpenState: function(aIndex) {},
+ selectionChanged: function() {},
+ cycleHeader: function(aCol) {},
+ cycleCell: function(aRow, aCol) {},
+ isEditable: function(aRow, aCol) {},
+ isSelectable: function(aRow, aCol) {},
+ setCellValue: function(aRow, aCol, aValue) {},
+ setCellText: function(aRow, aCol, aValue) {},
+ });
+ ]]></field>
+
+ </implementation>
+
+ <handlers>
+ <handler event="input"
+ action="if (!this.ignoreInputEvent) this.processInput();"/>
+
+ <handler event="keypress" phase="capturing"
+ action="return this.processKeyPress(event);"/>
+
+ <handler event="compositionstart" phase="capturing"
+ action="this.processStartComposition();"/>
+
+ <handler event="focus" phase="capturing"
+ action="this.userAction = 'typing';"/>
+
+ <handler event="blur" phase="capturing"
+ action="if ( !(this.ignoreBlurWhileSearching &amp;&amp; this.isSearching) ) {this.userAction = 'none'; this.finishAutoComplete(false, false, event);}"/>
+
+ <handler event="mousedown" phase="capturing"
+ action="if ( !this.mMenuOpen ) this.finishAutoComplete(false, false, event);"/>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-result-popup" extends="chrome://global/content/bindings/popup.xml#popup">
+ <resources>
+ <stylesheet src="chrome://communicator/content/autocomplete.css"/>
+ <stylesheet src="chrome://global/skin/autocomplete.css"/>
+ </resources>
+
+ <content ignorekeys="true" level="top">
+ <xul:tree anonid="tree" class="autocomplete-tree plain" flex="1">
+ <xul:treecols anonid="treecols">
+ <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteValue" flex="2"/>
+ <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteComment" flex="1" hidden="true"/>
+ </xul:treecols>
+ <xul:treechildren anonid="treebody" class="autocomplete-treebody"/>
+ </xul:tree>
+ </content>
+
+ <implementation implements="nsIAutoCompletePopup">
+ <constructor><![CDATA[
+ if (this.textbox && this.textbox.view)
+ this.initialize();
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ if (this.view)
+ this.tree.view = null;
+ ]]></destructor>
+
+ <field name="textbox">
+ document.getBindingParent(this);
+ </field>
+
+ <field name="tree">
+ document.getAnonymousElementByAttribute(this, "anonid", "tree");
+ </field>
+
+ <field name="treecols">
+ document.getAnonymousElementByAttribute(this, "anonid", "treecols");
+ </field>
+
+ <field name="treebody">
+ document.getAnonymousElementByAttribute(this, "anonid", "treebody");
+ </field>
+
+ <field name="view">
+ null
+ </field>
+
+ <!-- Setting tree.view doesn't always immediately create a selection,
+ so we ensure the selection by asking the tree for the view. Note:
+ this.view.selection is quicker if we know the selection exists. -->
+ <property name="selection" onget="return this.tree.view.selection;"/>
+
+ <property name="pageCount"
+ onget="return this.tree.treeBoxObject.getPageLength();"/>
+
+ <field name="maxRows">0</field>
+ <field name="mLastRows">0</field>
+
+ <method name="initialize">
+ <body><![CDATA[
+ this.showCommentColumn = this.textbox.showCommentColumn;
+ this.tree.view = this.textbox.view;
+ this.view = this.textbox.view;
+ this.maxRows = this.textbox.maxRows;
+ ]]></body>
+ </method>
+
+ <property name="showCommentColumn"
+ onget="return !this.treecols.lastChild.hidden;"
+ onset="this.treecols.lastChild.hidden = !val; return val;"/>
+
+ <method name="adjustHeight">
+ <body><![CDATA[
+ // detect the desired height of the tree
+ var bx = this.tree.treeBoxObject;
+ var view = this.view;
+ var rows = this.maxRows || 6;
+ if (!view.rowCount || (rows && view.rowCount < rows))
+ rows = view.rowCount;
+
+ var height = rows * bx.rowHeight;
+
+ if (height == 0)
+ this.tree.setAttribute("collapsed", "true");
+ else {
+ if (this.tree.hasAttribute("collapsed"))
+ this.tree.removeAttribute("collapsed");
+ this.tree.setAttribute("height", height);
+ }
+ ]]></body>
+ </method>
+
+ <method name="clearSelection">
+ <body>
+ this.selection.clearSelection();
+ </body>
+ </method>
+
+ <method name="getNextIndex">
+ <parameter name="aReverse"/>
+ <parameter name="aPage"/>
+ <parameter name="aIndex"/>
+ <parameter name="aMaxRow"/>
+ <body><![CDATA[
+ if (aMaxRow < 0)
+ return -1;
+
+ if (aIndex == -1)
+ return aReverse ? aMaxRow : 0;
+ if (aIndex == (aReverse ? 0 : aMaxRow))
+ return -1;
+
+ var amount = aPage ? this.pageCount - 1 : 1;
+ aIndex = aReverse ? aIndex - amount : aIndex + amount;
+ if (aIndex > aMaxRow)
+ return aMaxRow;
+ if (aIndex < 0)
+ return 0;
+ return aIndex;
+ ]]></body>
+ </method>
+
+ <!-- =================== nsIAutoCompletePopup =================== -->
+
+ <field name="input">
+ null
+ </field>
+
+ <!-- This property is meant to be overriden by bindings extending
+ this one. When the user selects an item from the list by
+ hitting enter or clicking, this method can set the value
+ of the textbox to a different value if it wants to. -->
+ <property name="overrideValue" readonly="true" onget="return null;"/>
+
+ <property name="selectedIndex">
+ <getter>
+ if (!this.view || !this.selection.count)
+ return -1;
+ var start = {}, end = {};
+ this.view.selection.getRangeAt(0, start, end);
+ return start.value;
+ </getter>
+ <setter>
+ if (this.view) {
+ this.selection.select(val);
+ if (val >= 0) {
+ this.view.selection.currentIndex = -1;
+ this.tree.treeBoxObject.ensureRowIsVisible(val);
+ }
+ }
+ return val;
+ </setter>
+ </property>
+
+ <property name="popupOpen" onget="return !!this.input;" readonly="true"/>
+
+ <method name="openAutocompletePopup">
+ <parameter name="aInput"/>
+ <parameter name="aElement"/>
+ <body><![CDATA[
+ if (!this.input) {
+ this.tree.view = aInput.controller;
+ this.view = this.tree.view;
+ this.showCommentColumn = aInput.showCommentColumn;
+ this.maxRows = aInput.maxRows;
+ this.invalidate();
+
+ var viewer = aElement.ownerGlobal
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .contentViewer;
+ var rect = aElement.getBoundingClientRect();
+ var width = Math.round((rect.right - rect.left) * viewer.fullZoom);
+ this.setAttribute("width", width > 100 ? width : 100);
+ // Adjust the direction (which is not inherited) of the autocomplete
+ // popup list, based on the textbox direction. (Bug 707039)
+ this.style.direction = aElement.ownerGlobal
+ .getComputedStyle(aElement)
+ .direction;
+ this.popupBoxObject.setConsumeRollupEvent(aInput.consumeRollupEvent
+ ? PopupBoxObject.ROLLUP_CONSUME
+ : PopupBoxObject.ROLLUP_NO_CONSUME);
+ this.openPopup(aElement, "after_start", 0, 0, false, false);
+ if (this.state != "closed")
+ this.input = aInput;
+ }
+ ]]></body>
+ </method>
+
+ <method name="closePopup">
+ <body>
+ this.hidePopup();
+ </body>
+ </method>
+
+ <method name="invalidate">
+ <body>
+ if (this.view)
+ this.adjustHeight();
+ this.tree.treeBoxObject.invalidate();
+ </body>
+ </method>
+
+ <method name="selectBy">
+ <parameter name="aReverse"/>
+ <parameter name="aPage"/>
+ <body><![CDATA[
+ try {
+ return this.selectedIndex = this.getNextIndex(aReverse, aPage, this.selectedIndex, this.view.rowCount - 1);
+ } catch (ex) {
+ // do nothing - occasionally timer-related js errors happen here
+ // e.g. "this.selectedIndex has no properties", when you type fast and hit a
+ // navigation key before this popup has opened
+ return -1;
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popupshowing">
+ if (this.textbox)
+ this.textbox.mMenuOpen = true;
+ </handler>
+
+ <handler event="popuphiding">
+ if (this.textbox)
+ this.textbox.mMenuOpen = false;
+ this.clearSelection();
+ this.input = null;
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-treebody">
+ <implementation>
+ <field name="popup">document.getBindingParent(this);</field>
+
+ <field name="mLastMoveTime">Date.now()</field>
+ </implementation>
+
+ <handlers>
+ <handler event="mouseout" action="this.popup.selectedIndex = -1;"/>
+
+ <handler event="mouseup"><![CDATA[
+ var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
+ if (rc != -1) {
+ this.popup.selectedIndex = rc;
+ this.popup.view.handleEnter(true);
+ }
+ ]]></handler>
+
+ <handler event="mousemove"><![CDATA[
+ if (Date.now() - this.mLastMoveTime > 30) {
+ var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
+ if (rc != -1 && rc != this.popup.selectedIndex)
+ this.popup.selectedIndex = rc;
+ this.mLastMoveTime = Date.now();
+ }
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="autocomplete-history-popup"
+ extends="chrome://global/content/bindings/popup.xml#popup-scrollbars">
+ <resources>
+ <stylesheet src="chrome://communicator/content/autocomplete.css"/>
+ <stylesheet src="chrome://global/skin/autocomplete.css"/>
+ </resources>
+
+ <implementation>
+ <method name="removeOpenAttribute">
+ <parameter name="parentNode"/>
+ <body><![CDATA[
+ parentNode.removeAttribute("open");
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popuphiding"><![CDATA[
+ setTimeout(this.removeOpenAttribute, 0, this.parentNode);
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="history-dropmarker" extends="chrome://global/content/bindings/general.xml#dropmarker">
+
+ <implementation>
+ <method name="showPopup">
+ <body><![CDATA[
+ var textbox = document.getBindingParent(this);
+ var kids = textbox.getElementsByClassName("autocomplete-history-popup");
+ if (kids.item(0) && textbox.getAttribute("open") != "true") { // Open history popup
+ var w = textbox.boxObject.width;
+ if (w != kids[0].boxObject.width)
+ kids[0].width = w;
+ kids[0].showPopup(textbox, -1, -1, "popup", "bottomleft", "topleft");
+ textbox.setAttribute("open", "true");
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="mousedown"><![CDATA[
+ this.showPopup();
+ ]]></handler>
+ </handlers>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/autocomplete/jar.mn b/comm/suite/components/autocomplete/jar.mn
new file mode 100644
index 0000000000..a1d10068f4
--- /dev/null
+++ b/comm/suite/components/autocomplete/jar.mn
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+toolkit.jar:
+ content/global/autocomplete.xml (content/autocomplete.xml)
+
+comm.jar:
+ content/communicator/autocomplete.css (content/autocomplete.css)
diff --git a/comm/suite/components/autocomplete/moz.build b/comm/suite/components/autocomplete/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/autocomplete/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/bindings/datetimepicker.xml b/comm/suite/components/bindings/datetimepicker.xml
new file mode 100644
index 0000000000..7475b6c04e
--- /dev/null
+++ b/comm/suite/components/bindings/datetimepicker.xml
@@ -0,0 +1,1316 @@
+<?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 bindings [
+<!ENTITY % datetimepickerDTD SYSTEM
+ "chrome://communicator/locale/datetimepicker.dtd">
+ %datetimepickerDTD;
+]>
+
+<bindings id="timepickerBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="datetimepicker-base"
+ extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+ <content align="center">
+ <xul:hbox class="datetimepicker-input-box" align="center"
+ xbl:inherits="context,disabled,readonly">
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-one"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/>
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-two"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/>
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-three"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-ampm"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ </xul:hbox>
+ <xul:spinbuttons anonid="buttons" xbl:inherits="disabled"
+ onup="this.parentNode._increaseOrDecrease(1);"
+ ondown="this.parentNode._increaseOrDecrease(-1);"/>
+ </content>
+
+ <implementation>
+ <field name="_dateValue">null</field>
+ <field name="_fieldOne">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-one");
+ </field>
+ <field name="_fieldTwo">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-two");
+ </field>
+ <field name="_fieldThree">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-three");
+ </field>
+ <field name="_fieldAMPM">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-ampm");
+ </field>
+ <field name="_separatorFirst">
+ document.getAnonymousElementByAttribute(this, "anonid", "sep-first");
+ </field>
+ <field name="_separatorSecond">
+ document.getAnonymousElementByAttribute(this, "anonid", "sep-second");
+ </field>
+ <field name="_lastFocusedField">null</field>
+ <field name="_hasEntry">true</field>
+ <field name="_valueEntered">false</field>
+ <field name="attachedControl">null</field>
+
+ <property name="_currentField" readonly="true">
+ <getter>
+ var focusedInput = document.activeElement;
+ if (focusedInput == this._fieldOne ||
+ focusedInput == this._fieldTwo ||
+ focusedInput == this._fieldThree ||
+ focusedInput == this._fieldAMPM)
+ return focusedInput;
+ return this._lastFocusedField || this._fieldOne;
+ </getter>
+ </property>
+
+ <property name="dateValue" onget="return new Date(this._dateValue);">
+ <setter>
+ <![CDATA[
+ if (!(val instanceof Date))
+ throw "Invalid Date";
+
+ this._setValueNoSync(val);
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="readOnly" onset="if (val) this.setAttribute('readonly', 'true');
+ else this.removeAttribute('readonly'); return val;"
+ onget="return this.getAttribute('readonly') == 'true';"/>
+
+ <method name="_fireEvent">
+ <parameter name="aEventName"/>
+ <parameter name="aTarget"/>
+ <body>
+ var event = document.createEvent("Events");
+ event.initEvent(aEventName, true, true);
+ return !aTarget.dispatchEvent(event);
+ </body>
+ </method>
+
+ <method name="_setValueOnChange">
+ <parameter name="aField"/>
+ <body>
+ <![CDATA[
+ if (!this._hasEntry)
+ return;
+
+ if (aField == this._fieldOne ||
+ aField == this._fieldTwo ||
+ aField == this._fieldThree) {
+ var value = Number(aField.value);
+ if (isNaN(value))
+ value = 0;
+
+ value = this._constrainValue(aField, value, true);
+ this._setFieldValue(aField, value);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_init">
+ <body/>
+ </method>
+
+ <constructor>
+ this._init();
+
+ var cval = this.getAttribute("value");
+ if (cval) {
+ try {
+ this.value = cval;
+ return;
+ } catch (ex) { }
+ }
+ this.dateValue = new Date();
+ </constructor>
+
+ <destructor>
+ if (this.attachedControl) {
+ this.attachedControl.attachedControl = null;
+ this.attachedControl = null;
+ }
+ </destructor>
+
+ </implementation>
+
+ <handlers>
+ <handler event="focus" phase="capturing">
+ <![CDATA[
+ var target = event.originalTarget;
+ if (target == this._fieldOne ||
+ target == this._fieldTwo ||
+ target == this._fieldThree ||
+ target == this._fieldAMPM)
+ this._lastFocusedField = target;
+ ]]>
+ </handler>
+
+ <handler event="keypress">
+ <![CDATA[
+ if (this._hasEntry && event.charCode &&
+ this._currentField != this._fieldAMPM &&
+ !(event.altKey || event.ctrlKey || event.metaKey) &&
+ (event.charCode < 48 || event.charCode > 57))
+ event.preventDefault();
+ ]]>
+ </handler>
+
+ <handler event="keypress" keycode="VK_UP">
+ if (this._hasEntry)
+ this._increaseOrDecrease(1);
+ </handler>
+ <handler event="keypress" keycode="VK_DOWN">
+ if (this._hasEntry)
+ this._increaseOrDecrease(-1);
+ </handler>
+
+ <handler event="input">
+ this._valueEntered = true;
+ </handler>
+
+ <handler event="change">
+ this._setValueOnChange(event.originalTarget);
+ </handler>
+ </handlers>
+
+ </binding>
+
+ <binding id="timepicker"
+#ifdef MOZ_SUITE
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datetimepicker-base">
+#else
+ extends="chrome://messenger/content/datetimepicker.xml#datetimepicker-base">
+#endif
+ <implementation>
+ <field name="is24HourClock">false</field>
+ <field name="hourLeadingZero">false</field>
+ <field name="minuteLeadingZero">true</field>
+ <field name="secondLeadingZero">true</field>
+ <field name="amIndicator">"AM"</field>
+ <field name="pmIndicator">"PM"</field>
+
+ <field name="hourField">null</field>
+ <field name="minuteField">null</field>
+ <field name="secondField">null</field>
+
+ <property name="value">
+ <getter>
+ <![CDATA[
+ var minute = this._dateValue.getMinutes();
+ if (minute < 10)
+ minute = "0" + minute;
+
+ var second = this._dateValue.getSeconds();
+ if (second < 10)
+ second = "0" + second;
+ return this._dateValue.getHours() + ":" + minute + ":" + second;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ var items = val.match(/^([0-9]{1,2})\:([0-9]{1,2})\:?([0-9]{1,2})?$/);
+ if (!items)
+ throw "Invalid Time";
+
+ var dt = this.dateValue;
+ dt.setHours(items[1]);
+ dt.setMinutes(items[2]);
+ dt.setSeconds(items[3] ? items[3] : 0);
+ this.dateValue = dt;
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="hour" onget="return this._dateValue.getHours();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 23)
+ throw "Invalid Hour";
+ this._setFieldValue(this.hourField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="minute" onget="return this._dateValue.getMinutes();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 59)
+ throw "Invalid Minute";
+ this._setFieldValue(this.minuteField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="second" onget="return this._dateValue.getSeconds();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 59)
+ throw "Invalid Second";
+ this._setFieldValue(this.secondField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="isPM">
+ <getter>
+ <![CDATA[
+ return (this.hour >= 12);
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ if (val) {
+ if (this.hour < 12)
+ this.hour += 12;
+ } else if (this.hour >= 12) {
+ this.hour -= 12;
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="hideSeconds">
+ <getter>
+ return (this.getAttribute("hideseconds") == "true");
+ </getter>
+ <setter>
+ if (val)
+ this.setAttribute("hideseconds", "true");
+ else
+ this.removeAttribute("hideseconds");
+ if (this.secondField)
+ this.secondField.parentNode.collapsed = val;
+ this._separatorSecond.collapsed = val;
+ return val;
+ </setter>
+ </property>
+ <property name="increment">
+ <getter>
+ <![CDATA[
+ var increment = this.getAttribute("increment");
+ increment = Number(increment);
+ if (isNaN(increment) || increment <= 0 || increment >= 60)
+ return 1;
+ return increment;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ if (typeof val == "number")
+ this.setAttribute("increment", val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <method name="_setValueNoSync">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ var dt = new Date(aValue);
+ if (!isNaN(dt)) {
+ this._dateValue = dt;
+ this.setAttribute("value", this.value);
+ this._updateUI(this.hourField, this.hour);
+ this._updateUI(this.minuteField, this.minute);
+ this._updateUI(this.secondField, this.second);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecrease">
+ <parameter name="aDir"/>
+ <body>
+ <![CDATA[
+ if (this.disabled || this.readOnly)
+ return;
+
+ var field = this._currentField;
+ if (this._valueEntered)
+ this._setValueOnChange(field);
+
+ if (field == this._fieldAMPM) {
+ this.isPM = !this.isPM;
+ this._fireEvent("change", this);
+ } else {
+ var oldval;
+ var change = aDir;
+ if (field == this.hourField) {
+ oldval = this.hour;
+ } else if (field == this.minuteField) {
+ oldval = this.minute;
+ change *= this.increment;
+ } else if (field == this.secondField) {
+ oldval = this.second;
+ }
+
+ var newval = this._constrainValue(field, oldval + change, false);
+
+ if (field == this.hourField)
+ this.hour = newval;
+ else if (field == this.minuteField)
+ this.minute = newval;
+ else if (field == this.secondField)
+ this.second = newval;
+
+ if (oldval != newval)
+ this._fireEvent("change", this);
+ }
+ field.select();
+ ]]>
+ </body>
+ </method>
+ <method name="_setFieldValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ if (aField == this.hourField)
+ this._dateValue.setHours(aValue);
+ else if (aField == this.minuteField)
+ this._dateValue.setMinutes(aValue);
+ else if (aField == this.secondField)
+ this._dateValue.setSeconds(aValue);
+
+ this.setAttribute("value", this.value);
+ this._updateUI(aField, aValue);
+
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ ]]>
+ </body>
+ </method>
+ <method name="_updateUI">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ this._valueEntered = false;
+
+ var prependZero = false;
+ if (aField == this.hourField) {
+ prependZero = this.hourLeadingZero;
+ if (!this.is24HourClock) {
+ if (aValue > 12)
+ aValue -= 12;
+ else if (aValue == 0)
+ aValue = 12;
+ this._fieldAMPM.value = this.isPM ? this.pmIndicator :
+ this.amIndicator;
+ }
+ } else if (aField == this.minuteField) {
+ prependZero = this.minuteLeadingZero;
+ } else if (aField == this.secondField) {
+ prependZero = this.secondLeadingZero;
+ }
+
+ if (prependZero && aValue < 10)
+ aField.value = "0" + aValue;
+ else
+ aField.value = aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_constrainValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <parameter name="aNoWrap"/>
+ <body>
+ <![CDATA[
+ // aNoWrap is true when the user entered a value, so just
+ // constrain within limits. If false, the value is being
+ // incremented or decremented, so wrap around values
+ var max = 60;
+ if (aField == this.hourField) {
+ max = 24;
+ // User input in the hour field should be adjusted as
+ // needed for 12-hour vs. 24-hour time.
+ if (aNoWrap && !this.is24HourClock) {
+ if (aValue && aValue < 12 && this.isPM)
+ aValue += 12;
+ else if (aValue == 12 && !this.isPM)
+ aValue = 0;
+ }
+ }
+ if (aValue < 0)
+ return aNoWrap ? 0 : max + aValue;
+ if (aValue >= max)
+ return aNoWrap ? max - 1 : aValue - max;
+ return aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_init">
+ <body>
+ <![CDATA[
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ this.hourField = this._fieldOne;
+ this.minuteField = this._fieldTwo;
+ this.secondField = this._fieldThree;
+
+ var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
+
+ // XXX TODO: The following hack should be fixed once Intl.Locale arrives in bug 1433303.
+ var locale = Services.locale.regionalPrefsLocales[0];
+ if (locale.includes("-u-"))
+ locale += "-ca-gregory-nu-latn";
+ else
+ locale += "-u-ca-gregory-nu-latn";
+ var dtf = new Services.intl.DateTimeFormat(locale, { timeStyle: "long" });
+
+ var pmTime = dtf.format(new Date(2000, 0, 1, 16, 7, 9));
+ var numberFields = pmTime.match(numberOrder);
+ if (numberFields) {
+ this._separatorFirst.value = numberFields[3];
+ this._separatorSecond.value = numberFields[5];
+ if (Number(numberFields[2]) > 12)
+ this.is24HourClock = true;
+ else
+ this.pmIndicator = numberFields[1] || numberFields[7];
+ }
+
+ var amTime = dtf.format(new Date(2000, 0, 1, 1, 7, 9));
+ numberFields = amTime.match(numberOrder);
+ if (numberFields) {
+ this.hourLeadingZero = (numberFields[2].length > 1);
+ this.minuteLeadingZero = (numberFields[4].length > 1);
+ this.secondLeadingZero = (numberFields[6].length > 1);
+
+ if (!this.is24HourClock) {
+ this.amIndicator = numberFields[1] || numberFields[7];
+ if (numberFields[1]) {
+ var mfield = this._fieldAMPM.parentNode;
+ var mcontainer = mfield.parentNode;
+ mcontainer.insertBefore(mfield, mcontainer.firstChild);
+ }
+ var size = (numberFields[1] || numberFields[7]).length;
+ if (this.pmIndicator.length > size)
+ size = this.pmIndicator.length;
+ this._fieldAMPM.size = size;
+ this._fieldAMPM.maxLength = size;
+ } else {
+ this._fieldAMPM.parentNode.collapsed = true;
+ }
+ }
+
+ this.hideSeconds = this.hideSeconds;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress">
+ <![CDATA[
+ // just allow any printable character to switch the AM/PM state
+ if (event.charCode && !this.disabled && !this.readOnly &&
+ this._currentField == this._fieldAMPM) {
+ this.isPM = !this.isPM;
+ this._fieldAMPM.select();
+ this._fireEvent("change", this);
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ </handlers>
+
+ </binding>
+
+ <binding id="datepicker"
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datetimepicker-base">
+ <implementation>
+ <field name="yearLeadingZero">false</field>
+ <field name="monthLeadingZero">true</field>
+ <field name="dateLeadingZero">true</field>
+
+ <field name="yearField"/>
+ <field name="monthField"/>
+ <field name="dateField"/>
+
+ <property name="value">
+ <getter>
+ <![CDATA[
+ var month = this._dateValue.getMonth();
+ month = (month < 9) ? month = "0" + ++month : month + 1;
+
+ var date = this._dateValue.getDate();
+ if (date < 10)
+ date = "0" + date;
+ return this._dateValue.getFullYear() + "-" + month + "-" + date;
+ ]]>
+
+ </getter>
+ <setter>
+ <![CDATA[
+ var results = val.match(/^([0-9]{1,4})\-([0-9]{1,2})\-([0-9]{1,2})$/);
+ if (!results)
+ throw "Invalid Date";
+
+ this.dateValue = new Date(results[1] + "/" + results[2] + "/" + results[3]);
+ this.setAttribute("value", this.value);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="year" onget="return this._dateValue.getFullYear();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 1 || valnum > 9999)
+ throw "Invalid Year";
+ this._setFieldValue(this.yearField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="month" onget="return this._dateValue.getMonth();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 11)
+ throw "Invalid Month";
+ this._setFieldValue(this.monthField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="date" onget="return this._dateValue.getDate();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 1 || valnum > 31)
+ throw "Invalid Date";
+ this._setFieldValue(this.dateField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="open" onget="return false;" onset="return val;"/>
+
+ <property name="displayedMonth" onget="return this.month;"
+ onset="this.month = val; return val;"/>
+ <property name="displayedYear" onget="return this.year;"
+ onset="this.year = val; return val;"/>
+
+ <method name="_setValueNoSync">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ var dt = new Date(aValue);
+ if (!isNaN(dt)) {
+ this._dateValue = dt;
+ this.setAttribute("value", this.value);
+ this._updateUI(this.yearField, this.year);
+ this._updateUI(this.monthField, this.month);
+ this._updateUI(this.dateField, this.date);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecrease">
+ <parameter name="aDir"/>
+ <body>
+ <![CDATA[
+ if (this.disabled || this.readOnly)
+ return;
+
+ var field = this._currentField;
+ if (this._valueEntered)
+ this._setValueOnChange(field);
+
+ var oldval;
+ if (field == this.yearField)
+ oldval = this.year;
+ else if (field == this.monthField)
+ oldval = this.month;
+ else if (field == this.dateField)
+ oldval = this.date;
+
+ var newval = this._constrainValue(field, oldval + aDir, false);
+
+ if (field == this.yearField)
+ this.year = newval;
+ else if (field == this.monthField)
+ this.month = newval;
+ else if (field == this.dateField)
+ this.date = newval;
+
+ if (oldval != newval)
+ this._fireEvent("change", this);
+ field.select();
+ ]]>
+ </body>
+ </method>
+ <method name="_setFieldValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ if (aField == this.yearField) {
+ let oldDate = this.date;
+ this._dateValue.setFullYear(aValue);
+ if (oldDate != this.date) {
+ this._dateValue.setDate(0);
+ this._updateUI(this.dateField, this.date);
+ }
+ } else if (aField == this.monthField) {
+ let oldDate = this.date;
+ this._dateValue.setMonth(aValue);
+ if (oldDate != this.date) {
+ this._dateValue.setDate(0);
+ this._updateUI(this.dateField, this.date);
+ }
+ } else if (aField == this.dateField) {
+ this._dateValue.setDate(aValue);
+ }
+
+ this.setAttribute("value", this.value);
+ this._updateUI(aField, aValue);
+
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ ]]>
+ </body>
+ </method>
+ <method name="_updateUI">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ this._valueEntered = false;
+
+ var prependZero = false;
+ if (aField == this.yearField) {
+ if (this.yearLeadingZero) {
+ aField.value = ("000" + aValue).slice(-4);
+ return;
+ }
+ } else if (aField == this.monthField) {
+ aValue++;
+ prependZero = this.monthLeadingZero;
+ } else if (aField == this.dateField) {
+ prependZero = this.dateLeadingZero;
+ }
+ if (prependZero && aValue < 10)
+ aField.value = "0" + aValue;
+ else
+ aField.value = aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_constrainValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <parameter name="aNoWrap"/>
+ <body>
+ <![CDATA[
+ // the month will be 1 to 12 if entered by the user, so subtract 1
+ if (aNoWrap && aField == this.monthField)
+ aValue--;
+
+ if (aField == this.dateField) {
+ if (aValue < 1)
+ return new Date(this.year, this.month + 1, 0).getDate();
+
+ var currentMonth = this.month;
+ var dt = new Date(this.year, currentMonth, aValue);
+ return (dt.getMonth() != currentMonth ? 1 : aValue);
+ }
+ var min = (aField == this.monthField) ? 0 : 1;
+ var max = (aField == this.monthField) ? 11 : 9999;
+ if (aValue < min)
+ return aNoWrap ? min : max;
+ if (aValue > max)
+ return aNoWrap ? max : min;
+ return aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_init">
+ <body>
+ <![CDATA[
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ // We'll default to YYYY/MM/DD to start.
+ var yfield = "input-one";
+ var mfield = "input-two";
+ var dfield = "input-three";
+ var twoDigitYear = false;
+ this.yearLeadingZero = true;
+ this.monthLeadingZero = true;
+ this.dateLeadingZero = true;
+
+ var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
+
+ // XXX TODO: The following hack should be fixed once Intl.Locale arrives in bug 1433303.
+ var locale = Services.locale.regionalPrefsLocales[0];
+ if (locale.includes("-u-"))
+ locale += "-ca-gregory-nu-latn";
+ else
+ locale += "-u-ca-gregory-nu-latn";
+ var dtf = new Services.intl.DateTimeFormat(locale, { dateStyle: "short" });
+
+ var dt = dtf.format(new Date(2002, 9, 4));
+ var numberFields = dt.match(numberOrder);
+ if (numberFields) {
+ this._separatorFirst.value = numberFields[3];
+ this._separatorSecond.value = numberFields[5];
+
+ var yi = 2, mi = 4, di = 6;
+
+ function fieldForNumber(i) {
+ if (i == 2)
+ return "input-one";
+ if (i == 4)
+ return "input-two";
+ return "input-three";
+ }
+
+ for (var i = 1; i < numberFields.length; i++) {
+ switch (Number(numberFields[i])) {
+ case 2:
+ twoDigitYear = true; // fall through
+ case 2002:
+ yi = i;
+ yfield = fieldForNumber(i);
+ break;
+ case 9:
+ case 10:
+ mi = i;
+ mfield = fieldForNumber(i);
+ break;
+ case 4:
+ di = i;
+ dfield = fieldForNumber(i);
+ break;
+ }
+ }
+
+ this.yearLeadingZero = (numberFields[yi].length > 1);
+ this.monthLeadingZero = (numberFields[mi].length > 1);
+ this.dateLeadingZero = (numberFields[di].length > 1);
+ }
+
+ this.yearField = document.getAnonymousElementByAttribute(this, "anonid", yfield);
+ if (!twoDigitYear)
+ this.yearField.parentNode.classList.add("datetimepicker-input-subbox", "datetimepicker-year");
+ this.monthField = document.getAnonymousElementByAttribute(this, "anonid", mfield);
+ this.dateField = document.getAnonymousElementByAttribute(this, "anonid", dfield);
+
+ this._fieldAMPM.parentNode.collapsed = true;
+ this.yearField.size = twoDigitYear ? 2 : 4;
+ this.yearField.maxLength = twoDigitYear ? 2 : 4;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ </binding>
+
+ <binding id="datepicker-grid"
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datepicker">
+ <content>
+ <vbox class="datepicker-mainbox"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox class="datepicker-monthbox" align="center">
+ <button class="datepicker-previous datepicker-button" type="repeat"
+ xbl:inherits="disabled"
+ oncommand="document.getBindingParent(this)._increaseOrDecreaseMonth(-1);"/>
+ <spacer flex="1"/>
+ <deck anonid="monthlabeldeck">
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ </deck>
+ <label anonid="yearlabel" class="datepicker-gridlabel"/>
+ <spacer flex="1"/>
+ <button class="datepicker-next datepicker-button" type="repeat"
+ xbl:inherits="disabled"
+ oncommand="document.getBindingParent(this)._increaseOrDecreaseMonth(1);"/>
+ </hbox>
+ <grid class="datepicker-grid" role="grid">
+ <columns>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ </columns>
+ <rows anonid="datebox">
+ <row anonid="dayofweekbox">
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ </content>
+
+ <implementation>
+ <field name="_hasEntry">false</field>
+ <field name="_weekStart">&firstdayofweek.default;</field>
+ <field name="_displayedDate">null</field>
+ <field name="_todayItem">null</field>
+
+ <field name="yearField">
+ document.getAnonymousElementByAttribute(this, "anonid", "yearlabel");
+ </field>
+ <field name="monthField">
+ document.getAnonymousElementByAttribute(this, "anonid", "monthlabeldeck");
+ </field>
+ <field name="dateField">
+ document.getAnonymousElementByAttribute(this, "anonid", "datebox");
+ </field>
+
+ <field name="_selectedItem">null</field>
+
+ <property name="selectedItem" onget="return this._selectedItem">
+ <setter>
+ <![CDATA[
+ if (!val.value)
+ return val;
+ if (val.parentNode.parentNode != this.dateField)
+ return val;
+
+ if (this._selectedItem)
+ this._selectedItem.removeAttribute("selected");
+ this._selectedItem = val;
+ val.setAttribute("selected", "true");
+ this._displayedDate.setDate(val.value);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="displayedMonth">
+ <getter>
+ return this._displayedDate.getMonth();
+ </getter>
+ <setter>
+ this._updateUI(this.monthField, val, true);
+ return val;
+ </setter>
+ </property>
+ <property name="displayedYear">
+ <getter>
+ return this._displayedDate.getFullYear();
+ </getter>
+ <setter>
+ this._updateUI(this.yearField, val, true);
+ return val;
+ </setter>
+ </property>
+
+ <method name="_init">
+ <body>
+ <![CDATA[
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ // XXX TODO: The following hack should be fixed once Intl.Locale arrives in bug 1433303.
+ var locale = Services.locale.regionalPrefsLocales[0];
+ if (locale.includes("-u-"))
+ locale += "-ca-gregory";
+ else
+ locale += "-u-ca-gregory";
+ var dtfMonth = new Services.intl.DateTimeFormat(locale, {month: "long", timeZone: "UTC"});
+ var dtfWeekday = new Services.intl.DateTimeFormat(locale, {weekday: "narrow"});
+
+ var monthLabel = this.monthField.firstChild;
+ var tempDate = new Date(Date.UTC(2005, 0, 1));
+ for (var month = 0; month < 12; month++) {
+ tempDate.setUTCMonth(month);
+ monthLabel.setAttribute("value", dtfMonth.format(tempDate));
+ monthLabel = monthLabel.nextSibling;
+ }
+
+ var fdow = Number(this.getAttribute("firstdayofweek"));
+ if (!isNaN(fdow) && fdow >= 0 && fdow <= 6)
+ this._weekStart = fdow;
+
+ var weekbox = document.getAnonymousElementByAttribute(this, "anonid", "dayofweekbox").childNodes;
+ var date = new Date();
+ date.setDate(date.getDate() - (date.getDay() - this._weekStart));
+ for (var i = 0; i < weekbox.length; i++) {
+ weekbox[i].value = dtfWeekday.format(date);
+ date.setDate(date.getDate() + 1);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_setValueNoSync">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ var dt = new Date(aValue);
+ if (!isNaN(dt)) {
+ this._dateValue = dt;
+ this.setAttribute("value", this.value);
+ this._updateUI();
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_updateUI">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <parameter name="aCheckMonth"/>
+ <body>
+ <![CDATA[
+ var date;
+ var currentMonth;
+ if (aCheckMonth) {
+ if (!this._displayedDate)
+ this._displayedDate = this.dateValue;
+
+ var expectedMonth = aValue;
+ if (aField == this.monthField) {
+ this._displayedDate.setMonth(aValue);
+ } else {
+ expectedMonth = this._displayedDate.getMonth();
+ this._displayedDate.setFullYear(aValue);
+ }
+
+ if (expectedMonth != -1 && expectedMonth != 12 &&
+ expectedMonth != this._displayedDate.getMonth()) {
+ // If the month isn't what was expected, then the month overflowed.
+ // Setting the date to 0 will go back to the last day of the right month.
+ this._displayedDate.setDate(0);
+ }
+
+ date = new Date(this._displayedDate);
+ currentMonth = this._displayedDate.getMonth();
+ } else {
+ var samemonth = (this._displayedDate &&
+ this._displayedDate.getMonth() == this.month &&
+ this._displayedDate.getFullYear() == this.year);
+ if (samemonth) {
+ var items = this.dateField.getElementsByAttribute("value", this.date);
+ if (items.length)
+ this.selectedItem = items[0];
+ return;
+ }
+
+ date = this.dateValue;
+ this._displayedDate = new Date(date);
+ currentMonth = this.month;
+ }
+
+ if (this._todayItem) {
+ this._todayItem.removeAttribute("today");
+ this._todayItem = null;
+ }
+
+ if (this._selectedItem) {
+ this._selectedItem.removeAttribute("selected");
+ this._selectedItem = null;
+ }
+
+ // Update the month and year title
+ this.monthField.selectedIndex = currentMonth;
+ this.yearField.setAttribute("value", date.getFullYear());
+
+ date.setDate(1);
+ var firstWeekday = (7 + date.getDay() - this._weekStart) % 7;
+ date.setDate(date.getDate() - firstWeekday);
+
+ var today = new Date();
+ var datebox = this.dateField;
+ for (var k = 1; k < datebox.childNodes.length; k++) {
+ var row = datebox.childNodes[k];
+ for (var i = 0; i < 7; i++) {
+ var item = row.childNodes[i];
+
+ if (currentMonth == date.getMonth()) {
+ item.value = date.getDate();
+
+ // highlight today
+ if (this._isSameDay(today, date)) {
+ this._todayItem = item;
+ item.setAttribute("today", "true");
+ }
+
+ // highlight the selected date
+ if (this._isSameDay(this._dateValue, date)) {
+ this._selectedItem = item;
+ item.setAttribute("selected", "true");
+ }
+ } else {
+ item.value = "";
+ }
+
+ date.setDate(date.getDate() + 1);
+ }
+ }
+
+ this._fireEvent("monthchange", this);
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecreaseDateFromEvent">
+ <parameter name="aEvent"/>
+ <parameter name="aDiff"/>
+ <body>
+ <![CDATA[
+ if (aEvent.originalTarget == this && !this.disabled && !this.readOnly) {
+ var newdate = this.dateValue;
+ newdate.setDate(newdate.getDate() + aDiff);
+ this.dateValue = newdate;
+ this._fireEvent("change", this);
+ }
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecreaseMonth">
+ <parameter name="aDir"/>
+ <body>
+ <![CDATA[
+ if (!this.disabled) {
+ var month = this._displayedDate ? this._displayedDate.getMonth() :
+ this.month;
+ this._updateUI(this.monthField, month + aDir, true);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_isSameDay">
+ <parameter name="aDate1"/>
+ <parameter name="aDate2"/>
+ <body>
+ <![CDATA[
+ return (aDate1 && aDate2 &&
+ aDate1.getDate() == aDate2.getDate() &&
+ aDate1.getMonth() == aDate2.getMonth() &&
+ aDate1.getFullYear() == aDate2.getFullYear());
+ ]]>
+ </body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+ <handler event="click">
+ <![CDATA[
+ if (event.button != 0 || this.disabled || this.readOnly)
+ return;
+
+ var target = event.originalTarget;
+ if (target.classList.contains("datepicker-gridlabel") &&
+ target != this.selectedItem) {
+ this.selectedItem = target;
+ this._dateValue = new Date(this._displayedDate);
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ this._fireEvent("change", this);
+
+ if (this.attachedControl && "open" in this.attachedControl)
+ this.attachedControl.open = false; // close the popup
+ }
+ ]]>
+ </handler>
+ <handler event="MozMousePixelScroll" preventdefault="true"/>
+ <handler event="DOMMouseScroll" preventdefault="true">
+ <![CDATA[
+ this._increaseOrDecreaseMonth(event.detail < 0 ? -1 : 1);
+ ]]>
+ </handler>
+ <handler event="keypress" keycode="VK_LEFT"
+ action="this._increaseOrDecreaseDateFromEvent(event, -1);"/>
+ <handler event="keypress" keycode="VK_RIGHT"
+ action="this._increaseOrDecreaseDateFromEvent(event, 1);"/>
+ <handler event="keypress" keycode="VK_UP"
+ action="this._increaseOrDecreaseDateFromEvent(event, -7);"/>
+ <handler event="keypress" keycode="VK_DOWN"
+ action="this._increaseOrDecreaseDateFromEvent(event, 7);"/>
+ <handler event="keypress" keycode="VK_PAGE_UP" preventdefault="true"
+ action="this._increaseOrDecreaseMonth(-1);"/>
+ <handler event="keypress" keycode="VK_PAGE_DOWN" preventdefault="true"
+ action="this._increaseOrDecreaseMonth(1);"/>
+ </handlers>
+ </binding>
+
+ <binding id="datepicker-popup" display="xul:menu"
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datepicker">
+ <content align="center">
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-box"
+ align="center"
+ allowevents="true"
+ xbl:inherits="context,disabled,readonly">
+ <xul:hbox class="datetimepicker-input-subbox" align="baseline">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-one"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/>
+ <xul:hbox class="datetimepicker-input-subbox" align="baseline">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-two"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/>
+ <xul:hbox class="datetimepicker-input-subbox" align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-three"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ <xul:hbox class="datetimepicker-input-subbox" align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-ampm"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ </xul:moz-input-box>
+ <xul:spinbuttons anonid="buttons" xbl:inherits="disabled" allowevents="true"
+ onup="this.parentNode._increaseOrDecrease(1);"
+ ondown="this.parentNode._increaseOrDecrease(-1);"/>
+ <xul:dropmarker class="datepicker-dropmarker" xbl:inherits="disabled"/>
+ <xul:panel onpopupshown="this.firstChild.focus();" level="top">
+ <xul:datepicker anonid="grid" type="grid" class="datepicker-popupgrid"
+ xbl:inherits="disabled,readonly,firstdayofweek"/>
+ </xul:panel>
+ </content>
+ <implementation>
+ <constructor>
+ var grid = document.getAnonymousElementByAttribute(this, "anonid", "grid");
+ this.attachedControl = grid;
+ grid.attachedControl = this;
+ grid._setValueNoSync(this._dateValue);
+ </constructor>
+ <property name="open" onget="return this.hasAttribute('open');">
+ <setter>
+ <![CDATA[
+ if (this.hasMenu())
+ this.openMenu(val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="displayedMonth">
+ <getter>
+ return document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedMonth;
+ </getter>
+ <setter>
+ document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedMonth = val;
+ return val;
+ </setter>
+ </property>
+ <property name="displayedYear">
+ <getter>
+ return document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedYear;
+ </getter>
+ <setter>
+ document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedYear = val;
+ return val;
+ </setter>
+ </property>
+ </implementation>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/bindings/findbar.xml b/comm/suite/components/bindings/findbar.xml
new file mode 100644
index 0000000000..dde9c5ebd9
--- /dev/null
+++ b/comm/suite/components/bindings/findbar.xml
@@ -0,0 +1,162 @@
+<?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/. -->
+
+<!--
+ SeaMonkey Flexible Findbar
+
+ The binding implemented here mostly works like its toolkit ancestor,
+ except that it will not appear during a manually triggered type ahead find
+ if accessibility.typeaheadfind.usefindbar is false, and the automatic
+ typeahead find is controlled by the accessibility.typeaheadfind.autostart
+ preference instead of the accessibility.typeaheadfind preference.
+
+ This allows the in status bar type ahead find to be used in place of the
+ findbar implementation and allows the in status bar type ahead find
+ to only need to cache the accessibility.typeaheadfind preference branch.
+-->
+
+<!DOCTYPE bindings>
+
+<bindings id="findbarBindings"
+ xmlns="http://www.mozilla.org/xbl">
+
+ <binding id="findbar"
+ extends="chrome://global/content/bindings/findbar.xml#findbar">
+ <implementation>
+ <constructor><![CDATA[
+ var prefsvc =
+ Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+
+ prefsvc.removeObserver("accessibility.typeaheadfind",
+ this._observer);
+ prefsvc.addObserver("accessibility.typeaheadfind.autostart",
+ this._suiteObserver);
+ prefsvc.addObserver("accessibility.typeaheadfind.usefindbar",
+ this._suiteObserver);
+
+ this._findAsYouType =
+ prefsvc.getBoolPref("accessibility.typeaheadfind.autostart");
+ this._useFindbar =
+ prefsvc.getBoolPref("accessibility.typeaheadfind.usefindbar");
+ ]]></constructor>
+
+ <field name="_suiteObserver"><![CDATA[({
+ _self: this,
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIObserver) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ observe: function(aSubject, aTopic, aPrefName) {
+ if (aTopic != "nsPref:changed")
+ return;
+
+ var prefsvc =
+ aSubject.QueryInterface(Ci.nsIPrefBranch);
+
+ switch (aPrefName) {
+ case "accessibility.typeaheadfind.autostart":
+ this._self._findAsYouType = prefsvc.getBoolPref(aPrefName);
+ this._self._updateBrowserWithState();
+ break;
+ case "accessibility.typeaheadfind.usefindbar":
+ this._self._useFindbar = prefsvc.getBoolPref(aPrefName);
+ break;
+ }
+ }
+ })]]></field>
+
+ <!-- This is necessary because the destructor isn't called when
+ we are removed from a document that is not destroyed. This
+ needs to be explicitly called in this case -->
+ <method name="destroy">
+ <body><![CDATA[
+ if (this._destroyed)
+ return;
+ this._destroyed = true;
+
+ this.browser = null;
+
+ var prefsvc =
+ Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+ prefsvc.removeObserver("accessibility.typeaheadfind.linksonly",
+ this._observer);
+ prefsvc.removeObserver("accessibility.typeaheadfind.casesensitive",
+ this._observer);
+ prefsvc.removeObserver("findbar.entireword", this._observer);
+ prefsvc.removeObserver("findbar.highlightAll", this._observer);
+ prefsvc.removeObserver("findbar.modalHighlight", this._observer);
+
+ prefsvc.removeObserver("accessibility.typeaheadfind.usefindbar",
+ this._suiteObserver);
+ prefsvc.removeObserver("accessibility.typeaheadfind.autostart",
+ this._suiteObserver);
+
+ // Clear all timers that might still be running.
+ this._cancelTimers();
+ ]]></body>
+ </method>
+
+ <method name="_updateBrowserWithState">
+ <body><![CDATA[
+ window.messageManager.broadcastAsyncMessage("Findbar:UpdateState", {
+ findMode: this._findMode,
+ findAsYouType: this._findAsYouType,
+ });
+ ]]></body>
+ </method>
+
+ <method name="receiveMessage">
+ <parameter name="aMessage"/>
+ <body><![CDATA[
+ switch (aMessage.name) {
+ case "Findbar:Mouseup":
+ if (!this.hidden && this._findMode != this.FIND_NORMAL)
+ this.close();
+ break;
+
+ case "Findbar:Keypress":
+ if (this._useFindbar)
+ return this._onBrowserKeypress(aMessage.data.fakeEvent,
+ aMessage.data.shouldFastFind);
+ break;
+ }
+ return undefined;
+ ]]></body>
+ </method>
+
+ <method name="startFastFind">
+ <parameter name="aMode"/>
+ <body><![CDATA[
+ if (this._findMode == aMode && this._quickFindTimeout) {
+ this._findField.select();
+ this._findField.focus();
+ return;
+ }
+
+ // Clear bar first, so that when openFindBar() calls setCaseSensitivity()
+ // it doesn't get confused by a lingering value
+ this._findField.value = "";
+
+ if (this._quickFindTimeout)
+ clearTimeout(this._quickFindTimeout);
+ this.open(aMode);
+ this._setFindCloseTimeout();
+ this._findField.select();
+ this._findField.focus();
+
+ this._updateStatusUI(this.nsITypeAheadFind.FIND_FOUND);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+</bindings>
diff --git a/comm/suite/components/bindings/general.xml b/comm/suite/components/bindings/general.xml
new file mode 100644
index 0000000000..9df42b3548
--- /dev/null
+++ b/comm/suite/components/bindings/general.xml
@@ -0,0 +1,37 @@
+<?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/. -->
+
+<bindings id="generalBindings"
+ 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="statusbarpanel-iconic" display="xul:button" role="xul:button"
+ extends="chrome://communicator/content/bindings/generalBindings.xml#statusbarpanel">
+ <content>
+ <xul:image class="statusbarpanel-icon" xbl:inherits="src,src=image"/>
+ </content>
+ </binding>
+
+ <binding id="statusbarpanel-iconic-text" display="xul:button" role="xul:button"
+ extends="chrome://communicator/content/bindings/generalBindings.xml#statusbarpanel">
+ <content>
+ <xul:image class="statusbarpanel-icon" xbl:inherits="src,src=image"/>
+ <xul:label class="statusbarpanel-text" xbl:inherits="value=label,crop"/>
+ </content>
+ </binding>
+
+ <binding id="statusbarpanel-backgroundbox" display="xul:button"
+ extends="chrome://communicator/content/bindings/general.xml#statusbarpanel-iconic-text">
+ <content>
+ <xul:hbox class="statusbarpanel-contentbox" xbl:inherits="dir">
+ <xul:image class="statusbarpanel-icon" xbl:inherits="src,src=image"/>
+ <xul:label class="statusbarpanel-text" xbl:inherits="value=label,crop"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/bindings/generalBindings.xml b/comm/suite/components/bindings/generalBindings.xml
new file mode 100644
index 0000000000..385e1166a2
--- /dev/null
+++ b/comm/suite/components/bindings/generalBindings.xml
@@ -0,0 +1,31 @@
+<?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/. -->
+
+<bindings id="generalBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="menu-vertical"
+ extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
+ <content>
+ <children includes="observes|template|menupopup|panel|tooltip"/>
+ <xul:hbox flex="1" align="center">
+ <xul:vbox flex="1" align="center">
+ <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,consumeanchor"/>
+ <xul:label class="toolbarbutton-text" crop="right" flex="1"
+ xbl:inherits="value=label,accesskey,crop,dragover-top,wrap"/>
+ <xul:label class="toolbarbutton-multiline-text" flex="1"
+ xbl:inherits="xbl:text=label,accesskey,wrap"/>
+ </xul:vbox>
+ <xul:dropmarker anonid="dropmarker" type="menu"
+ class="toolbarbutton-menu-dropmarker" xbl:inherits="disabled,label"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/bindings/jar.mn b/comm/suite/components/bindings/jar.mn
new file mode 100644
index 0000000000..6e231a38da
--- /dev/null
+++ b/comm/suite/components/bindings/jar.mn
@@ -0,0 +1,20 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/customizeToolbar.css (../customizeToolbar.css)
+ content/communicator/customizeToolbar.js (../customizeToolbar.js)
+* content/communicator/customizeToolbar.xhtml (../customizeToolbar.xhtml)
+ content/communicator/bindings/datetimepicker.xml (datetimepicker.xml)
+ content/communicator/bindings/findbar.xml (findbar.xml)
+ content/communicator/bindings/general.xml (general.xml)
+ content/communicator/bindings/generalBindings.xml (generalBindings.xml)
+ content/communicator/bindings/notification.xml (notification.xml)
+ content/communicator/bindings/numberbox.xml (numberbox.xml)
+ content/communicator/bindings/preferences.xml (preferences.xml)
+ content/communicator/bindings/spinbuttons.xml (spinbuttons.xml)
+* content/communicator/bindings/textbox.xml (textbox.xml)
+ content/communicator/bindings/toolbar.xml (toolbar.xml)
+ content/communicator/bindings/toolbar-xpfe.xml (toolbar-xpfe.xml)
+* content/communicator/bindings/prefwindow.xml (prefwindow.xml)
diff --git a/comm/suite/components/bindings/moz.build b/comm/suite/components/bindings/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/bindings/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/bindings/notification.xml b/comm/suite/components/bindings/notification.xml
new file mode 100644
index 0000000000..8965df67cb
--- /dev/null
+++ b/comm/suite/components/bindings/notification.xml
@@ -0,0 +1,2423 @@
+<?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 bindings [
+<!ENTITY % commNotificationDTD SYSTEM "chrome://communicator/locale/notification.dtd">
+ %commNotificationDTD;
+<!ENTITY % globalNotificationDTD SYSTEM "chrome://global/locale/notification.dtd">
+ %globalNotificationDTD;
+]>
+
+<bindings id="browserNotificationBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="browser-notificationbox"
+ extends="chrome://global/content/bindings/notification.xml#notificationbox">
+ <implementation implements="nsIObserver, nsIFormSubmitObserver, nsIWebProgressListener, nsIWebProgressListener2, nsIDOMEventListener">
+ <field name="_stringBundle" readonly="true">
+ <![CDATA[
+ Services.strings.createBundle("chrome://communicator/locale/notification.properties");
+ ]]>
+ </field>
+
+ <field name="_brandStringBundle" readonly="true">
+ <![CDATA[
+ Services.strings.createBundle("chrome://branding/locale/brand.properties");
+ ]]>
+ </field>
+
+ <field name="_placesBundle" readonly="true">
+ <![CDATA[
+ Services.strings.createBundle("chrome://communicator/locale/places/places.properties");
+ ]]>
+ </field>
+
+ <field name="wrappedJSObject">this</field>
+
+ <field name="_activeBrowser">null</field>
+
+ <property name="activeBrowser" readonly="true">
+ <getter>
+ <![CDATA[
+ if (!this._activeBrowser) {
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var browsers = this.getElementsByTagNameNS(XUL_NS, "browser");
+ for (var i = 0; this._activeBrowser = browsers.item(i); i++)
+ if (!this._activeBrowser.hidden)
+ break;
+ }
+ return this._activeBrowser;
+ ]]>
+ </getter>
+ </property>
+
+ <field name="_cwu">null</field>
+
+ <property name="contentWindowUtils" readonly="true">
+ <getter>
+ <![CDATA[
+ if (!this._cwu)
+ this._cwu = this.activeBrowser.contentWindow
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ return this._cwu;
+ ]]>
+ </getter>
+ </property>
+
+ <field name="usePrivateBrowsing" readonly="true">
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext)
+ .usePrivateBrowsing
+ </field>
+
+ <method name="onDocumentChange">
+ <body>
+ <![CDATA[
+ this.crashNotified = false;
+ if (this.popupCount) {
+ this.popupCount = 0;
+ this.notifyPopupCountChanged();
+ }
+ this.removeTransientNotifications();
+ ]]>
+ </body>
+ </method>
+
+ <method name="addProgressListener">
+ <body>
+ <![CDATA[
+ if (this.activeBrowser && !this._addedProgressListener) {
+ this.activeBrowser.webProgress
+ .addProgressListener(this, Ci.nsIWebProgress.NOTIFY_SECURITY |
+ Ci.nsIWebProgress.NOTIFY_LOCATION |
+ Ci.nsIWebProgress.NOTIFY_REFRESH);
+ this._addedProgressListener = true;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="lastMessage">"EnterInsecureMessage"</field>
+ <field name="lastState">0</field>
+
+ <method name="onSecurityChange">
+ <parameter name="aWebProgress"/>
+ <parameter name="aRequest"/>
+ <parameter name="aState"/>
+ <body>
+ <![CDATA[
+ if (aState < 0)
+ aState = this.lastState;
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ var pref = "security.warn_leaving_secure";
+ var message = "EnterInsecureMessage";
+ var priority = this.PRIORITY_WARNING_LOW;
+ var pane = "ssl_pane";
+ var buttons = [];
+ if (aState & nsIWebProgressListener.STATE_IS_SECURE) {
+ pref = "security.warn_entering_secure";
+ message = "EnterSecureMessage";
+ priority = this.PRIORITY_INFO_LOW;
+ } else if (aState & nsIWebProgressListener.STATE_IS_BROKEN) {
+ pref = "security.warn_viewing_mixed";
+ message = "MixedContentMessage";
+ priority = this.PRIORITY_CRITICAL_LOW;
+ }
+
+ if (aState & nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT &&
+ Services.prefs.getBoolPref("security.warn_mixed_active_content")) {
+ pref = "security.warn_mixed_active_content";
+ message = "MixedActiveContentMessage";
+ priority = this.PRIORITY_CRITICAL_LOW;
+ } else if (aState & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT &&
+ Services.prefs.getBoolPref("security.warn_mixed_active_content")) {
+ pref = "security.warn_mixed_active_content";
+ message = "BlockedActiveContentMessage";
+ priority = this.PRIORITY_INFO_LOW;
+ this.lastState = aState & ~nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
+ const nsIWebNavigation = Ci.nsIWebNavigation;
+ buttons = [{
+ label: this._stringBundle.GetStringFromName("SecurityKeepBlocking.label"),
+ accessKey: this._stringBundle.GetStringFromName("SecurityKeepBlocking.accesskey"),
+ callback: this.onSecurityChange.bind(this, null, null, -1)
+ }, {
+ label: this._stringBundle.GetStringFromName("SecurityUnblock.label"),
+ accessKey: this._stringBundle.GetStringFromName("SecurityUnblock.accesskey"),
+ callback: this.reloadPage.bind(this,
+ nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT |
+ nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE)
+ }];
+ } else if (aState & nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT &&
+ Services.prefs.getBoolPref("privacy.warn_tracking_content")) {
+ pref = "privacy.warn_tracking_content";
+ message = "TrackingContentMessage";
+ priority = this.PRIORITY_WARNING_LOW;
+ pane = "security_pane";
+ } else if (aState & nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT &&
+ Services.prefs.getBoolPref("privacy.warn_tracking_content")) {
+ pref = "privacy.warn_tracking_content";
+ message = "BlockedTrackingContentMessage";
+ priority = this.PRIORITY_INFO_LOW;
+ pane = "security_pane";
+ if (!this.usePrivateBrowsing) {
+ this.lastState = aState & ~nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
+ buttons = [{
+ label: this._stringBundle.GetStringFromName("SecurityKeepBlocking.label"),
+ accessKey: this._stringBundle.GetStringFromName("SecurityKeepBlocking.accesskey"),
+ callback: this.onSecurityChange.bind(this, null, null, -1)
+ }, {
+ label: this._stringBundle.GetStringFromName("SecurityUnblock.label"),
+ accessKey: this._stringBundle.GetStringFromName("SecurityUnblock.accesskey"),
+ callback: () => {
+ Services.perms.add(this.activeBrowser.currentURI,
+ "trackingprotection",
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+ this.reloadPage();
+ }
+ }];
+ }
+ } else if (aState & nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT &&
+ Services.prefs.getBoolPref("security.warn_mixed_display_content")) {
+ pref = "security.warn_mixed_display_content";
+ message = "MixedDisplayContentMessage";
+ priority = this.PRIORITY_WARNING_LOW;
+ } else if (aState & nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT &&
+ Services.prefs.getBoolPref("security.warn_mixed_display_content")) {
+ pref = "security.warn_mixed_display_content";
+ message = "BlockedDisplayContentMessage";
+ priority = this.PRIORITY_INFO_LOW;
+ }
+
+ if (this.lastMessage == message)
+ return false;
+
+ var box = this.getNotificationWithValue(this.lastMessage);
+ if (box)
+ box.close();
+
+ this.lastMessage = message;
+
+ if (!Services.prefs.getBoolPref(pref))
+ return true;
+
+ if ("goPreferences" in window) {
+ buttons.push({
+ label: this._stringBundle.GetStringFromName("SecurityPreferences.label"),
+ accessKey: this._stringBundle.GetStringFromName("SecurityPreferences.accesskey"),
+ callback: function() {
+ goPreferences(pane);
+ return true;
+ }
+ });
+ }
+ var text = this._stringBundle.GetStringFromName(message);
+ box = this.appendNotification(text, message, null, priority, buttons);
+ box.persistence = 1;
+ box.timeout = Date.now() + 20000; // 20 seconds
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="onLocationChange">
+ <parameter name="aWebProgress" />
+ <parameter name="aRequest" />
+ <parameter name="aLocation" />
+ <parameter name="aFlags" />
+ <body>
+ <![CDATA[
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ if (aWebProgress.DOMWindow == this.activeBrowser.contentWindow &&
+ !(aFlags & nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT))
+ this.onDocumentChange();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onRefreshAttempted">
+ <parameter name="aWebProgress" />
+ <parameter name="aURI" />
+ <parameter name="aDelay" />
+ <parameter name="aSameURI" />
+ <body>
+ <![CDATA[
+ if (Services.prefs.getBoolPref("accessibility.blockautorefresh")) {
+ let brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ let refreshButtonText =
+ this._stringBundle.GetStringFromName("refreshBlocked.goButton");
+ let refreshButtonAccesskey =
+ this._stringBundle.GetStringFromName("refreshBlocked.goButton.accesskey");
+ let message =
+ this._stringBundle.formatStringFromName(aSameURI ? "refreshBlocked.refreshLabel"
+ : "refreshBlocked.redirectLabel",
+ [brandShortName], 1);
+ let notification = this.getNotificationWithValue("refresh-blocked");
+ if (notification) {
+ notification.label = message;
+ } else {
+ let buttons = [{
+ label: refreshButtonText,
+ accessKey: refreshButtonAccesskey,
+ callback: function (aNotification, aButton) {
+ var refreshURI = aNotification.webProgress
+ .QueryInterface(Ci.nsIRefreshURI);
+ refreshURI.forceRefreshURI(aNotification.uri, null,
+ aNotification.delay, true);
+ }
+ }];
+ notification =
+ this.appendNotification(message, "refresh-blocked", null,
+ this.PRIORITY_INFO_MEDIUM, buttons);
+ }
+ // In the case of a meta refresh, the location has already
+ // changed. But in the case of an HTTP header refresh, the
+ // location changes synchronously after this call returns.
+ // Set the persistence to 1 temporarily to stop this from
+ // immediately clobbering the location bar (bug 516441),
+ // but set the persistence back to 0 as soon as possible.
+ setTimeout(function() { notification.persistence = 0; }, 0);
+ notification.persistence = 1;
+ notification.webProgress = aWebProgress;
+ notification.uri = aURI;
+ notification.delay = aDelay;
+ return false;
+ }
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="notify">
+ <parameter name="aFormElement"/>
+ <parameter name="aWindow"/>
+ <parameter name="aActionURI"/>
+ <parameter name="aCancel"/>
+ <body>
+ <![CDATA[
+ aCancel.value = false;
+ if (!aFormElement || !aWindow || !aActionURI)
+ return;
+
+ var browser = this.activeBrowser;
+
+ // inactive sidebar panel:
+ if (!browser || !browser.docShell || !browser.docShell.securityUI)
+ return;
+
+ // not our window:
+ if (aWindow.top != browser.contentWindow)
+ return;
+
+ // pref disabled:
+ if (!Services.prefs.getBoolPref("security.warn_submit_insecure"))
+ return;
+
+ // HTTPS uninteresting:
+ if (aActionURI.schemeIs("https"))
+ return;
+
+ // javascript doesn't hit the network:
+ if (aActionURI.schemeIs("javascript"))
+ return;
+
+ // PSM handles HTTPS source:
+ var uri;
+ try {
+ uri = aFormElement.nodePrincipal.URI;
+ } catch (e) {}
+ if (!uri)
+ uri = aFormElement.ownerDocument.documentURIObject;
+ if (uri.schemeIs("https"))
+ return;
+
+ var warn = { value: true };
+ var prompt = Services.prompt;
+ aCancel.value = prompt.confirmEx(
+ aWindow,
+ this._stringBundle.GetStringFromName("SecurityTitle"),
+ this._stringBundle.GetStringFromName("PostToInsecureFromInsecureMessage"),
+ prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
+ prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1,
+ this._stringBundle.GetStringFromName("PostToInsecureContinue"),
+ null, null,
+ this._stringBundle.GetStringFromName("PostToInsecureFromInsecureShowAgain"),
+ warn) != 0;
+ if (!aCancel.value)
+ Services.prefs.setBoolPref("security.warn_submit_insecure", warn.value);
+ ]]>
+ </body>
+ </method>
+
+ <method name="observe">
+ <parameter name="aSubject" />
+ <parameter name="aTopic" />
+ <parameter name="aData" />
+ <body>
+ <![CDATA[
+ var browser = this.activeBrowser;
+
+ // inactive sidebar panel:
+ if (!browser || !browser.docShell)
+ return;
+
+ switch (aTopic) {
+ case "indexedDB-permissions-prompt":
+ var requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
+ var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
+ if (contentWindow.top == browser.contentWindow)
+ this.promptIndexedDB(requestor, contentWindow, "permissions");
+ break;
+
+ case "indexedDB-quota-prompt":
+ var requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
+ var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
+ if (contentWindow.top == browser.contentWindow)
+ this.promptIndexedDB(requestor, contentWindow, "quota", aData);
+ break;
+
+ case "indexedDB-quota-cancel":
+ var requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor);
+ var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
+ if (contentWindow.top == browser.contentWindow)
+ this.cancelIndexedDB(requestor, contentWindow, "quota", aData);
+ break;
+
+ case "addon-install-blocked":
+ var installInfo = aSubject.wrappedJSObject;
+ if (installInfo.browser == browser)
+ this.addonInstallBlocked(installInfo);
+ break;
+
+ case "addon-install-complete":
+ var installInfo = aSubject.wrappedJSObject;
+ if (installInfo.browser == browser)
+ this.addonInstallComplete(installInfo);
+ break;
+
+ case "addon-install-disabled":
+ var installInfo = aSubject.wrappedJSObject;
+ if (installInfo.browser == browser)
+ this.addonInstallDisabled(installInfo);
+ break;
+
+ case "addon-install-failed":
+ var installInfo = aSubject.wrappedJSObject;
+ if (installInfo.browser == browser)
+ this.addonInstallFailed(installInfo);
+ break;
+
+ case "addon-install-started":
+ var installInfo = aSubject.wrappedJSObject;
+ if (installInfo.browser == browser)
+ this.addonInstallStarted(installInfo);
+ break;
+
+ case "offline-cache-update-completed":
+ var doc = browser.contentDocument;
+ if (!doc.documentElement)
+ break;
+
+ var manifest = doc.documentElement.getAttribute("manifest");
+ if (!manifest)
+ break;
+
+ try {
+ var manifestURI = Services.io.newURI(manifest,
+ doc.characterSet,
+ doc.documentURIObject);
+ var cacheUpdate =
+ aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
+ if (manifestURI.equals(cacheUpdate.manifestURI))
+ this.checkUsage(manifestURI);
+ } catch (e) {
+ alert(e);
+ }
+ break;
+
+ case "nsPref:changed":
+ if (aData == "privacy.popups.showBrowserMessage") {
+ if (Services.prefs.getBoolPref(aData))
+ return;
+
+ var popupNotification = this.getNotificationWithValue("popup-blocked");
+ if (popupNotification)
+ this.removeNotification(popupNotification);
+ }
+
+ if (aData == "dom.disable_open_during_load") {
+ // remove notifications when popup blocking has been turned off
+ if (Services.prefs.getBoolPref(aData) || !this.popupCount)
+ return;
+
+ var popupNotification = this.getNotificationWithValue("popup-blocked");
+ if (popupNotification)
+ this.removeNotification(popupNotification);
+ this.popupCount = 0;
+ this.notifyPopupCountChanged();
+ }
+ break;
+
+ case "perm-changed":
+ // If all permissions are cleared aSubject is null.
+ if (!aSubject)
+ return;
+
+ var permission = aSubject.QueryInterface(Ci.nsIPermission);
+ if (permission.type != "popup" || aData != "added" || !this.popupCount)
+ return;
+
+ if (permission.matchesURI(this.activeBrowser.currentURI, false)) {
+ var popupNotification = this.getNotificationWithValue("popup-blocked");
+ if (popupNotification)
+ this.removeNotification(popupNotification);
+ this.popupCount = 0;
+ this.notifyPopupCountChanged();
+ }
+ break;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="CrashSubmit">null</field>
+
+ <field name="crashNotified">false</field>
+
+ <method name="openURLPref">
+ <parameter name="aPref"/>
+ <body>
+ <![CDATA[
+ var url = Services.urlFormatter.formatURLPref(aPref);
+ var nsIBrowserDOMWindow = Ci.nsIBrowserDOMWindow;
+
+ var browserWin;
+ var whereToOpen = Services.prefs.getIntPref("browser.link.open_external");
+
+ if (whereToOpen != nsIBrowserDOMWindow.OPEN_NEWWINDOW) {
+ browserWin = Services.wm.getMostRecentWindow("navigator:browser");
+ }
+
+ if (!browserWin) {
+ var browserURL = "chrome://navigator/content/navigator.xul";
+ try {
+ browserURL = Services.prefs.getCharPref("browser.chromeURL");
+ } catch (ex) {}
+
+ window.openDialog(browserURL, "_blank", "chrome,all,dialog=no", url);
+ } else {
+ if (whereToOpen == nsIBrowserDOMWindow.OPEN_CURRENTWINDOW)
+ browserWin.loadURI(url);
+ else {
+ // new tab
+ var browser = browserWin.getBrowser();
+ var newTab = browser.addTab(url);
+ browser.selectedTab = newTab;
+ }
+ browserWin.content.focus();
+ }
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="makeNicePluginName">
+ <parameter name="aName"/>
+ <body>
+ <![CDATA[
+ // Clean up the plugin name by stripping off any trailing version
+ // numbers or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
+ // Do this by first stripping the numbers, etc. off the end, and
+ // then removing "Plugin" (and then trimming to get rid of any
+ // whitespace). Otherwise, something like "Java(TM) Plug-in
+ // 1.7.0_07" gets mangled.
+ var newName = aName.replace(/[\s\d\.\-\_\(\)]+$/, "").replace(/\bplug-?in\b/i, "").trim();
+ return newName;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getPluginUI">
+ <parameter name="aPlugin"/>
+ <parameter name="aAnonId"/>
+ <body>
+ <![CDATA[
+ return aPlugin.ownerDocument.getAnonymousElementByAttribute(aPlugin, "anonid", aAnonId);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addLinkClickCallback">
+ <parameter name="linkNode"/>
+ <parameter name="callback"/>
+ <body>
+ <![CDATA[
+ // XXX just doing (callback)(arg) was giving a same-origin error. bug?
+
+ // We use event bubbling for the event listeners.
+ let callbackArgs = Array.from(arguments).slice(2);
+ linkNode.addEventListener("click",
+ function(aEvent) {
+ if (!aEvent.isTrusted)
+ return;
+ if (aEvent.button != 0)
+ return;
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ if (callbackArgs.length == 0)
+ callbackArgs = [ aEvent ];
+ callback.apply(this, callbackArgs);
+ }.bind(this));
+
+ linkNode.addEventListener("keydown",
+ function(aEvent) {
+ if (!aEvent.isTrusted)
+ return;
+ if (aEvent.keyCode != aEvent.DOM_VK_RETURN)
+ return;
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ if (callbackArgs.length == 0)
+ callbackArgs = [ aEvent ];
+ callback.apply(this, callbackArgs);
+ }.bind(this));
+ ]]>
+ </body>
+ </method>
+
+ <!-- Callback for user clicking "submit a report" link -->
+ <method name="submitReport">
+ <parameter name="pluginDumpID"/>
+ <parameter name="plugin"/>
+ <body>
+ <![CDATA[
+ var keyVals = {};
+ if (plugin) {
+ let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
+ if (userComment)
+ keyVals.PluginUserComment = userComment;
+ if (this.getPluginUI(plugin, "submitURLOptIn").checked)
+ keyVals.PluginContentURL = plugin.ownerDocument.URL;
+ }
+ this.CrashSubmit.submit(pluginDumpID, { extraExtraKeyVals: keyVals,
+ recordSubmission: true });
+ ]]>
+ </body>
+ </method>
+
+ <!-- Callback for user clicking a "reload page" link -->
+ <method name="reloadPage">
+ <parameter name="flags"/>
+ <body>
+ <![CDATA[
+ this.activeBrowser.reloadWithFlags(flags);
+ ]]>
+ </body>
+ </method>
+
+ <!-- Callback for user clicking the help icon -->
+ <method name="openHelpPage">
+ <body>
+ <![CDATA[
+ //XXX Ratty need in app help here.
+ openHelp("plugin-crashed", "chrome://communicator/locale/help/suitehelp.rdf");
+ ]]>
+ </body>
+ </method>
+
+ <method name="showPluginCrashedNotification">
+ <parameter name="pluginDumpID"/>
+ <parameter name="messageString"/>
+ <body>
+ <![CDATA[
+ // If there's already an existing notification bar, don't do anything.
+ var notification = this.getNotificationWithValue("plugin-crashed");
+ if (notification)
+ return;
+
+ // Configure the notification bar
+ var priority = this.PRIORITY_WARNING_MEDIUM;
+ var reloadLabel = this._stringBundle.GetStringFromName("crashedpluginsMessage.reloadButton.label");
+ var reloadKey = this._stringBundle.GetStringFromName("crashedpluginsMessage.reloadButton.accesskey");
+ var submitLabel = this._stringBundle.GetStringFromName("crashedpluginsMessage.submitButton.label");
+ var submitKey = this._stringBundle.GetStringFromName("crashedpluginsMessage.submitButton.accesskey");
+
+ var buttons = [{
+ label: reloadLabel,
+ accessKey: reloadKey,
+ popup: null,
+ callback: this.reloadPage.bind(this)
+ }];
+
+ if (this.CrashSubmit && pluginDumpID) {
+ let submitButton = {
+ label: submitLabel,
+ accessKey: submitKey,
+ popup: null,
+ callback: this.submitReport.bind(this, pluginDumpID)
+ };
+ buttons.push(submitButton);
+ }
+
+ var notification = this.appendNotification(messageString, "plugin-crashed",
+ null, priority, buttons);
+
+ // Add the "learn more" link.
+ var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var link = notification.ownerDocument.createElementNS(XULNS, "label");
+ link.className = "text-link";
+ link.setAttribute("value", this._stringBundle.GetStringFromName("crashedpluginsMessage.learnMore"));
+ this.addLinkClickCallback(link, this.openHelpPage);
+ var description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
+ description.appendChild(link);
+ ]]>
+ </body>
+ </method>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ if (!aEvent.isTrusted)
+ return;
+ ]]>
+ </body>
+ </method>
+
+ <method name="playSoundForBlockedPopup">
+ <body>
+ <![CDATA[
+ const kCustomSound = 1;
+ var playSound = Services.prefs.getBoolPref("privacy.popups.sound_enabled");
+
+ if (playSound) {
+ var sound = Cc["@mozilla.org/sound;1"]
+ .createInstance(Ci.nsISound);
+
+ var soundType = Services.prefs.getIntPref("privacy.popups.sound_type");
+ if (soundType == kCustomSound) {
+ var soundUrlSpec = Services.prefs.getCharPref("privacy.popups.sound_url");
+ var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]
+ .getService(Ci.nsIFileProtocolHandler);
+ var file = fileHandler.getFileFromURLSpec(soundUrlSpec);
+ if (file.exists()) {
+ var soundUrl = fileHandler.newFileURI(file);
+ sound.play(soundUrl);
+ return;
+ }
+ }
+
+ // Either a custom sound is selected which does not exist
+ // or the system beep was selected, so make the system beep.
+ sound.beep();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="popupCount">0</field>
+
+ <method name="notifyPopupCountChanged">
+ <body>
+ <![CDATA[
+ this.dispatchEvent(new Event("PopupCountChanged",
+ { bubbles: true, cancelable: true }));
+ ]]>
+ </body>
+ </method>
+
+ <method name="allowPopupsForSite">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ Services.perms.add(this.activeBrowser.currentURI, "popup",
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+
+ this.removeCurrentNotification();
+ ]]>
+ </body>
+ </method>
+
+ <method name="offlineAppRequested">
+ <parameter name="aDocument"/>
+ <body>
+ <![CDATA[
+ var documentURI = aDocument.documentURIObject;
+ var pm = Services.perms;
+ if (pm.testExactPermission(documentURI, "offline-app") !=
+ Ci.nsIPermissionManager.UNKNOWN_ACTION)
+ return;
+
+ var host = documentURI.asciiHost;
+ var notificationName = "offline-app-requested-" + host;
+ var notification = this.getNotificationWithValue(notificationName);
+ if (notification)
+ notification.documents.push(aDocument);
+ else {
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("offlineApps.always"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.always.accesskey"),
+ callback: function() {
+ pm.add(documentURI, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
+ var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
+ .getService(Ci.nsIOfflineCacheUpdateService);
+ notification.documents.forEach(function(aDocument) {
+ if (!aDocument.documentElement)
+ return;
+ var manifest = aDocument.documentElement.getAttribute("manifest");
+ if (!manifest)
+ return;
+
+ try {
+ var manifestURI =
+ Services.io.newURI(manifest,
+ aDocument.characterSet,
+ aDocument.documentURIObject);
+ updateService.scheduleUpdate(manifestURI,
+ aDocument.documentURIObject,
+ window);
+ } catch (e) {
+ }
+ });
+ }
+ }, {
+ label: this._stringBundle.GetStringFromName("offlineApps.never"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.never.accesskey"),
+ callback: function() {
+ pm.add(documentURI, "offline-app", Ci.nsIPermissionManager.DENY_ACTION);
+ }
+ }, {
+ label: this._stringBundle.GetStringFromName("offlineApps.later"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.later.accesskey"),
+ callback: function() { /* no-op */ }
+ }];
+
+ var messageString = this._stringBundle.formatStringFromName(this.usePrivateBrowsing ?
+ "offlineApps.private" : "offlineApps.permissions", [host], 1);
+ var priority = this.PRIORITY_INFO_LOW;
+ notification = this.appendNotification(messageString, notificationName,
+ null, priority,
+ this.usePrivateBrowsing ?
+ null : buttons);
+ notification.documents = [aDocument];
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="checkUsage">
+ <parameter name="aURI"/>
+ <body>
+ <![CDATA[
+ if (Services.perms.testExactPermission(aURI, "offline-app") ==
+ Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN)
+ return;
+
+ var host = aURI.asciiHost;
+ var usage = 0;
+
+ var cacheService = Cc["@mozilla.org/network/application-cache-service;1"]
+ .getService(Ci.nsIApplicationCacheService);
+ cacheService.getGroups().forEach(function(aGroup) {
+ var uri = Services.io.newURI(aGroup);
+ if (uri.asciiHost == host)
+ usage += cacheService.getActiveCache(aGroup).usage;
+ });
+ var warnQuota = Services.prefs.getIntPref("offline-apps.quota.warn");
+ if (usage < warnQuota * 1024)
+ return;
+
+ var message = this._stringBundle.formatStringFromName("offlineApps.quota", [host, warnQuota / 1024], 2);
+ var priority = this.PRIORITY_WARNING_MEDIUM;
+ this.appendNotification(message, "offline-app-usage", null,
+ priority, null);
+ Services.perms.add(aURI, "offline-app",
+ Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
+ ]]>
+ </body>
+ </method>
+
+ <method name="showRightsNotification">
+ <body>
+ <![CDATA[
+ var rightsBundle = Services.strings.createBundle("chrome://branding/locale/aboutRights.properties");
+ var buttonLabel = rightsBundle.GetStringFromName("buttonLabel");
+ var buttonAccessKey = rightsBundle.GetStringFromName("buttonAccessKey");
+ var productName = this._brandStringBundle.GetStringFromName("brandFullName");
+ var notifyRightsText = rightsBundle.formatStringFromName("notifyRightsText2", [productName], 1);
+
+ var buttons = [{
+ label: buttonLabel,
+ accessKey: buttonAccessKey,
+ popup: null,
+ callback: function (aNotificationBox, aButton) {
+ var browser = document.getBindingParent(aNotificationBox);
+ browser.addTab("about:rights", { focusNewTab: true });
+ }
+ }];
+ var box = this.appendNotification(notifyRightsText, "about-rights",
+ null, this.PRIORITY_INFO_LOW,
+ buttons);
+ box.persistence = 3; // arbitrary number, just so bar sticks around for a bit
+ ]]>
+ </body>
+ </method>
+
+ <method name="showPlacesLockedWarning">
+ <body>
+ <![CDATA[
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var message = this._placesBundle.formatStringFromName("lockPrompt.text", [brandShortName], 1);
+ var buttons = [{
+ label: this._placesBundle.GetStringFromName("lockPromptInfoButton.label"),
+ accessKey: this._placesBundle.GetStringFromName("lockPromptInfoButton.accesskey"),
+ popup: null,
+ callback: function() {
+ openHelp("places-locked", "chrome://communicator/locale/help/suitehelp.rdf");
+ }
+ }];
+ var box = this.appendNotification(message, "places-locked", null,
+ this.PRIORITY_CRITICAL_MEDIUM,
+ buttons);
+ box.persistence = -1; // until user closes it
+ ]]>
+ </body>
+ </method>
+
+ <method name="showUpdateWarning">
+ <body>
+ <![CDATA[
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var message = this._stringBundle.formatStringFromName("updatePrompt.text", [brandShortName], 1);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("updatePromptCheckButton.label"),
+ accessKey: this._stringBundle.GetStringFromName("updatePromptCheckButton.accesskey"),
+ popup: null,
+ callback: function() {
+ Cc["@mozilla.org/updates/update-prompt;1"]
+ .createInstance(Ci.nsIUpdatePrompt)
+ .checkForUpdates();
+ }
+ }];
+ var box = this.appendNotification(message, "update-warning", null,
+ this.PRIORITY_CRITICAL_MEDIUM,
+ buttons);
+ box.persistence = -1; // until user closes it
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeNotifications">
+ <parameter name="aNotifications"/>
+ <body>
+ <![CDATA[
+ aNotifications.forEach(function(value) {
+ var box = this.getNotificationWithValue(value);
+ if (box)
+ this.removeNotification(box);
+ }, this);
+ ]]>
+ </body>
+ </method>
+
+ <method name="lwthemeInstallRequest">
+ <parameter name="aHost"/>
+ <parameter name="aCallback"/>
+ <body>
+ <![CDATA[
+ var message = this._stringBundle.formatStringFromName("lwthemeInstallRequest.message", [aHost], 1);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton.accesskey"),
+ popup: null,
+ callback: aCallback
+ }];
+ var box = this.appendNotification(message,
+ "lwtheme-install-request", null,
+ this.PRIORITY_INFO_MEDIUM,
+ buttons);
+ box.persistence = 1;
+ ]]>
+ </body>
+ </method>
+
+ <method name="lwthemeInstallNotification">
+ <parameter name="aCallback"/>
+ <body>
+ <![CDATA[
+ var message = this._stringBundle.GetStringFromName("lwthemeInstallNotification.message");
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton.accesskey"),
+ callback: aCallback
+ }, {
+ label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton.accesskey"),
+ callback: function() {
+ window.toEM("addons://list/theme");
+ }
+ }];
+ var box = this.appendNotification(message,
+ "lwtheme-install-notification",
+ null, this.PRIORITY_INFO_MEDIUM,
+ buttons);
+ box.persistence = 1;
+ box.timeout = Date.now() + 20000; // 20 seconds
+ ]]>
+ </body>
+ </method>
+
+ <method name="lwthemeNeedsRestart">
+ <parameter name="aNewThemeName"/>
+ <body>
+ <![CDATA[
+ var message = this._stringBundle.formatStringFromName("lwthemeNeedsRestart.message", [aNewThemeName], 1);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton.accesskey"),
+ popup: null,
+ callback: function() {
+ BrowserUtils.restartApplication();
+ }
+ }];
+ var box = this.appendNotification(message,
+ "lwtheme-install-notification",
+ null,this.PRIORITY_INFO_MEDIUM,
+ buttons);
+ box.persistence = 1;
+ box.timeout = Date.now() + 20000; // 20 seconds
+ ]]>
+ </body>
+ </method>
+
+ <method name="promptIndexedDB">
+ <parameter name="aRequestor"/>
+ <parameter name="aWindow"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ var host = aWindow.document.documentURIObject.asciiHost;
+ var property = this.usePrivateBrowsing ? "offlineApps.private" :
+ "offlineApps." + aTopic;
+ var message = this._stringBundle.formatStringFromName(property,
+ [host, aData], 2);
+ var observer = aRequestor.getInterface(Ci.nsIObserver);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("offlineApps.always"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.always.accesskey"),
+ popup: null,
+ callback: function allowIndexedDB() {
+ clearTimeout(box.timeout);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+ }
+ }, {
+ label: this._stringBundle.GetStringFromName("offlineApps.never"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.never.accesskey"),
+ popup: null,
+ callback: function denyIndexedDB() {
+ clearTimeout(box.timeout);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.DENY_ACTION);
+ }
+ }, {
+ label: this._stringBundle.GetStringFromName("offlineApps.later"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.later.accesskey"),
+ popup: null,
+ callback: function laterIndexedDB() {
+ clearTimeout(box.timeout);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ }
+ }];
+ var box = this.appendNotification(message,
+ "indexedDB-" + aTopic + "-prompt",
+ null, this.PRIORITY_INFO_LOW,
+ this.usePrivateBrowsing ?
+ null : buttons);
+ box.timeout = setTimeout(function() {
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ if (box.parentNode)
+ box.parentNode.removeNotification(box);
+ }, 300000); // 5 minutes
+ ]]>
+ </body>
+ </method>
+
+ <method name="cancelIndexedDB">
+ <parameter name="aRequestor"/>
+ <parameter name="aWindow"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ var popupNotification = this.getNotificationWithValue("indexedDB-" + aTopic + "-prompt");
+ if (popupNotification) {
+ clearTimeout(popupNotification.timeout);
+ this.removeNotification(popupNotification);
+ }
+
+ var observer = aRequestor.getInterface(Ci.nsIObserver);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallBlocked">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var host;
+ try {
+ // this fails with nsSimpleURIs like data: URIs
+ host = installInfo.originatingURI.host;
+ } catch (ex) {
+ host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
+ }
+ var notificationName = "addon-install-blocked";
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var messageString = this._stringBundle.formatStringFromName("xpinstallPromptWarning",
+ [brandShortName, host], 2);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton"),
+ accessKey: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton.accesskey"),
+ popup: null,
+ callback: function allowInstall() {
+ installInfo.install();
+ return false;
+ }
+ }];
+
+ if (!this.getNotificationWithValue(notificationName)) {
+ var priority = this.PRIORITY_WARNING_MEDIUM;
+ this.appendNotification(messageString, notificationName,
+ null, priority, buttons);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallCancelled">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+ var notificationName = "addon-install-cancelled";
+ var messageString = this._stringBundle.GetStringFromName("addonDownloadCancelled");
+ messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("addonDownloadRestartButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonDownloadRestartButton.accesskey"),
+ popup: null,
+ callback: function() {
+ var weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]
+ .getService(Ci.amIWebInstallListener);
+ if (weblistener.onWebInstallRequested(installInfo.browser, installInfo.originatingURI,
+ [aInstall], 1)) {
+ aInstall.install();
+ }
+ }
+ }];
+ var priority = this.PRIORITY_INFO_MEDIUM;
+ this.appendNotification(messageString, notificationName,
+ null, priority, buttons);
+
+ installInfo.installs.every(function(aInstall) {
+ aInstall.cancel();
+ });
+ return true; // the downloading notification closed automatically
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallComplete">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+
+ var notificationName = "addon-install-complete"
+ var addonNotification = this.getNotificationWithValue(notificationName);
+ if (addonNotification)
+ this.removeNotification(addonNotification);
+
+ var buttons = [];
+ var messageString;
+ if (installInfo.installs.some(install =>
+ install.addon.pendingOperations &
+ tmp.AddonManager.PENDING_INSTALL)) {
+ messageString = this._stringBundle.GetStringFromName("addonsInstalledNeedsRestart");
+ buttons.push({
+ label: this._stringBundle.GetStringFromName("addonInstallRestartButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonInstallRestartButton.accesskey"),
+ callback: function () {
+ BrowserUtils.restartApplication();
+ }
+ });
+ } else {
+ messageString = this._stringBundle.GetStringFromName("addonsInstalled");
+ }
+
+ if ("toEM" in window) {
+ buttons.push({
+ label: this._stringBundle.GetStringFromName("addonInstallManageButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonInstallManageButton.accesskey"),
+ callback: function() {
+ window.toEM("addons://list/extension");
+ }
+ });
+ }
+
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ messageString = tmp.PluralForm.get(installInfo.installs.length, messageString)
+ .replace("#1", installInfo.installs[0].name)
+ .replace("#2", installInfo.installs.length)
+ .replace("#3", brandShortName);
+ var priority = this.PRIORITY_WARNING_MEDIUM;
+ this.appendNotification(messageString, notificationName,
+ null, priority, buttons);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallDisabled">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var messageString;
+ var buttons;
+
+ var notificationName = "addon-install-disabled";
+ if (Services.prefs.prefIsLocked("xpinstall.enabled")) {
+ messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessageLocked");
+ buttons = [];
+ } else {
+ messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessage");
+ buttons = [{
+ label: this._stringBundle.GetStringFromName("xpinstallDisabledButton"),
+ accessKey: this._stringBundle.GetStringFromName("xpinstallDisabledButton.accesskey"),
+ popup: null,
+ callback: function editPrefs() {
+ Services.prefs.setBoolPref("xpinstall.enabled", true);
+ return false;
+ }
+ }];
+ }
+
+ if (!this.getNotificationWithValue(notificationName)) {
+ var priority = this.PRIORITY_WARNING_MEDIUM;
+ this.appendNotification(messageString, notificationName,
+ null, priority, buttons);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallFailed">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var notificationName = "addon-install-failed";
+ if (!this.getNotificationWithValue(notificationName)) {
+ var host;
+ try {
+ // this fails with nsSimpleURIs like data: URIs
+ host = installInfo.originatingURI.host;
+ } catch (ex) {
+ host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
+ }
+
+ var error = "addonErrorIncompatible";
+ var name = installInfo.installs[0].name;
+ installInfo.installs.some(function(install) {
+ if (install.error) {
+ name = install.name;
+ error = "addonError" + install.error;
+ return true;
+ }
+ if (install.addon.blocklistState ==
+ Ci.nsIBlocklistService.STATE_BLOCKED) {
+ name = install.name;
+ error = "addonErrorBlocklisted";
+ }
+ return false;
+ });
+
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var version = Services.appinfo.version;
+ var messageString = this._stringBundle.GetStringFromName(error)
+ .replace("#1", name)
+ .replace("#2", host)
+ .replace("#3", brandShortName)
+ .replace("#4", version);
+
+ var priority = this.PRIORITY_WARNING_MEDIUM;
+ this.appendNotification(messageString, notificationName,
+ null, priority, []);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallStarted">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
+ if (installInfo.installs.every(function(aInstall) {
+ return aInstall.state == tmp.AddonManager.STATE_DOWNLOADED;
+ }))
+ return;
+
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+ var notificationName = "addon-install-started";
+ var messageString = this._stringBundle.GetStringFromName("addonDownloading");
+ messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
+ var buttons = [{
+ label: this._stringBundle.GetStringFromName("addonDownloadCancelButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonDownloadCancelButton.accesskey"),
+ popup: null,
+ callback: this.addonInstallCancelled.bind(this, installInfo)
+ }];
+ var priority = this.PRIORITY_INFO_MEDIUM;
+ var box = this.appendNotification(messageString, notificationName,
+ null, priority, buttons);
+ box.installInfo = installInfo;
+ installInfo.installs.forEach(function(aInstall) {
+ aInstall.addListener(box);
+ });
+ ]]>
+ </body>
+ </method>
+
+ <method name="ignoreSafeBrowsingWarning">
+ <parameter name="aReason"/>
+ <parameter name="aBlockedInfo"/>
+
+ <body>
+ <![CDATA[
+ var uri = this.activeBrowser.currentURI;
+ var flag = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER;
+ this.activeBrowser.loadURIWithFlags(uri.asciiSpec, flag,
+ null, null, null);
+
+ Services.perms.add(uri, "safe-browsing",
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ Ci.nsIPermissionManager.EXPIRE_SESSION);
+
+ var title, label, accessKey, reportName, buttons;
+
+ switch (aReason) {
+ case "phishing":
+ title = "safebrowsing.deceptiveSite";
+ label = "safebrowsing.notADeceptiveSiteButton.label";
+ accessKey = "safebrowsing.notADeceptiveSiteButton.accessKey";
+ reportName = "PhishMistake";
+ break;
+ case "malware":
+ title = "safebrowsing.reportedAttackSite";
+ label = "safebrowsing.notAnAttackButton.label";
+ accessKey = "safebrowsing.notAnAttackButton.accessKey";
+ reportName = "MalwareMistake";
+ break;
+ case "unwanted":
+ title = "safebrowsing.reportedUnwantedSite";
+ break;
+ // No notifications for unknown reasons.
+ default:
+ return;
+ }
+
+ title = this._stringBundle.GetStringFromName(title);
+
+ buttons = [{
+ label: this._stringBundle.GetStringFromName("safebrowsing.getMeOutOfHereButton.label"),
+ accessKey: this._stringBundle.GetStringFromName("safebrowsing.getMeOutOfHereButton.accessKey"),
+ callback: getMeOutOfHere
+ }]
+
+ if (reportName) {
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/SafeBrowsing.jsm", tmp);
+ var reportUrl = tmp.SafeBrowsing.getReportURL(reportName, aBlockedInfo);
+
+ // There's no button if we can not get report url, for example
+ // if the provider of blockedInfo is not Google.
+ if (reportUrl) {
+ buttons.push({
+ label: this._stringBundle.GetStringFromName(label),
+ accessKey: this._stringBundle.GetStringFromName(accessKey),
+ callback() { openUILinkIn(reportUrl, "tabfocused"); }
+ });
+ }
+ }
+
+ var type = "blocked-badware-page";
+ var notification = this.getNotificationWithValue(type);
+ if (notification)
+ this.removeNotification(notification);
+
+ var box = this.appendNotification(title, type, null,
+ this.PRIORITY_CRITICAL_HIGH,
+ buttons);
+
+ // Persist the notification until the user removes so it
+ // doesn't get removed on redirects.
+ box.persistence = -1;
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+ ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
+
+ Services.obs.addObserver(this, "indexedDB-permissions-prompt");
+ Services.obs.addObserver(this, "indexedDB-quota-prompt");
+ Services.obs.addObserver(this, "indexedDB-quota-cancel");
+ Services.obs.addObserver(this, "addon-install-blocked");
+ Services.obs.addObserver(this, "addon-install-complete");
+ Services.obs.addObserver(this, "addon-install-disabled");
+ Services.obs.addObserver(this, "addon-install-failed");
+ Services.obs.addObserver(this, "addon-install-started");
+ Services.obs.addObserver(this, "offline-cache-update-completed");
+ Services.obs.addObserver(this, "perm-changed");
+ Services.obs.addObserver(this, "formsubmit");
+
+ Services.prefs.addObserver("privacy.popups.showBrowserMessage", this);
+ Services.prefs.addObserver("dom.disable_open_during_load", this);
+
+ this.addProgressListener();
+
+ if (AppConstants.MOZ_CRASHREPORTER)
+ ChromeUtils.import("resource://gre/modules/CrashSubmit.jsm", this);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ this.destroy();
+ ]]>
+ </destructor>
+
+ <field name="mDestroyed">false</field>
+
+ <!-- This is necessary because the destructor doesn't always get called when
+ we are removed from a tabbrowser. This will be explicitly called by tabbrowser -->
+ <method name="destroy">
+ <body>
+ <![CDATA[
+ if (this.mDestroyed)
+ return;
+ this.mDestroyed = true;
+
+ if (this._addedProgressListener) {
+ this.activeBrowser.webProgress.removeProgressListener(this);
+ this._addedProgressListener = false;
+ }
+
+ this._activeBrowser = null;
+ try {
+ Services.obs.removeObserver(this, "indexedDB-permissions-prompt");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "indexedDB-quota-prompt");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "indexedDB-quota-cancel");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "addon-install-blocked");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "addon-install-complete");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "addon-install-disabled");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "addon-install-failed");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "addon-install-started");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "offline-cache-update-completed");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "perm-changed");
+ } catch (ex) {}
+ try {
+ Services.obs.removeObserver(this, "formsubmit");
+ } catch (ex) {}
+
+ try {
+ Services.prefs.removeObserver("privacy.popups.showBrowserMessage", this);
+ } catch (ex) {}
+ try {
+ Services.prefs.removeObserver("dom.disable_open_during_load", this);
+ } catch (ex) {}
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="DOMContentLoaded" phase="capturing">
+ <![CDATA[
+ if (/^about:neterror\?e=netOffline/.test(event.target.documentURI))
+ event.target.addEventListener("click", function tryAgain(event) {
+ if (event.target.id == "errorTryAgain")
+ Services.io.offline = false;
+ }, true);
+ ]]>
+ </handler>
+
+ <handler event="DOMUpdatePageReport" phase="capturing">
+ <![CDATA[
+ var browser = this.activeBrowser;
+ if (!browser.blockedPopups || browser.blockedPopups.reported != false)
+ return;
+
+ // this.popupCount can be 0, while browser.blockedPopups has not been cleared.
+ if (!this.popupCount && browser.blockedPopups.length > 1) {
+ this.popupCount = browser.blockedPopups.length;
+ } else {
+ this.popupCount++;
+ }
+ this.playSoundForBlockedPopup();
+ this.notifyPopupCountChanged();
+
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+ if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage"))
+ {
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var message = this._stringBundle.GetStringFromName("popupWarning.message");
+ message = tmp.PluralForm.get(this.popupCount, message)
+ .replace("#1", brandShortName)
+ .replace("#2", this.popupCount);
+
+ var notification = this.getNotificationWithValue("popup-blocked");
+ if (notification) {
+ notification.label = message;
+ } else {
+ var popupButtonText = this._stringBundle.GetStringFromName("popupWarningButton");
+ var popupButtonAccesskey = this._stringBundle.GetStringFromName("popupWarningButton.accesskey");
+ var buttons = [{
+ label: popupButtonText,
+ accessKey: popupButtonAccesskey,
+ popup: "popupNotificationMenu",
+ callback: null
+ }];
+
+ const priority = this.PRIORITY_WARNING_MEDIUM;
+ this.appendNotification(message, "popup-blocked",
+ null, priority, buttons);
+ }
+ }
+ ]]>
+ </handler>
+
+ <handler event="PluginCrashed" phase="capturing">
+ <![CDATA[
+ // Ensure the plugin and event are of the right type.
+ var plugin = event.target;
+ var detail = event.detail;
+ if (!(detail instanceof Ci.nsIPropertyBag2))
+ return;
+
+ var submittedReport = detail.getPropertyAsBool("submittedCrashReport");
+ var doPrompt = true; // XXX followup for .getPropertyAsBool("doPrompt");
+ var submitReports = true; // XXX followup for .getPropertyAsBool("submitReports");
+ var pluginName = detail.getPropertyAsAString("pluginName");
+ var pluginFilename = detail.getPropertyAsAString("pluginFilename");
+ var pluginDumpID = detail.getPropertyAsAString("pluginDumpID");
+
+ // Remap the plugin name to a more user-presentable form.
+ pluginName = this.makeNicePluginName(pluginName);
+
+ // Force a style flush, so that we ensure our binding is attached.
+ plugin.clientTop;
+
+ // Configure the crashed-plugin placeholder.
+ var overlay = this.getPluginUI(plugin, "main");
+
+ var status;
+ var statusDiv = this.getPluginUI(plugin, "submitStatus");
+
+ if (this.CrashSubmit) {
+ // Determine which message to show regarding crash reports.
+ if (submittedReport) { // submitReports && !doPrompt, handled in observer
+ status = "submitted";
+ }
+ else if (!submitReports && !doPrompt) {
+ status = "noSubmit";
+ }
+ else { // doPrompt
+ status = "please";
+ this.getPluginUI(plugin, "submitButton").addEventListener("click",
+ function (event) {
+ if (event.button != 0 || !event.isTrusted)
+ return;
+ this.submitReport(pluginDumpID, plugin);
+ Services.prefs.setBoolPref("dom.ipc.plugins.reportCrashURL", optInCB.checked);
+ }.bind(this));
+ let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
+ optInCB.checked = Services.prefs.getBoolPref("dom.ipc.plugins.reportCrashURL");
+ }
+
+ // If we're showing the link to manually trigger report submission, we'll
+ // want to be able to update all the instances of the UI for this crash to
+ // show an updated message when a report is submitted.
+ if (doPrompt) {
+ let observer = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsISupportsWeakReference]),
+ observe: function(subject, topic, data) {
+ let propertyBag = subject;
+ if (!(propertyBag instanceof Ci.nsIPropertyBag2))
+ return;
+ // Ignore notifications for other crashes.
+ if (propertyBag.get("minidumpID") != pluginDumpID)
+ return;
+ statusDiv.setAttribute("status", data);
+ },
+
+ handleEvent : function(event) {
+ // Not expected to be called, just here for the closure.
+ }
+ }
+
+ // Use a weak reference, so we don't have to remove it...
+ Services.obs.addObserver(observer, "crash-report-status", true);
+ // ...alas, now we need something to hold a strong reference to prevent
+ // it from being GC. But I don't want to manually manage the reference's
+ // lifetime (which should be no greater than the page).
+ // Clever solution? Use a closure with an event listener on the statusDiv.
+ // When it goes away, so do the listener references and the closure.
+ statusDiv.addEventListener("mozCleverClosureHack", observer);
+ }
+ }
+
+ // If we don't have a minidumpID, we can't (or didn't) submit anything.
+ // This can happen if the plugin is killed from the task manager.
+ if (!pluginDumpID) {
+ status = "noReport";
+ }
+
+ statusDiv.setAttribute("status", status);
+
+ var helpIcon = this.getPluginUI(plugin, "helpIcon");
+ this.addLinkClickCallback(helpIcon, this.openHelpPage);
+
+ var messageString = this._stringBundle.formatStringFromName("crashedpluginsMessage.title", [pluginName], 1);
+ var crashText = this.getPluginUI(plugin, "crashedText");
+ crashText.textContent = messageString;
+
+ var link = this.getPluginUI(plugin, "reloadLink");
+ this.addLinkClickCallback(link, this.reloadPage);
+
+ overlay.classList.add("visible");
+ // If a previous plugin on the page was too small and resulted in
+ // adding a notification bar, then remove it because this plugin
+ // instance it big enough to serve as in-content notification.
+ var notification = this.getNotificationWithValue("plugin-crashed");
+ if (notification)
+ this.removeNotification(notification, true);
+ this.crashNotified = true;
+ ]]>
+ </handler>
+
+ <handler event="MozApplicationManifest" phase="capturing">
+ <![CDATA[
+ if (!Services.prefs.getBoolPref("browser.offline-apps.notify"))
+ return;
+
+ try {
+ if (Services.prefs.getBoolPref("offline-apps.allow_by_default"))
+ return;
+ } catch (e) {
+ }
+
+ this.offlineAppRequested(event.originalTarget);
+ ]]>
+ </handler>
+
+ <handler event="pageshow" phase="capturing">
+ <![CDATA[
+ // |event.persisted| is true when the page is loaded from the
+ // BF cache, so this code reshows the notification if necessary.
+ if (!event.persisted)
+ return;
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="popup-notification"
+ extends="chrome://communicator/content/bindings/notification.xml#browser-notificationbox">
+ <implementation>
+ <method name="onLocationChange">
+ <parameter name="aWebProgress" />
+ <parameter name="aRequest" />
+ <parameter name="aLocation" />
+ <parameter name="aFlags" />
+ <body>
+ <![CDATA[
+ const nsIWebProgressListener = Ci.nsIWebProgressListener;
+ if (aWebProgress.DOMWindow == this.activeBrowser.contentWindow &&
+ !(aFlags & nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
+ this.onDocumentChange();
+ PopupNotifications.locationChange(this.activeBrowser);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeNotifications">
+ <parameter name="aNotifications"/>
+ <body>
+ <![CDATA[
+ aNotifications.forEach(function(value) {
+ var notification = PopupNotifications.getNotification(value);
+ if (notification)
+ PopupNotifications.remove(notification);
+ });
+ ]]>
+ </body>
+ </method>
+
+ <method name="lwthemeInstallRequest">
+ <parameter name="aHost"/>
+ <parameter name="aCallback"/>
+ <body>
+ <![CDATA[
+ var message = this._stringBundle.formatStringFromName("lwthemeInstallRequest.message", [aHost], 1);
+ var action = {
+ label: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeInstallRequest.allowButton.accesskey"),
+ callback: aCallback
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "lwtheme-install-request", message,
+ "addons-notification-icon", action);
+ ]]>
+ </body>
+ </method>
+
+ <method name="lwthemeInstallNotification">
+ <parameter name="aCallback"/>
+ <body>
+ <![CDATA[
+ var message = this._stringBundle.GetStringFromName("lwthemeInstallNotification.message");
+ var mainAction = {
+ label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.undoButton.accesskey"),
+ callback: aCallback
+ };
+ var secondaryActions = [{
+ label: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeInstallNotification.manageButton.accesskey"),
+ callback: function() {
+ window.toEM("addons://list/theme");
+ }
+ }];
+ var options = {
+ timeout: Date.now() + 20000 // 20 seconds
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "lwtheme-install-notification", message,
+ "addons-notification-icon", mainAction,
+ secondaryActions, options);
+ ]]>
+ </body>
+ </method>
+
+ <method name="lwthemeNeedsRestart">
+ <parameter name="aNewThemeName"/>
+ <body>
+ <![CDATA[
+ var message = this._stringBundle.formatStringFromName("lwthemeNeedsRestart.message", [aNewThemeName], 1);
+ var action = {
+ label: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton"),
+ accessKey: this._stringBundle.GetStringFromName("lwthemeNeedsRestart.restartButton.accesskey"),
+ callback: function() {
+ BrowserUtils.restartApplication();
+ }
+ };
+ var options = {
+ timeout: Date.now() + 20000 // 20 seconds
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "lwtheme-install-notification", message,
+ "addons-notification-icon", action, null,
+ options);
+ ]]>
+ </body>
+ </method>
+
+ <method name="promptIndexedDB">
+ <parameter name="aRequestor"/>
+ <parameter name="aWindow"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ var host = aWindow.document.documentURIObject.asciiHost;
+ var property = this.usePrivateBrowsing ? "offlineApps.private" :
+ "offlineApps." + aTopic;
+ var message = this._stringBundle.formatStringFromName(property,
+ [host, aData], 2);
+ var observer = aRequestor.getInterface(Ci.nsIObserver);
+ var mainAction = {
+ label: this._stringBundle.GetStringFromName("offlineApps.always"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.always.accesskey"),
+ callback: function allowIndexedDB() {
+ clearTimeout(notification.timeout);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.ALLOW_ACTION);
+ }
+ };
+ var secondaryActions = [{
+ label: this._stringBundle.GetStringFromName("offlineApps.never"),
+ accessKey: this._stringBundle.GetStringFromName("offlineApps.never.accesskey"),
+ callback: function denyIndexedDB() {
+ clearTimeout(notification.timeout);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.DENY_ACTION);
+ }
+ }];
+ function notificationTimedOut() {
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ notification.remove();
+ }
+ var options = {
+ eventCallback: function(state) {
+ if (notification) {
+ // Always clear the timeout up front. If the doorhanger was
+ // temporarily dismissed, we'll set a new 30 second timeout
+ // to automatically cancel the request. If the doorhanger
+ // gets redisplayed we don't want it to time out unless it
+ // gets dismissed again. And if the doorhanger gets removed
+ // then we aren't interested in it any more.
+ clearTimeout(timeout);
+ if (state == "dismissed")
+ timeout = setTimeout(notificationTimedOut, 30000);
+ }
+ }
+ };
+ var notification =
+ PopupNotifications.show(this.activeBrowser,
+ "indexedDB-" + aTopic + "-prompt",
+ message, "indexedDB-notification-icon",
+ this.usePrivateBrowsing ? null : mainAction,
+ secondaryActions, options);
+ var timeout = setTimeout(notificationTimedOut, 300000); // 5 minutes
+ ]]>
+ </body>
+ </method>
+
+ <method name="cancelIndexedDB">
+ <parameter name="aRequestor"/>
+ <parameter name="aWindow"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ var popupNotification = PopupNotifications.getNotification("indexedDB-" + aTopic + "-prompt", this.activeBrowser);
+ if (popupNotification)
+ popupNotification.remove(); // eventCallback clears the timeout
+
+ var observer = aRequestor.getInterface(Ci.nsIObserver);
+ observer.observe(null, "indexedDB-" + aTopic + "-response",
+ Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallBlocked">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var host;
+ try {
+ // this fails with nsSimpleURIs like data: URIs
+ host = installInfo.originatingURI.host;
+ } catch (ex) {
+ host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
+ }
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var messageString = this._stringBundle.formatStringFromName("xpinstallPromptWarning",
+ [brandShortName, host], 2);
+ var action = {
+ label: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton"),
+ accessKey: this._stringBundle.GetStringFromName("xpinstallPromptInstallButton.accesskey"),
+ callback: function allowInstall() {
+ installInfo.install();
+ return false;
+ }
+ };
+
+ // Make notifications persist a minimum of 30 seconds
+ var options = {
+ timeout: Date.now() + 30000
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "addon-install-blocked", messageString,
+ "addons-notification-icon", action,
+ null, options);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallCancelled">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+ var messageString = this._stringBundle.GetStringFromName("addonDownloadCancelled");
+ messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
+ var action = {
+ label: this._stringBundle.GetStringFromName("addonDownloadRestartButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonDownloadRestartButton.accesskey"),
+ popup: null,
+ callback: function() {
+ var weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]
+ .getService(Ci.amIWebInstallListener);
+ if (weblistener.onWebInstallRequested(installInfo.browser, installInfo.originatingURI,
+ [aInstall], 1)) {
+ aInstall.install();
+ }
+ }
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "addon-install-cancelled", messageString,
+ "addons-notification-icon", action);
+
+ installInfo.installs.every(function(aInstall) {
+ aInstall.cancel();
+ });
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallComplete">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+
+ var messageString;
+ var mainAction = null;
+ var secondaryActions = null;
+
+ if ("toEM" in window) {
+ mainAction = {
+ label: this._stringBundle.GetStringFromName("addonInstallManageButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonInstallManageButton.accesskey"),
+ callback: function() {
+ window.toEM("addons://list/extension");
+ }
+ };
+ }
+
+ if (installInfo.installs.some(install =>
+ install.addon.pendingOperations &
+ tmp.AddonManager.PENDING_INSTALL)) {
+ messageString = this._stringBundle.GetStringFromName("addonsInstalledNeedsRestart");
+ if (mainAction)
+ secondaryActions = [mainAction];
+ mainAction = {
+ label: this._stringBundle.GetStringFromName("addonInstallRestartButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonInstallRestartButton.accesskey"),
+ callback: function () {
+ BrowserUtils.restartApplication();
+ }
+ };
+ } else {
+ messageString = this._stringBundle.GetStringFromName("addonsInstalled");
+ }
+
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ messageString = tmp.PluralForm.get(installInfo.installs.length, messageString)
+ .replace("#1", installInfo.installs[0].name)
+ .replace("#2", installInfo.installs.length)
+ .replace("#3", brandShortName);
+
+ // Make notifications persist a minimum of 30 seconds
+ var options = {
+ timeout: Date.now() + 30000
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "addon-install-complete", messageString,
+ "addons-notification-icon", mainAction,
+ secondaryActions, options);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallDisabled">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var messageString;
+ var action = null;
+
+ if (Services.prefs.prefIsLocked("xpinstall.enabled"))
+ messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessageLocked");
+ else {
+ messageString = this._stringBundle.GetStringFromName("xpinstallDisabledMessage");
+ action = {
+ label: this._stringBundle.GetStringFromName("xpinstallDisabledButton"),
+ accessKey: this._stringBundle.GetStringFromName("xpinstallDisabledButton.accesskey"),
+ callback: function editPrefs() {
+ Services.prefs.setBoolPref("xpinstall.enabled", true);
+ }
+ };
+ }
+
+ // Make notifications persist a minimum of 30 seconds
+ var options = {
+ timeout: Date.now() + 30000
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "addon-install-disabled", messageString,
+ "addons-notification-icon", action,
+ null, options);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallFailed">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var host;
+ try {
+ // this fails with nsSimpleURIs like data: URIs
+ host = installInfo.originatingURI.host;
+ } catch (ex) {
+ host = this._stringBundle.GetStringFromName("xpinstallHostNotAvailable");
+ }
+
+ var error = "addonErrorIncompatible";
+ var name = installInfo.installs[0].name;
+ installInfo.installs.some(function(install) {
+ if (install.error) {
+ name = install.name;
+ error = "addonError" + install.error;
+ return true;
+ }
+ if (install.addon.blocklistState ==
+ Ci.nsIBlocklistService.STATE_BLOCKED) {
+ name = install.name;
+ error = "addonErrorBlocklisted";
+ }
+ return false;
+ });
+
+ var brandShortName = this._brandStringBundle.GetStringFromName("brandShortName");
+ var version = Services.appinfo.version;
+ var messageString = this._stringBundle.GetStringFromName(error)
+ .replace("#1", name)
+ .replace("#2", host)
+ .replace("#3", brandShortName)
+ .replace("#4", version);
+
+ // Make notifications persist a minimum of 30 seconds
+ var options = {
+ timeout: Date.now() + 30000
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "addon-install-failed", messageString,
+ "addons-notification-icon", null,
+ null, options);
+ ]]>
+ </body>
+ </method>
+
+ <method name="addonInstallStarted">
+ <parameter name="installInfo"/>
+ <body>
+ <![CDATA[
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
+ if (installInfo.installs.every(function(aInstall) {
+ return aInstall.state == tmp.AddonManager.STATE_DOWNLOADED;
+ }))
+ return;
+
+ ChromeUtils.import("resource://gre/modules/PluralForm.jsm", tmp);
+ var messageString = this._stringBundle.GetStringFromName("addonDownloading");
+ messageString = tmp.PluralForm.get(installInfo.installs.length, messageString);
+ var action = {
+ label: this._stringBundle.GetStringFromName("addonDownloadCancelButton"),
+ accessKey: this._stringBundle.GetStringFromName("addonDownloadCancelButton.accesskey"),
+ callback: this.addonInstallCancelled.bind(this, installInfo)
+ };
+ var options = {
+ installInfo: installInfo
+ };
+ PopupNotifications.show(this.activeBrowser,
+ "addon-install-started", messageString,
+ "addons-notification-icon", action,
+ null, options);
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="addon-progress-notification"
+ extends="chrome://global/content/bindings/notification.xml#notification">
+ <content>
+ <xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
+ <xul:hbox anonid="details" align="center" flex="1"
+ oncommand="this.parentNode.parentNode._doButtonCommand(event);">
+ <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
+ <xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
+ <xul:progressmeter mode="undetermined" xbl:inherits="mode,value=progress"/>
+ <xul:label flex="1" xbl:inherits="value=status"/>
+ <children/>
+ </xul:hbox>
+ <xul:toolbarbutton ondblclick="event.stopPropagation();"
+ class="messageCloseButton tabbable"
+ xbl:inherits="hidden=hideclose"
+ tooltiptext="&closeNotification.tooltip;"
+ oncommand="document.getBindingParent(this).close();"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <destructor>
+ <![CDATA[
+ this.installInfo.installs.forEach(function(aInstall) {
+ aInstall.removeListener(this);
+ }, this);
+ ]]>
+ </destructor>
+
+ <method name="updateProgress">
+ <body>
+ <![CDATA[
+ var count = 0;
+ var progress = 0;
+ var max = 0;
+
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
+ this.installInfo.installs.forEach(function(aInstall) {
+ if (aInstall.maxProgress < 0)
+ max = -1;
+ else if (max >= 0)
+ max += aInstall.maxProgress;
+ progress += aInstall.progress;
+ if (aInstall.state < tmp.AddonManager.STATE_DOWNLOADED)
+ count++;
+ });
+
+ if (max < 0)
+ this.setAttribute("mode", "undetermined");
+ else {
+ this.setAttribute("mode", "determined");
+ this.setAttribute("progress", progress * 100 / max);
+ }
+
+ var now = Date.now();
+ if (!this.startTime) {
+ this.startTime = now;
+ this.lastUpdate = now - 750;
+ this.lastSeconds = null;
+ }
+
+ if (progress == max || now - this.lastUpdate >= 750) {
+ this.lastUpdate = now;
+ var elapsed = (now - this.startTime) / 1000;
+ var rate = elapsed && progress / elapsed;
+ ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", tmp);
+ var status;
+ [status, this.lastSeconds] = tmp.DownloadUtils.getDownloadStatus(progress, max, rate, this.lastSeconds);
+ this.setAttribute("status", status);
+ }
+
+ if (!count)
+ this.close();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadProgress">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadFailed">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadCancelled">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadEnded">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="sidebar-notification"
+ extends="chrome://global/content/bindings/notification.xml#notification">
+ <content>
+ <xul:vbox class="notification-inner outset" flex="1" xbl:inherits="type">
+ <xul:hbox align="center">
+ <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
+ <xul:arrowscrollbox orient="horizontal" flex="1" pack="end"
+ oncommand="document.getBindingParent(this)._doButtonCommand(event);">
+ <children/>
+ </xul:arrowscrollbox>
+ <xul:toolbarbutton ondblclick="event.stopPropagation();"
+ class="messageCloseButton tabbable"
+ xbl:inherits="hidden=hideclose"
+ tooltiptext="&closeNotification.tooltip;"
+ oncommand="document.getBindingParent(this).close();"/>
+ </xul:hbox>
+ <xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
+ </xul:vbox>
+ </content>
+ </binding>
+
+ <binding id="sidebar-addon-progress-notification"
+ extends="chrome://communicator/content/bindings/notification.xml#addon-progress-notification">
+ <content>
+ <xul:vbox class="notification-inner outset" flex="1" xbl:inherits="type">
+ <xul:hbox align="center">
+ <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
+ <xul:arrowscrollbox orient="horizontal" flex="1" pack="end"
+ oncommand="document.getBindingParent(this)._doButtonCommand(event);">
+ <children/>
+ </xul:arrowscrollbox>
+ <xul:toolbarbutton ondblclick="event.stopPropagation();"
+ class="messageCloseButton tabbable"
+ xbl:inherits="hidden=hideclose"
+ tooltiptext="&closeNotification.tooltip;"
+ oncommand="document.getBindingParent(this).close();"/>
+ </xul:hbox>
+ <xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
+ <xul:progressmeter mode="undetermined" xbl:inherits="mode,value=progress"/>
+ <xul:label xbl:inherits="value=status"/>
+ </xul:vbox>
+ </content>
+ </binding>
+
+ <binding id="addon-progress-popup-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
+ <content align="start">
+ <xul:image class="popup-notification-icon" xbl:inherits="popupid"/>
+ <xul:vbox flex="1">
+ <xul:description class="popup-notification-description addon-progress-description"
+ xbl:inherits="xbl:text=label"/>
+ <xul:hbox class="popup-notification-button-container" align="center">
+ <xul:progressmeter mode="undetermined"
+ xbl:inherits="mode,value=progress"/>
+ <xul:spacer flex="1"/>
+ <xul:button anonid="button"
+ class="popup-notification-menubutton"
+ type="menu-button"
+ xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
+ <xul:menupopup anonid="menupopup"
+ xbl:inherits="oncommand=menucommand">
+ <children/>
+ <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
+ label="&closeNotificationItem.label;"
+ xbl:inherits="oncommand=closeitemcommand"/>
+ </xul:menupopup>
+ </xul:button>
+ </xul:hbox>
+ <xul:label xbl:inherits="xbl:text=status"/>
+ </xul:vbox>
+ <xul:vbox pack="start">
+ <xul:toolbarbutton anonid="closebutton"
+ class="messageCloseButton close-icon popup-notification-closebutton tabbable"
+ xbl:inherits="oncommand=closebuttoncommand"
+ tooltiptext="&closeNotification.tooltip;"/>
+ </xul:vbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.installInfo = this.notification.options.installInfo;
+ this.installInfo.installs.forEach(function(aInstall) {
+ aInstall.addListener(this);
+ }, this);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ this.installInfo.installs.forEach(function(aInstall) {
+ aInstall.removeListener(this);
+ }, this);
+ ]]>
+ </destructor>
+
+ <method name="updateProgress">
+ <body>
+ <![CDATA[
+ var count = 0;
+ var progress = 0;
+ var max = 0;
+
+ var tmp = {};
+ ChromeUtils.import("resource://gre/modules/AddonManager.jsm", tmp);
+ this.installInfo.installs.forEach(function(aInstall) {
+ if (aInstall.maxProgress < 0)
+ max = -1;
+ else if (max >= 0)
+ max += aInstall.maxProgress;
+ progress += aInstall.progress;
+ if (aInstall.state < tmp.AddonManager.STATE_DOWNLOADED)
+ count++;
+ });
+
+ if (max < 0)
+ this.setAttribute("mode", "undetermined");
+ else {
+ this.setAttribute("mode", "determined");
+ this.setAttribute("progress", progress * 100 / max);
+ }
+
+ var now = Date.now();
+ if (!this.startTime) {
+ this.startTime = now;
+ this.lastUpdate = now - 750;
+ this.lastSeconds = null;
+ }
+
+ if (progress == max || now - this.lastUpdate >= 750) {
+ this.lastUpdate = now;
+ var elapsed = (now - this.startTime) / 1000;
+ var rate = elapsed && progress / elapsed;
+ ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm", tmp);
+ var status;
+ [status, this.lastSeconds] = tmp.DownloadUtils.getDownloadStatus(progress, max, rate, this.lastSeconds);
+ this.setAttribute("status", status);
+ }
+
+ if (!count)
+ PopupNotifications.remove(this.notification);
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadProgress">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadFailed">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadCancelled">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+
+ <method name="onDownloadEnded">
+ <body>
+ <![CDATA[
+ this.updateProgress();
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="center-item">
+ <content>
+ <xul:vbox flex="1" class="center-item-box"
+ xbl:inherits="warn,showseparator,padbottom">
+ <xul:hbox align="center">
+ <xul:image class="center-item-icon"
+ xbl:inherits="src=itemicon"/>
+ <xul:description class="center-item-label"
+ xbl:inherits="xbl:text=itemtext"/>
+ <xul:spacer flex="1"/>
+ <xul:button class="popup-notification-menubutton center-item-button"
+ oncommand="document.getBindingParent(this).runCallback();"
+ xbl:inherits="label=buttonlabel"/>
+ </xul:hbox>
+ <xul:hbox align="center" class="center-item-warning">
+ <xul:image class="center-item-warning-icon"/>
+ <xul:label class="center-item-warning-description" xbl:inherits="xbl:text=warningText"/>
+ <xul:label xbl:inherits="href=updateLink" value="&checkForUpdates;" class="text-link"/>
+ </xul:hbox>
+ </xul:vbox>
+ </content>
+ <resources>
+ <stylesheet src="chrome://global/skin/notification.css"/>
+ </resources>
+ <implementation>
+ <field name="action"></field>
+ <method name="runCallback">
+ <body><![CDATA[
+ let action = this.action;
+ action.callback();
+ let cas = action.popupnotification.notification.options.centerActions;
+ cas.splice(cas.indexOf(action), 1);
+ PopupNotifications._dismiss();
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/bindings/numberbox.xml b/comm/suite/components/bindings/numberbox.xml
new file mode 100644
index 0000000000..8f18bcbcdd
--- /dev/null
+++ b/comm/suite/components/bindings/numberbox.xml
@@ -0,0 +1,217 @@
+<?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/. -->
+
+<bindings id="numberboxBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="numberbox" extends="chrome://messenger/content/textbox.xml#textbox">
+
+ <content>
+ <xul:moz-input-box class="textbox-input-box numberbox-input-box" flex="1"
+ xbl:inherits="context,disabled,focused">
+ <html:input class="numberbox-input textbox-input" anonid="input"
+ xbl:inherits="value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/>
+ </xul:moz-input-box>
+ <xul:spinbuttons anonid="buttons" xbl:inherits="disabled,hidden=hidespinbuttons"/>
+ </content>
+
+ <implementation>
+ <field name="_valueEntered">false</field>
+ <field name="_spinButtons">null</field>
+ <field name="_value">0</field>
+
+ <property name="spinButtons" readonly="true">
+ <getter>
+ <![CDATA[
+ if (!this._spinButtons)
+ this._spinButtons = document.getAnonymousElementByAttribute(this, "anonid", "buttons");
+ return this._spinButtons;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="value" onget="return '' + this.valueNumber"
+ onset="return this.valueNumber = val;"/>
+
+ <property name="valueNumber">
+ <getter>
+ if (this._valueEntered) {
+ var newval = this.inputField.value;
+ this._validateValue(newval);
+ }
+ return this._value;
+ </getter>
+ <setter>
+ this._validateValue(val);
+ return val;
+ </setter>
+ </property>
+ <property name="min">
+ <getter>
+ var min = this.getAttribute("min");
+ return min ? Number(min) : 0;
+ </getter>
+ <setter>
+ <![CDATA[
+ if (typeof val == "number") {
+ this.setAttribute("min", val);
+ if (this.valueNumber < val)
+ this._validateValue(val);
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="max">
+ <getter>
+ var max = this.getAttribute("max");
+ return max ? Number(max) : Infinity;
+ </getter>
+ <setter>
+ <![CDATA[
+ if (typeof val != "number")
+ return val;
+ var min = this.min;
+ if (val < min)
+ val = min;
+ this.setAttribute("max", val);
+ if (this.valueNumber > val)
+ this._validateValue(val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <method name="_modifyUp">
+ <body>
+ <![CDATA[
+ if (this.disabled || this.readOnly)
+ return;
+ var oldval = this.valueNumber;
+ var newval = this._validateValue(this.valueNumber + 1);
+ this.inputField.select();
+ if (oldval != newval)
+ this._fireChange();
+ ]]>
+ </body>
+ </method>
+ <method name="_modifyDown">
+ <body>
+ <![CDATA[
+ if (this.disabled || this.readOnly)
+ return;
+ var oldval = this.valueNumber;
+ var newval = this._validateValue(this.valueNumber - 1);
+ this.inputField.select();
+ if (oldval != newval)
+ this._fireChange();
+ ]]>
+ </body>
+ </method>
+
+ <method name="_enableDisableButtons">
+ <body>
+ <![CDATA[
+ var buttons = this.spinButtons;
+ if (this.disabled || this.readOnly) {
+ buttons.decreaseDisabled = buttons.increaseDisabled = true;
+ } else {
+ buttons.decreaseDisabled = (this.valueNumber <= this.min);
+ buttons.increaseDisabled = (this.valueNumber >= this.max);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_validateValue">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ aValue = Number(aValue) || 0;
+ aValue = Math.round(aValue);
+
+ var min = this.min;
+ var max = this.max;
+ if (aValue < min)
+ aValue = min;
+ else if (aValue > max)
+ aValue = max;
+
+ this._valueEntered = false;
+ this._value = Number(aValue);
+ this.inputField.value = aValue;
+
+ this._enableDisableButtons();
+
+ return aValue;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_fireChange">
+ <body>
+ var evt = document.createEvent("Events");
+ evt.initEvent("change", true, true);
+ this.dispatchEvent(evt);
+ </body>
+ </method>
+
+ <constructor><![CDATA[
+ if (this.max < this.min)
+ this.max = this.min;
+
+ var value = this.inputField.value || 0;
+ this._validateValue(value);
+ ]]></constructor>
+
+ </implementation>
+
+ <handlers>
+ <handler event="input" phase="capturing">
+ this._valueEntered = true;
+ </handler>
+
+ <handler event="keypress">
+ <![CDATA[
+ if (!event.ctrlKey && !event.metaKey && !event.altKey && event.charCode) {
+ if (event.charCode == 45 && this.min < 0)
+ return;
+
+ if (event.charCode < 48 || event.charCode > 57)
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+
+ <handler event="keypress" keycode="VK_UP">
+ this._modifyUp();
+ </handler>
+
+ <handler event="keypress" keycode="VK_DOWN">
+ this._modifyDown();
+ </handler>
+
+ <handler event="up" preventdefault="true">
+ this._modifyUp();
+ </handler>
+
+ <handler event="down" preventdefault="true">
+ this._modifyDown();
+ </handler>
+
+ <handler event="change">
+ if (event.originalTarget == this.inputField) {
+ var newval = this.inputField.value;
+ this._validateValue(newval);
+ }
+ </handler>
+ </handlers>
+
+ </binding>
+</bindings>
diff --git a/comm/suite/components/bindings/preferences.xml b/comm/suite/components/bindings/preferences.xml
new file mode 100644
index 0000000000..8c5872ee76
--- /dev/null
+++ b/comm/suite/components/bindings/preferences.xml
@@ -0,0 +1,817 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE bindings [
+ <!ENTITY % preferencesDTD SYSTEM "chrome://communicator/locale/pref/preferences.dtd">
+ %preferencesDTD;
+ <!ENTITY % globalKeysDTD SYSTEM "chrome://global/locale/globalKeys.dtd">
+ %globalKeysDTD;
+]>
+
+<bindings id="preferencesBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+#
+# = Preferences Window Framework
+#
+# The syntax for use looks something like:
+#
+# <prefwindow>
+# <prefpane id="prefPaneA">
+# <preferences>
+# <preference id="preference1" name="app.preference1" type="bool" onchange="foo();"/>
+# <preference id="preference2" name="app.preference2" type="bool" useDefault="true"/>
+# </preferences>
+# <checkbox label="Preference" preference="preference1"/>
+# </prefpane>
+# </prefwindow>
+#
+
+ <binding id="preferences">
+ <implementation implements="nsIObserver">
+ <method name="_constructAfterChildren">
+ <body>
+ <![CDATA[
+ // This method will be called after the last of the child
+ // <preference> elements is constructed. Its purpose is to propagate
+ // the values to the associated form elements. Sometimes the code for
+ // some <preference> initializers depend on other <preference> elements
+ // being initialized so we wait and call updateElements on all of them
+ // once the last one has been constructed. See bugs 997570 and 992185.
+
+ var elements = this.getElementsByTagName("preference");
+ for (let element of elements) {
+ element.updateElements();
+ }
+
+ this._constructAfterChildrenCalled = true;
+ ]]>
+ </body>
+ </method>
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ for (var i = 0; i < this.childNodes.length; ++i) {
+ var preference = this.childNodes[i];
+ if (preference.name == aData) {
+ preference.value = preference.valueFromPreferences;
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="fireChangedEvent">
+ <parameter name="aPreference"/>
+ <body>
+ <![CDATA[
+ // Value changed, synthesize an event
+ try {
+ var event = document.createEvent("Events");
+ event.initEvent("change", true, true);
+ aPreference.dispatchEvent(event);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="service">
+ Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
+ </field>
+ <field name="rootBranch">
+ Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+ </field>
+ <field name="defaultBranch">
+ this.service.getDefaultBranch("");
+ </field>
+ <field name="rootBranchInternal">
+ Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+ </field>
+ <property name="type" readonly="true">
+ <getter>
+ <![CDATA[
+ return document.documentElement.type || "";
+ ]]>
+ </getter>
+ </property>
+ <property name="instantApply" readonly="true">
+ <getter>
+ <![CDATA[
+ var doc = document.documentElement;
+ return this.type == "child" ? doc.instantApply
+ : doc.instantApply || this.rootBranch.getBoolPref("browser.preferences.instantApply");
+ ]]>
+ </getter>
+ </property>
+
+ <!-- We want to call _constructAfterChildren after all child
+ <preference> elements have been constructed. To do this, we get
+ and store the node list of all child <preference> elements in the
+ constructor, and maintain a count which is incremented in the
+ constructor of <preference>. _constructAfterChildren is called
+ when the count matches the length of the list. -->
+ <field name="_constructedChildrenCount">0</field>
+ <field name="_preferenceChildren">null</field>
+ <!-- Some <preference> elements are added dynamically after
+ _constructAfterChildren has already been called - we want to
+ avoid looping over all of them again in this case so we remember
+ if we already called it. -->
+ <field name="_constructAfterChildrenCalled">false</field>
+ <constructor>
+ <![CDATA[
+ this._preferenceChildren = this.getElementsByTagName("preference");
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="preference">
+ <implementation>
+ <constructor>
+ <![CDATA[
+ // if the element has been inserted without the name attribute set,
+ // we have nothing to do here
+ if (!this.name)
+ return;
+
+ this.preferences.rootBranchInternal
+ .addObserver(this.name, this.preferences);
+ // In non-instant apply mode, we must try and use the last saved state
+ // from any previous opens of a child dialog instead of the value from
+ // preferences, to pick up any edits a user may have made.
+
+ var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager);
+ if (this.preferences.type == "child" &&
+ !this.instantApply && window.opener &&
+ secMan.isSystemPrincipal(window.opener.document.nodePrincipal)) {
+ var pdoc = window.opener.document;
+
+ // Try to find a preference element for the same preference.
+ var preference = null;
+ var parentPreferences = pdoc.getElementsByTagName("preferences");
+ for (var k = 0; (k < parentPreferences.length && !preference); ++k) {
+ var parentPrefs = parentPreferences[k]
+ .getElementsByAttribute("name", this.name);
+ for (var l = 0; (l < parentPrefs.length && !preference); ++l) {
+ if (parentPrefs[l].localName == "preference")
+ preference = parentPrefs[l];
+ }
+ }
+
+ // Don't use the value setter here, we don't want updateElements to be prematurely fired.
+ this._value = preference ? preference.value : this.valueFromPreferences;
+ } else {
+ this._value = this.valueFromPreferences;
+ }
+ if (this.preferences._constructAfterChildrenCalled) {
+ // This <preference> was added after _constructAfterChildren() was already called.
+ // We can directly call updateElements().
+ this.updateElements();
+ } else {
+ this.preferences._constructedChildrenCount++;
+ if (this.preferences._constructedChildrenCount ==
+ this.preferences._preferenceChildren.length) {
+ // This is the last <preference>, time to updateElements() on all of them.
+ this.preferences._constructAfterChildren();
+ }
+ }
+
+ this.dispatchEvent(new CustomEvent("bindingattached", { bubbles: false }));
+ ]]>
+ </constructor>
+ <destructor>
+ this.preferences.rootBranchInternal
+ .removeObserver(this.name, this.preferences);
+ </destructor>
+ <field name="_constructed">false</field>
+ <property name="instantApply">
+ <getter>
+ if (this.getAttribute("instantApply") == "false")
+ return false;
+ return this.getAttribute("instantApply") == "true" || this.preferences.instantApply;
+ </getter>
+ </property>
+
+ <property name="preferences" onget="return this.parentNode"/>
+ <property name="name" onget="return this.getAttribute('name');">
+ <setter>
+ if (val == this.name)
+ return val;
+
+ this.preferences.rootBranchInternal
+ .removeObserver(this.name, this.preferences);
+ this.setAttribute("name", val);
+ this.preferences.rootBranchInternal
+ .addObserver(val, this.preferences);
+
+ return val;
+ </setter>
+ </property>
+ <property name="type" onget="return this.getAttribute('type');"
+ onset="this.setAttribute('type', val); return val;"/>
+ <property name="inverted" onget="return this.getAttribute('inverted') == 'true';"
+ onset="this.setAttribute('inverted', val); return val;"/>
+ <property name="readonly" onget="return this.getAttribute('readonly') == 'true';"
+ onset="this.setAttribute('readonly', val); return val;"/>
+
+ <field name="_value">null</field>
+ <method name="_setValue">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ if (this.value !== aValue) {
+ this._value = aValue;
+ if (this.instantApply)
+ this.valueFromPreferences = aValue;
+ this.preferences.fireChangedEvent(this);
+ }
+ return aValue;
+ ]]>
+ </body>
+ </method>
+ <property name="value" onget="return this._value" onset="return this._setValue(val);"/>
+
+ <property name="locked">
+ <getter>
+ return this.preferences.rootBranch.prefIsLocked(this.name);
+ </getter>
+ </property>
+
+ <property name="disabled">
+ <getter>
+ return this.getAttribute("disabled") == "true";
+ </getter>
+ <setter>
+ <![CDATA[
+ if (val)
+ this.setAttribute("disabled", "true");
+ else
+ this.removeAttribute("disabled");
+
+ if (!this.id)
+ return val;
+
+ var elements = document.getElementsByAttribute("preference", this.id);
+ for (var i = 0; i < elements.length; ++i) {
+ elements[i].disabled = val;
+
+ var labels = document.getElementsByAttribute("control", elements[i].id);
+ for (var j = 0; j < labels.length; ++j)
+ labels[j].disabled = val;
+ }
+
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="tabIndex">
+ <getter>
+ return parseInt(this.getAttribute("tabindex"));
+ </getter>
+ <setter>
+ <![CDATA[
+ if (val)
+ this.setAttribute("tabindex", val);
+ else
+ this.removeAttribute("tabindex");
+
+ if (!this.id)
+ return val;
+
+ var elements = document.getElementsByAttribute("preference", this.id);
+ for (var i = 0; i < elements.length; ++i) {
+ elements[i].tabIndex = val;
+
+ var labels = document.getElementsByAttribute("control", elements[i].id);
+ for (var j = 0; j < labels.length; ++j)
+ labels[j].tabIndex = val;
+ }
+
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="hasUserValue">
+ <getter>
+ <![CDATA[
+ return this.preferences.rootBranch.prefHasUserValue(this.name) &&
+ this.value !== undefined;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="reset">
+ <body>
+ // defer reset until preference update
+ this.value = undefined;
+ </body>
+ </method>
+
+ <field name="_useDefault">false</field>
+ <property name="defaultValue">
+ <getter>
+ <![CDATA[
+ this._useDefault = true;
+ var val = this.valueFromPreferences;
+ this._useDefault = false;
+ return val;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="_branch">
+ <getter>
+ return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch;
+ </getter>
+ </property>
+
+ <field name="batching">false</field>
+
+ <method name="_reportUnknownType">
+ <body>
+ <![CDATA[
+ var consoleService = Cc["@mozilla.org/consoleservice;1"]
+ .getService(Ci.nsIConsoleService);
+ var msg = "<preference> with id='" + this.id + "' and name='" +
+ this.name + "' has unknown type '" + this.type + "'.";
+ consoleService.logStringMessage(msg);
+ ]]>
+ </body>
+ </method>
+
+ <property name="valueFromPreferences">
+ <getter>
+ <![CDATA[
+ try {
+ // Force a resync of value with preferences.
+ switch (this.type) {
+ case "int":
+ return this._branch.getIntPref(this.name);
+ case "bool":
+ var val = this._branch.getBoolPref(this.name);
+ return this.inverted ? !val : val;
+ case "wstring":
+ return this._branch
+ .getComplexValue(this.name, Ci.nsIPrefLocalizedString)
+ .data;
+ case "string":
+ case "unichar":
+ return this._branch.getStringPref(this.name);
+ case "fontname":
+ var family = this._branch.getStringPref(this.name);
+ var fontEnumerator = Cc["@mozilla.org/gfx/fontenumerator;1"]
+ .createInstance(Ci.nsIFontEnumerator);
+ return fontEnumerator.getStandardFamilyName(family);
+ case "file":
+ var f = this._branch
+ .getComplexValue(this.name, Ci.nsIFile);
+ return f;
+ default:
+ this._reportUnknownType();
+ }
+ } catch (e) { }
+ return null;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ // Exit early if nothing to do.
+ if (this.readonly || this.valueFromPreferences == val)
+ return val;
+
+ // The special value undefined means 'reset preference to default'.
+ if (val === undefined) {
+ this.preferences.rootBranch.clearUserPref(this.name);
+ return val;
+ }
+
+ // Force a resync of preferences with value.
+ switch (this.type) {
+ case "int":
+ this.preferences.rootBranch.setIntPref(this.name, val);
+ break;
+ case "bool":
+ this.preferences.rootBranch.setBoolPref(this.name, this.inverted ? !val : val);
+ break;
+ case "wstring":
+ var pls = Cc["@mozilla.org/pref-localizedstring;1"]
+ .createInstance(Ci.nsIPrefLocalizedString);
+ pls.data = val;
+ this.preferences.rootBranch
+ .setComplexValue(this.name, Ci.nsIPrefLocalizedString, pls);
+ break;
+ case "string":
+ case "unichar":
+ case "fontname":
+ this.preferences.rootBranch.setStringPref(this.name, val);
+ break;
+ case "file":
+ var lf;
+ if (typeof(val) == "string") {
+ lf = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsIFile);
+ lf.persistentDescriptor = val;
+ if (!lf.exists())
+ lf.initWithPath(val);
+ } else {
+ lf = val.QueryInterface(Ci.nsIFile);
+ }
+ this.preferences.rootBranch
+ .setComplexValue(this.name, Ci.nsIFile, lf);
+ break;
+ default:
+ this._reportUnknownType();
+ }
+ if (!this.batching)
+ this.preferences.service.savePrefFile(null);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <method name="setElementValue">
+ <parameter name="aElement"/>
+ <body>
+ <![CDATA[
+ if (this.locked)
+ aElement.disabled = true;
+
+ if (!this.isElementEditable(aElement))
+ return;
+
+ var rv = undefined;
+ if (aElement.hasAttribute("onsyncfrompreference")) {
+ // Value changed, synthesize an event
+ try {
+ var event = document.createEvent("Events");
+ event.initEvent("syncfrompreference", true, true);
+ var f = new Function("event",
+ aElement.getAttribute("onsyncfrompreference"));
+ rv = f.call(aElement, event);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ var val = rv;
+ if (val === undefined)
+ val = this.instantApply ? this.valueFromPreferences : this.value;
+ // if the preference is marked for reset, show default value in UI
+ if (val === undefined)
+ val = this.defaultValue;
+
+ /**
+ * Initialize a UI element property with a value. Handles the case
+ * where an element has not yet had a XBL binding attached for it and
+ * the property setter does not yet exist by setting the same attribute
+ * on the XUL element using DOM apis and assuming the element's
+ * constructor or property getters appropriately handle this state.
+ */
+ function setValue(element, attribute, value) {
+ if (attribute in element)
+ element[attribute] = value;
+ else
+ element.setAttribute(attribute, value);
+ }
+ if (aElement.localName == "checkbox") {
+ setValue(aElement, "checked", val);
+ } else if (aElement.localName == "colorpicker") {
+ setValue(aElement, "color", val);
+ } else if (aElement.localName == "textbox") {
+ // XXXmano Bug 303998: Avoid a caret placement issue if either the
+ // preference observer or its setter calls updateElements as a result
+ // of the input event handler.
+ if (aElement.value !== val)
+ setValue(aElement, "value", val);
+ } else {
+ setValue(aElement, "value", val);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="getElementValue">
+ <parameter name="aElement"/>
+ <body>
+ <![CDATA[
+ if (aElement.hasAttribute("onsynctopreference")) {
+ // Value changed, synthesize an event
+ try {
+ var event = document.createEvent("Events");
+ event.initEvent("synctopreference", true, true);
+ var f = new Function("event",
+ aElement.getAttribute("onsynctopreference"));
+ var rv = f.call(aElement, event);
+ if (rv !== undefined)
+ return rv;
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+
+ /**
+ * Read the value of an attribute from an element, assuming the
+ * attribute is a property on the element's node API. If the property
+ * is not present in the API, then assume its value is contained in
+ * an attribute, as is the case before a binding has been attached.
+ */
+ function getValue(element, attribute) {
+ if (attribute in element)
+ return element[attribute];
+ return element.getAttribute(attribute);
+ }
+ if (aElement.localName == "checkbox")
+ var value = getValue(aElement, "checked");
+ else if (aElement.localName == "colorpicker")
+ value = getValue(aElement, "color");
+ else
+ value = getValue(aElement, "value");
+
+ switch (this.type) {
+ case "int":
+ return parseInt(value, 10) || 0;
+ case "bool":
+ return typeof(value) == "boolean" ? value : value == "true";
+ }
+ return value;
+ ]]>
+ </body>
+ </method>
+
+ <method name="isElementEditable">
+ <parameter name="aElement"/>
+ <body>
+ <![CDATA[
+ switch (aElement.localName) {
+ case "checkbox":
+ case "colorpicker":
+ case "radiogroup":
+ case "textbox":
+ case "richlistitem":
+ case "richlistbox":
+ case "menulist":
+ return true;
+ }
+ return aElement.getAttribute("preference-editable") == "true";
+ ]]>
+ </body>
+ </method>
+
+ <method name="updateElements">
+ <body>
+ <![CDATA[
+ if (!this.id)
+ return;
+
+ // This "change" event handler tracks changes made to preferences by
+ // sources other than the user in this window.
+ var elements = document.getElementsByAttribute("preference", this.id);
+ for (var i = 0; i < elements.length; ++i)
+ this.setElementValue(elements[i]);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="change">
+ this.updateElements();
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="prefpane">
+ <implementation>
+ <method name="writePreferences">
+ <parameter name="aFlushToDisk"/>
+ <body>
+ <![CDATA[
+ // Write all values to preferences.
+ if (this._deferredValueUpdateElements.size) {
+ this._finalizeDeferredElements();
+ }
+
+ var preferences = this.preferences;
+ for (var i = 0; i < preferences.length; ++i) {
+ var preference = preferences[i];
+ preference.batching = true;
+ preference.valueFromPreferences = preference.value;
+ preference.batching = false;
+ }
+ if (aFlushToDisk) {
+ var psvc = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefService);
+ psvc.savePrefFile(null);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="src"
+ onget="return this.getAttribute('src');"
+ onset="this.setAttribute('src', val); return val;"/>
+ <property name="selected"
+ onget="return this.getAttribute('selected') == 'true';"
+ onset="this.setAttribute('selected', val); return val;"/>
+ <property name="image"
+ onget="return this.getAttribute('image');"
+ onset="this.setAttribute('image', val); return val;"/>
+ <property name="label"
+ onget="return this.getAttribute('label');"
+ onset="this.setAttribute('label', val); return val;"/>
+
+ <property name="preferenceElements"
+ onget="return this.getElementsByAttribute('preference', '*');"/>
+ <property name="preferences"
+ onget="return this.getElementsByTagName('preference');"/>
+
+ <property name="helpTopic">
+ <getter>
+ <![CDATA[
+ // if there are tabs, and the selected tab provides a helpTopic, return that
+ var box = this.getElementsByTagName("tabbox");
+ if (box[0]) {
+ var tab = box[0].selectedTab;
+ if (tab && tab.hasAttribute("helpTopic"))
+ return tab.getAttribute("helpTopic");
+ }
+
+ // otherwise, return the helpTopic of the current panel
+ return this.getAttribute("helpTopic");
+ ]]>
+ </getter>
+ </property>
+
+ <field name="_loaded">false</field>
+ <property name="loaded"
+ onget="return !this.src ? true : this._loaded;"
+ onset="this._loaded = val; return val;"/>
+
+ <method name="preferenceForElement">
+ <parameter name="aElement"/>
+ <body>
+ return document.getElementById(aElement.getAttribute("preference"));
+ </body>
+ </method>
+
+ <method name="getPreferenceElement">
+ <parameter name="aStartElement"/>
+ <body>
+ <![CDATA[
+ var temp = aStartElement;
+ while (temp && temp.nodeType == Node.ELEMENT_NODE &&
+ !temp.hasAttribute("preference"))
+ temp = temp.parentNode;
+ return temp && temp.nodeType == Node.ELEMENT_NODE ?
+ temp : aStartElement;
+ ]]>
+ </body>
+ </method>
+
+ <property name="DeferredTask" readonly="true">
+ <getter><![CDATA[
+ let module = {};
+ ChromeUtils.import("resource://gre/modules/DeferredTask.jsm", module);
+ Object.defineProperty(this, "DeferredTask", {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: module.DeferredTask,
+ });
+ return module.DeferredTask;
+ ]]></getter>
+ </property>
+ <method name="_deferredValueUpdate">
+ <parameter name="aElement"/>
+ <body>
+ <![CDATA[
+ delete aElement._deferredValueUpdateTask;
+ let preference = document.getElementById(aElement.getAttribute("preference"));
+ let prefVal = preference.getElementValue(aElement);
+ preference.value = prefVal;
+ this._deferredValueUpdateElements.delete(aElement);
+ ]]>
+ </body>
+ </method>
+ <field name="_deferredValueUpdateElements">
+ new Set();
+ </field>
+ <method name="_finalizeDeferredElements">
+ <body>
+ <![CDATA[
+ for (let el of this._deferredValueUpdateElements) {
+ if (el._deferredValueUpdateTask) {
+ el._deferredValueUpdateTask.finalize();
+ }
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="userChangedValue">
+ <parameter name="aElement"/>
+ <body>
+ <![CDATA[
+ let element = this.getPreferenceElement(aElement);
+ if (element.hasAttribute("preference")) {
+ if (element.getAttribute("delayprefsave") != "true") {
+ var preference = document.getElementById(element.getAttribute("preference"));
+ var prefVal = preference.getElementValue(element);
+ preference.value = prefVal;
+ } else {
+ if (!element._deferredValueUpdateTask) {
+ element._deferredValueUpdateTask = new this.DeferredTask(this._deferredValueUpdate.bind(this, element), 1000);
+ this._deferredValueUpdateElements.add(element);
+ } else {
+ // Each time the preference is changed, restart the delay.
+ element._deferredValueUpdateTask.disarm();
+ }
+ element._deferredValueUpdateTask.arm();
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="contentHeight">
+ <getter>
+ var targetHeight = parseInt(window.getComputedStyle(this).height);
+ targetHeight += parseInt(window.getComputedStyle(this).marginTop);
+ targetHeight += parseInt(window.getComputedStyle(this).marginBottom);
+ return targetHeight;
+ </getter>
+ </property>
+ </implementation>
+ <handlers>
+ <handler event="command">
+ // This "command" event handler tracks changes made to preferences by
+ // the user in this window.
+ if (event.sourceEvent)
+ event = event.sourceEvent;
+ this.userChangedValue(event.target);
+ </handler>
+ <handler event="select">
+ // This "select" event handler tracks changes made to colorpicker
+ // preferences by the user in this window.
+ if (event.target.localName == "colorpicker")
+ this.userChangedValue(event.target);
+ </handler>
+ <handler event="change">
+ // This "change" event handler tracks changes made to preferences by
+ // the user in this window.
+ this.userChangedValue(event.target);
+ </handler>
+ <handler event="input">
+ // This "input" event handler tracks changes made to preferences by
+ // the user in this window.
+ this.userChangedValue(event.target);
+ </handler>
+ <handler event="paneload">
+ <![CDATA[
+ // Initialize all values from preferences.
+ var elements = this.preferenceElements;
+ for (var i = 0; i < elements.length; ++i) {
+ try {
+ var preference = this.preferenceForElement(elements[i]);
+ preference.setElementValue(elements[i]);
+ } catch (e) {
+ dump("*** No preference found for " + elements[i].getAttribute("preference") + "\n");
+ }
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="panebutton" role="listitem"
+ extends="chrome://global/content/bindings/radio.xml#radio">
+ <content>
+ <xul:image class="paneButtonIcon" xbl:inherits="src"/>
+ <xul:label class="paneButtonLabel" xbl:inherits="value=label"/>
+ </content>
+ </binding>
+
+</bindings>
+
+# -*- 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/.
+
+#
+# This is PrefWindow 6. The Code Could Well Be Ready, Are You?
+#
+# Historical References:
+# PrefWindow V (February 1, 2003)
+# PrefWindow IV (April 24, 2000)
+# PrefWindow III (January 6, 2000)
+# PrefWindow II (???)
+# PrefWindow I (June 4, 1999)
+#
diff --git a/comm/suite/components/bindings/prefwindow.xml b/comm/suite/components/bindings/prefwindow.xml
new file mode 100644
index 0000000000..64173b6c76
--- /dev/null
+++ b/comm/suite/components/bindings/prefwindow.xml
@@ -0,0 +1,548 @@
+<?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/. -->
+
+<!--
+ SeaMonkey Extended Preferences Window Framework
+
+ The binding implemented here mostly works like its toolkit ancestor, with
+ one important difference: the <prefwindow> recognizes the first <tree> in
+ its content and assumes that this is the navigation tree:
+
+ <prefwindow>
+ <tree>
+ ...
+ <treeitem id="prefTreeItemA" prefpane="prefPaneA">
+ ...
+ </tree>
+ <prefpane id="prefPaneB">...</prefpane>
+ <prefpane id="prefPaneA">
+ </prefwindow>
+
+ The <tree> structure defines the hierarchical layout of the preference
+ window's navigation tree. A <treeitem>'s "prefpane" attribute references
+ one of the <prefpane>s given on the <prefwindow>'s main level.
+ All <prefpane>s not referenced by a <treeitem> will be appended to the
+ navigation tree's top level. <treeitem>s can be nested as needed, but
+ <treeitem>s without a related <prefpane> will be hidden.
+
+ Furthermore, if the <prefwindow> has attribute "autopanes" set to "true",
+ non-existing <prefpane>s will be generated automatically from certain
+ attributes of the <treeitem>:
+ - "url" must contain the <prefpane>'s url
+ - "prefpane" should contain the <prefpane>'s desired id,
+ otherwise its url will be used as id
+ - "helpTopic" may contain an index into SeaMonkey's help
+
+ Unlike in XPFE, where preferences panels were loaded into a separate
+ iframe, <prefpane>s are an integral part of the <prefwindow> document,
+ by virtue of loadOverlay. Hence <script>s will be loaded into the
+ <prefwindow> scope and possibly clash. To avoid this, <prefpane>s should
+ specify a "script" attribute with a whitespace delimited list of scripts
+ to load into the <prefpane>'s context. The subscriptloader will take care
+ of any internal scoping, so no this.* fest is necessary inside the script.
+
+ <prefwindow> users who want to share the very same file between SeaMonkey
+ and other toolkit apps should hide the <tree> (set <tree>.hidden=true);
+ this binding will then unhide the <tree> if necessary, ie more than just
+ one <prefpane> exists.
+ Also, the <tree> will get the class "prefnavtree" added, so that it may be
+ prestyled by the SeaMonkey themes.
+ Setting <prefwindow xpfe="false"> will enforce the application of just the
+ basic toolkit <prefwindow> even in SeaMonkey. The same "xpfe" attribute
+ exists for <prefpane>, too.
+-->
+
+<!DOCTYPE bindings [
+ <!ENTITY % dtdPrefs SYSTEM "chrome://communicator/locale/pref/preferences.dtd"> %dtdPrefs;
+ <!ENTITY % dtdGlobalPrefs SYSTEM "chrome://global/locale/preferences.dtd"> %dtdGlobalPrefs;
+]>
+
+<bindings id="prefwindowBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="prefwindow"
+ extends="chrome://communicator/content/bindings/preferences.xml#prefwindow">
+ <resources>
+ <stylesheet src="chrome://communicator/skin/preferences.css"/>
+ </resources>
+
+ <!-- The only difference between the following <content> and its toolkit
+ ancestor is the help button and the 'navTrees' <vbox> before the 'paneDeck'! -->
+ <content dlgbuttons="accept,cancel" persist="lastSelected screenX screenY"
+ closebuttonlabel="&preferencesCloseButton.label;"
+ closebuttonaccesskey="&preferencesCloseButton.accesskey;"
+ role="dialog">
+ <xul:radiogroup anonid="selector" orient="horizontal" class="paneSelector chromeclass-toolbar"
+ role="listbox"/> <!-- Expose to accessibility APIs as a listbox -->
+ <xul:hbox flex="1" class="paneDeckContainer">
+ <xul:vbox anonid="navTrees">
+ <children includes="tree"/>
+ </xul:vbox>
+ <xul:vbox flex="1">
+ <xul:dialogheader anonid="paneHeader" hidden="true"/>
+ <xul:deck anonid="paneDeck" flex="1">
+ <children includes="prefpane"/>
+ </xul:deck>
+ </xul:vbox>
+ </xul:hbox>
+ <xul:hbox anonid="dlg-buttons" class="prefWindow-dlgbuttons" pack="end">
+#ifdef XP_UNIX
+ <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+ <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
+ <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+ <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+ <xul:spacer anonid="spacer" flex="1"/>
+ <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
+ <xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
+#else
+ <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+ <xul:spacer anonid="spacer" flex="1"/>
+ <xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
+ <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+ <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
+ <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
+ <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+#endif
+ </xul:hbox>
+ <xul:hbox>
+ <children/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ // grab the first child tree and try to tie it to the prefpanes
+ var tree = this.getElementsByTagName('tree')[0];
+ this.initNavigationTree(tree);
+ // hide the toolkit pref strip if we have a tree
+ if (this._navigationTree)
+ this._selector.hidden = true;
+ ]]>
+ </constructor>
+
+ <field name="_navigationTree">null</field>
+
+ <!-- <prefwindow> users can call this method to exchange the <tree> -->
+ <method name="initNavigationTree">
+ <parameter name="aTreeElement"/>
+ <body>
+ <![CDATA[
+ this._navigationTree = null;
+ if (!aTreeElement)
+ return;
+
+ // don't grab trees in prefpanes etc.
+ if (aTreeElement.parentNode != this)
+ return;
+
+ // autogenerate <prefpane>s from <treecell>.url if requested
+ var autopanes = (this.getAttribute('autopanes') == 'true');
+ if (!autopanes)
+ {
+ // without autopanes, we can return early: don't bother
+ // with a navigation tree if we only have one prefpane
+ aTreeElement.hidden = (this.preferencePanes.length < 2);
+ if (aTreeElement.hidden)
+ return;
+ }
+
+ // ensure that we have a tree body
+ if (!aTreeElement.getElementsByTagName('treechildren').length)
+ aTreeElement.appendChild(document.createElement('treechildren'));
+
+ // ensure that we have a tree column
+ if (!aTreeElement.getElementsByTagName('treecol').length)
+ {
+ var navcols = document.createElement('treecols');
+ var navcol = document.createElement('treecol');
+ navcol.setAttribute('id', 'navtreecol');
+ navcol.setAttribute('primary', true);
+ navcol.setAttribute('flex', 1);
+ navcol.setAttribute('hideheader', true);
+ navcols.appendChild(navcol);
+ aTreeElement.appendChild(navcols);
+ aTreeElement.setAttribute('hidecolumnpicker', true);
+ }
+
+ // add the class "prefnavtree", so that themes can set defaults
+ aTreeElement.className += ' prefnavtree';
+
+ // Do some magic with the treeitem ingredient:
+ // - if it has a label attribute but no treerow child,
+ // generate a treerow with a treecell child with that label
+ // - if it has a prefpane attribute, tie it to that panel
+ // - if still no panel found and a url attribute is present,
+ // autogenerate the prefpane and connect to it
+ var treeitems = aTreeElement.getElementsByTagName('treeitem');
+ for (var i = 0; i < treeitems.length; ++i)
+ {
+ var node = treeitems[i];
+ var label = node.getAttribute('label');
+ if (label)
+ {
+ // autocreate the treecell?
+ var row = node.firstChild;
+ while (row && row.nodeName != 'treerow')
+ row = row.nextSibling;
+ if (!row)
+ {
+ var itemrow = document.createElement('treerow');
+ var itemcell = document.createElement('treecell');
+ itemcell.setAttribute('label', label);
+ itemrow.appendChild(itemcell);
+ node.appendChild(itemrow);
+ }
+ }
+ var paneID = node.getAttribute('prefpane');
+ var pane = paneID && document.getElementById(paneID);
+ if (!pane && autopanes)
+ {
+ // if we have a url, create a <prefpane> for it
+ var paneURL = node.getAttribute('url');
+ if (paneURL)
+ {
+ // reuse paneID if present, else use the url as id
+ pane = document.createElement('prefpane');
+ pane.setAttribute('id', paneID || paneURL);
+ pane.setAttribute('src', paneURL);
+ pane.setAttribute('label', label || paneID || paneURL);
+ var helpTopic = node.getAttribute('helpTopic');
+ if (helpTopic)
+ {
+ pane.setAttribute('helpURI', 'chrome://communicator/locale/help/suitehelp.rdf');
+ pane.setAttribute('helpTopic', helpTopic);
+ }
+ // add pane to prefwindow
+ this.appendChild(pane);
+ }
+ }
+ node.prefpane = pane;
+ if (pane)
+ pane.preftreeitem = node;
+ // hide unused treeitems
+ node.hidden = !pane;
+ }
+
+ // now that the number of <prefpane>s is known, try to return early:
+ // don't bother with a navigation tree if we only have one prefpane
+ aTreeElement.hidden = (this.preferencePanes.length < 2);
+ if (aTreeElement.hidden)
+ return;
+ this._navigationTree = aTreeElement;
+
+ // append any still unreferenced <prefpane>s to the tree's top level
+ for (var j = 0; j < this.preferencePanes.length; ++j)
+ {
+ // toolkit believes in fancy pane resizing - we don't
+ var lostpane = this.preferencePanes[j];
+ lostpane.setAttribute('flex', 1);
+
+ if (!("preftreeitem" in lostpane))
+ {
+ var treebody = this._navigationTree
+ .getElementsByTagName('treechildren')[0];
+ var treeitem = document.createElement('treeitem');
+ var treerow = document.createElement('treerow');
+ var treecell = document.createElement('treecell');
+ var label = lostpane.getAttribute('label');
+ if (!label)
+ label = lostpane.getAttribute('id');
+ treecell.setAttribute('label', label);
+ treerow.appendChild(treecell);
+ treeitem.appendChild(treerow);
+ treebody.appendChild(treeitem);
+ treeitem.prefpane = lostpane;
+ lostpane.preftreeitem = treeitem;
+ }
+ }
+
+ // Some parts of the toolkit base binding's initialization code (like
+ // panel select events) "fire" before we get here. Thus, we may need
+ // to sync the tree manually now (again), if we added any panels or
+ // if toolkit failed to select one.
+ // (This is a loose copy from the toolkit ctor.)
+ var lastPane = this.lastSelected &&
+ document.getElementById(this.lastSelected);
+ if (!lastPane)
+ this.lastSelected = "";
+ if ("arguments" in window && window.arguments[0])
+ {
+ var initialPane = document.getElementById(window.arguments[0]);
+ if (initialPane && initialPane.nodeName == "prefpane")
+ {
+ this.currentPane = initialPane;
+ this.lastSelected = initialPane.id;
+ }
+ }
+ else if (lastPane)
+ this.currentPane = lastPane;
+ try
+ {
+ this.showPane(this.currentPane); // may need to load it first
+ this.syncTreeWithPane(this.currentPane, true);
+ }
+ catch (e)
+ {
+ dump('***** broken prefpane: ' + this.currentPane.id + '\n' + e + '\n');
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- don't do any fancy animations -->
+ <property name="_shouldAnimate" onget="return false;"/>
+
+ <method name="setPaneTitle">
+ <parameter name="aPaneElement"/>
+ <body>
+#ifndef XP_MACOSX
+ <![CDATA[
+ // show pane title, if given
+ var paneHeader = document.getAnonymousElementByAttribute(this, 'anonid', 'paneHeader');
+ var paneHeaderLabel = '';
+ if (aPaneElement)
+ paneHeaderLabel = aPaneElement.getAttribute('label');
+ paneHeader.hidden = !paneHeaderLabel;
+ if (!paneHeader.hidden)
+ paneHeader.setAttribute('title', paneHeaderLabel);
+ ]]>
+#endif
+ </body>
+ </method>
+
+ <method name="syncPaneWithTree">
+ <parameter name="aTreeIndex"/>
+ <body>
+ <![CDATA[
+ var pane = null;
+ if ((this._navigationTree) && (aTreeIndex >= 0))
+ {
+ // load the prefpane associated with this treeitem
+ var treeitem = this._navigationTree.contentView
+ .getItemAtIndex(aTreeIndex);
+ if ('prefpane' in treeitem)
+ {
+ pane = treeitem.prefpane;
+ if (pane && (this.currentPane != pane))
+ {
+ try
+ {
+ this.showPane(pane); // may need to load it first
+ }
+ catch (e)
+ {
+ dump('***** broken prefpane: ' + pane.id + '\n' + e + '\n');
+ pane = null;
+ }
+ }
+ }
+ }
+ // don't show broken panels
+ this._paneDeck.hidden = (pane == null);
+ this.setPaneTitle(pane);
+ ]]>
+ </body>
+ </method>
+
+ <method name="syncTreeWithPane">
+ <parameter name="aPane"/>
+ <parameter name="aExpand"/>
+ <body>
+ <![CDATA[
+ if (this._navigationTree && aPane)
+ {
+ if ('preftreeitem' in aPane)
+ {
+ // make sure the treeitem is visible
+ var container = aPane.preftreeitem;
+ if (!aExpand)
+ container = container.parentNode.parentNode;
+ while (container != this._navigationTree)
+ {
+ container.setAttribute('open', true);
+ container = container.parentNode.parentNode;
+ }
+
+ // mark selected pane in navigation tree
+ var index = this._navigationTree.contentView
+ .getIndexOfItem(aPane.preftreeitem);
+ this._navigationTree.view.selection.select(index);
+ }
+ }
+ this.setPaneTitle(aPane);
+ if (this.getAttribute("overflow") != "auto")
+ {
+ if (this.scrollHeight > window.innerHeight)
+ window.innerHeight = this.scrollHeight;
+ if (this.scrollWidth > window.innerWidth)
+ window.innerWidth = this.scrollWidth;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- copied from contextHelp.js
+ Locate existing help window for this helpFileURI. -->
+ <method name="locateHelpWindow">
+ <parameter name="helpFileURI"/>
+ <body>
+ <![CDATA[
+ const iterator = Services.wm.getEnumerator("suite:help");
+ var topWindow = null;
+ var aWindow;
+
+ // Loop through help windows looking for one with selected helpFileURI
+ while (iterator.hasMoreElements())
+ {
+ aWindow = iterator.getNext();
+ if (aWindow.closed)
+ continue;
+ if (aWindow.getHelpFileURI() == helpFileURI)
+ topWindow = aWindow;
+ }
+ return topWindow;
+ ]]>
+ </body>
+ </method>
+
+ <!-- copied from contextHelp.js
+ Opens up the Help Viewer with the specified topic and helpFileURI. -->
+ <method name="openHelp">
+ <parameter name="topic"/>
+ <parameter name="helpFileURI"/>
+ <body>
+ <![CDATA[
+ // Empty help windows are not helpful...
+ if (!helpFileURI)
+ return;
+
+ // Try to find previously opened help.
+ var topWindow = this.locateHelpWindow(helpFileURI);
+ if (topWindow)
+ {
+ // Open topic in existing window.
+ topWindow.focus();
+ topWindow.displayTopic(topic);
+ }
+ else
+ {
+ // Open topic in new window.
+ const params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+ params.SetNumberStrings(2);
+ params.SetString(0, helpFileURI);
+ params.SetString(1, topic);
+ Services.ww.openWindow(null,
+ "chrome://help/content/help.xul",
+ "_blank",
+ "chrome,all,alwaysRaised,dialog=no",
+ params);
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="dialoghelp">
+ <![CDATA[
+ this.openHelp(this.currentPane.helpTopic, this.currentPane.getAttribute("helpURI"));
+ ]]>
+ </handler>
+ <handler event="select">
+ <![CDATA[
+ // navigation tree select or deck change?
+ var target = event.originalTarget;
+ if (target == this._navigationTree)
+ {
+ this.syncPaneWithTree(target.currentIndex);
+ }
+ else if (target == this._paneDeck)
+ {
+ // deck.selectedIndex is a string!
+ var pane = this.preferencePanes[Number(target.selectedIndex)];
+ this.syncTreeWithPane(pane, false);
+ }
+ ]]>
+ </handler>
+
+ <handler event="paneload">
+ <![CDATA[
+ // panes may load asynchronously,
+ // so we have to "late-sync" those to our navigation tree
+ this.syncTreeWithPane(event.originalTarget, false);
+ ]]>
+ </handler>
+
+ <handler event="keypress" key="&focusSearch.key;" modifiers="accel">
+ <![CDATA[
+ var searchBox = this.currentPane.getElementsByAttribute("type", "search")[0];
+ if (searchBox)
+ {
+ searchBox.focus();
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="prefpane"
+ extends="chrome://communicator/content/bindings/preferences.xml#prefpane">
+ <resources>
+ <stylesheet src="chrome://communicator/skin/preferences.css"/>
+ </resources>
+
+ <handlers>
+ <handler event="paneload">
+ <![CDATA[
+ // Since all <prefpane>s now share the same global document, their
+ // <script>s might clash. Thus we expect the "script" attribute to
+ // contain a whitespace delimited list of script files to be loaded
+ // into the <prefpane>'s context.
+
+ // list of scripts to load
+ var scripts = this.getAttribute('script').match(/\S+/g);
+ if (!scripts)
+ return;
+ var count = scripts.length;
+ for (var i = 0; i < count; ++i)
+ {
+ var script = scripts[i];
+ if (script)
+ {
+ try
+ {
+ Services.scriptloader.loadSubScript(script, this);
+ }
+ catch (e)
+ {
+ let errorStr =
+ "prefpane.paneload: loadSubScript(" + script + ") failed:\n" +
+ (e.fileName ? "at " + e.fileName + " : " + e.lineNumber + "\n"
+ : "") +
+ e + " - " + e.stack + "\n";
+ dump(errorStr);
+ Cu.reportError(errorStr);
+ }
+ }
+ }
+
+ // if we have a Startup method, call it
+ if ('Startup' in this)
+ this.Startup();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/bindings/spinbuttons.xml b/comm/suite/components/bindings/spinbuttons.xml
new file mode 100644
index 0000000000..90c249c9b2
--- /dev/null
+++ b/comm/suite/components/bindings/spinbuttons.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<bindings id="spinbuttonsBindings"
+ 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="spinbuttons"
+ extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+ <content>
+ <xul:vbox class="spinbuttons-box" flex="1">
+ <xul:button anonid="increaseButton" type="repeat" flex="1"
+ class="spinbuttons-button spinbuttons-up"
+ xbl:inherits="disabled,disabled=increasedisabled"/>
+ <xul:button anonid="decreaseButton" type="repeat" flex="1"
+ class="spinbuttons-button spinbuttons-down"
+ xbl:inherits="disabled,disabled=decreasedisabled"/>
+ </xul:vbox>
+ </content>
+
+ <implementation>
+ <property name="_increaseButton" readonly="true">
+ <getter>
+ return document.getAnonymousElementByAttribute(this, "anonid", "increaseButton");
+ </getter>
+ </property>
+ <property name="_decreaseButton" readonly="true">
+ <getter>
+ return document.getAnonymousElementByAttribute(this, "anonid", "decreaseButton");
+ </getter>
+ </property>
+
+ <property name="increaseDisabled"
+ onget="return this._increaseButton.getAttribute('disabled') == 'true';"
+ onset="if (val) this._increaseButton.setAttribute('disabled', 'true');
+ else this._increaseButton.removeAttribute('disabled'); return val;"/>
+ <property name="decreaseDisabled"
+ onget="return this._decreaseButton.getAttribute('disabled') == 'true';"
+ onset="if (val) this._decreaseButton.setAttribute('disabled', 'true');
+ else this._decreaseButton.removeAttribute('disabled'); return val;"/>
+ </implementation>
+
+ <handlers>
+ <handler event="mousedown">
+ <![CDATA[
+ // on the Mac, the native theme draws the spinbutton as a single widget
+ // so a state attribute is set based on where the mouse button was pressed
+ if (event.originalTarget == this._increaseButton)
+ this.setAttribute("state", "up");
+ else if (event.originalTarget == this._decreaseButton)
+ this.setAttribute("state", "down");
+ ]]>
+ </handler>
+
+ <handler event="mouseup">
+ this.removeAttribute("state");
+ </handler>
+ <handler event="mouseout">
+ this.removeAttribute("state");
+ </handler>
+
+ <handler event="command">
+ <![CDATA[
+ var eventname;
+ if (event.originalTarget == this._increaseButton)
+ eventname = "up";
+ else if (event.originalTarget == this._decreaseButton)
+ eventname = "down";
+
+ var evt = document.createEvent("Events");
+ evt.initEvent(eventname, true, true);
+ var cancel = this.dispatchEvent(evt);
+
+ if (this.hasAttribute("on" + eventname)) {
+ var fn = new Function("event", this.getAttribute("on" + eventname));
+ if (!fn.call(this, event))
+ cancel = true;
+ }
+
+ return !cancel;
+ ]]>
+ </handler>
+
+ </handlers>
+ </binding>
+
+</bindings> \ No newline at end of file
diff --git a/comm/suite/components/bindings/textbox.xml b/comm/suite/components/bindings/textbox.xml
new file mode 100644
index 0000000000..74893c29a8
--- /dev/null
+++ b/comm/suite/components/bindings/textbox.xml
@@ -0,0 +1,251 @@
+<?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/. -->
+
+<!-- This files relies on these specific Chrome/XBL globals -->
+<!-- globals ChromeWindow -->
+
+<!DOCTYPE bindings [
+ <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
+ %textcontextDTD;
+]>
+
+<bindings id="textboxBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="textbox">
+
+ <content>
+ <children/>
+ <xul:moz-input-box class="textbox-input-box" flex="1"
+ xbl:inherits="context,spellcheck">
+ <html:input class="textbox-input" anonid="input"
+ xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,noinitialfocus,mozactionhint,spellcheck"/>
+ </xul:moz-input-box>
+ </content>
+
+ <implementation implements="nsIDOMXULLabeledControlElement">
+ <!-- nsIDOMXULLabeledControlElement -->
+ <field name="crop">""</field>
+ <field name="image">""</field>
+ <field name="command">""</field>
+ <field name="accessKey">""</field>
+
+ <field name="mInputField">null</field>
+ <field name="mIgnoreClick">false</field>
+ <field name="mIgnoreFocus">false</field>
+ <field name="mEditor">null</field>
+
+ <property name="inputField" readonly="true">
+ <getter><![CDATA[
+ if (!this.mInputField)
+ this.mInputField = document.getAnonymousElementByAttribute(this, "anonid", "input");
+ return this.mInputField;
+ ]]></getter>
+ </property>
+
+ <property name="value" onset="this.inputField.value = val; return val;"
+ onget="return this.inputField.value;"/>
+ <property name="defaultValue" onset="this.inputField.defaultValue = val; return val;"
+ onget="return this.inputField.defaultValue;"/>
+ <property name="label" onset="this.setAttribute('label', val); return val;"
+ onget="return this.getAttribute('label') ||
+ (this.labelElement ? this.labelElement.value :
+ this.placeholder);"/>
+ <property name="placeholder" onset="this.inputField.placeholder = val; return val;"
+ onget="return this.inputField.placeholder;"/>
+ <property name="emptyText" onset="this.placeholder = val; return val;"
+ onget="return this.placeholder;"/>
+ <property name="type" onset="if (val) this.setAttribute('type', val);
+ else this.removeAttribute('type'); return val;"
+ onget="return this.getAttribute('type');"/>
+ <property name="maxLength" onset="this.inputField.maxLength = val; return val;"
+ onget="return this.inputField.maxLength;"/>
+ <property name="disabled" onset="this.inputField.disabled = val;
+ if (val) this.setAttribute('disabled', 'true');
+ else this.removeAttribute('disabled'); return val;"
+ onget="return this.inputField.disabled;"/>
+ <property name="tabIndex" onget="return parseInt(this.getAttribute('tabindex'));"
+ onset="this.inputField.tabIndex = val;
+ if (val) this.setAttribute('tabindex', val);
+ else this.removeAttribute('tabindex'); return val;"/>
+ <property name="size" onset="this.inputField.size = val; return val;"
+ onget="return this.inputField.size;"/>
+ <property name="readOnly" onset="this.inputField.readOnly = val;
+ if (val) this.setAttribute('readonly', 'true');
+ else this.removeAttribute('readonly'); return val;"
+ onget="return this.inputField.readOnly;"/>
+ <property name="clickSelectsAll"
+ onget="return this.getAttribute('clickSelectsAll') == 'true';"
+ onset="if (val) this.setAttribute('clickSelectsAll', 'true');
+ else this.removeAttribute('clickSelectsAll'); return val;" />
+
+ <property name="editor" readonly="true">
+ <getter><![CDATA[
+ if (!this.mEditor) {
+ this.mEditor = this.inputField.editor;
+ }
+ return this.mEditor;
+ ]]></getter>
+ </property>
+
+ <method name="reset">
+ <body><![CDATA[
+ this.value = this.defaultValue;
+ if (!this.editor) {
+ return false;
+ }
+ this.editor.clearUndoRedo();
+ return true;
+ ]]></body>
+ </method>
+
+ <method name="select">
+ <body>
+ this.inputField.select();
+ </body>
+ </method>
+
+ <property name="controllers" readonly="true" onget="return this.inputField.controllers"/>
+ <property name="textLength" readonly="true"
+ onget="return this.inputField.textLength;"/>
+ <property name="selectionStart" onset="this.inputField.selectionStart = val; return val;"
+ onget="return this.inputField.selectionStart;"/>
+ <property name="selectionEnd" onset="this.inputField.selectionEnd = val; return val;"
+ onget="return this.inputField.selectionEnd;"/>
+
+ <method name="setSelectionRange">
+ <parameter name="aSelectionStart"/>
+ <parameter name="aSelectionEnd"/>
+ <body>
+ this.inputField.setSelectionRange(aSelectionStart, aSelectionEnd);
+ </body>
+ </method>
+
+ <method name="_setNewlineHandling">
+ <body><![CDATA[
+ var str = this.getAttribute("newlines");
+ if (str && this.editor) {
+ for (let x in Ci.nsIEditor) {
+ if (/^eNewlines/.test(x)) {
+ if (str == RegExp.rightContext.toLowerCase()) {
+ this.editor.newlineHandling = Ci.nsIEditor[x];
+ break;
+ }
+ }
+ }
+ }
+ ]]></body>
+ </method>
+
+ <method name="_maybeSelectAll">
+ <body><![CDATA[
+ if (!this.mIgnoreClick && this.clickSelectsAll &&
+ document.activeElement == this.inputField &&
+ this.inputField.selectionStart == this.inputField.selectionEnd)
+ this.editor.selectAll();
+ ]]></body>
+ </method>
+
+ <constructor><![CDATA[
+ var str = this.boxObject.getProperty("value");
+ if (str) {
+ this.inputField.value = str;
+ this.boxObject.removeProperty("value");
+ }
+
+ this._setNewlineHandling();
+
+ if (this.hasAttribute("emptytext"))
+ this.placeholder = this.getAttribute("emptytext");
+ ]]></constructor>
+
+ <destructor>
+ <![CDATA[
+ var field = this.inputField;
+ if (field && field.value)
+ this.boxObject.setProperty("value", field.value);
+ this.mInputField = null;
+ ]]>
+ </destructor>
+
+ </implementation>
+
+ <handlers>
+ <handler event="focus" phase="capturing">
+ <![CDATA[
+ if (this.hasAttribute("focused"))
+ return;
+
+ switch (event.originalTarget) {
+ case this:
+ // Forward focus to actual HTML input
+ this.inputField.focus();
+ break;
+ case this.inputField:
+ if (this.mIgnoreFocus) {
+ this.mIgnoreFocus = false;
+ } else if (this.clickSelectsAll) {
+ try {
+ if (!this.editor || !this.editor.composing)
+ this.editor.selectAll();
+ } catch (e) {}
+ }
+ break;
+ default:
+ // Allow other children (e.g. URL bar buttons) to get focus
+ return;
+ }
+ this.setAttribute("focused", "true");
+ ]]>
+ </handler>
+
+ <handler event="blur" phase="capturing">
+ <![CDATA[
+ this.removeAttribute("focused");
+
+ // don't trigger clickSelectsAll when switching application windows
+ if (window == window.top &&
+ window.isChromeWindow &&
+ document.activeElement == this.inputField)
+ this.mIgnoreFocus = true;
+ ]]>
+ </handler>
+
+ <handler event="mousedown">
+ <![CDATA[
+ this.mIgnoreClick = this.hasAttribute("focused");
+
+ if (!this.mIgnoreClick) {
+ this.mIgnoreFocus = true;
+ this.inputField.setSelectionRange(0, 0);
+ if (event.originalTarget == this ||
+ event.originalTarget == this.inputField.parentNode)
+ this.inputField.focus();
+ }
+ ]]>
+ </handler>
+
+ <handler event="click" action="this._maybeSelectAll();"/>
+
+#ifndef XP_WIN
+ <handler event="contextmenu">
+ // Only care about context clicks on the textbox itself.
+ if (event.target != this)
+ return;
+
+ if (!event.button) // context menu opened via keyboard shortcut
+ return;
+ this._maybeSelectAll();
+ // see bug 576135 comment 4
+ let box = this.inputField.parentNode;
+ box._doPopupItemEnabling(box.menupopup);
+ </handler>
+#endif
+ </handlers>
+ </binding>
+</bindings>
diff --git a/comm/suite/components/bindings/toolbar-xpfe.xml b/comm/suite/components/bindings/toolbar-xpfe.xml
new file mode 100644
index 0000000000..d489a2eb73
--- /dev/null
+++ b/comm/suite/components/bindings/toolbar-xpfe.xml
@@ -0,0 +1,333 @@
+<?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/. -->
+
+<bindings id="toolbarBindings"
+ 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">
+
+ <!-- With the move to the new toolkit, SeaMonkey needs to overwrite certain
+ bindings if it wants to keep its distinctive likeness. The bindings
+ found here reimplement the XPFE toolbar behaviour by providing toolbar
+ bindings in in the chrome://communicator/ domain that are based upon the
+ new toolkit's toolbar bindings in the chrome://global/ domain.
+ The now hidden new toolkit bindings are accessible via a set xpfe="false"
+ attribute, though.
+ -->
+
+ <binding id="grippytoolbox" extends="chrome://communicator/content/bindings/toolbar.xml#toolbox">
+ <content orient="vertical">
+ <xul:vbox flex="1" class="toolbar-internal-box">
+ <children/>
+ </xul:vbox>
+ <xul:hbox tbattr="collapsed-tray-holder" class="collapsed-tray-holder" moz-collapsed="true" xbl:inherits="collapsed=inFullscreen">
+ <xul:hbox tbattr="collapsed-tray" class="collapsed-tray"/>
+ <xul:spacer flex="1" class="collapsed-tray-spacer"/>
+ </xul:hbox>
+ </content>
+ <implementation>
+ <field name="palette">
+ this.getElementsByTagName("toolbarpalette").item(0);
+ </field>
+
+ <constructor>
+ <![CDATA[
+ var set = this.toolbarset;
+ if (!set)
+ return;
+ var toolbars = this.getElementsByAttribute("customindex", "*");
+ for (let i = 0; i < toolbars.length; ++i) {
+ let bar = toolbars[i];
+ let name = bar.getAttribute("toolbarname").replace(" ", "_");
+ if (name) {
+ let attrs = ["mode", "iconsize", "labelalign", "hidden",
+ "collapsed", "moz-collapsed"];
+ for (let j = 0; j < attrs.length; j++) {
+ let attr = set.getAttribute(name + attrs[j]);
+ if (attr)
+ bar.setAttribute(attrs[j], attr);
+ }
+ bar.setAttribute("grippytooltiptext", name);
+ }
+ }
+ ]]>
+ </constructor>
+
+ <method name="collapseToolbar">
+ <parameter name="toolbar"/>
+ <body>
+ <![CDATA[
+ try {
+ this.createCollapsedGrippy(toolbar);
+ toolbar.setAttribute("collapsed", "true");
+ document.persist(toolbar.id, "collapsed");
+ toolbar.removeAttribute("moz-collapsed");
+ document.persist(toolbar.id, "moz-collapsed");
+ if (toolbar.hasAttribute("customindex"))
+ this.persistCustomCollapse(toolbar, "true");
+ }
+ catch(e) {
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="expandToolbar">
+ <parameter name="aGrippyID"/>
+ <body>
+ <![CDATA[
+ var idString = aGrippyID.substring("moz_tb_collapsed_".length, aGrippyID.length);
+ var toolbar = document.getElementById(idString);
+ toolbar.setAttribute("collapsed", "false");
+ var collapsedTray = document.getAnonymousElementByAttribute(this, "tbattr", "collapsed-tray");
+ var collapsedToolbar = document.getElementById("moz_tb_collapsed_" + toolbar.id);
+ collapsedToolbar.remove();
+ if (!collapsedTray.hasChildNodes())
+ document.getAnonymousElementByAttribute(this, "tbattr", "collapsed-tray-holder").setAttribute("moz-collapsed", "true");
+ document.persist(toolbar.id, "collapsed");
+ if (toolbar.hasAttribute("customindex"))
+ this.persistCustomCollapse(toolbar, "false");
+ ]]>
+ </body>
+ </method>
+
+ <method name="createCollapsedGrippy">
+ <parameter name="aToolbar"/>
+ <body>
+ <![CDATA[
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var existingGrippy = document.getAnonymousElementByAttribute(this, "id", "moz_tb_collapsed_" + aToolbar.id);
+ if (!existingGrippy) {
+ var grippy = document.getAnonymousElementByAttribute(aToolbar, "tbattr", "toolbar-grippy");
+ var boxObject = grippy.boxObject.QueryInterface(Ci.nsIBoxObject);
+ var collapsedGrippy = document.createElementNS(XUL_NS, "toolbargrippy");
+ if (collapsedGrippy) {
+ var width = boxObject.height > 20 ? boxObject.height : 23;
+ var height = boxObject.width > 10 ? boxObject.width : 12;
+ var styleString = "width: " + width + "px; height: " + height + "px;";
+ collapsedGrippy.setAttribute("style", styleString);
+ collapsedGrippy.setAttribute("tooltiptext", aToolbar.getAttribute("grippytooltiptext"));
+ collapsedGrippy.setAttribute("id", "moz_tb_collapsed_" + aToolbar.id);
+ collapsedGrippy.setAttribute("moz_grippy_collapsed", "true");
+ collapsedGrippy.setAttribute("tbgrippy-collapsed", "true");
+ var collapsedTrayHolder = document.getAnonymousElementByAttribute(this, "tbattr", "collapsed-tray-holder");
+ if (collapsedTrayHolder.getAttribute("moz-collapsed") == "true")
+ collapsedTrayHolder.removeAttribute("moz-collapsed");
+ document.getAnonymousElementByAttribute(this, "tbattr", "collapsed-tray").appendChild(collapsedGrippy);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="persistCustomCollapse">
+ <parameter name="toolbar"/>
+ <parameter name="collapsed"/>
+ <body>
+ <![CDATA[
+ var attr = toolbar.getAttribute("toolbarname").replace(" ", "_") + "collapsed";
+ this.toolbarset.setAttribute(attr, collapsed);
+ document.persist(this.toolbarset.id, attr);
+ var attr = toolbar.getAttribute("toolbarname").replace(" ", "_") + "moz-collapsed";
+ this.toolbarset.removeAttribute(attr);
+ document.persist(this.toolbarset.id, attr);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="grippytoolbar" extends="chrome://communicator/content/bindings/toolbar.xml#toolbar">
+ <content>
+ <xul:hbox flex="1" class="toolbar-box box-inherit">
+ <xul:toolbargrippy xbl:inherits="last-toolbar,hidden=grippyhidden,collapsed=inFullscreen"
+ tbattr="toolbar-grippy"
+ class="toolbar-grippy"/>
+ <xul:hbox flex="1" class="toolbar-holder box-inherit"
+ xbl:inherits="collapsed,last-toolbar,orient,align,pack">
+ <children/>
+ </xul:hbox>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ if (Services.prefs.getBoolPref("browser.toolbars.grippyhidden")) {
+ this.setAttribute("grippyhidden", "true");
+ }
+
+ if (this.getAttribute("moz-collapsed") == "true" &&
+ this.parentNode.localName == "toolbox")
+ this.parentNode.collapseToolbar(this);
+ else if (this.getAttribute("collapsed") == "true" &&
+ this.parentNode.localName == "toolbox")
+ this.parentNode.createCollapsedGrippy(this);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="grippytoolbar-primary" extends="chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar">
+ <implementation implements="nsIObserver">
+ <constructor>
+ <![CDATA[
+ this.prefs.addObserver(this.domain, this);
+ if (this.prefs.getIntPref(this.domain) != 2)
+ this.observe(this.prefs, "nsPref:changed", this.domain);
+ ]]>
+ </constructor>
+
+ <destructor>
+ this.prefs.removeObserver(this.domain, this);
+ </destructor>
+
+ <field name="domain" readonly="true">
+ "browser.chrome.toolbar_style"
+ </field>
+
+ <field name="prefs" readonly="true">
+ Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefService).getBranch(null)
+ </field>
+
+ <method name="observe">
+ <parameter name="subject"/>
+ <parameter name="topic"/>
+ <parameter name="name"/>
+ <body>
+ <![CDATA[
+ if (topic == "nsPref:changed" && name == this.domain) {
+ const styles = ["icons", "text", "full"];
+ const style = styles[this.prefs.getIntPref(name)];
+ this.parentNode.setAttribute("mode", style); // toolbox
+ if (!this.hasAttribute("customizable") ||
+ !this.hasAttribute("ignoremodepref"))
+ this.setAttribute("mode", style);
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="grippytoolbar-drag"
+ extends="chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar">
+ <implementation>
+ <field name="_dragBindingAlive">true</field>
+ <constructor>
+ <![CDATA[
+ if (!this._draggableStarted) {
+ this._draggableStarted = true;
+ try {
+ let tmp = {};
+ ChromeUtils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
+ let draggableThis = new tmp.WindowDraggingElement(this);
+ draggableThis.mouseDownCheck = function(e) {
+ // Don't move while customizing.
+ return this._dragBindingAlive &&
+ this.getAttribute("customizing") != "true";
+ }
+ } catch (e) {}
+ }
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="grippytoolbar-menubar"
+ extends="chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar"
+ display="xul:menubar"/>
+
+ <binding id="grippymenubar" extends="chrome://communicator/content/bindings/toolbar.xml#menubar">
+ <content>
+ <xul:hbox flex="1" class="toolbar-box">
+ <xul:toolbargrippy xbl:inherits="last-toolbar,hidden=grippyhidden"
+ tbattr="toolbar-grippy" class="toolbar-grippy"/>
+ <xul:hbox flex="1" class="toolbar-holder" xbl:inherits="collapsed,last-toolbar">
+ <children/>
+ </xul:hbox>
+ </xul:hbox>
+ </content>
+ <implementation>
+ <constructor>
+ <![CDATA[
+ if (Services.prefs.getBoolPref("browser.toolbars.grippyhidden")) {
+ this.setAttribute("grippyhidden", "true");
+ }
+
+ if (this.getAttribute("moz-collapsed") == "true" &&
+ this.parentNode.localName == "toolbox")
+ this.parentNode.collapseToolbar(this);
+ else if (this.getAttribute("collapsed") == "true" &&
+ this.parentNode.localName == "toolbox")
+ this.parentNode.createCollapsedGrippy(this);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="toolbargrippy" display="xul:button"
+ extends="chrome://communicator/content/bindings/toolbar.xml#toolbar-base">
+ <content>
+ <xul:image class="toolbargrippy-arrow"/>
+ <xul:spacer class="toolbargrippy-texture" flex="1"/>
+ </content>
+
+ <implementation>
+ <property name="collapsed">
+ <getter>
+ return this.hasAttribute("moz_grippy_collapsed");
+ </getter>
+ <setter>
+ if (val)
+ this.setAttribute("moz_grippy_collapsed", "true");
+ else
+ this.removeAttribute("moz_grippy_collapsed");
+ return val;
+ </setter>
+ </property>
+
+ <method name="returnNode">
+ <parameter name="aNodeA"/>
+ <parameter name="aNodeB"/>
+ <body>
+ <![CDATA[
+ var node = this.parentNode;
+ while (node && node.localName != "window" &&
+ (node.localName != aNodeA && (node.localName != aNodeB))) {
+ node = node.parentNode;
+ }
+ return node;
+ ]]>
+ </body>
+ </method>
+
+ <method name="grippyTriggered">
+ <body>
+ <![CDATA[
+ var toolbox = this.returnNode("toolbox");
+ var toolbar = this.returnNode("toolbar", "menubar");
+ if (this.collapsed)
+ toolbox.expandToolbar(this.id);
+ else
+ toolbox.collapseToolbar(toolbar);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="command">
+ <![CDATA[
+ this.grippyTriggered();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+</bindings>
+
diff --git a/comm/suite/components/bindings/toolbar.xml b/comm/suite/components/bindings/toolbar.xml
new file mode 100644
index 0000000000..cf36b421ad
--- /dev/null
+++ b/comm/suite/components/bindings/toolbar.xml
@@ -0,0 +1,579 @@
+<?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/. -->
+
+
+<bindings id="toolbarBindings"
+ 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="toolbox">
+ <implementation>
+ <field name="palette">
+ null
+ </field>
+
+ <field name="toolbarset">
+ null
+ </field>
+
+ <field name="customToolbarCount">
+ 0
+ </field>
+
+ <field name="externalToolbars">
+ []
+ </field>
+
+ <!-- Set by customizeToolbar.js -->
+ <property name="customizing">
+ <getter><![CDATA[
+ return this.getAttribute("customizing") == "true";
+ ]]></getter>
+ <setter><![CDATA[
+ if (val)
+ this.setAttribute("customizing", "true");
+ else
+ this.removeAttribute("customizing");
+ return val;
+ ]]></setter>
+ </property>
+
+ <constructor>
+ <![CDATA[
+ // Look to see if there is a toolbarset.
+ this.toolbarset = this.firstChild;
+ while (this.toolbarset && this.toolbarset.localName != "toolbarset")
+ this.toolbarset = this.toolbarset.nextSibling;
+
+ if (this.toolbarset) {
+ // Create each toolbar described by the toolbarset.
+ var index = 0;
+ while (this.toolbarset.hasAttribute("toolbar" + (++index))) {
+ var toolbarInfo = this.toolbarset.getAttribute("toolbar" + index);
+ var infoSplit = toolbarInfo.split(":");
+ this.appendCustomToolbar(infoSplit[0], infoSplit[1]);
+ }
+ }
+ ]]>
+ </constructor>
+
+ <method name="appendCustomToolbar">
+ <parameter name="aName"/>
+ <parameter name="aCurrentSet"/>
+ <body>
+ <![CDATA[
+ if (!this.toolbarset)
+ return null;
+ var toolbar = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "toolbar");
+ toolbar.id = "__customToolbar_" + aName.replace(" ", "_");
+ toolbar.setAttribute("customizable", "true");
+ toolbar.setAttribute("customindex", ++this.customToolbarCount);
+ toolbar.setAttribute("toolbarname", aName);
+ toolbar.setAttribute("currentset", aCurrentSet);
+ toolbar.setAttribute("mode", this.getAttribute("mode"));
+ toolbar.setAttribute("iconsize", this.getAttribute("iconsize"));
+ toolbar.setAttribute("context", this.toolbarset.getAttribute("context"));
+ toolbar.setAttribute("class", "chromeclass-toolbar");
+
+ this.insertBefore(toolbar, this.toolbarset);
+ return toolbar;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="toolbar" role="xul:toolbar">
+ <implementation>
+ <property name="toolbarName"
+ onget="return this.getAttribute('toolbarname');"
+ onset="this.setAttribute('toolbarname', val); return val;"/>
+
+ <field name="_toolbox">null</field>
+ <property name="toolbox" readonly="true">
+ <getter><![CDATA[
+ if (this._toolbox)
+ return this._toolbox;
+
+ let toolboxId = this.getAttribute("toolboxid");
+ if (toolboxId) {
+ let toolbox = document.getElementById(toolboxId);
+ if (!toolbox) {
+ let tbName = this.toolbarName;
+ if (tbName)
+ tbName = " (" + tbName + ")";
+ else
+ tbName = "";
+ throw new Error(`toolbar ID ${this.id}${tbName}: toolboxid attribute '${toolboxId}' points to a toolbox that doesn't exist`);
+ }
+
+ if (!toolbox.externalToolbars.includes(this))
+ toolbox.externalToolbars.push(this);
+
+ return this._toolbox = toolbox;
+ }
+
+ return this._toolbox = (this.parentNode &&
+ this.parentNode.localName == "toolbox") ?
+ this.parentNode : null;
+ ]]></getter>
+ </property>
+
+ <constructor>
+ <![CDATA[
+ if (document.readyState == "complete") {
+ this._init();
+ } else {
+ // Need to wait until XUL overlays are loaded. See bug 554279.
+ let self = this;
+ document.addEventListener("readystatechange", function listener(event) {
+ if (document.readyState != "complete")
+ return;
+ document.removeEventListener("readystatechange", listener);
+ self._init();
+ });
+ }
+ ]]>
+ </constructor>
+
+ <method name="_init">
+ <body>
+ <![CDATA[
+ // Searching for the toolbox palette in the toolbar binding because
+ // toolbars are constructed first.
+ var toolbox = this.toolbox;
+ if (!toolbox)
+ return;
+
+ if (!toolbox.palette) {
+ // Look to see if there is a toolbarpalette.
+ var node = toolbox.firstChild;
+ while (node) {
+ if (node.localName == "toolbarpalette")
+ break;
+ node = node.nextSibling;
+ }
+
+ if (!node)
+ return;
+
+ // Hold on to the palette but remove it from the document.
+ toolbox.palette = node;
+ toolbox.removeChild(node);
+ }
+
+ // Build up our contents from the palette.
+ var currentSet = this.getAttribute("currentset");
+ if (!currentSet)
+ currentSet = this.getAttribute("defaultset");
+ if (currentSet)
+ this.currentSet = currentSet;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_idFromNode">
+ <parameter name="aNode"/>
+ <body>
+ <![CDATA[
+ if (aNode.getAttribute("skipintoolbarset") == "true")
+ return "";
+
+ switch (aNode.localName) {
+ case "toolbarseparator":
+ return "separator";
+ case "toolbarspring":
+ return "spring";
+ case "toolbarspacer":
+ return "spacer";
+ default:
+ return aNode.id;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="currentSet">
+ <getter>
+ <![CDATA[
+ var node = this.firstChild;
+ var currentSet = [];
+ while (node) {
+ var id = this._idFromNode(node);
+ if (id) {
+ currentSet.push(id);
+ }
+ node = node.nextSibling;
+ }
+
+ return currentSet.join(",") || "__empty";
+ ]]>
+ </getter>
+
+ <setter>
+ <![CDATA[
+ if (val == this.currentSet)
+ return val;
+
+ var ids = (val == "__empty") ? [] : val.split(",");
+
+ var nodeidx = 0;
+ var paletteItems = { }, added = { };
+
+ var palette = this.toolbox ? this.toolbox.palette : null;
+
+ // build a cache of items in the toolbarpalette
+ var paletteChildren = palette ? palette.childNodes : [];
+ for (let c = 0; c < paletteChildren.length; c++) {
+ let curNode = paletteChildren[c];
+ paletteItems[curNode.id] = curNode;
+ }
+
+ var children = this.childNodes;
+
+ // iterate over the ids to use on the toolbar
+ for (let i = 0; i < ids.length; i++) {
+ let id = ids[i];
+ // iterate over the existing nodes on the toolbar. nodeidx is the
+ // spot where we want to insert items.
+ let found = false;
+ for (let c = nodeidx; c < children.length; c++) {
+ let curNode = children[c];
+ if (this._idFromNode(curNode) == id) {
+ // the node already exists. If c equals nodeidx, we haven't
+ // iterated yet, so the item is already in the right position.
+ // Otherwise, insert it here.
+ if (c != nodeidx) {
+ this.insertBefore(curNode, children[nodeidx]);
+ }
+
+ added[curNode.id] = true;
+ nodeidx++;
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ // move on to the next id
+ continue;
+ }
+
+ // the node isn't already on the toolbar, so add a new one.
+ var nodeToAdd = paletteItems[id] || this._getToolbarItem(id);
+ if (nodeToAdd && !(nodeToAdd.id in added)) {
+ added[nodeToAdd.id] = true;
+ this.insertBefore(nodeToAdd, children[nodeidx] || null);
+ nodeToAdd.setAttribute("removable", "true");
+ nodeidx++;
+ }
+ }
+
+ // remove any leftover removable nodes
+ for (let i = children.length - 1; i >= nodeidx; i--) {
+ let curNode = children[i];
+
+ let curNodeId = this._idFromNode(curNode);
+ // skip over fixed items
+ if (curNodeId && curNode.getAttribute("removable") == "true") {
+ if (palette)
+ palette.appendChild(curNode);
+ else
+ this.removeChild(curNode);
+ }
+ }
+
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <field name="_newElementCount">0</field>
+ <method name="_getToolbarItem">
+ <parameter name="aId"/>
+ <body>
+ <![CDATA[
+ const XUL_NS = "http://www.mozilla.org/keymaster/" +
+ "gatekeeper/there.is.only.xul";
+
+ var newItem = null;
+ switch (aId) {
+ // Handle special cases
+ case "separator":
+ case "spring":
+ case "spacer":
+ newItem = document.createElementNS(XUL_NS, "toolbar" + aId);
+ // Due to timers resolution Date.now() can be the same for
+ // elements created in small timeframes. So ids are
+ // differentiated through a unique count suffix.
+ newItem.id = aId + Date.now() + (++this._newElementCount);
+ if (aId == "spring")
+ newItem.flex = 1;
+ break;
+ default:
+ var toolbox = this.toolbox;
+ if (!toolbox)
+ break;
+
+ // look for an item with the same id, as the item may be
+ // in a different toolbar.
+ var item = document.getElementById(aId);
+ if (item && item.parentNode &&
+ item.parentNode.localName == "toolbar" &&
+ item.parentNode.toolbox == toolbox) {
+ newItem = item;
+ break;
+ }
+
+ if (toolbox.palette) {
+ // Attempt to locate an item with a matching ID within
+ // the palette.
+ let paletteItem = this.toolbox.palette.firstChild;
+ while (paletteItem) {
+ if (paletteItem.id == aId) {
+ newItem = paletteItem;
+ break;
+ }
+ paletteItem = paletteItem.nextSibling;
+ }
+ }
+ break;
+ }
+
+ return newItem;
+ ]]>
+ </body>
+ </method>
+
+ <method name="insertItem">
+ <parameter name="aId"/>
+ <parameter name="aBeforeElt"/>
+ <parameter name="aWrapper"/>
+ <body>
+ <![CDATA[
+ var newItem = this._getToolbarItem(aId);
+ if (!newItem)
+ return null;
+
+ var insertItem = newItem;
+ // make sure added items are removable
+ newItem.setAttribute("removable", "true");
+
+ // Wrap the item in another node if so inclined.
+ if (aWrapper) {
+ aWrapper.appendChild(newItem);
+ insertItem = aWrapper;
+ }
+
+ // Insert the palette item into the toolbar.
+ if (aBeforeElt)
+ this.insertBefore(insertItem, aBeforeElt);
+ else
+ this.appendChild(insertItem);
+
+ return newItem;
+ ]]>
+ </body>
+ </method>
+
+ <method name="hasCustomInteractiveItems">
+ <parameter name="aCurrentSet"/>
+ <body><![CDATA[
+ if (aCurrentSet == "__empty")
+ return false;
+
+ var defaultOrNoninteractive = (this.getAttribute("defaultset") || "")
+ .split(",")
+ .concat(["separator", "spacer", "spring"]);
+ return aCurrentSet.split(",").some(item => !defaultOrNoninteractive.includes(item));
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="toolbar-menubar-autohide"
+ extends="chrome://communicator/content/bindings/toolbar.xml#toolbar">
+ <implementation>
+ <constructor>
+ this._setInactive();
+ </constructor>
+ <destructor>
+ this._setActive();
+ </destructor>
+
+ <field name="_inactiveTimeout">null</field>
+
+ <field name="_contextMenuListener"><![CDATA[({
+ toolbar: this,
+ contextMenu: null,
+
+ get active() {
+ return !!this.contextMenu;
+ },
+
+ init(event) {
+ var node = event.target;
+ while (node != this.toolbar) {
+ if (node.localName == "menupopup")
+ return;
+ node = node.parentNode;
+ }
+
+ var contextMenuId = this.toolbar.getAttribute("context");
+ if (!contextMenuId)
+ return;
+
+ this.contextMenu = document.getElementById(contextMenuId);
+ if (!this.contextMenu)
+ return;
+
+ this.contextMenu.addEventListener("popupshown", this);
+ this.contextMenu.addEventListener("popuphiding", this);
+ this.toolbar.addEventListener("mousemove", this);
+ },
+ handleEvent(event) {
+ switch (event.type) {
+ case "popupshown":
+ this.toolbar.removeEventListener("mousemove", this);
+ break;
+ case "popuphiding":
+ case "mousemove":
+ this.toolbar._setInactiveAsync();
+ this.toolbar.removeEventListener("mousemove", this);
+ this.contextMenu.removeEventListener("popuphiding", this);
+ this.contextMenu.removeEventListener("popupshown", this);
+ this.contextMenu = null;
+ break;
+ }
+ },
+ })]]></field>
+
+ <method name="_setInactive">
+ <body><![CDATA[
+ this.setAttribute("inactive", "true");
+ ]]></body>
+ </method>
+
+ <method name="_setInactiveAsync">
+ <body><![CDATA[
+ this._inactiveTimeout = setTimeout(function(self) {
+ if (self.getAttribute("autohide") == "true") {
+ self._inactiveTimeout = null;
+ self._setInactive();
+ }
+ }, 0, this);
+ ]]></body>
+ </method>
+
+ <method name="_setActive">
+ <body><![CDATA[
+ if (this._inactiveTimeout) {
+ clearTimeout(this._inactiveTimeout);
+ this._inactiveTimeout = null;
+ }
+ this.removeAttribute("inactive");
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="DOMMenuBarActive" action="this._setActive();"/>
+ <handler event="popupshowing" action="this._setActive();"/>
+ <handler event="mousedown" button="2" action="this._contextMenuListener.init(event);"/>
+ <handler event="DOMMenuBarInactive"><![CDATA[
+ if (!this._contextMenuListener.active)
+ this._setInactiveAsync();
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="menubar" role="xul:menubar">
+ <implementation>
+ <field name="_active">false</field>
+ <field name="_statusbar">null</field>
+ <field name="_originalStatusText">null</field>
+ <property name="statusbar" onget="return this.getAttribute('statusbar');"
+ onset="this.setAttribute('statusbar', val); return val;"/>
+ <method name="_updateStatusText">
+ <parameter name="itemText"/>
+ <body>
+ <![CDATA[
+ if (!this._active)
+ return;
+ var newText = itemText ? itemText : this._originalStatusText;
+ if (newText != this._statusbar.label)
+ this._statusbar.label = newText;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ <handlers>
+ <handler event="DOMMenuBarActive">
+ <![CDATA[
+ if (!this.statusbar) return;
+ this._statusbar = document.getElementById(this.statusbar);
+ if (!this._statusbar)
+ return;
+ this._active = true;
+ this._originalStatusText = this._statusbar.label;
+ ]]>
+ </handler>
+ <handler event="DOMMenuBarInactive">
+ <![CDATA[
+ if (!this._active)
+ return;
+ this._active = false;
+ this._statusbar.label = this._originalStatusText;
+ ]]>
+ </handler>
+ <handler event="DOMMenuItemActive">this._updateStatusText(event.target.statusText);</handler>
+ <handler event="DOMMenuItemInactive">this._updateStatusText("");</handler>
+ </handlers>
+ </binding>
+
+ <binding id="toolbarpaletteitem">
+ <content>
+ <xul:hbox class="toolbarpaletteitem-box" flex="1" xbl:inherits="type,place">
+ <children/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="toolbarpaletteitem-palette"
+ extends="chrome://communicator/content/bindings/toolbar.xml#toolbarpaletteitem">
+ <content>
+ <xul:hbox class="toolbarpaletteitem-box" xbl:inherits="type,place">
+ <children/>
+ </xul:hbox>
+ <xul:label xbl:inherits="value=title"/>
+ </content>
+ </binding>
+
+ <binding id="toolbarpaletteitem-palette-wrapping-label"
+ extends="chrome://communicator/content/bindings/toolbar.xml#toolbarpaletteitem">
+ <content>
+ <xul:hbox class="toolbarpaletteitem-box" xbl:inherits="type,place">
+ <children/>
+ </xul:hbox>
+ <xul:label xbl:inherits="xbl:text=title"/>
+ </content>
+ </binding>
+
+ <binding id="menu-button"
+ extends="chrome://global/content/bindings/button.xml#button-base">
+
+ <content>
+ <children includes="observes|template|menupopup|panel|tooltip"/>
+ <xul:toolbarbutton class="box-inherit toolbarbutton-menubutton-button"
+ anonid="button" flex="1" allowevents="true"
+ xbl:inherits="disabled,crop,image,label,accesskey,command,wrap,badge,
+ align,dir,pack,orient,tooltiptext=buttontooltiptext"/>
+ <xul:dropmarker type="menu-button" class="toolbarbutton-menubutton-dropmarker"
+ anonid="dropmarker" xbl:inherits="align,dir,pack,orient,disabled,label,open,consumeanchor"/>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/build/Makefile.in b/comm/suite/components/build/Makefile.in
new file mode 100644
index 0000000000..2387227ab4
--- /dev/null
+++ b/comm/suite/components/build/Makefile.in
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include $(topsrcdir)/config/rules.mk
+
+# Ensure that we don't embed a manifest referencing the CRT.
+EMBED_MANIFEST_AT =
diff --git a/comm/suite/components/build/moz.build b/comm/suite/components/build/moz.build
new file mode 100644
index 0000000000..4778cb3404
--- /dev/null
+++ b/comm/suite/components/build/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ "nsSuiteCID.h",
+]
+
+SOURCES += [
+ "nsSuiteModule.cpp",
+]
+
+Library("suite")
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "../feeds",
+ "../migration/src",
+ "../profile",
+ "../shell",
+]
diff --git a/comm/suite/components/build/nsSuiteCID.h b/comm/suite/components/build/nsSuiteCID.h
new file mode 100644
index 0000000000..0ee2802151
--- /dev/null
+++ b/comm/suite/components/build/nsSuiteCID.h
@@ -0,0 +1,24 @@
+/* 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/. */
+
+// {e5eeef51-05ce-4885-9434-7287616d9547}
+#define NS_FEEDSNIFFER_CID \
+ { 0xe5eeef51, 0x5ce, 0x4885, { 0x94, 0x34, 0x72, 0x87, 0x61, 0x6d, 0x95, 0x47 } }
+
+#define NS_FEEDSNIFFER_CONTRACTID \
+ "@mozilla.org/browser/feeds/sniffer;1"
+
+// {39b688ec-e308-49e5-be6b-28dc7fcd6154}
+#define NS_SHELLSERVICE_CID \
+ { 0x39b688ec, 0xe308, 0x49e5, { 0xbe, 0x6b, 0x28, 0xdc, 0x7f, 0xcd, 0x61, 0x54 } }
+
+#define NS_SHELLSERVICE_CONTRACTID \
+ "@mozilla.org/suite/shell-service;1"
+
+// {9aa21826-9d1d-433d-8c10-f313b26fa9dd}
+#define NS_SUITEDIRECTORYPROVIDER_CID \
+ { 0x9aa21826, 0x9d1d, 0x433d, { 0x8c, 0x10, 0xf3, 0x13, 0xb2, 0x6f, 0xa9, 0xdd } }
+
+#define NS_SUITEDIRECTORYPROVIDER_CONTRACTID \
+ "@mozilla.org/suite/directory-provider;1"
diff --git a/comm/suite/components/build/nsSuiteModule.cpp b/comm/suite/components/build/nsSuiteModule.cpp
new file mode 100644
index 0000000000..c9d2b4503c
--- /dev/null
+++ b/comm/suite/components/build/nsSuiteModule.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; 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/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsSuiteDirectoryProvider.h"
+#include "nsThunderbirdProfileMigrator.h"
+#include "nsSuiteMigrationCID.h"
+#include "nsNetCID.h"
+#include "nsFeedSniffer.h"
+
+#if defined(XP_WIN)
+#include "nsWindowsShellService.h"
+#elif defined(XP_MACOSX)
+#include "nsMacShellService.h"
+#elif defined(MOZ_WIDGET_GTK)
+#include "nsGNOMEShellService.h"
+#endif
+
+using namespace mozilla;
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(XP_WIN)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowsShellService, Init)
+#elif defined(XP_MACOSX)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacShellService)
+#elif defined(MOZ_WIDGET_GTK)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init)
+#endif
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSuiteDirectoryProvider)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsThunderbirdProfileMigrator)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsFeedSniffer)
+
+#if defined(XP_WIN)
+NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
+#elif defined(XP_MACOSX)
+NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
+#elif defined(MOZ_WIDGET_GTK)
+NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
+#endif
+NS_DEFINE_NAMED_CID(NS_SUITEDIRECTORYPROVIDER_CID);
+NS_DEFINE_NAMED_CID(NS_THUNDERBIRDPROFILEMIGRATOR_CID);
+NS_DEFINE_NAMED_CID(NS_FEEDSNIFFER_CID);
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const mozilla::Module::CIDEntry kSuiteCIDs[] = {
+#if defined(XP_WIN)
+ { &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor },
+#elif defined(XP_MACOSX)
+ { &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor },
+#elif defined(MOZ_WIDGET_GTK)
+ { &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor },
+#endif
+ { &kNS_SUITEDIRECTORYPROVIDER_CID, false, NULL, nsSuiteDirectoryProviderConstructor },
+ { &kNS_THUNDERBIRDPROFILEMIGRATOR_CID, false, NULL, nsThunderbirdProfileMigratorConstructor },
+ { &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor },
+ { NULL }
+};
+
+static const mozilla::Module::ContractIDEntry kSuiteContracts[] = {
+#if defined(XP_WIN)
+ { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
+#elif defined(XP_MACOSX)
+ { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
+#elif defined(MOZ_WIDGET_GTK)
+ { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
+#endif
+ { NS_SUITEDIRECTORYPROVIDER_CONTRACTID, &kNS_SUITEDIRECTORYPROVIDER_CID },
+ { NS_SUITEPROFILEMIGRATOR_CONTRACTID_PREFIX "thunderbird", &kNS_THUNDERBIRDPROFILEMIGRATOR_CID },
+ { NS_FEEDSNIFFER_CONTRACTID, &kNS_FEEDSNIFFER_CID },
+ { NULL }
+};
+
+static const mozilla::Module::CategoryEntry kSuiteCategories[] = {
+ { XPCOM_DIRECTORY_PROVIDER_CATEGORY, "suite-directory-provider", NS_SUITEDIRECTORYPROVIDER_CONTRACTID },
+ { NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID },
+ { NULL }
+};
+
+extern const mozilla::Module kSuiteModule = {
+ mozilla::Module::kVersion,
+ kSuiteCIDs,
+ kSuiteContracts,
+ kSuiteCategories
+};
diff --git a/comm/suite/components/console/content/console.css b/comm/suite/components/console/content/console.css
new file mode 100644
index 0000000000..c3d0907c88
--- /dev/null
+++ b/comm/suite/components/console/content/console.css
@@ -0,0 +1,74 @@
+/* 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/. */
+
+.console-box {
+ -moz-binding: url("chrome://communicator/content/console/consoleBindings.xml#console-box");
+ overflow: auto;
+}
+
+.console-rows {
+ -moz-user-focus: normal;
+}
+
+.console-row[type="error"],
+.console-row[type="warning"],
+.console-row[type="message"][typetext] {
+ -moz-binding: url("chrome://communicator/content/console/consoleBindings.xml#error");
+}
+
+.console-row[type="message"] {
+ -moz-binding: url("chrome://communicator/content/console/consoleBindings.xml#message");
+}
+
+.console-msg-text,
+.console-error-msg {
+ white-space: pre-wrap;
+}
+
+.console-error-source {
+ -moz-binding: url("chrome://communicator/content/console/consoleBindings.xml#console-error-source");
+}
+
+.console-dots {
+ width: 1px;
+}
+
+/* :::::::::: hiding and showing of rows for each mode :::::::::: */
+
+.console-box[mode="Warnings"] > .console-box-internal > .console-rows
+ > .console-row[type="error"],
+.console-box[mode="Messages"] > .console-box-internal > .console-rows
+ > .console-row[type="error"]
+{
+ display: none;
+}
+
+.console-box[mode="Errors"] > .console-box-internal > .console-rows
+ > .console-row[type="warning"],
+.console-box[mode="Messages"] > .console-box-internal > .console-rows
+ > .console-row[type="warning"]
+{
+ display: none;
+}
+
+.console-box[mode="Errors"] > .console-box-internal > .console-rows
+ > .console-row[type="message"],
+.console-box[mode="Warnings"] > .console-box-internal > .console-rows
+ > .console-row[type="message"]
+{
+ display: none;
+}
+
+.filtered-by-string {
+ display: none;
+}
+
+/* If line number is 0, hide the line number section */
+.lineNumberRow[line="0"] {
+ display: none;
+}
+
+#TextboxEval {
+ direction: ltr;
+}
diff --git a/comm/suite/components/console/content/console.js b/comm/suite/components/console/content/console.js
new file mode 100644
index 0000000000..53e3c9f6dd
--- /dev/null
+++ b/comm/suite/components/console/content/console.js
@@ -0,0 +1,111 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gConsole, gConsoleBundle, gTextBoxEval, gEvaluator, gCodeToEvaluate;
+var gFilter;
+
+/* :::::::: Console Initialization ::::::::::::::: */
+
+window.onload = function()
+{
+ gConsole = document.getElementById("ConsoleBox");
+ gConsoleBundle = document.getElementById("ConsoleBundle");
+ gTextBoxEval = document.getElementById("TextboxEval");
+ gEvaluator = document.getElementById("Evaluator");
+ gFilter = document.getElementById("Filter");
+
+ updateSortCommand(gConsole.sortOrder);
+ updateModeCommand(gConsole.mode);
+
+ gEvaluator.addEventListener("load", loadOrDisplayResult, true);
+}
+
+/* :::::::: Console UI Functions ::::::::::::::: */
+
+function changeFilter()
+{
+ gConsole.filter = gFilter.value;
+
+ document.persist("ConsoleBox", "filter");
+}
+
+function changeMode(aMode)
+{
+ switch (aMode) {
+ case "Errors":
+ case "Warnings":
+ case "Messages":
+ gConsole.mode = aMode;
+ break;
+ case "All":
+ gConsole.mode = null;
+ }
+
+ document.persist("ConsoleBox", "mode");
+}
+
+function clearConsole()
+{
+ gConsole.clear();
+}
+
+function changeSortOrder(aOrder)
+{
+ updateSortCommand(gConsole.sortOrder = aOrder);
+}
+
+function updateSortCommand(aOrder)
+{
+ var orderString = aOrder == 'reverse' ? "Descend" : "Ascend";
+ var bc = document.getElementById("Console:sort"+orderString);
+ bc.setAttribute("checked", true);
+
+ orderString = aOrder == 'reverse' ? "Ascend" : "Descend";
+ bc = document.getElementById("Console:sort"+orderString);
+ bc.setAttribute("checked", false);
+}
+
+function updateModeCommand(aMode)
+{
+ /* aMode can end up invalid if it set by an extension that replaces */
+ /* mode and then it is uninstalled or disabled */
+ var bc = document.getElementById("Console:mode" + aMode) ||
+ document.getElementById("Console:modeAll");
+ bc.setAttribute("checked", true);
+}
+
+function onEvalKeyPress(aEvent)
+{
+ if (aEvent.keyCode == 13)
+ evaluateTypein();
+}
+
+function evaluateTypein()
+{
+ gCodeToEvaluate = gTextBoxEval.value;
+ // reset the iframe first; the code will be evaluated in loadOrDisplayResult
+ // below, once about:blank has completed loading (see bug 385092)
+ gEvaluator.contentWindow.location = "about:blank";
+}
+
+function loadOrDisplayResult()
+{
+ if (gCodeToEvaluate) {
+ gEvaluator.contentWindow.location = "javascript: " +
+ gCodeToEvaluate.replace(/%/g, "%25");
+ gCodeToEvaluate = "";
+ return;
+ }
+
+ var resultRange = gEvaluator.contentDocument.createRange();
+ resultRange.selectNode(gEvaluator.contentDocument.documentElement);
+ var result = resultRange.toString();
+ if (result)
+ Services.console.logStringMessage(result);
+ // or could use appendMessage which doesn't persist
+}
diff --git a/comm/suite/components/console/content/console.xul b/comm/suite/components/console/content/console.xul
new file mode 100644
index 0000000000..d5dd7ae0cb
--- /dev/null
+++ b/comm/suite/components/console/content/console.xul
@@ -0,0 +1,208 @@
+<?xml version="1.0"?> <!-- -*- tab-width: 4; indent-tabs-mode: nil -*- -->
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/console/console.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/console/console.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+
+<!DOCTYPE window SYSTEM "chrome://communicator/locale/console/console.dtd" >
+
+<window id="JSConsoleWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&errorConsole.title;"
+ windowtype="suite:console"
+ width="640"
+ height="480"
+ screenX="10"
+ screenY="10"
+ persist="screenX screenY width height sizemode"
+ onclose="return closeWindow(false);">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/console/console.js"/>
+ <script src="chrome://global/content/viewSourceUtils.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+
+ <stringbundle id="ConsoleBundle" src="chrome://communicator/locale/console/console.properties"/>
+
+ <commandset id="consoleCommands">
+ <commandset id="tasksCommands"/>
+ <command id="cmd_close" oncommand="closeWindow(true);"/>
+ </commandset>
+
+ <keyset id="consoleKeys">
+ <keyset id="tasksKeys"/>
+ <key id="key_close"
+ key="&closeCmd.commandkey;"
+ modifiers="accel"
+ command="cmd_close"/>
+ <key id="key_close2"
+ disabled="true"
+ keycode="VK_ESCAPE"
+ command="cmd_close"/>
+ <key id="key_focus1"
+ key="&focus1.commandkey;"
+ modifiers="accel"
+ oncommand="gTextBoxEval.focus();"/>
+ <key id="key_focus2"
+ key="&focus2.commandkey;"
+ modifiers="alt"
+ oncommand="gTextBoxEval.focus();"/>
+ <key id="key_copy"/>
+ </keyset>
+
+ <popupset id="ContextMenus">
+ <menupopup id="ConsoleContext">
+ <menuitem type="radio"
+ id="Console:sortAscend"
+ label="&sortFirst.label;"
+ accesskey="&sortFirst.accesskey;"
+ oncommand="changeSortOrder('forward');"/>
+ <menuitem type="radio"
+ id="Console:sortDescend"
+ label="&sortLast.label;"
+ accesskey="&sortLast.accesskey;"
+ oncommand="changeSortOrder('reverse');"/>
+ <menuseparator/>
+ <menuitem id="menu_copy_cm"
+ label="&copyCmd.label;"
+ accesskey="&copyCmd.accesskey;"
+ command="cmd_copy"/>
+ </menupopup>
+ </popupset>
+
+ <toolbox id="console-toolbox">
+ <menubar id="main-menubar"
+ class="chromeclass-menubar"
+ grippytooltiptext="&menuBar.tooltip;">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup">
+ <menuitem id="menu_close"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_Edit">
+ <menupopup>
+ <menuitem id="menu_copy"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_View">
+ <menupopup>
+ <menu label="&toolbarsCmd.label;"
+ accesskey="&toolbarsCmd.accesskey;">
+ <menupopup>
+ <menuitem id="toggleToolbarMode"
+ type="checkbox"
+ checked="true"
+ label="&toolbarMode.label;"
+ accesskey="&toolbarMode.accesskey;"
+ oncommand="goToggleToolbar('ToolbarMode','toggleToolbarMode');"/>
+ <menuitem id="toggleToolbarEval"
+ type="checkbox"
+ checked="true"
+ label="&toolbarEval.label;"
+ accesskey="&toolbarEval.accesskey;"
+ oncommand="goToggleToolbar('ToolbarEval','toggleToolbarEval');"/>
+ </menupopup>
+ </menu>
+ <menuseparator/>
+ <menuitem type="radio" observes="Console:sortAscend"/>
+ <menuitem type="radio" observes="Console:sortDescend"/>
+ </menupopup>
+ </menu>
+
+ <!-- tasks menu filled from tasksOverlay -->
+ <menu id="tasksMenu"/>
+
+ <!-- window menu filled from tasksOverlay -->
+ <menu id="windowMenu"/>
+
+ <!-- help menu filled from globalOverlay -->
+ <menu id="menu_Help"/>
+ </menubar>
+
+ <toolbar class="chromeclass-toolbar"
+ id="ToolbarMode"
+ grippytooltiptext="&modeToolbar.tooltip;">
+ <hbox id="viewGroup">
+ <toolbarbutton type="radio"
+ group="mode"
+ id="Console:modeAll"
+ label="&all.label;"
+ accesskey="&all.accesskey;"
+ oncommand="changeMode('All');"/>
+ <toolbarbutton type="radio"
+ group="mode"
+ id="Console:modeErrors"
+ label="&errors.label;"
+ accesskey="&errors.accesskey;"
+ oncommand="changeMode('Errors');"/>
+ <toolbarbutton type="radio"
+ group="mode"
+ id="Console:modeWarnings"
+ label="&warnings.label;"
+ accesskey="&warnings.accesskey;"
+ oncommand="changeMode('Warnings');"/>
+ <toolbarbutton type="radio"
+ group="mode"
+ id="Console:modeMessages"
+ label="&messages.label;"
+ accesskey="&messages.accesskey;"
+ oncommand="changeMode('Messages');"/>
+ </hbox>
+ <toolbarseparator/>
+ <toolbarbutton id="Console:clear"
+ label="&clear.label;"
+ accesskey="&clear.accesskey;"
+ oncommand="clearConsole();"/>
+ </toolbar>
+
+ <toolbar class="chromeclass-toolbar"
+ id="ToolbarEval"
+ align="center"
+ nowindowdrag="true"
+ grippytooltiptext="&entryToolbar.tooltip;">
+ <label value="&codeEval.label;"
+ accesskey="&codeEval.accesskey;"
+ control="TextboxEval"/>
+ <textbox id="TextboxEval"
+ class="toolbar"
+ flex="1"
+ value=""
+ onkeypress="onEvalKeyPress(event);"/>
+ <toolbarbutton id="ButtonEval"
+ label="&evaluate.label;"
+ accesskey="&evaluate.accesskey;"
+ oncommand="evaluateTypein();"/>
+ </toolbar>
+
+ </toolbox>
+
+ <vbox id="ConsoleBox"
+ class="console-box"
+ flex="1"
+ context="ConsoleContext"
+ persist="sortOrder"/>
+
+ <iframe name="Evaluator"
+ id="Evaluator"
+ collapsed="true"/>
+
+ <statusbar>
+ <statusbarpanel flex="1" pack="start">
+ <label value="&filter2.label;" control="Filter"/>
+ <textbox type="search"
+ id="Filter"
+ accesskey="&filter2.accesskey;"
+ oncommand="changeFilter();"/>
+ </statusbarpanel>
+ </statusbar>
+
+</window>
diff --git a/comm/suite/components/console/content/consoleBindings.xml b/comm/suite/components/console/content/consoleBindings.xml
new file mode 100644
index 0000000000..7b87a9f4eb
--- /dev/null
+++ b/comm/suite/components/console/content/consoleBindings.xml
@@ -0,0 +1,543 @@
+<?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 bindings SYSTEM "chrome://communicator/locale/console/console.dtd">
+
+<bindings id="consoleBindings"
+ 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="console-box" extends="xul:box">
+ <content>
+ <xul:stringbundle src="chrome://communicator/locale/console/console.properties" role="string-bundle"/>
+ <xul:vbox class="console-box-internal">
+ <xul:vbox class="console-rows" role="console-rows" xbl:inherits="dir=sortOrder"/>
+ </xul:vbox>
+ </content>
+
+ <implementation>
+ <field name="limit" readonly="true">
+ 250
+ </field>
+
+ <field name="fieldMaxLength" readonly="true">
+ <!-- Limit displayed string lengths to avoid performance issues. (Bug 796179 and 831020) -->
+ 200
+ </field>
+
+ <field name="showChromeErrors" readonly="true">
+ Services.prefs.getBoolPref("javascript.options.showInConsole");
+ </field>
+
+ <property name="count" readonly="true">
+ <getter>return this.mCount</getter>
+ </property>
+
+ <property name="mode">
+ <getter>return this.mMode;</getter>
+ <setter><![CDATA[
+ if (this.mode != val) {
+ this.mMode = val || "All";
+ this.setAttribute("mode", this.mMode);
+ this.selectedItem = null;
+ }
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="filter">
+ <getter>return this.mFilter;</getter>
+ <setter><![CDATA[
+ val = val.toLowerCase();
+ if (this.mFilter != val) {
+ this.mFilter = val;
+ for (let aRow of this.mConsoleRowBox.children) {
+ this.filterElement(aRow);
+ }
+ }
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="sortOrder">
+ <getter>return this.getAttribute("sortOrder");</getter>
+ <setter>this.setAttribute("sortOrder", val); return val;</setter>
+ </property>
+ <field name="mSelectedItem">null</field>
+ <property name="selectedItem">
+ <getter>return this.mSelectedItem</getter>
+ <setter><![CDATA[
+ if (this.mSelectedItem)
+ this.mSelectedItem.removeAttribute("selected");
+
+ this.mSelectedItem = val;
+ if (val)
+ val.setAttribute("selected", "true");
+
+ // Update edit commands
+ window.updateCommands("focus");
+ return val;
+ ]]></setter>
+ </property>
+
+ <method name="init">
+ <body><![CDATA[
+ this.mCount = 0;
+
+ this.mConsoleListener = {
+ console: this,
+ observe : function(aObject) {
+ // The message can arrive a little bit after the xbl binding has been
+ // unbind. So node.appendItem will not be available anymore.
+ if ('appendItem' in this.console)
+ this.console.appendItem(aObject);
+ }
+ };
+
+ this.mConsoleRowBox = document.getAnonymousElementByAttribute(this, "role", "console-rows");
+ this.mStrBundle = document.getAnonymousElementByAttribute(this, "role", "string-bundle");
+
+ try {
+ Services.console.registerListener(this.mConsoleListener);
+ } catch (ex) {
+ appendItem(
+ "Unable to display errors - couldn't get Console Service component. " +
+ "(Missing @mozilla.org/consoleservice;1)");
+ return;
+ }
+
+ this.mMode = this.getAttribute("mode") || "All";
+ this.mFilter = "";
+
+ this.appendInitialItems();
+ window.controllers.insertControllerAt(0, this._controller);
+ ]]></body>
+ </method>
+
+ <method name="destroy">
+ <body><![CDATA[
+ Services.console.unregisterListener(this.mConsoleListener);
+ window.controllers.removeController(this._controller);
+ ]]></body>
+ </method>
+
+ <method name="appendInitialItems">
+ <body><![CDATA[
+ var messages = Services.console.getMessageArray();
+
+ // In case getMessageArray returns 0-length array as null
+ if (!messages)
+ messages = [];
+
+ var limit = messages.length - this.limit;
+ if (limit < 0) limit = 0;
+
+ // Checks if console ever been cleared
+ for (var i = messages.length - 1; i >= limit; --i)
+ if (!messages[i].message)
+ break;
+
+ // Populate with messages after latest "clear"
+ while (++i < messages.length)
+ this.appendItem(messages[i]);
+ ]]></body>
+ </method>
+
+ <method name="appendItem">
+ <parameter name="aObject"/>
+ <body><![CDATA[
+ try {
+ // Try to QI it to a script error to get more info
+ var scriptError = aObject.QueryInterface(Ci.nsIScriptError);
+
+ // filter chrome urls
+ if (!this.showChromeErrors && scriptError.sourceName.substr(0, 9) == "chrome://")
+ return;
+
+ // filter private windows
+ if (scriptError.isFromPrivateWindow)
+ return;
+
+ this.appendError(scriptError);
+ } catch (ex) {
+ try {
+ // Try to QI it to a console message
+ var msg = aObject.QueryInterface(Ci.nsIConsoleMessage);
+ if (msg.message)
+ this.appendMessage(msg.message);
+ else // observed a null/"clear" message
+ this.clearConsole();
+ } catch (ex2) {
+ // Give up and append the object itself as a string
+ this.appendMessage(aObject);
+ }
+ }
+ ]]></body>
+ </method>
+
+ <method name="_truncateIfNecessary">
+ <parameter name="aString"/>
+ <parameter name="aMiddleCharacter"/>
+ <body><![CDATA[
+ if (!aString || aString.length <= this.fieldMaxLength)
+ return {string: aString, column: aMiddleCharacter};
+ let halfLimit = this.fieldMaxLength / 2;
+ if (!aMiddleCharacter || aMiddleCharacter < 0 || aMiddleCharacter > aString.length)
+ aMiddleCharacter = halfLimit;
+
+ let startPosition = 0;
+ let endPosition = aString.length;
+ if (aMiddleCharacter - halfLimit >= 0)
+ startPosition = aMiddleCharacter - halfLimit;
+ if (aMiddleCharacter + halfLimit <= aString.length)
+ endPosition = aMiddleCharacter + halfLimit;
+ if (endPosition - startPosition < this.fieldMaxLength)
+ endPosition += this.fieldMaxLength - (endPosition - startPosition);
+ let truncatedString = aString.substring(startPosition, endPosition);
+ let ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
+ Ci.nsIPrefLocalizedString).data;
+ if (startPosition > 0) {
+ truncatedString = ellipsis + truncatedString;
+ aMiddleCharacter += ellipsis.length;
+ }
+ if (endPosition < aString.length)
+ truncatedString = truncatedString + ellipsis;
+
+ return {
+ string: truncatedString,
+ column: aMiddleCharacter - startPosition
+ };
+ ]]></body>
+ </method>
+
+ <method name="appendError">
+ <parameter name="aObject"/>
+ <body><![CDATA[
+ var row = this.createConsoleRow();
+ var nsIScriptError = Ci.nsIScriptError;
+
+ // nsIConsoleMessage constants: debug, info, warn, error
+ var typetext = ["typeMessage", "typeMessage", "typeWarning", "typeError"][aObject.logLevel];
+ var type = ["message", "message", "warning", "error"][aObject.logLevel];
+
+ row.setAttribute("typetext", this.mStrBundle.getString(typetext));
+ row.setAttribute("type", type);
+ row.setAttribute("msg", aObject.errorMessage);
+ row.setAttribute("category", aObject.category);
+ row.setAttribute("time", this.properFormatTime(aObject.timeStamp));
+ if (aObject.lineNumber || aObject.sourceName) {
+ row.setAttribute("href", this._truncateIfNecessary(aObject.sourceName).string);
+ row.mSourceName = aObject.sourceName;
+ row.setAttribute("line", aObject.lineNumber);
+ } else {
+ row.setAttribute("hideSource", "true");
+ }
+ if (aObject.sourceLine) {
+ let sourceLine = aObject.sourceLine.replace(/\s/g, " ");
+ let truncatedLineObj = this._truncateIfNecessary(sourceLine, aObject.columnNumber);
+ row.setAttribute("code", truncatedLineObj.string);
+ row.mSourceLine = sourceLine;
+ if (aObject.columnNumber) {
+ row.setAttribute("col", aObject.columnNumber);
+ row.setAttribute("errorDots", this.repeatChar(" ", truncatedLineObj.column));
+ row.setAttribute("errorCaret", " ");
+ } else {
+ row.setAttribute("hideCaret", "true");
+ }
+ } else {
+ row.setAttribute("hideCode", "true");
+ }
+
+ this.appendConsoleRow(row);
+ ]]></body>
+ </method>
+
+ <method name="appendMessage">
+ <parameter name="aMessage"/>
+ <parameter name="aType"/>
+ <body><![CDATA[
+ var row = this.createConsoleRow();
+ row.setAttribute("type", aType || "message");
+ row.setAttribute("msg", aMessage);
+ this.appendConsoleRow(row);
+ ]]></body>
+ </method>
+
+ <method name="clear">
+ <body><![CDATA[
+ // add a "clear" message (mainly for other listeners)
+ Services.console.logStringMessage(null);
+ Services.console.reset();
+ ]]></body>
+ </method>
+
+ <method name="properFormatTime">
+ <parameter name="aTime"/>
+ <body><![CDATA[
+ const dateServ = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "short", timeStyle: "long"
+ });
+ return dateServ.format(aTime);
+ ]]></body>
+ </method>
+
+ <method name="copySelectedItem">
+ <body><![CDATA[
+ if (this.mSelectedItem) try {
+ const clipURI = "@mozilla.org/widget/clipboardhelper;1";
+ const clipI = Ci.nsIClipboardHelper;
+ var clipboard = Cc[clipURI].getService(clipI);
+
+ clipboard.copyString(this.mSelectedItem.toString());
+ } catch (ex) {
+ // Unable to copy anything, die quietly
+ }
+ ]]></body>
+ </method>
+
+ <method name="createConsoleRow">
+ <body><![CDATA[
+ var row = document.createElement("box");
+ row.setAttribute("class", "console-row");
+ row._IsConsoleRow = true;
+ row._ConsoleBox = this;
+ return row;
+ ]]></body>
+ </method>
+
+ <method name="appendConsoleRow">
+ <parameter name="aRow"/>
+ <body><![CDATA[
+ this.filterElement(aRow);
+ this.mConsoleRowBox.appendChild(aRow);
+ if (++this.mCount > this.limit) this.deleteFirst();
+ ]]></body>
+ </method>
+
+ <method name="deleteFirst">
+ <body><![CDATA[
+ var node = this.mConsoleRowBox.firstChild;
+ this.mConsoleRowBox.removeChild(node);
+ --this.mCount;
+ ]]></body>
+ </method>
+
+ <method name="clearConsole">
+ <body><![CDATA[
+ if (this.mCount == 0) // already clear
+ return;
+ this.mCount = 0;
+
+ var newRows = this.mConsoleRowBox.cloneNode(false);
+ this.mConsoleRowBox.parentNode.replaceChild(newRows, this.mConsoleRowBox);
+ this.mConsoleRowBox = newRows;
+ this.selectedItem = null;
+ ]]></body>
+ </method>
+
+ <method name="filterElement">
+ <parameter name="aRow" />
+ <body><![CDATA[
+ let anyMatch = ["msg", "line", "code"].some(function (key) {
+ return (aRow.hasAttribute(key) &&
+ this.stringMatchesFilters(aRow.getAttribute(key), this.mFilter));
+ }, this) || (aRow.mSourceName &&
+ this.stringMatchesFilters(aRow.mSourceName, this.mFilter));
+
+ if (anyMatch) {
+ aRow.classList.remove("filtered-by-string")
+ } else {
+ aRow.classList.add("filtered-by-string")
+ }
+ ]]></body>
+ </method>
+
+ <!-- UTILITY FUNCTIONS -->
+
+ <method name="repeatChar">
+ <parameter name="aChar"/>
+ <parameter name="aCol"/>
+ <body><![CDATA[
+ if (--aCol <= 0)
+ return "";
+
+ for (var i = 2; i < aCol; i += i)
+ aChar += aChar;
+
+ return aChar + aChar.slice(0, aCol - aChar.length);
+ ]]></body>
+ </method>
+
+ <method name="stringMatchesFilters">
+ <parameter name="aString"/>
+ <parameter name="aFilter"/>
+ <body><![CDATA[
+ if (!aString || !aFilter) {
+ return true;
+ }
+
+ let searchStr = aString.toLowerCase();
+ let filterStrings = aFilter.split(/\s+/);
+ return !filterStrings.some(function (f) {
+ return !searchStr.includes(f);
+ });
+ ]]></body>
+ </method>
+
+ <constructor>this.init();</constructor>
+ <destructor>this.destroy();</destructor>
+
+ <!-- Command controller for the copy command -->
+ <field name="_controller"><![CDATA[({
+ _outer: this,
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIController) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ supportsCommand: function(aCommand) {
+ return aCommand == "cmd_copy";
+ },
+
+ isCommandEnabled: function(aCommand) {
+ return aCommand == "cmd_copy" && this._outer.selectedItem;
+ },
+
+ doCommand: function(aCommand) {
+ if (aCommand == "cmd_copy")
+ this._outer.copySelectedItem();
+ },
+
+ onEvent: function() { }
+ });]]></field>
+ </implementation>
+
+ <handlers>
+ <handler event="mousedown"><![CDATA[
+ if (event.button == 0 || event.button == 2) {
+ var target = event.originalTarget;
+
+ while (target && !("_IsConsoleRow" in target))
+ target = target.parentNode;
+
+ if (target)
+ this.selectedItem = target;
+ }
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="error" extends="xul:box">
+ <content>
+ <xul:box class="console-row-internal-box" flex="1">
+ <xul:box class="console-row-icon" align="center" xbl:inherits="selected">
+ <xul:image class="console-icon" xbl:inherits="src,type"/>
+ </xul:box>
+ <xul:vbox class="console-row-content" xbl:inherits="selected" flex="1">
+ <xul:box class="console-row-msg" align="start">
+ <xul:label class="label" xbl:inherits="value=typetext"/>
+ <xul:description class="console-error-msg" xbl:inherits="xbl:text=msg" flex="1"/>
+ <xul:label class="label console-time" xbl:inherits="value=time"/>
+ </xul:box>
+ <xul:box class="console-row-file" xbl:inherits="hidden=hideSource">
+ <xul:label class="label" value="&errFile.label;"/>
+ <xul:box class="console-error-source" xbl:inherits="href,line"/>
+ <xul:spacer flex="1"/>
+ <xul:hbox class="lineNumberRow" xbl:inherits="line">
+ <xul:label class="label" value="&errLine.label;"/>
+ <xul:label class="label" xbl:inherits="value=line"/>
+ </xul:hbox>
+ </xul:box>
+ <xul:vbox class="console-row-code" xbl:inherits="selected,hidden=hideCode">
+ <xul:label class="monospace console-code" xbl:inherits="value=code" crop="end"/>
+ <xul:box xbl:inherits="hidden=hideCaret">
+ <xul:label class="monospace console-dots" xbl:inherits="value=errorDots"/>
+ <xul:label class="monospace console-caret" xbl:inherits="value=errorCaret"/>
+ <xul:spacer flex="1"/>
+ </xul:box>
+ </xul:vbox>
+ </xul:vbox>
+ </xul:box>
+ </content>
+
+ <implementation>
+ <field name="mSourceName">null</field>
+ <field name="mSourceLine">null</field>
+
+ <method name="toString">
+ <body><![CDATA[
+ let msg = "";
+ let strBundle = this._ConsoleBox.mStrBundle;
+
+ if (this.hasAttribute("time"))
+ msg += strBundle.getFormattedString("errTime", [this.getAttribute("time")]) + "\n";
+
+ msg += this.getAttribute("typetext") + " " + this.getAttribute("msg");
+
+ if (this.hasAttribute("line") && this.mSourceName) {
+ msg += "\n" + strBundle.getFormattedString("errFile",
+ [this.mSourceName]) + "\n";
+ if (this.hasAttribute("col")) {
+ msg += strBundle.getFormattedString("errLineCol",
+ [this.getAttribute("line"), this.getAttribute("col")]);
+ } else
+ msg += strBundle.getFormattedString("errLine", [this.getAttribute("line")]);
+ }
+
+ if (this.hasAttribute("code"))
+ msg += "\n" + strBundle.getString("errCode") + "\n" + this.mSourceLine;
+
+ return msg;
+ ]]></body>
+ </method>
+ </implementation>
+
+ </binding>
+
+ <binding id="message" extends="xul:box">
+ <content>
+ <xul:box class="console-internal-box" flex="1">
+ <xul:box class="console-row-icon" align="center">
+ <xul:image class="console-icon" xbl:inherits="src,type"/>
+ </xul:box>
+ <xul:vbox class="console-row-content" xbl:inherits="selected" flex="1">
+ <xul:vbox class="console-row-msg" flex="1">
+ <xul:description class="console-msg-text" xbl:inherits="xbl:text=msg"/>
+ </xul:vbox>
+ </xul:vbox>
+ </xul:box>
+ </content>
+
+ <implementation>
+ <method name="toString">
+ <body><![CDATA[
+ return this.getAttribute("msg");
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="console-error-source" extends="xul:box">
+ <content>
+ <xul:label class="text-link" xbl:inherits="value=href" crop="right"/>
+ </content>
+
+ <handlers>
+ <handler event="click" phase="capturing" button="0" preventdefault="true">
+ <![CDATA[
+ var url = document.getBindingParent(this).mSourceName;
+ url = url.substring(url.lastIndexOf(" ") + 1);
+ var line = getAttribute("line");
+ gViewSourceUtils.viewSource({URL: url, lineNumber: line});
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/console/jar.mn b/comm/suite/components/console/jar.mn
new file mode 100644
index 0000000000..3c54ac207c
--- /dev/null
+++ b/comm/suite/components/console/jar.mn
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/console/consoleBindings.xml (content/consoleBindings.xml)
+ content/communicator/console/console.css (content/console.css)
+ content/communicator/console/console.js (content/console.js)
+ content/communicator/console/console.xul (content/console.xul)
diff --git a/comm/suite/components/console/jsconsole-clhandler.js b/comm/suite/components/console/jsconsole-clhandler.js
new file mode 100644
index 0000000000..cf4612a296
--- /dev/null
+++ b/comm/suite/components/console/jsconsole-clhandler.js
@@ -0,0 +1,34 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function jsConsoleHandler() {}
+jsConsoleHandler.prototype = {
+ handle: function clh_handle(cmdLine) {
+ if (!cmdLine.handleFlag("suiteconsole", false))
+ return;
+
+ var console = Services.wm.getMostRecentWindow("suite:console");
+ if (!console) {
+ Services.ww.openWindow(null,
+ "chrome://communicator/content/console/console.xul",
+ "_blank", "chrome,dialog=no,all", cmdLine);
+ } else {
+ console.focus(); // the Error console was already open
+ }
+
+ if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO)
+ cmdLine.preventDefault = true;
+ },
+
+ helpInfo : " --suiteconsole Open the Error console.\n",
+
+ classID: Components.ID("{afeee354-8c99-4725-adb1-8502218c5c3c}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([jsConsoleHandler]);
diff --git a/comm/suite/components/console/jsconsole-clhandler.manifest b/comm/suite/components/console/jsconsole-clhandler.manifest
new file mode 100644
index 0000000000..af2cfb5f74
--- /dev/null
+++ b/comm/suite/components/console/jsconsole-clhandler.manifest
@@ -0,0 +1,3 @@
+component {afeee354-8c99-4725-adb1-8502218c5c3c} jsconsole-clhandler.js
+contract @mozilla.org/suite/console-clh;1 {afeee354-8c99-4725-adb1-8502218c5c3c}
+category command-line-handler t-jsconsole @mozilla.org/suite/console-clh;1
diff --git a/comm/suite/components/console/moz.build b/comm/suite/components/console/moz.build
new file mode 100644
index 0000000000..0fed37d675
--- /dev/null
+++ b/comm/suite/components/console/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXTRA_COMPONENTS += [
+ "jsconsole-clhandler.js",
+ "jsconsole-clhandler.manifest",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/customizeToolbar.css b/comm/suite/components/customizeToolbar.css
new file mode 100644
index 0000000000..f1242dc921
--- /dev/null
+++ b/comm/suite/components/customizeToolbar.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+#CustomizeToolbarWindow:-moz-lwtheme[lwtheme-image] {
+ background-image: none !important;
+ text-shadow: none;
+}
+
+#main-box {
+ padding: 8px;
+}
+
+#instructions {
+ font-weight: 600;
+ font-size: 1.2em;
+ margin-block: 5px 10px;
+}
+
+#palette-box {
+ overflow: auto;
+ display: block;
+ min-height: 3em;
+ background-color: hsla(0, 0%, 100%, .3);
+ border: 1px solid hsla(0, 0%, 50%, .4);
+}
+
+:root[lwt-tree] #palette-box {
+ scrollbar-color: rgba(204, 204, 204, .5) rgba(230, 230, 235, .5);
+}
+
+:root[lwt-tree-brighttext] #palette-box {
+ scrollbar-color: rgba(249, 249, 250, .4) rgba(20, 20, 25, .3);
+}
+
+#palette-box > toolbarpaletteitem {
+ padding: 8px 2px;
+ margin: 0 8px;
+}
+
+toolbarpaletteitem {
+ -moz-window-dragging: no-drag;
+ -moz-box-pack: start;
+}
+
+toolbarpaletteitem[place="palette"] {
+ -moz-box-orient: vertical;
+ width: 10em;
+ max-width: 10em;
+ /* icon (16) + margin (9 + 12) + 4 lines of text: */
+ height: calc(39px + 4em);
+ margin-bottom: 5px;
+ margin-inline-end: 24px;
+ overflow: visible;
+ display: inline-block;
+ vertical-align: top;
+}
+
+toolbarpaletteitem[place=palette]::after {
+ content: attr(title);
+ display: block;
+ text-align: center;
+}
+
+toolbarpaletteitem > toolbarbutton,
+toolbarpaletteitem > toolbarseparator,
+toolbarpaletteitem > toolbaritem {
+ /* Prevent children from getting events */
+ pointer-events: none;
+ -moz-box-pack: center;
+ -moz-box-flex: 1;
+}
+
+toolbarpaletteitem[type="separator"][place="palette"] {
+ -moz-box-align: center;
+}
+
+toolbarpaletteitem[type="separator"][place="palette"] toolbarseparator {
+ background-color: currentColor;
+}
+
+#main-box > box {
+ overflow: hidden;
+}
+
+/* Hide the toolbarbutton label because we replicate it on the wrapper */
+.toolbarbutton-text {
+ display: none;
+}
+
+toolbarbutton > .toolbarbutton-menubutton-dropmarker {
+ display: none;
+}
+
+#buttonBox {
+ margin-block: 5px;
+}
+
+#titlebarSettings > checkbox {
+ margin-inline: 0 15px;
+}
+
+#modelistLabel {
+ margin-top: 2px;
+}
diff --git a/comm/suite/components/customizeToolbar.js b/comm/suite/components/customizeToolbar.js
new file mode 100644
index 0000000000..0cdd45ba3d
--- /dev/null
+++ b/comm/suite/components/customizeToolbar.js
@@ -0,0 +1,855 @@
+/* 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 gToolboxDocument = null;
+var gToolbox = null;
+var gCurrentDragOverItem = null;
+var gToolboxChanged = false;
+var gToolboxSheet = false;
+var gPaletteBox = null;
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+function onLoad() {
+ if ("arguments" in window && window.arguments[0]) {
+ InitWithToolbox(window.arguments[0]);
+ repositionDialog(window);
+ } else if (window.frameElement && "toolbox" in window.frameElement) {
+ gToolboxSheet = true;
+ InitWithToolbox(window.frameElement.toolbox);
+ repositionDialog(window.frameElement.panel);
+ }
+}
+
+function InitWithToolbox(aToolbox) {
+ gToolbox = aToolbox;
+ dispatchCustomizationEvent("beforecustomization");
+ gToolboxDocument = gToolbox.ownerDocument;
+ gToolbox.customizing = true;
+ forEachCustomizableToolbar(function(toolbar) {
+ toolbar.setAttribute("customizing", "true");
+ });
+ gPaletteBox = document.getElementById("palette-box");
+
+ var elts = getRootElements();
+ for (let i = 0; i < elts.length; i++) {
+ elts[i].addEventListener("dragstart", onToolbarDragStart, true);
+ elts[i].addEventListener("dragover", onToolbarDragOver, true);
+ elts[i].addEventListener("dragexit", onToolbarDragExit, true);
+ elts[i].addEventListener("drop", onToolbarDrop, true);
+ }
+
+ initDialog();
+}
+
+function onClose() {
+ if (!gToolboxSheet) {
+ window.close();
+ } else {
+ finishToolbarCustomization();
+ }
+}
+
+function onUnload() {
+ if (!gToolboxSheet) {
+ finishToolbarCustomization();
+ }
+}
+
+function finishToolbarCustomization() {
+ removeToolboxListeners();
+ unwrapToolbarItems();
+ persistCurrentSets();
+ gToolbox.customizing = false;
+ forEachCustomizableToolbar(function(toolbar) {
+ toolbar.removeAttribute("customizing");
+ });
+
+ notifyParentComplete();
+}
+
+function initDialog() {
+ var mode = gToolbox.getAttribute("mode");
+ document.getElementById("modelist").value = mode;
+ var smallIconsCheckbox = document.getElementById("smallicons");
+ smallIconsCheckbox.checked = gToolbox.getAttribute("iconsize") == "small";
+ if (mode == "text") {
+ smallIconsCheckbox.disabled = true;
+ }
+
+ if (AppConstants.MOZ_APP_NAME == "thunderbird") {
+ document.getElementById(
+ "showTitlebar"
+ ).checked = !Services.prefs.getBoolPref("mail.tabs.drawInTitlebar");
+ document.getElementById(
+ "showDragSpace"
+ ).checked = Services.prefs.getBoolPref("mail.tabs.extraDragSpace");
+ if (
+ window.opener &&
+ window.opener.document.documentElement.getAttribute("windowtype") ==
+ "mail:3pane"
+ ) {
+ document.getElementById("titlebarSettings").hidden = false;
+ }
+ }
+
+ // Build up the palette of other items.
+ buildPalette();
+
+ // Wrap all the items on the toolbar in toolbarpaletteitems.
+ wrapToolbarItems();
+}
+
+function repositionDialog(aWindow) {
+ // Position the dialog touching the bottom of the toolbox and centered with
+ // it.
+ if (!aWindow) {
+ return;
+ }
+
+ var width;
+ if (aWindow != window) {
+ width = aWindow.getBoundingClientRect().width;
+ } else if (document.documentElement.hasAttribute("width")) {
+ width = document.documentElement.getAttribute("width");
+ } else {
+ width = parseInt(document.documentElement.style.width);
+ }
+ var boundingRect = gToolbox.getBoundingClientRect();
+ var screenX = gToolbox.screenX + (boundingRect.width - width) / 2;
+ var screenY = gToolbox.screenY + boundingRect.height;
+
+ aWindow.moveTo(screenX, screenY);
+}
+
+function removeToolboxListeners() {
+ var elts = getRootElements();
+ for (let i = 0; i < elts.length; i++) {
+ elts[i].removeEventListener("dragstart", onToolbarDragStart, true);
+ elts[i].removeEventListener("dragover", onToolbarDragOver, true);
+ elts[i].removeEventListener("dragexit", onToolbarDragExit, true);
+ elts[i].removeEventListener("drop", onToolbarDrop, true);
+ }
+}
+
+/**
+ * Invoke a callback on the toolbox to notify it that the dialog is done
+ * and going away.
+ */
+function notifyParentComplete() {
+ if ("customizeDone" in gToolbox) {
+ gToolbox.customizeDone(gToolboxChanged);
+ }
+ dispatchCustomizationEvent("aftercustomization");
+}
+
+function toolboxChanged(aType) {
+ gToolboxChanged = true;
+ if ("customizeChange" in gToolbox) {
+ gToolbox.customizeChange(aType);
+ }
+ dispatchCustomizationEvent("customizationchange");
+}
+
+function dispatchCustomizationEvent(aEventName) {
+ var evt = document.createEvent("Events");
+ evt.initEvent(aEventName, true, true);
+ gToolbox.dispatchEvent(evt);
+}
+
+/**
+ * Persist the current set of buttons in all customizable toolbars to
+ * localstore.
+ */
+function persistCurrentSets() {
+ if (!gToolboxChanged || gToolboxDocument.defaultView.closed) {
+ return;
+ }
+
+ forEachCustomizableToolbar(function(toolbar) {
+ // Calculate currentset and store it in the attribute.
+ var currentSet = toolbar.currentSet;
+ toolbar.setAttribute("currentset", currentSet);
+ Services.xulStore.persist(toolbar, "currentset");
+ });
+}
+
+/**
+ * Wraps all items in all customizable toolbars in a toolbox.
+ */
+function wrapToolbarItems() {
+ forEachCustomizableToolbar(function(toolbar) {
+ for (let item of toolbar.children) {
+ if (AppConstants.platform == "macosx") {
+ if (
+ item.firstElementChild &&
+ item.firstElementChild.localName == "menubar"
+ ) {
+ return;
+ }
+ }
+ if (isToolbarItem(item)) {
+ let wrapper = wrapToolbarItem(item);
+ cleanupItemForToolbar(item, wrapper);
+ }
+ }
+ });
+}
+
+function getRootElements() {
+ if (window.frameElement && "externalToolbars" in window.frameElement) {
+ return [gToolbox].concat(window.frameElement.externalToolbars);
+ }
+ if ("arguments" in window && window.arguments[1].length > 0) {
+ return [gToolbox].concat(window.arguments[1]);
+ }
+ return [gToolbox];
+}
+
+/**
+ * Unwraps all items in all customizable toolbars in a toolbox.
+ */
+function unwrapToolbarItems() {
+ let elts = getRootElements();
+ for (let i = 0; i < elts.length; i++) {
+ let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem");
+ let paletteItem;
+ while ((paletteItem = paletteItems.item(0)) != null) {
+ let toolbarItem = paletteItem.firstElementChild;
+ restoreItemForToolbar(toolbarItem, paletteItem);
+ paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
+ }
+ }
+}
+
+/**
+ * Creates a wrapper that can be used to contain a toolbaritem and prevent
+ * it from receiving UI events.
+ */
+function createWrapper(aId, aDocument) {
+ let wrapper = aDocument.createXULElement("toolbarpaletteitem");
+
+ wrapper.id = "wrapper-" + aId;
+ return wrapper;
+}
+
+/**
+ * Wraps an item that has been cloned from a template and adds
+ * it to the end of the palette.
+ */
+function wrapPaletteItem(aPaletteItem) {
+ var wrapper = createWrapper(aPaletteItem.id, document);
+
+ wrapper.appendChild(aPaletteItem);
+
+ // XXX We need to call this AFTER the palette item has been appended
+ // to the wrapper or else we crash dropping certain buttons on the
+ // palette due to removal of the command and disabled attributes - JRH
+ cleanUpItemForPalette(aPaletteItem, wrapper);
+
+ gPaletteBox.appendChild(wrapper);
+}
+
+/**
+ * Wraps an item that is currently on a toolbar and replaces the item
+ * with the wrapper. This is not used when dropping items from the palette,
+ * only when first starting the dialog and wrapping everything on the toolbars.
+ */
+function wrapToolbarItem(aToolbarItem) {
+ var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument);
+
+ wrapper.flex = aToolbarItem.flex;
+
+ aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem);
+
+ wrapper.appendChild(aToolbarItem);
+
+ return wrapper;
+}
+
+/**
+ * Get the list of ids for the current set of items on each toolbar.
+ */
+function getCurrentItemIds() {
+ var currentItems = {};
+ forEachCustomizableToolbar(function(toolbar) {
+ var child = toolbar.firstElementChild;
+ while (child) {
+ if (isToolbarItem(child)) {
+ currentItems[child.id] = 1;
+ }
+ child = child.nextElementSibling;
+ }
+ });
+ return currentItems;
+}
+
+/**
+ * Builds the palette of draggable items that are not yet in a toolbar.
+ */
+function buildPalette() {
+ // Empty the palette first.
+ while (gPaletteBox.lastElementChild) {
+ gPaletteBox.lastChild.remove();
+ }
+
+ // Add the toolbar separator item.
+ var templateNode = document.createXULElement("toolbarseparator");
+ templateNode.id = "separator";
+ wrapPaletteItem(templateNode);
+
+ // Add the toolbar spring item.
+ templateNode = document.createXULElement("toolbarspring");
+ templateNode.id = "spring";
+ templateNode.flex = 1;
+ wrapPaletteItem(templateNode);
+
+ // Add the toolbar spacer item.
+ templateNode = document.createXULElement("toolbarspacer");
+ templateNode.id = "spacer";
+ templateNode.flex = 1;
+ wrapPaletteItem(templateNode);
+
+ var currentItems = getCurrentItemIds();
+ templateNode = gToolbox.palette.firstElementChild;
+ while (templateNode) {
+ // Check if the item is already in a toolbar before adding it to the
+ // palette, but do not add back separators, springs and spacers - we do
+ // not want them duplicated.
+ if (!isSpecialItem(templateNode) && !(templateNode.id in currentItems)) {
+ var paletteItem = document.importNode(templateNode, true);
+ wrapPaletteItem(paletteItem);
+ }
+
+ templateNode = templateNode.nextElementSibling;
+ }
+}
+
+/**
+ * Makes sure that an item that has been cloned from a template
+ * is stripped of any attributes that may adversely affect its
+ * appearance in the palette.
+ */
+function cleanUpItemForPalette(aItem, aWrapper) {
+ aWrapper.setAttribute("place", "palette");
+ setWrapperType(aItem, aWrapper);
+
+ if (aItem.hasAttribute("title")) {
+ aWrapper.setAttribute("title", aItem.getAttribute("title"));
+ } else if (aItem.hasAttribute("label")) {
+ aWrapper.setAttribute("title", aItem.getAttribute("label"));
+ } else if (isSpecialItem(aItem)) {
+ var stringBundle = document.getElementById("stringBundle");
+ // Remove the common "toolbar" prefix to generate the string name.
+ var title = stringBundle.getString(aItem.localName.slice(7) + "Title");
+ aWrapper.setAttribute("title", title);
+ }
+ aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title"));
+
+ // Remove attributes that screw up our appearance.
+ aItem.removeAttribute("command");
+ aItem.removeAttribute("observes");
+ aItem.removeAttribute("type");
+ aItem.removeAttribute("width");
+ aItem.removeAttribute("checked");
+ aItem.removeAttribute("collapsed");
+
+ aWrapper.querySelectorAll("[disabled]").forEach(function(aNode) {
+ aNode.removeAttribute("disabled");
+ });
+}
+
+/**
+ * Makes sure that an item that has been cloned from a template
+ * is stripped of all properties that may adversely affect its
+ * appearance in the toolbar. Store critical properties on the
+ * wrapper so they can be put back on the item when we're done.
+ */
+function cleanupItemForToolbar(aItem, aWrapper) {
+ setWrapperType(aItem, aWrapper);
+ aWrapper.setAttribute("place", "toolbar");
+
+ if (aItem.hasAttribute("command")) {
+ aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
+ aItem.removeAttribute("command");
+ }
+
+ if (aItem.hasAttribute("collapsed")) {
+ aWrapper.setAttribute("itemcollapsed", aItem.getAttribute("collapsed"));
+ aItem.removeAttribute("collapsed");
+ }
+
+ if (aItem.checked) {
+ aWrapper.setAttribute("itemchecked", "true");
+ aItem.checked = false;
+ }
+
+ if (aItem.disabled) {
+ aWrapper.setAttribute("itemdisabled", "true");
+ aItem.disabled = false;
+ }
+}
+
+/**
+ * Restore all the properties that we stripped off above.
+ */
+function restoreItemForToolbar(aItem, aWrapper) {
+ if (aWrapper.hasAttribute("itemdisabled")) {
+ aItem.disabled = true;
+ }
+
+ if (aWrapper.hasAttribute("itemchecked")) {
+ aItem.checked = true;
+ }
+
+ if (aWrapper.hasAttribute("itemcollapsed")) {
+ let collapsed = aWrapper.getAttribute("itemcollapsed");
+ aItem.setAttribute("collapsed", collapsed);
+ }
+
+ if (aWrapper.hasAttribute("itemcommand")) {
+ let commandID = aWrapper.getAttribute("itemcommand");
+ aItem.setAttribute("command", commandID);
+
+ // XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
+ let command = gToolboxDocument.getElementById(commandID);
+ if (command && command.hasAttribute("disabled")) {
+ aItem.setAttribute("disabled", command.getAttribute("disabled"));
+ }
+ }
+}
+
+function setWrapperType(aItem, aWrapper) {
+ if (aItem.localName == "toolbarseparator") {
+ aWrapper.setAttribute("type", "separator");
+ } else if (aItem.localName == "toolbarspring") {
+ aWrapper.setAttribute("type", "spring");
+ } else if (aItem.localName == "toolbarspacer") {
+ aWrapper.setAttribute("type", "spacer");
+ } else if (aItem.localName == "toolbaritem" && aItem.firstElementChild) {
+ aWrapper.setAttribute("type", aItem.firstElementChild.localName);
+ }
+}
+
+function setDragActive(aItem, aValue) {
+ var node = aItem;
+ var direction = window.getComputedStyle(aItem).direction;
+ var value = direction == "ltr" ? "left" : "right";
+ if (aItem.localName == "toolbar") {
+ node = aItem.lastElementChild;
+ value = direction == "ltr" ? "right" : "left";
+ }
+
+ if (!node) {
+ return;
+ }
+
+ if (aValue) {
+ if (!node.hasAttribute("dragover")) {
+ node.setAttribute("dragover", value);
+ }
+ } else {
+ node.removeAttribute("dragover");
+ }
+}
+
+/**
+ * Restore the default set of buttons to fixed toolbars,
+ * remove all custom toolbars, and rebuild the palette.
+ */
+function restoreDefaultSet() {
+ // Unwrap the items on the toolbar.
+ unwrapToolbarItems();
+
+ // Remove all of the customized toolbars.
+ var child = gToolbox.lastElementChild;
+ while (child) {
+ if (child.hasAttribute("customindex")) {
+ var thisChild = child;
+ child = child.previousElementSibling;
+ thisChild.currentSet = "__empty";
+ gToolbox.removeChild(thisChild);
+ } else {
+ child = child.previousElementSibling;
+ }
+ }
+
+ // Restore the defaultset for fixed toolbars.
+ forEachCustomizableToolbar(function(toolbar) {
+ var defaultSet = toolbar.getAttribute("defaultset");
+ if (defaultSet) {
+ toolbar.currentSet = defaultSet;
+ }
+ });
+
+ // Restore the default icon size and mode.
+ document.getElementById("smallicons").checked = updateIconSize() == "small";
+ document.getElementById("modelist").value = updateToolbarMode();
+
+ // Now rebuild the palette.
+ buildPalette();
+
+ // Now re-wrap the items on the toolbar.
+ wrapToolbarItems();
+
+ toolboxChanged("reset");
+}
+
+function updateIconSize(aSize) {
+ return updateToolboxProperty("iconsize", aSize, "large");
+}
+
+function updateTitlebar() {
+ let titlebarCheckbox = document.getElementById("showTitlebar");
+ Services.prefs.setBoolPref(
+ "mail.tabs.drawInTitlebar",
+ !titlebarCheckbox.checked
+ );
+
+ // Bring the customizeToolbar window to front (on linux it's behind the main
+ // window). Otherwise the customization window gets left in the background.
+ setTimeout(() => window.focus(), 100);
+}
+
+function updateDragSpace() {
+ let dragSpaceCheckbox = document.getElementById("showDragSpace");
+ Services.prefs.setBoolPref(
+ "mail.tabs.extraDragSpace",
+ dragSpaceCheckbox.checked
+ );
+
+ // Bring the customizeToolbar window to front (on linux it's behind the main
+ // window). Otherwise the customization window gets left in the background.
+ setTimeout(() => window.focus(), 100);
+}
+
+function updateToolbarMode(aModeValue) {
+ var mode = updateToolboxProperty("mode", aModeValue, "icons");
+
+ var iconSizeCheckbox = document.getElementById("smallicons");
+ iconSizeCheckbox.disabled = mode == "text";
+
+ return mode;
+}
+
+function updateToolboxProperty(aProp, aValue, aToolkitDefault) {
+ var toolboxDefault =
+ gToolbox.getAttribute("default" + aProp) || aToolkitDefault;
+
+ gToolbox.setAttribute(aProp, aValue || toolboxDefault);
+ Services.xulStore.persist(gToolbox, aProp);
+
+ forEachCustomizableToolbar(function(toolbar) {
+ var toolbarDefault =
+ toolbar.getAttribute("default" + aProp) || toolboxDefault;
+ if (
+ toolbar.getAttribute("lock" + aProp) == "true" &&
+ toolbar.getAttribute(aProp) == toolbarDefault
+ ) {
+ return;
+ }
+
+ toolbar.setAttribute(aProp, aValue || toolbarDefault);
+ Services.xulStore.persist(toolbar, aProp);
+ });
+
+ toolboxChanged(aProp);
+
+ return aValue || toolboxDefault;
+}
+
+function forEachCustomizableToolbar(callback) {
+ if (window.frameElement && "externalToolbars" in window.frameElement) {
+ Array.from(window.frameElement.externalToolbars)
+ .filter(isCustomizableToolbar)
+ .forEach(callback);
+ } else if ("arguments" in window && window.arguments[1].length > 0) {
+ Array.from(window.arguments[1])
+ .filter(isCustomizableToolbar)
+ .forEach(callback);
+ }
+ Array.from(gToolbox.children)
+ .filter(isCustomizableToolbar)
+ .forEach(callback);
+}
+
+function isCustomizableToolbar(aElt) {
+ return (
+ aElt.localName == "toolbar" && aElt.getAttribute("customizable") == "true"
+ );
+}
+
+function isSpecialItem(aElt) {
+ return (
+ aElt.localName == "toolbarseparator" ||
+ aElt.localName == "toolbarspring" ||
+ aElt.localName == "toolbarspacer"
+ );
+}
+
+function isToolbarItem(aElt) {
+ return (
+ aElt.localName == "toolbarbutton" ||
+ aElt.localName == "toolbaritem" ||
+ aElt.localName == "toolbarseparator" ||
+ aElt.localName == "toolbarspring" ||
+ aElt.localName == "toolbarspacer"
+ );
+}
+
+// Drag and Drop observers
+
+function onToolbarDragExit(aEvent) {
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+
+ if (gCurrentDragOverItem) {
+ setDragActive(gCurrentDragOverItem, false);
+ }
+}
+
+function onToolbarDragStart(aEvent) {
+ var item = aEvent.target;
+ while (item && item.localName != "toolbarpaletteitem") {
+ if (item.localName == "toolbar") {
+ return;
+ }
+ item = item.parentNode;
+ }
+
+ item.setAttribute("dragactive", "true");
+
+ var dt = aEvent.dataTransfer;
+ var documentId = gToolboxDocument.documentElement.id;
+ dt.setData("text/toolbarwrapper-id/" + documentId, item.firstElementChild.id);
+ dt.effectAllowed = "move";
+}
+
+function onToolbarDragOver(aEvent) {
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+
+ var documentId = gToolboxDocument.documentElement.id;
+ if (
+ !aEvent.dataTransfer.types.includes(
+ "text/toolbarwrapper-id/" + documentId.toLowerCase()
+ )
+ ) {
+ return;
+ }
+
+ var toolbar = aEvent.target;
+ var dropTarget = aEvent.target;
+ while (toolbar && toolbar.localName != "toolbar") {
+ dropTarget = toolbar;
+ toolbar = toolbar.parentNode;
+ }
+
+ // Make sure we are dragging over a customizable toolbar.
+ if (!toolbar || !isCustomizableToolbar(toolbar)) {
+ gCurrentDragOverItem = null;
+ return;
+ }
+
+ var previousDragItem = gCurrentDragOverItem;
+
+ if (dropTarget.localName == "toolbar") {
+ gCurrentDragOverItem = dropTarget;
+ } else {
+ gCurrentDragOverItem = null;
+
+ var direction = window.getComputedStyle(dropTarget.parentNode).direction;
+ var boundingRect = dropTarget.getBoundingClientRect();
+ var dropTargetCenter = boundingRect.x + boundingRect.width / 2;
+ var dragAfter;
+ if (direction == "ltr") {
+ dragAfter = aEvent.clientX > dropTargetCenter;
+ } else {
+ dragAfter = aEvent.clientX < dropTargetCenter;
+ }
+
+ if (dragAfter) {
+ gCurrentDragOverItem = dropTarget.nextElementSibling;
+ if (!gCurrentDragOverItem) {
+ gCurrentDragOverItem = toolbar;
+ }
+ } else {
+ gCurrentDragOverItem = dropTarget;
+ }
+ }
+
+ if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
+ setDragActive(previousDragItem, false);
+ }
+
+ setDragActive(gCurrentDragOverItem, true);
+
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+}
+
+function onToolbarDrop(aEvent) {
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+
+ if (!gCurrentDragOverItem) {
+ return;
+ }
+
+ setDragActive(gCurrentDragOverItem, false);
+
+ var documentId = gToolboxDocument.documentElement.id;
+ var draggedItemId = aEvent.dataTransfer.getData(
+ "text/toolbarwrapper-id/" + documentId
+ );
+ if (gCurrentDragOverItem.id == draggedItemId) {
+ return;
+ }
+
+ var toolbar = aEvent.target;
+ while (toolbar.localName != "toolbar") {
+ toolbar = toolbar.parentNode;
+ }
+
+ var draggedPaletteWrapper = document.getElementById(
+ "wrapper-" + draggedItemId
+ );
+ if (!draggedPaletteWrapper) {
+ // The wrapper has been dragged from the toolbar.
+ // Get the wrapper from the toolbar document and make sure that
+ // it isn't being dropped on itself.
+ let wrapper = gToolboxDocument.getElementById("wrapper-" + draggedItemId);
+ if (wrapper == gCurrentDragOverItem) {
+ return;
+ }
+
+ // Don't allow non-removable kids (e.g., the menubar) to move.
+ if (wrapper.firstElementChild.getAttribute("removable") != "true") {
+ return;
+ }
+
+ // Remove the item from its place in the toolbar.
+ wrapper.remove();
+
+ // Determine which toolbar we are dropping on.
+ var dropToolbar = null;
+ if (gCurrentDragOverItem.localName == "toolbar") {
+ dropToolbar = gCurrentDragOverItem;
+ } else {
+ dropToolbar = gCurrentDragOverItem.parentNode;
+ }
+
+ // Insert the item into the toolbar.
+ if (gCurrentDragOverItem != dropToolbar) {
+ dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
+ } else {
+ dropToolbar.appendChild(wrapper);
+ }
+ } else {
+ // The item has been dragged from the palette
+
+ // Create a new wrapper for the item. We don't know the id yet.
+ let wrapper = createWrapper("", gToolboxDocument);
+
+ // Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
+ var newItem = toolbar.insertItem(
+ draggedItemId,
+ gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem,
+ wrapper
+ );
+
+ // Prepare the item and wrapper to look good on the toolbar.
+ cleanupItemForToolbar(newItem, wrapper);
+ wrapper.id = "wrapper-" + newItem.id;
+ wrapper.flex = newItem.flex;
+
+ // Remove the wrapper from the palette.
+ if (
+ draggedItemId != "separator" &&
+ draggedItemId != "spring" &&
+ draggedItemId != "spacer"
+ ) {
+ gPaletteBox.removeChild(draggedPaletteWrapper);
+ }
+ }
+
+ gCurrentDragOverItem = null;
+
+ toolboxChanged();
+}
+
+function onPaletteDragOver(aEvent) {
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+ var documentId = gToolboxDocument.documentElement.id;
+ if (
+ aEvent.dataTransfer.types.includes(
+ "text/toolbarwrapper-id/" + documentId.toLowerCase()
+ )
+ ) {
+ aEvent.preventDefault();
+ }
+}
+
+function onPaletteDrop(aEvent) {
+ if (isUnwantedDragEvent(aEvent)) {
+ return;
+ }
+ var documentId = gToolboxDocument.documentElement.id;
+ var itemId = aEvent.dataTransfer.getData(
+ "text/toolbarwrapper-id/" + documentId
+ );
+
+ var wrapper = gToolboxDocument.getElementById("wrapper-" + itemId);
+ if (wrapper) {
+ // Don't allow non-removable kids (e.g., the menubar) to move.
+ if (wrapper.firstElementChild.getAttribute("removable") != "true") {
+ return;
+ }
+
+ var wrapperType = wrapper.getAttribute("type");
+ if (
+ wrapperType != "separator" &&
+ wrapperType != "spacer" &&
+ wrapperType != "spring"
+ ) {
+ restoreItemForToolbar(wrapper.firstElementChild, wrapper);
+ wrapPaletteItem(document.importNode(wrapper.firstElementChild, true));
+ gToolbox.palette.appendChild(wrapper.firstElementChild);
+ }
+
+ // The item was dragged out of the toolbar.
+ wrapper.remove();
+ }
+
+ toolboxChanged();
+}
+
+function isUnwantedDragEvent(aEvent) {
+ try {
+ if (
+ Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")
+ ) {
+ return false;
+ }
+ } catch (ex) {}
+
+ /* Discard drag events that originated from a separate window to
+ prevent content->chrome privilege escalations. */
+ let mozSourceNode = aEvent.dataTransfer.mozSourceNode;
+ // mozSourceNode is null in the dragStart event handler or if
+ // the drag event originated in an external application.
+ if (!mozSourceNode) {
+ return true;
+ }
+ let sourceWindow = mozSourceNode.ownerGlobal;
+ return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView;
+}
diff --git a/comm/suite/components/customizeToolbar.xhtml b/comm/suite/components/customizeToolbar.xhtml
new file mode 100644
index 0000000000..2888272901
--- /dev/null
+++ b/comm/suite/components/customizeToolbar.xhtml
@@ -0,0 +1,110 @@
+<?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 dialog [
+<!ENTITY % customizeToolbarDTD SYSTEM
+#ifdef MOZ_SUITE
+ "chrome://communicator/locale/customizeToolbar.dtd">
+#else
+ "chrome://messenger/locale/customizeToolbar.dtd">
+#endif
+ %customizeToolbarDTD;
+]>
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+#ifdef MOZ_SUITE
+<?xml-stylesheet href="chrome://communicator/content/customizeToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/customizeToolbar.css" type="text/css"?>
+#else
+<?xml-stylesheet href="chrome://messenger/content/customizeToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/customizeToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/content/messenger.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/messenger.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/messageHeader.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/primaryToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/chat.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/addressbook.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/messengercompose/messengercompose.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/themeableDialog.css" type="text/css"?>
+#endif
+<?xml-stylesheet href="chrome://calendar/skin/calendar-task-view.css" type="text/css"?>
+<?xml-stylesheet href="chrome://calendar/skin/shared/dialogs/calendar-event-dialog.css" type="text/css"?>
+<?xml-stylesheet href="chrome://calendar/skin/calendar-toolbar.css" type="text/css"?>
+
+<window id="CustomizeToolbarWindow"
+ title="&dialog.title;"
+#ifdef MOZ_SUITE
+ onload="onLoad();"
+#else
+ lightweightthemes="true"
+ windowtype="mailnews:customizeToolbar"
+ onload="overlayOnLoad();"
+#endif
+ onunload="onUnload();"
+ style="&dialog.dimensions;"
+ persist="width height"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+
+#ifdef MOZ_SUITE
+<script src="chrome://communicator/content/customizeToolbar.js"/>
+<stringbundle id="stringBundle" src="chrome://communicator/locale/customizeToolbar.properties"/>
+#else
+<script src="chrome://messenger/content/customizeToolbar.js"/>
+<script src="chrome://messenger/content/mailCore.js"/>
+<stringbundle id="stringBundle" src="chrome://messenger/locale/customizeToolbar.properties"/>
+#endif
+
+<keyset id="CustomizeToolbarKeyset">
+ <key id="cmd_close1" keycode="VK_ESCAPE" oncommand="onClose();"/>
+ <key id="cmd_close2" keycode="VK_RETURN" oncommand="onClose();"/>
+</keyset>
+
+<vbox id="main-box" flex="1">
+ <description id="instructions">
+ &instructions.description;
+ </description>
+
+ <vbox flex="1" id="palette-box"
+ ondragstart="onToolbarDragStart(event)"
+ ondragover="onPaletteDragOver(event)"
+ ondrop="onPaletteDrop(event)"/>
+
+ <hbox id="buttonBox" align="center">
+#ifndef MOZ_SUITE
+ <hbox id="titlebarSettings" hidden="true">
+ <checkbox id="showTitlebar" oncommand="updateTitlebar();" label="&showTitlebar2.label;"/>
+ <checkbox id="showDragSpace" oncommand="updateDragSpace();" label="&extraDragSpace2.label;"/>
+ </hbox>
+#endif
+ <label id="modelistLabel" value="&show.label;" control="modelist"/>
+ <menulist id="modelist"
+ value="icons"
+#ifdef MOZ_SUITE
+ oncommand="updateToolbarMode(this.value);">
+#else
+ oncommand="overlayUpdateToolbarMode(this.value, 'mail-toolbox');">
+#endif
+ <menupopup id="modelistpopup">
+ <menuitem id="modefull" value="full" label="&iconsAndText.label;"/>
+ <menuitem id="modeicons" value="icons" label="&icons.label;"/>
+ <menuitem id="modetext" value="text" label="&text.label;"/>
+#ifndef MOZ_SUITE
+ <menuitem id="textbesideiconItem" value="textbesideicon" label="&iconsBesideText.label;"/>
+#endif
+ </menupopup>
+ </menulist>
+ <checkbox id="smallicons" oncommand="updateIconSize(this.checked ? 'small' : 'large');" label="&useSmallIcons.label;"/>
+ </hbox>
+ <hbox align="center">
+ <button id="restoreDefault" label="&restoreDefaultSet.label;" oncommand="restoreDefaultSet();"/>
+ <spacer flex="1"/>
+ <button id="donebutton" label="&saveChanges.label;" oncommand="onClose();"
+ default="true"/>
+ </hbox>
+</vbox>
+
+</window>
diff --git a/comm/suite/components/dataman/content/dataman.css b/comm/suite/components/dataman/content/dataman.css
new file mode 100644
index 0000000000..0e47f37674
--- /dev/null
+++ b/comm/suite/components/dataman/content/dataman.css
@@ -0,0 +1,45 @@
+/* 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/. */
+
+@namespace xhtml "http://www.w3.org/1999/xhtml";
+
+/* HTML link elements do weird things to the layout if they are not hidden */
+xhtml|link {
+ display: none;
+}
+
+/* generic item gets used for permissions that don't need any special treatment */
+richlistitem.permission {
+ -moz-binding: url('chrome://communicator/content/dataman/dataman.xml#perm-generic-item');
+ -moz-box-orient: vertical;
+}
+
+/* cookie item has an allow for session option */
+richlistitem.permission[type="cookie"] {
+ -moz-binding: url('chrome://communicator/content/dataman/dataman.xml#perm-cookie-item');
+}
+
+/* geolocation and indexedDB items default to always ask */
+richlistitem.permission[type="geo"],
+richlistitem.permission[type="indexedDB"] {
+ -moz-binding: url('chrome://communicator/content/dataman/dataman.xml#perm-geo-item');
+}
+
+/* content blocker items have an allow for same domain option */
+richlistitem.permission[type="script"],
+richlistitem.permission[type="image"],
+richlistitem.permission[type="stylesheet"],
+richlistitem.permission[type="object"],
+richlistitem.permission[type="document"],
+richlistitem.permission[type="subdocument"],
+richlistitem.permission[type="refresh"],
+richlistitem.permission[type="xbl"],
+richlistitem.permission[type="ping"],
+richlistitem.permission[type="xmlhttprequest"],
+richlistitem.permission[type="objectsubrequest"],
+richlistitem.permission[type="dtd"],
+richlistitem.permission[type="font"],
+richlistitem.permission[type="media"] {
+ -moz-binding: url('chrome://communicator/content/dataman/dataman.xml#perm-content-item');
+}
diff --git a/comm/suite/components/dataman/content/dataman.js b/comm/suite/components/dataman/content/dataman.js
new file mode 100644
index 0000000000..be3063a81f
--- /dev/null
+++ b/comm/suite/components/dataman/content/dataman.js
@@ -0,0 +1,3270 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Async} = ChromeUtils.import("resource://services-common/async.js");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+// Load DownloadUtils module for convertByteUnits
+const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+
+// locally loaded services
+var gLocSvc = {};
+ChromeUtils.defineModuleGetter(gLocSvc, "FormHistory",
+ "resource://gre/modules/FormHistory.jsm",
+ "FormHistory");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "url",
+ "@mozilla.org/network/url-parser;1?auth=maybe",
+ "nsIURLParser");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "clipboard",
+ "@mozilla.org/widget/clipboardhelper;1",
+ "nsIClipboardHelper");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "idn",
+ "@mozilla.org/network/idn-service;1",
+ "nsIIDNService");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "appcache",
+ "@mozilla.org/network/application-cache-service;1",
+ "nsIApplicationCacheService");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "domstoremgr",
+ "@mozilla.org/dom/storagemanager;1",
+ "nsIDOMStorageManager");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "idxdbmgr",
+ "@mozilla.org/dom/indexeddb/manager;1",
+ "nsIIndexedDatabaseManager");
+XPCOMUtils.defineLazyServiceGetter(gLocSvc, "ssm",
+ "@mozilla.org/scriptsecuritymanager;1",
+ "nsIScriptSecurityManager");
+
+// From nsContentBlocker.cpp
+const NOFOREIGN = 3;
+
+// :::::::::::::::::::: general functions ::::::::::::::::::::
+var gDataman = {
+ bundle: null,
+ debug: false,
+ timer: null,
+ viewToLoad: ["*", "formdata"],
+
+ initialize: function dataman_initialize() {
+ try {
+ this.debug = Services.prefs.getBoolPref("data_manager.debug");
+ }
+ catch (e) {}
+ this.bundle = document.getElementById("datamanBundle");
+
+ Services.obs.addObserver(this, "cookie-changed");
+ Services.obs.addObserver(this, "perm-changed");
+ Services.obs.addObserver(this, "passwordmgr-storage-changed");
+ Services.contentPrefs2.addObserverForName(null, this);
+ Services.obs.addObserver(this, "satchel-storage-changed");
+ Services.obs.addObserver(this, "dom-storage-changed");
+ Services.obs.addObserver(this, "dom-storage2-changed");
+
+ this.timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+
+ gTabs.initialize();
+ gDomains.initialize();
+
+ if ("arguments" in window &&
+ window.arguments.length >= 1 &&
+ window.arguments[0]) {
+ this.loadView(window.arguments[0])
+ }
+ },
+
+ shutdown: function dataman_shutdown() {
+ Services.obs.removeObserver(this, "cookie-changed");
+ Services.obs.removeObserver(this, "perm-changed");
+ Services.obs.removeObserver(this, "passwordmgr-storage-changed");
+ Services.contentPrefs2.removeObserverForName(null, this);
+ Services.obs.removeObserver(this, "satchel-storage-changed");
+ Services.obs.removeObserver(this, "dom-storage-changed");
+ Services.obs.removeObserver(this, "dom-storage2-changed");
+
+ gDomains.shutdown();
+ },
+
+ loadView: function dataman_loadView(aView) {
+ // Set variable, used in initizalization routine.
+ // Syntax: <domain>|<pane> (|<pane> is optional)
+ // Examples: example.com
+ // example.org|permissions
+ // example.org:8888|permissions|add|popup
+ // |cookies
+ // Allowed pane names:
+ // cookies, permissions, preferences, passwords, formdata
+ // Invalid views fall back to the default available ones
+ // Full host names (even including ports) for domain are allowed
+ // Empty domain with a pane specified will only list this data type
+ // Permissions allow specifying "add" and type to prefill the adding field
+ this.viewToLoad = aView.split('|');
+ if (gDomains.listLoadCompleted)
+ gDomains.loadView();
+ // Else will call this at the end of loading the list.
+ },
+
+ handleKeyPress: function dataman_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE &&
+ gTabs.tabbox.selectedPanel &&
+ gTabs.tabbox.selectedPanel.id == "forgetPanel") {
+ gForget.handleKeyPress(aEvent);
+ }
+ },
+
+ debugMsg: function dataman_debugMsg(aLogMessage) {
+ if (this.debug)
+ Services.console.logStringMessage(aLogMessage);
+ },
+
+ debugError: function dataman_debugError(aLogMessage) {
+ if (this.debug)
+ Cu.reportError(aLogMessage);
+ },
+
+ // :::::::::: data change observers ::::::::::
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIContentPrefObserver]),
+
+ observe: function co_observe(aSubject, aTopic, aData) {
+ gDataman.debugMsg("Observed: " + aTopic + " - " + aData);
+ switch (aTopic) {
+ case "cookie-changed":
+ gCookies.reactToChange(aSubject, aData);
+ break;
+ case "perm-changed":
+ gPerms.reactToChange(aSubject, aData);
+ break;
+ case "passwordmgr-storage-changed":
+ gPasswords.reactToChange(aSubject, aData);
+ break;
+ case "satchel-storage-changed":
+ gFormdata.reactToChange(aSubject, aData);
+ break;
+ case "dom-storage2-changed": // sessionStorage, localStorage
+ gStorage.reactToChange(aSubject, aData);
+ break;
+ default:
+ gDataman.debugError("Unexpected change topic observed: " + aTopic);
+ break;
+ }
+ },
+
+ // Compat with nsITimerCallback so we can be used in a timer.
+ notify: function(timer) {
+ gDataman.debugMsg("Timer fired, reloading storage: " + Date.now()/1000);
+ gStorage.reloadList();
+ },
+
+ onContentPrefSet: function co_onContentPrefSet(aGroup, aName, aValue) {
+ gDataman.debugMsg("Observed: content pref set");
+ gPrefs.reactToChange({host: aGroup, name: aName, value: aValue}, "prefSet");
+ },
+
+ onContentPrefRemoved: function co_onContentPrefRemoved(aGroup, aName) {
+ gDataman.debugMsg("Observed: content pref removed");
+ gPrefs.reactToChange({host: aGroup, name: aName}, "prefRemoved");
+ },
+
+ // :::::::::: utility functions ::::::::::
+ getTreeSelections: function dataman_getTreeSelections(aTree) {
+ let selections = [];
+ let select = aTree.view.selection;
+ if (select && aTree.view.rowCount) {
+ let count = select.getRangeCount();
+ let min = {};
+ let max = {};
+ for (let i = 0; i < count; i++) {
+ select.getRangeAt(i, min, max);
+ for (let k = min.value; k <= max.value; k++)
+ if (k != -1)
+ selections.push(k);
+ }
+ }
+ return selections;
+ },
+
+ getSelectedIDs: function dataman_getSelectedIDs(aTree, aIDFunction) {
+ // Get IDs of selected elements for later restoration.
+ let selectionCache = [];
+ if (aTree.view.selection.count < 1 || aTree.view.rowCount < 1)
+ return selectionCache;
+
+ // Walk all selected rows and cache their IDs.
+ let start = {};
+ let end = {};
+ let numRanges = aTree.view.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++){
+ aTree.view.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++)
+ selectionCache.push(aIDFunction(row));
+ }
+ return selectionCache;
+ },
+
+ restoreSelectionFromIDs: function dataman_restoreSelectionFromIDs(aTree, aIDFunction, aCachedIDs) {
+ // Restore selection from cached IDs (as possible).
+ if (!aCachedIDs.length)
+ return;
+
+ aTree.view.selection.clearSelection();
+ // Find out which current rows match a cached selection and add them to the selection.
+ for (let row = 0; row < aTree.view.rowCount; row++)
+ if (aCachedIDs.includes(aIDFunction(row)))
+ aTree.view.selection.toggleSelect(row);
+ },
+}
+
+// :::::::::::::::::::: base object to use as a prototype for all others ::::::::::::::::::::
+var gBaseTreeView = {
+ setTree: function(aTree) {},
+ getImageSrc: function(aRow, aColumn) {},
+ getProgressMode: function(aRow, aColumn) {},
+ getCellValue: function(aRow, aColumn) {},
+ isSeparator: function(aIndex) { return false; },
+ isSorted: function() { return false; },
+ isContainer: function(aIndex) { return false; },
+ cycleHeader: function(aCol) {},
+ getRowProperties: function(aRow) { return ""; },
+ getColumnProperties: function(aColumn) { return ""; },
+ getCellProperties: function(aRow, aColumn) { return ""; }
+};
+
+// :::::::::::::::::::: domain list ::::::::::::::::::::
+var gDomains = {
+ tree: null,
+ selectfield: null,
+ searchfield: null,
+
+ domains: {},
+ domainObjects: {},
+ displayedDomains: [],
+ selectedDomain: {},
+ xlcache: {},
+
+ ignoreSelect: false,
+ ignoreUpdate: false,
+ listLoadCompleted: false,
+
+ initialize: function domain_initialize() {
+ gDataman.debugMsg("Start building domain list: " + Date.now()/1000);
+
+ this.tree = document.getElementById("domainTree");
+ this.tree.view = this;
+
+ this.selectfield = document.getElementById("typeSelect");
+ this.searchfield = document.getElementById("domainSearch");
+
+ // global "domain"
+ Services.contentPrefs2.hasPrefs(null, null, {
+ handleResult(resultPref) {
+ gDomains.domainObjects["*"] = {title: "*",
+ displayTitle: "*",
+ hasPermissions: true,
+ hasPreferences: resultPref.value,
+ hasFormData: true};
+ },
+ handleCompletion: () => {
+ },
+ });
+
+ this.search("");
+ if (!gDataman.viewToLoad.length)
+ this.tree.view.selection.select(0);
+
+ let loaderInstance;
+
+ function nextStep() {
+ loaderInstance.next();
+ }
+
+ function* loader() {
+ // Add domains for all cookies we find.
+ gDataman.debugMsg("Add cookies to domain list: " + Date.now()/1000);
+ gDomains.ignoreUpdate = true;
+ gCookies.loadList();
+ for (let cookie of gCookies.cookies)
+ gDomains.addDomainOrFlag(cookie.rawHost, "hasCookies");
+ gDomains.ignoreUpdate = false;
+ gDomains.search(gDomains.searchfield.value);
+ yield setTimeout(nextStep, 0);
+
+ // Add domains for permissions.
+ gDataman.debugMsg("Add permissions to domain list: " + Date.now()/1000);
+ gDomains.ignoreUpdate = true;
+ let enumerator = Services.perms.enumerator;
+ while (enumerator.hasMoreElements()) {
+ let nextPermission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+
+ if (!gDomains.commonScheme(nextPermission.principal.URI.scheme)) {
+ gDomains.addDomainOrFlag("*", "hasPermissions");
+ }
+ else {
+ gDomains.addDomainOrFlag(nextPermission.principal.URI.host.replace(/^\./, ""), "hasPermissions");
+ }
+ }
+ gDomains.ignoreUpdate = false;
+ gDomains.search(gDomains.searchfield.value);
+ yield setTimeout(nextStep, 0);
+
+ let domains = [];
+ Services.contentPrefs2.getDomains(null, {
+ handleResult(resultPref) {
+ domains.push(resultPref.domain);
+ },
+ handleCompletion: () => {
+ // Add domains for content prefs.
+ gDataman.debugMsg("Add content prefs to domain list: " +
+ Date.now()/1000);
+ gDomains.ignoreUpdate = true;
+ for (let domain of domains) {
+ gDataman.debugMsg("Found pref: " + domain);
+ let prefHost = gDomains.getDomainFromHostWithCheck(domain);
+ gDomains.addDomainOrFlag(prefHost, "hasPreferences");
+ }
+ gDomains.ignoreUpdate = false;
+ gDomains.search(gDomains.searchfield.value);
+ },
+ });
+
+ // Add domains for passwords.
+ gDataman.debugMsg("Add passwords to domain list: " + Date.now()/1000);
+ gDomains.ignoreUpdate = true;
+ gPasswords.loadList();
+ for (let pSignon of gPasswords.allSignons) {
+ gDomains.addDomainOrFlag(pSignon.hostname, "hasPasswords");
+ }
+ gDomains.ignoreUpdate = false;
+ gDomains.search(gDomains.searchfield.value);
+ yield setTimeout(nextStep, 0);
+
+ // Add domains for web storages.
+ gDataman.debugMsg("Add storages to domain list: " + Date.now()/1000);
+ // Force DOM Storage to write its data to the disk.
+ Services.obs.notifyObservers(window, "domstorage-flush-timer");
+ yield setTimeout(nextStep, 0);
+ gStorage.loadList();
+ for (let sStorage of gStorage.storages) {
+ gDomains.addDomainOrFlag(sStorage.rawHost, "hasStorage");
+ }
+ gDomains.search(gDomains.searchfield.value);
+ // As we don't get notified of storage changes properly, reload on timer.
+ // The repeat time is in milliseconds, we're using 10 min for now.
+ gDataman.timer.initWithCallback(gDataman, 10 * 60000,
+ Ci.nsITimer.TYPE_REPEATING_SLACK);
+ yield setTimeout(nextStep, 0);
+
+ gDataman.debugMsg("Domain list built: " + Date.now()/1000);
+ gDomains.listLoadCompleted = true;
+ gDomains.loadView();
+ yield undefined;
+ }
+
+ loaderInstance = loader();
+ setTimeout(nextStep, 0);
+ },
+
+ shutdown: function domain_shutdown() {
+ gDataman.timer.cancel();
+ gTabs.shutdown();
+ this.tree.view = null;
+ },
+
+ loadView: function domain_loadView() {
+ // Load the view set in the dataman object.
+ gDataman.debugMsg("Load View: " + gDataman.viewToLoad.join(", "));
+ let loaderInstance;
+ function nextStep() {
+ loaderInstance.next();
+ }
+
+ function* loader() {
+ if (gDataman.viewToLoad.length) {
+ if (gDataman.viewToLoad[0] == "" && gDataman.viewToLoad.length > 1) {
+ let sType = gDataman.viewToLoad[1].substr(0,1).toUpperCase() +
+ gDataman.viewToLoad[1].substr(1);
+ gDataman.debugMsg("Select a specific data type: " + sType);
+ gDomains.selectfield.value = sType;
+ gDomains.selectType(sType);
+ yield setTimeout(nextStep, 0);
+
+ if (gDomains.tree.view.rowCount) {
+ // Select first domain and panel fitting selected type.
+ gDomains.tree.view.selection.select(0);
+ gDomains.tree.treeBoxObject.ensureRowIsVisible(0);
+ yield setTimeout(nextStep, 0);
+
+ // This should always exist and be enabled, but play safe.
+ let loadTabID = gDataman.viewToLoad[1] + "Tab";
+ if (gTabs[loadTabID] && !gTabs[loadTabID].disabled)
+ gTabs.tabbox.selectedTab = gTabs[loadTabID];
+ }
+ }
+ else {
+ gDataman.debugMsg("Domain for view found");
+ gDomains.selectfield.value = "all";
+ gDomains.selectType("all");
+ let host = gDataman.viewToLoad[0];
+
+ // Might have a host:port case, fake a scheme when none present.
+ if (!/:\//.test(host))
+ host = "foo://" + host;
+
+ gDataman.debugMsg("host: " + host);
+
+ let viewdomain = "*";
+
+ // avoid error message in log for the generic entry
+ if (host != "foo://*")
+ viewdomain = gDomains.getDomainFromHost(host);
+
+ gDataman.debugMsg("viewDomain: " + viewdomain);
+
+ let selectIdx = 0; // tree index to be selected
+ for (let i = 0; i < gDomains.displayedDomains.length; i++) {
+ if (gDomains.displayedDomains[i].title == viewdomain) {
+ selectIdx = i;
+ break;
+ }
+ }
+
+ let permAdd = (gDataman.viewToLoad[1] &&
+ gDataman.viewToLoad[1] == "permissions" &&
+ gDataman.viewToLoad[2] &&
+ gDataman.viewToLoad[2] == "add");
+ if (permAdd && selectIdx != 0 &&
+ (!(viewdomain in gDomains.domainObjects) ||
+ !gDomains.domainObjects[viewdomain].hasPermissions)) {
+ selectIdx = 0; // Force * domain as we have a perm panel there.
+ }
+
+ if (gDomains.tree.currentIndex != selectIdx) {
+ gDomains.tree.view.selection.select(selectIdx);
+ gDomains.tree.treeBoxObject.ensureRowIsVisible(selectIdx);
+ }
+ yield setTimeout(nextStep, 0);
+
+ if (gDataman.viewToLoad.length > 1) {
+ gDataman.debugMsg("Pane for view found");
+ let loadTabID = gDataman.viewToLoad[1] + "Tab";
+ if (gTabs[loadTabID] && !gTabs[loadTabID].disabled)
+ gTabs.tabbox.selectedTab = gTabs[loadTabID];
+
+ yield setTimeout(nextStep, 0);
+
+ if (permAdd) {
+ gDataman.debugMsg("Adding permission");
+ if (gPerms.addSelBox.hidden)
+ gPerms.addButtonClick();
+ gPerms.addHost.value = gDataman.viewToLoad[0];
+ if (gDataman.viewToLoad[3])
+ gPerms.addType.value = gDataman.viewToLoad[3];
+ gPerms.addCheck();
+ gPerms.addButton.focus();
+ }
+ }
+ }
+ }
+ yield setTimeout(nextStep, 0);
+
+ // Send a notification that we have finished.
+ Services.obs.notifyObservers(window, "dataman-loaded");
+ yield undefined;
+ }
+
+ loaderInstance = loader();
+ setTimeout(nextStep, 0);
+ },
+
+ _getObjID: function domain__getObjID(aIdx) {
+ return gDomains.displayedDomains[aIdx].title;
+ },
+
+ getDomainFromHostWithCheck: function domain_getDomainFromHostWithCheck(aHost) {
+ // Global content pref changes and others might not have a host.
+ if (!aHost) {
+ return '*';
+ }
+
+ let host = gDomains.getDomainFromHost(aHost);
+ // Host couldn't be found or is an internal page or data.
+ if (!host ||
+ host.trim().length == 0 ||
+ aHost.startsWith("about:") ||
+ aHost.startsWith("jar:"))
+ return '*';
+
+ return host.trim();
+ },
+
+ getDomainFromHost: function domain_getDomainFromHost(aHostname) {
+ // Find the base domain name for the given host name.
+ if (!this.xlcache[aHostname]) {
+ // aHostname is not always an actual host name, but potentially something
+ // URI-like, e.g. gopher://example.com and newURI doesn't work there as we
+ // need to display entries for schemes that are not supported (any more).
+ // nsIURLParser is a fast way to generically ensure a pure host name.
+ var hostName;
+ // Return vars for nsIURLParser must all be objects,
+ // see bug 568997 for improvements to that interface.
+ var schemePos = {}, schemeLen = {}, authPos = {}, authLen = {}, pathPos = {},
+ pathLen = {}, usernamePos = {}, usernameLen = {}, passwordPos = {},
+ passwordLen = {}, hostnamePos = {}, hostnameLen = {}, port = {};
+ try {
+ gLocSvc.url.parseURL(aHostname, -1, schemePos, schemeLen, authPos, authLen,
+ pathPos, pathLen);
+ var auth = aHostname.substring(authPos.value, authPos.value + authLen.value);
+ gLocSvc.url.parseAuthority(auth, authLen.value, usernamePos, usernameLen,
+ passwordPos, passwordLen, hostnamePos, hostnameLen, port);
+ hostName = auth.substring(hostnamePos.value, hostnamePos.value + hostnameLen.value);
+ }
+ catch (e) {
+ // IPv6 host names can come in without [] around them and therefore
+ // cause an error. Those consist of at least two colons and else only
+ // hexadecimal digits. Fix them by putting [] around them.
+ if (/^[a-f0-9]*:[a-f0-9]*:[a-f0-9:]*$/.test(aHostname)) {
+ gDataman.debugMsg("bare IPv6 address found: " + aHostname);
+ hostName = "[" + aHostname + "]";
+ }
+ else {
+ gDataman.debugError("Error while trying to get hostname from input: " + aHostname);
+ gDataman.debugError(e);
+ hostName = aHostname;
+ }
+ }
+
+ var domain;
+ try {
+ domain = Services.eTLD.getBaseDomainFromHost(hostName);
+ }
+ catch (e) {
+ gDataman.debugMsg("Unable to get domain from host name: " + hostName);
+ domain = hostName;
+ }
+ this.xlcache[aHostname] = domain;
+ gDataman.debugMsg("cached: " + aHostname + " -> " + this.xlcache[aHostname]);
+ } // end hostname not cached
+ return this.xlcache[aHostname];
+ },
+
+ // Used for checking if * global data domain should be used.
+ commonScheme: function domain_commonScheme(aScheme) {
+ // case intensitive search for domain schemes
+ return /^(https?|ftp|gopher)/i.test(aScheme);
+ },
+
+ hostMatchesSelected: function domain_hostMatchesSelected(aHostname) {
+ return this.getDomainFromHost(aHostname) == this.selectedDomain.title;
+ },
+
+ hostMatchesSelectedURI: function domain_hostMatchesSelectedURI(aURI) {
+ // default to * global data domain.
+ let mScheme = "*";
+
+ // First, try to get the scheme.
+ try {
+ mScheme = aURI.scheme;
+ }
+ catch (e) {
+ gDataman.debugError("Invalid permission found: " + aUri);
+ }
+
+ // See if his is a scheme which does not go into the global data domain.
+ if (!this.commonScheme(mScheme)) {
+ return ("*") == this.selectedDomain.title;
+ }
+
+ rawHost = aURI.host.replace(/^\./, "");
+ return this.getDomainFromHost(rawHost) == this.selectedDomain.title;
+ },
+
+ addDomainOrFlag: function domain_addDomainOrFlag(aHostname, aFlag) {
+ let domain;
+ // For existing domains, add flags, for others, add them to the object.
+ if (aHostname == "*")
+ domain = aHostname;
+ else
+ domain = this.getDomainFromHost(aHostname);
+
+ if (!this.domainObjects[domain]) {
+ this.domainObjects[domain] = {title: domain};
+ if (/xn--/.test(domain))
+ this.domainObjects[domain].displayTitle = gLocSvc.idn.convertToDisplayIDN(domain, {});
+ else
+ this.domainObjects[domain].displayTitle = this.domainObjects[domain].title;
+ this.domainObjects[domain][aFlag] = true;
+ gDataman.debugMsg("added domain: " + domain + " (with flag " + aFlag + ")");
+ if (!this.ignoreUpdate)
+ this.search(this.searchfield.value);
+ }
+ else if (!this.domainObjects[domain][aFlag]) {
+ this.domainObjects[domain][aFlag] = true;
+ gDataman.debugMsg("added flag " + aFlag + " to " + domain);
+ if (domain == this.selectedDomain.title) {
+ // Just update the tab states.
+ this.select(true);
+ }
+ }
+ },
+
+ removeDomainOrFlag: function domain_removeDomainOrFlag(aDomain, aFlag) {
+ // Remove a flag from the given domain,
+ // remove the whole domain if it doesn't have any flags left.
+ if (!this.domainObjects[aDomain])
+ return;
+
+ gDataman.debugMsg("removed flag " + aFlag + " from " + aDomain);
+ this.domainObjects[aDomain][aFlag] = false;
+ if (!this.domainObjects[aDomain].hasCookies &&
+ !this.domainObjects[aDomain].hasPermissions &&
+ !this.domainObjects[aDomain].hasPreferences &&
+ !this.domainObjects[aDomain].hasPasswords &&
+ !this.domainObjects[aDomain].hasStorage &&
+ !this.domainObjects[aDomain].hasFormData) {
+ gDataman.debugMsg("removed domain: " + aDomain);
+ // Get index in display tree.
+ let disp_idx = -1;
+ for (let i = 0; i < this.displayedDomains.length; i++) {
+ if (this.displayedDomains[i] == this.domainObjects[aDomain]) {
+ disp_idx = i;
+ break;
+ }
+ }
+ this.displayedDomains.splice(disp_idx, 1);
+ this.tree.treeBoxObject.rowCountChanged(disp_idx, -1);
+ delete this.domainObjects[aDomain];
+ // Make sure we clear the data pane when selection has been removed.
+ if (!this.tree.view.selection.count)
+ this.select();
+ }
+ else {
+ // Just update the tab states.
+ this.select(true);
+ }
+ },
+
+ resetFlagToDomains: function domain_resetFlagToDomains(aFlag, aDomainList) {
+ // Reset a flag to be only set on a specific set of domains,
+ // purging then-emtpy domain in the process.
+ // Needed when we need to reload a complete set of items.
+ gDataman.debugMsg("resetting domains for flag: " + aFlag);
+ this.ignoreSelect = true;
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ this.tree.view.selection.clearSelection();
+ // First, clear all domains of this flag.
+ for (let domain in this.domainObjects) {
+ this.domainObjects[domain][aFlag] = false;
+ }
+ // Then, set it again on all domains in the new list.
+ for (let domain of aDomainList) {
+ this.addDomainOrFlag(domain, aFlag);
+ }
+ // Now, purge all empty domains.
+ for (let domain in this.domainObjects) {
+ if (!this.domainObjects[domain].hasCookies &&
+ !this.domainObjects[domain].hasPermissions &&
+ !this.domainObjects[domain].hasPreferences &&
+ !this.domainObjects[domain].hasPasswords &&
+ !this.domainObjects[domain].hasStorage &&
+ !this.domainObjects[domain].hasFormData) {
+ delete this.domainObjects[domain];
+ }
+ }
+ this.search(this.searchfield.value);
+ this.ignoreSelect = false;
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ // Make sure we clear the data pane when selection has been removed.
+ if (!this.tree.view.selection.count && selectionCache.length)
+ this.select();
+ },
+
+ select: function domain_select(aNoTabSelect) {
+ if (this.ignoreSelect) {
+ if (this.tree.view.selection.count == 1)
+ this.selectedDomain = this.displayedDomains[this.tree.currentIndex];
+ return;
+ }
+
+ gDataman.debugMsg("Domain selected: " + Date.now()/1000);
+
+ if (!this.tree.view.selection.count) {
+ gTabs.cookiesTab.disabled = true;
+ gTabs.permissionsTab.disabled = true;
+ gTabs.preferencesTab.disabled = true;
+ gTabs.passwordsTab.disabled = true;
+ gTabs.storageTab.disabled = true;
+ gTabs.formdataTab.hidden = true;
+ gTabs.formdataTab.disabled = true;
+ gTabs.forgetTab.hidden = true;
+ gTabs.forgetTab.disabled = true;
+ gTabs.shutdown();
+ this.selectedDomain = {title: null};
+ gDataman.debugMsg("Domain select aborted (no selection)");
+ return;
+ }
+
+ if (this.tree.view.selection.count > 1) {
+ gDataman.debugError("Data Manager doesn't support anything but one selected domain");
+ this.tree.view.selection.clearSelection();
+ this.selectedDomain = {title: null};
+ return;
+ }
+ this.selectedDomain = this.displayedDomains[this.tree.currentIndex];
+ // Disable/enable and hide/show the tabs as needed.
+ gTabs.cookiesTab.disabled = !this.selectedDomain.hasCookies;
+ gTabs.permissionsTab.disabled = !this.selectedDomain.hasPermissions;
+ gTabs.preferencesTab.disabled = !this.selectedDomain.hasPreferences;
+ gTabs.passwordsTab.disabled = !this.selectedDomain.hasPasswords;
+ gTabs.storageTab.disabled = !this.selectedDomain.hasStorage;
+ gTabs.formdataTab.hidden = !this.selectedDomain.hasFormData;
+ gTabs.formdataTab.disabled = !this.selectedDomain.hasFormData;
+ gTabs.forgetTab.disabled = true;
+ gTabs.forgetTab.hidden = true;
+ // Switch to the first non-disabled tab if the one that's showing is
+ // disabled, otherwise, you can't use the keyboard to switch tabs.
+ if (gTabs.tabbox.selectedTab.disabled) {
+ for (let i = 0; i < gTabs.tabbox.tabs.childNodes.length; ++i) {
+ if (!gTabs.tabbox.tabs.childNodes[i].disabled) {
+ gTabs.tabbox.selectedIndex = i;
+ break;
+ }
+ }
+ }
+ if (!aNoTabSelect)
+ gTabs.select();
+
+ // Ensure the focus stays on our tree.
+ this.tree.focus();
+
+ gDataman.debugMsg("Domain select finished: " + Date.now()/1000);
+ },
+
+ handleKeyPress: function domain_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.forget();
+ }
+ else if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE &&
+ gTabs.activePanel == "forgetPanel") {
+ gForget.handleKeyPress(aEvent);
+ }
+ },
+
+ sort: function domain_sort() {
+ if (!this.displayedDomains.length)
+ return;
+
+ // compare function for two domain items
+ let compfunc = function domain_sort_compare(aOne, aTwo) {
+ // Make sure "*" is always first.
+ if (aOne.displayTitle == "*")
+ return -1;
+ if (aTwo.displayTitle == "*")
+ return 1;
+ return aOne.displayTitle.localeCompare(aTwo.displayTitle);
+ };
+
+ // Do the actual sorting of the array.
+ this.displayedDomains.sort(compfunc);
+ this.tree.treeBoxObject.invalidate();
+ },
+
+ forget: function domain_forget() {
+ gTabs.forgetTab.hidden = false;
+ gTabs.forgetTab.disabled = false;
+ gTabs.tabbox.selectedTab = gTabs.forgetTab;
+ },
+
+ selectType: function domain_selectType(aType) {
+ this.search(this.searchfield.value, aType);
+ },
+
+ search: function domain_search(aSearchString, aType) {
+ this.ignoreSelect = true;
+ this.tree.treeBoxObject.beginUpdateBatch();
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ this.tree.view.selection.clearSelection();
+ this.displayedDomains = [];
+ var lcSearch = aSearchString.toLocaleLowerCase();
+ var sType = aType || this.selectfield.value;
+ for (let domain in this.domainObjects) {
+ if (this.domainObjects[domain].displayTitle
+ .toLocaleLowerCase().includes(lcSearch) &&
+ (sType == "all" || this.domainObjects[domain]["has" + sType]))
+ this.displayedDomains.push(this.domainObjects[domain]);
+ }
+ this.sort();
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ this.tree.treeBoxObject.endUpdateBatch();
+ this.ignoreSelect = false;
+ // Make sure we clear the data pane when selection has been removed.
+ if (!this.tree.view.selection.count && selectionCache.length)
+ this.select();
+ },
+
+ focusSearch: function domain_focusSearch() {
+ this.searchfield.focus();
+ },
+
+ updateContext: function domain_updateContext() {
+ let forgetCtx = document.getElementById("domain-context-forget");
+ forgetCtx.disabled = !this.selectedDomain.title;
+ forgetCtx.label = this.selectedDomain.title == "*" ?
+ forgetCtx.getAttribute("label_global") :
+ forgetCtx.getAttribute("label_domain");
+ forgetCtx.accesskey = this.selectedDomain.title == "*" ?
+ forgetCtx.getAttribute("accesskey_global") :
+ forgetCtx.getAttribute("accesskey_domain");
+ },
+
+ // nsITreeView
+ __proto__: gBaseTreeView,
+ get rowCount() {
+ return this.displayedDomains.length;
+ },
+ getCellText: function(aRow, aColumn) {
+ switch (aColumn.id) {
+ case "domainCol":
+ return this.displayedDomains[aRow].displayTitle;
+ }
+ },
+};
+
+// :::::::::::::::::::: tab management ::::::::::::::::::::
+var gTabs = {
+ tabbox: null,
+ tabs: null,
+ cookiesTab: null,
+ permissionsTab: null,
+ preferencesTab: null,
+ passwordsTab: null,
+ storageTab: null,
+ formdataTab: null,
+ forgetTab: null,
+
+ panels: {},
+ activePanel: null,
+
+ initialize: function tabs_initialize() {
+ gDataman.debugMsg("Initializing tabs");
+ this.tabbox = document.getElementById("tabbox");
+ this.cookiesTab = document.getElementById("cookiesTab");
+ this.permissionsTab = document.getElementById("permissionsTab");
+ this.preferencesTab = document.getElementById("preferencesTab");
+ this.passwordsTab = document.getElementById("passwordsTab");
+ this.storageTab = document.getElementById("storageTab");
+ this.formdataTab = document.getElementById("formdataTab");
+ this.forgetTab = document.getElementById("forgetTab");
+
+ this.panels = {
+ cookiesPanel: gCookies,
+ permissionsPanel: gPerms,
+ preferencesPanel: gPrefs,
+ passwordsPanel: gPasswords,
+ storagePanel: gStorage,
+ formdataPanel: gFormdata,
+ forgetPanel: gForget
+ };
+ },
+
+ shutdown: function tabs_shutdown() {
+ gDataman.debugMsg("Shutting down tabs");
+ if (this.activePanel) {
+ this.panels[this.activePanel].shutdown();
+ this.activePanel = null;
+ }
+ },
+
+ select: function tabs_select() {
+ gDataman.debugMsg("Selecting tab");
+ if (this.activePanel) {
+ this.panels[this.activePanel].shutdown();
+ this.activePanel = null;
+ }
+
+ if (!this.tabbox || this.tabbox.selectedPanel.disabled)
+ return;
+
+ this.activePanel = this.tabbox.selectedPanel.id;
+ this.panels[this.activePanel].initialize();
+ },
+
+ selectAll: function tabs_selectAll() {
+ try {
+ this.panels[this.activePanel].selectAll();
+ }
+ catch (e) {
+ gDataman.debugError("SelectAll didn't work for " + this.activePanel + ": " + e);
+ }
+ },
+
+ focusSearch: function tabs_focusSearch() {
+ try {
+ this.panels[this.activePanel].focusSearch();
+ }
+ catch (e) {
+ gDataman.debugError("focusSearch didn't work for " + this.activePanel + ": " + e);
+ }
+ },
+};
+
+// :::::::::::::::::::: cookies panel ::::::::::::::::::::
+var gCookies = {
+ tree: null,
+ cookieInfoName: null,
+ cookieInfoValue: null,
+ cookieInfoHostLabel: null,
+ cookieInfoHost: null,
+ cookieInfoPath: null,
+ cookieInfoSendType: null,
+ cookieInfoExpires: null,
+ removeButton: null,
+ blockOnRemove: null,
+
+ cookies: [],
+ displayedCookies: [],
+
+ initialize: function cookies_initialize() {
+ gDataman.debugMsg("Initializing cookies panel");
+ this.tree = document.getElementById("cookiesTree");
+ this.tree.view = this;
+
+ this.cookieInfoName = document.getElementById("cookieInfoName");
+ this.cookieInfoValue = document.getElementById("cookieInfoValue");
+ this.cookieInfoHostLabel = document.getElementById("cookieInfoHostLabel");
+ this.cookieInfoHost = document.getElementById("cookieInfoHost");
+ this.cookieInfoPath = document.getElementById("cookieInfoPath");
+ this.cookieInfoSendType = document.getElementById("cookieInfoSendType");
+ this.cookieInfoExpires = document.getElementById("cookieInfoExpires");
+
+ this.removeButton = document.getElementById("cookieRemove");
+ this.blockOnRemove = document.getElementById("cookieBlockOnRemove");
+
+ // this.loadList() is being called in gDomains.initialize() already
+ this.tree.treeBoxObject.beginUpdateBatch();
+ this.displayedCookies = this.cookies.filter(
+ function (aCookie) {
+ return gDomains.hostMatchesSelected(aCookie.rawHost);
+ });
+ this.sort(null, false, false);
+ this.tree.treeBoxObject.endUpdateBatch();
+ },
+
+ shutdown: function cookies_shutdown() {
+ gDataman.debugMsg("Shutting down cookies panel");
+ this.tree.view.selection.clearSelection();
+ this.tree.view = null;
+ this.displayedCookies = [];
+ },
+
+ loadList: function cookies_loadList() {
+ this.cookies = [];
+ let enumerator = Services.cookies.enumerator;
+ while (enumerator.hasMoreElements()) {
+ let nextCookie = enumerator.getNext();
+ if (!nextCookie) break;
+ nextCookie = nextCookie.QueryInterface(Ci.nsICookie2);
+ this.cookies.push(this._makeCookieObject(nextCookie));
+ }
+ },
+
+ _makeCookieObject: function cookies__makeCookieObject(aCookie) {
+ return {host: aCookie.host,
+ name: aCookie.name,
+ path: aCookie.path,
+ originAttributes: aCookie.originAttributes,
+ value: aCookie.value,
+ isDomain: aCookie.isDomain,
+ rawHost: aCookie.rawHost,
+ displayHost: gLocSvc.idn.convertToDisplayIDN(aCookie.rawHost, {}),
+ isSecure: aCookie.isSecure,
+ isSession: aCookie.isSession,
+ isHttpOnly: aCookie.isHttpOnly,
+ expires: this._getExpiresString(aCookie.expires),
+ expiresSortValue: aCookie.expires};
+ },
+
+ _getObjID: function cookies__getObjID(aIdx) {
+ var curCookie = gCookies.displayedCookies[aIdx];
+ return curCookie.host + "|" + curCookie.path + "|" + curCookie.name;
+ },
+
+ _getExpiresString: function cookies__getExpiresString(aExpires) {
+ if (aExpires) {
+ let date = new Date(1000 * aExpires);
+
+ // If a server manages to set a really long-lived cookie, the dateservice
+ // can't cope with it properly, so we'll just return a blank string.
+ // See bug 238045 for details.
+ let expiry = "";
+ try {
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "full", timeStyle: "long" });
+ expiry = dateTimeFormatter.format(date);
+ }
+ catch (e) {}
+ return expiry;
+ }
+ return gDataman.bundle.getString("cookies.expireAtEndOfSession");
+ },
+
+ select: function cookies_select() {
+ var selections = gDataman.getTreeSelections(this.tree);
+ this.removeButton.disabled = !selections.length;
+ if (!selections.length) {
+ this._clearCookieInfo();
+ return true;
+ }
+
+ if (selections.length > 1) {
+ this._clearCookieInfo();
+ return true;
+ }
+
+ // At this point, we have a single cookie selected.
+ var showCookie = this.displayedCookies[selections[0]];
+
+ this.cookieInfoName.value = showCookie.name;
+ this.cookieInfoValue.value = showCookie.value;
+ this.cookieInfoHostLabel.value = showCookie.isDomain ?
+ this.cookieInfoHostLabel.getAttribute("value_domain") :
+ this.cookieInfoHostLabel.getAttribute("value_host");
+ this.cookieInfoHost.value = showCookie.host;
+ this.cookieInfoPath.value = showCookie.path;
+ var typestringID = "cookies." +
+ (showCookie.isSecure ? "secureOnly" : "anyConnection") +
+ (showCookie.isHttpOnly ? ".httponly" : ".all");
+ this.cookieInfoSendType.value = gDataman.bundle.getString(typestringID);
+ this.cookieInfoExpires.value = showCookie.expires;
+ return true;
+ },
+
+ selectAll: function cookies_selectAll() {
+ this.tree.view.selection.selectAll();
+ },
+
+ _clearCookieInfo: function cookies__clearCookieInfo() {
+ var fields = ["cookieInfoName", "cookieInfoValue", "cookieInfoHost",
+ "cookieInfoPath", "cookieInfoSendType", "cookieInfoExpires"];
+ for (let field of fields) {
+ this[field].value = "";
+ }
+ this.cookieInfoHostLabel.value = this.cookieInfoHostLabel.getAttribute("value_host");
+ },
+
+ handleKeyPress: function cookies_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.delete();
+ }
+ },
+
+ sort: function cookies_sort(aColumn, aUpdateSelection, aInvertDirection) {
+ // Make sure we have a valid column.
+ let column = aColumn;
+ if (!column) {
+ let sortedCol = this.tree.columns.getSortedColumn();
+ if (sortedCol)
+ column = sortedCol.element;
+ else
+ column = document.getElementById("cookieHostCol");
+ }
+ else if (column.localName == "treecols" || column.localName == "splitter")
+ return;
+
+ if (!column || column.localName != "treecol") {
+ Cu.reportError("No column found to sort cookies by");
+ return;
+ }
+
+ let dirAscending = column.getAttribute("sortDirection") !=
+ (aInvertDirection ? "ascending" : "descending");
+ let dirFactor = dirAscending ? 1 : -1;
+
+ // Clear attributes on all columns, we're setting them again after sorting.
+ for (let node = column.parentNode.firstChild; node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // compare function for two formdata items
+ let compfunc = function formdata_sort_compare(aOne, aTwo) {
+ switch (column.id) {
+ case "cookieHostCol":
+ return dirFactor * aOne.displayHost.localeCompare(aTwo.displayHost);
+ case "cookieNameCol":
+ return dirFactor * aOne.name.localeCompare(aTwo.name);
+ case "cookieExpiresCol":
+ return dirFactor * (aOne.expiresSortValue - aTwo.expiresSortValue);
+ }
+ return 0;
+ };
+
+ if (aUpdateSelection) {
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ }
+ this.tree.view.selection.clearSelection();
+
+ // Do the actual sorting of the array.
+ this.displayedCookies.sort(compfunc);
+ this.tree.treeBoxObject.invalidate();
+
+ if (aUpdateSelection) {
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ }
+
+ // Set attributes to the sorting we did.
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", dirAscending ? "ascending" : "descending");
+ },
+
+ delete: function cookies_delete() {
+ var selections = gDataman.getTreeSelections(this.tree);
+
+ if (selections.length > 1) {
+ let title = gDataman.bundle.getString("cookies.deleteSelectedTitle");
+ let msg = gDataman.bundle.getString("cookies.deleteSelected");
+ let flags = ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ let yes = gDataman.bundle.getString("cookies.deleteSelectedYes");
+ if (Services.prompt.confirmEx(window, title, msg, flags, yes, null, null,
+ null, {value: 0}) == 1) // 1=="Cancel" button
+ return;
+ }
+
+ this.tree.view.selection.clearSelection();
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = selections.length - 1; i >= 0; i--) {
+ let delCookie = this.displayedCookies[selections[i]];
+ this.cookies.splice(this.cookies.indexOf(this.displayedCookies[selections[i]]), 1);
+ this.displayedCookies.splice(selections[i], 1);
+ this.tree.treeBoxObject.rowCountChanged(selections[i], -1);
+ Services.cookies.remove(delCookie.host, delCookie.name, delCookie.path,
+ this.blockOnRemove.checked, delCookie.originAttributes);
+ }
+ if (!this.displayedCookies.length)
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasCookies");
+ // Select the entry after the first deleted one or the last of all entries.
+ if (selections.length && this.displayedCookies.length)
+ this.tree.view.selection.toggleSelect(selections[0] < this.displayedCookies.length ?
+ selections[0] :
+ this.displayedCookies.length - 1);
+ },
+
+ updateContext: function cookies_updateContext() {
+ document.getElementById("cookies-context-remove").disabled =
+ this.removeButton.disabled;
+ document.getElementById("cookies-context-selectall").disabled =
+ this.tree.view.selection.count >= this.tree.view.rowCount;
+ },
+
+ reactToChange: function cookies_reactToChange(aSubject, aData) {
+ // aData: added, changed, deleted, batch-deleted, cleared, reload
+ // see http://mxr.mozilla.org/mozilla-central/source/netwerk/cookie/nsICookieService.idl
+ if (aData == "batch-deleted" || aData == "cleared" || aData == "reload") {
+ // Go for re-parsing the whole thing, as cleared and reload need that anyhow
+ // (batch-deleted has an nsIArray of cookies, we could in theory do better there).
+ var selectionCache = [];
+ if (this.displayedCookies.length) {
+ selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ this.displayedCookies = [];
+ }
+ this.loadList();
+ var domainList = [];
+ for (let cookie of this.cookies) {
+ let domain = gDomains.getDomainFromHost(cookie.rawHost);
+ if (!domainList.includes(domain))
+ domainList.push(domain);
+ }
+ gDomains.resetFlagToDomains("hasCookies", domainList);
+ // Restore the local panel display if needed.
+ if (gTabs.activePanel == "cookiesPanel" &&
+ gDomains.selectedDomain.hasCookies) {
+ this.tree.treeBoxObject.beginUpdateBatch();
+ this.displayedCookies = this.cookies.filter(
+ function (aCookie) {
+ return gDomains.hostMatchesSelected(aCookie.rawHost);
+ });
+ this.sort(null, false, false);
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ this.tree.treeBoxObject.endUpdateBatch();
+ }
+ return;
+ }
+
+ // Usual notifications for added, changed, deleted - do "surgical" updates.
+ aSubject.QueryInterface(Ci.nsICookie2);
+ let domain = gDomains.getDomainFromHost(aSubject.rawHost);
+ // Does change affect possibly loaded Cookies pane?
+ let affectsLoaded = this.displayedCookies.length &&
+ gDomains.hostMatchesSelected(aSubject.rawHost);
+ if (aData == "added") {
+ this.cookies.push(this._makeCookieObject(aSubject));
+ if (affectsLoaded) {
+ this.displayedCookies.push(this.cookies[this.cookies.length - 1]);
+ this.tree.treeBoxObject.rowCountChanged(this.cookies.length - 1, 1);
+ this.sort(null, true, false);
+ }
+ else {
+ gDomains.addDomainOrFlag(aSubject.rawHost, "hasCookies");
+ }
+ }
+ else {
+ let idx = -1, disp_idx = -1, domainCookies = 0;
+ if (affectsLoaded) {
+ for (let i = 0; i < this.displayedCookies.length; i++) {
+ let cookie = this.displayedCookies[i];
+ if (cookie.host == aSubject.host && cookie.name == aSubject.name &&
+ cookie.path == aSubject.path) {
+ idx = this.cookies.indexOf(this.displayedCookies[i]);
+ disp_idx = i;
+ break;
+ }
+ }
+ if (aData == "deleted")
+ domainCookies = this.displayedCookies.length;
+ }
+ else {
+ for (let i = 0; i < this.cookies.length; i++) {
+ let cookie = this.cookies[i];
+ if (cookie.host == aSubject.host && cookie.name == aSubject.name &&
+ cookie.path == aSubject.path) {
+ idx = i;
+ if (aData != "deleted")
+ break;
+ }
+ if (aData == "deleted" &&
+ gDomains.getDomainFromHost(cookie.rawHost) == domain)
+ domainCookies++;
+ }
+ }
+ if (idx >= 0) {
+ if (aData == "changed") {
+ this.cookies[idx] = this._makeCookieObject(aSubject);
+ if (affectsLoaded)
+ this.tree.treeBoxObject.invalidateRow(disp_idx);
+ }
+ else if (aData == "deleted") {
+ this.cookies.splice(idx, 1);
+ if (affectsLoaded) {
+ this.displayedCookies.splice(disp_idx, 1);
+ this.tree.treeBoxObject.rowCountChanged(disp_idx, -1);
+ }
+ if (domainCookies == 1)
+ gDomains.removeDomainOrFlag(domain, "hasCookies");
+ }
+ }
+ }
+ },
+
+ forget: function cookies_forget() {
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = this.cookies.length - 1; i >= 0; i--) {
+ if (gDomains.hostMatchesSelected(this.cookies[i].rawHost)) {
+ // Remove from internal list needs to be before actually deleting.
+ let delCookie = this.cookies[i];
+ this.cookies.splice(i, 1);
+ Services.cookies.remove(delCookie.host, delCookie.name,
+ delCookie.path, false);
+ }
+ }
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasCookies");
+ },
+
+ // nsITreeView
+ __proto__: gBaseTreeView,
+ get rowCount() {
+ return this.displayedCookies.length;
+ },
+ getCellText: function(aRow, aColumn) {
+ let cookie = this.displayedCookies[aRow];
+ switch (aColumn.id) {
+ case "cookieHostCol":
+ return cookie.displayHost;
+ case "cookieNameCol":
+ return cookie.name;
+ case "cookieExpiresCol":
+ return cookie.expires;
+ }
+ },
+};
+
+// :::::::::::::::::::: permissions panel ::::::::::::::::::::
+var gPerms = {
+ list: null,
+ listPermission: [],
+
+ initialize: function permissions_initialize() {
+ gDataman.debugMsg("Initializing permissions panel");
+ this.list = document.getElementById("permList");
+ this.addSelBox = document.getElementById("permSelectionBox");
+ this.addHost = document.getElementById("permHost");
+ this.addType = document.getElementById("permType");
+ this.addButton = document.getElementById("permAddButton");
+
+ let enumerator = Services.perms.enumerator;
+
+ while (enumerator.hasMoreElements()) {
+ let nextPermission = enumerator.getNext();
+ nextPermission = nextPermission.QueryInterface(Ci.nsIPermission);
+
+ if (gDomains.hostMatchesSelectedURI(nextPermission.principal.URI)) {
+ let permElem = document.createElement("richlistitem");
+ permElem.setAttribute("type", nextPermission.type);
+ permElem.setAttribute("host", nextPermission.principal.origin);
+ permElem.setAttribute("displayHost", nextPermission.principal.origin);
+ permElem.setAttribute("capability", nextPermission.capability);
+ permElem.setAttribute("class", "permission");
+ gDataman.debugMsg("Adding Origin: " + nextPermission.principal.origin);
+ this.list.appendChild(permElem);
+ this.listPermission.push({id: nextPermission.length,
+ origin: nextPermission.principal.origin,
+ principal: nextPermission.principal,
+ type: nextPermission.type});
+ }
+ }
+ this.list.disabled = !this.list.itemCount;
+ this.addButton.disabled = false;
+ },
+
+ shutdown: function permissions_shutdown() {
+ gDataman.debugMsg("Shutting down permissions panel");
+ // XXX: Here we could detect if we still hold any non-default settings and
+ // trigger the removeDomainOrFlag if not.
+ while (this.list.hasChildNodes())
+ this.list.lastChild.remove();
+
+ this.addSelBox.hidden = true;
+ this.listPermission.length = 0;
+ },
+
+ // Find an item in the permissionsList by origin and type.
+ getPrincipalListItem: function permissions_getPrincipalListItem(aOrigin, aType) {
+
+ gDataman.debugMsg("Getting list item: " + aOrigin + " " + aType);
+
+ for (let elem of this.listPermission) {
+
+ gDataman.debugMsg("elem: " + elem.type);
+
+ // check if this is the one
+ if (elem.type == aType &&
+ elem.origin == aOrigin) {
+ gDataman.debugMsg("Found Element " + elem.origin);
+ return elem;
+ }
+ }
+ return null;
+ },
+
+ // Directly remove a permission.
+ // This function is called when the user checks the 'Use Default' button on the permissions panel.
+ // The item will be removed and the default permissions for the origin will be in place afterwards.
+ // This function will only handle the deletion. The remove will trigger an Observer message.
+ // Because the permission might be removed outside of this panel the code in there needs to clean
+ // up the panel and lists.
+ removeItem: function permissions_removeItem(aOrigin, aType) {
+
+ gDataman.debugMsg("Removing an Item: " + aOrigin + " " + aType);
+
+ let permElem = this.getPrincipalListItem(aOrigin, aType);
+
+ // This happens when we add a new permission.
+ if (permElem == null) {
+ gDataman.debugMsg("Unable to find an Item: " + aOrigin + " " + aType);
+ return;
+ }
+
+ gDataman.debugMsg("Found Element " + permElem.origin);
+
+ // It might be a new element. In this case the principal is null and we do not need to do
+ // anything here. We can not remove the list entry because it might be a new permission the
+ // user wants to change.
+ if (permElem.principal != null) {
+ // Delete the permission. We will deactivate the list item in the subsequent observer message.
+ try {
+ gDataman.debugMsg("Removing permission");
+ Services.perms.removeFromPrincipal(permElem.principal, permElem.type);
+ }
+ catch (e) {
+ gDataman.debugError("Permission could not be removed " +
+ permElem.principal.origin + " " +
+ permElem.principal.type
+ );
+ }
+ }
+ },
+
+ // Directly change a permission.
+ // This function is called when the user changes the value of a permission on the permissions panel.
+ // This function will only handle the update. The update will trigger an Observer message.
+ // Because the permission might be changed outside of this panel the code in there needs to handle
+ // further generic changes.
+ updateItem: function permissions_updateItem(aOrigin, aType, aValue) {
+
+ gDataman.debugMsg("Updating an Item: " + aOrigin + " " + aType + " " + aValue);
+
+ let permElem = this.getPrincipalListItem(aOrigin, aType);
+
+ if (permElem == null) {
+ gDataman.debugMsg("Unable to find an Item: " + aOrigin + " " + aType);
+ return;
+ }
+
+ // If this is a completely new permission we do not have a principal yet.
+ // This happens when we add a new item. We need to create a new permission
+ // from scratch.
+ // Maybe use
+ // principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {})
+ // but this might be undocumented?
+ if (permElem.principal == null)
+ {
+ // This can currently fail for some schemes like 'file://'.
+ // Maybe fix it later if needed.
+ try {
+ let uri = Services.io.newURI(new URL(aOrigin));
+ Services.perms.add(uri, aType, aValue);
+ }
+ catch (e) {
+ gDataman.debugError("New Permission could not be added " +
+ permElem.origin + " " +
+ permElem.type
+ );
+ }
+
+ } else {
+ Services.perms.addFromPrincipal(permElem.principal, permElem.type, aValue);
+ }
+ },
+
+ // Most functions of permissions are in the XBL items!
+ addButtonClick: function permissions_addButtonClick() {
+
+ gDataman.debugMsg("Add permissions button clicked!");
+
+ if (this.addSelBox.hidden) {
+ // Show addition box, disable button.
+ this.addButton.disabled = true;
+ this.addType.removeAllItems(); // Make sure list is clean.
+ let permTypes = ["allowXULXBL", "cookie", "geo", "image", "indexedDB",
+ "install", "login-saving", "object", "offline-app",
+ "popup", "script", "stylesheet",
+ "trackingprotection"];
+
+ // Look for a translation.
+ for (let permType of permTypes) {
+ let typeDesc = permType;
+ try {
+ typeDesc = gDataman.bundle.getString("perm." + permType + ".label");
+ }
+ catch (e) {
+ }
+ let menuitem = this.addType.appendItem(typeDesc, permType);
+ }
+ this.addType.setAttribute("label",
+ gDataman.bundle.getString("perm.type.default"));
+ this.addHost.value =
+ gDomains.selectedDomain.title == "*" ? "" : ("http://www." + gDomains.selectedDomain.title);
+ this.addSelBox.hidden = false;
+ }
+ else {
+ // Let the backend do the validation of the input field.
+ let nOrigin = "";
+
+ try {
+ nOrigin = new URL(this.addHost.value).origin;
+ } catch (e) {
+ // Show an error if URL is invalid.
+ window.alert(gDataman.bundle.getString("perm.validation.invalidurl"));
+ return;
+ }
+
+ // Url could be validated but User did probably enter half valid nonsense
+ // because the origin is undefined.
+ if ((nOrigin == null) || (nOrigin == "")) {
+ window.alert(gDataman.bundle.getString("perm.validation.invalidurl"));
+ return;
+ }
+
+ gDataman.debugMsg("New origin: " + nOrigin);
+
+ // Add entry to list, hide addition box.
+ let permElem = document.createElement("richlistitem");
+ permElem.setAttribute("type", this.addType.value);
+ permElem.setAttribute("host", nOrigin);
+ permElem.setAttribute("displayHost", nOrigin);
+ permElem.setAttribute("capability", this.getDefault(this.addType.value));
+ permElem.setAttribute("class", "permission");
+ this.list.appendChild(permElem);
+ this.list.disabled = false;
+ permElem.useDefault(true);
+ // Add a new entry to the permissions list.
+ // We do not have a principal yet so we use only the origin as identification.
+ this.listPermission.push({id: this.listPermission.length + 1,
+ origin: nOrigin,
+ principal: null,
+ type: this.addType.value});
+
+ this.addSelBox.hidden = true;
+ this.addType.removeAllItems();
+ }
+ },
+
+ addCheck: function permissions_addCheck() {
+ // Only enable button if both fields have (reasonable) values.
+ this.addButton.disabled = !(this.addType.value &&
+ gDomains.getDomainFromHost(this.addHost.value));
+ },
+
+ getDefault: function permissions_getDefault(aType) {
+ switch (aType) {
+ case "allowXULXBL":
+ return Services.perms.DENY_ACTION;
+ case "cookie":
+ if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == 2)
+ return Services.perms.DENY_ACTION;
+ if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") == 2)
+ return Ci.nsICookiePermission.ACCESS_SESSION;
+ return Services.perms.ALLOW_ACTION;
+ case "geo":
+ return Services.perms.DENY_ACTION;
+ case "indexedDB":
+ return Services.perms.DENY_ACTION;
+ case "install":
+ if (Services.prefs.getBoolPref("xpinstall.whitelist.required"))
+ return Services.perms.DENY_ACTION;
+ return Services.perms.ALLOW_ACTION;
+ case "offline-app":
+ try {
+ if (Services.prefs.getBoolPref("offline-apps.allow_by_default"))
+ return Services.perms.ALLOW_ACTION;
+ } catch(e) {
+ // this pref isn't set by default, ignore failures
+ }
+ if (Services.prefs.getBoolPref("browser.offline-apps.notify"))
+ return Services.perms.DENY_ACTION;
+ return Services.perms.UNKNOWN_ACTION;
+ case "popup":
+ if (Services.prefs.getBoolPref("dom.disable_open_during_load"))
+ return Services.perms.DENY_ACTION;
+ return Services.perms.ALLOW_ACTION;
+ case "trackingprotection":
+ return Services.perms.DENY_ACTION;
+ }
+
+ // We are not done yet.
+ // This should only be called for new permission types which have not been
+ // added to the Data Manager yet.
+ try {
+ // Look for an nsContentBlocker permission.
+ switch (Services.prefs.getIntPref("permissions.default." + aType)) {
+ case 3:
+ return NOFOREIGN;
+ case 2:
+ return Services.perms.DENY_ACTION;
+ default:
+ return Services.perms.ALLOW_ACTION;
+ }
+ } catch (e) {
+ return Services.perms.UNKNOWN_ACTION;
+ }
+ },
+
+ reactToChange: function permissions_reactToChange(aSubject, aData) {
+
+ // aData: added, changed, deleted, cleared
+ // aSubject: the subject which is the permission to be changed
+ // See http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsIPermissionManager.idl
+ if (aData == "cleared") {
+ gDataman.debugMsg("something has been cleared but why in permission?");
+ gDomains.resetFlagToDomains("hasPermissions", domainList);
+ return;
+ }
+
+ gDataman.debugMsg("react to change: " + aSubject.principal.origin + " " + aData);
+
+ aSubject.QueryInterface(Ci.nsIPermission);
+
+ let rawHost;
+ let domain;
+
+ if (!gDomains.commonScheme(aSubject.principal.URI.scheme)) {
+ rawHost = "*";
+ domain = "*";
+ }
+ else {
+ rawHost = aSubject.principal.URI.host.replace(/^\./, "");
+ domain = gDomains.getDomainFromHost(rawHost);
+ }
+
+ // Does change affect possibly loaded Preferences pane?
+ let affectsLoaded = this.list && this.list.childElementCount &&
+ gDomains.hostMatchesSelectedURI(aSubject.principal.URI);
+
+ let permElem = null;
+
+ if (affectsLoaded) {
+ for (let lChild of this.list.children) {
+ gDataman.debugMsg("checking type: " + lChild.getAttribute("class") + " " +
+ lChild.getAttribute("type") + " " + aSubject.type);
+
+ // Check type and host (origin) first.
+ if (lChild.getAttribute("type") == aSubject.type &&
+ lChild.getAttribute("host") == aSubject.principal.origin)
+ permElem = lChild;
+ }
+ }
+
+ if (aData == "deleted") {
+ if (affectsLoaded) {
+ permElem.useDefault(true, true);
+ }
+ else {
+ // Only remove if domain is not shown, note that this may leave an empty domain.
+ let haveDomainPerms = false;
+ let enumerator = Services.perms.enumerator;
+ while (enumerator.hasMoreElements()) {
+ let nextPermission = enumerator.getNext();
+ nextPermission = nextPermission.QueryInterface(Ci.nsIPermission);
+
+ let dDomain;
+
+ if (!gDomains.commonScheme(nextPermission.principal.URI.scheme)) {
+ dDomain = "*";
+ }
+ else {
+ dDomain = gDomains.getDomainFromHost(nextPermission.principal.URI.host.replace(/^\./, ""));
+ }
+
+ if (domain == dDomain) {
+ haveDomainPerms = true;
+ break;
+ }
+ }
+ if (!haveDomainPerms)
+ gDomains.removeDomainOrFlag(domain, "hasPermissions");
+ }
+ }
+ else if (aData == "changed" && affectsLoaded) {
+ permElem.setCapability(aSubject.capability, true);
+ }
+ else if (aData == "added") {
+ if (affectsLoaded) {
+ if (permElem) {
+ // Check if them permission list contains the principal.
+ // If not adding it to the permissions list.
+ // This might be the case for newly created items.
+ let permElem2 = this.getPrincipalListItem(aSubject.principal.origin, aSubject.type);
+
+ if (permElem2 != null &&
+ permElem2.principal == null) {
+ permElem2.principal = aSubject.principal;
+ }
+
+ permElem.useDefault(false, true);
+ permElem.setCapability(aSubject.capability, true);
+ }
+ else {
+ gDataman.debugMsg("Adding completely new item: " + aSubject.principal.origin + " " + aSubject.type);
+ permElem = document.createElement("richlistitem");
+ permElem.setAttribute("type", aSubject.type);
+ permElem.setAttribute("host", aSubject.principal.origin);
+ permElem.setAttribute("displayHost", aSubject.principal.origin);
+ permElem.setAttribute("capability", aSubject.capability);
+ permElem.setAttribute("class", "permission");
+ permElem.setAttribute("orient", "vertical");
+ this.list.appendChild(permElem);
+
+ // add an entry to the permissions list
+ this.listPermission.push({id: this.listPermission.length + 1,
+ origin: aSubject.principal.origin,
+ principal: aSubject.principal,
+ type: aSubject.type});
+ }
+ }
+ gDomains.addDomainOrFlag(rawHost, "hasPermissions");
+ }
+
+ this.list.disabled = !this.list.itemCount;
+ },
+
+ // This function is a called when you check that all permissions for the given domain should be
+ // deleted (forget).
+ forget: function permissions_forget() {
+ let delPerms = [];
+ let enumerator = Services.perms.enumerator;
+ while (enumerator.hasMoreElements()) {
+ let nextPermission = enumerator.getNext();
+ nextPermission = nextPermission.QueryInterface(Ci.nsIPermission);
+
+ if (gDomains.hostMatchesSelectedURI(nextPermission.principal.URI)) {
+ delPerms.push({principal: nextPermission.principal, type: nextPermission.type});
+ }
+ }
+
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = delPerms.length - 1; i >= 0; i--) {
+ Services.perms.removeFromPrincipal(delPerms[i].principal, delPerms[i].type);
+ }
+
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasPermissions");
+ },
+};
+
+// :::::::::::::::::::: content prefs panel ::::::::::::::::::::
+var gPrefs = {
+ tree: null,
+ removeButton: null,
+
+ prefs: [],
+
+ initialize: function prefs_initialize() {
+ gDataman.debugMsg("Initializing prefs panel");
+ this.tree = document.getElementById("prefsTree");
+ this.tree.view = this;
+
+ this.removeButton = document.getElementById("prefsRemove");
+
+ // Get all groups (hosts) that match the domain.
+ let domain = gDomains.selectedDomain.title;
+
+ if (domain == "*") {
+ domain = null;
+ }
+
+ let prefs = [];
+ Services.contentPrefs2.getBySubdomain(domain, null, {
+ handleResult(resultPref) {
+ prefs.push(resultPref);
+ },
+ handleCompletion: () => {
+ gPrefs.tree.treeBoxObject.beginUpdateBatch();
+ gPrefs.prefs = [];
+ for (let pref of prefs) {
+ if (!domain) {
+ gPrefs.prefs.push({host: null, displayHost: "", name: pref.name,
+ value: pref.value});
+ }
+ else {
+ let display = gLocSvc.idn.convertToDisplayIDN(pref.domain, {});
+ gPrefs.prefs.push({host: pref.domain, displayHost: display,
+ name: pref.name, value: pref.value});
+ }
+ }
+
+ gPrefs.sort(null, false, false);
+ gPrefs.tree.treeBoxObject.endUpdateBatch();
+ },
+ });
+ },
+
+ shutdown: function prefs_shutdown() {
+ gDataman.debugMsg("Shutting down prefs panel");
+ this.tree.view.selection.clearSelection();
+ this.tree.view = null;
+ this.prefs = [];
+ },
+
+ _getObjID: function prefs__getObjID(aIdx) {
+ var curPref = gPrefs.prefs[aIdx];
+ return curPref.host + "|" + curPref.name;
+ },
+
+ select: function prefs_select() {
+ var selections = gDataman.getTreeSelections(this.tree);
+ this.removeButton.disabled = !selections.length;
+ return true;
+ },
+
+ selectAll: function prefs_selectAll() {
+ this.tree.view.selection.selectAll();
+ },
+
+ handleKeyPress: function prefs_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.delete();
+ }
+ },
+
+ sort: function prefs_sort(aColumn, aUpdateSelection, aInvertDirection) {
+ // Make sure we have a valid column.
+ let column = aColumn;
+ if (!column) {
+ let sortedCol = this.tree.columns.getSortedColumn();
+ if (sortedCol)
+ column = sortedCol.element;
+ else
+ column = document.getElementById("prefsHostCol");
+ }
+ else if (column.localName == "treecols" || column.localName == "splitter")
+ return;
+
+ if (!column || column.localName != "treecol") {
+ Cu.reportError("No column found to sort form data by");
+ return;
+ }
+
+ let dirAscending = column.getAttribute("sortDirection") !=
+ (aInvertDirection ? "ascending" : "descending");
+ let dirFactor = dirAscending ? 1 : -1;
+
+ // Clear attributes on all columns, we're setting them again after sorting.
+ for (let node = column.parentNode.firstChild; node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // compare function for two content prefs
+ let compfunc = function prefs_sort_compare(aOne, aTwo) {
+ switch (column.id) {
+ case "prefsHostCol":
+ return dirFactor * aOne.displayHost.localeCompare(aTwo.displayHost);
+ case "prefsNameCol":
+ return dirFactor * aOne.name.localeCompare(aTwo.name);
+ case "prefsValueCol":
+ return dirFactor * aOne.value.toString().localeCompare(aTwo.value);
+ }
+ return 0;
+ };
+
+ if (aUpdateSelection) {
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ }
+ this.tree.view.selection.clearSelection();
+
+ // Do the actual sorting of the array.
+ this.prefs.sort(compfunc);
+ this.tree.treeBoxObject.invalidate();
+
+ if (aUpdateSelection) {
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ }
+
+ // Set attributes to the sorting we did.
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", dirAscending ? "ascending" : "descending");
+ },
+
+ delete: function prefs_delete() {
+ var selections = gDataman.getTreeSelections(this.tree);
+
+ if (selections.length > 1) {
+ let title = gDataman.bundle.getString("prefs.deleteSelectedTitle");
+ let msg = gDataman.bundle.getString("prefs.deleteSelected");
+ let flags = ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ let yes = gDataman.bundle.getString("prefs.deleteSelectedYes");
+ if (Services.prompt.confirmEx(window, title, msg, flags, yes, null, null,
+ null, {value: 0}) == 1) // 1=="Cancel" button
+ return;
+ }
+
+ this.tree.view.selection.clearSelection();
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = selections.length - 1; i >= 0; i--) {
+ let delPref = this.prefs[selections[i]];
+ this.prefs.splice(selections[i], 1);
+ this.tree.treeBoxObject.rowCountChanged(selections[i], -1);
+ Services.contentPrefs2.removeByDomainAndName(delPref.host, delPref.name, null);
+ }
+ if (!this.prefs.length)
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasPreferences");
+ // Select the entry after the first deleted one or the last of all entries.
+ if (selections.length && this.prefs.length)
+ this.tree.view.selection.toggleSelect(selections[0] < this.prefs.length ?
+ selections[0] :
+ this.prefs.length - 1);
+ },
+
+ updateContext: function prefs_updateContext() {
+ document.getElementById("prefs-context-remove").disabled =
+ this.removeButton.disabled;
+ document.getElementById("prefs-context-selectall").disabled =
+ this.tree.view.selection.count >= this.tree.view.rowCount;
+ },
+
+ reactToChange: function prefs_reactToChange(aSubject, aData) {
+ // aData: prefSet, prefRemoved
+ gDataman.debugMsg("Observed pref change for: " + aSubject.host);
+ // Do "surgical" updates.
+ let domain = gDomains.getDomainFromHostWithCheck(aSubject.host);
+ gDataman.debugMsg("domain: " + domain);
+ // Does change affect possibly loaded Preferences pane?
+ let affectsLoaded = this.prefs.length &&
+ (domain == gDomains.selectedDomain.title);
+
+ let idx = -1, domainPrefs = 0;
+ if (affectsLoaded) {
+ gDataman.debugMsg("affects loaded");
+ for (let i = 0; i < this.prefs.length; i++) {
+ let cpref = this.prefs[i];
+ if (cpref && cpref.host == aSubject.host && cpref.name == aSubject.name) {
+ idx = i;
+ break;
+ }
+ }
+ if (aData == "prefRemoved")
+ domainPrefs = this.prefs.length;
+ }
+ else if (aData == "prefRemoved") {
+ // See if there are any prefs left for that domain.
+ Services.contentPrefs2.hasPrefs(domain != "*" ? domain : null, null, {
+ handleResult(prefResult) {
+ if (!prefResult.value) {
+ gDomains.removeDomainOrFlag(domain, "hasPreferences");
+ }
+ },
+ handleCompletion: () => {
+ },
+ });
+ }
+ if (aData == "prefSet")
+ aSubject.displayHost = gLocSvc.idn.convertToDisplayIDN(aSubject.host, {});
+
+ // Affects loaded domain and is an existing pref.
+ if (idx >= 0) {
+ if (aData == "prefSet") {
+ this.prefs[idx] = aSubject;
+ if (affectsLoaded)
+ this.tree.treeBoxObject.invalidateRow(idx);
+ }
+ else if (aData == "prefRemoved") {
+ this.prefs.splice(idx, 1);
+ if (affectsLoaded) {
+ this.tree.treeBoxObject.rowCountChanged(idx, -1);
+ }
+ if (domainPrefs == 1)
+ gDomains.removeDomainOrFlag(domain, "hasPreferences");
+ }
+ }
+ else if (aData == "prefSet") {
+ // Affects loaded domain but is not an existing pref.
+ // Pref set, no prev index known - either new or existing pref domain.
+ if (affectsLoaded) {
+ this.prefs.push(aSubject);
+ this.tree.treeBoxObject.rowCountChanged(this.prefs.length - 1, 1);
+ this.sort(null, true, false);
+ }
+ // Not the loaded domain but it now has a preference.
+ else {
+ gDomains.addDomainOrFlag(domain, "hasPreferences");
+ }
+ }
+ },
+
+ forget: function prefs_forget() {
+ let callbacks = {
+ handleResult(resultDomain) {
+ },
+ handleCompletion: () => {
+ gDomains.removeDomainOrFlag(domain, "hasPreferences");
+ },
+ };
+
+ let domain = gDomains.selectedDomain.title;
+ if (domain == "*") {
+ Services.contentPrefs2.removeAllGlobals(null, callbacks);
+ }
+ else {
+ Services.contentPrefs2.removeBySubdomain(domain, null, callbacks);
+ }
+ },
+
+ // nsITreeView
+ __proto__: gBaseTreeView,
+ get rowCount() {
+ return this.prefs.length;
+ },
+ getCellText: function(aRow, aColumn) {
+ let cpref = this.prefs[aRow];
+ switch (aColumn.id) {
+ case "prefsHostCol":
+ return cpref.displayHost || "*";
+ case "prefsNameCol":
+ return cpref.name;
+ case "prefsValueCol":
+ return cpref.value;
+ }
+ },
+};
+
+// :::::::::::::::::::: passwords panel ::::::::::::::::::::
+var gPasswords = {
+ tree: null,
+ removeButton: null,
+ toggleButton: null,
+ pwdCol: null,
+
+ allSignons: [],
+ displayedSignons: [],
+ showPasswords: false,
+
+ initialize: function passwords_initialize() {
+ gDataman.debugMsg("Initializing passwords panel");
+ this.tree = document.getElementById("passwordsTree");
+ this.tree.view = this;
+
+ this.removeButton = document.getElementById("pwdRemove");
+ this.toggleButton = document.getElementById("pwdToggle");
+ this.toggleButton.label = gDataman.bundle.getString("pwd.showPasswords");
+ this.toggleButton.accessKey = gDataman.bundle.getString("pwd.showPasswords.accesskey");
+
+ this.pwdCol = document.getElementById("pwdPasswordCol");
+
+ this.tree.treeBoxObject.beginUpdateBatch();
+ // this.loadList() is being called in gDomains.initialize() already
+ this.displayedSignons = this.allSignons.filter(
+ function (aSignon) {
+ return gDomains.hostMatchesSelected(aSignon.hostname);
+ });
+ this.sort(null, false, false);
+ this.tree.treeBoxObject.endUpdateBatch();
+ },
+
+ shutdown: function passwords_shutdown() {
+ gDataman.debugMsg("Shutting down passwords panel");
+ if (this.showPasswords)
+ this.togglePasswordVisible();
+ this.tree.view.selection.clearSelection();
+ this.tree.view = null;
+ this.displayedSignons = [];
+ },
+
+ loadList: function passwords_loadList() {
+ this.allSignons = Services.logins.getAllLogins();
+ },
+
+ _getObjID: function passwords__getObjID(aIdx) {
+ var curSignon = gPasswords.displayedSignons[aIdx];
+ return curSignon.hostname + "|" + curSignon.httpRealm + "|" + curSignon.username;
+ },
+
+ select: function passwords_select() {
+ var selections = gDataman.getTreeSelections(this.tree);
+ this.removeButton.disabled = !selections.length;
+ return true;
+ },
+
+ selectAll: function passwords_selectAll() {
+ this.tree.view.selection.selectAll();
+ },
+
+ handleKeyPress: function passwords_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.delete();
+ }
+ },
+
+ sort: function passwords_sort(aColumn, aUpdateSelection, aInvertDirection) {
+ // Make sure we have a valid column.
+ let column = aColumn;
+ if (!column) {
+ let sortedCol = this.tree.columns.getSortedColumn();
+ if (sortedCol)
+ column = sortedCol.element;
+ else
+ column = document.getElementById("pwdHostCol");
+ }
+ else if (column.localName == "treecols" || column.localName == "splitter")
+ return;
+
+ if (!column || column.localName != "treecol") {
+ Cu.reportError("No column found to sort form data by");
+ return;
+ }
+
+ let dirAscending = column.getAttribute("sortDirection") !=
+ (aInvertDirection ? "ascending" : "descending");
+ let dirFactor = dirAscending ? 1 : -1;
+
+ // Clear attributes on all columns, we're setting them again after sorting.
+ for (let node = column.parentNode.firstChild; node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // compare function for two signons
+ let compfunc = function passwords_sort_compare(aOne, aTwo) {
+ switch (column.id) {
+ case "pwdHostCol":
+ return dirFactor * aOne.hostname.localeCompare(aTwo.hostname);
+ case "pwdUserCol":
+ return dirFactor * aOne.username.localeCompare(aTwo.username);
+ case "pwdPasswordCol":
+ return dirFactor * aOne.password.localeCompare(aTwo.password);
+ }
+ return 0;
+ };
+
+ if (aUpdateSelection) {
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ }
+ this.tree.view.selection.clearSelection();
+
+ // Do the actual sorting of the array.
+ this.displayedSignons.sort(compfunc);
+ this.tree.treeBoxObject.invalidate();
+
+ if (aUpdateSelection) {
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ }
+
+ // Set attributes to the sorting we did.
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", dirAscending ? "ascending" : "descending");
+ },
+
+ delete: function passwords_delete() {
+ var selections = gDataman.getTreeSelections(this.tree);
+
+ if (selections.length > 1) {
+ let title = gDataman.bundle.getString("pwd.deleteSelectedTitle");
+ let msg = gDataman.bundle.getString("pwd.deleteSelected");
+ let flags = ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ let yes = gDataman.bundle.getString("pwd.deleteSelectedYes");
+ if (Services.prompt.confirmEx(window, title, msg, flags, yes, null, null,
+ null, {value: 0}) == 1) // 1=="Cancel" button
+ return;
+ }
+
+ this.tree.view.selection.clearSelection();
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = selections.length - 1; i >= 0; i--) {
+ let delSignon = this.displayedSignons[selections[i]];
+ this.allSignons.splice(this.allSignons.indexOf(this.displayedSignons[selections[i]]), 1);
+ this.displayedSignons.splice(selections[i], 1);
+ this.tree.treeBoxObject.rowCountChanged(selections[i], -1);
+ Services.logins.removeLogin(delSignon);
+ }
+ if (!this.displayedSignons.length)
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasPasswords");
+ // Select the entry after the first deleted one or the last of all entries.
+ if (selections.length && this.displayedSignons.length)
+ this.tree.view.selection.toggleSelect(selections[0] < this.displayedSignons.length ?
+ selections[0] :
+ this.displayedSignons.length - 1);
+ },
+
+ togglePasswordVisible: function passwords_togglePasswordVisible() {
+ if (this.showPasswords || this._confirmShowPasswords()) {
+ this.showPasswords = !this.showPasswords;
+ this.toggleButton.label = gDataman.bundle.getString(this.showPasswords ?
+ "pwd.hidePasswords" :
+ "pwd.showPasswords");
+ this.toggleButton.accessKey = gDataman.bundle.getString(this.showPasswords ?
+ "pwd.hidePasswords.accesskey" :
+ "pwd.showPasswords.accesskey");
+ this.pwdCol.hidden = !this.showPasswords;
+ }
+ },
+
+ _confirmShowPasswords: function passwords__confirmShowPasswords() {
+ // This doesn't harm if passwords are not encrypted.
+ let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .createInstance(Ci.nsIPK11TokenDB);
+ let token = tokendb.getInternalKeyToken();
+
+ // If there is no master password, still give the user a chance to opt-out
+ // of displaying passwords
+ if (token.checkPassword(""))
+ return this._askUserShowPasswords();
+
+ // So there's a master password. But since checkPassword didn't succeed,
+ // we're logged out (per nsIPK11Token.idl).
+ try {
+ // Relogin and ask for the master password.
+ token.login(true); // 'true' means always prompt for token password. User
+ // will be prompted until clicking 'Cancel' or
+ // entering the correct password.
+ }
+ catch (e) {
+ // An exception will be thrown if the user cancels the login prompt dialog.
+ // User is also logged out of Software Security Device.
+ }
+
+ return token.isLoggedIn();
+ },
+
+ _askUserShowPasswords: function passwords__askUserShowPasswords() {
+ // Confirm the user wants to display passwords.
+ return Services.prompt.confirmEx(window, null,
+ gDataman.bundle.getString("pwd.noMasterPasswordPrompt"),
+ Services.prompt.STD_YES_NO_BUTTONS,
+ null, null, null, null, { value: false }) == 0; // 0=="Yes" button
+ },
+
+ updateContext: function passwords_updateContext() {
+ document.getElementById("pwd-context-remove").disabled =
+ this.removeButton.disabled;
+ document.getElementById("pwd-context-copypassword").disabled =
+ this.tree.view.selection.count != 1;
+ document.getElementById("pwd-context-selectall").disabled =
+ this.tree.view.selection.count >= this.tree.view.rowCount;
+ },
+
+ copySelPassword: function passwords_copySelPassword() {
+ // Copy selected signon's password to clipboard.
+ let row = this.tree.currentIndex;
+ let password = gPasswords.displayedSignons[row].password;
+ gLocSvc.clipboard.copyString(password);
+ },
+
+ copyPassword: function passwords_copyPassword() {
+ // Prompt for the master password upfront.
+ let token = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB)
+ .getInternalKeyToken();
+
+ if (this.showPasswords || token.checkPassword(""))
+ this.copySelPassword();
+ else {
+ try {
+ token.login(true);
+ this.copySelPassword();
+ } catch (ex) {
+ // If user cancels an exception is expected.
+ }
+ }
+ },
+
+ reactToChange: function passwords_reactToChange(aSubject, aData) {
+
+ // Not interested in legacy hostsaving changes here.
+ // They will be handled in perm-changed.
+ if (/^hostSaving/.test(aData))
+ return;
+
+ // aData: addLogin, modifyLogin, removeLogin, removeAllLogins
+ if (aData == "removeAllLogins") {
+ // Go for re-parsing the whole thing.
+ if (this.displayedSignons.length) {
+ this.tree.treeBoxObject.beginUpdateBatch();
+ this.tree.view.selection.clearSelection();
+ this.displayedSignons = [];
+ this.tree.treeBoxObject.endUpdateBatch();
+ }
+ this.loadList();
+ let domainList = [];
+ for (let lSignon of this.allSignons) {
+ let domain = gDomains.getDomainFromHost(lSignon.hostname);
+ if (!domainList.includes(domain))
+ domainList.push(domain);
+ }
+ gDomains.resetFlagToDomains("hasPasswords", domainList);
+ return;
+ }
+
+ // Usual notifications for addLogin, modifyLogin, removeLogin - do "surgical" updates.
+ let curLogin = null, oldLogin = null;
+ if (aData == "modifyLogin" &&
+ aSubject instanceof Ci.nsIArray) {
+ let enumerator = aSubject.enumerate();
+ if (enumerator.hasMoreElements()) {
+ oldLogin = enumerator.getNext();
+ oldLogin.QueryInterface(Ci.nsILoginInfo);
+ }
+ if (enumerator.hasMoreElements()) {
+ curLogin = enumerator.getNext();
+ curLogin.QueryInterface(Ci.nsILoginInfo);
+ }
+ }
+ else if (aSubject instanceof Ci.nsILoginInfo) {
+ curLogin = aSubject; oldLogin = aSubject;
+ }
+ else {
+ Cu.reportError("Observed an unrecognized signon change of type " + aData);
+ }
+
+ let domain = gDomains.getDomainFromHost(curLogin.hostname);
+ // Does change affect possibly loaded Passwords pane?
+ let affectsLoaded = this.displayedSignons.length &&
+ gDomains.hostMatchesSelected(curLogin.hostname);
+ if (aData == "addLogin") {
+ this.allSignons.push(curLogin);
+
+ if (affectsLoaded) {
+ this.displayedSignons.push(this.allSignons[this.allSignons.length - 1]);
+ this.tree.treeBoxObject.rowCountChanged(this.allSignons.length - 1, 1);
+ this.sort(null, true, false);
+ }
+ else {
+ gDomains.addDomainOrFlag(curLogin.hostname, "hasPasswords");
+ }
+ }
+ else {
+ let idx = -1, disp_idx = -1, domainPasswords = 0;
+ if (affectsLoaded) {
+ for (let i = 0; i < this.displayedSignons.length; i++) {
+ let signon = this.displayedSignons[i];
+ if (signon && signon.equals(oldLogin)) {
+ idx = this.allSignons.indexOf(this.displayedSignons[i]);
+ disp_idx = i;
+ break;
+ }
+ }
+ if (aData == "removeLogin")
+ domainPasswords = this.displayedSignons.length;
+ }
+ else {
+ for (let i = 0; i < this.allSignons.length; i++) {
+ let signon = this.allSignons[i];
+ if (signon && signon.equals(oldLogin)) {
+ idx = i;
+ if (aData != "removeLogin")
+ break;
+ }
+ if (aData == "removeLogin" &&
+ gDomains.getDomainFromHost(signon.hostname) == domain)
+ domainPasswords++;
+ }
+ }
+ if (idx >= 0) {
+ if (aData == "modifyLogin") {
+ this.allSignons[idx] = curLogin;
+ if (affectsLoaded)
+ this.tree.treeBoxObject.invalidateRow(disp_idx);
+ }
+ else if (aData == "removeLogin") {
+ this.allSignons.splice(idx, 1);
+ if (affectsLoaded) {
+ this.displayedSignons.splice(disp_idx, 1);
+ this.tree.treeBoxObject.rowCountChanged(disp_idx, -1);
+ }
+ if (domainPasswords == 1)
+ gDomains.removeDomainOrFlag(domain, "hasPasswords");
+ }
+ }
+ }
+ },
+
+ forget: function passwords_forget() {
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = this.allSignons.length - 1; i >= 0; i--) {
+ if (gDomains.hostMatchesSelected(this.allSignons[i].hostname)) {
+ // Remove from internal list needs to be before actually deleting.
+ let delSignon = this.allSignons[i];
+ this.allSignons.splice(i, 1);
+ Services.logins.removeLogin(delSignon);
+ }
+ }
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasPasswords");
+ },
+
+ // nsITreeView
+ __proto__: gBaseTreeView,
+ get rowCount() {
+ return this.displayedSignons.length;
+ },
+ getCellText: function(aRow, aColumn) {
+ let signon = this.displayedSignons[aRow];
+ switch (aColumn.id) {
+ case "pwdHostCol":
+ return signon.httpRealm ?
+ (signon.hostname + " (" + signon.httpRealm + ")") :
+ signon.hostname;
+ case "pwdUserCol":
+ return signon.username || "";
+ case "pwdPasswordCol":
+ return signon.password || "";
+ }
+ },
+};
+
+// :::::::::::::::::::: web storage panel ::::::::::::::::::::
+var gStorage = {
+ tree: null,
+ removeButton: null,
+
+ storages: [],
+ displayedStorages: [],
+
+ initialize: function storage_initialize() {
+ gDataman.debugMsg("Initializing storage panel");
+ this.tree = document.getElementById("storageTree");
+ this.tree.view = this;
+
+ this.removeButton = document.getElementById("storageRemove");
+
+ this.tree.treeBoxObject.beginUpdateBatch();
+ // this.loadList() is being called in gDomains.initialize() already
+ this.displayedStorages = this.storages.filter(
+ function (aStorage) {
+ return gDomains.hostMatchesSelected(aStorage.rawHost);
+ });
+ this.sort(null, false, false);
+ this.tree.treeBoxObject.endUpdateBatch();
+ },
+
+ shutdown: function storage_shutdown() {
+ gDataman.debugMsg("Shutting down storage panel");
+ this.tree.view.selection.clearSelection();
+ this.tree.view = null;
+ this.displayedStorages = [];
+ },
+
+ loadList: function storage_loadList() {
+ this.storages = [];
+
+ // Load appCache entries.
+ let groups = gLocSvc.appcache.getGroups();
+ gDataman.debugMsg("Loading " + groups.length + " appcache entries");
+ for (let lGroup of groups) {
+ let uri = Services.io.newURI(lGroup);
+ let cache = gLocSvc.appcache.getActiveCache(lGroup);
+ this.storages.push({host: uri.host,
+ rawHost: uri.host,
+ type: "appCache",
+ size: cache.usage,
+ groupID: lGroup});
+ }
+
+ // Load DOM storage entries, unfortunately need to go to the DB. :(
+ // Bug 343163 would make this easier and clean.
+ let domstorelist = [];
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append("webappsstore.sqlite");
+ if (file.exists()) {
+ var connection = Services.storage.openDatabase(file);
+ try {
+ if (connection.tableExists("webappsstore2")) {
+ var statement =
+ connection.createStatement("SELECT scope, key FROM webappsstore2");
+ while (statement.executeStep())
+ domstorelist.push({scope: statement.getString(0),
+ key: statement.getString(1)});
+ statement.reset();
+ statement.finalize();
+ }
+ } finally {
+ connection.close();
+ }
+ }
+ gDataman.debugMsg("Loading " + domstorelist.length + " DOM Storage entries");
+ // Scopes are reversed, e.g. |moc.elgoog.www.:http:80| (for localStorage).
+ for (let i = 0; i < domstorelist.length; i++) {
+ // Get the host from the reversed scope.
+ let scopeparts = domstorelist[i].scope.split(":");
+ let host = "", type = "unknown";
+ let origHost = scopeparts[0].split("").reverse().join("");
+ let rawHost = host = origHost.replace(/^\./, "");
+ if (scopeparts.length > 1) {
+ // This is a localStore, [1] is protocol, [2] is port.
+ type = "localStorage";
+ host = scopeparts[1].length ? scopeparts[1] + "://" + host : host;
+ // Add port if it's not the default for this protocol.
+ if (scopeparts[2] &&
+ !((scopeparts[1] == "http" && scopeparts[2] == 80) ||
+ (scopeparts[1] == "https" && scopeparts[2] == 443))) {
+ host = host + ":" + scopeparts[2];
+ }
+ }
+ // Make sure we only add known/supported types
+ if (type != "unknown") {
+ // Merge entries for one scope into a single entry if possible.
+ let scopefound = false;
+ for (let j = 0; j < this.storages.length; j++) {
+ if (this.storages[j].type == type && this.storages[j].host == host) {
+ this.storages[j].keys.push(domstorelist[i].key);
+ scopefound = true;
+ break;
+ }
+ }
+ if (!scopefound) {
+ this.storages.push({host: host,
+ rawHost: rawHost,
+ type: type,
+ // FIXME if you want getUsage no longer exists
+ // But I think it's not worth it. Seems the only way
+ // to do this is to get all the key names and values
+ // and add the string lengths together
+ // size: gLocSvc.domstoremgr.getUsage(rawHost),
+ size: 0,
+ origHost: origHost,
+ keys: [domstorelist[i].key]});
+ }
+ }
+ }
+
+ // Load indexedDB entries, unfortunately need to read directory for now. :(
+ // Bug 630858 would make this easier and clean.
+ let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ dir.append("indexedDB");
+ if (dir.exists() && dir.isDirectory()) {
+ // Enumerate subdir entries, names are like "http+++davidflanagan.com" or
+ // "https+++mochi.test+8888", and filter out the domain name and protocol
+ // from that.
+ // gLocSvc.idxdbmgr is usable as soon as we have a URI.
+ let files = dir.directoryEntries
+ .QueryInterface(Ci.nsIDirectoryEnumerator);
+ gDataman.debugMsg("Loading IndexedDB entries");
+
+ while (files.hasMoreElements()) {
+ let file = files.nextFile;
+ // Convert directory name to a URI.
+ let host = file.leafName.replace(/\+\+\+/, "://").replace(/\+(\d+)$/, ":$1");
+ let uri = Services.io.newURI(host);
+ this.storages.push({host: host,
+ rawHost: uri.host,
+ type: "indexedDB",
+ size: 0,
+ path: file.path});
+ // Get IndexedDB usage (DB size)
+ // See http://mxr.mozilla.org/mozilla-central/source/dom/indexedDB/nsIIndexedDatabaseManager.idl?mark=39-52#39
+ gLocSvc.idxdbmgr.getUsageForURI(uri,
+ function(aUri, aUsage) {
+ gStorage.storages.forEach(function(aElement) {
+ if (aUri.host == aElement.rawHost)
+ aElement.size = aUsage;
+ });
+ });
+ }
+ }
+ },
+
+ _getObjID: function storage__getObjID(aIdx) {
+ var curStorage = gStorage.displayedStorages[aIdx];
+ return curStorage.host + "|" + curStorage.type;
+ },
+
+ select: function storage_select() {
+ var selections = gDataman.getTreeSelections(this.tree);
+ this.removeButton.disabled = !selections.length;
+ return true;
+ },
+
+ selectAll: function storage_selectAll() {
+ this.tree.view.selection.selectAll();
+ },
+
+ handleKeyPress: function storage_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.delete();
+ }
+ },
+
+ sort: function storage_sort(aColumn, aUpdateSelection, aInvertDirection) {
+ // Make sure we have a valid column.
+ let column = aColumn;
+ if (!column) {
+ let sortedCol = this.tree.columns.getSortedColumn();
+ if (sortedCol)
+ column = sortedCol.element;
+ else
+ column = document.getElementById("storageHostCol");
+ }
+ else if (column.localName == "treecols" || column.localName == "splitter")
+ return;
+
+ if (!column || column.localName != "treecol") {
+ Cu.reportError("No column found to sort form data by");
+ return;
+ }
+
+ let dirAscending = column.getAttribute("sortDirection") !=
+ (aInvertDirection ? "ascending" : "descending");
+ let dirFactor = dirAscending ? 1 : -1;
+
+ // Clear attributes on all columns, we're setting them again after sorting.
+ for (let node = column.parentNode.firstChild; node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // compare function for two content prefs
+ let compfunc = function storage_sort_compare(aOne, aTwo) {
+ switch (column.id) {
+ case "storageHostCol":
+ return dirFactor * aOne.host.localeCompare(aTwo.host);
+ case "storageTypeCol":
+ return dirFactor * aOne.type.localeCompare(aTwo.type);
+ }
+ return 0;
+ };
+
+ if (aUpdateSelection) {
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ }
+ this.tree.view.selection.clearSelection();
+
+ // Do the actual sorting of the array.
+ this.displayedStorages.sort(compfunc);
+ this.tree.treeBoxObject.invalidate();
+
+ if (aUpdateSelection) {
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ }
+
+ // Set attributes to the sorting we did.
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", dirAscending ? "ascending" : "descending");
+ },
+
+ delete: function storage_delete() {
+ var selections = gDataman.getTreeSelections(this.tree);
+
+ if (selections.length > 1) {
+ let title = gDataman.bundle.getString("storage.deleteSelectedTitle");
+ let msg = gDataman.bundle.getString("storage.deleteSelected");
+ let flags = ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ let yes = gDataman.bundle.getString("storage.deleteSelectedYes");
+ if (Services.prompt.confirmEx(window, title, msg, flags, yes, null, null,
+ null, {value: 0}) == 1) // 1=="Cancel" button
+ return;
+ }
+
+ this.tree.view.selection.clearSelection();
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = selections.length - 1; i >= 0; i--) {
+ let delStorage = this.displayedStorages[selections[i]];
+ this.storages.splice(
+ this.storages.indexOf(this.displayedStorages[selections[i]]), 1);
+ this.displayedStorages.splice(selections[i], 1);
+ this.tree.treeBoxObject.rowCountChanged(selections[i], -1);
+ // Remove the actual entry.
+ this._deleteItem(delStorage);
+ }
+ if (!this.displayedStorages.length)
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasStorage");
+ // Select the entry after the first deleted one or the last of all entries.
+ if (selections.length && this.displayedStorages.length)
+ this.tree.view.selection.toggleSelect(selections[0] < this.displayedStorages.length ?
+ selections[0] :
+ this.displayedStorages.length - 1);
+ },
+
+ _deleteItem: function storage__deleteItem(aStorageItem) {
+ switch (aStorageItem.type) {
+ case "appCache":
+ gLocSvc.appcache.getActiveCache(aStorageItem.groupID).discard();
+ break;
+ case "localStorage":
+ let testHost = aStorageItem.host;
+ if (!/:/.test(testHost))
+ testHost = "http://" + testHost;
+ let uri = Services.io.newURI(testHost);
+ let principal = gLocSvc.ssm.createCodebasePrincipal(uri, {});
+ let storage = gLocSvc.domstoremgr.createStorage(null, principal, "");
+ storage.clear();
+ break;
+ case "indexedDB":
+ gLocSvc.idxdbmgr.clearDatabasesForURI(
+ Services.io.newURI(aStorageItem.host));
+ break;
+ }
+ },
+
+ updateContext: function storage_updateContext() {
+ document.getElementById("storage-context-remove").disabled =
+ this.removeButton.disabled;
+ document.getElementById("storage-context-selectall").disabled =
+ this.tree.view.selection.count >= this.tree.view.rowCount;
+ },
+
+ reloadList: function storage_reloadList() {
+ // As many storage types don't have app-wide functions to notify us of
+ // changes, call this one periodically to completely redo the storage
+ // list and so keep the Data Manager up to date.
+ var selectionCache = [];
+ if (this.displayedStorages.length) {
+ selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ this.displayedStorages = [];
+ }
+ this.loadList();
+ var domainList = [];
+ for (let lStorage of this.storages) {
+ let domain = gDomains.getDomainFromHost(lStorage.rawHost);
+ if (!domainList.includes(domain))
+ domainList.push(domain);
+ }
+ gDomains.resetFlagToDomains("hasStorage", domainList);
+ // Restore the local panel display if needed.
+ if (gTabs.activePanel == "storagePanel" &&
+ gDomains.selectedDomain.hasStorage) {
+ this.tree.treeBoxObject.beginUpdateBatch();
+ this.displayedStorages = this.storages.filter(
+ function (aStorage) {
+ return gDomains.hostMatchesSelected(aStorage.rawHost);
+ });
+ this.sort(null, false, false);
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ this.tree.treeBoxObject.endUpdateBatch();
+ }
+ },
+
+ reactToChange: function storage_reactToChange(aSubject, aData) {
+ // aData: null (sessionStorage, localStorage) + nsIDOMStorageEvent in aSubject
+ // --- for appCache and indexedDB, no change notifications are known!
+ // --- because of that, we don't do anything here and instead use
+ // reloadList periodically
+ // session storage also comes here, but currently not supported
+ // aData: null, all data in aSubject
+ // see https://developer.mozilla.org/en/DOM/Event/StorageEvent
+ switch (aData) {
+ case "localStorage":
+ case "sessionStorage":
+ break;
+ default:
+ Cu.reportError("Observed an unrecognized storage change of type " + aData);
+ }
+
+ gDataman.debugMsg("Found storage event for: " + aData);
+ },
+
+ forget: function storage_forget() {
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = this.storages.length - 1; i >= 0; i--) {
+ if (gDomains.hostMatchesSelected(this.storages[i].hostname)) {
+ // Remove from internal list should be before actually deleting.
+ let delStorage = this.storages[i];
+ this.storages.splice(i, 1);
+ this._deleteItem(delStorage);
+ }
+ }
+ gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasStorage");
+ },
+
+ // nsITreeView
+ __proto__: gBaseTreeView,
+ get rowCount() {
+ return this.displayedStorages.length;
+ },
+ getCellText: function(aRow, aColumn) {
+ let storage = this.displayedStorages[aRow];
+ switch (aColumn.id) {
+ case "storageHostCol":
+ return storage.host;
+ case "storageTypeCol":
+ return storage.type;
+ case "storageSizeCol":
+ return gDataman.bundle.getFormattedString("storageUsage",
+ DownloadUtils.convertByteUnits(storage.size));
+ }
+ },
+};
+
+// :::::::::::::::::::: form data panel ::::::::::::::::::::
+var gFormdata = {
+ tree: null,
+ removeButton: null,
+ searchfield: null,
+
+ formdata: [],
+ displayedFormdata: [],
+
+ initialize: function formdata_initialize() {
+ gDataman.debugMsg("Initializing form data panel");
+ this.tree = document.getElementById("formdataTree");
+ this.tree.view = this;
+
+ this.searchfield = document.getElementById("fdataSearch");
+ this.removeButton = document.getElementById("fdataRemove");
+
+ // Always load fresh list, no need to react to changes when pane not open.
+ this.loadList();
+ this.search("");
+ },
+
+ shutdown: function formdata_shutdown() {
+ gDataman.debugMsg("Shutting down form data panel");
+ this.tree.view.selection.clearSelection();
+ this.tree.view = null;
+ this.displayedFormdata = [];
+ },
+
+ _promiseLoadFormHistory: function formdata_promiseLoadFormHistory() {
+ return new Promise(resolve => {
+ let callbacks = {
+ handleResult(result) {
+ gFormdata.formdata.push({fieldname: result.fieldname,
+ value: result.value,
+ timesUsed: result.timesUsed,
+ firstUsed: gFormdata._getTimeString(result.firstUsed),
+ firstUsedSortValue: result.firstUsed,
+ lastUsed: gFormdata._getTimeString(result.lastUsed),
+ lastUsedSortValue: result.lastUsed,
+ guid: result.guid});
+ },
+ handleError(aError) {
+ Cu.reportError(aError);
+ },
+ handleCompletion(aReason) {
+ // This needs to stay in or Async.promiseSpinningly will fail.
+ resolve();
+ }
+ };
+ gLocSvc.FormHistory.search(["fieldname", "value", "timesUsed", "firstUsed", "lastUsed", "guid"],
+ {},
+ callbacks);
+ });
+ },
+
+ loadList: function formdata_loadList() {
+ this.formdata = [];
+ // Use Async.promiseSpinningly to Sync the call.
+ Async.promiseSpinningly(this._promiseLoadFormHistory());
+ },
+
+ _getTimeString: function formdata__getTimeString(aTimestamp) {
+ if (aTimestamp) {
+ let date = new Date(aTimestamp / 1000);
+
+ // If a date has an extreme value, the dateservice can't cope with it
+ // properly, so we'll just return a blank string.
+ // See bug 238045 for details.
+ let dtString = "";
+ try {
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "full", timeStyle: "long" });
+ dtString = dateTimeFormatter.format(date);
+ }
+ catch (e) {}
+ return dtString;
+ }
+ return "";
+ },
+
+ _getObjID: function formdata__getObjID(aIdx) {
+ return gFormdata.displayedFormdata[aIdx].guid;
+ },
+
+ select: function formdata_select() {
+ var selections = gDataman.getTreeSelections(this.tree);
+ this.removeButton.disabled = !selections.length;
+ return true;
+ },
+
+ selectAll: function formdata_selectAll() {
+ this.tree.view.selection.selectAll();
+ },
+
+ handleKeyPress: function formdata_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.delete();
+ }
+ },
+
+ sort: function formdata_sort(aColumn, aUpdateSelection, aInvertDirection) {
+ // Make sure we have a valid column.
+ let column = aColumn;
+ if (!column) {
+ let sortedCol = this.tree.columns.getSortedColumn();
+ if (sortedCol)
+ column = sortedCol.element;
+ else
+ column = document.getElementById("fdataFieldCol");
+ }
+ else if (column.localName == "treecols" || column.localName == "splitter")
+ return;
+
+ if (!column || column.localName != "treecol") {
+ Cu.reportError("No column found to sort form data by");
+ return;
+ }
+
+ let dirAscending = column.getAttribute("sortDirection") !=
+ (aInvertDirection ? "ascending" : "descending");
+ let dirFactor = dirAscending ? 1 : -1;
+
+ // Clear attributes on all columns, we're setting them again after sorting.
+ for (let node = column.parentNode.firstChild; node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // compare function for two formdata items
+ let compfunc = function formdata_sort_compare(aOne, aTwo) {
+ switch (column.id) {
+ case "fdataFieldCol":
+ return dirFactor * aOne.fieldname.localeCompare(aTwo.fieldname);
+ case "fdataValueCol":
+ return dirFactor * aOne.value.localeCompare(aTwo.value);
+ case "fdataCountCol":
+ return dirFactor * (aOne.timesUsed - aTwo.timesUsed);
+ case "fdataFirstCol":
+ return dirFactor * (aOne.firstUsedSortValue - aTwo.firstUsedSortValue);
+ case "fdataLastCol":
+ return dirFactor * (aOne.lastUsedSortValue - aTwo.lastUsedSortValue);
+ }
+ return 0;
+ };
+
+ if (aUpdateSelection) {
+ var selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ }
+ this.tree.view.selection.clearSelection();
+
+ // Do the actual sorting of the array.
+ this.displayedFormdata.sort(compfunc);
+ this.tree.treeBoxObject.invalidate();
+
+ if (aUpdateSelection) {
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ }
+
+ // Set attributes to the sorting we did.
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", dirAscending ? "ascending" : "descending");
+ },
+
+ delete: function formdata_delete() {
+ var selections = gDataman.getTreeSelections(this.tree);
+
+ if (selections.length > 1) {
+ let title = gDataman.bundle.getString("fdata.deleteSelectedTitle");
+ let msg = gDataman.bundle.getString("fdata.deleteSelected");
+ let flags = ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ let yes = gDataman.bundle.getString("fdata.deleteSelectedYes");
+ if (Services.prompt.confirmEx(window, title, msg, flags, yes, null, null,
+ null, {value: 0}) == 1) // 1=="Cancel" button
+ return;
+ }
+
+ this.tree.view.selection.clearSelection();
+ // Loop backwards so later indexes in the list don't change.
+ for (let i = selections.length - 1; i >= 0; i--) {
+ let delFData = this.displayedFormdata[selections[i]];
+ this.formdata.splice(this.formdata.indexOf(this.displayedFormdata[selections[i]]), 1);
+ this.displayedFormdata.splice(selections[i], 1);
+ this.tree.treeBoxObject.rowCountChanged(selections[i], -1);
+ let changes = [{op: "remove",
+ fieldname: delFData.fieldname,
+ value: delFData.value}];
+ // Async call but we don't care about the completion just now and remove the entry from the panel.
+ // If the call fails the entry will just reappear the next time the form panel is opened.
+ gLocSvc.FormHistory.update(changes);
+ }
+ // Select the entry after the first deleted one or the last of all entries.
+ if (selections.length && this.displayedFormdata.length)
+ this.tree.view.selection.toggleSelect(selections[0] < this.displayedFormdata.length ?
+ selections[0] :
+ this.displayedFormdata.length - 1);
+ },
+
+ search: function formdata_search(aSearchString) {
+ let selectionCache = gDataman.getSelectedIDs(this.tree, this._getObjID);
+ this.tree.treeBoxObject.beginUpdateBatch();
+ this.tree.view.selection.clearSelection();
+ var lcSearch = aSearchString.toLocaleLowerCase();
+ this.displayedFormdata = this.formdata.filter(
+ function(aFd) {
+ return aFd.fieldname.toLocaleLowerCase().includes(lcSearch) ||
+ aFd.value.toLocaleLowerCase().includes(lcSearch);
+ });
+ this.sort(null, false, false);
+ gDataman.restoreSelectionFromIDs(this.tree, this._getObjID, selectionCache);
+ this.tree.treeBoxObject.endUpdateBatch();
+ },
+
+ focusSearch: function formdata_focusSearch() {
+ this.searchfield.focus();
+ },
+
+ updateContext: function formdata_updateContext() {
+ document.getElementById("fdata-context-remove").disabled =
+ this.removeButton.disabled;
+ document.getElementById("fdata-context-selectall").disabled =
+ this.tree.view.selection.count >= this.tree.view.rowCount;
+ },
+
+ /**
+ * _promiseReadFormHistory
+ *
+ * Retrieves the formddata from the data for the given guid.
+ *
+ * @param aGuid guid for which form data should be returned.
+ * @return Promise<null if no row is found with the specified guid,
+ * or an object containing the row full content values>
+ */
+ _promiseReadFormHistory: function formdata_promiseReadFormHistory(aGuid) {
+
+ return new Promise((resolve, reject) => {
+ var entry = null;
+ let callbacks = {
+ handleResult(result) {
+ // There can be only one entry for a given guid.
+ // If there are more we will not behead it but instead
+ // only keep the last returned result.
+ entry = result;
+ },
+ handleError(aError) {
+ Cu.reportError(aError);
+ reject(error);
+ },
+ handleCompletion(aReason) {
+ resolve(entry);
+ }
+ };
+
+ gLocSvc.FormHistory.search(["fieldname", "value", "timesUsed", "firstUsed", "lastUsed", "guid"],
+ {guid :aGuid},
+ callbacks);
+ });
+ },
+
+ // Updates the form data panel when receiving a notification.
+ //
+ // The notification type is passed in aData.
+ //
+ // The following types are supported:
+ // formhistory-add formhistory-update formhistory-remove
+ // formhistory-expireoldentries
+ //
+ // The following types will be ignored:
+ // formhistory-shutdown formhistory-beforeexpireoldentries
+ reactToChange: function formdata_reactToChange(aSubject, aData) {
+
+ // Ignore changes when no form data pane is loaded
+ // or if we caught an unsupported notification.
+ if (!this.displayedFormdata.length ||
+ aData == "formhistory-shutdown" ||
+ aData == "formhistory-beforeexpireoldentries") {
+ return;
+ }
+
+ if (aData == "formhistory-expireoldentries") {
+ // Go for re-parsing the whole thing.
+ this.tree.treeBoxObject.beginUpdateBatch();
+ this.tree.view.selection.clearSelection();
+ this.displayedFormdata = [];
+ this.tree.treeBoxObject.endUpdateBatch();
+
+ this.loadList();
+ this.search("");
+ return;
+ }
+
+ if (aData != "formhistory-add" && aData != "formhistory-change" &&
+ aData != "formhistory-remove") {
+ Cu.reportError("Observed an unrecognized formdata change of type " + aData);
+ return;
+ }
+
+ var cGuid = null;
+
+ if (aSubject instanceof Ci.nsISupportsString) {
+ cGuid = aSubject.toString();
+ }
+
+ if (!cGuid) {
+ // See bug 1346850. Remove has a problem and always sends a null guid.
+ // We just let the panel stay the same which might cause minor problems
+ // because there is no longer a notification when removing all entries.
+ if (aData != "formhistory-remove") {
+ Cu.reportError("FormHistory guid is null for " + aData);
+ }
+ return;
+ }
+
+ var entryData = null;
+
+ if (aData == "formhistory-add" || aData == "formhistory-change") {
+ // Use Async.promiseSpinningly to Sync the call.
+ Async.promiseSpinningly(this._promiseReadFormHistory(cGuid).then(entry => {
+ if (entry) {
+ entryData = entry;
+ }
+ return;
+ }));
+
+ if (!entryData) {
+ Cu.reportError("Could not find added/modifed formdata entry");
+ return;
+ }
+ }
+
+ if (aData == "formhistory-add") {
+ this.formdata.push(entryData);
+ this.displayedFormdata.push(this.formdata[this.formdata.length - 1]);
+ this.tree.treeBoxObject.rowCountChanged(this.formdata.length - 1, 1);
+ this.search("");
+ }
+ else {
+ let idx = -1, disp_idx = -1;
+ for (let i = 0; i < this.displayedFormdata.length; i++) {
+ let fdata = this.displayedFormdata[i];
+ if (fdata && fdata.guid == cGuid) {
+ idx = this.formdata.indexOf(this.displayedFormdata[i]);
+ disp_idx = i;
+ break;
+ }
+ }
+ if (idx >= 0) {
+ if (aData == "formhistory-change") {
+ this.formdata[idx] = entryData;
+ this.tree.treeBoxObject.invalidateRow(disp_idx);
+ }
+ else if (aData == "formhistory-remove") {
+ this.formdata.splice(idx, 1);
+ this.displayedFormdata.splice(disp_idx, 1);
+ this.tree.treeBoxObject.rowCountChanged(disp_idx, -1);
+ }
+ }
+ }
+ },
+
+ forget: function formdata_forget() {
+ gLocSvc.FormHistory.update({ op: "remove" });
+ },
+
+ // nsITreeView
+ __proto__: gBaseTreeView,
+ get rowCount() {
+ return this.displayedFormdata.length;
+ },
+ getCellText: function(aRow, aColumn) {
+ let fdata = this.displayedFormdata[aRow];
+ switch (aColumn.id) {
+ case "fdataFieldCol":
+ return fdata.fieldname;
+ case "fdataValueCol":
+ return fdata.value;
+ case "fdataCountCol":
+ return fdata.timesUsed;
+ case "fdataFirstCol":
+ return fdata.firstUsed;
+ case "fdataLastCol":
+ return fdata.lastUsed;
+ }
+ },
+};
+
+// :::::::::::::::::::: forget panel ::::::::::::::::::::
+var gForget = {
+ forgetDesc: null,
+ forgetCookies: null,
+ forgetPermissions: null,
+ forgetPreferences: null,
+ forgetPasswords: null,
+ forgetStorage: null,
+ forgetFormdata: null,
+ forgetCookiesLabel: null,
+ forgetPermissionsLabel: null,
+ forgetPreferencesLabel: null,
+ forgetPasswordsLabel: null,
+ forgetStorageLabel: null,
+ forgetFormdataLabel: null,
+ forgetButton: null,
+
+ initialize: function forget_initialize() {
+ gDataman.debugMsg("Initializing forget panel");
+
+ this.forgetDesc = document.getElementById("forgetDesc");
+ ["forgetCookies", "forgetPermissions", "forgetPreferences",
+ "forgetPasswords", "forgetStorage", "forgetFormdata"]
+ .forEach(function(elemID) {
+ gForget[elemID] = document.getElementById(elemID);
+ gForget[elemID].hidden = false;
+ gForget[elemID].checked = false;
+ let labelID = elemID + "Label";
+ gForget[labelID] = document.getElementById(labelID);
+ gForget[labelID].hidden = true;
+ });
+ this.forgetButton = document.getElementById("forgetButton");
+ this.forgetButton.hidden = false;
+
+ if (gDomains.selectedDomain.title == "*")
+ this.forgetDesc.value = gDataman.bundle.getString("forget.desc.global.pre");
+ else
+ this.forgetDesc.value = gDataman.bundle.getFormattedString("forget.desc.domain.pre",
+ [gDomains.selectedDomain.title]);
+
+ this.forgetCookies.disabled = !gDomains.selectedDomain.hasCookies;
+ this.forgetPermissions.disabled = !gDomains.selectedDomain.hasPermissions;
+ this.forgetPreferences.disabled = !gDomains.selectedDomain.hasPreferences;
+ this.forgetPasswords.disabled = !gDomains.selectedDomain.hasPasswords;
+ this.forgetStorage.disabled = !gDomains.selectedDomain.hasStorage;
+ this.forgetFormdata.disabled = !gDomains.selectedDomain.hasFormData;
+ this.forgetFormdata.hidden = !gDomains.selectedDomain.hasFormData;
+ this.updateOptions();
+ },
+
+ shutdown: function forget_shutdown() {
+ gDataman.debugMsg("Shutting down forget panel");
+ },
+
+ updateOptions: function forget_updateOptions() {
+ this.forgetButton.disabled = !(this.forgetCookies.checked ||
+ this.forgetPermissions.checked ||
+ this.forgetPreferences.checked ||
+ this.forgetPasswords.checked ||
+ this.forgetStorage.checked ||
+ this.forgetFormdata.checked);
+ },
+
+ handleKeyPress: function forget_handleKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) {
+ // Make sure we do something that makes this panel go away.
+ if (gDomains.selectedDomain.title)
+ gDomains.select();
+ else
+ gDomains.tree.view.selection.select(0);
+ }
+ },
+
+ forget: function forget_forget() {
+ // Domain might get removed and selected domain changed!
+ let delDomainTitle = gDomains.selectedDomain.title;
+
+ if (this.forgetCookies.checked) {
+ gCookies.forget();
+ this.forgetCookiesLabel.hidden = false;
+ }
+ this.forgetCookies.hidden = true;
+
+ if (this.forgetPermissions.checked) {
+ gPerms.forget();
+ this.forgetPermissionsLabel.hidden = false;
+ }
+ this.forgetPermissions.hidden = true;
+
+ if (this.forgetPreferences.checked) {
+ gPrefs.forget();
+ this.forgetPreferencesLabel.hidden = false;
+ }
+ this.forgetPreferences.hidden = true;
+
+ if (this.forgetPasswords.checked) {
+ gPasswords.forget();
+ this.forgetPasswordsLabel.hidden = false;
+ }
+ this.forgetPasswords.hidden = true;
+
+ if (this.forgetStorage.checked) {
+ gStorage.forget();
+ this.forgetStorageLabel.hidden = false;
+ }
+ this.forgetStorage.hidden = true;
+
+ if (this.forgetFormdata.checked) {
+ gFormdata.forget();
+ this.forgetFormdataLabel.hidden = false;
+ }
+ this.forgetFormdata.hidden = true;
+
+ if (delDomainTitle == "*")
+ this.forgetDesc.value = gDataman.bundle.getString("forget.desc.global.post");
+ else
+ this.forgetDesc.value = gDataman.bundle.getFormattedString("forget.desc.domain.post",
+ [delDomainTitle]);
+ this.forgetButton.hidden = true;
+ },
+};
diff --git a/comm/suite/components/dataman/content/dataman.xml b/comm/suite/components/dataman/content/dataman.xml
new file mode 100644
index 0000000000..de90cac68d
--- /dev/null
+++ b/comm/suite/components/dataman/content/dataman.xml
@@ -0,0 +1,249 @@
+<?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 bindings [
+<!ENTITY % datamanDTD SYSTEM "chrome://communicator/locale/dataman/dataman.dtd">
+%datamanDTD;
+]>
+
+<bindings id="datamanBindings"
+ 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="perm-base-item"
+ extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <implementation>
+ <constructor><![CDATA[
+ var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+ var permLabel = this.type;
+ try {
+ permLabel = gDataman.bundle.getString("perm." + this.type + ".label");
+ }
+ catch (e) {
+ }
+ this.setAttribute("label", permLabel);
+ this._updateRadio();
+ ]]></constructor>
+
+ <property name="capability">
+ <getter><![CDATA[
+ if (this.hasAttribute("capability"))
+ return this.getAttribute("capability");
+ return -1;
+ ]]></getter>
+ <setter><![CDATA[
+ this.setAttribute("capability", val);
+ this._updateRadio();
+ ]]></setter>
+ </property>
+
+ <property name="host" readonly="true"
+ onget="return this.getAttribute('host');"/>
+
+ <property name="type" readonly="true"
+ onget="return this.getAttribute('type');"/>
+
+ <method name="_updateRadio">
+ <body><![CDATA[
+ let radio = document.getAnonymousElementByAttribute(this, "anonid",
+ "permSetting-" + this.capability);
+ if (radio)
+ radio.radioGroup.selectedItem = radio;
+ else {
+ let radioGroup = document.getAnonymousElementByAttribute(this, "anonid",
+ "radioGroup");
+ radioGroup.selectedIndex = -1;
+ }
+ ]]></body>
+ </method>
+
+ <method name="useDefault">
+ <parameter name="aChecked"/>
+ <parameter name="aUIUpdateOnly"/>
+ <body><![CDATA[
+ let checkbox = document.getAnonymousElementByAttribute(this, "anonid",
+ "useDefault");
+ if (checkbox.checked != aChecked)
+ checkbox.checked = aChecked;
+ let radioGroup = document.getAnonymousElementByAttribute(this, "anonid",
+ "radioGroup");
+ radioGroup.disabled = aChecked;
+ if (aChecked) {
+ if (!aUIUpdateOnly)
+ gPerms.removeItem(this.host, this.type);
+
+ this.capability = gPerms.getDefault(this.type);
+ }
+ this._updateRadio();
+ ]]></body>
+ </method>
+
+ <method name="setCapability">
+ <parameter name="aValue"/>
+ <parameter name="aUIUpdateOnly"/>
+ <body><![CDATA[
+ this.capability = aValue;
+ let radio = document.getAnonymousElementByAttribute(this, "anonid",
+ "permSetting-" + aValue);
+ if (radio && !radio.selected)
+ radio.radioGroup.selectedItem = radio;
+ if (!aUIUpdateOnly)
+ gPerms.updateItem(this.host, this.type, aValue);
+ ]]></body>
+ </method>
+
+ <method name="handleKeyPress">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ this.useDefault(true);
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress" phase="capturing"
+ action="return this.handleKeyPress(event);"/>
+ </handlers>
+ </binding>
+
+ <binding id="perm-generic-item"
+ extends="chrome://communicator/content/dataman/dataman.xml#perm-base-item">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="permHost" class="hostLabel" xbl:inherits="value=displayHost"/>
+ <xul:label anonid="permLabel" class="permissionLabel" xbl:inherits="value=label" control="radioGroup"/>
+ </xul:hbox>
+ <xul:hbox role="group" aria-labelledby="permLabel">
+ <xul:checkbox class="indent" anonid="useDefault" label="&perm.UseDefault;"
+ oncommand="document.getBindingParent(this).useDefault(this.checked);"/>
+ <xul:spacer flex="1"/>
+ <xul:radiogroup anonid="radioGroup" orient="horizontal">
+ <xul:radio anonid="permSetting-1" label="&perm.Allow;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.ALLOW_ACTION);"/>
+ <xul:radio anonid="permSetting-2" label="&perm.Block;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.DENY_ACTION);"/>
+ </xul:radiogroup>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="perm-cookie-item"
+ extends="chrome://communicator/content/dataman/dataman.xml#perm-base-item">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="permHost" class="hostLabel" xbl:inherits="value=displayHost"/>
+ <xul:label anonid="permLabel" class="permissionLabel" xbl:inherits="value=label" control="radioGroup"/>
+ </xul:hbox>
+ <xul:hbox role="group" aria-labelledby="permLabel">
+ <xul:checkbox class="indent" anonid="useDefault" label="&perm.UseDefault;"
+ oncommand="document.getBindingParent(this).useDefault(this.checked);"/>
+ <xul:spacer flex="1"/>
+ <xul:radiogroup anonid="radioGroup" orient="horizontal">
+ <xul:radio anonid="permSetting-1" label="&perm.Allow;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.ALLOW_ACTION);"/>
+ <xul:radio anonid="permSetting-8" label="&perm.AllowSession;"
+ oncommand="document.getBindingParent(this).setCapability(Ci.nsICookiePermission.ACCESS_SESSION);"/>
+ <xul:radio anonid="permSetting-2" label="&perm.Block;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.DENY_ACTION);"/>
+ </xul:radiogroup>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="perm-geo-item"
+ extends="chrome://communicator/content/dataman/dataman.xml#perm-base-item">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="permHost" class="hostLabel" xbl:inherits="value=displayHost"/>
+ <xul:label anonid="permLabel" class="permissionLabel" xbl:inherits="value=label" control="radioGroup"/>
+ </xul:hbox>
+ <xul:hbox role="group" aria-labelledby="permLabel">
+ <xul:checkbox class="indent" anonid="useDefault" label="&perm.AskAlways;"
+ oncommand="document.getBindingParent(this).useDefault(this.checked);"/>
+ <xul:spacer flex="1"/>
+ <xul:radiogroup anonid="radioGroup" orient="horizontal">
+ <xul:radio anonid="permSetting-1" label="&perm.Allow;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.ALLOW_ACTION);"/>
+ <xul:radio anonid="permSetting-2" label="&perm.Block;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.DENY_ACTION);"/>
+ </xul:radiogroup>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="perm-password-item" extends="chrome://communicator/content/dataman/dataman.xml#perm-base-item">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="permHost" class="hostLabel" xbl:inherits="value=displayHost"/>
+ <xul:label anonid="permLabel" class="permissionLabel" xbl:inherits="value=label" control="radioGroup"/>
+ </xul:hbox>
+ <xul:hbox role="group" aria-labelledby="permLabel">
+ <xul:checkbox class="indent" anonid="useDefault" hidden="true"/>
+ <xul:spacer flex="1"/>
+ <xul:radiogroup anonid="radioGroup" orient="horizontal">
+ <xul:radio anonid="permSetting-1" label="&perm.AskAlways;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.ALLOW_ACTION);"/>
+ <xul:radio anonid="permSetting-2" label="&perm.NeverSave;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.DENY_ACTION);"/>
+ </xul:radiogroup>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="useDefault">
+ <parameter name="aChecked"/>
+ <body><![CDATA[
+ // just for compat, makes it easier to generically "delete" perms
+ if (aChecked)
+ this.setCapability(Services.perms.ALLOW_ACTION);
+ ]]></body>
+ </method>
+
+ <method name="setCapability">
+ <parameter name="aValue"/>
+ <parameter name="aUIUpdateOnly"/>
+ <body><![CDATA[
+ this.capability = aValue;
+ let radio = document.getAnonymousElementByAttribute(this, "anonid",
+ "permSetting-" + aValue);
+ if (radio && !radio.selected)
+ radio.radioGroup.selectedItem = radio;
+ if (!aUIUpdateOnly)
+ Services.logins.setLoginSavingEnabled(this.host, aValue == Services.perms.ALLOW_ACTION);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="perm-content-item"
+ extends="chrome://communicator/content/dataman/dataman.xml#perm-base-item">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="permHost" class="hostLabel" xbl:inherits="value=displayHost"/>
+ <xul:label anonid="permLabel" class="permissionLabel" xbl:inherits="value=label" control="radioGroup"/>
+ </xul:hbox>
+ <xul:hbox role="group" aria-labelledby="permLabel">
+ <xul:checkbox class="indent" anonid="useDefault" label="&perm.UseDefault;"
+ oncommand="document.getBindingParent(this).useDefault(this.checked);"/>
+ <xul:spacer flex="1"/>
+ <xul:radiogroup anonid="radioGroup" orient="horizontal">
+ <xul:radio anonid="permSetting-1" label="&perm.Allow;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.ALLOW_ACTION);"/>
+ <xul:radio anonid="permSetting-3" label="&perm.AllowSameDomain;"
+ oncommand="document.getBindingParent(this).setCapability(NOFOREIGN);"/>
+ <xul:radio anonid="permSetting-2" label="&perm.Block;"
+ oncommand="document.getBindingParent(this).setCapability(Services.perms.DENY_ACTION);"/>
+ </xul:radiogroup>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ </bindings>
diff --git a/comm/suite/components/dataman/content/dataman.xul b/comm/suite/components/dataman/content/dataman.xul
new file mode 100644
index 0000000000..633119f566
--- /dev/null
+++ b/comm/suite/components/dataman/content/dataman.xul
@@ -0,0 +1,571 @@
+<?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://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/dataman/dataman.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/dataman/dataman.css" type="text/css"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE page [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % datamanDTD SYSTEM "chrome://communicator/locale/dataman/dataman.dtd">
+%datamanDTD;
+]>
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xhtml="http://www.w3.org/1999/xhtml"
+ id="dataman-page" title="&dataman.windowTitle;"
+ windowtype="data:manager"
+ onload="gDataman.initialize();"
+ onunload="gDataman.shutdown();"
+ onkeypress="gDataman.handleKeyPress(event);"
+ persist="screenX screenY width height sizemode">
+
+ <xhtml:link rel="shortcut icon"
+ href="chrome://communicator/skin/dataman/datamanIcon-16.png"/>
+
+ <script src="chrome://communicator/content/dataman/dataman.js"/>
+
+ <stringbundleset id="datamanBundleSet">
+ <stringbundle id="datamanBundle"
+ src="chrome://communicator/locale/dataman/dataman.properties"/>
+ </stringbundleset>
+
+ <commandset id="datamanCommands">
+ <command id="cmd_selectAll"
+ oncommand="gTabs.selectAll();"/>
+ <command id="cmd_search_domain"
+ oncommand="gDomains.focusSearch();"/>
+ <command id="cmd_search_data"
+ oncommand="gTabs.focusSearch();"/>
+ <command id="cmd_close"
+ oncommand="window.close();"/>
+ </commandset>
+
+ <keyset id="datamanKeys">
+ <key id="key_close"/>
+ <key id="key_selectAll"/>
+ <key id="key_search_domain"
+ command="cmd_search_domain"
+ key="&domain.search.key;"
+ modifiers="accel"/>
+ <key id="key_search_data"
+ command="cmd_search_data"
+ key="&data.search.key;"
+ modifiers="accel"/>
+ </keyset>
+
+ <popupset id="datamanContextSet">
+ <menupopup id="domainTreeContextMenu"
+ onpopupshowing="gDomains.updateContext();">
+ <menuitem id="domain-context-forget"
+ label_domain="&domain.ctx.forgetdomain.label;"
+ accesskey_domain="&domain.ctx.forgetdomain.accesskey;"
+ label_global="&domain.ctx.forgetglobal.label;"
+ accesskey_global="&domain.ctx.forgetglobal.accesskey;"
+ oncommand="gDomains.forget();"/>
+ </menupopup>
+
+ <menupopup id="cookiesTreeContextMenu"
+ onpopupshowing="gCookies.updateContext();">
+ <menuitem id="cookies-context-remove"
+ label="&cookies.ctx.remove.label;"
+ accesskey="&cookies.ctx.remove.accesskey;"
+ oncommand="gCookies.delete();"/>
+ <menuitem id="cookies-context-selectall"
+ label="&cookies.ctx.selectAll.label;"
+ accesskey="&cookies.ctx.selectAll.accesskey;"
+ oncommand="gCookies.selectAll();"/>
+ </menupopup>
+
+ <menupopup id="prefsTreeContextMenu"
+ onpopupshowing="gPrefs.updateContext();">
+ <menuitem id="prefs-context-remove"
+ label="&prefs.ctx.remove.label;"
+ accesskey="&prefs.ctx.remove.accesskey;"
+ oncommand="gPrefs.delete();"/>
+ <menuitem id="prefs-context-selectall"
+ label="&prefs.ctx.selectAll.label;"
+ accesskey="&prefs.ctx.selectAll.accesskey;"
+ oncommand="gPrefs.selectAll();"/>
+ </menupopup>
+
+ <menupopup id="passwordsTreeContextMenu"
+ onpopupshowing="gPasswords.updateContext();">
+ <menuitem id="pwd-context-remove"
+ label="&pwd.ctx.remove.label;"
+ accesskey="&pwd.ctx.remove.accesskey;"
+ oncommand="gPasswords.delete();"/>
+ <menuitem id="pwd-context-copypassword"
+ label="&pwd.ctx.copyPasswordCmd.label;"
+ accesskey="&pwd.ctx.copyPasswordCmd.accesskey;"
+ oncommand="gPasswords.copyPassword();"/>
+ <menuitem id="pwd-context-selectall"
+ label="&pwd.ctx.selectAll.label;"
+ accesskey="&pwd.ctx.selectAll.accesskey;"
+ oncommand="gPasswords.selectAll();"/>
+ </menupopup>
+
+ <menupopup id="storageTreeContextMenu"
+ onpopupshowing="gStorage.updateContext();">
+ <menuitem id="storage-context-remove"
+ label="&storage.ctx.remove.label;"
+ accesskey="&storage.ctx.remove.accesskey;"
+ oncommand="gStorage.delete();"/>
+ <menuitem id="storage-context-selectall"
+ label="&storage.ctx.selectAll.label;"
+ accesskey="&storage.ctx.selectAll.accesskey;"
+ oncommand="gStorage.selectAll();"/>
+ </menupopup>
+
+ <menupopup id="formdataTreeContextMenu"
+ onpopupshowing="gFormdata.updateContext();">
+ <menuitem id="fdata-context-remove"
+ label="&fdata.ctx.remove.label;"
+ accesskey="&fdata.ctx.remove.accesskey;"
+ oncommand="gFormdata.delete();"/>
+ <menuitem id="fdata-context-selectall"
+ label="&fdata.ctx.selectAll.label;"
+ accesskey="&fdata.ctx.selectAll.accesskey;"
+ oncommand="gFormdata.selectAll();"/>
+ </menupopup>
+ </popupset>
+
+ <hbox flex="1">
+ <vbox flex="1">
+ <menulist id="typeSelect"
+ oncommand="gDomains.selectType(this.value);">
+ <menupopup>
+ <menuitem label="&select.all.label;"
+ value="all"/>
+ <menuitem label="&select.cookies.label;"
+ value="Cookies"/>
+ <menuitem label="&select.permissions.label;"
+ value="Permissions"/>
+ <menuitem label="&select.preferences.label;"
+ value="Preferences"/>
+ <menuitem label="&select.passwords.label;"
+ value="Passwords"/>
+ <menuitem label="&select.storage.label;"
+ value="Storage"/>
+ </menupopup>
+ </menulist>
+ <textbox id="domainSearch"
+ clickSelectsAll="true"
+ type="search"
+ aria-controls="domainTree"
+ class="compact"
+ placeholder="&domain.search.placeholder;"
+ oncommand="gDomains.search(this.value);"/>
+ <tree id="domainTree"
+ seltype="single"
+ flex="1"
+ hidecolumnpicker="true"
+ onkeypress="gDomains.handleKeyPress(event);"
+ onselect="gDomains.select();"
+ context="domainTreeContextMenu">
+ <treecols>
+ <treecol id="domainCol"
+ label="&domain.tree.domain.label;"
+ flex="1"
+ sortDirection="ascending"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ </vbox>
+
+ <splitter/>
+
+ <tabbox id="tabbox" flex="6">
+ <tabs onselect="gTabs.select();">
+ <tab id="cookiesTab"
+ label="&tab.cookies.label;"
+ disabled="true"/>
+ <tab id="permissionsTab"
+ label="&tab.permissions.label;"
+ disabled="true"/>
+ <tab id="preferencesTab"
+ label="&tab.preferences.label;"
+ disabled="true"/>
+ <tab id="passwordsTab"
+ label="&tab.passwords.label;"
+ disabled="true"/>
+ <tab id="storageTab"
+ label="&tab.storage.label;"
+ disabled="true"/>
+ <tab id="formdataTab"
+ label="&tab.formdata.label;"
+ disabled="true"/>
+ <tab id="forgetTab"
+ label="&tab.forget.label;"
+ hidden="true"
+ onkeypress="gForget.handleKeyPress(event);"/>
+ </tabs>
+
+ <tabpanels id="tabpanels" flex="1">
+ <vbox id="cookiesPanel">
+ <description>&cookies.description;</description>
+ <tree id="cookiesTree"
+ flex="1"
+ onkeypress="gCookies.handleKeyPress(event);"
+ onselect="gCookies.select();"
+ context="cookiesTreeContextMenu">
+ <treecols onclick="gCookies.sort(event.target, true, true);">
+ <treecol id="cookieHostCol"
+ label="&cookies.tree.host.label;"
+ flex="5"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="cookieNameCol"
+ label="&cookies.tree.name.label;"
+ flex="5"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="cookieExpiresCol"
+ label="&cookies.tree.expires.label;"
+ flex="10"
+ hidden="true"
+ persist="width hidden"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <groupbox>
+ <caption label="&cookies.infobox.label;"/>
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+
+ <rows>
+ <row align="center">
+ <hbox pack="end">
+ <label value="&cookies.info.name.label;"
+ control="cookieInfoName"/>
+ </hbox>
+ <textbox id="cookieInfoName" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox pack="end">
+ <label value="&cookies.info.value.label;"
+ control="cookieInfoValue"/>
+ </hbox>
+ <textbox id="cookieInfoValue" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox pack="end">
+ <label id="cookieInfoHostLabel"
+ value="&cookies.info.host.label;"
+ value_host="&cookies.info.host.label;"
+ value_domain="&cookies.info.domain.label;"
+ control="cookieInfoHost"/>
+ </hbox>
+ <textbox id="cookieInfoHost" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox pack="end">
+ <label value="&cookies.info.path.label;"
+ control="cookieInfoPath"/>
+ </hbox>
+ <textbox id="cookieInfoPath" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox pack="end">
+ <label value="&cookies.info.sendtype.label;"
+ control="cookieInfoSendType"/>
+ </hbox>
+ <textbox id="cookieInfoSendType" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox pack="end">
+ <label value="&cookies.info.expires.label;"
+ control="cookieInfoExpires"/>
+ </hbox>
+ <textbox id="cookieInfoExpires" readonly="true" class="plain"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <hbox id="cookieButtons" align="center">
+ <button id="cookieRemove"
+ label="&cookies.button.remove.label;"
+ accesskey="&cookies.button.remove.accesskey;"
+ disabled="true"
+ oncommand="gCookies.delete();"/>
+ <checkbox id="cookieBlockOnRemove"
+ label="&cookies.blockOnRemove.label;"
+ accesskey="&cookies.blockOnRemove.accesskey;"
+ flex="1"
+ persist="checked"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="permissionsPanel">
+ <richlistbox id="permList" flex="1"
+ onkeypress="if (this.selectedItem)
+ this.selectedItem.handleKeyPress(event);"/>
+
+ <hbox id="permAddBox" align="center">
+ <hbox id="permSelectionBox" align="center" hidden="true">
+ <textbox id="permHost"
+ clickSelectsAll="true"
+ class="compact"
+ placeholder="&perm.host.placeholder;"
+ oninput="gPerms.addCheck();"/>
+ <menulist id="permType"
+ oncommand="gPerms.addCheck();"/>
+ </hbox>
+ <button id="permAddButton"
+ label="&perm.button.add.label;"
+ accesskey="&perm.button.add.accesskey;"
+ oncommand="gPerms.addButtonClick();"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="preferencesPanel">
+ <description>&prefs.description;</description>
+ <tree id="prefsTree"
+ flex="1"
+ onkeypress="gPrefs.handleKeyPress(event);"
+ onselect="gPrefs.select();"
+ context="prefsTreeContextMenu">
+ <treecols onclick="gPrefs.sort(event.target, true, true);">
+ <treecol id="prefsHostCol"
+ label="&prefs.tree.host.label;"
+ flex="5"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="prefsNameCol"
+ label="&prefs.tree.name.label;"
+ flex="5"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="prefsValueCol"
+ label="&prefs.tree.value.label;"
+ flex="10"
+ persist="width hidden"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox id="prefsButtons">
+ <button id="prefsRemove"
+ label="&prefs.button.remove.label;"
+ accesskey="&prefs.button.remove.accesskey;"
+ disabled="true"
+ oncommand="gPrefs.delete();"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="passwordsPanel">
+ <description>&pwd.description;</description>
+ <tree id="passwordsTree"
+ flex="1"
+ hidecolumnpicker="true"
+ onkeypress="gPasswords.handleKeyPress(event);"
+ onselect="gPasswords.select();"
+ context="passwordsTreeContextMenu">
+ <treecols onclick="gPasswords.sort(event.target, true, true);">
+ <treecol id="pwdHostCol"
+ label="&pwd.tree.host.label;"
+ flex="5"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="pwdUserCol"
+ label="&pwd.tree.username.label;"
+ flex="2"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="pwdPasswordCol"
+ label="&pwd.tree.password.label;"
+ flex="2"
+ hidden="true"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox id="passwordButtons">
+ <button id="pwdRemove"
+ label="&pwd.button.remove.label;"
+ accesskey="&pwd.button.remove.accesskey;"
+ disabled="true"
+ oncommand="gPasswords.delete();"/>
+ <spacer flex="1"/>
+ <button id="pwdToggle"
+ oncommand="gPasswords.togglePasswordVisible();"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="storagePanel">
+ <description>&storage.description;</description>
+ <tree id="storageTree"
+ flex="1"
+ hidecolumnpicker="true"
+ onkeypress="gStorage.handleKeyPress(event);"
+ onselect="gStorage.select();"
+ context="storageTreeContextMenu">
+ <treecols onclick="gStorage.sort(event.target, true, true);">
+ <treecol id="storageHostCol"
+ label="&storage.tree.host.label;"
+ flex="5"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="storageTypeCol"
+ label="&storage.tree.type.label;"
+ flex="2"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="storageSizeCol"
+ label="&storage.tree.size.label;"
+ flex="2"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox id="storageButtons">
+ <button id="storageRemove"
+ label="&storage.button.remove.label;"
+ accesskey="&storage.button.remove.accesskey;"
+ disabled="true"
+ oncommand="gStorage.delete();"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="formdataPanel">
+ <textbox id="fdataSearch"
+ clickSelectsAll="true"
+ type="search"
+ aria-controls="formdataTree"
+ class="compact"
+ placeholder="&fdata.search.placeholder;"
+ oncommand="gFormdata.search(this.value);"/>
+ <tree id="formdataTree"
+ flex="1"
+ onkeypress="gFormdata.handleKeyPress(event);"
+ onselect="gFormdata.select();"
+ context="formdataTreeContextMenu">
+ <treecols onclick="gFormdata.sort(event.target, true, true);">
+ <treecol id="fdataFieldCol"
+ label="&fdata.tree.fieldname.label;"
+ flex="5"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="fdataValueCol"
+ label="&fdata.tree.value.label;"
+ flex="10"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="fdataCountCol"
+ label="&fdata.tree.usecount.label;"
+ flex="2"
+ hidden="true"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="fdataFirstCol"
+ label="&fdata.tree.firstused.label;"
+ flex="10"
+ hidden="true"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="fdataLastCol"
+ label="&fdata.tree.lastused.label;"
+ flex="10"
+ hidden="true"
+ persist="width hidden"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox id="formdataButtons">
+ <button id="fdataRemove"
+ label="&fdata.button.remove.label;"
+ accesskey="&fdata.button.remove.accesskey;"
+ disabled="true"
+ oncommand="gFormdata.delete();"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="forgetPanel">
+ <description id="forgetDesc"/>
+ <hbox>
+ <checkbox id="forgetCookies"
+ label="&forget.cookies.label;"
+ accesskey="&forget.cookies.accesskey;"
+ disabled="true"
+ oncommand="gForget.updateOptions();"/>
+ <label id="forgetCookiesLabel"
+ value="&forget.cookies.label;"
+ hidden="true"/>
+ </hbox>
+ <hbox>
+ <checkbox id="forgetPermissions"
+ label="&forget.permissions.label;"
+ accesskey="&forget.permissions.accesskey;"
+ disabled="true"
+ oncommand="gForget.updateOptions();"/>
+ <label id="forgetPermissionsLabel"
+ value="&forget.permissions.label;"
+ hidden="true"/>
+ </hbox>
+ <hbox>
+ <checkbox id="forgetPreferences"
+ label="&forget.preferences.label;"
+ accesskey="&forget.preferences.accesskey;"
+ disabled="true"
+ oncommand="gForget.updateOptions();"/>
+ <label id="forgetPreferencesLabel"
+ value="&forget.preferences.label;"
+ hidden="true"/>
+ </hbox>
+ <hbox>
+ <checkbox id="forgetPasswords"
+ label="&forget.passwords.label;"
+ accesskey="&forget.passwords.accesskey;"
+ disabled="true"
+ oncommand="gForget.updateOptions();"/>
+ <label id="forgetPasswordsLabel"
+ value="&forget.passwords.label;"
+ hidden="true"/>
+ </hbox>
+ <hbox>
+ <checkbox id="forgetStorage"
+ label="&forget.storage.label;"
+ accesskey="&forget.storage.accesskey;"
+ disabled="true"
+ oncommand="gForget.updateOptions();"/>
+ <label id="forgetStorageLabel"
+ value="&forget.storage.label;"
+ hidden="true"/>
+ </hbox>
+ <hbox>
+ <checkbox id="forgetFormdata"
+ label="&forget.formdata.label;"
+ accesskey="&forget.formdata.accesskey;"
+ disabled="true"
+ hidden="true"
+ oncommand="gForget.updateOptions();"/>
+ <label id="forgetFormdataLabel"
+ value="&forget.formdata.label;"
+ hidden="true"/>
+ </hbox>
+ <hbox>
+ <button id="forgetButton"
+ label="&forget.button.label;"
+ accesskey="&forget.button.accesskey;"
+ disabled="true"
+ oncommand="gForget.forget();"/>
+ </hbox>
+ </vbox>
+ </tabpanels>
+ </tabbox>
+ </hbox>
+
+</page>
diff --git a/comm/suite/components/dataman/jar.mn b/comm/suite/components/dataman/jar.mn
new file mode 100644
index 0000000000..a58d3c9c7c
--- /dev/null
+++ b/comm/suite/components/dataman/jar.mn
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/dataman/dataman.css (content/dataman.css)
+ content/communicator/dataman/dataman.js (content/dataman.js)
+ content/communicator/dataman/dataman.xml (content/dataman.xml)
+ content/communicator/dataman/dataman.xul (content/dataman.xul)
diff --git a/comm/suite/components/dataman/moz.build b/comm/suite/components/dataman/moz.build
new file mode 100644
index 0000000000..aebed195ca
--- /dev/null
+++ b/comm/suite/components/dataman/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+BROWSER_CHROME_MANIFESTS += [
+ "tests/browser.ini",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/dataman/tests/browser.ini b/comm/suite/components/dataman/tests/browser.ini
new file mode 100644
index 0000000000..5cf2249c53
--- /dev/null
+++ b/comm/suite/components/dataman/tests/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ dataman_storage.appcache
+ dataman_storage.appcache^headers^
+ dataman_storage.html
+
+[browser_dataman_basics.js]
+[browser_dataman_callviews.js]
diff --git a/comm/suite/components/dataman/tests/browser_dataman_basics.js b/comm/suite/components/dataman/tests/browser_dataman_basics.js
new file mode 100644
index 0000000000..0013127f16
--- /dev/null
+++ b/comm/suite/components/dataman/tests/browser_dataman_basics.js
@@ -0,0 +1,831 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test basic functionality of the data manager.
+
+// Happens to match what's used in Data Manager itself.
+var gLocSvc = {
+ fhist: Cc["@mozilla.org/satchel/form-history;1"]
+ .getService(Ci.nsIFormHistory2),
+ idn: Cc["@mozilla.org/network/idn-service;1"]
+ .getService(Ci.nsIIDNService),
+};
+
+const DATAMAN_LOADED = "dataman-loaded";
+const TEST_DONE = "dataman-test-done";
+
+const kPreexistingDomains = 13;
+
+function test() {
+ // Preload data.
+ // Note that before this test starts, what is already set are permissions for
+ // addons.thunderbird.net to install addons as well as
+ // permissions for a number of sites used in mochitest to load XUL/XBL.
+ // For the latter, those 13 domains are used/listed: 127.0.0.1, bank1.com,
+ // bank2.com, example.com, example.org, mochi.test, mozilla.com, test,
+ // w3.org, w3c-test.org, xn--exaple-kqf.test, xn--exmple-cua.test,
+ // xn--hxajbheg2az3al.xn--jxalpdlp
+ // We should not touch those permissions so other tests can run, which means
+ // we should avoid using those domains altogether as we can't remove them.
+
+ let now_epoch = parseInt(Date.now() / 1000);
+
+ // Add dummy permission for getpersonas.com, less work (compared to rewriting
+ // the test to work without getpersonas.com)
+ Services.perms.add(Services.io.newURI("http://getpersonas.com/"),
+ "install", Services.perms.ALLOW_ACTION);
+
+ // Add cookie: not secure, non-HTTPOnly, session
+ Services.cookies.add("bar.geckoisgecko.org", "", "name0", "value0",
+ false, false, true, now_epoch + 600, {});
+ // Add cookie: not secure, HTTPOnly, session
+ Services.cookies.add("foo.geckoisgecko.org", "", "name1", "value1",
+ false, true, true, now_epoch + 600, {});
+ // Add cookie: secure, HTTPOnly, session
+ Services.cookies.add("secure.geckoisgecko.org", "", "name2", "value2",
+ true, true, true, now_epoch + 600, {});
+ // Add cookie: secure, non-HTTPOnly, expiry in an hour
+ Services.cookies.add("drumbeat.org", "", "name3", "value3",
+ true, false, false, now_epoch + 3600, {});
+
+ // Add a cookie for a pure IPv6 address.
+ Services.cookies.add("::1", "", "name4", "value4",
+ false, false, true, now_epoch + 600, {});
+
+ // Add a few form history entries
+ gLocSvc.fhist.addEntry("akey", "value0");
+ gLocSvc.fhist.addEntry("ekey", "value1");
+ gLocSvc.fhist.addEntry("ekey", "value2");
+ gLocSvc.fhist.addEntry("bkey", "value3");
+ gLocSvc.fhist.addEntry("bkey", "value4");
+ gLocSvc.fhist.addEntry("ckey", "value5");
+
+ // Add a few passwords
+ let loginInfo1 = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ loginInfo1.init("http://www.geckoisgecko.org", "http://www.geckoisgecko.org", null,
+ "dataman", "mysecret", "user", "pwd");
+ Services.logins.addLogin(loginInfo1);
+ let loginInfo2 = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ loginInfo2.init("gopher://geckoisgecko.org:4711", null, "foo",
+ "dataman", "mysecret", "", "");
+ Services.logins.addLogin(loginInfo2);
+ let loginInfo3 = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ loginInfo3.init("https://[::1]", null, "foo",
+ "dataman", "mysecret", "", "");
+ Services.logins.addLogin(loginInfo3);
+
+ //Services.prefs.setBoolPref("data_manager.debug", true);
+
+ gBrowser.addTab();
+ // Open the Data Manager, testing the menu item.
+ document.getElementById("tasksDataman").click();
+
+ var testIndex = 0;
+ var win;
+
+ let testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == DATAMAN_LOADED) {
+ Services.obs.removeObserver(testObs, DATAMAN_LOADED);
+ ok(true, "Data Manager is loaded");
+
+ win = content.wrappedJSObject;
+ Services.obs.addObserver(testObs, TEST_DONE);
+ // Trigger the first test now!
+ Services.obs.notifyObservers(window, TEST_DONE);
+ }
+ else {
+ // TEST_DONE triggered, run next test
+ info("run test #" + (testIndex + 1) + " of " + testFuncs.length +
+ " (" + testFuncs[testIndex].name + ")");
+ setTimeout(testFuncs[testIndex++], 0, win);
+
+ if (testIndex >= testFuncs.length) {
+ // Finish this up!
+ Services.obs.removeObserver(testObs, TEST_DONE);
+ Services.cookies.removeAll();
+ gLocSvc.fhist.removeAllEntries();
+ setTimeout(finish, 0);
+ }
+ }
+ }
+ };
+ waitForExplicitFinish();
+ Services.obs.addObserver(testObs, DATAMAN_LOADED);
+}
+
+var testFuncs = [
+function test_open_state(aWin) {
+ is(aWin.document.documentElement.id, "dataman-page",
+ "The active tab is the Data Manager");
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 6,
+ "The correct number of domains is listed");
+ is(aWin.gTabs.activePanel, "formdataPanel",
+ "Form data panel is selected");
+
+ aWin.document.getElementById("domainSearch").value = "mo";
+ aWin.document.getElementById("domainSearch").doCommand();
+ is(aWin.gDomains.tree.view.selection.count, 0,
+ "In search, non-matching selection is lost");
+ is(aWin.gDomains.tree.view.rowCount, 3,
+ "In search, the correct number of domains is listed");
+ is(aWin.gDomains.displayedDomains.map(function(aDom) { return aDom.title; })
+ .join(","),
+ "mochi.test,mozilla.com,mozilla.org",
+ "In search, the correct domains are listed");
+
+ aWin.gDomains.tree.view.selection.select(0);
+ aWin.document.getElementById("domainSearch").value = "";
+ aWin.document.getElementById("domainSearch").doCommand();
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 6,
+ "After search, the correct number of domains is listed");
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "After search, number of selections is correct");
+ is(aWin.gDomains.selectedDomain.title, "mochi.test",
+ "After search, matching selection is kept correctly");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_forget_ipv6(aWin) {
+ // The main purpose of IPv6 entries is that things load, delete them ASAP.
+ // Better forget panel tests (more checks) are in test_prefs_panel below.
+ aWin.gDomains.tree.view.selection.select(1);
+ is(aWin.gDomains.selectedDomain.title, "[::1]",
+ "IPv6 domain is selected");
+ aWin.document.getElementById("domain-context-forget").click();
+ is(aWin.gTabs.activePanel, "forgetPanel",
+ "Forget panel is selected");
+
+ aWin.document.getElementById("forgetCookies").click();
+ aWin.document.getElementById("forgetPasswords").click();
+ aWin.document.getElementById("forgetButton").click();
+ is(aWin.document.getElementById("forgetTab").hidden, true,
+ "Forget tab is hidden again");
+ is(aWin.document.getElementById("forgetTab").disabled, true,
+ "Forget panel is disabled again");
+
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 5,
+ "The IPv6 domain has been removed from the list");
+ is(aWin.gDomains.tree.view.selection.count, 0,
+ "No domain is selected");
+
+ aWin.gDomains.tree.view.selection.select(0);
+ is(aWin.gDomains.selectedDomain.title, "*",
+ "* domain is selected again");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_fdata_panel(aWin) {
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("formdataTab");
+ is(aWin.gTabs.activePanel, "formdataPanel",
+ "Form data panel is selected again");
+ is(aWin.gFormdata.tree.view.rowCount, 6,
+ "The correct number of form data entries is listed");
+
+ aWin.gFormdata.tree.view.selection.rangedSelect(0, 1, true); // item 0, 3
+ aWin.document.getElementById("fdataSearch").value = "b"; // item 3, 4 match
+ aWin.document.getElementById("fdataSearch").doCommand();
+ is(aWin.gFormdata.tree.view.selection.count, 1,
+ "In search, non-matching part of selection is lost");
+ is(aWin.gFormdata.displayedFormdata[aWin.gFormdata.tree.currentIndex].value, "value3",
+ "In search, matching part selection is kept correctly");
+ is(aWin.gFormdata.tree.view.rowCount, 2,
+ "In search, the correct number of form data entries is listed");
+ is(aWin.gFormdata.displayedFormdata.map(function(aFd) { return aFd.value; })
+ .join(","),
+ "value3,value4",
+ "In search, the correct domains are listed");
+
+ aWin.document.getElementById("fdataSearch").value = "";
+ aWin.document.getElementById("fdataSearch").doCommand();
+ is(aWin.gFormdata.tree.view.rowCount, 6,
+ "After search, the correct number of form data entries is listed");
+ is(aWin.gFormdata.tree.view.selection.count, 1,
+ "After search, number of selections is correct");
+ is(aWin.gFormdata.displayedFormdata[aWin.gFormdata.tree.currentIndex].value, "value3",
+ "After search, matching selection is kept correctly");
+
+ aWin.gFormdata.tree.view.selection.clearSelection();
+ is(aWin.document.getElementById("fdataRemove").disabled, true,
+ "The remove button is disabled");
+ aWin.gFormdata.tree.view.selection.rangedSelect(0, 1, true); // value0, value3
+ aWin.gFormdata.tree.view.selection.rangedSelect(3, 3, true); // value5
+ aWin.gFormdata.tree.view.selection.rangedSelect(5, 5, true); // value2
+ is(aWin.gFormdata.tree.view.selection.count, 4,
+ "The correct number of items is selected");
+ is(aWin.document.getElementById("fdataRemove").disabled, false,
+ "After selecting, the remove button is enabled");
+
+ gLocSvc.fhist.removeEntry("ckey", "value5");
+ is(aWin.gFormdata.tree.view.rowCount, 5,
+ "After remove, the correct number of form data entries is listed");
+ is(aWin.gFormdata.tree.view.selection.count, 3,
+ "After remove, the correct number of items is selected");
+
+ gLocSvc.fhist.addEntry("dkey", "value6");
+ is(aWin.gFormdata.tree.view.rowCount, 6,
+ "After add, the correct number of form data entries is listed");
+ is(aWin.gFormdata.tree.view.selection.count, 3,
+ "After add, the correct number of items is selected");
+
+ aWin.document.getElementById("fdataValueCol").click();
+ is(aWin.gFormdata.tree.view.selection.count, 3,
+ "After sort, the correct number of items is selected");
+ is(aWin.gDataman.getTreeSelections(aWin.gFormdata.tree)
+ .map(function(aSel) { return aWin.gFormdata.displayedFormdata[aSel].value; })
+ .join(","),
+ "value0,value2,value3",
+ "After sort, correct items are selected");
+
+ // Select only one for testing remove button, as catching the prompt is hard.
+ aWin.gFormdata.tree.view.selection.select(5);
+ aWin.document.getElementById("fdataRemove").click();
+ is(aWin.gFormdata.tree.view.rowCount, 5,
+ "After remove button, the correct number of form data entries is listed");
+ is(aWin.gFormdata.tree.view.selection.count, 1,
+ "After remove button, one item is selected again");
+ is(aWin.gFormdata.tree.currentIndex, 4,
+ "After remove button, correct index is selected");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_cookies_panel(aWin) {
+ aWin.gDomains.tree.view.selection.select(8);
+ is(aWin.gDomains.selectedDomain.title, "geckoisgecko.org",
+ "For cookie tests 1, correct domain is selected");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Cookies panel is selected");
+ is(aWin.gCookies.tree.view.rowCount, 3,
+ "The correct number of cookies is listed");
+
+ aWin.gCookies.tree.view.selection.select(0);
+ is(aWin.document.getElementById("cookieInfoSendType").value,
+ "Any type of connection",
+ "Correct send type for first cookie");
+ is(aWin.document.getElementById("cookieInfoExpires").value,
+ "At end of session",
+ "Correct expiry label for first cookie");
+
+ aWin.gCookies.tree.view.selection.select(1);
+ is(aWin.document.getElementById("cookieInfoSendType").value,
+ "Any type of connection, no script access",
+ "Correct send type for second cookie");
+
+ aWin.gCookies.tree.view.selection.select(2);
+ is(aWin.document.getElementById("cookieInfoSendType").value,
+ "Encrypted connections only and no script access",
+ "Correct send type for third cookie");
+
+ aWin.gDomains.tree.view.selection.select(4);
+ is(aWin.gDomains.selectedDomain.title, "drumbeat.org",
+ "For cookie tests 2, correct domain is selected");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Cookies panel is selected");
+ is(aWin.gCookies.tree.view.rowCount, 1,
+ "The correct number of cookies is listed");
+ aWin.gCookies.updateContext(); // don't actually open it, would be async
+ is(aWin.document.getElementById("cookies-context-selectall").disabled, false,
+ "The select all context menu item is enabled");
+ is(aWin.document.getElementById("cookies-context-remove").disabled, true,
+ "The remove context menu item is disabled");
+
+ aWin.document.getElementById("cookies-context-selectall").click();
+ is(aWin.document.getElementById("cookieInfoSendType").value,
+ "Encrypted connections only",
+ "Correct send type for fourth cookie");
+ isnot(aWin.document.getElementById("cookieInfoExpires").value,
+ "At end of session",
+ "Expiry label for this cookie is not session");
+ aWin.gCookies.updateContext(); // don't actually open it, would be async
+ is(aWin.document.getElementById("cookies-context-selectall").disabled, true,
+ "After selecting, the select all context menu item is disabled");
+ is(aWin.document.getElementById("cookies-context-remove").disabled, false,
+ "After selecting, the remove context menu item is enabled");
+
+ aWin.document.getElementById("cookies-context-remove").click();
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 4,
+ "The domain has been removed from the list");
+ is(aWin.gTabs.activePanel, null,
+ "No panel is active");
+ is(aWin.gTabs.tabbox.selectedTab.disabled, true,
+ "The selected panel is disabled");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_permissions_panel(aWin) {
+ aWin.gDomains.tree.view.selection.select(8);
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "For permissions tests, correct domain is selected");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Permissions panel is selected");
+ Services.perms.add(Services.io.newURI("http://cookie.getpersonas.com/"),
+ "cookie", Ci.nsICookiePermission.ACCESS_SESSION);
+ Services.perms.add(Services.io.newURI("http://cookie2.getpersonas.com/"),
+ "cookie", Services.perms.DENY_ACTION);
+ Services.perms.add(Services.io.newURI("http://geo.getpersonas.com/"),
+ "geo", Services.perms.ALLOW_ACTION);
+ Services.perms.add(Services.io.newURI("http://image.getpersonas.com/"),
+ "image", Services.perms.DENY_ACTION);
+ Services.perms.add(Services.io.newURI("http://indexedDB.getpersonas.com/"),
+ "indexedDB", Services.perms.ALLOW_ACTION);
+ Services.perms.add(Services.io.newURI("http://install.getpersonas.com/"),
+ "install", Services.perms.ALLOW_ACTION);
+ Services.perms.add(Services.io.newURI("http://offline.getpersonas.com/"),
+ "offline-app", Services.perms.ALLOW_ACTION);
+ Services.perms.add(Services.io.newURI("http://popup.getpersonas.com/"),
+ "popup", Services.perms.ALLOW_ACTION);
+ Services.perms.add(Services.io.newURI("http://test.getpersonas.com/"),
+ "test", Services.perms.DENY_ACTION);
+ Services.perms.add(Services.io.newURI("http://xul.getpersonas.com/"),
+ "allowXULXBL", Services.perms.ALLOW_ACTION);
+ Services.logins.setLoginSavingEnabled("password.getpersonas.com", false);
+ is(aWin.gPerms.list.children.length, 12,
+ "The correct number of permissions is displayed in the list");
+ for (let i = 1; i < aWin.gPerms.list.children.length; i++) {
+ let perm = aWin.gPerms.list.children[i];
+ switch (perm.type) {
+ case "allowXULXBL":
+ is(perm.getAttribute("label"), "Use XUL/XBL Markup",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 1,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 2,
+ "Set back to correct default");
+ break;
+ case "cookie":
+ is(perm.getAttribute("label"), "Set Cookies",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, perm.host == "cookie.getpersonas.com" ? 8 : 2,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 1,
+ "Set back to correct default");
+ break;
+ case "geo":
+ is(perm.getAttribute("label"), "Share Location",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 1,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 2,
+ "Set back to correct default");
+ break;
+ case "image":
+ is(perm.getAttribute("label"), "Load Images",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 2,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 1,
+ "Set back to correct default");
+ break;
+ case "indexedDB":
+ is(perm.getAttribute("label"), "Store Local Databases",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 1,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 2,
+ "Set back to correct default");
+ break;
+ case "install":
+ is(perm.getAttribute("label"), "Install Add-ons",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 1,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 2,
+ "Set back to correct default");
+ break;
+ case "offline-app":
+ is(perm.getAttribute("label"), "Offline Web Applications",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 1,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 1,
+ "Set back to correct default");
+ break;
+ case "password":
+ is(perm.getAttribute("label"), "Save Passwords",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 2,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 1,
+ "Set back to correct default");
+ break;
+ case "popup":
+ is(perm.getAttribute("label"), "Open Popup Windows",
+ "Correct label for type: " + perm.type);
+ is(perm.capability, 1,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 1,
+ "Set back to correct default");
+ break;
+ is(perm.getAttribute("label"), perm.type,
+ "Correct default label for type: " + perm.type);
+ is(perm.capability, 2,
+ "Correct capability for: " + perm.host);
+ perm.useDefault(true);
+ is(perm.capability, 0,
+ "Set to correct default");
+ break;
+ }
+ }
+
+ aWin.gDomains.tree.view.selection.select(0); // Switch to * domain.
+ aWin.gDomains.tree.view.selection.select(8); // Switch back to rebuild the perm list.
+ is(aWin.gPerms.list.children.length, 1,
+ "After the test, the correct number of permissions is displayed in the list");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_permissions_add(aWin) {
+ aWin.gDomains.tree.view.selection.select(0);
+ is(aWin.gDomains.selectedDomain.title, "*",
+ "For add permissions tests, * domain is selected again");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Permissions panel is selected");
+ is(aWin.gPerms.list.disabled, true,
+ "The permissions list is disabled");
+ is(aWin.gPerms.addButton.disabled, false,
+ "The add permissions button is enabled");
+ aWin.gPerms.addButton.click();
+ is(aWin.gPerms.addSelBox.hidden, false,
+ "The addition box is shown");
+ is(aWin.gPerms.addHost.value, "",
+ "The host is empty");
+ is(aWin.gPerms.addType.value, "",
+ "No type is selected");
+ is(aWin.gPerms.addButton.disabled, true,
+ "The add permissions button is disabled");
+ aWin.gPerms.addHost.value = "foo";
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("formdataTab");
+ is(aWin.gTabs.activePanel, "formdataPanel",
+ "Successfully switched to form data panel");
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("permissionsTab");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Successfully switched back to permissions panel");
+ is(aWin.gPerms.addButton.disabled, false,
+ "The add permissions button is enabled again");
+ is(aWin.gPerms.addSelBox.hidden, true,
+ "The addition box is hidden");
+ aWin.gPerms.addButton.click();
+ is(aWin.gPerms.addHost.value, "",
+ "The host is empty again");
+ is(aWin.gPerms.addType.value, "",
+ "No type is selected still");
+ aWin.gPerms.addHost.value = "data.permfoobar.com";
+ aWin.gPerms.addType.value = "cookie";
+ aWin.gPerms.addType.click();
+ is(aWin.gPerms.addButton.disabled, false,
+ "With host and type set, the add permissions button is enabled");
+ aWin.gPerms.addButton.click();
+ is(aWin.gPerms.list.disabled, false,
+ "After adding, the permissions list is enabled");
+ is(aWin.gPerms.list.children.length, 1,
+ "A permission is displayed in the list");
+ let perm = aWin.gPerms.list.children[0];
+ is(perm.type, "cookie",
+ "Added permission has correct type");
+ is(perm.host, "data.permfoobar.com",
+ "Added permission has correct host");
+ is(perm.capability, 1,
+ "Added permission has correct value (default)");
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("formdataTab");
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("permissionsTab");
+ is(aWin.gPerms.list.disabled, true,
+ "After switching between panels, the permissions list is disabled again");
+ aWin.gDomains.tree.view.selection.select(8);
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Switched to correct domain for another add test");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Permissions panel is selected");
+ aWin.gPerms.addButton.click();
+ is(aWin.gPerms.addHost.value, "getpersonas.com",
+ "On add, the host is set correctly");
+ is(aWin.gPerms.addType.value, "",
+ "Again, no type is selected");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_prefs_panel(aWin) {
+ Services.contentPrefs2.setPref("my.drumbeat.org", "data_manager.test", "foo", null);
+ Services.contentPrefs2.setPref("drumbeat.org", "data_manager.test", "bar", null);
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 5,
+ "The domain for prefs tests has been added from the list");
+ aWin.gDomains.tree.view.selection.select(4);
+ is(aWin.gDomains.selectedDomain.title, "drumbeat.org",
+ "For prefs tests, correct domain is selected");
+ is(aWin.gTabs.activePanel, "preferencesPanel",
+ "Preferences panel is selected");
+ is(aWin.gPrefs.tree.view.rowCount, 2,
+ "The correct number of prefs is listed");
+
+ aWin.gDomains.updateContext(); // don't actually open it, would be async
+ is(aWin.document.getElementById("domain-context-forget").disabled, false,
+ "The domain's forget context menu item is enabled");
+
+ aWin.document.getElementById("domain-context-forget").click();
+ is(aWin.gTabs.activePanel, "forgetPanel",
+ "Forget panel is selected");
+ is(aWin.document.getElementById("forgetTab").disabled, false,
+ "Forget panel is enabled");
+ is(aWin.document.getElementById("forgetTab").hidden, false,
+ "Forget panel is unhidden");
+
+ aWin.gDomains.tree.view.selection.select(3);
+ isnot(aWin.gDomains.selectedDomain.title, "drumbeat.org",
+ "Switching away goes to a different domain: " + aWin.gDomains.selectedDomain.title);
+ isnot(aWin.gTabs.activePanel, "forgetPanel",
+ "Forget panel is not selected any more: " + aWin.gTabs.activePanel);
+ is(aWin.document.getElementById("forgetTab").disabled, true,
+ "Forget panel is disabled");
+ is(aWin.document.getElementById("forgetTab").hidden, true,
+ "Forget panel is disabled");
+
+ aWin.gDomains.tree.view.selection.select(4);
+ is(aWin.gDomains.selectedDomain.title, "drumbeat.org",
+ "Correct domain is selected again");
+ aWin.document.getElementById("domain-context-forget").click();
+ is(aWin.gTabs.activePanel, "forgetPanel",
+ "Forget panel is selected again");
+ is(aWin.document.getElementById("forgetTab").disabled, false,
+ "Forget panel is enabled again");
+ is(aWin.document.getElementById("forgetTab").hidden, false,
+ "Forget panel is unhidden again");
+
+ is(aWin.document.getElementById("forgetPreferences").disabled, false,
+ "Forget preferences checkbox is enabled");
+ is(aWin.document.getElementById("forgetButton").disabled, true,
+ "Forget button is disabled");
+ aWin.document.getElementById("forgetPreferences").click();
+ is(aWin.document.getElementById("forgetPreferences").checked, true,
+ "Forget preferences checkbox is checked");
+ is(aWin.document.getElementById("forgetButton").disabled, false,
+ "Forget button is enabled");
+
+ aWin.document.getElementById("forgetButton").click();
+ is(aWin.document.getElementById("forgetButton").hidden, true,
+ "Forget button is hidden");
+ is(aWin.document.getElementById("forgetPreferences").hidden, true,
+ "Forget preferences checkbox is hidden");
+ is(aWin.document.getElementById("forgetPreferencesLabel").hidden, false,
+ "Forget preferences label is shown");
+ is(aWin.document.getElementById("forgetTab").hidden, true,
+ "Forget tab is hidden again");
+ is(aWin.document.getElementById("forgetTab").disabled, true,
+ "Forget panel is disabled again");
+
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 4,
+ "The domain for prefs tests has been removed from the list");
+ is(aWin.gDomains.tree.view.selection.count, 0,
+ "No domain is selected");
+
+ aWin.gDomains.updateContext(); // don't actually open it, would be async
+ is(aWin.document.getElementById("domain-context-forget").disabled, true,
+ "The domain's forget context menu item is disabled");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_passwords_panel(aWin) {
+ aWin.gDomains.tree.view.selection.select(7);
+ is(aWin.gDomains.selectedDomain.title, "geckoisgecko.org",
+ "For passwords tests, correct domain is selected");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Cookies panel is selected");
+
+ aWin.gDomains.updateContext(); // don't actually open it, would be async
+ is(aWin.document.getElementById("domain-context-forget").disabled, false,
+ "The domain's forget context menu item is enabled");
+
+ aWin.document.getElementById("domain-context-forget").click();
+ is(aWin.gTabs.activePanel, "forgetPanel",
+ "Forget panel is selected");
+ is(aWin.document.getElementById("forgetTab").disabled, false,
+ "Forget panel is enabled");
+ is(aWin.document.getElementById("forgetTab").hidden, false,
+ "Forget panel is unhidden");
+ is(aWin.document.getElementById("forgetPreferences").hidden, false,
+ "Forget preferences checkbox is shown");
+ is(aWin.document.getElementById("forgetPreferences").disabled, true,
+ "Forget preferences checkbox is disabled");
+ is(aWin.document.getElementById("forgetPreferencesLabel").hidden, true,
+ "Forget preferences label is hidden");
+ is(aWin.document.getElementById("forgetCookies").hidden, false,
+ "Forget cookies checkbox is shown");
+ is(aWin.document.getElementById("forgetCookies").disabled, false,
+ "Forget cookies checkbox is enabled");
+ is(aWin.document.getElementById("forgetCookiesLabel").hidden, true,
+ "Forget cookies label is hidden");
+ is(aWin.document.getElementById("forgetPasswords").hidden, false,
+ "Forget passwords checkbox is shown");
+ is(aWin.document.getElementById("forgetPasswords").disabled, false,
+ "Forget passwords checkbox is enabled");
+ is(aWin.document.getElementById("forgetPasswordsLabel").hidden, true,
+ "Forget passwords label is hidden");
+ is(aWin.document.getElementById("forgetButton").hidden, false,
+ "Forget button is shown");
+ is(aWin.document.getElementById("forgetButton").disabled, true,
+ "Forget button is disabled");
+
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("passwordsTab");
+ is(aWin.gTabs.activePanel, "passwordsPanel",
+ "Passwords panel is selected");
+ is(aWin.gPasswords.tree.view.rowCount, 2,
+ "The correct number of passwords is listed");
+ is(aWin.document.getElementById("pwdRemove").disabled, true,
+ "The remove button is disabled");
+
+ aWin.gPasswords.tree.view.selection.select(0);
+ is(aWin.document.getElementById("pwdRemove").disabled, false,
+ "After selecting, the remove button is enabled");
+
+ aWin.document.getElementById("pwdRemove").click();
+ is(aWin.gPasswords.tree.view.rowCount, 1,
+ "After deleting, the correct number of passwords is listed");
+ is(aWin.gPasswords.tree.view.selection.count, 1,
+ "After deleting, one password is selected again");
+ is(aWin.gPasswords.tree.currentIndex, 0,
+ "After deleting, correct index is selected");
+ is(aWin.document.getElementById("pwdRemove").disabled, false,
+ "After deleting, the remove button is still enabled");
+
+ aWin.gPasswords.tree.view.selection.select(0);
+ aWin.document.getElementById("pwdRemove").click();
+ is(aWin.document.getElementById("pwdRemove").disabled, true,
+ "After deleting last password, the remove button is disabled");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "After deleting last password, cookies panel is selected again");
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_idn(aWin) {
+ // Use a domain with an existing permission.
+ let testDomain = "xn--hxajbheg2az3al.xn--jxalpdlp";
+ let idnDomain = gLocSvc.idn.convertToDisplayIDN(testDomain, {});
+ isnot(testDomain, idnDomain, "Using a valid IDN domain");
+ // Add IDN cookie.
+ Services.cookies.add(testDomain, "", "name0", "value0", false, false, true,
+ parseInt(Date.now() / 1000) + 600, {});
+
+ aWin.document.getElementById("domainSearch").value = "xn--";
+ aWin.document.getElementById("domainSearch").doCommand();
+ is(aWin.gDomains.tree.view.rowCount, 1,
+ "Search for 'xn--' returns one result");
+ is(aWin.gDomains.displayedDomains.map(function(aDom) { return aDom.title; })
+ .join(","),
+ "xn--exaple-kqf.test", "In xn-- search, only the non-decodable domain is listed");
+ aWin.document.getElementById("domainSearch").value = idnDomain.charAt(3);
+ aWin.document.getElementById("domainSearch").doCommand();
+ is(aWin.gDomains.tree.view.rowCount, 1,
+ "IDN search returns a result");
+ is(aWin.gDomains.displayedDomains.map(function(aDom) { return aDom.title; })
+ .join(","),
+ testDomain, "In IDN search, the correct domain is listed");
+ aWin.document.getElementById("domainSearch").value = "";
+ aWin.document.getElementById("domainSearch").doCommand();
+
+ aWin.gDomains.tree.view.selection.select(kPreexistingDomains + 3);
+ is(aWin.gDomains.selectedDomain.title, testDomain,
+ "For IDN tests, correct domain is selected");
+ is(aWin.gDomains.selectedDomain.displayTitle, idnDomain,
+ "The display title of that domain is correct");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Cookies panel is selected");
+ is(aWin.gCookies.tree.view.getCellText(0, aWin.gCookies.tree.columns["cookieHostCol"]),
+ idnDomain,
+ "Correct domain displayed for IDN cookie");
+ aWin.gCookies.tree.view.selection.select(0);
+ aWin.document.getElementById("cookieRemove").click();
+
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "After deleting, correctly switched to permissions panel");
+ let perm = aWin.gPerms.list.children[0];
+ is(perm.host, "bug413909." + testDomain,
+ "Permission has correct host");
+ is(perm.getAttribute("displayHost"), "bug413909." + idnDomain,
+ "Permission has correct display host");
+
+ // Add pref with decoded IDN name.
+ Services.contentPrefs2.setPref(testDomain, "data_manager.test", "foo", null);
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("preferencesTab");
+ is(aWin.gTabs.activePanel, "preferencesPanel",
+ "Successfully switched to preferences panel for IDN tests");
+ // Add pref with encoded IDN name while panel is shown (different code path).
+ Services.contentPrefs2.setPref(idnDomain, "data_manager.test2", "bar", null);
+ is(aWin.gPrefs.tree.view.getCellText(0, aWin.gPrefs.tree.columns["prefsHostCol"]),
+ idnDomain,
+ "Correct domain displayed for punycode IDN preference");
+ is(aWin.gPrefs.tree.view.getCellText(1, aWin.gPrefs.tree.columns["prefsHostCol"]),
+ idnDomain,
+ "Correct domain displayed for utf8 IDN preference");
+ aWin.gPrefs.tree.view.selection.select(0);
+ aWin.document.getElementById("prefsRemove").click();
+ aWin.document.getElementById("prefsRemove").click();
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "After deleting, correctly switched back to permissions panel");
+
+ // Add IDN password (usually have encoded names)
+ let loginInfo1 = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ loginInfo1.init("http://" + idnDomain, "http://" + idnDomain, null,
+ "dataman", "mysecret", "user", "pwd");
+ Services.logins.addLogin(loginInfo1);
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("passwordsTab");
+ is(aWin.gTabs.activePanel, "passwordsPanel",
+ "Successfully switched to passwords panel for IDN tests");
+ is(aWin.gPasswords.tree.view.getCellText(0, aWin.gPasswords.tree.columns["pwdHostCol"]),
+ "http://" + idnDomain,
+ "Correct domain displayed for IDN password");
+ aWin.gPasswords.tree.view.selection.select(0);
+ aWin.document.getElementById("pwdRemove").click();
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "After deleting, correctly switched back to permissions panel");
+
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_storage_load(aWin) {
+ // Load the page that fills in several web storage entries.
+ Services.perms.add(Services.io.newURI("http://mochi.test:8888/"),
+ "offline-app", Services.perms.ALLOW_ACTION);
+
+ // Get the http address from the current chrome test path
+ let rootDir = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://mochi.test:8888/");
+ let testURL = rootDir + "dataman_storage.html";
+ let storagetab = gBrowser.addTab(testURL);
+ let stWin = storagetab.linkedBrowser.contentWindow.wrappedJSObject;
+ let dmStorageListener = {
+ handleEvent: function dmStorageHandler(aEvent) {
+ let tab = aEvent.target;
+ if (tab == storagetab) {
+ gBrowser.tabContainer.removeEventListener("TabClose", this);
+ // Force DOM Storage to write its data to the disk.
+ Services.obs.notifyObservers(null, "domstorage-flush-timer");
+ Services.perms.remove("mochi.test", "offline-app");
+ Services.obs.notifyObservers(window, TEST_DONE);
+ }
+ },
+ };
+ gBrowser.tabContainer.addEventListener("TabClose", dmStorageListener);
+},
+
+function test_storage_wait(aWin) {
+ // Wait to make sure that DOM Storage flushing has actually worked.
+ setTimeout(function foo() {
+ Services.obs.notifyObservers(window, TEST_DONE); }, 1000);
+},
+
+function test_storage(aWin) {
+ aWin.gStorage.reloadList();
+ info("appcache groups: " + aWin.gLocSvc.appcache.getGroups().length);
+ aWin.gDomains.tree.view.selection.select(8);
+ is(aWin.gDomains.selectedDomain.title, "mochi.test",
+ "For storage tests, correct domain is selected");
+ is(aWin.document.getElementById("storageTab").disabled, false,
+ "Storage panel is enabled");
+ aWin.gTabs.tabbox.selectedTab = aWin.document.getElementById("storageTab");
+ is(aWin.gTabs.activePanel, "storagePanel",
+ "Storage panel is selected");
+ is(aWin.gStorage.tree.view.rowCount, 3,
+ "The correct number of storages is listed");
+ is(aWin.gStorage.displayedStorages
+ .map(function(aStorage) { return aStorage.type; })
+ .sort().join(","),
+ "appCache,indexedDB,localStorage",
+ "The correct types of storage are listed");
+
+ for (let i = aWin.gStorage.tree.view.rowCount - 1; i >= 0; i--) {
+ let remType = aWin.gStorage.displayedStorages[0].type;
+ info("Removing " + remType);
+ aWin.gStorage.tree.view.selection.select(0);
+ aWin.document.getElementById("storageRemove").click();
+ is(aWin.gStorage.tree.view.rowCount, i,
+ remType + " entry removed");
+ }
+
+ isnot(aWin.gTabs.activePanel, "storagePanel",
+ "Storage panel is not selected any more");
+
+ Services.obs.notifyObservers(window, TEST_DONE);
+},
+
+function test_close(aWin) {
+ function dmWindowClosedListener() {
+ aWin.removeEventListener("unload", dmWindowClosedListener);
+ isnot(content.document.documentElement.id, "dataman-page",
+ "The active tab is not the Data Manager");
+ Services.obs.notifyObservers(window, TEST_DONE);
+ }
+ aWin.addEventListener("unload", dmWindowClosedListener);
+ aWin.close();
+}
+];
diff --git a/comm/suite/components/dataman/tests/browser_dataman_callviews.js b/comm/suite/components/dataman/tests/browser_dataman_callviews.js
new file mode 100644
index 0000000000..e7ace5ee5a
--- /dev/null
+++ b/comm/suite/components/dataman/tests/browser_dataman_callviews.js
@@ -0,0 +1,213 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test loading views in data manager.
+
+const DATAMAN_LOADED = "dataman-loaded";
+
+// See browser_dataman_basics.js.
+const kPreexistingDomains = 12;
+
+var testIndex = 0;
+
+function test() {
+ // Add cookies.
+ Services.cookies.add("getpersonas.com", "", "name0", "value0", false, false,
+ true, parseInt(Date.now() / 1000) + 600, {});
+ Services.cookies.add("drumbeat.org", "", "name1", "value1", false, false,
+ true, parseInt(Date.now() / 1000) + 600, {});
+
+ //Services.prefs.setBoolPref("data_manager.debug", true);
+
+ var win;
+
+ gBrowser.addTab();
+ toDataManager("example.org");
+
+ let testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == DATAMAN_LOADED) {
+ // Run next test
+ info("run test #" + (testIndex + 1) + " of " + testFuncs.length +
+ " (" + testFuncs[testIndex].name + ")");
+
+ ok(true, "Step " + (testIndex + 1) + ": Data Manager is loaded");
+ win = content.wrappedJSObject;
+
+ testFuncs[testIndex++](win);
+
+ if (testIndex >= testFuncs.length) {
+ // Finish this up!
+ Services.obs.removeObserver(testObs, DATAMAN_LOADED);
+ Services.cookies.remove("getpersonas.com", "name0", "value0", false);
+ Services.cookies.remove("drumbeat.org", "name1", "value1", false);
+ finish();
+ }
+ }
+ }
+ };
+ waitForExplicitFinish();
+ Services.obs.addObserver(testObs, DATAMAN_LOADED);
+}
+
+var testFuncs = [
+function test_load_basic(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "example.org",
+ "Step " + testIndex + ": The correct domain is selected");
+ toDataManager("getpersonas.com|cookies");
+},
+
+function test_switch_panel(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Step " + testIndex + ": Cookies panel is selected");
+ aWin.close();
+ gBrowser.addTab();
+ toDataManager("www.getpersonas.com:443|permissions");
+},
+
+function test_load_with_panel(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Step " + testIndex + ": Permissions panel is selected");
+ aWin.close();
+ gBrowser.addTab();
+ toDataManager("getpersonas.com|preferences");
+},
+
+function test_load_disabled_panel(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Step " + testIndex + ": Cookies panel is selected");
+ aWin.close();
+ gBrowser.addTab();
+ toDataManager("getpersonas.com|unknown");
+},
+
+function test_load_inexistent_panel(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Step " + testIndex + ": Cookies panel is selected");
+ aWin.close();
+ gBrowser.addTab();
+ toDataManager("unknowndomainexample.com");
+},
+
+function test_load_unknown_domain(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "*",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Step " + testIndex + ": Permissions panel is selected");
+ aWin.close();
+ gBrowser.addTab();
+ toDataManager("|cookies");
+},
+
+function test_load_datatype(aWin) {
+ is(aWin.gDomains.selectfield.value, "Cookies",
+ "Step " + testIndex + ": The correct menulist item is selected");
+ is(aWin.gDomains.tree.view.rowCount, 2,
+ "Step " + testIndex + ": The correct number of domains is listed");
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "drumbeat.org",
+ "Step " + testIndex + ": The selected domain is correct");
+ is(aWin.gTabs.activePanel, "cookiesPanel",
+ "Step " + testIndex + ": Cookies panel is selected");
+ aWin.gDomains.tree.view.selection.select(1);
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The second domain is correct as well");
+ toDataManager("|permissions");
+},
+
+function test_switch_datatype(aWin) {
+ is(aWin.gDomains.selectfield.value, "Permissions",
+ "Step " + testIndex + ": The correct menulist item is selected");
+ is(aWin.gDomains.tree.view.rowCount, kPreexistingDomains + 3,
+ "Step " + testIndex + ": The correct number of domains is listed");
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "*",
+ "Step " + testIndex + ": The selected domain is correct");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Step " + testIndex + ": Permissions panel is selected");
+ toDataManager("www.getpersonas.com");
+},
+
+function test_escape_datatype(aWin) {
+ is(aWin.gDomains.selectfield.value, "all",
+ "Step " + testIndex + ": The correct menulist item is selected");
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The correct domain is selected");
+ aWin.close();
+ gBrowser.addTab();
+ toDataManager("sub.getpersonas.com:8888|permissions|add|popup");
+},
+
+function test_load_add_perm_existdomain(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "getpersonas.com",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Step " + testIndex + ": Permissions panel is selected");
+ is(aWin.gPerms.addSelBox.hidden, false,
+ "Step " + testIndex + ": The addition box is shown");
+ is(aWin.gPerms.addHost.value, "sub.getpersonas.com:8888",
+ "Step " + testIndex + ": The correct host and port has been entered");
+ is(aWin.gPerms.addType.value, "popup",
+ "Step " + testIndex + ": The correct permission type has been selected");
+ toDataManager("foo.geckoisgecko.org|permissions|add|image");
+},
+
+function test_switch_add_perm_newdomain(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "*",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Step " + testIndex + ": Permissions panel is selected");
+ is(aWin.gPerms.addSelBox.hidden, false,
+ "Step " + testIndex + ": The addition box is shown");
+ is(aWin.gPerms.addHost.value, "foo.geckoisgecko.org",
+ "Step " + testIndex + ": The correct host has been entered");
+ is(aWin.gPerms.addType.value, "image",
+ "Step " + testIndex + ": The correct permission type has been selected");
+ toDataManager("drumbeat.org|permissions|add|cookie");
+},
+
+function test_switch_add_perm_nopermdomain(aWin) {
+ is(aWin.gDomains.tree.view.selection.count, 1,
+ "Step " + testIndex + ": One domain is selected");
+ is(aWin.gDomains.selectedDomain.title, "*",
+ "Step " + testIndex + ": The correct domain is selected");
+ is(aWin.gTabs.activePanel, "permissionsPanel",
+ "Step " + testIndex + ": Permissions panel is selected");
+ is(aWin.gPerms.addSelBox.hidden, false,
+ "Step " + testIndex + ": The addition box is shown");
+ is(aWin.gPerms.addHost.value, "drumbeat.org",
+ "Step " + testIndex + ": The correct host has been entered");
+ is(aWin.gPerms.addType.value, "cookie",
+ "Step " + testIndex + ": The correct permission type has been selected");
+ aWin.close();
+}
+];
diff --git a/comm/suite/components/dataman/tests/dataman_storage.appcache b/comm/suite/components/dataman/tests/dataman_storage.appcache
new file mode 100644
index 0000000000..bc05ca8016
--- /dev/null
+++ b/comm/suite/components/dataman/tests/dataman_storage.appcache
@@ -0,0 +1,5 @@
+CACHE MANIFEST
+# allow caching of the test itself.
+CACHE:
+browser_dataman_basics.js
+dataman_storage.html
diff --git a/comm/suite/components/dataman/tests/dataman_storage.appcache^headers^ b/comm/suite/components/dataman/tests/dataman_storage.appcache^headers^
new file mode 100644
index 0000000000..5efde3c5b0
--- /dev/null
+++ b/comm/suite/components/dataman/tests/dataman_storage.appcache^headers^
@@ -0,0 +1,2 @@
+Content-Type: text/cache-manifest
+
diff --git a/comm/suite/components/dataman/tests/dataman_storage.html b/comm/suite/components/dataman/tests/dataman_storage.html
new file mode 100644
index 0000000000..38ea3c3c38
--- /dev/null
+++ b/comm/suite/components/dataman/tests/dataman_storage.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html manifest="dataman_storage.appcache">
+<head>
+<script>
+var seenEvents = 0;
+function eventSeen(e){
+ seenEvents++;
+ document.getElementById("eventCnt").textContent = seenEvents;
+ if (seenEvents > 1)
+ setTimeout(close, 1000);
+}
+</script>
+</head>
+<body>
+<h1>Test</h1>
+<p id="eventCnt">*</p>
+<script>
+document.body.addEventListener('storage', eventSeen);
+document.addEventListener('idb-done', eventSeen);
+
+localStorage.setItem("localtest", "foo"); // issues no storage event (!?)
+globalStorage['mochi.test'].setItem("globaltest", "bar"); // issues a storage event
+
+var request = mozIndexedDB.open("test", "test-decription");
+request.onsuccess = function(e) {
+ var db = e.target.result;
+ var setVrequest = db.setVersion("1.0");
+ setVrequest.onsuccess = function(e) {
+ var store = db.createObjectStore("test", {keyPath: "foo"});
+ db.transaction(["test"], IDBTransaction.READ_WRITE, 0)
+ .objectStore("test").put({"foo": "bar"});
+ // create, define and dispatch the test-done event
+ var event = document.createEvent('Event');
+ event.initEvent('idb-done', true, true);
+ document.dispatchEvent(event);
+ }
+};
+</script>
+</body>
+</html>
diff --git a/comm/suite/components/downloads/DownloadsCommon.jsm b/comm/suite/components/downloads/DownloadsCommon.jsm
new file mode 100644
index 0000000000..81fdcccfa3
--- /dev/null
+++ b/comm/suite/components/downloads/DownloadsCommon.jsm
@@ -0,0 +1,800 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "DownloadsCommon",
+];
+
+/**
+ * Handles the Downloads panel shared methods and data access.
+ *
+ * This file includes the following constructors and global objects:
+ *
+ * DownloadsCommon
+ * This object is exposed directly to the consumers of this JavaScript module,
+ * and provides shared methods for all the instances of the user interface.
+ *
+ * DownloadsData
+ * Retrieves the list of past and completed downloads from the underlying
+ * Downloads API data, and provides asynchronous notifications allowing
+ * to build a consistent view of the available data.
+ */
+
+// Globals
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+ PluralForm: "resource://gre/modules/PluralForm.jsm",
+ DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
+ Downloads: "resource://gre/modules/Downloads.jsm",
+ DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
+ DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+ OS: "resource://gre/modules/osfile.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
+ let { ConsoleAPI } =
+ ChromeUtils.import("resource://gre/modules/Console.jsm", {});
+ let consoleOptions = {
+ maxLogLevelPref: "browser.download.loglevel",
+ prefix: "Downloads"
+ };
+ return new ConsoleAPI(consoleOptions);
+});
+
+const kDownloadsStringBundleUrl =
+ "chrome://communicator/locale/downloads/downloadmanager.properties";
+
+// Currently not used. Keep for future updates.
+const kDownloadsStringsRequiringFormatting = {
+ fileExecutableSecurityWarning: true
+};
+
+// Currently not used. Keep for future updates.
+const kDownloadsStringsRequiringPluralForm = {
+ otherDownloads3: true
+};
+
+const kPartialDownloadSuffix = ".part";
+
+const kPrefBranch = Services.prefs.getBranch("browser.download.");
+
+const PREF_DM_BEHAVIOR = "browser.download.manager.behavior";
+const PROGRESS_DIALOG_URL = "chrome://communicator/content/downloads/progressDialog.xul";
+
+var PrefObserver = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsISupportsWeakReference]),
+ getPref(name) {
+ try {
+ switch (typeof this.prefs[name]) {
+ case "boolean":
+ return kPrefBranch.getBoolPref(name);
+ }
+ } catch (ex) { }
+ return this.prefs[name];
+ },
+ observe(aSubject, aTopic, aData) {
+ if (this.prefs.hasOwnProperty(aData)) {
+ delete this[aData];
+ this[aData] = this.getPref(aData);
+ }
+ },
+ register(prefs) {
+ this.prefs = prefs;
+ kPrefBranch.addObserver("", this, true);
+ for (let key in prefs) {
+ let name = key;
+ XPCOMUtils.defineLazyGetter(this, name, function() {
+ return PrefObserver.getPref(name);
+ });
+ }
+ },
+};
+
+// PrefObserver.register({
+ // prefName: defaultValue
+// });
+
+
+// DownloadsCommon
+
+/**
+ * This object is exposed directly to the consumers of this JavaScript module,
+ * and provides shared methods for all the instances of the user interface.
+ */
+var DownloadsCommon = {
+ // The following legacy constants are still returned by stateOfDownload, but
+ // individual properties of the Download object should normally be used.
+ DOWNLOAD_NOTSTARTED: -1,
+ DOWNLOAD_DOWNLOADING: 0,
+ DOWNLOAD_FINISHED: 1,
+ DOWNLOAD_FAILED: 2,
+ DOWNLOAD_CANCELED: 3,
+ DOWNLOAD_PAUSED: 4,
+ DOWNLOAD_BLOCKED_PARENTAL: 6,
+ DOWNLOAD_DIRTY: 8,
+ DOWNLOAD_BLOCKED_POLICY: 9,
+
+ // The following are the possible values of the "attention" property.
+ ATTENTION_NONE: "",
+ ATTENTION_SUCCESS: "success",
+ ATTENTION_WARNING: "warning",
+ ATTENTION_SEVERE: "severe",
+
+ /**
+ * Returns an object whose keys are the string names from the downloads string
+ * bundle, and whose values are either the translated strings or functions
+ * returning formatted strings.
+ */
+ get strings() {
+ let strings = {};
+ let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
+ let enumerator = sb.getSimpleEnumeration();
+ while (enumerator.hasMoreElements()) {
+ let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+ let stringName = string.key;
+ if (stringName in kDownloadsStringsRequiringFormatting) {
+ strings[stringName] = function() {
+ // Convert "arguments" to a real array before calling into XPCOM.
+ return sb.formatStringFromName(stringName,
+ Array.slice(arguments, 0),
+ arguments.length);
+ };
+ } else if (stringName in kDownloadsStringsRequiringPluralForm) {
+ strings[stringName] = function(aCount) {
+ // Convert "arguments" to a real array before calling into XPCOM.
+ let formattedString = sb.formatStringFromName(stringName,
+ Array.slice(arguments, 0),
+ arguments.length);
+ return PluralForm.get(aCount, formattedString);
+ };
+ } else {
+ strings[stringName] = string.value;
+ }
+ }
+ delete this.strings;
+ return this.strings = strings;
+ },
+
+ /**
+ * Get access to one of the DownloadsData or HistoryDownloadsData objects
+ * depending on whether history downloads should be included.
+ *
+ * @param window
+ * The browser window which owns the download button.
+ * @param [optional] history
+ * True to include history downloads when the window is public.
+ */
+ // does not apply in SM
+ getData(window, history = false) {
+ if (history) {
+ return HistoryDownloadsData;
+ }
+ return DownloadsData;
+ },
+
+ /**
+ * Initializes the Downloads Manager common code.
+ */
+ init() {
+ const { DownloadsData } =
+ ChromeUtils.import("resource://gre/modules/Downloads.jsm");
+ const { DownloadIntegration } =
+ ChromeUtils.import("resource://gre/modules/DownloadIntegration.jsm");
+ DownloadIntegration.shouldPersistDownload = function() { return true; };
+ DownloadsData.initializeDataLink();
+ },
+
+ /**
+ * Returns the legacy state integer value for the provided Download object.
+ */
+ stateOfDownload(download) {
+ // Collapse state using the correct priority.
+ if (!download.stopped) {
+ return DownloadsCommon.DOWNLOAD_DOWNLOADING;
+ }
+ if (download.succeeded) {
+ return DownloadsCommon.DOWNLOAD_FINISHED;
+ }
+ if (download.error) {
+ if (download.error.becauseBlockedByParentalControls) {
+ return DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL;
+ }
+ if (download.error.becauseBlockedByReputationCheck) {
+ return DownloadsCommon.DOWNLOAD_DIRTY;
+ }
+ return DownloadsCommon.DOWNLOAD_FAILED;
+ }
+ if (download.canceled) {
+ if (download.hasPartialData) {
+ return DownloadsCommon.DOWNLOAD_PAUSED;
+ }
+ return DownloadsCommon.DOWNLOAD_CANCELED;
+ }
+ return DownloadsCommon.DOWNLOAD_NOTSTARTED;
+ },
+
+ /**
+ * Returns the state as a string for the provided Download object.
+ */
+ stateOfDownloadText(download) {
+ // Don't duplicate the logic so just call stateOfDownload.
+ let state = this.stateOfDownload(download);
+ let s = DownloadsCommon.strings;
+ let title = s.unblockHeaderUnblock;
+ let verboseState;
+
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ verboseState = s.statePaused;
+ break;
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ verboseState = s.stateDownloading;
+ break;
+ case DownloadsCommon.DOWNLOAD_FINISHED:
+ verboseState = s.stateCompleted;
+ break;
+ case DownloadsCommon.DOWNLOAD_FAILED:
+ verboseState = s.stateFailed;
+ break;
+ case DownloadsCommon.DOWNLOAD_CANCELED:
+ verboseState = s.stateCanceled;
+ break;
+ // Security Zone Policy
+ case DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL:
+ // Security Zone Policy
+ verboseState = s.stateBlockedParentalControls;
+ break;
+ // Security Zone Policy
+ case DownloadsCommon.DOWNLOAD_BLOCKED_POLICY:
+ verboseState = s.stateBlockedPolicy;
+ break;
+ // possible virus/spyware
+ case DownloadsCommon.DOWNLOAD_DIRTY:
+ verboseState = s.stateDirty;
+ break;
+ // Currently not returned.
+ case DownloadsCommon.DOWNLOAD_UPLOADING:
+ verboseState = s.stateNotStarted;
+ break;
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ verboseState = s.stateNotStarted;
+ break;
+ // Whoops!
+ default:
+ verboseState = s.stateUnknown;
+ break;
+ }
+
+ return verboseState;
+ },
+
+ /**
+ * Returns the transfer progress text for the provided Download object.
+ */
+ getTransferredBytes(download) {
+ let currentBytes;
+ let totalBytes;
+ // Download in progress.
+ // Download paused / canceled and has partial data.
+ if (!download.stopped ||
+ (download.canceled && download.hasPartialData)) {
+ currentBytes = download.currentBytes,
+ totalBytes = download.hasProgress ? download.totalBytes : -1;
+ // Download done but file missing.
+ } else if (download.succeeded && !download.exists) {
+ currentBytes = download.totalBytes ? download.totalBytes : -1;
+ totalBytes = -1;
+ // For completed downloads, show the file size
+ } else if (download.succeeded && download.target.size !== undefined) {
+ currentBytes = download.target.size;
+ totalBytes = -1;
+ // Some local files saves e.g. from attachments also have no size.
+ // They only have a target in downloads.json but no target.path.
+ // FIX ME later.
+ } else {
+ currentBytes = -1;
+ totalBytes = -1;
+ }
+
+ // We do not want to show 0 of xxx bytes.
+ if (currentBytes == 0) {
+ currentBytes = -1;
+ }
+
+ if (totalBytes == 0) {
+ totalBytes = -1;
+ }
+
+ // We tried everything.
+ if (currentBytes == -1 && totalBytes == -1) {
+ return "";
+ }
+
+ return DownloadUtils.getTransferTotal(currentBytes, totalBytes);
+ },
+
+ /**
+ * Returns the time remaining text for the provided Download object.
+ * For calculation a variable is stored in it.
+ */
+ getTimeRemaining(download) {
+ // If you do changes here please check progressDialog.js.
+ if (!download.stopped) {
+ let lastSec = (download.lastSec == null) ? Infinity : download.lastSec;
+ // Calculate the time remaining if we have valid values
+ let seconds = (download.speed > 0) && (download.totalBytes > 0)
+ ? (download.totalBytes - download.currentBytes) / download.speed
+ : -1;
+ let [timeLeft, newLast] = DownloadUtils.getTimeLeft(seconds, lastSec);
+ // Store it back for next calculation.
+ download.lastSec = newLast;
+ return timeLeft;
+ }
+ return "";
+ },
+
+ /**
+ * Opens a downloaded file.
+ *
+ * @param aFile
+ * the downloaded file to be opened.
+ * @param aMimeInfo
+ * the mime type info object. May be null.
+ * @param aOwnerWindow
+ * the window with which this action is associated.
+ */
+ openDownloadedFile(aFile, aMimeInfo, aOwnerWindow) {
+ if (!(aFile instanceof Ci.nsIFile)) {
+ throw new Error("aFile must be a nsIFile object");
+ }
+ if (aMimeInfo && !(aMimeInfo instanceof Ci.nsIMIMEInfo)) {
+ throw new Error("Invalid value passed for aMimeInfo");
+ }
+ if (!(aOwnerWindow instanceof Ci.nsIDOMWindow)) {
+ throw new Error("aOwnerWindow must be a dom-window object");
+ }
+
+ let isWindowsExe = AppConstants.platform == "win" &&
+ aFile.leafName.toLowerCase().endsWith(".exe");
+
+ let promiseShouldLaunch;
+ // Don't prompt on Windows for .exe since there will be a native prompt.
+ if (aFile.isExecutable() && !isWindowsExe) {
+ // We get a prompter for the provided window here, even though anchoring
+ // to the most recently active window should work as well.
+ promiseShouldLaunch =
+ DownloadUIHelper.getPrompter(aOwnerWindow)
+ .confirmLaunchExecutable(aFile.path);
+ } else {
+ promiseShouldLaunch = Promise.resolve(true);
+ }
+
+ promiseShouldLaunch.then(shouldLaunch => {
+ if (!shouldLaunch) {
+ return;
+ }
+
+ // Actually open the file.
+ try {
+ if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
+ aMimeInfo.launchWithFile(aFile);
+ return;
+ }
+ } catch (ex) { }
+
+ // If either we don't have the mime info, or the preferred action failed,
+ // attempt to launch the file directly.
+ try {
+ aFile.launch();
+ } catch (ex) {
+ // If launch fails, try sending it through the system's external "file:"
+ // URL handler.
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadUrl(NetUtil.newURI(aFile));
+ }
+ }).catch(Cu.reportError);
+ },
+
+ /**
+ * Show a downloaded file in the system file manager.
+ *
+ * @param aFile
+ * a downloaded file.
+ */
+ showDownloadedFile(aFile) {
+ if (!(aFile instanceof Ci.nsIFile)) {
+ throw new Error("aFile must be a nsIFile object");
+ }
+ try {
+ // Show the directory containing the file and select the file.
+ aFile.reveal();
+ } catch (ex) {
+ // If reveal fails for some reason (e.g., it's not implemented on unix
+ // or the file doesn't exist), try using the parent if we have it.
+ let parent = aFile.parent;
+ if (parent) {
+ this.showDirectory(parent);
+ }
+ }
+ },
+
+ /**
+ * Show the specified folder in the system file manager.
+ *
+ * @param aDirectory
+ * a directory to be opened with system file manager.
+ */
+ showDirectory(aDirectory) {
+ if (!(aDirectory instanceof Ci.nsIFile)) {
+ throw new Error("aDirectory must be a nsIFile object");
+ }
+ try {
+ aDirectory.launch();
+ } catch (ex) {
+ // If launch fails (probably because it's not implemented), let
+ // the OS handler try to open the directory.
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadUrl(NetUtil.newURI(aDirectory));
+ }
+ },
+
+ /**
+ * Displays an alert message box which asks the user if they want to
+ * unblock the downloaded file or not.
+ *
+ * @param options
+ * An object with the following properties:
+ * {
+ * verdict:
+ * The detailed reason why the download was blocked, according to
+ * the "Downloads.Error.BLOCK_VERDICT_" constants. If an unknown
+ * reason is specified, "Downloads.Error.BLOCK_VERDICT_MALWARE" is
+ * assumed.
+ * window:
+ * The window with which this action is associated.
+ * dialogType:
+ * String that determines which actions are available:
+ * - "unblock" to offer just "unblock".
+ * - "chooseUnblock" to offer "unblock" and "confirmBlock".
+ * - "chooseOpen" to offer "open" and "confirmBlock".
+ * }
+ *
+ * @return {Promise}
+ * @resolves String representing the action that should be executed:
+ * - "open" to allow the download and open the file.
+ * - "unblock" to allow the download without opening the file.
+ * - "confirmBlock" to delete the blocked data permanently.
+ * - "cancel" to do nothing and cancel the operation.
+ */
+ async confirmUnblockDownload({ verdict, window,
+ dialogType }) {
+ let s = DownloadsCommon.strings;
+
+ // All the dialogs have an action button and a cancel button, while only
+ // some of them have an additonal button to remove the file. The cancel
+ // button must always be the one at BUTTON_POS_1 because this is the value
+ // returned by confirmEx when using ESC or closing the dialog (bug 345067).
+ let title = s.unblockHeaderUnblock;
+ let firstButtonText = s.unblockButtonUnblock;
+ let firstButtonAction = "unblock";
+ let buttonFlags =
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+ (Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1);
+
+ switch (dialogType) {
+ case "unblock":
+ // Use only the unblock action. The default is to cancel.
+ buttonFlags += Ci.nsIPrompt.BUTTON_POS_1_DEFAULT;
+ break;
+ case "chooseUnblock":
+ // Use the unblock and remove file actions. The default is remove file.
+ buttonFlags +=
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) +
+ Ci.nsIPrompt.BUTTON_POS_2_DEFAULT;
+ break;
+ case "chooseOpen":
+ // Use the unblock and open file actions. The default is open file.
+ title = s.unblockHeaderOpen;
+ firstButtonText = s.unblockButtonOpen;
+ firstButtonAction = "open";
+ buttonFlags +=
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) +
+ Ci.nsIPrompt.BUTTON_POS_0_DEFAULT;
+ break;
+ default:
+ Cu.reportError("Unexpected dialog type: " + dialogType);
+ return "cancel";
+ }
+
+ let message;
+ switch (verdict) {
+ case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
+ message = s.unblockTypeUncommon2;
+ break;
+ case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
+ message = s.unblockTypePotentiallyUnwanted2;
+ break;
+ default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE
+ message = s.unblockTypeMalware;
+ break;
+ }
+ message += "\n\n" + s.unblockTip2;
+
+ Services.ww.registerNotification(function onOpen(subj, topic) {
+ if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+ // Make sure to listen for "DOMContentLoaded" because it is fired
+ // before the "load" event.
+ subj.addEventListener("DOMContentLoaded", function() {
+ if (subj.document.documentURI ==
+ "chrome://global/content/commonDialog.xul") {
+ Services.ww.unregisterNotification(onOpen);
+ let dialog = subj.document.getElementById("commonDialog");
+ if (dialog) {
+ // Change the dialog to use a warning icon.
+ dialog.classList.add("alert-dialog");
+ }
+ }
+ }, {once: true});
+ }
+ });
+
+ let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
+ firstButtonText, null,
+ s.unblockButtonConfirmBlock, null, {});
+ return [firstButtonAction, "cancel", "confirmBlock"][rv];
+ },
+};
+
+XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "log", () => {
+ return DownloadsLogger.log.bind(DownloadsLogger);
+});
+XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "error", () => {
+ return DownloadsLogger.error.bind(DownloadsLogger);
+});
+
+// DownloadsData
+
+/**
+ * Retrieves the list of past and completed downloads from the underlying
+ * Downloads API data, and provides asynchronous notifications allowing to
+ * build a consistent view of the available data.
+ *
+ * Note that using this object does not automatically initialize the list of
+ * downloads. This is useful to display a neutral progress indicator in
+ * the main browser window until the autostart timeout elapses.
+ *
+ * This powers the DownloadsData and HistoryDownloadsData singleton objects.
+ */
+ function DownloadsDataCtor({ isHistory } = {}) {
+
+ // Contains all the available Download objects and their integer state.
+ this.oldDownloadStates = new Map();
+
+ // For the history downloads list we don't need to register this as a view,
+ // but we have to ensure that the DownloadsData object is initialized before
+ // we register more views. This ensures that the view methods of DownloadsData
+ // are invoked before those of views registered on HistoryDownloadsData,
+ // allowing the endTime property to be set correctly.
+ if (isHistory) {
+ DownloadsData.initializeDataLink();
+ this._promiseList = DownloadsData._promiseList
+ .then(() => DownloadHistory.getList());
+ return;
+ }
+
+ // This defines "initializeDataLink" and "_promiseList" synchronously, then
+ // continues execution only when "initializeDataLink" is called, allowing the
+ // underlying data to be loaded only when actually needed.
+ this._promiseList = (async () => {
+ await new Promise(resolve => this.initializeDataLink = resolve);
+ let list = await Downloads.getList(Downloads.ALL);
+
+ await list.addView(this);
+ this._downloadsLoaded = true;
+
+ return list;
+ })();
+}
+
+DownloadsDataCtor.prototype = {
+ /**
+ * Starts receiving events for current downloads.
+ */
+ initializeDataLink() {},
+
+ /**
+ * Used by sound logic when download ends.
+ */
+ _sound: null,
+ /**
+ * Promise resolved with the underlying DownloadList object once we started
+ * receiving events for current downloads.
+ */
+ _promiseList: null,
+
+ _downloadsLoaded: null,
+
+ /**
+ * Iterator for all the available Download objects. This is empty until the
+ * data has been loaded using the JavaScript API for downloads.
+ */
+ get downloads() {
+ return this.oldDownloadStates.keys();
+ },
+
+ /**
+ * True if there are finished downloads that can be removed from the list.
+ */
+ get canRemoveFinished() {
+ for (let download of this.downloads) {
+ // Stopped, paused, and failed downloads with partial data are removed.
+ if (download.stopped && !(download.canceled && download.hasPartialData)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Asks the back-end to remove finished downloads from the list. This method
+ * is only called after the data link has been initialized.
+ */
+ removeFinished() {
+ Downloads.getList(Downloads.ALL)
+ .then(list => list.removeFinished())
+ .catch(Cu.reportError);
+ },
+
+ // Integration with the asynchronous Downloads back-end
+
+ // Download view
+ onDownloadAdded: function(download)
+ {
+ // Download objects do not store the end time of downloads, as the Downloads
+ // API does not need to persist this information for all platforms. Once a
+ // download terminates on a Desktop browser, it becomes a history download,
+ // for which the end time is stored differently, as a Places annotation.
+ download.endTime = Date.now();
+ this.oldDownloadStates.set(download,
+ DownloadsCommon.stateOfDownload(download));
+
+ download.displayName =
+ download.target.path ? OS.Path.basename(download.target.path)
+ : download.source.url;
+ this.onDownloadChanged(download);
+ if (!this._downloadsLoaded)
+ return;
+
+ var behavior = download.source.isPrivate ? 1 :
+ Services.prefs.getIntPref(PREF_DM_BEHAVIOR);
+ switch (behavior) {
+ case 0:
+ Cc["@mozilla.org/suite/suiteglue;1"]
+ .getService(Ci.nsISuiteGlue)
+ .showDownloadManager(true);
+ break;
+ case 1:
+ Services.ww.openWindow(null, PROGRESS_DIALOG_URL, null,
+ "chrome,titlebar,centerscreen,minimizable=yes,dialog=no",
+ { wrappedJSObject: download });
+ break;
+ }
+
+ return; // No UI for behavior >= 2
+ },
+
+ onDownloadChanged(download) {
+ let oldState = this.oldDownloadStates.get(download);
+ let newState = DownloadsCommon.stateOfDownload(download);
+ this.oldDownloadStates.set(download, newState);
+
+ if (oldState != newState &&
+ (download.succeeded ||
+ (download.canceled && !download.hasPartialData) ||
+ download.error)) {
+ // Store the end time that may be displayed by the views.
+ download.endTime = Date.now();
+
+ // This state transition code should actually be located in a Downloads
+ // API module (bug 941009).
+ // This might end with an exception if it is an unsupported uri scheme.
+ DownloadHistory.updateMetaData(download);
+
+ if (download.succeeded) {
+ this.playDownloadSound();
+ }
+ }
+ },
+
+ onDownloadRemoved(download) {
+ this.oldDownloadStates.delete(download);
+ },
+
+ // Download summary
+ onSummaryChanged: function() {
+
+ if (!gTaskbarProgress)
+ return;
+
+ const nsITaskbarProgress = Ci.nsITaskbarProgress;
+ var currentBytes = gDownloadsSummary.progressCurrentBytes;
+ var totalBytes = gDownloadsSummary.progressTotalBytes;
+ var state = gDownloadsSummary.allHaveStopped ?
+ currentBytes ? nsITaskbarProgress.STATE_PAUSED :
+ nsITaskbarProgress.STATE_NO_PROGRESS :
+ currentBytes < totalBytes ? nsITaskbarProgress.STATE_NORMAL :
+ nsITaskbarProgress.STATE_INDETERMINATE;
+ switch (state) {
+ case nsITaskbarProgress.STATE_NO_PROGRESS:
+ case nsITaskbarProgress.STATE_INDETERMINATE:
+ gTaskbarProgress.setProgressState(state, 0, 0);
+ break;
+ default:
+ gTaskbarProgress.setProgressState(state, currentBytes, totalBytes);
+ break;
+ }
+ },
+
+ // Play a download sound.
+ playDownloadSound: function()
+ {
+ if (Services.prefs.getBoolPref("browser.download.finished_download_sound")) {
+ if (!this._sound)
+ this._sound = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound);
+ try {
+ let url = Services.prefs.getStringPref("browser.download.finished_sound_url");
+ this._sound.play(Services.io.newURI(url));
+ } catch (e) {
+ this._sound.beep();
+ }
+ }
+ },
+
+ // Registration of views
+
+ /**
+ * Adds an object to be notified when the available download data changes.
+ * The specified object is initialized with the currently available downloads.
+ *
+ * @param aView
+ * DownloadsView object to be added. This reference must be passed to
+ * removeView before termination.
+ */
+ addView(aView) {
+ this._promiseList.then(list => list.addView(aView))
+ .catch(Cu.reportError);
+ },
+
+ /**
+ * Removes an object previously added using addView.
+ *
+ * @param aView
+ * DownloadsView object to be removed.
+ */
+ removeView(aView) {
+ this._promiseList.then(list => list.removeView(aView))
+ .catch(Cu.reportError);
+ },
+};
+
+XPCOMUtils.defineLazyGetter(this, "HistoryDownloadsData", function() {
+ return new DownloadsDataCtor({ isHistory: true });
+});
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
+ return new DownloadsDataCtor();
+});
diff --git a/comm/suite/components/downloads/DownloadsTaskbar.jsm b/comm/suite/components/downloads/DownloadsTaskbar.jsm
new file mode 100644
index 0000000000..6cefeedcca
--- /dev/null
+++ b/comm/suite/components/downloads/DownloadsTaskbar.jsm
@@ -0,0 +1,182 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et filetype=javascript
+ * 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 EXPORTED_SYMBOLS = [
+ "DownloadsTaskbar",
+];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ChromeUtils.defineModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function() {
+ if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
+ return null;
+ }
+ let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
+ .getService(Ci.nsIWinTaskbar);
+ return winTaskbar.available && winTaskbar;
+});
+
+XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function() {
+ return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
+ Cc["@mozilla.org/widget/macdocksupport;1"]
+ .getService(Ci.nsITaskbarProgress);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gGtkTaskbarProgress", function() {
+ return ("@mozilla.org/widget/taskbarprogress/gtk;1" in Cc) &&
+ Cc["@mozilla.org/widget/taskbarprogress/gtk;1"]
+ .getService(Ci.nsIGtkTaskbarProgress);
+});
+
+// DownloadsTaskbar
+
+/**
+ * Handles the download progress indicator in the taskbar.
+ */
+var DownloadsTaskbar = {
+ /**
+ * Underlying DownloadSummary providing the aggregate download information, or
+ * null if the indicator has never been initialized.
+ */
+ _summary: null,
+
+ /**
+ * nsITaskbarProgress object to which download information is dispatched.
+ * This can be null if the indicator has never been initialized or if the
+ * indicator is currently hidden on Windows.
+ */
+ _taskbarProgress: null,
+
+ /**
+ * This method is called after a new browser window is opened, and ensures
+ * that the download progress indicator is displayed in the taskbar.
+ *
+ * On Windows, the indicator is attached to the first browser window that
+ * calls this method. When the window is closed, the indicator is moved to
+ * another browser window, if available, in no particular order. When there
+ * are no browser windows visible, the indicator is hidden.
+ *
+ * On macOS, the indicator is initialized globally when this method is
+ * called for the first time. Subsequent calls have no effect.
+ *
+ * @param aBrowserWindow
+ * nsIDOMWindow object of the newly opened browser window to which the
+ * indicator may be attached.
+ */
+ registerIndicator(aWindow) {
+ if (!this._taskbarProgress) {
+ if (gMacTaskbarProgress) {
+ // On macOS, we have to register the global indicator only once.
+ this._taskbarProgress = gMacTaskbarProgress;
+ // Free the XPCOM reference on shutdown, to prevent detecting a leak.
+ Services.obs.addObserver(() => {
+ this._taskbarProgress = null;
+ gMacTaskbarProgress = null;
+ }, "quit-application-granted");
+ } else {
+ // On Windows, the indicator is currently hidden because we have no
+ // previous window, thus we should attach the indicator now.
+ // In gtk3, the window itself implements the progress interface.
+ this.attachIndicator(aWindow);
+ }
+ }
+
+ // Ensure that the DownloadSummary object will be created asynchronously.
+ if (!this._summary) {
+ Downloads.getSummary(Downloads.ALL).then(summary => {
+ // In case the method is re-entered, we simply ignore redundant
+ // invocations of the callback, instead of keeping separate state.
+ if (this._summary) {
+ return undefined;
+ }
+ this._summary = summary;
+ return this._summary.addView(this);
+ }).catch(Cu.reportError);
+ }
+ },
+
+ /**
+ * On Windows and linux attach the taskbar indicator to the specified window.
+ */
+ attachIndicator(aWindow) {
+ // If there is already a taskbarProgress this usually means the download
+ // manager became active. So clear the taskbar state first.
+ if (this._taskbarProgress) {
+ this._taskbarProgress.setProgressState(Ci.nsITaskbarProgress.STATE_NO_PROGRESS);
+ }
+
+ if (gWinTaskbar) {
+ // Activate the indicator on the specified window.
+ let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow).docShell;
+ this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
+ } else if (gGtkTaskbarProgress) {
+ // In gtk3, the window itself implements the progress interface.
+ if (!this._taskbarProgress) {
+ this._taskbarProgress = gGtkTaskbarProgress;
+ }
+
+ this._taskbarProgress.setPrimaryWindow(aWindow);
+ } else {
+ // macOS, not gtk3 or unsupported OS.
+ return;
+ }
+
+ // If the DownloadSummary object has already been created, we should update
+ // the state of the new indicator, otherwise it will be updated as soon as
+ // the DownloadSummary view is registered.
+ if (this._summary) {
+ this.onSummaryChanged();
+ }
+
+ aWindow.addEventListener("unload", () => {
+ let windows = Services.wm.getEnumerator(null);
+ let newActiveWindow = null;
+ if (windows.hasMoreElements()) {
+ newActiveWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+ }
+ if (newActiveWindow) {
+ // Move the progress indicator to the other window.
+ this.attachIndicator(newActiveWindow);
+ } else {
+ // The last window has been closed. We remove the reference to
+ // the taskbar progress object.
+ this._taskbarProgress = null;
+ }
+ });
+ },
+
+ // DownloadSummary view
+ onSummaryChanged() {
+ // If the last window has been closed, we have no indicator any more.
+ if (!this._taskbarProgress) {
+ return;
+ }
+
+ if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
+ this._taskbarProgress.setProgressState(
+ Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
+ } else {
+ // For a brief moment before completion, some download components may
+ // report more transferred bytes than the total number of bytes. Thus,
+ // ensure that we never break the expectations of the progress indicator.
+ let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
+ this._summary.progressCurrentBytes);
+ this._taskbarProgress.setProgressState(
+ Ci.nsITaskbarProgress.STATE_NORMAL,
+ progressCurrentBytes,
+ this._summary.progressTotalBytes);
+ }
+ },
+};
diff --git a/comm/suite/components/downloads/content/DownloadProgressListener.js b/comm/suite/components/downloads/content/DownloadProgressListener.js
new file mode 100644
index 0000000000..b5bab95727
--- /dev/null
+++ b/comm/suite/components/downloads/content/DownloadProgressListener.js
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/**
+ * DownloadProgressListener "class" is used to help update download items shown
+ * in the Download Manager UI such as displaying amount transferred, transfer
+ * rate, and time left for each download.
+ */
+function DownloadProgressListener() {}
+
+DownloadProgressListener.prototype = {
+ onDownloadAdded: function(aDownload) {
+ gDownloadTreeView.addDownload(aDownload);
+
+ // Update window title in-case we don't get all progress notifications
+ onUpdateProgress();
+ },
+
+ onDownloadChanged: function(aDownload) {
+ gDownloadTreeView.updateDownload(aDownload);
+
+ // Update window title
+ onUpdateProgress();
+ },
+
+ onDownloadRemoved: function(aDownload) {
+ gDownloadTreeView.removeDownload(aDownload);
+ }
+};
diff --git a/comm/suite/components/downloads/content/downloadmanager.js b/comm/suite/components/downloads/content/downloadmanager.js
new file mode 100644
index 0000000000..d390e655dd
--- /dev/null
+++ b/comm/suite/components/downloads/content/downloadmanager.js
@@ -0,0 +1,634 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ PluralForm: "resource://gre/modules/PluralForm.jsm",
+ Downloads: "resource://gre/modules/Downloads.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+ FileUtils: "resource://gre/modules/FileUtils.jsm",
+});
+
+var gDownloadTree;
+var gDownloadTreeView;
+var gDownloadList;
+var gDownloadStatus;
+var gDownloadListener;
+var gSearchBox;
+
+function dmStartup()
+{
+ Downloads.getList(Downloads.PUBLIC).then(dmAsyncStartup);
+}
+
+function dmAsyncStartup(aList)
+{
+ gDownloadList = aList;
+
+ gDownloadTree = document.getElementById("downloadTree");
+ gDownloadStatus = document.getElementById("statusbar-display");
+ gSearchBox = document.getElementById("search-box");
+
+ // Insert as first controller on the whole window
+ window.controllers.insertControllerAt(0, dlTreeController);
+
+ // We need to keep the view object around globally to access "local"
+ // non-nsITreeView methods
+ gDownloadTreeView = new DownloadTreeView();
+ gDownloadTree.view = gDownloadTreeView;
+
+ // The DownloadProgressListener (DownloadProgressListener.js) handles
+ // progress notifications.
+ gDownloadListener = new DownloadProgressListener();
+ gDownloadList.addView(gDownloadListener);
+
+ // correct keybinding command attributes which don't do our business yet
+ var key = document.getElementById("key_delete");
+ if (key.hasAttribute("command"))
+ key.setAttribute("command", "cmd_stop");
+ key = document.getElementById("key_delete2");
+ if (key.hasAttribute("command"))
+ key.setAttribute("command", "cmd_stop");
+
+ gDownloadTree.focus();
+
+ if (gDownloadTree.view.rowCount > 0)
+ gDownloadTree.view.selection.select(0);
+}
+
+function dmShutdown()
+{
+ gDownloadList.removeView(gDownloadListener);
+ window.controllers.removeController(dlTreeController);
+}
+
+function searchDownloads(aInput)
+{
+ gDownloadTreeView.searchView(aInput);
+}
+
+function sortDownloads(aEventTarget)
+{
+ var column = aEventTarget;
+ var colID = column.id;
+ var sortDirection = null;
+
+ // If the target is a menuitem, handle it and forward to a column
+ if (/^menu_SortBy/.test(colID)) {
+ colID = colID.replace(/^menu_SortBy/, "");
+ column = document.getElementById(colID);
+ var sortedColumn = gDownloadTree.columns.getSortedColumn();
+ if (sortedColumn && sortedColumn.id == colID)
+ sortDirection = sortedColumn.element.getAttribute("sortDirection");
+ else
+ sortDirection = "ascending";
+ }
+ else if (colID == "menu_Unsorted") {
+ // calling .sortView() with an "unsorted" colID returns us to original order
+ colID = "unsorted";
+ column = null;
+ sortDirection = "ascending";
+ }
+ else if (colID == "menu_SortAscending" || colID == "menu_SortDescending") {
+ sortDirection = colID.replace(/^menu_Sort/, "").toLowerCase();
+ var sortedColumn = gDownloadTree.columns.getSortedColumn();
+ if (sortedColumn) {
+ colID = sortedColumn.id;
+ column = sortedColumn.element;
+ }
+ }
+
+ // Abort if this is still no column
+ if (column && column.localName != "treecol")
+ return;
+
+ // Abort on cyler columns, we don't sort them
+ if (column && column.getAttribute("cycler") == "true")
+ return;
+
+ if (!sortDirection) {
+ // If not set above already, toggle the current direction
+ sortDirection = column.getAttribute("sortDirection") == "ascending" ?
+ "descending" : "ascending";
+ }
+
+ // Clear attributes on all columns, we're setting them again after sorting
+ for (let node = document.getElementById("Name"); node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // Actually sort the tree view
+ gDownloadTreeView.sortView(colID, sortDirection);
+
+ if (column) {
+ // Set attributes to the sorting we did
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", sortDirection);
+ }
+}
+
+async function removeDownload(aDownload)
+{
+ // Remove the associated history element first, if any, so that the views
+ // that combine history and session downloads won't resurrect the history
+ // download into the view just before it is deleted permanently.
+ try {
+ await PlacesUtils.history.remove(aDownload.source.url);
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ let list = await Downloads.getList(Downloads.ALL);
+ await list.remove(aDownload);
+ await aDownload.finalize(true);
+}
+
+function cancelDownload(aDownload)
+{
+ // This is the correct way to avoid race conditions when cancelling.
+ aDownload.cancel().catch(() => {});
+ aDownload.removePartialData().catch(Cu.reportError);
+}
+
+function openDownload(aDownload)
+{
+ let file = new FileUtils.File(aDownload.target.path);
+ DownloadsCommon.openDownloadedFile(file, null, window);
+}
+
+function showDownload(aDownload)
+{
+ let file;
+
+ if (aDownload.succeeded &&
+ aDownload.target.exists) {
+ file = new FileUtils.File(aDownload.target.path);
+ } else {
+ file = new FileUtils.File(aDownload.target.partFilePath);
+ }
+ DownloadsCommon.showDownloadedFile(file);
+}
+
+function showProperties(aDownload)
+{
+ openDialog("chrome://communicator/content/downloads/progressDialog.xul",
+ null, "chrome,titlebar,centerscreen,minimizable=yes,dialog=no",
+ { wrappedJSObject: aDownload }, true);
+}
+
+function onTreeSelect(aEvent)
+{
+ var selectionCount = gDownloadTreeView.selection.count;
+ if (selectionCount == 1) {
+ var selItemData = gDownloadTreeView.getRowData(gDownloadTree.currentIndex);
+ gDownloadStatus.label = selItemData.target.path;
+ } else {
+ gDownloadStatus.label = "";
+ }
+
+ window.updateCommands("tree-select");
+}
+
+function onUpdateViewColumns(aMenuItem)
+{
+ while (aMenuItem) {
+ // Each menuitem should be checked if its column is not hidden.
+ var colID = aMenuItem.id.replace(/^menu_Toggle/, "");
+ var column = document.getElementById(colID);
+ aMenuItem.setAttribute("checked", !column.hidden);
+ aMenuItem = aMenuItem.nextSibling;
+ }
+}
+
+function toggleColumn(aMenuItem)
+{
+ var colID = aMenuItem.id.replace(/^menu_Toggle/, "");
+ var column = document.getElementById(colID);
+ column.setAttribute("hidden", !column.hidden);
+}
+
+function onUpdateViewSort(aMenuItem)
+{
+ var unsorted = true;
+ var ascending = true;
+ while (aMenuItem) {
+ switch (aMenuItem.id) {
+ case "": // separator
+ break;
+ case "menu_Unsorted":
+ if (unsorted) // this would work even if Unsorted was last
+ aMenuItem.setAttribute("checked", "true");
+ break;
+ case "menu_SortAscending":
+ aMenuItem.setAttribute("disabled", unsorted);
+ if (!unsorted && ascending)
+ aMenuItem.setAttribute("checked", "true");
+ break;
+ case "menu_SortDescending":
+ aMenuItem.setAttribute("disabled", unsorted);
+ if (!unsorted && !ascending)
+ aMenuItem.setAttribute("checked", "true");
+ break;
+ default:
+ var colID = aMenuItem.id.replace(/^menu_SortBy/, "");
+ var column = document.getElementById(colID);
+ var direction = column.getAttribute("sortDirection");
+ if (column.getAttribute("sortActive") == "true" && direction) {
+ // We've found a sorted column. Remember its direction.
+ ascending = direction == "ascending";
+ unsorted = false;
+ aMenuItem.setAttribute("checked", "true");
+ }
+ }
+ aMenuItem = aMenuItem.nextSibling;
+ }
+}
+
+// This is called by the progress listener.
+var gLastComputedMean = -1;
+var gLastActiveDownloads = 0;
+function onUpdateProgress()
+{
+ var dls = gDownloadTreeView.getActiveDownloads();
+ var numActiveDownloads = dls.length;
+
+ // Use the default title and reset "last" values if there's no downloads
+ if (numActiveDownloads == 0) {
+ document.title = document.documentElement.getAttribute("statictitle");
+ gLastComputedMean = -1;
+ gLastActiveDownloads = 0;
+
+ return;
+ }
+
+ // Establish the mean transfer speed and amount downloaded.
+ var mean = 0;
+ var base = 0;
+ for (var dl of dls) {
+ if (dl.totalBytes > 0) {
+ mean += dl.currentBytes;
+ base += dl.totalBytes;
+ }
+ }
+
+ // Calculate the percent transferred, unless we don't have a total file size
+ var dlbundle = document.getElementById("dmBundle");
+ if (base != 0)
+ mean = Math.floor((mean / base) * 100);
+
+ // Update title of window
+ if (mean != gLastComputedMean || gLastActiveDownloads != numActiveDownloads) {
+ gLastComputedMean = mean;
+ gLastActiveDownloads = numActiveDownloads;
+
+ var title;
+ if (base == 0)
+ title = dlbundle.getFormattedString("downloadsTitleFiles",
+ [numActiveDownloads]);
+ else
+ title = dlbundle.getFormattedString("downloadsTitlePercent",
+ [numActiveDownloads, mean]);
+
+ // Get the correct plural form and insert number of downloads and percent
+ title = PluralForm.get(numActiveDownloads, title);
+
+ document.title = title;
+ }
+}
+
+function handlePaste() {
+ let trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ trans.init(null);
+
+ let flavors = ["text/x-moz-url", "text/unicode"];
+ flavors.forEach(trans.addDataFlavor);
+
+ Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
+
+ // Getting the data or creating the nsIURI might fail
+ try {
+ let data = {};
+ trans.getAnyTransferData({}, data, {});
+ let [url, name] = data.value.QueryInterface(Ci
+ .nsISupportsString).data.split("\n");
+
+ if (!url)
+ return;
+
+ DownloadURL(url, name || url, document);
+ } catch (ex) {}
+}
+
+var dlTreeController = {
+ supportsCommand: function(aCommand)
+ {
+ switch (aCommand) {
+ case "cmd_play":
+ case "cmd_pause":
+ case "cmd_resume":
+ case "cmd_retry":
+ case "cmd_cancel":
+ case "cmd_remove":
+ case "cmd_stop":
+ case "cmd_open":
+ case "cmd_show":
+ case "cmd_openReferrer":
+ case "cmd_copyLocation":
+ case "cmd_properties":
+ case "cmd_paste":
+ case "cmd_selectAll":
+ case "cmd_clearList":
+ return true;
+ }
+ return false;
+ },
+
+ isCommandEnabled: function(aCommand)
+ {
+ var selectionCount = 0;
+ if (gDownloadTreeView && gDownloadTreeView.selection)
+ selectionCount = gDownloadTreeView.selection.count;
+
+ var selItemData = [];
+ if (selectionCount) {
+ // walk all selected rows
+ let start = {};
+ let end = {};
+ let numRanges = gDownloadTreeView.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++) {
+ gDownloadTreeView.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++)
+ selItemData.push(gDownloadTreeView.getRowData(row));
+ }
+ }
+
+ switch (aCommand) {
+ case "cmd_play":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.succeeded || (!dldata.stopped && !dldata.hasPartialData))
+ return false;
+ }
+ return true;
+ case "cmd_pause":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.stopped || !dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_resume":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (!dldata.stopped || !dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_open":
+ return selectionCount == 1 &&
+ selItemData[0].succeeded &&
+ selItemData[0].target.exists;
+ case "cmd_show":
+ // target.exists is only set if the download finished and the target
+ // is still located there.
+ // For simplicity we just assume the target is there if the download
+ // has not succeeded e.g. is still in progress. This might be wrong
+ // but showDownload will deal with it.
+ return selectionCount == 1 &&
+ ((selItemData[0].succeeded &&
+ selItemData[0].target.exists) ||
+ !selItemData[0].succeeded);
+ case "cmd_cancel":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.stopped && !dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_retry":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.succeeded || !dldata.stopped || dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_remove":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (!dldata.stopped)
+ return false;
+ }
+ return true;
+ case "cmd_openReferrer":
+ return selectionCount == 1 && !!selItemData[0].source.referrer;
+ case "cmd_stop":
+ case "cmd_copyLocation":
+ return selectionCount > 0;
+ case "cmd_properties":
+ return selectionCount == 1;
+ case "cmd_selectAll":
+ return gDownloadTreeView.rowCount != selectionCount;
+ case "cmd_clearList":
+ // Since active downloads always sort before removable downloads,
+ // we only need to check that the last download has stopped.
+ return gDownloadTreeView.rowCount &&
+ !gDownloadTreeView.getRowData(gDownloadTreeView.rowCount - 1).isActive;
+ case "cmd_paste":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(aCommand) {
+ var selectionCount = 0;
+ if (gDownloadTreeView && gDownloadTreeView.selection)
+ selectionCount = gDownloadTreeView.selection.count;
+
+ var selItemData = [];
+ if (selectionCount) {
+ // walk all selected rows
+ let start = {};
+ let end = {};
+ let numRanges = gDownloadTreeView.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++) {
+ gDownloadTreeView.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++)
+ selItemData.push(gDownloadTreeView.getRowData(row));
+ }
+ }
+
+ switch (aCommand) {
+ case "cmd_play":
+ for (let dldata of selItemData) {
+ if (!dldata.stopped)
+ dldata.cancel();
+ else if (!dldata.succeeded)
+ dldata.start();
+ }
+ break;
+ case "cmd_pause":
+ for (let dldata of selItemData)
+ dldata.cancel();
+ break;
+ case "cmd_resume":
+ case "cmd_retry":
+ for (let dldata of selItemData) {
+ // Errors when retrying are already reported as download failures.
+ dldata.start();
+ }
+ break;
+ case "cmd_cancel":
+ for (let dldata of selItemData)
+ cancelDownload(dldata);
+ break;
+ case "cmd_remove":
+ for (let dldata of selItemData)
+ removeDownload(dldata).catch(Cu.reportError);
+ break;
+ case "cmd_stop":
+ for (let dldata of selItemData) {
+ if (dldata.isActive)
+ cancelDownload(dldata);
+ else
+ gDownloadList.remove(dldata);
+ }
+ break;
+ case "cmd_open":
+ openDownload(selItemData[0]);
+ break;
+ case "cmd_show":
+ showDownload(selItemData[0]);
+ break;
+ case "cmd_openReferrer":
+ openUILink(selItemData[0].source.referrer);
+ break;
+ case "cmd_copyLocation":
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ var uris = [];
+ for (let dldata of selItemData)
+ uris.push(dldata.source.url);
+ clipboard.copyString(uris.join("\n"), document);
+ break;
+ case "cmd_properties":
+ showProperties(selItemData[0]);
+ break;
+ case "cmd_selectAll":
+ gDownloadTreeView.selection.selectAll();
+ break;
+ case "cmd_clearList":
+ // Remove each download starting from the end until we hit a download
+ // that is in progress
+ for (let idx = gDownloadTreeView.rowCount - 1; idx >= 0; idx--) {
+ let dldata = gDownloadTreeView.getRowData(idx);
+ if (!dldata.isActive) {
+ gDownloadList.remove(dldata);
+ }
+ }
+
+ if (!gSearchBox.value)
+ break;
+
+ // Clear the input as if the user did it and move focus to the list
+ gSearchBox.value = "";
+ searchDownloads("");
+ gDownloadTree.focus();
+ break;
+ case "cmd_paste":
+ handlePaste();
+ break;
+ }
+ },
+
+ onEvent: function(aEvent){
+ switch (aEvent) {
+ case "tree-select":
+ this.onCommandUpdate();
+ }
+ },
+
+ onCommandUpdate: function() {
+ var cmds = ["cmd_play", "cmd_pause", "cmd_resume", "cmd_retry",
+ "cmd_cancel", "cmd_remove", "cmd_stop", "cmd_open", "cmd_show",
+ "cmd_openReferrer", "cmd_copyLocation", "cmd_properties",
+ "cmd_selectAll", "cmd_clearList"];
+ for (let command in cmds)
+ goUpdateCommand(cmds[command]);
+ }
+};
+
+var gDownloadDNDObserver = {
+ onDragStart: function (aEvent)
+ {
+ if (!gDownloadTreeView ||
+ !gDownloadTreeView.selection ||
+ !gDownloadTreeView.selection.count)
+ return;
+
+ var selItemData = gDownloadTreeView.getRowData(gDownloadTree.currentIndex);
+ var file = new FileUtils.File(selItemData.target.path);
+
+ if (!file.exists())
+ return;
+
+ var url = Services.io.newFileURI(file).spec;
+ var dt = aEvent.dataTransfer;
+ dt.mozSetDataAt("application/x-moz-file", file, 0);
+ dt.setData("text/uri-list", url + "\r\n");
+ dt.setData("text/plain", url + "\n");
+ dt.effectAllowed = "copyMove";
+ if (gDownloadTreeView.selection.count == 1)
+ dt.setDragImage(gDownloadStatus, 16, 16);
+ },
+
+ onDragOver: function (aEvent)
+ {
+ if (disallowDrop(aEvent))
+ return;
+
+ var types = aEvent.dataTransfer.types;
+ if (types.includes("text/uri-list") ||
+ types.includes("text/x-moz-url") ||
+ types.includes("text/plain"))
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ },
+
+ onDrop: function(aEvent)
+ {
+ if (disallowDrop(aEvent))
+ return;
+
+ var dt = aEvent.dataTransfer;
+ var url = dt.getData("URL");
+ var name;
+ if (!url) {
+ url = dt.getData("text/x-moz-url") || dt.getData("text/plain");
+ [url, name] = url.split("\n");
+ }
+ if (url) {
+ let doc = dt.mozSourceNode ? dt.mozSourceNode.ownerDocument : document;
+ saveURL(url, name || url, null, true, true, null, doc);
+ }
+ }
+};
+
+function disallowDrop(aEvent)
+{
+ var dt = aEvent.dataTransfer;
+ var file = dt.mozGetDataAt("application/x-moz-file", 0);
+ // If this is a local file, Don't try to download it again.
+ return file && file instanceof Ci.nsIFile;
+}
diff --git a/comm/suite/components/downloads/content/downloadmanager.xul b/comm/suite/components/downloads/content/downloadmanager.xul
new file mode 100644
index 0000000000..5633d284b6
--- /dev/null
+++ b/comm/suite/components/downloads/content/downloadmanager.xul
@@ -0,0 +1,452 @@
+<?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://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/downloads/downloadmanager.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % downloadsDTD SYSTEM "chrome://communicator/locale/downloads/downloadmanager.dtd">
+%downloadsDTD;
+<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+%globalDTD;
+]>
+
+<window id="downloadManager"
+ title="&downloadManager.title;" statictitle="&downloadManager.title;"
+ onload="dmStartup();" onunload="dmShutdown();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="500" height="400" screenX="10" screenY="10"
+ persist="width height screenX screenY sizemode"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ drawtitle="true"
+ windowtype="Download:Manager">
+
+ <script src="chrome://communicator/content/downloads/downloadmanager.js"/>
+ <script src="chrome://communicator/content/downloads/DownloadProgressListener.js"/>
+ <script src="chrome://communicator/content/downloads/treeView.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+
+ <broadcaster id="Communicator:WorkMode"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="dmBundle"
+ src="chrome://communicator/locale/downloads/downloadmanager.properties"/>
+ </stringbundleset>
+
+ <commandset id="dlWinCommands">
+ <commandset id="tasksCommands">
+ <!-- File Menu -->
+ <command id="cmd_close" oncommand="window.close()"/>
+ <!-- Search Box -->
+ <command id="cmd_search_focus"
+ oncommand="gSearchBox.focus();"/>
+ </commandset>
+ <commandset id="commandUpdate_Downloads"
+ commandupdater="true"
+ events="focus,tree-select"
+ oncommandupdate="dlTreeController.onCommandUpdate()"/>
+
+ <commandset id="downloadCommands">
+ <command id="cmd_play"
+ oncommand="goDoCommand('cmd_play');"/>
+ <command id="cmd_pause"
+ oncommand="goDoCommand('cmd_pause');"/>
+ <command id="cmd_resume"
+ oncommand="goDoCommand('cmd_resume');"/>
+ <command id="cmd_retry"
+ oncommand="goDoCommand('cmd_retry');"/>
+ <command id="cmd_cancel"
+ oncommand="goDoCommand('cmd_cancel');"/>
+ <command id="cmd_remove"
+ oncommand="goDoCommand('cmd_remove');"/>
+ <command id="cmd_stop"
+ oncommand="goDoCommand('cmd_stop');"/>
+ <command id="cmd_open"
+ oncommand="goDoCommand('cmd_open');"/>
+ <command id="cmd_show"
+ oncommand="goDoCommand('cmd_show');"/>
+ <command id="cmd_openReferrer"
+ oncommand="goDoCommand('cmd_openReferrer');"/>
+ <command id="cmd_copyLocation"
+ oncommand="goDoCommand('cmd_copyLocation');"/>
+ <command id="cmd_properties"
+ oncommand="goDoCommand('cmd_properties');"/>
+ <command id="cmd_clearList"
+ oncommand="goDoCommand('cmd_clearList');"/>
+ </commandset>
+ </commandset>
+
+ <keyset id="tasksKeys">
+ <!-- File Menu -->
+ <key id="key_open"
+ keycode="VK_RETURN"
+ command="cmd_open"/>
+ <key id="key_close"/>
+ <!-- Edit Menu -->
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"
+ command="cmd_paste"/>
+ <key id="key_play"
+ key=" "
+ command="cmd_play"/>
+ <key id="key_delete"/>
+ <key id="key_delete2"/>
+ <key id="key_selectAll"/>
+ <!-- Search Box -->
+ <key id="key_search_focus"
+ command="cmd_search_focus"
+ key="&search.key;"
+ modifiers="accel"/>
+ </keyset>
+
+ <popupset id="downloadPopupset">
+ <menupopup id="downloadContext">
+ <menuitem id="dlContext-pause"
+ label="&cmd.pause.label;"
+ accesskey="&cmd.pause.accesskey;"
+ command="cmd_pause"/>
+ <menuitem id="dlContext-resume"
+ label="&cmd.resume.label;"
+ accesskey="&cmd.resume.accesskey;"
+ command="cmd_resume"/>
+ <menuitem id="dlContext-retry"
+ label="&cmd.retry.label;"
+ accesskey="&cmd.retry.accesskey;"
+ command="cmd_retry"/>
+ <menuitem id="dlContext-cancel"
+ label="&cmd.cancel.label;"
+ accesskey="&cmd.cancel.accesskey;"
+ command="cmd_cancel"/>
+ <menuitem id="dlContext-remove"
+ label="&cmd.remove.label;"
+ accesskey="&cmd.remove.accesskey;"
+ command="cmd_remove"/>
+ <menuseparator/>
+ <menuitem id="dlContext-open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ command="cmd_open"
+ default="true"/>
+ <menuitem id="dlContext-show"
+ label="&cmd.show.label;"
+ accesskey="&cmd.show.accesskey;"
+ command="cmd_show"/>
+ <menuitem id="dlContext-openReferrer"
+ label="&cmd.goToDownloadPage.label;"
+ accesskey="&cmd.goToDownloadPage.accesskey;"
+ command="cmd_openReferrer"/>
+ <menuitem id="dlContext-copyLocation"
+ label="&cmd.copyDownloadLink.label;"
+ accesskey="&cmd.copyDownloadLink.accesskey;"
+ command="cmd_copyLocation"/>
+ <menuitem id="dlContext-properties"
+ label="&cmd.properties.label;"
+ accesskey="&cmd.properties.accesskey;"
+ command="cmd_properties"/>
+ <menuseparator/>
+ <menuitem id="context-selectall"/>
+ </menupopup>
+ </popupset>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="download-toolbox">
+ <menubar id="download-menubar"
+ grippytooltiptext="&menuBar.tooltip;">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup">
+ <menuitem id="dlMenu_open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ key="key_open"
+ command="cmd_open"/>
+ <menuitem id="dlMenu_show"
+ label="&cmd.show.label;"
+ accesskey="&cmd.show.accesskey;"
+ command="cmd_show"/>
+ <menuitem id="dlMenu_openReferrer"
+ label="&cmd.goToDownloadPage.label;"
+ accesskey="&cmd.goToDownloadPage.accesskey;"
+ command="cmd_openReferrer"/>
+ <menuitem id="dlMenu_properties"
+ label="&cmd.properties.label;"
+ accesskey="&cmd.properties.accesskey;"
+ command="cmd_properties"/>
+ <menuseparator/>
+ <menuitem id="menu_close"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_Edit">
+ <menupopup id="menu_EditPopup">
+ <menuitem id="dlMenu_pause"
+ label="&cmd.pause.label;"
+ accesskey="&cmd.pause.accesskey;"
+ command="cmd_pause"/>
+ <menuitem id="dlMenu_resume"
+ label="&cmd.resume.label;"
+ accesskey="&cmd.resume.accesskey;"
+ command="cmd_resume"/>
+ <menuitem id="dlMenu_retry"
+ label="&cmd.retry.label;"
+ accesskey="&cmd.retry.accesskey;"
+ command="cmd_retry"/>
+ <menuitem id="dlMenu_cancel"
+ label="&cmd.cancel.label;"
+ accesskey="&cmd.cancel.accesskey;"
+ command="cmd_cancel"/>
+ <menuseparator/>
+ <menuitem id="dlMenu_remove"
+ label="&cmd.remove.label;"
+ accesskey="&cmd.remove.accesskey;"
+ command="cmd_remove"/>
+ <menuitem id="dlMenu_copyLocation"
+ label="&cmd.copyDownloadLink.label;"
+ accesskey="&cmd.copyDownloadLink.accesskey;"
+ command="cmd_copyLocation"/>
+ <menuseparator/>
+ <menuitem id="dlMenu_clearList"
+ label="&cmd.clearList.label;"
+ accesskey="&cmd.clearList.accesskey;"
+ command="cmd_clearList"/>
+ <menuitem id="menu_selectAll"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_View">
+ <menupopup id="menu_ViewPopup">
+ <menu id="menu_ViewColumns"
+ label="&view.columns.label;"
+ accesskey="&view.columns.accesskey;">
+ <menupopup onpopupshowing="onUpdateViewColumns(this.firstChild);"
+ oncommand="toggleColumn(event.target);">
+ <menuitem id="menu_ToggleName" type="checkbox" disabled="true"
+ label="&col.name.label;"
+ accesskey="&col.name.accesskey;"/>
+ <menuitem id="menu_ToggleStatus" type="checkbox"
+ label="&col.status.label;"
+ accesskey="&col.status.accesskey;"/>
+ <menuitem id="menu_ToggleActionPlay" type="checkbox"
+ label="&col.actionPlay.label;"
+ accesskey="&col.actionPlay.accesskey;"/>
+ <menuitem id="menu_ToggleActionStop" type="checkbox"
+ label="&col.actionStop.label;"
+ accesskey="&col.actionStop.accesskey;"/>
+ <menuitem id="menu_ToggleProgress" type="checkbox"
+ label="&col.progress.label;"
+ accesskey="&col.progress.accesskey;"/>
+ <menuitem id="menu_ToggleTimeRemaining" type="checkbox"
+ label="&col.timeremaining.label;"
+ accesskey="&col.timeremaining.accesskey;"/>
+ <menuitem id="menu_ToggleTransferred" type="checkbox"
+ label="&col.transferred.label;"
+ accesskey="&col.transferred.accesskey;"/>
+ <menuitem id="menu_ToggleTransferRate" type="checkbox"
+ label="&col.transferrate.label;"
+ accesskey="&col.transferrate.accesskey;"/>
+ <menuitem id="menu_ToggleTimeElapsed" type="checkbox"
+ label="&col.timeelapsed.label;"
+ accesskey="&col.timeelapsed.accesskey;"/>
+ <menuitem id="menu_ToggleStartTime" type="checkbox"
+ label="&col.starttime.label;"
+ accesskey="&col.starttime.accesskey;"/>
+ <menuitem id="menu_ToggleEndTime" type="checkbox"
+ label="&col.endtime.label;"
+ accesskey="&col.endtime.accesskey;"/>
+ <menuitem id="menu_ToggleProgressPercent" type="checkbox"
+ label="&col.progresstext.label;"
+ accesskey="&col.progresstext.accesskey;"/>
+ <menuitem id="menu_ToggleSource" type="checkbox"
+ label="&col.source.label;"
+ accesskey="&col.source.accesskey;"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_ViewSortBy" label="&view.sortBy.label;"
+ accesskey="&view.sortBy.accesskey;">
+ <menupopup onpopupshowing="onUpdateViewSort(this.firstChild);"
+ oncommand="sortDownloads(event.target);">
+ <menuitem id="menu_Unsorted" type="radio" name="columns"
+ label="&view.unsorted.label;"
+ accesskey="&view.unsorted.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="menu_SortByName" type="radio" name="columns"
+ label="&col.name.label;"
+ accesskey="&col.name.accesskey;"/>
+ <menuitem id="menu_SortByStatus" type="radio" name="columns"
+ label="&col.status.label;"
+ accesskey="&col.status.accesskey;"/>
+ <menuitem id="menu_SortByProgress" type="radio" name="columns"
+ label="&col.progress.label;"
+ accesskey="&col.progress.accesskey;"/>
+ <menuitem id="menu_SortByTimeRemaining" type="radio" name="columns"
+ label="&col.timeremaining.label;"
+ accesskey="&col.timeremaining.accesskey;"/>
+ <menuitem id="menu_SortByTransferred" type="radio" name="columns"
+ label="&col.transferred.label;"
+ accesskey="&col.transferred.accesskey;"/>
+ <menuitem id="menu_SortByTransferRate" type="radio" name="columns"
+ label="&col.transferrate.label;"
+ accesskey="&col.transferrate.accesskey;"/>
+ <menuitem id="menu_SortByTimeElapsed" type="radio" name="columns"
+ label="&col.timeelapsed.label;"
+ accesskey="&col.timeelapsed.accesskey;"/>
+ <menuitem id="menu_SortByStartTime" type="radio" name="columns"
+ label="&col.starttime.label;"
+ accesskey="&col.starttime.accesskey;"/>
+ <menuitem id="menu_SortByEndTime" type="radio" name="columns"
+ label="&col.endtime.label;"
+ accesskey="&col.endtime.accesskey;"/>
+ <menuitem id="menu_SortByProgressPercent" type="radio" name="columns"
+ label="&col.progresstext.label;"
+ accesskey="&col.progresstext.accesskey;"/>
+ <menuitem id="menu_SortBySource" type="radio" name="columns"
+ label="&col.source.label;"
+ accesskey="&col.source.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="menu_SortAscending" type="radio" name="direction"
+ label="&view.sortAscending.label;"
+ accesskey="&view.sortAscending.accesskey;"/>
+ <menuitem id="menu_SortDescending" type="radio" name="direction"
+ label="&view.sortDescending.label;"
+ accesskey="&view.sortDescending.accesskey;"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ <menu id="tasksMenu">
+ <menupopup id="taskPopup">
+ <menuitem id="dlMenu_find"
+ label="&search.label;"
+ accesskey="&search.accesskey;"
+ hidden="true"
+ command="cmd_search_focus"
+ key="key_search_focus"/>
+ <menuseparator hidden="true"/>
+ </menupopup>
+ </menu>
+ <menu id="windowMenu"/>
+ <menu id="menu_Help"/>
+ </menubar>
+ <toolbar class="chromeclass-toolbar"
+ id="downloadToolbar"
+ align="center"
+ grippytooltiptext="&searchBar.tooltip;">
+ <textbox id="search-box"
+ clickSelectsAll="true"
+ type="search"
+ hidden="true"
+ aria-controls="downloadTree"
+ class="compact"
+ placeholder="&search.placeholder;"
+ oncommand="searchDownloads(this.value);"/>
+ <spacer flex="1"/>
+ <button id="clearListButton" command="cmd_clearList"
+ label="&cmd.clearList.label;"
+ accesskey="&cmd.clearList.accesskey;"
+ tooltiptext="&cmd.clearList.tooltip;"/>
+ </toolbar>
+ </toolbox>
+
+ <tree id="downloadTree"
+ flex="1" type="downloads"
+ class="plain"
+ context="downloadContext"
+ enableColumnDrag="true"
+ onselect="onTreeSelect(event);">
+ <treecols context="" onclick="sortDownloads(event.target)">
+ <treecol id="Name"
+ label="&col.name.label;"
+ tooltiptext="&col.name.tooltip;"
+ flex="3"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Status" hidden="true"
+ label="&col.status.label;"
+ tooltiptext="&col.status.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ActionPlay" cycler="true"
+ label="&col.actionPlay.label;"
+ tooltiptext="&col.actionPlay.tooltip;"
+ class="treecol-image" fixed="true"
+ persist="hidden ordinal"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ActionStop" cycler="true"
+ label="&col.actionStop.label;"
+ tooltiptext="&col.actionStop.tooltip;"
+ class="treecol-image" fixed="true"
+ persist="hidden ordinal"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Progress" type="progressmeter"
+ label="&col.progress.label;"
+ tooltiptext="&col.progress.tooltip;"
+ flex="3"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ProgressPercent" hidden="true"
+ label="&col.progresstext.label;"
+ tooltiptext="&col.progresstext.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="TimeRemaining"
+ label="&col.timeremaining.label;"
+ tooltiptext="&col.timeremaining.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Transferred"
+ label="&col.transferred.label;"
+ tooltiptext="&col.transferred.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="TransferRate"
+ label="&col.transferrate.label;"
+ tooltiptext="&col.transferrate.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="TimeElapsed" hidden="true"
+ label="&col.timeelapsed.label;"
+ tooltiptext="&col.timeelapsed.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="StartTime" hidden="true"
+ label="&col.starttime.label;"
+ tooltiptext="&col.starttime.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="EndTime" hidden="true"
+ label="&col.endtime.label;"
+ tooltiptext="&col.endtime.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Source" hidden="true"
+ label="&col.source.label;"
+ tooltiptext="&col.source.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ </treecols>
+ <treechildren ondblclick="goDoCommand('cmd_open');"
+ ondragstart="gDownloadDNDObserver.onDragStart(event);"
+ ondragover="gDownloadDNDObserver.onDragOver(event);"
+ ondrop="gDownloadDNDObserver.onDrop(event);"/>
+ </tree>
+ <statusbar id="status-bar" class="chromeclass-status">
+ <statusbarpanel id="statusbar-display" flex="1"/>
+ <statusbarpanel class="statusbarpanel-iconic" id="offline-status"/>
+ </statusbar>
+
+</window>
diff --git a/comm/suite/components/downloads/content/progressDialog.js b/comm/suite/components/downloads/content/progressDialog.js
new file mode 100644
index 0000000000..dae93f7fe9
--- /dev/null
+++ b/comm/suite/components/downloads/content/progressDialog.js
@@ -0,0 +1,240 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+});
+
+var gDownload;
+var gDownloadBundle;
+
+var gDlList;
+var gDlStatus;
+var gDlListener;
+var gDlSize;
+var gTimeLeft;
+var gProgressMeter;
+var gProgressText;
+var gCloseWhenDone;
+
+function progressStartup() {
+ gDownload = window.arguments[0].wrappedJSObject;
+ Downloads.getList(gDownload.source.isPrivate ? Downloads.PRIVATE : Downloads.PUBLIC).then(progressAsyncStartup);
+}
+
+function progressAsyncStartup(aList) {
+ gDlList = aList;
+
+ // cache elements to save .getElementById() calls
+ gDownloadBundle = document.getElementById("dmBundle");
+ gDlStatus = document.getElementById("dlStatus");
+ gDlSize = document.getElementById("dlSize");
+ gTimeLeft = document.getElementById("timeLeft");
+ gProgressMeter = document.getElementById("progressMeter");
+ gProgressText = document.getElementById("progressText");
+ gCloseWhenDone = document.getElementById("closeWhenDone");
+
+ // Insert as first controller on the whole window
+ window.controllers.insertControllerAt(0, ProgressDlgController);
+
+ if (gDownload.isPrivate)
+ gCloseWhenDone.hidden = true;
+ else
+ gCloseWhenDone.checked = Services.prefs.getBoolPref("browser.download.progress.closeWhenDone");
+
+ if (gDownload.succeeded) {
+ if (gCloseWhenDone.checked && !window.arguments[1])
+ window.close();
+ }
+
+ var fName = document.getElementById("fileName");
+ var fSource = document.getElementById("fileSource");
+ fName.label = gDownload.displayName;
+ fName.tooltipText = gDownload.target.path;
+ var uri = Services.io.newURI(gDownload.source.url);
+ var fromString;
+ try {
+ fromString = uri.host;
+ }
+ catch (e) { }
+ if (!fromString)
+ fromString = uri.prePath;
+ fSource.label = gDownloadBundle.getFormattedString("fromSource", [fromString]);
+ fSource.tooltipText = gDownload.source.url;
+
+ // The DlProgressListener handles progress notifications.
+ gDlListener = new DlProgressListener();
+ gDlList.addView(gDlListener);
+
+ updateDownload();
+ updateButtons();
+ window.updateCommands("dlstate-change");
+}
+
+function progressShutdown() {
+ gDlList.removeView(gDlListener);
+ window.controllers.removeController(ProgressDlgController);
+ if (!gCloseWhenDone.hidden)
+ Services.prefs.setBoolPref("browser.download.progress.closeWhenDone",
+ gCloseWhenDone.checked);
+}
+
+function updateDownload() {
+ if (gDownload.hasProgress) {
+ gProgressText.value = gDownloadBundle.getFormattedString("percentFormat",
+ [gDownload.progress]);
+ gProgressText.hidden = false;
+ gProgressMeter.value = gDownload.progress;
+ gProgressMeter.mode = "determined";
+ } else {
+ gProgressText.hidden = true;
+ gProgressMeter.mode = "undetermined";
+ }
+ if (gDownload.stopped) {
+ gProgressMeter.style.opacity = 0.5;
+ } else {
+ gProgressMeter.style.opacity = 1;
+ }
+ // Update window title
+ let statusString = DownloadsCommon.stateOfDownloadText(gDownload);
+
+ if (gDownload.hasProgress) {
+ document.title = gDownloadBundle.getFormattedString("progressTitlePercent",
+ [gDownload.progress,
+ gDownload.displayName,
+ statusString]);
+ }
+ else {
+ document.title = gDownloadBundle.getFormattedString("progressTitle",
+ [gDownload.displayName,
+ statusString]);
+ }
+
+ // download size / transferred bytes
+ gDlSize.value = DownloadsCommon.getTransferredBytes(gDownload);
+
+ // time remaining
+ gTimeLeft.value = DownloadsCommon.getTimeRemaining(gDownload);
+
+ // download status
+ gDlStatus.value = statusString;
+
+}
+
+function updateButtons() {
+ document.getElementById("pauseButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_pause");
+ document.getElementById("resumeButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_resume");
+ document.getElementById("retryButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_retry");
+ document.getElementById("cancelButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_cancel");
+}
+
+/**
+ * DlProgressListener "class" is used to help update download items shown
+ * in the progress dialog such as displaying amount transferred, transfer
+ * rate, and time left for the download.
+ *
+ * This class implements the downloadProgressListener interface.
+ */
+function DlProgressListener() {}
+
+DlProgressListener.prototype = {
+ onDownloadChanged: function(aDownload) {
+ if (aDownload == gDownload) {
+ if (gCloseWhenDone.checked && aDownload.succeeded) {
+ window.close();
+ }
+ updateDownload();
+ updateButtons();
+ window.updateCommands("dlstate-change");
+ }
+ },
+
+ onDownloadRemoved: function(aDownload) {
+ if (aDownload == gDownload)
+ window.close();
+ }
+};
+
+var ProgressDlgController = {
+ supportsCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pause":
+ case "cmd_resume":
+ case "cmd_retry":
+ case "cmd_cancel":
+ case "cmd_open":
+ case "cmd_show":
+ case "cmd_openReferrer":
+ case "cmd_copyLocation":
+ return true;
+ }
+ return false;
+ },
+
+ isCommandEnabled: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pause":
+ return !gDownload.stopped && gDownload.hasPartialData;
+ case "cmd_resume":
+ return gDownload.stopped && gDownload.hasPartialData;
+ case "cmd_open":
+ return gDownload.succeeded && gDownload.target.exists;
+ case "cmd_show":
+ return gDownload.target.exists;
+ case "cmd_cancel":
+ return !gDownload.stopped || gDownload.hasPartialData;
+ case "cmd_retry":
+ return !gDownload.succeeded && gDownload.stopped && !gDownload.hasPartialData;
+ case "cmd_openReferrer":
+ return !!gDownload.source.referrer;
+ case "cmd_copyLocation":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pause":
+ gDownload.cancel();
+ break;
+ case "cmd_resume":
+ case "cmd_retry":
+ gDownload.start();
+ break;
+ case "cmd_cancel":
+ cancelDownload(gDownload);
+ break;
+ case "cmd_open":
+ openDownload(gDownload);
+ break;
+ case "cmd_show":
+ showDownload(gDownload);
+ break;
+ case "cmd_openReferrer":
+ openUILink(gDownload.source.referrer);
+ break;
+ case "cmd_copyLocation":
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(gDownload.source.url);
+ break;
+ }
+ },
+
+ onEvent: function(aEvent) {
+ },
+
+ onCommandUpdate: function() {
+ var cmds = ["cmd_pause", "cmd_resume", "cmd_retry", "cmd_cancel",
+ "cmd_open", "cmd_show", "cmd_openReferrer", "cmd_copyLocation"];
+ for (let command in cmds)
+ goUpdateCommand(cmds[command]);
+ }
+};
diff --git a/comm/suite/components/downloads/content/progressDialog.xul b/comm/suite/components/downloads/content/progressDialog.xul
new file mode 100644
index 0000000000..cb8178f6fd
--- /dev/null
+++ b/comm/suite/components/downloads/content/progressDialog.xul
@@ -0,0 +1,108 @@
+<?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://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/downloads/downloadmanager.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE window SYSTEM "chrome://communicator/locale/downloads/progressDialog.dtd">
+
+<window id="dlProgressWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="progressStartup();" onunload="progressShutdown();"
+ title="&progress.title;"
+ persist="screenX screenY"
+ style="width:40em;">
+
+ <script src="chrome://communicator/content/downloads/downloadmanager.js"/>
+ <script src="chrome://communicator/content/downloads/progressDialog.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="dmBundle"
+ src="chrome://communicator/locale/downloads/downloadmanager.properties"/>
+ </stringbundleset>
+
+ <commandset id="dlProgressCommands">
+ <commandset id="commandUpdate_DlProgress"
+ commandupdater="true"
+ events="focus,dlstate-change"
+ oncommandupdate="ProgressDlgController.onCommandUpdate();"/>
+
+ <commandset id="downloadCommands">
+ <command id="cmd_pause"
+ oncommand="goDoCommand('cmd_pause');"/>
+ <command id="cmd_resume"
+ oncommand="goDoCommand('cmd_resume');"/>
+ <command id="cmd_retry"
+ oncommand="goDoCommand('cmd_retry');"/>
+ <command id="cmd_cancel"
+ oncommand="goDoCommand('cmd_cancel');"/>
+ <command id="cmd_open"
+ oncommand="goDoCommand('cmd_open');"/>
+ <command id="cmd_show"
+ oncommand="goDoCommand('cmd_show');"/>
+ <command id="cmd_openReferrer"
+ oncommand="goDoCommand('cmd_openReferrer');"/>
+ <command id="cmd_copyLocation"
+ oncommand="goDoCommand('cmd_copyLocation');"/>
+ <command id="cmd_close" oncommand="window.close();"/>
+ </commandset>
+ </commandset>
+
+ <keyset>
+ <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/>
+ <key keycode="VK_ESCAPE" command="cmd_close"/>
+ <key key="." modifiers="meta" command="cmd_close"/>
+ </keyset>
+
+ <hbox align="end">
+ <vbox flex="1" align="start">
+ <button id="fileName" crop="center" label="" type="menu">
+ <menupopup id="file-popup">
+ <menuitem id="dlContext-open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ command="cmd_open"/>
+ <menuitem id="dlContext-show"
+ label="&cmd.show.label;"
+ accesskey="&cmd.show.accesskey;"
+ command="cmd_show"/>
+ </menupopup>
+ </button>
+ <button id="fileSource" crop="center" label="" type="menu">
+ <menupopup id="source-popup">
+ <menuitem id="dlContext-openReferrer"
+ label="&cmd.goToDownloadPage.label;"
+ accesskey="&cmd.goToDownloadPage.accesskey;"
+ command="cmd_openReferrer"/>
+ <menuitem id="dlContext-copyLocation"
+ label="&cmd.copyDownloadLink.label;"
+ accesskey="&cmd.copyDownloadLink.accesskey;"
+ command="cmd_copyLocation"/>
+ </menupopup>
+ </button>
+ <label id="dlSize" value=""/>
+ <label id="timeLeft" value=""/>
+ <label id="dlStatus" value=""/>
+ </vbox>
+ <button id="pauseButton" class="mini-button"
+ command="cmd_pause" tooltiptext="&cmd.pause.tooltip;"/>
+ <button id="resumeButton" class="mini-button"
+ command="cmd_resume" tooltiptext="&cmd.resume.tooltip;"/>
+ <button id="retryButton" class="mini-button"
+ command="cmd_retry" tooltiptext="&cmd.retry.tooltip;"/>
+ <button id="cancelButton" class="mini-button"
+ command="cmd_cancel" tooltiptext="&cmd.cancel.tooltip;"/>
+ </hbox>
+ <hbox id="progressBox">
+ <progressmeter id="progressMeter" mode="determined" flex="1"/>
+ <label id="progressText" value=""/>
+ </hbox>
+ <checkbox id="closeWhenDone"
+ label="&closeWhenDone.label;"
+ accesskey="&closeWhenDone.accesskey;"/>
+</window>
diff --git a/comm/suite/components/downloads/content/treeView.js b/comm/suite/components/downloads/content/treeView.js
new file mode 100644
index 0000000000..03e1c48a11
--- /dev/null
+++ b/comm/suite/components/downloads/content/treeView.js
@@ -0,0 +1,483 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+ DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
+});
+
+function DownloadTreeView() {
+ this._dlList = [];
+ this._searchTerms = [];
+ this.dateTimeFormatter =
+ new Services.intl.DateTimeFormat(undefined,
+ {dateStyle: "short",
+ timeStyle: "long"});
+}
+
+DownloadTreeView.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView]),
+
+ // ***** nsITreeView attributes and methods *****
+ get rowCount() {
+ return this._dlList.length;
+ },
+
+ selection: null,
+
+ getRowProperties: function(aRow) {
+ let dl = this._dlList[aRow];
+ // (in)active
+ let properties = dl.isActive ? "active": "inactive";
+ // resumable
+ if (dl.hasPartialData)
+ properties += " resumable";
+
+ // Download states
+ let state = DownloadsCommon.stateOfDownload(dl);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ properties += " paused";
+ break;
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ properties += " downloading";
+ break;
+ case DownloadsCommon.DOWNLOAD_FINISHED:
+ properties += " finished";
+ break;
+ case DownloadsCommon.DOWNLOAD_FAILED:
+ properties += " failed";
+ break;
+ case DownloadsCommon.DOWNLOAD_CANCELED:
+ properties += " canceled";
+ break;
+ case DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL: // Parental Controls
+ case DownloadsCommon.DOWNLOAD_BLOCKED_POLICY: // Security Zone Policy
+ case DownloadsCommon.DOWNLOAD_DIRTY: // possible virus/spyware
+ properties += " blocked";
+ break;
+ }
+
+ return properties;
+ },
+ getCellProperties: function(aRow, aColumn) {
+ // Append all row properties to the cell
+ return this.getRowProperties(aRow);
+ },
+ getColumnProperties: function(aColumn) { return ""; },
+ isContainer: function(aRow) { return false; },
+ isContainerOpen: function(aRow) { return false; },
+ isContainerEmpty: function(aRow) { return false; },
+ isSeparator: function(aRow) { return false; },
+ isSorted: function() { return false; },
+ canDrop: function(aIdx, aOrientation) { return false; },
+ drop: function(aIdx, aOrientation) { },
+ getParentIndex: function(aRow) { return -1; },
+ hasNextSibling: function(aRow, aAfterIdx) { return false; },
+ getLevel: function(aRow) { return 0; },
+
+ getImageSrc: function(aRow, aColumn) {
+ if (aColumn.id == "Name")
+ return "moz-icon://" + this._dlList[aRow].target.path + "?size=16";
+ return "";
+ },
+
+ getProgressMode: function(aRow, aColumn) {
+ if (aColumn.id == "Progress")
+ return this._dlList[aRow].progressMode;
+ return Ci.nsITreeView.PROGRESS_NONE;
+ },
+
+ getCellValue: function(aRow, aColumn) {
+ if (aColumn.id == "Progress")
+ return this._dlList[aRow].progress;
+ return "";
+ },
+
+ getCellText: function(aRow, aColumn) {
+ let dl = this._dlList[aRow];
+ switch (aColumn.id) {
+ case "Name":
+ return dl.displayName;
+ case "Status":
+ return DownloadsCommon.stateOfDownloadText(dl);
+ case "Progress":
+ if (dl.isActive)
+ return dl.progress;
+ return DownloadsCommon.stateOfDownloadText(dl);
+ case "ProgressPercent":
+ return dl.succeeded ? 100 : dl.progress;
+ case "TimeRemaining":
+ return DownloadsCommon.getTimeRemaining(dl);
+ case "Transferred":
+ return DownloadsCommon.getTransferredBytes(dl);
+ case "TransferRate":
+ let state = DownloadsCommon.stateOfDownload(dl);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ let [rate, unit] = DownloadUtils.convertByteUnits(dl.speed);
+ return this._dlbundle.getFormattedString("speedFormat", [rate, unit]);
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ return this._dlbundle.getString("statePaused");
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ return this._dlbundle.getString("stateNotStarted");
+ }
+ return "";
+ case "TimeElapsed":
+ // With no end time persisted in the downloads backend this is
+ // utterly useless unless the download is progressing.
+ if (DownloadsCommon.stateOfDownload(dl) ==
+ DownloadsCommon.DOWNLOAD_DOWNLOADING && dl.startTime) {
+ let seconds = (Date.now() - dl.startTime) / 1000;
+ let [time1, unit1, time2, unit2] =
+ DownloadUtils.convertTimeUnits(seconds);
+ if (seconds < 3600 || time2 == 0) {
+ return this._dlbundle.getFormattedString("timeSingle", [time1, unit1]);
+ }
+ return this._dlbundle.getFormattedString("timeDouble", [time1, unit1, time2, unit2]);
+ }
+ return "";
+ case "StartTime":
+ if (dl.startTime) {
+ return this.dateTimeFormatter.format(dl.startTime);
+ }
+ return "";
+ case "EndTime":
+ // This might end with an exception if it is an unsupported uri
+ // scheme.
+ let metaData = DownloadHistory.getPlacesMetaDataFor(dl.source.url);
+
+ if (metaData.endTime) {
+ return this.dateTimeFormatter.format(metaData.endTime);
+ }
+ return "";
+ case "Source":
+ return dl.source.url;
+ }
+ return "";
+ },
+
+ setTree: function(aTree) {
+ this._tree = aTree;
+ this._dlbundle = document.getElementById("dmBundle");
+ },
+
+ toggleOpenState: function(aRow) { },
+ cycleHeader: function(aColumn) { },
+ selectionChanged: function() { },
+ cycleCell: function(aRow, aColumn) {
+ var dl = this._dlList[aRow];
+ switch (aColumn.id) {
+ case "ActionPlay":
+ if (dl.stopped) {
+ if (!dl.succeeded)
+ dl.start();
+ } else {
+ if (dl.hasPartialData)
+ dl.cancel();
+ }
+ break;
+ case "ActionStop":
+ if (dl.isActive)
+ cancelDownload(dl);
+ else
+ removeDownload(dl);
+ break;
+ }
+ },
+ isEditable: function(aRow, aColumn) { return false; },
+ isSelectable: function(aRow, aColumn) { return false; },
+ setCellValue: function(aRow, aColumn, aText) { },
+ setCellText: function(aRow, aColumn, aText) { },
+
+ // ***** local public methods *****
+
+ addDownload: function(aDownload) {
+ aDownload.progressMode = Ci.nsITreeView.PROGRESS_NONE;
+ aDownload.lastSec = Infinity;
+ let state = DownloadsCommon.stateOfDownload(aDownload);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ aDownload.endTime = Date.now();
+ // At this point, we know if we are an indeterminate download or not.
+ aDownload.progressMode = aDownload.hasProgress ?
+ Ci.nsITreeView.PROGRESS_UNDETERMINED :
+ Ci.nsITreeView.PROGRESS_NORMAL;
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ aDownload.isActive = 1;
+ break;
+ default:
+ aDownload.isActive = 0;
+ break;
+ }
+
+ // prepend in natural sorting
+ aDownload.listIndex = this._lastListIndex--;
+
+ // Prepend data to the download list
+ this._dlList.unshift(aDownload);
+
+ // Tell the tree we added 1 row at index 0
+ this._tree.rowCountChanged(0, 1);
+
+ // Data has changed, so re-sorting might be needed
+ this.sortView("", "", aDownload, 0);
+
+ window.updateCommands("tree-select");
+ },
+
+ updateDownload: function(aDownload) {
+ var row = this._dlList.indexOf(aDownload);
+ if (row == -1) {
+ // No download row found to update, but as it's obviously going on,
+ // add it to the list now (can happen with very fast, e.g. local dls)
+ this.onDownloadAdded(aDownload);
+ return;
+ }
+ let state = DownloadsCommon.stateOfDownload(aDownload);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ // At this point, we know if we are an indeterminate download or not.
+ aDownload.progressMode = aDownload.hasProgress ?
+ Ci.nsITreeView.PROGRESS_NORMAL : Ci.nsITreeView.PROGRESS_UNDETERMINED;
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ aDownload.isActive = 1;
+ break;
+ default:
+ aDownload.isActive = 0;
+ aDownload.progressMode = Ci.nsITreeView.PROGRESS_NONE;
+ // This preference may not be set, so defaulting to two.
+ var flashCount = 2;
+ try {
+ flashCount = Services.prefs.getIntPref(PREF_FLASH_COUNT);
+ } catch (e) { }
+ getAttentionWithCycleCount(flashCount);
+ break;
+ }
+
+ // Repaint the tree row
+ this._tree.invalidateRow(row);
+
+ // Data has changed, so re-sorting might be needed
+ this.sortView("", "", aDownload, row);
+
+ window.updateCommands("tree-select");
+ },
+
+ removeDownload: function(aDownload) {
+ var row = this._dlList.indexOf(aDownload);
+ // Make sure we have an item to remove
+ if (row == -1)
+ return;
+
+ var index = this.selection.currentIndex;
+ var wasSingleSelection = this.selection.count == 1;
+
+ // Remove data from the download list
+ this._dlList.splice(row, 1);
+
+ // Tell the tree we removed 1 row at the given row index
+ this._tree.rowCountChanged(row, -1);
+
+ // Update selection if only removed download was selected
+ if (wasSingleSelection && this.selection.count == 0) {
+ index = Math.min(index, this.rowCount - 1);
+ if (index >= 0)
+ this.selection.select(index);
+ }
+
+ window.updateCommands("tree-select");
+ },
+
+ searchView: function(aInput) {
+ // Stringify the previous search
+ var prevSearch = this._searchTerms.join(" ");
+
+ // Array of space-separated lower-case search terms
+ this._searchTerms = aInput.trim().toLowerCase().split(/\s+/);
+
+ // Don't rebuild the download list if the search didn't change
+ if (this._searchTerms.join(" ") == prevSearch)
+ return;
+
+ // Cache the current selection
+ this._cacheSelection();
+
+ // Rebuild the tree with set search terms
+ //this.initTree();
+
+ // Restore the selection
+ this._restoreSelection();
+ },
+
+ sortView: function(aColumnID, aDirection, aDownload, aRow) {
+ var sortAscending = aDirection != "descending";
+
+ if (aColumnID == "" && aDirection == "") {
+ // Re-sort in already selected/cached order
+ var sortedColumn = this._tree.columns.getSortedColumn();
+ if (sortedColumn) {
+ aColumnID = sortedColumn.id;
+ sortAscending = sortedColumn.element.getAttribute("sortDirection") != "descending";
+ }
+ // no need for else, use default case of switch, sortAscending is true
+ }
+
+ // Compare function for two _dlList items
+ var compfunc = function(a, b) {
+ // Active downloads are always at the beginning
+ // i.e. 0 for .isActive is larger (!) than 1
+ if (a.isActive < b.isActive)
+ return 1;
+ if (a.isActive > b.isActive)
+ return -1;
+ // Same active/inactive state, sort normally
+ var comp_a = null;
+ var comp_b = null;
+ switch (aColumnID) {
+ case "Name":
+ comp_a = a.displayName.toLowerCase();
+ comp_b = b.displayName.toLowerCase();
+ break;
+ case "Status":
+ comp_a = DownloadsCommon.stateOfDownload(a);
+ comp_b = DownloadsCommon.stateOfDownload(b);
+ break;
+ case "Progress":
+ case "ProgressPercent":
+ // Use original sorting for inactive entries
+ // Use only one isActive to be sure we do the same
+ comp_a = a.isActive ? a.progress : a.listIndex;
+ comp_b = a.isActive ? b.progress : b.listIndex;
+ break;
+ case "TimeRemaining":
+ comp_a = a.isActive ? a.lastSec : a.listIndex;
+ comp_b = a.isActive ? b.lastSec : b.listIndex;
+ break;
+ case "Transferred":
+ comp_a = a.currentBytes;
+ comp_b = b.currentBytes;
+ break;
+ case "TransferRate":
+ comp_a = a.isActive ? a.speed : a.listIndex;
+ comp_b = a.isActive ? b.speed : b.listIndex;
+ break;
+ case "TimeElapsed":
+ comp_a = (a.endTime && a.startTime && (a.endTime > a.startTime))
+ ? a.endTime - a.startTime
+ : 0;
+ comp_b = (b.endTime && b.startTime && (b.endTime > b.startTime))
+ ? b.endTime - b.startTime
+ : 0;
+ break;
+ case "StartTime":
+ comp_a = a.startTime;
+ comp_b = b.startTime;
+ break;
+ case "EndTime":
+ comp_a = a.endTime;
+ comp_b = b.endTime;
+ break;
+ case "Source":
+ comp_a = a.source.url;
+ comp_b = b.source.url;
+ break;
+ case "unsorted": // Special case for reverting to original order
+ default:
+ comp_a = a.listIndex;
+ comp_b = b.listIndex;
+ }
+ if (comp_a > comp_b)
+ return sortAscending ? 1 : -1;
+ if (comp_a < comp_b)
+ return sortAscending ? -1 : 1;
+ return 0;
+ }
+
+ // Cache the current selection
+ this._cacheSelection();
+
+ // Do the actual sorting of the array
+ this._dlList.sort(compfunc);
+
+ var row = this._dlList.indexOf(aDownload);
+ if (row == -1)
+ // Repaint the tree
+ this._tree.invalidate();
+ else if (row == aRow)
+ // No effect
+ this._selectionCache = null;
+ else if (row < aRow)
+ // Download moved up from aRow to row
+ this._tree.invalidateRange(row, aRow);
+ else
+ // Download moved down from aRow to row
+ this._tree.invalidateRange(aRow, row)
+
+ // Restore the selection
+ this._restoreSelection();
+ },
+
+ getRowData: function(aRow) {
+ return this._dlList[aRow];
+ },
+
+ getActiveDownloads: function() {
+ return this._dlList.filter(dld => !dld.stopped);
+ },
+
+ // ***** local member vars *****
+
+ _tree: null,
+ _dlBundle: null,
+ _lastListIndex: 0,
+ _selectionCache: null,
+
+ // ***** local helper functions *****
+
+ // Cache IDs of selected downloads for later restoration
+ _cacheSelection: function() {
+ // Abort if there's already something cached
+ if (this._selectionCache)
+ return;
+
+ this._selectionCache = [];
+ if (this.selection.count < 1)
+ return;
+
+ // Walk all selected rows and cache their download IDs
+ var start = {};
+ var end = {};
+ var numRanges = this.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++){
+ this.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++){
+ this._selectionCache.push(this._dlList[row]);
+ }
+ }
+ },
+
+ // Restore selection from cached IDs (as possible)
+ _restoreSelection: function() {
+ // Abort if the cache is empty
+ if (!this._selectionCache)
+ return;
+
+ this.selection.clearSelection();
+ for (let dl of this._selectionCache) {
+ // Find out what row this is now and if possible, add it to the selection
+ var row = this._dlList.indexOf(dl);
+ if (row != -1)
+ this.selection.rangedSelect(row, row, true);
+ }
+ // Work done, clear the cache
+ this._selectionCache = null;
+ },
+};
diff --git a/comm/suite/components/downloads/content/uploadProgress.js b/comm/suite/components/downloads/content/uploadProgress.js
new file mode 100644
index 0000000000..0cd4d27817
--- /dev/null
+++ b/comm/suite/components/downloads/content/uploadProgress.js
@@ -0,0 +1,189 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+
+const kInterval = 750; // Default to .75 seconds.
+
+var gPersist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist);
+var gSource = window.arguments[0].QueryInterface(Ci.nsIFileURL);
+var gTarget = window.arguments[1].QueryInterface(Ci.nsIURL);
+var gFileName = gSource.file.leafName;
+var gFileSize = gSource.file.fileSize;
+var gPercent = -1;
+var gStartTime;
+var gLastUpdate;
+var gLastSeconds;
+var gBundle;
+var gStatus;
+var gTime;
+var gSize;
+var gProgress;
+var gMeter;
+
+function onLoad()
+{
+ gBundle = document.getElementById("dmBundle");
+ gStatus = document.getElementById("status");
+ gTime = document.getElementById("timeElapsed");
+ gSize = document.getElementById("size");
+ gProgress = document.getElementById("progressText");
+ gMeter = document.getElementById("progress");
+ var status = gBundle.getString("stateNotStarted");
+ document.title =
+ gBundle.getFormattedString("progressTitle", [gFileName, status]);
+ gStatus.value = status;
+ gTime.value = gBundle.getFormattedString("timeSingle",
+ DownloadUtils.convertTimeUnits(0));
+ gSize.value = DownloadUtils.getTransferTotal(0, gFileSize);
+ document.getElementById("target").value =
+ gBundle.getFormattedString("toTarget", [gTarget.resolve(".")]);
+ document.getElementById("source").value =
+ gBundle.getFormattedString("fromSource", [gSource.file.leafName]);
+ gPersist.progressListener = gProgressListener;
+ gPersist.saveURI(gSource, null, null, 0, null, null, gTarget, null);
+ document.documentElement.getButton("cancel").focus();
+}
+
+function onUnload()
+{
+ if (gPersist)
+ gPersist.cancel(Cr.NS_BINDING_ABORTED);
+ gPersist = null;
+}
+
+function setPercent(aPercent, aStatus)
+{
+ gPercent = aPercent;
+ document.title = gBundle.getFormattedString("progressTitlePercent",
+ [aPercent, gFileName, aStatus]);
+ gProgress.value = gBundle.getFormattedString("percentFormat", [aPercent]);
+ gMeter.mode = "normal";
+ gMeter.value = aPercent;
+}
+
+var gProgressListener = {
+ // ----- nsIWebProgressListener methods -----
+
+ // Look for STATE_STOP and close dialog to indicate completion when it happens.
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aRequest instanceof Ci.nsIChannel &&
+ aRequest.URI.equals(gTarget) &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ gPersist = null;
+ var status = gBundle.getString("stateCompleted");
+ setPercent(100, status);
+ gStatus.value = status;
+ gSize.value = DownloadUtils.getTransferTotal(gFileSize, gFileSize);
+ setTimeout(window.close, kInterval);
+ }
+ },
+
+ // Handle progress notifications.
+ onProgressChange: function(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ return this.onProgressChange64(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress);
+ },
+
+ onProgressChange64: function(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ if (aRequest instanceof Ci.nsIChannel &&
+ aRequest.URI.equals(gTarget)) {
+ // Get current time.
+ var now = Date.now();
+
+ // If interval hasn't elapsed, ignore it.
+ if (!gStartTime)
+ gStartTime = now;
+ else if (now - gLastUpdate < kInterval && aCurTotalProgress < gFileSize)
+ return;
+
+ // Update this time.
+ gLastUpdate = now;
+
+ // Update elapsed time.
+ var elapsed = (now - gStartTime) / 1000;
+
+ // Calculate percentage.
+ var status = gBundle.getString("stateUploading");
+ var percent = -1;
+ if (gFileSize > 0)
+ percent = Math.floor(aCurTotalProgress * 100 / gFileSize);
+ if (percent != gPercent)
+ setPercent(percent, status);
+
+ // Update time remaining.
+ var rate = elapsed && aCurTotalProgress / elapsed;
+ if (rate && gFileSize) {
+ var timeLeft;
+ [timeLeft, gLastSeconds] =
+ DownloadUtils.getTimeLeft((gFileSize - aCurTotalProgress) / rate,
+ gLastSeconds);
+ status = gBundle.getFormattedString("statusActive", [status, timeLeft]);
+ }
+ gStatus.value = status;
+
+ // Update dialog's display of elapsed time.
+ var timeUnits = DownloadUtils.convertTimeUnits(elapsed);
+ var timeString = timeUnits[2] ? "timeDouble" : "timeSingle";
+ gTime.value = gBundle.getFormattedString(timeString, timeUnits);
+
+ // Update size (nn KB of mm KB at xx.x KB/sec)
+ var size = DownloadUtils.getTransferTotal(aCurTotalProgress, gFileSize);
+ if (elapsed)
+ size = gBundle.getFormattedString("sizeSpeed", [size,
+ gBundle.getFormattedString("speedFormat",
+ DownloadUtils.convertByteUnits(rate))]);
+ gSize.value = size;
+ }
+ },
+
+ // Look for error notifications and display alert to user.
+ onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {
+ // Check for error condition (only if dialog is still open).
+ if (!Cr.isSuccessCode(aStatus)) {
+ // Display error alert (using text supplied by back-end).
+ Services.prompt.alert(window, document.title, aMessage);
+ // Close the dialog.
+ window.close();
+ }
+ },
+
+ // Ignore onLocationChange and onSecurityChange notifications.
+ onLocationChange: function( aWebProgress, aRequest, aLocation, aFlags ) {
+ },
+
+ onSecurityChange: function( aWebProgress, aRequest, aState ) {
+ },
+
+ // ---------- nsISupports methods ----------
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIWebProgressListener2,
+ Ci.nsIWebProgressListener,
+ Ci.nsIInterfaceRequestor]),
+
+ // ---------- nsIInterfaceRequestor methods ----------
+
+ getInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIPrompt) ||
+ aIID.equals(Ci.nsIAuthPrompt)) {
+ var prompt;
+ if (aIID.equals(Ci.nsIPrompt))
+ prompt = Services.ww.getNewPrompter(window);
+ else
+ prompt = Services.ww.getNewAuthPrompter(window);
+ return prompt;
+ }
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
diff --git a/comm/suite/components/downloads/content/uploadProgress.xul b/comm/suite/components/downloads/content/uploadProgress.xul
new file mode 100644
index 0000000000..43e95d5432
--- /dev/null
+++ b/comm/suite/components/downloads/content/uploadProgress.xul
@@ -0,0 +1,33 @@
+<?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 dialog>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="cancel"
+ onload="onLoad();"
+ onunload="onUnload();"
+ style="width: 40em;">
+
+ <script src="chrome://communicator/content/downloads/uploadProgress.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="dmBundle"
+ src="chrome://communicator/locale/downloads/downloadmanager.properties"/>
+ </stringbundleset>
+
+ <label id="source" value="" crop="center"/>
+ <label id="target" value="" crop="center"/>
+ <label id="size" value=""/>
+ <label id="timeElapsed" value=""/>
+ <label id="status" value=""/>
+ <hbox>
+ <progressmeter id="progress" mode="undetermined" value="0" flex="1"/>
+ <label id="progressText" value="" style="width: 4ch; text-align: right;"/>
+ </hbox>
+</dialog>
diff --git a/comm/suite/components/downloads/jar.mn b/comm/suite/components/downloads/jar.mn
new file mode 100644
index 0000000000..9abbb0cd7b
--- /dev/null
+++ b/comm/suite/components/downloads/jar.mn
@@ -0,0 +1,14 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+% content communicator %content/communicator/ contentaccessible=yes
+ content/communicator/downloads/downloadmanager.js (content/downloadmanager.js)
+ content/communicator/downloads/downloadmanager.xul (content/downloadmanager.xul)
+ content/communicator/downloads/DownloadProgressListener.js (content/DownloadProgressListener.js)
+ content/communicator/downloads/progressDialog.xul (content/progressDialog.xul)
+ content/communicator/downloads/progressDialog.js (content/progressDialog.js)
+ content/communicator/downloads/uploadProgress.xul (content/uploadProgress.xul)
+ content/communicator/downloads/uploadProgress.js (content/uploadProgress.js)
+ content/communicator/downloads/treeView.js (content/treeView.js)
diff --git a/comm/suite/components/downloads/moz.build b/comm/suite/components/downloads/moz.build
new file mode 100644
index 0000000000..cc6cc0f1d6
--- /dev/null
+++ b/comm/suite/components/downloads/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
+
+JAR_MANIFESTS += ["jar.mn"]
+
+EXTRA_JS_MODULES += [
+ "DownloadsCommon.jsm",
+ "DownloadsTaskbar.jsm",
+]
+
+with Files("**"):
+ BUG_COMPONENT = ("SeaMonkey", "Downloads")
diff --git a/comm/suite/components/downloads/tests/chrome/chrome.ini b/comm/suite/components/downloads/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..dd5f9fc31f
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/chrome.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+
+[test_action_keys_respect_focus.xul]
+[test_basic_functionality.xul]
+[test_cleanup_search.xul]
+[test_clear_button_disabled.xul]
+[test_close_download_manager.xul]
+[test_delete_key_cancels.xul]
+[test_delete_key_removes.xul]
+[test_drag.xul]
+[test_enter_dblclick_opens.xul]
+[test_multi_select.xul]
+[test_multiword_search.xul]
+[test_open_properties.xul]
+[test_removeDownload_updates_ui.xul]
+[test_search_clearlist.xul]
+[test_search_keys.xul]
+[test_select_all.xul]
+[test_space_key_pauses_resumes.xul]
+[test_space_key_retries.xul]
+[test_ui_stays_open_on_alert_clickback.xul]
diff --git a/comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul b/comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul
new file mode 100644
index 0000000000..765e0a3a9c
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul
@@ -0,0 +1,376 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test for bug 474622 to check that action keys (Del, Backspace, Return)
+ * respect focus, i.e. work as expected in the Search field, Clear List button
+ * and download list.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+var openInvokeCount = 0;
+var removeInvokeCount = 0;
+var resumeInvokeCount = 0;
+var testedFunctions = {
+ openDownload : null,
+ removeDownload : null,
+ resumeDownload : null
+};
+
+function getCounter(aFn)
+{
+ switch (aFn) {
+ case "openDownload":
+ return () => openInvokeCount++;
+ case "removeDownload":
+ return () => removeInvokeCount++;
+ case "resumeDownload":
+ return () => resumeInvokeCount++;
+ }
+}
+
+function backupTestedFunction(aFn, aWin)
+{
+ ok(true, "(info) backupTestedFunction('" + aFn + "')");
+
+ [testedFunctions[aFn], aWin[aFn]] = [aWin[aFn], getCounter(aFn)];
+}
+function restoreTestedFunction(aFn, aWin)
+{
+ aWin[aFn] = testedFunctions[aFn];
+ testedFunctions[aFn] = null;
+
+ ok(true, "(info) restoreTestedFunction('" + aFn + "')");
+}
+
+function keyPressObs(aWin, aKey)
+{
+ this.mWin = aWin;
+ this.mKey = aKey;
+}
+keyPressObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic)
+ synthesizeKey(this.mKey, {}, this.mWin);
+ }
+};
+var searchAndPressKey = function(aKey, aWin, aValue) {
+ var searchbox = aWin.document.getElementById("search-box");
+ searchbox.focus();
+ if (aValue != null)
+ searchbox.value = aValue;
+
+ // Press given key after a short delay to allow focus() to complete
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(new keyPressObs(aWin, aKey), 500,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.wasPaused = false;
+ this.wasResumed = false;
+ this.wasFinished = false;
+}
+dlObs.prototype = {
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
+ !this.wasPaused)
+ {
+ this.wasPaused = true;
+ this.mWin.pauseDownload(aDownload.id);
+ return;
+ }
+
+ var searchbox = this.mWin.document.getElementById("search-box");
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED &&
+ !this.wasResumed)
+ {
+ this.wasResumed = true;
+
+ // Fill Search with an added space (test continues in testObs)
+ backupTestedFunction("resumeDownload", this.mWin);
+ searchAndPressKey(" ", this.mWin, "paused");
+ } else
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED &&
+ !this.wasFinished)
+ {
+ this.wasFinished = true;
+
+ // The formerly paused download was resumed successfully, is now complete
+ // and still selected. Since it is a real download it can be opened.
+
+ // Init Search (test continues in testObs)
+ backupTestedFunction("openDownload", this.mWin);
+ searchAndPressKey("VK_RETURN", this.mWin, "delete me");
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload()
+ {
+ function createURI(aObj)
+ {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ // The "paused" part of this filename will be searched for later.
+ destFile.append("download.paused");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // Empty any old downloads
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("cleanUp");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = dm.DBConnection.createStatement(
+ "INSERT INTO moz_downloads (name, target, source, state) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ try {
+ for (let site of ["delete.me", "i.live"]) {
+ stmt.bindByIndex(0, "Finished Download");
+ stmt.bindByIndex(1, filePath);
+ stmt.bindByIndex(2, "http://" + site + "/file");
+ stmt.bindByIndex(3, dm.DOWNLOAD_FINISHED);
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+ const IS_MAC = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime)
+ .OS == "Darwin";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var downloadTree = win.document.getElementById("downloadTree");
+ var searchbox = win.document.getElementById("search-box");
+ var clearList = win.document.getElementById("clearListButton");
+
+ // The list must have built, so figure out what test to do
+ switch (testPhase++) {
+ case 0:
+ // Init Search
+ searchbox.value = "delete me";
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ // Clear Search
+ backupTestedFunction("removeDownload", win);
+ searchAndPressKey("VK_DELETE", win);
+
+ break;
+ case 2:
+ is(removeInvokeCount, 0, "Search box: Del didn't remove download");
+
+ // Search has been cleared, init again
+ searchbox.value = "live";
+ searchbox.doCommand();
+
+ break;
+ case 3:
+ // Clear Search
+ searchAndPressKey("VK_BACK_SPACE", win);
+
+ break;
+ case 4:
+ is(removeInvokeCount, 0, "Search box: Backspace didn't remove download");
+ restoreTestedFunction("removeDownload", win);
+
+ // Add paused download (test continues in dlObs)
+ dm.addListener(new dlObs(win));
+ addDownload();
+
+ break;
+ case 5:
+ // Back from dlObs
+ is(resumeInvokeCount, 0, "Search box: Space didn't resume download");
+
+ // Focus download tree and select first (paused) download
+ downloadTree.focus();
+ downloadTree.view.selection.select(0);
+
+ // Simulate Resume download
+ synthesizeKey(" ", {}, win);
+ is(resumeInvokeCount, 1, "Download list: Space resumed download");
+
+ // Resume download for real (test continues in dlObs)
+ restoreTestedFunction("resumeDownload", win);
+ synthesizeKey(" ", {}, win);
+
+ break;
+ case 6:
+ // Back from dlObs
+ is(openInvokeCount, 0, "Search box: Return didn't open download");
+
+ // Search has been changed, init again to get formerly paused download
+ searchbox.value = "paused";
+ searchbox.doCommand();
+
+ break;
+ case 7:
+ // Focus download tree and select first (formerly paused) download
+ downloadTree.focus();
+ downloadTree.view.selection.select(0);
+
+ // Simulate Open download
+ synthesizeKey("VK_RETURN", {}, win);
+ is(openInvokeCount, 1, "Download list: Return opened download");
+
+ // Clear List: Return (execute Clear List)
+ // MacOSX: VK_RETURN doesn't work on this button (See bug 506850).
+ if (IS_MAC)
+ // Workaround not to time out.
+ clearList.doCommand();
+ else {
+ clearList.focus();
+ synthesizeKey("VK_RETURN", {}, win);
+ }
+
+ break;
+ case 8:
+ if (IS_MAC)
+ is(openInvokeCount, 1, "Clear List: doCommand() didn't open download (MacOSX)");
+ else
+ is(openInvokeCount, 1, "Clear List: Enter didn't open download (Linux, Windows)");
+ restoreTestedFunction("openDownload", win);
+
+ // We're done here
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ win.close();
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul b/comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul
new file mode 100644
index 0000000000..ffedc65227
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul
@@ -0,0 +1,281 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Make sure the download manager can display downloads in the right order and
+ * contains the expected data. The list has one of each download state ordered
+ * by the start/end times.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+// Downloads are sorted by endTime, so make sure the end times are distinct
+const DownloadData = [
+ /* Active states first */
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859238,
+ state: Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859237,
+ state: Ci.nsIDownloadManager.DOWNLOAD_PAUSED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859236,
+ state: Ci.nsIDownloadManager.DOWNLOAD_SCANNING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859235,
+ state: Ci.nsIDownloadManager.DOWNLOAD_QUEUED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ /* Finished states */
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859234,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859233,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FAILED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859232,
+ state: Ci.nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859231,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859230,
+ state: Ci.nsIDownloadManager.DOWNLOAD_DIRTY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859229,
+ endTime: 1180493839859229,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function test_numberOfTreeItems(aWin)
+{
+ var doc = aWin.document;
+ var dlTree = doc.getElementById("downloadTree");
+ is(dlTree.view.rowCount, DownloadData.length,
+ "There is the correct number of tree items");
+}
+
+function test_properDownloadData(aWin)
+{
+ // This also tests the ordering of the display
+ var doc = aWin.document;
+ var dlTree = doc.getElementById("downloadTree");
+ var view = dlTree.view;
+ var colName = dlTree.columns.getNamedColumn("Name");
+ var colState = dlTree.columns.getNamedColumn("Status");
+ var colTarget = dlTree.columns.getNamedColumn("Name");
+ var colSource = dlTree.columns.getNamedColumn("Source");
+ var stateString;
+ var statusBar = doc.getElementById("statusbar-display");
+
+ for (var i = 0; i < view.rowCount; i++) {
+ view.selection.select(i);
+ is(view.getCellText(i, colName), DownloadData[i].name,
+ "Download names match up");
+ switch (DownloadData[i].state) {
+ case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
+ stateString = "Paused";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING:
+ stateString = "Downloading";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
+ stateString = "Finished";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
+ stateString = "Failed";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
+ stateString = "Canceled";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL: // Parental Controls
+ case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY: // Security Zone Policy
+ case Ci.nsIDownloadManager.DOWNLOAD_DIRTY: // possible virus/spyware
+ stateString = "Blocked";
+ break;
+ default:
+ stateString = "Not Started";
+ break;
+ }
+ is(view.getCellText(i, colState), stateString,
+ "Download states match up");
+
+ var filePath = Services.io.newURI(DownloadData[i].target)
+ .QueryInterface(Ci.nsIFileURL)
+ .file.clone()
+ .QueryInterface(Ci.nsIFile)
+ .path;
+ is(statusBar.label, filePath,
+ "Download targets match up");
+ is(view.getCellText(i, colSource), DownloadData[i].source,
+ "Download sources match up");
+ }
+}
+
+var testFuncs = [
+ test_numberOfTreeItems
+ , test_properDownloadData
+];
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // First, we populate the database with some fake data
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+
+ // Now we can run our tests
+ for (let t of testFuncs)
+ t(win);
+
+ win.close();
+ dmFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ };
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul b/comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul
new file mode 100644
index 0000000000..37394168dc
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul
@@ -0,0 +1,172 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test for bug 414850 to make sure only downloads that are shown when
+ * searching are cleared and afterwards, the default list is shown.
+ *
+ * Test bug 430486 to make sure the Clear list button is disabled only when
+ * there are no download items visible.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("cleanUp");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, target, source, state) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ try {
+ for (let site of ["delete.me", "i.live"]) {
+ stmt.bindByIndex(0, "Super Pimped Download");
+ stmt.bindByIndex(1, filePath);
+ stmt.bindByIndex(2, "http://" + site + "/file");
+ stmt.bindByIndex(3, dm.DOWNLOAD_FINISHED);
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var downloadView = win.document.getElementById("downloadTree").view;
+ var searchbox = win.document.getElementById("search-box");
+ var clearList = win.document.getElementById("clearListButton");
+
+ // The list must have built, so figure out what test to do
+ switch (testPhase++) {
+ case 0:
+ // Make sure the button is initially enabled
+ is(clearList.disabled, false, "Clear list is enabled for default 2 item view");
+
+ // Search for multiple words in any order in all places
+ searchbox.value = "delete me";
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ // Search came back with 1 item
+ is(downloadView.rowCount, 1, "Search found the item to delete");
+ is(clearList.disabled, false, "Clear list is enabled for search matching 1 item");
+
+ // Clear the list that has the single matched item
+ clearList.doCommand();
+
+ break;
+ case 2:
+ // Done rebuilding with one item left
+ is(downloadView.rowCount, 1, "Clear list rebuilt the list with one");
+ is(clearList.disabled, false, "Clear list still enabled for 1 item in default view");
+
+ // Clear the whole list
+ clearList.doCommand();
+
+ break;
+ case 3:
+ // There's nothing left
+ is(downloadView.rowCount, 0, "Clear list killed everything");
+ is(clearList.disabled, true, "Clear list is disabled for no items");
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul b/comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul
new file mode 100644
index 0000000000..0b713a31c3
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Anoop Saldanha <poonaatsoc@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests 437422. This test basically intends to checks if the clear list
+ * button is disabled when:
+ * 1. an invalid search string has been entered into the search box.
+ * 2. active downloads are present in the dm ui
+ * 3. we have both case (1) and (2)
+ */
+-->
+
+<window title="Download Manager Test"
+ onload="runTest();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+const nsIDownloadManager = Ci.nsIDownloadManager;
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+const DoneDownloadData = [
+ { name: "Dead",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+const ActiveDownloadData = [
+ { name: "Patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_DOWNLOADING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function runTest()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DoneDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ //stmt.finalize();
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ var doc = aSubject.document;
+ var searchbox = doc.getElementById("search-box");
+ var clearButton = doc.getElementById("clearListButton");
+
+ switch (testPhase++) {
+ case 0:
+ // Ensure that the clear list button is enabled at first
+ ok(!clearButton.disabled,
+ "The clear list button is not disabled initially.");
+
+ // Now, insert an nonsensical search string - nothing should show up,
+ // and the button should be disabled in the next test phase
+ searchbox.value = "Nonsensical";
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ ok(clearButton.disabled,
+ "The clear list button is disabled with a nonsensical search " +
+ "term entered");
+
+ // Clear the search box
+ searchbox.value = "";
+ searchbox.doCommand();
+ break;
+
+ case 2:
+ // Populate the download manager with an active download now, and
+ // rebuild the list
+ stmt.reset();
+ for (let dl of ActiveDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+ dm.cleanUp();
+
+ break;
+ case 3:
+ ok(clearButton.disabled,
+ "The clear list button is disabled when we only have an active " +
+ "download");
+
+ // Now, insert an nonsensical search string - only the active download
+ // should show up, and the button should be disabled in the next test
+ // phase
+ searchbox.value = "Nonsensical";
+ searchbox.doCommand();
+ break;
+ case 4:
+ ok(clearButton.disabled,
+ "The clear list button is disabled with a nonsensical search " +
+ "term entered and one active download");
+
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul b/comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul
new file mode 100644
index 0000000000..94251a3771
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Anoop Saldanha <poonaatsoc@gmail.com>
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This test basically checks if the download manager
+ * closes when you press the esc key and accel + w.
+ */
+-->
+
+<window title="Download Manager Test"
+ onload="runTest();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+const dmui = Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsIDownloadManagerUI);
+
+function testCloseDMWithAccelKey(aWin)
+{
+ function dmWindowClosedListener() {
+ aWin.removeEventListener("unload", dmWindowClosedListener, false);
+ ok(!dmui.visible, "DMUI closes with accel + w");
+ SimpleTest.finish();
+ }
+ aWin.addEventListener("unload", dmWindowClosedListener, false);
+
+ synthesizeKey("w", { accelKey: true }, aWin);
+}
+
+function runTest()
+{
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var testPhase = 0;
+ // Specify an observer that will be notified when the dm has been rendered on screen
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ SimpleTest.waitForFocus(function () { closeDM(aSubject) }, aSubject);
+ }
+ };
+
+ function closeDM(win) {
+ // if we add more ways to close DM with keys, add more cases here
+ switch(testPhase++) {
+ case 0:
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ testCloseDMWithAccelKey(win);
+ }
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul b/comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul
new file mode 100644
index 0000000000..f02459dd6f
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul
@@ -0,0 +1,200 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the delete key will cancel a download in the UI.
+ * This test was added in bug 474622.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+var invokeCount = 0;
+var cancelDownload = null;
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.wasPaused = false;
+ this.wasCanceled = false;
+}
+dlObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ // We're done!
+ this.mWin.close();
+ SimpleTest.finish();
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
+ !this.wasPaused) {
+ // Make a copy of the cancelDownload function and replace it with a test
+ var counter = () => invokeCount++;
+ [cancelDownload, this.mWin["cancelDownload"]] = [this.mWin["cancelDownload"], counter];
+
+ synthesizeKey("VK_DELETE", {}, this.mWin);
+ is(invokeCount, 1, "Delete canceled the active download");
+
+ this.wasPaused = true;
+ this.mWin.pauseDownload(aDownload.id);
+ }
+
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED &&
+ !this.wasCanceled) {
+ synthesizeKey("VK_DELETE", {}, this.mWin);
+ is(invokeCount, 2, "Delete canceled the paused download");
+
+ // After all tests, restore original function
+ this.mWin["cancelDownload"] = cancelDownload;
+
+ this.wasCanceled = true;
+ this.mWin.cancelDownload(aDownload);
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // First, we clear out the database
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { cancelDL(aSubject) }, aSubject);
+ }
+ };
+
+ function cancelDL(win) {
+ var doc = win.document;
+ dm.addListener(new dlObs(win));
+
+ addDownload();
+ // we need to focus the download as well
+ doc.getElementById("downloadTree").view.selection.select(0);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul b/comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul
new file mode 100644
index 0000000000..8462c3323b
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul
@@ -0,0 +1,198 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This test ensures that the delete key removes a download. This was added by
+ * bug 411172.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+// Downloads are sorted by endTime, so make sure the end times are distinct
+const DownloadData = [
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859236,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FAILED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859234,
+ state: Ci.nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859232,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859230,
+ state: Ci.nsIDownloadManager.DOWNLOAD_DIRTY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859229,
+ endTime: 1180493839859229,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // First, we populate the database with some fake data
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { deleteDL(aSubject) }, aSubject);
+ }
+ };
+
+ function deleteDL(win) {
+ var doc = win.document;
+
+ var stmt = db.createStatement("SELECT COUNT(*) FROM moz_downloads");
+ try {
+ stmt.executeStep();
+ let dlTree = doc.getElementById("downloadTree");
+ is(stmt.getInt32(0), dlTree.view.rowCount,
+ "The database and the number of downloads display matches");
+ stmt.reset();
+
+ let len = DownloadData.length;
+ for (let i = 0; i < len; i++) {
+ synthesizeKey("VK_DELETE", {}, win);
+
+ stmt.executeStep();
+ is(stmt.getInt32(0), len - (i + 1),
+ "The download was properly removed");
+ stmt.reset();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ win.close();
+ dmFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_drag.xul b/comm/suite/components/downloads/tests/chrome/test_drag.xul
new file mode 100644
index 0000000000..133e633c39
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_drag.xul
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Phil Lacy <philbaseless-firefox@yahoo.com> (Original Author)
+ * Jens Hatlak <jh@junetz.de>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Assure download manager can load valid list item as
+ * "application/moz-x-file", "text/uri-list" and "text/plain"
+ */
+
+based on toolkit/mozapps/downloads/tests/chrome/test_bug_462172.xul
+https://bugzilla.mozilla.org/show_bug.cgi?id=462172
+
+create a file with unique name
+create another file with unique name and delete it
+load into downloads database
+open download manager
+synthesize drag on both files
+missing file should not init drag
+real file should return transferdata with application/x-moz-file,
+ text/uri-list (CRLF-terminated) and text/plain (LF-terminated)
+close window
+-->
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
+
+ <script>
+ <![CDATA[
+var missingFileElid;
+var realFileElid;
+const kFiller = "notApplicable";
+const kFillerURL = "https://bugzilla.mozilla.org/show_bug.cgi?id=462172"
+var realFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+var missingFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+
+realFile.append(kFiller);
+realFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var realFilePath = Services.io.newFileURI(realFile).spec;
+
+missingFile.append(kFiller);
+missingFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var missingFilePath = Services.io.newFileURI(missingFile).spec;
+missingFile.remove(false);
+
+// Dummy data for our files.
+// 'source' field must be in form of a URL.
+const DownloadData = [
+ { name: kFiller,
+ source: kFillerURL,
+ target: realFilePath,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED },
+ { name: kFiller,
+ source: kFillerURL,
+ target: missingFilePath,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED }
+];
+
+function mouseDragStartOnCell(aTree, aRow, aColumn, aWin, aFile)
+{
+ // get cell coordinates
+ if (typeof aTree.columns != "undefined")
+ aColumn = aTree.columns[aColumn];
+ var rect = aTree.treeBoxObject.getCoordsForCellItem(aRow, aColumn, "text");
+ return synthesizeDragStart(aTree.body, aFile, aWin, rect.x, rect.y);
+}
+
+function compareFunc(actualData, expectedData)
+{
+ return expectedData.equals(actualData);
+}
+
+var dragRealFile = [[
+ { type: "application/x-moz-file",
+ data: realFile,
+ eqTest: compareFunc },
+ { type: "text/uri-list",
+ data: realFilePath + "\r\n" },
+ { type: "text/plain",
+ data: realFilePath + "\n" }
+]];
+var dragMissingFile = [[
+ { type: "application/x-moz-file",
+ data: missingFile,
+ eqTest: compareFunc },
+ { type: "text/uri-list",
+ data: missingFilePath + "\r\n" },
+ { type: "text/plain",
+ data: missingFilePath + "\n" }
+]];
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ // load files into db
+ var db = dm.DBConnection;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads ( name, source, target, state)" +
+ "VALUES (:name, :source, :target, :state)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ win.focus();
+
+ var downloadTree = win.document.getElementById("downloadTree");
+
+ // Now we can run our tests
+ // Unordered sorting -> DownloadData/insert order: realFile, missingFile
+ // Column 4 is "Progress" (column 1, "Status", is hidden by default)
+ var result = mouseDragStartOnCell(downloadTree, 0, 4, win, dragRealFile);
+ is(result, null, "Checking for Real file match");
+ result = mouseDragStartOnCell(downloadTree, 1, 4, win, dragMissingFile);
+ isnot(result, null, "Drag start did not return item for missing file");
+
+ // Done.
+ win.close();
+ realFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ };
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul b/comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul
new file mode 100644
index 0000000000..59c105ceaa
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul
@@ -0,0 +1,243 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 495545 (implemented by bug 474622) to make sure the enter key
+ * or a double click actually calls opening the downloaded file.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+// similar, but not equal to the one in tree_shared.js
+function mouseDblClickOnCell(aTree, aRow, aColumn, aWin)
+{
+ // get cell coordinates
+ if (typeof aTree.columns != "undefined")
+ aColumn = aTree.columns[aColumn];
+ var rect = aTree.treeBoxObject.getCoordsForCellItem(aRow, aColumn, "text");
+ synthesizeMouse(aTree.body, rect.x, rect.y, { clickCount: 2 }, aWin);
+}
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.currDownload = null;
+}
+dlObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ var downloadTree = this.mWin.document.getElementById("downloadTree");
+ var downloadView = downloadTree.view;
+
+ // Default test/check for invocations
+ var invokeCount = 0;
+ var counter = () => invokeCount++;
+
+ // Run tests
+
+ // Make a copy of the openDownload function and replace it with a test
+ let copy;
+ [copy, this.mWin["openDownload"]] = [this.mWin["openDownload"], counter];
+
+ // Select the first (paused) download for not calling openDownload
+ downloadView.selection.select(0);
+
+ synthesizeKey("VK_RETURN", {}, this.mWin);
+ is(invokeCount, 0, "Enter didn't do anything");
+
+ mouseDblClickOnCell(downloadTree, 0, 3, this.mWin);
+ is(invokeCount, 0, "Double click didn't do anything");
+
+ // Select the second (finished) download for calling openDownload
+ downloadView.selection.select(1);
+
+ synthesizeKey("VK_RETURN", {}, this.mWin);
+ is(invokeCount, 1, "Enter opened download");
+
+ mouseDblClickOnCell(downloadTree, 1, 3, this.mWin);
+ is(invokeCount, 2, "Double click opened download");
+
+ // After all tests, restore original function
+ this.mWin["openDownload"] = copy;
+
+ // We're done!
+ this.mWin.close();
+ this.currDownload.targetFile.remove(false);
+ SimpleTest.finish();
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
+ this.currDownload = aDownload;
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (source, state, target, referrer) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ // add first download: PAUSED state
+ try {
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("dltest-paused");
+ var fileSpec = Services.io.newFileURI(file).spec;
+ stmt.bindByIndex(0, "http://example.com/file");
+ stmt.bindByIndex(1, dm.DOWNLOAD_PAUSED);
+ stmt.bindByIndex(2, fileSpec);
+ stmt.bindByIndex(3, "http://referrer/");
+
+ // Add it!
+ stmt.execute();
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ dm.addListener(new dlObs(win));
+
+ // add second download: FINISHED state, actually created
+ // (checked by cmd_open)
+ addDownload();
+
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_multi_select.xul b/comm/suite/components/downloads/tests/chrome/test_multi_select.xul
new file mode 100644
index 0000000000..0c0a05cf4b
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_multi_select.xul
@@ -0,0 +1,204 @@
+<?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/. -->
+<!--
+ * Test bug 228842 to make sure multiple selections work in the download
+ * manager by making sure commands work as expected for both single and doubly
+ * selected items.
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (source, state, target, referrer) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ try {
+ for (let site of ["ed.agadak.net", "mozilla.org", "mozilla.com", "mozilla.net"]) {
+ let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append(site);
+ let fileSpec = Services.io.newFileURI(file).spec;
+
+ stmt.bindByIndex(0, "http://" + site + "/file");
+ stmt.bindByIndex(1, dm.DOWNLOAD_FINISHED);
+ stmt.bindByIndex(2, fileSpec);
+ stmt.bindByIndex(3, "http://referrer/");
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var downloadView = win.document.getElementById("downloadTree").view;
+
+ // Default test/check for invocations
+ var invokeCount = 0;
+ var counter = () => invokeCount++;
+
+ // Accessors for returning a value for various properties
+ var getItems = () => downloadView.rowCount;
+ var getSelected = () => downloadView.selection.count;
+ var getClipboard = function() {
+ var clip = Cc["@mozilla.org/widget/clipboard;1"]
+ .getService(Ci.nsIClipboard);
+ var trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+
+ trans.init(null);
+ trans.addDataFlavor("text/unicode");
+ clip.getData(trans, clip.kGlobalClipboard);
+ var str = {};
+ trans.getTransferData("text/unicode", str, {});
+ return str.value.QueryInterface(Ci.nsISupportsString)
+ .data;
+ };
+
+ // Array of tests that consist of the command name, download manager
+ // function to temporarily replace, method to use in its place, value to
+ // use when checking correctness
+ var commandTests = [
+ ["pause", "pauseDownload", counter, counter],
+ ["resume", "resumeDownload", counter, counter],
+ ["cancel", "cancelDownload", counter, counter],
+ ["open", "openDownload", counter, counter],
+ ["show", "showDownload", counter, counter],
+ ["properties", "showProperties", counter, counter],
+ ["retry", "retryDownload", counter, counter],
+ ["openReferrer", "openUILink", counter, counter],
+ ["copyLocation", null, null, getClipboard],
+ ["remove", null, null, getItems],
+ ["selectAll", null, null, getSelected],
+ ];
+
+ // All the expected results for both single and double selections
+ var allExpected = {
+ single: {
+ pause: [0, "Paused no downloads"],
+ resume: [0, "Resumed no downloads"],
+ cancel: [0, "Canceled no downloads"],
+ open: [0, "Opened no downloads"],
+ show: [0, "Showed no downloads"],
+ properties: [1, "Called properties for one download"],
+ retry: [0, "Retried no downloads"],
+ openReferrer: [1, "Opened one referrer"],
+ copyLocation: ["http://ed.agadak.net/file", "Copied one location"],
+ remove: [3, "Removed one download, remaining 3"],
+ selectAll: [3, "Selected all 3 remaining downloads"],
+ },
+ double: {
+ pause: [0, "Paused neither download"],
+ resume: [0, "Resumed neither download"],
+ cancel: [0, "Canceled neither download"],
+ open: [0, "Opened neither download"],
+ show: [0, "Showed neither download"],
+ properties: [0, "Called properties for neither download"],
+ retry: [0, "Retried neither download"],
+ openReferrer: [0, "Opened neither referrer"],
+ copyLocation: ["http://mozilla.org/file\nhttp://mozilla.com/file", "Copied both locations"],
+ remove: [1, "Removed both downloads, remaining 1"],
+ selectAll: [1, "Selected the 1 remaining download"],
+ },
+ };
+
+ var cmdName;
+
+ // Run two tests: single selected, double selected
+ for (let whichTest of ["single", "double"]) {
+ let expected = allExpected[whichTest];
+
+ if (whichTest == "double")
+ // Select the first 2 downloads for double
+ downloadView.selection.rangedSelect(0, 1, false);
+ else
+ // Select the first download for single
+ downloadView.selection.select(0);
+
+ for (let [command, func, test, value] of commandTests) {
+ // Make a copy of the original function and replace it with a test
+ let copy;
+ [copy, win[func]] = [win[func], test];
+
+ // Run the command from the menu
+ if (command == "selectAll")
+ cmdName = "menu_" + command;
+ else
+ cmdName = "dlMenu_" + command;
+
+ win.document.getElementById(cmdName).doCommand();
+
+ // Make sure the value is as expected
+ let [correct, message] = expected[command];
+ is(value(), correct, message);
+
+ // Restore original values
+ invokeCount = 0;
+ win[func] = copy;
+ }
+ }
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_multiword_search.xul b/comm/suite/components/downloads/tests/chrome/test_multiword_search.xul
new file mode 100644
index 0000000000..ae2817c3fa
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_multiword_search.xul
@@ -0,0 +1,173 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test for bug 419403 that lets the download manager support multiple word
+ * search against the download name, source/referrer, date/time, file size,
+ * etc.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("multiWord");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, target, source, state, endTime, maxBytes) " +
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
+
+ try {
+ for (let site of ["ed.agadak.net", "mozilla.org"]) {
+ stmt.bindByIndex(0, "Super Pimped Download");
+ stmt.bindByIndex(1, filePath);
+ stmt.bindByIndex(2, "http://" + site + "/file");
+ stmt.bindByIndex(3, dm.DOWNLOAD_FINISHED);
+ stmt.bindByIndex(4, new Date(1985, 7, 2) * 1000);
+ stmt.bindByIndex(5, 111222333444);
+
+ // Add it!
+ stmt.execute();
+ }
+ } finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = -1;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var downloadView = win.document.getElementById("downloadTree").view;
+ var searchbox = win.document.getElementById("search-box");
+
+ var search = function(aTerms) {
+ searchbox.value = aTerms;
+ searchbox.doCommand();
+ };
+
+ let testResults = function(aExpected) {
+ is(downloadView.rowCount, aExpected,
+ "Phase " + testPhase + ": search matched " + aExpected + " download(s)");
+ };
+
+ // The list must have built, so figure out what test to do
+ switch (++testPhase) {
+ case 0:
+ // Search for multiple words in any order in all places
+ search("download super pimped multiWord");
+
+ break;
+ case 1:
+ // Done populating the two items
+ testResults(2);
+
+ // Do partial word matches including the site
+ search("Agadak.net downl pimp multi");
+
+ break;
+ case 2:
+ // Done populating the one result
+ testResults(1);
+
+ // The search term shouldn't be treated like a regular expression,
+ // e.g. "D.wnload" shouldn't match "Download".
+ search("D.wnload");
+
+ break;
+ case 3:
+ testResults(0);
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_open_properties.xul b/comm/suite/components/downloads/tests/chrome/test_open_properties.xul
new file mode 100644
index 0000000000..2f319d6766
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_open_properties.xul
@@ -0,0 +1,197 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Anoop Saldanha <poonaatsoc@gmail.com> (Original Author)
+ * Robert Kaiser <kairo@kairo.at>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests bug 474620 - opening a progress dialog with the "properties"
+ * item in the download manager.
+ */
+-->
+
+<window title="Download Manager Test"
+ onload="runTest();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+const nsIDownloadManager = Ci.nsIDownloadManager;
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+const DoneDownloadData = [
+ { name: "Dead",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+const ActiveDownloadData = [
+ { name: "Patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_DOWNLOADING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function runTest()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DoneDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ //stmt.finalize();
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var dmview;
+ var testStartTime = Date.now();
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ switch (testPhase++) {
+ case 0:
+ ok(!!aSubject.document.getElementById("dlWinCommands"),
+ "The download manager window is active");
+ // the download manager is started, select the first download
+ dmview = aSubject.document.getElementById("downloadTree").view;
+ dmview.selection.select(0);
+ // call "properties"
+ aSubject.document.getElementById("dlMenu_properties").doCommand();
+
+ break;
+ case 1:
+ ok(!!aSubject.document.getElementById("dlProgressCommands"),
+ "The progress dialog was called for a canceled download");
+ var endTimeSeconds = Math.round(DoneDownloadData[0].endTime / 1000);
+ is(aSubject.gEndTime, endTimeSeconds, "End time matches data");
+
+ // Close the progress window
+ aSubject.close();
+
+ // Populate the download manager with an active download now, and
+ // rebuild the list
+ stmt.reset();
+ for (let dl of ActiveDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+ dm.cleanUp();
+
+ break;
+ case 2:
+ ok(!!aSubject.document.getElementById("dlWinCommands"),
+ "The download manager window got an event");
+ // the download manager UI is rebuilt, select the first download
+ dmview.selection.select(0);
+ // call "properties"
+ aSubject.document.getElementById("dlMenu_properties").doCommand();
+
+ break;
+ case 3:
+ ok(!!aSubject.document.getElementById("dlProgressCommands"),
+ "The progress dialog was called for an active download");
+ // active downloads updated to current time,
+ // just check if it's set to later than start of the test
+ ok(aSubject.gEndTime >= testStartTime, "End time within test run");
+
+ // Close the progress window
+ aSubject.close();
+
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul b/comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul
new file mode 100644
index 0000000000..d5b5d6c657
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 394039 to make sure calling removeDownload of the
+ * nsIDownloadManager service correctly updates the download window.
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+const DownloadData = [
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // First, we populate the database with some fake data
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var doc = win.document;
+
+ // Note: This also tests the ordering of the display
+ var stmt = db.createStatement("SELECT id FROM moz_downloads");
+ var id = -1;
+ try {
+ stmt.executeStep();
+ id = stmt.getInt64(0);
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ dm.removeDownload(id);
+ var dlTreeView = doc.getElementById("downloadTree").view;
+ is(dlTreeView.rowCount, 0,
+ "The download was properly removed");
+
+ win.close();
+ dmFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul b/comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul
new file mode 100644
index 0000000000..862291cd5d
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul
@@ -0,0 +1,168 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 431188 to make sure the Clear list button is enabled after doing a
+ * search and finding results.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("cleanUp");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (target, source, state, endTime) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ // Add a bunch of downloads that don't match the search
+ var sites = [];
+ for (let i = 0; i < 50; i++)
+ sites.push("i-hate.clear-list-" + i);
+
+ // Add one download that matches the search
+ var searchTerm = "one-download.match-search";
+ sites.push(searchTerm);
+
+ try {
+ for (let site of sites) {
+ stmt.bindByIndex(0, filePath);
+ stmt.bindByIndex(1, "http://" + site + "/file");
+ stmt.bindByIndex(2, dm.DOWNLOAD_FINISHED);
+ // Make the one that matches slightly older so it appears last
+ stmt.bindByIndex(3, 1112223334445556 - (site == searchTerm));
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var downloadView = win.document.getElementById("downloadTree").view;
+ var searchbox = win.document.getElementById("search-box");
+ var clearList = win.document.getElementById("clearListButton");
+
+ // The list must have built, so figure out what test to do
+ switch (testPhase++) {
+ case 0:
+ case 2:
+ // Search for the one download
+ searchbox.value = searchTerm;
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ // Search came back with 1 item
+ is(downloadView.rowCount, 1, "Search found the item to delete");
+ is(clearList.disabled, false, "Clear list is enabled for search matching 1 item");
+
+ // Clear the list that has the single matched item
+ clearList.doCommand();
+
+ break;
+ case 3:
+ // There's nothing that matches the search
+ is(downloadView.rowCount, 0, "Clear list killed the one matching download");
+ is(clearList.disabled, true, "Clear list is disabled for no items");
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_search_keys.xul b/comm/suite/components/downloads/tests/chrome/test_search_keys.xul
new file mode 100644
index 0000000000..e651e341b5
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_search_keys.xul
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Make sure the download manager can display downloads in the right order and
+ * contains the expected data. The list has one of each download state ordered
+ * by the start/end times.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function test_meta_f(aWin)
+{
+ var doc = aWin.document;
+ var searchbox = doc.getElementById("search-box");
+ var dlTree = doc.getElementById("downloadTree");
+
+ // Enusre the searchbox is not focused
+ dlTree.focus();
+
+ // Dispatch the right key combination
+ synthesizeKey("f", {accelKey: true}, aWin);
+
+ ok(searchbox.hasAttribute("focused"), "Searchbox is focused");
+}
+
+var testFuncs = [
+ test_meta_f,
+]
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ // Now we can run our tests
+ for (let t of testFuncs)
+ t(win);
+
+ win.close();
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_select_all.xul b/comm/suite/components/downloads/tests/chrome/test_select_all.xul
new file mode 100644
index 0000000000..85dadcb168
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_select_all.xul
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 429614 to make sure ctrl/cmd-a work to select all downloads and
+ * hitting delete removes them all.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("selectAll");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (target, source, state) " +
+ "VALUES (?1, ?2, ?3)");
+
+ var sites = ["mozilla.org", "mozilla.com", "select.all"];
+ try {
+ for (let site of sites) {
+ stmt.bindByIndex(0, filePath);
+ stmt.bindByIndex(1, "http://" + site + "/file");
+ stmt.bindByIndex(2, dm.DOWNLOAD_FINISHED);
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var downloadView = win.document.getElementById("downloadTree").view;
+
+ is(downloadView.rowCount, sites.length, "All downloads displayed");
+
+ // Select all downloads
+ var isMac = AppConstants.platform == "macosx";
+ synthesizeKey("a", { metaKey: isMac, ctrlKey: !isMac }, win);
+ is(downloadView.selection.count, sites.length, "All downloads selected");
+
+ // Delete all downloads
+ synthesizeKey("VK_DELETE", {}, win);
+ is(downloadView.rowCount, 0, "All downloads removed");
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul b/comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul
new file mode 100644
index 0000000000..d22624be62
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul
@@ -0,0 +1,221 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the space key will pause and resume a download in the UI.
+ * This test was added in bug 413985.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function bug413985obs(aWin)
+{
+ this.mWin = aWin;
+ this.wasPaused = false;
+ this.wasResumed = false;
+ this.wasFinished = false;
+}
+bug413985obs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ if (this.wasFinished) {
+ // We're done!
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+ this.mWin.close();
+ SimpleTest.finish();
+ } else {
+ if (!this.wasPaused)
+ this.wasPaused = true;
+ else if (!this.wasResumed)
+ this.wasResumed = true;
+
+ // dispatch a space keypress to pause/resume the download
+ synthesizeKey(" ", {}, this.mWin);
+ }
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ ok(true, "State value = " + aDownload.state);
+ switch (aDownload.state) {
+ case Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING:
+ if (!this.wasPaused) {
+ ok(true, "The download was started successfully");
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ } else {
+ ok(this.wasResumed, "The download was resumed successfully");
+ }
+ break;
+
+ case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
+ ok(this.wasPaused && !this.wasResumed,
+ "The download was paused successfully");
+ if (!this.wasResumed) {
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ break;
+
+ case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
+ ok(this.wasPaused && this.wasResumed,
+ "The download was paused, and then resumed to completion");
+ this.wasFinished = true;
+ aDownload.targetFile.remove(false);
+
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ break;
+
+ default:
+ break;
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ // SeaMonkey: Use a bigger file than "http://example.com/httpd.js". (Bug 595685)
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/tests/fonts/mplus/mplus-1p-regular.ttf"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // First, we clear out the database
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var doc = win.document;
+ dm.addListener(new bug413985obs(win));
+
+ addDownload();
+ // we need to focus the download as well
+ doc.getElementById("downloadTree").view.selection.select(0);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul b/comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul
new file mode 100644
index 0000000000..5255f9caea
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul
@@ -0,0 +1,198 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the space key will retry a download in the UI.
+ * This test was added in bug 474622.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.wasCanceled = false;
+ this.wasFinished = false;
+}
+dlObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ if (this.wasFinished) {
+ // We're done!
+ this.mWin.close();
+ SimpleTest.finish();
+ } else {
+ // dispatch a space keypress to retry the download
+ synthesizeKey(" ", {}, this.mWin);
+ }
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
+ !this.wasCanceled) {
+ this.wasCanceled = true;
+ this.mWin.cancelDownload(aDownload);
+ }
+
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_CANCELED) {
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
+ ok(this.wasCanceled,
+ "The download was canceled, retried and then ran to completion");
+ this.wasFinished = true;
+ aDownload.targetFile.remove(false);
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // First, we clear out the database
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var doc = win.document;
+ dm.addListener(new dlObs(win));
+
+ addDownload();
+ // we need to focus the download as well
+ doc.getElementById("downloadTree").view.selection.select(0);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul b/comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul
new file mode 100644
index 0000000000..f54fb2dc21
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 397935 to make sure the download manager ui window stays open when
+ * it's opened by the user clicking the alert and has the close-when-done pref
+ * set.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ // Empty any old downloads
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var setClose = aVal =>
+ Services.prefs.setBoolPref("browser.download.manager.closeWhenDone", aVal);
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+
+ // Note: This test will not be valid if the download list is built
+ // synchronously in Startup in downloads.js
+ // Make sure the window stays open
+ var dmui = Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsIDownloadManagerUI);
+ ok(dmui.visible, "Download Manager stays open on alert click");
+
+ win.close();
+ setClose(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ };
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Simulate an alert click with pref set to true
+ setClose(true);
+ dm.QueryInterface(Ci.nsIObserver)
+ .observe(null, "alertclickcallback", null);
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/feeds/FeedConverter.js b/comm/suite/components/feeds/FeedConverter.js
new file mode 100644
index 0000000000..153f2a803d
--- /dev/null
+++ b/comm/suite/components/feeds/FeedConverter.js
@@ -0,0 +1,461 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.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 TYPE_ANY = "*/*";
+
+const FEEDHANDLER_URI = "about:feeds";
+
+const PREF_SELECTED_APP = "browser.feeds.handlers.application";
+const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
+const PREF_SELECTED_ACTION = "browser.feeds.handler";
+const PREF_SELECTED_READER = "browser.feeds.handler.default";
+
+const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
+const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
+const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
+const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
+
+const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
+const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
+const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
+const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
+
+function getPrefAppForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_APP;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_APP;
+
+ default:
+ return PREF_SELECTED_APP;
+ }
+}
+
+function getPrefWebForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_WEB;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_WEB;
+
+ default:
+ return PREF_SELECTED_WEB;
+ }
+}
+
+function getPrefActionForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_ACTION;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_ACTION;
+
+ default:
+ return PREF_SELECTED_ACTION;
+ }
+}
+
+function getPrefReaderForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_READER;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_READER;
+
+ default:
+ return PREF_SELECTED_READER;
+ }
+}
+
+function LOG(str) {
+ if (Services.prefs.getBoolPref("feeds.log", false))
+ dump("*** Feeds: " + str + "\n");
+}
+
+function FeedConverter() {
+}
+
+FeedConverter.prototype = {
+ /**
+ * This is the downloaded text data for the feed.
+ */
+ _data: null,
+
+ /**
+ * This is the object listening to the conversion, which is ultimately the
+ * docshell for the load.
+ */
+ _listener: null,
+
+ /**
+ * Records if the feed was sniffed
+ */
+ _sniffed: false,
+
+ /**
+ * See nsISupports.idl
+ */
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIFeedResultListener,
+ Ci.nsIStreamConverter,
+ Ci.nsIStreamListener,
+ Ci.nsIRequestObserver]),
+ classID: Components.ID("{88592f45-3866-4c8e-9d8a-ab58b290fcf7}"),
+
+ /**
+ * See nsIStreamConverter.idl
+ */
+ convert: function convert(sourceStream, sourceType, destinationType,
+ context) {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ /**
+ * See nsIStreamConverter.idl
+ */
+ asyncConvertData: function asyncConvertData(sourceType, destinationType,
+ listener, context) {
+ this._listener = listener;
+ },
+
+ /**
+ * Whether or not the preview page is being forced.
+ */
+ _forcePreviewPage: false,
+
+ /**
+ * Release our references to various things once we're done using them.
+ */
+ _releaseHandles: function _releaseHandles() {
+ this._listener = null;
+ this._request = null;
+ this._processor = null;
+ },
+
+ /**
+ * See nsIFeedResultListener.idl
+ */
+ handleResult: function handleResult(result) {
+ // Feeds come in various content types, which our feed sniffer coerces to
+ // the maybe.feed type. However, feeds are used as a transport for
+ // different data types, e.g. news/blogs (traditional feed), video/audio
+ // (podcasts) and photos (photocasts, photostreams). Each of these is
+ // different in that there's a different class of application suitable for
+ // handling feeds of that type, but without a content-type differentiation
+ // it is difficult for us to disambiguate.
+ //
+ // The other problem is that if the user specifies an auto-action handler
+ // for one feed application, the fact that the content type is shared means
+ // that all other applications will auto-load with that handler too,
+ // regardless of the content-type.
+ //
+ // This means that content-type alone is not enough to determine whether
+ // or not a feed should be auto-handled. Therefore for feeds we need
+ // to always use this stream converter, even when an auto-action is
+ // specified, not the basic one provided by WebContentConverter. This
+ // converter needs to consume all of the data and parse it, and based on
+ // that determination make a judgement about type.
+ //
+ // Since there are no content types for this content, and I'm not going to
+ // invent any, the upshot is that while a user can set an auto-handler for
+ // generic feed content, the system will prevent them from setting an auto-
+ // handler for other stream types. In those cases, the user will always see
+ // the preview page and have to select a handler. We can guess and show
+ // a client handler, but will not be able to show web handlers for those
+ // types.
+ //
+ // If this is just a feed, not some kind of specialized application, then
+ // auto-handlers can be set and we should obey them.
+ try {
+ var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]
+ .getService(Ci.nsIFeedResultService);
+ if (!this._forcePreviewPage && result.doc) {
+ var feed = result.doc.QueryInterface(Ci.nsIFeed);
+ var handler = Services.prefs.getCharPref(getPrefActionForType(feed.type), "ask");
+
+ if (handler != "ask") {
+ if (handler == "reader")
+ handler = Services.prefs.getCharPref(getPrefReaderForType(feed.type), "messenger");
+ switch (handler) {
+ case "web":
+ var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]
+ .getService(Ci.nsIWebContentConverterService);
+ if ((feed.type == Ci.nsIFeed.TYPE_FEED &&
+ wccr.getAutoHandler(TYPE_MAYBE_FEED)) ||
+ (feed.type == Ci.nsIFeed.TYPE_VIDEO &&
+ wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) ||
+ (feed.type == Ci.nsIFeed.TYPE_AUDIO &&
+ wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) {
+ wccr.loadPreferredHandler(this._request);
+ return;
+ }
+ break;
+
+ default:
+ LOG("unexpected handler: " + handler);
+ // fall through -- let feed service handle error
+ case "bookmarks":
+ case "client":
+ case "messenger":
+ try {
+ var title = feed.title ? feed.title.plainText() : "";
+ var desc = feed.subtitle ? feed.subtitle.plainText() : "";
+ feedService.addToClientReader(result.uri.spec, title, desc, feed.type);
+ return;
+ } catch(ex) {
+ /* fallback to preview mode */
+ }
+ }
+ }
+ }
+
+ var chromeChannel;
+ var oldChannel = this._request.QueryInterface(Ci.nsIChannel);
+ var loadInfo = oldChannel.loadInfo;
+
+ // If there was no automatic handler, or this was a podcast,
+ // photostream or some other kind of application, show the
+ // preview page if the parser returned a document.
+ if (result.doc) {
+
+ // Store the result in the result service so that the display
+ // page can access it.
+ feedService.addFeedResult(result);
+
+ // Now load the actual XUL document.
+ var chromeURI = Services.io.newURI(FEEDHANDLER_URI);
+ chromeChannel = Services.io.newChannelFromURIWithLoadInfo(chromeURI, loadInfo);
+ // carry the origin attributes from the channel that loaded the feed.
+ chromeChannel.owner = Services.scriptSecurityManager
+ .createCodebasePrincipal(chromeURI,
+ loadInfo.originAttributes);
+ chromeChannel.originalURI = result.uri;
+ }
+ else
+ chromeChannel = Services.io.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
+
+ chromeChannel.loadGroup = this._request.loadGroup;
+ chromeChannel.asyncOpen2(this._listener);
+ }
+ finally {
+ this._releaseHandles();
+ }
+ },
+
+ /**
+ * See nsIStreamListener.idl
+ */
+ onDataAvailable: function onDataAvailable(request, context, inputStream,
+ sourceOffset, count) {
+ if (this._processor)
+ this._processor.onDataAvailable(request, context, inputStream,
+ sourceOffset, count);
+ },
+
+ /**
+ * See nsIRequestObserver.idl
+ */
+ onStartRequest: function onStartRequest(request, context) {
+ var channel = request.QueryInterface(Ci.nsIChannel);
+
+ // Check for a header that tells us there was no sniffing
+ // The value doesn't matter.
+ try {
+ var httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
+ // Make sure to check requestSucceeded before the potentially-throwing
+ // getResponseHeader.
+ if (!httpChannel.requestSucceeded) {
+ // Just give up, but don't forget to cancel the channel first!
+ request.cancel(Cr.NS_BINDING_ABORTED);
+ return;
+ }
+ // Note: this throws if the header is not set.
+ httpChannel.getResponseHeader("X-Moz-Is-Feed");
+ }
+ catch (ex) {
+ this._sniffed = true;
+ }
+
+ this._request = request;
+
+ // Save and reset the forced state bit early, in case there's some kind of
+ // error.
+ var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]
+ .getService(Ci.nsIFeedResultService);
+ this._forcePreviewPage = feedService.forcePreviewPage;
+ feedService.forcePreviewPage = false;
+
+ // Parse feed data as it comes in
+ this._processor = Cc["@mozilla.org/feed-processor;1"]
+ .createInstance(Ci.nsIFeedProcessor);
+ this._processor.listener = this;
+ this._processor.parseAsync(null, channel.URI);
+
+ this._processor.onStartRequest(request, context);
+ },
+
+ /**
+ * See nsIRequestObserver.idl
+ */
+ onStopRequest: function onStopRequest(request, context, status) {
+ if (this._processor)
+ this._processor.onStopRequest(request, context, status);
+ }
+
+};
+
+/**
+ * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
+ * converter completes.
+ */
+function FeedResultService() {
+}
+
+FeedResultService.prototype = {
+ /**
+ * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
+ * value in case the same URI is requested concurrently.
+ */
+ _results: { },
+
+ /**
+ * See nsIFeedResultService.idl
+ */
+ forcePreviewPage: false,
+
+ /**
+ * See nsIFeedResultService.idl
+ */
+ addToClientReader: function addToClientReader(spec, title, subtitle, feedType) {
+ var handler = Services.prefs.getCharPref(getPrefActionForType(feedType), "reader");
+ if (handler == "ask" || handler == "reader")
+ handler = Services.prefs.getCharPref(getPrefReaderForType(feedType), "messenger");
+
+ switch (handler) {
+ case "client":
+ var clientApp = Services.prefs.getComplexValue(getPrefAppForType(feedType),
+ Ci.nsIFile);
+
+ // For the benefit of applications that might know how to deal with more
+ // URLs than just feeds, send feed: URLs in the following format:
+ //
+ // http urls: replace scheme with feed, e.g.
+ // http://foo.com/index.rdf -> feed://foo.com/index.rdf
+ // other urls: prepend feed: scheme, e.g.
+ // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
+ var feedURI = Services.io.newURI(spec);
+ if (feedURI.schemeIs("http")) {
+ feedURI.scheme = "feed";
+ spec = feedURI.spec;
+ }
+ else
+ spec = "feed:" + spec;
+
+ // Retrieving the shell service might fail on some systems, most
+ // notably systems where GNOME is not installed.
+ try {
+ var ss = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService);
+ ss.openApplicationWithURI(clientApp, spec);
+ } catch(e) {
+ // If we couldn't use the shell service, fallback to using a
+ // nsIProcess instance
+ var p = Cc["@mozilla.org/process/util;1"]
+ .createInstance(Ci.nsIProcess);
+ p.init(clientApp);
+ p.run(false, [spec], 1);
+ }
+ break;
+
+ default:
+ // "web" should have been handled elsewhere
+ LOG("unexpected handler: " + handler);
+ // fall through
+ case "bookmarks":
+ var topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ topWindow.PlacesCommandHook.addLiveBookmark(spec, title, subtitle)
+ .catch(Cu.reportError);
+ break;
+ case "messenger":
+ Cc["@mozilla.org/newsblog-feed-downloader;1"]
+ .getService(Ci.nsINewsBlogFeedDownloader)
+ .subscribeToFeed("feed:" + spec, null, null);
+ break;
+
+ }
+ },
+
+ /**
+ * See nsIFeedResultService.idl
+ */
+ addFeedResult: function addFeedResult(feedResult) {
+ if (feedResult == null)
+ throw new Error("null feedResult!");
+ if (feedResult.uri == null)
+ throw new Error("null URI!");
+ var spec = feedResult.uri.spec;
+ if (!this._results[spec])
+ this._results[spec] = [];
+ this._results[spec].push(feedResult);
+ },
+
+ /**
+ * See nsIFeedResultService.idl
+ */
+ getFeedResult: function getFeedResult(uri) {
+ if (uri == null)
+ throw new Error("null URI!");
+ var resultList = this._results[uri.spec];
+ for (let i = 0; i < resultList.length; ++i) {
+ if (resultList[i].uri == uri)
+ return resultList[i];
+ }
+ return null;
+ },
+
+ /**
+ * See nsIFeedResultService.idl
+ */
+ removeFeedResult: function removeFeedResult(uri) {
+ if (uri == null)
+ throw new Error("null URI!");
+ var resultList = this._results[uri.spec];
+ if (!resultList)
+ return;
+ var deletions = 0;
+ for (let i = 0; i < resultList.length; ++i) {
+ if (resultList[i].uri == uri) {
+ delete resultList[i];
+ ++deletions;
+ }
+ }
+
+ // send the holes to the end
+ resultList.sort();
+ // and trim the list
+ resultList.splice(resultList.length - deletions, deletions);
+ if (resultList.length == 0)
+ delete this._results[uri.spec];
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFeedResultService]),
+ classID: Components.ID("{e5b05e9d-f037-48e4-b9a4-b99476582927}")
+};
+
+var components = [FeedConverter,
+ FeedResultService];
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/comm/suite/components/feeds/FeedWriter.js b/comm/suite/components/feeds/FeedWriter.js
new file mode 100644
index 0000000000..c02e914caf
--- /dev/null
+++ b/comm/suite/components/feeds/FeedWriter.js
@@ -0,0 +1,1211 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+const FEEDWRITER_CID = Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}");
+const FEEDWRITER_CONTRACTID = "@mozilla.org/browser/feeds/result-writer;1";
+
+const XML_NS = "http://www.w3.org/XML/1998/namespace";
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
+const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
+const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
+const STRING_BUNDLE_URI = "chrome://communicator/locale/feeds/subscribe.properties";
+
+const PREF_SELECTED_APP = "browser.feeds.handlers.application";
+const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
+const PREF_SELECTED_ACTION = "browser.feeds.handler";
+const PREF_SELECTED_READER = "browser.feeds.handler.default";
+
+const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
+const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
+const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
+const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
+
+const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
+const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
+const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
+const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
+
+const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
+
+const TITLE_ID = "feedTitleText";
+const SUBTITLE_ID = "feedSubtitleText";
+
+function getPrefAppForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_APP;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_APP;
+
+ default:
+ return PREF_SELECTED_APP;
+ }
+}
+
+function getPrefWebForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_WEB;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_WEB;
+
+ default:
+ return PREF_SELECTED_WEB;
+ }
+}
+
+function getPrefActionForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_ACTION;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_ACTION;
+
+ default:
+ return PREF_SELECTED_ACTION;
+ }
+}
+
+function getPrefReaderForType(t) {
+ switch (t) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return PREF_VIDEO_SELECTED_READER;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return PREF_AUDIO_SELECTED_READER;
+
+ default:
+ return PREF_SELECTED_READER;
+ }
+}
+
+function LOG(str) {
+ if (Services.prefs.getBoolPref("feeds.log", false))
+ dump("*** Feeds: " + str + "\n");
+}
+
+/**
+ * Wrapper function for nsIIOService::newURI.
+ * @param aURLSpec
+ * The URL string from which to create an nsIURI.
+ * @returns an nsIURI object, or null if the creation of the URI failed.
+ */
+function makeURI(aURLSpec, aCharset) {
+ try {
+ return Services.io.newURI(aURLSpec, aCharset);
+ } catch (ex) {
+ }
+
+ return null;
+}
+
+/**
+ * Converts a number of bytes to the appropriate unit that results in a
+ * number that needs fewer than 4 digits
+ *
+ * @return a pair: [new value with 3 sig. figs., its unit]
+ */
+function convertByteUnits(aBytes) {
+ var units = ["bytes", "kilobytes", "megabytes", "gigabytes"];
+ var unitIndex = 0;
+
+ // convert to next unit if it needs 4 digits (after rounding), but only if
+ // we know the name of the next unit
+ while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
+ aBytes /= 1024;
+ unitIndex++;
+ }
+
+ // Get rid of insignificant bits by truncating to 1 or 0 decimal points
+ // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
+ aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0);
+
+ return [aBytes, units[unitIndex]];
+}
+
+function FeedWriter() {
+ this._mimeSvc = Cc["@mozilla.org/mime;1"]
+ .getService(Ci.nsIMIMEService);
+}
+
+FeedWriter.prototype = {
+ _getPropertyAsBag: function getPropertyAsBag(container, property) {
+ return container.fields.getProperty(property)
+ .QueryInterface(Ci.nsIPropertyBag2);
+ },
+
+ _getPropertyAsString: function getPropertyAsString(container, property) {
+ try {
+ return container.fields.getPropertyAsAString(property);
+ }
+ catch (e) {
+ }
+ return "";
+ },
+
+ /**
+ * @param element
+ * The element to add the text content to.
+ * @param text
+ * An nsIFeedTextConstruct
+ */
+ _setContentText: function setContentText(element, text) {
+ if (typeof element == "string")
+ element = this._document.getElementById(element);
+
+ // Takes the content of the nsIFeedTextConstruct and creates a
+ // sanitized documentFragment.
+ var docFragment = text.createDocumentFragment(element);
+ element.innerHTML = "";
+ element.appendChild(docFragment);
+ if (text.base)
+ element.setAttributeNS(XML_NS, "base", text.base.spec);
+ },
+
+ /**
+ * Safely sets the href attribute on an anchor tag, providing the URI
+ * specified can be loaded according to rules.
+ * @param element
+ * The element to set a URI attribute on
+ * @param attribute
+ * The attribute of the element to set the URI to, e.g. href or src
+ * @param uri
+ * The URI spec to set as the href
+ */
+ _safeSetURIAttribute: function safeSetURIAttribute(element, attribute, uri) {
+ const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
+ try {
+ Services.scriptSecurityManager.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags);
+ // checkLoadURIStrWithPrincipal will throw if the link URI should not be
+ // loaded, either because our feedURI isn't allowed to load it or per
+ // the rules specified in |flags|, so we'll never "linkify" the link...
+ element.setAttribute(attribute, uri);
+ }
+ catch (e) {
+ // Not allowed to load this link because checkLoadURIStrWithPrincipal threw
+ }
+ },
+
+ __bundle: null,
+ get _bundle() {
+ if (!this.__bundle) {
+ this.__bundle = Services.strings.createBundle(STRING_BUNDLE_URI);
+ }
+
+ return this.__bundle;
+ },
+
+ _getFormattedString: function getFormattedString(key, params) {
+ return this._bundle.formatStringFromName(key, params, params.length);
+ },
+
+ _getString: function getString(key) {
+ try {
+ return this._bundle.GetStringFromName(key);
+ } catch(e) {
+ LOG("Couldn't retrieve key from bundle");
+ }
+
+ return null;
+ },
+
+ /* Magic helper methods to be used instead of xbl properties */
+ _getSelectedItemFromMenulist: function getSelectedItemFromList(aList) {
+ return aList.getElementsByAttribute("selected", "true").item(0);
+ },
+
+ _setCheckboxCheckedState: function setCheckboxCheckedState(aCheckbox, aValue) {
+ // see checkbox.xml, xbl bindings are not visible through xrays!
+ var change = (aValue != (aCheckbox.getAttribute('checked') == 'true'));
+ if (aValue)
+ aCheckbox.setAttribute("checked", "true");
+ else
+ aCheckbox.removeAttribute("checked");
+
+ if (change) {
+ aCheckbox.dispatchEvent(new this._document.defaultView.Event(
+ "CheckboxStateChange", { bubbles: true, cancelable: true }));
+ }
+ },
+
+ /**
+ * Returns a date suitable for displaying in the feed preview.
+ * If the date cannot be parsed, the return value is "null".
+ * @param dateString
+ * A date as extracted from a feed entry. (entry.updated)
+ */
+ _parseDate: function parseDate(dateString) {
+ // Convert the date into the user's local time zone.
+ var dateObj = new Date(dateString);
+ // Make sure the date we're given is valid.
+ if (!dateObj.getTime())
+ return false;
+
+ return this._dateFormatter.format(dateObj);
+ },
+
+ __dateFormatter: null,
+ get _dateFormatter() {
+ if (!this.__dateFormatter) {
+ const dtOptions = { timeStyle: "short", dateStyle: "long" };
+ this.__dateFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
+ }
+ return this.__dateFormatter;
+ },
+
+ /**
+ * Returns the feed type.
+ */
+ __feedType: null,
+ _getFeedType: function getFeedType() {
+ if (this.__feedType != null)
+ return this.__feedType;
+
+ try {
+ // grab the feed because it's got the feed.type in it.
+ var container = this._getContainer();
+ var feed = container.QueryInterface(Ci.nsIFeed);
+ this.__feedType = feed.type;
+ return feed.type;
+ } catch (ex) {
+ }
+
+ return Ci.nsIFeed.TYPE_FEED;
+ },
+
+ /**
+ * Maps a feed type to a maybe-feed mimetype.
+ */
+ _getMimeTypeForFeedType: function getMimeTypeForFeedType() {
+ switch (this._getFeedType()) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ return TYPE_MAYBE_VIDEO_FEED;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ return TYPE_MAYBE_AUDIO_FEED;
+
+ default:
+ return TYPE_MAYBE_FEED;
+ }
+ },
+
+ /**
+ * Writes the feed title into the preview document.
+ * @param container
+ * The feed container, an nsIFeedContainer
+ */
+ _setTitleText: function setTitleText(container) {
+ if (container.title) {
+ this._setContentText(TITLE_ID, container.title);
+ this._document.title = container.title.plainText();
+ }
+
+ var feed = container.QueryInterface(Ci.nsIFeed);
+ if (feed && feed.subtitle)
+ this._setContentText(SUBTITLE_ID, feed.subtitle);
+ },
+
+ /**
+ * Writes the title image into the preview document if one is present.
+ * @param container
+ * The feed container
+ */
+ _setTitleImage: function setTitleImage(container) {
+ try {
+ var parts = container.image;
+
+ // Set up the title image (supplied by the feed)
+ var feedTitleImage = this._document.getElementById("feedTitleImage");
+ this._safeSetURIAttribute(feedTitleImage, "src",
+ parts.getPropertyAsAString("url"));
+
+ // Set up the title image link
+ var feedTitleLink = this._document.getElementById("feedTitleLink");
+
+ var titleText = this._getFormattedString("linkTitleTextFormat",
+ [parts.getPropertyAsAString("title")]);
+ feedTitleLink.setAttribute("title", titleText);
+ var titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;
+ feedTitleLink.style.MozMarginEnd = titleImageWidth + "px";
+
+ this._safeSetURIAttribute(feedTitleLink, "href",
+ parts.getPropertyAsAString("link"));
+ }
+ catch (e) {
+ LOG("Failed to set Title Image (this is benign): " + e);
+ }
+ },
+
+ /**
+ * Writes all entries contained in the feed.
+ * @param container
+ * The container of entries in the feed
+ */
+ _writeFeedContent: function writeFeedContent(container) {
+ // Build the actual feed content
+ var feed = container.QueryInterface(Ci.nsIFeed);
+ if (feed.items.length == 0)
+ return;
+
+ var feedContent = this._document.getElementById("feedContent");
+
+ for (let i = 0; i < feed.items.length; ++i) {
+ let entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
+ entry.QueryInterface(Ci.nsIFeedContainer);
+
+ let entryContainer = this._document.createElementNS(HTML_NS, "div");
+ entryContainer.className = "entry";
+
+ // If the entry has a title, make it a link
+ if (entry.title) {
+ let a = this._document.createElementNS(HTML_NS, "a");
+ let span = this._document.createElementNS(HTML_NS, "span");
+ a.appendChild(span);
+ this._setContentText(span, entry.title);
+
+ // Entries are not required to have links, so entry.link can be null.
+ if (entry.link)
+ this._safeSetURIAttribute(a, "href", entry.link.spec);
+
+ let title = this._document.createElementNS(HTML_NS, "h3");
+ title.appendChild(a);
+
+ let lastUpdated = this._parseDate(entry.updated);
+ if (lastUpdated) {
+ let dateDiv = this._document.createElementNS(HTML_NS, "div");
+ dateDiv.className = "lastUpdated";
+ dateDiv.textContent = lastUpdated;
+ title.appendChild(dateDiv);
+ }
+
+ entryContainer.appendChild(title);
+ }
+
+ var body = this._document.createElementNS(HTML_NS, "div");
+ var summary = entry.summary || entry.content;
+ var docFragment = null;
+ if (summary) {
+ if (summary.base)
+ body.setAttributeNS(XML_NS, "base", summary.base.spec);
+ else
+ LOG("no base?");
+ docFragment = summary.createDocumentFragment(body);
+ if (docFragment)
+ body.appendChild(docFragment);
+
+ // If the entry doesn't have a title, append a # permalink
+ // See http://scripting.com/rss.xml for an example
+ if (!entry.title && entry.link) {
+ var a = this._document.createElementNS(HTML_NS, "a");
+ a.appendChild(this._document.createTextNode("#"));
+ this._safeSetURIAttribute(a, "href", entry.link.spec);
+ body.appendChild(this._document.createTextNode(" "));
+ body.appendChild(a);
+ }
+
+ }
+ body.className = "feedEntryContent";
+ entryContainer.appendChild(body);
+
+ if (entry.enclosures && entry.enclosures.length > 0) {
+ var enclosuresDiv = this._buildEnclosureDiv(entry);
+ entryContainer.appendChild(enclosuresDiv);
+ }
+
+ feedContent.appendChild(entryContainer);
+
+ var clearDiv = this._document.createElementNS(HTML_NS, "div");
+ clearDiv.style.clear = "both";
+ feedContent.appendChild(clearDiv);
+ }
+ },
+
+ /**
+ * Takes a url to a media item and returns the best name it can come up with.
+ * Frequently this is the filename portion (e.g. passing in
+ * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex
+ * cases, this will return the entire url (e.g. passing in
+ * http://example.com/somedirectory/ would return
+ * http://example.com/somedirectory/).
+ * @param aURL
+ * The URL string from which to create a display name
+ * @returns a string
+ */
+ _getURLDisplayName: function getURLDisplayName(aURL) {
+ var url = makeURI(aURL);
+
+ if ((url instanceof Ci.nsIURL) && url.fileName)
+ return decodeURIComponent(url.fileName);
+ return aURL;
+ },
+
+ /**
+ * Takes a FeedEntry with enclosures, generates the HTML code to represent
+ * them, and returns that.
+ * @param entry
+ * FeedEntry with enclosures
+ * @returns element
+ */
+ _buildEnclosureDiv: function buildEnclosureDiv(entry) {
+ var enclosuresDiv = this._document.createElementNS(HTML_NS, "div");
+ enclosuresDiv.className = "enclosures";
+
+ enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel")));
+
+ for (let i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) {
+ let enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2);
+
+ if (!(enc.hasKey("url")))
+ continue;
+
+ let enclosureDiv = this._document.createElementNS(HTML_NS, "div");
+ enclosureDiv.setAttribute("class", "enclosure");
+
+ let mozicon = "moz-icon://.txt?size=16";
+ let type_text = null;
+ let size_text = null;
+
+ if (enc.hasKey("type")) {
+ type_text = enc.get("type");
+ try {
+ let handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null);
+
+ if (handlerInfoWrapper)
+ type_text = handlerInfoWrapper.description;
+
+ if (type_text && type_text.length > 0)
+ mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
+
+ } catch (ex) {
+ }
+
+ }
+
+ if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
+ let enc_size = convertByteUnits(parseInt(enc.get("length")));
+
+ size_text = this._getFormattedString("enclosureSizeText",
+ [enc_size[0], this._getString(enc_size[1])]);
+ }
+
+ let iconimg = this._document.createElementNS(HTML_NS, "img");
+ iconimg.setAttribute("src", mozicon);
+ iconimg.setAttribute("class", "type-icon");
+ enclosureDiv.appendChild(iconimg);
+
+ enclosureDiv.appendChild(this._document.createTextNode( " " ));
+
+ let enc_href = this._document.createElementNS(HTML_NS, "a");
+ enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url"))));
+ this._safeSetURIAttribute(enc_href, "href", enc.get("url"));
+ enclosureDiv.appendChild(enc_href);
+
+ if (type_text && size_text)
+ enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")"));
+
+ else if (type_text)
+ enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")"))
+
+ else if (size_text)
+ enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")"))
+
+ enclosuresDiv.appendChild(enclosureDiv);
+ }
+
+ return enclosuresDiv;
+ },
+
+ /**
+ * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
+ * Displays error information if there was one.
+ * @param result
+ * The parsed feed result
+ * @returns A valid nsIFeedContainer object containing the contents of
+ * the feed.
+ */
+ _getContainer: function getContainer(result) {
+ var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]
+ .getService(Ci.nsIFeedResultService);
+
+ try {
+ var result = feedService.getFeedResult(this._getOriginalURI(this._window));
+
+ if (result.bozo) {
+ LOG("Subscribe Preview: feed result is bozo?!");
+ }
+ }
+ catch (e) {
+ LOG("Subscribe Preview: feed not available?!");
+ }
+
+ try {
+ var container = result.doc;
+ }
+ catch (e) {
+ LOG("Subscribe Preview: no result.doc? Why didn't the original reload?");
+ return null;
+ }
+ return container;
+ },
+
+ /**
+ * Get the human-readable display name of a file. This could be the
+ * application name.
+ * @param file
+ * A nsIFile to look up the name of
+ * @returns The display name of the application represented by the file.
+ */
+ _getFileDisplayName: function getFileDisplayName(file) {
+ if (AppConstants.platform == "win" &&
+ file instanceof Ci.nsILocalFileWin) {
+ try {
+ return file.getVersionInfoField("FileDescription");
+ } catch (e) {}
+ } else if (AppConstants.platform == "macosx" &&
+ file instanceof Ci.nsILocalFileMac) {
+ try {
+ return file.bundleDisplayName;
+ } catch (e) {}
+ }
+ return file.leafName;
+ },
+
+ /**
+ * Get moz-icon url for a file
+ * @param file
+ * A nsIFile object for which the moz-icon:// is returned
+ * @returns moz-icon url of the given file as a string
+ */
+ _getFileIconURL: function getFileIconURL(file) {
+ var fph = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ var urlSpec = fph.getURLSpecFromFile(file);
+ return "moz-icon://" + urlSpec + "?size=16";
+ },
+
+ /**
+ * Helper method to set the selected application and system default
+ * reader menuitems details from a file object
+ * @param aMenuItem
+ * The menuitem on which the attributes should be set
+ * @param aFile
+ * The menuitem's associated file
+ */
+ _initMenuItemWithFile: function(aMenuItem, aFile) {
+ aMenuItem.setAttribute("label", this._getFileDisplayName(aFile));
+ aMenuItem.setAttribute("image", this._getFileIconURL(aFile));
+ },
+
+ /**
+ * Helper method to get an element in the XBL binding where the handler
+ * selection UI lives
+ */
+ _getUIElement: function getUIElement(id) {
+ return this._document.getAnonymousElementByAttribute(
+ this._document.getElementById("feedSubscribeLine"), "anonid", id);
+ },
+
+ /**
+ * Displays a prompt from which the user may choose a (client) feed reader.
+ * @param aCallback the callback method, passes in true if a feed reader was
+ * selected, false otherwise.
+ */
+ _chooseClientApp: function chooseClientApp(aCallback) {
+ try {
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult == Ci.nsIFilePicker.returnOK) {
+ this._selectedApp = fp.file;
+ if (this._selectedApp) {
+ let file = Services.dirsvc.get("XREExeF", Ci.nsIFile);
+ if (fp.file.leafName != file.leafName) {
+ this._initMenuItemWithFile(this._selectedAppMenuItem,
+ this._selectedApp);
+
+ // Show and select the selected application menuitem
+ this._selectedAppMenuItem.hidden = false;
+ this._selectedAppMenuItem.doCommand();
+ if (aCallback) {
+ aCallback(true);
+ return;
+ }
+ }
+ }
+ }
+ if (aCallback) {
+ aCallback(false);
+ }
+ }.bind(this);
+
+ fp.init(this._window,
+ this._getString("chooseApplicationDialogTitle"),
+ Ci.nsIFilePicker.modeOpen);
+ fp.appendFilters(Ci.nsIFilePicker.filterApps);
+ fp.open(fpCallback);
+ } catch(ex) {}
+ },
+
+ _setAlwaysUseCheckedState: function setAlwaysUseCheckedState(feedType) {
+ var checkbox = this._getUIElement("alwaysUse");
+ if (checkbox) {
+ var alwaysUse = (Services.prefs.getCharPref(getPrefActionForType(feedType), "ask") != "ask");
+ this._setCheckboxCheckedState(checkbox, alwaysUse);
+ }
+ },
+
+ _setSubscribeUsingLabel: function setSubscribeUsingLabel() {
+ var stringLabel = "subscribeFeedUsing";
+ switch (this._getFeedType()) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ stringLabel = "subscribeVideoPodcastUsing";
+ break;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ stringLabel = "subscribeAudioPodcastUsing";
+ break;
+ }
+
+ var subscribeUsing = this._getUIElement("subscribeUsingDescription");
+ subscribeUsing.setAttribute("value", this._getString(stringLabel));
+ },
+
+ _setAlwaysUseLabel: function setAlwaysUseLabel() {
+ var checkbox = this._getUIElement("alwaysUse");
+ if (checkbox) {
+ if (this._handlersMenuList) {
+ var handlerName = this._getSelectedItemFromMenulist(this._handlersMenuList)
+ .getAttribute("label");
+ var stringLabel = "alwaysUseForFeeds";
+ switch (this._getFeedType()) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ stringLabel = "alwaysUseForVideoPodcasts";
+ break;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ stringLabel = "alwaysUseForAudioPodcasts";
+ break;
+ }
+
+ checkbox.setAttribute("label", this._getFormattedString(stringLabel, [handlerName]));
+ }
+ }
+ },
+
+ // nsIDOMEventListener
+ handleEvent: function(event) {
+ if (event.target != this._document &&
+ event.target.ownerDocument != this._document) {
+ LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
+ return;
+ }
+
+ if (event.type == "load")
+ this._writeContent();
+ else if (event.type == "unload")
+ this._close();
+ else if (event.type == "command") {
+ switch (event.target.getAttribute("anonid")) {
+ case "subscribeButton":
+ this._subscribe();
+ break;
+ case "chooseApplicationMenuItem":
+ /* Bug 351263: Make sure to not steal focus if the "Choose
+ * Application" item is being selected with the keyboard. We do this
+ * by ignoring command events while the dropdown is closed (user
+ * arrowing through the combobox), but handling them while the
+ * combobox dropdown is open (user pressed enter when an item was
+ * selected). If we don't show the filepicker here, it will be shown
+ * when clicking "Subscribe Now".
+ */
+ var popupbox = this._handlersMenuList.firstChild.boxObject;
+ if (popupbox.popupState == "hiding") {
+ this._chooseClientApp(function(aResult) {
+ if (!aResult) {
+ // Select the (per-prefs) selected handler if no application
+ // was selected
+ this._setSelectedHandler(this._getFeedType());
+ }
+ }.bind(this));
+ }
+ break;
+ default:
+ this._setAlwaysUseLabel();
+ }
+ }
+ },
+
+ _setSelectedHandler: function setSelectedHandler(feedType) {
+ var handler = Services.prefs.getCharPref(getPrefReaderForType(feedType), "messenger");
+
+ switch (handler) {
+ case "web":
+ if (this._handlersMenuList) {
+ var url = Services.prefs.getStringPref(getPrefWebForType(feedType));
+ var handlers = this._handlersMenuList.getElementsByAttribute("webhandlerurl", url);
+ if (handlers.length == 0) {
+ LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist");
+ return;
+ }
+
+ handlers[0].doCommand();
+ }
+ break;
+ case "client":
+ try {
+ this._selectedApp =
+ Services.prefs.getComplexValue(getPrefAppForType(feedType),
+ Ci.nsIFile);
+ }
+ catch(ex) {
+ this._selectedApp = null;
+ }
+
+ if (this._selectedApp) {
+ this._initMenuItemWithFile(this._selectedAppMenuItem,
+ this._selectedApp);
+ this._selectedAppMenuItem.hidden = false;
+ this._selectedAppMenuItem.doCommand();
+
+ // Only show the default reader menuitem if the default reader
+ // isn't the selected application
+ if (this._defaultSystemReader) {
+ var shouldHide = this._defaultSystemReader.path == this._selectedApp.path;
+ this._defaultHandlerMenuItem.hidden = shouldHide;
+ }
+ break;
+ }
+ case "bookmarks":
+ var liveBookmarksMenuItem = this._getUIElement("liveBookmarksMenuItem");
+ if (liveBookmarksMenuItem)
+ liveBookmarksMenuItem.doCommand();
+ break;
+ // fall through if this._selectedApp is null
+ default:
+ var messengerFeedsMenuItem = this._getUIElement("messengerFeedsMenuItem");
+ if (messengerFeedsMenuItem)
+ messengerFeedsMenuItem.doCommand();
+ break;
+ }
+ },
+
+ _initSubscriptionUI: function initSubscriptionUI() {
+ var handlersMenuPopup = this._getUIElement("handlersMenuPopup");
+ if (!handlersMenuPopup)
+ return;
+
+ var feedType = this._getFeedType();
+
+ // change the background
+ var header = this._document.getElementById("feedHeader");
+ switch (feedType) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ header.className = "videoPodcastBackground";
+ break;
+
+ case Ci.nsIFeed.TYPE_AUDIO:
+ header.className = "audioPodcastBackground";
+ break;
+
+ default:
+ header.className = "feedBackground";
+ }
+
+ var liveBookmarksMenuItem = this._getUIElement("liveBookmarksMenuItem");
+
+ // Last-selected application
+ var menuItem = liveBookmarksMenuItem.cloneNode(false);
+ menuItem.removeAttribute("selected");
+ menuItem.setAttribute("anonid", "selectedAppMenuItem");
+ menuItem.className = "menuitem-iconic selectedAppMenuItem";
+ menuItem.setAttribute("handlerType", "client");
+ try {
+ this._selectedApp = Services.prefs.getComplexValue(getPrefAppForType(feedType),
+ Ci.nsIFile);
+
+ if (this._selectedApp.exists())
+ this._initMenuItemWithFile(menuItem, this._selectedApp);
+ else {
+ // Hide the menuitem if the last selected application doesn't exist
+ menuItem.hidden = true;
+ }
+ }
+ catch(ex) {
+ // Hide the menuitem until an application is selected
+ menuItem.hidden = true;
+ }
+ this._selectedAppMenuItem = menuItem;
+ handlersMenuPopup.appendChild(menuItem);
+
+ // List the default feed reader
+ try {
+ this._defaultSystemReader = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService)
+ .defaultFeedReader;
+ menuItem = liveBookmarksMenuItem.cloneNode(false);
+ menuItem.removeAttribute("selected");
+ menuItem.setAttribute("anonid", "defaultHandlerMenuItem");
+ menuItem.className = "menuitem-iconic defaultHandlerMenuItem";
+ menuItem.setAttribute("handlerType", "client");
+
+ this._initMenuItemWithFile(menuItem, this._defaultSystemReader);
+
+ // Hide the default reader item if it points to the same application
+ // as the last-selected application
+ if (this._selectedApp &&
+ this._selectedApp.path == this._defaultSystemReader.path)
+ menuItem.hidden = true;
+ }
+ catch(ex) {
+ }
+
+ if (menuItem) {
+ this._defaultHandlerMenuItem = menuItem;
+ handlersMenuPopup.appendChild(menuItem);
+ }
+
+ // "Choose Application..." menuitem
+ menuItem = liveBookmarksMenuItem.cloneNode(false);
+ menuItem.removeAttribute("selected");
+ menuItem.setAttribute("anonid", "chooseApplicationMenuItem");
+ menuItem.className = "menuitem-iconic chooseApplicationMenuItem";
+ menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem"));
+ handlersMenuPopup.appendChild(menuItem);
+
+ // separator
+ menuItem = liveBookmarksMenuItem.nextSibling.cloneNode(false);
+ handlersMenuPopup.appendChild(menuItem);
+
+ // List of web handlers
+ var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]
+ .getService(Ci.nsIWebContentConverterService);
+ var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType));
+ if (handlers.length != 0) {
+ for (let i = 0; i < handlers.length; ++i) {
+ menuItem = liveBookmarksMenuItem.cloneNode(false);
+ menuItem.removeAttribute("selected");
+ menuItem.className = "menuitem-iconic";
+ menuItem.setAttribute("label", handlers[i].name);
+ menuItem.setAttribute("handlerType", "web");
+ menuItem.setAttribute("webhandlerurl", handlers[i].uri);
+ handlersMenuPopup.appendChild(menuItem);
+ }
+ }
+
+ this._setSelectedHandler(feedType);
+
+ // "Subscribe using..."
+ this._setSubscribeUsingLabel();
+
+ // "Always use..." checkbox initial state
+ this._setAlwaysUseCheckedState(feedType);
+ this._setAlwaysUseLabel();
+
+ // We update the "Always use.." checkbox label whenever the selected item
+ // in the list is changed
+ handlersMenuPopup.addEventListener("command", this);
+
+ // Set up the "Subscribe Now" button
+ this._getUIElement("subscribeButton")
+ .addEventListener("command", this);
+
+ // first-run ui
+ var showFirstRunUI = true;
+ try {
+ showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
+ }
+ catch (ex) {
+ }
+ if (showFirstRunUI) {
+ var textfeedinfo1, textfeedinfo2;
+ switch (feedType) {
+ case Ci.nsIFeed.TYPE_VIDEO:
+ textfeedinfo1 = "feedSubscriptionVideoPodcast1";
+ textfeedinfo2 = "feedSubscriptionVideoPodcast2";
+ break;
+ case Ci.nsIFeed.TYPE_AUDIO:
+ textfeedinfo1 = "feedSubscriptionAudioPodcast1";
+ textfeedinfo2 = "feedSubscriptionAudioPodcast2";
+ break;
+ default:
+ textfeedinfo1 = "feedSubscriptionFeed1";
+ textfeedinfo2 = "feedSubscriptionFeed2";
+ }
+
+ this._document.getElementById("feedSubscriptionInfo1").textContent =
+ this._getString(textfeedinfo1);
+ this._document.getElementById("feedSubscriptionInfo2").textContent =
+ this._getString(textfeedinfo2);
+ header.setAttribute("firstrun", "true");
+ Services.prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
+ }
+ },
+
+ /**
+ * Returns the original URI object of the feed and ensures that this
+ * component is only ever invoked from the preview document.
+ * @param aWindow
+ * The window of the document invoking the BrowserFeedWriter
+ */
+ _getOriginalURI: function getOriginalURI(aWindow) {
+ let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let chan = docShell.currentDocumentChannel;
+
+ // We probably need to call Inherit() for this, but right now we can't call
+ // it from JS.
+ let attrs = docShell.getOriginAttributes();
+ let nullPrincipal = Services.scriptSecurityManager
+ .createNullPrincipal(attrs);
+
+ // This channel is not going to be opened, use a nullPrincipal
+ // and the most restrictive securityFlag.
+ let resolvedURI = NetUtil.newChannel({
+ uri: "about:feeds",
+ loadingPrincipal: nullPrincipal,
+ securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
+ }).URI;
+
+ if (resolvedURI.equals(chan.URI))
+ return chan.originalURI;
+
+ return null;
+ },
+
+ _window: null,
+ _document: null,
+ _feedURI: null,
+ _feedPrincipal: null,
+ _handlersMenuList: null,
+ _selectedAppMenuItem: null,
+ _defaultHandlerMenuItem: null,
+
+ // nsIDOMGlobalPropertyInitializer
+ init: function init(aWindow) {
+ this._feedURI = this._getOriginalURI(aWindow);
+ if (!this._feedURI)
+ return;
+
+ this._window = aWindow;
+ this._document = aWindow.document;
+ this._handlersMenuList = this._getUIElement("handlersMenuList");
+
+ this._feedPrincipal = Services.scriptSecurityManager
+ .createCodebasePrincipal(this._feedURI, {});
+
+ LOG("Subscribe Preview: feed uri = " + this._window.location.href);
+
+ // Set up the subscription UI
+ this._initSubscriptionUI();
+ Services.prefs.addObserver(PREF_SELECTED_ACTION, this);
+ Services.prefs.addObserver(PREF_SELECTED_READER, this);
+ Services.prefs.addObserver(PREF_SELECTED_WEB, this);
+ Services.prefs.addObserver(PREF_SELECTED_APP, this);
+ Services.prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this);
+ Services.prefs.addObserver(PREF_VIDEO_SELECTED_READER, this);
+ Services.prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this);
+ Services.prefs.addObserver(PREF_VIDEO_SELECTED_APP, this);
+
+ Services.prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this);
+ Services.prefs.addObserver(PREF_AUDIO_SELECTED_READER, this);
+ Services.prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this);
+ Services.prefs.addObserver(PREF_AUDIO_SELECTED_APP, this);
+
+ this._window.addEventListener("load", this);
+ this._window.addEventListener("unload", this);
+ },
+
+ _writeContent: function writeContent() {
+ if (!this._window)
+ return;
+
+ try {
+ // Set up the feed content
+ var container = this._getContainer();
+ if (!container)
+ return;
+
+ this._setTitleText(container);
+ this._setTitleImage(container);
+ this._writeFeedContent(container);
+ }
+ finally {
+ this._removeFeedFromCache();
+ }
+ },
+
+ _close: function close() {
+ this._window.removeEventListener("load", this);
+ this._window.removeEventListener("unload", this);
+ this._getUIElement("handlersMenuPopup")
+ .removeEventListener("command", this);
+ this._getUIElement("subscribeButton")
+ .removeEventListener("command", this);
+ this._document = null;
+ this._window = null;
+ Services.prefs.removeObserver(PREF_SELECTED_ACTION, this);
+ Services.prefs.removeObserver(PREF_SELECTED_READER, this);
+ Services.prefs.removeObserver(PREF_SELECTED_WEB, this);
+ Services.prefs.removeObserver(PREF_SELECTED_APP, this);
+ Services.prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this);
+ Services.prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this);
+ Services.prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this);
+ Services.prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this);
+
+ Services.prefs.removeObserver(PREF_AUDIO_SELECTED_ACTION, this);
+ Services.prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this);
+ Services.prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this);
+ Services.prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
+
+ this._removeFeedFromCache();
+ this.__bundle = null;
+ this._feedURI = null;
+ this._selectedAppMenuItem = null;
+ this._defaultHandlerMenuItem = null;
+ },
+
+ _removeFeedFromCache: function removeFeedFromCache() {
+ if (this._feedURI) {
+ var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]
+ .getService(Ci.nsIFeedResultService);
+ feedService.removeFeedResult(this._feedURI);
+ this._feedURI = null;
+ }
+ },
+
+ _subscribe: function subscribe() {
+ var feedType = this._getFeedType();
+
+ // Subscribe to the feed using the selected handler and save prefs
+ var defaultHandler = "reader";
+ var useAsDefault = this._getUIElement("alwaysUse").getAttribute("checked");
+
+ var selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList);
+ let subscribeCallback = function() {
+ if (selectedItem.hasAttribute("webhandlerurl")) {
+ var webURI = selectedItem.getAttribute("webhandlerurl");
+ Services.prefs.setCharPref(getPrefReaderForType(feedType), "web");
+
+ Services.prefs.setStringPref(getPrefWebForType(feedType), webURI);
+
+ var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]
+ .getService(Ci.nsIWebContentConverterService);
+ var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
+ if (handler) {
+ if (useAsDefault)
+ wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
+
+ this._window.location.href = handler.getHandlerURI(this._window.location.href);
+ }
+ }
+ else {
+ switch (selectedItem.getAttribute("anonid")) {
+ case "selectedAppMenuItem":
+ Services.prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsIFile,
+ this._selectedApp);
+ Services.prefs.setCharPref(getPrefReaderForType(feedType), "client");
+ break;
+ case "defaultHandlerMenuItem":
+ Services.prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsIFile,
+ this._defaultSystemReader);
+ Services.prefs.setCharPref(getPrefReaderForType(feedType), "client");
+ break;
+ case "liveBookmarksMenuItem":
+ defaultHandler = "bookmarks";
+ Services.prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
+ break;
+ case "messengerFeedsMenuItem":
+ defaultHandler = "messenger";
+ Services.prefs.setCharPref(getPrefReaderForType(feedType), "messenger");
+ break;
+ }
+ var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]
+ .getService(Ci.nsIFeedResultService);
+
+ // Pull the title and subtitle out of the document
+ var feedTitle = this._document.getElementById(TITLE_ID).textContent;
+ var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
+ feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
+ }
+
+ // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
+ // to either "reader" (If a web reader or if an application is selected),
+ // or to "messenger" (if the messenger feeds option is selected).
+ // Otherwise, we should set it to "ask"
+ if (useAsDefault) {
+ Services.prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
+ } else {
+ Services.prefs.setCharPref(getPrefActionForType(feedType), "ask");
+ }
+ }.bind(this);
+
+ // Show the file picker before subscribing if the
+ // choose application menuitem was chosen using the keyboard
+ if (selectedItem.getAttribute("anonid") == "chooseApplicationMenuItem") {
+ this._chooseClientApp(function(aResult) {
+ if (aResult) {
+ selectedItem =
+ this._getSelectedItemFromMenulist(this._handlersMenuList);
+ subscribeCallback();
+ }
+ }.bind(this));
+ } else {
+ subscribeCallback();
+ }
+ },
+
+ // nsIObserver
+ observe: function observe(subject, topic, data) {
+ if (!this._window) {
+ // this._window is null unless this.init was called with a trusted
+ // window object.
+ return;
+ }
+
+ var feedType = this._getFeedType();
+
+ if (topic == "nsPref:changed") {
+ switch (data) {
+ case PREF_SELECTED_READER:
+ case PREF_SELECTED_WEB:
+ case PREF_SELECTED_APP:
+ case PREF_VIDEO_SELECTED_READER:
+ case PREF_VIDEO_SELECTED_WEB:
+ case PREF_VIDEO_SELECTED_APP:
+ case PREF_AUDIO_SELECTED_READER:
+ case PREF_AUDIO_SELECTED_WEB:
+ case PREF_AUDIO_SELECTED_APP:
+ this._setSelectedHandler(feedType);
+ break;
+ case PREF_SELECTED_ACTION:
+ case PREF_VIDEO_SELECTED_ACTION:
+ case PREF_AUDIO_SELECTED_ACTION:
+ this._setAlwaysUseCheckedState(feedType);
+ }
+ }
+ },
+
+ classID: FEEDWRITER_CID,
+ QueryInterface: XPCOMUtils.generateQI([ Ci.nsIDOMGlobalPropertyInitializer,
+ Ci.nsIDOMEventListener,
+ Ci.nsIObserver])
+
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]);
diff --git a/comm/suite/components/feeds/SuiteFeeds.manifest b/comm/suite/components/feeds/SuiteFeeds.manifest
new file mode 100644
index 0000000000..e44e0c4c31
--- /dev/null
+++ b/comm/suite/components/feeds/SuiteFeeds.manifest
@@ -0,0 +1,11 @@
+component {88592f45-3866-4c8e-9d8a-ab58b290fcf7} FeedConverter.js
+contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.feed&to=*/* {88592f45-3866-4c8e-9d8a-ab58b290fcf7}
+contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.video.feed&to=*/* {88592f45-3866-4c8e-9d8a-ab58b290fcf7}
+contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.audio.feed&to=*/* {88592f45-3866-4c8e-9d8a-ab58b290fcf7}
+component {e5b05e9d-f037-48e4-b9a4-b99476582927} FeedConverter.js
+contract @mozilla.org/browser/feeds/result-service;1 {e5b05e9d-f037-48e4-b9a4-b99476582927}
+component {49bb6593-3aff-4eb3-a068-2712c28bd58e} FeedWriter.js
+contract @mozilla.org/browser/feeds/result-writer;1 {49bb6593-3aff-4eb3-a068-2712c28bd58e}
+component {792a7e82-06a0-437c-af63-b2d12e808acc} WebContentConverter.js
+contract @mozilla.org/embeddor.implemented/web-content-handler-registrar;1 {792a7e82-06a0-437c-af63-b2d12e808acc}
+category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1
diff --git a/comm/suite/components/feeds/WebContentConverter.js b/comm/suite/components/feeds/WebContentConverter.js
new file mode 100644
index 0000000000..ef519c7df2
--- /dev/null
+++ b/comm/suite/components/feeds/WebContentConverter.js
@@ -0,0 +1,818 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const WCCR_CONTRACTID = "@mozilla.org/embeddor.implemented/web-content-handler-registrar;1";
+const WCCR_CLASSID = Components.ID("{792a7e82-06a0-437c-af63-b2d12e808acc}");
+
+const WCC_CLASSID = Components.ID("{db7ebf28-cc40-415f-8a51-1b111851df1e}");
+const WCC_CLASSNAME = "Web Service Handler";
+
+const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
+const TYPE_ANY = "*/*";
+
+const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto.";
+const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types.";
+const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
+const PREF_SELECTED_ACTION = "browser.feeds.handler";
+const PREF_SELECTED_READER = "browser.feeds.handler.default";
+const PREF_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external";
+const PREF_ALLOW_DIFFERENT_HOST = "gecko.handlerService.allowRegisterFromDifferentHost";
+
+const STRING_BUNDLE_URI = "chrome://communicator/locale/feeds/subscribe.properties";
+
+const NS_ERROR_MODULE_DOM = 0x80530000;
+const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12;
+
+function LOG(str) {
+ try {
+ if (Services.prefs.getBoolPref("feeds.log"))
+ dump("*** Feeds: " + str + "\n");
+ }
+ catch (ex) {
+ }
+}
+
+function getNotificationBox(aWindow)
+{
+ return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler.parentNode.wrappedJSObject;
+}
+
+function WebContentConverter() {
+}
+
+WebContentConverter.prototype = {
+ convert: function convert() { },
+ asyncConvertData: function asyncConvertData() { },
+ onDataAvailable: function onDataAvailable() { },
+ onStopRequest: function onStopRequest() { },
+
+ onStartRequest: function onStartRequest(request, context) {
+ var wccr = Cc[WCCR_CONTRACTID]
+ .getService(Ci.nsIWebContentConverterService);
+ wccr.loadPreferredHandler(request);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIStreamConverter,
+ Ci.nsIStreamListener])
+};
+
+var WebContentConverterFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return new WebContentConverter().QueryInterface(iid);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIFactory])
+};
+
+function ServiceInfo(contentType, uri, name) {
+ this._contentType = contentType;
+ this._uri = uri;
+ this._name = name;
+}
+
+ServiceInfo.prototype = {
+ /**
+ * See nsIHandlerApp
+ */
+ get name() {
+ return this._name;
+ },
+
+ /**
+ * See nsIHandlerApp
+ */
+ equals: function equals(aHandlerApp) {
+ if (!aHandlerApp)
+ throw Cr.NS_ERROR_NULL_POINTER;
+
+ if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo &&
+ aHandlerApp.contentType == this.contentType &&
+ aHandlerApp.uri == this.uri)
+ return true;
+
+ return false;
+ },
+
+ /**
+ * See nsIWebContentHandlerInfo
+ */
+ get contentType() {
+ return this._contentType;
+ },
+
+ /**
+ * See nsIWebContentHandlerInfo
+ */
+ get uri() {
+ return this._uri;
+ },
+
+ /**
+ * See nsIWebContentHandlerInfo
+ */
+ getHandlerURI: function getHandlerURI(uri) {
+ return this._uri.replace(/%s/gi, encodeURIComponent(uri));
+ },
+
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIWebContentHandlerInfo])
+};
+
+function WebContentConverterRegistrar() {
+ this._contentTypes = { };
+ this._autoHandleContentTypes = { };
+}
+
+WebContentConverterRegistrar.prototype = {
+ __bundle: null,
+ get _bundle() {
+ if (!this.__bundle) {
+ this.__bundle = Services.strings.createBundle(STRING_BUNDLE_URI);
+ }
+ return this.__bundle;
+ },
+
+ _getFormattedString: function getFormattedString(key, params) {
+ return this._bundle.formatStringFromName(key, params, params.length);
+ },
+
+ _getString: function getString(key) {
+ try {
+ return this._bundle.GetStringFromName(key);
+ } catch(e) {
+ LOG("Couldn't retrieve key from bundle");
+ }
+
+ return null;
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ getAutoHandler: function getAutoHandler(contentType) {
+ contentType = this._resolveContentType(contentType);
+ if (contentType in this._autoHandleContentTypes)
+ return this._autoHandleContentTypes[contentType];
+ return null;
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ setAutoHandler: function setAutoHandler(contentType, handler) {
+ if (handler && !this._typeIsRegistered(contentType, handler.uri))
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ contentType = this._resolveContentType(contentType);
+ this._setAutoHandler(contentType, handler);
+
+ var autoBranch = Services.prefs.getBranch(PREF_CONTENTHANDLERS_AUTO);
+ if (handler)
+ autoBranch.setCharPref(contentType, handler.uri);
+ else if (autoBranch.prefHasUserValue(contentType))
+ autoBranch.clearUserPref(contentType);
+
+ Services.prefs.savePrefFile(null);
+ },
+
+ /**
+ * Update the internal data structure (not persistent)
+ */
+ _setAutoHandler: function setAutoHandler(contentType, handler) {
+ if (handler)
+ this._autoHandleContentTypes[contentType] = handler;
+ else if (contentType in this._autoHandleContentTypes)
+ delete this._autoHandleContentTypes[contentType];
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ getWebContentHandlerByURI: function getWebContentHandlerByURI(contentType, uri) {
+ var handlers = this.getContentHandlers(contentType, { });
+ for (let i = 0; i < handlers.length; ++i) {
+ if (handlers[i].uri == uri)
+ return handlers[i];
+ }
+ return null;
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ loadPreferredHandler: function loadPreferredHandler(request) {
+ var channel = request.QueryInterface(Ci.nsIChannel);
+ var contentType = this._resolveContentType(channel.contentType);
+ var handler = this.getAutoHandler(contentType);
+ if (handler) {
+ request.cancel(Cr.NS_ERROR_FAILURE);
+
+ let triggeringPrincipal = channel.loadInfo
+ ? channel.loadInfo.triggeringPrincipal
+ : Services.scriptSecurityManager.getSystemPrincipal();
+
+ let webNavigation = channel.notificationCallbacks
+ .getInterface(Ci.nsIWebNavigation);
+ webNavigation.loadURI(handler.getHandlerURI(channel.URI.spec),
+ Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
+ null, null, null, triggeringPrincipal);
+ }
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ removeProtocolHandler: function removeProtocolHandler(aProtocol, aURITemplate) {
+ var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
+ var handlers = handlerInfo.possibleApplicationHandlers;
+ for (let i = 0; i < handlers.length; i++) {
+ try { // We only want to test web handlers
+ let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
+ if (handler.uriTemplate == aURITemplate) {
+ handlers.removeElementAt(i);
+ let hs = Cc["@mozilla.org/uriloader/handler-service;1"]
+ .getService(Ci.nsIHandlerService);
+ hs.store(handlerInfo);
+ return;
+ }
+ } catch (e) {
+ /* it wasn't a web handler */
+ }
+ }
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ removeContentHandler: function removeContentHandler(contentType, uri) {
+ function notURI(serviceInfo) {
+ return serviceInfo.uri != uri;
+ }
+
+ if (contentType in this._contentTypes) {
+ this._contentTypes[contentType] = this._contentTypes[contentType]
+ .filter(notURI);
+ }
+ },
+
+ /**
+ *
+ */
+ _mappings: {
+ "application/rss+xml": TYPE_MAYBE_FEED,
+ "application/atom+xml": TYPE_MAYBE_FEED,
+ },
+
+ /**
+ * These are types for which there is a separate content converter aside
+ * from our built in generic one. We should not automatically register
+ * a factory for creating a converter for these types.
+ */
+ _blockedTypes: {
+ "application/vnd.mozilla.maybe.feed": true,
+ },
+
+ /**
+ * Determines the "internal" content type based on the _mappings.
+ * @param contentType
+ * @returns The resolved contentType value.
+ */
+ _resolveContentType: function resolveContentType(contentType) {
+ if (contentType in this._mappings)
+ return this._mappings[contentType];
+ return contentType;
+ },
+
+ _makeURI: function(aURL, aOriginCharset, aBaseURI) {
+ return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
+ },
+
+ _checkAndGetURI: function checkAndGetURI(aURIString, aContentWindow) {
+ try {
+ var uri = this._makeURI(aURIString);
+ } catch (ex) {
+ // not supposed to throw according to spec
+ return;
+ }
+
+ // For security reasons we reject non-http(s) urls (see bug 354316),
+ // we may need to revise this once we support more content types
+ // XXX this should be a "security exception" according to spec, but that
+ // isn't defined yet.
+ if (uri.scheme != "http" && uri.scheme != "https")
+ throw("Permission denied to add " + uri.spec + " as a content or protocol handler");
+
+ // We also reject handlers registered from a different host (see bug 402287)
+ // The pref allows us to test the feature
+ if ((!Services.prefs.prefHasUserValue(PREF_ALLOW_DIFFERENT_HOST) ||
+ !Services.prefs.getBoolPref(PREF_ALLOW_DIFFERENT_HOST)) &&
+ aContentWindow.location.hostname != uri.host)
+ throw("Permission denied to add " + uri.spec + " as a content or protocol handler");
+
+ // If the uri doesn't contain '%s', it won't be a good handler
+ if (!uri.spec.includes("%s"))
+ throw NS_ERROR_DOM_SYNTAX_ERR;
+
+ return uri;
+ },
+
+ /**
+ * Determines if a web handler is already registered.
+ *
+ * @param aProtocol
+ * The scheme of the web handler we are checking for.
+ * @param aURITemplate
+ * The URI template that the handler uses to handle the protocol.
+ * @return true if it is already registered, false otherwise.
+ */
+ _protocolHandlerRegistered: function protocolHandlerRegistered(aProtocol, aURITemplate) {
+ var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
+ var handlers = handlerInfo.possibleApplicationHandlers;
+ for (let i = 0; i < handlers.length; i++) {
+ try { // We only want to test web handlers
+ let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
+ if (handler.uriTemplate == aURITemplate)
+ return true;
+ } catch (e) { /* it wasn't a web handler */ }
+ }
+ return false;
+ },
+
+ /**
+ * See nsIWebContentHandlerRegistrar
+ */
+ registerProtocolHandler: function registerProtocolHandler(aProtocol, aURIString,
+ aTitle, aContentWindow) {
+ LOG("registerProtocolHandler(" + aProtocol + "," + aURIString + "," + aTitle + ")");
+
+ var win = aContentWindow;
+ var isPB = win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsILoadContext)
+ .usePrivateBrowsing;
+ if (isPB) {
+ // Inside the private browsing mode, we don't want to alert the user to save
+ // a protocol handler. We log it to the error console so that web developers
+ // would have some way to tell what's going wrong.
+ Services.console.logStringMessage("Web page denied access to register a protocol handler inside private browsing mode");
+ return;
+ }
+
+ // First, check to make sure this isn't already handled internally (we don't
+ // want to let them take over, say "chrome").
+ var handler = Services.io.getProtocolHandler(aProtocol);
+ if (!(handler instanceof Ci.nsIExternalProtocolHandler)) {
+ // This is handled internally, so we don't want them to register
+ Services.console.logStringMessage("Permission denied to add " + aURIString + " as a protocol handler");
+ return;
+ }
+
+ // check if it is in the black list
+ var allowed;
+ try {
+ allowed = Services.prefs.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol);
+ }
+ catch (e) {
+ allowed = Services.prefs.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default");
+ }
+ if (!allowed) {
+ Services.console.logStringMessage("Not allowed to register a protocol handler for " + aProtocol);
+ return;
+ }
+
+ var uri = this._checkAndGetURI(aURIString, aContentWindow);
+
+ var buttons, message;
+ if (this._protocolHandlerRegistered(aProtocol, uri.spec))
+ message = this._getFormattedString("protocolHandlerRegistered",
+ [aTitle, aProtocol]);
+ else {
+ // Now Ask the user and provide the proper callback
+ message = this._getFormattedString("addProtocolHandler",
+ [aTitle, uri.host, aProtocol]);
+ var notificationIcon = uri.resolve("/favicon.ico");
+ var notificationValue = "Protocol Registration: " + aProtocol;
+ var addButton = {
+ label: this._getString("addProtocolHandlerAddButton"),
+ accessKey: this._getString("addHandlerAddButtonAccesskey"),
+ protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle },
+
+ callback: function addProtocolHandlerButtonCallback(aNotification, aButtonInfo) {
+ var protocol = aButtonInfo.protocolInfo.protocol;
+ var uri = aButtonInfo.protocolInfo.uri;
+ var name = aButtonInfo.protocolInfo.name;
+
+ var handler = Cc["@mozilla.org/uriloader/web-handler-app;1"]
+ .createInstance(Ci.nsIWebHandlerApp);
+ handler.name = name;
+ handler.uriTemplate = uri;
+
+ var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var handlerInfo = eps.getProtocolHandlerInfo(protocol);
+ handlerInfo.possibleApplicationHandlers.appendElement(handler);
+
+ // Since the user has agreed to add a new handler, chances are good
+ // that the next time they see a handler of this type, they're going
+ // to want to use it. Reset the handlerInfo to ask before the next
+ // use.
+ handlerInfo.alwaysAskBeforeHandling = true;
+
+ var hs = Cc["@mozilla.org/uriloader/handler-service;1"]
+ .getService(Ci.nsIHandlerService);
+ hs.store(handlerInfo);
+ }
+ };
+ buttons = [addButton];
+ }
+
+ var notificationBox = getNotificationBox(aContentWindow);
+ notificationBox.appendNotification(message,
+ notificationValue,
+ notificationIcon,
+ notificationBox.PRIORITY_INFO_LOW,
+ buttons);
+ },
+
+ /**
+ * See nsIWebContentHandlerRegistrar
+ * If a DOM window is provided, then the request came from content, so we
+ * prompt the user to confirm the registration.
+ */
+ registerContentHandler: function registerContentHandler(aContentType, aURIString,
+ aTitle, aContentWindow) {
+ LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")");
+
+ // We only support feed types at present.
+ // XXX this should be a "security exception" according to spec, but that
+ // isn't defined yet.
+ var contentType = this._resolveContentType(aContentType);
+ if (contentType != TYPE_MAYBE_FEED)
+ return;
+
+ if (aContentWindow) {
+ var uri = this._checkAndGetURI(aURIString, aContentWindow);
+
+ this._appendFeedReaderNotification(uri, aTitle, getNotificationBox(aContentWindow));
+ }
+ else
+ this._registerContentHandler(contentType, aURIString, aTitle);
+ },
+
+ /**
+ * Appends a notifcation for the given feed reader details.
+ *
+ * The notification could be either a pseudo-dialog which lets
+ * the user to add the feed reader:
+ * [ [icon] Add %feed-reader-name% (%feed-reader-host%) as a Feed Reader? (Add) [x] ]
+ *
+ * or a simple message for the case where the feed reader is already registered:
+ * [ [icon] %feed-reader-name% is already registered as a Feed Reader [x] ]
+ *
+ * A new notification isn't appended if the given notificationbox has a
+ * notification for the same feed reader.
+ *
+ * @param aURI
+ * The url of the feed reader as a nsIURI object
+ * @param aName
+ * The feed reader name as it was passed to registerContentHandler
+ * @param aNotificationBox
+ * The notification box to which a notification might be appended
+ * @return true if a notification has been appended, false otherwise.
+ */
+ _appendFeedReaderNotification: function appendFeedReaderNotification(aURI, aName, aNotificationBox) {
+ var uriSpec = aURI.spec;
+ var notificationValue = "feed reader notification: " + uriSpec;
+ var notificationIcon = aURI.resolve("/favicon.ico");
+
+ // Don't append a new notification if the notificationbox
+ // has a notification for the given feed reader already
+ if (aNotificationBox.getNotificationWithValue(notificationValue))
+ return false;
+
+ var buttons, message;
+ if (this.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uriSpec))
+ message = this._getFormattedString("handlerRegistered", [aName]);
+ else {
+ message = this._getFormattedString("addHandler", [aName, aURI.host]);
+ var self = this;
+ var addButton = {
+ _outer: self,
+ label: self._getString("addHandlerAddButton"),
+ accessKey: self._getString("addHandlerAddButtonAccesskey"),
+ feedReaderInfo: { uri: uriSpec, name: aName },
+
+ /* static */
+ callback: function addFeedReaderButtonCallback(aNotification, aButtonInfo) {
+ var uri = aButtonInfo.feedReaderInfo.uri;
+ var name = aButtonInfo.feedReaderInfo.name;
+ var outer = aButtonInfo._outer;
+
+ // The reader could have been added from another window mean while
+ if (!outer.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uri))
+ outer._registerContentHandler(TYPE_MAYBE_FEED, uri, name);
+
+ // avoid reference cycles
+ aButtonInfo._outer = null;
+
+ return false;
+ }
+ };
+ buttons = [addButton];
+ }
+
+ aNotificationBox.appendNotification(message,
+ notificationValue,
+ notificationIcon,
+ aNotificationBox.PRIORITY_INFO_LOW,
+ buttons);
+ return true;
+ },
+
+ /**
+ * Save Web Content Handler metadata to persistent preferences.
+ * @param contentType
+ * The content Type being handled
+ * @param uri
+ * The uri of the web service
+ * @param title
+ * The human readable name of the web service
+ *
+ * This data is stored under:
+ *
+ * browser.contentHandlers.type0 = content/type
+ * browser.contentHandlers.uri0 = http://www.foo.com/q=%s
+ * browser.contentHandlers.title0 = Foo 2.0alphr
+ */
+ _saveContentHandlerToPrefs: function saveContentHandlerToPrefs(contentType, uri, title) {
+ var i = 0;
+ var typeBranch = null;
+ while (true) {
+ typeBranch = Services.prefs.getBranch(PREF_CONTENTHANDLERS_BRANCH + i + ".");
+ try {
+ typeBranch.getCharPref("type");
+ ++i;
+ }
+ catch (e) {
+ // No more handlers
+ break;
+ }
+ }
+ if (typeBranch) {
+ typeBranch.setCharPref("type", contentType);
+ var pls = Cc["@mozilla.org/pref-localizedstring;1"]
+ .createInstance(Ci.nsIPrefLocalizedString);
+ pls.data = uri;
+ typeBranch.setComplexValue("uri",
+ Ci.nsIPrefLocalizedString, pls);
+ pls.data = title;
+ typeBranch.setComplexValue("title",
+ Ci.nsIPrefLocalizedString, pls);
+
+ Services.prefs.savePrefFile(null);
+ }
+ },
+
+ /**
+ * Determines if there is a type with a particular uri registered for the
+ * specified content type already.
+ * @param contentType
+ * The content type that the uri handles
+ * @param uri
+ * The uri of the
+ */
+ _typeIsRegistered: function typeIsRegistered(contentType, uri) {
+ if (!(contentType in this._contentTypes))
+ return false;
+
+ var services = this._contentTypes[contentType];
+ for (let i = 0; i < services.length; ++i) {
+ // This uri has already been registered
+ if (services[i].uri == uri)
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Gets a stream converter contract id for the specified content type.
+ * @param contentType
+ * The source content type for the conversion.
+ * @returns A contract id to construct a converter to convert between the
+ * contentType and *\/*.
+ */
+ _getConverterContractID: function getConverterContractID(contentType) {
+ const template = "@mozilla.org/streamconv;1?from=%s&to=*/*";
+ return template.replace(/%s/, contentType);
+ },
+
+ /**
+ * Register a web service handler for a content type.
+ *
+ * @param contentType
+ * the content type being handled
+ * @param uri
+ * the URI of the web service
+ * @param title
+ * the human readable name of the web service
+ */
+ _registerContentHandler: function registerContentHandler(contentType, uri, title) {
+ this._updateContentTypeHandlerMap(contentType, uri, title);
+ this._saveContentHandlerToPrefs(contentType, uri, title);
+
+ if (contentType == TYPE_MAYBE_FEED) {
+ // Make the new handler the last-selected reader in the preview page
+ // and make sure the preview page is shown the next time a feed is visited
+ Services.prefs.setCharPref(PREF_SELECTED_READER, "web");
+
+ Services.prefs.setStringPref(PREF_SELECTED_WEB, uri);
+ Services.prefs.setCharPref(PREF_SELECTED_ACTION, "ask");
+ this._setAutoHandler(TYPE_MAYBE_FEED, null);
+ }
+ },
+
+ /**
+ * Update the content type -> handler map. This mapping is not persisted, use
+ * registerContentHandler or _saveContentHandlerToPrefs for that purpose.
+ * @param contentType
+ * The content Type being handled
+ * @param uri
+ * The uri of the web service
+ * @param title
+ * The human readable name of the web service
+ */
+ _updateContentTypeHandlerMap: function updateContentTypeHandlerMap(contentType, uri, title) {
+ if (!(contentType in this._contentTypes))
+ this._contentTypes[contentType] = [];
+
+ // Avoid adding duplicates
+ if (this._typeIsRegistered(contentType, uri))
+ return;
+
+ this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title));
+
+ if (!(contentType in this._blockedTypes)) {
+ var converterContractID = this._getConverterContractID(contentType);
+ var cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID,
+ WebContentConverterFactory);
+ }
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ getContentHandlers: function getContentHandlers(contentType, countRef) {
+ countRef.value = 0;
+ if (!(contentType in this._contentTypes))
+ return [];
+
+ var handlers = this._contentTypes[contentType];
+ countRef.value = handlers.length;
+ return handlers;
+ },
+
+ /**
+ * See nsIWebContentConverterService
+ */
+ resetHandlersForType: function resetHandlersForType(contentType) {
+ // currently unused within the tree, so only useful for extensions; previous
+ // impl. was buggy (and even infinite-looped!), so I argue that this is a
+ // definite improvement
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ /**
+ * Registers a handler from the settings on a preferences branch.
+ *
+ * @param branch
+ * an nsIPrefBranch containing "type", "uri", and "title" preferences
+ * corresponding to the content handler to be registered
+ */
+ _registerContentHandlerWithBranch: function(branch) {
+ /**
+ * Since we support up to six predefined readers, we need to handle gaps
+ * better, since the first branch with user-added values will be .6
+ *
+ * How we deal with that is to check to see if there's no prefs in the
+ * branch and stop cycling once that's true. This doesn't fix the case
+ * where a user manually removes a reader, but that's not supported yet!
+ */
+ var vals = branch.getChildList("");
+ if (vals.length == 0)
+ return;
+
+ try {
+ var type = branch.getCharPref("type");
+ var uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
+ var title = branch.getComplexValue("title",
+ Ci.nsIPrefLocalizedString).data;
+ this._updateContentTypeHandlerMap(type, uri, title);
+ }
+ catch(ex) {
+ // do nothing, the next branch might have values
+ }
+ },
+
+ /**
+ * Load the auto handler, content handler and protocol tables from
+ * preferences.
+ */
+ _init: function init() {
+ var kids = Services.prefs.getBranch(PREF_CONTENTHANDLERS_BRANCH)
+ .getChildList("");
+ // first get the numbers of the providers by getting all ###.uri prefs
+ var nums = [];
+ for (let i = 0; i < kids.length; i++) {
+ let match = /^(\d+)\.uri$/.exec(kids[i]);
+ if (match)
+ nums.push(match[1]);
+ }
+ // sort them, to get them back in order
+ nums.sort(function(a, b) {return a - b;});
+ // now register them
+ for (let i = 0; i < nums.length; i++) {
+ let branch = Services.prefs.getBranch(PREF_CONTENTHANDLERS_BRANCH + nums[i] + ".");
+ this._registerContentHandlerWithBranch(branch);
+ }
+
+ // We need to do this _after_ registering all of the available handlers,
+ // so that getWebContentHandlerByURI can return successfully.
+ try {
+ var autoBranch = Services.prefs.getBranch(PREF_CONTENTHANDLERS_AUTO);
+ var childPrefs = autoBranch.getChildList("");
+ for (let i = 0; i < childPrefs.length; ++i) {
+ let type = childPrefs[i];
+ let uri = autoBranch.getCharPref(type);
+ if (uri) {
+ let handler = this.getWebContentHandlerByURI(type, uri);
+ this._setAutoHandler(type, handler);
+ }
+ }
+ }
+ catch (e) {
+ }
+ },
+
+ /**
+ * See nsIObserver
+ */
+ observe: function observe(subject, topic, data) {
+ switch (topic) {
+ case "app-startup":
+ Services.obs.addObserver(this, "final-ui-startup");
+ break;
+ case "final-ui-startup":
+ Services.obs.removeObserver(this, "final-ui-startup");
+ this._init();
+ break;
+ }
+ },
+
+ /**
+ * See nsIFactory
+ */
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ return this.QueryInterface(iid);
+ },
+
+ classID: WCCR_CLASSID,
+ classInfo: XPCOMUtils.generateCI({
+ classID: WCCR_CLASSID,
+ contractID: WCCR_CONTRACTID,
+ interfaces: [Ci.nsIWebContentConverterService,
+ Ci.nsIWebContentHandlerRegistrar,
+ Ci.nsIObserver,
+ Ci.nsIFactory],
+ flags: Ci.nsIClassInfo.DOM_OBJECT}),
+
+ /**
+ * See nsISupports
+ */
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIWebContentConverterService,
+ Ci.nsIWebContentHandlerRegistrar,
+ Ci.nsIObserver,
+ Ci.nsIFactory])
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrar]);
diff --git a/comm/suite/components/feeds/content/subscribe.css b/comm/suite/components/feeds/content/subscribe.css
new file mode 100644
index 0000000000..1ba7611b80
--- /dev/null
+++ b/comm/suite/components/feeds/content/subscribe.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+#feedSubscribeLine {
+ -moz-binding: url(chrome://communicator/content/feeds/subscribe.xml#feedreaderUI);
+}
diff --git a/comm/suite/components/feeds/content/subscribe.xhtml b/comm/suite/components/feeds/content/subscribe.xhtml
new file mode 100644
index 0000000000..c1c4b90765
--- /dev/null
+++ b/comm/suite/components/feeds/content/subscribe.xhtml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % globalDTD
+ SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % feedDTD
+ SYSTEM "chrome://communicator/locale/feeds/subscribe.dtd">
+ %feedDTD;
+]>
+
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<html id="feedHandler"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&feedPage.title;</title>
+ <link rel="stylesheet"
+ href="chrome://communicator/content/feeds/subscribe.css"
+ type="text/css"
+ media="all"/>
+ <link rel="stylesheet"
+ href="chrome://communicator/skin/feed-subscribe.css"
+ type="text/css"
+ media="all"/>
+ </head>
+ <body>
+ <div id="feedHeaderContainer">
+ <div id="feedHeader" dir="&locale.dir;">
+ <div id="feedIntroText">
+ <p id="feedSubscriptionInfo1" />
+ <p id="feedSubscriptionInfo2" />
+ </div>
+ <div id="feedSubscribeLine" />
+ </div>
+ </div>
+
+ <div id="feedBody">
+ <div id="feedTitle">
+ <a id="feedTitleLink">
+ <img id="feedTitleImage"/>
+ </a>
+ <div id="feedTitleContainer">
+ <h1 id="feedTitleText"/>
+ <h2 id="feedSubtitleText"/>
+ </div>
+ </div>
+ <div id="feedContent"/>
+ </div>
+ </body>
+</html>
diff --git a/comm/suite/components/feeds/content/subscribe.xml b/comm/suite/components/feeds/content/subscribe.xml
new file mode 100644
index 0000000000..33a9e539df
--- /dev/null
+++ b/comm/suite/components/feeds/content/subscribe.xml
@@ -0,0 +1,42 @@
+<?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 bindings SYSTEM "chrome://communicator/locale/feeds/subscribe.dtd">
+
+<bindings id="feedBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <binding id="feedreaderUI" bindToUntrustedContent="true">
+ <content>
+ <xul:vbox>
+ <xul:hbox align="center">
+ <xul:description anonid="subscribeUsingDescription" class="subscribeUsingDescription"/>
+ <xul:menulist anonid="handlersMenuList" class="handlersMenuList" aria-labelledby="subscribeUsingDescription">
+ <xul:menupopup anonid="handlersMenuPopup" class="handlersMenuPopup">
+ <xul:menuitem anonid="messengerFeedsMenuItem" label="&feedMessenger;" class="menuitem-iconic messengerFeedsMenuItem" image="chrome://communicator/skin/icons/feedIcon16.png" selected="true"/>
+ <xul:menuitem anonid="liveBookmarksMenuItem" label="&feedLiveBookmarks;" class="menuitem-iconic liveBookmarksMenuItem" image="chrome://communicator/skin/icons/feedIcon16.png"/>
+ <xul:menuseparator/>
+ </xul:menupopup>
+ </xul:menulist>
+ </xul:hbox>
+ <xul:hbox>
+ <xul:checkbox anonid="alwaysUse" class="alwaysUse" checked="false"/>
+ </xul:hbox>
+ <xul:hbox align="center">
+ <xul:spacer flex="1"/>
+ <xul:button label="&feedSubscribeNow;" anonid="subscribeButton" class="subscribeButton"/>
+ </xul:hbox>
+ </xul:vbox>
+ </content>
+ <implementation>
+ <constructor>
+ new BrowserFeedWriter();
+ </constructor>
+ </implementation>
+ <resources>
+ <stylesheet src="chrome://communicator/skin/feed-subscribe-ui.css"/>
+ </resources>
+ </binding>
+</bindings>
diff --git a/comm/suite/components/feeds/jar.mn b/comm/suite/components/feeds/jar.mn
new file mode 100644
index 0000000000..b1df80fa40
--- /dev/null
+++ b/comm/suite/components/feeds/jar.mn
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/feeds/subscribe.css (content/subscribe.css)
+ content/communicator/feeds/subscribe.xhtml (content/subscribe.xhtml)
+ content/communicator/feeds/subscribe.xml (content/subscribe.xml)
diff --git a/comm/suite/components/feeds/moz.build b/comm/suite/components/feeds/moz.build
new file mode 100644
index 0000000000..07571081e3
--- /dev/null
+++ b/comm/suite/components/feeds/moz.build
@@ -0,0 +1,27 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# XPIDL_SOURCES += [
+# 'nsIFeedResultService.idl',
+# 'nsIWebContentConverterRegistrar.idl',
+# ]
+
+# XPIDL_MODULE = 'suite-feeds'
+
+SOURCES += [
+ "nsFeedSniffer.cpp",
+]
+
+EXTRA_COMPONENTS += [
+ "FeedConverter.js",
+ "FeedWriter.js",
+ "SuiteFeeds.manifest",
+ "WebContentConverter.js",
+]
+
+FINAL_LIBRARY = "suite"
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/feeds/nsFeedSniffer.cpp b/comm/suite/components/feeds/nsFeedSniffer.cpp
new file mode 100644
index 0000000000..eba3a012ff
--- /dev/null
+++ b/comm/suite/components/feeds/nsFeedSniffer.cpp
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsFeedSniffer.h"
+
+#include "mozilla/Unused.h"
+
+#include "nsNetCID.h"
+#include "nsXPCOM.h"
+#include "nsCOMPtr.h"
+#include "nsStringStream.h"
+
+#include "nsICategoryManager.h"
+#include "nsIServiceManager.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIStreamConverterService.h"
+#include "nsIStreamConverter.h"
+
+#include "nsIStreamListener.h"
+
+#include "nsIHttpChannel.h"
+#include "nsIMIMEHeaderParam.h"
+
+#include "nsMimeTypes.h"
+#include "nsIURI.h"
+#include <algorithm>
+
+#define TYPE_ATOM "application/atom+xml"
+#define TYPE_RSS "application/rss+xml"
+#define TYPE_MAYBE_FEED "application/vnd.mozilla.maybe.feed"
+
+#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_RSS "http://purl.org/rss/1.0/"
+
+#define MAX_BYTES 512u
+
+NS_IMPL_ISUPPORTS(nsFeedSniffer,
+ nsIContentSniffer,
+ nsIStreamListener,
+ nsIRequestObserver)
+
+nsresult
+nsFeedSniffer::ConvertEncodedData(nsIRequest* request,
+ const uint8_t* data,
+ uint32_t length)
+{
+ nsresult rv = NS_OK;
+
+ mDecodedData = "";
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
+ if (!httpChannel)
+ return NS_ERROR_NO_INTERFACE;
+
+ nsAutoCString contentEncoding;
+
+ mozilla::Unused << httpChannel->GetResponseHeader("Content-Encoding"_ns,
+ contentEncoding);
+ if (!contentEncoding.IsEmpty()) {
+ nsCOMPtr<nsIStreamConverterService> converterService(do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID));
+ if (converterService) {
+ ToLowerCase(contentEncoding);
+
+ nsCOMPtr<nsIStreamListener> converter;
+ rv = converterService->AsyncConvertData(contentEncoding.get(),
+ "uncompressed", this, nullptr,
+ getter_AddRefs(converter));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ converter->OnStartRequest(request);
+
+ nsCOMPtr<nsIStringInputStream> rawStream =
+ do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID);
+ if (!rawStream)
+ return NS_ERROR_FAILURE;
+
+ rv = rawStream->SetData((const char*)data, length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = converter->OnDataAvailable(request, rawStream, 0, length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ converter->OnStopRequest(request, NS_OK);
+ }
+ }
+ return rv;
+}
+
+template<int N>
+static bool
+StringBeginsWithLowercaseLiteral(nsAString& aString,
+ const char (&aSubstring)[N])
+{
+ return StringHead(aString, N).LowerCaseEqualsLiteral(aSubstring);
+}
+
+bool
+HasAttachmentDisposition(nsIHttpChannel* httpChannel)
+{
+ if (!httpChannel)
+ return false;
+
+ uint32_t disp;
+ nsresult rv = httpChannel->GetContentDisposition(&disp);
+
+ if (NS_SUCCEEDED(rv) && disp == nsIChannel::DISPOSITION_ATTACHMENT)
+ return true;
+
+ return false;
+}
+
+/**
+ * @return the first occurrence of a character within a string buffer,
+ * or nullptr if not found
+ */
+inline const char*
+FindChar(char c, const char *begin, const char *end)
+{
+ return static_cast<const char *>(memchr(begin, c, end - begin));
+}
+
+/**
+ *
+ * Determine if a substring is the "documentElement" in the document.
+ *
+ * All of our sniffed substrings: <rss, <feed, <rdf:RDF must be the "document"
+ * element within the XML DOM, i.e. the root container element. Otherwise,
+ * it's possible that someone embedded one of these tags inside a document of
+ * another type, e.g. a HTML document, and we don't want to show the preview
+ * page if the document isn't actually a feed.
+ *
+ * @param start
+ * The beginning of the data being sniffed
+ * @param end
+ * The end of the data being sniffed, right before the substring that
+ * was found.
+ * @returns true if the found substring is the documentElement, false
+ * otherwise.
+ */
+static bool
+IsDocumentElement(const char *start, const char* end)
+{
+ // For every tag in the buffer, check to see if it's a PI, Doctype or
+ // comment, our desired substring or something invalid.
+ while ( (start = FindChar('<', start, end)) ) {
+ ++start;
+ if (start >= end)
+ return false;
+
+ // Check to see if the character following the '<' is either '?' or '!'
+ // (processing instruction or doctype or comment)... these are valid nodes
+ // to have in the prologue.
+ if (*start != '?' && *start != '!')
+ return false;
+
+ // Now advance the iterator until the '>' (We do this because we don't want
+ // to sniff indicator substrings that are embedded within other nodes, e.g.
+ // comments: <!-- <rdf:RDF .. > -->
+ start = FindChar('>', start, end);
+ if (!start)
+ return false;
+
+ ++start;
+ }
+ return true;
+}
+
+/**
+ * Determines whether or not a string exists as the root element in an XML data
+ * string buffer.
+ * @param dataString
+ * The data being sniffed
+ * @param substring
+ * The substring being tested for existence and root-ness.
+ * @returns true if the substring exists and is the documentElement, false
+ * otherwise.
+ */
+static bool
+ContainsTopLevelSubstring(nsACString& dataString, const char *substring)
+{
+ nsACString::const_iterator start, end;
+ dataString.BeginReading(start);
+ dataString.EndReading(end);
+
+ if (!FindInReadable(nsCString(substring), start, end)){
+ return false;
+ }
+
+ auto offset = start.get() - dataString.Data();
+
+ const char *begin = dataString.BeginReading();
+
+ // Only do the validation when we find the substring.
+ return IsDocumentElement(begin, begin + offset);
+}
+
+NS_IMETHODIMP
+nsFeedSniffer::GetMIMETypeFromContent(nsIRequest* request,
+ const uint8_t* data,
+ uint32_t length,
+ nsACString& sniffedType)
+{
+ nsCOMPtr<nsIHttpChannel> channel(do_QueryInterface(request));
+ if (!channel)
+ return NS_ERROR_NO_INTERFACE;
+
+ // Check that this is a GET request, since you can't subscribe to a POST...
+ nsAutoCString method;
+ nsresult rv;
+ mozilla::Unused << channel->GetRequestMethod(method);
+ if (!method.EqualsLiteral("GET")) {
+ sniffedType.Truncate();
+ return NS_OK;
+ }
+
+ // We need to find out if this is a load of a view-source document. In this
+ // case we do not want to override the content type, since the source display
+ // does not need to be converted from feed format to XUL. More importantly,
+ // we don't want to change the content type from something
+ // nsContentDLF::CreateInstance knows about (e.g. application/xml, text/html
+ // etc) to something that only the application fe knows about (maybe.feed)
+ // thus deactivating syntax highlighting.
+ nsCOMPtr<nsIURI> originalURI;
+ channel->GetOriginalURI(getter_AddRefs(originalURI));
+
+ nsAutoCString scheme;
+ originalURI->GetScheme(scheme);
+ if (scheme.EqualsLiteral("view-source")) {
+ sniffedType.Truncate();
+ return NS_OK;
+ }
+
+ // Check the Content-Type to see if it is set correctly. If it is set to
+ // something specific that we think is a reliable indication of a feed, don't
+ // bother sniffing since we assume the site maintainer knows what they're
+ // doing.
+ nsAutoCString contentType;
+ channel->GetContentType(contentType);
+ bool noSniff = contentType.EqualsLiteral(TYPE_RSS) ||
+ contentType.EqualsLiteral(TYPE_ATOM);
+
+ if (noSniff) {
+ // check for an attachment after we have a likely feed.
+ if(HasAttachmentDisposition(channel)) {
+ sniffedType.Truncate();
+ return NS_OK;
+ }
+
+ // set the feed header as a response header, since we have good metadata
+ // telling us that the feed is supposed to be RSS or Atom
+ mozilla::DebugOnly<nsresult> rv =
+ channel->SetResponseHeader("X-Moz-Is-Feed"_ns,
+ "1"_ns, false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ sniffedType.AssignLiteral(TYPE_MAYBE_FEED);
+ return NS_OK;
+ }
+
+ // Don't sniff arbitrary types. Limit sniffing to situations that
+ // we think can reasonably arise.
+ if (!contentType.EqualsLiteral(TEXT_HTML) &&
+ !contentType.EqualsLiteral(APPLICATION_OCTET_STREAM) &&
+ // Same criterion as XMLHttpRequest. Should we be checking for "+xml"
+ // and check for text/xml and application/xml by hand instead?
+ contentType.Find("xml") == -1) {
+ sniffedType.Truncate();
+ return NS_OK;
+ }
+
+ // Now we need to potentially decompress data served with
+ // Content-Encoding: gzip
+ rv = ConvertEncodedData(request, data, length);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // We cap the number of bytes to scan at MAX_BYTES to prevent picking up
+ // false positives by accidentally reading document content, e.g. a "how to
+ // make a feed" page.
+ const char* testData;
+ if (mDecodedData.IsEmpty()) {
+ testData = (const char*)data;
+ length = std::min<uint32_t>(length, MAX_BYTES);
+ } else {
+ testData = mDecodedData.get();
+ length = std::min<uint32_t>(mDecodedData.Length(), MAX_BYTES);
+ }
+
+ // The strategy here is based on that described in:
+ // http://blogs.msdn.com/rssteam/articles/PublishersGuide.aspx
+ // for interoperarbility purposes.
+
+ // Thus begins the actual sniffing.
+ nsDependentCSubstring dataString((const char*)testData, length);
+
+ bool isFeed = false;
+
+ // RSS 0.91/0.92/2.0
+ isFeed = ContainsTopLevelSubstring(dataString, "<rss");
+
+ // Atom 1.0
+ if (!isFeed)
+ isFeed = ContainsTopLevelSubstring(dataString, "<feed");
+
+ // RSS 1.0
+ if (!isFeed) {
+ bool foundNS_RDF = FindInReadable(nsLiteralCString(NS_RDF), dataString);
+ bool foundNS_RSS = FindInReadable(nsLiteralCString(NS_RSS), dataString);
+ isFeed = ContainsTopLevelSubstring(dataString, "<rdf:RDF") &&
+ foundNS_RDF && foundNS_RSS;
+ }
+
+ // If we sniffed a feed, coerce our internal type
+ if (isFeed && !HasAttachmentDisposition(channel))
+ sniffedType.AssignLiteral(TYPE_MAYBE_FEED);
+ else
+ sniffedType.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFeedSniffer::OnStartRequest(nsIRequest* request)
+{
+ return NS_OK;
+}
+
+nsresult
+nsFeedSniffer::AppendSegmentToString(nsIInputStream* inputStream,
+ void* closure,
+ const char* rawSegment,
+ uint32_t toOffset,
+ uint32_t count,
+ uint32_t* writeCount)
+{
+ nsCString* decodedData = static_cast<nsCString*>(closure);
+ decodedData->Append(rawSegment, count);
+ *writeCount = count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFeedSniffer::OnDataAvailable(nsIRequest* request, nsIInputStream* stream,
+ uint64_t offset, uint32_t count)
+{
+ uint32_t read;
+ return stream->ReadSegments(AppendSegmentToString, &mDecodedData, count,
+ &read);
+}
+
+NS_IMETHODIMP
+nsFeedSniffer::OnStopRequest(nsIRequest* request, nsresult status)
+{
+ return NS_OK;
+}
diff --git a/comm/suite/components/feeds/nsFeedSniffer.h b/comm/suite/components/feeds/nsFeedSniffer.h
new file mode 100644
index 0000000000..0128de2483
--- /dev/null
+++ b/comm/suite/components/feeds/nsFeedSniffer.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIContentSniffer.h"
+#include "nsIStreamListener.h"
+#include "nsString.h"
+#include "nsSuiteCID.h"
+#include "mozilla/Attributes.h"
+
+class nsFeedSniffer final : public nsIContentSniffer, nsIStreamListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTSNIFFER
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ static nsresult AppendSegmentToString(nsIInputStream* inputStream,
+ void* closure,
+ const char* rawSegment,
+ uint32_t toOffset,
+ uint32_t count,
+ uint32_t* writeCount);
+
+protected:
+ ~nsFeedSniffer() {}
+
+ nsresult ConvertEncodedData(nsIRequest* request, const uint8_t* data,
+ uint32_t length);
+
+private:
+ nsCString mDecodedData;
+};
diff --git a/comm/suite/components/feeds/nsIFeedResultService.idl b/comm/suite/components/feeds/nsIFeedResultService.idl
new file mode 100644
index 0000000000..a72292abd6
--- /dev/null
+++ b/comm/suite/components/feeds/nsIFeedResultService.idl
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+interface nsIURI;
+interface nsIRequest;
+interface nsIFeedResult;
+
+/**
+ * nsIFeedResultService provides a globally-accessible object for retrieving
+ * the results of feed processing.
+ */
+[scriptable, uuid(950a829e-c20e-4dc3-b447-f8b753ae54da)]
+interface nsIFeedResultService : nsISupports
+{
+ /**
+ * When set to true, forces the preview page to be displayed, regardless
+ * of the user's preferences.
+ */
+ attribute boolean forcePreviewPage;
+
+ /**
+ * Adds a URI to the user's specified external feed handler, or live
+ * bookmarks.
+ * @param uri
+ * The uri of the feed to add.
+ * @param title
+ * The title of the feed to add.
+ * @param subtitle
+ * The subtitle of the feed to add.
+ * @param feedType
+ * The nsIFeed type of the feed. See nsIFeed.idl
+ */
+ void addToClientReader(in AUTF8String uri,
+ in AString title,
+ in AString subtitle,
+ in unsigned long feedType);
+
+ /**
+ * Registers a Feed Result object with a globally accessible service
+ * so that it can be accessed by a singleton method outside the usual
+ * flow of control in document loading.
+ *
+ * @param feedResult
+ * An object implementing nsIFeedResult representing the feed.
+ */
+ void addFeedResult(in nsIFeedResult feedResult);
+
+ /**
+ * Gets a Feed Handler object registered using addFeedResult.
+ *
+ * @param uri
+ * The URI of the feed a handler is being requested for
+ */
+ nsIFeedResult getFeedResult(in nsIURI uri);
+
+ /**
+ * Unregisters a Feed Handler object registered using addFeedResult.
+ * @param uri
+ * The feed URI the handler was registered under. This must be
+ * the same *instance* the feed was registered under.
+ */
+ void removeFeedResult(in nsIURI uri);
+};
diff --git a/comm/suite/components/feeds/nsIWebContentConverterRegistrar.idl b/comm/suite/components/feeds/nsIWebContentConverterRegistrar.idl
new file mode 100644
index 0000000000..68ec48a936
--- /dev/null
+++ b/comm/suite/components/feeds/nsIWebContentConverterRegistrar.idl
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMIMEInfo.idl"
+#include "nsIWebContentHandlerRegistrar.idl"
+
+interface nsIRequest;
+
+[scriptable, uuid(eb361098-5158-4b21-8f98-50b445f1f0b2)]
+interface nsIWebContentHandlerInfo : nsIHandlerApp
+{
+ /**
+ * The content type handled by the handler
+ */
+ readonly attribute AString contentType;
+
+ /**
+ * The uri of the handler, with an embedded %s where the URI of the loaded
+ * document will be encoded.
+ */
+ readonly attribute AString uri;
+
+ /**
+ * Gets the service URL Spec, with the loading document URI encoded in it.
+ * @param uri
+ * The URI of the document being loaded
+ * @returns The URI of the service with the loading document URI encoded in
+ * it.
+ */
+ AString getHandlerURI(in AString uri);
+};
+
+[scriptable, uuid(de7cc06e-e778-45cb-b7db-7a114e1e75b1)]
+interface nsIWebContentConverterService : nsIWebContentHandlerRegistrar
+{
+ /**
+ * Specifies the handler to be used to automatically handle all links of a
+ * certain content type from now on.
+ * @param contentType
+ * The content type to automatically load with the specified handler
+ * @param handler
+ * A web service handler. If this is null, no automatic action is
+ * performed and the user must choose.
+ * @throws NS_ERROR_NOT_AVAILABLE if the service refered to by |handler| is
+ * not already registered.
+ */
+ void setAutoHandler(in AString contentType, in nsIWebContentHandlerInfo handler);
+
+ /**
+ * Gets the auto handler specified for a particular content type
+ * @param contentType
+ * The content type to look up an auto handler for.
+ * @returns The web service handler that will automatically handle all
+ * documents of the specified type. null if there is no automatic
+ * handler. (Handlers may be registered, just none of them specified
+ * as "automatic").
+ */
+ nsIWebContentHandlerInfo getAutoHandler(in AString contentType);
+
+ /**
+ * Gets a web handler for the specified service URI
+ * @param contentType
+ * The content type of the service being located
+ * @param uri
+ * The service URI of the handler to locate.
+ * @returns A web service handler that uses the specified uri.
+ */
+ nsIWebContentHandlerInfo getWebContentHandlerByURI(in AString contentType,
+ in AString uri);
+
+ /**
+ * Loads the preferred handler when content of a registered type is about
+ * to be loaded.
+ * @param request
+ * The nsIRequest for the load of the content
+ */
+ void loadPreferredHandler(in nsIRequest request);
+
+ /**
+ * Removes a registered protocol handler
+ * @param protocol
+ * The protocol scheme to remove a service handler for
+ * @param uri
+ * The uri of the service handler to remove
+ */
+ void removeProtocolHandler(in AString protocol, in AString uri);
+
+ /**
+ * Removes a registered content handler
+ * @param contentType
+ * The content type to remove a service handler for
+ * @param uri
+ * The uri of the service handler to remove
+ */
+ void removeContentHandler(in AString contentType, in AString uri);
+
+ /**
+ * Gets the list of content handlers for a particular type.
+ * @param contentType
+ * The content type to get handlers for
+ * @returns An array of nsIWebContentHandlerInfo objects
+ */
+ void getContentHandlers(in AString contentType,
+ [optional] out unsigned long count,
+ [retval,array,size_is(count)] out nsIWebContentHandlerInfo handlers);
+
+ /**
+ * Resets the list of available content handlers to the default set from
+ * the distribution.
+ * @param contentType
+ * The content type to reset handlers for
+ */
+ void resetHandlersForType(in AString contentType);
+};
diff --git a/comm/suite/components/helpviewer/content/contextHelp.js b/comm/suite/components/helpviewer/content/contextHelp.js
new file mode 100644
index 0000000000..151cd777a9
--- /dev/null
+++ b/comm/suite/components/helpviewer/content/contextHelp.js
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+// Set the default content pack to the Mozilla content pack. Use the
+// setHelpFileURI function to set this value.
+var helpFileURI;
+
+// openHelp - Opens up the Mozilla Help Viewer with the specified
+// topic and content pack.
+// see http://www.mozilla.org/projects/help-viewer/content_packs.html
+function openHelp(topic, contentPack)
+{
+ // helpFileURI is the content pack to use in this function. If contentPack is defined,
+ // use that and set the helpFileURI to that value so that it will be the default.
+ helpFileURI = contentPack || helpFileURI;
+
+ // Try to find previously opened help.
+ var topWindow = locateHelpWindow(helpFileURI);
+
+ if ( topWindow ) {
+ // Open topic in existing window.
+ topWindow.focus();
+ topWindow.displayTopic(topic);
+ } else {
+ // Open topic in new window.
+ const params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+ params.SetNumberStrings(2);
+ params.SetString(0, helpFileURI);
+ params.SetString(1, topic);
+
+ let openFeatures = "chrome,all,dialog=no";
+
+ if (AppConstants.platform == "win") {
+ openFeatures += ",alwaysRaised";
+ }
+ Services.ww.openWindow(null, "chrome://help/content/help.xul", "_blank",
+ openFeatures, params);
+ }
+}
+
+// setHelpFileURI - Sets the default content pack to use in the Help Viewer
+function setHelpFileURI(rdfURI)
+{
+ helpFileURI = rdfURI;
+}
+
+// Locate existing help window for this content pack.
+function locateHelpWindow(contentPack) {
+ const iterator = Services.wm.getEnumerator("suite:help");
+ var topWindow = null;
+ var aWindow;
+
+ // Loop through help windows looking for one with selected content
+ // pack.
+ while (iterator.hasMoreElements()) {
+ aWindow = iterator.getNext();
+ if (!aWindow.closed && aWindow.getHelpFileURI() == contentPack) {
+ topWindow = aWindow;
+ }
+ }
+ return topWindow;
+}
diff --git a/comm/suite/components/helpviewer/content/help.js b/comm/suite/components/helpviewer/content/help.js
new file mode 100644
index 0000000000..4a7d0b1cbb
--- /dev/null
+++ b/comm/suite/components/helpviewer/content/help.js
@@ -0,0 +1,856 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+// Global Variables
+var helpExternal;
+var helpBrowser;
+var helpSearchPanel;
+var emptySearch;
+var emptySearchText;
+var emptySearchLink = "about:blank";
+var helpTocPanel;
+var helpIndexPanel;
+var helpGlossaryPanel;
+var strBundle;
+var gTocDSList = "";
+
+// Namespaces
+const NC = "http://home.netscape.com/NC-rdf#";
+const MAX_LEVEL = 40; // maximum depth of recursion in search datasources.
+const MAX_HISTORY_MENU_ITEMS = 6;
+
+const platform = getCurrentPlatform();
+
+// Resources
+const RDF = Cc["@mozilla.org/rdf/rdf-service;1"]
+ .getService(Ci.nsIRDFService);
+const RDF_ROOT = RDF.GetResource("urn:root");
+const NC_PANELLIST = RDF.GetResource(NC + "panellist");
+const NC_PANELID = RDF.GetResource(NC + "panelid");
+const NC_EMPTY_SEARCH_TEXT = RDF.GetResource(NC + "emptysearchtext");
+const NC_EMPTY_SEARCH_LINK = RDF.GetResource(NC + "emptysearchlink");
+const NC_DATASOURCES = RDF.GetResource(NC + "datasources");
+const NC_PLATFORM = RDF.GetResource(NC + "platform");
+const NC_SUBHEADINGS = RDF.GetResource(NC + "subheadings");
+const NC_NAME = RDF.GetResource(NC + "name");
+const NC_CHILD = RDF.GetResource(NC + "child");
+const NC_LINK = RDF.GetResource(NC + "link");
+const NC_TITLE = RDF.GetResource(NC + "title");
+const NC_BASE = RDF.GetResource(NC + "base");
+const NC_DEFAULTTOPIC = RDF.GetResource(NC + "defaulttopic");
+
+var RDFContainer = Cc["@mozilla.org/rdf/container;1"]
+ .createInstance(Ci.nsIRDFContainer);
+
+var RE;
+
+var helpFileURI;
+var helpFileDS;
+// Set from nc:base attribute on help rdf file. It may be used for prefix
+// reduction on all links within the current help set.
+var helpBaseURI;
+
+/* defaultTopic is either set
+ 1. in the openHelp() call, passed as an argument to the Help window and
+ evaluated in init(), or
+ 2. in nc:defaulttopic in the content pack (e.g. firebirdhelp.rdf),
+ evaluated in loadHelpRDF(), or
+ 3. "welcome" as a fallback, specified in loadHelpRDF() as well;
+ displayTopic() then uses defaultTopic because topic is null. */
+var defaultTopic;
+
+const NSRESULT_RDF_SYNTAX_ERROR = 0x804e03f7;
+
+// Translate the current application platform to one the
+// help viewer understands.
+function getCurrentPlatform() {
+
+ // The supported platforms are defined in
+ // suite/locales/en-US/chrome/common/help/suitehelp.rdf.
+ // We can't just return the current platform 1:1 because
+ // this would need l10n changes for all languages.
+ if (AppConstants.platform == "win") {
+ return "win";
+ }
+
+ if (AppConstants.platform == "macosx") {
+ return "mac";
+ }
+
+ if (AppConstants.platform == "linux") {
+ return "unix";
+ }
+
+ // We never end up here in official builds.
+ return "---";
+}
+
+// This function is called by dialogs/windows that want to display
+// context-sensitive help
+// These dialogs/windows should include the script
+// chrome://help/content/contextHelp.js
+function displayTopic(topic) {
+ // Get the page to open.
+ var uri = getLink(topic);
+ // Use default topic if specified topic is not found.
+ if (!uri) {
+ uri = getLink(defaultTopic);
+ }
+ // Load the page.
+ if (uri)
+ loadURI(uri);
+}
+
+// Initialize the Help window
+function init() {
+ // Cache panel references.
+ helpSearchPanel = document.getElementById("help-search-panel");
+ helpTocPanel = document.getElementById("help-toc-panel");
+ helpIndexPanel = document.getElementById("help-index-panel");
+ helpGlossaryPanel = document.getElementById("help-glossary-panel");
+ helpBrowser = document.getElementById("help-content");
+
+ // Turn off unnecessary features for security
+ helpBrowser.docShell.allowJavascript = false;
+ helpBrowser.docShell.allowPlugins = false;
+ helpBrowser.docShell.allowSubframes = false;
+ helpBrowser.docShell.allowMetaRedirects = false;
+
+ strBundle = document.getElementById("bundle_help");
+ emptySearchText = strBundle.getString("emptySearchText");
+
+ // Get the content pack, base URL, and help topic
+ var helpTopic = defaultTopic;
+ if ("arguments" in window &&
+ window.arguments[0] instanceof Ci.nsIDialogParamBlock) {
+ helpFileURI = window.arguments[0].GetString(0);
+ // trailing "/" included.
+ helpBaseURI = helpFileURI.substring(0, helpFileURI.lastIndexOf("/")+1);
+ helpTopic = window.arguments[0].GetString(1);
+ }
+
+ loadHelpRDF();
+ displayTopic(helpTopic);
+
+ // Move to Center of Screen
+ const width = document.documentElement.getAttribute("width");
+ const height = document.documentElement.getAttribute("height");
+ window.moveTo((screen.availWidth - width) / 2, (screen.availHeight - height) / 2);
+
+ // Initialize history.
+ getWebNavigation().sessionHistory =
+ Cc["@mozilla.org/browser/shistory;1"].createInstance(Ci.nsISHistory);
+ window.XULBrowserWindow = new nsHelpStatusHandler();
+
+ //Start the status handler.
+ window.XULBrowserWindow.init();
+
+ // Hook up UI through Progress Listener
+ const interfaceRequestor = helpBrowser.docShell.QueryInterface(Ci.nsIInterfaceRequestor);
+ const webProgress = interfaceRequestor.getInterface(Ci.nsIWebProgress);
+
+ webProgress.addProgressListener(window.XULBrowserWindow, Ci.nsIWebProgress.NOTIFY_ALL);
+
+ var searchBox = document.getElementById("findText");
+ searchBox.clickSelectsAll = Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll", true);
+
+ setTimeout(focusSearch, 0);
+
+ helpExternal = document.getElementById("help-external");
+ helpExternal.docShell.useErrorPages = false;
+ helpExternal
+ .docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIURIContentListener)
+ .parentContentListener = helpContentListener;
+ helpExternal.addProgressListener(window.XULBrowserWindow, Ci.nsIWebProgress.NOTIFY_ALL);
+
+}
+function contentClick(event) {
+ // is this a left click on a link?
+ if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey || event.button != 0)
+ return true;
+
+ // is this a link?
+ var target = event.target;
+ while (!(target instanceof HTMLAnchorElement))
+ if (!(target = target.parentNode))
+ return true;
+
+ // is this an internal link?
+ if (target.href.lastIndexOf("chrome:", 0) == 0)
+ return true;
+
+ var uri = target.href;
+ if (/^x-moz-url-link:/.test(uri))
+ uri = Services.urlFormatter.formatURLPref(RegExp.rightContext);
+
+ try {
+ helpExternal.webNavigation
+ .loadURI(uri,
+ Ci.nsIWebNavigation.LOAD_FLAGS_IS_LINK,
+ null, null, null,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ } catch (e) {}
+ return false;
+}
+
+function showSidebar() {
+ document.getElementById("help-sidebar-splitter").setAttribute("state", "open");
+}
+
+// needed by findUtils.js
+var gFindInstData;
+function getFindInstData()
+{
+ if (!gFindInstData) {
+ gFindInstData = new nsFindInstData();
+ gFindInstData.browser = getBrowser();
+ // defaults for rootSearchWindow and currentSearchWindow are fine here
+ }
+ return gFindInstData;
+}
+
+function showSearchSidebar() {
+ // if you tab too quickly, you end up with stuck focus, revert focus to the searchbar
+ var searchTree = document.getElementById("help-toc-panel");
+ if (searchTree.treeBoxObject.focused) {
+ focusSearch();
+ }
+
+ var tableOfContents = document.getElementById("help-toc-sidebar");
+ tableOfContents.setAttribute("hidden", "true");
+
+ var sidebar = document.getElementById("help-search-sidebar");
+ sidebar.removeAttribute("hidden");
+}
+
+function hideSearchSidebar(aEvent) {
+ // if we're focused in the search results, focus content
+ var searchTree = document.getElementById("help-search-tree");
+ if (searchTree.treeBoxObject.focused) {
+ content.focus();
+ }
+
+ var sidebar = document.getElementById("help-search-sidebar");
+ sidebar.setAttribute("hidden", "true");
+
+ var tableOfContents = document.getElementById("help-toc-sidebar");
+ tableOfContents.removeAttribute("hidden");
+}
+
+// loadHelpRDF
+// Parse the provided help content pack RDF file, and use it to
+// populate the datasources attached to the trees in the viewer.
+// Filter out any information not applicable to the user's platform.
+function loadHelpRDF() {
+ if (!helpFileDS) {
+ try {
+ helpFileDS = RDF.GetDataSourceBlocking(helpFileURI);
+ } catch (e) {
+ if (e.result == NSRESULT_RDF_SYNTAX_ERROR) {
+ log("Help file: " + helpFileURI + " contains a syntax error.");
+ } else {
+ log("Help file: " + helpFileURI + " was not found.");
+ }
+ }
+
+ try {
+ document.title = getAttribute(helpFileDS, RDF_ROOT, NC_TITLE, "");
+ helpBaseURI = getAttribute(helpFileDS, RDF_ROOT, NC_BASE, helpBaseURI);
+ // if there's no nc:defaulttopic in the content pack, set "welcome"
+ // as the default topic
+ defaultTopic = getAttribute(helpFileDS,
+ RDF_ROOT, NC_DEFAULTTOPIC, "welcome");
+
+ var panelDefs = helpFileDS.GetTarget(RDF_ROOT, NC_PANELLIST, true);
+ RDFContainer.Init(helpFileDS, panelDefs);
+ var iterator = RDFContainer.GetElements();
+ while (iterator.hasMoreElements()) {
+ var panelDef = iterator.getNext();
+
+ var panelID = getAttribute(helpFileDS, panelDef, NC_PANELID, null);
+ var datasources = getAttribute(helpFileDS, panelDef, NC_DATASOURCES, "");
+ var panelPlatforms = getAttribute(helpFileDS, panelDef, NC_PLATFORM, null);
+
+ if (panelPlatforms && !panelPlatforms.split(/\s+/).includes(platform))
+ continue; // ignore datasources for other platforms
+
+ // empty datasources are valid on search panel definitions
+ // convert them to "rdf:null" which can be filtered and ignored
+ if (!datasources)
+ datasources = "rdf:null";
+
+ datasources = normalizeLinks(helpBaseURI, datasources);
+
+ var datasourceArray = datasources.split(/\s+/)
+ .filter(function(x) { return x != "rdf:null"; })
+ .map(RDF.GetDataSourceBlocking);
+
+ // Cache Additional Datasources to Augment Search Datasources.
+ if (panelID == "search") {
+ emptySearchText = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_TEXT, emptySearchText);
+ emptySearchLink = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_LINK, emptySearchLink);
+
+ datasourceArray.forEach(helpSearchPanel.database.AddDataSource,
+ helpSearchPanel.database);
+ if (!panelPlatforms)
+ filterDatasourceByPlatform(helpSearchPanel.database);
+
+ continue; // to next panel definition
+ }
+
+ // cache toc datasources list for use in getLink()
+ if (panelID == "toc")
+ gTocDSList += " " + datasources;
+
+ var tree = document.getElementById("help-" + panelID + "-panel");
+
+ // add each datasource to the current tree
+ datasourceArray.forEach(tree.database.AddDataSource,
+ tree.database);
+
+ // filter and display the current tree
+ if (!panelPlatforms)
+ filterDatasourceByPlatform(tree.database);
+ tree.builder.rebuild();
+ }
+ } catch (e) {
+ log(e + "");
+ }
+ }
+}
+
+// filterDatasourceByPlatform
+// Remove statements for other platforms from a datasource.
+function filterDatasourceByPlatform(aDatasource) {
+ filterNodeByPlatform(aDatasource, RDF_ROOT, 0);
+}
+
+// filterNodeByPlatform
+// Remove statements for other platforms from the provided datasource.
+function filterNodeByPlatform(aDatasource, aCurrentResource, aCurrentLevel) {
+ if (aCurrentLevel > MAX_LEVEL) {
+ log("Datasources over " + MAX_LEVEL + " levels deep are unsupported.");
+ return;
+ }
+
+ // get the subheadings under aCurrentResource and filter them
+ var nodes = aDatasource.GetTargets(aCurrentResource, NC_SUBHEADINGS, true);
+ while (nodes.hasMoreElements()) {
+ var node = nodes.getNext();
+ node = node.QueryInterface(Ci.nsIRDFResource);
+ // should we test for rdf:Seq here? see also doFindOnDatasource
+ filterSeqByPlatform(aDatasource, node, aCurrentLevel+1);
+ }
+}
+
+// filterSeqByPlatform
+// Go through the children of aNode, if any, removing statements applicable
+// only on other platforms.
+function filterSeqByPlatform(aDatasource, aNode, aCurrentLevel) {
+ // get nc:subheading children into an enumerator
+ var RDFC = Cc["@mozilla.org/rdf/container;1"]
+ .createInstance(Ci.nsIRDFContainer);
+ RDFC.Init(aDatasource, aNode);
+ var targets = RDFC.GetElements();
+
+ // process items in the rdf:Seq
+ while (targets.hasMoreElements()) {
+ var currentTarget = targets.getNext();
+
+ // find out on which platforms this node is meaningful
+ var nodePlatforms = getAttribute(aDatasource,
+ currentTarget.QueryInterface(Ci.nsIRDFResource),
+ NC_PLATFORM,
+ platform);
+
+ if (!nodePlatforms.split(/\s+/).includes(platform)) { // node is for another platform
+ var currentNode = currentTarget.QueryInterface(Ci.nsIRDFNode);
+ // "false" because we don't want to renumber elements in the container
+ RDFC.RemoveElement(currentNode, false);
+
+ // move to next node - ignore the children, because 1) they might be
+ // needed elsewhere and 2) nodes not connected to RDF_ROOT are ignored
+ continue;
+ }
+
+ // filter any children
+ filterNodeByPlatform(aDatasource, currentTarget, aCurrentLevel+1);
+ }
+}
+
+// Prepend helpBaseURI to list of space separated links if they don't start with
+// "chrome:"
+function normalizeLinks(helpBaseURI, links) {
+ if (!helpBaseURI) {
+ return links;
+ }
+ var ls = links.split(/\s+/);
+ if (ls.length == 0) {
+ return links;
+ }
+ for (var i=0; i < ls.length; ++i) {
+ if (ls[i] == "")
+ continue;
+
+ if (ls[i].substr(0,7) != "chrome:" && ls[i].substr(0,4) != "rdf:")
+ ls[i] = helpBaseURI + ls[i];
+ }
+ return ls.join(" ");
+}
+
+function getLink(ID) {
+ if (!ID)
+ return null;
+
+ var tocDS = document.getElementById("help-toc-panel").database;
+ if (!tocDS)
+ return null;
+
+ // URIs include both the ID part and the base file name,
+ // so we need to check for a matching ID in each datasource
+ var tocDSArray = gTocDSList.split(/\s+/)
+ .filter(function(x) { return x != "rdf:null"; });
+
+ for (var i = 0; i < tocDSArray.length; i++) {
+ var resource = RDF.GetResource(tocDSArray[i] + "#" + ID);
+ var link = tocDS.GetTarget(resource, NC_LINK, true);
+ if (!link) // no such rdf:ID found
+ continue;
+ return link.QueryInterface(Ci.nsIRDFLiteral).Value;
+ }
+ return null;
+}
+
+// Called by contextHelp.js to determine if this window is displaying the
+// requested help file.
+function getHelpFileURI() {
+ return helpFileURI;
+}
+
+function getBrowser() {
+ return helpBrowser;
+}
+
+function getWebNavigation() {
+ try {
+ return helpBrowser.webNavigation;
+ } catch (e)
+ {
+ return null;
+ }
+}
+
+function loadURI(uri) {
+ if (uri.substr(0,7) != "chrome:") {
+ uri = helpBaseURI + uri;
+ }
+ getWebNavigation().loadURI(uri,
+ Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
+ null, null, null,
+ Services.scriptSecurityManager.getSystemPrincipal());
+}
+
+function goBack() {
+ try
+ {
+ getWebNavigation().goBack();
+ } catch (e)
+ {
+ }
+}
+
+function goForward() {
+ try
+ {
+ getWebNavigation().goForward();
+ } catch(e)
+ {
+ }
+}
+
+function goHome() {
+ // Load "Welcome" page
+ displayTopic(defaultTopic);
+}
+
+function print() {
+ try {
+ _content.print();
+ } catch (e) {
+ }
+}
+
+function FillHistoryMenu(aParent, aMenu)
+ {
+ // Remove old entries if any
+ deleteHistoryItems(aParent);
+
+ var sessionHistory = getWebNavigation().sessionHistory;
+
+ var count = sessionHistory.count;
+ var index = sessionHistory.index;
+ var end;
+ var j;
+ var entry;
+
+ switch (aMenu)
+ {
+ case "back":
+ end = (index > MAX_HISTORY_MENU_ITEMS) ? index - MAX_HISTORY_MENU_ITEMS : 0;
+ if ((index - 1) < end) return false;
+ for (j = index - 1; j >= end; j--)
+ {
+ entry = sessionHistory.getEntryAtIndex(j);
+ if (entry)
+ createMenuItem(aParent, j, entry.title);
+ }
+ break;
+ case "forward":
+ end = ((count-index) > MAX_HISTORY_MENU_ITEMS) ? index + MAX_HISTORY_MENU_ITEMS : count - 1;
+ if ((index + 1) > end) return false;
+ for (j = index + 1; j <= end; j++)
+ {
+ entry = sessionHistory.getEntryAtIndex(j);
+ if (entry)
+ createMenuItem(aParent, j, entry.title);
+ }
+ break;
+ }
+ return true;
+ }
+
+function createMenuItem( aParent, aIndex, aLabel)
+ {
+ var menuitem = document.createElement( "menuitem" );
+ menuitem.setAttribute( "label", aLabel );
+ menuitem.setAttribute( "index", aIndex );
+ aParent.appendChild( menuitem );
+ }
+
+function deleteHistoryItems(aParent)
+{
+ var children = aParent.childNodes;
+ for (var i = children.length - 1; i >= 0; --i)
+ {
+ var index = children[i].getAttribute("index");
+ if (index)
+ aParent.removeChild(children[i]);
+ }
+}
+
+function createBackMenu(event) {
+ return FillHistoryMenu(event.target, "back");
+}
+
+function createForwardMenu(event) {
+ return FillHistoryMenu(event.target, "forward");
+}
+
+function gotoHistoryIndex(aEvent) {
+ var index = aEvent.target.getAttribute("index");
+ if (!index) {
+ return false;
+ }
+ try {
+ getWebNavigation().gotoIndex(index);
+ } catch(ex) {
+ return false;
+ }
+ return true;
+}
+
+function nsHelpStatusHandler() {
+ this.init();
+}
+
+nsHelpStatusHandler.prototype = {
+
+ onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {},
+ onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress,
+ aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {},
+ onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {},
+ onSecurityChange : function(aWebProgress, aRequest, state) {},
+ onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags) {
+ UpdateBackForwardButtons();
+ },
+ QueryInterface : function(aIID) {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsIXULBrowserWindow) ||
+ aIID.equals(Ci.nsISupports)) {
+ return this;
+ }
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ init : function() {},
+
+ destroy : function() {},
+
+ setJSStatus : function(status) {},
+ setOverLink : function(link, context) {},
+ onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {}
+}
+
+function UpdateBackForwardButtons() {
+ var backBroadcaster = document.getElementById("canGoBack");
+ var forwardBroadcaster = document.getElementById("canGoForward");
+ var webNavigation = getWebNavigation();
+
+ // Avoid setting attributes on broadcasters if the value hasn't changed!
+ // Remember, guys, setting attributes on elements is expensive! They
+ // get inherited into anonymous content, broadcast to other widgets, etc.!
+ // Don't do it if the value hasn't changed! - dwh
+
+ var backDisabled = (backBroadcaster.getAttribute("disabled") == "true");
+ var forwardDisabled = (forwardBroadcaster.getAttribute("disabled") == "true");
+
+ if (backDisabled == webNavigation.canGoBack) {
+ if (backDisabled)
+ backBroadcaster.removeAttribute("disabled");
+ else
+ backBroadcaster.setAttribute("disabled", true);
+ }
+
+ if (forwardDisabled == webNavigation.canGoForward) {
+ if (forwardDisabled)
+ forwardBroadcaster.removeAttribute("disabled");
+ else
+ forwardBroadcaster.setAttribute("disabled", true);
+ }
+}
+
+function onselect_loadURI(tree) {
+ try {
+ var resource = tree.view.getResourceAtIndex(tree.currentIndex);
+ var link = tree.database.GetTarget(resource, NC_LINK, true);
+ if (link) {
+ link = link.QueryInterface(Ci.nsIRDFLiteral);
+ loadURI(link.Value);
+ }
+ } catch (e) {
+ }// when switching between tabs a spurious row number is returned.
+}
+
+function focusSearch() {
+ var searchBox = document.getElementById("findText");
+ searchBox.focus();
+}
+
+// doFind - Searches the help files for what is located in findText and outputs into
+// the find search tree.
+function doFind() {
+ if (document.getElementById("help-search-sidebar").hidden)
+ showSearchSidebar();
+
+ var searchTree = document.getElementById("help-search-tree");
+ var findText = document.getElementById("findText");
+
+ // clear any previous results.
+ clearDatabases(searchTree.database);
+
+ // if the search string is empty or contains only whitespace, purge the results tree and return
+ RE = findText.value.match(/\S+/g);
+ if (!RE) {
+ searchTree.builder.rebuild();
+ hideSearchSidebar();
+ return;
+ }
+
+ // compile the search string, which has already been split up above, into regexps
+ for (var i=0; i < RE.length; ++i) {
+ RE[i] = new RegExp(RE[i], "i");
+ }
+ emptySearch = true;
+
+ // search TOC
+ var resultsDS = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
+ .createInstance(Ci.nsIRDFDataSource);
+ var sourceDS = helpTocPanel.database;
+ doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
+
+ // search glossary.
+ sourceDS = helpGlossaryPanel.database;
+ doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
+
+ // search index
+ sourceDS = helpIndexPanel.database;
+ doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
+
+ // search additional search datasources
+ sourceDS = helpSearchPanel.database;
+ doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
+
+ if (emptySearch)
+ assertSearchEmpty(resultsDS);
+ // Add the datasource to the search tree
+ searchTree.database.AddDataSource(resultsDS);
+ searchTree.builder.rebuild();
+}
+
+function clearDatabases(compositeDataSource) {
+ var enumDS = compositeDataSource.GetDataSources()
+ while (enumDS.hasMoreElements()) {
+ var ds = enumDS.getNext();
+ compositeDataSource.RemoveDataSource(ds);
+ }
+}
+
+function doFindOnDatasource(resultsDS, sourceDS, resource, level) {
+ if (level > MAX_LEVEL) {
+ try {
+ log("Recursive reference to resource: " + resource.Value + ".");
+ } catch (e) {
+ log("Recursive reference to unknown resource.");
+ }
+ return;
+ }
+ // find all SUBHEADING children of current resource.
+ var targets = sourceDS.GetTargets(resource, NC_SUBHEADINGS, true);
+ while (targets.hasMoreElements()) {
+ var target = targets.getNext().QueryInterface(Ci.nsIRDFResource);
+ // The first child of a rdf:subheading should (must) be a rdf:seq.
+ // Should we test for a SEQ here?
+ doFindOnSeq(resultsDS, sourceDS, target, level+1);
+ }
+}
+
+function doFindOnSeq(resultsDS, sourceDS, resource, level) {
+ // load up an RDFContainer so we can access the contents of the current
+ // rdf:seq.
+ RDFContainer.Init(sourceDS, resource);
+ var targets = RDFContainer.GetElements();
+ while (targets.hasMoreElements()) {
+ var target = targets.getNext();
+ var link = sourceDS.GetTarget(target, NC_LINK, true);
+ var name = sourceDS.GetTarget(target, NC_NAME, true);
+
+ if (link && name instanceof Ci.nsIRDFLiteral && isMatch(name.Value)) {
+ // we have found a search entry - add it to the results datasource.
+ var urn = RDF.GetAnonymousResource();
+ resultsDS.Assert(urn, NC_NAME, name, true);
+ resultsDS.Assert(urn, NC_LINK, link, true);
+ resultsDS.Assert(RDF_ROOT, NC_CHILD, urn, true);
+
+ emptySearch = false;
+ }
+ // process any nested rdf:seq elements.
+ doFindOnDatasource(resultsDS, sourceDS, target, level+1);
+ }
+}
+
+function assertSearchEmpty(resultsDS) {
+ var resSearchEmpty = RDF.GetResource("urn:emptySearch");
+ resultsDS.Assert(RDF_ROOT,
+ NC_CHILD,
+ resSearchEmpty,
+ true);
+ resultsDS.Assert(resSearchEmpty,
+ NC_NAME,
+ RDF.GetLiteral(emptySearchText),
+ true);
+ resultsDS.Assert(resSearchEmpty,
+ NC_LINK,
+ RDF.GetLiteral(emptySearchLink),
+ true);
+}
+
+function isMatch(text) {
+ for (var i=0; i < RE.length; ++i ) {
+ if (!RE[i].test(text)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function getAttribute(datasource, resource, attributeResourceName,
+ defaultValue) {
+ var literal = datasource.GetTarget(resource, attributeResourceName, true);
+ if (!literal) {
+ return defaultValue;
+ }
+ return getLiteralValue(literal, defaultValue);
+}
+
+function getLiteralValue(literal, defaultValue) {
+ if (literal) {
+ literal = literal.QueryInterface(Ci.nsIRDFLiteral);
+ if (literal) {
+ return literal.Value;
+ }
+ }
+ if (defaultValue) {
+ return defaultValue;
+ }
+ return null;
+}
+
+// Write debug string to error console.
+function log(aText) {
+ Services.console.logStringMessage(aText);
+}
+
+// getXulWin - Returns the current Help window as a nsIXULWindow.
+function getXulWin()
+{
+ window.QueryInterface(Ci.nsIInterfaceRequestor);
+ var webnav = window.getInterface(Ci.nsIWebNavigation);
+ var dsti = webnav.QueryInterface(Ci.nsIDocShellTreeItem);
+ var treeowner = dsti.treeOwner;
+ var ifreq = treeowner.QueryInterface(Ci.nsIInterfaceRequestor);
+
+ return ifreq.getInterface(Ci.nsIXULWindow);
+}
+
+// toggleZLevel - Toggles whether or not the window will always appear on top. Because
+// alwaysRaised is not supported on an OS other than Windows, this code will not
+// be used in those builds.
+//
+// element - The DOM node that persists the checked state.
+function toggleZLevel(element)
+{
+ if (AppConstants.platform != "win") {
+ return;
+ }
+
+ var xulwin = getXulWin();
+
+ // Now we can flip the zLevel, and set the attribute so that it persists correctly
+ if (xulwin.zLevel > xulwin.normalZ) {
+ xulwin.zLevel = xulwin.normalZ;
+ element.setAttribute("checked", "false");
+ } else {
+ xulwin.zLevel = xulwin.raisedZ;
+ element.setAttribute("checked", "true");
+ }
+}
+
+var helpContentListener = {
+ doContent: function(aContentType, aIsContentPreferred, aRequest, aContentHandler) {
+ throw Cr.NS_ERROR_UNEXPECTED;
+ },
+ isPreferred: function(aContentType, aDesiredContentType) {
+ return false;
+ },
+ canHandleContent: function(aContentType, aIsContentPreferred, aDesiredContentType) {
+ return false;
+ },
+ loadCookie: null,
+ parentContentListener: null,
+ QueryInterface: function (aIID) {
+ if (aIID.equals(Ci.nsIURIContentListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
diff --git a/comm/suite/components/helpviewer/content/help.xul b/comm/suite/components/helpviewer/content/help.xul
new file mode 100644
index 0000000000..514d0ebb1b
--- /dev/null
+++ b/comm/suite/components/helpviewer/content/help.xul
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8"?>
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<?xml-stylesheet href="chrome://communicator/skin/helpviewer/help.css" type="text/css"?>
+
+<?xul-overlay href="chrome://help/content/helpContextOverlay.xul"?>
+<!DOCTYPE window [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % helpDTD SYSTEM "chrome://help/locale/help.dtd">
+ %helpDTD;
+]>
+
+<window id="help"
+ windowtype="suite:help"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="700"
+ height="550"
+#ifdef XP_WIN
+ persist="width height screenX screenY zlevel"
+#else
+ persist="width height screenX screenY"
+#endif
+ onload="init();"
+ onunload="window.XULBrowserWindow.destroy();">
+
+ <script src="chrome://help/content/help.js"/>
+ <script src="chrome://global/content/viewZoomOverlay.js"/>
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/findUtils.js"/>
+
+ <menupopup id="backMenu" position="after_start"
+ onpopupshowing="return createBackMenu(event);"
+ oncommand="gotoHistoryIndex(event);"/>
+ <menupopup id="forwardMenu" position="after_start"
+ onpopupshowing="return createForwardMenu(event);"
+ oncommand="gotoHistoryIndex(event);"/>
+ <popupset id="contentAreaContextSet"/>
+
+ <broadcasterset id="helpBroadcasters">
+ <broadcaster id="canGoBack" disabled="true"/>
+ <broadcaster id="canGoForward" disabled="true"/>
+ </broadcasterset>
+ <commandset id="globalEditMenuItems"/>
+ <commandset id="selectEditMenuItems">
+ <command id="cmd_close" oncommand="close();"/>
+ <command id="Help:Home" oncommand="goHome();"/>
+ <command id="Help:Back" oncommand="goBack();" observes="canGoBack"/>
+ <command id="Help:Forward" oncommand="goForward();" observes="canGoForward"/>
+ <command id="Help:ToggleSidebar" oncommand="toggleSidebar();"/>
+ <command id="cmd_closeWindow" oncommand="close();"/>
+ <command id="cmd_fullZoomReduce" oncommand="ZoomManager.reduce();"/>
+ <command id="cmd_fullZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
+ <command id="cmd_fullZoomReset" oncommand="ZoomManager.reset();"/>
+ <command id="cmd_find"
+ oncommand="findInPage(getFindInstData());"/>
+ <command id="cmd_findAgain"
+ oncommand="findAgainInPage(getFindInstData(), false);"/>
+ <command id="cmd_findPrevious"
+ oncommand="findAgainInPage(getFindInstData(), true);"/>
+ <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')" disabled="true"/>
+ <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
+ </commandset>
+ <keyset id="keys">
+ <key id="goHome" keycode="VK_HOME" command="Help:Home" modifiers="alt"/>
+#ifdef XP_UNIX
+ <key key="&goBackCmd.commandkey;" command="Help:Back" modifiers="accel"/>
+ <key key="&goForwardCmd.commandkey;" command="Help:Forward" modifiers="accel"/>
+#endif
+#ifdef XP_MACOSX
+ <key id="goBackKb" keycode="VK_LEFT" command="Help:Back" modifiers="accel"/>
+ <key id="goForwardKb" keycode="VK_RIGHT" command="Help:Forward" modifiers="accel"/>
+#else
+ <key id="goBackKb" keycode="VK_LEFT" command="Help:Back" modifiers="alt"/>
+ <key id="goForwardKb" keycode="VK_RIGHT" command="Help:Forward" modifiers="alt"/>
+ <key keycode="VK_BACK" command="Help:Back"/>
+ <key keycode="VK_BACK" command="Help:Forward" modifiers="shift"/>
+#endif
+ <key id="printKb" key="&printCmd.commandkey;" oncommand="print();"
+ modifiers="accel"/>
+ <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
+ <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
+ <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
+ <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
+ <key keycode="&findAgainCmd.commandkey2;" command="cmd_findPrevious" modifiers="shift"/>
+ <key id="key_closeWindow" key="&closeWindow.commandkey;"
+ command="cmd_closeWindow" modifiers="accel"/>
+ <key id="key_closeSearchSidebar" keycode="VK_ESCAPE"
+ oncommand="hideSearchSidebar(event)"/>
+ <key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;"
+ command="cmd_fullZoomEnlarge" modifiers="accel"/>
+ <key id="key_fullZoomEnlarge2" key="&fullZoomEnlargeCmd.commandkey2;"
+ command="cmd_fullZoomEnlarge" modifiers="accel"/>
+ <key id="key_fullZoomEnlarge3" key="&fullZoomEnlargeCmd.commandkey3;"
+ command="cmd_fullZoomEnlarge" modifiers="accel"/>
+ <key id="key_fullZoomReduce" key="&fullZoomReduceCmd.commandkey;"
+ command="cmd_fullZoomReduce" modifiers="accel"/>
+ <key id="key_fullZoomReduce2" key="&fullZoomReduceCmd.commandkey2;"
+ command="cmd_fullZoomReduce" modifiers="accel"/>
+ <key id="key_fullZoomReset" key="&fullZoomResetCmd.commandkey;"
+ command="cmd_fullZoomReset" modifiers="accel"/>
+ <key id="key_fullZoomReset2" key="&fullZoomResetCmd.commandkey2;"
+ command="cmd_fullZoomReset" modifiers="accel"/>
+ <key id="key_focusSearch" key="&helpSearch.commandkey;"
+ oncommand="focusSearch()" modifiers="accel"/>
+
+ </keyset>
+ <stringbundle id="bundle_viewZoom"/>
+ <stringbundle id="findBundle"
+ src="chrome://global/locale/finddialog.properties"/>
+ <stringbundle id="bundle_help"
+ src="chrome://help/locale/help.properties"/>
+
+ <toolbox id="help-toolbox">
+ <toolbar id="HelpToolbar" class="chromeclass-toolbar">
+ <toolbarbutton id="help-back-button" type="menu-button"
+ label="&backButton.label;"
+ oncommand="if (event.target == this) goBack(); else gotoHistoryIndex(event);"
+ observes="canGoBack" context="backMenu"
+ tooltiptext="&backButton.tooltip;">
+ <menupopup context="" onpopupshowing="createBackMenu(event);"/>
+ </toolbarbutton>
+ <toolbarbutton id="help-forward-button" type="menu-button"
+ oncommand="if (event.target == this) goForward(); else gotoHistoryIndex(event);"
+ tooltiptext="&forwardButton.tooltip;"
+ observes="canGoForward">
+ <menupopup context="" onpopupshowing="createForwardMenu(event);"/>
+ </toolbarbutton>
+ <toolbarbutton id="help-home-button"
+ tooltiptext="&homeButton.tooltip;"
+ command="Help:Home"/>
+ <toolbarseparator/>
+ <toolbarbutton id="help-print-button"
+ label="&printButton.label;"
+ oncommand="print();"
+ tooltiptext="&printButton.tooltip;"/>
+ <toolbarspring flex="1"/>
+ <toolbaritem id="search-box"
+ align="center" pack="center">
+ <textbox id="findText"
+ type="search"
+ placeholder="&search.emptytext;"
+ aria-controls="help-toc-panel"
+ oncommand="showSidebar(); doFind();"/>
+ </toolbaritem>
+ </toolbar>
+ </toolbox>
+
+ <hbox flex="1">
+ <vbox id="help-sidebar" persist="width">
+ <vbox flex="1" id="help-toc-sidebar">
+ <sidebarheader align="center">
+ <label id="help-toc-sidebar-header" flex="1" crop="end" value="&toctab.label;"
+ accesskey="&toctab.accesskey;" control="help-toc-panel"/>
+ </sidebarheader>
+ <tree id="help-toc-panel" class="focusring"
+ flex="1" treelines="true" hidecolumnpicker="true"
+ datasources="rdf:null"
+ containment="http://home.netscape.com/NC-rdf#subheadings"
+ ref="urn:root" flags="dont-build-content"
+ onselect="onselect_loadURI(this)">
+ <template>
+ <rule>
+ <conditions>
+ <content uri="?uri"/>
+ <triple subject="?uri"
+ predicate="http://home.netscape.com/NC-rdf#subheadings"
+ object="?subheadings"/>
+ <member container="?subheadings"
+ child="?subheading"/>
+ <triple subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#name"
+ object="?name"/>
+ </conditions>
+ <action>
+ <treechildren>
+ <treeitem uri="?subheading">
+ <treerow>
+ <treecell label="?name"/>
+ </treerow>
+ </treeitem>
+ </treechildren>
+ </action>
+ </rule>
+ </template>
+ <treecols>
+ <treecol id="NameColumn" flex="1" hideheader="true"
+ primary="true"/>
+ </treecols>
+ </tree>
+ </vbox>
+ <vbox id="help-search-sidebar" hidden="true" flex="1">
+ <sidebarheader align="center">
+ <label id="help-search-sidebar-header" flex="1" crop="end"
+ value="&searchHeader.label;"/>
+ </sidebarheader>
+ <tree id="help-search-tree" class="focusring"
+ flex="1" hidecolumnpicker="true"
+ datasources="rdf:null"
+ containment="http://home.netscape.com/NC-rdf#child"
+ ref="urn:root" flags="dont-build-content"
+ onselect="onselect_loadURI(this)">
+ <template>
+ <rule>
+ <conditions>
+ <content uri="?uri"/>
+ <member container="?uri"
+ child="?subheading"/>
+ </conditions>
+ <bindings>
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#name"
+ object="?name"/>
+ </bindings>
+ <action>
+ <treechildren>
+ <treeitem uri="?subheading">
+ <treerow>
+ <treecell label="?name"/>
+ </treerow>
+ </treeitem>
+ </treechildren>
+ </action>
+ </rule>
+ </template>
+ <treecols>
+ <treecol id="ResultsColumn" flex="1"
+ hideheader="true" primary="true"
+ sortActive="true" sortDirection="ascending"
+ sort="?name"/>
+ </treecols>
+ </tree>
+ </vbox>
+
+ <!-- BEGIN hidden trees used for searching -->
+ <!-- xxxmpc: we need a better solution for this -->
+
+ <vbox id="help-sidebar-hidden-trees" hidden="true">
+ <tree id="help-glossary-panel"
+ flex="1" hidecolumnpicker="true"
+ datasources="rdf:null"
+ containment="http://home.netscape.com/NC-rdf#subheadings"
+ ref="urn:root" flags="dont-build-content"/>
+ <tree id="help-index-panel"
+ flex="1" datasources="rdf:null"
+ hidecolumnpicker="true"
+ containment="http://home.netscape.com/NC-rdf#subheadings"
+ ref="urn:root"
+ flags="dont-build-content dont-test-empty"/>
+ <tree id="help-search-panel"
+ flex="1" hidecolumnpicker="true"
+ datasources="rdf:null"
+ containment="http://home.netscape.com/NC-rdf#subheadings"
+ ref="urn:root" flags="dont-build-content"/>
+ </vbox>
+
+ <!-- END HIDDEN ITEMS -->
+ </vbox>
+
+ <splitter id="help-sidebar-splitter" collapse="before">
+ <grippy/>
+ </splitter>
+
+ <vbox id="appcontent" flex="3">
+ <!-- type attribute is used by frame construction to locate
+ iframes intended to hold (html) content -->
+ <browser context="contentAreaContextMenu"
+ type="content"
+ primary="true"
+ id="help-content"
+ src="about:blank"
+ flex="1"
+ onclick="return contentClick(event);"/>
+ <findbar id="FindToolbar" browserid="help-content"/>
+ <browser type="content"
+ id="help-external"
+ collapsed="true"/>
+
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/comm/suite/components/helpviewer/content/helpContextOverlay.xul b/comm/suite/components/helpviewer/content/helpContextOverlay.xul
new file mode 100644
index 0000000000..60cdaf69ca
--- /dev/null
+++ b/comm/suite/components/helpviewer/content/helpContextOverlay.xul
@@ -0,0 +1,58 @@
+<?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 % helpDTD SYSTEM "chrome://help/locale/help.dtd">
+ %helpDTD;
+]>
+<overlay id="contentAreaContextOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+# Help Window's right-click menu
+ <popupset id="contentAreaContextSet">
+ <menupopup id="contentAreaContextMenu"
+ onpopupshowing="goUpdateCommand('cmd_copy')">
+ <menuitem id="context-back"
+ label="&backButton.label;"
+ accesskey="&backButton.accesskey;"
+ observes="canGoBack"
+ oncommand="goBack()"/>
+ <menuitem id="context-forward"
+ label="&forwardButton.label;"
+ accesskey="&forwardButton.accesskey;"
+ observes="canGoForward"
+ oncommand="goForward()"/>
+ <menuseparator/>
+ <menuitem id="context-copy"
+ label="&copyCmd.label;"
+ accesskey="&copyCmd.accesskey;"
+ command="cmd_copy"
+ disabled="true"/>
+ <menuitem id="context-selectall"
+ label="&selectAllCmd.label;"
+ accesskey="&selectAllCmd.accesskey;"
+ command="cmd_selectAll"/>
+ <menuseparator/>
+ <menuitem id="zoom-in"
+ label="&fullZoomEnlargeBtn.label;"
+ accesskey="&fullZoomEnlargeBtn.accesskey;"
+ oncommand="ZoomManager.enlarge();"/>
+ <menuitem id="zoom-out"
+ label="&fullZoomReduceBtn.label;"
+ accesskey="&fullZoomReduceBtn.accesskey;"
+ oncommand="ZoomManager.reduce();"/>
+#ifdef XP_WIN
+ <menuseparator/>
+ <menuitem id="context-zlevel"
+ type="checkbox"
+ checked="true"
+ persist="checked"
+ label="&zLevel.label;"
+ accesskey="&zLevel.accesskey;"
+ oncommand="toggleZLevel(this);"/>
+#endif
+ </menupopup>
+ </popupset>
+</overlay>
diff --git a/comm/suite/components/helpviewer/content/platformClasses.css b/comm/suite/components/helpviewer/content/platformClasses.css
new file mode 100644
index 0000000000..30332fdef3
--- /dev/null
+++ b/comm/suite/components/helpviewer/content/platformClasses.css
@@ -0,0 +1,13 @@
+/* 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/. */
+
+%ifdef XP_WIN
+.noWin, .mac, .unix { display: none; }
+%else
+%ifdef XP_MACOSX
+.noMac, .win, .unix { display: none; }
+%else
+.noUnix, .win, .mac { display: none; }
+%endif
+%endif
diff --git a/comm/suite/components/helpviewer/jar.mn b/comm/suite/components/helpviewer/jar.mn
new file mode 100644
index 0000000000..d536245fef
--- /dev/null
+++ b/comm/suite/components/helpviewer/jar.mn
@@ -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/.
+
+comm.jar:
+% content help %content/communicator/helpviewer/
+* content/communicator/helpviewer/help.xul (content/help.xul)
+ content/communicator/helpviewer/contextHelp.js (content/contextHelp.js)
+ content/communicator/helpviewer/help.js (content/help.js)
+* content/communicator/helpviewer/helpContextOverlay.xul (content/helpContextOverlay.xul)
+* content/communicator/helpviewer/platformClasses.css (content/platformClasses.css)
diff --git a/comm/suite/components/helpviewer/moz.build b/comm/suite/components/helpviewer/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/helpviewer/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/migration/SuiteProfileMigrator.js b/comm/suite/components/migration/SuiteProfileMigrator.js
new file mode 100644
index 0000000000..0ad4dfcca3
--- /dev/null
+++ b/comm/suite/components/migration/SuiteProfileMigrator.js
@@ -0,0 +1,149 @@
+/* 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/. */
+
+"use strict";
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const { FileUtils } =
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+const { AppConstants } =
+ ChromeUtils.import('resource://gre/modules/AppConstants.jsm');
+
+ChromeUtils.defineModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+
+function ProfileMigrator() {
+}
+
+ProfileMigrator.prototype = {
+ migrate: function PM_migrate(aStartup) {
+ // By opening the wizard with a supplied migrator, it will automatically
+ // migrate from it.
+ let [key, migrator] = this._getDefaultMigrator();
+ if (!key)
+ return;
+
+ let params = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ params.appendElement(this._toString(key));
+ params.appendElement(migrator);
+ params.appendElement(aStartup);
+
+ Services.ww.openWindow(null,
+ "chrome://communicator/content/migration/migration.xul",
+ "_blank",
+ "chrome,dialog,modal,centerscreen,titlebar",
+ params);
+ },
+
+ _toString: function PM__toString(aStr) {
+ let str = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ str.data = aStr;
+ return str;
+ },
+
+ _getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) {
+ let cid = "@mozilla.org/profile/migrator;1?app=suite&type=" + aKey;
+ let migrator = Cc[cid].createInstance(Ci.nsISuiteProfileMigrator);
+ if (migrator.sourceExists)
+ return migrator;
+ return null;
+ },
+
+ // We don't yet support checking for the default browser on all platforms,
+ // needless to say we don't have migrators for all browsers. Thus, for each
+ // platform, there's a fallback list of migrators used in these cases.
+ _PLATFORM_FALLBACK_LIST:
+ ["thunderbird"],
+
+ _getDefaultMigrator: function PM__getDefaultMigrator() {
+
+ let migratorsOrdered = Array.from(this._PLATFORM_FALLBACK_LIST);
+
+ // FIXME This is all so not working currently.
+ // There are currently no migrators for browsers available.
+ // See Bug 739056.
+ if (false) {
+ let defaultBrowser = "";
+
+ if (AppConstants.platform == "win") {
+ try {
+ const REG_KEY = "SOFTWARE\\Classes\\HTTP\\shell\\open\\command";
+ let regKey = Cc["@mozilla.org/windows-registry-key;1"]
+ .createInstance(Ci.nsIWindowsRegKey);
+ regKey.open(regKey.ROOT_KEY_LOCAL_MACHINE, REG_KEY,
+ regKey.ACCESS_READ);
+ let value = regKey.readStringValue("").toLowerCase();
+ let pathMatches = value.match(/^"?(.+?\.exe)"?/);
+ if (!pathMatches) {
+ throw new Error("Could not extract path from " +
+ REG_KEY + "(" + value + ")");
+ }
+
+ // We want to find out what the default browser is but the path in and of
+ // itself isn't enough. Why? Because sometimes on Windows paths get
+ // truncated like so: C:\PROGRA~1\MOZILL~2\MOZILL~1.EXE. How do we know
+ // what product that is? Mozilla's file objects do nothing to 'normalize'
+ // the path so we need to attain an actual product descriptor from the
+ // file somehow, and in this case it means getting the "InternalName"
+ // field of the file's VERSIONINFO resource.
+ //
+ // In the file's resource segment there is a VERSIONINFO section that is
+ // laid out like this:
+ //
+ // VERSIONINFO
+ // StringFileInfo
+ // <TranslationID>
+ // InternalName "iexplore"
+ // VarFileInfo
+ // Translation <TranslationID>
+ //
+ // By Querying the VERSIONINFO section for its Tranlations, we can find
+ // out where the InternalName lives (A file can have more than one
+ // translation of its VERSIONINFO segment, but we just assume the first
+ // one).
+ let file = FileUtils.File(pathMatches[1])
+ .QueryInterface(Ci.nsILocalFileWin);
+ switch (file.getVersionInfoField("InternalName").toLowerCase()) {
+ case "iexplore":
+ defaultBrowser = "ie";
+ break;
+ case "chrome":
+ defaultBrowser = "chrome";
+ break;
+ }
+ }
+ catch (ex) {
+ Cu.reportError("Could not retrieve default browser: " + ex);
+ }
+ }
+
+ // If we found the default browser and we have support for that browser,
+ // make sure to check it before any other browser, by moving it to the head
+ // of the array.
+ if (defaultBrowser) {
+ migratorsOrdered.sort((a, b) => b == defaultBrowser ? 1 : 0);
+ }
+ }
+
+ for (let key of migratorsOrdered) {
+ let migrator = this._getMigratorIfSourceExists(key);
+ if (migrator) {
+ return [key, migrator];
+ }
+ }
+
+ return ["", null];
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIProfileMigrator]),
+ classDescription: "Profile Migrator",
+ contractID: "@mozilla.org/toolkit/profile-migrator;1",
+ classID: Components.ID("{d5148b7c-ba4e-4f7a-a80b-1ae48b90b910}"),
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([ProfileMigrator]);
diff --git a/comm/suite/components/migration/SuiteProfileMigrator.manifest b/comm/suite/components/migration/SuiteProfileMigrator.manifest
new file mode 100644
index 0000000000..a57376b855
--- /dev/null
+++ b/comm/suite/components/migration/SuiteProfileMigrator.manifest
@@ -0,0 +1,2 @@
+component {d5148b7c-ba4e-4f7a-a80b-1ae48b90b910} SuiteProfileMigrator.js
+contract @mozilla.org/toolkit/profile-migrator;1 {d5148b7c-ba4e-4f7a-a80b-1ae48b90b910}
diff --git a/comm/suite/components/migration/content/migration.js b/comm/suite/components/migration/content/migration.js
new file mode 100644
index 0000000000..b106e51e22
--- /dev/null
+++ b/comm/suite/components/migration/content/migration.js
@@ -0,0 +1,413 @@
+/* -*- 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const NS_PROFILE_MIGRATOR_CONTRACTID =
+ "@mozilla.org/profile/migrator;1?app=suite&type=";
+
+var MigrationWizard = {
+ _source: "", // Source Profile Migrator ContractID suffix
+ _itemsFlags: Ci.nsISuiteProfileMigrator.ALL, // Selected Import Data Sources
+ _selectedProfile: null, // Selected Profile name to import from
+ _wiz: null, // Shortcut to the wizard
+ _migrator: null, // The actual profile migrator.
+ _autoMigrate: null, // Whether or not we are actually migrating.
+ _singleItem: false, // Are we choosing just to import a single
+ // item into the current profile?
+ _newHomePage: null, // Are we setting a new home page - what to?
+
+ init: function() {
+ Services.obs.addObserver(this, "Migration:Started");
+ Services.obs.addObserver(this, "Migration:ItemBeforeMigrate");
+ Services.obs.addObserver(this, "Migration:ItemAfterMigrate");
+ Services.obs.addObserver(this, "Migration:Ended");
+ Services.obs.addObserver(this, "Migration:Progress");
+
+ this._wiz = document.documentElement;
+ this._wiz.canRewind = false;
+
+ if ("arguments" in window) {
+ if ("arguments" in window && window.arguments[0] == "bookmarks") {
+ this._singleItem = true;
+ this._itemsFlags = Ci.nsISuiteProfileMigrator.BOOKMARKS;
+ document.getElementById("fromFile").hidden = false;
+ document.getElementById("importBookmarks").hidden = false;
+ document.getElementById("importAll").hidden = true;
+ }
+ else if (window.arguments.length > 1) {
+ this._source = window.arguments[0];
+ this._migrator =
+ window.arguments[1] instanceof Ci.nsISuiteProfileMigrator ?
+ window.arguments[1] : null;
+ this._autoMigrate = window.arguments[2]
+ .QueryInterface(Ci.nsIProfileStartup);
+ // Show the "nothing" option in the automigrate case to provide an
+ // easily identifiable way to avoid migration and create a new profile.
+ document.getElementById("nothing").hidden = false;
+ }
+ }
+
+ this.onImportSourcePageShow();
+ },
+
+ uninit: function() {
+ Services.obs.removeObserver(this, "Migration:Started");
+ Services.obs.removeObserver(this, "Migration:ItemBeforeMigrate");
+ Services.obs.removeObserver(this, "Migration:ItemAfterMigrate");
+ Services.obs.removeObserver(this, "Migration:Ended");
+ Services.obs.removeObserver(this, "Migration:Progress");
+ },
+
+ // 1 - Import Source
+ onImportSourcePageShow: function() {
+ // Figure out what source apps are are available to import from:
+ var group = document.getElementById("importSourceGroup");
+ var firstSelectable = null;
+ for (var i = 0; i < group.childNodes.length; ++i) {
+ var suffix = group.childNodes[i].id;
+ if (suffix != "nothing" && suffix != "fromFile") {
+ var contractID = NS_PROFILE_MIGRATOR_CONTRACTID + suffix;
+ var migrator = null;
+ if (contractID in Cc) {
+ migrator = Cc[contractID]
+ .createInstance(Ci.nsISuiteProfileMigrator);
+ } else {
+ dump("*** invalid contractID =" + contractID + "\n");
+ // This is an invalid contract id, therefore hide this element
+ // and allow things to continue - that way we should be able to
+ // copy with anything happening.
+ group.childNodes[i].hidden = true;
+ break;
+ }
+
+ // Ensure that we only allow import selections for profile
+ // migrators that support the requested action.
+ if (!migrator.sourceExists ||
+ !(migrator.supportedItems & this._itemsFlags)) {
+ group.childNodes[i].hidden = true;
+ break;
+ }
+
+ if (!firstSelectable && !group.childNodes[i].disabled &&
+ !group.childNodes[i].hidden) {
+ firstSelectable = group.childNodes[i];
+ }
+ }
+ }
+
+ if (this._source) {
+ // Somehow the Profile Migrator got confused, and gave us a migrate source
+ // that doesn't actually exist. This could be because of a bogus registry
+ // state. Set the _source property to null so the first visible item in
+ // the list is selected instead.
+ if (document.getElementById(this._source).hidden)
+ this._source = null;
+ }
+ group.selectedItem = this._source ?
+ document.getElementById(this._source) : firstSelectable;
+ },
+
+ onImportSourcePageAdvanced: function() {
+ var newSource = document.getElementById("importSourceGroup").value;
+
+ if (newSource == "nothing" || newSource == "fromFile") {
+ if (newSource == "fromFile") {
+ window.opener.fromFile = true;
+ }
+ document.documentElement.cancel();
+ // Don't let the wizard migrate to the next page event though we've
+ // called cancel - cancel may not get processed first.
+ return false;
+ }
+
+ if (!this._migrator || newSource != this._source) {
+ // Create the migrator for the selected source.
+ var contractID = NS_PROFILE_MIGRATOR_CONTRACTID + newSource;
+ this._migrator = Cc[contractID]
+ .createInstance(Ci.nsISuiteProfileMigrator);
+
+ this._selectedProfile = null;
+ }
+ this._source = newSource;
+
+ // check for more than one source profile
+ if (this._migrator.sourceHasMultipleProfiles)
+ this._wiz.currentPage.next = "selectProfile";
+ else {
+ if (this._autoMigrate)
+ this._wiz.currentPage.next = "homePageImport";
+ else if (this._singleItem)
+ this._wiz.currentPage.next = "migrating";
+ else
+ this._wiz.currentPage.next = "importItems";
+
+ var sourceProfiles = this._migrator.sourceProfiles;
+ if (sourceProfiles && sourceProfiles.length == 1) {
+ this._selectedProfile = sourceProfiles
+ .queryElementAt(0, Ci.nsISupportsString).data;
+ }
+ else
+ this._selectedProfile = "";
+ }
+ return true;
+ },
+
+ // 2 - [Profile Selection]
+ onSelectProfilePageShow: function() {
+ var profiles = document.getElementById("profiles");
+ while (profiles.hasChildNodes())
+ profiles.lastChild.remove();
+
+ var sourceProfiles = this._migrator.sourceProfiles;
+ var count = sourceProfiles.length;
+ for (var i = 0; i < count; ++i) {
+ var item = document.createElement("radio");
+ item.id = sourceProfiles.queryElementAt(i, Ci.nsISupportsString).data;
+ item.setAttribute("label", item.id);
+ profiles.appendChild(item);
+ }
+
+ profiles.selectedItem = this._selectedProfile ?
+ document.getElementById(this._selectedProfile) : profiles.firstChild;
+ },
+
+ onSelectProfilePageAdvanced: function() {
+ this._selectedProfile = document.getElementById("profiles").selectedItem.id;
+
+ // If we're automigrating or just doing bookmarks don't show the item
+ // selection page
+ if (this._autoMigrate)
+ this._wiz.currentPage.next = "homePageImport";
+ else if (this._singleItem)
+ this._wiz.currentPage.next = "migrating"
+ },
+
+ // 3 - ImportItems
+ onImportItemsPageShow: function() {
+ var dataSources = document.getElementById("dataSources");
+ while (dataSources.hasChildNodes())
+ dataSources.lastChild.remove();
+
+ var bundle = document.getElementById("bundle");
+
+ var items = this._migrator.getMigrateData(this._selectedProfile,
+ this._autoMigrate);
+
+ for (var i = 0; i < 16; ++i) {
+ var itemID = items & (1 << i);
+ if (itemID) {
+ var checkbox = document.createElement("checkbox");
+ checkbox.id = itemID;
+ try {
+ checkbox.setAttribute("label",
+ bundle.getString(itemID + "_" + this._source));
+ }
+ catch (ex) {
+ checkbox.setAttribute("label",
+ bundle.getString(itemID + "_generic"));
+ }
+ dataSources.appendChild(checkbox);
+ if (this._itemsFlags & itemID)
+ checkbox.setAttribute("checked", true);
+ }
+ }
+ },
+
+ onImportItemsPageRewound: function() {
+ this._wiz.canAdvance = true;
+ this.onImportItemsPageAdvanced();
+ },
+
+ onImportItemsPageAdvanced: function() {
+ var dataSources = document.getElementById("dataSources");
+ this._itemsFlags = 0;
+ for (var i = 0; i < dataSources.childNodes.length; ++i) {
+ var checkbox = dataSources.childNodes[i];
+ if (checkbox.checked)
+ this._itemsFlags |= checkbox.id;
+ }
+ },
+
+ onImportItemCommand: function(aEvent) {
+ this._wiz.canAdvance = document
+ .getElementById("dataSources")
+ .getElementsByAttribute("checked", "true").item(0) != null;
+ },
+
+ // 4 - Home Page Selection
+ onHomePageMigrationPageShow: function() {
+ // only want this on the first run
+ if (!this._autoMigrate) {
+ this._wiz.advance();
+ return;
+ }
+
+ var bundle = document.getElementById("bundle");
+ var pageTitle = bundle.getString("homePageMigrationPageTitle");
+ var pageDesc = bundle.getString("homePageMigrationDescription");
+
+ document.getElementById("homePageImport").setAttribute("label", pageTitle);
+ document.getElementById("homePageImportDesc")
+ .setAttribute("value", pageDesc);
+
+ this._wiz._adjustWizardHeader();
+
+ var homePageStart = document.getElementById("homePageStart");
+
+ // Find out if the target profile already has a homepage or not
+ var mainStr = this.targetHasHomePageURL() ?
+ bundle.getString("homePageStartCurrent") :
+ bundle.getString("homePageStartDefault");
+
+ homePageStart.setAttribute("label", mainStr);
+
+ var source = null;
+ if (this._source != "") {
+ source = "sourceName" + this._source;
+ }
+
+ var availableItems = this._migrator.getMigrateData(this._selectedProfile,
+ this._autoMigrate);
+
+ if (source && (availableItems & Ci.nsISuiteProfileMigrator.HOMEPAGEDATA)) {
+ var appName = document.getElementById("bundle").getString(source);
+ var oldHomePageLabel = bundle.getFormattedString("homePageImport",
+ [appName]);
+ var oldHomePage = document.getElementById("oldHomePage");
+ oldHomePage.setAttribute("label", oldHomePageLabel);
+ oldHomePage.setAttribute("value", "source");
+ oldHomePage.removeAttribute("hidden");
+ oldHomePage.focus();
+
+ document.getElementById("homePageRadioGroup").selectedItem = oldHomePage;
+ }
+ else {
+ // if we don't have at least two options, just advance
+ this._wiz.advance();
+ }
+ },
+
+ onHomePageMigrationPageAdvanced: function() {
+ // we might not have a selectedItem if we're in fallback mode
+ try {
+ this._newHomePage = document.getElementById("homePageRadioGroup")
+ .value;
+ } catch(ex) {}
+ },
+
+ // 5 - Migrating
+ onMigratingPageShow: function() {
+ this._wiz.getButton("cancel").disabled = true;
+ this._wiz.canRewind = false;
+ this._wiz.canAdvance = false;
+
+ // When migrating a profile on startup, show all of the data that can be
+ // received from this source, but exclude home pages if the user didn't
+ // want to migrate it.
+ if (this._autoMigrate) {
+ this._itemsFlags = this._migrator.getMigrateData(this._selectedProfile,
+ this._autoMigrate);
+ if (!this._newHomePage)
+ this._itemsFlags &= ~Ci.nsISuiteProfileMigrator.HOMEPAGEDATA;
+ }
+
+ this._listItems("migratingItems");
+ setTimeout(this.onMigratingMigrate, 0, this);
+ },
+
+ onMigratingMigrate: function(aOuter) {
+ aOuter._migrator.migrate(aOuter._itemsFlags,
+ aOuter._autoMigrate,
+ aOuter._selectedProfile);
+ },
+
+ _listItems: function(aID) {
+ var items = document.getElementById(aID);
+ while (items.hasChildNodes())
+ items.lastChild.remove();
+
+ var bundle = document.getElementById("bundle");
+ var itemID;
+ for (var x = 1; x < Ci.nsISuiteProfileMigrator.ALL;
+ x = x << 1) {
+ if (x & this._itemsFlags) {
+ var label = document.createElement("label");
+ label.id = x + "_migrated";
+ try {
+ label.setAttribute("value",
+ bundle.stringBundle
+ .GetStringFromName(x + "_"+ this._source));
+ label.setAttribute("class", "migration-pending");
+ items.appendChild(label);
+ }
+ catch (ex) {
+ try {
+ label.setAttribute("value", bundle.getString(x + "_generic"));
+ label.setAttribute("class", "migration-pending");
+ items.appendChild(label);
+ }
+ catch (e) {
+ // if the block above throws, we've enumerated all the import
+ // data types we currently support and are now just wasting time.
+ break;
+ }
+ }
+ }
+ }
+ },
+
+ observe: function(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "Migration:Progress":
+ document.getElementById("progressBar").value = aData;
+ break;
+ case "Migration:Started":
+ break;
+ case "Migration:ItemBeforeMigrate":
+ var label = document.getElementById(aData + "_migrated");
+ if (label)
+ label.setAttribute("class", "migration-in-progress");
+ break;
+ case "Migration:ItemAfterMigrate":
+ var label = document.getElementById(aData + "_migrated");
+ if (label)
+ label.setAttribute("class", "migration-finished");
+ break;
+ case "Migration:Ended":
+ // We're done now.
+ this._wiz.canAdvance = true;
+ this._wiz.advance();
+
+ if (this._autoMigrate)
+ setTimeout(close, 5000);
+
+ break;
+ }
+ },
+
+ onDonePageShow: function() {
+ this._wiz.getButton("cancel").disabled = true;
+ this._wiz.canRewind = false;
+ this._listItems("doneItems");
+ },
+
+ targetHasHomePageURL: function() {
+ var targetPrefsFile = this._autoMigrate.directory;
+
+ targetPrefsFile.append("prefs.js");
+
+ // If the target prefs file doesn't exist, then we can't have a
+ // homepage set in the target profile.
+ if (!targetPrefsFile.exists())
+ return false;
+
+ Services.prefs.resetPrefs();
+
+ Services.prefs.readUserPrefsFromFile(targetPrefsFile);
+
+ return Services.prefs.prefHasUserValue("browser.startup.homepage");
+ }
+};
diff --git a/comm/suite/components/migration/content/migration.xul b/comm/suite/components/migration/content/migration.xul
new file mode 100644
index 0000000000..ad8b279361
--- /dev/null
+++ b/comm/suite/components/migration/content/migration.xul
@@ -0,0 +1,95 @@
+<?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 dialog SYSTEM "chrome://communicator/locale/migration/migration.dtd" >
+
+<wizard id="migrationWizard"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&migrationWizard.title;"
+ onload="MigrationWizard.init()"
+ onunload="MigrationWizard.uninit()"
+ style="width: 40em; height: 32em;"
+ branded="true"
+ buttons="accept,cancel"
+ xmlns:xhtml="http://www.w3.org/1999/xhtml">
+
+ <script src="chrome://communicator/content/migration/migration.js"/>
+
+ <stringbundle id="bundle"
+ src="chrome://communicator/locale/migration/migration.properties"/>
+
+ <wizardpage id="importSource" pageid="importSource" next="selectProfile"
+ label="&importSource.title;"
+ onpageadvanced="return MigrationWizard.onImportSourcePageAdvanced();">
+ <label id="importAll"
+ control="importSourceGroup">&importAllFrom.label;</label>
+ <label id="importBookmarks" control="importSourceGroup"
+ hidden="true">&importBookmarksFrom.label;</label>
+
+ <radiogroup id="importSourceGroup" align="start">
+ <radio id="thunderbird" label="&importFromThunderbird.label;"
+ accesskey="&importFromThunderbird.accesskey;"
+ value="thunderbird"/>
+ <!-- fromfile is used for bookmark importing -->
+ <radio id="fromFile" label="&importFromFile.label;" value="fromFile"
+ accesskey="&importFromFile.accesskey;" hidden="true"/>
+ <radio id="nothing" label="&importFromNothing.label;" value="nothing"
+ accesskey="&importFromNothing.accesskey;" hidden="true"/>
+ </radiogroup>
+ </wizardpage>
+
+ <wizardpage id="selectProfile" pageid="selectProfile"
+ label="&selectProfile.title;" next="importItems"
+ onpageshow="return MigrationWizard.onSelectProfilePageShow();"
+ onpageadvanced="return MigrationWizard.onSelectProfilePageAdvanced();">
+ <label control="profiles">&selectProfile.label;</label>
+
+ <radiogroup id="profiles" align="left"/>
+ </wizardpage>
+
+ <wizardpage id="importItems" pageid="importItems" label="&importItems.title;"
+ next="homePageImport"
+ onpageshow="return MigrationWizard.onImportItemsPageShow();"
+ onpageadvanced="return MigrationWizard.onImportItemsPageAdvanced();"
+ oncommand="MigrationWizard.onImportItemCommand();">
+ <label control="dataSources">&importItems.label;</label>
+
+ <vbox id="dataSources" style="overflow-y: auto;"
+ align="left" flex="1" role="group"/>
+ </wizardpage>
+
+ <wizardpage id="homePageImport" pageid="homePageImport"
+ next="migrating"
+ onpageshow="return MigrationWizard.onHomePageMigrationPageShow();"
+ onpageadvanced="return MigrationWizard.onHomePageMigrationPageAdvanced();">
+
+ <label id="homePageImportDesc" control="homePageRadioGroup"/>
+ <radiogroup id="homePageRadioGroup" align="start">
+ <radio id="oldHomePage" hidden="true"/>
+ <radio id="homePageStart" selected="true"/>
+ </radiogroup>
+ </wizardpage>
+
+ <wizardpage id="migrating" pageid="migrating" label="&migrating.title;"
+ next="done" onpageshow="MigrationWizard.onMigratingPageShow();">
+ <label control="migratingItems">&migrating.label;</label>
+
+ <vbox id="migratingItems" style="overflow-y: auto;" align="left" role="group"/>
+
+ <hbox>
+ <progressmeter class="progressmeter-statusbar" id="progressBar"
+ flex="1" mode="normal" value="0"/>
+ </hbox>
+ </wizardpage>
+
+ <wizardpage id="done" pageid="done" label="&done.title;"
+ onpageshow="MigrationWizard.onDonePageShow();">
+ <label control="doneItems">&done.label;</label>
+
+ <vbox id="doneItems" style="overflow-y: auto;" align="left" role="group"/>
+ </wizardpage>
+</wizard>
diff --git a/comm/suite/components/migration/jar.mn b/comm/suite/components/migration/jar.mn
new file mode 100644
index 0000000000..a202ce87a1
--- /dev/null
+++ b/comm/suite/components/migration/jar.mn
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/migration/migration.xul (content/migration.xul)
+ content/communicator/migration/migration.js (content/migration.js)
diff --git a/comm/suite/components/migration/moz.build b/comm/suite/components/migration/moz.build
new file mode 100644
index 0000000000..ef93d7617a
--- /dev/null
+++ b/comm/suite/components/migration/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ "public",
+ "src",
+]
+
+EXTRA_COMPONENTS += [
+ "SuiteProfileMigrator.js",
+ "SuiteProfileMigrator.manifest",
+]
+
+FINAL_LIBRARY = "suite"
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/migration/public/moz.build b/comm/suite/components/migration/public/moz.build
new file mode 100644
index 0000000000..a25b10157b
--- /dev/null
+++ b/comm/suite/components/migration/public/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "nsISuiteProfileMigrator.idl",
+]
+
+XPIDL_MODULE = "suitemigration"
+
+EXPORTS += [
+ "nsSuiteMigrationCID.h",
+]
diff --git a/comm/suite/components/migration/public/nsISuiteProfileMigrator.idl b/comm/suite/components/migration/public/nsISuiteProfileMigrator.idl
new file mode 100644
index 0000000000..ca0bd98413
--- /dev/null
+++ b/comm/suite/components/migration/public/nsISuiteProfileMigrator.idl
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIArray;
+interface nsIProfileStartup;
+
+[scriptable, uuid(b6adb2b8-5e3b-4fdd-b085-d58998b5c21a)]
+interface nsISuiteProfileMigrator : nsISupports
+{
+ /**
+ * profile items to migrate. use with migrate().
+ */
+ const unsigned short SETTINGS = 0x0001;
+ const unsigned short COOKIES = 0x0002;
+ const unsigned short HISTORY = 0x0004;
+ const unsigned short HOMEPAGEDATA = 0x0008;
+ const unsigned short PASSWORDS = 0x0010;
+ const unsigned short BOOKMARKS = 0x0020;
+ const unsigned short OTHERDATA = 0x0040;
+ const unsigned short ACCOUNT_SETTINGS = 0x0080;
+ const unsigned short ADDRESSBOOK_DATA = 0x0100;
+ const unsigned short JUNKTRAINING = 0x0200;
+ const unsigned short NEWSDATA = 0x0400;
+ const unsigned short MAILDATA = 0x0800;
+ const unsigned short ALL = 0x0FFF;
+
+ /**
+ * Copy user profile information to the current active profile.
+ *
+ * @param aItems list of data items to migrate. see above for values.
+ * @param aReplace replace or append current data where applicable.
+ * @param aProfile profile to migrate from, if there is more than one.
+ */
+ void migrate(in unsigned short aItems, in nsIProfileStartup aStartup,
+ in wstring aProfile);
+
+ /**
+ * A bit field containing profile items that this migrator is able
+ * to import for a specified source profile.
+ *
+ * @param aProfile the profile that we are looking for available data
+ * to import
+ * @param aStarting "true" if the profile is not currently being used.
+ * @returns bit field containing profile items (see above)
+ */
+ unsigned short getMigrateData(in wstring aProfile, in boolean aDoingStartup);
+
+ /**
+ * A bit field containing profile items that this migrator may be able
+ * to import for any source profile of its type.
+ */
+ readonly attribute unsigned short supportedItems;
+
+ /**
+ * Whether or not there is any data that can be imported from this
+ * browser (i.e. whether or not it is installed, and there exists
+ * a user profile)
+ */
+ readonly attribute boolean sourceExists;
+
+ /**
+ * Whether or not the import source implementing this interface
+ * has multiple user profiles configured.
+ */
+ readonly attribute boolean sourceHasMultipleProfiles;
+
+ /**
+ * An enumeration of available profiles. If the import source does
+ * not support profiles, this attribute is null.
+ */
+ readonly attribute nsIArray sourceProfiles;
+};
diff --git a/comm/suite/components/migration/public/nsSuiteMigrationCID.h b/comm/suite/components/migration/public/nsSuiteMigrationCID.h
new file mode 100644
index 0000000000..252f5ed9df
--- /dev/null
+++ b/comm/suite/components/migration/public/nsSuiteMigrationCID.h
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define NS_SUITEPROFILEMIGRATOR_CONTRACTID_PREFIX "@mozilla.org/profile/migrator;1?app=suite&type="
+
+#define NS_THUNDERBIRDPROFILEMIGRATOR_CID \
+{ 0x6ba91adb, 0xa4ed, 0x405f, { 0xbd, 0x6c, 0xe9, 0x04, 0xa9, 0x9d, 0x9a, 0xd8 } }
diff --git a/comm/suite/components/migration/src/moz.build b/comm/suite/components/migration/src/moz.build
new file mode 100644
index 0000000000..367fecf45e
--- /dev/null
+++ b/comm/suite/components/migration/src/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ "nsSuiteProfileMigratorBase.cpp",
+ "nsSuiteProfileMigratorUtils.cpp",
+ "nsThunderbirdProfileMigrator.cpp",
+]
+
+FINAL_LIBRARY = "suite"
diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp
new file mode 100644
index 0000000000..521fa32215
--- /dev/null
+++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.cpp
@@ -0,0 +1,844 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsSuiteProfileMigratorUtils.h"
+#include "nsCRT.h"
+#include "nsIFile.h"
+#include "nsILineInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsIPrefService.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIURL.h"
+#include "nsSuiteProfileMigratorBase.h"
+#include "nsNetUtil.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIFileProtocolHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "prtime.h"
+#include "nsINIParser.h"
+#include "nsArrayUtils.h"
+
+#define MAIL_DIR_50_NAME u"Mail"_ns
+#define IMAP_MAIL_DIR_50_NAME u"ImapMail"_ns
+#define NEWS_DIR_50_NAME u"News"_ns
+#define DIR_NAME_CHROME u"chrome"_ns
+
+NS_IMPL_ISUPPORTS(nsSuiteProfileMigratorBase, nsISuiteProfileMigrator,
+ nsITimerCallback)
+
+using namespace mozilla;
+
+///////////////////////////////////////////////////////////////////////////////
+// nsITimerCallback
+
+NS_IMETHODIMP
+nsSuiteProfileMigratorBase::Notify(nsITimer *timer) {
+ CopyNextFolder();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSuiteProfileMigratorBase::GetName(nsACString& aName) {
+ aName.AssignLiteral("nsSuiteProfileMigratorBase");
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsSuiteProfileMigratorBase
+
+nsSuiteProfileMigratorBase::nsSuiteProfileMigratorBase() {
+ mFileCopyTransactionIndex = 0;
+ mObserverService = do_GetService("@mozilla.org/observer-service;1");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsISuiteProfileMigrator methods
+
+NS_IMETHODIMP
+nsSuiteProfileMigratorBase::GetSourceExists(bool* aResult) {
+ nsCOMPtr<nsIArray> profiles;
+ GetSourceProfiles(getter_AddRefs(profiles));
+
+ if (profiles) {
+ uint32_t count;
+ profiles->GetLength(&count);
+ *aResult = count > 0;
+ }
+ else
+ *aResult = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSuiteProfileMigratorBase::GetSourceHasMultipleProfiles(bool* aResult) {
+ nsCOMPtr<nsIArray> profiles;
+ GetSourceProfiles(getter_AddRefs(profiles));
+
+ if (profiles) {
+ uint32_t count;
+ profiles->GetLength(&count);
+ *aResult = count > 1;
+ }
+ else
+ *aResult = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSuiteProfileMigratorBase::GetSourceProfiles(nsIArray** aResult) {
+ if (!mProfileNames && !mProfileLocations) {
+ nsresult rv;
+ mProfileNames = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mProfileLocations = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Fills mProfileNames and mProfileLocations
+ FillProfileDataFromRegistry();
+ }
+
+ NS_IF_ADDREF(*aResult = mProfileNames);
+ return NS_OK;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Pref Transform methods
+
+#define GETPREF(xform, method, value) \
+ xform->prefHasValue = NS_SUCCEEDED(aBranch->method(xform->sourcePrefName, value)); \
+ return NS_OK;
+
+#define SETPREF(xform, method, value) \
+ if (xform->prefHasValue) { \
+ return aBranch->method(xform->targetPrefName ? \
+ xform->targetPrefName : \
+ xform->sourcePrefName, value); \
+ } \
+ return NS_OK;
+
+nsresult
+nsSuiteProfileMigratorBase::GetString(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ PrefTransform* xform = (PrefTransform*)aTransform;
+ nsCString str;
+ nsresult rv = aBranch->GetCharPref(xform->sourcePrefName, str);
+ if (NS_SUCCEEDED(rv)) {
+ xform->prefHasValue = true;
+ xform->stringValue = moz_xstrdup(str.get());
+ }
+ return rv;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::SetString(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ PrefTransform* xform = (PrefTransform*) aTransform;
+ SETPREF(aTransform, SetCharPref,
+ nsDependentCString(xform->stringValue));
+}
+
+nsresult
+nsSuiteProfileMigratorBase::GetBool(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ GETPREF(aTransform, GetBoolPref, &aTransform->boolValue)
+}
+
+nsresult
+nsSuiteProfileMigratorBase::SetBool(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ SETPREF(aTransform, SetBoolPref, aTransform->boolValue)
+}
+
+nsresult
+nsSuiteProfileMigratorBase::GetInt(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ GETPREF(aTransform, GetIntPref, &aTransform->intValue)
+}
+
+nsresult
+nsSuiteProfileMigratorBase::SetInt(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ SETPREF(aTransform, SetIntPref, aTransform->intValue)
+}
+
+nsresult
+nsSuiteProfileMigratorBase::SetFile(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ // In this case targetPrefName is just an additional preference
+ // that needs to be modified and not what the sourcePrefName is
+ // going to be saved to once it is modified.
+ nsresult rv = NS_OK;
+ if (aTransform->prefHasValue) {
+ nsCOMPtr<nsIProtocolHandler> handler;
+ nsCOMPtr<nsIIOService> ioService(do_GetIOService());
+ if (!ioService)
+ return NS_OK;
+ rv = ioService->GetProtocolHandler("file", getter_AddRefs(handler));
+ if (NS_FAILED(rv))
+ return NS_OK;
+ nsCOMPtr<nsIFileProtocolHandler> fileHandler(do_QueryInterface(handler));
+ if (!fileHandler)
+ return NS_OK;
+
+ nsCString fileURL(aTransform->stringValue);
+ nsCOMPtr<nsIFile> file;
+ // Start off by assuming fileURL is a URL spec and
+ // try and get a File from it.
+ rv = fileHandler->GetFileFromURLSpec(fileURL, getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ // Okay it wasn't a URL spec so assume it is a localfile,
+ // if this fails then just don't set anything.
+ rv = NS_NewNativeLocalFile(fileURL, false, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return NS_OK;
+ }
+ // Now test to see if File exists and is an actual file.
+ bool exists = false;
+ rv = file->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists)
+ rv = file->IsFile(&exists);
+
+ if (NS_SUCCEEDED(rv) && exists) {
+ // After all that let's just get the URL spec and set the pref to it.
+ rv = fileHandler->GetURLSpecFromFile(file, fileURL);
+ if (NS_FAILED(rv))
+ return NS_OK;
+ rv = aBranch->SetCharPref(aTransform->sourcePrefName, fileURL);
+ if (NS_SUCCEEDED(rv) && aTransform->targetPrefName)
+ rv = aBranch->SetIntPref(aTransform->targetPrefName, 1);
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::SetImage(PrefTransform* aTransform,
+ nsIPrefBranch* aBranch) {
+ if (aTransform->prefHasValue)
+ // This transforms network.image.imageBehavior into
+ // permissions.default.image
+ return aBranch->SetIntPref("permissions.default.image",
+ aTransform->intValue == 1 ? 3 :
+ aTransform->intValue == 2 ? 2 : 1);
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// General Utility Methods
+
+nsresult
+nsSuiteProfileMigratorBase::GetSourceProfile(const char16_t* aProfile) {
+ uint32_t count;
+ mProfileNames->GetLength(&count);
+ for (uint32_t i = 0; i < count; ++i) {
+ nsCOMPtr<nsISupportsString> str(do_QueryElementAt(mProfileNames, i));
+ nsString profileName;
+ str->GetData(profileName);
+ if (profileName.Equals(aProfile))
+ {
+ mSourceProfile = do_QueryElementAt(mProfileLocations, i);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::GetProfileDataFromProfilesIni(nsIFile* aDataDir,
+ nsIMutableArray* aProfileNames,
+ nsIMutableArray* aProfileLocations) {
+ nsresult rv;
+ nsCOMPtr<nsIFile> profileIni;
+ rv = aDataDir->Clone(getter_AddRefs(profileIni));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ profileIni->Append(u"profiles.ini"_ns);
+
+ // Does it exist?
+ bool profileFileExists = false;
+ rv = profileIni->Exists(&profileFileExists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!profileFileExists)
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ nsINIParser parser;
+ rv = parser.Init(profileIni);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString buffer, filePath;
+ bool isRelative;
+
+ unsigned int c = 0;
+ for (c = 0; true; ++c) {
+ nsAutoCString profileID("Profile");
+ profileID.AppendInt(c);
+
+ rv = parser.GetString(profileID.get(), "IsRelative", buffer);
+ if (NS_FAILED(rv))
+ break;
+
+ isRelative = buffer.EqualsLiteral("1");
+
+ rv = parser.GetString(profileID.get(), "Path", filePath);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Malformed profiles.ini: Path= not found");
+ continue;
+ }
+
+ rv = parser.GetString(profileID.get(), "Name", buffer);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Malformed profiles.ini: Name= not found");
+ continue;
+ }
+
+ nsCOMPtr<nsIFile> rootDir;
+ rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(rootDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isRelative)
+ rv = rootDir->SetRelativeDescriptor(aDataDir, filePath);
+ else
+ rv = rootDir->SetPersistentDescriptor(filePath);
+
+ if (NS_FAILED(rv)) continue;
+
+ bool exists;
+ rootDir->Exists(&exists);
+
+ if (exists) {
+ aProfileLocations->AppendElement(rootDir);
+
+ nsCOMPtr<nsISupportsString> profileNameString(
+ do_CreateInstance("@mozilla.org/supports-string;1"));
+
+ profileNameString->SetData(NS_ConvertUTF8toUTF16(buffer));
+ aProfileNames->AppendElement(profileNameString);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::CopyFile(const char* aSourceFileName,
+ const char* aTargetFileName) {
+ nsCOMPtr<nsIFile> sourceFile;
+ mSourceProfile->Clone(getter_AddRefs(sourceFile));
+
+ sourceFile->AppendNative(nsDependentCString(aSourceFileName));
+ bool exists = false;
+ sourceFile->Exists(&exists);
+ if (!exists)
+ return NS_OK;
+
+ nsCOMPtr<nsIFile> targetFile;
+ mTargetProfile->Clone(getter_AddRefs(targetFile));
+
+ targetFile->AppendNative(nsDependentCString(aTargetFileName));
+ targetFile->Exists(&exists);
+ if (exists)
+ targetFile->Remove(false);
+
+ return sourceFile->CopyToNative(mTargetProfile,
+ nsDependentCString(aTargetFileName));
+}
+
+// helper function, copies the contents of srcDir into destDir.
+// destDir will be created if it doesn't exist.
+nsresult
+nsSuiteProfileMigratorBase::RecursiveCopy(nsIFile* srcDir,
+ nsIFile* destDir) {
+ bool exists;
+ nsresult rv = srcDir->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!exists)
+ // We do not want to fail if the source folder does not exist because then
+ // parts of the migration process following this would not get executed
+ return NS_OK;
+
+ bool isDir;
+
+ rv = srcDir->IsDirectory(&isDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!isDir)
+ return NS_ERROR_INVALID_ARG;
+
+ rv = destDir->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = destDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDirectoryEnumerator> dirIterator;
+ rv = srcDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore = false;
+ while (NS_SUCCEEDED(dirIterator->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsIFile> dirEntry;
+ rv = dirIterator->GetNextFile(getter_AddRefs(dirEntry));
+ if (NS_SUCCEEDED(rv) && dirEntry) {
+ rv = dirEntry->IsDirectory(&isDir);
+ if (NS_SUCCEEDED(rv)) {
+ if (isDir) {
+ nsCOMPtr<nsIFile> newChild;
+ rv = destDir->Clone(getter_AddRefs(newChild));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString leafName;
+ dirEntry->GetLeafName(leafName);
+
+ newChild->AppendRelativePath(leafName);
+ rv = newChild->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = newChild->Create(nsIFile::DIRECTORY_TYPE, 0775);
+
+ rv = RecursiveCopy(dirEntry, newChild);
+ }
+ }
+ else {
+ // we aren't going to do any actual file copying here. Instead,
+ // add this to our file transaction list so we can copy files
+ // asynchronously...
+ fileTransactionEntry fileEntry;
+
+ fileEntry.srcFile = dirEntry;
+ fileEntry.destFile = destDir;
+
+ mFileCopyTransactions.AppendElement(fileEntry);
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+void
+nsSuiteProfileMigratorBase::ReadBranch(const char * branchName,
+ nsIPrefService* aPrefService,
+ PBStructArray &aPrefs) {
+ // Enumerate the branch
+ nsCOMPtr<nsIPrefBranch> branch;
+ aPrefService->GetBranch(branchName, getter_AddRefs(branch));
+
+ nsTArray<nsCString> prefs;
+
+ nsresult rv = branch->GetChildList("", prefs);
+ if (NS_FAILED(rv))
+ return;
+
+ for (auto& pref : prefs) {
+ // Save each pref's value into an array
+ char* currPref = moz_xstrdup(pref.get());
+ int32_t type;
+ branch->GetPrefType(currPref, &type);
+
+ PrefBranchStruct* prefBranch = new PrefBranchStruct;
+ if (!prefBranch) {
+ NS_WARNING("Could not create new PrefBranchStruct");
+ free(currPref);
+ return;
+ }
+ prefBranch->prefName = currPref;
+ prefBranch->type = type;
+
+ switch (type) {
+ case nsIPrefBranch::PREF_STRING: {
+ nsCString str;
+ rv = branch->GetCharPref(currPref, str);
+ prefBranch->stringValue = moz_xstrdup(str.get());
+ break;
+ }
+ case nsIPrefBranch::PREF_BOOL:
+ rv = branch->GetBoolPref(currPref, &prefBranch->boolValue);
+ break;
+ case nsIPrefBranch::PREF_INT:
+ rv = branch->GetIntPref(currPref, &prefBranch->intValue);
+ break;
+ default:
+ NS_WARNING("Invalid prefBranch Type in "
+ "nsSuiteProfileMigratorBase::ReadBranch\n");
+ break;
+ }
+
+ if (NS_SUCCEEDED(rv))
+ aPrefs.AppendElement(prefBranch);
+ }
+}
+
+void
+nsSuiteProfileMigratorBase::WriteBranch(const char * branchName,
+ nsIPrefService* aPrefService,
+ PBStructArray &aPrefs) {
+ // Enumerate the branch
+ nsCOMPtr<nsIPrefBranch> branch;
+ aPrefService->GetBranch(branchName, getter_AddRefs(branch));
+
+ uint32_t count = aPrefs.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ PrefBranchStruct* pref = aPrefs.ElementAt(i);
+
+ switch (pref->type) {
+ case nsIPrefBranch::PREF_STRING: {
+ branch->SetCharPref(pref->prefName,
+ nsDependentCString(pref->stringValue));
+ free(pref->stringValue);
+ pref->stringValue = nullptr;
+ break;
+ }
+ case nsIPrefBranch::PREF_BOOL:
+ branch->SetBoolPref(pref->prefName, pref->boolValue);
+ break;
+ case nsIPrefBranch::PREF_INT:
+ branch->SetIntPref(pref->prefName, pref->intValue);
+ break;
+ default:
+ NS_WARNING("Invalid Pref Type in "
+ "nsSuiteProfileMigratorBase::WriteBranch\n");
+ break;
+ }
+ free(pref->prefName);
+ pref->prefName = nullptr;
+ delete pref;
+ pref = nullptr;
+ }
+ aPrefs.Clear();
+}
+
+nsresult
+nsSuiteProfileMigratorBase::GetFileValue(nsIPrefBranch* aPrefBranch,
+ const char* aRelPrefName,
+ const char* aPrefName,
+ nsIFile** aReturnFile) {
+ nsCString prefValue;
+ nsCOMPtr<nsIFile> theFile;
+ nsresult rv = aPrefBranch->GetCharPref(aRelPrefName, prefValue);
+ if (NS_SUCCEEDED(rv)) {
+ // The pref has the format: [ProfD]a/b/c
+ if (!StringBeginsWith(prefValue, "[ProfD]"_ns))
+ return NS_ERROR_FILE_NOT_FOUND;
+
+ rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(theFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = theFile->SetRelativeDescriptor(mSourceProfile, Substring(prefValue, 7));
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ rv = aPrefBranch->GetComplexValue(aPrefName,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(theFile));
+ }
+
+ theFile.forget(aReturnFile);
+ return rv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Mail Import Functions
+
+nsresult
+nsSuiteProfileMigratorBase::CopyAddressBookDirectories(PBStructArray &aLdapServers,
+ nsIPrefService* aPrefService) {
+ // each server has a pref ending with .filename. The value of that pref
+ // points to a profile which we need to migrate.
+ nsAutoString index;
+ index.AppendInt(nsISuiteProfileMigrator::ADDRESSBOOK_DATA);
+ NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
+
+ uint32_t count = aLdapServers.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ PrefBranchStruct* pref = aLdapServers.ElementAt(i);
+ nsDependentCString prefName(pref->prefName);
+
+ if (StringEndsWith(prefName, ".filename"_ns)) {
+ CopyFile(pref->stringValue, pref->stringValue);
+ }
+
+ // we don't need to do anything to the fileName pref itself
+ }
+
+ NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get());
+
+ return NS_OK;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::CopySignatureFiles(PBStructArray &aIdentities,
+ nsIPrefService* aPrefService) {
+ nsresult rv = NS_OK;
+
+ uint32_t count = aIdentities.Length();
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ PrefBranchStruct* pref = aIdentities.ElementAt(i);
+ nsDependentCString prefName(pref->prefName);
+
+ // a partial fix for bug #255043
+ // if the user's signature file from seamonkey lives in the
+ // old profile root, we'll copy it over to the new profile root and
+ // then set the pref to the new value. Note, this doesn't work for
+ // multiple signatures that live below the seamonkey profile root
+ if (StringEndsWith(prefName, ".sig_file"_ns))
+ {
+ // turn the pref into a nsIFile
+ nsCOMPtr<nsIFile> srcSigFile =
+ do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ rv = srcSigFile->SetPersistentDescriptor(nsDependentCString(pref->stringValue));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> targetSigFile;
+ rv = mTargetProfile->Clone(getter_AddRefs(targetSigFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // now make the copy
+ bool exists;
+ srcSigFile->Exists(&exists);
+ if (exists)
+ {
+ nsAutoString leafName;
+ srcSigFile->GetLeafName(leafName);
+ // will fail if we've already copied a sig file here
+ srcSigFile->CopyTo(targetSigFile, leafName);
+ targetSigFile->Append(leafName);
+
+ // now write out the new descriptor
+ nsAutoCString descriptorString;
+ rv = targetSigFile->GetPersistentDescriptor(descriptorString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ free(pref->stringValue);
+ pref->stringValue = ToNewCString(descriptorString);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::CopyJunkTraining(bool aReplace)
+{
+ return aReplace ? CopyFile(FILE_NAME_JUNKTRAINING,
+ FILE_NAME_JUNKTRAINING) : NS_OK;
+}
+
+nsresult
+nsSuiteProfileMigratorBase::CopyMailFolderPrefs(PBStructArray &aMailServers,
+ nsIPrefService* aPrefService) {
+ // Each server has a .directory pref which points to the location of the
+ // mail data for that server. We need to do two things for that case...
+ // (1) Fix up the directory path for the new profile
+ // (2) copy the mail folder data from the source directory pref to the
+ // destination directory pref
+ CopyFile(FILE_NAME_VIRTUALFOLDERS, FILE_NAME_VIRTUALFOLDERS);
+
+ int32_t count = aMailServers.Length();
+ for (int32_t i = 0; i < count; ++i) {
+ PrefBranchStruct* pref = aMailServers.ElementAt(i);
+ nsDependentCString prefName(pref->prefName);
+
+ if (StringEndsWith(prefName, ".directory"_ns)) {
+ // let's try to get a branch for this particular server to simplify things
+ prefName.Cut(prefName.Length() - strlen("directory"),
+ strlen("directory"));
+ prefName.Insert("mail.server.", 0);
+
+ nsCOMPtr<nsIPrefBranch> serverBranch;
+ aPrefService->GetBranch(prefName.get(), getter_AddRefs(serverBranch));
+
+ if (!serverBranch)
+ break; // should we clear out this server pref from aMailServers?
+
+ nsCString serverType;
+ serverBranch->GetCharPref("type", serverType);
+
+ nsCOMPtr<nsIFile> sourceMailFolder;
+ nsresult rv = GetFileValue(serverBranch, "directory-rel", "directory",
+ getter_AddRefs(sourceMailFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // now based on type, we need to build a new destination path for the
+ // mail folders for this server
+ nsCOMPtr<nsIFile> targetMailFolder;
+ if (serverType.Equals("imap")) {
+ mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
+ targetMailFolder->Append(IMAP_MAIL_DIR_50_NAME);
+ }
+ else if (serverType.Equals("none") || serverType.Equals("pop3")) {
+ // local folders and POP3 servers go under <profile>\Mail
+ mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
+ targetMailFolder->Append(MAIL_DIR_50_NAME);
+ }
+ else if (serverType.Equals("nntp")) {
+ mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
+ targetMailFolder->Append(NEWS_DIR_50_NAME);
+ }
+
+ if (targetMailFolder) {
+ // for all of our server types, append the host name to the directory
+ // as part of the new location
+ nsCString hostName;
+ serverBranch->GetCharPref("hostname", hostName);
+ targetMailFolder->Append(NS_ConvertASCIItoUTF16(hostName));
+
+ // we should make sure the host name based directory we are going to
+ // migrate the accounts into is unique. This protects against the
+ // case where the user has multiple servers with the same host name.
+ rv = targetMailFolder->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RecursiveCopy(sourceMailFolder, targetMailFolder);
+ // now we want to make sure the actual directory pref that gets
+ // transformed into the new profile's pref.js has the right file
+ // location.
+ nsAutoCString descriptorString;
+ rv = targetMailFolder->GetPersistentDescriptor(descriptorString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ free(pref->stringValue);
+ pref->stringValue = ToNewCString(descriptorString);
+ }
+ }
+ else if (StringEndsWith(prefName, ".newsrc.file"_ns)) {
+ // copy the news RC file into \News. this won't work if the user has
+ // different newsrc files for each account I don't know what to do in
+ // that situation.
+ nsCOMPtr<nsIFile> targetNewsRCFile;
+ mTargetProfile->Clone(getter_AddRefs(targetNewsRCFile));
+ targetNewsRCFile->Append(NEWS_DIR_50_NAME);
+
+ // turn the pref into a nsIFile
+ nsCOMPtr<nsIFile> srcNewsRCFile =
+ do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ nsresult rv = srcNewsRCFile->SetPersistentDescriptor(
+ nsDependentCString(pref->stringValue));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // now make the copy
+ bool exists;
+ srcNewsRCFile->Exists(&exists);
+ if (exists) {
+ nsAutoString leafName;
+ srcNewsRCFile->GetLeafName(leafName);
+ // will fail if we've already copied a newsrc file here
+ srcNewsRCFile->CopyTo(targetNewsRCFile,leafName);
+ targetNewsRCFile->Append(leafName);
+
+ // now write out the new descriptor
+ nsAutoCString descriptorString;
+ rv = targetNewsRCFile->GetPersistentDescriptor(descriptorString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ free(pref->stringValue);
+ pref->stringValue = ToNewCString(descriptorString);
+ }
+ }
+ }
+
+ // Remove all .directory-rel prefs as those might have changed; MailNews
+ // will create those prefs again on first use
+ for (int32_t i = count; i-- > 0; ) {
+ PrefBranchStruct* pref = aMailServers.ElementAt(i);
+ nsDependentCString prefName(pref->prefName);
+
+ if (StringEndsWith(prefName, ".directory-rel"_ns)) {
+ if (pref->type == nsIPrefBranch::PREF_STRING)
+ free(pref->stringValue);
+
+ aMailServers.RemoveElementAt(i);
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsSuiteProfileMigratorBase::CopyMailFolders() {
+ nsAutoString index;
+ index.AppendInt(nsISuiteProfileMigrator::MAILDATA);
+ NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
+
+ // Generate the max progress value now that we know all of the files we
+ // need to copy
+ uint32_t count = mFileCopyTransactions.Length();
+ mMaxProgress = 0;
+ mCurrentProgress = 0;
+
+ for (uint32_t i = 0; i < count; ++i) {
+ fileTransactionEntry fileTransaction = mFileCopyTransactions[i];
+ int64_t fileSize;
+ fileTransaction.srcFile->GetFileSize(&fileSize);
+ mMaxProgress += fileSize;
+ }
+
+ CopyNextFolder();
+}
+
+void
+nsSuiteProfileMigratorBase::CopyNextFolder() {
+ if (mFileCopyTransactionIndex < mFileCopyTransactions.Length()) {
+ fileTransactionEntry fileTransaction =
+ mFileCopyTransactions.ElementAt(mFileCopyTransactionIndex++);
+
+ // copy the file
+ fileTransaction.srcFile->CopyTo(fileTransaction.destFile,
+ EmptyString());
+
+ // add to our current progress
+ int64_t fileSize;
+ fileTransaction.srcFile->GetFileSize(&fileSize);
+ mCurrentProgress += fileSize;
+
+ uint32_t percentage = (uint32_t)(mCurrentProgress * 100 / mMaxProgress);
+
+ nsAutoString index;
+ index.AppendInt(percentage);
+
+ NOTIFY_OBSERVERS(MIGRATION_PROGRESS, index.get());
+
+ if (mFileCopyTransactionIndex == mFileCopyTransactions.Length())
+ {
+ EndCopyFolders();
+ return;
+ }
+
+ // fire a timer to handle the next one.
+ mFileIOTimer = do_CreateInstance("@mozilla.org/timer;1");
+
+ if (mFileIOTimer)
+ mFileIOTimer->InitWithCallback(static_cast<nsITimerCallback *>(this),
+ 1, nsITimer::TYPE_ONE_SHOT);
+ }
+ else
+ EndCopyFolders();
+
+ return;
+}
+
+void
+nsSuiteProfileMigratorBase::EndCopyFolders() {
+ mFileCopyTransactions.Clear();
+ mFileCopyTransactionIndex = 0;
+
+ // notify the UI that we are done with the migration process
+ nsAutoString index;
+ index.AppendInt(nsISuiteProfileMigrator::MAILDATA);
+ NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get());
+
+ NOTIFY_OBSERVERS(MIGRATION_ENDED, nullptr);
+}
diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h
new file mode 100644
index 0000000000..6a4d40e3df
--- /dev/null
+++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorBase.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSuiteProfileMigratorBase___h___
+#define nsSuiteProfileMigratorBase___h___
+
+#include "nsAttrValue.h"
+#include "nsIFile.h"
+#include "nsIMutableArray.h"
+#include "nsTArray.h"
+#include "nsITimer.h"
+#include "nsIObserverService.h"
+#include "nsISuiteProfileMigrator.h"
+#include "nsSuiteMigrationCID.h"
+
+class nsIPrefBranch;
+class nsIPrefService;
+
+struct fileTransactionEntry {
+ nsCOMPtr<nsIFile> srcFile; // the src path including leaf name
+ nsCOMPtr<nsIFile> destFile; // the destination path
+ nsString newName; // only valid if the file should be renamed after
+ // getting copied
+};
+
+#define FILE_NAME_PREFS "prefs.js"
+#define FILE_NAME_JUNKTRAINING "training.dat"
+#define FILE_NAME_VIRTUALFOLDERS "virtualFolders.dat"
+
+#define F(a) nsSuiteProfileMigratorBase::a
+
+#define MAKEPREFTRANSFORM(pref, newpref, getmethod, setmethod) \
+ { pref, newpref, F(Get##getmethod), F(Set##setmethod), false, { -1 } }
+
+#define MAKESAMETYPEPREFTRANSFORM(pref, method) \
+ { pref, 0, F(Get##method), F(Set##method), false, { -1 } }
+
+class nsSuiteProfileMigratorBase : public nsISuiteProfileMigrator,
+ public nsITimerCallback,
+ public nsINamed
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ nsSuiteProfileMigratorBase();
+
+ struct PrefTransform;
+ typedef nsresult(*prefConverter)(PrefTransform*, nsIPrefBranch*);
+
+ struct PrefTransform {
+ const char* sourcePrefName;
+ const char* targetPrefName;
+ prefConverter prefGetterFunc;
+ prefConverter prefSetterFunc;
+ bool prefHasValue;
+ union {
+ int32_t intValue;
+ bool boolValue;
+ char* stringValue;
+ };
+ };
+
+ struct PrefBranchStruct {
+ char* prefName;
+ int32_t type;
+ union {
+ char* stringValue;
+ int32_t intValue;
+ bool boolValue;
+ };
+ };
+
+ typedef nsTArray<PrefBranchStruct*> PBStructArray;
+
+ // nsISuiteProfileMigrator methods
+ NS_IMETHOD GetSourceExists(bool* aSourceExists) override;
+ NS_IMETHOD GetSourceHasMultipleProfiles(bool* aSourceHasMultipleProfiles) override;
+ NS_IMETHOD GetSourceProfiles(nsIArray** aResult) override;
+
+ // Pref Transform Methods
+ static nsresult GetString(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult SetString(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult GetBool(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult SetBool(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult GetInt(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult SetInt(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult SetFile(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult SetImage(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+ static nsresult SetCookie(PrefTransform* aTransform, nsIPrefBranch* aBranch);
+
+protected:
+ virtual ~nsSuiteProfileMigratorBase() {}
+ // This function is designed to be overriden by derived classes so that
+ // the required profile data for the specific application can be obtained.
+ virtual nsresult FillProfileDataFromRegistry() = 0;
+
+ // General Utility Methods
+ nsresult GetSourceProfile(const char16_t* aProfile);
+ nsresult GetProfileDataFromProfilesIni(nsIFile* aDataDir,
+ nsIMutableArray* aProfileNames,
+ nsIMutableArray* aProfileLocations);
+ nsresult GetFileValue(nsIPrefBranch* aPrefBranch, const char* aRelPrefName,
+ const char* aPrefName, nsIFile** aReturnFile);
+ nsresult CopyFile(const char* aSourceFileName,
+ const char* aTargetFileName);
+ nsresult RecursiveCopy(nsIFile* srcDir, nsIFile* destDir);
+ void ReadBranch(const char * branchName, nsIPrefService* aPrefService,
+ PBStructArray &aPrefs);
+ void WriteBranch(const char * branchName, nsIPrefService* aPrefService,
+ PBStructArray &aPrefs);
+
+ // Mail Import Functions
+ nsresult CopyAddressBookDirectories(PBStructArray &aLdapServers,
+ nsIPrefService* aPrefService);
+ nsresult CopySignatureFiles(PBStructArray &aIdentities,
+ nsIPrefService* aPrefService);
+ nsresult CopyJunkTraining(bool aReplace);
+ nsresult CopyMailFolderPrefs(PBStructArray &aMailServers,
+ nsIPrefService* aPrefService);
+ void CopyMailFolders();
+ void CopyNextFolder();
+ void EndCopyFolders();
+
+ // Source & Target profiles
+ nsCOMPtr<nsIFile> mSourceProfile;
+ nsCOMPtr<nsIFile> mTargetProfile;
+
+ // list of src/destination files we still have to copy into the new profile
+ // directory
+ nsTArray<fileTransactionEntry> mFileCopyTransactions;
+ uint32_t mFileCopyTransactionIndex;
+
+ nsCOMPtr<nsIObserverService> mObserverService;
+ int64_t mMaxProgress;
+ int64_t mCurrentProgress;
+
+ nsCOMPtr<nsIMutableArray> mProfileNames;
+ nsCOMPtr<nsIMutableArray> mProfileLocations;
+
+ nsCOMPtr<nsITimer> mFileIOTimer;
+};
+
+#endif
diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp
new file mode 100644
index 0000000000..091a96540d
--- /dev/null
+++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSuiteProfileMigratorUtils.h"
+#include "nsIPrefBranch.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsIProfileMigrator.h"
+
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIStringBundle.h"
+#include "nsCRT.h"
+
+#define MIGRATION_BUNDLE "chrome://communicator/migration/locale/migration.properties"
+
+using namespace mozilla;
+
+void GetMigrateDataFromArray(MigrationData* aDataArray,
+ int32_t aDataArrayLength,
+ bool aReplace, nsIFile* aSourceProfile,
+ uint16_t* aResult)
+{
+ nsCOMPtr<nsIFile> sourceFile;
+ bool exists;
+ MigrationData* cursor;
+ MigrationData* end = aDataArray + aDataArrayLength;
+ for (cursor = aDataArray; cursor < end; ++cursor) {
+ // When in replace mode, all items can be imported.
+ // When in non-replace mode, only items that do not require file
+ // replacement can be imported.
+ if (aReplace || !cursor->replaceOnly) {
+ aSourceProfile->Clone(getter_AddRefs(sourceFile));
+ sourceFile->AppendNative(nsDependentCString(cursor->fileName));
+ sourceFile->Exists(&exists);
+ if (exists)
+ *aResult |= cursor->sourceFlag;
+ }
+ }
+}
+
+void
+GetProfilePath(nsIProfileStartup* aStartup, nsIFile** aProfileDir)
+{
+ *aProfileDir = nullptr;
+
+ if (aStartup) {
+ aStartup->GetDirectory(aProfileDir);
+ }
+ else {
+ nsCOMPtr<nsIProperties> dirSvc
+ (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+ if (dirSvc) {
+ dirSvc->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
+ (void**)aProfileDir);
+ }
+ }
+}
diff --git a/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h
new file mode 100644
index 0000000000..e469f5723d
--- /dev/null
+++ b/comm/suite/components/migration/src/nsSuiteProfileMigratorUtils.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SuiteProfileMigratorUtils_h__
+#define SuiteProfileMigratorUtils_h__
+
+#define MIGRATION_ITEMBEFOREMIGRATE "Migration:ItemBeforeMigrate"
+#define MIGRATION_ITEMAFTERMIGRATE "Migration:ItemAfterMigrate"
+#define MIGRATION_STARTED "Migration:Started"
+#define MIGRATION_ENDED "Migration:Ended"
+#define MIGRATION_PROGRESS "Migration:Progress"
+
+#define NOTIFY_OBSERVERS(message, item) \
+ mObserverService->NotifyObservers(nullptr, message, item)
+
+#define COPY_DATA(func, replace, itemIndex) \
+ if (NS_SUCCEEDED(rv) && (aItems & itemIndex || !aItems)) { \
+ nsAutoString index; \
+ index.AppendInt(itemIndex); \
+ NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); \
+ rv = func(replace); \
+ NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); \
+ }
+
+#define MAKEPREFTRANSFORM(pref, newpref, getmethod, setmethod) \
+ { pref, newpref, F(Get##getmethod), F(Set##setmethod), false, { -1 } }
+
+#define MAKESAMETYPEPREFTRANSFORM(pref, method) \
+ { pref, 0, F(Get##method), F(Set##method), false, { -1 } }
+
+#include "nsString.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+class nsIPrefBranch;
+class nsIProfileStartup;
+class nsIFile;
+
+struct MigrationData {
+ const char* fileName;
+ uint32_t sourceFlag;
+ bool replaceOnly;
+};
+
+class nsIFile;
+void GetMigrateDataFromArray(MigrationData* aDataArray,
+ int32_t aDataArrayLength,
+ bool aReplace,
+ nsIFile* aSourceProfile,
+ uint16_t* aResult);
+
+
+// get the base directory of the *target* profile
+// this is already cloned, modify it to your heart's content
+void GetProfilePath(nsIProfileStartup* aStartup,
+ nsIFile** aProfileDir);
+
+#endif
diff --git a/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp
new file mode 100644
index 0000000000..3f5365f12a
--- /dev/null
+++ b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.cpp
@@ -0,0 +1,571 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSuiteProfileMigratorUtils.h"
+#include "mozilla/ArrayUtils.h"
+#include "nsCRT.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIObserverService.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsIPrefService.h"
+#include "nsIServiceManager.h"
+#include "nsISupportsPrimitives.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThunderbirdProfileMigrator.h"
+#include "prprf.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// nsThunderbirdProfileMigrator
+
+#define FILE_NAME_SITEPERM_OLD "cookperm.txt"
+#define FILE_NAME_SITEPERM_NEW "hostperm.1"
+#define FILE_NAME_CERT8DB "cert8.db"
+#define FILE_NAME_KEY3DB "key3.db"
+#define FILE_NAME_SECMODDB "secmod.db"
+#define FILE_NAME_HISTORY "history.dat"
+#define FILE_NAME_SIGNONS "signons.sqlite"
+#define FILE_NAME_MIMETYPES "mimeTypes.rdf"
+#define FILE_NAME_USER_PREFS "user.js"
+#define FILE_NAME_PERSONALDICTIONARY "persdict.dat"
+#define FILE_NAME_MAILVIEWS "mailViews.dat"
+
+NS_IMPL_ISUPPORTS(nsThunderbirdProfileMigrator, nsISuiteProfileMigrator,
+ nsITimerCallback)
+
+nsThunderbirdProfileMigrator::nsThunderbirdProfileMigrator()
+{
+}
+
+nsThunderbirdProfileMigrator::~nsThunderbirdProfileMigrator()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsISuiteProfileMigrator
+
+NS_IMETHODIMP
+nsThunderbirdProfileMigrator::Migrate(uint16_t aItems,
+ nsIProfileStartup* aStartup,
+ const char16_t* aProfile)
+{
+ nsresult rv = NS_OK;
+ bool aReplace = aStartup ? true : false;
+
+ if (!mTargetProfile) {
+ GetProfilePath(aStartup, getter_AddRefs(mTargetProfile));
+ if (!mTargetProfile)
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+ if (!mSourceProfile) {
+ GetSourceProfile(aProfile);
+ if (!mSourceProfile)
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ NOTIFY_OBSERVERS(MIGRATION_STARTED, nullptr);
+
+ COPY_DATA(CopyPreferences, aReplace, nsISuiteProfileMigrator::SETTINGS);
+ COPY_DATA(CopyHistory, aReplace, nsISuiteProfileMigrator::HISTORY);
+ COPY_DATA(CopyPasswords, aReplace, nsISuiteProfileMigrator::PASSWORDS);
+
+ // fake notifications for things we've already imported as part of
+ // CopyPreferences
+ nsAutoString index;
+ index.AppendInt(nsISuiteProfileMigrator::ACCOUNT_SETTINGS);
+ NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
+ NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get());
+
+ index.Truncate();
+ index.AppendInt(nsISuiteProfileMigrator::NEWSDATA);
+ NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
+ NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get());
+
+ // copy junk mail training file
+ COPY_DATA(CopyJunkTraining, aReplace, nsISuiteProfileMigrator::JUNKTRAINING);
+
+ if (aReplace &&
+ (aItems & nsISuiteProfileMigrator::SETTINGS ||
+ aItems & nsISuiteProfileMigrator::PASSWORDS ||
+ !aItems)) {
+ // Permissions (Images)
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_SITEPERM_NEW, FILE_NAME_SITEPERM_NEW);
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_SITEPERM_OLD, FILE_NAME_SITEPERM_OLD);
+ }
+
+ // the last thing to do is to actually copy over any mail folders
+ // we have marked for copying we want to do this last and it will be
+ // asynchronous so the UI doesn't freeze up while we perform
+ // this potentially very long operation.
+ CopyMailFolders();
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsThunderbirdProfileMigrator::GetMigrateData(const char16_t* aProfile,
+ bool aReplace,
+ uint16_t* aResult)
+{
+ *aResult = 0;
+
+ if (!mSourceProfile) {
+ GetSourceProfile(aProfile);
+ if (!mSourceProfile)
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // migration fields for things we always migrate
+ *aResult =
+ nsISuiteProfileMigrator::ACCOUNT_SETTINGS |
+ nsISuiteProfileMigrator::MAILDATA |
+ nsISuiteProfileMigrator::NEWSDATA |
+ nsISuiteProfileMigrator::ADDRESSBOOK_DATA;
+
+ MigrationData data[] = { { FILE_NAME_PREFS,
+ nsISuiteProfileMigrator::SETTINGS,
+ true },
+ { FILE_NAME_USER_PREFS,
+ nsISuiteProfileMigrator::SETTINGS,
+ true },
+ { FILE_NAME_HISTORY,
+ nsISuiteProfileMigrator::HISTORY,
+ true },
+ { FILE_NAME_SIGNONS,
+ nsISuiteProfileMigrator::PASSWORDS,
+ true },
+ { FILE_NAME_JUNKTRAINING,
+ nsISuiteProfileMigrator::JUNKTRAINING,
+ true } };
+
+ GetMigrateDataFromArray(data, sizeof(data)/sizeof(MigrationData),
+ aReplace, mSourceProfile, aResult);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsThunderbirdProfileMigrator::GetSupportedItems(uint16_t *aSupportedItems)
+{
+ NS_ENSURE_ARG_POINTER(aSupportedItems);
+
+ *aSupportedItems = nsISuiteProfileMigrator::SETTINGS |
+ nsISuiteProfileMigrator::HISTORY |
+ nsISuiteProfileMigrator::JUNKTRAINING |
+ nsISuiteProfileMigrator::PASSWORDS |
+ nsISuiteProfileMigrator::ACCOUNT_SETTINGS |
+ nsISuiteProfileMigrator::MAILDATA |
+ nsISuiteProfileMigrator::NEWSDATA |
+ nsISuiteProfileMigrator::ADDRESSBOOK_DATA;
+
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsThunderbirdProfileMigrator
+
+nsresult
+nsThunderbirdProfileMigrator::FillProfileDataFromRegistry()
+{
+ // Find the Thunderbird Registry
+ nsCOMPtr<nsIProperties> fileLocator(
+ do_GetService("@mozilla.org/file/directory_service;1"));
+ nsCOMPtr<nsIFile> thunderbirdData;
+#ifdef XP_WIN
+ fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(thunderbirdData));
+
+ thunderbirdData->Append(u"Thunderbird"_ns);
+
+#elif defined(XP_MACOSX)
+ fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(thunderbirdData));
+
+ thunderbirdData->Append(u"Thunderbird"_ns);
+
+#elif defined(XP_UNIX)
+ fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(thunderbirdData));
+
+ thunderbirdData->Append(u".thunderbird"_ns);
+
+#else
+ // On other OS just abort
+ return NS_ERROR_FILE_NOT_FOUND;
+#endif
+
+ // Try profiles.ini first
+ return GetProfileDataFromProfilesIni(thunderbirdData,
+ mProfileNames,
+ mProfileLocations);
+}
+
+static
+nsThunderbirdProfileMigrator::PrefTransform gTransforms[] = {
+ MAKESAMETYPEPREFTRANSFORM("accessibility.typeaheadfind.autostart", Bool),
+ MAKESAMETYPEPREFTRANSFORM("accessibility.typeaheadfind.linksonly", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("browser.anchor_color", String),
+ MAKESAMETYPEPREFTRANSFORM("browser.active_color", String),
+ MAKESAMETYPEPREFTRANSFORM("browser.display.background_color", String),
+ MAKESAMETYPEPREFTRANSFORM("browser.display.foreground_color", String),
+ MAKESAMETYPEPREFTRANSFORM("browser.display.use_system_colors", Bool),
+ MAKESAMETYPEPREFTRANSFORM("browser.display.document_color_use", Int),
+ MAKESAMETYPEPREFTRANSFORM("browser.display.use_document_fonts", Bool),
+ MAKESAMETYPEPREFTRANSFORM("browser.enable_automatic_image_resizing", Bool),
+ MAKESAMETYPEPREFTRANSFORM("browser.tabs.autoHide", Bool),
+ MAKESAMETYPEPREFTRANSFORM("browser.tabs.loadInBackground", Bool),
+ MAKESAMETYPEPREFTRANSFORM("browser.underline_anchors", Bool),
+ MAKESAMETYPEPREFTRANSFORM("browser.visited_color", String),
+
+ MAKESAMETYPEPREFTRANSFORM("dom.disable_open_during_load", Bool),
+ MAKESAMETYPEPREFTRANSFORM("dom.disable_window_move_resize", Bool),
+ MAKESAMETYPEPREFTRANSFORM("dom.disable_window_flip", Bool),
+ MAKESAMETYPEPREFTRANSFORM("dom.disable_window_open_feature.status", Bool),
+ MAKESAMETYPEPREFTRANSFORM("dom.disable_window_status_change", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("extensions.spellcheck.inline.max-misspellings",Int),
+
+ MAKESAMETYPEPREFTRANSFORM("general.warnOnAboutConfig", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("intl.accept_charsets", String),
+ MAKESAMETYPEPREFTRANSFORM("intl.accept_languages", String),
+ MAKESAMETYPEPREFTRANSFORM("intl.charset.fallback.override", String),
+
+ MAKESAMETYPEPREFTRANSFORM("javascript.enabled", Bool),
+ MAKESAMETYPEPREFTRANSFORM("javascript.options.relimit", Bool),
+ MAKESAMETYPEPREFTRANSFORM("javascript.options.showInConsole", Bool),
+ MAKESAMETYPEPREFTRANSFORM("javascript.options.strict", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("layout.spellcheckDefault", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.accounts", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.defaultaccount", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.localfoldersserver", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.accountwizard.deferstorage", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.adaptivefilters.junk_threshold", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.autoComplete.highlightNonMatches", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.biff.animate_doc_icon", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound.type", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound.url", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.biff.show_alert", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.biff.show_tray_icon", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.check_all_imap_folders_for_new", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.citation_color", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.collect_addressbook", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_incoming", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_newsgroup", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_outgoing", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.compose.add_undisclosed_recipients", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.compose.autosave", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.compose.autosaveinterval", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.compose.dontWarnMail2Newsgroup", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.compose.other.header", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.content_disposition_type", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.default_html_action", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.default_sendlater_uri", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.delete_matches_sort_order", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.display_glyph", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.display_struct", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.enable_autocomplete", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.fcc_folder", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.file_attach_binary", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.fixed_width_messages", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.forward_message_mode", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.incorporate.return_receipt", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.inline_attachments", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.label_ascii_only_mail_as_us_ascii", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.notification.sound", String),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.pane_config.dynamic", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.password_protect_local_cache", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.phishing.detection.enabled", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.pop3.deleteFromServerOnMove", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.prompt_purge_threshhold", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.purge_threshhold", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.purge.ask", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.purge.min_delay", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.purge.timer_interval", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.quoteasblock", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.quoted_graphical", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.quoted_size", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.quoted_style", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.receipt.request_header_type", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.receipt.request_return_receipt_on", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.send_struct", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.show_headers", Int),
+ MAKESAMETYPEPREFTRANSFORM("mail.smtp.useMatchingDomainServer", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.smtp.useMatchingHostNameServer", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.smtp.defaultserver", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.smtpservers", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.spellcheck.inline", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.SpellCheckBeforeSend", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.startup.enabledMailCheckOnce", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.strict_threading", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.strictly_mime", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.strictly_mime_headers", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.strictly_mime.parm_folding", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("mail.thread_without_re", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.trusteddomains", String),
+ MAKESAMETYPEPREFTRANSFORM("mail.warn_on_send_accel_key", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.warn_filter_changed", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mail.wrap_long_lines", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.account_central_page.url", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.confirm.moveFoldersToTrash", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.customDBHeaders", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.customHeaders", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.default_sort_order", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.default_sort_type", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.default_news_sort_order", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.default_news_sort_type", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.display.disable_format_flowed_support", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.display.disallow_mime_handlers", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.display.html_as", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.display_html_sanitzer.allowed_tags", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.display.original_date", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.display.prefer_plaintext", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.force_ascii_search", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.force_charset_override", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showOrganization", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showUserAgent", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.headers.extraExpandedHeaders", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.html_domains", String),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.mark_message_read.delay", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.mark_message_read.delay.interval", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.message_display.disable_remote_image", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.nav_crosses_folders", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sync_mail", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sych_news", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sync_send_unsent", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.offline_sync_work_offline", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.open_window_warning", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.plaintext_domains", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.remember_selected_message", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.reply_in_default_charset", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.reuse_message_window", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.scroll_to_new_message", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.search_date_format", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.search_date_leading_zeros", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.search_date_separator", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.send_default_charset", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.send_plaintext_flowed", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.show_send_progress", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.start_page.enabled", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.start_page.url", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.tcptimeout", Int),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.thread_pane_column_unthreads", Bool),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.view_default_charset", String),
+ MAKESAMETYPEPREFTRANSFORM("mailnews.wraplength", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("messenger.save.dir", String),
+
+ MAKESAMETYPEPREFTRANSFORM("msgcompose.background_color", String),
+ MAKESAMETYPEPREFTRANSFORM("msgcompose.font_face", String),
+ MAKESAMETYPEPREFTRANSFORM("msgcompose.font_size", String),
+ MAKESAMETYPEPREFTRANSFORM("msgcompose.text_color", String),
+
+ MAKESAMETYPEPREFTRANSFORM("news.get_messages_on_select", Bool),
+ MAKESAMETYPEPREFTRANSFORM("news.show_size_in_lines", Bool),
+ MAKESAMETYPEPREFTRANSFORM("news.update_unread_on_expand", Bool),
+
+ // pdi is the new preference, but nii is the old one - so do nii first, and
+ // then do pdi to account for both situations
+ MAKEPREFTRANSFORM("network.image.imageBehavior", 0, Int, Image),
+ MAKESAMETYPEPREFTRANSFORM("permissions.default.image", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.autoconfig_url", String),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp", String),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp_port", Int),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.http", String),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.http_port", Int),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.no_proxies_on", String),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.socks", String),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.socks_port", Int),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl", String),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl_port", Int),
+ MAKESAMETYPEPREFTRANSFORM("network.proxy.type", Int),
+
+ MAKESAMETYPEPREFTRANSFORM("offline.autodetect", Bool),
+ MAKESAMETYPEPREFTRANSFORM("offline.download.download_messages", Int),
+ MAKESAMETYPEPREFTRANSFORM("offline.send.unsent_messages", Int),
+ MAKESAMETYPEPREFTRANSFORM("offline.startup_state", Int),
+ MAKESAMETYPEPREFTRANSFORM("security.default_personal_cert", String),
+ MAKESAMETYPEPREFTRANSFORM("security.password_lifetime", Int),
+ MAKESAMETYPEPREFTRANSFORM("security.tls.version.min", Int),
+ MAKESAMETYPEPREFTRANSFORM("security.tls.version.max", Int),
+ MAKESAMETYPEPREFTRANSFORM("security.warn_entering_secure", Bool),
+ MAKESAMETYPEPREFTRANSFORM("security.warn_leaving_secure", Bool),
+ MAKESAMETYPEPREFTRANSFORM("security.warn_submit_insecure", Bool),
+ MAKESAMETYPEPREFTRANSFORM("security.warn_viewing_mixed", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("signon.rememberSignons", Bool),
+
+ MAKESAMETYPEPREFTRANSFORM("slider.snapMultiplier", Int),
+ MAKESAMETYPEPREFTRANSFORM("startup.homepage_override_url", String),
+
+#ifdef XP_UNIX
+ MAKESAMETYPEPREFTRANSFORM("ui.allow_platform_file_picker", Bool),
+#endif
+ MAKESAMETYPEPREFTRANSFORM("ui.click_hold_context_menus", Bool)
+};
+
+nsresult
+nsThunderbirdProfileMigrator::TransformPreferences(
+ const char* aSourcePrefFileName,
+ const char* aTargetPrefFileName)
+{
+ PrefTransform* transform;
+ PrefTransform* end = gTransforms + sizeof(gTransforms)/sizeof(PrefTransform);
+
+ // Load the source pref file
+ nsCOMPtr<nsIPrefService> psvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ psvc->ResetPrefs();
+
+ nsCOMPtr<nsIFile> sourcePrefsFile;
+ mSourceProfile->Clone(getter_AddRefs(sourcePrefsFile));
+ sourcePrefsFile->AppendNative(nsDependentCString(aSourcePrefFileName));
+ psvc->ReadUserPrefsFromFile(sourcePrefsFile);
+
+ nsCOMPtr<nsIPrefBranch> branch(do_QueryInterface(psvc));
+ for (transform = gTransforms; transform < end; ++transform)
+ transform->prefGetterFunc(transform, branch);
+
+ // read in the various pref branch trees for accounts, identities, servers,
+ // etc.
+ static const char* branchNames[] =
+ {
+ // Keep the three below first, or change the indexes below
+ "mail.identity.",
+ "mail.server.",
+ "ldap_2.",
+ "accessibility.",
+ "applications.",
+ "bidi.",
+ "dom.",
+ "editor.",
+ "font.",
+ "helpers.",
+ "mail.account.",
+ "mail.addr_book.",
+ "mail.imap.",
+ "mail.mdn.",
+ "mail.smtpserver.",
+ "mail.spam.",
+ "mail.toolbars.",
+ "mailnews.labels.",
+ "mailnews.reply_",
+ "mailnews.tags.",
+ "middlemouse.",
+ "mousewheel.",
+ "network.http.",
+ "print.",
+ "privacy.",
+ "security.OSCP.",
+ "security.crl.",
+ "ui.key.",
+ "wallet."
+ };
+
+ PBStructArray branches[MOZ_ARRAY_LENGTH(branchNames)];
+ uint32_t i;
+ for (i = 0; i < MOZ_ARRAY_LENGTH(branchNames); ++i)
+ ReadBranch(branchNames[i], psvc, branches[i]);
+
+ // the signature file prefs may be paths to files in the thunderbird profile
+ // path so we need to copy them over and fix these paths up before we write
+ // them out to the new prefs.js
+ CopySignatureFiles(branches[0], psvc);
+
+ // certain mail prefs may actually be absolute paths instead of profile
+ // relative paths we need to fix these paths up before we write them out to
+ // the new prefs.js
+ CopyMailFolderPrefs(branches[1], psvc);
+
+ CopyAddressBookDirectories(branches[2], psvc);
+
+ // Now that we have all the pref data in memory, load the target pref file,
+ // and write it back out
+ psvc->ResetPrefs();
+
+ nsCOMPtr<nsIFile> targetPrefsFile;
+ mTargetProfile->Clone(getter_AddRefs(targetPrefsFile));
+ targetPrefsFile->AppendNative(nsDependentCString(aTargetPrefFileName));
+
+ // Don't use nullptr here as we're too early in the cycle for the prefs
+ // service to get its default file (because the NS_GetDirectoryService items
+ // aren't fully set up yet).
+ psvc->ReadUserPrefsFromFile(targetPrefsFile);
+
+ for (transform = gTransforms; transform < end; ++transform)
+ transform->prefSetterFunc(transform, branch);
+
+ for (i = 0; i < MOZ_ARRAY_LENGTH(branchNames); ++i)
+ WriteBranch(branchNames[i], psvc, branches[i]);
+
+ psvc->SavePrefFile(targetPrefsFile);
+
+ return NS_OK;
+}
+
+nsresult
+nsThunderbirdProfileMigrator::CopyPreferences(bool aReplace)
+{
+ nsresult rv = NS_OK;
+ if (!aReplace)
+ return rv;
+
+ if (NS_SUCCEEDED(rv))
+ rv = TransformPreferences(FILE_NAME_PREFS, FILE_NAME_PREFS);
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_USER_PREFS, FILE_NAME_USER_PREFS);
+
+ // Security Stuff
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_CERT8DB, FILE_NAME_CERT8DB);
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_KEY3DB, FILE_NAME_KEY3DB);
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_SECMODDB, FILE_NAME_SECMODDB);
+
+ // User MIME Type overrides
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_MIMETYPES, FILE_NAME_MIMETYPES);
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_PERSONALDICTIONARY, FILE_NAME_PERSONALDICTIONARY);
+ if (NS_SUCCEEDED(rv))
+ rv = CopyFile(FILE_NAME_MAILVIEWS, FILE_NAME_MAILVIEWS);
+
+ return rv;
+}
+
+nsresult
+nsThunderbirdProfileMigrator::CopyHistory(bool aReplace)
+{
+ return aReplace ? CopyFile(FILE_NAME_HISTORY, FILE_NAME_HISTORY) : NS_OK;
+}
+
+nsresult
+nsThunderbirdProfileMigrator::CopyPasswords(bool aReplace)
+{
+ return aReplace ? CopyFile(FILE_NAME_SIGNONS, FILE_NAME_SIGNONS) : NS_OK;
+}
diff --git a/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h
new file mode 100644
index 0000000000..2b3bbf87ec
--- /dev/null
+++ b/comm/suite/components/migration/src/nsThunderbirdProfileMigrator.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ThunderbirdProfileMigrator_h__
+#define ThunderbirdProfileMigrator_h__
+
+#include "nsISuiteProfileMigrator.h"
+#include "nsIFile.h"
+#include "nsIObserverService.h"
+#include "nsSuiteProfileMigratorBase.h"
+#include "nsITimer.h"
+#include "mozilla/Attributes.h"
+
+class nsIFile;
+class nsIPrefBranch;
+class nsIPrefService;
+
+class nsThunderbirdProfileMigrator final : public nsSuiteProfileMigratorBase
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ nsThunderbirdProfileMigrator();
+
+ // nsISuiteProfileMigrator methods
+ NS_IMETHOD Migrate(uint16_t aItems, nsIProfileStartup *aStartup,
+ const char16_t *aProfile) override;
+ NS_IMETHOD GetMigrateData(const char16_t *aProfile, bool aDoingStartup,
+ uint16_t *_retval) override;
+ NS_IMETHOD GetSupportedItems(uint16_t *aSupportedItems) override;
+
+protected:
+ virtual ~nsThunderbirdProfileMigrator();
+ nsresult FillProfileDataFromRegistry() override;
+ nsresult CopyPreferences(bool aReplace);
+ nsresult TransformPreferences(const char* aSourcePrefFileName,
+ const char* aTargetPrefFileName);
+ nsresult CopyHistory(bool aReplace);
+ nsresult CopyPasswords(bool aReplace);
+};
+
+#endif
diff --git a/comm/suite/components/moz.build b/comm/suite/components/moz.build
new file mode 100644
index 0000000000..2e3b677e92
--- /dev/null
+++ b/comm/suite/components/moz.build
@@ -0,0 +1,52 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ "autocomplete",
+ "bindings",
+ "console",
+ "dataman",
+ "downloads",
+ "feeds",
+ "helpviewer",
+ "migration",
+ "permissions",
+ "places",
+ "pref",
+ "profile",
+ "sanitize",
+ "search",
+ "security",
+ "sessionstore",
+ "shell",
+ "sidebar",
+]
+
+# build is always last as it adds the local includes from the other components.
+DIRS += [
+ "build",
+]
+
+BROWSER_CHROME_MANIFESTS += [
+ "tests/browser/browser.ini",
+]
+
+MOCHITEST_CHROME_MANIFESTS += [
+ "tests/chrome/chrome.ini",
+]
+
+XPIDL_SOURCES += [
+ "nsISuiteGlue.idl",
+]
+
+XPIDL_MODULE = "suite-components"
+
+EXTRA_COMPONENTS += [
+ "nsAbout.js",
+ "nsGopherProtocolStubHandler.js",
+ "nsSuiteGlue.js",
+ "SuiteComponents.manifest",
+]
diff --git a/comm/suite/components/nsAbout.js b/comm/suite/components/nsAbout.js
new file mode 100644
index 0000000000..eb93ce0dda
--- /dev/null
+++ b/comm/suite/components/nsAbout.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const SCRIPT = Ci.nsIAboutModule.ALLOW_SCRIPT;
+const UNTRUSTED = Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT;
+const HIDE = Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT;
+const INDEXEDDB = Ci.nsIAboutModule.ENABLE_INDEXED_DB;
+
+function About() { }
+About.prototype = {
+ Flags: SCRIPT,
+ URI: "chrome://communicator/content/about.xhtml",
+ blockedFlags: SCRIPT | UNTRUSTED | HIDE,
+ blockedURI: "chrome://communicator/content/blockedSite.xhtml",
+ certerrorFlags: SCRIPT | UNTRUSTED | HIDE,
+ certerrorURI: "chrome://communicator/content/certError.xhtml",
+ dataFlags: SCRIPT,
+ dataURI: "chrome://communicator/content/dataman/dataman.xul",
+ feedsFlags: SCRIPT | UNTRUSTED | HIDE,
+ feedsURI: "chrome://communicator/content/feeds/subscribe.xhtml",
+ lifeFlags: SCRIPT | UNTRUSTED | HIDE,
+ lifeURI: "chrome://communicator/content/aboutLife.xhtml",
+ newserrorFlags: SCRIPT | HIDE,
+ newserrorURI: "chrome://messenger/content/newsError.xhtml",
+ privatebrowsingFlags: SCRIPT,
+ privatebrowsingURI: "chrome://communicator/content/aboutPrivateBrowsing.xul",
+ rightsFlags: SCRIPT | UNTRUSTED,
+ rightsURI: "chrome://branding/content/aboutRights.xhtml",
+ sessionrestoreFlags: SCRIPT | HIDE,
+ sessionrestoreURI: "chrome://communicator/content/aboutSessionRestore.xhtml",
+ // synctabsFlags: SCRIPT,
+ // synctabsURI: "chrome://communicator/content/aboutSyncTabs.xul",
+
+ classID: Components.ID("{d54f2c89-8fd6-4eeb-a7a4-51d4dcdf460f}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+
+ getModule: function(aURI) {
+ return aURI.pathQueryRef.replace(/-|\W.*$/g, "").toLowerCase();
+ },
+
+ getURIFlags: function(aURI) {
+ return this[this.getModule(aURI) + "Flags"];
+ },
+
+ newChannel: function(aURI, aLoadInfo) {
+ let module = this.getModule(aURI);
+ let newURI = Services.io.newURI(this[module + "URI"]);
+
+ // We want a happy family which is always providing a loadInfo object.
+ if (!aLoadInfo) {
+ // Write out an error so that we have a stack and can fix the caller.
+ Cu.reportError('aLoadInfo was not provided in nsAbout.newChannel!');
+ }
+
+ let channel = aLoadInfo ?
+ Services.io.newChannelFromURIWithLoadInfo(newURI, aLoadInfo) :
+ Services.io.newChannelFromURI(newURI, null,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null,
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+
+ channel.originalURI = aURI;
+ if (this[module + "Flags"] & UNTRUSTED) {
+ let principal = Services.scriptSecurityManager.createCodebasePrincipal(aURI, {});
+ channel.owner = principal;
+ }
+ return channel;
+ },
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([About]);
diff --git a/comm/suite/components/nsGopherProtocolStubHandler.js b/comm/suite/components/nsGopherProtocolStubHandler.js
new file mode 100644
index 0000000000..5f4451d9e9
--- /dev/null
+++ b/comm/suite/components/nsGopherProtocolStubHandler.js
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/* This is a simple module which can be used as a template for any newly
+ unsupported protocol. In this case, it redirects gopher:// protocol
+ requests to the Mozilla Add-Ons page for OverbiteFF, which is a
+ cross-platform extension for Gopherspace. This gives a soft-landing for
+ support, which was withdrawn in Mozilla 2.0. See bugs 388195 and 572000. */
+
+function GopherProtocol()
+{
+}
+
+GopherProtocol.prototype = {
+ classDescription: "Gopher protocol handler stub",
+ classID: Components.ID("{22042bdb-56e4-47c6-8b12-fdfa859c05a9}"),
+
+ // nsISupports
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
+
+ // nsIProtocolHandler
+ scheme: "gopher",
+ defaultPort: 70,
+ protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
+ Ci.nsIProtocolHandler.URI_NOAUTH |
+ Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,
+
+ allowPort: function GP_allowPort(port, scheme) {
+ return false; // meaningless.
+ },
+
+ newURI: function GP_newURI(spec, charset, baseURI) {
+ var uri = Cc["@mozilla.org/network/standard-url;1"]
+ .createInstance(Ci.nsIStandardURL);
+ uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD,
+ this.defaultPort, spec, charset, baseURI)
+ return uri;
+ },
+
+ newChannel: function GP_newChannel(inputURI) {
+ return this.newChannel2(inputURI, null);
+ },
+
+ newChannel2: function GP_newChannel2(inputURI, loadinfo) {
+ var newURI = Services.io.newURI("chrome://communicator/content/gopherAddon.xhtml");
+ // Create a chrome channel, and de-chrome it, to our information page.
+ var chan =
+ loadinfo ? Services.io.newChannelFromURIWithLoadInfo(newURI, loadinfo) :
+ Services.io.newChannelFromURI(newURI, null,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null,
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ chan.originalURI = inputURI;
+ chan.owner = Services.scriptSecurityManager.createCodebasePrincipal(inputURI, {});
+ return chan;
+ }
+};
+
+/* Make our factory. */
+var components = [ GopherProtocol ];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/comm/suite/components/nsISuiteGlue.idl b/comm/suite/components/nsISuiteGlue.idl
new file mode 100644
index 0000000000..d2cc03aea2
--- /dev/null
+++ b/comm/suite/components/nsISuiteGlue.idl
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMWindow;
+
+/**
+ * ***** this is the suite version of nsIBrowserGlue *****
+ * nsISuiteGlue is a dirty and rather fluid interface to host shared utility
+ * methods used by suite UI code, but which are not local to a suite window.
+ * The component implementing this interface is meant to be a singleton
+ * (service) and should progressively replace some of the shared "glue" code
+ * scattered in suite/ (e.g. bits of utilOverlay.js,
+ * contentAreaUtils.js, globalOverlay.js), avoiding dynamic
+ * inclusion and initialization of a ton of JS code for *each* window.
+ * Due to its nature and origin, this interface won't probably be the most
+ * elegant or stable in the mozilla codebase, but its aim is rather pragmatic:
+ * 1) reducing the performance overhead which affects browser window load;
+ * 2) allow global hooks (e.g. startup and shutdown observers) which survive
+ * suite windows to accomplish suite-related activities, such as shutdown
+ * sanitization (see bug #284086)
+ */
+
+[scriptable, uuid(b3a787fd-4c05-4518-98e3-20bc10a0f586)]
+interface nsISuiteGlue : nsISupports
+{
+ /**
+ * Opens the Download Manager.
+ */
+ void showDownloadManager( [optional] in boolean newDownload );
+
+ /**
+ * Deletes privacy sensitive data according to user preferences
+ *
+ * @param aParentWindow an optionally null window which is the parent of the
+ * sanitization dialog (if it has to be shown per user preferences)
+ *
+ */
+ void sanitize(in nsIDOMWindow aParentWindow);
+};
diff --git a/comm/suite/components/nsSuiteGlue.js b/comm/suite/components/nsSuiteGlue.js
new file mode 100644
index 0000000000..2d0b5600f4
--- /dev/null
+++ b/comm/suite/components/nsSuiteGlue.js
@@ -0,0 +1,1676 @@
+/* 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 XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var { migrateMailnews } =
+ ChromeUtils.import("resource:///modules/mailnewsMigrator.js");
+var { ExtensionSupport } =
+ ChromeUtils.import("resource:///modules/ExtensionSupport.jsm");
+var { LightweightThemeConsumer } =
+ ChromeUtils.import("resource://gre/modules/LightweightThemeConsumer.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ AddonManager: "resource://gre/modules/AddonManager.jsm",
+ LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+ FileUtils: "resource://gre/modules/FileUtils.jsm",
+ OS: "resource://gre/modules/osfile.jsm",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+ PlacesBackups: "resource://gre/modules/PlacesBackups.jsm",
+ AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
+ AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
+ DateTimePickerHelper: "resource://gre/modules/DateTimePickerHelper.jsm",
+ BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
+ BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
+ RecentWindow: "resource:///modules/RecentWindow.jsm",
+ Sanitizer: "resource:///modules/Sanitizer.jsm",
+ ShellService: "resource:///modules/ShellService.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
+ Integration: "resource://gre/modules/Integration.jsm",
+ PermissionUI: "resource:///modules/PermissionUI.jsm",
+ AppConstants: "resource://gre/modules/AppConstants.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "DebuggerServer", () => {
+ var tmp = {};
+ ChromeUtils.import("resource://devtools/shared/Loader.jsm", tmp);
+ return tmp.require("devtools/server/main").DebuggerServer;
+});
+
+var global = this;
+
+var listeners = {
+ mm: {
+ // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN nsBrowserGlue.js
+ "RemoteLogins:findLogins": ["LoginManagerParent"],
+ "RemoteLogins:findRecipes": ["LoginManagerParent"],
+ "RemoteLogins:onFormSubmit": ["LoginManagerParent"],
+ "RemoteLogins:autoCompleteLogins": ["LoginManagerParent"],
+ "RemoteLogins:removeLogin": ["LoginManagerParent"],
+ "RemoteLogins:insecureLoginFormPresent": ["LoginManagerParent"],
+ // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN nsBrowserGlue.js
+ },
+
+ receiveMessage(modules, data) {
+ let val;
+ for (let module of modules[data.name]) {
+ try {
+ val = global[module].receiveMessage(data) || val;
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ return val;
+ },
+
+ init() {
+ let receiveMessageMM = this.receiveMessage.bind(this, this.mm);
+ for (let message of Object.keys(this.mm)) {
+ Services.mm.addMessageListener(message, receiveMessageMM);
+ }
+ }
+};
+
+// We try to backup bookmarks at idle times, to avoid doing that at shutdown.
+// Number of idle seconds before trying to backup bookmarks 8 minutes.
+const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 15 * 60;
+// Minimum interval between backups. We try to not create more than one backup
+// per interval.
+const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
+
+// Devtools Preferences
+const DEBUGGER_REMOTE_ENABLED = "devtools.debugger.remote-enabled";
+const DEBUGGER_REMOTE_PORT = "devtools.debugger.remote-port";
+const DEBUGGER_FORCE_LOCAL = "devtools.debugger.force-local";
+const DEBUGGER_WIFI_VISIBLE = "devtools.remote.wifi.visible";
+const DOWNLOAD_MANAGER_URL = "chrome://communicator/content/downloads/downloadmanager.xul";
+const PREF_FOCUS_WHEN_STARTING = "browser.download.manager.focusWhenStarting";
+const PREF_FLASH_COUNT = "browser.download.manager.flashCount";
+
+var gDownloadManager;
+
+// Constructor
+function SuiteGlue() {
+ XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
+ "@mozilla.org/widget/idleservice;1",
+ "nsIIdleService");
+
+ this._init();
+ extensionDefaults(); // extensionSupport.jsm
+}
+
+SuiteGlue.prototype = {
+ _saveSession: false,
+ _isIdleObserver: false,
+ _isPlacesDatabaseLocked: false,
+ _migrationImportsDefaultBookmarks: false,
+
+ _setPrefToSaveSession: function()
+ {
+ Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
+ },
+
+ _logConsoleAPI: function(aEvent)
+ {
+ const nsIScriptError = Ci.nsIScriptError;
+ var flg = nsIScriptError.errorFlag;
+ switch (aEvent.level) {
+ case "warn":
+ flg = nsIScriptError.warningFlag;
+ case "error":
+ var scriptError = Cc["@mozilla.org/scripterror;1"]
+ .createInstance(nsIScriptError);
+ scriptError.initWithWindowID(Array.from(aEvent.arguments),
+ aEvent.filename, "", aEvent.lineNumber, 0,
+ flg, "content javascript", aEvent.innerID);
+ Services.console.logMessage(scriptError);
+ break;
+ case "log":
+ case "info":
+ Services.console.logStringMessage(Array.from(aEvent.arguments));
+ break;
+ }
+ },
+
+ _setSyncAutoconnectDelay: function BG__setSyncAutoconnectDelay() {
+ // Assume that a non-zero value for services.sync.autoconnectDelay should override
+ if (Services.prefs.prefHasUserValue("services.sync.autoconnectDelay")) {
+ let prefDelay = Services.prefs.getIntPref("services.sync.autoconnectDelay");
+
+ if (prefDelay > 0)
+ return;
+ }
+
+ // delays are in seconds
+ const MAX_DELAY = 300;
+ let delay = 3;
+ let browserEnum = Services.wm.getEnumerator("navigator:browser");
+ while (browserEnum.hasMoreElements()) {
+ delay += browserEnum.getNext().gBrowser.tabs.length;
+ }
+ delay = delay <= MAX_DELAY ? delay : MAX_DELAY;
+
+ const {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+ Weave.Service.scheduler.delayedAutoConnect(delay);
+ },
+
+ // nsIObserver implementation
+ observe: function(subject, topic, data)
+ {
+ switch(topic) {
+ case "nsPref:changed":
+ switch (data) {
+ case DEBUGGER_REMOTE_ENABLED:
+ if (this.dbgIsEnabled)
+ this.dbgStart();
+ else
+ this.dbgStop();
+ break;
+ case DEBUGGER_REMOTE_PORT:
+ case DEBUGGER_FORCE_LOCAL:
+ /**
+ * If the server is not on, port changes have nothing to affect.
+ * The new value will be picked up if the server is started.
+ */
+ if (this.dbgIsEnabled)
+ this.dbgRestart();
+ break;
+ case DEBUGGER_WIFI_VISIBLE:
+ // Wifi visibility has changed, we need to restart the debugger
+ // server.
+ if (this.dbgIsEnabled && !Services.prefs.getBoolPref(DEBUGGER_FORCE_LOCAL))
+ this.dbgRestart();
+ break;
+ }
+ break;
+ case "profile-before-change":
+ // Any component depending on Places should be finalized in
+ // _onPlacesShutdown. Any component that doesn't need to act after
+ // the UI has gone should be finalized in _onQuitApplicationGranted.
+ this._dispose();
+ break;
+ case "profile-after-change":
+ this._onProfileAfterChange();
+ break;
+ case "chrome-document-global-created":
+ // Set up lwt, but only if the "lightweightthemes" attr is set on the root
+ // (i.e. in messenger.xul).
+ subject.addEventListener("DOMContentLoaded", () => {
+ if (subject.document.documentElement.hasAttribute("lightweightthemes")) {
+ new LightweightThemeConsumer(subject.document);
+ }
+ }, {once: true});
+ break;
+ case "final-ui-startup":
+ this._onProfileStartup();
+ this._promptForMasterPassword();
+ this._checkForNewAddons();
+ Services.search.init();
+ listeners.init();
+
+ Services.mm.loadFrameScript("chrome://navigator/content/content.js",
+ true);
+ ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
+ break;
+ case "browser-delayed-startup-finished":
+ // Intended fallthrough.
+ case "mail-startup-done":
+ Services.obs.removeObserver(this, "browser-delayed-startup-finished");
+ Services.obs.removeObserver(this, "mail-startup-done");
+ this._onFirstWindowLoaded(subject);
+ break;
+ case "sessionstore-windows-restored":
+ this._onBrowserStartup(subject);
+ break;
+ case "browser:purge-session-history":
+ // reset the console service's error buffer
+ Services.console.logStringMessage(null); // clear the console (in case it's open)
+ Services.console.reset();
+ break;
+ case "quit-application-requested":
+ this._onQuitRequest(subject, data);
+ break;
+ case "quit-application-granted":
+ this._onQuitApplicationGranted();
+ break;
+ case "browser-lastwindow-close-requested":
+ // The application is not actually quitting, but the last full browser
+ // window is about to be closed.
+ this._onQuitRequest(subject, "lastwindow");
+ break;
+ case "browser-lastwindow-close-granted":
+ if (this._saveSession)
+ this._setPrefToSaveSession();
+ break;
+ case "console-api-log-event":
+ if (Services.prefs.getBoolPref("browser.dom.window.console.enabled"))
+ this._logConsoleAPI(subject.wrappedJSObject);
+ break;
+// case "weave:service:ready":
+// this._setSyncAutoconnectDelay();
+// break;
+// case "weave:engine:clients:display-uri":
+// this._onDisplaySyncURI(subject);
+// break;
+ case "session-save":
+ this._setPrefToSaveSession();
+ subject.QueryInterface(Ci.nsISupportsPRBool);
+ subject.data = true;
+ break;
+ case "places-init-complete":
+ if (!this._migrationImportsDefaultBookmarks)
+ this._initPlaces(false);
+
+ Services.obs.removeObserver(this, "places-init-complete");
+ break;
+ case "idle":
+ this._backupBookmarks();
+ break;
+ case "initial-migration":
+ this._initialMigrationPerformed = true;
+ break;
+ case "browser-search-engine-modified":
+ break;
+ case "notifications-open-settings":
+ // Since this is a web notification, there's probably a browser window.
+ var mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ if (mostRecentBrowserWindow)
+ mostRecentBrowserWindow.toDataManager("|permissions");
+ break;
+ case "timer-callback":
+ // Load the Login Manager data from disk off the main thread, some time
+ // after startup. If the data is required before the timeout, for example
+ // because a restored page contains a password field, it will be loaded on
+ // the main thread, and this initialization request will be ignored.
+ Services.logins;
+ break;
+ case "handle-xul-text-link":
+ let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
+ if (!linkHandled.data) {
+ let mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ if (mostRecentBrowserWindow) {
+ let dataObj = JSON.parse(data);
+ let where = mostRecentBrowserWindow.whereToOpenLink(dataObj, false, true, true);
+ // Preserve legacy behavior of non-modifier left-clicks
+ // opening in a new selected tab.
+ if (where == "current") {
+ where = "tabfocused";
+ }
+ mostRecentBrowserWindow.openUILinkIn(dataObj.href, where);
+ linkHandled.data = true;
+ }
+ }
+ break;
+ }
+ },
+
+ // nsIWebProgressListener partial implementation
+ onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags)
+ {
+ if (aWebProgress.isTopLevel &&
+ aWebProgress instanceof Ci.nsIDocShell &&
+ aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_NORMAL &&
+ aWebProgress.useGlobalHistory &&
+ aWebProgress instanceof Ci.nsILoadContext &&
+ !aWebProgress.usePrivateBrowsing) {
+ switch (aLocation.scheme) {
+ case "about":
+ case "imap":
+ case "news":
+ case "mailbox":
+ case "moz-anno":
+ case "view-source":
+ case "chrome":
+ case "resource":
+ case "data":
+ case "wyciwyg":
+ case "javascript":
+ break;
+ default:
+ Services.prefs.setStringPref("browser.history.last_page_visited",
+ aLocation.spec);
+ break;
+ }
+ }
+ },
+
+ // initialization (called on application startup)
+ _init: function()
+ {
+ // observer registration
+ Services.obs.addObserver(this, "profile-before-change", true);
+ Services.obs.addObserver(this, "profile-after-change", true);
+ Services.obs.addObserver(this, "final-ui-startup", true);
+ Services.obs.addObserver(this, "browser-delayed-startup-finished", true);
+ Services.obs.addObserver(this, "mail-startup-done", true);
+ Services.obs.addObserver(this, "sessionstore-windows-restored", true);
+ Services.obs.addObserver(this, "browser:purge-session-history", true);
+ Services.obs.addObserver(this, "quit-application-requested", true);
+ Services.obs.addObserver(this, "quit-application-granted", true);
+ Services.obs.addObserver(this, "browser-lastwindow-close-requested", true);
+ Services.obs.addObserver(this, "browser-lastwindow-close-granted", true);
+ Services.obs.addObserver(this, "console-api-log-event", true);
+ Services.obs.addObserver(this, "weave:service:ready", true);
+ Services.obs.addObserver(this, "weave:engine:clients:display-uri", true);
+ Services.obs.addObserver(this, "session-save", true);
+ Services.obs.addObserver(this, "places-init-complete", true);
+ Services.obs.addObserver(this, "browser-search-engine-modified", true);
+ Services.obs.addObserver(this, "notifications-open-settings", true);
+ Services.obs.addObserver(this, "chrome-document-global-created", true);
+ Services.prefs.addObserver("devtools.debugger.", this, true);
+ Services.obs.addObserver(this, "handle-xul-text-link", true);
+ Cc['@mozilla.org/docloaderservice;1']
+ .getService(Ci.nsIWebProgress)
+ .addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
+ },
+
+ // cleanup (called on application shutdown)
+ _dispose: function BG__dispose() {
+ try {
+ Services.obs.removeObserver(this, "chrome-document-global-created");
+ }
+ catch (ex) {}
+ if (this._isIdleObserver) {
+ this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME_SEC);
+ delete this._isIdleObserver;
+ }
+ },
+
+ // profile is available
+ _onProfileAfterChange: function()
+ {
+ // check if we're in safe mode
+ if (Services.appinfo.inSafeMode) {
+ Services.ww.openWindow(null, "chrome://communicator/content/safeMode.xul",
+ "_blank", "chrome,centerscreen,modal,resizable=no", null);
+ }
+ this._copyDefaultProfileFiles();
+ },
+
+ // profile startup handler (contains profile initialization routines)
+ _onProfileStartup: function()
+ {
+ this._migrateUI();
+ this._migrateUI2();
+ migrateMailnews(); // mailnewsMigrator.js
+
+ Sanitizer.onStartup();
+
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 3000, timer.TYPE_ONE_SHOT);
+ },
+
+ /**
+ * Determine if the UI has been upgraded for this release. If not
+ * reset or migrate some user configurations depending on the migration
+ * level.
+ */
+ _migrateUI() {
+ const UI_VERSION = 10;
+
+ // If the pref is not set this is a new or pre SeaMonkey 2.49 profile.
+ // We can't tell so we just run migration with version 0.
+ let currentUIVersion =
+ Services.prefs.getIntPref("suite.migration.version", 0);
+
+ if (currentUIVersion >= UI_VERSION)
+ return;
+
+ if (currentUIVersion < 1) {
+ // Run any migrations due prior to 2.49.
+ this._updatePrefs();
+ this._migrateDownloadPrefs();
+
+ // Migrate remote content exceptions for email addresses which are
+ // encoded as chrome URIs.
+ let permissionsDB =
+ Services.dirsvc.get("ProfD", Ci.nsIFile);
+ permissionsDB.append("permissions.sqlite");
+ let db = Services.storage.openDatabase(permissionsDB);
+
+ try {
+ let statement = db.createStatement(
+ "select origin, permission from moz_perms where " +
+ // Avoid 'like' here which needs to be escaped.
+ " substr(origin, 1, 28) = 'chrome://messenger/content/?';");
+
+ try {
+ while (statement.executeStep()) {
+ let origin = statement.getUTF8String(0);
+ let permission = statement.getInt32(1);
+ Services.console.logStringMessage("Mail-Image-Perm Mig: " + origin);
+ Services.perms.remove(
+ Services.io.newURI(origin), "image");
+ origin = origin.replace("chrome://messenger/content/?",
+ "chrome://messenger/content/");
+ Services.perms.add(
+ Services.io.newURI(origin), "image", permission);
+ }
+ } finally {
+ statement.finalize();
+ }
+
+ // Sadly we still need to clear the database manually. Experiments
+ // showed that the permissions manager deletes only one record.
+ db.defaultTransactionType = Ci.mozIStorageConnection.TRANSACTION_EXCLUSIVE;
+ db.beginTransaction();
+
+ try {
+ db.executeSimpleSQL("delete from moz_perms where " +
+ " substr(origin, 1, 28) = 'chrome://messenger/content/?';");
+ db.commitTransaction();
+ } catch (ex) {
+ db.rollbackTransaction();
+ throw ex;
+ }
+ } finally {
+ db.close();
+ }
+ }
+
+ // Migration of disabled safebrowsing-phishing setting after pref renaming.
+ if (currentUIVersion < 2) {
+ try {
+ if (!Services.prefs.getBoolPref("browser.safebrowsing.enabled")) {
+ Services.prefs.setBoolPref("browser.safebrowsing.phishing.enabled", false);
+ Services.prefs.clearUserPref("browser.safebrowsing.enabled");
+ }
+ } catch (ex) {}
+ }
+
+ // Pretend currentUIVersion 3 never happened (used in 2.57 for a time).
+
+ // Remove obsolete download preferences set by user.
+ if (currentUIVersion < 4) {
+ try {
+ if (Services.prefs.prefHasUserValue("browser.download.manager.showAlertOnComplete")) {
+ Services.prefs.clearUserPref("browser.download.manager.showAlertOnComplete");
+ }
+ if (Services.prefs.prefHasUserValue("browser.download.manager.showAlertInterval")) {
+ Services.prefs.clearUserPref("browser.download.manager.showAlertInterval");
+ }
+ if (Services.prefs.prefHasUserValue("browser.download.manager.retention")) {
+ Services.prefs.clearUserPref("browser.download.manager.retention");
+ }
+ if (Services.prefs.prefHasUserValue("browser.download.manager.quitBehavior")) {
+ Services.prefs.clearUserPref("browser.download.manager.quitBehavior");
+ }
+ if (Services.prefs.prefHasUserValue("browser.download.manager.scanWhenDone")) {
+ Services.prefs.clearUserPref("browser.download.manager.scanWhenDone");
+ }
+ if (Services.prefs.prefHasUserValue("browser.download.manager.showWhenStarting")) {
+ Services.prefs.clearUserPref("browser.download.manager.showWhenStarting");
+ }
+ if (Services.prefs.prefHasUserValue("browser.download.manager.closeWhenDone")) {
+ Services.prefs.clearUserPref("browser.download.manager.closeWhenDone");
+ }
+ } catch (ex) {}
+ }
+
+ if (currentUIVersion < 5) {
+ // Delete obsolete ssl and strict transport security permissions.
+ let perms = Services.perms.enumerator;
+ while (perms.hasMoreElements()) {
+ let perm = perms.getNext();
+ if (perm.type == "falsestart-rc4" ||
+ perm.type == "falsestart-rsa" ||
+ perm.type == "sts/use" ||
+ perm.type == "sts/subd") {
+ Services.perms.removePermission(perm);
+ }
+ }
+ }
+
+ // Pretend currentUIVersion 6 and 7 never happened (used in 2.57 for a
+ // time).
+
+ // Migrate sanitizer options.
+ if (currentUIVersion < 8) {
+ const prefs = [ "history", "urlbar", "formdata", "passwords",
+ "downloads", "cookies", "cache", "sessions",
+ "offlineApps" ];
+
+ for (let pref of prefs) {
+ try {
+ let prefOld = "privacy.item." + pref;
+
+ // Migrate user value otherwise use default.
+ // Only the names have changed but not the default values.
+ if (Services.prefs.prefHasUserValue(prefOld)) {
+ let prefCpd = "privacy.cpd." + pref;
+ let prefShutdown = "privacy.clearOnShutdown." + pref;
+
+ // If it has a value this should never fail.
+ let oldValue = Services.prefs.getBoolPref(prefOld);
+ Services.prefs.setBoolPref(prefCpd, oldValue);
+ Services.prefs.setBoolPref(prefShutdown, oldValue);
+ Services.prefs.clearUserPref(prefOld);
+ }
+ } catch (ex) {
+ // Better safe than sorry.
+ Cu.reportError(ex);
+ }
+ }
+
+ // We might bring this back later but currently set to default.
+ Services.prefs.clearUserPref("privacy.sanitize.promptOnSanitize");
+
+ // As a precaution set to default if the user has enabled
+ // clearing data on shutdown because there will no longer be
+ // a possible prompt.
+ Services.prefs.clearUserPref("privacy.sanitize.sanitizeOnShutdown");
+ }
+
+ // Migrate mail tab options.
+ if (currentUIVersion < 9) {
+ const tabPrefs = [ "autoHide", "opentabfor.doubleclick",
+ "opentabfor.middleclick" ];
+ for (let pref of tabPrefs) {
+ try {
+ let prefBT = "browser.tabs." + pref;
+
+ // Copy user value otherwise use default.
+ if (Services.prefs.prefHasUserValue(prefBT)) {
+ let prefMT = "mail.tabs." + pref;
+
+ // If it has a value this should never fail.
+ let valueBT = Services.prefs.getBoolPref(prefBT);
+ Services.prefs.setBoolPref(prefMT, valueBT);
+ }
+ } catch (ex) {
+ // Better safe than sorry.
+ Cu.reportError(ex);
+ }
+ }
+
+ // We might bring this back later but currently set to default.
+ Services.prefs.clearUserPref("browser.tabs.opentabfor.doubleclick");
+ }
+
+ // Migrate the old requested locales prefs to use the new model
+ if (currentUIVersion < 10) {
+ const SELECTED_LOCALE_PREF = "general.useragent.locale";
+ const MATCHOS_LOCALE_PREF = "intl.locale.matchOS";
+
+ if (Services.prefs.prefHasUserValue(MATCHOS_LOCALE_PREF) ||
+ Services.prefs.prefHasUserValue(SELECTED_LOCALE_PREF)) {
+ if (Services.prefs.getBoolPref(MATCHOS_LOCALE_PREF, false)) {
+ Services.locale.setRequestedLocales([]);
+ } else {
+ let locale = Services.prefs.getComplexValue(SELECTED_LOCALE_PREF,
+ Ci.nsIPrefLocalizedString);
+ if (locale) {
+ try {
+ Services.locale.setRequestedLocales([locale.data]);
+ } catch (e) {
+ /* Don't panic if the value is not a valid locale code. */
+ }
+ }
+ }
+ Services.prefs.clearUserPref(SELECTED_LOCALE_PREF);
+ Services.prefs.clearUserPref(MATCHOS_LOCALE_PREF);
+ }
+ }
+
+ // Update the migration version.
+ Services.prefs.setIntPref("suite.migration.version", UI_VERSION);
+ },
+
+ /**
+ * Determine if the UI has been upgraded for this 2.57 or later release.
+ * If not reset or migrate some user configurations depending on the
+ * migration level.
+ * Only migration steps for 2.57 and higher are included in this function.
+ * When the 2.53 branch is retired this function can be merged with
+ * _migrateUI again.
+ */
+ _migrateUI2() {
+ const UI_VERSION2 = 1;
+
+ // If the pref is not set this is a new or pre SeaMonkey 2.57 profile.
+ // We can't tell so we just run migration with version 0.
+ let currentUIVersion2 =
+ Services.prefs.getIntPref("suite.migration.version2", 0);
+
+ if (currentUIVersion2 >= UI_VERSION2)
+ return;
+
+ // Run any migrations due prior to 2.57.
+ if (currentUIVersion2 < 1) {
+ // The XUL directory viewer is no longer provided.
+ try {
+ if (Services.prefs.getIntPref("network.dir.format") == 3) {
+ Services.prefs.setIntPref("network.dir.format", 2);
+ }
+ } catch (ex) {}
+ }
+
+ // Update the migration version.
+ Services.prefs.setIntPref("suite.migration.version2", UI_VERSION2);
+ },
+
+ // Copies additional profile files from the default profile tho the current profile.
+ // Only files not covered by the regular profile creation process.
+ // Currently only the userchrome examples.
+ _copyDefaultProfileFiles: function()
+ {
+ // Copy default chrome example files if they do not exist in the current profile.
+ var profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ profileDir.append("chrome");
+
+ // The chrome directory in the current/new profile already exists so no copying.
+ if (profileDir.exists())
+ return;
+
+ let defaultProfileDir = Services.dirsvc.get("DefRt",
+ Ci.nsIFile);
+ defaultProfileDir.append("profile");
+ defaultProfileDir.append("chrome");
+
+ if (defaultProfileDir.exists() && defaultProfileDir.isDirectory()) {
+ try {
+ this._copyDir(defaultProfileDir, profileDir);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ },
+
+ // Simple copy function for copying complete aSource Directory to aDestiniation.
+ _copyDir: function(aSource, aDestination)
+ {
+ let enumerator = aSource.directoryEntries;
+
+ while (enumerator.hasMoreElements()) {
+ let file = enumerator.nextFile;
+
+ if (file.isDirectory()) {
+ let subdir = aDestination.clone();
+ subdir.append(file.leafName);
+
+ // Create the target directory. If it already exists continue copying files.
+ try {
+ subdir.create(Ci.nsIFile.DIRECTORY_TYPE,
+ FileUtils.PERMS_DIRECTORY);
+ } catch (ex) {
+ if (ex.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS)
+ throw ex;
+ }
+ // Directory created. Now copy the files.
+ this._copyDir(file, subdir);
+ } else {
+ try {
+ file.copyTo(aDestination, null);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+ },
+
+ // Browser startup complete. All initial windows have opened.
+ _onBrowserStartup: function(aWindow) {
+ // For any add-ons that were installed disabled and can be enabled offer
+ // them to the user.
+ var browser = aWindow.getBrowser();
+ var changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED);
+ if (changedIDs.length) {
+ AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
+ aAddons.forEach(function(aAddon) {
+ // If the add-on isn't user disabled or can't be enabled then skip it.
+ if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
+ return;
+
+ browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
+ })
+ });
+ }
+
+ var notifyBox = browser.getNotificationBox();
+
+ // Show about:rights notification, if needed.
+ if (this._shouldShowRights())
+ this._showRightsNotification(notifyBox);
+
+ // Load the "more info" page for a locked places.sqlite
+ // This property is set earlier by places-database-locked topic.
+ if (this._isPlacesDatabaseLocked) {
+ notifyBox.showPlacesLockedWarning();
+ }
+
+ // Detect if updates are off and warn for outdated builds.
+ if (this._shouldShowUpdateWarning())
+ notifyBox.showUpdateWarning();
+
+ this._checkForDefaultClient(aWindow);
+ },
+
+ // First mail or browser window loaded.
+ _onFirstWindowLoaded: function(aWindow) {
+ AutoCompletePopup.init();
+ DateTimePickerHelper.init();
+
+ if ("@mozilla.org/windows-taskbar;1" in Cc &&
+ Cc["@mozilla.org/windows-taskbar;1"]
+ .getService(Ci.nsIWinTaskbar).available) {
+ let temp = {};
+ ChromeUtils.import("resource:///modules/WindowsJumpLists.jsm", temp);
+ temp.WinTaskbarJumpList.startup();
+ }
+
+ // Initialize the download manager after the app starts so that
+ // auto-resume downloads begin (such as after crashing or quitting with
+ // active downloads) and speeds up the first-load of the download manager.
+ // If the user manually opens the download manager before the init is
+ // done, the downloads will start right away, and initializing again
+ // won't hurt.
+ // Afterwards init the taskbar and eventuall show the download progress if
+ // on a supported platform.
+ (async () => {
+ DownloadsCommon.init();
+ })().catch(ex => {
+ Cu.reportError(ex);
+ }).then(() => {
+ ChromeUtils.import("resource:///modules/DownloadsTaskbar.jsm", {})
+ .DownloadsTaskbar.registerIndicator(aWindow);
+ });
+ },
+
+ /**
+ * Application shutdown handler.
+ */
+ _onQuitApplicationGranted: function()
+ {
+ if (this._saveSession) {
+ this._setPrefToSaveSession();
+ }
+ AutoCompletePopup.uninit();
+ DateTimePickerHelper.uninit();
+ },
+
+ _promptForMasterPassword: function()
+ {
+ if (!Services.prefs.getBoolPref("signon.startup.prompt"))
+ return;
+
+ // Try to avoid the multiple master password prompts on startup scenario
+ // by prompting for the master password upfront.
+ let token = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB)
+ .getInternalKeyToken();
+
+ // Only log in to the internal token if it is already initialized,
+ // otherwise we get a "Change Master Password" dialog.
+ try {
+ if (!token.needsUserInit)
+ token.login(false);
+ } catch (ex) {
+ // If user cancels an exception is expected.
+ }
+ },
+
+ // If new add-ons were installed during startup, open the add-ons manager.
+ _checkForNewAddons: function()
+ {
+ const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
+
+ if (!Services.prefs.prefHasUserValue(PREF_EM_NEW_ADDONS_LIST))
+ return;
+
+ const args = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ let str = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ args.appendElement(str);
+ str = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ str.data = Services.prefs.getCharPref(PREF_EM_NEW_ADDONS_LIST);
+ args.appendElement(str);
+ const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
+ // This window is the "first" to open.
+ // 'alwaysRaised' makes sure it stays in the foreground (though unfocused)
+ // so it is noticed.
+ const EMFEATURES = "all,dialog=no,alwaysRaised";
+ Services.ww.openWindow(null, EMURL, "_blank", EMFEATURES, args);
+
+ Services.prefs.clearUserPref(PREF_EM_NEW_ADDONS_LIST);
+ },
+
+ _onQuitRequest: function(aCancelQuit, aQuitType)
+ {
+ // If user has already dismissed quit request, then do nothing
+ if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
+ return;
+
+ var windowcount = 0;
+ var pagecount = 0;
+ var browserEnum = Services.wm.getEnumerator("navigator:browser");
+ while (browserEnum.hasMoreElements()) {
+ // XXXbz should we skip closed windows here?
+ windowcount++;
+
+ var browser = browserEnum.getNext();
+ var tabbrowser = browser.document.getElementById("content");
+ if (tabbrowser)
+ pagecount += tabbrowser.browsers.length;
+ }
+
+ this._saveSession = false;
+ if (pagecount < 2)
+ return;
+
+ if (aQuitType != "restart" && aQuitType != "lastwindow")
+ aQuitType = "quit";
+
+ var showPrompt = true;
+ try {
+ // browser.warnOnQuit is a hidden global boolean to override all quit prompts
+ // browser.warnOnRestart specifically covers app-initiated restarts where we restart the app
+ // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
+ if (Services.prefs.getIntPref("browser.startup.page") == 3 ||
+ Services.prefs.getBoolPref("browser.sessionstore.resume_session_once") ||
+ !Services.prefs.getBoolPref("browser.warnOnQuit"))
+ showPrompt = false;
+ else if (aQuitType == "restart")
+ showPrompt = Services.prefs.getBoolPref("browser.warnOnRestart");
+ else
+ showPrompt = Services.prefs.getBoolPref("browser.tabs.warnOnClose");
+ } catch (ex) {}
+
+ if (showPrompt) {
+ var quitBundle = Services.strings.createBundle("chrome://communicator/locale/quitDialog.properties");
+ var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+
+ var appName = brandBundle.GetStringFromName("brandShortName");
+ var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
+ [appName], 1);
+
+ var message;
+ if (aQuitType == "restart")
+ message = quitBundle.formatStringFromName("messageRestart",
+ [appName], 1);
+ else if (windowcount == 1) /* close browser only, or quit application with only 1 browser window */
+ message = quitBundle.formatStringFromName("messageNoWindows",
+ [appName], 1);
+ else /* quit application with 2 or more windows */
+ message = quitBundle.formatStringFromName("message",
+ [appName], 1);
+
+ var flags = Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1 +
+ Services.prompt.BUTTON_POS_0_DEFAULT;
+
+ var neverAsk = {value:false};
+ var button0Title, button1Title, button2Title;
+ var neverAskText = quitBundle.GetStringFromName("neverAsk");
+
+ if (aQuitType == "restart") {
+ button0Title = quitBundle.GetStringFromName("restartNowTitle");
+ button1Title = quitBundle.GetStringFromName("restartLaterTitle");
+ } else {
+ flags += Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2;
+ button0Title = quitBundle.GetStringFromName(
+ (aQuitType == "quit" ? "saveTitle" : "savelastwindowTitle"));
+ button1Title = quitBundle.GetStringFromName("cancelTitle");
+ button2Title = quitBundle.GetStringFromName(aQuitType + "Title"); /* "quitTitle" or "lastwindowTitle" */
+ }
+
+ var mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ var buttonChoice = Services.prompt.confirmEx(mostRecentBrowserWindow, quitDialogTitle, message,
+ flags, button0Title, button1Title, button2Title,
+ neverAskText, neverAsk);
+
+ switch (buttonChoice) {
+ case 2:
+ if (neverAsk.value)
+ Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
+ break;
+ case 1:
+ aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
+ aCancelQuit.data = true;
+ break;
+ case 0:
+ this._saveSession = true;
+ if (neverAsk.value) {
+ if (aQuitType == "restart")
+ Services.prefs.setBoolPref("browser.warnOnRestart", false);
+ else {
+ // always save state when shutting down
+ Services.prefs.setIntPref("browser.startup.page", 3);
+ }
+ }
+ break;
+ }
+ }
+ },
+
+ /*
+ * _shouldShowRights - Determines if the user should be shown the
+ * about:rights notification. The notification should *not* be shown if
+ * we've already shown the current version, or if the override pref says to
+ * never show it. The notification *should* be shown if it's never been seen
+ * before, if a newer version is available, or if the override pref says to
+ * always show it.
+ */
+ _shouldShowRights: function () {
+ // Look for an unconditional override pref. If set, do what it says.
+ // (true --> never show, false --> always show)
+ try {
+ return !Services.prefs.getBoolPref("browser.rights.override");
+ } catch (e) { }
+ // Ditto, for the legacy EULA pref (tinderbox testing profile sets this).
+ try {
+ return !Services.prefs.getBoolPref("browser.EULA.override");
+ } catch (e) { }
+
+ // Look to see if the user has seen the current version or not.
+ var currentVersion = Services.prefs.getIntPref("browser.rights.version");
+ try {
+ return !Services.prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
+ } catch (e) { }
+
+ // We haven't shown the notification before, so do so now.
+ return true;
+ },
+
+ _showRightsNotification: function(aNotifyBox) {
+ // Stick the notification onto the selected tab of the active browser window.
+ aNotifyBox.showRightsNotification();
+
+ // Set pref to indicate we've shown the notficiation.
+ var currentVersion = Services.prefs.getIntPref("browser.rights.version");
+ Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
+ },
+
+ /*
+ * _shouldShowUpdateWarning - Determines if the user should be warned about
+ * having updates off and an old build that likely should be updated.
+ */
+ _shouldShowUpdateWarning: function () {
+ // If the Updater is not available we don't show the warning.
+ if (!AppConstants.MOZ_UPDATER) {
+ return false;
+ }
+ // Look for an unconditional override pref. If set, do what it says.
+ // (true --> never show, false --> always show)
+ try {
+ return !Services.prefs.getBoolPref("app.updatecheck.override");
+ } catch (e) { }
+ // If updates are enabled, we don't need to worry.
+ if (Services.prefs.getBoolPref("app.update.enabled"))
+ return false;
+ var maxAge = 90 * 86400; // 90 days
+ var now = Math.round(Date.now() / 1000);
+ // If there was an automated update tried in the interval, don't worry.
+ const PREF_APP_UPDATE_LASTUPDATETIME = "app.update.lastUpdateTime.background-update-timer";
+ var lastUpdateTime = Services.prefs.prefHasUserValue(PREF_APP_UPDATE_LASTUPDATETIME) ?
+ Services.prefs.getIntPref(PREF_APP_UPDATE_LASTUPDATETIME) : 0;
+ if (lastUpdateTime + maxAge > now)
+ return false;
+
+ var buildID = Services.appinfo.appBuildID;
+ // construct build date from ID
+ var buildDate = new Date(buildID.substr(0, 4),
+ buildID.substr(4, 2) - 1,
+ buildID.substr(6, 2));
+ var buildTime = Math.round(buildDate / 1000);
+ // We should warn if the build is older than the max age.
+ return (buildTime + maxAge <= now);
+ },
+
+ // This method gets the shell service and has it check its settings.
+ // This will do nothing on platforms without a shell service.
+ _checkForDefaultClient: function checkForDefaultClient(aWindow)
+ {
+ if (ShellService) try {
+ var appTypes = ShellService.shouldBeDefaultClientFor;
+
+ // Show the default client dialog only if we should check for the default
+ // client and we aren't already the default for the stored app types in
+ // shell.checkDefaultApps.
+ if (appTypes && ShellService.shouldCheckDefaultClient &&
+ !ShellService.isDefaultClient(true, appTypes)) {
+ aWindow.openDialog("chrome://communicator/content/defaultClientDialog.xul",
+ "DefaultClient",
+ "modal,centerscreen,chrome,resizable=no");
+ }
+ } catch (e) {}
+ },
+
+ /**
+ * Initialize Places
+ * - imports the bookmarks html file if bookmarks database is empty, try to
+ * restore bookmarks from a JSON backup if the backend indicates that the
+ * database was corrupt.
+ *
+ * These prefs can be set up by the frontend:
+ *
+ * WARNING: setting these preferences to true will overwite existing bookmarks
+ *
+ * - browser.places.importBookmarksHTML
+ * Set to true will import the bookmarks.html file from the profile folder.
+ * - browser.places.smartBookmarksVersion
+ * Set during HTML import to indicate that Smart Bookmarks were created.
+ * Set to -1 to disable Smart Bookmarks creation.
+ * Set to 0 to restore current Smart Bookmarks.
+ * - browser.bookmarks.restore_default_bookmarks
+ * Set to true by safe-mode dialog to indicate we must restore default
+ * bookmarks.
+ */
+ _initPlaces: function BG__initPlaces(aInitialMigrationPerformed) {
+ // We must instantiate the history service since it will tell us if we
+ // need to import or restore bookmarks due to first-run, corruption or
+ // forced migration (due to a major schema change).
+ // If the database is corrupt or has been newly created we should
+ // import bookmarks.
+ let dbStatus = PlacesUtils.history.databaseStatus;
+
+ // The places.sqlite database is locked. We show a notification box for
+ // it in _onBrowserStartup.
+ if (dbStatus == PlacesUtils.history.DATABASE_STATUS_LOCKED) {
+ this._isPlacesDatabaseLocked = true;
+ Services.console.logStringMessage("places.sqlite is locked");
+ // Note: initPlaces should always happen when the first window is ready,
+ // in any case, better safe than sorry.
+ Services.obs.notifyObservers(null, "places-browser-init-complete");
+ return;
+ }
+
+ let importBookmarks = !aInitialMigrationPerformed &&
+ (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE ||
+ dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT);
+
+ // Check if user or an extension has required to import bookmarks.html.
+ let importBookmarksHTML = false;
+ try {
+ importBookmarksHTML =
+ Services.prefs.getBoolPref("browser.places.importBookmarksHTML");
+ if (importBookmarksHTML)
+ importBookmarks = true;
+ } catch (ex) {}
+
+ // Support legacy bookmarks.html format for apps that depend on that format.
+ // Default if the pref does not exists is 'Do not export'.
+ let autoExportHTML = Services.prefs.getBoolPref("browser.bookmarks.autoExportHTML", false);
+
+ if (autoExportHTML) {
+ // Sqlite.jsm and Places shutdown happen at profile-before-change, thus,
+ // to be on the safe side, this should run earlier.
+ AsyncShutdown.profileChangeTeardown.addBlocker(
+ "Places: export bookmarks.html",
+ () => BookmarkHTMLUtils.exportToFile(Services.dirsvc.get("BMarks",
+ Ci.nsIFile).path));
+ }
+
+ (async () => {
+ // Check if Safe Mode or the user has required to restore bookmarks from
+ // default profile's bookmarks.html.
+ let restoreDefaultBookmarks = false;
+ try {
+ restoreDefaultBookmarks =
+ Services.prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks");
+ if (restoreDefaultBookmarks) {
+ // Ensure that we already have a bookmarks backup for today.
+ await this._backupBookmarks();
+ importBookmarks = true;
+ }
+ } catch (ex) {}
+
+ // This may be reused later, check for "=== undefined" to see if it has
+ // been populated already.
+ let lastBackupFile;
+
+ // If the user did not require to restore default bookmarks, or import
+ // from bookmarks.html, we will try to restore from JSON.
+ if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
+ // Get latest JSON backup.
+ lastBackupFile = await PlacesBackups.getMostRecentBackup();
+ if (lastBackupFile) {
+ // Restore from JSON backup.
+ await BookmarkJSONUtils.importFromFile(lastBackupFile, true);
+ importBookmarks = false;
+ } else {
+ // We have created a new database but we don't have any backup available.
+ importBookmarks = true;
+ let bookmarksHTMLFile = Services.dirsvc.get("BMarks", Ci.nsIFile);
+ if (bookmarksHTMLFile.exists(bookmarksHTMLFile)) {
+ // If bookmarks.html is available in current profile import it...
+ importBookmarksHTML = true;
+ } else {
+ // ...otherwise we will restore defaults.
+ restoreDefaultBookmarks = true;
+ }
+ }
+ }
+
+ // If bookmarks are not imported, then initialize smart bookmarks. This
+ // happens during a common startup.
+ // Otherwise, if any kind of import runs, smart bookmarks creation should
+ // be delayed till the import operations has finished. Not doing so would
+ // cause them to be overwritten by the newly imported bookmarks.
+ if (!importBookmarks) {
+ try {
+ await this.ensurePlacesDefaultQueriesInitialized();
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ } else {
+ // An import operation is about to run.
+ // Don't try to recreate smart bookmarks if autoExportHTML is true or
+ // smart bookmarks are disabled.
+ let smartBookmarksVersion = Services.prefs.getIntPref("browser.places.smartBookmarksVersion", 0);
+ if (!autoExportHTML && smartBookmarksVersion != -1)
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+
+ let bookmarksURI = null;
+ if (restoreDefaultBookmarks) {
+ // User wants to restore bookmarks.html file from default profile folder
+ bookmarksURI = Services.io.newURI("resource:///defaults/profile/bookmarks.html");
+ } else {
+ let bookmarksFile = Services.dirsvc.get("BMarks", Ci.nsIFile);
+ if (bookmarksFile.exists(bookmarksFile)) {
+ bookmarksURI = Services.io.newFileURI(bookmarksFile);
+ }
+ }
+
+ if (bookmarksURI) {
+ // Import from bookmarks.html file.
+ try {
+ await BookmarkHTMLUtils.importFromURL(bookmarksURI.spec, true);
+ } catch (e) {
+ Cu.reportError("Bookmarks.html file could be corrupt. " + e);
+ }
+ try {
+ // Ensure that smart bookmarks are created once the operation is
+ // complete.
+ await this.ensurePlacesDefaultQueriesInitialized();
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ } else {
+ Cu.reportError(new Error("Unable to find bookmarks.html file."));
+ }
+
+ // Reset preferences, so we won't try to import again at next run
+ if (importBookmarksHTML)
+ Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false);
+ if (restoreDefaultBookmarks)
+ Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks",
+ false);
+ }
+
+ AsyncShutdown.quitApplicationGranted.addBlocker(
+ "Places: export bookmarks at dawn",
+ () => this._backupBookmarks());
+
+ // Initialize bookmark archiving on idle.
+ if (!this._isIdleObserver) {
+ this._idleService.addIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME_SEC);
+ this._isIdleObserver = true;
+ }
+
+ })().catch(ex => {
+ Cu.reportError(ex);
+ }).then(() => {
+ // NB: deliberately after the catch so that we always do this, even if
+ // we threw halfway through initializing in the Task above.
+ Services.obs.notifyObservers(null, "places-browser-init-complete");
+ });
+ },
+
+ /**
+ * If a backup for today doesn't exist, this creates one.
+ */
+ _backupBookmarks: function BG__backupBookmarks() {
+ return (async function() {
+ let lastBackupFile = await PlacesBackups.getMostRecentBackup();
+ // Should backup bookmarks if there are no backups or the maximum
+ // interval between backups elapsed.
+ if (!lastBackupFile ||
+ new Date() - PlacesBackups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS * 86400000) {
+ let maxBackups = Services.prefs.getIntPref("browser.bookmarks.max_backups");
+ await PlacesBackups.create(maxBackups);
+ }
+ })();
+ },
+
+ _updatePrefs: function()
+ {
+ // Make sure that the doNotTrack value conforms to the conversion from
+ // three-state to two-state. (This reverts a setting of "please track me"
+ // to the default "don't say anything").
+ try {
+ if (Services.prefs.getIntPref("privacy.donottrackheader.value") != 1) {
+ Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
+ Services.prefs.clearUserPref("privacy.donottrackheader.value");
+ }
+ } catch (ex) {}
+
+ // Migration of document-color preference which changed from boolean to
+ // tri-state; 0=always but not accessibility themes, 1=always, 2=never
+ try {
+ if (!Services.prefs.getBoolPref("browser.display.use_document_colors")) {
+ Services.prefs.setIntPref("browser.display.document_color_use", 2);
+ Services.prefs.clearUserPref("browser.display.use_document_colors");
+ }
+ } catch (ex) {}
+
+ // Try to get dictionary preference and adjust if not valid.
+ var prefName = "spellchecker.dictionary";
+ var prefValue = Services.prefs.getCharPref(prefName);
+
+ // replace underscore with dash if found in language
+ if (/_/.test(prefValue)) {
+ prefValue = prefValue.replace(/_/g, "-");
+ Services.prefs.setCharPref(prefName, prefValue);
+ }
+
+ var spellChecker = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+ var dictList = spellChecker.getDictionaryList();
+ // If the preference contains an invalid dictionary, set it to a valid
+ // dictionary, any dictionary will do.
+ if (dictList.length && !dictList.includes(prefValue))
+ Services.prefs.setCharPref(prefName, dictList[0]);
+ },
+
+ _migrateDownloadPrefs: function()
+ {
+ // Migration of download-manager preferences
+ if (Services.prefs.getPrefType("browser.download.dir") == Services.prefs.PREF_INVALID ||
+ Services.prefs.getPrefType("browser.download.lastDir") != Services.prefs.PREF_INVALID)
+ return; //Do nothing if .dir does not exist, or if it exists and lastDir does not
+
+ try {
+ Services.prefs.setComplexValue("browser.download.lastDir",
+ Ci.nsIFile,
+ Services.prefs.getComplexValue("browser.download.dir",
+ Ci.nsIFile));
+ } catch (ex) {
+ // Ensure that even if we don't end up migrating to a lastDir that we
+ // don't attempt another update. This will throw when QI'ed to
+ // nsIFile, but it does fallback gracefully.
+ Services.prefs.setCharPref("browser.download.lastDir", "");
+ }
+
+ try {
+ Services.prefs.setBoolPref("browser.download.useDownloadDir",
+ Services.prefs.getBoolPref("browser.download.autoDownload"));
+ } catch (ex) {}
+
+ try {
+ Services.prefs.setIntPref("browser.download.manager.behavior",
+ Services.prefs.getIntPref("browser.downloadmanager.behavior"));
+ } catch (ex) {}
+
+ try {
+ Services.prefs.setBoolPref("browser.download.progress.closeWhenDone",
+ !Services.prefs.getBoolPref("browser.download.progressDnldDialog.keepAlive"));
+ } catch (ex) {}
+ },
+
+ /**
+ * Devtools Debugger
+ */
+ get dbgIsEnabled()
+ {
+ return Services.prefs.getBoolPref(DEBUGGER_REMOTE_ENABLED);
+ },
+
+ dbgStart: function()
+ {
+ var port = Services.prefs.getIntPref(DEBUGGER_REMOTE_PORT);
+
+ // Make sure chrome debugging is enabled, no sense in starting otherwise.
+ DebuggerServer.allowChromeProcess = true;
+
+ if (!DebuggerServer.initialized) {
+ DebuggerServer.init();
+ DebuggerServer.addBrowserActors();
+ }
+ try {
+ let listener = DebuggerServer.createListener();
+ listener.portOrPath = port;
+
+ // Expose this listener via wifi discovery, if enabled.
+ if (Services.prefs.getBoolPref(DEBUGGER_WIFI_VISIBLE) &&
+ !Services.prefs.getBoolPref(DEBUGGER_FORCE_LOCAL)) {
+ listener.discoverable = true;
+ }
+
+ listener.open();
+ } catch(e) {}
+ },
+
+ dbgStop: function()
+ {
+ if (DebuggerServer.initialized)
+ DebuggerServer.closeAllListeners();
+ },
+
+ dbgRestart: function()
+ {
+ this.dbgStop();
+ this.dbgStart();
+ },
+
+ // ------------------------------
+ // public nsISuiteGlue members
+ // ------------------------------
+
+ showDownloadManager: function(newDownload)
+ {
+ if (!gDownloadManager) {
+ // Use an empty arguments string or the download manager window
+ // will miss the toolbar and other features.
+ var argString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ argString.data = "";
+ gDownloadManager = Services.ww.openWindow(null, DOWNLOAD_MANAGER_URL,
+ null,
+ "all,dialog=no,non-private",
+ argString);
+ gDownloadManager.addEventListener("load", function() {
+ gDownloadManager.addEventListener("unload", function() {
+ gDownloadManager = null;
+ });
+ // Attach the taskbar progress meter to the download manager window.
+ ChromeUtils.import("resource:///modules/DownloadsTaskbar.jsm", {})
+ .DownloadsTaskbar.attachIndicator(gDownloadManager);
+ });
+ } else if (!newDownload ||
+ Services.prefs.getBoolPref(PREF_FOCUS_WHEN_STARTING)) {
+ gDownloadManager.focus();
+ } else {
+ // This preference may not be set, so defaulting to two.
+ var flashCount = 2;
+ try {
+ flashCount = Services.prefs.getIntPref(PREF_FLASH_COUNT);
+ } catch (e) { }
+ gDownloadManager.getAttentionWithCycleCount(flashCount);
+ }
+ },
+
+ sanitize(aParentWindow) {
+ Sanitizer.showUI(aParentWindow);
+ },
+
+ async ensurePlacesDefaultQueriesInitialized() {
+ // This is the current smart bookmarks version, it must be increased every
+ // time they change.
+ // When adding a new smart bookmark below, its newInVersion property must
+ // be set to the version it has been added in. We will compare its value
+ // to users' smartBookmarksVersion and add new smart bookmarks without
+ // recreating old deleted ones.
+ const SMART_BOOKMARKS_VERSION = 7;
+ const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
+ const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
+
+ // TODO bug 399268: should this be a pref?
+ const MAX_RESULTS = 10;
+
+ // Get current smart bookmarks version. If not set, create them.
+ let smartBookmarksCurrentVersion = Services.prefs.getIntPref(SMART_BOOKMARKS_PREF, 0);
+
+ // If version is current, or smart bookmarks are disabled, bail out.
+ if (smartBookmarksCurrentVersion == -1 ||
+ smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION) {
+ return;
+ }
+
+ try {
+ let menuIndex = 0;
+ let toolbarIndex = 0;
+ let bundle = Services.strings.createBundle("chrome://communicator/locale/places/places.properties");
+ let queryOptions = Ci.nsINavHistoryQueryOptions;
+
+ let smartBookmarks = {
+ MostVisited: {
+ title: bundle.GetStringFromName("mostVisitedTitle"),
+ url: "place:sort=" + queryOptions.SORT_BY_VISITCOUNT_DESCENDING +
+ "&maxResults=" + MAX_RESULTS,
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ newInVersion: 1
+ },
+ RecentlyBookmarked: {
+ title: bundle.GetStringFromName("recentlyBookmarkedTitle"),
+ url: "place:folder=BOOKMARKS_MENU" + "&folder=UNFILED_BOOKMARKS" +
+ "&folder=TOOLBAR" +
+ "&queryType=" + queryOptions.QUERY_TYPE_BOOKMARKS +
+ "&sort=" + queryOptions.SORT_BY_DATEADDED_DESCENDING +
+ "&maxResults=" + MAX_RESULTS +
+ "&excludeQueries=1",
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ newInVersion: 1
+ },
+ RecentTags: {
+ title: bundle.GetStringFromName("recentTagsTitle"),
+ url: "place:type=" + queryOptions.RESULTS_AS_TAG_QUERY +
+ "&sort=" + queryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
+ "&maxResults=" + MAX_RESULTS,
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ newInVersion: 1
+ },
+ };
+
+ // Set current guid, parentGuid and index of existing Smart Bookmarks.
+ // We will use those to create a new version of the bookmark at the same
+ // position.
+ let smartBookmarkItemIds = PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ for (let itemId of smartBookmarkItemIds) {
+ let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
+ if (queryId in smartBookmarks) {
+ // Known smart bookmark.
+ let smartBookmark = smartBookmarks[queryId];
+ smartBookmark.guid = await PlacesUtils.promiseItemGuid(itemId);
+
+ if (!smartBookmark.url) {
+ await PlacesUtils.bookmarks.remove(smartBookmark.guid);
+ continue;
+ }
+
+ let bm = await PlacesUtils.bookmarks.fetch(smartBookmark.guid);
+ smartBookmark.parentGuid = bm.parentGuid;
+ smartBookmark.index = bm.index;
+ } else {
+ // We don't remove old Smart Bookmarks because user could still
+ // find them useful, or could have personalized them.
+ // Instead we remove the Smart Bookmark annotation.
+ PlacesUtils.annotations.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
+ }
+ }
+
+ for (let queryId of Object.keys(smartBookmarks)) {
+ let smartBookmark = smartBookmarks[queryId];
+
+ // We update or create only changed or new smart bookmarks.
+ // Also we respect user choices, so we won't try to create a smart
+ // bookmark if it has been removed.
+ if (smartBookmarksCurrentVersion > 0 &&
+ smartBookmark.newInVersion <= smartBookmarksCurrentVersion &&
+ !smartBookmark.guid || !smartBookmark.url)
+ continue;
+
+ // Remove old version of the smart bookmark if it exists, since it
+ // will be replaced in place.
+ if (smartBookmark.guid) {
+ await PlacesUtils.bookmarks.remove(smartBookmark.guid);
+ }
+
+ // Create the new smart bookmark and store its updated guid.
+ if (!("index" in smartBookmark)) {
+ if (smartBookmark.parentGuid == PlacesUtils.bookmarks.toolbarGuid)
+ smartBookmark.index = toolbarIndex++;
+ else if (smartBookmark.parentGuid == PlacesUtils.bookmarks.menuGuid)
+ smartBookmark.index = menuIndex++;
+ }
+ smartBookmark = await PlacesUtils.bookmarks.insert(smartBookmark);
+ let itemId = await PlacesUtils.promiseItemId(smartBookmark.guid);
+ PlacesUtils.annotations.setItemAnnotation(itemId,
+ SMART_BOOKMARKS_ANNO,
+ queryId, 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ }
+
+ // If we are creating all Smart Bookmarks from ground up, add a
+ // separator below them in the bookmarks menu.
+ if (smartBookmarksCurrentVersion == 0 &&
+ smartBookmarkItemIds.length == 0) {
+ let bm = await PlacesUtils.bookmarks.fetch({ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: menuIndex });
+ // Don't add a separator if the menu was empty or there is one already.
+ if (bm && bm.type != PlacesUtils.bookmarks.TYPE_SEPARATOR) {
+ await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ index: menuIndex });
+ }
+ }
+ } catch (ex) {
+ Cu.reportError(ex);
+ } finally {
+ Services.prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
+ Services.prefs.savePrefFile(null);
+ }
+ },
+
+ /**
+ * Called as an observer when Sync's "display URI" notification is fired.
+ */
+ _onDisplaySyncURI: function _onDisplaySyncURI(data) {
+ try {
+ var url = data.wrappedJSObject.object.uri;
+ var mostRecentBrowserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ if (mostRecentBrowserWindow) {
+ mostRecentBrowserWindow.getBrowser().addTab(url, { focusNewTab: true });
+ mostRecentBrowserWindow.content.focus();
+ } else {
+ var args = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ args.data = url;
+ var chromeURL = Services.prefs.getCharPref("browser.chromeURL");
+ Services.ww.openWindow(null, chromeURL, "_blank", "chrome,all,dialog=no", args);
+ }
+ } catch (e) {
+ Cu.reportError("Error displaying tab received by Sync: " + e);
+ }
+ },
+
+ // for XPCOM
+ classID: Components.ID("{bbbbe845-5a1b-40ee-813c-f84b8faaa07c}"),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference,
+ Ci.nsISuiteGlue])
+
+}
+
+/**
+ * ContentPermissionIntegration is responsible for showing the user
+ * simple permission prompts when content requests additional
+ * capabilities.
+ *
+ * While there are some built-in permission prompts, createPermissionPrompt
+ * can also be overridden by system add-ons or tests to provide new ones.
+ *
+ * This override ability is provided by Integration.jsm. See
+ * PermissionUI.jsm for an example of how to provide a new prompt
+ * from an add-on.
+ */
+var ContentPermissionIntegration = {
+ /**
+ * Creates a PermissionPrompt for a given permission type and
+ * nsIContentPermissionRequest.
+ *
+ * @param {string} type
+ * The type of the permission request from content. This normally
+ * matches the "type" field of an nsIContentPermissionType, but it
+ * can be something else if the permission does not use the
+ * nsIContentPermissionRequest model. Note that this type might also
+ * be different from the permission key used in the permissions
+ * database.
+ * Example: "geolocation"
+ * @param {nsIContentPermissionRequest} request
+ * The request for a permission from content.
+ * @return {PermissionPrompt} (see PermissionUI.jsm),
+ * or undefined if the type cannot be handled.
+ */
+ createPermissionPrompt(type, request) {
+ switch (type) {
+ case "geolocation": {
+ return new PermissionUI.GeolocationPermissionPrompt(request);
+ }
+ case "desktop-notification": {
+ return new PermissionUI.DesktopNotificationPermissionPrompt(request);
+ }
+ case "persistent-storage": {
+ if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
+ return new PermissionUI.PersistentStoragePermissionPrompt(request);
+ }
+ }
+ }
+ return undefined;
+ },
+};
+
+function ContentPermissionPrompt() {}
+
+ContentPermissionPrompt.prototype = {
+ classID: Components.ID("{9d4c845d-3f09-402a-b66d-50f291d7d50f}"),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
+
+ /**
+ * This implementation of nsIContentPermissionPrompt.prompt ensures
+ * that there's only one nsIContentPermissionType in the request,
+ * and that it's of type nsIContentPermissionType. Failing to
+ * satisfy either of these conditions will result in this method
+ * throwing NS_ERRORs. If the combined ContentPermissionIntegration
+ * cannot construct a prompt for this particular request, an
+ * NS_ERROR_FAILURE will be thrown.
+ *
+ * Any time an error is thrown, the nsIContentPermissionRequest is
+ * cancelled automatically.
+ *
+ * @param {nsIContentPermissionRequest} request
+ * The request that we're to show a prompt for.
+ */
+ prompt(request) {
+ try {
+ // Only allow exactly one permission request here.
+ let types = request.types.QueryInterface(Ci.nsIArray);
+ if (types.length != 1) {
+ throw Components.Exception(
+ "Expected an nsIContentPermissionRequest with only 1 type.",
+ Cr.NS_ERROR_UNEXPECTED);
+ }
+
+ let type = types.queryElementAt(0, Ci.nsIContentPermissionType).type;
+ let combinedIntegration =
+ Integration.contentPermission.getCombined(ContentPermissionIntegration);
+
+ let permissionPrompt =
+ combinedIntegration.createPermissionPrompt(type, request);
+ if (!permissionPrompt) {
+ throw Components.Exception(
+ "Failed to handle permission of type ${type}",
+ Cr.NS_ERROR_FAILURE);
+ }
+
+ permissionPrompt.prompt();
+ } catch (ex) {
+ Cu.reportError(ex);
+ request.cancel();
+ throw ex;
+ }
+ },
+};
+
+//module initialization
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([SuiteGlue, ContentPermissionPrompt]);
diff --git a/comm/suite/components/permissions/content/cookieViewer.js b/comm/suite/components/permissions/content/cookieViewer.js
new file mode 100644
index 0000000000..d864f72908
--- /dev/null
+++ b/comm/suite/components/permissions/content/cookieViewer.js
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+// cookies and permissions list
+var cookies = [];
+var permissions = [];
+var allCookies = [];
+var deletedCookies = [];
+var deletedPermissions = [];
+
+var cookieBundle;
+var gUpdatingBatch = "";
+var lastCookieSortColumn;
+var lastCookieSortAscending;
+var lastPermissionSortColumn;
+var lastPermissionSortAscending;
+
+function Startup() {
+
+ // arguments passed to this routine:
+ // cookieManager
+
+ // intialize string bundle
+ cookieBundle = document.getElementById("cookieBundle");
+
+ // load in the cookies and permissions
+ cookiesTree = document.getElementById("cookiesTree");
+ lastCookieSortAscending = (cookiesTree.getAttribute("sortAscending") == "true");
+ lastCookieSortColumn = cookiesTree.getAttribute("sortColumn");
+ permissionsTree = document.getElementById("permissionsTree");
+ lastPermissionSortAscending = (permissionsTree.getAttribute("sortAscending") == "true");
+ lastPermissionSortColumn = permissionsTree.getAttribute("sortColumn");
+ loadCookies();
+ loadPermissions();
+
+ // be prepared to reload the display if anything changes
+ Services.obs.addObserver(cookieReloadDisplay, "cookie-changed", false);
+ Services.obs.addObserver(cookieReloadDisplay, "perm-changed", false);
+
+ // filter the table if requested by caller
+ if (window.arguments &&
+ window.arguments[0] &&
+ window.arguments[0].filterString)
+ setFilter(window.arguments[0].filterString);
+
+ document.getElementById("filter").focus();
+}
+
+function Shutdown() {
+ Services.obs.removeObserver(cookieReloadDisplay, "cookie-changed");
+ Services.obs.removeObserver(cookieReloadDisplay, "perm-changed");
+}
+
+function PromptConfirm(title, msg, yes) {
+ var flags =
+ ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ return Services.prompt.confirmEx(window,
+ cookieBundle.getString(title),
+ cookieBundle.getString(msg),
+ flags,
+ cookieBundle.getString(yes),
+ null, null, null, {value:0});
+}
+
+var cookieReloadDisplay = {
+ observe: function(subject, topic, state) {
+ if (topic == gUpdatingBatch)
+ return;
+ if (topic == "cookie-changed") {
+ allCookies.length = 0;
+ loadCookies();
+ } else if (topic == "perm-changed") {
+ permissions.length = 0;
+ loadPermissions();
+ }
+ }
+}
+
+function doSelectAll() {
+ var elem = document.commandDispatcher.focusedElement;
+ if (elem && "treeBoxObject" in elem)
+ elem.view.selection.selectAll();
+}
+
+/*** =================== COOKIES CODE =================== ***/
+
+var cookiesTreeView = {
+ rowCount : 0,
+ setTree : function(tree){},
+ getImageSrc : function(row,column) {},
+ getProgressMode : function(row,column) {},
+ getCellValue : function(row,column) {},
+ getCellText : function(row,column){ return cookies[row][column.id]; },
+ isSeparator : function(index) {return false;},
+ isSorted: function() { return false; },
+ isContainer : function(index) {return false;},
+ cycleHeader : function(aCol) {},
+ getRowProperties : function(row) { return ""; },
+ getColumnProperties : function(column) { return ""; },
+ getCellProperties : function(row, column) { return ""; }
+};
+var cookiesTree;
+
+function Cookie(id, host, name, path, originAttributes, value,
+ isDomain, rawHost, isSecure, expires) {
+ this.id = id;
+ this.host = host;
+ this.name = name;
+ this.path = path;
+ this.originAttributes = originAttributes;
+ this.value = value;
+ this.isDomain = isDomain;
+ this.rawHost = rawHost;
+ this.isSecure = isSecure;
+ this.expires = GetExpiresString(expires);
+ this.expiresSortValue = expires;
+}
+
+function loadCookies() {
+ // load cookies into a table
+ var enumerator = Services.cookies.enumerator;
+ var count = 0;
+ while (enumerator.hasMoreElements()) {
+ var nextCookie = enumerator.getNext();
+ if (!nextCookie) break;
+ nextCookie = nextCookie.QueryInterface(Ci.nsICookie);
+ var host = nextCookie.host;
+ allCookies.push(
+ new Cookie(count++, host, nextCookie.name,
+ nextCookie.path, nextCookie.originAttributes,
+ nextCookie.value, nextCookie.isDomain,
+ host.charAt(0)=="." ? host.slice(1) : host,
+ nextCookie.isSecure, nextCookie.expires));
+ }
+
+ // filter, sort and display the table
+ cookiesTree.view = cookiesTreeView;
+ filter(document.getElementById("filter").value);
+}
+
+function GetExpiresString(expires) {
+ if (expires) {
+ var date = new Date(1000*expires);
+
+ // if a server manages to set a really long-lived cookie, the dateservice
+ // can't cope with it properly, so we'll just return a blank string
+ // see bug 238045 for details
+ var expiry = "";
+ try {
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "full", timeStyle: "long" });
+ expiry = dateTimeFormatter.format(date);
+ } catch(ex) {
+ // do nothing
+ }
+ return expiry;
+ }
+ return cookieBundle.getString("expireAtEndOfSession");
+}
+
+function CookieSelected() {
+ var selections = GetTreeSelections(cookiesTree);
+ if (selections.length) {
+ document.getElementById("removeCookie").removeAttribute("disabled");
+ } else {
+ document.getElementById("removeCookie").setAttribute("disabled", "true");
+ ClearCookieProperties();
+ return true;
+ }
+
+ var idx = selections[0];
+ if (idx >= cookies.length) {
+ // Something got out of synch. See bug 119812 for details
+ dump("Tree and viewer state are out of sync! " +
+ "Help us figure out the problem in bug 119812");
+ return false;
+ }
+
+ var props = [
+ {id: "ifl_name", value: cookies[idx].name},
+ {id: "ifl_value", value: cookies[idx].value},
+ {id: "ifl_isDomain",
+ value: cookies[idx].isDomain ?
+ cookieBundle.getString("domainColon") : cookieBundle.getString("hostColon")},
+ {id: "ifl_host", value: cookies[idx].host},
+ {id: "ifl_path", value: cookies[idx].path},
+ {id: "ifl_isSecure",
+ value: cookies[idx].isSecure ?
+ cookieBundle.getString("forSecureOnly") :
+ cookieBundle.getString("forAnyConnection")},
+ {id: "ifl_expires", value: cookies[idx].expires}
+ ];
+
+ var value;
+ var field;
+
+ for (let lProp of props)
+ {
+ field = document.getElementById(lProp.id);
+ if ((selections.length > 1) && (lProp.id != "ifl_isDomain")) {
+ value = ""; // clear field if multiple selections
+ } else {
+ value = lProp.value;
+ }
+ field.value = value;
+ }
+ return true;
+}
+
+function ClearCookieProperties() {
+ var properties =
+ ["ifl_name","ifl_value","ifl_host","ifl_path","ifl_isSecure","ifl_expires"];
+ for (let prop of properties) {
+ document.getElementById(prop).value = "";
+ }
+}
+
+function DeleteCookie() {
+ if (cookiesTreeView.selection.count > 1 &&
+ PromptConfirm("deleteSelectedCookiesTitle",
+ "deleteSelectedCookies",
+ "deleteSelectedCookiesYes") == 1) {
+ return;
+ }
+ DeleteSelectedItemFromTree(cookiesTree, cookiesTreeView,
+ cookies, deletedCookies,
+ "removeCookie", "removeAllCookies");
+ if (document.getElementById("filter").value) {
+ // remove selected cookies from unfiltered set
+ for (let cookie of deletedCookies) {
+ allCookies.splice(allCookies.indexOf(cookie), 1);
+ }
+ }
+ if (!cookies.length) {
+ ClearCookieProperties();
+ }
+ FinalizeCookieDeletions();
+}
+
+function DeleteAllCookies() {
+ if (PromptConfirm("deleteAllCookiesTitle",
+ "deleteAllCookies",
+ "deleteAllCookiesYes") == 1) {
+ return;
+ }
+
+ ClearCookieProperties();
+ DeleteAllFromTree(cookiesTree, cookiesTreeView,
+ cookies, deletedCookies,
+ "removeCookie", "removeAllCookies");
+ allCookies.length = 0;
+ FinalizeCookieDeletions();
+}
+
+function FinalizeCookieDeletions() {
+ gUpdatingBatch = "cookie-changed";
+ for (let delCookie of deletedCookies) {
+ Services.cookies.remove(delCookie.host,
+ delCookie.name,
+ delCookie.path,
+ document.getElementById("checkbox").checked,
+ delCookie.originAttributes);
+ }
+ deletedCookies.length = 0;
+ gUpdatingBatch = "";
+}
+
+function HandleCookieKeyPress(e) {
+ if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ DeleteCookie();
+ }
+}
+
+function CookieColumnSort(column, updateSelection) {
+ lastCookieSortAscending =
+ SortTree(cookiesTree, cookiesTreeView, cookies,
+ column, lastCookieSortColumn, lastCookieSortAscending,
+ updateSelection);
+ lastCookieSortColumn = column;
+
+ SetSortDirection(cookiesTree, column, lastCookieSortAscending);
+}
+
+/*** =================== PERMISSIONS CODE =================== ***/
+
+var permissionsTreeView = {
+ rowCount : 0,
+ setTree : function(tree){},
+ getImageSrc : function(row,column) {},
+ getProgressMode : function(row,column) {},
+ getCellValue : function(row,column) {},
+ getCellText : function(row,column) { return permissions[row][column.id]; },
+ isSeparator : function(index) {return false;},
+ isSorted: function() { return false; },
+ isContainer : function(index) {return false;},
+ cycleHeader : function(aCol) {},
+ getRowProperties : function(row) { return ""; },
+ getColumnProperties : function(column) { return ""; },
+ getCellProperties : function(row, column) { return ""; }
+};
+var permissionsTree;
+
+function Permission(id, principal, type, capability) {
+ this.id = id;
+ this.principal = principal;
+ this.host = principal.URI.hostPort;
+ this.scheme = principal.URI.scheme;
+ this.type = type;
+ this.capability = capability;
+}
+
+function loadPermissions() {
+ // load permissions into a table
+ var enumerator = Services.perms.enumerator;
+ var canStr = cookieBundle.getString("can");
+ var canSessionStr = cookieBundle.getString("canSession");
+ var cannotStr = cookieBundle.getString("cannot");
+ var capability;
+ var count = 0;
+ var permission;
+ while (enumerator.hasMoreElements()) {
+ permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+ // We are only interested in cookie permissions in this code.
+ if (permission.type == "cookie") {
+ // It is currently possible to add a cookie permission for about:xxx
+ // and other internal pages. They are probably invalid and will be
+ // ignored for now.
+ // Test if the permission has a host.
+ try {
+ permission.principal.URI.host;
+ }
+ catch (e) {
+ Cu.reportError("Invalid permission found: " +
+ permission.principal.origin + " " + permission.type);
+ continue;
+ }
+
+ switch (permission.capability) {
+ case Ci.nsIPermissionManager.ALLOW_ACTION:
+ capability = canStr;
+ break;
+ case Ci.nsIPermissionManager.DENY_ACTION:
+ capability = cannotStr;
+ break;
+ case Ci.nsICookiePermission.ACCESS_SESSION:
+ capability = canSessionStr;
+ break;
+ default:
+ continue;
+ }
+ permissions.push(new Permission(count++,
+ permission.principal,
+ permission.type,
+ capability));
+ }
+ }
+ permissionsTreeView.rowCount = permissions.length;
+
+ // sort and display the table
+ permissionsTree.view = permissionsTreeView;
+ permissionsTreeView.selection.clearSelection();
+ SortTree(permissionsTree, permissionsTreeView, permissions,
+ lastPermissionSortColumn, lastPermissionSortColumn,
+ !lastPermissionSortAscending);
+
+ // disable "remove all" button if there are no cookies
+ document.getElementById("removeAllPermissions").disabled = permissions.length == 0;
+}
+
+function DeletePermission() {
+ if (permissionsTreeView.selection.count > 1 &&
+ PromptConfirm("deleteSelectedSitesTitle",
+ "deleteSelectedCookiesSites",
+ "deleteSelectedSitesYes") == 1) {
+ return;
+ }
+ DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView,
+ permissions, deletedPermissions,
+ "removePermission", "removeAllPermissions");
+ FinalizePermissionDeletions();
+}
+
+function setCookiePermissions(action) {
+ var site = document.getElementById("cookie-site");
+
+ // let the backend do the validation
+ try {
+ var url = new URL(site.value);
+ } catch (e) {
+ // show an error if URL is invalid
+ window.alert(cookieBundle.getString("allowedURLSchemes"));
+ return;
+ }
+
+ try {
+ var uri = Services.io.newURI(url);
+ } catch (e) {
+ // show an error if URI can not be constructed or adding it failed
+ window.alert(cookieBundle.getString("errorAddPermission"));
+ return;
+ }
+ // only allow a few schemes here
+ // others like file:// would produce an invalid entry in the database
+ if (uri.scheme != "http" &&
+ uri.scheme != "https") {
+ // show an error if uri uses invalid scheme
+ window.alert(uri.scheme + ": " + cookieBundle.getString("allowedURLSchemes"));
+ return;
+ }
+
+ if (Services.perms.testPermission(uri, "cookie") != action)
+ Services.perms.add(uri, "cookie", action);
+
+ site.focus();
+ site.value = "";
+}
+
+function DeleteAllPermissions() {
+ if (PromptConfirm("deleteAllSitesTitle",
+ "deleteAllCookiesSites",
+ "deleteAllSitesYes") == 1) {
+ return;
+ }
+
+ DeleteAllFromTree(permissionsTree, permissionsTreeView,
+ permissions, deletedPermissions,
+ "removePermission", "removeAllPermissions");
+ FinalizePermissionDeletions();
+}
+
+function FinalizePermissionDeletions() {
+ if (!deletedPermissions.length)
+ return;
+
+ gUpdatingBatch = "perm-changed";
+ for (let permission of deletedPermissions)
+ Services.perms.removeFromPrincipal(permission.principal, permission.type);
+ deletedPermissions.length = 0;
+ gUpdatingBatch = "";
+}
+
+function HandlePermissionKeyPress(e) {
+ if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ DeletePermission();
+ }
+}
+
+function PermissionColumnSort(column, updateSelection) {
+ lastPermissionSortAscending =
+ SortTree(permissionsTree, permissionsTreeView, permissions,
+ column, lastPermissionSortColumn, lastPermissionSortAscending,
+ updateSelection);
+ lastPermissionSortColumn = column;
+
+ SetSortDirection(permissionsTree, column, lastPermissionSortAscending);
+}
+
+/*** ============ CODE FOR HELP BUTTON =================== ***/
+
+function doHelpButton()
+{
+ var selTab = document.getElementById("tabbox").selectedTab;
+ var key = selTab.getAttribute("help");
+ openHelp(key, "chrome://communicator/locale/help/suitehelp.rdf");
+}
+
+/*** =================== FILTER CODE =================== ***/
+
+function filterCookies(aFilterValue)
+{
+ var filterSet = [];
+ for (let cookie of allCookies) {
+ if (cookie.rawHost.includes(aFilterValue) ||
+ cookie.name.includes(aFilterValue) ||
+ cookie.value.includes(aFilterValue))
+ filterSet.push(cookie);
+ }
+ return filterSet;
+}
+
+function filter(filter)
+{
+ // clear the display
+ var oldCount = cookiesTreeView.rowCount;
+ cookiesTreeView.rowCount = 0;
+ cookiesTree.treeBoxObject.rowCountChanged(0, -oldCount);
+
+ // set up the display
+ cookies = filter ? filterCookies(filter) : allCookies;
+ cookiesTreeView.rowCount = cookies.length;
+ cookiesTree.treeBoxObject.rowCountChanged(0, cookiesTreeView.rowCount);
+
+ // sort the tree according to the last sort parameters
+ SortTree(cookiesTree, cookiesTreeView, cookies, lastCookieSortColumn,
+ lastCookieSortColumn, !lastCookieSortAscending);
+
+ // disable Remove All Cookies button if the view is filtered or there are no cookies
+ if (filter || !cookies.length)
+ document.getElementById("removeAllCookies").setAttribute("disabled", "true");
+ else
+ document.getElementById("removeAllCookies").removeAttribute("disabled");
+
+ // if the view is filtered and not empty then select the first item
+ if (filter && cookies.length)
+ cookiesTreeView.selection.select(0);
+}
+
+function setFilter(aFilterString)
+{
+ document.getElementById("filter").value = aFilterString;
+ filter(aFilterString);
+}
+
+function focusFilterBox()
+{
+ var filterBox = document.getElementById("filter");
+ filterBox.focus();
+ filterBox.select();
+}
diff --git a/comm/suite/components/permissions/content/cookieViewer.xul b/comm/suite/components/permissions/content/cookieViewer.xul
new file mode 100644
index 0000000000..62a9db9ad3
--- /dev/null
+++ b/comm/suite/components/permissions/content/cookieViewer.xul
@@ -0,0 +1,225 @@
+<?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/. -->
+
+<!-- CHANGE THIS WHEN MOVING FILES -->
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!-- CHANGE THIS WHEN MOVING FILES -->
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/permissions/cookieViewer.dtd" >
+
+<dialog id="cookieviewer"
+ buttons="help"
+ title="&windowtitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="mozilla:cookieviewer"
+ style="width: 65ch; height: 42em;"
+ onload="Startup()"
+ onunload="Shutdown()"
+ ondialoghelp="doHelpButton();"
+ persist="screenX screenY width height">
+
+ <script src="chrome://communicator/content/permissions/cookieViewer.js"/>
+ <script src="chrome://communicator/content/permissions/permissionsUtils.js"/>
+ <script src="chrome://global/content/treeUtils.js"/>
+ <script src="chrome://help/content/contextHelp.js" />
+
+ <keyset id="dialogKeys">
+ <key key="&focusSearch.key;"
+ modifiers="accel"
+ oncommand="focusFilterBox();"/>
+ <key key="&selectAll.key;"
+ modifiers="accel"
+ oncommand="doSelectAll();"/>
+ </keyset>
+ <stringbundle id="cookieBundle"
+ src="chrome://communicator/locale/permissions/cookieViewer.properties"/>
+
+ <tabbox id="tabbox" flex="1">
+ <tabs>
+ <tab id="cookiesTab" label="&tab.cookiesonsystem.label;" help="cookies_stored"/>
+ <tab id="permissionsTab" label="&tab.bannedservers.label;" help="cookie_sites"/>
+ </tabs>
+ <tabpanels id="panel" flex="1">
+ <vbox class="tabpanel" id="system" flex="1">
+ <vbox id="dummyContainer" flex="1">
+ <!-- filter -->
+ <hbox align="center">
+ <textbox id="filter"
+ flex="1"
+ type="search"
+ aria-controls="cookiesTree"
+ placeholder="&search.placeholder;"
+ oncommand="filter(this.value);"/>
+ </hbox>
+ <separator class="thin"/>
+ <label value="&div.cookiesonsystem.label;" control="cookiesTree"/>
+ <separator class="thin"/>
+ <tree id="cookiesTree" flex="1" style="height: 10em;"
+ onkeypress="HandleCookieKeyPress(event);"
+ onselect="CookieSelected();"
+ sortAscending="true"
+ sortColumn="rawHost"
+ persist="sortAscending sortColumn">
+ <treecols>
+ <treecol id="rawHost"
+ label="&treehead.cookiedomain.label;"
+ flex="5"
+ onclick="CookieColumnSort(this.id, true);"
+ sortDirection="ascending"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="name"
+ label="&treehead.cookiename.label;"
+ flex="5"
+ onclick="CookieColumnSort(this.id, true);"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="expires"
+ label="&treehead.cookieexpires.label;"
+ flex="10"
+ hidden="true"
+ onclick="CookieColumnSort(this.id, true);"
+ persist="width hidden"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <groupbox>
+ <caption label="&treehead.infoselected.label;"/>
+ <!-- labels -->
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.name.label;" control="ifl_name"/>
+ </hbox>
+ <textbox id="ifl_name" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.value.label;" control="ifl_value"/>
+ </hbox>
+ <textbox id="ifl_value" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label id="ifl_isDomain" value="&props.domain.label;" control="ifl_host"/>
+ </hbox>
+ <textbox id="ifl_host" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.path.label;" control="ifl_path"/>
+ </hbox>
+ <textbox id="ifl_path" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.secure.label;" control="ifl_isSecure"/>
+ </hbox>
+ <textbox id="ifl_isSecure" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.expires.label;" control="ifl_expires"/>
+ </hbox>
+ <textbox id="ifl_expires" readonly="true" class="plain"/>
+ </row>
+
+ </rows>
+ </grid>
+ </groupbox>
+ <hbox>
+ <button id="removeCookie" disabled="true"
+ label="&button.removecookie.label;"
+ accesskey="&button.removecookie.accesskey;"
+ oncommand="DeleteCookie();"/>
+ <button id="removeAllCookies"
+ label="&button.removeallcookies.label;"
+ accesskey="&button.removeallcookies.accesskey;"
+ oncommand="DeleteAllCookies();"/>
+ <!-- todo: <button id="restoreCookies" class="dialog push" disabled="true" label="&button.restorecookie.label;" oncommand="RestoreCookies();"/> -->
+ </hbox>
+ <separator class="thin"/>
+ <hbox align="start">
+ <checkbox id="checkbox" label="&futureCookies.label;" accesskey="&futureCookies.accesskey;" persist="checked"/>
+ </hbox>
+ </vbox>
+ </vbox>
+
+ <vbox id="servers" flex="1">
+ <description id="permissionsText">&div.bannedservers.label;</description>
+ <separator class="thin"/>
+ <hbox>
+ <textbox id="cookie-site"
+ flex="1"
+ oninput="handleHostInput(this.value);"/>
+ <button id="btnBlock" label="&blockSite.label;" disabled="true"
+ accesskey="&blockSite.accesskey;"
+ oncommand="setCookiePermissions(Ci.nsIPermissionManager.DENY_ACTION);"/>
+ <button id="btnSession" label="&allowSiteSession.label;" disabled="true"
+ accesskey="&allowSiteSession.accesskey;"
+ oncommand="setCookiePermissions(Ci.nsICookiePermission.ACCESS_SESSION);"/>
+ <button id="btnAllow" label="&allowSite.label;" disabled="true"
+ accesskey="&allowSite.accesskey;"
+ oncommand="setCookiePermissions(Ci.nsIPermissionManager.ALLOW_ACTION);"/>
+ </hbox>
+ <separator class="thin"/>
+ <tree id="permissionsTree"
+ flex="1"
+ style="height: 10em;"
+ hidecolumnpicker="true"
+ onkeypress="HandlePermissionKeyPress(event);"
+ onselect="PermissionSelected(this);"
+ sortAscending="true"
+ sortColumn="host"
+ persist="sortAscending sortColumn">
+ <treecols>
+ <treecol id="host"
+ label="&treehead.sitename.label;"
+ flex="5"
+ onclick="PermissionColumnSort(this.id, true);"
+ sortDirection="ascending"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="scheme"
+ label="&treehead.scheme.label;"
+ flex="5"
+ onclick="PermissionColumnSort(this.id, true);"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="capability"
+ label="&treehead.status.label;"
+ flex="5"
+ onclick="PermissionColumnSort(this.id, true);"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox>
+ <button id="removePermission"
+ disabled="true"
+ label="&removepermission.label;"
+ accesskey="&removepermission.accesskey;"
+ oncommand="DeletePermission();"/>
+ <button id="removeAllPermissions"
+ label="&removeallpermissions.label;"
+ accesskey="&removeallpermissions.accesskey;"
+ oncommand="DeleteAllPermissions();"/>
+ </hbox>
+ </vbox>
+
+ </tabpanels>
+ </tabbox>
+</dialog>
diff --git a/comm/suite/components/permissions/content/permissionsManager.js b/comm/suite/components/permissions/content/permissionsManager.js
new file mode 100644
index 0000000000..9004dc3cda
--- /dev/null
+++ b/comm/suite/components/permissions/content/permissionsManager.js
@@ -0,0 +1,287 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+var permissions = [];
+var removals = [];
+
+var sortColumn;
+var sortAscending;
+
+var permissionsTreeView = {
+ rowCount: 0,
+ setTree: function(tree) {},
+ getImageSrc: function(row, column) {},
+ getProgressMode: function(row, column) {},
+ getCellValue: function(row, column) {},
+ getCellText: function(row, column) { return permissions[row][column.id]; },
+ isSeparator: function(index) { return false; },
+ isSorted: function() { return false; },
+ isContainer: function(index) { return false; },
+ cycleHeader: function(column) {},
+ getRowProperties: function(row, column) { return ""; },
+ getColumnProperties: function(column) { return ""; },
+ getCellProperties: function(row, column) { return ""; }
+ };
+
+var permissionsTree;
+var permissionType = "popup";
+var gManageCapability;
+
+var permissionsBundle;
+
+function Startup() {
+ var introText, windowTitle;
+
+ permissionsTree = document.getElementById("permissionsTree");
+
+ permissionsBundle = document.getElementById("permissionsBundle");
+
+ sortAscending = (permissionsTree.getAttribute("sortAscending") == "true");
+ sortColumn = permissionsTree.getAttribute("sortColumn");
+
+ var params = { blockVisible : true,
+ sessionVisible : true,
+ allowVisible : true,
+ manageCapability : true
+ };
+
+ if (window.arguments && window.arguments[0]) {
+ params = window.arguments[0];
+ setHost(params.prefilledHost);
+ permissionType = params.permissionType;
+ gManageCapability = params.manageCapability;
+ introText = params.introText;
+ windowTitle = params.windowTitle;
+ }
+
+ document.getElementById("btnBlock").hidden = !params.blockVisible;
+ document.getElementById("btnSession").hidden = !params.sessionVisible;
+ document.getElementById("btnAllow").hidden = !params.allowVisible;
+
+ document.getElementById("permissionsText").textContent = introText ||
+ permissionsBundle.getString(permissionType + "permissionstext");
+
+ document.title = windowTitle ||
+ permissionsBundle.getString(permissionType + "permissionstitle");
+
+ var dialogElement = document.getElementById("permissionsManager");
+ dialogElement.setAttribute("windowtype", "permissions-" + permissionType);
+
+ var urlFieldVisible = params.blockVisible ||
+ params.sessionVisible ||
+ params.allowVisible;
+
+ document.getElementById("url").hidden = !urlFieldVisible;
+ document.getElementById("urlLabel").hidden = !urlFieldVisible;
+
+ handleHostInput(document.getElementById("url").value);
+ loadPermissions();
+}
+
+function onAccept() {
+ finalizeChanges();
+ reInitialize();
+
+ // Don't close the window.
+ return false;
+}
+
+function onCancel() {
+ reInitialize();
+
+ // Don't close the window.
+ return false;
+}
+
+function reInitialize() {
+ permissions = [];
+ removals = [];
+
+ // loadPermissions will reverse the sort direction so flip it now.
+ sortAscending = !sortAscending;
+
+ // Reload permissions tree.
+ loadPermissions();
+}
+
+function setHost(aHost) {
+ document.getElementById("url").value = aHost;
+}
+
+function Permission(id, principal, host, type, capability, perm) {
+ this.id = id;
+ this.principal = principal;
+ this.host = host;
+ this.rawHost = host.replace(/^\./, "");
+ this.type = type;
+ this.capability = capability;
+ this.perm = perm;
+}
+
+function loadPermissions() {
+ var enumerator = Services.perms.enumerator;
+ var count = 0;
+ var permission;
+
+ try {
+ while (enumerator.hasMoreElements()) {
+ permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+ if (permission.type == permissionType &&
+ (!gManageCapability || permission.capability == gManageCapability)) {
+ permissions.push(new Permission(count++,
+ permission.principal,
+ permission.principal.URI.host,
+ permission.type,
+ capabilityString(permission.capability),
+ permission.capability));
+ }
+ }
+ } catch(ex) {
+ }
+
+ permissionsTreeView.rowCount = permissions.length;
+
+ // sort and display the table
+ permissionsTree.view = permissionsTreeView;
+ permissionColumnSort(sortColumn, false);
+
+ // disable "remove all" button if there are none
+ document.getElementById("removeAllPermissions").disabled =
+ permissions.length == 0;
+}
+
+function capabilityString(aCapability) {
+ var capability = null;
+ switch (aCapability) {
+ case Ci.nsIPermissionManager.ALLOW_ACTION:
+ capability = "can";
+ break;
+ case Ci.nsIPermissionManager.DENY_ACTION:
+ capability = "cannot";
+ break;
+ // we should only ever hit this for cookies
+ case Ci.nsICookiePermission.ACCESS_SESSION:
+ capability = "canSession";
+ break;
+ default:
+ break;
+ }
+ return permissionsBundle.getString(capability);
+}
+
+function permissionColumnSort(aColumn, aUpdateSelection) {
+ sortAscending =
+ SortTree(permissionsTree, permissionsTreeView, permissions,
+ aColumn, sortColumn, sortAscending, aUpdateSelection);
+ sortColumn = aColumn;
+
+ SetSortDirection(permissionsTree, aColumn, sortAscending);
+}
+
+function deletePermissions() {
+ DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView,
+ permissions, removals,
+ "removePermission", "removeAllPermissions");
+}
+
+function deleteAllPermissions() {
+ DeleteAllFromTree(permissionsTree, permissionsTreeView, permissions,
+ removals, "removePermission", "removeAllPermissions");
+}
+
+function finalizeChanges() {
+ let p;
+
+ for (let i in permissions) {
+ p = permissions[i];
+ try {
+ // Principal is null so a permission we just added in this session.
+ if (p.principal == null) {
+ let uri = Services.io.newURI("https://" + p.host);
+ Services.perms.add(uri, p.type, p.perm);
+ }
+ } catch(ex) {
+ }
+ }
+
+ for (let i in removals) {
+ p = removals[i];
+ try {
+ // Principal is not null so not a permission we just added in this
+ // session.
+ if (p.principal) {
+ Services.perms.removeFromPrincipal(p.principal,
+ p.type);
+ }
+ } catch(ex) {
+ }
+ }
+}
+
+function handlePermissionKeyPress(e) {
+ if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ deletePermissions();
+ }
+}
+
+function addPermission(aPermission) {
+ var textbox = document.getElementById("url");
+ // trim any leading and trailing spaces and scheme
+ var host = trimSpacesAndScheme(textbox.value);
+ try {
+ let uri = Services.io.newURI("https://" + host);
+ host = uri.host;
+ } catch(ex) {
+ var message = permissionsBundle.getFormattedString("alertInvalid", [host]);
+ var title = permissionsBundle.getString("alertInvalidTitle");
+ Services.prompt.alert(window, title, message);
+ textbox.value = "";
+ textbox.focus();
+ handleHostInput("");
+ return;
+ }
+
+ // we need this whether the perm exists or not
+ var stringCapability = capabilityString(aPermission);
+
+ // check whether the permission already exists, if not, add it
+ var exists = false;
+ for (var i in permissions) {
+ if (permissions[i].rawHost == host) {
+ // Avoid calling the permission manager if the capability settings are
+ // the same. Otherwise allow the call to the permissions manager to
+ // update the listbox for us.
+ exists = permissions[i].perm == aPermission;
+ break;
+ }
+ }
+
+ if (!exists) {
+ permissions.push(new Permission(permissions.length, null, host,
+ permissionType, stringCapability,
+ aPermission));
+
+ permissionsTreeView.rowCount = permissions.length;
+ permissionsTree.treeBoxObject.rowCountChanged(permissions.length - 1, 1);
+ permissionsTree.treeBoxObject.ensureRowIsVisible(permissions.length - 1);
+ }
+ textbox.value = "";
+ textbox.focus();
+
+ // covers a case where the site exists already, so the buttons don't disable
+ handleHostInput("");
+
+ // enable "remove all" button as needed
+ document.getElementById("removeAllPermissions").disabled = permissions.length == 0;
+}
+
+function doHelpButton() {
+ openHelp(permissionsBundle.getString(permissionType + "permissionshelp"), "chrome://communicator/locale/help/suitehelp.rdf");
+ return true;
+}
diff --git a/comm/suite/components/permissions/content/permissionsManager.xul b/comm/suite/components/permissions/content/permissionsManager.xul
new file mode 100644
index 0000000000..4231fd1a96
--- /dev/null
+++ b/comm/suite/components/permissions/content/permissionsManager.xul
@@ -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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/permissions/permissionsManager.dtd" >
+
+<dialog id="permissionsManager"
+ buttons="accept,cancel,help"
+ windowtype="exceptions"
+ title="&windowtitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width:32em; height:42em;"
+ persist="width height screenX screenY"
+ onload="Startup();"
+ ondialogaccept="return onAccept();"
+ ondialogcancel="return onCancel();"
+ ondialoghelp="return doHelpButton();">
+
+ <script src="chrome://communicator/content/permissions/permissionsManager.js"/>
+ <script src="chrome://communicator/content/permissions/permissionsUtils.js"/>
+ <script src="chrome://global/content/treeUtils.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+
+ <stringbundle id="permissionsBundle"
+ src="chrome://communicator/locale/permissions/permissionsManager.properties"/>
+
+ <description id="permissionsText"/>
+ <separator class="thin"/>
+ <label id="urlLabel"
+ value="&address.label;"
+ accesskey="&address.accesskey;"
+ control="url"/>
+ <hbox align="start">
+ <textbox id="url" flex="1" oninput="handleHostInput(event.target.value);"/>
+ </hbox>
+ <hbox pack="end">
+ <button id="btnBlock" disabled="true" accesskey="&block.accesskey;"
+ label="&block.label;" oncommand="addPermission(Ci.nsIPermissionManager.DENY_ACTION);"/>
+ <button id="btnSession" disabled="true" accesskey="&session.accesskey;"
+ label="&session.label;" oncommand="addPermission(Ci.nsICookiePermission.ACCESS_SESSION);"/>
+ <button id="btnAllow" disabled="true" accesskey="&allow.accesskey;"
+ label="&allow.label;" oncommand="addPermission(Ci.nsIPermissionManager.ALLOW_ACTION);"/>
+ </hbox>
+ <separator class="thin"/>
+ <tree id="permissionsTree" flex="1" style="height: 18em;"
+ hidecolumnpicker="true"
+ onkeypress="handlePermissionKeyPress(event)"
+ onselect="PermissionSelected(this);"
+ sortAscending="false"
+ sortColumn="rawHost"
+ persist="sortAscending sortColumn">
+ <treecols>
+ <treecol id="rawHost"
+ label="&treehead.sitename.label;"
+ flex="3"
+ onclick="permissionColumnSort(this.id, true);"
+ sortDirection="descending"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="capability"
+ label="&treehead.status.label;"
+ flex="1"
+ onclick="permissionColumnSort(this.id, true);"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <separator class="thin"/>
+ <hbox>
+ <button id="removePermission" disabled="true"
+ label="&remove.label;" accesskey="&remove.accesskey;"
+ oncommand="deletePermissions();"/>
+ <button id="removeAllPermissions"
+ label="&removeall.label;" accesskey="&removeall.accesskey;"
+ oncommand="deleteAllPermissions();"/>
+ </hbox>
+</dialog>
diff --git a/comm/suite/components/permissions/content/permissionsUtils.js b/comm/suite/components/permissions/content/permissionsUtils.js
new file mode 100644
index 0000000000..4b208f87da
--- /dev/null
+++ b/comm/suite/components/permissions/content/permissionsUtils.js
@@ -0,0 +1,130 @@
+/* -*- 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/. */
+
+function DeleteAllFromTree
+ (tree, view, table, deletedTable, removeButton, removeAllButton) {
+
+ gTreeUtils.deleteAll(tree, view, table, deletedTable);
+
+ // disable buttons
+ document.getElementById(removeButton).setAttribute("disabled", "true")
+ document.getElementById(removeAllButton).setAttribute("disabled","true");
+}
+
+function DeleteSelectedItemFromTree
+ (tree, view, table, deletedTable, removeButton, removeAllButton) {
+
+ gTreeUtils.deleteSelectedItems(tree, view, table, deletedTable);
+
+ // disable buttons if nothing left in the table
+ if (!table.length) {
+ document.getElementById(removeButton).setAttribute("disabled", "true")
+ document.getElementById(removeAllButton).setAttribute("disabled","true");
+ }
+}
+
+function GetTreeSelections(tree) {
+ var selections = [];
+ var select = tree.view.selection;
+ if (select) {
+ var count = select.getRangeCount();
+ var min = new Object();
+ var max = new Object();
+ for (var i=0; i<count; i++) {
+ select.getRangeAt(i, min, max);
+ for (var k=min.value; k<=max.value; k++) {
+ if (k != -1) {
+ selections[selections.length] = k;
+ }
+ }
+ }
+ }
+ return selections;
+}
+
+function SortTree(tree, view, table, column, lastSortColumn, lastSortAscending, updateSelection) {
+
+ // remember which item was selected so we can restore it after the sort
+ var selections = GetTreeSelections(tree);
+ var selectedNumber = selections.length ? table[selections[0]].id : -1;
+
+ // do the sort or re-sort
+ // this is a temporary hack for 1.7, we should implement
+ // display and sort variables here for trees in general
+ var sortColumn;
+ var comparator;
+ if (column == "expires") {
+ sortColumn = "expiresSortValue";
+ comparator = function compare(a, b) { return a - b; };
+ } else {
+ sortColumn = column;
+ comparator = function compare(a, b) {
+ return a.toLowerCase().localeCompare(b.toLowerCase());
+ };
+ }
+ if (lastSortColumn == "expires") {
+ lastSortColumn = "expiresSortValue";
+ }
+ var ascending = gTreeUtils.sort(tree, view, table, sortColumn, comparator,
+ lastSortColumn, lastSortAscending);
+
+ // restore the selection
+ if (selectedNumber >= 0 && updateSelection) {
+ var selectedRow = -1;
+ for (var s = 0; s < table.length; s++) {
+ if (table[s].id == selectedNumber) {
+ selectedRow = s;
+ break;
+ }
+ }
+
+ if (selectedRow > 0) {
+ // update selection and display the results
+ tree.view.selection.select(selectedRow);
+ tree.treeBoxObject.invalidate();
+ tree.treeBoxObject.ensureRowIsVisible(selectedRow);
+ }
+ }
+
+ return ascending;
+}
+
+function handleHostInput(aValue) {
+ // trim any leading and trailing spaces and scheme
+ // and set buttons appropiately
+ btnDisable(!trimSpacesAndScheme(aValue));
+}
+
+function trimSpacesAndScheme(aString) {
+ if (!aString)
+ return "";
+ return aString.trim().replace(/([-\w]*:\/+)?/, "");
+}
+
+function btnDisable(aDisabled) {
+ document.getElementById("btnSession").disabled = aDisabled;
+ document.getElementById("btnBlock").disabled = aDisabled;
+ document.getElementById("btnAllow").disabled = aDisabled;
+}
+
+function PermissionSelected(tree) {
+ var hasSelection = tree.view.selection.count > 0;
+ document.getElementById("removePermission").disabled = !hasSelection;
+}
+
+function SetSortDirection(tree, column, ascending) {
+ // first we need to get the right elements
+ for (let col of tree.getElementsByTagName("treecol")) {
+ if (col.id == column) {
+ // set the sortDirection attribute to get the styling going
+ col.setAttribute("sortDirection", ascending ? "ascending" : "descending");
+ }
+ else {
+ // clear out the sortDirection attribute on the rest of the columns
+ col.removeAttribute("sortDirection");
+ }
+ }
+}
diff --git a/comm/suite/components/permissions/jar.mn b/comm/suite/components/permissions/jar.mn
new file mode 100644
index 0000000000..15784fc80b
--- /dev/null
+++ b/comm/suite/components/permissions/jar.mn
@@ -0,0 +1,10 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/permissions/cookieViewer.js (content/cookieViewer.js)
+ content/communicator/permissions/cookieViewer.xul (content/cookieViewer.xul)
+ content/communicator/permissions/permissionsManager.js (content/permissionsManager.js)
+ content/communicator/permissions/permissionsManager.xul (content/permissionsManager.xul)
+ content/communicator/permissions/permissionsUtils.js (content/permissionsUtils.js)
diff --git a/comm/suite/components/permissions/moz.build b/comm/suite/components/permissions/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/permissions/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/places/PlacesUIUtils.jsm b/comm/suite/components/places/PlacesUIUtils.jsm
new file mode 100644
index 0000000000..5440384212
--- /dev/null
+++ b/comm/suite/components/places/PlacesUIUtils.jsm
@@ -0,0 +1,1499 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["PlacesUIUtils"];
+
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {clearTimeout, setTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+
+Cu.importGlobalProperties(["Element"]);
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+ PluralForm: "resource://gre/modules/PluralForm.jsm",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
+ RecentWindow: "resource:///modules/RecentWindow.jsm",
+ PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
+ PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "bundle", function() {
+ return Services.strings.createBundle("chrome://communicator/locale/places/places.properties");
+});
+
+const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+const FAVICON_REQUEST_TIMEOUT = 60 * 1000;
+// Map from windows to arrays of data about pending favicon loads.
+var gFaviconLoadDataMap = new Map();
+
+const ITEM_CHANGED_BATCH_NOTIFICATION_THRESHOLD = 10;
+
+// copied from utilityOverlay.js
+const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
+
+var InternalFaviconLoader = {
+ /**
+ * This gets called for every inner window that is destroyed.
+ * In the parent process, we process the destruction ourselves. In the child process,
+ * we notify the parent which will then process it based on that message.
+ */
+ observe(subject, topic, data) {
+ let innerWindowID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ this.removeRequestsForInner(innerWindowID);
+ },
+
+ /**
+ * Actually cancel the request, and clear the timeout for cancelling it.
+ */
+ _cancelRequest({uri, innerWindowID, timerID, callback}, reason) {
+ // Break cycle
+ let request = callback.request;
+ delete callback.request;
+ // Ensure we don't time out.
+ clearTimeout(timerID);
+ try {
+ request.cancel();
+ } catch (ex) {
+ Cu.reportError("When cancelling a request for " + uri.spec + " because " + reason + ", it was already canceled!");
+ }
+ },
+
+ /**
+ * Called for every inner that gets destroyed, only in the parent process.
+ */
+ removeRequestsForInner(innerID) {
+ for (let [window, loadDataForWindow] of gFaviconLoadDataMap) {
+ let newLoadDataForWindow = loadDataForWindow.filter(loadData => {
+ let innerWasDestroyed = loadData.innerWindowID == innerID;
+ if (innerWasDestroyed) {
+ this._cancelRequest(loadData, "the inner window was destroyed or a new favicon was loaded for it");
+ }
+ // Keep the items whose inner is still alive.
+ return !innerWasDestroyed;
+ });
+ // Map iteration with for...of is safe against modification, so
+ // now just replace the old value:
+ gFaviconLoadDataMap.set(window, newLoadDataForWindow);
+ }
+ },
+
+ /**
+ * Called when a toplevel chrome window unloads. We use this to tidy up after ourselves,
+ * avoid leaks, and cancel any remaining requests. The last part should in theory be
+ * handled by the inner-window-destroyed handlers. We clean up just to be on the safe side.
+ */
+ onUnload(win) {
+ let loadDataForWindow = gFaviconLoadDataMap.get(win);
+ if (loadDataForWindow) {
+ for (let loadData of loadDataForWindow) {
+ this._cancelRequest(loadData, "the chrome window went away");
+ }
+ }
+ gFaviconLoadDataMap.delete(win);
+ },
+
+ /**
+ * Remove a particular favicon load's loading data from our map tracking
+ * load data per chrome window.
+ *
+ * @param win
+ * the chrome window in which we should look for this load
+ * @param filterData ({innerWindowID, uri, callback})
+ * the data we should use to find this particular load to remove.
+ *
+ * @return the loadData object we removed, or null if we didn't find any.
+ */
+ _removeLoadDataFromWindowMap(win, {innerWindowID, uri, callback}) {
+ let loadDataForWindow = gFaviconLoadDataMap.get(win);
+ if (loadDataForWindow) {
+ let itemIndex = loadDataForWindow.findIndex(loadData => {
+ return loadData.innerWindowID == innerWindowID &&
+ loadData.uri.equals(uri) &&
+ loadData.callback.request == callback.request;
+ });
+ if (itemIndex != -1) {
+ let loadData = loadDataForWindow[itemIndex];
+ loadDataForWindow.splice(itemIndex, 1);
+ return loadData;
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Create a function to use as a nsIFaviconDataCallback, so we can remove cancelling
+ * information when the request succeeds. Note that right now there are some edge-cases,
+ * such as about: URIs with chrome:// favicons where the success callback is not invoked.
+ * This is OK: we will 'cancel' the request after the timeout (or when the window goes
+ * away) but that will be a no-op in such cases.
+ */
+ _makeCompletionCallback(win, id) {
+ return {
+ onComplete(uri) {
+ let loadData = InternalFaviconLoader._removeLoadDataFromWindowMap(win, {
+ uri,
+ innerWindowID: id,
+ callback: this,
+ });
+ if (loadData) {
+ clearTimeout(loadData.timerID);
+ }
+ delete this.request;
+ },
+ };
+ },
+
+ ensureInitialized() {
+ if (this._initialized) {
+ return;
+ }
+ this._initialized = true;
+
+ Services.obs.addObserver(this, "inner-window-destroyed");
+ Services.ppmm.addMessageListener("Toolkit:inner-window-destroyed", msg => {
+ this.removeRequestsForInner(msg.data);
+ });
+ },
+
+ loadFavicon(browser, principal, uri, requestContextID) {
+ this.ensureInitialized();
+ let win = browser.ownerGlobal;
+ if (!gFaviconLoadDataMap.has(win)) {
+ gFaviconLoadDataMap.set(win, []);
+ let unloadHandler = event => {
+ let doc = event.target;
+ let eventWin = doc.defaultView;
+ if (eventWin == win) {
+ win.removeEventListener("unload", unloadHandler);
+ this.onUnload(win);
+ }
+ };
+ win.addEventListener("unload", unloadHandler, true);
+ }
+
+ let {innerWindowID, currentURI} = browser;
+
+ // First we do the actual setAndFetch call:
+ let loadType = PrivateBrowsingUtils.isWindowPrivate(win)
+ ? PlacesUtils.favicons.FAVICON_LOAD_PRIVATE
+ : PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE;
+ let callback = this._makeCompletionCallback(win, innerWindowID);
+ let request = PlacesUtils.favicons.setAndFetchFaviconForPage(currentURI, uri, false,
+ loadType, callback, principal,
+ requestContextID);
+
+ // Now register the result so we can cancel it if/when necessary.
+ if (!request) {
+ // The favicon service can return with success but no-op (and leave request
+ // as null) if the icon is the same as the page (e.g. for images) or if it is
+ // the favicon for an error page. In this case, we do not need to do anything else.
+ return;
+ }
+ callback.request = request;
+ let loadData = {innerWindowID, uri, callback};
+ loadData.timerID = setTimeout(() => {
+ this._cancelRequest(loadData, "it timed out");
+ this._removeLoadDataFromWindowMap(win, loadData);
+ }, FAVICON_REQUEST_TIMEOUT);
+ let loadDataForWindow = gFaviconLoadDataMap.get(win);
+ loadDataForWindow.push(loadData);
+ },
+};
+
+var PlacesUIUtils = {
+ ORGANIZER_LEFTPANE_VERSION: 8,
+ ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
+ ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
+
+ LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
+ DESCRIPTION_ANNO: "bookmarkProperties/description",
+
+ /**
+ * Makes a URI from a spec, and do fixup
+ * @param aSpec
+ * The string spec of the URI
+ * @return A URI object for the spec.
+ */
+ createFixedURI: function PUIU_createFixedURI(aSpec) {
+ return Services.uriFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
+ },
+
+ getFormattedString: function PUIU_getFormattedString(key, params) {
+ return bundle.formatStringFromName(key, params, params.length);
+ },
+
+ /**
+ * Get a localized plural string for the specified key name and numeric value
+ * substituting parameters.
+ *
+ * @param aKey
+ * String, key for looking up the localized string in the bundle
+ * @param aNumber
+ * Number based on which the final localized form is looked up
+ * @param aParams
+ * Array whose items will substitute #1, #2,... #n parameters
+ * in the string.
+ *
+ * @see https://developer.mozilla.org/en/Localization_and_Plurals
+ * @return The localized plural string.
+ */
+ getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) {
+ let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey));
+
+ // Replace #1 with aParams[0], #2 with aParams[1], and so on.
+ return str.replace(/\#(\d+)/g, function(matchedId, matchedNumber) {
+ let param = aParams[parseInt(matchedNumber, 10) - 1];
+ return param !== undefined ? param : matchedId;
+ });
+ },
+
+ getString: function PUIU_getString(key) {
+ return bundle.GetStringFromName(key);
+ },
+
+ /**
+ * Shows the bookmark dialog corresponding to the specified info.
+ *
+ * @param aInfo
+ * Describes the item to be edited/added in the dialog.
+ * See documentation at the top of bookmarkProperties.js
+ * @param aWindow
+ * Owner window for the new dialog.
+ *
+ * @see documentation at the top of bookmarkProperties.js
+ * @return true if any transaction has been performed, false otherwise.
+ */
+ showBookmarkDialog(aInfo, aParentWindow) {
+ // Preserve size attributes differently based on the fact the dialog has
+ // a folder picker or not, since it needs more horizontal space than the
+ // other controls.
+ let hasFolderPicker = !("hiddenRows" in aInfo) ||
+ !aInfo.hiddenRows.includes("folderPicker");
+ // Use a different chrome url to persist different sizes.
+ let dialogURL = hasFolderPicker ?
+ "chrome://communicator/content/places/bookmarkProperties2.xul" :
+ "chrome://communicator/content/places/bookmarkProperties.xul";
+
+ let features = "centerscreen,chrome,modal,resizable=yes";
+
+ let topUndoEntry;
+ let batchBlockingDeferred;
+
+ // Set the transaction manager into batching mode.
+ topUndoEntry = PlacesTransactions.topUndoEntry;
+ batchBlockingDeferred = PromiseUtils.defer();
+ PlacesTransactions.batch(async () => {
+ await batchBlockingDeferred.promise;
+ });
+
+ aParentWindow.openDialog(dialogURL, "", features, aInfo);
+
+ let performed = ("performed" in aInfo && aInfo.performed);
+
+ batchBlockingDeferred.resolve();
+
+ if (!performed &&
+ topUndoEntry != PlacesTransactions.topUndoEntry) {
+ PlacesTransactions.undo().catch(Cu.reportError);
+ }
+
+ return performed;
+ },
+
+ /**
+ * set and fetch a favicon. Can only be used from the parent process.
+ * @param browser {Browser} The XUL browser element for which we're fetching a favicon.
+ * @param principal {Principal} The loading principal to use for the fetch.
+ * @param uri {URI} The URI to fetch.
+ */
+ loadFavicon(browser, principal, uri, requestContextID) {
+ if (gInContentProcess) {
+ throw new Error("Can't track loads from within the child process!");
+ }
+ InternalFaviconLoader.loadFavicon(browser, principal, uri, requestContextID);
+ },
+
+ /**
+ * Returns the closet ancestor places view for the given DOM node
+ * @param aNode
+ * a DOM node
+ * @return the closet ancestor places view if exists, null otherwsie.
+ */
+ getViewForNode: function PUIU_getViewForNode(aNode) {
+ let node = aNode;
+
+ if (node.localName == "panelview" && node._placesView) {
+ return node._placesView;
+ }
+
+ // The view for a <menu> of which its associated menupopup is a places
+ // view, is the menupopup.
+ if (node.localName == "menu" && !node._placesNode &&
+ node.lastChild._placesView)
+ return node.lastChild._placesView;
+
+ while (Element.isInstance(node)) {
+ if (node._placesView)
+ return node._placesView;
+ if (node.localName == "tree" && node.getAttribute("type") == "places")
+ return node;
+
+ node = node.parentNode;
+ }
+
+ return null;
+ },
+
+ /**
+ * Returns the active PlacesController for a given command.
+ *
+ * @param win The window containing the affected view
+ * @param command The command
+ * @return a PlacesController
+ */
+ getControllerForCommand(win, command) {
+ // A context menu may be built for non-focusable views. Thus, we first try
+ // to look for a view associated with document.popupNode
+ let popupNode;
+ try {
+ popupNode = win.document.popupNode;
+ } catch (e) {
+ // The document went away (bug 797307).
+ return null;
+ }
+ if (popupNode) {
+ let view = this.getViewForNode(popupNode);
+ if (view && view._contextMenuShown)
+ return view.controllers.getControllerForCommand(command);
+ }
+
+ // When we're not building a context menu, only focusable views
+ // are possible. Thus, we can safely use the command dispatcher.
+ let controller = win.top.document.commandDispatcher
+ .getControllerForCommand(command);
+ return controller || null;
+ },
+
+ /**
+ * Update all the Places commands for the given window.
+ *
+ * @param win The window to update.
+ */
+ updateCommands(win) {
+ // Get the controller for one of the places commands.
+ let controller = this.getControllerForCommand(win, "placesCmd_open");
+ for (let command of [
+ "placesCmd_open",
+ "placesCmd_open:window",
+ "placesCmd_open:privatewindow",
+ "placesCmd_open:tab",
+ "placesCmd_new:folder",
+ "placesCmd_new:bookmark",
+ "placesCmd_new:separator",
+ "placesCmd_show:info",
+ "placesCmd_reload",
+ "placesCmd_sortBy:name",
+ "placesCmd_cut",
+ "placesCmd_copy",
+ "placesCmd_paste",
+ "placesCmd_delete",
+ ]) {
+ win.goSetCommandEnabled(command,
+ controller && controller.isCommandEnabled(command));
+ }
+ },
+
+ /**
+ * Executes the given command on the currently active controller.
+ *
+ * @param win The window containing the affected view
+ * @param command The command to execute
+ */
+ doCommand(win, command) {
+ let controller = this.getControllerForCommand(win, command);
+ if (controller && controller.isCommandEnabled(command))
+ controller.doCommand(command);
+ },
+
+ /**
+ * By calling this before visiting an URL, the visit will be associated to a
+ * TRANSITION_TYPED transition (if there is no a referrer).
+ * This is used when visiting pages from the history menu, history sidebar,
+ * url bar, url autocomplete results, and history searches from the places
+ * organizer. If this is not called visits will be marked as
+ * TRANSITION_LINK.
+ */
+ markPageAsTyped: function PUIU_markPageAsTyped(aURL) {
+ PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL));
+ },
+
+ /**
+ * By calling this before visiting an URL, the visit will be associated to a
+ * TRANSITION_BOOKMARK transition.
+ * This is used when visiting pages from the bookmarks menu,
+ * personal toolbar, and bookmarks from within the places organizer.
+ * If this is not called visits will be marked as TRANSITION_LINK.
+ */
+ markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) {
+ PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
+ },
+
+ /**
+ * By calling this before visiting an URL, any visit in frames will be
+ * associated to a TRANSITION_FRAMED_LINK transition.
+ * This is actually used to distinguish user-initiated visits in frames
+ * so automatic visits can be correctly ignored.
+ */
+ markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) {
+ PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL));
+ },
+
+ /**
+ * Allows opening of javascript/data URI only if the given node is
+ * bookmarked (see bug 224521).
+ * @param aURINode
+ * a URI node
+ * @param aWindow
+ * a window on which a potential error alert is shown on.
+ * @return true if it's safe to open the node in the browser, false otherwise.
+ *
+ */
+ checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) {
+ if (PlacesUtils.nodeIsBookmark(aURINode))
+ return true;
+
+ var uri = Services.io.newURI(aURINode.uri);
+ if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
+ const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
+ var brandShortName = Services.strings
+ .createBundle(BRANDING_BUNDLE_URI)
+ .GetStringFromName("brandShortName");
+
+ var errorStr = this.getString("load-js-data-url-error");
+ Services.prompt.alert(aWindow, brandShortName, errorStr);
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Get the description associated with a document, as specified in a <META>
+ * element.
+ * @param doc
+ * A DOM Document to get a description for
+ * @return A description string if a META element was discovered with a
+ * "description" or "httpequiv" attribute, empty string otherwise.
+ */
+ getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) {
+ var metaElements = doc.getElementsByTagName("META");
+ for (var i = 0; i < metaElements.length; ++i) {
+ if (metaElements[i].name.toLowerCase() == "description" ||
+ metaElements[i].httpEquiv.toLowerCase() == "description") {
+ return metaElements[i].content;
+ }
+ }
+ return "";
+ },
+
+ /**
+ * Retrieve the description of an item
+ * @param aItemId
+ * item identifier
+ * @return the description of the given item, or an empty string if it is
+ * not set.
+ */
+ getItemDescription: function PUIU_getItemDescription(aItemId) {
+ if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO))
+ return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO);
+ return "";
+ },
+
+ /**
+ * Check whether or not the given node represents a removable entry (either in
+ * history or in bookmarks).
+ *
+ * @param aNode
+ * a node, except the root node of a query.
+ * @param aView
+ * The view originating the request.
+ * @return true if the aNode represents a removable entry, false otherwise.
+ */
+ canUserRemove(aNode, aView) {
+ let parentNode = aNode.parent;
+ if (!parentNode) {
+ // canUserRemove doesn't accept root nodes.
+ return false;
+ }
+
+ // Is it a query pointing to one of the special root folders?
+ if (PlacesUtils.nodeIsQuery(parentNode) && PlacesUtils.nodeIsFolder(aNode)) {
+ let guid = PlacesUtils.getConcreteItemGuid(aNode);
+ // If the parent folder is not a folder, it must be a query, and so this node
+ // cannot be removed.
+ if (PlacesUtils.isRootItem(guid)) {
+ return false;
+ }
+ }
+
+ // If it's not a bookmark, we can remove it unless it's a child of a
+ // livemark.
+ if (aNode.itemId == -1) {
+ // Rather than executing a db query, checking the existence of the feedURI
+ // annotation, detect livemark children by the fact that they are the only
+ // direct non-bookmark children of bookmark folders.
+ return !PlacesUtils.nodeIsFolder(parentNode);
+ }
+
+ // Generally it's always possible to remove children of a query.
+ if (PlacesUtils.nodeIsQuery(parentNode))
+ return true;
+
+ // Otherwise it has to be a child of an editable folder.
+ return !this.isFolderReadOnly(parentNode, aView);
+ },
+
+ /**
+ * DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH
+ * TO GUIDS IS COMPLETE (BUG 1071511).
+ *
+ * Check whether or not the given Places node points to a folder which
+ * should not be modified by the user (i.e. its children should be unremovable
+ * and unmovable, new children should be disallowed, etc).
+ * These semantics are not inherited, meaning that read-only folder may
+ * contain editable items (for instance, the places root is read-only, but all
+ * of its direct children aren't).
+ *
+ * You should only pass folder nodes.
+ *
+ * @param placesNode
+ * any folder result node.
+ * @param view
+ * The view originating the request.
+ * @throws if placesNode is not a folder result node or views is invalid.
+ * @note livemark "folders" are considered read-only (but see bug 1072833).
+ * @return true if placesNode is a read-only folder, false otherwise.
+ */
+ isFolderReadOnly(placesNode, view) {
+ if (typeof placesNode != "object" || !PlacesUtils.nodeIsFolder(placesNode)) {
+ throw new Error("invalid value for placesNode");
+ }
+ if (!view || typeof view != "object") {
+ throw new Error("invalid value for aView");
+ }
+ let itemId = PlacesUtils.getConcreteItemId(placesNode);
+ if (itemId == PlacesUtils.placesRootId ||
+ view.controller.hasCachedLivemarkInfo(placesNode))
+ return true;
+
+ // leftPaneFolderId is a lazy getter
+ // performing at least a synchronous DB query (and on its very first call
+ // in a fresh profile, it also creates the entire structure).
+ // Therefore we don't want to this function, which is called very often by
+ // isCommandEnabled, to ever be the one that invokes it first, especially
+ // because isCommandEnabled may be called way before the left pane folder is
+ // even created (for example, if the user only uses the bookmarks menu or
+ // toolbar for managing bookmarks). To do so, we avoid comparing to those
+ // special folder if the lazy getter is still in place. This is safe merely
+ // because the only way to access the left pane contents goes through
+ // "resolving" the leftPaneFolderId getter.
+ if (typeof Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").get == "function") {
+ return false;
+ }
+ return itemId == this.leftPaneFolderId;
+ },
+
+ /** aItemsToOpen needs to be an array of objects of the form:
+ * {uri: string, isBookmark: boolean}
+ */
+ _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
+ if (!aItemsToOpen.length)
+ return;
+
+ // Prefer the caller window if it's a browser window, otherwise use
+ // the top browser window.
+ var browserWindow = null;
+ browserWindow =
+ aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
+ aWindow : RecentWindow.getMostRecentBrowserWindow();
+
+ var urls = [];
+ let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow);
+ for (let item of aItemsToOpen) {
+ urls.push(item.uri);
+ if (skipMarking) {
+ continue;
+ }
+
+ if (item.isBookmark)
+ this.markPageAsFollowedBookmark(item.uri);
+ else
+ this.markPageAsTyped(item.uri);
+ }
+
+ // whereToOpenLink doesn't return "window" when there's no browser window
+ // open (Bug 630255).
+ var where = browserWindow ?
+ browserWindow.whereToOpenLink(aEvent, false, true) : "window";
+ if (where == "window") {
+ // There is no browser window open, thus open a new one.
+ var uriList = PlacesUtils.toISupportsString(urls.join("|"));
+ var args = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ args.appendElement(uriList);
+ browserWindow = Services.ww.openWindow(aWindow,
+ "chrome://navigator/content/navigator.xul",
+ null, "chrome,dialog=no,all", args);
+ return;
+ }
+
+ var loadInBackground = where == "tabshifted";
+ // For consistency, we want all the bookmarks to open in new tabs, instead
+ // of having one of them replace the currently focused tab. Hence we call
+ // loadTabs with aReplace set to false.
+ browserWindow.gBrowser.loadTabs(urls, {
+ inBackground: loadInBackground,
+ replace: false,
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ });
+ },
+
+ openLiveMarkNodesInTabs:
+ function PUIU_openLiveMarkNodesInTabs(aNode, aEvent, aView) {
+ let window = aView.ownerWindow;
+
+ PlacesUtils.livemarks.getLivemark({id: aNode.itemId})
+ .then(aLivemark => {
+ let urlsToOpen = [];
+
+ let nodes = aLivemark.getNodesForContainer(aNode);
+ for (let node of nodes) {
+ urlsToOpen.push({uri: node.uri, isBookmark: false});
+ }
+
+ if (OpenInTabsUtils.confirmOpenInTabs(urlsToOpen.length, window)) {
+ this._openTabset(urlsToOpen, aEvent, window);
+ }
+ }, Cu.reportError);
+ },
+
+ openContainerNodeInTabs:
+ function PUIU_openContainerInTabs(aNode, aEvent, aView) {
+ let window = aView.ownerWindow;
+
+ let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
+ if (OpenInTabsUtils.confirmOpenInTabs(urlsToOpen.length, window)) {
+ this._openTabset(urlsToOpen, aEvent, window);
+ }
+ },
+
+ openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) {
+ let window = aView.ownerWindow;
+
+ let urlsToOpen = [];
+ for (var i = 0; i < aNodes.length; i++) {
+ // Skip over separators and folders.
+ if (PlacesUtils.nodeIsURI(aNodes[i]))
+ urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
+ }
+ this._openTabset(urlsToOpen, aEvent, window);
+ },
+
+ /**
+ * Loads the node's URL in the appropriate tab or window or as a web
+ * panel given the user's preference specified by modifier keys tracked by a
+ * DOM mouse/key event.
+ * @param aNode
+ * An uri result node.
+ * @param aEvent
+ * The DOM mouse/key event with modifier keys set that track the
+ * user's preferred destination window or tab.
+ * @param aExternal
+ * Called from the library window or an external application.
+ * Link handling for external applications will apply when true.
+ */
+ openNodeWithEvent:
+ function PUIU_openNodeWithEvent(aNode, aEvent, aExternal = false) {
+ let window = aEvent.target.ownerGlobal;
+ let whereTo;
+ if (aExternal) {
+ let openParms = window.whereToLoadExternalLink();
+ whereTo = openParms.where;
+ }
+ else {
+ whereTo = window.whereToOpenLink(aEvent, false, true);
+ }
+ this._openNodeIn(aNode, whereTo, window);
+ },
+
+ /**
+ * Loads the node's URL in the appropriate tab or window or as a
+ * web panel.
+ * see also openUILinkIn
+ */
+ openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) {
+ let window = aView.ownerWindow;
+ this._openNodeIn(aNode, aWhere, window, aPrivate);
+ },
+
+ _openNodeIn: function PUIU__openNodeIn(aNode, aWhere, aWindow, aPrivate = false) {
+ if (aNode && PlacesUtils.nodeIsURI(aNode) &&
+ this.checkURLSecurity(aNode, aWindow)) {
+ let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
+
+ if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
+ if (isBookmark)
+ this.markPageAsFollowedBookmark(aNode.uri);
+ else
+ this.markPageAsTyped(aNode.uri);
+ }
+
+ // Check whether the node is a bookmark which should be opened as
+ // a web panel
+ // Currently not supported in SeaMonkey. Please stay tuned.
+ // if (aWhere == "current" && isBookmark) {
+ // if (PlacesUtils.annotations
+ // .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
+ // let browserWin = this._getTopBrowserWin();
+ // if (browserWin) {
+ // browserWin.openWebPanel(aNode.title, aNode.uri);
+ // return;
+ // }
+ // }
+ // }
+
+ aWindow.openUILinkIn(aNode.uri, aWhere, {
+ allowPopups: aNode.uri.startsWith("javascript:"),
+ inBackground: Services.prefs.getBoolPref("browser.tabs.avoidBrowserFocus"),
+ aNoReferrer: true,
+ private: aPrivate,
+ });
+ }
+ },
+
+ /**
+ * Helper for guessing scheme from an url string.
+ * Used to avoid nsIURI overhead in frequently called UI functions.
+ *
+ * @param aUrlString the url to guess the scheme from.
+ *
+ * @return guessed scheme for this url string.
+ *
+ * @note this is not supposed be perfect, so use it only for UI purposes.
+ */
+ guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
+ return aUrlString.substr(0, aUrlString.indexOf(":"));
+ },
+
+ getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) {
+ var title;
+ if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) {
+ // if node title is empty, try to set the label using host and filename
+ // Services.io.newURI() will throw if aNode.uri is not a valid URI
+ try {
+ var uri = Services.io.newURI(aNode.uri);
+ var host = uri.host;
+ var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
+ // if fileName is empty, use path to distinguish labels
+ if (aDoNotCutTitle) {
+ title = host + uri.pathQueryRef;
+ } else {
+ title = host + (fileName ?
+ (host ? "/" + this.ellipsis + "/" : "") + fileName :
+ uri.pathQueryRef);
+ }
+ } catch (e) {
+ // Use (no title) for non-standard URIs (data:, javascript:, ...)
+ title = "";
+ }
+ } else
+ title = aNode.title;
+
+ return title || this.getString("noTitle");
+ },
+
+ get leftPaneQueries() {
+ // build the map
+ this.leftPaneFolderId;
+ return this.leftPaneQueries;
+ },
+
+ get leftPaneFolderId() {
+ delete this.leftPaneFolderId;
+ return this.leftPaneFolderId = this.maybeRebuildLeftPane();
+ },
+
+ // Get the folder id for the organizer left-pane folder.
+ maybeRebuildLeftPane() {
+ let leftPaneRoot = -1;
+
+ // Shortcuts to services.
+ let bs = PlacesUtils.bookmarks;
+ let as = PlacesUtils.annotations;
+
+ // This is the list of the left pane queries.
+ let queries = {
+ "PlacesRoot": { title: "" },
+ "History": { title: this.getString("OrganizerQueryHistory") },
+ "Tags": { title: this.getString("OrganizerQueryTags") },
+ "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
+ };
+ // All queries but PlacesRoot.
+ const EXPECTED_QUERY_COUNT = 3;
+
+ // Removes an item and associated annotations, ignoring eventual errors.
+ function safeRemoveItem(aItemId) {
+ try {
+ if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
+ !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
+ // Some extension annotated their roots with our query annotation,
+ // so we should not delete them.
+ return;
+ }
+ // removeItemAnnotation does not check if item exists, nor the anno,
+ // so this is safe to do.
+ as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO);
+ // This will throw if the annotation is an orphan.
+ bs.removeItem(aItemId);
+ } catch (e) { /* orphan anno */ }
+ }
+
+ // Returns true if item really exists, false otherwise.
+ function itemExists(aItemId) {
+ try {
+ bs.getFolderIdForItem(aItemId);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ // Get all items marked as being the left pane folder.
+ let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO);
+ if (items.length > 1) {
+ // Something went wrong, we cannot have more than one left pane folder,
+ // remove all left pane folders and continue. We will create a new one.
+ items.forEach(safeRemoveItem);
+ } else if (items.length == 1 && items[0] != -1) {
+ leftPaneRoot = items[0];
+ // Check that organizer left pane root is valid.
+ let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO);
+ if (version != this.ORGANIZER_LEFTPANE_VERSION ||
+ !itemExists(leftPaneRoot)) {
+ // Invalid root, we must rebuild the left pane.
+ safeRemoveItem(leftPaneRoot);
+ leftPaneRoot = -1;
+ }
+ }
+
+ if (leftPaneRoot != -1) {
+ // A valid left pane folder has been found.
+ // Build the leftPaneQueries Map. This is used to quickly access them,
+ // associating a mnemonic name to the real item ids.
+ delete this.leftPaneQueries;
+ this.leftPaneQueries = {};
+
+ let queryItems = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO);
+ // While looping through queries we will also check for their validity.
+ let queriesCount = 0;
+ let corrupt = false;
+ for (let i = 0; i < queryItems.length; i++) {
+ let queryName = as.getItemAnnotation(queryItems[i], this.ORGANIZER_QUERY_ANNO);
+
+ // Some extension did use our annotation to decorate their items
+ // with icons, so we should check only our elements, to avoid dataloss.
+ if (!(queryName in queries))
+ continue;
+
+ let query = queries[queryName];
+ query.itemId = queryItems[i];
+
+ if (!itemExists(query.itemId)) {
+ // Orphan annotation, bail out and create a new left pane root.
+ corrupt = true;
+ break;
+ }
+
+ // Check that all queries have valid parents.
+ let parentId = bs.getFolderIdForItem(query.itemId);
+ if (!queryItems.includes(parentId) && parentId != leftPaneRoot) {
+ // The parent is not part of the left pane, bail out and create a new
+ // left pane root.
+ corrupt = true;
+ break;
+ }
+
+ // Titles could have been corrupted or the user could have changed his
+ // locale. Check title and eventually fix it.
+ if (bs.getItemTitle(query.itemId) != query.title)
+ bs.setItemTitle(query.itemId, query.title);
+ if ("concreteId" in query) {
+ if (bs.getItemTitle(query.concreteId) != query.concreteTitle)
+ bs.setItemTitle(query.concreteId, query.concreteTitle);
+ }
+
+ // Add the query to our cache.
+ this.leftPaneQueries[queryName] = query.itemId;
+ queriesCount++;
+ }
+
+ // Note: it's not enough to just check for queriesCount, since we may
+ // find an invalid query just after accounting for a sufficient number of
+ // valid ones. As well as we can't just rely on corrupt since we may find
+ // less valid queries than expected.
+ if (corrupt || queriesCount != EXPECTED_QUERY_COUNT) {
+ // Queries number is wrong, so the left pane must be corrupt.
+ // Note: we can't just remove the leftPaneRoot, because some query could
+ // have a bad parent, so we have to remove all items one by one.
+ queryItems.forEach(safeRemoveItem);
+ safeRemoveItem(leftPaneRoot);
+ } else {
+ // Everything is fine, return the current left pane folder.
+ return leftPaneRoot;
+ }
+ }
+
+ // Create a new left pane folder.
+ var callback = {
+ // Helper to create an organizer special query.
+ create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
+ let itemId = bs.insertBookmark(aParentId,
+ Services.io.newURI(aQueryUrl),
+ bs.DEFAULT_INDEX,
+ queries[aQueryName].title);
+ // Mark as special organizer query.
+ as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName,
+ 0, as.EXPIRE_NEVER);
+ // We should never backup this, since it changes between profiles.
+ as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
+ 0, as.EXPIRE_NEVER);
+ // Add to the queries map.
+ PlacesUIUtils.leftPaneQueries[aQueryName] = itemId;
+ return itemId;
+ },
+
+ // Helper to create an organizer special folder.
+ create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
+ // Left Pane Root Folder.
+ let folderId = bs.createFolder(aParentId,
+ queries[aFolderName].title,
+ bs.DEFAULT_INDEX);
+ // We should never backup this, since it changes between profiles.
+ as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
+ 0, as.EXPIRE_NEVER);
+
+ if (aIsRoot) {
+ // Mark as special left pane root.
+ as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
+ PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
+ 0, as.EXPIRE_NEVER);
+ } else {
+ // Mark as special organizer folder.
+ as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName,
+ 0, as.EXPIRE_NEVER);
+ PlacesUIUtils.leftPaneQueries[aFolderName] = folderId;
+ }
+ return folderId;
+ },
+
+ runBatched: function CB_runBatched(aUserData) {
+ delete PlacesUIUtils.leftPaneQueries;
+ PlacesUIUtils.leftPaneQueries = { };
+
+ // Left Pane Root Folder.
+ leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);
+
+ // History Query.
+ this.create_query("History", leftPaneRoot,
+ "place:type=" +
+ Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
+ "&sort=" +
+ Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
+
+ // Tags Query.
+ this.create_query("Tags", leftPaneRoot,
+ "place:type=" +
+ Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
+ "&sort=" +
+ Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
+
+ // All Bookmarks Folder.
+ this.create_query("AllBookmarks", leftPaneRoot,
+ "place:type=" +
+ Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY);
+ }
+ };
+ bs.runInBatchMode(callback, null);
+
+ return leftPaneRoot;
+ },
+
+ /**
+ * If an item is a left-pane query, returns the name of the query
+ * or an empty string if not.
+ *
+ * @param aItemId id of a container
+ * @return the name of the query, or empty string if not a left-pane query
+ */
+ getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) {
+ var queryName = "";
+ // If the let pane hasn't been built, use the annotation service
+ // directly, to avoid building the left pane too early.
+ if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) {
+ try {
+ queryName = PlacesUtils.annotations.
+ getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO);
+ } catch (ex) {
+ // doesn't have the annotation
+ queryName = "";
+ }
+ } else {
+ // If the left pane has already been built, use the name->id map
+ // cached in PlacesUIUtils.
+ for (let [name, id] of Object.entries(this.leftPaneQueries)) {
+ if (aItemId == id)
+ queryName = name;
+ }
+ }
+ return queryName;
+ },
+
+ shouldShowTabsFromOtherComputersMenuitem() {
+ let weaveOK = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED &&
+ Weave.Svc.Prefs.get("firstSync", "") != "notReady";
+ return weaveOK;
+ },
+
+ /**
+ * WARNING TO ADDON AUTHORS: DO NOT USE THIS METHOD. IT'S LIKELY TO BE REMOVED IN A
+ * FUTURE RELEASE.
+ *
+ * Checks if a place: href represents a folder shortcut.
+ *
+ * @param queryString
+ * the query string to check (a place: href)
+ * @return whether or not queryString represents a folder shortcut.
+ * @throws if queryString is malformed.
+ */
+ isFolderShortcutQueryString(queryString) {
+ // Based on GetSimpleBookmarksQueryFolder in nsNavHistory.cpp.
+
+ let queriesParam = { }, optionsParam = { };
+ PlacesUtils.history.queryStringToQueries(queryString,
+ queriesParam,
+ { },
+ optionsParam);
+ let queries = queries.value;
+ if (queries.length == 0)
+ throw new Error(`Invalid place: uri: ${queryString}`);
+ return queries.length == 1 &&
+ queries[0].folderCount == 1 &&
+ !queries[0].hasBeginTime &&
+ !queries[0].hasEndTime &&
+ !queries[0].hasDomain &&
+ !queries[0].hasURI &&
+ !queries[0].hasSearchTerms &&
+ !queries[0].tags.length == 0 &&
+ optionsParam.value.maxResults == 0;
+ },
+
+ /**
+ * @see showAddBookmarkUI
+ * This opens the dialog with only the name and folder pickers visible by
+ * default.
+ *
+ * This is to be used only outside of the SeaMonkey browser part e.g. for
+ * bookmarking in mail and news windows.
+ *
+ * You can still pass in the various paramaters as the default properties
+ * for the new bookmark.
+ *
+ * The keyword field will be visible only if the aKeyword parameter
+ * was used.
+ */
+ showMinimalAddBookmarkUI:
+ function PUIU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription,
+ aDefaultInsertionPoint, aShowPicker,
+ aLoadInSidebar, aKeyword, aPostData,
+ aCharSet) {
+ var info = {
+ action: "add",
+ type: "bookmark",
+ hiddenRows: ["description"]
+ };
+ if (aURI)
+ info.uri = aURI;
+
+ // allow default empty title
+ if (typeof(aTitle) == "string")
+ info.title = aTitle;
+
+ if (aDescription)
+ info.description = aDescription;
+
+ if (aDefaultInsertionPoint) {
+ info.defaultInsertionPoint = aDefaultInsertionPoint;
+ if (!aShowPicker)
+ info.hiddenRows.push("folderPicker");
+ }
+
+ info.hiddenRows = info.hiddenRows.concat(["location"]);
+
+ if (typeof(aKeyword) == "string") {
+ info.keyword = aKeyword;
+ // Hide the Tags field if we are adding a keyword.
+ info.hiddenRows.push("tags");
+ // Keyword related params.
+ if (typeof(aPostData) == "string")
+ info.postData = aPostData;
+ if (typeof(aCharSet) == "string")
+ info.charSet = aCharSet;
+ }
+ else
+ info.hiddenRows.push("keyword");
+
+ return this.showBookmarkDialog(info,
+ focusManager.activeWindow ||
+ Services.wm.getMostRecentWindow(null));
+ },
+
+ /**
+ * Helpers for consumers of editBookmarkOverlay which don't have a node as their input.
+ *
+ * Given a bookmark object for either a url bookmark or a folder, returned by
+ * Bookmarks.fetch (see Bookmark.jsm), this creates a node-like object suitable for
+ * initialising the edit overlay with it.
+ *
+ * @param aFetchInfo
+ * a bookmark object returned by Bookmarks.fetch.
+ * @return a node-like object suitable for initialising editBookmarkOverlay.
+ * @throws if aFetchInfo is representing a separator.
+ */
+ async promiseNodeLikeFromFetchInfo(aFetchInfo) {
+ if (aFetchInfo.itemType == PlacesUtils.bookmarks.TYPE_SEPARATOR)
+ throw new Error("promiseNodeLike doesn't support separators");
+
+ let parent = {
+ itemId: await PlacesUtils.promiseItemId(aFetchInfo.parentGuid),
+ bookmarkGuid: aFetchInfo.parentGuid,
+ type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
+ };
+
+ return Object.freeze({
+ itemId: await PlacesUtils.promiseItemId(aFetchInfo.guid),
+ bookmarkGuid: aFetchInfo.guid,
+ title: aFetchInfo.title,
+ uri: aFetchInfo.url !== undefined ? aFetchInfo.url.href : "",
+
+ get type() {
+ if (aFetchInfo.itemType == PlacesUtils.bookmarks.TYPE_FOLDER)
+ return Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;
+
+ if (this.uri.length == 0)
+ throw new Error("Unexpected item type");
+
+ if (/^place:/.test(this.uri)) {
+ if (this.isFolderShortcutQueryString(this.uri))
+ return Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
+
+ return Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY;
+ }
+
+ return Ci.nsINavHistoryResultNode.RESULT_TYPE_URI;
+ },
+
+ get parent() {
+ return parent;
+ }
+ });
+ },
+
+ /**
+ * This function wraps potentially large places transaction operations
+ * with batch notifications to the result node, hence switching the views
+ * to batch mode.
+ *
+ * @param {nsINavHistoryResult} resultNode The result node to turn on batching.
+ * @note If resultNode is not supplied, the function will pass-through to
+ * functionToWrap.
+ * @param {Integer} itemsBeingChanged The count of items being changed. If the
+ * count is lower than a threshold, then
+ * batching won't be set.
+ * @param {Function} functionToWrap The function to
+ */
+ async batchUpdatesForNode(resultNode, itemsBeingChanged, functionToWrap) {
+ if (!resultNode) {
+ await functionToWrap();
+ return;
+ }
+
+ resultNode = resultNode.QueryInterface(Ci.nsINavBookmarkObserver);
+
+ if (itemsBeingChanged > ITEM_CHANGED_BATCH_NOTIFICATION_THRESHOLD) {
+ resultNode.onBeginUpdateBatch();
+ }
+
+ try {
+ await functionToWrap();
+ } finally {
+ if (itemsBeingChanged > ITEM_CHANGED_BATCH_NOTIFICATION_THRESHOLD) {
+ resultNode.onEndUpdateBatch();
+ }
+ }
+ },
+
+ /**
+ * Constructs a Places Transaction for the drop or paste of a blob of data
+ * into a container.
+ *
+ * @param aData
+ * The unwrapped data blob of dropped or pasted data.
+ * @param aNewParentGuid
+ * GUID of the container the data was dropped or pasted into.
+ * @param aIndex
+ * The index within the container the item was dropped or pasted at.
+ * @param aCopy
+ * The drag action was copy, so don't move folders or links.
+ *
+ * @return a Places Transaction that can be transacted for performing the
+ * move/insert command.
+ */
+ getTransactionForData(aData, aNewParentGuid, aIndex, aCopy) {
+ if (!this.SUPPORTED_FLAVORS.includes(aData.type))
+ throw new Error(`Unsupported '${aData.type}' data type`);
+
+ if ("itemGuid" in aData && "instanceId" in aData &&
+ aData.instanceId == PlacesUtils.instanceId) {
+ if (!this.PLACES_FLAVORS.includes(aData.type))
+ throw new Error(`itemGuid unexpectedly set on ${aData.type} data`);
+
+ let info = { guid: aData.itemGuid,
+ newParentGuid: aNewParentGuid,
+ newIndex: aIndex };
+ if (aCopy) {
+ info.excludingAnnotation = "Places/SmartBookmark";
+ return PlacesTransactions.Copy(info);
+ }
+ return PlacesTransactions.Move(info);
+ }
+
+ // Since it's cheap and harmless, we allow the paste of separators and
+ // bookmarks from builds that use legacy transactions (i.e. when itemGuid
+ // was not set on PLACES_FLAVORS data). Containers are a different story,
+ // and thus disallowed.
+ if (aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER)
+ throw new Error("Can't copy a container from a legacy-transactions build");
+
+ if (aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
+ return PlacesTransactions.NewSeparator({ parentGuid: aNewParentGuid,
+ index: aIndex });
+ }
+
+ let title = aData.type != PlacesUtils.TYPE_UNICODE ? aData.title
+ : aData.uri;
+ return PlacesTransactions.NewBookmark({ url: Services.io.newURI(aData.uri),
+ title,
+ parentGuid: aNewParentGuid,
+ index: aIndex });
+ },
+
+ /**
+ * Processes a set of transfer items that have been dropped or pasted.
+ * Batching will be applied where necessary.
+ *
+ * @param {Array} items A list of unwrapped nodes to process.
+ * @param {Object} insertionPoint The requested point for insertion.
+ * @param {Boolean} doCopy Set to true to copy the items, false will move them
+ * if possible.
+ * @paramt {Object} view The view that should be used for batching.
+ * @return {Array} Returns an empty array when the insertion point is a tag, else
+ * returns an array of copied or moved guids.
+ */
+ async handleTransferItems(items, insertionPoint, doCopy, view) {
+ let transactions;
+ let itemsCount;
+ if (insertionPoint.isTag) {
+ let urls = items.filter(item => "uri" in item).map(item => item.uri);
+ itemsCount = urls.length;
+ transactions = [PlacesTransactions.Tag({ urls, tag: insertionPoint.tagName })];
+ } else {
+ let insertionIndex = await insertionPoint.getIndex();
+ itemsCount = items.length;
+ transactions = await getTransactionsForTransferItems(
+ items, insertionIndex, insertionPoint.guid, doCopy);
+ }
+
+ // Check if we actually have something to add, if we don't it probably wasn't
+ // valid, or it was moving to the same location, so just ignore it.
+ if (!transactions.length) {
+ return [];
+ }
+
+ let guidsToSelect = [];
+ let resultForBatching = getResultForBatching(view);
+
+ // If we're inserting into a tag, we don't get the guid, so we'll just
+ // pass the transactions direct to the batch function.
+ let batchingItem = transactions;
+ if (!insertionPoint.isTag) {
+ // If we're not a tag, then we need to get the ids of the items to select.
+ batchingItem = async () => {
+ for (let transaction of transactions) {
+ let guid = await transaction.transact();
+ if (guid) {
+ guidsToSelect.push(guid);
+ }
+ }
+ };
+ }
+
+ await this.batchUpdatesForNode(resultForBatching, itemsCount, async () => {
+ await PlacesTransactions.batch(batchingItem);
+ });
+
+ return guidsToSelect;
+ },
+};
+
+// These are lazy getters to avoid importing PlacesUtils immediately.
+XPCOMUtils.defineLazyGetter(PlacesUIUtils, "PLACES_FLAVORS", () => {
+ return [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
+ PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
+ PlacesUtils.TYPE_X_MOZ_PLACE];
+});
+XPCOMUtils.defineLazyGetter(PlacesUIUtils, "URI_FLAVORS", () => {
+ return [PlacesUtils.TYPE_X_MOZ_URL,
+ TAB_DROP_TYPE,
+ PlacesUtils.TYPE_UNICODE];
+});
+XPCOMUtils.defineLazyGetter(PlacesUIUtils, "SUPPORTED_FLAVORS", () => {
+ return [...PlacesUIUtils.PLACES_FLAVORS,
+ ...PlacesUIUtils.URI_FLAVORS];
+});
+
+XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
+ return Services.prefs.getComplexValue("intl.ellipsis",
+ Ci.nsIPrefLocalizedString).data;
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "focusManager",
+ "@mozilla.org/focus-manager;1",
+ "nsIFocusManager");
+
+/**
+ * Determines if an unwrapped node can be moved.
+ *
+ * @param unwrappedNode
+ * A node unwrapped by PlacesUtils.unwrapNodes().
+ * @return True if the node can be moved, false otherwise.
+ */
+function canMoveUnwrappedNode(unwrappedNode) {
+ if ((unwrappedNode.concreteGuid && PlacesUtils.isRootItem(unwrappedNode.concreteGuid)) ||
+ unwrappedNode.id <= 0 || PlacesUtils.isRootItem(unwrappedNode.id)) {
+ return false;
+ }
+
+ let parentGuid = unwrappedNode.parentGuid;
+ // If there's no parent Guid, this was likely a virtual query that returns
+ // bookmarks, such as a tags query.
+ if (!parentGuid ||
+ parentGuid == PlacesUtils.bookmarks.rootGuid) {
+ return false;
+ }
+ // leftPaneFolderId and allBookmarksFolderId are lazy getters running
+ // at least a synchronous DB query. Therefore we don't want to invoke
+ // them first, especially because isCommandEnabled may be called way
+ // before the left pane folder is even necessary.
+ if (typeof Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId").get != "function" &&
+ (unwrappedNode.parent == PlacesUIUtils.leftPaneFolderId)) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * This gets the most appropriate item for using for batching. In the case of multiple
+ * views being related, the method returns the most expensive result to batch.
+ * For example, if it detects the left-hand library pane, then it will look for
+ * and return the reference to the right-hand pane.
+ *
+ * @param {Object} viewOrElement The item to check.
+ * @return {Object} Will return the best result node to batch, or null
+ * if one could not be found.
+ */
+function getResultForBatching(viewOrElement) {
+ if (viewOrElement && Element.isInstance(viewOrElement) &&
+ viewOrElement.id === "placesList") {
+ // Note: fall back to the existing item if we can't find the right-hane pane.
+ viewOrElement = viewOrElement.ownerDocument.getElementById("placeContent") || viewOrElement;
+ }
+
+ if (viewOrElement && viewOrElement.result) {
+ return viewOrElement.result;
+ }
+
+ return null;
+}
+
+/**
+ * Processes a set of transfer items and returns transactions to insert or
+ * move them.
+ *
+ * @param {Array} items A list of unwrapped nodes to get transactions for.
+ * @param {Integer} insertionIndex The requested index for insertion.
+ * @param {String} insertionParentGuid The guid of the parent folder to insert
+ * or move the items to.
+ * @param {Boolean} doCopy Set to true to copy the items, false will move them
+ * if possible.
+ * @return {Array} Returns an array of created PlacesTransactions.
+ */
+async function getTransactionsForTransferItems(items, insertionIndex,
+ insertionParentGuid, doCopy) {
+ let transactions = [];
+ let index = insertionIndex;
+
+ for (let item of items) {
+ if (index != -1 && item.itemGuid) {
+ // Note: we use the parent from the existing bookmark as the sidebar
+ // gives us an unwrapped.parent that is actually a query and not the real
+ // parent.
+ let existingBookmark = await PlacesUtils.bookmarks.fetch(item.itemGuid);
+
+ // If we're dropping on the same folder, then we may need to adjust
+ // the index to insert at the correct place.
+ if (existingBookmark && insertionParentGuid == existingBookmark.parentGuid) {
+ if (index > existingBookmark.index) {
+ // If we're dragging down, we need to go one lower to insert at
+ // the real point as moving the element changes the index of
+ // everything below by 1.
+ index--;
+ } else if (index == existingBookmark.index) {
+ // This isn't moving so we skip it.
+ continue;
+ }
+ }
+ }
+
+ // If this is not a copy, check for safety that we can move the
+ // source, otherwise report an error and fallback to a copy.
+ if (!doCopy && !canMoveUnwrappedNode(item)) {
+ Cu.reportError("Tried to move an unmovable Places " +
+ "node, reverting to a copy operation.");
+ doCopy = true;
+ }
+ transactions.push(
+ PlacesUIUtils.getTransactionForData(item,
+ insertionParentGuid,
+ index,
+ doCopy));
+
+ if (index != -1 && item.itemGuid) {
+ index++;
+ }
+ }
+ return transactions;
+}
diff --git a/comm/suite/components/places/content/bookmarkProperties.js b/comm/suite/components/places/content/bookmarkProperties.js
new file mode 100644
index 0000000000..8078a4e9a7
--- /dev/null
+++ b/comm/suite/components/places/content/bookmarkProperties.js
@@ -0,0 +1,524 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * The panel is initialized based on data given in the js object passed
+ * as window.arguments[0]. The object must have the following fields set:
+ * @ action (String). Possible values:
+ * - "add" - for adding a new item.
+ * @ type (String). Possible values:
+ * - "bookmark"
+ * @ loadBookmarkInSidebar - optional, the default state for the
+ * "Load this bookmark in the sidebar" field.
+ * - "folder"
+ * @ URIList (Array of nsIURI objects) - optional, list of uris to
+ * be bookmarked under the new folder.
+ * - "livemark"
+ * @ uri (nsIURI object) - optional, the default uri for the new item.
+ * The property is not used for the "folder with items" type.
+ * @ title (String) - optional, the default title for the new item.
+ * @ description (String) - optional, the default description for the new
+ * item.
+ * @ defaultInsertionPoint (InsertionPoint JS object) - optional, the
+ * default insertion point for the new item.
+ * @ keyword (String) - optional, the default keyword for the new item.
+ * @ postData (String) - optional, POST data to accompany the keyword.
+ * @ charSet (String) - optional, character-set to accompany the keyword.
+ * Notes:
+ * 1) If |uri| is set for a bookmark/livemark item and |title| isn't,
+ * the dialog will query the history tables for the title associated
+ * with the given uri. If the dialog is set to adding a folder with
+ * bookmark items under it (see URIList), a default static title is
+ * used ("[Folder Name]").
+ * 2) The index field of the default insertion point is ignored if
+ * the folder picker is shown.
+ * - "edit" - for editing a bookmark item or a folder.
+ * @ type (String). Possible values:
+ * - "bookmark"
+ * @ node (an nsINavHistoryResultNode object) - a node representing
+ * the bookmark.
+ * - "folder" (also applies to livemarks)
+ * @ node (an nsINavHistoryResultNode object) - a node representing
+ * the folder.
+ * @ hiddenRows (Strings array) - optional, list of rows to be hidden
+ * regardless of the item edited or added by the dialog.
+ * Possible values:
+ * - "title"
+ * - "location"
+ * - "description"
+ * - "keyword"
+ * - "tags"
+ * - "loadInSidebar"
+ * - "folderPicker" - hides both the tree and the menu.
+ *
+ * window.arguments[0].performed is set to true if any transaction has
+ * been performed by the dialog.
+ */
+
+/* import-globals-from editBookmarkOverlay.js */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+const BOOKMARK_ITEM = 0;
+const BOOKMARK_FOLDER = 1;
+const LIVEMARK_CONTAINER = 2;
+
+const ACTION_EDIT = 0;
+const ACTION_ADD = 1;
+
+var elementsHeight = new Map();
+
+var BookmarkPropertiesPanel = {
+
+ /** UI Text Strings */
+ __strings: null,
+ get _strings() {
+ if (!this.__strings) {
+ this.__strings = document.getElementById("stringBundle");
+ }
+ return this.__strings;
+ },
+
+ _action: null,
+ _itemType: null,
+ _itemId: -1,
+ _uri: null,
+ _loadInSidebar: false,
+ _title: "",
+ _description: "",
+ _URIs: [],
+ _keyword: "",
+ _postData: null,
+ _charSet: "",
+ _feedURI: null,
+ _siteURI: null,
+
+ _defaultInsertionPoint: null,
+ _hiddenRows: [],
+
+ /**
+ * This method returns the correct label for the dialog's "accept"
+ * button based on the variant of the dialog.
+ */
+ _getAcceptLabel: function BPP__getAcceptLabel() {
+ if (this._action == ACTION_ADD) {
+ if (this._URIs.length)
+ return this._strings.getString("dialogAcceptLabelAddMulti");
+
+ if (this._itemType == LIVEMARK_CONTAINER)
+ return this._strings.getString("dialogAcceptLabelAddLivemark");
+
+ if (this._dummyItem || this._loadInSidebar)
+ return this._strings.getString("dialogAcceptLabelAddItem");
+
+ return this._strings.getString("dialogAcceptLabelSaveItem");
+ }
+ return this._strings.getString("dialogAcceptLabelEdit");
+ },
+
+ /**
+ * This method returns the correct title for the current variant
+ * of this dialog.
+ */
+ _getDialogTitle: function BPP__getDialogTitle() {
+ if (this._action == ACTION_ADD) {
+ if (this._itemType == BOOKMARK_ITEM)
+ return this._strings.getString("dialogTitleAddBookmark");
+ if (this._itemType == LIVEMARK_CONTAINER)
+ return this._strings.getString("dialogTitleAddLivemark");
+
+ // add folder
+ if (this._itemType != BOOKMARK_FOLDER)
+ throw new Error("Unknown item type");
+ if (this._URIs.length)
+ return this._strings.getString("dialogTitleAddMulti");
+
+ return this._strings.getString("dialogTitleAddFolder");
+ }
+ if (this._action == ACTION_EDIT) {
+ return this._strings.getFormattedString("dialogTitleEdit", [this._title]);
+ }
+ return "";
+ },
+
+ /**
+ * Determines the initial data for the item edited or added by this dialog
+ */
+ async _determineItemInfo() {
+ let dialogInfo = window.arguments[0];
+ this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT;
+ this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : [];
+ if (this._action == ACTION_ADD) {
+ if (!("type" in dialogInfo))
+ throw new Error("missing type property for add action");
+
+ if ("title" in dialogInfo)
+ this._title = dialogInfo.title;
+
+ if ("defaultInsertionPoint" in dialogInfo) {
+ this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint;
+ } else {
+ this._defaultInsertionPoint =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.bookmarksMenuFolderId,
+ parentGuid: PlacesUtils.bookmarks.menuGuid
+ });
+ }
+
+ switch (dialogInfo.type) {
+ case "bookmark":
+ this._itemType = BOOKMARK_ITEM;
+ if ("uri" in dialogInfo) {
+ if (!(dialogInfo.uri instanceof Ci.nsIURI))
+ throw new Error("uri property should be a uri object");
+ this._uri = dialogInfo.uri;
+ if (typeof(this._title) != "string") {
+ this._title = await PlacesUtils.history.fetch(this._uri) ||
+ this._uri.spec;
+ }
+ } else {
+ this._uri = Services.io.newURI("about:blank");
+ this._title = this._strings.getString("newBookmarkDefault");
+ this._dummyItem = true;
+ }
+
+ if ("loadBookmarkInSidebar" in dialogInfo)
+ this._loadInSidebar = dialogInfo.loadBookmarkInSidebar;
+
+ if ("keyword" in dialogInfo) {
+ this._keyword = dialogInfo.keyword;
+ this._isAddKeywordDialog = true;
+ if ("postData" in dialogInfo)
+ this._postData = dialogInfo.postData;
+ if ("charSet" in dialogInfo)
+ this._charSet = dialogInfo.charSet;
+ }
+ break;
+
+ case "folder":
+ this._itemType = BOOKMARK_FOLDER;
+ if (!this._title) {
+ if ("URIList" in dialogInfo) {
+ this._title = this._strings.getString("bookmarkAllTabsDefault");
+ this._URIs = dialogInfo.URIList;
+ } else
+ this._title = this._strings.getString("newFolderDefault");
+ this._dummyItem = true;
+ }
+ break;
+
+ case "livemark":
+ this._itemType = LIVEMARK_CONTAINER;
+ if ("feedURI" in dialogInfo)
+ this._feedURI = dialogInfo.feedURI;
+ if ("siteURI" in dialogInfo)
+ this._siteURI = dialogInfo.siteURI;
+
+ if (!this._title) {
+ if (this._feedURI) {
+ this._title = await PlacesUtils.history.fetch(this._feedURI) ||
+ this._feedURI.spec;
+ } else
+ this._title = this._strings.getString("newLivemarkDefault");
+ }
+ }
+
+ if ("description" in dialogInfo)
+ this._description = dialogInfo.description;
+ } else { // edit
+ this._node = dialogInfo.node;
+ this._title = this._node.title;
+ if (PlacesUtils.nodeIsFolder(this._node))
+ this._itemType = BOOKMARK_FOLDER;
+ else if (PlacesUtils.nodeIsURI(this._node))
+ this._itemType = BOOKMARK_ITEM;
+ }
+ },
+
+ /**
+ * This method should be called by the onload of the Bookmark Properties
+ * dialog to initialize the state of the panel.
+ */
+ async onDialogLoad() {
+ await this._determineItemInfo();
+
+ document.title = this._getDialogTitle();
+
+ // Disable the buttons until we have all the information required.
+ let acceptButton = document.documentElement.getButton("accept");
+ acceptButton.disabled = true;
+
+ // Allow initialization to complete in a truely async manner so that we're
+ // not blocking the main thread.
+ this._initDialog().catch(ex => {
+ Cu.reportError(`Failed to initialize dialog: ${ex}`);
+ });
+ },
+
+ /**
+ * Initializes the dialog, gathering the required bookmark data. This function
+ * will enable the accept button (if appropraite) when it is complete.
+ */
+ async _initDialog() {
+ let acceptButton = document.documentElement.getButton("accept");
+ acceptButton.label = this._getAcceptLabel();
+ let acceptButtonDisabled = false;
+
+ // Do not use sizeToContent, otherwise, due to bug 90276, the dialog will
+ // grow at every opening.
+ // Since elements can be uncollapsed asynchronously, we must observe their
+ // mutations and resize the dialog using a cached element size.
+ this._height = window.outerHeight;
+ this._mutationObserver = new MutationObserver(mutations => {
+ for (let mutation of mutations) {
+ let target = mutation.target;
+ let id = target.id;
+ if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id))
+ continue;
+
+ let collapsed = target.getAttribute("collapsed") === "true";
+ let wasCollapsed = mutation.oldValue === "true";
+ if (collapsed == wasCollapsed)
+ continue;
+
+ if (collapsed) {
+ this._height -= elementsHeight.get(id);
+ elementsHeight.delete(id);
+ } else {
+ elementsHeight.set(id, target.boxObject.height);
+ this._height += elementsHeight.get(id);
+ }
+ window.resizeTo(window.outerWidth, this._height);
+ }
+ });
+
+ this._mutationObserver.observe(document,
+ { subtree: true,
+ attributeOldValue: true,
+ attributeFilter: ["collapsed"] });
+
+ // Some controls are flexible and we want to update their cached size when
+ // the dialog is resized.
+ window.addEventListener("resize", this);
+
+ switch (this._action) {
+ case ACTION_EDIT:
+ gEditItemOverlay.initPanel({ node: this._node,
+ hiddenRows: this._hiddenRows,
+ focusedElement: "first" });
+ acceptButtonDisabled = gEditItemOverlay.readOnly;
+ break;
+ case ACTION_ADD:
+ this._node = await this._promiseNewItem();
+ // Edit the new item
+ gEditItemOverlay.initPanel({ node: this._node,
+ hiddenRows: this._hiddenRows,
+ postData: this._postData,
+ focusedElement: "first" });
+
+ // Empty location field if the uri is about:blank, this way inserting a new
+ // url will be easier for the user, Accept button will be automatically
+ // disabled by the input listener until the user fills the field.
+ let locationField = this._element("locationField");
+ if (locationField.value == "about:blank")
+ locationField.value = "";
+
+ // if this is an uri related dialog disable accept button until
+ // the user fills an uri value.
+ if (this._itemType == BOOKMARK_ITEM)
+ acceptButtonDisabled = !this._inputIsValid();
+ break;
+ }
+
+ if (!gEditItemOverlay.readOnly) {
+ // Listen on uri fields to enable accept button if input is valid
+ if (this._itemType == BOOKMARK_ITEM) {
+ this._element("locationField")
+ .addEventListener("input", this);
+ if (this._isAddKeywordDialog) {
+ this._element("keywordField")
+ .addEventListener("input", this);
+ }
+ }
+ }
+ // Only enable the accept button once we've finished everything.
+ acceptButton.disabled = acceptButtonDisabled;
+ },
+
+ // nsIDOMEventListener
+ handleEvent: function BPP_handleEvent(aEvent) {
+ var target = aEvent.target;
+ switch (aEvent.type) {
+ case "input":
+ if (target.id == "editBMPanel_locationField" ||
+ target.id == "editBMPanel_keywordField") {
+ // Check uri fields to enable accept button if input is valid
+ document.documentElement
+ .getButton("accept").disabled = !this._inputIsValid();
+ }
+ break;
+ case "resize":
+ for (let [id, oldHeight] of elementsHeight) {
+ let newHeight = document.getElementById(id).boxObject.height;
+ this._height += -oldHeight + newHeight;
+ elementsHeight.set(id, newHeight);
+ }
+ break;
+ }
+ },
+
+ // nsISupports
+ QueryInterface: function BPP_QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIDOMEventListener) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ _element: function BPP__element(aID) {
+ return document.getElementById("editBMPanel_" + aID);
+ },
+
+ onDialogUnload() {
+ // gEditItemOverlay does not exist anymore here, so don't rely on it.
+ this._mutationObserver.disconnect();
+ delete this._mutationObserver;
+
+ window.removeEventListener("resize", this);
+
+ // Calling removeEventListener with arguments which do not identify any
+ // currently registered EventListener on the EventTarget has no effect.
+ this._element("locationField")
+ .removeEventListener("input", this);
+ },
+
+ onDialogAccept() {
+ // We must blur current focused element to save its changes correctly
+ document.commandDispatcher.focusedElement.blur();
+ // We have to uninit the panel first, otherwise late changes could force it
+ // to commit more transactions.
+ gEditItemOverlay.uninitPanel(true);
+ window.arguments[0].performed = true;
+ },
+
+ onDialogCancel() {
+ // We have to uninit the panel first, otherwise late changes could force it
+ // to commit more transactions.
+ gEditItemOverlay.uninitPanel(true);
+ window.arguments[0].performed = false;
+ },
+
+ /**
+ * This method checks to see if the input fields are in a valid state.
+ *
+ * @returns true if the input is valid, false otherwise
+ */
+ _inputIsValid: function BPP__inputIsValid() {
+ if (this._itemType == BOOKMARK_ITEM &&
+ !this._containsValidURI("locationField"))
+ return false;
+ if (this._isAddKeywordDialog && !this._element("keywordField").value.length)
+ return false;
+
+ return true;
+ },
+
+ /**
+ * Determines whether the XUL textbox with the given ID contains a
+ * string that can be converted into an nsIURI.
+ *
+ * @param aTextboxID
+ * the ID of the textbox element whose contents we'll test
+ *
+ * @returns true if the textbox contains a valid URI string, false otherwise
+ */
+ _containsValidURI: function BPP__containsValidURI(aTextboxID) {
+ try {
+ var value = this._element(aTextboxID).value;
+ if (value) {
+ PlacesUIUtils.createFixedURI(value);
+ return true;
+ }
+ } catch (e) { }
+ return false;
+ },
+
+ /**
+ * [New Item Mode] Get the insertion point details for the new item, given
+ * dialog state and opening arguments.
+ *
+ * The container-identifier and insertion-index are returned separately in
+ * the form of [containerIdentifier, insertionIndex]
+ */
+ async _getInsertionPointDetails() {
+ return [
+ this._defaultInsertionPoint.itemId,
+ await this._defaultInsertionPoint.getIndex(),
+ this._defaultInsertionPoint.guid,
+ ];
+ },
+
+ async _promiseNewItem() {
+ let [containerId, index, parentGuid] = await this._getInsertionPointDetails();
+ let annotations = [];
+ if (this._description) {
+ annotations.push({ name: PlacesUIUtils.DESCRIPTION_ANNO,
+ value: this._description });
+ }
+ if (this._loadInSidebar) {
+ annotations.push({ name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
+ value: true });
+ }
+
+ let itemGuid;
+ let info = { parentGuid, index, title: this._title, annotations };
+ if (this._itemType == BOOKMARK_ITEM) {
+ info.url = this._uri;
+ if (this._keyword)
+ info.keyword = this._keyword;
+ if (this._postData)
+ info.postData = this._postData;
+
+ if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window))
+ PlacesUtils.setCharsetForURI(this._uri, this._charSet);
+
+ itemGuid = await PlacesTransactions.NewBookmark(info).transact();
+ } else if (this._itemType == LIVEMARK_CONTAINER) {
+ info.feedUrl = this._feedURI;
+ if (this._siteURI)
+ info.siteUrl = this._siteURI;
+
+ itemGuid = await PlacesTransactions.NewLivemark(info).transact();
+ } else if (this._itemType == BOOKMARK_FOLDER) {
+ // NewFolder requires a url rather than uri.
+ info.children = this._URIs.map(item => {
+ return { url: item.uri, title: item.title };
+ });
+ itemGuid = await PlacesTransactions.NewFolder(info).transact();
+ } else {
+ throw new Error(`unexpected value for _itemType: ${this._itemType}`);
+ }
+
+ this._itemGuid = itemGuid;
+ this._itemId = await PlacesUtils.promiseItemId(itemGuid);
+ return Object.freeze({
+ itemId: this._itemId,
+ bookmarkGuid: this._itemGuid,
+ title: this._title,
+ uri: this._uri ? this._uri.spec : "",
+ type: this._itemType == BOOKMARK_ITEM ?
+ Ci.nsINavHistoryResultNode.RESULT_TYPE_URI :
+ Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER,
+ parent: {
+ itemId: containerId,
+ bookmarkGuid: parentGuid,
+ type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
+ }
+ });
+ }
+};
diff --git a/comm/suite/components/places/content/bookmarkProperties.xul b/comm/suite/components/places/content/bookmarkProperties.xul
new file mode 100644
index 0000000000..739d21c879
--- /dev/null
+++ b/comm/suite/components/places/content/bookmarkProperties.xul
@@ -0,0 +1,41 @@
+<?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://communicator/skin/"?>
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/editBookmarkOverlay.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/bookmarks.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/places/editBookmarkOverlay.xul"?>
+
+<!DOCTYPE dialog [
+ <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://communicator/locale/places/editBookmarkOverlay.dtd">
+ %editBookmarkOverlayDTD;
+]>
+
+<dialog id="bookmarkproperties"
+ buttons="accept, cancel"
+ buttoniconaccept="save"
+ ondialogaccept="BookmarkPropertiesPanel.onDialogAccept();"
+ ondialogcancel="BookmarkPropertiesPanel.onDialogCancel();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="BookmarkPropertiesPanel.onDialogLoad();"
+ onunload="BookmarkPropertiesPanel.onDialogUnload();"
+ style="min-width: 30em;"
+ persist="screenX screenY width">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="stringBundle"
+ src="chrome://communicator/locale/places/bookmarkProperties.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://communicator/content/places/editBookmarkOverlay.js"/>
+ <script src="chrome://communicator/content/places/bookmarkProperties.js"/>
+
+<vbox id="editBookmarkPanelContent"/>
+
+</dialog>
diff --git a/comm/suite/components/places/content/bookmarksPanel.js b/comm/suite/components/places/content/bookmarksPanel.js
new file mode 100644
index 0000000000..e0c79eb742
--- /dev/null
+++ b/comm/suite/components/places/content/bookmarksPanel.js
@@ -0,0 +1,24 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function init() {
+ document.getElementById("bookmarks-view").place =
+ "place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
+}
+
+function searchBookmarks(aSearchString) {
+ var tree = document.getElementById("bookmarks-view");
+ if (!aSearchString)
+ tree.place = tree.place;
+ else
+ tree.applyFilter(aSearchString,
+ [PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.toolbarFolderId,
+ PlacesUtils.mobileFolderId]);
+}
+
+window.addEventListener("SidebarFocused",
+ () => document.getElementById("search-box").focus());
diff --git a/comm/suite/components/places/content/bookmarksPanel.xul b/comm/suite/components/places/content/bookmarksPanel.xul
new file mode 100644
index 0000000000..59b9957a5a
--- /dev/null
+++ b/comm/suite/components/places/content/bookmarksPanel.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sidebar/sidebarListView.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/bookmarks.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE page SYSTEM "chrome://communicator/locale/places/places.dtd">
+
+<page id="bookmarksPanel"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="init();"
+ onunload="SidebarUtils.setMouseoverURL('');">
+
+ <script src="chrome://communicator/content/bookmarks/sidebarUtils.js"/>
+ <script src="chrome://communicator/content/bookmarks/bookmarksPanel.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+
+ <commandset id="placesCommands"/>
+ <menupopup id="placesContext"/>
+
+ <!-- Bookmarks and history tooltip -->
+ <tooltip id="bhTooltip"/>
+
+ <hbox id="sidebar-search-container" align="center">
+ <textbox id="search-box" flex="1" type="search"
+ placeholder="&search.placeholder;"
+ aria-controls="bookmarks-view"
+ oncommand="searchBookmarks(this.value);"/>
+ </hbox>
+
+ <tree id="bookmarks-view" class="sidebar-placesTree" type="places"
+ flex="1"
+ hidecolumnpicker="true"
+ treelines="true"
+ context="placesContext"
+ onkeypress="SidebarUtils.handleTreeKeyPress(event);"
+ onclick="SidebarUtils.handleTreeClick(this, event, true);"
+ onmousemove="SidebarUtils.handleTreeMouseMove(event);"
+ onmouseout="SidebarUtils.setMouseoverURL('');">
+ <treecols>
+ <treecol id="title" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ <treechildren id="bookmarks-view-children" view="bookmarks-view"
+ class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
+ </tree>
+</page>
diff --git a/comm/suite/components/places/content/browserPlacesViews.js b/comm/suite/components/places/content/browserPlacesViews.js
new file mode 100644
index 0000000000..4b3e4c24c2
--- /dev/null
+++ b/comm/suite/components/places/content/browserPlacesViews.js
@@ -0,0 +1,2287 @@
+/* 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/. */
+
+/* eslint-env mozilla/browser-window */
+
+/**
+ * The base view implements everything that's common to the toolbar and
+ * menu views.
+ */
+function PlacesViewBase(aPlace, aOptions = {}) {
+ if ("rootElt" in aOptions)
+ this._rootElt = aOptions.rootElt;
+ if ("viewElt" in aOptions)
+ this._viewElt = aOptions.viewElt;
+ this.options = aOptions;
+ this._controller = new PlacesController(this);
+ this.place = aPlace;
+ this._viewElt.controllers.appendController(this._controller);
+}
+
+PlacesViewBase.prototype = {
+ // The xul element that holds the entire view.
+ _viewElt: null,
+ get viewElt() {
+ return this._viewElt;
+ },
+
+ get associatedElement() {
+ return this._viewElt;
+ },
+
+ get controllers() {
+ return this._viewElt.controllers;
+ },
+
+ // The xul element that represents the root container.
+ _rootElt: null,
+
+ // Set to true for views that are represented by native widgets (i.e.
+ // the native mac menu).
+ _nativeView: false,
+
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsINavHistoryResultObserver,
+ Ci.nsISupportsWeakReference]),
+
+ _place: "",
+ get place() {
+ return this._place;
+ },
+ set place(val) {
+ this._place = val;
+
+ let history = PlacesUtils.history;
+ let queries = { }, options = { };
+ history.queryStringToQueries(val, queries, { }, options);
+ if (!queries.value.length)
+ queries.value = [history.getNewQuery()];
+
+ let result = history.executeQueries(queries.value, queries.value.length,
+ options.value);
+ result.addObserver(this);
+ return val;
+ },
+
+ _result: null,
+ get result() {
+ return this._result;
+ },
+ set result(val) {
+ if (this._result == val)
+ return val;
+
+ if (this._result) {
+ this._result.removeObserver(this);
+ this._resultNode.containerOpen = false;
+ }
+
+ if (this._rootElt.localName == "menupopup")
+ this._rootElt._built = false;
+
+ this._result = val;
+ if (val) {
+ this._resultNode = val.root;
+ this._rootElt._placesNode = this._resultNode;
+ this._domNodes = new Map();
+ this._domNodes.set(this._resultNode, this._rootElt);
+
+ // This calls _rebuild through invalidateContainer.
+ this._resultNode.containerOpen = true;
+ } else {
+ this._resultNode = null;
+ delete this._domNodes;
+ }
+
+ return val;
+ },
+
+ _options: null,
+ get options() {
+ return this._options;
+ },
+ set options(val) {
+ if (!val)
+ val = {};
+
+ if (!("extraClasses" in val))
+ val.extraClasses = {};
+ this._options = val;
+
+ return val;
+ },
+
+ /**
+ * Gets the DOM node used for the given places node.
+ *
+ * @param aPlacesNode
+ * a places result node.
+ * @param aAllowMissing
+ * whether the node may be missing
+ * @throws if there is no DOM node set for aPlacesNode.
+ */
+ _getDOMNodeForPlacesNode:
+ function PVB__getDOMNodeForPlacesNode(aPlacesNode, aAllowMissing = false) {
+ let node = this._domNodes.get(aPlacesNode, null);
+ if (!node && !aAllowMissing) {
+ throw new Error("No DOM node set for aPlacesNode.\nnode.type: " +
+ aPlacesNode.type + ". node.parent: " + aPlacesNode);
+ }
+ return node;
+ },
+
+ get controller() {
+ return this._controller;
+ },
+
+ get selType() {
+ return "single";
+ },
+ selectItems() { },
+ selectAll() { },
+
+ get selectedNode() {
+ if (this._contextMenuShown) {
+ let anchor = this._contextMenuShown.triggerNode;
+ if (!anchor)
+ return null;
+
+ if (anchor._placesNode)
+ return this._rootElt == anchor ? null : anchor._placesNode;
+
+ anchor = anchor.parentNode;
+ return this._rootElt == anchor ? null : (anchor._placesNode || null);
+ }
+ return null;
+ },
+
+ get hasSelection() {
+ return this.selectedNode != null;
+ },
+
+ get selectedNodes() {
+ let selectedNode = this.selectedNode;
+ return selectedNode ? [selectedNode] : [];
+ },
+
+ get removableSelectionRanges() {
+ // On static content the current selectedNode would be the selection's
+ // parent node. We don't want to allow removing a node when the
+ // selection is not explicit.
+ if (document.popupNode &&
+ (document.popupNode == "menupopup" || !document.popupNode._placesNode))
+ return [];
+
+ return [this.selectedNodes];
+ },
+
+ get draggableSelection() {
+ return [this._draggedElt];
+ },
+
+ get insertionPoint() {
+ // There is no insertion point for history queries, so bail out now and
+ // save a lot of work when updating commands.
+ let resultNode = this._resultNode;
+ if (PlacesUtils.nodeIsQuery(resultNode) &&
+ PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
+ Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
+ return null;
+
+ // By default, the insertion point is at the top level, at the end.
+ let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
+ let container = this._resultNode;
+ let orientation = Ci.nsITreeView.DROP_BEFORE;
+ let tagName = null;
+
+ let selectedNode = this.selectedNode;
+ if (selectedNode) {
+ let popup = document.popupNode;
+ if (!popup._placesNode || popup._placesNode == this._resultNode ||
+ popup._placesNode.itemId == -1 || !selectedNode.parent) {
+ // If a static menuitem is selected, or if the root node is selected,
+ // the insertion point is inside the folder, at the end.
+ container = selectedNode;
+ orientation = Ci.nsITreeView.DROP_ON;
+ } else {
+ // In all other cases the insertion point is before that node.
+ container = selectedNode.parent;
+ index = container.getChildIndex(selectedNode);
+ if (PlacesUtils.nodeIsTagQuery(container)) {
+ tagName = container.title;
+ // TODO (Bug 1160193): properly support dropping on a tag root.
+ if (!tagName)
+ return null;
+ }
+ }
+ }
+
+ if (this.controller.disallowInsertion(container))
+ return null;
+
+ return new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(container),
+ parentGuid: PlacesUtils.getConcreteItemGuid(container),
+ index, orientation, tagName
+ });
+ },
+
+ buildContextMenu: function PVB_buildContextMenu(aPopup) {
+ this._contextMenuShown = aPopup;
+ window.updateCommands("places");
+ return this.controller.buildContextMenu(aPopup);
+ },
+
+ destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
+ this._contextMenuShown = null;
+ },
+
+ clearAllContents(aPopup) {
+ let kid = aPopup.firstChild;
+ while (kid) {
+ let next = kid.nextSibling;
+ if (!kid.classList.contains("panel-header")) {
+ kid.remove();
+ }
+ kid = next;
+ }
+ aPopup._emptyMenuitem = aPopup._startMarker = aPopup._endMarker = null;
+ },
+
+ _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) {
+ // Ensure markers are here when `invalidateContainer` is called before the
+ // popup is shown, which may the case for panelviews, for example.
+ this._ensureMarkers(aPopup);
+ // Remove Places nodes from the popup.
+ let child = aPopup._startMarker;
+ while (child.nextSibling != aPopup._endMarker) {
+ let sibling = child.nextSibling;
+ if (sibling._placesNode && !aDelay) {
+ aPopup.removeChild(sibling);
+ } else if (sibling._placesNode && aDelay) {
+ // HACK (bug 733419): the popups originating from the OS X native
+ // menubar don't live-update while open, thus we don't clean it
+ // until the next popupshowing, to avoid zombie menuitems.
+ if (!aPopup._delayedRemovals)
+ aPopup._delayedRemovals = [];
+ aPopup._delayedRemovals.push(sibling);
+ child = child.nextSibling;
+ } else {
+ child = child.nextSibling;
+ }
+ }
+ },
+
+ _rebuildPopup: function PVB__rebuildPopup(aPopup) {
+ let resultNode = aPopup._placesNode;
+ if (!resultNode.containerOpen)
+ return;
+
+ if (this.controller.hasCachedLivemarkInfo(resultNode)) {
+ this._setEmptyPopupStatus(aPopup, false);
+ aPopup._built = true;
+ this._populateLivemarkPopup(aPopup);
+ return;
+ }
+
+ this._cleanPopup(aPopup);
+
+ let cc = resultNode.childCount;
+ if (cc > 0) {
+ this._setEmptyPopupStatus(aPopup, false);
+ let fragment = document.createDocumentFragment();
+ for (let i = 0; i < cc; ++i) {
+ let child = resultNode.getChild(i);
+ this._insertNewItemToPopup(child, fragment);
+ }
+ aPopup.insertBefore(fragment, aPopup._endMarker);
+ } else {
+ this._setEmptyPopupStatus(aPopup, true);
+ }
+ aPopup._built = true;
+ },
+
+ _removeChild: function PVB__removeChild(aChild) {
+ // If document.popupNode pointed to this child, null it out,
+ // otherwise controller's command-updating may rely on the removed
+ // item still being "selected".
+ if (document.popupNode == aChild)
+ document.popupNode = null;
+
+ aChild.remove();
+ },
+
+ _setEmptyPopupStatus:
+ function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
+ if (!aPopup._emptyMenuitem) {
+ let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
+ aPopup._emptyMenuitem = document.createElement("menuitem");
+ aPopup._emptyMenuitem.setAttribute("label", label);
+ aPopup._emptyMenuitem.setAttribute("disabled", true);
+ aPopup._emptyMenuitem.className = "bookmark-item";
+ if (typeof this.options.extraClasses.entry == "string")
+ aPopup._emptyMenuitem.classList.add(this.options.extraClasses.entry);
+ }
+
+ if (aEmpty) {
+ aPopup.setAttribute("emptyplacesresult", "true");
+ // Don't add the menuitem if there is static content.
+ if (!aPopup._startMarker.previousSibling &&
+ !aPopup._endMarker.nextSibling)
+ aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
+ } else {
+ aPopup.removeAttribute("emptyplacesresult");
+ try {
+ aPopup.removeChild(aPopup._emptyMenuitem);
+ } catch (ex) {}
+ }
+ },
+
+ _createDOMNodeForPlacesNode:
+ function PVB__createDOMNodeForPlacesNode(aPlacesNode) {
+ this._domNodes.delete(aPlacesNode);
+
+ let element;
+ let type = aPlacesNode.type;
+ if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
+ element = document.createElement("menuseparator");
+ element.setAttribute("class", "small-separator");
+ } else {
+ let itemId = aPlacesNode.itemId;
+ if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI) {
+ element = document.createElement("menuitem");
+ element.className = "menuitem-iconic bookmark-item menuitem-with-favicon";
+ element.setAttribute("scheme",
+ PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri));
+ } else if (PlacesUtils.containerTypes.includes(type)) {
+ element = document.createElement("menu");
+ element.setAttribute("container", "true");
+
+ if (aPlacesNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
+ element.setAttribute("query", "true");
+ if (PlacesUtils.nodeIsTagQuery(aPlacesNode))
+ element.setAttribute("tagContainer", "true");
+ else if (PlacesUtils.nodeIsDay(aPlacesNode))
+ element.setAttribute("dayContainer", "true");
+ else if (PlacesUtils.nodeIsHost(aPlacesNode))
+ element.setAttribute("hostContainer", "true");
+ } else if (itemId != -1) {
+ PlacesUtils.livemarks.getLivemark({ id: itemId })
+ .then(aLivemark => {
+ element.setAttribute("livemark", "true");
+ if (AppConstants.platform === "macosx") {
+ // OS X native menubar doesn't track list-style-images since
+ // it doesn't have a frame (bug 733415). Thus enforce updating.
+ element.setAttribute("image", "");
+ element.removeAttribute("image");
+ }
+ this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
+ }, () => undefined);
+ }
+
+ let popup = document.createElement("menupopup");
+ popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
+
+ if (!this._nativeView) {
+ popup.setAttribute("placespopup", "true");
+ }
+
+ element.appendChild(popup);
+ element.className = "menu-iconic bookmark-item";
+ if (typeof this.options.extraClasses.entry == "string") {
+ element.classList.add(this.options.extraClasses.entry);
+ }
+
+ this._domNodes.set(aPlacesNode, popup);
+ } else
+ throw "Unexpected node";
+
+ element.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode));
+
+ let icon = aPlacesNode.icon;
+ if (icon)
+ element.setAttribute("image", icon);
+ }
+
+ element._placesNode = aPlacesNode;
+ if (!this._domNodes.has(aPlacesNode))
+ this._domNodes.set(aPlacesNode, element);
+
+ return element;
+ },
+
+ _insertNewItemToPopup:
+ function PVB__insertNewItemToPopup(aNewChild, aInsertionNode, aBefore = null) {
+ let element = this._createDOMNodeForPlacesNode(aNewChild);
+
+ if (element.localName == "menuitem" || element.localName == "menu") {
+ if (typeof this.options.extraClasses.entry == "string")
+ element.classList.add(this.options.extraClasses.entry);
+ }
+
+ aInsertionNode.insertBefore(element, aBefore);
+ return element;
+ },
+
+ _setLivemarkSiteURIMenuItem:
+ function PVB__setLivemarkSiteURIMenuItem(aPopup) {
+ let livemarkInfo = this.controller.getCachedLivemarkInfo(aPopup._placesNode);
+ let siteUrl = livemarkInfo && livemarkInfo.siteURI ?
+ livemarkInfo.siteURI.spec : null;
+ if (!siteUrl && aPopup._siteURIMenuitem) {
+ aPopup.removeChild(aPopup._siteURIMenuitem);
+ aPopup._siteURIMenuitem = null;
+ aPopup.removeChild(aPopup._siteURIMenuseparator);
+ aPopup._siteURIMenuseparator = null;
+ } else if (siteUrl && !aPopup._siteURIMenuitem) {
+ // Add "Open (Feed Name)" menuitem.
+ aPopup._siteURIMenuitem = document.createElement("menuitem");
+ aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
+ if (typeof this.options.extraClasses.entry == "string") {
+ aPopup._siteURIMenuitem.classList.add(this.options.extraClasses.entry);
+ }
+ aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
+ aPopup._siteURIMenuitem.setAttribute("oncommand",
+ "openUILink(this.getAttribute('targetURI'), event);");
+
+ // If a user middle-clicks this item we serve the oncommand event.
+ // We are using checkForMiddleClick because of Bug 246720.
+ // Note: stopPropagation is needed to avoid serving middle-click
+ // with BT_onClick that would open all items in tabs.
+ aPopup._siteURIMenuitem.setAttribute("onclick",
+ "checkForMiddleClick(this, event); event.stopPropagation();");
+ let label =
+ PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
+ [aPopup.parentNode.getAttribute("label")]);
+ aPopup._siteURIMenuitem.setAttribute("label", label);
+ aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);
+
+ aPopup._siteURIMenuseparator = document.createElement("menuseparator");
+ aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
+ }
+ },
+
+ /**
+ * Add, update or remove the livemark status menuitem.
+ * @param aPopup
+ * The livemark container popup
+ * @param aStatus
+ * The livemark status
+ */
+ _setLivemarkStatusMenuItem:
+ function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
+ let statusMenuitem = aPopup._statusMenuitem;
+ if (!statusMenuitem) {
+ // Create the status menuitem and cache it in the popup object.
+ statusMenuitem = document.createElement("menuitem");
+ statusMenuitem.className = "livemarkstatus-menuitem";
+ if (typeof this.options.extraClasses.entry == "string") {
+ statusMenuitem.classList.add(this.options.extraClasses.entry);
+ }
+ statusMenuitem.setAttribute("disabled", true);
+ aPopup._statusMenuitem = statusMenuitem;
+ }
+
+ if (aStatus == Ci.mozILivemark.STATUS_LOADING ||
+ aStatus == Ci.mozILivemark.STATUS_FAILED) {
+ // Status has changed, update the cached status menuitem.
+ let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ?
+ "bookmarksLivemarkLoading" : "bookmarksLivemarkFailed";
+ statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
+ if (aPopup._startMarker.nextSibling != statusMenuitem)
+ aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
+ } else if (aPopup._statusMenuitem.parentNode == aPopup) {
+ // The livemark has finished loading.
+ aPopup.removeChild(aPopup._statusMenuitem);
+ }
+ },
+
+ toggleCutNode: function PVB_toggleCutNode(aPlacesNode, aValue) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // We may get the popup for menus, but we need the menu itself.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+ if (aValue)
+ elt.setAttribute("cutting", "true");
+ else
+ elt.removeAttribute("cutting");
+ },
+
+ nodeURIChanged: function PVB_nodeURIChanged(aPlacesNode, aURIString) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ elt.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri));
+ },
+
+ nodeIconChanged: function PVB_nodeIconChanged(aPlacesNode) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // There's no UI representation for the root node, thus there's nothing to
+ // be done when the icon changes.
+ if (elt == this._rootElt)
+ return;
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup") {
+ elt = elt.parentNode;
+ }
+ // We must remove and reset the attribute to force an update.
+ elt.removeAttribute("image");
+ elt.setAttribute("image", aPlacesNode.icon);
+ },
+
+ nodeAnnotationChanged:
+ function PVB_nodeAnnotationChanged(aPlacesNode, aAnno) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // All livemarks have a feedURI, so use it as our indicator of a livemark
+ // being modified.
+ if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
+ let menu = elt.parentNode;
+ if (!menu.hasAttribute("livemark")) {
+ menu.setAttribute("livemark", "true");
+ if (AppConstants.platform === "macosx") {
+ // OS X native menubar doesn't track list-style-images since
+ // it doesn't have a frame (bug 733415). Thus enforce updating.
+ menu.setAttribute("image", "");
+ menu.removeAttribute("image");
+ }
+ }
+
+ PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
+ .then(aLivemark => {
+ // Controller will use this to build the meta data for the node.
+ this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
+ this.invalidateContainer(aPlacesNode);
+ }, () => undefined);
+ }
+ },
+
+ nodeTitleChanged:
+ function PVB_nodeTitleChanged(aPlacesNode, aNewTitle) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // There's no UI representation for the root node, thus there's
+ // nothing to be done when the title changes.
+ if (elt == this._rootElt)
+ return;
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ if (!aNewTitle && elt.localName != "toolbarbutton") {
+ // Many users consider toolbars as shortcuts containers, so explicitly
+ // allow empty labels on toolbarbuttons. For any other element try to be
+ // smarter, guessing a title from the uri.
+ elt.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode));
+ } else {
+ elt.setAttribute("label", aNewTitle);
+ }
+ },
+
+ nodeRemoved:
+ function PVB_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ if (parentElt._built) {
+ parentElt.removeChild(elt);
+
+ // Figure out if we need to show the "<Empty>" menu-item.
+ // TODO Bug 517701: This doesn't seem to handle the case of an empty
+ // root.
+ if (parentElt._startMarker.nextSibling == parentElt._endMarker)
+ this._setEmptyPopupStatus(parentElt, true);
+ }
+ },
+
+ nodeHistoryDetailsChanged:
+ function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
+ if (aPlacesNode.parent &&
+ this.controller.hasCachedLivemarkInfo(aPlacesNode.parent)) {
+ // Find the node in the parent.
+ let popup = this._getDOMNodeForPlacesNode(aPlacesNode.parent);
+ for (let child = popup._startMarker.nextSibling;
+ child != popup._endMarker;
+ child = child.nextSibling) {
+ if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
+ if (aPlacesNode.accessCount)
+ child.setAttribute("visited", "true");
+ else
+ child.removeAttribute("visited");
+ break;
+ }
+ }
+ }
+ },
+
+ nodeTagsChanged() { },
+ nodeDateAddedChanged() { },
+ nodeLastModifiedChanged() { },
+ nodeKeywordChanged() { },
+ sortingChanged() { },
+ batching() { },
+
+ nodeInserted:
+ function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
+ if (!parentElt._built)
+ return;
+
+ let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
+ aIndex + 1;
+ this._insertNewItemToPopup(aPlacesNode, parentElt,
+ parentElt.childNodes[index] || parentElt._endMarker);
+ this._setEmptyPopupStatus(parentElt, false);
+ },
+
+ nodeMoved:
+ function PBV_nodeMoved(aPlacesNode,
+ aOldParentPlacesNode, aOldIndex,
+ aNewParentPlacesNode, aNewIndex) {
+ // Note: the current implementation of moveItem does not actually
+ // use this notification when the item in question is moved from one
+ // folder to another. Instead, it calls nodeRemoved and nodeInserted
+ // for the two folders. Thus, we can assume old-parent == new-parent.
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ // If our root node is a folder, it might be moved. There's nothing
+ // we need to do in that case.
+ if (elt == this._rootElt)
+ return;
+
+ let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
+ if (parentElt._built) {
+ // Move the node.
+ parentElt.removeChild(elt);
+ let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
+ aNewIndex + 1;
+ parentElt.insertBefore(elt, parentElt.childNodes[index]);
+ }
+ },
+
+ containerStateChanged:
+ function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
+ if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
+ aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
+ this.invalidateContainer(aPlacesNode);
+
+ if (PlacesUtils.nodeIsFolder(aPlacesNode)) {
+ let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+ if (queryOptions.excludeItems) {
+ return;
+ }
+
+ PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
+ .then(aLivemark => {
+ let shouldInvalidate =
+ !this.controller.hasCachedLivemarkInfo(aPlacesNode);
+ this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
+ if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
+ aLivemark.registerForUpdates(aPlacesNode, this);
+ // Prioritize the current livemark.
+ aLivemark.reload();
+ PlacesUtils.livemarks.reloadLivemarks();
+ if (shouldInvalidate)
+ this.invalidateContainer(aPlacesNode);
+ } else {
+ aLivemark.unregisterForUpdates(aPlacesNode);
+ }
+ }, () => undefined);
+ }
+ }
+ },
+
+ _populateLivemarkPopup: function PVB__populateLivemarkPopup(aPopup) {
+ this._setLivemarkSiteURIMenuItem(aPopup);
+ // Show the loading status only if there are no entries yet.
+ if (aPopup._startMarker.nextSibling == aPopup._endMarker)
+ this._setLivemarkStatusMenuItem(aPopup, Ci.mozILivemark.STATUS_LOADING);
+
+ PlacesUtils.livemarks.getLivemark({ id: aPopup._placesNode.itemId })
+ .then(aLivemark => {
+ let placesNode = aPopup._placesNode;
+ if (!placesNode.containerOpen)
+ return;
+
+ if (aLivemark.status != Ci.mozILivemark.STATUS_LOADING)
+ this._setLivemarkStatusMenuItem(aPopup, aLivemark.status);
+ this._cleanPopup(aPopup,
+ this._nativeView && aPopup.parentNode.hasAttribute("open"));
+
+ let children = aLivemark.getNodesForContainer(placesNode);
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ this.nodeInserted(placesNode, child, i);
+ if (child.accessCount)
+ this._getDOMNodeForPlacesNode(child).setAttribute("visited", true);
+ else
+ this._getDOMNodeForPlacesNode(child).removeAttribute("visited");
+ }
+ }, Cu.reportError);
+ },
+
+ /**
+ * Checks whether the popup associated with the provided element is open.
+ * This method may be overridden by classes that extend this base class.
+ *
+ * @param {Element} elt
+ * @return {Boolean}
+ */
+ _isPopupOpen(elt) {
+ return !!elt.parentNode.open;
+ },
+
+ invalidateContainer: function PVB_invalidateContainer(aPlacesNode) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+ elt._built = false;
+
+ // If the menupopup is open we should live-update it.
+ if (this._isPopupOpen(elt))
+ this._rebuildPopup(elt);
+ },
+
+ uninit: function PVB_uninit() {
+ if (this._result) {
+ this._result.removeObserver(this);
+ this._resultNode.containerOpen = false;
+ this._resultNode = null;
+ this._result = null;
+ }
+
+ if (this._controller) {
+ this._controller.terminate();
+ // Removing the controller will fail if it is already no longer there.
+ // This can happen if the view element was removed/reinserted without
+ // our knowledge. There is no way to check for that having happened
+ // without the possibility of an exception. :-(
+ try {
+ this._viewElt.controllers.removeController(this._controller);
+ } catch (ex) {
+ } finally {
+ this._controller = null;
+ }
+ }
+
+ delete this._viewElt._placesView;
+ },
+
+ get isRTL() {
+ if ("_isRTL" in this)
+ return this._isRTL;
+
+ return this._isRTL = document.defaultView
+ .getComputedStyle(this.viewElt)
+ .direction == "rtl";
+ },
+
+ get ownerWindow() {
+ return window;
+ },
+
+ /**
+ * Adds an "Open All in Tabs" menuitem to the bottom of the popup.
+ * @param aPopup
+ * a Places popup.
+ */
+ _mayAddCommandsItems: function PVB__mayAddCommandsItems(aPopup) {
+ // The command items are never added to the root popup.
+ if (aPopup == this._rootElt)
+ return;
+
+ let hasMultipleURIs = false;
+
+ // Check if the popup contains at least 2 menuitems with places nodes.
+ // We don't currently support opening multiple uri nodes when they are not
+ // populated by the result.
+ if (aPopup._placesNode.childCount > 0) {
+ let currentChild = aPopup.firstChild;
+ let numURINodes = 0;
+ while (currentChild) {
+ if (currentChild.localName == "menuitem" && currentChild._placesNode) {
+ if (++numURINodes == 2)
+ break;
+ }
+ currentChild = currentChild.nextSibling;
+ }
+ hasMultipleURIs = numURINodes > 1;
+ }
+
+ let isLiveMark = false;
+ if (this.controller.hasCachedLivemarkInfo(aPopup._placesNode)) {
+ hasMultipleURIs = true;
+ isLiveMark = true;
+ }
+
+ if (!hasMultipleURIs) {
+ aPopup.setAttribute("singleitempopup", "true");
+ } else {
+ aPopup.removeAttribute("singleitempopup");
+ }
+
+ if (!hasMultipleURIs) {
+ // We don't have to show any option.
+ if (aPopup._endOptOpenAllInTabs) {
+ aPopup.removeChild(aPopup._endOptOpenAllInTabs);
+ aPopup._endOptOpenAllInTabs = null;
+
+ aPopup.removeChild(aPopup._endOptSeparator);
+ aPopup._endOptSeparator = null;
+ }
+ } else if (!aPopup._endOptOpenAllInTabs) {
+ // Create a separator before options.
+ aPopup._endOptSeparator = document.createElement("menuseparator");
+ aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
+ aPopup.appendChild(aPopup._endOptSeparator);
+
+ // Add the "Open All in Tabs" menuitem.
+ aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
+ aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
+
+ if (typeof this.options.extraClasses.entry == "string")
+ aPopup._endOptOpenAllInTabs.classList.add(this.options.extraClasses.entry);
+ if (typeof this.options.extraClasses.footer == "string")
+ aPopup._endOptOpenAllInTabs.classList.add(this.options.extraClasses.footer);
+
+ if (isLiveMark) {
+ aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
+ "PlacesUIUtils.openLiveMarkNodesInTabs(this.parentNode._placesNode, event, " +
+ "PlacesUIUtils.getViewForNode(this));");
+ } else {
+ aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
+ "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
+ "PlacesUIUtils.getViewForNode(this));");
+ }
+ aPopup._endOptOpenAllInTabs.setAttribute("onclick",
+ "checkForMiddleClick(this, event); event.stopPropagation();");
+ aPopup._endOptOpenAllInTabs.setAttribute("label",
+ gNavigatorBundle.getString("menuOpenAllInTabs.label"));
+ aPopup.appendChild(aPopup._endOptOpenAllInTabs);
+ }
+ },
+
+ _ensureMarkers: function PVB__ensureMarkers(aPopup) {
+ if (aPopup._startMarker)
+ return;
+
+ // _startMarker is an hidden menuseparator that lives before places nodes.
+ aPopup._startMarker = document.createElement("menuseparator");
+ aPopup._startMarker.hidden = true;
+ aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
+
+ // _endMarker is a DOM node that lives after places nodes, specified with
+ // the 'insertionPoint' option or will be a hidden menuseparator.
+ let node = this.options.insertionPoint ?
+ aPopup.querySelector(this.options.insertionPoint) : null;
+ if (node) {
+ aPopup._endMarker = node;
+ } else {
+ aPopup._endMarker = document.createElement("menuseparator");
+ aPopup._endMarker.hidden = true;
+ }
+ aPopup.appendChild(aPopup._endMarker);
+
+ // Move the markers to the right position.
+ let firstNonStaticNodeFound = false;
+ for (let i = 0; i < aPopup.childNodes.length; i++) {
+ let child = aPopup.childNodes[i];
+ // Menus that have static content at the end, but are initially empty,
+ // use a special "builder" attribute to figure out where to start
+ // inserting places nodes.
+ if (child.getAttribute("builder") == "end") {
+ aPopup.insertBefore(aPopup._endMarker, child);
+ break;
+ }
+
+ if (child._placesNode && !firstNonStaticNodeFound) {
+ firstNonStaticNodeFound = true;
+ aPopup.insertBefore(aPopup._startMarker, child);
+ }
+ }
+ if (!firstNonStaticNodeFound) {
+ aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
+ }
+ },
+
+ _onPopupShowing: function PVB__onPopupShowing(aEvent) {
+ // Avoid handling popupshowing of inner views.
+ let popup = aEvent.originalTarget;
+
+ this._ensureMarkers(popup);
+
+ // Remove any delayed element, see _cleanPopup for details.
+ if ("_delayedRemovals" in popup) {
+ while (popup._delayedRemovals.length > 0) {
+ popup.removeChild(popup._delayedRemovals.shift());
+ }
+ }
+
+ if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
+ if (!popup._placesNode.containerOpen)
+ popup._placesNode.containerOpen = true;
+ if (!popup._built)
+ this._rebuildPopup(popup);
+
+ this._mayAddCommandsItems(popup);
+ }
+ },
+
+ _addEventListeners:
+ function PVB__addEventListeners(aObject, aEventNames, aCapturing = false) {
+ for (let i = 0; i < aEventNames.length; i++) {
+ aObject.addEventListener(aEventNames[i], this, aCapturing);
+ }
+ },
+
+ _removeEventListeners:
+ function PVB__removeEventListeners(aObject, aEventNames, aCapturing = false) {
+ for (let i = 0; i < aEventNames.length; i++) {
+ aObject.removeEventListener(aEventNames[i], this, aCapturing);
+ }
+ },
+};
+
+function PlacesToolbar(aPlace) {
+ let startTime = Date.now();
+ // Add some smart getters for our elements.
+ let thisView = this;
+ [
+ ["_viewElt", "PlacesToolbar"],
+ ["_rootElt", "PlacesToolbarItems"],
+ ["_dropIndicator", "PlacesToolbarDropIndicator"],
+ ["_chevron", "PlacesChevron"],
+ ["_chevronPopup", "PlacesChevronPopup"]
+ ].forEach(function(elementGlobal) {
+ let [name, id] = elementGlobal;
+ thisView.__defineGetter__(name, function() {
+ let element = document.getElementById(id);
+ if (!element)
+ return null;
+
+ delete thisView[name];
+ return thisView[name] = element;
+ });
+ });
+
+ this._viewElt._placesView = this;
+
+ this._addEventListeners(this._viewElt, this._cbEvents, false);
+ this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
+ this._addEventListeners(this._rootElt, ["overflow", "underflow"], true);
+ this._addEventListeners(window, ["resize", "unload"], false);
+
+ // If personal-bookmarks has been dragged to the tabs toolbar,
+ // we have to track addition and removals of tabs, to properly
+ // recalculate the available space for bookmarks.
+ // TODO (bug 734730): Use a performant mutation listener when available.
+ if (this._viewElt.parentNode.parentNode == document.getElementById("TabsToolbar")) {
+ this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
+ }
+
+ PlacesViewBase.call(this, aPlace);
+}
+
+PlacesToolbar.prototype = {
+ __proto__: PlacesViewBase.prototype,
+
+ _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
+ "mousemove", "mouseover", "mouseout"],
+
+ QueryInterface: function PT_QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIDOMEventListener) ||
+ aIID.equals(Ci.nsITimerCallback))
+ return this;
+
+ return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
+ },
+
+ uninit: function PT_uninit() {
+ this._removeEventListeners(this._viewElt, this._cbEvents, false);
+ this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
+ true);
+ this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true);
+ this._removeEventListeners(window, ["resize", "unload"], false);
+ this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
+
+ if (this._chevron._placesView) {
+ this._chevron._placesView.uninit();
+ }
+
+ PlacesViewBase.prototype.uninit.apply(this, arguments);
+ },
+
+ _openedMenuButton: null,
+ _allowPopupShowing: true,
+
+ _rebuild: function PT__rebuild() {
+ // Clear out references to existing nodes, since they will be removed
+ // and re-added.
+ if (this._overFolder.elt)
+ this._clearOverFolder();
+
+ this._openedMenuButton = null;
+ while (this._rootElt.hasChildNodes()) {
+ this._rootElt.firstChild.remove();
+ }
+
+ let fragment = document.createDocumentFragment();
+ let cc = this._resultNode.childCount;
+ if (cc > 0) {
+ // There could be a lot of nodes, but we only want to build the ones that
+ // are likely to be shown, not all of them. Then we'll lazily create the
+ // missing nodes when needed.
+ // We don't want to cause reflows at every node insertion to calculate
+ // a precise size, thus we guess a size from the first node.
+ let button = this._insertNewItem(this._resultNode.getChild(0),
+ this._rootElt);
+ requestAnimationFrame(() => {
+ // May have been destroyed in the meanwhile.
+ if (!this._resultNode || !this._rootElt)
+ return;
+ // We assume a button with just the icon will be more or less a square,
+ // then compensate the measurement error by considering a larger screen
+ // width. Moreover the window could be bigger than the screen.
+ let size = button.clientHeight;
+ let limit = Math.min(cc, parseInt((window.screen.width * 1.5) / size));
+ for (let i = 1; i < limit; ++i) {
+ this._insertNewItem(this._resultNode.getChild(i), fragment);
+ }
+ this._rootElt.appendChild(fragment);
+
+ this.updateNodesVisibility();
+ });
+ }
+
+ if (this._chevronPopup.hasAttribute("type")) {
+ // Chevron has already been initialized, but since we are forcing
+ // a rebuild of the toolbar, it has to be rebuilt.
+ // Otherwise, it will be initialized when the toolbar overflows.
+ this._chevronPopup.place = this.place;
+ }
+ },
+
+ _insertNewItem:
+ function PT__insertNewItem(aChild, aInsertionNode, aBefore = null) {
+ this._domNodes.delete(aChild);
+
+ let type = aChild.type;
+ let button;
+ if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
+ button = document.createElement("toolbarseparator");
+ } else {
+ button = document.createElement("toolbarbutton");
+ button.className = "bookmark-item";
+ button.setAttribute("label", aChild.title || "");
+
+ if (PlacesUtils.containerTypes.includes(type)) {
+ button.setAttribute("type", "menu");
+ button.setAttribute("container", "true");
+
+ if (PlacesUtils.nodeIsQuery(aChild)) {
+ button.setAttribute("query", "true");
+ if (PlacesUtils.nodeIsTagQuery(aChild))
+ button.setAttribute("tagContainer", "true");
+ } else if (PlacesUtils.nodeIsFolder(aChild)) {
+ PlacesUtils.livemarks.getLivemark({ id: aChild.itemId })
+ .then(aLivemark => {
+ button.setAttribute("livemark", "true");
+ this.controller.cacheLivemarkInfo(aChild, aLivemark);
+ }, () => undefined);
+ }
+
+ let popup = document.createElement("menupopup");
+ popup.setAttribute("placespopup", "true");
+ button.appendChild(popup);
+ popup._placesNode = PlacesUtils.asContainer(aChild);
+ popup.setAttribute("context", "placesContext");
+
+ this._domNodes.set(aChild, popup);
+ } else if (PlacesUtils.nodeIsURI(aChild)) {
+ button.setAttribute("scheme",
+ PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
+ }
+ }
+
+ button._placesNode = aChild;
+ if (!this._domNodes.has(aChild))
+ this._domNodes.set(aChild, button);
+
+ if (aBefore)
+ aInsertionNode.insertBefore(button, aBefore);
+ else
+ aInsertionNode.appendChild(button);
+ return button;
+ },
+
+ _updateChevronPopupNodesVisibility:
+ function PT__updateChevronPopupNodesVisibility() {
+ // Note the toolbar by default builds less nodes than the chevron popup.
+ for (let toolbarNode = this._rootElt.firstChild,
+ node = this._chevronPopup._startMarker.nextSibling;
+ toolbarNode && node;
+ toolbarNode = toolbarNode.nextSibling, node = node.nextSibling) {
+ node.hidden = toolbarNode.style.visibility != "hidden";
+ }
+ },
+
+ _onChevronPopupShowing:
+ function PT__onChevronPopupShowing(aEvent) {
+ // Handle popupshowing only for the chevron popup, not for nested ones.
+ if (aEvent.target != this._chevronPopup)
+ return;
+
+ if (!this._chevron._placesView)
+ this._chevron._placesView = new PlacesMenu(aEvent, this.place);
+
+ this._updateChevronPopupNodesVisibility();
+ },
+
+ handleEvent: function PT_handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "unload":
+ this.uninit();
+ break;
+ case "resize":
+ // This handler updates nodes visibility in both the toolbar
+ // and the chevron popup when a window resize does not change
+ // the overflow status of the toolbar.
+ if (aEvent.target == aEvent.currentTarget) {
+ this.updateNodesVisibility();
+ }
+ break;
+ case "overflow":
+ if (!this._isOverflowStateEventRelevant(aEvent))
+ return;
+ this._onOverflow();
+ break;
+ case "underflow":
+ if (!this._isOverflowStateEventRelevant(aEvent))
+ return;
+ this._onUnderflow();
+ break;
+ case "TabOpen":
+ case "TabClose":
+ this.updateNodesVisibility();
+ break;
+ case "dragstart":
+ this._onDragStart(aEvent);
+ break;
+ case "dragover":
+ this._onDragOver(aEvent);
+ break;
+ case "dragexit":
+ this._onDragExit(aEvent);
+ break;
+ case "dragend":
+ this._onDragEnd(aEvent);
+ break;
+ case "drop":
+ this._onDrop(aEvent);
+ break;
+ case "mouseover":
+ this._onMouseOver(aEvent);
+ break;
+ case "mousemove":
+ this._onMouseMove(aEvent);
+ break;
+ case "mouseout":
+ this._onMouseOut(aEvent);
+ break;
+ case "popupshowing":
+ this._onPopupShowing(aEvent);
+ break;
+ case "popuphidden":
+ this._onPopupHidden(aEvent);
+ break;
+ default:
+ throw "Trying to handle unexpected event.";
+ }
+ },
+
+ _isOverflowStateEventRelevant: function PT_isOverflowStateEventRelevant(aEvent) {
+ // Ignore events not aimed at ourselves, as well as purely vertical ones:
+ return aEvent.target == aEvent.currentTarget && aEvent.detail > 0;
+ },
+
+ _onOverflow: function PT_onOverflow() {
+ // Attach the popup binding to the chevron popup if it has not yet
+ // been initialized.
+ if (!this._chevronPopup.hasAttribute("type")) {
+ this._chevronPopup.setAttribute("place", this.place);
+ this._chevronPopup.setAttribute("type", "places");
+ }
+ this._chevron.collapsed = false;
+ this.updateNodesVisibility();
+ },
+
+ _onUnderflow: function PT_onUnderflow() {
+ this.updateNodesVisibility();
+ this._chevron.collapsed = true;
+ },
+
+ updateNodesVisibility: function PT_updateNodesVisibility() {
+ // Update the chevron on a timer. This will avoid repeated work when
+ // lot of changes happen in a small timeframe.
+ if (this._updateNodesVisibilityTimer)
+ this._updateNodesVisibilityTimer.cancel();
+
+ this._updateNodesVisibilityTimer = this._setTimer(100);
+ },
+
+ _updateNodesVisibilityTimerCallback: function PT__updateNodesVisibilityTimerCallback() {
+ let scrollRect = this._rootElt.getBoundingClientRect();
+ let childOverflowed = false;
+ for (let child of this._rootElt.childNodes) {
+ // Once a child overflows, all the next ones will.
+ if (!childOverflowed) {
+ let childRect = child.getBoundingClientRect();
+ childOverflowed = this.isRTL ? (childRect.left < scrollRect.left)
+ : (childRect.right > scrollRect.right);
+ }
+
+ if (childOverflowed) {
+ child.removeAttribute("image");
+ child.style.visibility = "hidden";
+ } else {
+ let icon = child._placesNode.icon;
+ if (icon)
+ child.setAttribute("image", icon);
+ child.style.visibility = "visible";
+ }
+ }
+
+ // We rebuild the chevron on popupShowing, so if it is open
+ // we must update it.
+ if (!this._chevron.collapsed && this._chevron.open)
+ this._updateChevronPopupNodesVisibility();
+ let event = new CustomEvent("BookmarksToolbarVisibilityUpdated", {bubbles: true});
+ this._viewElt.dispatchEvent(event);
+ },
+
+ nodeInserted:
+ function PT_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
+ if (parentElt == this._rootElt) { // Node is on the toolbar.
+ let children = this._rootElt.childNodes;
+ // Nothing to do if it's a never-visible node, but note it's possible
+ // we are appending.
+ if (aIndex > children.length)
+ return;
+
+ // Note that childCount is already accounting for the node being added,
+ // thus we must subtract one node from it.
+ if (this._resultNode.childCount - 1 > children.length) {
+ if (aIndex == children.length) {
+ // If we didn't build all the nodes and new node is being appended,
+ // we can skip it as well.
+ return;
+ }
+ // Keep the number of built nodes consistent.
+ this._rootElt.removeChild(this._rootElt.lastChild);
+ }
+
+ let button = this._insertNewItem(aPlacesNode, this._rootElt,
+ children[aIndex] || null);
+ let prevSiblingOverflowed = aIndex > 0 && aIndex <= children.length &&
+ children[aIndex - 1].style.visibility == "hidden";
+ if (prevSiblingOverflowed) {
+ button.style.visibility = "hidden";
+ } else {
+ let icon = aPlacesNode.icon;
+ if (icon)
+ button.setAttribute("image", icon);
+ this.updateNodesVisibility();
+ }
+ return;
+ }
+
+ PlacesViewBase.prototype.nodeInserted.apply(this, arguments);
+ },
+
+ nodeRemoved:
+ function PT_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
+ if (parentElt == this._rootElt) { // Node is on the toolbar.
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
+ // Nothing to do if it's a never-visible node.
+ if (!elt)
+ return;
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ let overflowed = elt.style.visibility == "hidden";
+ this._removeChild(elt);
+ if (this._resultNode.childCount > this._rootElt.childNodes.length) {
+ // A new node should be built to keep a coherent number of children.
+ this._insertNewItem(this._resultNode.getChild(this._rootElt.childNodes.length),
+ this._rootElt);
+ }
+ if (!overflowed)
+ this.updateNodesVisibility();
+ return;
+ }
+
+ PlacesViewBase.prototype.nodeRemoved.apply(this, arguments);
+ },
+
+ nodeMoved:
+ function PT_nodeMoved(aPlacesNode,
+ aOldParentPlacesNode, aOldIndex,
+ aNewParentPlacesNode, aNewIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
+ if (parentElt == this._rootElt) { // Node is on the toolbar.
+ // Do nothing if the node will never be visible.
+ let lastBuiltIndex = this._rootElt.childNodes.length - 1;
+ if (aOldIndex > lastBuiltIndex && aNewIndex > lastBuiltIndex + 1)
+ return;
+
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
+ if (elt) {
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+ this._removeChild(elt);
+ }
+
+ if (aNewIndex > lastBuiltIndex + 1) {
+ if (this._resultNode.childCount > this._rootElt.childNodes.length) {
+ // If the element was built and becomes non built, another node should
+ // be built to keep a coherent number of children.
+ this._insertNewItem(this._resultNode.getChild(this._rootElt.childNodes.length),
+ this._rootElt);
+ }
+ return;
+ }
+
+ if (!elt) {
+ // The node has not been inserted yet, so we must create it.
+ elt = this._insertNewItem(aPlacesNode, this._rootElt, this._rootElt.childNodes[aNewIndex]);
+ let icon = aPlacesNode.icon;
+ if (icon)
+ elt.setAttribute("image", icon);
+ } else {
+ this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
+ }
+
+ // The chevron view may get nodeMoved after the toolbar. In such a case,
+ // we should ensure (by manually swapping menuitems) that the actual nodes
+ // are in the final position before updateNodesVisibility tries to update
+ // their visibility, or the chevron may go out of sync.
+ // Luckily updateNodesVisibility runs on a timer, so, by the time it updates
+ // nodes, the menu has already handled the notification.
+
+ this.updateNodesVisibility();
+ return;
+ }
+
+ PlacesViewBase.prototype.nodeMoved.apply(this, arguments);
+ },
+
+ nodeAnnotationChanged:
+ function PT_nodeAnnotationChanged(aPlacesNode, aAnno) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
+ // Nothing to do if it's a never-visible node.
+ if (!elt || elt == this._rootElt)
+ return;
+
+ // We're notified for the menupopup, not the containing toolbarbutton.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ if (elt.parentNode == this._rootElt) { // Node is on the toolbar.
+ // All livemarks have a feedURI, so use it as our indicator.
+ if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
+ elt.setAttribute("livemark", true);
+
+ PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
+ .then(aLivemark => {
+ this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
+ this.invalidateContainer(aPlacesNode);
+ }, Cu.reportError);
+ }
+ } else {
+ // Node is in a submenu.
+ PlacesViewBase.prototype.nodeAnnotationChanged.apply(this, arguments);
+ }
+ },
+
+ nodeTitleChanged: function PT_nodeTitleChanged(aPlacesNode, aNewTitle) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
+
+ // Nothing to do if it's a never-visible node.
+ if (!elt || elt == this._rootElt)
+ return;
+
+ PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments);
+
+ // Here we need the <menu>.
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ if (elt.parentNode == this._rootElt) { // Node is on the toolbar.
+ if (elt.style.visibility != "hidden")
+ this.updateNodesVisibility();
+ }
+ },
+
+ invalidateContainer: function PT_invalidateContainer(aPlacesNode) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode, true);
+ // Nothing to do if it's a never-visible node.
+ if (!elt)
+ return;
+
+ if (elt == this._rootElt) {
+ // Container is the toolbar itself.
+ this._rebuild();
+ return;
+ }
+
+ PlacesViewBase.prototype.invalidateContainer.apply(this, arguments);
+ },
+
+ _overFolder: { elt: null,
+ openTimer: null,
+ hoverTime: 350,
+ closeTimer: null },
+
+ _clearOverFolder: function PT__clearOverFolder() {
+ // The mouse is no longer dragging over the stored menubutton.
+ // Close the menubutton, clear out drag styles, and clear all
+ // timers for opening/closing it.
+ if (this._overFolder.elt && this._overFolder.elt.lastChild) {
+ if (!this._overFolder.elt.lastChild.hasAttribute("dragover")) {
+ this._overFolder.elt.lastChild.hidePopup();
+ }
+ this._overFolder.elt.removeAttribute("dragover");
+ this._overFolder.elt = null;
+ }
+ if (this._overFolder.openTimer) {
+ this._overFolder.openTimer.cancel();
+ this._overFolder.openTimer = null;
+ }
+ if (this._overFolder.closeTimer) {
+ this._overFolder.closeTimer.cancel();
+ this._overFolder.closeTimer = null;
+ }
+ },
+
+ /**
+ * This function returns information about where to drop when dragging over
+ * the toolbar. The returned object has the following properties:
+ * - ip: the insertion point for the bookmarks service.
+ * - beforeIndex: child index to drop before, for the drop indicator.
+ * - folderElt: the folder to drop into, if applicable.
+ */
+ _getDropPoint: function PT__getDropPoint(aEvent) {
+ if (!PlacesUtils.nodeIsFolder(this._resultNode))
+ return null;
+
+ let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
+ let elt = aEvent.target;
+ if (elt._placesNode && elt != this._rootElt &&
+ elt.localName != "menupopup") {
+ let eltRect = elt.getBoundingClientRect();
+ let eltIndex = Array.prototype.indexOf.call(this._rootElt.childNodes, elt);
+ if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
+ !PlacesUIUtils.isFolderReadOnly(elt._placesNode, this)) {
+ // This is a folder.
+ // If we are in the middle of it, drop inside it.
+ // Otherwise, drop before it, with regards to RTL mode.
+ let threshold = eltRect.width * 0.25;
+ if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
+ : (aEvent.clientX < eltRect.left + threshold)) {
+ // Drop before this folder.
+ dropPoint.ip =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(this._resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
+ index: eltIndex,
+ orientation: Ci.nsITreeView.DROP_BEFORE
+ });
+ dropPoint.beforeIndex = eltIndex;
+ } else if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
+ : (aEvent.clientX < eltRect.right - threshold)) {
+ // Drop inside this folder.
+ let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
+ elt._placesNode.title : null;
+ dropPoint.ip =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(elt._placesNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode),
+ tagName
+ });
+ dropPoint.beforeIndex = eltIndex;
+ dropPoint.folderElt = elt;
+ } else {
+ // Drop after this folder.
+ let beforeIndex =
+ (eltIndex == this._rootElt.childNodes.length - 1) ?
+ -1 : eltIndex + 1;
+
+ dropPoint.ip =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(this._resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
+ index: beforeIndex,
+ orientation: Ci.nsITreeView.DROP_BEFORE
+ });
+ dropPoint.beforeIndex = beforeIndex;
+ }
+ } else {
+ // This is a non-folder node or a read-only folder.
+ // Drop before it with regards to RTL mode.
+ let threshold = eltRect.width * 0.5;
+ if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
+ : (aEvent.clientX < eltRect.left + threshold)) {
+ // Drop before this bookmark.
+ dropPoint.ip =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(this._resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
+ index: eltIndex,
+ orientation: Ci.nsITreeView.DROP_BEFORE
+ });
+ dropPoint.beforeIndex = eltIndex;
+ } else {
+ // Drop after this bookmark.
+ let beforeIndex =
+ eltIndex == this._rootElt.childNodes.length - 1 ?
+ -1 : eltIndex + 1;
+ dropPoint.ip =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(this._resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
+ index: beforeIndex,
+ orientation: Ci.nsITreeView.DROP_BEFORE
+ });
+ dropPoint.beforeIndex = beforeIndex;
+ }
+ }
+ } else {
+ // We are most likely dragging on the empty area of the
+ // toolbar, we should drop after the last node.
+ dropPoint.ip =
+ new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(this._resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(this._resultNode),
+ orientation: Ci.nsITreeView.DROP_BEFORE
+ });
+ dropPoint.beforeIndex = -1;
+ }
+
+ return dropPoint;
+ },
+
+ _setTimer: function PT_setTimer(aTime) {
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
+ return timer;
+ },
+
+ notify: function PT_notify(aTimer) {
+ if (aTimer == this._updateNodesVisibilityTimer) {
+ this._updateNodesVisibilityTimer = null;
+ // Bug 1440070: This should use promiseDocumentFlushed, so that
+ // _updateNodesVisibilityTimerCallback can use getBoundsWithoutFlush.
+ window.requestAnimationFrame(this._updateNodesVisibilityTimerCallback.bind(this));
+ } else if (aTimer == this._ibTimer) {
+ // * Timer to turn off indicator bar.
+ this._dropIndicator.collapsed = true;
+ this._ibTimer = null;
+ } else if (aTimer == this._overFolder.openTimer) {
+ // * Timer to open a menubutton that's being dragged over.
+ // Set the autoopen attribute on the folder's menupopup so that
+ // the menu will automatically close when the mouse drags off of it.
+ this._overFolder.elt.lastChild.setAttribute("autoopened", "true");
+ this._overFolder.elt.open = true;
+ this._overFolder.openTimer = null;
+ } else if (aTimer == this._overFolder.closeTimer) {
+ // * Timer to close a menubutton that's been dragged off of.
+ // Close the menubutton if we are not dragging over it or one of
+ // its children. The autoopened attribute will let the menu know to
+ // close later if the menu is still being dragged over.
+ let currentPlacesNode = PlacesControllerDragHelper.currentDropTarget;
+ let inHierarchy = false;
+ while (currentPlacesNode) {
+ if (currentPlacesNode == this._rootElt) {
+ inHierarchy = true;
+ break;
+ }
+ currentPlacesNode = currentPlacesNode.parentNode;
+ }
+ // The _clearOverFolder() function will close the menu for
+ // _overFolder.elt. So null it out if we don't want to close it.
+ if (inHierarchy)
+ this._overFolder.elt = null;
+
+ // Clear out the folder and all associated timers.
+ this._clearOverFolder();
+ }
+ },
+
+ _onMouseOver: function PT__onMouseOver(aEvent) {
+ let button = aEvent.target;
+ if (button.parentNode == this._rootElt && button._placesNode &&
+ PlacesUtils.nodeIsURI(button._placesNode))
+ window.XULBrowserWindow.setOverLink(aEvent.target._placesNode.uri, null);
+ },
+
+ _onMouseOut: function PT__onMouseOut(aEvent) {
+ window.XULBrowserWindow.setOverLink("", null);
+ },
+
+ _cleanupDragDetails: function PT__cleanupDragDetails() {
+ // Called on dragend and drop.
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this._draggedElt = null;
+ if (this._ibTimer)
+ this._ibTimer.cancel();
+
+ this._dropIndicator.collapsed = true;
+ },
+
+ _onDragStart: function PT__onDragStart(aEvent) {
+ // Sub menus have their own d&d handlers.
+ let draggedElt = aEvent.target;
+ if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
+ return;
+
+ if (draggedElt.localName == "toolbarbutton" &&
+ draggedElt.getAttribute("type") == "menu") {
+ // If the drag gesture on a container is toward down we open instead
+ // of dragging.
+ let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY;
+ let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX;
+ if ((translateY) >= Math.abs(translateX / 2)) {
+ // Don't start the drag.
+ aEvent.preventDefault();
+ // Open the menu.
+ draggedElt.open = true;
+ return;
+ }
+
+ // If the menu is open, close it.
+ if (draggedElt.open) {
+ draggedElt.lastChild.hidePopup();
+ draggedElt.open = false;
+ }
+ }
+
+ // Activate the view and cache the dragged element.
+ this._draggedElt = draggedElt._placesNode;
+ this._rootElt.focus();
+
+ this._controller.setDataTransfer(aEvent);
+ aEvent.stopPropagation();
+ },
+
+ _onDragOver: function PT__onDragOver(aEvent) {
+ // Cache the dataTransfer
+ PlacesControllerDragHelper.currentDropTarget = aEvent.target;
+ let dt = aEvent.dataTransfer;
+
+ let dropPoint = this._getDropPoint(aEvent);
+ if (!dropPoint || !dropPoint.ip ||
+ !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
+ this._dropIndicator.collapsed = true;
+ aEvent.stopPropagation();
+ return;
+ }
+
+ if (this._ibTimer) {
+ this._ibTimer.cancel();
+ this._ibTimer = null;
+ }
+
+ if (dropPoint.folderElt || aEvent.originalTarget == this._chevron) {
+ // Dropping over a menubutton or chevron button.
+ // Set styles and timer to open relative menupopup.
+ let overElt = dropPoint.folderElt || this._chevron;
+ if (this._overFolder.elt != overElt) {
+ this._clearOverFolder();
+ this._overFolder.elt = overElt;
+ this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime);
+ }
+ if (!this._overFolder.elt.hasAttribute("dragover"))
+ this._overFolder.elt.setAttribute("dragover", "true");
+
+ this._dropIndicator.collapsed = true;
+ } else {
+ // Dragging over a normal toolbarbutton,
+ // show indicator bar and move it to the appropriate drop point.
+ let ind = this._dropIndicator;
+ ind.parentNode.collapsed = false;
+ let halfInd = ind.clientWidth / 2;
+ let translateX;
+ if (this.isRTL) {
+ halfInd = Math.ceil(halfInd);
+ translateX = 0 - this._rootElt.getBoundingClientRect().right - halfInd;
+ if (this._rootElt.firstChild) {
+ if (dropPoint.beforeIndex == -1)
+ translateX += this._rootElt.lastChild.getBoundingClientRect().left;
+ else {
+ translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
+ .getBoundingClientRect().right;
+ }
+ }
+ } else {
+ halfInd = Math.floor(halfInd);
+ translateX = 0 - this._rootElt.getBoundingClientRect().left +
+ halfInd;
+ if (this._rootElt.firstChild) {
+ if (dropPoint.beforeIndex == -1)
+ translateX += this._rootElt.lastChild.getBoundingClientRect().right;
+ else {
+ translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
+ .getBoundingClientRect().left;
+ }
+ }
+ }
+
+ ind.style.transform = "translate(" + Math.round(translateX) + "px)";
+ ind.style.marginInlineStart = (-ind.clientWidth) + "px";
+ ind.collapsed = false;
+
+ // Clear out old folder information.
+ this._clearOverFolder();
+ }
+
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ },
+
+ _onDrop: function PT__onDrop(aEvent) {
+ PlacesControllerDragHelper.currentDropTarget = aEvent.target;
+
+ let dropPoint = this._getDropPoint(aEvent);
+ if (dropPoint && dropPoint.ip) {
+ PlacesControllerDragHelper.onDrop(dropPoint.ip, aEvent.dataTransfer)
+ .catch(Cu.reportError);
+ aEvent.preventDefault();
+ }
+
+ this._cleanupDragDetails();
+ aEvent.stopPropagation();
+ },
+
+ _onDragExit: function PT__onDragExit(aEvent) {
+ PlacesControllerDragHelper.currentDropTarget = null;
+
+ // Set timer to turn off indicator bar (if we turn it off
+ // here, dragenter might be called immediately after, creating
+ // flicker).
+ if (this._ibTimer)
+ this._ibTimer.cancel();
+ this._ibTimer = this._setTimer(10);
+
+ // If we hovered over a folder, close it now.
+ if (this._overFolder.elt)
+ this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
+ },
+
+ _onDragEnd: function PT_onDragEnd(aEvent) {
+ this._cleanupDragDetails();
+ },
+
+ _onPopupShowing: function PT__onPopupShowing(aEvent) {
+ if (!this._allowPopupShowing) {
+ this._allowPopupShowing = true;
+ aEvent.preventDefault();
+ return;
+ }
+
+ let parent = aEvent.target.parentNode;
+ if (parent.localName == "toolbarbutton")
+ this._openedMenuButton = parent;
+
+ PlacesViewBase.prototype._onPopupShowing.apply(this, arguments);
+ },
+
+ _onPopupHidden: function PT__onPopupHidden(aEvent) {
+ let popup = aEvent.target;
+ let placesNode = popup._placesNode;
+ // Avoid handling popuphidden of inner views
+ if (placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
+ // UI performance: folder queries are cheap, keep the resultnode open
+ // so we don't rebuild its contents whenever the popup is reopened.
+ // Though, we want to always close feed containers so their expiration
+ // status will be checked at next opening.
+ if (!PlacesUtils.nodeIsFolder(placesNode) ||
+ this.controller.hasCachedLivemarkInfo(placesNode)) {
+ placesNode.containerOpen = false;
+ }
+ }
+
+ let parent = popup.parentNode;
+ if (parent.localName == "toolbarbutton") {
+ this._openedMenuButton = null;
+ // Clear the dragover attribute if present, if we are dragging into a
+ // folder in the hierachy of current opened popup we don't clear
+ // this attribute on clearOverFolder. See Notify for closeTimer.
+ if (parent.hasAttribute("dragover"))
+ parent.removeAttribute("dragover");
+ }
+ },
+
+ _onMouseMove: function PT__onMouseMove(aEvent) {
+ // Used in dragStart to prevent dragging folders when dragging down.
+ this._cachedMouseMoveEvent = aEvent;
+
+ if (this._openedMenuButton == null ||
+ PlacesControllerDragHelper.getSession())
+ return;
+
+ let target = aEvent.originalTarget;
+ if (this._openedMenuButton != target &&
+ target.localName == "toolbarbutton" &&
+ target.type == "menu") {
+ this._openedMenuButton.open = false;
+ target.open = true;
+ }
+ }
+};
+
+/**
+ * View for Places menus. This object should be created during the first
+ * popupshowing that's dispatched on the menu.
+ */
+function PlacesMenu(aPopupShowingEvent, aPlace, aOptions) {
+ this._rootElt = aPopupShowingEvent.target; // <menupopup>
+ this._viewElt = this._rootElt.parentNode; // <menu>
+ this._viewElt._placesView = this;
+ this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
+ this._addEventListeners(window, ["unload"], false);
+
+ if (AppConstants.platform === "macosx") {
+ // Must walk up to support views in sub-menus, like Bookmarks Toolbar menu.
+ for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) {
+ if (elt.localName == "menubar") {
+ this._nativeView = true;
+ break;
+ }
+ }
+ }
+
+ PlacesViewBase.call(this, aPlace, aOptions);
+ this._onPopupShowing(aPopupShowingEvent);
+}
+
+PlacesMenu.prototype = {
+ __proto__: PlacesViewBase.prototype,
+
+ QueryInterface: function PM_QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIDOMEventListener))
+ return this;
+
+ return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
+ },
+
+ _removeChild: function PM_removeChild(aChild) {
+ PlacesViewBase.prototype._removeChild.apply(this, arguments);
+ },
+
+ uninit: function PM_uninit() {
+ this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
+ true);
+ this._removeEventListeners(window, ["unload"], false);
+
+ PlacesViewBase.prototype.uninit.apply(this, arguments);
+ },
+
+ handleEvent: function PM_handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "unload":
+ this.uninit();
+ break;
+ case "popupshowing":
+ this._onPopupShowing(aEvent);
+ break;
+ case "popuphidden":
+ this._onPopupHidden(aEvent);
+ break;
+ }
+ },
+
+ _onPopupHidden: function PM__onPopupHidden(aEvent) {
+ // Avoid handling popuphidden of inner views.
+ let popup = aEvent.originalTarget;
+ let placesNode = popup._placesNode;
+ if (!placesNode || PlacesUIUtils.getViewForNode(popup) != this)
+ return;
+
+ // UI performance: folder queries are cheap, keep the resultnode open
+ // so we don't rebuild its contents whenever the popup is reopened.
+ // Though, we want to always close feed containers so their expiration
+ // status will be checked at next opening.
+ if (!PlacesUtils.nodeIsFolder(placesNode) ||
+ this.controller.hasCachedLivemarkInfo(placesNode))
+ placesNode.containerOpen = false;
+
+ // The autoopened attribute is set for folders which have been
+ // automatically opened when dragged over. Turn off this attribute
+ // when the folder closes because it is no longer applicable.
+ popup.removeAttribute("autoopened");
+ popup.removeAttribute("dragstart");
+ }
+};
+
+function PlacesPanelMenuView(aPlace, aViewId, aRootId, aOptions) {
+ this._viewElt = document.getElementById(aViewId);
+ this._rootElt = document.getElementById(aRootId);
+ this._viewElt._placesView = this;
+ this.options = aOptions;
+
+ PlacesViewBase.call(this, aPlace, aOptions);
+}
+
+PlacesPanelMenuView.prototype = {
+ __proto__: PlacesViewBase.prototype,
+
+ QueryInterface: function PAMV_QueryInterface(aIID) {
+ return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
+ },
+
+ uninit: function PAMV_uninit() {
+ PlacesViewBase.prototype.uninit.apply(this, arguments);
+ },
+
+ _insertNewItem:
+ function PAMV__insertNewItem(aChild, aInsertionNode, aBefore = null) {
+ this._domNodes.delete(aChild);
+
+ let type = aChild.type;
+ let button;
+ if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
+ button = document.createElement("toolbarseparator");
+ button.setAttribute("class", "small-separator");
+ } else {
+ button = document.createElement("toolbarbutton");
+ button.className = "bookmark-item";
+ if (typeof this.options.extraClasses.entry == "string")
+ button.classList.add(this.options.extraClasses.entry);
+ button.setAttribute("label", aChild.title || "");
+ let icon = aChild.icon;
+ if (icon)
+ button.setAttribute("image", icon);
+
+ if (PlacesUtils.containerTypes.includes(type)) {
+ button.setAttribute("container", "true");
+
+ if (PlacesUtils.nodeIsQuery(aChild)) {
+ button.setAttribute("query", "true");
+ if (PlacesUtils.nodeIsTagQuery(aChild))
+ button.setAttribute("tagContainer", "true");
+ } else if (PlacesUtils.nodeIsFolder(aChild)) {
+ PlacesUtils.livemarks.getLivemark({ id: aChild.itemId })
+ .then(aLivemark => {
+ button.setAttribute("livemark", "true");
+ this.controller.cacheLivemarkInfo(aChild, aLivemark);
+ }, () => undefined);
+ }
+ } else if (PlacesUtils.nodeIsURI(aChild)) {
+ button.setAttribute("scheme",
+ PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
+ }
+ }
+
+ button._placesNode = aChild;
+ if (!this._domNodes.has(aChild))
+ this._domNodes.set(aChild, button);
+
+ aInsertionNode.insertBefore(button, aBefore);
+ return button;
+ },
+
+ nodeInserted:
+ function PAMV_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
+ if (parentElt != this._rootElt)
+ return;
+
+ let children = this._rootElt.childNodes;
+ this._insertNewItem(aPlacesNode, this._rootElt,
+ aIndex < children.length ? children[aIndex] : null);
+ },
+
+ nodeRemoved:
+ function PAMV_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
+ if (parentElt != this._rootElt)
+ return;
+
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+ this._removeChild(elt);
+ },
+
+ nodeMoved:
+ function PAMV_nodeMoved(aPlacesNode,
+ aOldParentPlacesNode, aOldIndex,
+ aNewParentPlacesNode, aNewIndex) {
+ let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
+ if (parentElt != this._rootElt)
+ return;
+
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+ this._removeChild(elt);
+ this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
+ },
+
+ nodeAnnotationChanged:
+ function PAMV_nodeAnnotationChanged(aPlacesNode, aAnno) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+ // There's no UI representation for the root node.
+ if (elt == this._rootElt)
+ return;
+
+ if (elt.parentNode != this._rootElt)
+ return;
+
+ // All livemarks have a feedURI, so use it as our indicator.
+ if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
+ elt.setAttribute("livemark", true);
+
+ PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
+ .then(aLivemark => {
+ this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
+ this.invalidateContainer(aPlacesNode);
+ }, Cu.reportError);
+ }
+ },
+
+ nodeTitleChanged: function PAMV_nodeTitleChanged(aPlacesNode, aNewTitle) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+
+ // There's no UI representation for the root node.
+ if (elt == this._rootElt)
+ return;
+
+ PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments);
+ },
+
+ invalidateContainer: function PAMV_invalidateContainer(aPlacesNode) {
+ let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
+ if (elt != this._rootElt)
+ return;
+
+ // Container is the toolbar itself.
+ while (this._rootElt.hasChildNodes()) {
+ this._rootElt.firstChild.remove();
+ }
+
+ let fragment = document.createDocumentFragment();
+ for (let i = 0; i < this._resultNode.childCount; ++i) {
+ this._insertNewItem(this._resultNode.getChild(i), fragment);
+ }
+ this._rootElt.appendChild(fragment);
+ }
+};
+
+this.PlacesPanelview = class extends PlacesViewBase {
+ constructor(container, panelview, place, options = {}) {
+ options.rootElt = container;
+ options.viewElt = panelview;
+ super(place, options);
+ this._viewElt._placesView = this;
+ // We're simulating a popup show, because a panelview may only be shown when
+ // its containing popup is already shown.
+ this._onPopupShowing({ originalTarget: this._rootElt });
+ this._addEventListeners(window, ["unload"]);
+ this._rootElt.setAttribute("context", "placesContext");
+ }
+
+ get events() {
+ if (this._events)
+ return this._events;
+ return this._events = ["click", "command", "dragend", "dragstart", "ViewHiding", "ViewShown"];
+ }
+
+ handleEvent(event) {
+ switch (event.type) {
+ case "click":
+ // For middle clicks, fall through to the command handler.
+ if (event.button != 1) {
+ break;
+ }
+ case "command":
+ this._onCommand(event);
+ break;
+ case "dragend":
+ this._onDragEnd(event);
+ break;
+ case "dragstart":
+ this._onDragStart(event);
+ break;
+ case "unload":
+ this.uninit(event);
+ break;
+ case "ViewHiding":
+ this._onPopupHidden(event);
+ break;
+ case "ViewShown":
+ this._onViewShown(event);
+ break;
+ }
+ }
+
+ _onCommand(event) {
+ let button = event.originalTarget;
+ if (!button._placesNode)
+ return;
+
+ let modifKey = AppConstants.platform === "macosx" ? event.metaKey
+ : event.ctrlKey;
+ if (!PlacesUIUtils.openInTabClosesMenu && modifKey) {
+ // If 'Recent Bookmarks' in Bookmarks Panel.
+ if (button.parentNode.id == "panelMenu_bookmarksMenu") {
+ button.setAttribute("closemenu", "none");
+ }
+ } else {
+ button.removeAttribute("closemenu");
+ }
+ PlacesUIUtils.openNodeWithEvent(button._placesNode, event);
+ // Unlike left-click, middle-click requires manual menu closing.
+ if (button.parentNode.id != "panelMenu_bookmarksMenu" ||
+ (event.type == "click" && event.button == 1 && PlacesUIUtils.openInTabClosesMenu)) {
+ this.panelMultiView.closest("panel").hidePopup();
+ }
+ }
+
+ _onDragEnd() {
+ this._draggedElt = null;
+ }
+
+ _onDragStart(event) {
+ let draggedElt = event.originalTarget;
+ if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
+ return;
+
+ // Activate the view and cache the dragged element.
+ this._draggedElt = draggedElt._placesNode;
+ this._rootElt.focus();
+
+ this._controller.setDataTransfer(event);
+ event.stopPropagation();
+ }
+
+ uninit(event) {
+ this._removeEventListeners(this.panelMultiView, this.events);
+ this._removeEventListeners(window, ["unload"]);
+ delete this.panelMultiView;
+ super.uninit(event);
+ }
+
+ _createDOMNodeForPlacesNode(placesNode) {
+ this._domNodes.delete(placesNode);
+
+ let element;
+ let type = placesNode.type;
+ if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
+ element = document.createElement("toolbarseparator");
+ } else {
+ if (type != Ci.nsINavHistoryResultNode.RESULT_TYPE_URI)
+ throw "Unexpected node";
+
+ element = document.createElement("toolbarbutton");
+ element.classList.add("subviewbutton", "subviewbutton-iconic", "bookmark-item");
+ element.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(placesNode.uri));
+ element.setAttribute("label", PlacesUIUtils.getBestTitle(placesNode));
+
+ let icon = placesNode.icon;
+ if (icon)
+ element.setAttribute("image", icon);
+ }
+
+ element._placesNode = placesNode;
+ if (!this._domNodes.has(placesNode))
+ this._domNodes.set(placesNode, element);
+
+ return element;
+ }
+
+ _setEmptyPopupStatus(panelview, empty = false) {
+ if (!panelview._emptyMenuitem) {
+ let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
+ panelview._emptyMenuitem = document.createElement("toolbarbutton");
+ panelview._emptyMenuitem.setAttribute("label", label);
+ panelview._emptyMenuitem.setAttribute("disabled", true);
+ panelview._emptyMenuitem.className = "subviewbutton";
+ if (typeof this.options.extraClasses.entry == "string")
+ panelview._emptyMenuitem.classList.add(this.options.extraClasses.entry);
+ }
+
+ if (empty) {
+ panelview.setAttribute("emptyplacesresult", "true");
+ // Don't add the menuitem if there is static content.
+ // We also support external usage for custom crafted panels - which'll have
+ // no markers present.
+ if (!panelview._startMarker ||
+ (!panelview._startMarker.previousSibling && !panelview._endMarker.nextSibling)) {
+ panelview.insertBefore(panelview._emptyMenuitem, panelview._endMarker);
+ }
+ } else {
+ panelview.removeAttribute("emptyplacesresult");
+ try {
+ panelview.removeChild(panelview._emptyMenuitem);
+ } catch (ex) {}
+ }
+ }
+
+ _isPopupOpen() {
+ return PanelView.forNode(this._viewElt).active;
+ }
+
+ _onPopupHidden(event) {
+ let panelview = event.originalTarget;
+ let placesNode = panelview._placesNode;
+ // Avoid handling ViewHiding of inner views
+ if (placesNode && PlacesUIUtils.getViewForNode(panelview) == this) {
+ // UI performance: folder queries are cheap, keep the resultnode open
+ // so we don't rebuild its contents whenever the popup is reopened.
+ // Though, we want to always close feed containers so their expiration
+ // status will be checked at next opening.
+ if (!PlacesUtils.nodeIsFolder(placesNode) ||
+ this.controller.hasCachedLivemarkInfo(placesNode)) {
+ placesNode.containerOpen = false;
+ }
+ }
+ }
+
+ _onPopupShowing(event) {
+ // If the event came from the root element, this is the first time
+ // we ever get here.
+ if (event.originalTarget == this._rootElt) {
+ // Start listening for events from all panels inside the panelmultiview.
+ this.panelMultiView = this._viewElt.panelMultiView;
+ this._addEventListeners(this.panelMultiView, this.events);
+ }
+ super._onPopupShowing(event);
+ }
+
+ _onViewShown(event) {
+ if (event.originalTarget != this._viewElt)
+ return;
+
+ // Because PanelMultiView reparents the panelview internally, the controller
+ // may get lost. In that case we'll append it again, because we certainly
+ // need it later!
+ if (!this.controllers.getControllerCount() && this._controller)
+ this.controllers.appendController(this._controller);
+ }
+};
diff --git a/comm/suite/components/places/content/controller.js b/comm/suite/components/places/content/controller.js
new file mode 100644
index 0000000000..1bf51db463
--- /dev/null
+++ b/comm/suite/components/places/content/controller.js
@@ -0,0 +1,1442 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/**
+ * Represents an insertion point within a container where we can insert
+ * items.
+ * @param {object} an object containing the following properties:
+ * - parentId
+ * The identifier of the parent container
+ * - parentGuid
+ * The unique identifier of the parent container
+ * - index
+ * The index within the container where to insert, defaults to appending
+ * - orientation
+ * The orientation of the insertion. NOTE: the adjustments to the
+ * insertion point to accommodate the orientation should be done by
+ * the person who constructs the IP, not the user. The orientation
+ * is provided for informational purposes only! Defaults to DROP_ON.
+ * - tagName
+ * The tag name if this IP is set to a tag, null otherwise.
+ * - dropNearNode
+ * When defined index will be calculated based on this node
+ */
+function PlacesInsertionPoint({ parentId, parentGuid,
+ index = PlacesUtils.bookmarks.DEFAULT_INDEX,
+ orientation = Ci.nsITreeView.DROP_ON,
+ tagName = null,
+ dropNearNode = null }) {
+ this.itemId = parentId;
+ this.guid = parentGuid;
+ this._index = index;
+ this.orientation = orientation;
+ this.tagName = tagName;
+ this.dropNearNode = dropNearNode;
+}
+
+PlacesInsertionPoint.prototype = {
+ set index(val) {
+ return this._index = val;
+ },
+
+ async getIndex() {
+ if (this.dropNearNode) {
+ // If dropNearNode is set up we must calculate the index of the item near
+ // which we will drop.
+ let index = (await PlacesUtils.bookmarks.fetch(this.dropNearNode.bookmarkGuid)).index;
+ return this.orientation == Ci.nsITreeView.DROP_BEFORE ? index : index + 1;
+ }
+ return this._index;
+ },
+
+ get isTag() {
+ return typeof(this.tagName) == "string";
+ }
+};
+
+/**
+ * Places Controller
+ */
+
+function PlacesController(aView) {
+ this._view = aView;
+ XPCOMUtils.defineLazyServiceGetter(this, "clipboard",
+ "@mozilla.org/widget/clipboard;1",
+ "nsIClipboard");
+ XPCOMUtils.defineLazyGetter(this, "profileName", function() {
+ return Services.dirsvc.get("ProfD", Ci.nsIFile).leafName;
+ });
+
+ this._cachedLivemarkInfoObjects = new Map();
+}
+
+PlacesController.prototype = {
+ /**
+ * The places view.
+ */
+ _view: null,
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIClipboardOwner
+ ]),
+
+ // nsIClipboardOwner
+ LosingOwnership: function PC_LosingOwnership(aXferable) {
+ this.cutNodes = [];
+ },
+
+ terminate: function PC_terminate() {
+ this._releaseClipboardOwnership();
+ },
+
+ supportsCommand: function PC_supportsCommand(aCommand) {
+ // Non-Places specific commands that we also support
+ switch (aCommand) {
+ case "cmd_undo":
+ case "cmd_redo":
+ case "cmd_cut":
+ case "cmd_copy":
+ case "cmd_paste":
+ case "cmd_delete":
+ case "cmd_selectAll":
+ return true;
+ }
+
+ // All other Places Commands are prefixed with "placesCmd_" ... this
+ // filters out other commands that we do _not_ support (see 329587).
+ const CMD_PREFIX = "placesCmd_";
+ return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX);
+ },
+
+ isCommandEnabled: function PC_isCommandEnabled(aCommand) {
+ switch (aCommand) {
+ case "cmd_undo":
+ return PlacesTransactions.topUndoEntry != null;
+ case "cmd_redo":
+ return PlacesTransactions.topRedoEntry != null;
+ case "cmd_cut":
+ case "placesCmd_cut":
+ for (let node of this._view.selectedNodes) {
+ // If selection includes history nodes or tags-as-bookmark, disallow
+ // cutting.
+ if (node.itemId == -1 ||
+ (node.parent && PlacesUtils.nodeIsTagQuery(node.parent))) {
+ return false;
+ }
+ }
+ // Otherwise fall through the cmd_delete check.
+ case "cmd_delete":
+ case "placesCmd_delete":
+ case "placesCmd_deleteDataHost":
+ return this._hasRemovableSelection();
+ case "cmd_copy":
+ case "placesCmd_copy":
+ return this._view.hasSelection;
+ case "cmd_paste":
+ case "placesCmd_paste":
+ return this._canInsert(true) && this._isClipboardDataPasteable();
+ case "cmd_selectAll":
+ if (this._view.selType != "single") {
+ let rootNode = this._view.result.root;
+ if (rootNode.containerOpen && rootNode.childCount > 0)
+ return true;
+ }
+ return false;
+ case "placesCmd_open":
+ case "placesCmd_open:window":
+ case "placesCmd_open:privatewindow":
+ case "placesCmd_open:tab": {
+ let selectedNode = this._view.selectedNode;
+ return selectedNode && PlacesUtils.nodeIsURI(selectedNode);
+ }
+ case "placesCmd_new:folder":
+ return this._canInsert();
+ case "placesCmd_new:bookmark":
+ return this._canInsert();
+ case "placesCmd_new:separator":
+ return this._canInsert() &&
+ !PlacesUtils.asQuery(this._view.result.root).queryOptions.excludeItems &&
+ this._view.result.sortingMode ==
+ Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
+ case "placesCmd_show:info": {
+ let selectedNode = this._view.selectedNode;
+ return selectedNode && PlacesUtils.getConcreteItemId(selectedNode) != -1;
+ }
+ case "placesCmd_reload": {
+ // Livemark containers
+ let selectedNode = this._view.selectedNode;
+ return selectedNode && this.hasCachedLivemarkInfo(selectedNode);
+ }
+ case "placesCmd_sortBy:name": {
+ let selectedNode = this._view.selectedNode;
+ return selectedNode &&
+ PlacesUtils.nodeIsFolder(selectedNode) &&
+ !PlacesUIUtils.isFolderReadOnly(selectedNode, this._view) &&
+ this._view.result.sortingMode ==
+ Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
+ }
+ case "placesCmd_createBookmark":
+ var node = this._view.selectedNode;
+ return node && PlacesUtils.nodeIsURI(node) && node.itemId == -1;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function PC_doCommand(aCommand) {
+ switch (aCommand) {
+ case "cmd_undo":
+ PlacesTransactions.undo().catch(Cu.reportError);
+ break;
+ case "cmd_redo":
+ PlacesTransactions.redo().catch(Cu.reportError);
+ break;
+ case "cmd_cut":
+ case "placesCmd_cut":
+ this.cut();
+ break;
+ case "cmd_copy":
+ case "placesCmd_copy":
+ this.copy();
+ break;
+ case "cmd_paste":
+ case "placesCmd_paste":
+ this.paste().catch(Cu.reportError);
+ break;
+ case "cmd_delete":
+ case "placesCmd_delete":
+ this.remove("Remove Selection").catch(Cu.reportError);
+ break;
+ case "placesCmd_deleteDataHost":
+ var host;
+ if (PlacesUtils.nodeIsHost(this._view.selectedNode)) {
+ var queries = this._view.selectedNode.getQueries();
+ host = queries[0].domain;
+ } else
+ host = Services.io.newURI(this._view.selectedNode.uri).host;
+ ForgetAboutSite.removeDataFromDomain(host)
+ .catch(Cu.reportError);
+ break;
+ case "cmd_selectAll":
+ this.selectAll();
+ break;
+ case "placesCmd_open":
+ PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
+ break;
+ case "placesCmd_open:window":
+ PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view);
+ break;
+ case "placesCmd_open:privatewindow":
+ PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view, true);
+ break;
+ case "placesCmd_open:tab":
+ PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view);
+ break;
+ case "placesCmd_new:folder":
+ this.newItem("folder").catch(Cu.reportError);
+ break;
+ case "placesCmd_new:bookmark":
+ this.newItem("bookmark").catch(Cu.reportError);
+ break;
+ case "placesCmd_new:separator":
+ this.newSeparator().catch(Cu.reportError);
+ break;
+ case "placesCmd_show:info":
+ this.showBookmarkPropertiesForSelection();
+ break;
+ case "placesCmd_reload":
+ this.reloadSelectedLivemark();
+ break;
+ case "placesCmd_sortBy:name":
+ this.sortFolderByName().catch(Cu.reportError);
+ break;
+ case "placesCmd_createBookmark":
+ let node = this._view.selectedNode;
+ PlacesUIUtils.showBookmarkDialog({ action: "add",
+ type: "bookmark",
+ hiddenRows: [ "description",
+ "keyword",
+ "location",
+ "loadInSidebar" ],
+ uri: Services.io.newURI(node.uri),
+ title: node.title
+ }, window.top);
+ break;
+ }
+ },
+
+ onEvent: function PC_onEvent(eventName) { },
+
+
+ /**
+ * Determine whether or not the selection can be removed, either by the
+ * delete or cut operations based on whether or not any of its contents
+ * are non-removable. We don't need to worry about recursion here since it
+ * is a policy decision that a removable item not be placed inside a non-
+ * removable item.
+ *
+ * @return true if all nodes in the selection can be removed,
+ * false otherwise.
+ */
+ _hasRemovableSelection() {
+ var ranges = this._view.removableSelectionRanges;
+ if (!ranges.length)
+ return false;
+
+ var root = this._view.result.root;
+
+ for (var j = 0; j < ranges.length; j++) {
+ var nodes = ranges[j];
+ for (var i = 0; i < nodes.length; ++i) {
+ // Disallow removing the view's root node
+ if (nodes[i] == root)
+ return false;
+
+ if (!PlacesUIUtils.canUserRemove(nodes[i], this._view))
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Determines whether or not nodes can be inserted relative to the selection.
+ */
+ _canInsert: function PC__canInsert(isPaste) {
+ var ip = this._view.insertionPoint;
+ return ip != null && (isPaste || ip.isTag != true);
+ },
+
+ /**
+ * Looks at the data on the clipboard to see if it is paste-able.
+ * Paste-able data is:
+ * - in a format that the view can receive
+ * @return true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
+ * - clipboard data is of type TEXT_UNICODE and
+ * is a valid URI.
+ */
+ _isClipboardDataPasteable: function PC__isClipboardDataPasteable() {
+ // if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely
+ // pasteable, with no need to unwrap all the nodes.
+
+ var flavors = PlacesUIUtils.PLACES_FLAVORS;
+ var clipboard = this.clipboard;
+ var hasPlacesData =
+ clipboard.hasDataMatchingFlavors(flavors,
+ Ci.nsIClipboard.kGlobalClipboard);
+ if (hasPlacesData)
+ return this._view.insertionPoint != null;
+
+ // if the clipboard doesn't have TYPE_X_MOZ_PLACE_* data, we also allow
+ // pasting of valid "text/unicode" and "text/x-moz-url" data
+ var xferable = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ xferable.init(null);
+
+ xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_URL);
+ xferable.addDataFlavor(PlacesUtils.TYPE_UNICODE);
+ clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
+
+ try {
+ // getAnyTransferData will throw if no data is available.
+ var data = { }, type = { };
+ xferable.getAnyTransferData(type, data, { });
+ data = data.value.QueryInterface(Ci.nsISupportsString).data;
+ if (type.value != PlacesUtils.TYPE_X_MOZ_URL &&
+ type.value != PlacesUtils.TYPE_UNICODE)
+ return false;
+
+ // unwrapNodes() will throw if the data blob is malformed.
+ PlacesUtils.unwrapNodes(data, type.value);
+ return this._view.insertionPoint != null;
+ } catch (e) {
+ // getAnyTransferData or unwrapNodes failed
+ return false;
+ }
+ },
+
+ /**
+ * Gathers information about the selected nodes according to the following
+ * rules:
+ * "link" node is a URI
+ * "bookmark" node is a bookmark
+ * "livemarkChild" node is a child of a livemark
+ * "tagChild" node is a child of a tag
+ * "folder" node is a folder
+ * "query" node is a query
+ * "separator" node is a separator line
+ * "host" node is a host
+ *
+ * @return an array of objects corresponding the selected nodes. Each
+ * object has each of the properties above set if its corresponding
+ * node matches the rule. In addition, the annotations names for each
+ * node are set on its corresponding object as properties.
+ * Notes:
+ * 1) This can be slow, so don't call it anywhere performance critical!
+ */
+ _buildSelectionMetadata: function PC__buildSelectionMetadata() {
+ var metadata = [];
+ var nodes = this._view.selectedNodes;
+
+ for (var i = 0; i < nodes.length; i++) {
+ var nodeData = {};
+ var node = nodes[i];
+ var nodeType = node.type;
+ var uri = null;
+
+ // We don't use the nodeIs* methods here to avoid going through the type
+ // property way too often
+ switch (nodeType) {
+ case Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY:
+ nodeData.query = true;
+ if (node.parent) {
+ switch (PlacesUtils.asQuery(node.parent).queryOptions.resultType) {
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
+ nodeData.host = true;
+ break;
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
+ nodeData.day = true;
+ break;
+ }
+ }
+ break;
+ case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER:
+ case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT:
+ nodeData.folder = true;
+ break;
+ case Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR:
+ nodeData.separator = true;
+ break;
+ case Ci.nsINavHistoryResultNode.RESULT_TYPE_URI:
+ nodeData.link = true;
+ uri = Services.io.newURI(node.uri);
+ if (PlacesUtils.nodeIsBookmark(node)) {
+ nodeData.bookmark = true;
+ var parentNode = node.parent;
+ if (parentNode) {
+ if (PlacesUtils.nodeIsTagQuery(parentNode))
+ nodeData.tagChild = true;
+ else if (this.hasCachedLivemarkInfo(parentNode))
+ nodeData.livemarkChild = true;
+ }
+ }
+ break;
+ }
+
+ // annotations
+ if (uri) {
+ let names = PlacesUtils.annotations.getPageAnnotationNames(uri);
+ for (let j = 0; j < names.length; ++j)
+ nodeData[names[j]] = true;
+ }
+
+ // For items also include the item-specific annotations
+ if (node.itemId != -1) {
+ let names = PlacesUtils.annotations
+ .getItemAnnotationNames(node.itemId);
+ for (let j = 0; j < names.length; ++j)
+ nodeData[names[j]] = true;
+ }
+ metadata.push(nodeData);
+ }
+
+ return metadata;
+ },
+
+ /**
+ * Determines if a context-menu item should be shown
+ * @param aMenuItem
+ * the context menu item
+ * @param aMetaData
+ * meta data about the selection
+ * @return true if the conditions (see buildContextMenu) are satisfied
+ * and the item can be displayed, false otherwise.
+ */
+ _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
+ var selectiontype = aMenuItem.getAttribute("selectiontype");
+ if (!selectiontype) {
+ selectiontype = "single|multiple";
+ }
+ var selectionTypes = selectiontype.split("|");
+ if (selectionTypes.includes("any")) {
+ return true;
+ }
+ var count = aMetaData.length;
+ if (count > 1 && !selectionTypes.includes("multiple"))
+ return false;
+ if (count == 1 && !selectionTypes.includes("single"))
+ return false;
+ // NB: if there is no selection, we show the item if and only if
+ // the selectiontype includes 'none' - the metadata list will be
+ // empty so none of the other criteria will apply anyway.
+ if (count == 0)
+ return selectionTypes.includes("none");
+
+ var forceHideAttr = aMenuItem.getAttribute("forcehideselection");
+ if (forceHideAttr) {
+ var forceHideRules = forceHideAttr.split("|");
+ for (let i = 0; i < aMetaData.length; ++i) {
+ for (let j = 0; j < forceHideRules.length; ++j) {
+ if (forceHideRules[j] in aMetaData[i])
+ return false;
+ }
+ }
+ }
+
+ var selectionAttr = aMenuItem.getAttribute("selection");
+ if (!selectionAttr) {
+ return !aMenuItem.hidden;
+ }
+
+ if (selectionAttr == "any")
+ return true;
+
+ var showRules = selectionAttr.split("|");
+ var anyMatched = false;
+ function metaDataNodeMatches(metaDataNode, rules) {
+ for (var i = 0; i < rules.length; i++) {
+ if (rules[i] in metaDataNode)
+ return true;
+ }
+ return false;
+ }
+
+ for (var i = 0; i < aMetaData.length; ++i) {
+ if (metaDataNodeMatches(aMetaData[i], showRules))
+ anyMatched = true;
+ else
+ return false;
+ }
+ return anyMatched;
+ },
+
+ /**
+ * Detects information (meta-data rules) about the current selection in the
+ * view (see _buildSelectionMetadata) and sets the visibility state for each
+ * of the menu-items in the given popup with the following rules applied:
+ * 0) The "ignoreitem" attribute may be set to "true" for this code not to
+ * handle that menuitem.
+ * 1) The "selectiontype" attribute may be set on a menu-item to "single"
+ * if the menu-item should be visible only if there is a single node
+ * selected, or to "multiple" if the menu-item should be visible only if
+ * multiple nodes are selected, or to "none" if the menuitems should be
+ * visible for if there are no selected nodes, or to a |-separated
+ * combination of these.
+ * If the attribute is not set or set to an invalid value, the menu-item
+ * may be visible irrespective of the selection.
+ * 2) The "selection" attribute may be set on a menu-item to the various
+ * meta-data rules for which it may be visible. The rules should be
+ * separated with the | character.
+ * 3) A menu-item may be visible only if at least one of the rules set in
+ * its selection attribute apply to each of the selected nodes in the
+ * view.
+ * 4) The "forcehideselection" attribute may be set on a menu-item to rules
+ * for which it should be hidden. This attribute takes priority over the
+ * selection attribute. A menu-item would be hidden if at least one of the
+ * given rules apply to one of the selected nodes. The rules should be
+ * separated with the | character.
+ * 5) The "hideifnoinsertionpoint" attribute may be set on a menu-item to
+ * true if it should be hidden when there's no insertion point
+ * 6) The visibility state of a menu-item is unchanged if none of these
+ * attribute are set.
+ * 7) These attributes should not be set on separators for which the
+ * visibility state is "auto-detected."
+ * 8) The "hideifprivatebrowsing" attribute may be set on a menu-item to
+ * true if it should be hidden inside the private browsing mode
+ * @param aPopup
+ * The menupopup to build children into.
+ * @return true if at least one item is visible, false otherwise.
+ */
+ buildContextMenu: function PC_buildContextMenu(aPopup) {
+ var metadata = this._buildSelectionMetadata();
+ var ip = this._view.insertionPoint;
+ var noIp = !ip || ip.isTag;
+
+ var separator = null;
+ var visibleItemsBeforeSep = false;
+ var usableItemCount = 0;
+ for (var i = 0; i < aPopup.childNodes.length; ++i) {
+ var item = aPopup.childNodes[i];
+ if (item.getAttribute("ignoreitem") == "true") {
+ continue;
+ }
+ if (item.localName != "menuseparator") {
+ // We allow pasting into tag containers, so special case that.
+ var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" &&
+ noIp && !(ip && ip.isTag && item.id == "placesContext_paste");
+ var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" &&
+ PrivateBrowsingUtils.isWindowPrivate(window);
+ var shouldHideItem = hideIfNoIP || hideIfPrivate ||
+ !this._shouldShowMenuItem(item, metadata);
+ item.hidden = item.disabled = shouldHideItem;
+
+ if (!item.hidden) {
+ visibleItemsBeforeSep = true;
+ usableItemCount++;
+
+ // Show the separator above the menu-item if any
+ if (separator) {
+ separator.hidden = false;
+ separator = null;
+ }
+ }
+ } else { // menuseparator
+ // Initially hide it. It will be unhidden if there will be at least one
+ // visible menu-item above and below it.
+ item.hidden = true;
+
+ // We won't show the separator at all if no items are visible above it
+ if (visibleItemsBeforeSep)
+ separator = item;
+
+ // New separator, count again:
+ visibleItemsBeforeSep = false;
+ }
+ }
+
+ // Set Open Folder/Links In Tabs items enabled state if they're visible
+ if (usableItemCount > 0) {
+ var openContainerInTabsItem = document.getElementById("placesContext_openContainer:tabs");
+ if (!openContainerInTabsItem.hidden) {
+ var containerToUse = this._view.selectedNode || this._view.result.root;
+ if (PlacesUtils.nodeIsContainer(containerToUse)) {
+ if (!PlacesUtils.hasChildURIs(containerToUse)) {
+ openContainerInTabsItem.disabled = true;
+ // Ensure that we don't display the menu if nothing is enabled:
+ usableItemCount--;
+ }
+ }
+ }
+ }
+
+ // Make sure to display the correct string when multiple pages are selected.
+ let stringId = metadata.length === 1 ? "SinglePage" : "MultiplePages";
+
+ let deleteHistoryItem = document.getElementById("placesContext_delete_history");
+ deleteHistoryItem.label = PlacesUIUtils.getString(`cmd.delete${stringId}.label`);
+ deleteHistoryItem.accessKey = PlacesUIUtils.getString(`cmd.delete${stringId}.accesskey`);
+
+ let createBookmarkItem = document.getElementById("placesContext_createBookmark");
+ createBookmarkItem.label = PlacesUIUtils.getString(`cmd.bookmark${stringId}.label`);
+ createBookmarkItem.accessKey = PlacesUIUtils.getString(`cmd.bookmark${stringId}.accesskey`);
+
+ return usableItemCount > 0;
+ },
+
+ /**
+ * Select all links in the current view.
+ */
+ selectAll: function PC_selectAll() {
+ this._view.selectAll();
+ },
+
+ /**
+ * Opens the bookmark properties for the selected URI Node.
+ */
+ showBookmarkPropertiesForSelection() {
+ let node = this._view.selectedNode;
+ if (!node)
+ return;
+
+ PlacesUIUtils.showBookmarkDialog({ action: "edit",
+ node,
+ hiddenRows: [ "folderPicker" ]
+ }, window.top);
+ },
+
+ /**
+ * Reloads the selected livemark if any.
+ */
+ reloadSelectedLivemark: function PC_reloadSelectedLivemark() {
+ var selectedNode = this._view.selectedNode;
+ if (selectedNode) {
+ let itemId = selectedNode.itemId;
+ PlacesUtils.livemarks.getLivemark({ id: itemId })
+ .then(aLivemark => {
+ aLivemark.reload(true);
+ }, Cu.reportError);
+ }
+ },
+
+ /**
+ * Opens the links in the selected folder, or the selected links in new tabs.
+ */
+ openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
+ var node = this._view.selectedNode;
+ var nodes = this._view.selectedNodes;
+ // In the case of no selection, open the root node:
+ if (!node && !nodes.length) {
+ node = this._view.result.root;
+ }
+ if (node && PlacesUtils.nodeIsContainer(node))
+ PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._view);
+ else
+ PlacesUIUtils.openURINodesInTabs(nodes, aEvent, this._view);
+ },
+
+ /**
+ * Shows the Add Bookmark UI for the current insertion point.
+ *
+ * @param aType
+ * the type of the new item (bookmark/livemark/folder)
+ */
+ async newItem(aType) {
+ let ip = this._view.insertionPoint;
+ if (!ip)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ let performed =
+ PlacesUIUtils.showBookmarkDialog({ action: "add",
+ type: aType,
+ defaultInsertionPoint: ip,
+ hiddenRows: [ "folderPicker" ]
+ }, window.top);
+ if (performed) {
+ // Select the new item.
+ // TODO (Bug 1425555): When we remove places transactions, we might be
+ // able to improve showBookmarkDialog to return the guid direct, and
+ // avoid the fetch.
+ let insertedNode = await PlacesUtils.bookmarks.fetch({
+ parentGuid: ip.guid,
+ index: await ip.getIndex()
+ });
+
+ this._view.selectItems([insertedNode.guid], false);
+ }
+ },
+
+ /**
+ * Create a new Bookmark separator somewhere.
+ */
+ async newSeparator() {
+ var ip = this._view.insertionPoint;
+ if (!ip)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ let index = await ip.getIndex();
+ let txn = PlacesTransactions.NewSeparator({ parentGuid: ip.guid, index });
+ let guid = await txn.transact();
+ // Select the new item.
+ this._view.selectItems([guid], false);
+ },
+
+ /**
+ * Sort the selected folder by name
+ */
+ async sortFolderByName() {
+ let guid = PlacesUtils.getConcreteItemGuid(this._view.selectedNode);
+ await PlacesTransactions.SortByName(guid).transact();
+ },
+
+ /**
+ * Walk the list of folders we're removing in this delete operation, and
+ * see if the selected node specified is already implicitly being removed
+ * because it is a child of that folder.
+ * @param node
+ * Node to check for containment.
+ * @param pastFolders
+ * List of folders the calling function has already traversed
+ * @return true if the node should be skipped, false otherwise.
+ */
+ _shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) {
+ /**
+ * Determines if a node is contained by another node within a resultset.
+ * @param node
+ * The node to check for containment for
+ * @param parent
+ * The parent container to check for containment in
+ * @return true if node is a member of parent's children, false otherwise.
+ */
+ function isNodeContainedBy(parent) {
+ var cursor = node.parent;
+ while (cursor) {
+ if (cursor == parent)
+ return true;
+ cursor = cursor.parent;
+ }
+ return false;
+ }
+
+ for (var j = 0; j < pastFolders.length; ++j) {
+ if (isNodeContainedBy(pastFolders[j]))
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Creates a set of transactions for the removal of a range of items.
+ * A range is an array of adjacent nodes in a view.
+ * @param [in] range
+ * An array of nodes to remove. Should all be adjacent.
+ * @param [out] transactions
+ * An array of transactions.
+ * @param [optional] removedFolders
+ * An array of folder nodes that have already been removed.
+ * @return {Integer} The total number of items affected.
+ */
+ async _removeRange(range, transactions, removedFolders) {
+ if (!(transactions instanceof Array))
+ throw new Error("Must pass a transactions array");
+ if (!removedFolders)
+ removedFolders = [];
+
+ let bmGuidsToRemove = [];
+ let totalItems = 0;
+
+ for (var i = 0; i < range.length; ++i) {
+ var node = range[i];
+ if (this._shouldSkipNode(node, removedFolders))
+ continue;
+
+ totalItems++;
+
+ if (PlacesUtils.nodeIsTagQuery(node.parent)) {
+ // This is a uri node inside a tag container. It needs a special
+ // untag transaction.
+ let tag = node.parent.title;
+ if (!tag) {
+ // TODO: Bug 1432405 Try using getConcreteItemGuid.
+ let tagItemId = PlacesUtils.getConcreteItemId(node.parent);
+ let tagGuid = await PlacesUtils.promiseItemGuid(tagItemId);
+ tag = (await PlacesUtils.bookmarks.fetch(tagGuid)).title;
+ }
+ transactions.push(PlacesTransactions.Untag({ urls: [node.uri], tag }));
+ } else if (PlacesUtils.nodeIsTagQuery(node) && node.parent &&
+ PlacesUtils.nodeIsQuery(node.parent) &&
+ PlacesUtils.asQuery(node.parent).queryOptions.resultType ==
+ Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY) {
+ // This is a tag container.
+ // Untag all URIs tagged with this tag only if the tag container is
+ // child of the "Tags" query in the library, in all other places we
+ // must only remove the query node.
+ let tag = node.title;
+ let URIs = PlacesUtils.tagging.getURIsForTag(tag);
+ transactions.push(PlacesTransactions.Untag({ tag, urls: URIs }));
+ } else if (PlacesUtils.nodeIsURI(node) &&
+ PlacesUtils.nodeIsQuery(node.parent) &&
+ PlacesUtils.asQuery(node.parent).queryOptions.queryType ==
+ Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
+ // This is a uri node inside an history query.
+ PlacesUtils.history.remove(node.uri).catch(Cu.reportError);
+ // History deletes are not undoable, so we don't have a transaction.
+ } else if (node.itemId == -1 &&
+ PlacesUtils.nodeIsQuery(node) &&
+ PlacesUtils.asQuery(node).queryOptions.queryType ==
+ Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
+ // This is a dynamically generated history query, like queries
+ // grouped by site, time or both. Dynamically generated queries don't
+ // have an itemId even if they are descendants of a bookmark.
+ this._removeHistoryContainer(node);
+ // History deletes are not undoable, so we don't have a transaction.
+ } else {
+ // This is a common bookmark item.
+ if (PlacesUtils.nodeIsFolder(node)) {
+ // If this is a folder we add it to our array of folders, used
+ // to skip nodes that are children of an already removed folder.
+ removedFolders.push(node);
+ }
+ bmGuidsToRemove.push(node.bookmarkGuid);
+ }
+ }
+ if (bmGuidsToRemove.length) {
+ transactions.push(PlacesTransactions.Remove({ guids: bmGuidsToRemove }));
+ }
+ return totalItems;
+ },
+
+ async _removeRowsFromBookmarks() {
+ let ranges = this._view.removableSelectionRanges;
+ let transactions = [];
+ let removedFolders = [];
+ let totalItems = 0;
+
+ for (let range of ranges) {
+ totalItems += await this._removeRange(range, transactions, removedFolders);
+ }
+
+ if (transactions.length > 0) {
+ await PlacesUIUtils.batchUpdatesForNode(this._view.result, totalItems, async () => {
+ await PlacesTransactions.batch(transactions);
+ });
+ }
+ },
+
+ /**
+ * Removes the set of selected ranges from history, asynchronously.
+ *
+ * @note history deletes are not undoable.
+ */
+ _removeRowsFromHistory: function PC__removeRowsFromHistory() {
+ let nodes = this._view.selectedNodes;
+ let URIs = new Set();
+ for (let i = 0; i < nodes.length; ++i) {
+ let node = nodes[i];
+ if (PlacesUtils.nodeIsURI(node)) {
+ URIs.add(node.uri);
+ } else if (PlacesUtils.nodeIsQuery(node) &&
+ PlacesUtils.asQuery(node).queryOptions.queryType ==
+ Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
+ this._removeHistoryContainer(node);
+ }
+ }
+
+ PlacesUtils.history.remove([...URIs]).catch(Cu.reportError);
+ },
+
+ /**
+ * Removes history visits for an history container node.
+ * @param [in] aContainerNode
+ * The container node to remove.
+ *
+ * @note history deletes are not undoable.
+ */
+ _removeHistoryContainer: function PC__removeHistoryContainer(aContainerNode) {
+ if (PlacesUtils.nodeIsHost(aContainerNode)) {
+ // Site container.
+ PlacesUtils.history.removePagesFromHost(aContainerNode.title, true);
+ } else if (PlacesUtils.nodeIsDay(aContainerNode)) {
+ // Day container.
+ let query = aContainerNode.getQueries()[0];
+ let beginTime = query.beginTime;
+ let endTime = query.endTime;
+ if (!query || !beginTime || !endTime)
+ throw new Error("A valid date container query should exist!");
+ // We want to exclude beginTime from the removal because
+ // removePagesByTimeframe includes both extremes, while date containers
+ // exclude the lower extreme. So, if we would not exclude it, we would
+ // end up removing more history than requested.
+ PlacesUtils.history.removePagesByTimeframe(beginTime + 1, endTime);
+ }
+ },
+
+ /**
+ * Removes the selection
+ */
+ async remove() {
+ if (!this._hasRemovableSelection())
+ return;
+
+ var root = this._view.result.root;
+
+ if (PlacesUtils.nodeIsFolder(root)) {
+ await this._removeRowsFromBookmarks();
+ } else if (PlacesUtils.nodeIsQuery(root)) {
+ var queryType = PlacesUtils.asQuery(root).queryOptions.queryType;
+ if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS) {
+ await this._removeRowsFromBookmarks();
+ } else if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
+ this._removeRowsFromHistory();
+ } else {
+ throw new Error("implement support for QUERY_TYPE_UNIFIED");
+ }
+ } else
+ throw new Error("unexpected root");
+ },
+
+ /**
+ * Fills a DataTransfer object with the content of the selection that can be
+ * dropped elsewhere.
+ * @param aEvent
+ * The dragstart event.
+ */
+ setDataTransfer: function PC_setDataTransfer(aEvent) {
+ let dt = aEvent.dataTransfer;
+
+ let result = this._view.result;
+ let didSuppressNotifications = result.suppressNotifications;
+ if (!didSuppressNotifications)
+ result.suppressNotifications = true;
+
+ function addData(type, index, feedURI) {
+ let wrapNode = PlacesUtils.wrapNode(node, type, feedURI);
+ dt.mozSetDataAt(type, wrapNode, index);
+ }
+
+ function addURIData(index, feedURI) {
+ addData(PlacesUtils.TYPE_X_MOZ_URL, index, feedURI);
+ addData(PlacesUtils.TYPE_UNICODE, index, feedURI);
+ addData(PlacesUtils.TYPE_HTML, index, feedURI);
+ }
+
+ try {
+ let nodes = this._view.draggableSelection;
+ for (let i = 0; i < nodes.length; ++i) {
+ var node = nodes[i];
+
+ // This order is _important_! It controls how this and other
+ // applications select data to be inserted based on type.
+ addData(PlacesUtils.TYPE_X_MOZ_PLACE, i);
+
+ // Drop the feed uri for livemark containers
+ let livemarkInfo = this.getCachedLivemarkInfo(node);
+ if (livemarkInfo) {
+ addURIData(i, livemarkInfo.feedURI.spec);
+ } else if (node.uri) {
+ addURIData(i);
+ }
+ }
+ } finally {
+ if (!didSuppressNotifications)
+ result.suppressNotifications = false;
+ }
+ },
+
+ get clipboardAction() {
+ let action = {};
+ let actionOwner;
+ try {
+ let xferable = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ xferable.init(null);
+ xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION);
+ this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
+ xferable.getTransferData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, action, {});
+ [action, actionOwner] =
+ action.value.QueryInterface(Ci.nsISupportsString).data.split(",");
+ } catch (ex) {
+ // Paste from external sources don't have any associated action, just
+ // fallback to a copy action.
+ return "copy";
+ }
+ // For cuts also check who inited the action, since cuts across different
+ // instances should instead be handled as copies (The sources are not
+ // available for this instance).
+ if (action == "cut" && actionOwner != this.profileName)
+ action = "copy";
+
+ return action;
+ },
+
+ _releaseClipboardOwnership: function PC__releaseClipboardOwnership() {
+ if (this.cutNodes.length > 0) {
+ // This clears the logical clipboard, doesn't remove data.
+ this.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
+ }
+ },
+
+ _clearClipboard: function PC__clearClipboard() {
+ let xferable = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ xferable.init(null);
+ // Empty transferables may cause crashes, so just add an unknown type.
+ const TYPE = "text/x-moz-place-empty";
+ xferable.addDataFlavor(TYPE);
+ xferable.setTransferData(TYPE, PlacesUtils.toISupportsString(""), 0);
+ this.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
+ },
+
+ _populateClipboard: function PC__populateClipboard(aNodes, aAction) {
+ // This order is _important_! It controls how this and other applications
+ // select data to be inserted based on type.
+ let contents = [
+ { type: PlacesUtils.TYPE_X_MOZ_PLACE, entries: [] },
+ { type: PlacesUtils.TYPE_X_MOZ_URL, entries: [] },
+ { type: PlacesUtils.TYPE_HTML, entries: [] },
+ { type: PlacesUtils.TYPE_UNICODE, entries: [] },
+ ];
+
+ // Avoid handling descendants of a copied node, the transactions take care
+ // of them automatically.
+ let copiedFolders = [];
+ aNodes.forEach(function(node) {
+ if (this._shouldSkipNode(node, copiedFolders))
+ return;
+ if (PlacesUtils.nodeIsFolder(node))
+ copiedFolders.push(node);
+
+ let livemarkInfo = this.getCachedLivemarkInfo(node);
+ let feedURI = livemarkInfo && livemarkInfo.feedURI.spec;
+
+ contents.forEach(function(content) {
+ content.entries.push(
+ PlacesUtils.wrapNode(node, content.type, feedURI)
+ );
+ });
+ }, this);
+
+ function addData(type, data) {
+ xferable.addDataFlavor(type);
+ xferable.setTransferData(type, PlacesUtils.toISupportsString(data),
+ data.length * 2);
+ }
+
+ let xferable = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ xferable.init(null);
+ let hasData = false;
+ // This order matters here! It controls how this and other applications
+ // select data to be inserted based on type.
+ contents.forEach(function(content) {
+ if (content.entries.length > 0) {
+ hasData = true;
+ let glue =
+ content.type == PlacesUtils.TYPE_X_MOZ_PLACE ? "," : PlacesUtils.endl;
+ addData(content.type, content.entries.join(glue));
+ }
+ });
+
+ // Track the exected action in the xferable. This must be the last flavor
+ // since it's the least preferred one.
+ // Enqueue a unique instance identifier to distinguish operations across
+ // concurrent instances of the application.
+ addData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, aAction + "," + this.profileName);
+
+ if (hasData) {
+ this.clipboard.setData(xferable,
+ this.cutNodes.length > 0 ? this : null,
+ Ci.nsIClipboard.kGlobalClipboard);
+ }
+ },
+
+ _cutNodes: [],
+ get cutNodes() {
+ return this._cutNodes;
+ },
+ set cutNodes(aNodes) {
+ let self = this;
+ function updateCutNodes(aValue) {
+ self._cutNodes.forEach(function(aNode) {
+ self._view.toggleCutNode(aNode, aValue);
+ });
+ }
+
+ updateCutNodes(false);
+ this._cutNodes = aNodes;
+ updateCutNodes(true);
+ return aNodes;
+ },
+
+ /**
+ * Copy Bookmarks and Folders to the clipboard
+ */
+ copy: function PC_copy() {
+ let result = this._view.result;
+ let didSuppressNotifications = result.suppressNotifications;
+ if (!didSuppressNotifications)
+ result.suppressNotifications = true;
+ try {
+ this._populateClipboard(this._view.selectedNodes, "copy");
+ } finally {
+ if (!didSuppressNotifications)
+ result.suppressNotifications = false;
+ }
+ },
+
+ /**
+ * Cut Bookmarks and Folders to the clipboard
+ */
+ cut: function PC_cut() {
+ let result = this._view.result;
+ let didSuppressNotifications = result.suppressNotifications;
+ if (!didSuppressNotifications)
+ result.suppressNotifications = true;
+ try {
+ this._populateClipboard(this._view.selectedNodes, "cut");
+ this.cutNodes = this._view.selectedNodes;
+ } finally {
+ if (!didSuppressNotifications)
+ result.suppressNotifications = false;
+ }
+ },
+
+ /**
+ * Paste Bookmarks and Folders from the clipboard
+ */
+ async paste() {
+ // No reason to proceed if there isn't a valid insertion point.
+ let ip = this._view.insertionPoint;
+ if (!ip)
+ throw Cr.NS_ERROR_NOT_AVAILABLE;
+
+ let action = this.clipboardAction;
+
+ let xferable = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ xferable.init(null);
+ // This order matters here! It controls the preferred flavors for this
+ // paste operation.
+ [ PlacesUtils.TYPE_X_MOZ_PLACE,
+ PlacesUtils.TYPE_X_MOZ_URL,
+ PlacesUtils.TYPE_UNICODE,
+ ].forEach(type => xferable.addDataFlavor(type));
+
+ this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
+
+ // Now get the clipboard contents, in the best available flavor.
+ let data = {}, type = {}, items = [];
+ try {
+ xferable.getAnyTransferData(type, data, {});
+ data = data.value.QueryInterface(Ci.nsISupportsString).data;
+ type = type.value;
+ items = PlacesUtils.unwrapNodes(data, type);
+ } catch (ex) {
+ // No supported data exists or nodes unwrap failed, just bail out.
+ return;
+ }
+
+ let doCopy = action == "copy";
+ let itemsToSelect = await PlacesUIUtils.handleTransferItems(items, ip, doCopy, this._view);
+
+ // Cut/past operations are not repeatable, so clear the clipboard.
+ if (action == "cut") {
+ this._clearClipboard();
+ }
+
+ if (itemsToSelect.length > 0)
+ this._view.selectItems(itemsToSelect, false);
+ },
+
+ /**
+ * Cache the livemark info for a node. This allows the controller and the
+ * views to treat the given node as a livemark.
+ * @param aNode
+ * a places result node.
+ * @param aLivemarkInfo
+ * a mozILivemarkInfo object.
+ */
+ cacheLivemarkInfo: function PC_cacheLivemarkInfo(aNode, aLivemarkInfo) {
+ this._cachedLivemarkInfoObjects.set(aNode, aLivemarkInfo);
+ },
+
+ /**
+ * Returns whether or not there's cached mozILivemarkInfo object for a node.
+ * @param aNode
+ * a places result node.
+ * @return true if there's a cached mozILivemarkInfo object for
+ * aNode, false otherwise.
+ */
+ hasCachedLivemarkInfo: function PC_hasCachedLivemarkInfo(aNode) {
+ return this._cachedLivemarkInfoObjects.has(aNode);
+ },
+
+ /**
+ * Returns the cached livemark info for a node, if set by cacheLivemarkInfo,
+ * null otherwise.
+ * @param aNode
+ * a places result node.
+ * @return the mozILivemarkInfo object for aNode, if set, null otherwise.
+ */
+ getCachedLivemarkInfo: function PC_getCachedLivemarkInfo(aNode) {
+ return this._cachedLivemarkInfoObjects.get(aNode, null);
+ },
+
+ /**
+ * Checks if we can insert into a container.
+ * @param container
+ * The container were we are want to drop
+ */
+ disallowInsertion(container) {
+ if (!container)
+ throw new Error("empty container");
+ // Allow dropping into Tag containers and editable folders.
+ return !PlacesUtils.nodeIsTagQuery(container) &&
+ (!PlacesUtils.nodeIsFolder(container) ||
+ PlacesUIUtils.isFolderReadOnly(container, this._view));
+ },
+
+ /**
+ * Determines if a node can be moved.
+ *
+ * @param aNode
+ * A nsINavHistoryResultNode node.
+ * @return True if the node can be moved, false otherwise.
+ */
+ canMoveNode(node) {
+ // Only bookmark items are movable.
+ if (node.itemId == -1)
+ return false;
+
+ // Once tags and bookmarked are divorced, the tag-query check should be
+ // removed.
+ let parentNode = node.parent;
+ return parentNode != null &&
+ PlacesUtils.nodeIsFolder(parentNode) &&
+ !PlacesUIUtils.isFolderReadOnly(parentNode, this._view) &&
+ !PlacesUtils.nodeIsTagQuery(parentNode);
+ },
+};
+
+/**
+ * Handles drag and drop operations for views. Note that this is view agnostic!
+ * You should not use PlacesController._view within these methods, since
+ * the view that the item(s) have been dropped on was not necessarily active.
+ * Drop functions are passed the view that is being dropped on.
+ */
+var PlacesControllerDragHelper = {
+ /**
+ * DOM Element currently being dragged over
+ */
+ currentDropTarget: null,
+
+ /**
+ * Determines if the mouse is currently being dragged over a child node of
+ * this menu. This is necessary so that the menu doesn't close while the
+ * mouse is dragging over one of its submenus
+ * @param node
+ * The container node
+ * @return true if the user is dragging over a node within the hierarchy of
+ * the container, false otherwise.
+ */
+ draggingOverChildNode: function PCDH_draggingOverChildNode(node) {
+ let currentNode = this.currentDropTarget;
+ while (currentNode) {
+ if (currentNode == node)
+ return true;
+ currentNode = currentNode.parentNode;
+ }
+ return false;
+ },
+
+ /**
+ * @return The current active drag session. Returns null if there is none.
+ */
+ getSession: function PCDH__getSession() {
+ return this.dragService.getCurrentSession();
+ },
+
+ /**
+ * Extract the first accepted flavor from a list of flavors.
+ * @param aFlavors
+ * The flavors list of type DOMStringList.
+ */
+ getFirstValidFlavor: function PCDH_getFirstValidFlavor(aFlavors) {
+ for (let i = 0; i < aFlavors.length; i++) {
+ if (PlacesUIUtils.SUPPORTED_FLAVORS.includes(aFlavors[i]))
+ return aFlavors[i];
+ }
+
+ // If no supported flavor is found, check if data includes text/plain
+ // contents. If so, request them as text/unicode, a conversion will happen
+ // automatically.
+ if (aFlavors.contains("text/plain")) {
+ return PlacesUtils.TYPE_UNICODE;
+ }
+
+ return null;
+ },
+
+ /**
+ * Determines whether or not the data currently being dragged can be dropped
+ * on a places view.
+ * @param ip
+ * The insertion point where the items should be dropped.
+ */
+ canDrop: function PCDH_canDrop(ip, dt) {
+ let dropCount = dt.mozItemCount;
+
+ // Check every dragged item.
+ for (let i = 0; i < dropCount; i++) {
+ let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
+ if (!flavor)
+ return false;
+
+ // Urls can be dropped on any insertionpoint.
+ // XXXmano: remember that this method is called for each dragover event!
+ // Thus we shouldn't use unwrapNodes here at all if possible.
+ // I think it would be OK to accept bogus data here (e.g. text which was
+ // somehow wrapped as TAB_DROP_TYPE, this is not in our control, and
+ // will just case the actual drop to be a no-op), and only rule out valid
+ // expected cases, which are either unsupported flavors, or items which
+ // cannot be dropped in the current insertionpoint. The last case will
+ // likely force us to use unwrapNodes for the private data types of
+ // places.
+ if (flavor == TAB_DROP_TYPE)
+ continue;
+
+ let data = dt.mozGetDataAt(flavor, i);
+ let nodes;
+ try {
+ nodes = PlacesUtils.unwrapNodes(data, flavor);
+ } catch (e) {
+ return false;
+ }
+
+ for (let dragged of nodes) {
+ // Only bookmarks and urls can be dropped into tag containers.
+ if (ip.isTag &&
+ dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
+ (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
+ (dragged.uri && dragged.uri.startsWith("place:")) ))
+ return false;
+
+ // The following loop disallows the dropping of a folder on itself or
+ // on any of its descendants.
+ if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
+ (dragged.uri && dragged.uri.startsWith("place:")) ) {
+ let parentId = ip.itemId;
+ while (parentId != PlacesUtils.placesRootId) {
+ if (dragged.concreteId == parentId || dragged.id == parentId)
+ return false;
+ parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
+ }
+ }
+ }
+ }
+ return true;
+ },
+
+ /**
+ * Handles the drop of one or more items onto a view.
+ *
+ * @param {Object} insertionPoint The insertion point where the items should
+ * be dropped.
+ * @param {Object} dt The dataTransfer information for the drop.
+ * @param {Object} view The view or the tree element. This allows
+ * batching to take place.
+ */
+ async onDrop(insertionPoint, dt, view) {
+ let doCopy = ["copy", "link"].includes(dt.dropEffect);
+
+ let dropCount = dt.mozItemCount;
+
+ // Following flavors may contain duplicated data.
+ let duplicable = new Map();
+ duplicable.set(PlacesUtils.TYPE_UNICODE, new Set());
+ duplicable.set(PlacesUtils.TYPE_X_MOZ_URL, new Set());
+
+ // Collect all data from the DataTransfer before processing it, as the
+ // DataTransfer is only valid during the synchronous handling of the `drop`
+ // event handler callback.
+ let nodes = [];
+ for (let i = 0; i < dropCount; ++i) {
+ let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
+ if (!flavor)
+ return;
+
+ let data = dt.mozGetDataAt(flavor, i);
+ if (duplicable.has(flavor)) {
+ let handled = duplicable.get(flavor);
+ if (handled.has(data))
+ continue;
+ handled.add(data);
+ }
+
+ if (flavor != TAB_DROP_TYPE) {
+ nodes = [...nodes, ...PlacesUtils.unwrapNodes(data, flavor)];
+ } else if (data instanceof XULElement && data.localName == "tab" &&
+ data.ownerGlobal.isChromeWindow) {
+ let uri = data.linkedBrowser.currentURI;
+ let spec = uri ? uri.spec : "about:blank";
+ nodes.push({
+ uri: spec,
+ title: data.label,
+ type: PlacesUtils.TYPE_X_MOZ_URL
+ });
+ } else {
+ throw new Error("bogus data was passed as a tab");
+ }
+ }
+
+ await PlacesUIUtils.handleTransferItems(nodes, insertionPoint, doCopy, view);
+ },
+
+XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService",
+ "@mozilla.org/widget/dragservice;1",
+ "nsIDragService");
diff --git a/comm/suite/components/places/content/editBookmarkOverlay.js b/comm/suite/components/places/content/editBookmarkOverlay.js
new file mode 100644
index 0000000000..5a4f9c23c3
--- /dev/null
+++ b/comm/suite/components/places/content/editBookmarkOverlay.js
@@ -0,0 +1,1129 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed";
+const MAX_FOLDER_ITEM_IN_MENU_LIST = 5;
+
+var gEditItemOverlay = {
+ _observersAdded: false,
+ _staticFoldersListBuilt: false,
+
+ _paneInfo: null,
+ _setPaneInfo(aInitInfo) {
+ if (!aInitInfo)
+ return this._paneInfo = null;
+
+ if ("uris" in aInitInfo && "node" in aInitInfo)
+ throw new Error("ambiguous pane info");
+ if (!("uris" in aInitInfo) && !("node" in aInitInfo))
+ throw new Error("Neither node nor uris set for pane info");
+
+ // We either pass a node or uris.
+ let node = "node" in aInitInfo ? aInitInfo.node : null;
+
+ // Since there's no true UI for folder shortcuts (they show up just as their target
+ // folders), when the pane shows for them it's opened in read-only mode, showing the
+ // properties of the target folder.
+ let itemId = node ? node.itemId : -1;
+ let itemGuid = node ? PlacesUtils.getConcreteItemGuid(node) : null;
+ let isItem = itemId != -1;
+ let isFolderShortcut = isItem &&
+ node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
+ let isTag = node && PlacesUtils.nodeIsTagQuery(node);
+ if (isTag) {
+ itemId = PlacesUtils.getConcreteItemId(node);
+ // For now we don't have access to the item guid synchronously for tags,
+ // so we'll need to fetch it later.
+ }
+ let isURI = node && PlacesUtils.nodeIsURI(node);
+ let uri = isURI ? Services.io.newURI(node.uri) : null;
+ let title = node ? node.title : null;
+ let isBookmark = isItem && isURI;
+ let bulkTagging = !node;
+ let uris = bulkTagging ? aInitInfo.uris : null;
+ let visibleRows = new Set();
+ let isParentReadOnly = false;
+ let postData = aInitInfo.postData;
+ let parentId = -1;
+ let parentGuid = null;
+
+ if (node && isItem) {
+ if (!node.parent || (node.parent.itemId > 0 && !node.parent.bookmarkGuid)) {
+ throw new Error("Cannot use an incomplete node to initialize the edit bookmark panel");
+ }
+ let parent = node.parent;
+ isParentReadOnly = !PlacesUtils.nodeIsFolder(parent);
+ if (!isParentReadOnly) {
+ let folderId = PlacesUtils.getConcreteItemId(parent);
+ isParentReadOnly = folderId == PlacesUtils.placesRootId ||
+ (!("get" in Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId")) &&
+ (folderId == PlacesUIUtils.leftPaneFolderId));
+ }
+ parentId = parent.itemId;
+ parentGuid = parent.bookmarkGuid;
+ }
+
+ let focusedElement = aInitInfo.focusedElement;
+ let onPanelReady = aInitInfo.onPanelReady;
+
+ return this._paneInfo = { itemId, itemGuid, parentId, parentGuid, isItem,
+ isURI, uri, title,
+ isBookmark, isFolderShortcut, isParentReadOnly,
+ bulkTagging, uris,
+ visibleRows, postData, isTag, focusedElement,
+ onPanelReady };
+ },
+
+ get initialized() {
+ return this._paneInfo != null;
+ },
+
+ // Backwards-compatibility getters
+ get itemId() {
+ if (!this.initialized || this._paneInfo.bulkTagging)
+ return -1;
+ return this._paneInfo.itemId;
+ },
+
+ get uri() {
+ if (!this.initialized)
+ return null;
+ if (this._paneInfo.bulkTagging)
+ return this._paneInfo.uris[0];
+ return this._paneInfo.uri;
+ },
+
+ get multiEdit() {
+ return this.initialized && this._paneInfo.bulkTagging;
+ },
+
+ // Check if the pane is initialized to show only read-only fields.
+ get readOnly() {
+ // TODO (Bug 1120314): Folder shortcuts are currently read-only due to some
+ // quirky implementation details (the most important being the "smart"
+ // semantics of node.title that makes hard to edit the right entry).
+ // This pane is read-only if:
+ // * the panel is not initialized
+ // * the node is a folder shortcut
+ // * the node is not bookmarked and not a tag container
+ // * the node is child of a read-only container and is not a bookmarked
+ // URI nor a tag container
+ return !this.initialized ||
+ this._paneInfo.isFolderShortcut ||
+ (!this._paneInfo.isItem && !this._paneInfo.isTag) ||
+ (this._paneInfo.isParentReadOnly && !this._paneInfo.isBookmark && !this._paneInfo.isTag);
+ },
+
+ // the first field which was edited after this panel was initialized for
+ // a certain item
+ _firstEditedField: "",
+
+ _initNamePicker() {
+ if (this._paneInfo.bulkTagging)
+ throw new Error("_initNamePicker called unexpectedly");
+
+ // title may by null, which, for us, is the same as an empty string.
+ this._initTextField(this._namePicker, this._paneInfo.title || "");
+ },
+
+ _initLocationField() {
+ if (!this._paneInfo.isURI)
+ throw new Error("_initLocationField called unexpectedly");
+ this._initTextField(this._locationField, this._paneInfo.uri.spec);
+ },
+
+ _initDescriptionField() {
+ if (!this._paneInfo.isItem)
+ throw new Error("_initDescriptionField called unexpectedly");
+
+ this._initTextField(this._descriptionField,
+ PlacesUIUtils.getItemDescription(this._paneInfo.itemId));
+ },
+
+ async _initKeywordField(newKeyword = "") {
+ if (!this._paneInfo.isBookmark) {
+ throw new Error("_initKeywordField called unexpectedly");
+ }
+
+ // Reset the field status synchronously now, eventually we'll reinit it
+ // later if we find an existing keyword. This way we can ensure to be in a
+ // consistent status when reusing the panel across different bookmarks.
+ this._keyword = newKeyword;
+ this._initTextField(this._keywordField, newKeyword);
+
+ if (!newKeyword) {
+ let entries = [];
+ await PlacesUtils.keywords.fetch({ url: this._paneInfo.uri.spec },
+ e => entries.push(e));
+ if (entries.length > 0) {
+ // We show an existing keyword if either POST data was not provided, or
+ // if the POST data is the same.
+ let existingKeyword = entries[0].keyword;
+ let postData = this._paneInfo.postData;
+ if (postData) {
+ let sameEntry = entries.find(e => e.postData === postData);
+ existingKeyword = sameEntry ? sameEntry.keyword : "";
+ }
+ if (existingKeyword) {
+ this._keyword = existingKeyword;
+ // Update the text field to the existing keyword.
+ this._initTextField(this._keywordField, this._keyword);
+ }
+ }
+ }
+ },
+
+ _initLoadInSidebar() {
+ if (!this._paneInfo.isBookmark)
+ throw new Error("_initLoadInSidebar called unexpectedly");
+
+ this._loadInSidebarCheckbox.checked =
+ PlacesUtils.annotations.itemHasAnnotation(
+ this._paneInfo.itemId, PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
+ },
+
+ /**
+ * Initialize the panel.
+ *
+ * @param aInfo
+ * An object having:
+ * 1. one of the following properties:
+ * - node: either a result node or a node-like object representing the
+ * item to be edited. A node-like object must have the following
+ * properties (with values that match exactly those a result node
+ * would have): itemId, bookmarkGuid, uri, title, type.
+ * - uris: an array of uris for bulk tagging.
+ *
+ * 2. any of the following optional properties:
+ * - hiddenRows (Strings array): list of rows to be hidden regardless
+ * of the item edited. Possible values: "title", "location",
+ * "description", "keyword", "loadInSidebar", "feedLocation",
+ * "siteLocation", folderPicker"
+ */
+ initPanel(aInfo) {
+ if (typeof(aInfo) != "object" || aInfo === null)
+ throw new Error("aInfo must be an object.");
+ if ("node" in aInfo) {
+ try {
+ aInfo.node.type;
+ } catch (e) {
+ // If the lazy loader for |type| generates an exception, it means that
+ // this bookmark could not be loaded. This sometimes happens when tests
+ // create a bookmark by clicking the bookmark star, then try to cleanup
+ // before the bookmark panel has finished opening. Either way, if we
+ // cannot retrieve the bookmark information, we cannot open the panel.
+ return;
+ }
+ }
+
+ // For sanity ensure that the implementer has uninited the panel before
+ // trying to init it again, or we could end up leaking due to observers.
+ if (this.initialized)
+ this.uninitPanel(false);
+
+ let { parentId, isItem, isURI,
+ isBookmark, bulkTagging, uris,
+ visibleRows, focusedElement,
+ onPanelReady } = this._setPaneInfo(aInfo);
+
+ let showOrCollapse =
+ (rowId, isAppropriateForInput, nameInHiddenRows = null) => {
+ let visible = isAppropriateForInput;
+ if (visible && "hiddenRows" in aInfo && nameInHiddenRows)
+ visible &= !aInfo.hiddenRows.includes(nameInHiddenRows);
+ if (visible)
+ visibleRows.add(rowId);
+ return !(this._element(rowId).collapsed = !visible);
+ };
+
+ if (showOrCollapse("nameRow", !bulkTagging, "name")) {
+ this._initNamePicker();
+ this._namePicker.readOnly = this.readOnly;
+ }
+
+ // In some cases we want to hide the location field, since it's not
+ // human-readable, but we still want to initialize it.
+ showOrCollapse("locationRow", isURI, "location");
+ if (isURI) {
+ this._initLocationField();
+ this._locationField.readOnly = this.readOnly;
+ }
+
+ // hide the description field for
+ if (showOrCollapse("descriptionRow", isItem && !this.readOnly,
+ "description")) {
+ this._initDescriptionField();
+ this._descriptionField.readOnly = this.readOnly;
+ }
+
+ if (showOrCollapse("keywordRow", isBookmark, "keyword")) {
+ this._initKeywordField().catch(Cu.reportError);
+ this._keywordField.readOnly = this.readOnly;
+ }
+
+ // Collapse the tag selector if the item does not accept tags.
+ if (showOrCollapse("tagsRow", isURI || bulkTagging, "tags"))
+ this._initTagsField();
+ else if (!this._element("tagsSelectorRow").collapsed)
+ this.toggleTagsSelector();
+
+ // Load in sidebar.
+ if (showOrCollapse("loadInSidebarCheckbox", isBookmark, "loadInSidebar")) {
+ this._initLoadInSidebar();
+ }
+
+ // Folder picker.
+ // Technically we should check that the item is not moveable, but that's
+ // not cheap (we don't always have the parent), and there's no use case for
+ // this (it's only the Star UI that shows the folderPicker)
+ if (showOrCollapse("folderRow", isItem, "folderPicker")) {
+ this._initFolderMenuList(parentId).catch(Cu.reportError);
+ }
+
+ // Selection count.
+ if (showOrCollapse("selectionCount", bulkTagging)) {
+ this._element("itemsCountText").value =
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ uris.length,
+ [uris.length]);
+ }
+
+ // Observe changes.
+ if (!this._observersAdded) {
+ PlacesUtils.bookmarks.addObserver(this);
+ window.addEventListener("unload", this);
+ this._observersAdded = true;
+ }
+
+ let focusElement = () => {
+ // The focusedElement possible values are:
+ // * preferred: focus the field that the user touched first the last
+ // time the pane was shown (either namePicker or tagsField)
+ // * first: focus the first non collapsed textbox
+ // Note: since all controls are collapsed by default, we don't get the
+ // default XUL dialog behavior, that selects the first control, so we set
+ // the focus explicitly.
+ let elt;
+ if (focusedElement === "preferred") {
+ /* eslint-disable no-undef */
+ elt = this._element(Services.prefs.getCharPref("browser.bookmarks.editDialog.firstEditField"));
+ /* eslint-enable no-undef */
+ } else if (focusedElement === "first") {
+ elt = document.querySelector("textbox:not([collapsed=true])");
+ }
+ if (elt) {
+ elt.focus();
+ elt.select();
+ }
+ };
+
+ if (onPanelReady) {
+ onPanelReady(focusElement);
+ } else {
+ focusElement();
+ }
+ },
+
+ /**
+ * Finds tags that are in common among this._currentInfo.uris;
+ */
+ _getCommonTags() {
+ if ("_cachedCommonTags" in this._paneInfo)
+ return this._paneInfo._cachedCommonTags;
+
+ let uris = [...this._paneInfo.uris];
+ let firstURI = uris.shift();
+ let commonTags = new Set(PlacesUtils.tagging.getTagsForURI(firstURI));
+ if (commonTags.size == 0)
+ return this._cachedCommonTags = [];
+
+ for (let uri of uris) {
+ let curentURITags = PlacesUtils.tagging.getTagsForURI(uri);
+ for (let tag of commonTags) {
+ if (!curentURITags.includes(tag)) {
+ commonTags.delete(tag);
+ if (commonTags.size == 0)
+ return this._paneInfo.cachedCommonTags = [];
+ }
+ }
+ }
+ return this._paneInfo._cachedCommonTags = [...commonTags];
+ },
+
+ _initTextField(aElement, aValue) {
+ if (aElement.value != aValue) {
+ aElement.value = aValue;
+
+ // Clear the editor's undo stack, but note that editor may be null here.
+ aElement.editor?.clearUndoRedo();
+ }
+ },
+
+ /**
+ * Appends a menu-item representing a bookmarks folder to a menu-popup.
+ * @param aMenupopup
+ * The popup to which the menu-item should be added.
+ * @param aFolderId
+ * The identifier of the bookmarks folder.
+ * @param aTitle
+ * The title to use as a label.
+ * @return the new menu item.
+ */
+ _appendFolderItemToMenupopup(aMenupopup, aFolderId, aTitle) {
+ // First make sure the folders-separator is visible
+ this._element("foldersSeparator").hidden = false;
+
+ var folderMenuItem = document.createElement("menuitem");
+ var folderTitle = aTitle;
+ folderMenuItem.folderId = aFolderId;
+ folderMenuItem.setAttribute("label", folderTitle);
+ folderMenuItem.className = "menuitem-iconic folder-icon";
+ aMenupopup.appendChild(folderMenuItem);
+ return folderMenuItem;
+ },
+
+ async _initFolderMenuList(aSelectedFolder) {
+ // clean up first
+ var menupopup = this._folderMenuList.menupopup;
+ while (menupopup.childNodes.length > 6)
+ menupopup.removeChild(menupopup.lastChild);
+
+ // Build the static list
+ if (!this._staticFoldersListBuilt) {
+ let unfiledItem = this._element("unfiledRootItem");
+ unfiledItem.label = PlacesUtils.getString("OtherBookmarksFolderTitle");
+ unfiledItem.folderId = PlacesUtils.unfiledBookmarksFolderId;
+ let bmMenuItem = this._element("bmRootItem");
+ bmMenuItem.label = PlacesUtils.getString("BookmarksMenuFolderTitle");
+ bmMenuItem.folderId = PlacesUtils.bookmarksMenuFolderId;
+ let toolbarItem = this._element("toolbarFolderItem");
+ toolbarItem.label = PlacesUtils.getString("BookmarksToolbarFolderTitle");
+ toolbarItem.folderId = PlacesUtils.toolbarFolderId;
+ this._staticFoldersListBuilt = true;
+ }
+
+ // List of recently used folders:
+ var folderIds =
+ PlacesUtils.annotations.getItemsWithAnnotation(LAST_USED_ANNO);
+
+ /**
+ * The value of the LAST_USED_ANNO annotation is the time (in the form of
+ * Date.getTime) at which the folder has been last used.
+ *
+ * First we build the annotated folders array, each item has both the
+ * folder identifier and the time at which it was last-used by this dialog
+ * set. Then we sort it descendingly based on the time field.
+ */
+ this._recentFolders = [];
+ for (let folderId of folderIds) {
+ var lastUsed =
+ PlacesUtils.annotations.getItemAnnotation(folderId, LAST_USED_ANNO);
+ let guid = await PlacesUtils.promiseItemGuid(folderId);
+ let bm = await PlacesUtils.bookmarks.fetch(guid);
+ // Since this could be a root mobile folder, we should get the proper
+ // title.
+ let title = PlacesUtils.bookmarks.getLocalizedTitle(bm);
+ this._recentFolders.push({ folderId, guid, title, lastUsed });
+ }
+ this._recentFolders.sort(function(a, b) {
+ if (b.lastUsed < a.lastUsed)
+ return -1;
+ if (b.lastUsed > a.lastUsed)
+ return 1;
+ return 0;
+ });
+
+ var numberOfItems = Math.min(MAX_FOLDER_ITEM_IN_MENU_LIST,
+ this._recentFolders.length);
+ for (let i = 0; i < numberOfItems; i++) {
+ await this._appendFolderItemToMenupopup(menupopup,
+ this._recentFolders[i].folderId,
+ this._recentFolders[i].title);
+ }
+
+ let selectedFolderGuid = await PlacesUtils.promiseItemGuid(aSelectedFolder);
+ let title = (await PlacesUtils.bookmarks.fetch(selectedFolderGuid)).title;
+ var defaultItem = this._getFolderMenuItem(aSelectedFolder, title);
+ this._folderMenuList.selectedItem = defaultItem;
+
+ // Set a selectedIndex attribute to show special icons
+ this._folderMenuList.setAttribute("selectedIndex",
+ this._folderMenuList.selectedIndex);
+
+ // Hide the folders-separator if no folder is annotated as recently-used
+ this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6);
+ this._folderMenuList.disabled = this.readOnly;
+ },
+
+ QueryInterface:
+ XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
+ Ci.nsINavBookmarkObserver]),
+
+ _element(aID) {
+ return document.getElementById("editBMPanel_" + aID);
+ },
+
+ uninitPanel(aHideCollapsibleElements) {
+ if (aHideCollapsibleElements) {
+ // Hide the folder tree if it was previously visible.
+ var folderTreeRow = this._element("folderTreeRow");
+ if (!folderTreeRow.collapsed)
+ this.toggleFolderTreeVisibility();
+
+ // Hide the tag selector if it was previously visible.
+ var tagsSelectorRow = this._element("tagsSelectorRow");
+ if (!tagsSelectorRow.collapsed)
+ this.toggleTagsSelector();
+ }
+
+ if (this._observersAdded) {
+ PlacesUtils.bookmarks.removeObserver(this);
+ this._observersAdded = false;
+ }
+
+ this._setPaneInfo(null);
+ this._firstEditedField = "";
+ },
+
+ onTagsFieldChange() {
+ // Check for _paneInfo existing as the dialog may be closing but receiving
+ // async updates from unresolved promises.
+ if (this._paneInfo &&
+ (this._paneInfo.isURI || this._paneInfo.bulkTagging)) {
+ this._updateTags().then(
+ anyChanges => {
+ // Check _paneInfo here as we might be closing the dialog.
+ if (anyChanges && this._paneInfo)
+ this._mayUpdateFirstEditField("tagsField");
+ }, Cu.reportError);
+ }
+ },
+
+ /**
+ * For a given array of currently-set tags and the tags-input-field
+ * value, returns which tags should be removed and which should be added in
+ * the form of { removedTags: [...], newTags: [...] }.
+ */
+ _getTagsChanges(aCurrentTags) {
+ let inputTags = this._getTagsArrayFromTagsInputField();
+
+ // Optimize the trivial cases (which are actually the most common).
+ if (inputTags.length == 0 && aCurrentTags.length == 0)
+ return { newTags: [], removedTags: [] };
+ if (inputTags.length == 0)
+ return { newTags: [], removedTags: aCurrentTags };
+ if (aCurrentTags.length == 0)
+ return { newTags: inputTags, removedTags: [] };
+
+ // Do not remove tags that may be reinserted with a different
+ // case, since the tagging service may handle those more efficiently.
+ let lcInputTags = inputTags.map(t => t.toLowerCase());
+ let removedTags = aCurrentTags.filter(t => !lcInputTags.includes(t.toLowerCase()));
+ let newTags = inputTags.filter(t => !aCurrentTags.includes(t));
+ return { removedTags, newTags };
+ },
+
+ // Adds and removes tags for one or more uris.
+ _setTagsFromTagsInputField(aCurrentTags, aURIs) {
+ let { removedTags, newTags } = this._getTagsChanges(aCurrentTags);
+ if (removedTags.length + newTags.length == 0)
+ return false;
+
+ let setTags = async function() {
+ if (removedTags.length > 0) {
+ await PlacesTransactions.Untag({ urls: aURIs, tags: removedTags })
+ .transact();
+ }
+ if (newTags.length > 0) {
+ await PlacesTransactions.Tag({ urls: aURIs, tags: newTags })
+ .transact();
+ }
+ };
+
+ // Only in the library info-pane it's safe (and necessary) to batch these.
+ // TODO bug 1093030: cleanup this mess when the bookmarksProperties dialog
+ // and star UI code don't "run a batch in the background".
+ if (window.document.documentElement.id == "places")
+ PlacesTransactions.batch(setTags).catch(Cu.reportError);
+ else
+ setTags().catch(Cu.reportError);
+ return true;
+ },
+
+ async _updateTags() {
+ let uris = this._paneInfo.bulkTagging ?
+ this._paneInfo.uris : [this._paneInfo.uri];
+ let currentTags = this._paneInfo.bulkTagging ?
+ await this._getCommonTags() :
+ PlacesUtils.tagging.getTagsForURI(uris[0]);
+ let anyChanges = this._setTagsFromTagsInputField(currentTags, uris);
+ if (!anyChanges)
+ return false;
+
+ // The panel could have been closed in the meanwhile.
+ if (!this._paneInfo)
+ return false;
+
+ // Ensure the tagsField is in sync, clean it up from empty tags
+ currentTags = this._paneInfo.bulkTagging ?
+ this._getCommonTags() :
+ PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
+ this._initTextField(this._tagsField, currentTags.join(", "), false);
+ return true;
+ },
+
+ /**
+ * Stores the first-edit field for this dialog, if the passed-in field
+ * is indeed the first edited field
+ * @param aNewField
+ * the id of the field that may be set (without the "editBMPanel_"
+ * prefix)
+ */
+ _mayUpdateFirstEditField(aNewField) {
+ // * The first-edit-field behavior is not applied in the multi-edit case
+ // * if this._firstEditedField is already set, this is not the first field,
+ // so there's nothing to do
+ if (this._paneInfo.bulkTagging || this._firstEditedField)
+ return;
+
+ this._firstEditedField = aNewField;
+
+ // set the pref
+ Services.prefs.setCharPref("browser.bookmarks.editDialog.firstEditField", aNewField);
+ },
+
+ async onNamePickerChange() {
+ if (this.readOnly || !(this._paneInfo.isItem || this._paneInfo.isTag))
+ return;
+
+ // Here we update either the item title or its cached static title
+ let newTitle = this._namePicker.value;
+ if (!newTitle && this._paneInfo.isTag) {
+ // We don't allow setting an empty title for a tag, restore the old one.
+ this._initNamePicker();
+ } else {
+ this._mayUpdateFirstEditField("namePicker");
+
+ let guid = this._paneInfo.isTag
+ ? (await PlacesUtils.promiseItemGuid(this._paneInfo.itemId))
+ : this._paneInfo.itemGuid;
+ await PlacesTransactions.EditTitle({ guid, title: newTitle }).transact();
+ }
+ },
+
+ onDescriptionFieldChange() {
+ if (this.readOnly || !this._paneInfo.isItem)
+ return;
+
+ let description = this._element("descriptionField").value;
+ if (description != PlacesUIUtils.getItemDescription(this._paneInfo.itemId)) {
+ let annotation =
+ { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description };
+ let guid = this._paneInfo.itemGuid;
+ PlacesTransactions.Annotate({ guid, annotation })
+ .transact().catch(Cu.reportError);
+ }
+ },
+
+ onLocationFieldChange() {
+ if (this.readOnly || !this._paneInfo.isBookmark)
+ return;
+
+ let newURI;
+ try {
+ newURI = PlacesUIUtils.createFixedURI(this._locationField.value);
+ } catch (ex) {
+ // TODO: Bug 1089141 - Provide some feedback about the invalid url.
+ return;
+ }
+
+ if (this._paneInfo.uri.equals(newURI))
+ return;
+
+ let guid = this._paneInfo.itemGuid;
+ PlacesTransactions.EditUrl({ guid, url: newURI })
+ .transact().catch(Cu.reportError);
+ },
+
+ onKeywordFieldChange() {
+ if (this.readOnly || !this._paneInfo.isBookmark)
+ return;
+
+ let oldKeyword = this._keyword;
+ let keyword = this._keyword = this._keywordField.value;
+ let postData = this._paneInfo.postData;
+ let guid = this._paneInfo.itemGuid;
+ PlacesTransactions.EditKeyword({ guid, keyword, postData, oldKeyword })
+ .transact().catch(Cu.reportError);
+ },
+
+ onLoadInSidebarCheckboxCommand() {
+ if (!this.initialized || !this._paneInfo.isBookmark)
+ return;
+
+ let annotation = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO };
+ if (this._loadInSidebarCheckbox.checked)
+ annotation.value = true;
+
+ let guid = this._paneInfo.itemGuid;
+ PlacesTransactions.Annotate({ guid, annotation })
+ .transact().catch(Cu.reportError);
+ },
+
+ toggleFolderTreeVisibility() {
+ var expander = this._element("foldersExpander");
+ var folderTreeRow = this._element("folderTreeRow");
+ if (!folderTreeRow.collapsed) {
+ expander.className = "expander-down";
+ expander.setAttribute("tooltiptext",
+ expander.getAttribute("tooltiptextdown"));
+ folderTreeRow.collapsed = true;
+ this._element("chooseFolderSeparator").hidden =
+ this._element("chooseFolderMenuItem").hidden = false;
+ } else {
+ expander.className = "expander-up";
+ expander.setAttribute("tooltiptext",
+ expander.getAttribute("tooltiptextup"));
+ folderTreeRow.collapsed = false;
+
+ // XXXmano: Ideally we would only do this once, but for some odd reason,
+ // the editable mode set on this tree, together with its collapsed state
+ // breaks the view.
+ const FOLDER_TREE_PLACE_URI =
+ "place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&type=" +
+ Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY;
+ this._folderTree.place = FOLDER_TREE_PLACE_URI;
+
+ this._element("chooseFolderSeparator").hidden =
+ this._element("chooseFolderMenuItem").hidden = true;
+ this._folderTree.selectItems([this._paneInfo.parentGuid]);
+ this._folderTree.focus();
+ }
+ },
+
+ /**
+ * Get the corresponding menu-item in the folder-menu-list for a bookmarks
+ * folder if such an item exists. Otherwise, this creates a menu-item for the
+ * folder. If the items-count limit (see MAX_FOLDERS_IN_MENU_LIST) is reached,
+ * the new item replaces the last menu-item.
+ * @param aFolderId
+ * The identifier of the bookmarks folder.
+ * @param aTitle
+ * The title to use in case of menuitem creation.
+ * @return handle to the menuitem.
+ */
+ _getFolderMenuItem(aFolderId, aTitle) {
+ let menupopup = this._folderMenuList.menupopup;
+ let menuItem = Array.prototype.find.call(
+ menupopup.childNodes, item => item.folderId === aFolderId);
+ if (menuItem !== undefined)
+ return menuItem;
+
+ // 3 special folders + separator + folder-items-count limit
+ if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
+ menupopup.removeChild(menupopup.lastChild);
+
+ return this._appendFolderItemToMenupopup(menupopup, aFolderId, aTitle);
+ },
+
+ async onFolderMenuListCommand(aEvent) {
+ // Check for _paneInfo existing as the dialog may be closing but receiving
+ // async updates from unresolved promises.
+ if (!this._paneInfo) {
+ return;
+ }
+ // Set a selectedIndex attribute to show special icons
+ this._folderMenuList.setAttribute("selectedIndex",
+ this._folderMenuList.selectedIndex);
+
+ if (aEvent.target.id == "editBMPanel_chooseFolderMenuItem") {
+ // reset the selection back to where it was and expand the tree
+ // (this menu-item is hidden when the tree is already visible
+ let item = this._getFolderMenuItem(this._paneInfo.parentId,
+ this._paneInfo.title);
+ this._folderMenuList.selectedItem = item;
+ // XXXmano HACK: setTimeout 100, otherwise focus goes back to the
+ // menulist right away
+ setTimeout(() => this.toggleFolderTreeVisibility(), 100);
+ return;
+ }
+
+ // Move the item
+ let containerId = this._folderMenuList.selectedItem.folderId;
+ if (this._paneInfo.parentId != containerId &&
+ this._paneInfo.itemId != containerId) {
+ let newParentGuid = await PlacesUtils.promiseItemGuid(containerId);
+ let guid = this._paneInfo.itemGuid;
+ await PlacesTransactions.Move({ guid, newParentGuid }).transact();
+
+ // Mark the containing folder as recently-used if it isn't in the
+ // static list
+ if (containerId != PlacesUtils.unfiledBookmarksFolderId &&
+ containerId != PlacesUtils.toolbarFolderId &&
+ containerId != PlacesUtils.bookmarksMenuFolderId) {
+ this._markFolderAsRecentlyUsed(containerId)
+ .catch(Cu.reportError);
+ }
+
+ // Auto-show the bookmarks toolbar when adding / moving an item there.
+ if (containerId == PlacesUtils.toolbarFolderId) {
+ Services.obs.notifyObservers(null, "autoshow-bookmarks-toolbar");
+ }
+ }
+
+ // Update folder-tree selection
+ var folderTreeRow = this._element("folderTreeRow");
+ if (!folderTreeRow.collapsed) {
+ var selectedNode = this._folderTree.selectedNode;
+ if (!selectedNode ||
+ PlacesUtils.getConcreteItemId(selectedNode) != containerId)
+ this._folderTree.selectItems([containerId]);
+ }
+ },
+
+ onFolderTreeSelect() {
+ var selectedNode = this._folderTree.selectedNode;
+
+ // Disable the "New Folder" button if we cannot create a new folder
+ this._element("newFolderButton")
+ .disabled = !this._folderTree.insertionPoint || !selectedNode;
+
+ if (!selectedNode)
+ return;
+
+ var folderId = PlacesUtils.getConcreteItemId(selectedNode);
+ if (this._folderMenuList.selectedItem.folderId == folderId)
+ return;
+
+ var folderItem = this._getFolderMenuItem(folderId, selectedNode.title);
+ this._folderMenuList.selectedItem = folderItem;
+ folderItem.doCommand();
+ },
+
+ async _markFolderAsRecentlyUsed(aFolderId) {
+ // Expire old unused recent folders.
+ let guids = [];
+ while (this._recentFolders.length > MAX_FOLDER_ITEM_IN_MENU_LIST) {
+ let folderId = this._recentFolders.pop().folderId;
+ let guid = await PlacesUtils.promiseItemGuid(folderId);
+ guids.push(guid);
+ }
+ if (guids.length > 0) {
+ let annotation = this._getLastUsedAnnotationObject(false);
+ PlacesTransactions.Annotate({ guids, annotation })
+ .transact().catch(Cu.reportError);
+ }
+
+ // Mark folder as recently used
+ let annotation = this._getLastUsedAnnotationObject(true);
+ let guid = await PlacesUtils.promiseItemGuid(aFolderId);
+ PlacesTransactions.Annotate({ guid, annotation })
+ .transact().catch(Cu.reportError);
+ },
+
+ /**
+ * Returns an object which could then be used to set/unset the
+ * LAST_USED_ANNO annotation for a folder.
+ *
+ * @param aLastUsed
+ * Whether to set or unset the LAST_USED_ANNO annotation.
+ * @returns an object representing the annotation which could then be used
+ * with the transaction manager.
+ */
+ _getLastUsedAnnotationObject(aLastUsed) {
+ return { name: LAST_USED_ANNO,
+ value: aLastUsed ? new Date().getTime() : null };
+ },
+
+ _rebuildTagsSelectorList() {
+ let tagsSelector = this._element("tagsSelector");
+ let tagsSelectorRow = this._element("tagsSelectorRow");
+ if (tagsSelectorRow.collapsed)
+ return;
+
+ // Save the current scroll position and restore it after the rebuild.
+ let firstIndex = tagsSelector.getIndexOfFirstVisibleRow();
+ let selectedIndex = tagsSelector.selectedIndex;
+ let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label
+ : null;
+
+ while (tagsSelector.hasChildNodes()) {
+ tagsSelector.removeChild(tagsSelector.lastChild);
+ }
+
+ let tagsInField = this._getTagsArrayFromTagsInputField();
+ let allTags = PlacesUtils.tagging.allTags;
+ for (let tag of allTags) {
+ let elt = document.createElement("listitem");
+ elt.setAttribute("type", "checkbox");
+ elt.setAttribute("label", tag);
+ if (tagsInField.includes(tag))
+ elt.setAttribute("checked", "true");
+ tagsSelector.appendChild(elt);
+ if (selectedTag === tag)
+ selectedIndex = tagsSelector.getIndexOfItem(elt);
+ }
+
+ // Restore position.
+ // The listbox allows to scroll only if the required offset doesn't
+ // overflow its capacity, thus need to adjust the index for removals.
+ firstIndex =
+ Math.min(firstIndex,
+ tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows());
+ tagsSelector.scrollToIndex(firstIndex);
+ if (selectedIndex >= 0 && tagsSelector.itemCount > 0) {
+ selectedIndex = Math.min(selectedIndex, tagsSelector.itemCount - 1);
+ tagsSelector.selectedIndex = selectedIndex;
+ tagsSelector.ensureIndexIsVisible(selectedIndex);
+ }
+ },
+
+ toggleTagsSelector() {
+ var tagsSelector = this._element("tagsSelector");
+ var tagsSelectorRow = this._element("tagsSelectorRow");
+ var expander = this._element("tagsSelectorExpander");
+ if (tagsSelectorRow.collapsed) {
+ expander.className = "expander-up";
+ expander.setAttribute("tooltiptext",
+ expander.getAttribute("tooltiptextup"));
+ tagsSelectorRow.collapsed = false;
+ this._rebuildTagsSelectorList();
+
+ // This is a no-op if we've added the listener.
+ tagsSelector.addEventListener("CheckboxStateChange", this);
+ } else {
+ expander.className = "expander-down";
+ expander.setAttribute("tooltiptext",
+ expander.getAttribute("tooltiptextdown"));
+ tagsSelectorRow.collapsed = true;
+ }
+ },
+
+ /**
+ * Splits "tagsField" element value, returning an array of valid tag strings.
+ *
+ * @return Array of tag strings found in the field value.
+ */
+ _getTagsArrayFromTagsInputField() {
+ let tags = this._element("tagsField").value;
+ return tags.trim()
+ .split(/\s*,\s*/) // Split on commas and remove spaces.
+ .filter(tag => tag.length > 0); // Kill empty tags.
+ },
+
+ async newFolder() {
+ let ip = this._folderTree.insertionPoint;
+
+ // default to the bookmarks menu folder
+ if (!ip) {
+ ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.bookmarksMenuFolderId,
+ parentGuid: PlacesUtils.bookmarks.menuGuid
+ });
+ }
+
+ // XXXmano: add a separate "New Folder" string at some point...
+ let title = this._element("newFolderButton").label;
+ await PlacesTransactions.NewFolder({ parentGuid: ip.guid, title,
+ index: await ip.getIndex() })
+ .transact().catch(Cu.reportError);
+
+ this._folderTree.focus();
+ this._folderTree.selectItems([ip.itemId]);
+ PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
+ this._folderTree.selectItems([this._lastNewItem]);
+ this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
+ this._folderTree.columns.getFirstColumn());
+ },
+
+ // nsIDOMEventListener
+ handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "CheckboxStateChange":
+ // Update the tags field when items are checked/unchecked in the listbox
+ let tags = this._getTagsArrayFromTagsInputField();
+ let tagCheckbox = aEvent.target;
+
+ let curTagIndex = tags.indexOf(tagCheckbox.label);
+ let tagsSelector = this._element("tagsSelector");
+ tagsSelector.selectedItem = tagCheckbox;
+
+ if (tagCheckbox.checked) {
+ if (curTagIndex == -1)
+ tags.push(tagCheckbox.label);
+ } else if (curTagIndex != -1) {
+ tags.splice(curTagIndex, 1);
+ }
+ this._element("tagsField").value = tags.join(", ");
+ this._updateTags();
+ break;
+ case "unload":
+ this.uninitPanel(false);
+ break;
+ }
+ },
+
+ _initTagsField() {
+ let tags;
+ if (this._paneInfo.isURI)
+ tags = PlacesUtils.tagging.getTagsForURI(this._paneInfo.uri);
+ else if (this._paneInfo.bulkTagging)
+ tags = this._getCommonTags();
+ else
+ throw new Error("_promiseTagsStr called unexpectedly");
+
+ this._initTextField(this._tagsField, tags.join(", "));
+ },
+
+ async _onTagsChange(guid, changedURI = null) {
+ let paneInfo = this._paneInfo;
+ let updateTagsField = false;
+ if (paneInfo.isURI) {
+ if (paneInfo.isBookmark && guid == paneInfo.itemGuid) {
+ updateTagsField = true;
+ } else if (!paneInfo.isBookmark) {
+ if (!changedURI) {
+ let href = (await PlacesUtils.bookmarks.fetch(guid)).url.href;
+ changedURI = Services.io.newURI(href);
+ }
+ updateTagsField = changedURI.equals(paneInfo.uri);
+ }
+ } else if (paneInfo.bulkTagging) {
+ if (!changedURI) {
+ let href = (await PlacesUtils.bookmarks.fetch(guid)).url.href;
+ changedURI = Services.io.newURI(href);
+ }
+ if (paneInfo.uris.some(uri => uri.equals(changedURI))) {
+ updateTagsField = true;
+ delete this._paneInfo._cachedCommonTags;
+ }
+ } else {
+ throw new Error("_onTagsChange called unexpectedly");
+ }
+
+ if (updateTagsField) {
+ this._initTagsField();
+ // Any tags change should be reflected in the tags selector.
+ if (this._element("tagsSelector")) {
+ this._rebuildTagsSelectorList();
+ }
+ }
+ },
+
+ _onItemTitleChange(aItemId, aNewTitle) {
+ if (aItemId == this._paneInfo.itemId) {
+ this._paneInfo.title = aNewTitle;
+ this._initTextField(this._namePicker, aNewTitle);
+ } else if (this._paneInfo.visibleRows.has("folderRow")) {
+ // If the title of a folder which is listed within the folders
+ // menulist has been changed, we need to update the label of its
+ // representing element.
+ let menupopup = this._folderMenuList.menupopup;
+ for (let menuitem of menupopup.childNodes) {
+ if ("folderId" in menuitem && menuitem.folderId == aItemId) {
+ menuitem.label = aNewTitle;
+ break;
+ }
+ }
+ }
+ // We need to also update title of recent folders.
+ if (this._recentFolders) {
+ for (let folder of this._recentFolders) {
+ if (folder.folderId == aItemId) {
+ folder.title = aNewTitle;
+ break;
+ }
+ }
+ }
+ },
+
+ // nsINavBookmarkObserver
+ onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aValue,
+ aLastModified, aItemType, aParentId, aGuid) {
+ if (aProperty == "tags" && this._paneInfo.visibleRows.has("tagsRow")) {
+ this._onTagsChange(aGuid).catch(Cu.reportError);
+ return;
+ }
+ if (aProperty == "title" && (this._paneInfo.isItem || this._paneInfo.isTag)) {
+ // This also updates titles of folders in the folder menu list.
+ this._onItemTitleChange(aItemId, aValue);
+ return;
+ }
+
+ if (!this._paneInfo.isItem || this._paneInfo.itemId != aItemId) {
+ return;
+ }
+
+ switch (aProperty) {
+ case "uri":
+ let newURI = Services.io.newURI(aValue);
+ if (!newURI.equals(this._paneInfo.uri)) {
+ this._paneInfo.uri = newURI;
+ if (this._paneInfo.visibleRows.has("locationRow"))
+ this._initLocationField();
+
+ if (this._paneInfo.visibleRows.has("tagsRow")) {
+ delete this._paneInfo._cachedCommonTags;
+ this._onTagsChange(aGuid, newURI).catch(Cu.reportError);
+ }
+ }
+ break;
+ case "keyword":
+ if (this._paneInfo.visibleRows.has("keywordRow"))
+ this._initKeywordField(aValue).catch(Cu.reportError);
+ break;
+ case PlacesUIUtils.DESCRIPTION_ANNO:
+ if (this._paneInfo.visibleRows.has("descriptionRow"))
+ this._initDescriptionField();
+ break;
+ case PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO:
+ if (this._paneInfo.visibleRows.has("loadInSidebarCheckbox"))
+ this._initLoadInSidebar();
+ break;
+ }
+ },
+
+ onItemMoved(id, oldParentId, oldIndex, newParentId, newIndex, type, guid,
+ oldParentGuid, newParentGuid) {
+ if (!this._paneInfo.isItem || this._paneInfo.itemId != id) {
+ return;
+ }
+
+ this._paneInfo.parentId = newParentId;
+ this._paneInfo.parentGuid = newParentGuid;
+
+ if (!this._paneInfo.visibleRows.has("folderRow") ||
+ newParentId == this._folderMenuList.selectedItem.folderId) {
+ return;
+ }
+
+ // Just setting selectItem _does not_ trigger oncommand, so we don't
+ // recurse.
+ PlacesUtils.bookmarks.fetch(newParentGuid).then(bm => {
+ this._folderMenuList.selectedItem = this._getFolderMenuItem(newParentId,
+ bm.title);
+ });
+ },
+
+ onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI) {
+ this._lastNewItem = aItemId;
+ },
+
+ onItemRemoved() { },
+ onBeginUpdateBatch() { },
+ onEndUpdateBatch() { },
+ onItemVisited() { },
+};
+
+
+for (let elt of ["folderMenuList", "folderTree", "namePicker",
+ "locationField", "descriptionField", "keywordField",
+ "tagsField", "loadInSidebarCheckbox"]) {
+ let eltScoped = elt;
+ XPCOMUtils.defineLazyGetter(gEditItemOverlay, `_${eltScoped}`,
+ () => gEditItemOverlay._element(eltScoped));
+}
diff --git a/comm/suite/components/places/content/editBookmarkOverlay.xul b/comm/suite/components/places/content/editBookmarkOverlay.xul
new file mode 100644
index 0000000000..c4ff90a2bf
--- /dev/null
+++ b/comm/suite/components/places/content/editBookmarkOverlay.xul
@@ -0,0 +1,191 @@
+<!-- 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 % editBookmarkOverlayDTD SYSTEM "chrome://communicator/locale/places/editBookmarkOverlay.dtd">
+%editBookmarkOverlayDTD;
+]>
+
+<?xml-stylesheet href="chrome://communicator/skin/places/editBookmarkOverlay.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/bookmarks.css"?>
+
+<overlay id="editBookmarkOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <vbox id="editBookmarkPanelContent" flex="1">
+ <hbox id="editBMPanel_selectionCount" pack="center">
+ <label id="editBMPanel_itemsCountText"/>
+ </hbox>
+
+ <grid id="editBookmarkPanelGrid" flex="1">
+ <columns id="editBMPanel_columns">
+ <column id="editBMPanel_labelColumn" />
+ <column flex="1" id="editBMPanel_editColumn" />
+ </columns>
+ <rows id="editBMPanel_rows">
+ <row id="editBMPanel_nameRow"
+ align="center"
+ collapsed="true">
+ <label value="&editBookmarkOverlay.name.label;"
+ class="editBMPanel_rowLabel"
+ accesskey="&editBookmarkOverlay.name.accesskey;"
+ control="editBMPanel_namePicker"/>
+ <textbox id="editBMPanel_namePicker"
+ onchange="gEditItemOverlay.onNamePickerChange().catch(Cu.reportError);"/>
+ </row>
+
+ <row id="editBMPanel_locationRow"
+ align="center"
+ collapsed="true">
+ <label value="&editBookmarkOverlay.location.label;"
+ class="editBMPanel_rowLabel"
+ accesskey="&editBookmarkOverlay.location.accesskey;"
+ control="editBMPanel_locationField"/>
+ <textbox id="editBMPanel_locationField"
+ class="uri-element"
+ onchange="gEditItemOverlay.onLocationFieldChange();"/>
+ </row>
+
+ <row id="editBMPanel_folderRow"
+ align="center"
+ collapsed="true">
+ <label value="&editBookmarkOverlay.folder.label;"
+ class="editBMPanel_rowLabel"
+ control="editBMPanel_folderMenuList"/>
+ <hbox flex="1" align="center">
+ <menulist id="editBMPanel_folderMenuList"
+ class="folder-icon"
+ flex="1"
+ oncommand="gEditItemOverlay.onFolderMenuListCommand(event).catch(Cu.reportError);">
+ <menupopup>
+ <!-- Static item for special folders -->
+ <menuitem id="editBMPanel_toolbarFolderItem"
+ class="menuitem-iconic folder-icon"/>
+ <menuitem id="editBMPanel_bmRootItem"
+ class="menuitem-iconic folder-icon"/>
+ <menuitem id="editBMPanel_unfiledRootItem"
+ class="menuitem-iconic folder-icon"/>
+ <menuseparator id="editBMPanel_chooseFolderSeparator"/>
+ <menuitem id="editBMPanel_chooseFolderMenuItem"
+ label="&editBookmarkOverlay.choose.label;"
+ class="menuitem-iconic folder-icon"/>
+ <menuseparator id="editBMPanel_foldersSeparator" hidden="true"/>
+ </menupopup>
+ </menulist>
+ <button id="editBMPanel_foldersExpander"
+ class="expander-down"
+ tooltiptext="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
+ tooltiptextdown="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
+ tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
+ oncommand="gEditItemOverlay.toggleFolderTreeVisibility();"/>
+ </hbox>
+ </row>
+
+ <row id="editBMPanel_folderTreeRow"
+ collapsed="true"
+ flex="1">
+ <spacer/>
+ <vbox flex="1">
+ <tree id="editBMPanel_folderTree"
+ flex="1"
+ class="placesTree"
+ type="places"
+ treelines="true"
+ height="150"
+ minheight="150"
+ editable="true"
+ onselect="gEditItemOverlay.onFolderTreeSelect();"
+ hidecolumnpicker="true">
+ <treecols>
+ <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+
+ <hbox id="editBMPanel_newFolderBox">
+ <button label="&editBookmarkOverlay.newFolderButton.label;"
+ id="editBMPanel_newFolderButton"
+ accesskey="&editBookmarkOverlay.newFolderButton.accesskey;"
+ oncommand="gEditItemOverlay.newFolder().catch(Cu.reportError);"/>
+ </hbox>
+ </vbox>
+ </row>
+
+ <row id="editBMPanel_tagsRow"
+ align="center"
+ collapsed="true">
+ <label value="&editBookmarkOverlay.tags.label;"
+ class="editBMPanel_rowLabel"
+ accesskey="&editBookmarkOverlay.tags.accesskey;"
+ control="editBMPanel_tagsField"/>
+ <hbox flex="1" align="center">
+ <textbox id="editBMPanel_tagsField"
+ type="autocomplete"
+ flex="1"
+ autocompletesearch="places-tag-autocomplete"
+ autocompletepopup="PopupAutoComplete"
+ completedefaultindex="true"
+ tabscrolling="true"
+ placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"
+ onchange="gEditItemOverlay.onTagsFieldChange();"/>
+ <button id="editBMPanel_tagsSelectorExpander"
+ class="expander-down"
+ tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
+ tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
+ tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
+ oncommand="gEditItemOverlay.toggleTagsSelector();"/>
+ </hbox>
+ </row>
+
+ <row id="editBMPanel_tagsSelectorRow"
+ align="center"
+ collapsed="true">
+ <spacer/>
+ <listbox id="editBMPanel_tagsSelector"
+ height="150"/>
+ </row>
+
+ <row id="editBMPanel_keywordRow"
+ align="center"
+ collapsed="true">
+ <observes element="additionalInfoBroadcaster" attribute="hidden"/>
+ <label value="&editBookmarkOverlay.keyword.label;"
+ class="editBMPanel_rowLabel"
+ accesskey="&editBookmarkOverlay.keyword.accesskey;"
+ control="editBMPanel_keywordField"/>
+ <textbox id="editBMPanel_keywordField"
+ onchange="gEditItemOverlay.onKeywordFieldChange();"/>
+ </row>
+
+ <row id="editBMPanel_descriptionRow"
+ collapsed="true">
+ <observes element="additionalInfoBroadcaster" attribute="hidden"/>
+ <label value="&editBookmarkOverlay.description.label;"
+ class="editBMPanel_rowLabel"
+ accesskey="&editBookmarkOverlay.description.accesskey;"
+ control="editBMPanel_descriptionField"/>
+ <textbox id="editBMPanel_descriptionField"
+ multiline="true"
+ rows="4"
+ onchange="gEditItemOverlay.onDescriptionFieldChange();"/>
+ </row>
+ </rows>
+ </grid>
+
+ <checkbox id="editBMPanel_loadInSidebarCheckbox"
+ collapsed="true"
+ hidden="true"
+ disabled="true"
+ label="&editBookmarkOverlay.loadInSidebar.label;"
+ accesskey="&editBookmarkOverlay.loadInSidebar.accesskey;"
+ oncommand="gEditItemOverlay.onLoadInSidebarCheckboxCommand();">
+ <!-- Not yet supported
+ <observes element="additionalInfoBroadcaster" attribute="hidden"/> -->
+ </checkbox>
+
+ <!-- If the ids are changing or additional fields are being added, be sure
+ to sync the values in places.js -->
+ <broadcaster id="additionalInfoBroadcaster"/>
+ </vbox>
+</overlay>
diff --git a/comm/suite/components/places/content/history-panel.js b/comm/suite/components/places/content/history-panel.js
new file mode 100644
index 0000000000..6353b51c66
--- /dev/null
+++ b/comm/suite/components/places/content/history-panel.js
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var gHistoryTree;
+var gSearchBox;
+var gHistoryGrouping = "";
+var gSearching = false;
+
+function HistorySidebarInit() {
+ gHistoryTree = document.getElementById("historyTree");
+ gSearchBox = document.getElementById("search-box");
+
+ gHistoryGrouping = document.getElementById("viewButton").
+ getAttribute("selectedsort");
+
+ if (gHistoryGrouping == "site")
+ document.getElementById("bysite").setAttribute("checked", "true");
+ else if (gHistoryGrouping == "visited")
+ document.getElementById("byvisited").setAttribute("checked", "true");
+ else if (gHistoryGrouping == "lastvisited")
+ document.getElementById("bylastvisited").setAttribute("checked", "true");
+ else if (gHistoryGrouping == "dayandsite")
+ document.getElementById("bydayandsite").setAttribute("checked", "true");
+ else
+ document.getElementById("byday").setAttribute("checked", "true");
+
+ searchHistory("");
+}
+
+function GroupBy(groupingType) {
+ gHistoryGrouping = groupingType;
+ searchHistory(gSearchBox.value);
+}
+
+function searchHistory(aInput) {
+ var query = PlacesUtils.history.getNewQuery();
+ var options = PlacesUtils.history.getNewQueryOptions();
+
+ const NHQO = Ci.nsINavHistoryQueryOptions;
+ var sortingMode;
+ var resultType;
+
+ switch (gHistoryGrouping) {
+ case "visited":
+ resultType = NHQO.RESULTS_AS_URI;
+ sortingMode = NHQO.SORT_BY_VISITCOUNT_DESCENDING;
+ break;
+ case "lastvisited":
+ resultType = NHQO.RESULTS_AS_URI;
+ sortingMode = NHQO.SORT_BY_DATE_DESCENDING;
+ break;
+ case "dayandsite":
+ resultType = NHQO.RESULTS_AS_DATE_SITE_QUERY;
+ break;
+ case "site":
+ resultType = NHQO.RESULTS_AS_SITE_QUERY;
+ sortingMode = NHQO.SORT_BY_TITLE_ASCENDING;
+ break;
+ case "day":
+ default:
+ resultType = NHQO.RESULTS_AS_DATE_QUERY;
+ break;
+ }
+
+ if (aInput) {
+ query.searchTerms = aInput;
+ if (gHistoryGrouping != "visited" && gHistoryGrouping != "lastvisited") {
+ sortingMode = NHQO.SORT_BY_FRECENCY_DESCENDING;
+ resultType = NHQO.RESULTS_AS_URI;
+ }
+ }
+
+ options.sortingMode = sortingMode;
+ options.resultType = resultType;
+ options.includeHidden = !!aInput;
+
+ // call load() on the tree manually
+ // instead of setting the place attribute in history-panel.xul
+ // otherwise, we will end up calling load() twice
+ gHistoryTree.load([query], options);
+}
+
+window.addEventListener("SidebarFocused",
+ () => gSearchBox.focus());
diff --git a/comm/suite/components/places/content/history-panel.xul b/comm/suite/components/places/content/history-panel.xul
new file mode 100644
index 0000000000..cb3b77ad17
--- /dev/null
+++ b/comm/suite/components/places/content/history-panel.xul
@@ -0,0 +1,95 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sidebar/sidebarListView.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/bookmarks.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE page [
+<!ENTITY % placesDTD SYSTEM "chrome://communicator/locale/places/places.dtd">
+%placesDTD;
+<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
+%editMenuOverlayDTD;
+]>
+
+<!-- we need to keep id="history-panel" for upgrade and switching
+ between versions of the browser -->
+
+<page id="history-panel" orient="vertical"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="HistorySidebarInit();"
+ onunload="SidebarUtils.setMouseoverURL('');">
+
+ <script src="chrome://communicator/content/bookmarks/sidebarUtils.js"/>
+ <script src="chrome://communicator/content/places/history-panel.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+
+ <commandset id="placesCommands"/>
+
+#include ../../../../../toolkit/content/editMenuKeys.inc.xhtml
+#ifdef XP_MACOSX
+ <keyset id="editMenuKeysExtra">
+ <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/>
+ </keyset>
+#endif
+
+ <!-- required to overlay the context menu -->
+ <menupopup id="placesContext"/>
+
+ <!-- Bookmarks and history tooltip -->
+ <tooltip id="bhTooltip"/>
+
+ <hbox id="sidebar-search-container">
+ <textbox id="search-box" flex="1" type="search"
+ placeholder="&search.placeholder;"
+ aria-controls="historyTree"
+ oncommand="searchHistory(this.value);"/>
+ <button id="viewButton" style="min-width:0px !important;" type="menu"
+ label="&view.label;" accesskey="&view.accesskey;" selectedsort="day"
+ persist="selectedsort">
+ <menupopup>
+ <menuitem id="bydayandsite" label="&byDayAndSite.label;"
+ accesskey="&byDayAndSite.accesskey;" type="radio"
+ oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'dayandsite'); GroupBy('dayandsite');"/>
+ <menuitem id="bysite" label="&bySite.label;"
+ accesskey="&bySite.accesskey;" type="radio"
+ oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'site'); GroupBy('site');"/>
+ <menuitem id="byday" label="&byDate.label;"
+ accesskey="&byDate.accesskey;"
+ type="radio"
+ oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'day'); GroupBy('day');"/>
+ <menuitem id="byvisited" label="&byMostVisited.label;"
+ accesskey="&byMostVisited.accesskey;"
+ type="radio"
+ oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'visited'); GroupBy('visited');"/>
+ <menuitem id="bylastvisited" label="&byLastVisited.label;"
+ accesskey="&byLastVisited.accesskey;"
+ type="radio"
+ oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'lastvisited'); GroupBy('lastvisited');"/>
+ </menupopup>
+ </button>
+ </hbox>
+
+ <tree id="historyTree"
+ class="sidebar-placesTree"
+ flex="1"
+ type="places"
+ treelines="true"
+ context="placesContext"
+ hidecolumnpicker="true"
+ onkeypress="SidebarUtils.handleTreeKeyPress(event);"
+ onclick="SidebarUtils.handleTreeClick(this, event, true);"
+ onmousemove="SidebarUtils.handleTreeMouseMove(event);"
+ onmouseout="SidebarUtils.setMouseoverURL('');">
+ <treecols>
+ <treecol id="title" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ <treechildren class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
+ </tree>
+</page>
diff --git a/comm/suite/components/places/content/menu.xml b/comm/suite/components/places/content/menu.xml
new file mode 100644
index 0000000000..24af8629bb
--- /dev/null
+++ b/comm/suite/components/places/content/menu.xml
@@ -0,0 +1,624 @@
+<?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/. -->
+
+<bindings id="placesMenuBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="places-popup-base"
+ extends="chrome://global/content/bindings/popup.xml#popup">
+ <content>
+ <xul:hbox flex="1">
+ <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
+ <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
+ </xul:vbox>
+ <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
+ smoothscroll="false">
+ <children/>
+ </xul:arrowscrollbox>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+
+ <field name="AppConstants" readonly="true">
+ (ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
+ </field>
+
+ <field name="_indicatorBar">
+ document.getAnonymousElementByAttribute(this, "class",
+ "menupopup-drop-indicator-bar");
+ </field>
+
+ <field name="_scrollBox">
+ document.getAnonymousElementByAttribute(this, "class",
+ "popup-internal-box");
+ </field>
+
+ <!-- This is the view that manage the popup -->
+ <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
+
+ <!-- Check if we should hide the drop indicator for the target -->
+ <method name="_hideDropIndicator">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ let target = aEvent.target;
+
+ // Don't draw the drop indicator outside of markers or if current
+ // node is not a Places node.
+ let betweenMarkers =
+ (this._startMarker.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_FOLLOWING) &&
+ (this._endMarker.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_PRECEDING);
+
+ // Hide the dropmarker if current node is not a Places node.
+ return !(target && target._placesNode && betweenMarkers);
+ ]]></body>
+ </method>
+
+ <!-- This function returns information about where to drop when
+ dragging over this popup insertion point -->
+ <method name="_getDropPoint">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ // Can't drop if the menu isn't a folder
+ let resultNode = this._placesNode;
+
+ if (!PlacesUtils.nodeIsFolder(resultNode) ||
+ this._rootView.controller.disallowInsertion(resultNode)) {
+ return null;
+ }
+
+ var dropPoint = { ip: null, folderElt: null };
+
+ // The element we are dragging over
+ let elt = aEvent.target;
+ if (elt.localName == "menupopup")
+ elt = elt.parentNode;
+
+ // Calculate positions taking care of arrowscrollbox
+ let scrollbox = this._scrollBox;
+ let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
+ let scrollboxOffset = scrollbox.scrollBoxObject.y -
+ (scrollbox.boxObject.y - this.boxObject.y);
+ let eltY = elt.boxObject.y - scrollboxOffset;
+ let eltHeight = elt.boxObject.height;
+
+ if (!elt._placesNode) {
+ // If we are dragging over a non places node drop at the end.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode)
+ });
+ // We can set folderElt if we are dropping over a static menu that
+ // has an internal placespopup.
+ let isMenu = elt.localName == "menu" ||
+ (elt.localName == "toolbarbutton" &&
+ elt.getAttribute("type") == "menu");
+ if (isMenu && elt.lastChild &&
+ elt.lastChild.hasAttribute("placespopup"))
+ dropPoint.folderElt = elt;
+ return dropPoint;
+ }
+
+ let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
+ elt._placesNode.title : null;
+ if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
+ !PlacesUIUtils.isFolderReadOnly(elt._placesNode, this._rootView)) ||
+ PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
+ // This is a folder or a tag container.
+ if (eventY - eltY < eltHeight * 0.20) {
+ // If mouse is in the top part of the element, drop above folder.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ orientation: Ci.nsITreeView.DROP_BEFORE,
+ tagName,
+ dropNearNode: elt._placesNode
+ });
+ return dropPoint;
+ } else if (eventY - eltY < eltHeight * 0.80) {
+ // If mouse is in the middle of the element, drop inside folder.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(elt._placesNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(elt._placesNode),
+ tagName
+ });
+ dropPoint.folderElt = elt;
+ return dropPoint;
+ }
+ } else if (eventY - eltY <= eltHeight / 2) {
+ // This is a non-folder node or a readonly folder.
+ // If the mouse is above the middle, drop above this item.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ orientation: Ci.nsITreeView.DROP_BEFORE,
+ tagName,
+ dropNearNode: elt._placesNode
+ });
+ return dropPoint;
+ }
+
+ // Drop below the item.
+ dropPoint.ip = new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(resultNode),
+ parentGuid: PlacesUtils.getConcreteItemGuid(resultNode),
+ orientation: Ci.nsITreeView.DROP_AFTER,
+ tagName,
+ dropNearNode: elt._placesNode,
+ });
+ return dropPoint;
+ ]]></body>
+ </method>
+
+ <!-- Sub-menus should be opened when the mouse drags over them, and closed
+ when the mouse drags off. The overFolder object manages opening and
+ closing of folders when the mouse hovers. -->
+ <field name="_overFolder"><![CDATA[({
+ _self: this,
+ _folder: {elt: null,
+ openTimer: null,
+ hoverTime: 350,
+ closeTimer: null},
+ _closeMenuTimer: null,
+
+ get elt() {
+ return this._folder.elt;
+ },
+ set elt(val) {
+ return this._folder.elt = val;
+ },
+
+ get openTimer() {
+ return this._folder.openTimer;
+ },
+ set openTimer(val) {
+ return this._folder.openTimer = val;
+ },
+
+ get hoverTime() {
+ return this._folder.hoverTime;
+ },
+ set hoverTime(val) {
+ return this._folder.hoverTime = val;
+ },
+
+ get closeTimer() {
+ return this._folder.closeTimer;
+ },
+ set closeTimer(val) {
+ return this._folder.closeTimer = val;
+ },
+
+ get closeMenuTimer() {
+ return this._closeMenuTimer;
+ },
+ set closeMenuTimer(val) {
+ return this._closeMenuTimer = val;
+ },
+
+ setTimer: function OF__setTimer(aTime) {
+ var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
+ return timer;
+ },
+
+ notify: function OF__notify(aTimer) {
+ // Function to process all timer notifications.
+
+ if (aTimer == this._folder.openTimer) {
+ // Timer to open a submenu that's being dragged over.
+ this._folder.elt.lastChild.setAttribute("autoopened", "true");
+ this._folder.elt.lastChild.showPopup(this._folder.elt);
+ this._folder.openTimer = null;
+ } else if (aTimer == this._folder.closeTimer) {
+ // Timer to close a submenu that's been dragged off of.
+ // Only close the submenu if the mouse isn't being dragged over any
+ // of its child menus.
+ var draggingOverChild = PlacesControllerDragHelper
+ .draggingOverChildNode(this._folder.elt);
+ if (draggingOverChild)
+ this._folder.elt = null;
+ this.clear();
+
+ // Close any parent folders which aren't being dragged over.
+ // (This is necessary because of the above code that keeps a folder
+ // open while its children are being dragged over.)
+ if (!draggingOverChild)
+ this.closeParentMenus();
+ } else if (aTimer == this.closeMenuTimer) {
+ // Timer to close this menu after the drag exit.
+ var popup = this._self;
+ // if we are no more dragging we can leave the menu open to allow
+ // for better D&D bookmark organization
+ if (PlacesControllerDragHelper.getSession() &&
+ !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
+ popup.hidePopup();
+ // Close any parent menus that aren't being dragged over;
+ // otherwise they'll stay open because they couldn't close
+ // while this menu was being dragged over.
+ this.closeParentMenus();
+ }
+ this._closeMenuTimer = null;
+ }
+ },
+
+ // Helper function to close all parent menus of this menu,
+ // as long as none of the parent's children are currently being
+ // dragged over.
+ closeParentMenus: function OF__closeParentMenus() {
+ var popup = this._self;
+ var parent = popup.parentNode;
+ while (parent) {
+ if (parent.localName == "menupopup" && parent._placesNode) {
+ if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode))
+ break;
+ parent.hidePopup();
+ }
+ parent = parent.parentNode;
+ }
+ },
+
+ // The mouse is no longer dragging over the stored menubutton.
+ // Close the menubutton, clear out drag styles, and clear all
+ // timers for opening/closing it.
+ clear: function OF__clear() {
+ if (this._folder.elt && this._folder.elt.lastChild) {
+ if (!this._folder.elt.lastChild.hasAttribute("dragover"))
+ this._folder.elt.lastChild.hidePopup();
+ // remove menuactive style
+ this._folder.elt.removeAttribute("_moz-menuactive");
+ this._folder.elt = null;
+ }
+ if (this._folder.openTimer) {
+ this._folder.openTimer.cancel();
+ this._folder.openTimer = null;
+ }
+ if (this._folder.closeTimer) {
+ this._folder.closeTimer.cancel();
+ this._folder.closeTimer = null;
+ }
+ }
+ })]]></field>
+
+ <method name="_cleanupDragDetails">
+ <body><![CDATA[
+ // Called on dragend and drop.
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this._rootView._draggedElt = null;
+ this.removeAttribute("dragover");
+ this.removeAttribute("dragstart");
+ this._indicatorBar.hidden = true;
+ ]]></body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+ <handler event="DOMMenuItemActive"><![CDATA[
+ let elt = event.target;
+ if (elt.parentNode != this)
+ return;
+
+ if (this.AppConstants.platform === "macosx") {
+ // XXX: The following check is a temporary hack until bug 420033 is
+ // resolved.
+ let parentElt = elt.parent;
+ while (parentElt) {
+ if (parentElt.id == "bookmarksMenuPopup" ||
+ parentElt.id == "goPopup")
+ return;
+
+ parentElt = parentElt.parentNode;
+ }
+ }
+
+ if (window.XULBrowserWindow) {
+ let placesNode = elt._placesNode;
+
+ var linkURI;
+ if (placesNode && PlacesUtils.nodeIsURI(placesNode))
+ linkURI = placesNode.uri;
+ else if (elt.hasAttribute("targetURI"))
+ linkURI = elt.getAttribute("targetURI");
+
+ if (linkURI)
+ window.XULBrowserWindow.setOverLink(linkURI, null);
+ }
+ ]]></handler>
+
+ <handler event="DOMMenuItemInactive"><![CDATA[
+ let elt = event.target;
+ if (elt.parentNode != this)
+ return;
+
+ if (window.XULBrowserWindow)
+ window.XULBrowserWindow.setOverLink("", null);
+ ]]></handler>
+
+ <handler event="dragstart"><![CDATA[
+ let elt = event.target;
+ if (!elt._placesNode)
+ return;
+
+ let draggedElt = elt._placesNode;
+
+ // Force a copy action if parent node is a query or we are dragging a
+ // not-removable node.
+ if (!this._rootView.controller.canMoveNode(draggedElt))
+ event.dataTransfer.effectAllowed = "copyLink";
+
+ // Activate the view and cache the dragged element.
+ this._rootView._draggedElt = draggedElt;
+ this._rootView.controller.setDataTransfer(event);
+ this.setAttribute("dragstart", "true");
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="drop"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = event.target;
+
+ let dropPoint = this._getDropPoint(event);
+ if (dropPoint && dropPoint.ip) {
+ PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer)
+ .catch(Cu.reportError);
+ event.preventDefault();
+ }
+
+ this._cleanupDragDetails();
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragover"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = event.target;
+ let dt = event.dataTransfer;
+
+ let dropPoint = this._getDropPoint(event);
+ if (!dropPoint || !dropPoint.ip ||
+ !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
+ this._indicatorBar.hidden = true;
+ event.stopPropagation();
+ return;
+ }
+
+ // Mark this popup as being dragged over.
+ this.setAttribute("dragover", "true");
+
+ if (dropPoint.folderElt) {
+ // We are dragging over a folder.
+ // _overFolder should take the care of opening it on a timer.
+ if (this._overFolder.elt &&
+ this._overFolder.elt != dropPoint.folderElt) {
+ // We are dragging over a new folder, let's clear old values
+ this._overFolder.clear();
+ }
+ if (!this._overFolder.elt) {
+ this._overFolder.elt = dropPoint.folderElt;
+ // Create the timer to open this folder.
+ this._overFolder.openTimer = this._overFolder
+ .setTimer(this._overFolder.hoverTime);
+ }
+ // Since we are dropping into a folder set the corresponding style.
+ dropPoint.folderElt.setAttribute("_moz-menuactive", true);
+ } else {
+ // We are not dragging over a folder.
+ // Clear out old _overFolder information.
+ this._overFolder.clear();
+ }
+
+ // Autoscroll the popup strip if we drag over the scroll buttons.
+ let anonid = event.originalTarget.getAttribute("anonid");
+ let scrollDir = 0;
+ if (anonid == "scrollbutton-up") {
+ scrollDir = -1;
+ } else if (anonid == "scrollbutton-down") {
+ scrollDir = 1;
+ }
+ if (scrollDir != 0) {
+ this._scrollBox.scrollByIndex(scrollDir, true);
+ }
+
+ // Check if we should hide the drop indicator for this target.
+ if (dropPoint.folderElt || this._hideDropIndicator(event)) {
+ this._indicatorBar.hidden = true;
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ // We should display the drop indicator relative to the arrowscrollbox.
+ let sbo = this._scrollBox.scrollBoxObject;
+ let newMarginTop = 0;
+ if (scrollDir == 0) {
+ let elt = this.firstChild;
+ while (elt && event.screenY > elt.boxObject.screenY +
+ elt.boxObject.height / 2)
+ elt = elt.nextSibling;
+ newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
+ sbo.height;
+ } else if (scrollDir == 1)
+ newMarginTop = sbo.height;
+
+ // Set the new marginTop based on arrowscrollbox.
+ newMarginTop += sbo.y - this._scrollBox.boxObject.y;
+ this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
+ this._indicatorBar.hidden = false;
+
+ event.preventDefault();
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragexit"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = null;
+ this.removeAttribute("dragover");
+
+ // If we have not moved to a valid new target clear the drop indicator
+ // this happens when moving out of the popup.
+ let target = event.relatedTarget;
+ if (!target || !this.contains(target))
+ this._indicatorBar.hidden = true;
+
+ // Close any folder being hovered over
+ if (this._overFolder.elt) {
+ this._overFolder.closeTimer = this._overFolder
+ .setTimer(this._overFolder.hoverTime);
+ }
+
+ // The autoopened attribute is set when this folder was automatically
+ // opened after the user dragged over it. If this attribute is set,
+ // auto-close the folder on drag exit.
+ // We should also try to close this popup if the drag has started
+ // from here, the timer will check if we are dragging over a child.
+ if (this.hasAttribute("autoopened") ||
+ this.hasAttribute("dragstart")) {
+ this._overFolder.closeMenuTimer = this._overFolder
+ .setTimer(this._overFolder.hoverTime);
+ }
+
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragend"><![CDATA[
+ this._cleanupDragDetails();
+ ]]></handler>
+
+ </handlers>
+ </binding>
+
+ <!-- Most of this is copied from the arrowpanel binding in popup.xml -->
+ <binding id="places-popup-arrow"
+ extends="chrome://communicator/content/places/menu.xml#places-popup-base">
+ <content flip="both" side="top" position="bottomcenter topright">
+ <xul:vbox anonid="container" class="panel-arrowcontainer" flex="1"
+ xbl:inherits="side,panelopen">
+ <xul:box anonid="arrowbox" class="panel-arrowbox">
+ <xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
+ </xul:box>
+ <xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
+ <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
+ <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
+ </xul:vbox>
+ <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
+ smoothscroll="false">
+ <children/>
+ </xul:arrowscrollbox>
+ </xul:box>
+ </xul:vbox>
+ </content>
+
+ <implementation>
+ <constructor><![CDATA[
+ this.style.pointerEvents = "none";
+ ]]></constructor>
+ <method name="adjustArrowPosition">
+ <body><![CDATA[
+ var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
+
+ var anchor = this.anchorNode;
+ if (!anchor) {
+ arrow.hidden = true;
+ return;
+ }
+
+ var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
+ var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
+
+ var position = this.alignmentPosition;
+ var offset = this.alignmentOffset;
+
+ this.setAttribute("arrowposition", position);
+
+ // if this panel has a "sliding" arrow, we may have previously set margins...
+ arrowbox.style.removeProperty("transform");
+ if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
+ container.orient = "horizontal";
+ arrowbox.orient = "vertical";
+ if (position.indexOf("_after") > 0) {
+ arrowbox.pack = "end";
+ } else {
+ arrowbox.pack = "start";
+ }
+ arrowbox.style.transform = "translate(0, " + -offset + "px)";
+
+ // The assigned side stays the same regardless of direction.
+ var isRTL = (window.getComputedStyle(this).direction == "rtl");
+
+ if (position.indexOf("start_") == 0) {
+ container.dir = "reverse";
+ this.setAttribute("side", isRTL ? "left" : "right");
+ } else {
+ container.dir = "";
+ this.setAttribute("side", isRTL ? "right" : "left");
+ }
+ } else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
+ container.orient = "";
+ arrowbox.orient = "";
+ if (position.indexOf("_end") > 0) {
+ arrowbox.pack = "end";
+ } else {
+ arrowbox.pack = "start";
+ }
+ arrowbox.style.transform = "translate(" + -offset + "px, 0)";
+
+ if (position.indexOf("before_") == 0) {
+ container.dir = "reverse";
+ this.setAttribute("side", "bottom");
+ } else {
+ container.dir = "";
+ this.setAttribute("side", "top");
+ }
+ }
+
+ arrow.hidden = false;
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popupshowing" phase="target"><![CDATA[
+ this.adjustArrowPosition();
+ this.setAttribute("animate", "open");
+ ]]></handler>
+ <handler event="popupshown" phase="target"><![CDATA[
+ this.setAttribute("panelopen", "true");
+ let disablePointerEvents;
+ if (!this.hasAttribute("disablepointereventsfortransition")) {
+ let container = document.getAnonymousElementByAttribute(this, "anonid", "container");
+ let cs = getComputedStyle(container);
+ let transitionProp = cs.transitionProperty;
+ let transitionTime = parseFloat(cs.transitionDuration);
+ disablePointerEvents = (transitionProp.includes("transform") ||
+ transitionProp == "all") &&
+ transitionTime > 0;
+ this.setAttribute("disablepointereventsfortransition", disablePointerEvents);
+ } else {
+ disablePointerEvents = this.getAttribute("disablepointereventsfortransition") == "true";
+ }
+ if (!disablePointerEvents) {
+ this.style.removeProperty("pointer-events");
+ }
+ ]]></handler>
+ <handler event="transitionend"><![CDATA[
+ if (event.originalTarget.getAttribute("anonid") == "container" &&
+ (event.propertyName == "transform" || event.propertyName == "-moz-window-transform")) {
+ this.style.removeProperty("pointer-events");
+ }
+ ]]></handler>
+ <handler event="popuphiding" phase="target"><![CDATA[
+ this.setAttribute("animate", "cancel");
+ ]]></handler>
+ <handler event="popuphidden" phase="target"><![CDATA[
+ this.removeAttribute("panelopen");
+ if (this.getAttribute("disablepointereventsfortransition") == "true") {
+ this.style.pointerEvents = "none";
+ }
+ this.removeAttribute("animate");
+ ]]></handler>
+ </handlers>
+ </binding>
+</bindings>
diff --git a/comm/suite/components/places/content/organizer.css b/comm/suite/components/places/content/organizer.css
new file mode 100644
index 0000000000..47b1832c16
--- /dev/null
+++ b/comm/suite/components/places/content/organizer.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+#searchFilter {
+ width: 23em;
+}
diff --git a/comm/suite/components/places/content/places.css b/comm/suite/components/places/content/places.css
new file mode 100644
index 0000000000..598e60b256
--- /dev/null
+++ b/comm/suite/components/places/content/places.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+tree[type="places"] {
+ -moz-binding: url("chrome://communicator/content/places/tree.xml#places-tree");
+}
+
+tree[type="places"] > treechildren::-moz-tree-cell {
+ /* ensure we use the direction of the website title / url instead of the
+ * browser locale */
+ unicode-bidi: plaintext;
+}
+
+#bhtTitleText {
+ /* ensure we use the direction of the website title instead of the
+ * browser locale */
+ unicode-bidi: plaintext;
+}
+
+.toolbar-drop-indicator {
+ position: relative;
+ z-index: 1;
+}
+
+menupopup[placespopup="true"] {
+ -moz-binding: url("chrome://communicator/content/places/menu.xml#places-popup-base");
+}
+
+/* Apply crisp rendering for favicons at exactly 2dppx resolution */
+@media (resolution: 2dppx) {
+ #bookmarksChildren,
+ .sidebar-placesTreechildren,
+ .placesTree > treechildren {
+ image-rendering: -moz-crisp-edges;
+ }
+}
diff --git a/comm/suite/components/places/content/places.js b/comm/suite/components/places/content/places.js
new file mode 100644
index 0000000000..f793bf4184
--- /dev/null
+++ b/comm/suite/components/places/content/places.js
@@ -0,0 +1,1366 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from editBookmarkOverlay.js */
+/* import-globals-from ../../../../../toolkit/content/contentAreaUtils.js */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+ChromeUtils.defineModuleGetter(this, "MigrationUtils",
+ "resource:///modules/MigrationUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "BookmarkJSONUtils",
+ "resource://gre/modules/BookmarkJSONUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesBackups",
+ "resource://gre/modules/PlacesBackups.jsm");
+ChromeUtils.defineModuleGetter(this, "DownloadUtils",
+ "resource://gre/modules/DownloadUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+
+const RESTORE_FILEPICKER_FILTER_EXT = "*.json;*.jsonlz4";
+
+var PlacesOrganizer = {
+ _places: null,
+
+ // IDs of fields from editBookmarkOverlay that should be hidden when infoBox
+ // is minimal. IDs should be kept in sync with the IDs of the elements
+ // observing additionalInfoBroadcaster.
+ _additionalInfoFields: [
+ "editBMPanel_descriptionRow",
+ "editBMPanel_loadInSidebarCheckbox",
+ "editBMPanel_keywordRow",
+ ],
+
+ _initFolderTree() {
+ var leftPaneRoot = PlacesUIUtils.leftPaneFolderId;
+ this._places.place = "place:excludeItems=1&expandQueries=0&folder=" + leftPaneRoot;
+ },
+
+ /**
+ * Selects a left pane built-in item.
+ *
+ * @param {String} item The built-in item to select, may be one of (case sensitive):
+ * AllBookmarks, BookmarksMenu, BookmarksToolbar,
+ * History, Tags, UnfiledBookmarks.
+ */
+ selectLeftPaneBuiltIn(item) {
+ switch (item) {
+ case "AllBookmarks":
+ case "History":
+ case "Tags": {
+ var itemId = PlacesUIUtils.leftPaneQueries[item];
+ this._places.selectItems([itemId]);
+ // Forcefully expand all-bookmarks
+ if (item == "AllBookmarks" || item == "History")
+ PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
+ break;
+ }
+ case "BookmarksMenu":
+ this.selectLeftPaneContainerByHierarchy([
+ PlacesUIUtils.leftPaneQueries.AllBookmarks,
+ PlacesUtils.bookmarks.virtualMenuGuid
+ ]);
+ break;
+ case "BookmarksToolbar":
+ this.selectLeftPaneContainerByHierarchy([
+ PlacesUIUtils.leftPaneQueries.AllBookmarks,
+ PlacesUtils.bookmarks.virtualToolbarGuid
+ ]);
+ break;
+ case "UnfiledBookmarks":
+ this.selectLeftPaneContainerByHierarchy([
+ PlacesUIUtils.leftPaneQueries.AllBookmarks,
+ PlacesUtils.bookmarks.virtualUnfiledGuid
+ ]);
+ break;
+ default:
+ throw new Error(`Unrecognized item ${item} passed to selectLeftPaneRootItem`);
+ }
+ },
+
+ /**
+ * Opens a given hierarchy in the left pane, stopping at the last reachable
+ * container. Note: item ids should be considered deprecated.
+ *
+ * @param aHierarchy A single container or an array of containers, sorted from
+ * the outmost to the innermost in the hierarchy. Each
+ * container may be either an item id, a Places URI string,
+ * or a named query, like:
+ * "BookmarksMenu", "BookmarksToolbar", "UnfiledBookmarks", "AllBookmarks".
+ * @see PlacesUIUtils.leftPaneQueries for supported named queries.
+ */
+ selectLeftPaneContainerByHierarchy(aHierarchy) {
+ if (!aHierarchy)
+ throw new Error("Containers hierarchy not specified");
+ let hierarchy = [].concat(aHierarchy);
+ let selectWasSuppressed = this._places.view.selection.selectEventsSuppressed;
+ if (!selectWasSuppressed)
+ this._places.view.selection.selectEventsSuppressed = true;
+ try {
+ for (let container of hierarchy) {
+ switch (typeof container) {
+ case "number":
+ this._places.selectItems([container], false);
+ break;
+ case "string":
+ try {
+ this.selectLeftPaneBuiltIn(container);
+ } catch (ex) {
+ if (container.substr(0, 6) == "place:") {
+ this._places.selectPlaceURI(container);
+ } else {
+ // May be a guid.
+ this._places.selectItems([container], false);
+ }
+ }
+ break;
+ default:
+ throw new Error("Invalid container type found: " + container);
+ }
+ PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
+ }
+ } finally {
+ if (!selectWasSuppressed)
+ this._places.view.selection.selectEventsSuppressed = false;
+ }
+ },
+
+ init: function PO_init() {
+ ContentArea.init();
+
+ this._places = document.getElementById("placesList");
+ this._initFolderTree();
+
+ var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks
+ if (window.arguments && window.arguments[0])
+ leftPaneSelection = window.arguments[0];
+
+ this.selectLeftPaneContainerByHierarchy(leftPaneSelection);
+ if (leftPaneSelection === "History") {
+ let historyNode = this._places.selectedNode;
+ if (historyNode.childCount > 0)
+ this._places.selectNode(historyNode.getChild(0));
+ }
+
+ // clear the back-stack
+ this._backHistory.splice(0, this._backHistory.length);
+ document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
+
+ // Set up the search UI.
+ PlacesSearchBox.init();
+
+ window.addEventListener("AppCommand", this, true);
+
+ // remove the "Properties" context-menu item, we've our own details pane
+ document.getElementById("placesContext")
+ .removeChild(document.getElementById("placesContext_show:info"));
+
+ ContentArea.focus();
+ },
+
+ QueryInterface: function PO_QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIDOMEventListener) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ handleEvent: function PO_handleEvent(aEvent) {
+ if (aEvent.type != "AppCommand")
+ return;
+
+ aEvent.stopPropagation();
+ switch (aEvent.command) {
+ case "Back":
+ if (this._backHistory.length > 0)
+ this.back();
+ break;
+ case "Forward":
+ if (this._forwardHistory.length > 0)
+ this.forward();
+ break;
+ case "Search":
+ PlacesSearchBox.findAll();
+ break;
+ }
+ },
+
+ destroy: function PO_destroy() {
+ },
+
+ _location: null,
+ get location() {
+ return this._location;
+ },
+
+ set location(aLocation) {
+ if (!aLocation || this._location == aLocation)
+ return aLocation;
+
+ if (this.location) {
+ this._backHistory.unshift(this.location);
+ this._forwardHistory.splice(0, this._forwardHistory.length);
+ }
+
+ this._location = aLocation;
+ this._places.selectPlaceURI(aLocation);
+
+ if (!this._places.hasSelection) {
+ // If no node was found for the given place: uri, just load it directly
+ ContentArea.currentPlace = aLocation;
+ }
+ this.updateDetailsPane();
+
+ // update navigation commands
+ if (this._backHistory.length == 0)
+ document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
+ else
+ document.getElementById("OrganizerCommand:Back").removeAttribute("disabled");
+ if (this._forwardHistory.length == 0)
+ document.getElementById("OrganizerCommand:Forward").setAttribute("disabled", true);
+ else
+ document.getElementById("OrganizerCommand:Forward").removeAttribute("disabled");
+
+ return aLocation;
+ },
+
+ _backHistory: [],
+ _forwardHistory: [],
+
+ back: function PO_back() {
+ this._forwardHistory.unshift(this.location);
+ var historyEntry = this._backHistory.shift();
+ this._location = null;
+ this.location = historyEntry;
+ },
+ forward: function PO_forward() {
+ this._backHistory.unshift(this.location);
+ var historyEntry = this._forwardHistory.shift();
+ this._location = null;
+ this.location = historyEntry;
+ },
+
+ /**
+ * Called when a place folder is selected in the left pane.
+ * @param resetSearchBox
+ * true if the search box should also be reset, false otherwise.
+ * The search box should be reset when a new folder in the left
+ * pane is selected; the search scope and text need to be cleared in
+ * preparation for the new folder. Note that if the user manually
+ * resets the search box, either by clicking its reset button or by
+ * deleting its text, this will be false.
+ */
+ _cachedLeftPaneSelectedURI: null,
+ onPlaceSelected: function PO_onPlaceSelected(resetSearchBox) {
+ // Don't change the right-hand pane contents when there's no selection.
+ if (!this._places.hasSelection)
+ return;
+
+ let node = this._places.selectedNode;
+ let placeURI = node.uri;
+
+ // If either the place of the content tree in the right pane has changed or
+ // the user cleared the search box, update the place, hide the search UI,
+ // and update the back/forward buttons by setting location.
+ if (ContentArea.currentPlace != placeURI || !resetSearchBox) {
+ ContentArea.currentPlace = placeURI;
+ this.location = placeURI;
+ }
+
+ // When we invalidate a container we use suppressSelectionEvent, when it is
+ // unset a select event is fired, in many cases the selection did not really
+ // change, so we should check for it, and return early in such a case. Note
+ // that we cannot return any earlier than this point, because when
+ // !resetSearchBox, we need to update location and hide the UI as above,
+ // even though the selection has not changed.
+ if (placeURI == this._cachedLeftPaneSelectedURI)
+ return;
+ this._cachedLeftPaneSelectedURI = placeURI;
+
+ // At this point, resetSearchBox is true, because the left pane selection
+ // has changed; otherwise we would have returned earlier.
+
+ PlacesSearchBox.searchFilter.reset();
+ this._setSearchScopeForNode(node);
+ this.updateDetailsPane();
+ },
+
+ /**
+ * Sets the search scope based on aNode's properties.
+ * @param aNode
+ * the node to set up scope from
+ */
+ _setSearchScopeForNode: function PO__setScopeForNode(aNode) {
+ let itemId = aNode.itemId;
+
+ if (PlacesUtils.nodeIsHistoryContainer(aNode) ||
+ itemId == PlacesUIUtils.leftPaneQueries.History) {
+ PlacesQueryBuilder.setScope("history");
+ } else {
+ // Default to All Bookmarks for all other nodes, per bug 469437.
+ PlacesQueryBuilder.setScope("bookmarks");
+ }
+ },
+
+ /**
+ * Handle clicks on the places list.
+ * Single Left click, right click or modified click do not result in any
+ * special action, since they're related to selection.
+ * @param aEvent
+ * The mouse event.
+ */
+ onPlacesListClick: function PO_onPlacesListClick(aEvent) {
+ // Only handle clicks on tree children.
+ if (aEvent.target.localName != "treechildren")
+ return;
+
+ let node = this._places.selectedNode;
+ if (node) {
+ let middleClick = aEvent.button == 1 && aEvent.detail == 1;
+ if (middleClick && PlacesUtils.nodeIsContainer(node)) {
+ // The command execution function will take care of seeing if the
+ // selection is a folder or a different container type, and will
+ // load its contents in tabs.
+ PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._places);
+ }
+ }
+ },
+
+ /**
+ * Handle focus changes on the places list and the current content view.
+ */
+ updateDetailsPane: function PO_updateDetailsPane() {
+ if (!ContentArea.currentViewOptions.showDetailsPane)
+ return;
+ let view = PlacesUIUtils.getViewForNode(document.activeElement);
+ if (view) {
+ let selectedNodes = view.selectedNode ?
+ [view.selectedNode] : view.selectedNodes;
+ this._fillDetailsPane(selectedNodes);
+ }
+ },
+
+ openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) {
+ if (aContainer.itemId != -1) {
+ PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
+ this._places.selectItems([aContainer.itemId], false);
+ } else if (PlacesUtils.nodeIsQuery(aContainer)) {
+ this._places.selectPlaceURI(aContainer.uri);
+ }
+ },
+
+ /**
+ * Returns the options associated with the query currently loaded in the
+ * main places pane.
+ */
+ getCurrentOptions: function PO_getCurrentOptions() {
+ return PlacesUtils.asQuery(ContentArea.currentView.result.root).queryOptions;
+ },
+
+ /**
+ * Returns the queries associated with the query currently loaded in the
+ * main places pane.
+ */
+ getCurrentQueries: function PO_getCurrentQueries() {
+ return PlacesUtils.asQuery(ContentArea.currentView.result.root).getQueries();
+ },
+
+ /**
+ * Show the migration wizard for importing passwords,
+ * cookies, history, preferences, and bookmarks.
+ */
+ importFromBrowser: function PO_importFromBrowser() {
+ // We pass in the type of source we're using for future use:
+ MigrationUtils.showMigrationWizard(window, [MigrationUtils.MIGRATION_ENTRYPOINT_PLACES]);
+ },
+
+ /**
+ * Open a file-picker and import the selected file into the bookmarks store
+ */
+ importFromFile: function PO_importFromFile() {
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) {
+ var {BookmarkHTMLUtils} = ChromeUtils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+ BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false)
+ .catch(Cu.reportError);
+ }
+ };
+
+ fp.init(window, PlacesUIUtils.getString("SelectImport"),
+ Ci.nsIFilePicker.modeOpen);
+ fp.appendFilters(Ci.nsIFilePicker.filterHTML);
+ fp.open(fpCallback);
+ },
+
+ /**
+ * Allows simple exporting of bookmarks.
+ */
+ exportBookmarks: function PO_exportBookmarks() {
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult != Ci.nsIFilePicker.returnCancel) {
+ var {BookmarkHTMLUtils} = ChromeUtils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+ BookmarkHTMLUtils.exportToFile(fp.file.path)
+ .catch(Cu.reportError);
+ }
+ };
+
+ fp.init(window, PlacesUIUtils.getString("EnterExport"),
+ Ci.nsIFilePicker.modeSave);
+ fp.appendFilters(Ci.nsIFilePicker.filterHTML);
+ fp.defaultString = "bookmarks.html";
+ fp.open(fpCallback);
+ },
+
+ /**
+ * Populates the restore menu with the dates of the backups available.
+ */
+ populateRestoreMenu: function PO_populateRestoreMenu() {
+ let restorePopup = document.getElementById("fileRestorePopup");
+
+ const dtOptions = {
+ dateStyle: "long"
+ };
+ let dateFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
+
+ // Remove existing menu items. Last item is the restoreFromFile item.
+ while (restorePopup.childNodes.length > 1)
+ restorePopup.firstChild.remove();
+
+ (async function() {
+ let backupFiles = await PlacesBackups.getBackupFiles();
+ if (backupFiles.length == 0)
+ return;
+
+ // Populate menu with backups.
+ for (let i = 0; i < backupFiles.length; i++) {
+ let fileSize = (await OS.File.stat(backupFiles[i])).size;
+ let [size, unit] = DownloadUtils.convertByteUnits(fileSize);
+ let sizeString = PlacesUtils.getFormattedString("backupFileSizeText",
+ [size, unit]);
+ let sizeInfo;
+ let bookmarkCount = PlacesBackups.getBookmarkCountForFile(backupFiles[i]);
+ if (bookmarkCount != null) {
+ sizeInfo = " (" + sizeString + " - " +
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ bookmarkCount,
+ [bookmarkCount]) +
+ ")";
+ } else {
+ sizeInfo = " (" + sizeString + ")";
+ }
+
+ let backupDate = PlacesBackups.getDateForFile(backupFiles[i]);
+ let m = restorePopup.insertBefore(document.createElement("menuitem"),
+ document.getElementById("restoreFromFile"));
+ m.setAttribute("label", dateFormatter.format(backupDate) + sizeInfo);
+ m.setAttribute("value", OS.Path.basename(backupFiles[i]));
+ m.setAttribute("oncommand",
+ "PlacesOrganizer.onRestoreMenuItemClick(this);");
+ }
+
+ // Add the restoreFromFile item.
+ restorePopup.insertBefore(document.createElement("menuseparator"),
+ document.getElementById("restoreFromFile"));
+ })();
+ },
+
+ /**
+ * Called when a menuitem is selected from the restore menu.
+ */
+ async onRestoreMenuItemClick(aMenuItem) {
+ let backupName = aMenuItem.getAttribute("value");
+ let backupFilePaths = await PlacesBackups.getBackupFiles();
+ for (let backupFilePath of backupFilePaths) {
+ if (OS.Path.basename(backupFilePath) == backupName) {
+ PlacesOrganizer.restoreBookmarksFromFile(backupFilePath);
+ break;
+ }
+ }
+ },
+
+ /**
+ * Called when 'Choose File...' is selected from the restore menu.
+ * Prompts for a file and restores bookmarks to those in the file.
+ */
+ onRestoreBookmarksFromFile: function PO_onRestoreBookmarksFromFile() {
+ let backupsDir = Services.dirsvc.get("Desk", Ci.nsIFile);
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let fpCallback = aResult => {
+ if (aResult != Ci.nsIFilePicker.returnCancel) {
+ this.restoreBookmarksFromFile(fp.file.path);
+ }
+ };
+
+ fp.init(window, PlacesUIUtils.getString("bookmarksRestoreTitle"),
+ Ci.nsIFilePicker.modeOpen);
+ fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
+ RESTORE_FILEPICKER_FILTER_EXT);
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+ fp.displayDirectory = backupsDir;
+ fp.open(fpCallback);
+ },
+
+ /**
+ * Restores bookmarks from a JSON file.
+ */
+ restoreBookmarksFromFile: function PO_restoreBookmarksFromFile(aFilePath) {
+ // check file extension
+ if (!aFilePath.toLowerCase().endsWith("json") &&
+ !aFilePath.toLowerCase().endsWith("jsonlz4")) {
+ this._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreFormatError"));
+ return;
+ }
+
+ // confirm ok to delete existing bookmarks
+ if (!Services.prompt.confirm(null,
+ PlacesUIUtils.getString("bookmarksRestoreAlertTitle"),
+ PlacesUIUtils.getString("bookmarksRestoreAlert")))
+ return;
+
+ (async function() {
+ try {
+ await BookmarkJSONUtils.importFromFile(aFilePath, true);
+ } catch (ex) {
+ PlacesOrganizer._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreParseError"));
+ }
+ })();
+ },
+
+ _showErrorAlert: function PO__showErrorAlert(aMsg) {
+ var brandShortName = document.getElementById("brandStrings").
+ getString("brandShortName");
+
+ Services.prompt.alert(window, brandShortName, aMsg);
+ },
+
+ /**
+ * Backup bookmarks to desktop, auto-generate a filename with a date.
+ * The file is a JSON serialization of bookmarks, tags and any annotations
+ * of those items.
+ */
+ backupBookmarks: function PO_backupBookmarks() {
+ let backupsDir = Services.dirsvc.get("Desk", Ci.nsIFile);
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult != Ci.nsIFilePicker.returnCancel) {
+ // There is no OS.File version of the filepicker yet (Bug 937812).
+ PlacesBackups.saveBookmarksToJSONFile(fp.file.path)
+ .catch(Cu.reportError);
+ }
+ };
+
+ fp.init(window, PlacesUIUtils.getString("bookmarksBackupTitle"),
+ Ci.nsIFilePicker.modeSave);
+ fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
+ RESTORE_FILEPICKER_FILTER_EXT);
+ fp.defaultString = PlacesBackups.getFilenameForDate();
+ fp.defaultExtension = "json";
+ fp.displayDirectory = backupsDir;
+ fp.open(fpCallback);
+ },
+
+ _detectAndSetDetailsPaneMinimalState:
+ function PO__detectAndSetDetailsPaneMinimalState(aNode) {
+ /**
+ * The details of simple folder-items (as opposed to livemarks) or the
+ * of livemark-children are not likely to fill the infoBox anyway,
+ * thus we remove the "More/Less" button and show all details.
+ *
+ * the wasminimal attribute here is used to persist the "more/less"
+ * state in a bookmark->folder->bookmark scenario.
+ */
+ var infoBox = document.getElementById("infoBox");
+ var infoBoxExpanderWrapper = document.getElementById("infoBoxExpanderWrapper");
+ var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");
+
+ if (!aNode) {
+ infoBoxExpanderWrapper.hidden = true;
+ return;
+ }
+ if (aNode.itemId != -1 &&
+ PlacesUtils.nodeIsFolder(aNode) && !aNode._feedURI) {
+ if (infoBox.getAttribute("minimal") == "true")
+ infoBox.setAttribute("wasminimal", "true");
+ infoBox.removeAttribute("minimal");
+ infoBoxExpanderWrapper.hidden = true;
+ } else {
+ if (infoBox.getAttribute("wasminimal") == "true")
+ infoBox.setAttribute("minimal", "true");
+ infoBox.removeAttribute("wasminimal");
+ infoBoxExpanderWrapper.hidden =
+ this._additionalInfoFields.every(id =>
+ document.getElementById(id).collapsed);
+ }
+ additionalInfoBroadcaster.hidden = infoBox.getAttribute("minimal") == "true";
+ },
+
+ // NOT YET USED
+ updateThumbnailProportions: function PO_updateThumbnailProportions() {
+ var previewBox = document.getElementById("previewBox");
+ var canvas = document.getElementById("itemThumbnail");
+ var height = previewBox.boxObject.height;
+ var width = height * (screen.width / screen.height);
+ canvas.width = width;
+ canvas.height = height;
+ },
+
+ _fillDetailsPane: function PO__fillDetailsPane(aNodeList) {
+ var infoBox = document.getElementById("infoBox");
+ var detailsDeck = document.getElementById("detailsDeck");
+
+ // Make sure the infoBox UI is visible if we need to use it, we hide it
+ // below when we don't.
+ infoBox.hidden = false;
+ let selectedNode = aNodeList.length == 1 ? aNodeList[0] : null;
+
+ // If a textbox within a panel is focused, force-blur it so its contents
+ // are saved
+ if (gEditItemOverlay.itemId != -1) {
+ var focusedElement = document.commandDispatcher.focusedElement;
+ if ((focusedElement instanceof HTMLInputElement ||
+ focusedElement instanceof HTMLTextAreaElement) &&
+ /^editBMPanel.*/.test(focusedElement.parentNode.parentNode.id))
+ focusedElement.blur();
+
+ // don't update the panel if we are already editing this node unless we're
+ // in multi-edit mode
+ if (selectedNode) {
+ let concreteId = PlacesUtils.getConcreteItemId(selectedNode);
+ var nodeIsSame = gEditItemOverlay.itemId == selectedNode.itemId ||
+ gEditItemOverlay.itemId == concreteId ||
+ (selectedNode.itemId == -1 && gEditItemOverlay.uri &&
+ gEditItemOverlay.uri == selectedNode.uri);
+ if (nodeIsSame && detailsDeck.selectedIndex == 1 &&
+ !gEditItemOverlay.multiEdit)
+ return;
+ }
+ }
+
+ // Clean up the panel before initing it again.
+ gEditItemOverlay.uninitPanel(false);
+
+ if (selectedNode && !PlacesUtils.nodeIsSeparator(selectedNode)) {
+ detailsDeck.selectedIndex = 1;
+
+ gEditItemOverlay.initPanel({ node: selectedNode,
+ hiddenRows: ["folderPicker"] });
+
+ this._detectAndSetDetailsPaneMinimalState(selectedNode);
+ } else if (!selectedNode && aNodeList[0]) {
+ if (aNodeList.every(PlacesUtils.nodeIsURI)) {
+ let uris = aNodeList.map(node => Services.io.newURI(node.uri));
+ detailsDeck.selectedIndex = 1;
+ gEditItemOverlay.initPanel({ uris,
+ hiddenRows: ["folderPicker",
+ "loadInSidebar",
+ "location",
+ "keyword",
+ "description",
+ "name"]});
+ this._detectAndSetDetailsPaneMinimalState(selectedNode);
+ } else {
+ detailsDeck.selectedIndex = 0;
+ let selectItemDesc = document.getElementById("selectItemDescription");
+ let itemsCountLabel = document.getElementById("itemsCountText");
+ selectItemDesc.hidden = false;
+ itemsCountLabel.value =
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ aNodeList.length, [aNodeList.length]);
+ infoBox.hidden = true;
+ }
+ } else {
+ detailsDeck.selectedIndex = 0;
+ infoBox.hidden = true;
+ let selectItemDesc = document.getElementById("selectItemDescription");
+ let itemsCountLabel = document.getElementById("itemsCountText");
+ let itemsCount = 0;
+ if (ContentArea.currentView.result) {
+ let rootNode = ContentArea.currentView.result.root;
+ if (rootNode.containerOpen)
+ itemsCount = rootNode.childCount;
+ }
+ if (itemsCount == 0) {
+ selectItemDesc.hidden = true;
+ itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.noItems");
+ } else {
+ selectItemDesc.hidden = false;
+ itemsCountLabel.value =
+ PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
+ itemsCount, [itemsCount]);
+ }
+ }
+ },
+
+ // NOT YET USED
+ _updateThumbnail: function PO__updateThumbnail() {
+ var bo = document.getElementById("previewBox").boxObject;
+ var width = bo.width;
+ var height = bo.height;
+
+ var canvas = document.getElementById("itemThumbnail");
+ var ctx = canvas.getContext("2d");
+ var notAvailableText = canvas.getAttribute("notavailabletext");
+ ctx.save();
+ ctx.fillStyle = "-moz-Dialog";
+ ctx.fillRect(0, 0, width, height);
+ ctx.translate(width / 2, height / 2);
+
+ ctx.fillStyle = "GrayText";
+ ctx.mozTextStyle = "12pt sans serif";
+ var len = ctx.mozMeasureText(notAvailableText);
+ ctx.translate(-len / 2, 0);
+ ctx.mozDrawText(notAvailableText);
+ ctx.restore();
+ },
+
+ toggleAdditionalInfoFields: function PO_toggleAdditionalInfoFields() {
+ var infoBox = document.getElementById("infoBox");
+ var infoBoxExpander = document.getElementById("infoBoxExpander");
+ var infoBoxExpanderLabel = document.getElementById("infoBoxExpanderLabel");
+ var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");
+
+ if (infoBox.getAttribute("minimal") == "true") {
+ infoBox.removeAttribute("minimal");
+ infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("lesslabel");
+ infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("lessaccesskey");
+ infoBoxExpander.className = "expander-up";
+ additionalInfoBroadcaster.removeAttribute("hidden");
+ } else {
+ infoBox.setAttribute("minimal", "true");
+ infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("morelabel");
+ infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("moreaccesskey");
+ infoBoxExpander.className = "expander-down";
+ additionalInfoBroadcaster.setAttribute("hidden", "true");
+ }
+ },
+};
+
+/**
+ * A set of utilities relating to search within Bookmarks and History.
+ */
+var PlacesSearchBox = {
+
+ /**
+ * The Search text field
+ */
+ get searchFilter() {
+ return document.getElementById("searchFilter");
+ },
+
+ /**
+ * Folders to include when searching.
+ */
+ _folders: [],
+ get folders() {
+ if (this._folders.length == 0) {
+ this._folders.push(PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.toolbarFolderId,
+ PlacesUtils.mobileFolderId);
+ }
+ return this._folders;
+ },
+ set folders(aFolders) {
+ this._folders = aFolders;
+ return aFolders;
+ },
+
+ /**
+ * Run a search for the specified text, over the collection specified by
+ * the dropdown arrow. The default is all bookmarks, but can be
+ * localized to the active collection.
+ * @param filterString
+ * The text to search for.
+ */
+ search: function PSB_search(filterString) {
+ var PO = PlacesOrganizer;
+ // If the user empties the search box manually, reset it and load all
+ // contents of the current scope.
+ // XXX this might be to jumpy, maybe should search for "", so results
+ // are ungrouped, and search box not reset
+ if (filterString == "") {
+ PO.onPlaceSelected(false);
+ return;
+ }
+
+ let currentView = ContentArea.currentView;
+
+ // Search according to the current scope, which was set by
+ // PQB_setScope()
+ switch (PlacesSearchBox.filterCollection) {
+ case "bookmarks":
+ currentView.applyFilter(filterString, this.folders);
+ break;
+ case "history": {
+ let currentOptions = PO.getCurrentOptions();
+ if (currentOptions.queryType != Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
+ let query = PlacesUtils.history.getNewQuery();
+ query.searchTerms = filterString;
+ let options = currentOptions.clone();
+ // Make sure we're getting uri results.
+ options.resultType = currentOptions.RESULTS_AS_URI;
+ options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
+ options.includeHidden = true;
+ currentView.load([query], options);
+ } else {
+ currentView.applyFilter(filterString, null, true);
+ }
+ break;
+ }
+ default:
+ throw "Invalid filterCollection on search";
+ }
+
+ // Update the details panel
+ PlacesOrganizer.updateDetailsPane();
+ },
+
+ /**
+ * Finds across all history or all bookmarks.
+ */
+ findAll: function PSB_findAll() {
+ switch (this.filterCollection) {
+ case "history":
+ PlacesQueryBuilder.setScope("history");
+ break;
+ default:
+ PlacesQueryBuilder.setScope("bookmarks");
+ break;
+ }
+ this.focus();
+ },
+
+ /**
+ * Updates the display with the title of the current collection.
+ * @param aTitle
+ * The title of the current collection.
+ */
+ updateCollectionTitle: function PSB_updateCollectionTitle(aTitle) {
+ let title = "";
+ switch (this.filterCollection) {
+ case "history":
+ title = PlacesUIUtils.getString("searchHistory");
+ break;
+ default:
+ title = PlacesUIUtils.getString("searchBookmarks");
+ }
+ this.searchFilter.placeholder = title;
+ },
+
+ /**
+ * Gets/sets the active collection from the dropdown menu.
+ */
+ get filterCollection() {
+ return this.searchFilter.getAttribute("collection");
+ },
+ set filterCollection(collectionName) {
+ if (collectionName == this.filterCollection)
+ return collectionName;
+
+ this.searchFilter.setAttribute("collection", collectionName);
+ this.updateCollectionTitle();
+
+ return collectionName;
+ },
+
+ /**
+ * Focus the search box
+ */
+ focus: function PSB_focus() {
+ this.searchFilter.focus();
+ },
+
+ /**
+ * Set up the gray text in the search bar as the Places View loads.
+ */
+ init: function PSB_init() {
+ if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll", false)) {
+ this.searchFilter.setAttribute("clickSelectsAll", true);
+ }
+ this.updateCollectionTitle();
+ },
+
+ /**
+ * Gets or sets the text shown in the Places Search Box
+ */
+ get value() {
+ return this.searchFilter.value;
+ },
+ set value(value) {
+ return this.searchFilter.value = value;
+ },
+};
+
+/**
+ * Functions and data for advanced query builder
+ */
+var PlacesQueryBuilder = {
+
+ queries: [],
+ queryOptions: null,
+
+ /**
+ * Sets the search scope. This can be called when no search is active, and
+ * in that case, when the user does begin a search aScope will be used (see
+ * PSB_search()). If there is an active search, it's performed again to
+ * update the content tree.
+ * @param aScope
+ * The search scope: "bookmarks", "collection" or "history".
+ */
+ setScope: function PQB_setScope(aScope) {
+ // Determine filterCollection, folders, and scopeButtonId based on aScope.
+ var filterCollection;
+ var folders = [];
+ switch (aScope) {
+ case "history":
+ filterCollection = "history";
+ break;
+ case "bookmarks":
+ filterCollection = "bookmarks";
+ folders.push(PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.toolbarFolderId,
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.mobileFolderId);
+ break;
+ default:
+ throw "Invalid search scope";
+ }
+
+ // Update the search box. Re-search if there's an active search.
+ PlacesSearchBox.filterCollection = filterCollection;
+ PlacesSearchBox.folders = folders;
+ var searchStr = PlacesSearchBox.searchFilter.value;
+ if (searchStr)
+ PlacesSearchBox.search(searchStr);
+ }
+};
+
+/**
+ * Population and commands for the View Menu.
+ */
+var ViewMenu = {
+ /**
+ * Removes content generated previously from a menupopup.
+ * @param popup
+ * The popup that contains the previously generated content.
+ * @param startID
+ * The id attribute of an element that is the start of the
+ * dynamically generated region - remove elements after this
+ * item only.
+ * Must be contained by popup. Can be null (in which case the
+ * contents of popup are removed).
+ * @param endID
+ * The id attribute of an element that is the end of the
+ * dynamically generated region - remove elements up to this
+ * item only.
+ * Must be contained by popup. Can be null (in which case all
+ * items until the end of the popup will be removed). Ignored
+ * if startID is null.
+ * @returns The element for the caller to insert new items before,
+ * null if the caller should just append to the popup.
+ */
+ _clean: function VM__clean(popup, startID, endID) {
+ if (endID && !startID)
+ throw new Error("meaningless to have valid endID and null startID");
+ if (startID) {
+ var startElement = document.getElementById(startID);
+ if (!startElement)
+ throw new Error("startID does not correspond to an existing element");
+ if (startElement.parentNode != popup)
+ throw new Error("startElement is not in popup");
+ var endElement = null;
+ if (endID) {
+ endElement = document.getElementById(endID);
+ if (!endElement)
+ throw new Error("endID does not correspond to an existing element");
+ if (endElement.parentNode != popup)
+ throw new Error("endElement is not in popup");
+ }
+ while (startElement.nextSibling != endElement)
+ popup.removeChild(startElement.nextSibling);
+ return endElement;
+ }
+ while (popup.hasChildNodes()) {
+ popup.firstChild.remove();
+ }
+ return null;
+ },
+
+ /**
+ * Fills a menupopup with a list of columns
+ * @param event
+ * The popupshowing event that invoked this function.
+ * @param startID
+ * see _clean
+ * @param endID
+ * see _clean
+ * @param type
+ * the type of the menuitem, e.g. "radio" or "checkbox".
+ * Can be null (no-type).
+ * Checkboxes are checked if the column is visible.
+ * @param propertyPrefix
+ * If propertyPrefix is non-null:
+ * propertyPrefix + column ID + ".label" will be used to get the
+ * localized label string.
+ * propertyPrefix + column ID + ".accesskey" will be used to get the
+ * localized accesskey.
+ * If propertyPrefix is null, the column label is used as label and
+ * no accesskey is assigned.
+ */
+ fillWithColumns: function VM_fillWithColumns(event, startID, endID, type, propertyPrefix) {
+ var popup = event.target;
+ var pivot = this._clean(popup, startID, endID);
+
+ var content = document.getElementById("placeContent");
+ var columns = content.columns;
+ for (var i = 0; i < columns.count; ++i) {
+ var column = columns.getColumnAt(i).element;
+ var menuitem = document.createElement("menuitem");
+ menuitem.id = "menucol_" + column.id;
+ menuitem.column = column;
+ var label = column.getAttribute("label");
+ if (propertyPrefix) {
+ var menuitemPrefix = propertyPrefix;
+ // for string properties, use "name" as the id, instead of "title"
+ // see bug #386287 for details
+ var columnId = column.getAttribute("anonid");
+ menuitemPrefix += columnId == "title" ? "name" : columnId;
+ label = PlacesUIUtils.getString(menuitemPrefix + ".label");
+ var accesskey = PlacesUIUtils.getString(menuitemPrefix + ".accesskey");
+ menuitem.setAttribute("accesskey", accesskey);
+ }
+ menuitem.setAttribute("label", label);
+ if (type == "radio") {
+ menuitem.setAttribute("type", "radio");
+ menuitem.setAttribute("name", "columns");
+ // This column is the sort key. Its item is checked.
+ if (column.getAttribute("sortDirection") != "") {
+ menuitem.setAttribute("checked", "true");
+ }
+ } else if (type == "checkbox") {
+ menuitem.setAttribute("type", "checkbox");
+ // Cannot uncheck the primary column.
+ if (column.getAttribute("primary") == "true")
+ menuitem.setAttribute("disabled", "true");
+ // Items for visible columns are checked.
+ if (!column.hidden)
+ menuitem.setAttribute("checked", "true");
+ }
+ if (pivot)
+ popup.insertBefore(menuitem, pivot);
+ else
+ popup.appendChild(menuitem);
+ }
+ event.stopPropagation();
+ },
+
+ /**
+ * Set up the content of the view menu.
+ */
+ populateSortMenu: function VM_populateSortMenu(event) {
+ this.fillWithColumns(event, "viewUnsorted", "directionSeparator", "radio", "view.sortBy.1.");
+
+ var sortColumn = this._getSortColumn();
+ var viewSortAscending = document.getElementById("viewSortAscending");
+ var viewSortDescending = document.getElementById("viewSortDescending");
+ // We need to remove an existing checked attribute because the unsorted
+ // menu item is not rebuilt every time we open the menu like the others.
+ var viewUnsorted = document.getElementById("viewUnsorted");
+ if (!sortColumn) {
+ viewSortAscending.removeAttribute("checked");
+ viewSortDescending.removeAttribute("checked");
+ viewUnsorted.setAttribute("checked", "true");
+ } else if (sortColumn.getAttribute("sortDirection") == "ascending") {
+ viewSortAscending.setAttribute("checked", "true");
+ viewSortDescending.removeAttribute("checked");
+ viewUnsorted.removeAttribute("checked");
+ } else if (sortColumn.getAttribute("sortDirection") == "descending") {
+ viewSortDescending.setAttribute("checked", "true");
+ viewSortAscending.removeAttribute("checked");
+ viewUnsorted.removeAttribute("checked");
+ }
+ },
+
+ /**
+ * Shows/Hides a tree column.
+ * @param element
+ * The menuitem element for the column
+ */
+ showHideColumn: function VM_showHideColumn(element) {
+ var column = element.column;
+
+ var splitter = column.nextSibling;
+ if (splitter && splitter.localName != "splitter")
+ splitter = null;
+
+ if (element.getAttribute("checked") == "true") {
+ column.setAttribute("hidden", "false");
+ if (splitter)
+ splitter.removeAttribute("hidden");
+ } else {
+ column.setAttribute("hidden", "true");
+ if (splitter)
+ splitter.setAttribute("hidden", "true");
+ }
+ },
+
+ /**
+ * Gets the last column that was sorted.
+ * @returns the currently sorted column, null if there is no sorted column.
+ */
+ _getSortColumn: function VM__getSortColumn() {
+ var content = document.getElementById("placeContent");
+ var cols = content.columns;
+ for (var i = 0; i < cols.count; ++i) {
+ var column = cols.getColumnAt(i).element;
+ var sortDirection = column.getAttribute("sortDirection");
+ if (sortDirection == "ascending" || sortDirection == "descending")
+ return column;
+ }
+ return null;
+ },
+
+ /**
+ * Sorts the view by the specified column.
+ * @param aColumn
+ * The colum that is the sort key. Can be null - the
+ * current sort column or the title column will be used.
+ * @param aDirection
+ * The direction to sort - "ascending" or "descending".
+ * Can be null - the last direction or descending will be used.
+ *
+ * If both aColumnID and aDirection are null, the view will be unsorted.
+ */
+ setSortColumn: function VM_setSortColumn(aColumn, aDirection) {
+ var result = document.getElementById("placeContent").result;
+ if (!aColumn && !aDirection) {
+ result.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
+ return;
+ }
+
+ var columnId;
+ if (aColumn) {
+ columnId = aColumn.getAttribute("anonid");
+ if (!aDirection) {
+ let sortColumn = this._getSortColumn();
+ if (sortColumn)
+ aDirection = sortColumn.getAttribute("sortDirection");
+ }
+ } else {
+ let sortColumn = this._getSortColumn();
+ columnId = sortColumn ? sortColumn.getAttribute("anonid") : "title";
+ }
+
+ // This maps the possible values of columnId (i.e., anonid's of treecols in
+ // placeContent) to the default sortingMode and sortingAnnotation values for
+ // each column.
+ // key: Sort key in the name of one of the
+ // nsINavHistoryQueryOptions.SORT_BY_* constants
+ // dir: Default sort direction to use if none has been specified
+ // anno: The annotation to sort by, if key is "ANNOTATION"
+ var colLookupTable = {
+ title: { key: "TITLE", dir: "ascending" },
+ tags: { key: "TAGS", dir: "ascending" },
+ url: { key: "URI", dir: "ascending" },
+ date: { key: "DATE", dir: "descending" },
+ visitCount: { key: "VISITCOUNT", dir: "descending" },
+ dateAdded: { key: "DATEADDED", dir: "descending" },
+ lastModified: { key: "LASTMODIFIED", dir: "descending" },
+ description: { key: "ANNOTATION",
+ dir: "ascending",
+ anno: PlacesUIUtils.DESCRIPTION_ANNO }
+ };
+
+ // Make sure we have a valid column.
+ if (!colLookupTable.hasOwnProperty(columnId))
+ throw new Error("Invalid column");
+
+ // Use a default sort direction if none has been specified. If aDirection
+ // is invalid, result.sortingMode will be undefined, which has the effect
+ // of unsorting the tree.
+ aDirection = (aDirection || colLookupTable[columnId].dir).toUpperCase();
+
+ var sortConst = "SORT_BY_" + colLookupTable[columnId].key + "_" + aDirection;
+ result.sortingAnnotation = colLookupTable[columnId].anno || "";
+ result.sortingMode = Ci.nsINavHistoryQueryOptions[sortConst];
+ }
+};
+
+var ContentArea = {
+ _specialViews: new Map(),
+
+ init: function CA_init() {
+ this._deck = document.getElementById("placesViewsDeck");
+ this._toolbar = document.getElementById("placesToolbar");
+ ContentTree.init();
+ this._setupView();
+ },
+
+ /**
+ * Gets the content view to be used for loading the given query.
+ * If a custom view was set by setContentViewForQueryString, that
+ * view would be returned, else the default tree view is returned
+ *
+ * @param aQueryString
+ * a query string
+ * @return the view to be used for loading aQueryString.
+ */
+ getContentViewForQueryString:
+ function CA_getContentViewForQueryString(aQueryString) {
+ try {
+ if (this._specialViews.has(aQueryString)) {
+ let { view, options } = this._specialViews.get(aQueryString);
+ if (typeof view == "function") {
+ view = view();
+ this._specialViews.set(aQueryString, { view, options });
+ }
+ return view;
+ }
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ return ContentTree.view;
+ },
+
+ /**
+ * Sets a custom view to be used rather than the default places tree
+ * whenever the given query is selected in the left pane.
+ * @param aQueryString
+ * a query string
+ * @param aView
+ * Either the custom view or a function that will return the view
+ * the first (and only) time it's called.
+ * @param [optional] aOptions
+ * Object defining special options for the view.
+ * @see ContentTree.viewOptions for supported options and default values.
+ */
+ setContentViewForQueryString:
+ function CA_setContentViewForQueryString(aQueryString, aView, aOptions) {
+ if (!aQueryString ||
+ typeof aView != "object" && typeof aView != "function")
+ throw new Error("Invalid arguments");
+
+ this._specialViews.set(aQueryString, { view: aView,
+ options: aOptions || {} });
+ },
+
+ get currentView() {
+ return PlacesUIUtils.getViewForNode(this._deck.selectedPanel);
+ },
+ set currentView(aNewView) {
+ let oldView = this.currentView;
+ if (oldView != aNewView) {
+ this._deck.selectedPanel = aNewView.associatedElement;
+
+ // If the content area inactivated view was focused, move focus
+ // to the new view.
+ if (document.activeElement == oldView.associatedElement)
+ aNewView.associatedElement.focus();
+ }
+ return aNewView;
+ },
+
+ get currentPlace() {
+ return this.currentView.place;
+ },
+ set currentPlace(aQueryString) {
+ let oldView = this.currentView;
+ let newView = this.getContentViewForQueryString(aQueryString);
+ newView.place = aQueryString;
+ if (oldView != newView) {
+ oldView.active = false;
+ this.currentView = newView;
+ this._setupView();
+ newView.active = true;
+ }
+ return aQueryString;
+ },
+
+ /**
+ * Applies view options.
+ */
+ _setupView: function CA__setupView() {
+ let options = this.currentViewOptions;
+
+ // showDetailsPane.
+ let detailsDeck = document.getElementById("detailsDeck");
+ detailsDeck.hidden = !options.showDetailsPane;
+
+ // toolbarSet.
+ for (let elt of this._toolbar.childNodes) {
+ elt.hidden = !options.toolbarSet.includes(elt.id);
+ }
+ },
+
+ /**
+ * Options for the current view.
+ *
+ * @see ContentTree.viewOptions for supported options and default values.
+ */
+ get currentViewOptions() {
+ // Use ContentTree options as default.
+ let viewOptions = ContentTree.viewOptions;
+ if (this._specialViews.has(this.currentPlace)) {
+ let { options } = this._specialViews.get(this.currentPlace);
+ for (let option in options) {
+ viewOptions[option] = options[option];
+ }
+ }
+ return viewOptions;
+ },
+
+ focus() {
+ this._deck.selectedPanel.focus();
+ }
+};
+
+var ContentTree = {
+ init: function CT_init() {
+ this._view = document.getElementById("placeContent");
+ },
+
+ get view() {
+ return this._view;
+ },
+
+ get viewOptions() {
+ return Object.seal({
+ showDetailsPane: true,
+ toolbarSet: "placesMenu, toolbar-spacer, searchFilter"
+ });
+ },
+
+ openSelectedNode: function CT_openSelectedNode(aEvent) {
+ let view = this.view;
+ PlacesUIUtils.openNodeWithEvent(view.selectedNode, aEvent, true);
+ },
+
+ onClick: function CT_onClick(aEvent) {
+ let node = this.view.selectedNode;
+ if (node) {
+ let doubleClick = aEvent.button == 0 && aEvent.detail == 2;
+ let middleClick = aEvent.button == 1 && aEvent.detail == 1;
+ if (PlacesUtils.nodeIsURI(node) && (doubleClick || middleClick)) {
+ // Open associated uri in the browser.
+ this.openSelectedNode(aEvent);
+ } else if (middleClick && PlacesUtils.nodeIsContainer(node)) {
+ // The command execution function will take care of seeing if the
+ // selection is a folder or a different container type, and will
+ // load its contents in tabs.
+ PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this.view);
+ }
+ }
+ },
+
+ onKeyPress: function CT_onKeyPress(aEvent) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
+ this.openSelectedNode(aEvent);
+ }
+};
diff --git a/comm/suite/components/places/content/places.xul b/comm/suite/components/places/content/places.xul
new file mode 100644
index 0000000000..d5acde063d
--- /dev/null
+++ b/comm/suite/components/places/content/places.xul
@@ -0,0 +1,337 @@
+<?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://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/content/places/organizer.css"?>
+
+<?xml-stylesheet href="chrome://communicator/skin/"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/bookmarks.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/editBookmarkOverlay.xul"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % placesDTD SYSTEM "chrome://communicator/locale/places/places.dtd">
+%placesDTD;
+<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
+%editMenuOverlayDTD;
+<!ENTITY % navDTD SYSTEM "chrome://navigator/locale/navigator.dtd">
+%navDTD;
+]>
+
+<window id="places"
+ title="&places.library.title;"
+ windowtype="Places:Organizer"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="PlacesOrganizer.init();"
+ onunload="PlacesOrganizer.destroy();"
+ width="&places.library.width;" height="&places.library.height;"
+ screenX="10" screenY="10"
+ toggletoolbar="true"
+ persist="width height screenX screenY sizemode">
+
+ <script src="chrome://communicator/content/places/places.js"/>
+ <script src="chrome://communicator/content/places/editBookmarkOverlay.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+
+ <stringbundleset id="placesStringSet">
+ <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
+ </stringbundleset>
+
+ <commandset id="placesCommands"/>
+ <commandset id="tasksCommands"/>
+
+ <commandset id="organizerCommandSet">
+ <command id="OrganizerCommand_find:all"
+ oncommand="PlacesSearchBox.findAll();"/>
+ <command id="OrganizerCommand_export"
+ oncommand="PlacesOrganizer.exportBookmarks();"/>
+ <command id="OrganizerCommand_import"
+ oncommand="PlacesOrganizer.importFromFile();"/>
+ <command id="OrganizerCommand_backup"
+ oncommand="PlacesOrganizer.backupBookmarks();"/>
+ <command id="OrganizerCommand_restoreFromFile"
+ oncommand="PlacesOrganizer.onRestoreBookmarksFromFile();"/>
+ <command id="OrganizerCommand_search:save"
+ oncommand="PlacesOrganizer.saveSearch();"/>
+ <command id="OrganizerCommand_search:moreCriteria"
+ oncommand="PlacesQueryBuilder.addRow();"/>
+ <command id="OrganizerCommand:Back"
+ oncommand="PlacesOrganizer.back();"/>
+ <command id="OrganizerCommand:Forward"
+ oncommand="PlacesOrganizer.forward();"/>
+ </commandset>
+
+ <keyset id="placesOrganizerKeyset">
+ <!-- Instantiation Keys -->
+ <key id="placesKey_close" key="&cmd.close.key;" modifiers="accel"
+ oncommand="close();"/>
+
+ <!-- Command Keys -->
+ <key id="placesKey_find:all"
+ command="OrganizerCommand_find:all"
+ key="&cmd.find.key;"
+ modifiers="accel"/>
+
+ <!-- Back/Forward Keys Support -->
+ <key id="placesKey_goBackKb"
+ keycode="VK_LEFT"
+ command="OrganizerCommand:Back"
+ modifiers="accel"/>
+ <key id="placesKey_goForwardKb"
+ keycode="VK_RIGHT"
+ command="OrganizerCommand:Forward"
+ modifiers="accel"/>
+ </keyset>
+
+#include ../../../../../toolkit/content/editMenuKeys.inc.xhtml
+#ifdef XP_MACOSX
+ <keyset id="editMenuKeysExtra">
+ <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/>
+ </keyset>
+#endif
+
+ <keyset id="tasksKeys">
+ <key id="key_close2" disabled="true"/>
+ </keyset>
+
+ <popupset id="placesPopupset">
+ <menupopup id="placesContext"/>
+ <menupopup id="placesColumnsContext"
+ onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
+ oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
+ </popupset>
+
+ <toolbox id="placesToolbox">
+ <toolbar id="placesToolbar"
+ class="chromeclass-toolbar"
+ align="center">
+ <menubar id="placesMenu">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup">
+ <menuitem id="newbookmark"
+ command="placesCmd_new:bookmark"
+ label="&cmd.new_bookmark.label;"
+ accesskey="&cmd.new_bookmark.accesskey;"/>
+ <menuitem id="newfolder"
+ command="placesCmd_new:folder"
+ label="&cmd.new_folder.label;"
+ accesskey="&cmd.new_folder.accesskey;"/>
+ <menuitem id="newseparator"
+ command="placesCmd_new:separator"
+ label="&cmd.new_separator.label;"
+ accesskey="&cmd.new_separator.accesskey;"/>
+ <menuseparator id="fileNewSeparator"/>
+ <menuitem id="orgClose"
+ key="placesKey_close"
+ label="&file.close.label;"
+ accesskey="&file.close.accesskey;"
+ oncommand="close();"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_Edit">
+ <menupopup id="menu_EditPopup">
+ <menuitem id="menu_undo"/>
+ <menuitem id="menu_redo"/>
+
+ <menuseparator id="orgCutSeparator"/>
+
+ <menuitem id="menu_cut"
+ selection="separator|link|folder|mixed"/>
+ <menuitem id="menu_copy"
+ selection="separator|link|folder|mixed"/>
+ <menuitem id="menu_paste"
+ selection="mutable"/>
+ <menuitem id="menu_delete"/>
+
+ <menuseparator id="selectAllSeparator"/>
+
+ <menuitem id="menu_selectAll"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_View">
+ <menupopup id="menu_ViewPopup"
+ onpopupshowing="onViewToolbarsPopupShowing(event)"
+ oncommand="onViewToolbarCommand(event);">
+ <menuseparator id="toolbarmode-sep"/>
+ <menu id="viewColumns"
+ label="&view.columns.label;" accesskey="&view.columns.accesskey;">
+ <menupopup onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
+ oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
+ </menu>
+
+ <menu id="viewSort" label="&view.sort.label;"
+ accesskey="&view.sort.accesskey;">
+ <menupopup onpopupshowing="ViewMenu.populateSortMenu(event);"
+ oncommand="ViewMenu.setSortColumn(event.target.column, null);">
+ <menuitem id="viewUnsorted" type="radio" name="columns"
+ label="&view.unsorted.label;" accesskey="&view.unsorted.accesskey;"
+ oncommand="ViewMenu.setSortColumn(null, null);"/>
+ <menuseparator id="directionSeparator"/>
+ <menuitem id="viewSortAscending" type="radio" name="direction"
+ label="&view.sortAscending.label;" accesskey="&view.sortAscending.accesskey;"
+ oncommand="ViewMenu.setSortColumn(null, 'ascending'); event.stopPropagation();"/>
+ <menuitem id="viewSortDescending" type="radio" name="direction"
+ label="&view.sortDescending.label;" accesskey="&view.sortDescending.accesskey;"
+ oncommand="ViewMenu.setSortColumn(null, 'descending'); event.stopPropagation();"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+
+ <!-- tasks menu filled from tasksOverlay -->
+ <menu id="tasksMenu">
+ <menupopup id="taskPopup">
+ <menuitem id="backupBookmarks"
+ command="OrganizerCommand_backup"
+ label="&cmd.backup.label;"
+ accesskey="&cmd.backup.accesskey;"/>
+ <menu id="fileRestoreMenu" label="&cmd.restore2.label;"
+ accesskey="&cmd.restore2.accesskey;">
+ <menupopup id="fileRestorePopup" onpopupshowing="PlacesOrganizer.populateRestoreMenu();">
+ <menuitem id="restoreFromFile"
+ command="OrganizerCommand_restoreFromFile"
+ label="&cmd.restoreFromFile.label;"
+ accesskey="&cmd.restoreFromFile.accesskey;"/>
+ </menupopup>
+ </menu>
+ <menuseparator/>
+ <menuitem id="fileImport"
+ command="OrganizerCommand_import"
+ label="&importBookmarksFromHTML.label;"
+ accesskey="&importBookmarksFromHTML.accesskey;"/>
+ <menuitem id="fileExport"
+ command="OrganizerCommand_export"
+ label="&exportBookmarksToHTML.label;"
+ accesskey="&exportBookmarksToHTML.accesskey;"/>
+ <menuseparator/>
+ </menupopup>
+ </menu>
+
+ <!-- window menu filled from tasksOverlay -->
+ <menu id="windowMenu"/>
+
+ <!-- help menu filled from globalOverlay -->
+ <menu id="menu_Help"/>
+ </menubar>
+
+ <toolbarspring id="toolbar-spacer"/>
+
+ <textbox id="searchFilter"
+ type="search"
+ aria-controls="placeContent"
+ oncommand="PlacesSearchBox.search(this.value);"
+ collection="bookmarks">
+ </textbox>
+ </toolbar>
+ </toolbox>
+
+ <hbox flex="1" id="placesView">
+ <tree id="placesList"
+ class="plain placesTree"
+ type="places"
+ hidecolumnpicker="true"
+ treelines="true"
+ context="placesContext"
+ onselect="PlacesOrganizer.onPlaceSelected(true);"
+ onclick="PlacesOrganizer.onPlacesListClick(event);"
+ onfocus="PlacesOrganizer.updateDetailsPane(event);"
+ seltype="single"
+ persist="width"
+ width="200"
+ minwidth="100"
+ maxwidth="400">
+ <treecols>
+ <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ <splitter collapse="none" persist="state"></splitter>
+ <vbox id="contentView" flex="4">
+ <deck id="placesViewsDeck"
+ selectedIndex="0"
+ flex="1">
+ <tree id="placeContent"
+ class="plain placesTree"
+ treelines="true"
+ context="placesContext"
+ flex="1"
+ type="places"
+ selectfirstnode="true"
+ enableColumnDrag="true"
+ onfocus="PlacesOrganizer.updateDetailsPane(event)"
+ onselect="PlacesOrganizer.updateDetailsPane(event)"
+ onkeypress="ContentTree.onKeyPress(event);"
+ onopenflatcontainer="PlacesOrganizer.openFlatContainer(aContainer);">
+ <treecols id="placeContentColumns" context="placesColumnsContext">
+ <treecol label="&col.name.label;" id="placesContentTitle" anonid="title" flex="5" primary="true" ordinal="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.tags.label;" id="placesContentTags" anonid="tags" flex="2"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.mostrecentvisit.label;" id="placesContentDate" anonid="date" flex="1" hidden="true"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.description.label;" id="placesContentDescription" anonid="description" flex="1" hidden="true"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.dateadded.label;" id="placesContentDateAdded" anonid="dateAdded" flex="1" hidden="true"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="&col.lastmodified.label;" id="placesContentLastModified" anonid="lastModified" flex="1" hidden="true"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ </treecols>
+ <treechildren flex="1" onclick="ContentTree.onClick(event);"/>
+ </tree>
+ </deck>
+ <deck id="detailsDeck" style="height: 11em;">
+ <vbox id="itemsCountBox" align="center">
+ <spacer flex="3"/>
+ <label id="itemsCountText"/>
+ <spacer flex="1"/>
+ <description id="selectItemDescription">
+ &detailsPane.selectAnItemText.description;
+ </description>
+ <spacer flex="3"/>
+ </vbox>
+ <vbox id="infoBox" minimal="true">
+ <vbox id="editBookmarkPanelContent" flex="1"/>
+ <hbox id="infoBoxExpanderWrapper" align="center">
+
+ <button type="image" id="infoBoxExpander"
+ class="expander-down"
+ oncommand="PlacesOrganizer.toggleAdditionalInfoFields();"
+ observes="paneElementsBroadcaster"/>
+
+ <label id="infoBoxExpanderLabel"
+ lesslabel="&detailsPane.less.label;"
+ lessaccesskey="&detailsPane.less.accesskey;"
+ morelabel="&detailsPane.more.label;"
+ moreaccesskey="&detailsPane.more.accesskey;"
+ value="&detailsPane.more.label;"
+ accesskey="&detailsPane.more.accesskey;"
+ control="infoBoxExpander"/>
+
+ </hbox>
+ </vbox>
+ </deck>
+ </vbox>
+ </hbox>
+</window>
diff --git a/comm/suite/components/places/content/placesOverlay.xul b/comm/suite/components/places/content/placesOverlay.xul
new file mode 100644
index 0000000000..a71f1e2269
--- /dev/null
+++ b/comm/suite/components/places/content/placesOverlay.xul
@@ -0,0 +1,228 @@
+<!-- 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 % placesDTD SYSTEM "chrome://communicator/locale/places/places.dtd">
+%placesDTD;
+<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
+%editMenuOverlayDTD;
+]>
+
+<overlay id="placesOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script>
+ <![CDATA[
+ const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ ChromeUtils.defineModuleGetter(window,
+ "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
+ ChromeUtils.defineModuleGetter(window,
+ "PlacesUIUtils", "resource:///modules/PlacesUIUtils.jsm");
+ ChromeUtils.defineModuleGetter(window,
+ "PlacesTransactions", "resource://gre/modules/PlacesTransactions.jsm");
+ ChromeUtils.defineModuleGetter(window,
+ "ForgetAboutSite", "resource://gre/modules/ForgetAboutSite.jsm");
+ ChromeUtils.defineModuleGetter(window,
+ "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+ XPCOMUtils.defineLazyScriptGetter(window, "PlacesTreeView",
+ "chrome://communicator/content/places/treeView.js");
+ XPCOMUtils.defineLazyScriptGetter(window,
+ ["PlacesInsertionPoint", "PlacesController", "PlacesControllerDragHelper"],
+ "chrome://communicator/content/places/controller.js");
+ ]]></script>
+
+ <!-- Bookmarks and history tooltip -->
+ <tooltip id="bhTooltip" noautohide="true"
+ onpopupshowing="return window.top.BookmarksEventHandler.fillInBHTooltip(document, event)">
+ <vbox id="bhTooltipTextBox" flex="1">
+ <label id="bhtTitleText" class="tooltip-label" />
+ <label id="bhtUrlText" crop="center" class="tooltip-label uri-element" />
+ </vbox>
+ </tooltip>
+
+ <commandset id="placesCommands"
+ commandupdater="true"
+ events="focus,sort,places"
+ oncommandupdate="PlacesUIUtils.updateCommands(window);">
+ <command id="placesCmd_open"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open');"/>
+ <command id="placesCmd_open:window"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open:window');"/>
+ <command id="placesCmd_open:privatewindow"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open:privatewindow');"/>
+ <command id="placesCmd_open:tab"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_open:tab');"/>
+
+ <command id="placesCmd_new:bookmark"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_new:bookmark');"/>
+ <command id="placesCmd_new:folder"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_new:folder');"/>
+ <command id="placesCmd_new:separator"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_new:separator');"/>
+ <command id="placesCmd_show:info"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_show:info');"/>
+ <command id="placesCmd_rename"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_show:info');"
+ observes="placesCmd_show:info"/>
+ <command id="placesCmd_reload"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_reload');"/>
+ <command id="placesCmd_sortBy:name"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_sortBy:name');"/>
+ <command id="placesCmd_deleteDataHost"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_deleteDataHost');"/>
+ <command id="placesCmd_createBookmark"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_createBookmark');"/>
+
+ <!-- Special versions of cut/copy/paste/delete which check for an open context menu. -->
+ <command id="placesCmd_cut"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_cut');"/>
+ <command id="placesCmd_copy"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_copy');"/>
+ <command id="placesCmd_paste"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_paste');"/>
+ <command id="placesCmd_delete"
+ oncommand="PlacesUIUtils.doCommand(window, 'placesCmd_delete');"/>
+ </commandset>
+
+ <menupopup id="placesContext"
+ onpopupshowing="this._view = PlacesUIUtils.getViewForNode(document.popupNode);
+ if (!PlacesUIUtils.openInTabClosesMenu) {
+ document.getElementById ('placesContext_open:newtab')
+ .setAttribute('closemenu', 'single');
+ }
+ return this._view.buildContextMenu(this);"
+ onpopuphiding="this._view.destroyContextMenu();">
+ <menuitem id="placesContext_open"
+ command="placesCmd_open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ default="true"
+ selectiontype="single"
+ selection="link"/>
+ <menuitem id="placesContext_open:newtab"
+ command="placesCmd_open:tab"
+ label="&cmd.open_tab.label;"
+ accesskey="&cmd.open_tab.accesskey;"
+ selectiontype="single"
+ selection="link"/>
+ <menuitem id="placesContext_openContainer:tabs"
+ oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode);
+ view.controller.openSelectionInTabs(event);"
+ onclick="checkForMiddleClick(this, event);"
+ label="&cmd.open_all_in_tabs.label;"
+ accesskey="&cmd.open_all_in_tabs.accesskey;"
+ selectiontype="single|none"
+ selection="folder|host|query"/>
+ <menuitem id="placesContext_openLinks:tabs"
+ oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode);
+ view.controller.openSelectionInTabs(event);"
+ onclick="checkForMiddleClick(this, event);"
+ label="&cmd.open_all_in_tabs.label;"
+ accesskey="&cmd.open_all_in_tabs.accesskey;"
+ selectiontype="multiple"
+ selection="link"/>
+ <menuitem id="placesContext_open:newwindow"
+ command="placesCmd_open:window"
+ label="&cmd.open_window.label;"
+ accesskey="&cmd.open_window.accesskey;"
+ selectiontype="single"
+ selection="link"/>
+ <menuitem id="placesContext_open:newprivatewindow"
+ command="placesCmd_open:privatewindow"
+ label="&cmd.open_private_window.label;"
+ accesskey="&cmd.open_private_window.accesskey;"
+ selectiontype="single"
+ selection="link"
+ hideifprivatebrowsing="true"/>
+ <menuseparator id="placesContext_openSeparator"/>
+ <menuitem id="placesContext_new:bookmark"
+ command="placesCmd_new:bookmark"
+ label="&cmd.new_bookmark.label;"
+ accesskey="&cmd.new_bookmark.accesskey;"
+ selectiontype="any"
+ hideifnoinsertionpoint="true"/>
+ <menuitem id="placesContext_new:folder"
+ command="placesCmd_new:folder"
+ label="&cmd.new_folder.label;"
+ accesskey="&cmd.context_new_folder.accesskey;"
+ selectiontype="any"
+ hideifnoinsertionpoint="true"/>
+ <menuitem id="placesContext_new:separator"
+ command="placesCmd_new:separator"
+ label="&cmd.new_separator.label;"
+ accesskey="&cmd.new_separator.accesskey;"
+ closemenu="single"
+ selectiontype="any"
+ hideifnoinsertionpoint="true"/>
+ <menuseparator id="placesContext_newSeparator"/>
+ <menuitem id="placesContext_createBookmark"
+ command="placesCmd_createBookmark"
+ selection="link"
+ forcehideselection="bookmark|tagChild"/>
+ <menuitem id="placesContext_cut"
+ command="placesCmd_cut"
+ label="&cutCmd.label;"
+ accesskey="&cutCmd.accesskey;"
+ closemenu="single"
+ selection="bookmark|folder|separator|query"
+ forcehideselection="tagChild|livemarkChild"/>
+ <menuitem id="placesContext_copy"
+ command="placesCmd_copy"
+ label="&copyCmd.label;"
+ closemenu="single"
+ accesskey="&copyCmd.accesskey;"
+ selection="any"/>
+ <menuitem id="placesContext_paste"
+ command="placesCmd_paste"
+ label="&pasteCmd.label;"
+ closemenu="single"
+ accesskey="&pasteCmd.accesskey;"
+ selectiontype="any"
+ hideifnoinsertionpoint="true"/>
+ <menuseparator id="placesContext_editSeparator"/>
+ <menuitem id="placesContext_delete"
+ command="placesCmd_delete"
+ label="&deleteCmd.label;"
+ accesskey="&deleteCmd.accesskey;"
+ closemenu="single"
+ selection="bookmark|tagChild|folder|query|dynamiccontainer|separator|host"/>
+ <menuitem id="placesContext_delete_history"
+ command="placesCmd_delete"
+ closemenu="single"
+ selection="link"
+ forcehideselection="bookmark"/>
+ <menuitem id="placesContext_deleteHost"
+ command="placesCmd_deleteDataHost"
+ label="&cmd.deleteDomainData.label;"
+ accesskey="&cmd.deleteDomainData.accesskey;"
+ closemenu="single"
+ selection="link|host"
+ selectiontype="single"
+ forcehideselection="bookmark"/>
+ <menuseparator id="placesContext_deleteSeparator"/>
+ <menuitem id="placesContext_sortBy:name"
+ command="placesCmd_sortBy:name"
+ label="&cmd.sortby_name.label;"
+ accesskey="&cmd.context_sortby_name.accesskey;"
+ closemenu="single"
+ selection="folder"/>
+ <menuitem id="placesContext_reload"
+ command="placesCmd_reload"
+ label="&cmd.reloadLivebookmark.label;"
+ accesskey="&cmd.reloadLivebookmark.accesskey;"
+ closemenu="single"
+ selection="livemark/feedURI"/>
+ <menuseparator id="placesContext_sortSeparator"/>
+ <menuitem id="placesContext_show:info"
+ command="placesCmd_show:info"
+ label="&cmd.properties.label;"
+ accesskey="&cmd.properties.accesskey;"
+ selection="bookmark|folder|query"
+ forcehideselection="livemarkChild"/>
+ </menupopup>
+
+</overlay>
diff --git a/comm/suite/components/places/content/sidebarUtils.js b/comm/suite/components/places/content/sidebarUtils.js
new file mode 100644
index 0000000000..b11f891187
--- /dev/null
+++ b/comm/suite/components/places/content/sidebarUtils.js
@@ -0,0 +1,105 @@
+/* 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 {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+let uidensity = window.top.document.documentElement.getAttribute("uidensity");
+if (uidensity) {
+ document.documentElement.setAttribute("uidensity", uidensity);
+}
+
+var SidebarUtils = {
+ handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) {
+ // right-clicks are not handled here
+ if (aEvent.button == 2)
+ return;
+
+ var tbo = aTree.treeBoxObject;
+ var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
+
+ if (cell.row == -1 || cell.childElt == "twisty")
+ return;
+
+ var mouseInGutter = false;
+ if (aGutterSelect) {
+ var rect = tbo.getCoordsForCellItem(cell.row, cell.col, "image");
+ // getCoordsForCellItem returns the x coordinate in logical coordinates
+ // (i.e., starting from the left and right sides in LTR and RTL modes,
+ // respectively.) Therefore, we make sure to exclude the blank area
+ // before the tree item icon (that is, to the left or right of it in
+ // LTR and RTL modes, respectively) from the click target area.
+ var isRTL = window.getComputedStyle(aTree).direction == "rtl";
+ if (isRTL)
+ mouseInGutter = aEvent.clientX > rect.x;
+ else
+ mouseInGutter = aEvent.clientX < rect.x;
+ }
+
+ var metaKey = AppConstants.platform === "macosx" ? aEvent.metaKey
+ : aEvent.ctrlKey;
+ var modifKey = metaKey || aEvent.shiftKey;
+ var isContainer = tbo.view.isContainer(cell.row);
+ var openInTabs = isContainer &&
+ (aEvent.button == 1 ||
+ (aEvent.button == 0 && modifKey)) &&
+ PlacesUtils.hasChildURIs(aTree.view.nodeForTreeIndex(cell.row));
+
+ if (aEvent.button == 0 && isContainer && !openInTabs) {
+ tbo.view.toggleOpenState(cell.row);
+ } else if (!mouseInGutter && openInTabs &&
+ aEvent.originalTarget.localName == "treechildren") {
+ tbo.view.selection.select(cell.row);
+ PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, aTree);
+ } else if (!mouseInGutter && !isContainer &&
+ aEvent.originalTarget.localName == "treechildren") {
+ // Clear all other selection since we're loading a link now. We must
+ // do this *before* attempting to load the link since openURL uses
+ // selection as an indication of which link to load.
+ tbo.view.selection.select(cell.row);
+ PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent);
+ }
+ },
+
+ handleTreeKeyPress: function SU_handleTreeKeyPress(aEvent) {
+ let node = aEvent.target.selectedNode;
+ if (node) {
+ if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
+ PlacesUIUtils.openNodeWithEvent(node, aEvent);
+ }
+ },
+
+ /**
+ * The following function displays the URL of a node that is being
+ * hovered over.
+ */
+ handleTreeMouseMove: function SU_handleTreeMouseMove(aEvent) {
+ if (aEvent.target.localName != "treechildren")
+ return;
+
+ var tree = aEvent.target.parentNode;
+ var tbo = tree.treeBoxObject;
+ var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
+
+ // cell.row is -1 when the mouse is hovering an empty area within the tree.
+ // To avoid showing a URL from a previously hovered node for a currently
+ // hovered non-url node, we must clear the moused-over URL in these cases.
+ if (cell.row != -1) {
+ var node = tree.view.nodeForTreeIndex(cell.row);
+ if (PlacesUtils.nodeIsURI(node))
+ this.setMouseoverURL(node.uri);
+ else
+ this.setMouseoverURL("");
+ } else
+ this.setMouseoverURL("");
+ },
+
+ setMouseoverURL: function SU_setMouseoverURL(aURL) {
+ // When the browser window is closed with an open sidebar, the sidebar
+ // unload event happens after the browser's one. In this case
+ // top.XULBrowserWindow has been nullified already.
+ if (top.XULBrowserWindow) {
+ top.XULBrowserWindow.setOverLink(aURL, null);
+ }
+ }
+};
diff --git a/comm/suite/components/places/content/tree.xml b/comm/suite/components/places/content/tree.xml
new file mode 100644
index 0000000000..f033f193fa
--- /dev/null
+++ b/comm/suite/components/places/content/tree.xml
@@ -0,0 +1,812 @@
+<?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/. -->
+
+<bindings id="placesTreeBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree">
+ <implementation>
+ <constructor><![CDATA[
+ // Force an initial build.
+ if (this.place)
+ this.place = this.place;
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ // Break the treeviewer->result->treeviewer cycle.
+ // Note: unsetting the result's viewer also unsets
+ // the viewer's reference to our treeBoxObject.
+ var result = this.result;
+ if (result) {
+ result.root.containerOpen = false;
+ }
+
+ // Unregister the controllber before unlinking the view, otherwise it
+ // may still try to update commands on a view with a null result.
+ if (this._controller) {
+ this._controller.terminate();
+ this.controllers.removeController(this._controller);
+ }
+
+ if (this.view) {
+ this.view.uninit();
+ }
+ this.view = null;
+ ]]></destructor>
+
+ <property name="controller"
+ readonly="true"
+ onget="return this._controller"/>
+
+ <!-- overriding -->
+ <property name="view">
+ <getter><![CDATA[
+ try {
+ return this.treeBoxObject.view.wrappedJSObject || null;
+ } catch (e) {
+ return null;
+ }
+ ]]></getter>
+ <setter><![CDATA[
+ return this.treeBoxObject.view = val;
+ ]]></setter>
+ </property>
+
+ <property name="associatedElement"
+ readonly="true"
+ onget="return this"/>
+
+ <method name="applyFilter">
+ <parameter name="filterString"/>
+ <parameter name="folderRestrict"/>
+ <parameter name="includeHidden"/>
+ <body><![CDATA[
+ // preserve grouping
+ var queryNode = PlacesUtils.asQuery(this.result.root);
+ var options = queryNode.queryOptions.clone();
+
+ // Make sure we're getting uri results.
+ // We do not yet support searching into grouped queries or into
+ // tag containers, so we must fall to the default case.
+ if (PlacesUtils.nodeIsHistoryContainer(queryNode) ||
+ options.resultType == options.RESULTS_AS_TAG_QUERY ||
+ options.resultType == options.RESULTS_AS_TAG_CONTENTS ||
+ options.resultType == options.RESULTS_AS_ROOTS_QUERY)
+ options.resultType = options.RESULTS_AS_URI;
+
+ var query = PlacesUtils.history.getNewQuery();
+ query.searchTerms = filterString;
+
+ if (folderRestrict) {
+ query.setFolders(folderRestrict, folderRestrict.length);
+ options.queryType = options.QUERY_TYPE_BOOKMARKS;
+ }
+
+ options.includeHidden = !!includeHidden;
+
+ this.load([query], options);
+ ]]></body>
+ </method>
+
+ <method name="load">
+ <parameter name="queries"/>
+ <parameter name="options"/>
+ <body><![CDATA[
+ let result = PlacesUtils.history
+ .executeQueries(queries, queries.length,
+ options);
+ let callback;
+ if (this.flatList) {
+ let onOpenFlatContainer = this.onOpenFlatContainer;
+ if (onOpenFlatContainer)
+ callback = new Function("aContainer", onOpenFlatContainer);
+ }
+
+ if (!this._controller) {
+ this._controller = new PlacesController(this);
+ this.controllers.appendController(this._controller);
+ }
+
+ let treeView = new PlacesTreeView(this.flatList, callback, this._controller);
+
+ // Observer removal is done within the view itself. When the tree
+ // goes away, treeboxobject calls view.setTree(null), which then
+ // calls removeObserver.
+ result.addObserver(treeView);
+ this.view = treeView;
+
+ if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) {
+ treeView.selection.select(0);
+ }
+
+ this._cachedInsertionPoint = undefined;
+ ]]></body>
+ </method>
+
+ <property name="flatList">
+ <getter><![CDATA[
+ return this.getAttribute("flatList") == "true";
+ ]]></getter>
+ <setter><![CDATA[
+ if (this.flatList != val) {
+ this.setAttribute("flatList", val);
+ // reload with the last place set
+ if (this.place)
+ this.place = this.place;
+ }
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="onOpenFlatContainer">
+ <getter><![CDATA[
+ return this.getAttribute("onopenflatcontainer");
+ ]]></getter>
+ <setter><![CDATA[
+ if (this.onOpenFlatContainer != val) {
+ this.setAttribute("onopenflatcontainer", val);
+ // reload with the last place set
+ if (this.place)
+ this.place = this.place;
+ }
+ return val;
+ ]]></setter>
+ </property>
+
+ <!--
+ Causes a particular node represented by the specified placeURI to be
+ selected in the tree. All containers above the node in the hierarchy
+ will be opened, so that the node is visible.
+ -->
+ <method name="selectPlaceURI">
+ <parameter name="placeURI"/>
+ <body><![CDATA[
+ // Do nothing if a node matching the given uri is already selected
+ if (this.hasSelection && this.selectedNode.uri == placeURI)
+ return;
+
+ function findNode(container, nodesURIChecked) {
+ var containerURI = container.uri;
+ if (containerURI == placeURI)
+ return container;
+ if (nodesURIChecked.includes(containerURI))
+ return null;
+
+ // never check the contents of the same query
+ nodesURIChecked.push(containerURI);
+
+ var wasOpen = container.containerOpen;
+ if (!wasOpen)
+ container.containerOpen = true;
+ for (var i = 0; i < container.childCount; ++i) {
+ var child = container.getChild(i);
+ var childURI = child.uri;
+ if (childURI == placeURI)
+ return child;
+ else if (PlacesUtils.nodeIsContainer(child)) {
+ var nested = findNode(PlacesUtils.asContainer(child), nodesURIChecked);
+ if (nested)
+ return nested;
+ }
+ }
+
+ if (!wasOpen)
+ container.containerOpen = false;
+
+ return null;
+ }
+
+ var container = this.result.root;
+ console.assert(container, "No result, cannot select place URI!");
+ if (!container)
+ return;
+
+ var child = findNode(container, []);
+ if (child)
+ this.selectNode(child);
+ else {
+ // If the specified child could not be located, clear the selection
+ var selection = this.view.selection;
+ selection.clearSelection();
+ }
+ ]]></body>
+ </method>
+
+ <!--
+ Causes a particular node to be selected in the tree, resulting in all
+ containers above the node in the hierarchy to be opened, so that the
+ node is visible.
+ -->
+ <method name="selectNode">
+ <parameter name="node"/>
+ <body><![CDATA[
+ var view = this.view;
+
+ var parent = node.parent;
+ if (parent && !parent.containerOpen) {
+ // Build a list of all of the nodes that are the parent of this one
+ // in the result.
+ var parents = [];
+ var root = this.result.root;
+ while (parent && parent != root) {
+ parents.push(parent);
+ parent = parent.parent;
+ }
+
+ // Walk the list backwards (opening from the root of the hierarchy)
+ // opening each folder as we go.
+ for (var i = parents.length - 1; i >= 0; --i) {
+ let index = view.treeIndexForNode(parents[i]);
+ if (index != -1 &&
+ view.isContainer(index) && !view.isContainerOpen(index))
+ view.toggleOpenState(index);
+ }
+ // Select the specified node...
+ }
+
+ let index = view.treeIndexForNode(node);
+ if (index == -1)
+ return;
+
+ view.selection.select(index);
+ // ... and ensure it's visible, not scrolled off somewhere.
+ this.treeBoxObject.ensureRowIsVisible(index);
+ ]]></body>
+ </method>
+
+ <!-- nsIPlacesView -->
+ <property name="result">
+ <getter><![CDATA[
+ try {
+ return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
+ } catch (e) {
+ return null;
+ }
+ ]]></getter>
+ </property>
+
+ <!-- nsIPlacesView -->
+ <property name="place">
+ <getter><![CDATA[
+ return this.getAttribute("place");
+ ]]></getter>
+ <setter><![CDATA[
+ this.setAttribute("place", val);
+
+ var queriesRef = { };
+ var queryCountRef = { };
+ var optionsRef = { };
+ PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
+ if (queryCountRef.value == 0)
+ queriesRef.value = [PlacesUtils.history.getNewQuery()];
+ if (!optionsRef.value)
+ optionsRef.value = PlacesUtils.history.getNewQueryOptions();
+
+ this.load(queriesRef.value, optionsRef.value);
+
+ return val;
+ ]]></setter>
+ </property>
+
+ <!-- nsIPlacesView -->
+ <property name="hasSelection">
+ <getter><![CDATA[
+ return this.view && this.view.selection.count >= 1;
+ ]]></getter>
+ </property>
+
+ <!-- nsIPlacesView -->
+ <property name="selectedNodes">
+ <getter><![CDATA[
+ let nodes = [];
+ if (!this.hasSelection)
+ return nodes;
+
+ let selection = this.view.selection;
+ let rc = selection.getRangeCount();
+ let resultview = this.view;
+ for (let i = 0; i < rc; ++i) {
+ let min = { }, max = { };
+ selection.getRangeAt(i, min, max);
+ for (let j = min.value; j <= max.value; ++j) {
+ nodes.push(resultview.nodeForTreeIndex(j));
+ }
+ }
+ return nodes;
+ ]]></getter>
+ </property>
+
+ <method name="toggleCutNode">
+ <parameter name="aNode"/>
+ <parameter name="aValue"/>
+ <body><![CDATA[
+ this.view.toggleCutNode(aNode, aValue);
+ ]]></body>
+ </method>
+
+ <!-- nsIPlacesView -->
+ <property name="removableSelectionRanges">
+ <getter><![CDATA[
+ // This property exists in addition to selectedNodes because it
+ // encodes selection ranges (which only occur in list views) into
+ // the return value. For each removed range, the index at which items
+ // will be re-inserted upon the remove transaction being performed is
+ // the first index of the range, so that the view updates correctly.
+ //
+ // For example, if we remove rows 2,3,4 and 7,8 from a list, when we
+ // undo that operation, if we insert what was at row 3 at row 3 again,
+ // it will show up _after_ the item that was at row 5. So we need to
+ // insert all items at row 2, and the tree view will update correctly.
+ //
+ // Also, this function collapses the selection to remove redundant
+ // data, e.g. when deleting this selection:
+ //
+ // http://www.foo.com/
+ // (-) Some Folder
+ // http://www.bar.com/
+ //
+ // ... returning http://www.bar.com/ as part of the selection is
+ // redundant because it is implied by removing "Some Folder". We
+ // filter out all such redundancies since some partial amount of
+ // the folder's children may be selected.
+ //
+ let nodes = [];
+ if (!this.hasSelection)
+ return nodes;
+
+ var selection = this.view.selection;
+ var rc = selection.getRangeCount();
+ var resultview = this.view;
+ // This list is kept independently of the range selected (i.e. OUTSIDE
+ // the for loop) since the row index of a container is unique for the
+ // entire view, and we could have some really wacky selection and we
+ // don't want to blow up.
+ var containers = { };
+ for (var i = 0; i < rc; ++i) {
+ var range = [];
+ var min = { }, max = { };
+ selection.getRangeAt(i, min, max);
+
+ for (var j = min.value; j <= max.value; ++j) {
+ if (this.view.isContainer(j))
+ containers[j] = true;
+ if (!(this.view.getParentIndex(j) in containers))
+ range.push(resultview.nodeForTreeIndex(j));
+ }
+ nodes.push(range);
+ }
+ return nodes;
+ ]]></getter>
+ </property>
+
+ <!-- nsIPlacesView -->
+ <property name="draggableSelection"
+ onget="return this.selectedNodes"/>
+
+ <!-- nsIPlacesView -->
+ <property name="selectedNode">
+ <getter><![CDATA[
+ var view = this.view;
+ if (!view || view.selection.count != 1)
+ return null;
+
+ var selection = view.selection;
+ var min = { }, max = { };
+ selection.getRangeAt(0, min, max);
+
+ return this.view.nodeForTreeIndex(min.value);
+ ]]></getter>
+ </property>
+
+ <!-- nsIPlacesView -->
+ <property name="insertionPoint">
+ <getter><![CDATA[
+ // invalidated on selection and focus changes
+ if (this._cachedInsertionPoint !== undefined)
+ return this._cachedInsertionPoint;
+
+ // there is no insertion point for history queries
+ // so bail out now and save a lot of work when updating commands
+ var resultNode = this.result.root;
+ if (PlacesUtils.nodeIsQuery(resultNode) &&
+ PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
+ Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
+ return this._cachedInsertionPoint = null;
+
+ var orientation = Ci.nsITreeView.DROP_BEFORE;
+ // If there is no selection, insert at the end of the container.
+ if (!this.hasSelection) {
+ var index = this.view.rowCount - 1;
+ this._cachedInsertionPoint =
+ this._getInsertionPoint(index, orientation);
+ return this._cachedInsertionPoint;
+ }
+
+ // This is a two-part process. The first part is determining the drop
+ // orientation.
+ // * The default orientation is to drop _before_ the selected item.
+ // * If the selected item is a container, the default orientation
+ // is to drop _into_ that container.
+ //
+ // Warning: It may be tempting to use tree indexes in this code, but
+ // you must not, since the tree is nested and as your tree
+ // index may change when folders before you are opened and
+ // closed. You must convert your tree index to a node, and
+ // then use getChildIndex to find your absolute index in
+ // the parent container instead.
+ //
+ var resultView = this.view;
+ var selection = resultView.selection;
+ var rc = selection.getRangeCount();
+ var min = { }, max = { };
+ selection.getRangeAt(rc - 1, min, max);
+
+ // If the sole selection is a container, and we are not in
+ // a flatlist, insert into it.
+ // Note that this only applies to _single_ selections,
+ // if the last element within a multi-selection is a
+ // container, insert _adjacent_ to the selection.
+ //
+ // If the sole selection is the bookmarks toolbar folder, we insert
+ // into it even if it is not opened
+ if (selection.count == 1 && resultView.isContainer(max.value) &&
+ !this.flatList)
+ orientation = Ci.nsITreeView.DROP_ON;
+
+ this._cachedInsertionPoint =
+ this._getInsertionPoint(max.value, orientation);
+ return this._cachedInsertionPoint;
+ ]]></getter>
+ </property>
+
+ <method name="_getInsertionPoint">
+ <parameter name="index"/>
+ <parameter name="orientation"/>
+ <body><![CDATA[
+ var result = this.result;
+ var resultview = this.view;
+ var container = result.root;
+ var dropNearNode = null;
+ console.assert(container, "null container");
+ // When there's no selection, assume the container is the container
+ // the view is populated from (i.e. the result's itemId).
+ if (index != -1) {
+ var lastSelected = resultview.nodeForTreeIndex(index);
+ if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
+ // If the last selected item is an open container, append _into_
+ // it, rather than insert adjacent to it.
+ container = lastSelected;
+ index = -1;
+ } else if (lastSelected.containerOpen &&
+ orientation == Ci.nsITreeView.DROP_AFTER &&
+ lastSelected.hasChildren) {
+ // If the last selected item is an open container and the user is
+ // trying to drag into it as a first item, really insert into it.
+ container = lastSelected;
+ orientation = Ci.nsITreeView.DROP_ON;
+ index = 0;
+ } else {
+ // Use the last-selected node's container.
+ container = lastSelected.parent;
+
+ // See comment in the treeView.js's copy of this method
+ if (!container || !container.containerOpen)
+ return null;
+
+ // Avoid the potentially expensive call to getChildIndex
+ // if we know this container doesn't allow insertion
+ if (this.controller.disallowInsertion(container))
+ return null;
+
+ var queryOptions = PlacesUtils.asQuery(result.root).queryOptions;
+ if (queryOptions.sortingMode !=
+ Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
+ // If we are within a sorted view, insert at the end
+ index = -1;
+ } else if (queryOptions.excludeItems ||
+ queryOptions.excludeQueries ||
+ queryOptions.excludeReadOnlyFolders) {
+ // Some item may be invisible, insert near last selected one.
+ // We don't replace index here to avoid requests to the db,
+ // instead it will be calculated later by the controller.
+ index = -1;
+ dropNearNode = lastSelected;
+ } else {
+ var lsi = container.getChildIndex(lastSelected);
+ index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
+ }
+ }
+ }
+
+ if (this.controller.disallowInsertion(container))
+ return null;
+
+ // TODO (Bug 1160193): properly support dropping on a tag root.
+ let tagName = null;
+ if (PlacesUtils.nodeIsTagQuery(container)) {
+ tagName = container.title;
+ if (!tagName)
+ return null;
+ }
+
+ return new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(container),
+ parentGuid: PlacesUtils.getConcreteItemGuid(container),
+ index, orientation, tagName, dropNearNode
+ });
+ ]]></body>
+ </method>
+
+ <!-- nsIPlacesView -->
+ <method name="selectAll">
+ <body><![CDATA[
+ this.view.selection.selectAll();
+ ]]></body>
+ </method>
+
+ <!-- This method will select the first node in the tree that matches
+ each given item guid. It will open any folder nodes that it needs
+ to in order to show the selected items.
+ Note: An array of ids or guids (or a mixture) may be passed as aIDs.
+ Passing IDs should be considered deprecated.
+ -->
+ <method name="selectItems">
+ <parameter name="aIDs"/>
+ <parameter name="aOpenContainers"/>
+ <body><![CDATA[
+ // Never open containers in flat lists.
+ if (this.flatList)
+ aOpenContainers = false;
+ // By default, we do search and select within containers which were
+ // closed (note that containers in which nodes were not found are
+ // closed).
+ if (aOpenContainers === undefined)
+ aOpenContainers = true;
+
+ var ids = aIDs; // don't manipulate the caller's array
+
+ // Array of nodes found by findNodes which are to be selected
+ var nodes = [];
+
+ // Array of nodes found by findNodes which should be opened
+ var nodesToOpen = [];
+
+ // A set of GUIDs of container-nodes that were previously searched,
+ // and thus shouldn't be searched again. This is empty at the initial
+ // start of the recursion and gets filled in as the recursion
+ // progresses.
+ var checkedGuidsSet = new Set();
+
+ /**
+ * Recursively search through a node's children for items
+ * with the given IDs. When a matching item is found, remove its ID
+ * from the IDs array, and add the found node to the nodes dictionary.
+ *
+ * NOTE: This method will leave open any node that had matching items
+ * in its subtree.
+ */
+ function findNodes(node) {
+ var foundOne = false;
+ // See if node matches an ID we wanted; add to results.
+ // For simple folder queries, check both itemId and the concrete
+ // item id.
+ var index = ids.indexOf(node.itemId);
+ if (index == -1 &&
+ node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
+ index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId);
+
+ if (index == -1) {
+ index = ids.indexOf(node.bookmarkGuid);
+ if (index == -1) {
+ let concreteGuid = PlacesUtils.getConcreteItemGuid(node);
+ if (concreteGuid != node.bookmarkGuid) {
+ index = ids.indexOf(concreteGuid);
+ }
+ }
+ }
+
+ if (index != -1) {
+ nodes.push(node);
+ foundOne = true;
+ ids.splice(index, 1);
+ }
+
+ var concreteGuid = PlacesUtils.getConcreteItemGuid(node);
+ if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
+ checkedGuidsSet.has(concreteGuid))
+ return foundOne;
+
+ // Only follow a query if it has been been explicitly opened by the
+ // caller. We support the "AllBookmarks" case to allow callers to
+ // specify just the top-level bookmark folders.
+ let shouldOpen = aOpenContainers && (PlacesUtils.nodeIsFolder(node) ||
+ (PlacesUtils.nodeIsQuery(node) && node.itemId == PlacesUIUtils.leftPaneQueries.AllBookmarks));
+
+ PlacesUtils.asContainer(node);
+ if (!node.containerOpen && !shouldOpen)
+ return foundOne;
+
+ checkedGuidsSet.add(concreteGuid);
+
+ // Remember the beginning state so that we can re-close
+ // this node if we don't find any additional results here.
+ var previousOpenness = node.containerOpen;
+ node.containerOpen = true;
+ for (var child = 0; child < node.childCount && ids.length > 0; child++) {
+ var childNode = node.getChild(child);
+ var found = findNodes(childNode);
+ if (!foundOne)
+ foundOne = found;
+ }
+
+ // If we didn't find any additional matches in this node's
+ // subtree, revert the node to its previous openness.
+ if (foundOne)
+ nodesToOpen.unshift(node);
+ node.containerOpen = previousOpenness;
+ return foundOne;
+ }
+
+ // Disable notifications while looking for nodes.
+ let result = this.result;
+ let didSuppressNotifications = result.suppressNotifications;
+ if (!didSuppressNotifications)
+ result.suppressNotifications = true;
+ try {
+ findNodes(this.result.root);
+ } finally {
+ if (!didSuppressNotifications)
+ result.suppressNotifications = false;
+ }
+
+ // For all the nodes we've found, highlight the corresponding
+ // index in the tree.
+ var resultview = this.view;
+ var selection = this.view.selection;
+ selection.selectEventsSuppressed = true;
+ selection.clearSelection();
+ // Open nodes containing found items
+ for (let i = 0; i < nodesToOpen.length; i++) {
+ nodesToOpen[i].containerOpen = true;
+ }
+ for (let i = 0; i < nodes.length; i++) {
+ var index = resultview.treeIndexForNode(nodes[i]);
+ if (index == -1)
+ continue;
+ selection.rangedSelect(index, index, true);
+ }
+ selection.selectEventsSuppressed = false;
+ ]]></body>
+ </method>
+
+ <field name="_contextMenuShown">false</field>
+
+ <method name="buildContextMenu">
+ <parameter name="aPopup"/>
+ <body><![CDATA[
+ this._contextMenuShown = true;
+ return this.controller.buildContextMenu(aPopup);
+ ]]></body>
+ </method>
+
+ <method name="destroyContextMenu">
+ <parameter name="aPopup"/>
+ this._contextMenuShown = false;
+ <body/>
+ </method>
+
+ <property name="ownerWindow"
+ readonly="true"
+ onget="return window;"/>
+
+ <field name="_active">true</field>
+ <property name="active"
+ onget="return this._active"
+ onset="return this._active = val"/>
+
+ </implementation>
+ <handlers>
+ <handler event="focus"><![CDATA[
+ this._cachedInsertionPoint = undefined;
+
+ // See select handler. We need the sidebar's places commandset to be
+ // updated as well
+ document.commandDispatcher.updateCommands("focus");
+ ]]></handler>
+ <handler event="select"><![CDATA[
+ this._cachedInsertionPoint = undefined;
+
+ // This additional complexity is here for the sidebars
+ var win = window;
+ while (true) {
+ win.document.commandDispatcher.updateCommands("focus");
+ if (win == window.top)
+ break;
+
+ win = win.parent;
+ }
+ ]]></handler>
+
+ <handler event="dragstart"><![CDATA[
+ if (event.target.localName != "treechildren")
+ return;
+
+ let nodes = this.selectedNodes;
+ for (let i = 0; i < nodes.length; i++) {
+ let node = nodes[i];
+
+ // Disallow dragging the root node of a tree.
+ if (!node.parent) {
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ // If this node is child of a readonly container (e.g. a livemark)
+ // or cannot be moved, we must force a copy.
+ if (!this.controller.canMoveNode(node)) {
+ event.dataTransfer.effectAllowed = "copyLink";
+ break;
+ }
+ }
+
+ this._controller.setDataTransfer(event);
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragover"><![CDATA[
+ if (event.target.localName != "treechildren")
+ return;
+
+ let cell = this.treeBoxObject.getCellAt(event.clientX, event.clientY);
+ let node = cell.row != -1 ?
+ this.view.nodeForTreeIndex(cell.row) :
+ this.result.root;
+ // cache the dropTarget for the view
+ PlacesControllerDragHelper.currentDropTarget = node;
+
+ // We have to calculate the orientation since view.canDrop will use
+ // it and we want to be consistent with the dropfeedback.
+ let tbo = this.treeBoxObject;
+ let rowHeight = tbo.rowHeight;
+ let eventY = event.clientY - tbo.treeBody.boxObject.y -
+ rowHeight * (cell.row - tbo.getFirstVisibleRow());
+
+ let orientation = Ci.nsITreeView.DROP_BEFORE;
+
+ if (cell.row == -1) {
+ // If the row is not valid we try to insert inside the resultNode.
+ orientation = Ci.nsITreeView.DROP_ON;
+ } else if (PlacesUtils.nodeIsContainer(node) &&
+ eventY > rowHeight * 0.75) {
+ // If we are below the 75% of a container the treeview we try
+ // to drop after the node.
+ orientation = Ci.nsITreeView.DROP_AFTER;
+ } else if (PlacesUtils.nodeIsContainer(node) &&
+ eventY > rowHeight * 0.25) {
+ // If we are below the 25% of a container the treeview we try
+ // to drop inside the node.
+ orientation = Ci.nsITreeView.DROP_ON;
+ }
+
+ if (!this.view.canDrop(cell.row, orientation, event.dataTransfer))
+ return;
+
+ event.preventDefault();
+ event.stopPropagation();
+ ]]></handler>
+
+ <handler event="dragend"><![CDATA[
+ PlacesControllerDragHelper.currentDropTarget = null;
+ ]]></handler>
+
+ </handlers>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/places/content/treeView.js b/comm/suite/components/places/content/treeView.js
new file mode 100644
index 0000000000..209af6549a
--- /dev/null
+++ b/comm/suite/components/places/content/treeView.js
@@ -0,0 +1,1823 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PTV_interfaces = [Ci.nsITreeView,
+ Ci.nsINavHistoryResultObserver,
+ Ci.nsISupportsWeakReference];
+
+/**
+ * This returns the key for any node/details object.
+ *
+ * @param nodeOrDetails
+ * A node, or an object containing the following properties:
+ * - uri
+ * - time
+ * - itemId
+ * In case any of these is missing, an empty string will be returned. This is
+ * to facilitate easy delete statements which occur due to assignment to items in `this._rows`,
+ * since the item we are deleting may be undefined in the array.
+ *
+ * @return key or empty string.
+ */
+function makeNodeDetailsKey(nodeOrDetails) {
+ if (nodeOrDetails &&
+ typeof nodeOrDetails === "object" &&
+ "uri" in nodeOrDetails &&
+ "time" in nodeOrDetails &&
+ "itemId" in nodeOrDetails) {
+ return `${nodeOrDetails.uri}*${nodeOrDetails.time}*${nodeOrDetails.itemId}`;
+ }
+ return "";
+}
+
+function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
+ this._tree = null;
+ this._result = null;
+ this._selection = null;
+ this._rootNode = null;
+ this._rows = [];
+ this._flatList = aFlatList;
+ this._nodeDetails = new Map();
+ this._openContainerCallback = aOnOpenFlatContainer;
+ this._controller = aController;
+}
+
+PlacesTreeView.prototype = {
+ get wrappedJSObject() {
+ return this;
+ },
+
+ __xulStore: null,
+ get _xulStore() {
+ if (!this.__xulStore) {
+ this.__xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
+ }
+ return this.__xulStore;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI(PTV_interfaces),
+
+ // Bug 761494:
+ // ----------
+ // Some addons use methods from nsINavHistoryResultObserver and
+ // nsINavHistoryResultTreeViewer, without QIing to these interfaces first.
+ // That's not a problem when the view is retrieved through the
+ // <tree>.view getter (which returns the wrappedJSObject of this object),
+ // it raises an issue when the view retrieved through the treeBoxObject.view
+ // getter. Thus, to avoid breaking addons, the interfaces are prefetched.
+ classInfo: XPCOMUtils.generateCI({ interfaces: PTV_interfaces }),
+
+ /**
+ * This is called once both the result and the tree are set.
+ */
+ _finishInit: function PTV__finishInit() {
+ let selection = this.selection;
+ if (selection)
+ selection.selectEventsSuppressed = true;
+
+ if (!this._rootNode.containerOpen) {
+ // This triggers containerStateChanged which then builds the visible
+ // section.
+ this._rootNode.containerOpen = true;
+ } else
+ this.invalidateContainer(this._rootNode);
+
+ // "Activate" the sorting column and update commands.
+ this.sortingChanged(this._result.sortingMode);
+
+ if (selection)
+ selection.selectEventsSuppressed = false;
+ },
+
+ uninit() {
+ if (this._editingObservers) {
+ for (let observer of this._editingObservers.values()) {
+ observer.disconnect();
+ }
+ delete this._editingObservers;
+ }
+ },
+
+ /**
+ * Plain Container: container result nodes which may never include sub
+ * hierarchies.
+ *
+ * When the rows array is constructed, we don't set the children of plain
+ * containers. Instead, we keep placeholders for these children. We then
+ * build these children lazily as the tree asks us for information about each
+ * row. Luckily, the tree doesn't ask about rows outside the visible area.
+ *
+ * @see _getNodeForRow and _getRowForNode for the actual magic.
+ *
+ * @note It's guaranteed that all containers are listed in the rows
+ * elements array. It's also guaranteed that separators (if they're not
+ * filtered, see below) are listed in the visible elements array, because
+ * bookmark folders are never built lazily, as described above.
+ *
+ * @param aContainer
+ * A container result node.
+ *
+ * @return true if aContainer is a plain container, false otherwise.
+ */
+ _isPlainContainer: function PTV__isPlainContainer(aContainer) {
+ // Livemarks are always plain containers.
+ if (this._controller.hasCachedLivemarkInfo(aContainer))
+ return true;
+
+ // We don't know enough about non-query containers.
+ if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode))
+ return false;
+
+ switch (aContainer.queryOptions.resultType) {
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY:
+ case Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY:
+ return false;
+ }
+
+ // If it's a folder, it's not a plain container.
+ let nodeType = aContainer.type;
+ return nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER &&
+ nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
+ },
+
+ /**
+ * Gets the row number for a given node. Assumes that the given node is
+ * visible (i.e. it's not an obsolete node).
+ *
+ * @param aNode
+ * A result node. Do not pass an obsolete node, or any
+ * node which isn't supposed to be in the tree (e.g. separators in
+ * sorted trees).
+ * @param [optional] aForceBuild
+ * @see _isPlainContainer.
+ * If true, the row will be computed even if the node still isn't set
+ * in our rows array.
+ * @param [optional] aParentRow
+ * The row of aNode's parent. Ignored for the root node.
+ * @param [optional] aNodeIndex
+ * The index of aNode in its parent. Only used if aParentRow is
+ * set too.
+ *
+ * @throws if aNode is invisible.
+ * @note If aParentRow and aNodeIndex are passed and parent is a plain
+ * container, this method will just return a calculated row value, without
+ * making assumptions on existence of the node at that position.
+ * @return aNode's row if it's in the rows list or if aForceBuild is set, -1
+ * otherwise.
+ */
+ _getRowForNode:
+ function PTV__getRowForNode(aNode, aForceBuild, aParentRow, aNodeIndex) {
+ if (aNode == this._rootNode)
+ throw new Error("The root node is never visible");
+
+ // A node is removed form the view either if it has no parent or if its
+ // root-ancestor is not the root node (in which case that's the node
+ // for which nodeRemoved was called).
+ let ancestors = Array.from(PlacesUtils.nodeAncestors(aNode));
+ if (ancestors.length == 0 ||
+ ancestors[ancestors.length - 1] != this._rootNode) {
+ throw new Error("Removed node passed to _getRowForNode");
+ }
+
+ // Ensure that the entire chain is open, otherwise that node is invisible.
+ for (let ancestor of ancestors) {
+ if (!ancestor.containerOpen)
+ throw new Error("Invisible node passed to _getRowForNode");
+ }
+
+ // Non-plain containers are initially built with their contents.
+ let parent = aNode.parent;
+ let parentIsPlain = this._isPlainContainer(parent);
+ if (!parentIsPlain) {
+ if (parent == this._rootNode) {
+ return this._rows.indexOf(aNode);
+ }
+
+ return this._rows.indexOf(aNode, aParentRow);
+ }
+
+ let row = -1;
+ let useNodeIndex = typeof(aNodeIndex) == "number";
+ if (parent == this._rootNode) {
+ row = useNodeIndex ? aNodeIndex : this._rootNode.getChildIndex(aNode);
+ } else if (useNodeIndex && typeof(aParentRow) == "number") {
+ // If we have both the row of the parent node, and the node's index, we
+ // can avoid searching the rows array if the parent is a plain container.
+ row = aParentRow + aNodeIndex + 1;
+ } else {
+ // Look for the node in the nodes array. Start the search at the parent
+ // row. If the parent row isn't passed, we'll pass undefined to indexOf,
+ // which is fine.
+ row = this._rows.indexOf(aNode, aParentRow);
+ if (row == -1 && aForceBuild) {
+ let parentRow = typeof(aParentRow) == "number" ? aParentRow
+ : this._getRowForNode(parent);
+ row = parentRow + parent.getChildIndex(aNode) + 1;
+ }
+ }
+
+ if (row != -1) {
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
+ this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
+ this._rows[row] = aNode;
+ }
+
+ return row;
+ },
+
+ /**
+ * Given a row, finds and returns the parent details of the associated node.
+ *
+ * @param aChildRow
+ * Row number.
+ * @return [parentNode, parentRow]
+ */
+ _getParentByChildRow: function PTV__getParentByChildRow(aChildRow) {
+ let node = this._getNodeForRow(aChildRow);
+ let parent = (node === null) ? this._rootNode : node.parent;
+
+ // The root node is never visible
+ if (parent == this._rootNode)
+ return [this._rootNode, -1];
+
+ let parentRow = this._rows.lastIndexOf(parent, aChildRow - 1);
+ return [parent, parentRow];
+ },
+
+ /**
+ * Gets the node at a given row.
+ */
+ _getNodeForRow: function PTV__getNodeForRow(aRow) {
+ if (aRow < 0) {
+ return null;
+ }
+
+ let node = this._rows[aRow];
+ if (node !== undefined)
+ return node;
+
+ // Find the nearest node.
+ let rowNode, row;
+ for (let i = aRow - 1; i >= 0 && rowNode === undefined; i--) {
+ rowNode = this._rows[i];
+ row = i;
+ }
+
+ // If there's no container prior to the given row, it's a child of
+ // the root node (remember: all containers are listed in the rows array).
+ if (!rowNode) {
+ let newNode = this._rootNode.getChild(aRow);
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
+ this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
+ return this._rows[aRow] = newNode;
+ }
+
+ // Unset elements may exist only in plain containers. Thus, if the nearest
+ // node is a container, it's the row's parent, otherwise, it's a sibling.
+ if (rowNode instanceof Ci.nsINavHistoryContainerResultNode) {
+ let newNode = rowNode.getChild(aRow - row - 1);
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
+ this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
+ return this._rows[aRow] = newNode;
+ }
+
+ let [parent, parentRow] = this._getParentByChildRow(row);
+ let newNode = parent.getChild(aRow - parentRow - 1);
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
+ this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
+ return this._rows[aRow] = newNode;
+ },
+
+ /**
+ * This takes a container and recursively appends our rows array per its
+ * contents. Assumes that the rows arrays has no rows for the given
+ * container.
+ *
+ * @param [in] aContainer
+ * A container result node.
+ * @param [in] aFirstChildRow
+ * The first row at which nodes may be inserted to the row array.
+ * In other words, that's aContainer's row + 1.
+ * @param [out] aToOpen
+ * An array of containers to open once the build is done.
+ *
+ * @return the number of rows which were inserted.
+ */
+ _buildVisibleSection:
+ function PTV__buildVisibleSection(aContainer, aFirstChildRow, aToOpen) {
+ // There's nothing to do if the container is closed.
+ if (!aContainer.containerOpen)
+ return 0;
+
+ // Inserting the new elements into the rows array in one shot (by
+ // Array.prototype.concat) is faster than resizing the array (by splice) on each loop
+ // iteration.
+ let cc = aContainer.childCount;
+ let newElements = new Array(cc);
+ // We need to clean up the node details from aFirstChildRow + 1 to the end of rows.
+ for (let i = aFirstChildRow + 1; i < this._rows.length; i++) {
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[i]));
+ }
+ this._rows = this._rows.splice(0, aFirstChildRow)
+ .concat(newElements, this._rows);
+
+ if (this._isPlainContainer(aContainer))
+ return cc;
+
+ let sortingMode = this._result.sortingMode;
+
+ let rowsInserted = 0;
+ for (let i = 0; i < cc; i++) {
+ let curChild = aContainer.getChild(i);
+ let curChildType = curChild.type;
+
+ let row = aFirstChildRow + rowsInserted;
+
+ // Don't display separators when sorted.
+ if (curChildType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
+ if (sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
+ // Remove the element for the filtered separator.
+ // Notice that the rows array was initially resized to include all
+ // children.
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
+ this._rows.splice(row, 1);
+ continue;
+ }
+ }
+
+ this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
+ this._nodeDetails.set(makeNodeDetailsKey(curChild), curChild);
+ this._rows[row] = curChild;
+ rowsInserted++;
+
+ // Recursively do containers.
+ if (!this._flatList &&
+ curChild instanceof Ci.nsINavHistoryContainerResultNode &&
+ !this._controller.hasCachedLivemarkInfo(curChild)) {
+ let uri = curChild.uri;
+ let isopen = false;
+
+ if (uri) {
+ let val = this._xulStore.getValue(document.documentURI, uri, "open");
+ isopen = (val == "true");
+ }
+
+ if (isopen != curChild.containerOpen)
+ aToOpen.push(curChild);
+ else if (curChild.containerOpen && curChild.childCount > 0)
+ rowsInserted += this._buildVisibleSection(curChild, row + 1, aToOpen);
+ }
+ }
+
+ return rowsInserted;
+ },
+
+ /**
+ * This counts how many rows a node takes in the tree. For containers it
+ * will count the node itself plus any child node following it.
+ */
+ _countVisibleRowsForNodeAtRow:
+ function PTV__countVisibleRowsForNodeAtRow(aNodeRow) {
+ let node = this._rows[aNodeRow];
+
+ // If it's not listed yet, we know that it's a leaf node (instanceof also
+ // null-checks).
+ if (!(node instanceof Ci.nsINavHistoryContainerResultNode))
+ return 1;
+
+ let outerLevel = node.indentLevel;
+ for (let i = aNodeRow + 1; i < this._rows.length; i++) {
+ let rowNode = this._rows[i];
+ if (rowNode && rowNode.indentLevel <= outerLevel)
+ return i - aNodeRow;
+ }
+
+ // This node plus its children take up the bottom of the list.
+ return this._rows.length - aNodeRow;
+ },
+
+ _getSelectedNodesInRange:
+ function PTV__getSelectedNodesInRange(aFirstRow, aLastRow) {
+ let selection = this.selection;
+ let rc = selection.getRangeCount();
+ if (rc == 0)
+ return [];
+
+ // The visible-area borders are needed for checking whether a
+ // selected row is also visible.
+ let firstVisibleRow = this._tree.getFirstVisibleRow();
+ let lastVisibleRow = this._tree.getLastVisibleRow();
+
+ let nodesInfo = [];
+ for (let rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
+ let min = { }, max = { };
+ selection.getRangeAt(rangeIndex, min, max);
+
+ // If this range does not overlap the replaced chunk, we don't need to
+ // persist the selection.
+ if (max.value < aFirstRow || min.value > aLastRow)
+ continue;
+
+ let firstRow = Math.max(min.value, aFirstRow);
+ let lastRow = Math.min(max.value, aLastRow);
+ for (let i = firstRow; i <= lastRow; i++) {
+ nodesInfo.push({
+ node: this._rows[i],
+ oldRow: i,
+ wasVisible: i >= firstVisibleRow && i <= lastVisibleRow
+ });
+ }
+ }
+
+ return nodesInfo;
+ },
+
+ /**
+ * Tries to find an equivalent node for a node which was removed. We first
+ * look for the original node, in case it was just relocated. Then, if we
+ * that node was not found, we look for a node that has the same itemId, uri
+ * and time values.
+ *
+ * @param aUpdatedContainer
+ * An ancestor of the node which was removed. It does not have to be
+ * its direct parent.
+ * @param aOldNode
+ * The node which was removed.
+ *
+ * @return the row number of an equivalent node for aOldOne, if one was
+ * found, -1 otherwise.
+ */
+ _getNewRowForRemovedNode:
+ function PTV__getNewRowForRemovedNode(aUpdatedContainer, aOldNode) {
+ let parent = aOldNode.parent;
+ if (parent) {
+ // If the node's parent is still set, the node is not obsolete
+ // and we should just find out its new position.
+ // However, if any of the node's ancestor is closed, the node is
+ // invisible.
+ let ancestors = PlacesUtils.nodeAncestors(aOldNode);
+ for (let ancestor of ancestors) {
+ if (!ancestor.containerOpen) {
+ return -1;
+ }
+ }
+
+ return this._getRowForNode(aOldNode, true);
+ }
+
+ // There's a broken edge case here.
+ // If a visit appears in two queries, and the second one was
+ // the old node, we'll select the first one after refresh. There's
+ // nothing we could do about that, because aOldNode.parent is
+ // gone by the time invalidateContainer is called.
+ let newNode = this._nodeDetails.get(makeNodeDetailsKey(aOldNode));
+
+ if (!newNode)
+ return -1;
+
+ return this._getRowForNode(newNode, true);
+ },
+
+ /**
+ * Restores a given selection state as near as possible to the original
+ * selection state.
+ *
+ * @param aNodesInfo
+ * The persisted selection state as returned by
+ * _getSelectedNodesInRange.
+ * @param aUpdatedContainer
+ * The container which was updated.
+ */
+ _restoreSelection:
+ function PTV__restoreSelection(aNodesInfo, aUpdatedContainer) {
+ if (aNodesInfo.length == 0)
+ return;
+
+ let selection = this.selection;
+
+ // Attempt to ensure that previously-visible selection will be visible
+ // if it's re-selected. However, we can only ensure that for one row.
+ let scrollToRow = -1;
+ for (let i = 0; i < aNodesInfo.length; i++) {
+ let nodeInfo = aNodesInfo[i];
+ let row = this._getNewRowForRemovedNode(aUpdatedContainer,
+ nodeInfo.node);
+ // Select the found node, if any.
+ if (row != -1) {
+ selection.rangedSelect(row, row, true);
+ if (nodeInfo.wasVisible && scrollToRow == -1)
+ scrollToRow = row;
+ }
+ }
+
+ // If only one node was previously selected and there's no selection now,
+ // select the node at its old row, if any.
+ if (aNodesInfo.length == 1 && selection.count == 0) {
+ let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1);
+ if (row != -1) {
+ selection.rangedSelect(row, row, true);
+ if (aNodesInfo[0].wasVisible && scrollToRow == -1)
+ scrollToRow = aNodesInfo[0].oldRow;
+ }
+ }
+
+ if (scrollToRow != -1)
+ this._tree.ensureRowIsVisible(scrollToRow);
+ },
+
+ _convertPRTimeToString: function PTV__convertPRTimeToString(aTime) {
+ const MS_PER_MINUTE = 60000;
+ const MS_PER_DAY = 86400000;
+ let timeMs = aTime / 1000; // PRTime is in microseconds
+
+ // Date is calculated starting from midnight, so the modulo with a day are
+ // milliseconds from today's midnight.
+ // getTimezoneOffset corrects that based on local time, notice midnight
+ // can have a different offset during DST-change days.
+ let dateObj = new Date();
+ let now = dateObj.getTime() - dateObj.getTimezoneOffset() * MS_PER_MINUTE;
+ let midnight = now - (now % MS_PER_DAY);
+ midnight += new Date(midnight).getTimezoneOffset() * MS_PER_MINUTE;
+
+ let timeObj = new Date(timeMs);
+ return timeMs >= midnight ? this._todayFormatter.format(timeObj)
+ : this._dateFormatter.format(timeObj);
+ },
+
+ // We use a different formatter for times within the current day,
+ // so we cache both a "today" formatter and a general date formatter.
+ __todayFormatter: null,
+ get _todayFormatter() {
+ if (!this.__todayFormatter) {
+ const dtOptions = { timeStyle: "short" };
+ this.__todayFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
+ }
+ return this.__todayFormatter;
+ },
+
+ __dateFormatter: null,
+ get _dateFormatter() {
+ if (!this.__dateFormatter) {
+ const dtOptions = {
+ dateStyle: "short",
+ timeStyle: "short"
+ };
+ this.__dateFormatter = new Services.intl.DateTimeFormat(undefined, dtOptions);
+ }
+ return this.__dateFormatter;
+ },
+
+ COLUMN_TYPE_UNKNOWN: 0,
+ COLUMN_TYPE_TITLE: 1,
+ COLUMN_TYPE_URI: 2,
+ COLUMN_TYPE_DATE: 3,
+ COLUMN_TYPE_VISITCOUNT: 4,
+ COLUMN_TYPE_DESCRIPTION: 5,
+ COLUMN_TYPE_DATEADDED: 6,
+ COLUMN_TYPE_LASTMODIFIED: 7,
+ COLUMN_TYPE_TAGS: 8,
+
+ _getColumnType: function PTV__getColumnType(aColumn) {
+ let columnType = aColumn.element.getAttribute("anonid") || aColumn.id;
+
+ switch (columnType) {
+ case "title":
+ return this.COLUMN_TYPE_TITLE;
+ case "url":
+ return this.COLUMN_TYPE_URI;
+ case "date":
+ return this.COLUMN_TYPE_DATE;
+ case "visitCount":
+ return this.COLUMN_TYPE_VISITCOUNT;
+ case "description":
+ return this.COLUMN_TYPE_DESCRIPTION;
+ case "dateAdded":
+ return this.COLUMN_TYPE_DATEADDED;
+ case "lastModified":
+ return this.COLUMN_TYPE_LASTMODIFIED;
+ case "tags":
+ return this.COLUMN_TYPE_TAGS;
+ }
+ return this.COLUMN_TYPE_UNKNOWN;
+ },
+
+ _sortTypeToColumnType: function PTV__sortTypeToColumnType(aSortType) {
+ switch (aSortType) {
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING:
+ return [this.COLUMN_TYPE_TITLE, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_DESCENDING:
+ return [this.COLUMN_TYPE_TITLE, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING:
+ return [this.COLUMN_TYPE_DATE, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING:
+ return [this.COLUMN_TYPE_DATE, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_ASCENDING:
+ return [this.COLUMN_TYPE_URI, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_DESCENDING:
+ return [this.COLUMN_TYPE_URI, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_ASCENDING:
+ return [this.COLUMN_TYPE_VISITCOUNT, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING:
+ return [this.COLUMN_TYPE_VISITCOUNT, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_ASCENDING:
+ if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
+ return [this.COLUMN_TYPE_DESCRIPTION, false];
+ break;
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_DESCENDING:
+ if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
+ return [this.COLUMN_TYPE_DESCRIPTION, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_ASCENDING:
+ return [this.COLUMN_TYPE_DATEADDED, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING:
+ return [this.COLUMN_TYPE_DATEADDED, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_ASCENDING:
+ return [this.COLUMN_TYPE_LASTMODIFIED, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING:
+ return [this.COLUMN_TYPE_LASTMODIFIED, true];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_ASCENDING:
+ return [this.COLUMN_TYPE_TAGS, false];
+ case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_DESCENDING:
+ return [this.COLUMN_TYPE_TAGS, true];
+ }
+ return [this.COLUMN_TYPE_UNKNOWN, false];
+ },
+
+ // nsINavHistoryResultObserver
+ nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) {
+ console.assert(this._result, "Got a notification but have no result!");
+ if (!this._tree || !this._result)
+ return;
+
+ // Bail out for hidden separators.
+ if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
+ return;
+
+ let parentRow;
+ if (aParentNode != this._rootNode) {
+ parentRow = this._getRowForNode(aParentNode);
+
+ // Update parent when inserting the first item, since twisty has changed.
+ if (aParentNode.childCount == 1)
+ this._tree.invalidateRow(parentRow);
+ }
+
+ // Compute the new row number of the node.
+ let row = -1;
+ let cc = aParentNode.childCount;
+ if (aNewIndex == 0 || this._isPlainContainer(aParentNode) || cc == 0) {
+ // We don't need to worry about sub hierarchies of the parent node
+ // if it's a plain container, or if the new node is its first child.
+ if (aParentNode == this._rootNode)
+ row = aNewIndex;
+ else
+ row = parentRow + aNewIndex + 1;
+ } else {
+ // Here, we try to find the next visible element in the child list so we
+ // can set the new visible index to be right before that. Note that we
+ // have to search down instead of up, because some siblings could have
+ // children themselves that would be in the way.
+ let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) &&
+ this.isSorted();
+ for (let i = aNewIndex + 1; i < cc; i++) {
+ let node = aParentNode.getChild(i);
+ if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) {
+ // The children have not been shifted so the next item will have what
+ // should be our index.
+ row = this._getRowForNode(node, false, parentRow, i);
+ break;
+ }
+ }
+ if (row < 0) {
+ // At the end of the child list without finding a visible sibling. This
+ // is a little harder because we don't know how many rows the last item
+ // in our list takes up (it could be a container with many children).
+ let prevChild = aParentNode.getChild(aNewIndex - 1);
+ let prevIndex = this._getRowForNode(prevChild, false, parentRow,
+ aNewIndex - 1);
+ row = prevIndex + this._countVisibleRowsForNodeAtRow(prevIndex);
+ }
+ }
+
+ this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
+ this._rows.splice(row, 0, aNode);
+ this._tree.rowCountChanged(row, 1);
+
+ if (PlacesUtils.nodeIsContainer(aNode) &&
+ PlacesUtils.asContainer(aNode).containerOpen) {
+ this.invalidateContainer(aNode);
+ }
+ },
+
+ /**
+ * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being
+ * removed but the node it is collapsed with is not being removed (this then
+ * just swap out the removee with its collapsing partner). The only time
+ * when we really remove things is when deleting URIs, which will apply to
+ * all collapsees. This function is called sometimes when resorting items.
+ * However, we won't do this when sorted by date because dates will never
+ * change for visits, and date sorting is the only time things are collapsed.
+ */
+ nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) {
+ console.assert(this._result, "Got a notification but have no result!");
+ if (!this._tree || !this._result)
+ return;
+
+ // XXX bug 517701: We don't know what to do when the root node is removed.
+ if (aNode == this._rootNode)
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+
+ // Bail out for hidden separators.
+ if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
+ return;
+
+ let parentRow = aParentNode == this._rootNode ?
+ undefined : this._getRowForNode(aParentNode, true);
+ let oldRow = this._getRowForNode(aNode, true, parentRow, aOldIndex);
+ if (oldRow < 0)
+ throw Cr.NS_ERROR_UNEXPECTED;
+
+ // If the node was exclusively selected, the node next to it will be
+ // selected.
+ let selectNext = false;
+ let selection = this.selection;
+ if (selection.getRangeCount() == 1) {
+ let min = { }, max = { };
+ selection.getRangeAt(0, min, max);
+ if (min.value == max.value &&
+ this.nodeForTreeIndex(min.value) == aNode)
+ selectNext = true;
+ }
+
+ // Remove the node and its children, if any.
+ let count = this._countVisibleRowsForNodeAtRow(oldRow);
+ for (let splicedNode of this._rows.splice(oldRow, count)) {
+ this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
+ }
+ this._tree.rowCountChanged(oldRow, -count);
+
+ // Redraw the parent if its twisty state has changed.
+ if (aParentNode != this._rootNode && !aParentNode.hasChildren) {
+ parentRow = oldRow - 1;
+ this._tree.invalidateRow(parentRow);
+ }
+
+ // Restore selection if the node was exclusively selected.
+ if (!selectNext)
+ return;
+
+ // Restore selection.
+ let rowToSelect = Math.min(oldRow, this._rows.length - 1);
+ if (rowToSelect != -1)
+ this.selection.rangedSelect(rowToSelect, rowToSelect, true);
+ },
+
+ nodeMoved:
+ function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+ console.assert(this._result, "Got a notification but have no result!");
+ if (!this._tree || !this._result)
+ return;
+
+ // Bail out for hidden separators.
+ if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
+ return;
+
+ // Note that at this point the node has already been moved by the backend,
+ // so we must give hints to _getRowForNode to get the old row position.
+ let oldParentRow = aOldParent == this._rootNode ?
+ undefined : this._getRowForNode(aOldParent, true);
+ let oldRow = this._getRowForNode(aNode, true, oldParentRow, aOldIndex);
+ if (oldRow < 0)
+ throw Cr.NS_ERROR_UNEXPECTED;
+
+ // If this node is a container it could take up more than one row.
+ let count = this._countVisibleRowsForNodeAtRow(oldRow);
+
+ // Persist selection state.
+ let nodesToReselect =
+ this._getSelectedNodesInRange(oldRow, oldRow + count);
+ if (nodesToReselect.length > 0)
+ this.selection.selectEventsSuppressed = true;
+
+ // Redraw the parent if its twisty state has changed.
+ if (aOldParent != this._rootNode && !aOldParent.hasChildren) {
+ let parentRow = oldRow - 1;
+ this._tree.invalidateRow(parentRow);
+ }
+
+ // Remove node and its children, if any, from the old position.
+ for (let splicedNode of this._rows.splice(oldRow, count)) {
+ this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
+ }
+ this._tree.rowCountChanged(oldRow, -count);
+
+ // Insert the node into the new position.
+ this.nodeInserted(aNewParent, aNode, aNewIndex);
+
+ // Restore selection.
+ if (nodesToReselect.length > 0) {
+ this._restoreSelection(nodesToReselect, aNewParent);
+ this.selection.selectEventsSuppressed = false;
+ }
+ },
+
+ _invalidateCellValue: function PTV__invalidateCellValue(aNode,
+ aColumnType) {
+ console.assert(this._result, "Got a notification but have no result!");
+ if (!this._tree || !this._result)
+ return;
+
+ // Nothing to do for the root node.
+ if (aNode == this._rootNode)
+ return;
+
+ let row = this._getRowForNode(aNode);
+ if (row == -1)
+ return;
+
+ let column = this._findColumnByType(aColumnType);
+ if (column && !column.element.hidden) {
+ if (aColumnType == this.COLUMN_TYPE_TITLE)
+ this._tree.removeImageCacheEntry(row, column);
+ this._tree.invalidateCell(row, column);
+ }
+
+ // Last modified time is altered for almost all node changes.
+ if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
+ let lastModifiedColumn =
+ this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
+ if (lastModifiedColumn && !lastModifiedColumn.hidden)
+ this._tree.invalidateCell(row, lastModifiedColumn);
+ }
+ },
+
+ _populateLivemarkContainer: function PTV__populateLivemarkContainer(aNode) {
+ PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
+ .then(aLivemark => {
+ let placesNode = aNode;
+ // Need to check containerOpen since getLivemark is async.
+ if (!placesNode.containerOpen)
+ return;
+
+ let children = aLivemark.getNodesForContainer(placesNode);
+ for (let i = 0; i < children.length; i++) {
+ let child = children[i];
+ this.nodeInserted(placesNode, child, i);
+ }
+ }, Cu.reportError);
+ },
+
+ nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) {
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+ },
+
+ nodeURIChanged: function PTV_nodeURIChanged(aNode, aOldURI) {
+ this._nodeDetails.delete(makeNodeDetailsKey({uri: aOldURI,
+ itemId: aNode.itemId,
+ time: aNode.time}));
+ this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI);
+ },
+
+ nodeIconChanged: function PTV_nodeIconChanged(aNode) {
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+ },
+
+ nodeHistoryDetailsChanged:
+ function PTV_nodeHistoryDetailsChanged(aNode, aOldVisitDate,
+ aOldVisitCount) {
+ this._nodeDetails.delete(makeNodeDetailsKey({uri: aNode.uri,
+ itemId: aNode.itemId,
+ time: aOldVisitDate}));
+ this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
+ if (aNode.parent && this._controller.hasCachedLivemarkInfo(aNode.parent)) {
+ // Find the node in the parent.
+ let parentRow = this._flatList ? 0 : this._getRowForNode(aNode.parent);
+ for (let i = parentRow; i < this._rows.length; i++) {
+ let child = this.nodeForTreeIndex(i);
+ if (child.uri == aNode.uri) {
+ this._cellProperties.delete(child);
+ this._invalidateCellValue(child, this.COLUMN_TYPE_TITLE);
+ break;
+ }
+ }
+ return;
+ }
+
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATE);
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_VISITCOUNT);
+ },
+
+ nodeTagsChanged: function PTV_nodeTagsChanged(aNode) {
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS);
+ },
+
+ nodeKeywordChanged(aNode, aNewKeyword) {},
+
+ nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) {
+ if (aAnno == PlacesUIUtils.DESCRIPTION_ANNO) {
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_DESCRIPTION);
+ } else if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
+ PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
+ .then(aLivemark => {
+ this._controller.cacheLivemarkInfo(aNode, aLivemark);
+ let properties = this._cellProperties.get(aNode);
+ this._cellProperties.set(aNode, properties += " livemark");
+ // The livemark attribute is set as a cell property on the title cell.
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+ }, Cu.reportError);
+ }
+ },
+
+ nodeDateAddedChanged: function PTV_nodeDateAddedChanged(aNode, aNewValue) {
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATEADDED);
+ },
+
+ nodeLastModifiedChanged:
+ function PTV_nodeLastModifiedChanged(aNode, aNewValue) {
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_LASTMODIFIED);
+ },
+
+ containerStateChanged:
+ function PTV_containerStateChanged(aNode, aOldState, aNewState) {
+ this.invalidateContainer(aNode);
+
+ if (PlacesUtils.nodeIsFolder(aNode) ||
+ (this._flatList && aNode == this._rootNode)) {
+ let queryOptions = PlacesUtils.asQuery(this._rootNode).queryOptions;
+ if (queryOptions.excludeItems) {
+ return;
+ }
+ if (aNode.itemId != -1) { // run when there's a valid node id
+ PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
+ .then(aLivemark => {
+ let shouldInvalidate =
+ !this._controller.hasCachedLivemarkInfo(aNode);
+ this._controller.cacheLivemarkInfo(aNode, aLivemark);
+ if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
+ aLivemark.registerForUpdates(aNode, this);
+ // Prioritize the current livemark.
+ aLivemark.reload();
+ PlacesUtils.livemarks.reloadLivemarks();
+ if (shouldInvalidate)
+ this.invalidateContainer(aNode);
+ } else {
+ aLivemark.unregisterForUpdates(aNode);
+ }
+ }, () => undefined);
+ }
+ }
+ },
+
+ invalidateContainer: function PTV_invalidateContainer(aContainer) {
+ console.assert(this._result, "Need to have a result to update");
+ if (!this._tree)
+ return;
+
+ // If we are currently editing, don't invalidate the container until we
+ // finish.
+ if (this._tree.element.getAttribute("editing")) {
+ if (!this._editingObservers) {
+ this._editingObservers = new Map();
+ }
+ if (!this._editingObservers.has(aContainer)) {
+ let mutationObserver = new MutationObserver(() => {
+ Services.tm.dispatchToMainThread(
+ () => this.invalidateContainer(aContainer));
+ let observer = this._editingObservers.get(aContainer);
+ observer.disconnect();
+ this._editingObservers.delete(aContainer);
+ });
+
+ mutationObserver.observe(this._tree.element, {
+ attributes: true,
+ attributeFilter: ["editing"],
+ });
+
+ this._editingObservers.set(aContainer, mutationObserver);
+ }
+ return;
+ }
+
+ let startReplacement, replaceCount;
+ if (aContainer == this._rootNode) {
+ startReplacement = 0;
+ replaceCount = this._rows.length;
+
+ // If the root node is now closed, the tree is empty.
+ if (!this._rootNode.containerOpen) {
+ this._nodeDetails.clear();
+ this._rows = [];
+ if (replaceCount)
+ this._tree.rowCountChanged(startReplacement, -replaceCount);
+
+ return;
+ }
+ } else {
+ // Update the twisty state.
+ let row = this._getRowForNode(aContainer);
+ this._tree.invalidateRow(row);
+
+ // We don't replace the container node itself, so we should decrease the
+ // replaceCount by 1.
+ startReplacement = row + 1;
+ replaceCount = this._countVisibleRowsForNodeAtRow(row) - 1;
+ }
+
+ // Persist selection state.
+ let nodesToReselect =
+ this._getSelectedNodesInRange(startReplacement,
+ startReplacement + replaceCount);
+
+ // Now update the number of elements.
+ this.selection.selectEventsSuppressed = true;
+
+ // First remove the old elements
+ for (let splicedNode of this._rows.splice(startReplacement, replaceCount)) {
+ this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
+ }
+
+ // If the container is now closed, we're done.
+ if (!aContainer.containerOpen) {
+ let oldSelectionCount = this.selection.count;
+ if (replaceCount)
+ this._tree.rowCountChanged(startReplacement, -replaceCount);
+
+ // Select the row next to the closed container if any of its
+ // children were selected, and nothing else is selected.
+ if (nodesToReselect.length > 0 &&
+ nodesToReselect.length == oldSelectionCount) {
+ this.selection.rangedSelect(startReplacement, startReplacement, true);
+ this._tree.ensureRowIsVisible(startReplacement);
+ }
+
+ this.selection.selectEventsSuppressed = false;
+ return;
+ }
+
+ // Otherwise, start a batch first.
+ this._tree.beginUpdateBatch();
+ if (replaceCount)
+ this._tree.rowCountChanged(startReplacement, -replaceCount);
+
+ let toOpenElements = [];
+ let elementsAddedCount = this._buildVisibleSection(aContainer,
+ startReplacement,
+ toOpenElements);
+ if (elementsAddedCount)
+ this._tree.rowCountChanged(startReplacement, elementsAddedCount);
+
+ if (!this._flatList) {
+ // Now, open any containers that were persisted.
+ for (let i = 0; i < toOpenElements.length; i++) {
+ let item = toOpenElements[i];
+ let parent = item.parent;
+
+ // Avoid recursively opening containers.
+ while (parent) {
+ if (parent.uri == item.uri)
+ break;
+ parent = parent.parent;
+ }
+
+ // If we don't have a parent, we made it all the way to the root
+ // and didn't find a match, so we can open our item.
+ if (!parent && !item.containerOpen)
+ item.containerOpen = true;
+ }
+ }
+
+ if (this._controller.hasCachedLivemarkInfo(aContainer)) {
+ let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+ if (!queryOptions.excludeItems) {
+ this._populateLivemarkContainer(aContainer);
+ }
+ }
+
+ this._tree.endUpdateBatch();
+
+ // Restore selection.
+ this._restoreSelection(nodesToReselect, aContainer);
+ this.selection.selectEventsSuppressed = false;
+ },
+
+ _columns: [],
+ _findColumnByType: function PTV__findColumnByType(aColumnType) {
+ if (this._columns[aColumnType])
+ return this._columns[aColumnType];
+
+ let columns = this._tree.columns;
+ let colCount = columns.count;
+ for (let i = 0; i < colCount; i++) {
+ let column = columns.getColumnAt(i);
+ let columnType = this._getColumnType(column);
+ this._columns[columnType] = column;
+ if (columnType == aColumnType)
+ return column;
+ }
+
+ // That's completely valid. Most of our trees actually include just the
+ // title column.
+ return null;
+ },
+
+ sortingChanged: function PTV__sortingChanged(aSortingMode) {
+ if (!this._tree || !this._result)
+ return;
+
+ // Depending on the sort mode, certain commands may be disabled.
+ window.updateCommands("sort");
+
+ let columns = this._tree.columns;
+
+ // Clear old sorting indicator.
+ let sortedColumn = columns.getSortedColumn();
+ if (sortedColumn)
+ sortedColumn.element.removeAttribute("sortDirection");
+
+ // Set new sorting indicator by looking through all columns for ours.
+ if (aSortingMode == Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
+ return;
+
+ let [desiredColumn, desiredIsDescending] =
+ this._sortTypeToColumnType(aSortingMode);
+ let column = this._findColumnByType(desiredColumn);
+ if (column) {
+ let sortDir = desiredIsDescending ? "descending" : "ascending";
+ column.element.setAttribute("sortDirection", sortDir);
+ }
+ },
+
+ _inBatchMode: false,
+ batching: function PTV__batching(aToggleMode) {
+ if (this._inBatchMode != aToggleMode) {
+ this._inBatchMode = this.selection.selectEventsSuppressed = aToggleMode;
+ if (this._inBatchMode) {
+ this._tree.beginUpdateBatch();
+ } else {
+ this._tree.endUpdateBatch();
+ }
+ }
+ },
+
+ get result() {
+ return this._result;
+ },
+ set result(val) {
+ if (this._result) {
+ this._result.removeObserver(this);
+ this._rootNode.containerOpen = false;
+ }
+
+ if (val) {
+ this._result = val;
+ this._rootNode = this._result.root;
+ this._cellProperties = new Map();
+ this._cuttingNodes = new Set();
+ } else if (this._result) {
+ delete this._result;
+ delete this._rootNode;
+ delete this._cellProperties;
+ delete this._cuttingNodes;
+ }
+
+ // If the tree is not set yet, setTree will call finishInit.
+ if (this._tree && val)
+ this._finishInit();
+
+ return val;
+ },
+
+ /**
+ * This allows you to get at the real node for a given row index. This is
+ * only valid when a tree is attached.
+ *
+ * @param {Integer} aIndex The index for the node to get.
+ * @return {Ci.nsINavHistoryResultNode} The node.
+ * @throws Cr.NS_ERROR_INVALID_ARG if the index is greater than the number of
+ * rows.
+ */
+ nodeForTreeIndex(aIndex) {
+ if (aIndex > this._rows.length)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ return this._getNodeForRow(aIndex);
+ },
+
+ /**
+ * Reverse of nodeForTreeIndex, returns the row index for a given result node.
+ * The node should be part of the tree.
+ *
+ * @param {Ci.nsINavHistoryResultNode} aNode The node to look for in the tree.
+ * @returns {Integer} The found index, or -1 if the item is not visible or not found.
+ */
+ treeIndexForNode(aNode) {
+ // The API allows passing invisible nodes.
+ try {
+ return this._getRowForNode(aNode, true);
+ } catch (ex) { }
+
+ return -1;
+ },
+
+ // nsITreeView
+ get rowCount() {
+ return this._rows.length;
+ },
+ get selection() {
+ return this._selection;
+ },
+ set selection(val) {
+ this._selection = val;
+ },
+
+ getRowProperties() { return ""; },
+
+ getCellProperties:
+ function PTV_getCellProperties(aRow, aColumn) {
+ // for anonid-trees, we need to add the column-type manually
+ var props = "";
+ let columnType = aColumn.element.getAttribute("anonid");
+ if (columnType)
+ props += columnType;
+ else
+ columnType = aColumn.id;
+
+ // Set the "ltr" property on url cells
+ if (columnType == "url")
+ props += " ltr";
+
+ if (columnType != "title")
+ return props;
+
+ let node = this._getNodeForRow(aRow);
+
+ if (this._cuttingNodes.has(node)) {
+ props += " cutting";
+ }
+
+ let properties = this._cellProperties.get(node);
+ if (properties === undefined) {
+ properties = "";
+ let itemId = node.itemId;
+ let nodeType = node.type;
+ if (PlacesUtils.containerTypes.includes(nodeType)) {
+ if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
+ properties += " query";
+ if (PlacesUtils.nodeIsTagQuery(node))
+ properties += " tagContainer";
+ else if (PlacesUtils.nodeIsDay(node))
+ properties += " dayContainer";
+ else if (PlacesUtils.nodeIsHost(node))
+ properties += " hostContainer";
+ } else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
+ nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
+ if (itemId != -1) {
+ if (this._controller.hasCachedLivemarkInfo(node)) {
+ properties += " livemark";
+ } else {
+ PlacesUtils.livemarks.getLivemark({ id: itemId })
+ .then(aLivemark => {
+ this._controller.cacheLivemarkInfo(node, aLivemark);
+ let livemarkProps = this._cellProperties.get(node);
+ this._cellProperties.set(node, livemarkProps += " livemark");
+ // The livemark attribute is set as a cell property on the title cell.
+ this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE);
+ }, () => undefined);
+ }
+ }
+ }
+
+ if (itemId == -1) {
+ switch (node.bookmarkGuid) {
+ case PlacesUtils.bookmarks.virtualToolbarGuid:
+ properties += ` queryFolder_${PlacesUtils.bookmarks.toolbarGuid}`;
+ break;
+ case PlacesUtils.bookmarks.virtualMenuGuid:
+ properties += ` queryFolder_${PlacesUtils.bookmarks.menuGuid}`;
+ break;
+ case PlacesUtils.bookmarks.virtualUnfiledGuid:
+ properties += ` queryFolder_${PlacesUtils.bookmarks.unfiledGuid}`;
+ break;
+ }
+ } else {
+ let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
+ if (queryName)
+ properties += " OrganizerQuery_" + queryName;
+ }
+ } else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
+ properties += " separator";
+ else if (PlacesUtils.nodeIsURI(node)) {
+ properties += " " + PlacesUIUtils.guessUrlSchemeForUI(node.uri);
+
+ if (this._controller.hasCachedLivemarkInfo(node.parent)) {
+ properties += " livemarkItem";
+ if (node.accessCount) {
+ properties += " visited";
+ }
+ }
+ }
+
+ this._cellProperties.set(node, properties);
+ }
+
+ return props + " " + properties;
+ },
+
+ getColumnProperties(aColumn) { return ""; },
+
+ isContainer: function PTV_isContainer(aRow) {
+ // Only leaf nodes aren't listed in the rows array.
+ let node = this._rows[aRow];
+ if (node === undefined || !PlacesUtils.nodeIsContainer(node))
+ return false;
+
+ // Flat-lists may ignore expandQueries and other query options when
+ // they are asked to open a container.
+ if (this._flatList)
+ return true;
+
+ // Treat non-expandable childless queries as non-containers, unless they
+ // are tags.
+ if (PlacesUtils.nodeIsQuery(node) && !PlacesUtils.nodeIsTagQuery(node)) {
+ PlacesUtils.asQuery(node);
+ return node.queryOptions.expandQueries || node.hasChildren;
+ }
+ return true;
+ },
+
+ isContainerOpen: function PTV_isContainerOpen(aRow) {
+ if (this._flatList)
+ return false;
+
+ // All containers are listed in the rows array.
+ return this._rows[aRow].containerOpen;
+ },
+
+ isContainerEmpty: function PTV_isContainerEmpty(aRow) {
+ if (this._flatList)
+ return true;
+
+ let node = this._rows[aRow];
+ if (this._controller.hasCachedLivemarkInfo(node)) {
+ let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+ return queryOptions.excludeItems;
+ }
+
+ // All containers are listed in the rows array.
+ return !node.hasChildren;
+ },
+
+ isSeparator: function PTV_isSeparator(aRow) {
+ // All separators are listed in the rows array.
+ let node = this._rows[aRow];
+ return node && PlacesUtils.nodeIsSeparator(node);
+ },
+
+ isSorted: function PTV_isSorted() {
+ return this._result.sortingMode !=
+ Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
+ },
+
+ canDrop: function PTV_canDrop(aRow, aOrientation, aDataTransfer) {
+ if (!this._result)
+ throw Cr.NS_ERROR_UNEXPECTED;
+
+ // Drop position into a sorted treeview would be wrong.
+ if (this.isSorted())
+ return false;
+
+ let ip = this._getInsertionPoint(aRow, aOrientation);
+ return ip && PlacesControllerDragHelper.canDrop(ip, aDataTransfer);
+ },
+
+ _getInsertionPoint: function PTV__getInsertionPoint(index, orientation) {
+ let container = this._result.root;
+ let dropNearNode = null;
+ // When there's no selection, assume the container is the container
+ // the view is populated from (i.e. the result's itemId).
+ if (index != -1) {
+ let lastSelected = this.nodeForTreeIndex(index);
+ if (this.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
+ // If the last selected item is an open container, append _into_
+ // it, rather than insert adjacent to it.
+ container = lastSelected;
+ index = -1;
+ } else if (lastSelected.containerOpen &&
+ orientation == Ci.nsITreeView.DROP_AFTER &&
+ lastSelected.hasChildren) {
+ // If the last selected node is an open container and the user is
+ // trying to drag into it as a first node, really insert into it.
+ container = lastSelected;
+ orientation = Ci.nsITreeView.DROP_ON;
+ index = 0;
+ } else {
+ // Use the last-selected node's container.
+ container = lastSelected.parent;
+
+ // During its Drag & Drop operation, the tree code closes-and-opens
+ // containers very often (part of the XUL "spring-loaded folders"
+ // implementation). And in certain cases, we may reach a closed
+ // container here. However, we can simply bail out when this happens,
+ // because we would then be back here in less than a millisecond, when
+ // the container had been reopened.
+ if (!container || !container.containerOpen)
+ return null;
+
+ // Avoid the potentially expensive call to getChildIndex
+ // if we know this container doesn't allow insertion.
+ if (this._controller.disallowInsertion(container))
+ return null;
+
+ let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
+ if (queryOptions.sortingMode !=
+ Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
+ // If we are within a sorted view, insert at the end.
+ index = -1;
+ } else if (queryOptions.excludeItems ||
+ queryOptions.excludeQueries ||
+ queryOptions.excludeReadOnlyFolders) {
+ // Some item may be invisible, insert near last selected one.
+ // We don't replace index here to avoid requests to the db,
+ // instead it will be calculated later by the controller.
+ index = -1;
+ dropNearNode = lastSelected;
+ } else {
+ let lsi = container.getChildIndex(lastSelected);
+ index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
+ }
+ }
+ }
+
+ if (this._controller.disallowInsertion(container))
+ return null;
+
+ // TODO (Bug 1160193): properly support dropping on a tag root.
+ let tagName = null;
+ if (PlacesUtils.nodeIsTagQuery(container)) {
+ tagName = container.title;
+ if (!tagName)
+ return null;
+ }
+
+ return new PlacesInsertionPoint({
+ parentId: PlacesUtils.getConcreteItemId(container),
+ parentGuid: PlacesUtils.getConcreteItemGuid(container),
+ index, orientation, tagName, dropNearNode
+ });
+ },
+
+ drop: function PTV_drop(aRow, aOrientation, aDataTransfer) {
+ // We are responsible for translating the |index| and |orientation|
+ // parameters into a container id and index within the container,
+ // since this information is specific to the tree view.
+ let ip = this._getInsertionPoint(aRow, aOrientation);
+ if (ip) {
+ PlacesControllerDragHelper.onDrop(ip, aDataTransfer, this._tree.element)
+ .catch(Cu.reportError)
+ .then(() => {
+ // We should only clear the drop target once
+ // the onDrop is complete, as it is an async function.
+ PlacesControllerDragHelper.currentDropTarget = null;
+ });
+ }
+ },
+
+ getParentIndex: function PTV_getParentIndex(aRow) {
+ let [, parentRow] = this._getParentByChildRow(aRow);
+ return parentRow;
+ },
+
+ hasNextSibling: function PTV_hasNextSibling(aRow, aAfterIndex) {
+ if (aRow == this._rows.length - 1) {
+ // The last row has no sibling.
+ return false;
+ }
+
+ let node = this._rows[aRow];
+ if (node === undefined || this._isPlainContainer(node.parent)) {
+ // The node is a child of a plain container.
+ // If the next row is either unset or has the same parent,
+ // it's a sibling.
+ let nextNode = this._rows[aRow + 1];
+ return (nextNode == undefined || nextNode.parent == node.parent);
+ }
+
+ let thisLevel = node.indentLevel;
+ for (let i = aAfterIndex + 1; i < this._rows.length; ++i) {
+ let rowNode = this._getNodeForRow(i);
+ let nextLevel = rowNode.indentLevel;
+ if (nextLevel == thisLevel)
+ return true;
+ if (nextLevel < thisLevel)
+ break;
+ }
+
+ return false;
+ },
+
+ getLevel(aRow) {
+ return this._getNodeForRow(aRow).indentLevel;
+ },
+
+ getImageSrc: function PTV_getImageSrc(aRow, aColumn) {
+ // Only the title column has an image.
+ if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE)
+ return "";
+
+ let node = this._getNodeForRow(aRow);
+ return node.icon;
+ },
+
+ getCellValue(aRow, aColumn) { },
+
+ getCellText: function PTV_getCellText(aRow, aColumn) {
+ let node = this._getNodeForRow(aRow);
+ switch (this._getColumnType(aColumn)) {
+ case this.COLUMN_TYPE_TITLE:
+ // normally, this is just the title, but we don't want empty items in
+ // the tree view so return a special string if the title is empty.
+ // Do it here so that callers can still get at the 0 length title
+ // if they go through the "result" API.
+ if (PlacesUtils.nodeIsSeparator(node))
+ return "";
+ return PlacesUIUtils.getBestTitle(node, true);
+ case this.COLUMN_TYPE_TAGS:
+ return node.tags;
+ case this.COLUMN_TYPE_URI:
+ if (PlacesUtils.nodeIsURI(node))
+ return node.uri;
+ return "";
+ case this.COLUMN_TYPE_DATE:
+ let nodeTime = node.time;
+ if (nodeTime == 0 || !PlacesUtils.nodeIsURI(node)) {
+ // hosts and days shouldn't have a value for the date column.
+ // Actually, you could argue this point, but looking at the
+ // results, seeing the most recently visited date is not what
+ // I expect, and gives me no information I know how to use.
+ // Only show this for URI-based items.
+ return "";
+ }
+
+ return this._convertPRTimeToString(nodeTime);
+ case this.COLUMN_TYPE_VISITCOUNT:
+ return node.accessCount;
+ case this.COLUMN_TYPE_DESCRIPTION:
+ if (node.itemId != -1) {
+ try {
+ return PlacesUtils.annotations.
+ getItemAnnotation(node.itemId, PlacesUIUtils.DESCRIPTION_ANNO);
+ } catch (ex) { /* has no description */ }
+ }
+ return "";
+ case this.COLUMN_TYPE_DATEADDED:
+ if (node.dateAdded)
+ return this._convertPRTimeToString(node.dateAdded);
+ return "";
+ case this.COLUMN_TYPE_LASTMODIFIED:
+ if (node.lastModified)
+ return this._convertPRTimeToString(node.lastModified);
+ return "";
+ }
+ return "";
+ },
+
+ setTree: function PTV_setTree(aTree) {
+ // If we are replacing the tree during a batch, there is a concrete risk
+ // that the treeView goes out of sync, thus it's safer to end the batch now.
+ // This is a no-op if we are not batching.
+ this.batching(false);
+
+ let hasOldTree = this._tree != null;
+ this._tree = aTree;
+
+ if (this._result) {
+ if (hasOldTree) {
+ // detach from result when we are detaching from the tree.
+ // This breaks the reference cycle between us and the result.
+ if (!aTree) {
+ this._result.removeObserver(this);
+ this._rootNode.containerOpen = false;
+ }
+ }
+ if (aTree)
+ this._finishInit();
+ }
+ },
+
+ toggleOpenState: function PTV_toggleOpenState(aRow) {
+ if (!this._result)
+ throw Cr.NS_ERROR_UNEXPECTED;
+
+ let node = this._rows[aRow];
+ if (this._flatList && this._openContainerCallback) {
+ this._openContainerCallback(node);
+ return;
+ }
+
+ // Persist containers open status, but never persist livemarks.
+ if (!this._controller.hasCachedLivemarkInfo(node)) {
+ let uri = node.uri;
+
+ if (uri) {
+ let docURI = document.documentURI;
+
+ if (node.containerOpen) {
+ this._xulStore.removeValue(docURI, uri, "open");
+ } else {
+ this._xulStore.setValue(docURI, uri, "open", "true");
+ }
+ }
+ }
+
+ node.containerOpen = !node.containerOpen;
+ },
+
+ cycleHeader: function PTV_cycleHeader(aColumn) {
+ if (!this._result)
+ throw Cr.NS_ERROR_UNEXPECTED;
+
+ // Sometimes you want a tri-state sorting, and sometimes you don't. This
+ // rule allows tri-state sorting when the root node is a folder. This will
+ // catch the most common cases. When you are looking at folders, you want
+ // the third state to reset the sorting to the natural bookmark order. When
+ // you are looking at history, that third state has no meaning so we try
+ // to disallow it.
+ //
+ // The problem occurs when you have a query that results in bookmark
+ // folders. One example of this is the subscriptions view. In these cases,
+ // this rule doesn't allow you to sort those sub-folders by their natural
+ // order.
+ let allowTriState = PlacesUtils.nodeIsFolder(this._result.root);
+
+ let oldSort = this._result.sortingMode;
+ let oldSortingAnnotation = this._result.sortingAnnotation;
+ let newSort;
+ let newSortingAnnotation = "";
+ const NHQO = Ci.nsINavHistoryQueryOptions;
+ switch (this._getColumnType(aColumn)) {
+ case this.COLUMN_TYPE_TITLE:
+ if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING)
+ newSort = NHQO.SORT_BY_TITLE_DESCENDING;
+ else if (allowTriState && oldSort == NHQO.SORT_BY_TITLE_DESCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_TITLE_ASCENDING;
+
+ break;
+ case this.COLUMN_TYPE_URI:
+ if (oldSort == NHQO.SORT_BY_URI_ASCENDING)
+ newSort = NHQO.SORT_BY_URI_DESCENDING;
+ else if (allowTriState && oldSort == NHQO.SORT_BY_URI_DESCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_URI_ASCENDING;
+
+ break;
+ case this.COLUMN_TYPE_DATE:
+ if (oldSort == NHQO.SORT_BY_DATE_ASCENDING)
+ newSort = NHQO.SORT_BY_DATE_DESCENDING;
+ else if (allowTriState &&
+ oldSort == NHQO.SORT_BY_DATE_DESCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_DATE_ASCENDING;
+
+ break;
+ case this.COLUMN_TYPE_VISITCOUNT:
+ // visit count default is unusual because we sort by descending
+ // by default because you are most likely to be looking for
+ // highly visited sites when you click it
+ if (oldSort == NHQO.SORT_BY_VISITCOUNT_DESCENDING)
+ newSort = NHQO.SORT_BY_VISITCOUNT_ASCENDING;
+ else if (allowTriState && oldSort == NHQO.SORT_BY_VISITCOUNT_ASCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_VISITCOUNT_DESCENDING;
+
+ break;
+ case this.COLUMN_TYPE_DESCRIPTION:
+ if (oldSort == NHQO.SORT_BY_ANNOTATION_ASCENDING &&
+ oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) {
+ newSort = NHQO.SORT_BY_ANNOTATION_DESCENDING;
+ newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO;
+ } else if (allowTriState &&
+ oldSort == NHQO.SORT_BY_ANNOTATION_DESCENDING &&
+ oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
+ newSort = NHQO.SORT_BY_NONE;
+ else {
+ newSort = NHQO.SORT_BY_ANNOTATION_ASCENDING;
+ newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO;
+ }
+
+ break;
+ case this.COLUMN_TYPE_DATEADDED:
+ if (oldSort == NHQO.SORT_BY_DATEADDED_ASCENDING)
+ newSort = NHQO.SORT_BY_DATEADDED_DESCENDING;
+ else if (allowTriState &&
+ oldSort == NHQO.SORT_BY_DATEADDED_DESCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_DATEADDED_ASCENDING;
+
+ break;
+ case this.COLUMN_TYPE_LASTMODIFIED:
+ if (oldSort == NHQO.SORT_BY_LASTMODIFIED_ASCENDING)
+ newSort = NHQO.SORT_BY_LASTMODIFIED_DESCENDING;
+ else if (allowTriState &&
+ oldSort == NHQO.SORT_BY_LASTMODIFIED_DESCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_LASTMODIFIED_ASCENDING;
+
+ break;
+ case this.COLUMN_TYPE_TAGS:
+ if (oldSort == NHQO.SORT_BY_TAGS_ASCENDING)
+ newSort = NHQO.SORT_BY_TAGS_DESCENDING;
+ else if (allowTriState && oldSort == NHQO.SORT_BY_TAGS_DESCENDING)
+ newSort = NHQO.SORT_BY_NONE;
+ else
+ newSort = NHQO.SORT_BY_TAGS_ASCENDING;
+
+ break;
+ default:
+ throw Cr.NS_ERROR_INVALID_ARG;
+ }
+ this._result.sortingAnnotation = newSortingAnnotation;
+ this._result.sortingMode = newSort;
+ },
+
+ isEditable: function PTV_isEditable(aRow, aColumn) {
+ // At this point we only support editing the title field.
+ if (aColumn.index != 0)
+ return false;
+
+ let node = this._rows[aRow];
+ if (!node) {
+ Cu.reportError("isEditable called for an unbuilt row.");
+ return false;
+ }
+ let itemGuid = node.bookmarkGuid;
+
+ // Only bookmark-nodes are editable. Fortunately, this checks also takes
+ // care of livemark children.
+ if (itemGuid == "")
+ return false;
+
+ // The following items are also not editable, even though they are bookmark
+ // items.
+ // * places-roots
+ // * the left pane special folders and queries (those are place: uri
+ // bookmarks)
+ // * separators
+ //
+ // Note that concrete itemIds aren't used intentionally. For example, we
+ // have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
+ // except for the one under All Bookmarks.
+ if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemGuid) ||
+ PlacesUtils.isQueryGeneratedFolder(itemGuid))
+ return false;
+
+ let parentId = PlacesUtils.getConcreteItemId(node.parent);
+ if (parentId == PlacesUIUtils.leftPaneFolderId) {
+ // Note that the for the time being this is the check that actually
+ // blocks renaming places "roots", and not the isRootItem check above.
+ // That's because places root are only exposed through folder shortcuts
+ // descendants of the left pane folder.
+ return false;
+ }
+
+ return true;
+ },
+
+ setCellText: function PTV_setCellText(aRow, aColumn, aText) {
+ // We may only get here if the cell is editable.
+ let node = this._rows[aRow];
+ if (node.title != aText) {
+ PlacesTransactions.EditTitle({ guid: node.bookmarkGuid, title: aText })
+ .transact().catch(Cu.reportError);
+ }
+ },
+
+ toggleCutNode: function PTV_toggleCutNode(aNode, aValue) {
+ let currentVal = this._cuttingNodes.has(aNode);
+ if (currentVal != aValue) {
+ if (aValue)
+ this._cuttingNodes.add(aNode);
+ else
+ this._cuttingNodes.delete(aNode);
+
+ this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+ }
+ },
+
+ selectionChanged() { },
+ cycleCell(aRow, aColumn) { },
+ isSelectable(aRow, aColumn) { return false; },
+};
diff --git a/comm/suite/components/places/jar.mn b/comm/suite/components/places/jar.mn
new file mode 100644
index 0000000000..e0bb80042b
--- /dev/null
+++ b/comm/suite/components/places/jar.mn
@@ -0,0 +1,30 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+# Provide another URI for the bookmarkProperties dialog so we can persist the
+# attributes separately
+ content/communicator/places/bookmarkProperties2.xul (content/bookmarkProperties.xul)
+* content/communicator/places/places.xul (content/places.xul)
+ content/communicator/places/places.js (content/places.js)
+ content/communicator/places/places.css (content/places.css)
+ content/communicator/places/organizer.css (content/organizer.css)
+ content/communicator/places/bookmarkProperties.xul (content/bookmarkProperties.xul)
+ content/communicator/places/bookmarkProperties.js (content/bookmarkProperties.js)
+ content/communicator/places/placesOverlay.xul (content/placesOverlay.xul)
+ content/communicator/places/menu.xml (content/menu.xml)
+ content/communicator/places/tree.xml (content/tree.xml)
+ content/communicator/places/controller.js (content/controller.js)
+ content/communicator/places/treeView.js (content/treeView.js)
+ content/communicator/places/browserPlacesViews.js (content/browserPlacesViews.js)
+# keep the Places version of the history sidebar at history/history-panel.xul
+# to prevent having to worry about between versions of the browser
+* content/communicator/history/history-panel.xul (content/history-panel.xul)
+ content/communicator/places/history-panel.js (content/history-panel.js)
+# ditto for the bookmarks sidebar
+ content/communicator/bookmarks/bookmarksPanel.xul (content/bookmarksPanel.xul)
+ content/communicator/bookmarks/bookmarksPanel.js (content/bookmarksPanel.js)
+ content/communicator/bookmarks/sidebarUtils.js (content/sidebarUtils.js)
+ content/communicator/places/editBookmarkOverlay.xul (content/editBookmarkOverlay.xul)
+ content/communicator/places/editBookmarkOverlay.js (content/editBookmarkOverlay.js)
diff --git a/comm/suite/components/places/moz.build b/comm/suite/components/places/moz.build
new file mode 100644
index 0000000000..6c33011ed4
--- /dev/null
+++ b/comm/suite/components/places/moz.build
@@ -0,0 +1,28 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += [
+ "tests/autocomplete/xpcshell.ini",
+ "tests/unit/xpcshell.ini",
+]
+
+MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
+BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.ini"]
+
+JAR_MANIFESTS += ["jar.mn"]
+
+EXTRA_COMPONENTS += [
+ "nsPlacesAutoComplete.js",
+ "nsPlacesAutoComplete.manifest",
+]
+
+EXTRA_JS_MODULES += [
+ "PlacesUIUtils.jsm",
+]
+
+
+with Files("**"):
+ BUG_COMPONENT = ("SeaMonkey", "Bookmarks & History")
diff --git a/comm/suite/components/places/nsPlacesAutoComplete.js b/comm/suite/components/places/nsPlacesAutoComplete.js
new file mode 100644
index 0000000000..89c6d66753
--- /dev/null
+++ b/comm/suite/components/places/nsPlacesAutoComplete.js
@@ -0,0 +1,1323 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab
+ * 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 {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+
+////////////////////////////////////////////////////////////////////////////////
+//// Constants
+
+// This SQL query fragment provides the following:
+// - whether the entry is bookmarked (kQueryIndexBookmarked)
+// - the bookmark title, if it is a bookmark (kQueryIndexBookmarkTitle)
+// - the tags associated with a bookmarked entry (kQueryIndexTags)
+const kBookTagSQLFragment =
+ `EXISTS(SELECT 1 FROM moz_bookmarks WHERE fk = h.id) AS bookmarked,
+ (
+ SELECT title FROM moz_bookmarks WHERE fk = h.id AND title NOTNULL
+ ORDER BY lastModified DESC LIMIT 1
+ ) AS btitle,
+ (
+ SELECT GROUP_CONCAT(t.title, ',')
+ FROM moz_bookmarks b
+ JOIN moz_bookmarks t ON t.id = +b.parent AND t.parent = :parent
+ WHERE b.fk = h.id
+ ) AS tags`;
+
+// observer topics
+const kTopicShutdown = "places-shutdown";
+const kPrefChanged = "nsPref:changed";
+
+// Match type constants. These indicate what type of search function we should
+// be using.
+const MATCH_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE;
+const MATCH_BOUNDARY_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY_ANYWHERE;
+const MATCH_BOUNDARY = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY;
+const MATCH_BEGINNING = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING;
+const MATCH_BEGINNING_CASE_SENSITIVE = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING_CASE_SENSITIVE;
+
+// AutoComplete index constants. All AutoComplete queries will provide these
+// columns in this order.
+const kQueryIndexURL = 0;
+const kQueryIndexTitle = 1;
+const kQueryIndexBookmarked = 2;
+const kQueryIndexBookmarkTitle = 3;
+const kQueryIndexTags = 4;
+const kQueryIndexVisitCount = 5;
+const kQueryIndexTyped = 6;
+const kQueryIndexPlaceId = 7;
+const kQueryIndexQueryType = 8;
+const kQueryIndexOpenPageCount = 9;
+
+// AutoComplete query type constants. Describes the various types of queries
+// that we can process.
+const kQueryTypeKeyword = 0;
+const kQueryTypeFiltered = 1;
+
+// This separator is used as an RTL-friendly way to split the title and tags.
+// It can also be used by an nsIAutoCompleteResult consumer to re-split the
+// "comment" back into the title and the tag.
+const kTitleTagsSeparator = " \u2013 ";
+
+const kBrowserUrlbarBranch = "browser.urlbar.";
+// Toggle autocomplete.
+const kBrowserUrlbarAutocompleteEnabledPref = "autocomplete.enabled";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+XPCOMUtils.defineLazyServiceGetter(this, "gTextURIService",
+ "@mozilla.org/intl/texttosuburi;1",
+ "nsITextToSubURI");
+
+////////////////////////////////////////////////////////////////////////////////
+//// Helpers
+
+/**
+ * Initializes our temporary table on a given database.
+ *
+ * @param aDatabase
+ * The mozIStorageConnection to set up the temp table on.
+ */
+function initTempTable(aDatabase)
+{
+ // Note: this should be kept up-to-date with the definition in
+ // nsPlacesTables.h.
+ let stmt = aDatabase.createAsyncStatement(
+ `CREATE TEMP TABLE moz_openpages_temp (
+ url TEXT PRIMARY KEY
+ , open_count INTEGER
+ )`
+ );
+ stmt.executeAsync();
+ stmt.finalize();
+
+ // Note: this should be kept up-to-date with the definition in
+ // nsPlacesTriggers.h.
+ stmt = aDatabase.createAsyncStatement(
+ `CREATE TEMPORARY TRIGGER moz_openpages_temp_afterupdate_trigger
+ AFTER UPDATE OF open_count ON moz_openpages_temp FOR EACH ROW
+ WHEN NEW.open_count = 0
+ BEGIN
+ DELETE FROM moz_openpages_temp
+ WHERE url = NEW.url;
+ END`
+ );
+ stmt.executeAsync();
+ stmt.finalize();
+}
+
+/**
+ * Used to unescape encoded URI strings, and drop information that we do not
+ * care about for searching.
+ *
+ * @param aURIString
+ * The text to unescape and modify.
+ * @return the modified uri.
+ */
+function fixupSearchText(aURIString)
+{
+ let uri = stripPrefix(aURIString);
+ return gTextURIService.unEscapeURIForUI("UTF-8", uri);
+}
+
+/**
+ * Strip prefixes from the URI that we don't care about for searching.
+ *
+ * @param aURIString
+ * The text to modify.
+ * @return the modified uri.
+ */
+function stripPrefix(aURIString)
+{
+ let uri = aURIString;
+
+ if (uri.indexOf("http://") == 0) {
+ uri = uri.slice(7);
+ }
+ else if (uri.indexOf("https://") == 0) {
+ uri = uri.slice(8);
+ }
+ else if (uri.indexOf("ftp://") == 0) {
+ uri = uri.slice(6);
+ }
+
+ if (uri.indexOf("www.") == 0) {
+ uri = uri.slice(4);
+ }
+ return uri;
+}
+
+/**
+ * safePrefGetter get the pref with type safety.
+ * This will return the default value provided if no pref is set.
+ *
+ * @param aPrefBranch
+ * The nsIPrefBranch containing the required preference
+ * @param aName
+ * A preference name
+ * @param aDefault
+ * The preference's default value
+ * @return the preference value or provided default
+ */
+
+function safePrefGetter(aPrefBranch, aName, aDefault) {
+ let types = {
+ boolean: "Bool",
+ number: "Int",
+ string: "Char"
+ };
+ let type = types[typeof(aDefault)];
+ if (!type) {
+ throw "Unknown type!";
+ }
+
+ // If the pref isn't set, we want to use the default.
+ if (aPrefBranch.getPrefType(aName) == Ci.nsIPrefBranch.PREF_INVALID) {
+ return aDefault;
+ }
+ try {
+ return aPrefBranch["get" + type + "Pref"](aName);
+ }
+ catch (e) {
+ return aDefault;
+ }
+}
+
+/**
+ * Whether UnifiedComplete is alive.
+ */
+function isUnifiedCompleteInstantiated() {
+ try {
+ return Components.manager.QueryInterface(Ci.nsIServiceManager)
+ .isServiceInstantiated(Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"],
+ Ci.mozIPlacesAutoComplete);
+ } catch (ex) {
+ return false;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// AutoCompleteStatementCallbackWrapper class
+
+/**
+ * Wraps a callback and ensures that handleCompletion is not dispatched if the
+ * query is no longer tracked.
+ *
+ * @param aAutocomplete
+ * A reference to a nsPlacesAutoComplete.
+ * @param aCallback
+ * A reference to a mozIStorageStatementCallback
+ * @param aDBConnection
+ * The database connection to execute the queries on.
+ */
+function AutoCompleteStatementCallbackWrapper(aAutocomplete, aCallback,
+ aDBConnection)
+{
+ this._autocomplete = aAutocomplete;
+ this._callback = aCallback;
+ this._db = aDBConnection;
+}
+
+AutoCompleteStatementCallbackWrapper.prototype = {
+ //////////////////////////////////////////////////////////////////////////////
+ //// mozIStorageStatementCallback
+
+ handleResult: function ACSCW_handleResult(aResultSet)
+ {
+ this._callback.handleResult.apply(this._callback, arguments);
+ },
+
+ handleError: function ACSCW_handleError(aError)
+ {
+ this._callback.handleError.apply(this._callback, arguments);
+ },
+
+ handleCompletion: function ACSCW_handleCompletion(aReason)
+ {
+ // Only dispatch handleCompletion if we are not done searching and are a
+ // pending search.
+ if (!this._autocomplete.isSearchComplete() &&
+ this._autocomplete.isPendingSearch(this._handle)) {
+ this._callback.handleCompletion.apply(this._callback, arguments);
+ }
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// AutoCompleteStatementCallbackWrapper
+
+ /**
+ * Executes the specified query asynchronously. This object will notify
+ * this._callback if we should notify (logic explained in handleCompletion).
+ *
+ * @param aQueries
+ * The queries to execute asynchronously.
+ * @return a mozIStoragePendingStatement that can be used to cancel the
+ * queries.
+ */
+ executeAsync: function ACSCW_executeAsync(aQueries)
+ {
+ return this._handle = this._db.executeAsync(aQueries, aQueries.length,
+ this);
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.mozIStorageStatementCallback,
+ ])
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsPlacesAutoComplete class
+//// @mozilla.org/autocomplete/search;1?name=history
+
+function nsPlacesAutoComplete()
+{
+ //////////////////////////////////////////////////////////////////////////////
+ //// Shared Constants for Smart Getters
+
+ // TODO bug 412736 in case of a frecency tie, break it with h.typed and
+ // h.visit_count which is better than nothing. This is slow, so not doing it
+ // yet...
+ function baseQuery(conditions = "") {
+ let query = `SELECT h.url, h.title, ${kBookTagSQLFragment},
+ h.visit_count, h.typed, h.id, :query_type,
+ t.open_count
+ FROM moz_places h
+ LEFT JOIN moz_openpages_temp t ON t.url = h.url
+ WHERE h.frecency <> 0
+ AND AUTOCOMPLETE_MATCH(:searchString, h.url,
+ IFNULL(btitle, h.title), tags,
+ h.visit_count, h.typed,
+ bookmarked, t.open_count,
+ :matchBehavior, :searchBehavior)
+ ${conditions}
+ ORDER BY h.frecency DESC, h.id DESC
+ LIMIT :maxResults`;
+ return query;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// Smart Getters
+
+ XPCOMUtils.defineLazyGetter(this, "_db", function() {
+ // Get a cloned, read-only version of the database. We'll only ever write
+ // to our own in-memory temp table, and having a cloned copy means we do not
+ // run the risk of our queries taking longer due to the main database
+ // connection performing a long-running task.
+ let db = PlacesUtils.history.DBConnection.clone(true);
+
+ // Autocomplete often fallbacks to a table scan due to lack of text indices.
+ // In such cases a larger cache helps reducing IO. The default Storage
+ // value is MAX_CACHE_SIZE_BYTES in storage/mozStorageConnection.cpp.
+ let stmt = db.createAsyncStatement("PRAGMA cache_size = -6144"); // 6MiB
+ stmt.executeAsync();
+ stmt.finalize();
+
+ // Create our in-memory tables for tab tracking.
+ initTempTable(db);
+
+ // Populate the table with current open pages cache contents.
+ if (this._openPagesCache.length > 0) {
+ // Avoid getter re-entrance from the _registerOpenPageQuery lazy getter.
+ let stmt = this._registerOpenPageQuery =
+ db.createAsyncStatement(this._registerOpenPageQuerySQL);
+ let params = stmt.newBindingParamsArray();
+ for (let i = 0; i < this._openPagesCache.length; i++) {
+ let bp = params.newBindingParams();
+ bp.bindByName("page_url", this._openPagesCache[i]);
+ params.addParams(bp);
+ }
+ stmt.bindParameters(params);
+ stmt.executeAsync();
+ stmt.finalize();
+ delete this._openPagesCache;
+ }
+
+ return db;
+ });
+
+ this._customQuery = (conditions = "") => {
+ return this._db.createAsyncStatement(baseQuery(conditions));
+ };
+
+ XPCOMUtils.defineLazyGetter(this, "_defaultQuery", function() {
+ return this._db.createAsyncStatement(baseQuery());
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_historyQuery", function() {
+ // Enforce ignoring the visit_count index, since the frecency one is much
+ // faster in this case. ANALYZE helps the query planner to figure out the
+ // faster path, but it may not have run yet.
+ return this._db.createAsyncStatement(baseQuery("AND +h.visit_count > 0"));
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_bookmarkQuery", function() {
+ return this._db.createAsyncStatement(baseQuery("AND bookmarked"));
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_tagsQuery", function() {
+ return this._db.createAsyncStatement(baseQuery("AND tags IS NOT NULL"));
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_openPagesQuery", function() {
+ return this._db.createAsyncStatement(
+ `SELECT t.url, t.url, NULL, NULL, NULL, NULL, NULL, NULL,
+ :query_type, t.open_count, NULL
+ FROM moz_openpages_temp t
+ LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url
+ WHERE h.id IS NULL
+ AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
+ NULL, NULL, NULL, t.open_count,
+ :matchBehavior, :searchBehavior)
+ ORDER BY t.ROWID DESC
+ LIMIT :maxResults`
+ );
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_typedQuery", function() {
+ return this._db.createAsyncStatement(baseQuery("AND h.typed = 1"));
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_adaptiveQuery", function() {
+ return this._db.createAsyncStatement(
+ `/* do not warn (bug 487789) */
+ SELECT h.url, h.title, ${kBookTagSQLFragment},
+ h.visit_count, h.typed, h.id, :query_type, t.open_count
+ FROM (
+ SELECT ROUND(
+ MAX(use_count) * (1 + (input = :search_string)), 1
+ ) AS rank, place_id
+ FROM moz_inputhistory
+ WHERE input BETWEEN :search_string AND :search_string || X'FFFF'
+ GROUP BY place_id
+ ) AS i
+ JOIN moz_places h ON h.id = i.place_id
+ LEFT JOIN moz_openpages_temp t ON t.url = h.url
+ WHERE AUTOCOMPLETE_MATCH(NULL, h.url,
+ IFNULL(btitle, h.title), tags,
+ h.visit_count, h.typed, bookmarked,
+ t.open_count,
+ :matchBehavior, :searchBehavior)
+ ORDER BY rank DESC, h.frecency DESC`
+ );
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_keywordQuery", function() {
+ return this._db.createAsyncStatement(
+ `/* do not warn (bug 487787) */
+ SELECT REPLACE(h.url, '%s', :query_string) AS search_url, h.title,
+ 1, NULL, NULL, h.visit_count, h.typed, h.id,
+ :query_type, t.open_count
+ FROM moz_keywords k
+ JOIN moz_places h ON k.place_id = h.id
+ LEFT JOIN moz_openpages_temp t ON t.url = search_url
+ WHERE k.keyword = LOWER(:keyword)`
+ );
+ });
+
+ this._registerOpenPageQuerySQL =
+ `INSERT OR REPLACE INTO moz_openpages_temp (url, open_count)
+ VALUES (:page_url,
+ IFNULL(
+ (
+ SELECT open_count + 1
+ FROM moz_openpages_temp
+ WHERE url = :page_url
+ ),
+ 1
+ )
+ )`;
+ XPCOMUtils.defineLazyGetter(this, "_registerOpenPageQuery", function() {
+ return this._db.createAsyncStatement(this._registerOpenPageQuerySQL);
+ });
+
+ XPCOMUtils.defineLazyGetter(this, "_unregisterOpenPageQuery", function() {
+ return this._db.createAsyncStatement(
+ `UPDATE moz_openpages_temp
+ SET open_count = open_count - 1
+ WHERE url = :page_url`
+ );
+ });
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// Initialization
+
+ // load preferences
+ this._prefs = Services.prefs.getBranch(kBrowserUrlbarBranch);
+ this._syncEnabledPref();
+ this._loadPrefs(true);
+
+ // register observers
+ Services.obs.addObserver(this, kTopicShutdown);
+}
+
+nsPlacesAutoComplete.prototype = {
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsIAutoCompleteSearch
+
+ startSearch: function PAC_startSearch(aSearchString, aSearchParam,
+ aPreviousResult, aListener)
+ {
+ // Stop the search in case the controller has not taken care of it.
+ this.stopSearch();
+
+ // Note: We don't use aPreviousResult to make sure ordering of results are
+ // consistent. See bug 412730 for more details.
+
+ // We want to store the original string with no leading or trailing
+ // whitespace for case sensitive searches.
+ this._originalSearchString = aSearchString.trim();
+
+ this._currentSearchString =
+ fixupSearchText(this._originalSearchString.toLowerCase());
+
+ let params = new Set(aSearchParam.split(" "));
+ this._enableActions = params.has("enable-actions");
+ this._disablePrivateActions = params.has("disable-private-actions");
+
+ this._listener = aListener;
+ let result = Cc["@mozilla.org/autocomplete/simple-result;1"].
+ createInstance(Ci.nsIAutoCompleteSimpleResult);
+ result.setSearchString(aSearchString);
+ result.setListener(this);
+ this._result = result;
+
+ // If we are not enabled, we need to return now.
+ if (!this._enabled) {
+ this._finishSearch(true);
+ return;
+ }
+
+ // Reset our search behavior to the default.
+ if (this._currentSearchString) {
+ this._behavior = this._defaultBehavior;
+ }
+ else {
+ this._behavior = this._emptySearchDefaultBehavior;
+ }
+ // For any given search, we run up to four queries:
+ // 1) keywords (this._keywordQuery)
+ // 2) adaptive learning (this._adaptiveQuery)
+ // 3) open pages not supported by history (this._openPagesQuery)
+ // 4) query from this._getSearch
+ // (1) only gets ran if we get any filtered tokens from this._getSearch,
+ // since if there are no tokens, there is nothing to match, so there is no
+ // reason to run the query).
+ let {query, tokens} =
+ this._getSearch(this._getUnfilteredSearchTokens(this._currentSearchString));
+ let queries = tokens.length ?
+ [this._getBoundKeywordQuery(tokens), this._getBoundAdaptiveQuery()] :
+ [this._getBoundAdaptiveQuery()];
+
+ if (this._hasBehavior("openpage")) {
+ queries.push(this._getBoundOpenPagesQuery(tokens));
+ }
+ queries.push(query);
+
+ // Start executing our queries.
+ this._executeQueries(queries);
+
+ // Set up our persistent state for the duration of the search.
+ this._searchTokens = tokens;
+ this._usedPlaces = {};
+ },
+
+ stopSearch: function PAC_stopSearch()
+ {
+ // We need to cancel our searches so we do not get any [more] results.
+ // However, it's possible we haven't actually started any searches, so this
+ // method may throw because this._pendingQuery may be undefined.
+ if (this._pendingQuery) {
+ this._stopActiveQuery();
+ }
+
+ this._finishSearch(false);
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsIAutoCompleteSimpleResultListener
+
+ onValueRemoved: function PAC_onValueRemoved(aResult, aURISpec, aRemoveFromDB)
+ {
+ if (aRemoveFromDB) {
+ PlacesUtils.history.removePage(NetUtil.newURI(aURISpec));
+ }
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// mozIPlacesAutoComplete
+
+ // If the connection has not yet been started, use this local cache. This
+ // prevents autocomplete from initing the database till the first search.
+ _openPagesCache: [],
+ registerOpenPage: function PAC_registerOpenPage(aURI)
+ {
+ if (!this._databaseInitialized) {
+ this._openPagesCache.push(aURI.spec);
+ return;
+ }
+
+ let stmt = this._registerOpenPageQuery;
+ stmt.params.page_url = aURI.spec;
+ stmt.executeAsync();
+ },
+
+ unregisterOpenPage: function PAC_unregisterOpenPage(aURI)
+ {
+ if (!this._databaseInitialized) {
+ let index = this._openPagesCache.indexOf(aURI.spec);
+ if (index != -1) {
+ this._openPagesCache.splice(index, 1);
+ }
+ return;
+ }
+
+ let stmt = this._unregisterOpenPageQuery;
+ stmt.params.page_url = aURI.spec;
+ stmt.executeAsync();
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// mozIStorageStatementCallback
+
+ handleResult: function PAC_handleResult(aResultSet)
+ {
+ let row, haveMatches = false;
+ while ((row = aResultSet.getNextRow())) {
+ let match = this._processRow(row);
+ haveMatches = haveMatches || match;
+
+ if (this._result.matchCount == this._maxRichResults) {
+ // We have enough results, so stop running our search.
+ this._stopActiveQuery();
+
+ // And finish our search.
+ this._finishSearch(true);
+ return;
+ }
+
+ }
+
+ // Notify about results if we've gotten them.
+ if (haveMatches) {
+ this._notifyResults(true);
+ }
+ },
+
+ handleError: function PAC_handleError(aError)
+ {
+ Cu.reportError("Places AutoComplete: An async statement encountered an " +
+ "error: " + aError.result + ", '" + aError.message + "'");
+ },
+
+ handleCompletion: function PAC_handleCompletion(aReason)
+ {
+ // If we have already finished our search, we should bail out early.
+ if (this.isSearchComplete()) {
+ return;
+ }
+
+ // If we do not have enough results, and our match type is
+ // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more
+ // results.
+ if (this._matchBehavior == MATCH_BOUNDARY_ANYWHERE &&
+ this._result.matchCount < this._maxRichResults && !this._secondPass) {
+ this._secondPass = true;
+ let queries = [
+ this._getBoundAdaptiveQuery(MATCH_ANYWHERE),
+ this._getBoundSearchQuery(MATCH_ANYWHERE, this._searchTokens),
+ ];
+ this._executeQueries(queries);
+ return;
+ }
+
+ this._finishSearch(true);
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsIObserver
+
+ observe: function PAC_observe(aSubject, aTopic, aData)
+ {
+ if (aTopic == kTopicShutdown) {
+ Services.obs.removeObserver(this, kTopicShutdown);
+
+ // Remove our preference observer.
+ this._prefs.removeObserver("", this);
+ delete this._prefs;
+
+ // Finalize the statements that we have used.
+ let stmts = [
+ "_defaultQuery",
+ "_historyQuery",
+ "_bookmarkQuery",
+ "_tagsQuery",
+ "_openPagesQuery",
+ "_typedQuery",
+ "_adaptiveQuery",
+ "_keywordQuery",
+ "_registerOpenPageQuery",
+ "_unregisterOpenPageQuery",
+ ];
+ for (let i = 0; i < stmts.length; i++) {
+ // We do not want to create any query we haven't already created, so
+ // see if it is a getter first.
+ if (Object.getOwnPropertyDescriptor(this, stmts[i]).value !== undefined) {
+ this[stmts[i]].finalize();
+ }
+ }
+
+ if (this._databaseInitialized) {
+ this._db.asyncClose();
+ }
+ }
+ else if (aTopic == kPrefChanged) {
+ // Avoid re-entrancy when flipping linked preferences.
+ if (this._ignoreNotifications)
+ return;
+ this._ignoreNotifications = true;
+ this._loadPrefs(false, aTopic, aData);
+ this._ignoreNotifications = false;
+ }
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsPlacesAutoComplete
+
+ get _databaseInitialized() {
+ return Object.getOwnPropertyDescriptor(this, "_db").value !== undefined;
+ },
+
+ /**
+ * Generates the tokens used in searching from a given string.
+ *
+ * @param aSearchString
+ * The string to generate tokens from.
+ * @return an array of tokens.
+ */
+ _getUnfilteredSearchTokens: function PAC_unfilteredSearchTokens(aSearchString)
+ {
+ // Calling split on an empty string will return an array containing one
+ // empty string. We don't want that, as it'll break our logic, so return an
+ // empty array then.
+ return aSearchString.length ? aSearchString.split(" ") : [];
+ },
+
+ /**
+ * Properly cleans up when searching is completed.
+ *
+ * @param aNotify
+ * Indicates if we should notify the AutoComplete listener about our
+ * results or not.
+ */
+ _finishSearch: function PAC_finishSearch(aNotify)
+ {
+ // Notify about results if we are supposed to.
+ if (aNotify) {
+ this._notifyResults(false);
+ }
+
+ // Clear our state
+ delete this._originalSearchString;
+ delete this._currentSearchString;
+ delete this._strippedPrefix;
+ delete this._searchTokens;
+ delete this._listener;
+ delete this._result;
+ delete this._usedPlaces;
+ delete this._pendingQuery;
+ this._secondPass = false;
+ this._enableActions = false;
+ },
+
+ /**
+ * Executes the given queries asynchronously.
+ *
+ * @param aQueries
+ * The queries to execute.
+ */
+ _executeQueries: function PAC_executeQueries(aQueries)
+ {
+ // Because we might get a handleCompletion for canceled queries, we want to
+ // filter out queries we no longer care about (described in the
+ // handleCompletion implementation of AutoCompleteStatementCallbackWrapper).
+
+ // Create our wrapper object and execute the queries.
+ let wrapper = new AutoCompleteStatementCallbackWrapper(this, this, this._db);
+ this._pendingQuery = wrapper.executeAsync(aQueries);
+ },
+
+ /**
+ * Stops executing our active query.
+ */
+ _stopActiveQuery: function PAC_stopActiveQuery()
+ {
+ this._pendingQuery.cancel();
+ delete this._pendingQuery;
+ },
+
+ /**
+ * Notifies the listener about results.
+ *
+ * @param aSearchOngoing
+ * Indicates if the search is ongoing or not.
+ */
+ _notifyResults: function PAC_notifyResults(aSearchOngoing)
+ {
+ let result = this._result;
+ let resultCode = result.matchCount ? "RESULT_SUCCESS" : "RESULT_NOMATCH";
+ if (aSearchOngoing) {
+ resultCode += "_ONGOING";
+ }
+ result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]);
+ this._listener.onSearchResult(this, result);
+ },
+
+ /**
+ * Synchronize suggest.* prefs with autocomplete.enabled.
+ */
+ _syncEnabledPref: function PAC_syncEnabledPref()
+ {
+ let suggestPrefs = ["suggest.history", "suggest.bookmark", "suggest.openpage"];
+ let types = ["History", "Bookmark", "Openpage"];
+
+ this._enabled = safePrefGetter(this._prefs, kBrowserUrlbarAutocompleteEnabledPref,
+ true);
+ this._suggestHistory = safePrefGetter(this._prefs, "suggest.history", true);
+ this._suggestBookmark = safePrefGetter(this._prefs, "suggest.bookmark", true);
+ this._suggestOpenpage = safePrefGetter(this._prefs, "suggest.openpage", true);
+
+ if (this._enabled) {
+ // If the autocomplete preference is active, activate all suggest
+ // preferences only if all of them are false.
+ if (types.every(type => this["_suggest" + type] == false)) {
+ for (let type of suggestPrefs) {
+ this._prefs.setBoolPref(type, true);
+ }
+ }
+ } else {
+ // If the preference was deactivated, deactivate all suggest preferences.
+ for (let type of suggestPrefs) {
+ this._prefs.setBoolPref(type, false);
+ }
+ }
+ },
+
+ /**
+ * Loads the preferences that we care about.
+ *
+ * @param [optional] aRegisterObserver
+ * Indicates if the preference observer should be added or not. The
+ * default value is false.
+ * @param [optional] aTopic
+ * Observer's topic, if any.
+ * @param [optional] aSubject
+ * Observer's subject, if any.
+ */
+ _loadPrefs: function PAC_loadPrefs(aRegisterObserver, aTopic, aData)
+ {
+ // Avoid race conditions with UnifiedComplete component.
+ if (aData && !isUnifiedCompleteInstantiated()) {
+ // Synchronize suggest.* prefs with autocomplete.enabled.
+ if (aData == kBrowserUrlbarAutocompleteEnabledPref) {
+ this._syncEnabledPref();
+ } else if (aData.startsWith("suggest.")) {
+ let suggestPrefs = ["suggest.history", "suggest.bookmark", "suggest.openpage"];
+ this._prefs.setBoolPref(kBrowserUrlbarAutocompleteEnabledPref,
+ suggestPrefs.some(pref => safePrefGetter(this._prefs, pref, true)));
+ }
+ }
+
+ this._enabled = safePrefGetter(this._prefs,
+ kBrowserUrlbarAutocompleteEnabledPref,
+ true);
+ this._matchBehavior = safePrefGetter(this._prefs,
+ "matchBehavior",
+ MATCH_BOUNDARY_ANYWHERE);
+ this._filterJavaScript = safePrefGetter(this._prefs, "filter.javascript", true);
+ this._maxRichResults = safePrefGetter(this._prefs, "maxRichResults", 25);
+ this._restrictHistoryToken = safePrefGetter(this._prefs,
+ "restrict.history", "^");
+ this._restrictBookmarkToken = safePrefGetter(this._prefs,
+ "restrict.bookmark", "*");
+ this._restrictTypedToken = safePrefGetter(this._prefs, "restrict.typed", "~");
+ this._restrictTagToken = safePrefGetter(this._prefs, "restrict.tag", "+");
+ this._restrictOpenPageToken = safePrefGetter(this._prefs,
+ "restrict.openpage", "%");
+ this._matchTitleToken = safePrefGetter(this._prefs, "match.title", "#");
+ this._matchURLToken = safePrefGetter(this._prefs, "match.url", "@");
+
+ this._suggestHistory = safePrefGetter(this._prefs, "suggest.history", true);
+ this._suggestBookmark = safePrefGetter(this._prefs, "suggest.bookmark", true);
+ this._suggestOpenpage = safePrefGetter(this._prefs, "suggest.openpage", true);
+ this._suggestTyped = safePrefGetter(this._prefs, "suggest.history.onlyTyped", false);
+
+ // If history is not set, onlyTyped value should be ignored.
+ if (!this._suggestHistory) {
+ this._suggestTyped = false;
+ }
+ let types = ["History", "Bookmark", "Openpage", "Typed"];
+ this._defaultBehavior = types.reduce((memo, type) => {
+ let prefValue = this["_suggest" + type];
+ return memo | (prefValue &&
+ Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()]);
+ }, 0);
+
+ // Further restrictions to apply for "empty searches" (i.e. searches for "").
+ // The empty behavior is typed history, if history is enabled. Otherwise,
+ // it is bookmarks, if they are enabled. If both history and bookmarks are disabled,
+ // it defaults to open pages.
+ this._emptySearchDefaultBehavior = Ci.mozIPlacesAutoComplete.BEHAVIOR_RESTRICT;
+ if (this._suggestHistory) {
+ this._emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
+ Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED;
+ } else if (this._suggestBookmark) {
+ this._emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_BOOKMARK;
+ } else {
+ this._emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_OPENPAGE;
+ }
+
+ // Validate matchBehavior; default to MATCH_BOUNDARY_ANYWHERE.
+ if (this._matchBehavior != MATCH_ANYWHERE &&
+ this._matchBehavior != MATCH_BOUNDARY &&
+ this._matchBehavior != MATCH_BEGINNING) {
+ this._matchBehavior = MATCH_BOUNDARY_ANYWHERE;
+ }
+ // register observer
+ if (aRegisterObserver) {
+ this._prefs.addObserver("", this);
+ }
+ },
+
+ /**
+ * Given an array of tokens, this function determines which query should be
+ * ran. It also removes any special search tokens.
+ *
+ * @param aTokens
+ * An array of search tokens.
+ * @return an object with two properties:
+ * query: the correctly optimized, bound query to search the database
+ * with.
+ * tokens: the filtered list of tokens to search with.
+ */
+ _getSearch: function PAC_getSearch(aTokens)
+ {
+ let foundToken = false;
+ let restrict = (behavior) => {
+ if (!foundToken) {
+ this._behavior = 0;
+ this._setBehavior("restrict");
+ foundToken = true;
+ }
+ this._setBehavior(behavior);
+ };
+
+ // Set the proper behavior so our call to _getBoundSearchQuery gives us the
+ // correct query.
+ for (let i = aTokens.length - 1; i >= 0; i--) {
+ switch (aTokens[i]) {
+ case this._restrictHistoryToken:
+ restrict("history");
+ break;
+ case this._restrictBookmarkToken:
+ restrict("bookmark");
+ break;
+ case this._restrictTagToken:
+ restrict("tag");
+ break;
+ case this._restrictOpenPageToken:
+ if (!this._enableActions) {
+ continue;
+ }
+ restrict("openpage");
+ break;
+ case this._matchTitleToken:
+ restrict("title");
+ break;
+ case this._matchURLToken:
+ restrict("url");
+ break;
+ case this._restrictTypedToken:
+ restrict("typed");
+ break;
+ default:
+ // We do not want to remove the token if we did not match.
+ continue;
+ }
+
+ aTokens.splice(i, 1);
+ }
+
+ // Set the right JavaScript behavior based on our preference. Note that the
+ // preference is whether or not we should filter JavaScript, and the
+ // behavior is if we should search it or not.
+ if (!this._filterJavaScript) {
+ this._setBehavior("javascript");
+ }
+
+ return {
+ query: this._getBoundSearchQuery(this._matchBehavior, aTokens),
+ tokens: aTokens
+ };
+ },
+
+ /**
+ * @return a string consisting of the search query to be used based on the
+ * previously set urlbar suggestion preferences.
+ */
+ _getSuggestionPrefQuery: function PAC_getSuggestionPrefQuery()
+ {
+ if (!this._hasBehavior("restrict") && this._hasBehavior("history") &&
+ this._hasBehavior("bookmark")) {
+ return this._hasBehavior("typed") ? this._customQuery("AND h.typed = 1")
+ : this._defaultQuery;
+ }
+ let conditions = [];
+ if (this._hasBehavior("history")) {
+ // Enforce ignoring the visit_count index, since the frecency one is much
+ // faster in this case. ANALYZE helps the query planner to figure out the
+ // faster path, but it may not have up-to-date information yet.
+ conditions.push("+h.visit_count > 0");
+ }
+ if (this._hasBehavior("typed")) {
+ conditions.push("h.typed = 1");
+ }
+ if (this._hasBehavior("bookmark")) {
+ conditions.push("bookmarked");
+ }
+ if (this._hasBehavior("tag")) {
+ conditions.push("tags NOTNULL");
+ }
+
+ return conditions.length ? this._customQuery("AND " + conditions.join(" AND "))
+ : this._defaultQuery;
+ },
+
+ /**
+ * Obtains the search query to be used based on the previously set search
+ * behaviors (accessed by this._hasBehavior). The query is bound and ready to
+ * execute.
+ *
+ * @param aMatchBehavior
+ * How this query should match its tokens to the search string.
+ * @param aTokens
+ * An array of search tokens.
+ * @return the correctly optimized query to search the database with and the
+ * new list of tokens to search with. The query has all the needed
+ * parameters bound, so consumers can execute it without doing any
+ * additional work.
+ */
+ _getBoundSearchQuery: function PAC_getBoundSearchQuery(aMatchBehavior,
+ aTokens)
+ {
+ let query = this._getSuggestionPrefQuery();
+
+ // Bind the needed parameters to the query so consumers can use it.
+ let params = query.params;
+ params.parent = PlacesUtils.tagsFolderId;
+ params.query_type = kQueryTypeFiltered;
+ params.matchBehavior = aMatchBehavior;
+ params.searchBehavior = this._behavior;
+
+ // We only want to search the tokens that we are left with - not the
+ // original search string.
+ params.searchString = aTokens.join(" ");
+
+ // Limit the query to the the maximum number of desired results.
+ // This way we can avoid doing more work than needed.
+ params.maxResults = this._maxRichResults;
+
+ return query;
+ },
+
+ _getBoundOpenPagesQuery: function PAC_getBoundOpenPagesQuery(aTokens)
+ {
+ let query = this._openPagesQuery;
+
+ // Bind the needed parameters to the query so consumers can use it.
+ let params = query.params;
+ params.query_type = kQueryTypeFiltered;
+ params.matchBehavior = this._matchBehavior;
+ params.searchBehavior = this._behavior;
+
+ // We only want to search the tokens that we are left with - not the
+ // original search string.
+ params.searchString = aTokens.join(" ");
+ params.maxResults = this._maxRichResults;
+
+ return query;
+ },
+
+ /**
+ * Obtains the keyword query with the properly bound parameters.
+ *
+ * @param aTokens
+ * The array of search tokens to check against.
+ * @return the bound keyword query.
+ */
+ _getBoundKeywordQuery: function PAC_getBoundKeywordQuery(aTokens)
+ {
+ // The keyword is the first word in the search string, with the parameters
+ // following it.
+ let searchString = this._originalSearchString;
+ let queryString = "";
+ let queryIndex = searchString.indexOf(" ");
+ if (queryIndex != -1) {
+ queryString = searchString.substring(queryIndex + 1);
+ }
+ // We need to escape the parameters as if they were the query in a URL
+ queryString = encodeURIComponent(queryString).replace(/%20/g, "+");
+
+ // The first word could be a keyword, so that's what we'll search.
+ let keyword = aTokens[0];
+
+ let query = this._keywordQuery;
+ let params = query.params;
+ params.keyword = keyword;
+ params.query_string = queryString;
+ params.query_type = kQueryTypeKeyword;
+
+ return query;
+ },
+
+ /**
+ * Obtains the adaptive query with the properly bound parameters.
+ *
+ * @return the bound adaptive query.
+ */
+ _getBoundAdaptiveQuery: function PAC_getBoundAdaptiveQuery(aMatchBehavior)
+ {
+ // If we were not given a match behavior, use the stored match behavior.
+ if (arguments.length == 0) {
+ aMatchBehavior = this._matchBehavior;
+ }
+
+ let query = this._adaptiveQuery;
+ let params = query.params;
+ params.parent = PlacesUtils.tagsFolderId;
+ params.search_string = this._currentSearchString;
+ params.query_type = kQueryTypeFiltered;
+ params.matchBehavior = aMatchBehavior;
+ params.searchBehavior = this._behavior;
+
+ return query;
+ },
+
+ /**
+ * Processes a mozIStorageRow to generate the proper data for the AutoComplete
+ * result. This will add an entry to the current result if it matches the
+ * criteria.
+ *
+ * @param aRow
+ * The row to process.
+ * @return true if the row is accepted, and false if not.
+ */
+ _processRow: function PAC_processRow(aRow)
+ {
+ // Before we do any work, make sure this entry isn't already in our results.
+ let entryId = aRow.getResultByIndex(kQueryIndexPlaceId);
+ let escapedEntryURL = aRow.getResultByIndex(kQueryIndexURL);
+ let openPageCount = aRow.getResultByIndex(kQueryIndexOpenPageCount) || 0;
+
+ // If actions are enabled and the page is open, add only the switch-to-tab
+ // result. Otherwise, add the normal result.
+ let [url, action] = this._enableActions && openPageCount > 0 && this._hasBehavior("openpage") ?
+ ["moz-action:switchtab," + escapedEntryURL, "action "] :
+ [escapedEntryURL, ""];
+
+ if (this._inResults(entryId, url)) {
+ return false;
+ }
+
+ let entryTitle = aRow.getResultByIndex(kQueryIndexTitle) || "";
+ let entryBookmarked = aRow.getResultByIndex(kQueryIndexBookmarked);
+ let entryBookmarkTitle = entryBookmarked ?
+ aRow.getResultByIndex(kQueryIndexBookmarkTitle) : null;
+ let entryTags = aRow.getResultByIndex(kQueryIndexTags) || "";
+
+ // Always prefer the bookmark title unless it is empty
+ let title = entryBookmarkTitle || entryTitle;
+
+ let style;
+ if (aRow.getResultByIndex(kQueryIndexQueryType) == kQueryTypeKeyword) {
+ style = "keyword";
+ title = NetUtil.newURI(escapedEntryURL).host;
+ }
+
+ // We will always prefer to show tags if we have them.
+ let showTags = !!entryTags;
+
+ // However, we'll act as if a page is not bookmarked if the user wants
+ // only history and not bookmarks and there are no tags.
+ if (this._hasBehavior("history") && !this._hasBehavior("bookmark") &&
+ !showTags) {
+ showTags = false;
+ style = "favicon";
+ }
+
+ // If we have tags and should show them, we need to add them to the title.
+ if (showTags) {
+ title += kTitleTagsSeparator + entryTags;
+ }
+ // We have to determine the right style to display. Tags show the tag icon,
+ // bookmarks get the bookmark icon, and keywords get the keyword icon. If
+ // the result does not fall into any of those, it just gets the favicon.
+ if (!style) {
+ // It is possible that we already have a style set (from a keyword
+ // search or because of the user's preferences), so only set it if we
+ // haven't already done so.
+ if (showTags) {
+ style = "tag";
+ }
+ else if (entryBookmarked) {
+ style = "bookmark";
+ }
+ else {
+ style = "favicon";
+ }
+ }
+
+ this._addToResults(entryId, url, title, action + style);
+ return true;
+ },
+
+ /**
+ * Checks to see if the given place has already been added to the results.
+ *
+ * @param aPlaceId
+ * The place id to check for, may be null.
+ * @param aUrl
+ * The url to check for.
+ * @return true if the place has been added, false otherwise.
+ *
+ * @note Must check both the id and the url for a negative match, since
+ * autocomplete may run in the middle of a new page addition. In such
+ * a case the switch-to-tab query would hash the page by url, then a
+ * next query, running after the page addition, would hash it by id.
+ * It's not possible to just rely on url though, since keywords
+ * dynamically modify the url to include their search string.
+ */
+ _inResults: function PAC_inResults(aPlaceId, aUrl)
+ {
+ if (aPlaceId && aPlaceId in this._usedPlaces) {
+ return true;
+ }
+ return aUrl in this._usedPlaces;
+ },
+
+ /**
+ * Adds a result to the AutoComplete results. Also tracks that we've added
+ * this place_id into the result set.
+ *
+ * @param aPlaceId
+ * The place_id of the item to be added to the result set. This is
+ * used by _inResults.
+ * @param aURISpec
+ * The URI spec for the entry.
+ * @param aTitle
+ * The title to give the entry.
+ * @param aStyle
+ * Indicates how the entry should be styled when displayed.
+ */
+ _addToResults: function PAC_addToResults(aPlaceId, aURISpec, aTitle,
+ aStyle)
+ {
+ // Add this to our internal tracker to ensure duplicates do not end up in
+ // the result. _usedPlaces is an Object that is being used as a set.
+ // Not all entries have a place id, thus we fallback to the url for them.
+ // We cannot use only the url since keywords entries are modified to
+ // include the search string, and would be returned multiple times. Ids
+ // are faster too.
+ this._usedPlaces[aPlaceId || aURISpec] = true;
+
+ // Obtain the favicon for this URI.
+ let favicon = "page-icon:" + aURISpec;
+ this._result.appendMatch(aURISpec, aTitle, favicon, aStyle);
+ },
+
+ /**
+ * Determines if the specified AutoComplete behavior is set.
+ *
+ * @param aType
+ * The behavior type to test for.
+ * @return true if the behavior is set, false otherwise.
+ */
+ _hasBehavior: function PAC_hasBehavior(aType)
+ {
+ let behavior = Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()];
+
+ if (this._disablePrivateActions &&
+ behavior == Ci.mozIPlacesAutoComplete.BEHAVIOR_OPENPAGE) {
+ return false;
+ }
+
+ return this._behavior & behavior;
+ },
+
+ /**
+ * Enables the desired AutoComplete behavior.
+ *
+ * @param aType
+ * The behavior type to set.
+ */
+ _setBehavior: function PAC_setBehavior(aType)
+ {
+ this._behavior |=
+ Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()];
+ },
+
+ /**
+ * Determines if we are done searching or not.
+ *
+ * @return true if we have completed searching, false otherwise.
+ */
+ isSearchComplete: function PAC_isSearchComplete()
+ {
+ // If _pendingQuery is null, we should no longer do any work since we have
+ // already called _finishSearch. This means we completed our search.
+ return this._pendingQuery == null;
+ },
+
+ /**
+ * Determines if the given handle of a pending statement is a pending search
+ * or not.
+ *
+ * @param aHandle
+ * A mozIStoragePendingStatement to check and see if we are waiting for
+ * results from it still.
+ * @return true if it is a pending query, false otherwise.
+ */
+ isPendingSearch: function PAC_isPendingSearch(aHandle)
+ {
+ return this._pendingQuery == aHandle;
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsISupports
+
+ classID: Components.ID("d0272978-beab-4adc-a3d4-04b76acfa4e7"),
+
+ _xpcom_factory: XPCOMUtils.generateSingletonFactory(nsPlacesAutoComplete),
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIAutoCompleteSearch,
+ Ci.nsIAutoCompleteSimpleResultListener,
+ Ci.mozIPlacesAutoComplete,
+ Ci.mozIStorageStatementCallback,
+ Ci.nsIObserver,
+ Ci.nsISupportsWeakReference,
+ ])
+};
+
+var components = [nsPlacesAutoComplete];
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/comm/suite/components/places/nsPlacesAutoComplete.manifest b/comm/suite/components/places/nsPlacesAutoComplete.manifest
new file mode 100644
index 0000000000..77dc732af2
--- /dev/null
+++ b/comm/suite/components/places/nsPlacesAutoComplete.manifest
@@ -0,0 +1,3 @@
+component {d0272978-beab-4adc-a3d4-04b76acfa4e7} nsPlacesAutoComplete.js
+contract @mozilla.org/autocomplete/search;1?name=history {d0272978-beab-4adc-a3d4-04b76acfa4e7}
+
diff --git a/comm/suite/components/places/tests/autocomplete/head_autocomplete.js b/comm/suite/components/places/tests/autocomplete/head_autocomplete.js
new file mode 100644
index 0000000000..bccc7f56d2
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/head_autocomplete.js
@@ -0,0 +1,307 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// Import common head.
+{
+ let commonFile = do_get_file("../head_common.js", false);
+ let uri = Services.io.newFileURI(commonFile);
+ Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+// Put any other stuff relative to this test folder below.
+
+
+/**
+ * Header file for autocomplete testcases that create a set of pages with uris,
+ * titles, tags and tests that a given search term matches certain pages.
+ */
+
+var current_test = 0;
+
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ timeout: 10,
+ textValue: "",
+ searches: null,
+ searchParam: "",
+ popupOpen: false,
+ minResultsForPopup: 0,
+ invalidate: function() {},
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+ get popup() { return this; },
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+ setSelectedIndex: function() {},
+ get searchCount() { return this.searches.length; },
+ getSearchAt: function(aIndex) { return this.searches[aIndex]; },
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIAutoCompleteInput,
+ Ci.nsIAutoCompletePopup,
+ ])
+};
+
+function toURI(aSpec) {
+ return uri(aSpec);
+}
+
+var appendTags = true;
+// Helper to turn off tag matching in results
+function ignoreTags()
+{
+ print("Ignoring tags from results");
+ appendTags = false;
+}
+
+function ensure_results(aSearch, aExpected)
+{
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+ getService(Ci.nsIAutoCompleteController);
+
+ // Make an AutoCompleteInput that uses our searches
+ // and confirms results on search complete
+ let input = new AutoCompleteInput(["history"]);
+
+ controller.input = input;
+
+ if (typeof kSearchParam == "string")
+ input.searchParam = kSearchParam;
+
+ let numSearchesStarted = 0;
+ input.onSearchBegin = function() {
+ numSearchesStarted++;
+ Assert.equal(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function() {
+ Assert.equal(numSearchesStarted, 1);
+ aExpected = aExpected.slice();
+
+ // Check to see the expected uris and titles match up (in any order)
+ for (let i = 0; i < controller.matchCount; i++) {
+ let value = controller.getValueAt(i);
+ let comment = controller.getCommentAt(i);
+
+ print("Looking for '" + value + "', '" + comment + "' in expected results...");
+ let j;
+ for (j = 0; j < aExpected.length; j++) {
+ // Skip processed expected results
+ if (aExpected[j] == undefined)
+ continue;
+
+ let [uri, title, tags] = gPages[aExpected[j]];
+
+ // Load the real uri and titles and tags if necessary
+ uri = toURI(kURIs[uri]).spec;
+ title = kTitles[title];
+ if (tags && appendTags)
+ title += " \u2013 " + tags.map(aTag => kTitles[aTag]);
+ print("Checking against expected '" + uri + "', '" + title + "'...");
+
+ // Got a match on both uri and title?
+ if (uri == value && title == comment) {
+ print("Got it at index " + j + "!!");
+ // Make it undefined so we don't process it again
+ aExpected[j] = undefined;
+ break;
+ }
+ }
+
+ // We didn't hit the break, so we must have not found it
+ if (j == aExpected.length)
+ do_throw("Didn't find the current result ('" + value + "', '" + comment + "') in expected: " + aExpected);
+ }
+
+ // Make sure we have the right number of results
+ print("Expecting " + aExpected.length + " results; got " +
+ controller.matchCount + " results");
+ Assert.equal(controller.matchCount, aExpected.length);
+
+ // If we expect results, make sure we got matches
+ Assert.equal(controller.searchStatus, aExpected.length ?
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH :
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH);
+
+ // Fetch the next test if we have more
+ if (++current_test < gTests.length)
+ run_test();
+
+ do_test_finished();
+ };
+
+ print("Searching for.. '" + aSearch + "'");
+ controller.startSearch(aSearch);
+}
+
+// Get history services
+var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
+var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+var tagsvc = Cc["@mozilla.org/browser/tagging-service;1"].
+ getService(Ci.nsITaggingService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+
+// Some date not too long ago
+var gDate = new Date(Date.now() - 1000 * 60 * 60) * 1000;
+// Store the page info for each uri
+var gPages = [];
+
+// Initialization tasks to be run before the next test
+var gNextTestSetupTasks = [];
+
+/**
+ * Adds a page, and creates various properties for it depending on the
+ * parameters passed in. This function will also add one visit, unless
+ * aNoVisit is true.
+ *
+ * @param aURI
+ * An index into kURIs that holds the string for the URI we are to add a
+ * page for.
+ * @param aTitle
+ * An index into kTitles that holds the string for the title we are to
+ * associate with the specified URI.
+ * @param aBook [optional]
+ * An index into kTitles that holds the string for the title we are to
+ * associate with the bookmark. If this is undefined, no bookmark is
+ * created.
+ * @param aTags [optional]
+ * An array of indexes into kTitles that hold the strings for the tags we
+ * are to associate with the URI. If this is undefined (or aBook is), no
+ * tags are added.
+ * @param aKey [optional]
+ * A string to associate as the keyword for this bookmark. aBook must be
+ * a valid index into kTitles for this to be checked and used.
+ * @param aTransitionType [optional]
+ * The transition type to use when adding the visit. The default is
+ * nsINavHistoryService::TRANSITION_LINK.
+ * @param aNoVisit [optional]
+ * If true, no visit is added for the URI. If false or undefined, a
+ * visit is added.
+ */
+function addPageBook(aURI, aTitle, aBook, aTags, aKey, aTransitionType, aNoVisit)
+{
+ gNextTestSetupTasks.push([task_addPageBook, arguments]);
+}
+
+async function task_addPageBook(aURI, aTitle, aBook, aTags, aKey, aTransitionType, aNoVisit)
+{
+ // Add a page entry for the current uri
+ gPages[aURI] = [aURI, aBook != undefined ? aBook : aTitle, aTags];
+
+ let uri = toURI(kURIs[aURI]);
+ let title = kTitles[aTitle];
+
+ let out = [aURI, aTitle, aBook, aTags, aKey];
+ out.push("\nuri=" + kURIs[aURI]);
+ out.push("\ntitle=" + title);
+
+ // Add the page and a visit if we need to
+ if (!aNoVisit) {
+ await PlacesTestUtils.addVisits({
+ uri: uri,
+ transition: aTransitionType || TRANSITION_LINK,
+ visitDate: gDate,
+ title: title
+ });
+ out.push("\nwith visit");
+ }
+
+ // Add a bookmark if we need to
+ if (aBook != undefined) {
+ let book = kTitles[aBook];
+ let bmid = bmsvc.insertBookmark(bmsvc.unfiledBookmarksFolder, uri,
+ bmsvc.DEFAULT_INDEX, book);
+ out.push("\nbook=" + book);
+
+ // Add a keyword to the bookmark if we need to
+ if (aKey != undefined)
+ await PlacesUtils.keywords.insert({url: uri.spec, keyword: aKey});
+
+ // Add tags if we need to
+ if (aTags != undefined && aTags.length > 0) {
+ // Convert each tag index into the title
+ let tags = aTags.map(aTag => kTitles[aTag]);
+ tagsvc.tagURI(uri, tags);
+ out.push("\ntags=" + tags);
+ }
+ }
+
+ print("\nAdding page/book/tag: " + out.join(", "));
+}
+
+function run_test() {
+ print("\n");
+ // always search in history + bookmarks, no matter what the default is
+ prefs.setBoolPref("browser.urlbar.suggest.history", true);
+ prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
+ prefs.setBoolPref("browser.urlbar.suggest.openpage", true);
+ prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ // Load the test and print a description then run the test
+ let [description, search, expected, func] = gTests[current_test];
+ print(description);
+
+ // By default assume we want to match tags
+ appendTags = true;
+
+ // Do an extra function if necessary
+ if (func)
+ func();
+
+ (async function() {
+ // Iterate over all tasks and execute them
+ for (let [fn, args] of gNextTestSetupTasks) {
+ await fn.apply(this, args);
+ }
+
+ // Clean up to allow tests to register more functions.
+ gNextTestSetupTasks = [];
+
+ // At this point frecency could still be updating due to latest pages
+ // updates. This is not a problem in real life, but autocomplete tests
+ // should return reliable resultsets, thus we have to wait.
+ await PlacesTestUtils.promiseAsyncUpdates();
+
+ })().then(() => ensure_results(search, expected),
+ do_report_unexpected_exception);
+}
+
+// Utility function to remove history pages
+function removePages(aURIs)
+{
+ gNextTestSetupTasks.push([do_removePages, arguments]);
+}
+
+function do_removePages(aURIs)
+{
+ for (let uri of aURIs)
+ histsvc.removePage(toURI(kURIs[uri]));
+}
+
+// Utility function to mark pages as typed
+function markTyped(aURIs, aTitle)
+{
+ gNextTestSetupTasks.push([task_markTyped, arguments]);
+}
+
+async function task_markTyped(aURIs, aTitle)
+{
+ for (let uri of aURIs) {
+ await PlacesTestUtils.addVisits({
+ uri: toURI(kURIs[uri]),
+ transition: TRANSITION_TYPED,
+ title: kTitles[aTitle]
+ });
+ }
+}
diff --git a/comm/suite/components/places/tests/autocomplete/test_416211.js b/comm/suite/components/places/tests/autocomplete/test_416211.js
new file mode 100644
index 0000000000..8f662b5b13
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_416211.js
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/*
+ * Test bug 416211 to make sure results that match the tag show the bookmark
+ * title instead of the page title.
+ */
+
+var theTag = "superTag";
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://theuri/",
+];
+var kTitles = [
+ "Page title",
+ "Bookmark title",
+ theTag,
+];
+
+// Add page with a title, bookmark, and [tags]
+addPageBook(0, 0, 1, [2]);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Make sure the tag match gives the bookmark title",
+ theTag, [0]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_416214.js b/comm/suite/components/places/tests/autocomplete/test_416214.js
new file mode 100644
index 0000000000..f891180069
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_416214.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+/*
+ * Test autocomplete for non-English URLs that match the tag bug 416214. Also
+ * test bug 417441 by making sure escaped ascii characters like "+" remain
+ * escaped.
+ *
+ * - add a visit for a page with a non-English URL
+ * - add a tag for the page
+ * - search for the tag
+ * - test number of matches (should be exactly one)
+ * - make sure the url is decoded
+ */
+
+var theTag = "superTag";
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://escaped/ユニコード",
+ "http://asciiescaped/blocking-firefox3%2B",
+];
+var kTitles = [
+ "title",
+ theTag,
+];
+
+// Add pages that match the tag
+addPageBook(0, 0, 0, [1]);
+addPageBook(1, 0, 0, [1]);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Make sure tag matches return the right url as well as '+' remain escaped",
+ theTag, [0, 1]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_417798.js b/comm/suite/components/places/tests/autocomplete/test_417798.js
new file mode 100644
index 0000000000..6306c4125c
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_417798.js
@@ -0,0 +1,36 @@
+/* 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/. */
+
+/**
+ * Test for bug 417798 to make sure javascript: URIs don't show up unless the
+ * user searches for javascript: explicitly.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://abc/def",
+ "javascript:5",
+];
+var kTitles = [
+ "Title with javascript:",
+];
+
+addPageBook(0, 0); // regular url
+// javascript: uri as bookmark (no visit)
+addPageBook(1, 0, 0, undefined, undefined, undefined, true);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Match non-javascript: with plain search",
+ "a", [0]],
+ ["1: Match non-javascript: with almost javascript:",
+ "javascript", [0]],
+ ["2: Match javascript:",
+ "javascript:", [0, 1]],
+ ["3: Match nothing with non-first javascript:",
+ "5 javascript:", []],
+ ["4: Match javascript: with multi-word search",
+ "javascript: 5", [1]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_418257.js b/comm/suite/components/places/tests/autocomplete/test_418257.js
new file mode 100644
index 0000000000..edff19d8b0
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_418257.js
@@ -0,0 +1,43 @@
+/* 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/. */
+
+/**
+ * Test bug 418257 by making sure tags are returned with the title as part of
+ * the "comment" if there are tags even if we didn't match in the tags. They
+ * are separated from the title by a endash.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://page1",
+ "http://page2",
+ "http://page3",
+ "http://page4",
+];
+var kTitles = [
+ "tag1",
+ "tag2",
+ "tag3",
+];
+
+// Add pages with varying number of tags
+addPageBook(0, 0, 0, [0]);
+addPageBook(1, 0, 0, [0, 1]);
+addPageBook(2, 0, 0, [0, 2]);
+addPageBook(3, 0, 0, [0, 1, 2]);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Make sure tags come back in the title when matching tags",
+ "page1 tag", [0]],
+ ["1: Check tags in title for page2",
+ "page2 tag", [1]],
+ ["2: Make sure tags appear even when not matching the tag",
+ "page3", [2]],
+ ["3: Multiple tags come in commas for page4",
+ "page4", [3]],
+ ["4: Extra test just to make sure we match the title",
+ "tag2", [1, 3]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_422277.js b/comm/suite/components/places/tests/autocomplete/test_422277.js
new file mode 100644
index 0000000000..d6eb193dc8
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_422277.js
@@ -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/. */
+
+/**
+ * Test bug 422277 to make sure bad escaped uris don't get escaped. This makes
+ * sure we don't hit an assertion for "not a UTF8 string".
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://site/%EAid",
+];
+var kTitles = [
+ "title",
+];
+
+addPageBook(0, 0);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Bad escaped uri stays escaped",
+ "site", [0]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_autocomplete_on_value_removed_479089.js b/comm/suite/components/places/tests/autocomplete/test_autocomplete_on_value_removed_479089.js
new file mode 100644
index 0000000000..5969fc9f5b
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_autocomplete_on_value_removed_479089.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/*
+ * Need to test that removing a page from autocomplete actually removes a page
+ * Description From Shawn Wilsher :sdwilsh 2009-02-18 11:29:06 PST
+ * We don't test the code path of onValueRemoved
+ * for the autocomplete implementation
+ * Bug 479089
+ */
+
+var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+getService(Ci.nsINavHistoryService);
+
+function run_test()
+{
+ run_next_test();
+}
+
+add_task(async function test_autocomplete_on_value_removed()
+{
+ // QI to nsIAutoCompleteSimpleResultListener
+ var listener = Cc["@mozilla.org/autocomplete/search;1?name=history"].
+ getService(Ci.nsIAutoCompleteSimpleResultListener);
+
+ // add history visit
+ var testUri = uri("http://foo.mozilla.com/");
+ await PlacesTestUtils.addVisits({
+ uri: testUri,
+ referrer: uri("http://mozilla.com/")
+ });
+ // create a query object
+ var query = histsvc.getNewQuery();
+ // create the options object we will never use
+ var options = histsvc.getNewQueryOptions();
+ // look for this uri only
+ query.uri = testUri;
+ // execute
+ var queryRes = histsvc.executeQuery(query, options);
+ // open the result container
+ queryRes.root.containerOpen = true;
+ // debug queries
+ // dump_table("moz_places");
+ Assert.equal(queryRes.root.childCount, 1);
+ // call the untested code path
+ listener.onValueRemoved(null, testUri.spec, true);
+ // make sure it is GONE from the DB
+ Assert.equal(queryRes.root.childCount, 0);
+ // close the container
+ queryRes.root.containerOpen = false;
+});
diff --git a/comm/suite/components/places/tests/autocomplete/test_download_embed_bookmarks.js b/comm/suite/components/places/tests/autocomplete/test_download_embed_bookmarks.js
new file mode 100644
index 0000000000..65bec60cc7
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_download_embed_bookmarks.js
@@ -0,0 +1,53 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim:set ts=2 sw=2 sts=2 et:
+ * 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/. */
+
+/**
+ * Tests bug 449406 to ensure that TRANSITION_DOWNLOAD, TRANSITION_EMBED and
+ * TRANSITION_FRAMED_LINK bookmarked uri's show up in the location bar.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://download/bookmarked",
+ "http://embed/bookmarked",
+ "http://framed/bookmarked",
+ "http://download",
+ "http://embed",
+ "http://framed",
+];
+var kTitles = [
+ "download-bookmark",
+ "embed-bookmark",
+ "framed-bookmark",
+ "download2",
+ "embed2",
+ "framed2",
+];
+
+// Add download and embed uris
+addPageBook(0, 0, 0, undefined, undefined, TRANSITION_DOWNLOAD);
+addPageBook(1, 1, 1, undefined, undefined, TRANSITION_EMBED);
+addPageBook(2, 2, 2, undefined, undefined, TRANSITION_FRAMED_LINK);
+addPageBook(3, 3, undefined, undefined, undefined, TRANSITION_DOWNLOAD);
+addPageBook(4, 4, undefined, undefined, undefined, TRANSITION_EMBED);
+addPageBook(5, 5, undefined, undefined, undefined, TRANSITION_FRAMED_LINK);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Searching for bookmarked download uri matches",
+ kTitles[0], [0]],
+ ["1: Searching for bookmarked embed uri matches",
+ kTitles[1], [1]],
+ ["2: Searching for bookmarked framed uri matches",
+ kTitles[2], [2]],
+ ["3: Searching for download uri does not match",
+ kTitles[3], []],
+ ["4: Searching for embed uri does not match",
+ kTitles[4], []],
+ ["5: Searching for framed uri does not match",
+ kTitles[5], []],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_empty_search.js b/comm/suite/components/places/tests/autocomplete/test_empty_search.js
new file mode 100644
index 0000000000..df8eac383a
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_empty_search.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+/**
+ * Test for bug 426864 that makes sure the empty search (drop down list) only
+ * shows typed pages from history.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://foo/0",
+ "http://foo/1",
+ "http://foo/2",
+ "http://foo/3",
+ "http://foo/4",
+ "http://foo/5",
+];
+var kTitles = [
+ "title",
+];
+
+// Visited (in history)
+addPageBook(0, 0); // history
+addPageBook(1, 0, 0); // bookmark
+addPageBook(2, 0); // history typed
+addPageBook(3, 0, 0); // bookmark typed
+
+// Unvisited bookmark
+addPageBook(4, 0, 0); // bookmark
+addPageBook(5, 0, 0); // bookmark typed
+
+// Set some pages as typed
+markTyped([2, 3, 5], 0);
+// Remove pages from history to treat them as unvisited
+removePages([4, 5]);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Match everything",
+ "foo", [0, 1, 2, 3, 4, 5]],
+ ["1: Match only typed history",
+ "foo ^ ~", [2, 3]],
+ ["2: Drop-down empty search matches only typed history",
+ "", [2, 3]],
+ ["3: Drop-down empty search matches only bookmarks",
+ "", [2, 3], matchBookmarks],
+ ["4: Drop-down empty search matches only typed",
+ "", [2, 3], matchTyped],
+];
+
+function matchBookmarks() {
+ prefs.setBoolPref("browser.urlbar.suggest.history", false);
+ prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
+ clearPrefs();
+}
+
+function matchTyped() {
+ prefs.setBoolPref("browser.urlbar.suggest.history", true);
+ prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
+ clearPrefs();
+}
+
+function clearPrefs() {
+ prefs.clearUserPref("browser.urlbar.suggest.history");
+ prefs.clearUserPref("browser.urlbar.suggest.bookmark");
+ prefs.clearUserPref("browser.urlbar.suggest.history.onlyTyped");
+}
diff --git a/comm/suite/components/places/tests/autocomplete/test_enabled.js b/comm/suite/components/places/tests/autocomplete/test_enabled.js
new file mode 100644
index 0000000000..a12242763f
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_enabled.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+/**
+ * Test for bug 471903 to make sure searching in autocomplete can be turned on
+ * and off. Also test bug 463535 for pref changing search.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://url/0",
+];
+var kTitles = [
+ "title",
+];
+
+addPageBook(0, 0); // visited page
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["1: plain search",
+ "url", [0]],
+ ["2: search disabled",
+ "url", [], () => setSearch(0)],
+ ["3: resume normal search",
+ "url", [0], () => setSearch(1)],
+];
+
+function setSearch(aSearch) {
+ prefs.setBoolPref("browser.urlbar.autocomplete.enabled", !!aSearch);
+}
+
+add_task(async function test_sync_enabled() {
+ // Initialize autocomplete component.
+ Cc["@mozilla.org/autocomplete/search;1?name=history"]
+ .getService(Ci.mozIPlacesAutoComplete);
+
+ let types = [ "history", "bookmark", "openpage" ];
+
+ // Test the service keeps browser.urlbar.autocomplete.enabled synchronized
+ // with browser.urlbar.suggest prefs.
+ for (let type of types) {
+ Services.prefs.setBoolPref("browser.urlbar.suggest." + type, true);
+ }
+ Assert.equal(Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled"), true);
+
+ // Disable autocomplete and check all the suggest prefs are set to false.
+ Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", false);
+ for (let type of types) {
+ Assert.equal(Services.prefs.getBoolPref("browser.urlbar.suggest." + type), false);
+ }
+
+ // Setting even a single suggest pref to true should enable autocomplete.
+ Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+ for (let type of types.filter(t => t != "history")) {
+ Assert.equal(Services.prefs.getBoolPref("browser.urlbar.suggest." + type), false);
+ }
+ Assert.equal(Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled"), true);
+
+ // Disable autocoplete again, then re-enable it and check suggest prefs
+ // have been reset.
+ Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", false);
+ Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", true);
+ for (let type of types.filter(t => t != "history")) {
+ Assert.equal(Services.prefs.getBoolPref("browser.urlbar.suggest." + type), true);
+ }
+});
diff --git a/comm/suite/components/places/tests/autocomplete/test_escape_self.js b/comm/suite/components/places/tests/autocomplete/test_escape_self.js
new file mode 100644
index 0000000000..0b0918b8ff
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_escape_self.js
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/**
+ * Test bug 422698 to make sure searches with urls from the location bar
+ * correctly match itself when it contains escaped characters.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://unescapeduri/",
+ "http://escapeduri/%40/",
+];
+var kTitles = [
+ "title",
+];
+
+// Add unescaped and escaped uris
+addPageBook(0, 0);
+addPageBook(1, 0);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Unescaped location matches itself",
+ kURIs[0], [0]],
+ ["1: Escaped location matches itself",
+ kURIs[1], [1]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_ignore_protocol.js b/comm/suite/components/places/tests/autocomplete/test_ignore_protocol.js
new file mode 100644
index 0000000000..2ad63b735f
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_ignore_protocol.js
@@ -0,0 +1,27 @@
+/* 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/. */
+
+/**
+ * Test bug 424509 to make sure searching for "h" doesn't match "http" of urls.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://site/",
+ "http://happytimes/",
+];
+var kTitles = [
+ "title",
+];
+
+// Add site without "h" and with "h"
+addPageBook(0, 0);
+addPageBook(1, 0);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Searching for h matches site and not http://",
+ "h", [1]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_keyword_search.js b/comm/suite/components/places/tests/autocomplete/test_keyword_search.js
new file mode 100644
index 0000000000..796f386bb8
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_keyword_search.js
@@ -0,0 +1,73 @@
+/* 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/. */
+
+/**
+ * Test for bug 392143 that puts keyword results into the autocomplete. Makes
+ * sure that multiple parameter queries get spaces converted to +, + converted
+ * to %2B, non-ascii become escaped, and pages in history that match the
+ * keyword uses the page's title.
+ *
+ * Also test for bug 249468 by making sure multiple keyword bookmarks with the
+ * same keyword appear in the list.
+ */
+
+// Details for the keyword bookmark
+var keyBase = "http://abc/?search=";
+var keyKey = "key";
+
+// A second keyword bookmark with the same keyword
+var otherBase = "http://xyz/?foo=";
+
+var unescaped = "ユニコード";
+var pageInHistory = "ThisPageIsInHistory";
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ keyBase + "%s",
+ keyBase + "term",
+ keyBase + "multi+word",
+ keyBase + "blocking%2B",
+ keyBase + unescaped,
+ keyBase + pageInHistory,
+ keyBase,
+ otherBase + "%s",
+ keyBase + "twoKey",
+ otherBase + "twoKey"
+];
+var kTitles = [
+ "Generic page title",
+ "Keyword title",
+ "abc",
+ "xyz"
+];
+
+// Add the keyword bookmark
+addPageBook(0, 0, 1, [], keyKey);
+// Add in the "fake pages" for keyword searches
+gPages[1] = [1, 2];
+gPages[2] = [2, 2];
+gPages[3] = [3, 2];
+gPages[4] = [4, 2];
+// Add a page into history
+addPageBook(5, 2);
+gPages[6] = [6, 2];
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Plain keyword query",
+ keyKey + " term", [1]],
+ ["1: Multi-word keyword query",
+ keyKey + " multi word", [2]],
+ ["2: Keyword query with +",
+ keyKey + " blocking+", [3]],
+ ["3: Unescaped term in query",
+ keyKey + " " + unescaped, [4]],
+ ["4: Keyword that happens to match a page",
+ keyKey + " " + pageInHistory, [5]],
+ ["5: Keyword without query (without space)",
+ keyKey, [6]],
+ ["6: Keyword without query (with space)",
+ keyKey + " ", [6]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_match_beginning.js b/comm/suite/components/places/tests/autocomplete/test_match_beginning.js
new file mode 100644
index 0000000000..b9ba3ab39b
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_match_beginning.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+/**
+ * Test bug 451760 which allows matching only at the beginning of urls or
+ * titles to simulate Firefox 2 functionality.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://x.com/y",
+ "https://y.com/x",
+];
+var kTitles = [
+ "a b",
+ "b a",
+];
+
+addPageBook(0, 0);
+addPageBook(1, 1);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ // Tests after this one will match at the beginning
+ ["0: Match at the beginning of titles",
+ "a", [0],
+ () => setBehavior(3)],
+ ["1: Match at the beginning of titles",
+ "b", [1]],
+ ["2: Match at the beginning of urls",
+ "x", [0]],
+ ["3: Match at the beginning of urls",
+ "y", [1]],
+
+ // Tests after this one will match against word boundaries and anywhere
+ ["4: Sanity check that matching anywhere finds more",
+ "a", [0, 1],
+ () => setBehavior(1)],
+];
+
+function setBehavior(aType) {
+ prefs.setIntPref("browser.urlbar.matchBehavior", aType);
+}
diff --git a/comm/suite/components/places/tests/autocomplete/test_multi_word_search.js b/comm/suite/components/places/tests/autocomplete/test_multi_word_search.js
new file mode 100644
index 0000000000..c0b896c868
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_multi_word_search.js
@@ -0,0 +1,49 @@
+/* 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/. */
+
+/**
+ * Test for bug 401869 to allow multiple words separated by spaces to match in
+ * the page title, page url, or bookmark title to be considered a match. All
+ * terms must match but not all terms need to be in the title, etc.
+ *
+ * Test bug 424216 by making sure bookmark titles are always shown if one is
+ * available. Also bug 425056 makes sure matches aren't found partially in the
+ * page title and partially in the bookmark.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://a.b.c/d-e_f/h/t/p",
+ "http://d.e.f/g-h_i/h/t/p",
+ "http://g.h.i/j-k_l/h/t/p",
+ "http://j.k.l/m-n_o/h/t/p",
+];
+var kTitles = [
+ "f(o)o b<a>r",
+ "b(a)r b<a>z",
+];
+
+// Regular pages
+addPageBook(0, 0);
+addPageBook(1, 1);
+// Bookmarked pages
+addPageBook(2, 0, 0);
+addPageBook(3, 0, 1);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: Match 2 terms all in url",
+ "c d", [0]],
+ ["1: Match 1 term in url and 1 term in title",
+ "b e", [0, 1]],
+ ["2: Match 3 terms all in title; display bookmark title if matched",
+ "b a z", [1, 3]],
+ ["3: Match 2 terms in url and 1 in title; make sure bookmark title is used for search",
+ "k f t", [2]],
+ ["4: Match 3 terms in url and 1 in title",
+ "d i g z", [1]],
+ ["5: Match nothing",
+ "m o z i", []],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_special_search.js b/comm/suite/components/places/tests/autocomplete/test_special_search.js
new file mode 100644
index 0000000000..78bf5a7d64
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_special_search.js
@@ -0,0 +1,183 @@
+/* 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/. */
+
+/**
+ * Test for bug 395161 that allows special searches that restrict results to
+ * history/bookmark/tagged items and title/url matches.
+ *
+ * Test 485122 by making sure results don't have tags when restricting result
+ * to just history either by default behavior or dynamic query restrict.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://url/",
+ "http://url/2",
+ "http://foo.bar/",
+ "http://foo.bar/2",
+ "http://url/star",
+ "http://url/star/2",
+ "http://foo.bar/star",
+ "http://foo.bar/star/2",
+ "http://url/tag",
+ "http://url/tag/2",
+ "http://foo.bar/tag",
+ "http://foo.bar/tag/2",
+];
+var kTitles = [
+ "title",
+ "foo.bar",
+];
+
+// Plain page visits
+addPageBook(0, 0); // plain page
+addPageBook(1, 1); // title
+addPageBook(2, 0); // url
+addPageBook(3, 1); // title and url
+
+// Bookmarked pages (no tag)
+addPageBook(4, 0, 0); // bookmarked page
+addPageBook(5, 1, 1); // title
+addPageBook(6, 0, 0); // url
+addPageBook(7, 1, 1); // title and url
+
+// Tagged pages
+addPageBook(8, 0, 0, [1]); // tagged page
+addPageBook(9, 1, 1, [1]); // title
+addPageBook(10, 0, 0, [1]); // url
+addPageBook(11, 1, 1, [1]); // title and url
+
+// Remove pages from history to treat them as unvisited, so pages that do have
+// visits are 0,1,2,3,5,10
+removePages([4, 6, 7, 8, 9, 11]);
+// Set some pages as typed
+markTyped([0, 10], 0);
+markTyped([3], 1);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ // Test restricting searches
+ ["0: History restrict",
+ "^", [0, 1, 2, 3, 5, 10]],
+ ["1: Star restrict",
+ "*", [4, 5, 6, 7, 8, 9, 10, 11]],
+ ["2: Tag restrict",
+ "+", [8, 9, 10, 11]],
+
+ // Test specials as any word position
+ ["3: Special as first word",
+ "^ foo bar", [1, 2, 3, 5, 10]],
+ ["4: Special as middle word",
+ "foo ^ bar", [1, 2, 3, 5, 10]],
+ ["5: Special as last word",
+ "foo bar ^", [1, 2, 3, 5, 10]],
+
+ // Test restricting and matching searches with a term
+ ["6.1: foo ^ -> history",
+ "foo ^", [1, 2, 3, 5, 10]],
+ ["6.2: foo | -> history (change pref)",
+ "foo |", [1, 2, 3, 5, 10], () => changeRestrict("history", "|")],
+ ["7.1: foo * -> is star",
+ "foo *", [5, 6, 7, 8, 9, 10, 11], () => resetRestrict("history")],
+ ["7.2: foo | -> is star (change pref)",
+ "foo |", [5, 6, 7, 8, 9, 10, 11], () => changeRestrict("bookmark", "|")],
+ ["8.1: foo # -> in title",
+ "foo #", [1, 3, 5, 7, 8, 9, 10, 11], () => resetRestrict("bookmark")],
+ ["8.2: foo | -> in title (change pref)",
+ "foo |", [1, 3, 5, 7, 8, 9, 10, 11], () => changeRestrict("title", "|")],
+ ["9.1: foo @ -> in url",
+ "foo @", [2, 3, 6, 7, 10, 11], () => resetRestrict("title")],
+ ["9.2: foo | -> in url (change pref)",
+ "foo |", [2, 3, 6, 7, 10, 11], () => changeRestrict("url", "|")],
+ ["10: foo + -> is tag",
+ "foo +", [8, 9, 10, 11], () => resetRestrict("url")],
+ ["10.2: foo | -> is tag (change pref)",
+ "foo |", [8, 9, 10, 11], () => changeRestrict("tag", "|")],
+ ["10.3: foo ~ -> is typed",
+ "foo ~", [3, 10], () => resetRestrict("tag")],
+ ["10.4: foo | -> is typed (change pref)",
+ "foo |", [3, 10], () => changeRestrict("typed", "|")],
+
+ // Test various pairs of special searches
+ ["11: foo ^ * -> history, is star",
+ "foo ^ *", [5, 10], () => resetRestrict("typed")],
+ ["12: foo ^ # -> history, in title",
+ "foo ^ #", [1, 3, 5, 10]],
+ ["13: foo ^ @ -> history, in url",
+ "foo ^ @", [2, 3, 10]],
+ ["14: foo ^ + -> history, is tag",
+ "foo ^ +", [10]],
+ ["14.1: foo ^ ~ -> history, is typed",
+ "foo ^ ~", [3, 10]],
+ ["15: foo * # -> is star, in title",
+ "foo * #", [5, 7, 8, 9, 10, 11]],
+ ["16: foo * @ -> is star, in url",
+ "foo * @", [6, 7, 10, 11]],
+ ["17: foo * + -> same as +",
+ "foo * +", [8, 9, 10, 11]],
+ ["17.1: foo * ~ -> is star, is typed",
+ "foo * ~", [10]],
+ ["18: foo # @ -> in title, in url",
+ "foo # @", [3, 7, 10, 11]],
+ ["19: foo # + -> in title, is tag",
+ "foo # +", [8, 9, 10, 11]],
+ ["19.1: foo # ~ -> in title, is typed",
+ "foo # ~", [3, 10]],
+ ["20: foo @ + -> in url, is tag",
+ "foo @ +", [10, 11]],
+ ["20.1: foo @ ~ -> in url, is typed",
+ "foo @ ~", [3, 10]],
+ ["20.2: foo + ~ -> is tag, is typed",
+ "foo + ~", [10]],
+
+ // Test default usage by setting certain bits of default.behavior to 1
+ ["21: foo -> default history",
+ "foo", [1, 2, 3, 5, 10], function () { setPref({ history: true }); }],
+ ["22: foo -> default history or is star",
+ "foo", [1, 2, 3, 5, 6, 7, 8, 9, 10, 11], () => setPref({ history: true, bookmark: true })],
+ ["22.1: foo -> default history or is star, is typed",
+ "foo", [3, 10], () => setPref({ history: true, bookmark: true, "history.onlyTyped": true })],
+
+];
+
+function setPref(aTypes) {
+ clearSuggestPrefs();
+ for (let type in aTypes) {
+ prefs.setBoolPref("browser.urlbar.suggest." + type, aTypes[type]);
+ }
+}
+
+function clearSuggestPrefs() {
+ prefs.setBoolPref("browser.urlbar.suggest.history", false);
+ prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
+ prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
+ prefs.setBoolPref("browser.urlbar.suggest.openpage", false);
+}
+
+function changeRestrict(aType, aChar)
+{
+ let branch = "browser.urlbar.";
+ // "title" and "url" are different from everything else, so special case them.
+ if (aType == "title" || aType == "url")
+ branch += "match.";
+ else
+ branch += "restrict.";
+
+ print("changing restrict for " + aType + " to '" + aChar + "'");
+ prefs.setCharPref(branch + aType, aChar);
+}
+
+function resetRestrict(aType)
+{
+ let branch = "browser.urlbar.";
+ // "title" and "url" are different from everything else, so special case them.
+ if (aType == "title" || aType == "url")
+ branch += "match.";
+ else
+ branch += "restrict.";
+
+ if (prefs.prefHasUserValue(branch + aType))
+ prefs.clearUserPref(branch + aType);
+}
diff --git a/comm/suite/components/places/tests/autocomplete/test_swap_protocol.js b/comm/suite/components/places/tests/autocomplete/test_swap_protocol.js
new file mode 100644
index 0000000000..860b722498
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_swap_protocol.js
@@ -0,0 +1,63 @@
+/* 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/. */
+
+/**
+ * Test bug 424717 to make sure searching with an existing location like
+ * http://site/ also matches https://site/ or ftp://site/. Same thing for
+ * ftp://site/ and https://site/.
+ *
+ * Test bug 461483 to make sure a search for "w" doesn't match the "www." from
+ * site subdomains.
+ */
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://www.site/",
+ "http://site/",
+ "ftp://ftp.site/",
+ "ftp://site/",
+ "https://www.site/",
+ "https://site/",
+ "http://woohoo/",
+ "http://wwwwwwacko/",
+];
+var kTitles = [
+ "title",
+];
+
+// Add various protocols of site
+addPageBook(0, 0);
+addPageBook(1, 0);
+addPageBook(2, 0);
+addPageBook(3, 0);
+addPageBook(4, 0);
+addPageBook(5, 0);
+addPageBook(6, 0);
+addPageBook(7, 0);
+
+var allSite = [0, 1, 2, 3, 4, 5];
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ ["0: http://www.site matches all site", "http://www.site", allSite],
+ ["1: http://site matches all site", "http://site", allSite],
+ ["2: ftp://ftp.site matches itself", "ftp://ftp.site", [2]],
+ ["3: ftp://site matches all site", "ftp://site", allSite],
+ ["4: https://www.site matches all site", "https://www.site", allSite],
+ ["5: https://site matches all site", "https://site", allSite],
+ ["6: www.site matches all site", "www.site", allSite],
+
+ ["7: w matches none of www.", "w", [6, 7]],
+ ["8: http://w matches none of www.", "w", [6, 7]],
+ ["9: http://www.w matches none of www.", "w", [6, 7]],
+
+ ["10: ww matches none of www.", "ww", [7]],
+ ["11: http://ww matches none of www.", "http://ww", [7]],
+ ["12: http://www.ww matches none of www.", "http://www.ww", [7]],
+
+ ["13: www matches none of www.", "www", [7]],
+ ["14: http://www matches none of www.", "http://www", [7]],
+ ["15: http://www.www matches none of www.", "http://www.www", [7]],
+];
diff --git a/comm/suite/components/places/tests/autocomplete/test_tabmatches.js b/comm/suite/components/places/tests/autocomplete/test_tabmatches.js
new file mode 100644
index 0000000000..22f83a86e6
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_tabmatches.js
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim:set ts=2 sw=2 sts=2 et:
+ * 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 gTabRestrictChar = "%";
+prefs.setCharPref("browser.urlbar.restrict.openpage", gTabRestrictChar);
+registerCleanupFunction(() => {
+ prefs.clearUserPref("browser.urlbar.restrict.openpage");
+});
+
+var kSearchParam = "enable-actions";
+
+var kURIs = [
+ "http://abc.com/",
+ "moz-action:switchtab,http://abc.com/",
+ "http://xyz.net/",
+ "moz-action:switchtab,http://xyz.net/",
+ "about:mozilla",
+ "moz-action:switchtab,about:mozilla",
+ "data:text/html,test",
+ "moz-action:switchtab,data:text/html,test"
+];
+
+var kTitles = [
+ "ABC rocks",
+ "xyz.net - we're better than ABC",
+ "about:mozilla",
+ "data:text/html,test"
+];
+
+addPageBook(0, 0);
+gPages[1] = [1, 0];
+addPageBook(2, 1);
+gPages[3] = [3, 1];
+
+addOpenPages(0, 1);
+
+// PAges that cannot be registered in history.
+addOpenPages(4, 1);
+gPages[5] = [5, 2];
+addOpenPages(6, 1);
+gPages[7] = [7, 3];
+
+var gTests = [
+ ["0: single result, that is also a tab match",
+ "abc.com", [1]],
+ ["1: two results, one tab match",
+ "abc", [1, 2]],
+ ["2: two results, both tab matches",
+ "abc", [1, 3],
+ function() {
+ addOpenPages(2, 1);
+ }],
+ ["3: two results, both tab matches, one has multiple tabs",
+ "abc", [1, 3],
+ function() {
+ addOpenPages(2, 5);
+ }],
+ ["4: two results, no tab matches",
+ "abc", [0, 2],
+ function() {
+ removeOpenPages(0, 1);
+ removeOpenPages(2, 6);
+ }],
+ ["5: tab match search with restriction character",
+ gTabRestrictChar + " abc", [1],
+ function() {
+ addOpenPages(0, 1);
+ }],
+ ["6: tab match with not-addable pages",
+ "mozilla", [5]],
+ ["7: tab match with not-addable pages and restriction character",
+ gTabRestrictChar + " mozilla", [5]],
+ ["8: tab match with not-addable pages and only restriction character",
+ gTabRestrictChar, [1, 5, 7]],
+];
+
+
+function addOpenPages(aUri, aCount) {
+ let num = aCount || 1;
+ let acprovider = Cc["@mozilla.org/autocomplete/search;1?name=history"].
+ getService(Ci.mozIPlacesAutoComplete);
+ for (let i = 0; i < num; i++) {
+ acprovider.registerOpenPage(toURI(kURIs[aUri]));
+ }
+}
+
+function removeOpenPages(aUri, aCount) {
+ let num = aCount || 1;
+ let acprovider = Cc["@mozilla.org/autocomplete/search;1?name=history"].
+ getService(Ci.mozIPlacesAutoComplete);
+ for (let i = 0; i < num; i++) {
+ acprovider.unregisterOpenPage(toURI(kURIs[aUri]));
+ }
+}
diff --git a/comm/suite/components/places/tests/autocomplete/test_word_boundary_search.js b/comm/suite/components/places/tests/autocomplete/test_word_boundary_search.js
new file mode 100644
index 0000000000..b4ae368491
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/test_word_boundary_search.js
@@ -0,0 +1,105 @@
+/* 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/. */
+
+/**
+ * Test bug 393678 to make sure matches against the url, title, tags are only
+ * made on word boundaries instead of in the middle of words.
+ *
+ * Make sure we don't try matching one after a CamelCase because the upper-case
+ * isn't really a word boundary. (bug 429498)
+ *
+ * Bug 429531 provides switching between "must match on word boundary" and "can
+ * match," so leverage "must match" pref for checking word boundary logic and
+ * make sure "can match" matches anywhere.
+ */
+
+var katakana = ["\u30a8", "\u30c9"]; // E, Do
+var ideograph = ["\u4efb", "\u5929", "\u5802"]; // Nin Ten Do
+
+// Define some shared uris and titles (each page needs its own uri)
+var kURIs = [
+ "http://matchme/",
+ "http://dontmatchme/",
+ "http://title/1",
+ "http://title/2",
+ "http://tag/1",
+ "http://tag/2",
+ "http://crazytitle/",
+ "http://katakana/",
+ "http://ideograph/",
+ "http://camel/pleaseMatchMe/",
+];
+var kTitles = [
+ "title1",
+ "matchme2",
+ "dontmatchme3",
+ "!@#$%^&*()_+{}|:<>?word",
+ katakana.join(""),
+ ideograph.join(""),
+];
+
+// Boundaries on the url
+addPageBook(0, 0);
+addPageBook(1, 0);
+// Boundaries on the title
+addPageBook(2, 1);
+addPageBook(3, 2);
+// Boundaries on the tag
+addPageBook(4, 0, 0, [1]);
+addPageBook(5, 0, 0, [2]);
+// Lots of word boundaries before a word
+addPageBook(6, 3);
+// Katakana
+addPageBook(7, 4);
+// Ideograph
+addPageBook(8, 5);
+// CamelCase
+addPageBook(9, 0);
+
+// Provide for each test: description; search terms; array of gPages indices of
+// pages that should match; optional function to be run before the test
+var gTests = [
+ // Tests after this one will match only on word boundaries
+ ["0: Match 'match' at the beginning or after / or on a CamelCase",
+ "match", [0, 2, 4, 9],
+ () => setBehavior(2)],
+ ["1: Match 'dont' at the beginning or after /",
+ "dont", [1, 3, 5]],
+ ["2: Match '2' after the slash and after a word (in tags too)",
+ "2", [2, 3, 4, 5]],
+ ["3: Match 't' at the beginning or after /",
+ "t", [0, 1, 2, 3, 4, 5, 9]],
+ ["4: Match 'word' after many consecutive word boundaries",
+ "word", [6]],
+ ["5: Match a word boundary '/' for everything",
+ "/", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]],
+ ["6: Match word boundaries '()_+' that are among word boundaries",
+ "()_+", [6]],
+
+ ["7: Katakana characters form a string, so match the beginning",
+ katakana[0], [7]],
+ /*["8: Middle of a katakana word shouldn't be matched",
+ katakana[1], []],*/
+
+ ["9: Ideographs are treated as words so 'nin' is one word",
+ ideograph[0], [8]],
+ ["10: Ideographs are treated as words so 'ten' is another word",
+ ideograph[1], [8]],
+ ["11: Ideographs are treated as words so 'do' is yet another",
+ ideograph[2], [8]],
+
+ ["12: Extra negative assert that we don't match in the middle",
+ "ch", []],
+ ["13: Don't match one character after a camel-case word boundary (bug 429498)",
+ "atch", []],
+
+ // Tests after this one will match against word boundaries and anywhere
+ ["14: Match on word boundaries as well as anywhere (bug 429531)",
+ "tch", [0, 1, 2, 3, 4, 5, 9],
+ () => setBehavior(1)],
+];
+
+function setBehavior(aType) {
+ prefs.setIntPref("browser.urlbar.matchBehavior", aType);
+}
diff --git a/comm/suite/components/places/tests/autocomplete/xpcshell.ini b/comm/suite/components/places/tests/autocomplete/xpcshell.ini
new file mode 100644
index 0000000000..7c018dbcc7
--- /dev/null
+++ b/comm/suite/components/places/tests/autocomplete/xpcshell.ini
@@ -0,0 +1,29 @@
+[DEFAULT]
+head = head_autocomplete.js
+tail =
+skip-if = toolkit == 'android' || toolkit == 'gonk'
+
+[test_416211.js]
+[test_416214.js]
+[test_417798.js]
+[test_418257.js]
+[test_422277.js]
+[test_autocomplete_on_value_removed_479089.js]
+# Bug 676989: test fails consistently on Android
+fail-if = os == "android"
+[test_download_embed_bookmarks.js]
+# Bug 676989: test fails consistently on Android
+fail-if = os == "android"
+[test_empty_search.js]
+# Bug 676989: test fails consistently on Android
+fail-if = os == "android"
+[test_enabled.js]
+[test_escape_self.js]
+[test_ignore_protocol.js]
+[test_keyword_search.js]
+[test_match_beginning.js]
+[test_multi_word_search.js]
+[test_special_search.js]
+[test_swap_protocol.js]
+[test_tabmatches.js]
+[test_word_boundary_search.js]
diff --git a/comm/suite/components/places/tests/browser/browser.ini b/comm/suite/components/places/tests/browser/browser.ini
new file mode 100644
index 0000000000..f21d4b73a2
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files = head.js
+
+[browser_0_library_left_pane_migration.js]
+[browser_425884.js]
+[browser_drag_bookmarks_on_toolbar.js]
+[browser_library_infoBox.js]
+[browser_library_left_pane_commands.js]
+[browser_library_left_pane_fixnames.js]
+[browser_library_open_leak.js]
+[browser_library_views_liveupdate.js]
+[browser_sort_in_library.js]
diff --git a/comm/suite/components/places/tests/browser/browser_0_library_left_pane_migration.js b/comm/suite/components/places/tests/browser/browser_0_library_left_pane_migration.js
new file mode 100644
index 0000000000..1db96f5000
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_0_library_left_pane_migration.js
@@ -0,0 +1,93 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Test we correctly migrate Library left pane to the latest version.
+ * Note: this test MUST be the first between browser chrome tests, or results
+ * of next tests could be unexpected due to PlacesUIUtils getters.
+ */
+
+const TEST_URI = "http://www.mozilla.org/";
+
+function onLibraryReady(organizer) {
+ // Check left pane.
+ ok(PlacesUIUtils.leftPaneFolderId > 0,
+ "Left pane folder correctly created");
+ var leftPaneItems =
+ PlacesUtils.annotations
+ .getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(leftPaneItems.length, 1,
+ "We correctly have only 1 left pane folder");
+ var leftPaneRoot = leftPaneItems[0];
+ is(leftPaneRoot, PlacesUIUtils.leftPaneFolderId,
+ "leftPaneFolderId getter has correct value");
+ // Check version has been upgraded.
+ var version =
+ PlacesUtils.annotations.getItemAnnotation(leftPaneRoot,
+ PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
+ "Left pane version has been correctly upgraded");
+
+ // Check left pane is populated.
+ organizer.PlacesOrganizer.selectLeftPaneQuery('AllBookmarks');
+ is(organizer.PlacesOrganizer._places.selectedNode.itemId,
+ PlacesUIUtils.leftPaneQueries["AllBookmarks"],
+ "Library left pane is populated and working");
+
+ // Close Library window.
+ organizer.close();
+ // No need to cleanup anything, we have a correct left pane now.
+ finish();
+}
+
+function test() {
+ waitForExplicitFinish();
+ // Sanity checks.
+ ok(PlacesUtils, "PlacesUtils is running in chrome context");
+ ok(PlacesUIUtils, "PlacesUIUtils is running in chrome context");
+ ok(PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION > 0,
+ "Left pane version in chrome context, current version is: " + PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION );
+
+ // Check if we have any left pane folder already set, remove it eventually.
+ var leftPaneItems = PlacesUtils.annotations
+ .getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ if (leftPaneItems.length > 0) {
+ // The left pane has already been created, touching it now would cause
+ // next tests to rely on wrong values (and possibly crash)
+ is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
+ // Check version.
+ var version = PlacesUtils.annotations.getItemAnnotation(leftPaneItems[0],
+ PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, "Left pane version is actual");
+ ok(true, "left pane has already been created, skipping test");
+ finish();
+ return;
+ }
+
+ // Create a fake left pane folder with an old version (current version - 1).
+ var fakeLeftPaneRoot =
+ PlacesUtils.bookmarks.createFolder(PlacesUtils.placesRootId, "",
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(fakeLeftPaneRoot,
+ PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
+ PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION - 1,
+ 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+
+ // Check fake left pane root has been correctly created.
+ var leftPaneItems =
+ PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
+ is(leftPaneItems[0], fakeLeftPaneRoot, "left pane root itemId is correct");
+
+ // Check version.
+ var version = PlacesUtils.annotations.getItemAnnotation(fakeLeftPaneRoot,
+ PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION - 1, "Left pane version correctly set");
+
+ // Open Library, this will upgrade our left pane version.
+ openLibrary(onLibraryReady);
+}
diff --git a/comm/suite/components/places/tests/browser/browser_425884.js b/comm/suite/components/places/tests/browser/browser_425884.js
new file mode 100644
index 0000000000..4140ac0234
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_425884.js
@@ -0,0 +1,103 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+function test() {
+ // sanity check
+ ok(PlacesUtils, "checking PlacesUtils, running in chrome context?");
+ ok(PlacesUIUtils, "checking PlacesUIUtils, running in chrome context?");
+
+ /*
+ Deep copy of bookmark data, using the front-end codepath:
+
+ - create test folder A
+ - add a subfolder to folder A, and add items to it
+ - validate folder A (sanity check)
+ - copy folder A, creating new folder B, using the front-end path
+ - validate folder B
+ - undo copy transaction
+ - validate folder B (empty)
+ - redo copy transaction
+ - validate folder B's contents
+
+ */
+
+ var toolbarId = PlacesUtils.toolbarFolderId;
+ var toolbarNode = PlacesUtils.getFolderContents(toolbarId).root;
+
+ var oldCount = toolbarNode.childCount;
+ var testRootId = PlacesUtils.bookmarks.createFolder(toolbarId, "test root", -1);
+ is(toolbarNode.childCount, oldCount+1, "confirm test root node is a container, and is empty");
+ var testRootNode = toolbarNode.getChild(toolbarNode.childCount-1);
+ testRootNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
+ testRootNode.containerOpen = true;
+ is(testRootNode.childCount, 0, "confirm test root node is a container, and is empty");
+
+ // create folder A, fill it, validate its contents
+ var folderAId = PlacesUtils.bookmarks.createFolder(testRootId, "A", -1);
+ populate(folderAId);
+ var folderANode = PlacesUtils.getFolderContents(folderAId).root;
+ validate(folderANode);
+ is(testRootNode.childCount, 1, "create test folder");
+
+ // copy it, using the front-end helper functions
+ var serializedNode = PlacesUtils.wrapNode(folderANode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER);
+ var rawNode = PlacesUtils.unwrapNodes(serializedNode, PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER).shift();
+ // confirm serialization
+ ok(rawNode.type, "confirm json node");
+ folderANode.containerOpen = false;
+
+ var transaction = PlacesUIUtils.makeTransaction(rawNode,
+ PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
+ testRootId,
+ -1,
+ true);
+ ok(transaction, "create transaction");
+ PlacesUtils.transactionManager.doTransaction(transaction);
+ // confirm copy
+ is(testRootNode.childCount, 2, "create test folder via copy");
+
+ // validate the copy
+ var folderBNode = testRootNode.getChild(1);
+ validate(folderBNode);
+
+ // undo the transaction, confirm the removal
+ PlacesUtils.transactionManager.undoTransaction();
+ is(testRootNode.childCount, 1, "confirm undo removed the copied folder");
+
+ // redo the transaction
+ PlacesUtils.transactionManager.redoTransaction();
+ is(testRootNode.childCount, 2, "confirm redo re-copied the folder");
+ folderBNode = testRootNode.getChild(1);
+ validate(folderBNode);
+
+ // Close containers, cleaning up their observers.
+ testRootNode.containerOpen = false;
+ toolbarNode.containerOpen = false;
+
+ // clean up
+ PlacesUtils.transactionManager.undoTransaction();
+ PlacesUtils.bookmarks.removeItem(folderAId);
+}
+
+function populate(aFolderId) {
+ var folderId = PlacesUtils.bookmarks.createFolder(aFolderId, "test folder", -1);
+ PlacesUtils.bookmarks.insertBookmark(folderId, PlacesUtils._uri("http://foo"), -1, "test bookmark");
+ PlacesUtils.bookmarks.insertSeparator(folderId, -1);
+}
+
+function validate(aNode) {
+ PlacesUtils.asContainer(aNode);
+ aNode.containerOpen = true;
+ is(aNode.childCount, 1, "confirm child count match");
+ var folderNode = aNode.getChild(0);
+ is(folderNode.title, "test folder", "confirm folder title");
+ PlacesUtils.asContainer(folderNode);
+ folderNode.containerOpen = true;
+ is(folderNode.childCount, 2, "confirm child count match");
+ var bookmarkNode = folderNode.getChild(0);
+ var separatorNode = folderNode.getChild(1);
+ folderNode.containerOpen = false;
+ aNode.containerOpen = false;
+}
diff --git a/comm/suite/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js b/comm/suite/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js
new file mode 100644
index 0000000000..62af31ccf7
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js
@@ -0,0 +1,233 @@
+/* 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 TEST_URL = "http://www.mozilla.org";
+const TEST_TITLE = "example_title";
+
+var gBookmarksToolbar = window.document.getElementById("PlacesToolbar");
+var dragDirections = { LEFT: 0, UP: 1, RIGHT: 2, DOWN: 3 };
+
+/**
+ * Tests dragging on toolbar.
+ *
+ * We must test these 2 cases:
+ * - Dragging toward left, top, right should start a drag.
+ * - Dragging toward down should should open the container if the item is a
+ * container, drag the item otherwise.
+ *
+ * @param aElement
+ * DOM node element we will drag
+ * @param aExpectedDragData
+ * Array of flavors and values in the form:
+ * [ ["text/plain: sometext", "text/html: <b>sometext</b>"], [...] ]
+ * Pass an empty array to check that drag even has been canceled.
+ * @param aDirection
+ * Direction for the dragging gesture, see dragDirections helper object.
+ */
+function synthesizeDragWithDirection(aElement, aExpectedDragData, aDirection) {
+ var trapped = false;
+
+ // Dragstart listener function.
+ var trapDrag = function(event) {
+ trapped = true;
+ var dataTransfer = event.dataTransfer;
+ is(dataTransfer.mozItemCount, aExpectedDragData.length,
+ "Number of dragged items should be the same.");
+
+ for (var t = 0; t < dataTransfer.mozItemCount; t++) {
+ var types = dataTransfer.mozTypesAt(t);
+ var expecteditem = aExpectedDragData[t];
+ is(types.length, expecteditem.length,
+ "Number of flavors for item " + t + " should be the same.");
+
+ for (var f = 0; f < types.length; f++) {
+ is(types[f], expecteditem[f].substring(0, types[f].length),
+ "Flavor " + types[f] + " for item " + t + " should be the same.");
+ is(dataTransfer.mozGetDataAt(types[f], t),
+ expecteditem[f].substring(types[f].length + 2),
+ "Contents for item " + t + " with flavor " + types[f] + " should be the same.");
+ }
+ }
+
+ if (!aExpectedDragData.length)
+ ok(event.defaultPrevented, "Drag has been canceled.");
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ var prevent = function(aEvent) {aEvent.preventDefault();}
+
+ var xIncrement = 0;
+ var yIncrement = 0;
+
+ switch (aDirection) {
+ case dragDirections.LEFT:
+ xIncrement = -1;
+ break;
+ case dragDirections.RIGHT:
+ xIncrement = +1;
+ break;
+ case dragDirections.UP:
+ yIncrement = -1;
+ break;
+ case dragDirections.DOWN:
+ yIncrement = +1;
+ break;
+ }
+
+ var rect = aElement.getBoundingClientRect();
+ var startingPoint = { x: (rect.right - rect.left)/2,
+ y: (rect.bottom - rect.top)/2 };
+
+ EventUtils.synthesizeMouse(aElement,
+ startingPoint.x,
+ startingPoint.y,
+ { type: "mousedown" });
+ EventUtils.synthesizeMouse(aElement,
+ startingPoint.x + xIncrement * 1,
+ startingPoint.y + yIncrement * 1,
+ { type: "mousemove" });
+ gBookmarksToolbar.addEventListener("dragstart", trapDrag);
+ EventUtils.synthesizeMouse(aElement,
+ startingPoint.x + xIncrement * 9,
+ startingPoint.y + yIncrement * 9,
+ { type: "mousemove" });
+ ok(trapped, "A dragstart event has been trapped.");
+ gBookmarksToolbar.removeEventListener("dragstart", trapDrag);
+
+ // This is likely to cause a click event, and, in case we are dragging a
+ // bookmark, an unwanted page visit. Prevent the click event.
+ aElement.addEventListener("click", prevent);
+ EventUtils.synthesizeMouse(aElement,
+ startingPoint.x + xIncrement * 9,
+ startingPoint.y + yIncrement * 9,
+ { type: "mouseup" });
+ aElement.removeEventListener("click", prevent);
+
+ // Cleanup eventually opened menus.
+ if (aElement.localName == "menu" && aElement.open)
+ aElement.open = false;
+}
+
+function getToolbarNodeForItemId(aItemId) {
+ var children = document.getElementById("PlacesToolbarItems").childNodes;
+ var node = null;
+ for (var i = 0; i < children.length; i++) {
+ if (aItemId == children[i]._placesNode.itemId) {
+ node = children[i];
+ break;
+ }
+ }
+ return node;
+}
+
+function getExpectedDataForPlacesNode(aNode) {
+ var wrappedNode = [];
+ var flavors = ["text/x-moz-place",
+ "text/x-moz-url",
+ "text/plain",
+ "text/html"];
+
+ flavors.forEach(function(aFlavor) {
+ var wrappedFlavor = aFlavor + ": " +
+ PlacesUtils.wrapNode(aNode, aFlavor);
+ wrappedNode.push(wrappedFlavor);
+ });
+
+ return [wrappedNode];
+}
+
+var gTests = [
+
+//------------------------------------------------------------------------------
+
+ {
+ desc: "Drag a folder on toolbar",
+ run: function() {
+ // Create a test folder to be dragged.
+ var folderId = PlacesUtils.bookmarks
+ .createFolder(PlacesUtils.toolbarFolderId,
+ TEST_TITLE,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ var element = getToolbarNodeForItemId(folderId);
+ isnot(element, null, "Found node on toolbar");
+
+ isnot(element._placesNode, null, "Toolbar node has an associated Places node.");
+ var expectedData = getExpectedDataForPlacesNode(element._placesNode);
+
+ ok(true, "Dragging left");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT);
+ ok(true, "Dragging right");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT);
+ ok(true, "Dragging up");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.UP);
+ ok(true, "Dragging down");
+ synthesizeDragWithDirection(element, new Array(), dragDirections.DOWN);
+
+ // Cleanup.
+ PlacesUtils.bookmarks.removeItem(folderId);
+ }
+ },
+
+//------------------------------------------------------------------------------
+
+ {
+ desc: "Drag a bookmark on toolbar",
+ run: function() {
+ // Create a test bookmark to be dragged.
+ var itemId = PlacesUtils.bookmarks
+ .insertBookmark(PlacesUtils.toolbarFolderId,
+ PlacesUtils._uri(TEST_URL),
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ TEST_TITLE);
+ var element = getToolbarNodeForItemId(itemId);
+ isnot(element, null, "Found node on toolbar");
+
+ isnot(element._placesNode, null, "Toolbar node has an associated Places node.");
+ var expectedData = getExpectedDataForPlacesNode(element._placesNode);
+
+ ok(true, "Dragging left");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT);
+ ok(true, "Dragging right");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT);
+ ok(true, "Dragging up");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.UP);
+ ok(true, "Dragging down");
+ synthesizeDragWithDirection(element, expectedData, dragDirections.DOWN);
+
+ // Cleanup.
+ PlacesUtils.bookmarks.removeItem(itemId);
+ }
+ },
+];
+
+function nextTest() {
+ if (gTests.length) {
+ var test = gTests.shift();
+ info("Start of test: " + test.desc);
+ test.run();
+
+ setTimeout(nextTest, 0);
+ }
+ else {
+ // Collapse the personal toolbar if needed.
+ if (wasCollapsed)
+ toolbar.collapsed = true;
+ finish();
+ }
+}
+
+var toolbar = document.getElementById("PersonalToolbar");
+var wasCollapsed = toolbar.collapsed;
+
+function test() {
+ // Uncollapse the personal toolbar if needed.
+ if (wasCollapsed)
+ toolbar.collapsed = false;
+
+ waitForExplicitFinish();
+ nextTest();
+}
+
diff --git a/comm/suite/components/places/tests/browser/browser_library_infoBox.js b/comm/suite/components/places/tests/browser/browser_library_infoBox.js
new file mode 100644
index 0000000000..03ee11574a
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_library_infoBox.js
@@ -0,0 +1,171 @@
+/* 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/. */
+
+/**
+ * Test appropriate visibility of infoBoxExpanderWrapper and
+ * additionalInfoFields in infoBox section of library
+ */
+
+const TEST_URI = "http://www.mozilla.org/";
+
+var gTests = [];
+var gLibrary;
+
+//------------------------------------------------------------------------------
+
+gTests.push({
+ desc: "Bug 430148 - Remove or hide the more/less button in details pane...",
+ run: function() {
+ var PO = gLibrary.PlacesOrganizer;
+ var infoBoxExpanderWrapper = getAndCheckElmtById("infoBoxExpanderWrapper");
+
+ function addVisitsCallback() {
+ // open all bookmarks node
+ PO.selectLeftPaneQuery("AllBookmarks");
+ isnot(PO._places.selectedNode, null,
+ "Correctly selected all bookmarks node.");
+ checkInfoBoxSelected(PO);
+ ok(infoBoxExpanderWrapper.hidden,
+ "Expander button is hidden for all bookmarks node.");
+ checkAddInfoFieldsCollapsed(PO);
+
+ // open bookmarks menu node
+ PO.selectLeftPaneQuery("BookmarksMenu");
+ isnot(PO._places.selectedNode, null,
+ "Correctly selected bookmarks menu node.");
+ checkInfoBoxSelected(PO);
+ ok(infoBoxExpanderWrapper.hidden,
+ "Expander button is hidden for bookmarks menu node.");
+ checkAddInfoFieldsCollapsed(PO);
+
+ // open recently bookmarked node
+ var menuNode = PO._places.selectedNode.
+ QueryInterface(Ci.nsINavHistoryContainerResultNode);
+ menuNode.containerOpen = true;
+ var childNode = menuNode.getChild(0);
+ isnot(childNode, null, "Bookmarks menu child node exists.");
+ var recentlyBookmarkedTitle = PlacesUIUtils.
+ getString("recentlyBookmarkedTitle");
+ isnot(recentlyBookmarkedTitle, null,
+ "Correctly got the recently bookmarked title locale string.");
+ is(childNode.title, recentlyBookmarkedTitle,
+ "Correctly selected recently bookmarked node.");
+ PO._places.selectNode(childNode);
+ checkInfoBoxSelected(PO);
+ // Note: SeaMonkey differs from Firefox UI in this case.
+ ok(infoBoxExpanderWrapper.hidden,
+ "Expander button is hidden for recently bookmarked node.");
+ checkAddInfoFieldsNotCollapsed(PO);
+
+ // open first bookmark
+ PO._content.focus();
+ var view = PO._content.treeBoxObject.view;
+ ok(view.rowCount > 0, "Bookmark item exists.");
+ view.selection.select(0);
+ checkInfoBoxSelected(PO);
+ ok(!infoBoxExpanderWrapper.hidden,
+ "Expander button is not hidden for bookmark item.");
+ checkAddInfoFieldsNotCollapsed(PO);
+ checkAddInfoFields(PO, "bookmark item");
+
+ // make sure additional fields are still hidden in second bookmark item
+ ok(view.rowCount > 1, "Second bookmark item exists.");
+ view.selection.select(1);
+ checkInfoBoxSelected(PO);
+ ok(!infoBoxExpanderWrapper.hidden,
+ "Expander button is not hidden for second bookmark item.");
+ checkAddInfoFieldsNotCollapsed(PO);
+ checkAddInfoFields(PO, "second bookmark item");
+
+ menuNode.containerOpen = false;
+
+ waitForClearHistory(nextTest);
+ };
+
+ // Add a visit to browser history
+ addVisits(
+ { uri: PlacesUtils._uri(TEST_URI),
+ visitDate: Date.now()*1000,
+ transition: PlacesUtils.history.TRANSITION_TYPED },
+ addVisitsCallback);
+ }
+});
+
+function checkInfoBoxSelected(PO) {
+ is(getAndCheckElmtById("detailsDeck").selectedIndex, 1,
+ "Selected element in detailsDeck is infoBox.");
+}
+
+function checkAddInfoFieldsCollapsed(PO) {
+ PO._additionalInfoFields.forEach(function (id) {
+ ok(getAndCheckElmtById(id).collapsed,
+ "Additional info field correctly collapsed: #" + id);
+ });
+}
+
+function checkAddInfoFieldsNotCollapsed(PO) {
+ ok(PO._additionalInfoFields.some(function (id) {
+ return !getAndCheckElmtById(id).collapsed;
+ }), "Some additional info field correctly not collapsed");
+}
+
+function checkAddInfoFields(PO, nodeName) {
+ ok(true, "Checking additional info fields visibiity for node: " + nodeName);
+ var expanderButton = getAndCheckElmtById("infoBoxExpander");
+
+ // make sure additional fields are hidden by default
+ PO._additionalInfoFields.forEach(function (id) {
+ ok(getAndCheckElmtById(id).hidden,
+ "Additional info field correctly hidden by default: #" + id);
+ });
+
+ // toggle fields and make sure they are hidden/unhidden as expected
+ expanderButton.click();
+ PO._additionalInfoFields.forEach(function (id) {
+ ok(!getAndCheckElmtById(id).hidden,
+ "Additional info field correctly unhidden after toggle: #" + id);
+ });
+ expanderButton.click();
+ PO._additionalInfoFields.forEach(function (id) {
+ ok(getAndCheckElmtById(id).hidden,
+ "Additional info field correctly hidden after toggle: #" + id);
+ });
+}
+
+function getAndCheckElmtById(id) {
+ var elmt = gLibrary.document.getElementById(id);
+ isnot(elmt, null, "Correctly got element: #" + id);
+ return elmt;
+}
+
+//------------------------------------------------------------------------------
+
+function nextTest() {
+ if (gTests.length) {
+ var test = gTests.shift();
+ ok(true, "TEST: " + test.desc);
+ dump("TEST: " + test.desc + "\n");
+ test.run();
+ }
+ else {
+ // Close Library window.
+ gLibrary.close();
+ // No need to cleanup anything, we have a correct left pane now.
+ finish();
+ }
+}
+
+function test() {
+ waitForExplicitFinish();
+ // Sanity checks.
+ ok(PlacesUtils, "PlacesUtils is running in chrome context");
+ ok(PlacesUIUtils, "PlacesUIUtils is running in chrome context");
+
+ // Open Library.
+ openLibrary(function (library) {
+ gLibrary = library;
+ gLibrary.PlacesOrganizer._places.focus();
+ nextTest(gLibrary);
+ });
+}
diff --git a/comm/suite/components/places/tests/browser/browser_library_left_pane_commands.js b/comm/suite/components/places/tests/browser/browser_library_left_pane_commands.js
new file mode 100644
index 0000000000..bd4f089018
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_library_left_pane_commands.js
@@ -0,0 +1,100 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Test enabled commands in the left pane folder of the Library.
+ */
+
+const TEST_URI = "http://www.mozilla.org/";
+
+var gTests = [];
+var gLibrary;
+
+//------------------------------------------------------------------------------
+
+gTests.push({
+ desc: "Bug 490156 - Can't delete smart bookmark containers",
+ run: function() {
+ // Select and open the left pane "Bookmarks Toolbar" folder.
+ var PO = gLibrary.PlacesOrganizer;
+ PO.selectLeftPaneQuery('BookmarksToolbar');
+ isnot(PO._places.selectedNode, null, "We have a valid selection");
+ is(PlacesUtils.getConcreteItemId(PO._places.selectedNode),
+ PlacesUtils.toolbarFolderId,
+ "We have correctly selected bookmarks toolbar node.");
+
+ // Check that both cut and delete commands are disabled.
+ ok(!PO._places.controller.isCommandEnabled("cmd_cut"),
+ "Cut command is disabled");
+ ok(!PO._places.controller.isCommandEnabled("cmd_delete"),
+ "Delete command is disabled");
+
+ var toolbarNode = PO._places.selectedNode
+ .QueryInterface(Ci.nsINavHistoryContainerResultNode);
+ toolbarNode.containerOpen = true;
+
+ // Add an History query to the toolbar.
+ PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId,
+ PlacesUtils._uri("place:sort=4"),
+ 0, // Insert at start.
+ "special_query");
+ // Get first child and check it is the "Most Visited" smart bookmark.
+ ok(toolbarNode.childCount > 0, "Toolbar node has children");
+ var queryNode = toolbarNode.getChild(0);
+ is(queryNode.title, "special_query", "Query node is correctly selected");
+
+ // Select query node.
+ PO._places.selectNode(queryNode);
+ is(PO._places.selectedNode, queryNode, "We correctly selected query node");
+
+ // Check that both cut and delete commands are enabled.
+ ok(PO._places.controller.isCommandEnabled("cmd_cut"),
+ "Cut command is enabled");
+ ok(PO._places.controller.isCommandEnabled("cmd_delete"),
+ "Delete command is enabled");
+
+ // Execute the delete command and check bookmark has been removed.
+ PO._places.controller.doCommand("cmd_delete");
+ try {
+ PlacesUtils.bookmarks.getFolderIdForItem(queryNode.itemId);
+ ok(false, "Unable to remove query node bookmark");
+ } catch(ex) {
+ ok(true, "Query node bookmark has been correctly removed");
+ }
+
+ toolbarNode.containerOpen = false;
+ nextTest();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+function nextTest() {
+ if (gTests.length) {
+ var test = gTests.shift();
+ info("Start of test: " + test.desc);
+ test.run();
+ }
+ else {
+ // Close Library window.
+ gLibrary.close();
+ // No need to cleanup anything, we have a correct left pane now.
+ finish();
+ }
+}
+
+function test() {
+ waitForExplicitFinish();
+ // Sanity checks.
+ ok(PlacesUtils, "PlacesUtils is running in chrome context");
+ ok(PlacesUIUtils, "PlacesUIUtils is running in chrome context");
+
+ // Open Library.
+ openLibrary(function (library) {
+ gLibrary = library;
+ nextTest();
+ });
+}
diff --git a/comm/suite/components/places/tests/browser/browser_library_left_pane_fixnames.js b/comm/suite/components/places/tests/browser/browser_library_left_pane_fixnames.js
new file mode 100644
index 0000000000..d46baf520a
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_library_left_pane_fixnames.js
@@ -0,0 +1,92 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Test we correctly fix broken Library left pane queries names.
+ */
+
+// Array of left pane queries objects, each one has the following properties:
+// name: query's identifier got from annotations,
+// itemId: query's itemId,
+// correctTitle: original and correct query's title.
+var leftPaneQueries = [];
+
+function onLibraryReady(organizer) {
+ // Check titles have been fixed.
+ for (var i = 0; i < leftPaneQueries.length; i++) {
+ var query = leftPaneQueries[i];
+ if ("concreteId" in query) {
+ is(PlacesUtils.bookmarks.getItemTitle(query.concreteId),
+ query.concreteTitle, "Concrete title is correct for query " + query.name);
+ }
+ }
+
+ // Close Library window.
+ organizer.close();
+ // No need to cleanup anything, we have a correct left pane now.
+ finish();
+}
+
+function test() {
+ waitForExplicitFinish();
+ // Sanity checks.
+ ok(PlacesUtils, "PlacesUtils is running in chrome context");
+ ok(PlacesUIUtils, "PlacesUIUtils is running in chrome context");
+ ok(PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION > 0,
+ "Left pane version in chrome context, current version is: " + PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION );
+
+ // Ensure left pane is initialized.
+ ok(PlacesUIUtils.leftPaneFolderId > 0, "left pane folder is initialized");
+
+ // Get the left pane folder.
+ var leftPaneItems = PlacesUtils.annotations
+ .getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+
+ is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
+ // Check version.
+ var version = PlacesUtils.annotations
+ .getItemAnnotation(leftPaneItems[0],
+ PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, "Left pane version is actual");
+
+ // Get all left pane queries.
+ var items = PlacesUtils.annotations
+ .getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_QUERY_ANNO);
+ // Get current queries names.
+ for (var i = 0; i < items.length; i++) {
+ var itemId = items[i];
+ var queryName = PlacesUtils.annotations
+ .getItemAnnotation(items[i],
+ PlacesUIUtils.ORGANIZER_QUERY_ANNO);
+ var query = { name: queryName,
+ itemId: itemId,
+ correctTitle: PlacesUtils.bookmarks.getItemTitle(itemId) }
+ switch (queryName) {
+ case "BookmarksToolbar":
+ query.concreteId = PlacesUtils.toolbarFolderId;
+ query.concreteTitle = PlacesUtils.bookmarks.getItemTitle(query.concreteId);
+ break;
+ case "BookmarksMenu":
+ query.concreteId = PlacesUtils.bookmarksMenuFolderId;
+ query.concreteTitle = PlacesUtils.bookmarks.getItemTitle(query.concreteId);
+ break;
+ case "UnfiledBookmarks":
+ query.concreteId = PlacesUtils.unfiledBookmarksFolderId;
+ query.concreteTitle = PlacesUtils.bookmarks.getItemTitle(query.concreteId);
+ break;
+ }
+ leftPaneQueries.push(query);
+ // Rename to a bad title.
+ PlacesUtils.bookmarks.setItemTitle(query.itemId, "badName");
+ if ("concreteId" in query)
+ PlacesUtils.bookmarks.setItemTitle(query.concreteId, "badName");
+ }
+
+ PlacesUIUtils.__defineGetter__("leftPaneFolderId", cachedLeftPaneFolderIdGetter);
+
+ // Open Library, this will kick-off left pane code.
+ openLibrary(onLibraryReady);
+}
diff --git a/comm/suite/components/places/tests/browser/browser_library_open_leak.js b/comm/suite/components/places/tests/browser/browser_library_open_leak.js
new file mode 100644
index 0000000000..03aa3408f7
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_library_open_leak.js
@@ -0,0 +1,23 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Bug 474831
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=474831
+ *
+ * Tests for leaks caused by simply opening and closing the Places Library
+ * window. Opens the Places Library window, waits for it to load, closes it,
+ * and finishes.
+ */
+
+function test() {
+ waitForExplicitFinish();
+ openLibrary(function (win) {
+ ok(true, "Library has been correctly opened");
+ win.close();
+ finish();
+ });
+}
diff --git a/comm/suite/components/places/tests/browser/browser_library_views_liveupdate.js b/comm/suite/components/places/tests/browser/browser_library_views_liveupdate.js
new file mode 100644
index 0000000000..33fe4f0b34
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_library_views_liveupdate.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/. */
+
+/**
+ * Tests Library Left pane view for liveupdate.
+ */
+
+var gLibrary = null;
+
+function test() {
+ waitForExplicitFinish();
+ // This test takes quite some time, and timeouts frequently, so we require
+ // more time to run.
+ // See Bug 525610.
+ requestLongerTimeout(2);
+
+ // Sanity checks.
+ ok(PlacesUtils, "PlacesUtils in context");
+ ok(PlacesUIUtils, "PlacesUIUtils in context");
+
+ // Open Library, we will check the left pane.
+ openLibrary(function (library) {
+ gLibrary = library;
+ startTest();
+ });
+}
+
+/**
+ * Adds bookmarks observer, and executes a bunch of bookmarks operations.
+ */
+function startTest() {
+ var bs = PlacesUtils.bookmarks;
+ // Add observers.
+ bs.addObserver(bookmarksObserver);
+ PlacesUtils.annotations.addObserver(bookmarksObserver);
+ var addedBookmarks = [];
+
+ // MENU
+ ok(true, "*** Acting on menu bookmarks");
+ var id = bs.insertBookmark(bs.bookmarksMenuFolder,
+ PlacesUtils._uri("http://bm1.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "bm1");
+ addedBookmarks.push(id);
+ id = bs.insertBookmark(bs.bookmarksMenuFolder,
+ PlacesUtils._uri("place:"),
+ bs.DEFAULT_INDEX,
+ "bm2");
+ bs.setItemTitle(id, "bm2_edited");
+ addedBookmarks.push(id);
+ id = bs.insertSeparator(bs.bookmarksMenuFolder, bs.DEFAULT_INDEX);
+ addedBookmarks.push(id);
+ id = bs.createFolder(bs.bookmarksMenuFolder,
+ "bmf",
+ bs.DEFAULT_INDEX);
+ bs.setItemTitle(id, "bmf_edited");
+ addedBookmarks.push(id);
+ id = bs.insertBookmark(id,
+ PlacesUtils._uri("http://bmf1.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "bmf1");
+ addedBookmarks.push(id);
+ bs.moveItem(id, bs.bookmarksMenuFolder, 0);
+
+ // TOOLBAR
+ ok(true, "*** Acting on toolbar bookmarks");
+ bs.insertBookmark(bs.toolbarFolder,
+ PlacesUtils._uri("http://tb1.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "tb1");
+ bs.setItemTitle(id, "tb1_edited");
+ addedBookmarks.push(id);
+ id = bs.insertBookmark(bs.toolbarFolder,
+ PlacesUtils._uri("place:"),
+ bs.DEFAULT_INDEX,
+ "tb2");
+ bs.setItemTitle(id, "tb2_edited");
+ addedBookmarks.push(id);
+ id = bs.insertSeparator(bs.toolbarFolder, bs.DEFAULT_INDEX);
+ addedBookmarks.push(id);
+ id = bs.createFolder(bs.toolbarFolder,
+ "tbf",
+ bs.DEFAULT_INDEX);
+ bs.setItemTitle(id, "tbf_edited");
+ addedBookmarks.push(id);
+ id = bs.insertBookmark(id,
+ PlacesUtils._uri("http://tbf1.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "bmf1");
+ addedBookmarks.push(id);
+ bs.moveItem(id, bs.toolbarFolder, 0);
+
+ // UNSORTED
+ ok(true, "*** Acting on unsorted bookmarks");
+ id = bs.insertBookmark(bs.unfiledBookmarksFolder,
+ PlacesUtils._uri("http://ub1.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "ub1");
+ bs.setItemTitle(id, "ub1_edited");
+ addedBookmarks.push(id);
+ id = bs.insertBookmark(bs.unfiledBookmarksFolder,
+ PlacesUtils._uri("place:"),
+ bs.DEFAULT_INDEX,
+ "ub2");
+ bs.setItemTitle(id, "ub2_edited");
+ addedBookmarks.push(id);
+ id = bs.insertSeparator(bs.unfiledBookmarksFolder, bs.DEFAULT_INDEX);
+ addedBookmarks.push(id);
+ id = bs.createFolder(bs.unfiledBookmarksFolder,
+ "ubf",
+ bs.DEFAULT_INDEX);
+ bs.setItemTitle(id, "ubf_edited");
+ addedBookmarks.push(id);
+ id = bs.insertBookmark(id,
+ PlacesUtils._uri("http://ubf1.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "ubf1");
+ addedBookmarks.push(id);
+ bs.moveItem(id, bs.unfiledBookmarksFolder, 0);
+
+ // Remove all added bookmarks.
+ addedBookmarks.forEach(function (aItem) {
+ // If we remove an item after its containing folder has been removed,
+ // this will throw, but we can ignore that.
+ try {
+ bs.removeItem(aItem);
+ } catch (ex) {}
+ });
+
+ // Remove observers.
+ bs.removeObserver(bookmarksObserver);
+ PlacesUtils.annotations.removeObserver(bookmarksObserver);
+ finishTest();
+}
+
+/**
+ * Restores browser state and calls finish.
+ */
+function finishTest() {
+ // Close Library window.
+ gLibrary.close();
+ finish();
+}
+
+/**
+ * The observer is where magic happens, for every change we do it will look for
+ * nodes positions in the affected views.
+ */
+var bookmarksObserver = {
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsINavBookmarkObserver
+ , Ci.nsIAnnotationObserver
+ ]),
+
+ // nsIAnnotationObserver
+ onItemAnnotationSet: function() {},
+ onItemAnnotationRemoved: function() {},
+ onPageAnnotationSet: function() {},
+ onPageAnnotationRemoved: function() {},
+
+ // nsINavBookmarkObserver
+ onItemAdded: function PSB_onItemAdded(aItemId, aFolderId, aIndex, aItemType,
+ aURI) {
+ var node = null;
+ var index = null;
+ [node, index] = getNodeForTreeItem(aItemId, gLibrary.PlacesOrganizer._places);
+ // Left pane should not be updated for normal bookmarks or separators.
+ var type = PlacesUtils.bookmarks.getItemType(aItemId);
+ switch (type) {
+ case PlacesUtils.bookmarks.TYPE_BOOKMARK:
+ var uriString = PlacesUtils.bookmarks.getBookmarkURI(aItemId).spec;
+ var isQuery = uriString.substr(0, 6) == "place:";
+ if (isQuery) {
+ isnot(node, null, "Found new Places node in left pane");
+ ok(index >= 0, "Node is at index " + index);
+ break;
+ }
+ // Fallback to separator case if this is not a query.
+ case PlacesUtils.bookmarks.TYPE_SEPARATOR:
+ is(node, null, "New Places node not added in left pane");
+ break;
+ default:
+ isnot(node, null, "Found new Places node in left pane");
+ ok(index >= 0, "Node is at index " + index);
+ }
+ },
+
+ onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex) {
+ var node = null;
+ var index = null;
+ [node, index] = getNodeForTreeItem(aItemId, gLibrary.PlacesOrganizer._places);
+ is(node, null, "Places node not found in left pane");
+ },
+
+ onItemMoved: function(aItemId,
+ aOldFolderId, aOldIndex,
+ aNewFolderId, aNewIndex) {
+ var node = null;
+ var index = null;
+ [node, index] = getNodeForTreeItem(aItemId, gLibrary.PlacesOrganizer._places);
+ // Left pane should not be updated for normal bookmarks or separators.
+ var type = PlacesUtils.bookmarks.getItemType(aItemId);
+ switch (type) {
+ case PlacesUtils.bookmarks.TYPE_BOOKMARK:
+ var uriString = PlacesUtils.bookmarks.getBookmarkURI(aItemId).spec;
+ var isQuery = uriString.substr(0, 6) == "place:";
+ if (isQuery) {
+ isnot(node, null, "Found new Places node in left pane");
+ ok(index >= 0, "Node is at index " + index);
+ break;
+ }
+ // Fallback to separator case if this is not a query.
+ case PlacesUtils.bookmarks.TYPE_SEPARATOR:
+ is(node, null, "New Places node not added in left pane");
+ break;
+ default:
+ isnot(node, null, "Found new Places node in left pane");
+ ok(index >= 0, "Node is at index " + index);
+ }
+ },
+
+ onBeginUpdateBatch: function PSB_onBeginUpdateBatch() {},
+ onEndUpdateBatch: function PSB_onEndUpdateBatch() {},
+ onItemVisited: function() {},
+ onItemChanged: function PSB_onItemChanged(aItemId, aProperty,
+ aIsAnnotationProperty, aNewValue) {
+ if (aProperty == "title") {
+ let validator = function(aTreeRowIndex) {
+ let tree = gLibrary.PlacesOrganizer._places;
+ let cellText = tree.view.getCellText(aTreeRowIndex,
+ tree.columns.getColumnAt(0));
+ return cellText == aNewValue;
+ }
+ let [node, index, valid] = getNodeForTreeItem(aItemId, gLibrary.PlacesOrganizer._places, validator);
+ if (node) // Only visible nodes.
+ ok(valid, "Title cell value has been correctly updated");
+ }
+ }
+};
+
+
+/**
+ * Get places node and index for an itemId in a tree view.
+ *
+ * @param aItemId
+ * item id of the item to search.
+ * @param aTree
+ * Tree to search in.
+ * @param aValidator [optional]
+ * function to check row validity if found. Defaults to {return true;}.
+ * @returns [node, index, valid] or [null, null, false] if not found.
+ */
+function getNodeForTreeItem(aItemId, aTree, aValidator) {
+
+ function findNode(aContainerIndex) {
+ if (aTree.view.isContainerEmpty(aContainerIndex))
+ return [null, null, false];
+
+ // The rowCount limit is just for sanity, but we will end looping when
+ // we have checked the last child of this container or we have found node.
+ for (var i = aContainerIndex + 1; i < aTree.view.rowCount; i++) {
+ var node = aTree.view.nodeForTreeIndex(i);
+
+ if (node.itemId == aItemId) {
+ // Minus one because we want relative index inside the container.
+ let valid = aValidator ? aValidator(i) : true;
+ return [node, i - aTree.view.getParentIndex(i) - 1, valid];
+ }
+
+ if (PlacesUtils.nodeIsFolder(node)) {
+ // Open container.
+ aTree.view.toggleOpenState(i);
+ // Search inside it.
+ var foundNode = findNode(i);
+ // Close container.
+ aTree.view.toggleOpenState(i);
+ // Return node if found.
+ if (foundNode[0] != null)
+ return foundNode;
+ }
+
+ // We have finished walking this container.
+ if (!aTree.view.hasNextSibling(aContainerIndex + 1, i))
+ break;
+ }
+ return [null, null, false]
+ }
+
+ // Root node is hidden, so we need to manually walk the first level.
+ for (var i = 0; i < aTree.view.rowCount; i++) {
+ // Open container.
+ aTree.view.toggleOpenState(i);
+ // Search inside it.
+ var foundNode = findNode(i);
+ // Close container.
+ aTree.view.toggleOpenState(i);
+ // Return node if found.
+ if (foundNode[0] != null)
+ return foundNode;
+ }
+ return [null, null, false];
+}
diff --git a/comm/suite/components/places/tests/browser/browser_sort_in_library.js b/comm/suite/components/places/tests/browser/browser_sort_in_library.js
new file mode 100644
index 0000000000..6967be16aa
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/browser_sort_in_library.js
@@ -0,0 +1,254 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests the following bugs:
+ *
+ * Bug 443745 - View>Sort>of "alpha" sort items is default to Z>A instead of A>Z
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=443745
+ *
+ * Bug 444179 - Library>Views>Sort>Sort by Tags does nothing
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=444179
+ *
+ * Basically, fully tests sorting the placeContent tree in the Places Library
+ * window. Sorting is verified by comparing the nsINavHistoryResult returned by
+ * placeContent.result to the expected sort values.
+ */
+
+// Two properties of nsINavHistoryResult control the sort of the tree:
+// sortingMode and sortingAnnotation. sortingMode's value is one of the
+// nsINavHistoryQueryOptions.SORT_BY_* constants. sortingAnnotation is the
+// annotation used to sort for SORT_BY_ANNOTATION_* mode.
+//
+// This lookup table maps the possible values of anonid's of the treecols to
+// objects that represent the treecols' correct state after the user sorts the
+// previously unsorted tree by selecting a column from the Views > Sort menu.
+// sortingMode is constructed from the key and dir properties (i.e.,
+// SORT_BY_<key>_<dir>) and sortingAnnotation is checked against anno. anno
+// may be undefined if key is not "ANNOTATION".
+const SORT_LOOKUP_TABLE = {
+ title: { key: "TITLE", dir: "ASCENDING" },
+ tags: { key: "TAGS", dir: "ASCENDING" },
+ url: { key: "URI", dir: "ASCENDING" },
+ date: { key: "DATE", dir: "DESCENDING" },
+ visitCount: { key: "VISITCOUNT", dir: "DESCENDING" },
+ keyword: { key: "KEYWORD", dir: "ASCENDING" },
+ dateAdded: { key: "DATEADDED", dir: "DESCENDING" },
+ lastModified: { key: "LASTMODIFIED", dir: "DESCENDING" },
+ description: { key: "ANNOTATION",
+ dir: "ASCENDING",
+ anno: "bookmarkProperties/description" }
+};
+
+// This is the column that's sorted if one is not specified and the tree is
+// currently unsorted. Set it to a key substring in the name of one of the
+// nsINavHistoryQueryOptions.SORT_BY_* constants, e.g., "TITLE", "URI".
+// Method ViewMenu.setSortColumn in browser/components/places/content/places.js
+// determines this value.
+const DEFAULT_SORT_KEY = "TITLE";
+
+// Part of the test is checking that sorts stick, so each time we sort we need
+// to remember it.
+var prevSortDir = null;
+var prevSortKey = null;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Ensures that the sort of aTree is aSortingMode and aSortingAnno.
+ *
+ * @param aTree
+ * the tree to check
+ * @param aSortingMode
+ * one of the Ci.nsINavHistoryQueryOptions.SORT_BY_* constants
+ * @param aSortingAnno
+ * checked only if sorting mode is one of the
+ * Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_* constants
+ */
+function checkSort(aTree, aSortingMode, aSortingAnno) {
+ // The placeContent tree's sort is determined by the nsINavHistoryResult it
+ // stores. Get it and check that the sort is what the caller expects.
+ let res = aTree.result;
+ isnot(res, null,
+ "sanity check: placeContent.result should not return null");
+
+ // Check sortingMode.
+ is(res.sortingMode, aSortingMode,
+ "column should now have sortingMode " + aSortingMode);
+
+ // Check sortingAnnotation, but only if sortingMode is ANNOTATION.
+ if ([Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_ASCENDING,
+ Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_DESCENDING].
+ indexOf(aSortingMode) >= 0) {
+ is(res.sortingAnnotation, aSortingAnno,
+ "column should now have sorting annotation " + aSortingAnno);
+ }
+}
+
+/**
+ * Sets the sort of aTree.
+ *
+ * @param aOrganizerWin
+ * the Places window
+ * @param aTree
+ * the tree to sort
+ * @param aUnsortFirst
+ * true if the sort should be set to SORT_BY_NONE before sorting by aCol
+ * and aDir
+ * @param aShouldFail
+ * true if setSortColumn should fail on aCol or aDir
+ * @param aCol
+ * the column of aTree by which to sort
+ * @param aDir
+ * either "ascending" or "descending"
+ */
+function setSort(aOrganizerWin, aTree, aUnsortFirst, aShouldFail, aCol, aDir) {
+ if (aUnsortFirst) {
+ aOrganizerWin.ViewMenu.setSortColumn();
+ checkSort(aTree, Ci.nsINavHistoryQueryOptions.SORT_BY_NONE, "");
+
+ // Remember the sort key and direction.
+ prevSortKey = null;
+ prevSortDir = null;
+ }
+
+ let failed = false;
+ try {
+ aOrganizerWin.ViewMenu.setSortColumn(aCol, aDir);
+
+ // Remember the sort key and direction.
+ if (!aCol && !aDir) {
+ prevSortKey = null;
+ prevSortDir = null;
+ }
+ else {
+ if (aCol)
+ prevSortKey = SORT_LOOKUP_TABLE[aCol.getAttribute("anonid")].key;
+ else if (prevSortKey === null)
+ prevSortKey = DEFAULT_SORT_KEY;
+
+ if (aDir)
+ prevSortDir = aDir.toUpperCase();
+ else if (prevSortDir === null)
+ prevSortDir = SORT_LOOKUP_TABLE[aCol.getAttribute("anonid")].dir;
+ }
+ } catch (exc) {
+ failed = true;
+ }
+
+ is(failed, !!aShouldFail,
+ "setSortColumn on column " +
+ (aCol ? aCol.getAttribute("anonid") : "(no column)") +
+ " with direction " + (aDir || "(no direction)") +
+ " and table previously " + (aUnsortFirst ? "unsorted" : "sorted") +
+ " should " + (aShouldFail ? "" : "not ") + "fail");
+}
+
+/**
+ * Tries sorting by an invalid column and sort direction.
+ *
+ * @param aOrganizerWin
+ * the Places window
+ * @param aPlaceContentTree
+ * the placeContent tree in aOrganizerWin
+ */
+function testInvalid(aOrganizerWin, aPlaceContentTree) {
+ // Invalid column should fail by throwing an exception.
+ let bogusCol = document.createElement("treecol");
+ bogusCol.setAttribute("anonid", "bogusColumn");
+ setSort(aOrganizerWin, aPlaceContentTree, true, true, bogusCol, "ascending");
+
+ // Invalid direction reverts to SORT_BY_NONE.
+ setSort(aOrganizerWin, aPlaceContentTree, false, false, null, "bogus dir");
+ checkSort(aPlaceContentTree, Ci.nsINavHistoryQueryOptions.SORT_BY_NONE, "");
+}
+
+/**
+ * Tests sorting aPlaceContentTree by column only and then by both column
+ * and direction.
+ *
+ * @param aOrganizerWin
+ * the Places window
+ * @param aPlaceContentTree
+ * the placeContent tree in aOrganizerWin
+ * @param aUnsortFirst
+ * true if, before each sort we try, we should sort to SORT_BY_NONE
+ */
+function testSortByColAndDir(aOrganizerWin, aPlaceContentTree, aUnsortFirst) {
+ let cols = aPlaceContentTree.getElementsByTagName("treecol");
+ ok(cols.length > 0, "sanity check: placeContent should contain columns");
+
+ for (let i = 0; i < cols.length; i++) {
+ let col = cols.item(i);
+ ok(col.hasAttribute("anonid"),
+ "sanity check: column " + col.id + " should have anonid");
+
+ let colId = col.getAttribute("anonid");
+ ok(colId in SORT_LOOKUP_TABLE,
+ "sanity check: unexpected placeContent column anonid");
+
+ let sortConst =
+ "SORT_BY_" + SORT_LOOKUP_TABLE[colId].key + "_" +
+ (aUnsortFirst ? SORT_LOOKUP_TABLE[colId].dir : prevSortDir);
+ let expectedSortMode = Ci.nsINavHistoryQueryOptions[sortConst];
+ let expectedAnno = SORT_LOOKUP_TABLE[colId].anno || "";
+
+ // Test sorting by only a column.
+ setSort(aOrganizerWin, aPlaceContentTree, aUnsortFirst, false, col);
+ checkSort(aPlaceContentTree, expectedSortMode, expectedAnno);
+
+ // Test sorting by both a column and a direction.
+ ["ascending", "descending"].forEach(function (dir) {
+ let sortConst =
+ "SORT_BY_" + SORT_LOOKUP_TABLE[colId].key + "_" + dir.toUpperCase();
+ let expectedSortMode = Ci.nsINavHistoryQueryOptions[sortConst];
+ setSort(aOrganizerWin, aPlaceContentTree, aUnsortFirst, false, col, dir);
+ checkSort(aPlaceContentTree, expectedSortMode, expectedAnno);
+ });
+ }
+}
+
+/**
+ * Tests sorting aPlaceContentTree by direction only.
+ *
+ * @param aOrganizerWin
+ * the Places window
+ * @param aPlaceContentTree
+ * the placeContent tree in aOrganizerWin
+ * @param aUnsortFirst
+ * true if, before each sort we try, we should sort to SORT_BY_NONE
+ */
+function testSortByDir(aOrganizerWin, aPlaceContentTree, aUnsortFirst) {
+ ["ascending", "descending"].forEach(function (dir) {
+ let key = (aUnsortFirst ? DEFAULT_SORT_KEY : prevSortKey);
+ let sortConst = "SORT_BY_" + key + "_" + dir.toUpperCase();
+ let expectedSortMode = Ci.nsINavHistoryQueryOptions[sortConst];
+ setSort(aOrganizerWin, aPlaceContentTree, aUnsortFirst, false, null, dir);
+ checkSort(aPlaceContentTree, expectedSortMode, "");
+ });
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+function test() {
+ waitForExplicitFinish();
+
+ openLibrary(function (win) {
+ let tree = win.document.getElementById("placeContent");
+ isnot(tree, null, "sanity check: placeContent tree should exist");
+ // Run the tests.
+ testSortByColAndDir(win, tree, true);
+ testSortByColAndDir(win, tree, false);
+ testSortByDir(win, tree, true);
+ testSortByDir(win, tree, false);
+ testInvalid(win, tree);
+ // Reset the sort to SORT_BY_NONE.
+ setSort(win, tree, false, false);
+ // Close the window and finish.
+ win.close();
+ finish();
+ });
+}
diff --git a/comm/suite/components/places/tests/browser/head.js b/comm/suite/components/places/tests/browser/head.js
new file mode 100644
index 0000000000..d0fa1cdd49
--- /dev/null
+++ b/comm/suite/components/places/tests/browser/head.js
@@ -0,0 +1,95 @@
+
+// We need to cache this before test runs...
+var cachedLeftPaneFolderIdGetter;
+var getter = PlacesUIUtils.__lookupGetter__("leftPaneFolderId");
+if (!cachedLeftPaneFolderIdGetter && typeof(getter) == "function")
+ cachedLeftPaneFolderIdGetter = getter;
+
+// ...And restore it when test ends.
+registerCleanupFunction(function() {
+ let getter = PlacesUIUtils.__lookupGetter__("leftPaneFolderId");
+ if (cachedLeftPaneFolderIdGetter && typeof(getter) != "function")
+ PlacesUIUtils.__defineGetter__("leftPaneFolderId",
+ cachedLeftPaneFolderIdGetter);
+});
+
+function openLibrary(callback) {
+ var library = window.openDialog(
+ "chrome://communicator/content/places/places.xul",
+ "", "chrome,toolbar=yes,dialog=no,resizable");
+ waitForFocus(function () {
+ callback(library);
+ }, library);
+}
+
+/**
+ * Waits for completion of a clear history operation, before
+ * proceeding with aCallback.
+ *
+ * @param aCallback
+ * Function to be called when done.
+ */
+function waitForClearHistory(aCallback) {
+ Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+ aCallback();
+ }, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+ PlacesUtils.bhistory.removeAllPages();
+}
+
+/**
+ * Asynchronously adds visits to a page, invoking a callback function when done.
+ *
+ * @param aPlaceInfo
+ * Can be an nsIURI, in such a case a single LINK visit will be added.
+ * Otherwise can be an object describing the visit to add, or an array
+ * of these objects:
+ * { uri: nsIURI of the page,
+ * transition: one of the TRANSITION_* from nsINavHistoryService,
+ * [optional] title: title of the page,
+ * [optional] visitDate: visit date in microseconds from the epoch
+ * [optional] referrer: nsIURI of the referrer for this visit
+ * }
+ * @param [optional] aCallback
+ * Function to be invoked on completion.
+ */
+function addVisits(aPlaceInfo, aCallback) {
+ let places = [];
+ if (aPlaceInfo instanceof Ci.nsIURI) {
+ places.push({ uri: aPlaceInfo });
+ }
+ else if (Array.isArray(aPlaceInfo)) {
+ places = places.concat(aPlaceInfo);
+ } else {
+ places.push(aPlaceInfo)
+ }
+
+ // Create mozIVisitInfo for each entry.
+ let now = Date.now();
+ for (let i = 0; i < places.length; i++) {
+ if (!places[i].title) {
+ places[i].title = "test visit for " + places[i].uri.spec;
+ }
+ places[i].visits = [{
+ transitionType: places[i].transition === undefined ? Ci.nsINavHistoryService.TRANSITION_LINK
+ : places[i].transition,
+ visitDate: places[i].visitDate || (now++) * 1000,
+ referrerURI: places[i].referrer
+ }];
+ }
+
+ PlacesUtils.asyncHistory.updatePlaces(
+ places,
+ {
+ handleError: function AAV_handleError() {
+ throw("Unexpected error in adding visit.");
+ },
+ handleResult: function () {},
+ handleCompletion: function UP_handleCompletion() {
+ if (aCallback)
+ aCallback();
+ }
+ }
+ );
+}
+
diff --git a/comm/suite/components/places/tests/chrome/chrome.ini b/comm/suite/components/places/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..38de538ec2
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/chrome.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files = head.js
+
+[test_0_bug510634.xul]
+[test_0_multiple_left_pane.xul]
+[test_bug427633_no_newfolder_if_noip.xul]
+[test_bug485100-change-case-loses-tag.xul]
+[test_bug549192.xul]
+[test_bug549491.xul]
+[test_treeview_date.xul]
diff --git a/comm/suite/components/places/tests/chrome/head.js b/comm/suite/components/places/tests/chrome/head.js
new file mode 100644
index 0000000000..90d19c9def
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/head.js
@@ -0,0 +1,55 @@
+/**
+ * Asynchronously adds visits to a page, invoking a callback function when done.
+ *
+ * @param aPlaceInfo
+ * Can be an nsIURI, in such a case a single LINK visit will be added.
+ * Otherwise can be an object describing the visit to add, or an array
+ * of these objects:
+ * { uri: nsIURI of the page,
+ * transition: one of the TRANSITION_* from nsINavHistoryService,
+ * [optional] title: title of the page,
+ * [optional] visitDate: visit date in microseconds from the epoch
+ * [optional] referrer: nsIURI of the referrer for this visit
+ * }
+ * @param [optional] aCallback
+ * Function to be invoked on completion.
+ */
+function addVisits(aPlaceInfo, aCallback) {
+ let places = [];
+ if (aPlaceInfo instanceof Ci.nsIURI) {
+ places.push({ uri: aPlaceInfo });
+ }
+ else if (Array.isArray(aPlaceInfo)) {
+ places = places.concat(aPlaceInfo);
+ } else {
+ places.push(aPlaceInfo)
+ }
+
+ // Create mozIVisitInfo for each entry.
+ let now = Date.now();
+ for (let i = 0; i < places.length; i++) {
+ if (!places[i].title) {
+ places[i].title = "test visit for " + places[i].uri.spec;
+ }
+ places[i].visits = [{
+ transitionType: places[i].transition === undefined ? PlacesUtils.history.TRANSITION_LINK
+ : places[i].transition,
+ visitDate: places[i].visitDate || (now++) * 1000,
+ referrerURI: places[i].referrer
+ }];
+ }
+
+ PlacesUtils.asyncHistory.updatePlaces(
+ places,
+ {
+ handleError: function AAV_handleError() {
+ throw("Unexpected error in adding visit.");
+ },
+ handleResult: function () {},
+ handleCompletion: function UP_handleCompletion() {
+ if (aCallback)
+ aCallback();
+ }
+ }
+ );
+}
diff --git a/comm/suite/components/places/tests/chrome/test_0_bug510634.xul b/comm/suite/components/places/tests/chrome/test_0_bug510634.xul
new file mode 100644
index 0000000000..95515b8e9f
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_0_bug510634.xul
@@ -0,0 +1,87 @@
+<?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://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="510634: Wrong icons on bookmarks sidebar"
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <body xmlns="http://www.w3.org/1999/xhtml" />
+
+ <tree id="tree"
+ type="places"
+ flex="1">
+ <treecols>
+ <treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+
+ <script>
+ <![CDATA[
+
+ /**
+ * Bug 510634 - Wrong icons on bookmarks sidebar
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=510634
+ *
+ * Ensures that properties for special queries are set on their tree nodes,
+ * even if PlacesUIUtils.leftPaneFolderId was not initialized.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // We need to cache and restore this getter in order to simulate
+ // Bug 510634
+ let cachedLeftPaneFolderIdGetter =
+ PlacesUIUtils.__lookupGetter__("leftPaneFolderId");
+
+ let leftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
+
+ // restore the getter
+ PlacesUIUtils.__defineGetter__("leftPaneFolderId", cachedLeftPaneFolderIdGetter);
+
+ // Setup the places tree contents.
+ let tree = document.getElementById("tree");
+ tree.place = "place:queryType=1&folder=" + leftPaneFolderId;
+
+ // Open All Bookmarks
+ PlacesUtils.asContainer(tree.view.nodeForTreeIndex(1)).containerOpen = true;
+
+ // The query-property is set on the title column for each row.
+ let titleColumn = tree.treeBoxObject.columns.getColumnAt(0);
+
+ ["Tags", "AllBookmarks", "BookmarksToolbar",
+ "BookmarksMenu", "UnfiledBookmarks"].forEach(
+ function(aQueryName, aRow) {
+ let rowProperties = tree.view.getCellProperties(aRow, titleColumn).split(" ");
+ ok(rowProperties.includes("OrganizerQuery_" + aQueryName),
+ "OrganizerQuery_" + aQueryName + " is set");
+ }
+ );
+
+ // Close the root node
+ tree.result.root.containerOpen = false;
+
+ // Restore the getter for the next test.
+ PlacesUIUtils.__defineGetter__("leftPaneFolderId", cachedLeftPaneFolderIdGetter);
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+</window>
diff --git a/comm/suite/components/places/tests/chrome/test_0_multiple_left_pane.xul b/comm/suite/components/places/tests/chrome/test_0_multiple_left_pane.xul
new file mode 100644
index 0000000000..574bf3c1c6
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_0_multiple_left_pane.xul
@@ -0,0 +1,82 @@
+<?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/. -->
+
+<!-- Bug 466422:
+ - Check that we replace the left pane with a correct one if it gets corrupted
+ - and we end up having more than one. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Test handling of multiple left pane folders"
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+ <script>
+ <![CDATA[
+
+ function runTest() {
+ // Sanity checks.
+ ok(PlacesUtils, "PlacesUtils is running in chrome context");
+ ok(PlacesUIUtils, "PlacesUIUtils is running in chrome context");
+ ok(PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION > 0,
+ "Left pane version in chrome context, " +
+ "current version is: " + PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION );
+
+ var fakeLeftPanes = [];
+ var as = PlacesUtils.annotations;
+ var bs = PlacesUtils.bookmarks;
+
+ // We need 2 left pane folders to simulate a corrupt profile.
+ do {
+ let leftPaneItems = as.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ // Create a fake left pane folder.
+ let fakeLeftPaneRoot = bs.createFolder(PlacesUtils.placesRootId, "",
+ bs.DEFAULT_INDEX);
+ as.setItemAnnotation(fakeLeftPaneRoot, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
+ PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, 0,
+ as.EXPIRE_NEVER);
+ fakeLeftPanes.push(fakeLeftPaneRoot);
+ } while (fakeLeftPanes.length < 2);
+
+ // Initialize the left pane queries.
+ PlacesUIUtils.leftPaneFolderId;
+
+ // Check left pane.
+ ok(PlacesUIUtils.leftPaneFolderId > 0,
+ "Left pane folder correctly created");
+ var leftPaneItems = as.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
+ is(leftPaneItems.length, 1,
+ "We correctly have only 1 left pane folder");
+
+ // Check that all old left pane items have been removed.
+ fakeLeftPanes.forEach(function(aItemId) {
+ try {
+ bs.getItemTitle(aItemId);
+ throw("This folder should have been removed");
+ } catch (ex) {}
+ });
+
+ }
+ ]]>
+ </script>
+
+</window>
diff --git a/comm/suite/components/places/tests/chrome/test_bug427633_no_newfolder_if_noip.xul b/comm/suite/components/places/tests/chrome/test_bug427633_no_newfolder_if_noip.xul
new file mode 100644
index 0000000000..8c1d70ed2a
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_bug427633_no_newfolder_if_noip.xul
@@ -0,0 +1,83 @@
+<?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://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/skin/places/editBookmarkOverlay.css"?>
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/places/editBookmarkOverlay.xul"?>
+
+<!DOCTYPE window [
+ <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://communicator/locale/places/editBookmarkOverlay.dtd">
+ %editBookmarkOverlayDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Bug 427633 - Disable creating a New Folder in the bookmarks dialogs if insertionPoint is invalid"
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script src="chrome://communicator/content/places/editBookmarkOverlay.js"/>
+
+ <body xmlns="http://www.w3.org/1999/xhtml" />
+
+ <vbox id="editBookmarkPanelContent"/>
+
+ <script>
+ <![CDATA[
+
+ /**
+ * Bug 427633 - Disable creating a New Folder in the bookmarks dialogs if
+ * insertionPoint is invalid.
+ */
+
+ function runTest() {
+ var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+ function uri(spec) {
+ return Services.io.newURI(spec);
+ }
+
+ // Add a bookmark.
+ var itemId = bs.insertBookmark(bs.toolbarFolder,
+ uri("http://www.mozilla.org/"),
+ bs.DEFAULT_INDEX,
+ "mozilla");
+
+ // Init panel.
+ ok(gEditItemOverlay, "gEditItemOverlay is in context");
+ gEditItemOverlay.initPanel(itemId);
+ ok(gEditItemOverlay._initialized, "gEditItemOverlay is initialized");
+ // We must be sure tree is initialized, so we wait for place to be set.
+ SimpleTest.waitForExplicitFinish();
+ var tree = gEditItemOverlay._element("folderTree");
+ tree.addEventListener("DOMAttrModified", function treeDOMAttrMod(event) {
+ if (event.attrName != "place")
+ return;
+ tree.removeEventListener("DOMAttrModified", treeDOMAttrMod, false);
+ SimpleTest.executeSoon(function() {
+ tree.view.selection.clearSelection();
+ ok(document.getElementById("editBMPanel_newFolderButton").disabled,
+ "New folder button is disabled if there's no selection");
+
+ // Cleanup.
+ bs.removeItem(itemId);
+ SimpleTest.finish();
+ });
+ }, false);
+ // Open the folder tree.
+ document.getElementById("editBMPanel_foldersExpander").doCommand();
+ }
+ ]]>
+ </script>
+
+</window>
diff --git a/comm/suite/components/places/tests/chrome/test_bug485100-change-case-loses-tag.xul b/comm/suite/components/places/tests/chrome/test_bug485100-change-case-loses-tag.xul
new file mode 100644
index 0000000000..15c0ad4ad0
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_bug485100-change-case-loses-tag.xul
@@ -0,0 +1,82 @@
+<?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://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/skin/places/editBookmarkOverlay.css"?>
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/places/editBookmarkOverlay.xul"?>
+
+<!DOCTYPE window [
+ <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://communicator/locale/places/editBookmarkOverlay.dtd">
+ %editBookmarkOverlayDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="485100: Exchanging a letter of a tag name with its big/small equivalent removes tag from bookmark"
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script src="chrome://communicator/content/places/editBookmarkOverlay.js"/>
+
+ <body xmlns="http://www.w3.org/1999/xhtml" />
+
+ <vbox id="editBookmarkPanelContent"/>
+
+ <script>
+ <![CDATA[
+
+ function runTest() {
+ var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+ var ts = Cc["@mozilla.org/browser/tagging-service;1"].
+ getService(Ci.nsITaggingService);
+ function uri(spec) {
+ return Services.io.newURI(spec);
+ }
+
+ var testURI = uri("http://www.mozilla.org/");
+ var testTag = "foo";
+ var testTagUpper = "Foo";
+
+ // Add a bookmark
+ var itemId = bs.insertBookmark(bs.toolbarFolder,
+ testURI,
+ bs.DEFAULT_INDEX,
+ "mozilla");
+
+ // Init panel
+ ok(gEditItemOverlay, "gEditItemOverlay is in context");
+ gEditItemOverlay.initPanel(itemId);
+
+ // add a tag
+ document.getElementById("editBMPanel_tagsField").value = testTag;
+ gEditItemOverlay.onTagsFieldBlur();
+
+ // test that the tag has been added in the backend
+ is(ts.getTagsForURI(testURI)[0], testTag, "tags match");
+
+ // change the tag
+ document.getElementById("editBMPanel_tagsField").value = testTagUpper;
+ gEditItemOverlay.onTagsFieldBlur();
+
+ // test that the tag has been added in the backend
+ is(ts.getTagsForURI(testURI)[0], testTagUpper, "tags match");
+
+ // Cleanup.
+ ts.untagURI(testURI, [testTag]);
+ bs.removeItem(itemId);
+ }
+ ]]>
+ </script>
+
+</window>
diff --git a/comm/suite/components/places/tests/chrome/test_bug549192.xul b/comm/suite/components/places/tests/chrome/test_bug549192.xul
new file mode 100644
index 0000000000..55e5502764
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_bug549192.xul
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/licenses/publicdomain/
+ -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="549192: History view not updated after deleting entry"
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script src="head.js" />
+
+ <body xmlns="http://www.w3.org/1999/xhtml" />
+
+ <tree id="tree"
+ type="places"
+ flatList="true"
+ flex="1">
+ <treecols>
+ <treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+
+ <script>
+ <![CDATA[
+ /**
+ * Bug 1388827 / Bug 874407
+ * Ensures that history views are updated properly after visits.
+ *
+ * Bug 549192
+ * Ensures that history views are updated after deleting entries.
+ */
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // The mochitest page is added to history.
+ waitForClearHistory(continue_test);
+ }
+
+ function continue_test() {
+ // Add some visits.
+ let vtime = Date.now() * 1000;
+ const ttype = PlacesUtils.history.TRANSITION_TYPED;
+ let places =
+ [{ uri: Services.io.newURI("http://example.tld/"),
+ visitDate: ++vtime, transition: ttype },
+ { uri: Services.io.newURI("http://example2.tld/"),
+ visitDate: ++vtime, transition: ttype },
+ { uri: Services.io.newURI("http://example3.tld/"),
+ visitDate: ++vtime, transition: ttype }];
+
+ addVisits(places, function() {
+ // Make a history query.
+ let query = PlacesUtils.history.getNewQuery();
+ let opts = PlacesUtils.history.getNewQueryOptions();
+ opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
+ let queryURI = PlacesUtils.history.queriesToQueryString([query], 1, opts);
+
+ // Setup the places tree contents.
+ var tree = document.getElementById("tree");
+ tree.place = queryURI;
+
+ // loop through the rows and check them.
+ let treeView = tree.view;
+ let selection = treeView.selection;
+ let rc = treeView.rowCount;
+
+ for (let i = 0; i < rc; i++) {
+ selection.select(i);
+ let node = tree.selectedNode;
+ is(node.uri, places[rc - i - 1].uri.spec,
+ "Found expected node at position " + i + ".");
+ }
+
+ is(rc, 3, "Found expected number of rows.");
+
+ // First check live-update of the view when adding visits.
+ places.forEach(place => place.visitDate = ++vtime);
+ addVisits(places, function() {
+ for (let i = 0; i < rc; i++) {
+ selection.select(i);
+ let node = tree.selectedNode;
+ is(node.uri, places[rc - i - 1].uri.spec,
+ "Found expected node at position " + i + ".");
+ }
+
+ // Now remove the pages and verify live-update again.
+ for (let i = 0; i < rc; i++) {
+ selection.select(0);
+ let node = tree.selectedNode;
+ tree.controller.remove("Removing page");
+ ok(treeView.treeIndexForNode(node) == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE,
+ node.uri + " removed.");
+ ok(treeView.rowCount == rc - i - 1, "Rows count decreased");
+ }
+
+ // Cleanup.
+ waitForClearHistory(SimpleTest.finish);
+ });
+ });
+ }
+
+ ]]></script>
+</window>
diff --git a/comm/suite/components/places/tests/chrome/test_bug549491.xul b/comm/suite/components/places/tests/chrome/test_bug549491.xul
new file mode 100644
index 0000000000..f211d62fd2
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_bug549491.xul
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/licenses/publicdomain/
+ -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="549491: 'The root node is never visible' exception when details of the root node are modified "
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script src="head.js" />
+
+ <body xmlns="http://www.w3.org/1999/xhtml" />
+
+ <tree id="tree"
+ type="places"
+ flatList="true"
+ flex="1">
+ <treecols>
+ <treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="Date" anonid="date" flex="1"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+
+ <script>
+ <![CDATA[
+ /**
+ * Bug 549491
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=549491
+ *
+ * Ensures that changing the details of places tree's root-node doesn't
+ * throw.
+ */
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // The mochitest page is added to history.
+ waitForClearHistory(continue_test);
+ }
+
+ function continue_test() {
+ addVisits(
+ {uri: Services.io.newURI("http://example.tld/"),
+ visitDate: Date.now() * 1000,
+ transition: PlacesUtils.history.TRANSITION_TYPED},
+ function() {
+ // Make a history query.
+ let query = PlacesUtils.history.getNewQuery();
+ let opts = PlacesUtils.history.getNewQueryOptions();
+ let queryURI = PlacesUtils.history.queriesToQueryString([query], 1, opts);
+
+ // Setup the places tree contents.
+ let tree = document.getElementById("tree");
+ tree.place = queryURI;
+
+ let rootNode = tree.result.root;
+ let obs = tree.view.QueryInterface(Ci.nsINavHistoryResultObserver);
+ obs.nodeHistoryDetailsChanged(rootNode, rootNode.time, rootNode.accessCount);
+ obs.nodeTitleChanged(rootNode, rootNode.title);
+ ok(true, "No exceptions thrown");
+
+ // Cleanup.
+ waitForClearHistory(SimpleTest.finish);
+ });
+ }
+
+ /**
+ * Clears history invoking callback when done.
+ */
+ function waitForClearHistory(aCallback) {
+ const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
+ let observer = {
+ observe: function(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
+ aCallback();
+ }
+ };
+ Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED);
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
+ }
+
+ ]]></script>
+</window>
diff --git a/comm/suite/components/places/tests/chrome/test_treeview_date.xul b/comm/suite/components/places/tests/chrome/test_treeview_date.xul
new file mode 100644
index 0000000000..c390a66d2b
--- /dev/null
+++ b/comm/suite/components/places/tests/chrome/test_treeview_date.xul
@@ -0,0 +1,179 @@
+<?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://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<?xml-stylesheet href="chrome://communicator/content/places/places.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/places/organizer.css"?>
+<?xul-overlay href="chrome://communicator/content/places/placesOverlay.xul"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="435322: Places tree view's formatting"
+ onload="runTest();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script src="head.js" />
+
+ <body xmlns="http://www.w3.org/1999/xhtml" />
+
+ <tree id="tree"
+ type="places"
+ flatList="true"
+ flex="1">
+ <treecols>
+ <treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="Tags" id="tags" anonid="tags" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="Url" id="url" anonid="url" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="Visit Date" id="date" anonid="date" flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol label="Visit Count" id="visitCount" anonid="visitCount" flex="1"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+
+ <script>
+ <![CDATA[
+
+ /**
+ * Bug 435322
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=435322
+ *
+ * Ensures that date in places treeviews is correctly formatted.
+ */
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // The mochitest page is added to history.
+ waitForClearHistory(continue_test);
+ }
+
+ function continue_test() {
+
+ var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ var bh = hs.QueryInterface(Ci.nsIBrowserHistory);
+ var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+
+ function uri(spec) {
+ return Services.io.newURI(spec);
+ }
+
+ var midnight = new Date();
+ midnight.setHours(0);
+ midnight.setMinutes(0);
+ midnight.setSeconds(0);
+ midnight.setMilliseconds(0);
+
+ function addVisitsCallback() {
+ // add a bookmark to the midnight visit
+ var itemId = bs.insertBookmark(bs.toolbarFolder,
+ uri("http://at.midnight.com/"),
+ bs.DEFAULT_INDEX,
+ "A bookmark at midnight");
+ // Make a history query.
+ var query = hs.getNewQuery();
+ var opts = hs.getNewQueryOptions();
+ var queryURI = hs.queriesToQueryString([query], 1, opts);
+
+ // Setup the places tree contents.
+ var tree = document.getElementById("tree");
+ tree.place = queryURI;
+
+ // loop through the rows and check formatting
+ var treeView = tree.view;
+ var rc = treeView.rowCount;
+ ok(rc >= 3, "Rows found");
+ var columns = tree.columns;
+ ok(columns.count > 0, "Columns found");
+ for (var r = 0; r < rc; r++) {
+ var node = treeView.nodeForTreeIndex(r);
+ ok(node, "Places node found");
+ for (var ci = 0; ci < columns.count; ci++) {
+ var c = columns.getColumnAt(ci);
+ var text = treeView.getCellText(r, c);
+ switch (c.element.getAttribute("anonid")) {
+ case "title":
+ // The title can differ, we did not set any title so we would
+ // expect null, but in such a case the view will generate a title
+ // through PlacesUIUtils.getBestTitle.
+ if (node.title)
+ is(text, node.title, "Title is correct");
+ break;
+ case "url":
+ is(text, node.uri, "Uri is correct");
+ break;
+ case "date":
+ var timeObj = new Date(node.time / 1000);
+ // Default is short date format.
+ let dtOptions = { dateStyle: "short", timeStyle: "short" };
+ // For today's visits we don't show date portion.
+ if (node.uri == "http://at.midnight.com/" ||
+ node.uri == "http://after.midnight.com/") {
+ dtOptions.dateStyle = undefined;
+ } else if (node.uri != "http://before.midnight.com/") {
+ // Avoid to test spurious uris, due to how the test works
+ // a redirecting uri could be put in the tree while we test.
+ break;
+ }
+ let timeStr = new Services.intl.DateTimeFormat(undefined, dtOptions).format(timeObj);
+ is(text, timeStr, "Date format is correct");
+ break;
+ case "visitCount":
+ is(text, 1, "Visit count is correct");
+ break;
+ }
+ }
+ }
+ // Cleanup.
+ bs.removeItem(itemId);
+ waitForClearHistory(SimpleTest.finish);
+ }
+
+ // Add a visit 1ms before midnight, a visit at midnight, and a visit 1ms
+ // after midnight.
+ addVisits(
+ [{uri: uri("http://before.midnight.com/"),
+ visitDate: (midnight.getTime() - 1) * 1000,
+ transition: hs.TRANSITION_TYPED},
+ {uri: uri("http://at.midnight.com/"),
+ visitDate: (midnight.getTime()) * 1000,
+ transition: hs.TRANSITION_TYPED},
+ {uri: uri("http://after.midnight.com/"),
+ visitDate: (midnight.getTime() + 1) * 1000,
+ transition: hs.TRANSITION_TYPED}],
+ addVisitsCallback);
+
+ }
+
+ /**
+ * Clears history invoking callback when done.
+ */
+ function waitForClearHistory(aCallback) {
+ const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
+ let observer = {
+ observe: function(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
+ aCallback();
+ }
+ };
+ Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED);
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
+ }
+
+ ]]>
+ </script>
+</window>
diff --git a/comm/suite/components/places/tests/head_common.js b/comm/suite/components/places/tests/head_common.js
new file mode 100644
index 0000000000..88ecb6d6ba
--- /dev/null
+++ b/comm/suite/components/places/tests/head_common.js
@@ -0,0 +1,868 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const CURRENT_SCHEMA_VERSION = 33;
+const FIRST_UPGRADABLE_SCHEMA_VERSION = 11;
+
+const NS_APP_USER_PROFILE_50_DIR = "ProfD";
+const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
+
+// Shortcuts to transitions type.
+const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
+const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
+const TRANSITION_BOOKMARK = Ci.nsINavHistoryService.TRANSITION_BOOKMARK;
+const TRANSITION_EMBED = Ci.nsINavHistoryService.TRANSITION_EMBED;
+const TRANSITION_FRAMED_LINK = Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK;
+const TRANSITION_REDIRECT_PERMANENT = Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT;
+const TRANSITION_REDIRECT_TEMPORARY = Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY;
+const TRANSITION_DOWNLOAD = Ci.nsINavHistoryService.TRANSITION_DOWNLOAD;
+const TRANSITION_RELOAD = Ci.nsINavHistoryService.TRANSITION_RELOAD;
+
+const TITLE_LENGTH_MAX = 4096;
+
+Cu.importGlobalProperties(["URL"]);
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+ChromeUtils.defineModuleGetter(this, "Promise",
+ "resource://gre/modules/Promise.jsm");
+ChromeUtils.defineModuleGetter(this, "Services",
+ "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(this, "BookmarkJSONUtils",
+ "resource://gre/modules/BookmarkJSONUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "BookmarkHTMLUtils",
+ "resource://gre/modules/BookmarkHTMLUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesBackups",
+ "resource://gre/modules/PlacesBackups.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
+ "resource://testing-common/PlacesTestUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "PlacesTransactions",
+ "resource://gre/modules/PlacesTransactions.jsm");
+ChromeUtils.defineModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+ChromeUtils.defineModuleGetter(this, "Sqlite",
+ "resource://gre/modules/Sqlite.jsm");
+
+// This imports various other objects in addition to PlacesUtils.
+var {PlacesUtils} = ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "SMALLPNG_DATA_URI", function() {
+ return NetUtil.newURI(
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
+ "AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==");
+});
+XPCOMUtils.defineLazyGetter(this, "SMALLSVG_DATA_URI", function() {
+ return NetUtil.newURI(
+ "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy5" +
+ "3My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBmaWxs" +
+ "PSIjNDI0ZTVhIj4NCiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iN" +
+ "DQiIHN0cm9rZT0iIzQyNGU1YSIgc3Ryb2tlLXdpZHRoPSIxMSIgZmlsbD" +
+ "0ibm9uZSIvPg0KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjI0LjYiIHI9IjY" +
+ "uNCIvPg0KICA8cmVjdCB4PSI0NSIgeT0iMzkuOSIgd2lkdGg9IjEwLjEi" +
+ "IGhlaWdodD0iNDEuOCIvPg0KPC9zdmc%2BDQo%3D");
+});
+
+var gTestDir = do_get_cwd();
+
+// Initialize profile.
+var gProfD = do_get_profile();
+
+// Remove any old database.
+clearDB();
+
+/**
+ * Shortcut to create a nsIURI.
+ *
+ * @param aSpec
+ * URLString of the uri.
+ */
+function uri(aSpec) {
+ return NetUtil.newURI(aSpec);
+}
+
+
+/**
+ * Gets the database connection. If the Places connection is invalid it will
+ * try to create a new connection.
+ *
+ * @param [optional] aForceNewConnection
+ * Forces creation of a new connection to the database. When a
+ * connection is asyncClosed it cannot anymore schedule async statements,
+ * though connectionReady will keep returning true (Bug 726990).
+ *
+ * @return The database connection or null if unable to get one.
+ */
+var gDBConn;
+function DBConn(aForceNewConnection) {
+ if (!aForceNewConnection) {
+ let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+ .DBConnection;
+ if (db.connectionReady)
+ return db;
+ }
+
+ // If the Places database connection has been closed, create a new connection.
+ if (!gDBConn || aForceNewConnection) {
+ let file = Services.dirsvc.get('ProfD', Ci.nsIFile);
+ file.append("places.sqlite");
+ let dbConn = gDBConn = Services.storage.openDatabase(file);
+
+ // Be sure to cleanly close this connection.
+ promiseTopicObserved("profile-before-change").then(() => dbConn.asyncClose());
+ }
+
+ return gDBConn.connectionReady ? gDBConn : null;
+}
+
+/**
+ * Reads data from the provided inputstream.
+ *
+ * @return an array of bytes.
+ */
+function readInputStreamData(aStream) {
+ let bistream = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ try {
+ bistream.setInputStream(aStream);
+ let expectedData = [];
+ let avail;
+ while ((avail = bistream.available())) {
+ expectedData = expectedData.concat(bistream.readByteArray(avail));
+ }
+ return expectedData;
+ } finally {
+ bistream.close();
+ }
+}
+
+/**
+ * Reads the data from the specified nsIFile.
+ *
+ * @param aFile
+ * The nsIFile to read from.
+ * @return an array of bytes.
+ */
+function readFileData(aFile) {
+ let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ // init the stream as RD_ONLY, -1 == default permissions.
+ inputStream.init(aFile, 0x01, -1, null);
+
+ // Check the returned size versus the expected size.
+ let size = inputStream.available();
+ let bytes = readInputStreamData(inputStream);
+ if (size != bytes.length) {
+ throw "Didn't read expected number of bytes";
+ }
+ return bytes;
+}
+
+/**
+ * Reads the data from the named file, verifying the expected file length.
+ *
+ * @param aFileName
+ * This file should be located in the same folder as the test.
+ * @param aExpectedLength
+ * Expected length of the file.
+ *
+ * @return The array of bytes read from the file.
+ */
+function readFileOfLength(aFileName, aExpectedLength) {
+ let data = readFileData(do_get_file(aFileName));
+ Assert.equal(data.length, aExpectedLength);
+ return data;
+}
+
+
+/**
+ * Returns the base64-encoded version of the given string. This function is
+ * similar to window.btoa, but is available to xpcshell tests also.
+ *
+ * @param aString
+ * Each character in this string corresponds to a byte, and must be a
+ * code point in the range 0-255.
+ *
+ * @return The base64-encoded string.
+ */
+function base64EncodeString(aString) {
+ var stream = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ stream.setData(aString, aString.length);
+ var encoder = Cc["@mozilla.org/scriptablebase64encoder;1"]
+ .createInstance(Ci.nsIScriptableBase64Encoder);
+ return encoder.encodeToString(stream, aString.length);
+}
+
+
+/**
+ * Compares two arrays, and returns true if they are equal.
+ *
+ * @param aArray1
+ * First array to compare.
+ * @param aArray2
+ * Second array to compare.
+ */
+function compareArrays(aArray1, aArray2) {
+ if (aArray1.length != aArray2.length) {
+ print("compareArrays: array lengths differ\n");
+ return false;
+ }
+
+ for (let i = 0; i < aArray1.length; i++) {
+ if (aArray1[i] != aArray2[i]) {
+ print("compareArrays: arrays differ at index " + i + ": " +
+ "(" + aArray1[i] + ") != (" + aArray2[i] +")\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Deletes a previously created sqlite file from the profile folder.
+ */
+function clearDB() {
+ try {
+ let file = Services.dirsvc.get('ProfD', Ci.nsIFile);
+ file.append("places.sqlite");
+ if (file.exists())
+ file.remove(false);
+ } catch (ex) { dump("Exception: " + ex); }
+}
+
+
+/**
+ * Dumps the rows of a table out to the console.
+ *
+ * @param aName
+ * The name of the table or view to output.
+ */
+function dump_table(aName)
+{
+ let stmt = DBConn().createStatement("SELECT * FROM " + aName);
+
+ print("\n*** Printing data from " + aName);
+ let count = 0;
+ while (stmt.executeStep()) {
+ let columns = stmt.numEntries;
+
+ if (count == 0) {
+ // Print the column names.
+ for (let i = 0; i < columns; i++)
+ dump(stmt.getColumnName(i) + "\t");
+ dump("\n");
+ }
+
+ // Print the rows.
+ for (let i = 0; i < columns; i++) {
+ switch (stmt.getTypeOfIndex(i)) {
+ case Ci.mozIStorageValueArray.VALUE_TYPE_NULL:
+ dump("NULL\t");
+ break;
+ case Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER:
+ dump(stmt.getInt64(i) + "\t");
+ break;
+ case Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT:
+ dump(stmt.getDouble(i) + "\t");
+ break;
+ case Ci.mozIStorageValueArray.VALUE_TYPE_TEXT:
+ dump(stmt.getString(i) + "\t");
+ break;
+ }
+ }
+ dump("\n");
+
+ count++;
+ }
+ print("*** There were a total of " + count + " rows of data.\n");
+
+ stmt.finalize();
+}
+
+
+/**
+ * Checks if an address is found in the database.
+ * @param aURI
+ * nsIURI or address to look for.
+ * @return place id of the page or 0 if not found
+ */
+function page_in_database(aURI)
+{
+ let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+ let stmt = DBConn().createStatement(
+ "SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url"
+ );
+ stmt.params.url = url;
+ try {
+ if (!stmt.executeStep())
+ return 0;
+ return stmt.getInt64(0);
+ }
+ finally {
+ stmt.finalize();
+ }
+}
+
+/**
+ * Checks how many visits exist for a specified page.
+ * @param aURI
+ * nsIURI or address to look for.
+ * @return number of visits found.
+ */
+function visits_in_database(aURI)
+{
+ let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+ let stmt = DBConn().createStatement(
+ `SELECT count(*) FROM moz_historyvisits v
+ JOIN moz_places h ON h.id = v.place_id
+ WHERE url_hash = hash(:url) AND url = :url`
+ );
+ stmt.params.url = url;
+ try {
+ if (!stmt.executeStep())
+ return 0;
+ return stmt.getInt64(0);
+ }
+ finally {
+ stmt.finalize();
+ }
+}
+
+/**
+ * Checks that we don't have any bookmark
+ */
+function check_no_bookmarks() {
+ let query = PlacesUtils.history.getNewQuery();
+ let folders = [
+ PlacesUtils.bookmarks.toolbarFolder,
+ PlacesUtils.bookmarks.bookmarksMenuFolder,
+ PlacesUtils.bookmarks.unfiledBookmarksFolder,
+ ];
+ query.setFolders(folders, 3);
+ let options = PlacesUtils.history.getNewQueryOptions();
+ options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
+ let root = PlacesUtils.history.executeQuery(query, options).root;
+ root.containerOpen = true;
+ if (root.childCount != 0)
+ do_throw("Unable to remove all bookmarks");
+ root.containerOpen = false;
+}
+
+/**
+ * Allows waiting for an observer notification once.
+ *
+ * @param aTopic
+ * Notification topic to observe.
+ *
+ * @return {Promise}
+ * @resolves The array [aSubject, aData] from the observed notification.
+ * @rejects Never.
+ */
+function promiseTopicObserved(aTopic)
+{
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(observe, aTopic);
+ resolve([aSubject, aData]);
+ }, aTopic);
+ });
+}
+
+/**
+ * Simulates a Places shutdown.
+ */
+var shutdownPlaces = function() {
+ info("shutdownPlaces: starting");
+ let promise = new Promise(resolve => {
+ Services.obs.addObserver(resolve, "places-connection-closed");
+ });
+ let hs = PlacesUtils.history.QueryInterface(Ci.nsIObserver);
+ hs.observe(null, "profile-change-teardown", null);
+ info("shutdownPlaces: sent profile-change-teardown");
+ hs.observe(null, "test-simulate-places-shutdown", null);
+ info("shutdownPlaces: sent test-simulate-places-shutdown");
+ return promise.then(() => {
+ info("shutdownPlaces: complete");
+ });
+};
+
+const FILENAME_BOOKMARKS_HTML = "bookmarks.html";
+const FILENAME_BOOKMARKS_JSON = "bookmarks-" +
+ (PlacesBackups.toISODateString(new Date())) + ".json";
+
+/**
+ * Creates a bookmarks.html file in the profile folder from a given source file.
+ *
+ * @param aFilename
+ * Name of the file to copy to the profile folder. This file must
+ * exist in the directory that contains the test files.
+ *
+ * @return nsIFile object for the file.
+ */
+function create_bookmarks_html(aFilename) {
+ if (!aFilename)
+ do_throw("you must pass a filename to create_bookmarks_html function");
+ remove_bookmarks_html();
+ let bookmarksHTMLFile = gTestDir.clone();
+ bookmarksHTMLFile.append(aFilename);
+ Assert.ok(bookmarksHTMLFile.exists());
+ bookmarksHTMLFile.copyTo(gProfD, FILENAME_BOOKMARKS_HTML);
+ let profileBookmarksHTMLFile = gProfD.clone();
+ profileBookmarksHTMLFile.append(FILENAME_BOOKMARKS_HTML);
+ Assert.ok(profileBookmarksHTMLFile.exists());
+ return profileBookmarksHTMLFile;
+}
+
+
+/**
+ * Remove bookmarks.html file from the profile folder.
+ */
+function remove_bookmarks_html() {
+ let profileBookmarksHTMLFile = gProfD.clone();
+ profileBookmarksHTMLFile.append(FILENAME_BOOKMARKS_HTML);
+ if (profileBookmarksHTMLFile.exists()) {
+ profileBookmarksHTMLFile.remove(false);
+ Assert.ok(!profileBookmarksHTMLFile.exists());
+ }
+}
+
+
+/**
+ * Check bookmarks.html file exists in the profile folder.
+ *
+ * @return nsIFile object for the file.
+ */
+function check_bookmarks_html() {
+ let profileBookmarksHTMLFile = gProfD.clone();
+ profileBookmarksHTMLFile.append(FILENAME_BOOKMARKS_HTML);
+ Assert.ok(profileBookmarksHTMLFile.exists());
+ return profileBookmarksHTMLFile;
+}
+
+
+/**
+ * Creates a JSON backup in the profile folder folder from a given source file.
+ *
+ * @param aFilename
+ * Name of the file to copy to the profile folder. This file must
+ * exist in the directory that contains the test files.
+ *
+ * @return nsIFile object for the file.
+ */
+function create_JSON_backup(aFilename) {
+ if (!aFilename)
+ do_throw("you must pass a filename to create_JSON_backup function");
+ let bookmarksBackupDir = gProfD.clone();
+ bookmarksBackupDir.append("bookmarkbackups");
+ if (!bookmarksBackupDir.exists()) {
+ bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+ Assert.ok(bookmarksBackupDir.exists());
+ }
+ let profileBookmarksJSONFile = bookmarksBackupDir.clone();
+ profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
+ if (profileBookmarksJSONFile.exists()) {
+ profileBookmarksJSONFile.remove();
+ }
+ let bookmarksJSONFile = gTestDir.clone();
+ bookmarksJSONFile.append(aFilename);
+ Assert.ok(bookmarksJSONFile.exists());
+ bookmarksJSONFile.copyTo(bookmarksBackupDir, FILENAME_BOOKMARKS_JSON);
+ profileBookmarksJSONFile = bookmarksBackupDir.clone();
+ profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
+ Assert.ok(profileBookmarksJSONFile.exists());
+ return profileBookmarksJSONFile;
+}
+
+
+/**
+ * Remove bookmarksbackup dir and all backups from the profile folder.
+ */
+function remove_all_JSON_backups() {
+ let bookmarksBackupDir = gProfD.clone();
+ bookmarksBackupDir.append("bookmarkbackups");
+ if (bookmarksBackupDir.exists()) {
+ bookmarksBackupDir.remove(true);
+ Assert.ok(!bookmarksBackupDir.exists());
+ }
+}
+
+
+/**
+ * Check a JSON backup file for today exists in the profile folder.
+ *
+ * @param aIsAutomaticBackup The boolean indicates whether it's an automatic
+ * backup.
+ * @return nsIFile object for the file.
+ */
+function check_JSON_backup(aIsAutomaticBackup) {
+ let profileBookmarksJSONFile;
+ if (aIsAutomaticBackup) {
+ let bookmarksBackupDir = gProfD.clone();
+ bookmarksBackupDir.append("bookmarkbackups");
+ let files = bookmarksBackupDir.directoryEntries;
+ let backup_date = PlacesBackups.toISODateString(new Date());
+ while (files.hasMoreElements()) {
+ let entry = files.nextFile;
+ if (PlacesBackups.filenamesRegex.test(entry.leafName)) {
+ profileBookmarksJSONFile = entry;
+ break;
+ }
+ }
+ } else {
+ profileBookmarksJSONFile = gProfD.clone();
+ profileBookmarksJSONFile.append("bookmarkbackups");
+ profileBookmarksJSONFile.append(FILENAME_BOOKMARKS_JSON);
+ }
+ Assert.ok(profileBookmarksJSONFile.exists());
+ return profileBookmarksJSONFile;
+}
+
+/**
+ * Returns the frecency of a url.
+ *
+ * @param aURI
+ * The URI or spec to get frecency for.
+ * @return the frecency value.
+ */
+function frecencyForUrl(aURI)
+{
+ let url = aURI;
+ if (aURI instanceof Ci.nsIURI) {
+ url = aURI.spec;
+ } else if (aURI instanceof URL) {
+ url = aURI.href;
+ }
+ let stmt = DBConn().createStatement(
+ "SELECT frecency FROM moz_places WHERE url_hash = hash(?1) AND url = ?1"
+ );
+ stmt.bindByIndex(0, url);
+ try {
+ if (!stmt.executeStep()) {
+ throw new Error("No result for frecency.");
+ }
+ return stmt.getInt32(0);
+ } finally {
+ stmt.finalize();
+ }
+}
+
+/**
+ * Returns the hidden status of a url.
+ *
+ * @param aURI
+ * The URI or spec to get hidden for.
+ * @return @return true if the url is hidden, false otherwise.
+ */
+function isUrlHidden(aURI)
+{
+ let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+ let stmt = DBConn().createStatement(
+ "SELECT hidden FROM moz_places WHERE url_hash = hash(?1) AND url = ?1"
+ );
+ stmt.bindByIndex(0, url);
+ if (!stmt.executeStep())
+ throw new Error("No result for hidden.");
+ let hidden = stmt.getInt32(0);
+ stmt.finalize();
+
+ return !!hidden;
+}
+
+/**
+ * Compares two times in usecs, considering eventual platform timers skews.
+ *
+ * @param aTimeBefore
+ * The older time in usecs.
+ * @param aTimeAfter
+ * The newer time in usecs.
+ * @return true if times are ordered, false otherwise.
+ */
+function is_time_ordered(before, after) {
+ // Windows has an estimated 16ms timers precision, since Date.now() and
+ // PR_Now() use different code atm, the results can be unordered by this
+ // amount of time. See bug 558745 and bug 557406.
+ let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
+ // Just to be safe we consider 20ms.
+ let skew = isWindows ? 20000000 : 0;
+ return after - before > -skew;
+}
+
+/**
+ * Shutdowns Places, invoking the callback when the connection has been closed.
+ *
+ * @param aCallback
+ * Function to be called when done.
+ */
+function waitForConnectionClosed(aCallback)
+{
+ promiseTopicObserved("places-connection-closed").then(aCallback);
+ shutdownPlaces();
+}
+
+/**
+ * Tests if a given guid is valid for use in Places or not.
+ *
+ * @param aGuid
+ * The guid to test.
+ * @param [optional] aStack
+ * The stack frame used to report the error.
+ */
+function do_check_valid_places_guid(aGuid,
+ aStack)
+{
+ if (!aStack) {
+ aStack = Components.stack.caller;
+ }
+ Assert.ok(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), aStack);
+}
+
+/**
+ * Retrieves the guid for a given uri.
+ *
+ * @param aURI
+ * The uri to check.
+ * @param [optional] aStack
+ * The stack frame used to report the error.
+ * @return the associated the guid.
+ */
+function do_get_guid_for_uri(aURI,
+ aStack)
+{
+ if (!aStack) {
+ aStack = Components.stack.caller;
+ }
+ let stmt = DBConn().createStatement(
+ `SELECT guid
+ FROM moz_places
+ WHERE url_hash = hash(:url) AND url = :url`
+ );
+ stmt.params.url = aURI.spec;
+ Assert.ok(stmt.executeStep(), aStack);
+ let guid = stmt.row.guid;
+ stmt.finalize();
+ do_check_valid_places_guid(guid, aStack);
+ return guid;
+}
+
+/**
+ * Tests that a guid was set in moz_places for a given uri.
+ *
+ * @param aURI
+ * The uri to check.
+ * @param [optional] aGUID
+ * The expected guid in the database.
+ */
+function do_check_guid_for_uri(aURI,
+ aGUID)
+{
+ let caller = Components.stack.caller;
+ let guid = do_get_guid_for_uri(aURI, caller);
+ if (aGUID) {
+ do_check_valid_places_guid(aGUID, caller);
+ Assert.equal(guid, aGUID, caller);
+ }
+}
+
+/**
+ * Retrieves the guid for a given bookmark.
+ *
+ * @param aId
+ * The bookmark id to check.
+ * @param [optional] aStack
+ * The stack frame used to report the error.
+ * @return the associated the guid.
+ */
+function do_get_guid_for_bookmark(aId,
+ aStack)
+{
+ if (!aStack) {
+ aStack = Components.stack.caller;
+ }
+ let stmt = DBConn().createStatement(
+ `SELECT guid
+ FROM moz_bookmarks
+ WHERE id = :item_id`
+ );
+ stmt.params.item_id = aId;
+ Assert.ok(stmt.executeStep(), aStack);
+ let guid = stmt.row.guid;
+ stmt.finalize();
+ do_check_valid_places_guid(guid, aStack);
+ return guid;
+}
+
+/**
+ * Tests that a guid was set in moz_places for a given bookmark.
+ *
+ * @param aId
+ * The bookmark id to check.
+ * @param [optional] aGUID
+ * The expected guid in the database.
+ */
+function do_check_guid_for_bookmark(aId,
+ aGUID)
+{
+ let caller = Components.stack.caller;
+ let guid = do_get_guid_for_bookmark(aId, caller);
+ if (aGUID) {
+ do_check_valid_places_guid(aGUID, caller);
+ Assert.equal(guid, aGUID, caller);
+ }
+}
+
+/**
+ * Compares 2 arrays returning whether they contains the same elements.
+ *
+ * @param a1
+ * First array to compare.
+ * @param a2
+ * Second array to compare.
+ * @param [optional] sorted
+ * Whether the comparison should take in count position of the elements.
+ * @return true if the arrays contain the same elements, false otherwise.
+ */
+function do_compare_arrays(a1, a2, sorted)
+{
+ if (a1.length != a2.length)
+ return false;
+
+ if (sorted) {
+ return a1.every((e, i) => e == a2[i]);
+ }
+ return a1.filter(e => !a2.includes(e)).length == 0 &&
+ a2.filter(e => !a1.includes(e)).length == 0;
+}
+
+/**
+ * Generic nsINavBookmarkObserver that doesn't implement anything, but provides
+ * dummy methods to prevent errors about an object not having a certain method.
+ */
+function NavBookmarkObserver() {}
+
+NavBookmarkObserver.prototype = {
+ onBeginUpdateBatch: function () {},
+ onEndUpdateBatch: function () {},
+ onItemAdded: function () {},
+ onItemRemoved: function () {},
+ onItemChanged: function () {},
+ onItemVisited: function () {},
+ onItemMoved: function () {},
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsINavBookmarkObserver,
+ ])
+};
+
+/**
+ * Generic nsINavHistoryObserver that doesn't implement anything, but provides
+ * dummy methods to prevent errors about an object not having a certain method.
+ */
+function NavHistoryObserver() {}
+
+NavHistoryObserver.prototype = {
+ onBeginUpdateBatch: function () {},
+ onEndUpdateBatch: function () {},
+ onVisit: function () {},
+ onTitleChanged: function () {},
+ onDeleteURI: function () {},
+ onClearHistory: function () {},
+ onPageChanged: function () {},
+ onDeleteVisits: function () {},
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsINavHistoryObserver,
+ ])
+};
+
+/**
+ * Generic nsINavHistoryResultObserver that doesn't implement anything, but
+ * provides dummy methods to prevent errors about an object not having a certain
+ * method.
+ */
+function NavHistoryResultObserver() {}
+
+NavHistoryResultObserver.prototype = {
+ batching: function () {},
+ containerStateChanged: function () {},
+ invalidateContainer: function () {},
+ nodeAnnotationChanged: function () {},
+ nodeDateAddedChanged: function () {},
+ nodeHistoryDetailsChanged: function () {},
+ nodeIconChanged: function () {},
+ nodeInserted: function () {},
+ nodeKeywordChanged: function () {},
+ nodeLastModifiedChanged: function () {},
+ nodeMoved: function () {},
+ nodeRemoved: function () {},
+ nodeTagsChanged: function () {},
+ nodeTitleChanged: function () {},
+ nodeURIChanged: function () {},
+ sortingChanged: function () {},
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsINavHistoryResultObserver,
+ ])
+};
+
+/**
+ * Asynchronously check a url is visited.
+ *
+ * @param aURI The URI.
+ * @return {Promise}
+ * @resolves When the check has been added successfully.
+ * @rejects JavaScript exception.
+ */
+function promiseIsURIVisited(aURI) {
+ let deferred = Promise.defer();
+
+ PlacesUtils.asyncHistory.isURIVisited(aURI, function(aURI, aIsVisited) {
+ deferred.resolve(aIsVisited);
+ });
+
+ return deferred.promise;
+}
+
+/**
+ * Asynchronously set the favicon associated with a page.
+ * @param aPageURI
+ * The page's URI
+ * @param aIconURI
+ * The URI of the favicon to be set.
+ */
+function promiseSetIconForPage(aPageURI, aIconURI) {
+ let deferred = Promise.defer();
+ PlacesUtils.favicons.setAndFetchFaviconForPage(
+ aPageURI, aIconURI, true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ () => { deferred.resolve(); },
+ Services.scriptSecurityManager.getSystemPrincipal());
+ return deferred.promise;
+}
+
+function checkBookmarkObject(info) {
+ do_check_valid_places_guid(info.guid);
+ do_check_valid_places_guid(info.parentGuid);
+ Assert.ok(typeof info.index == "number", "index should be a number");
+ Assert.ok(info.dateAdded.constructor.name == "Date", "dateAdded should be a Date");
+ Assert.ok(info.lastModified.constructor.name == "Date", "lastModified should be a Date");
+ Assert.ok(info.lastModified >= info.dateAdded, "lastModified should never be smaller than dateAdded");
+ Assert.ok(typeof info.type == "number", "type should be a number");
+}
+
+/**
+ * Reads foreign_count value for a given url.
+ */
+async function foreign_count(url) {
+ if (url instanceof Ci.nsIURI)
+ url = url.spec;
+ let db = await PlacesUtils.promiseDBConnection();
+ let rows = await db.executeCached(
+ `SELECT foreign_count FROM moz_places
+ WHERE url_hash = hash(:url) AND url = :url
+ `, { url });
+ return rows.length == 0 ? 0 : rows[0].getResultByName("foreign_count");
+}
diff --git a/comm/suite/components/places/tests/unit/bookmarks.glue.html b/comm/suite/components/places/tests/unit/bookmarks.glue.html
new file mode 100644
index 0000000000..07b22e9b3f
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/bookmarks.glue.html
@@ -0,0 +1,16 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+ It will be read and overwritten.
+ DO NOT EDIT! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1>Bookmarks Menu</H1>
+
+<DL><p>
+ <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+ <DT><H3 ADD_DATE="1233157910" LAST_MODIFIED="1233157972" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar</H3>
+<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
+ <DL><p>
+ <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+ </DL><p>
+</DL><p>
diff --git a/comm/suite/components/places/tests/unit/bookmarks.glue.json b/comm/suite/components/places/tests/unit/bookmarks.glue.json
new file mode 100644
index 0000000000..8ca855ad42
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/bookmarks.glue.json
@@ -0,0 +1 @@
+{"title":"","id":1,"dateAdded":1233157910552624,"lastModified":1233157955206833,"type":"text/x-moz-place-container","root":"placesRoot","children":[{"title":"Bookmarks Menu","id":2,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157993171424,"type":"text/x-moz-place-container","root":"bookmarksMenuFolder","children":[{"title":"examplejson","id":27,"parent":2,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":1,"title":"Bookmarks Toolbar","id":3,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157972101126,"annos":[{"name":"bookmarkProperties/description","flags":0,"expires":4,"mimeType":null,"type":3,"value":"Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar"}],"type":"text/x-moz-place-container","root":"toolbarFolder","children":[{"title":"examplejson","id":26,"parent":3,"dateAdded":1233157972101126,"lastModified":1233157984999673,"type":"text/x-moz-place","uri":"http://example.com/"}]},{"index":2,"title":"Tags","id":4,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157910582667,"type":"text/x-moz-place-container","root":"tagsFolder","children":[]},{"index":3,"title":"Unsorted Bookmarks","id":5,"parent":1,"dateAdded":1233157910552624,"lastModified":1233157911033315,"type":"text/x-moz-place-container","root":"unfiledBookmarksFolder","children":[]}]} \ No newline at end of file
diff --git a/comm/suite/components/places/tests/unit/corruptDB.sqlite b/comm/suite/components/places/tests/unit/corruptDB.sqlite
new file mode 100644
index 0000000000..b234246cac
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/corruptDB.sqlite
Binary files differ
diff --git a/comm/suite/components/places/tests/unit/distribution.ini b/comm/suite/components/places/tests/unit/distribution.ini
new file mode 100644
index 0000000000..f94a1be3c5
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/distribution.ini
@@ -0,0 +1,21 @@
+# Distribution Configuration File
+# Bug 516444 demo
+
+[Global]
+id=516444
+version=1.0
+about=Test distribution file
+
+[BookmarksToolbar]
+item.1.title=Toolbar Link Before
+item.1.link=http://mozilla.com/
+item.2.type=default
+item.3.title=Toolbar Link After
+item.3.link=http://mozilla.com/
+
+[BookmarksMenu]
+item.1.title=Menu Link Before
+item.1.link=http://mozilla.com/
+item.2.type=default
+item.3.title=Menu Link After
+item.3.link=http://mozilla.com/ \ No newline at end of file
diff --git a/comm/suite/components/places/tests/unit/head_bookmarks.js b/comm/suite/components/places/tests/unit/head_bookmarks.js
new file mode 100644
index 0000000000..0a795620fd
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/head_bookmarks.js
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// Import common head.
+var commonFile = do_get_file("../head_common.js", false);
+var uri = Services.io.newFileURI(commonFile);
+Services.scriptloader.loadSubScript(uri.spec, this);
+
+// Put any other stuff relative to this test folder below.
+
+
+XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
+ const {PlacesUIUtils} = ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
+ return PlacesUIUtils;
+});
+
+
+const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
+const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
+
+
+// Needed by some test that relies on having an app registered.
+ChromeUtils.import("resource://testing-common/AppInfo.jsm", this);
+updateAppInfo({
+ name: "PlacesTest",
+ ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
+ version: "1",
+ platformVersion: "",
+});
+
+// Smart bookmarks constants.
+const SMART_BOOKMARKS_VERSION = 4;
+// 1 = "Most Visited".
+const SMART_BOOKMARKS_ON_TOOLBAR = 1;
+// 3 = "Recently Bookmarked", "Recent Tags", separator.
+const SMART_BOOKMARKS_ON_MENU = 3; // Takes in count the additional separator.
+
+// Default bookmarks constants.
+// 4 = "SeaMonkey", "mozilla.org", "mozillaZine".
+const DEFAULT_BOOKMARKS_ON_TOOLBAR = 3;
+// 2 = "SeaMonkey and Mozilla", "Search the Web".
+const DEFAULT_BOOKMARKS_ON_MENU = 3; // Takes in count the additional separator.
diff --git a/comm/suite/components/places/tests/unit/test_421483.js b/comm/suite/components/places/tests/unit/test_421483.js
new file mode 100644
index 0000000000..5315617e58
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_421483.js
@@ -0,0 +1,84 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Get bookmarks service
+try {
+ var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+} catch(ex) {
+ do_throw("Could not get Bookmarks service\n");
+}
+
+// Get annotation service
+try {
+ var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
+ getService(Ci.nsIAnnotationService);
+} catch(ex) {
+ do_throw("Could not get Annotation service\n");
+}
+
+// Get browser glue
+try {
+ var gluesvc = Cc["@mozilla.org/suite/suiteglue;1"].
+ getService(Ci.nsISuiteGlue);
+ // Avoid default bookmarks import.
+ gluesvc.QueryInterface(Ci.nsIObserver).observe(null, "initial-migration", null);
+} catch(ex) {
+ do_throw("Could not get SuiteGlue service\n");
+}
+
+const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
+const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
+
+// main
+function run_test() {
+ // TEST 1: smart bookmarks disabled
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
+ gluesvc.ensurePlacesDefaultQueriesInitialized();
+ var smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.equal(smartBookmarkItemIds.length, 0);
+ // check that pref has not been bumped up
+ Assert.equal(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1);
+
+ // TEST 2: create smart bookmarks
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+ gluesvc.ensurePlacesDefaultQueriesInitialized();
+ smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.notEqual(smartBookmarkItemIds.length, 0);
+ // check that pref has been bumped up
+ Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
+
+ var smartBookmarksCount = smartBookmarkItemIds.length;
+
+ // TEST 3: smart bookmarks restore
+ // remove one smart bookmark and restore
+ bmsvc.removeItem(smartBookmarkItemIds[0]);
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+ gluesvc.ensurePlacesDefaultQueriesInitialized();
+ smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.equal(smartBookmarkItemIds.length, smartBookmarksCount);
+ // check that pref has been bumped up
+ Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
+
+ // TEST 4: move a smart bookmark, change its title, then restore
+ // smart bookmark should be restored in place
+ var parent = bmsvc.getFolderIdForItem(smartBookmarkItemIds[0]);
+ var oldTitle = bmsvc.getItemTitle(smartBookmarkItemIds[0]);
+ // create a subfolder and move inside it
+ var newParent = bmsvc.createFolder(parent, "test", bmsvc.DEFAULT_INDEX);
+ bmsvc.moveItem(smartBookmarkItemIds[0], newParent, bmsvc.DEFAULT_INDEX);
+ // change title
+ bmsvc.setItemTitle(smartBookmarkItemIds[0], "new title");
+ // restore
+ Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
+ gluesvc.ensurePlacesDefaultQueriesInitialized();
+ smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
+ Assert.equal(smartBookmarkItemIds.length, smartBookmarksCount);
+ Assert.equal(bmsvc.getFolderIdForItem(smartBookmarkItemIds[0]), newParent);
+ Assert.equal(bmsvc.getItemTitle(smartBookmarkItemIds[0]), oldTitle);
+ // check that pref has been bumped up
+ Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
+}
diff --git a/comm/suite/components/places/tests/unit/test_PUIU_makeTransaction.js b/comm/suite/components/places/tests/unit/test_PUIU_makeTransaction.js
new file mode 100644
index 0000000000..ce13f4e2ee
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_PUIU_makeTransaction.js
@@ -0,0 +1,355 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function waitForBookmarkNotification(aNotification, aCallback, aProperty)
+{
+ PlacesUtils.bookmarks.addObserver({
+ validate: function (aMethodName, aData)
+ {
+ if (aMethodName == aNotification &&
+ (!aProperty || aProperty == aData.property)) {
+ PlacesUtils.bookmarks.removeObserver(this);
+ aCallback(aData);
+ }
+ },
+
+ // nsINavBookmarkObserver
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]),
+ onBeginUpdateBatch: function onBeginUpdateBatch() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onEndUpdateBatch: function onEndUpdateBatch() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onItemAdded: function onItemAdded(aItemId, aParentId, aIndex, aItemType,
+ aURI, aTitle)
+ {
+ return this.validate(arguments.callee.name, { id: aItemId,
+ index: aIndex,
+ type: aItemType,
+ url: aURI ? aURI.spec : null,
+ title: aTitle });
+ },
+ onItemRemoved: function onItemRemoved() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onItemChanged: function onItemChanged(aItemId, aProperty, aIsAnno,
+ aNewValue, aLastModified, aItemType)
+ {
+ return this.validate(arguments.callee.name,
+ { id: aItemId,
+ get index() { return PlacesUtils.bookmarks.getItemIndex(this.id); },
+ type: aItemType,
+ property: aProperty,
+ get url() { return aItemType == PlacesUtils.bookmarks.TYPE_BOOKMARK ?
+ PlacesUtils.bookmarks.getBookmarkURI(this.id).spec :
+ null; },
+ get title() { return PlacesUtils.bookmarks.getItemTitle(this.id); },
+ });
+ },
+ onItemVisited: function onItemVisited() {
+ return this.validate(arguments.callee.name, arguments);
+ },
+ onItemMoved: function onItemMoved(aItemId, aOldParentId, aOldIndex,
+ aNewParentId, aNewIndex, aItemType)
+ {
+ this.validate(arguments.callee.name, { id: aItemId,
+ index: aNewIndex,
+ type: aItemType });
+ }
+ });
+}
+
+function wrapNodeByIdAndParent(aItemId, aParentId)
+{
+ let wrappedNode;
+ let root = PlacesUtils.getFolderContents(aParentId, false, false).root;
+ for (let i = 0; i < root.childCount; ++i) {
+ let node = root.getChild(i);
+ if (node.itemId == aItemId) {
+ let type;
+ if (PlacesUtils.nodeIsContainer(node)) {
+ type = PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER;
+ }
+ else if (PlacesUtils.nodeIsURI(node)) {
+ type = PlacesUtils.TYPE_X_MOZ_PLACE;
+ }
+ else if (PlacesUtils.nodeIsSeparator(node)) {
+ type = PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR;
+ }
+ else {
+ do_throw("Unknown node type");
+ }
+ wrappedNode = PlacesUtils.wrapNode(node, type);
+ }
+ }
+ root.containerOpen = false;
+ return JSON.parse(wrappedNode);
+}
+
+add_test(function test_text_paste()
+{
+ const TEST_URL = "http://places.moz.org/"
+ const TEST_TITLE = "Places bookmark"
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.url, TEST_URL);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ Assert.equal(aData.index, 0);
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ { title: TEST_TITLE, uri: TEST_URL },
+ PlacesUtils.TYPE_X_MOZ_URL,
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true // Unused for text.
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+});
+
+add_test(function test_container()
+{
+ const TEST_TITLE = "Places folder"
+
+ waitForBookmarkNotification("onItemChanged", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_FOLDER);
+ Assert.equal(aData.index, 1);
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_FOLDER);
+ Assert.equal(aData.index, 2);
+ let id = aData.id;
+
+ waitForBookmarkNotification("onItemMoved", function(aData)
+ {
+ Assert.equal(aData.id, id);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_FOLDER);
+ Assert.equal(aData.index, 1);
+
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ 1, // Move to position 1.
+ false
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ });
+
+ try {
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch(ex) {
+ do_throw(ex);
+ }
+ }, "random-anno");
+
+ let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
+ TEST_TITLE,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(id, PlacesUIUtils.DESCRIPTION_ANNO,
+ "description", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.setItemAnnotation(id, "random-anno",
+ "random-value", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+});
+
+
+add_test(function test_separator()
+{
+ waitForBookmarkNotification("onItemChanged", function(aData)
+ {
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
+ Assert.equal(aData.index, 3);
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
+ Assert.equal(aData.index, 4);
+ let id = aData.id;
+
+ waitForBookmarkNotification("onItemMoved", function(aData)
+ {
+ Assert.equal(aData.id, id);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
+ Assert.equal(aData.index, 1);
+
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ 1, // Move to position 1.
+ false
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ });
+
+ try {
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch(ex) {
+ do_throw(ex);
+ }
+ }, "random-anno");
+
+ let id = PlacesUtils.bookmarks.insertSeparator(PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(id, "random-anno",
+ "random-value", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+});
+
+add_test(function test_bookmark()
+{
+ const TEST_URL = "http://places.moz.org/"
+ const TEST_TITLE = "Places bookmark"
+
+ waitForBookmarkNotification("onItemChanged", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.url, TEST_URL);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ Assert.equal(aData.index, 5);
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.url, TEST_URL);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ Assert.equal(aData.index, 6);
+ let id = aData.id;
+
+ waitForBookmarkNotification("onItemMoved", function(aData)
+ {
+ Assert.equal(aData.id, id);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ Assert.equal(aData.index, 1);
+
+ run_next_test();
+ });
+
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ 1, // Move to position 1.
+ false
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ });
+
+ try {
+ let txn = PlacesUIUtils.makeTransaction(
+ wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId),
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch(ex) {
+ do_throw(ex);
+ }
+ }, "random-anno");
+
+ let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+ NetUtil.newURI(TEST_URL),
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ TEST_TITLE);
+ PlacesUtils.annotations.setItemAnnotation(id, PlacesUIUtils.DESCRIPTION_ANNO,
+ "description", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.setItemAnnotation(id, "random-anno",
+ "random-value", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+});
+
+add_test(function test_visit()
+{
+ const TEST_URL = "http://places.moz.org/"
+ const TEST_TITLE = "Places bookmark"
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.url, TEST_URL);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ Assert.equal(aData.index, 7);
+
+ waitForBookmarkNotification("onItemAdded", function(aData)
+ {
+ Assert.equal(aData.title, TEST_TITLE);
+ Assert.equal(aData.url, TEST_URL);
+ Assert.equal(aData.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
+ Assert.equal(aData.index, 8);
+ run_next_test();
+ });
+
+ try {
+ let node = wrapNodeByIdAndParent(aData.id, PlacesUtils.unfiledBookmarksFolderId);
+ // Simulate a not-bookmarked node, will copy it to a new bookmark.
+ node.id = -1;
+ let txn = PlacesUIUtils.makeTransaction(
+ node,
+ 0, // Unused for real nodes.
+ PlacesUtils.unfiledBookmarksFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ true
+ );
+ PlacesUtils.transactionManager.doTransaction(txn);
+ } catch(ex) {
+ do_throw(ex);
+ }
+ });
+
+ PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+ NetUtil.newURI(TEST_URL),
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ TEST_TITLE);
+});
+
+add_test(function check_annotations() {
+ // As last step check how many items for each annotation exist.
+
+ // Copies should retain the description annotation.
+ let descriptions =
+ PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.DESCRIPTION_ANNO, {});
+ Assert.equal(descriptions.length, 4);
+
+ // Only the original bookmarks should have this annotation.
+ let others = PlacesUtils.annotations.getItemsWithAnnotation("random-anno", {});
+ Assert.equal(others.length, 3);
+ run_next_test();
+});
+
+function run_test()
+{
+ run_next_test();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_corrupt.js b/comm/suite/components/places/tests/unit/test_browserGlue_corrupt.js
new file mode 100644
index 0000000000..b0a3e6fb67
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_corrupt.js
@@ -0,0 +1,85 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue correctly restores bookmarks from a JSON backup if
+ * database is corrupt and one backup is available.
+ */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "bs",
+ "@mozilla.org/browser/nav-bookmarks-service;1",
+ "nsINavBookmarksService");
+XPCOMUtils.defineLazyServiceGetter(this, "anno",
+ "@mozilla.org/browser/annotation-service;1",
+ "nsIAnnotationService");
+
+var bookmarksObserver = {
+ onBeginUpdateBatch: function() {},
+ onEndUpdateBatch: function() {
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.notEqual(itemId, -1);
+ if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
+ continue_test();
+ },
+ onItemAdded: function() {},
+ onItemRemoved: function(id, folder, index, itemType) {},
+ onItemChanged: function() {},
+ onItemVisited: function(id, visitID, time) {},
+ onItemMoved: function() {},
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+};
+
+function run_test() {
+ do_test_pending();
+
+ // Create our bookmarks.html copying bookmarks.glue.html to the profile
+ // folder. It should be ignored.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Create our JSON backup copying bookmarks.glue.json to the profile folder.
+ create_JSON_backup("bookmarks.glue.json");
+
+ // Remove current database file.
+ let db = gProfD.clone();
+ db.append("places.sqlite");
+ if (db.exists()) {
+ db.remove(false);
+ Assert.ok(!db.exists());
+ }
+ // Create a corrupt database.
+ let corruptDB = gTestDir.clone();
+ corruptDB.append("corruptDB.sqlite");
+ corruptDB.copyTo(gProfD, "places.sqlite");
+ Assert.ok(db.exists());
+
+ // Initialize nsSuiteGlue before Places.
+ Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsISuiteGlue);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // Check the database was corrupt.
+ // nsSuiteGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CORRUPT);
+
+ // The test will continue once restore has finished and smart bookmarks
+ // have been created.
+ bs.addObserver(bookmarksObserver);
+}
+
+function continue_test() {
+ // Check that JSON backup has been restored.
+ // Notice restore from JSON notification is fired before smart bookmarks creation.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(bs.getItemTitle(itemId), "examplejson");
+
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+
+ do_test_finished();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js b/comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js
new file mode 100644
index 0000000000..aa6ec0e716
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup.js
@@ -0,0 +1,81 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue correctly imports from bookmarks.html if database
+ * is corrupt but a JSON backup is not available.
+ */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "bs",
+ "@mozilla.org/browser/nav-bookmarks-service;1",
+ "nsINavBookmarksService");
+XPCOMUtils.defineLazyServiceGetter(this, "anno",
+ "@mozilla.org/browser/annotation-service;1",
+ "nsIAnnotationService");
+
+var bookmarksObserver = {
+ onBeginUpdateBatch: function() {},
+ onEndUpdateBatch: function() {
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.notEqual(itemId, -1);
+ if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
+ continue_test();
+ },
+ onItemAdded: function() {},
+ onItemRemoved: function(id, folder, index, itemType) {},
+ onItemChanged: function() {},
+ onItemVisited: function(id, visitID, time) {},
+ onItemMoved: function() {},
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+};
+
+function run_test() {
+ do_test_pending();
+
+ // Create bookmarks.html in the profile.
+ create_bookmarks_html("bookmarks.glue.html");
+ // Remove JSON backup from profile.
+ remove_all_JSON_backups();
+
+ // Remove current database file.
+ let db = gProfD.clone();
+ db.append("places.sqlite");
+ if (db.exists()) {
+ db.remove(false);
+ Assert.ok(!db.exists());
+ }
+ // Create a corrupt database.
+ let corruptDB = gTestDir.clone();
+ corruptDB.append("corruptDB.sqlite");
+ corruptDB.copyTo(gProfD, "places.sqlite");
+ Assert.ok(db.exists());
+
+ // Initialize nsSuiteGlue before Places.
+ Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsISuiteGlue);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // Check the database was corrupt.
+ // nsSuiteGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CORRUPT);
+
+ // The test will continue once import has finished and smart bookmarks
+ // have been created.
+ bs.addObserver(bookmarksObserver);
+}
+
+function continue_test() {
+ // Check that bookmarks html has been restored.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(bs.getItemTitle(itemId), "example");
+
+ remove_bookmarks_html();
+
+ do_test_finished();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js b/comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js
new file mode 100644
index 0000000000..54a6bc829f
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_corrupt_nobackup_default.js
@@ -0,0 +1,80 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue correctly restores default bookmarks if database is
+ * corrupt, nor a JSON backup nor bookmarks.html are available.
+ */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "bs",
+ "@mozilla.org/browser/nav-bookmarks-service;1",
+ "nsINavBookmarksService");
+XPCOMUtils.defineLazyServiceGetter(this, "anno",
+ "@mozilla.org/browser/annotation-service;1",
+ "nsIAnnotationService");
+
+var bookmarksObserver = {
+ onBeginUpdateBatch: function() {},
+ onEndUpdateBatch: function() {
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.notEqual(itemId, -1);
+ if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
+ continue_test();
+ },
+ onItemAdded: function() {},
+ onItemRemoved: function(id, folder, index, itemType) {},
+ onItemChanged: function() {},
+ onItemVisited: function(id, visitID, time) {},
+ onItemMoved: function() {},
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+};
+
+function run_test() {
+ do_test_pending();
+
+ // Remove bookmarks.html from profile.
+ remove_bookmarks_html();
+ // Remove JSON backup from profile.
+ remove_all_JSON_backups();
+
+ // Remove current database file.
+ let db = gProfD.clone();
+ db.append("places.sqlite");
+ if (db.exists()) {
+ db.remove(false);
+ Assert.ok(!db.exists());
+ }
+ // Create a corrupt database.
+ let corruptDB = gTestDir.clone();
+ corruptDB.append("corruptDB.sqlite");
+ corruptDB.copyTo(gProfD, "places.sqlite");
+ Assert.ok(db.exists());
+
+ // Initialize nsSuiteGlue before Places.
+ Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsISuiteGlue);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // Check the database was corrupt.
+ // nsSuiteGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CORRUPT);
+
+ // The test will continue once import has finished and smart bookmarks
+ // have been created.
+ bs.addObserver(bookmarksObserver);
+}
+
+function continue_test() {
+ // Check that default bookmarks have been restored.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
+ Assert.ok(itemId > 0);
+ Assert.equal(bs.getItemTitle(itemId), "SeaMonkey");
+
+ do_test_finished();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_distribution.js b/comm/suite/components/places/tests/unit/test_browserGlue_distribution.js
new file mode 100644
index 0000000000..8c12711b6a
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_distribution.js
@@ -0,0 +1,124 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue does not overwrite bookmarks imported from the
+ * migrators. They usually run before nsSuiteGlue, so if we find any
+ * bookmark on init, we should not try to import.
+ */
+
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+const PREF_BMPROCESSED = "distribution.516444.bookmarksProcessed";
+const PREF_DISTRIBUTION_ID = "distribution.id";
+
+const TOPIC_FINAL_UI_STARTUP = "final-ui-startup";
+const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete";
+
+function run_test() {
+ // This is needed but we still have to investigate the reason, could just be
+ // we try to act too late in the game, moving our shutdown earlier will help.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // TODO: re-enable when bug 523936 is fixed.
+ return;
+
+ do_test_pending();
+
+ // Copy distribution.ini file to our app dir.
+ let distroDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+ distroDir.append("distribution");
+ let iniFile = distroDir.clone();
+ iniFile.append("distribution.ini");
+ if (iniFile.exists()) {
+ iniFile.remove(false);
+ print("distribution.ini already exists, did some test forget to cleanup?");
+ }
+
+ let testDistributionFile = gTestDir.clone();
+ testDistributionFile.append("distribution.ini");
+ testDistributionFile.copyTo(distroDir, "distribution.ini");
+ do_check_true(testDistributionFile.exists());
+
+ // Disable Smart Bookmarks creation.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
+ // Avoid migrateUI, we are just simulating a partial startup.
+ Services.prefs.setIntPref("browser.migration.version", 1);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // Check a new database has been created.
+ // nsSuiteGlue will use databaseStatus to manage initialization.
+ do_check_eq(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
+
+ // Initialize nsSuiteGlue.
+ let bg = Cc["@mozilla.org/suite/suiteglue;1"].
+ getService(Ci.nsISuiteGlue);
+
+ let os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ let observer = {
+ observe: function(aSubject, aTopic, aData) {
+ os.removeObserver(this, PlacesUtils.TOPIC_INIT_COMPLETE);
+
+ // Simulate browser startup.
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ TOPIC_FINAL_UI_STARTUP,
+ null);
+ // Test will continue on customization complete notification.
+ let cObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ os.removeObserver(this, TOPIC_CUSTOMIZATION_COMPLETE);
+ do_execute_soon(continue_test);
+ }
+ }
+ os.addObserver(cObserver, TOPIC_CUSTOMIZATION_COMPLETE, false);
+ }
+ }
+ os.addObserver(observer, PlacesUtils.TOPIC_INIT_COMPLETE, false);
+}
+
+function continue_test() {
+ let bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+
+ dump_table("moz_bookmarks");
+
+ // Check the custom bookmarks exist on menu.
+ let menuItemId = bs.getIdForItemAt(bs.bookmarksMenuFolder, 0);
+ do_check_neq(menuItemId, -1);
+ do_check_eq(bs.getItemTitle(menuItemId), "Menu Link Before");
+ menuItemId = bs.getIdForItemAt(bs.bookmarksMenuFolder, 1 + DEFAULT_BOOKMARKS_ON_MENU);
+ do_check_neq(menuItemId, -1);
+ do_check_eq(bs.getItemTitle(menuItemId), "Menu Link After");
+
+ // Check the custom bookmarks exist on toolbar.
+ let toolbarItemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ do_check_neq(toolbarItemId, -1);
+ do_check_eq(bs.getItemTitle(toolbarItemId), "Toolbar Link Before");
+ toolbarItemId = bs.getIdForItemAt(bs.toolbarFolder, 1 + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ do_check_neq(toolbarItemId, -1);
+ do_check_eq(bs.getItemTitle(toolbarItemId), "Toolbar Link After");
+
+ // Check the bmprocessed pref has been created.
+ do_check_true(Services.prefs.getBoolPref(PREF_BMPROCESSED));
+
+ // Check distribution prefs have been created.
+ do_check_eq(Services.prefs.getCharPref(PREF_DISTRIBUTION_ID), "516444");
+
+ do_test_finished();
+}
+
+do_register_cleanup(function() {
+ // Remove the distribution file, even if the test failed, otherwise all
+ // next tests will import it.
+ let iniFile = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+ iniFile.append("distribution");
+ iniFile.append("distribution.ini");
+ if (iniFile.exists())
+ iniFile.remove(false);
+ do_check_false(iniFile.exists());
+});
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_migrate.js b/comm/suite/components/places/tests/unit/test_browserGlue_migrate.js
new file mode 100644
index 0000000000..8568454f04
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_migrate.js
@@ -0,0 +1,89 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue does not overwrite bookmarks imported from the
+ * migrators. They usually run before nsSuiteGlue, so if we find any
+ * bookmark on init, we should not try to import.
+ */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "bs",
+ "@mozilla.org/browser/nav-bookmarks-service;1",
+ "nsINavBookmarksService");
+XPCOMUtils.defineLazyServiceGetter(this, "anno",
+ "@mozilla.org/browser/annotation-service;1",
+ "nsIAnnotationService");
+
+var bookmarksObserver = {
+ onBeginUpdateBatch: function() {},
+ onEndUpdateBatch: function() {
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.notEqual(itemId, -1);
+ if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
+ continue_test();
+ },
+ onItemAdded: function() {},
+ onItemRemoved: function(id, folder, index, itemType) {},
+ onItemChanged: function() {},
+ onItemVisited: function(id, visitID, time) {},
+ onItemMoved: function() {},
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+};
+
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+
+function run_test() {
+ do_test_pending();
+
+ // Create our bookmarks.html copying bookmarks.glue.html to the profile
+ // folder. It will be ignored.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Remove current database file.
+ let db = gProfD.clone();
+ db.append("places.sqlite");
+ if (db.exists()) {
+ db.remove(false);
+ Assert.ok(!db.exists());
+ }
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // Check a new database has been created.
+ // nsSuiteGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
+
+ // A migrator would run before nsSuiteGlue Places initialization, so mimic
+ // that behavior adding a bookmark and notifying the migration.
+ bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://mozilla.org/"),
+ bs.DEFAULT_INDEX, "migrated");
+
+ // Initialize nsSuiteGlue.
+ let bg = Cc["@mozilla.org/suite/suiteglue;1"].
+ getService(Ci.nsIObserver);
+ bg.observe(null, "initial-migration", null)
+
+ // The test will continue once import has finished and smart bookmarks
+ // have been created.
+ bs.addObserver(bookmarksObserver);
+}
+
+function continue_test() {
+ // Check the created bookmarks still exist.
+ let itemId = bs.getIdForItemAt(bs.bookmarksMenuFolder, SMART_BOOKMARKS_ON_MENU);
+ Assert.equal(bs.getItemTitle(itemId), "migrated");
+
+ // Check that we have not imported any new bookmark.
+ Assert.equal(bs.getIdForItemAt(bs.bookmarksMenuFolder, SMART_BOOKMARKS_ON_MENU + 1), -1);
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_MENU), -1);
+
+ remove_bookmarks_html();
+
+ do_test_finished();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_prefs.js b/comm/suite/components/places/tests/unit/test_browserGlue_prefs.js
new file mode 100644
index 0000000000..49610d1476
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_prefs.js
@@ -0,0 +1,272 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue is correctly interpreting the preferences settable
+ * by the user or by other components.
+ */
+
+/** Bug 539067
+ * Test is disabled due to random failures and timeouts, see run_test.
+ * This is commented out to avoid leaks.
+// Initialize SuiteGlue.
+var bg = Cc["@mozilla.org/suite/suiteglue;1"].
+ getService(Ci.nsISuiteGlue);
+*/
+
+// Initialize Places through Bookmarks Service.
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+
+// Get other services.
+const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
+const PREF_RESTORE_DEFAULT_BOOKMARKS = "browser.bookmarks.restore_default_bookmarks";
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
+
+function waitForImportAndSmartBookmarks(aCallback) {
+ Services.obs.addObserver(function waitImport() {
+ Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
+ // Delay to test eventual smart bookmarks creation.
+ executeSoon(function () {
+ promiseAsyncUpdates().then(aCallback);
+ });
+ }, "bookmarks-restore-success");
+}
+
+var tests = [];
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Import from bookmarks.html if importBookmarksHTML is true.",
+ exec: function() {
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, 0), -1);
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ waitForImportAndSmartBookmarks(function () {
+ // Check bookmarks.html has been imported, and a smart bookmark has been
+ // created.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder,
+ SMART_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(bs.getItemTitle(itemId), "example");
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+ next_test();
+ });
+ // Force nsSuiteGlue::_initPlaces().
+ do_log_info("Simulate Places init");
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_INIT_COMPLETE,
+ null);
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "import from bookmarks.html, but don't create smart bookmarks if they are disabled",
+ exec: function() {
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, 0), -1);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ waitForImportAndSmartBookmarks(function () {
+ // Check bookmarks.html has been imported, but smart bookmarks have not
+ // been created.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.equal(bs.getItemTitle(itemId), "example");
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+ next_test();
+ });
+ // Force nsSuiteGlue::_initPlaces().
+ do_log_info("Simulate Places init");
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_INIT_COMPLETE,
+ null);
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Import from bookmarks.html, but don't create smart bookmarks if autoExportHTML is true and they are at latest version",
+ exec: function() {
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, 0), -1);
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 999);
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ waitForImportAndSmartBookmarks(function () {
+ // Check bookmarks.html has been imported, but smart bookmarks have not
+ // been created.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.equal(bs.getItemTitle(itemId), "example");
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+ // Check preferences have been reverted.
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+ next_test();
+ });
+ // Force nsSuiteGlue::_initPlaces()
+ do_log_info("Simulate Places init");
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_INIT_COMPLETE,
+ null);
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Import from bookmarks.html, and create smart bookmarks if autoExportHTML is true and they are not at latest version.",
+ exec: function() {
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, 0), -1);
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+
+ waitForImportAndSmartBookmarks(function () {
+ // Check bookmarks.html has been imported, but smart bookmarks have not
+ // been created.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(bs.getItemTitle(itemId), "example");
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+ // Check preferences have been reverted.
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+ next_test();
+ });
+ // Force nsSuiteGlue::_initPlaces()
+ do_log_info("Simulate Places init");
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_INIT_COMPLETE,
+ null);
+ }
+});
+
+//------------------------------------------------------------------------------
+tests.push({
+ description: "restore from default bookmarks.html if restore_default_bookmarks is true.",
+ exec: function() {
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, 0), -1);
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
+
+ waitForImportAndSmartBookmarks(function () {
+ // Check bookmarks.html has been restored.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR + 1);
+ Assert.ok(itemId > 0);
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+
+ next_test();
+ });
+ // Force nsSuiteGlue::_initPlaces()
+ do_log_info("Simulate Places init");
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_INIT_COMPLETE,
+ null);
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "setting both importBookmarksHTML and restore_default_bookmarks should restore defaults.",
+ exec: function() {
+ // Sanity check: we should not have any bookmark on the toolbar.
+ Assert.equal(bs.getIdForItemAt(bs.toolbarFolder, 0), -1);
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
+ Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
+
+ waitForImportAndSmartBookmarks(function () {
+ // Check bookmarks.html has been restored.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR + 1);
+ Assert.ok(itemId > 0);
+ // Check preferences have been reverted.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+ do_test_finished();
+ });
+ // Force nsSuiteGlue::_initPlaces()
+ do_log_info("Simulate Places init");
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_INIT_COMPLETE,
+ null);
+ }
+});
+
+//------------------------------------------------------------------------------
+
+function finish_test() {
+ // Clean up database from all bookmarks.
+ remove_all_bookmarks();
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+
+ do_test_finished();
+}
+var testIndex = 0;
+function next_test() {
+ // Clean up database from all bookmarks.
+ remove_all_bookmarks();
+ // nsSuiteGlue stops observing topics after first notification,
+ // so we add back the observer to test additional runs.
+ Services.obs.addObserver(bg.QueryInterface(Ci.nsIObserver),
+ PlacesUtils.TOPIC_INIT_COMPLETE);
+ Services.obs.addObserver(bg.QueryInterface(Ci.nsIObserver),
+ PlacesUtils.TOPIC_DATABASE_LOCKED);
+ // Execute next test.
+ let test = tests.shift();
+ print("\nTEST " + (++testIndex) + ": " + test.description);
+ test.exec();
+}
+function run_test() {
+ // Bug 539067: disabled due to random failures and timeouts.
+ return;
+
+ do_test_pending();
+ // Enqueue test, so it will consume the default places-init-complete
+ // notification created at Places init.
+ do_timeout(0, start_tests);
+}
+
+function start_tests() {
+ // Clean up database from all bookmarks.
+ remove_all_bookmarks();
+
+ // Ensure preferences status.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+ try {
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+ do_throw("importBookmarksHTML pref should not exist");
+ }
+ catch(ex) {}
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+
+ // Create our bookmarks.html from bookmarks.glue.html.
+ create_bookmarks_html("bookmarks.glue.html");
+ // Create our JSON backup from bookmarks.glue.json.
+ create_JSON_backup("bookmarks.glue.json");
+ // Kick-off tests.
+ next_test();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_restore.js b/comm/suite/components/places/tests/unit/test_browserGlue_restore.js
new file mode 100644
index 0000000000..c84bdb0b69
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_restore.js
@@ -0,0 +1,81 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue correctly restores bookmarks from a JSON backup if
+ * database has been created and one backup is available.
+ */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "bs",
+ "@mozilla.org/browser/nav-bookmarks-service;1",
+ "nsINavBookmarksService");
+XPCOMUtils.defineLazyServiceGetter(this, "anno",
+ "@mozilla.org/browser/annotation-service;1",
+ "nsIAnnotationService");
+
+var bookmarksObserver = {
+ onBeginUpdateBatch: function() {},
+ onEndUpdateBatch: function() {
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
+ Assert.notEqual(itemId, -1);
+ if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
+ continue_test();
+ },
+ onItemAdded: function() {},
+ onItemRemoved: function(id, folder, index, itemType) {},
+ onItemChanged: function() {},
+ onItemVisited: function(id, visitID, time) {},
+ onItemMoved: function() {},
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
+};
+
+function run_test() {
+ do_test_pending();
+
+ // Create our bookmarks.html copying bookmarks.glue.html to the profile
+ // folder. It will be ignored.
+ create_bookmarks_html("bookmarks.glue.html");
+
+ // Create our JSON backup copying bookmarks.glue.json to the profile
+ // folder. It will be ignored.
+ create_JSON_backup("bookmarks.glue.json");
+
+ // Remove current database file.
+ let db = gProfD.clone();
+ db.append("places.sqlite");
+ if (db.exists()) {
+ db.remove(false);
+ Assert.ok(!db.exists());
+ }
+
+ // Initialize nsSuiteGlue before Places.
+ Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsISuiteGlue);
+
+ // Initialize Places through the History Service.
+ let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+ // Check a new database has been created.
+ // nsSuiteGlue uses databaseStatus to manage initialization.
+ Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
+
+ // The test will continue once restore has finished and smart bookmarks
+ // have been created.
+ bs.addObserver(bookmarksObserver);
+}
+
+function continue_test() {
+ // Check that JSON backup has been restored.
+ // Notice restore from JSON notification is fired before smart bookmarks creation.
+ let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(bs.getItemTitle(itemId), "examplejson");
+
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+
+ do_test_finished();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_shutdown.js b/comm/suite/components/places/tests/unit/test_browserGlue_shutdown.js
new file mode 100644
index 0000000000..b4e5f17247
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_shutdown.js
@@ -0,0 +1,152 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue is correctly exporting based on preferences values,
+ * and creating bookmarks backup if one does not exist for today.
+ */
+
+// Initialize nsSuiteGlue after Places.
+var bg = Cc["@mozilla.org/suite/suiteglue;1"].
+ getService(Ci.nsISuiteGlue);
+
+// Initialize Places through Bookmarks Service.
+var bs = PlacesUtils.bookmarks;
+
+// Get other services.
+const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
+
+var tests = [];
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Export to bookmarks.html if autoExportHTML is true.",
+ exec: function() {
+ // Sanity check: we should have bookmarks on the toolbar.
+ do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML);
+
+ // Force nsSuiteGlue::_shutdownPlaces().
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_SHUTDOWN,
+ null);
+
+ // Check bookmarks.html has been created.
+ check_bookmarks_html();
+ // Check JSON backup has been created.
+ check_JSON_backup();
+
+ // Check preferences have not been reverted.
+ do_check_true(Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+ // Reset preferences.
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Export to bookmarks.html if autoExportHTML is true and a bookmarks.html exists.",
+ exec: function() {
+ // Sanity check: we should have bookmarks on the toolbar.
+ do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
+
+ // Set preferences.
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
+
+ // Create a bookmarks.html in the profile.
+ let profileBookmarksHTMLFile = create_bookmarks_html("bookmarks.glue.html");
+ // Get file lastModified and size.
+ let lastMod = profileBookmarksHTMLFile.lastModifiedTime;
+ let fileSize = profileBookmarksHTMLFile.fileSize;
+
+ // Force nsSuiteGlue::_shutdownPlaces().
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_SHUTDOWN,
+ null);
+
+ // Check a new bookmarks.html has been created.
+ let profileBookmarksHTMLFile = check_bookmarks_html();
+ //XXX not working on Linux unit boxes. Could be filestats caching issue.
+ //do_check_true(profileBookmarksHTMLFile.lastModifiedTime > lastMod);
+ do_check_neq(profileBookmarksHTMLFile.fileSize, fileSize);
+
+ // Check preferences have not been reverted.
+ do_check_true(Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+ // Reset preferences.
+ Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Backup to JSON should be a no-op if a backup for today already exists.",
+ exec: function() {
+ // Sanity check: we should have bookmarks on the toolbar.
+ do_check_true(bs.getIdForItemAt(bs.toolbarFolder, 0) > 0);
+
+ // Create a JSON backup in the profile.
+ let profileBookmarksJSONFile = create_JSON_backup("bookmarks.glue.json");
+ // Get file lastModified and size.
+ let lastMod = profileBookmarksJSONFile.lastModifiedTime;
+ let fileSize = profileBookmarksJSONFile.fileSize;
+
+ // Force nsSuiteGlue::_shutdownPlaces().
+ bg.QueryInterface(Ci.nsIObserver).observe(null,
+ PlacesUtils.TOPIC_SHUTDOWN,
+ null);
+
+ // Check a new JSON backup has not been created.
+ do_check_true(profileBookmarksJSONFile.exists());
+ do_check_eq(profileBookmarksJSONFile.lastModifiedTime, lastMod);
+ do_check_eq(profileBookmarksJSONFile.fileSize, fileSize);
+
+ do_test_finished();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+function finish_test() {
+ do_test_finished();
+}
+
+var testIndex = 0;
+function next_test() {
+ // Remove bookmarks.html from profile.
+ remove_bookmarks_html();
+ // Remove JSON backups from profile.
+ remove_all_JSON_backups();
+
+ // Execute next test.
+ let test = tests.shift();
+ dump("\nTEST " + (++testIndex) + ": " + test.description);
+ test.exec();
+}
+
+function run_test() {
+ do_test_pending();
+
+ // Clean up bookmarks.
+ remove_all_bookmarks();
+
+ // Create some bookmarks.
+ bs.insertBookmark(bs.bookmarksMenuFolder, uri("http://mozilla.org/"),
+ bs.DEFAULT_INDEX, "bookmark-on-menu");
+ bs.insertBookmark(bs.toolbarFolder, uri("http://mozilla.org/"),
+ bs.DEFAULT_INDEX, "bookmark-on-toolbar");
+
+ // Kick-off tests.
+ next_test();
+}
diff --git a/comm/suite/components/places/tests/unit/test_browserGlue_smartBookmarks.js b/comm/suite/components/places/tests/unit/test_browserGlue_smartBookmarks.js
new file mode 100644
index 0000000000..bf0b93b807
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_browserGlue_smartBookmarks.js
@@ -0,0 +1,351 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that nsSuiteGlue is correctly interpreting the preferences settable
+ * by the user or by other components.
+ */
+
+const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
+const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
+const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
+const PREF_RESTORE_DEFAULT_BOOKMARKS = "browser.bookmarks.restore_default_bookmarks";
+
+const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
+
+/**
+ * Rebuilds smart bookmarks listening to console output to report any message or
+ * exception generated when calling ensurePlacesDefaultQueriesInitialized().
+ */
+function rebuildSmartBookmarks() {
+ let consoleListener = {
+ observe: function(aMsg) {
+ print("Got console message: " + aMsg.message);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIConsoleListener
+ ]),
+ };
+ Services.console.reset();
+ Services.console.registerListener(consoleListener);
+ Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsISuiteGlue)
+ .ensurePlacesDefaultQueriesInitialized();
+ Services.console.unregisterListener(consoleListener);
+}
+
+
+var tests = [];
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "All smart bookmarks are created if smart bookmarks version is 0.",
+ exec: function() {
+ // Sanity check: we should have default bookmark.
+ Assert.notEqual(PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0), -1);
+ Assert.notEqual(PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0), -1);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
+
+ rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "An existing smart bookmark is replaced when version changes.",
+ exec: function() {
+ // Sanity check: we have a smart bookmark on the toolbar.
+ let itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
+ Assert.notEqual(itemId, -1);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
+ // Change its title.
+ PlacesUtils.bookmarks.setItemTitle(itemId, "new title");
+ Assert.equal(PlacesUtils.bookmarks.getItemTitle(itemId), "new title");
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check smart bookmark has been replaced, itemId has changed.
+ itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
+ Assert.notEqual(itemId, -1);
+ Assert.notEqual(PlacesUtils.bookmarks.getItemTitle(itemId), "new title");
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "bookmarks position is retained when version changes.",
+ exec: function() {
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ let itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
+ let firstItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId);
+
+ itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
+ let secondItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check smart bookmarks are still in correct position.
+ itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
+ Assert.equal(PlacesUtils.bookmarks.getItemTitle(itemId), firstItemTitle);
+
+ itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
+ Assert.equal(PlacesUtils.bookmarks.getItemTitle(itemId), secondItemTitle);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "moved bookmarks position is retained when version changes.",
+ exec: function() {
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ let itemId1 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId1, SMART_BOOKMARKS_ANNO));
+ let firstItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId1);
+
+ let itemId2 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId2, SMART_BOOKMARKS_ANNO));
+ let secondItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId2);
+
+ // Move the first smart bookmark to the end of the menu.
+ PlacesUtils.bookmarks.moveItem(itemId1, PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+
+ Assert.equal(itemId1, PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX));
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ rebuildSmartBookmarks();
+
+ // Count items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check smart bookmarks are still in correct position.
+ itemId2 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId2, SMART_BOOKMARKS_ANNO));
+ Assert.equal(PlacesUtils.bookmarks.getItemTitle(itemId2), secondItemTitle);
+
+ itemId1 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ Assert.ok(PlacesUtils.annotations.itemHasAnnotation(itemId1, SMART_BOOKMARKS_ANNO));
+ Assert.equal(PlacesUtils.bookmarks.getItemTitle(itemId1), firstItemTitle);
+
+ // Move back the smart bookmark to the original position.
+ PlacesUtils.bookmarks.moveItem(itemId1, PlacesUtils.bookmarksMenuFolderId, 1);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "An explicitly removed smart bookmark should not be recreated.",
+ exec: function() {
+ // Remove toolbar's smart bookmarks
+ PlacesUtils.bookmarks.removeItem(PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0));
+
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
+
+ rebuildSmartBookmarks();
+
+ // Count items.
+ // We should not have recreated the smart bookmark on toolbar.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+
+ next_test();
+ }
+});
+
+//------------------------------------------------------------------------------
+
+tests.push({
+ description: "Even if a smart bookmark has been removed recreate it if version is 0.",
+ exec: function() {
+ // Sanity check items.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Set preferences.
+ Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
+
+ rebuildSmartBookmarks();
+
+ // Count items.
+ // We should not have recreated the smart bookmark on toolbar.
+ Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
+ SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
+ Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
+ SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
+
+ // Check version has been updated.
+ Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
+ SMART_BOOKMARKS_VERSION);
+
+ next_test();
+ }
+});
+//------------------------------------------------------------------------------
+
+function countFolderChildren(aFolderItemId) {
+ let rootNode = PlacesUtils.getFolderContents(aFolderItemId).root;
+ let cc = rootNode.childCount;
+ // Dump contents.
+ for (let i = 0; i < cc ; i++) {
+ let node = rootNode.getChild(i);
+ let title = PlacesUtils.nodeIsSeparator(node) ? "---" : node.title;
+ print("Found child(" + i + "): " + title);
+ }
+ rootNode.containerOpen = false;
+ return cc;
+}
+
+function next_test() {
+ if (tests.length) {
+ // Execute next test.
+ let test = tests.shift();
+ print("\nTEST: " + test.description);
+ test.exec();
+ }
+ else {
+ // Clean up database from all bookmarks.
+ remove_all_bookmarks();
+ do_test_finished();
+ }
+}
+
+function run_test() {
+ do_test_pending();
+
+ remove_bookmarks_html();
+ remove_all_JSON_backups();
+
+ // Initialize SuiteGlue, but remove its listener to places-init-complete.
+ let bg = Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsIObserver);
+ // Initialize Places.
+ PlacesUtils.history;
+ // Usually places init would async notify to glue, but we want to avoid
+ // randomness here, thus we fire the notification synchronously.
+ bg.observe(null, "places-init-complete", null);
+
+ // Ensure preferences status.
+ Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
+ // XXXkairo: might get set due to the different logic of SeaMonkey imports
+ // but there could be some real bug so import is set and restore not
+ try {
+ Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+ }
+ catch(ex) {}
+ try {
+ Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+ // do_throw("importBookmarksHTML pref should not exist");
+ }
+ catch(ex) {}
+
+ waitForImportAndSmartBookmarks(next_test);
+}
+
+function waitForImportAndSmartBookmarks(aCallback) {
+ Services.obs.addObserver(function waitImport() {
+ Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
+ // Delay to test eventual smart bookmarks creation.
+ executeSoon(function () {
+ promiseAsyncUpdates().then(aCallback);
+ });
+ }, "bookmarks-restore-success");
+}
diff --git a/comm/suite/components/places/tests/unit/test_clearHistory_shutdown.js b/comm/suite/components/places/tests/unit/test_clearHistory_shutdown.js
new file mode 100644
index 0000000000..2fa7a42372
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -0,0 +1,181 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that requesting clear history at shutdown will really clear history.
+ */
+
+const URIS = [
+ "http://a.example1.com/"
+, "http://b.example1.com/"
+, "http://b.example2.com/"
+, "http://c.example3.com/"
+];
+
+const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
+
+var EXPECTED_NOTIFICATIONS = [
+ "places-shutdown"
+, "places-will-close-connection"
+, "places-expiration-finished"
+, "places-connection-closed"
+];
+
+const UNEXPECTED_NOTIFICATIONS = [
+ "xpcom-shutdown"
+];
+
+const URL = "ftp://localhost/clearHistoryOnShutdown/";
+
+var notificationIndex = 0;
+
+var notificationsObserver = {
+ observe: function observe(aSubject, aTopic, aData) {
+ print("Received notification: " + aTopic);
+
+ // Note that some of these notifications could arrive multiple times, for
+ // example in case of sync, we allow that.
+ if (EXPECTED_NOTIFICATIONS[notificationIndex] != aTopic)
+ notificationIndex++;
+ Assert.equal(EXPECTED_NOTIFICATIONS[notificationIndex], aTopic);
+
+ if (aTopic != TOPIC_CONNECTION_CLOSED)
+ return;
+
+ getDistinctNotifications().forEach(
+ topic => Services.obs.removeObserver(notificationsObserver, topic)
+ );
+
+ print("Looking for uncleared stuff.");
+
+ let stmt = DBConn().createStatement(
+ "SELECT id FROM moz_places WHERE url = :page_url "
+ );
+
+ try {
+ URIS.forEach(function(aUrl) {
+ stmt.params.page_url = aUrl;
+ Assert.ok(!stmt.executeStep());
+ stmt.reset();
+ });
+ } finally {
+ stmt.finalize();
+ }
+
+ // Check cache.
+ checkCache(URL);
+ }
+}
+
+var timeInMicroseconds = Date.now() * 1000;
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(async function test_execute() {
+ do_test_pending();
+
+ print("Initialize suiteglue before Places");
+ // Avoid default bookmarks import.
+ Cc["@mozilla.org/suite/suiteglue;1"].getService(Ci.nsIObserver)
+ .observe(null, "initial-migration", null);
+
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.history", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.urlbar", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.formdata", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.passwords", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.downloads", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cache", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.sessions", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.offlineApps", true);
+
+ Services.prefs.setBoolPref("privacy.sanitize.sanitizeOnShutdown", true);
+ // Unlike Firefox, SeaMonkey still supports the confirmation dialog
+ // which is called from Sanitizer's init method checkSettings().
+ Services.prefs.setBoolPref("privacy.sanitize.promptOnSanitize", false);
+
+ print("Add visits.");
+ for (let aUrl of URIS) {
+ await promiseAddVisits({uri: uri(aUrl), visitDate: timeInMicroseconds++,
+ transition: PlacesUtils.history.TRANSITION_TYPED})
+ }
+ print("Add cache.");
+ storeCache(URL, "testData");
+});
+
+function run_test_continue()
+{
+ print("Simulate and wait shutdown.");
+ getDistinctNotifications().forEach(
+ topic =>
+ Services.obs.addObserver(notificationsObserver, topic)
+ );
+
+ // Simulate an exit so that Sanitizer's init method checkSettings() is called.
+ print("Simulate 'quit-application-granted' too for SeaMonkey.");
+ Services.obs.notifyObservers(null, "quit-application-granted");
+
+ shutdownPlaces();
+
+ // Shutdown the download manager.
+ Services.obs.notifyObservers(null, "quit-application");
+}
+
+function getDistinctNotifications() {
+ let ar = EXPECTED_NOTIFICATIONS.concat(UNEXPECTED_NOTIFICATIONS);
+ return [...new Set(ar)];
+}
+
+function storeCache(aURL, aContent) {
+ let cache = Cc["@mozilla.org/network/cache-service;1"].
+ getService(Ci.nsICacheService);
+ let session = cache.createSession("FTP", Ci.nsICache.STORE_ANYWHERE,
+ Ci.nsICache.STREAM_BASED);
+
+
+ var storeCacheListener = {
+ onCacheEntryAvailable: function (entry, access, status) {
+ Assert.equal(status, Cr.NS_OK);
+
+ entry.setMetaDataElement("servertype", "0");
+ var os = entry.openOutputStream(0);
+
+ var written = os.write(aContent, aContent.length);
+ if (written != aContent.length) {
+ do_throw("os.write has not written all data!\n" +
+ " Expected: " + written + "\n" +
+ " Actual: " + aContent.length + "\n");
+ }
+ os.close();
+ entry.close();
+ executeSoon(run_test_continue);
+ }
+ };
+
+ session.asyncOpenCacheEntry(aURL,
+ Ci.nsICache.ACCESS_READ_WRITE,
+ storeCacheListener);
+}
+
+function checkCache(aURL) {
+ let cache = Cc["@mozilla.org/network/cache-service;1"].
+ getService(Ci.nsICacheService);
+ let session = cache.createSession("FTP", Ci.nsICache.STORE_ANYWHERE,
+ Ci.nsICache.STREAM_BASED);
+
+ var checkCacheListener = {
+ onCacheEntryAvailable: function (entry, access, status) {
+ Assert.equal(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
+ do_test_finished();
+ }
+ };
+
+ session.asyncOpenCacheEntry(aURL,
+ Ci.nsICache.ACCESS_READ,
+ checkCacheListener);
+}
diff --git a/comm/suite/components/places/tests/unit/test_leftpane_corruption_handling.js b/comm/suite/components/places/tests/unit/test_leftpane_corruption_handling.js
new file mode 100644
index 0000000000..c8b275e543
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/test_leftpane_corruption_handling.js
@@ -0,0 +1,189 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ * Tests that we build a working leftpane in various corruption situations.
+ */
+
+// Used to store the original leftPaneFolderId getter.
+var gLeftPaneFolderIdGetter;
+var gAllBookmarksFolderIdGetter;
+// Used to store the original left Pane status as a JSON string.
+var gReferenceJSON;
+var gLeftPaneFolderId;
+// Third party annotated folder.
+var gFolderId;
+
+// Corruption cases.
+var gTests = [
+
+ function test1() {
+ print("1. Do nothing, checks test calibration.");
+ },
+
+ function test2() {
+ print("2. Delete the left pane folder.");
+ PlacesUtils.bookmarks.removeItem(gLeftPaneFolderId);
+ },
+
+ function test3() {
+ print("3. Delete a child of the left pane folder.");
+ let id = PlacesUtils.bookmarks.getIdForItemAt(gLeftPaneFolderId, 0);
+ PlacesUtils.bookmarks.removeItem(id);
+ },
+
+ function test4() {
+ print("4. Delete AllBookmarks.");
+ PlacesUtils.bookmarks.removeItem(PlacesUIUtils.allBookmarksFolderId);
+ },
+
+ function test5() {
+ print("5. Create a duplicated left pane folder.");
+ let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
+ "PlacesRoot",
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(id, ORGANIZER_FOLDER_ANNO,
+ "PlacesRoot", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ },
+
+ function test6() {
+ print("6. Create a duplicated left pane query.");
+ let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
+ "AllBookmarks",
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(id, ORGANIZER_QUERY_ANNO,
+ "AllBookmarks", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+ },
+
+ function test7() {
+ print("7. Remove the left pane folder annotation.");
+ PlacesUtils.annotations.removeItemAnnotation(gLeftPaneFolderId,
+ ORGANIZER_FOLDER_ANNO);
+ },
+
+ function test8() {
+ print("8. Remove a left pane query annotation.");
+ PlacesUtils.annotations.removeItemAnnotation(PlacesUIUtils.allBookmarksFolderId,
+ ORGANIZER_QUERY_ANNO);
+ },
+
+ function test9() {
+ print("9. Remove a child of AllBookmarks.");
+ let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUIUtils.allBookmarksFolderId, 0);
+ PlacesUtils.bookmarks.removeItem(id);
+ },
+
+];
+
+function run_test() {
+ // We want empty roots.
+ remove_all_bookmarks();
+
+ // Sanity check.
+ Assert.ok(!!PlacesUIUtils);
+
+ // Check getters.
+ gLeftPaneFolderIdGetter = PlacesUIUtils.__lookupGetter__("leftPaneFolderId");
+ Assert.equal(typeof(gLeftPaneFolderIdGetter), "function");
+ gAllBookmarksFolderIdGetter = PlacesUIUtils.__lookupGetter__("allBookmarksFolderId");
+ Assert.equal(typeof(gAllBookmarksFolderIdGetter), "function");
+
+ // Add a third party bogus annotated item. Should not be removed.
+ gFolderId = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
+ "test",
+ PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.annotations.setItemAnnotation(gFolderId, ORGANIZER_QUERY_ANNO,
+ "test", 0,
+ PlacesUtils.annotations.EXPIRE_NEVER);
+
+ // Create the left pane, and store its current status, it will be used
+ // as reference value.
+ gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
+ gReferenceJSON = folderToJSON(gLeftPaneFolderId);
+
+ // Kick-off tests.
+ do_test_pending();
+ do_timeout(0, run_next_test);
+}
+
+function run_next_test() {
+ if (gTests.length) {
+ // Create corruption.
+ let test = gTests.shift();
+ test();
+ // Regenerate getters.
+ PlacesUIUtils.__defineGetter__("leftPaneFolderId", gLeftPaneFolderIdGetter);
+ gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
+ PlacesUIUtils.__defineGetter__("allBookmarksFolderId", gAllBookmarksFolderIdGetter);
+ // Check the new left pane folder.
+ let leftPaneJSON = folderToJSON(gLeftPaneFolderId);
+ Assert.ok(compareJSON(gReferenceJSON, leftPaneJSON));
+ Assert.equal(PlacesUtils.bookmarks.getItemTitle(gFolderId), "test");
+ // Go to next test.
+ do_timeout(0, run_next_test);
+ }
+ else {
+ // All tests finished.
+ remove_all_bookmarks();
+ do_test_finished();
+ }
+}
+
+/**
+ * Convert a folder item id to a JSON representation of it and its contents.
+ */
+function folderToJSON(aItemId) {
+ let query = PlacesUtils.history.getNewQuery();
+ query.setFolders([aItemId], 1);
+ let options = PlacesUtils.history.getNewQueryOptions();
+ options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
+ let root = PlacesUtils.history.executeQuery(query, options).root;
+ let writer = {
+ value: "",
+ write: function PU_wrapNode__write(aStr, aLen) {
+ this.value += aStr;
+ }
+ };
+ PlacesUtils.serializeNodeAsJSONToOutputStream(root, writer, false, false);
+ Assert.ok(writer.value.length > 0);
+ return writer.value;
+}
+
+/**
+ * Compare the JSON representation of 2 nodes, skipping everchanging properties
+ * like dates.
+ */
+function compareJSON(aNodeJSON_1, aNodeJSON_2) {
+ let node1 = JSON.parse(aNodeJSON_1);
+ let node2 = JSON.parse(aNodeJSON_2);
+
+ // List of properties we should not compare (expected to be different).
+ const SKIP_PROPS = ["dateAdded", "lastModified", "id"];
+
+ function compareObjects(obj1, obj2) {
+ function count(o) { var n = 0; for (let p in o) n++; return n; }
+ Assert.equal(count(obj1), count(obj2));
+ for (let prop in obj1) {
+ // Skip everchanging values.
+ if (SKIP_PROPS.includes(prop))
+ continue;
+ // Skip undefined objects, otherwise we hang on them.
+ if (!obj1[prop])
+ continue;
+ if (typeof(obj1[prop]) == "object")
+ return compareObjects(obj1[prop], obj2[prop]);
+ if (obj1[prop] !== obj2[prop]) {
+ print(prop + ": " + obj1[prop] + "!=" + obj2[prop]);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return compareObjects(node1, node2);
+}
diff --git a/comm/suite/components/places/tests/unit/xpcshell.ini b/comm/suite/components/places/tests/unit/xpcshell.ini
new file mode 100644
index 0000000000..a2753df42b
--- /dev/null
+++ b/comm/suite/components/places/tests/unit/xpcshell.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+head = head_bookmarks.js
+tail =
+run-sequentially = Avoid bustage.
+support-files = distribution.ini corruptDB.sqlite bookmarks.glue.html bookmarks.glue.json
+
+[test_421483.js]
+[test_browserGlue_corrupt.js]
+[test_browserGlue_corrupt_nobackup.js]
+[test_browserGlue_corrupt_nobackup_default.js]
+[test_browserGlue_distribution.js]
+[test_browserGlue_migrate.js]
+[test_browserGlue_prefs.js]
+[test_browserGlue_restore.js]
+[test_browserGlue_shutdown.js]
+[test_browserGlue_smartBookmarks.js]
+[test_clearHistory_shutdown.js]
+[test_leftpane_corruption_handling.js]
+[test_PUIU_makeTransaction.js]
diff --git a/comm/suite/components/pref/content/pref-advanced.js b/comm/suite/components/pref/content/pref-advanced.js
new file mode 100644
index 0000000000..1f1c290329
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-advanced.js
@@ -0,0 +1,90 @@
+/* -*- 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/. */
+
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const {ShellService} = ChromeUtils.import("resource:///modules/ShellService.jsm");
+
+var defaultClient = 0;
+var defaultApps = 0;
+
+function Startup()
+{
+ InitPlatformIntegration();
+ CrashReportsCheck();
+}
+
+/**
+ * System preferences
+ */
+
+function InitPlatformIntegration() {
+ if (ShellService) {
+ try {
+ this.defaultApps = ShellService.shouldBeDefaultClientFor;
+ ["Browser", "Mail", "News", "Rss"].forEach(function(aType) {
+ let button = document.getElementById("setDefault" + aType);
+ try {
+ let client = Ci.nsIShellService[aType.toUpperCase()];
+ let isDefault = ShellService.isDefaultClient(false, client);
+ if (isDefault) {
+ this.defaultClient |= client;
+ }
+ button.disabled = isDefault;
+ document.getElementById("defaultClientGroup").hidden = false;
+ } catch (e) {
+ button.hidden = true;
+ }
+ });
+ } catch (e) {
+ }
+ }
+}
+
+function ApplySetAsDefaultClient() {
+ let pane = document.getElementById("advanced_pane");
+ ShellService.setDefaultClient(false, false, pane.defaultClient);
+ ShellService.shouldBeDefaultClientFor = pane.defaultApps;
+}
+
+function onSetDefault(aButton, aType) {
+ if (document.documentElement.instantApply) {
+ ShellService.setDefaultClient(false, false, Ci.nsIShellService[aType]);
+ ShellService.shouldBeDefaultClientFor |= Ci.nsIShellService[aType];
+ } else {
+ this.defaultClient |= Ci.nsIShellService[aType];
+ this.defaultApps |= Ci.nsIShellService[aType];
+ window.addEventListener("dialogaccept", this.ApplySetAsDefaultClient, true);
+ }
+
+ aButton.disabled = true;
+}
+
+function onNewsChange(aChecked) {
+ let snws = document.getElementById("network.protocol-handler.external.snews");
+ let nntp = document.getElementById("network.protocol-handler.external.nntp");
+
+ if (!snws.locked)
+ snws.value = aChecked;
+
+ if (!nntp.locked)
+ nntp.value = aChecked;
+}
+
+function CrashReportsCheck()
+{
+ if (AppConstants.MOZ_CRASHREPORTER) {
+ var cr = Cc["@mozilla.org/toolkit/crash-reporter;1"]
+ .getService(Ci.nsICrashReporter);
+ document.getElementById("crashReports").hidden = !cr.enabled;
+ document.getElementById("submitCrashes").checked = cr.submitReports;
+ }
+}
+
+function updateSubmitCrashes(aChecked)
+{
+ Cc["@mozilla.org/toolkit/crash-reporter;1"]
+ .getService(Ci.nsICrashReporter)
+ .submitReports = aChecked;
+}
diff --git a/comm/suite/components/pref/content/pref-advanced.xul b/comm/suite/components/pref/content/pref-advanced.xul
new file mode 100644
index 0000000000..6b77581e4c
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-advanced.xul
@@ -0,0 +1,174 @@
+<?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 % prefAdvancedDTD SYSTEM "chrome://communicator/locale/pref/pref-advanced.dtd"> %prefAdvancedDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="advanced_pane"
+ label="&pref.advanced.title;"
+ script="chrome://communicator/content/pref/pref-advanced.js">
+
+ <preferences id="advanced_preferences">
+ <preference id="shell.checkDefaultClient"
+ name="shell.checkDefaultClient"
+ type="bool"/>
+ <preference id="pref.browser.disable_button.default_browser"
+ name="pref.browser.disable_button.default_browser"
+ type="bool"
+ readonly="true"/>
+ <preference id="system.windows.lock_ui.defaultMailClient"
+ name="system.windows.lock_ui.defaultMailClient"
+ type="bool"
+ readonly="true"/>
+ <preference id="system.windows.lock_ui.defaultNewsClient"
+ name="system.windows.lock_ui.defaultNewsClient"
+ type="bool"
+ readonly="true"/>
+ <preference id="system.windows.lock_ui.defaultFeedClient"
+ name="system.windows.lock_ui.defaultFeedClient"
+ type="bool"
+ readonly="true"/>
+ <preference id="network.protocol-handler.external.mailto"
+ name="network.protocol-handler.external.mailto"
+ type="bool"
+ inverted="true"/>
+ <preference id="network.protocol-handler.external.news"
+ name="network.protocol-handler.external.news"
+ type="bool"
+ inverted="true"/>
+ <preference id="network.protocol-handler.external.snews"
+ name="network.protocol-handler.external.snews"
+ type="bool"
+ inverted="true"/>
+ <preference id="network.protocol-handler.external.nntp"
+ name="network.protocol-handler.external.nntp"
+ type="bool"
+ inverted="true"/>
+ <preference id="print.use_native_print_dialog"
+ name="print.use_native_print_dialog"
+ type="bool"/>
+ <preference id="print.use_global_printsettings"
+ name="print.use_global_printsettings"
+ type="bool"/>
+ <preference id="devtools.debugger.remote-enabled"
+ name="devtools.debugger.remote-enabled"
+ type="bool"/>
+ <preference id="devtools.debugger.force-local"
+ name="devtools.debugger.force-local"
+ inverted="true"
+ type="bool"/>
+ <preference id="devtools.debugger.prompt-connection"
+ name="devtools.debugger.prompt-connection"
+ type="bool"/>
+ <preference id="devtools.debugger.remote-port"
+ name="devtools.debugger.remote-port"
+ type="int"/>
+ </preferences>
+
+ <groupbox id="defaultClientGroup" hidden="true">
+ <caption label="&prefCheckDefault.caption;"/>
+ <checkbox id="checkDefaultClient"
+ label="&prefCheckDefaultClient.label;"
+ accesskey="&prefCheckDefaultClient.accesskey;"
+ preference="shell.checkDefaultClient"/>
+ <vbox>
+ <separator class="thin"/>
+
+ <description>&defaultClientFor.description;</description>
+ <hbox class="indent" align="center">
+ <button id="setDefaultBrowser"
+ label="&setDefaultBrowser.label;"
+ accesskey="&setDefaultBrowser.accesskey;"
+ oncommand="onSetDefault(this, 'BROWSER');"
+ preference="pref.browser.disable_button.default_browser"/>
+ <button id="setDefaultMail"
+ label="&setDefaultMail.label;"
+ accesskey="&setDefaultMail.accesskey;"
+ oncommand="onSetDefault(this, 'MAIL');"
+ preference="system.windows.lock_ui.defaultMailClient"/>
+ <button id="setDefaultNews"
+ label="&setDefaultNews.label;"
+ accesskey="&setDefaultNews.accesskey;"
+ oncommand="onSetDefault(this, 'NEWS');"
+ preference="system.windows.lock_ui.defaultNewsClient"/>
+ <button id="setDefaultRss"
+ label="&setDefaultFeed.label;"
+ accesskey="&setDefaultFeed.accesskey;"
+ oncommand="onSetDefault(this, 'RSS');"
+ preference="system.windows.lock_ui.defaultFeedClient"/>
+ </hbox>
+ </vbox>
+
+ <separator class="thin"/>
+
+ <description>&useInternalSettings.description;</description>
+ <hbox class="indent" align="center">
+ <checkbox id="useInternalMail"
+ label="&useInternalMail.label;"
+ accesskey="&useInternalMail.accesskey;"
+ preference="network.protocol-handler.external.mailto"/>
+ <checkbox id="useInternalNews"
+ label="&useInternalNews.label;"
+ accesskey="&useInternalNews.accesskey;"
+ oncommand="onNewsChange(this.checked);"
+ preference="network.protocol-handler.external.news"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox id="printing">
+ <caption label="&printing.label;"/>
+ <checkbox id="nglayoutUseNativePrintDialog"
+ label="&useNativePrintDialog.label;"
+ accesskey="&useNativePrintDialog.accesskey;"
+ preference="print.use_native_print_dialog"/>
+ <checkbox id="printUseGlobalPrintSettings"
+ label="&useGlobalPrintSettings.label;"
+ accesskey="&useGlobalPrintSettings.accesskey;"
+ preference="print.use_global_printsettings"/>
+ </groupbox>
+
+ <groupbox id="crashReports" hidden="true">
+ <caption id="crashReportsCaption" label="&crashReports.caption;"/>
+ <checkbox id="submitCrashes"
+ label="&submitCrashes.label;"
+ accesskey="&submitCrashes.accesskey;"
+ oncommand="updateSubmitCrashes(this.checked);"/>
+ </groupbox>
+
+ <groupbox id="devTools">
+ <caption id="devToolsCaption" label="&devTools.caption;"/>
+ <checkbox id="allowDebugger"
+ label="&allowDebugger.label;"
+ accesskey="&allowDebugger.accesskey;"
+ preference="devtools.debugger.remote-enabled"/>
+ <checkbox id="allowRemoteConnections"
+ label="&allowRemoteConnections.label;"
+ accesskey="&allowRemoteConnections.accesskey;"
+ preference="devtools.debugger.force-local"/>
+ <checkbox id="connectionPrompt"
+ label="&connectionPrompt.label;"
+ accesskey="&connectionPrompt.accesskey;"
+ preference="devtools.debugger.prompt-connection"/>
+
+ <hbox align="center">
+ <label id="remoteDebuggerPortBefore"
+ value="&remoteDebuggerPort.label;"
+ accesskey="&remoteDebuggerPort.accesskey;"
+ control="remoteDebuggerPort"/>
+ <textbox id="remoteDebuggerPort"
+ type="number"
+ min="0"
+ max="65535"
+ size="5"
+ preference="devtools.debugger.remote-port"
+ aria-labelledby="remoteDebuggerPortBefore remoteDebuggerPort"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-appearance.js b/comm/suite/components/pref/content/pref-appearance.js
new file mode 100644
index 0000000000..a54b14786d
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-appearance.js
@@ -0,0 +1,102 @@
+/* -*- 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/. */
+
+// Load spell-checker module to properly determine language strings
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function Startup()
+{
+ SwitchLocales_Load();
+ NumberLocales_Load();
+}
+
+/**
+ * From locale switcher's switch.js:
+ * Load available locales into selection menu
+ */
+function SwitchLocales_Load() {
+ var menulist = document.getElementById("switchLocales");
+
+ var cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Ci.nsIToolkitChromeRegistry);
+
+ var langNames = document.getElementById("languageNamesBundle");
+ var regNames = document.getElementById("regionNamesBundle");
+
+ var matched = false;
+ var currentLocale = Services.locale.getRequestedLocale() || undefined;
+ var locales = cr.getLocalesForPackage("global");
+
+ while (locales.hasMore()) {
+ var locale = locales.getNext();
+
+ var parts = locale.split(/-/);
+
+ var displayName;
+ try {
+ displayName = langNames.getString(parts[0]);
+ if (parts.length > 1) {
+ try {
+ displayName += " (" + regNames.getString(parts[1].toLowerCase()) + ")";
+ }
+ catch (e) {
+ displayName += " (" + parts[1] + ")";
+ }
+ }
+ }
+ catch (e) {
+ displayName = locale;
+ }
+
+ var item = menulist.appendItem(displayName, locale);
+ if (!matched && currentLocale && currentLocale == locale) {
+ matched = true;
+ menulist.selectedItem = item;
+ }
+ }
+ // If somehow we have not found the current locale, select the first in list.
+ if (!matched) {
+ menulist.selectedIndex = 1;
+ }
+}
+
+/**
+ * Determine the appropriate value to set and set it.
+ */
+function SelectLocale(aElement) {
+ var locale = aElement.value;
+ var currentLocale = Services.locale.getRequestedLocale() || undefined;
+ if (!currentLocale || (currentLocale && currentLocale != locale)) {
+ Services.locale.setRequestedLocales([locale]);
+ }
+}
+
+/**
+ * When starting up, determine application and regional locale settings
+ * and add the respective strings to the prefpane labels.
+ */
+function NumberLocales_Load()
+{
+ const osprefs =
+ Cc["@mozilla.org/intl/ospreferences;1"]
+ .getService(Ci.mozIOSPreferences);
+
+ let appLocale = Services.locale.appLocalesAsBCP47[0];
+ let rsLocale = osprefs.regionalPrefsLocales[0];
+ let names = Services.intl.getLocaleDisplayNames(undefined, [appLocale, rsLocale]);
+
+ let appLocaleRadio = document.getElementById("appLocale");
+ let rsLocaleRadio = document.getElementById("rsLocale");
+ let prefutilitiesBundle = document.getElementById("bundle_prefutilities");
+
+ let appLocaleLabel = prefutilitiesBundle.getFormattedString("appLocale.label",
+ [names[0]]);
+ let rsLocaleLabel = prefutilitiesBundle.getFormattedString("rsLocale.label",
+ [names[1]]);
+ appLocaleRadio.setAttribute("label", appLocaleLabel);
+ rsLocaleRadio.setAttribute("label", rsLocaleLabel);
+ appLocaleRadio.accessKey = prefutilitiesBundle.getString("appLocale.accesskey");
+ rsLocaleRadio.accessKey = prefutilitiesBundle.getString("rsLocale.accesskey");
+}
diff --git a/comm/suite/components/pref/content/pref-appearance.xul b/comm/suite/components/pref/content/pref-appearance.xul
new file mode 100644
index 0000000000..132e0a614e
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-appearance.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/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
+ <!ENTITY % prefAppearanceDTD SYSTEM "chrome://communicator/locale/pref/pref-appearance.dtd"> %prefAppearanceDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="appearance_pane"
+ label="&pref.appearance.title;"
+ script="chrome://communicator/content/pref/pref-appearance.js">
+
+ <preferences id="appearance_preferences">
+ <preference id="general.startup.browser"
+ name="general.startup.browser"
+ type="bool"/>
+ <preference id="browser.chrome.toolbar_style"
+ name="browser.chrome.toolbar_style"
+ type="int"/>
+ <preference id="browser.chrome.toolbar_tips"
+ name="browser.chrome.toolbar_tips"
+ type="bool"/>
+ <preference id="browser.toolbars.grippyhidden"
+ name="browser.toolbars.grippyhidden"
+ type="bool"/>
+ <preference id="intl.regional_prefs.use_os_locales"
+ name="intl.regional_prefs.use_os_locales"
+ type="bool"/>
+ </preferences>
+
+ <hbox>
+ <groupbox id="generalStartupPreferences" align="start" flex="1">
+ <caption label="&onStartLegend.label;"/>
+
+ <checkbox id="generalStartupBrowser"
+ label="&navCheck.label;"
+ accesskey="&navCheck.accesskey;"
+ preference="general.startup.browser"/>
+ </groupbox>
+
+ <groupbox id="toolbarStyleBox" align="start" flex="1">
+ <caption label="&showToolsLegend.label;"/>
+
+ <radiogroup id="toolbarStyle"
+ preference="browser.chrome.toolbar_style">
+ <radio value="2"
+ label="&picsNtextRadio.label;"
+ accesskey="&picsNtextRadio.accesskey;"/>
+ <radio value="0"
+ label="&picsOnlyRadio.label;"
+ accesskey="&picsOnlyRadio.accesskey;"/>
+ <radio value="1"
+ label="&textonlyRadio.label;"
+ accesskey="&textonlyRadio.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+ </hbox>
+
+ <vbox class="box-padded" align="start">
+ <checkbox id="showHideTooltips"
+ label="&showHideTooltips.label;"
+ accesskey="&showHideTooltips.accesskey;"
+ preference="browser.chrome.toolbar_tips"/>
+ </vbox>
+#ifndef XP_MACOSX
+ <vbox class="box-padded"
+ align="start">
+ <checkbox id="showHideGrippies"
+ label="&showHideGrippies.label;"
+ accesskey="&showHideGrippies.accesskey;"
+ preference="browser.toolbars.grippyhidden"/>
+ </vbox>
+#endif
+ <groupbox id="switchLocaleBox" align="start">
+ <caption label="&pref.locales.title;"/>
+ <description>&selectLocale.label;</description>
+
+ <menulist id="switchLocales"
+ onselect="SelectLocale(this);"/>
+
+ </groupbox>
+
+ <groupbox id="dateTimeFormatting" align="start">
+ <caption label="&dateTimeFormatting.label;"/>
+ <radiogroup id="formatLocale"
+ preference="intl.regional_prefs.use_os_locales"
+ orient="vertical">
+ <radio id="appLocale"
+ value="false"/>
+ <!-- label and accesskey will be set dynamically -->
+ <radio id="rsLocale"
+ value="true"/>
+ <!-- label and accesskey will be set dynamically -->
+ </radiogroup>
+ </groupbox>
+
+ <description>&restartOnLocaleChange.label;</description>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-applicationManager.js b/comm/suite/components/pref/content/pref-applicationManager.js
new file mode 100644
index 0000000000..83d18953ef
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-applicationManager.js
@@ -0,0 +1,100 @@
+/* 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gAppManagerDialog = {
+ _removed: [],
+
+ init: function appManager_init() {
+ this.handlerInfo = window.arguments[0];
+
+ var bundle = document.getElementById("appManagerBundle");
+ var contentText;
+ if (this.handlerInfo.type == TYPE_MAYBE_FEED)
+ contentText = bundle.getString("descriptionHandleWebFeeds");
+ else {
+ var description = gApplicationsPane._describeType(this.handlerInfo);
+ var key =
+ (this.handlerInfo.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) ?
+ "descriptionHandleFile" :
+ "descriptionHandleProtocol";
+ contentText = bundle.getFormattedString(key, [description]);
+ }
+ 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.className = "listitem-iconic";
+ item.setAttribute("image",
+ gApplicationsPane._getIconURLForHandlerApp(app));
+ item.app = app;
+ }
+
+ list.selectedIndex = 0;
+ },
+
+ onOK: function appManager_onOK() {
+ 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 appManager_onCancel() {
+ // do nothing
+ },
+
+ remove: function appManager_remove() {
+ 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 appManager_onSelect() {
+ var list = document.getElementById("appList");
+ if (!list.selectedItem) {
+ document.getElementById("cmd_delete").setAttribute("disabled", "true");
+ return;
+ }
+ document.getElementById("cmd_delete").removeAttribute("disabled");
+ 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/comm/suite/components/pref/content/pref-applicationManager.xul b/comm/suite/components/pref/content/pref-applicationManager.xul
new file mode 100644
index 0000000000..38988e1080
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-applicationManager.xul
@@ -0,0 +1,56 @@
+<?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://communicator/skin/"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/pref/pref-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 src="chrome://communicator/content/pref/pref-applications.js"/>
+ <script src="chrome://communicator/content/pref/pref-applicationManager.js"/>
+
+ <commandset id="appManagerCommandSet">
+ <command id="cmd_delete"
+ oncommand="gAppManagerDialog.remove();"
+ disabled="true"/>
+ </commandset>
+
+ <keyset id="appManagerKeyset">
+ <key id="delete" keycode="VK_DELETE" command="cmd_delete"/>
+ </keyset>
+
+ <stringbundleset id="appManagerBundleset">
+ <stringbundle id="appManagerBundle"
+ src="chrome://communicator/locale/pref/pref-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_delete"/>
+ <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/comm/suite/components/pref/content/pref-applications.js b/comm/suite/components/pref/content/pref-applications.js
new file mode 100644
index 0000000000..b3ca0d71fd
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-applications.js
@@ -0,0 +1,1606 @@
+/* -*- Mode: Java; tab-width: 2; 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/. */
+
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const {ShellService} = ChromeUtils.import("resource:///modules/ShellService.jsm");
+// Needed as this script is also loaded by pref-applicationManager.xul.
+const {XPCOMUtils} =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function Startup()
+{
+ gApplicationsPane.init();
+}
+
+XPCOMUtils.defineLazyServiceGetters(this, {
+ gCategoryManager: ["@mozilla.org/categorymanager;1", "nsICategoryManager"],
+ gHandlerService: ["@mozilla.org/uriloader/handler-service;1", "nsIHandlerService"],
+ gMIMEService: ["@mozilla.org/mime;1", "nsIMIMEService"],
+ gWebContentConverterService: ["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1", "nsIWebContentConverterService"],
+});
+
+//****************************************************************************//
+// Constants & Enumeration Values
+
+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";
+
+/*
+ * Preferences where we store handling information about the feed type.
+ *
+ * browser.feeds.handler
+ * - "messenger", "reader" (clarified further using the .default preference),
+ * or "ask" -- indicates the default handler being used to process feeds;
+ * "messenger" is obsolete, use "reader" instead; to specify that the
+ * handler is messenger, set browser.feeds.handler.default to "messenger";
+ *
+ * browser.feeds.handler.default
+ * - "messenger", "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
+ * - nsIFile, 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.
+const kActionChooseApp = -2;
+const kActionManageApp = -1;
+
+//****************************************************************************//
+// Utilities
+
+function getFileDisplayName(aFile) {
+ if (AppConstants.platform == "win" &&
+ aFile instanceof Ci.nsILocalFileWin) {
+ try {
+ return aFile.getVersionInfoField("FileDescription");
+ } catch (e) {}
+ } else if (AppConstants.platform == "macosx" &&
+ aFile instanceof Ci.nsILocalFileMac) {
+ try {
+ return aFile.bundleDisplayName;
+ } catch (e) {}
+ }
+ return aFile.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 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,
+
+
+ //**************************************************************************//
+ // nsIHandlerInfo
+
+ // The MIME type or protocol scheme.
+ type: null,
+
+ get description() {
+ if (this.wrappedHandlerInfo.description)
+ return this.wrappedHandlerInfo.description;
+
+ if (this.primaryExtension) {
+ var extension = this.primaryExtension.toUpperCase();
+ return gApplicationsPane._prefsBundle.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(aNewHandler) {
+ var possibleApps = this.possibleApplicationHandlers.enumerate();
+ while (possibleApps.hasMoreElements()) {
+ if (possibleApps.getNext().equals(aNewHandler))
+ return;
+ }
+ this.possibleApplicationHandlers.appendElement(aNewHandler);
+ },
+
+ removePossibleApplicationHandler(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 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)) {
+ return this.wrappedHandlerInfo.hasDefaultHandler ?
+ Ci.nsIHandlerInfo.useSystemDefault :
+ Ci.nsIHandlerInfo.saveToDisk;
+ }
+
+ return this.wrappedHandlerInfo.preferredAction;
+ },
+
+ set preferredAction(aNewValue) {
+ this.wrappedHandlerInfo.preferredAction = aNewValue;
+ },
+
+ get alwaysAskBeforeHandling() {
+ // 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.
+ get primaryExtension() {
+ try {
+ if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
+ this.wrappedHandlerInfo.primaryExtension)
+ return this.wrappedHandlerInfo.primaryExtension;
+ } catch(ex) {}
+
+ return null;
+ },
+
+ //**************************************************************************//
+ // Storage
+
+ store() {
+ gHandlerService.store(this.wrappedHandlerInfo);
+ },
+
+
+ //**************************************************************************//
+ // Icons
+
+ get smallIcon() {
+ return this._getIcon(16);
+ },
+
+ get largeIcon() {
+ return this._getIcon(32);
+ },
+
+ _getIcon(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;
+
+ // We're falling back to a generic icon when we can't get a URL for one
+ // (for example in the case of protocol schemes).
+ return null;
+ },
+
+ // The type class is used for setting icons through CSS for types that don't
+ // explicitly set their icons.
+ typeClass: "unknown"
+
+};
+
+
+//****************************************************************************//
+// 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,
+
+ //**************************************************************************//
+ // nsIHandlerInfo
+
+ get description() {
+ return gApplicationsPane._prefsBundle.getString(this.typeClass);
+ },
+
+ get preferredApplicationHandler() {
+ switch (document.getElementById(this._prefSelectedReader).value) {
+ case "client":
+ var file = document.getElementById(this._prefSelectedApp).value;
+ if (file)
+ return getLocalHandlerApp(file);
+
+ return null;
+
+ case "web":
+ var uri = document.getElementById(this._prefSelectedWeb).value;
+ if (!uri)
+ return null;
+ return gWebContentConverterService.getWebContentHandlerByURI(this.type, uri);
+
+ case "messenger":
+ default:
+ // When the pref is set to messenger, 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) {
+ document.getElementById(this._prefSelectedApp).value = aNewValue.executable;
+ document.getElementById(this._prefSelectedReader).value = "client";
+ }
+ else if (aNewValue instanceof Ci.nsIWebContentHandlerInfo) {
+ document.getElementById(this._prefSelectedWeb).value = aNewValue.uri;
+ document.getElementById(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, RSS News & Blogs, 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.
+ gWebContentConverterService.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, nsIArray::enumerate
+ // and nsIArray::indexOf.
+ this._possibleApplicationHandlers = {
+ _inner: [],
+ _removed: [],
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIMutableArray, Ci.nsIArray]),
+
+ get length() {
+ return this._inner.length;
+ },
+
+ enumerate: function() {
+ return new ArrayEnumerator(this._inner);
+ },
+
+ indexOf: function indexOf(startIndex, element) {
+ return this._inner.indexOf(element, startIndex);
+ },
+
+ appendElement: function(aHandlerApp) {
+ 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 = document.getElementById(this._prefSelectedApp).value;
+ if (preferredAppFile) {
+ let preferredApp = getLocalHandlerApp(preferredAppFile);
+ let defaultApp = this._defaultApplicationHandler;
+ if (!defaultApp || !defaultApp.equals(preferredApp))
+ this._possibleApplicationHandlers.appendElement(preferredApp);
+ }
+
+ // Add the registered web handlers. There can be any number of these.
+ var webHandlers = gWebContentConverterService.getContentHandlers(this.type);
+ for (let webHandler of webHandlers)
+ this._possibleApplicationHandlers.appendElement(webHandler);
+
+ return this._possibleApplicationHandlers;
+ },
+
+ __defaultApplicationHandler: undefined,
+ get _defaultApplicationHandler() {
+ if (this.__defaultApplicationHandler !== undefined)
+ return this.__defaultApplicationHandler;
+
+ var defaultFeedReader = null;
+ try {
+ defaultFeedReader = ShellService.defaultFeedReader;
+ }
+ catch(ex) {
+ // no default reader
+ }
+
+ 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() {
+ try {
+ if (ShellService.defaultFeedReader)
+ return true;
+ }
+ catch(ex) {
+ // no default reader
+ }
+
+ 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 (document.getElementById(this._prefSelectedAction).value) {
+
+ 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":
+ case "messenger":
+ default:
+ return Ci.nsIHandlerInfo.handleInternally;
+ }
+ },
+
+ set preferredAction(aNewValue) {
+ switch (aNewValue) {
+
+ case Ci.nsIHandlerInfo.handleInternally:
+ document.getElementById(this._prefSelectedReader).value = "messenger";
+ break;
+
+ case Ci.nsIHandlerInfo.useHelperApp:
+ document.getElementById(this._prefSelectedAction).value = "reader";
+ // The controller has already set preferredApplicationHandler
+ // to the new helper app.
+ break;
+
+ case Ci.nsIHandlerInfo.useSystemDefault:
+ document.getElementById(this._prefSelectedAction).value = "reader";
+ this.preferredApplicationHandler = this._defaultApplicationHandler;
+ break;
+ }
+ },
+
+ get alwaysAskBeforeHandling() {
+ return document.getElementById(this._prefSelectedAction).value == "ask";
+ },
+
+ set alwaysAskBeforeHandling(aNewValue) {
+ if (aNewValue)
+ document.getElementById(this._prefSelectedAction).value = "ask";
+ else
+ document.getElementById(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
+
+ primaryExtension: "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() {
+ for (let app of this._possibleApplicationHandlers._removed) {
+ if (app instanceof Ci.nsILocalHandlerApp) {
+ let pref = document.getElementById(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);
+ gWebContentConverterService.removeContentHandler(app.contentType,
+ app.uri);
+ }
+ }
+ this._possibleApplicationHandlers._removed = [];
+ },
+
+
+ //**************************************************************************//
+ // Icons
+
+ smallIcon: null,
+
+ largeIcon: null,
+
+ // The type class is used for setting icons through CSS for types that don't
+ // explicitly set their icons.
+ typeClass: "webFeed",
+};
+
+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,
+ typeClass: "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,
+ typeClass: "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,
+ typeClass: "audioPodcastFeed",
+};
+
+
+//****************************************************************************//
+// 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,
+
+
+ //**************************************************************************//
+ // Initialization & Destruction
+
+ init() {
+ // Initialize shortcuts to some commonly accessed elements & values.
+ this._brandShortName =
+ document.getElementById("bundleBrand").getString("brandShortName");
+ this._prefsBundle = document.getElementById("bundlePrefApplications");
+ 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.
+ Services.prefs.addObserver(PREF_FEED_SELECTED_APP, this);
+ Services.prefs.addObserver(PREF_FEED_SELECTED_WEB, this);
+ Services.prefs.addObserver(PREF_FEED_SELECTED_ACTION, this);
+
+ Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_APP, this);
+ Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_WEB, this);
+ Services.prefs.addObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this);
+
+ Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_APP, this);
+ Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_WEB, this);
+ Services.prefs.addObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this);
+
+ // Listen for window unload so we can remove our preference observers.
+ window.addEventListener("unload", this);
+
+ // Listen for user events on the listbox and its children
+ this._list.addEventListener("select", this);
+ this._list.addEventListener("command", this);
+
+ // 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.
+ this._sortColumn = document.getElementById("typeColumn");
+ 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");
+ }
+
+ // Load the data and build the list of handlers.
+ this._loadData();
+ this._rebuildVisibleTypes();
+ this._sortVisibleTypes();
+ this._rebuildView();
+
+ // Notify observers that the UI is now ready
+ Services.obs.notifyObservers(window, "app-handler-pane-loaded");
+ },
+
+ destroy() {
+ this._list.removeEventListener("command", this);
+ this._list.removeEventListener("select", this);
+ window.removeEventListener("unload", this);
+ Services.prefs.removeObserver(PREF_FEED_SELECTED_APP, this);
+ Services.prefs.removeObserver(PREF_FEED_SELECTED_WEB, this);
+ Services.prefs.removeObserver(PREF_FEED_SELECTED_ACTION, this);
+
+ Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_APP, this);
+ Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_WEB, this);
+ Services.prefs.removeObserver(PREF_VIDEO_FEED_SELECTED_ACTION, this);
+
+ Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_APP, this);
+ Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_WEB, this);
+ Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_ACTION, this);
+ },
+
+
+ //**************************************************************************//
+ // nsISupports
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsIDOMEventListener]),
+
+ //**************************************************************************//
+ // nsIObserver
+
+ observe(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) {
+ // All the prefs we observe can affect what we display, so we rebuild
+ // the view when any of them changes.
+ this._rebuildView();
+ }
+ },
+
+
+ //**************************************************************************//
+ // nsIDOMEventListener
+
+ handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "unload":
+ this.destroy();
+ break;
+ case "select":
+ if (this._list.selectedItem)
+ this._list.setAttribute("lastSelectedType",
+ this._list.selectedItem.type);
+ break;
+ case "command":
+ var target = aEvent.originalTarget;
+ switch (target.localName) {
+ case "listitem":
+ if (!this._list.disabled &&
+ target.type == this._list.getAttribute("lastSelectedType"))
+ this._list.selectedItem = target;
+ break;
+ case "listcell":
+ this.rebuildActionsMenu();
+ break;
+ case "menuitem":
+ switch (parseInt(target.value)) {
+ case kActionChooseApp:
+ this.chooseApp();
+ break;
+ case kActionManageApp:
+ this.manageApp();
+ break;
+ default:
+ this.onSelectAction(target);
+ break;
+ }
+ break;
+ }
+ }
+ },
+
+
+ //**************************************************************************//
+ // Composed Model Construction
+
+ _loadData() {
+ this._loadFeedHandler();
+ this._loadApplicationHandlers();
+ },
+
+ _loadFeedHandler() {
+ this._handledTypes[TYPE_MAYBE_FEED] = feedHandlerInfo;
+
+ this._handledTypes[TYPE_MAYBE_VIDEO_FEED] = videoFeedHandlerInfo;
+
+ this._handledTypes[TYPE_MAYBE_AUDIO_FEED] = audioFeedHandlerInfo;
+ },
+
+ /**
+ * Load the set of handlers defined by the application datastore.
+ */
+ _loadApplicationHandlers() {
+ var wrappedHandlerInfos = gHandlerService.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;
+ }
+ }
+ },
+
+
+ //**************************************************************************//
+ // View Construction
+
+ _rebuildVisibleTypes() {
+ // Reset the list of visible types and the visible type description counts.
+ this._visibleTypes = [];
+ this._visibleTypeDescriptionCount = {};
+
+ for (let type in this._handledTypes) {
+ let handlerInfo = this._handledTypes[type];
+
+ // 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() {
+ // Clear the list of entries (the first 2 elements are <listcols> and
+ // <listhead>, they should never get removed).
+ while (this._list.childNodes.length > 2)
+ this._list.lastChild.remove();
+
+ 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 (let visibleType of visibleTypes) {
+ let item = document.createElement("listitem");
+ item.setAttribute("allowevents", "true");
+ item.setAttribute("type", visibleType.type);
+ item.setAttribute("typeDescription", this._describeType(visibleType));
+ if (visibleType.smallIcon)
+ item.setAttribute("typeIcon", visibleType.smallIcon);
+ else
+ item.setAttribute("typeClass", visibleType.typeClass);
+ item.setAttribute("actionDescription",
+ this._describePreferredAction(visibleType));
+
+ if (!this._setIconClassForPreferredAction(visibleType, item)) {
+ var sysIcon = this._getIconURLForPreferredAction(visibleType);
+ if (sysIcon)
+ item.setAttribute("actionIcon", sysIcon);
+ else
+ item.setAttribute("appHandlerIcon", "app");
+ }
+
+ this._list.appendChild(item);
+ }
+ },
+
+ _matchesFilter(aType) {
+ var filterValue = this._filter.value.toLowerCase();
+ return this._describeType(aType).toLowerCase().includes(filterValue) ||
+ this._describePreferredAction(aType).toLowerCase().includes(filterValue);
+ },
+
+ /**
+ * 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(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(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]);
+ return this._prefsBundle.getString("alwaysAsk");
+ }
+
+ // The nsHandlerInfoAction enumeration values in nsIHandlerInfo identify
+ // the actions the application can take with content of various types.
+ // But since we've stopped support for plugins, there's no value
+ // identifying the "use plugin" action, so we use this constant instead.
+ const kActionUsePlugin = -3;
+
+ switch (aHandlerInfo.preferredAction) {
+ case Ci.nsIHandlerInfo.saveToDisk:
+ return this._prefsBundle.getString("saveFile");
+
+ case Ci.nsIHandlerInfo.useHelperApp:
+ var preferredApp = aHandlerInfo.preferredApplicationHandler;
+ var name = (preferredApp instanceof Ci.nsILocalHandlerApp) ?
+ getFileDisplayName(preferredApp.executable) :
+ preferredApp.name;
+ return this._prefsBundle.getFormattedString("useApp", [name]);
+
+ case Ci.nsIHandlerInfo.handleInternally:
+ // For the feed type, handleInternally means News & Blogs.
+ if (isFeedType(aHandlerInfo.type))
+ return this._prefsBundle.getFormattedString("addNewsBlogsInApp",
+ [this._brandShortName]);
+
+ // For other types, handleInternally looks like either useHelperApp
+ // or useSystemDefault depending on whether or not there's a preferred
+ // handler app.
+ return (this.isValidHandlerApp(aHandlerInfo.preferredApplicationHandler)) ?
+ aHandlerInfo.preferredApplicationHandler.name :
+ 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]);
+
+ // We no longer support plugins, select "ask" instead:
+ case kActionUsePlugin:
+ return this._prefsBundle.getString("alwaysAsk");
+ }
+ // we should never end up here but do a return to end up with a value
+ return null;
+ },
+
+ /**
+ * 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(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;
+
+ if (aHandlerApp instanceof Ci.nsIGIOMimeApp)
+ return aHandlerApp.command;
+
+ return false;
+ },
+
+ _isValidHandlerExecutable(aExecutable) {
+ var file = Services.dirsvc.get("XREExeF",
+ Ci.nsIFile);
+ return aExecutable &&
+ aExecutable.exists() &&
+ aExecutable.isExecutable() &&
+ aExecutable.leafName != file.leafName;
+ },
+
+ /**
+ * Rebuild the actions menu for the selected entry. Gets called by
+ * the listcell constructor when an entry in the list gets selected.
+ * Note that this would not work from onselect on the listbox because
+ * the XBL needs to be applied _before_ calling this function!
+ */
+ rebuildActionsMenu() {
+ var typeItem = this._list.selectedItem;
+ var handlerInfo = this._handledTypes[typeItem.type];
+ var cell =
+ document.getAnonymousElementByAttribute(typeItem, "anonid", "action-cell");
+ var menu =
+ document.getAnonymousElementByAttribute(cell, "anonid", "action-menu");
+ var menuPopup = menu.menupopup;
+
+ // Clear out existing items.
+ while (menuPopup.hasChildNodes())
+ menuPopup.lastChild.remove();
+
+ {
+ let askMenuItem = document.createElement("menuitem");
+ askMenuItem.setAttribute("class", "handler-action");
+ askMenuItem.setAttribute("value", 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("appHandlerIcon", "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)) {
+ let saveMenuItem = document.createElement("menuitem");
+ saveMenuItem.setAttribute("class", "handler-action");
+ saveMenuItem.setAttribute("value", Ci.nsIHandlerInfo.saveToDisk);
+ let label = this._prefsBundle.getString("saveFile");
+ saveMenuItem.setAttribute("label", label);
+ saveMenuItem.setAttribute("tooltiptext", label);
+ saveMenuItem.setAttribute("appHandlerIcon", "save");
+ menuPopup.appendChild(saveMenuItem);
+ }
+
+ // If this is the feed type, add a News & Blogs item.
+ if (isFeedType(handlerInfo.type)) {
+ let internalMenuItem = document.createElement("menuitem");
+ internalMenuItem.setAttribute("class", "handler-action");
+ internalMenuItem.setAttribute("value", Ci.nsIHandlerInfo.handleInternally);
+ let label = this._prefsBundle.getFormattedString("addNewsBlogsInApp",
+ [this._brandShortName]);
+ internalMenuItem.setAttribute("label", label);
+ internalMenuItem.setAttribute("tooltiptext", label);
+ internalMenuItem.setAttribute("appHandlerIcon", "feed");
+ menuPopup.appendChild(internalMenuItem);
+ }
+
+ // Add a separator to distinguish these items from the helper app items
+ // that follow them.
+ let menuSeparator = document.createElement("menuseparator");
+ menuPopup.appendChild(menuSeparator);
+
+ // Create a menu item for the OS default application, if any.
+ if (handlerInfo.hasDefaultHandler) {
+ let defaultMenuItem = document.createElement("menuitem");
+ defaultMenuItem.setAttribute("class", "handler-action");
+ defaultMenuItem.setAttribute("value", Ci.nsIHandlerInfo.useSystemDefault);
+ let label = this._prefsBundle.getFormattedString("useDefault",
+ [handlerInfo.defaultDescription]);
+ defaultMenuItem.setAttribute("label", label);
+ defaultMenuItem.setAttribute("tooltiptext", handlerInfo.defaultDescription);
+ let sysIcon = this._getIconURLForSystemDefault(handlerInfo);
+ if (sysIcon)
+ defaultMenuItem.setAttribute("image", sysIcon);
+ else
+ defaultMenuItem.setAttribute("appHandlerIcon", "app");
+
+ 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("class", "handler-action");
+ menuItem.setAttribute("value", 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);
+ let sysIcon = this._getIconURLForHandlerApp(possibleApp);
+ if (sysIcon)
+ menuItem.setAttribute("image", sysIcon);
+ else
+ menuItem.setAttribute("appHandlerIcon", "app");
+
+ // 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);
+ }
+
+// Add gio handlers
+ if (Cc["@mozilla.org/gio-service;1"]) {
+ let gIOSvc = Cc["@mozilla.org/gio-service;1"]
+ .getService(Ci.nsIGIOService);
+ var gioApps = gIOSvc.getAppsForURIScheme(typeItem.type);
+ let enumerator = gioApps.enumerate();
+ let possibleHandlers = handlerInfo.possibleApplicationHandlers;
+ while (enumerator.hasMoreElements()) {
+ let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp);
+ // OS handler share the same name, it's most likely the same app, skipping...
+ if (handler.name == handlerInfo.defaultDescription) {
+ continue;
+ }
+ // Check if the handler is already in possibleHandlers
+ let appAlreadyInHandlers = false;
+ for (let i = possibleHandlers.length - 1; i >= 0; --i) {
+ let app = possibleHandlers.queryElementAt(i, Ci.nsIHandlerApp);
+ // nsGIOMimeApp::Equals is able to compare with Ci.nsILocalHandlerApp
+ if (handler.equals(app)) {
+ appAlreadyInHandlers = true;
+ break;
+ }
+ }
+ if (!appAlreadyInHandlers) {
+ let menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("action", Ci.nsIHandlerInfo.useHelperApp);
+ let label = this._prefsBundle.getFormattedString("useApp", [handler.name]);
+ menuItem.setAttribute("label", label);
+ menuItem.setAttribute("tooltiptext", label);
+ menuItem.setAttribute("image", this._getIconURLForHandlerApp(handler));
+
+ // 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 = handler;
+
+ menuPopup.appendChild(menuItem);
+ possibleAppMenuItems.push(menuItem);
+ }
+ }
+ }
+
+ // Create a menu item for selecting a local application.
+ let canOpenWithOtherApp = true;
+ if (AppConstants.platform == "win") {
+ // On Windows, selecting an application to open another application
+ // would be meaningless so we special case executables.
+ let executableType = gMIMEService.getTypeFromExtension("exe");
+ canOpenWithOtherApp = handlerInfo.type != executableType;
+ }
+ if (canOpenWithOtherApp)
+ {
+ let menuItem = document.createElement("menuitem");
+ menuItem.setAttribute("class", "handler-action");
+ menuItem.setAttribute("value", kActionChooseApp);
+ 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("class", "handler-action");
+ menuItem.setAttribute("value", kActionManageApp);
+ 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.value = Ci.nsIHandlerInfo.alwaysAsk;
+ else if (handlerInfo.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
+ preferredApp)
+ menu.selectedItem =
+ possibleAppMenuItems.filter(v => v.handlerApp.equals(preferredApp))[0];
+ else
+ menu.value = handlerInfo.preferredAction;
+ },
+
+
+ //**************************************************************************//
+ // Sorting & Filtering
+
+ _sortColumn: null,
+
+ /**
+ * Sort the list when the user clicks on a column header.
+ */
+ sort(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() {
+ 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();
+ },
+
+
+ //**************************************************************************//
+ // Changes
+
+ onSelectAction(aActionItem) {
+ this._storingAction = true;
+
+ try {
+ this._storeAction(aActionItem);
+ }
+ finally {
+ this._storingAction = false;
+ }
+ },
+
+ _storeAction(aActionItem) {
+ var typeItem = this._list.selectedItem;
+ var handlerInfo = this._handledTypes[typeItem.type];
+
+ let action = parseInt(aActionItem.getAttribute("value"));
+
+ // 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 preferred action.
+ handlerInfo.preferredAction = action;
+
+ // Set the "always ask" flag.
+ handlerInfo.alwaysAskBeforeHandling = action == Ci.nsIHandlerInfo.alwaysAsk;
+
+ handlerInfo.store();
+
+ // Make sure the handler info object is flagged to indicate that there is
+ // now some user configuration for the type.
+
+ // Update the action label and image to reflect the new preferred action.
+ typeItem.setAttribute("actionDescription",
+ this._describePreferredAction(handlerInfo));
+ if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
+ var sysIcon = this._getIconURLForPreferredAction(handlerInfo);
+ if (sysIcon)
+ typeItem.setAttribute("actionIcon", sysIcon);
+ else
+ typeItem.setAttribute("appHandlerIcon", "app");
+ }
+ },
+
+ manageApp() {
+ var typeItem = this._list.selectedItem;
+ var handlerInfo = this._handledTypes[typeItem.type];
+
+ document.documentElement.openSubDialog("chrome://communicator/content/pref/pref-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 listitem too. Will be visible when selecting another row
+ typeItem.setAttribute("actionDescription",
+ this._describePreferredAction(handlerInfo));
+ if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
+ var sysIcon = this._getIconURLForPreferredAction(handlerInfo);
+ if (sysIcon)
+ typeItem.setAttribute("actionIcon", sysIcon);
+ else
+ typeItem.setAttribute("appHandlerIcon", "app");
+ }
+ },
+
+ handlerApp: null,
+
+ finishChooseApp() {
+ if (this.handlerApp) {
+ // Add the app to the type's list of possible handlers.
+ let handlerInfo = this._handledTypes[this._list.selectedItem.type];
+ handlerInfo.addPossibleApplicationHandler(this.handlerApp);
+ }
+
+ // 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 (this.handlerApp) {
+ var actionsCell =
+ document.getAnonymousElementByAttribute(this._list.selectedItem,
+ "anonid", "action-cell");
+ var actionsMenu =
+ document.getAnonymousElementByAttribute(actionsCell,
+ "anonid", "action-menu");
+ let menuItems = actionsMenu.menupopup.childNodes;
+ for (let i = 0; i < menuItems.length; i++) {
+ let menuItem = menuItems[i];
+ if (menuItem.handlerApp &&
+ menuItem.handlerApp.equals(this.handlerApp)) {
+ actionsMenu.selectedIndex = i;
+ this.onSelectAction(menuItem);
+ break;
+ }
+ }
+ }
+ },
+
+ chooseApp() {
+ this.handlerApp = null;
+
+ if (AppConstants.platform == "win") {
+ let params = {};
+ let handlerInfo = this._handledTypes[this._list.selectedItem.type];
+
+ if (isFeedType(handlerInfo.type)) {
+ // MIME info will be null, create a temp object.
+ params.mimeInfo =
+ gMIMEService.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)) {
+ this.handlerApp = params.handlerApp;
+ }
+ this.finishChooseApp();
+ } else if (Services.prefs.getBoolPref("browser.download.useAppChooser", true) && ("@mozilla.org/applicationchooser;1" in Cc)) {
+ let mimeInfo;
+ let handlerInfo = this._handledTypes[this._list.selectedItem.type];
+ if (isFeedType(handlerInfo.type)) {
+ // MIME info will be null, create a temp object.
+ mimeInfo =
+ gMIMEService.getFromTypeAndExtension(handlerInfo.type,
+ handlerInfo.primaryExtension);
+ } else {
+ mimeInfo = handlerInfo.wrappedHandlerInfo;
+ }
+
+ var appChooser = Cc["@mozilla.org/applicationchooser;1"]
+ .createInstance(Ci.nsIApplicationChooser);
+ appChooser.init(window, this._prefsBundle.getString("fpTitleChooseApp"));
+ var contentTypeDialogObj = this;
+ let appChooserCallback = function appChooserCallback_done(aResult) {
+ if (aResult) {
+ contentTypeDialogObj.handlerApp = aResult.QueryInterface(Ci.nsILocalHandlerApp);
+ }
+ contentTypeDialogObj.finishChooseApp();
+ };
+ appChooser.open(mimeInfo.MIMEType, appChooserCallback);
+ // The finishChooseApp is called from appChooserCallback
+ } else {
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+ let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
+ fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
+ fp.appendFilters(Ci.nsIFilePicker.filterApps);
+
+ // 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.open(rv => {
+ if (rv == Ci.nsIFilePicker.returnOK && fp.file &&
+ this._isValidHandlerExecutable(fp.file)) {
+ let handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]
+ .createInstance(Ci.nsILocalHandlerApp);
+ handlerApp.name = getFileDisplayName(fp.file);
+ handlerApp.executable = fp.file;
+ this.handlerApp = handlerApp;
+ }
+ this.finishChooseApp();
+ });
+ }
+ },
+
+ _setIconClassForPreferredAction(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("appHandlerIcon", "ask");
+ return true;
+ }
+
+ switch (aHandlerInfo.preferredAction) {
+ case Ci.nsIHandlerInfo.saveToDisk:
+ aElement.setAttribute("appHandlerIcon", "save");
+ return true;
+
+ case Ci.nsIHandlerInfo.handleInternally:
+ if (isFeedType(aHandlerInfo.type)) {
+ aElement.setAttribute("appHandlerIcon", "feed");
+ return true;
+ }
+ break;
+ }
+ aElement.removeAttribute("appHandlerIcon");
+ return false;
+ },
+
+ _getIconURLForPreferredAction(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.
+ return null;
+ },
+
+ _getIconURLForHandlerApp(aHandlerApp) {
+ if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
+ return this._getIconURLForFile(aHandlerApp.executable);
+
+ if (Services.prefs.getBoolPref("browser.chrome.favicons")) { // q.v. Bug 514671
+ 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(aFile) {
+ var fph = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ var urlSpec = fph.getURLSpecFromFile(aFile);
+
+ return "moz-icon://" + urlSpec + "?size=16";
+ },
+
+ _getIconURLForWebApp(aWebAppURITemplate) {
+ var uri = Services.io.newURI(aWebAppURITemplate);
+
+ // Unfortunately we need to use favicon.ico here, but we don't know
+ // about any other possibility to retrieve an icon for the web app/site
+ // without loading a specific full URL and parsing it for a possible
+ // shortcut icon.
+
+ return /^https?/.test(uri.scheme) ? uri.resolve("/favicon.ico") : "";
+ },
+
+ _getIconURLForSystemDefault(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 null;
+ }
+
+};
diff --git a/comm/suite/components/pref/content/pref-applications.xul b/comm/suite/components/pref/content/pref-applications.xul
new file mode 100644
index 0000000000..351c254252
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-applications.xul
@@ -0,0 +1,113 @@
+<?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 % prefApplicationsDTD SYSTEM "chrome://communicator/locale/pref/pref-applications.dtd"> %prefApplicationsDTD;
+]>
+
+<overlay id="ApplicationsPaneOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="applications_pane"
+ label="&pref.applications.title;"
+ script="chrome://communicator/content/pref/pref-applications.js">
+
+ <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"/>
+ <preference id="browser.download.useAppChooser"
+ name="browser.download.useAppChooser"
+ type="bool"/>
+ </preferences>
+
+ <stringbundleset id="appBundleset">
+ <stringbundle id="bundleBrand"
+ src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="bundlePrefApplications"
+ src="chrome://communicator/locale/pref/pref-applications.properties"/>
+ </stringbundleset>
+
+ <hbox align="center">
+ <textbox id="filter"
+ flex="1"
+ type="search"
+ placeholder="&search.placeholder;"
+ clickSelectsAll="true"
+ aria-controls="handlersView"
+ oncommand="gApplicationsPane._rebuildView();"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <listbox id="handlersView" persist="lastSelectedType" flex="1"
+ preference="pref.downloads.disable_button.edit_actions">
+ <listcols>
+ <listcol width="1" flex="1"/>
+ <listcol width="1" flex="1"/>
+ </listcols>
+ <listhead>
+ <listheader id="typeColumn" label="&typeColumn.label;" value="type"
+ accesskey="&typeColumn.accesskey;" persist="sortDirection"
+ onclick="gApplicationsPane.sort(event);"
+ sortDirection="ascending"/>
+ <listheader id="actionColumn" label="&actionColumn2.label;" value="action"
+ accesskey="&actionColumn2.accesskey;" persist="sortDirection"
+ onclick="gApplicationsPane.sort(event);"/>
+ </listhead>
+ </listbox>
+#ifdef XP_LINUX
+ <separator class="thin"/>
+
+ <hbox align="center">
+ <checkbox id="downloadUseAppChooser"
+ label="&useAppChooser.label;"
+ accesskey="&useAppChooser.accesskey;"
+ preference="browser.download.useAppChooser"/>
+ </hbox>
+#endif
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-cache.js b/comm/suite/components/pref/content/pref-cache.js
new file mode 100644
index 0000000000..be2245b98b
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-cache.js
@@ -0,0 +1,113 @@
+/* -*- 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/. */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+function Startup()
+{
+ updateActualCacheSize();
+}
+
+// Needs to be global because the cache service only keeps a weak reference.
+var CacheObserver = {
+ /* nsICacheStorageConsumptionObserver */
+ onNetworkCacheDiskConsumption: function(aConsumption) {
+ var actualSizeLabel = document.getElementById("cacheSizeInfo");
+ var sizeStrings = DownloadUtils.convertByteUnits(aConsumption);
+ var prefStrBundle = document.getElementById("bundle_prefutilities");
+ var sizeStr = prefStrBundle.getFormattedString("cacheSizeInfo",
+ sizeStrings);
+ actualSizeLabel.textContent = sizeStr;
+ },
+
+ /* nsISupports */
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsICacheStorageConsumptionObserver,
+ Ci.nsISupportsWeakReference])
+};
+
+// because the cache is in kilobytes, and the UI is in megabytes.
+function ReadCacheDiskCapacity()
+{
+ var pref = document.getElementById("browser.cache.disk.capacity");
+ return pref.value >> 10;
+}
+
+function WriteCacheDiskCapacity(aField)
+{
+ return aField.value << 10;
+}
+
+function ReadCacheFolder(aField)
+{
+ var pref = document.getElementById("browser.cache.disk.parent_directory");
+ var file = pref.value;
+
+ if (!file)
+ {
+ try
+ {
+ // no disk cache folder pref set; default to profile directory
+ file = GetSpecialDirectory(Services.dirsvc.has("ProfLD") ? "ProfLD"
+ : "ProfD");
+ }
+ catch (ex) {}
+ }
+
+ if (file) {
+ aField.file = file;
+ aField.label = AppConstants.platform == "macosx" ? file.leafName : file.path;
+ }
+}
+
+function CacheSelectFolder()
+{
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+ let title = document.getElementById("bundle_prefutilities")
+ .getString("cachefolder");
+
+ fp.init(window, title, Ci.nsIFilePicker.modeGetFolder);
+ fp.displayDirectory =
+ document.getElementById("browser.cache.disk.parent_directory").value;
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+
+ fp.open(rv => {
+ if (rv != Ci.nsIFilePicker.returnOK || !fp.file) {
+ return;
+ }
+ document.getElementById("browser.cache.disk.parent_directory").value = fp.file;
+ });
+}
+
+function ClearDiskAndMemCache()
+{
+ Services.cache2.clear();
+ updateActualCacheSize();
+}
+
+function updateCacheSizeUI(cacheSizeEnabled)
+{
+ document.getElementById("browserCacheDiskCacheBefore").disabled = cacheSizeEnabled;
+ document.getElementById("browserCacheDiskCache").disabled = cacheSizeEnabled;
+ document.getElementById("browserCacheDiskCacheAfter").disabled = cacheSizeEnabled;
+}
+
+function ReadSmartSizeEnabled()
+{
+ var enabled = document.getElementById("browser.cache.disk.smart_size.enabled").value;
+ updateCacheSizeUI(enabled);
+ return enabled;
+}
+
+function updateActualCacheSize()
+{
+ Services.cache2.asyncGetDiskConsumption(CacheObserver);
+}
diff --git a/comm/suite/components/pref/content/pref-cache.xul b/comm/suite/components/pref/content/pref-cache.xul
new file mode 100644
index 0000000000..6b60ddef2b
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-cache.xul
@@ -0,0 +1,142 @@
+<?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 % prefCacheDTD SYSTEM "chrome://communicator/locale/pref/pref-cache.dtd">
+%prefCacheDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="cache_pane"
+ label="&pref.cache.title;"
+ script="chrome://communicator/content/pref/pref-cache.js">
+
+ <preferences>
+ <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"
+ type="bool"/>
+ <preference id="pref.advanced.cache.disable_button.clear_disk"
+ name="pref.advanced.cache.disable_button.clear_disk"
+ type="bool"/>
+ <preference id="browser.cache.check_doc_frequency"
+ name="browser.cache.check_doc_frequency"
+ type="int"/>
+ <preference id="network.prefetch-next"
+ name="network.prefetch-next"
+ type="bool"/>
+ <preference id="browser.cache.disk.parent_directory"
+ name="browser.cache.disk.parent_directory"
+ type="file"/>
+ <preference id="browser.cache.disk.enable"
+ name="browser.cache.disk.enable"
+ type="bool"/>
+ <preference id="browser.cache.memory.enable"
+ name="browser.cache.memory.enable"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&pref.cache.caption;"/>
+
+ <description>&cachePara;</description>
+
+ <vbox align="start">
+ <label id="cacheSizeInfo"/>
+ <checkbox id="allowSmartSize"
+ label="&cacheCheck.label;"
+ accesskey="&cacheCheck.accesskey;"
+ onsyncfrompreference="return document.getElementById('cache_pane').ReadSmartSizeEnabled();"
+ preference="browser.cache.disk.smart_size.enabled"/>
+ </vbox>
+ <hbox align="center">
+ <label id="browserCacheDiskCacheBefore"
+ value="&diskCacheUpTo.label;"
+ accesskey="&diskCacheUpTo.accesskey;"
+ control="browserCacheDiskCache"/>
+ <textbox id="browserCacheDiskCache"
+ size="5"
+ type="number"
+ aria-labelledby="browserCacheDiskCacheBefore browserCacheDiskCache browserCacheDiskCacheAfter"
+ preference="browser.cache.disk.capacity"
+ onsyncfrompreference="return document.getElementById('cache_pane').ReadCacheDiskCapacity();"
+ onsynctopreference="return document.getElementById('cache_pane').WriteCacheDiskCapacity(this);"/>
+ <label id="browserCacheDiskCacheAfter"
+ value="&spaceMbytes;"/>
+ <button label="&clearDiskCache.label;"
+ accesskey="&clearDiskCache.accesskey;"
+ oncommand="ClearDiskAndMemCache();"
+ id="clearDiskCache"
+ preference="pref.advanced.cache.disable_button.clear_disk"/>
+ </hbox>
+
+ <vbox>
+ <label value="&diskCacheFolder.label;"/>
+ <hbox align="center">
+ <filefield id="browserCacheDiskCacheFolder"
+ flex="1"
+ preference="browser.cache.disk.parent_directory"
+ preference-editable="true"
+ onsyncfrompreference="return document.getElementById('cache_pane').ReadCacheFolder(this);"/>
+ <button label="&chooseDiskCacheFolder.label;"
+ accesskey="&chooseDiskCacheFolder.accesskey;"
+ oncommand="CacheSelectFolder();"
+ id="chooseDiskCacheFolder">
+ <observes element="browserCacheDiskCacheFolder"
+ attribute="disabled"/>
+ </button>
+ </hbox>
+ </vbox>
+ <description>&diskCacheFolderExplanation;</description>
+
+ <separator class="thin"/>
+
+ <label control="browserCacheCheckDocFrequency"
+ value="&docCache.label;"
+ accesskey="&docCache.accesskey;"/>
+ <hbox align="start">
+ <menulist id="browserCacheCheckDocFrequency"
+ class="indent"
+ preference="browser.cache.check_doc_frequency">
+ <menupopup>
+ <menuitem value="1" label="&checkEveryTime.label;"/>
+ <menuitem value="3" label="&checkAutomatically.label;"/>
+ <menuitem value="0" label="&checkOncePerSession.label;"/>
+ <menuitem value="2" label="&checkNever.label;"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+
+ </groupbox>
+
+ <groupbox id="prefetch">
+ <caption id="prefetchLabel" label="&prefetchTitle.label;"/>
+ <vbox id="prefetchBox" align="start">
+ <checkbox id="enablePrefetch"
+ label="&enablePrefetch.label;"
+ accesskey="&enablePrefetch.accesskey;"
+ preference="network.prefetch-next"/>
+ </vbox>
+ </groupbox>
+
+ <groupbox id="debugCache">
+ <caption label="&debugCache.label;"/>
+ <hbox align="center">
+ <checkbox id="browserEnableDiskCache"
+ label="&debugEnableDiskCache.label;"
+ accesskey="&debugEnableDiskCache.accesskey;"
+ preference="browser.cache.disk.enable"/>
+ <checkbox id="browserEnableCache"
+ label="&debugEnableMemCache.label;"
+ accesskey="&debugEnableMemCache.accesskey;"
+ preference="browser.cache.memory.enable"/>
+ </hbox>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-colors.js b/comm/suite/components/pref/content/pref-colors.js
new file mode 100644
index 0000000000..619af1bd5e
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-colors.js
@@ -0,0 +1,26 @@
+/* -*- 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/. */
+
+function Startup()
+{
+ ToggleCustomColorPickers(document.getElementById("browser.display.use_system_colors").value);
+}
+
+function ToggleCustomColorPickers(aChecked)
+{
+ TogglePickerDisability(aChecked, "browserForegroundColor");
+ TogglePickerDisability(aChecked, "browserBackgroundColor");
+}
+
+function TogglePickerDisability(aDisable, aPicker)
+{
+ var element = document.getElementById(aPicker);
+ aDisable = aDisable ||
+ document.getElementById(element.getAttribute("preference")).locked;
+
+ element.disabled = aDisable;
+ element = document.getElementById(aPicker + "Label");
+ element.disabled = aDisable;
+}
diff --git a/comm/suite/components/pref/content/pref-colors.xul b/comm/suite/components/pref/content/pref-colors.xul
new file mode 100644
index 0000000000..97c5d0c631
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-colors.xul
@@ -0,0 +1,131 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-colors.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="colors_pane"
+ label="&pref.colors.title;"
+ script="chrome://communicator/content/pref/pref-colors.js">
+
+ <preferences id="colors_preferences">
+ <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"
+ onchange="ToggleCustomColorPickers(this.value);"/>
+ <preference id="browser.anchor_color"
+ name="browser.anchor_color"
+ type="string"/>
+ <preference id="browser.active_color"
+ name="browser.active_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.document_color_use"
+ name="browser.display.document_color_use"
+ type="int"/>
+ </preferences>
+ <hbox>
+ <groupbox flex="1" id="pageColours">
+ <caption label="&color;"/>
+ <hbox align="center">
+ <label id="browserForegroundColorLabel"
+ value="&textColor.label;"
+ accesskey="&textColor.accesskey;"
+ flex="1"
+ control="browserForegroundColor"/>
+ <colorpicker id="browserForegroundColor"
+ type="button"
+ palettename="standard"
+ preference="browser.display.foreground_color"/>
+ </hbox>
+ <hbox align="center" style="margin-top: 5px">
+ <label id="browserBackgroundColorLabel"
+ value="&backgroundColor.label;"
+ accesskey="&backgroundColor.accesskey;"
+ flex="1"
+ control="browserBackgroundColor"/>
+ <colorpicker id="browserBackgroundColor"
+ type="button"
+ 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;"
+ flex="1"
+ control="browserAnchorColor"/>
+ <colorpicker id="browserAnchorColor"
+ type="button"
+ palettename="standard"
+ preference="browser.anchor_color"/>
+ </hbox>
+ <hbox align="center" style="margin-top: 5px">
+ <label value="&activeLinkColor.label;"
+ accesskey="&activeLinkColor.accesskey;"
+ flex="1"
+ control="browserActiveColor"/>
+ <colorpicker id="browserActiveColor"
+ type="button"
+ palettename="standard"
+ preference="browser.active_color"/>
+ </hbox>
+ <hbox align="center" style="margin-top: 5px">
+ <label value="&visitedLinkColor.label;"
+ accesskey="&visitedLinkColor.accesskey;"
+ flex="1"
+ control="browserVisitedColor"/>
+ <colorpicker id="browserVisitedColor"
+ type="button"
+ 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>
+
+ <groupbox>
+ <caption label="&someProvColors;"/>
+
+ <radiogroup id="browserDocumentColorUse"
+ preference="browser.display.document_color_use">
+ <radio value="1" label="&alwaysUseDocumentColors.label;"
+ accesskey="&alwaysUseDocumentColors.accesskey;"/>
+ <radio value="2" label="&useMyColors.label;"
+ accesskey="&useMyColors.accesskey;"/>
+ <radio value="0" label="&automaticColors.label;"
+ accesskey="&automaticColors.accesskey;"/>
+ </radiogroup>
+
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-content.js b/comm/suite/components/pref/content/pref-content.js
new file mode 100644
index 0000000000..964c216a28
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-content.js
@@ -0,0 +1,141 @@
+/* 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 {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+var minMinValue;
+var maxMinValue;
+
+/**
+ * When starting up, obtain min and max values for the zoom-range controls
+ * from the first and last values of the zoom-levels array.
+ */
+function Startup()
+{
+ let minElement = document.getElementById("minZoom");
+ let maxElement = document.getElementById("maxZoom");
+ let minMaxLimit = 200;
+ let maxMinLimit = 50; // allow reasonable amounts of overlap
+ let zoomValues = Services.prefs.getCharPref("toolkit.zoomManager.zoomValues")
+ .split(",").map(parseFloat);
+ zoomValues.sort((a, b) => a - b);
+
+ let firstValue = Math.round(100 * zoomValues[0]);
+ let lastValue = Math.round(100 * zoomValues[zoomValues.length - 1]);
+
+ minMinValue = firstValue;
+ minElement.min = minMinValue;
+ minElement.max = lastValue > minMaxLimit ? minMaxLimit : lastValue;
+
+ maxMinValue = firstValue < maxMinLimit ? maxMinLimit : firstValue;
+ maxElement.min = maxMinValue;
+ maxElement.max = lastValue;
+
+ /* defaultZoom stuff */
+
+ let defaultElement = document.getElementById("defaultZoom");
+
+ defaultElement.min = Services.prefs.getIntPref("zoom.minPercent");
+ defaultElement.max = Services.prefs.getIntPref("zoom.maxPercent");
+
+ var zoomValue = Services.contentPrefs2
+ .getCachedGlobal("browser.content.full-zoom", null);
+ if (zoomValue && zoomValue.value) {
+ defaultElement.value = Math.round(zoomValue.value * 100);
+ return;
+ }
+
+ defaultElement.value = 100;
+ Services.contentPrefs2.getGlobal("browser.content.full-zoom", null, {
+ handleResult(pref) {
+ defaultElement.value = Math.round(pref.value * 100);
+ },
+ handleCompletion(reason) {}
+ });
+}
+
+/**
+ * Suspend "min" value while manually typing in a number.
+ */
+function DisableMinCheck(element)
+{
+ element.min = 0;
+}
+
+/**
+ * Modify the maxZoom setting if minZoom was chosen to be larger than it.
+ */
+function AdjustMaxZoom()
+{
+ let minElement = document.getElementById("minZoom");
+ let maxElement = document.getElementById("maxZoom");
+ let maxPref = document.getElementById("zoom.maxPercent");
+
+ if(minElement.valueNumber > maxElement.valueNumber)
+ maxPref.value = minElement.value;
+
+ minElement.min = minMinValue;
+
+ let defaultElement = document.getElementById("defaultZoom");
+ if (defaultElement.valueNumber < minElement.valueNumber) {
+ defaultElement.valueNumber = minElement.valueNumber;
+ SetDefaultZoom();
+ }
+ defaultElement.min = minElement.valueNumber;
+}
+
+/**
+ * Modify the minZoom setting if maxZoom was chosen to be smaller than it,
+ * adjusting maxZoom first if it's below maxMinValue.
+ */
+function AdjustMinZoom()
+{
+ let minElement = document.getElementById("minZoom");
+ let maxElement = document.getElementById("maxZoom");
+ let minPref = document.getElementById("zoom.minPercent");
+ let maxValue = maxElement.valueNumber < maxMinValue ?
+ maxMinValue : maxElement.valueNumber;
+
+ if(maxValue < minElement.valueNumber)
+ minPref.value = maxValue;
+
+ maxElement.min = maxMinValue;
+
+ let defaultElement = document.getElementById("defaultZoom");
+ if (defaultElement.valueNumber > maxElement.valueNumber) {
+ defaultElement.valueNumber = maxElement.valueNumber;
+ SetDefaultZoom();
+ }
+ defaultElement.max = maxElement.valueNumber;
+}
+
+/**
+ * Set default zoom.
+ */
+function SetDefaultZoom()
+{
+ let defaultElement = document.getElementById("defaultZoom");
+
+ if (defaultElement.valueNumber == 100) {
+ Services.contentPrefs2.removeGlobal("browser.content.full-zoom", null);
+ return;
+ }
+
+ let new_value = defaultElement.valueNumber / 100.;
+ Services.contentPrefs2.setGlobal("browser.content.full-zoom", new_value,
+ null);
+}
+
+/**
+ * When the user toggles the layers.acceleration.disabled pref,
+ * sync its new value to the gfx.direct2d.disabled pref too.
+ */
+function updateHardwareAcceleration(aVal)
+{
+ if (AppConstants.platform == "win") {
+ document.getElementById("gfx.direct2d.disabled").value = aVal;
+ }
+}
diff --git a/comm/suite/components/pref/content/pref-content.xul b/comm/suite/components/pref/content/pref-content.xul
new file mode 100644
index 0000000000..513027c03c
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-content.xul
@@ -0,0 +1,131 @@
+<?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 % prefContentDTD SYSTEM "chrome://communicator/locale/pref/pref-content.dtd"> %prefContentDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="content_pane"
+ label="&pref.content.title;"
+ script="chrome://communicator/content/pref/pref-content.js">
+
+ <preferences id="content_preferences">
+ <preference id="general.autoScroll"
+ name="general.autoScroll"
+ type="bool"/>
+ <preference id="general.smoothScroll"
+ name="general.smoothScroll"
+ type="bool"/>
+ <preference id="zoom.minPercent"
+ name="zoom.minPercent"
+ type="int"/>
+ <preference id="zoom.maxPercent"
+ name="zoom.maxPercent"
+ type="int"/>
+ <preference id="browser.zoom.full"
+ name="browser.zoom.full"
+ type="bool" inverted="true"/>
+ <preference id="browser.zoom.siteSpecific"
+ name="browser.zoom.siteSpecific"
+ type="bool"/>
+ <preference id="browser.zoom.showZoomStatusPanel"
+ name="browser.zoom.showZoomStatusPanel"
+ type="bool"/>
+ <preference id="browser.enable_automatic_image_resizing"
+ name="browser.enable_automatic_image_resizing"
+ type="bool"/>
+ <preference id="gfx.direct2d.disabled"
+ name="gfx.direct2d.disabled"
+ type="bool" inverted="true"/>
+ <preference id="layers.acceleration.disabled"
+ name="layers.acceleration.disabled"
+ type="bool" inverted="true"
+ onchange="updateHardwareAcceleration(this.value);"/>
+ </preferences>
+
+ <description>&pref.content.description;</description>
+
+ <groupbox id="scrollingGroup" align="start">
+ <caption label="&scrolling.label;"/>
+
+ <checkbox id="useAutoScroll"
+ label="&useAutoScroll.label;"
+ accesskey="&useAutoScroll.accesskey;"
+ preference="general.autoScroll"/>
+ <checkbox id="useSmoothScroll"
+ label="&useSmoothScroll.label;"
+ accesskey="&useSmoothScroll.accesskey;"
+ preference="general.smoothScroll"/>
+ </groupbox>
+
+ <groupbox id="zoomPreferences" align="start">
+ <caption label="&zoomPrefs.label;"/>
+
+ <hbox align="center">
+ <label value="&minZoom.label;"
+ accesskey="&minZoom.accesskey;"
+ control="minZoom"/>
+ <textbox id="minZoom"
+ type="number"
+ size="3"
+ increment="10"
+ preference="zoom.minPercent"
+ oninput="DisableMinCheck(this);"
+ onchange="AdjustMaxZoom();"/>
+ <label value="&maxZoom.label;"
+ accesskey="&maxZoom.accesskey;"
+ control="maxZoom"/>
+ <textbox id="maxZoom"
+ type="number"
+ size="3"
+ increment="10"
+ preference="zoom.maxPercent"
+ oninput="DisableMinCheck(this);"
+ onchange="AdjustMinZoom();"/>
+ <label value="&percent.label;"/>
+ </hbox>
+
+ <checkbox id="textZoomOnly"
+ label="&textZoomOnly.label;"
+ accesskey="&textZoomOnly.accesskey;"
+ preference="browser.zoom.full"/>
+ <checkbox id="zoomSiteSpecific"
+ label="&siteSpecific.label;"
+ accesskey="&siteSpecific.accesskey;"
+ preference="browser.zoom.siteSpecific"/>
+ <checkbox id="showZoomStatusPanel"
+ label="&showZoomStatusPanel.label;"
+ accesskey="&showZoomStatusPanel.accesskey;"
+ preference="browser.zoom.showZoomStatusPanel"/>
+ <checkbox id="enableAutomaticImageResizing"
+ label="&enableAutomaticImageResizing.label;"
+ accesskey="&enableAutomaticImageResizing.accesskey;"
+ preference="browser.enable_automatic_image_resizing"/>
+
+ <hbox align="center">
+ <label value="&defaultZoom.label;"
+ accesskey="&defaultZoom.accesskey;"
+ control="defaultZoom"/>
+ <textbox id="defaultZoom"
+ type="number"
+ size="3"
+ increment="10"
+ onchange="SetDefaultZoom();"/>
+ <label value="&percent.label;"/>
+ </hbox>
+ </groupbox>
+
+ <vbox class="box-padded" align="start">
+ <checkbox id="allowHWAccel"
+ label="&allowHWAccel.label;"
+ accesskey="&allowHWAccel.accesskey;"
+ preference="layers.acceleration.disabled"/>
+ </vbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-cookies.js b/comm/suite/components/pref/content/pref-cookies.js
new file mode 100644
index 0000000000..fd51881735
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-cookies.js
@@ -0,0 +1,34 @@
+/* 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/. */
+
+function Startup()
+{
+ SetDisables(false);
+}
+
+function SetDisables(aSetFocus)
+{
+ // Policy 1 was "ask before accepting" and is no longer valid.
+
+ // const for Cookie Accept Policy
+ const kCookiesDisabled = 2;
+ // const for Cookie Lifetime Policy
+ const kAcceptForNDays = 3;
+
+ var behavior = document.getElementById("networkCookieBehavior");
+ var behaviorPref = document.getElementById(behavior.getAttribute("preference"));
+
+ var lifetime = document.getElementById("networkCookieLifetime");
+ var lifetimePref = document.getElementById(lifetime.getAttribute("preference"));
+ var days = document.getElementById("lifetimeDays");
+ var daysPref = document.getElementById(days.getAttribute("preference"));
+
+ var cookiesDisabled = (behaviorPref.value == kCookiesDisabled);
+ lifetime.disabled = cookiesDisabled || lifetimePref.locked;
+ days.disabled = cookiesDisabled || daysPref.locked ||
+ (lifetimePref.value != kAcceptForNDays);
+
+ if (!days.disabled && aSetFocus)
+ days.focus();
+}
diff --git a/comm/suite/components/pref/content/pref-cookies.xul b/comm/suite/components/pref/content/pref-cookies.xul
new file mode 100644
index 0000000000..c41be21dc7
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-cookies.xul
@@ -0,0 +1,89 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-cookies.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="cookies_pane"
+ label="&pref.cookies.title;"
+ script="chrome://communicator/content/pref/pref-cookies.js">
+ <preferences id="cookies_preferences">
+ <preference id="network.cookie.cookieBehavior"
+ name="network.cookie.cookieBehavior"
+ type="int"
+ onchange="SetDisables(false);"/>
+ <preference id="network.cookie.lifetimePolicy"
+ name="network.cookie.lifetimePolicy"
+ type="int"
+ onchange="SetDisables(this.value == '3');"/>
+ <preference id="network.cookie.lifetime.days"
+ name="network.cookie.lifetime.days"
+ type="int"/>
+ <preference id="pref.advanced.cookies.disable_button.view_cookies"
+ name="pref.advanced.cookies.disable_button.view_cookies"
+ type="bool"/>
+ </preferences>
+
+ <groupbox id="networkCookieAcceptPolicy">
+ <caption label="&cookiePolicy.label;"/>
+ <radiogroup id="networkCookieBehavior"
+ preference="network.cookie.cookieBehavior">
+ <radio value="2"
+ label="&disableCookies.label;"
+ accesskey="&disableCookies.accesskey;"/>
+ <radio value="1"
+ label="&accNo3rdPartyCookies.label;"
+ accesskey="&accNo3rdPartyCookies.accesskey;"/>
+ <radio value="3"
+ label="&acc3rdPartyVisited.label;"
+ accesskey="&acc3rdPartyVisited.accesskey;"/>
+ <radio value="0"
+ label="&accAllCookies.label;"
+ accesskey="&accAllCookies.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+ <groupbox id="networkCookieLifetimePolicy">
+ <caption label="&cookieRetentionPolicy.label;"/>
+ <radiogroup id="networkCookieLifetime"
+ preference="network.cookie.lifetimePolicy">
+ <radio value="0"
+ label="&acceptNormally.label;"
+ accesskey="&acceptNormally.accesskey;"/>
+ <radio value="2"
+ label="&acceptForSession.label;"
+ accesskey="&acceptForSession.accesskey;"/>
+ <hbox align="center">
+ <radio id="acceptForNDays"
+ value="3"
+ label="&acceptforNDays.label;"
+ accesskey="&acceptforNDays.accesskey;"
+ aria-labelledby="acceptForNDays lifetimeDays daysLabel"/>
+ <textbox id="lifetimeDays"
+ type="number"
+ max="999"
+ min="0"
+ size="3"
+ maxlength="3"
+ preference="network.cookie.lifetime.days"
+ aria-labelledby="acceptForNDays lifetimeDays daysLabel"/>
+ <label id="daysLabel"
+ value="&days.label;"/>
+ </hbox>
+ </radiogroup>
+ </groupbox>
+ <groupbox id="manageCookiesAndSites">
+ <caption label="&manageCookies.label;"/>
+ <description>&manageCookiesDescription.label;</description>
+ <hbox pack="end">
+ <button id="viewCookieButton"
+ label="&viewCookies.label;"
+ accesskey="&viewCookies.accesskey;"
+ preference="pref.advanced.cookies.disable_button.view_cookies"
+ oncommand="toDataManager('|cookies');"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-debugging.js b/comm/suite/components/pref/content/pref-debugging.js
new file mode 100644
index 0000000000..269a0afeac
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-debugging.js
@@ -0,0 +1,15 @@
+/* -*- 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/. */
+
+function Startup() {
+ let paintFlashing = document.getElementById("nglayout.debug.paint_flashing");
+ enableFlashingChrome(paintFlashing.value);
+}
+
+function enableFlashingChrome(aValue) {
+ var paintFlashingChrome = document.getElementById("nglayoutDebugPaintFlashingChrome");
+
+ paintFlashingChrome.disabled = aValue;
+}
diff --git a/comm/suite/components/pref/content/pref-debugging.xul b/comm/suite/components/pref/content/pref-debugging.xul
new file mode 100644
index 0000000000..8e42f6f756
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-debugging.xul
@@ -0,0 +1,120 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-debugging.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="debugging_pane"
+ label="&pref.debugging.title;"
+ script="chrome://communicator/content/pref/pref-debugging.js">
+
+ <preferences id="debugging_preferences">
+ <preference id="nglayout.debug.paint_flashing"
+ name="nglayout.debug.paint_flashing"
+ type="bool"
+ onchange="enableFlashingChrome(this.value);"/>
+ <preference id="nglayout.debug.paint_flashing_chrome"
+ name="nglayout.debug.paint_flashing_chrome"
+ type="bool"/>
+ <preference id="nglayout.debug.paint_dumping"
+ name="nglayout.debug.paint_dumping"
+ type="bool"/>
+ <preference id="nglayout.debug.invalidate_dumping"
+ name="nglayout.debug.invalidate_dumping"
+ type="bool"/>
+ <preference id="nglayout.debug.event_dumping"
+ name="nglayout.debug.event_dumping"
+ type="bool"/>
+ <preference id="nglayout.debug.motion_event_dumping"
+ name="nglayout.debug.motion_event_dumping"
+ type="bool"/>
+ <preference id="nglayout.debug.crossing_event_dumping"
+ name="nglayout.debug.crossing_event_dumping"
+ type="bool"/>
+ <preference id="layout.reflow.showframecounts"
+ name="layout.reflow.showframecounts"
+ type="bool"/>
+ <preference id="layout.reflow.dumpframecounts"
+ name="layout.reflow.dumpframecounts"
+ type="bool"/>
+ <preference id="layout.reflow.dumpframebyframecounts"
+ name="layout.reflow.dumpframebyframecounts"
+ type="bool"/>
+ <preference id="xul.debug.box"
+ name="xul.debug.box"
+ type="bool"/>
+ <preference id="nglayout.debug.disable_xul_cache"
+ name="nglayout.debug.disable_xul_cache"
+ type="bool"/>
+ </preferences>
+
+ <hbox>
+ <!-- Event Debugging -->
+ <groupbox id="eventDebugging" align="start" flex="1">
+ <caption label="&debugEvents.label;"/>
+ <checkbox id="nglayoutDebugPaintFlashing"
+ label="&debugPaintFlashing.label;"
+ accesskey="&debugPaintFlashing.accesskey;"
+ preference="nglayout.debug.paint_flashing"/>
+ <checkbox id="nglayoutDebugPaintFlashingChrome"
+ label="&debugPaintFlashingChrome.label;"
+ accesskey="&debugPaintFlashingChrome.accesskey;"
+ preference="nglayout.debug.paint_flashing_chrome"/>
+ <checkbox id="nglayoutDebugPaintDumping"
+ label="&debugPaintDumping.label;"
+ accesskey="&debugPaintDumping.accesskey;"
+ preference="nglayout.debug.paint_dumping"/>
+ <checkbox id="nglayoutDebugInvalidateDumping"
+ label="&debugInvalidateDumping.label;"
+ accesskey="&debugInvalidateDumping.accesskey;"
+ preference="nglayout.debug.invalidate_dumping"/>
+ <checkbox id="nglayoutDebugEventDumping"
+ label="&debugEventDumping.label;"
+ accesskey="&debugEventDumping.accesskey;"
+ preference="nglayout.debug.event_dumping"/>
+ <checkbox id="nglayoutDebugMotionEventDumping"
+ label="&debugMotionEventDumping.label;"
+ accesskey="&debugMotionEventDumping.accesskey;"
+ preference="nglayout.debug.motion_event_dumping"/>
+ <checkbox id="nglayoutDebugCrossingEventDumping"
+ label="&debugCrossingEventDumping.label;"
+ accesskey="&debugCrossingEventDumping.accesskey;"
+ preference="nglayout.debug.crossing_event_dumping"/>
+ </groupbox>
+
+ <vbox align="start" flex="1">
+ <!-- Reflow Event Debugging -->
+ <groupbox id="reflowEventDebugging">
+ <caption label="&debugReflowEvents.label;"/>
+ <checkbox id="layoutReflowShowFrameCounts"
+ label="&debugReflowShowFrameCounts.label;"
+ accesskey="&debugReflowShowFrameCounts.accesskey;"
+ preference="layout.reflow.showframecounts"/>
+ <checkbox id="layoutReflowDumpFrameCounts"
+ label="&debugReflowDumpFrameCounts.label;"
+ accesskey="&debugReflowDumpFrameCounts.accesskey;"
+ preference="layout.reflow.dumpframecounts"/>
+ <checkbox id="layoutReflowDumpFrameByFrameCounts"
+ label="&debugReflowDumpFrameByFrameCounts.label;"
+ accesskey="&debugReflowDumpFrameByFrameCounts.accesskey;"
+ preference="layout.reflow.dumpframebyframecounts"/>
+ </groupbox>
+
+ <!-- Render Debugging -->
+ <groupbox id="renderDebugging">
+ <caption label="&debugRendering.label;"/>
+ <checkbox id="debugXULBoxes"
+ label="&debugXULBox.label;"
+ accesskey="&debugXULBox.accesskey;"
+ preference="xul.debug.box"/>
+ <checkbox id="nglayoutDebugDisableXULCache"
+ label="&debugDisableXULCache.label;"
+ accesskey="&debugDisableXULCache.accesskey;"
+ preference="nglayout.debug.disable_xul_cache"/>
+ </groupbox>
+ </vbox>
+ </hbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-download.js b/comm/suite/components/pref/content/pref-download.js
new file mode 100644
index 0000000000..bc52f800b6
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-download.js
@@ -0,0 +1,197 @@
+/* -*- 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/. */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { FileUtils } =
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+
+ChromeUtils.defineModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+
+const kDesktop = 0;
+const kDownloads = 1;
+const kUserDir = 2;
+var gFPHandler;
+var gSoundUrlPref;
+
+function Startup()
+{
+ // Define globals
+ gFPHandler = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ gSoundUrlPref = document.getElementById("browser.download.finished_sound_url");
+ setSoundEnabled(document.getElementById("browser.download.finished_download_sound").value);
+}
+
+/**
+ * Enables/disables the folder field and Browse button based on whether a
+ * default download directory is being used.
+ */
+function readUseDownloadDir()
+{
+ 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;
+}
+
+/**
+ * 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.
+ */
+function chooseFolder()
+{
+ return chooseFolderTask().catch(Cu.reportError);
+}
+
+async function chooseFolderTask()
+{
+ let title = document.getElementById("bundle_prefutilities")
+ .getString("downloadfolder");
+ let folderListPref = document.getElementById("browser.download.folderList");
+ let currentDirPref = await _indexToFolder(folderListPref.value);
+ let defDownloads = await _indexToFolder(kDownloads);
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+
+ fp.init(window, title, Ci.nsIFilePicker.modeGetFolder);
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+ // First try to open what's currently configured
+ if (currentDirPref && currentDirPref.exists()) {
+ fp.displayDirectory = currentDirPref;
+ } else if (defDownloads && defDownloads.exists()) {
+ // Try the system's download dir
+ fp.displayDirectory = defDownloads;
+ } else {
+ // Fall back to Desktop
+ fp.displayDirectory = await _indexToFolder(kDesktop);
+ }
+
+ let result = await new Promise(resolve => fp.open(resolve));
+ if (result != Ci.nsIFilePicker.returnOK) {
+ return;
+ }
+
+ document.getElementById("browser.download.dir").value = fp.file;
+ folderListPref.value = await _folderToIndex(fp.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.
+}
+
+/**
+ * Initializes the download folder display settings based on the user's
+ * preferences.
+ */
+function displayDownloadDirPref()
+{
+ displayDownloadDirPrefTask().catch(Cu.reportError);
+}
+
+async function displayDownloadDirPrefTask()
+{
+ var folderListPref = document.getElementById("browser.download.folderList");
+ var currentDirPref = await _indexToFolder(folderListPref.value); // file
+ var prefutilitiesBundle = document.getElementById("bundle_prefutilities");
+ var iconUrlSpec = gFPHandler.getURLSpecFromFile(currentDirPref);
+ var downloadFolder = document.getElementById("downloadFolder");
+ downloadFolder.image = "moz-icon://" + iconUrlSpec + "?size=16";
+
+ // Display a 'pretty' label or the path in the UI.
+ switch (folderListPref.value) {
+ case kDesktop:
+ downloadFolder.label = prefutilitiesBundle.getString("desktopFolderName");
+ break;
+ case kDownloads:
+ downloadFolder.label = prefutilitiesBundle.getString("downloadsFolderName");
+ break;
+ default:
+ downloadFolder.label = currentDirPref ? currentDirPref.path : "";
+ break;
+ }
+}
+
+/**
+ * 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"
+ */
+async function _getDownloadsFolder(aFolder)
+{
+ switch (aFolder) {
+ case "Desktop":
+ return Services.dirsvc.get("Desk", Ci.nsIFile);
+ case "Downloads":
+ let downloadsDir = await Downloads.getSystemDownloadsDirectory();
+ return new FileUtils.File(downloadsDir);
+ }
+ 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
+ * kDesktop if aFolder is the Desktop or is unspecified,
+ * kDownloads if aFolder is the Downloads folder,
+ * kUserDir otherwise
+ */
+async function _folderToIndex(aFolder)
+{
+ if (!aFolder || aFolder.equals(await _getDownloadsFolder("Desktop"))) {
+ return kDesktop;
+ }
+
+ if (aFolder.equals(await _getDownloadsFolder("Downloads"))) {
+ return kDownloads;
+ }
+
+ return kUserDir;
+ }
+
+/**
+ * Converts an integer into the corresponding folder.
+ *
+ * @param aIndex
+ * an integer
+ * @returns the Desktop folder if aIndex == kDesktop,
+ * the Downloads folder if aIndex == kDownloads,
+ * the folder stored in browser.download.dir
+ */
+async function _indexToFolder(aIndex)
+{
+ var folder;
+ switch (aIndex) {
+ case kDownloads:
+ folder = await _getDownloadsFolder("Downloads");
+ break;
+ case kDesktop:
+ folder = await _getDownloadsFolder("Desktop");
+ break;
+ default:
+ folder = document.getElementById("browser.download.dir").value;
+ break;
+ }
+ if (!folder ||
+ !folder.exists()) {
+ return "";
+ }
+
+ return folder;
+}
+
+function setSoundEnabled(aEnable)
+{
+ EnableElementById("downloadSndURL", aEnable, false);
+ document.getElementById("downloadSndPlay").disabled = !aEnable;
+}
diff --git a/comm/suite/components/pref/content/pref-download.xul b/comm/suite/components/pref/content/pref-download.xul
new file mode 100644
index 0000000000..e3f626204c
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-download.xul
@@ -0,0 +1,120 @@
+<?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 % prefDownloadDTD SYSTEM "chrome://communicator/locale/pref/pref-download.dtd">
+%prefDownloadDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="download_pane"
+ label="&pref.download.title;"
+ script="chrome://communicator/content/pref/pref-download.js">
+
+ <preferences>
+ <preference id="browser.download.manager.behavior"
+ name="browser.download.manager.behavior"
+ type="int"/>
+ <preference id="browser.download.manager.focusWhenStarting"
+ name="browser.download.manager.focusWhenStarting"
+ type="bool" inverted="true"/>
+ <preference id="browser.download.useDownloadDir"
+ name="browser.download.useDownloadDir"
+ type="bool"/>
+ <preference id="browser.download.dir"
+ name="browser.download.dir"
+ type="file"/>
+ <preference id="browser.download.folderList"
+ name="browser.download.folderList"
+ type="int"
+ onchange="displayDownloadDirPref();"/>
+ <preference id="browser.download.finished_download_sound"
+ name="browser.download.finished_download_sound"
+ type="bool"
+ onchange="setSoundEnabled(this.value);"/>
+ <preference id="browser.download.finished_sound_url"
+ name="browser.download.finished_sound_url"
+ type="string"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&downloadBehavior.label;"/>
+ <radiogroup id="downloadBehavior"
+ preference="browser.download.manager.behavior">
+ <radio value="2"
+ label="&doNothing.label;"
+ accesskey="&doNothing.accesskey;"/>
+ <radio value="1"
+ label="&openProgressDialog.label;"
+ accesskey="&openProgressDialog.accesskey;"/>
+ <radio value="0"
+ label="&openDM.label;"
+ accesskey="&openDM.accesskey;"/>
+ </radiogroup>
+ <checkbox id="focusWhenStarting"
+ class="indent"
+ preference="browser.download.manager.focusWhenStarting"
+ label="&flashWhenOpen.label;"
+ accesskey="&flashWhenOpen.accesskey;"/>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&downloadLocation.label;"/>
+ <radiogroup id="saveWhere"
+ preference="browser.download.useDownloadDir"
+ onsyncfrompreference="return document.getElementById('download_pane').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.dir"
+ preference-editable="true"
+ aria-labelledby="saveTo"
+ onsyncfrompreference="document.getElementById('download_pane').displayDownloadDirPref();"/>
+ <button id="chooseFolder" oncommand="chooseFolder();"
+ label="&chooseDownloadFolder.label;"
+ accesskey="&chooseDownloadFolder.accesskey;"/>
+ </hbox>
+ <radio id="alwaysAsk" value="false"
+ label="&alwaysAsk.label;"
+ accesskey="&alwaysAsk.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&finishedBehavior.label;"/>
+ <hbox align="center">
+ <checkbox id="finishedNotificationSound"
+ label="&playSound.label;"
+ preference="browser.download.finished_download_sound"
+ accesskey="&playSound.accesskey;"/>
+ </hbox>
+
+ <hbox align="center" class="indent">
+ <filefield id="downloadSndURL"
+ flex="1"
+ preference="browser.download.finished_sound_url"
+ preference-editable="true"
+ onsyncfrompreference="return WriteSoundField(this, document.getElementById('download_pane').gSoundUrlPref.value);"/>
+ <hbox align="center">
+ <button id="downloadSndBrowse"
+ label="&browse.label;"
+ accesskey="&browse.accesskey;"
+ oncommand="SelectSound(gSoundUrlPref);">
+ <observes element="downloadSndURL" attribute="disabled"/>
+ </button>
+ <button id="downloadSndPlay"
+ label="&playButton.label;"
+ accesskey="&playButton.accesskey;"
+ oncommand="PlaySound(gSoundUrlPref.value, false);"/>
+ </hbox>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-findasyoutype.js b/comm/suite/components/pref/content/pref-findasyoutype.js
new file mode 100644
index 0000000000..fefd5a0d46
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-findasyoutype.js
@@ -0,0 +1,15 @@
+/* -*- 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/. */
+
+function Startup()
+{
+ var prefAutostart = document.getElementById("accessibility.typeaheadfind.autostart");
+ SetLinksOnlyEnabled(prefAutostart.value);
+}
+
+function SetLinksOnlyEnabled(aEnable)
+{
+ EnableElementById("findAsYouTypeAutoWhat", aEnable, false);
+}
diff --git a/comm/suite/components/pref/content/pref-findasyoutype.xul b/comm/suite/components/pref/content/pref-findasyoutype.xul
new file mode 100644
index 0000000000..612b3fa768
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-findasyoutype.xul
@@ -0,0 +1,70 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-findasyoutype.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="findasyoutype_pane"
+ label="&pref.findAsYouType.title;"
+ script="chrome://communicator/content/pref/pref-findasyoutype.js">
+
+ <preferences id="findasyoutype_preferences">
+ <preference id="accessibility.typeaheadfind.autostart"
+ name="accessibility.typeaheadfind.autostart"
+ onchange="SetLinksOnlyEnabled(this.value);"
+ type="bool"/>
+ <preference id="accessibility.typeaheadfind.linksonly"
+ name="accessibility.typeaheadfind.linksonly"
+ type="bool"/>
+ <preference id="accessibility.typeaheadfind.enablesound"
+ name="accessibility.typeaheadfind.enablesound"
+ type="bool"/>
+ <preference id="accessibility.typeaheadfind.enabletimeout"
+ name="accessibility.typeaheadfind.enabletimeout"
+ type="bool"/>
+ <preference id="accessibility.typeaheadfind.usefindbar"
+ name="accessibility.typeaheadfind.usefindbar"
+ type="bool"/>
+ </preferences>
+
+ <groupbox align="start">
+ <caption label="&findAsYouTypeBehavior.label;"/>
+ <checkbox id="findAsYouTypeEnableAuto"
+ label="&findAsYouTypeEnableAuto.label;"
+ accesskey="&findAsYouTypeEnableAuto.accesskey;"
+ preference="accessibility.typeaheadfind.autostart"/>
+ <radiogroup id="findAsYouTypeAutoWhat"
+ class="indent"
+ preference="accessibility.typeaheadfind.linksonly">
+ <radio value="false"
+ label="&findAsYouTypeAutoText.label;"
+ accesskey="&findAsYouTypeAutoText.accesskey;"/>
+ <radio value="true"
+ label="&findAsYouTypeAutoLinks.label;"
+ accesskey="&findAsYouTypeAutoLinks.accesskey;"/>
+ </radiogroup>
+ <description>&findAsYouTypeTip.label;</description>
+
+ <vbox class="box-padded"
+ align="start">
+ <separator class="thin" />
+ <checkbox id="findAsYouTypeSound"
+ label="&findAsYouTypeSound.label;"
+ accesskey="&findAsYouTypeSound.accesskey;"
+ preference="accessibility.typeaheadfind.enablesound"/>
+ <checkbox id="findAsYouTypeTimeout"
+ label="&findAsYouTypeTimeout.label;"
+ accesskey="&findAsYouTypeTimeout.accesskey;"
+ preference="accessibility.typeaheadfind.enabletimeout"/>
+ <checkbox id="findAsYouTypeFindbarEnable"
+ label="&findAsYouTypeFindbarEnable.label;"
+ accesskey="&findAsYouTypeFindbarEnable.accesskey;"
+ preference="accessibility.typeaheadfind.usefindbar"/>
+ </vbox>
+ <description>&findAsYouTypeFindbarEnableTip.label;</description>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-fonts.js b/comm/suite/components/pref/content/pref-fonts.js
new file mode 100644
index 0000000000..aa226c89af
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-fonts.js
@@ -0,0 +1,220 @@
+/* -*- 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/. */
+
+var gAllFonts = null;
+var gFontEnumerator = null;
+var gDisabled = false;
+
+function GetFontEnumerator()
+{
+ if (!gFontEnumerator)
+ {
+ gFontEnumerator = Cc["@mozilla.org/gfx/fontenumerator;1"]
+ .createInstance(Ci.nsIFontEnumerator);
+ }
+ return gFontEnumerator;
+}
+
+function BuildFontList(aLanguage, aFontType, aMenuList, aPreference)
+{
+ var defaultFont = null;
+ // Load Font Lists
+ var fonts = GetFontEnumerator().EnumerateFonts(aLanguage, aFontType);
+ if (fonts.length)
+ {
+ defaultFont = GetFontEnumerator().getDefaultFont(aLanguage, aFontType);
+ }
+ else
+ {
+ fonts = GetFontEnumerator().EnumerateFonts(aLanguage, "");
+ if (fonts.length)
+ defaultFont = GetFontEnumerator().getDefaultFont(aLanguage, "");
+ }
+
+ if (!gAllFonts)
+ gAllFonts = GetFontEnumerator().EnumerateAllFonts();
+
+ // Reset the list
+ while (aMenuList.hasChildNodes())
+ aMenuList.lastChild.remove();
+
+ // Build the UI for the Default Font and Fonts for this CSS type.
+ var popup = document.createElement("menupopup");
+ var separator;
+ if (fonts.length > 0)
+ {
+ const prefutilitiesBundle = document.getElementById("bundle_prefutilities");
+ let label = defaultFont ?
+ prefutilitiesBundle.getFormattedString("labelDefaultFont2", [defaultFont]) :
+ prefutilitiesBundle.getString("labelDefaultFontUnnamed");
+ let menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", label);
+ menuitem.setAttribute("value", ""); // Default Font has a blank value
+ popup.appendChild(menuitem);
+
+ separator = document.createElement("menuseparator");
+ popup.appendChild(separator);
+
+ for (let font of fonts)
+ {
+ menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("value", font);
+ menuitem.setAttribute("label", font);
+ popup.appendChild(menuitem);
+ }
+ }
+
+ // Build the UI for the remaining fonts.
+ if (gAllFonts.length > fonts.length)
+ {
+ // Both lists are sorted, and the Fonts-By-Type list is a subset of the
+ // All-Fonts list, so walk both lists side-by-side, skipping values we've
+ // already created menu items for.
+
+ if (fonts.length)
+ {
+ separator = document.createElement("menuseparator");
+ popup.appendChild(separator);
+ }
+
+ for (i = 0; i < gAllFonts.length; ++i)
+ {
+ if (fonts.lastIndexOf(gAllFonts[i], 0) == 0)
+ {
+ fonts.shift(); //Remove matched font from array
+ }
+ else
+ {
+ menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("value", gAllFonts[i]);
+ menuitem.setAttribute("label", gAllFonts[i]);
+ popup.appendChild(menuitem);
+ }
+ }
+ }
+ aMenuList.appendChild(popup);
+
+ // Fully populated so re-enable menulist before setting preference,
+ // unless panel is locked.
+ if (!gDisabled)
+ aMenuList.disabled = false;
+ aMenuList.setAttribute("preference", aPreference.id);
+ aPreference.setElementValue(aMenuList);
+}
+
+function ReadFontLanguageGroup()
+{
+ var prefs = [{format: "default", type: "string", element: "defaultFontType", fonttype: "" },
+ {format: "name.", type: "unichar", element: "serif", fonttype: "serif" },
+ {format: "name.", type: "unichar", element: "sans-serif", fonttype: "sans-serif"},
+ {format: "name.", type: "unichar", element: "monospace", fonttype: "monospace" },
+ {format: "name.", type: "unichar", element: "cursive", fonttype: "cursive" },
+ {format: "name.", type: "unichar", element: "fantasy", fonttype: "fantasy" },
+ {format: "name-list.", type: "unichar", element: null, fonttype: "serif" },
+ {format: "name-list.", type: "unichar", element: null, fonttype: "sans-serif"},
+ {format: "name-list.", type: "unichar", element: null, fonttype: "monospace" },
+ {format: "name-list.", type: "unichar", element: null, fonttype: "cursive" },
+ {format: "name-list.", type: "unichar", element: null, fonttype: "fantasy" },
+ {format: "size.variable", type: "int", element: "sizeVar", fonttype: "" },
+ {format: "size.fixed", type: "int", element: "sizeMono", fonttype: "" },
+ {format: "minimum-size", type: "int", element: "minSize", fonttype: "" }];
+ gDisabled = document.getElementById("browser.display.languageList").locked;
+ var fontLanguage = document.getElementById("font.language.group");
+ if (gDisabled)
+ fontLanguage.disabled = true;
+ var languageGroup = fontLanguage.value;
+ var preferences = document.getElementById("fonts_preferences");
+ for (var i = 0; i < prefs.length; ++i)
+ {
+ var name = "font."+ prefs[i].format + prefs[i].fonttype + "." + languageGroup;
+ var preference = document.getElementById(name);
+ if (!preference)
+ {
+ preference = document.createElement("preference");
+ 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)
+ {
+ if (prefs[i].fonttype)
+ {
+ // Set an empty label so it does not jump when items are added.
+ element.setAttribute("label", "");
+ // Disable menulist for the moment.
+ element.disabled = true;
+ // Lazily populate font lists, each gets re-enabled at the end.
+ window.setTimeout(BuildFontList, 0, languageGroup,
+ prefs[i].fonttype, element, preference);
+ }
+ else
+ {
+ // Unless the panel is locked, make sure these elements are not
+ // disabled just in case they were in the last language group.
+ element.disabled = gDisabled;
+ element.setAttribute("preference", preference.id);
+ preference.setElementValue(element);
+ }
+ }
+ }
+}
+
+function ReadFontSelection(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)
+ 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(",");
+
+ for (var i = 0; i < fontNames.length; ++i)
+ {
+ fontItems = aElement.getElementsByAttribute("value", fontNames[i].trim());
+ if (fontItems.length)
+ return fontItems[0].getAttribute("value");
+ }
+ return defaultValue;
+}
+
+function ReadFontPref(aElement, aDefaultValue)
+{
+ // Check to see if preference value exists,
+ // if not return given default value.
+ var preference = document.getElementById(aElement.getAttribute("preference"));
+ return preference.value || aDefaultValue;
+}
+
+function ReadUseDocumentFonts()
+{
+ var preference = document.getElementById("browser.display.use_document_fonts");
+ return preference.value == 1;
+}
+
+function WriteUseDocumentFonts(aUseDocumentFonts)
+{
+ return aUseDocumentFonts.checked ? 1 : 0;
+}
diff --git a/comm/suite/components/pref/content/pref-fonts.xul b/comm/suite/components/pref/content/pref-fonts.xul
new file mode 100644
index 0000000000..554f161a73
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-fonts.xul
@@ -0,0 +1,260 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-fonts.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="fonts_pane"
+ label="&pref.fonts.title;"
+ script="chrome://communicator/content/pref/pref-fonts.js">
+ <preferences id="fonts_preferences">
+ <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="browser.display.languageList"
+ name="browser.display.languageList"
+ type="wstring"/>
+ </preferences>
+
+ <groupbox>
+ <caption align="center">
+ <label value="&language.label;"
+ accesskey="&language.accesskey;"
+ control="selectLangs"/>
+ <menulist id="selectLangs" preference="font.language.group"
+ onsyncfrompreference="document.getElementById('fonts_pane').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-TW" label="&font.langGroup.trad-chinese;"/>
+ <menuitem value="zh-HK" label="&font.langGroup.trad-chinese-hk;"/>
+ <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-math" label="&font.langGroup.math;"/>
+ <menuitem value="x-orya" label="&font.langGroup.odia;"/>
+ <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>
+
+ <separator class="thin"/>
+
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ <column/>
+ </columns>
+
+ <rows>
+ <row align="center">
+ <spacer/>
+ <label value="&typefaces.label;"/>
+ <label value="&sizes.label;"/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&proportional.label;"
+ accesskey="&proportional.accesskey;"
+ control="defaultFontType"/>
+ </hbox>
+ <menulist id="defaultFontType" flex="1" style="width: 0px;"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontSelection(this);">
+ <menupopup>
+ <menuitem value="serif"
+ label="&useDefaultFontSerif.label;"/>
+ <menuitem value="sans-serif"
+ label="&useDefaultFontSansSerif.label;"/>
+ </menupopup>
+ </menulist>
+ <menulist id="sizeVar" class="small-margin"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontPref(this, 16);">
+ <menupopup>
+ <menuitem value="8" label="8"/>
+ <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>
+ <separator class="thin"/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&serif.label;"
+ accesskey="&serif.accesskey;"
+ control="serif"/>
+ </hbox>
+ <menulist id="serif" class="prefpanel-font-list"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontSelection(this);"/>
+ <spacer/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&sans-serif.label;"
+ accesskey="&sans-serif.accesskey;"
+ control="sans-serif"/>
+ </hbox>
+ <menulist id="sans-serif" class="prefpanel-font-list"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontSelection(this);"/>
+ <spacer/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&cursive.label;"
+ accesskey="&cursive.accesskey;"
+ control="cursive"/>
+ </hbox>
+ <menulist id="cursive" class="prefpanel-font-list"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontSelection(this);"/>
+ <spacer/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&fantasy.label;"
+ accesskey="&fantasy.accesskey;"
+ control="fantasy"/>
+ </hbox>
+ <menulist id="fantasy" class="prefpanel-font-list"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontSelection(this);"/>
+ <spacer/>
+ </row>
+ <row>
+ <separator class="thin"/>
+ </row>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&monospace.label;"
+ accesskey="&monospace.accesskey;"
+ control="monospace"/>
+ </hbox>
+ <menulist id="monospace" class="prefpanel-font-list"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontSelection(this);"/>
+ <menulist id="sizeMono"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontPref(this, 12);">
+ <menupopup>
+ <menuitem value="8" label="8"/>
+ <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>
+ <separator class="thin"/>
+ </row>
+ <row>
+ <spacer/>
+ <hbox align="center" pack="end">
+ <label value="&minSize.label;"
+ accesskey="&minSize.accesskey;"
+ control="minSize"/>
+ </hbox>
+ <menulist id="minSize"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadFontPref(this, 0);">
+ <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"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <separator class="thin"/>
+
+ <!-- Unchecking this removes the ability to select dynamic fonts -->
+ <checkbox id="browserUseDocumentFonts"
+ label="&useDocumentFonts.label;"
+ accesskey="&useDocumentFonts.accesskey;"
+ preference="browser.display.use_document_fonts"
+ onsyncfrompreference="return document.getElementById('fonts_pane').ReadUseDocumentFonts();"
+ onsynctopreference="return document.getElementById('fonts_pane').WriteUseDocumentFonts(this);"/>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-history.js b/comm/suite/components/pref/content/pref-history.js
new file mode 100644
index 0000000000..8f22073241
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-history.js
@@ -0,0 +1,55 @@
+/* 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/. */
+
+function Startup()
+{
+ var urlbarHistButton = document.getElementById("ClearUrlBarHistoryButton");
+ var lastUrlPref = document.getElementById("general.open_location.last_url");
+ var locBarPref = document.getElementById("browser.urlbar.historyEnabled");
+
+ var isBtnDisabled = lastUrlPref.locked || !locBarPref.value;
+
+ try {
+ if (!isBtnDisabled && !lastUrlPref.hasUserValue) {
+ var file = GetUrlbarHistoryFile();
+ if (!file.exists())
+ isBtnDisabled = true;
+ else {
+ var connection = Services.storage.openDatabase(file);
+ isBtnDisabled = !connection.tableExists("urlbarhistory");
+ connection.close();
+ }
+ }
+ urlbarHistButton.disabled = isBtnDisabled;
+ }
+ catch(ex) {
+ }
+ var globalHistButton = document.getElementById("browserClearHistory");
+ var globalHistory = Cc["@mozilla.org/browser/nav-history-service;1"]
+ .getService(Ci.nsINavHistoryService);
+ if (!globalHistory.hasHistoryEntries)
+ globalHistButton.disabled = true;
+}
+
+function prefClearGlobalHistory()
+{
+ const {PlacesUtils} = ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
+ PlacesUtils.history.clear();
+}
+
+function prefClearUrlbarHistory(aButton)
+{
+ document.getElementById("general.open_location.last_url").valueFromPreferences = "";
+ var file = GetUrlbarHistoryFile();
+ if (file.exists())
+ file.remove(false);
+ aButton.disabled = true;
+}
+
+function prefUrlBarHistoryToggle(aChecked)
+{
+ var file = GetUrlbarHistoryFile();
+ if (file.exists())
+ document.getElementById("ClearUrlBarHistoryButton").disabled = !aChecked;
+}
diff --git a/comm/suite/components/pref/content/pref-history.xul b/comm/suite/components/pref/content/pref-history.xul
new file mode 100644
index 0000000000..63638544c5
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-history.xul
@@ -0,0 +1,99 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-history.dtd" >
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="history_pane"
+ label="&pref.history.title;"
+ script="chrome://communicator/content/pref/pref-history.js">
+ <preferences id="history_preferences">
+ <preference id="places.history.enabled"
+ name="places.history.enabled"
+ type="bool"/>
+ <preference id="pref.browser.history.disable_button.clear_hist"
+ name="pref.browser.history.disable_button.clear_hist"
+ type="bool"/>
+ <preference id="pref.browser.history.disable_button.clear_urlbar"
+ name="pref.browser.history.disable_button.clear_urlbar"
+ type="bool"/>
+ <preference id="browser.urlbar.historyEnabled"
+ name="browser.urlbar.historyEnabled"
+ type="bool"/>
+ <preference id="general.open_location.last_url"
+ name="general.open_location.last_url"
+ type="string"/>
+ <preference id="browser.formfill.enable"
+ name="browser.formfill.enable"
+ type="bool"/>
+ <preference id="browser.formfill.expire_days"
+ name="browser.formfill.expire_days"
+ type="int"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&pref.history.caption;"/>
+ <hbox align="center">
+ <description flex="1">&historyPages.label;</description>
+ <hbox align="center"
+ pack="end">
+ <button label="&clearHistory.label;"
+ accesskey="&clearHistory.accesskey;"
+ oncommand="prefClearGlobalHistory();"
+ id="browserClearHistory"
+ preference="pref.browser.history.disable_button.clear_hist"/>
+ </hbox>
+ </hbox>
+ <checkbox id="histEnable"
+ label="&enableHistory.label;"
+ accesskey="&enableHistory.accesskey;"
+ preference="places.history.enabled"/>
+ </groupbox>
+
+ <!-- no honey, I haven't been viewing porn, honest! -->
+ <groupbox>
+ <caption label="&locationBarHistory.caption;"/>
+ <hbox align="center">
+ <vbox pack="end">
+ <checkbox id="urlbarHistoryEnabled"
+ label="&urlBarHistoryEnabled.caption;"
+ accesskey="&urlBarHistoryEnabled.accesskey;"
+ preference="browser.urlbar.historyEnabled"
+ oncommand="prefUrlBarHistoryToggle(this.checked);"/>
+ <hbox align="center"
+ pack="end">
+ <description flex="1">&clearLocationBar.label;</description>
+ <button id="ClearUrlBarHistoryButton"
+ label="&clearLocationBarButton.label;"
+ accesskey="&clearLocationBarButton.accesskey;"
+ oncommand="prefClearUrlbarHistory(this); this.disabled = true;"
+ preference="pref.browser.history.disable_button.clear_urlbar"/>
+ </hbox>
+ </vbox>
+ </hbox>
+ </groupbox>
+
+ <!-- form history -->
+ <groupbox>
+ <caption label="&formfillHistory.caption;"/>
+ <checkbox id="formfillEnable"
+ label="&enableFormfill.label;"
+ accesskey="&enableFormfill.accesskey;"
+ preference="browser.formfill.enable"/>
+ <hbox align="center">
+ <label value="&formfillExpire.label;"
+ accesskey="&formfillExpire.accesskey;"
+ control="formfillDay"/>
+ <textbox id="formfillDay"
+ type="number"
+ size="4"
+ preference="browser.formfill.expire_days"/>
+ <label value="&formfillDays.label;"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-http.js b/comm/suite/components/pref/content/pref-http.js
new file mode 100644
index 0000000000..eb04b9f274
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-http.js
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+function Startup() {
+ let compatMode = document.getElementById("uaFirefoxCompat");
+ let modeFirefox =
+ document.getElementById("general.useragent.compatMode.firefox");
+ let modeStrict =
+ document.getElementById("general.useragent.compatMode.strict-firefox");
+
+ if (modeStrict.value)
+ compatMode.value = "strict";
+ else if (modeFirefox.value)
+ compatMode.value = "compat";
+ else
+ compatMode.value = "none";
+}
+
+function updateUAPrefs(aCompatMode) {
+ let modeFirefox =
+ document.getElementById("general.useragent.compatMode.firefox");
+ // The strict option will only work in builds compiled from a SeaMonkey
+ // release branch. Additional code needs to be added to the mozilla sources.
+ // See Bug 1242294 for the needed changes.
+ let modeStrict =
+ document.getElementById("general.useragent.compatMode.strict-firefox");
+ switch (aCompatMode.value) {
+ case "strict":
+ modeStrict.value = true;
+ modeFirefox.value = false;
+ break;
+ case "compat":
+ modeStrict.value = false;
+ modeFirefox.value = true;
+ break;
+ case "none":
+ modeStrict.value = false;
+ modeFirefox.value = false;
+ }
+}
diff --git a/comm/suite/components/pref/content/pref-http.xul b/comm/suite/components/pref/content/pref-http.xul
new file mode 100644
index 0000000000..bea6545418
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-http.xul
@@ -0,0 +1,82 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-http.dtd">
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="http_pane"
+ label="&pref.http.title;"
+ script="chrome://communicator/content/pref/pref-http.js">
+
+ <preferences>
+ <preference id="network.http.version"
+ name="network.http.version"
+ type="string"/>
+ <preference id="network.http.proxy.version"
+ name="network.http.proxy.version"
+ type="string"/>
+ <preference id="general.useragent.compatMode.firefox"
+ name="general.useragent.compatMode.firefox"
+ type="bool"/>
+ <preference id="general.useragent.compatMode.strict-firefox"
+ name="general.useragent.compatMode.strict-firefox"
+ type="bool"/>
+ </preferences>
+
+ <description>&prefPara;</description>
+
+ <hbox align="start">
+ <groupbox flex="1">
+ <caption label="&prefDirect.label;"/>
+ <vbox class="indent" align="start">
+ <radiogroup id="httpVersion"
+ preference="network.http.version">
+ <radio value="1.0"
+ label="&prefEnableHTTP10.label;"
+ accesskey="&prefEnableHTTP10.accesskey;"/>
+ <radio value="1.1"
+ label="&prefEnableHTTP11.label;"
+ accesskey="&prefEnableHTTP11.accesskey;"/>
+ </radiogroup>
+ </vbox>
+ </groupbox>
+
+ <groupbox flex="1">
+ <caption label="&prefProxy.label;"/>
+ <vbox class="indent" align="start">
+ <radiogroup id="httpVersionProxy"
+ preference="network.http.proxy.version">
+ <radio value="1.0"
+ label="&prefEnableHTTP10.label;"
+ accesskey="&prefEnableHTTP10Proxy.accesskey;"/>
+ <radio value="1.1"
+ label="&prefEnableHTTP11.label;"
+ accesskey="&prefEnableHTTP11Proxy.accesskey;"/>
+ </radiogroup>
+ </vbox>
+ </groupbox>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <groupbox>
+ <caption label="&prefUseragent.label;"/>
+ <radiogroup id="uaFirefoxCompat"
+ oncommand="updateUAPrefs(this);">
+ <radio value="strict"
+ label="&prefFirefoxStrict.label;"
+ accesskey="&prefFirefoxStrict.accesskey;"/>
+ <radio value="none"
+ label="&prefFirefoxNone.label;"
+ accesskey="&prefFirefoxNone.accesskey;"/>
+ <radio value="compat"
+ label="&prefFirefoxCompat2.label;"
+ accesskey="&prefFirefoxCompat2.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <description>&prefCompatWarning2.desc;</description>
+ </prefpane>
+
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-images.xul b/comm/suite/components/pref/content/pref-images.xul
new file mode 100644
index 0000000000..92c048dc56
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-images.xul
@@ -0,0 +1,47 @@
+<?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 % prefImagesDTD SYSTEM "chrome://communicator/locale/pref/pref-images.dtd" >
+%prefImagesDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="images_pane"
+ label="&pref.images.title;">
+ <preferences id="images_preferences">
+ <preference id="permissions.default.image"
+ name="permissions.default.image" type="int"/>
+ <preference id="pref.advanced.images.disable_button.view_image" type="bool"
+ name="pref.advanced.images.disable_button.view_image"/>
+ </preferences>
+
+ <groupbox id="imagesArea">
+ <caption label="&imageBlocking.label;"/>
+
+ <description>&imageDetails;</description>
+
+ <radiogroup id="networkImageBehaviour"
+ preference="permissions.default.image">
+ <radio value="2" label="&loadNoImagesRadio.label;"
+ accesskey="&loadNoImagesRadio.accesskey;"/>
+ <radio value="3" label="&loadOrgImagesRadio.label;"
+ accesskey="&loadOrgImagesRadio.accesskey;"/>
+ <radio value="1" label="&loadAllImagesRadio.label;"
+ accesskey="&loadAllImagesRadio.accesskey;"/>
+ </radiogroup>
+
+ <hbox pack="end">
+ <button id="viewImages"
+ label="&viewPermissions.label;"
+ accesskey="&viewPermissions.accesskey;"
+ oncommand="toDataManager('|permissions');"
+ preference="pref.advanced.images.disable_button.view_image"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-keynav.js b/comm/suite/components/pref/content/pref-keynav.js
new file mode 100644
index 0000000000..5210fdbe5c
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-keynav.js
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+const kTabToLinks = 4;
+const kTabToForms = 2;
+const kTabToTextboxes = 1;
+
+function Startup()
+{
+ if (AppConstants.platform == "macosx") {
+ document.getElementById("tabNavigationPrefs").setAttribute("hidden", true);
+ }
+
+ UpdateBrowseWithCaretItems();
+}
+
+function ReadTabNav(aField)
+{
+ var curval = document.getElementById("accessibility.tabfocus").value;
+ // Return the right bit based on the id of "aField"
+ if (aField.id == "tabNavigationLinks")
+ return (curval & kTabToLinks) != 0;
+
+ return (curval & kTabToForms) != 0;
+}
+
+function WriteTabNav(aField)
+{
+ var curval = document.getElementById("accessibility.tabfocus").value;
+ // Textboxes are always part of the tab order
+ curval |= kTabToTextboxes;
+ // Select the bit, we have to change, based on the id of "aField"
+ var bit = kTabToForms;
+ if (aField.id == "tabNavigationLinks")
+ bit = kTabToLinks;
+
+ if (aField.checked)
+ return curval | bit;
+
+ return curval & ~bit;
+}
+
+function UpdateBrowseWithCaretItems()
+{
+ document.getElementById("browseWithCaretWarn").disabled =
+ !document.getElementById("accessibility.browsewithcaret_shortcut.enabled").value ||
+ document.getElementById("accessibility.browsewithcaret").locked;
+}
diff --git a/comm/suite/components/pref/content/pref-keynav.xul b/comm/suite/components/pref/content/pref-keynav.xul
new file mode 100644
index 0000000000..39ea0e7d1f
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-keynav.xul
@@ -0,0 +1,104 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-keynav.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="keynav_pane"
+ label="&pref.keyNav.title;"
+ script="chrome://communicator/content/pref/pref-keynav.js">
+
+ <preferences id="keynav_preferences">
+ <preference id="accessibility.tabfocus"
+ name="accessibility.tabfocus"
+ type="int"/>
+ <preference id="accessibility.browsewithcaret"
+ name="accessibility.browsewithcaret"
+ type="bool"/>
+ <preference id="accessibility.browsewithcaret_shortcut.enabled"
+ name="accessibility.browsewithcaret_shortcut.enabled"
+ type="bool"
+ onchange="UpdateBrowseWithCaretItems();"/>
+ <preference id="accessibility.warn_on_browsewithcaret"
+ name="accessibility.warn_on_browsewithcaret"
+ type="bool"/>
+ <preference id="ui.key.accelKey"
+ name="ui.key.accelKey"
+ type="int"/>
+ <preference id="ui.key.menuAccessKey"
+ name="ui.key.menuAccessKey"
+ type="int"/>
+ </preferences>
+
+ <groupbox id="tabNavigationPrefs"
+ align="start">
+ <caption label="&tabNavigationBehavior.label;"/>
+ <description>&tabNavigationDesc.label;</description>
+
+ <checkbox id="tabNavigationLinks"
+ label="&tabNavigationLinks.label;"
+ accesskey="&tabNavigationLinks.accesskey;"
+ preference="accessibility.tabfocus"
+ onsyncfrompreference="return document.getElementById('keynav_pane').ReadTabNav(this);"
+ onsynctopreference="return document.getElementById('keynav_pane').WriteTabNav(this);"/>
+ <checkbox id="tabNavigationForms"
+ label="&tabNavigationForms.label;"
+ accesskey="&tabNavigationForms.accesskey;"
+ preference="accessibility.tabfocus"
+ onsyncfrompreference="return document.getElementById('keynav_pane').ReadTabNav(this);"
+ onsynctopreference="return document.getElementById('keynav_pane').WriteTabNav(this);"/>
+ <description>&tabNavigationTextboxes.label;</description>
+ </groupbox>
+
+ <groupbox id="browseWithCaretPrefs"
+ align="start">
+ <caption label="&accessibilityBrowseWithCaret.label;"/>
+ <description>&browseWithCaretDesc.label;</description>
+ <checkbox id="browseWithCaretUse"
+ label="&browseWithCaretUse.label;"
+ accesskey="&browseWithCaretUse.accesskey;"
+ preference="accessibility.browsewithcaret"/>
+ <checkbox id="browseWithCaretShortCut"
+ label="&browseWithCaretShortCut.label;"
+ accesskey="&browseWithCaretShortCut.accesskey;"
+ preference="accessibility.browsewithcaret_shortcut.enabled"/>
+ <checkbox id="browseWithCaretWarn"
+ class="indent"
+ label="&browseWithCaretWarn.label;"
+ accesskey="&browseWithCaretWarn.accesskey;"
+ preference="accessibility.warn_on_browsewithcaret"/>
+ </groupbox>
+
+ <groupbox id="modifiers">
+ <caption label="&modifiers.label;"/>
+ <hbox align="center">
+ <label id="acceleratorKey"
+ value="&acceleratorKey.label;"
+ accesskey="&acceleratorKey.accesskey;"
+ control="acceleratorKeyValue"/>
+ <textbox id="acceleratorKeyValue"
+ type="number"
+ min="0"
+ max="255"
+ size="3"
+ preference="ui.key.accelKey"
+ aria-labelledby="acceleratorKey acceleratorKeyValue"/>
+ <label id="menuAccessKey"
+ value="&menuAccessKey.label;"
+ accesskey="&menuAccessKey.accesskey;"
+ control="menuAccessKeyValue"/>
+ <textbox id="menuAccessKeyValue"
+ type="number"
+ min="0"
+ max="255"
+ size="3"
+ preference="ui.key.menuAccessKey"
+ aria-labelledby="menuAccessKey menuAccessKeyValue"/>
+ </hbox>
+ <description>&modifiersDesc.label;</description>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-languages-add.js b/comm/suite/components/pref/content/pref-languages-add.js
new file mode 100644
index 0000000000..e539b961cc
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-languages-add.js
@@ -0,0 +1,147 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gLanguageNames;
+var gAvailableLanguages;
+var gOtherLanguages;
+var gSelectedLanguages = [];
+var gInvalidLanguages;
+
+function OnLoadAddLanguages()
+{
+ gLanguageNames = window.arguments[0];
+ gAvailableLanguages = document.getElementById("availableLanguages");
+ gSelectedLanguages = document.getElementById("intl.accept_languages").value
+ .toLowerCase().split(/\s*,\s*/);
+ gOtherLanguages = document.getElementById("otherLanguages");
+
+ if (gLanguageNames)
+ {
+ for (var i = 0; i < gLanguageNames.length; i++)
+ {
+ if (!gSelectedLanguages.includes(gLanguageNames[i][1]))
+ gAvailableLanguages.appendItem(gLanguageNames[i][0],
+ gLanguageNames[i][1]);
+ }
+ }
+}
+
+function IsRFC1766LangTag(aCandidate)
+{
+ /* reject bogus lang strings, INCLUDING those with HTTP "q"
+ values kludged on the end of them
+
+ Valid language codes examples:
+ i.e. ja-JP-kansai (Kansai dialect of Japanese)
+ en-US-texas (Texas dialect)
+ i-klingon-tng (did TOS Klingons speak in non-English?)
+ sgn-US-MA (Martha Vineyard's Sign Language)
+ */
+ var tags = aCandidate.split('-');
+ var checkedTags = 0;
+
+ if (/^[ix]$/.test(tags[0]))
+ {
+ if (tags.length < 2)
+ return false;
+ checkedTags++;
+ }
+ else
+ /* if not IANA "i" or a private "x" extension, the primary
+ tag should be a ISO 639 country code, two or three letters long.
+ we don't check if the country code is bogus or not.
+ */
+ {
+ if (!/^[a-z]{2,3}$/.test(tags[0]))
+ return false;
+ checkedTags++;
+
+ /* the first subtag can be either a 2 letter ISO 3166 country code,
+ or an IANA registered tag from 3 to 8 characters.
+ */
+ if (tags.length > 1)
+ {
+ if (!/^[a-z0-9]{2,8}$/.test(tags[1]))
+ return false;
+
+ /* do not allow user-assigned ISO 3166 country codes */
+ if (/^(aa|zz|x[a-z]|q[m-z])$/.test(tags[1]))
+ return false;
+ checkedTags++;
+ }
+ }
+
+ /* any remaining subtags must be one to eight alphabetic characters */
+
+ while (checkedTags < tags.length)
+ {
+ if (!/^[a-z0-9]{1,8}$/.test(tags[checkedTags]))
+ return false;
+ checkedTags++;
+ }
+ return true;
+}
+
+function WriteAddedLanguages(aListbox)
+{
+ var invalidLangs = [];
+ // selected languages
+ var languages = aListbox.selectedItems;
+ var addedLang = Array.from(languages, e => e.value);
+
+ // user-defined languages
+ languages = gOtherLanguages.value;
+ if (languages)
+ {
+ let languageIds = languages.replace(/\s+/g, "").toLowerCase().split(",");
+ for (var i = 0; i < languageIds.length; i++)
+ {
+ let languageId = languageIds[i];
+ if (IsRFC1766LangTag(languageId))
+ {
+ if (!addedLang.includes(languageId) &&
+ !gSelectedLanguages.includes(languageId))
+ addedLang.push(languageId);
+ }
+ else
+ {
+ invalidLangs.push(languageId);
+ }
+ }
+ }
+
+ if (invalidLangs.length)
+ gInvalidLanguages = invalidLangs.join(", ");
+ else
+ gSelectedLanguages = gSelectedLanguages.concat(addedLang);
+
+ return gSelectedLanguages.join(",");
+}
+
+function OnAccept()
+{
+ if (!gInvalidLanguages)
+ return true;
+
+ let prefLangBundle = document.getElementById("prefLangAddBundle");
+ const kErrorMsg = prefLangBundle.getString("illegalOtherLanguage") + " " +
+ gInvalidLanguages;
+ const kErrorTitle = prefLangBundle.getString("illegalOtherLanguageTitle");
+ Services.prompt.alert(this.window, kErrorTitle, kErrorMsg);
+
+ gInvalidLanguages = null;
+ gOtherLanguages.focus();
+ return false;
+}
+
+function HandleDoubleClick()
+{
+ document.documentElement.acceptDialog();
+}
+
+function DoBeforeAccept()
+{
+ gAvailableLanguages.doCommand();
+}
diff --git a/comm/suite/components/pref/content/pref-languages-add.xul b/comm/suite/components/pref/content/pref-languages-add.xul
new file mode 100644
index 0000000000..0ae11aee9b
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-languages-add.xul
@@ -0,0 +1,54 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE prefwindow SYSTEM "chrome://communicator/locale/pref/pref-languages.dtd" >
+
+
+<prefwindow xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="addLanguagesPreferences"
+ title="&languages.customize.add.title.label;"
+ type="child"
+ onload="OnLoadAddLanguages();"
+ onbeforeaccept="DoBeforeAccept();"
+ ondialogaccept="return OnAccept();">
+
+ <script src="chrome://communicator/content/pref/pref-languages-add.js"/>
+
+ <prefpane id="addLanguagesPane">
+ <preferences id="addLanguages">
+ <preference id="intl.accept_languages"
+ name="intl.accept_languages"
+ type="wstring"/>
+ </preferences>
+
+ <stringbundleset id="langAddBundleset">
+ <stringbundle id="prefLangAddBundle"
+ src="chrome://communicator/locale/pref/pref-languages.properties"/>
+ </stringbundleset>
+
+ <description style="width: 1px;">&languages.customize.prefAddLangDescript;</description>
+ <separator class="thin"/>
+ <description style="width: 1px;">&languages.customize.available.label;</description>
+
+ <listbox id="availableLanguages"
+ flex="1"
+ seltype="multiple"
+ preference="intl.accept_languages"
+ ondblclick="HandleDoubleClick();"
+ onsynctopreference="return WriteAddedLanguages(this);"/>
+
+ <hbox align="center">
+ <label value="&languages.customize.others.label;"
+ accesskey="&languages.customize.others.accesskey;"
+ control="otherLanguages"/>
+ <textbox id="otherLanguages" size="12" flex="1"/>
+ <label value="&languages.customize.others.examples;" control="otherLanguages"/>
+ </hbox>
+ </prefpane>
+</prefwindow>
diff --git a/comm/suite/components/pref/content/pref-languages.js b/comm/suite/components/pref/content/pref-languages.js
new file mode 100644
index 0000000000..de2895ee11
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-languages.js
@@ -0,0 +1,200 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gActiveLanguages;
+var gLanguages;
+var gLanguageNames = [];
+var gLanguageTitles = {};
+
+function Startup()
+{
+ gActiveLanguages = document.getElementById("activeLanguages");
+ // gLanguages stores the ordered list of languages, due to the nature
+ // of childNodes it is live and updates automatically.
+ gLanguages = gActiveLanguages.childNodes;
+
+ ReadAvailableLanguages();
+}
+
+function AddLanguage()
+{
+ document.documentElement.openSubDialog("chrome://communicator/content/pref/pref-languages-add.xul", "addlangwindow", gLanguageNames);
+}
+
+function ReadAvailableLanguages()
+{
+ var i = 0;
+ var languagesBundle = document.getElementById("languageNamesBundle");
+ var prefLangBundle = document.getElementById("prefLangBundle");
+ var regionsBundle = document.getElementById("regionNamesBundle");
+ var langStrings = document.getElementById("acceptedBundle").strings;
+
+ while (langStrings.hasMoreElements())
+ {
+ // Progress through the bundle.
+ var curItem = langStrings.getNext();
+
+ if (!(curItem instanceof Ci.nsIPropertyElement))
+ break;
+
+ var stringNameProperty = curItem.key.split('.');
+
+ var str = stringNameProperty[0];
+ if (str && stringNameProperty[1] == 'accept')
+ {
+ var stringLangRegion = str.split('-');
+
+ if (stringLangRegion[0])
+ {
+ var language = "";
+ var region = null;
+
+ try
+ {
+ language = languagesBundle.getString(stringLangRegion[0]);
+ }
+ catch (ex) {}
+
+ if (stringLangRegion.length > 1)
+ {
+ try
+ {
+ region = regionsBundle.getString(stringLangRegion[1]);
+ }
+ catch (ex) {}
+ }
+
+ var title;
+ if (region)
+ title = prefLangBundle.getFormattedString("languageRegionCodeFormat",
+ [language, region, str]);
+ else
+ title = prefLangBundle.getFormattedString("languageCodeFormat",
+ [language, str]);
+ gLanguageTitles[str] = title;
+ if (curItem.value == "true")
+ gLanguageNames.push([title, str]);
+ }
+ }
+ }
+
+ // Sort on first element.
+ gLanguageNames.sort(
+ function compareFn(a, b)
+ {
+ return a[0].localeCompare(b[0]);
+ }
+ );
+}
+
+function ReadActiveLanguages()
+{
+ var arrayOfPrefs = document.getElementById("intl.accept_languages").value
+ .toLowerCase().split(/\s*,\s*/);
+
+ // No need to rebuild listitems if languages in prefs and listitems match.
+ if (InSync(arrayOfPrefs))
+ return;
+
+ while (gActiveLanguages.hasChildNodes())
+ gActiveLanguages.lastChild.remove();
+
+ arrayOfPrefs.forEach(
+ function(aKey)
+ {
+ if (aKey)
+ {
+ let langTitle = gLanguageTitles.hasOwnProperty(aKey) ?
+ gLanguageTitles[aKey] : "[" + aKey + "]";
+ gActiveLanguages.appendItem(langTitle, aKey);
+ }
+ }
+ );
+
+ SelectLanguage();
+
+ return;
+}
+
+// Checks whether listitems and pref values matches, returns false if not.
+function InSync(aPrefArray)
+{
+ // Can't match if they don't have the same length.
+ if (aPrefArray.length != gLanguages.length)
+ return false;
+
+ return aPrefArray.every(
+ function(aElement, aIndex)
+ {
+ return aElement == gLanguages[aIndex].value;
+ }
+ );
+}
+
+// Called on onsynctopreference.
+function WriteActiveLanguages()
+{
+ return Array.from(gLanguages, e => e.value).join(",");
+}
+
+function MoveUp()
+{
+ var selected = gActiveLanguages.selectedItem;
+ var before = selected.previousSibling;
+ if (before)
+ {
+ before.parentNode.insertBefore(selected, before);
+ gActiveLanguages.selectItem(selected);
+ gActiveLanguages.ensureElementIsVisible(selected);
+ }
+
+ SelectLanguage();
+ gActiveLanguages.doCommand();
+}
+
+function MoveDown()
+{
+ var selected = gActiveLanguages.selectedItem;
+ if (selected.nextSibling)
+ {
+ var before = selected.nextSibling.nextSibling;
+ gActiveLanguages.insertBefore(selected, before);
+ gActiveLanguages.selectItem(selected);
+ }
+
+ SelectLanguage();
+ gActiveLanguages.doCommand();
+}
+
+function RemoveActiveLanguage(aEvent)
+{
+ if (aEvent && aEvent.keyCode != aEvent.DOM_VK_DELETE &&
+ aEvent.keyCode != aEvent.DOM_VK_BACK_SPACE)
+ return;
+
+ var nextNode = null;
+
+ while (gActiveLanguages.selectedItem)
+ {
+ var selectedNode = gActiveLanguages.selectedItem;
+ nextNode = selectedNode.nextSibling || selectedNode.previousSibling;
+ selectedNode.remove();
+ }
+
+ if (nextNode)
+ gActiveLanguages.selectItem(nextNode);
+
+ SelectLanguage();
+ gActiveLanguages.doCommand();
+}
+
+function SelectLanguage()
+{
+ var len = gActiveLanguages.selectedItems.length;
+ EnableElementById("langRemove", len, false);
+ var selected = gActiveLanguages.selectedItem;
+ EnableElementById("langDown", (len == 1) && selected.nextSibling, false);
+ EnableElementById("langUp", (len == 1) && selected.previousSibling, false);
+}
diff --git a/comm/suite/components/pref/content/pref-languages.xul b/comm/suite/components/pref/content/pref-languages.xul
new file mode 100644
index 0000000000..a17deae032
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-languages.xul
@@ -0,0 +1,124 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % prefLanguagesDTD SYSTEM "chrome://communicator/locale/pref/pref-languages.dtd"> %prefLanguagesDTD;
+ <!ENTITY % prefUtilitiesDTD SYSTEM "chrome://communicator/locale/pref/prefutilities.dtd"> %prefUtilitiesDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="languages_pane"
+ label="&languages.customize.title;"
+ script="chrome://communicator/content/pref/pref-languages.js">
+
+ <preferences id="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.add"
+ name="pref.browser.language.disable_button.add"
+ type="bool"/>
+ <preference id="pref.browser.language.disable_button.remove"
+ name="pref.browser.language.disable_button.remove"
+ type="bool"/>
+ <preference id="intl.charset.fallback.override"
+ name="intl.charset.fallback.override"
+ type="string"/>
+ </preferences>
+
+ <stringbundleset id="langBundleset">
+ <stringbundle id="acceptedBundle"
+ src="resource://gre/res/language.properties"/>
+ <stringbundle id="prefLangBundle"
+ src="chrome://communicator/locale/pref/pref-languages.properties"/>
+ </stringbundleset>
+
+ <groupbox flex="1">
+ <caption label="&langtitle.label;"/>
+ <description>&languages.customize.prefLangDescript;</description>
+ <label accesskey="&languages.customize.active.accesskey;"
+ control="activeLanguages">&languages.customize.active.label;</label>
+ <hbox flex="1">
+ <listbox id="activeLanguages"
+ flex="1"
+ style="width: 0px; height: 0px;"
+ seltype="multiple"
+ preference="intl.accept_languages"
+ onkeypress="RemoveActiveLanguage(event);"
+ onselect="SelectLanguage();"
+ onsynctopreference="return document.getElementById('languages_pane').WriteActiveLanguages();"
+ onsyncfrompreference="return document.getElementById('languages_pane').ReadActiveLanguages(this);"/>
+ <vbox>
+ <button id="langUp"
+ class="up"
+ disabled="true"
+ label="&languages.customize.moveUp.label;"
+ accesskey="&languages.customize.moveUp.accesskey;"
+ preference="pref.browser.language.disable_button.up"
+ oncommand="MoveUp();"/>
+ <button id="langDown"
+ class="down"
+ disabled="true"
+ label="&languages.customize.moveDown.label;"
+ accesskey="&languages.customize.moveDown.accesskey;"
+ preference="pref.browser.language.disable_button.down"
+ oncommand="MoveDown();"/>
+ <spacer flex="1"/>
+ <button id="langAdd"
+ label="&languages.customize.addButton.label;"
+ accesskey="&languages.customize.addButton.accesskey;"
+ preference="pref.browser.language.disable_button.add"
+ oncommand="AddLanguage();"/>
+ <button id="langRemove"
+ disabled="true"
+ label="&languages.customize.deleteButton.label;"
+ accesskey="&languages.customize.deleteButton.accesskey;"
+ preference="pref.browser.language.disable_button.remove"
+ oncommand="RemoveActiveLanguage(null);"/>
+ </vbox>
+ </hbox>
+ </groupbox>
+
+ <groupbox align="start">
+ <caption label="&languages.customize.Fallback2.grouplabel;"/>
+ <description>&languages.customize.Fallback2.desc;</description>
+ <hbox align="center">
+ <label value="&languages.customize.Fallback2.label;"
+ accesskey="&languages.customize.Fallback2.accesskey;"
+ control="defaultCharsetList"/>
+ <menulist id="defaultCharsetList"
+ preference="intl.charset.fallback.override">
+ <menupopup>
+ <menuitem label="&FallbackCharset.auto;" value=""/>
+ <menuitem label="&FallbackCharset.arabic;" value="windows-1256"/>
+ <menuitem label="&FallbackCharset.baltic;" value="windows-1257"/>
+ <menuitem label="&FallbackCharset.ceiso;" value="ISO-8859-2"/>
+ <menuitem label="&FallbackCharset.cewindows;" value="windows-1250"/>
+ <menuitem label="&FallbackCharset.simplified;" value="gbk"/>
+ <menuitem label="&FallbackCharset.traditional;" value="Big5"/>
+ <menuitem label="&FallbackCharset.cyrillic;" value="windows-1251"/>
+ <menuitem label="&FallbackCharset.greek;" value="ISO-8859-7"/>
+ <menuitem label="&FallbackCharset.hebrew;" value="windows-1255"/>
+ <menuitem label="&FallbackCharset.japanese;" value="Shift_JIS"/>
+ <menuitem label="&FallbackCharset.korean;" value="EUC-KR"/>
+ <menuitem label="&FallbackCharset.thai;" value="windows-874"/>
+ <menuitem label="&FallbackCharset.turkish;" value="windows-1254"/>
+ <menuitem label="&FallbackCharset.vietnamese;" value="windows-1258"/>
+ <menuitem label="&FallbackCharset.other;" value="windows-1252"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-links.js b/comm/suite/components/pref/content/pref-links.js
new file mode 100644
index 0000000000..2ca7a3e921
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-links.js
@@ -0,0 +1,15 @@
+/* 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/. */
+
+function Startup()
+{
+ ToggleRestrictionGroup(document.getElementById("browser.link.open_newwindow").value);
+}
+
+function ToggleRestrictionGroup(value)
+{
+ document.getElementById("restrictionGroup").disabled =
+ value == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW ||
+ document.getElementById("browser.link.open_newwindow.restriction").locked;
+}
diff --git a/comm/suite/components/pref/content/pref-links.xul b/comm/suite/components/pref/content/pref-links.xul
new file mode 100644
index 0000000000..6ebb8d9cb2
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-links.xul
@@ -0,0 +1,78 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-links.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="links_pane"
+ label="&linksHeader.label;"
+ script="chrome://communicator/content/pref/pref-links.js">
+
+ <preferences id="links_preferences">
+ <preference id="browser.link.open_newwindow"
+ name="browser.link.open_newwindow"
+ type="int"
+ onchange="ToggleRestrictionGroup(this.value);"/>
+ <preference id="browser.link.open_newwindow.restriction"
+ name="browser.link.open_newwindow.restriction"
+ type="int"/>
+ <preference id="browser.link.open_external"
+ name="browser.link.open_external"
+ type="int"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&newWindow.label;"/>
+ <description>&newWindowDescription.label;</description>
+ <radiogroup id="newWindowGroup"
+ class="indent"
+ preference="browser.link.open_newwindow">
+ <radio value="1"
+ label="&openCurrent.label;"
+ accesskey="&newWindowGroupCurrent.accesskey;"/>
+ <radio value="3"
+ label="&openTab.label;"
+ accesskey="&newWindowGroupTab.accesskey;"/>
+ <radio value="2"
+ label="&openWindow.label;"
+ accesskey="&newWindowGroupWindow.accesskey;"/>
+ </radiogroup>
+ <separator class="thin"/>
+ <description>&newWindowRestriction.label;</description>
+ <radiogroup id="restrictionGroup"
+ class="indent"
+ preference="browser.link.open_newwindow.restriction">
+ <radio value="0"
+ label="&divertAll.label;"
+ accesskey="&divertAll.accesskey;"/>
+ <radio value="2"
+ label="&divertNoFeatures.label;"
+ accesskey="&divertNoFeatures.accesskey;"/>
+ <radio value="1"
+ label="&dontDivert.label;"
+ accesskey="&dontDivert.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&external.label;"/>
+ <description>&externalDescription.label;</description>
+ <radiogroup id="externalGroup"
+ class="indent"
+ preference="browser.link.open_external">
+ <radio value="1"
+ label="&openCurrent.label;"
+ accesskey="&externalGroupCurrent.accesskey;"/>
+ <radio value="3"
+ label="&openTab.label;"
+ accesskey="&externalGroupTab.accesskey;"/>
+ <radio value="2"
+ label="&openWindow.label;"
+ accesskey="&externalGroupWindow.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-locationbar.js b/comm/suite/components/pref/content/pref-locationbar.js
new file mode 100644
index 0000000000..042621eb35
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-locationbar.js
@@ -0,0 +1,42 @@
+/* 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/. */
+
+function Startup()
+{
+ // On systems that has the file view component, autoFill and showPopup will
+ // return results from local browsing "history", even if autocomplete.enabled
+ // is turned off, so we'll need to remove the dependent look in the ui.
+
+ if ("@mozilla.org/autocomplete/search;1?name=file" in Cc)
+ {
+ // We indent the checkboxes with the class attribute set to "indent", so
+ // just remove the attribute.
+ document.getElementById("autoFill").removeAttribute("class");
+ document.getElementById("showPopup").removeAttribute("class");
+ }
+
+ updateDependent();
+}
+
+function updateDependent()
+{
+ var matchHistoryPref = document.getElementById("browser.urlbar.suggest.history");
+ EnableElementById("matchOnlyTyped", matchHistoryPref.value);
+
+ var matchBookmarkPref = document.getElementById("browser.urlbar.suggest.bookmark");
+ var autoCompleteEnabled = matchHistoryPref.value || matchBookmarkPref.value;
+ EnableElementById("matchBehavior", autoCompleteEnabled);
+
+ // If autoFill has a class attribute, we don't have the file view component.
+ // We then need to update autoFill and showPopup.
+ if (document.getElementById("autoFill").hasAttribute("class"))
+ {
+ EnableElementById("autoFill", autoCompleteEnabled);
+ EnableElementById("showPopup", autoCompleteEnabled);
+ }
+
+ // We need to update autocomplete.enabled as the backend still respects it.
+ document.getElementById("browser.urlbar.autocomplete.enabled").value =
+ autoCompleteEnabled;
+}
diff --git a/comm/suite/components/pref/content/pref-locationbar.xul b/comm/suite/components/pref/content/pref-locationbar.xul
new file mode 100644
index 0000000000..3c781e4c60
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-locationbar.xul
@@ -0,0 +1,127 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-locationbar.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="locationBar_pane"
+ label="&pref.locationBar.title;"
+ script="chrome://communicator/content/pref/pref-locationbar.js">
+
+ <preferences id="locationBar_preferences">
+ <!-- The suggest preferences need to come first otherwise the backend
+ will enable both bookmarks and history if either is enabled -->
+ <preference id="browser.urlbar.suggest.bookmark"
+ name="browser.urlbar.suggest.bookmark"
+ type="bool"
+ onchange="updateDependent();"/>
+ <preference id="browser.urlbar.suggest.history"
+ name="browser.urlbar.suggest.history"
+ type="bool"
+ onchange="updateDependent();"/>
+ <preference id="browser.urlbar.suggest.history.onlyTyped"
+ name="browser.urlbar.suggest.history.onlyTyped"
+ type="bool"/>
+ <preference id="browser.urlbar.autocomplete.enabled"
+ name="browser.urlbar.autocomplete.enabled"
+ type="bool"/>
+ <preference id="browser.urlbar.matchBehavior"
+ name="browser.urlbar.matchBehavior"
+ type="int"/>
+ <preference id="browser.urlbar.autoFill"
+ name="browser.urlbar.autoFill"
+ type="bool"
+ onchange="updateMatchPrefs();"/>
+ <preference id="browser.urlbar.showPopup"
+ name="browser.urlbar.showPopup"
+ type="bool"
+ onchange="updateMatchPrefs();"/>
+ <preference id="browser.urlbar.showSearch"
+ name="browser.urlbar.showSearch"
+ type="bool"/>
+ <preference id="browser.urlbar.formatting.enabled"
+ name="browser.urlbar.formatting.enabled"
+ type="bool"/>
+ <preference id="browser.urlbar.highlight.secure"
+ name="browser.urlbar.highlight.secure"
+ type="bool"/>
+ <preference id="browser.fixup.alternate.enabled"
+ name="browser.fixup.alternate.enabled"
+ type="bool"/>
+ <preference id="keyword.enabled"
+ name="keyword.enabled"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&autoComplete.label;"/>
+ <checkbox id="matchHistory"
+ label="&autoCompleteMatchHistory.label;"
+ accesskey="&autoCompleteMatchHistory.accesskey;"
+ preference="browser.urlbar.suggest.history"/>
+ <checkbox id="matchOnlyTyped"
+ class="indent"
+ label="&autoCompleteMatchOnlyTyped.label;"
+ accesskey="&autoCompleteMatchOnlyTyped.accesskey;"
+ preference="browser.urlbar.suggest.history.onlyTyped"/>
+ <checkbox id="matchBookmark"
+ label="&autoCompleteMatchBookmarks.label;"
+ accesskey="&autoCompleteMatchBookmarks.accesskey;"
+ preference="browser.urlbar.suggest.bookmark"/>
+ <hbox align="center" class="indent">
+ <label value="&autoCompleteMatch.label;" control="matchBehavior"
+ accesskey="&autoCompleteMatch.accesskey;"/>
+ <menulist id="matchBehavior"
+ preference="browser.urlbar.matchBehavior">
+ <menupopup>
+ <menuitem value="0" label="&autoCompleteMatchAnywhere;"/>
+ <menuitem value="1" label="&autoCompleteMatchWordsFirst;"/>
+ <menuitem value="2" label="&autoCompleteMatchWords;"/>
+ <menuitem value="3" label="&autoCompleteMatchStart;"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <checkbox id="autoFill"
+ class="indent"
+ label="&autoCompleteAutoFill.label;"
+ accesskey="&autoCompleteAutoFill.accesskey;"
+ preference="browser.urlbar.autoFill"/>
+ <checkbox id="showPopup"
+ class="indent"
+ label="&autoCompleteShowPopup.label;"
+ accesskey="&autoCompleteShowPopup.accesskey;"
+ preference="browser.urlbar.showPopup"/>
+ <checkbox id="showSearch"
+ label="&showInternetSearch.label;"
+ accesskey="&showInternetSearch.accesskey;"
+ preference="browser.urlbar.showSearch"/>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&formatting.label;"/>
+ <checkbox id="domainFormattingEnabled"
+ label="&domainFormatting.label;"
+ accesskey="&domainFormatting.accesskey;"
+ preference="browser.urlbar.formatting.enabled"/>
+ <checkbox id="highlightSecureEnabled"
+ label="&highlightSecure.label;"
+ accesskey="&highlightSecure.accesskey;"
+ preference="browser.urlbar.highlight.secure"/>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&unknownLocations.label;"/>
+ <checkbox id="domainGuessingEnabled"
+ label="&domainGuessing.label;"
+ accesskey="&domainGuessing.accesskey;"
+ preference="browser.fixup.alternate.enabled"/>
+ <checkbox id="browserGoBrowsingEnabled"
+ label="&keywords.label;"
+ accesskey="&keywords.accesskey;"
+ preference="keyword.enabled"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-media.xul b/comm/suite/components/pref/content/pref-media.xul
new file mode 100644
index 0000000000..3a2411a634
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-media.xul
@@ -0,0 +1,60 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-media.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="media_pane"
+ label="&pref.media.title;">
+
+ <preferences id="media_preferences">
+ <preference id="media.autoplay.enabled"
+ name="media.autoplay.enabled"
+ type="bool"/>
+ <preference id="media.eme.enabled"
+ name="media.eme.enabled"
+ type="bool"/>
+ <preference id="image.animation_mode"
+ name="image.animation_mode"
+ type="string"/>
+ </preferences>
+
+ <groupbox id="mediaHTML5Preferences" align="start">
+ <caption label="&mediaHTML5Preferences.label;"/>
+ <checkbox id="autoplay"
+ label="&allowMediaAutoplay.label;"
+ accesskey="&allowMediaAutoplay.accesskey;"
+ preference="media.autoplay.enabled"/>
+ </groupbox>
+
+ <!-- REMOVE #ifndef once EME are ready for prime time, meta bug 1015800 -->
+#ifndef RELEASE_OR_BETA
+ <groupbox id="drmPreferences">
+ <caption label="&enableDrmMedia.label;"/>
+ <checkbox id="emeForSuite"
+ label="&enableEmeForSuite.label;"
+ accesskey="&enableEmeForSuite.accesskey;"
+ preference="media.eme.enabled"/>
+ </groupbox>
+#endif
+
+ <groupbox>
+ <caption label="&animLoopingTitle.label;"/>
+ <radiogroup id="imageLooping"
+ preference="image.animation_mode">
+ <radio value="normal"
+ label="&animLoopAsSpecified.label;"
+ accesskey="&animLoopAsSpecified.accesskey;"/>
+ <radio value="once"
+ label="&animLoopOnce.label;"
+ accesskey="&animLoopOnce.accesskey;"/>
+ <radio value="none"
+ label="&animLoopNever.label;"
+ accesskey="&animLoopNever.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-mousewheel.js b/comm/suite/components/pref/content/pref-mousewheel.js
new file mode 100644
index 0000000000..6902a0c3cf
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-mousewheel.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+function doEnabling(aElement)
+{
+ var preference = document.getElementById(aElement.getAttribute("preference"));
+ var prefix = aElement.id.replace(/action$/, "");
+ var vertical = document.getElementById(prefix + "delta_multiplier_y");
+ EnableElement(vertical, preference.value);
+ updateCheckbox(vertical);
+ var actionX = document.getElementById(prefix + "action_x");
+ if (actionX.value < 0)
+ doEnablingX(actionX);
+}
+
+function doEnablingX(aElement)
+{
+ var preference = document.getElementById(aElement.getAttribute("preference"));
+ var prefix = aElement.id.replace(/action_x$/, "");
+ var value = preference.value;
+ if (value < 0) {
+ var action = document.getElementById(prefix + "action");
+ preference = document.getElementById(action.getAttribute("preference"));
+ value = preference.value;
+ }
+ var horizontal = document.getElementById(prefix + "delta_multiplier_x");
+ EnableElement(horizontal, value);
+ updateCheckbox(horizontal);
+}
+
+function updateCheckbox(aTextbox)
+{
+ var preference = document.getElementById(aTextbox.getAttribute("preference"));
+ var checkbox = aTextbox.parentNode.lastChild;
+ checkbox.checked = preference.value < 0;
+ checkbox.disabled = !preference.value || aTextbox.disabled
+}
+
+function updateTextbox(aCheckbox)
+{
+ var textbox = aCheckbox.previousSibling.previousSibling;
+ var preference = document.getElementById(textbox.getAttribute("preference"));
+ preference.value = -preference.value;
+}
diff --git a/comm/suite/components/pref/content/pref-mousewheel.xul b/comm/suite/components/pref/content/pref-mousewheel.xul
new file mode 100644
index 0000000000..63346781cc
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-mousewheel.xul
@@ -0,0 +1,298 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-mousewheel.dtd" >
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="mousewheel_pane"
+ label="&pref.mouseWheel.title;"
+ script="chrome://communicator/content/pref/pref-mousewheel.js">
+
+ <preferences id="mousewheel_preferences">
+ <preference id="mousewheel.default.action"
+ name="mousewheel.default.action"
+ type="int"/>
+ <preference id="mousewheel.default.delta_multiplier_y"
+ name="mousewheel.default.delta_multiplier_y"
+ type="int"/>
+ <preference id="mousewheel.default.action.override_x"
+ name="mousewheel.default.action.override_x"
+ type="int"/>
+ <preference id="mousewheel.default.delta_multiplier_x"
+ name="mousewheel.default.delta_multiplier_x"
+ type="int"/>
+ <preference id="mousewheel.with_alt.action"
+ name="mousewheel.with_alt.action"
+ type="int"/>
+ <preference id="mousewheel.with_alt.delta_multiplier_y"
+ name="mousewheel.with_alt.delta_multiplier_y"
+ type="int"/>
+ <preference id="mousewheel.with_alt.action.override_x"
+ name="mousewheel.with_alt.action.override_x"
+ type="int"/>
+ <preference id="mousewheel.with_alt.delta_multiplier_x"
+ name="mousewheel.with_alt.delta_multiplier_x"
+ type="int"/>
+ <preference id="mousewheel.with_control.action"
+ name="mousewheel.with_control.action"
+ type="int"/>
+ <preference id="mousewheel.with_control.delta_multiplier_y"
+ name="mousewheel.with_control.delta_multiplier_y"
+ type="int"/>
+ <preference id="mousewheel.with_control.action.override_x"
+ name="mousewheel.with_control.action.override_x"
+ type="int"/>
+ <preference id="mousewheel.with_control.delta_multiplier_x"
+ name="mousewheel.with_control.delta_multiplier_x"
+ type="int"/>
+ <preference id="mousewheel.with_shift.action"
+ name="mousewheel.with_shift.action"
+ type="int"/>
+ <preference id="mousewheel.with_shift.delta_multiplier_y"
+ name="mousewheel.with_shift.delta_multiplier_y"
+ type="int"/>
+ <preference id="mousewheel.with_shift.action.override_x"
+ name="mousewheel.with_shift.action.override_x"
+ type="int"/>
+ <preference id="mousewheel.with_shift.delta_multiplier_x"
+ name="mousewheel.with_shift.delta_multiplier_x"
+ type="int"/>
+ </preferences>
+
+ <description>&mouseWheelPanel.label;</description>
+
+ <tabbox class="spaced">
+ <tabs>
+ <tab label="&usingJustTheWheel.label;"/>
+#ifndef XP_MACOSX
+ <tab label="&usingWheelAndAlt.label2;"/>
+#else
+ <tab label="&usingWheelAndOption.label;"/>
+#endif
+ <tab label="&usingWheelAndCtrl.label2;"/>
+ <tab label="&usingWheelAndShft.label2;"/>
+ </tabs>
+
+ <tabpanels>
+
+ <!-- no key modifiers -->
+ <vbox>
+ <groupbox>
+ <caption label="&mouseWheelGroup.label;"/>
+ <radiogroup id="mousewheel_default_action"
+ preference="mousewheel.default.action"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnabling(this);">
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothing.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocument.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&history.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoom.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_default_delta_multiplier_y"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_default_delta_multiplier_y"
+ accesskey="&wheelSpeed.accesskey;"
+ preference="mousewheel.default.delta_multiplier_y"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirection.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&mouseWheelHorizGroup.label;"/>
+ <radiogroup id="mousewheel_default_action_x"
+ preference="mousewheel.default.action.override_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnablingX(this);">
+ <radio value="-1" label="&sameAsVertical.label;" accesskey="&sameAsVertical.accesskey;"/>
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothingHoriz.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocumentHoriz.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&historyHoriz.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoomHoriz.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_default_delta_multiplier_x"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_default_delta_multiplier_x"
+ accesskey="&wheelSpeedHoriz.accesskey;"
+ preference="mousewheel.default.delta_multiplier_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirectionHoriz.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+ </vbox>
+
+ <!-- alt modifiers -->
+ <vbox>
+ <groupbox>
+ <caption label="&mouseWheelGroup.label;"/>
+ <radiogroup id="mousewheel_with_alt_action"
+ preference="mousewheel.with_alt.action"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnabling(this);">
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothing.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocument.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&history.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoom.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_with_alt_delta_multiplier_y"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_with_alt_delta_multiplier_y"
+ accesskey="&wheelSpeed.accesskey;"
+ preference="mousewheel.with_alt.delta_multiplier_y"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirection.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&mouseWheelHorizGroup.label;"/>
+ <radiogroup id="mousewheel_with_alt_action_x"
+ preference="mousewheel.with_alt.action.override_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnablingX(this);">
+ <radio value="-1" label="&sameAsVertical.label;" accesskey="&sameAsVertical.accesskey;"/>
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothingHoriz.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocumentHoriz.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&historyHoriz.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoomHoriz.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_with_alt_delta_multiplier_x"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_with_alt_delta_multiplier_x"
+ accesskey="&wheelSpeedHoriz.accesskey;"
+ preference="mousewheel.with_alt.delta_multiplier_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirectionHoriz.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+ </vbox>
+
+ <!-- control modifiers -->
+ <vbox>
+ <groupbox>
+ <caption label="&mouseWheelGroup.label;"/>
+ <radiogroup id="mousewheel_with_control_action"
+ preference="mousewheel.with_control.action"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnabling(this);">
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothing.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocument.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&history.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoom.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_with_control_delta_multiplier_y"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_with_control_delta_multiplier_y"
+ accesskey="&wheelSpeed.accesskey;"
+ preference="mousewheel.with_control.delta_multiplier_y"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirection.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&mouseWheelHorizGroup.label;"/>
+ <radiogroup id="mousewheel_with_control_action_x"
+ preference="mousewheel.with_control.action.override_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnablingX(this);">
+ <radio value="-1" label="&sameAsVertical.label;" accesskey="&sameAsVertical.accesskey;"/>
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothingHoriz.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocumentHoriz.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&historyHoriz.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoomHoriz.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_with_control_delta_multiplier_x"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_with_control_delta_multiplier_x"
+ accesskey="&wheelSpeedHoriz.accesskey;"
+ preference="mousewheel.with_control.delta_multiplier_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirectionHoriz.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+ </vbox>
+
+ <!-- shift modifiers -->
+ <vbox>
+ <groupbox>
+ <caption label="&mouseWheelGroup.label;"/>
+ <radiogroup id="mousewheel_with_shift_action"
+ preference="mousewheel.with_shift.action"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnabling(this);">
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothing.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocument.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&history.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoom.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_with_shift_delta_multiplier_y"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_with_shift_delta_multiplier_y"
+ accesskey="&wheelSpeed.accesskey;"
+ preference="mousewheel.with_shift.delta_multiplier_y"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirection.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&mouseWheelHorizGroup.label;"/>
+ <radiogroup id="mousewheel_with_shift_action_x"
+ preference="mousewheel.with_shift.action.override_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').doEnablingX(this);">
+ <radio value="-1" label="&sameAsVertical.label;" accesskey="&sameAsVertical.accesskey;"/>
+ <radio value="0" label="&doNothing.label;" accesskey="&doNothingHoriz.accesskey;"/>
+ <radio value="1" label="&scrollDocument.label;" accesskey="&scrollDocumentHoriz.accesskey;"/>
+ <radio value="2" label="&history.label;" accesskey="&historyHoriz.accesskey;"/>
+ <radio value="3" label="&zoom.label;" accesskey="&zoomHoriz.accesskey;"/>
+ </radiogroup>
+ <hbox align="center">
+ <label control="mousewheel_with_shift_delta_multiplier_x"
+ value="&wheelSpeed.label;"/>
+ <textbox type="number" min="-999999" max="999999" size="6"
+ id="mousewheel_with_shift_delta_multiplier_x"
+ accesskey="&wheelSpeedHoriz.accesskey;"
+ preference="mousewheel.with_shift.delta_multiplier_x"
+ onsyncfrompreference="document.getElementById('mousewheel_pane').updateCheckbox(this);"/>
+ <label value="%"/>
+ <checkbox label="&reverseDirection.label;"
+ accesskey="&reverseDirectionHoriz.accesskey;"
+ oncommand="updateTextbox(this);"/>
+ </hbox>
+ </groupbox>
+ </vbox>
+ </tabpanels>
+ </tabbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-navigator.js b/comm/suite/components/pref/content/pref-navigator.js
new file mode 100644
index 0000000000..5ff271dc2b
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-navigator.js
@@ -0,0 +1,262 @@
+/* -*- 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/. */
+
+const {ShellService} = ChromeUtils.import("resource:///modules/ShellService.jsm");
+
+// The contents of this file will be loaded into the scope of the object
+// <prefpane id="navigator_pane">!
+
+// platform integration
+const PFINT_NOT_DEFAULT = 0;
+const PFINT_DEFAULT = 1;
+const PFINT_PENDING = 2;
+
+
+// put "global" definitions here for easy reference
+var gDefaultHomePage = "";
+var gHomePagePrefPeak = 0;
+var gPreferences = null;
+
+
+// <preferences> access helper methods
+function GetHomePagePrefCount()
+{
+ return document.getElementById("browser.startup.homepage.count").value;
+}
+
+function SetHomePagePrefCount(aCount)
+{
+ document.getElementById("browser.startup.homepage.count").value = aCount;
+}
+
+function GetHomePagePrefName(aIndex)
+{
+ var prefname = "browser.startup.homepage";
+ if (aIndex > 0)
+ prefname += "." + aIndex;
+ return prefname;
+}
+
+function GetHomePagePref(aIndex)
+{
+ // return the <preference> at aIndex
+ return document.getElementById(GetHomePagePrefName(aIndex));
+}
+
+function AddHomePagePref(aIndex)
+{
+ // create new <preference> for aIndex
+ var pref = document.createElement("preference");
+ var prefname = GetHomePagePrefName(aIndex);
+ pref.setAttribute("id", prefname);
+ pref.setAttribute("name", prefname);
+ pref.setAttribute("type", "wstring");
+ gPreferences.appendChild(pref);
+ return pref;
+}
+
+// homepage group textbox helper methods
+function GetHomePageGroup()
+{
+ return document.getElementById("browserStartupHomepage").value;
+}
+
+function SetHomePageValue(aValue)
+{
+ document.getElementById("browserStartupHomepage").value = aValue;
+}
+
+// helper methods for reading current page URIs
+function GetMostRecentBrowser()
+{
+ var browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ return browserWindow && browserWindow.getBrowser();
+}
+
+function GetCurrentPage()
+{
+ var tabbrowser = GetMostRecentBrowser();
+ return tabbrowser && tabbrowser.currentURI.spec || ""; // ensure string
+}
+
+function GetCurrentGroup()
+{
+ var uris = [];
+ var tabbrowser = GetMostRecentBrowser();
+ if (tabbrowser)
+ {
+ var browsers = tabbrowser.browsers;
+ var browsersLen = browsers.length;
+ for (var i = 0; i < browsersLen; ++i)
+ uris[i] = browsers[i].currentURI.spec;
+ }
+ return uris.join("\n");
+}
+
+// synchronize button states with current input
+function CanonifyURLList(aList)
+{
+ return (aList + "\n").replace(/\n+/g, "\n");
+}
+
+function UpdateHomePageButtons()
+{
+ var homePageGroup = CanonifyURLList(GetHomePageGroup());
+ var currentPage = CanonifyURLList(GetCurrentPage());
+ var currentGroup = CanonifyURLList(GetCurrentGroup());
+
+ // disable "current page" button if current page is already the homepage
+ var currentPageButton = document.getElementById("browserUseCurrent");
+ currentPageButton.disabled = (homePageGroup == currentPage) ||
+ (currentPage == "\n");
+
+ // disable "current group" button if current group already set or no group
+ var currentGroupButton = document.getElementById("browserUseCurrentGroup");
+ currentGroupButton.disabled = (homePageGroup == currentGroup) ||
+ (currentGroup == currentPage);
+
+ // disable "restore" button if homepage hasn't changed
+ var restoreButton = document.getElementById("browserUseDefault");
+ restoreButton.disabled = (homePageGroup == gDefaultHomePage);
+}
+
+function UpdateHomePagePrefs()
+{
+ // update the list of <preference>s to the current settings
+ var newCount = 1; // current number of homepages
+ var homePageGroup = CanonifyURLList(GetHomePageGroup()).split("\n");
+ GetHomePagePref(0).value = "about:blank"; // in case it's empty
+ if (homePageGroup[0])
+ {
+ // we have at least one homepage
+ // (the last index is always empty due to canonification)
+ newCount = homePageGroup.length - 1
+ for (var i = 0; i < newCount; ++i)
+ {
+ var pref = GetHomePagePref(i) || AddHomePagePref(i);
+ pref.value = homePageGroup[i];
+ }
+ }
+
+ // work around bug 410562:
+ // reset unneeded preferences on dialogaccept only
+
+ // update pref count watermark before setting new number of homepages
+ var alreadyRequested = (gHomePagePrefPeak > 0);
+ var oldCount = GetHomePagePrefCount();
+ if (gHomePagePrefPeak < oldCount)
+ gHomePagePrefPeak = oldCount;
+ SetHomePagePrefCount(newCount);
+
+ var needCleanup = (newCount < gHomePagePrefPeak);
+ if (document.documentElement.instantApply)
+ {
+ // throw away unneeded preferences now
+ if (needCleanup)
+ HomePagePrefCleanup();
+ }
+ else if (needCleanup != alreadyRequested)
+ {
+ // cleanup necessity changed
+ if (needCleanup)
+ {
+ // register OK handler for the capturing phase
+ window.addEventListener("dialogaccept", this.HomePagePrefCleanup, true);
+ }
+ else
+ {
+ // no cleanup necessary, remove OK handler
+ window.removeEventListener("dialogaccept", this.HomePagePrefCleanup, true);
+ }
+ }
+}
+
+function HomePagePrefCleanup()
+{
+ // remove the old user prefs values that we didn't overwrite
+ var count = GetHomePagePrefCount();
+ for (var j = count; j < gHomePagePrefPeak; ++j)
+ {
+ // clear <preference>
+ var pref = GetHomePagePref(j);
+ pref.valueFromPreferences = undefined;
+ pref.remove();
+ }
+ gHomePagePrefPeak = 0; // cleanup done
+}
+
+function UpdateHomePageListFromInput()
+{
+ UpdateHomePagePrefs();
+ UpdateHomePageButtons();
+}
+
+function UpdateHomePageList(aSingleURL)
+{
+ // write single URL into input box and set it as the list of homepages
+ SetHomePageValue(aSingleURL);
+ UpdateHomePageListFromInput();
+}
+
+function SelectFile()
+{
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+ let title = document.getElementById("bundle_prefutilities")
+ .getString("choosehomepage");
+ fp.init(window, title, Ci.nsIFilePicker.modeOpen);
+ fp.appendFilters(Ci.nsIFilePicker.filterAll |
+ Ci.nsIFilePicker.filterText |
+ Ci.nsIFilePicker.filterXML |
+ Ci.nsIFilePicker.filterHTML |
+ Ci.nsIFilePicker.filterImages);
+
+ fp.open(rv => {
+ if (rv == Ci.nsIFilePicker.returnOK && fp.fileURL.spec &&
+ fp.fileURL.spec.length > 0) {
+ UpdateHomePageList(fp.fileURL.spec);
+ }
+ });
+}
+
+function SetHomePageToCurrentPage()
+{
+ UpdateHomePageList(GetCurrentPage());
+}
+
+function SetHomePageToCurrentGroup()
+{
+ UpdateHomePageList(GetCurrentGroup());
+}
+
+function SetHomePageToDefaultPage()
+{
+ UpdateHomePageList(gDefaultHomePage);
+}
+
+function Startup()
+{
+ // homepage groups can have an arbitrary number of <preference>s,
+ // except for the default (0), thus we create them manually here
+ gPreferences = document.getElementById("navigator_preferences");
+ var count = GetHomePagePrefCount();
+ var homePageGroup = GetHomePagePref(0).value + "\n";
+ for (var i = 1; i < count; ++i)
+ homePageGroup += AddHomePagePref(i).value + "\n";
+ gDefaultHomePage = CanonifyURLList(GetHomePagePref(0).defaultValue);
+ SetHomePageValue(homePageGroup);
+ UpdateHomePageButtons();
+}
+
+function SwitchPage(aIndex)
+{
+ document.getElementById("behaviourDeck").selectedIndex = aIndex;
+}
+
+function WriteConcurrentTabs()
+{
+ var val = document.getElementById("maxConcurrentTabsGroup").value;
+ return val > 0 ? document.getElementById("maxConcurrentTabs").value : val;
+}
diff --git a/comm/suite/components/pref/content/pref-navigator.xul b/comm/suite/components/pref/content/pref-navigator.xul
new file mode 100644
index 0000000000..2b9888419f
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-navigator.xul
@@ -0,0 +1,188 @@
+<?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 % navigatorDTD SYSTEM "chrome://communicator/locale/pref/pref-navigator.dtd"> %navigatorDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="navigator_pane"
+ label="&pref.navigator.title;"
+ script="chrome://communicator/content/pref/pref-navigator.js">
+
+ <preferences id="navigator_preferences">
+ <preference id="browser.startup.page"
+ name="browser.startup.page"
+ type="int"/>
+ <preference id="browser.windows.loadOnNewWindow"
+ name="browser.windows.loadOnNewWindow"
+ type="int"/>
+ <preference id="browser.tabs.loadOnNewTab"
+ name="browser.tabs.loadOnNewTab"
+ type="int"/>
+ <preference id="browser.startup.homepage"
+ name="browser.startup.homepage"
+ type="wstring"/>
+ <preference id="browser.startup.homepage.count"
+ name="browser.startup.homepage.count"
+ type="int"/>
+ <preference id="browser.sessionstore.max_concurrent_tabs"
+ name="browser.sessionstore.max_concurrent_tabs"
+ type="int"/>
+ <preference id="browser.chrome.site_icons"
+ name="browser.chrome.site_icons"
+ type="bool"/>
+ <preference id="browser.chrome.favicons"
+ name="browser.chrome.favicons"
+ type="bool"/>
+ <preference id="pref.browser.homepage.disable_button.select_file"
+ name="pref.browser.homepage.disable_button.select_file"
+ type="bool"/>
+ <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.current_group"
+ name="pref.browser.homepage.disable_button.current_group"
+ type="bool"/>
+ <preference id="pref.browser.homepage.disable_button.default_page"
+ name="pref.browser.homepage.disable_button.default_page"
+ type="bool"/>
+ </preferences>
+
+ <hbox>
+ <!-- navigator startup / new window / new tab behaviour -->
+ <groupbox flex="1">
+ <caption align="center">
+ <label value="&navRadio.label;"
+ accesskey="&navRadio.accesskey;"
+ control="selectDisplayOn"/>
+ <menulist id="selectDisplayOn"
+ oncommand="SwitchPage(this.selectedIndex);">
+ <menupopup>
+ <menuitem label="&navStartPageMenu.label;"/>
+ <menuitem label="&newWinPageMenu.label;"/>
+ <menuitem label="&newTabPageMenu.label;"/>
+ </menupopup>
+ </menulist>
+ </caption>
+ <deck id="behaviourDeck" flex="1">
+ <radiogroup id="startupPage" preference="browser.startup.page">
+ <radio value="0"
+ label="&blankPageRadio.label;"
+ accesskey="&blankPageRadio.accesskey;"/>
+ <radio value="1"
+ label="&homePageRadio.label;"
+ accesskey="&homePageRadio.accesskey;"/>
+ <radio value="2"
+ label="&lastPageRadio.label;"
+ accesskey="&lastPageRadio.accesskey;"/>
+ <radio value="3"
+ label="&restoreSessionRadio.label;"
+ accesskey="&restoreSessionRadio.accesskey;"/>
+ </radiogroup>
+ <radiogroup id="newWinPage"
+ preference="browser.windows.loadOnNewWindow">
+ <radio value="0"
+ label="&blankPageRadio.label;"
+ accesskey="&blankPageRadio.accesskey;"/>
+ <radio value="1"
+ label="&homePageRadio.label;"
+ accesskey="&homePageRadio.accesskey;"/>
+ <radio value="2"
+ label="&lastPageRadio.label;"
+ accesskey="&lastPageRadio.accesskey;"/>
+ </radiogroup>
+ <radiogroup id="newTabPage" preference="browser.tabs.loadOnNewTab">
+ <radio value="0"
+ label="&blankPageRadio.label;"
+ accesskey="&blankPageRadio.accesskey;"/>
+ <radio value="1"
+ label="&homePageRadio.label;"
+ accesskey="&homePageRadio.accesskey;"/>
+ <radio value="2"
+ label="&lastPageRadio.label;"
+ accesskey="&lastPageRadio.accesskey;"/>
+ </radiogroup>
+ </deck>
+ </groupbox>
+
+ <!-- session restore background tabs -->
+ <groupbox flex="1">
+ <caption label="&restoreSessionIntro.label;"/>
+ <radiogroup id="maxConcurrentTabsGroup"
+ align="start"
+ preference="browser.sessionstore.max_concurrent_tabs"
+ onsyncfrompreference="var val = document.getElementById(this.getAttribute('preference')).value; return val > 0 ? 3 : val;"
+ onsynctopreference="return document.getElementById('navigator_pane').WriteConcurrentTabs();">
+ <radio value="-1"
+ label="&restoreImmediately.label;"
+ accesskey="&restoreImmediately.accesskey;"/>
+ <hbox align="center">
+ <radio id="restoreTabs"
+ value="3"
+ onclick="this.nextSibling.focus();"
+ label="&restoreTabs.label;"
+ accesskey="&restoreTabs.accesskey;"/>
+ <textbox id="maxConcurrentTabs"
+ type="number"
+ size="2"
+ min="1"
+ value="3"
+ aria-labelledby="restoreTabs maxConcurrentTabs restoreTabsAtATime"
+ preference="browser.sessionstore.max_concurrent_tabs"
+ onsyncfrompreference="var pref = document.getElementById(this.getAttribute('preference')); var val = pref.value; var valid = val > 0; this.disabled = pref.locked || !valid; return valid ? val : this.value;"
+ onsynctopreference="return document.getElementById('navigator_pane').WriteConcurrentTabs();"/>
+ <label id="restoreTabsAtATime" value="&restoreTabsAtATime.label;">
+ <observes element="maxConcurrentTabsGroup" attribute="disabled"/>
+ </label>
+ </hbox>
+ <radio value="0"
+ label="&restoreDeferred.label;"
+ accesskey="&restoreDeferred.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+ </hbox>
+
+ <groupbox id="siteIconPreferences">
+ <caption label="&siteIcons.label;"/>
+
+ <checkbox id="useSiteIcons"
+ label="&useSiteIcons.label;"
+ accesskey="&useSiteIcons.accesskey;"
+ preference="browser.chrome.site_icons"/>
+ <checkbox id="useFavIcons"
+ label="&useFavIcons.label;"
+ accesskey="&useFavIcons.accesskey;"
+ preference="browser.chrome.favicons"/>
+ </groupbox>
+
+ <!-- homepage specification -->
+ <description>&homePageIntro.label;</description>
+ <hbox>
+ <textbox id="browserStartupHomepage" class="uri-element" flex="1"
+ multiline="true" wrap="off" timeout="500"
+ oninput="UpdateHomePageListFromInput();"/>
+ <vbox>
+ <button label="&browseFile.label;" accesskey="&browseFile.accesskey;"
+ oncommand="SelectFile();"
+ id="browserChooseFile"
+ preference="pref.browser.homepage.disable_button.select_file"/>
+ <button label="&useCurrent.label;" accesskey="&useCurrent.accesskey;"
+ oncommand="SetHomePageToCurrentPage();"
+ id="browserUseCurrent"
+ preference="pref.browser.homepage.disable_button.current_page"/>
+ <button label="&useCurrentGroup.label;" accesskey="&useCurrentGroup.accesskey;"
+ oncommand="SetHomePageToCurrentGroup();"
+ id="browserUseCurrentGroup"
+ preference="pref.browser.homepage.disable_button.current_group"/>
+ <button label="&useDefault.label;" accesskey="&useDefault.accesskey;"
+ oncommand="SetHomePageToDefaultPage();"
+ id="browserUseDefault"
+ preference="pref.browser.homepage.disable_button.default_page"/>
+ </vbox>
+ </hbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-offlineapps.js b/comm/suite/components/pref/content/pref-offlineapps.js
new file mode 100644
index 0000000000..db7c44cb81
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-offlineapps.js
@@ -0,0 +1,178 @@
+/* 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 {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function Startup()
+{
+ OfflineAppsObserver.init();
+
+ let always = document.getElementById("offline-apps.allow_by_default").value;
+ UpdateNotifyBox(always);
+}
+
+var OfflineAppsObserver = {
+
+ init: function offlineAppsInit() {
+ this.update();
+ Services.obs.addObserver(this, "perm-changed");
+ window.addEventListener("unload", this);
+ },
+
+ update: function offlineAppsUpdate() {
+ UpdateActualCacheSize();
+ UpdateOfflineApps();
+ },
+
+ observe: function offlineAppsObserve(aSubject, aTopic, aData) {
+ if (aTopic == "perm-changed")
+ this.update();
+ },
+
+ handleEvent: function offlineAppsEvent(aEvent) {
+ if (aEvent.type == "unload") {
+ window.removeEventListener("unload", this);
+ Services.obs.removeObserver(this, "perm-changed");
+ }
+ }
+}
+
+function UpdateActualCacheSize()
+{
+ var visitor = {
+ onCacheStorageInfo: function(aEntryCount, aTotalSize)
+ {
+ let actualSizeLabel = document.getElementById("offlineAppSizeInfo");
+ let sizeStrings = DownloadUtils.convertByteUnits(aTotalSize);
+ let bundle = document.getElementById("bundle_prefutilities");
+ let sizeStr = bundle.getFormattedString("offlineAppSizeInfo",
+ sizeStrings);
+ actualSizeLabel.textContent = sizeStr;
+ },
+
+ onCacheEntryInfo: function(entryInfo)
+ {
+ },
+
+ onCacheEntryVisitCompleted: function()
+ {
+ }
+ };
+
+ Services.cache2.appCacheStorage(Services.loadContextInfo.default, null)
+ .asyncVisitStorage(visitor, false);
+}
+
+/**
+ * Clears the application cache.
+ */
+var callback = {
+ onCacheEntryDoomed: function(aResult) {
+ UpdateActualCacheSize();
+ UpdateOfflineApps();
+ }
+};
+
+function ClearOfflineAppCache()
+{
+ try {
+ Services.cache2.appCacheStorage(Services.loadContextInfo.default, null)
+ .asyncEvictStorage(callback);
+ } catch(ex) {}
+}
+
+function UpdateNotifyBox(aValue)
+{
+ EnableElementById("offlineNotifyAsk", !aValue);
+
+ // remove this once bug 934457 and bug 1024832 are fixed
+ document.getElementById("offlineNotifyPermissions").disabled = aValue;
+}
+
+function _getOfflineAppUsage(aPermission)
+{
+ var appCache = Cc["@mozilla.org/network/application-cache-service;1"]
+ .getService(Ci.nsIApplicationCacheService);
+ var groups = appCache.getGroups();
+
+ var usage = 0;
+ for (let i = 0; i < groups.length; i++) {
+ let uri = Services.io.newURI(groups[i]);
+ if (aPermission.matchesURI(uri, true))
+ usage += appCache.getActiveCache(groups[i]).usage;
+ }
+ return usage;
+}
+
+/**
+ * Updates the list of offline applications.
+ */
+function UpdateOfflineApps()
+{
+ var list = document.getElementById("offlineAppsList");
+ while (list.hasChildNodes())
+ list.lastChild.remove();
+
+ var bundle = document.getElementById("bundle_prefutilities");
+ var pm = Services.perms;
+ var enumerator = pm.enumerator;
+
+ while (enumerator.hasMoreElements()) {
+ let perm = enumerator.getNext()
+ .QueryInterface(Ci.nsIPermission);
+ if (perm.type != "offline-app" ||
+ perm.capability != pm.ALLOW_ACTION)
+ continue;
+
+ let usage = _getOfflineAppUsage(perm);
+ let row = document.createElement("listitem");
+ row.setAttribute("host", perm.principal.URI.host);
+ let converted = DownloadUtils.convertByteUnits(usage);
+ row.setAttribute("usage", bundle.getFormattedString("offlineAppUsage",
+ converted));
+ list.appendChild(row);
+ }
+}
+
+function OfflineAppSelected(aList)
+{
+ document.getElementById("offlineAppsListRemove")
+ .setAttribute("disabled", !aList.selectedItem);
+}
+
+function RemoveOfflineApp()
+{
+ var list = document.getElementById("offlineAppsList");
+ var item = list.selectedItem;
+ var host = item.getAttribute("host");
+
+ var flags = Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1;
+
+ var bundle = document.getElementById("bundle_prefutilities");
+ var title = bundle.getString("offlineAppRemoveTitle");
+ var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]);
+ var confirm = bundle.getString("offlineAppRemoveConfirm");
+ if (Services.prompt.confirmEx(window, title, prompt, flags, confirm,
+ null, null, null, {}))
+ return;
+
+ // clear offline cache entries
+ var appCache = Cc["@mozilla.org/network/application-cache-service;1"]
+ .getService(Ci.nsIApplicationCacheService);
+ var groups = appCache.getGroups();
+ for (let i = 0; i < groups.length; i++) {
+ var uri = Services.io.newURI(groups[i]);
+ if (uri.asciiHost == host)
+ appCache.getActiveCache(groups[i]).discard();
+ }
+
+ // remove the permission
+ // Services.perms.remove(host, "offline-app");
+
+ UpdateOfflineApps();
+ OfflineAppSelected(list);
+ UpdateActualCacheSize();
+}
diff --git a/comm/suite/components/pref/content/pref-offlineapps.xul b/comm/suite/components/pref/content/pref-offlineapps.xul
new file mode 100644
index 0000000000..d12a26c808
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-offlineapps.xul
@@ -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 % prefOfflineCacheDTD SYSTEM "chrome://communicator/locale/pref/pref-offlineapps.dtd">
+ %prefOfflineCacheDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="offlineapps_pane"
+ label="&pref.offlineapps.title;"
+ script="chrome://communicator/content/pref/pref-offlineapps.js">
+
+ <preferences>
+ <preference id="offline-apps.allow_by_default"
+ name="offline-apps.allow_by_default"
+ type="bool"
+ onchange="UpdateNotifyBox(this.value);"/>
+ <preference id="browser.offline-apps.notify"
+ name="browser.offline-apps.notify"
+ type="bool"/>
+ </preferences>
+
+ <groupbox id="offlineGroup" flex="1">
+ <caption label="&pref.offlineCache.caption;"/>
+
+ <hbox align="center">
+ <label id="offlineAppSizeInfo" flex="1"/>
+ <button id="clearOfflineAppCache"
+ icon="clear"
+ label="&clearOfflineAppCache.label;"
+ accesskey="&clearOfflineAppCache.accesskey;"
+ oncommand="ClearOfflineAppCache();"/>
+ </hbox>
+ <radiogroup id="offlineDefault"
+ preference="offline-apps.allow_by_default">
+ <radio id="offlineAlwaysAllow"
+ value="true"
+ label="&offlineAlwaysAllow.label;"
+ accesskey="&offlineAlwaysAllow.accesskey;"/>
+ <hbox align="center">
+ <radio id="offlineExplicit"
+ flex="1"
+ value="false"
+ label="&offlineExplicit.label;"
+ accesskey="&offlineExplicit.accesskey;"/>
+ <button id="offlineNotifyPermissions"
+ label="&offlineNotifyPermissions.label;"
+ accesskey="&offlineNotifyPermissions.accesskey;"
+ oncommand="toDataManager('|permissions');"/>
+ </hbox>
+ </radiogroup>
+ <checkbox id="offlineNotifyAsk"
+ class="indent"
+ label="&offlineNotifyAsk.label;"
+ accesskey="&offlineNotifyAsk.accesskey;"
+ preference="browser.offline-apps.notify"/>
+ <separator class="thin"/>
+ <hbox flex="1">
+ <vbox flex="1">
+ <label id="offlineAppsListLabel">&offlineAppsUsage.label;</label>
+ <listbox id="offlineAppsList"
+ flex="1"
+ aria-labelledby="offlineAppsListLabel"
+ onselect="OfflineAppSelected(this);">
+ </listbox>
+ </vbox>
+ <vbox pack="end">
+ <button id="offlineAppsListRemove"
+ disabled="true"
+ label="&offlineAppsListRemove.label;"
+ accesskey="&offlineAppsListRemove.accesskey;"
+ oncommand="RemoveOfflineApp();"/>
+ </vbox>
+ </hbox>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-popups.js b/comm/suite/components/pref/content/pref-popups.js
new file mode 100644
index 0000000000..dc8a2b42c2
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-popups.js
@@ -0,0 +1,95 @@
+/* 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 gSoundUrlPref;
+
+function Startup()
+{
+ gSoundUrlPref = document.getElementById("privacy.popups.sound_url");
+
+ SetLists();
+
+ SetButtons();
+}
+
+function SetLists()
+{
+ const kPopupType = "popup";
+
+ var pref = document.getElementById("privacy.popups.remove_blacklist");
+ if (pref.value)
+ {
+ var enumerator = Services.perms.enumerator;
+ var uris = [];
+
+ while (enumerator.hasMoreElements())
+ {
+ var permission = enumerator.getNext();
+ if (permission instanceof Ci.nsIPermission)
+ {
+ if ((permission.type == kPopupType) &&
+ (permission.capability == Ci.nsIPermissionManager.DENY_ACTION))
+ uris.push(permission.principal.URI);
+ }
+ }
+
+ for (var i in uris)
+ Services.perms.remove(uris[i], kPopupType);
+
+ pref.value = false;
+ }
+
+ pref = document.getElementById("privacy.popups.prefill_whitelist");
+ if (pref.value)
+ {
+ try
+ {
+ var whitelist = document.getElementById("privacy.popups.default_whitelist").value;
+ var hosts = whitelist.split(",");
+
+ for (var i in hosts)
+ {
+ var host = "http://" + hosts[i];
+ var uri = Services.io.newURI(host);
+ Services.perms.add(uri, kPopupType, true);
+ }
+ }
+ catch (ex) {}
+
+ pref.value = false;
+ }
+}
+
+function SetButtons()
+{
+ var prefString = document.getElementById("popupPolicy")
+ .getAttribute("preference");
+ var enable = document.getElementById(prefString).value;
+ EnableElementById("exceptionsButton", enable, false);
+ EnableElementById("displayIcon", enable, false);
+ EnableElementById("displayPopupsNotification", enable, false);
+
+ var element = document.getElementById("playSound");
+ EnableElement(element, enable, false);
+
+ prefString = element.getAttribute("preference");
+ EnableSoundRadio(enable && document.getElementById(prefString).value);
+}
+
+function EnableSoundRadio(aSoundChecked)
+{
+ const kCustomSound = 1;
+
+ var element = document.getElementById("popupSoundType");
+ EnableElement(element, aSoundChecked, false);
+ var pref = document.getElementById(element.getAttribute("preference"));
+ EnableSoundUrl(aSoundChecked && (pref.value == kCustomSound));
+}
+
+function EnableSoundUrl(aCustomSelected)
+{
+ EnableElementById("playSoundUrl", aCustomSelected, false);
+ EnableElementById("selectSound", aCustomSelected, false);
+ EnableElementById("playSoundButton", aCustomSelected, false);
+}
diff --git a/comm/suite/components/pref/content/pref-popups.xul b/comm/suite/components/pref/content/pref-popups.xul
new file mode 100644
index 0000000000..78f7a75e57
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-popups.xul
@@ -0,0 +1,132 @@
+<?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 % prefPopupsDTD SYSTEM "chrome://communicator/locale/pref/pref-popups.dtd">
+%prefPopupsDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="popups_pane"
+ label="&pref.popups.title;"
+ script="chrome://communicator/content/pref/pref-popups.js">
+ <preferences id="popups_preferences">
+ <preference id="dom.disable_open_during_load"
+ name="dom.disable_open_during_load"
+ type="bool"
+ onchange="SetButtons();"/>
+ <preference id="pref.advanced.popups.disable_button.view_popups"
+ name="pref.advanced.popups.disable_button.view_popups"
+ type="bool"/>
+ <preference id="privacy.popups.sound_enabled"
+ name="privacy.popups.sound_enabled"
+ type="bool"
+ onchange="EnableSoundRadio(this.value);"/>
+ <preference id="privacy.popups.sound_type"
+ name="privacy.popups.sound_type"
+ type="int"
+ onchange="EnableSoundUrl(this.value == 1);"/>
+ <preference id="privacy.popups.sound_url"
+ name="privacy.popups.sound_url"
+ type="string"
+ onchange="EnableElementById('previewSound', true, false);"/>
+ <preference id="pref.advanced.popups.disable_button.select_sound"
+ name="pref.advanced.popups.disable_button.select_sound"
+ type="bool"/>
+ <preference id="pref.advanced.popups.disable_button.preview_sound"
+ name="pref.advanced.popups.disable_button.preview_sound"
+ type="bool"/>
+ <preference id="privacy.popups.statusbar_icon_enabled"
+ name="privacy.popups.statusbar_icon_enabled"
+ type="bool"/>
+ <preference id="privacy.popups.showBrowserMessage"
+ name="privacy.popups.showBrowserMessage"
+ type="bool"/>
+ <preference id="privacy.popups.prefill_whitelist"
+ name="privacy.popups.prefill_whitelist"
+ type="bool"/>
+ <preference id="privacy.popups.remove_blacklist"
+ name="privacy.popups.remove_blacklist"
+ type="bool"/>
+ <preference id="privacy.popups.default_whitelist"
+ name="privacy.popups.default_whitelist"
+ type="string"/>
+ </preferences>
+
+ <groupbox id="popupsArea">
+ <caption label="&pref.popups.caption;"/>
+
+ <hbox>
+ <checkbox id="popupPolicy"
+ label="&popupBlock.label;"
+ accesskey="&popupBlock.accesskey;"
+ preference="dom.disable_open_during_load"/>
+ <spacer flex="1"/>
+ <button id="exceptionsButton"
+ label="&viewPermissions.label;"
+ accesskey="&viewPermissions.accesskey;"
+ preference="pref.advanced.popups.disable_button.view_popups"
+ oncommand="toDataManager('|permissions');"/>
+ </hbox>
+ <separator class="thin"/>
+ <description id="whenBlock">&whenBlock.description;</description>
+ <hbox>
+ <checkbox id="playSound"
+ label="&playSound.label;"
+ accesskey="&playSound.accesskey;"
+ preference="privacy.popups.sound_enabled"/>
+ </hbox>
+ <hbox class="indent">
+ <radiogroup id="popupSoundType"
+ preference="privacy.popups.sound_type"
+ aria-labelledby="playSound">
+ <radio id="popupSystemSound"
+ class="iconic"
+ value="0"
+ label="&systemSound.label;"
+ accesskey="&systemSound.accesskey;"/>
+ <radio id="popupCustomSound"
+ class="iconic"
+ value="1"
+ label="&customSound.label;"
+ accesskey="&customSound.accesskey;"/>
+ </radiogroup>
+ </hbox>
+ <hbox class="indent">
+ <filefield id="playSoundUrl"
+ flex="1"
+ preference="privacy.popups.sound_url"
+ preference-editable="true"
+ onsyncfrompreference="return WriteSoundField(this, document.getElementById('popups_pane').gSoundUrlPref.value);"
+ aria-labelledby="popupCustomSound"/>
+ <button id="selectSound"
+ label="&selectSound.label;"
+ accesskey="&selectSound.accesskey;"
+ preference="pref.advanced.popups.disable_button.select_sound"
+ oncommand="SelectSound(gSoundUrlPref);"/>
+ <button id="playSoundButton"
+ label="&playSoundButton.label;"
+ accesskey="&playSoundButton.accesskey;"
+ preference="pref.advanced.popups.disable_button.preview_sound"
+ oncommand="PlaySound(gSoundUrlPref.value, false);"/>
+ </hbox>
+ <hbox>
+ <checkbox id="displayIcon"
+ label="&displayIcon.label;"
+ accesskey="&displayIcon.accesskey;"
+ preference="privacy.popups.statusbar_icon_enabled"/>
+ </hbox>
+ <hbox>
+ <checkbox id="displayPopupsNotification"
+ label="&displayNotification.label;"
+ accesskey="&displayNotification.accesskey;"
+ preference="privacy.popups.showBrowserMessage"/>
+ </hbox>
+ <separator class="thin"/>
+ <description>&popupNote.description;</description>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-privatedata.js b/comm/suite/components/pref/content/pref-privatedata.js
new file mode 100644
index 0000000000..ba7305bc41
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-privatedata.js
@@ -0,0 +1,30 @@
+/* -*- 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/. */
+
+function Startup() {
+ let pref = document.getElementById("privacy.sanitize.sanitizeOnShutdown");
+ updateClearOnShutdownBox(pref.valueFromPreferences);
+}
+
+/**
+ * Disable/enable clear on shutdown items in dialog depending on general pref
+ * to clear on shutdown.
+ */
+function updateClearOnShutdownBox(aDisable) {
+ let clearOnShutdownBox = document.getElementById("clearOnShutdownBox");
+ for (let childNode of clearOnShutdownBox.childNodes) {
+ childNode.disabled = !aDisable;
+ }
+}
+
+/**
+ * Displays a dialog from which individual parts of private data may be
+ * cleared.
+ */
+function clearPrivateDataNow() {
+ Cc["@mozilla.org/suite/suiteglue;1"]
+ .getService(Ci.nsISuiteGlue)
+ .sanitize(window);
+}
diff --git a/comm/suite/components/pref/content/pref-privatedata.xul b/comm/suite/components/pref/content/pref-privatedata.xul
new file mode 100644
index 0000000000..97e236def8
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-privatedata.xul
@@ -0,0 +1,181 @@
+<?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 % prefPrivateDataDTD SYSTEM "chrome://communicator/locale/pref/pref-privatedata.dtd">
+ %prefPrivateDataDTD;
+ <!ENTITY % prefSanitizeDTD SYSTEM "chrome://communicator/locale/sanitize.dtd">
+ %prefSanitizeDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="privatedata_pane" label="&pref.privatedata.title;"
+ script="chrome://communicator/content/pref/pref-privatedata.js">
+ <preferences id="privatedata_preferences">
+ <!-- Clear Private Data -->
+ <preference id="privacy.sanitize.sanitizeOnShutdown"
+ name="privacy.sanitize.sanitizeOnShutdown"
+ type="bool"
+ onchange="updateClearOnShutdownBox(this.value);"/>
+ <!-- Clear Private Data on shutdown -->
+ <preference id="privacy.clearOnShutdown.history"
+ name="privacy.clearOnShutdown.history"
+ type="bool"/>
+ <preference id="privacy.clearOnShutdown.urlbar"
+ name="privacy.clearOnShutdown.urlbar"
+ type="bool"/>
+ <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"/>
+
+ <!-- Clear Private Data manually -->
+ <preference id="privacy.cpd.history"
+ name="privacy.cpd.history"
+ type="bool"/>
+ <preference id="privacy.cpd.urlbar"
+ name="privacy.cpd.urlbar"
+ type="bool"/>
+ <preference id="privacy.cpd.formdata"
+ name="privacy.cpd.formdata"
+ type="bool"/>
+ <preference id="privacy.cpd.passwords"
+ name="privacy.cpd.passwords"
+ type="bool"/>
+ <preference id="privacy.cpd.downloads"
+ name="privacy.cpd.downloads"
+ type="bool"/>
+ <preference id="privacy.cpd.cookies"
+ name="privacy.cpd.cookies"
+ type="bool"/>
+ <preference id="privacy.cpd.cache"
+ name="privacy.cpd.cache"
+ type="bool"/>
+ <preference id="privacy.cpd.offlineApps"
+ name="privacy.cpd.offlineApps"
+ type="bool"/>
+ <preference id="privacy.cpd.sessions"
+ name="privacy.cpd.sessions"
+ type="bool"/>
+ <preference id="privacy.cpd.siteSettings"
+ name="privacy.cpd.siteSettings"
+ type="bool"/>
+ </preferences>
+
+ <!-- Clear Private Data -->
+ <groupbox id="clearPrivateDataGroup">
+ <caption label="&clearPrivateData.label;"/>
+ <button id="clearDataNow" icon="clear"
+ label="&clearDataDialog.label;"
+ accesskey="&clearDataDialog.accesskey;"
+ oncommand="clearPrivateDataNow();"/>
+ <separator class="thin" />
+ <hbox id="clearDataBox" align="center">
+ <checkbox id="alwaysClear" flex="1"
+ label="&alwaysClear.label;"
+ accesskey="&alwaysClear.accesskey;"
+ preference="privacy.sanitize.sanitizeOnShutdown"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <label id="clearDataSettings"
+ value="&clearData.label;"/>
+
+ <hbox>
+ <groupbox id="clearCpdBox" flex="1">
+ <caption label="&clearData.cpd.label;"/>
+ <checkbox label="&itemHistory.label;"
+ accesskey="&itemHistory.accesskey;"
+ preference="privacy.cpd.history"/>
+ <checkbox label="&itemUrlBar.label;"
+ accesskey="&itemUrlBar.accesskey;"
+ preference="privacy.cpd.urlbar"/>
+ <checkbox label="&itemDownloads.label;"
+ accesskey="&itemDownloads.accesskey;"
+ preference="privacy.cpd.downloads"/>
+ <checkbox label="&itemFormSearchHistory.label;"
+ accesskey="&itemFormSearchHistory.accesskey;"
+ preference="privacy.cpd.formdata"/>
+ <checkbox label="&itemCache.label;"
+ accesskey="&itemCache.accesskey;"
+ preference="privacy.cpd.cache"/>
+ <checkbox label="&itemCookies.label;"
+ accesskey="&itemCookies.accesskey;"
+ preference="privacy.cpd.cookies"/>
+ <checkbox label="&itemOfflineApps.label;"
+ accesskey="&itemOfflineApps.accesskey;"
+ preference="privacy.cpd.offlineApps"/>
+ <checkbox label="&itemPasswords.label;"
+ accesskey="&itemPasswords.accesskey;"
+ preference="privacy.cpd.passwords"/>
+ <checkbox label="&itemSessions.label;"
+ accesskey="&itemSessions.accesskey;"
+ preference="privacy.cpd.sessions"/>
+ <checkbox label="&itemSitePreferences.label;"
+ accesskey="&itemSitePreferences.accesskey;"
+ preference="privacy.cpd.siteSettings"/>
+ </groupbox>
+
+ <groupbox id="clearOnShutdownBox" flex="1">
+ <caption label="&clearData.onShutdown.label;"/>
+ <checkbox label="&itemHistory.label;"
+ accesskey="&itemHistoryS.accesskey;"
+ preference="privacy.clearOnShutdown.history"/>
+ <checkbox label="&itemUrlBar.label;"
+ accesskey="&itemUrlBarS.accesskey;"
+ preference="privacy.clearOnShutdown.urlbar"/>
+ <checkbox label="&itemDownloads.label;"
+ accesskey="&itemDownloadsS.accesskey;"
+ preference="privacy.clearOnShutdown.downloads"/>
+ <checkbox label="&itemFormSearchHistory.label;"
+ accesskey="&itemFormSearchHistoryS.accesskey;"
+ preference="privacy.clearOnShutdown.formdata"/>
+ <checkbox label="&itemCache.label;"
+ accesskey="&itemCacheS.accesskey;"
+ preference="privacy.clearOnShutdown.cache"/>
+ <checkbox label="&itemCookies.label;"
+ accesskey="&itemCookiesS.accesskey;"
+ preference="privacy.clearOnShutdown.cookies"/>
+ <checkbox label="&itemOfflineApps.label;"
+ accesskey="&itemOfflineAppsS.accesskey;"
+ preference="privacy.clearOnShutdown.offlineApps"/>
+ <checkbox label="&itemPasswords.label;"
+ accesskey="&itemPasswordsS.accesskey;"
+ preference="privacy.clearOnShutdown.passwords"/>
+ <checkbox label="&itemSessions.label;"
+ accesskey="&itemSessionsS.accesskey;"
+ preference="privacy.clearOnShutdown.sessions"/>
+ <checkbox label="&itemSitePreferences.label;"
+ accesskey="&itemSitePreferencesS.accesskey;"
+ preference="privacy.clearOnShutdown.siteSettings"/>
+ </groupbox>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-proxies-advanced.xul b/comm/suite/components/pref/content/pref-proxies-advanced.xul
new file mode 100644
index 0000000000..69313f80e7
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-proxies-advanced.xul
@@ -0,0 +1,194 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE prefwindow SYSTEM "chrome://communicator/locale/pref/pref-proxies-advanced.dtd" >
+
+<prefwindow xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="advancedProxyPreferences"
+ type="child"
+ onload="AdvancedInit();"
+ title="&pref.proxies.advanced.title;"
+ persist="screenX screenY">
+
+ <script src="chrome://communicator/content/pref/pref-proxies.js"/>
+ <script src="chrome://communicator/content/pref/preferences.js"/>
+
+ <prefpane helpTopic="nav-prefs-advanced-proxy-advanced"
+ helpURI="chrome://communicator/locale/help/suitehelp.rdf">
+ <preferences>
+ <preference id="network.proxy.http"
+ name="network.proxy.http"
+ type="string"
+ onchange="DoProxyHostCopy(this.value);"/>
+ <preference id="network.proxy.http_port"
+ name="network.proxy.http_port"
+ type="int"
+ onchange="DoProxyPortCopy(this.value);"/>
+ <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.ftp"
+ name="network.proxy.ftp"
+ type="string"/>
+ <preference id="network.proxy.ftp_port"
+ name="network.proxy.ftp_port"
+ type="int"/>
+ <preference id="network.proxy.share_proxy_settings"
+ name="network.proxy.share_proxy_settings"
+ type="bool"
+ onchange="DoProxyCopy(this.value);"/>
+ <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"/>
+ <preference id="network.proxy.socks_remote_dns"
+ name="network.proxy.socks_remote_dns"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&protocols.caption;"/>
+ <description style="width: 1px;">&protocols.description;</description>
+
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+
+ <rows>
+ <row>
+ <hbox align="center" pack="end">
+ <label value="&http.label;"
+ accesskey="&http.accesskey;"
+ control="networkProxyHTTP"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxyHTTP"
+ preference="network.proxy.http"
+ flex="1"
+ class="uri-element"/>
+ <label value="&port.label;"
+ accesskey="&HTTPPort.accesskey;"
+ control="networkProxyHTTP_Port"/>
+ <textbox id="networkProxyHTTP_Port"
+ preference="network.proxy.http_port"
+ type="number"
+ max="65535"
+ size="5"/>
+ </hbox>
+ </row>
+
+ <row>
+ <spacer/>
+ <hbox>
+ <checkbox id="networkProxyShareSettings"
+ label="&reuseProxy.label;"
+ accesskey="&reuseProxy.accesskey;"
+ preference="network.proxy.share_proxy_settings"/>
+ </hbox>
+ </row>
+
+ <row>
+ <hbox align="center" pack="end">
+ <label value="&ssl.label;"
+ accesskey="&ssl.accesskey;"
+ control="networkProxySSL"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxySSL"
+ preference="network.proxy.ssl"
+ flex="1"
+ class="uri-element"/>
+ <label value="&port.label;"
+ accesskey="&SSLPort.accesskey;"
+ control="networkProxySSL_Port"/>
+ <textbox id="networkProxySSL_Port"
+ preference="network.proxy.ssl_port"
+ type="number"
+ max="65535"
+ size="5"/>
+ </hbox>
+ </row>
+
+ <row>
+ <hbox align="center" pack="end">
+ <label value="&ftp.label;" accesskey="&ftp.accesskey;"
+ control="networkProxyFTP"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxyFTP"
+ preference="network.proxy.ftp"
+ flex="1"
+ class="uri-element"/>
+ <label value="&port.label;"
+ accesskey="&FTPPort.accesskey;"
+ control="networkProxyFTP_Port"/>
+ <textbox id="networkProxyFTP_Port"
+ preference="network.proxy.ftp_port"
+ type="number"
+ max="65535"
+ size="5"/>
+ </hbox>
+ </row>
+
+ </rows>
+ </grid>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&socks.caption;"/>
+ <description style="width: 1px;">&socks.description;</description>
+
+ <hbox align="center" pack="end">
+ <label value="&socks.label;"
+ accesskey="&socks.accesskey;"
+ control="networkProxySOCKS"/>
+ <textbox id="networkProxySOCKS"
+ preference="network.proxy.socks"
+ flex="1"
+ class="uri-element"/>
+ <label value="&port.label;"
+ accesskey="&SOCKSport.accesskey;"
+ control="networkProxySOCKS_Port"/>
+ <textbox id="networkProxySOCKS_Port"
+ type="number"
+ preference="network.proxy.socks_port"
+ max="65535"
+ size="5"/>
+ </hbox>
+
+ <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>
+
+ <hbox align="left">
+ <checkbox id="networkProxySOCKSRemoteDNS"
+ label="&socksRemoteDNS.label;"
+ accesskey="&socksRemoteDNS.accesskey;"
+ preference="network.proxy.socks_remote_dns"/>
+ </hbox>
+
+ </groupbox>
+ </prefpane>
+</prefwindow>
diff --git a/comm/suite/components/pref/content/pref-proxies.js b/comm/suite/components/pref/content/pref-proxies.js
new file mode 100644
index 0000000000..5120c3f5d0
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-proxies.js
@@ -0,0 +1,188 @@
+/* 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 kNoProxy = 0;
+const kManualProxy = 1;
+const kAutoConfigProxy = 2;
+const kObsoleteProxy = 3;
+const kAutoDiscoverProxy = 4;
+const kSystemProxy = 5;
+
+var gInstantApply;
+var gHTTP;
+var gHTTPPort;
+var gSSL;
+var gSSLPort;
+var gFTP;
+var gFTPPort;
+var gAutoURL;
+var gProxyType;
+var gShareSettings;
+
+// Only used by main prefwindow
+function Startup()
+{
+ InitCommonGlobals();
+ gAutoURL = document.getElementById("network.proxy.autoconfig_url");
+ gProxyType = document.getElementById("network.proxy.type");
+
+ // Check for system proxy settings class and unhide UI if present
+ if ("@mozilla.org/system-proxy-settings;1" in Cc)
+ document.getElementById("systemPref").hidden = false;
+
+ // Calculate a sane default for network.proxy.share_proxy_settings.
+ if (gShareSettings.value == null)
+ gShareSettings.value = DefaultForShareSettingsPref();
+
+ // The pref value 3 (kObsoleteProxy) for network.proxy.type is unused to
+ // maintain backwards compatibility. Treat 3 (kObsoleteProxy) equally to
+ // 0 (kNoProxy). See bug 115720.
+ if (gProxyType.value == kObsoleteProxy)
+ gProxyType.value = kNoProxy;
+
+ DoEnabling();
+}
+
+// Only used by child prefwindow
+function AdvancedInit()
+{
+ InitCommonGlobals();
+ DoProxyCopy(gShareSettings.value);
+}
+
+function InitCommonGlobals()
+{
+ gInstantApply = document.documentElement.instantApply;
+ gHTTP = document.getElementById("network.proxy.http");
+ gHTTPPort = document.getElementById("network.proxy.http_port");
+ gSSL = document.getElementById("network.proxy.ssl");
+ gSSLPort = document.getElementById("network.proxy.ssl_port");
+ gFTP = document.getElementById("network.proxy.ftp");
+ gFTPPort = document.getElementById("network.proxy.ftp_port");
+ gShareSettings = document.getElementById("network.proxy.share_proxy_settings");
+}
+
+// Returns true if all protocol specific proxies and all their
+// ports are set to the same value, false otherwise.
+function DefaultForShareSettingsPref()
+{
+ return gHTTP.value == gSSL.value &&
+ gHTTP.value == gFTP.value &&
+ gHTTPPort.value == gSSLPort.value &&
+ gHTTPPort.value == gFTPPort.value;
+}
+
+function DoEnabling()
+{
+ // convenience arrays
+ var manual = ["networkProxyHTTP", "networkProxyHTTP_Port",
+ "networkProxyNone", "advancedButton"];
+ var auto = ["networkProxyAutoconfigURL", "autoReload"];
+
+ switch (gProxyType.value)
+ {
+ case kNoProxy:
+ case kAutoDiscoverProxy:
+ case kSystemProxy:
+ Disable(manual);
+ Disable(auto);
+ break;
+ case kManualProxy:
+ Disable(auto);
+ if (!gProxyType.locked)
+ EnableUnlockedElements(manual, true);
+ break;
+ case kAutoConfigProxy:
+ default:
+ Disable(manual);
+ if (!gProxyType.locked)
+ {
+ EnableElementById("networkProxyAutoconfigURL", true, false);
+ EnableUnlockedButton(gAutoURL);
+ }
+ break;
+ }
+}
+
+function Disable(aElementIds)
+{
+ for (var i = 0; i < aElementIds.length; i++)
+ document.getElementById(aElementIds[i]).setAttribute("disabled", "true");
+}
+
+function EnableUnlockedElements(aElementIds, aEnable)
+{
+ for (var i = 0; i < aElementIds.length; i++)
+ EnableElementById(aElementIds[i], aEnable, false);
+}
+
+function EnableUnlockedButton(aElement)
+{
+ var enable = gInstantApply ||
+ (aElement.valueFromPreferences == aElement.value);
+ EnableElementById("autoReload", enable, false);
+}
+
+function ReloadPAC() {
+ // This reloads the PAC URL stored in preferences.
+ // When not in instant apply mode, the button that calls this gets
+ // disabled if the preference and what is showing in the UI differ.
+ Cc["@mozilla.org/network/protocol-proxy-service;1"]
+ .getService().reloadPAC();
+}
+
+function FixProxyURL(aURL)
+{
+ try
+ {
+ aURL.value =
+ Services.uriFixup.createFixupURI(aURL.value,
+ Ci.nsIURIFixup.FIXUP_FLAG_NONE).spec;
+ }
+ catch (e) {}
+
+ if (!gInstantApply)
+ EnableUnlockedButton(aURL);
+}
+
+function OpenAdvancedDialog()
+{
+ document.documentElement.openSubDialog("chrome://communicator/content/pref/pref-proxies-advanced.xul",
+ "AdvancedProxyPreferences", null);
+}
+
+function DoProxyCopy(aChecked)
+{
+ DoProxyHostCopy(gHTTP.value);
+ DoProxyPortCopy(gHTTPPort.value);
+ var nonshare = ["networkProxySSL", "networkProxySSL_Port",
+ "networkProxyFTP", "networkProxyFTP_Port"];
+ EnableUnlockedElements(nonshare, !aChecked);
+}
+
+function DoProxyHostCopy(aValue)
+{
+ if (!gShareSettings.value)
+ return;
+
+ gSSL.value = aValue;
+ gFTP.value = aValue;
+}
+
+function DoProxyPortCopy(aValue)
+{
+ if (!gShareSettings.value)
+ return;
+
+ gSSLPort.value = aValue;
+ gFTPPort.value = aValue;
+}
+
+function UpdateProxies()
+{
+ var noProxiesPref = document.getElementById("network.proxy.no_proxies_on");
+
+ noProxiesPref.value = noProxiesPref.value.replace(/[;, \n]+/g, ", ")
+ .replace(/^, |, $/g, "");
+}
diff --git a/comm/suite/components/pref/content/pref-proxies.xul b/comm/suite/components/pref/content/pref-proxies.xul
new file mode 100644
index 0000000000..acd1a1f053
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-proxies.xul
@@ -0,0 +1,156 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-proxies.dtd">
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="proxies_pane"
+ label="&pref.proxies.title;"
+ script="chrome://communicator/content/pref/pref-proxies.js">
+ <preferences id="proxies_preferences">
+ <preference id="network.proxy.type"
+ name="network.proxy.type"
+ type="int"
+ onchange="DoEnabling();"/>
+ <preference id="network.proxy.autoconfig_url"
+ name="network.proxy.autoconfig_url"
+ type="string"/>
+ <preference id="pref.advanced.proxies.disable_button.reload"
+ name="pref.advanced.proxies.disable_button.reload"
+ type="bool"/>
+ <preference id="network.proxy.http"
+ name="network.proxy.http"
+ type="string"
+ onchange="DoProxyHostCopy(this.value);"/>
+ <preference id="network.proxy.http_port"
+ name="network.proxy.http_port"
+ type="int"
+ onchange="DoProxyPortCopy(this.value);"/>
+ <preference id="pref.advanced.proxies.disable_button.advanced"
+ name="pref.advanced.proxies.disable_button.advanced"
+ type="bool"/>
+ <preference id="network.proxy.no_proxies_on"
+ name="network.proxy.no_proxies_on"
+ type="string"/>
+ <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.ftp"
+ name="network.proxy.ftp"
+ type="string"/>
+ <preference id="network.proxy.ftp_port"
+ name="network.proxy.ftp_port"
+ type="int"/>
+ <preference id="network.proxy.share_proxy_settings"
+ name="network.proxy.share_proxy_settings"
+ type="bool"/>
+ </preferences>
+
+ <description>&pref.proxies.desc;</description>
+ <groupbox>
+ <caption label="&proxyTitle.label;"/>
+ <radiogroup id="networkProxyType"
+ preference="network.proxy.type"
+ align="stretch">
+ <vbox align="start">
+ <radio value="0"
+ label="&directTypeRadio.label;"
+ accesskey="&directTypeRadio.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="2"
+ label="&autoTypeRadio.label;"
+ accesskey="&autoTypeRadio.accesskey;"/>
+ </vbox>
+
+ <hbox class="indent" align="center">
+ <textbox id="networkProxyAutoconfigURL"
+ flex="1"
+ class="uri-element"
+ onchange="FixProxyURL(this);"
+ preference="network.proxy.autoconfig_url"/>
+ <button id="autoReload"
+ label="&reload.label;"
+ accesskey="&reload.accesskey;"
+ oncommand="ReloadPAC();"
+ preference="pref.advanced.proxies.disable_button.reload"/>
+ </hbox>
+
+ <vbox align="start">
+ <radio value="1"
+ label="&manualTypeRadio.label;"
+ accesskey="&manualTypeRadio.accesskey;"/>
+ </vbox>
+
+ <grid class="indent">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+
+ <rows>
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&http.label;"
+ accesskey="&http.accesskey;"
+ control="networkProxyHTTP"/>
+ </hbox>
+ <textbox id="networkProxyHTTP"
+ preference="network.proxy.http"
+ class="uri-element"/>
+ </row>
+
+ <row>
+ <hbox align="center" pack="end">
+ <label value="&port.label;"
+ accesskey="&HTTPPort.accesskey;"
+ control="networkProxyHTTP_Port"/>
+ </hbox>
+ <hbox align="center">
+ <textbox id="networkProxyHTTP_Port"
+ preference="network.proxy.http_port"
+ type="number"
+ max="65535"
+ size="5"/>
+ <spacer flex="1"/>
+ <button id="advancedButton"
+ label="&advanced.label;"
+ accesskey="&advanced.accesskey;"
+ align="end"
+ oncommand="OpenAdvancedDialog();"
+ preference="pref.advanced.proxies.disable_button.advanced"/>
+ </hbox>
+ </row>
+
+ <row align="baseline">
+ <hbox align="center" pack="end">
+ <label value="&noproxy.label;"
+ accesskey="&noproxy.accesskey;"
+ control="networkProxyNone"/>
+ </hbox>
+ <textbox id="networkProxyNone"
+ multiline="true"
+ preference="network.proxy.no_proxies_on"
+ class="uri-element"
+ onchange="UpdateProxies();"/>
+ </row>
+ <row>
+ <spacer/>
+ <description control="networkProxyNone">&noproxyExplain.label;
+ </description>
+ </row>
+ </rows>
+ </grid>
+ </radiogroup>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-scripts.js b/comm/suite/components/pref/content/pref-scripts.js
new file mode 100644
index 0000000000..eb16b4d62f
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-scripts.js
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+
+function setDisableState(id, state) {
+ var component = document.getElementById(id);
+ var preference = component.getAttribute("preference");
+ var isLocked = document.getElementById(preference).locked;
+ component.disabled = isLocked || state;
+}
+
+function changeDisabledState(state) {
+ //Set the states of the groupbox children state based on the "javascript enabled" checkbox value
+ setDisableState("allowWindowMoveResize", state);
+ setDisableState("allowWindowStatusChange", state);
+ setDisableState("allowWindowFlip", state);
+ setDisableState("allowHideStatusBar", state);
+ setDisableState("allowContextmenuDisable", state);
+}
+
+function javascriptEnabledChange() {
+ var javascriptDisabled = !document.getElementById('javascript.enabled').value;
+ changeDisabledState(javascriptDisabled);
+}
+
+function Startup() {
+ javascriptEnabledChange();
+}
diff --git a/comm/suite/components/pref/content/pref-scripts.xul b/comm/suite/components/pref/content/pref-scripts.xul
new file mode 100644
index 0000000000..9018f29cc4
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-scripts.xul
@@ -0,0 +1,92 @@
+<?xml version="1.0"?><!-- -*- Mode: HTML -*- -->
+<!-- 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 SYSTEM "chrome://communicator/locale/pref/pref-scripts.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="scripts_pane"
+ label="&pref.scripts2.title;"
+ script="chrome://communicator/content/pref/pref-scripts.js">
+
+ <preferences id="scripts_preferences">
+ <preference id="javascript.enabled"
+ name="javascript.enabled"
+ type="bool"
+ onchange="javascriptEnabledChange();"/>
+ <preference id="dom.disable_window_move_resize"
+ name="dom.disable_window_move_resize"
+ type="bool" inverted="true"/>
+ <preference id="dom.disable_window_flip"
+ name="dom.disable_window_flip"
+ type="bool" inverted="true"/>
+ <preference id="dom.disable_window_open_feature.status"
+ name="dom.disable_window_open_feature.status"
+ type="bool" inverted="true"/>
+ <preference id="dom.disable_window_status_change"
+ name="dom.disable_window_status_change"
+ type="bool" inverted="true"/>
+ <preference id="dom.event.contextmenu.enabled"
+ name="dom.event.contextmenu.enabled"
+ type="bool"/>
+ <preference id="browser.dom.window.dump.enabled"
+ name="browser.dom.window.dump.enabled"
+ type="bool"/>
+ <preference id="javascript.options.strict"
+ name="javascript.options.strict"
+ type="bool"/>
+ <preference id="javascript.options.showInConsole"
+ name="javascript.options.showInConsole"
+ type="bool"/>
+ </preferences>
+
+ <groupbox id="javascriptPreferences" flex="1">
+ <caption label="&enableJavaScript.label;"/>
+
+ <checkbox id="javascriptAllowNavigator"
+ label="&navigator.label;"
+ accesskey="&navigator.accesskey;"
+ preference="javascript.enabled"/>
+
+ <label control="AllowList"
+ class="indent"
+ value="&allowScripts.label;"
+ accesskey="&allowScripts.accesskey;"/>
+
+ <listbox id="AllowList" class="indent" flex="1">
+ <listitem type="checkbox" id="allowWindowMoveResize"
+ label="&allowWindowMoveResize.label;"
+ preference="dom.disable_window_move_resize"/>
+ <listitem type="checkbox" id="allowWindowFlip"
+ label="&allowWindowFlip.label;"
+ preference="dom.disable_window_flip"/>
+ <listitem type="checkbox" id="allowHideStatusBar"
+ label="&allowHideStatusBar.label;"
+ preference="dom.disable_window_open_feature.status"/>
+ <listitem type="checkbox" id="allowWindowStatusChange"
+ label="&allowWindowStatusChange.label;"
+ preference="dom.disable_window_status_change"/>
+ <listitem type="checkbox" id="allowContextmenuDisable"
+ label="&allowContextmenuDisable.label;"
+ preference="dom.event.contextmenu.enabled"/>
+ </listbox>
+ </groupbox>
+
+ <groupbox id="debugging">
+ <caption label="&debugging.label;"/>
+ <checkbox id="browserDOMWindowDumpEnabled"
+ label="&debugEnableDump.label;"
+ accesskey="&debugEnableDump.accesskey;"
+ preference="browser.dom.window.dump.enabled"/>
+ <checkbox id="javascriptOptionsStrict"
+ label="&debugStrictJavascript.label;"
+ accesskey="&debugStrictJavascript.accesskey;"
+ preference="javascript.options.strict"/>
+ <checkbox id="javascriptOptionsShowInConsole"
+ label="&debugConsoleJavascript.label;"
+ accesskey="&debugConsoleJavascript.accesskey;"
+ preference="javascript.options.showInConsole"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-search.js b/comm/suite/components/pref/content/pref-search.js
new file mode 100755
index 0000000000..8f17af63d2
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-search.js
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function Startup() {
+ MakeList();
+ SearchObserver.init();
+}
+
+var SearchObserver = {
+ init: function searchEngineListObserver_init() {
+ Services.obs.addObserver(this, "browser-search-engine-modified");
+ window.addEventListener("unload", this);
+ },
+
+ observe: function searchEngineListObj_observe(aEngine, aTopic, aVerb) {
+ if (aTopic != "browser-search-engine-modified")
+ return;
+ MakeList();
+ },
+
+ handleEvent: function searchEngineListEvent(aEvent) {
+ if (aEvent.type == "unload") {
+ window.removeEventListener("unload", this);
+ Services.obs.removeObserver(this, "browser-search-engine-modified");
+ }
+ }
+};
+
+function MakeList() {
+ var menulist = document.getElementById("engineList");
+ var currentEngineName = Services.search.currentEngine.name;
+
+ // Make sure the popup is empty.
+ menulist.removeAllItems();
+
+ var engines = Services.search.getVisibleEngines();
+ for (let engine of engines) {
+ let name = engine.name;
+ let menuitem = menulist.appendItem(name, name);
+ menuitem.setAttribute("class", "menuitem-iconic");
+ if (engine.iconURI)
+ menuitem.setAttribute("image", engine.iconURI.spec);
+ menuitem.engine = engine;
+ if (engine.name == currentEngineName) {
+ // Set selection to the current default engine.
+ menulist.selectedItem = menuitem;
+ }
+ }
+ // If the current engine isn't in the list any more, select the first item.
+ if (menulist.selectedIndex < 0)
+ menulist.selectedIndex = 0;
+}
+
+function UpdateDefaultEngine(selectedItem) {
+ Services.search.currentEngine = selectedItem.engine;
+ Services.obs.notifyObservers(null, "browser-search-engine-modified", "engine-current");
+}
diff --git a/comm/suite/components/pref/content/pref-search.xul b/comm/suite/components/pref/content/pref-search.xul
new file mode 100755
index 0000000000..e3eaa61701
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-search.xul
@@ -0,0 +1,50 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-search.dtd">
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="search_pane"
+ label="&pref.search.title;"
+ script="chrome://communicator/content/pref/pref-search.js">
+
+ <preferences id="search_preferences">
+ <preference id="browser.search.openintab"
+ name="browser.search.openintab"
+ type="bool"/>
+ <preference id="browser.search.opentabforcontextsearch"
+ name="browser.search.opentabforcontextsearch"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&legendHeader;"/>
+
+ <hbox align="center">
+ <label value="&defaultSearchEngine.label;"
+ accesskey="&defaultSearchEngine.accesskey;"
+ control="engineList"/>
+ <menulist id="engineList"
+ oncommand="UpdateDefaultEngine(this.selectedItem)"/>
+ </hbox>
+ <hbox pack="end">
+ <button id="managerButton"
+ label="&engineManager.label;"
+ oncommand="OpenSearchEngineManager();"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&searchResults.label;"/>
+ <checkbox id="openSearchTab"
+ label="&openInTab.label;"
+ accesskey="&openInTab.accesskey;"
+ preference="browser.search.openintab"/>
+ <checkbox id="openContextSearchTab"
+ label="&openContextSearchTab.label;"
+ accesskey="&openContextSearchTab.accesskey;"
+ preference="browser.search.opentabforcontextsearch"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-security.js b/comm/suite/components/pref/content/pref-security.js
new file mode 100644
index 0000000000..31dba56b7a
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-security.js
@@ -0,0 +1,15 @@
+/* -*- 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/. */
+
+function Startup()
+{
+ var prefTrackProtect = document.getElementById("privacy.trackingprotection.enabled");
+ SetWarnTrackEnabled(prefTrackProtect.value);
+}
+
+function SetWarnTrackEnabled(aEnable)
+{
+ EnableElementById("warnTrackContent", aEnable, false);
+}
diff --git a/comm/suite/components/pref/content/pref-security.xul b/comm/suite/components/pref/content/pref-security.xul
new file mode 100644
index 0000000000..6823df7f9e
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-security.xul
@@ -0,0 +1,108 @@
+<?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 % prefSecurityDTD SYSTEM "chrome://communicator/locale/pref/pref-security.dtd">
+%prefSecurityDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="security_pane" label="&pref.security.title;"
+ script="chrome://communicator/content/pref/pref-security.js">
+ <preferences id="security_preferences">
+ <!-- User Tracking -->
+ <preference id="privacy.donottrackheader.enabled"
+ name="privacy.donottrackheader.enabled"
+ type="bool"/>
+ <preference id="privacy.trackingprotection.enabled"
+ name="privacy.trackingprotection.enabled"
+ type="bool"
+ onchange="SetWarnTrackEnabled(this.value);"/>
+ <preference id="privacy.warn_tracking_content"
+ name="privacy.warn_tracking_content"
+ type="bool"/>
+
+ <!-- Location Aware Browsing -->
+ <preference id="geo.enabled"
+ name="geo.enabled"
+ type="bool"/>
+
+ <!-- Safe Browsing -->
+ <preference id="browser.safebrowsing.malware.enabled"
+ name="browser.safebrowsing.malware.enabled"
+ type="bool"/>
+ <preference id="browser.safebrowsing.phishing.enabled"
+ name="browser.safebrowsing.phishing.enabled"
+ type="bool"/>
+
+ <preference id="accessibility.blockautorefresh"
+ name="accessibility.blockautorefresh"
+ type="bool"/>
+ </preferences>
+
+ <!-- User Tracking -->
+ <groupbox id="trackingGroup">
+ <caption label="&tracking.label;"/>
+
+ <description>&trackingIntro.label;</description>
+ <checkbox id="doNotTrack"
+ label="&doNotTrack.label;"
+ accesskey="&doNotTrack.accesskey;"
+ preference="privacy.donottrackheader.enabled"/>
+ <checkbox id="trackProtect"
+ label="&trackProtect.label;"
+ accesskey="&trackProtect.accesskey;"
+ preference="privacy.trackingprotection.enabled"/>
+ <checkbox id="warnTrackContent"
+ class="indent"
+ label="&warnTrackContent.label;"
+ accesskey="&warnTrackContent.accesskey;"
+ preference="privacy.warn_tracking_content"/>
+ </groupbox>
+
+ <!-- Location Aware Browsing -->
+ <groupbox id="geoLocationGroup">
+ <caption label="&geoLocation.label;"/>
+
+ <description>&geoIntro.label;</description>
+ <radiogroup id="geoSelection"
+ preference="geo.enabled">
+ <radio id="geoEnabled"
+ value="true"
+ label="&geoEnabled.label;"
+ accesskey="&geoEnabled.accesskey;"/>
+ <radio id="geoDisabled"
+ value="false"
+ label="&geoDisabled.label;"
+ accesskey="&geoDisabled.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <!-- Safe Browsing -->
+ <groupbox id="safeBrowsingGroup">
+ <caption label="&safeBrowsing.label;"/>
+
+ <description>&safeBrowsingIntro.label;</description>
+ <checkbox id="blockAttackSites"
+ label="&blockAttackSites.label;"
+ accesskey="&blockAttackSites.accesskey;"
+ preference="browser.safebrowsing.malware.enabled"/>
+ <checkbox id="blockWebForgeries"
+ label="&blockWebForgeries.label;"
+ accesskey="&blockWebForgeries.accesskey;"
+ preference="browser.safebrowsing.phishing.enabled"/>
+ </groupbox>
+
+ <vbox class="box-padded" align="start">
+ <checkbox id="blockAutoRefresh"
+ label="&blockAutoRefresh.label;"
+ accesskey="&blockAutoRefresh.accesskey;"
+ preference="accessibility.blockautorefresh"/>
+ </vbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-smartupdate.js b/comm/suite/components/pref/content/pref-smartupdate.js
new file mode 100644
index 0000000000..8e9712a936
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-smartupdate.js
@@ -0,0 +1,87 @@
+/* -*- 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/. */
+
+var gCanCheckForUpdates;
+
+function Startup()
+{
+ var hasUpdater = "nsIApplicationUpdateService" in Ci;
+
+ if (hasUpdater)
+ {
+ var aus = Cc["@mozilla.org/updates/update-service;1"]
+ .getService(Ci.nsIApplicationUpdateService);
+ gCanCheckForUpdates = aus.canCheckForUpdates;
+
+ UpdateAddonsItems();
+ UpdateAppItems();
+ }
+ else
+ {
+ var appGroupBox = document.getElementById("appUpdatesGroupBox");
+ appGroupBox.hidden = true;
+ }
+}
+
+/*
+ * Preferences:
+ *
+ * app.update.enabled
+ * - boolean:
+ * - true if updates to the application are enabled, false otherwise
+ * extensions.update.enabled
+ * - boolean:
+ * - true if updates to extensions and themes are enabled, false otherwise
+ * app.update.auto
+ * - true if updates should be automatically downloaded and installed,
+ * false if the user should be asked what he wants to do when an
+ * update is available
+ */
+function UpdateAddonsItems()
+{
+ var addOnsCheck = !document.getElementById("xpinstall.enabled").value;
+
+ document.getElementById("addOnsUpdatesEnabled").disabled =
+ addOnsCheck ||
+ document.getElementById("extensions.update.enabled").locked;
+
+ document.getElementById("addOnsUpdateFrequency").disabled =
+ !document.getElementById("xpinstall.enabled").value ||
+ !document.getElementById("extensions.update.enabled").value ||
+ document.getElementById("extensions.update.interval").locked;
+
+ document.getElementById("allowedSitesLink").disabled =
+ addOnsCheck;
+
+ document.getElementById("addOnsModeAutoEnabled").disabled =
+ addOnsCheck ||
+ !document.getElementById("extensions.update.enabled").value ||
+ document.getElementById("extensions.update.enabled").locked;
+}
+
+function UpdateAppItems()
+{
+ var enabledPref = document.getElementById("app.update.enabled");
+
+ document.getElementById("appUpdatesEnabled").disabled =
+ !gCanCheckForUpdates || enabledPref.locked;
+
+ document.getElementById("appUpdateFrequency").disabled =
+ !enabledPref.value || !gCanCheckForUpdates ||
+ document.getElementById("app.update.interval").locked;
+
+ document.getElementById("appModeAutoEnabled").disabled =
+ !enabledPref.value || !gCanCheckForUpdates;
+}
+
+/**
+ * Displays the history of installed updates.
+ */
+function ShowUpdateHistory()
+{
+ Cc["@mozilla.org/updates/update-prompt;1"]
+ .createInstance(Ci.nsIUpdatePrompt)
+ .showUpdateHistory(window);
+}
diff --git a/comm/suite/components/pref/content/pref-smartupdate.xul b/comm/suite/components/pref/content/pref-smartupdate.xul
new file mode 100644
index 0000000000..a9b9546c31
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-smartupdate.xul
@@ -0,0 +1,139 @@
+<?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 % prefSmartUpdateDTD SYSTEM "chrome://communicator/locale/pref/pref-smartupdate.dtd">
+%prefSmartUpdateDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="smartupdate_pane"
+ label="&pref.smartUpdate.title;"
+ script="chrome://communicator/content/pref/pref-smartupdate.js">
+
+ <preferences id="smartupdate_preferences">
+ <preference id="xpinstall.enabled"
+ name="xpinstall.enabled"
+ type="bool"
+ onchange="UpdateAddonsItems();"/>
+ <preference id="extensions.update.enabled"
+ name="extensions.update.enabled"
+ type="bool"
+ onchange="UpdateAddonsItems();"/>
+ <preference id="extensions.update.interval"
+ name="extensions.update.interval"
+ type="int"/>
+ <preference id="extensions.update.autoUpdateDefault"
+ name="extensions.update.autoUpdateDefault"
+ type="bool"/>
+ <preference id="extensions.getAddons.cache.enabled"
+ name="extensions.getAddons.cache.enabled"
+ type="bool"/>
+ <preference id="app.update.enabled"
+ name="app.update.enabled"
+ type="bool"
+ onchange="UpdateAppItems();"/>
+ <preference id="app.update.auto"
+ name="app.update.auto"
+ type="bool"
+ onchange="UpdateAppItems();"/>
+ <preference id="app.update.interval"
+ name="app.update.interval"
+ type="int"/>
+ <preference id="app.update.disable_button.showUpdateHistory"
+ name="app.update.disable_button.showUpdateHistory"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&addOnsTitle.label;"/>
+ <hbox align="center">
+ <checkbox id="XPInstallEnabled"
+ label="&addOnsAllow.label;"
+ flex="1"
+ accesskey="&addOnsAllow.accesskey;"
+ preference="xpinstall.enabled"/>
+ <label id="allowedSitesLink"
+ class="text-link"
+ value="&allowedSitesLink.label;"
+ onclick="toDataManager('|permissions');"/>
+ </hbox>
+ <hbox class="indent">
+ <checkbox id="addOnsUpdatesEnabled"
+ label="&autoAddOnsUpdates.label;"
+ accesskey="&autoAddOnsUpdates.accesskey;"
+ preference="extensions.update.enabled"/>
+ <radiogroup id="addOnsUpdateFrequency"
+ orient="horizontal"
+ preference="extensions.update.interval">
+ <radio id="addOnsFreqDaily"
+ label="&daily.label;"
+ accesskey="&addOnsDaily.accesskey;"
+ value="86400"/>
+ <radio id="addOnsFreqWeekly"
+ label="&weekly.label;"
+ accesskey="&addOnsWeekly.accesskey;"
+ value="604800"/>
+ </radiogroup>
+ </hbox>
+ <hbox class="indent">
+ <checkbox id="addOnsModeAutoEnabled"
+ class="indent"
+ label="&addOnsModeAutomatic.label;"
+ flex="1"
+ accesskey="&addOnsModeAutomatic.accesskey;"
+ preference="extensions.update.autoUpdateDefault"/>
+ </hbox>
+ <hbox align="center">
+ <checkbox id="enablePersonalized"
+ flex="1"
+ label="&enablePersonalized.label;"
+ accesskey="&enablePersonalized.accesskey;"
+ preference="extensions.getAddons.cache.enabled"/>
+ <label id="addonManagerLink"
+ class="text-link"
+ onclick="toEM('addons://list/extension');"
+ value="&addonManagerLink.label;"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox id="appUpdatesGroupBox">
+ <caption label="&appUpdates.caption;"/>
+ <hbox>
+ <checkbox id="appUpdatesEnabled"
+ label="&autoAppUpdates.label;"
+ accesskey="&autoAppUpdates.accesskey;"
+ preference="app.update.enabled"/>
+ <radiogroup id="appUpdateFrequency"
+ orient="horizontal"
+ preference="app.update.interval">
+ <radio id="appFreqDaily"
+ label="&daily.label;"
+ accesskey="&appDaily.accesskey;"
+ value="86400"/>
+ <radio id="appFreqWeekly"
+ label="&weekly.label;"
+ accesskey="&appWeekly.accesskey;"
+ value="604800"/>
+ </radiogroup>
+ </hbox>
+ <checkbox id="appModeAutoEnabled"
+ class="indent"
+ label="&appModeAutomatic.label;"
+ flex="1"
+ accesskey="&appModeAutomatic.accesskey;"
+ preference="app.update.auto"/>
+ <hbox pack="end">
+ <button id="showUpdateHistory"
+ label="&updateHistoryButton.label;"
+ accesskey="&updateHistoryButton.accesskey;"
+ preference="app.update.disable_button.showUpdateHistory"
+ oncommand="ShowUpdateHistory();"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-spelling.js b/comm/suite/components/pref/content/pref-spelling.js
new file mode 100644
index 0000000000..6c214af3fc
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-spelling.js
@@ -0,0 +1,119 @@
+/* -*- 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/. */
+
+var gDictCount = 0;
+var gLastSelectedLang;
+
+function Startup() {
+ if ("@mozilla.org/spellchecker;1" in Cc)
+ InitLanguageMenu();
+ else
+ {
+ document.getElementById("generalSpelling").hidden = true;
+ document.getElementById("mailSpelling").hidden = true;
+ document.getElementById("noSpellCheckLabel").hidden = false;
+ }
+}
+
+function InitLanguageMenu() {
+ var spellChecker = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Get the list of dictionaries from the spellchecker.
+ var dictList = spellChecker.getDictionaryList();
+ var count = dictList.length;
+
+ // If dictionary count hasn't changed then no need to update the menu.
+ if (gDictCount == count)
+ return;
+
+ // Store current dictionary count.
+ gDictCount = count;
+
+ // Load the string bundles that will help us map
+ // RFC 1766 strings to UI strings.
+
+ // Load the language string bundle.
+ var languageBundle = document.getElementById("languageNamesBundle");
+ var regionBundle = null;
+ // If we have a language string bundle, load the region string bundle.
+ if (languageBundle)
+ regionBundle = document.getElementById("regionNamesBundle");
+
+ var menuStr2;
+ var isoStrArray;
+ var langId;
+ var langLabel;
+
+ for (let i = 0; i < count; i++) {
+ try {
+ langId = dictList[i];
+ isoStrArray = dictList[i].split(/[-_]/);
+
+ if (languageBundle && isoStrArray[0])
+ langLabel = languageBundle.getString(isoStrArray[0].toLowerCase());
+
+ if (regionBundle && langLabel && isoStrArray.length > 1 && isoStrArray[1]) {
+ menuStr2 = regionBundle.getString(isoStrArray[1].toLowerCase());
+ if (menuStr2)
+ langLabel += "/" + menuStr2;
+ }
+
+ if (langLabel && isoStrArray.length > 2 && isoStrArray[2])
+ langLabel += " (" + isoStrArray[2] + ")";
+
+ if (!langLabel)
+ langLabel = langId;
+ } catch (ex) {
+ // getString throws an exception when a key is not found in the
+ // bundle. In that case, just use the original dictList string.
+ langLabel = langId;
+ }
+ dictList[i] = [langLabel, langId];
+ }
+
+ // sort by locale-aware collation
+ dictList.sort(
+ function compareFn(a, b) {
+ return a[0].localeCompare(b[0]);
+ }
+ );
+
+ var languageMenuList = document.getElementById("languageMenuList");
+ // Remove any languages from the list.
+ var languageMenuPopup = languageMenuList.menupopup;
+ while (languageMenuPopup.firstChild.localName != "menuseparator")
+ languageMenuPopup.firstChild.remove();
+
+ var curLang = languageMenuList.value;
+ var defaultItem = null;
+
+ for (let i = 0; i < count; i++) {
+ let item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
+ item.setAttribute("label", dictList[i][0]);
+ item.setAttribute("value", dictList[i][1]);
+ let beforeItem = gDialog.LanguageMenulist.getItemAtIndex(i);
+ languageMenuPopup.insertBefore(item, beforeItem);
+
+ if (curLang && dictList[i][1] == curLang)
+ defaultItem = item;
+ }
+
+ // Now make sure the correct item in the menu list is selected.
+ if (defaultItem) {
+ languageMenuList.selectedItem = defaultItem;
+ gLastSelectedLang = defaultItem;
+ }
+}
+
+function SelectLanguage(aTarget) {
+ if (aTarget.value != "more-cmd")
+ gLastSelectedLang = aTarget;
+ else {
+ openDictionaryList();
+ if (gLastSelectedLang)
+ document.getElementById("languageMenuList").selectedItem = gLastSelectedLang;
+ }
+}
diff --git a/comm/suite/components/pref/content/pref-spelling.xul b/comm/suite/components/pref/content/pref-spelling.xul
new file mode 100644
index 0000000000..93fa605fb5
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-spelling.xul
@@ -0,0 +1,80 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-spelling.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="spelling_pane"
+ label="&prefSpelling.title;"
+ script="chrome://communicator/content/pref/pref-spelling.js">
+
+ <preferences id="spelling_preferences">
+ <preference id="mail.SpellCheckBeforeSend"
+ name="mail.SpellCheckBeforeSend"
+ type="bool"/>
+ <preference id="mail.spellcheck.inline"
+ name="mail.spellcheck.inline"
+ type="bool"/>
+ <preference id="spellchecker.dictionary"
+ name="spellchecker.dictionary"
+ type="string"
+ onchange="SelectLanguage(event.target)"/>
+ <preference id="layout.spellcheckDefault"
+ name="layout.spellcheckDefault"
+ type="int"/>
+ </preferences>
+
+ <label id="noSpellCheckLabel"
+ value="&noSpellCheckAvailable.label;"
+ hidden="true"/>
+
+ <groupbox id="generalSpelling" align="start">
+ <caption label="&generalSpelling.label;"/>
+ <hbox align="center" pack="start">
+ <label value="&languagePopup.label;"
+ accesskey="&languagePopup.accessKey;"
+ control="languageMenuList"/>
+ <menulist id="languageMenuList"
+ preference="spellchecker.dictionary">
+ <menupopup onpopupshowing="InitLanguageMenu();">
+ <!-- dynamic content populated by JS -->
+ <menuseparator/>
+ <menuitem value="more-cmd" label="&moreDictionaries.label;"/>
+ </menupopup>
+ </menulist>
+ <spring flex="1"/>
+ </hbox>
+ <separator class="thin"/>
+ <hbox align="center">
+ <label value="&checkSpellingWhenTyping.label;"
+ accesskey="&checkSpellingWhenTyping.accesskey;"
+ control="spellcheckDefault"/>
+ <menulist id="spellcheckDefault"
+ preference="layout.spellcheckDefault">
+ <menupopup>
+ <menuitem value="0" label="&dontCheckSpelling.label;"/>
+ <menuitem value="1" label="&multilineCheckSpelling.label;"/>
+ <menuitem value="2" label="&alwaysCheckSpelling.label;"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </groupbox>
+
+ <groupbox id="mailSpelling" align="start">
+ <caption label="&spellForMailAndNews.label;"/>
+ <vbox align="start">
+ <checkbox id="spellCheckBeforeSend"
+ label="&checkSpellingBeforeSend.label;"
+ accesskey="&checkSpellingBeforeSend.accesskey;"
+ preference="mail.SpellCheckBeforeSend"/>
+ <checkbox id="inlineSpellCheck"
+ label="&spellCheckInline.label;"
+ accesskey="&spellCheckInline.accesskey;"
+ preference="mail.spellcheck.inline"/>
+ </vbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-sync.js b/comm/suite/components/pref/content/pref-sync.js
new file mode 100644
index 0000000000..06d1825a70
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-sync.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/. */
+
+const {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+
+const PAGE_NO_ACCOUNT = 0;
+const PAGE_HAS_ACCOUNT = 1;
+const PAGE_NEEDS_UPDATE = 2;
+
+var gSyncPane = {
+ 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";
+ },
+
+ topics: [ "weave:service:ready",
+ "weave:service:login:error",
+ "weave:service:login:finish",
+ "weave:service:start-over",
+ "weave:service:setup-complete",
+ "weave:service:logout:finish"],
+
+ init: function () {
+ for (var topic of this.topics)
+ Services.obs.addObserver(this, topic);
+
+ window.addEventListener("unload", this);
+
+ var xps = Cc["@mozilla.org/weave/service;1"]
+ .getService().wrappedJSObject;
+ if (xps.ready)
+ this.observe(null, "weave:service:ready", null);
+ else
+ xps.ensureLoaded();
+ },
+
+ handleEvent: function (aEvent) {
+ window.removeEventListener("unload", this);
+
+ for (var topic of this.topics)
+ Services.obs.removeObserver(this, topic);
+ },
+
+ observe: function (aSubject, aTopic, aData) {
+ 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;
+ let prefutilitiesBundle = document.getElementById("bundle_prefutilities");
+ let buttonChoice =
+ Services.prompt.confirmEx(window,
+ prefutilitiesBundle.getString("syncUnlink.title"),
+ prefutilitiesBundle.getString("syncUnlink.label"),
+ flags,
+ prefutilitiesBundle.getString("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();
+ },
+
+ openSetup: function (resetSync) {
+ var win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
+ if (win)
+ win.focus();
+ else {
+ window.openDialog("chrome://communicator/content/sync/syncSetup.xul",
+ "weaveSetup", "centerscreen,chrome,resizable=no", resetSync);
+ }
+ },
+
+ openQuotaDialog: function () {
+ let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
+ if (win)
+ win.focus();
+ else
+ window.openDialog("chrome://communicator/content/sync/syncQuota.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://communicator/content/sync/syncAddDevice.xul",
+ "syncAddDevice", "centerscreen,chrome,resizable=no");
+ },
+
+ resetSync: function () {
+ this.openSetup(true);
+ }
+};
diff --git a/comm/suite/components/pref/content/pref-sync.xul b/comm/suite/components/pref/content/pref-sync.xul
new file mode 100644
index 0000000000..4163c0de70
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-sync.xul
@@ -0,0 +1,158 @@
+<?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://communicator/locale/sync/syncBrand.dtd">
+<!ENTITY % syncDTD SYSTEM "chrome://communicator/locale/pref/pref-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="sync_pane"
+ helpTopic="sync_prefs"
+ 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.passwords" name="services.sync.engine.passwords" type="bool"/>
+ <preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/>
+ <preference id="engine.tabs" name="services.sync.engine.tabs" type="bool"/>
+ </preferences>
+
+ <script src="chrome://communicator/content/pref/pref-sync.js"/>
+ <script src="chrome://communicator/content/sync/syncUtils.js"/>
+
+ <deck id="weavePrefsDeck">
+ <vbox id="noAccount" align="center">
+ <spacer flex="1"/>
+ <description id="syncDesc" flex="1">
+ &weaveDesc.label;
+ </description>
+ <button id="setupButton"
+ label="&setupButton.label;"
+ accesskey="&setupButton.accesskey;"
+ oncommand="gSyncPane.openSetup();"/>
+ <separator/>
+ <spacer flex="3"/>
+ </vbox>
+ <vbox id="hasAccount">
+ <groupbox>
+ <caption label="&accountGroupboxCaption.label;"/>
+ <hbox align="center">
+ <label value="&accountName.label;" control="accountName"/>
+ <textbox id="accountName" flex="1" readonly="true"/>
+ <button type="menu"
+ label="&manageAccount.label;"
+ accesskey="&manageAccount.accesskey;">
+ <menupopup>
+ <menuitem label="&viewQuota.label;"
+ accesskey="&viewQuota.accesskey;"
+ oncommand="gSyncPane.openQuotaDialog();"/>
+ <menuseparator/>
+ <menuitem label="&changePassword.label;"
+ accesskey="&changePassword.accesskey;"
+ oncommand="gSyncUtils.changePassword();"/>
+ <menuitem label="&myRecoveryKey.label;"
+ accesskey="&myRecoveryKey.accesskey;"
+ oncommand="gSyncUtils.resetPassphrase();"/>
+ <menuseparator/>
+ <menuitem label="&resetSync.label;"
+ accesskey="&resetSync.accesskey;"
+ oncommand="gSyncPane.resetSync();"/>
+ <menuitem label="&unlinkDevice.label;"
+ accesskey="&unlinkDevice.accesskey;"
+ oncommand="gSyncPane.startOver(true);"/>
+ <menuseparator/>
+ <menuitem label="&addDevice.label;"
+ accesskey="&addDevice.accesskey;"
+ oncommand="gSyncPane.openAddDevice();"/>
+ </menupopup>
+ </button>
+ </hbox>
+ <vbox>
+ <label value="&syncMy2.label;"/>
+ <listbox id="syncEnginesList" flex="1">
+ <listitem type="checkbox"
+ label="&engine.addons.label;"
+ accesskey="&engine.addons.accesskey;"
+ preference="engine.addons"/>
+ <listitem type="checkbox"
+ label="&engine.bookmarks.label;"
+ accesskey="&engine.bookmarks.accesskey;"
+ preference="engine.bookmarks"/>
+ <listitem type="checkbox"
+ label="&engine.history.label;"
+ accesskey="&engine.history.accesskey;"
+ preference="engine.history"/>
+ <listitem type="checkbox"
+ label="&engine.passwords.label;"
+ accesskey="&engine.passwords.accesskey;"
+ preference="engine.passwords"/>
+ <listitem type="checkbox"
+ label="&engine.prefs.label;"
+ accesskey="&engine.prefs.accesskey;"
+ preference="engine.prefs"/>
+ <listitem type="checkbox"
+ label="&engine.tabs.label;"
+ accesskey="&engine.tabs.accesskey;"
+ preference="engine.tabs"/>
+ </listbox>
+ </vbox>
+ </groupbox>
+ <groupbox>
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label value="&syncComputerName.label;"
+ accesskey="&syncComputerName.accesskey;"
+ control="syncComputerName"/>
+ <textbox id="syncComputerName"
+ onchange="gSyncUtils.changeName(this);"/>
+ </row>
+ </rows>
+ </grid>
+ </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=""/>
+ <button label="&updatePass.label;"
+ accesskey="&updatePass.accesskey;"
+ oncommand="gSyncPane.updatePass(); return false;"
+ id="updatePassButton"/>
+ <button label="&resetPass.label;"
+ accesskey="&resetPass.accesskey;"
+ oncommand="gSyncPane.resetPass(); return false;"
+ id="resetPassButton"/>
+ </hbox>
+ <button label="&unlinkDevice.label;"
+ accesskey="&unlinkDevice.accesskey;"
+ oncommand="gSyncPane.startOver(true); return false;"
+ id="unlinkDeviceButton"/>
+ </vbox>
+ </deck>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/pref-tabs.xul b/comm/suite/components/pref/content/pref-tabs.xul
new file mode 100644
index 0000000000..24361fba85
--- /dev/null
+++ b/comm/suite/components/pref/content/pref-tabs.xul
@@ -0,0 +1,113 @@
+<?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 SYSTEM "chrome://communicator/locale/pref/pref-tabs.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="tabs_pane"
+ label="&tabHeader.label;">
+
+ <preferences id="tabs_preferences">
+ <preference id="browser.tabs.autoHide"
+ name="browser.tabs.autoHide"
+ type="bool"/>
+ <preference id="browser.tabs.loadInBackground"
+ name="browser.tabs.loadInBackground"
+ type="bool"
+ inverted="true"/>
+ <preference id="browser.tabs.loadDivertedInBackground"
+ name="browser.tabs.loadDivertedInBackground"
+ type="bool"
+ inverted="true"/>
+ <preference id="browser.tabs.avoidBrowserFocus"
+ name="browser.tabs.avoidBrowserFocus"
+ type="bool"
+ inverted="true"/>
+ <preference id="browser.tabs.warnOnClose"
+ name="browser.tabs.warnOnClose"
+ type="bool"/>
+ <preference id="browser.tabs.insertRelatedAfterCurrent"
+ name="browser.tabs.insertRelatedAfterCurrent"
+ type="bool"/>
+ <preference id="browser.tabs.opentabfor.middleclick"
+ name="browser.tabs.opentabfor.middleclick"
+ type="bool"/>
+ <preference id="browser.tabs.opentabfor.urlbar"
+ name="browser.tabs.opentabfor.urlbar"
+ type="bool"/>
+ <preference id="suite.manager.dataman.openAsDialog"
+ name="suite.manager.dataman.openAsDialog"
+ inverted="true"
+ type="bool"/>
+ <preference id="suite.manager.addons.openAsDialog"
+ name="suite.manager.addons.openAsDialog"
+ inverted="true"
+ type="bool"/>
+ </preferences>
+
+ <groupbox id="generalTabPreferences" align="start">
+ <caption label="&tabDisplay.label;"/>
+ <checkbox id="tabStrip"
+ label="&autoHide.label;"
+ accesskey="&autoHide.accesskey2;"
+ preference="browser.tabs.autoHide"/>
+ <checkbox id="tabBackground"
+ label="&background.label;"
+ accesskey="&background.accesskey;"
+ preference="browser.tabs.loadInBackground"/>
+ <checkbox id="tabDivertedBackground"
+ label="&diverted.label;"
+ accesskey="&diverted.accesskey;"
+ preference="browser.tabs.loadDivertedInBackground"/>
+ <checkbox id="tabAvoidBrowserFocus"
+ label="&browserFocus.label;"
+ accesskey="&browserFocus.accesskey;"
+ preference="browser.tabs.avoidBrowserFocus"/>
+ <checkbox id="tabWarnOnClose"
+ label="&warnOnClose.label;"
+ accesskey="&warnOnClose.accesskey;"
+ preference="browser.tabs.warnOnClose"/>
+ <checkbox id="tabRelatedAfterCurrent"
+ label="&relatedAfterCurrent.label;"
+ accesskey="&relatedAfterCurrent.accesskey;"
+ preference="browser.tabs.insertRelatedAfterCurrent"/>
+ </groupbox>
+
+ <groupbox id="useTabPreferences" align="start">
+ <caption label="&openTabs.label;"/>
+ <checkbox id="middleClick"
+#ifndef XP_MACOSX
+ label="&middleClick.label;"
+ accesskey="&middleClick.accesskey;"
+#else
+ label="&middleClickMac.label;"
+ accesskey="&middleClickMac.accesskey;"
+#endif
+ preference="browser.tabs.opentabfor.middleclick"/>
+ <checkbox id="urlBar"
+#ifndef XP_MACOSX
+ label="&urlbar.label;"
+ accesskey="&urlbar.accesskey;"
+#else
+ label="&urlbarMac.label;"
+ accesskey="&urlbarMac.accesskey;"
+#endif
+ preference="browser.tabs.opentabfor.urlbar"/>
+ </groupbox>
+
+ <groupbox id="useManagersPreferences" align="start">
+ <caption label="&openManagers.label;"/>
+ <checkbox id="openDataManager"
+ label="&openDataManager.label;"
+ accesskey="&openDataManager.accesskey;"
+ preference="suite.manager.dataman.openAsDialog"/>
+ <checkbox id="openAddOnsManager"
+ label="&openAddOnsManager.label;"
+ accesskey="&openAddOnsManager.accesskey;"
+ preference="suite.manager.addons.openAsDialog"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/pref/content/preferences.js b/comm/suite/components/pref/content/preferences.js
new file mode 100644
index 0000000000..091a3904fc
--- /dev/null
+++ b/comm/suite/components/pref/content/preferences.js
@@ -0,0 +1,99 @@
+/* -*- 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/. */
+
+// The content of this file is loaded into the scope of the
+// prefwindow and will be available to all prefpanes!
+
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+function OnLoad()
+{
+ // Make sure that the preferences window fits the screen.
+ let dialog = document.documentElement;
+ let curHeight = dialog.scrollHeight;
+ let curWidth = dialog.scrollWidth;
+
+ // Leave some space for desktop toolbar and window decoration.
+ let maxHeight = window.screen.availHeight - 48;
+ let maxWidth = window.screen.availWidth - 24;
+
+ // Trigger overflow situation within 40px for bug 868495 expansions.
+ let setHeight = curHeight > maxHeight - 40 ? maxHeight : curHeight;
+ let setWidth = curWidth > maxWidth ? maxWidth : curWidth;
+
+ if (setHeight == curHeight && setWidth == curWidth)
+ dialog.setAttribute("overflow", "visible");
+
+ window.innerHeight = setHeight;
+ window.innerWidth = setWidth;
+}
+
+function EnableElementById(aElementId, aEnable, aFocus)
+{
+ EnableElement(document.getElementById(aElementId), aEnable, aFocus);
+}
+
+function EnableElement(aElement, aEnable, aFocus)
+{
+ let pref = document.getElementById(aElement.getAttribute("preference"));
+ let enabled = aEnable && !pref.locked;
+
+ aElement.disabled = !enabled;
+
+ if (enabled && aFocus)
+ aElement.focus();
+}
+
+function WriteSoundField(aField, aValue)
+{
+ var file = GetFileFromString(aValue);
+ if (file)
+ {
+ aField.file = file;
+ aField.label = (AppConstants.platform == "macosx") ? file.leafName : file.path;
+ }
+}
+
+function SelectSound(aSoundUrlPref)
+{
+ var soundUrlPref = aSoundUrlPref;
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+ var prefutilitiesBundle = document.getElementById("bundle_prefutilities");
+ fp.init(window, prefutilitiesBundle.getString("choosesound"),
+ Ci.nsIFilePicker.modeOpen);
+
+ let file = GetFileFromString(soundUrlPref.value);
+ if (file && file.parent && file.parent.exists())
+ fp.displayDirectory = file.parent;
+
+ let filterExts = "*.wav; *.wave";
+ // On Mac, allow AIFF and CAF files too.
+ if (AppConstants.platform == "macosx") {
+ filterExts += "; *.aif; *.aiff; *.caf";
+ }
+ fp.appendFilter(prefutilitiesBundle.getString("SoundFiles"), filterExts);
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+ fp.open(rv => {
+ if (rv == Ci.nsIFilePicker.returnOK && fp.fileURL.spec &&
+ fp.fileURL.spec.length > 0) {
+ soundUrlPref.value = fp.fileURL.spec;
+ }
+ });
+}
+
+function PlaySound(aValue, aMail)
+{
+ const nsISound = Ci.nsISound;
+ var sound = Cc["@mozilla.org/sound;1"]
+ .createInstance(nsISound);
+
+ if (aValue)
+ sound.play(Services.io.newURI(aValue));
+ else if (aMail && (AppConstants.platform != "macosx"))
+ sound.playEventSound(nsISound.EVENT_NEW_MAIL_RECEIVED);
+ else
+ sound.beep();
+}
diff --git a/comm/suite/components/pref/content/preferences.xul b/comm/suite/components/pref/content/preferences.xul
new file mode 100644
index 0000000000..787d086af5
--- /dev/null
+++ b/comm/suite/components/pref/content/preferences.xul
@@ -0,0 +1,264 @@
+<?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 type="text/css" href="chrome://communicator/skin/"?>
+<?xml-stylesheet type="text/css" href="chrome://communicator/content/communicator.css"?>
+<?xml-stylesheet type="text/css" href="chrome://communicator/content/pref/prefpanels.css"?>
+<?xml-stylesheet type="text/css" href="chrome://communicator/skin/prefpanels.css"?>
+<?xml-stylesheet type="text/css" href="chrome://communicator/skin/preferences.css"?>
+
+<!DOCTYPE prefwindow SYSTEM "chrome://communicator/locale/pref/preferences.dtd">
+
+<prefwindow id="prefDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&prefWindow.title;"
+#ifndef XP_WIN
+#ifndef XP_MACOSX
+ style="&prefWindow.size;"
+#else
+ style="&prefWindowMac2.size;"
+#endif
+#else
+ style="&prefWindowWin2.size;"
+#endif
+ overflow="auto"
+ onload="OnLoad();"
+ windowtype="mozilla:preferences"
+ buttons="accept,cancel,help"
+ autopanes="true">
+
+ <script src="chrome://communicator/content/pref/preferences.js"/>
+ <!-- Used by pref-smartupdate, pref-privatedata, pref-cookies, pref-images, pref-popups and pref-passwords, as well as pref-sync (gSyncUtils.*open* -> openUILinkIn) -->
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://communicator/content/tasksOverlay.js"/>
+
+ <stringbundleset id="prefBundleset">
+ <stringbundle id="bundle_prefutilities"
+ src="chrome://communicator/locale/pref/prefutilities.properties"/>
+ <stringbundle id="languageNamesBundle"
+ src="chrome://global/locale/languageNames.properties"/>
+ <stringbundle id="regionNamesBundle"
+ src="chrome://global/locale/regionNames.properties"/>
+ </stringbundleset>
+
+ <tree id="prefsTree"
+ style="width: 13em;"
+ seltype="single"
+ hidecolumnpicker="true"
+ hidden="true"
+ flex="1">
+ <treecols>
+ <treecol id="categoryCol"
+ label="&categoryHeader;"
+ primary="true"
+ flex="1"/>
+ </treecols>
+
+ <treechildren id="prefsPanelChildren">
+ <!-- Appearance items -->
+ <treeitem container="true"
+ id="appearanceItem"
+ label="&appear.label;"
+ prefpane="appearance_pane"
+ helpTopic="appearance_pref"
+ url="chrome://communicator/content/pref/pref-appearance.xul">
+ <treechildren id="appearanceChildren">
+ <treeitem id="contentItem"
+ label="&content.label;"
+ prefpane="content_pane"
+ helpTopic="appearance_pref_content"
+ url="chrome://communicator/content/pref/pref-content.xul"/>
+ <treeitem id="fontsItem"
+ label="&fonts.label;"
+ prefpane="fonts_pane"
+ helpTopic="appearance_pref_fonts"
+ url="chrome://communicator/content/pref/pref-fonts.xul"/>
+ <treeitem id="colorsItem"
+ label="&colors.label;"
+ prefpane="colors_pane"
+ helpTopic="appearance_pref_colors"
+ url="chrome://communicator/content/pref/pref-colors.xul"/>
+ <treeitem id="mediaItem"
+ label="&media.label;"
+ prefpane="media_pane"
+ helpTopic="appearance_pref_media"
+ url="chrome://communicator/content/pref/pref-media.xul"/>
+ <treeitem id="spellingItem"
+ label="&spellingPane.label;"
+ prefpane="spelling_pane"
+ helpTopic="appearance_pref_spelling"
+ url="chrome://communicator/content/pref/pref-spelling.xul"/>
+ </treechildren>
+ </treeitem>
+
+ <!-- Browser items -->
+ <treeitem container="true"
+ id="navigatorItem"
+ label="&navigator.label;"
+ prefpane="navigator_pane"
+ helpTopic="navigator_pref_navigator"
+ url="chrome://communicator/content/pref/pref-navigator.xul">
+ <treechildren id="navigatorChildren">
+ <treeitem id="historyItem"
+ label="&history.label;"
+ prefpane="history_pane"
+ helpTopic="navigator_pref_history"
+ url="chrome://communicator/content/pref/pref-history.xul"/>
+ <treeitem id="languagesItem"
+ label="&languages.label;"
+ prefpane="languages_pane"
+ helpTopic="navigator_pref_languages"
+ url="chrome://communicator/content/pref/pref-languages.xul"/>
+ <treeitem id="applicationsItem"
+ label="&applications.label;"
+ prefpane="applications_pane"
+ helpTopic="navigator_pref_helper_applications"
+ url="chrome://communicator/content/pref/pref-applications.xul"/>
+ <treeitem id="locationBarItem"
+ label="&locationBar.label;"
+ prefpane="locationBar_pane"
+ helpTopic="navigator_pref_location_bar"
+ url="chrome://communicator/content/pref/pref-locationbar.xul"/>
+ <treeitem id="searchItem"
+ label="&search.label;"
+ prefpane="search_pane"
+ helpTopic="navigator_pref_internet_searching"
+ url="chrome://communicator/content/pref/pref-search.xul"/>
+ <treeitem id="tabsItem"
+ label="&tabWindows.label;"
+ prefpane="tabs_pane"
+ helpTopic="navigator_pref_tabbed_browsing"
+ url="chrome://communicator/content/pref/pref-tabs.xul"/>
+ <treeitem id="linksItem"
+ label="&links.label;"
+ prefpane="links_pane"
+ helpTopic="navigator_pref_link_behavior"
+ url="chrome://communicator/content/pref/pref-links.xul"/>
+ <treeitem id="downloadItem"
+ label="&download.label;"
+ prefpane="download_pane"
+ helpTopic="navigator_pref_downloads"
+ url="chrome://communicator/content/pref/pref-download.xul"/>
+ </treechildren>
+ </treeitem>
+
+ <!-- Privacy & Security items -->
+ <treeitem container="true"
+ id="securityItem"
+ prefpane="security_pane"
+ label="&security.label;"
+ helpTopic="sec_gen"
+ url="chrome://communicator/content/pref/pref-security.xul">
+ <treechildren id="securityChildren">
+ <treeitem id="privatedataItem"
+ label="&privatedata.label;"
+ prefpane="privatedata_pane"
+ helpTopic="privatedata_prefs"
+ url="chrome://communicator/content/pref/pref-privatedata.xul"/>
+ <treeitem id="cookiesItem"
+ label="&cookies.label;"
+ prefpane="cookies_pane"
+ helpTopic="cookies_prefs"
+ url="chrome://communicator/content/pref/pref-cookies.xul"/>
+ <treeitem id="imagesItem"
+ label="&images.label;"
+ prefpane="images_pane"
+ helpTopic="images_prefs"
+ url="chrome://communicator/content/pref/pref-images.xul"/>
+ <treeitem id="popupsItem"
+ label="&popups.label;"
+ prefpane="popups_pane"
+ helpTopic="pop_up_blocking_prefs"
+ url="chrome://communicator/content/pref/pref-popups.xul"/>
+ <treeitem id="passwordsItem"
+ label="&passwords.label;"
+ prefpane="passwords_pane"
+ url="chrome://pippki/content/pref-passwords.xul"
+ helpTopic="passwords_prefs"/>
+ <treeitem id="sslItem"
+ label="&ssltls.label;"
+ prefpane="ssl_pane"
+ url="chrome://pippki/content/pref-ssl.xul"
+ helpTopic="ssl_prefs"/>
+ <treeitem id="certItem"
+ label="&certs.label;"
+ prefpane="certs_pane"
+ url="chrome://pippki/content/pref-certs.xul"
+ helpTopic="certs_prefs"/>
+ </treechildren>
+ </treeitem>
+
+ <!-- Sync
+ <treeitem id="syncItem"
+ label="&sync.label;"
+ prefpane="sync_pane"
+ url="chrome://communicator/content/pref/pref-sync.xul"
+ helpTopic="sync_prefs"/> -->
+
+ <!-- Advanced items -->
+ <treeitem container="true"
+ id="advancedItem"
+ label="&advance.label;"
+ prefpane="advanced_pane"
+ helpTopic="advanced_pref_advanced"
+ url="chrome://communicator/content/pref/pref-advanced.xul">
+ <treechildren id="advancedChildren">
+ <treeitem id="scriptsItem"
+ label="&scriptsAndWindows2.label;"
+ prefpane="scripts_pane"
+ helpTopic="advanced_pref_scripts"
+ url="chrome://communicator/content/pref/pref-scripts.xul"/>
+ <treeitem id="keynavItem"
+ label="&keynav.label;"
+ prefpane="keynav_pane"
+ helpTopic="advanced_pref_keyboard_nav"
+ url="chrome://communicator/content/pref/pref-keynav.xul"/>
+ <treeitem id="findasyoutypeItem"
+ label="&findAsYouType.label;"
+ prefpane="findasyoutype_pane"
+ helpTopic="advanced_pref_find_as_you_type"
+ url="chrome://communicator/content/pref/pref-findasyoutype.xul"/>
+ <treeitem id="cacheItem"
+ label="&cache.label;"
+ prefpane="cache_pane"
+ helpTopic="advanced_pref_cache"
+ url="chrome://communicator/content/pref/pref-cache.xul"/>
+ <treeitem id="offlineAppsItem"
+ label="&offlineApps.label;"
+ prefpane="offlineapps_pane"
+ helpTopic="advanced_pref_offlineapps"
+ url="chrome://communicator/content/pref/pref-offlineapps.xul"/>
+ <treeitem id="proxiesItem"
+ label="&proxies.label;"
+ prefpane="proxies_pane"
+ helpTopic="advanced_pref_proxies"
+ url="chrome://communicator/content/pref/pref-proxies.xul"/>
+ <treeitem id="httpItem"
+ label="&httpnetworking.label;"
+ prefpane="http_pane"
+ helpTopic="advanced_http_networking"
+ url="chrome://communicator/content/pref/pref-http.xul"/>
+ <treeitem id="smartupdateItem"
+ label="&smart.label;"
+ prefpane="smartupdate_pane"
+ helpTopic="advanced_pref_installation"
+ url="chrome://communicator/content/pref/pref-smartupdate.xul"/>
+ <treeitem id="mousewheelItem"
+ label="&mousewheel.label;"
+ prefpane="mousewheel_pane"
+ helpTopic="advanced_pref_mouse_wheel"
+ url="chrome://communicator/content/pref/pref-mousewheel.xul"/>
+ <treeitem id="debuggingItem"
+ label="&debugging.label;"
+ prefpane="debugging_pane"
+ helpTopic="advanced_pref_debugging"
+ url="chrome://communicator/content/pref/pref-debugging.xul"/>
+ </treechildren>
+ </treeitem>
+ </treechildren>
+ </tree>
+
+</prefwindow>
diff --git a/comm/suite/components/pref/content/prefpanels.css b/comm/suite/components/pref/content/prefpanels.css
new file mode 100755
index 0000000000..c38509d590
--- /dev/null
+++ b/comm/suite/components/pref/content/prefpanels.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+#handlersView > listitem {
+ -moz-binding: url("chrome://communicator/content/pref/prefpanels.xml#handler");
+}
+
+.listcell-iconic.handler-action[selected="true"] {
+ -moz-binding: url("chrome://communicator/content/pref/prefpanels.xml#handler-action-selected");
+}
+
+#offlineAppsList > listitem {
+ -moz-binding: url("chrome://communicator/content/pref/prefpanels.xml#offlineapp");
+}
+
+/*
+ * Font dialog menulist fixes
+ */
+#defaultFontType,
+#serif,
+#sans-serif,
+#monospace {
+ min-width: 30ch;
+}
+
+/*
+ * Calendar Event/Tasks menulist fix
+ */
+
+#defaults-itemtype-menulist {
+ min-width: 20ch;
+}
diff --git a/comm/suite/components/pref/content/prefpanels.xml b/comm/suite/components/pref/content/prefpanels.xml
new file mode 100644
index 0000000000..347eac9755
--- /dev/null
+++ b/comm/suite/components/pref/content/prefpanels.xml
@@ -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/. -->
+
+<!DOCTYPE bindings [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % applicationsDTD SYSTEM "chrome://communicator/locale/pref/pref-applications.dtd">
+ %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" extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <implementation>
+ <constructor>
+ this.doCommand();
+ </constructor>
+ <property name="type" readonly="true">
+ <getter>
+ return this.getAttribute("type");
+ </getter>
+ </property>
+ </implementation>
+ <content>
+ <xul:listcell class="listcell-iconic handler-type" align="center" crop="end"
+ xbl:inherits="tooltiptext=typeDescription,label=typeDescription,image=typeIcon,typeClass"/>
+ <xul:listcell anonid="action-cell" class="listcell-iconic handler-action" align="center" crop="end"
+ xbl:inherits="tooltiptext=actionDescription,label=actionDescription,image=actionIcon,appHandlerIcon,selected"/>
+ </content>
+ </binding>
+
+ <binding id="handler-action-selected" extends="chrome://global/content/bindings/listbox.xml#listcell">
+ <content>
+ <xul:menulist anonid="action-menu" class="actionsMenu" flex="1" crop="end" selectedIndex="1">
+ <xul:menupopup/>
+ </xul:menulist>
+ </content>
+
+ <implementation>
+ <constructor>
+ this.doCommand();
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="offlineapp" extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <content>
+ <xul:listcell xbl:inherits="label=host"/>
+ <xul:listcell xbl:inherits="label=usage"/>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/pref/jar.mn b/comm/suite/components/pref/jar.mn
new file mode 100644
index 0000000000..86f1472dc6
--- /dev/null
+++ b/comm/suite/components/pref/jar.mn
@@ -0,0 +1,73 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+* content/communicator/pref/preferences.xul (content/preferences.xul)
+ content/communicator/pref/preferences.js (content/preferences.js)
+ content/communicator/pref/prefpanels.css (content/prefpanels.css)
+ content/communicator/pref/prefpanels.xml (content/prefpanels.xml)
+ content/communicator/pref/pref-advanced.js (content/pref-advanced.js)
+ content/communicator/pref/pref-advanced.xul (content/pref-advanced.xul)
+ content/communicator/pref/pref-appearance.js (content/pref-appearance.js)
+* content/communicator/pref/pref-appearance.xul (content/pref-appearance.xul)
+* content/communicator/pref/pref-applications.xul (content/pref-applications.xul)
+ content/communicator/pref/pref-applications.js (content/pref-applications.js)
+ content/communicator/pref/pref-applicationManager.js (content/pref-applicationManager.js)
+ content/communicator/pref/pref-applicationManager.xul (content/pref-applicationManager.xul)
+ content/communicator/pref/pref-cache.js (content/pref-cache.js)
+ content/communicator/pref/pref-cache.xul (content/pref-cache.xul)
+ content/communicator/pref/pref-colors.js (content/pref-colors.js)
+ content/communicator/pref/pref-colors.xul (content/pref-colors.xul)
+ content/communicator/pref/pref-content.js (content/pref-content.js)
+ content/communicator/pref/pref-content.xul (content/pref-content.xul)
+ content/communicator/pref/pref-cookies.js (content/pref-cookies.js)
+ content/communicator/pref/pref-cookies.xul (content/pref-cookies.xul)
+ content/communicator/pref/pref-debugging.js (content/pref-debugging.js)
+ content/communicator/pref/pref-debugging.xul (content/pref-debugging.xul)
+ content/communicator/pref/pref-download.js (content/pref-download.js)
+ content/communicator/pref/pref-download.xul (content/pref-download.xul)
+ content/communicator/pref/pref-findasyoutype.js (content/pref-findasyoutype.js)
+ content/communicator/pref/pref-findasyoutype.xul (content/pref-findasyoutype.xul)
+ content/communicator/pref/pref-fonts.js (content/pref-fonts.js)
+ content/communicator/pref/pref-fonts.xul (content/pref-fonts.xul)
+ content/communicator/pref/pref-history.js (content/pref-history.js)
+ content/communicator/pref/pref-history.xul (content/pref-history.xul)
+ content/communicator/pref/pref-http.js (content/pref-http.js)
+ content/communicator/pref/pref-http.xul (content/pref-http.xul)
+ content/communicator/pref/pref-images.xul (content/pref-images.xul)
+ content/communicator/pref/pref-keynav.js (content/pref-keynav.js)
+ content/communicator/pref/pref-keynav.xul (content/pref-keynav.xul)
+ content/communicator/pref/pref-languages.js (content/pref-languages.js)
+ content/communicator/pref/pref-languages.xul (content/pref-languages.xul)
+ content/communicator/pref/pref-languages-add.xul (content/pref-languages-add.xul)
+ content/communicator/pref/pref-languages-add.js (content/pref-languages-add.js)
+ content/communicator/pref/pref-links.js (content/pref-links.js)
+ content/communicator/pref/pref-links.xul (content/pref-links.xul)
+ content/communicator/pref/pref-locationbar.js (content/pref-locationbar.js)
+ content/communicator/pref/pref-locationbar.xul (content/pref-locationbar.xul)
+* content/communicator/pref/pref-media.xul (content/pref-media.xul)
+ content/communicator/pref/pref-mousewheel.js (content/pref-mousewheel.js)
+* content/communicator/pref/pref-mousewheel.xul (content/pref-mousewheel.xul)
+ content/communicator/pref/pref-navigator.js (content/pref-navigator.js)
+ content/communicator/pref/pref-navigator.xul (content/pref-navigator.xul)
+ content/communicator/pref/pref-offlineapps.js (content/pref-offlineapps.js)
+ content/communicator/pref/pref-offlineapps.xul (content/pref-offlineapps.xul)
+ content/communicator/pref/pref-popups.js (content/pref-popups.js)
+ content/communicator/pref/pref-popups.xul (content/pref-popups.xul)
+ content/communicator/pref/pref-privatedata.js (content/pref-privatedata.js)
+ content/communicator/pref/pref-privatedata.xul (content/pref-privatedata.xul)
+ content/communicator/pref/pref-proxies.js (content/pref-proxies.js)
+ content/communicator/pref/pref-proxies.xul (content/pref-proxies.xul)
+ content/communicator/pref/pref-proxies-advanced.xul (content/pref-proxies-advanced.xul)
+ content/communicator/pref/pref-scripts.js (content/pref-scripts.js)
+ content/communicator/pref/pref-scripts.xul (content/pref-scripts.xul)
+ content/communicator/pref/pref-search.js (content/pref-search.js)
+ content/communicator/pref/pref-search.xul (content/pref-search.xul)
+ content/communicator/pref/pref-security.js (content/pref-security.js)
+ content/communicator/pref/pref-security.xul (content/pref-security.xul)
+ content/communicator/pref/pref-smartupdate.js (content/pref-smartupdate.js)
+ content/communicator/pref/pref-smartupdate.xul (content/pref-smartupdate.xul)
+ content/communicator/pref/pref-spelling.js (content/pref-spelling.js)
+ content/communicator/pref/pref-spelling.xul (content/pref-spelling.xul)
+* content/communicator/pref/pref-tabs.xul (content/pref-tabs.xul)
diff --git a/comm/suite/components/pref/moz.build b/comm/suite/components/pref/moz.build
new file mode 100644
index 0000000000..7c2a1e68e6
--- /dev/null
+++ b/comm/suite/components/pref/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+BROWSER_CHROME_MANIFESTS += [
+ "tests/browser/browser.ini",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/pref/tests/browser/browser.ini b/comm/suite/components/pref/tests/browser/browser.ini
new file mode 100644
index 0000000000..04cb66aaaa
--- /dev/null
+++ b/comm/suite/components/pref/tests/browser/browser.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[browser_bug410900.js]
diff --git a/comm/suite/components/pref/tests/browser/browser_bug410900.js b/comm/suite/components/pref/tests/browser/browser_bug410900.js
new file mode 100644
index 0000000000..97275dd6b2
--- /dev/null
+++ b/comm/suite/components/pref/tests/browser/browser_bug410900.js
@@ -0,0 +1,55 @@
+function test() {
+ waitForExplicitFinish();
+
+ // Setup a phony handler to ensure the app pane will be populated.
+ var handler = Cc["@mozilla.org/uriloader/web-handler-app;1"]
+ .createInstance(Ci.nsIWebHandlerApp);
+ handler.name = "App pane alive test";
+ handler.uriTemplate = "http://test.mozilla.org/%s";
+
+ var extps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var info = extps.getProtocolHandlerInfo("apppanetest");
+ info.possibleApplicationHandlers.appendElement(handler);
+
+ var hserv = Cc["@mozilla.org/uriloader/handler-service;1"]
+ .getService(Ci.nsIHandlerService);
+ hserv.store(info);
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+
+ function observer(win, topic, data) {
+ if (topic != "app-handler-pane-loaded")
+ return;
+
+ obs.removeObserver(observer, "app-handler-pane-loaded");
+ runTest(win);
+ }
+ obs.addObserver(observer, "app-handler-pane-loaded");
+
+ openDialog("chrome://communicator/content/pref/preferences.xul",
+ "PrefWindow", "chrome,titlebar,dialog=no,resizable",
+ "applications_pane");
+}
+
+function runTest(win) {
+ var sel = win.document.documentElement.getAttribute("lastSelected");
+ ok(sel == "applications_pane", "Specified pane was opened");
+
+ var rbox = win.document.getElementById("handlersView");
+ ok(rbox, "handlersView is present");
+
+ var items = rbox && rbox.getElementsByTagName("listitem");
+ ok(items && items.length > 0, "App handler list populated");
+
+ var handlerAdded = false;
+ for (var i = 0; i < items.length; i++) {
+ if (items[i].getAttribute("type") == "apppanetest")
+ handlerAdded = true;
+ }
+ ok(handlerAdded, "apppanetest protocol handler was succesfully added");
+
+ win.close();
+ finish();
+}
diff --git a/comm/suite/components/profile/content/profileSelection.js b/comm/suite/components/profile/content/profileSelection.js
new file mode 100644
index 0000000000..6400d73359
--- /dev/null
+++ b/comm/suite/components/profile/content/profileSelection.js
@@ -0,0 +1,344 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gProfileBundle;
+var gBrandBundle;
+var gProfileService;
+var gProfileManagerMode = "selection";
+var gDialogParams = window.arguments[0]
+ .QueryInterface(Ci.nsIDialogParamBlock);
+
+function StartUp()
+{
+ gProfileBundle = document.getElementById("bundle_profile");
+ gBrandBundle = document.getElementById("bundle_brand");
+ if (gDialogParams.objects) {
+ document.documentElement.getButton("accept").setAttribute("label",
+ document.documentElement.getAttribute("buttonlabelstart"));
+ document.documentElement.getButton("cancel").setAttribute("label",
+ document.documentElement.getAttribute("buttonlabelexit"));
+ document.getElementById('intro').textContent =
+ document.getElementById('intro').getAttribute("start");
+ document.getElementById('offlineState').hidden = false;
+ gDialogParams.SetInt(0, 0);
+ }
+
+ gProfileService = Cc["@mozilla.org/toolkit/profile-service;1"]
+ .getService(Ci.nsIToolkitProfileService);
+ var profileEnum = gProfileService.profiles;
+ var selectedProfile = null;
+ try {
+ selectedProfile = gProfileService.selectedProfile;
+ }
+ catch (ex) {
+ }
+ while (profileEnum.hasMoreElements()) {
+ AddItem(profileEnum.getNext().QueryInterface(Ci.nsIToolkitProfile),
+ selectedProfile);
+ }
+
+ var autoSelect = document.getElementById("autoSelect");
+ if (Services.prefs.getBoolPref("profile.manage_only_at_launch"))
+ autoSelect.hidden = true;
+ else
+ autoSelect.checked = gProfileService.startWithLastProfile;
+
+ DoEnabling();
+}
+
+// function : <profileSelection.js>::AddItem();
+// purpose : utility function for adding items to a tree.
+function AddItem(aProfile, aProfileToSelect)
+{
+ var tree = document.getElementById("profiles");
+ var treeitem = document.createElement("treeitem");
+ var treerow = document.createElement("treerow");
+ var treecell = document.createElement("treecell");
+ var treetip = document.getElementById("treetip");
+ var profileDir = gProfileService.getProfileByName(aProfile.name).rootDir;
+
+ treecell.setAttribute("label", aProfile.name);
+ treerow.appendChild(treecell);
+ treeitem.appendChild(treerow);
+ treeitem.setAttribute("tooltip", profileDir.path);
+ treetip.setAttribute("value", profileDir.path);
+ tree.lastChild.appendChild(treeitem);
+ treeitem.profile = aProfile;
+ if (aProfile == aProfileToSelect) {
+ var profileIndex = tree.view.getIndexOfItem(treeitem);
+ tree.view.selection.select(profileIndex);
+ tree.treeBoxObject.ensureRowIsVisible(profileIndex);
+ }
+}
+
+// function : <profileSelection.js>::AcceptDialog();
+// purpose : sets the current profile to the selected profile (user choice: "Start Mozilla")
+function AcceptDialog()
+{
+ var autoSelect = document.getElementById("autoSelect");
+ if (!autoSelect.hidden) {
+ gProfileService.startWithLastProfile = autoSelect.checked;
+ gProfileService.flush();
+ }
+
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+
+ if (!gDialogParams.objects) {
+ var profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ var profLD = Services.dirsvc.get("ProfLD", Ci.nsIFile);
+
+ if (selected.profile.rootDir.equals(profD) &&
+ selected.profile.localDir.equals(profLD))
+ return true;
+ }
+
+ try {
+ var profileLock = selected.profile.lock({});
+ gProfileService.selectedProfile = selected.profile;
+ gProfileService.defaultProfile = selected.profile;
+ gProfileService.flush();
+ if (gDialogParams.objects) {
+ gDialogParams.objects.insertElementAt(profileLock, 0);
+ gProfileService.startOffline = document.getElementById("offlineState").checked;
+ gDialogParams.SetInt(0, 1);
+ gDialogParams.SetString(0, selected.profile.name);
+ return true;
+ }
+ profileLock.unlock();
+ } catch (e) {
+ var brandName = gBrandBundle.getString("brandShortName");
+ var message = gProfileBundle.getFormattedString("dirLocked",
+ [brandName, selected.profile.name]);
+ Services.prompt.alert(window, null, message);
+ return false;
+ }
+
+ // Although switching profile works by performing a restart internally,
+ // the user is quitting the old profile, so make it look like a quit.
+ var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
+ .createInstance(Ci.nsISupportsPRBool);
+ Services.obs.notifyObservers(cancelQuit, "quit-application-requested");
+
+ if (cancelQuit.data) {
+ return false;
+ }
+
+ try {
+ var env = Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment);
+ env.set("XRE_PROFILE_NAME", selected.profile.name);
+ env.set("XRE_PROFILE_PATH", selected.profile.rootDir.path);
+ env.set("XRE_PROFILE_LOCAL_PATH", selected.profile.localDir.path);
+ var app = Services.startup;
+ app.quit(app.eAttemptQuit | app.eRestart);
+ return true;
+ }
+ catch (e) {
+ env.set("XRE_PROFILE_NAME", "");
+ env.set("XRE_PROFILE_PATH", "");
+ env.set("XRE_PROFILE_LOCAL_PATH", "");
+ return false;
+ }
+}
+
+// invoke the createProfile Wizard
+function CreateProfileWizard()
+{
+ window.openDialog('chrome://mozapps/content/profile/createProfileWizard.xul',
+ '', 'centerscreen,chrome,modal,titlebar');
+}
+
+// update the display to show the additional profile
+function CreateProfile(aProfile)
+{
+ gProfileService.flush();
+ AddItem(aProfile, aProfile);
+}
+
+// rename the selected profile
+function RenameProfile()
+{
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+ var profileName = selected.profile.name;
+ var newName = {value: profileName};
+ var dialogTitle = gProfileBundle.getString("renameProfileTitle");
+ var msg = gProfileBundle.getFormattedString("renameProfilePrompt", [profileName]);
+ var ps = Services.prompt;
+ if (ps.prompt(window, dialogTitle, msg, newName, null, {value: 0}) &&
+ newName.value != profileName) {
+ if (!/\S/.test(newName.value)) {
+ ps.alert(window, gProfileBundle.getString("profileNameInvalidTitle"),
+ gProfileBundle.getString("profileNameEmpty"));
+ return false;
+ }
+
+ if (/([\\*:?<>|\/\"])/.test(newName.value)) {
+ ps.alert(window, gProfileBundle.getString("profileNameInvalidTitle"),
+ gProfileBundle.getFormattedString("invalidChar", [RegExp.$1]));
+ return false;
+ }
+
+ try {
+ gProfileService.getProfileByName(newName.value);
+ ps.alert(window, gProfileBundle.getString("profileExistsTitle"),
+ gProfileBundle.getString("profileExists"));
+ return false;
+ }
+ catch (e) {
+ }
+
+ selected.profile.name = newName.value;
+ gProfileService.flush();
+ selected.firstChild.firstChild.setAttribute("label", newName.value);
+ }
+}
+
+function ConfirmDelete()
+{
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+ if (!selected.profile.rootDir.exists()) {
+ DeleteProfile(false);
+ return;
+ }
+
+ try {
+ var profileLock = selected.profile.lock({});
+ var dialogTitle = gProfileBundle.getString("deleteTitle");
+ var dialogText;
+
+ var path = selected.profile.rootDir.path;
+ dialogText = gProfileBundle.getFormattedString("deleteProfile", [path]);
+ var ps = Services.prompt;
+ var buttonPressed = ps.confirmEx(window, dialogTitle, dialogText,
+ (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
+ (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1) +
+ (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_2),
+ gProfileBundle.getString("dontDeleteFiles"), null,
+ gProfileBundle.getString("deleteFiles"), null, {value: 0});
+ profileLock.unlock();
+ if (buttonPressed != 1)
+ DeleteProfile(buttonPressed == 2);
+ } catch (e) {
+ var dialogTitle = gProfileBundle.getString("deleteTitle");
+ var brandName = gBrandBundle.getString("brandShortName");
+ var dialogText = gProfileBundle.getFormattedString("deleteLocked",
+ [brandName, selected.profile.name]);
+ ps.alert(window, dialogTitle, dialogText);
+ }
+}
+
+// Delete the profile, with the delete flag set as per instruction above.
+function DeleteProfile(aDeleteFiles)
+{
+ var profileTree = document.getElementById("profiles");
+ var selected = profileTree.view.getItemAtIndex(profileTree.currentIndex);
+ var previous = profileTree.currentIndex && profileTree.currentIndex - 1;
+
+ try {
+ selected.profile.remove(aDeleteFiles);
+ gProfileService.flush();
+ selected.remove();
+
+ if (profileTree.view.rowCount != 0) {
+ profileTree.view.selection.select(previous);
+ profileTree.treeBoxObject.ensureRowIsVisible(previous);
+ }
+
+ // set the button state
+ DoEnabling();
+ }
+ catch (ex) {
+ dump("Exception during profile deletion.\n");
+ }
+}
+
+function SwitchProfileManagerMode()
+{
+ var captionLine;
+ var prattleIndex;
+
+ if (gProfileManagerMode == "selection") {
+ prattleIndex = 1;
+ captionLine = gProfileBundle.getString("manageTitle");
+
+ document.getElementById("profiles").focus();
+
+ // hide the manage profiles button...
+ document.documentElement.getButton("extra2").hidden = true;
+ gProfileManagerMode = "manager";
+ }
+ else {
+ prattleIndex = 0;
+ captionLine = gProfileBundle.getString("selectTitle");
+ gProfileManagerMode = "selection";
+ }
+
+ // swap deck
+ document.getElementById("prattle").selectedIndex = prattleIndex;
+
+ // change the title of the profile manager/selection window.
+ document.getElementById("header").setAttribute("description", captionLine);
+ document.title = captionLine;
+}
+
+// do button enabling based on tree selection
+function DoEnabling()
+{
+ var acceptButton = document.documentElement.getButton("accept");
+ var deleteButton = document.getElementById("deleteButton");
+ var renameButton = document.getElementById("renameButton");
+
+ var disabled = document.getElementById("profiles").view.selection.count == 0;
+ acceptButton.disabled = disabled;
+ deleteButton.disabled = disabled;
+ renameButton.disabled = disabled;
+}
+
+// handle key event on tree
+function HandleKeyEvent(aEvent)
+{
+ if (gProfileManagerMode != "manager")
+ return;
+
+ switch (aEvent.keyCode)
+ {
+ case KeyEvent.DOM_VK_BACK_SPACE:
+ case KeyEvent.DOM_VK_DELETE:
+ if (!document.getElementById("deleteButton").disabled)
+ ConfirmDelete();
+ break;
+ case KeyEvent.DOM_VK_F2:
+ if (!document.getElementById("renameButton").disabled)
+ RenameProfile();
+ }
+}
+
+function HandleClickEvent(aEvent)
+{
+ if (aEvent.button == 0 && aEvent.target.parentNode.view.selection.count != 0 && AcceptDialog()) {
+ window.close();
+ return true;
+ }
+
+ return false;
+}
+
+function HandleToolTipEvent(aEvent)
+{
+ var treeTip = document.getElementById("treetip");
+ var tree = document.getElementById("profiles");
+
+ var cell = tree.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY);
+ if (cell.row < 0)
+ aEvent.preventDefault();
+ else
+ treeTip.label = tree.view.getItemAtIndex(cell.row).tooltip;
+}
diff --git a/comm/suite/components/profile/content/profileSelection.xul b/comm/suite/components/profile/content/profileSelection.xul
new file mode 100644
index 0000000000..dd62b4a7dd
--- /dev/null
+++ b/comm/suite/components/profile/content/profileSelection.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/profile/profile.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % profileDTD SYSTEM "chrome://communicator/locale/profile/profileSelection.dtd">
+%profileDTD;
+]>
+
+<dialog id="profileWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&windowTitle.label;"
+ windowtype="mozilla:profileSelection"
+ orient="vertical"
+ style="width: 42em;"
+ buttons="accept,cancel,extra2"
+ buttonlabelaccept="&select.label;"
+ buttonlabelstart="&start.label;"
+ buttonlabelexit="&exit.label;"
+ buttonlabelextra2="&manage.label;"
+ buttonaccesskeyextra2="&manage.accesskey;"
+ ondialogaccept="return AcceptDialog();"
+ ondialogextra2="SwitchProfileManagerMode();"
+ onload="StartUp();">
+
+ <stringbundle id="bundle_profile"
+ src="chrome://communicator/locale/profile/profileSelection.properties"/>
+ <stringbundle id="bundle_brand"
+ src="chrome://branding/locale/brand.properties"/>
+
+ <script src="chrome://communicator/content/profile/profileSelection.js"/>
+ <script src="chrome://mozapps/content/profile/createProfileWizard.js"/>
+
+ <dialogheader id="header" title="&profileManager.title;" description="&windowTitle.label;"/>
+
+ <hbox class="wizard-box" flex="1">
+
+ <!-- instructions -->
+ <deck id="prattle">
+ <description id="intro" start="&introStart.label;">&introSwitch.label;</description>
+ <vbox>
+ <description id="label">&profileManagerText.label;</description>
+ <separator/>
+ <hbox>
+ <vbox flex="1" id="managebuttons">
+ <button id="newButton" label="&newButton.label;" accesskey="&newButton.accesskey;" oncommand="CreateProfileWizard();"/>
+ <button id="renameButton" label="&renameButton.label;" accesskey="&renameButton.accesskey;" oncommand="RenameProfile();"/>
+ <button id="deleteButton" label="&deleteButton.label;" accesskey="&deleteButton.accesskey;" oncommand="ConfirmDelete();"/>
+ </vbox>
+ <spacer flex="2"/>
+ </hbox>
+ </vbox>
+ </deck>
+
+ <separator class="thin" orient="vertical"/>
+
+ <vbox flex="1">
+ <tooltip id="treetip"
+ onpopupshowing="HandleToolTipEvent(event);">
+ </tooltip>
+ <tree id="profiles" flex="1" seltype="single"
+ hidecolumnpicker="true"
+ onselect="DoEnabling();"
+ onkeypress="HandleKeyEvent(event);">
+ <treecols>
+ <treecol label="&availableProfiles.label;" flex="1" sortLocked="true"/>
+ </treecols>
+ <treechildren tooltip="treetip"
+ ondblclick="HandleClickEvent(event);"/>
+ </tree>
+ <checkbox id="offlineState" label="&offlineState.label;" accesskey="&offlineState.accesskey;" hidden="true"/>
+ <checkbox id="autoSelect" label="&autoSelect.label;" accesskey="&autoSelect.accesskey;"/>
+ </vbox>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/components/profile/jar.mn b/comm/suite/components/profile/jar.mn
new file mode 100644
index 0000000000..bf9706ba55
--- /dev/null
+++ b/comm/suite/components/profile/jar.mn
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+% override chrome://mozapps/content/profile/profileSelection.xul chrome://communicator/content/profile/profileSelection.xul
+ content/communicator/profile/profileSelection.js (content/profileSelection.js)
+ content/communicator/profile/profileSelection.xul (content/profileSelection.xul)
diff --git a/comm/suite/components/profile/moz.build b/comm/suite/components/profile/moz.build
new file mode 100644
index 0000000000..0f2d51ba64
--- /dev/null
+++ b/comm/suite/components/profile/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ "nsSuiteDirectoryProvider.cpp",
+]
+
+FINAL_LIBRARY = "suite"
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/profile/nsSuiteDirectoryProvider.cpp b/comm/suite/components/profile/nsSuiteDirectoryProvider.cpp
new file mode 100755
index 0000000000..9d19615625
--- /dev/null
+++ b/comm/suite/components/profile/nsSuiteDirectoryProvider.cpp
@@ -0,0 +1,248 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsSuiteDirectoryProvider.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIPrefBranch.h"
+#include "nsDirectoryServiceDefs.h"
+#include "mozilla/intl/LocaleService.h"
+#include "nsIPrefService.h"
+#include "nsArrayEnumerator.h"
+#include "nsEnumeratorUtils.h"
+
+using mozilla::intl::LocaleService;
+
+NS_IMPL_ISUPPORTS(nsSuiteDirectoryProvider,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2)
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::GetFile(const char *aKey,
+ bool *aPersist,
+ nsIFile* *aResult)
+{
+ // NOTE: This function can be reentrant through the NS_GetSpecialDirectory
+ // call, so be careful not to cause infinite recursion.
+ // i.e. the check for supported files must come first.
+ const char* leafName = nullptr;
+
+ if (!strcmp(aKey, NS_APP_BOOKMARKS_50_FILE))
+ leafName = "bookmarks.html";
+ else if (!strcmp(aKey, NS_APP_USER_PANELS_50_FILE))
+ leafName = "panels.rdf";
+ else
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> parentDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(parentDir));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIFile> file;
+ rv = parentDir->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDependentCString leafStr(leafName);
+ file->AppendNative(leafStr);
+
+ bool exists;
+ if (NS_SUCCEEDED(file->Exists(&exists)) && !exists)
+ EnsureProfileFile(leafStr, parentDir, file);
+
+ *aPersist = true;
+ NS_IF_ADDREF(*aResult = file);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::GetFiles(const char *aKey,
+ nsISimpleEnumerator* *aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMArray<nsIFile> baseFiles;
+ AppendDistroSearchDirs(dirSvc, baseFiles);
+
+ nsCOMPtr<nsISimpleEnumerator> baseEnum;
+ rv = NS_NewArrayEnumerator(getter_AddRefs(baseEnum), baseFiles);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_ERROR_FAILURE;
+}
+
+void
+nsSuiteDirectoryProvider::EnsureProfileFile(const nsACString& aLeafName,
+ nsIFile* aParentDir,
+ nsIFile* aTarget)
+{
+ nsCOMPtr<nsIFile> defaultsDir;
+
+ NS_GetSpecialDirectory(NS_APP_DEFAULTS_50_DIR,
+ getter_AddRefs(defaultsDir));
+ if (!defaultsDir)
+ return;
+
+ nsresult rv = defaultsDir->AppendNative("profile"_ns);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ defaultsDir->AppendNative(aLeafName);
+
+ defaultsDir->CopyToNative(aParentDir, aLeafName);
+}
+
+NS_IMPL_ISUPPORTS(nsSuiteDirectoryProvider::AppendingEnumerator,
+ nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::AppendingEnumerator::HasMoreElements(bool *aResult)
+{
+ *aResult = mNext != nullptr;
+ return NS_OK;
+}
+
+void
+nsSuiteDirectoryProvider::AppendingEnumerator::GetNext()
+{
+ // Ignore all errors
+
+ bool more;
+ while (NS_SUCCEEDED(mBase->HasMoreElements(&more)) && more) {
+ nsCOMPtr<nsISupports> nextSupports;
+ mBase->GetNext(getter_AddRefs(nextSupports));
+
+ mNext = do_QueryInterface(nextSupports);
+ if (!mNext)
+ continue;
+
+ mNext->AppendNative(mLeafName);
+
+ bool exists;
+ if (NS_SUCCEEDED(mNext->Exists(&exists)) && exists)
+ return;
+ }
+
+ mNext = nullptr;
+}
+
+NS_IMETHODIMP
+nsSuiteDirectoryProvider::AppendingEnumerator::GetNext(nsISupports* *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!mNext) {
+ *aResult = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ADDREF(*aResult = mNext);
+
+ GetNext();
+
+ return NS_OK;
+}
+
+nsSuiteDirectoryProvider::AppendingEnumerator::AppendingEnumerator
+ (nsISimpleEnumerator* aBase, const char* const aLeafName) :
+ mBase(aBase), mLeafName(aLeafName)
+{
+ // Initialize mNext to begin.
+ GetNext();
+}
+
+// Appends the distribution-specific search engine directories to the
+// array. The directory structure is as follows:
+
+// appdir/
+// \- distribution/
+// \- searchplugins/
+// |- common/
+// \- locale/
+// |- <locale 1>/
+// ...
+// \- <locale N>/
+
+// common engines are loaded for all locales. If there is no locale
+// directory for the current locale, there is a pref:
+// "distribution.searchplugins.defaultLocale"
+// which specifies a default locale to use.
+
+void
+nsSuiteDirectoryProvider::AppendDistroSearchDirs(nsIProperties* aDirSvc,
+ nsCOMArray<nsIFile> &array)
+{
+ nsCOMPtr<nsIFile> searchPlugins;
+ nsresult rv = aDirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(searchPlugins));
+ if (NS_FAILED(rv))
+ return;
+ searchPlugins->AppendNative("distribution"_ns);
+ searchPlugins->AppendNative("searchplugins"_ns);
+
+ bool exists;
+ rv = searchPlugins->Exists(&exists);
+ if (NS_FAILED(rv) || !exists)
+ return;
+
+ nsCOMPtr<nsIFile> commonPlugins;
+ rv = searchPlugins->Clone(getter_AddRefs(commonPlugins));
+ if (NS_SUCCEEDED(rv)) {
+ commonPlugins->AppendNative("common"_ns);
+ rv = commonPlugins->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists)
+ array.AppendObject(commonPlugins);
+ }
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs) {
+ nsCOMPtr<nsIFile> localePlugins;
+ rv = searchPlugins->Clone(getter_AddRefs(localePlugins));
+ if (NS_FAILED(rv))
+ return;
+
+ localePlugins->AppendNative("locale"_ns);
+
+ // we didn't append the locale dir - try the default one
+ nsCString defLocale;
+ rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale",
+ defLocale);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIFile> defLocalePlugins;
+ rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins));
+ if (NS_SUCCEEDED(rv)) {
+ defLocalePlugins->AppendNative(defLocale);
+ rv = defLocalePlugins->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists) {
+ array.AppendObject(defLocalePlugins);
+ return; // all done
+ }
+ }
+ }
+
+ // we didn't have a defaultLocale, use the user agent locale
+ nsAutoCString locale;
+ LocaleService::GetInstance()->GetAppLocaleAsLangTag(locale);
+
+ nsCOMPtr<nsIFile> curLocalePlugins;
+ rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins));
+ if (NS_SUCCEEDED(rv)) {
+ curLocalePlugins->AppendNative(locale);
+ rv = curLocalePlugins->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && exists) {
+ array.AppendObject(curLocalePlugins);
+ return; // all done
+ }
+ }
+ }
+}
diff --git a/comm/suite/components/profile/nsSuiteDirectoryProvider.h b/comm/suite/components/profile/nsSuiteDirectoryProvider.h
new file mode 100644
index 0000000000..6a06be3c30
--- /dev/null
+++ b/comm/suite/components/profile/nsSuiteDirectoryProvider.h
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SuiteDirectoryProvider_h__
+#define SuiteDirectoryProvider_h__
+
+#include "nsCOMArray.h"
+#include "nsIDirectoryService.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIProperties.h"
+#include "mozilla/Attributes.h"
+#include "nsSuiteCID.h"
+
+#define NS_APP_BOOKMARKS_50_FILE "BMarks"
+
+class nsSuiteDirectoryProvider final : public nsIDirectoryServiceProvider2
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+private:
+ ~nsSuiteDirectoryProvider() {}
+
+ void EnsureProfileFile(const nsACString& aLeafName,
+ nsIFile* aParentDir, nsIFile* aTarget);
+
+ void AppendDistroSearchDirs(nsIProperties* aDirSvc,
+ nsCOMArray<nsIFile> &array);
+
+ void AppendFileKey(const char *key, nsIProperties* aDirSvc,
+ nsCOMArray<nsIFile> &array);
+
+ class AppendingEnumerator final : public nsISimpleEnumerator
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ AppendingEnumerator(nsISimpleEnumerator* aBase,
+ const char* const aLeafName);
+
+ private:
+ ~AppendingEnumerator() {}
+ void GetNext();
+
+ nsCOMPtr<nsISimpleEnumerator> mBase;
+ nsDependentCString mLeafName;
+ nsCOMPtr<nsIFile> mNext;
+ };
+};
+
+#endif
diff --git a/comm/suite/components/sanitize/Sanitizer.jsm b/comm/suite/components/sanitize/Sanitizer.jsm
new file mode 100644
index 0000000000..1207827423
--- /dev/null
+++ b/comm/suite/components/sanitize/Sanitizer.jsm
@@ -0,0 +1,947 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["Sanitizer"];
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ AppConstants: "resource://gre/modules/AppConstants.jsm",
+ console: "resource://gre/modules/Console.jsm",
+ Downloads: "resource://gre/modules/Downloads.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+ FormHistory: "resource://gre/modules/FormHistory.jsm",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+ setTimeout: "resource://gre/modules/Timer.jsm",
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager",
+ "@mozilla.org/serviceworkers/manager;1",
+ "nsIServiceWorkerManager");
+XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService",
+ "@mozilla.org/dom/quota-manager-service;1",
+ "nsIQuotaManagerService");
+
+// Used as unique id for pending sanitizations.
+var gPendingSanitizationSerial = 0;
+
+/**
+ * A number of iterations after which to yield time back
+ * to the system.
+ */
+const YIELD_PERIOD = 10;
+
+var Sanitizer = {
+ /**
+ * Whether we should sanitize on shutdown.
+ */
+ PREF_SANITIZE_ON_SHUTDOWN: "privacy.sanitize.sanitizeOnShutdown",
+
+ /**
+ * During a sanitization this is set to a JSON containing an array of the
+ * pending sanitizations. This allows to retry sanitizations on startup in
+ * case they dind't run or were interrupted by a crash.
+ * Use addPendingSanitization and removePendingSanitization to manage it.
+ */
+ PREF_PENDING_SANITIZATIONS: "privacy.sanitize.pending",
+
+ /**
+ * Pref branches to fetch sanitization options from.
+ */
+ PREF_CPD_BRANCH: "privacy.cpd.",
+ PREF_SHUTDOWN_BRANCH: "privacy.clearOnShutdown.",
+
+ /**
+ * The fallback timestamp used when no argument is given to
+ * Sanitizer.getClearRange.
+ */
+ PREF_TIMESPAN: "privacy.sanitize.timeSpan",
+
+ /**
+ * Time span constants corresponding to values of the preference
+ * privacy.sanitize.timeSpan It is used to determine how much history
+ * to clear, for various items.
+ */
+ TIMESPAN_EVERYTHING: 0,
+ TIMESPAN_HOUR: 1,
+ TIMESPAN_2HOURS: 2,
+ TIMESPAN_4HOURS: 3,
+ TIMESPAN_TODAY: 4,
+ TIMESPAN_5MIN: 5,
+ TIMESPAN_24HOURS: 6,
+
+ /**
+ * Whether we should sanitize on shutdown.
+ * When this is set, a pending sanitization should also be added and removed
+ * when shutdown sanitization is complete. This allows to retry incomplete
+ * sanitizations on startup.
+ */
+ shouldSanitizeOnShutdown: false,
+
+ /**
+ * Shows a sanitization dialog to the user.
+ *
+ * @param [optional] parentWindow the window to use as
+ * parent for the created dialog.
+ */
+ showUI(parentWindow) {
+ let win = AppConstants.platform == "macosx" ?
+ null : // make this an app-modal window on Mac
+ parentWindow;
+ Services.ww.openWindow(win,
+ "chrome://communicator/content/sanitizeDialog.xul",
+ "Sanitize",
+ "chrome,titlebar,centerscreen,dialog,modal",
+ null);
+ },
+
+ /**
+ * Performs startup tasks:
+ * - Checks if sanitizations were not completed during the last session.
+ * - Registers sanitize-on-shutdown.
+ */
+ async onStartup() {
+ // First, collect pending sanitizations from the last session, before we
+ // add pending sanitizations for this session.
+ let pendingSanitizations = getAndClearPendingSanitizations();
+
+ // Check if we should sanitize on shutdown.
+ this.shouldSanitizeOnShutdown =
+ Services.prefs.getBoolPref(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, false);
+ Services.prefs.addObserver(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, this,
+ true);
+ // Add a pending shutdown sanitization, if necessary.
+ if (this.shouldSanitizeOnShutdown) {
+ let itemsToClear =
+ getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
+ addPendingSanitization("shutdown", itemsToClear, {});
+ }
+ // Shutdown sanitization is always pending, but the user may change the
+ // sanitize on shutdown prefs during the session. Then the pending
+ // sanitization would become stale and must be updated.
+ Services.prefs.addObserver(Sanitizer.PREF_SHUTDOWN_BRANCH, this, true);
+
+ // Make sure that we are triggered during shutdown.
+ let shutdownClient = PlacesUtils.history.shutdownClient.jsclient;
+ // We need to pass to sanitize() (through sanitizeOnShutdown) a state
+ // object that tracks the status of the shutdown blocker. This 'progress'
+ // object will be updated during sanitization and reported with the crash
+ // in case of a shutdown timeout.
+ // We use the `options` argument to pass the `progress` object to
+ // sanitize().
+ let progress = { isShutdown: true };
+ shutdownClient.addBlocker("sanitize.js: Sanitize on shutdown",
+ () => sanitizeOnShutdown(progress),
+ {fetchState: () => ({ progress })}
+ );
+
+ // Finally, run the sanitizations that were left pending, because we
+ // crashed before completing them.
+ for (let {itemsToClear, options} of pendingSanitizations) {
+ try {
+ await this.sanitize(itemsToClear, options);
+ } catch (ex) {
+ Cu.reportError("A previously pending sanitization failed: " +
+ itemsToClear + "\n" + ex);
+ }
+ }
+ },
+
+ /**
+ * Returns a 2 element array representing the start and end times,
+ * in the uSec-since-epoch format that PRTime likes. If we should
+ * clear everything, this function returns null.
+ *
+ * @param ts [optional] a timespan to convert to start and end time.
+ * Falls back to the privacy.sanitize.timeSpan
+ * preference if this argument is omitted.
+ * If this argument is provided, it has to be one of the
+ * Sanitizer.TIMESPAN_* constants. This function will
+ * throw an error otherwise.
+ *
+ * @return {Array} a 2-element Array containing the start and end times.
+ */
+ getClearRange(ts) {
+ if (ts === undefined)
+ ts = Services.prefs.getIntPref(Sanitizer.PREF_TIMESPAN);
+ if (ts === Sanitizer.TIMESPAN_EVERYTHING)
+ return null;
+
+ // PRTime is microseconds while JS time is milliseconds
+ var endDate = Date.now() * 1000;
+ switch (ts) {
+ case Sanitizer.TIMESPAN_5MIN :
+ // 5*60*1000000
+ var startDate = endDate - 300000000;
+ break;
+ case Sanitizer.TIMESPAN_HOUR :
+ // 1*60*60*1000000
+ startDate = endDate - 3600000000;
+ break;
+ case Sanitizer.TIMESPAN_2HOURS :
+ // 2*60*60*1000000
+ startDate = endDate - 7200000000;
+ break;
+ case Sanitizer.TIMESPAN_4HOURS :
+ // 4*60*60*1000000
+ startDate = endDate - 14400000000;
+ break;
+ case Sanitizer.TIMESPAN_TODAY :
+ // Start with today
+ var d = new Date();
+ // zero us back to midnight...
+ d.setHours(0);
+ d.setMinutes(0);
+ d.setSeconds(0);
+ // convert to epoch usec
+ startDate = d.valueOf() * 1000;
+ break;
+ case Sanitizer.TIMESPAN_24HOURS :
+ // 24*60*60*1000000
+ startDate = endDate - 86400000000;
+ break;
+ default:
+ throw "Invalid time span for clear private data: " + ts;
+ }
+ return [startDate, endDate];
+ },
+
+ /**
+ * Deletes privacy sensitive data in a batch, according to user preferences.
+ * Returns a promise which is resolved if no errors occurred. If an error
+ * occurs, a message is reported to the console and all other items are still
+ * cleared before the promise is finally rejected.
+ *
+ * @param [optional] itemsToClear
+ * Array of items to be cleared. if specified only those
+ * items get cleared, irrespectively of the preference settings.
+ * @param [optional] options
+ * Object whose properties are options for this sanitization:
+ * - ignoreTimespan (default: true): Time span only makes sense in
+ * certain cases. Consumers who want to only clear some private
+ * data can opt in by setting this to false, and can optionally
+ * specify a specific range.
+ * If timespan is not ignored, and range is not set, sanitize()
+ * will use the value of the timespan pref to determine a range.
+ * - range (default: null)
+ * - privateStateForNewWindow (default: "non-private"): when clearing
+ * open windows, defines the private state for the newly opened
+ * window.
+ */
+ async sanitize(itemsToClear = null, options = {}) {
+ let progress = options.progress || {};
+ if (!itemsToClear)
+ itemsToClear = getItemsToClearFromPrefBranch(this.PREF_CPD_BRANCH);
+ let promise = sanitizeInternal(this.items, itemsToClear, progress,
+ options);
+
+ // Depending on preferences, the sanitizer may perform asynchronous
+ // work before it starts cleaning up the Places database (e.g. closing
+ // windows). We need to make sure that the connection to that database
+ // hasn't been closed by the time we use it.
+ // Though, if this is a sanitize on shutdown, we already have a blocker.
+ if (!progress.isShutdown) {
+ let shutdownClient = Cc["@mozilla.org/browser/nav-history-service;1"]
+ .getService(Ci.nsPIPlacesDatabase)
+ .shutdownClient
+ .jsclient;
+ shutdownClient.addBlocker("sanitize.js: Sanitize",
+ promise,
+ {
+ fetchState: () => ({ progress })
+ }
+ );
+ }
+
+ try {
+ await promise;
+ } finally {
+ Services.obs.notifyObservers(null, "sanitizer-sanitization-complete");
+ }
+ },
+
+ observe(subject, topic, data) {
+ if (topic == "nsPref:changed") {
+ if (data.startsWith(this.PREF_SHUTDOWN_BRANCH) &&
+ this.shouldSanitizeOnShutdown) {
+ // Update the pending shutdown sanitization.
+ removePendingSanitization("shutdown");
+ let itemsToClear =
+ getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
+ addPendingSanitization("shutdown", itemsToClear, {});
+ } else if (data == this.PREF_SANITIZE_ON_SHUTDOWN) {
+ this.shouldSanitizeOnShutdown =
+ Services.prefs.getBoolPref(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN,
+ false);
+ removePendingSanitization("shutdown");
+ if (this.shouldSanitizeOnShutdown) {
+ let itemsToClear =
+ getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
+ addPendingSanitization("shutdown", itemsToClear, {});
+ }
+ }
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsiObserver,
+ Ci.nsISupportsWeakReference
+ ]),
+
+ items: {
+ cache: {
+ async clear(range) {
+ let seenException;
+
+ try {
+ // Cache doesn't consult timespan, nor does it have the
+ // facility for timespan-based eviction. Wipe it.
+ Services.cache2.clear();
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ try {
+ let imageCache = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools)
+ .getImgCacheForDocument(null);
+ // clearCache: true=chrome, false=content.
+ imageCache.clearCache(false);
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ if (seenException) {
+ throw seenException;
+ }
+ }
+ },
+
+ cookies: {
+ async clear(range) {
+ let seenException;
+ let yieldCounter = 0;
+
+ // Clear cookies.
+ try {
+ if (range) {
+ // Iterate through the cookies and delete any created after our
+ // cutoff.
+ let cookiesEnum = Services.cookies.enumerator;
+ while (cookiesEnum.hasMoreElements()) {
+ let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
+
+ if (cookie.creationTime > range[0]) {
+ // This cookie was created after our cutoff, clear it
+ Services.cookies.remove(cookie.host, cookie.name, cookie.path,
+ false, cookie.originAttributes);
+
+ if (++yieldCounter % YIELD_PERIOD == 0) {
+ // Don't block the main thread too long
+ await new Promise(resolve => setTimeout(resolve, 0));
+ }
+ }
+ }
+ } else {
+ // Remove everything
+ Services.cookies.removeAll();
+ // Don't block the main thread too long
+ await new Promise(resolve => setTimeout(resolve, 0));
+ }
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ // Clear deviceIds. Done asynchronously (returns before complete).
+ try {
+ let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"]
+ .getService(Ci.nsIMediaManagerService);
+ mediaMgr.sanitizeDeviceIds(range && range[0]);
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ if (seenException) {
+ throw seenException;
+ }
+ },
+ },
+
+ offlineApps: {
+ async clear(range) {
+ // AppCache
+ ChromeUtils.import("resource:///modules/OfflineAppCacheHelper.jsm");
+ // This doesn't wait for the cleanup to be complete.
+ OfflineAppCacheHelper.clear();
+
+ // LocalStorage
+ Services.obs.notifyObservers(null, "extension:purge-localStorage");
+
+ // ServiceWorkers
+ let promises = [];
+ let serviceWorkers = serviceWorkerManager.getAllRegistrations();
+ for (let i = 0; i < serviceWorkers.length; i++) {
+ let sw = serviceWorkers
+ .queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
+
+ promises.push(new Promise(resolve => {
+ let unregisterCallback = {
+ unregisterSucceeded: () => { resolve(true); },
+ // We don't care about failures.
+ unregisterFailed: () => { resolve(true); },
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIServiceWorkerUnregisterCallback])
+ };
+
+ serviceWorkerManager.propagateUnregister(sw.principal,
+ unregisterCallback,
+ sw.scope);
+ }));
+ }
+
+ await Promise.all(promises);
+
+ // QuotaManager
+ promises = [];
+ await new Promise(resolve => {
+ quotaManagerService.getUsage(request => {
+ if (request.resultCode != Cr.NS_OK) {
+ // We are probably shutting down. We don't want to propagate the
+ // error, rejecting the promise.
+ resolve();
+ return;
+ }
+
+ for (let item of request.result) {
+ let principal =
+ Services.scriptSecurityManager
+ .createCodebasePrincipalFromOrigin(item.origin);
+ let uri = principal.URI;
+ if (uri.scheme == "http" || uri.scheme == "https" ||
+ uri.scheme == "file") {
+ promises.push(new Promise(r => {
+ let req =
+ quotaManagerService.clearStoragesForPrincipal(principal,
+ null, false);
+ req.callback = () => { r(); };
+ }));
+ }
+ }
+ resolve();
+ });
+ });
+
+ return Promise.all(promises);
+ }
+ },
+
+ history: {
+ async clear(range) {
+ let seenException;
+ try {
+ if (range) {
+ await PlacesUtils.history.removeVisitsByFilter({
+ beginDate: new Date(range[0] / 1000),
+ endDate: new Date(range[1] / 1000)
+ });
+ } else {
+ // Remove everything.
+ await PlacesUtils.history.clear();
+ }
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ try {
+ let clearStartingTime = range ? String(range[0]) : "";
+ Services.obs.notifyObservers(null, "browser:purge-session-history",
+ clearStartingTime);
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ try {
+ let predictor = Cc["@mozilla.org/network/predictor;1"]
+ .getService(Ci.nsINetworkPredictor);
+ predictor.reset();
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ if (seenException) {
+ throw seenException;
+ }
+ }
+ },
+
+ urlbar: {
+ async clear(range) {
+ let seenException;
+ // Clear last URL of the Open Web Location dialog
+ try {
+ Services.prefs.clearUserPref("general.open_location.last_url");
+ } catch(ex) {}
+
+ try {
+ // Clear URLbar history (see also pref-history.js)
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append("urlbarhistory.sqlite");
+ if (file.exists()) {
+ file.remove(false);
+ }
+ } catch (ex) {
+ seenException = ex;
+ }
+ if (seenException) {
+ throw seenException;
+ }
+ }
+ },
+
+ formdata: {
+ async clear(range) {
+ let seenException;
+ try {
+ // Clear undo history of all search and find bars.
+ let windows = Services.wm.getEnumerator("navigator:browser");
+ while (windows.hasMoreElements()) {
+ let win = windows.getNext();
+ let currentDocument = win.document;
+
+ let findBar = currentDocument.getElementById("FindToolbar");
+ if (findBar) {
+ findBar.clear();
+ }
+ // searchBar.textbox may not exist due to the search bar binding
+ // not having been constructed yet if the search bar is in the
+ // overflow or menu panel. It won't have a value or edit history in
+ // that case.
+ let searchBar = currentDocument.getElementById("searchbar");
+ if (searchBar && searchBar.textbox) {
+ searchBar.textbox.reset();
+ }
+
+ let sideSearchBar = win.BrowserSearch.searchSidebar;
+ if (sideSearchBar) {
+ sideSearchBar.reset();
+ }
+ }
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ try {
+ let change = { op: "remove" };
+ if (range) {
+ [ change.firstUsedStart, change.firstUsedEnd ] = range;
+ }
+ await new Promise(resolve => {
+ FormHistory.update(change, {
+ handleError(e) {
+ seenException = new Error("Error " + e.result + ": " +
+ e.message);
+ },
+ handleCompletion() {
+ resolve();
+ }
+ });
+ });
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ if (seenException) {
+ throw seenException;
+ }
+ }
+ },
+
+ downloads: {
+ async clear(range) {
+ try {
+ let filterByTime = null;
+ if (range) {
+ // Convert microseconds back to milliseconds for date comparisons.
+ let rangeBeginMs = range[0] / 1000;
+ let rangeEndMs = range[1] / 1000;
+ filterByTime = download => download.startTime >= rangeBeginMs &&
+ download.startTime <= rangeEndMs;
+ }
+
+ // Clear all completed/cancelled downloads
+ let list = await Downloads.getList(Downloads.ALL);
+ list.removeFinished(filterByTime);
+ } catch (ex) {}
+ }
+ },
+
+ passwords: {
+ async clear(range) {
+ try {
+ Services.logins.removeAllLogins();
+ } catch (ex) {}
+ }
+ },
+
+ sessions: {
+ async clear(range) {
+ try {
+ // clear all auth tokens
+ let sdr = Cc["@mozilla.org/security/sdr;1"]
+ .getService(Ci.nsISecretDecoderRing);
+ sdr.logoutAndTeardown();
+
+ // clear FTP and plain HTTP auth sessions
+ Services.obs.notifyObservers(null, "net:clear-active-logins");
+ } catch (ex) {}
+ }
+ },
+
+ siteSettings: {
+ async clear(range) {
+ let seenException;
+
+ let startDateMS = range ? range[0] / 1000 : null;
+
+ try {
+ // Clear site-specific permissions like
+ // "Allow this site to open popups".
+ // We ignore the "end" range and hope it is now() - none of the
+ // interfaces used here support a true range anyway.
+ if (startDateMS == null) {
+ Services.perms.removeAll();
+ } else {
+ Services.perms.removeAllSince(startDateMS);
+ }
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ try {
+ // Clear site-specific settings like page-zoom level
+ let cps = Cc["@mozilla.org/content-pref/service;1"]
+ .getService(Ci.nsIContentPrefService2);
+ if (startDateMS == null) {
+ cps.removeAllDomains(null);
+ } else {
+ cps.removeAllDomainsSince(startDateMS, null);
+ }
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ try {
+ // Clear site security settings - no support for ranges in this
+ // interface either, so we clearAll().
+ let sss = Cc["@mozilla.org/ssservice;1"]
+ .getService(Ci.nsISiteSecurityService);
+ sss.clearAll();
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ // Clear all push notification subscriptions
+ try {
+ await new Promise((resolve, reject) => {
+ let push = Cc["@mozilla.org/push/Service;1"]
+ .getService(Ci.nsIPushService);
+ push.clearForDomain("*", status => {
+ if (Components.isSuccessCode(status)) {
+ resolve();
+ } else {
+ reject(new Error("Error clearing push subscriptions: " +
+ status));
+ }
+ });
+ });
+ } catch (ex) {
+ seenException = ex;
+ }
+
+ if (seenException) {
+ throw seenException;
+ }
+ }
+ },
+
+ openWindows: {
+ _canCloseWindow(win) {
+ if (win.CanCloseWindow()) {
+ // We already showed PermitUnload for the window, so let's
+ // make sure we don't do it again when we actually close the
+ // window.
+ win.skipNextCanClose = true;
+ return true;
+ }
+ return false;
+ },
+ _resetAllWindowClosures(windowList) {
+ for (let win of windowList) {
+ win.skipNextCanClose = false;
+ }
+ },
+ async clear(range, privateStateForNewWindow = "non-private") {
+ // NB: this closes all *browser* windows, not other windows like the
+ // library, about window, browser console, etc.
+
+ // Keep track of the time in case we get stuck in la-la-land because of
+ // onbeforeunload dialogs.
+ let existingWindow = Services.appShell.hiddenDOMWindow;
+ let startDate = existingWindow.performance.now();
+
+ // First check if all these windows are OK with being closed:
+ let windowEnumerator = Services.wm.getEnumerator("navigator:browser");
+ let windowList = [];
+ while (windowEnumerator.hasMoreElements()) {
+ let someWin = windowEnumerator.getNext();
+ windowList.push(someWin);
+ // If someone says "no" to a beforeunload prompt, we abort here:
+ if (!this._canCloseWindow(someWin)) {
+ this._resetAllWindowClosures(windowList);
+ throw new Error("Sanitize could not close windows: " +
+ "cancelled by user");
+ }
+
+ // ...however, beforeunload prompts spin the event loop, and so the
+ // code here won't get hit until the prompt has been dismissed.
+ // If more than 1 minute has elapsed since we started prompting,
+ // stop, because the user might not even remember initiating the
+ // 'forget', and the timespans will be all wrong by now anyway:
+ if (existingWindow.performance.now() > (startDate + 60 * 1000)) {
+ this._resetAllWindowClosures(windowList);
+ throw new Error("Sanitize could not close windows: timeout");
+ }
+ }
+
+ // If/once we get here, we should actually be able to close all
+ // windows.
+
+ // First create a new window. We do this first so that on non-mac, we
+ // don't accidentally close the app by closing all the windows.
+ let handler = Cc["@mozilla.org/browser/clh;1"]
+ .getService(Ci.nsIBrowserHandler);
+ let defaultArgs = handler.defaultArgs;
+ let features = "chrome,all,dialog=no," + privateStateForNewWindow;
+ let newWindow = existingWindow.openDialog("chrome://browser/content/",
+ "_blank", features,
+ defaultArgs);
+
+ let onFullScreen = null;
+ if (AppConstants.platform == "macosx") {
+ onFullScreen = function(e) {
+ newWindow.removeEventListener("fullscreen", onFullScreen);
+ let docEl = newWindow.document.documentElement;
+ let sizemode = docEl.getAttribute("sizemode");
+ if (!newWindow.fullScreen && sizemode == "fullscreen") {
+ docEl.setAttribute("sizemode", "normal");
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ return undefined;
+ };
+ newWindow.addEventListener("fullscreen", onFullScreen);
+ }
+
+ let promiseReady = new Promise(resolve => {
+ // Window creation and destruction is asynchronous. We need to wait
+ // until all existing windows are fully closed, and the new window is
+ // fully open, before continuing. Otherwise the rest of the sanitizer
+ // could run too early (and miss new cookies being set when a page
+ // closes) and/or run too late (and not have a fully-formed window
+ // yet in existence). See bug 1088137.
+ let newWindowOpened = false;
+ let onWindowOpened = function(subject, topic, data) {
+ if (subject != newWindow)
+ return;
+
+ Services.obs.removeObserver(onWindowOpened,
+ "browser-delayed-startup-finished");
+ if (AppConstants.platform == "macosx") {
+ newWindow.removeEventListener("fullscreen", onFullScreen);
+ }
+ newWindowOpened = true;
+ // If we're the last thing to happen, invoke callback.
+ if (numWindowsClosing == 0) {
+ resolve();
+ }
+ };
+
+ let numWindowsClosing = windowList.length;
+ let onWindowClosed = function() {
+ numWindowsClosing--;
+ if (numWindowsClosing == 0) {
+ Services.obs.removeObserver(onWindowClosed,
+ "xul-window-destroyed");
+ // If we're the last thing to happen, invoke callback.
+ if (newWindowOpened) {
+ resolve();
+ }
+ }
+ };
+ Services.obs.addObserver(onWindowOpened,
+ "browser-delayed-startup-finished");
+ Services.obs.addObserver(onWindowClosed, "xul-window-destroyed");
+ });
+
+ // Start the process of closing windows
+ while (windowList.length) {
+ windowList.pop().close();
+ }
+ newWindow.focus();
+ await promiseReady;
+ }
+ },
+ },
+};
+
+async function sanitizeInternal(items, aItemsToClear, progress, options = {}) {
+ let { ignoreTimespan = true, range } = options;
+ let seenError = false;
+ // Shallow copy the array, as we are going to modify it in place later.
+ if (!Array.isArray(aItemsToClear))
+ throw new Error("Must pass an array of items to clear.");
+ let itemsToClear = [...aItemsToClear];
+
+ // Store the list of items to clear, in case we are killed before we
+ // get a chance to complete.
+ let uid = gPendingSanitizationSerial++;
+ // Shutdown sanitization is managed outside.
+ if (!progress.isShutdown)
+ addPendingSanitization(uid, itemsToClear, options);
+
+ // Store the list of items to clear, for debugging/forensics purposes
+ for (let k of itemsToClear) {
+ progress[k] = "ready";
+ }
+
+ // Ensure open windows get cleared first, if they're in our list, so that
+ // they don't stick around in the recently closed windows list, and so we
+ // can cancel the whole thing if the user selects to keep a window open
+ // from a beforeunload prompt.
+ let openWindowsIndex = itemsToClear.indexOf("openWindows");
+ if (openWindowsIndex != -1) {
+ itemsToClear.splice(openWindowsIndex, 1);
+ await items.openWindows.clear(null, options);
+ progress.openWindows = "cleared";
+ }
+
+ // If we ignore timespan, clear everything,
+ // otherwise, pick a range.
+ if (!ignoreTimespan && !range) {
+ range = Sanitizer.getClearRange();
+ }
+
+ // For performance reasons we start all the clear tasks at once, then wait
+ // for their promises later.
+ // Some of the clear() calls may raise exceptions (for example bug 265028),
+ // we catch and store them, but continue to sanitize as much as possible.
+ // Callers should check returned errors and give user feedback
+ // about items that could not be sanitized
+ let annotateError = (name, ex) => {
+ progress[name] = "failed";
+ seenError = true;
+ console.error("Error sanitizing " + name, ex);
+ };
+
+ // Array of objects in form { name, promise }.
+ // `name` is the item's name and `promise` may be a promise, if the
+ // sanitization is asynchronous, or the function return value, otherwise.
+ let handles = [];
+ for (let name of itemsToClear) {
+ let item = items[name];
+ try {
+ // Catch errors here, so later we can just loop through these.
+ handles.push({ name,
+ promise: item.clear(range, options)
+ .then(() => progress[name] = "cleared",
+ ex => annotateError(name, ex))
+ });
+ } catch (ex) {
+ annotateError(name, ex);
+ }
+ }
+ for (let handle of handles) {
+ progress[handle.name] = "blocking";
+ await handle.promise;
+ }
+
+ // Sanitization is complete.
+ if (!progress.isShutdown)
+ removePendingSanitization(uid);
+ progress = {};
+ if (seenError) {
+ throw new Error("Error sanitizing");
+ }
+}
+
+async function sanitizeOnShutdown(progress) {
+ if (!Sanitizer.shouldSanitizeOnShutdown) {
+ return;
+ }
+ // Need to sanitize upon shutdown
+ let itemsToClear =
+ getItemsToClearFromPrefBranch(Sanitizer.PREF_SHUTDOWN_BRANCH);
+ await Sanitizer.sanitize(itemsToClear, { progress });
+ // We didn't crash during shutdown sanitization, so annotate it to avoid
+ // sanitizing again on startup.
+ removePendingSanitization("shutdown");
+ Services.prefs.savePrefFile(null);
+}
+
+/**
+ * Gets an array of items to clear from the given pref branch.
+ * @param branch The pref branch to fetch.
+ * @return Array of items to clear
+ */
+function getItemsToClearFromPrefBranch(branch) {
+ branch = Services.prefs.getBranch(branch);
+ return Object.keys(Sanitizer.items).filter(itemName => {
+ try {
+ return branch.getBoolPref(itemName);
+ } catch (ex) {
+ return false;
+ }
+ });
+}
+
+/**
+ * These functions are used to track pending sanitization on the next
+ * startup in case of a crash before a sanitization could happen.
+ * @param id A unique id identifying the sanitization
+ * @param itemsToClear The items to clear
+ * @param options The Sanitize options
+ */
+function addPendingSanitization(id, itemsToClear, options) {
+ let pendingSanitizations = safeGetPendingSanitizations();
+ pendingSanitizations.push({id, itemsToClear, options});
+ Services.prefs.setStringPref(Sanitizer.PREF_PENDING_SANITIZATIONS,
+ JSON.stringify(pendingSanitizations));
+}
+function removePendingSanitization(id) {
+ let pendingSanitizations = safeGetPendingSanitizations();
+ let i = pendingSanitizations.findIndex(s => s.id == id);
+ let [s] = pendingSanitizations.splice(i, 1);
+ Services.prefs.setStringPref(Sanitizer.PREF_PENDING_SANITIZATIONS,
+ JSON.stringify(pendingSanitizations));
+ return s;
+}
+function getAndClearPendingSanitizations() {
+ let pendingSanitizations = safeGetPendingSanitizations();
+ if (pendingSanitizations.length)
+ Services.prefs.clearUserPref(Sanitizer.PREF_PENDING_SANITIZATIONS);
+ return pendingSanitizations;
+}
+function safeGetPendingSanitizations() {
+ try {
+ return JSON.parse(
+ Services.prefs.getStringPref(Sanitizer.PREF_PENDING_SANITIZATIONS,
+ "[]"));
+ } catch (ex) {
+ Cu.reportError("Invalid JSON value for pending sanitizations: " + ex);
+ return [];
+ }
+}
diff --git a/comm/suite/components/sanitize/content/sanitizeDialog.js b/comm/suite/components/sanitize/content/sanitizeDialog.js
new file mode 100644
index 0000000000..aadc73ad10
--- /dev/null
+++ b/comm/suite/components/sanitize/content/sanitizeDialog.js
@@ -0,0 +1,111 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { Sanitizer } = ChromeUtils.import("resource:///modules/Sanitizer.jsm");
+
+var gSanitizePromptDialog = {
+
+ get bundleSanitize() {
+ if (!this._bundleSanitize)
+ this._bundleSanitize = document.getElementById("bundleSanitize");
+ return this._bundleSanitize;
+ },
+
+ get selectedTimespan() {
+ var durList = document.getElementById("sanitizeDurationChoice");
+ return parseInt(durList.value);
+ },
+
+ get sanitizePreferences() {
+ if (!this._sanitizePreferences) {
+ this._sanitizePreferences =
+ document.getElementById("sanitizePreferences");
+ }
+ return this._sanitizePreferences;
+ },
+
+ init() {
+ document.documentElement.getButton("accept").label =
+ this.bundleSanitize.getString("sanitizeButtonOK");
+ },
+
+ sanitize() {
+ // Update pref values before handing off to the sanitizer.
+ this.updatePrefs();
+
+ // As the sanitize is async, we disable the buttons, update the label on
+ // the 'accept' button to indicate things are happening and return false.
+ // Once the async operation completes (either with or without errors)
+ // we close the window.
+ let docElt = document.documentElement;
+ let acceptButton = docElt.getButton("accept");
+ acceptButton.disabled = true;
+ acceptButton.setAttribute("label",
+ this.bundleSanitize
+ .getString("sanitizeButtonClearing"));
+ docElt.getButton("cancel").disabled = true;
+
+ try {
+ let range = Sanitizer.getClearRange(this.selectedTimespan);
+ let options = {
+ ignoreTimespan: !range,
+ range,
+ };
+ Sanitizer.sanitize(null, options)
+ .catch(Cu.reportError)
+ .then(() => window.close())
+ .catch(Cu.reportError);
+ return false;
+ } catch (er) {
+ Cu.reportError("Exception during sanitize: " + er);
+ return true; // We *do* want to close immediately on error.
+ }
+ },
+
+ /**
+ * Called when the value of a preference element is synced from the actual
+ * pref. Enables or disables the OK button appropriately.
+ */
+ onReadGeneric() {
+ var found = false;
+
+ // Find any other pref that's checked and enabled.
+ var i = 0;
+ while (!found && i < this.sanitizePreferences.childNodes.length) {
+ var preference = this.sanitizePreferences.childNodes[i];
+
+ found = !!preference.value &&
+ !preference.disabled;
+ i++;
+ }
+
+ try {
+ document.documentElement.getButton("accept").disabled = !found;
+ } catch (e) { }
+
+ return undefined;
+ },
+
+ /**
+ * Sanitizer.prototype.sanitize() requires the prefs to be up-to-date.
+ * Because the type of this prefwindow is "child" -- and that's needed
+ * because without it the dialog has no OK and Cancel buttons -- the
+ * prefs are not updated on dialogaccept on platforms that don't support
+ * instant-apply (i.e., Windows). We must therefore manually set the prefs
+ * from their corresponding preference elements.
+ */
+ updatePrefs() {
+ Services.prefs.setIntPref(Sanitizer.PREF_TIMESPAN, this.selectedTimespan);
+
+ // Now manually set the prefs from their corresponding preference
+ // elements.
+ var prefs = this.sanitizePreferences.rootBranch;
+ for (let i = 0; i < this.sanitizePreferences.childNodes.length; ++i) {
+ var p = this.sanitizePreferences.childNodes[i];
+ prefs.setBoolPref(p.name, p.value);
+ }
+ },
+};
diff --git a/comm/suite/components/sanitize/content/sanitizeDialog.xul b/comm/suite/components/sanitize/content/sanitizeDialog.xul
new file mode 100644
index 0000000000..86c641751c
--- /dev/null
+++ b/comm/suite/components/sanitize/content/sanitizeDialog.xul
@@ -0,0 +1,154 @@
+<?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/"?>
+<?xml-stylesheet href="chrome://communicator/skin/sanitizeDialog.css"?>
+
+<!DOCTYPE prefwindow [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % sanitizeDTD SYSTEM "chrome://communicator/locale/sanitize.dtd">
+ %sanitizeDTD;
+]>
+
+<dialog id="SanitizeDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&sanitizeDialog.title;"
+ dlgbuttons="accept,cancel"
+ style="width: &sanitizeDialog.width;;"
+ onload="gSanitizePromptDialog.init();"
+ ondialogaccept="return gSanitizePromptDialog.sanitize();">
+
+ <script src="chrome://communicator/content/sanitizeDialog.js"/>
+ <stringbundle id="bundleSanitize"
+ src="chrome://communicator/locale/sanitize.properties"/>
+
+ <prefpane id="SanitizeDialogPane">
+ <preferences id="sanitizePreferences">
+ <preference id="privacy.cpd.history"
+ name="privacy.cpd.history"
+ type="bool"/>
+ <preference id="privacy.cpd.urlbar"
+ name="privacy.cpd.urlbar"
+ type="bool"/>
+ <preference id="privacy.cpd.downloads"
+ name="privacy.cpd.downloads"
+ type="bool"/>
+ <preference id="privacy.cpd.formdata"
+ name="privacy.cpd.formdata"
+ type="bool"/>
+ <preference id="privacy.cpd.cache"
+ name="privacy.cpd.cache"
+ type="bool"/>
+ <preference id="privacy.cpd.cookies"
+ name="privacy.cpd.cookies"
+ type="bool"/>
+ <preference id="privacy.cpd.offlineApps"
+ name="privacy.cpd.offlineApps"
+ type="bool"/>
+ <preference id="privacy.cpd.passwords"
+ name="privacy.cpd.passwords"
+ type="bool"/>
+ <preference id="privacy.cpd.sessions"
+ name="privacy.cpd.sessions"
+ type="bool"/>
+ <preference id="privacy.cpd.siteSettings"
+ name="privacy.cpd.siteSettings"
+ type="bool"/>
+ </preferences>
+
+ <preferences id="nonItemPreferences">
+ <preference id="privacy.sanitize.timeSpan"
+ name="privacy.sanitize.timeSpan"
+ type="int"/>
+ </preferences>
+
+ <vbox id="sanitizeWarningBox">
+ <spacer flex="1"/>
+ <hbox align="center">
+ <image id="sanitizeWarningIcon"/>
+ <vbox id="sanitizeWarningDescBox" flex="1">
+ <description id="sanitizeSelectedWarning">
+ &sanitizeSelectedWarning;
+ </description>
+ <description id="sanitizeUndoWarning">
+ &sanitizeUndoWarning;
+ </description>
+ </vbox>
+ </hbox>
+ <spacer flex="1"/>
+ </vbox>
+
+ <separator class="thin"/>
+
+ <hbox id="SanitizeDurationBox" align="center">
+ <label id="sanitizeDurationLabel"
+ value="&clearTimeDuration.label;"
+ accesskey="&clearTimeDuration.accesskey;"
+ control="sanitizeDurationChoice"/>
+ <menulist id="sanitizeDurationChoice"
+ preference="privacy.sanitize.timeSpan"
+ flex="1">
+ <menupopup id="sanitizeDurationPopup">
+ <menuitem label="&clearTimeDuration.lastHour;" value="1"/>
+ <menuitem label="&clearTimeDuration.last2Hours;" value="2"/>
+ <menuitem label="&clearTimeDuration.last4Hours;" value="3"/>
+ <menuitem label="&clearTimeDuration.today;" value="4"/>
+ <menuseparator/>
+ <menuitem label="&clearTimeDuration.everything;" value="0"/>
+ </menupopup>
+ </menulist>
+ <label id="sanitizeDurationSuffixLabel"
+ value="&clearTimeDuration.suffix;"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <groupbox id="itemList" flex="1">
+ <caption label="&sanitizeItems.label;"/>
+ <checkbox label="&itemHistory.label;"
+ accesskey="&itemHistory.accesskey;"
+ preference="privacy.cpd.history"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemUrlBar.label;"
+ accesskey="&itemUrlBar.accesskey;"
+ preference="privacy.cpd.urlbar"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemDownloads.label;"
+ accesskey="&itemDownloads.accesskey;"
+ preference="privacy.cpd.downloads"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemFormSearchHistory.label;"
+ accesskey="&itemFormSearchHistory.accesskey;"
+ preference="privacy.cpd.formdata"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemCache.label;"
+ accesskey="&itemCache.accesskey;"
+ preference="privacy.cpd.cache"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemCookies.label;"
+ accesskey="&itemCookies.accesskey;"
+ preference="privacy.cpd.cookies"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemOfflineApps.label;"
+ accesskey="&itemOfflineApps.accesskey;"
+ preference="privacy.cpd.offlineApps"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemPasswords.label;"
+ accesskey="&itemPasswords.accesskey;"
+ preference="privacy.cpd.passwords"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemSessions.label;"
+ accesskey="&itemSessions.accesskey;"
+ preference="privacy.cpd.sessions"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ <checkbox label="&itemSitePreferences.label;"
+ accesskey="&itemSitePreferences.accesskey;"
+ preference="privacy.cpd.siteSettings"
+ onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+ </groupbox>
+ </prefpane>
+</dialog>
diff --git a/comm/suite/components/sanitize/jar.mn b/comm/suite/components/sanitize/jar.mn
new file mode 100644
index 0000000000..c4255defd7
--- /dev/null
+++ b/comm/suite/components/sanitize/jar.mn
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.comm.jar:
+
+comm.jar:
+% content communicator %content/communicator/ contentaccessible=yes
+ content/communicator/sanitizeDialog.js (content/sanitizeDialog.js)
+ content/communicator/sanitizeDialog.xul (content/sanitizeDialog.xul)
diff --git a/comm/suite/components/sanitize/moz.build b/comm/suite/components/sanitize/moz.build
new file mode 100644
index 0000000000..be07f2ece6
--- /dev/null
+++ b/comm/suite/components/sanitize/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+EXTRA_JS_MODULES += [
+ "Sanitizer.jsm",
+]
+
+with Files("**"):
+ BUG_COMPONENT = ("SeaMonkey", "Bookmarks & History")
diff --git a/comm/suite/components/search/content/engineManager.js b/comm/suite/components/search/content/engineManager.js
new file mode 100644
index 0000000000..c505bff7fc
--- /dev/null
+++ b/comm/suite/components/search/content/engineManager.js
@@ -0,0 +1,508 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+ChromeUtils.defineModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+
+const ENGINE_FLAVOR = "text/x-moz-search-engine";
+
+const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled";
+
+var gEngineView = null;
+
+var gEngineManagerDialog = {
+ init: function engineManager_init() {
+ gEngineView = new EngineView(new EngineStore());
+
+ var suggestEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF);
+ document.getElementById("enableSuggest").checked = suggestEnabled;
+
+ var tree = document.getElementById("engineList");
+ tree.view = gEngineView;
+
+ Services.obs.addObserver(this, "browser-search-engine-modified");
+ },
+
+ destroy: function engineManager_destroy() {
+ // Remove the observer
+ Services.obs.removeObserver(this, "browser-search-engine-modified");
+ },
+
+ observe: function engineManager_observe(aEngine, aTopic, aVerb) {
+ if (aTopic == "browser-search-engine-modified") {
+ aEngine.QueryInterface(Ci.nsISearchEngine);
+ switch (aVerb) {
+ case "engine-added":
+ gEngineView._engineStore.addEngine(aEngine);
+ gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
+ break;
+ case "engine-changed":
+ gEngineView._engineStore.reloadIcons();
+ gEngineView.invalidate();
+ break;
+ case "engine-removed":
+ case "engine-current":
+ case "engine-default":
+ // Not relevant
+ break;
+ }
+ }
+ },
+
+ onOK: function engineManager_onOK() {
+ // Set the preference
+ var newSuggestEnabled = document.getElementById("enableSuggest").checked;
+ Services.prefs.setBoolPref(BROWSER_SUGGEST_PREF, newSuggestEnabled);
+
+ // Commit the changes
+ gEngineView._engineStore.commit();
+ },
+
+ onRestoreDefaults: function engineManager_onRestoreDefaults() {
+ var num = gEngineView._engineStore.restoreDefaultEngines();
+ gEngineView.rowCountChanged(0, num);
+ gEngineView.invalidate();
+ },
+
+ showRestoreDefaults: function engineManager_showRestoreDefaults(val) {
+ document.documentElement.getButton("extra2").disabled = !val;
+ },
+
+ loadAddEngines: function engineManager_loadAddEngines() {
+ this.onOK();
+ window.arguments[0].value = true; // see OpenSearchEngineManager()
+ window.close();
+ },
+
+ remove: function engineManager_remove() {
+ gEngineView._engineStore.removeEngine(gEngineView.selectedEngine);
+ var index = gEngineView.selectedIndex;
+ gEngineView.rowCountChanged(index, -1);
+ gEngineView.invalidate();
+ gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
+ gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
+ document.getElementById("engineList").focus();
+ },
+
+ /**
+ * Moves the selected engine either up or down in the engine list
+ * @param aDir
+ * -1 to move the selected engine down, +1 to move it up.
+ */
+ bump: function engineManager_move(aDir) {
+ var selectedEngine = gEngineView.selectedEngine;
+ var newIndex = gEngineView.selectedIndex - aDir;
+
+ gEngineView._engineStore.moveEngine(selectedEngine, newIndex);
+
+ gEngineView.invalidate();
+ gEngineView.selection.select(newIndex);
+ gEngineView.ensureRowIsVisible(newIndex);
+ this.showRestoreDefaults(true);
+ document.getElementById("engineList").focus();
+ },
+
+ selectEditKeyword: function engineManager_selectEditKeyword() {
+ let index = gEngineView.selectedIndex;
+ // No engine selected.
+ if (index == -1)
+ return;
+
+ let tree = document.getElementById("engineList");
+ let column = tree.columns.getColumnFor(document.getElementById("engineKeyword"));
+ tree.startEditing(index, column);
+ },
+
+ async editKeyword(aEngine, aNewKeyword) {
+ let keyword = aNewKeyword.trim();
+ if (keyword) {
+ let eduplicate = false;
+ let dupName = "";
+
+ // Check for duplicates in Places keywords.
+ let bduplicate = !!(await PlacesUtils.keywords.fetch(keyword));
+
+ // Check for duplicates in changes we haven't committed yet
+ let engines = gEngineView._engineStore.engines;
+ for (let engine of engines) {
+ if (engine.alias == keyword &&
+ engine.name != aEngine.name) {
+ eduplicate = true;
+ dupName = engine.name;
+ break;
+ }
+ }
+
+ // Notify the user if they have chosen an existing engine/bookmark keyword
+ if (eduplicate || bduplicate) {
+ let strings = document.getElementById("engineManagerBundle");
+ let dtitle = strings.getString("duplicateTitle");
+ let bmsg = strings.getString("duplicateBookmarkMsg");
+ let emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]);
+
+ Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg);
+ return false;
+ }
+ }
+
+ gEngineView._engineStore.changeEngine(aEngine, "alias", keyword);
+ gEngineView.invalidate();
+ return true;
+ },
+
+ onSelect: function engineManager_onSelect() {
+ // Buttons only work if an engine is selected and it's not the last engine,
+ // the latter is true when the selected is first and last at the same time.
+ var lastSelected = (gEngineView.selectedIndex == gEngineView.lastIndex);
+ var firstSelected = (gEngineView.selectedIndex == 0);
+ var noSelection = (gEngineView.selectedIndex == -1);
+
+ document.getElementById("cmd_remove")
+ .setAttribute("disabled", noSelection ||
+ (firstSelected && lastSelected));
+
+ document.getElementById("cmd_moveup")
+ .setAttribute("disabled", noSelection || firstSelected);
+
+ document.getElementById("cmd_movedown")
+ .setAttribute("disabled", noSelection || lastSelected);
+
+ document.getElementById("cmd_editkeyword")
+ .setAttribute("disabled", noSelection);
+ },
+
+ onKeydown: function(aEvent) {
+ var tree = document.getElementById("engineList");
+ if (tree.editingColumn)
+ return;
+
+ if (aEvent.keyCode == (AppConstants.platform == "macosx" ?
+ KeyEvent.DOM_VK_RETURN : KeyEvent.DOM_VK_F2) &&
+ tree.startEditing(gEngineView.selectedIndex,
+ tree.columns.engineKeyword)) {
+ aEvent.preventDefault();
+ }
+ }
+};
+
+function onDragEngineStart(event) {
+ var selectedIndex = gEngineView.selectedIndex;
+ if (selectedIndex >= 0) {
+ event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
+ event.dataTransfer.effectAllowed = "move";
+ }
+}
+
+// "Operation" objects
+function EngineMoveOp(aEngineClone, aNewIndex) {
+ if (!aEngineClone)
+ throw new Error("bad args to new EngineMoveOp!");
+ this._engine = aEngineClone.originalEngine;
+ this._newIndex = aNewIndex;
+}
+EngineMoveOp.prototype = {
+ _engine: null,
+ _newIndex: null,
+ commit: function EMO_commit() {
+ Services.search.moveEngine(this._engine, this._newIndex);
+ }
+}
+
+function EngineRemoveOp(aEngineClone) {
+ if (!aEngineClone)
+ throw new Error("bad args to new EngineRemoveOp!");
+ this._engine = aEngineClone.originalEngine;
+}
+EngineRemoveOp.prototype = {
+ _engine: null,
+ commit: function ERO_commit() {
+ Services.search.removeEngine(this._engine);
+ }
+}
+
+function EngineUnhideOp(aEngineClone, aNewIndex) {
+ if (!aEngineClone)
+ throw new Error("bad args to new EngineUnhideOp!");
+ this._engine = aEngineClone.originalEngine;
+ this._newIndex = aNewIndex;
+}
+EngineUnhideOp.prototype = {
+ _engine: null,
+ _newIndex: null,
+ commit: function EUO_commit() {
+ this._engine.hidden = false;
+ Services.search.moveEngine(this._engine, this._newIndex);
+ }
+}
+
+function EngineChangeOp(aEngineClone, aProp, aValue) {
+ if (!aEngineClone)
+ throw new Error("bad args to new EngineChangeOp!");
+
+ this._engine = aEngineClone.originalEngine;
+ this._prop = aProp;
+ this._newValue = aValue;
+}
+EngineChangeOp.prototype = {
+ _engine: null,
+ _prop: null,
+ _newValue: null,
+ commit: function ECO_commit() {
+ this._engine[this._prop] = this._newValue;
+ }
+}
+
+function EngineStore() {
+ this._engines = Services.search.getVisibleEngines().map(this._cloneEngine);
+ this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine);
+
+ this._ops = [];
+
+ // check if we need to disable the restore defaults button
+ var someHidden = this._defaultEngines.some(e => e.hidden);
+ gEngineManagerDialog.showRestoreDefaults(someHidden);
+}
+EngineStore.prototype = {
+ _engines: null,
+ _defaultEngines: null,
+ _ops: null,
+
+ get engines() {
+ return this._engines;
+ },
+ set engines(val) {
+ this._engines = val;
+ return val;
+ },
+
+ _getIndexForEngine: function ES_getIndexForEngine(aEngine) {
+ return this._engines.indexOf(aEngine);
+ },
+
+ _getEngineByName: function ES_getEngineByName(aName) {
+ for (var engine of this._engines)
+ if (engine.name == aName)
+ return engine;
+
+ return null;
+ },
+
+ _cloneEngine: function ES_cloneEngine(aEngine) {
+ var clonedObj={};
+ for (var i in aEngine)
+ clonedObj[i] = aEngine[i];
+ clonedObj.originalEngine = aEngine;
+ return clonedObj;
+ },
+
+ // Callback for Array's some(). A thisObj must be passed to some()
+ _isSameEngine: function ES_isSameEngine(aEngineClone) {
+ return aEngineClone.originalEngine == this.originalEngine;
+ },
+
+ commit: function ES_commit() {
+ var currentEngine = this._cloneEngine(Services.search.currentEngine);
+ for (var i = 0; i < this._ops.length; i++)
+ this._ops[i].commit();
+
+ // Restore currentEngine if it is a default engine that is still visible.
+ // Needed if the user deletes currentEngine and then restores it.
+ if (this._defaultEngines.some(this._isSameEngine, currentEngine) &&
+ !currentEngine.originalEngine.hidden)
+ Services.search.currentEngine = currentEngine.originalEngine;
+ },
+
+ addEngine: function ES_addEngine(aEngine) {
+ this._engines.push(this._cloneEngine(aEngine));
+ },
+
+ moveEngine: function ES_moveEngine(aEngine, aNewIndex) {
+ if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
+ throw new Error("ES_moveEngine: invalid aNewIndex!");
+ var index = this._getIndexForEngine(aEngine);
+ if (index == -1)
+ throw new Error("ES_moveEngine: invalid engine?");
+
+ if (index == aNewIndex)
+ return; // nothing to do
+
+ // Move the engine in our internal store
+ var removedEngine = this._engines.splice(index, 1)[0];
+ this._engines.splice(aNewIndex, 0, removedEngine);
+
+ this._ops.push(new EngineMoveOp(aEngine, aNewIndex));
+ },
+
+ removeEngine: function ES_removeEngine(aEngine) {
+ var index = this._getIndexForEngine(aEngine);
+ if (index == -1)
+ throw new Error("invalid engine?");
+
+ this._engines.splice(index, 1);
+ this._ops.push(new EngineRemoveOp(aEngine));
+ if (this._defaultEngines.some(this._isSameEngine, aEngine))
+ gEngineManagerDialog.showRestoreDefaults(true);
+ },
+
+ restoreDefaultEngines: function ES_restoreDefaultEngines() {
+ var added = 0;
+
+ for (var i = 0; i < this._defaultEngines.length; ++i) {
+ var e = this._defaultEngines[i];
+
+ // If the engine is already in the list, just move it.
+ if (this._engines.some(this._isSameEngine, e)) {
+ this.moveEngine(this._getEngineByName(e.name), i);
+ } else {
+ // Otherwise, add it back to our internal store
+ this._engines.splice(i, 0, e);
+ this._ops.push(new EngineUnhideOp(e, i));
+ added++;
+ }
+ }
+ gEngineManagerDialog.showRestoreDefaults(false);
+ return added;
+ },
+
+ changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) {
+ var index = this._getIndexForEngine(aEngine);
+ if (index == -1)
+ throw new Error("invalid engine?");
+
+ this._engines[index][aProp] = aNewValue;
+ this._ops.push(new EngineChangeOp(aEngine, aProp, aNewValue));
+ },
+
+ reloadIcons: function ES_reloadIcons() {
+ this._engines.forEach(function (e) {
+ e.uri = e.originalEngine.uri;
+ });
+ }
+}
+
+function EngineView(aEngineStore) {
+ this._engineStore = aEngineStore;
+}
+EngineView.prototype = {
+ _engineStore: null,
+ tree: null,
+
+ get lastIndex() {
+ return this.rowCount - 1;
+ },
+ get selectedIndex() {
+ var seln = this.selection;
+ if (seln.getRangeCount() > 0) {
+ var min = {};
+ seln.getRangeAt(0, min, {});
+ return min.value;
+ }
+ return -1;
+ },
+ get selectedEngine() {
+ return this._engineStore.engines[this.selectedIndex];
+ },
+
+ // Helpers
+ rowCountChanged: function (index, count) {
+ this.tree.rowCountChanged(index, count);
+ },
+
+ invalidate: function () {
+ this.tree.invalidate();
+ },
+
+ ensureRowIsVisible: function (index) {
+ this.tree.ensureRowIsVisible(index);
+ },
+
+ getSourceIndexFromDrag: function (dataTransfer) {
+ return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
+ },
+
+ // nsITreeView
+ get rowCount() {
+ return this._engineStore.engines.length;
+ },
+
+ getImageSrc: function(index, column) {
+ if (column.id == "engineName" && this._engineStore.engines[index].iconURI)
+ return this._engineStore.engines[index].iconURI.spec;
+ return "";
+ },
+
+ getCellText: function(index, column) {
+ if (column.id == "engineName")
+ return this._engineStore.engines[index].name;
+ else if (column.id == "engineKeyword")
+ return this._engineStore.engines[index].alias;
+ return "";
+ },
+
+ setCellText: function(index, column, value) {
+ if (column.id == "engineKeyword") {
+ gEngineManagerDialog.editKeyword(this._engineStore.engines[index], value)
+ .then(valid => {
+ if (!valid)
+ document.getElementById("engineList").startEditing(index, column);
+ });
+ }
+ },
+
+ setTree: function(tree) {
+ this.tree = tree;
+ },
+
+ canDrop: function(targetIndex, orientation, dataTransfer) {
+ var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
+ return (sourceIndex != -1 &&
+ sourceIndex != targetIndex &&
+ sourceIndex != targetIndex + orientation);
+ },
+
+ drop: function(dropIndex, orientation, dataTransfer) {
+ var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
+ var sourceEngine = this._engineStore.engines[sourceIndex];
+
+ if (dropIndex > sourceIndex) {
+ if (orientation == Ci.nsITreeView.DROP_BEFORE)
+ dropIndex--;
+ } else {
+ if (orientation == Ci.nsITreeView.DROP_AFTER)
+ dropIndex++;
+ }
+
+ this._engineStore.moveEngine(sourceEngine, dropIndex);
+ gEngineManagerDialog.showRestoreDefaults(true);
+
+ // Redraw, and adjust selection
+ this.invalidate();
+ this.selection.select(dropIndex);
+ },
+
+ selection: null,
+ getRowProperties: function(index) { return ""; },
+ getCellProperties: function(index, column) { return ""; },
+ getColumnProperties: function(column) { return ""; },
+ isContainer: function(index) { return false; },
+ isContainerOpen: function(index) { return false; },
+ isContainerEmpty: function(index) { return false; },
+ isSeparator: function(index) { return false; },
+ isSorted: function(index) { return false; },
+ getParentIndex: function(index) { return -1; },
+ hasNextSibling: function(parentIndex, index) { return false; },
+ getLevel: function(index) { return 0; },
+ getProgressMode: function(index, column) { },
+ getCellValue: function(index, column) { },
+ toggleOpenState: function(index) { },
+ cycleHeader: function(column) { },
+ selectionChanged: function() { },
+ cycleCell: function(row, column) { },
+ isEditable: function(index, column) { return column.id == "engineKeyword"; },
+ isSelectable: function(index, column) { return false; },
+ setCellValue: function(index, column, value) { },
+};
diff --git a/comm/suite/components/search/content/engineManager.xul b/comm/suite/components/search/content/engineManager.xul
new file mode 100644
index 0000000000..b160afc4eb
--- /dev/null
+++ b/comm/suite/components/search/content/engineManager.xul
@@ -0,0 +1,98 @@
+<?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/"?>
+<?xml-stylesheet href="chrome://communicator/skin/search/engineManager.css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/search/engineManager.dtd">
+
+<dialog id="engineManager"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel,extra2"
+ buttonlabelextra2="&restoreDefaults.label;"
+ buttonaccesskeyextra2="&restoreDefaults.accesskey;"
+ onload="gEngineManagerDialog.init();"
+ onunload="gEngineManagerDialog.destroy();"
+ ondialogaccept="gEngineManagerDialog.onOK();"
+ ondialogextra2="gEngineManagerDialog.onRestoreDefaults();"
+ title="&engineManager.title;"
+ style="&engineManager.style;"
+ persist="screenX screenY width height"
+ windowtype="Browser:SearchManager">
+
+ <script src="chrome://communicator/content/search/engineManager.js"/>
+
+ <commandset id="engineManagerCommandSet">
+ <command id="cmd_remove"
+ oncommand="gEngineManagerDialog.remove();"
+ disabled="true"/>
+ <command id="cmd_moveup"
+ oncommand="gEngineManagerDialog.bump(1);"
+ disabled="true"/>
+ <command id="cmd_movedown"
+ oncommand="gEngineManagerDialog.bump(-1);"
+ disabled="true"/>
+ <command id="cmd_editkeyword"
+ oncommand="gEngineManagerDialog.selectEditKeyword();"
+ disabled="true"/>
+ </commandset>
+
+ <keyset id="engineManagerKeyset">
+ <key id="delete" keycode="VK_DELETE" command="cmd_remove"/>
+ </keyset>
+
+ <stringbundleset id="engineManagerBundleset">
+ <stringbundle id="engineManagerBundle" src="chrome://communicator/locale/search/engineManager.properties"/>
+ </stringbundleset>
+
+ <description>&engineManager.intro;</description>
+ <separator class="thin"/>
+ <hbox flex="1">
+ <tree id="engineList"
+ flex="1"
+ rows="10"
+ hidecolumnpicker="true"
+ editable="true"
+ seltype="single"
+ onselect="gEngineManagerDialog.onSelect();"
+ onkeydown="gEngineManagerDialog.onKeydown(event);">
+ <treechildren id="engineChildren" flex="1"
+ ondragstart="onDragEngineStart(event);"/>
+ <treecols>
+ <treecol id="engineName" flex="4" label="&columnLabel.name;"/>
+ <treecol id="engineKeyword" flex="1" label="&columnLabel.keyword;"/>
+ </treecols>
+ </tree>
+ <vbox>
+ <spacer flex="1"/>
+ <button id="edit"
+ label="&edit.label;"
+ accesskey="&edit.accesskey;"
+ command="cmd_editkeyword"/>
+ <button id="up"
+ label="&up.label;"
+ accesskey="&up.accesskey;"
+ command="cmd_moveup"/>
+ <button id="down"
+ label="&dn.label;"
+ accesskey="&dn.accesskey;"
+ command="cmd_movedown"/>
+ <spacer flex="1"/>
+ <button id="remove"
+ label="&remove.label;"
+ accesskey="&remove.accesskey;"
+ command="cmd_remove"/>
+ </vbox>
+ </hbox>
+ <hbox>
+ <checkbox id="enableSuggest"
+ label="&enableSuggest.label;"
+ accesskey="&enableSuggest.accesskey;"/>
+ </hbox>
+ <hbox>
+ <label id="addEngines" class="text-link" value="&addEngine.label;"
+ onclick="if (event.button == 0) { gEngineManagerDialog.loadAddEngines(); }"/>
+ </hbox>
+</dialog>
diff --git a/comm/suite/components/search/content/search-panel.js b/comm/suite/components/search/content/search-panel.js
new file mode 100644
index 0000000000..c6ab43c082
--- /dev/null
+++ b/comm/suite/components/search/content/search-panel.js
@@ -0,0 +1,86 @@
+/* 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 {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {FormHistory} = ChromeUtils.import("resource://gre/modules/FormHistory.jsm");
+
+const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified";
+var isPB, menulist, textbox;
+
+function Startup() {
+ menulist = document.getElementById("sidebar-search-engines");
+ textbox = document.getElementById("sidebar-search-text");
+ isPB = top.gPrivate;
+ if (isPB)
+ textbox.searchParam += "|private";
+
+ LoadEngineList();
+ Services.obs.addObserver(engineObserver, SEARCH_ENGINE_TOPIC, true);
+}
+
+function LoadEngineList() {
+ var currentEngineName = Services.search.currentEngine.name;
+ // Make sure the popup is empty.
+ menulist.removeAllItems();
+
+ var engines = Services.search.getVisibleEngines();
+ for (let engine of engines) {
+ let name = engine.name;
+ let menuitem = menulist.appendItem(name, name);
+ menuitem.setAttribute("class", "menuitem-iconic");
+ if (engine.iconURI)
+ menuitem.setAttribute("image", engine.iconURI.spec);
+ menuitem.engine = engine;
+ if (engine.name == currentEngineName) {
+ // Set selection to the current default engine.
+ menulist.selectedItem = menuitem;
+ }
+ }
+ // If the current engine isn't in the list any more, select the first item.
+ if (menulist.selectedIndex < 0)
+ menulist.selectedIndex = 0;
+}
+
+function SelectEngine() {
+ if (menulist.selectedItem)
+ Services.search.currentEngine = menulist.selectedItem.engine;
+ Services.obs.notifyObservers(null, SEARCH_ENGINE_TOPIC, "engine-current");
+}
+
+function doSearch() {
+ var textValue = textbox.value;
+
+ // Save the current value in the form history (shared with the search bar)
+ // except when in Private Browsing mode.
+
+ if (textValue && !isPB) {
+ FormHistory.update({
+ op: "bump",
+ fieldname: "searchbar-history",
+ value: textValue
+ }, {
+ handleError: function(aError) {
+ Cu.reportError("Saving search to form history failed: " + aError.message);
+ }
+ });
+ }
+
+ var where = Services.prefs.getBoolPref("browser.search.openintab") ? "tab" : "current";
+ var submission = Services.search.currentEngine.getSubmission(textValue);
+ openUILinkIn(submission.uri.spec, where, null, submission.postData);
+}
+
+var engineObserver = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsISupportsWeakReference]),
+
+ observe: function(aEngine, aTopic, aVerb) {
+ if (aTopic == SEARCH_ENGINE_TOPIC) {
+ // Right now, always just rebuild the list after any modification.
+ LoadEngineList();
+ }
+ }
+}
diff --git a/comm/suite/components/search/content/search-panel.xul b/comm/suite/components/search/content/search-panel.xul
new file mode 100644
index 0000000000..8f7c86285e
--- /dev/null
+++ b/comm/suite/components/search/content/search-panel.xul
@@ -0,0 +1,48 @@
+<?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://communicator/content/search/searchbarBindings.css"?>
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/search/search.css" type="text/css"?>
+
+<!DOCTYPE page SYSTEM "chrome://communicator/locale/search/search-panel.dtd" >
+<page id="searchPanel"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="Startup();"
+ elementtofocus="sidebar-search-text">
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://communicator/content/search/search-panel.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+
+ <popupset id="sidebarPopupset">
+ <panel id="PopupAutoComplete"
+ type="autocomplete"
+ noautofocus="true"/>
+ </popupset>
+
+ <menulist id="sidebar-search-engines"
+ oncommand="SelectEngine(this);"/>
+
+ <hbox align="center">
+ <textbox id="sidebar-search-text" flex="1"
+ class="search-textbox padded"
+ ontextentered="doSearch();"
+ placeholder="&search.placeholder;"
+ type="autocomplete"
+ inputtype="search"
+ autocompletepopup="PopupAutoComplete"
+ autocompletesearch="search-autocomplete"
+ autocompletesearchparam="searchbar-history"
+ maxrows="10"
+ completeselectedindex="true"
+ tabscrolling="true"/>
+ <button id="searchButton" label="&search.button.label;"
+ oncommand="doSearch();"/>
+ </hbox>
+ <button id="managerButton"
+ label="&search.engineManager.label;"
+ oncommand="window.top.OpenSearchEngineManager();"/>
+</page>
diff --git a/comm/suite/components/search/content/search.xml b/comm/suite/components/search/content/search.xml
new file mode 100644
index 0000000000..8e89824314
--- /dev/null
+++ b/comm/suite/components/search/content/search.xml
@@ -0,0 +1,739 @@
+<?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 bindings [
+ <!ENTITY % searchBarDTD SYSTEM "chrome://communicator/locale/search/searchbar.dtd">
+ %searchBarDTD;
+ <!ENTITY % textcontextDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+ %textcontextDTD;
+ <!ENTITY % navigatorDTD SYSTEM "chrome://navigator/locale/navigator.dtd">
+ %navigatorDTD;
+]>
+
+<bindings id="SearchBindings"
+ 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="searchbar">
+ <resources>
+ <stylesheet src="chrome://communicator/content/search/searchbarBindings.css"/>
+ <stylesheet src="chrome://communicator/skin/search/searchbar.css"/>
+ </resources>
+ <content>
+ <xul:stringbundle src="chrome://communicator/locale/search/search.properties"
+ anonid="searchbar-stringbundle"/>
+ <!--
+ There is a dependency between "maxrows" attribute and
+ "SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing
+ one of them requires changing the other one.
+ -->
+ <xul:textbox class="searchbar-textbox"
+ anonid="searchbar-textbox"
+ type="autocomplete"
+ inputtype="search"
+ flex="1"
+ autocompletepopup="_child"
+ autocompletesearch="search-autocomplete"
+ autocompletesearchparam="searchbar-history"
+ maxrows="10"
+ completeselectedindex="true"
+ showcommentcolumn="true"
+ tabscrolling="true"
+ xbl:inherits="disabled">
+ <xul:box>
+ <xul:toolbarbutton class="plain searchbar-engine-button"
+ type="menu"
+ anonid="searchbar-engine-button"
+ xbl:inherits="image">
+ <xul:menupopup class="searchbar-popup"
+ anonid="searchbar-popup">
+ <xul:menuseparator/>
+ <xul:menuitem class="open-engine-manager"
+ anonid="open-engine-manager"
+ label="&cmd_engineManager.label;"
+ oncommand="OpenSearchEngineManager();"/>
+ </xul:menupopup>
+ </xul:toolbarbutton>
+ </xul:box>
+ <xul:hbox class="search-go-container">
+ <xul:image class="search-go-button"
+ anonid="search-go-button"
+ onclick="handleSearchCommand(event);"
+ tooltiptext="&searchEndCap.label;"/>
+ </xul:hbox>
+ <xul:panel anonid="searchPopupAutoComplete"
+ type="autocomplete"
+ noautofocus="true"/>
+ </xul:textbox>
+ </content>
+
+ <implementation implements="nsIObserver, nsIBrowserSearchInitObserver, nsISearchInstallCallback">
+ <constructor><![CDATA[
+ if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
+ return;
+
+ if (this.usePrivateBrowsing)
+ this._textbox.searchParam += "|private";
+
+ Services.obs.addObserver(this, "browser-search-engine-modified");
+ Services.obs.addObserver(this, "browser-search-service");
+ this._initialized = true;
+
+ Services.search.init(this);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ if (this._initialized) {
+ this._initialized = false;
+ Services.obs.removeObserver(this, "browser-search-engine-modified");
+ Services.obs.removeObserver(this, "browser-search-service");
+ }
+
+ // Make sure to break the cycle from _textbox to us. Otherwise we leak
+ // the world. But make sure it's actually pointing to us.
+ // Also make sure the textbox has ever been constructed, otherwise the
+ // _textbox getter will cause the textbox constructor to run, add an
+ // observer, and leak the world too.
+ if (this._textboxInitialized && this._textbox.mController.input == this)
+ this._textbox.mController.input = null;
+ ]]></destructor>
+
+ <field name="_stringBundle">document.getAnonymousElementByAttribute(this,
+ "anonid", "searchbar-stringbundle");</field>
+ <field name="_textboxInitialized">false</field>
+ <field name="_textbox">document.getAnonymousElementByAttribute(this,
+ "anonid", "searchbar-textbox");</field>
+ <field name="_popup">document.getAnonymousElementByAttribute(this,
+ "anonid", "searchbar-popup");</field>
+ <field name="searchButton">document.getAnonymousElementByAttribute(this,
+ "anonid", "searchbar-engine-button");</field>
+ <field name="usePrivateBrowsing" readonly="true">
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext)
+ .usePrivateBrowsing
+ </field>
+ <field name="_initialized">false</field>
+ <field name="_engines">null</field>
+ <field name="_needToBuildPopup">true</field>
+ <field name="FormHistory" readonly="true"><![CDATA[
+ (ChromeUtils.import("resource://gre/modules/FormHistory.jsm", {}))
+ .FormHistory;
+ ]]>
+ </field>
+ <property name="engines" readonly="true">
+ <getter><![CDATA[
+ if (!this._engines)
+ this._engines = Services.search.getVisibleEngines();
+ return this._engines;
+ ]]></getter>
+ </property>
+
+ <property name="currentEngine">
+ <setter><![CDATA[
+ Services.search.currentEngine = val;
+ Services.obs.notifyObservers(null, "browser-search-engine-modified",
+ "engine-current");
+ return val;
+ ]]></setter>
+ <getter><![CDATA[
+ var currentEngine = Services.search.currentEngine;
+ // Return a dummy engine if there is no currentEngine
+ return currentEngine || {name: "", uri: null};
+ ]]></getter>
+ </property>
+
+ <!-- textbox is used by sanitize.js to clear the undo history when
+ clearing form information. -->
+ <property name="textbox" readonly="true"
+ onget="return this._textbox;"/>
+
+ <property name="value" onget="return this._textbox.value;"
+ onset="return this._textbox.value = val;"/>
+
+ <method name="focus">
+ <body><![CDATA[
+ this._textbox.focus();
+ ]]></body>
+ </method>
+
+ <method name="select">
+ <body><![CDATA[
+ this._textbox.select();
+ ]]></body>
+ </method>
+
+ <method name="onInitComplete">
+ <parameter name="aStatus"/>
+ <body><![CDATA[
+ if (!this._initialized)
+ return;
+ if (Components.isSuccessCode(aStatus)) {
+ // Refresh the display (updating icon, etc)
+ this.updateDisplay();
+ } else {
+ Cu.reportError("Cannot initialize search service, bailing out: " + aStatus);
+ }
+ ]]></body>
+ </method>
+
+ <method name="onSuccess">
+ <parameter name="aEngine"/>
+ <body><![CDATA[
+ this.currentEngine = aEngine;
+ ]]></body>
+ </method>
+
+ <method name="onError">
+ <parameter name="aErrorCode"/>
+ <body><![CDATA[
+ ]]></body>
+ </method>
+
+ <method name="observe">
+ <parameter name="aEngine"/>
+ <parameter name="aTopic"/>
+ <parameter name="aVerb"/>
+ <body><![CDATA[
+ if (aTopic == "browser-search-engine-modified" ||
+ (aTopic == "browser-search-service" && aVerb == "init-complete")) {
+ switch (aVerb) {
+ case "engine-removed":
+ this.offerNewEngine(aEngine);
+ break;
+ case "engine-added":
+ this.hideNewEngine(aEngine);
+ break;
+ case "engine-current":
+ // The current engine was changed. Rebuilding the menu appears to
+ // confuse its idea of whether it should be open when it's just
+ // been clicked, so we force it to close now.
+ this._popup.hidePopup();
+ break;
+ case "engine-changed":
+ // An engine was removed (or hidden) or added, or an icon was
+ // changed. Do nothing special.
+ }
+
+ // Make sure the engine list is refetched next time it's needed
+ this._engines = null;
+
+ // Rebuild the popup and update the display after any modification.
+ this.rebuildPopup();
+ this.updateDisplay();
+ }
+ ]]></body>
+ </method>
+
+ <!-- There are two seaprate lists of search engines, whose uses intersect
+ in this file. The search service (nsIBrowserSearchService and
+ nsSearchService.js) maintains a list of Engine objects which is used to
+ populate the searchbox list of available engines and to perform queries.
+ That list is accessed here via this.SearchService, and it's that sort of
+ Engine that is passed to this binding's observer as aEngine.
+
+ In addition, navigator.js fills two lists of autodetected search engines
+ (browser.engines and browser.hiddenEngines) as properties of
+ mCurrentBrowser. Those lists contain unnamed JS objects of the form
+ { uri:, title:, icon: }, and that's what the searchbar uses to determine
+ whether to show any "Add <EngineName>" menu items in the drop-down.
+
+ The two types of engines are currently related by their identifying
+ titles (the Engine object's 'name'), although that may change; see bug
+ 335102. -->
+
+ <!-- If the engine that was just removed from the searchbox list was
+ autodetected on this page, move it to each browser's active list so it
+ will be offered to be added again. -->
+ <method name="offerNewEngine">
+ <parameter name="aEngine"/>
+ <body><![CDATA[
+ for (var browser of getBrowser().browsers) {
+ if (browser.hiddenEngines) {
+ // XXX This will need to be changed when engines are identified by
+ // URL rather than title; see bug 335102.
+ var removeTitle = aEngine.wrappedJSObject.name;
+ for (var i = 0; i < browser.hiddenEngines.length; i++) {
+ if (browser.hiddenEngines[i].title == removeTitle) {
+ if (!browser.engines)
+ browser.engines = [];
+ browser.engines.push(browser.hiddenEngines[i]);
+ browser.hiddenEngines.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+ this.updateSearchButton();
+ ]]></body>
+ </method>
+
+ <!-- If the engine that was just added to the searchbox list was
+ autodetected on this page, move it to each browser's hidden list so it is
+ no longer offered to be added. -->
+ <method name="hideNewEngine">
+ <parameter name="aEngine"/>
+ <body><![CDATA[
+ for (var browser of getBrowser().browsers) {
+ if (browser.engines) {
+ // XXX This will need to be changed when engines are identified by
+ // URL rather than title; see bug 335102.
+ var removeTitle = aEngine.wrappedJSObject.name;
+ for (var i = 0; i < browser.engines.length; i++) {
+ if (browser.engines[i].title == removeTitle) {
+ if (!browser.hiddenEngines)
+ browser.hiddenEngines = [];
+ browser.hiddenEngines.push(browser.engines[i]);
+ browser.engines.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+ this.updateSearchButton();
+ ]]></body>
+ </method>
+
+ <method name="updateSearchButton">
+ <body><![CDATA[
+ var engines = getBrowser().mCurrentBrowser.engines;
+ if (engines && engines.length)
+ this.searchButton.setAttribute("addengines", "true");
+ else
+ this.searchButton.removeAttribute("addengines");
+ ]]></body>
+ </method>
+
+ <method name="updateDisplay">
+ <body><![CDATA[
+ var uri = this.currentEngine.iconURI;
+ this.setAttribute("image", uri ? uri.spec : "");
+
+ var name = this.currentEngine.name;
+ var text = this._stringBundle.getFormattedString("searchtip", [name]);
+ this._textbox.placeholder = name;
+ this._textbox.tooltipText = text;
+ ]]></body>
+ </method>
+
+ <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items
+ for new search engines that can be added to the available list). This
+ is called each time the popup is shown.
+ -->
+ <method name="rebuildPopupDynamic">
+ <body><![CDATA[
+ // We might not have added the main popup items yet, do that first
+ // if needed.
+ if (this._needToBuildPopup)
+ this.rebuildPopup();
+
+ var popup = this._popup;
+ // Clear any addengine menuitems, including addengine-item entries and
+ // the addengine-separator. Work backward to avoid invalidating the
+ // indexes as items are removed.
+ var items = popup.childNodes;
+ for (var i = items.length - 1; i >= 0; i--) {
+ if (items[i].classList.contains("addengine-item") ||
+ items[i].classList.contains("addengine-separator"))
+ items[i].remove();
+ }
+
+ var addengines = getBrowser().mCurrentBrowser.engines;
+ if (addengines && addengines.length > 0) {
+ const kXULNS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ // Find the (first) separator in the remaining menu, or the first item
+ // if no separators are present.
+ var insertLocation = popup.firstChild;
+ while (insertLocation.nextSibling &&
+ insertLocation.localName != "menuseparator") {
+ insertLocation = insertLocation.nextSibling;
+ }
+ if (insertLocation.localName != "menuseparator")
+ insertLocation = popup.firstChild;
+
+ var separator = document.createElementNS(kXULNS, "menuseparator");
+ separator.setAttribute("class", "addengine-separator");
+ popup.insertBefore(separator, insertLocation);
+
+ // Insert the "add this engine" items.
+ for (var i = 0; i < addengines.length; i++) {
+ var engineInfo = addengines[i];
+ var labelStr =
+ this._stringBundle.getFormattedString("cmd_addFoundEngine",
+ [engineInfo.title]);
+ var menuitem = document.createElementNS(kXULNS, "menuitem");
+ menuitem.setAttribute("class", "menuitem-iconic addengine-item");
+ menuitem.setAttribute("label", labelStr);
+ menuitem.setAttribute("tooltiptext", engineInfo.uri);
+ menuitem.setAttribute("uri", engineInfo.uri);
+ if (engineInfo.icon)
+ menuitem.setAttribute("image", engineInfo.icon);
+ menuitem.setAttribute("title", engineInfo.title);
+ popup.insertBefore(menuitem, insertLocation);
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!-- Rebuilds the list of visible search engines in the menu. Does not remove
+ or update any dynamic entries (i.e., "Add this engine" items) nor the
+ Manage Engines item. This is called by the observer when the list of
+ visible engines, or the currently selected engine, has changed.
+ -->
+ <method name="rebuildPopup">
+ <body><![CDATA[
+ var popup = this._popup;
+
+ // Clear the popup, down to the first separator
+ while (popup.firstChild && popup.firstChild.localName != "menuseparator")
+ popup.firstChild.remove();
+
+ const kXULNS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ var engines = this.engines;
+ for (var i = engines.length - 1; i >= 0; --i) {
+ var menuitem = document.createElementNS(kXULNS, "menuitem");
+ var name = engines[i].name;
+ menuitem.setAttribute("label", name);
+ menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem menuitem-with-favicon");
+ // Since this menu is rebuilt by the observer method whenever a new
+ // engine is selected, the "selected" attribute does not need to be
+ // explicitly cleared anywhere.
+ if (engines[i] == this.currentEngine)
+ menuitem.setAttribute("selected", "true");
+ var tooltip = this._stringBundle.getFormattedString("searchtip", [name]);
+ menuitem.setAttribute("tooltiptext", tooltip);
+ if (engines[i].iconURI)
+ menuitem.setAttribute("image", engines[i].iconURI.spec);
+ popup.insertBefore(menuitem, popup.firstChild);
+ menuitem.engine = engines[i];
+ }
+
+ this._needToBuildPopup = false;
+ ]]></body>
+ </method>
+
+ <method name="selectEngine">
+ <parameter name="aEvent"/>
+ <parameter name="isNextEngine"/>
+ <body><![CDATA[
+ // Find the new index
+ var newIndex = this.engines.indexOf(this.currentEngine);
+ newIndex += isNextEngine ? 1 : -1;
+
+ if (newIndex >= 0 && newIndex < this.engines.length)
+ this.currentEngine = this.engines[newIndex];
+
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ ]]></body>
+ </method>
+
+ <method name="handleSearchCommand">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var textBox = this._textbox;
+ var textValue = textBox.value;
+
+ var where = "current";
+ if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") {
+ if (aEvent.button == 2)
+ return;
+ where = whereToOpenLink(aEvent, false, true);
+ }
+ else {
+ var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
+ if ((aEvent && aEvent.altKey) ^ newTabPref)
+ where = "tabfocused";
+ }
+
+ this.doSearch(textValue, where);
+ ]]></body>
+ </method>
+
+ <method name="doSearch">
+ <parameter name="aData"/>
+ <parameter name="aWhere"/>
+ <body><![CDATA[
+ var textBox = this._textbox;
+
+ // Save the current value in the form history.
+ if (aData && !this.usePrivateBrowsing && this.FormHistory.enabled) {
+ this.FormHistory.update(
+ { op: "bump",
+ fieldname: textBox.getAttribute("autocompletesearchparam"),
+ value: aData },
+ { handleError(aError) {
+ Cu.reportError("Saving search to form history failed: " + aError.message);
+ }});
+ }
+
+ // null parameter below specifies HTML response for search
+ var submission = this.currentEngine.getSubmission(aData);
+ openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="command"><![CDATA[
+ const target = event.originalTarget;
+ if (target.engine) {
+ this.currentEngine = target.engine;
+ } else if (target.classList.contains("addengine-item")) {
+ // We only detect OpenSearch files
+ var type = Ci.nsISearchEngine.DATA_XML;
+ // Select the installed engine if the installation succeeds
+ Services.search.addEngine(target.getAttribute("uri"), type,
+ target.getAttribute("image"), false,
+ this);
+ }
+ else
+ return;
+
+ this.focus();
+ this.select();
+ ]]></handler>
+
+ <handler event="popupshowing" action="this.rebuildPopupDynamic();"/>
+
+ <handler event="DOMMouseScroll"
+ phase="capturing"
+ modifiers="accel"
+ action="this.selectEngine(event, (event.detail > 0));"/>
+
+ <handler event="focus"><![CDATA[
+ // Speculatively connect to the current engine's search URI (and
+ // suggest URI, if different) to reduce request latency.
+ this.currentEngine.speculativeConnect({window: window});
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="search-textbox"
+ extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
+ <implementation implements="nsIObserver">
+ <constructor><![CDATA[
+ var bindingParent = document.getBindingParent(this);
+ if (bindingParent && bindingParent.parentNode.parentNode.localName ==
+ "toolbarpaletteitem")
+ return;
+
+ if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
+ this.setAttribute("clickSelectsAll", true);
+
+ // Add items to context menu and attach controller to handle them
+ this.controllers.appendController(this.searchbarController);
+ // bindingParent is not set in the sidebar because there is no
+ // searchbar created in it.
+ if (bindingParent) {
+ bindingParent._textboxInitialized = true;
+ }
+
+ // Add observer for suggest preference
+ Services.prefs.addObserver("browser.search.suggest.enabled", this);
+
+ this._inputBox.setAttribute("suggestchecked", this._suggestEnabled);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ Services.prefs.removeObserver("browser.search.suggest.enabled", this);
+
+ // Because XBL and the customize toolbar code interacts poorly,
+ // there may not be anything to remove here
+ try {
+ this.controllers.removeController(this.searchbarController);
+ } catch (ex) { }
+ ]]></destructor>
+ <field name="_inputBox">
+ document.getAnonymousElementByAttribute(this, "anonid", "textbox-input-box");
+ </field>
+ <field name="_suggestEnabled">
+ Services.prefs.getBoolPref("browser.search.suggest.enabled");
+ </field>
+
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body><![CDATA[
+ if (aTopic == "nsPref:changed") {
+ this._suggestEnabled =
+ Services.prefs.getBoolPref("browser.search.suggest.enabled");
+ this._inputBox.setAttribute("suggestchecked", this._suggestEnabled);
+ }
+ ]]></body>
+ </method>
+
+ <field name="FormHistory" readonly="true"><![CDATA[
+ (ChromeUtils.import("resource://gre/modules/FormHistory.jsm", {}))
+ .FormHistory;
+ ]]>
+ </field>
+
+ <!-- nsIController -->
+ <field name="searchbarController" readonly="true"><![CDATA[({
+ supportsCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pasteAndSearch":
+ case "cmd_clearhistory":
+ case "cmd_togglesuggest":
+ return true;
+ }
+ return false;
+ },
+
+ isCommandEnabled: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pasteAndSearch":
+ return document.commandDispatcher
+ .getControllerForCommand("cmd_paste")
+ .isCommandEnabled("cmd_paste");
+ case "cmd_clearhistory":
+ case "cmd_togglesuggest":
+ return true;
+ }
+ return false;
+ },
+
+ doCommand: function (aCommand) {
+ switch (aCommand) {
+ case "cmd_pasteAndSearch":
+ this.select();
+ goDoCommand("cmd_paste");
+ this.onTextEntered();
+ break;
+ case "cmd_clearhistory":
+ this.FormHistory.update(
+ { op : "remove", fieldname : "searchbar-history" },
+ null);
+ this.value = "";
+ break;
+ case "cmd_togglesuggest":
+ // The pref observer will update _suggestEnabled and the menu
+ // checkmark.
+ Services.prefs.setBoolPref("browser.search.suggest.enabled",
+ !this._suggestEnabled);
+ break;
+ default:
+ // do nothing with unrecognized command
+ }
+ }.bind(this)
+ })]]></field>
+ </implementation>
+
+ <handlers>
+ <handler event="dragover">
+ <![CDATA[
+ var types = event.dataTransfer.types;
+ if (types.includes("text/plain") || types.includes("text/x-moz-text-internal"))
+ event.preventDefault();
+ ]]>
+ </handler>
+
+ <handler event="drop">
+ <![CDATA[
+ var dataTransfer = event.dataTransfer;
+ var data = dataTransfer.getData("text/plain");
+ if (!data)
+ data = dataTransfer.getData("text/x-moz-text-internal");
+ if (data) {
+ event.preventDefault();
+ this.value = data;
+ this.onTextEntered();
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="searchbar-textbox"
+ extends="chrome://communicator/content/search/search.xml#search-textbox">
+ <implementation>
+ <method name="openSearch">
+ <body>
+ <![CDATA[
+ // Don't open search popup if history popup is open
+ if (!this.popupOpen) {
+ document.getBindingParent(this).searchButton.open = true;
+ return false;
+ }
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <!-- override |onTextEntered| in autocomplete.xml -->
+ <method name="onTextEntered">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var evt = aEvent || this.mEnterEvent;
+ document.getBindingParent(this).handleSearchCommand(evt);
+ this.mEnterEvent = null;
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress" keycode="VK_UP" modifiers="accel"
+ phase="capturing"
+ action="document.getBindingParent(this).selectEngine(event, false);"/>
+
+ <handler event="keypress" keycode="VK_DOWN" modifiers="accel"
+ phase="capturing"
+ action="document.getBindingParent(this).selectEngine(event, true);"/>
+
+ <handler event="keypress" keycode="VK_DOWN" modifiers="alt"
+ phase="capturing"
+ action="return this.openSearch();"/>
+
+ <handler event="keypress" keycode="VK_UP" modifiers="alt"
+ phase="capturing"
+ action="return this.openSearch();"/>
+
+ <handler event="keypress" keycode="VK_F4" phase="capturing">
+ <![CDATA[
+ return (AppConstants.platform == "macosx") || this.openSearch()
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="input-box-search" extends="chrome://global/content/bindings/textbox.xml#input-box">
+ <content context="_child">
+ <children/>
+ <xul:menupopup anonid="input-box-contextmenu"
+ class="textbox-contextmenu"
+ onpopupshowing="var input =
+ this.parentNode.getElementsByAttribute('anonid', 'input')[0];
+ if (document.commandDispatcher.focusedElement != input)
+ input.focus();
+ this.parentNode._doPopupItemEnabling(this);"
+ oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if (cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
+ <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
+ <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
+ <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
+ <xul:menuitem label="&pasteSearchCmd.label;" accesskey="&pasteSearchCmd.accesskey;" cmd="cmd_pasteAndSearch"/>
+ <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
+ <xul:menuseparator/>
+ <xul:menuitem label="&clearHistoryCmd.label;" accesskey="&clearHistoryCmd.accesskey;" cmd="cmd_clearhistory"/>
+ <xul:menuitem label="&showSuggestionsCmd.label;"
+ accesskey="&showSuggestionsCmd.accesskey;"
+ anonid="toggle-suggest-item"
+ type="checkbox"
+ autocheck="false"
+ xbl:inherits="checked=suggestchecked"
+ cmd="cmd_togglesuggest"/>
+ </xul:menupopup>
+ </content>
+ </binding>
+</bindings>
diff --git a/comm/suite/components/search/content/searchbarBindings.css b/comm/suite/components/search/content/searchbarBindings.css
new file mode 100644
index 0000000000..3fefc9365a
--- /dev/null
+++ b/comm/suite/components/search/content/searchbarBindings.css
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.search-textbox {
+ -moz-binding: url("chrome://communicator/content/search/search.xml#search-textbox");
+}
+
+.searchbar-textbox {
+ -moz-binding: url("chrome://communicator/content/search/search.xml#searchbar-textbox");
+}
+
+.textbox-input-box {
+ -moz-binding: url("chrome://communicator/content/search/search.xml#input-box-search");
+}
+
+.autocomplete-history-dropmarker {
+ display: none;
+}
diff --git a/comm/suite/components/search/jar.mn b/comm/suite/components/search/jar.mn
new file mode 100644
index 0000000000..d6ec19c78e
--- /dev/null
+++ b/comm/suite/components/search/jar.mn
@@ -0,0 +1,16 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+% content communicator %content/communicator/ contentaccessible=yes
+ content/communicator/search/engineManager.js (content/engineManager.js)
+ content/communicator/search/engineManager.xul (content/engineManager.xul)
+ content/communicator/search/search.xml (content/search.xml)
+ content/communicator/search/searchbarBindings.css (content/searchbarBindings.css)
+ content/communicator/search/search-panel.js (content/search-panel.js)
+ content/communicator/search/search-panel.xul (content/search-panel.xul)
+
+ searchplugins/ (searchplugins/**)
+
+% resource search-plugins %searchplugins/
diff --git a/comm/suite/components/search/moz.build b/comm/suite/components/search/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/search/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/search/searchplugins/allegro-pl.xml b/comm/suite/components/search/searchplugins/allegro-pl.xml
new file mode 100644
index 0000000000..e6a9f97b4e
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/allegro-pl.xml
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Allegro</ShortName>
+<Description>Wyszukiwanie w aukcjach Allegro</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAFr/OgBa/zkAWv84AFr/OABa/zgAWv85AFr/PABa/wn///8A////AP///wD///8A////AABa/xYAWv+4AFr//wBa//8AWv//AFr//wBa//8AWv//AFr//wBa//8AWv+H////AP///wD///8A////AP///wAAWv/wAFr//wBa//8AWv//AFr//wBa//8AWv//AFr//wBa//8AWv//AFr/gv///wD///8A////AP///wAAWv9MAFr//wBa//8AWv//AFr/ewBa/zUAWv82AFr/NABa/2YAWv//AFr//wBa/4H///8A////AP///wD///8AAFr/gwBa//8AWv//AFr/if///wD///8A////AP///wAAWv8yAFr//wBa//8AWv+A////AP///wD///8A////AABa/4MAWv//AFr//wBa/8L///8A////AP///wD///8AAFr/MQBa//8AWv//AFr/gP///wD///8A////AP///wAAWv8oAFr//wBa//8AWv//AFr/xABa/34AWv9/AFr/fQBa/5sAWv//AFr//wBa/4D///8A////AP///wD///8A////AABa/5QAWv//AFr//wBa//8AWv//AFr//wBa//8AWv//AFr//wBa//8AWv+A////AP///wD///8A////AP///wD///8AAFr/NwBa/7UAWv//AFr//wBa//8AWv//AFr//wBa//8AWv//AFr/gf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AABa/2IAWv//AFr//wBa/4L///8A////AP///wD///8A////AP///wAAWv/RAFr/ywBa/0b///8A////AABa/woAWv/mAFr//wBa//8AWv85////AP///wD///8A////AP///wD///8AAFr//wBa//8AWv//AFr//wBa//8AWv//AFr//wBa//8AWv/v////AP///wD///8A////AP///wD///8A////AABa/5gAWv//AFr//wBa//8AWv//AFr//wBa//8AWv//AFr/J////wD///8A////AP///wD///8A////AP///wD///8AAFr/FgBa/14AWv+CAFr/gQBa/4IAWv9eAFr/BP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AADwAwAA4AMAAOPjAADD4wAAw+MAAOHDAADgAwAA+AMAAP/jAADzxwAA8AcAAPAPAAD+PwAA//8AAA==</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://allegro.pl/listing/listing.php"
+ resultdomain="allegro.pl">
+ <Param name="string" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://allegro.pl/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-br.xml b/comm/suite/components/search/searchplugins/amazon-br.xml
new file mode 100644
index 0000000000..4eb17f368d
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-br.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.com.br</ShortName>
+<Description>Pesquisa Amazon.com.br</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.com.br/exec/obidos/external-search/"
+ resultdomain="amazon.com.br">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.com.br/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-de.xml b/comm/suite/components/search/searchplugins/amazon-de.xml
new file mode 100644
index 0000000000..b25a056ced
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-de.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.de</ShortName>
+<Description>Amazon.de Suche</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.de/exec/obidos/external-search/"
+ resultdomain="amazon.de">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.de/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-en-GB.xml b/comm/suite/components/search/searchplugins/amazon-en-GB.xml
new file mode 100644
index 0000000000..30f57ff346
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-en-GB.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.co.uk</ShortName>
+<Description>Amazon.co.uk Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.co.uk/exec/obidos/external-search/"
+ resultdomain="amazon.co.uk">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.co.uk/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-es.xml b/comm/suite/components/search/searchplugins/amazon-es.xml
new file mode 100644
index 0000000000..641a44948a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-es.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.es</ShortName>
+<Description>Amazon.es Buscar</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.es/exec/obidos/external-search/"
+ resultdomain="amazon.es">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.es/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-fr.xml b/comm/suite/components/search/searchplugins/amazon-fr.xml
new file mode 100644
index 0000000000..9e3176f067
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-fr.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.fr</ShortName>
+<Description>Recherche Amazon.fr</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.fr/exec/obidos/external-search/"
+ resultdomain="amazon.fr">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.fr/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-it.xml b/comm/suite/components/search/searchplugins/amazon-it.xml
new file mode 100644
index 0000000000..488a41583d
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-it.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.it</ShortName>
+<Description>Ricerca Amazon.it</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.it/exec/obidos/external-search/"
+ resultdomain="amazon.it">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.it/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-jp.xml b/comm/suite/components/search/searchplugins/amazon-jp.xml
new file mode 100644
index 0000000000..a1725d74c0
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-jp.xml
@@ -0,0 +1,32 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.co.jp</ShortName>
+<Description>Amazon.co.jp Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.co.jp/exec/obidos/external-search/"
+ resultdomain="amazon.co.jp">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="mode" value="blended"/>
+ <!--
+ <Param name="mode" value="books-jp"/>
+ <Param name="mode" value="books-us"/>
+ -->
+ <Param name="sourceid" value="Mozilla-search"/>
+ <!--
+ <Param name="sz" value="25"/>
+ <Param name="rank" value="+salesrank"/>
+ <Param name="rank" value="+pricerank"/>
+ <Param name="rank" value="+inverse-pricerank"/>
+ <Param name="rank" value="+daterank"/>
+ <Param name="rank" value="+titlerank"/>
+ <Param name="rank" value="-titlerank"/>
+ -->
+</Url>
+<SearchForm>https://www.amazon.co.jp/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon-zh-CN.xml b/comm/suite/components/search/searchplugins/amazon-zh-CN.xml
new file mode 100644
index 0000000000..bdf814bf71
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon-zh-CN.xml
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>亚马逊</ShortName>
+<Description>亚马逊æœç´¢</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.cn/mn/searchApp"
+ resultdomain="amazon.cn">
+ <Param name="keywords" value="{searchTerms}"/>
+ <Param name="ix" value="sunray"/>
+ <Param name="pageletid" value="headsearch"/>
+ <Param name="searchType" value=""/>
+ <Param name="Go.x" value="0"/>
+ <Param name="Go.y" value="0"/>
+ <Param name="bestSaleNum" value="0"/>
+</Url>
+<SearchForm>https://www.amazon.cn/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/amazon.xml b/comm/suite/components/search/searchplugins/amazon.xml
new file mode 100644
index 0000000000..b84e74a584
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/amazon.xml
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Amazon.com</ShortName>
+<Description>Amazon.com Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/amazon.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://completion.amazon.com/search/complete">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="search-alias" value="aps"/>
+ <Param name="mkt" value="1"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.amazon.com/exec/obidos/external-search/"
+ resultdomain="amazon.com">
+ <Param name="field-keywords" value="{searchTerms}"/>
+ <Param name="ie" value="{inputEncoding}"/>
+ <Param name="mode" value="blended"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.amazon.com/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/atlas-sk.xml b/comm/suite/components/search/searchplugins/atlas-sk.xml
new file mode 100644
index 0000000000..4caaf67811
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/atlas-sk.xml
@@ -0,0 +1,14 @@
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Atlas</ShortName>
+<Description>Internetovy portal - Atlas.sk</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALESURBVHjaYvz//z8DMvj7nuHXY4YfLxiA4hziDJzyDIyCyPIAAcSCYP55w/Bi7deHW188e/H5B/tfRg5mVnZuAWEJVXteWR8GVjGIKoAAYoTa8P3mz0tVj27f+8rrIazmxSkgx8TM+v/fr6/vH768vZ+L7aOKZTI7vw5QIUAAAW3+///7ve8H3W+sCHh+59zf/+jgz5//j64fO7e16fPrG0AuQAAxAI35e7n4xVr3L68f/8cNnt27fOXQit+/vgMEEOP/z5d+HMn+r97Eqehw/9Wf5Ue+/vrDyMLCxMLIyMTMKMzDbKPBqi7FBHTMz+/f2Dg4AAKIheH9JUYuTTYZK6DQ07d/Zu36+O0HIzs7MzMTEysrMycH26Zzv0t9OW3Umdk5uYBqAAKI5e+394y8mkysbECOhjRLTzTX9x9/mZiY/v//t+/0g1MvJO+/5t989pe5CicrM8jPAAHExMDE85+ZCxJkInwsqrJ8Pxk43nxl+vybjY2V9d+fn3/+Mjx88+/nb2h0AQQQCxOPIsvvuxDOhpPfGtd8fvWJgY0V6KL/zMyKzBwM//7++/2PCR67AAHExCigwcDMzvDn8/ffDGtOfHvwhoH970c7yftxhi9Nuc/+/vjk9z/mv38Z4dELEEAsDJxijJL2DEws//8w/GdgYmX+//c/44GTDw7/+/Lr509maTEmBsa///4zwKwACCBg0mD8zyPz689v5v8/HDUZTt399+OPMLO8x39GBjZGRqDCn7///vqDSEEAAcQCTBk/vn9/9+79t29fjcV/ZZp9P3Pv7/effxkY/nNyccnJyvAL8EkJMLGxQF0FEEDA+GFgZWVhYWUFJoGvX75JsT4zFXzz7t2nX7//crNwG4rquTiYcnKywm0ACCBGePL+8/fvn99/fv768+Pn71+//gDjAehadnY2QX4eNjZmuAaAAAMA9HBNfsA2E5MAAAAASUVORK5CYII=</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.atlas.sk/search.php"
+ resultdomain="atlas.sk">
+ <Param name="phrase" value="{searchTerms}"/>
+ <Param name="sourceid" value="firefox"/>
+</Url>
+<SearchForm>https://www.atlas.sk/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/azet-sk.xml b/comm/suite/components/search/searchplugins/azet-sk.xml
new file mode 100644
index 0000000000..49b04cf57e
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/azet-sk.xml
@@ -0,0 +1,15 @@
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Azet</ShortName>
+<Description>Azet - portal, kde je vzdy najviac ludi</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANinbf/GdRz/xnUc/8qDNv/Yp23/68+u/9inbf/Yp23/2Kdt/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMqDNv/GdRz/xnUc/8Z1HP/Kgzb/xnUc/8Z1HP/GdRz/xnUc/8qDNv8AAAAAAAAAAAAAAAAAAAAAAAAAAOvPrv/GdRz/xnUc/8qDNv8AAAAAAAAAAAAAAADYp23/xnUc/8Z1HP/Kgzb/AAAAAAAAAAAAAAAAAAAAAAAAAADrz67/xnUc/8Z1HP/Yp23/AAAAAAAAAAAAAAAA68+u/8Z1HP/GdRz/yoM2/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMqDNv/GdRz/xnUc/9inbf/rz67/68+u/+vPrv/GdRz/xnUc/8qDNv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2Kdt/8qDNv/GdRz/xnUc/8Z1HP/GdRz/xnUc/8Z1HP/Kgzb/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADrz67/2Kdt/8Z1HP/GdRz/yoM2//qu2f/uR5n/7keZ/+5Hmf/uR5n/AAAAAMqDNv/Kgzb/yoM2/wAAAAAAAAAAAAAAAOvPrv/GdRz/xnUc/8qDNv/uR5n/7keZ/+5Hmf/uR5n/7keZ/wAAAADYp23/xnUc/8Z1HP/Kgzb/68+u/+vPrv/Kgzb/xnUc/8Z1HP/Yp23/7keZ/+5Hmf/uR5n/7keZ//qu2f8AAAAAAAAAANinbf/GdRz/xnUc/8Z1HP/GdRz/xnUc/8Z1HP/Yp23/AAAAAAAAAAD6rtn/AAAAAOvPrv/Kgzb/vXsA/9inbf8AAAAAAAAAAOvPrv/rz67/68+u/+vPrv8AAAAAAAAAAAAAAAAAAAAAAAAAAOvPrv/Bfwz/vXsA/717AP/Yp23/AAAAAFnCg/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADKgzb/vXsA/717AP/Kgzb/AAAAAFnCg/9ZwoP/WcKD/wAAAAATof//E6H//wAAAAAAAAAAAAAAAAAAAAAAAAAA68+u/8F/DP/Bfwz/68+u/wAAAABZwoP/WcKD/1nCg/8AAAAAE6H//xOh//8Tof//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZwoP/WcKD/1nCg/9ZwoP/AAAAABOh//8Tof//E6H//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFnCg/9ZwoP/AAAAAAAAAAATof//E6H//xOh//8AAAAAAAAAAP4AAAD8AAAA+HAAAPhwAAD8AAAA/gAAAP/gAAAEcAAABAAAAAYBAAChhwAAwX8AAMInAADCIwAA/CMAAP5jAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAATCwAAEwsAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAAAAABgIAAAv///wDMZgAFtGQUM7VnEkW1ZxJFs2YSRrNnE0OzZxYvs2YaCv///wD///8Av2AgCLhmFBm1ahUYtWoVGK1mFBmuaBcW1YAABv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8As2YSRrVnE7m0ZxTyvm0V/75tFf+/bRX/u2sU/7NnE+6zZxO9s2YTa79AAAS0ZxNqs2cU2LNnFMy0ZxTLs2cT17RnE665XRcL////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/gAAC////ALNoFFuzZxT2wG0V/7VnFP+zZxP9tGcT+7NmE/m0ZxP7tGcU/7trFP+3aRT/s2cTubNnFNC+bRX/uWoU/7lqFP/CbxX/s2YTr////wD/AAAB////AP///wD///8A////AP///wD///8A////AP///wD///8AqlUAA////wC0aBEstGYT7bpqFP+0ZxP6tGYT/rNmFP+4aRT/wm8V/71sFf+9bBX/vmwV/7dpFP+5ahT/tGcU/7RnE/y0ZxP8s2cT/bZoFP+zZxN5////AICAAAL///8A////AP///wD///8A////AP///wD///8A////AP///wCAgAAC////ALRnE3fBbhX/tGcT+rRnFP+0ZhP+t2kU/7RnFMu0ZxNqs2cSVLVoElO1ZhR/s2YT4bdpFP+0ZhP+tGcU/7NmFP+zZxT/uGkU/7NoE3b///8A/4AAAv///wD///8A////AP///wD///8A////AP///wD///8A/wAAAf///wCqVQADs2YTorxrFP+zZxP9s2cT/bpqFP+zZhTbuGoVJP///wD///8A////AP///wCyZRIrs2cU2LlqFP+zZxP9s2YU/7NnFP+4aRT/tGcTd////wCqVQAD////AP///wD///8A////AP///wD///8A////AP///wD//wAB////ALZtJAezZhOvu2sU/7NnE/20ZxP7wG4V/7NnFLP///8AqnEcCapVAAaZZgAFqlUAA////wCzZhNstmgU/7RnFP+0ZhP+s2cU/7hpFP+0ZxN3////AP//AAH///8A////AP///wD///8A////AP///wD///8A////AP8AAAH///8A////ALNnFJe+bRX/s2YT/LRnE/y7axT/s2cT07tmEQ////8A////AICAAALVgAAG////ALNmEyizZxTzt2kU/7NnE/2zZxT/uGkU/7RnE3f///8A//8AAf///wD///8A////AP///wD///8A////AP///wD///8A////AKpVAAP///8AtGcUZr5sFf+0ZxP7tGYT/rRmE/64aRT/tGcTq7RkFDOxYhQN////AP///wD///8As2YaCrRmE++4aRT/s2cT/bNnFP+4aRT/tGcTd////wD//wAB////AP///wD///8A////AP///wD///8A////AP///wD///8AgIAAAv///wCuaw0TtGcUzMFuFf+zZxT/tGcT+7RmE/y7axT/s2cU9rNnE720ZxScs2YTh7NlFFuzZxI5s2cT7rhpFP+zZxP9s2cU/7hpFP+0ZxN3////AP//AAH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AgIAAAv///wC3aRYutGcUv7VnFP++bRX/u2sU/7VoFP+1ZxT/uWoU/79tFf+/bRX/s2cT/bRnFOezZhP8tGcU/7RmE/6zZxT/uGkU/7RnE3f///8A//8AAf///wD///8A////AAAAAAH/AP8BgACAAoAAgAKAAIACgACAAv8AAAH///8AgIAAAv///wCqgAAGsmgTYLRnE66zZhTRs2cU67RnFP/AbhX/vWwV/7prFP+6ahT/uGkU/7RnFP+zZxT/s2YU/7NnFP+4aRT/tGcTd////wD//wAB////AP///wCAAIACAAAAAf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALhjDhKzZhQys2cVSrRlFFi1ZhR1tGcUmbNmFNG3aBT/tGcU/7NnFP+zZhT/s2cU/7hpFP+0ZxN3////AP//AAH///8AgACAAv///wD///8AiTtiDYgzZg+SN20OkjdtDogzZg+LLnQL////AJlmAAW7ZhEPu2YRD7RpDxGvYBAQtm0kB////wD///8A////AP///wD/AAABtm0SDrJnFH6zZhT/tGcU/7RmE/6zZxT/uGkU/7JnE3f///8A//8AAYAAgAL///8AizdpLo01bZSMNWu6jTZrvow2bL2MNWu8jDVrxo02a40AAP8Cs2cAJbRnFLC1ZhTAtGcTvLRnE7yzZhPKsmUVSf///wC5dBcLv4AABKpVAAP///8Av2oVDLRmE/C4aRT/s2cT/bNnFP+4aRT/tGcTd////wD//wAB////AI84aimMNWvYmTp1/5I4cP+SOHD/kjhw/5E3b/+XOnT/jDZr1XYAiQ3JeQATtGYW38JvFf+5ahT/uWoU/8ZxFv+0ZxOu////AP///wD///8A////AP///wCzZhJGtGcT+rZoFP+zZxP9s2cU/7dpFP+zZhN2////AP//AAH///8AjDVsh5g6dP+MNWz5jTZr/Iw1bP2MNWz9jDZs+402a/yNNWz4jTlqOv///wCzZheevWwV/7RnFPizZhP8s2YT/LdoFP+zZhOdsmUTNbFpFi63aRYutGYUS7NnE9O4aRT/tGYT/rRmE/6zZxT/tmgU/7RoEkf///8AgIAAAv///wCMNWyVlzp0/4w1a/qNNmz/jDZs/402a/yMNmz/kjdw/5M4cP+NOGpg////ALBmHC2zZxTivWwV/7NmE/y0ZhP8tGcU/7trFP+0ZhP+s2cU67RnE/C4aRT/uWkU/7RmE/6zZxP9tGcT+r1sFf+0ZxPSuGMOEv///wCAgAAC////AIs2a3WXOXT/jDVs/Yw2bPuMNmv8kjhw/5A3bv+NNmvKhy50QoAAgAaqcRwJ////ALZoE0K0ZhPgvWwV/7prFP+0ZhP+tGYT+rNnE/20ZxT/tGcU/7RnE/u0ZxP6tGYT/rhpFP+/bRX/tGcT5LZoFjv///8AgIAAAv///wD///8AkjFtFY02a7WVOXL/lTlz/5U5cv+MNmvcijNtaf///wDmmQAKtGgKhLRnFLexYxUk////ALFpFCeyaBaVsmgV2LZoFP/BbhX/vm0V/75sFP++bBT/vm0U/8FuFf+5ahT/s2cT4rRnFKa0ZBQz////AP8AAAH///8A////AP8A/wH///8AkjdtDow0a12NNWuGjDdsZnAAjxD///8AtGgNYrNnFt28axX/v20V/7VmFNG2ZhJG////AP///wDZQAAUsmUVSbJqGGq0ZhWEs2cihrJoJoexaCeJsWcrcrJnGk+0ZBMp////AP///wCAgAAC////AP///wD///8A////AIAAgAL///8A////AP///wD///8AumwANLNnF8e3aRX/u2sU/7NmE/y0ZhP6wG4V/7NnFcb/AAABQrVaHwDJaxP///8A////AP///wD///8A////AP///wD///8A////AP///wD/AAABgIAAAv///wD///8A////AP///wD///8A////AIAAgAKZM2YF////ALFmG0GzZhXyvWwU/7RnFP+zZhP8tGYU/rRnFP+zaBX/uV0ATQDXeRM6tF3bObRc1T20XZ9As1tXAAD/ASSC8z8khPBVJYLyYCyD7mlxcaoJ////AAAA/wH///8A////AP///wD///8A////AP///wD///8AAAAAAf///wD///8As2YUor1sFf+zZxP7tGYT/rRnFP+zZhP8vW0V/7NnFKT///8ANbZeiz2/Y/85uF//Psho/zm2WsAkbf8HJILzviWJ/f8lhvj/JYv//ySE81X///8AAKr/A////wD///8A////AP///wD///8A////AP///wD/AAAB////AP///wC0ZxS3uGkU/7NmE/yzZxT/s2YT/LdpFP+zZxXyyEUAJQDFZTA7tFz7ObZe/zi1Xfo6umH/ObVaryuA/wYjg/O/JYv//yOE8/gkh/r/I4Py0iCA7xD///8AAID/Av///wD///8A////AP///wD///8A////AP///wD///8A////ALRnE4jBbhX/tGcU/7NnE/21ZxT/v20V/7RmEYT///8AO7Rduzu+Yv84tV38OLRd/Tq8Yv84tVqyK4D/BiOC874liv//JIPz+yOD8/0liv//JIPzgP///wAA//8B////AP///wD///8A////AP///wD///8A////AICAAAL///8As2YRHrRmE8W2aBT/uGkU/7ZoFP+zaBa4/wAAAiu3YFg6tl7/OLZe/zi1Xf44tF39Orxi/zi2WbErgP8GI4LzviWL//8jg/P8I4Py/iSF9/8kg/LtJIbzKv///wAAgP8C////AP///wD///8A////AP///wD///8A////AP8AAAH///8AsWIUDbVnEmG1ZxODtGcTXJ+AIAj///8AObNdsjzBZP84tV37OLVd/zi0Xf06vGL/ObVZtCRt/wcjgvO+JYr//yOD8/wjg/P/I4Pz/SWJ/v8jhPN8////AAAA/wH///8A////AP///wD///8A////AP///wD///8A////AP8AAAH///8A////AP///wD///8A////AP///wA5tV2wPMBj/zi1Xfo4tV3/OLRd/Dq8Yv84tVqfAED/BCOD88Iliv//JIPz+yOD8/8jg/P9JIf6/ySD8o7///8A////AAAA/wH///8A////AP///wD///8A////AP///wD///8A////AP8AAAGqVQADqlUAA/+AAAKAv0AE////ADm1Xm87v2P/Obhf/zi1Xfs5uV//O71i/za1ZFn///8AJIXxlCaO//8jg/P8I4Pz/COD8v8liv//IoPyYf///wAAgIAC////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAH///8AM7NmCjm2XY84tV33PMBj/zi1XvA5tFyCJLaSB////wAkie0cI4PzvSWL//8lif7/JYj8/yOD9KEagOYK////AAD//wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAD/AAH///8A////ADm2XjE5tVxIObVbLf///wD///8AAKqqA////wAiiO4PIoL0WiSE9Hgig/FKK4D/Bv///wAA//8B////AP///wD///8A////AP/kAwH/+AAB/9AAAv+gAAL/oAAC/0A8Av9AQgL/YDIC/6AOAv+gAAL/0AAC4CgAAs//AAKwIHgCQAAiAoAAPgKAEAACgBAAAoAIAAWAhAALQQMAN7wAf8/IAABf2AQAX9gAAC/4CAAv6AAAF/QQABf78AAb/BAQF//QEBf/7Ggv</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.azet.sk/katalog/vyhladavanie/firmy/"
+ resultdomain="azet.sk"
+ rel="searchform">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="k" value=""/>
+</Url>
+<SearchForm>https://www.azet.sk/katalog/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/bing.xml b/comm/suite/components/search/searchplugins/bing.xml
new file mode 100644
index 0000000000..bb093f4bb5
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/bing.xml
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Bing</ShortName>
+<Description>Bing. Search by Microsoft.</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAICAAAAEACACoCAAAJgAAABAQAAABAAgAaAUAAM4IAAAoAAAAIAAAAEAAAAABAAgAAAAAAIAEAAAAAAAAAAAAAAABAAAAAAAAhIQMAI+PIwCXlzEAn59BAKioUwCurl8AtbVuAL6+fwDJyZMA0tKlANjYsgDe3r4A5eXMAOzs2QDy8uUA+fnzAP///wAAGi8AAC1QAAA/cAAAUZAAAGOwAAB2zwAAiPAAEZj/ADGm/wBRs/8AccH/AJHP/wCx3f8A0ev/AP///wAAAAAAACwvAABLUAAAaHAAAIaQAAClsAAAw88AAOHwABHv/wAx8f8AUfP/AHH1/wCR9/8Asfn/ANH7/wD///8AAAAAAAAvIQAAUDcAAHBMAACQYwAAsHkAAM+PAADwpgAR/7QAMf++AFH/yABx/9MAkf/cALH/5QDR//AA////AAAAAAAALw4AAFAYAABwIgAAkCwAALA2AADPQAAA8EoAEf9bADH/cQBR/4cAcf+dAJH/sgCx/8kA0f/fAP///wAAAAAAAi8AAARQAAAGcAAACJAAAAqwAAALzwAADvAAACD/EgA9/zEAW/9RAHn/cQCY/5EAtf+xANT/0QD///8AAAAAABQvAAAiUAAAMHAAAD2QAABMsAAAWc8AAGfwAAB4/xEAiv8xAJz/UQCu/3EAwP+RANL/sQDk/9EA////AAAAAAAmLwAAQFAAAFpwAAB0kAAAjrAAAKnPAADC8AAA0f8RANj/MQDe/1EA4/9xAOn/kQDv/7EA9v/RAP///wAAAAAALyYAAFBBAABwWwAAkHQAALCOAADPqQAA8MMAAP/SEQD/2DEA/91RAP/kcQD/6pEA//CxAP/20QD///8AAAAAAC8UAABQIgAAcDAAAJA+AACwTQAAz1sAAPBpAAD/eREA/4oxAP+dUQD/r3EA/8GRAP/SsQD/5dEA////AAAAAAAvAwAAUAQAAHAGAACQCQAAsAoAAM8MAADwDgAA/yASAP8+MQD/XFEA/3pxAP+XkQD/trEA/9TRAP///wAAAAAALwAOAFAAFwBwACEAkAArALAANgDPAEAA8ABJAP8RWgD/MXAA/1GGAP9xnAD/kbIA/7HIAP/R3wD///8AAAAAAC8AIABQADYAcABMAJAAYgCwAHgAzwCOAPAApAD/EbMA/zG+AP9RxwD/cdEA/5HcAP+x5QD/0fAA////AAAAAAAsAC8ASwBQAGkAcACHAJAApQCwAMQAzwDhAPAA8BH/APIx/wD0Uf8A9nH/APeR/wD5sf8A+9H/AP///wAAAAAAGwAvAC0AUAA/AHAAUgCQAGMAsAB2AM8AiADwAJkR/wCmMf8AtFH/AMJx/wDPkf8A3LH/AOvR/wD///8AAAAAAAgALwAOAFAAFQBwABsAkAAhALAAJgDPACwA8AA+Ef8AWDH/AHFR/wCMcf8AppH/AL+x/wDa0f8A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBggCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAsQEA0HAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwkPEBAQEBALBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMEBAQEBAQEBAPCQMAAAAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAQEBAQEBAQDQgBAAAAAAAAAAAAAAAAAAAAAAABDRAQEBAQEBAQEBAQEA0GAQAAAAAAAAAAAAAAAAAAAAENEBAQDgcLEBAQEBAQEBALBAAAAAAAAAAAAAAAAAAAAQ0QEBANAQEHDRAQEBAQEBAOCAAAAAAAAAAAAAAAAAABDRAQEA4BAAACCA4QEBAQEBANAQAAAAAAAAAAAAAAAAENEBAQDgEAAAAABAsQEBAQEA0BAAAAAAAAAAAAAAAAAQ0QEBAOAQAAAAABBw4QEBAQDQEAAAAAAAAAAAAAAAABDRAQEA4BAAABBw0QEBAQEBANAQAAAAAAAAAAAAAAAAENEBAQDgEAAAcQEBAQEBAQEA0BAAAAAAAAAAAAAAAAAQ0QEBAOAQABCxAQEBAQEBANBwAAAAAAAAAAAAAAAAABDRAQEA4BAAQPEBAQEA0IBAEAAAAAAAAAAAAAAAAAAAENEBAQDgEACRAQDQkGAQAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAOAQILCgYCAAAAAAAAAAAAAAAAAAAAAAAAAAABDRAQEA4BAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENEBAQDgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAOAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRAQEA4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENEBAQDgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ0QEBAOAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRAQEA0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENEA0IBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQcFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAACEhAAAjIwAAJKSFgCZmSgAqqpRALKyYgC+vnsAxsaKAM3NmQDU1KgA4ODBAOjozwDw8OEA+fnyAP///wD///8AAAAAAAAaLwAALVAAAD9wAABRkAAAY7AAAHbPAACI8AARmP8AMab/AFGz/wBxwf8Akc//ALHd/wDR6/8A////AAAAAAAALC8AAEtQAABocAAAhpAAAKWwAADDzwAA4fAAEe//ADHx/wBR8/8AcfX/AJH3/wCx+f8A0fv/AP///wAAAAAAAC8hAABQNwAAcEwAAJBjAACweQAAz48AAPCmABH/tAAx/74AUf/IAHH/0wCR/9wAsf/lANH/8AD///8AAAAAAAAvDgAAUBgAAHAiAACQLAAAsDYAAM9AAADwSgAR/1sAMf9xAFH/hwBx/50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAAIP8SAD3/MQBb/1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAAZ/AAAHj/EQCK/zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAAqc8AAMLwAADR/xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAAsI4AAM+pAADwwwAA/9IRAP/YMQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAAAAAALxQAAFAiAABwMAAAkD4AALBNAADPWwAA8GkAAP95EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD///8AAAAAAC8DAABQBAAAcAYAAJAJAACwCgAAzwwAAPAOAAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/1NEA////AAAAAAAvAA4AUAAXAHAAIQCQACsAsAA2AM8AQADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/scgA/9HfAP///wAAAAAALwAgAFAANgBwAEwAkABiALAAeADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/kdwA/7HlAP/R8AD///8AAAAAACwALwBLAFAAaQBwAIcAkAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2cf8A95H/APmx/wD70f8A////AAAAAAAbAC8ALQBQAD8AcABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0Uf8AwnH/AM+R/wDcsf8A69H/AP///wAAAAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBYMf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMAAAAAAAAAAAAAAAACBgwMBwIAAAAAAAAAAAAABg4ODg4MBgIAAAAAAAAAAAYODQoODg4KBAAAAAAAAAAGDgwDBQsODg4FAAAAAAAABg4MAgADCg4OBgAAAAAAAAYODAICCg4ODgYAAAAAAAAGDgwCBg4OCwcCAAAAAAAABg4MAwkIBAIAAAAAAAAAAAYODAICAAAAAAAAAAAAAAAGDgwCAAAAAAAAAAAAAAAABg4MAgAAAAAAAAAAAAAAAAYMCAEAAAAAAAAAAAAAAAACAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</Image>
+<Url type="application/x-suggestions+json"
+ template="https://www.bing.com/osjson.aspx">
+ <Param name="query" value="{searchTerms}"/>
+ <Param name="form" value="OSDJAS"/>
+ <Param name="language" value="{moz:locale}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.bing.com/search"
+ rel="searchform">
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/bolcom-nl.xml b/comm/suite/components/search/searchplugins/bolcom-nl.xml
new file mode 100644
index 0000000000..4cbfa4ba1c
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/bolcom-nl.xml
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>bol.com</ShortName>
+<Description>Zoeken bij bol.com</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAAAABMLAAATCwAAAAEAAAAAAAAAAAAA////ANJtQADot6AA3JJvAPXc0ADkpYYA7sq5ANiAVgD77egAzmMxAO3CqwDmr5IA4Jp8ANR4SgDcimMA+eTeAPHSwwD99/UA0Gk5APPWygD34NYA1nxQAOKggADos5gA7MaxAOCYdQD98+8A6r2oANRzRgDcjGkA2oheAP37+QDOZzUA5rWcAPvx6gD56OAA0nFCANZ4TgD12swA7si1AOq7pADkp4oA89TGAOCefQDiooQA0G09ANZ+VADYimAA3pZzAOrBrADtvacA//39AP359wDOZTMA/fXxANBnNwD78+wA0Gs7ANR1SAD34tgA9d7SANiCWAD12MoA3IxlANyOZwDuxrMA3ph3AOq/qgDquaIA6LWeAOazmgDvyLcA78q1AO/IswDswa0A4Jx7AOKkhgDOZTEA0Gc1APvv6ADQazkA+ejeANJxRADUdkoA1HhMANZ6TgDWflAA89jKANZ8UgDWflIA89bIANyMYwDuxrUA6LWcAOKgggDms5gA4qKCAOaxkgDkp4gA5q2SAOzBqwDrwKoA1HZIANR3SgDgnn4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQESP0IoPSABAQEBAQEBATljLgoKCk47AyABAQEBARBVCkxFAz42CgpcNwEBASMOCkwtJh5EMAoKCkEgAQFpCgpfTAoKCgoKCgoKCwFQOAoKOiclCgoKCgoKClk0SQoKCgoPBVMKCgoKCgo2JF4KNl5dZBoMAmUHKk8KCltHCkYfIUMFViIdNgZINgorSQoDPmAKOhkxEwoKMhY2UglPWlsYYh0cFzwvIV5oVjQBDQpNZhcpBAoISlgsCjMBAVBnCmEUVwoKCgoKCkAgAQEBFVQKVgoKCgoKCh8jAQEBAQEjX1EKCgoKJV41AQEBAQEBAQEbEURLJzUBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.bol.com/nl/s/algemeen/zoekresultaten/Ntt/{searchTerms}/Ntk/media_all/Nty/1/suggestedFor/{searchTerms}/N/0/Ne/0/search/true/searchType/qck/index.html"
+ resultdomain="bol.com">
+</Url>
+<SearchForm>https://www.bol.com/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/chambers-en-GB.xml b/comm/suite/components/search/searchplugins/chambers-en-GB.xml
new file mode 100644
index 0000000000..c0f4617a57
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/chambers-en-GB.xml
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Chambers (UK)</ShortName>
+<Description>Chambers 21st Century Dictionary Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAICAAAAEAIAAsAwAAJgAAABAQAAABACAAPwIAAFIDAACJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAALzSURBVFiF7ddNqBVlHMfxz5nqHpN8y7DyqjTYG2RwQWKCW5uiRRD2CkGbIl1IOxdBGFKgQRgkWC2iRUG1bBG0iUyCopgKJSyNskYjKiz0Kr1crzanxfMYc+fOOZ5z4tQi/6vhef7z/L4zz8v/9/B/j9agL+RJmuAiLMHF8fk3HMWvOJ6VxalKfgttzGRlUQ4NkCfpJbgFt+J6LMeCOPjJKD6Fb7APu7EHV2MLHs/K4ouBAfIknY97sRFrMYaf8DE+ww9YEaHWRrAEJ5Cjg5uxLiuLXfXxzz+L+DiexAOYH7/0ZezEl1lZnKzkjuHKmLsBl+K22N3BqiaNrgB5kq7E87gjftEMnsXTWVmcqOdnZTGD/XmSPoH38AwmYnerm1bSRXwxtlfE4U1sbxKvgfwZf/V6fF7purwvgLjKN+KeSv9RPJeVxVQv8RrIHjyGI7FpvC8ArIkAY5W2D/Bpv+KVeBuvoMSKPEnbPQHi1z9o9oLpYFdWFn8Mqp6VxWm8hINYink9AYQtdLvZ23Ma+wcVr8S3eEc4tBacDeAGrK61TeH7YdXj6fcCNuHnen99a9xo9twTtt/0sAAR4gAONPX9/Qfi/F/2T4SGieoUtLHyvwTodlq1ceG/AdDBqYacRUKxGTnANL5ryJmHa0YOkJVFB4caclq4KU/SC0YKEOMj/N6QN2nu+TASgL2a9+s47oz2aqQAv+AtoXjU8x425FrIk7SVJ+lknqRX9ASI6+B1fN0wzlXYHL3CoDEpOKm7egLEOIgd5q6FFu7HtjxJl/WjmifpWJ6k6/Ci4B3fqOc0zmk0ok/hEXNrw2m8i634JFqxpvcn8JBgaA9hQ1YWe/sCiIMsxOYIMaeMCk5nt2BWvoptqwQbPiE45CV4H5uaxHsCRIg27sOjglM6ryGtI7hlwrF9ZsxjeE3wkV3LeT/3gpZQpO4WfOJ1WNwFZgY/Cn/mVXxYte5DAdRgFgoH0rVCjViOZcKtqBAW2j4crl7PzkWv+AufXtBu3nNrKQAAAABJRU5ErkJggolQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAgZJREFUOI2Nk7FrU1EUxn/n3PdSFYOCowX7koY4FAdbkUKFig5OIojF1UVwFbcKxsH/QF3sII4RRJwEhSpoadJYHSpYrEYDIlSDUCvSJu8eh7ynaQziN9zpnu9+3+/eC10ykDI4/k/ye0mHBQxgkWhfSzkpWM6QQLB1RJcs3nhohIUMwbuDrHwxEO0drmp0eVNZwuyEiCya2GMPP73ZNTSzrCr3Nmnn+6ao6NDNuhasqtH13rzPKGbnJXr0WvNWIToCUAanZVCAqsud24W7sEr7g3qZBqgxGhpojdFwguXvmDu9jm+YkwLAmdR9jsHtFY1evdVhq2h0O3XvTlBjNOykzF2s6NCVdE/QYRAeAkY2MAwa/ZCP8aIFYF5nMrR2AkyBDwBCZ7nA1FnnPtpb4nWpA3tlDVhL2FkngbEtJSmwG+BuHwMBsz/kDRKAJnyOMTwGJgeSBL5flV4pQBDryxhrtjvW48/JjwjYLJNBTwUnYMnpAqAGOsb7Bsj9AYQQ2RGqTQMc5Um7DM6SgwTiOQb3LrjoPGmFq6m7d6Uf+HoGQbGz8xrdeEMxOwWxJHWqQe6Yk+COh28AJVBJoqmAXyAqesfMgMlEFqVJ/BXjqSFqYvsFAi9cGo/rD0qgJfDdn0kFvDEZ1NzHU97kOGbDCHswVk1sVnzr1mE+NdO9fxFNu/5LvS/0F8COyR9a0q7OAAAAAElFTkSuQmCC</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.chambers.co.uk/search/"
+ resultdomain="chambers.co.uk">
+ <Param name="query" value="{searchTerms}"/>
+ <Param name="title" value="21st"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.chambers.co.uk/search/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/cnrtl-tlfi-fr.xml b/comm/suite/components/search/searchplugins/cnrtl-tlfi-fr.xml
new file mode 100644
index 0000000000..0379fe0f9a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/cnrtl-tlfi-fr.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Portail Lexical - CNRTL</ShortName>
+<Description>Centre National de Ressources Textuelles et Lexicales</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAAAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgbUWgbUWfa0SZZECVXzyUXTuUXTuUXTuUXTuUXTuUXTuUXTuUXTuUXTuXYT6eakOgbUWfbESXYD2PVTiMUjiLUTeMUTeMUTeMUTeMUTeMUTeMUTeMUTeLUTePVTebZkGgbUWcaUPAmoTawbTZwbXZwbXZwbXZwbXZwbXZwbXZwbXZwbXZwbXPuKyaaE6aZUCgbUW8kXP639D94dL+4tP+4tP+4tP+4tP+4tP+4tP+4tP+4tP+4tPt08WhcFWbZ0KgbUXXs5z+49T+49T+5df+5tn+5tn+5tn+5tn+5tn+5tn+5tn+5tn74tSvgWGfbESgbUXat6D+49Ty1MS0h2qtflutf1utf1utf1utf1utf1utf1utf1utf1qjcUqgbUWgbUXat6H53tC3i3SXYT6gbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUXat6H12syrfmeXYT6gbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWhbkbat6H02suoe2SSWjmcZ0KdakOdakOdakOdakOdakOdakOdakOdakOea0SgbUWhbkbat6H12syrgW6JTC+OVDWQVzeQVzeQVzeQVzeQVzeQVzeQVzeQVzeUXTudaUOhbkbbuKH74NLXvrGhdmSZaVSaalWaalWaalWaalWaalV3TFNgOUtgOUt9STmaZUChbkbUsZn+5NX739Du08boz8Hoz8Hoz8HozsHozsHozsGFesdCQsxBQchtRVOaZkGgbUWzhmT85dj+5NX+49X+49X+49X+49X+49T+49T+49SPg9BDRNJCQ85yTFidaUOgbUWgbUW8knPnyrft0L/t0L/t0L/t0L/s0L/s0L/s0L+fjLpqYLtoXrqFXV6gbUWgbUWgbkagbUWgbUWhbkahbkahbkahbkagbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUWgbkagbUWgbUWhbkahbkahbkahbkagbUWgbUWgbUWgbUWgbUWgbUWgbUWgbUUAAH9fAADP/gAAz/4AAPr/AADC/gAAwP4AAE5eAAD//wAA//8AAMb+AAD//wAAwP4AAE5eAAD//wAA//8AAMz+</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://www.cnrtl.fr/utilities/OPEN">
+ <Param name="query" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.cnrtl.fr/lexicographie/{searchTerms}">
+</Url>
+<SearchForm>https://www.cnrtl.fr/lexicographie/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/drae.xml b/comm/suite/components/search/searchplugins/drae.xml
new file mode 100644
index 0000000000..504628fbf0
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/drae.xml
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Diccionario RAE</ShortName>
+<Description>Real Academia Española. Diccionario Usual.</Description>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAA////AP///wDP8eMAw83LAKyLlgCshYMAqpKMSIc/QsyeYl2PmVZPPZlpbgB1enQApru/AL/V2wD///8A////AP///wD///8Az/HjAMHJxgCzn6oAmlZUSIk5NrWjgoOhoWRfwH0jINKAKiefi315GMDc4gDL6fEA////AP///wD///8A////AOj//wDCvrkAjEdOTYkvMv2bYWCYoH+CP59wcUSrgX2Ahiwk/5xBRMGTkpIAfYuJAP///wD///8A////AP///wCOqqMAnpCMOZRFQ9WRW164p4KAfpRmYXaGNTWNnnNxa5dZWbKLIij/gVhVbzI0LQD///8A////AP///wD///8AZ0xMH3hGRoiiZ2eNoGNkrJdjYbqeb22Jk01Lo5xlY66bZGWinGBfx7KNjaF+bm1B////AP///wD///8A////ALB7fE+bVlqzhUJCgZRfYIOfZmipjlhXep58d0WbY2XDoXp6cJZWVG+EMDPTkTU6iP///wD///8A////AP///wClYGJjn2BmtotNS7SCRECnooeJTax9fIibV1OeoHl0ZZlobYKVT1Fvjk9Qs38vM4b///8A////AP///wD///8AS0g/CYA+NoqeR0XfmGhno66ZmG6woKFxiFZXsKmHhGujcXCNjT0+6pNUU7ikgnk4////AP///wD///8A////AKPHvwCdoZ8mhykw3Y9MTbW3o6BlmndyT3hRTlq1nJmJmm1xkZ5TVtWAQD+Td2BXAP///wD///8A////AP///wDg2+YAu6qtJIA5MraJOjXTkmBfY4lRVIiVV1eds5uYL49XV52FJCb3q25qY6V3eAH///8A////AP///wD///8AwtzIAKSjmiiDR0x7ml5fzX4iJuiXVFZynGtvaYUeIuGQPDzem1has4JWVEOeYWQA////AP///wD///8A////AM7/6wCgwLcAjFNRK8XEwwCffXyKhigozZRDSa11MSu9oWBfQJJ+fEBle3YAg316AP///wD///8A////AP///wDAz8cAuL65AMCyqwCpiIQLl2RkbY0+P9GHHBn9gkxKfIx3cQByfHQAYIiAAF+DewD///8A////AP///wD///8AmnN0AJ57egCikI4AlGRkrZE/Q+qWTEivi0BArJU2Pv6VbmuKi3p1AJF3cwCQd3MA////AP///wD///8A////AKmcngCpnJ4App6iALeemiqndW1jiU9Pm55ueZefXFlkv7OxItr6/wDT5u0A0+btAP///wD///8A////AP///wCxq7AAsauwAK6stADBrKcAsZWHAJyAg0ewpqVAtIN3ALmrqgDH9f8AxOTsAMTk7AD///8A////APw/AAD4DwAA8A8AAOAHAADAAwAAwAMAAMADAADAAwAA4AcAAOADAADgBwAA9A8AAPg/AAD4HwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wDO8OQAy+HSALmopQDC2vIAs6GoAJ9gXgClcGgAro2IAMTc2SeHRkqQjU5TiaiOkC6MRTxF08K4I2hWTQBTFR0Az5SYAGRdWQB+Zl0A0fP2AL/c3gC/3N4A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7w5ADL4dIAuailAMLa8gCzoagAn2BeAKd1bQCxkI0AqIKCi3EIBv+HQ0TinG5qg4g1LLm9hnyAZ1ZMAFMXHwDRlpsAZl9cAH5lXADR8/YAv9zeAL/c3gD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AzvDkAMvh0gC5qKUAwtnyALKhpwCgYWAAr4iBD5NUUG2JLibvk1RWxaRybZyTQj7RkE5O1H8MCv9mDQu+eTc+V8qanC5lYVoFfmVdANDz9gC/3N4Av9zeAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDO8OQAyuDQALutqwDG5P0AtKOpA5xcX0eYUE2eexES0Z12dpbI6e4RrJGPepZMSrqvko1UmG9wn5A4MOuRMirkkkNB2Gw+Om6ahHoA0P//ANb3+QDX+PoA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7w5ADM5dYAtZuWAMHP5gC2qK85jTs1x4MoK/GUQ0HRmGhmYaeUlzOSZnJelmdlQ65wYmmwiY1un398hYgnFtp2Dw//m09TtLKHhWS0x8wEjaKjAIqhogD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A0/frANP36gCugHUAlWJtAZVaX5p6AAD+mGFlm6J0dYaYUEqgn1ZWlZldZICOUFByll1dcJpcX6CWU0qipYCBfZRQT8SAGxfnoVBVqZNtaQYAAQAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQ9egAzevfCqh9eUF+JiOrfBwZ/513fJyXWGGZlltbnKyJf5aQcWwjhGJhUaFWT6xWVFcAooOKVZpiWaWfdnSPlkhHtHMICvmAFiHxkGBdlVJIQh1cU0oA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAHBgBfYWIeuIOLxpNJQtaMSkvQs724VpJVT6aPTU/Nu7e3X5JxdWBzLy2ilk1FwXszLZKea3J2qIyGdpVPR8N/HibFiEpTwoo4MtCXUlXgvaGeZ5GEeAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAGFTTjOhamWgnpGRLKVwcnKYVFWuhTk+36aAgI2phH9/o3Boo6Fuco6lcHJRfQUA/4g/Otqsi4dlpXt+loo0O9uje3aYn3FzoLORlIC0rrBDKScmHv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAuNzYAqnFumWkYGcJyODpZoVRXoZ1OUrKaXmCrnHV3kJVYVsWPQTO8rY6GYbCDh2qVTkqhjUpD4qR+hZGrjpBrjURBz5ZaVai1mJNloXB1hppoZLOtkItm////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJh6eR6ea3OSj0BBupxQTYWhbWlxll5bl49KT7KjcXKFlmlroXoaIfmRam1ehnx7MbCAel6bam+2nmputKp8dlucZF2ihjg41JFFRsuLNTbvjkZD0o5NVWn///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Au5OQQZ5gY5aRTk2gt4mJa3FBPz9vLTOmsI2Lfa+ZlVybYWmXnVBNoZuIhD+WoJ0ApFpXjpNWUdaPX1+1nXqAa6Jzc1+tfnhDgTM2a3oyOXOQOTm5fBkeiv///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCYU1NXjkVMxJRbZrOSYGivhD8/spA3NMqWZ2mBgU1Pe6+RkGS3mpNVoFNPqJxGQLCQR0OdsIqAWJVcYYSOdHucoHJ6bZtYVDyXXVptj1NfZ4Y7Od5uFB6a////AP///wD///8A////AP///wD///8A////AP///wD///8A////AK5ycV+cW1+gmGBfhY9VVZ+XXVe4jTMr6IFWV3ZzU1p1t5efVp9zb4eba2qyllhVw5hkZH6wj4k3oXRwdp1tZ2CcXmOjhyov345CQ7OfZGOPj1pXyH9JS33///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ASkdGFKyIhHeYPj6ofh0a2aSCf5OISEO9pnVtorSPi3+oiopss5+dXKiXoH+YYmqrmllQnJ18fX2rmJJ2t5CJU4M3Pc2TVVbLm11YnIxHR7GmfHmQr3htU////wD///8A////AP///wD///8A////AP///wD///8A////AP///wAfKCIAgGpbFJA/O6qCGBj/om1qp5ZkZ5+MU1SysqOeWqiLjJTI4+ARq4B/h41BSNmZZ1+jl2JguqJ7hnKph4hzj0hGyaF4cYyMNjXUhiAl+ZhqZpOcfnMN////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFJtXABab10CmW1qjKZdYrmLNTLJmFpgiIpESsOujo2HsI2Ne6R6doy0k4RMNTIzFnNnbEi1i4e3s5GMZJ1jZqGTXmGnpoCFdIk3Pem1bHCxRyEnSgAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A8P//AOz//wylrKooh1Zla30OEv9/KiLjlmZrhayGh4W2iX5WqYJ8WJNSVIhkJihskktGgr6spVqvoJ9/iDw+05NeYJ+nfoOGiCor4rV7d7JfQjg/DRMKAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDI1eAA09zoCItzdSN0MzGklkpO64c9NMKqeXGIgn56FoZWWIyzioxtc1lZK6Jub5a/kohiq5SQY7S2r0WRYGVmhTY0zYk9N89zBQbtnWFhiLeIghu/gX4A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AK6HiQCmfYI1p3NwZptoWGaWWFSxjzs5vZFLSruLYV5mjkxMhYs6PLKCVFSYjllgn5JMUZWMP0OMuKGaEKeIhEl9FxryhC0w1ZBNUZ+eWFdzrn+BUKpzdwX///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AyujVAMXk0gu1komOdERFUppsaVKKNDLsgS4264UoJe2IMDizizw+c6BkYZGZcG20hDEyq5BJUaqVSk6Jijw8wZJNSNeaYnCdllxblb91bapoTEc0BQ0IAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDQ/+cA4v//AIyWlw1uPkl/oV5lgZhpZ4+lhISkjkhHyHcLEv+HLC7DpGtvDbWenwyNQEGZhCUq93EAAP+ENDfwmV1bn6FeX3OucnGhWk1KUgkCAwAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM383wDk//kAdISFAGhgYQCsd3GBnWZncMbi6hayv78giDUxwIQpJPifc3BlpXaATIowO7OFKSX1qmxivplaVn2RRkuXgSklrJSMjUVFX2MAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Azv/iAOX//AB0hYYAYlJVALeZjAKlcWoIxMnEAMbX0AKOREPHdwkH9IczNdyVSlDWdAAA/3suLtx5e3cPkkM+AKNcYSGhnJUwd5KMBEVYWgAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDP/OEA4//3AIKVkwBza2sAwKufAMC6tQCvl4YAk05PVYg9Od2WUlR8iDk51YozMLGaTUKqdSAl5VdIQA5LPTYAp46PALi4swCOm5cATlRVAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AMixuADIsbgAybK5AMmzugDGr7gAwb69DJpuZWSRXGR0qYmFYpRTVpePT0rAjj4+t4wtLrKIKCnXmFBShZmekx9JW1sAHSAgAC8/PgAZJCYAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Aa05YAGpNVwBtUVsAa1BaAG1RXgCfgIJvkVNWt5NXWKyfYmSejTU3vo07N8eUTVC2iDAvwZhZW52JLzvetGln0lxFPyoQAAAAJQsIAC4SDQA9HhUAORsTAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAACQ8MAJBpYo2NNTLojkBEypRDQ9B3BgL5k0tDp5xzcYaCLTHnjkZTu5BLTraWZWC3wJuYVc2mogDIoZ0AyKGdAMihnQDIoZ0A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAGBQQAr6erI7yak2qtj4xpsJGQg5E5PK2niotkm2txa4koL7GfYGCcpWdkeqp/e2nE4Ocbz/j/AMzv/ADM7/wAzO/8AMzv/AD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAAAAAAAAAAAAAAAAAAAAAAUAAAC0usQA0MnFAMWijwKXgn8yhlZTcqBsb6yleoCbpI6WcLSXkz+3eGoMqHFqAMTf4gDR9P8Azez2AM3s9gDN7PYAzez2AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAABQEAALK1vgDJu7cA1KqXAHhhYABcbnAAh1NWlLCMjm686/oAxL+2ALV3aQCmbmkAw93hAM/y/gDM6vQAzOr0AMzq9ADM6vQA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAAAAAAAAAAAAAAAAAAFAQAAsrW+AMm8twDSqpcAa1JRAISuugDFs7VJq6aYMb7i4gDBt7EAtHZnAKZuagDD3eEAz/L+AMzq9ADM6vQAzOr0AMzq9AD///8A////AP///wD///8A////AP/4H///+B///+AB//+AAf//gAB//wAAf/wAQD/8AAA//AAAH/wAAB/4AAAf+ACAH/gAAB/4AAAf+AAAH/wAAB/8AAA//AAAP/wAAD/8AAAf/AAAP/4AAH//gAD//5AI///wD///wAf//8AD///AA///wAP///AP///+f////n//</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://dle.rae.es/"
+ resultdomain="dle.rae.es"
+ rel="searchform">
+ <Param name="w" value="{searchTerms}"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-cs-CZ.xml b/comm/suite/components/search/searchplugins/duckduckgo-cs-CZ.xml
new file mode 100644
index 0000000000..115c842201
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-cs-CZ.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo nabízí vyhledávání na webu s respektem k vašemu soukromí</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="cz-cs"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="cz-cs"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-de-DE.xml b/comm/suite/components/search/searchplugins/duckduckgo-de-DE.xml
new file mode 100644
index 0000000000..12fb8984a6
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-de-DE.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="de-de"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="de-de"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-el-GR.xml b/comm/suite/components/search/searchplugins/duckduckgo-el-GR.xml
new file mode 100644
index 0000000000..cf8b2bfc9d
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-el-GR.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="gr-el"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="gr-el"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-en-GB.xml b/comm/suite/components/search/searchplugins/duckduckgo-en-GB.xml
new file mode 100644
index 0000000000..fd7675f76a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-en-GB.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="uk-en"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="uk-en"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-en-US.xml b/comm/suite/components/search/searchplugins/duckduckgo-en-US.xml
new file mode 100644
index 0000000000..6248bee092
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-en-US.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="us-en"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="us-en"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-es-AR.xml b/comm/suite/components/search/searchplugins/duckduckgo-es-AR.xml
new file mode 100644
index 0000000000..b81bc494bf
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-es-AR.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="ar-es"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="ar-es"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-es-ES.xml b/comm/suite/components/search/searchplugins/duckduckgo-es-ES.xml
new file mode 100644
index 0000000000..1ad4d18187
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-es-ES.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="es-es"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="es-es"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-fi-FI.xml b/comm/suite/components/search/searchplugins/duckduckgo-fi-FI.xml
new file mode 100644
index 0000000000..13464cca94
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-fi-FI.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="fi-fi"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="fi-fi"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-fr-FR.xml b/comm/suite/components/search/searchplugins/duckduckgo-fr-FR.xml
new file mode 100644
index 0000000000..1246d22c7c
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-fr-FR.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="fr-fr"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="fr-fr"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-hu-HU.xml b/comm/suite/components/search/searchplugins/duckduckgo-hu-HU.xml
new file mode 100644
index 0000000000..f9687933c3
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-hu-HU.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="hu-hu"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="hu-hu"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-it-IT.xml b/comm/suite/components/search/searchplugins/duckduckgo-it-IT.xml
new file mode 100644
index 0000000000..c11e0535e6
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-it-IT.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="it-it"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="it-it"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-ja-JP.xml b/comm/suite/components/search/searchplugins/duckduckgo-ja-JP.xml
new file mode 100644
index 0000000000..efdd4ce471
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-ja-JP.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="jp-jp"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="jp-jp"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-nb-NO.xml b/comm/suite/components/search/searchplugins/duckduckgo-nb-NO.xml
new file mode 100644
index 0000000000..73178f134b
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-nb-NO.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="no-no"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="no-no"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-nl-NL.xml b/comm/suite/components/search/searchplugins/duckduckgo-nl-NL.xml
new file mode 100644
index 0000000000..d96adc43c9
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-nl-NL.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="nl-nl"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="nl-nl"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-pl-PL.xml b/comm/suite/components/search/searchplugins/duckduckgo-pl-PL.xml
new file mode 100644
index 0000000000..bb254d7093
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-pl-PL.xml
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>Wyszukiwarka DuckDuckGo</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="pl-pl"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kd" value="-1"/>
+ <Param name="kg" value="p"/>
+ <Param name="kl" value="pl-pl"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-pt-BR.xml b/comm/suite/components/search/searchplugins/duckduckgo-pt-BR.xml
new file mode 100644
index 0000000000..c59346f866
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-pt-BR.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="br-pt"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="br-pt"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-pt-PT.xml b/comm/suite/components/search/searchplugins/duckduckgo-pt-PT.xml
new file mode 100644
index 0000000000..ffef17a097
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-pt-PT.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="pt-pt"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="pt-pt"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-ru-RU.xml b/comm/suite/components/search/searchplugins/duckduckgo-ru-RU.xml
new file mode 100644
index 0000000000..ed09b6d693
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-ru-RU.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>ПоиÑк через DuckDuckGo</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="ru-ru"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="ru-ru"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-sk-SK.xml b/comm/suite/components/search/searchplugins/duckduckgo-sk-SK.xml
new file mode 100644
index 0000000000..1645037fd0
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-sk-SK.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="sk-sk"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="sk-sk"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-sv-SE.xml b/comm/suite/components/search/searchplugins/duckduckgo-sv-SE.xml
new file mode 100644
index 0000000000..39ad76cb1f
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-sv-SE.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="se-sv"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="se-sv"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-zh-CN.xml b/comm/suite/components/search/searchplugins/duckduckgo-zh-CN.xml
new file mode 100644
index 0000000000..4e5ce956da
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-zh-CN.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="cn-zh"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="cn-zh"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo-zh-TW.xml b/comm/suite/components/search/searchplugins/duckduckgo-zh-TW.xml
new file mode 100644
index 0000000000..f4ff8417fc
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo-zh-TW.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="kl" value="tw-tzh"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="kl" value="tw-tzh"/>
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/duckduckgo.xml b/comm/suite/components/search/searchplugins/duckduckgo.xml
new file mode 100644
index 0000000000..f06dd8d852
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/duckduckgo.xml
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>DuckDuckGo (Global)</ShortName>
+<Description>DuckDuckGo provides a privacy-aware search engine for the web</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/duckduckgo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ac.duckduckgo.com/ac/">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="type" value="list"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://duckduckgo.com/"
+ rel="searchform">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="t" value="seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay-de.xml b/comm/suite/components/search/searchplugins/ebay-de.xml
new file mode 100644
index 0000000000..330fc3fbff
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay-de.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay (de)</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/707-53477-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.de/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.de/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay-en-GB.xml b/comm/suite/components/search/searchplugins/ebay-en-GB.xml
new file mode 100644
index 0000000000..2c1a3869eb
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay-en-GB.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay (uk)</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/710-53481-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.co.uk/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.co.uk/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay-es.xml b/comm/suite/components/search/searchplugins/ebay-es.xml
new file mode 100644
index 0000000000..3ed7b9cd9a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay-es.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay (es)</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/1185-53479-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.es/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.es/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay-fr.xml b/comm/suite/components/search/searchplugins/ebay-fr.xml
new file mode 100644
index 0000000000..43035ee7fe
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay-fr.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay (fr)</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/709-53476-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.fr/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.fr/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay-it.xml b/comm/suite/components/search/searchplugins/ebay-it.xml
new file mode 100644
index 0000000000..b8a946f4ff
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay-it.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay (it)</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/724-53478-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.it/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.it/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay-nl.xml b/comm/suite/components/search/searchplugins/ebay-nl.xml
new file mode 100644
index 0000000000..8222538b76
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay-nl.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay (nl)</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/1346-53482-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.nl/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.nl/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/ebay.xml b/comm/suite/components/search/searchplugins/ebay.xml
new file mode 100644
index 0000000000..9aae3d2923
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/ebay.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>eBay</ShortName>
+<Description>eBay - Online auctions</Description>
+<Image width="16" height="16">resource://search-plugins/images/ebay.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://rover.ebay.com/rover/1/711-53200-19255-0/1"
+ resultdomain="ebay.com">
+ <Param name="ff3" value="4"/>
+ <Param name="toolid" value="20004"/>
+ <Param name="campid" value="5338192028"/>
+ <Param name="customid" value=""/>
+ <Param name="mpre" value="https://www.ebay.com/sch/{searchTerms}" />
+</Url>
+<SearchForm>https://www.ebay.com/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/google-jp.xml b/comm/suite/components/search/searchplugins/google-jp.xml
new file mode 100644
index 0000000000..29e5083514
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/google-jp.xml
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Google</ShortName>
+<Description>Google Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/google.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://www.google.com/complete/search">
+ <Param name="client" value="firefox"/>
+ <Param name="hl" value="=ja"/>
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.google.com/search">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="ie" value="utf-8"/>
+ <Param name="oe" value="utf-8"/>
+ <Param name="aq" value="t"/>
+ <Param name="hl" value="ja"/>
+ <MozParam name="client"
+ condition="defaultEngine"
+ trueValue="seamonkey-a"
+ falseValue="seamonkey"/>
+</Url>
+<SearchForm>https://www.google.co.jp/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/google.xml b/comm/suite/components/search/searchplugins/google.xml
new file mode 100644
index 0000000000..758b5ee482
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/google.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Google</ShortName>
+<Description>Google Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/google.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://www.google.com/complete/search">
+ <Param name="client" value="firefox"/>
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.google.com/search"
+ rel="searchform">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="ie" value="utf-8"/>
+ <Param name="oe" value="utf-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/heureka-cz.xml b/comm/suite/components/search/searchplugins/heureka-cz.xml
new file mode 100644
index 0000000000..1db516b89f
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/heureka-cz.xml
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Heureka</ShortName>
+<Description>Vyhledávání na Heureka.cz</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAAAAAAAAAA9fX13/X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX13/X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//769f/59/X/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/+/y9f8mjP7/lMP5//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/09fX//fn1/6XM+P97t/r/ir/6/9jm9v8ahf7/AHb//7fV+P/19fX/9fX1//X19f/19fX/9fX1//X19f/09fX/0eL2/w1///8AdP//NZP9/wJ5//8AdP//A3n//7HT+P/39vX/9fX1//X19f/19fX/9fX1//X19f/19fX/9PT1/w5///8wkf3/8vP1//Dz9f///vT/gbr6/wBy///I3vf/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/5fF+f8Ad///+ff1/8rf9/8Ab///nMj4///79f85lf3/TZ/8//r49f/19fX/9fX1//X19f/19fX/9fX1//X19f98t/r/N5T9//n39f8Nfv//HIb+/wR6///y9PX/g7v6/xOC////+/T/9fX1//X19f/19fX/9fX1//X19f/19fX/grr6/xqF/v/y9PX/Xqj7//359f8bhv7/W6b8/3Cx+v8pjf3//vr1//X19f/19fX/9fX1//X19f/19fX/9fX1/9fm9v8AcP//mcb5//759f/09fX/wdv3/wBz//8Lfv//g7v6//b19f/19fX/9fX1//X19f/19fX/9fX1//X19f/59/X/eLX6/wBw//9Ypfz/nsn5/3W0+v8Ge///LY/9//r49f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//z59f+s0Pj/MpH9/xOC//8giP7/f7n6//r49f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//359f//+/T///r1//b29f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX13/X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX13wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAA9fX1gPX19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19YD19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/5vH+f9Amf3/bbD7//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f+bx/n/BHr//wR6//8Eev//9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/m8f5/wR6//8Eev//BHr//xOC/v/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/X5vb/bbD7/zGR/f8Eev//BHr//wR6//9Amf3/m8f5/4y/+f8Eev//BHr//wR6//8Tgv7/yN73//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/bbD7/wR6//8Eev//BHr//wR6//8Eev//BHr//wR6//8Eev//BHr//wR6//8Eev//E4L+/8je9//19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/zGR/f8Eev//BHr//wR6//8iif7/Xqj7/324+v9Amf3/BHr//wR6//8Eev//BHr//xOC/v/I3vf/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f9tsPv/BHr//wR6//8Tgv7/m8f5//X19f/19fX/9fX1//X19f/m7fb/bbD7/wR6//8Eev//BHr//8je9//19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/udb4/wR6//8Eev//E4L+/8je9//19fX/9fX1/9fm9v+51vj/9fX1//X19f/19fX/m8f5/wR6//8Eev//QJn9//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f9eqPv/BHr//wR6//+51vj/9fX1//X19f/X5vb/E4L+/wR6//99uPr/9fX1//X19f/19fX/QJn9/wR6//8Eev//yN73//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/xOC/v8Eev//MZH9//X19f/19fX/9fX1/zGR/f8Eev//BHr//xOC/v/X5vb/9fX1//X19f+51vj/BHr//wR6//99uPr/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/BHr//wR6//9tsPv/9fX1//X19f9tsPv/BHr//wR6//8Eev//BHr//0+g/P/19fX/9fX1//X19f8Eev//BHr//0CZ/f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f8Eev//BHr//324+v/19fX/udb4/wR6//8Eev//MZH9/0+g/P8Eev//BHr//6rP+P/19fX/9fX1/xOC/v8Eev//QJn9//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/wR6//8Eev//bbD7//X19f+bx/n/BHr//xOC/v/I3vf/1+b2/xOC/v8Eev//Ion+/+bt9v/19fX/BHr//wR6//9PoPz/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/Ion+/wR6//8iif7/9fX1//X19f/I3vf/1+b2//X19f/19fX/jL/5/wR6//8Eev//bbD7/6rP+P8Eev//BHr//324+v/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f9tsPv/BHr//wR6//+Mv/n/9fX1//X19f/19fX/9fX1//X19f/19fX/MZH9/wR6//8Eev//MZH9/wR6//8Eev//1+b2//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/9fm9v8Eev//BHr//xOC/v+51vj/9fX1//X19f/19fX/9fX1//X19f+51vj/BHr//wR6//8Eev//BHr//0CZ/f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/4y/+f8Eev//BHr//wR6//99uPr/5u32//X19f/19fX/9fX1/7nW+P8iif7/BHr//wR6//8Tgv7/1+b2//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/16o+/8Eev//BHr//wR6//8Eev//MZH9/0CZ/f8Tgv7/BHr//wR6//8Eev//E4L+/8je9//19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/6rP+P8Tgv7/BHr//wR6//8Eev//BHr//wR6//8Eev//BHr//0CZ/f/X5vb/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1/+bt9v+bx/n/T6D8/0CZ/f9Amf3/QJn9/22w+//I3vf/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fWA9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1//X19f/19fX/9fX1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://www.heureka.cz/direct/firefox/autocompleter.php">
+ <Param name="query" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.heureka.cz/"
+ resultdomain="heureka.cz">
+ <Param name="h[fraze]" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.heureka.cz/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/hoepli.xml b/comm/suite/components/search/searchplugins/hoepli.xml
new file mode 100644
index 0000000000..3d26499bc4
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/hoepli.xml
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Hoepli</ShortName>
+<Description>Dizionario della lingua italiana Hoepli</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAEZJREFUeNpi/P//PwMpgAVCMSYehDD+z7dHlsYUZ2IgEQxCDSxofLgvB4+TcMUDLZ2kQKqGBxQ6iZHU1EqyDQAAAAD//wMApAcRQrj9oIAAAAAASUVORK5CYII=</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.grandidizionari.it/Dizionario_Italiano/cerca.aspx"
+ resultdomain="hoepli.it">
+ <Param name="idD" value="1"/>
+ <Param name="utm_source" value="mozilla-firefox"/>
+ <Param name="query" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.grandidizionari.it/Dizionario_Italiano.aspx?idD=1</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/huuto-fi.xml b/comm/suite/components/search/searchplugins/huuto-fi.xml
new file mode 100644
index 0000000000..c20966b761
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/huuto-fi.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Huuto.net</ShortName>
+<Description>Hakukone Huuto.nettiin, suomalaiseen nettihuutokauppaan.</Description>
+<InputEncoding>ISO-8859-1</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQABMTEwAfHx8AISEhACkpKQAyMjIAPj4+AElJSQBVVVUAWVlZAGFhYQBubm4AfHx8AICAgACJiYkAmZmZAKSkpACsrKwAtbW1AL+/vwDExMQAy8vLANPT0wDc3NwA4uLiAO7u7gDw8PAA/v7+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAABkAAAAAAAAAAAAAAAAZEgwJDRUAAAAAAAAAAAAAFQYAAAkREhYAAAAAAAAAGA0AAAUPEhQXFQAAAAAAABMEAAQKDxIUGxcXAAAAABcKAAMKDQ8XGhscEBUAABkQAQcIDRARGRwaGhkLFgAXBwcKDRATFRcaFxESDw8YDQYKDREUGBsYEBANCRIIEwcKDREUGRscGwwDBgYJCAsKDBEVFxscHBwXAwABAgkJDREVGRscHBwcHBMDAQILDxMYGxscHBwbGhocEgYDDhcaGxsbHBsaGRkYFxcVExccHBoZGBYVFxgZABkZAAAAExMWGBgYGQAAAAAAAAAAAP7////wP///8A///+AH///gA///wAH//4AA//+AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//ACf//wH///8=</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.huuto.net/fi/showlist.php3">
+ <Param name="tits" value="{searchTerms}"/>
+ <Param name="status" value="N"/>
+ <Param name="sellstyle" value="k"/>
+ <Param name="order" value="R"/>
+ <Param name="cat" value="%25"/>
+ <Param name="lcat" value="X"/>
+ <Param name="start" value="0"/>
+ <Param name="num" value="50"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+<SearchForm>https://www.huuto.net/fi/search_index.php3</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/images/amazon.ico b/comm/suite/components/search/searchplugins/images/amazon.ico
new file mode 100644
index 0000000000..1c39eaf8fe
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/amazon.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/images/duckduckgo.ico b/comm/suite/components/search/searchplugins/images/duckduckgo.ico
new file mode 100644
index 0000000000..dda80dfd88
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/duckduckgo.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/images/ebay.ico b/comm/suite/components/search/searchplugins/images/ebay.ico
new file mode 100644
index 0000000000..3af7a36484
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/ebay.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/images/google.ico b/comm/suite/components/search/searchplugins/images/google.ico
new file mode 100644
index 0000000000..82339b3b1d
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/google.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/images/startpage.ico b/comm/suite/components/search/searchplugins/images/startpage.ico
new file mode 100644
index 0000000000..19991e7478
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/startpage.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/images/wikipedia.ico b/comm/suite/components/search/searchplugins/images/wikipedia.ico
new file mode 100644
index 0000000000..4314071e24
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/wikipedia.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/images/yahoo.ico b/comm/suite/components/search/searchplugins/images/yahoo.ico
new file mode 100644
index 0000000000..9bd1d9f7c0
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/images/yahoo.ico
Binary files differ
diff --git a/comm/suite/components/search/searchplugins/list.json b/comm/suite/components/search/searchplugins/list.json
new file mode 100644
index 0000000000..f08601f7de
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/list.json
@@ -0,0 +1,217 @@
+{
+ "default": {
+ "searchDefault": "DuckDuckGo",
+ "searchOrder": ["DuckDuckGo", "Startpage", "Google", "Yahoo"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "google", "startpage", "wikipedia", "yahoo"
+ ]
+ },
+ "regionOverrides": {},
+ "locales": {
+ "en-US": {
+ "default": {
+ "visibleDefaultEngines": [
+ "amazon", "duckduckgo", "duckduckgo-en-US", "ebay", "google", "startpage", "wikipedia", "yahoo"
+ ]
+ }
+ },
+ "cs": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "Seznam", "DuckDuckGo"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-cs-CZ", "google", "heureka-cz", "mapy-cz", "seznam-cz", "startpage", "wikipedia-cz"
+ ]
+ }
+ },
+ "de": {
+ "default": {
+ "visibleDefaultEngines": [
+ "amazon-de", "duckduckgo", "duckduckgo-de-DE", "ebay-de", "google", "startpage", "wikipedia-de", "yahoo-de"
+ ]
+ }
+ },
+ "el": {
+ "default": {
+ "searchOrder": ["DuckDuckGo", "Startpage", "Google"],
+ "visibleDefaultEngines": [
+ "amazon-en-GB", "duckduckgo", "duckduckgo-el-GR", "google", "startpage", "wikipedia-el"
+ ]
+ }
+ },
+ "en-GB": {
+ "default": {
+ "searchOrder": ["DuckDuckGo", "Startpage", "Google", "Yahoo.co.uk"],
+ "visibleDefaultEngines": [
+ "amazon-en-GB", "chambers-en-GB", "duckduckgo", "duckduckgo-en-GB", "ebay-en-GB", "google", "startpage", "wikipedia", "yahoo-en-GB"
+ ]
+ }
+ },
+ "es-AR": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo Argentina"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-es-AR", "google", "startpage", "wikipedia-es", "yahoo-ar"
+ ]
+ }
+ },
+ "es-ES": {
+ "default": {
+ "visibleDefaultEngines": [
+ "amazon-es", "drae", "duckduckgo", "duckduckgo-es-ES", "ebay-es", "google", "startpage", "wikipedia-es", "yahoo-es"
+ ]
+ }
+ },
+ "fi": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-fi-FI", "google", "huuto-fi", "startpage", "wikipedia-fi", "yahoo-fi"
+ ]
+ }
+ },
+ "fr": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Startpage", "Yahoo"],
+ "visibleDefaultEngines": [
+ "amazon-fr", "cnrtl-tlfi-fr", "duckduckgo", "duckduckgo-fr-FR", "ebay-fr", "google", "startpage", "wikipedia-fr", "yahoo-fr"
+ ]
+ }
+ },
+ "hu": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-hu-HU", "google", "startpage", "vatera", "wikipedia-hu"
+ ]
+ }
+ },
+ "it": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo"],
+ "visibleDefaultEngines": [
+ "amazon-it", "bing", "duckduckgo", "duckduckgo-it-IT", "ebay-it", "google", "hoepli", "startpage", "wikipedia-it", "yahoo-it"
+ ]
+ }
+ },
+ "ja-JP-macos": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo! JAPAN"],
+ "visibleDefaultEngines": [
+ "amazon-jp", "duckduckgo", "duckduckgo-ja-JP", "google-jp", "startpage", "wikipedia-ja", "yahoo-jp"
+ ]
+ }
+ },
+ "ja": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo! JAPAN"],
+ "visibleDefaultEngines": [
+ "amazon-jp", "duckduckgo", "duckduckgo-ja-JP", "google-jp", "startpage", "wikipedia-ja", "yahoo-jp"
+ ]
+ }
+ },
+ "ka": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo (Global)"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "google", "startpage", "wikipedia-ka"
+ ]
+ }
+ },
+ "nb-NO": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Startpage", "Yahoo"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-nb-NO", "google", "startpage", "wikipedia-NO", "yahoo-NO"
+ ]
+ }
+ },
+ "nl": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Startpage", "Yahoo"],
+ "visibleDefaultEngines": [
+ "bolcom-nl", "duckduckgo", "duckduckgo-nl-NL", "ebay-nl", "google", "marktplaats-nl", "startpage", "wikipedia-nl", "yahoo-nl"
+ ]
+ }
+ },
+ "pl": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "Startpage", "DuckDuckGo"],
+ "visibleDefaultEngines": [
+ "allegro-pl", "duckduckgo", "duckduckgo-pl-PL", "google", "pwn-pl", "startpage-pl", "wikipedia-pl", "wolnelektury-pl"
+ ]
+ }
+ },
+ "pt-BR": {
+ "default": {
+ "searchDefault": "Google",
+ "visibleDefaultEngines": [
+ "amazon-br", "bing", "duckduckgo", "duckduckgo-pt-BR", "google", "startpage", "yahoo-br", "wikipedia-pt"
+ ]
+ }
+ },
+ "pt-PT": {
+ "default": {
+ "searchOrder": ["DuckDuckGo", "Startpage", "Google", "SAPO", "Priberam", "Wikipedia (pt)"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-pt-PT", "google", "priberam", "sapo", "startpage", "wikipedia-pt"
+ ]
+ }
+ },
+ "ru": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-ru-RU", "google", "startpage", "wikipedia-ru"
+ ]
+ }
+ },
+ "sk": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "Azet", "DuckDuckGo"],
+ "visibleDefaultEngines": [
+ "atlas-sk", "azet-sk", "duckduckgo", "duckduckgo-sk-SK", "google", "startpage", "wikipedia-sk", "zoznam-sk"
+ ]
+ }
+ },
+ "sv-SE": {
+ "default": {
+ "searchOrder": ["DuckDuckGo", "Startpage", "Google", "Bing"],
+ "visibleDefaultEngines": [
+ "bing", "duckduckgo", "duckduckgo-sv-SE", "google", "prisjakt-sv-SE", "startpage", "tyda-sv-SE", "wikipedia-sv-SE", "yahoo-sv-SE"
+ ]
+ }
+ },
+ "zh-CN": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo!"],
+ "visibleDefaultEngines": [
+ "amazon-zh-CN", "duckduckgo", "duckduckgo-zh-CN", "google", "startpage", "wikipedia-zh-CN", "yahoo-zh-CN"
+ ]
+ }
+ },
+ "zh-TW": {
+ "default": {
+ "searchDefault": "Google",
+ "searchOrder": ["Google", "DuckDuckGo", "Yahoo!"],
+ "visibleDefaultEngines": [
+ "duckduckgo", "duckduckgo-zh-TW", "google", "startpage", "wikipedia-zh-TW", "yahoo-bid-zh-TW", "yahoo-zh-TW"
+ ]
+ }
+ }
+ }
+}
diff --git a/comm/suite/components/search/searchplugins/mapy-cz.xml b/comm/suite/components/search/searchplugins/mapy-cz.xml
new file mode 100644
index 0000000000..7d2fb59615
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/mapy-cz.xml
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Mapy.cz</ShortName>
+<Description>Vyhledávání na Mapy.cz</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAICAAAAEAIACvBAAAJgAAABAQAAABACAAPwIAANUEAACJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAR2SURBVFiFrZdbbFRVFIa/OZ1pO+20ldILLa3FtJpgSbmJLQRiFE3KRZMqidYHfTEYCMTwYo0PxCcDvkCMEmM0UR/EBy9EoEGUEIlGIKXBYqPSYrlVpJUO08tM22nn92HOnOlczplBWMlK9tn73/9aZ52191nLtftUK1mKD9gEPA4sAx4ASsy1ADAAnAdOAkeB8WxI3VlgGoAOoB0otMFUmNoMvApMAAeBvUC/E7khCRvNl7RHUq+kVyQVOmCTtdDc0ytpr8mVFmvYOFYP/GK+eW4WUbKTXOB1k6shHcAtKXluBdAJVN6F4WRZBvwEbAS65y4kR6ABOHaPjcek0uROiMTcCHiBL4HydLu3LO5gaeUT+EM3+OaPfQwEfv0/TpQDXwEtQAjAAGHqW6Clc54TdEn5Oly4KPVW075kNwXukrS4LLTJtAUIQwKJeold5jitTs4Erdco8BTx2KIXbbESNFe30Vq/DeRKt75LokGKR+ANkMfJ84hmE2K5qmqDbRQWz1/D5ge3sbb2WVbXtKXDeEybGJKKJLVnOtu5OfkJDuTm5LO6ti0FV+17iOcb38TlcgHwaPVmO84XJBUZkjZmc8l4jLyUjGpZ+DRuI8/ClOYv5OWlb+PJiWPLCmucLquNBmh9psTxeUrJMVJv7QJPMY9UbQDEfO9Ctq7chy/3vgSMC5cT93pDoskpmSQo89ZYhJLoGTplPa+7fwsV3kVsXbmf4rz5KU5GFHHibjIk1WcKf1Vx/O4YDl7l29/fJTw7DcA8bxU7Wz60jE9MBzh68QMLPzUTdOKuN0DFmT5Bbclii3BwrI+x6WF+vva1Nec2PAD4Q//w/tntDI7+aa1Nzow7cRcbUoRM2jBvuUV4xf8bUoQfBz5nenbSmr8a6OW9M9v5N3gVFzlzHJhw5DYkjTqFf0FhA0V5pRbhZf8FJDE+PcIPlz4D4Mz1Ixw4u4PRqaGUIxt1wJZ/1A26BJSlZI8pjZVrrXEoPMaN8Ytm+ODkwKecvnaI0EwgaVf8Dxv/BGnlkltSD9FKJq0sX/CkNb4wdIrZSOKNGAzfTtkzYyYoQMiMgI30GBIn7I5JTdHDVPjqLHTX9e8cj2tMQ+F4OTgZDjphT7hBnURruJR6b01dmzW+OX6Zv0a6cAinJbeCg3EH7D9BEOg0JI1J+iI5QbzuElZUP2Whv+//xLxUMteEgakh/h7tA+CKv9cOd1DSWKwg2QO8BHhiBptrn8FtRMvBvlvn6B48hrJ4+5gcOL2TCl8dA/7z6ZbDRCtm3GZ4+oF9RAvIqAM1m+i/1c25weN0DR5GRLI2DjARHmHAP2K3vB/oA3C9dnhVbNILnAaa7sjSnUsPsJpoDiSUZCHQc6DhTFfzXeiwaSMYm3NLCaHtB1q592U5wE2iZXlCp5SuMekG1hIN1b2SHpOzO3nBrjXrl9Qs6R1J4WyOno2GTY5mkzMFY9eaAUwSbc0agY8wkyZLCQEfm3s7TK604tpxaFm2pD5gM4nteaz+uk1ie36ELNvz/wAQuz6qTTbcEAAAAABJRU5ErkJggolQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAgZJREFUOI2Nk01I1FEUxX/z/I9OVJKKEZmCFX0YmGSUSFARYxa4Mlq06gukUISE3ERtA1sVJrUQqm3UQhd9uDIiCowIlQgiZiwKRXIxmc0097T5P/s7zKILB+5977z7zuHdF7s63k5B7ADOAEeBWiAGpIHnwD1gMkoOJPm8DLgOdANBQdMKYDdwCRgE+oBctEEpMAIkC+UUhAN6gO1AB5B1kpA0ICkZ5lzcM0jf/gesDirxawVok3RDEk5Sg6TuKGFVvJx1ifUkN59ecfBA7UlaN3X6+oKkBidZl2ROMjx8NG04wtp4FZLRWH2Y9i3nOb61y/MCyc4Gkh2KmowRY01pBaY8JbGA1rpOUj/ec2LX5WVO5JI2Z6Z6M+FRWVZD4OK8/TYGQEtNB6carzG/+JXZTAqACL/OSfmslMejvrIJgPTCFPffXaG0JEF6YZqhNz1kcguhgn/8QLIZoMpr2lndAsCn+QnmFlMMT/Tzcf41eeWKWUg7M417SQlXzraqfQB8z3wmb8bU7Aty+Sxm4lcuU2jhmZPsrmQmGc01xwhcnIeTA0RfxWPu58yyAsn+SDbsJJuSbFAymjcmeZl+zKv0o6INRj/cov/JQV/fkWw61juy14/y6H+Mso+xcJSX/ABlJeuQ7GYoraiC0OrtkLsk2Yrf+BvoBYaAc6GaunDvC/CUIt/5L8Hdoep4zHF5AAAAAElFTkSuQmCC</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.mapy.cz/"
+ resultdomain="mapy.cz">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="sourceid" value="Searchmodule_3"/>
+</Url>
+<SearchForm>https://www.mapy.cz/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/marktplaats-nl.xml b/comm/suite/components/search/searchplugins/marktplaats-nl.xml
new file mode 100644
index 0000000000..16ef62c52a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/marktplaats-nl.xml
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Marktplaats.nl</ShortName>
+<Description>Zoeken in alle categorieën op Marktplaats.nl</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABC0lEQVR42rSSoU7DUBSGv42FNKHiBtMrEE2KwnRysoIwP4UZ6SOQ9AW2B5hFN+BRCDZVSQKi9WtSeVFUVCwEgehdb0p2xQTHnfz/d3POf+6gyhYcU0OOrBGQpBSV7lcxod9zFBVJChBIZhOGrWk61rKq/z75mAFIwfKW6Xg/0s0eeH6j2Rm3qnXrCaTo7yAFUlAqSmWAp6zX9gBP4AmAh1cjr3PuIntKrVYq1rl2S8GZYwcCSSABPmuATc484lLaAdfRcptMUZn0rIebTXAdgOvFAfcBoJsKmEd2YKtodjryVWyC7k7ZqSf3cZSkvHzw1fC+5eqCcxcg9Al9kpRNzvePVk9HDP79t/4OALMxUpBz00CfAAAAAElFTkSuQmCC</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.marktplaats.nl/z.html"
+ resultdomain="marktplaats.nl">
+ <Param name="query" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.marktplaats.nl</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/priberam.xml b/comm/suite/components/search/searchplugins/priberam.xml
new file mode 100644
index 0000000000..edc2922690
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/priberam.xml
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Priberam</ShortName>
+<Description>Dicionário Priberam</Description>
+<InputEncoding>ISO-8859-15</InputEncoding>
+<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAAHnlligAAAABGdBTUEAANbY1E9YMgAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKoSURBVHjaYvz7+dv3r98Y/n7+7u/jCxBAjH8/fmVgYGBat23T1+/fAQKI8e+X7wz//xeVlykqKjB8fP4qJj4GqHzR3LkAAQRV9+zlCylxCSYGJkYgunXnNohx78qNv5++fXn9Dkgy/Prw5YVwLQsLyy2hSoAAYgRaATTt4ZPHX799+fTh06Mnj4P9g5iZmZgYGBmASExEZPfevdHJCbdu3548c+rd+3eZGMCAk5Mz7b/Zg4cP33x8X5Cd+2vHbcaf7z4dOXbUwdaOARUABBDUXUDw79+/L9++MTIx8nJxA53FwsDI+Ofvn2vXr/Fy85y7fOnvnz9hQSEga4Ee+vPpK5D8+eGzqpLy8wePbpy7CPQ4C8g4Rsblq1ZIiEncvnd36vTpvLw8xaqFDECPyUhJA0kgkpeTB5K/P3wBeQ5oCAjdexuiYsvEzARkvz11++f91yzfvn27dv36rvDepcXtC7gvGttarbAq+asuzbh/yw4zU1MODg5UXzACBBg45P/9h4uAfPPly+9fv779+M7EzHzt5g0JMXFdLW2oNBMjOKjAwfjh44cLFy98+faVh49XWFRk3qIF2iaGl65cPnv+7NUb1yBqQFoYgKaDkQC/gIG+AR8P77nz58urq46fPPX/37/TZ86EBoW8ffcut6jg/qOHIB1AJ0E8ePX0ufrK6o3r1v/6+BkYdNaWlnp6ekDG7UtXwoNDLp89B1IGdD9Iw6dvpw4e2bBi9a8PnyEBdvPqVSkpKaBxU/snQkSg6Mt3aAKB+/hj+a6fM/dfZfslvDu+rreN+R9Dfnbu5YuXzk/a/PvZ28qwPMbbV65NmTFDVETk04cPMioKroY2CtxibLICDGzMwGA4cPSwvZWNoIAgw6+/fx99YFYUZPz39cd/pGDFD4CJBwBLx4gNwqkZawAAAABJRU5ErkJggg==</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.priberam.pt/dlpo/firefox.aspx">
+ <Param name="pal" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.priberam.pt/dlpo/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/prisjakt-sv-SE.xml b/comm/suite/components/search/searchplugins/prisjakt-sv-SE.xml
new file mode 100644
index 0000000000..ef9a339196
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/prisjakt-sv-SE.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Prisjakt</ShortName>
+<Description>Prisjakt - jämför priser och produkter</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAAAAABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKieMACommAAqJaMAKyauAC0prwBMSLsAZF%2FFAH16zQBwa9EAi4nTALu55QCxrvYA7Ov4AP%2F%2F%2FwBwADIAaQBjAG8AAADAHiQAfO8SAEwAAAAoJYAAjAAAAOS%2F9QBxGuYAjAAAAJgFAgAA8BIAGAAAAHDvEgDI7xIA4xq%2BAIwAAACYBQIAAPASABgAAAAAAAAA2T7GABg%2FxgDkCQUAVAAAAGDyEgChUcYA5AkFAFQAAABg8hIAAAAAAMzyEgBghgcAHjvnAPc65wDg8hIAWAcXAKzvEgCw7xIAgP4SAAlI6QBYMOgA%2F%2F%2F%2FAB475wAbrQEAYIYHAODyEgBYBxcABACkAAAApAD%2F%2F%2F8AsAgAAAAAQAAEAKQAZAAAAGIAbQBwADIAaQBjAG8ALgBlAHgAZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAAAAAAAAAAAAAAACAAAAXPESALgLpADoC8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AvCAAAAAAAAAAAAAAAAAP%2F%2F%2FwAAAAAAAAAAAAAAAAAAAAAAX6bnAAAAAAAAAAAAAAAAAAAAAAAoLxQAAAAAAAkOAgACDgIADQAAAADw%2FQAA4P0AAg4CAAkOAgAAAAAA9gvCAODyEgBSAAAAAAAAACTVpAAAAAAA%2F%2F%2F%2FAFzxEgBq8RIAXPESAMzx5wAEwPUARPESAAAAFACoRPkARQAAAHgTFAAAABQAoCAUABzxEgAg8RIAZPMSAPCI%2BgBSAAAAAAAAAJDWpAAAAAAAOor1AAAAAAAA7P0AAAAAAAAAAABwADIAaQBjAG8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDO8YApTbGAM0JAQAPAIUAAAAAAJDWpADNCQEAAQAAAAAAAAAAAAAAFTbGAM0JAQAAAAEAwU1BAM0JAQAAAwAABMD1AFik5wB0AAAAAAAAAOjyEgB0AAAAAAAAAAAAAAD%2F%2F%2F8AAAAAAAAAAAAAAAAAAAAAAP%2F%2F%2FwAAAAAAAAAAAAAAAAAAAAAAKC8UAAAAAABkxfUAqfHnAIwAAAAAAAAAAAAAAAAAAAB88hIAAADdAAADAAAAAAAAyfHnAAADAAAAAN0AjAAAAAAAAAAAAwAAAQAYAAAAAABw8hIAAAAAAKqb9QCzm%2FUA4PUSACQAAgAA7P0AEW5AAAUAAAAkAAIAAPD9AJzyEgACAAAAQKP1AJACAgD5m%2FUA4En8ACOj9QAro%2FUAAAAAAAgCAACgIBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAAAAAAAAAAAAACwgGFQYICwAAAAAAAAANCgUEBAEEBAUKDQAAAAAACgQEBwwCBAQEBAoAAAAACwUEBAQOAwQEBAQFCwAAAAgEBAwODg4OCQQEBAgAAAAGBAQEBAQEBA4EBAQGAAAVFQECAwwODg4MAwIBFRUAAAYEBAQOBAMEBAQEBAYAAAAIBAQECQ4ODg4MBAQIAAAACwUEBAQEAw4EBAQFCwAAAAAKBAQEBAIMBwQECgAAAAAADQoFBAQBBAQFCg0AAAAAAAAACwgGFQYICwAAAAAAAAAAAAAAABUAAAAAAAAAAP%2F%2FRgD%2B%2FwAA8B8AAMAHAADABwAAgAMAFYADAACAAwAAAAEAAIADBhWAAwsAgAMAAMAHDQrABwQB8B8FCv7%2FAAA%3D</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://www.prisjakt.nu/plugins/opensearch/suggestions.php">
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://www.prisjakt.nu/supersearch.php"
+ resultdomain="prisjakt.nu">
+ <Param name="s" value="{searchTerms}"/>
+ <Param name="r" value="1"/>
+ <Param name="e" value="utf8"/>
+ <Param name="ref" value="155"/>
+</Url>
+<SearchForm>https://www.prisjakt.nu/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/pwn-pl.xml b/comm/suite/components/search/searchplugins/pwn-pl.xml
new file mode 100644
index 0000000000..b1b2ff6ff6
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/pwn-pl.xml
@@ -0,0 +1,14 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Encyklopedia PWN</ShortName>
+<Description>Wyszukiwanie w Encyklopedii PWN</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAI9UExURQAAAB9YryBWrgmU3SBWrh9YryBWriBWriBWri0zkwmU3Q2L1hCBzg2L1hCBzhR3xiNOqB9YsBxitydFoRhsvxR3xhxitxhsvyNOqB9YsCdEoCo7mi01lSs6mSBWriBWri00lCBWrgqT3CBWriBWrgmU3Ss6mQqR2g2L1iNOqCdEoC0zkyBWriBWridEoCBXryBWris6mSBWri0zlA2K1RCBzh9YsB9YryNOqC0zlCBWrhR3xhCBzhxitxhsvyBWriBWrh9YsBR3xhxitxhsvyBWri0zkyBWriBWriBWriwykx9YryBWriwykyBWriBWriBWri0zkyBXryBWrh9YryBWriBWriBWrisykyBWriBWriBWriBWriBWriBWriBWrh9YryBWriBWriBWriBWriBWriBWriBWriBWriBWriBWriBWrgmU3Q2L1idEoBCBziJNpwWS3Cs6mW6UzBR2xiNOqB9YrxVrvhphtxCAzgyL1hd4xyFZsEWJzD96whl6yBSP1w1/zQiT3Ehrtg6AzguK1SVCn8XQ6B1Xr3eq2hJ1xmiQynmi1D2Z2F2q3hB0xSSX2l605TNbru31+93l8nmKw5XC5io6mBltv4W/5mi553uy3yhFoRdfthp6yODn87LO6svh8iZputTc7is6mFyFxTh0v4Kp1+rv+Ojv+EqNzTuCyLHN6Xyk1YK34ZLD55il0Z3A44an1mKLyBlgthZrvs/U6S08mtrg8DJAnLe/3jNBnGd6vCw7mTZEnjWei8gAAABsdFJOUwBNVQgHMwpZVGO9vQgIvQgICAgICL29vb29u4AO+w0uT3y1kwz+VRX4/VUhCZf9LR/9hWNK/f1gVWQyVVVVVV4gVf39/QhifaW+Y1OvZJK0lWRiMUKef3NlTZSZerKsFimuImocX3IVYkMpUosemJAAAAEISURBVBjTY2BQV1LNaSoszAEBLmYGBgYTjdzWCbMmNlTl5uZy8wIFbExbemeun9zXWJmfz8cDFLB26pq2bt7C2f311SWi/EABO5etGxesWrN6RmdtubgIUMDWecvylWuXzV3UU1chJgQUcDTrWLqieNPm7uKaMklBoICFZsGkxfN3TGlvLiiVEAAKaOnlzdmwa8+2qW15eVLCQAE1w6IlO3fv3bd9epGsNJDPoG1s6eblyxkRyMmpIAMS0Ddn9GZkDWFICGJlkGcBCrAbWXn6RIbGs0f5O3DIgQR0mdzDwuOSM5liAuxVQALKBq7BsWmJKRzp0R46QAEORQYGtoykVJZsliw/NgYAqJdNo4WkE2YAAAAASUVORK5CYII=</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://encyklopedia.pwn.pl/szukaj/{searchTerms}"/>
+<SearchForm>https://encyklopedia.pwn.pl/szukaj/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/sapo.xml b/comm/suite/components/search/searchplugins/sapo.xml
new file mode 100644
index 0000000000..a45e47b84a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/sapo.xml
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>SAPO</ShortName>
+<Description>Pesquisa SAPO</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAAAAABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXmHT/EnM8/wd6L/8EdRj/BHUY/wd6L/8Sczz/WJh0/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACt+UP8FaTL/B4I8/wmoTv8JO03/Ew62/xMOtv8JME7/CahO/weCPP8GajL/K35Q/wAAAAAAAAAAAAAAABJzPP8Hgjz/Cq5P/wm6Vv8BIwj/DAB8/xcG3v8ZB+b/DACA/wEUBf8JtVT/CahO/weCPP8Sczz/AAAAAECTZP8IkEP/Ca5Q/wquT/8CHA3/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AhwN/wmuUP8Krk//CJBD/0OUZv8IkEP/CahO/wquT/8FYCz/CJBD/wd3N/8DIA//AQwE/wEMBP8CHA3/BWAs/wiQQ/8FYCz/CahO/wquT/8HjkL/CahO/wSIPP8Hdzf/BqQ+/wakPv8GpD7/BqQ+/wakPv8GpD7/BqQ+/wakPv8GpD7/BqQ+/wN9N/8DfTf/CahO/1fAhv8Vm0//Ao82/wOmR/8Pok//BqQ+/wSeNP8Dpkf/A6ZH/wSeNP8GpD7/D6JP/wOmR/8Cjzb/FZtP/13Ejf8AAAAAa8eW/13Ejf8TqFP/AZgu/x2Zfv8xxdP/D6JP/w+iT/8xxdP/HZl+/wGXLP8Sp1L/aMiV/3fLof8AAAAAAAAAAAAAAACc1rv/u+rN/3G0nP8E8Pf/A/D3/xfNz/8Xzc//A/D3/wPw9/9ws5v/wezS/5zWu/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGQkH/BPD3/wAkJv8D8Pf/A/D3/wAsLv8D8Pf/RkJA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkJB/0vw9v8E8Pf/NrjA/zi1vf8D8Pf/SPH3/0ZCQP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGQkH/etfY/0ZCQf9GQkH/edbY/0ZCQP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEZCQf8AAAAAAAAAAEZCQf9+fHz/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////////wD///wAP//4AB//8AAP//AAD//wAA//8AAP//gAH//8AD///wD///8A////gf///9n////////w==</Image>
+<!-- Suggestions disabled as SSL is not available as at 23 Apr 2020
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://pesquisa.sapo.pt/livesapo">
+ <Param name="q" value="{searchTerms}"/>
+</Url> -->
+<Url type="text/html"
+ method="GET"
+ template="https://pesquisa.sapo.pt/FF2">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="enc" value="utf-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/seznam-cz.xml b/comm/suite/components/search/searchplugins/seznam-cz.xml
new file mode 100644
index 0000000000..e5c5bd27d7
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/seznam-cz.xml
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Seznam</ShortName>
+<Description>Vyhledávání na Seznam.cz</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAICAAAAEAIACABAAAJgAAABAQAAABACAAKQIAAKYEAACJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAARHSURBVFiFvZfLbxNXFMZ/M45DHk5I1SQFRB4QC6HGuAipSlrEojwk5HQH/AdlhVhkU7pg0QWLtpsmbLJou0KoFQqQhZtIoCgKEVG6CEJBNdA4dBPxcuLYJo7jseeeLjyObGfsDCHqJ92Ffc/9vm/OPXfmXE1EcAgP0Ad8BRwFDgC7rbk48C/wGJgA/gRWHbGKyFbDKyK/iMiqOMeqtca7FT9KqXKjRin1g1IqrZSSbY60UupHi8tWR1NK2SWmCxi2Ur0TeAxcAMKlE3YGjgGjwCc7JJ7HGyAAPCoyYJpm4W8vMA207LB4HhHgSwoyoRcURK2IDItIi4PCtB+RCDIygphmuZgWEbltaSEiVBW4+x74zPGzZLPIwgLMz8OzZ8iTJ2jPn4NSyOIi+qVL5Vb6La0rAFomk4Fc0T0F3JU05eVL1OgoMjMDT5+CYWzMaYVxuo7r5k00r7ccVQb4FAjnM/BdRXHDIDM4iHnrFihVJGZrVCnk4UPc5Q24Lc1vNMMwGoBXQL09m5Dq78ecnNxSGEAA6uqoHRrC5fdXCk0Ce6tEJFBWHDDu32d9YqKsuADoOnpnJy6fj6qjR6k+eRKtqYktXvP1QKAKOFUpKhuPY5imrYHqQIC6c+dw+3xodXWVaMrhlC4i/kpHq+bsWVRHB4ZpkikZWmcn7mPHoLZ2e8dWxK+lUqkI0FzJporHWb52jWQwWFztQFV7O57z5/H09VG1b9/7ZmBJS6VSaaDaSXRqdpbloSHWpqZAZMNMfqd3dXfjOX0az5kz7Dp0yAmloa2trTk2kEc6HGb5xg0SwSBmLLYpKwA1R47QfPEiuwMB0MqeH0NLJpNbbkE5SCZDYnKSlbExEuPjmPH4JjP1PT0cGBjAvWePHcWSlkwmZ4Ce7RgoMpPNkpieJjI8TDQYhGx2w0RNRwe+0VFcDQ2ly/7SRWRu2x+fws7G5aLxxAm6Bgfx3buH1ta2cXISL17w5s4du3VzuoiM74SBoqN78CD7+/tJmyaGNbLZrF3suE6u+Uh+6BaUIh4KbYgrj4eWQKA0ZA0Y1UXknYj8sVNPvx6J8PfVq/xz/ToZpWg4fpzPR0aobm0tjf1dRN5p8Xgccp1QiC0+x+WQSSR4PTbG4t27LE1O4m5qYm9fH/svXODjHtv6zgDdwHz+cxwGfga+dSKo0mmis7O8nZri7YMHxEMhGg8fprm3F+/lyzT39qK5XJUoBoB5AC0Wi+X/rAVmyHUsGxClWF1YIProEbFQCCMaxUylqGtro8nn4yO/H09XF5quO/EOMAd8Qa4GigxAQVOq0mkS4TCpV69wNzZS395OTWvr+wjZYVNTqq2srJQG/b9teTQatQv2Arcp2Y4PwBxwDpuLSbl8hsm9nn8iV7HbRcbi6LETr2QAYJ1c69wN/IpVNA6RAn6z1l6xuGyhLS8vOyX1AF9TfD1vsuZiFF/Pgzi8nv8HoFPlnCBLVLQAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAHwSURBVDiNjZI/S5tRFMZ/N2/y1pKi4Dewi3/SoSD6CYoamsExH8CCtJObKLo5CZ20tNBVUFxEKAixdRIcO9g6BTqoaDBGJEO8f87p4BtJ3rbSA89yznN+3Pvci6qS0oiqvlfVE1VtJfqZ9EbSfkSkrVhE1kQkiIj+Q0FEPorI0/YeIQRCCHEIoRJC0P/U12QHnHM459adc/qH6nX1lYqG+Xn1R0fp+ZpzDqy1BWutt9aqtVZto6FuaUn90JB6Y9SDOlA/Pq4Pnnt5a20hKyIzQERSWiphDg/RdiOKYHgYUy6DCB0VATNZEZnq7IZ6HQUUiObmyCwsQD5/D+wGAEyZZrN5B8QPJ6jV8IuLhO1tUCUzMUE0PU2mWMT096cB1tze3nYBHkBnZ7iNDfzODuH4GOKYXLnMk5UVTF9f29Y0Nzc3J8BQGtBZUq3SWl3Fbm6SnZzk2dZWe/QjIyJ7HZ/pr2JggHh5mTvAdc/2zdXV1QvgOx0v0RXq6Smt3V3swQG5sTHys7OY3l6AALw0tVoNYB14i/f4apVwcYFcXyONBqanh3h0lOzgYJr9AXhnLi8vSUL8oq3WK6IIk8s9FgnAN6AI2ExyFysirzWO1zWK5JE8REQ+iUgp2cGcn5+n6QXgDTAFPAcs8AvYBz4Dx53m36J46V9BLx/IAAAAAElFTkSuQmCC</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://suggest.seznam.cz/fulltext_ff">
+ <Param name="phrase" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://search.seznam.cz/"
+ resultdomain="seznam.cz"
+ rel="searchform">
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/startpage-pl.xml b/comm/suite/components/search/searchplugins/startpage-pl.xml
new file mode 100644
index 0000000000..c59e4cf512
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/startpage-pl.xml
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Startpage</ShortName>
+<Description>Prywatne wyszukiwanie za pomocÄ… Startpage.com</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/startpage.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.startpage.com/do/search"
+ resultDomain="startpage.com">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="segment" value="startpage.seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/startpage.xml b/comm/suite/components/search/searchplugins/startpage.xml
new file mode 100644
index 0000000000..52a33cf061
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/startpage.xml
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Startpage</ShortName>
+<Description>Private search with Startpage.com</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/startpage.ico</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.startpage.com/do/search"
+ resultDomain="startpage.com">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="segment" value="startpage.seamonkey"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/tyda-sv-SE.xml b/comm/suite/components/search/searchplugins/tyda-sv-SE.xml
new file mode 100644
index 0000000000..3c0ab2429c
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/tyda-sv-SE.xml
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Tyda.se</ShortName>
+<Description>Tyda.se, lexikon, ordlista och översättning.</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABQklEQVR42mO8fv3GxvVbdu/ay0AcYBYRkrhx4+aXL1+I1cDCxE68agYGBiY4y93Dtbe/Q1lFiVgN4uJi+gZ6PDw8xGrQN9BlYGBwc3eWkJQIDgmIi4+GiEtISsTFR+vp6WDYICHOwMCgoqLMzc2lpKwYlxANcV5gkF9cQjQvHy+6hl079jAwMEybOuvunXvr1m5kYGBwc3dhYGCwtrF88eLlo4eP0TUgg7t37t29c8/dw1VZRUlCQvzokeOfPn/Gp4GBgWHd2o08PNyNzbUMDAzr1236+OEjAQ1Hjxz78uWrhIT4xQuXXjx/gSWU0MCXL1+PHjnGwMCwaydKqmGBsxYtXLpo4VJkOQkJcbg2wjaIioroG+hBHIaiwcbWWkJSAlNDQlIsAwPDzu270WM6IipERUUZU4OEhPjv37+fI3kXAgDUBm6U4URcZQAAAABJRU5ErkJggg==</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://tyda.se/"
+ resultdomain="tyda.se">
+ <Param name="w" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://tyda.se/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/vatera.xml b/comm/suite/components/search/searchplugins/vatera.xml
new file mode 100644
index 0000000000..189e53ff33
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/vatera.xml
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Vatera</ShortName>
+<Description>Keresés a Vatera.hu piacterén</Description>
+<InputEncoding>ISO-8859-2</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wEAAAAAAGD/GABa/2MAWv9eAFv/XwBa/2AAWv8RAAAAAAAA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZv8FAAAAAABZ/4wAZv//AF7//wBe//8AZv//AFn/dQAAAAAAgP8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWv/VAFv//wBa//sAWv/6AF3//wBa/8MAAAAAAID/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAFX/AwAAAAAAXP8yAFv//wBb//8AWf/qAFr/7QBc//8AWv/6AF3/IQAAAAAAgP8CAAAAAAAAAAAAAAAAAAAAAACA/wQAAAAAAFr/fwBf//8AYP//AFn/iQBa/5sAX///AF///wBa/2kAAAAAAID/BAAAAAAAAAAAAAAAAAAAAAAAAP8BAAAAAABa/8sAXf//AF///wBb/zgAWv9PAGD//wBd//8AWv+4AAAAAABV/wMAAAAAAAAAAAAAAAAAgP8CAAAAAABZ/ygAWv/9AFz//wBa/+oAVf8GAFX/GABa//YAXP//AFr/8wBg/xgAAAAAAID/AgAAAAAAAAAAAID/BAAAAAAAWv9xAF///wBe//8AWv+oAAAAAAAAAAAAWv+8AF3//wBf//8AWf9cAAAAAABA/wQAAAAAAAAAAACA/wIAAAAAAFv/vwBd//8AXv//AFn/WQAAAAAAAAAAAFr/bwBf//8AXv//AFr/rAAAAAAAQP8EAAAAAACA/wIAAAAAAF7/HgBa//cAW///AFr/8wBZ/xcAAAAAAAAAAABc/ycAWv/9AFv//wBa/+wAYP8QAAAAAAAA/wEAQP8EAAAAAABZ/2QAX///AF3//wBa/7gAAAAAAFX/AwAA/wEAAAAAAFr/ywBd//8AXv//AFr/TwAAAAAAQP8EAFX/AwAAAAAAWv+vAFz//wBe//8AWf9qAAAAAABA/wQAgP8EAAAAAABa/4AAXv//AF3//wBa/5sAAAAAAID/BAAAAAAAWP8aAFr//wBh//8AXP//AFr/IgAAAAAAgP8CAFX/AwAAAAAAWv8zAF///wBh//8AW//yAE7/DQAAAAAAAAAAAFX/EgBa/1IAWf9TAFn/QgAAAAAAAAAAAAAAAAAAAAAAAAAAAID/AgBa/0cAWv9SAFn/UABV/wwAAAAAAAD/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AffvAADoFwAA6BcAAPgXAADQCwAA0AsAANALAACgBQAAoYUAAKGFAABBggAAQkIAAEJCAACCQQAAh8EAAH/+AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABV/wMAVf8DAFX/AwBV/wMAVf8DAFX/AwBV/wMAVf8DAFX/AwCA/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFX/AwAAAAAAW/9iAFr/xgBa/7sAWv+9AFr/vQBa/70AWv+9AFr/vABa/8MAWf9FAAAAAABV/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wEAAAAAAFX/BgBa/+oAYv//AF7//wBf//8AX///AF///wBf//8AXf//AGT//wBa/8QAAAAAAFX/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFX/AwAAAAAAWf9CAF3//wBa//gAWv/8AFr//ABa//sAWv/7AFr/+wBa//sAWv/9AFn/+ABa/x8AAAAAAID/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP8EAAAAAABa/5EAYP//AFr/+gBa//8AWv//AFr//gBa//8AWv//AFr//wBZ//sAYP//AFr/ZgAAAAAAQP8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AQAAAAAAgP8CAFr/2gBd//8AWv/9AFr//wBa//8AWv//AFr//wBa//8AWv//AFr/+wBf//8AWv+1AAAAAABV/wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVf8DAAAAAABd/zcAXP//AFr//QBa//8AWv//AFr//wBa//sAWv/8AFr//wBa//8AWv/+AFv//wBa//MAWf8XAAAAAAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA/wQAAAAAAFr/hQBg//8AWv/6AFr//wBa//sAYP//AFr/pwBa/8EAX///AFr//ABa//8AWf/7AGD//wBc/1kAAAAAAED/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AQAAAAAAWf/QAF3//wBa//wAWv//AFn/+wBh//8AWf9FAFv/cwBi//8AWv/6AFr//wBa//sAYP//AFv/qQAAAAAAQP8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA/wIAAAAAAFv/LQBb//8AWv/+AFr//wBa//4AW///AFr/9gBd/wsAWv8zAF3//wBa//0AWv//AFr//QBc//8AWv/rAFX/DwAAAAAAAP8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAID/BAAAAAAAWf94AGD//wBa//oAWv//AFr/+wBf//8AWv+4AAAAAABV/wYAWv/cAF3//wBa//0AWv//AFr/+wBf//8AWf9NAAAAAABA/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP8CAAAAAABZ/8UAXv//AFr//ABa//8AWv/6AGD//wBa/2kAAAAAAAAAAABb/5UAYP//AFr/+gBa//8AWf/7AGD//wBZ/50AAAAAAED/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAID/AgAAAAAAV/8jAFr/+wBa//8AWv/+AFr//gBa//8AWv/7AFf/IwAAAAAAAAAAAFr/RwBe//8AWv/8AFr//wBa//0AXP//AFr/4gBt/wcAAAAAAAD/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP8EAAAAAABZ/2oAYP//AFr/+gBa//8AWv/8AF7//wBa/8YAAAAAAFX/AwAAAAAAVf8MAFv/5wBc//8AWv/9AFr//wBa//wAXv//AFr/QQAAAAAAVf8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABV/wMAAAAAAFr/uQBf//8AWv/7AFr//wBa//oAYP//AFr/egAAAAAAgP8EAED/BAAAAAAAWf+mAGD//wBZ//sAWv//AFr/+gBg//8AWv+QAAAAAACA/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP8CAAAAAABc/xkAWv/1AFv//wBa//4AWv//AFr//gBb//8AWv8wAAAAAABV/wMAQP8EAAAAAABb/1cAX///AFr/+wBa//8AWv/9AF3//wBa/9gAAP8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA/wQAAAAAAFr/XQBg//8AWf/7AFr//wBa//wAXf//AFr/1AAAAAAAAAAAAAAAAAAA/wEAAAAAAF3/FgBa//IAW///AFr//gBa//8AWv/9AFz//wBa/zYAAAAAAFX/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAED/BAAAAAAAWv+tAF///wBa//sAWv//AFr/+gBg//8AWv+LAAAAAACA/wQAAAAAAAAAAABV/wMAAAAAAFr/tgBf//8AWv/7AFr//wBa//oAYP//AFr/ggAAAAAAgP8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wEAAAAAAFr/EQBa/+0AW///AFn//gBa//8AWv/9AF3//wBa/z4AAAAAAFX/AwAAAAAAAAAAAED/BAAAAAAAWf9nAGD//wBa//oAWv//AFr//ABe//8AWv/OAAAAAAAA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAED/BAAAAAAAWf9QAF///wBa//sAWv//AFr//QBc//8AWv/gAFX/BgAAAAAAAP8BAAAAAAAAAAAAgP8CAAAAAABd/yEAWf/7AFr//wBa//4AWv/+AFr//gBa//8AWf8rAAAAAACA/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQP8EAAAAAABa/58AYP//AFn/+wBa//8AWf/7AGD//wBa/5sAAAAAAED/BAAAAAAAAAAAAAAAAAAAAAAAgP8CAAAAAABa/8QAXv//AFr//ABa//8AWv/6AGD//wBZ/3UAAAAAAID/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AQAAAAAAVf8JAFn/5ABc//8AWv/9AFr//wBa//sAX///AFn/TQAAAAAAQP8EAAAAAAAAAAAAAAAAAAAAAACA/wQAAAAAAFn/eABg//8AWv/6AFr//wBa//wAXv//AFv/wgAAAAAAgP8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAVf8DAAAAAABa/0QAXv//AFr//ABa//8AWv/9AFv//wBa/+sAVf8PAAAAAAAA/wEAAAAAAAAAAAAAAAAAAAAAAID/AgAAAAAAWf8uAFv//wBa//4AWv//AFr//gBa//8AWv/6AFX/IQAAAAAAgP8CAAAAAAAAAAAAAAAAAAAAAACA/wQAAAAAAFn/kgBg//8AWv/6AFr//wBa//sAX///AFn/qwAAAAAAQP8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWv/SAF3//wBa//wAWv//AFr/+gBg//8AWf9nAAAAAABA/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWf/WAFv//wBa//kAWv/7AFr/9gBf//8AWv9dAAAAAABA/wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP8EAAAAAABZ/4kAX///AFr/9gBZ//sAWv/3AF7//wBa/7IAAAAAAFX/AwAAAAAAAAAAAAAAAABV/wMAAAAAAFn/QgBj//8AX///AGD//wBg//8AYf//AFv//wBc/xkAAAAAAID/AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABV/wMAAAAAAFn/PABi//8AYP//AGD//wBg//8AYf//AFz//wBe/x4AAAAAAID/AgAAAAAAAAAAAFX/AwAAAAAAWf88AFv/qQBZ/50AWv+fAFn/nQBa/6gAWf9wAAAAAACA/wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP8CAFn/hgBa/6QAWv+eAFr/nwBa/54AWv+kAFz/JAAAAAAAgP8CAAAAAAAAAAAAAAAAAAD/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAID/AgCA/wQAQP8EAED/BABA/wQAgP8EAFX/AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA/wQAQP8EAED/BABA/wQAQP8EAID/BAAA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////4Af//9/7//+gBf//QAX//0AC//9AAv/+gAL//oABf/6AAX/+gAF//QAAv/0BAL/9AYC/+gGAX/oCgF/6AkBf9AJAP/QHQC/0BaAv6AWgL+gFoBfoC9AX0AvQF9AL0AvQF/gL8BfoC6AX6AWgL/gF39//++A//Af/////</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.vatera.hu/listings/index.php">
+ <Param name="q" value="{searchTerms}"/>
+ <Param name="c" value="0"/>
+ <Param name="td" value="on"/>
+</Url>
+<SearchForm>https://www.vatera.hu/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-NO.xml b/comm/suite/components/search/searchplugins/wikipedia-NO.xml
new file mode 100644
index 0000000000..2449202447
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-NO.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (no)</ShortName>
+<Description>Wikipedia, den frie encyklopedi</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://no.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://no.wikipedia.org/wiki/Spesial:Søk"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-cz.xml b/comm/suite/components/search/searchplugins/wikipedia-cz.xml
new file mode 100644
index 0000000000..0c6672105a
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-cz.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedie (cs)</ShortName>
+<Description>Wikipedia, svobodná encyclopedie</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://cs.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://cs.wikipedia.org/wiki/Speciální:Hledání"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-de.xml b/comm/suite/components/search/searchplugins/wikipedia-de.xml
new file mode 100644
index 0000000000..d9e46f4a5c
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-de.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (de)</ShortName>
+<Description>Wikipedia, die freie Enzyklopädie</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://de.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://de.wikipedia.org/wiki/Spezial:Suche"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-el.xml b/comm/suite/components/search/searchplugins/wikipedia-el.xml
new file mode 100644
index 0000000000..1c73c65c53
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-el.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (el)</ShortName>
+<Description>Βικιπαίδεια, η ελεÏθεÏη εγκυκλοπαίδεια</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://el.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://el.wikipedia.org/wiki/Ειδικό:Αναζήτηση"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-es.xml b/comm/suite/components/search/searchplugins/wikipedia-es.xml
new file mode 100644
index 0000000000..dc1f798fd3
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-es.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (es)</ShortName>
+<Description>Wikipedia, la enciclopedia libre</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://es.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://es.wikipedia.org/wiki/Especial:Buscar"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-fi.xml b/comm/suite/components/search/searchplugins/wikipedia-fi.xml
new file mode 100644
index 0000000000..27ff04653d
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-fi.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (fi)</ShortName>
+<Description>Wikipedia (fi), vapaa tietosanakirja</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://fi.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://fi.wikipedia.org/wiki/Toiminnot:Haku"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search" />
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-fr.xml b/comm/suite/components/search/searchplugins/wikipedia-fr.xml
new file mode 100644
index 0000000000..8d999262bb
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-fr.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipédia (fr)</ShortName>
+<Description>Wikipédia, l'encyclopédie libre</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://fr.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://fr.wikipedia.org/wiki/Spécial:Recherche"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-hu.xml b/comm/suite/components/search/searchplugins/wikipedia-hu.xml
new file mode 100644
index 0000000000..d2888bfe7e
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-hu.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipédia (hu)</ShortName>
+<Description>Wikipedia, the free encyclopedia</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://hu.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://hu.wikipedia.org/wiki/Speciális:Keresés"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-it.xml b/comm/suite/components/search/searchplugins/wikipedia-it.xml
new file mode 100644
index 0000000000..47040f6f6d
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-it.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (it)</ShortName>
+<Description>Wikipedia, l'enciclopedia libera</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://it.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://it.wikipedia.org/wiki/Speciale:Ricerca"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-ja.xml b/comm/suite/components/search/searchplugins/wikipedia-ja.xml
new file mode 100644
index 0000000000..3f516ff96e
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-ja.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (ja)</ShortName>
+<Description>Wikipedia - フリー百科事典</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ja.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://ja.wikipedia.org/wiki/特別:検索"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-ka.xml b/comm/suite/components/search/searchplugins/wikipedia-ka.xml
new file mode 100644
index 0000000000..7d90efd508
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-ka.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>ვიკიპედირ(ka)</ShortName>
+<Description>ვიკიპედიáƒ, თáƒáƒ•áƒ˜áƒ¡áƒ£áƒ¤áƒáƒšáƒ˜ ენციკლáƒáƒžáƒ”დიáƒ</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ka.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://ka.wikipedia.org/wiki/სპეციáƒáƒšáƒ£áƒ áƒ˜:ძიებáƒ"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-nl.xml b/comm/suite/components/search/searchplugins/wikipedia-nl.xml
new file mode 100644
index 0000000000..0999f48b17
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-nl.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (nl)</ShortName>
+<Description>De vrije encyclopedie</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://nl.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://nl.wikipedia.org/wiki/Speciaal:Zoeken"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-pl.xml b/comm/suite/components/search/searchplugins/wikipedia-pl.xml
new file mode 100644
index 0000000000..3d4f26e5f9
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-pl.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (pl)</ShortName>
+<Description>Wikipedia, wolna encyklopedia</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://pl.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://pl.wikipedia.org/wiki/Specjalna:Szukaj"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-pt.xml b/comm/suite/components/search/searchplugins/wikipedia-pt.xml
new file mode 100644
index 0000000000..4745e920a8
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-pt.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (pt)</ShortName>
+<Description>Wikipédia, a enciclopédia livre</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://pt.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://pt.wikipedia.org/wiki/Especial:Pesquisar"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-ru.xml b/comm/suite/components/search/searchplugins/wikipedia-ru.xml
new file mode 100644
index 0000000000..4710887074
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-ru.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Ð’Ð¸ÐºÐ¸Ð¿ÐµÐ´Ð¸Ñ (ru)</ShortName>
+<Description>ВикипедиÑ, ÑÐ²Ð¾Ð±Ð¾Ð´Ð½Ð°Ñ ÑнциклопедиÑ</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ru.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://ru.wikipedia.org/wiki/СлужебнаÑ:ПоиÑк"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-sk.xml b/comm/suite/components/search/searchplugins/wikipedia-sk.xml
new file mode 100644
index 0000000000..3fc19e7f61
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-sk.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipédia (sk)</ShortName>
+<Description>Wikipédia, slobodná a otvorená encyklopédia</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://sk.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://sk.wikipedia.org/wiki/Špeciálne:Hľadanie"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-sv-SE.xml b/comm/suite/components/search/searchplugins/wikipedia-sv-SE.xml
new file mode 100644
index 0000000000..224e43242e
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-sv-SE.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (sv)</ShortName>
+<Description>Wikipedia, den fria encyklopedin</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://sv.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://sv.wikipedia.org/wiki/Special:Sök"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-zh-CN.xml b/comm/suite/components/search/searchplugins/wikipedia-zh-CN.xml
new file mode 100644
index 0000000000..a852639c18
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-zh-CN.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>维基百科</ShortName>
+<Description>维基百科,自由的百科全书</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://zh.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://zh.wikipedia.org/wiki/Special:æœç´¢"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia-zh-TW.xml b/comm/suite/components/search/searchplugins/wikipedia-zh-TW.xml
new file mode 100644
index 0000000000..57357e10df
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia-zh-TW.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (zh)</ShortName>
+<Description>維基百科,自由的百科全書</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://zh.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://zh.wikipedia.org/wiki/Special:æœç´¢"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+ <Param name="variant" value="zh-tw"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wikipedia.xml b/comm/suite/components/search/searchplugins/wikipedia.xml
new file mode 100644
index 0000000000..3daf9a2724
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wikipedia.xml
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wikipedia (en)</ShortName>
+<Description>Wikipedia, the Free Encyclopedia</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/wikipedia.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://en.wikipedia.org/w/api.php">
+ <Param name="action" value="opensearch"/>
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://en.wikipedia.org/wiki/Special:Search"
+ resultdomain="wikipedia.org"
+ rel="searchform">
+ <Param name="search" value="{searchTerms}"/>
+ <Param name="sourceid" value="Mozilla-search"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/wolnelektury-pl.xml b/comm/suite/components/search/searchplugins/wolnelektury-pl.xml
new file mode 100644
index 0000000000..b2d4e649a3
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/wolnelektury-pl.xml
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Wolne Lektury</ShortName>
+<Description>Biblioteka internetowa WolneLektury.pl</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image height="16" width="16" type="image/png">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAZlBMVEWauyuauiubuiubuyqauyqbvCrHuBfHuBfsvADrvgDsvgDsvQDrvgDsvQCbuyvsvgC4ABy8ABy+ABm+ABq/ABq/ABm+ABk9YW4+YW49YW49YnA9Y3E4Y3E4Y3G/ABo+Ym+/ABm4ABw5ZyQ5AAAAInRSTlPbyaSAWzYSADZbgKTJ2///EjZbf6TJ28+kgFs2EgD//4AA+LFVUAAAAFdJREFUeAGdzsEKgDAMA9CklzlwQy/+/zeKoCAuspVdvAx8hwYCpSXoIKNLqC7Elo8RjaxnL/i7KAxyqc1ChkgHuiWjOm3qfwyv5G9xjFa4SfMurbhV4QWhBzKL2hdUYQAAAABJRU5ErkJggg==</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://wolnelektury.pl/katalog/jtags/">
+ <Param name="mozhint" value="1"/>
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://wolnelektury.pl/szukaj/">
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://wolnelektury.pl</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-NO.xml b/comm/suite/components/search/searchplugins/yahoo-NO.xml
new file mode 100644
index 0000000000..fc2a299577
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-NO.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Søk</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://no.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://no.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-ar.xml b/comm/suite/components/search/searchplugins/yahoo-ar.xml
new file mode 100644
index 0000000000..e7729d1998
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-ar.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo Argentina</ShortName>
+<Description>Buscar en Yahoo Argentina</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://ar.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://ar.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-bid-zh-TW.xml b/comm/suite/components/search/searchplugins/yahoo-bid-zh-TW.xml
new file mode 100644
index 0000000000..b06761db8b
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-bid-zh-TW.xml
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo!奇摩æ‹è³£</ShortName>
+<Description>Yahoo!奇摩æ‹è³£</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/gif;base64,R0lGODlhEAAQAMQfAP+7Q4aWvf2QBaKtx//MS+6OLI1MMf/XYv+sKkpalEZNepyUa4KOo2ttjpx4Tdh1ALdrI9lSAHhaVWNSa+Z7Ge9zAPdrALVaIR4/ie9rAOZrELXB0Gtzc/9zCJg+Hf///yH5BAEAAB8ALAAAAAAQABAAAAWM4CeO3xAEA6mKw3IQDrOSAUEAiBPMpXsciIdk1XAUfkCBwKBiAG4IAED5UKg4iGxFWfFgNk0lF+KJWBqribgCwUgKlsRok0hMHvj2YNKJfzYMBxcXE4UTEQkbChlxDT9SBZEUGogBiwkSBFE4khoGcgEJAw0GEBSRBRQUF580CQawsbAJO64Kt7i0KiEAOw==</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://tw.search.bid.yahoo.com/search/ac"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-br.xml b/comm/suite/components/search/searchplugins/yahoo-br.xml
new file mode 100644
index 0000000000..7504947fb6
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-br.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Pesquisa Yahoo</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://br.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://br.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-de.xml b/comm/suite/components/search/searchplugins/yahoo-de.xml
new file mode 100644
index 0000000000..1ffafd0799
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-de.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Suche</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://de.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://de.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-en-GB.xml b/comm/suite/components/search/searchplugins/yahoo-en-GB.xml
new file mode 100644
index 0000000000..0e3f0a37e2
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-en-GB.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo.co.uk</ShortName>
+<Description>Yahoo UK &amp; Ireland Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://uk.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://uk.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-es.xml b/comm/suite/components/search/searchplugins/yahoo-es.xml
new file mode 100644
index 0000000000..721153e384
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-es.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Buscar</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://es.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://es.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-fi.xml b/comm/suite/components/search/searchplugins/yahoo-fi.xml
new file mode 100644
index 0000000000..739e6207fe
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-fi.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo-haku</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://fi.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://fi.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-fr.xml b/comm/suite/components/search/searchplugins/yahoo-fr.xml
new file mode 100644
index 0000000000..12da5c09f1
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-fr.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Recherche Yahoo</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://fr.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://fr.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-it.xml b/comm/suite/components/search/searchplugins/yahoo-it.xml
new file mode 100644
index 0000000000..950ca8a1f4
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-it.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://it.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://it.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-jp.xml b/comm/suite/components/search/searchplugins/yahoo-jp.xml
new file mode 100644
index 0000000000..0545a54949
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-jp.xml
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo! JAPAN</ShortName>
+<Description>Yahoo Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/24zAP//MwD//zMA//8zAP//MwD//wAAAAAAAAAAMwD/7TMA/+MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkI//8zAP//MwD/bwAAAAAAAAAAAAAAADEA/zQyAP8pNwD/FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5CP//MwD//zIA/3AAAAAAAAAAAAAAAAAAAAAAMgD/wDIA/6cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AP8sMwD/9zMA//8zAP//NwD/FwAAAAAAAAAAAAAAADQA/3EzAP//MQD/PgAAAAAAAAAAAAAAAAAAAAA3AP8OMwD/2DMA//80AP9PMwD/WjMA//8xAP8qAAAAAAAAAAAyAP8kMwD//zMA/9EAAAAIAAAAAAAAAAAAAAADMwD/tDMA//8yAP+JAAAAAAAAAAAxAP85MwD//zQA/1gAAAAAAAAAADMA/9UzAP//MwD/qQAAAAAAAAAAMgD/kzMA//8zAP/EAAAABAAAAAAAAAAAMwD//zMA//8zAP//MwD//zMA/30xAP8+MwD/+TEA/z4yAP+sMwD//zMA//8zAP//MwD//zMA/5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD//wAA//8AAP//AADgZwAA+OMAAPjzAADwcQAA4DAAAMMYAADDAAAAA/8AAP//AAD//wAA//8AAP//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//AAAAAAAAAAAAAAAAMQD/HzQA/2MyAP+iAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyAP+dMwD/9jMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA/3gAAAAAAAAAAAAAAAAzAP+/MwD//zMA//80AP9UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/JzQA/4ozAP//MwD//zMA//8zAP/nMwD/NzQA/ycxAP8VAAAABAAAAAAAAAAAAAAAADMA/1AzAP//MwD/zjMA/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA//8zAP//MwD//zMA/98AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAA/yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD//zMA//8zAP//MwD/3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUA/yIzAP9zMwD/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP//MwD//zMA//8zAP/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/njMA//8zAP/pMwD/DwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/TzMA//8zAP//MwD//zMA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP9QMwD//zMA//80AP+PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg0AP+UMwD//zMA//8zAP//MwD//jIA/1IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4A/wszAP/2MwD//zMA//sxAP8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD/lzMA//8zAP//MwD//zMA/84zAP/5MwD/+TIA/1EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/7MzAP//MwD//zQA/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADIA/1szAP//MwD//zMA//8zAP/fOQD/EjMA/0EzAP/tMwD//DQA/2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMgD/ZTMA//8zAP//MwD//zIA/1IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxAP8vMwD/9TMA//8zAP//MwD/+DEA/zQAAAAAAAAAADUA/yIzAP/UMwD//zMA/5YAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP8ZMwD//TMA//8zAP//MwD/3wAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALgD/FjMA/98zAP//MwD//zMA//8yAP9mAAAAAAAAAAAAAAAAAAAAADMA/w8zAP+4MwD//zMA/8MzAP8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP/IMwD//zMA//8zAP//MgD/fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzAP/DMwD//zMA//8zAP//MgD/ogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzAP+zMwD//zMA/+k0AP9JAAAAAAAAAAAAAAAAAAAAADMA/3kzAP//MwD//zMA//8zAP/2MAD/IAAAAAAAAAAAAAAAAAAAAAAAAAABMwD/pDMA//8zAP//MwD//zMA/9YuAP8LAAAAAAAAAAAAAAAAAAAAAAAAAAA1AP8/NAD/YjMA/5ozAP//MwD//zMA//8zAP+/MwD/gzIA/00wAP8QNQD/KzMA//8zAP//MwD//zMA//8zAP+zAAAAAAAAAAAAAAAAAAAAADMA/40zAP//MwD//zMA//8zAP/3MwD/LQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/98zAP/fMwD/3zMA/98zAP/fMwD/3zMA/98zAP/fMwD/3zMA/58AAAAAMwD/3TMA//8zAP/fMwD/kTEA/0MzAP88MgD/YDMA/4IzAP+6MwD//zMA//8zAP//MwD//zMA/7oyAP9HNAD/NjAA/yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AP9FMgD/LgAAAAAAAAAAAAAAADMA//YzAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////////////////////////////////+ABw//gAcP/+AHD//4fz//+H/H//h/w//wf8P/4D/B/+Af4f/AD+D/gYPgfwPB8H4H4PA8B8AAPA/ACAAD//nAA//////////////////////////////////////////////</Image>
+<Url type="text/html" method="GET"
+ template="https://search.yahoo.co.jp/search"
+ resultdomain="yahoo.co.jp"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+ <Param name="fr" value="mozff" />
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-nl.xml b/comm/suite/components/search/searchplugins/yahoo-nl.xml
new file mode 100644
index 0000000000..8d9323b744
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-nl.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Zoeken</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://nl.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://nl.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-sv-SE.xml b/comm/suite/components/search/searchplugins/yahoo-sv-SE.xml
new file mode 100644
index 0000000000..4646c3404c
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-sv-SE.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Sök</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://se.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://se.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-zh-CN.xml b/comm/suite/components/search/searchplugins/yahoo-zh-CN.xml
new file mode 100644
index 0000000000..99a6b6f220
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-zh-CN.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo!</ShortName>
+<Description>Yahoo!奇摩æœå°‹</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://zh.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://zh.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo-zh-TW.xml b/comm/suite/components/search/searchplugins/yahoo-zh-TW.xml
new file mode 100644
index 0000000000..8045260d8e
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo-zh-TW.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo!</ShortName>
+<Description>Yahoo!奇摩æœå°‹</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://tw.search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://tw.search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/yahoo.xml b/comm/suite/components/search/searchplugins/yahoo.xml
new file mode 100644
index 0000000000..4359b9c481
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/yahoo.xml
@@ -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/. -->
+
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Yahoo</ShortName>
+<Description>Yahoo Search</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16">resource://search-plugins/images/yahoo.ico</Image>
+<Url type="application/x-suggestions+json"
+ method="GET"
+ template="https://search.yahoo.com/sugg/ff">
+ <Param name="output" value="fxjson"/>
+ <Param name="appid" value="smd"/>
+ <Param name="command" value="{searchTerms}"/>
+</Url>
+<Url type="text/html"
+ method="GET"
+ template="https://search.yahoo.com/search"
+ resultdomain="yahoo.com"
+ rel="searchform">
+ <Param name="p" value="{searchTerms}"/>
+ <Param name="ei" value="UTF-8"/>
+</Url>
+</SearchPlugin>
diff --git a/comm/suite/components/search/searchplugins/zoznam-sk.xml b/comm/suite/components/search/searchplugins/zoznam-sk.xml
new file mode 100644
index 0000000000..37ac6b8678
--- /dev/null
+++ b/comm/suite/components/search/searchplugins/zoznam-sk.xml
@@ -0,0 +1,13 @@
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/" xmlns:os="http://a9.com/-/spec/opensearch/1.1/">
+<ShortName>Zoznam</ShortName>
+<Description>Zoznam slovenskeho internetu</Description>
+<InputEncoding>WINDOWS-1250</InputEncoding>
+<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAApUlEQVQ4jWNggIJ/DAz//zEw/D+tKv3/dqT3fwYiASNMMy4FZ1WlGRg4OBgEdDQYVJdvZcRpEswFhPBmTu7/p1Wl4RYyohtCrNMZGBgYmBgYGHE7CYsL0TVjuIBUzUQZgE8zQQMIacZrADGacRpArGasBpCiGcMAUjWjGECOZqg67ABXUl5ta/x/d2o0aoo9bKqNO+3rGv+/7Gr8/3BmNknJnGgAAJxPXsbphIDfAAAAAElFTkSuQmCC</Image>
+<Url type="text/html"
+ method="GET"
+ template="https://www.zoznam.sk/hladaj.fcgi">
+ <Param name="co" value="odkazy"/>
+ <Param name="s" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.zoznam.sk/</SearchForm>
+</SearchPlugin>
diff --git a/comm/suite/components/security/content/prefs/pref-certs.js b/comm/suite/components/security/content/prefs/pref-certs.js
new file mode 100644
index 0000000000..a630f0aa9d
--- /dev/null
+++ b/comm/suite/components/security/content/prefs/pref-certs.js
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function Startup()
+{
+ var securityOCSPEnabled = document.getElementById("security.OCSP.enabled");
+ DoEnabling(securityOCSPEnabled.value);
+}
+
+function DoEnabling(aOCSPPrefValue)
+{
+ EnableElementById("requireWorkingOCSP", aOCSPPrefValue != 0, false);
+}
+
+function OpenCertManager()
+{
+ document.documentElement
+ .openWindow("mozilla:certmanager",
+ "chrome://pippki/content/certManager.xul",
+ "", null);
+}
+
+function OpenDeviceManager()
+{
+ document.documentElement
+ .openWindow("mozilla:devicemanager",
+ "chrome://pippki/content/device_manager.xul",
+ "", null);
+}
diff --git a/comm/suite/components/security/content/prefs/pref-certs.xul b/comm/suite/components/security/content/prefs/pref-certs.xul
new file mode 100644
index 0000000000..3caac6499c
--- /dev/null
+++ b/comm/suite/components/security/content/prefs/pref-certs.xul
@@ -0,0 +1,100 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % prefCertsDTD SYSTEM "chrome://pippki/locale/pref-certs.dtd">
+ %prefCertsDTD;
+ <!ENTITY % prefSslDTD SYSTEM "chrome://pippki/locale/pref-ssl.dtd">
+ %prefSslDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="certs_pane"
+ label="&pref.certs.title;"
+ script="chrome://pippki/content/pref-certs.js">
+ <preferences id="cert_preferences">
+ <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"
+ onchange="DoEnabling(this.value);"/>
+ <preference id="security.OCSP.require"
+ name="security.OCSP.require"
+ type="bool"/>
+ </preferences>
+
+
+ <groupbox align="start">
+ <caption label="&SSLClientAuthMethod.caption;"/>
+ <description>&certselect.description;</description>
+ <radiogroup id="certSelection"
+ orient="horizontal"
+ preference="security.default_personal_cert"
+ aria-labelledby="CertGroupCaption CertSelectionDesc">
+ <radio value="Select Automatically"
+ label="&certselect.auto;"
+ accesskey="&certselect.auto.accesskey;"/>
+ <radio value="Ask Every Time"
+ label="&certselect.ask;"
+ accesskey="&certselect.ask.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <!-- Certificate manager -->
+ <groupbox>
+ <caption label="&managecerts.caption;"/>
+ <description>&managecerts.text;</description>
+ <hbox align="center">
+ <button label="&managecerts.button;"
+ oncommand="OpenCertManager();"
+ id="openCertManagerButton"
+ accesskey="&managecerts.accesskey;"
+ preference="security.disable_button.openCertManager"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Device manager -->
+ <groupbox>
+ <caption label="&managedevices.caption;"/>
+ <description>&managedevices.text;</description>
+ <hbox align="center">
+ <button label="&managedevices.button;"
+ oncommand="OpenDeviceManager();"
+ id="openDeviceManagerButton"
+ accesskey="&managedevices.accesskey;"
+ preference="security.disable_button.openDeviceManager"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Validation -->
+ <groupbox align="start">
+ <caption label="&validation.ocsp.caption;"/>
+ <checkbox id="enableOCSPBox"
+ label="&enableOCSP.label;"
+ accesskey="&enableOCSP.accesskey;"
+ onsynctopreference="return +this.checked;"
+ preference="security.OCSP.enabled"/>
+ <separator class="thin"/>
+ <checkbox id="requireWorkingOCSP"
+ label="&validation.requireOCSP.description;"
+ accesskey="&validation.requireOCSP.accesskey;"
+ preference="security.OCSP.require"/>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/security/content/prefs/pref-passwords.js b/comm/suite/components/security/content/prefs/pref-passwords.js
new file mode 100644
index 0000000000..f958a37055
--- /dev/null
+++ b/comm/suite/components/security/content/prefs/pref-passwords.js
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gInternalToken;
+
+function Startup() {
+ var tokendb = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB);
+ gInternalToken = tokendb.getInternalKeyToken();
+}
+
+function ChangePW()
+{
+ var p = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+ p.SetString(1, "");
+ window.openDialog("chrome://pippki/content/changepassword.xul", "",
+ "chrome,centerscreen,modal", p);
+}
+
+function ResetPW()
+{
+ var p = Cc["@mozilla.org/embedcomp/dialogparam;1"]
+ .createInstance(Ci.nsIDialogParamBlock);
+ p.SetString(1, gInternalToken.tokenName);
+ window.openDialog("chrome://pippki/content/resetpassword.xul", "",
+ "chrome,centerscreen,modal", p);
+}
diff --git a/comm/suite/components/security/content/prefs/pref-passwords.xul b/comm/suite/components/security/content/prefs/pref-passwords.xul
new file mode 100644
index 0000000000..af12060f0d
--- /dev/null
+++ b/comm/suite/components/security/content/prefs/pref-passwords.xul
@@ -0,0 +1,82 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % prefMast SYSTEM "chrome://pippki/locale/pref-masterpass.dtd">
+ %prefMast;
+ <!ENTITY % prefPass SYSTEM "chrome://pippki/locale/pref-passwords.dtd">
+ %prefPass;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="passwords_pane"
+ label="&pref.passwords.title;"
+ script="chrome://pippki/content/pref-passwords.js">
+
+ <preferences id="passwords_preferences">
+ <preference id="signon.rememberSignons"
+ name="signon.rememberSignons"
+ type="bool"/>
+ <preference id="pref.advanced.password.disable_button.view_stored_password"
+ name="pref.advanced.password.disable_button.view_stored_password"
+ type="bool"/>
+ <preference id="security.disable_button.changePassword"
+ name="security.disable_button.changePassword"
+ type="bool"/>
+ <preference id="security.disable_button.resetPassword"
+ name="security.disable_button.resetPassword"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&signonHeader.caption;"/>
+ <description>&signonDescription.label;</description>
+ <hbox>
+ <checkbox id="signonRememberSignons"
+ label="&signonEnabled.label;"
+ accesskey="&signonEnabled.accesskey;"
+ preference="signon.rememberSignons"/>
+ </hbox>
+ <hbox pack="end">
+ <button id="viewStoredPassword"
+ label="&viewSignons.label;"
+ accesskey="&viewSignons.accesskey;"
+ oncommand="toDataManager('|passwords');"
+ preference="pref.advanced.password.disable_button.view_stored_password"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Change Password -->
+ <groupbox>
+ <caption label="&changepassword.caption;"/>
+ <description>&changepassword.text;</description>
+ <hbox>
+ <button label="&changepassword.button;"
+ oncommand="ChangePW();"
+ id="changePasswordButton"
+ accesskey="&changepassword.accesskey;"
+ preference="security.disable_button.changePassword"/>
+ </hbox>
+ </groupbox>
+
+ <!-- Reset Password -->
+ <groupbox>
+ <caption label="&resetpassword.caption;"/>
+ <description>&resetpassword.text;</description>
+ <hbox>
+ <button label="&resetpassword2.button;"
+ oncommand="ResetPW();"
+ id="resetPasswordButton"
+ accesskey="&resetpassword2.accesskey;"
+ preference="security.disable_button.resetPassword"/>
+ </hbox>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/security/content/prefs/pref-ssl.js b/comm/suite/components/security/content/prefs/pref-ssl.js
new file mode 100644
index 0000000000..1e807f7402
--- /dev/null
+++ b/comm/suite/components/security/content/prefs/pref-ssl.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/. */
+
+function Startup()
+{
+ // map associating preference values with checkbox element IDs
+ gSslPrefElements = new Map([[1, "allowTLS10"],
+ [2, "allowTLS11"],
+ [3, "allowTLS12"],
+ [4, "allowTLS13"]]);
+
+ // initial setting of checkboxes based on preference values
+ UpdateSslBoxes();
+}
+
+function UpdateSslBoxes()
+{
+ // get minimum and maximum allowed protocol and locked status
+ let minVersion = document.getElementById("security.tls.version.min").value;
+ let maxVersion = document.getElementById("security.tls.version.max").value;
+ let minLocked = document.getElementById("security.tls.version.min").locked;
+ let maxLocked = document.getElementById("security.tls.version.max").locked;
+
+ // check if allowable limits are violated, use default values if they are
+ if (minVersion > maxVersion || !gSslPrefElements.has(minVersion)
+ || !gSslPrefElements.has(maxVersion))
+ {
+ minVersion = document.getElementById("security.tls.version.min").defaultValue;
+ maxVersion = document.getElementById("security.tls.version.max").defaultValue;
+ }
+
+ // set checked, disabled, and locked status for each protocol checkbox
+ for (let [version, id] of gSslPrefElements)
+ {
+ let currentBox = document.getElementById(id);
+ currentBox.checked = version >= minVersion && version <= maxVersion;
+
+ if ((minLocked && maxLocked) || (minLocked && version <= minVersion) ||
+ (maxLocked && version >= maxVersion))
+ {
+ // boxes subject to a preference's locked status are disabled and grayed
+ currentBox.removeAttribute("nogray");
+ currentBox.disabled = true;
+ }
+ else
+ {
+ // boxes which the user can't uncheck are disabled but not grayed
+ currentBox.setAttribute("nogray", "true");
+ currentBox.disabled = (version > minVersion && version < maxVersion) ||
+ (version == minVersion && version == maxVersion);
+ }
+ }
+}
+
+function UpdateSslPrefs()
+{
+ // this is called whenever a checkbox changes
+ let minVersion = -1;
+ let maxVersion = -1;
+
+ // find the first and last checkboxes which are now checked
+ for (let [version, id] of gSslPrefElements)
+ {
+ if (document.getElementById(id).checked)
+ {
+ if (minVersion < 0) // first box checked
+ minVersion = version;
+ maxVersion = version; // last box checked so far
+ }
+ }
+
+ // if minVersion is valid, then maxVersion is as well -> update prefs
+ if (minVersion >= 0)
+ {
+ document.getElementById("security.tls.version.min").value = minVersion;
+ document.getElementById("security.tls.version.max").value = maxVersion;
+ }
+
+ // update checkbox values and visibility based on prefs again
+ UpdateSslBoxes();
+}
diff --git a/comm/suite/components/security/content/prefs/pref-ssl.xul b/comm/suite/components/security/content/prefs/pref-ssl.xul
new file mode 100644
index 0000000000..8541c0f2a1
--- /dev/null
+++ b/comm/suite/components/security/content/prefs/pref-ssl.xul
@@ -0,0 +1,120 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % prefSslDTD SYSTEM "chrome://pippki/locale/pref-ssl.dtd">
+ %prefSslDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <prefpane id="ssl_pane"
+ label="&pref.ssltls.title;"
+ script="chrome://pippki/content/pref-ssl.js">
+ <preferences id="ssl_preferences">
+ <preference id="security.tls.version.min"
+ name="security.tls.version.min"
+ type="int"/>
+ <preference id="security.tls.version.max"
+ name="security.tls.version.max"
+ type="int"/>
+ <preference id="security.warn_entering_secure"
+ name="security.warn_entering_secure"
+ type="bool"/>
+ <preference id="security.warn_leaving_secure"
+ name="security.warn_leaving_secure"
+ type="bool"/>
+ <preference id="security.warn_submit_insecure"
+ name="security.warn_submit_insecure"
+ type="bool"/>
+ <preference id="security.warn_mixed_active_content"
+ name="security.warn_mixed_active_content"
+ type="bool"/>
+ <preference id="security.mixed_content.block_active_content"
+ name="security.mixed_content.block_active_content"
+ type="bool"/>
+ <preference id="security.warn_mixed_display_content"
+ name="security.warn_mixed_display_content"
+ type="bool"/>
+ <preference id="security.mixed_content.block_display_content"
+ name="security.mixed_content.block_display_content"
+ type="bool"/>
+ </preferences>
+
+ <groupbox align="start">
+ <caption label="&SSLTLSProtocolVersions.caption;"/>
+ <description>&limit.description;</description>
+
+ <hbox align="center">
+ <label id="allowEnable"
+ value="&limit.enable.label;"/>
+ <checkbox id="allowTLS10"
+ class="nogray-disabled"
+ label="&limit.tls10.label;"
+ accesskey="&limit.tls10.accesskey;"
+ oncommand="UpdateSslPrefs();"/>
+ <checkbox id="allowTLS11"
+ class="nogray-disabled"
+ label="&limit.tls11.label;"
+ accesskey="&limit.tls11.accesskey;"
+ oncommand="UpdateSslPrefs();"/>
+ <checkbox id="allowTLS12"
+ class="nogray-disabled"
+ label="&limit.tls12.label;"
+ accesskey="&limit.tls12.accesskey;"
+ oncommand="UpdateSslPrefs();"/>
+ <checkbox id="allowTLS13"
+ class="nogray-disabled"
+ label="&limit.tls13.label;"
+ accesskey="&limit.tls13.accesskey;"
+ oncommand="UpdateSslPrefs();"/>
+ </hbox>
+
+ </groupbox>
+
+ <groupbox align="start">
+ <caption label="&SSLTLSWarnings.caption;"/>
+ <description>&warn.description2;</description>
+ <checkbox id="warnEnteringSecure"
+ label="&warn.enteringsecure;"
+ accesskey="&warn.enteringsecure.accesskey;"
+ preference="security.warn_entering_secure"/>
+ <checkbox id="warnLeavingSecure"
+ label="&warn.leavingsecure;"
+ accesskey="&warn.leavingsecure.accesskey;"
+ preference="security.warn_leaving_secure"/>
+ <checkbox id="warnInsecurePost"
+ label="&warn.insecurepost;"
+ accesskey="&warn.insecurepost.accesskey;"
+ preference="security.warn_submit_insecure"/>
+ </groupbox>
+
+ <groupbox align="start">
+ <caption label="&SSLMixedContent.caption;"/>
+ <description>&mixed.description;</description>
+ <checkbox id="warnMixedActiveContent"
+ label="&warn.mixedactivecontent;"
+ accesskey="&warn.mixedactivecontent.accesskey;"
+ preference="security.warn_mixed_active_content"/>
+ <checkbox id="blockActiveContent"
+ label="&block.activecontent;"
+ accesskey="&block.activecontent.accesskey;"
+ preference="security.mixed_content.block_active_content"/>
+ <checkbox id="warnMixedDisplayContent"
+ label="&warn.mixeddisplaycontent;"
+ accesskey="&warn.mixeddisplaycontent.accesskey;"
+ preference="security.warn_mixed_display_content"/>
+ <checkbox id="blockDisplayContent"
+ label="&block.displaycontent;"
+ accesskey="&block.displaycontent.accesskey;"
+ preference="security.mixed_content.block_display_content"/>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/components/security/jar.mn b/comm/suite/components/security/jar.mn
new file mode 100644
index 0000000000..e98909e681
--- /dev/null
+++ b/comm/suite/components/security/jar.mn
@@ -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/.
+
+pippki.jar:
+ content/pippki/pref-certs.js (content/prefs/pref-certs.js)
+ content/pippki/pref-certs.xul (content/prefs/pref-certs.xul)
+ content/pippki/pref-passwords.js (content/prefs/pref-passwords.js)
+ content/pippki/pref-passwords.xul (content/prefs/pref-passwords.xul)
+ content/pippki/pref-ssl.js (content/prefs/pref-ssl.js)
+ content/pippki/pref-ssl.xul (content/prefs/pref-ssl.xul)
diff --git a/comm/suite/components/security/moz.build b/comm/suite/components/security/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/components/security/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/sessionstore/XPathGenerator.jsm b/comm/suite/components/sessionstore/XPathGenerator.jsm
new file mode 100644
index 0000000000..e202468a27
--- /dev/null
+++ b/comm/suite/components/sessionstore/XPathGenerator.jsm
@@ -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 EXPORTED_SYMBOLS = ["XPathGenerator"];
+
+var XPathGenerator = {
+ // these two hashes should be kept in sync
+ namespaceURIs: { "xhtml": "http://www.w3.org/1999/xhtml" },
+ namespacePrefixes: { "http://www.w3.org/1999/xhtml": "xhtml" },
+
+ /**
+ * Generates an approximate XPath query to an (X)HTML node
+ */
+ generate: function sss_xph_generate(aNode) {
+ // have we reached the document node already?
+ if (!aNode.parentNode)
+ return "";
+
+ // Access localName, namespaceURI just once per node since it's expensive.
+ let nNamespaceURI = aNode.namespaceURI;
+ let nLocalName = aNode.localName;
+
+ let prefix = this.namespacePrefixes[nNamespaceURI] || null;
+ let tag = (prefix ? prefix + ":" : "") + this.escapeName(nLocalName);
+
+ // stop once we've found a tag with an ID
+ if (aNode.id)
+ return "//" + tag + "[@id=" + this.quoteArgument(aNode.id) + "]";
+
+ // count the number of previous sibling nodes of the same tag
+ // (and possible also the same name)
+ let count = 0;
+ let nName = aNode.name || null;
+ for (let n = aNode; (n = n.previousSibling); )
+ if (n.localName == nLocalName && n.namespaceURI == nNamespaceURI &&
+ (!nName || n.name == nName))
+ count++;
+
+ // recurse until hitting either the document node or an ID'd node
+ return this.generate(aNode.parentNode) + "/" + tag +
+ (nName ? "[@name=" + this.quoteArgument(nName) + "]" : "") +
+ (count ? "[" + (count + 1) + "]" : "");
+ },
+
+ /**
+ * Resolves an XPath query generated by XPathGenerator.generate
+ */
+ resolve: function sss_xph_resolve(aDocument, aQuery) {
+ let xptype = aDocument.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
+ return aDocument.evaluate(aQuery, aDocument, this.resolveNS, xptype, null).singleNodeValue;
+ },
+
+ /**
+ * Namespace resolver for the above XPath resolver
+ */
+ resolveNS: function sss_xph_resolveNS(aPrefix) {
+ return XPathGenerator.namespaceURIs[aPrefix] || null;
+ },
+
+ /**
+ * @returns valid XPath for the given node (usually just the local name itself)
+ */
+ escapeName: function sss_xph_escapeName(aName) {
+ // we can't just use the node's local name, if it contains
+ // special characters (cf. bug 485482)
+ return /^\w+$/.test(aName) ? aName :
+ "*[local-name()=" + this.quoteArgument(aName) + "]";
+ },
+
+ /**
+ * @returns a properly quoted string to insert into an XPath query
+ */
+ quoteArgument: function sss_xph_quoteArgument(aArg) {
+ return !/'/.test(aArg) ? "'" + aArg + "'" :
+ !/"/.test(aArg) ? '"' + aArg + '"' :
+ "concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')";
+ },
+
+ /**
+ * @returns an XPath query to all savable form field nodes
+ */
+ get restorableFormNodes() {
+ // for a comprehensive list of all available <INPUT> types see
+ // http://mxr.mozilla.org/mozilla-central/search?string=kInputTypeTable
+ let ignoreTypes = ["password", "hidden", "button", "image", "submit", "reset"];
+ // XXXzeniko work-around until lower-case has been implemented (bug 398389)
+ let toLowerCase = '"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"';
+ let ignore = "not(translate(@type, " + toLowerCase + ")='" +
+ ignoreTypes.join("' or translate(@type, " + toLowerCase + ")='") + "')";
+ let formNodesXPath = "//textarea|//select|//xhtml:textarea|//xhtml:select|" +
+ "//input[" + ignore + "]|//xhtml:input[" + ignore + "]";
+
+ delete this.restorableFormNodes;
+ return (this.restorableFormNodes = formNodesXPath);
+ }
+};
diff --git a/comm/suite/components/sessionstore/content/aboutSessionRestore.js b/comm/suite/components/sessionstore/content/aboutSessionRestore.js
new file mode 100644
index 0000000000..677bf20adc
--- /dev/null
+++ b/comm/suite/components/sessionstore/content/aboutSessionRestore.js
@@ -0,0 +1,291 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gStateObject;
+var gTreeData;
+
+// Page initialization
+
+window.onload = function() {
+ // establish the event handlers for <tree> and <button> elements
+ var tabList = document.getElementById("tabList");
+ tabList.addEventListener("click", onListClick);
+ tabList.addEventListener("keydown", onListKeyDown);
+
+ document.getElementById("errorTryAgain")
+ .addEventListener("command", restoreSession);
+
+ document.getElementById("errorCancel")
+ .addEventListener("command", startNewSession);
+
+ // the crashed session state is kept inside a textbox so that SessionStore picks it up
+ // (for when the tab is closed or the session crashes right again)
+ var sessionData = document.getElementById("sessionData");
+ if (!sessionData.value) {
+ var ss = Cc["@mozilla.org/suite/sessionstartup;1"].getService(Ci.nsISessionStartup);
+ sessionData.value = ss.state;
+ if (!sessionData.value) {
+ document.getElementById("errorTryAgain").disabled = true;
+ return;
+ }
+ }
+ // make sure the data is tracked to be restored in case of a subsequent crash
+ sessionData.dispatchEvent(new UIEvent("input",
+ { bubbles: true, cancelable: true, view: window, detail: 0 }));
+
+ gStateObject = JSON.parse(sessionData.value);
+
+ initTreeView();
+
+ document.getElementById("errorTryAgain").focus();
+};
+
+function initTreeView() {
+ var tabList = document.getElementById("tabList");
+ var winLabel = tabList.getAttribute("_window_label");
+
+ gTreeData = [];
+ gStateObject.windows.forEach(function(aWinData, aIx) {
+ var winState = {
+ label: winLabel.replace("%S", (aIx + 1)),
+ open: true,
+ checked: true,
+ ix: aIx
+ };
+ winState.tabs = aWinData.tabs.map(function(aTabData) {
+ var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
+ var iconURL = aTabData.attributes && aTabData.attributes.image || null;
+ // don't initiate a connection just to fetch a favicon (see bug 462863)
+ if (/^https?:/.test(iconURL))
+ iconURL = "moz-anno:favicon:" + iconURL;
+ return {
+ label: entry.title || entry.url,
+ checked: true,
+ src: iconURL,
+ parent: winState
+ };
+ });
+ gTreeData.push(winState);
+ for (var tab of winState.tabs)
+ gTreeData.push(tab);
+ }, this);
+
+ tabList.view = treeView;
+ tabList.view.selection.select(0);
+}
+
+// User actions
+
+function restoreSession() {
+ document.getElementById("errorTryAgain").disabled = true;
+
+ // remove all unselected tabs from the state before restoring it
+ var ix = gStateObject.windows.length - 1;
+ for (var t = gTreeData.length - 1; t >= 0; t--) {
+ if (treeView.isContainer(t)) {
+ if (gTreeData[t].checked === 0)
+ // this window will be restored partially
+ gStateObject.windows[ix].tabs =
+ gStateObject.windows[ix].tabs.filter((aTabData, aIx) =>
+ gTreeData[t].tabs[aIx].checked);
+ else if (!gTreeData[t].checked)
+ // this window won't be restored at all
+ gStateObject.windows.splice(ix, 1);
+ ix--;
+ }
+ }
+ var stateString = JSON.stringify(gStateObject);
+
+ var ss = Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.nsISessionStore);
+ var top = getBrowserWindow();
+
+ // if there's only this page open, reuse the window for restoring the session
+ if (top.gBrowser.tabContainer.childNodes.length == 1) {
+ ss.setWindowState(top, stateString, true);
+ return;
+ }
+
+ // restore the session into a new window and close the current tab
+ var newWindow = top.openDialog(top.location, "_blank", "chrome,dialog=no,all", "about:blank");
+ var tab = top.gBrowser.selectedTab;
+ newWindow.addEventListener("load", function newWindowLoad() {
+ newWindow.removeEventListener("load", newWindowLoad, true);
+ ss.setWindowState(newWindow, stateString, true);
+
+ top.gBrowser.removeTab(tab);
+ }, true);
+}
+
+function startNewSession() {
+ if (Services.prefs.getIntPref("browser.startup.page") == 1)
+ getBrowserWindow().BrowserHome();
+ else
+ getBrowserWindow().getBrowser().loadURI("about:blank");
+}
+
+function onListClick(aEvent) {
+ // don't react to right-clicks
+ if (aEvent.button == 2)
+ return;
+
+ var cell = treeView.treeBox.getCellAt(aEvent.clientX, aEvent.clientY);
+ if (cell.col) {
+ // restore this specific tab in the same window for middle-clicking
+ // or Ctrl+clicking on a tab's title
+ if ((aEvent.button == 1 || aEvent.ctrlKey) && cell.col.id == "title" &&
+ !treeView.isContainer(cell.row))
+ restoreSingleTab(cell.row, aEvent.shiftKey);
+ else if (cell.col.id == "restore")
+ toggleRowChecked(cell.row);
+ }
+}
+
+function onListKeyDown(aEvent) {
+ switch (aEvent.keyCode)
+ {
+ case KeyEvent.DOM_VK_SPACE:
+ toggleRowChecked(document.getElementById("tabList").currentIndex);
+ break;
+ case KeyEvent.DOM_VK_RETURN:
+ var ix = document.getElementById("tabList").currentIndex;
+ if (aEvent.ctrlKey && !treeView.isContainer(ix))
+ restoreSingleTab(ix, aEvent.shiftKey);
+ break;
+ }
+}
+
+// Helper functions
+
+function getBrowserWindow() {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+}
+
+function toggleRowChecked(aIx) {
+ var item = gTreeData[aIx];
+ item.checked = !item.checked;
+ treeView.treeBox.invalidateRow(aIx);
+
+ function isChecked(aItem) {
+ return aItem.checked;
+ }
+
+ if (treeView.isContainer(aIx)) {
+ // (un)check all tabs of this window as well
+ for (var tab of item.tabs) {
+ tab.checked = item.checked;
+ treeView.treeBox.invalidateRow(gTreeData.indexOf(tab));
+ }
+ }
+ else {
+ // update the window's checkmark as well (0 means "partially checked")
+ item.parent.checked = item.parent.tabs.every(isChecked) ? true :
+ item.parent.tabs.some(isChecked) ? 0 : false;
+ treeView.treeBox.invalidateRow(gTreeData.indexOf(item.parent));
+ }
+
+ document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
+}
+
+function restoreSingleTab(aIx, aShifted) {
+ var tabbrowser = getBrowserWindow().gBrowser;
+ var newTab = tabbrowser.addTab();
+ var item = gTreeData[aIx];
+
+ var ss = Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.nsISessionStore);
+ var tabState = gStateObject.windows[item.parent.ix]
+ .tabs[aIx - gTreeData.indexOf(item.parent) - 1];
+ ss.setTabState(newTab, JSON.stringify(tabState));
+
+ // respect the preference as to whether to select the tab (the Shift key inverses)
+ if (Services.prefs.getBoolPref("browser.tabs.loadInBackground") != !aShifted)
+ tabbrowser.selectedTab = newTab;
+}
+
+// Tree controller
+
+var treeView = {
+ treeBox: null,
+ selection: null,
+
+ get rowCount() { return gTreeData.length; },
+ setTree: function(treeBox) { this.treeBox = treeBox; },
+ getCellText: function(idx, column) { return gTreeData[idx].label; },
+ isContainer: function(idx) { return "open" in gTreeData[idx]; },
+ getCellValue: function(idx, column){ return gTreeData[idx].checked; },
+ isContainerOpen: function(idx) { return gTreeData[idx].open; },
+ isContainerEmpty: function(idx) { return false; },
+ isSeparator: function(idx) { return false; },
+ isSorted: function() { return false; },
+ isEditable: function(idx, column) { return false; },
+ canDrop: function(idx, orientation, dt) { return false; },
+ getLevel: function(idx) { return this.isContainer(idx) ? 0 : 1; },
+
+ getParentIndex: function(idx) {
+ if (!this.isContainer(idx))
+ for (var t = idx - 1; t >= 0 ; t--)
+ if (this.isContainer(t))
+ return t;
+ return -1;
+ },
+
+ hasNextSibling: function(idx, after) {
+ var thisLevel = this.getLevel(idx);
+ for (var t = after + 1; t < gTreeData.length; t++)
+ if (this.getLevel(t) <= thisLevel)
+ return this.getLevel(t) == thisLevel;
+ return false;
+ },
+
+ toggleOpenState: function(idx) {
+ if (!this.isContainer(idx))
+ return;
+ var item = gTreeData[idx];
+ if (item.open) {
+ // remove this window's tab rows from the view
+ var thisLevel = this.getLevel(idx);
+ for (var t = idx + 1; t < gTreeData.length && this.getLevel(t) > thisLevel; t++);
+ var deletecount = t - idx - 1;
+ gTreeData.splice(idx + 1, deletecount);
+ this.treeBox.rowCountChanged(idx + 1, -deletecount);
+ }
+ else {
+ // add this window's tab rows to the view
+ var toinsert = gTreeData[idx].tabs;
+ for (var i = 0; i < toinsert.length; i++)
+ gTreeData.splice(idx + i + 1, 0, toinsert[i]);
+ this.treeBox.rowCountChanged(idx + 1, toinsert.length);
+ }
+ item.open = !item.open;
+ this.treeBox.invalidateRow(idx);
+ },
+
+ getCellProperties: function(idx, column) {
+ if (column.id == "restore" && this.isContainer(idx) && gTreeData[idx].checked === 0)
+ return "partial";
+ if (column.id == "title")
+ return this.getImageSrc(idx, column) ? "icon" : "noicon";
+ return "";
+ },
+
+ getRowProperties: function(idx) {
+ var winState = gTreeData[idx].parent || gTreeData[idx];
+ return winState.ix % 2 != 0 ? "alternate" : "";
+ },
+
+ getImageSrc: function(idx, column) {
+ if (column.id == "title")
+ return gTreeData[idx].src || null;
+ return null;
+ },
+
+ getProgressMode : function(idx, column) { },
+ cycleHeader: function(column) { },
+ cycleCell: function(idx, column) { },
+ selectionChanged: function() { },
+ getColumnProperties: function(column) { return ""; }
+};
diff --git a/comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml b/comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml
new file mode 100644
index 0000000000..34c9a893ea
--- /dev/null
+++ b/comm/suite/components/sessionstore/content/aboutSessionRestore.xhtml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<!DOCTYPE html [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+ <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
+ %netErrorDTD;
+ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % restorepageDTD SYSTEM "chrome://communicator/locale/aboutSessionRestore.dtd">
+ %restorepageDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&restorepage.tabtitle;</title>
+ <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all"/>
+ <link rel="stylesheet" href="chrome://communicator/skin/aboutSessionRestore.css" type="text/css" media="all"/>
+ <link rel="icon" type="image/png" href="chrome://global/skin/icons/question-16.png"/>
+
+ <script src="chrome://communicator/content/aboutSessionRestore.js"/>
+ </head>
+
+ <body dir="&locale.dir;">
+
+ <!-- PAGE CONTAINER (for styling purposes only) -->
+ <div id="errorPageContainer">
+
+ <!-- Error Title -->
+ <div id="errorTitle">
+ <h1 id="errorTitleText">&restorepage.pagetitle;</h1>
+ </div>
+
+ <!-- LONG CONTENT (the section most likely to require scrolling) -->
+ <div id="errorLongContent">
+
+ <!-- Short Description -->
+ <div id="errorShortDesc">
+ <p id="errorShortDescText">&restorepage.issueDesc;</p>
+ </div>
+
+ <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
+ <div id="errorLongDesc">
+ <p>&restorepage.remedies;</p>
+ <ul>
+ <li>&restorepage.dueToChrome;</li>
+ <li>&restorepage.dueToContent;</li>
+ </ul>
+ </div>
+
+ <!-- Short Description -->
+ <div id="errorTrailerDesc">
+ <tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="tabList" flex="1" seltype="single" hidecolumnpicker="true"
+ _window_label="&restorepage.windowLabel;">
+ <treecols>
+ <treecol id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
+ <splitter class="tree-splitter"/>
+ <treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
+ </treecols>
+ <treechildren flex="1"/>
+ </tree>
+ </div>
+ </div>
+
+ <!-- Buttons -->
+ <hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="buttons">
+ <button id="errorTryAgain" label="&restorepage.restoreButton;"
+ accesskey="&restorepage.restore.access;"/>
+ <button id="errorCancel" label="&restorepage.cancelButton;"
+ accesskey="&restorepage.cancel.access;"/>
+ </hbox>
+ <!-- holds the session data for when the tab is closed -->
+ <input type="hidden" id="sessionData"/>
+ </div>
+
+ </body>
+</html>
diff --git a/comm/suite/components/sessionstore/jar.mn b/comm/suite/components/sessionstore/jar.mn
new file mode 100644
index 0000000000..5a398c353e
--- /dev/null
+++ b/comm/suite/components/sessionstore/jar.mn
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/aboutSessionRestore.js (content/aboutSessionRestore.js)
+ content/communicator/aboutSessionRestore.xhtml (content/aboutSessionRestore.xhtml)
diff --git a/comm/suite/components/sessionstore/moz.build b/comm/suite/components/sessionstore/moz.build
new file mode 100644
index 0000000000..9d8fd29468
--- /dev/null
+++ b/comm/suite/components/sessionstore/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "nsISessionStartup.idl",
+ "nsISessionStore.idl",
+]
+
+XPIDL_MODULE = "suitecommon"
+
+EXTRA_COMPONENTS += [
+ "nsSessionStartup.js",
+ "nsSessionStartup.manifest",
+ "nsSessionStore.js",
+]
+
+EXTRA_JS_MODULES.sessionstore = [
+ "XPathGenerator.jsm",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/sessionstore/nsISessionStartup.idl b/comm/suite/components/sessionstore/nsISessionStartup.idl
new file mode 100644
index 0000000000..018b0c1d4c
--- /dev/null
+++ b/comm/suite/components/sessionstore/nsISessionStartup.idl
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsISessionStore keeps track of the current browsing state - i.e.
+ * tab history, cookies, scroll state, form data, POSTDATA and window features
+ * - and allows to restore everything into one window.
+ */
+
+[scriptable, uuid(dd709821-820a-4d0d-b7e8-a566b32377ef)]
+interface nsISessionStartup: nsISupports
+{
+ // Get session state
+ readonly attribute jsval state;
+
+ /**
+ * Determine if session should be restored
+ */
+ boolean doRestore();
+
+ /**
+ * What type of session we're restoring.
+ * NO_SESSION There is no data available from the previous session
+ * RECOVER_SESSION The last session crashed. It will either be restored or
+ * about:sessionrestore will be shown.
+ * RESUME_SESSION The previous session should be restored at startup
+ * DEFER_SESSION The previous session is fine, but it shouldn't be restored
+ * without explicit action (with the exception of pinned tabs)
+ */
+ const unsigned long NO_SESSION = 0;
+ const unsigned long RECOVER_SESSION = 1;
+ const unsigned long RESUME_SESSION = 2;
+ const unsigned long DEFER_SESSION = 3;
+
+ readonly attribute unsigned long sessionType;
+};
diff --git a/comm/suite/components/sessionstore/nsISessionStore.idl b/comm/suite/components/sessionstore/nsISessionStore.idl
new file mode 100644
index 0000000000..2fc2f22175
--- /dev/null
+++ b/comm/suite/components/sessionstore/nsISessionStore.idl
@@ -0,0 +1,216 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMWindow;
+interface nsINode;
+
+/**
+ * nsISessionStore keeps track of the current browsing state - i.e.
+ * tab history, cookies, scroll state, form data, POSTDATA and window features
+ * - and allows to restore everything into one browser window.
+ *
+ * The nsISessionStore API operates mostly on browser windows and the tabbrowser
+ * tabs contained in them:
+ *
+ * * "Browser windows" are those DOM windows having loaded
+ * chrome://navigator/content/navigator.xul . From overlays you can just pass
+ * the global |window| object to the API, though (or |top| from a sidebar).
+ * From elsewhere you can get browser windows through the nsIWindowMediator
+ * by looking for "navigator:browser" windows.
+ *
+ * * "Tabbrowser tabs" are all the child nodes of a browser window's
+ * |getBrowser().tabContainer| such as e.g. |getBrowser().selectedTab|.
+ */
+
+[scriptable, uuid(27a8bd2b-dd76-4cee-82eb-a25f6a94478f)]
+interface nsISessionStore : nsISupports
+{
+ /**
+ * Initialize the service
+ */
+ void init(in nsIDOMWindow aWindow);
+
+ /**
+ * Is it possible to restore the previous session. Will always be false when
+ * in Private Browsing mode.
+ */
+ attribute boolean canRestoreLastSession;
+
+ /**
+ * Restore the previous session if possible. This will not overwrite the
+ * current session. Instead the previous session will be merged into the
+ * current session. Current windows will be reused if they were windows that
+ * pinned tabs were previously restored into. New windows will be opened as
+ * needed.
+ *
+ * Note: This will throw if there is no previous state to restore. Check with
+ * canRestoreLastSession first to avoid thrown errors.
+ */
+ void restoreLastSession();
+
+ /**
+ * Get the current browsing state.
+ * @returns a JSON string representing the session state.
+ */
+ AString getBrowserState();
+
+ /**
+ * Set the browsing state.
+ * This will immediately restore the state of the whole application to the state
+ * passed in, *replacing* the current session.
+ *
+ * @param aState is a JSON string representing the session state.
+ */
+ void setBrowserState(in AString aState);
+
+ /**
+ * @param aWindow is the browser window whose state is to be returned.
+ *
+ * @returns a JSON string representing a session state with only one window.
+ */
+ AString getWindowState(in nsIDOMWindow aWindow);
+
+ /**
+ * @param aWindow is the browser window whose state is to be set.
+ * @param aState is a JSON string representing a session state.
+ * @param aOverwrite boolean overwrite existing tabs
+ */
+ void setWindowState(in nsIDOMWindow aWindow, in AString aState, in boolean aOverwrite);
+
+ /**
+ * @param aTab is the tabbrowser tab whose state is to be returned.
+ *
+ * @returns a JSON string representing the state of the tab
+ * (note: doesn't contain cookies - if you need them, use getWindowState instead).
+ */
+ AString getTabState(in nsINode aTab);
+
+ /**
+ * @param aTab is the tabbrowser tab whose state is to be set.
+ * @param aState is a JSON string representing a session state.
+ */
+ void setTabState(in nsINode aTab, in AString aState);
+
+ /**
+ * Duplicates a given tab as thoroughly as possible.
+ *
+ * @param aWindow is the browser window into which the tab will be duplicated.
+ * Pass null if you want to create a new window.
+ * @param aTab is the tabbrowser tab to duplicate (can be from a different window).
+ * @param aDelta is the offset to the history entry that you want to load.
+ * @param aRelated is a flag to be passed to addTab().
+ * @returns a reference to the newly created tab, or null if opening a window.
+ */
+ nsINode duplicateTab(in nsIDOMWindow aWindow, in nsINode aTab,
+ [optional] in long aDelta,
+ [optional] in boolean aRelated);
+
+ /**
+ * Get the number of restore-able tabs for a browser window
+ */
+ unsigned long getClosedTabCount(in nsIDOMWindow aWindow);
+
+ /**
+ * Get closed tab data
+ *
+ * @param aWindow is the browser window for which to get closed tab data
+ * @returns a JSON string representing the list of closed tabs.
+ */
+ AString getClosedTabData(in nsIDOMWindow aWindow);
+
+ /**
+ * @param aWindow is the browser window to reopen a closed tab in.
+ * @param aIndex is the index of the tab to be restored (FIFO ordered).
+ * @returns a reference to the reopened tab.
+ */
+ nsINode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
+
+ /**
+ * @param aWindow is the browser window associated with the closed tab.
+ * @param aIndex is the index of the closed tab to be removed (FIFO ordered).
+ */
+ nsINode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
+
+ /**
+ * Get the number of restore-able windows
+ */
+ unsigned long getClosedWindowCount();
+
+ /**
+ * Get closed windows data
+ *
+ * @returns a JSON string representing the list of closed windows.
+ */
+ AString getClosedWindowData();
+
+ /**
+ * @param aIndex is the index of the windows to be restored (FIFO ordered).
+ * @returns the nsIDOMWindow object of the reopened window
+ */
+ nsIDOMWindow undoCloseWindow(in unsigned long aIndex);
+
+ /**
+ * @param aIndex is the index of the closed window to be removed (FIFO ordered).
+ *
+ * @throws NS_ERROR_INVALID_ARG
+ * when aIndex does not map to a closed window
+ */
+ nsINode forgetClosedWindow(in unsigned long aIndex);
+
+ /**
+ * @param aWindow is the window to get the value for.
+ * @param aKey is the value's name.
+ *
+ * @returns A string value or an empty string if none is set.
+ */
+ AString getWindowValue(in nsIDOMWindow aWindow, in AString aKey);
+
+ /**
+ * @param aWindow is the browser window to set the value for.
+ * @param aKey is the value's name.
+ * @param aStringValue is the value itself (use toSource/eval before setting JS objects).
+ */
+ void setWindowValue(in nsIDOMWindow aWindow, in AString aKey, in AString aStringValue);
+
+ /**
+ * @param aWindow is the browser window to get the value for.
+ * @param aKey is the value's name.
+ */
+ void deleteWindowValue(in nsIDOMWindow aWindow, in AString aKey);
+
+ /**
+ * @param aTab is the tabbrowser tab to get the value for.
+ * @param aKey is the value's name.
+ *
+ * @returns A string value or an empty string if none is set.
+ */
+ AString getTabValue(in nsINode aTab, in AString aKey);
+
+ /**
+ * @param aTab is the tabbrowser tab to set the value for.
+ * @param aKey is the value's name.
+ * @param aStringValue is the value itself (use toSource/eval before setting JS objects).
+ */
+ void setTabValue(in nsINode aTab, in AString aKey, in AString aStringValue);
+
+ /**
+ * @param aTab is the tabbrowser tab to get the value for.
+ * @param aKey is the value's name.
+ */
+ void deleteTabValue(in nsINode aTab, in AString aKey);
+
+ /**
+ * @param aName is the name of the attribute to save/restore for all tabbrowser tabs.
+ */
+ void persistTabAttribute(in AString aName);
+
+ /**
+ * Returns true if the last window was closed and should be restored
+ *
+ * @returns true if the last window was closed and should be restored
+ */
+ boolean doRestoreLastWindow();
+};
diff --git a/comm/suite/components/sessionstore/nsSessionStartup.js b/comm/suite/components/sessionstore/nsSessionStartup.js
new file mode 100644
index 0000000000..32494d1f27
--- /dev/null
+++ b/comm/suite/components/sessionstore/nsSessionStartup.js
@@ -0,0 +1,223 @@
+/* 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/. */
+
+/**
+ * Session Storage and Restoration
+ *
+ * Overview
+ * This service reads user's session file at startup, and makes a determination
+ * as to whether the session should be restored. It will restore the session
+ * under the circumstances described below.
+ *
+ * Crash Detection
+ * The session file stores a session.state property, that
+ * indicates whether the browser is currently running. When the browser shuts
+ * down, the field is changed to "stopped". At startup, this field is read, and
+ * if it's value is "running", then it's assumed that the browser had previously
+ * crashed, or at the very least that something bad happened, and that we should
+ * restore the session.
+ *
+ * Forced Restarts
+ * In the event that a restart is required due to application update or extension
+ * installation, set the browser.sessionstore.resume_session_once pref to true,
+ * and the session will be restored the next time the browser starts.
+ *
+ * Always Resume
+ * This service will always resume the session if the integer pref
+ * browser.startup.page is set to 3.
+*/
+
+/* :::::::: Constants and Helpers ::::::::::::::: */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const STATE_RUNNING_STR = "running";
+
+function debug(aMsg) {
+ Services.console.logStringMessage("SessionStartup: " + aMsg);
+}
+
+/* :::::::: The Service ::::::::::::::: */
+
+function SessionStartup() {
+}
+
+SessionStartup.prototype = {
+
+ // the state to restore at startup
+ _initialState: null,
+ _sessionType: Ci.nsISessionStartup.NO_SESSION,
+
+/* ........ Global Event Handlers .............. */
+
+ /**
+ * Initialize the component
+ */
+ init: function sss_init() {
+ // get file references
+ let sessionFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ sessionFile.append("sessionstore.json");
+
+ let doResumeSessionOnce = Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
+ let doResumeSession = doResumeSessionOnce ||
+ Services.prefs.getIntPref("browser.startup.page") == 3;
+
+ var resumeFromCrash = Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash");
+
+ // only continue if the session file exists
+ if (!sessionFile.exists())
+ return;
+
+ // get string containing session state
+ let iniString = this._readStateFile(sessionFile);
+ if (!iniString)
+ return;
+
+ try {
+ // parse the session state into JS objects
+ this._initialState = JSON.parse(iniString);
+ }
+ catch (ex) {
+ doResumeSession = false;
+ debug("The session file is invalid: " + ex);
+ }
+
+ // If this is a normal restore then throw away any previous session
+ if (!doResumeSessionOnce && this._initialState)
+ delete this._initialState.lastSessionState;
+
+ let lastSessionCrashed =
+ this._initialState && this._initialState.session &&
+ this._initialState.session.state &&
+ this._initialState.session.state == STATE_RUNNING_STR;
+
+ // set the startup type
+ if (lastSessionCrashed && resumeFromCrash)
+ this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
+ else if (!lastSessionCrashed && doResumeSession)
+ this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
+ else if (this._initialState)
+ this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
+ else
+ this._initialState = null; // reset the state
+
+ if (this.doRestore()) {
+ // wait for the first browser window to open
+ Services.obs.addObserver(this, "sessionstore-windows-restored", true);
+ }
+ },
+
+ /**
+ * Handle notifications
+ */
+ observe: function sss_observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "app-startup":
+ Services.obs.addObserver(this, "final-ui-startup", true);
+ Services.obs.addObserver(this, "quit-application", true);
+ break;
+ case "final-ui-startup":
+ Services.obs.removeObserver(this, "final-ui-startup");
+ Services.obs.removeObserver(this, "quit-application");
+ this.init();
+ break;
+ case "quit-application":
+ // no reason for initializing at this point (cf. bug 409115)
+ Services.obs.removeObserver(this, "final-ui-startup");
+ Services.obs.removeObserver(this, "quit-application");
+ break;
+ case "sessionstore-windows-restored":
+ // no need in repeating this, since session type won't change
+ Services.obs.removeObserver(this, "sessionstore-windows-restored");
+ // free _initialState after nsSessionStore is done with it
+ this._initialState = null;
+ // reset session type after restore
+ this._sessionType = Ci.nsISessionStartup.NO_SESSION;
+ break;
+ }
+ },
+
+/* ........ Public API ................*/
+
+ /**
+ * Get the session state as a string
+ */
+ get state() {
+ return this._initialState;
+ },
+
+ /**
+ * Determine whether there is a pending session restore.
+ * @returns bool
+ */
+ doRestore: function sss_doRestore() {
+ return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
+ this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
+ },
+
+ /**
+ * Get the type of pending session store, if any.
+ */
+ get sessionType() {
+ return this._sessionType;
+ },
+
+/* ........ Storage API .............. */
+
+ /**
+ * Reads a session state file into a string and lets
+ * observers modify the state before it's being used
+ *
+ * @param aFile is any nsIFile
+ * @returns a session state string
+ */
+ _readStateFile: function sss_readStateFile(aFile) {
+ var stateString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ stateString.data = this._readFile(aFile) || "";
+
+ Services.obs.notifyObservers(stateString, "sessionstore-state-read");
+
+ return stateString.data;
+ },
+
+ /**
+ * reads a file into a string
+ * @param aFile
+ * nsIFile
+ * @returns string
+ */
+ _readFile: function sss_readFile(aFile) {
+ try {
+ var stream = Cc["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Ci.nsIFileInputStream);
+ stream.init(aFile, 0x01, 0, 0);
+ var cvStream = Cc["@mozilla.org/intl/converter-input-stream;1"]
+ .createInstance(Ci.nsIConverterInputStream);
+ cvStream.init(stream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+
+ var content = "";
+ var data = {};
+ while (cvStream.readString(4096, data)) {
+ content += data.value;
+ }
+ cvStream.close();
+
+ return content.replace(/\r\n?/g, "\n");
+ }
+ catch (ex) { Cu.reportError(ex); }
+
+ return null;
+ },
+
+ /* ........ QueryInterface .............. */
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsISupportsWeakReference,
+ Ci.nsISessionStartup]),
+ classID: Components.ID("{4e6c1112-57b6-44ba-adf9-99fb573b0a30}")
+
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStartup]);
diff --git a/comm/suite/components/sessionstore/nsSessionStartup.manifest b/comm/suite/components/sessionstore/nsSessionStartup.manifest
new file mode 100644
index 0000000000..c1ade9aa87
--- /dev/null
+++ b/comm/suite/components/sessionstore/nsSessionStartup.manifest
@@ -0,0 +1,11 @@
+# This components must restrict its registration for the app-startup category
+# to the specific list of apps that use it so it doesn't get loaded in xpcshell.
+# Thus we restrict it to these apps:
+#
+# suite {4e6c1112-57b6-44ba-adf9-99fb573b0a30}
+
+component {4e6c1112-57b6-44ba-adf9-99fb573b0a30} nsSessionStartup.js
+contract @mozilla.org/suite/sessionstartup;1 {4e6c1112-57b6-44ba-adf9-99fb573b0a30}
+category app-startup SessionStartup service,@mozilla.org/suite/sessionstartup;1
+component {d37ccdf1-496f-4135-9575-037180af010d} nsSessionStore.js
+contract @mozilla.org/suite/sessionstore;1 {d37ccdf1-496f-4135-9575-037180af010d}
diff --git a/comm/suite/components/sessionstore/nsSessionStore.js b/comm/suite/components/sessionstore/nsSessionStore.js
new file mode 100644
index 0000000000..e469af96bb
--- /dev/null
+++ b/comm/suite/components/sessionstore/nsSessionStore.js
@@ -0,0 +1,4174 @@
+/* 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/. */
+
+/**
+ * Session Storage and Restoration
+ *
+ * Overview
+ * This service keeps track of a user's session, storing the various bits
+ * required to return the browser to its current state. The relevant data is
+ * stored in memory, and is periodically saved to disk in a file in the
+ * profile directory. The service is started at first window load, in
+ * delayedStartup, and will restore the session from the data received from
+ * the nsSessionStartup service.
+ */
+
+/* :::::::: Constants and Helpers ::::::::::::::: */
+
+const STATE_STOPPED = 0;
+const STATE_RUNNING = 1;
+const STATE_QUITTING = -1;
+
+const STATE_STOPPED_STR = "stopped";
+const STATE_RUNNING_STR = "running";
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+const PRIVACY_NONE = 0;
+const PRIVACY_ENCRYPTED = 1;
+const PRIVACY_FULL = 2;
+
+const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
+const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
+
+// global notifications observed
+const OBSERVING = [
+ "domwindowclosed",
+ "quit-application-requested", "quit-application-granted", "quit-application",
+ "browser-lastwindow-close-granted", "browser:purge-session-history"
+];
+
+/*
+XUL Window properties to (re)store
+Restored in restoreDimensions()
+*/
+const WINDOW_ATTRIBUTES = {
+ width: "outerWidth",
+ height: "outerHeight",
+ screenX: "screenX",
+ screenY: "screenY",
+ sizemode: "windowState"
+};
+
+/*
+Hideable window features to (re)store
+Restored in restoreWindowFeatures()
+*/
+const WINDOW_HIDEABLE_FEATURES = [
+ "menubar", "toolbar", "locationbar",
+ "personalbar", "statusbar", "scrollbars"
+];
+
+/*
+docShell capabilities to (re)store
+Restored in restoreHistory()
+eg: browser.docShell["allow" + aCapability] = false;
+
+XXX keep these in sync with all the attributes starting
+ with "allow" in /docshell/base/nsIDocShell.idl
+*/
+const CAPABILITIES = [
+ "Subframes", "Plugins", "Javascript", "MetaRedirects", "Images",
+ "DNSPrefetch", "Auth", "WindowControl"
+];
+
+// These are tab events that we listen to.
+const TAB_EVENTS = ["TabOpen", "TabClose", "TabSelect", "TabShow", "TabHide"];
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "SecMan",
+ "@mozilla.org/scriptsecuritymanager;1", "nsIScriptSecurityManager");
+XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
+ "@mozilla.org/gfx/screenmanager;1", "nsIScreenManager");
+XPCOMUtils.defineLazyServiceGetter(this, "uuidGenerator",
+ "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
+
+ChromeUtils.defineModuleGetter(this, "AppConstants",
+ "resource://gre/modules/AppConstants.jsm");
+ChromeUtils.defineModuleGetter(this, "Utils",
+ "resource://gre/modules/sessionstore/Utils.jsm");
+ChromeUtils.defineModuleGetter(this, "XPathGenerator",
+ "resource:///modules/sessionstore/XPathGenerator.jsm");
+
+function debug(aMsg) {
+ Services.console.logStringMessage("SessionStore: " + aMsg);
+}
+
+/* :::::::: The Service ::::::::::::::: */
+
+function SessionStoreService() {
+ XPCOMUtils.defineLazyGetter(this, "_prefBranch", function () {
+ return Services.prefs.getBranch("browser.");
+ });
+
+ // minimal interval between two save operations (in milliseconds)
+ XPCOMUtils.defineLazyGetter(this, "_interval", function () {
+ // used often, so caching/observing instead of fetching on-demand
+ this._prefBranch.addObserver("sessionstore.interval", this, true);
+ return this._prefBranch.getIntPref("sessionstore.interval");
+ });
+
+ // when crash recovery is disabled, session data is not written to disk
+ XPCOMUtils.defineLazyGetter(this, "_resume_from_crash", function () {
+ // get crash recovery state from prefs and allow for proper reaction to state changes
+ this._prefBranch.addObserver("sessionstore.resume_from_crash", this, true);
+ return this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
+ });
+}
+
+SessionStoreService.prototype = {
+ classID: Components.ID("{d37ccdf1-496f-4135-9575-037180af010d}"),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore,
+ Ci.nsIDOMEventListener,
+ Ci.nsIObserver,
+ Ci.nsISupportsWeakReference]),
+
+ // xul:tab attributes to (re)store (extensions might want to hook in here);
+ // the favicon is always saved for the about:sessionrestore page
+ xulAttributes: {"image": true},
+
+ // set default load state
+ _loadState: STATE_STOPPED,
+
+ // During the initial restore and setBrowserState calls tracks the number of
+ // windows yet to be restored
+ _restoreCount: -1,
+
+ // whether a setBrowserState call is in progress
+ _browserSetState: false,
+
+ // time in milliseconds (Date.now()) when the session was last written to file
+ _lastSaveTime: 0,
+
+ // time in milliseconds when the session was started (saved across sessions),
+ // defaults to now if no session was restored or timestamp doesn't exist
+ _sessionStartTime: Date.now(),
+
+ // states for all currently opened windows
+ _windows: {},
+
+ // states for all recently closed windows
+ _closedWindows: [],
+
+ // collection of session states yet to be restored
+ _statesToRestore: {},
+
+ // counts the number of crashes since the last clean start
+ _recentCrashes: 0,
+
+ // whether the last window was closed and should be restored
+ _restoreLastWindow: false,
+
+ // tabs to restore in order
+ _tabsToRestore: { visible: [], hidden: [] },
+ _tabsRestoringCount: 0,
+
+ // number of tabs to restore concurrently, pref controlled.
+ _maxConcurrentTabRestores: null,
+
+ // The state from the previous session (after restoring pinned tabs). This
+ // state is persisted and passed through to the next session during an app
+ // restart to make the third party add-on warning not trash the deferred
+ // session
+ _lastSessionState: null,
+
+ // Whether we've been initialized
+ _initialized: false,
+
+ // Mapping from legacy docshellIDs to docshellUUIDs.
+ _docshellUUIDMap: new Map(),
+
+/* ........ Public Getters .............. */
+
+ get canRestoreLastSession() {
+ // Always disallow restoring the previous session when in private browsing
+ return this._lastSessionState;
+ },
+
+ set canRestoreLastSession(val) {
+ // Cheat a bit; only allow false.
+ if (!val)
+ this._lastSessionState = null;
+ },
+
+/* ........ Global Event Handlers .............. */
+
+ /**
+ * Initialize the component
+ */
+ initService: function() {
+ OBSERVING.forEach(function(aTopic) {
+ Services.obs.addObserver(this, aTopic, true);
+ }, this);
+
+ this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
+ this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
+
+ this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
+ this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
+
+ // this pref is only read at startup, so no need to observe it
+ this._sessionhistory_max_entries =
+ this._prefBranch.getIntPref("sessionhistory.max_entries");
+
+ this._maxConcurrentTabRestores =
+ this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
+ this._prefBranch.addObserver("sessionstore.max_concurrent_tabs", this, true);
+
+ // Make sure gRestoreTabsProgressListener has a reference to sessionstore
+ // so that it can make calls back in
+ gRestoreTabsProgressListener.ss = this;
+
+ // get file references
+ this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ this._sessionFileBackup = this._sessionFile.clone();
+ this._sessionFile.append("sessionstore.json");
+ this._sessionFileBackup.append("sessionstore.bak");
+
+ // get string containing session state
+ var ss = Cc["@mozilla.org/suite/sessionstartup;1"]
+ .getService(Ci.nsISessionStartup);
+ try {
+ if (ss.sessionType != Ci.nsISessionStartup.NO_SESSION)
+ this._initialState = ss.state;
+ }
+ catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
+
+ if (this._initialState) {
+ try {
+ // If we're doing a DEFERRED session, then we want to pull pinned tabs
+ // out so they can be restored.
+ if (ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) {
+ let [iniState, remainingState] = this._prepDataForDeferredRestore(this._initialState);
+ // If we have a iniState with windows, that means that we have windows
+ // with app tabs to restore.
+ if (iniState.windows.length)
+ this._initialState = iniState;
+ else
+ this._initialState = null;
+ if (remainingState.windows.length)
+ this._lastSessionState = remainingState;
+ }
+ else {
+ // Get the last deferred session in case the user still wants to
+ // restore it
+ this._lastSessionState = this._initialState.lastSessionState;
+
+ let lastSessionCrashed =
+ this._initialState.session && this._initialState.session.state &&
+ this._initialState.session.state == STATE_RUNNING_STR;
+ if (lastSessionCrashed) {
+ this._recentCrashes = (this._initialState.session &&
+ this._initialState.session.recentCrashes || 0) + 1;
+
+ if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
+ // replace the crashed session with a restore-page-only session
+ let pageData = {
+ url: "about:sessionrestore",
+ triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL,
+ formdata: { "#sessionData": JSON.stringify(this._initialState) }
+ };
+ this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
+ }
+ }
+
+ // Load the session start time from the previous state
+ this._sessionStartTime = this._initialState.session &&
+ this._initialState.session.startTime ||
+ this._sessionStartTime;
+
+ // make sure that at least the first window doesn't have anything hidden
+ delete this._initialState.windows[0].hidden;
+ // Since nothing is hidden in the first window, it cannot be a popup
+ delete this._initialState.windows[0].isPopup;
+ // clear any lastSessionWindowID attributes since those don't matter
+ // during normal restore
+ this._initialState.windows.forEach(function(aWindow) {
+ delete aWindow.__lastSessionWindowID;
+ });
+ }
+ }
+ catch (ex) { debug("The session file is invalid: " + ex); }
+ }
+
+ if (this._resume_from_crash) {
+ // create a backup if the session data file exists
+ try {
+ if (this._sessionFileBackup.exists())
+ this._sessionFileBackup.remove(false);
+ if (this._sessionFile.exists())
+ this._sessionFile.copyTo(null, this._sessionFileBackup.leafName);
+ }
+ catch (ex) { Cu.reportError(ex); } // file was write-locked?
+ }
+
+ // at this point, we've as good as resumed the session, so we can
+ // clear the resume_session_once flag, if it's set
+ if (this._loadState != STATE_QUITTING &&
+ this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
+ this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
+
+ this._initialized = true;
+ },
+
+ /**
+ * Start tracking a window.
+ * This function also initializes the component if it's not already
+ * initialized.
+ */
+ init: function sss_init(aWindow) {
+ // Initialize the service if needed.
+ if (!this._initialized)
+ this.initService();
+
+ if (aWindow) {
+ this.onLoad(aWindow);
+ } else if (this._loadState == STATE_STOPPED) {
+ // If init is being called with a null window, it's possible that we
+ // just want to tell sessionstore that a session is live (as is the case
+ // with starting Firefox with -private, for example; see bug 568816),
+ // so we should mark the load state as running to make sure that
+ // things like setBrowserState calls will succeed in restoring the session.
+ this._loadState = STATE_RUNNING;
+ }
+ },
+ /**
+ * Called on application shutdown, after notifications:
+ * quit-application-granted, quit-application
+ */
+ _uninit: function sss_uninit() {
+ // save all data for session resuming
+ this.saveState(true);
+
+ // clear out _tabsToRestore in case it's still holding refs
+ this._tabsToRestore.visible = null;
+ this._tabsToRestore.hidden = null;
+
+ // remove the ref to us from the progress listener
+ gRestoreTabsProgressListener.ss = null;
+
+ // Make sure to break our cycle with the save timer
+ if (this._saveTimer) {
+ this._saveTimer.cancel();
+ this._saveTimer = null;
+ }
+ },
+
+ /**
+ * Handle notifications
+ */
+ observe: function sss_observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "domwindowclosed": // catch closed windows
+ this.onClose(aSubject);
+ break;
+ case "quit-application-requested":
+ // get a current snapshot of all windows
+ this._forEachBrowserWindow(function(aWindow) {
+ this._collectWindowData(aWindow);
+ });
+ DirtyWindows.clear();
+ break;
+ case "quit-application-granted":
+ // freeze the data at what we've got (ignoring closing windows)
+ this._loadState = STATE_QUITTING;
+ break;
+ case "browser-lastwindow-close-granted":
+ // last browser window is quitting.
+ // remember to restore the last window when another browser window is openend
+ // do not account for pref(resume_session_once) at this point, as it might be
+ // set by another observer getting this notice after us
+ this._restoreLastWindow = true;
+ break;
+ case "quit-application":
+ if (aData == "restart" && !this._isSwitchingProfile()) {
+ this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
+ // The browser:purge-session-history notification fires after the
+ // quit-application notification so unregister the
+ // browser:purge-session-history notification to prevent clearing
+ // session data on disk on a restart. It is also unnecessary to
+ // perform any other sanitization processing on a restart as the
+ // browser is about to exit anyway.
+ Services.obs.removeObserver(this, "browser:purge-session-history");
+ }
+
+ if (aData != "restart") {
+ // Throw away the previous session on shutdown
+ this._lastSessionState = null;
+ }
+
+ this._loadState = STATE_QUITTING; // just to be sure
+ this._uninit();
+ break;
+ case "browser:purge-session-history": // catch sanitization
+ this._clearDisk();
+ // If the browser is shutting down, simply return after clearing the
+ // session data on disk as this notification fires after the
+ // quit-application notification so the browser is about to exit.
+ if (this._loadState == STATE_QUITTING)
+ return;
+ this._lastSessionState = null;
+ let openWindows = {};
+ this._forEachBrowserWindow(function(aWindow) {
+ //Hide "Restore Last Session" menu item
+ let restoreItem = aWindow.document.getElementById("historyRestoreLastSession");
+ restoreItem.setAttribute("disabled", "true");
+
+ Array.from(aWindow.getBrowser().tabs).forEach(function(aTab) {
+ delete aTab.linkedBrowser.__SS_data;
+ delete aTab.linkedBrowser.__SS_formDataSaved;
+ if (aTab.linkedBrowser.__SS_restoreState)
+ this._resetTabRestoringState(aTab);
+ });
+ openWindows[aWindow.__SSi] = true;
+ });
+ // also clear all data about closed tabs and windows
+ for (let ix in this._windows) {
+ if (ix in openWindows) {
+ this._windows[ix]._closedTabs = [];
+ }
+ else {
+ delete this._windows[ix];
+ }
+ }
+ // also clear all data about closed windows
+ this._closedWindows = [];
+ // give the tabbrowsers a chance to clear their histories first
+ if (this._getMostRecentBrowserWindow())
+ Services.tm.mainThread.dispatch(this.saveState.bind(this, true),
+ Ci.nsIThread.DISPATCH_NORMAL);
+ else if (this._loadState == STATE_RUNNING)
+ this.saveState(true);
+ break;
+ case "nsPref:changed": // catch pref changes
+ switch (aData) {
+ // if the user decreases the max number of closed tabs they want
+ // preserved update our internal states to match that max
+ case "sessionstore.max_tabs_undo":
+ this._max_tabs_undo = this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
+ for (let ix in this._windows) {
+ this._windows[ix]._closedTabs.splice(this._max_tabs_undo, this._windows[ix]._closedTabs.length);
+ }
+ break;
+ case "sessionstore.max_windows_undo":
+ this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
+ this._capClosedWindows();
+ break;
+ case "sessionstore.interval":
+ this._interval = this._prefBranch.getIntPref("sessionstore.interval");
+ // reset timer and save
+ if (this._saveTimer) {
+ this._saveTimer.cancel();
+ this._saveTimer = null;
+ }
+ this.saveStateDelayed(null, -1);
+ break;
+ case "sessionstore.resume_from_crash":
+ this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
+ // either create the file with crash recovery information or remove it
+ // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
+ if (this._resume_from_crash)
+ this.saveState(true);
+ else if (this._loadState == STATE_RUNNING)
+ this._clearDisk();
+ break;
+ case "sessionstore.max_concurrent_tabs":
+ this._maxConcurrentTabRestores =
+ this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
+ break;
+ }
+ break;
+ case "timer-callback": // timer call back for delayed saving
+ this._saveTimer = null;
+ this.saveState();
+ break;
+ }
+ },
+
+/* ........ Window Event Handlers .............. */
+
+ /**
+ * Implement nsIDOMEventListener for handling various window and tab events
+ */
+ handleEvent: function sss_handleEvent(aEvent) {
+ var win = aEvent.currentTarget.ownerDocument.defaultView;
+ switch (aEvent.type) {
+ case "load":
+ // If __SS_restore_data is set, then we need to restore the document
+ // (form data, scrolling, etc.). This will only happen when a tab is
+ // first restored.
+ if (aEvent.currentTarget.__SS_restore_data)
+ this.restoreDocument(win, aEvent.currentTarget, aEvent);
+ // We still need to call onTabLoad, so fall through to "pageshow" case.
+ case "pageshow":
+ this.onTabLoad(win, aEvent.currentTarget, aEvent);
+ break;
+ case "input":
+ case "DOMAutoComplete":
+ this.onTabInput(win, aEvent.currentTarget);
+ break;
+ case "TabOpen":
+ this.onTabAdd(win, aEvent.originalTarget);
+ break;
+ case "TabClose":
+ // aEvent.detail determines if the tab was closed by moving to a different window
+ if (!aEvent.detail)
+ this.onTabClose(win, aEvent.originalTarget);
+ this.onTabRemove(win, aEvent.originalTarget);
+ break;
+ case "TabSelect":
+ this.onTabSelect(win);
+ break;
+ case "TabShow":
+ this.onTabShow(aEvent.originalTarget);
+ break;
+ case "TabHide":
+ this.onTabHide(aEvent.originalTarget);
+ break;
+ }
+ },
+
+ /**
+ * If it's the first window load since app start...
+ * - determine if we're reloading after a crash or a forced-restart
+ * - restore window state
+ * - restart downloads
+ * Set up event listeners for this window's tabs
+ * @param aWindow
+ * Window reference
+ */
+ onLoad: function sss_onLoad(aWindow) {
+ // return if window has already been initialized
+ if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
+ return;
+
+ // ignore non-browser windows and windows opened while shutting down
+ if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" ||
+ this._loadState == STATE_QUITTING)
+ return;
+
+ // assign it a unique identifier (timestamp)
+ aWindow.__SSi = "window" + Date.now();
+
+ // and create its data object
+ this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
+
+ if (!this._isWindowLoaded(aWindow))
+ this._windows[aWindow.__SSi]._restoring = true;
+ if (!aWindow.toolbar.visible)
+ this._windows[aWindow.__SSi].isPopup = true;
+
+ // perform additional initialization when the first window is loading
+ if (this._loadState == STATE_STOPPED) {
+ this._loadState = STATE_RUNNING;
+ this._lastSaveTime = Date.now();
+
+ // restore a crashed session resp. resume the last session if requested
+ if (this._initialState) {
+ // make sure that the restored tabs are first in the window
+ this._initialState._firstTabs = true;
+ this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
+ this.restoreWindow(aWindow, this._initialState,
+ this._isCmdLineEmpty(aWindow));
+ delete this._initialState;
+
+ // _loadState changed from "stopped" to "running"
+ // force a save operation so that crashes happening during startup are correctly counted
+ this.saveState(true);
+ }
+ else {
+ // Nothing to restore, notify observers things are complete.
+ this.windowToFocus = aWindow;
+ Services.tm.mainThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
+
+ // the next delayed save request should execute immediately
+ this._lastSaveTime -= this._interval;
+ }
+ }
+ // this window was opened by _openWindowWithState
+ else if (!this._isWindowLoaded(aWindow)) {
+ let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
+ this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
+ }
+ else if (this._restoreLastWindow && aWindow.toolbar.visible &&
+ this._closedWindows.length) {
+ // default to the most-recently closed window
+ // don't use popup windows
+ let closedWindowState = null;
+ let closedWindowIndex;
+ for (let i = 0; i < this._closedWindows.length; i++) {
+ // Take the first non-popup, point our object at it, and break out.
+ if (!this._closedWindows[i].isPopup) {
+ closedWindowState = this._closedWindows[i];
+ closedWindowIndex = i;
+ break;
+ }
+ }
+
+ if (closedWindowState) {
+ let newWindowState;
+ if (AppConstants.platform == "macosx" || !this._doResumeSession()) {
+ // We want to split the window up into pinned tabs and unpinned tabs.
+ // Pinned tabs should be restored. If there are any remaining tabs,
+ // they should be added back to _closedWindows.
+ // We'll cheat a little bit and reuse _prepDataForDeferredRestore
+ // even though it wasn't built exactly for this.
+ let [appTabsState, normalTabsState] =
+ this._prepDataForDeferredRestore({ windows: [closedWindowState] });
+
+ // These are our pinned tabs, which we should restore
+ if (appTabsState.windows.length) {
+ newWindowState = appTabsState.windows[0];
+ delete newWindowState.__lastSessionWindowID;
+ }
+
+ // In case there were no unpinned tabs, remove the window from _closedWindows
+ if (!normalTabsState.windows.length) {
+ this._closedWindows.splice(closedWindowIndex, 1);
+ }
+ // Or update _closedWindows with the modified state
+ else {
+ delete normalTabsState.windows[0].__lastSessionWindowID;
+ this._closedWindows[closedWindowIndex] = normalTabsState.windows[0];
+ }
+ }
+ else {
+ // If we're just restoring the window, make sure it gets removed from
+ // _closedWindows.
+ this._closedWindows.splice(closedWindowIndex, 1);
+ newWindowState = closedWindowState;
+ delete newWindowState.hidden;
+ }
+
+ if (newWindowState) {
+ // Ensure that the window state isn't hidden
+ this._restoreCount = 1;
+ let state = { windows: [newWindowState] };
+ this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow));
+ }
+ }
+ // we actually restored the session just now.
+ this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
+ }
+ if (this._restoreLastWindow && aWindow.toolbar.visible) {
+ // always reset (if not a popup window)
+ // we don't want to restore a window directly after, for example,
+ // undoCloseWindow was executed.
+ this._restoreLastWindow = false;
+ }
+
+ var tabbrowser = aWindow.getBrowser();
+
+ // add tab change listeners to all already existing tabs
+ for (let i = 0; i < tabbrowser.tabs.length; i++) {
+ this.onTabAdd(aWindow, tabbrowser.tabs[i], true);
+ }
+ // notification of tab add/remove/selection/show/hide
+ TAB_EVENTS.forEach(function(aEvent) {
+ tabbrowser.tabContainer.addEventListener(aEvent, this, true);
+ }, this);
+ },
+
+ /**
+ * On window close...
+ * - remove event listeners from tabs
+ * - save all window data
+ * @param aWindow
+ * Window reference
+ */
+ onClose: function sss_onClose(aWindow) {
+ // this window was about to be restored - conserve its original data, if any
+ let isFullyLoaded = this._isWindowLoaded(aWindow);
+ if (!isFullyLoaded) {
+ if (!aWindow.__SSi)
+ aWindow.__SSi = "window" + Date.now();
+ this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
+ delete this._statesToRestore[aWindow.__SS_restoreID];
+ delete aWindow.__SS_restoreID;
+ }
+
+ // ignore windows not tracked by SessionStore
+ if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
+ return;
+ }
+
+ if (this.windowToFocus && this.windowToFocus == aWindow) {
+ delete this.windowToFocus;
+ }
+
+ var tabbrowser = aWindow.getBrowser();
+
+ TAB_EVENTS.forEach(function(aEvent) {
+ tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
+ }, this);
+
+ // remove the progress listener for this window
+ try {
+ tabbrowser.removeTabsProgressListener(gRestoreTabsProgressListener);
+ } catch (ex) {};
+
+ let winData = this._windows[aWindow.__SSi];
+ if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down
+ // update all window data for a last time
+ this._collectWindowData(aWindow);
+
+ if (isFullyLoaded) {
+ winData.title = aWindow.content.document.title || tabbrowser.selectedTab.label;
+ winData.title = this._replaceLoadingTitle(winData.title, tabbrowser,
+ tabbrowser.selectedTab);
+ this._updateCookies([winData]);
+ }
+
+ // save the window if it has multiple tabs or a single saveable tab
+ if (winData.tabs.length > 1 ||
+ (winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]))) {
+ this._closedWindows.unshift(winData);
+ this._capClosedWindows();
+ }
+
+ // clear this window from the list
+ delete this._windows[aWindow.__SSi];
+
+ // save the state without this window to disk
+ this.saveStateDelayed();
+ }
+
+ for (let i = 0; i < tabbrowser.tabs.length; i++) {
+ this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
+ }
+
+ // Cache the window state until it is completely gone.
+ DyingWindowCache.set(aWindow, winData);
+
+ delete aWindow.__SSi;
+ },
+
+ /**
+ * set up listeners for a new tab
+ * @param aWindow
+ * Window reference
+ * @param aTab
+ * Tab reference
+ * @param aNoNotification
+ * bool Do not save state if we're updating an existing tab
+ */
+ onTabAdd: function sss_onTabAdd(aWindow, aTab, aNoNotification) {
+ let browser = aTab.linkedBrowser;
+ browser.addEventListener("load", this, true);
+ browser.addEventListener("pageshow", this, true);
+ browser.addEventListener("input", this, true);
+ browser.addEventListener("DOMAutoComplete", this, true);
+
+ if (!aNoNotification) {
+ this.saveStateDelayed(aWindow);
+ }
+
+ this._updateCrashReportURL(aWindow);
+ },
+
+ /**
+ * remove listeners for a tab
+ * @param aWindow
+ * Window reference
+ * @param aTab
+ * Tab reference
+ * @param aNoNotification
+ * bool Do not save state if we're updating an existing tab
+ */
+ onTabRemove: function sss_onTabRemove(aWindow, aTab, aNoNotification) {
+ let browser = aTab.linkedBrowser;
+ browser.removeEventListener("load", this, true);
+ browser.removeEventListener("pageshow", this, true);
+ browser.removeEventListener("change", this, true);
+ browser.removeEventListener("input", this, true);
+ browser.removeEventListener("DOMAutoComplete", this, true);
+
+ delete browser.__SS_data;
+
+ // If this tab was in the middle of restoring or still needs to be restored,
+ // we need to reset that state. If the tab was restoring, we will attempt to
+ // restore the next tab.
+ let previousState = browser.__SS_restoreState;
+ if (previousState) {
+ this._resetTabRestoringState(aTab);
+ if (previousState == TAB_STATE_RESTORING)
+ this.restoreNextTab();
+ }
+
+ if (!aNoNotification) {
+ this.saveStateDelayed(aWindow);
+ }
+ },
+
+ /**
+ * When a tab closes, collect its properties
+ * @param aWindow
+ * Window reference
+ * @param aTab
+ * Tab reference
+ */
+ onTabClose: function sss_onTabClose(aWindow, aTab) {
+ // notify the tabbrowser that the tab state will be retrieved for the last time
+ // (so that extension authors can easily set data on soon-to-be-closed tabs)
+ var event = aWindow.document.createEvent("Events");
+ event.initEvent("SSTabClosing", true, false);
+ aTab.dispatchEvent(event);
+
+ // don't update our internal state if we don't have to
+ if (this._max_tabs_undo == 0) {
+ return;
+ }
+
+ // make sure that the tab related data is up-to-date
+ var tabState = this._collectTabData(aTab);
+ this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
+
+ // store closed-tab data for undo
+ if (this._shouldSaveTabState(tabState)) {
+ aTab.tabData = { state: tabState };
+ var closedTabs = this._windows[aWindow.__SSi]._closedTabs;
+ closedTabs.unshift(aTab.tabData);
+ if (closedTabs.length > this._max_tabs_undo)
+ closedTabs.length = this._max_tabs_undo;
+ };
+ },
+
+ /**
+ * When a tab loads, save state.
+ * @param aWindow
+ * Window reference
+ * @param aBrowser
+ * Browser reference
+ * @param aEvent
+ * Event obj
+ */
+ onTabLoad: function sss_onTabLoad(aWindow, aBrowser, aEvent) {
+ // react on "load" and solitary "pageshow" events (the first "pageshow"
+ // following "load" is too late for deleting the data caches)
+ // It's possible to get a load event after calling stop on a browser (when
+ // overwriting tabs). We want to return early if the tab hasn't been restored yet.
+ if ((aEvent.type != "load" && !aEvent.persisted) ||
+ (aBrowser.__SS_restoreState &&
+ aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)) {
+ return;
+ }
+
+ delete aBrowser.__SS_data;
+ this.saveStateDelayed(aWindow);
+
+ // attempt to update the current URL we send in a crash report
+ this._updateCrashReportURL(aWindow);
+ },
+
+ /**
+ * Called when a browser sends the "input" notification
+ * @param aWindow
+ * Window reference
+ * @param aBrowser
+ * Browser reference
+ */
+ onTabInput: function sss_onTabInput(aWindow, aBrowser) {
+ this.saveStateDelayed(aWindow, 3000);
+ },
+
+ /**
+ * When a tab is selected, save session data
+ * @param aWindow
+ * Window reference
+ */
+ onTabSelect: function sss_onTabSelect(aWindow) {
+ if (this._loadState == STATE_RUNNING) {
+ this._windows[aWindow.__SSi].selected = aWindow.getBrowser().tabContainer.selectedIndex;
+
+ let tab = aWindow.getBrowser().selectedTab;
+ // If __SS_restoreState is still on the browser and it is
+ // TAB_STATE_NEEDS_RESTORE, then then we haven't restored
+ // this tab yet. Explicitly call restoreTab to kick off the restore.
+ if (tab.linkedBrowser.__SS_restoreState &&
+ tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ this.restoreTab(tab);
+
+ // attempt to update the current URL we send in a crash report
+ this._updateCrashReportURL(aWindow);
+ }
+ },
+
+ onTabShow: function sss_onTabShow(aTab) {
+ // If the tab hasn't been restored yet, move it into the right _tabsToRestore bucket
+ if (aTab.linkedBrowser.__SS_restoreState &&
+ aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab), 1);
+ // Just put it at the end of the list of visible tabs;
+ this._tabsToRestore.visible.push(aTab);
+ }
+ },
+
+ onTabHide: function sss_onTabHide(aTab) {
+ // If the tab hasn't been restored yet, move it into the right _tabsToRestore bucket
+ if (aTab.linkedBrowser.__SS_restoreState &&
+ aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab), 1);
+ // Just put it at the end of the list of hidden tabs;
+ this._tabsToRestore.hidden.push(aTab);
+ }
+ },
+
+/* ........ nsISessionStore API .............. */
+
+ getBrowserState: function sss_getBrowserState() {
+ return this._toJSONString(this._getCurrentState());
+ },
+
+ setBrowserState: function sss_setBrowserState(aState) {
+ this._handleClosedWindows();
+
+ try {
+ var state = JSON.parse(aState);
+ }
+ catch (ex) { /* invalid state object - don't restore anything */ }
+ if (!state || !state.windows)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ this._browserSetState = true;
+
+ // Make sure _tabsToRestore is emptied out
+ this._resetRestoringState();
+
+ var window = this._getMostRecentBrowserWindow();
+ if (!window) {
+ this._restoreCount = 1;
+ this._openWindowWithState(state);
+ return;
+ }
+
+ // close all other browser windows
+ this._forEachBrowserWindow(function(aWindow) {
+ if (aWindow != window) {
+ aWindow.close();
+ this.onClose(aWindow);
+ }
+ });
+
+ // make sure closed window data isn't kept
+ this._closedWindows = [];
+
+ // determine how many windows are meant to be restored
+ this._restoreCount = state.windows ? state.windows.length : 0;
+
+ // restore to the given state
+ this.restoreWindow(window, state, true);
+ },
+
+ getWindowState: function sss_getWindowState(aWindow) {
+ if ("__SSi" in aWindow) {
+ return this._toJSONString(this._getWindowState(aWindow));
+ }
+
+ if (DyingWindowCache.has(aWindow)) {
+ let data = DyingWindowCache.get(aWindow);
+ return this._toJSONString({ windows: [data] });
+ }
+
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+ },
+
+ setWindowState: function sss_setWindowState(aWindow, aState, aOverwrite) {
+ if (!aWindow.__SSi)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ this.restoreWindow(aWindow, aState, aOverwrite);
+ },
+
+ getTabState: function sss_getTabState(aTab) {
+ if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ var tabState = this._collectTabData(aTab);
+
+ var window = aTab.ownerDocument.defaultView;
+ this._updateTextAndScrollDataForTab(window, aTab.linkedBrowser, tabState);
+
+ return this._toJSONString(tabState);
+ },
+
+ setTabState: function sss_setTabState(aTab, aState) {
+ var tabState = JSON.parse(aState);
+ if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ var window = aTab.ownerDocument.defaultView;
+ this._sendWindowStateEvent(window, "Busy");
+ this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
+ },
+
+ duplicateTab: function sss_duplicateTab(aWindow, aTab, aDelta, aRelated) {
+ if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi ||
+ aWindow && !aWindow.getBrowser)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ var tabState = this._collectTabData(aTab, true);
+ var sourceWindow = aTab.ownerDocument.defaultView;
+ this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
+ tabState.index += aDelta;
+ tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
+
+ if (aWindow) {
+ this._sendWindowStateEvent(aWindow, "Busy");
+ var newTab = aWindow.getBrowser()
+ .addTab(null, { relatedToCurrent: aRelated });
+ this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
+ return newTab;
+ }
+
+ var state = { windows: [{ tabs: [tabState] }] };
+ this.windowToFocus = this._openWindowWithState(state);
+ return null;
+ },
+
+ _getClosedTabs: function sss_getClosedTabs(aWindow) {
+ if (!aWindow.__SSi)
+ return this._toJSONString(aWindow.__SS_dyingCache._closedTabs);
+
+ var closedTabs = this._windows[aWindow.__SSi]._closedTabs;
+ closedTabs = closedTabs.concat(aWindow.getBrowser().savedBrowsers);
+ closedTabs = closedTabs.filter(function(aTabData, aIndex, aArray) {
+ return aArray.indexOf(aTabData) == aIndex;
+ });
+ return closedTabs;
+ },
+
+ getClosedTabCount: function sss_getClosedTabCount(aWindow) {
+ if ("__SSi" in aWindow) {
+ return this._windows[aWindow.__SSi]._closedTabs.length;
+ }
+
+ if (DyingWindowCache.has(aWindow)) {
+ return DyingWindowCache.get(aWindow)._closedTabs.length;
+ }
+
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+ },
+
+ getClosedTabData: function sss_getClosedTabData(aWindow) {
+ if ("__SSi" in aWindow) {
+ return this._toJSONString(this._windows[aWindow.__SSi]._closedTabs);
+ }
+
+ if (DyingWindowCache.has(aWindow)) {
+ let data = DyingWindowCache.get(aWindow);
+ return this._toJSONString(data._closedTabs);
+ }
+
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+ },
+
+ undoCloseTab: function sss_undoCloseTab(aWindow, aIndex) {
+ if (!aWindow.__SSi)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ var closedTabs = this._getClosedTabs(aWindow);
+ if (!(aIndex in closedTabs))
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ // fetch the data of closed tab, while removing it from the array
+ let closedTab = closedTabs[aIndex];
+ if (aIndex in this._windows[aWindow.__SSi]._closedTabs)
+ this._windows[aWindow.__SSi]._closedTabs.splice(aIndex, 1);
+ var tabbrowser = aWindow.getBrowser();
+ var index = tabbrowser.savedBrowsers.indexOf(closedTab);
+ this._sendWindowStateEvent(aWindow, "Busy");
+ if (index != -1)
+ // SeaMonkey has its own undoclosetab functionality
+ return tabbrowser.restoreTab(index);
+
+ // create a new tab
+ var tab = tabbrowser.addTab();
+
+ // restore the tab's position
+ tabbrowser.moveTabTo(tab, closedTab.pos);
+
+ // restore tab content
+ this.restoreHistoryPrecursor(aWindow, [tab], [closedTab.state], 1, 0, 0);
+
+ // focus the tab's content area (bug 342432)
+ tab.linkedBrowser.focus();
+
+ return tab;
+ },
+
+ forgetClosedTab: function sss_forgetClosedTab(aWindow, aIndex) {
+ if (!aWindow.__SSi)
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ var closedTabs = this._getClosedTabs(aWindow);
+ if (!(aIndex in closedTabs))
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ // remove closed tab from the array
+ var closedTab = closedTabs[aIndex];
+ if (aIndex in this._windows[aWindow.__SSi]._closedTabs)
+ this._windows[aWindow.__SSi]._closedTabs.splice(aIndex, 1);
+ var tabbrowser = aWindow.getBrowser();
+ var index = tabbrowser.savedBrowsers.indexOf(closedTab);
+ if (index != -1)
+ tabbrowser.forgetSavedBrowser(aIndex);
+ },
+
+ getClosedWindowCount: function sss_getClosedWindowCount() {
+ return this._closedWindows.length;
+ },
+
+ getClosedWindowData: function sss_getClosedWindowData() {
+ return this._toJSONString(this._closedWindows);
+ },
+
+ undoCloseWindow: function sss_undoCloseWindow(aIndex) {
+ if (!(aIndex in this._closedWindows))
+ return null;
+
+ // reopen the window
+ let state = { windows: this._closedWindows.splice(aIndex, 1) };
+ let window = this._openWindowWithState(state);
+ this.windowToFocus = window;
+ return window;
+ },
+
+ forgetClosedWindow: function sss_forgetClosedWindow(aIndex) {
+ // default to the most-recently closed window
+ aIndex = aIndex || 0;
+ if (!(aIndex in this._closedWindows))
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+
+ // remove closed window from the array
+ this._closedWindows.splice(aIndex, 1);
+ },
+
+ getWindowValue: function sss_getWindowValue(aWindow, aKey) {
+ if ("__SSi" in aWindow) {
+ var data = this._windows[aWindow.__SSi].extData || {};
+ return data[aKey] || "";
+ }
+ if (DyingWindowCache.has(aWindow)) {
+ let data = DyingWindowCache.get(aWindow).extData || {};
+ return data[aKey] || "";
+ }
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+ },
+
+ setWindowValue: function sss_setWindowValue(aWindow, aKey, aStringValue) {
+ if (aWindow.__SSi) {
+ if (!this._windows[aWindow.__SSi].extData) {
+ this._windows[aWindow.__SSi].extData = {};
+ }
+ this._windows[aWindow.__SSi].extData[aKey] = aStringValue;
+ this.saveStateDelayed(aWindow);
+ }
+ else {
+ throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
+ }
+ },
+
+ deleteWindowValue: function sss_deleteWindowValue(aWindow, aKey) {
+ if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
+ this._windows[aWindow.__SSi].extData[aKey])
+ delete this._windows[aWindow.__SSi].extData[aKey];
+ },
+
+ getTabValue: function sss_getTabValue(aTab, aKey) {
+ let data = {};
+ if (aTab.__SS_extdata) {
+ data = aTab.__SS_extdata;
+ }
+ else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
+ // If the tab hasn't been fully restored, get the data from the to-be-restored data
+ data = aTab.linkedBrowser.__SS_data.extData;
+ }
+ return data[aKey] || "";
+ },
+
+ setTabValue: function sss_setTabValue(aTab, aKey, aStringValue) {
+ // If the tab hasn't been restored, then set the data there, otherwise we
+ // could lose newly added data.
+ let saveTo;
+ if (aTab.__SS_extdata) {
+ saveTo = aTab.__SS_extdata;
+ }
+ else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
+ saveTo = aTab.linkedBrowser.__SS_data.extData;
+ }
+ else {
+ aTab.__SS_extdata = {};
+ saveTo = aTab.__SS_extdata;
+ }
+ saveTo[aKey] = aStringValue;
+ this.saveStateDelayed(aTab.ownerDocument.defaultView);
+ },
+
+ deleteTabValue: function sss_deleteTabValue(aTab, aKey) {
+ // We want to make sure that if data is accessed early, we attempt to delete
+ // that data from __SS_data as well. Otherwise we'll throw in cases where
+ // data can be set or read.
+ let deleteFrom = null;
+ if (aTab.__SS_extdata) {
+ deleteFrom = aTab.__SS_extdata;
+ }
+ else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
+ deleteFrom = aTab.linkedBrowser.__SS_data.extData;
+ }
+
+ if (deleteFrom && deleteFrom[aKey])
+ delete deleteFrom[aKey];
+ },
+
+ persistTabAttribute: function sss_persistTabAttribute(aName) {
+ if (aName in this.xulAttributes)
+ return; // this attribute is already being tracked
+
+ this.xulAttributes[aName] = true;
+ this.saveStateDelayed();
+ },
+
+ doRestoreLastWindow: function sss_doRestoreLastWindow() {
+ let state = null;
+ this._closedWindows.forEach(function(aWinState) {
+ if (!state && !aWinState.isPopup) {
+ state = aWinState;
+ }
+ });
+ return (this._restoreLastWindow && state &&
+ this._doResumeSession());
+ },
+
+ /**
+ * Restores the session state stored in _lastSessionState. This will attempt
+ * to merge data into the current session. If a window was opened at startup
+ * with pinned tab(s), then the remaining data from the previous session for
+ * that window will be opened into that winddow. Otherwise new windows will
+ * be opened.
+ */
+ restoreLastSession: function sss_restoreLastSession() {
+ // Use the public getter since it also checks PB mode
+ if (!this.canRestoreLastSession)
+ throw (Components.returnCode = Cr.NS_ERROR_FAILURE);
+
+ // First collect each window with its id...
+ let windows = {};
+ this._forEachBrowserWindow(function(aWindow) {
+ if (aWindow.__SS_lastSessionWindowID)
+ windows[aWindow.__SS_lastSessionWindowID] = aWindow;
+ });
+
+ let lastSessionState = this._lastSessionState;
+
+ // This shouldn't ever be the case...
+ if (!lastSessionState.windows.length)
+ throw (Components.returnCode = Cr.NS_ERROR_UNEXPECTED);
+
+ // We're technically doing a restore, so set things up so we send the
+ // notification when we're done. We want to send "sessionstore-browser-state-restored".
+ this._restoreCount = lastSessionState.windows.length;
+ this._browserSetState = true;
+
+ // We want to re-use the last opened window instead of opening a new one in
+ // the case where it's "empty" and not associated with a window in the session.
+ // We will do more processing via _prepWindowToRestoreInto if we need to use
+ // the lastWindow.
+ let lastWindow = this._getMostRecentBrowserWindow();
+ let canUseLastWindow = lastWindow &&
+ !lastWindow.__SS_lastSessionWindowID;
+
+ // Restore into windows or open new ones as needed.
+ for (let i = 0; i < lastSessionState.windows.length; i++) {
+ let winState = lastSessionState.windows[i];
+ let lastSessionWindowID = winState.__lastSessionWindowID;
+ // delete lastSessionWindowID so we don't add that to the window again
+ delete winState.__lastSessionWindowID;
+
+ // See if we can use an open window. First try one that is associated with
+ // the state we're trying to restore and then fallback to the last selected
+ // window.
+ let windowToUse = windows[lastSessionWindowID];
+ if (!windowToUse && canUseLastWindow) {
+ windowToUse = lastWindow;
+ canUseLastWindow = false;
+ }
+
+ let [canUseWindow, canOverwriteTabs] = this._prepWindowToRestoreInto(windowToUse);
+
+ // If there's a window already open that we can restore into, use that
+ if (canUseWindow) {
+ // Since we're not overwriting existing tabs, we want to merge _closedTabs,
+ // putting existing ones first. Then make sure we're respecting the max pref.
+ if (winState._closedTabs && winState._closedTabs.length) {
+ let curWinState = this._windows[windowToUse.__SSi];
+ curWinState._closedTabs = curWinState._closedTabs.concat(winState._closedTabs);
+ curWinState._closedTabs.splice(this._max_tabs_undo, curWinState._closedTabs.length);
+ }
+
+ // Restore into that window - pretend it's a followup since we'll already
+ // have a focused window.
+ //XXXzpao This is going to merge extData together (taking what was in
+ // winState over what is in the window already. The hack we have
+ // in _preWindowToRestoreInto will prevent most (all?) Panorama
+ // weirdness but we will still merge other extData.
+ // Bug 588217 should make this go away by merging the group data.
+ this.restoreWindow(windowToUse, { windows: [winState] }, canOverwriteTabs, true);
+ }
+ else {
+ this._openWindowWithState({ windows: [winState] });
+ }
+ }
+
+ // Merge closed windows from this session with ones from last session
+ if (lastSessionState._closedWindows) {
+ this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows);
+ this._capClosedWindows();
+ }
+
+ // Set data that persists between sessions
+ this._recentCrashes = lastSessionState.session &&
+ lastSessionState.session.recentCrashes || 0;
+ this._sessionStartTime = lastSessionState.session &&
+ lastSessionState.session.startTime ||
+ this._sessionStartTime;
+
+ this._lastSessionState = null;
+ },
+
+ /**
+ * See if aWindow is usable for use when restoring a previous session via
+ * restoreLastSession. If usable, prepare it for use.
+ *
+ * @param aWindow
+ * the window to inspect & prepare
+ * @returns [canUseWindow, canOverwriteTabs]
+ * canUseWindow: can the window be used to restore into
+ * canOverwriteTabs: all of the current tabs are home pages and we
+ * can overwrite them
+ */
+ _prepWindowToRestoreInto: function sss__prepWindowToRestoreInto(aWindow) {
+ if (!aWindow)
+ return [false, false];
+
+ // We might be able to overwrite the existing tabs instead of just adding
+ // the previous session's tabs to the end. This will be set if possible.
+ let canOverwriteTabs = false;
+
+ // Step 1 of processing:
+ // Inspect extData for Panorama identifiers. If found, then we want to
+ // inspect further. If there is a single group, then we can use this
+ // window. If there are multiple groups then we won't use this window.
+ let data = this.getWindowValue(aWindow, "tabview-group");
+ if (data) {
+ data = JSON.parse(data);
+
+ // Multiple keys means multiple groups, which means we don't want to use this window.
+ if (Object.keys(data).length > 1) {
+ return [false, false];
+ }
+ else {
+ // If there is only one group, then we want to ensure that its group id
+ // is 0. This is how Panorama forces group merging when new tabs are opened.
+ //XXXzpao This is a hack and the proper fix really belongs in Panorama.
+ let groupKey = Object.keys(data)[0];
+ if (groupKey !== "0") {
+ data["0"] = data[groupKey];
+ delete data[groupKey];
+ this.setWindowValue(aWindow, "tabview-groups", JSON.stringify(data));
+ }
+ }
+ }
+
+ // Step 2 of processing:
+ // If we're still here, then the window is usable. Look at the open tabs in
+ // comparison to home pages. If all the tabs are home pages then we'll end
+ // up overwriting all of them. Otherwise we'll just close the tabs that
+ // match home pages.
+ let homePages = aWindow.getHomePage();
+ let removableTabs = [];
+ let tabbrowser = aWindow.getBrowser();
+ let normalTabsLen = tabbrowser.tabs.length - tabbrowser._numPinnedTabs;
+ for (let i = 0; i < tabbrowser.tabs.length; i++) {
+ let tab = tabbrowser.tabs[i];
+ if (homePages.includes(tab.linkedBrowser.currentURI.spec)) {
+ removableTabs.push(tab);
+ }
+ }
+
+ if (tabbrowser.tabs.length == removableTabs.length) {
+ canOverwriteTabs = true;
+ }
+ else {
+ // If we're not overwriting all of the tabs, then close the home tabs.
+ for (let i = removableTabs.length - 1; i >= 0; i--) {
+ tabbrowser.removeTab(removableTabs.pop(), { animate: false });
+ }
+ }
+
+ return [true, canOverwriteTabs];
+ },
+
+/* ........ Saving Functionality .............. */
+
+ /**
+ * Store all session data for a window
+ * @param aWindow
+ * Window reference
+ */
+ _saveWindowHistory: function sss_saveWindowHistory(aWindow) {
+ var tabbrowser = aWindow.getBrowser();
+ var tabs = tabbrowser.tabs;
+ var tabsData = this._windows[aWindow.__SSi].tabs = [];
+
+ for (var i = 0; i < tabs.length; i++)
+ tabsData.push(this._collectTabData(tabs[i]));
+
+ this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
+ },
+
+ /**
+ * Collect data related to a single tab
+ * @param aTab
+ * tabbrowser tab
+ * @param aFullData
+ * always return privacy sensitive data (use with care)
+ * @returns object
+ */
+ _collectTabData: function sss_collectTabData(aTab, aFullData) {
+ var tabData = { entries: [] };
+ var browser = aTab.linkedBrowser;
+
+ if (!browser || !browser.currentURI)
+ // can happen when calling this function right after .addTab()
+ return tabData;
+ else if (browser.__SS_data &&
+ browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ // use the data to be restored when the tab hasn't been completely loaded
+ tabData = browser.__SS_data;
+ if (aTab.pinned)
+ tabData.pinned = true;
+ else
+ delete tabData.pinned;
+ tabData.hidden = aTab.hidden;
+
+ // If __SS_extdata is set then we'll use that since it might be newer.
+ if (aTab.__SS_extdata)
+ tabData.extData = aTab.__SS_extdata;
+ // If it exists but is empty then a key was likely deleted. In that case just
+ // delete extData.
+ if (tabData.extData && !Object.keys(tabData.extData).length)
+ delete tabData.extData;
+ return tabData;
+ }
+
+ var history = null;
+ try {
+ history = browser.sessionHistory;
+ }
+ catch (ex) { } // this could happen if we catch a tab during (de)initialization
+
+ // XXXzeniko anchor navigation doesn't reset __SS_data, so we could reuse
+ // data even when we shouldn't (e.g. Back, different anchor)
+ if (history && browser.__SS_data &&
+ browser.__SS_data.entries[history.index] &&
+ browser.__SS_data.entries[history.index].url == browser.currentURI.spec &&
+ history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
+ tabData = browser.__SS_data;
+ tabData.index = history.index + 1;
+ }
+ else if (history && history.count > 0) {
+ try {
+ for (var j = 0; j < history.count; j++) {
+ let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j),
+ aFullData, aTab.pinned);
+ tabData.entries.push(entry);
+ }
+ // If we make it through the for loop, then we're ok and we should clear
+ // any indicator of brokenness.
+ delete aTab.__SS_broken_history;
+ }
+ catch (ex) {
+ // In some cases, getEntryAtIndex will throw. This seems to be due to
+ // history.count being higher than it should be. By doing this in a
+ // try-catch, we'll update history to where it breaks, assert for
+ // non-release builds, and still save sessionstore.js. We'll track if
+ // we've shown the assert for this tab so we only show it once.
+ // cf. bug 669196.
+ if (!aTab.__SS_broken_history) {
+ // First Focus the window & tab we're having trouble with.
+ aTab.ownerDocument.defaultView.focus();
+ aTab.ownerDocument.defaultView.getBrowser().selectedTab = aTab;
+ debug("SessionStore failed gathering complete history " +
+ "for the focused window/tab. See bug 669196.");
+ aTab.__SS_broken_history = true;
+ }
+ }
+ tabData.index = history.index + 1;
+
+ // make sure not to cache privacy sensitive data which shouldn't get out
+ if (!aFullData)
+ browser.__SS_data = tabData;
+ }
+ else if (browser.currentURI.spec != "about:blank" ||
+ browser.contentDocument.body.hasChildNodes()) {
+ tabData.entries[0] = { url: browser.currentURI.spec,
+ triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL };
+ tabData.index = 1;
+ }
+
+ // If there is a userTypedValue set, then either the user has typed something
+ // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
+ // is used to indicate whether the tab was in some sort of loading state with
+ // userTypedValue.
+ if (browser.userTypedValue) {
+ tabData.userTypedValue = browser.userTypedValue;
+ tabData.userTypedClear = browser.userTypedClear;
+ } else {
+ delete tabData.userTypedValue;
+ delete tabData.userTypedClear;
+ }
+
+ var disallow = [];
+ for (var i = 0; i < CAPABILITIES.length; i++)
+ if (!browser.docShell["allow" + CAPABILITIES[i]])
+ disallow.push(CAPABILITIES[i]);
+ if (disallow.length > 0)
+ tabData.disallow = disallow.join(",");
+ else if (tabData.disallow)
+ delete tabData.disallow;
+
+ tabData.attributes = {};
+ for (let name in this.xulAttributes) {
+ if (aTab.hasAttribute(name))
+ tabData.attributes[name] = aTab.getAttribute(name);
+ }
+
+ if (aTab.__SS_extdata)
+ tabData.extData = aTab.__SS_extdata;
+ else if (tabData.extData)
+ delete tabData.extData;
+
+ if (history && browser.docShell instanceof Ci.nsIDocShell)
+ this._serializeSessionStorage(tabData, history, browser.docShell, aFullData,
+ false);
+
+ return tabData;
+ },
+
+ /**
+ * Get an object that is a serialized representation of a History entry
+ * Used for data storage
+ * @param aEntry
+ * nsISHEntry instance
+ * @param aFullData
+ * always return privacy sensitive data (use with care)
+ * @param aIsPinned
+ * the tab is pinned and should be treated differently for privacy
+ * @returns object
+ */
+ _serializeHistoryEntry:
+ function sss_serializeHistoryEntry(aEntry, aFullData, aIsPinned) {
+ var entry = { url: aEntry.URI.spec,
+ triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL };
+
+ if (aEntry.title && aEntry.title != entry.url) {
+ entry.title = aEntry.title;
+ }
+ if (aEntry.isSubFrame) {
+ entry.subframe = true;
+ }
+ if (!(aEntry instanceof Ci.nsISHEntry)) {
+ return entry;
+ }
+
+ var cacheKey = aEntry.cacheKey;
+ if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 &&
+ cacheKey.data != 0) {
+ // XXXbz would be better to have cache keys implement
+ // nsISerializable or something.
+ entry.cacheKey = cacheKey.data;
+ }
+ entry.ID = aEntry.ID;
+ entry.docshellUUID = aEntry.docshellID.toString();
+
+ if (aEntry.referrerURI)
+ entry.referrer = aEntry.referrerURI.spec;
+
+ if (aEntry.contentType)
+ entry.contentType = aEntry.contentType;
+
+ var x = {}, y = {};
+ aEntry.getScrollPosition(x, y);
+ if (x.value != 0 || y.value != 0)
+ entry.scroll = x.value + "," + y.value;
+
+ try {
+ var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata");
+ if (aEntry.postData && (aFullData || prefPostdata &&
+ this._checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) {
+ aEntry.postData.QueryInterface(Ci.nsISeekableStream)
+ .seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
+ var stream = Cc["@mozilla.org/binaryinputstream;1"]
+ .createInstance(Ci.nsIBinaryInputStream);
+ stream.setInputStream(aEntry.postData);
+ var postBytes = stream.readByteArray(stream.available());
+ var postdata = String.fromCharCode.apply(null, postBytes);
+ if (aFullData || prefPostdata == -1 ||
+ postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <=
+ prefPostdata) {
+ // We can stop doing base64 encoding once our serialization into JSON
+ // is guaranteed to handle all chars in strings, including embedded
+ // nulls.
+ entry.postdata_b64 = btoa(postdata);
+ }
+ }
+ }
+ catch (ex) { debug(ex); } // POSTDATA is tricky - especially since some extensions don't get it right
+
+ // Collect triggeringPrincipal data for the current history entry.
+ // Please note that before Bug 1297338 there was no concept of a
+ // principalToInherit. To remain backward/forward compatible we
+ // serialize the principalToInherit as triggeringPrincipal_b64.
+ // Once principalToInherit is well established (within Gecko 55)
+ // we can update this code, remove triggeringPrincipal_b64 and
+ // just keep triggeringPrincipal_base64 as well as
+ // principalToInherit_base64.
+ if (aEntry.principalToInherit) {
+ try {
+ let principalToInherit = Utils.serializePrincipal(aEntry.principalToInherit);
+ if (principalToInherit) {
+ entry.triggeringPrincipal_b64 = principalToInherit;
+ entry.principalToInherit_base64 = principalToInherit;
+ }
+ } catch (e) {
+ debug(e);
+ }
+ }
+
+ if (aEntry.triggeringPrincipal) {
+ try {
+ let triggeringPrincipal = Utils.serializePrincipal(aEntry.triggeringPrincipal);
+ if (triggeringPrincipal) {
+ entry.triggeringPrincipal_base64 = triggeringPrincipal;
+ }
+ } catch (e) {
+ debug(e);
+ }
+ }
+
+ entry.docIdentifier = aEntry.BFCacheEntry.ID;
+
+ if (aEntry.stateData) {
+ entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
+ entry.structuredCloneVersion = aEntry.stateData.formatVersion;
+ }
+
+ if (!(aEntry instanceof Ci.nsISHContainer)) {
+ return entry;
+ }
+
+ if (aEntry.childCount > 0) {
+ entry.children = [];
+ for (var i = 0; i < aEntry.childCount; i++) {
+ var child = aEntry.GetChildAt(i);
+ if (child) {
+ entry.children.push(this._serializeHistoryEntry(child, aFullData, aIsPinned));
+ }
+ else { // to maintain the correct frame order, insert a dummy entry
+ entry.children.push({ url: "about:blank",
+ triggeringPrincipal_base64: Utils.SERIALIZED_SYSTEMPRINCIPAL});
+ }
+ // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595)
+ if (/^wyciwyg:\/\//.test(entry.children[i].url)) {
+ delete entry.children;
+ break;
+ }
+ }
+ }
+
+ return entry;
+ },
+
+ /**
+ * Updates all sessionStorage "super cookies"
+ * @param aTabData
+ * The data object for a specific tab
+ * @param aHistory
+ * That tab's session history
+ * @param aDocShell
+ * That tab's docshell (containing the sessionStorage)
+ * @param aFullData
+ * always return privacy sensitive data (use with care)
+ * @param aIsPinned
+ * the tab is pinned and should be treated differently for privacy
+ */
+ _serializeSessionStorage:
+ function sss_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData, aIsPinned) {
+ let storageData = {};
+ let hasContent = false;
+
+ for (let i = 0; i < aHistory.count; i++) {
+ let principal;
+ try {
+ let uri = aHistory.getEntryAtIndex(i).URI;
+ principal = SecMan.getDocShellCodebasePrincipal(uri, aDocShell);
+ }
+ catch (ex) {
+ // Chances are that this is getEntryAtIndex throwing, as seen in bug 669196.
+ // We've already asserted in _collectTabData, so we won't show that again.
+ continue;
+ }
+
+ // sessionStorage is saved per principal (cf. nsGlobalWindow::GetSessionStorage)
+ let origin;
+ try {
+ origin = principal.origin;
+ }
+ catch (ex) {
+ origin = principal.URI.spec;
+ }
+
+ if (storageData[origin])
+ continue;
+
+ let isHTTPS = principal.URI && principal.URI.schemeIs("https");
+ if (!(aFullData || this._checkPrivacyLevel(isHTTPS, aIsPinned)))
+ continue;
+
+ let storage, storageItemCount = 0;
+
+ let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ try {
+ let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
+ storage = storageManager.getStorage(window, principal);
+
+ // See Bug 1232955 - storage.length can throw, catch that failure here inside the try.
+ if (storage)
+ storageItemCount = storage.length;
+ }
+ catch (ex) { /* sessionStorage might throw if it's turned off, see bug 458954 */ }
+
+ if (storageItemCount == 0)
+ continue;
+
+ let data = storageData[origin] = {};
+
+ for (let j = 0; j < storageItemCount; j++) {
+ try {
+ let key = storage.key(j);
+ data[key] = storage.getItem(key);
+ }
+ catch (ex) { /* XXXzeniko this currently throws for secured items (cf. bug 442048) */ }
+ }
+ hasContent = true;
+ }
+
+ if (hasContent)
+ aTabData.storage = storageData;
+ },
+
+ /**
+ * go through all tabs and store the current scroll positions
+ * and innerHTML content of WYSIWYG editors
+ * @param aWindow
+ * Window reference
+ */
+ _updateTextAndScrollData: function sss_updateTextAndScrollData(aWindow) {
+ var browsers = aWindow.getBrowser().browsers;
+ for (var i = 0; i < browsers.length; i++) {
+ try {
+ var tabData = this._windows[aWindow.__SSi].tabs[i];
+ if (browsers[i].__SS_data &&
+ browsers[i].__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ continue; // ignore incompletely initialized tabs
+ this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
+ }
+ catch (ex) { debug(ex); } // get as much data as possible, ignore failures (might succeed the next time)
+ }
+ },
+
+ /**
+ * go through all frames and store the current scroll positions
+ * and innerHTML content of WYSIWYG editors
+ * @param aWindow
+ * Window reference
+ * @param aBrowser
+ * single browser reference
+ * @param aTabData
+ * tabData object to add the information to
+ * @param aFullData
+ * always return privacy sensitive data (use with care)
+ */
+ _updateTextAndScrollDataForTab:
+ function sss_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) {
+ var tabIndex = (aTabData.index || aTabData.entries.length) - 1;
+ // entry data needn't exist for tabs just initialized with an incomplete session state
+ if (!aTabData.entries[tabIndex])
+ return;
+
+ let selectedPageStyle = aBrowser.markupDocumentViewer.authorStyleDisabled ? "_nostyle" :
+ this._getSelectedPageStyle(aBrowser.contentWindow);
+ if (selectedPageStyle)
+ aTabData.pageStyle = selectedPageStyle;
+ else if (aTabData.pageStyle)
+ delete aTabData.pageStyle;
+
+ this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
+ aTabData.entries[tabIndex],
+ aFullData,
+ !!aTabData.pinned);
+ if (aBrowser.currentURI.spec == "about:config")
+ aTabData.entries[tabIndex].formdata = {
+ "#textbox": aBrowser.contentDocument.getElementById("textbox").value
+ };
+ },
+
+ /**
+ * go through all subframes and store all form data, the current
+ * scroll positions and innerHTML content of WYSIWYG editors
+ * @param aWindow
+ * Window reference
+ * @param aContent
+ * frame reference
+ * @param aData
+ * part of a tabData object to add the information to
+ * @param aFullData
+ * always return privacy sensitive data (use with care)
+ * @param aIsPinned
+ * the tab is pinned and should be treated differently for privacy
+ */
+ _updateTextAndScrollDataForFrame:
+ function sss_updateTextAndScrollDataForFrame(aWindow, aContent, aData,
+ aFullData, aIsPinned) {
+ for (var i = 0; i < aContent.frames.length; i++) {
+ if (aData.children && aData.children[i])
+ this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i],
+ aData.children[i],
+ aFullData, aIsPinned);
+ }
+ var isHTTPS = this._getURIFromString((aContent.parent || aContent).
+ document.location.href).schemeIs("https");
+ if (aFullData || this._checkPrivacyLevel(isHTTPS, aIsPinned) ||
+ aContent.top.document.location.href == "about:sessionrestore") {
+ let formData = this._collectFormDataForFrame(aContent.document);
+ if (formData)
+ aData.formdata = formData;
+ else if (aData.formdata)
+ delete aData.formdata;
+
+ // designMode is undefined e.g. for XUL documents (as about:config)
+ if ((aContent.document.designMode || "") == "on") {
+ if (aData.innerHTML === undefined && !aFullData) {
+ // we get no "input" events from iframes - listen for keypress here
+ aContent.addEventListener("keypress", this.saveStateDelayed.bind(this, aWindow, 3000), true);
+ }
+ aData.innerHTML = aContent.document.body.innerHTML;
+ }
+ }
+
+ // get scroll position from nsIDOMWindowUtils, since it allows avoiding a
+ // flush of layout
+ let domWindowUtils = aContent.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ let scrollX = {}, scrollY = {};
+ domWindowUtils.getScrollXY(false, scrollX, scrollY);
+ aData.scroll = scrollX.value + "," + scrollY.value;
+ },
+
+ /**
+ * determine the title of the currently enabled style sheet (if any)
+ * and recurse through the frameset if necessary
+ * @param aContent is a frame reference
+ * @returns the title style sheet determined to be enabled (empty string if none)
+ */
+ _getSelectedPageStyle: function sss_getSelectedPageStyle(aContent) {
+ const forScreen = /(?:^|,)\s*(?:all|screen)\s*(?:,|$)/i;
+ for (let i = 0; i < aContent.document.styleSheets.length; i++) {
+ let ss = aContent.document.styleSheets[i];
+ let media = ss.media.mediaText;
+ if (!ss.disabled && ss.title && (!media || forScreen.test(media)))
+ return ss.title
+ }
+ for (let i = 0; i < aContent.frames.length; i++) {
+ let selectedPageStyle = this._getSelectedPageStyle(aContent.frames[i]);
+ if (selectedPageStyle)
+ return selectedPageStyle;
+ }
+ return "";
+ },
+
+ /**
+ * collect the state of all form elements
+ * @param aDocument
+ * document reference
+ */
+ _collectFormDataForFrame: function sss_collectFormDataForFrame(aDocument) {
+ let formNodes = aDocument.evaluate(XPathGenerator.restorableFormNodes, aDocument,
+ XPathGenerator.resolveNS,
+ aDocument.defaultView.XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
+ let node = formNodes.iterateNext();
+ if (!node)
+ return null;
+
+ const MAX_GENERATED_XPATHS = 100;
+ let generatedCount = 0;
+
+ let data = {};
+ do {
+ let nId = node.id;
+ let hasDefaultValue = true;
+ let value;
+
+ // Only generate a limited number of XPath expressions for perf reasons (cf. bug 477564)
+ if (!nId && generatedCount > MAX_GENERATED_XPATHS)
+ continue;
+
+ if (ChromeUtils.getClassName(node) === "HTMLInputElement" ||
+ ChromeUtils.getClassName(node) === "HTMLTextAreaElement") {
+ switch (node.type) {
+ case "checkbox":
+ case "radio":
+ value = node.checked;
+ hasDefaultValue = value == node.defaultChecked;
+ break;
+ case "file":
+ value = { type: "file", fileList: node.mozGetFileNameArray() };
+ hasDefaultValue = !value.fileList.length;
+ break;
+ default: // text, textarea
+ value = node.value;
+ hasDefaultValue = value == node.defaultValue;
+ break;
+ }
+ }
+ else if (!node.multiple) {
+ // <select>s without the multiple attribute are hard to determine the
+ // default value, so assume we don't have the default.
+ hasDefaultValue = false;
+ value = node.selectedIndex;
+ }
+ else {
+ // <select>s with the multiple attribute are easier to determine the
+ // default value since each <option> has a defaultSelected
+ let options = Array.from(node.options, function(aOpt, aIx) {
+ let oSelected = aOpt.selected;
+ hasDefaultValue = hasDefaultValue && (oSelected == aOpt.defaultSelected);
+ return oSelected ? aIx : -1;
+ });
+ value = options.filter(aIx => aIx >= 0);
+ }
+ // In order to reduce XPath generation (which is slow), we only save data
+ // for form fields that have been changed. (cf. bug 537289)
+ if (!hasDefaultValue) {
+ if (nId) {
+ data["#" + nId] = value;
+ }
+ else {
+ generatedCount++;
+ data[XPathGenerator.generate(node)] = value;
+ }
+ }
+
+ } while ((node = formNodes.iterateNext()));
+
+ return data;
+ },
+
+ /**
+ * extract the base domain from a history entry and its children
+ * @param aEntry
+ * the history entry, serialized
+ * @param aHosts
+ * the hash that will be used to store hosts eg, { hostname: true }
+ * @param aCheckPrivacy
+ * should we check the privacy level for https
+ * @param aIsPinned
+ * is the entry we're evaluating for a pinned tab; used only if
+ * aCheckPrivacy
+ */
+ _extractHostsForCookiesFromEntry:
+ function sss__extractHostsForCookiesFromEntry(aEntry, aHosts, aCheckPrivacy, aIsPinned) {
+
+ if (aEntry.children) {
+ aEntry.children.forEach(function(entry) {
+ this._extractHostsForCookiesFromEntry(entry, aHosts, aCheckPrivacy, aIsPinned);
+ }, this);
+ }
+ },
+
+ /**
+ * extract the base domain from a host & scheme
+ * @param aHost
+ * the host of a uri (usually via nsIURI.host)
+ * @param aScheme
+ * the scheme of a uri (usually via nsIURI.scheme)
+ * @param aHosts
+ * the hash that will be used to store hosts eg, { hostname: true }
+ * @param aCheckPrivacy
+ * should we check the privacy level for https
+ * @param aIsPinned
+ * is the entry we're evaluating for a pinned tab; used only if
+ * aCheckPrivacy
+ */
+ _extractHostsForCookiesFromHostScheme:
+ function sss__extractHostsForCookiesFromHostScheme(aHost, aScheme, aHosts, aCheckPrivacy, aIsPinned) {
+ // host and scheme may not be set (for about: urls for example), in which
+ // case testing scheme will be sufficient.
+ if (/https?/.test(aScheme) && !aHosts[aHost] &&
+ (!aCheckPrivacy ||
+ this._checkPrivacyLevel(aScheme == "https", aIsPinned))) {
+ // By setting this to true or false, we can determine when looking at
+ // the host in _updateCookies if we should check for privacy.
+ aHosts[aHost] = aIsPinned;
+ }
+ else if (aScheme == "file") {
+ aHosts[aHost] = true;
+ }
+ },
+
+ /**
+ * Serialize cookie data
+ * @param aWindows
+ * JS object containing window data references
+ * { id: winData, etc. }
+ */
+ _updateCookies: function sss_updateCookies(aWindows) {
+ var jscookies = {};
+ // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
+ var MAX_EXPIRY = Math.pow(2, 62);
+
+ for (let window of aWindows) {
+ window.cookies = [];
+
+ // Collect all hosts for the current window.
+ let hosts = {};
+ window.tabs.forEach(function(tab) {
+ tab.entries.forEach(function(entry) {
+ this._extractHostsForCookiesFromEntry(entry, hosts, true, tab.pinned);
+ }, this);
+ }, this);
+
+ for (var [host, isPinned] of Object.entries(hosts)) {
+ try {
+ var list = Services.cookies.getCookiesFromHost(host, {});
+ while (list.hasMoreElements()) {
+ var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
+ // window._hosts will only have hosts with the right privacy rules,
+ // so there is no need to do anything special with this call to
+ // _checkPrivacyLevel.
+ if (cookie.isSession && this._checkPrivacyLevel(cookie.isSecure, isPinned)) {
+ // use the cookie's host, path, and name as keys into a hash,
+ // to make sure we serialize each cookie only once
+
+ // lazily build up a 3-dimensional hash, with
+ // host, path, and name as keys
+ if (!jscookies[cookie.host])
+ jscookies[cookie.host] = {};
+ if (!jscookies[cookie.host][cookie.path])
+ jscookies[cookie.host][cookie.path] = {};
+
+ if (!jscookies[cookie.host][cookie.path][cookie.name]) {
+ var jscookie = { "host": cookie.host, "value": cookie.value };
+ // only add attributes with non-default values (saving a few bits)
+ if (cookie.path)
+ jscookie.path = cookie.path;
+ if (cookie.name)
+ jscookie.name = cookie.name;
+ if (cookie.isSecure)
+ jscookie.secure = true;
+ if (cookie.isHttpOnly)
+ jscookie.httponly = true;
+ if (cookie.expiry < MAX_EXPIRY)
+ jscookie.expiry = cookie.expiry;
+ if (cookie.originAttributes)
+ jscookie.originAttributes = cookie.originAttributes;
+
+ jscookies[cookie.host][cookie.path][cookie.name] = jscookie;
+ }
+ window.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
+ }
+ }
+ }
+ catch (ex) {
+ debug("getCookiesFromHost failed. Host: " + host);
+ }
+ }
+
+ // don't include empty cookie sections
+ if (!window.cookies.length)
+ delete window.cookies;
+ }
+ },
+
+ /**
+ * Store window dimensions, visibility, sidebar
+ * @param aWindow
+ * Window reference
+ */
+ _updateWindowFeatures: function sss_updateWindowFeatures(aWindow) {
+ var winData = this._windows[aWindow.__SSi];
+
+ for (var aAttr in WINDOW_ATTRIBUTES)
+ winData[aAttr] = this._getWindowDimension(aWindow, aAttr);
+
+ var hidden = WINDOW_HIDEABLE_FEATURES.filter(function(aItem) {
+ return aWindow[aItem] && !aWindow[aItem].visible;
+ });
+ if (hidden.length != 0)
+ winData.hidden = hidden.join(",");
+ else if (winData.hidden)
+ delete winData.hidden;
+
+ var sidebar = aWindow.document.getElementById("sidebar-box").getAttribute("sidebarcommand");
+ if (sidebar)
+ winData.sidebar = sidebar;
+ else if (winData.sidebar)
+ delete winData.sidebar;
+ },
+
+ /**
+ * serialize session data as Ini-formatted string
+ * @param aUpdateAll
+ * Bool update all windows
+ * @returns string
+ */
+ _getCurrentState: function sss_getCurrentState(aUpdateAll) {
+ this._handleClosedWindows();
+
+ var activeWindow = this._getMostRecentBrowserWindow();
+
+ if (this._loadState == STATE_RUNNING) {
+ // update the data for all windows with activities since the last save operation
+ this._forEachBrowserWindow(function(aWindow) {
+ if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
+ return;
+ if (aUpdateAll || DirtyWindows.has(aWindow) || aWindow == activeWindow) {
+ this._collectWindowData(aWindow);
+ }
+ else { // always update the window features (whose change alone never triggers a save operation)
+ this._updateWindowFeatures(aWindow);
+ }
+ });
+ DirtyWindows.clear();
+ }
+
+ // collect the data for all windows
+ var total = [], ids = [];
+ var nonPopupCount = 0;
+ var ix;
+ for (ix in this._windows) {
+ if (this._windows[ix]._restoring) // window data is still in _statesToRestore
+ continue;
+ total.push(this._windows[ix]);
+ ids.push(ix);
+ if (!this._windows[ix].isPopup)
+ nonPopupCount++;
+ }
+ this._updateCookies(total);
+
+ // collect the data for all windows yet to be restored
+ for (ix in this._statesToRestore) {
+ for (let winData of this._statesToRestore[ix].windows) {
+ total.push(winData);
+ if (!winData.isPopup)
+ nonPopupCount++;
+ }
+ }
+
+ // shallow copy this._closedWindows to preserve current state
+ let lastClosedWindowsCopy = this._closedWindows.slice();
+
+ // If no non-popup browser window remains open, return the state of the last
+ // closed window(s). We only want to do this when we're actually "ending"
+ // the session.
+ //XXXzpao We should do this for _restoreLastWindow == true, but that has
+ // its own check for popups. c.f. bug 597619
+ if (AppConstants.platform != "macosx" &&
+ nonPopupCount == 0 && lastClosedWindowsCopy.length > 0 &&
+ this._loadState == STATE_QUITTING) {
+ // prepend the last non-popup browser window, so that if the user loads more tabs
+ // at startup we don't accidentally add them to a popup window
+ do {
+ total.unshift(lastClosedWindowsCopy.shift())
+ } while (total[0].isPopup)
+ }
+
+ if (activeWindow) {
+ this.activeWindowSSiCache = activeWindow.__SSi || "";
+ }
+ ix = ids.indexOf(this.activeWindowSSiCache);
+ // We don't want to restore focus to a minimized window.
+ if (ix != -1 && total[ix].sizemode == "minimized")
+ ix = -1;
+
+ let session = {
+ state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,
+ lastUpdate: Date.now(),
+ startTime: this._sessionStartTime,
+ recentCrashes: this._recentCrashes
+ };
+
+ return {
+ windows: total,
+ selectedWindow: ix + 1,
+ _closedWindows: lastClosedWindowsCopy,
+ session: session
+ };
+ },
+
+ /**
+ * serialize session data for a window
+ * @param aWindow
+ * Window reference
+ * @returns string
+ */
+ _getWindowState: function sss_getWindowState(aWindow) {
+ if (!this._isWindowLoaded(aWindow))
+ return this._statesToRestore[aWindow.__SS_restoreID];
+
+ if (this._loadState == STATE_RUNNING) {
+ this._collectWindowData(aWindow);
+ }
+
+ let windows = [this._windows[aWindow.__SSi]];
+ this._updateCookies(windows);
+
+ return { windows: windows };
+ },
+
+ _collectWindowData: function sss_collectWindowData(aWindow) {
+ if (!this._isWindowLoaded(aWindow))
+ return;
+
+ // update the internal state data for this window
+ this._saveWindowHistory(aWindow);
+ this._updateTextAndScrollData(aWindow);
+ this._updateWindowFeatures(aWindow);
+
+ // Make sure we keep __SS_lastSessionWindowID around for cases like entering
+ // or leaving PB mode.
+ if (aWindow.__SS_lastSessionWindowID)
+ this._windows[aWindow.__SSi].__lastSessionWindowID =
+ aWindow.__SS_lastSessionWindowID;
+
+ DirtyWindows.remove(aWindow);
+ },
+
+/* ........ Restoring Functionality .............. */
+
+ /**
+ * restore features to a single window
+ * @param aWindow
+ * Window reference
+ * @param aState
+ * JS object or its eval'able source
+ * @param aOverwriteTabs
+ * bool overwrite existing tabs w/ new ones
+ * @param aFollowUp
+ * bool this isn't the restoration of the first window
+ */
+ restoreWindow: function sss_restoreWindow(aWindow, aState, aOverwriteTabs, aFollowUp) {
+ if (!aFollowUp) {
+ this.windowToFocus = aWindow;
+ }
+ // initialize window if necessary
+ if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
+ this.onLoad(aWindow);
+
+ try {
+ var root = typeof aState == "string" ? JSON.parse(aState) : aState;
+ if (!root.windows[0]) {
+ this._sendRestoreCompletedNotifications();
+ return; // nothing to restore
+ }
+ }
+ catch (ex) { // invalid state object - don't restore anything
+ debug(ex);
+ this._sendRestoreCompletedNotifications();
+ return;
+ }
+
+ // We're not returning from this before we end up calling restoreHistoryPrecursor
+ // for this window, so make sure we send the SSWindowStateBusy event.
+ this._sendWindowStateEvent(aWindow, "Busy");
+
+ if (root._closedWindows)
+ this._closedWindows = root._closedWindows;
+
+ var winData;
+ if (!aState.selectedWindow || aState.selectedWindow > aState.windows.length) {
+ aState.selectedWindow = 0;
+ }
+ // open new windows for all further window entries of a multi-window session
+ // (unless they don't contain any tab data)
+ for (var w = 1; w < root.windows.length; w++) {
+ winData = root.windows[w];
+ if (winData && winData.tabs && winData.tabs[0]) {
+ var window = this._openWindowWithState({ windows: [winData] });
+ if (w == aState.selectedWindow - 1) {
+ this.windowToFocus = window;
+ }
+ }
+ }
+ winData = root.windows[0];
+ if (!winData.tabs) {
+ winData.tabs = [];
+ }
+ // don't restore a single blank tab when we've had an external
+ // URL passed in for loading at startup (cf. bug 357419)
+ else if (root._firstTabs && !aOverwriteTabs && winData.tabs.length == 1 &&
+ (!winData.tabs[0].entries || winData.tabs[0].entries.length == 0)) {
+ winData.tabs = [];
+ }
+
+ var tabbrowser = aWindow.getBrowser();
+ var openTabCount = aOverwriteTabs ? tabbrowser.browsers.length : -1;
+ var newTabCount = winData.tabs.length;
+ var tabs = [];
+
+ // disable smooth scrolling while adding, moving, removing and selecting tabs
+ var tabstrip = tabbrowser.tabContainer.arrowScrollbox;
+ var smoothScroll = tabstrip.smoothScroll;
+ tabstrip.smoothScroll = false;
+
+ // make sure that the selected tab won't be closed in order to
+ // prevent unnecessary flickering
+ if (aOverwriteTabs && tabbrowser.tabContainer.selectedIndex >= newTabCount)
+ tabbrowser.moveTabTo(tabbrowser.selectedTab, newTabCount - 1);
+
+ for (var t = 0; t < newTabCount; t++) {
+ tabs.push(t < openTabCount ?
+ tabbrowser.tabs[t] :
+ // Ftr, SeaMonkey doesn't support animation (yet).
+ tabbrowser.addTab("about:blank"));
+ // when resuming at startup: add additionally requested pages to the end
+ if (!aOverwriteTabs && root._firstTabs) {
+ tabbrowser.moveTabTo(tabs[t], t);
+ }
+ }
+
+ // If overwriting tabs, we want to reset each tab's "restoring" state. Since
+ // we're overwriting those tabs, they should no longer be restoring. The
+ // tabs will be rebuilt and marked if they need to be restored after loading
+ // state (in restoreHistoryPrecursor).
+ if (aOverwriteTabs) {
+ for (let i = 0; i < tabbrowser.tabs.length; i++) {
+ if (tabbrowser.browsers[i].__SS_restoreState)
+ this._resetTabRestoringState(tabbrowser.tabs[i]);
+ }
+ }
+
+ // We want to set up a counter on the window that indicates how many tabs
+ // in this window are unrestored. This will be used in restoreNextTab to
+ // determine if gRestoreTabsProgressListener should be removed from the window.
+ // If we aren't overwriting existing tabs, then we want to add to the existing
+ // count in case there are still tabs restoring.
+ if (!aWindow.__SS_tabsToRestore)
+ aWindow.__SS_tabsToRestore = 0;
+ if (aOverwriteTabs)
+ aWindow.__SS_tabsToRestore = newTabCount;
+ else
+ aWindow.__SS_tabsToRestore += newTabCount;
+
+ // We want to correlate the window with data from the last session, so
+ // assign another id if we have one. Otherwise clear so we don't do
+ // anything with it.
+ delete aWindow.__SS_lastSessionWindowID;
+ if (winData.__lastSessionWindowID)
+ aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
+
+ // when overwriting tabs, remove all superflous ones
+ for (t = openTabCount - 1; t >= newTabCount; t--) {
+ tabbrowser.removeTab(tabbrowser.tabs[t]);
+ }
+
+ if (aOverwriteTabs) {
+ this.restoreWindowFeatures(aWindow, winData);
+ delete this._windows[aWindow.__SSi].extData;
+ }
+ if (winData.cookies) {
+ this.restoreCookies(winData.cookies);
+ }
+ if (winData.extData) {
+ if (!this._windows[aWindow.__SSi].extData) {
+ this._windows[aWindow.__SSi].extData = {};
+ }
+ for (var key in winData.extData) {
+ this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
+ }
+ }
+ if (aOverwriteTabs || root._firstTabs) {
+ this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
+ }
+
+ this.restoreHistoryPrecursor(aWindow, tabs, winData.tabs,
+ (aOverwriteTabs ? (parseInt(winData.selected) || 1) : 0), 0, 0);
+
+ // set smoothScroll back to the original value
+ tabstrip.smoothScroll = smoothScroll;
+
+ this._sendRestoreCompletedNotifications();
+ },
+
+ /**
+ * Manage history restoration for a window
+ * @param aWindow
+ * Window to restore the tabs into
+ * @param aTabs
+ * Array of tab references
+ * @param aTabData
+ * Array of tab data
+ * @param aSelectTab
+ * Index of selected tab
+ * @param aIx
+ * Index of the next tab to check readyness for
+ * @param aCount
+ * Counter for number of times delaying b/c browser or history aren't ready
+ */
+ restoreHistoryPrecursor:
+ function sss_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount) {
+ var tabbrowser = aWindow.getBrowser();
+
+ // make sure that all browsers and their histories are available
+ // - if one's not, resume this check in 100ms (repeat at most 10 times)
+ for (var t = aIx; t < aTabs.length; t++) {
+ try {
+ if (!tabbrowser.getBrowserForTab(aTabs[t]).webNavigation.sessionHistory) {
+ throw new Error();
+ }
+ }
+ catch (ex) { // in case browser or history aren't ready yet
+ if (aCount < 10) {
+ var restoreHistoryFunc = function(self) {
+ self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount + 1);
+ }
+ aWindow.setTimeout(restoreHistoryFunc, 100, this);
+ return;
+ }
+ }
+ }
+
+ if (!this._isWindowLoaded(aWindow)) {
+ // from now on, the data will come from the actual window
+ delete this._statesToRestore[aWindow.__SS_restoreID];
+ delete aWindow.__SS_restoreID;
+ delete this._windows[aWindow.__SSi]._restoring;
+
+ // It's important to set the window state to dirty so that
+ // we collect their data for the first time when saving state.
+ DirtyWindows.add(aWindow);
+ }
+
+ if (aTabs.length == 0) {
+ // this is normally done in restoreHistory() but as we're returning early
+ // here we need to take care of it.
+ this._sendWindowStateEvent(aWindow, "Ready");
+ return;
+ }
+
+ if (aTabs.length > 1) {
+ // Load hidden tabs last, by pushing them to the end of the list
+ let unhiddenTabs = aTabs.length;
+ for (let t = 0; t < unhiddenTabs; ) {
+ if (aTabData[t].hidden) {
+ aTabs = aTabs.concat(aTabs.splice(t, 1));
+ aTabData = aTabData.concat(aTabData.splice(t, 1));
+ if (aSelectTab > t)
+ --aSelectTab;
+ --unhiddenTabs;
+ continue;
+ }
+ ++t;
+ }
+
+ // Determine if we can optimize & load visible tabs first
+ let maxVisibleTabs = Math.ceil(tabbrowser.tabContainer.arrowScrollbox.scrollClientSize /
+ aTabs[unhiddenTabs - 1].getBoundingClientRect().width);
+
+ // make sure we restore visible tabs first, if there are enough
+ if (maxVisibleTabs < unhiddenTabs && aSelectTab > 1) {
+ let firstVisibleTab = 0;
+ if (unhiddenTabs - maxVisibleTabs > aSelectTab) {
+ // aSelectTab is leftmost since we scroll to it when possible
+ firstVisibleTab = aSelectTab - 1;
+ } else {
+ // aSelectTab is rightmost or no more room to scroll right
+ firstVisibleTab = unhiddenTabs - maxVisibleTabs;
+ }
+ aTabs = aTabs.splice(firstVisibleTab, maxVisibleTabs).concat(aTabs);
+ aTabData = aTabData.splice(firstVisibleTab, maxVisibleTabs).concat(aTabData);
+ aSelectTab -= firstVisibleTab;
+ }
+ }
+
+ // make sure to restore the selected tab first (if any)
+ if (aSelectTab-- && aTabs[aSelectTab]) {
+ aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
+ aTabData.unshift(aTabData.splice(aSelectTab, 1)[0]);
+ tabbrowser.selectedTab = aTabs[0];
+ }
+
+ // Prepare the tabs so that they can be properly restored. We'll pin/unpin
+ // and show/hide tabs as necessary. We'll also set the labels, user typed
+ // value, and attach a copy of the tab's data in case we close it before
+ // it's been restored.
+ for (t = 0; t < aTabs.length; t++) {
+ let tab = aTabs[t];
+ let browser = tabbrowser.getBrowserForTab(tab);
+ let tabData = aTabData[t];
+
+ if (tabData.hidden) {
+ tab.setAttribute("hidden", true);
+ } else {
+ if (tab.hidden) {
+ tab.removeAttribute("hidden");
+ }
+ }
+
+ for (let name in tabData.attributes)
+ this.xulAttributes[name] = true;
+
+ // keep the data around to prevent dataloss in case
+ // a tab gets closed before it's been properly restored
+ browser.__SS_data = tabData;
+ browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
+
+ // Make sure that set/getTabValue will set/read the correct data by
+ // wiping out any current value in tab.__SS_extdata.
+ delete tab.__SS_extdata;
+
+ if (!tabData.entries || tabData.entries.length == 0) {
+ // make sure to blank out this tab's content
+ // (just purging the tab's history won't be enough)
+ browser.contentDocument.location = "about:blank";
+ continue;
+ }
+
+ browser.stop(); // in case about:blank isn't done yet
+
+ // wall-paper fix for bug 439675: make sure that the URL to be loaded
+ // is always visible in the address bar
+ let activeIndex = (tabData.index || tabData.entries.length) - 1;
+ let activePageData = tabData.entries[activeIndex] || null;
+ let uri = activePageData ? activePageData.url || null : null;
+
+ // NB: we won't set initial URIs (about:blank, about:privatebrowsing, etc.)
+ // here because their load will not normally trigger a location bar clearing
+ // when they finish loading (to avoid race conditions where we then
+ // clear user input instead), so we shouldn't set them here either.
+ // They also don't fall under the issues in bug 439675 where user input
+ // needs to be preserved if the load doesn't succeed.
+ if (!browser.userTypedValue && uri && !aWindow.gInitialPages.has(uri)) {
+ browser.userTypedValue = uri;
+ }
+
+ // Also make sure currentURI is set so that switch-to-tab works before
+ // the tab is restored. We'll reset this to about:blank when we try to
+ // restore the tab to ensure that docshell doeesn't get confused.
+ if (uri)
+ browser.docShell.setCurrentURI(this._getURIFromString(uri));
+
+ // If the page has a title, set it.
+ if (activePageData) {
+ if (activePageData.title) {
+ tab.label = activePageData.title;
+ tab.crop = "end";
+ } else if (activePageData.url != "about:blank") {
+ tab.label = activePageData.url;
+ tab.crop = "center";
+ }
+ }
+ }
+
+ // helper hashes for ensuring unique frame IDs and unique document
+ // identifiers.
+ var idMap = { used: {} };
+ var docIdentMap = {};
+ this.restoreHistory(aWindow, aTabs, aTabData, idMap, docIdentMap);
+ },
+
+ /**
+ * Restore history for a window
+ * @param aWindow
+ * Window reference
+ * @param aTabs
+ * Array of tab references
+ * @param aTabData
+ * Array of tab data
+ * @param aIdMap
+ * Hash for ensuring unique frame IDs
+ */
+ restoreHistory:
+ function sss_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap) {
+ // if the tab got removed before being completely restored, then skip it
+ while (aTabs.length > 0 && (!aTabs[0].parentNode || !aTabs[0].linkedBrowser)) {
+ aTabs.shift();
+ aTabData.shift();
+ }
+ if (aTabs.length == 0) {
+ // At this point we're essentially ready for consumers to read/write data
+ // via the sessionstore API so we'll send the SSWindowStateReady event.
+ this._sendWindowStateEvent(aWindow, "Ready");
+ return; // no more tabs to restore
+ }
+
+ var tab = aTabs.shift();
+ var tabData = aTabData.shift();
+
+ var browser = aWindow.getBrowser().getBrowserForTab(tab);
+ var history = browser.webNavigation.sessionHistory;
+
+ if (history.count > 0) {
+ history.PurgeHistory(history.count);
+ }
+
+ browser.__SS_shistoryListener = new SessionStoreSHistoryListener(this, tab);
+ history.addSHistoryListener(browser.__SS_shistoryListener);
+
+ if (!tabData.entries) {
+ tabData.entries = [];
+ }
+ if (tabData.extData) {
+ tab.__SS_extdata = {};
+ for (let key in tabData.extData)
+ tab.__SS_extdata[key] = tabData.extData[key];
+ }
+ else
+ delete tab.__SS_extdata;
+
+ for (var i = 0; i < tabData.entries.length; i++) {
+ let cloneEntry = false;
+ //XXXzpao Wallpaper patch for bug 509315
+ if (!tabData.entries[i].url)
+ continue;
+
+ let shEntry = this._deserializeHistoryEntry(tabData.entries[i],
+ aIdMap, aDocIdentMap);
+ try {
+ history.addEntry(shEntry, true);
+ }
+ catch (ex) {
+ cloneEntry = true;
+ }
+
+ // Workaround for bug 1466911.
+ // FIXME Remove this after the issue which caused the exception above
+ // to be thrown has been fixed.
+ if (cloneEntry) {
+ shEntry = shEntry.clone();
+ shEntry.abandonBFCacheEntry();
+
+ try {
+ history.addEntry(shEntry, true);
+ }
+ catch (ex) {
+ Cu.reportError(ex);
+ }
+ }
+ }
+
+ // make sure to reset the capabilities and attributes, in case this tab gets reused
+ var disallow = (tabData.disallow)?tabData.disallow.split(","):[];
+ CAPABILITIES.forEach(function(aCapability) {
+ browser.docShell["allow" + aCapability] = !disallow.includes(aCapability);
+ });
+ for (let name in this.xulAttributes)
+ tab.removeAttribute(name);
+ for (let name in tabData.attributes)
+ tab.setAttribute(name, tabData.attributes[name]);
+
+ if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell)
+ this._deserializeSessionStorage(tabData.storage, browser.docShell);
+
+ // notify the tabbrowser that the tab chrome has been restored
+ var event = aWindow.document.createEvent("Events");
+ event.initEvent("SSTabRestoring", true, false);
+ tab.dispatchEvent(event);
+
+ // Restore the history in the next tab
+ Services.tm.mainThread.dispatch(this.restoreHistory.bind(this, aWindow,
+ aTabs, aTabData, aIdMap, aDocIdentMap), Ci.nsIThread.DISPATCH_NORMAL);
+
+ // This could cause us to ignore the max_concurrent_tabs pref a bit, but
+ // it ensures each window will have its selected tab loaded.
+ if (aWindow.getBrowser().selectedBrowser == browser) {
+ this.restoreTab(tab);
+ }
+ else {
+ // Put the tab into the right bucket
+ if (tabData.hidden)
+ this._tabsToRestore.hidden.push(tab);
+ else
+ this._tabsToRestore.visible.push(tab);
+ this.restoreNextTab();
+ }
+ },
+
+ /**
+ * Restores the specified tab. If the tab can't be restored (eg, no history or
+ * calling gotoIndex fails), then state changes will be rolled back.
+ * This method will check if gTabsProgressListener is attached to the tab's
+ * window, ensuring that we don't get caught without one.
+ * This method removes the session history listener right before starting to
+ * attempt a load. This will prevent cases of "stuck" listeners.
+ * If this method returns false, then it is up to the caller to decide what to
+ * do. In the common case (restoreNextTab), we will want to then attempt to
+ * restore the next tab. In the other case (selecting the tab, reloading the
+ * tab), the caller doesn't actually want to do anything if no page is loaded.
+ *
+ * @param aTab
+ * the tab to restore
+ *
+ * @returns true/false indicating whether or not a load actually happened
+ */
+ restoreTab: function sss_restoreTab(aTab) {
+ let window = aTab.ownerDocument.defaultView;
+ let browser = aTab.linkedBrowser;
+ let tabData = browser.__SS_data;
+
+ // If the tabData which we're sending down has any sessionStorage associated
+ // with it, we need to send down permissions for the domains, as this
+ // information will be needed to correctly restore the session.
+ if (tabData.storage) {
+ for (let origin of Object.getOwnPropertyNames(tabData.storage)) {
+ try {
+ let {frameLoader} = browser;
+ if (frameLoader.tabParent) {
+ let attrs = browser.contentPrincipal.originAttributes;
+ let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
+ let principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
+ frameLoader.tabParent.transmitPermissionsForPrincipal(principal);
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+
+ // There are cases within where we haven't actually started a load. In that
+ // that case we'll reset state changes we made and return false to the caller
+ // can handle appropriately.
+ let didStartLoad = false;
+
+ // Make sure that the tabs progress listener is attached to this window
+ this._ensureTabsProgressListener(window);
+
+ // Make sure that this tab is removed from _tabsToRestore
+ this._removeTabFromTabsToRestore(aTab);
+
+ // Increase our internal count.
+ this._tabsRestoringCount++;
+
+ // Set this tab's state to restoring
+ browser.__SS_restoreState = TAB_STATE_RESTORING;
+
+ // Remove the history listener, since we no longer need it once we start restoring
+ this._removeSHistoryListener(aTab);
+
+ let activeIndex = (tabData.index || tabData.entries.length) - 1;
+ if (activeIndex >= tabData.entries.length)
+ activeIndex = tabData.entries.length - 1;
+
+ // Reset currentURI.
+ browser.webNavigation.setCurrentURI(this._getURIFromString("about:blank"));
+
+ // Attach data that will be restored on "load" event, after tab is restored.
+ if (activeIndex > -1) {
+ // restore those aspects of the currently active documents which are not
+ // preserved in the plain history entries (mainly scroll state and text data)
+ browser.__SS_restore_data = tabData.entries[activeIndex] || {};
+ browser.__SS_restore_pageStyle = tabData.pageStyle || "";
+ browser.__SS_restore_tab = aTab;
+
+ didStartLoad = true;
+ try {
+ // In order to work around certain issues in session history, we need to
+ // force session history to update its internal index and call reload
+ // instead of gotoIndex. See bug 597315.
+ var sessionHistory = browser.webNavigation.sessionHistory;
+ sessionHistory.index = activeIndex;
+ sessionHistory.reloadCurrentEntry();
+ }
+ catch (ex) {
+ // ignore page load errors
+ aTab.removeAttribute("busy");
+ didStartLoad = false;
+ }
+ }
+
+ // Handle userTypedValue. Setting userTypedValue seems to update gURLbar
+ // as needed. Calling loadURI will cancel form filling in restoreDocument
+ if (tabData.userTypedValue) {
+ browser.userTypedValue = tabData.userTypedValue;
+
+ if (tabData.userTypedClear) {
+ // Make it so that we'll enter restoreDocument on page load. We will
+ // fire SSTabRestored from there. We don't have any form data to
+ // restore so we can just set the URL to null.
+ browser.__SS_restore_data = { url: null };
+ browser.__SS_restore_tab = aTab;
+ didStartLoad = true;
+ browser.webNavigation
+ .loadURI(tabData.userTypedValue,
+ Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
+ null, null, null,
+ Services.scriptSecurityManager.getSystemPrincipal());
+ }
+ }
+
+ // If we didn't start a load, then we won't reset this tab through the usual
+ // channel (via the progress listener), so reset the tab ourselves. We will
+ // also send SSTabRestored since this tab has technically been restored.
+ if (!didStartLoad) {
+ this._sendTabRestoredNotification(aTab);
+ this._resetTabRestoringState(aTab);
+ }
+
+ return didStartLoad;
+ },
+
+ /**
+ * This _attempts_ to restore the next available tab. If the restore fails,
+ * then we will attempt the next one.
+ * There are conditions where this won't do anything:
+ * if we're in the process of quitting
+ * if there are no tabs to restore
+ * if we have already reached the limit for number of tabs to restore
+ */
+ restoreNextTab: function sss_restoreNextTab() {
+ // If we call in here while quitting, we don't actually want to do anything
+ if (this._loadState == STATE_QUITTING)
+ return;
+
+ // If it's not possible to restore anything, then just bail out.
+ if (this._maxConcurrentTabRestores >= 0 &&
+ this._tabsRestoringCount >= this._maxConcurrentTabRestores)
+ return;
+
+ // Look in visible, then hidden
+ let nextTabArray;
+ if (this._tabsToRestore.visible.length) {
+ nextTabArray = this._tabsToRestore.visible;
+ }
+ else if (this._tabsToRestore.hidden.length) {
+ nextTabArray = this._tabsToRestore.hidden;
+ }
+
+ if (nextTabArray) {
+ let tab = nextTabArray.shift();
+ let didStartLoad = this.restoreTab(tab);
+ // If we don't start a load in the restored tab (eg, no entries) then we
+ // want to attempt to restore the next tab.
+ if (!didStartLoad)
+ this.restoreNextTab();
+ }
+ },
+
+ /**
+ * expands serialized history data into a session-history-entry instance
+ * @param aEntry
+ * Object containing serialized history data for a URL
+ * @param aIdMap
+ * Hash for ensuring unique frame IDs
+ * @returns nsISHEntry
+ */
+ _deserializeHistoryEntry:
+ function sss_deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) {
+
+ var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"]
+ .createInstance(Ci.nsISHEntry);
+
+ shEntry.URI = this._getURIFromString(aEntry.url);
+ shEntry.title = aEntry.title || aEntry.url;
+ if (aEntry.subframe)
+ shEntry.isSubFrame = aEntry.subframe || false;
+ shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
+ if (aEntry.contentType)
+ shEntry.contentType = aEntry.contentType;
+ if (aEntry.referrer)
+ shEntry.referrerURI = this._getURIFromString(aEntry.referrer);
+
+ if (aEntry.cacheKey) {
+ var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"]
+ .createInstance(Ci.nsISupportsPRUint32);
+ cacheKey.data = aEntry.cacheKey;
+ shEntry.cacheKey = cacheKey;
+ }
+
+ if (aEntry.ID) {
+ // get a new unique ID for this frame (since the one from the last
+ // start might already be in use)
+ var id = aIdMap[aEntry.ID] || 0;
+ if (!id) {
+ for (id = Date.now(); id in aIdMap.used; id++);
+ aIdMap[aEntry.ID] = id;
+ aIdMap.used[id] = true;
+ }
+ shEntry.ID = id;
+ }
+
+ // If we have the legacy docshellID on our entry, upgrade it to a
+ // docshellUUID by going through the mapping.
+ if (aEntry.docshellID) {
+ if (!this._docshellUUIDMap.has(aEntry.docshellID)) {
+ // Convert the nsID to a string so that the docshellUUID property
+ // is correctly stored as a string.
+ this._docshellUUIDMap.set(aEntry.docshellID,
+ uuidGenerator.generateUUID().toString());
+ }
+ aEntry.docshellUUID = this._docshellUUIDMap.get(aEntry.docshellID);
+ delete aEntry.docshellID;
+ }
+
+ if (aEntry.docshellUUID)
+ shEntry.docshellID = Components.ID(aEntry.docshellUUID);
+
+ if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
+ shEntry.stateData =
+ Cc["@mozilla.org/docshell/structured-clone-container;1"]
+ .createInstance(Ci.nsIStructuredCloneContainer);
+
+ shEntry.stateData.initFromBase64(aEntry.structuredCloneState,
+ aEntry.structuredCloneVersion);
+ }
+
+ if (aEntry.scroll) {
+ var scrollPos = (aEntry.scroll || "0,0").split(",");
+ scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
+ shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
+ }
+
+ if (aEntry.postdata_b64) {
+ var postdata = atob(aEntry.postdata_b64);
+ var stream = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ stream.setData(postdata, postdata.length);
+ shEntry.postData = stream;
+ }
+
+ let childDocIdents = {};
+ if (aEntry.docIdentifier) {
+ // If we have a serialized document identifier, try to find an SHEntry
+ // which matches that doc identifier and adopt that SHEntry's
+ // BFCacheEntry. If we don't find a match, insert shEntry as the match
+ // for the document identifier.
+ let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
+ if (!matchingEntry) {
+ matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents};
+ aDocIdentMap[aEntry.docIdentifier] = matchingEntry;
+ }
+ else {
+ shEntry.adoptBFCacheEntry(matchingEntry.shEntry);
+ childDocIdents = matchingEntry.childDocIdents;
+ }
+ }
+
+ // The field entry.owner_b64 got renamed to entry.triggeringPricipal_b64 in
+ // Bug 1286472 and Bug 1334780 for SeaMonkey.
+ // To remain backward compatible we still have to support that field for a
+ // few cycles before we can remove it.
+ if (aEntry.owner_b64) {
+ aEntry.triggeringPricipal_b64 = aEntry.owner_b64;
+ delete aEntry.owner_b64;
+ }
+
+ // Before introducing the concept of principalToInherit we only had
+ // a triggeringPrincipal within every entry which basically is the
+ // equivalent of the new principalToInherit. To avoid compatibility
+ // issues, we first check if the entry has entries for
+ // triggeringPrincipal_base64 and principalToInherit_base64. If not
+ // we fall back to using the principalToInherit (which is stored
+ // as triggeringPrincipal_b64) as the triggeringPrincipal and
+ // the principalToInherit.
+ // FF55 will remove the triggeringPrincipal_b64, see Bug 1301666.
+ if (aEntry.triggeringPrincipal_base64 || aEntry.principalToInherit_base64) {
+ if (aEntry.triggeringPrincipal_base64) {
+ shEntry.triggeringPrincipal =
+ Utils.deserializePrincipal(aEntry.triggeringPrincipal_base64);
+ }
+ if (aEntry.principalToInherit_base64) {
+ shEntry.principalToInherit =
+ Utils.deserializePrincipal(aEntry.principalToInherit_base64);
+ }
+ } else if (aEntry.triggeringPrincipal_b64) {
+ shEntry.triggeringPrincipal = Utils.deserializePrincipal(aEntry.triggeringPrincipal_b64);
+ shEntry.principalToInherit = shEntry.triggeringPrincipal;
+ }
+
+ if (aEntry.children && shEntry instanceof Ci.nsISHContainer) {
+ for (var i = 0; i < aEntry.children.length; i++) {
+ //XXXzpao Wallpaper patch for bug 509315
+ if (!aEntry.children[i].url)
+ continue;
+
+ // We're mysteriously getting sessionrestore.js files with a cycle in
+ // the doc-identifier graph. (That is, we have an entry where doc
+ // identifier A is an ancestor of doc identifier B, and another entry
+ // where doc identifier B is an ancestor of A.)
+ //
+ // If we were to respect these doc identifiers, we'd create a cycle in
+ // the SHEntries themselves, which causes the docshell to loop forever
+ // when it looks for the root SHEntry.
+ //
+ // So as a hack to fix this, we restrict the scope of a doc identifier
+ // to be a node's siblings and cousins, and pass childDocIdents, not
+ // aDocIdents, to _deserializeHistoryEntry. That is, we say that two
+ // SHEntries with the same doc identifier have the same document iff
+ // they have the same parent or their parents have the same document.
+
+ shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap,
+ childDocIdents), i);
+ }
+ }
+
+ return shEntry;
+ },
+
+ /**
+ * restores all sessionStorage "super cookies"
+ * @param aStorageData
+ * Storage data to be restored
+ * @param aDocShell
+ * A tab's docshell (containing the sessionStorage)
+ */
+ _deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
+
+ for (let origin of Object.keys(aStorageData)) {
+ let data = aStorageData[origin];
+
+ let principal;
+
+ try {
+ // NOTE: We record the full origin for the URI which the
+ // sessionStorage is being captured for. As of bug 1319114 this code
+ // stopped parsing any origins which have originattributes correctly, as
+ // it decided to use the origin attributes from the docshell, and try to
+ // interpret the origin as a URI. Since bug 1473426 code now correctly
+ // parses the full origin, and then discards the origin attributes, to
+ // make the behavior line up with the original intentions in bug 1235657
+ // while preserving the ability to read all session storage from
+ // previous versions. In the future, if this behavior is desired, we may
+ // want to use the spec instead of the origin as the key, and avoid
+ // transmitting origin attribute information which we then discard when
+ // restoring.
+ //
+ // If changing this logic, make sure to also change the principal
+ // computation logic in restoretab.
+ let attrs = aDocShell.getOriginAttributes();
+ let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
+ principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
+ } catch (e) {
+ Cu.reportError(e);
+ continue;
+ }
+
+ let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
+ let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+
+ // There is no need to pass documentURI, it's only used to fill documentURI property of
+ // domstorage event, which in this case has no consumer. Prevention of events in case
+ // of missing documentURI will be solved in a followup bug to bug 600307.
+ let storage = storageManager.createStorage(window, principal, "");
+
+ for (let key of Object.keys(data)) {
+ try {
+ storage.setItem(key, data[key]);
+ } catch (e) {
+ // Throws e.g. for URIs that can't have sessionStorage.
+ Cu.reportError(e);
+ }
+ }
+ }
+ },
+
+ /**
+ * Restore properties to a loaded document
+ */
+ restoreDocument: function sss_restoreDocument(aWindow, aBrowser, aEvent) {
+ // wait for the top frame to be loaded completely
+ if (!aEvent || !aEvent.originalTarget || !aEvent.originalTarget.defaultView || aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) {
+ return;
+ }
+
+ // always call this before injecting content into a document!
+ function hasExpectedURL(aDocument, aURL) {
+ return !aURL || aURL.replace(/#.*/, "") == aDocument.location.href.replace(/#.*/, "");
+ }
+
+ function restoreFormData(aDocument, aData, aURL) {
+ for (let key in aData) {
+ if (!hasExpectedURL(aDocument, aURL))
+ return;
+
+ let node = key.charAt(0) == "#" ? aDocument.getElementById(key.slice(1)) :
+ XPathGenerator.resolve(aDocument, key);
+ if (!node)
+ continue;
+
+ let eventType;
+ let value = aData[key];
+ if (typeof value == "string" && node.type != "file") {
+ if (node.value == value)
+ continue; // don't dispatch an input event for no change
+
+ node.value = value;
+ eventType = "input";
+ }
+ else if (typeof value == "boolean") {
+ if (node.checked == value)
+ continue; // don't dispatch a change event for no change
+
+ node.checked = value;
+ eventType = "change";
+ }
+ else if (typeof value == "number") {
+ // We saved the value blindly since selects take more work to determine
+ // default values. So now we should check to avoid unnecessary events.
+ if (node.selectedIndex == value)
+ continue;
+
+ try {
+ node.selectedIndex = value;
+ eventType = "change";
+ } catch (ex) { /* throws for invalid indices */ }
+ }
+ else if (value && value.fileList && value.type == "file" && node.type == "file") {
+ node.mozSetFileNameArray(value.fileList, value.fileList.length);
+ eventType = "input";
+ }
+ else if (value && typeof value.indexOf == "function" && node.options) {
+ Array.from(node.options).forEach(function(aOpt, aIx) {
+ aOpt.selected = value.includes(aIx);
+
+ // Only fire the event here if this wasn't selected by default
+ if (!aOpt.defaultSelected)
+ eventType = "change";
+ });
+ }
+
+ // Fire events for this node if applicable
+ if (eventType) {
+ let event = aDocument.createEvent("UIEvents");
+ event.initUIEvent(eventType, true, true, aDocument.defaultView, 0);
+ node.dispatchEvent(event);
+ }
+ }
+ }
+
+ let selectedPageStyle = aBrowser.__SS_restore_pageStyle;
+ function restoreTextDataAndScrolling(aContent, aData, aPrefix) {
+ if (aData.formdata)
+ restoreFormData(aContent.document, aData.formdata, aData.url);
+ if (aData.innerHTML) {
+ aWindow.setTimeout(function() {
+ if (aContent.document.designMode == "on" &&
+ hasExpectedURL(aContent.document, aData.url)) {
+ aContent.document.body.innerHTML = aData.innerHTML;
+ }
+ }, 0);
+ }
+ var match;
+ if (aData.scroll && (match = /(\d+),(\d+)/.exec(aData.scroll)) != null) {
+ aContent.scrollTo(match[1], match[2]);
+ }
+ Array.from(aContent.document.styleSheets).forEach(function(aSS) {
+ aSS.disabled = aSS.title && aSS.title != selectedPageStyle;
+ });
+ for (var i = 0; i < aContent.frames.length; i++) {
+ if (aData.children && aData.children[i] &&
+ hasExpectedURL(aContent.document, aData.url)) {
+ restoreTextDataAndScrolling(aContent.frames[i], aData.children[i], aPrefix + i + "|");
+ }
+ }
+ }
+
+ // don't restore text data and scrolling state if the user has navigated
+ // away before the loading completed (except for in-page navigation)
+ if (hasExpectedURL(aEvent.originalTarget, aBrowser.__SS_restore_data.url)) {
+ var content = aEvent.originalTarget.defaultView;
+ restoreTextDataAndScrolling(content, aBrowser.__SS_restore_data, "");
+ aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
+ }
+
+ // notify the tabbrowser that this document has been completely restored
+ this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
+
+ delete aBrowser.__SS_restore_data;
+ delete aBrowser.__SS_restore_pageStyle;
+ delete aBrowser.__SS_restore_tab;
+ },
+
+ /**
+ * Restore visibility and dimension features to a window
+ * @param aWindow
+ * Window reference
+ * @param aWinData
+ * Object containing session data for the window
+ */
+ restoreWindowFeatures: function sss_restoreWindowFeatures(aWindow, aWinData) {
+ var hidden = (aWinData.hidden)?aWinData.hidden.split(","):[];
+ WINDOW_HIDEABLE_FEATURES.forEach(function(aItem) {
+ aWindow[aItem].visible = !hidden.includes(aItem);
+ });
+
+ if (aWinData.isPopup)
+ this._windows[aWindow.__SSi].isPopup = true;
+ else
+ delete this._windows[aWindow.__SSi].isPopup;
+
+ Services.tm.mainThread.dispatch(this.restoreDimensions.bind(this, aWindow,
+ +aWinData.width || 0,
+ +aWinData.height || 0,
+ "screenX" in aWinData ? +aWinData.screenX : NaN,
+ "screenY" in aWinData ? +aWinData.screenY : NaN,
+ aWinData.sizemode || "", aWinData.sidebar || ""),
+ Ci.nsIThread.DISPATCH_NORMAL);
+ },
+
+ /**
+ * Restore a window's dimensions
+ * @param aWidth
+ * Window width
+ * @param aHeight
+ * Window height
+ * @param aLeft
+ * Window left
+ * @param aTop
+ * Window top
+ * @param aSizeMode
+ * Window size mode (eg: maximized)
+ * @param aSidebar
+ * Sidebar command
+ */
+ restoreDimensions: function sss_restoreDimensions(aWindow, aWidth, aHeight, aLeft, aTop, aSizeMode, aSidebar) {
+ var win_ = this._getWindowDimension.bind(this, aWindow);
+
+ // find available space on the screen where this window is being placed
+ let screen = gScreenManager.screenForRect(aLeft, aTop, aWidth, aHeight);
+ if (screen) {
+ let screenLeft = {}, screenTop = {}, screenWidth = {}, screenHeight = {};
+ screen.GetAvailRectDisplayPix(screenLeft, screenTop, screenWidth, screenHeight);
+ // constrain the dimensions to the actual space available
+ if (aWidth > screenWidth.value) {
+ aWidth = screenWidth.value;
+ }
+ if (aHeight > screenHeight.value) {
+ aHeight = screenHeight.value;
+ }
+ // and then pull the window within the screen's bounds
+ if (aLeft < screenLeft.value) {
+ aLeft = screenLeft.value;
+ } else if (aLeft + aWidth > screenLeft.value + screenWidth.value) {
+ aLeft = screenLeft.value + screenWidth.value - aWidth;
+ }
+ if (aTop < screenTop.value) {
+ aTop = screenTop.value;
+ } else if (aTop + aHeight > screenTop.value + screenHeight.value) {
+ aTop = screenTop.value + screenHeight.value - aHeight;
+ }
+ }
+
+ // only modify those aspects which aren't correct yet
+ if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height"))) {
+ aWindow.resizeTo(aWidth, aHeight);
+ }
+ if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
+ aWindow.moveTo(aLeft, aTop);
+ }
+ if (aSizeMode && win_("sizemode") != aSizeMode)
+ {
+ switch (aSizeMode)
+ {
+ case "maximized":
+ aWindow.maximize();
+ break;
+ case "minimized":
+ aWindow.minimize();
+ break;
+ case "normal":
+ aWindow.restore();
+ break;
+ }
+ }
+ var sidebar = aWindow.document.getElementById("sidebar-box");
+ if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
+ aWindow.toggleSidebar(aSidebar);
+ }
+ // since resizing/moving a window brings it to the foreground,
+ // we might want to re-focus the last focused window
+ if (this.windowToFocus && this.windowToFocus.content) {
+ this.windowToFocus.content.focus();
+ }
+ },
+
+ /**
+ * Restores cookies
+ * @param aCookies
+ * Array of cookie objects
+ */
+ restoreCookies: function sss_restoreCookies(aCookies) {
+ // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
+ var MAX_EXPIRY = Math.pow(2, 62);
+ for (let i = 0; i < aCookies.length; i++) {
+ var cookie = aCookies[i];
+ try {
+ Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
+ cookie.value, !!cookie.secure, !!cookie.httponly,
+ true,
+ "expiry" in cookie ? cookie.expiry : MAX_EXPIRY,
+ "originAttributes" in cookie ? cookie.originAttributes : {});
+ }
+ catch (ex) { Cu.reportError(ex); } // don't let a single cookie stop recovering
+ }
+ },
+
+/* ........ Disk Access .............. */
+
+ /**
+ * save state delayed by N ms
+ * marks window as dirty (i.e. data update can't be skipped)
+ * @param aWindow
+ * Window reference
+ * @param aDelay
+ * Milliseconds to delay
+ */
+ saveStateDelayed: function sss_saveStateDelayed(aWindow, aDelay) {
+ if (aWindow) {
+ DirtyWindows.add(aWindow);
+ }
+
+ if (!this._saveTimer && this._resume_from_crash) {
+ // interval until the next disk operation is allowed
+ var minimalDelay = this._lastSaveTime + this._interval - Date.now();
+
+ // if we have to wait, set a timer, otherwise saveState directly
+ aDelay = Math.max(minimalDelay, aDelay || 2000);
+ if (aDelay > 0) {
+ this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ else {
+ this.saveState();
+ }
+ }
+ },
+
+ /**
+ * save state to disk
+ * @param aUpdateAll
+ * Bool update all windows
+ */
+ saveState: function sss_saveState(aUpdateAll) {
+ // if crash recovery is disabled, only save session resuming information
+ if (!this._resume_from_crash && this._loadState == STATE_RUNNING)
+ return;
+
+ // If crash recovery is disabled, we only want to resume with pinned tabs
+ // if we crash.
+ let pinnedOnly = this._loadState == STATE_RUNNING && !this._resume_from_crash;
+
+ var oState = this._getCurrentState(aUpdateAll);
+ if (!oState)
+ return;
+
+ // Persist the last session if we deferred restoring it
+ if (this._lastSessionState)
+ oState.lastSessionState = this._lastSessionState;
+
+ this._saveStateObject(oState);
+ },
+
+ /**
+ * write a state object to disk
+ */
+ _saveStateObject: function sss_saveStateObject(aStateObj) {
+ var stateString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ // parentheses are for backwards compatibility with older sessionstore files
+ stateString.data = this._toJSONString(aStateObj);
+
+ Services.obs.notifyObservers(stateString, "sessionstore-state-write");
+
+ // don't touch the file if an observer has deleted all state data
+ if (stateString.data)
+ this._writeFile(this._sessionFile, stateString.data);
+
+ this._lastSaveTime = Date.now();
+ },
+
+ /**
+ * delete session datafile and backup
+ */
+ _clearDisk: function sss_clearDisk() {
+ if (this._sessionFile.exists()) {
+ try {
+ this._sessionFile.remove(false);
+ }
+ catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
+ }
+ if (this._sessionFileBackup.exists()) {
+ try {
+ this._sessionFileBackup.remove(false);
+ }
+ catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
+ }
+ },
+
+/* ........ Auxiliary Functions .............. */
+
+ /**
+ * call a callback for all currently opened browser windows
+ * (might miss the most recent one)
+ * @param aFunc
+ * Callback each window is passed to
+ */
+ _forEachBrowserWindow: function sss_forEachBrowserWindow(aFunc) {
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+
+ while (windowsEnum.hasMoreElements()) {
+ var window = windowsEnum.getNext();
+ if (!window.closed && window.__SSi) {
+ aFunc.call(this, window);
+ }
+ }
+ },
+
+ /**
+ * Returns most recent window
+ * @returns Window reference
+ */
+ _getMostRecentBrowserWindow: function sss_getMostRecentBrowserWindow() {
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+ if (!win)
+ return null;
+ if (!win.closed)
+ return win;
+
+ let broken_wm_z_order =
+ AppConstants.platform != "macosx" && AppConstants.platform != "win";
+
+ if (broken_wm_z_order) {
+ win = null;
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+ // this is oldest to newest, so this gets a bit ugly
+ while (windowsEnum.hasMoreElements()) {
+ let nextWin = windowsEnum.getNext();
+ if (!nextWin.closed)
+ win = nextWin;
+ }
+ return win;
+ }
+
+ var windowsEnum =
+ Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
+
+ while (windowsEnum.hasMoreElements()) {
+ win = windowsEnum.getNext();
+ if (!win.closed)
+ return win;
+ }
+
+ return null;
+ },
+
+ /**
+ * Calls onClose for windows that are determined to be closed but aren't
+ * destroyed yet, which would otherwise cause getBrowserState and
+ * setBrowserState to treat them as open windows.
+ */
+ _handleClosedWindows: function sss_handleClosedWindows() {
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+
+ while (windowsEnum.hasMoreElements()) {
+ var window = windowsEnum.getNext();
+ if (window.closed) {
+ this.onClose(window);
+ }
+ }
+ },
+
+ /**
+ * open a new browser window for a given session state
+ * called when restoring a multi-window session
+ * @param aState
+ * Object containing session data
+ */
+ _openWindowWithState: function sss_openWindowWithState(aState) {
+ var argString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ argString.data = "about:blank";
+
+ var features = "chrome,dialog=no,suppressanimation,all";
+ var winState = aState.windows[0];
+ for (var aAttr in WINDOW_ATTRIBUTES) {
+ // Use !isNaN as an easy way to ignore sizemode and check for numbers
+ if (aAttr in winState && !isNaN(winState[aAttr]))
+ features += "," + WINDOW_ATTRIBUTES[aAttr] + "=" + winState[aAttr];
+ }
+
+ var window =
+ Services.ww.openWindow(null, this._prefBranch.getCharPref("chromeURL"),
+ "_blank", features, argString);
+
+ do {
+ var ID = "window" + Math.random();
+ } while (ID in this._statesToRestore);
+ this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
+
+ return window;
+ },
+
+ /**
+ * Gets the tab for the given browser. This should be marginally better
+ * than using tabbrowser's getTabForContentWindow. This assumes the browser
+ * is the linkedBrowser of a tab, not a dangling browser.
+ *
+ * @param aBrowser
+ * The browser from which to get the tab.
+ */
+ _getTabForBrowser: function sss_getTabForBrowser(aBrowser) {
+ let windowTabs = aBrowser.ownerDocument.defaultView.getBrowser().tabs;
+ for (let i = 0; i < windowTabs.length; i++) {
+ let tab = windowTabs[i];
+ if (tab.linkedBrowser == aBrowser)
+ return tab;
+ }
+ },
+
+ /**
+ * Whether or not to resume session, if not recovering from a crash.
+ * @returns bool
+ */
+ _doResumeSession: function sss_doResumeSession() {
+ return this._prefBranch.getIntPref("startup.page") == 3 ||
+ this._prefBranch.getBoolPref("sessionstore.resume_session_once");
+ },
+
+ /**
+ * Are we restarting to switch profile.
+ * @returns bool
+ */
+ _isSwitchingProfile: function sss_isSwitchingProfile() {
+ var env = Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment);
+ return env.exists("XRE_PROFILE_NAME");
+ },
+
+ /**
+ * whether the user wants to load any other page at startup
+ * (except the homepage) - needed for determining whether to overwrite the current tabs
+ * C.f.: nsBrowserContentHandler's defaultArgs implementation.
+ * @returns bool
+ */
+ _isCmdLineEmpty: function sss_isCmdLineEmpty(aWindow) {
+ return "arguments" in aWindow && aWindow.arguments.length &&
+ aWindow.arguments[0] == "about:blank";
+ },
+
+ /**
+ * don't save sensitive data if the user doesn't want to
+ * (distinguishes between encrypted and non-encrypted sites)
+ * @param aIsHTTPS
+ * Bool is encrypted
+ * @param aUseDefaultPref
+ * don't do normal check for deferred
+ * @returns bool
+ */
+ _checkPrivacyLevel: function sss_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) {
+ let pref = "sessionstore.privacy_level";
+ // If we're in the process of quitting and we're not autoresuming the session
+ // then we should treat it as a deferred session. We have a different privacy
+ // pref for that case.
+ if (!aUseDefaultPref && this._loadState == STATE_QUITTING && !this._doResumeSession())
+ pref = "sessionstore.privacy_level_deferred";
+ return this._prefBranch.getIntPref(pref) < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL);
+ },
+
+ /**
+ * on popup windows, the XULWindow's attributes seem not to be set correctly
+ * we use thus JSDOMWindow attributes for sizemode and normal window attributes
+ * (and hope for reasonable values when maximized/minimized - since then
+ * outerWidth/outerHeight aren't the dimensions of the restored window)
+ * @param aWindow
+ * Window reference
+ * @param aAttribute
+ * String sizemode | width | height | other window attribute
+ * @returns string
+ */
+ _getWindowDimension: function sss_getWindowDimension(aWindow, aAttribute) {
+ var dimension = aWindow[WINDOW_ATTRIBUTES[aAttribute]];
+ if (aAttribute == "sizemode") {
+ switch (dimension) {
+ case aWindow.STATE_MAXIMIZED:
+ return "maximized";
+ case aWindow.STATE_MINIMIZED:
+ return "minimized";
+ default:
+ return "normal";
+ }
+ }
+
+ if (aWindow.windowState == aWindow.STATE_NORMAL) {
+ return dimension;
+ }
+ return aWindow.document.documentElement.getAttribute(aAttribute) || dimension;
+ },
+
+ /**
+ * Get nsIURI from string
+ * @param string
+ * @returns nsIURI
+ */
+ _getURIFromString: function sss_getURIFromString(aString) {
+ return Services.io.newURI(aString);
+ },
+
+ /**
+ * Annotate a breakpad crash report with the currently selected tab's URL.
+ */
+ _updateCrashReportURL: function sss_updateCrashReportURL(aWindow) {
+
+ // If the crash reporter isn't built, we bail out.
+ if (!AppConstants.MOZ_CRASHREPORTER) {
+ return;
+ }
+
+ try {
+ var currentURI = aWindow.getBrowser().currentURI.clone();
+ // if the current URI contains a username/password, remove it
+ try {
+ currentURI.userPass = "";
+ }
+ catch (ex) { } // ignore failures on about: URIs
+
+ Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsICrashReporter)
+ .annotateCrashReport("URL", currentURI.spec);
+ }
+ catch (ex) {
+ // don't make noise when crashreporter is built but not enabled
+ if (ex.result != Cr.NS_ERROR_NOT_INITIALIZED)
+ debug(ex);
+ }
+ },
+
+ /**
+ * @param aState is a session state
+ * @param aRecentCrashes is the number of consecutive crashes
+ * @returns whether a restore page will be needed for the session state
+ */
+ _needsRestorePage: function sss_needsRestorePage(aState, aRecentCrashes) {
+ const SIX_HOURS_IN_MS = 6 * 60 * 60 * 1000;
+
+ // don't display the page when there's nothing to restore
+ if (!aState.windows || !aState.windows.length)
+ return false;
+
+ // don't wrap a single about:sessionrestore page
+ let winData = aState.windows;
+ if (winData.length == 1 && winData[0].tabs &&
+ winData[0].tabs.length == 1 && winData[0].tabs[0].entries &&
+ winData[0].tabs[0].entries.length == 1 &&
+ winData[0].tabs[0].entries[0].url == "about:sessionrestore")
+ return false;
+
+ // don't automatically restore in Safe Mode
+ if (Services.appinfo.inSafeMode)
+ return true;
+
+ let max_resumed_crashes =
+ this._prefBranch.getIntPref("sessionstore.max_resumed_crashes");
+ let sessionAge = aState.session && aState.session.lastUpdate &&
+ (Date.now() - aState.session.lastUpdate);
+
+ return max_resumed_crashes != -1 &&
+ (aRecentCrashes > max_resumed_crashes ||
+ sessionAge && sessionAge >= SIX_HOURS_IN_MS);
+ },
+
+ /**
+ * Determine if the tab state we're passed is something we should save. This
+ * is used when closing a tab or closing a window with a single tab
+ *
+ * @param aTabState
+ * The current tab state
+ * @returns boolean
+ */
+ _shouldSaveTabState: function sss__shouldSaveTabState(aTabState) {
+ // If the tab has only the transient about:blank history entry, no other
+ // session history, and no userTypedValue, then we don't actually want to
+ // store this tab's data.
+ return aTabState.entries.length &&
+ !(aTabState.entries.length == 1 &&
+ aTabState.entries[0].url == "about:blank" &&
+ !aTabState.userTypedValue);
+ },
+
+ /**
+ * This is going to take a state as provided at startup (via
+ * nsISessionStartup.state) and split it into 2 parts. The first part
+ * (defaultState) will be a state that should still be restored at startup,
+ * while the second part (state) is a state that should be saved for later.
+ * defaultState will be comprised of windows with only pinned tabs, extracted
+ * from state. It will contain the cookies that go along with the history
+ * entries in those tabs. It will also contain window position information.
+ *
+ * defaultState will be restored at startup. state will be placed into
+ * this._lastSessionState and will be kept in case the user explicitly wants
+ * to restore the previous session (publicly exposed as restoreLastSession).
+ *
+ * @param state
+ * The state, presumably from nsISessionStartup.state
+ * @returns [defaultState, state]
+ */
+ _prepDataForDeferredRestore: function sss__prepDataForDeferredRestore(state) {
+ let defaultState = { windows: [], selectedWindow: 1 };
+
+ state.selectedWindow = state.selectedWindow || 1;
+
+ // Look at each window, remove pinned tabs, adjust selectedindex,
+ // remove window if necessary.
+ for (let wIndex = 0; wIndex < state.windows.length;) {
+ let window = state.windows[wIndex];
+ window.selected = window.selected || 1;
+ // We're going to put the state of the window into this object
+ let pinnedWindowState = { tabs: [], cookies: []};
+ for (let tIndex = 0; tIndex < window.tabs.length;) {
+ if (window.tabs[tIndex].pinned) {
+ // Adjust window.selected
+ if (tIndex + 1 < window.selected)
+ window.selected -= 1;
+ else if (tIndex + 1 == window.selected)
+ pinnedWindowState.selected = pinnedWindowState.tabs.length + 2;
+ // + 2 because the tab isn't actually in the array yet
+
+ // Now add the pinned tab to our window
+ pinnedWindowState.tabs =
+ pinnedWindowState.tabs.concat(window.tabs.splice(tIndex, 1));
+ // We don't want to increment tIndex here.
+ continue;
+ }
+ tIndex++;
+ }
+
+ // At this point the window in the state object has been modified (or not)
+ // We want to build the rest of this new window object if we have pinnedTabs.
+ if (pinnedWindowState.tabs.length) {
+ // First get the other attributes off the window
+ WINDOW_ATTRIBUTES.forEach(function(attr) {
+ if (attr in window) {
+ pinnedWindowState[attr] = window[attr];
+ delete window[attr];
+ }
+ });
+ // We're just copying position data into the pinned window.
+ // Not copying over:
+ // - _closedTabs
+ // - extData
+ // - isPopup
+ // - hidden
+
+ // Assign a unique ID to correlate the window to be opened with the
+ // remaining data
+ window.__lastSessionWindowID = pinnedWindowState.__lastSessionWindowID
+ = "" + Date.now() + Math.random();
+
+ // Extract the cookies that belong with each pinned tab
+ this._splitCookiesFromWindow(window, pinnedWindowState);
+
+ // Actually add this window to our defaultState
+ defaultState.windows.push(pinnedWindowState);
+ // Remove the window from the state if it doesn't have any tabs
+ if (!window.tabs.length) {
+ if (wIndex + 1 <= state.selectedWindow)
+ state.selectedWindow -= 1;
+ else if (wIndex + 1 == state.selectedWindow)
+ defaultState.selectedIndex = defaultState.windows.length + 1;
+
+ state.windows.splice(wIndex, 1);
+ // We don't want to increment wIndex here.
+ continue;
+ }
+
+
+ }
+ wIndex++;
+ }
+
+ return [defaultState, state];
+ },
+
+ /**
+ * Splits out the cookies from aWinState into aTargetWinState based on the
+ * tabs that are in aTargetWinState.
+ * This alters the state of aWinState and aTargetWinState.
+ */
+ _splitCookiesFromWindow:
+ function sss_splitCookiesFromWindow(aWinState, aTargetWinState) {
+ if (!aWinState.cookies || !aWinState.cookies.length)
+ return;
+
+ // Get the hosts for history entries in aTargetWinState
+ let cookieHosts = {};
+ aTargetWinState.tabs.forEach(function(tab) {
+ tab.entries.forEach(function(entry) {
+ this._extractHostsForCookiesFromEntry(entry, cookieHosts, false);
+ }, this);
+ }, this);
+
+ // By creating a regex we reduce overhead and there is only one loop pass
+ // through either array (cookieHosts and aWinState.cookies).
+ let hosts = Object.keys(cookieHosts).join("|").replace(/\./g, "\\.");
+ let cookieRegex = new RegExp(".*(" + hosts + ")");
+ for (let cIndex = 0; cIndex < aWinState.cookies.length;) {
+ if (cookieRegex.test(aWinState.cookies[cIndex].host)) {
+ aTargetWinState.cookies =
+ aTargetWinState.cookies.concat(aWinState.cookies.splice(cIndex, 1));
+ continue;
+ }
+ cIndex++;
+ }
+ },
+
+ /**
+ * Converts a JavaScript object into a JSON string
+ * (see http://www.json.org/ for more information).
+ *
+ * The inverse operation consists of JSON.parse(JSON_string).
+ *
+ * @param aJSObject is the object to be converted
+ * @returns the object's JSON representation
+ */
+ _toJSONString: function sss_toJSONString(aJSObject) {
+ return JSON.stringify(aJSObject);
+ },
+
+ _sendRestoreCompletedNotifications: function sss_sendRestoreCompletedNotifications() {
+ // not all windows restored, yet
+ if (this._restoreCount > 1) {
+ this._restoreCount--;
+ return;
+ }
+
+ // observers were already notified
+ if (this._restoreCount == -1)
+ return;
+
+ Services.tm.mainThread.dispatch(this, Ci.nsIThread.DISPATCH_NORMAL);
+
+ this._restoreCount = -1;
+ },
+
+ run: function sss_run() {
+ // This was the last window restored at startup, notify observers.
+ Services.obs.notifyObservers(this.windowToFocus,
+ this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED);
+ this._browserSetState = false;
+ },
+
+ /**
+ * Dispatch an SSWindowState_____ event for the given window.
+ * @param aWindow the window
+ * @param aType the type of event, SSWindowState will be prepended to this string
+ */
+ _sendWindowStateEvent: function sss_sendWindowStateEvent(aWindow, aType) {
+ let event = aWindow.document.createEvent("Events");
+ event.initEvent("SSWindowState" + aType, true, false);
+ aWindow.dispatchEvent(event);
+ },
+
+ /**
+ * Dispatch the SSTabRestored event for the given tab.
+ * @param aTab the which has been restored
+ */
+ _sendTabRestoredNotification: function sss_sendTabRestoredNotification(aTab) {
+ let event = aTab.ownerDocument.createEvent("Events");
+ event.initEvent("SSTabRestored", true, false);
+ aTab.dispatchEvent(event);
+ },
+
+ /**
+ * @param aWindow
+ * Window reference
+ * @returns whether this window's data is still cached in _statesToRestore
+ * because it's not fully loaded yet
+ */
+ _isWindowLoaded: function sss_isWindowLoaded(aWindow) {
+ return !aWindow.__SS_restoreID;
+ },
+
+ /**
+ * Replace "Loading..." with the tab label (with minimal side-effects)
+ * @param aString is the string the title is stored in
+ * @param aTabbrowser is a tabbrowser object, containing aTab
+ * @param aTab is the tab whose title we're updating & using
+ *
+ * @returns aString that has been updated with the new title
+ */
+ _replaceLoadingTitle : function sss_replaceLoadingTitle(aString, aTabbrowser, aTab) {
+ if (aString == aTabbrowser.mStringBundle.getString("tabs.loading")) {
+ aTabbrowser.setTabTitle(aTab);
+ [aString, aTab.label] = [aTab.label, aString];
+ }
+ return aString;
+ },
+
+ /**
+ * Resize this._closedWindows to the value of the pref, except in the case
+ * where we don't have any non-popup windows on Windows and Linux. Then we must
+ * resize such that we have at least one non-popup window.
+ */
+ _capClosedWindows : function sss_capClosedWindows() {
+ if (this._closedWindows.length <= this._max_windows_undo)
+ return;
+ let spliceTo = this._max_windows_undo;
+ if (AppConstants.platform != "macosx") {
+ let normalWindowIndex = 0;
+ // try to find a non-popup window in this._closedWindows
+ while (normalWindowIndex < this._closedWindows.length &&
+ this._closedWindows[normalWindowIndex].isPopup)
+ normalWindowIndex++;
+ if (normalWindowIndex >= this._max_windows_undo)
+ spliceTo = normalWindowIndex + 1;
+ }
+
+ this._closedWindows.splice(spliceTo, this._closedWindows.length);
+ },
+
+ /**
+ * Reset state to prepare for a new session state to be restored.
+ */
+ _resetRestoringState: function sss_initRestoringState() {
+ this._tabsToRestore = { visible: [], hidden: [] };
+ this._tabsRestoringCount = 0;
+ },
+
+ /**
+ * Reset the restoring state for a particular tab. This will be called when
+ * removing a tab or when a tab needs to be reset (it's being overwritten).
+ *
+ * @param aTab
+ * The tab that will be "reset"
+ */
+ _resetTabRestoringState: function sss_resetTabRestoringState(aTab) {
+ let window = aTab.ownerDocument.defaultView;
+ let browser = aTab.linkedBrowser;
+
+ // Keep the tab's previous state for later in this method
+ let previousState = browser.__SS_restoreState;
+
+ // The browser is no longer in any sort of restoring state.
+ delete browser.__SS_restoreState;
+
+ // We want to decrement window.__SS_tabsToRestore here so that we always
+ // decrement it AFTER a tab is done restoring or when a tab gets "reset".
+ window.__SS_tabsToRestore--;
+
+ // Remove the progress listener if we should.
+ this._removeTabsProgressListener(window);
+
+ if (previousState == TAB_STATE_RESTORING) {
+ if (this._tabsRestoringCount)
+ this._tabsRestoringCount--;
+ }
+ else if (previousState == TAB_STATE_NEEDS_RESTORE) {
+ // Make sure the session history listener is removed. This is normally
+ // done in restoreTab, but this tab is being removed before that gets called.
+ this._removeSHistoryListener(aTab);
+
+ // Make sure that the tab is removed from the list of tabs to restore.
+ // Again, this is normally done in restoreTab, but that isn't being called
+ // for this tab.
+ this._removeTabFromTabsToRestore(aTab);
+ }
+ },
+
+ /**
+ * Remove the tab from this._tabsToRestore[visible/hidden]
+ *
+ * @param aTab
+ */
+ _removeTabFromTabsToRestore: function sss_removeTabFromTabsToRestore(aTab) {
+ let arr = this._tabsToRestore[aTab.hidden ? "hidden" : "visible"];
+ let index = arr.indexOf(aTab);
+ if (index > -1)
+ arr.splice(index, 1);
+ },
+
+ /**
+ * Add the tabs progress listener to the window if it isn't already
+ *
+ * @param aWindow
+ * The window to add our progress listener to
+ */
+ _ensureTabsProgressListener: function sss_ensureTabsProgressListener(aWindow) {
+ let tabbrowser = aWindow.getBrowser();
+ try {
+ tabbrowser.addTabsProgressListener(gRestoreTabsProgressListener);
+ } catch (ex) { }
+ },
+
+ /**
+ * Attempt to remove the tabs progress listener from the window.
+ *
+ * @param aWindow
+ * The window from which to remove our progress listener from
+ */
+ _removeTabsProgressListener: function sss_removeTabsProgressListener(aWindow) {
+ // If there are no tabs left to restore (or restoring) in this window, then
+ // we can safely remove the progress listener from this window.
+ if (!aWindow.__SS_tabsToRestore)
+ try {
+ aWindow.getBrowser().removeTabsProgressListener(gRestoreTabsProgressListener);
+ } catch (ex) { }
+ },
+
+ /**
+ * Remove the session history listener from the tab's browser if there is one.
+ *
+ * @param aTab
+ * The tab who's browser to remove the listener
+ */
+ _removeSHistoryListener: function sss_removeSHistoryListener(aTab) {
+ let browser = aTab.linkedBrowser;
+ if (browser.__SS_shistoryListener) {
+ browser.webNavigation.sessionHistory.
+ removeSHistoryListener(browser.__SS_shistoryListener);
+ delete browser.__SS_shistoryListener;
+ }
+ },
+
+/* ........ Storage API .............. */
+
+ /**
+ * write file to disk
+ * @param aFile
+ * nsIFile
+ * @param aData
+ * String data
+ */
+ _writeFile: function sss_writeFile(aFile, aData) {
+ // Initialize the file output stream.
+ var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ ostream.init(aFile, 0x02 | 0x08 | 0x20, parseInt("0600", 8), ostream.DEFER_OPEN);
+
+ // Obtain a converter to convert our data to a UTF-8 encoded input stream.
+ var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ // Asynchronously copy the data to the file.
+ var istream = converter.convertToInputStream(aData);
+ var ObserverService = this._observerService;
+ NetUtil.asyncCopy(istream, ostream, function(rc) {
+ if (Components.isSuccessCode(rc)) {
+ Services.obs.notifyObservers(null,
+ "sessionstore-state-write-complete");
+ }
+ });
+ }
+};
+
+// A map storing a closed window's state data until it goes aways (is GC'ed).
+// This ensures that API clients can still read (but not write) states of
+// windows they still hold a reference to but we don't.
+var DyingWindowCache = {
+ _data: new WeakMap(),
+
+ has: function (window) {
+ return this._data.has(window);
+ },
+
+ get: function (window) {
+ return this._data.get(window);
+ },
+
+ set: function (window, data) {
+ this._data.set(window, data);
+ },
+
+ remove: function (window) {
+ this._data.delete(window);
+ }
+};
+
+// A weak set of dirty windows. We use it to determine which windows we need to
+// recollect data for when getCurrentState() is called.
+var DirtyWindows = {
+ _data: new WeakMap(),
+
+ has: function (window) {
+ return this._data.has(window);
+ },
+
+ add: function (window) {
+ return this._data.set(window, true);
+ },
+
+ remove: function (window) {
+ this._data.delete(window);
+ },
+
+ clear: function (window) {
+ this._data = new WeakMap();
+ }
+};
+
+// This is used to help meter the number of restoring tabs. This is the control
+// point for telling the next tab to restore. It gets attached to each gBrowser
+// via gBrowser.addTabsProgressListener
+var gRestoreTabsProgressListener = {
+ ss: null,
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // Ignore state changes on browsers that we've already restored and state
+ // changes that aren't applicable.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
+ // We need to reset the tab before starting the next restore.
+ let tab = this.ss._getTabForBrowser(aBrowser);
+ this.ss._resetTabRestoringState(tab);
+ this.ss.restoreNextTab();
+ }
+ }
+}
+
+// A SessionStoreSHistoryListener will be attached to each browser before it is
+// restored. We need to catch reloads that occur before the tab is restored
+// because otherwise, docShell will reload an old URI (usually about:blank).
+function SessionStoreSHistoryListener(ss, aTab) {
+ this.tab = aTab;
+ this.ss = ss;
+}
+
+SessionStoreSHistoryListener.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISHistoryListener,
+ Ci.nsISupportsWeakReference]),
+ browser: null,
+ ss: null,
+ tab: null,
+ OnHistoryNewEntry: function(aNewURI) { },
+ OnHistoryGotoIndex: function(aIndex, aGotoURI) { },
+ OnHistoryPurge: function(aNumEntries) { },
+ OnHistoryReload: function(aReloadURI, aReloadFlags) {
+ // On reload, we want to make sure that session history loads the right
+ // URI. In order to do that, we will just call restoreTab. That will remove
+ // the history listener and load the right URI.
+ this.ss.restoreTab(this.tab);
+ // Returning false will stop the load that docshell is attempting.
+ return false;
+ },
+ OnHistoryReplaceEntry: function(aIndex) { },
+}
+
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStoreService]);
diff --git a/comm/suite/components/shell/ShellService.jsm b/comm/suite/components/shell/ShellService.jsm
new file mode 100644
index 0000000000..2af3e75c6b
--- /dev/null
+++ b/comm/suite/components/shell/ShellService.jsm
@@ -0,0 +1,110 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["ShellService"];
+
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/**
+ * Internal functionality to save and restore the docShell.allow* properties.
+ */
+var ShellServiceInternal = {
+ /**
+ * Used to determine whether or not to offer "Set as desktop background"
+ * functionality. Even if shell service is available it is not
+ * guaranteed that it is able to set the background for every desktop
+ * which is especially true for Linux with its many different desktop
+ * environments.
+ */
+ get canSetDesktopBackground() {
+ if (AppConstants.platform == "win" ||
+ AppConstants.platform == "macosx") {
+ return true;
+ }
+
+ if (AppConstants.platform == "linux") {
+ if (this.shellService) {
+ let linuxShellService = this.shellService
+ .QueryInterface(Ci.nsIGNOMEShellService);
+ return linuxShellService.canSetDesktopBackground;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Used to determine whether or not to show a "Set Default Client"
+ * query dialog. This attribute is true if the application is starting
+ * up and "shell.checkDefaultClient" is true, otherwise it is false.
+ */
+ _checkedThisSession: false,
+ get shouldCheckDefaultClient() {
+ // If we've already checked, the suite has been started and this is a
+ // new window open, and we don't want to check again.
+ if (this._checkedThisSession) {
+ return false;
+ }
+
+ return Services.prefs.getBoolPref("shell.checkDefaultClient");
+ },
+
+ set shouldCheckDefaultClient(shouldCheck) {
+ Services.prefs.setBoolPref("shell.checkDefaultClient", !!shouldCheck);
+ },
+
+ get shouldBeDefaultClientFor() {
+ return Services.prefs.getIntPref("shell.checkDefaultApps");
+ },
+
+ set shouldBeDefaultClientFor(appTypes) {
+ Services.prefs.setIntPref("shell.checkDefaultApps", appTypes);
+ },
+
+ setDefaultClient(forAllUsers, claimAllTypes, appTypes) {
+ try {
+ this.shellService.setDefaultClient(forAllUsers, claimAllTypes, appTypes);
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ },
+
+ isDefaultClient(startupCheck, appTypes) {
+ // If this is the first window, maintain internal state that we've
+ // checked this session (so that subsequent window opens don't show the
+ // default client dialog).
+ if (startupCheck) {
+ this._checkedThisSession = true;
+ }
+ if (this.shellService) {
+ return this.shellService.isDefaultClient(startupCheck, appTypes);
+ }
+ return false;
+ }
+};
+
+XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService",
+ "@mozilla.org/suite/shell-service;1", Ci.nsIShellService);
+
+/**
+ * The external API exported by this module.
+ */
+var ShellService = new Proxy(ShellServiceInternal, {
+ get(target, name) {
+ if (name in target) {
+ return target[name];
+ }
+ if (target.shellService) {
+ return target.shellService[name];
+ }
+ Services.console.logStringMessage(`${name} not found in ShellService: ${target.shellService}`);
+ return undefined;
+ }
+});
diff --git a/comm/suite/components/shell/content/setDesktopBackground.js b/comm/suite/components/shell/content/setDesktopBackground.js
new file mode 100644
index 0000000000..efcc5734e4
--- /dev/null
+++ b/comm/suite/components/shell/content/setDesktopBackground.js
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+var gShell = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService);
+
+var gImage, gImageName, gPosition, gPicker, gDesktop;
+
+function onLoad()
+{
+ document.getElementById("itemsBox").hidden = AppConstants.platform == "macosx";
+ gImage = window.arguments[0];
+ gImageName = window.arguments[1];
+ gPosition = document.getElementById("position");
+ gPicker = document.getElementById("picker");
+ gDesktop = document.getElementById("desktop");
+
+ sizeToContent();
+ window.innerWidth += screen.width / 2 - gDesktop.boxObject.width;
+ window.innerHeight += screen.height / 2 - gDesktop.boxObject.height;
+
+ try {
+ var color = gShell.desktopBackgroundColor;
+ color = (0xF000000 | color).toString(16).toUpperCase().replace("F", "#");
+ gDesktop.style.backgroundColor = color;
+ gPicker.color = color;
+ } catch (e) {
+ gPicker.parentNode.hidden = true;
+ }
+
+ gDesktop.style.backgroundImage = 'url("' + gImage.src + '")';
+
+ updatePosition();
+}
+
+function onApply()
+{
+ if (!gPicker.parentNode.hidden)
+ gShell.desktopBackgroundColor = parseInt(gPicker.color.substr(1), 16);
+
+ gShell.setDesktopBackground(gImage, Ci.nsIShellService[gPosition.value],
+ gImageName);
+}
+
+function updatePosition()
+{
+ gDesktop.style.backgroundPosition = "center";
+ gDesktop.style.backgroundRepeat = "no-repeat";
+ switch (gPosition.value) {
+ case "BACKGROUND_FIT":
+ gDesktop.style.backgroundSize = "contain";
+ return;
+ case "BACKGROUND_FILL":
+ gDesktop.style.backgroundSize = "cover";
+ return;
+ case "BACKGROUND_STRETCH":
+ gDesktop.style.backgroundPosition = "";
+ gDesktop.style.backgroundSize = "100% 100%";
+ return;
+ case "BACKGROUND_TILE":
+ gDesktop.style.backgroundPosition = "";
+ gDesktop.style.backgroundRepeat = "repeat";
+ }
+ gDesktop.style.backgroundSize =
+ (gImage.naturalWidth / 2) + "px " + (gImage.naturalHeight / 2) + "px";
+}
+
+function updateColor()
+{
+ gDesktop.style.backgroundColor = gPicker.color;
+}
diff --git a/comm/suite/components/shell/content/setDesktopBackground.xul b/comm/suite/components/shell/content/setDesktopBackground.xul
new file mode 100644
index 0000000000..16fb384658
--- /dev/null
+++ b/comm/suite/components/shell/content/setDesktopBackground.xul
@@ -0,0 +1,49 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/setDesktopBackground.dtd">
+
+<dialog id="setDesktopBackground"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad();"
+ buttons="accept,extra2"
+ buttoniconaccept="close"
+ buttonlabelaccept="&close.label;"
+ buttoniconextra2="apply"
+ buttonlabelextra2="&apply.label;"
+ buttonaccesskeyextra2="&apply.accesskey;"
+ ondialogextra2="onApply();"
+ title="&setDesktopBackground.title;">
+
+ <script src="chrome://communicator/content/setDesktopBackground.js"/>
+
+ <hbox id="itemsBox" align="center">
+ <label value="&position.label;" accesskey="&position.accesskey;"/>
+ <menulist id="position"
+ value="BACKGROUND_STRETCH"
+ persist="value"
+ oncommand="updatePosition();">
+ <menupopup>
+ <menuitem value="BACKGROUND_TILE" label="&position.tile.label;"/>
+ <menuitem value="BACKGROUND_STRETCH" label="&position.stretch.label;"/>
+ <menuitem value="BACKGROUND_CENTER" label="&position.center.label;"/>
+ <menuitem value="BACKGROUND_FILL" label="&position.fill.label;"/>
+ <menuitem value="BACKGROUND_FIT" label="&position.fit.label;"/>
+ </menupopup>
+ </menulist>
+ <hbox flex="1" pack="end">
+ <label value="&picker.label;" accesskey="&picker.accesskey;"/>
+ <colorpicker id="picker" type="button" onchange="updateColor();"/>
+ </hbox>
+ </hbox>
+
+ <groupbox flex="1">
+ <caption label="&preview.caption;"/>
+ <spacer id="desktop" flex="1"/>
+ </groupbox>
+</dialog>
diff --git a/comm/suite/components/shell/jar.mn b/comm/suite/components/shell/jar.mn
new file mode 100644
index 0000000000..c9eb925472
--- /dev/null
+++ b/comm/suite/components/shell/jar.mn
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/setDesktopBackground.js (content/setDesktopBackground.js)
+ content/communicator/setDesktopBackground.xul (content/setDesktopBackground.xul)
diff --git a/comm/suite/components/shell/moz.build b/comm/suite/components/shell/moz.build
new file mode 100644
index 0000000000..e508876c57
--- /dev/null
+++ b/comm/suite/components/shell/moz.build
@@ -0,0 +1,49 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "nsIShellService.idl",
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ XPIDL_SOURCES += [
+ "nsIMacShellService.idl",
+ ]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ XPIDL_SOURCES += [
+ "nsIGNOMEShellService.idl",
+ ]
+
+XPIDL_MODULE = "shellservice"
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ SOURCES += [
+ "nsWindowsShellService.cpp",
+ ]
+ LOCAL_INCLUDES += [
+ "/other-licenses/nsis/Contrib/CityHash/cityhash",
+ ]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ SOURCES += ["nsMacShellService.cpp"]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ SOURCES += ["nsGNOMEShellService.cpp"]
+
+if SOURCES:
+ EXTRA_COMPONENTS += [
+ "nsSetDefault.js",
+ "nsSetDefault.manifest",
+ ]
+
+EXTRA_JS_MODULES += [
+ "ShellService.jsm",
+]
+
+FINAL_LIBRARY = "suite"
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/shell/nsGNOMEShellService.cpp b/comm/suite/components/shell/nsGNOMEShellService.cpp
new file mode 100644
index 0000000000..15ea0e1131
--- /dev/null
+++ b/comm/suite/components/shell/nsGNOMEShellService.cpp
@@ -0,0 +1,463 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "mozilla/ArrayUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsGNOMEShellService.h"
+#include "nsShellService.h"
+#include "nsIServiceManager.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIPrefService.h"
+#include "prenv.h"
+#include "nsString.h"
+#include "nsIGIOService.h"
+#include "nsIGSettingsService.h"
+#include "nsIStringBundle.h"
+#include "nsIOutputStream.h"
+#include "nsIProcess.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIImageLoadingContent.h"
+#include "imgIRequest.h"
+#include "imgIContainer.h"
+#include "mozilla/GRefPtr.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/dom/Element.h"
+#if defined(MOZ_WIDGET_GTK)
+#include "nsImageToPixbuf.h"
+#endif
+#include "nsXULAppAPI.h"
+#include "gfxPlatform.h"
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <limits.h>
+#include <stdlib.h>
+
+using namespace mozilla;
+
+struct ProtocolAssociation {
+ uint16_t app;
+ const char* protocol;
+ bool essential;
+};
+
+struct MimeTypeAssociation {
+ uint16_t app;
+ const char* mimeType;
+ const char* extensions;
+};
+
+static const ProtocolAssociation gProtocols[] = {
+ { nsIShellService::BROWSER, "http", true },
+ { nsIShellService::BROWSER, "https", true },
+ { nsIShellService::BROWSER, "ftp", false },
+ { nsIShellService::BROWSER, "chrome", false },
+ { nsIShellService::MAIL, "mailto", true },
+ { nsIShellService::NEWS, "news", true },
+ { nsIShellService::NEWS, "snews", true },
+ { nsIShellService::RSS, "feed", true }
+};
+
+static const MimeTypeAssociation gMimeTypes[] = {
+ { nsIShellService::BROWSER, "text/html", "htm html shtml" },
+ { nsIShellService::BROWSER, "application/xhtml+xml", "xhtml xht" },
+ { nsIShellService::MAIL, "message/rfc822", "eml" },
+ { nsIShellService::RSS, "application/rss+xml", "rss" }
+};
+
+#define kDesktopBGSchema "org.gnome.desktop.background"
+#define kDesktopImageGSKey "picture-uri"
+#define kDesktopOptionGSKey "picture-options"
+#define kDesktopDrawBGGSKey "draw-background"
+#define kDesktopColorGSKey "primary-color"
+
+NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService)
+
+nsresult
+GetBrandName(nsACString& aBrandName)
+{
+ // get the product brand name from localized strings
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService(do_GetService("@mozilla.org/intl/stringbundle;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> brandBundle;
+ rv = bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle));
+ NS_ENSURE_TRUE(brandBundle, rv);
+
+ nsAutoString brandName;
+ rv = brandBundle->GetStringFromName("brandShortName", brandName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF16toUTF8(brandName, aBrandName);
+ return rv;
+}
+
+nsresult
+nsGNOMEShellService::Init()
+{
+ nsresult rv;
+
+ if (gfxPlatform::IsHeadless()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Check G_BROKEN_FILENAMES. If it's set, then filenames in glib use
+ // the locale encoding. If it's not set, they use UTF-8.
+ mUseLocaleFilenames = PR_GetEnv("G_BROKEN_FILENAMES") != nullptr;
+
+ if (GetAppPathFromLauncher()) return NS_OK;
+
+ nsCOMPtr<nsIFile> appPath;
+ rv = NS_GetSpecialDirectory(XRE_EXECUTABLE_FILE, getter_AddRefs(appPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return appPath->GetNativePath(mAppPath);
+}
+
+bool nsGNOMEShellService::GetAppPathFromLauncher() {
+ gchar *tmp;
+
+ const char* launcher = PR_GetEnv("MOZ_APP_LAUNCHER");
+ if (!launcher) return false;
+
+ if (g_path_is_absolute(launcher)) {
+ mAppPath = launcher;
+ tmp = g_path_get_basename(launcher);
+ gchar* fullpath = g_find_program_in_path(tmp);
+ if (fullpath && mAppPath.Equals(fullpath)) mAppIsInPath = true;
+ g_free(fullpath);
+ } else {
+ tmp = g_find_program_in_path(launcher);
+ if (!tmp) return false;
+ mAppPath = tmp;
+ mAppIsInPath = true;
+ }
+
+ g_free(tmp);
+ return true;
+}
+
+bool
+nsGNOMEShellService::CheckHandlerMatchesAppName(const nsACString &handler) const
+{
+ gint argc;
+ gchar** argv;
+ nsAutoCString command(handler);
+
+ // The string will be something of the form: [/path/to/]application "%s"
+ // We want to remove all of the parameters and get just the binary name.
+
+ if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) {
+ command.Assign(argv[0]);
+ g_strfreev(argv);
+ }
+
+ gchar *commandPath;
+ if (mUseLocaleFilenames) {
+ gchar *nativePath =
+ g_filename_from_utf8(command.get(), -1, nullptr, nullptr, nullptr);
+ if (!nativePath) {
+ NS_ERROR("Error converting path to filesystem encoding");
+ return false;
+ }
+
+ commandPath = g_find_program_in_path(nativePath);
+ g_free(nativePath);
+ } else {
+ commandPath = g_find_program_in_path(command.get());
+ }
+
+ if (!commandPath) return false;
+
+ bool matches = mAppPath.Equals(commandPath);
+ g_free(commandPath);
+ return matches;
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::IsDefaultClient(bool aStartupCheck, uint16_t aApps,
+ bool* aIsDefaultClient)
+{
+ *aIsDefaultClient = false;
+
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+ nsAutoCString handler;
+ nsCOMPtr<nsIGIOMimeApp> gioApp;
+
+ for (unsigned int i = 0; i < ArrayLength(gProtocols); i++) {
+ if (aApps & gProtocols[i].app) {
+ if (!gProtocols[i].essential) continue;
+
+ if (giovfs) {
+ handler.Truncate();
+ nsCOMPtr<nsIHandlerApp> handlerApp;
+ nsDependentCString protocol(gProtocols[i].protocol);
+ giovfs->GetAppForURIScheme(protocol, getter_AddRefs(handlerApp));
+ gioApp = do_QueryInterface(handlerApp);
+ if (!gioApp)
+ return NS_OK;
+
+ if (NS_SUCCEEDED(gioApp->GetCommand(handler)) &&
+ !CheckHandlerMatchesAppName(handler))
+ return NS_OK;
+ }
+ }
+ }
+
+ *aIsDefaultClient = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::SetDefaultClient(bool aForAllUsers,
+ bool aClaimAllTypes, uint16_t aApps)
+{
+ nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+ if (giovfs) {
+ nsresult rv;
+ nsCString brandName;
+ rv = GetBrandName(brandName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIGIOMimeApp> appInfo;
+ rv = giovfs->FindAppFromCommand(mAppPath, getter_AddRefs(appInfo));
+ if (NS_FAILED(rv)) {
+ // Application was not found in the list of installed applications
+ // provided by OS. Fallback to create appInfo from command and name.
+ rv = giovfs->CreateAppFromCommand(mAppPath, brandName,
+ getter_AddRefs(appInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // set handler for the protocols
+ for (unsigned int i = 0; i < ArrayLength(gProtocols); ++i) {
+ if (aApps & gProtocols[i].app) {
+ if (appInfo && (gProtocols[i].essential || aClaimAllTypes)) {
+ nsDependentCString protocol(gProtocols[i].protocol);
+ appInfo->SetAsDefaultForURIScheme(protocol);
+ }
+ }
+ }
+
+ if (aClaimAllTypes) {
+ for (unsigned int i = 0; i < ArrayLength(gMimeTypes); i++) {
+ if (aApps & gMimeTypes[i].app) {
+ nsDependentCString type(gMimeTypes[i].mimeType);
+ appInfo->SetAsDefaultForMimeType(type);
+ nsDependentCString extensions(gMimeTypes[i].extensions);
+ appInfo->SetAsDefaultForFileExtensions(extensions);
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult)
+{
+ // for Gnome or desktops using the same GSettings keys
+ const char *currentDesktop = getenv("XDG_CURRENT_DESKTOP");
+ if (currentDesktop && strstr(currentDesktop, "GNOME") != nullptr) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ const char *gnomeSession = getenv("GNOME_DESKTOP_SESSION_ID");
+ if (gnomeSession) {
+ *aResult = true;
+ } else {
+ *aResult = false;
+ }
+
+ return NS_OK;
+}
+
+static nsresult WriteImage(const nsCString &aPath, imgIContainer *aImage) {
+#if !defined(MOZ_WIDGET_GTK)
+ return NS_ERROR_NOT_AVAILABLE;
+#else
+ RefPtr<GdkPixbuf> pixbuf = nsImageToPixbuf::ImageToPixbuf(aImage);
+ if (!pixbuf) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr);
+ return res ? NS_OK : NS_ERROR_FAILURE;
+#endif
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::SetDesktopBackground(dom::Element* aElement,
+ int32_t aPosition,
+ const nsACString& aImageName)
+{
+ nsresult rv;
+ nsCOMPtr<nsIImageLoadingContent> imageContent =
+ do_QueryInterface(aElement, &rv);
+ if (!imageContent) return rv;
+
+ // Get the image container.
+ nsCOMPtr<imgIRequest> request;
+ rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(request));
+ if (!request) return rv;
+ nsCOMPtr<imgIContainer> container;
+ rv = request->GetImage(getter_AddRefs(container));
+ if (!container) return rv;
+
+ // Set desktop wallpaper filling style.
+ nsAutoCString options;
+ switch (aPosition) {
+ case BACKGROUND_TILE:
+ options.AssignLiteral("wallpaper");
+ break;
+ case BACKGROUND_STRETCH:
+ options.AssignLiteral("stretched");
+ break;
+ case BACKGROUND_FILL:
+ options.AssignLiteral("zoom");
+ break;
+ case BACKGROUND_FIT:
+ options.AssignLiteral("scaled");
+ break;
+ default:
+ options.AssignLiteral("centered");
+ break;
+ }
+
+ // Write the background file to the home directory.
+ nsCString filePath(PR_GetEnv("HOME"));
+
+ nsCString brandName;
+ rv = GetBrandName(brandName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Build the file name.
+ filePath.Append('/');
+ filePath.Append(brandName);
+ filePath.AppendLiteral("_wallpaper.png");
+
+ // Write the image to a file in the home dir.
+ rv = WriteImage(filePath, container);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIGSettingsService> gsettings =
+ do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
+ if (gsettings) {
+ nsCOMPtr<nsIGSettingsCollection> background_settings;
+ gsettings->GetCollectionForSchema(nsLiteralCString(kDesktopBGSchema),
+ getter_AddRefs(background_settings));
+ if (background_settings) {
+ gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr);
+ if (!file_uri) return NS_ERROR_FAILURE;
+
+ background_settings->SetString(nsLiteralCString(kDesktopOptionGSKey),
+ options);
+ background_settings->SetString(nsLiteralCString(kDesktopImageGSKey),
+ nsDependentCString(file_uri));
+ g_free(file_uri);
+ background_settings->SetBoolean(nsLiteralCString(kDesktopDrawBGGSKey),
+ true);
+ return rv;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+#define COLOR_16_TO_8_BIT(_c) ((_c) >> 8)
+#define COLOR_8_TO_16_BIT(_c) ((_c) << 8 | (_c))
+
+NS_IMETHODIMP
+nsGNOMEShellService::GetDesktopBackgroundColor(uint32_t *aColor)
+{
+ nsCOMPtr<nsIGSettingsService> gsettings =
+ do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
+ nsCOMPtr<nsIGSettingsCollection> background_settings;
+ nsAutoCString background;
+
+ if (gsettings) {
+ gsettings->GetCollectionForSchema(nsLiteralCString(kDesktopBGSchema),
+ getter_AddRefs(background_settings));
+ if (background_settings) {
+ background_settings->GetString(nsLiteralCString(kDesktopColorGSKey),
+ background);
+ }
+ }
+
+ if (background.IsEmpty()) {
+ *aColor = 0;
+ return NS_OK;
+ }
+
+ GdkColor color;
+ NS_ENSURE_TRUE(gdk_color_parse(background.get(), &color), NS_ERROR_FAILURE);
+
+ *aColor = COLOR_16_TO_8_BIT(color.red) << 16 |
+ COLOR_16_TO_8_BIT(color.green) << 8 |
+ COLOR_16_TO_8_BIT(color.blue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::SetDesktopBackgroundColor(uint32_t aColor)
+{
+ NS_ENSURE_ARG_MAX(aColor, 0xFFFFFF);
+
+ uint16_t red = COLOR_8_TO_16_BIT((aColor >> 16) & 0xff);
+ uint16_t green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff);
+ uint16_t blue = COLOR_8_TO_16_BIT(aColor & 0xff);
+ char colorString[14];
+ sprintf(colorString, "#%04x%04x%04x", red, green, blue);
+
+ nsCOMPtr<nsIGSettingsService> gsettings =
+ do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
+ if (gsettings) {
+ nsCOMPtr<nsIGSettingsCollection> background_settings;
+ gsettings->GetCollectionForSchema(nsLiteralCString(kDesktopBGSchema),
+ getter_AddRefs(background_settings));
+ if (background_settings) {
+ background_settings->SetString(nsLiteralCString(kDesktopColorGSKey),
+ nsDependentCString(colorString));
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProcess> process =
+ do_CreateInstance("@mozilla.org/process/util;1", &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = process->Init(aApplication);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsCString& spec = PromiseFlatCString(aURI);
+ const char* specStr = spec.get();
+ return process->Run(false, &specStr, 1);
+}
+
+NS_IMETHODIMP
+nsGNOMEShellService::GetDefaultFeedReader(nsIFile** _retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/comm/suite/components/shell/nsGNOMEShellService.h b/comm/suite/components/shell/nsGNOMEShellService.h
new file mode 100644
index 0000000000..558663a2fe
--- /dev/null
+++ b/comm/suite/components/shell/nsGNOMEShellService.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsgnomeshellservice_h____
+#define nsgnomeshellservice_h____
+
+#include "nsIGNOMEShellService.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+#include "nsSuiteCID.h"
+
+class nsGNOMEShellService final : public nsIGNOMEShellService
+{
+public:
+ nsGNOMEShellService() : mAppIsInPath(false) {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISHELLSERVICE
+ NS_DECL_NSIGNOMESHELLSERVICE
+
+ nsresult Init();
+
+private:
+ ~nsGNOMEShellService() {}
+
+ bool CheckHandlerMatchesAppName(const nsACString& handler) const;
+
+ bool GetAppPathFromLauncher();
+ bool mUseLocaleFilenames;
+ nsCString mAppPath;
+ bool mAppIsInPath;
+};
+
+#endif // nsgnomeshellservice_h____
+
diff --git a/comm/suite/components/shell/nsIGNOMEShellService.idl b/comm/suite/components/shell/nsIGNOMEShellService.idl
new file mode 100644
index 0000000000..64bf823c45
--- /dev/null
+++ b/comm/suite/components/shell/nsIGNOMEShellService.idl
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIShellService.idl"
+
+[scriptable, uuid(26ea117f-f1f1-4d53-8581-1322fcafa0d4)]
+interface nsIGNOMEShellService : nsIShellService
+{
+ /**
+ * Used to determine whether or not to offer "Set as desktop background"
+ * functionality. Even if shell service is available it is not
+ * guaranteed that it is able to set the background for every desktop
+ * which is especially true for Linux with its many different desktop
+ * environments.
+ */
+ readonly attribute boolean canSetDesktopBackground;
+};
diff --git a/comm/suite/components/shell/nsIMacShellService.idl b/comm/suite/components/shell/nsIMacShellService.idl
new file mode 100644
index 0000000000..a8e16c98a1
--- /dev/null
+++ b/comm/suite/components/shell/nsIMacShellService.idl
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIShellService.idl"
+
+[scriptable, uuid(66f4a5cf-8807-43dd-8e65-9954f3c34cf2)]
+interface nsIMacShellService : nsIShellService
+{
+ const long APPLICATION_KEYCHAIN_ACCESS = 2;
+ const long APPLICATION_NETWORK = 3;
+ const long APPLICATION_DESKTOP = 4;
+};
diff --git a/comm/suite/components/shell/nsIShellService.idl b/comm/suite/components/shell/nsIShellService.idl
new file mode 100644
index 0000000000..9320fc6afb
--- /dev/null
+++ b/comm/suite/components/shell/nsIShellService.idl
@@ -0,0 +1,98 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsISupports.idl"
+
+interface nsIFile;
+
+webidl Element;
+
+[scriptable, uuid(d7a19d24-9c98-4f88-b11e-52fa8c39ceea)]
+interface nsIShellService : nsISupports
+{
+ /**
+ * app types we can be registered to handle
+ */
+ const unsigned short BROWSER = 0x0001;
+ const unsigned short MAIL = 0x0002;
+ const unsigned short NEWS = 0x0004;
+ const unsigned short RSS = 0x0008;
+
+ /**
+ * Determines whether or not SeaMonkey is the "Default Client" for the
+ * passed in app type.
+ *
+ * This is simply whether or not SeaMonkey is registered to handle
+ * the url schemes associated with the app.
+ *
+ * @param aStartupCheck true if this is the check being performed
+ * by the first window at startup,
+ * false otherwise.
+ * @param aApps the application types being tested (Browser, Mail, News, RSS)
+ */
+ boolean isDefaultClient(in boolean aStartupCheck, in unsigned short aApps);
+
+ /**
+ * Registers SeaMonkey as the "Default Client" for the
+ * passed in app types.
+ *
+ * @param aForAllUsers Whether or not SeaMonkey should attempt
+ * to become the default client for all
+ * users on a multi-user system.
+ * @param aClaimAllTypes Register SeaMonkey as the handler for
+ * additional protocols (ftp, chrome etc)
+ * and web documents (.html, .xhtml etc).
+ * @param aApps the application types being tested (Mail, News, Browser, RSS)
+ */
+ void setDefaultClient(in boolean aForAllUsers, in boolean aClaimAllTypes, in unsigned short aApps);
+
+ /**
+ * Sets the desktop background image using either the HTML <IMG>
+ * element supplied or the background image of the element supplied.
+ *
+ * @param aImageElement Either a HTML <IMG> element or an element with
+ * a background image from which to source the
+ * background image.
+ * @param aPosition How to place the image on the desktop
+ * @param aImageName The image name. Equivalent to the leaf name of the
+ * location.href.
+ */
+
+ void setDesktopBackground(in Element aElement,
+ in long aPosition,
+ in ACString aImageName);
+
+ /**
+ * Flags for positioning/sizing of the Desktop Background image.
+ */
+ const long BACKGROUND_TILE = 1;
+ const long BACKGROUND_STRETCH = 2;
+ const long BACKGROUND_CENTER = 3;
+ const long BACKGROUND_FILL = 4;
+ const long BACKGROUND_FIT = 5;
+
+ /**
+ * The desktop background color, visible when no background image is
+ * used, or if the background image is centered and does not fill the
+ * entire screen. An RGB value (r << 16 | g << 8 | b)
+ */
+ attribute unsigned long desktopBackgroundColor;
+
+ /**
+ * Opens an application with a specific URI to load.
+ * @param application
+ * The application file (or bundle directory, on OS X)
+ * @param uri
+ * The uri to be loaded by the application
+ */
+ void openApplicationWithURI(in nsIFile aApplication, in ACString aURI);
+
+ /**
+ * The default system handler for web feeds
+ */
+ readonly attribute nsIFile defaultFeedReader;
+};
+
diff --git a/comm/suite/components/shell/nsMacShellService.cpp b/comm/suite/components/shell/nsMacShellService.cpp
new file mode 100644
index 0000000000..6aa8aa3088
--- /dev/null
+++ b/comm/suite/components/shell/nsMacShellService.cpp
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsIImageLoadingContent.h"
+#include "mozilla/dom/Document.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIContent.h"
+#include "nsICookieJarSettings.h"
+#include "nsILocalFileMac.h"
+#include "nsIObserverService.h"
+#include "nsIPrefService.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsIURL.h"
+#include "nsIWebBrowserPersist.h"
+#include "nsMacShellService.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+#include "nsShellService.h"
+#include "nsString.h"
+#include "nsIDocShell.h"
+#include "nsILoadContext.h"
+#include "nsIPrefService.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ReferrerInfo.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#define SAFARI_BUNDLE_IDENTIFIER "com.apple.Safari"
+
+using mozilla::dom::Element;
+
+NS_IMPL_ISUPPORTS(nsMacShellService, nsIShellService, nsIWebProgressListener)
+
+NS_IMETHODIMP
+nsMacShellService::IsDefaultClient(bool aStartupCheck, uint16_t aApps, bool *aIsDefaultClient)
+{
+ *aIsDefaultClient = false;
+
+ if (aApps & nsIShellService::BROWSER)
+ if(!isDefaultHandlerForProtocol(CFSTR("http")))
+ return NS_OK;
+ if (aApps & nsIShellService::MAIL)
+ if(!isDefaultHandlerForProtocol(CFSTR("mailto")))
+ return NS_OK;
+ if (aApps & nsIShellService::NEWS)
+ if(!isDefaultHandlerForProtocol(CFSTR("news")))
+ return NS_OK;
+ if (aApps & nsIShellService::RSS)
+ if(!isDefaultHandlerForProtocol(CFSTR("feed")))
+ return NS_OK;
+
+ *aIsDefaultClient = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::SetDefaultClient(bool aForAllUsers,
+ bool aClaimAllTypes, uint16_t aApps)
+{
+ // Note: We don't support aForAllUsers on macOS.
+
+ CFStringRef suiteID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle());
+ if (!suiteID)
+ return NS_ERROR_FAILURE;
+
+ if (aApps & nsIShellService::BROWSER)
+ {
+ if (::LSSetDefaultHandlerForURLScheme(CFSTR("http"), suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+ if (::LSSetDefaultHandlerForURLScheme(CFSTR("https"), suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+ if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll, suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+ if (::LSSetDefaultRoleHandlerForContentType(CFSTR("public.xhtml"), kLSRolesAll, suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aApps & nsIShellService::MAIL)
+ if (::LSSetDefaultHandlerForURLScheme(CFSTR("mailto"), suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+ if (aApps & nsIShellService::NEWS)
+ if (::LSSetDefaultHandlerForURLScheme(CFSTR("news"), suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+ if (aApps & nsIShellService::RSS)
+ if (::LSSetDefaultHandlerForURLScheme(CFSTR("feed"), suiteID) != noErr)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+bool
+nsMacShellService::isDefaultHandlerForProtocol(CFStringRef aScheme)
+{
+ bool isDefault = false;
+
+ CFStringRef suiteID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle());
+ if (!suiteID)
+ {
+ // CFBundleGetIdentifier is expected to return nullptr only if the specified
+ // bundle doesn't have a bundle identifier in its dictionary. In this case,
+ // that means a failure, since our bundle does have an identifier.
+ return isDefault;
+ }
+
+ // Get the default handler's bundle ID for the scheme.
+ CFStringRef defaultHandlerID = ::LSCopyDefaultHandlerForURLScheme(aScheme);
+ if (defaultHandlerID)
+ {
+ // The handler ID in LaunchServices is in all lower case, but the bundle
+ // identifier could have upper case characters. So we're using
+ // CFStringCompare with the kCFCompareCaseInsensitive option here.
+ isDefault = ::CFStringCompare(suiteID, defaultHandlerID,
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo;
+ ::CFRelease(defaultHandlerID);
+ }
+
+ return isDefault;
+}
+
+NS_IMETHODIMP
+nsMacShellService::SetDesktopBackground(Element* aElement,
+ int32_t aPosition,
+ const nsACString& aImageName)
+{
+ // Note: We don't support aPosition on OS X.
+
+ // Get the image URI:
+ nsresult rv;
+ nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIURI> imageURI;
+ rv = imageContent->GetCurrentURI(getter_AddRefs(imageURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsIURI *docURI = aElement->OwnerDoc()->GetDocumentURI();
+ if (!docURI)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIProperties> fileLocator
+ (do_GetService("@mozilla.org/file/directory_service;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the current user's "Pictures" folder (That's ~/Pictures):
+ fileLocator->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(mBackgroundFile));
+ if (!mBackgroundFile)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsAutoString fileNameUnicode;
+ CopyUTF8toUTF16(aImageName, fileNameUnicode);
+
+ // and add the image file name itself:
+ mBackgroundFile->Append(fileNameUnicode);
+
+ // Download the image; the desktop background will be set in OnStateChange():
+ nsCOMPtr<nsIWebBrowserPersist> wbp
+ (do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
+ nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE;
+
+ wbp->SetPersistFlags(flags);
+ wbp->SetProgressListener(this);
+
+ nsCOMPtr<nsILoadContext> loadContext;
+ nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetContainer();
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
+ if (docShell)
+ {
+ loadContext = do_QueryInterface(docShell);
+ }
+
+ auto referrerInfo = mozilla::MakeRefPtr<mozilla::dom::ReferrerInfo>(*aElement);
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
+ aElement->OwnerDoc()->CookieJarSettings();
+ return wbp->SaveURI(imageURI, aElement->NodePrincipal(), 0, referrerInfo,
+ cookieJarSettings, nullptr, nullptr, mBackgroundFile,
+ nsIContentPolicy::TYPE_IMAGE, loadContext);
+}
+
+NS_IMETHODIMP
+nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI* aLocation,
+ uint32_t aFlags)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aState)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aEvent) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aStateFlags,
+ nsresult aStatus)
+{
+ if (aStateFlags & STATE_STOP)
+ {
+ bool exists = false;
+ mBackgroundFile->Exists(&exists);
+ if (!exists)
+ return NS_OK;
+
+ nsAutoCString nativePath;
+ mBackgroundFile->GetNativePath(nativePath);
+
+ AEDesc tAEDesc = { typeNull, nil };
+ OSErr err = noErr;
+ AliasHandle aliasHandle = nil;
+ FSRef pictureRef;
+ OSStatus status;
+
+ // Convert the path into a FSRef:
+ status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef,
+ nullptr);
+ if (status == noErr)
+ {
+ err = ::FSNewAlias(nil, &pictureRef, &aliasHandle);
+ if (err == noErr && aliasHandle == nil)
+ err = paramErr;
+
+ if (err == noErr)
+ {
+ // We need the descriptor (based on the picture file reference)
+ // for the 'Set Desktop Picture' apple event.
+ char handleState = ::HGetState((Handle)aliasHandle);
+ ::HLock((Handle)aliasHandle);
+ err = ::AECreateDesc(typeAlias, *aliasHandle,
+ GetHandleSize((Handle)aliasHandle), &tAEDesc);
+ // Unlock the alias handler:
+ ::HSetState((Handle)aliasHandle, handleState);
+ ::DisposeHandle((Handle)aliasHandle);
+ }
+ if (err == noErr)
+ {
+ AppleEvent tAppleEvent;
+ OSType sig = 'MACS';
+ AEBuildError tAEBuildError;
+ // Create a 'Set Desktop Picture' Apple Event:
+ err = ::AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplSignature,
+ &sig, sizeof(OSType), kAutoGenerateReturnID,
+ kAnyTransactionID, &tAppleEvent, &tAEBuildError,
+ "'----':'obj '{want:type (prop),form:prop" \
+ ",seld:type('dpic'),from:'null'()},data:(@)",
+ &tAEDesc);
+ if (err == noErr)
+ {
+ AppleEvent reply = { typeNull, nil };
+ // Send the event we built, the reply event isn't necessary:
+ err = ::AESend(&tAppleEvent, &reply, kAENoReply, kAENormalPriority,
+ kNoTimeOut, nil, nil);
+ ::AEDisposeDesc(&tAppleEvent);
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::GetDesktopBackgroundColor(uint32_t *aColor)
+{
+ // This method and |SetDesktopBackgroundColor| has no meaning on macOS.
+ // The mac desktop preferences UI uses pictures for the few solid colors it
+ // supports.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMacShellService::SetDesktopBackgroundColor(uint32_t aColor)
+{
+ // This method and |GetDesktopBackgroundColor| has no meaning on macOS.
+ // The mac desktop preferences UI uses pictures for the few solid colors it
+ // supports.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI)
+{
+ nsCOMPtr<nsILocalFileMac> lfm(do_QueryInterface(aApplication));
+ CFURLRef appURL;
+ nsresult rv = lfm->GetCFURL(&appURL);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsCString& spec = PromiseFlatCString(aURI);
+ const UInt8* uriString = (const UInt8*)spec.get();
+ CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(),
+ kCFStringEncodingUTF8, nullptr);
+ if (!uri)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr);
+ if (!uris)
+ {
+ ::CFRelease(uri);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ LSLaunchURLSpec launchSpec;
+ launchSpec.appURL = appURL;
+ launchSpec.itemURLs = uris;
+ launchSpec.passThruParams = nullptr;
+ launchSpec.launchFlags = kLSLaunchDefaults;
+ launchSpec.asyncRefCon = nullptr;
+
+ OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr);
+
+ ::CFRelease(uris);
+ ::CFRelease(uri);
+
+ return err != noErr ? NS_ERROR_FAILURE : NS_OK;
+}
+
+NS_IMETHODIMP
+nsMacShellService::GetDefaultFeedReader(nsIFile** _retval)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ *_retval = nullptr;
+
+ CFStringRef defaultHandlerID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("feed"));
+ if (!defaultHandlerID)
+ {
+ defaultHandlerID = ::CFStringCreateWithCString(kCFAllocatorDefault,
+ SAFARI_BUNDLE_IDENTIFIER,
+ kCFStringEncodingASCII);
+ }
+
+ CFURLRef defaultHandlerURL = nullptr;
+ OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator,
+ defaultHandlerID,
+ nullptr, // inName
+ nullptr, // outAppRef
+ &defaultHandlerURL);
+
+ if (status == noErr && defaultHandlerURL)
+ {
+ nsCOMPtr<nsILocalFileMac> defaultReader =
+ do_CreateInstance("@mozilla.org/file/local;1", &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = defaultReader->InitWithCFURL(defaultHandlerURL);
+ if (NS_SUCCEEDED(rv))
+ {
+ NS_ADDREF(*_retval = defaultReader);
+ }
+ }
+
+ ::CFRelease(defaultHandlerURL);
+ }
+
+ ::CFRelease(defaultHandlerID);
+
+ return rv;
+}
diff --git a/comm/suite/components/shell/nsMacShellService.h b/comm/suite/components/shell/nsMacShellService.h
new file mode 100644
index 0000000000..4fa92513c2
--- /dev/null
+++ b/comm/suite/components/shell/nsMacShellService.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsmacshellservice_h____
+#define nsmacshellservice_h____
+
+#include "nsIMacShellService.h"
+#include "nsIWebProgressListener.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+#include "nsSuiteCID.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+class nsMacShellService final : public nsIShellService,
+ public nsIWebProgressListener
+{
+public:
+ nsMacShellService() {};
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISHELLSERVICE
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+protected:
+ ~nsMacShellService() {}
+ bool isDefaultHandlerForProtocol(CFStringRef aScheme);
+
+private:
+ nsCOMPtr<nsIFile> mBackgroundFile;
+};
+
+#endif
diff --git a/comm/suite/components/shell/nsSetDefault.js b/comm/suite/components/shell/nsSetDefault.js
new file mode 100644
index 0000000000..4d35dc4531
--- /dev/null
+++ b/comm/suite/components/shell/nsSetDefault.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+/*
+ * This component handles the startup command line arguments of the form:
+ * -setDefaultBrowser
+ * -setDefaultMail
+ * -setDefaultNews
+ * -setDefaultFeed
+ */
+
+const nsICommandLineHandler = Ci.nsICommandLineHandler;
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function nsSetDefault() {
+}
+
+nsSetDefault.prototype = {
+ handle: function nsSetDefault_handle(aCmdline) {
+ if (aCmdline.handleFlag("setDefaultBrowser", false)) {
+ var shell = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService);
+ shell.setDefaultClient(true, true, Ci.nsIShellService.BROWSER);
+ }
+ else if (aCmdline.handleFlag("setDefaultMail", false)) {
+ var shell = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService);
+ shell.setDefaultClient(true, true, Ci.nsIShellService.MAIL);
+ }
+ else if (aCmdline.handleFlag("setDefaultNews", false)) {
+ var shell = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService);
+ shell.setDefaultClient(true, true, Ci.nsIShellService.NEWS);
+ }
+ else if (aCmdline.handleFlag("setDefaultFeed", false)) {
+ var shell = Cc["@mozilla.org/suite/shell-service;1"]
+ .getService(Ci.nsIShellService);
+ shell.setDefaultClient(true, true, Ci.nsIShellService.RSS);
+ }
+ },
+
+ helpInfo: " -setDefaultBrowser Set this app as the default browser client.\n" +
+ " -setDefaultMail Set this app as the default mail client.\n" +
+ " -setDefaultNews Set this app as the default newsreader.\n" +
+ " -setDefaultFeed Set this app as the default feedreader.\n",
+
+ classID: Components.ID("{a3d5b950-690a-491f-a881-2c2cdcd241cb}"),
+ QueryInterface: XPCOMUtils.generateQI([nsICommandLineHandler])
+}
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSetDefault]);
+
diff --git a/comm/suite/components/shell/nsSetDefault.manifest b/comm/suite/components/shell/nsSetDefault.manifest
new file mode 100644
index 0000000000..7d4a585bc7
--- /dev/null
+++ b/comm/suite/components/shell/nsSetDefault.manifest
@@ -0,0 +1,3 @@
+component {a3d5b950-690a-491f-a881-2c2cdcd241cb} nsSetDefault.js
+contract @mozilla.org/suite/default-browser-clh;1 {a3d5b950-690a-491f-a881-2c2cdcd241cb}
+category command-line-handler m-setdefault @mozilla.org/suite/default-browser-clh;1
diff --git a/comm/suite/components/shell/nsShellService.h b/comm/suite/components/shell/nsShellService.h
new file mode 100644
index 0000000000..f5275a6556
--- /dev/null
+++ b/comm/suite/components/shell/nsShellService.h
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIShellService.h"
+
+#define PREF_CHECKDEFAULTCLIENT "shell.checkDefaultClient"
+
+#define SHELLSERVICE_PROPERTIES "chrome://communicator/locale/shellservice.properties"
+#define BRAND_PROPERTIES "chrome://branding/locale/brand.properties"
diff --git a/comm/suite/components/shell/nsWindowsShellService.cpp b/comm/suite/components/shell/nsWindowsShellService.cpp
new file mode 100644
index 0000000000..7cdf1cbd88
--- /dev/null
+++ b/comm/suite/components/shell/nsWindowsShellService.cpp
@@ -0,0 +1,793 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWindowsShellService.h"
+
+#include "imgIContainer.h"
+#include "imgIRequest.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "nsIContent.h"
+#include "nsIImageLoadingContent.h"
+#include "nsIOutputStream.h"
+#include "nsIPrefService.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsIServiceManager.h"
+#include "nsIStringBundle.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsShellService.h"
+#include "nsIProcess.h"
+#include "nsICategoryManager.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIWindowsRegKey.h"
+#include "nsUnicharUtils.h"
+#include "nsIURLFormatter.h"
+#include "nsXULAppAPI.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/dom/Element.h"
+
+#include "windows.h"
+#include "shellapi.h"
+
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0600
+#define INITGUID
+#include <shlobj.h>
+
+#ifndef MAX_BUF
+#define MAX_BUF 4096
+#endif
+
+#define REG_SUCCEEDED(val) \
+ (val == ERROR_SUCCESS)
+
+#define REG_FAILED(val) \
+ (val != ERROR_SUCCESS)
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIShellService)
+
+static nsresult
+OpenKeyForReading(HKEY aKeyRoot, const wchar_t* aKeyName, HKEY* aKey)
+{
+ DWORD res = ::RegOpenKeyExW(aKeyRoot, aKeyName, 0, KEY_READ, aKey);
+ switch (res) {
+ case ERROR_SUCCESS:
+ break;
+ case ERROR_ACCESS_DENIED:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ case ERROR_FILE_NOT_FOUND:
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Default SeaMonkey OS integration Registry Settings
+// Note: Some settings only exist when using the installer!
+// The setting of SeaMonkey as default application is made by a helper
+// application since writing those values may require elevation.
+//
+// Default Browser settings:
+// - File Extension Mappings
+// -----------------------
+// The following file extensions:
+// .htm .html .shtml .xht .xhtml
+// are mapped like so:
+//
+// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ SeaMonkeyHTML
+//
+// as aliases to the class:
+//
+// HKCU\SOFTWARE\Classes\SeaMonkeyHTML\
+// DefaultIcon (default) REG_SZ <appfolder>\chrome\icons\default\html-file.ico
+// shell\open\command (default) REG_SZ <apppath> -url "%1"
+//
+// - Windows Vista Protocol Handler
+//
+// HKCU\SOFTWARE\Classes\SeaMonkeyURL\(default) REG_SZ <appname> URL
+// EditFlags REG_DWORD 2
+// FriendlyTypeName REG_SZ <appname> URL
+// DefaultIcon (default) REG_SZ <apppath>,1
+// shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
+// shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
+// shell\open\ddeexec NoActivateHandler REG_SZ
+// \Application (default) REG_SZ SeaMonkey
+// \Topic (default) REG_SZ WWW_OpenURL
+//
+// - Protocol Mappings
+// -----------------
+// The following protocols:
+// HTTP, HTTPS, FTP
+// are mapped like so:
+//
+// HKCU\SOFTWARE\Classes\<protocol>\
+// DefaultIcon (default) REG_SZ <apppath>,0
+// shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
+// shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
+// shell\open\ddeexec NoActivateHandler REG_SZ
+// \Application (default) REG_SZ SeaMonkey
+// \Topic (default) REG_SZ WWW_OpenURL
+//
+// - Windows Start Menu (Win2K SP2, XP SP1, and newer)
+// -------------------------------------------------
+// The following keys are set to make SeaMonkey appear in the Start Menu as the
+// browser:
+//
+// HKCU\SOFTWARE\Clients\StartMenuInternet\SEAMONKEY.EXE\
+// (default) REG_SZ <appname>
+// DefaultIcon (default) REG_SZ <apppath>,0
+// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
+// InstallInfo IconsVisible REG_DWORD 1
+// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
+// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
+// shell\open\command (default) REG_SZ <apppath>
+// shell\properties (default) REG_SZ <appname> &Preferences
+// shell\properties\command (default) REG_SZ <apppath> -preferences
+// shell\safemode (default) REG_SZ <appname> &Safe Mode
+// shell\safemode\command (default) REG_SZ <apppath> -safe-mode
+//
+//
+//
+// Default Mail&News settings
+//
+// - File Extension Mappings
+// -----------------------
+// The following file extension:
+// .eml
+// is mapped like this:
+//
+// HKCU\SOFTWARE\Classes\.eml (default) REG_SZ SeaMonkeyEML
+//
+// That aliases to this class:
+// HKCU\SOFTWARE\Classes\SeaMonkeyEML\ (default) REG_SZ SeaMonkey (Mail) Document
+// FriendlyTypeName REG_SZ SeaMonkey (Mail) Document
+// DefaultIcon (default) REG_SZ <appfolder>\chrome\icons\default\html-file.ico
+// shell\open\command (default) REG_SZ <apppath> "%1"
+//
+// - Windows Vista Protocol Handler
+//
+// HKCU\SOFTWARE\Classes\SeaMonkeyCOMPOSE (default) REG_SZ SeaMonkey (Mail) URL
+// DefaultIcon REG_SZ <apppath>,0
+// EditFlags REG_DWORD 2
+// shell\open\command (default) REG_SZ <apppath> -osint -compose "%1"
+//
+// HKCU\SOFTWARE\Classes\SeaMonkeyNEWS (default) REG_SZ SeaMonkey (News) URL
+// DefaultIcon REG_SZ <apppath>,0
+// EditFlags REG_DWORD 2
+// shell\open\command (default) REG_SZ <apppath> -osint -news "%1"
+//
+//
+// - Protocol Mappings
+// -----------------
+// The following protocol:
+// mailto
+// is mapped like this:
+//
+// HKCU\SOFTWARE\Classes\mailto\ (default) REG_SZ SeaMonkey (Mail) URL
+// EditFlags REG_DWORD 2
+// URL Protocol REG_SZ
+// DefaultIcon (default) REG_SZ <apppath>,0
+// shell\open\command (default) REG_SZ <apppath> -osint -compose "%1"
+//
+// The following protocols:
+// news,nntp,snews
+// are mapped like this:
+//
+// HKCU\SOFTWARE\Classes\<protocol>\ (default) REG_SZ SeaMonkey (News) URL
+// EditFlags REG_DWORD 2
+// URL Protocol REG_SZ
+// DefaultIcon (default) REG_SZ <appath>,0
+// shell\open\command (default) REG_SZ <appath> -osint -news "%1"
+//
+// - Windows Start Menu (Win2K SP2, XP SP1, and newer)
+// -------------------------------------------------
+// The following keys are set to make SeaMonkey appear in the Start Menu as
+// the default mail program:
+//
+// HKCU\SOFTWARE\Clients\Mail\SeaMonkey
+// (default) REG_SZ <appname>
+// DLLPath REG_SZ <appfolder>\mozMapi32.dll
+// DefaultIcon (default) REG_SZ <apppath>,0
+// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
+// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
+// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
+// shell\open\command (default) REG_SZ <apppath> -mail
+// shell\properties (default) REG_SZ <appname> &Preferences
+// shell\properties\command (default) REG_SZ <apppath> -preferences
+//
+// Also set SeaMonkey as News reader (Usenet), though Windows does currently
+// not expose a default news reader to UI. Applications like Outlook
+// also add themselves to this registry key
+//
+// HKCU\SOFTWARE\Clients\News\SeaMonkey
+// (default) REG_SZ <appname>
+// DLLPath REG_SZ <appfolder>\mozMapi32.dll
+// DefaultIcon (default) REG_SZ <apppath>,0
+// shell\open\command (default) REG_SZ <apppath> -news
+//
+///////////////////////////////////////////////////////////////////////////////
+
+
+typedef enum {
+ NO_SUBSTITUTION = 0x00,
+ APP_PATH_SUBSTITUTION = 0x01
+} SettingFlags;
+
+#define APP_REG_NAME L"SeaMonkey"
+// APP_REG_NAME_MAIL and APP_REG_NAME_NEWS should be kept in synch with
+// AppRegNameMail and AppRegNameNews in the installer file: defines.nsi.in
+#define APP_REG_NAME_MAIL L"SeaMonkey (Mail)"
+#define APP_REG_NAME_NEWS L"SeaMonkey (News)"
+#define CLS_HTML "SeaMonkeyHTML"
+#define CLS_URL "SeaMonkeyURL"
+#define CLS_EML "SeaMonkeyEML"
+#define CLS_MAILTOURL "SeaMonkeyCOMPOSE"
+#define CLS_NEWSURL "SeaMonkeyNEWS"
+#define CLS_FEEDURL "SeaMonkeyFEED"
+#define SMI "SOFTWARE\\Clients\\StartMenuInternet\\"
+#define DI "\\DefaultIcon"
+#define II "\\InstallInfo"
+#define SOP "\\shell\\open\\command"
+
+#define VAL_ICON "%APPPATH%,0"
+#define VAL_HTML_OPEN "\"%APPPATH%\" -url \"%1\""
+#define VAL_URL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
+#define VAL_MAIL_OPEN "\"%APPPATH%\" \"%1\""
+
+#define MAKE_KEY_NAME1(PREFIX, MID) \
+ PREFIX MID
+
+// The DefaultIcon registry key value should never be used (e.g. NON_ESSENTIAL)
+// when checking if SeaMonkey is the default browser since other applications
+// (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon
+// Handlers.
+// see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for more info.
+static SETTING gBrowserSettings[] = {
+ // File Extension Class - as of 1.8.1.2 the value for VAL_URL_OPEN is also
+ // checked for CLS_HTML since SeaMonkey should also own opening local files
+ // when set as the default browser.
+ { MAKE_KEY_NAME1(CLS_HTML, SOP), "", VAL_HTML_OPEN, APP_PATH_SUBSTITUTION },
+
+ // Protocol Handler Class - for Vista and above
+ { MAKE_KEY_NAME1(CLS_URL, SOP), "", VAL_URL_OPEN, APP_PATH_SUBSTITUTION },
+
+ // Protocol Handlers
+ { MAKE_KEY_NAME1("HTTP", DI), "", VAL_ICON, APP_PATH_SUBSTITUTION },
+ { MAKE_KEY_NAME1("HTTP", SOP), "", VAL_URL_OPEN, APP_PATH_SUBSTITUTION },
+ { MAKE_KEY_NAME1("HTTPS", DI), "", VAL_ICON, APP_PATH_SUBSTITUTION },
+ { MAKE_KEY_NAME1("HTTPS", SOP), "", VAL_URL_OPEN, APP_PATH_SUBSTITUTION }
+
+ // These values must be set by hand, since they contain localized strings.
+ // seamonkey.exe\shell\properties (default) REG_SZ SeaMonkey &Preferences
+ // seamonkey.exe\shell\safemode (default) REG_SZ SeaMonkey &Safe Mode
+};
+
+ static SETTING gMailSettings[] = {
+ // File Extension Aliases
+ { ".eml", "", CLS_EML, NO_SUBSTITUTION },
+ // File Extension Class
+ { MAKE_KEY_NAME1(CLS_EML, SOP), "", VAL_MAIL_OPEN, APP_PATH_SUBSTITUTION},
+
+ // Protocol Handler Class - for Vista and above
+ { MAKE_KEY_NAME1(CLS_MAILTOURL, SOP), "", "\"%APPPATH%\" -osint -compose \"%1\"", APP_PATH_SUBSTITUTION },
+
+ // Protocol Handlers
+ { MAKE_KEY_NAME1("mailto", SOP), "", "\"%APPPATH%\" -osint -compose \"%1\"", APP_PATH_SUBSTITUTION }
+ };
+
+ static SETTING gNewsSettings[] = {
+ // Protocol Handler Class - for Vista and above
+ { MAKE_KEY_NAME1(CLS_NEWSURL, SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION },
+
+ // Protocol Handlers
+ { MAKE_KEY_NAME1("news", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION },
+ { MAKE_KEY_NAME1("nntp", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION },
+};
+
+ static SETTING gFeedSettings[] = {
+ // Protocol Handler Class - for Vista and above
+ { MAKE_KEY_NAME1(CLS_FEEDURL, SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION },
+
+ // Protocol Handlers
+ { MAKE_KEY_NAME1("feed", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION },
+};
+
+nsresult
+GetHelperPath(nsString& aPath)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> appHelper;
+ rv = directoryService->Get(XRE_EXECUTABLE_FILE,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(appHelper));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->SetNativeLeafName("uninstall"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->AppendNative("helper.exe"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->GetPath(aPath);
+
+ aPath.Insert('"', 0);
+ aPath.Append('"');
+
+ return rv;
+}
+
+nsresult
+LaunchHelper(const nsString& aPath)
+{
+ STARTUPINFOW si = {sizeof(si), 0};
+ PROCESS_INFORMATION pi = {0};
+
+ BOOL ok = CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr,
+ FALSE, 0, nullptr, nullptr, &si, &pi);
+
+ if (!ok)
+ return NS_ERROR_FAILURE;
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return NS_OK;
+}
+
+/* helper routine. Iterate over the passed in settings object,
+ testing each key to see if we are handling it.
+*/
+bool
+nsWindowsShellService::TestForDefault(SETTING aSettings[], int32_t aSize)
+{
+ wchar_t currValue[MAX_BUF];
+ SETTING* end = aSettings + aSize;
+ for (SETTING * settings = aSettings; settings < end; ++settings) {
+ NS_ConvertUTF8toUTF16 dataLongPath(settings->valueData);
+ NS_ConvertUTF8toUTF16 dataShortPath(settings->valueData);
+ NS_ConvertUTF8toUTF16 key(settings->keyName);
+ NS_ConvertUTF8toUTF16 value(settings->valueName);
+ if (settings->flags & APP_PATH_SUBSTITUTION) {
+ int32_t offset = dataLongPath.Find(u"%APPPATH%");
+ dataLongPath.Replace(offset, 9, mAppLongPath);
+ // Remove the quotes around %APPPATH% in VAL_OPEN for short paths
+ int32_t offsetQuoted = dataShortPath.Find(u"\"%APPPATH%\"");
+ if (offsetQuoted != -1)
+ dataShortPath.Replace(offsetQuoted, 11, mAppShortPath);
+ else
+ dataShortPath.Replace(offset, 9, mAppShortPath);
+ }
+
+ ::ZeroMemory(currValue, sizeof(currValue));
+ HKEY theKey;
+ nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, key.get(), &theKey);
+ if (NS_FAILED(rv))
+ // Key does not exist
+ return false;
+
+ DWORD len = sizeof currValue;
+ DWORD res = ::RegQueryValueExW(theKey, value.get(),
+ nullptr, nullptr, (LPBYTE)currValue, &len);
+ // Close the key we opened.
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(res) ||
+ _wcsicmp(dataLongPath.get(), currValue) &&
+ _wcsicmp(dataShortPath.get(), currValue)) {
+ // Key wasn't set, or was set to something else (something else became the default client)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult nsWindowsShellService::Init()
+{
+ wchar_t appPath[MAX_BUF];
+ if (!::GetModuleFileNameW(0, appPath, MAX_BUF))
+ return NS_ERROR_FAILURE;
+
+ mAppLongPath.Assign(appPath);
+
+ // Support short path to the exe so if it is already set the user is not
+ // prompted to set the default mail client again.
+ if (!::GetShortPathNameW(appPath, appPath, MAX_BUF))
+ return NS_ERROR_FAILURE;
+
+ mAppShortPath.Assign(appPath);
+
+ return NS_OK;
+}
+
+bool
+nsWindowsShellService::IsDefaultClientVista(uint16_t aApps, bool* aIsDefaultClient)
+{
+ IApplicationAssociationRegistration* pAAR;
+
+ HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
+ nullptr,
+ CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistration,
+ (void**)&pAAR);
+
+ if (SUCCEEDED(hr)) {
+ BOOL isDefaultBrowser = true;
+ BOOL isDefaultMail = true;
+ BOOL isDefaultNews = true;
+ if (aApps & nsIShellService::BROWSER)
+ pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME, &isDefaultBrowser);
+ if (aApps & nsIShellService::MAIL)
+ pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_MAIL, &isDefaultMail);
+ if (aApps & nsIShellService::NEWS)
+ pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_NEWS, &isDefaultNews);
+
+ *aIsDefaultClient = isDefaultBrowser && isDefaultNews && isDefaultMail;
+
+ pAAR->Release();
+ return true;
+ }
+ return false;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::IsDefaultClient(bool aStartupCheck, uint16_t aApps, bool *aIsDefaultClient)
+{
+ *aIsDefaultClient = true;
+
+ // for each type, check if it is the default app
+ // browser check needs to be at the top
+ if (aApps & nsIShellService::BROWSER) {
+ *aIsDefaultClient &= TestForDefault(gBrowserSettings, sizeof(gBrowserSettings)/sizeof(SETTING));
+ // Only check if this app is default on Vista if the previous checks
+ // indicate that this app is the default.
+ if (*aIsDefaultClient)
+ IsDefaultClientVista(nsIShellService::BROWSER, aIsDefaultClient);
+ }
+ if (aApps & nsIShellService::MAIL) {
+ *aIsDefaultClient &= TestForDefault(gMailSettings, sizeof(gMailSettings)/sizeof(SETTING));
+ // Only check if this app is default on Vista if the previous checks
+ // indicate that this app is the default.
+ if (*aIsDefaultClient)
+ IsDefaultClientVista(nsIShellService::MAIL, aIsDefaultClient);
+ }
+ if (aApps & nsIShellService::NEWS) {
+ *aIsDefaultClient &= TestForDefault(gNewsSettings, sizeof(gNewsSettings)/sizeof(SETTING));
+ // Only check if this app is default on Vista if the previous checks
+ // indicate that this app is the default.
+ if (*aIsDefaultClient)
+ IsDefaultClientVista(nsIShellService::NEWS, aIsDefaultClient);
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDefaultClient(bool aForAllUsers,
+ bool aClaimAllTypes, uint16_t aApps)
+{
+ nsAutoString appHelperPath;
+ if (NS_FAILED(GetHelperPath(appHelperPath)))
+ return NS_ERROR_FAILURE;
+
+ if (aForAllUsers)
+ appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
+ else {
+ appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
+ if (aApps & nsIShellService::BROWSER)
+ appHelperPath.AppendLiteral(" Browser");
+
+ if (aApps & nsIShellService::MAIL)
+ appHelperPath.AppendLiteral(" Mail");
+
+ if (aApps & nsIShellService::NEWS)
+ appHelperPath.AppendLiteral(" News");
+ }
+
+ return LaunchHelper(appHelperPath);
+}
+
+static nsresult
+WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
+{
+ nsresult rv;
+
+ RefPtr<SourceSurface> surface =
+ aImage->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE);
+ NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
+
+ // For either of the following formats we want to set the biBitCount member
+ // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
+ // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
+ // for the BI_RGB value we use for the biCompression member.
+ MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+ surface->GetFormat() == SurfaceFormat::B8G8R8X8);
+
+ RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
+ NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
+
+ int32_t width = dataSurface->GetSize().width;
+ int32_t height = dataSurface->GetSize().height;
+ int32_t bytesPerPixel = 4 * sizeof(uint8_t);
+ int32_t bytesPerRow = bytesPerPixel * width;
+
+ // initialize these bitmap structs which we will later
+ // serialize directly to the head of the bitmap file
+ BITMAPINFOHEADER bmi;
+ bmi.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.biWidth = width;
+ bmi.biHeight = height;
+ bmi.biPlanes = 1;
+ bmi.biBitCount = (WORD)bytesPerPixel*8;
+ bmi.biCompression = BI_RGB;
+ bmi.biSizeImage = bytesPerRow * height;
+ bmi.biXPelsPerMeter = 0;
+ bmi.biYPelsPerMeter = 0;
+ bmi.biClrUsed = 0;
+ bmi.biClrImportant = 0;
+
+ BITMAPFILEHEADER bf;
+ bf.bfType = 0x4D42; // 'BM'
+ bf.bfReserved1 = 0;
+ bf.bfReserved2 = 0;
+ bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
+ bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
+
+ // get a file output stream
+ nsCOMPtr<nsIOutputStream> stream;
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ DataSourceSurface::MappedSurface map;
+ if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // write the bitmap headers and rgb pixel data to the file
+ rv = NS_ERROR_FAILURE;
+ if (stream) {
+ uint32_t written;
+ stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
+ if (written == sizeof(BITMAPFILEHEADER)) {
+ stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
+ if (written == sizeof(BITMAPINFOHEADER)) {
+ // write out the image data backwards because the desktop won't
+ // show bitmaps with negative heights for top-to-bottom
+ uint32_t i = map.mStride * height;
+ rv = NS_OK;
+ do {
+ i -= map.mStride;
+ stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
+ if (written != bytesPerRow) {
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ } while (i != 0);
+ }
+ }
+
+ stream->Close();
+ }
+
+ dataSurface->Unmap();
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDesktopBackground(dom::Element* aElement,
+ int32_t aPosition,
+ const nsACString& aImageName)
+{
+ if (!aElement || !aElement->IsHTMLElement(nsGkAtoms::img)) {
+ // XXX write background loading stuff!
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIImageLoadingContent> imageContent =
+ do_QueryInterface(aElement, &rv);
+ if (!imageContent)
+ return rv;
+
+ // get the image container
+ nsCOMPtr<imgIRequest> request;
+ rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+ getter_AddRefs(request));
+ if (!request)
+ return rv;
+
+ nsCOMPtr<imgIContainer> container;
+ rv = request->GetImage(getter_AddRefs(container));
+ if (!container)
+ return NS_ERROR_FAILURE;
+
+ // get the file name from localized strings
+ nsCOMPtr<nsIStringBundleService> bundleService(
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStringBundle> shellBundle;
+ rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
+ getter_AddRefs(shellBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // e.g. "Desktop Background.bmp"
+ nsAutoString fileLeafName;
+ rv = shellBundle->GetStringFromName("desktopBackgroundLeafNameWin",
+ fileLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the profile root directory
+ nsCOMPtr<nsIFile> file;
+ rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
+ getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // eventually, the path is "%APPDATA%\Mozilla\SeaMonkey\Desktop Background.bmp"
+ rv = file->Append(fileLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString path;
+ rv = file->GetPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // write the bitmap to a file in the profile directory
+ rv = WriteBitmap(file, container);
+
+ // if the file was written successfully, set it as the system wallpaper
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIWindowsRegKey> key(do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ u"Control Panel\\Desktop"_ns,
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int style = 0;
+ switch (aPosition) {
+ case BACKGROUND_STRETCH:
+ style = 2;
+ break;
+ case BACKGROUND_FILL:
+ style = 10;
+ break;
+ case BACKGROUND_FIT:
+ style = 6;
+ break;
+ }
+
+ nsString value;
+ value.AppendInt(style);
+ rv = key->WriteStringValue(u"WallpaperStyle"_ns, value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ value.Assign(aPosition == BACKGROUND_TILE ? '1' : '0');
+ rv = key->WriteStringValue(u"TileWallpaper"_ns, value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
+ SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor)
+{
+ uint32_t color = ::GetSysColor(COLOR_DESKTOP);
+ *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor)
+{
+ int parameter = COLOR_DESKTOP;
+ BYTE r = (aColor >> 16);
+ BYTE g = (aColor << 16) >> 24;
+ BYTE b = (aColor << 24) >> 24;
+ COLORREF color = RGB(r,g,b);
+
+ ::SetSysColors(1, &parameter, &color);
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> key(do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+ u"Control Panel\\Colors"_ns,
+ nsIWindowsRegKey::ACCESS_SET_VALUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ wchar_t rgb[12];
+ _snwprintf(rgb, 12, L"%u %u %u", r, g, b);
+ rv = key->WriteStringValue(u"Background"_ns,
+ nsDependentString(rgb));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return key->Close();
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
+ const nsACString& aURI)
+{
+ nsresult rv;
+ nsCOMPtr<nsIProcess> process =
+ do_CreateInstance("@mozilla.org/process/util;1", &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = process->Init(aApplication);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsCString& spec = PromiseFlatCString(aURI);
+ const char* specStr = spec.get();
+ return process->Run(false, &specStr, 1);
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval)
+{
+ *_retval = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowsRegKey> key(do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+ u"feed\\shell\\open\\command"_ns,
+ nsIWindowsRegKey::ACCESS_READ);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString path;
+ rv = key->ReadStringValue(EmptyString(), path);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (path.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ if (path.First() == '"') {
+ // Everything inside the quotes
+ path = Substring(path, 1, path.FindChar('"', 1) - 1);
+ } else {
+ // Everything up to the first space
+ path = Substring(path, 0, path.FindChar(' '));
+ }
+
+ nsCOMPtr<nsIFile> defaultReader =
+ do_CreateInstance("@mozilla.org/file/local;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = defaultReader->InitWithPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = defaultReader->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_ERROR_FAILURE;
+
+ NS_ADDREF(*_retval = defaultReader);
+ return NS_OK;
+}
diff --git a/comm/suite/components/shell/nsWindowsShellService.h b/comm/suite/components/shell/nsWindowsShellService.h
new file mode 100644
index 0000000000..a29dff2e5d
--- /dev/null
+++ b/comm/suite/components/shell/nsWindowsShellService.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nscore.h"
+#include "nsShellService.h"
+#include "nsString.h"
+#include "nsIShellService.h"
+#include "mozilla/Attributes.h"
+#include "nsSuiteCID.h"
+
+#include <windows.h>
+
+typedef struct {
+ const char* keyName;
+ const char* valueName;
+ const char* valueData;
+
+ int32_t flags;
+} SETTING;
+
+class nsWindowsShellService final : public nsIShellService
+{
+public:
+ nsWindowsShellService() {};
+ nsresult Init();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISHELLSERVICE
+
+protected:
+ ~nsWindowsShellService() {}
+ bool IsDefaultClientVista(uint16_t aApps, bool* aIsDefaultClient);
+ bool TestForDefault(SETTING aSettings[], int32_t aSize);
+
+private:
+ nsString mAppLongPath;
+ nsString mAppShortPath;
+};
+
diff --git a/comm/suite/components/sidebar/SuiteSidebar.manifest b/comm/suite/components/sidebar/SuiteSidebar.manifest
new file mode 100644
index 0000000000..3506176b04
--- /dev/null
+++ b/comm/suite/components/sidebar/SuiteSidebar.manifest
@@ -0,0 +1,4 @@
+component {22117140-9c6e-11d3-aaf1-00805f8a4905} nsSidebar.js
+contract @mozilla.org/sidebar;1 {22117140-9c6e-11d3-aaf1-00805f8a4905}
+category JavaScript-global-property sidebar @mozilla.org/sidebar;1
+category JavaScript-global-property external @mozilla.org/sidebar;1
diff --git a/comm/suite/components/sidebar/content/PageNotFound.xul b/comm/suite/components/sidebar/content/PageNotFound.xul
new file mode 100644
index 0000000000..960357f947
--- /dev/null
+++ b/comm/suite/components/sidebar/content/PageNotFound.xul
@@ -0,0 +1,13 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE page SYSTEM "chrome://communicator/locale/sidebar/sidebarOverlay.dtd">
+
+<page xmlns ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <description class="header">&sidebar.pagenotfound.label;</description>
+</page>
+
diff --git a/comm/suite/components/sidebar/content/customize-panel.js b/comm/suite/components/sidebar/content/customize-panel.js
new file mode 100644
index 0000000000..912619108a
--- /dev/null
+++ b/comm/suite/components/sidebar/content/customize-panel.js
@@ -0,0 +1,43 @@
+/* -*- Mode: Java -*-
+ * 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/. */
+
+// the rdf service
+var RDF = Cc["@mozilla.org/rdf/rdf-service;"]
+ .getService(Ci.nsIRDFService);
+
+var NC = "http://home.netscape.com/NC-rdf#";
+
+var sidebarObj = new Object;
+var customizeObj = new Object;
+
+function Init()
+{
+ customizeObj.id = window.arguments[0];
+ customizeObj.url = window.arguments[1];
+ sidebarObj.datasource_uri = window.arguments[2];
+ sidebarObj.resource = window.arguments[3];
+
+ sidebarObj.datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
+
+ var customize_frame = document.getElementById('customize_frame');
+ customize_frame.setAttribute('src', customizeObj.url);
+}
+
+// Use an assertion to pass a "refresh" event to all the sidebars.
+// They use observers to watch for this assertion (in sidebarOverlay.js).
+function RefreshPanel() {
+ var sb_resource = RDF.GetResource(sidebarObj.resource);
+ var refresh_resource = RDF.GetResource(NC + "refresh_panel");
+ var panel_resource = RDF.GetLiteral(customizeObj.id);
+
+ sidebarObj.datasource.Assert(sb_resource,
+ refresh_resource,
+ panel_resource,
+ true);
+ sidebarObj.datasource.Unassert(sb_resource,
+ refresh_resource,
+ panel_resource);
+}
+
diff --git a/comm/suite/components/sidebar/content/customize-panel.xul b/comm/suite/components/sidebar/content/customize-panel.xul
new file mode 100644
index 0000000000..3fcd3908f1
--- /dev/null
+++ b/comm/suite/components/sidebar/content/customize-panel.xul
@@ -0,0 +1,23 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<!DOCTYPE window>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="navigator:browser"
+ onload="Init();"
+ onunload="RefreshPanel();;">
+
+ <script src="chrome://communicator/content/sidebar/customize-panel.js" />
+
+ <browser id="customize_frame"
+ type="content"
+ primary="true"
+ src="about:blank"
+ flex="1"/>
+</window>
diff --git a/comm/suite/components/sidebar/content/customize.js b/comm/suite/components/sidebar/content/customize.js
new file mode 100644
index 0000000000..e8400e909c
--- /dev/null
+++ b/comm/suite/components/sidebar/content/customize.js
@@ -0,0 +1,692 @@
+/* -*- Mode: Java; tab-width: 4; insert-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/. */
+
+//////////////////////////////////////////////////////////////
+// Import modules
+//////////////////////////////////////////////////////////////
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+//////////////////////////////////////////////////////////////
+// Global variables
+//////////////////////////////////////////////////////////////
+
+// Set to true for noise
+const CUST_DEBUG = false;
+
+// the rdf service
+var RDF = Cc["@mozilla.org/rdf/rdf-service;1"]
+ .getService(Ci.nsIRDFService);
+var NC = "http://home.netscape.com/NC-rdf#";
+
+var sidebarObj = new Object;
+var allPanelsObj = new Object;
+var original_panels = new Array();
+
+//////////////////////////////////////////////////////////////
+// Sidebar Init/Destroy
+//////////////////////////////////////////////////////////////
+
+function sidebar_customize_init()
+{
+ allPanelsObj.datasources = window.arguments[0];
+ allPanelsObj.resource = window.arguments[1];
+ sidebarObj.datasource_uri = window.arguments[2];
+ sidebarObj.resource = window.arguments[3];
+
+ debug("Init: all panels datasources = " + allPanelsObj.datasources);
+ debug("Init: all panels resource = " + allPanelsObj.resource);
+ debug("Init: sidebarObj.datasource_uri = " + sidebarObj.datasource_uri);
+ debug("Init: sidebarObj.resource = " + sidebarObj.resource);
+
+ var all_panels = document.getElementById('other-panels');
+ var current_panels = document.getElementById('current-panels');
+
+ debug("Adding observer to all panels database.");
+ all_panels.database.AddObserver(panels_observer);
+
+ allPanelsObj.datasources = allPanelsObj.datasources.trim().split(/\s+/);
+ for (var ii = 0; ii < allPanelsObj.datasources.length; ii++) {
+ debug("Init: Adding "+allPanelsObj.datasources[ii]);
+
+ // This will load the datasource, if it isn't already.
+ var datasource = RDF.GetDataSource(allPanelsObj.datasources[ii]);
+ all_panels.database.AddDataSource(datasource);
+ }
+
+ // Add the datasource for current list of panels. It selects panels out
+ // of the other datasources.
+ debug("Init: Adding current panels, "+sidebarObj.datasource_uri);
+ sidebarObj.datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
+
+ // Root the customize dialog at the correct place.
+ debug("Init: reset all panels ref, "+allPanelsObj.resource);
+ all_panels.setAttribute('ref', allPanelsObj.resource);
+
+ // Create a "container" wrapper around the current panels to
+ // manipulate the RDF:Seq more easily.
+ var panel_list = sidebarObj.datasource.GetTarget(RDF.GetResource(sidebarObj.resource), RDF.GetResource(NC + "panel-list"), true);
+ sidebarObj.container = Cc["@mozilla.org/rdf/container;1"].createInstance(Ci.nsIRDFContainer);
+ sidebarObj.container.Init(sidebarObj.datasource, panel_list);
+
+ // Add all the current panels to the tree
+ current_panels = sidebarObj.container.GetElements();
+ while (current_panels.hasMoreElements()) {
+ var panel = current_panels.getNext().QueryInterface(Ci.nsIRDFResource);
+ if (add_node_to_current_list(sidebarObj.datasource, panel) >= 0) {
+ original_panels.push(panel.Value);
+ original_panels[panel.Value] = true;
+ }
+ }
+
+ var links =
+ all_panels.database.GetSources(RDF.GetResource(NC + "haslink"),
+ RDF.GetLiteral("true"), true);
+
+ while (links.hasMoreElements()) {
+ var folder =
+ links.getNext().QueryInterface(Ci.nsIRDFResource);
+ var folder_name = folder.Value;
+ debug("+++ fixing up remote container " + folder_name + "\n");
+ fixup_remote_container(folder_name);
+ }
+
+ sizeToContent();
+}
+
+function sidebar_customize_destruct()
+{
+ var all_panels = document.getElementById('other-panels');
+ debug("Removing observer from all_panels database.");
+ all_panels.database.RemoveObserver(panels_observer);
+}
+
+
+//////////////////////////////////////////////////////////////////
+// Panels' RDF Datasource Observer
+//////////////////////////////////////////////////////////////////
+var panels_observer = {
+ onAssert : function(ds,src,prop,target) {
+ //debug ("observer: assert");
+ // "refresh" is asserted by select menu and by customize.js.
+ if (prop == RDF.GetResource(NC + "link")) {
+ setTimeout(fixup_remote_container, 100, src.Value);
+ }
+ },
+ onUnassert : function(ds,src,prop,target) {
+ //debug ("observer: unassert");
+ },
+ onChange : function(ds,src,prop,old_target,new_target) {
+ //debug ("observer: change");
+ },
+ onMove : function(ds,old_src,new_src,prop,target) {
+ //debug ("observer: move");
+ },
+ onBeginUpdateBatch : function(ds) {
+ //debug ("observer: onBeginUpdateBatch");
+ },
+ onEndUpdateBatch : function(ds) {
+ //debug ("observer: onEndUpdateBatch");
+ }
+};
+
+function fixup_remote_container(id)
+{
+ debug('fixup_remote_container('+id+')');
+
+ var container = document.getElementById(id);
+ if (container) {
+ container.setAttribute('container', 'true');
+ container.removeAttribute('open');
+ }
+}
+
+function fixup_children(id) {
+ // Add container="true" on nodes with "link" attribute
+ var treeitem = document.getElementById(id);
+
+ var children = treeitem.childNodes.item(1).childNodes;
+ for (var ii=0; ii < children.length; ii++) {
+ var child = children.item(ii);
+ if (child.getAttribute('link') != '' &&
+ child.getAttribute('container') != 'true') {
+ child.setAttribute('container', 'true');
+ child.removeAttribute('open');
+ }
+ }
+}
+
+function get_attr(registry,service,attr_name)
+{
+ var attr = registry.GetTarget(service,
+ RDF.GetResource(NC + attr_name),
+ true);
+ if (attr)
+ attr = attr.QueryInterface(Ci.nsIRDFLiteral);
+ if (attr)
+ attr = attr.Value;
+ return attr;
+}
+
+function SelectChangeForOtherPanels(event, target)
+{
+ enable_buttons_for_other_panels();
+}
+
+function ClickOnOtherPanels(event)
+{
+ var tree = document.getElementById("other-panels");
+
+ var rowIndex = -1;
+ if (event.type == "click" && event.button == 0) {
+ var b = tree.treeBoxObject;
+ var cell = b.getCellAt(event.clientX, event.clientY);
+
+ if (cell.childElt == "twisty" || event.detail == 2) {
+ rowIndex = cell.row;
+ }
+ }
+
+ if (rowIndex < 0) return;
+
+ var treeitem = tree.contentView.getItemAtIndex(rowIndex);
+ var res = RDF.GetResource(treeitem.id);
+
+ if (treeitem.getAttribute('container') == 'true') {
+ if (treeitem.getAttribute('open') == 'true') {
+ var link = treeitem.getAttribute('link');
+ var loaded_link = treeitem.getAttribute('loaded_link');
+ if (link != '' && !loaded_link) {
+ debug("Has remote datasource: "+link);
+ add_datasource_to_other_panels(link);
+ treeitem.setAttribute('loaded_link', 'true');
+ } else {
+ setTimeout(fixup_children, 100, treeitem.getAttribute('id'));
+ }
+ }
+ }
+
+ // Remove the selection in the "current" panels list
+ var current_panels = document.getElementById('current-panels');
+ current_panels.view.selection.clearSelection();
+ enable_buttons_for_current_panels();
+}
+
+function add_datasource_to_other_panels(link) {
+ // Convert the |link| attribute into a URL
+ var url = document.location;
+ debug("Current URL: " +url);
+ debug("Current link: " +link);
+
+ var uri = Cc['@mozilla.org/network/standard-url;1'].createInstance();
+ uri = uri.QueryInterface(Ci.nsIURI);
+ uri.spec = url;
+ uri = uri.resolve(link);
+
+ debug("New URL: " +uri);
+
+ // Add the datasource to the tree
+ var all_panels = document.getElementById('other-panels');
+ all_panels.database.AddDataSource(RDF.GetDataSource(uri));
+
+ // XXX This is a hack to force re-display
+ //all_panels.setAttribute('ref', allPanelsObj.resource);
+}
+
+// Handle a selection change in the current panels.
+function SelectChangeForCurrentPanels() {
+ // Remove the selection in the available panels list
+ var all_panels = document.getElementById('other-panels');
+ all_panels.view.selection.clearSelection();
+
+ enable_buttons_for_current_panels();
+}
+
+// Move the selected item up the the current panels list.
+function MoveUp() {
+ var tree = document.getElementById('current-panels');
+ if (tree.view.selection.count == 1) {
+ var index = tree.currentIndex;
+ var selected = tree.contentView.getItemAtIndex(index);
+ var before = selected.previousSibling;
+ if (before) {
+ selected.remove();
+ before.parentNode.insertBefore(selected, before);
+ tree.view.selection.select(index-1);
+ tree.treeBoxObject.ensureRowIsVisible(index-1);
+ }
+ }
+}
+
+// Move the selected item down the the current panels list.
+function MoveDown() {
+ var tree = document.getElementById('current-panels');
+ if (tree.view.selection.count == 1) {
+ var index = tree.currentIndex;
+ var selected = tree.contentView.getItemAtIndex(index);
+ if (selected.nextSibling) {
+ if (selected.nextSibling.nextSibling)
+ selected.parentNode.insertBefore(selected, selected.nextSibling.nextSibling);
+ else
+ selected.parentNode.appendChild(selected);
+ tree.view.selection.select(index+1);
+ tree.treeBoxObject.ensureRowIsVisible(index+1);
+ }
+ }
+}
+
+function PreviewPanel()
+{
+ var tree = document.getElementById('other-panels');
+ var database = tree.database;
+ var sel = tree.view.selection;
+ var rangeCount = sel.getRangeCount();
+ for (var range = 0; range < rangeCount; ++range) {
+ var min = {}, max = {};
+ sel.getRangeAt(range, min, max);
+ for (var index = min.value; index <= max.value; ++index) {
+ var item = tree.contentView.getItemAtIndex(index);
+ var res = RDF.GetResource(item.id);
+
+ var preview_name = get_attr(database, res, 'title');
+ var preview_URL = get_attr(database, res, 'content');
+ if (!preview_URL || !preview_name) continue;
+
+ window.openDialog("chrome://communicator/content/sidebar/preview.xul",
+ "_blank", "chrome,resizable,close,dialog=no",
+ preview_name, preview_URL);
+ }
+ }
+}
+
+// Add the selected panel(s).
+function AddPanel()
+{
+ var added = -1;
+
+ var tree = document.getElementById('other-panels');
+ var database = tree.database;
+ var sel = tree.view.selection;
+ var ranges = sel.getRangeCount();
+ for (var range = 0; range < ranges; ++range) {
+ var min = {}, max = {};
+ sel.getRangeAt(range, min, max);
+ for (var index = min.value; index <= max.value; ++index) {
+ var item = tree.contentView.getItemAtIndex(index);
+ if (item.getAttribute("container") != "true") {
+ var res = RDF.GetResource(item.id);
+ // Add the panel to the current list.
+ added = add_node_to_current_list(database, res);
+ }
+ }
+ }
+
+ if (added >= 0) {
+ // Remove the selection in the other list.
+ // Selection will move to "current" list.
+ tree.view.selection.clearSelection();
+
+ var current_panels = document.getElementById('current-panels');
+ current_panels.view.selection.select(added);
+ current_panels.treeBoxObject.ensureRowIsVisible(added);
+ }
+}
+
+// Copy a panel node into a database such as the current panel list.
+function add_node_to_current_list(registry, service)
+{
+ debug("Adding "+service.Value);
+
+ // Copy out the attributes we want
+ var option_title = get_attr(registry, service, 'title');
+ var option_customize = get_attr(registry, service, 'customize');
+ var option_content = get_attr(registry, service, 'content');
+ if (!option_title || !option_content)
+ return -1;
+
+ var tree = document.getElementById('current-panels');
+ var tree_root = tree.lastChild;
+
+ // Check to see if the panel already exists...
+ var i = 0;
+ for (var treeitem = tree_root.firstChild; treeitem; treeitem = treeitem.nextSibling) {
+ if (treeitem.id == service.Value)
+ // The panel is already in the current panel list.
+ // Avoid adding it twice.
+ return i;
+ ++i;
+ }
+
+ // Create a treerow for the new panel
+ var item = document.createElement('treeitem');
+ var row = document.createElement('treerow');
+ var cell = document.createElement('treecell');
+
+ // Copy over the attributes
+ item.setAttribute('id', service.Value);
+ cell.setAttribute('label', option_title);
+
+ // Add it to the current panels tree
+ item.appendChild(row);
+ row.appendChild(cell);
+ tree_root.appendChild(item);
+ return i;
+}
+
+// Remove the selected panel(s) from the current list tree.
+function RemovePanel()
+{
+ var tree = document.getElementById('current-panels');
+ var sel = tree.view.selection;
+
+ var nextNode = -1;
+ var rangeCount = sel.getRangeCount();
+ for (var range = rangeCount-1; range >= 0; --range) {
+ var min = {}, max = {};
+ sel.getRangeAt(range, min, max);
+ for (var index = max.value; index >= min.value; --index) {
+ var item = tree.contentView.getItemAtIndex(index);
+ nextNode = item.nextSibling ? index : -1;
+ item.remove();
+ }
+ }
+
+ if (nextNode >= 0)
+ sel.select(nextNode);
+}
+
+// Bring up a new window with the customize url
+// for an individual panel.
+function CustomizePanel()
+{
+ var tree = document.getElementById('current-panels');
+ var numSelected = tree.view.selection.count;
+
+ if (numSelected == 1) {
+ var index = tree.currentIndex;
+ var selectedNode = tree.contentView.getItemAtIndex(index);
+ var panel_id = selectedNode.getAttribute('id');
+ var customize_url = selectedNode.getAttribute('customize');
+
+ debug("url = " + customize_url);
+
+ if (!customize_url) return;
+
+ window.openDialog('chrome://communicator/content/sidebar/customize-panel.xul',
+ '_blank',
+ 'chrome,resizable,width=690,height=600,dialog=no,close',
+ panel_id,
+ customize_url,
+ sidebarObj.datasource_uri,
+ sidebarObj.resource);
+ }
+}
+
+function BrowseMorePanels()
+{
+ var url = '';
+ var browser_url = "chrome://navigator/content/navigator.xul";
+ var locale;
+ try {
+ url = Services.prefs.getCharPref("sidebar.customize.more_panels.url");
+ var temp = Services.prefs.getCharPref("browser.chromeURL");
+ if (temp)
+ browser_url = temp;
+ } catch(ex) {
+ debug("Unable to get prefs: "+ex);
+ }
+ window.openDialog(browser_url, "_blank", "chrome,all,dialog=no", url);
+}
+
+function customize_getBrowserURL()
+{
+ return url;
+}
+
+// Serialize the new list of panels.
+function Save()
+{
+ persist_dialog_dimensions();
+
+ var all_panels = document.getElementById('other-panels');
+ var current_panels = document.getElementById('current-panels');
+
+ // See if list membership has changed
+ var panels = [];
+ var tree_root = current_panels.lastChild.childNodes;
+ var list_unchanged = (tree_root.length == original_panels.length);
+ for (var i = 0; i < tree_root.length; i++) {
+ var panel = tree_root[i].id;
+ panels.push(panel);
+ panels[panel] = true;
+ if (list_unchanged && original_panels[i] != panel)
+ list_unchanged = false;
+ }
+ if (list_unchanged)
+ return;
+
+ // Remove all the current panels from the datasource.
+ current_panels = sidebarObj.container.GetElements();
+ while (current_panels.hasMoreElements()) {
+ panel = current_panels.getNext().QueryInterface(Ci.nsIRDFResource);
+
+ // "Check if the item is one of the broadcaster panels imported to RDF from
+ // mainBroadcasterSet. If so, then don't remove it from datasource.
+ var master_list = sidebarObj.datasource.GetTarget(RDF.GetResource(allPanelsObj.resource), RDF.GetResource(NC + "panel-list"), true);
+ var masterSeq = Cc["@mozilla.org/rdf/container;1"]
+ .createInstance(Ci.nsIRDFContainer);
+ masterSeq.Init(sidebarObj.datasource, master_list);
+ var inmaster = (masterSeq.IndexOf(panel) != -1);
+
+ if (panel.Value in panels || inmaster) {
+ // This panel will remain in the sidebar.
+ // Remove the resource, but keep all the other attributes.
+ // Removing it will allow it to be added in the correct order.
+ // Saving the attributes will preserve things such as the exclude state.
+ sidebarObj.container.RemoveElement(panel, false);
+ } else {
+ // Kiss it goodbye.
+ delete_resource_deeply(sidebarObj.container, panel);
+ }
+ }
+
+ // Add the new list of panels
+ for (var ii = 0; ii < panels.length; ++ii) {
+ var id = panels[ii];
+ var resource = RDF.GetResource(id);
+ if (id in original_panels) {
+ sidebarObj.container.AppendElement(resource);
+ } else {
+ copy_resource_deeply(all_panels.database, resource, sidebarObj.container);
+ }
+ }
+ refresh_all_sidebars();
+
+ // Write the modified panels out.
+ sidebarObj.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+}
+
+// Search for an element in an array
+function has_element(array, element) {
+ for (var ii=0; ii < array.length; ii++) {
+ if (array[ii] == element) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Search for targets from resource in datasource
+function has_targets(datasource, resource) {
+ var arcs = datasource.ArcLabelsOut(resource);
+ return arcs.hasMoreElements();
+}
+
+// Use an assertion to pass a "refresh" event to all the sidebars.
+// They use observers to watch for this assertion (in sidebarOverlay.js).
+function refresh_all_sidebars() {
+ sidebarObj.datasource.Assert(RDF.GetResource(sidebarObj.resource),
+ RDF.GetResource(NC + "refresh"),
+ RDF.GetLiteral("true"),
+ true);
+ sidebarObj.datasource.Unassert(RDF.GetResource(sidebarObj.resource),
+ RDF.GetResource(NC + "refresh"),
+ RDF.GetLiteral("true"));
+}
+
+// Remove a resource and all the arcs out from it.
+function delete_resource_deeply(container, resource) {
+ var arcs = container.DataSource.ArcLabelsOut(resource);
+ while (arcs.hasMoreElements()) {
+ var arc = arcs.getNext();
+ var targets = container.DataSource.GetTargets(resource, arc, true);
+ while (targets.hasMoreElements()) {
+ var target = targets.getNext();
+ container.DataSource.Unassert(resource, arc, target, true);
+ }
+ }
+ container.RemoveElement(resource, false);
+}
+
+// Copy a resource and all its arcs out to a new container.
+function copy_resource_deeply(source_datasource, resource, dest_container) {
+ var arcs = source_datasource.ArcLabelsOut(resource);
+ while (arcs.hasMoreElements()) {
+ var arc = arcs.getNext();
+ var targets = source_datasource.GetTargets(resource, arc, true);
+ while (targets.hasMoreElements()) {
+ var target = targets.getNext();
+ dest_container.DataSource.Assert(resource, arc, target, true);
+ }
+ }
+ dest_container.AppendElement(resource);
+}
+
+function enable_buttons_for_other_panels()
+{
+ var add_button = document.getElementById('add_button');
+ var preview_button = document.getElementById('preview_button');
+ var all_panels = document.getElementById('other-panels');
+
+ var sel = all_panels.view.selection;
+ var num_selected = sel ? sel.count : 0;
+ if (sel) {
+ var ranges = sel.getRangeCount();
+ for (var range = 0; range < ranges; ++range) {
+ var min = {}, max = {};
+ sel.getRangeAt(range, min, max);
+ for (var index = min; index <= max; ++index) {
+ var node = all_panels.contentView.getItemAtIndex(index);
+ if (node.getAttribute('container') != 'true') {
+ ++num_selected;
+ }
+ }
+ }
+ }
+
+ if (num_selected > 0) {
+ add_button.removeAttribute('disabled');
+ preview_button.removeAttribute('disabled');
+ } else {
+ add_button.setAttribute('disabled','true');
+ preview_button.setAttribute('disabled','true');
+ }
+}
+
+function enable_buttons_for_current_panels() {
+ var up = document.getElementById('up');
+ var down = document.getElementById('down');
+ var tree = document.getElementById('current-panels');
+ var customize = document.getElementById('customize-button');
+ var remove = document.getElementById('remove-button');
+
+ var numSelected = tree.view.selection.count;
+ var canMoveUp = false, canMoveDown = false, customizeURL = '';
+
+ if (numSelected == 1 && tree.view.selection.isSelected(tree.currentIndex)) {
+ var selectedNode = tree.view.getItemAtIndex(tree.currentIndex);
+ customizeURL = selectedNode.getAttribute('customize');
+ canMoveUp = selectedNode != selectedNode.parentNode.firstChild;
+ canMoveDown = selectedNode != selectedNode.parentNode.lastChild;
+ }
+
+ up.disabled = !canMoveUp;
+ down.disabled = !canMoveDown;
+ customize.disabled = !customizeURL;
+ remove.disabled = !numSelected;
+}
+
+function persist_dialog_dimensions() {
+ // Stole this code from navigator.js to
+ // insure the windows dimensions are saved.
+
+ // Get the current window position/size.
+ var x = window.screenX;
+ var y = window.screenY;
+ var h = window.outerHeight;
+ var w = window.outerWidth;
+
+ // Store these into the window attributes (for persistence).
+ var win = document.getElementById( "main-window" );
+ win.setAttribute( "x", x );
+ win.setAttribute( "y", y );
+ win.setAttribute( "height", h );
+ win.setAttribute( "width", w );
+}
+
+///////////////////////////////////////////////////////////////
+// Handy Debug Tools
+//////////////////////////////////////////////////////////////
+var debug = null;
+var dump_attributes = null;
+var dump_tree = null;
+var _dump_tree_recur = null;
+
+if (!CUST_DEBUG) {
+ debug = function (s) {};
+ dump_attributes = function (node, depth) {};
+ dump_tree = function (node) {};
+ _dump_tree_recur = function (node, depth, index) {};
+} else {
+ debug = function (s) { dump("-*- sb customize: " + s + "\n"); };
+
+ dump_attributes = function (node, depth) {
+ var attributes = node.attributes;
+ var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | . ";
+
+ if (!attributes || attributes.length == 0) {
+ debug(indent.substr(indent.length - depth*2) + "no attributes");
+ }
+ for (var ii=0; ii < attributes.length; ii++) {
+ var attr = attributes.item(ii);
+ debug(indent.substr(indent.length - depth*2) + attr.name +
+ "=" + attr.value);
+ }
+ }
+ dump_tree = function (node) {
+ _dump_tree_recur(node, 0, 0);
+ }
+ _dump_tree_recur = function (node, depth, index) {
+ if (!node) {
+ debug("dump_tree: node is null");
+ }
+ var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | + ";
+ debug(indent.substr(indent.length - depth*2) + index +
+ " " + node.nodeName);
+ if (node.nodeType != Node.TEXT_NODE) {
+ dump_attributes(node, depth);
+ }
+ var kids = node.childNodes;
+ for (var ii=0; ii < kids.length; ii++) {
+ _dump_tree_recur(kids[ii], depth + 1, ii);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////
+// Install the load/unload handlers
+//////////////////////////////////////////////////////////////
+addEventListener("load", sidebar_customize_init, false);
+addEventListener("unload", sidebar_customize_destruct, false);
diff --git a/comm/suite/components/sidebar/content/customize.xul b/comm/suite/components/sidebar/content/customize.xul
new file mode 100644
index 0000000000..0e76f65445
--- /dev/null
+++ b/comm/suite/components/sidebar/content/customize.xul
@@ -0,0 +1,137 @@
+<?xml version="1.0"?> <!-- -*- Mode: HTML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sidebar/customize.css"
+ type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % customizeDTD SYSTEM "chrome://communicator/locale/sidebar/customize.dtd" >
+%customizeDTD;
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+]>
+
+<dialog
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="main-window"
+ title="&sidebar.customize.title;"
+ windowtype="sidebar:customize"
+ height="400"
+ persist="screenX screenY width height"
+ buttons="accept,cancel,extra2"
+ spacerflex="1"
+ buttonlabelextra2="&sidebar.more.label;"
+ buttonaccesskeyextra2="&sidebar.more.accesskey;"
+ ondialogextra2="BrowseMorePanels();"
+ ondialogaccept="return Save();">
+
+ <script src="chrome://communicator/content/sidebar/customize.js"/>
+
+ <hbox flex="1">
+ <vbox flex="1">
+ <label accesskey="&sidebar.customize.additional.accesskey;"
+ control="other-panels" value="&sidebar.customize.additional.label;"
+ crop="right"/>
+
+ <tree id="other-panels" flex="1"
+ datasources="rdf:null" hidecolumnpicker="true"
+ containment="http://home.netscape.com/NC-rdf#panel-list"
+ onselect="SelectChangeForOtherPanels(event, event.target.parentNode.parentNode);"
+ onclick="if (event.detail == 2) { AddPanel(); } ClickOnOtherPanels(event);">
+
+ <template>
+ <rule>
+ <conditions>
+ <content uri="?uri"/>
+ <triple subject="?uri" object="?panel-list"
+ predicate="http://home.netscape.com/NC-rdf#panel-list"/>
+ <member container="?panel-list" child="?panel"/>
+ </conditions>
+
+ <bindings>
+ <binding subject="?panel" object="?title"
+ predicate="http://home.netscape.com/NC-rdf#title"/>
+ <binding subject="?panel" object="?link"
+ predicate="http://home.netscape.com/NC-rdf#link"/>
+ </bindings>
+
+ <action>
+ <treechildren>
+ <treeitem uri="?panel" link="?link">
+ <treerow>
+ <treecell label="?title"/>
+ </treerow>
+ </treeitem>
+ </treechildren>
+ </action>
+ </rule>
+ </template>
+
+ <treecols>
+ <treecol id="AvailNameCol" flex="1" primary="true" hideheader="true"/>
+ </treecols>
+ </tree>
+
+ <!-- xxxslamm Need to add descriptive panel text here -->
+ <hbox class="button-group">
+ <button id="add_button" oncommand="AddPanel()"
+ label="&sidebar.customize.add.label;"
+ accesskey="&sidebar.customize.add.accesskey;" disabled="true"/>
+
+ <button id="preview_button" oncommand="PreviewPanel()"
+ label="&sidebar.customize.preview.label;"
+ accesskey="&sidebar.customize.preview.accesskey;"
+ disabled="true"/>
+ </hbox>
+ </vbox>
+
+ <separator orient="vertical"/>
+
+ <!-- The panels that the user currently has chosen -->
+ <vbox flex="1">
+ <label value="&sidebar.customize.current2.label;"
+ accesskey="&sidebar.customize.current2.accesskey;"
+ control="current-panels" crop="right"/>
+ <tree id="current-panels" flex="1" hidecolumnpicker="true"
+ onselect="SelectChangeForCurrentPanels();">
+ <treecols>
+ <treecol id="CurrentNameCol" flex="1" hideheader="true"/>
+ </treecols>
+
+ <treechildren/>
+ </tree>
+
+ <hbox class="button-group">
+ <button id="customize-button" oncommand="CustomizePanel();"
+ label="&sidebar.customize.customize.label;" disabled="true"
+ accesskey="&sidebar.customize.customize.accesskey;"/>
+ <button id="remove-button" oncommand="RemovePanel()"
+ label="&sidebar.customize.remove.label;" disabled="true"
+ accesskey="&sidebar.customize.remove.accesskey;"/>
+ </hbox>
+ </vbox>
+
+ <separator orient="vertical" class="thin"/>
+
+ <!-- The 'reorder' buttons -->
+ <vbox id="reorder">
+ <spacer flex="1"/>
+ <button oncommand="MoveUp();" id="up" class="up"
+ disabled="true" label="&sidebar.customize.up.label;"
+ accesskey="&sidebar.customize.up.accesskey;"/>
+ <button oncommand="MoveDown();" id="down" class="down"
+ disabled="true" label="&sidebar.customize.down.label;"
+ accesskey="&sidebar.customize.down.accesskey;"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ </hbox>
+
+</dialog>
+
diff --git a/comm/suite/components/sidebar/content/preview.js b/comm/suite/components/sidebar/content/preview.js
new file mode 100644
index 0000000000..c5238beacd
--- /dev/null
+++ b/comm/suite/components/sidebar/content/preview.js
@@ -0,0 +1,15 @@
+/* -*- Mode: Java -*-
+ * 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/. */
+
+function Init()
+{
+ var panel_name = window.arguments[0];
+ var panel_URL = window.arguments[1];
+
+ var panel_title = document.getElementById('paneltitle');
+ var preview_frame = document.getElementById('previewframe');
+ panel_title.setAttribute('label', panel_name);
+ preview_frame.setAttribute('src', panel_URL);
+}
diff --git a/comm/suite/components/sidebar/content/preview.xul b/comm/suite/components/sidebar/content/preview.xul
new file mode 100644
index 0000000000..ef2434c278
--- /dev/null
+++ b/comm/suite/components/sidebar/content/preview.xul
@@ -0,0 +1,30 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sidebar/sidebar.css"
+ type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sidebar/preview.css"
+ type="text/css"?>
+
+<!DOCTYPE window SYSTEM "chrome://communicator/locale/sidebar/preview.dtd" >
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="Init();"
+ title="&sidebar.preview.title.label;">
+
+ <script src="chrome://communicator/content/sidebar/preview.js" />
+
+ <vbox id="panel-container" flex="1">
+
+ <hbox id="paneltitle" class="box-texttab texttab-sidebar" selected="true"/>
+ <!-- <iframe id="previewframe" type="content" src="about:blank" flex="1"/>-->
+ <iframe class="box-panel" id="previewframe" type="content" src="about:blank" flex="1"/>
+
+ </vbox>
+
+</window>
diff --git a/comm/suite/components/sidebar/content/sidebarBindings.xml b/comm/suite/components/sidebar/content/sidebarBindings.xml
new file mode 100644
index 0000000000..675d591957
--- /dev/null
+++ b/comm/suite/components/sidebar/content/sidebarBindings.xml
@@ -0,0 +1,34 @@
+<?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/. -->
+
+
+<bindings id="globalBindings"
+ 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="texttab">
+ <content>
+ <xul:image class="box-texttab-left"/>
+ <xul:vbox class="box-texttab-text-container" xbl:inherits="value" flex="1">
+ <xul:spacer flex="1"/>
+ <xul:label class="box-texttab-text" xbl:inherits="value=label" crop="right"/>
+ <xul:spacer flex="1"/>
+ </xul:vbox>
+ <xul:image class="box-texttab-right"/>
+ <xul:spacer class="box-texttab-right-space"/>
+ </content>
+ </binding>
+
+ <binding id="sidebar-header-box" extends="xul:box">
+ <content align="center">
+ <xul:label class="sidebar-header-text" xbl:inherits="value=label,crop" crop="right" flex="1"/>
+ <xul:box>
+ <children/>
+ </xul:box>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/sidebar/content/sidebarOverlay.css b/comm/suite/components/sidebar/content/sidebarOverlay.css
new file mode 100644
index 0000000000..0d4df5f16a
--- /dev/null
+++ b/comm/suite/components/sidebar/content/sidebarOverlay.css
@@ -0,0 +1,78 @@
+/* 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/. */
+
+/** sidebarOverlay.css [CONTENT]
+ * This file is for style rules essential for correct sidebar operation.
+ * These rules will not change on a skin-by-skin basis.
+ **/
+
+#sidebar-box {
+ width: 162px;
+ min-height: 10px;
+ min-width: 30px;
+ max-width: 400px;
+}
+
+#sidebar-panels {
+ min-width: 1px;
+ min-height: 10px;
+}
+
+.iframe-panel {
+ min-width: 1px;
+ min-height: 1px;
+}
+
+#sidebar-iframe-no-panels {
+ min-width: 1px;
+ min-height: 1px;
+ overflow: auto;
+}
+
+.browser-sidebar {
+ min-width: 1px;
+ min-height: 1px;
+}
+
+/*
+ * Sidebar and Panel title buttons
+ */
+sidebarheader[type="box"] {
+ -moz-binding: url(chrome://communicator/content/sidebar/sidebarBindings.xml#sidebar-header-box);
+}
+sidebarheader[type="splitter"] {
+ -moz-binding: url(chrome://communicator/content/sidebar/sidebarBindings.xml#sidebar-header-splitter);
+ /* a vertical splitter */
+ cursor: n-resize;
+}
+
+.sidebarheader-main {
+ min-width: 1px;
+ min-height: 1px;
+}
+
+/**
+ * texttab folder lookalike e.g. for sidebar panel headers
+ */
+ .box-texttab
+ {
+ min-height : 10px;
+ min-width : 10px;
+ }
+
+ .box-texttab-right-space
+ {
+ min-width : 1px;
+ }
+
+/**
+ * prevent the notification in the sidebar from being too wide
+ */
+.sidebar-notificationbox > notification {
+ -moz-binding: url(chrome://communicator/content/bindings/notification.xml#sidebar-notification);
+}
+
+.sidebar-notificationbox > notification[value="addon-progress"] {
+ -moz-binding: url(chrome://communicator/content/bindings/notification.xml#sidebar-addon-progress-notification);
+}
diff --git a/comm/suite/components/sidebar/content/sidebarOverlay.js b/comm/suite/components/sidebar/content/sidebarOverlay.js
new file mode 100644
index 0000000000..95b0af029d
--- /dev/null
+++ b/comm/suite/components/sidebar/content/sidebarOverlay.js
@@ -0,0 +1,1704 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+// xul/id summary:
+//
+// <box> sidebar-box
+// <splitter> sidebar-panels-splitter
+// <box> sidebar-panels-splitter-box*
+// <sidebarheader> sidebar-title-box
+// <menubutton> sidebar-panel-picker*
+// <menupopup> sidebar-panel-picker-popup
+// <box> sidebar-panels
+// <template> sidebar-template*
+// <box> sidebar-iframe-no-panels
+// <splitter> sidebar-splitter
+// <menupopup> menu_View_Popup*
+// <menuitem> sidebar-menu
+
+//////////////////////////////////////////////////////////////
+// Import modules
+//////////////////////////////////////////////////////////////
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+//////////////////////////////////////////////////////////////
+// Global variables
+//////////////////////////////////////////////////////////////
+
+
+var gCurFrame;
+var gTimeoutID = null;
+var gMustInit = true;
+var gAboutToUncollapse = false;
+var gCheckMissingPanels = true;
+
+function setBlank()
+{
+ gTimeoutID = null;
+ gCurFrame.setAttribute('src', 'chrome://communicator/content/sidebar/PageNotFound.xul');
+}
+
+
+// Uncomment for debug output
+const SB_DEBUG = false;
+
+// pref for limiting number of tabs in view
+// initialized in sidebar_overlay_init()
+var gNumTabsInViewPref;
+
+// The rdf service
+var RDF = Cc["@mozilla.org/rdf/rdf-service;1"]
+ .getService(Ci.nsIRDFService);
+
+const NC = "http://home.netscape.com/NC-rdf#";
+
+// The directory services property to find panels.rdf
+const PANELS_RDF_FILE = "UPnls";
+const SIDEBAR_VERSION = "0.1";
+
+// The default sidebar:
+var sidebarObj = new Object;
+sidebarObj.never_built = true;
+
+//////////////////////////////////////////////////////////////////////
+// sbPanelList Class
+//
+// Wrapper around DOM representation of the sidebar. This UI event
+// handlers and the sidebar datasource observers call into this. This
+// class is responsible for keeping the DOM state of the sidebar up to
+// date.
+// This class does not make any changes to the sidebar rdf datasource.
+//////////////////////////////////////////////////////////////////////
+
+function sbPanelList(container_id)
+{
+ debug("sbPanelList("+container_id+")");
+ this.node = document.getElementById(container_id);
+ this.childNodes = this.node.childNodes;
+ this.initialized = false; // set after first display of tabs
+}
+
+sbPanelList.prototype.get_panel_from_id =
+function (id)
+{
+ debug("get_panel_from_id(" + id + ")");
+ var index = 0;
+ var header = null;
+ if (id && id != '') {
+ for (var ii=2; ii < this.node.childNodes.length; ii += 2) {
+ header = this.node.childNodes.item(ii);
+ if (header.getAttribute('id') == id) {
+ debug("get_panel_from_id: Found at index, " + ii);
+ index = ii;
+ break;
+ }
+ }
+ }
+ if (index > 0) {
+ return new sbPanel(id, header, index);
+ } else {
+ return null;
+ }
+}
+
+sbPanelList.prototype.get_panel_from_header_node =
+function (node)
+{
+ return this.get_panel_from_id(node.getAttribute('id'));
+}
+
+sbPanelList.prototype.get_panel_from_header_index =
+function (index)
+{
+ return this.get_panel_from_header_node(this.node.childNodes.item(index));
+}
+
+sbPanelList.prototype.find_first =
+function (panels)
+{
+ debug("pick_default_panel: length=" + this.node.childNodes.length);
+ for (var ii = 2; ii < this.node.childNodes.length; ii += 2) {
+ var panel = this.get_panel_from_header_index(ii);
+ if (!panel.is_excluded() && panel.is_in_view()) {
+ return panel;
+ }
+ }
+ return null;
+}
+
+sbPanelList.prototype.find_last =
+function (panels)
+{
+ debug("pick_default_panel: length=" + this.node.childNodes.length);
+ for (var ii=(this.node.childNodes.length - 1); ii >= 2; ii -= 2) {
+ var panel = this.get_panel_from_header_index(ii);
+ if (!panel.is_excluded() && panel.is_in_view()) {
+ return panel;
+ }
+ }
+ return null;
+}
+
+sbPanelList.prototype.visible_panels_exist =
+function ()
+{
+ var i;
+ var panels = this.node.childNodes;
+ for (i = 2; i < panels.length; i += 2)
+ {
+ if (!panels.item(i).hidden)
+ return true;
+ }
+ return false;
+}
+
+sbPanelList.prototype.num_panels_included =
+function ()
+{
+ var count = 0;
+ var panels = this.node.childNodes;
+ for (var i = 2; i < panels.length; i += 2)
+ {
+ var curr = this.get_panel_from_header_index(i);
+ if (!curr.is_excluded())
+ count++;
+ }
+ return count;
+}
+
+sbPanelList.prototype.num_panels_in_view =
+function ()
+{
+ var count = 0;
+ var panels = this.node.childNodes;
+ for (var i = 2; i < panels.length; i += 2)
+ {
+ var curr = this.get_panel_from_header_index(i);
+ if (curr.is_in_view())
+ count++;
+ }
+ return count;
+}
+
+sbPanelList.prototype.select =
+function (panel, force_reload)
+{
+ if (!force_reload && panel.is_selected()) {
+ return;
+ }
+ // select(): Open this panel and possibly reload it.
+ if (this.node.getAttribute('last-selected-panel') != panel.id) {
+ // "last-selected-panel" is used as a global variable.
+ // this.update() will reference "last-selected-panel".
+ // This way the value can be persisted in xulstore.json.
+ this.node.setAttribute('last-selected-panel', panel.id);
+ }
+ this.update(force_reload);
+}
+
+sbPanelList.prototype.exclude =
+function (panel)
+{
+ if (this.node.getAttribute('last-selected-panel') == panel.id) {
+ this.select_default_panel();
+ } else {
+ this.update(false);
+ }
+}
+
+
+sbPanelList.prototype.select_default_panel =
+function ()
+{
+ var default_panel = null
+
+ // First, check the XUL for the "defaultpanel" attribute of "sidebar-box".
+ var sidebar_container = document.getElementById('sidebar-box');
+ var content_default_id = sidebar_container.getAttribute('defaultpanel');
+ if (content_default_id != '') {
+ var content = sidebarObj.panels.get_panel_from_id(content_default_id);
+ if (content && !content.is_excluded() && content.is_in_view()) {
+ default_panel = content;
+ }
+ }
+
+ // Second, try to use the panel persisted in 'last-selected-panel'.
+ if (!default_panel) {
+ var last_selected_id = this.node.getAttribute('last-selected-panel');
+ if (last_selected_id != '') {
+ var last = sidebarObj.panels.get_panel_from_id(last_selected_id);
+ if (last && !last.is_excluded() && last.is_in_view()) {
+ default_panel = last;
+ }
+ }
+ }
+
+ // Finally, just use the last one in the list.
+ if (!default_panel) {
+ default_panel = this.find_last();
+ }
+
+ if (default_panel) {
+ this.node.setAttribute('last-selected-panel', default_panel.id);
+ }
+ this.update(false);
+}
+
+sbPanelList.prototype.refresh =
+function ()
+{
+ var last_selected_id = this.node.getAttribute('last-selected-panel');
+ var last_selected = sidebarObj.panels.get_panel_from_id(last_selected_id);
+ if (last_selected && last_selected.is_selected()) {
+ // The desired panel is already selected
+ this.update(false);
+ } else {
+ this.select_default_panel();
+ }
+}
+
+// panel_loader(): called from a timer that is set in sbPanelList.update()
+// Removes the "Loading..." screen when the panel has finished loading.
+function panel_loader() {
+ debug("panel_loader()");
+
+ if (gTimeoutID != null) {
+ clearTimeout(gTimeoutID);
+ gTimeoutID = null;
+ }
+
+ this.removeEventListener("load", panel_loader, true);
+ this.removeAttribute('collapsed');
+ // uncollapse the notificationbox element
+ this.parentNode.removeAttribute('collapsed');
+ // register a progress listener for the notificationbox,
+ // now that this browser has a docShell
+ this.parentNode.addProgressListener();
+ this.setAttribute('loadstate', 'loaded');
+ // hide the load area
+ this.parentNode.parentNode.firstChild.setAttribute('hidden', 'true');
+
+ if (this.hasAttribute('focusOnLoad')) {
+ var elementToFocus = this.contentDocument.documentElement.getAttribute('elementtofocus');
+ if (elementToFocus) {
+ var element = this.contentDocument.getElementById(elementToFocus);
+ if (element)
+ element.focus();
+ else
+ dump(elementToFocus + ' element was not found to focus!\n');
+ } else {
+ this.contentWindow.focus();
+ }
+ this.removeAttribute('focusOnLoad');
+ }
+}
+sbPanelList.prototype.update =
+function (force_reload)
+{
+ // This function requires that the attribute 'last-selected-panel'
+ // holds the id of a non-excluded panel. If it doesn't, no panel will
+ // be selected. The attribute is used instead of a function
+ // parameter to allow the value to be persisted in xulstore.json.
+ var selected_id = this.node.getAttribute('last-selected-panel');
+
+ if (sidebar_is_collapsed()) {
+ sidebarObj.collapsed = true;
+ } else {
+ sidebarObj.collapsed = false;
+ }
+
+ var num_included = sidebarObj.panels.num_panels_included();
+ if (num_included > gNumTabsInViewPref)
+ document.getElementById("nav-buttons-box").hidden = false;
+ else
+ document.getElementById("nav-buttons-box").hidden = true;
+
+ var have_set_top = 0;
+ var have_set_after_selected = 0;
+ var is_after_selected = 0;
+ var last_header = 0;
+ var num_in_view = 0;
+ debug("this.initialized: " + this.initialized);
+ for (var ii=2; ii < this.node.childNodes.length; ii += 2) {
+ var header = this.node.childNodes.item(ii);
+ var content = this.node.childNodes.item(ii+1);
+ var id = header.getAttribute('id');
+ var panel = new sbPanel(id, header, ii);
+ var excluded = panel.is_excluded();
+ var in_view = false;
+ if (!this.initialized)
+ {
+ if (num_in_view < gNumTabsInViewPref)
+ in_view = true;
+ }
+ else
+ {
+ if (header.getAttribute("in-view") == "true")
+ in_view = true;
+ }
+ if (excluded || !in_view)
+ {
+ debug("item("+ii/2+") excluded: " + excluded +
+ " in view: " + in_view);
+ header.setAttribute('hidden','true');
+ content.setAttribute('hidden','true');
+ if (!in_view)
+ {
+ header.setAttribute("in-view", false);
+ header.removeAttribute("top-panel");
+ header.removeAttribute("last-panel");
+ }
+ } else {
+ // only set if in view
+ if (!this.initialized || (num_in_view < gNumTabsInViewPref))
+ last_header = header;
+ header.removeAttribute('last-panel');
+ // only set if in view
+ if (!have_set_top &&
+ (!this.initialized || (header.getAttribute("in-view") == "true")))
+ {
+ header.setAttribute('top-panel','true');
+ have_set_top = 1;
+ } else {
+ header.removeAttribute('top-panel');
+ }
+ if (!have_set_after_selected && is_after_selected) {
+ header.setAttribute('first-panel-after-selected','true');
+ have_set_after_selected = 1
+ } else {
+ header.removeAttribute('first-panel-after-selected');
+ }
+ header.removeAttribute('hidden');
+ header.setAttribute("in-view", true);
+ num_in_view++;
+
+ // (a) when we have hit the maximum number of tabs that can be in view and no tab
+ // has been selected yet
+ // -or-
+ // (b) when we have reached the last tab we are about to display
+ if ( ((num_in_view == num_included) ||
+ (num_in_view == gNumTabsInViewPref)) &&
+ !is_after_selected )
+ {
+ selected_id = id;
+ this.node.setAttribute('last-selected-panel', id);
+ }
+
+ // Pick sandboxed, or unsandboxed iframe
+ var iframe = panel.get_iframe();
+ var notificationbox = iframe.parentNode;
+ var load_state;
+
+ if (selected_id == id) {
+ is_after_selected = 1
+ debug("item("+ii/2+") selected");
+ header.setAttribute('selected', 'true');
+ content.removeAttribute('hidden');
+ content.removeAttribute('collapsed');
+
+ if (sidebarObj.collapsed && panel.is_sandboxed()) {
+ if (!panel.is_persistent()) {
+ debug(" set src=about:blank");
+ iframe.setAttribute('src', 'about:blank');
+ }
+ } else {
+ var saved_src = iframe.getAttribute('content');
+ var src = iframe.getAttribute('src');
+ // either we have been requested to force_reload or the
+ // panel src has changed so we must restore the original src
+ if (force_reload || (saved_src != src)) {
+ debug(" set src="+saved_src);
+ iframe.setAttribute('src', saved_src);
+
+ if (gTimeoutID != null)
+ clearTimeout(gTimeoutID);
+
+ gCurFrame = iframe;
+ gTimeoutID = setTimeout(setBlank, 20000);
+ }
+ }
+
+ load_state = content.getAttribute('loadstate');
+ if (load_state == 'stopped') {
+ load_state = 'never loaded';
+ toggleLoadarea(content);
+ }
+ if (load_state == 'never loaded') {
+ iframe.removeAttribute('hidden');
+ iframe.setAttribute('loadstate', 'loading');
+ iframe.addEventListener('load', panel_loader, true);
+ }
+ } else {
+ debug("item("+ii/2+")");
+ header.removeAttribute('selected');
+ content.setAttribute('collapsed','true');
+
+ if (!panel.is_persistent()) {
+ iframe.setAttribute('src', 'about:blank');
+ load_state = content.getAttribute('loadstate');
+ if (load_state == 'loading') {
+ iframe.removeEventListener("load", panel_loader, true);
+ content.setAttribute('hidden','true');
+ iframe.setAttribute('loadstate', 'never loaded');
+ }
+ }
+ }
+ }
+ }
+ if (last_header) {
+ last_header.setAttribute('last-panel','true');
+ }
+
+ var no_panels_iframe = document.getElementById('sidebar-iframe-no-panels');
+ if (have_set_top) {
+ no_panels_iframe.setAttribute('hidden','true');
+ // The hide and show of 'sidebar-panels' should not be needed,
+ // but some old profiles may have this persisted as hidden (50973).
+ this.node.removeAttribute('hidden');
+ } else {
+ no_panels_iframe.removeAttribute('hidden');
+ }
+
+ this.initialized = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// sbPanel Class
+//
+// Like sbPanelList, this class is a wrapper around DOM representation
+// of individual panels in the sidebar. This UI event handlers and the
+// sidebar datasource observers call into this. This class is
+// responsible for keeping the DOM state of the sidebar up to date.
+// This class does not make any changes to the sidebar rdf datasource.
+//////////////////////////////////////////////////////////////////////
+
+function sbPanel(id, header, index)
+{
+ // This constructor should only be called by sbPanelList class.
+ // To create a panel instance, use the helper functions in sbPanelList:
+ // sb_panel.get_panel_from_id(id)
+ // sb_panel.get_panel_from_header_node(dom_node)
+ // sb_panel.get_panel_from_header_index(index)
+ this.id = id;
+ this.header = header;
+ this.index = index;
+ this.parent = sidebarObj.panels;
+}
+
+sbPanel.prototype.get_header =
+function ()
+{
+ return this.header;
+}
+
+sbPanel.prototype.get_content =
+function ()
+{
+ return this.get_header().nextSibling;
+}
+
+sbPanel.prototype.is_sandboxed =
+function ()
+{
+ if (typeof this.sandboxed == "undefined") {
+ var notificationbox = this.get_content().childNodes.item(1);
+ var unsandboxed_iframe = notificationbox.firstChild;
+ this.sandboxed = !unsandboxed_iframe.getAttribute('content').match(/^chrome:/);
+ }
+ return this.sandboxed;
+}
+
+sbPanel.prototype.get_iframe =
+function ()
+{
+ if (typeof this.iframe == "undefined") {
+ var notificationbox = this.get_content().childNodes.item(1);
+ this.iframe = this.is_sandboxed() ? notificationbox.lastChild :
+ notificationbox.firstChild;
+ }
+ return this.iframe;
+}
+
+// This exclude function is used on panels and on the panel picker menu.
+// That is why it is hanging out in the global name space instead of
+// minding its own business in the class.
+function sb_panel_is_excluded(node)
+{
+ var exclude = node.getAttribute('exclude');
+ return ( exclude && exclude != '' &&
+ exclude.includes(sidebarObj.component));
+}
+sbPanel.prototype.is_excluded =
+function ()
+{
+ return sb_panel_is_excluded(this.get_header());
+}
+
+sbPanel.prototype.is_in_view =
+function()
+{
+ return (this.header.getAttribute("in-view") == "true");
+}
+
+sbPanel.prototype.is_selected =
+function (panel_id)
+{
+ return 'true' == this.get_header().getAttribute('selected');
+}
+
+sbPanel.prototype.is_persistent =
+function ()
+{
+ var rv = false;
+ var datasource = sidebarObj.datasource;
+ var persistNode = datasource.GetTarget(RDF.GetResource(this.id),
+ RDF.GetResource(NC + "persist"),
+ true);
+ if (persistNode)
+ {
+ persistNode =
+ persistNode.QueryInterface(Ci.nsIRDFLiteral);
+ rv = persistNode.Value == 'true';
+ }
+
+ return rv;
+}
+
+sbPanel.prototype.select =
+function (force_reload)
+{
+ this.parent.select(this, force_reload);
+}
+
+sbPanel.prototype.stop_load =
+function ()
+{
+ var iframe = this.get_iframe();
+ var content = this.get_content();
+ var load_state = iframe.getAttribute('loadstate');
+ if (load_state == "loading") {
+ debug("Stop the presses");
+ iframe.removeEventListener("load", panel_loader, true);
+ content.setAttribute("loadstate", "stopped");
+ iframe.setAttribute('src', 'about:blank');
+ toggleLoadarea(content);
+ }
+}
+
+function toggleLoadarea(content)
+{
+ // toggle between "loading" and "load stopped" in the UI
+ var widgetBox = content.firstChild.firstChild;
+ var widgetBoxKids = widgetBox.childNodes;
+ var stopButton = widgetBoxKids.item(3);
+ var reloadButton = widgetBoxKids.item(4);
+ var loadingImage = widgetBox.firstChild;
+ var loadingText = loadingImage.nextSibling;
+ var loadStoppedText = loadingText.nextSibling;
+
+ // sanity check
+ if (stopButton.getAttribute("type") != "stop")
+ {
+ debug("Error: Expected button of type=\"stop\" but didn't get one!");
+ return;
+ }
+
+ if (!stopButton.hidden)
+ {
+ // change button from "stop" to "reload"
+ stopButton.hidden = "true";
+ reloadButton.removeAttribute("hidden");
+
+ // hide the loading image and set text to "load stopped"
+ loadingImage.hidden = "true";
+ loadingText.hidden = "true";
+ loadStoppedText.removeAttribute("hidden");
+ }
+ else
+ {
+ // change button from "reload" to "stop"
+ stopButton.removeAttribute("hidden");
+ reloadButton.hidden = "true";
+
+ // show the loading image and set text to "loading"
+ loadingImage.removeAttribute("hidden");
+ loadingText.removeAttribute("hidden");
+ loadStoppedText.hidden = "true";
+ }
+}
+
+sbPanel.prototype.exclude =
+function ()
+{
+ // Exclusion is handled by the datasource,
+ // but we need to make sure this panel is no longer selected.
+ this.get_header().removeAttribute('selected');
+ this.parent.exclude(this);
+}
+
+sbPanel.prototype.reload =
+function ()
+{
+ if (!this.is_excluded()) {
+ this.select(true);
+ }
+}
+
+//////////////////////////////////////////////////////////////////
+// Panels' RDF Datasource Observer
+//
+// This observer will ensure that the Sidebar UI stays current
+// when the datasource changes.
+// - When "refresh" is asserted, the sidebar refreshed.
+// Currently this happens when a panel is included/excluded or
+// added/removed (the later comes from the customize dialog).
+// - When "refresh_panel" is asserted, the targeted panel is reloaded.
+// Currently this happens when the customize panel dialog is closed.
+//////////////////////////////////////////////////////////////////
+var panel_observer = {
+ onAssert : function(ds,src,prop,target) {
+ //debug ("observer: assert");
+ // "refresh" is asserted by select menu and by customize.js.
+ if (prop == RDF.GetResource(NC + "refresh")) {
+ sidebarObj.panels.initialized = false; // reset so panels are put in view
+ sidebarObj.panels.refresh();
+ } else if (prop == RDF.GetResource(NC + "refresh_panel")) {
+ var panel_id = target.QueryInterface(Ci.nsIRDFLiteral).Value;
+ var panel = sidebarObj.panels.get_panel_from_id(panel_id);
+ panel.reload();
+ }
+ },
+ onUnassert : function(ds,src,prop,target) {
+ //debug ("observer: unassert");
+ },
+ onChange : function(ds,src,prop,old_target,new_target) {
+ //debug ("observer: change");
+ },
+ onMove : function(ds,old_src,new_src,prop,target) {
+ //debug ("observer: move");
+ },
+ onBeginUpdateBatch : function(ds) {
+ //debug ("observer: onBeginUpdateBatch");
+ },
+ onEndUpdateBatch : function(ds) {
+ //debug ("observer: onEndUpdateBatch");
+ }
+};
+
+// Use an assertion to pass a "refresh" event to all the sidebars.
+// They use observers to watch for this assertion (see above).
+function refresh_all_sidebars() {
+ sidebarObj.datasource.Assert(RDF.GetResource(sidebarObj.resource),
+ RDF.GetResource(NC + "refresh"),
+ RDF.GetLiteral("true"),
+ true);
+ sidebarObj.datasource.Unassert(RDF.GetResource(sidebarObj.resource),
+ RDF.GetResource(NC + "refresh"),
+ RDF.GetLiteral("true"));
+}
+
+//////////////////////////////////////////////////////////////
+// Sidebar Init
+//////////////////////////////////////////////////////////////
+function sidebar_overlay_init() {
+ if (sidebar_is_collapsed() && !gAboutToUncollapse)
+ return;
+ gMustInit = false;
+ sidebarObj.panels = new sbPanelList('sidebar-panels');
+ sidebarObj.datasource_uri = get_sidebar_datasource_uri();
+ sidebarObj.datasource = RDF.GetDataSourceBlocking(sidebarObj.datasource_uri);
+ sidebarObj.resource = 'urn:sidebar:current-panel-list';
+
+ sidebarObj.master_datasources = sidebarObj.datasource_uri;
+ sidebarObj.master_resource = 'urn:sidebar:master-panel-list';
+ sidebarObj.component = gPrivate ? "navigator:browser" :
+ document.documentElement.getAttribute('windowtype');
+ debug("sidebarObj.component is " + sidebarObj.component);
+
+ // Sync RDF with broadcasters.
+ SidebarBroadcastersToRDF();
+
+ // Initialize the display
+ var sidebar_element = document.getElementById('sidebar-box');
+ var sidebar_menuitem = document.getElementById('sidebar-menu');
+ if (sidebar_is_hidden()) {
+ if (sidebar_menuitem) {
+ sidebar_menuitem.setAttribute('checked', 'false');
+ }
+ } else {
+ if (sidebar_menuitem) {
+ sidebar_menuitem.setAttribute('checked', 'true');
+ }
+
+ // for old profiles that don't persist the hidden attribute when splitter is not hidden.
+ var sidebar_splitter = document.getElementById('sidebar-splitter')
+ if (sidebar_splitter)
+ sidebar_splitter.setAttribute('hidden', 'false');
+
+ if (sidebarObj.never_built) {
+ sidebarObj.never_built = false;
+
+ debug("sidebar = " + sidebarObj);
+ debug("sidebarObj.resource = " + sidebarObj.resource);
+ debug("sidebarObj.datasource_uri = " + sidebarObj.datasource_uri);
+
+ // Obtain the pref for limiting the number of tabs in view, defaults to 8.
+ gNumTabsInViewPref = Services.prefs.getIntPref("sidebar.num_tabs_in_view", 8);
+
+ // Show the header for the panels area. Use a splitter if there
+ // is stuff over the panels area.
+ var sidebar_panels_splitter = document.getElementById('sidebar-panels-splitter');
+ if (sidebar_element.firstChild != sidebar_panels_splitter) {
+ debug("Showing the panels splitter");
+ sidebar_panels_splitter.removeAttribute('hidden');
+ }
+ }
+ if (sidebar_is_collapsed()) {
+ sidebarObj.collapsed = true;
+ } else {
+ sidebarObj.collapsed = false;
+ }
+
+ sidebar_open_default_panel(100, 0);
+ }
+}
+
+function sidebar_overlay_destruct() {
+ var panels = document.getElementById('sidebar-panels');
+ debug("Removing observer from database.");
+ panels.database.RemoveObserver(panel_observer);
+}
+
+var gBusyOpeningDefault = false;
+
+function sidebar_open_default_panel(wait, tries) {
+ // check for making function reentrant
+ if (gBusyOpeningDefault)
+ return;
+ gBusyOpeningDefault = true;
+
+ var ds = sidebarObj.datasource;
+ var currentListRes = RDF.GetResource("urn:sidebar:current-panel-list");
+ var panelListRes = RDF.GetResource("http://home.netscape.com/NC-rdf#panel-list");
+ var container = ds.GetTarget(currentListRes, panelListRes, true);
+ if (container) {
+ // Add the user's current panel choices to the template builder,
+ // which will aggregate it with the other datasources that describe
+ // the individual panel's title, customize URL, and content URL.
+ var panels = document.getElementById('sidebar-panels');
+ panels.database.AddDataSource(ds);
+
+ debug("Adding observer to database.");
+ panels.database.AddObserver(panel_observer);
+
+ // XXX This is a hack to force re-display
+ panels.builder.rebuild();
+ } else {
+ if (tries < 3) {
+ // No children yet, try again later
+ setTimeout(sidebar_open_default_panel, wait, wait*2, ++tries);
+ gBusyOpeningDefault = false;
+ return;
+ } else {
+ sidebar_fixup_datasource();
+ }
+ }
+
+ sidebarObj.panels.refresh();
+ gBusyOpeningDefault = false;
+ if (gCheckMissingPanels)
+ check_for_missing_panels();
+}
+
+function SidebarRebuild() {
+ sidebarObj.panels.initialized = false; // reset so panels are brought in view
+ var panels = document.getElementById("sidebar-panels");
+ panels.builder.rebuild();
+ sidebar_open_default_panel(100, 0);
+}
+
+function check_for_missing_panels() {
+ var tabs = sidebarObj.panels.node.childNodes;
+ var currHeader;
+ var currTab;
+ for (var i = 2; i < tabs.length; i += 2) {
+ currHeader = tabs[i];
+ currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i);
+ if (!currTab.is_excluded()) {
+ if (currHeader.hasAttribute("prereq") && currHeader.getAttribute("prereq") != "") {
+ var prereq_file = currHeader.getAttribute("prereq");
+ var channel =
+ Services.io.newChannelFromURI(Services.io.newURI(prereq_file),
+ null,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null,
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ try {
+ channel.open();
+ }
+ catch (ex) {
+ if (ex.result != Cr.NS_ERROR_FILE_NOT_FOUND) {
+ throw ex;
+ }
+ sidebarObj.datasource.Assert(RDF.GetResource(currHeader.getAttribute("id")),
+ RDF.GetResource(NC + "exclude"),
+ RDF.GetLiteral(sidebarObj.component),
+ true);
+ currTab.exclude();
+ }
+ }
+ }
+ }
+ gCheckMissingPanels = false;
+}
+
+//////////////////////////////////////////////////////////////
+// Sidebar File and Datasource functions
+//////////////////////////////////////////////////////////////
+
+function sidebar_get_panels_file() {
+ try {
+ // Use the fileLocator to look in the profile directory to find
+ // 'panels.rdf', which is the database of the user's currently
+ // selected panels.
+ // If <profile>/panels.rdf doesn't exist, GetFileLocation() will copy
+ // bin/defaults/profile/panels.rdf to <profile>/panels.rdf
+ var sidebar_file = GetSpecialDirectory(PANELS_RDF_FILE);
+ if (!sidebar_file.exists()) {
+ // This should not happen, as GetFileLocation() should copy
+ // defaults/panels.rdf to the users profile directory
+ debug("Sidebar panels file does not exist");
+ throw("Panels file does not exist");
+ }
+ return sidebar_file;
+ } catch (ex) {
+ // This should not happen
+ debug("Error: Unable to grab panels file.\n");
+ throw(ex);
+ }
+ return null;
+}
+
+function sidebar_revert_to_default_panels() {
+ try {
+ var sidebar_file = sidebar_get_panels_file();
+
+ sidebar_file.remove(false);
+
+ // Since we just removed the panels file,
+ // this should copy the defaults over.
+ sidebar_file = sidebar_get_panels_file();
+
+ debug("sidebar defaults reloaded");
+ var datasource = sidebarObj.datasource;
+ datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Refresh(true);
+ } catch (ex) {
+ debug("Error: Unable to reload panel defaults file.\n");
+ }
+ return null;
+}
+
+function get_sidebar_datasource_uri() {
+ try {
+ var sidebar_file = sidebar_get_panels_file();
+
+ var fileHandler = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
+
+ return fileHandler.getURLSpecFromFile(sidebar_file);
+ } catch (ex) {
+ // This should not happen
+ debug("Error: Unable to load panels file.\n");
+ }
+ return null;
+}
+
+function sidebar_fixup_datasource() {
+ var datasource = sidebarObj.datasource;
+ var resource = RDF.GetResource(sidebarObj.resource);
+
+ var panel_list = datasource.GetTarget(resource,
+ RDF.GetResource(NC+"panel-list"),
+ true);
+ if (!panel_list) {
+ debug("Sidebar datasource is an old format or busted\n");
+ sidebar_revert_to_default_panels();
+ } else {
+ // The datasource is ok, but it just has no panels.
+ // sidebar_refresh() will display some helper content.
+ // Do nothing here.
+ }
+}
+
+//////////////////////////////////////////////////////////////
+// Sidebar Interface for XUL
+//////////////////////////////////////////////////////////////
+
+// Change the sidebar content to the selected panel.
+// Called when a panel title is clicked.
+function SidebarSelectPanel(header, should_popopen, should_unhide) {
+ debug("SidebarSelectPanel("+header+","+should_popopen+","+should_unhide+")");
+ var panel = sidebarObj.panels.get_panel_from_header_node(header);
+
+ if (!panel) {
+ return false;
+ }
+
+ var popopen = false;
+ var unhide = false;
+
+ if (panel.is_excluded()) {
+ return false;
+ }
+ if (sidebar_is_hidden()) {
+ if (should_unhide) {
+ unhide = true;
+ } else {
+ return false;
+ }
+ }
+ if (sidebar_is_collapsed()) {
+ if (should_popopen) {
+ popopen = true;
+ } else {
+ return false;
+ }
+ }
+ if (unhide) SidebarShowHide();
+ if (popopen) SidebarExpandCollapse();
+
+ try {
+ panel.get_iframe().setAttribute('focusOnLoad', true);
+ } catch (ex) {
+ // ignore exception for cases where content isn't built yet
+ // e.g., auto opening search tab: we don't want to focus search field
+ }
+ if (!panel.is_selected()) panel.select(false);
+
+ return true;
+}
+
+function SidebarGetLastSelectedPanel()
+{
+ return (sidebarObj.panels &&
+ sidebarObj.panels.node.getAttribute('last-selected-panel'));
+}
+
+function SidebarGetRelativePanel(direction)
+{
+ // direction == 1 to view next panel, -1 to view prev panel
+
+ if (sidebar_is_hidden())
+ SidebarShowHide();
+ if (sidebar_is_collapsed())
+ SidebarExpandCollapse();
+
+ var currentPanel = sidebarObj.panels.get_panel_from_id(SidebarGetLastSelectedPanel());
+ if (!currentPanel) {
+ sidebarObj.panels.select_default_panel();
+ return;
+ }
+
+ var newPanel = currentPanel;
+
+ do {
+ var newPanelIndex = newPanel.index + (direction * 2);
+ if (newPanelIndex < 2 || newPanelIndex >= sidebarObj.panels.node.childNodes.length)
+ newPanel = (direction == 1)? sidebarObj.panels.find_first(): sidebarObj.panels.find_last();
+ else
+ newPanel = sidebarObj.panels.get_panel_from_header_index(newPanelIndex);
+
+ if (!newPanel)
+ break;
+
+ if (!newPanel.is_excluded()) {
+ SidebarSelectPanel(newPanel.header, true, true); // found a panel that's not excluded to select -- do it
+ break;
+ }
+ } while (newPanel != currentPanel); // keep looking for a panel, but don't loop infinitely
+}
+
+function SidebarStopPanelLoad(header) {
+ var panel = sidebarObj.panels.get_panel_from_header_node(header);
+ panel.stop_load();
+}
+
+function SidebarReloadPanel(header) {
+ var panel = sidebarObj.panels.get_panel_from_header_node(header);
+ panel.reload();
+}
+
+// No one is calling this right now.
+function SidebarReload() {
+ sidebarObj.panels.refresh();
+}
+
+// Set up a lame hack to avoid opening two customize
+// windows on a double click.
+var gDisableCustomize = false;
+function enable_customize() {
+ gDisableCustomize = false;
+}
+
+// Bring up the Sidebar customize dialog.
+function SidebarCustomize() {
+ // Use a single sidebar customize dialog
+ var customizeWindow = Services.wm.getMostRecentWindow('sidebar:customize');
+
+ if (customizeWindow) {
+ debug("Reuse existing customize dialog");
+ customizeWindow.focus();
+ } else {
+ debug("Open a new customize dialog");
+
+ if (false == gDisableCustomize) {
+ debug("First time creating customize dialog");
+ gDisableCustomize = true;
+
+ var panels = document.getElementById('sidebar-panels');
+
+ customizeWindow = window.openDialog(
+ 'chrome://communicator/content/sidebar/customize.xul',
+ '_blank','centerscreen,chrome,resizable,dialog=no,dependent',
+ sidebarObj.master_datasources,
+ sidebarObj.master_resource,
+ sidebarObj.datasource_uri,
+ sidebarObj.resource);
+ setTimeout(enable_customize, 2000);
+ }
+ }
+}
+
+function BrowseMorePanels()
+{
+ var url = '';
+ var browser_url = "chrome://navigator/content/navigator.xul";
+ var locale;
+ try {
+ url = Services.prefs.getCharPref("sidebar.customize.directory.url");
+ var temp = Services.prefs.getCharPref("browser.chromeURL");
+ if (temp)
+ browser_url = temp;
+ } catch(ex) {
+ debug("Unable to get prefs: "+ex);
+ }
+ window.openDialog(browser_url, "_blank", "chrome,all,dialog=no", url);
+}
+
+
+
+function sidebar_is_collapsed() {
+ var sidebar_splitter = document.getElementById('sidebar-splitter');
+ return (sidebar_splitter &&
+ sidebar_splitter.getAttribute('state') == 'collapsed');
+}
+
+function SidebarExpandCollapse() {
+ var sidebar_splitter = document.getElementById('sidebar-splitter');
+ var sidebar_box = document.getElementById('sidebar-box');
+ if (sidebar_splitter.getAttribute('state') == 'collapsed') {
+ if (gMustInit)
+ sidebar_overlay_init();
+ debug("Expanding the sidebar");
+ sidebar_splitter.removeAttribute('state');
+ sidebar_box.removeAttribute('collapsed');
+ SidebarSetButtonOpen(true);
+ } else {
+ debug("Collapsing the sidebar");
+ sidebar_splitter.setAttribute('state', 'collapsed');
+ sidebar_box.setAttribute('collapsed', 'true');
+ SidebarSetButtonOpen(false);
+ }
+}
+
+// sidebar_is_hidden() - Helper function for SidebarShowHide().
+function sidebar_is_hidden() {
+ var sidebar_title = document.getElementById('sidebar-title-box');
+ var sidebar_box = document.getElementById('sidebar-box');
+ return sidebar_box.getAttribute('hidden') == 'true'
+ || sidebar_title.getAttribute('hidden') == 'true';
+}
+
+// Show/Hide the entire sidebar.
+// Invoked by the "View / Sidebar" menu option.
+function SidebarShowHide() {
+ var sidebar_box = document.getElementById('sidebar-box');
+ var title_box = document.getElementById('sidebar-title-box');
+ var sidebar_panels_splitter = document.getElementById('sidebar-panels-splitter');
+ var sidebar_panels_splitter_box = document.getElementById('sidebar-panels-splitter-box');
+ var sidebar_splitter = document.getElementById('sidebar-splitter');
+ var sidebar_menu_item = document.getElementById('sidebar-menu');
+ var tabs_menu = document.getElementById('sidebar-panel-picker');
+
+ if (sidebar_is_hidden()) {
+ debug("Showing the sidebar");
+
+ // for older profiles:
+ sidebar_box.setAttribute('hidden', 'false');
+ sidebar_panels_splitter_box.setAttribute('hidden', 'false');
+
+ sidebar_box.removeAttribute('collapsed');
+ if (sidebar_splitter.getAttribute('state') == 'collapsed')
+ sidebar_splitter.removeAttribute('state');
+ title_box.removeAttribute('hidden');
+ sidebar_panels_splitter_box.removeAttribute('collapsed');
+ sidebar_splitter.setAttribute('hidden', 'false');
+ if (sidebar_box.firstChild != sidebar_panels_splitter) {
+ debug("Showing the panels splitter");
+ sidebar_panels_splitter.removeAttribute('hidden');
+ if (sidebar_panels_splitter.getAttribute('state') == 'collapsed')
+ sidebar_panels_splitter.removeAttribute('state');
+ }
+ sidebar_overlay_init();
+ sidebar_menu_item.setAttribute('checked', 'true');
+ tabs_menu.removeAttribute('hidden');
+ SidebarSetButtonOpen(true);
+ } else {
+ debug("Hiding the sidebar");
+ var hide_everything = sidebar_panels_splitter.getAttribute('hidden') == 'true';
+ if (hide_everything) {
+ debug("Hide everything");
+ sidebar_box.setAttribute('collapsed', 'true');
+ sidebar_splitter.setAttribute('hidden', 'true');
+ } else {
+ sidebar_panels_splitter.setAttribute('hidden', 'true');
+ }
+ title_box.setAttribute('hidden', 'true');
+ sidebar_panels_splitter_box.setAttribute('collapsed', 'true');
+ sidebar_menu_item.setAttribute('checked', 'false');
+ tabs_menu.setAttribute('hidden', 'true');
+ SidebarSetButtonOpen(false);
+ }
+ // Immediately save persistent values
+ document.persist('sidebar-title-box', 'hidden');
+ PersistWidth();
+ window.content.focus();
+}
+
+function SidebarGetState() {
+ if (sidebar_is_hidden())
+ return "hidden";
+ if (sidebar_is_collapsed())
+ return "collapsed";
+ return "visible";
+}
+
+function SidebarSetState(aState) {
+ document.getElementById("sidebar-box").hidden = aState != "visible";
+ document.getElementById("sidebar-splitter").hidden = aState == "hidden";
+}
+
+function SidebarBuildPickerPopup() {
+ var menu = document.getElementById('sidebar-panel-picker-popup');
+ menu.database.AddDataSource(sidebarObj.datasource);
+ menu.builder.rebuild();
+
+ for (var ii=3; ii < menu.childNodes.length; ii++) {
+ var panel_menuitem = menu.childNodes.item(ii);
+ if (sb_panel_is_excluded(panel_menuitem)) {
+ debug(ii+": "+panel_menuitem.getAttribute('label')+ ": excluded; uncheck.");
+ panel_menuitem.removeAttribute('checked');
+ } else {
+ debug(ii+": "+panel_menuitem.getAttribute('label')+ ": included; check.");
+ panel_menuitem.setAttribute('checked', 'true');
+ }
+ }
+}
+
+function SidebarTogglePanel(panel_menuitem) {
+ if (!panel_menuitem.classList.contains("menuitem-sidebar") &&
+ !panel_menuitem.classList.contains("texttab-sidebar"))
+ return;
+
+ // Create a "container" wrapper around the current panels to
+ // manipulate the RDF:Seq more easily.
+
+ var did_exclude = false;
+ var panel_id = panel_menuitem.getAttribute('id');
+ var panel = sidebarObj.panels.get_panel_from_id(panel_id);
+ var panel_exclude = panel_menuitem.getAttribute('exclude')
+ if (panel_exclude == '') {
+ // Nothing excluded for this panel yet, so add this component to the list.
+ debug("Excluding " + panel_id + " from " + sidebarObj.component);
+ sidebarObj.datasource.Assert(RDF.GetResource(panel_id),
+ RDF.GetResource(NC + "exclude"),
+ RDF.GetLiteral(sidebarObj.component),
+ true);
+ panel.exclude();
+ did_exclude = true;
+ } else {
+ // Panel has an exclude string, but it may or may not have the
+ // current component listed in the string.
+ debug("Current exclude string: " + panel_exclude);
+ var new_exclude = panel_exclude;
+ if (sb_panel_is_excluded(panel_menuitem)) {
+ debug("Plucking this component out of the exclude list");
+ var replace_pat = new RegExp(sidebarObj.component + "\s*");
+ new_exclude = new_exclude.replace(replace_pat, "").trimLeft();
+ // did_exclude remains false
+ } else {
+ debug("Adding this component to the exclude list");
+ new_exclude = new_exclude + " " + sidebarObj.component;
+ panel.exclude();
+ did_exclude = true;
+ }
+ if (new_exclude == '') {
+ debug("Removing exclude list");
+ sidebarObj.datasource.Unassert(RDF.GetResource(panel_id),
+ RDF.GetResource(NC + "exclude"),
+ RDF.GetLiteral(sidebarObj.component));
+ } else {
+ debug("New exclude string: " + new_exclude);
+ exclude_target =
+ sidebarObj.datasource.GetTarget(RDF.GetResource(panel_id),
+ RDF.GetResource(NC + "exclude"),
+ true);
+ sidebarObj.datasource.Change(RDF.GetResource(panel_id),
+ RDF.GetResource(NC + "exclude"),
+ exclude_target,
+ RDF.GetLiteral(new_exclude));
+ }
+ }
+
+ var tabs = sidebarObj.panels.node.childNodes;
+
+ if (did_exclude)
+ {
+ // if we excluded a tab in view then add another one
+ if (panel.is_in_view())
+ {
+ // we excluded one so let's try to bring a non-excluded one into view
+ var newFirst = null;
+ var added = false;
+ for (var i = 2; i < tabs.length ; i += 2)
+ {
+ var currTab = sidebarObj.panels.get_panel_from_header_index(i);
+ var hasPotential = !currTab.is_excluded() && !currTab.is_in_view();
+
+ // set potential new first tab in case we can't find one after the
+ // tab that was just excluded
+ if (!newFirst && hasPotential)
+ newFirst = currTab;
+
+ if (i > panel.index && hasPotential)
+ {
+ currTab.header.setAttribute("in-view", true);
+ added = true;
+ break;
+ }
+ }
+ if (!added && newFirst)
+ newFirst.header.setAttribute("in-view", true);
+
+ // lose it from current view
+ panel.header.setAttribute("in-view", false);
+ }
+ }
+ else
+ {
+ panel.header.setAttribute("in-view", true);
+
+ // if we have one too many tabs we better get rid of an old one
+ if (sidebarObj.panels.num_panels_in_view() > gNumTabsInViewPref)
+ {
+ // we included a new tab so let's take the last one out of view
+ for (i = 2; i < tabs.length; i += 2)
+ {
+ var currHeader = tabs[i];
+ if (currHeader.hasAttribute("last-panel"))
+ currHeader.setAttribute("in-view", false);
+ }
+ }
+
+ panel.select(false);
+ }
+
+ if (did_exclude && !sidebarObj.panels.visible_panels_exist())
+ // surrender focus to main content area
+ window.content.focus();
+ else
+ // force all the sidebars to update
+ refresh_all_sidebars();
+
+ // Write the modified panels out.
+ sidebarObj.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+}
+
+function SidebarNavigate(aDirection)
+{
+ debug("SidebarNavigate " + aDirection);
+
+ var tabs = sidebarObj.panels.node.childNodes;
+ var i;
+ var currHeader;
+ var currTab;
+ // move forward a tab (down in the template)
+ if (aDirection > 0)
+ {
+ // ensure we have a tab below the last one
+ var foundLast = false;
+ var oldFirst = null;
+ for (i = 2; i < tabs.length; i += 2)
+ {
+ currHeader = tabs[i];
+ currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i);
+
+ if (!currTab.is_excluded())
+ {
+ if (foundLast)
+ {
+ debug("toggling old first and new last");
+ debug("new last: " + currHeader.getAttribute("id"));
+ debug("old first: " + oldFirst.getAttribute("id"));
+ currHeader.setAttribute("in-view", true);
+ oldFirst.setAttribute("in-view", false);
+
+ // if old first was selected select new first instead
+ if (oldFirst.getAttribute("id") ==
+ sidebarObj.panels.node.getAttribute("last-selected-panel"))
+ {
+ sidebarObj.panels.node.setAttribute('last-selected-panel',
+ currTab.id);
+ }
+
+ break;
+ }
+
+ if (!foundLast && currHeader.hasAttribute("last-panel"))
+ {
+ debug("found last");
+ foundLast = true;
+ }
+
+ // set the old first in case we find a new last below
+ // the old last and need to toggle the new first's ``in-view''
+ if (!oldFirst && currTab.is_in_view())
+ oldFirst = currHeader;
+ }
+ }
+ }
+
+ // move back a tab (up in the template)
+ else if (aDirection < 0)
+ {
+ var newFirst = null, newLast = null;
+ var foundFirst = false;
+ for (i = 2; i < tabs.length; i += 2)
+ {
+ currHeader = tabs[i];
+ currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i);
+
+ if (!currTab.is_excluded())
+ {
+ if (!foundFirst && currHeader.hasAttribute("top-panel"))
+ {
+ debug("found first");
+ foundFirst = true;
+ }
+ if (!foundFirst)
+ {
+ debug("setting newFirst");
+ newFirst = currHeader;
+ }
+
+ if (currHeader.hasAttribute("last-panel"))
+ {
+ debug("found last");
+
+ // ensure we have a tab above the first one
+ if (newFirst)
+ {
+ debug("toggling new first and old last");
+ debug("new first: " + newFirst.getAttribute("id"));
+ debug("old last: " + currHeader.getAttribute("id"));
+
+ newFirst.setAttribute("in-view", true);
+ currHeader.setAttribute("in-view", false); // hide old last
+
+ // if old last was selected, now select one above it
+ if (sidebarObj.panels.node.getAttribute("last-selected-panel") ==
+ currTab.id)
+ {
+ sidebarObj.panels.node.setAttribute("last-selected-panel",
+ newLast.getAttribute("id"));
+ }
+
+ break;
+ }
+ }
+ if (currTab.is_in_view())
+ newLast = currHeader;
+ }
+ }
+ }
+
+ if (aDirection)
+ sidebarObj.panels.update(false);
+}
+
+//////////////////////////////////////////////////////////////
+// Sidebar Hacks and Work-arounds
+//////////////////////////////////////////////////////////////
+
+// SidebarCleanUpExpandCollapse() - Respond to grippy click.
+function SidebarCleanUpExpandCollapse() {
+ // XXX Mini hack. Persist isn't working too well. Force the persist,
+ // but wait until the change has commited.
+ if (gMustInit) {
+ gAboutToUncollapse = true;
+ sidebar_overlay_init();
+ }
+
+ setTimeout(Persist, 100, "sidebar-box", "collapsed");
+ setTimeout(() => sidebarObj.panels.refresh(), 100);
+}
+
+function PersistHeight() {
+ // XXX Mini hack. Persist isn't working too well. Force the persist,
+ // but wait until the last drag has been committed.
+ // May want to do something smarter here like only force it if the
+ // height has really changed.
+ setTimeout(Persist, 100, "sidebar-panels-splitter-box", "height");
+}
+
+function PersistWidth() {
+ // XXX Mini hack. Persist isn't working too well. Force the persist,
+ // but wait until the width change has commited. Also see bug 16516.
+ setTimeout(Persist, 100, "sidebar-box", "width");
+
+ var is_collapsed = document.getElementById("sidebar-box")
+ .getAttribute("collapsed") == "true";
+ SidebarSetButtonOpen(!is_collapsed);
+}
+
+function Persist(aAttribute, aValue) {
+ document.persist(aAttribute, aValue);
+}
+
+function SidebarFinishClick() {
+ PersistWidth();
+
+ var is_collapsed = document.getElementById('sidebar-box').getAttribute('collapsed') == 'true';
+ debug("collapsed: " + is_collapsed);
+ if (is_collapsed != sidebarObj.collapsed) {
+ if (gMustInit)
+ sidebar_overlay_init();
+ }
+}
+
+function SidebarSetButtonOpen(aSidebarNowOpen)
+{
+ // change state so toolbar icon can be updated
+ var pt = document.getElementById("PersonalToolbar");
+ if (pt) {
+ pt.setAttribute("prefixopen", aSidebarNowOpen);
+
+ // set tooltip for toolbar icon
+ var header = document.getElementById("sidebar-title-box");
+ var tooltip = header.getAttribute(aSidebarNowOpen ?
+ "tooltipclose" : "tooltipopen");
+ pt.setAttribute("prefixtooltip", tooltip);
+ }
+}
+
+function SidebarInitContextMenu(aMenu, aPopupNode)
+{
+ var panel = sidebarObj.panels.get_panel_from_header_node(aPopupNode);
+ var switchItem = document.getElementById("switch-ctx-item");
+ var reloadItem = document.getElementById("reload-ctx-item");
+ var stopItem = document.getElementById("stop-ctx-item");
+
+ // the current panel can be reloaded, but other panels are not showing
+ // any content, so we only allow you to switch to other panels
+ if (panel.is_selected())
+ {
+ switchItem.setAttribute("collapsed", "true");
+ reloadItem.removeAttribute("disabled");
+ }
+ else
+ {
+ switchItem.removeAttribute("collapsed");
+ reloadItem.setAttribute("disabled", "true");
+ }
+
+ // only if a panel is currently loading enable the ``Stop'' item
+ if (panel.get_iframe().getAttribute("loadstate") == "loading")
+ stopItem.removeAttribute("disabled");
+ else
+ stopItem.setAttribute("disabled", "true");
+}
+
+///////////////////////////////////////////////////////////////
+// Handy Debug Tools
+//////////////////////////////////////////////////////////////
+var debug = null;
+var dump_attributes = null;
+var dump_tree = null;
+if (!SB_DEBUG) {
+ debug = function (s) {};
+ dump_attributes = function (node, depth) {};
+ dump_tree = function (node) {};
+ var _dump_tree_recur = function (node, depth, index) {};
+} else {
+ debug = function (s) { dump("-*- sbOverlay: " + s + "\n"); };
+
+ dump_attributes = function (node, depth) {
+ var attributes = node.attributes;
+ var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | . ";
+
+ if (!attributes || attributes.length == 0) {
+ debug(indent.substr(indent.length - depth*2) + "no attributes");
+ }
+ for (var ii=0; ii < attributes.length; ii++) {
+ var attr = attributes.item(ii);
+ debug(indent.substr(indent.length - depth*2) + attr.name +
+ "=" + attr.value);
+ }
+ }
+ dump_tree = function (node) {
+ _dump_tree_recur(node, 0, 0);
+ }
+ _dump_tree_recur = function (node, depth, index) {
+ if (!node) {
+ debug("dump_tree: node is null");
+ }
+ var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | + ";
+ debug(indent.substr(indent.length - depth*2) + index +
+ " " + node.nodeName);
+ if (node.nodeType != Node.TEXT_NODE) {
+ dump_attributes(node, depth);
+ }
+ var kids = node.childNodes;
+ for (var ii=0; ii < kids.length; ii++) {
+ _dump_tree_recur(kids[ii], depth + 1, ii);
+ }
+ }
+}
+
+function SidebarBroadcastersToRDF()
+{
+ // Only the broadcasters in browser are synced to panels.rdf
+ if (sidebarObj.component != "navigator:browser")
+ return;
+
+ // Translation rules to translate between new broadcaster id and old RDF id.
+ const TRANSLATE = {viewBookmarksSidebar: "bookmarks",
+ viewHistorySidebar: "history",
+ viewSearchSidebar: "search",
+ viewAddressbookSidebar: "addressbook"};
+ const URN_PREFIX = "urn:sidebar:panel:";
+
+ const RDFCU = Cc['@mozilla.org/rdf/container-utils;1']
+ .getService(Ci.nsIRDFContainerUtils);
+
+ /*
+ * Initialize RDF stuff.
+ */
+ let ds = sidebarObj.datasource;
+ let panelListRes = RDF.GetResource(NC + "panel-list");
+
+ let currentListRes = RDF.GetResource(sidebarObj.resource);
+ let masterListRes = RDF.GetResource(sidebarObj.master_resource);
+ let currentTarget = ds.GetTarget(currentListRes, panelListRes, true);
+ let masterTarget = ds.GetTarget(masterListRes, panelListRes, true);
+ if (!masterTarget) {
+ // No "master-panel-list" found, so create it.
+ masterTarget = RDF.GetAnonymousResource();
+ ds.Assert(masterListRes, panelListRes, masterTarget, true);
+ }
+ let currentSeq = RDFCU.MakeSeq(ds, currentTarget);
+ let masterSeq = RDFCU.MakeSeq(ds, masterTarget);
+
+ /*
+ * Run over broadcasters in browser window and add/update RDF entries
+ * based on them.
+ */
+ let titleRes = RDF.GetResource(NC + "title");
+ let urlRes = RDF.GetResource(NC + "content");
+
+ let bset = document.getElementById("mainBroadcasterSet");
+ let broadcasters = bset.getElementsByTagName("broadcaster");
+ let bclist = {};
+ for (let bId = 0; bId < broadcasters.length; bId++) {
+ let curBC = broadcasters[bId];
+ let title = curBC.getAttribute("sidebartitle") || curBC.getAttribute("label");
+ let url = curBC.getAttribute("sidebarurl");
+ let bcid = (curBC.id in TRANSLATE) ? TRANSLATE[curBC.id] : curBC.id;
+
+ if (!url || !title || !bcid)
+ continue;
+
+ // This one is needed later to check for obsolete sidebars.
+ bclist[bcid] = 1;
+
+ let panelRes = RDF.GetResource(URN_PREFIX + bcid);
+
+ // Literals of values that should be in RDF.
+ let titleLit = RDF.GetLiteral(title);
+ let urlLit = RDF.GetLiteral(url);
+ // Literals of values that are in RDF.
+ let curtitleLit = ds.GetTarget(panelRes, titleRes, true);
+ let cururlLit = ds.GetTarget(panelRes, urlRes, true);
+
+ // If the item doesn't already exist, create it.
+ if (!curtitleLit && !cururlLit) {
+ ds.Assert(panelRes, titleRes, titleLit, true);
+ ds.Assert(panelRes, urlRes, urlLit, true);
+ masterSeq.AppendElement(panelRes);
+ if (currentSeq.IndexOf(panelRes) == -1)
+ currentSeq.AppendElement(panelRes);
+ }
+ // Item already exists, but perhaps we need to update...
+ else {
+ let curtitle = curtitleLit.QueryInterface(Ci.nsIRDFLiteral).Value;
+ let cururl = cururlLit.QueryInterface(Ci.nsIRDFLiteral).Value;
+
+ if (curtitle != title)
+ ds.Change(panelRes, titleRes, curtitleLit, titleLit);
+
+ if (cururl != url)
+ ds.Change(panelRes, urlRes, cururlLit, urlLit);
+ }
+ }
+
+ /*
+ * Do the same the other way around to delete obsolete sidebars.
+ */
+
+ let masterElements = masterSeq.GetElements();
+ while (masterElements.hasMoreElements()) {
+ let curElementRes = masterElements.getNext();
+ let curId = curElementRes.QueryInterface(Ci.nsIRDFResource).Value;
+
+ if (curId.substr(0, URN_PREFIX.length) != URN_PREFIX)
+ continue;
+
+ curId = curId.substr(URN_PREFIX.length);
+ if (!(curId in bclist)) {
+ let properties = ds.ArcLabelsOut(curElementRes);
+ while(properties.hasMoreElements()) {
+ let propertyRes = properties.getNext();
+ let valueLit = ds.GetTarget(curElementRes, propertyRes, true);
+ ds.Unassert(curElementRes, propertyRes, valueLit);
+ }
+ masterSeq.RemoveElement(curElementRes, true);
+ if (currentSeq.IndexOf(curElementRes) != -1)
+ currentSeq.RemoveElement(curElementRes, true);
+ }
+ }
+
+ // Write modified data.
+ sidebarObj.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+}
+
+
+//////////////////////////////////////////////////////////////
+// Install the load/unload handlers
+//////////////////////////////////////////////////////////////
+addEventListener("load", sidebar_overlay_init, false);
+addEventListener("unload", sidebar_overlay_destruct, false);
diff --git a/comm/suite/components/sidebar/content/sidebarOverlay.xul b/comm/suite/components/sidebar/content/sidebarOverlay.xul
new file mode 100644
index 0000000000..0c1aa08566
--- /dev/null
+++ b/comm/suite/components/sidebar/content/sidebarOverlay.xul
@@ -0,0 +1,247 @@
+<?xml version="1.0"?> <!-- -*- Mode: HTML; indent-tabs-mode: nil -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- This overlay requires that the files it overlays has the menupopup
+ contentAreaContextMenu defined for context menus to work correctly in
+ certain custom tabs -->
+
+<?xml-stylesheet href="chrome://communicator/content/sidebar/sidebarOverlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sidebar/sidebar.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % sidebarOverlayDTD SYSTEM "chrome://communicator/locale/sidebar/sidebarOverlay.dtd" >
+%sidebarOverlayDTD;
+]>
+
+<overlay id="sidebarOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <!-- Overlay of broadcasterset to get our panels in -->
+ <broadcasterset id="mainBroadcasterSet">
+ <broadcaster id="viewBookmarksSidebar"
+ autoCheck="false"
+ type="checkbox"
+ group="sidebar"
+ sidebartitle="&sidebar.client-bookmarks.label;"
+ sidebarurl="chrome://communicator/content/bookmarks/bookmarksPanel.xul"
+ oncommand="toggleSidebar('viewBookmarksSidebar');"/>
+ <broadcaster id="viewHistorySidebar"
+ autoCheck="false"
+ type="checkbox"
+ group="sidebar"
+ sidebartitle="&sidebar.client-history.label;"
+ sidebarurl="chrome://communicator/content/history/history-panel.xul"
+ oncommand="toggleSidebar('viewHistorySidebar');"/>
+ <broadcaster id="viewSearchSidebar"
+ autoCheck="false"
+ type="checkbox"
+ group="sidebar"
+ sidebartitle="&sidebar.search.label;"
+ sidebarurl="chrome://communicator/content/search/search-panel.xul"
+ oncommand="toggleSidebar('viewSearchSidebar');"/>
+ <broadcaster id="viewAddressbookSidebar"
+ autoCheck="false"
+ type="checkbox"
+ group="sidebar"
+ sidebartitle="&sidebar.client-addressbook.label;"
+ sidebarurl="chrome://messenger/content/addressbook/addressbook-panel.xul"
+ oncommand="toggleSidebar('viewAddressbookSidebar');"/>
+ </broadcasterset>
+
+ <command id="toggleSidebar" oncommand="SidebarShowHide();"/>
+#ifndef XP_MACOSX
+ <key id="showHideSidebar"
+ keycode="VK_F9"
+ command="toggleSidebar"/>
+#else
+ <key id="showHideSidebar"
+ key="&showHideSidebarCmd.key;"
+ modifiers="accel,alt"
+ command="toggleSidebar"/>
+#endif
+ <menupopup id="sidebarPopup"
+ onpopupshowing="SidebarInitContextMenu(this, document.popupNode);">
+ <menuitem id="switch-ctx-item" label="&sidebar.switch.label;"
+ accesskey="&sidebar.switch.accesskey;" default="true"
+ oncommand="SidebarSelectPanel(document.popupNode,false,false);"/>
+ <menuitem id="reload-ctx-item" label="&sidebar.reload.label;"
+ accesskey="&sidebar.reload.accesskey;" disabled="true"
+ oncommand="SidebarReloadPanel(document.popupNode);"/>
+ <menuitem id="stop-ctx-item" label="&sidebar.loading.stop.label;"
+ accesskey="&sidebar.loading.stop.accesskey;" disabled="true"
+ oncommand="SidebarStopPanelLoad(document.popupNode);"/>
+ <menuseparator/>
+ <menuitem id="hide-ctx-item" label="&sidebar.hide.label;"
+ accesskey="&sidebar.hide.accesskey;"
+ oncommand="SidebarTogglePanel(document.popupNode);"/>
+ <menuseparator/>
+ <menuitem id="customize-ctx-item" label="&sidebar.customize.label;"
+ accesskey="&sidebar.customize.accesskey;"
+ oncommand="SidebarCustomize();"/>
+ </menupopup>
+
+ <!-- Overlay the sidebar panels -->
+ <vbox id="sidebar-box" hidden="true" persist="hidden width collapsed">
+ <splitter id="sidebar-panels-splitter" collapse="after" persist="state"
+ onmouseup="PersistHeight();" hidden="true">
+ <grippy/>
+ </splitter>
+ <vbox id="sidebar-panels-splitter-box" flex="1"
+ persist="collapsed">
+ <sidebarheader id="sidebar-title-box" class="sidebarheader-main"
+ label="&sidebar.panels.label;" persist="hidden" type="box"
+ collapse="after" onmouseup="PersistHeight();"
+ tooltipopen="&sidebar.open.tooltip;"
+ tooltipclose="&sidebar.close.tooltip;">
+ <toolbarbutton type="menu" id="sidebar-panel-picker" class="tabbable"
+ onpopupshowing="SidebarBuildPickerPopup();"
+ label="&sidebar.picker.label;" >
+ <menupopup id="sidebar-panel-picker-popup"
+ datasources="rdf:null"
+ ref="urn:sidebar:current-panel-list"
+ oncommand="SidebarTogglePanel(event.target);" >
+ <template>
+ <rule>
+ <conditions>
+ <content uri="?uri"/>
+ <triple subject="?uri"
+ predicate="http://home.netscape.com/NC-rdf#panel-list"
+ object="?panel-list"/>
+ <member container="?panel-list" child="?panel"/>
+ <triple subject="?panel"
+ predicate="http://home.netscape.com/NC-rdf#title"
+ object="?title" />
+ </conditions>
+ <bindings>
+ <binding subject="?panel"
+ predicate="http://home.netscape.com/NC-rdf#exclude"
+ object="?exclude"/>
+ <binding subject="?panel"
+ predicate="http://home.netscape.com/NC-rdf#prereq"
+ object="?prereq"/>
+ </bindings>
+ <action>
+ <menuitem uri="?panel" type="checkbox" class="menuitem-sidebar"
+ label="?title" exclude="?exclude" prereq="?prereq"/>
+ </action>
+ </rule>
+ </template>
+ <menuitem label="&sidebar.customize.label;" accesskey="&sidebar.customize.accesskey;"
+ oncommand="SidebarCustomize();" />
+ <menuitem label="&sidebar.sbDirectory.label;"
+ oncommand="BrowseMorePanels();" />
+ <menuseparator />
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="sidebar-close-button" oncommand="SidebarShowHide();"
+ tooltiptext="&sidebar.close.tooltip;"/>
+ </sidebarheader>
+
+ <vbox id="sidebar-panels"
+ datasources="rdf:null"
+ ref="urn:sidebar:current-panel-list"
+ last-selected-panel="urn:sidebar:panel:bookmarks"
+ persist="last-selected-panel height collapsed" flex="1"
+ onclick="return contentAreaClick(event);">
+ <template id="sidebar-template">
+ <rule>
+ <conditions>
+ <content uri="?uri"/>
+ <triple subject="?uri" object="?panel-list"
+ predicate="http://home.netscape.com/NC-rdf#panel-list" />
+ <member container="?panel-list" child="?panel"/>
+ <triple subject="?panel" object="?title"
+ predicate="http://home.netscape.com/NC-rdf#title" />
+ <triple subject="?panel" object="?content"
+ predicate="http://home.netscape.com/NC-rdf#content" />
+ </conditions>
+ <bindings>
+ <binding subject="?panel" object="?exclude"
+ predicate="http://home.netscape.com/NC-rdf#exclude" />
+ <binding subject="?panel" object="?prereq"
+ predicate="http://home.netscape.com/NC-rdf#prereq" />
+ </bindings>
+ <action>
+ <hbox uri="?panel" class="box-texttab texttab-sidebar"
+ oncommand="SidebarSelectPanel(this,false,false)"
+ hidden="true" label="?title" exclude="?exclude"
+ prereq="?prereq" context="sidebarPopup"/>
+ <vbox uri="?panel" flex="1" hidden="true"
+ loadstate="never loaded">
+ <vbox flex="1" class="iframe-panel loadarea">
+ <hbox flex="1" align="center">
+ <image class="image-panel-loading"/>
+ <label class="text-panel-loading"
+ value="&sidebar.loading.label;"/>
+ <label class="text-panel-loading" hidden="true"
+ loading="false"
+ value="&sidebar.loadstopped.label;"/>
+ <button type="stop" label="&sidebar.loading.stop.label;"
+ oncommand="SidebarStopPanelLoad(this.parentNode.parentNode.parentNode.previousSibling);"/>
+ <button label="&sidebar.reload.label;" hidden="true"
+ oncommand="SidebarReload();"/>
+ </hbox>
+ <spacer flex="100%"/>
+ </vbox>
+ <notificationbox flex="1" collapsed="true" class="sidebar-notificationbox browser-notificationbox">
+ <browser flex="1" class="browser-sidebar" src="about:blank"
+ hidden="true" collapsed="true" content="?content"
+ disablehistory="true"/>
+ <browser flex="1" class="browser-sidebar" src="about:blank"
+ hidden="true" collapsed="true" content="?content"
+ type="content" context="contentAreaContextMenu"
+ disablehistory="true" tooltip="aHTMLTooltip"/>
+ </notificationbox>
+ </vbox>
+ </action>
+ </rule>
+ </template>
+ <vbox id="sidebar-iframe-no-panels" class="iframe-panel" flex="1"
+ hidden="true">
+ <description>&sidebar.no-panels.state;</description>
+ <description>&sidebar.no-panels.add;</description>
+ <description>&sidebar.no-panels.hide;</description>
+ </vbox>
+ </vbox>
+ <vbox flex="0">
+ <hbox id="nav-buttons-box" hidden="true">
+ <toolbarbutton flex="1" pack="center"
+ class="sidebar-nav-button tab-fwd" onclick="SidebarNavigate(-1);"/>
+ <toolbarbutton flex="1" pack="center"
+ class="sidebar-nav-button tab-back" onclick="SidebarNavigate(1);"/>
+ </hbox>
+ </vbox>
+ </vbox>
+ </vbox>
+
+ <!-- Splitter on the right of sidebar -->
+ <splitter id="sidebar-splitter" collapse="before" persist="state hidden"
+ class="chromeclass-extrachrome sidebar-splitter" align="center"
+ hidden="true" onmouseup="SidebarFinishClick();">
+ <grippy class="sidebar-splitter-grippy"
+ onclick="SidebarCleanUpExpandCollapse();"/>
+ </splitter>
+
+ <!-- View->Sidebar toggle -->
+ <menupopup id="menu_View_Popup">
+ <menu id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup">
+ <menuseparator/>
+ <menuitem id="sidebar-menu" type="checkbox"
+ label="&sidebarCmd.label;"
+ accesskey="&sidebarCmd.accesskey;"
+ command="toggleSidebar"
+ key="showHideSidebar"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+
+ <!-- Scripts go last, because they peek at state to tweak menus -->
+ <script src="chrome://communicator/content/sidebar/sidebarOverlay.js"/>
+
+</overlay>
+
diff --git a/comm/suite/components/sidebar/jar.mn b/comm/suite/components/sidebar/jar.mn
new file mode 100644
index 0000000000..d6ab848a98
--- /dev/null
+++ b/comm/suite/components/sidebar/jar.mn
@@ -0,0 +1,16 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/sidebar/customize-panel.js (content/customize-panel.js)
+ content/communicator/sidebar/customize-panel.xul (content/customize-panel.xul)
+ content/communicator/sidebar/customize.js (content/customize.js)
+ content/communicator/sidebar/customize.xul (content/customize.xul)
+ content/communicator/sidebar/PageNotFound.xul (content/PageNotFound.xul)
+ content/communicator/sidebar/preview.js (content/preview.js)
+ content/communicator/sidebar/preview.xul (content/preview.xul)
+ content/communicator/sidebar/sidebarBindings.xml (content/sidebarBindings.xml)
+ content/communicator/sidebar/sidebarOverlay.css (content/sidebarOverlay.css)
+ content/communicator/sidebar/sidebarOverlay.js (content/sidebarOverlay.js)
+* content/communicator/sidebar/sidebarOverlay.xul (content/sidebarOverlay.xul)
diff --git a/comm/suite/components/sidebar/moz.build b/comm/suite/components/sidebar/moz.build
new file mode 100644
index 0000000000..7a2e523961
--- /dev/null
+++ b/comm/suite/components/sidebar/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "nsISidebar.idl",
+]
+
+XPIDL_MODULE = "suite-sidebar"
+
+EXTRA_COMPONENTS += [
+ "nsSidebar.js",
+ "SuiteSidebar.manifest",
+]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/sidebar/nsISidebar.idl b/comm/suite/components/sidebar/nsISidebar.idl
new file mode 100644
index 0000000000..515b939872
--- /dev/null
+++ b/comm/suite/components/sidebar/nsISidebar.idl
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; 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/. */
+
+/*
+
+ The Sidebar API for 3rd parties
+
+*/
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(97bfa970-8222-4c3f-bbe8-42141e4c7982)]
+interface nsISidebar : nsISupports
+{
+ void addPanel(in AString aTitle, in AString aContentURL,
+ in AString aCustomizeURL);
+ void addPersistentPanel(in AString aTitle, in AString aContentURL,
+ in AString aCustomizeURL);
+ void addSearchEngine(in AString engineURL, in AString iconURL,
+ in AString suggestedTitle, in AString suggestedCategory);
+ void AddSearchProvider(in AString aDescriptionURL);
+ unsigned long IsSearchProviderInstalled(in AString aSearchURL);
+};
diff --git a/comm/suite/components/sidebar/nsSidebar.js b/comm/suite/components/sidebar/nsSidebar.js
new file mode 100644
index 0000000000..472ec25a5d
--- /dev/null
+++ b/comm/suite/components/sidebar/nsSidebar.js
@@ -0,0 +1,348 @@
+/* -*- Mode: C++; 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/. */
+
+/*
+ * No magic constructor behaviour, as is de rigeur for XPCOM.
+ * If you must perform some initialization, and it could possibly fail (even
+ * due to an out-of-memory condition), you should use an Init method, which
+ * can convey failure appropriately (thrown exception in JS,
+ * NS_FAILED(nsresult) return in C++).
+ *
+ * In JS, you can actually cheat, because a thrown exception will cause the
+ * CreateInstance call to fail in turn, but not all languages are so lucky.
+ * (Though ANSI C++ provides exceptions, they are verboten in Mozilla code
+ * for portability reasons -- and even when you're building completely
+ * platform-specific code, you can't throw across an XPCOM method boundary.)
+ */
+
+const DEBUG = false; /* set to false to suppress debug messages */
+const PANELS_RDF_FILE = "UPnls"; /* directory services property to find panels.rdf */
+
+const SIDEBAR_CONTRACTID = "@mozilla.org/sidebar;1";
+const SIDEBAR_CID = Components.ID("{22117140-9c6e-11d3-aaf1-00805f8a4905}");
+const CONTAINER_CONTRACTID = "@mozilla.org/rdf/container;1";
+const NETSEARCH_CONTRACTID = "@mozilla.org/rdf/datasource;1?name=internetsearch"
+const nsISupports = Ci.nsISupports;
+const nsISidebar = Ci.nsISidebar;
+const nsIRDFContainer = Ci.nsIRDFContainer;
+const nsIProperties = Ci.nsIProperties;
+const nsIFileURL = Ci.nsIFileURL;
+const nsIRDFRemoteDataSource = Ci.nsIRDFRemoteDataSource;
+const nsIClassInfo = Ci.nsIClassInfo;
+
+// File extension for Sherlock search plugin description files
+const SHERLOCK_FILE_EXT_REGEXP = /\.src$/i;
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function nsSidebar()
+{
+ const RDF_CONTRACTID = "@mozilla.org/rdf/rdf-service;1";
+ const nsIRDFService = Ci.nsIRDFService;
+
+ this.rdf = Cc[RDF_CONTRACTID].getService(nsIRDFService);
+ this.datasource_uri = getSidebarDatasourceURI(PANELS_RDF_FILE);
+ gDebugLog('datasource_uri is ' + this.datasource_uri);
+ this.resource = 'urn:sidebar:current-panel-list';
+ this.datasource = this.rdf.GetDataSource(this.datasource_uri);
+}
+
+nsSidebar.prototype.nc = "http://home.netscape.com/NC-rdf#";
+
+nsSidebar.prototype.isPanel =
+function (aContentURL)
+{
+ var container =
+ Cc[CONTAINER_CONTRACTID].createInstance(nsIRDFContainer);
+
+ container.Init(this.datasource, this.rdf.GetResource(this.resource));
+
+ /* Create a resource for the new panel and add it to the list */
+ var panel_resource =
+ this.rdf.GetResource("urn:sidebar:3rdparty-panel:" + aContentURL);
+
+ return (container.IndexOf(panel_resource) != -1);
+}
+
+function sidebarURLSecurityCheck(url)
+{
+ if (!/(^http:|^ftp:|^https:)/i.test(url))
+ throw "Script attempted to add sidebar panel from illegal source";
+}
+
+/* decorate prototype to provide ``class'' methods and property accessors */
+nsSidebar.prototype.addPanel =
+function (aTitle, aContentURL, aCustomizeURL)
+{
+ gDebugLog("addPanel(" + aTitle + ", " + aContentURL + ", " +
+ aCustomizeURL + ")");
+
+ return this.addPanelInternal(aTitle, aContentURL, aCustomizeURL, false);
+}
+
+nsSidebar.prototype.addPersistentPanel =
+function(aTitle, aContentURL, aCustomizeURL)
+{
+ gDebugLog("addPersistentPanel(" + aTitle + ", " + aContentURL + ", " +
+ aCustomizeURL + ")\n");
+
+ return this.addPanelInternal(aTitle, aContentURL, aCustomizeURL, true);
+}
+
+nsSidebar.prototype.addPanelInternal =
+function (aTitle, aContentURL, aCustomizeURL, aPersist)
+{
+ sidebarURLSecurityCheck(aContentURL);
+
+ // Create a "container" wrapper around the current panels to
+ // manipulate the RDF:Seq more easily.
+ var panel_list = this.datasource.GetTarget(this.rdf.GetResource(this.resource), this.rdf.GetResource(nsSidebar.prototype.nc+"panel-list"), true);
+ if (panel_list) {
+ panel_list.QueryInterface(Ci.nsIRDFResource);
+ } else {
+ // Datasource is busted. Start over.
+ gDebugLog("Sidebar datasource is busted\n");
+ }
+
+ var container = Cc[CONTAINER_CONTRACTID].createInstance(nsIRDFContainer);
+ container.Init(this.datasource, panel_list);
+
+ /* Create a resource for the new panel and add it to the list */
+ var panel_resource =
+ this.rdf.GetResource("urn:sidebar:3rdparty-panel:" + aContentURL);
+ var panel_index = container.IndexOf(panel_resource);
+ var stringBundle, titleMessage, dialogMessage;
+ if (panel_index != -1)
+ {
+ try {
+ stringBundle = Services.strings.createBundle("chrome://communicator/locale/sidebar/sidebar.properties");
+ if (stringBundle) {
+ titleMessage = stringBundle.GetStringFromName("dupePanelAlertTitle");
+ dialogMessage = stringBundle.GetStringFromName("dupePanelAlertMessage2");
+ dialogMessage = dialogMessage.replace(/%url%/, aContentURL);
+ }
+ }
+ catch (e) {
+ titleMessage = "Sidebar";
+ dialogMessage = aContentURL + " already exists in Sidebar. No string bundle";
+ }
+
+ Services.prompt.alert(null, titleMessage, dialogMessage);
+
+ return;
+ }
+
+ try {
+ stringBundle = Services.strings.createBundle("chrome://communicator/locale/sidebar/sidebar.properties");
+ if (stringBundle) {
+ titleMessage = stringBundle.GetStringFromName("addPanelConfirmTitle");
+ dialogMessage = stringBundle.GetStringFromName("addPanelConfirmMessage2");
+ if (aPersist)
+ {
+ var warning = stringBundle.GetStringFromName("persistentPanelWarning2");
+ dialogMessage += "\n" + warning;
+ }
+ dialogMessage = dialogMessage.replace(/%title%/, aTitle);
+ dialogMessage = dialogMessage.replace(/%url%/, aContentURL);
+ dialogMessage = dialogMessage.replace(/#/g, "\n");
+ }
+ }
+ catch (e) {
+ titleMessage = "Add Tab to Sidebar";
+ dialogMessage = "No string bundle. Add the Tab '" + aTitle + "' to Sidebar?\n\n" + "Source: " + aContentURL;
+ }
+
+ var rv = Services.prompt.confirm(null, titleMessage, dialogMessage);
+
+ if (!rv)
+ return;
+
+ /* Now make some sidebar-ish assertions about it... */
+ this.datasource.Assert(panel_resource,
+ this.rdf.GetResource(this.nc + "title"),
+ this.rdf.GetLiteral(aTitle),
+ true);
+ this.datasource.Assert(panel_resource,
+ this.rdf.GetResource(this.nc + "content"),
+ this.rdf.GetLiteral(aContentURL),
+ true);
+ if (aCustomizeURL)
+ this.datasource.Assert(panel_resource,
+ this.rdf.GetResource(this.nc + "customize"),
+ this.rdf.GetLiteral(aCustomizeURL),
+ true);
+ var persistValue = aPersist ? "true" : "false";
+ this.datasource.Assert(panel_resource,
+ this.rdf.GetResource(this.nc + "persist"),
+ this.rdf.GetLiteral(persistValue),
+ true);
+
+ container.AppendElement(panel_resource);
+
+ // Use an assertion to pass a "refresh" event to all the sidebars.
+ // They use observers to watch for this assertion (in sidebarOverlay.js).
+ this.datasource.Assert(this.rdf.GetResource(this.resource),
+ this.rdf.GetResource(this.nc + "refresh"),
+ this.rdf.GetLiteral("true"),
+ true);
+ this.datasource.Unassert(this.rdf.GetResource(this.resource),
+ this.rdf.GetResource(this.nc + "refresh"),
+ this.rdf.GetLiteral("true"));
+
+ /* Write the modified panels out. */
+ this.datasource.QueryInterface(nsIRDFRemoteDataSource).Flush();
+}
+
+nsSidebar.prototype.validateSearchEngine =
+function (engineURL, iconURL)
+{
+ try
+ {
+ // Make sure the URLs are HTTP, HTTPS, or FTP.
+ var isWeb = /^(https?|ftp):\/\//i;
+
+ if (!isWeb.test(engineURL))
+ throw "Unsupported search engine URL";
+
+ if (iconURL && !isWeb.test(iconURL))
+ throw "Unsupported search icon URL.";
+ }
+ catch(ex)
+ {
+ gDebugLog(ex);
+ Cu.reportError("Invalid argument passed to window.sidebar.addSearchEngine: " + ex);
+
+ var searchBundle = Services.strings.createBundle("chrome://global/locale/search/search.properties");
+ var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+ var brandName = brandBundle.GetStringFromName("brandShortName");
+ var title = searchBundle.GetStringFromName("error_invalid_engine_title");
+ var msg = searchBundle.formatStringFromName("error_invalid_engine_msg",
+ [brandName], 1);
+ Services.ww.getNewPrompter(null).alert(title, msg);
+ return false;
+ }
+
+ return true;
+}
+
+// The suggestedTitle and suggestedCategory parameters are ignored, but remain
+// for backward compatibility.
+nsSidebar.prototype.addSearchEngine =
+function (engineURL, iconURL, suggestedTitle, suggestedCategory)
+{
+ gDebugLog("addSearchEngine(" + engineURL + ", " + iconURL + ", " +
+ suggestedCategory + ", " + suggestedTitle + ")");
+
+ if (!this.validateSearchEngine(engineURL, iconURL))
+ return;
+
+ // OpenSearch files will likely be far more common than Sherlock files, and
+ // have less consistent suffixes, so we assume that ".src" is a Sherlock
+ // (text) file, and anything else is OpenSearch (XML).
+ var dataType;
+ if (SHERLOCK_FILE_EXT_REGEXP.test(engineURL))
+ dataType = Ci.nsISearchEngine.DATA_TEXT;
+ else
+ dataType = Ci.nsISearchEngine.DATA_XML;
+
+ Services.search.addEngine(engineURL, dataType, iconURL, true);
+}
+
+// This function exists largely to implement window.external.AddSearchProvider(),
+// to match other browsers' APIs. The capitalization, although nonstandard here,
+// is therefore important.
+nsSidebar.prototype.AddSearchProvider =
+function (aDescriptionURL)
+{
+ // Get the favicon URL for the current page, or our best guess at the current
+ // page since we don't have easy access to the active document. Most search
+ // engines will override this with an icon specified in the OpenSearch
+ // description anyway.
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+ var browser = win.getBrowser();
+ var iconURL = "";
+ // Use documentURIObject in the check for shouldLoadFavIcon so that we
+ // do the right thing with about:-style error pages. Bug 453442
+ if (browser.shouldLoadFavIcon(browser.selectedBrowser
+ .contentDocument
+ .documentURIObject))
+ iconURL = browser.getIcon();
+
+ if (!this.validateSearchEngine(aDescriptionURL, iconURL))
+ return;
+
+ const typeXML = Ci.nsISearchEngine.DATA_XML;
+ Services.search.addEngine(aDescriptionURL, typeXML, iconURL, true);
+}
+
+// This function exists to implement window.external.IsSearchProviderInstalled(),
+// for compatibility with other browsers. It will return an integer value
+// indicating whether the given engine is installed for the current user.
+// However, it is currently stubbed out due to security/privacy concerns
+// stemming from difficulties in determining what domain issued the request.
+// See bug 340604 and
+// http://msdn.microsoft.com/en-us/library/aa342526%28VS.85%29.aspx .
+// XXX Implement this!
+nsSidebar.prototype.IsSearchProviderInstalled =
+function (aSearchURL)
+{
+ return 0;
+}
+
+nsSidebar.prototype.classInfo = XPCOMUtils.generateCI({
+ classID: SIDEBAR_CID,
+ contractID: SIDEBAR_CONTRACTID,
+ classDescription: "Sidebar",
+ interfaces: [nsISidebar],
+ flags: nsIClassInfo.DOM_OBJECT});
+
+nsSidebar.prototype.QueryInterface =
+ XPCOMUtils.generateQI([nsISidebar]);
+
+nsSidebar.prototype.classID = SIDEBAR_CID;
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSidebar]);
+
+var gDebugLog;
+
+/* static functions */
+if (DEBUG)
+ gDebugLog = function (s) { dump("-*- sidebar component: " + s + "\n"); }
+else
+ gDebugLog = function (s) {}
+
+function getSidebarDatasourceURI(panels_file_id)
+{
+ try
+ {
+ /* use the fileLocator to look in the profile directory
+ * to find 'panels.rdf', which is the
+ * database of the user's currently selected panels.
+ * if <profile>/panels.rdf doesn't exist, get will copy
+ *bin/defaults/profile/panels.rdf to <profile>/panels.rdf */
+ var sidebar_file = Services.dirsvc.get(panels_file_id,
+ Ci.nsIFile);
+
+ if (!sidebar_file.exists())
+ {
+ /* this should not happen, as GetFileLocation() should copy
+ * defaults/panels.rdf to the users profile directory */
+ gDebugLog("sidebar file does not exist");
+ return null;
+ }
+
+ var file_handler = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
+ var sidebar_uri = file_handler.getURLSpecFromFile(sidebar_file);
+ gDebugLog("sidebar uri is " + sidebar_uri);
+ return sidebar_uri;
+ }
+ catch (ex)
+ {
+ /* this should not happen */
+ gDebugLog("caught " + ex + " getting sidebar datasource uri");
+ return null;
+ }
+}
diff --git a/comm/suite/components/sync/content/aboutSyncTabs-bindings.xml b/comm/suite/components/sync/content/aboutSyncTabs-bindings.xml
new file mode 100644
index 0000000000..6365bf55fc
--- /dev/null
+++ b/comm/suite/components/sync/content/aboutSyncTabs-bindings.xml
@@ -0,0 +1,46 @@
+<?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/. -->
+
+<bindings id="tabBindings"
+ 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="tab-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <content>
+ <xul:hbox flex="1">
+ <xul:vbox>
+ <xul:image class="tabIcon"
+ xbl:inherits="src=icon"/>
+ </xul:vbox>
+ <xul:vbox flex="1">
+ <xul:label xbl:inherits="value=title,selected"
+ crop="end" flex="1" class="title"/>
+ <xul:label xbl:inherits="value=url,selected"
+ crop="end" flex="1" class="url"/>
+ </xul:vbox>
+ </xul:hbox>
+ </content>
+ <handlers>
+ <handler event="dblclick" button="0">
+ <![CDATA[
+ RemoteTabViewer.openSelected();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="client-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <content>
+ <xul:hbox align="center" onfocus="event.target.blur();" onselect="return false;">
+ <xul:image/>
+ <xul:label xbl:inherits="value=clientName"
+ class="clientName"
+ crop="center" flex="1"/>
+ </xul:hbox>
+ </content>
+ </binding>
+</bindings>
diff --git a/comm/suite/components/sync/content/aboutSyncTabs.css b/comm/suite/components/sync/content/aboutSyncTabs.css
new file mode 100644
index 0000000000..83782b4d5f
--- /dev/null
+++ b/comm/suite/components/sync/content/aboutSyncTabs.css
@@ -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/. */
+
+richlistitem[type="tab"] {
+ -moz-binding: url(chrome://communicator/content/aboutSyncTabs-bindings.xml#tab-listing);
+}
+
+richlistitem[type="client"] {
+ -moz-binding: url(chrome://communicator/content/aboutSyncTabs-bindings.xml#client-listing);
+}
diff --git a/comm/suite/components/sync/content/aboutSyncTabs.js b/comm/suite/components/sync/content/aboutSyncTabs.js
new file mode 100644
index 0000000000..e13c2be685
--- /dev/null
+++ b/comm/suite/components/sync/content/aboutSyncTabs.js
@@ -0,0 +1,293 @@
+/* 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 {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+const {PlacesUIUtils} = ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
+const {PlacesUtils} = ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var RemoteTabViewer = {
+ _tabsList: null,
+
+ init: function () {
+ Services.obs.addObserver(this, "weave:service:login:finish");
+ Services.obs.addObserver(this, "weave:engine:sync:finish");
+
+ this._tabsList = document.getElementById("tabsList");
+
+ this.buildList(true);
+ },
+
+ uninit: function () {
+ Services.obs.removeObserver(this, "weave:service:login:finish");
+ Services.obs.removeObserver(this, "weave:engine:sync:finish");
+ },
+
+ buildList: function(force) {
+ if (!Weave.Service.isLoggedIn || !this._refetchTabs(force))
+ return;
+ //XXXzpao We should say something about not being logged in & not having data
+ // or tell the appropriate condition. (bug 583344)
+
+ this._generateTabList();
+ },
+
+ createItem: function(attrs) {
+ let item = document.createElement("richlistitem");
+
+ // Copy the attributes from the argument into the item
+ for (let attr in attrs)
+ item.setAttribute(attr, attrs[attr]);
+
+ if (attrs["type"] == "tab")
+ item.label = attrs.title || attrs.url;
+
+ return item;
+ },
+
+ filterTabs: function(event) {
+ let val = event.target.value.toLowerCase();
+ let numTabs = this._tabsList.getRowCount();
+ let client = null;
+ for (let i = 0; i < numTabs; i++) {
+ let item = this._tabsList.getItemAtIndex(i);
+ let hide = false; // By default, make sure the item is visible.
+ switch (item.getAttribute("type")) {
+ case "tab":
+ if (item.getAttribute("url").toLowerCase().indexOf(val) == -1 &&
+ item.getAttribute("title").toLowerCase().indexOf(val) == -1)
+ hide = true;
+ else
+ client = null; // This client should not be hidden.
+ break;
+ case "client":
+ if (client)
+ client.hidden = true; // Hide the last client, it had no visible tabs.
+ client = item;
+ break;
+ }
+ item.hidden = hide;
+ }
+ if (client)
+ client.hidden = true; // Hide the last client, it had no visible tabs.
+ },
+
+ openSelected: function() {
+ let items = this._tabsList.selectedItems;
+ let urls = [];
+ for (let i = 0; i < items.length; i++) {
+ if (items[i].getAttribute("type") == "tab") {
+ urls.push(items[i].getAttribute("url"));
+ let index = this._tabsList.getIndexOfItem(items[i]);
+ this._tabsList.removeItemAt(index);
+ }
+ }
+ if (urls.length) {
+ getTopWin().getBrowser().loadTabs(urls);
+ this._tabsList.clearSelection();
+ }
+ },
+
+ bookmarkSingleTab: function() {
+ let item = this._tabsList.selectedItems[0];
+ let uri = Weave.Utils.makeURI(item.getAttribute("url"));
+ let title = item.getAttribute("title");
+ PlacesUIUtils.showMinimalAddBookmarkUI(uri, title);
+ },
+
+ bookmarkSelectedTabs: function() {
+ let items = this._tabsList.selectedItems;
+ let URIs = [];
+ let titles = [];
+ for (let i = 0; i < items.length; i++) {
+ if (items[i].getAttribute("type") == "tab") {
+ let uri = Weave.Utils.makeURI(items[i].getAttribute("url"));
+ if (!uri)
+ continue;
+
+ URIs.push(uri);
+ titles.push(items[i].getAttribute("title"));
+ }
+ }
+ if (URIs.length)
+ PlacesUIUtils.showMinimalAddMultiBookmarkUI(URIs, titles);
+ },
+
+ getIcon: function (iconUri, defaultIcon) {
+ try {
+ let iconURI = Weave.Utils.makeURI(iconUri);
+ return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
+ } catch(ex) {
+ // Do nothing.
+ }
+
+ // Just give the provided default icon or the system's default.
+ return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec;
+ },
+
+ _generateTabList: function() {
+ let engine = Weave.Service.engineManager.get("tabs");
+ let list = this._tabsList;
+
+ // clear out existing richlistitems
+ let count = list.getRowCount();
+ if (count > 0) {
+ for (let i = count - 1; i >= 0; i--)
+ list.removeItemAt(i);
+ }
+
+ let seenURLs = new Set();
+ let localURLs = engine.getOpenURLs();
+
+ for (let [guid, client] of Object.entries(engine.getAllClients())) {
+ let appendClient = true;
+
+ client.tabs.forEach(function({title, urlHistory, icon}) {
+ let url = urlHistory[0];
+ if (!url || localURLs.has(url) || seenURLs.has(url))
+ return;
+
+ seenURLs.add(url);
+
+ if (appendClient) {
+ let attrs = {
+ type: "client",
+ clientName: client.clientName,
+ class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop"
+ };
+ let clientEnt = this.createItem(attrs);
+ list.appendChild(clientEnt);
+ appendClient = false;
+ clientEnt.disabled = true;
+ }
+ let attrs = {
+ type: "tab",
+ title: title || url,
+ url: url,
+ icon: this.getIcon(icon)
+ }
+ let tab = this.createItem(attrs);
+ list.appendChild(tab);
+ }, this);
+ }
+ },
+
+ adjustContextMenu: function(event) {
+ let mode = "all";
+ switch (this._tabsList.selectedItems.length) {
+ case 0:
+ break;
+ case 1:
+ mode = "single"
+ break;
+ default:
+ mode = "multiple";
+ break;
+ }
+ let menu = document.getElementById("tabListContext");
+ let el = menu.firstChild;
+ while (el) {
+ let showFor = el.getAttribute("showFor");
+ if (showFor)
+ el.hidden = showFor != mode && showFor != "all";
+ else // menuseparator
+ el.hidden = mode == "all";
+ el = el.nextSibling;
+ }
+ },
+
+ _refetchTabs: function(force) {
+ if (!force) {
+ // Don't bother refetching tabs if we already did so recently
+ let lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch", 0);
+ let now = Math.floor(Date.now() / 1000);
+ if (now - lastFetch < 30)
+ return false;
+ }
+
+ // if Clients hasn't synced yet this session, need to sync it as well
+ if (Weave.Service.clientsEngine.lastSync == 0)
+ Weave.Service.clientsEngine.sync();
+
+ // Force a sync only for the tabs engine
+ let engine = Weave.Service.engineManager.get("tabs");
+ engine.lastModified = null;
+ engine.sync();
+ Services.prefs.setIntPref("services.sync.lastTabFetch",
+ Math.floor(Date.now() / 1000));
+
+ return true;
+ },
+
+ observe: function(subject, topic, data) {
+ switch (topic) {
+ case "weave:service:login:finish":
+ this.buildList(true);
+ break;
+ case "weave:engine:sync:finish":
+ if (subject == "tabs")
+ this._generateTabList();
+ break;
+ }
+ },
+
+ handleClick: function(event) {
+ if (event.target.getAttribute("type") == "tab" && event.button == 1) {
+ let url = event.target.getAttribute("url");
+ openUILink(url, event);
+ let index = this._tabsList.getIndexOfItem(event.target);
+ this._tabsList.removeItemAt(index);
+ }
+ }
+};
+
+var EventDirector = {
+ handleEvent: function(event) {
+ switch (event.type) {
+ case "click":
+ RemoteTabViewer.handleClick(event);
+ break;
+ case "contextmenu":
+ RemoteTabViewer.adjustContextMenu(event);
+ break;
+ case "command":
+ switch (event.target.id) {
+ case "openSingleTab":
+ case "openSelectedTabs":
+ RemoteTabViewer.openSelected();
+ break;
+ case "bookmarkSingleTab":
+ RemoteTabViewer.bookmarkSingleTab();
+ break;
+ case "bookmarkSelectedTabs":
+ RemoteTabViewer.bookmarkSelectedTabs();
+ break;
+ case "buildList":
+ RemoteTabViewer.buildList();
+ break;
+ case "filterTabs":
+ RemoteTabViewer.filterTabs(event);
+ break;
+ }
+ break;
+ }
+ }
+};
+
+window.onload = function() {
+ RemoteTabViewer.init();
+
+ let tabsList = document.getElementById("tabsList");
+ tabsList.addEventListener("click", EventDirector);
+ tabsList.addEventListener("contextmenu", EventDirector);
+
+ document.getElementById("tabListContext")
+ .addEventListener("command", EventDirector);
+ document.getElementById("filterTabs")
+ .addEventListener("command", EventDirector);
+}
+
+window.onunload = function() {
+ RemoteTabViewer.uninit();
+}
diff --git a/comm/suite/components/sync/content/aboutSyncTabs.xul b/comm/suite/components/sync/content/aboutSyncTabs.xul
new file mode 100644
index 0000000000..b0355557de
--- /dev/null
+++ b/comm/suite/components/sync/content/aboutSyncTabs.xul
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/aboutSyncTabs.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/content/aboutSyncTabs.css" type="text/css"?>
+
+<!DOCTYPE window [
+ <!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://communicator/locale/aboutSyncTabs.dtd">
+ %aboutSyncTabsDTD;
+]>
+
+<window id="tabs-display"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="&tabs.otherComputers.label;">
+
+ <script src="chrome://communicator/content/aboutSyncTabs.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+
+ <html:head>
+ <html:link rel="icon" href="chrome://communicator/skin/sync/sync-16.png"/>
+ </html:head>
+
+ <popupset id="contextmenus">
+ <menupopup id="tabListContext">
+ <menuitem id="openSingleTab"
+ label="&tabs.context.openTab.label;"
+ accesskey="&tabs.context.openTab.accesskey;"
+ showFor="single"/>
+ <menuitem id="bookmarkSingleTab"
+ label="&tabs.context.bookmarkSingleTab.label;"
+ accesskey="&tabs.context.bookmarkSingleTab.accesskey;"
+ showFor="single"/>
+ <menuitem id="openSelectedTabs"
+ label="&tabs.context.openMultipleTabs.label;"
+ accesskey="&tabs.context.openMultipleTabs.accesskey;"
+ showFor="multiple"/>
+ <menuitem id="bookmarkSelectedTabs"
+ label="&tabs.context.bookmarkMultipleTabs.label;"
+ accesskey="&tabs.context.bookmarkMultipleTabs.accesskey;"
+ showFor="multiple"/>
+ <menuseparator/>
+ <menuitem id="buildList"
+ label="&tabs.context.refreshList.label;"
+ accesskey="&tabs.context.refreshList.accesskey;"
+ showFor="all"/>
+ </menupopup>
+ </popupset>
+ <richlistbox id="tabsList"
+ context="tabListContext"
+ seltype="multiple"
+ class="plain"
+ align="center"
+ flex="1">
+ <hbox id="headers" align="center">
+ <label id="tabsListHeading"
+ value="&tabs.otherComputers.label;"/>
+ <spacer flex="1"/>
+ <textbox id="filterTabs"
+ type="search"
+ aria-controls="tabsList"
+ emptytext="&tabs.searchText.label;"/>
+ </hbox>
+ </richlistbox>
+</window>
diff --git a/comm/suite/components/sync/content/syncAddDevice.js b/comm/suite/components/sync/content/syncAddDevice.js
new file mode 100644
index 0000000000..d986b54f00
--- /dev/null
+++ b/comm/suite/components/sync/content/syncAddDevice.js
@@ -0,0 +1,142 @@
+/* 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 {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const PIN_PART_LENGTH = 4;
+
+const ADD_DEVICE_PAGE = 0;
+const SYNC_KEY_PAGE = 1;
+const DEVICE_CONNECTED_PAGE = 2;
+
+var gSyncAddDevice = {
+ init: function init() {
+ this.nextFocusEl = { pin1: this.pin2,
+ pin2: this.pin3,
+ pin3: this.wizard.getButton("next") };
+
+ this.throbber = document.getElementById("add-device-throbber");
+ this.errorRow = document.getElementById("errorRow");
+ },
+
+ onPageShow: function onPageShow() {
+ this.wizard.getButton("back").hidden = true;
+
+ switch (this.wizard.pageIndex) {
+ case ADD_DEVICE_PAGE:
+ this.onTextBoxInput();
+ this.wizard.canRewind = false;
+ this.wizard.getButton("next").hidden = false;
+ this.pin1.focus();
+ break;
+ case SYNC_KEY_PAGE:
+ this.wizard.canAdvance = false;
+ this.wizard.canRewind = true;
+ this.wizard.getButton("back").hidden = false;
+ this.wizard.getButton("next").hidden = true;
+ document.getElementById("weavePassphrase").value =
+ Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey);
+ break;
+ case DEVICE_CONNECTED_PAGE:
+ this.wizard.canAdvance = true;
+ this.wizard.canRewind = false;
+ this.wizard.getButton("cancel").hidden = true;
+ break;
+ }
+ },
+
+ onWizardAdvance: function onWizardAdvance() {
+ switch (this.wizard.pageIndex) {
+ case ADD_DEVICE_PAGE:
+ this.startTransfer();
+ return false;
+ case DEVICE_CONNECTED_PAGE:
+ window.close();
+ return false;
+ }
+ return true;
+ },
+
+ startTransfer: function startTransfer() {
+ this.errorRow.hidden = true;
+ // When onAbort is called, Weave may already be gone.
+ const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
+
+ let self = this;
+ let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
+ onPaired: function onPaired() {
+ let credentials = {account: Weave.Service.identity.account,
+ password: Weave.Service.identity.basicPassword,
+ synckey: Weave.Service.identity.syncKey,
+ serverURL: Weave.Service.serverURL};
+ jpakeclient.sendAndComplete(credentials);
+ },
+ onComplete: function onComplete() {
+ delete self._jpakeclient;
+ self.wizard.pageIndex = DEVICE_CONNECTED_PAGE;
+
+ // Schedule a sync for soonish to fetch the data uploaded by the
+ // device with which we just paired.
+ Weave.Service.scheduler.scheduleNextSync(Weave.Service.scheduler.activeInterval);
+ },
+ onAbort: function onAbort(error) {
+ delete self._jpakeclient;
+
+ // Aborted by user, ignore.
+ if (error == JPAKE_ERROR_USERABORT)
+ return;
+
+ self.errorRow.hidden = false;
+ self.throbber.hidden = true;
+ self.pin1.value = self.pin2.value = self.pin3.value = "";
+ self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
+ self.pin1.focus();
+ }
+ });
+ this.throbber.hidden = false;
+ this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true;
+ this.wizard.canAdvance = false;
+
+ let pin = this.pin1.value + this.pin2.value + this.pin3.value;
+ let expectDelay = false;
+ jpakeclient.pairWithPIN(pin, expectDelay);
+ },
+
+ onWizardBack: function onWizardBack() {
+ if (this.wizard.pageIndex != SYNC_KEY_PAGE)
+ return true;
+
+ this.wizard.pageIndex = ADD_DEVICE_PAGE;
+ return false;
+ },
+
+ onWizardCancel: function onWizardCancel() {
+ if (this._jpakeclient) {
+ this._jpakeclient.abort();
+ delete this._jpakeclient;
+ }
+ return true;
+ },
+
+ onTextBoxInput: function onTextBoxInput(textbox) {
+ this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH &&
+ this.pin2.value.length == PIN_PART_LENGTH &&
+ this.pin3.value.length == PIN_PART_LENGTH);
+ if (textbox && textbox.value.length == PIN_PART_LENGTH)
+ this.nextFocusEl[textbox.id].focus();
+ },
+
+ goToSyncKeyPage: function goToSyncKeyPage() {
+ this.wizard.pageIndex = SYNC_KEY_PAGE;
+ }
+};
+
+// onWizardAdvance() and onPageShow() are run before init() so we'll set
+// these up as lazy getters.
+["wizard", "pin1", "pin2", "pin3"].forEach(function (id) {
+ XPCOMUtils.defineLazyGetter(gSyncAddDevice, id, function() {
+ return document.getElementById(id);
+ });
+});
diff --git a/comm/suite/components/sync/content/syncAddDevice.xul b/comm/suite/components/sync/content/syncAddDevice.xul
new file mode 100644
index 0000000000..e7a30d2aa3
--- /dev/null
+++ b/comm/suite/components/sync/content/syncAddDevice.xul
@@ -0,0 +1,128 @@
+<?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://communicator/skin/sync/syncSetup.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sync/syncCommon.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://communicator/locale/sync/syncBrand.dtd">
+<!ENTITY % syncSetupDTD SYSTEM "chrome://communicator/locale/sync/syncSetup.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncSetupDTD;
+]>
+<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ id="wizard"
+ title="&addDevice.title.label;"
+ windowtype="Sync:AddDevice"
+ persist="screenX screenY"
+ onwizardnext="return gSyncAddDevice.onWizardAdvance();"
+ onwizardback="return gSyncAddDevice.onWizardBack();"
+ onwizardcancel="gSyncAddDevice.onWizardCancel();"
+ onload="gSyncAddDevice.init();">
+
+ <script src="chrome://communicator/content/sync/syncAddDevice.js"/>
+ <script src="chrome://communicator/content/sync/syncUtils.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+
+ <wizardpage id="addDevicePage"
+ label="&addDevice.title.label;"
+ onpageshow="gSyncAddDevice.onPageShow();">
+ <description>
+ &addDevice.dialog.description.label;
+ <label class="text-link"
+ value="&addDevice.showMeHow.label;"
+ href="https://services.mozilla.com/sync/help/add-device"/>
+ </description>
+ <spacer flex="1"/>
+ <description>
+ &addDevice.dialog.enterCode.label;
+ </description>
+ <spacer flex="1"/>
+ <vbox align="center">
+ <textbox id="pin1"
+ class="pin"
+ size="4"
+ maxlength="4"
+ oninput="gSyncAddDevice.onTextBoxInput(this);"
+ onfocus="this.select();"/>
+ <textbox id="pin2"
+ class="pin"
+ size="4"
+ maxlength="4"
+ oninput="gSyncAddDevice.onTextBoxInput(this);"
+ onfocus="this.select();"/>
+ <textbox id="pin3"
+ class="pin"
+ size="4"
+ maxlength="4"
+ oninput="gSyncAddDevice.onTextBoxInput(this);"
+ onfocus="this.select();"/>
+ </vbox>
+ <spacer flex="1"/>
+ <vbox id="add-device-throbber" align="center" hidden="true">
+ <image/>
+ </vbox>
+ <hbox id="errorRow" pack="center" hidden="true">
+ <image class="statusIcon" status="error"/>
+ <label class="status"
+ value="&addDevice.dialog.tryAgain.label;"/>
+ </hbox>
+ <spacer flex="3"/>
+ <label class="text-link"
+ value="&addDevice.dontHaveDevice.label;"
+ onclick="gSyncAddDevice.goToSyncKeyPage();"/>
+ </wizardpage>
+
+ <!-- Need a non-empty label here, otherwise we get a default label on Mac -->
+ <wizardpage id="syncKeyPage"
+ label=" "
+ onpageshow="gSyncAddDevice.onPageShow();">
+ <description>
+ &addDevice.dialog.recoveryKey.label;
+ </description>
+ <spacer/>
+
+ <groupbox>
+ <label value="&recoveryKeyEntry.label;"
+ accesskey="&recoveryKeyEntry.accesskey;"
+ control="weavePassphrase"/>
+ <textbox id="weavePassphrase"
+ readonly="true"/>
+ </groupbox>
+
+ <groupbox align="center">
+ <description>&recoveryKeyBackup.description;</description>
+ <hbox>
+ <button id="printSyncKeyButton"
+ label="&button.syncKeyBackup.print.label;"
+ accesskey="&button.syncKeyBackup.print.accesskey;"
+ oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
+ <button id="saveSyncKeyButton"
+ label="&button.syncKeyBackup.save.label;"
+ accesskey="&button.syncKeyBackup.save.accesskey;"
+ oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
+ </hbox>
+ </groupbox>
+ </wizardpage>
+
+ <wizardpage id="deviceConnectedPage"
+ label="&addDevice.dialog.connected.label;"
+ onpageshow="gSyncAddDevice.onPageShow();">
+ <vbox align="center">
+ <image id="successPageIcon"/>
+ </vbox>
+ <separator/>
+ <description class="normal">
+ &addDevice.dialog.successful.label;
+ </description>
+ </wizardpage>
+
+</wizard>
diff --git a/comm/suite/components/sync/content/syncGenericChange.js b/comm/suite/components/sync/content/syncGenericChange.js
new file mode 100644
index 0000000000..13cab89811
--- /dev/null
+++ b/comm/suite/components/sync/content/syncGenericChange.js
@@ -0,0 +1,232 @@
+/* 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 {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var Change = {
+ _dialog: null,
+ _dialogType: null,
+ _status: null,
+ _statusIcon: null,
+ _firstBox: null,
+ _secondBox: null,
+ _passphraseBox: null,
+
+ get _currentPasswordInvalid() {
+ return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
+ },
+
+ get _updatingPassphrase() {
+ return this._dialogType == "UpdatePassphrase";
+ },
+
+ onLoad: function Change_onLoad() {
+ /* Load labels */
+ let introText = document.getElementById("introText");
+ let warningText = document.getElementById("warningText");
+
+ // load some other elements & info from the window
+ this._dialog = document.getElementById("change-dialog");
+ this._dialogType = window.arguments[0];
+ this._duringSetup = window.arguments[1];
+ this._status = document.getElementById("status");
+ this._statusIcon = document.getElementById("statusIcon");
+ this._statusRow = document.getElementById("statusRow");
+ this._firstBox = document.getElementById("textBox1");
+ this._secondBox = document.getElementById("textBox2");
+ this._passphraseBox = document.getElementById("passphraseBox");
+
+ this._dialog.getButton("finish").disabled = true;
+ this._dialog.getButton("back").hidden = true;
+
+ this._stringBundle =
+ Services.strings.createBundle("chrome://communicator/locale/sync/syncGenericChange.properties");
+
+ switch (this._dialogType) {
+ case "UpdatePassphrase":
+ case "ResetPassphrase":
+ document.getElementById("textBox1Row").hidden = true;
+ document.getElementById("textBox2Row").hidden = true;
+ document.getElementById("passphraseLabel").value
+ = this._str("new.recoverykey.label");
+ document.getElementById("passphraseSpacer").hidden = false;
+
+ if (this._updatingPassphrase) {
+ document.getElementById("passphraseHelpBox").hidden = false;
+ document.title = this._str("new.recoverykey.title");
+ introText.textContent = this._str("new.recoverykey.introText");
+ this._dialog.getButton("finish").label
+ = this._str("new.recoverykey.acceptButton");
+ }
+ else {
+ document.getElementById("generatePassphraseButton").hidden = false;
+ document.getElementById("passphraseBackupButtons").hidden = false;
+ this._passphraseBox.readOnly = true;
+ let pp = Weave.Service.identity.syncKey;
+ if (Weave.Utils.isPassphrase(pp))
+ pp = Weave.Utils.hyphenatePassphrase(pp);
+ this._passphraseBox.value = pp;
+ this._passphraseBox.focus();
+ document.title = this._str("change.recoverykey.title");
+ introText.textContent = this._str("change.recoverykey.introText2");
+ warningText.textContent = this._str("change.recoverykey.warningText");
+ this._dialog.getButton("finish").label
+ = this._str("change.recoverykey.acceptButton");
+ if (this._duringSetup)
+ this._dialog.getButton("finish").disabled = false;
+ }
+ break;
+ case "ChangePassword":
+ document.getElementById("passphraseRow").hidden = true;
+ let box1label = document.getElementById("textBox1Label");
+ let box2label = document.getElementById("textBox2Label");
+ box1label.value = this._str("new.password.label");
+
+ if (this._currentPasswordInvalid) {
+ document.title = this._str("new.password.title");
+ introText.textContent = this._str("new.password.introText");
+ this._dialog.getButton("finish").label
+ = this._str("new.password.acceptButton");
+ document.getElementById("textBox2Row").hidden = true;
+ }
+ else {
+ document.title = this._str("change.password.title");
+ box2label.value = this._str("new.password.confirm");
+ introText.textContent = this._str("change.password3.introText");
+ warningText.textContent = this._str("change.password.warningText");
+ this._dialog.getButton("finish").label
+ = this._str("change.password.acceptButton");
+ }
+ break;
+ }
+ document.getElementById("change-page")
+ .setAttribute("label", document.title);
+ },
+
+ _clearStatus: function _clearStatus() {
+ this._status.textContent = "";
+ this._statusIcon.removeAttribute("status");
+ },
+
+ _updateStatus: function Change__updateStatus(str, state) {
+ this._updateStatusWithString(this._str(str), state);
+ },
+
+ _updateStatusWithString: function Change__updateStatusWithString(string, state) {
+ this._statusRow.hidden = false;
+ this._status.textContent = string;
+ this._statusIcon.setAttribute("status", state);
+
+ let error = state == "error";
+ this._dialog.getButton("cancel").disabled = !error;
+ this._dialog.getButton("finish").disabled = !error;
+ document.getElementById("printSyncKeyButton").disabled = !error;
+ document.getElementById("saveSyncKeyButton").disabled = !error;
+
+ if (state == "success")
+ window.setTimeout(window.close, 1500);
+ },
+
+ onDialogAccept: function() {
+ switch (this._dialogType) {
+ case "UpdatePassphrase":
+ case "ResetPassphrase":
+ return this.doChangePassphrase();
+ break;
+ case "ChangePassword":
+ return this.doChangePassword();
+ break;
+ }
+ },
+
+ doGeneratePassphrase: function () {
+ let passphrase = Weave.Utils.generatePassphrase();
+ this._passphraseBox.value = Weave.Utils.hyphenatePassphrase(passphrase);
+ this._clearStatus();
+ this._dialog.getButton("finish").disabled = false;
+ },
+
+ doChangePassphrase: function Change_doChangePassphrase() {
+ let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value);
+ if (this._updatingPassphrase) {
+ Weave.Service.identity.syncKey = pp;
+ if (Weave.Service.login()) {
+ this._updateStatus("change.recoverykey.success", "success");
+ Weave.Service.persistLogin();
+ }
+ else {
+ this._updateStatus("new.passphrase.status.incorrect", "error");
+ }
+ }
+ else {
+ this._updateStatus("change.recoverykey.label", "active");
+
+ if (Weave.Service.changePassphrase(pp))
+ this._updateStatus("change.recoverykey.success", "success");
+ else
+ this._updateStatus("change.recoverykey.error", "error");
+ }
+
+ return false;
+ },
+
+ doChangePassword: function Change_doChangePassword() {
+ if (this._currentPasswordInvalid) {
+ Weave.Service.identity.basicPassword = this._firstBox.value;
+ if (Weave.Service.login()) {
+ this._updateStatus("change.password.status.success", "success");
+ Weave.Service.persistLogin();
+ }
+ else {
+ this._updateStatus("new.password.status.incorrect", "error");
+ }
+ }
+ else {
+ this._updateStatus("change.password.status.active", "active");
+
+ if (Weave.Service.changePassword(this._firstBox.value))
+ this._updateStatus("change.password.status.success", "success");
+ else
+ this._updateStatus("change.password.status.error", "error");
+ }
+
+ return false;
+ },
+
+ validate: function () {
+ let valid = false;
+ let errorString = "";
+
+ if (this._dialogType == "ChangePassword") {
+ if (this._currentPasswordInvalid)
+ [valid, errorString] = gSyncUtils.validatePassword(this._firstBox);
+ else
+ [valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox);
+ }
+ else {
+ if (!this._updatingPassphrase)
+ return;
+
+ valid = this._passphraseBox.value != "";
+ }
+
+ if (errorString == "")
+ this._clearStatus();
+ else
+ this._updateStatusWithString(errorString, "error");
+
+ this._statusRow.hidden = valid;
+ this._dialog.getButton("finish").disabled = !valid;
+ },
+
+ _str: function Change__string(str) {
+ try {
+ return this._stringBundle.GetStringFromName(str);
+ } catch (e) {
+ Cu.reportError("Missing string: " + str);
+ throw e;
+ }
+ }
+};
diff --git a/comm/suite/components/sync/content/syncGenericChange.xul b/comm/suite/components/sync/content/syncGenericChange.xul
new file mode 100644
index 0000000000..aac4a004fa
--- /dev/null
+++ b/comm/suite/components/sync/content/syncGenericChange.xul
@@ -0,0 +1,116 @@
+<?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://communicator/skin/sync/syncSetup.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sync/syncCommon.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://communicator/locale/sync/syncBrand.dtd">
+<!ENTITY % syncSetupDTD SYSTEM "chrome://communicator/locale/sync/syncSetup.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncSetupDTD;
+]>
+<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ id="change-dialog"
+ windowtype="Weave:ChangeSomething"
+ persist="screenX screenY"
+ onwizardnext="Change.onLoad();"
+ onwizardfinish="return Change.onDialogAccept();">
+
+ <script src="chrome://communicator/content/sync/syncGenericChange.js"/>
+ <script src="chrome://communicator/content/sync/syncUtils.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+
+ <wizardpage id="change-page"
+ label="">
+
+ <description id="introText"/>
+
+ <separator class="thin"/>
+
+ <groupbox>
+ <grid>
+ <columns>
+ <column align="right"/>
+ <column flex="3"/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="textBox1Row" align="center">
+ <label id="textBox1Label" control="textBox1"/>
+ <textbox id="textBox1" type="password" oninput="Change.validate();"/>
+ <spacer/>
+ </row>
+ <row id="textBox2Row" align="center">
+ <label id="textBox2Label" control="textBox2"/>
+ <textbox id="textBox2" type="password" oninput="Change.validate();"/>
+ <spacer/>
+ </row>
+ </rows>
+ </grid>
+
+ <vbox id="passphraseRow">
+ <hbox flex="1">
+ <label id="passphraseLabel" control="passphraseBox"/>
+ <spacer flex="1"/>
+ <label id="generatePassphraseButton"
+ hidden="true"
+ value="&recoveryGenerateNewKey.label;"
+ class="text-link inline-link"
+ onclick="event.stopPropagation();
+ Change.doGeneratePassphrase();"/>
+ </hbox>
+ <textbox id="passphraseBox"
+ flex="1"
+ onfocus="this.select();"
+ oninput="Change.validate();"/>
+ </vbox>
+
+ <vbox id="feedback" pack="center">
+ <hbox id="statusRow" align="center">
+ <image id="statusIcon" class="statusIcon"/>
+ <label id="status" class="status" value=" "/>
+ </hbox>
+ </vbox>
+ </groupbox>
+
+ <separator class="thin"/>
+
+ <hbox id="passphraseBackupButtons"
+ hidden="true"
+ pack="center">
+ <button id="printSyncKeyButton"
+ label="&button.syncKeyBackup.print.label;"
+ accesskey="&button.syncKeyBackup.print.accesskey;"
+ oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
+ <button id="saveSyncKeyButton"
+ label="&button.syncKeyBackup.save.label;"
+ accesskey="&button.syncKeyBackup.save.accesskey;"
+ oncommand="gSyncUtils.passphraseSave('passphraseBox');"/>
+ </hbox>
+
+ <vbox id="passphraseHelpBox"
+ hidden="true">
+ <description>
+ &existingRecoveryKey.description;
+ <label class="text-link"
+ href="https://services.mozilla.com/sync/help/manual-setup">
+ &addDevice.showMeHow.label;
+ </label>
+ </description>
+ </vbox>
+
+ <spacer id="passphraseSpacer" flex="1" hidden="true"/>
+
+ <description id="warningText" class="data"/>
+
+ <spacer flex="1"/>
+ </wizardpage>
+</wizard>
diff --git a/comm/suite/components/sync/content/syncKey.xhtml b/comm/suite/components/sync/content/syncKey.xhtml
new file mode 100644
index 0000000000..6ce35fa1cc
--- /dev/null
+++ b/comm/suite/components/sync/content/syncKey.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html [
+ <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+ %htmlDTD;
+ <!ENTITY % syncBrandDTD SYSTEM "chrome://communicator/locale/sync/syncBrand.dtd">
+ %syncBrandDTD;
+ <!ENTITY % syncKeyDTD SYSTEM "chrome://communicator/locale/sync/syncKey.dtd">
+ %syncKeyDTD;
+]>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>&syncKey.page.title;</title>
+ <meta name="robots" content="noindex"/>
+ <style type="text/css">
+ #synckey { font-size: 1.5em; }
+ footer { font-size: 0.67em; }
+ </style>
+</head>
+
+<body>
+<h1>&syncKey.page.title;</h1>
+
+<p id="synckey">SYNCKEY</p>
+
+<p>&syncKey.page.description;</p>
+
+<div id="column1">
+ <h2>&syncKey.keepItSecret.heading;</h2>
+ <p>&syncKey.keepItSecret.description;</p>
+</div>
+
+<div id="column2">
+ <h2>&syncKey.keepItSafe.heading;</h2>
+ <p><em>&syncKey.keepItSafe1.description;</em>&syncKey.keepItSafe2.description;<em>&syncKey.keepItSafe3.description;</em>&syncKey.keepItSafe4.description;</p>
+</div>
+
+<p>&syncKey.findOutMore1.label;<a href="https://services.mozilla.com">https://services.mozilla.com</a>&syncKey.findOutMore2.label;</p>
+
+<footer>
+ &syncKey.footer1.label;<a id="tosLink" href="termsURL">termsURL</a>&syncKey.footer2.label;<a id="ppLink" href="privacyURL">privacyURL</a>&syncKey.footer3.label;
+</footer>
+
+</body>
+</html>
diff --git a/comm/suite/components/sync/content/syncNotification.xml b/comm/suite/components/sync/content/syncNotification.xml
new file mode 100644
index 0000000000..a33a06c030
--- /dev/null
+++ b/comm/suite/components/sync/content/syncNotification.xml
@@ -0,0 +1,93 @@
+<?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 bindings [
+<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
+%notificationDTD;
+]>
+
+<bindings id="notificationBindings" xmlns="http://www.mozilla.org/xbl">
+
+ <binding id="notificationbox" extends="chrome://global/content/bindings/notification.xml#notificationbox">
+ <implementation>
+ <constructor><![CDATA[
+ let localScope = {};
+ ChromeUtils.import("resource://services-common/observers.js", localScope);
+ ChromeUtils.import("resource://services-sync/notifications.js", localScope);
+
+ localScope.Observers.add("weave:notification:added", this.onNotificationAdded, this);
+ localScope.Observers.add("weave:notification:removed", this.onNotificationRemoved, this);
+ localScope.Notifications.notifications.forEach(this._appendNotification, this);
+ ]]></constructor>
+
+ <destructor><![CDATA[
+ let localScope = {};
+ ChromeUtils.import("resource://services-common/observers.js", localScope);
+ localScope.Observers.remove("weave:notification:added", this.onNotificationAdded, this);
+ localScope.Observers.remove("weave:notification:removed", this.onNotificationRemoved, this);
+ ]]></destructor>
+
+ <method name="onNotificationAdded">
+ <parameter name="subject"/>
+ <parameter name="data"/>
+ <body><![CDATA[
+ this._appendNotification(subject);
+ ]]></body>
+ </method>
+
+ <method name="onNotificationRemoved">
+ <parameter name="subject"/>
+ <parameter name="data"/>
+ <body><![CDATA[
+ // If the view of the notification hasn't been removed yet, remove it.
+ var notifications = this.allNotifications;
+ for (let notification of notifications) {
+ if (notification.notification == subject) {
+ notification.close();
+ break;
+ }
+ }
+ ]]></body>
+ </method>
+
+ <method name="_appendNotification">
+ <parameter name="notification"/>
+ <body><![CDATA[
+ var node = this.appendNotification(notification.description,
+ notification.title,
+ notification.iconURL,
+ notification.priority,
+ notification.buttons);
+ node.notification = notification;
+ ]]></body>
+ </method>
+
+ </implementation>
+ </binding>
+
+ <binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
+ <implementation>
+ <method name="close">
+ <body><![CDATA[
+ let localScope = {};
+ ChromeUtils.import("resource://services-sync/notifications.js", localScope);
+ localScope.Notifications.remove(this.notification);
+
+ // We should be able to call the base class's close method here
+ // to remove the notification element from the notification box,
+ // but we can't because of bug 373652, so instead we copied its code
+ // and execute it below.
+ var control = this.control;
+ if (control)
+ control.removeNotification(this);
+ else
+ this.hidden = true;
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/components/sync/content/syncQuota.js b/comm/suite/components/sync/content/syncQuota.js
new file mode 100644
index 0000000000..c4d2d03676
--- /dev/null
+++ b/comm/suite/components/sync/content/syncQuota.js
@@ -0,0 +1,252 @@
+/* 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 {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+
+var gSyncQuota = {
+
+ init: function init() {
+ this.bundle = document.getElementById("quotaStrings");
+ let caption = document.getElementById("treeCaption");
+ caption.textContent = this.bundle.getString("quota.treeCaption.label");
+
+ gUsageTreeView.init();
+ this.tree = document.getElementById("usageTree");
+ this.tree.view = gUsageTreeView;
+
+ this.loadData();
+ },
+
+ loadData: function loadData() {
+ this._usage_req = Weave.Service.getStorageInfo(Weave.INFO_COLLECTION_USAGE,
+ function (error, usage) {
+ delete gSyncQuota._usage_req;
+ // displayUsageData handles null values, so no need to check 'error'.
+ gUsageTreeView.displayUsageData(usage);
+ });
+
+ let usageLabel = document.getElementById("usageLabel");
+ let bundle = this.bundle;
+ this._quota_req = Weave.Service.getStorageInfo(Weave.INFO_QUOTA,
+ function (error, quota) {
+ delete gSyncQuota._quota_req;
+ if (error) {
+ usageLabel.value = bundle.getString("quota.usageError.label");
+ return;
+ }
+ let used = gSyncQuota.convertKB(quota[0]);
+ if (!quota[1]) {
+ // No quota on the server.
+ usageLabel.value = bundle.getFormattedString(
+ "quota.usageNoQuota.label", used);
+ return;
+ }
+ let percent = Math.round(100 * quota[0] / quota[1]);
+ let total = gSyncQuota.convertKB(quota[1]);
+ usageLabel.value = bundle.getFormattedString(
+ "quota.usagePercentage.label", [percent].concat(used).concat(total));
+ });
+ },
+
+ uninit: function uninit() {
+ if (this._usage_req)
+ this._usage_req.abort();
+ if (this._quota_req)
+ this._quota_req.abort();
+ },
+
+ onAccept: function onAccept() {
+ let engines = gUsageTreeView.getEnginesToDisable();
+ for (let engine of engines) {
+ Weave.Service.engineManager.get(engine).enabled = false;
+ }
+ if (engines.length) {
+ // The 'Weave' object will disappear once the window closes.
+ let Service = Weave.Service;
+ Weave.Utils.nextTick(function() { Service.sync(); });
+ }
+ return true;
+ },
+
+ convertKB: function convertKB(value) {
+ return DownloadUtils.convertByteUnits(value * 1024);
+ }
+
+};
+
+var gUsageTreeView = {
+
+ _ignored: {keys: true,
+ meta: true,
+ clients: true},
+
+ /*
+ * Internal data structures underlaying the tree.
+ */
+ _collections: [],
+ _byname: {},
+
+ init: function init() {
+ let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label");
+ for (let engine of Weave.Service.engineManager.getEnabled()) {
+ if (this._ignored[engine.name])
+ continue;
+
+ // Some engines use the same pref, which means they can only be turned on
+ // and off together. We need to combine them here as well.
+ let existing = this._byname[engine.prefName];
+ if (existing) {
+ existing.engines.push(engine.name);
+ continue;
+ }
+
+ let obj = {name: engine.prefName,
+ title: this._collectionTitle(engine),
+ engines: [engine.name],
+ enabled: true,
+ sizeLabel: retrievingLabel};
+ this._collections.push(obj);
+ this._byname[engine.prefName] = obj;
+ }
+ },
+
+ _collectionTitle: function _collectionTitle(engine) {
+ try {
+ return gSyncQuota.bundle.getString(
+ "collection." + engine.prefName + ".label");
+ } catch (ex) {
+ return engine.Name;
+ }
+ },
+
+ /*
+ * Process the quota information as returned by info/collection_usage.
+ */
+ displayUsageData: function displayUsageData(data) {
+ for (let coll of this._collections) {
+ coll.size = 0;
+ // If we couldn't retrieve any data, just blank out the label.
+ if (!data) {
+ coll.sizeLabel = "";
+ continue;
+ }
+
+ for (let engineName of coll.engines)
+ coll.size += data[engineName] || 0;
+ let sizeLabel = "";
+ sizeLabel = gSyncQuota.bundle.getFormattedString(
+ "quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size));
+ coll.sizeLabel = sizeLabel;
+ }
+ let sizeColumn = this.treeBox.columns.getNamedColumn("size");
+ this.treeBox.invalidateColumn(sizeColumn);
+ },
+
+ /*
+ * Handle click events on the tree.
+ */
+ onTreeClick: function onTreeClick(event) {
+ if (event.button == 2)
+ return;
+
+ let cell = this.treeBox.getCellAt(event.clientX, event.clientY);
+ if (cell.col && cell.col.id == "enabled")
+ this.toggle(cell.row);
+ },
+
+ /*
+ * Toggle enabled state of an engine.
+ */
+ toggle: function toggle(row) {
+ // Update the tree
+ let collection = this._collections[row];
+ collection.enabled = !collection.enabled;
+ this.treeBox.invalidateRow(row);
+
+ // Display which ones will be removed
+ let freeup = 0;
+ let toremove = [];
+ for (let collection of this._collections) {
+ if (collection.enabled)
+ continue;
+ toremove.push(collection.name);
+ freeup += collection.size;
+ }
+
+ let caption = document.getElementById("treeCaption");
+ if (!toremove.length) {
+ caption.className = "";
+ caption.textContent = gSyncQuota.bundle.getString("quota.treeCaption.label");
+ return;
+ }
+
+ toremove = toremove.map(coll => this._byname[coll].title);
+ toremove = toremove.join(gSyncQuota.bundle.getString("quota.list.separator"));
+ caption.textContent = gSyncQuota.bundle.getFormattedString(
+ "quota.removal.label", [toremove]);
+ if (freeup)
+ caption.textContent += gSyncQuota.bundle.getFormattedString(
+ "quota.freeup.label", gSyncQuota.convertKB(freeup));
+ caption.className = "captionWarning";
+ },
+
+ /*
+ * Return a list of engines (or rather their pref names) that should be
+ * disabled.
+ */
+ getEnginesToDisable: function getEnginesToDisable() {
+ return this._collections.filter(coll => !coll.enabled).map(coll => coll.name);
+ },
+
+ // nsITreeView
+
+ get rowCount() {
+ return this._collections.length;
+ },
+
+ getRowProperties: function(index) { return ""; },
+ getCellProperties: function(row, col) { return ""; },
+ getColumnProperties: function(col) { return ""; },
+ isContainer: function(index) { return false; },
+ isContainerOpen: function(index) { return false; },
+ isContainerEmpty: function(index) { return false; },
+ isSeparator: function(index) { return false; },
+ isSorted: function() { return false; },
+ canDrop: function(index, orientation, dataTransfer) { return false; },
+ drop: function(row, orientation, dataTransfer) {},
+ getParentIndex: function(rowIndex) {},
+ hasNextSibling: function(rowIndex, afterIndex) { return false; },
+ getLevel: function(index) { return 0; },
+ getImageSrc: function(row, col) {},
+
+ getCellValue: function(row, col) {
+ return this._collections[row].enabled;
+ },
+
+ getCellText: function getCellText(row, col) {
+ let collection = this._collections[row];
+ switch (col.id) {
+ case "collection":
+ return collection.title;
+ case "size":
+ return collection.sizeLabel;
+ default:
+ return "";
+ }
+ },
+
+ setTree: function setTree(tree) {
+ this.treeBox = tree;
+ },
+
+ toggleOpenState: function(index) {},
+ cycleHeader: function(col) {},
+ selectionChanged: function() {},
+ cycleCell: function(row, col) {},
+ isEditable: function(row, col) { return false; },
+ isSelectable: function (row, col) { return false; },
+ setCellValue: function(row, col, value) {},
+ setCellText: function(row, col, value) {},
+};
diff --git a/comm/suite/components/sync/content/syncQuota.xul b/comm/suite/components/sync/content/syncQuota.xul
new file mode 100644
index 0000000000..f7b1fd7aa4
--- /dev/null
+++ b/comm/suite/components/sync/content/syncQuota.xul
@@ -0,0 +1,62 @@
+<?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://communicator/skin/sync/syncQuota.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://communicator/locale/sync/syncBrand.dtd">
+<!ENTITY % syncQuotaDTD SYSTEM "chrome://communicator/locale/sync/syncQuota.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncQuotaDTD;
+]>
+<dialog id="quotaDialog"
+ windowtype="Sync:ViewQuota"
+ persist="screenX screenY width height"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="gSyncQuota.init();"
+ onunload="gSyncQuota.uninit();"
+ buttons="accept,cancel"
+ title="&quota.dialogTitle.label;"
+ ondialogaccept="return gSyncQuota.onAccept();">
+
+ <script src="chrome://communicator/content/sync/syncQuota.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="quotaStrings"
+ src="chrome://communicator/locale/sync/syncQuota.properties"/>
+ </stringbundleset>
+
+ <label id="usageLabel"
+ value="&quota.retrievingInfo.label;"/>
+ <separator/>
+ <tree id="usageTree"
+ seltype="single"
+ hidecolumnpicker="true"
+ onclick="gUsageTreeView.onTreeClick(event);"
+ flex="1">
+ <treecols>
+ <treecol id="enabled"
+ type="checkbox"
+ fixed="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="collection"
+ label="&quota.typeColumn.label;"
+ flex="1"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="size"
+ label="&quota.sizeColumn.label;"
+ flex="1"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <separator/>
+ <description id="treeCaption"/>
+
+</dialog>
diff --git a/comm/suite/components/sync/content/syncSetup.js b/comm/suite/components/sync/content/syncSetup.js
new file mode 100644
index 0000000000..c648948568
--- /dev/null
+++ b/comm/suite/components/sync/content/syncSetup.js
@@ -0,0 +1,961 @@
+/* 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/. */
+
+// page consts
+
+const INTRO_PAGE = 0;
+const NEW_ACCOUNT_START_PAGE = 1;
+const NEW_ACCOUNT_PP_PAGE = 2;
+const NEW_ACCOUNT_CAPTCHA_PAGE = 3;
+const EXISTING_ACCOUNT_CONNECT_PAGE = 4;
+const EXISTING_ACCOUNT_LOGIN_PAGE = 5;
+const OPTIONS_PAGE = 6;
+const OPTIONS_CONFIRM_PAGE = 7;
+const SETUP_SUCCESS_PAGE = 8;
+
+// Broader than we'd like, but after this changed from api-secure.recaptcha.net
+// we had no choice. At least we only do this for the duration of setup.
+// See discussion in Bugs 508112 and 653307.
+const RECAPTCHA_DOMAIN = "https://www.google.com";
+
+const {Weave} = ChromeUtils.import("resource://services-sync/main.js");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {PlacesUtils} = ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+
+var gSyncSetup = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+ Ci.nsISupportsWeakReference]),
+
+ captchaBrowser: null,
+ wizard: null,
+ _disabledSites: [],
+
+ status: {
+ password: false,
+ email: false,
+ server: true
+ },
+
+ get _remoteSites() {
+ return [Weave.Service.serverURL, RECAPTCHA_DOMAIN];
+ },
+
+ get _usingMainServers() {
+ if (this._settingUpNew)
+ return document.getElementById("server").selectedIndex == 0;
+ return document.getElementById("existingServer").selectedIndex == 0;
+ },
+
+ init: function () {
+ let obs = [
+ ["weave:service:changepassphrase", "onResetPassphrase"],
+ ["weave:service:login:start", "onLoginStart"],
+ ["weave:service:login:error", "onLoginEnd"],
+ ["weave:service:login:finish", "onLoginEnd"]];
+
+ // Add the observers now and remove them on unload
+ let self = this;
+ let addRem = function(add) {
+ obs.forEach(function([topic, func]) {
+ //XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
+ // of `this`. Fix in a followup. (bug 583347)
+ if (add)
+ Weave.Svc.Obs.add(topic, self[func], self);
+ else
+ Weave.Svc.Obs.remove(topic, self[func], self);
+ });
+ };
+ addRem(true);
+ window.addEventListener("unload", () => addRem(false));
+
+ setTimeout(function () {
+ // Force Service to be loaded so that engines are registered.
+ // See Bug 670082.
+ Weave.Service;
+ }, 0);
+
+ this.captchaBrowser = document.getElementById("captcha");
+ this.wizard = document.getElementById("accountSetup");
+
+ if (window.arguments && window.arguments[0] == true) {
+ // we're resetting sync
+ this._resettingSync = true;
+ this.wizard.pageIndex = OPTIONS_PAGE;
+ }
+ else {
+ this.wizard.canAdvance = false;
+ this.captchaBrowser.addProgressListener(this);
+ Weave.Svc.Prefs.set("firstSync", "notReady");
+ }
+
+ this.wizard.getButton("extra1").label =
+ this._stringBundle.GetStringFromName("button.syncOptions.label");
+
+ // Remember these values because the options pages change them temporarily.
+ this._nextButtonLabel = this.wizard.getButton("next").label;
+ this._nextButtonAccesskey = this.wizard.getButton("next")
+ .getAttribute("accesskey");
+ this._backButtonLabel = this.wizard.getButton("back").label;
+ this._backButtonAccesskey = this.wizard.getButton("back")
+ .getAttribute("accesskey");
+ },
+
+ startNewAccountSetup: function () {
+ if (!Weave.Utils.ensureMPUnlocked())
+ return false;
+ this._settingUpNew = true;
+ this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE;
+ },
+
+ useExistingAccount: function () {
+ if (!Weave.Utils.ensureMPUnlocked())
+ return false;
+ this._settingUpNew = false;
+ this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
+ },
+
+ resetPassphrase: function resetPassphrase() {
+ // Apply the existing form fields so that
+ // Weave.Service.changePassphrase() has the necessary credentials.
+ Weave.Service.identity.account = document.getElementById("existingAccountName").value;
+ Weave.Service.identity.basicPassword = document.getElementById("existingPassword").value;
+
+ // Generate a new passphrase so that Weave.Service.login() will
+ // actually do something.
+ let passphrase = Weave.Utils.generatePassphrase();
+ Weave.Service.identity.syncKey = passphrase;
+
+ // Only open the dialog if username + password are actually correct.
+ Weave.Service.login();
+ if (![Weave.LOGIN_FAILED_INVALID_PASSPHRASE,
+ Weave.LOGIN_FAILED_NO_PASSPHRASE,
+ Weave.LOGIN_SUCCEEDED].includes(Weave.Status.login))
+ return;
+
+ // Hide any errors about the passphrase, we know it's not right.
+ let feedback = document.getElementById("existingPassphraseFeedbackRow");
+ feedback.hidden = true;
+ let el = document.getElementById("existingPassphrase");
+ el.value = Weave.Utils.hyphenatePassphrase(passphrase);
+
+ // changePassphrase() will sync, make sure we set the "firstSync" pref
+ // according to the user's pref.
+ Weave.Svc.Prefs.reset("firstSync");
+ this.setupInitialSync();
+ gSyncUtils.resetPassphrase(true);
+ },
+
+ onResetPassphrase: function () {
+ document.getElementById("existingPassphrase").value =
+ Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey);
+ this.checkFields();
+ this.wizard.advance();
+ },
+
+ onLoginStart: function () {
+ this.toggleLoginFeedback(false);
+ },
+
+ onLoginEnd: function () {
+ this.toggleLoginFeedback(true);
+ },
+
+ toggleLoginFeedback: function (stop) {
+ document.getElementById("login-throbber").hidden = stop;
+ let password = document.getElementById("existingPasswordFeedbackRow");
+ let server = document.getElementById("existingServerFeedbackRow");
+ let passphrase = document.getElementById("existingPassphraseFeedbackRow");
+
+ if (!stop || Weave.Status.login == Weave.LOGIN_SUCCEEDED) {
+ password.hidden = server.hidden = passphrase.hidden = true;
+ return;
+ }
+
+ let feedback;
+ switch (Weave.Status.login) {
+ case Weave.LOGIN_FAILED_NETWORK_ERROR:
+ case Weave.LOGIN_FAILED_SERVER_ERROR:
+ feedback = server;
+ break;
+ case Weave.LOGIN_FAILED_LOGIN_REJECTED:
+ case Weave.LOGIN_FAILED_NO_USERNAME:
+ case Weave.LOGIN_FAILED_NO_PASSWORD:
+ feedback = password;
+ break;
+ case Weave.LOGIN_FAILED_INVALID_PASSPHRASE:
+ feedback = passphrase;
+ break;
+ }
+ this._setFeedbackMessage(feedback, false, Weave.Status.login);
+ },
+
+ setupInitialSync: function () {
+ let action = document.getElementById("mergeChoiceRadio").value;
+ switch (action) {
+ case "resetClient":
+ // if we're not resetting sync, we don't need to explicitly
+ // call resetClient
+ if (!this._resettingSync)
+ return;
+ // otherwise, fall through
+ case "wipeClient":
+ case "wipeRemote":
+ Weave.Svc.Prefs.set("firstSync", action);
+ break;
+ }
+ },
+
+ // fun with validation!
+ checkFields: function () {
+ this.wizard.canAdvance = this.readyToAdvance();
+ },
+
+ readyToAdvance: function () {
+ switch (this.wizard.pageIndex) {
+ case INTRO_PAGE:
+ return false;
+ case NEW_ACCOUNT_START_PAGE:
+ for (let i in this.status) {
+ if (!this.status[i])
+ return false;
+ }
+ if (this._usingMainServers)
+ return document.getElementById("tos").checked;
+
+ return true;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ let hasUser = document.getElementById("existingAccountName").value != "";
+ let hasPass = document.getElementById("existingPassword").value != "";
+ let hasKey = document.getElementById("existingPassphrase").value != "";
+ if (hasUser && hasPass && hasKey) {
+ if (this._usingMainServers)
+ return true;
+
+ if (this._validateServer(document.getElementById("existingServer"), false))
+ return true;
+ }
+ return false;
+ }
+ // Default, e.g. wizard's special page -1 etc.
+ return true;
+ },
+
+ onEmailInput: function () {
+ // Check account validity when the user stops typing for 1 second.
+ if (this._checkAccountTimer)
+ window.clearTimeout(this._checkAccountTimer);
+ this._checkAccountTimer = window.setTimeout(function () {
+ gSyncSetup.checkAccount();
+ }, 1000);
+ },
+
+ checkAccount: function() {
+ delete this._checkAccountTimer;
+ let value = Weave.Utils.normalizeAccount(
+ document.getElementById("weaveEmail").value);
+ if (!value) {
+ this.status.email = false;
+ this.checkFields();
+ return;
+ }
+
+ let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ let feedback = document.getElementById("emailFeedbackRow");
+ let valid = re.test(value);
+
+ let str = "";
+ if (!valid) {
+ str = "invalidEmail.label";
+ } else {
+ let availCheck = Weave.Service.checkAccount(value);
+ valid = availCheck == "available";
+ if (!valid) {
+ if (availCheck == "notAvailable")
+ str = "usernameNotAvailable.label";
+ else
+ str = availCheck;
+ }
+ }
+
+ this._setFeedbackMessage(feedback, valid, str);
+ this.status.email = valid;
+ if (valid)
+ Weave.Service.identity.account = value;
+ this.checkFields();
+ },
+
+ onPasswordChange: function () {
+ let password = document.getElementById("weavePassword");
+ let valid, str;
+ if (password.value == document.getElementById("weavePassphrase").value) {
+ // xxxmpc - hack, sigh
+ valid = false;
+ str = Weave.Utils.getErrorString("change.password.pwSameAsRecoveryKey");
+ }
+ else {
+ let pwconfirm = document.getElementById("weavePasswordConfirm");
+ [valid, str] = gSyncUtils.validatePassword(password, pwconfirm);
+ }
+
+ let feedback = document.getElementById("passwordFeedbackRow");
+ this._setFeedback(feedback, valid, str);
+
+ this.status.password = valid;
+ this.checkFields();
+ },
+
+ onPassphraseGenerate: function () {
+ let passphrase = Weave.Utils.generatePassphrase();
+ Weave.Service.identity.syncKey = passphrase;
+ let el = document.getElementById("weavePassphrase");
+ el.value = Weave.Utils.hyphenatePassphrase(passphrase);
+ },
+
+ onPageShow: function() {
+ switch (this.wizard.pageIndex) {
+ case INTRO_PAGE:
+ this.wizard.getButton("next").hidden = true;
+ this.wizard.getButton("back").hidden = true;
+ this.wizard.getButton("extra1").hidden = true;
+ break;
+ case NEW_ACCOUNT_PP_PAGE:
+ document.getElementById("saveSyncKeyButton").focus();
+ let el = document.getElementById("weavePassphrase");
+ if (!el.value)
+ this.onPassphraseGenerate();
+ this.checkFields();
+ break;
+ case NEW_ACCOUNT_START_PAGE:
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = false;
+ this.wizard.canRewind = true;
+ this.checkFields();
+ break;
+ case EXISTING_ACCOUNT_CONNECT_PAGE:
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = false;
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.canAdvance = false;
+ this.wizard.canRewind = true;
+ this.startEasySetup();
+ break;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ this.wizard.canRewind = true;
+ this.checkFields();
+ break;
+ case SETUP_SUCCESS_PAGE:
+ this.wizard.canRewind = false;
+ this.wizard.canAdvance = true;
+ this.wizard.getButton("back").hidden = true;
+ this.wizard.getButton("next").hidden = true;
+ this.wizard.getButton("cancel").hidden = true;
+ this.wizard.getButton("finish").hidden = false;
+ this._handleSuccess();
+ break;
+ case OPTIONS_PAGE:
+ this.wizard.canRewind = false;
+ this.wizard.canAdvance = true;
+ if (!this._resettingSync) {
+ this.wizard.getButton("next").label =
+ this._stringBundle.GetStringFromName("button.syncOptionsDone.label");
+ this.wizard.getButton("next").removeAttribute("accesskey");
+ }
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("back").hidden = true;
+ this.wizard.getButton("cancel").hidden = !this._resettingSync;
+ this.wizard.getButton("extra1").hidden = true;
+ document.getElementById("syncComputerName").value = Weave.Service.clientsEngine.localName;
+ document.getElementById("syncOptions").collapsed = this._resettingSync;
+ document.getElementById("mergeOptions").collapsed = this._settingUpNew;
+ break;
+ case OPTIONS_CONFIRM_PAGE:
+ this.wizard.canRewind = true;
+ this.wizard.canAdvance = true;
+ this.wizard.getButton("back").label =
+ this._stringBundle.GetStringFromName("button.syncOptionsCancel.label");
+ this.wizard.getButton("back").removeAttribute("accesskey");
+ this.wizard.getButton("back").hidden = this._resettingSync;
+ this.wizard.getButton("next").hidden = false;
+ this.wizard.getButton("finish").hidden = true;
+ break;
+ }
+ },
+
+ onWizardAdvance: function () {
+ // Check pageIndex so we don't prompt before the Sync setup wizard appears.
+ // This is a fallback in case the Master Password gets locked mid-wizard.
+ if (this.wizard.pageIndex >= 0 && !Weave.Utils.ensureMPUnlocked())
+ return false;
+
+ if (!this.wizard.pageIndex)
+ return true;
+
+ switch (this.wizard.pageIndex) {
+ case NEW_ACCOUNT_START_PAGE:
+ // If the user selects Next (e.g. by hitting enter) when we haven't
+ // executed the delayed checks yet, execute them immediately.
+ if (this._checkAccountTimer)
+ this.checkAccount();
+ if (this._checkServerTimer)
+ this.checkServer();
+ return this.wizard.canAdvance;
+ case NEW_ACCOUNT_CAPTCHA_PAGE:
+ let doc = this.captchaBrowser.contentDocument;
+ let getField = function getField(field) {
+ let node = doc.getElementById("recaptcha_" + field + "_field");
+ return node && node.value;
+ };
+
+ // Display throbber
+ let feedback = document.getElementById("captchaFeedback");
+ let image = feedback.firstChild;
+ let label = image.nextSibling;
+ image.setAttribute("status", "active");
+ label.value = this._stringBundle.GetStringFromName("verifying.label");
+ feedback.hidden = false;
+
+ let password = document.getElementById("weavePassword").value;
+ let email = Weave.Utils.normalizeAccount(
+ document.getElementById("weaveEmail").value);
+ let challenge = getField("challenge");
+ let response = getField("response");
+
+ let error = Weave.Service.createAccount(email, password,
+ challenge, response);
+
+ if (error == null) {
+ Weave.Service.identity.account = email;
+ Weave.Service.identity.basicPassword = password;
+ this._handleNoScript(false);
+ this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
+ return false;
+ }
+
+ image.setAttribute("status", "error");
+ label.value = Weave.Utils.getErrorString(error);
+ return false;
+ case NEW_ACCOUNT_PP_PAGE:
+ // Time to load the captcha.
+ // First check for NoScript and whitelist the right sites.
+ this._handleNoScript(true);
+ this.captchaBrowser.loadURI(Weave.Service.miscAPI + "captcha_html");
+ break;
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ Weave.Service.identity.account = Weave.Utils.normalizeAccount(
+ document.getElementById("existingAccountName").value);
+ Weave.Service.identity.basicPassword = document.getElementById("existingPassword").value;
+ let pp = document.getElementById("existingPassphrase").value;
+ Weave.Service.identity.syncKey = Weave.Utils.normalizePassphrase(pp);
+ if (Weave.Service.login())
+ this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
+ return false;
+ case OPTIONS_PAGE:
+ let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
+ // No confirmation needed on new account setup or merge option
+ // with existing account.
+ if (this._settingUpNew || (!this._resettingSync && desc == 0))
+ return this.returnFromOptions();
+ return this._handleChoice();
+ case OPTIONS_CONFIRM_PAGE:
+ if (this._resettingSync) {
+ this.onWizardFinish();
+ window.close();
+ return false;
+ }
+ return this.returnFromOptions();
+ }
+ return true;
+ },
+
+ onWizardBack: function () {
+ switch (this.wizard.pageIndex) {
+ case NEW_ACCOUNT_START_PAGE:
+ case EXISTING_ACCOUNT_LOGIN_PAGE:
+ this.wizard.pageIndex = INTRO_PAGE;
+ return false;
+ case EXISTING_ACCOUNT_CONNECT_PAGE:
+ this.abortEasySetup();
+ this.wizard.pageIndex = INTRO_PAGE;
+ return false;
+ case OPTIONS_CONFIRM_PAGE:
+ // Backing up from the confirmation page = resetting first sync to merge.
+ document.getElementById("mergeChoiceRadio").selectedIndex = 0;
+ return this.returnFromOptions();
+ }
+ return true;
+ },
+
+ onWizardFinish: function () {
+ this.setupInitialSync();
+
+ if (!this._resettingSync) {
+ function isChecked(element) {
+ return document.getElementById(element).hasAttribute("checked");
+ }
+
+ let prefs = ["engine.bookmarks", "engine.passwords", "engine.history",
+ "engine.tabs", "engine.prefs", "engine.addons"];
+ for (let i = 0; i < prefs.length; i++) {
+ Weave.Svc.Prefs.set(prefs[i], isChecked(prefs[i]));
+ }
+ this._handleNoScript(false);
+ if (Weave.Svc.Prefs.get("firstSync", "") == "notReady")
+ Weave.Svc.Prefs.reset("firstSync");
+
+ Weave.Service.persistLogin();
+ Weave.Svc.Obs.notify("weave:service:setup-complete");
+ if (this._settingUpNew)
+ gSyncUtils.openFirstClientFirstrun();
+ else
+ gSyncUtils.openAddedClientFirstrun();
+ }
+
+ if (!Weave.Service.isLoggedIn)
+ Weave.Service.login();
+
+ Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
+ },
+
+ onWizardCancel: function () {
+ if (this._resettingSync)
+ return;
+
+ if (this.wizard.pageIndex == SETUP_SUCCESS_PAGE) {
+ this.onWizardFinish();
+ return;
+ }
+ this.abortEasySetup();
+ this._handleNoScript(false);
+ Weave.Service.startOver();
+ },
+
+ onSyncOptions: function () {
+ this._beforeOptionsPage = this.wizard.pageIndex;
+ this.wizard.pageIndex = OPTIONS_PAGE;
+ },
+
+ returnFromOptions: function() {
+ this.wizard.getButton("next").label = this._nextButtonLabel;
+ this.wizard.getButton("next").setAttribute("accesskey",
+ this._nextButtonAccesskey);
+ this.wizard.getButton("back").label = this._backButtonLabel;
+ this.wizard.getButton("back").setAttribute("accesskey",
+ this._backButtonAccesskey);
+ this.wizard.getButton("cancel").hidden = false;
+ this.wizard.getButton("extra1").hidden = false;
+ this.wizard.pageIndex = this._beforeOptionsPage;
+ return false;
+ },
+
+ startEasySetup: function () {
+ // Don't do anything if we have a client already (e.g. we went to
+ // Sync Options and just came back).
+ if (this._jpakeclient)
+ return;
+
+ // When onAbort is called, Weave may already be gone.
+ const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
+
+ let self = this;
+ this._jpakeclient = new Weave.JPAKEClient({
+ displayPIN: function displayPIN(pin) {
+ document.getElementById("easySetupPIN1").value = pin.slice(0, 4);
+ document.getElementById("easySetupPIN2").value = pin.slice(4, 8);
+ document.getElementById("easySetupPIN3").value = pin.slice(8);
+ },
+
+ onPairingStart: function onPairingStart() {},
+
+ onPaired: function onPaired() {},
+
+ onComplete: function onComplete(credentials) {
+ Weave.Service.identity.account = credentials.account;
+ Weave.Service.identity.basicPassword = credentials.password;
+ Weave.Service.identity.syncKey = credentials.synckey;
+ Weave.Service.serverURL = credentials.serverURL;
+ self.wizard.pageIndex = SETUP_SUCCESS_PAGE;
+ },
+
+ onAbort: function onAbort(error) {
+ delete self._jpakeclient;
+
+ // Ignore if wizard is aborted.
+ if (error == JPAKE_ERROR_USERABORT)
+ return;
+
+ // Automatically go to manual setup if we couldn't acquire a channel.
+ if (error == Weave.JPAKE_ERROR_CHANNEL) {
+ self.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+ return;
+ }
+
+ // Restart on all other errors.
+ self.startEasySetup();
+ }
+ });
+ this._jpakeclient.receiveNoPIN();
+ },
+
+ abortEasySetup: function () {
+ document.getElementById("easySetupPIN1").value = "";
+ document.getElementById("easySetupPIN2").value = "";
+ document.getElementById("easySetupPIN3").value = "";
+ if (!this._jpakeclient)
+ return;
+
+ this._jpakeclient.abort();
+ delete this._jpakeclient;
+ },
+
+ manualSetup: function () {
+ this.abortEasySetup();
+ this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
+ },
+
+ // _handleNoScript is needed because it blocks the captcha. So we temporarily
+ // allow the necessary sites so that we can verify the user is in fact a human.
+ // This was done with the help of Giorgio (NoScript author). See bug 508112.
+ _handleNoScript: function (addExceptions) {
+ // if NoScript isn't installed, or is disabled, bail out.
+ if (!("@maone.net/noscript-service;1" in Cc))
+ return;
+
+ let ns = Cc["@maone.net/noscript-service;1"].getService().wrappedJSObject;
+ if (addExceptions) {
+ this._remoteSites.forEach(function(site) {
+ site = ns.getSite(site);
+ if (!ns.isJSEnabled(site)) {
+ this._disabledSites.push(site); // save status
+ ns.setJSEnabled(site, true); // allow site
+ }
+ }, this);
+ }
+ else {
+ this._disabledSites.forEach(function(site) {
+ ns.setJSEnabled(site, false);
+ });
+ this._disabledSites = [];
+ }
+ },
+
+ _updateControl: function(controlId) {
+ let control = document.getElementById(controlId);
+ if (control.selectedIndex == 0) {
+ control.editable = false;
+ Weave.Svc.Prefs.reset("serverURL");
+ } else {
+ // Prevent double selection upon using down key.
+ control.activeChild = null;
+ control.editable = true;
+ control.value = "";
+ }
+ // Force a style flush to ensure that the binding is attached.
+ control.clientTop;
+ control.focus();
+ return control;
+ },
+
+ onExistingServerCommand: function () {
+ this._updateControl("existingServer");
+ document.getElementById("existingServerFeedbackRow").hidden = true;
+ this.checkFields();
+ },
+
+ onExistingServerInput: function () {
+ // Check custom server validity when the user stops typing for 1 second.
+ if (this._existingServerTimer)
+ window.clearTimeout(this._existingServerTimer);
+ this._existingServerTimer = window.setTimeout(function () {
+ gSyncSetup.checkFields();
+ }, 1000);
+ },
+
+ onServerCommand: function () {
+ document.getElementById("TOSRow").hidden = !this._usingMainServers;
+ let control = this._updateControl("server");
+ if (control.selectedIndex != 0) {
+ // checkServer() will call checkAccount() and checkFields().
+ this.checkServer();
+ return;
+ }
+ this.checkAccount();
+ this.status.server = true;
+ document.getElementById("serverFeedbackRow").hidden = true;
+ this.checkFields();
+ },
+
+ onServerInput: function () {
+ // Check custom server validity when the user stops typing for 1 second.
+ if (this._checkServerTimer)
+ window.clearTimeout(this._checkServerTimer);
+ this._checkServerTimer = window.setTimeout(function () {
+ gSyncSetup.checkServer();
+ }, 1000);
+ },
+
+ checkServer: function () {
+ delete this._checkServerTimer;
+ let el = document.getElementById("server");
+ let valid = false;
+ let feedback = document.getElementById("serverFeedbackRow");
+
+ let str = "";
+ if (el.value) {
+ valid = this._validateServer(el, true);
+ let str = valid ? "" : "serverInvalid.label";
+ this._setFeedbackMessage(feedback, valid, str);
+ }
+ else {
+ this._setFeedbackMessage(feedback, true);
+ }
+
+ // Recheck account against the new server.
+ if (valid)
+ this.checkAccount();
+
+ this.status.server = valid;
+ this.checkFields();
+ },
+
+ // xxxmpc - checkRemote is a hack, we can't verify a minimal server is live
+ // without auth, so we won't validate in the existing-server case.
+ _validateServer: function (element, checkRemote) {
+ let valid = false;
+ let val = element.value;
+ if (!val)
+ return false;
+
+ let uri = Weave.Utils.makeURI(val);
+
+ if (!uri)
+ uri = Weave.Utils.makeURI("https://" + val);
+
+ if (uri && checkRemote) {
+ function isValid(uri) {
+ Weave.Service.serverURL = uri.spec;
+ let check = Weave.Service.checkAccount("a");
+ return (check == "available" || check == "notAvailable");
+ }
+
+ if (uri.schemeIs("http")) {
+ uri.scheme = "https";
+ if (isValid(uri))
+ valid = true;
+ else
+ // setting the scheme back to http
+ uri.scheme = "http";
+ }
+ if (!valid)
+ valid = isValid(uri);
+ }
+ else if (uri) {
+ valid = true;
+ Weave.Service.serverURL = uri.spec;
+ }
+
+ if (valid)
+ element.value = Weave.Service.serverURL;
+ else
+ Weave.Svc.Prefs.reset("serverURL");
+
+ return valid;
+ },
+
+ _handleSuccess: function() {
+ let self = this;
+ function fill(id, string) {
+ document.getElementById(id).textContent =
+ string ? self._stringBundle.GetStringFromName(string) : "";
+ }
+
+ fill("firstSyncAction", "");
+ fill("firstSyncActionWarning", "");
+ if (this._settingUpNew) {
+ fill("firstSyncAction", "newAccount.action.label");
+ fill("firstSyncActionChange", "newAccount.change.label");
+ return;
+ }
+ fill("firstSyncActionChange", "existingAccount.change.label");
+ let action = document.getElementById("mergeChoiceRadio").value;
+ let id = action == "resetClient" ? "firstSyncAction" : "firstSyncActionWarning";
+ fill(id, action + ".change.label");
+ },
+
+ _handleChoice: function () {
+ let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
+ document.getElementById("chosenActionDeck").selectedIndex = desc;
+ switch (desc) {
+ case 1:
+ if (this._case1Setup)
+ break;
+
+ let places_db = PlacesUtils.history
+ .QueryInterface(Ci.nsPIPlacesDatabase)
+ .DBConnection;
+ if (Weave.Service.engineManager.get("history").enabled) {
+ let daysOfHistory = 0;
+ let stm = places_db.createStatement(
+ "SELECT ROUND(( " +
+ "strftime('%s','now','localtime','utc') - " +
+ "( " +
+ "SELECT visit_date FROM moz_historyvisits " +
+ "ORDER BY visit_date ASC LIMIT 1 " +
+ ")/1000000 " +
+ ")/86400) AS daysOfHistory ");
+
+ if (stm.step())
+ daysOfHistory = stm.getInt32(0);
+ // Support %S for historical reasons (see bug 600141)
+ document.getElementById("historyCount").value =
+ PluralForm.get(daysOfHistory,
+ this._stringBundle.GetStringFromName("historyDaysCount.label"))
+ .replace("%S", daysOfHistory)
+ .replace("#1", daysOfHistory);
+ } else {
+ document.getElementById("historyCount").hidden = true;
+ }
+
+ if (Weave.Service.engineManager.get("bookmarks").enabled) {
+ let bookmarks = 0;
+ let stm = places_db.createStatement(
+ "SELECT count(*) AS bookmarks " +
+ "FROM moz_bookmarks b " +
+ "LEFT JOIN moz_bookmarks t ON " +
+ "b.parent = t.id WHERE b.type = 1 AND t.parent <> :tag");
+ stm.params.tag = PlacesUtils.tagsFolderId;
+ if (stm.executeStep())
+ bookmarks = stm.row.bookmarks;
+ // Support %S for historical reasons (see bug 600141)
+ document.getElementById("bookmarkCount").value =
+ PluralForm.get(bookmarks,
+ this._stringBundle.GetStringFromName("bookmarksCount.label"))
+ .replace("%S", bookmarks)
+ .replace("#1", bookmarks);
+ } else {
+ document.getElementById("bookmarkCount").hidden = true;
+ }
+
+ if (Weave.Service.engineManager.get("passwords").enabled) {
+ let logins = Services.logins.getAllLogins({});
+ // Support %S for historical reasons (see bug 600141)
+ document.getElementById("passwordCount").value =
+ PluralForm.get(logins.length,
+ this._stringBundle.GetStringFromName("passwordsCount.label"))
+ .replace("%S", logins.length)
+ .replace("#1", logins.length);
+ } else {
+ document.getElementById("passwordCount").hidden = true;
+ }
+
+ let addonsEngine = Weave.Service.engineManager.get("addons");
+ if (addonsEngine.enabled) {
+ let ids = addonsEngine._store.getAllIDs();
+ let blessedcount = Object.keys(ids).filter(id => ids[id]).length;
+ // Bug 600141 does not apply as this does not have to support existing strings.
+ document.getElementById("addonCount").value =
+ PluralForm.get(blessedcount,
+ this._stringBundle.GetStringFromName("addonsCount.label"))
+ .replace("#1", blessedcount);
+ } else {
+ document.getElementById("addonCount").hidden = true;
+ }
+
+ if (!Weave.Service.engineManager.get("prefs").enabled) {
+ document.getElementById("prefsWipe").hidden = true;
+ }
+
+ this._case1Setup = true;
+ break;
+ case 2:
+ if (this._case2Setup)
+ break;
+ let count = 0;
+ function appendNode(label) {
+ let box = document.getElementById("clientList");
+ let node = document.createElement("label");
+ node.setAttribute("value", label);
+ node.setAttribute("class", "data indent");
+ box.appendChild(node);
+ }
+
+ for (let name of Weave.Service.clientsEngine.stats.names) {
+ // Don't list the current client
+ if (name == Weave.Service.clientsEngine.localName)
+ continue;
+
+ // Only show the first several client names
+ if (++count <= 5)
+ appendNode(name);
+ }
+ if (count > 5) {
+ // Support %S for historical reasons (see bug 600141)
+ let label =
+ PluralForm.get(count - 5,
+ this._stringBundle.GetStringFromName("additionalClientCount.label"))
+ .replace("%S", count - 5)
+ .replace("#1", count - 5);
+ appendNode(label);
+ }
+ this._case2Setup = true;
+ break;
+ }
+
+ return true;
+ },
+
+ // sets class and string on a feedback element
+ // if no property string is passed in, we clear label/style
+ _setFeedback: function (element, success, string) {
+ element.hidden = success || !string;
+ let classname = success ? "success" : "error";
+ let image = element.getElementsByAttribute("class", "statusIcon")[0];
+ image.setAttribute("status", classname);
+ let label = element.getElementsByAttribute("class", "status")[0];
+ label.value = string;
+ },
+
+ // shim
+ _setFeedbackMessage: function (element, success, string) {
+ let str = "";
+ if (string) {
+ try {
+ str = this._stringBundle.GetStringFromName(string);
+ } catch(e) {}
+
+ if (!str)
+ str = Weave.Utils.getErrorString(string);
+ }
+ this._setFeedback(element, success, str);
+ },
+
+ onStateChange: function(webProgress, request, stateFlags, status) {
+ // We're only looking for the end of the frame load
+ if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) == 0)
+ return;
+ if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) == 0)
+ return;
+ if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) == 0)
+ return;
+
+ // If we didn't find the captcha, assume it's not needed and move on
+ if (request.QueryInterface(Ci.nsIHttpChannel).responseStatus == 404)
+ this.onWizardAdvance();
+ },
+ onProgressChange: function() {},
+ onStatusChange: function() {},
+ onSecurityChange: function() {},
+ onLocationChange: function () {}
+};
+
+// onWizardAdvance() and onPageShow() are run before init(), so we'll set
+// wizard & _stringBundle up as lazy getters.
+XPCOMUtils.defineLazyGetter(gSyncSetup, "wizard", function() {
+ return document.getElementById("accountSetup");
+});
+XPCOMUtils.defineLazyGetter(gSyncSetup, "_stringBundle", function() {
+ return Services.strings.createBundle("chrome://communicator/locale/sync/syncSetup.properties");
+});
diff --git a/comm/suite/components/sync/content/syncSetup.xul b/comm/suite/components/sync/content/syncSetup.xul
new file mode 100644
index 0000000000..2df682bb82
--- /dev/null
+++ b/comm/suite/components/sync/content/syncSetup.xul
@@ -0,0 +1,482 @@
+<?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://communicator/skin/sync/syncSetup.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/sync/syncCommon.css" type="text/css"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+<!ENTITY % syncBrandDTD SYSTEM "chrome://communicator/locale/sync/syncBrand.dtd">
+<!ENTITY % syncSetupDTD SYSTEM "chrome://communicator/locale/sync/syncSetup.dtd">
+%brandDTD;
+%syncBrandDTD;
+%syncSetupDTD;
+]>
+<wizard id="accountSetup" title="&accountSetupTitle.label;"
+ windowtype="Weave:AccountSetup"
+ persist="screenX screenY"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onwizardnext="return gSyncSetup.onWizardAdvance();"
+ onwizardback="return gSyncSetup.onWizardBack();"
+ onwizardfinish="gSyncSetup.onWizardFinish();"
+ onwizardcancel="gSyncSetup.onWizardCancel();"
+ onload="gSyncSetup.init();">
+
+ <script src="chrome://communicator/content/sync/syncSetup.js"/>
+ <script src="chrome://communicator/content/sync/syncUtils.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+
+ <wizardpage id="pickSetupType"
+ label="&syncBrand.fullName.label;"
+ onpageshow="gSyncSetup.onPageShow();">
+ <vbox align="center" flex="1">
+ <description id="pickSetupDesc">
+ &setup.pickSetupType.description;
+ </description>
+ <spacer flex="1"/>
+ <button id="newAccount"
+ class="accountChoiceButton"
+ label="&button.createNewAccount.label;"
+ oncommand="gSyncSetup.startNewAccountSetup();"
+ align="center"/>
+ <spacer flex="3"/>
+ </vbox>
+ <separator class="groove"/>
+ <vbox align="center" flex="1">
+ <spacer flex="3"/>
+ <label value="&setup.haveAccount.label;"/>
+ <spacer flex="1"/>
+ <button id="existingAccount"
+ class="accountChoiceButton"
+ label="&button.connect.label;"
+ oncommand="gSyncSetup.useExistingAccount();"/>
+ <spacer flex="3"/>
+ </vbox>
+ </wizardpage>
+
+ <wizardpage id="newAccountStart"
+ label="&setup.newAccountDetailsPage.title.label;"
+ onextra1="gSyncSetup.onSyncOptions();"
+ onpageshow="gSyncSetup.onPageShow();">
+ <grid>
+ <columns>
+ <column/>
+ <column class="inputColumn" flex="1"/>
+ </columns>
+ <rows>
+ <row id="emailRow" align="center">
+ <label value="&setup.emailAddress.label;"
+ accesskey="&setup.emailAddress.accesskey;"
+ control="weaveEmail"/>
+ <textbox id="weaveEmail"
+ oninput="gSyncSetup.onEmailInput();"/>
+ </row>
+ <row id="emailFeedbackRow" align="center" hidden="true">
+ <spacer/>
+ <hbox>
+ <image class="statusIcon"/>
+ <label class="status" value=" "/>
+ </hbox>
+ </row>
+ <row id="passwordRow" align="center">
+ <label value="&signIn.password.label;"
+ accesskey="&signIn.password.accesskey;"
+ control="weavePassword"/>
+ <textbox id="weavePassword"
+ type="password"
+ onchange="gSyncSetup.onPasswordChange();"/>
+ </row>
+ <row id="confirmRow" align="center">
+ <label value="&setup.confirmPassword.label;"
+ accesskey="&setup.confirmPassword.accesskey;"
+ control="weavePasswordConfirm"/>
+ <textbox id="weavePasswordConfirm"
+ type="password"
+ onchange="gSyncSetup.onPasswordChange();"/>
+ </row>
+ <row id="passwordFeedbackRow" align="center" hidden="true">
+ <spacer/>
+ <hbox>
+ <image class="statusIcon"/>
+ <label class="status" value=" "/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="server"
+ accesskey="&server.accesskey;"
+ value="&server.label;"/>
+ <menulist id="server"
+ oncommand="gSyncSetup.onServerCommand();"
+ oninput="gSyncSetup.onServerInput();">
+ <menupopup>
+ <menuitem label="&serverType.main.label;"/>
+ <menuitem label="&serverType.custom2.label;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row id="serverFeedbackRow" align="center" hidden="true">
+ <spacer/>
+ <hbox>
+ <image class="statusIcon"/>
+ <label class="status" value=" "/>
+ </hbox>
+ </row>
+ <row id="TOSRow" align="center">
+ <spacer/>
+ <hbox align="center">
+ <checkbox id="tos"
+ accesskey="&setup.tosAgree1.accesskey;"
+ oncommand="this.focus(); gSyncSetup.checkFields();"/>
+ <description id="tosDesc"
+ flex="1"
+ onclick="document.getElementById('tos').focus();
+ document.getElementById('tos').click();">
+ &setup.tosAgree1.label;
+ <label class="text-link inline-link"
+ onclick="event.stopPropagation(); gSyncUtils.openToS();">
+ &setup.tosLink.label;
+ </label>
+ &setup.tosAgree2.label;
+ <label class="text-link inline-link"
+ onclick="event.stopPropagation();
+ gSyncUtils.openPrivacyPolicy();">
+ &setup.ppLink.label;
+ </label>
+ &setup.tosAgree3.label;
+ </description>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </wizardpage>
+
+ <wizardpage id="newSyncKey"
+ label="&setup.newRecoveryKeyPage.title.label;"
+ onextra1="gSyncSetup.onSyncOptions();"
+ onpageshow="gSyncSetup.onPageShow();">
+ <description>
+ &setup.newRecoveryKeyPage.description.label;
+ </description>
+ <spacer/>
+
+ <groupbox>
+ <label value="&recoveryKeyEntry.label;"
+ accesskey="&recoveryKeyEntry.accesskey;"
+ control="weavePassphrase"/>
+ <textbox id="weavePassphrase"
+ readonly="true"
+ onfocus="this.select();"/>
+ </groupbox>
+
+ <groupbox align="center">
+ <description>&recoveryKeyBackup.description;</description>
+ <hbox>
+ <button id="printSyncKeyButton"
+ label="&button.syncKeyBackup.print.label;"
+ accesskey="&button.syncKeyBackup.print.accesskey;"
+ oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
+ <button id="saveSyncKeyButton"
+ label="&button.syncKeyBackup.save.label;"
+ accesskey="&button.syncKeyBackup.save.accesskey;"
+ oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
+ </hbox>
+ </groupbox>
+ </wizardpage>
+
+ <wizardpage id="captchaEntry"
+ label="&setup.captchaPage2.title.label;"
+ onextra1="gSyncSetup.onSyncOptions();">
+ <vbox flex="1" align="center">
+ <browser height="150"
+ width="450"
+ id="captcha"
+ type="content"
+ disablehistory="true"/>
+ <spacer flex="1"/>
+ <hbox id="captchaFeedback" hidden="true">
+ <image class="statusIcon"/>
+ <label class="status" value=" "/>
+ </hbox>
+ <spacer flex="3"/>
+ </vbox>
+ </wizardpage>
+
+ <wizardpage id="addDevice"
+ label="&addDevice.title.label;"
+ onextra1="gSyncSetup.onSyncOptions();"
+ onpageshow="gSyncSetup.onPageShow();">
+ <description>
+ &addDevice.setup.description.label;
+ <label class="text-link"
+ value="&addDevice.showMeHow.label;"
+ href="https://services.mozilla.com/sync/help/easy-setup"/>
+ </description>
+ <description>&addDevice.setup.enterCode.label;</description>
+ <spacer flex="1"/>
+ <vbox align="center" flex="1">
+ <textbox id="easySetupPIN1"
+ class="pin"
+ value=""
+ size="4"
+ disabled="true"/>
+ <textbox id="easySetupPIN2"
+ class="pin"
+ value=""
+ size="4"
+ disabled="true"/>
+ <textbox id="easySetupPIN3"
+ class="pin"
+ value=""
+ size="4"
+ disabled="true"/>
+ </vbox>
+ <spacer flex="3"/>
+ <label class="text-link"
+ value="&addDevice.dontHaveDevice.label;"
+ onclick="gSyncSetup.manualSetup();"/>
+ </wizardpage>
+
+ <wizardpage id="existingAccount"
+ label="&setup.signInPage.title.label;"
+ onextra1="gSyncSetup.onSyncOptions();"
+ onpageshow="gSyncSetup.onPageShow();">
+ <grid>
+ <columns>
+ <column/>
+ <column class="inputColumn" flex="1"/>
+ </columns>
+ <rows>
+ <row id="existingAccountRow" align="center">
+ <label id="existingAccountLabel"
+ value="&signIn.account2.label;"
+ accesskey="&signIn.account2.accesskey;"
+ control="existingAccountName"/>
+ <textbox id="existingAccountName"
+ oninput="gSyncSetup.checkFields(event);"
+ onchange="gSyncSetup.checkFields(event);"/>
+ </row>
+ <row id="existingPasswordRow" align="center">
+ <label id="existingPasswordLabel"
+ value="&signIn.password.label;"
+ accesskey="&signIn.password.accesskey;"
+ control="existingPassword"/>
+ <textbox id="existingPassword"
+ type="password"
+ onkeyup="gSyncSetup.checkFields(event);"
+ onchange="gSyncSetup.checkFields(event);"/>
+ </row>
+ <row id="existingPasswordFeedbackRow" align="center" hidden="true">
+ <spacer/>
+ <hbox>
+ <image class="statusIcon"/>
+ <label class="status" value=" "/>
+ </hbox>
+ </row>
+ <row align="center">
+ <spacer/>
+ <label class="text-link"
+ value="&resetPassword.label;"
+ onclick="gSyncUtils.resetPassword(); return false;"/>
+ </row>
+ <row align="center">
+ <label control="existingServer"
+ accesskey="&server.accesskey;"
+ value="&server.label;"/>
+ <menulist id="existingServer"
+ oncommand="gSyncSetup.onExistingServerCommand();"
+ oninput="gSyncSetup.onExistingServerInput();">
+ <menupopup>
+ <menuitem label="&serverType.main.label;"/>
+ <menuitem label="&serverType.custom2.label;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row id="existingServerFeedbackRow" align="center" hidden="true">
+ <spacer/>
+ <hbox>
+ <image class="statusIcon"/>
+ <vbox>
+ <label class="status" value=" "/>
+ </vbox>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+
+ <groupbox>
+ <label id="existingPassphraseLabel"
+ value="&signIn.recoveryKey.label;"
+ accesskey="&signIn.recoveryKey.accesskey;"
+ control="existingPassphrase"/>
+ <textbox id="existingPassphrase"
+ oninput="gSyncSetup.checkFields();"/>
+ <hbox id="login-throbber" hidden="true">
+ <image/>
+ <label value="&verifying.label;"/>
+ </hbox>
+ <vbox align="left" id="existingPassphraseFeedbackRow" hidden="true">
+ <hbox>
+ <image class="statusIcon"/>
+ <label class="status" value=" "/>
+ </hbox>
+ </vbox>
+ </groupbox>
+ <vbox id="passphraseHelpBox">
+ <description>
+ &existingRecoveryKey.description;
+ <label class="text-link"
+ href="https://services.mozilla.com/sync/help/manual-setup">
+ &addDevice.showMeHow.label;
+ </label>
+ <spacer id="passphraseHelpSpacer"/>
+ <label class="text-link"
+ onclick="gSyncSetup.resetPassphrase(); return false;">
+ &resetSyncKey.label;
+ </label>
+ </description>
+ </vbox>
+ </wizardpage>
+
+ <wizardpage id="syncOptionsPage"
+ label="&setup.optionsPage.title;"
+ onpageshow="gSyncSetup.onPageShow();">
+ <groupbox id="syncOptions">
+ <grid>
+ <columns>
+ <column/>
+ <column class="inputColumn" flex="1"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label value="&syncComputerName.label;"
+ accesskey="&syncComputerName.accesskey;"
+ control="syncComputerName"/>
+ <textbox id="syncComputerName"
+ flex="1"
+ onchange="gSyncUtils.changeName(this);"/>
+ </row>
+ <row>
+ <label value="&syncMy.label;"/>
+ <vbox>
+ <checkbox label="&engine.addons.label;"
+ accesskey="&engine.addons.accesskey;"
+ id="engine.addons"
+ checked="true"/>
+ <checkbox label="&engine.bookmarks.label;"
+ accesskey="&engine.bookmarks.accesskey;"
+ id="engine.bookmarks"
+ checked="true"/>
+ <checkbox label="&engine.history.label;"
+ accesskey="&engine.history.accesskey;"
+ id="engine.history"
+ checked="true"/>
+ <checkbox label="&engine.passwords.label;"
+ accesskey="&engine.passwords.accesskey;"
+ id="engine.passwords"
+ checked="true"/>
+ <checkbox label="&engine.prefs.label;"
+ accesskey="&engine.prefs.accesskey;"
+ id="engine.prefs"
+ checked="true"/>
+ <checkbox label="&engine.tabs.label;"
+ accesskey="&engine.tabs.accesskey;"
+ id="engine.tabs"
+ checked="true"/>
+ </vbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <groupbox id="mergeOptions">
+ <radiogroup id="mergeChoiceRadio" pack="start">
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows flex="1">
+ <row align="center">
+ <radio value="resetClient"
+ label="&choice2.merge.main.label;"
+ class="mergeChoiceButton"/>
+ <label value="&choice2.merge.recommended.label;" class="recommended"/>
+ </row>
+ <row align="center">
+ <radio value="wipeClient"
+ label="&choice2.client.main.label;"
+ class="mergeChoiceButton"/>
+ </row>
+ <row align="center">
+ <radio value="wipeRemote"
+ label="&choice2.server.main.label;"
+ class="mergeChoiceButton"/>
+ </row>
+ </rows>
+ </grid>
+ </radiogroup>
+ </groupbox>
+ </wizardpage>
+
+ <wizardpage id="syncOptionsConfirm"
+ label="&setup.optionsConfirmPage.title;"
+ onpageshow="gSyncSetup.onPageShow();">
+ <deck id="chosenActionDeck">
+ <vbox id="chosenActionMerge" class="confirm">
+ <description class="normal">
+ &confirm.merge.label;
+ </description>
+ </vbox>
+ <vbox id="chosenActionWipeClient" class="confirm">
+ <description class="normal">
+ &confirm.client2.label;
+ </description>
+ <separator class="thin"/>
+ <vbox id="dataList">
+ <label class="data indent" id="bookmarkCount"/>
+ <label class="data indent" id="historyCount"/>
+ <label class="data indent" id="passwordCount"/>
+ <label class="data indent" id="addonCount"/>
+ <label class="data indent" id="prefsWipe"
+ value="&engine.prefs.label;"/>
+ </vbox>
+ <separator class="thin"/>
+ <description class="normal">
+ &confirm.client.moreinfo.label;
+ </description>
+ </vbox>
+ <vbox id="chosenActionWipeServer" class="confirm">
+ <description class="normal">
+ &confirm.server2.label;
+ </description>
+ <separator class="thin"/>
+ <vbox id="clientList">
+ </vbox>
+ </vbox>
+ </deck>
+ </wizardpage>
+
+ <wizardpage id="successfulSetup"
+ label="&setup.successPage.title;"
+ onextra1="gSyncSetup.onSyncOptions();"
+ onpageshow="gSyncSetup.onPageShow();">
+ <vbox align="center">
+ <image id="successPageIcon"/>
+ </vbox>
+ <separator/>
+ <description class="normal">
+ <html:span id="firstSyncAction"/>
+ <html:strong id="firstSyncActionWarning"/>
+ <html:span id="firstSyncActionChange"/>
+ </description>
+ <description>
+ &continueUsing.label;
+ </description>
+ </wizardpage>
+</wizard>
diff --git a/comm/suite/components/sync/content/syncUI.js b/comm/suite/components/sync/content/syncUI.js
new file mode 100644
index 0000000000..668447c46d
--- /dev/null
+++ b/comm/suite/components/sync/content/syncUI.js
@@ -0,0 +1,454 @@
+/* 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 gSyncUI = {
+ _obs: ["weave:notification:added",
+ "weave:service:sync:start",
+ "weave:service:sync:delayed",
+ "weave:service:quota:remaining",
+ "weave:service:setup-complete",
+ "weave:service:login:start",
+ "weave:service:login:finish",
+ "weave:service:logout:finish",
+ "weave:service:start-over",
+ "weave:ui:login:error",
+ "weave:ui:sync:error",
+ "weave:ui:sync:finish",
+ "weave:ui:clear-error"],
+
+ _unloaded: false,
+
+ init: function SUI_init() {
+ // Update the Tools menu according to whether Sync is set up or not.
+ let taskPopup = document.getElementById("taskPopup");
+ if (taskPopup)
+ taskPopup.addEventListener("popupshowing", this.updateUI.bind(this));
+
+ // Proceed to set up the UI if Sync has already started up.
+ // Otherwise we'll do it when Sync is firing up.
+ if (Cc["@mozilla.org/weave/service;1"]
+ .getService().wrappedJSObject.ready) {
+ this.initUI();
+ return;
+ }
+
+ Services.obs.addObserver(this, "weave:service:ready", true);
+
+ // Remove the observer if the window is closed before the observer
+ // was triggered.
+ window.addEventListener("unload", function SUI_unload() {
+ gSyncUI._unloaded = true;
+ window.removeEventListener("unload", SUI_unload);
+ Services.obs.removeObserver(gSyncUI, "weave:service:ready");
+
+ if (Weave.Status.ready) {
+ gSyncUI._obs.forEach(function(topic) {
+ Services.obs.removeObserver(gSyncUI, topic);
+ });
+ }
+ });
+ },
+
+ initUI: function SUI_initUI() {
+ this._obs.forEach(function(topic) {
+ Services.obs.addObserver(this, topic, true);
+ }, this);
+
+ // Find the alltabs-popup
+ let popup = document.getElementById("alltabs-popup");
+ if (popup) {
+ popup.addEventListener(
+ "popupshowing", this.alltabsPopupShowing.bind(this), true);
+
+ if (Weave.Notifications.notifications.length)
+ this.initNotifications();
+ }
+ this.updateUI();
+ },
+
+ initNotifications: function SUI_initNotifications() {
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ let notificationbox = document.createElementNS(XULNS, "notificationbox");
+ notificationbox.id = "sync-notifications";
+
+ let statusbar = document.getElementById("status-bar");
+ statusbar.parentNode.insertBefore(notificationbox, statusbar);
+
+ // Force a style flush to ensure that our binding is attached.
+ notificationbox.clientTop;
+
+ // notificationbox will listen to observers from now on.
+ Services.obs.removeObserver(this, "weave:notification:added");
+ },
+
+ _wasDelayed: false,
+
+ _needsSetup: function SUI__needsSetup() {
+ let firstSync = "";
+ try {
+ firstSync = Services.prefs.getCharPref("services.sync.firstSync");
+ } catch (e) { }
+ return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
+ firstSync == "notReady";
+ },
+
+ updateUI: function SUI_updateUI() {
+ let needsSetup = this._needsSetup();
+ document.getElementById("sync-setup-state").hidden = !needsSetup;
+ document.getElementById("sync-syncnow-state").hidden = needsSetup;
+
+ let syncButton = document.getElementById("sync-button");
+ if (syncButton) {
+ syncButton.removeAttribute("status");
+ this._updateLastSyncTime();
+ if (needsSetup)
+ syncButton.removeAttribute("tooltiptext");
+ }
+ },
+
+ alltabsPopupShowing: function(event) {
+ // Should we show the menu item?
+ //XXXphilikon We should remove the check for isLoggedIn here and have
+ // about:sync-tabs auto-login (bug 583344)
+ if (!Weave.Service.isLoggedIn || !Weave.Service.engineManager.get("tabs").enabled)
+ return;
+
+ let label = this._stringBundle.GetStringFromName("tabs.fromOtherComputers.label");
+
+ let popup = document.getElementById("alltabs-popup");
+ if (!popup)
+ return;
+
+ let menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("id", "sync-tabs-menuitem");
+ menuitem.setAttribute("label", label);
+ menuitem.setAttribute("class", "alltabs-item");
+ menuitem.setAttribute("oncommand", "BrowserOpenSyncTabs();");
+
+ let sep = document.createElement("menuseparator");
+ sep.setAttribute("id", "sync-tabs-sep");
+
+ // Fake the tab object on the menu entries, so that we don't have to worry
+ // about removing them ourselves. They will just get cleaned up by popup
+ // binding. This also makes sure the statusbar updates with the URL.
+ menuitem.tab = { "linkedBrowser": { "currentURI": { "spec": label } } };
+ sep.tab = { "linkedBrowser": { "currentURI": { "spec": " " } } };
+
+ popup.insertBefore(sep, popup.firstChild);
+ popup.insertBefore(menuitem, sep);
+ },
+
+ // Functions called by observers
+ onActivityStart: function SUI_onActivityStart() {
+ let syncButton = document.getElementById("sync-button");
+ if (syncButton)
+ syncButton.setAttribute("status", "active");
+ },
+
+ onSyncDelay: function SUI_onSyncDelay() {
+ // basically, we want to just inform users that stuff is going to take a while
+ let title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
+ let description = this._stringBundle.GetStringFromName("error.sync.no_node_found");
+ let buttons = [new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
+ this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
+ function() { gSyncUI.openServerStatus(); return true; }
+ )];
+ let notification = new Weave.Notification(
+ title, description, null, Weave.Notifications.PRIORITY_INFO, buttons);
+ Weave.Notifications.replaceTitle(notification);
+ this._wasDelayed = true;
+ },
+
+ onLoginFinish: function SUI_onLoginFinish() {
+ // Clear out any login failure notifications
+ let title = this._stringBundle.GetStringFromName("error.login.title");
+ this.clearError(title);
+ },
+
+ onLoginError: function SUI_onLoginError() {
+ // if login fails, any other notifications are essentially moot
+ Weave.Notifications.removeAll();
+
+ // if we haven't set up the client, don't show errors
+ if (this._needsSetup()) {
+ this.updateUI();
+ return;
+ }
+
+ let title = this._stringBundle.GetStringFromName("error.login.title");
+
+ let description;
+ if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
+ // Convert to days
+ let lastSync =
+ Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
+ description =
+ this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
+ } else {
+ let reason = Weave.Utils.getErrorString(Weave.Status.login);
+ description =
+ this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
+ }
+
+ let buttons = [];
+ buttons.push(new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName("error.login.prefs.label"),
+ this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
+ function() { gSyncUI.openPrefs(); return true; }
+ ));
+
+ let notification = new Weave.Notification(title, description, null,
+ Weave.Notifications.PRIORITY_WARNING, buttons);
+ Weave.Notifications.replaceTitle(notification);
+ this.updateUI();
+ },
+
+ onLogout: function SUI_onLogout() {
+ this.updateUI();
+ },
+
+ onStartOver: function SUI_onStartOver() {
+ this.clearError();
+ },
+
+ onQuotaNotice: function onQuotaNotice(subject, data) {
+ let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
+ let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
+ let buttons = [];
+ buttons.push(new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
+ this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
+ function() { gSyncUI.openQuotaDialog(); return true; }
+ ));
+
+ let notification = new Weave.Notification(
+ title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
+ Weave.Notifications.replaceTitle(notification);
+ },
+
+ openServerStatus: function () {
+ let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
+ openUILinkIn(statusURL, "tab");
+ },
+
+ // Commands
+ doSync: function SUI_doSync() {
+ setTimeout(() => Weave.Service.errorHandler.syncAndReportErrors(), 0);
+ },
+
+ handleToolbarButton: function SUI_handleToolbarButton() {
+ if (this._needsSetup())
+ this.openSetup();
+ else
+ this.doSync();
+ },
+
+ //XXXzpao should be part of syncCommon.js - which we might want to make a module...
+ // To be fixed in a followup (bug 583366)
+ openSetup: function SUI_openSetup() {
+ let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
+ if (win)
+ win.focus();
+ else {
+ window.openDialog("chrome://communicator/content/sync/syncSetup.xul",
+ "weaveSetup", "centerscreen,chrome,resizable=no");
+ }
+ },
+
+ openQuotaDialog: function SUI_openQuotaDialog() {
+ let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
+ if (win)
+ win.focus();
+ else
+ Services.ww.activeWindow.openDialog(
+ "chrome://communicator/content/sync/syncQuota.xul", "",
+ "centerscreen,chrome,dialog,modal");
+ },
+
+ openPrefs: function SUI_openPrefs() {
+ goPreferences("sync_pane");
+ },
+
+
+ // Helpers
+ _updateLastSyncTime: function SUI__updateLastSyncTime() {
+ let syncButton = document.getElementById("sync-button");
+ if (!syncButton)
+ return;
+
+ let lastSync;
+ try {
+ lastSync = Services.prefs.getCharPref("services.sync.lastSync");
+ }
+ catch (e) { };
+ if (!lastSync || this._needsSetup()) {
+ syncButton.removeAttribute("tooltiptext");
+ return;
+ }
+
+ // Show the day-of-week and time (HH:MM) of last sync
+ let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
+ let lastSyncLabel =
+ this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
+ syncButton.setAttribute("tooltiptext", lastSyncLabel);
+ },
+
+ clearError: function SUI_clearError(errorString) {
+ Weave.Notifications.removeAll(errorString);
+ this.updateUI();
+ },
+
+ onSyncFinish: function SUI_onSyncFinish() {
+ let title = this._stringBundle.GetStringFromName("error.sync.title");
+
+ // Clear out sync failures on a successful sync
+ this.clearError(title);
+
+ if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
+ title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
+ this.clearError(title);
+ this._wasDelayed = false;
+ }
+ },
+
+ onSyncError: function SUI_onSyncError() {
+ let title = this._stringBundle.GetStringFromName("error.sync.title");
+
+ if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
+ this.onLoginError();
+ return;
+ }
+
+ let description;
+ if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
+ // Convert to days
+ let lastSync =
+ Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
+ description =
+ this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
+ } else {
+ let error = Weave.Utils.getErrorString(Weave.Status.sync);
+ description =
+ this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
+ }
+ let priority = Weave.Notifications.PRIORITY_WARNING;
+ let buttons = [];
+
+ // Check if the client is outdated in some way
+ let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
+ for (let [engine, reason] of Object.entries(Weave.Status.engines))
+ outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
+
+ if (outdated) {
+ description = this._stringBundle.GetStringFromName(
+ "error.sync.needUpdate.description");
+ buttons.push(new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
+ this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
+ function() { window.openUILinkIn("https://services.mozilla.com/update/", "tab"); return true; }
+ ));
+ }
+ else if (Weave.Status.sync == Weave.OVER_QUOTA) {
+ description = this._stringBundle.GetStringFromName(
+ "error.sync.quota.description");
+ buttons.push(new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName(
+ "error.sync.viewQuotaButton.label"),
+ this._stringBundle.GetStringFromName(
+ "error.sync.viewQuotaButton.accesskey"),
+ function() { gSyncUI.openQuotaDialog(); return true; } )
+ );
+ }
+ else if (Weave.Status.enforceBackoff) {
+ priority = Weave.Notifications.PRIORITY_INFO;
+ buttons.push(new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
+ this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
+ function() { gSyncUI.openServerStatus(); return true; }
+ ));
+ }
+ else {
+ priority = Weave.Notifications.PRIORITY_INFO;
+ buttons.push(new Weave.NotificationButton(
+ this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
+ this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
+ function() { gSyncUI.doSync(); return true; }
+ ));
+ }
+
+ let notification =
+ new Weave.Notification(title, description, null, priority, buttons);
+ Weave.Notifications.replaceTitle(notification);
+
+ if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
+ title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
+ Weave.Notifications.removeAll(title);
+ this._wasDelayed = false;
+ }
+
+ this.updateUI();
+ },
+
+ observe: function SUI_observe(subject, topic, data) {
+ if (this._unloaded)
+ throw "SyncUI observer called after unload: " + topic;
+
+ switch (topic) {
+ case "weave:service:sync:start":
+ this.onActivityStart();
+ break;
+ case "weave:ui:sync:finish":
+ this.onSyncFinish();
+ break;
+ case "weave:ui:sync:error":
+ this.onSyncError();
+ break;
+ case "weave:service:sync:delayed":
+ this.onSyncDelay();
+ break;
+ case "weave:service:quota:remaining":
+ this.onQuotaNotice();
+ break;
+ case "weave:service:setup-complete":
+ this.onLoginFinish();
+ break;
+ case "weave:service:login:start":
+ this.onActivityStart();
+ break;
+ case "weave:service:login:finish":
+ this.onLoginFinish();
+ break;
+ case "weave:ui:login:error":
+ this.onLoginError();
+ break;
+ case "weave:service:logout:finish":
+ this.onLogout();
+ break;
+ case "weave:service:start-over":
+ this.onStartOver();
+ break;
+ case "weave:service:ready":
+ this.initUI();
+ break;
+ case "weave:notification:added":
+ this.initNotifications();
+ break;
+ case "weave:ui:clear-error":
+ this.clearError();
+ break;
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIObserver,
+ Ci.nsISupportsWeakReference
+ ])
+};
+
+XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
+ //XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
+ // but for now just make it work
+ return Services.strings.createBundle("chrome://weave/locale/services/sync.properties");
+});
diff --git a/comm/suite/components/sync/content/syncUtils.js b/comm/suite/components/sync/content/syncUtils.js
new file mode 100644
index 0000000000..fe63654073
--- /dev/null
+++ b/comm/suite/components/sync/content/syncUtils.js
@@ -0,0 +1,224 @@
+/* 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/. */
+
+// Weave should always exist before before this file gets included.
+var gSyncUtils = {
+ _openLink: function (url) {
+ if (document.documentElement.id == "change-dialog")
+ Services.wm.getMostRecentWindow("navigator:browser")
+ .openUILinkIn(url, "tab");
+ else
+ openUILinkIn(url, "tab");
+ },
+
+ changeName: function changeName(input) {
+ // Make sure to update to a modified name, e.g., empty-string -> default
+ Weave.Service.clientsEngine.localName = input.value;
+ input.value = Weave.Service.clientsEngine.localName;
+ },
+
+ openChange: function openChange(type, duringSetup) {
+ // Just re-show the dialog if it's already open
+ let openedDialog = Services.wm.getMostRecentWindow("Sync:" + type);
+ if (openedDialog != null) {
+ openedDialog.focus();
+ return;
+ }
+
+ // Open up the change dialog
+ let changeXUL = "chrome://communicator/content/sync/syncGenericChange.xul";
+ let changeOpt = "centerscreen,chrome,resizable=no";
+ Services.ww.activeWindow.openDialog(changeXUL, "", changeOpt,
+ type, duringSetup);
+ },
+
+ changePassword: function () {
+ if (Weave.Utils.ensureMPUnlocked())
+ this.openChange("ChangePassword");
+ },
+
+ resetPassphrase: function (duringSetup) {
+ if (Weave.Utils.ensureMPUnlocked())
+ this.openChange("ResetPassphrase", duringSetup);
+ },
+
+ updatePassphrase: function () {
+ if (Weave.Utils.ensureMPUnlocked())
+ this.openChange("UpdatePassphrase");
+ },
+
+ resetPassword: function () {
+ this._openLink(Weave.Service.pwResetURL);
+ },
+
+ openToS: function () {
+ this._openLink(Weave.Svc.Prefs.get("termsURL"));
+ },
+
+ openPrivacyPolicy: function () {
+ this._openLink(Weave.Svc.Prefs.get("privacyURL"));
+ },
+
+ // xxxmpc - fix domain before 1.3 final (bug 583652)
+ // xxxInvisibleSmiley - we should really have our own pages
+ // since these refer to Firefox in the page contents
+ _baseURL: "http://www.mozilla.com/firefox/sync/",
+
+ openFirstClientFirstrun: function () {
+ let url = this._baseURL + "firstrun.html";
+ this._openLink(url);
+ },
+
+ openAddedClientFirstrun: function () {
+ let url = this._baseURL + "secondrun.html";
+ this._openLink(url);
+ },
+
+ /**
+ * Prepare an invisible iframe with the passphrase backup document.
+ * Used by both the print and saving methods.
+ *
+ * @param elid : ID of the form element containing the passphrase.
+ * @param callback : Function called once the iframe has loaded.
+ */
+ _preparePPiframe: function(elid, callback) {
+ let pp = document.getElementById(elid).value;
+
+ // Create an invisible iframe whose contents we can print.
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "chrome://communicator/content/sync/syncKey.xhtml");
+ iframe.setAttribute("type", "content");
+ iframe.collapsed = true;
+ document.documentElement.appendChild(iframe);
+ iframe.addEventListener("load", function loadListener() {
+ iframe.removeEventListener("load", loadListener, true);
+
+ // Remove the license block.
+ let node = iframe.contentDocument.firstChild;
+ if (node && node.nodeType == Node.COMMENT_NODE)
+ node.remove();
+
+ // Insert the Sync Key into the page.
+ let el = iframe.contentDocument.getElementById("synckey");
+ el.firstChild.nodeValue = pp;
+
+ // Insert the TOS and Privacy Policy URLs into the page.
+ let termsURL = Weave.Svc.Prefs.get("termsURL");
+ el = iframe.contentDocument.getElementById("tosLink");
+ el.setAttribute("href", termsURL);
+ el.firstChild.nodeValue = termsURL;
+
+ let privacyURL = Weave.Svc.Prefs.get("privacyURL");
+ el = iframe.contentDocument.getElementById("ppLink");
+ el.setAttribute("href", privacyURL);
+ el.firstChild.nodeValue = privacyURL;
+
+ callback(iframe);
+ }, true);
+ },
+
+ /**
+ * Print passphrase backup document.
+ *
+ * @param elid : ID of the form element containing the passphrase.
+ */
+ passphrasePrint: function(elid) {
+ this._preparePPiframe(elid, function(iframe) {
+ let webBrowserPrint = iframe.contentWindow
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebBrowserPrint);
+ let printSettings = PrintUtils.getPrintSettings();
+
+ // Display no header/footer decoration except for the date.
+ printSettings.headerStrLeft
+ = printSettings.headerStrCenter
+ = printSettings.headerStrRight
+ = printSettings.footerStrLeft
+ = printSettings.footerStrCenter = "";
+ printSettings.footerStrRight = "&D";
+
+ try {
+ webBrowserPrint.print(printSettings, null);
+ } catch (ex) {
+ // print()'s return codes are expressed as exceptions. Ignore.
+ }
+ });
+ },
+
+ /**
+ * Save passphrase backup document to disk as HTML file.
+ *
+ * @param elid : ID of the form element containing the passphrase.
+ */
+ passphraseSave: function(elid) {
+ let dialogTitle = this._stringBundle.GetStringFromName("save.recoverykey.title");
+ let defaultSaveName = this._stringBundle.GetStringFromName("save.recoverykey.defaultfilename");
+ this._preparePPiframe(elid, function(iframe) {
+ let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ let fpCallback = function fpCallback_done(aResult) {
+ if (aResult == Ci.nsIFilePicker.returnOK ||
+ aResult == Ci.nsIFilePicker.returnReplace) {
+ let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(filepicker.file, -1, parseInt("0600", 8), 0);
+
+ let serializer = new XMLSerializer();
+ let output = serializer.serializeToString(iframe.contentDocument);
+ output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
+ '"DTD/xhtml1-strict.dtd">');
+ output = Weave.Utils.encodeUTF8(output);
+ stream.write(output, output.length);
+ }
+ };
+
+ fp.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
+ fp.appendFilters(Ci.nsIFilePicker.filterHTML);
+ fp.defaultString = defaultSaveName;
+ fp.open(fpCallback);
+ return false;
+ });
+ },
+
+ /**
+ * validatePassword
+ *
+ * @param el1 : the first textbox element in the form
+ * @param el2 : the second textbox element, if omitted it's an update form
+ *
+ * returns [valid, errorString]
+ */
+ validatePassword: function (el1, el2) {
+ let valid = false;
+ let val1 = el1.value;
+ let val2 = el2 ? el2.value : "";
+ let error = "";
+
+ if (!el2)
+ valid = val1.length >= Weave.MIN_PASS_LENGTH;
+ else if (val1 && val1 == Weave.Service.identity.username)
+ error = "change.password.pwSameAsUsername";
+ else if (val1 && val1 == Weave.Service.identity.account)
+ error = "change.password.pwSameAsEmail";
+ else if (val1 && val1 == Weave.Service.identity.basicPassword)
+ error = "change.password.pwSameAsPassword";
+ else if (val1 && val1 == Weave.Service.identity.syncKey)
+ error = "change.password.pwSameAsRecoveryKey";
+ else if (val1 && val2) {
+ if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
+ valid = true;
+ else if (val1.length < Weave.MIN_PASS_LENGTH)
+ error = "change.password.tooShort";
+ else if (val1 != val2)
+ error = "change.password.mismatch";
+ }
+ let errorString = error ? Weave.Utils.getErrorString(error) : "";
+ return [valid, errorString];
+ }
+};
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGetter(gSyncUtils, "_stringBundle", function() {
+ return Services.strings.createBundle("chrome://communicator/locale/sync/syncSetup.properties");
+});
diff --git a/comm/suite/components/sync/jar.mn b/comm/suite/components/sync/jar.mn
new file mode 100644
index 0000000000..42138bae9a
--- /dev/null
+++ b/comm/suite/components/sync/jar.mn
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/communicator/aboutSyncTabs.xul (content/aboutSyncTabs.xul)
+ content/communicator/aboutSyncTabs.js (content/aboutSyncTabs.js)
+ content/communicator/aboutSyncTabs.css (content/aboutSyncTabs.css)
+ content/communicator/aboutSyncTabs-bindings.xml (content/aboutSyncTabs-bindings.xml)
+ content/communicator/sync/syncAddDevice.xul (content/syncAddDevice.xul)
+ content/communicator/sync/syncAddDevice.js (content/syncAddDevice.js)
+ content/communicator/sync/syncSetup.xul (content/syncSetup.xul)
+ content/communicator/sync/syncSetup.js (content/syncSetup.js)
+ content/communicator/sync/syncGenericChange.xul (content/syncGenericChange.xul)
+ content/communicator/sync/syncGenericChange.js (content/syncGenericChange.js)
+ content/communicator/sync/syncKey.xhtml (content/syncKey.xhtml)
+ content/communicator/sync/syncNotification.xml (content/syncNotification.xml)
+ content/communicator/sync/syncQuota.xul (content/syncQuota.xul)
+ content/communicator/sync/syncQuota.js (content/syncQuota.js)
+ content/communicator/sync/syncUtils.js (content/syncUtils.js)
+ content/communicator/sync/syncUI.js (content/syncUI.js)
diff --git a/comm/suite/components/sync/moz.build b/comm/suite/components/sync/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/sync/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/components/tests/browser/browser.ini b/comm/suite/components/tests/browser/browser.ini
new file mode 100644
index 0000000000..96c17932c0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser.ini
@@ -0,0 +1,72 @@
+[DEFAULT]
+support-files = head.js
+
+[browser_339445.js]
+support-files = browser_339445_sample.html
+[browser_345898.js]
+[browser_346337.js]
+support-files = browser_346337_sample.html
+[browser_350525.js]
+[browser_354894.js]
+[browser_367052.js]
+[browser_393716.js]
+[browser_394759_basic.js]
+[browser_394759_behavior.js]
+[browser_408470.js]
+support-files = browser_408470_sample.html
+[browser_423132.js]
+support-files = browser_423132_sample.html
+[browser_447951.js]
+support-files = browser_447951_sample.html
+[browser_448741.js]
+[browser_454908.js]
+support-files = browser_454908_sample.html
+[browser_456342.js]
+support-files = browser_456342_sample.xhtml
+[browser_461634.js]
+[browser_463206.js]
+support-files = browser_463206_sample.html
+[browser_465215.js]
+[browser_465223.js]
+[browser_466937.js]
+support-files = browser_466937_sample.html
+[browser_477657.js]
+[browser_480893.js]
+[browser_483330.js]
+[browser_485482.js]
+support-files = browser_485482_sample.html
+[browser_490040.js]
+[browser_491168.js]
+[browser_491577.js]
+[browser_493467.js]
+[browser_500328.js]
+[browser_514751.js]
+[browser_522545.js]
+[browser_524745.js]
+[browser_526613.js]
+[browser_528776.js]
+[browser_581937.js]
+[browser_586068-cascaded_restore.js]
+[browser_597315.js]
+support-files =
+ browser_597315_index.html
+ browser_597315_a.html
+ browser_597315_b.html
+ browser_597315_c.html
+ browser_597315_c1.html
+ browser_597315_c2.html
+[browser_607016.js]
+[browser_615394-SSWindowState_events.js]
+[browser_625257.js]
+[browser_636279.js]
+[browser_637020.js]
+support-files = browser_637020_slow.sjs
+[browser_645428.js]
+[browser_665702-state_session.js]
+[browser_687710.js]
+[browser_687710_2.js]
+[browser_694378.js]
+[browser_bug431826.js]
+[browser_isempty.js]
+[browser_markPageAsFollowedLink.js]
+support-files = framedPage.html frameLeft.html frameRight.html
diff --git a/comm/suite/components/tests/browser/browser_339445.js b/comm/suite/components/tests/browser/browser_339445.js
new file mode 100644
index 0000000000..fb41aebb24
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_339445.js
@@ -0,0 +1,34 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 339445 **/
+
+ waitForExplicitFinish();
+
+ let testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_339445_sample.html";
+
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ is(doc.getElementById("storageTestItem").textContent, "PENDING",
+ "sessionStorage value has been set");
+
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ this.removeEventListener("load", testTab2LBLoad, true);
+ let doc2 = tab2.linkedBrowser.contentDocument;
+ is(doc2.getElementById("storageTestItem").textContent, "SUCCESS",
+ "sessionStorage value has been duplicated");
+
+ // clean up
+ getBrowser().removeTab(tab2);
+ getBrowser().removeTab(tab);
+
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_339445_sample.html b/comm/suite/components/tests/browser/browser_339445_sample.html
new file mode 100644
index 0000000000..1fd7d5f032
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_339445_sample.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Test for bug 339445</title>
+
+storageTestItem = <span id="storageTestItem">FAIL</span>
+
+<!--
+ storageTestItem's textContent will be one of the following:
+ * FAIL : sessionStorage wasn't available
+ * PENDING : the test value has been initialized on first load
+ * SUCCESS : the test value was correctly retrieved
+-->
+
+<script>
+ document.getElementById("storageTestItem").textContent =
+ sessionStorage["storageTestItem"] || "PENDING";
+ sessionStorage["storageTestItem"] = "SUCCESS";
+</script>
diff --git a/comm/suite/components/tests/browser/browser_345898.js b/comm/suite/components/tests/browser/browser_345898.js
new file mode 100644
index 0000000000..7f8c26f1a6
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_345898.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 345898 **/
+
+ function test(aLambda) {
+ try {
+ aLambda();
+ return false;
+ }
+ catch (ex) {
+ return ex.name == "NS_ERROR_ILLEGAL_VALUE";
+ }
+ }
+
+ // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+ ok(test(() => ss.getWindowState({})),
+ "Invalid window for getWindowState throws");
+ ok(test(() => ss.setWindowState({}, "", false)),
+ "Invalid window for setWindowState throws");
+ ok(test(() => ss.getTabState({})),
+ "Invalid tab for getTabState throws");
+ ok(test(() => ss.setTabState({}, "{}")),
+ "Invalid tab state for setTabState throws");
+ ok(test(() => ss.setTabState({}, '{ "entries": [] }')),
+ "Invalid tab for setTabState throws");
+ ok(test(() => ss.duplicateTab({}, {})),
+ "Invalid tab for duplicateTab throws");
+ ok(test(() => ss.duplicateTab({}, getBrowser().selectedTab)),
+ "Invalid window for duplicateTab throws");
+ ok(test(() => ss.getClosedTabData({})),
+ "Invalid window for getClosedTabData throws");
+ ok(test(() => ss.undoCloseTab({}, 0)),
+ "Invalid window for undoCloseTab throws");
+ ok(test(() => ss.undoCloseTab(window, -1)),
+ "Invalid index for undoCloseTab throws");
+ ok(test(() => ss.getWindowValue({}, "")),
+ "Invalid window for getWindowValue throws");
+ ok(test(() => ss.getWindowValue({}, "")),
+ "Invalid window for getWindowValue throws");
+ ok(test(() => ss.getWindowValue({}, "", "")),
+ "Invalid window for setWindowValue throws");
+}
diff --git a/comm/suite/components/tests/browser/browser_346337.js b/comm/suite/components/tests/browser/browser_346337.js
new file mode 100644
index 0000000000..f97e36cc24
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_346337.js
@@ -0,0 +1,122 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 346337 **/
+
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("346337_test1.file");
+ let filePath1 = file.path;
+ file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("346337_test2.file");
+ let filePath2 = file.path;
+
+ let fieldList = {
+ "//input[@name='input']": Date.now().toString(),
+ "//input[@name='spaced 1']": Math.random().toString(),
+ "//input[3]": "three",
+ "//input[@type='checkbox']": true,
+ "//input[@name='uncheck']": false,
+ "//input[@type='radio'][1]": false,
+ "//input[@type='radio'][2]": true,
+ "//input[@type='radio'][3]": false,
+ "//select": 2,
+ "//select[@multiple]": [1, 3],
+ "//textarea[1]": "",
+ "//textarea[2]": "Some text... " + Math.random(),
+ "//textarea[3]": "Some more text\n" + new Date(),
+ "//input[@type='file'][1]": [filePath1],
+ "//input[@type='file'][2]": [filePath1, filePath2]
+ };
+
+ function getElementByXPath(aTab, aQuery) {
+ let doc = aTab.linkedBrowser.contentDocument;
+ let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
+ return doc.evaluate(aQuery, doc, null, xptype, null).singleNodeValue;
+ }
+
+ function setFormValue(aTab, aQuery, aValue) {
+ let node = getElementByXPath(aTab, aQuery);
+ if (typeof aValue == "string")
+ node.value = aValue;
+ else if (typeof aValue == "boolean")
+ node.checked = aValue;
+ else if (typeof aValue == "number")
+ node.selectedIndex = aValue;
+ else if (ChromeUtils.getClassName(node) === "HTMLInputElement" && node.type == "file")
+ node.mozSetFileNameArray(aValue, aValue.length);
+ else
+ Array.from(node.options).forEach((aOpt, aIx) =>
+ aOpt.selected = aValue.includes(aIx));
+ }
+
+ function compareFormValue(aTab, aQuery, aValue) {
+ let node = getElementByXPath(aTab, aQuery);
+ if (!node)
+ return false;
+ if (ChromeUtils.getClassName(node) === "HTMLInputElement") {
+ if (node.type == "file") {
+ let fileNames = node.mozGetFileNameArray();
+ return fileNames.length == aValue.length &&
+ Array.from(fileNames).every(aFile => aValue.includes(aFile));
+ }
+ return aValue == (node.type == "checkbox" || node.type == "radio" ?
+ node.checked : node.value);
+ }
+ if (ChromeUtils.getClassName(node) === "HTMLTextAreaElement")
+ return aValue == node.value;
+ if (!node.multiple)
+ return aValue == node.selectedIndex;
+ return Array.from(node.options).every((aOpt, aIx) =>
+ aValue.includes(aIx) == aOpt.selected);
+ }
+
+ // test setup
+ let tabbrowser = getBrowser();
+ waitForExplicitFinish();
+
+ // make sure we don't save form data at all (except for tab duplication)
+ Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_346337_sample.html";
+ let tab = tabbrowser.addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function loadListener1(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", loadListener1, true);
+ for (let xpath in fieldList)
+ setFormValue(tab, xpath, fieldList[xpath]);
+
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("pageshow", function pageshowListener2(aEvent) {
+ tab2.linkedBrowser.removeEventListener("pageshow", pageshowListener2, true);
+ for (let xpath in fieldList)
+ ok(compareFormValue(tab2, xpath, fieldList[xpath]),
+ "The value for \"" + xpath + "\" was correctly restored");
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function pageshowListener3(aEvent) {
+ browser.removeEventListener("load", pageshowListener3, true);
+ let tab3 = tabbrowser.undoCloseTab(0);
+ tab3.linkedBrowser.addEventListener("pageshow", function pageshowListener4(aEvent) {
+ tab3.linkedBrowser.removeEventListener("pageshow", pageshowListener4, true);
+ for (let xpath in fieldList)
+ if (fieldList[xpath])
+ ok(!compareFormValue(tab3, xpath, fieldList[xpath]),
+ "The value for \"" + xpath + "\" was correctly discarded");
+
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.privacy_level"))
+ Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+ // undoCloseTab can reuse a single blank tab, so we have to
+ // make sure not to close the window when closing our last tab
+ if (tabbrowser.tabContainer.childNodes.length == 1)
+ tabbrowser.addTab();
+ tabbrowser.removeTab(tab3);
+ finish();
+ }, true);
+ }, true);
+ // clean up
+ tabbrowser.removeTab(tab2);
+ tabbrowser.removeTab(tab);
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_346337_sample.html b/comm/suite/components/tests/browser/browser_346337_sample.html
new file mode 100644
index 0000000000..b0c305775e
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_346337_sample.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Test for bug 346337</title>
+
+<h3>Text Fields</h3>
+<input type="text" name="input">
+<input type="text" name="spaced 1">
+<input>
+
+<h3>Checkboxes and Radio buttons</h3>
+<input type="checkbox" name="check"> Check 1
+<input type="checkbox" name="uncheck" checked> Check 2
+<p>
+<input type="radio" name="group" value="1"> Radio 1
+<input type="radio" name="group" value="some"> Radio 2
+<input type="radio" name="group" checked> Radio 3
+
+<h3>Selects</h3>
+<select name="any">
+ <option value="1"> Select 1
+ <option value="some"> Select 2
+ <option>Select 3
+</select>
+<select multiple="multiple">
+ <option value=1> Multi-select 1
+ <option value=2> Multi-select 2
+ <option value=3> Multi-select 3
+ <option value=4> Multi-select 4
+</select>
+
+<h3>Text Areas</h3>
+<textarea name="testarea"></textarea>
+<textarea name="sized one" rows="5" cols="25"></textarea>
+<textarea></textarea>
+
+<h3>File Selector</h3>
+<input type="file">
+<input type="file" multiple>
diff --git a/comm/suite/components/tests/browser/browser_350525.js b/comm/suite/components/tests/browser/browser_350525.js
new file mode 100644
index 0000000000..c5fb0e11e7
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_350525.js
@@ -0,0 +1,100 @@
+function test() {
+ /** Test for Bug 350525 **/
+
+ function test(aLambda) {
+ try {
+ return aLambda() || true;
+ }
+ catch (ex) { }
+ return false;
+ }
+
+ waitForExplicitFinish();
+
+ ////////////////////////////
+ // setWindowValue, et al. //
+ ////////////////////////////
+ let key = "Unique name: " + Date.now();
+ let value = "Unique value: " + Math.random();
+
+ // test adding
+ ok(test(() => ss.setWindowValue(window, key, value)), "set a window value");
+
+ // test retrieving
+ is(ss.getWindowValue(window, key), value, "stored window value matches original");
+
+ // test deleting
+ ok(test(() => ss.deleteWindowValue(window, key)), "delete the window value");
+
+ // value should not exist post-delete
+ is(ss.getWindowValue(window, key), "", "window value was deleted");
+
+ // test deleting a non-existent value
+ ok(test(() => ss.deleteWindowValue(window, key)), "delete non-existent window value");
+
+ /////////////////////////
+ // setTabValue, et al. //
+ /////////////////////////
+ key = "Unique name: " + Math.random();
+ value = "Unique value: " + Date.now();
+ let tab = getBrowser().addTab();
+ tab.linkedBrowser.stop();
+
+ // test adding
+ ok(test(() => ss.setTabValue(tab, key, value)), "store a tab value");
+
+ // test retrieving
+ is(ss.getTabValue(tab, key), value, "stored tab value match original");
+
+ // test deleting
+ ok(test(() => ss.deleteTabValue(tab, key)), "delete the tab value");
+ // value should not exist post-delete
+ is(ss.getTabValue(tab, key), "", "tab value was deleted");
+
+ // test deleting a non-existent value
+ ok(test(() => ss.deleteTabValue(tab, key)), "delete non-existent tab value");
+
+ // clean up
+ getBrowser().removeTab(tab);
+
+ /////////////////////////////////////
+ // getClosedTabCount, undoCloseTab //
+ /////////////////////////////////////
+
+ // get closed tab count
+ let count = ss.getClosedTabCount(window);
+ let max_tabs_undo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
+ ok(0 <= count && count <= max_tabs_undo,
+ "getClosedTabCount returns zero or at most max_tabs_undo");
+
+ // create a new tab
+ let testURL = "about:";
+ tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+ // make sure that the next closed tab will increase getClosedTabCount
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
+
+ // remove tab
+ getBrowser().removeTab(tab);
+
+ // getClosedTabCount
+ var newcount = ss.getClosedTabCount(window);
+ ok(newcount > count, "after closing a tab, getClosedTabCount has been incremented");
+
+ // undoCloseTab
+ tab = test(() => ss.undoCloseTab(window, 0));
+ ok(tab, "undoCloseTab doesn't throw")
+
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad2(aEvent) {
+ this.removeEventListener("load", testTabLBLoad2, true);
+ is(this.currentURI.spec, testURL, "correct tab was reopened");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+ getBrowser().removeTab(tab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_354894.js b/comm/suite/components/tests/browser/browser_354894.js
new file mode 100644
index 0000000000..8fbc5330d0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_354894.js
@@ -0,0 +1,459 @@
+/* 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/. */
+
+/**
+ * Checks that restoring the last browser window in session is actually
+ * working:
+ * 1.1) Open a new browser window
+ * 1.2) Add some tabs
+ * 1.3) Close that window
+ * 1.4) Opening another window
+ * --> State is restored
+ *
+ * 2.1) Open a new browser window
+ * 2.2) Add some tabs
+ * 2.4) Open some popups
+ * 2.5) Add another tab to one popup (so that it gets stored) and close it again
+ * 2.5) Close the browser window
+ * 2.6) Open another browser window
+ * --> State of the closed browser window, but not of the popup, is restored
+ *
+ * 3.1) Open a popup
+ * 3.2) Add another tab to the popup (so that it gets stored) and close it again
+ * 3.3) Open a window
+ * --> Nothing at all should be restored
+ *
+ * 4.1) Open two browser windows and close them again
+ * 4.2) undoCloseWindow() one
+ * 4.3) Open another browser window
+ * --> Nothing at all should be restored
+ *
+ * Checks the new notifications are correctly posted and processed, that is
+ * for each successful -requested a -granted is received, but omitted if
+ * -requested was cnceled
+ * Said notifications are:
+ * - browser-lastwindow-close-requested
+ * - browser-lastwindow-close-granted
+ * Tests are:
+ * 5) Cancel closing when first observe a -requested
+ * --> Window is kept open
+ * 6) Count the number of notifications
+ * --> count(-requested) == count(-granted) + 1
+ * --> (The first -requested was canceled, so off-by-one)
+ * 7) (Mac only) Mac version of Test 5 additionally preparing Test 6
+ *
+ * @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894
+ * @note It is implicitly tested that restoring the last window works when
+ * non-browser windows are around. The "Run Tests" window as well as the main
+ * browser window (wherein the test code gets executed) won't be considered
+ * browser windows. To achiveve this said main browser window has it's windowtype
+ * attribute modified so that it's not considered a browser window any longer.
+ * This is crucial, because otherwise there would be two browser windows around,
+ * said main test window and the one opened by the tests, and hence the new
+ * logic wouldn't be executed at all.
+ * @note Mac only tests the new notifications, as restoring the last window is
+ * not enabled on that platform (platform shim; the application is kept running
+ * although there are no windows left)
+ * @note There is a difference when closing a browser window with
+ * BrowserTryToCloseWindow() as opposed to close(). The former will make
+ * nsSessionStore restore a window next time it gets a chance and will post
+ * notifications. The latter won't.
+ */
+
+function browserWindowsCount(expected, msg) {
+ if (typeof expected == "number")
+ expected = [expected, expected];
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ is(count, expected[0], msg + " (nsIWindowMediator)");
+ let state = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore)
+ .getBrowserState();
+ is(JSON.parse(state).windows.length, expected[1], msg + " (getBrowserState)");
+}
+
+function test() {
+ browserWindowsCount(1, "Only one browser window should be open initially");
+
+ if (AppConstants.platform == "macosx") {
+ todo(false, "Test disabled on MacOSX. (Bug 520787)");
+ return;
+ }
+
+ waitForExplicitFinish();
+ // This test takes some time to run, and it could timeout randomly.
+ // So we require a longer timeout. See bug 528219.
+ requestLongerTimeout(2);
+
+ // Some urls that might be opened in tabs and/or popups
+ // Do not use about:blank:
+ // That one is reserved for special purposes in the tests
+ const TEST_URLS = ["about:mozilla", "about:buildconfig"];
+
+ // Number of -request notifications to except
+ // remember to adjust when adding new tests
+ const NOTIFICATIONS_EXPECTED = 4;
+
+ // Window features of popup windows
+ const POPUP_FEATURES = "toolbar=no,resizable=no,status=no";
+
+ // Window features of browser windows
+ const CHROME_FEATURES = "chrome,all,dialog=no";
+
+ // Store the old window type for cleanup
+ var oldWinType = "";
+ // Store the old tabs.warnOnClose pref so that we may reset it during
+ // cleanup
+ var oldWarnTabsOnClose = Services.prefs.getBoolPref("browser.tabs.warnOnClose");
+
+ // Observe these, and also use to count the number of hits
+ var observing = {
+ "browser-lastwindow-close-requested": 0,
+ "browser-lastwindow-close-granted": 0
+ };
+
+ /**
+ * Helper: Will observe and handle the notifications for us
+ */
+ var observer = {
+ hitCount: 0,
+
+ observe: function(aCancel, aTopic, aData) {
+ // count so that we later may compare
+ observing[aTopic]++;
+
+ // handle some tests
+ if (++this.hitCount == 1) {
+ // Test 6
+ aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true;
+ }
+ }
+ };
+
+ /**
+ * Helper: Sets prefs as the testsuite requires
+ * @note Will be reset in cleanTestSuite just before finishing the tests
+ */
+ function setPrefs() {
+ Services.prefs.setIntPref("browser.startup.page", 3);
+ Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
+ }
+
+ /**
+ * Helper: Sets up this testsuite
+ */
+ function setupTestsuite(testFn) {
+ // Register our observers
+ for (let o in observing)
+ Services.obs.addObserver(observer, o);
+
+ // Make the main test window not count as a browser window any longer
+ oldWinType = document.documentElement.getAttribute("windowtype");
+ document.documentElement.setAttribute("windowtype", "navigator:testrunner");
+ }
+
+ /**
+ * Helper: Cleans up behind the testsuite
+ */
+ function cleanupTestsuite(callback) {
+ // Finally remove observers again
+ for (let o in observing)
+ Services.obs.removeObserver(observer, o, false);
+
+ // Reset the prefs we touched
+ for (let pref of [
+ "browser.startup.page"
+ ]) {
+ if (Services.prefs.prefHasUserValue(pref))
+ Services.prefs.clearUserPref(pref);
+ }
+ Services.prefs.setBoolPref("browser.tabs.warnOnClose", oldWarnTabsOnClose);
+
+ // Reset the window type
+ document.documentElement.setAttribute("windowtype", oldWinType);
+ }
+
+ /**
+ * Helper: sets the prefs and a new window with our test tabs
+ */
+ function setupTestAndRun(testFn) {
+ // Prepare the prefs
+ setPrefs();
+
+ // Prepare a window; open it and add more tabs
+ let newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:config");
+ newWin.addEventListener("load", function loadListener1(aEvent) {
+ newWin.removeEventListener("load", loadListener1);
+ newWin.getBrowser().addEventListener("pageshow", function pageshowListener2(aEvent) {
+ newWin.getBrowser().removeEventListener("pageshow", pageshowListener2, true);
+ for (let url of TEST_URLS) {
+ newWin.getBrowser().addTab(url);
+ }
+
+ executeSoon(() => testFn(newWin));
+ }, true);
+ });
+ }
+
+ /**
+ * Test 1: Normal in-session restore
+ * @note: Non-Mac only
+ */
+ function testOpenCloseNormal(nextFn) {
+ setupTestAndRun(function(newWin) {
+ // Close the window
+ // window.close doesn't push any close events,
+ // so use BrowserTryToCloseWindow
+ newWin.BrowserTryToCloseWindow();
+
+ // The first request to close is denied by our observer (Test 6)
+ ok(!newWin.closed, "First close request was denied");
+ if (!newWin.closed) {
+ newWin.BrowserTryToCloseWindow();
+ ok(newWin.closed, "Second close request was granted");
+ }
+
+ // Open a new window
+ // The previously closed window should be restored
+ newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin.addEventListener("load", function loadListener3() {
+ newWin.removeEventListener("load", loadListener3);
+ executeSoon(function() {
+ is(newWin.getBrowser().browsers.length, TEST_URLS.length + 1,
+ "Restored window in-session with otherpopup windows around");
+
+ // Cleanup
+ newWin.close();
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ });
+ }
+
+ /**
+ * Test 2: Open some popup windows to check those aren't restored, but
+ * the browser window is
+ * @note: Non-Mac only
+ */
+ function testOpenCloseWindowAndPopup(nextFn) {
+ setupTestAndRun(function(newWin) {
+ // open some popups
+ let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]);
+ let popup2 = openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]);
+ popup2.addEventListener("load", function loadListener4() {
+ popup2.removeEventListener("load", loadListener4);
+ popup2.getBrowser().addEventListener("pageshow", function pageshowListener5() {
+ popup2.getBrowser().removeEventListener("pageshow", pageshowListener5, true);
+ popup2.getBrowser().addTab(TEST_URLS[0]);
+ // close the window
+ newWin.BrowserTryToCloseWindow();
+
+ // Close the popup window
+ // The test is successful when not this popup window is restored
+ // but instead newWin
+ popup2.close();
+
+ // open a new window the previously closed window should be restored to
+ newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin.addEventListener("load", function loadListener6() {
+ newWin.removeEventListener("load", loadListener6);
+ executeSoon(function() {
+ is(newWin.getBrowser().browsers.length, TEST_URLS.length + 1,
+ "Restored window and associated tabs in session");
+
+ // Cleanup
+ newWin.close();
+ popup.close();
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ }, true);
+ });
+ });
+ }
+
+ /**
+ * Test 3: Open some popup window to check it isn't restored.
+ * Instead nothing at all should be restored
+ * @note: Non-Mac only
+ */
+ function testOpenCloseOnlyPopup(nextFn) {
+ // prepare the prefs
+ setPrefs();
+
+ // This will cause nsSessionStore to restore a window the next time it
+ // gets a chance.
+ let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
+ popup.addEventListener("load", function loadListener7() {
+ popup.removeEventListener("load", loadListener7, true);
+ is(popup.getBrowser().browsers.length, 1,
+ "Did not restore the popup window (1)");
+ popup.BrowserTryToCloseWindow();
+
+ // Real tests
+ popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
+ popup.addEventListener("load", function loadListener8() {
+ popup.removeEventListener("load", loadListener8);
+ popup.getBrowser().addEventListener("pageshow", function pageshowListener9() {
+ popup.getBrowser().removeEventListener("pageshow", pageshowListener9, true);
+ popup.getBrowser().addTab(TEST_URLS[0]);
+
+ is(popup.getBrowser().browsers.length, 2,
+ "Did not restore to the popup window (2)");
+
+ // Close the popup window
+ // The test is successful when not this popup window is restored
+ // but instead a new window is opened without restoring anything
+ popup.close();
+
+ let newWin = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin.addEventListener("load", function loadListener10() {
+ newWin.removeEventListener("load", loadListener10, true);
+ executeSoon(function() {
+ isnot(newWin.getBrowser().browsers.length, 2,
+ "Did not restore the popup window");
+ is(TEST_URLS.indexOf(newWin.getBrowser().browsers[0].currentURI.spec), -1,
+ "Did not restore the popup window (2)");
+
+ // Cleanup
+ newWin.close();
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ }, true);
+ });
+ }, true);
+ }
+
+ /**
+ * Test 4: Open some windows and do undoCloseWindow. This should prevent any
+ * restoring later in the test
+ * @note: Non-Mac only
+ */
+ function testOpenCloseRestoreFromPopup(nextFn) {
+ setupTestAndRun(function(newWin) {
+ setupTestAndRun(function(newWin2) {
+ newWin.BrowserTryToCloseWindow();
+ newWin2.BrowserTryToCloseWindow();
+
+ browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
+
+ newWin = undoCloseWindow(0);
+
+ newWin2 = openDialog(location, "_blank", CHROME_FEATURES, "about:blank");
+ newWin2.addEventListener("load", function loadListener11() {
+ newWin2.removeEventListener("load", loadListener11, true);
+ executeSoon(function() {
+ is(newWin2.getBrowser().browsers.length, 1,
+ "Did not restore, as undoCloseWindow() was last called");
+ is(TEST_URLS.indexOf(newWin2.getBrowser().browsers[0].currentURI.spec), -1,
+ "Did not restore, as undoCloseWindow() was last called (2)");
+
+ browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup");
+
+ // Cleanup
+ newWin.close();
+ newWin2.close();
+
+ browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
+
+ // Next please
+ executeSoon(nextFn);
+ });
+ }, true);
+ });
+ });
+ }
+
+ /**
+ * Test 5: Check whether the right number of notifications was received during
+ * the tests
+ */
+ function testNotificationCount(nextFn) {
+ is(observing["browser-lastwindow-close-requested"], NOTIFICATIONS_EXPECTED,
+ "browser-lastwindow-close-requested notifications observed");
+
+ // -request must be one more as we cancel the first one we hit,
+ // and hence won't produce a corresponding -grant
+ // @see observer.observe
+ is(observing["browser-lastwindow-close-requested"],
+ observing["browser-lastwindow-close-granted"] + 1,
+ "Notification count for -request and -grant matches");
+
+ executeSoon(nextFn);
+ }
+
+ /**
+ * Test 6: Test if closing can be denied on Mac
+ * Futhermore prepares the testNotificationCount test (Test 6)
+ * @note: Mac only
+ */
+ function testMacNotifications(nextFn, iteration) {
+ iteration = iteration || 1;
+ setupTestAndRun(function(newWin) {
+ // close the window
+ // window.close doesn't push any close events,
+ // so use BrowserTryToCloseWindow
+ newWin.BrowserTryToCloseWindow();
+ if (iteration == 1) {
+ ok(!newWin.closed, "First close attempt denied");
+ if (!newWin.closed) {
+ newWin.BrowserTryToCloseWindow();
+ ok(newWin.closed, "Second close attempt granted");
+ }
+ }
+
+ if (iteration < NOTIFICATIONS_EXPECTED - 1) {
+ executeSoon(() => testMacNotifications(nextFn, ++iteration));
+ }
+ else {
+ executeSoon(nextFn);
+ }
+ });
+ }
+
+ // Execution starts here
+
+ setupTestsuite();
+ if (AppConstants.platform == "macosx") {
+ // Mac tests
+ testMacNotifications(function () {
+ testNotificationCount(function () {
+ cleanupTestsuite();
+ browserWindowsCount(1, "Only one browser window should be open eventually");
+ finish();
+ });
+ });
+ }
+ else {
+ // Non-Mac Tests
+ testOpenCloseNormal(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseNormal");
+ testOpenCloseWindowAndPopup(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseWindowAndPopup");
+ testOpenCloseOnlyPopup(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseOnlyPopup");
+ testOpenCloseRestoreFromPopup(function () {
+ browserWindowsCount([0, 1], "browser windows after testOpenCloseRestoreFromPopup");
+ testNotificationCount(function () {
+ cleanupTestsuite();
+ browserWindowsCount(1, "browser windows after testNotificationCount");
+ finish();
+ });
+ });
+ });
+ });
+ });
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_367052.js b/comm/suite/components/tests/browser/browser_367052.js
new file mode 100644
index 0000000000..54ffaf0253
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_367052.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 367052 **/
+
+ waitForExplicitFinish();
+
+ // make sure that the next closed tab will increase getClosedTabCount
+ let max_tabs_undo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo + 1);
+ let closedTabCount = ss.getClosedTabCount(window);
+
+ // restore a blank tab
+ let tab = getBrowser().addTab("about:");
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+
+ let history = tab.linkedBrowser.webNavigation.sessionHistory;
+ ok(history.count >= 1, "the new tab does have at least one history entry");
+
+ ss.setTabState(tab, '{ "entries": [] }');
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad2(aEvent) {
+ this.removeEventListener("load", testTabLBLoad2, true);
+ ok(history.count == 0, "the tab was restored without any history whatsoever");
+
+ getBrowser().removeTab(tab);
+ ok(ss.getClosedTabCount(window) == closedTabCount,
+ "The closed blank tab wasn't added to Recently Closed Tabs");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.max_tabs_undo"))
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_393716.js b/comm/suite/components/tests/browser/browser_393716.js
new file mode 100644
index 0000000000..ce1d33e167
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_393716.js
@@ -0,0 +1,74 @@
+function test() {
+ /** Test for Bug 393716 **/
+
+ waitForExplicitFinish();
+
+ /////////////////
+ // getTabState //
+ /////////////////
+ let key = "Unique key: " + Date.now();
+ let value = "Unique value: " + Math.random();
+ let testURL = "about:config";
+
+ // create a new tab
+ let tab = getBrowser().addTab(testURL);
+ ss.setTabValue(tab, key, value);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+ // get the tab's state
+ let state = ss.getTabState(tab);
+ ok(state, "get the tab's state");
+
+ // verify the tab state's integrity
+ state = eval("(" + state + ")");
+ ok(state instanceof Object && state.entries instanceof Array && state.entries.length > 0,
+ "state object seems valid");
+ ok(state.entries.length == 1 && state.entries[0].url == testURL,
+ "Got the expected state object (test URL)");
+ ok(state.extData && state.extData[key] == value,
+ "Got the expected state object (test manually set tab value)");
+
+ // clean up
+ getBrowser().removeTab(tab);
+ }, true);
+
+ //////////////////////////////////
+ // setTabState and duplicateTab //
+ //////////////////////////////////
+ let key2 = "key2";
+ let value2 = "Value " + Math.random();
+ let value3 = "Another value: " + Date.now();
+ let state = { entries: [{ url: testURL }], extData: { key2: value2 } };
+
+ // create a new tab
+ let tab2 = getBrowser().addTab();
+ // set the tab's state
+ ss.setTabState(tab2, JSON.stringify(state));
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ this.removeEventListener("load", testTab2LBLoad, true);
+ // verify the correctness of the restored tab
+ ok(ss.getTabValue(tab2, key2) == value2 && this.currentURI.spec == testURL,
+ "the tab's state was correctly restored");
+
+ // add text data
+ let textbox = this.contentDocument.getElementById("textbox");
+ textbox.value = value3;
+
+ // duplicate the tab
+ let duplicateTab = ss.duplicateTab(window, tab2);
+ getBrowser().removeTab(tab2);
+
+ duplicateTab.linkedBrowser.addEventListener("load", function testTab2DupLBLoad(aEvent) {
+ this.removeEventListener("load", testTab2DupLBLoad, true);
+ // verify the correctness of the duplicated tab
+ ok(ss.getTabValue(duplicateTab, key2) == value2 && this.currentURI.spec == testURL,
+ "correctly duplicated the tab's state");
+ let textbox = this.contentDocument.getElementById("textbox");
+ is(textbox.value, value3, "also duplicated text data");
+
+ // clean up
+ getBrowser().removeTab(duplicateTab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_394759_basic.js b/comm/suite/components/tests/browser/browser_394759_basic.js
new file mode 100644
index 0000000000..a2137cc60c
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_394759_basic.js
@@ -0,0 +1,77 @@
+/* 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/. */
+
+/** Test for Bug 394759, ported in Bug 510890 **/
+
+function test() {
+ waitForExplicitFinish();
+
+ let testURL = "about:config";
+ let uniqueKey = "bug 394759";
+ let uniqueValue = "unik" + Date.now();
+ let uniqueText = "pi != " + Math.random();
+
+ // Be consistent: let the page actually display, as we are "interacting" with it.
+ Services.prefs.setBoolPref("general.warnOnAboutConfig", false);
+
+ // make sure that the next closed window will increase getClosedWindowCount
+ let max_windows_undo = Services.prefs.getIntPref("browser.sessionstore.max_windows_undo");
+ Services.prefs.setIntPref("browser.sessionstore.max_windows_undo", max_windows_undo + 1);
+ let closedWindowCount = ss.getClosedWindowCount();
+
+ provideWindow(function onTestURLLoaded(newWin) {
+ newWin.getBrowser().addTab().linkedBrowser.stop();
+
+ // mark the window with some unique data to be restored later on
+ ss.setWindowValue(newWin, uniqueKey, uniqueValue);
+ let textbox = newWin.content.document.getElementById("textbox");
+ textbox.value = uniqueText;
+
+ newWin.close();
+
+ is(ss.getClosedWindowCount(), closedWindowCount + 1,
+ "The closed window was added to Recently Closed Windows");
+ let data = JSON.parse(ss.getClosedWindowData())[0];
+ ok(data.title == testURL && JSON.stringify(data).includes(uniqueText),
+ "The closed window data was stored correctly");
+
+ // reopen the closed window and ensure its integrity
+ let newWin2 = ss.undoCloseWindow(0);
+
+ ok(newWin2 instanceof ChromeWindow,
+ "undoCloseWindow actually returned a window");
+ is(ss.getClosedWindowCount(), closedWindowCount,
+ "The reopened window was removed from Recently Closed Windows");
+
+ // SSTabRestored will fire more than once, so we need to make sure we count them
+ let restoredTabs = 0;
+ let expectedTabs = data.tabs.length;
+ newWin2.addEventListener("SSTabRestored", function sstabrestoredListener(aEvent) {
+ ++restoredTabs;
+ info("Restored tab " + restoredTabs + "/" + expectedTabs);
+ if (restoredTabs < expectedTabs) {
+ return;
+ }
+
+ newWin2.removeEventListener("SSTabRestored", sstabrestoredListener, true);
+
+ is(newWin2.getBrowser().tabs.length, 2,
+ "The window correctly restored 2 tabs");
+ is(newWin2.getBrowser().currentURI.spec, testURL,
+ "The window correctly restored the URL");
+
+ let textbox = newWin2.content.document.getElementById("textbox");
+ is(textbox.value, uniqueText,
+ "The window correctly restored the form");
+ is(ss.getWindowValue(newWin2, uniqueKey), uniqueValue,
+ "The window correctly restored the data associated with it");
+
+ // clean up
+ newWin2.close();
+ Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
+ Services.prefs.clearUserPref("general.warnOnAboutConfig");
+ finish();
+ }, true);
+ }, testURL);
+}
diff --git a/comm/suite/components/tests/browser/browser_394759_behavior.js b/comm/suite/components/tests/browser/browser_394759_behavior.js
new file mode 100644
index 0000000000..79c70cd937
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_394759_behavior.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+/** Test for Bug 394759, ported in Bug 510890 **/
+
+function test() {
+ // This test takes quite some time, and timeouts frequently, so we require
+ // more time to run.
+ // See Bug 518970.
+ requestLongerTimeout(2);
+
+ waitForExplicitFinish();
+
+ // helper function that does the actual testing
+ function openWindowRec(windowsToOpen, expectedResults, recCallback) {
+ // do actual checking
+ if (!windowsToOpen.length) {
+ let closedWindowData = JSON.parse(ss.getClosedWindowData());
+ let numPopups = closedWindowData.filter(function(el, i, arr) {
+ return el.isPopup;
+ }).length;
+ let numNormal = ss.getClosedWindowCount() - numPopups;
+
+ let oResults = AppConstants.platform == "macosx" ? expectedResults.mac
+ : expectedResults.other;
+ is(numPopups, oResults.popup,
+ "There were " + oResults.popup + " popup windows to repoen");
+ is(numNormal, oResults.normal,
+ "There were " + oResults.normal + " normal windows to repoen");
+
+ // cleanup & return
+ executeSoon(recCallback);
+ return;
+ }
+
+ // hack to force window to be considered a popup (toolbar=no didn't work)
+ let winData = windowsToOpen.shift();
+ let settings = "chrome,dialog=no," +
+ (winData.isPopup ? "all=no" : "all");
+ let url = "http://example.com/?window=" + windowsToOpen.length;
+
+ provideWindow(function onTestURLLoaded(win) {
+ win.close();
+ openWindowRec(windowsToOpen, expectedResults, recCallback);
+ }, url, settings);
+ }
+
+ let windowsToOpen = [{isPopup: false},
+ {isPopup: false},
+ {isPopup: true},
+ {isPopup: true},
+ {isPopup: true}];
+ let expectedResults = {mac: {popup: 3, normal: 0},
+ other: {popup: 3, normal: 1}};
+ let windowsToOpen2 = [{isPopup: false},
+ {isPopup: false},
+ {isPopup: false},
+ {isPopup: false},
+ {isPopup: false}];
+ let expectedResults2 = {mac: {popup: 0, normal: 3},
+ other: {popup: 0, normal: 3}};
+ openWindowRec(windowsToOpen, expectedResults, function() {
+ openWindowRec(windowsToOpen2, expectedResults2, finish);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_408470.js b/comm/suite/components/tests/browser/browser_408470.js
new file mode 100644
index 0000000000..099aa7cbf4
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_408470.js
@@ -0,0 +1,57 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 408470 **/
+
+ waitForExplicitFinish();
+
+ let pendingCount = 1;
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_408470_sample.html";
+ let tab = getBrowser().addTab(testURL);
+ let window = tab.ownerDocument.defaultView;
+
+ tab.linkedBrowser.addEventListener("load", function loadListener1(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", loadListener1, true);
+ // enable all stylesheets and verify that they're correctly persisted
+ Array.from(tab.linkedBrowser.contentDocument.styleSheets).forEach(function(aSS, aIx) {
+ pendingCount++;
+ let ssTitle = aSS.title;
+ stylesheetSwitchAll(tab.linkedBrowser.contentWindow, ssTitle);
+
+ let newTab = ss.duplicateTab(window,tab);
+ newTab.linkedBrowser.addEventListener("load", function loadListener2(aEvent) {
+ newTab.linkedBrowser.removeEventListener("load", loadListener2, true);
+ let states = Array.from(newTab.linkedBrowser.contentDocument.styleSheets,
+ aSS => !aSS.disabled);
+ let correct = states.indexOf(true) == aIx && !states.includes(true, aIx + 1);
+
+ if (/^fail_/.test(ssTitle))
+ ok(!correct, "didn't restore stylesheet " + ssTitle);
+ else
+ ok(correct, "restored stylesheet " + ssTitle);
+
+ getBrowser().removeTab(newTab);
+ if (--pendingCount == 0)
+ finish();
+ }, true);
+ });
+
+ // disable all styles and verify that this is correctly persisted
+ tab.linkedBrowser.markupDocumentViewer.authorStyleDisabled = true;
+ let newTab = ss.duplicateTab(window,tab);
+ newTab.linkedBrowser.addEventListener("load", function loadListener3(aEvent) {
+ newTab.linkedBrowser.removeEventListener("load", loadListener3, true);
+ is(newTab.linkedBrowser.markupDocumentViewer.authorStyleDisabled, true,
+ "disabled all stylesheets");
+
+ getBrowser().removeTab(newTab);
+ if (--pendingCount == 0)
+ finish();
+ }, true);
+
+ getBrowser().removeTab(tab);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_408470_sample.html b/comm/suite/components/tests/browser/browser_408470_sample.html
new file mode 100644
index 0000000000..44122b9453
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_408470_sample.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Test for bug 408470</title>
+
+<link href="404.css" title="default" rel="stylesheet">
+<link href="404.css" title="alternate" rel="alternate stylesheet">
+<link href="404.css" title="altERnate" rel=" styLEsheet altERnate ">
+<link href="404.css" title="media_empty" rel="alternate stylesheet" media="">
+<link href="404.css" title="media_all" rel="alternate stylesheet" media="all">
+<link href="404.css" title="media_ALL" rel="alternate stylesheet" media=" ALL ">
+<link href="404.css" title="media_screen" rel="alternate stylesheet" media="screen">
+<link href="404.css" title="media_print_screen" rel="alternate stylesheet" media="print,screen">
+<link href="404.css" title="fail_media_print" rel="alternate stylesheet" media="print">
+<link href="404.css" title="fail_media_projection" rel="stylesheet" media="projection">
+<link href="404.css" title="fail_media_invalid" rel="alternate stylesheet" media="hallo">
+
+</head>
+<body></body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_423132.js b/comm/suite/components/tests/browser/browser_423132.js
new file mode 100644
index 0000000000..87108f6c6f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_423132.js
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ // test that cookies are stored and restored correctly by sessionstore,
+ // bug 423132, ported by bug 524371.
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ let cs = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
+ cs.removeAll();
+
+ // make sure that sessionstore.js can be forced to be created by setting
+ // the interval pref to 0
+ Services.prefs.setIntPref("browser.sessionstore.interval", 0);
+
+ const testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_423132_sample.html";
+
+ // open a new window
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:blank");
+
+ // make sure sessionstore saves the cookie data, then close the window
+ newWin.addEventListener("load", function testNewWinLoad(aEvent) {
+ newWin.removeEventListener("load", testNewWinLoad);
+
+ newWin.getBrowser().selectedBrowser.loadURI(testURL, null, null);
+
+ newWin.getBrowser().addEventListener("pageshow", function testNewWinPageShow(aEvent) {
+ newWin.getBrowser().removeEventListener("pageshow", testNewWinPageShow, true);
+
+ // get the sessionstore state for the window
+ let state = ss.getWindowState(newWin);
+
+ // verify our cookie got set during pageload
+ let e = cs.enumerator;
+ let cookie;
+ let i = 0;
+ while (e.hasMoreElements()) {
+ cookie = e.getNext().QueryInterface(Ci.nsICookie);
+ i++;
+ }
+ is(i, 1, "expected one cookie");
+
+ // remove the cookie
+ cs.removeAll();
+
+ // restore the window state
+ ss.setWindowState(newWin, state, true);
+
+ // at this point, the cookie should be restored...
+ e = cs.enumerator;
+ let cookie2;
+ while (e.hasMoreElements()) {
+ cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
+ if (cookie.name == cookie2.name)
+ break;
+ }
+ is(cookie.name, cookie2.name, "cookie name successfully restored");
+ is(cookie.value, cookie2.value, "cookie value successfully restored");
+ is(cookie.path, cookie2.path, "cookie path successfully restored");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.interval"))
+ Services.prefs.clearUserPref("browser.sessionstore.interval");
+ cs.removeAll();
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ }, true);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_423132_sample.html b/comm/suite/components/tests/browser/browser_423132_sample.html
new file mode 100644
index 0000000000..bac1866cbc
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_423132_sample.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script>
+ // generate an enormous random number...
+ var r = Math.floor(Math.random() * Math.pow(2, 62)).toString();
+
+ // ... and use it to set a randomly named cookie
+ document.cookie = r + "=value; path=/ohai";
+ </script>
+<body>
+</body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_447951.js b/comm/suite/components/tests/browser/browser_447951.js
new file mode 100644
index 0000000000..259d49a0fa
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_447951.js
@@ -0,0 +1,50 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 447951 **/
+
+ waitForExplicitFinish();
+ const baseURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_447951_sample.html#";
+
+ let tab = getBrowser().addTab();
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+
+ let tabState = { entries: [] };
+ let max_entries = Services.prefs.getIntPref("browser.sessionhistory.max_entries");
+ for (let i = 0; i < max_entries; i++)
+ tabState.entries.push({ url: baseURL + i });
+
+ ss.setTabState(tab, JSON.stringify(tabState));
+ tab.addEventListener("SSTabRestored", function testTabSSTabRestored(aEvent) {
+ tab.removeEventListener("SSTabRestored", testTabSSTabRestored);
+ tabState = JSON.parse(ss.getTabState(tab));
+ is(tabState.entries.length, max_entries, "session history filled to the limit");
+ is(tabState.entries[0].url, baseURL + 0, "... but not more");
+
+ // visit yet another anchor (appending it to session history)
+ let doc = tab.linkedBrowser.contentDocument;
+ let event = doc.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, doc.defaultView, 1,
+ 0, 0, 0, 0, false, false, false, false, 0, null);
+ doc.querySelector("a").dispatchEvent(event);
+
+ executeSoon(function() {
+ tabState = JSON.parse(ss.getTabState(tab));
+ is(tab.linkedBrowser.currentURI.spec, baseURL + "end",
+ "the new anchor was loaded");
+ is(tabState.entries[tabState.entries.length - 1].url, baseURL + "end",
+ "... and ignored");
+ is(tabState.entries[0].url, baseURL + 1,
+ "... and the first item was removed");
+
+ // clean up
+ getBrowser().removeTab(tab);
+ finish();
+ });
+ });
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_447951_sample.html b/comm/suite/components/tests/browser/browser_447951_sample.html
new file mode 100644
index 0000000000..b9ad7bf1f1
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_447951_sample.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<title>Testcase for bug 447951</title>
+
+<a href="#end">click me</a>
diff --git a/comm/suite/components/tests/browser/browser_448741.js b/comm/suite/components/tests/browser/browser_448741.js
new file mode 100644
index 0000000000..85aee816f2
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_448741.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 448741 **/
+
+ waitForExplicitFinish();
+
+ let uniqueName = "bug 448741";
+ let uniqueValue = "as good as unique: " + Date.now();
+
+ // set a unique value on a new, blank tab
+ var tab = getBrowser().addTab();
+ tab.linkedBrowser.stop();
+ ss.setTabValue(tab, uniqueName, uniqueValue);
+ let valueWasCleaned = false;
+
+ // prevent our value from being written to disk
+ function cleaningObserver(aSubject, aTopic, aData) {
+ ok(aTopic == "sessionstore-state-write", "observed correct topic?");
+ ok(aSubject instanceof Ci.nsISupportsString, "subject is a string?");
+ ok(aSubject.data.includes(uniqueValue), "data contains our value?");
+
+ // find the data for the newly added tab and delete it
+ let state = JSON.parse(aSubject.data);
+ state.windows.forEach(function (winData) {
+ winData.tabs.forEach(function (tabData) {
+ if (tabData.extData && uniqueName in tabData.extData &&
+ tabData.extData[uniqueName] == uniqueValue) {
+ delete tabData.extData[uniqueName];
+ valueWasCleaned = true;
+ }
+ });
+ });
+
+ ok(valueWasCleaned, "found and removed the specific tab value");
+ aSubject.data = JSON.stringify(state);
+ Services.obs.removeObserver(cleaningObserver, aTopic, false);
+ }
+
+ // make sure that all later observers don't see that value any longer
+ function checkingObserver(aSubject, aTopic, aData) {
+ ok(valueWasCleaned && aSubject instanceof Ci.nsISupportsString,
+ "ready to check the cleaned state?");
+ ok(!aSubject.data.includes(uniqueValue), "data no longer contains our value?");
+
+ // clean up
+ getBrowser().removeTab(tab);
+ Services.obs.removeObserver(checkingObserver, aTopic, false);
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.interval"))
+ Services.prefs.clearUserPref("browser.sessionstore.interval");
+ finish();
+ }
+
+ // last added observers are invoked first
+ Services.obs.addObserver(checkingObserver, "sessionstore-state-write");
+ Services.obs.addObserver(cleaningObserver, "sessionstore-state-write");
+
+ // trigger an immediate save operation
+ Services.prefs.setIntPref("browser.sessionstore.interval", 0);
+}
diff --git a/comm/suite/components/tests/browser/browser_454908.js b/comm/suite/components/tests/browser/browser_454908.js
new file mode 100644
index 0000000000..e5ce5f932e
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_454908.js
@@ -0,0 +1,52 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 454908 **/
+
+ waitForExplicitFinish();
+
+ let fieldValues = {
+ username: "User " + Math.random(),
+ passwd: "pwd" + Date.now()
+ };
+
+ // make sure we do save form data
+ Services.prefs.setIntPref("browser.sessionstore.privacy_level", 0);
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_454908_sample.html";
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ for (let id in fieldValues)
+ doc.getElementById(id).value = fieldValues[id];
+
+ getBrowser().removeTab(tab);
+
+ tab = getBrowser().undoCloseTab();
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad2(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad2, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ for (let id in fieldValues) {
+ let node = doc.getElementById(id);
+ if (node.type == "password")
+ is(node.value, "", "password wasn't saved/restored");
+ else
+ is(node.value, fieldValues[id], "username was saved/restored");
+ }
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.privacy_level"))
+ Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+ // undoCloseTab can reuse a single blank tab, so we have to
+ // make sure not to close the window when closing our last tab
+ if (gBrowser.tabContainer.childNodes.length == 1)
+ gBrowser.addTab();
+ gBrowser.removeTab(tab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_454908_sample.html b/comm/suite/components/tests/browser/browser_454908_sample.html
new file mode 100644
index 0000000000..02f40bf20b
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_454908_sample.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<title>Test for bug 454908</title>
+
+<h3>Dummy Login</h3>
+<form>
+<p>Username: <input type="text" id="username">
+<p>Password: <input type="password" id="passwd">
+</form>
diff --git a/comm/suite/components/tests/browser/browser_456342.js b/comm/suite/components/tests/browser/browser_456342.js
new file mode 100644
index 0000000000..86bcb0ef06
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_456342.js
@@ -0,0 +1,47 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 456342 **/
+
+ waitForExplicitFinish();
+
+ // make sure we do save form data
+ Services.prefs.setIntPref("browser.sessionstore.privacy_level", 0);
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_456342_sample.xhtml";
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ this.removeEventListener("load", testTabLBLoad, true);
+
+ let expectedValue = "try to save me";
+ // Since bug 537289 we only save non-default values, so we need to set each
+ // form field's value after load.
+ let formEls = aEvent.originalTarget.forms[0].elements;
+ for (let i = 0; i < formEls.length; i++)
+ formEls[i].value = expectedValue;
+
+ getBrowser().removeTab(tab);
+
+ let undoItems = JSON.parse(ss.getClosedTabData(window));
+ let savedFormData = undoItems[0].state.entries[0].formdata;
+
+ let countGood = 0, countBad = 0;
+ for (let value of Object.values(savedFormData)) {
+ if (value == expectedValue)
+ countGood++;
+ else
+ countBad++;
+ }
+
+ is(countGood, 4, "Saved text for non-standard input fields");
+ is(countBad, 0, "Didn't save text for ignored field types");
+
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.privacy_level"))
+ Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+ finish();
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_456342_sample.xhtml b/comm/suite/components/tests/browser/browser_456342_sample.xhtml
new file mode 100644
index 0000000000..f0b0005b77
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_456342_sample.xhtml
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head><title>Test for bug 456342</title></head>
+
+<body>
+<form>
+<h3>Non-standard &lt;input&gt;s</h3>
+<p>Search <input type="search" id="searchTerm"/></p>
+<p>Image Search: <input type="image search" /></p>
+<p>Autocomplete: <input type="autocomplete" name="fill-in"/></p>
+<p>Mistyped: <input type="txet" name="mistyped"/></p>
+
+<h3>Ignored types</h3>
+<input type="hidden" name="hideme"/>
+<input type="HIDDEN" name="hideme2"/>
+<input type="submit" name="submit"/>
+<input type="reset" name="reset"/>
+<input type="image" name="image"/>
+<input type="button" name="button"/>
+<input type="password" name="password"/>
+<input type="PassWord" name="password2"/>
+<input type="PASSWORD" name="password3"/>
+</form>
+
+</body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_461634.js b/comm/suite/components/tests/browser/browser_461634.js
new file mode 100644
index 0000000000..28207e7e6b
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_461634.js
@@ -0,0 +1,88 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 461634, ported by Bug 524345 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ const REMEMBER = Date.now(), FORGET = Math.random();
+ let test_state = { windows: [{ "tabs": [{ "entries": [] }], _closedTabs: [
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: REMEMBER },
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
+ { state: { entries: [{ url: "http://www.example.net/" }] }, title: REMEMBER },
+ ] }] };
+ let remember_count = 2;
+
+ function countByTitle(aClosedTabList, aTitle) {
+ return aClosedTabList.filter(aData => aData.title == aTitle).length;
+ }
+
+ function testForError(aFunction) {
+ try {
+ aFunction();
+ return false;
+ }
+ catch (ex) {
+ return ex.name == "NS_ERROR_ILLEGAL_VALUE";
+ }
+ }
+
+ // open a window and add the above closed tab list
+ let newWin = openDialog(location, "", "chrome,all,dialog=no");
+ newWin.addEventListener("load", function loadListener(aEvent) {
+ newWin.removeEventListener("load", loadListener);
+
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo",
+ test_state.windows[0]._closedTabs.length);
+ ss.setWindowState(newWin, JSON.stringify(test_state), true);
+
+ let closedTabs = JSON.parse(ss.getClosedTabData(newWin));
+ is(closedTabs.length, test_state.windows[0]._closedTabs.length,
+ "Closed tab list has the expected length");
+ is(countByTitle(closedTabs, FORGET),
+ test_state.windows[0]._closedTabs.length - remember_count,
+ "The correct amout of tabs are to be forgotten");
+ is(countByTitle(closedTabs, REMEMBER), remember_count,
+ "Everything is set up.");
+
+ // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+ ok(testForError(() => ss.forgetClosedTab({}, 0)),
+ "Invalid window for forgetClosedTab throws");
+ ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
+ "Invalid tab for forgetClosedTab throws");
+ ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
+ "Invalid tab for forgetClosedTab throws");
+
+ // Remove third tab, then first tab
+ ss.forgetClosedTab(newWin, 2);
+ ss.forgetClosedTab(newWin, null);
+
+ closedTabs = JSON.parse(ss.getClosedTabData(newWin));
+ is(closedTabs.length, remember_count,
+ "The correct amout of tabs was removed");
+ is(countByTitle(closedTabs, FORGET), 0,
+ "All tabs specifically forgotten were indeed removed");
+ is(countByTitle(closedTabs, REMEMBER), remember_count,
+ "... and tabs not specifically forgetten weren't.");
+
+ // clean up
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+ finish();
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_463206.js b/comm/suite/components/tests/browser/browser_463206.js
new file mode 100644
index 0000000000..c044787546
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_463206.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 463206 **/
+
+ waitForExplicitFinish();
+
+ let testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_463206_sample.html";
+
+ var frameCount = 0;
+ let tab = getBrowser().addTab(testURL);
+ let window = tab.ownerDocument.defaultView;
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ // wait for all frames to load completely
+ if (frameCount++ < 5)
+ return;
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ function typeText(aTextField, aValue) {
+ aTextField.value = aValue;
+
+ let event = aTextField.ownerDocument.createEvent("UIEvents");
+ event.initUIEvent("input", true, true, aTextField.ownerDocument.defaultView, 0);
+ aTextField.dispatchEvent(event);
+ }
+
+ let doc = tab.linkedBrowser.contentDocument;
+ typeText(doc.getElementById("out1"), Date.now());
+ typeText(doc.getElementsByName("1|#out2")[0], Math.random());
+ typeText(doc.defaultView.frames[0].frames[1].document.getElementById("in1"), new Date());
+
+ frameCount = 0;
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ // wait for all frames to load completely
+ if (frameCount++ < 5)
+ return;
+ tab2.linkedBrowser.removeEventListener("load", testTab2LBLoad, true);
+
+ let doc = tab2.linkedBrowser.contentDocument;
+ let win = tab2.linkedBrowser.contentWindow;
+ isnot(doc.getElementById("out1").value,
+ win.frames[1].document.getElementById("out1").value,
+ "text isn't reused for frames");
+ isnot(doc.getElementsByName("1|#out2")[0].value, "",
+ "text containing | and # is correctly restored");
+ is(win.frames[1].document.getElementById("out2").value, "",
+ "id prefixes can't be faked");
+ // Disabled for now, Bug 588077
+ // isnot(win.frames[0].frames[1].document.getElementById("in1").value, "",
+ // "id prefixes aren't mixed up");
+ is(win.frames[1].frames[0].document.getElementById("in1").value, "",
+ "id prefixes aren't mixed up");
+
+ // clean up
+ getBrowser().removeTab(tab2);
+ getBrowser().removeTab(tab);
+
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_463206_sample.html b/comm/suite/components/tests/browser/browser_463206_sample.html
new file mode 100644
index 0000000000..48a841ee69
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_463206_sample.html
@@ -0,0 +1,10 @@
+<!-- Testcase originally by <moz_bug_r_a4@yahoo.com> -->
+
+<!DOCTYPE html>
+<title>Test for bug 463206</title>
+
+<iframe src="data:text/html,<iframe></iframe><iframe%20src='data:text/html,<input%2520id=%2522in1%2522>'></iframe>"></iframe>
+<iframe src="data:text/html,<input%20id='out1'><input%20id='out2'><iframe%20src='data:text/html,<input%2520id=%2522in1%2522>'>"></iframe>
+
+<input id="out1">
+<input name="1|#out2">
diff --git a/comm/suite/components/tests/browser/browser_465215.js b/comm/suite/components/tests/browser/browser_465215.js
new file mode 100644
index 0000000000..f34bd780b0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_465215.js
@@ -0,0 +1,39 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 465215 **/
+
+ waitForExplicitFinish();
+
+ let uniqueName = "bug 465215";
+ let uniqueValue1 = "as good as unique: " + Date.now();
+ let uniqueValue2 = "as good as unique: " + Math.random();
+
+ // set a unique value on a new, blank tab
+ let tab1 = gBrowser.addTab();
+ tab1.linkedBrowser.addEventListener("load", function testTab1LBLoad() {
+ tab1.linkedBrowser.removeEventListener("load", testTab1LBLoad, true);
+ ss.setTabValue(tab1, uniqueName, uniqueValue1);
+
+ // duplicate the tab with that value
+ let tab2 = ss.duplicateTab(window, tab1);
+ is(ss.getTabValue(tab2, uniqueName), uniqueValue1, "tab value was duplicated");
+
+ ss.setTabValue(tab2, uniqueName, uniqueValue2);
+ isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
+
+ // overwrite the tab with the value which should remove it
+ ss.setTabState(tab1, JSON.stringify({ entries: [] }));
+ tab1.linkedBrowser.addEventListener("load", function testTab1LBLoad2() {
+ tab1.linkedBrowser.removeEventListener("load", testTab1LBLoad2, true);
+ is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
+
+ // clean up
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab1);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_465223.js b/comm/suite/components/tests/browser/browser_465223.js
new file mode 100644
index 0000000000..89e1e69042
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_465223.js
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 465223 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ let uniqueKey1 = "bug 465223.1";
+ let uniqueKey2 = "bug 465223.2";
+ let uniqueValue1 = "unik" + Date.now();
+ let uniqueValue2 = "pi != " + Math.random();
+
+ // open a window and set a value on it
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
+ newWin.addEventListener("load", function loadListener(aEvent) {
+ newWin.removeEventListener("load", loadListener);
+
+ ss.setWindowValue(newWin, uniqueKey1, uniqueValue1);
+
+ let newState = { windows: [{ tabs:[{ entries: [] }], extData: {} }] };
+ newState.windows[0].extData[uniqueKey2] = uniqueValue2;
+ ss.setWindowState(newWin, JSON.stringify(newState), false);
+
+ is(newWin.gBrowser.tabContainer.childNodes.length, 2,
+ "original tab wasn't overwritten");
+ is(ss.getWindowValue(newWin, uniqueKey1), uniqueValue1,
+ "window value wasn't overwritten when the tabs weren't");
+ is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue2,
+ "new window value was correctly added");
+
+ newState.windows[0].extData[uniqueKey2] = uniqueValue1;
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ is(newWin.gBrowser.tabContainer.childNodes.length, 1,
+ "original tabs were overwritten");
+ is(ss.getWindowValue(newWin, uniqueKey1), "",
+ "window value was cleared");
+ is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue1,
+ "window value was correctly overwritten");
+
+ // clean up
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_466937.js b/comm/suite/components/tests/browser/browser_466937.js
new file mode 100644
index 0000000000..8d34f65aeb
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_466937.js
@@ -0,0 +1,43 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 466937 **/
+
+ waitForExplicitFinish();
+
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("466937_test.file");
+ let testPath = file.path;
+
+ let testURL = "http://mochi.test:8888/browser/" +
+ "suite/common/tests/browser/browser_466937_sample.html";
+
+ let tab = getBrowser().addTab(testURL);
+ let window = tab.ownerDocument.defaultView;
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ doc.getElementById("reverse_thief").value = "/home/user/secret2";
+ doc.getElementById("bystander").value = testPath;
+
+ let tab2 = ss.duplicateTab(window,tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ tab2.linkedBrowser.removeEventListener("load", testTab2LBLoad, true);
+ doc = tab2.linkedBrowser.contentDocument;
+ is(doc.getElementById("thief").value, "",
+ "file path wasn't set to text field value");
+ is(doc.getElementById("reverse_thief").value, "",
+ "text field value wasn't set to full file path");
+ is(doc.getElementById("bystander").value, testPath,
+ "normal case: file path was correctly preserved");
+
+ // clean up
+ gBrowser.removeTab(tab2);
+ gBrowser.removeTab(tab);
+
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_466937_sample.html b/comm/suite/components/tests/browser/browser_466937_sample.html
new file mode 100644
index 0000000000..f876719987
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_466937_sample.html
@@ -0,0 +1,21 @@
+<!-- Testcase originally by <moz_bug_r_a4@yahoo.com> -->
+
+<!DOCTYPE html>
+<title>Test for bug 466937</title>
+
+<input id="thief" value="/home/user/secret">
+<input type="file" id="reverse_thief">
+<input type="file" id="bystander">
+
+<script>
+ window.addEventListener("DOMContentLoaded", function windowDOMContentLoaded() {
+ window.removeEventListener("DOMContentLoaded", windowDOMContentLoaded);
+ if (!document.location.hash) {
+ document.location.hash = "#ready";
+ }
+ else {
+ document.getElementById("thief").type = "file";
+ document.getElementById("reverse_thief").type = "text";
+ }
+ });
+</script>
diff --git a/comm/suite/components/tests/browser/browser_477657.js b/comm/suite/components/tests/browser/browser_477657.js
new file mode 100644
index 0000000000..23683a8b12
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_477657.js
@@ -0,0 +1,85 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 477657 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ // Test fails randomly on OS X (bug 482975)
+ if ("nsILocalFileMac" in Ci)
+ return;
+
+ waitForExplicitFinish();
+
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
+ newWin.addEventListener("load", function loadListener(aEvent) {
+ newWin.removeEventListener("load", loadListener);
+
+ let newState = { windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: [{
+ state: { entries: [{ url: "about:" }]},
+ title: "About:"
+ }],
+ sizemode: "maximized"
+ }] };
+
+ let uniqueKey = "bug 477657";
+ let uniqueValue = "unik" + Date.now();
+
+ ss.setWindowValue(newWin, uniqueKey, uniqueValue);
+ is(ss.getWindowValue(newWin, uniqueKey), uniqueValue,
+ "window value was set before the window was overwritten");
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ // use setTimeout(..., 0) to mirror sss_restoreWindowFeatures
+ setTimeout(function() {
+ is(ss.getWindowValue(newWin, uniqueKey), "",
+ "window value was implicitly cleared");
+
+ is(newWin.windowState, newWin.STATE_MAXIMIZED,
+ "the window was maximized");
+
+ is(JSON.parse(ss.getClosedTabData(newWin)).length, 1,
+ "the closed tab was added before the window was overwritten");
+ delete newState.windows[0]._closedTabs;
+ delete newState.windows[0].sizemode;
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ setTimeout(function() {
+ is(JSON.parse(ss.getClosedTabData(newWin)).length, 0,
+ "closed tabs were implicitly cleared");
+
+ is(newWin.windowState, newWin.STATE_MAXIMIZED,
+ "the window remains maximized");
+ newState.windows[0].sizemode = "normal";
+ ss.setWindowState(newWin, JSON.stringify(newState), true);
+
+ setTimeout(function() {
+ isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
+ "the window was explicitly unmaximized");
+
+ newWin.close();
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ }, 0);
+ }, 0);
+ }, 0);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_480893.js b/comm/suite/components/tests/browser/browser_480893.js
new file mode 100644
index 0000000000..41de93c8f1
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_480893.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function test() {
+ /** Test for Bug 480893 **/
+
+ waitForExplicitFinish();
+
+ // Test that starting a new session loads a blank page if Firefox is
+ // configured to display a blank page at startup (browser.startup.page = 0)
+ Services.prefs.setIntPref("browser.startup.page", 0);
+ let tab = getBrowser().addTab("about:sessionrestore");
+ getBrowser().selectedTab = tab;
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function testBrowserLoad(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad, true);
+ let doc = browser.contentDocument;
+
+ // click on the "Start New Session" button after about:sessionrestore is loaded
+ doc.getElementById("errorCancel").click();
+ browser.addEventListener("load", function testBrowserLoad2(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad2, true);
+ let doc = browser.contentDocument;
+
+ is(doc.URL, "about:blank", "loaded page is about:blank");
+
+ // Test that starting a new session loads the homepage (set to http://mochi.test:8888)
+ // if Firefox is configured to display a homepage at startup (browser.startup.page = 1)
+ let homepage = "http://mochi.test:8888/";
+ Services.prefs.setCharPref("browser.startup.homepage", homepage);
+ Services.prefs.setIntPref("browser.startup.page", 1);
+ getBrowser().loadURI("about:sessionrestore");
+ browser.addEventListener("load", function testBrowserLoad3(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad3, true);
+ let doc = browser.contentDocument;
+
+ // click on the "Start New Session" button after about:sessionrestore is loaded
+ doc.getElementById("errorCancel").click();
+ browser.addEventListener("load", function testBrowserLoad4(aEvent) {
+ browser.removeEventListener("load", testBrowserLoad4, true);
+ let doc = browser.contentDocument;
+
+ is(doc.URL, homepage, "loaded page is the homepage");
+
+ // close tab, restore default values and finish the test
+ getBrowser().removeTab(tab);
+ // we need this if-statement because if there is no user set value,
+ // clearUserPref throws a uncatched exception and finish is not called
+ if (Services.prefs.prefHasUserValue("browser.startup.page"))
+ Services.prefs.clearUserPref("browser.startup.page");
+ Services.prefs.clearUserPref("browser.startup.homepage");
+ finish();
+ }, true);
+ }, true);
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_483330.js b/comm/suite/components/tests/browser/browser_483330.js
new file mode 100644
index 0000000000..3e650488b7
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_483330.js
@@ -0,0 +1,37 @@
+function test() {
+ /** Test for Bug 483330 **/
+
+ waitForExplicitFinish();
+
+ let tab = getBrowser().addTab();
+ getBrowser().selectedTab = tab;
+
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function loadListener(e) {
+ browser.removeEventListener("load", loadListener, true);
+
+ // Scroll the content document
+ browser.contentWindow.scrollTo(1100, 1200);
+ is(browser.contentWindow.scrollX, 1100, "scrolled horizontally");
+ is(browser.contentWindow.scrollY, 1200, "scrolled vertically");
+
+ getBrowser().removeTab(tab);
+
+ let newTab = ss.undoCloseTab(window, 0);
+ newTab.addEventListener("SSTabRestored", function tabRestored(e) {
+ newTab.removeEventListener("SSTabRestored", tabRestored, true);
+
+ let newBrowser = newTab.linkedBrowser;
+
+ // check that the scroll position was restored
+ is(newBrowser.contentWindow.scrollX, 1100, "still scrolled horizontally");
+ is(newBrowser.contentWindow.scrollY, 1200, "still scrolled vertically");
+
+ getBrowser().removeTab(newTab);
+
+ finish();
+ }, true);
+ }, true);
+
+ browser.loadURI("data:text/html,<body style='width: 100000px; height: 100000px;'><p>top</p></body>");
+}
diff --git a/comm/suite/components/tests/browser/browser_485482.js b/comm/suite/components/tests/browser/browser_485482.js
new file mode 100644
index 0000000000..6e4573f609
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_485482.js
@@ -0,0 +1,36 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 485482, ported by Bug 487922 **/
+
+ waitForExplicitFinish();
+
+ let uniqueValue = Math.random();
+
+ let rootDir = getRootDirectory(gTestPath);
+ let testURL = rootDir + "browser_485482_sample.html";
+ let tab = getBrowser().addTab(testURL);
+ tab.linkedBrowser.addEventListener("load", function testTabLBLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", testTabLBLoad, true);
+ let doc = tab.linkedBrowser.contentDocument;
+ doc.querySelector("input[type=text]").value = uniqueValue;
+ doc.querySelector("input[type=checkbox]").checked = true;
+
+ let tab2 = ss.duplicateTab(window, tab);
+ tab2.linkedBrowser.addEventListener("load", function testTab2LBLoad(aEvent) {
+ tab2.linkedBrowser.removeEventListener("load", testTab2LBLoad, true);
+ doc = tab2.linkedBrowser.contentDocument;
+ is(doc.querySelector("input[type=text]").value, uniqueValue,
+ "generated XPath expression was valid");
+ ok(doc.querySelector("input[type=checkbox]").checked,
+ "generated XPath expression was valid");
+
+ // clean up
+ getBrowser().removeTab(tab2);
+ getBrowser().removeTab(tab);
+ finish();
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_485482_sample.html b/comm/suite/components/tests/browser/browser_485482_sample.html
new file mode 100644
index 0000000000..c2097b5930
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_485482_sample.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>Test for bug 485482</title>
+
+<bad=name>
+ <input type="text">
+</bad=name>
+
+<worse=name>
+ <l0c@l+na~e"'§>
+ <input type="checkbox" name="check"> Check
+ </l0c@l+na~e"'§>
+</worse=name>
diff --git a/comm/suite/components/tests/browser/browser_490040.js b/comm/suite/components/tests/browser/browser_490040.js
new file mode 100644
index 0000000000..91687058f6
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_490040.js
@@ -0,0 +1,139 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 490040, ported by Bug 511640 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ function testWithState(aState) {
+ // Ensure we can store the window if needed.
+ let curClosedWindowCount = ss.getClosedWindowCount();
+ Services.prefs.setIntPref("browser.sessionstore.max_windows_undo",
+ curClosedWindowCount + 1);
+
+ var origWin;
+ function windowObserver(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ if (origWin && theWin != origWin)
+ return;
+
+ switch (aTopic) {
+ case "domwindowopened":
+ origWin = theWin;
+ theWin.addEventListener("load", function testTheWinLoad() {
+ theWin.removeEventListener("load", testTheWinLoad);
+ executeSoon(function () {
+ // Close the window as soon as the first tab loads, or
+ // immediately if there are no tabs.
+ if (aState.windowState.windows[0].tabs[0].entries.length) {
+ theWin.gBrowser.addEventListener("load",
+ function testTheWinLoad2() {
+ theWin.gBrowser.removeEventListener("load", testTheWinLoad2,
+ true);
+ theWin.close();
+ }, true);
+ } else {
+ executeSoon(function () {
+ theWin.close();
+ });
+ }
+ ss.setWindowState(theWin, JSON.stringify(aState.windowState),
+ true);
+ });
+ });
+ break;
+
+ case "domwindowclosed":
+ Services.ww.unregisterNotification(windowObserver);
+ // Use executeSoon to ensure this happens after SS observer.
+ executeSoon(function () {
+ is(ss.getClosedWindowCount(),
+ curClosedWindowCount + (aState.shouldBeAdded ? 1 : 0),
+ "That window should " + (aState.shouldBeAdded ? "" : "not ") +
+ "be restorable");
+ executeSoon(runNextTest);
+ });
+ break;
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+ Services.ww.openWindow(null,
+ location,
+ "_blank",
+ "chrome,all,dialog=no",
+ null);
+ }
+
+ // Only windows with open tabs are restorable. Windows where a lone tab is
+ // detached may have _closedTabs, but is left with just an empty tab.
+ let states = [
+ {
+ shouldBeAdded: true,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [{ url: "http://example.com", title: "example.com" }] }],
+ selected: 1,
+ _closedTabs: []
+ }]
+ }
+ },
+ {
+ shouldBeAdded: false,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: []
+ }]
+ }
+ },
+ {
+ shouldBeAdded: false,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: [{ state: { entries: [{ url: "http://example.com", index: 1 }] } }]
+ }]
+ }
+ },
+ {
+ shouldBeAdded: false,
+ windowState: {
+ windows: [{
+ tabs: [{ entries: [] }],
+ _closedTabs: [],
+ extData: { keyname: "pi != " + Math.random() }
+ }]
+ }
+ }
+ ];
+
+ function runNextTest() {
+ if (states.length) {
+ let state = states.shift();
+ testWithState(state);
+ }
+ else {
+ if (Services.prefs.prefHasUserValue("browser.sessionstore.max_windows_undo"))
+ Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ }
+ }
+ runNextTest();
+}
+
diff --git a/comm/suite/components/tests/browser/browser_491168.js b/comm/suite/components/tests/browser/browser_491168.js
new file mode 100644
index 0000000000..82ed998e99
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_491168.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ // make sure we use sessionstore for undoClosetab
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+
+ /** Test for Bug 491168, ported by Bug 524369 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+
+ const REFERRER1 = "http://example.org/?" + Date.now();
+ const REFERRER2 = "http://example.org/?" + Math.random();
+
+ let tab = getBrowser().addTab();
+ getBrowser().selectedTab = tab;
+
+ let browser = tab.linkedBrowser;
+ browser.addEventListener("load", function testBrowserLoad() {
+ browser.removeEventListener("load", testBrowserLoad, true);
+
+ let tabState = JSON.parse(ss.getTabState(tab));
+ is(tabState.entries[0].referrer, REFERRER1,
+ "Referrer retrieved via getTabState matches referrer set via loadURI.");
+
+ tabState.entries[0].referrer = REFERRER2;
+ ss.setTabState(tab, JSON.stringify(tabState));
+
+ tab.addEventListener("SSTabRestored", function testBrowserTabRestored() {
+ tab.removeEventListener("SSTabRestored", testBrowserTabRestored, true);
+ is(window.content.document.referrer, REFERRER2, "document.referrer matches referrer set via setTabState.");
+
+ getBrowser().removeTab(tab);
+ let newTab = ss.undoCloseTab(window, 0);
+ newTab.addEventListener("SSTabRestored", function testBrowserNewTabRest() {
+ newTab.removeEventListener("SSTabRestored", testBrowserNewTabRest, true);
+
+ is(window.content.document.referrer, REFERRER2, "document.referrer is still correct after closing and reopening the tab.");
+ getBrowser().removeTab(newTab);
+
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ // clean up
+ if (Services.prefs.prefHasUserValue("browser.tabs.max_tabs_undo"))
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ finish();
+ }, true);
+ }, true);
+ },true);
+
+ let referrerURI = Services.io.newURI(REFERRER1);
+ browser.loadURI("http://example.org", referrerURI, null);
+}
diff --git a/comm/suite/components/tests/browser/browser_491577.js b/comm/suite/components/tests/browser/browser_491577.js
new file mode 100644
index 0000000000..f65590abaf
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_491577.js
@@ -0,0 +1,119 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 491577 **/
+
+ // test setup
+ waitForExplicitFinish();
+
+ const REMEMBER = Date.now(), FORGET = Math.random();
+ let test_state = {
+ windows: [ { tabs: [{ entries: [{ url: "http://example.com/", triggeringPrincipal_base64 }] }], selected: 1 } ],
+ _closedWindows: [
+ // _closedWindows[0]
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] },
+ { entries: [{ url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" }] }
+ ],
+ selected: 2,
+ title: FORGET,
+ _closedTabs: []
+ },
+ // _closedWindows[1]
+ {
+ tabs: [
+ { entries: [{ url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" }] },
+ { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] },
+ { entries: [{ url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" }] },
+ ],
+ selected: 3,
+ title: REMEMBER,
+ _closedTabs: []
+ },
+ // _closedWindows[2]
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] }
+ ],
+ selected: 1,
+ title: FORGET,
+ _closedTabs: [
+ {
+ state: {
+ entries: [
+ { url: "http://mozilla.org/", triggeringPrincipal_base64, title: "title" },
+ { url: "http://mozilla.org/again", triggeringPrincipal_base64, title: "title" }
+ ]
+ },
+ pos: 1,
+ title: "title"
+ },
+ {
+ state: {
+ entries: [
+ { url: "http://example.com", triggeringPrincipal_base64, title: "title" }
+ ]
+ },
+ title: "title"
+ }
+ ]
+ }
+ ]
+ };
+ let remember_count = 1;
+
+ function countByTitle(aClosedWindowList, aTitle) {
+ return aClosedWindowList.filter(aData => aData.title == aTitle).length;
+ }
+
+ function testForError(aFunction) {
+ try {
+ aFunction();
+ return false;
+ } catch (ex) {
+ return ex.name == "NS_ERROR_ILLEGAL_VALUE";
+ }
+ }
+
+ // open a window and add the above closed window list
+ let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
+ promiseWindowLoaded(newWin).then(() => {
+ gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
+ test_state._closedWindows.length);
+ ss.setWindowState(newWin, JSON.stringify(test_state), true);
+
+ let closedWindows = JSON.parse(ss.getClosedWindowData());
+ is(closedWindows.length, test_state._closedWindows.length,
+ "Closed window list has the expected length");
+ is(countByTitle(closedWindows, FORGET),
+ test_state._closedWindows.length - remember_count,
+ "The correct amount of windows are to be forgotten");
+ is(countByTitle(closedWindows, REMEMBER), remember_count,
+ "Everything is set up.");
+
+ // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+ ok(testForError(() => ss.forgetClosedWindow(-1)),
+ "Invalid window for forgetClosedWindow throws");
+ ok(testForError(() => ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
+ "Invalid window for forgetClosedWindow throws");
+
+ // Remove third window, then first window
+ ss.forgetClosedWindow(2);
+ ss.forgetClosedWindow(null);
+
+ closedWindows = JSON.parse(ss.getClosedWindowData());
+ is(closedWindows.length, remember_count,
+ "The correct amount of windows were removed");
+ is(countByTitle(closedWindows, FORGET), 0,
+ "All windows specifically forgotten were indeed removed");
+ is(countByTitle(closedWindows, REMEMBER), remember_count,
+ "... and windows not specifically forgetten weren't.");
+
+ // clean up
+ gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
+ BrowserTestUtils.closeWindow(newWin).then(finish);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_493467.js b/comm/suite/components/tests/browser/browser_493467.js
new file mode 100644
index 0000000000..1b8f5f78d8
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_493467.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 493467, ported by Bug 524365 **/
+
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ let tab = getBrowser().addTab();
+ tab.linkedBrowser.stop();
+ let tabState = JSON.parse(ss.getTabState(tab));
+ is(tabState.disallow || "", "", "Everything is allowed per default");
+
+ // collect all permissions that can be set on a docShell (i.e. all
+ // attributes starting with "allow" such as "allowJavascript") and
+ // disallow them all, as SessionStore only remembers disallowed ones
+ let permissions = [];
+ let docShell = tab.linkedBrowser.docShell;
+ for (let attribute in docShell) {
+ if (/^allow([A-Z].*)/.test(attribute)) {
+ permissions.push(RegExp.$1);
+ docShell[attribute] = false;
+ }
+ }
+
+ // make sure that all available permissions have been remembered
+ tabState = JSON.parse(ss.getTabState(tab));
+ let disallow = tabState.disallow.split(",");
+ permissions.forEach(function(aName) {
+ ok(disallow.includes(aName), "Saved state of allow" + aName);
+ });
+ // IF A TEST FAILS, please add the missing permission's name (without the
+ // leading "allow") to nsSessionStore.js's CAPABILITIES array. Thanks.
+
+ getBrowser().removeTab(tab);
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+}
diff --git a/comm/suite/components/tests/browser/browser_500328.js b/comm/suite/components/tests/browser/browser_500328.js
new file mode 100644
index 0000000000..2286a5f6c3
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_500328.js
@@ -0,0 +1,115 @@
+/* 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/. */
+
+function checkState(tab) {
+ // Go back and then forward, and make sure that the state objects received
+ // from the popState event are as we expect them to be.
+ //
+ // We also add a node to the document's body when after going back and make
+ // sure it's still there after we go forward -- this is to test that the two
+ // history entries correspond to the same document.
+
+ let popStateCount = 0;
+
+ tab.linkedBrowser.addEventListener('popstate', function checkStateTabPopState(aEvent) {
+ let contentWindow = tab.linkedBrowser.contentWindow;
+ if (popStateCount == 0) {
+ popStateCount++;
+
+ is(tab.linkedBrowser.contentWindow.testState, 'foo',
+ 'testState after going back');
+
+ ok(aEvent.state, "Event should have a state property.");
+ is(JSON.stringify(tab.linkedBrowser.contentWindow.history.state), JSON.stringify({obj1:1}),
+ "first popstate object.");
+
+ // Add a node with id "new-elem" to the document.
+ let doc = contentWindow.document;
+ ok(!doc.getElementById("new-elem"),
+ "doc shouldn't contain new-elem before we add it.");
+ let elem = doc.createElement("div");
+ elem.id = "new-elem";
+ doc.body.appendChild(elem);
+
+ contentWindow.history.forward();
+ }
+ else if (popStateCount == 1) {
+ popStateCount++;
+ is(aEvent.state.obj3.toString(), '/^a$/', "second popstate object.");
+
+ // Make sure that the new-elem node is present in the document. If it's
+ // not, then this history entry has a different doc identifier than the
+ // previous entry, which is bad.
+ let doc = contentWindow.document;
+ let newElem = doc.getElementById("new-elem");
+ ok(newElem, "doc should contain new-elem.");
+ newElem.remove();
+ ok(!doc.getElementById("new-elem"), "new-elem should be removed.");
+
+ // Clean up after ourselves and finish the test.
+ tab.linkedBrowser.removeEventListener("popstate", checkStateTabPopState,
+ true);
+ getBrowser().removeTab(tab);
+ finish();
+ }
+ }, true);
+
+ // Set some state in the page's window. When we go back(), the page should
+ // be retrieved from bfcache, and this state should still be there.
+ tab.linkedBrowser.contentWindow.testState = 'foo';
+
+ // Now go back. This should trigger the popstate event handler above.
+ tab.linkedBrowser.contentWindow.history.back();
+}
+
+function test() {
+ // Tests session restore functionality of history.pushState and
+ // history.replaceState(). (Bug 500328)
+
+ waitForExplicitFinish();
+
+ // We open a new blank window, let it load, and then load in
+ // http://example.com. We need to load the blank window first, otherwise the
+ // docshell gets confused and doesn't have a current history entry.
+ let tab = getBrowser().addTab("about:blank");
+ let tabBrowser = tab.linkedBrowser;
+
+ tabBrowser.addEventListener("load", function testTabBrowserLoad(aEvent) {
+ tabBrowser.removeEventListener("load", testTabBrowserLoad, true);
+
+ tabBrowser.loadURI("http://example.com", null, null);
+
+ tabBrowser.addEventListener("load", function testTabBrowserLoad2(aEvent) {
+ tabBrowser.removeEventListener("load", testTabBrowserLoad2, true);
+
+ // After these push/replaceState calls, the window should have three
+ // history entries:
+ // testURL (state object: null) <-- oldest
+ // testURL (state object: {obj1:1})
+ // testURL?page2 (state object: {obj3:/^a$/}) <-- newest
+ let contentWindow = tab.linkedBrowser.contentWindow;
+ let history = contentWindow.history;
+ history.pushState({obj1:1}, "title-obj1");
+ history.pushState({obj2:2}, "title-obj2", "?page2");
+ history.replaceState({obj3:/^a$/}, "title-obj3");
+
+ let state = ss.getTabState(tab);
+ getBrowser().removeTab(tab);
+
+ // Restore the state into a new tab. Things don't work well when we
+ // restore into the old tab, but that's not a real use case anyway.
+ let tab2 = getBrowser().addTab("about:blank");
+ ss.setTabState(tab2, state, true);
+
+ // Run checkState() once the tab finishes loading its restored state.
+ tab2.linkedBrowser.addEventListener("load", function testTBTab2LBLoad() {
+ tab2.linkedBrowser.removeEventListener("load", testTBTab2LBLoad, true);
+ SimpleTest.executeSoon(function() {
+ checkState(tab2);
+ });
+ }, true);
+
+ }, true);
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_514751.js b/comm/suite/components/tests/browser/browser_514751.js
new file mode 100644
index 0000000000..2290814aa9
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_514751.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 509315 (Wallpaper) **/
+
+ waitForExplicitFinish();
+
+ let state = {
+ windows: [{
+ tabs: [{
+ entries: [
+ { url: "http://www.mozilla.org/projects/minefield/", title: "Minefield Start Page" },
+ {}
+ ]
+ }]
+ }]
+ };
+
+ let windowObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+
+ switch(aTopic) {
+ case "domwindowopened":
+ theWin.addEventListener("load", function testTheWinLoad() {
+ theWin.removeEventListener("load", testTheWinLoad);
+ executeSoon(function() {
+ var gotError = false;
+ try {
+ ss.setWindowState(theWin, JSON.stringify(state), true);
+ } catch (e) {
+ if (/NS_ERROR_MALFORMED_URI/.test(e))
+ gotError = true;
+ }
+ ok(!gotError, "Didn't get a malformed URI error.");
+ executeSoon(function() {
+ theWin.close();
+ });
+ });
+ });
+ break;
+
+ case "domwindowclosed":
+ Services.ww.unregisterNotification(this);
+ finish();
+ break;
+ }
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+ Services.ww.openWindow(null,
+ location,
+ "_blank",
+ "chrome,all,dialog=no",
+ null);
+
+}
diff --git a/comm/suite/components/tests/browser/browser_522545.js b/comm/suite/components/tests/browser/browser_522545.js
new file mode 100644
index 0000000000..9088c88c81
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_522545.js
@@ -0,0 +1,280 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 522545 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ waitForExplicitFinish();
+ requestLongerTimeout(2);
+
+ // This tests the following use case:
+ // User opens a new tab which gets focus. The user types something into the
+ // address bar, then crashes or quits.
+ function test_newTabFocused() {
+ let state = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }] },
+ { entries: [], userTypedValue: "example.com", userTypedClear: 0 }
+ ],
+ selected: 2
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ is(browser.currentURI.spec, "about:blank",
+ "No history entries still sets currentURI to about:blank");
+ is(browser.userTypedValue, "example.com",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "example.com",
+ "Address bar's value correctly restored");
+ // Change tabs to make sure address bar value gets updated
+ getBrowser().selectedTab = getBrowser().tabContainer.getItemAtIndex(0);
+ is(gURLBar.value, "about:mozilla",
+ "Address bar's value correctly updated");
+ runNextTest();
+ });
+ }
+
+ // This tests the following use case:
+ // User opens a new tab which gets focus. The user types something into the
+ // address bar, switches back to the first tab, then crashes or quits.
+ function test_newTabNotFocused() {
+ let state = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }] },
+ { entries: [], userTypedValue: "example.org", userTypedClear: 0 }
+ ],
+ selected: 1
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().getBrowserAtIndex(1);
+ is(browser.currentURI.spec, "about:blank",
+ "No history entries still sets currentURI to about:blank");
+ is(browser.userTypedValue, "example.org",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "about:mozilla",
+ "Address bar's value correctly restored");
+ // Change tabs to make sure address bar value gets updated
+ getBrowser().selectedTab = getBrowser().tabContainer.getItemAtIndex(1);
+ is(gURLBar.value, "example.org",
+ "Address bar's value correctly updated");
+ runNextTest();
+ });
+ }
+
+ // This tests the following use case:
+ // User is in a tab with session history, then types something in the
+ // address bar, then crashes or quits.
+ function test_existingSHEnd_noClear() {
+ let state = {
+ windows: [{
+ tabs: [{
+ entries: [{ url: "about:mozilla" }, { url: "about:config" }],
+ index: 2,
+ userTypedValue: "example.com",
+ userTypedClear: 0
+ }]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ is(browser.currentURI.spec, "about:config",
+ "browser.currentURI set to current entry in SH");
+ is(browser.userTypedValue, "example.com",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "example.com",
+ "Address bar's value correctly restored to userTypedValue");
+ runNextTest();
+ });
+ }
+
+ // This tests the following use case:
+ // User is in a tab with session history, presses back at some point, then
+ // types something in the address bar, then crashes or quits.
+ function test_existingSHMiddle_noClear() {
+ let state = {
+ windows: [{
+ tabs: [{
+ entries: [{ url: "about:mozilla" }, { url: "about:config" }],
+ index: 1,
+ userTypedValue: "example.org",
+ userTypedClear: 0
+ }]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ is(browser.currentURI.spec, "about:mozilla",
+ "browser.currentURI set to current entry in SH");
+ is(browser.userTypedValue, "example.org",
+ "userTypedValue was correctly restored");
+ is(browser.userTypedClear, 0,
+ "userTypeClear restored as expected");
+ is(gURLBar.value, "example.org",
+ "Address bar's value correctly restored to userTypedValue");
+ runNextTest();
+ });
+ }
+
+ // This test simulates lots of tabs opening at once and then quitting/crashing.
+ function test_getBrowserState_lotsOfTabsOpening() {
+ getBrowser().stop();
+
+ let uris = [];
+ for (let i = 0; i < 25; i++)
+ uris.push("http://example.com/" + i);
+
+ // We're waiting for the first location change, which should indicate
+ // one of the tabs has loaded and the others haven't. So one should
+ // be in a non-userTypedValue case, while others should still have
+ // userTypedValue and userTypedClear set.
+ getBrowser().addTabsProgressListener({
+ onLocationChange: function (aBrowser) {
+ if (uris.includes(aBrowser.currentURI.spec)) {
+ getBrowser().removeTabsProgressListener(this);
+ firstLocationChange();
+ }
+ }
+ });
+
+ function firstLocationChange() {
+ let state = JSON.parse(ss.getBrowserState());
+ let hasUTV = state.windows[0].tabs.some(function(aTab) {
+ return aTab.userTypedValue && aTab.userTypedClear && !aTab.entries.length;
+ });
+
+ ok(hasUTV, "At least one tab has a userTypedValue with userTypedClear with no loaded URL");
+
+ getBrowser().addEventListener("load", firstLoad, true);
+ }
+
+ function firstLoad() {
+ getBrowser().removeEventListener("load", firstLoad, true);
+
+ let state = JSON.parse(ss.getBrowserState());
+ let hasSH = state.windows[0].tabs.some(function(aTab) {
+ return !("userTypedValue" in aTab) && aTab.entries[0].url;
+ });
+
+ ok(hasSH, "At least one tab has its entry in SH");
+
+ runNextTest();
+ }
+
+ getBrowser().loadTabs(uris);
+ }
+
+ // This simulates setting a userTypedValue and ensures that just typing in the
+ // URL bar doesn't set userTypedClear as well.
+ function test_getBrowserState_userTypedValue() {
+ let state = {
+ windows: [{
+ tabs: [{ entries: [] }]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = getBrowser().selectedBrowser;
+ // Make sure this tab isn't loading and state is clear before we test.
+ is(browser.userTypedValue, null, "userTypedValue is empty to start");
+ is(browser.userTypedClear, 0, "userTypedClear is 0 to start");
+
+ gURLBar.value = "example.org";
+ let event = document.createEvent("Events");
+ event.initEvent("input", true, false);
+ gURLBar.dispatchEvent(event);
+
+ executeSoon(function() {
+ is(browser.userTypedValue, "example.org",
+ "userTypedValue was set when changing gURLBar.value");
+ is(browser.userTypedClear, 0,
+ "userTypedClear was not changed when changing gURLBar.value");
+
+ // Now make sure ss gets these values too
+ let newState = JSON.parse(ss.getBrowserState());
+ is(newState.windows[0].tabs[0].userTypedValue, "example.org",
+ "sessionstore got correct userTypedValue");
+ is(newState.windows[0].tabs[0].userTypedClear, 0,
+ "sessionstore got correct userTypedClear");
+ runNextTest();
+ });
+ });
+ }
+
+ // test_getBrowserState_lotsOfTabsOpening tested userTypedClear in a few cases,
+ // but not necessarily any that had legitimate URIs in the state of loading
+ // (eg, "http://example.com"), so this test will cover that case.
+ function test_userTypedClearLoadURI() {
+ let state = {
+ windows: [{
+ tabs: [
+ { entries: [], userTypedValue: "http://example.com", userTypedClear: 2 }
+ ]
+ }]
+ };
+
+ waitForBrowserState(state, function() {
+ let browser = gBrowser.selectedBrowser;
+ is(browser.currentURI.spec, "http://example.com/",
+ "userTypedClear=2 caused userTypedValue to be loaded");
+ is(browser.userTypedValue, null,
+ "userTypedValue was null after loading a URI");
+ is(browser.userTypedClear, 0,
+ "userTypeClear reset to 0");
+ is(gURLBar.value, "http://example.com/",
+ "Address bar's value set after loading URI");
+ runNextTest();
+ });
+ }
+
+
+ let tests = [test_newTabFocused, test_newTabNotFocused,
+ test_existingSHEnd_noClear, test_existingSHMiddle_noClear,
+ test_getBrowserState_lotsOfTabsOpening,
+ test_getBrowserState_userTypedValue, test_userTypedClearLoadURI];
+ let originalState = ss.getBrowserState();
+ let state = {
+ windows: [{
+ tabs: [{ entries: [{ url: "about:blank" }] }]
+ }]
+ };
+ function runNextTest() {
+ if (tests.length) {
+ waitForBrowserState(state, tests.shift());
+ } else {
+ ss.setBrowserState(originalState);
+ executeSoon(function () {
+ is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
+ finish();
+ });
+ }
+ }
+
+ // Run the tests!
+ runNextTest();
+}
diff --git a/comm/suite/components/tests/browser/browser_524745.js b/comm/suite/components/tests/browser/browser_524745.js
new file mode 100644
index 0000000000..c14b868779
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_524745.js
@@ -0,0 +1,59 @@
+/* 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/. */
+
+function browserWindowsCount() {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ return count;
+}
+
+function test() {
+ /** Test for Bug 524745, ported by bug 558638 **/
+ is(browserWindowsCount(), 1, "Only one browser window should be open initially");
+
+ let uniqKey = "bug524745";
+ let uniqVal = Date.now();
+
+ waitForExplicitFinish();
+
+ let window_B = openDialog(location, "_blank", "chrome,all,dialog=no");
+ window_B.addEventListener("load", function testWindowBLoad(aEvent) {
+ window_B.removeEventListener("load", testWindowBLoad);
+
+ waitForFocus(function() {
+ // Add identifying information to window_B
+ ss.setWindowValue(window_B, uniqKey, uniqVal);
+ let state = JSON.parse(ss.getBrowserState());
+ let selectedWindow = state.windows[state.selectedWindow - 1];
+ is(selectedWindow.extData && selectedWindow.extData[uniqKey], uniqVal,
+ "selectedWindow is window_B");
+
+ // Now minimize window_B. The selected window shouldn't have the secret data
+ window_B.minimize();
+ waitForFocus(function() {
+ state = JSON.parse(ss.getBrowserState());
+ selectedWindow = state.windows[state.selectedWindow - 1];
+ ok(!selectedWindow.extData || !selectedWindow.extData[uniqKey],
+ "selectedWindow is not window_B after minimizing it");
+
+ // Now minimize the last open window (assumes no other tests left windows open)
+ window.minimize();
+ state = JSON.parse(ss.getBrowserState());
+ is(state.selectedWindow, 0,
+ "selectedWindow should be 0 when all windows are minimized");
+
+ // Cleanup
+ window.restore();
+ window_B.close();
+ is(browserWindowsCount(), 1,
+ "Only one browser window should be open eventually");
+ finish();
+ });
+ }, window_B);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_526613.js b/comm/suite/components/tests/browser/browser_526613.js
new file mode 100644
index 0000000000..d7a664403f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_526613.js
@@ -0,0 +1,71 @@
+/* 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/. */
+
+function test() {
+ /** Test for Bug 526613, porting done in Bug 548211 **/
+
+ waitForExplicitFinish();
+
+ function browserWindowsCount(expected) {
+ let count = 0;
+ let e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ is(count, expected,
+ "number of open browser windows according to nsIWindowMediator");
+ let state = ss.getBrowserState();
+ info(state);
+ is(JSON.parse(state).windows.length, expected,
+ "number of open browser windows according to getBrowserState");
+ }
+
+ browserWindowsCount(1);
+
+ // backup old state
+ let oldState = ss.getBrowserState();
+ // create a new state for testing
+ let testState = {
+ windows: [
+ { tabs: [{ entries: [{ url: "http://example.com/" }] }], selected: 1 },
+ { tabs: [{ entries: [{ url: "about:mozilla" }] }], selected: 1 },
+ ],
+ // make sure the first window is focused, otherwise when restoring the
+ // old state, the first window is closed and the test harness gets unloaded
+ selectedWindow: 1
+ };
+
+ let pass = 1;
+ function observer(aSubject, aTopic, aData) {
+ is(aTopic, "sessionstore-browser-state-restored",
+ "The sessionstore-browser-state-restored notification was observed");
+
+ if (pass++ == 1) {
+ browserWindowsCount(2);
+
+ // let the first window be focused (see above)
+ function pollMostRecentWindow() {
+ if (Services.wm.getMostRecentWindow("navigator:browser") == window) {
+ ss.setBrowserState(oldState);
+ } else {
+ info("waiting for the current window to become active");
+ setTimeout(pollMostRecentWindow, 0);
+ window.focus(); //XXX Why is this needed?
+ }
+ }
+ pollMostRecentWindow();
+ }
+ else {
+ browserWindowsCount(1);
+ ok(!window.closed, "Restoring the old state should have left this window open");
+ Services.obs.removeObserver(observer, "sessionstore-browser-state-restored");
+ finish();
+ }
+ }
+ Services.obs.addObserver(observer, "sessionstore-browser-state-restored");
+
+ // set browser to test state
+ ss.setBrowserState(JSON.stringify(testState));
+}
diff --git a/comm/suite/components/tests/browser/browser_528776.js b/comm/suite/components/tests/browser/browser_528776.js
new file mode 100644
index 0000000000..3f316ace06
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_528776.js
@@ -0,0 +1,29 @@
+function browserWindowsCount(expected) {
+ var count = 0;
+ var e = Services.wm.getEnumerator("navigator:browser");
+ while (e.hasMoreElements()) {
+ if (!e.getNext().closed)
+ ++count;
+ }
+ is(count, expected,
+ "number of open browser windows according to nsIWindowMediator");
+ is(JSON.parse(ss.getBrowserState()).windows.length, expected,
+ "number of open browser windows according to getBrowserState");
+}
+
+function test() {
+ /** Test for Bug 528776, ported by Bug 548228 **/
+
+ waitForExplicitFinish();
+
+ browserWindowsCount(1);
+
+ var win = openDialog(location, "", "chrome,all,dialog=no");
+ win.addEventListener("load", function loadListener() {
+ win.removeEventListener("load", loadListener);
+ browserWindowsCount(2);
+ win.close();
+ browserWindowsCount(1);
+ finish();
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_581937.js b/comm/suite/components/tests/browser/browser_581937.js
new file mode 100644
index 0000000000..5f807715c5
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_581937.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+ // Tests that an about:blank tab with no history will not be saved into
+ // session store and thus, it will not show up in Recently Closed Tabs.
+
+var tab;
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", 0);
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+ Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+
+ is(ss.getClosedTabCount(window), 0, "should be no closed tabs");
+
+ getBrowser().tabContainer.addEventListener("TabOpen", onTabOpen, true);
+
+ tab = getBrowser().addTab();
+}
+
+function onTabOpen(aEvent) {
+ getBrowser().tabContainer.removeEventListener("TabOpen", onTabOpen, true);
+
+ // Let other listeners react to the TabOpen event before removing the tab.
+ executeSoon(function() {
+ is(getBrowser().browsers[1].currentURI.spec, "about:blank",
+ "we will be removing an about:blank tab");
+
+ getBrowser().removeTab(tab);
+
+ is(ss.getClosedTabCount(window), 0, "should still be no closed tabs");
+
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ executeSoon(finish);
+ });
+}
diff --git a/comm/suite/components/tests/browser/browser_586068-cascaded_restore.js b/comm/suite/components/tests/browser/browser_586068-cascaded_restore.js
new file mode 100644
index 0000000000..6389884048
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_586068-cascaded_restore.js
@@ -0,0 +1,730 @@
+/* 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 stateBackup = ss.getBrowserState();
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+function test() {
+ /** Test for Bug 586068 - Cascade page loads when restoring **/
+ waitForExplicitFinish();
+ // This test does a lot of window opening / closing and waiting for loads.
+ // In order to prevent timeouts, we'll extend the default that mochitest uses.
+ requestLongerTimeout(4);
+ runNextTest();
+}
+
+// test_reloadCascade, test_reloadReload are generated tests that are run out
+// of cycle (since they depend on current state). They're listed in [tests] here
+// so that it is obvious when they run in respect to the other tests.
+var tests = [test_cascade, test_select, test_multiWindowState,
+ test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
+ test_setBrowserStateInterrupted, test_reload,
+ /* test_reloadReload, */ test_reloadCascadeSetup,
+ /* test_reloadCascade */];
+function runNextTest() {
+ // Reset the pref
+ try {
+ Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+ } catch (e) {}
+
+ // set an empty state & run the next test, or finish
+ if (tests.length) {
+ // Enumerate windows and close everything but our primary window. We can't
+ // use waitForFocus() because apparently it's buggy. See bug 599253.
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+ while (windowsEnum.hasMoreElements()) {
+ var currentWindow = windowsEnum.getNext();
+ if (currentWindow != window) {
+ currentWindow.close();
+ }
+ }
+
+ ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }] }] }));
+ let currentTest = tests.shift();
+ info("running " + currentTest.name);
+ executeSoon(currentTest);
+ }
+ else {
+ ss.setBrowserState(stateBackup);
+ executeSoon(finish);
+ }
+}
+
+
+function test_cascade() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n");
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_cascade_progressCallback();
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } }
+ ] }] };
+
+ let loadCount = 0;
+ // Since our progress listener is fired before the one in sessionstore, our
+ // expected counts look a little weird. This is because we inspect the state
+ // before sessionstore has marked the tab as finished restoring and before it
+ // starts restoring the next tab
+ let expectedCounts = [
+ [5, 1, 0],
+ [4, 1, 1],
+ [3, 1, 2],
+ [2, 1, 3],
+ [1, 1, 4],
+ [0, 1, 5]
+ ];
+
+ function test_cascade_progressCallback() {
+ loadCount++;
+ let counts = countTabs();
+ let expected = expectedCounts[loadCount - 1];
+
+ is(counts[0], expected[0], "test_cascade: load " + loadCount + " - # tabs that need to be restored");
+ is(counts[1], expected[1], "test_cascade: load " + loadCount + " - # tabs that are restoring");
+ is(counts[2], expected[2], "test_cascade: load " + loadCount + " - # tabs that has been restored");
+
+ if (loadCount < state.windows[0].tabs.length)
+ return;
+
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ runNextTest();
+ }
+
+ // This progress listener will get attached before the listener in session store.
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_select() {
+ // Set the pref to 0 so we know exactly how many tabs should be restoring at
+ // any given time. This guarantees that a finishing load won't start another.
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_select_progressCallback(aBrowser);
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } }
+ ], selected: 1 }] };
+
+ let loadCount = 0;
+ // expectedCounts looks a little wierd for the test case, but it works. See
+ // comment in test_cascade for an explanation
+ let expectedCounts = [
+ [5, 1, 0],
+ [4, 1, 1],
+ [3, 1, 2],
+ [2, 1, 3],
+ [1, 1, 4],
+ [0, 1, 5]
+ ];
+ let tabOrder = [0, 5, 1, 4, 3, 2];
+
+ function test_select_progressCallback(aBrowser) {
+ loadCount++;
+
+ let counts = countTabs();
+ let expected = expectedCounts[loadCount - 1];
+
+ is(counts[0], expected[0], "test_select: load " + loadCount + " - # tabs that need to be restored");
+ is(counts[1], expected[1], "test_select: load " + loadCount + " - # tabs that are restoring");
+ is(counts[2], expected[2], "test_select: load " + loadCount + " - # tabs that has been restored");
+
+ if (loadCount < state.windows[0].tabs.length) {
+ // double check that this tab was the right one
+ let expectedData = state.windows[0].tabs[tabOrder[loadCount - 1]].extData.uniq;
+ let tab;
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ if (!tab && window.getBrowser().tabs[i].linkedBrowser == aBrowser)
+ tab = window.getBrowser().tabs[i];
+ }
+ is(ss.getTabValue(tab, "uniq"), expectedData, "test_select: load " + loadCount + " - correct tab was restored");
+
+ // select the next tab
+ window.getBrowser().selectTabAtIndex(tabOrder[loadCount]);
+ return;
+ }
+
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ runNextTest();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_multiWindowState() {
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_multiWindowState_progressCallback(aBrowser);
+ }
+ }
+
+ // The first window will be put into the already open window and the second
+ // window will be opened with _openWindowWithState, which is the source of the problem.
+ let state = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#0" }], extData: { "uniq": r() } }
+ ],
+ selected: 1
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } }
+ ],
+ selected: 4
+ }
+ ] };
+ let numTabs = state.windows[0].tabs.length + state.windows[1].tabs.length;
+
+ let loadCount = 0;
+ function test_multiWindowState_progressCallback(aBrowser) {
+ loadCount++;
+
+ if (loadCount < numTabs)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs, "test_multiWindowState: all tabs were restored");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_multiWindowState: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ runNextTest();
+ }
+
+ // We also want to catch the 2nd window, so we need to observe domwindowopened
+ function windowObserver(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ if (aTopic == "domwindowopened") {
+ theWin.addEventListener("load", function theWinLoad() {
+ theWin.removeEventListener("load", theWinLoad);
+
+ Services.ww.unregisterNotification(windowObserver);
+ theWin.getBrowser().addTabsProgressListener(progressListener);
+ });
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_setWindowStateNoOverwrite() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_setWindowStateNoOverwrite_progressCallback(aBrowser);
+ }
+ }
+
+ // We'll use 2 states so that we can make sure calling setWindowState doesn't
+ // wipe out currently restoring data.
+ let state1 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com#1" }] },
+ { entries: [{ url: "http://example.com#2" }] },
+ { entries: [{ url: "http://example.com#3" }] },
+ { entries: [{ url: "http://example.com#4" }] },
+ { entries: [{ url: "http://example.com#5" }] },
+ ] }] };
+ let state2 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org#1" }] },
+ { entries: [{ url: "http://example.org#2" }] },
+ { entries: [{ url: "http://example.org#3" }] },
+ { entries: [{ url: "http://example.org#4" }] },
+ { entries: [{ url: "http://example.org#5" }] }
+ ] }] };
+
+ let numTabs = state1.windows[0].tabs.length + state2.windows[0].tabs.length;
+
+ let loadCount = 0;
+ function test_setWindowStateNoOverwrite_progressCallback(aBrowser) {
+ loadCount++;
+
+ // When loadCount == 2, we'll also restore state2 into the window
+ if (loadCount == 2)
+ ss.setWindowState(window, JSON.stringify(state2), false);
+
+ if (loadCount < numTabs)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
+ // window.__SS_tabsToRestore isn't decremented until after the progress
+ // listener is called. Since we get in here before that, we still expect
+ // the count to be 1.
+ is(window.__SS_tabsToRestore, 1,
+ "test_setWindowStateNoOverwrite: window doesn't think there are more tabs to restore");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_setWindowStateNoOverwrite: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+
+ runNextTest();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setWindowStateOverwrite() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_setWindowStateOverwrite_progressCallback(aBrowser);
+ }
+ }
+
+ // We'll use 2 states so that we can make sure calling setWindowState doesn't
+ // wipe out currently restoring data.
+ let state1 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com#1" }] },
+ { entries: [{ url: "http://example.com#2" }] },
+ { entries: [{ url: "http://example.com#3" }] },
+ { entries: [{ url: "http://example.com#4" }] },
+ { entries: [{ url: "http://example.com#5" }] },
+ ] }] };
+ let state2 = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org#1" }] },
+ { entries: [{ url: "http://example.org#2" }] },
+ { entries: [{ url: "http://example.org#3" }] },
+ { entries: [{ url: "http://example.org#4" }] },
+ { entries: [{ url: "http://example.org#5" }] }
+ ] }] };
+
+ let numTabs = 2 + state2.windows[0].tabs.length;
+
+ let loadCount = 0;
+ function test_setWindowStateOverwrite_progressCallback(aBrowser) {
+ loadCount++;
+
+ // When loadCount == 2, we'll also restore state2 into the window
+ if (loadCount == 2)
+ ss.setWindowState(window, JSON.stringify(state2), true);
+
+ if (loadCount < numTabs)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs, "test_setWindowStateOverwrite: all tabs were restored");
+ // window.__SS_tabsToRestore isn't decremented until after the progress
+ // listener is called. Since we get in here before that, we still expect
+ // the count to be 1.
+ is(window.__SS_tabsToRestore, 1,
+ "test_setWindowStateOverwrite: window doesn't think there are more tabs to restore");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_setWindowStateOverwrite: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+
+ runNextTest();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setBrowserStateInterrupted() {
+ // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ // We only care about load events when the tab still has
+ // __SS_restoreState == TAB_STATE_RESTORING on it.
+ // Since our listener is attached before the sessionstore one, this works out.
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_setBrowserStateInterrupted_progressCallback(aBrowser);
+ }
+ }
+
+ // The first state will be loaded using setBrowserState, followed by the 2nd
+ // state also being loaded using setBrowserState, interrupting the first restore.
+ let state1 = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }
+ ],
+ selected: 1
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+ ],
+ selected: 3
+ }
+ ] };
+ let state2 = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#6" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#7" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#8" }], extData: { "uniq": r() } }
+ ],
+ selected: 3
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#7" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#8" }], extData: { "uniq": r() } },
+ ],
+ selected: 1
+ }
+ ] };
+
+ // interruptedAfter will be set after the selected tab from each window have loaded.
+ let interruptedAfter = 0;
+ let loadedWindow1 = false;
+ let loadedWindow2 = false;
+ let numTabs = state2.windows[0].tabs.length + state2.windows[1].tabs.length;
+
+ let loadCount = 0;
+ function test_setBrowserStateInterrupted_progressCallback(aBrowser) {
+ loadCount++;
+
+ if (aBrowser.currentURI.spec == state1.windows[0].tabs[2].entries[0].url)
+ loadedWindow1 = true;
+ if (aBrowser.currentURI.spec == state1.windows[1].tabs[0].entries[0].url)
+ loadedWindow2 = true;
+
+ if (!interruptedAfter && loadedWindow1 && loadedWindow2) {
+ interruptedAfter = loadCount;
+ ss.setBrowserState(JSON.stringify(state2));
+ return;
+ }
+
+ if (loadCount < numTabs + interruptedAfter)
+ return;
+
+ // We don't actually care about load order in this test, just that they all
+ // do load.
+ is(loadCount, numTabs + interruptedAfter,
+ "test_setBrowserStateInterrupted: all tabs were restored");
+ let count = countTabs();
+ is(count[0], 0,
+ "test_setBrowserStateInterrupted: there are no tabs left needing restore");
+
+ // Remove the progress listener from this window, it will be removed from
+ // theWin when that window is closed (in setBrowserState).
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ Services.ww.unregisterNotification(windowObserver);
+ runNextTest();
+ }
+
+ // We also want to catch the extra windows (there should be 2), so we need to observe domwindowopened
+ function windowObserver(aSubject, aTopic, aData) {
+ let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ if (aTopic == "domwindowopened") {
+ theWin.addEventListener("load", function wObserverTheWinLoad() {
+ theWin.removeEventListener("load", wObserverTheWinLoad);
+
+ Services.ww.unregisterNotification(windowObserver);
+ theWin.getBrowser().addTabsProgressListener(progressListener);
+ });
+ }
+ }
+ Services.ww.registerNotification(windowObserver);
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state1));
+}
+
+
+function test_reload() {
+ // Set the pref to 0 so we know exactly how many tabs should be restoring at
+ // any given time. This guarantees that a finishing load won't start another.
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_reload_progressCallback(aBrowser);
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org/#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#4" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#6" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#7" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#8" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#9" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#10" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#11" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#12" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#13" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#14" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#15" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#16" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#17" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org/#18" }], extData: { "uniq": r() } }
+ ], selected: 1 }] };
+
+ let loadCount = 0;
+ function test_reload_progressCallback(aBrowser) {
+ loadCount++;
+
+ is(aBrowser.currentURI.spec, state.windows[0].tabs[loadCount - 1].entries[0].url,
+ "test_reload: load " + loadCount + " - browser loaded correct url");
+
+ if (loadCount <= state.windows[0].tabs.length) {
+ // double check that this tab was the right one
+ let expectedData = state.windows[0].tabs[loadCount - 1].extData.uniq;
+ let tab;
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ if (!tab && window.getBrowser().tabs[i].linkedBrowser == aBrowser)
+ tab = window.getBrowser().tabs[i];
+ }
+ is(ss.getTabValue(tab, "uniq"), expectedData,
+ "test_reload: load " + loadCount + " - correct tab was restored");
+
+ if (loadCount == state.windows[0].tabs.length) {
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ executeSoon(function() {
+ _test_reloadAfter("test_reloadReload", state, runNextTest);
+ });
+ }
+ else {
+ // reload the next tab
+ window.getBrowser().reloadTab(window.getBrowser().tabs[loadCount]);
+ }
+ }
+
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+// This doesn't actually test anything, just does a cascaded restore with default
+// settings. This really just sets up to test that reloads work.
+function test_reloadCascadeSetup() {
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_cascadeReloadSetup_progressCallback();
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.com/#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#4" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#5" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com/#6" }], extData: { "uniq": r() } }
+ ] }] };
+
+ let loadCount = 0;
+ function test_cascadeReloadSetup_progressCallback() {
+ loadCount++;
+ if (loadCount < state.windows[0].tabs.length)
+ return;
+
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ executeSoon(function() {
+ _test_reloadAfter("test_reloadCascade", state, runNextTest);
+ });
+ }
+
+ // This progress listener will get attached before the listener in session store.
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+
+// This is a generic function that will attempt to reload each test. We do this
+// a couple times, so make it utilitarian.
+// This test expects that aState contains a single window and that each tab has
+// a unique extData value eg. { "uniq": value }.
+function _test_reloadAfter(aTestName, aState, aCallback) {
+ info("starting " + aTestName);
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ test_reloadAfter_progressCallback(aBrowser);
+ }
+ }
+
+ // Simulate a left mouse button click with no modifiers, which is what
+ // Command-R, or clicking reload does.
+ let fakeEvent = {
+ button: 0,
+ metaKey: false,
+ altKey: false,
+ ctrlKey: false,
+ shiftKey: false,
+ }
+
+ let loadCount = 0;
+ function test_reloadAfter_progressCallback(aBrowser) {
+ loadCount++;
+
+ if (loadCount <= aState.windows[0].tabs.length) {
+ // double check that this tab was the right one
+ let expectedData = aState.windows[0].tabs[loadCount - 1].extData.uniq;
+ let tab;
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ if (!tab && window.getBrowser().tabs[i].linkedBrowser == aBrowser)
+ tab = window.getBrowser().tabs[i];
+ }
+ is(ss.getTabValue(tab, "uniq"), expectedData,
+ aTestName + ": load " + loadCount + " - correct tab was reloaded");
+
+ if (loadCount == aState.windows[0].tabs.length) {
+ window.getBrowser().removeTabsProgressListener(progressListener);
+ aCallback();
+ }
+ else {
+ // reload the next tab
+ window.getBrowser().selectTabAtIndex(loadCount);
+ BrowserReload(fakeEvent);
+ }
+ }
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ BrowserReload(fakeEvent);
+}
+
+
+function countTabs() {
+ let needsRestore = 0,
+ isRestoring = 0,
+ wasRestored = 0;
+
+ let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+
+ while (windowsEnum.hasMoreElements()) {
+ let window = windowsEnum.getNext();
+ if (window.closed)
+ continue;
+
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ let browser = window.getBrowser().tabs[i].linkedBrowser;
+ if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ isRestoring++;
+ else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ needsRestore++;
+ else
+ wasRestored++;
+ }
+ }
+ return [needsRestore, isRestoring, wasRestored];
+}
+
+function r() {
+ return "" + Date.now() + Math.random();
+}
+
diff --git a/comm/suite/components/tests/browser/browser_597315.js b/comm/suite/components/tests/browser/browser_597315.js
new file mode 100644
index 0000000000..516ff5ae88
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315.js
@@ -0,0 +1,64 @@
+/* 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 stateBackup = ss.getBrowserState();
+
+function test() {
+ /** Test for Bug 597315 - Frameset history does not work properly when restoring a tab **/
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+
+ let testURL = getRootDirectory(gTestPath) + "browser_597315_index.html";
+ let tab = getBrowser().addTab(testURL);
+ getBrowser().selectedTab = tab;
+
+ waitForLoadsInBrowser(tab.linkedBrowser, 4, function() {
+ let browser_b = tab.linkedBrowser.contentDocument.getElementsByTagName("frame")[1];
+ let document_b = browser_b.contentDocument;
+ let links = document_b.getElementsByTagName("a");
+
+ // We're going to click on the first link, so listen for another load event
+ waitForLoadsInBrowser(tab.linkedBrowser, 1, function() {
+ waitForLoadsInBrowser(tab.linkedBrowser, 1, function() {
+
+ getBrowser().removeTab(tab);
+ // wait for 4 loads again...
+ let newTab = ss.undoCloseTab(window, 0);
+
+ waitForLoadsInBrowser(newTab.linkedBrowser, 4, function() {
+ getBrowser().goBack();
+ waitForLoadsInBrowser(newTab.linkedBrowser, 1, function() {
+
+ let expectedURLEnds = ["a.html", "b.html", "c1.html"];
+ let frames = newTab.linkedBrowser.contentDocument.getElementsByTagName("frame");
+ for (let i = 0; i < frames.length; i++) {
+ is(frames[i].contentDocument.location,
+ getRootDirectory(gTestPath) + "browser_597315_" + expectedURLEnds[i],
+ "frame " + i + " has the right url");
+ }
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ getBrowser().removeTab(newTab);
+ ss.setBrowserState(stateBackup);
+ executeSoon(finish);
+ });
+ });
+ });
+ EventUtils.sendMouseEvent({type:"click"}, links[1], browser_b.contentWindow);
+ });
+ EventUtils.sendMouseEvent({type:"click"}, links[0], browser_b.contentWindow);
+ });
+}
+
+// helper function
+function waitForLoadsInBrowser(aBrowser, aLoadCount, aCallback) {
+ let loadCount = 0;
+ aBrowser.addEventListener("load", function aBrowserLoad(aEvent) {
+ if (++loadCount < aLoadCount)
+ return;
+
+ aBrowser.removeEventListener("load", aBrowserLoad, true);
+ aCallback();
+ }, true);
+}
diff --git a/comm/suite/components/tests/browser/browser_597315_a.html b/comm/suite/components/tests/browser/browser_597315_a.html
new file mode 100755
index 0000000000..8e7b35d7a1
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_a.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm A!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_b.html b/comm/suite/components/tests/browser/browser_597315_b.html
new file mode 100755
index 0000000000..f8dbfb2a27
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_b.html
@@ -0,0 +1,10 @@
+<html>
+ <body>
+ I'm B!<br/>
+ <a target="c" href="browser_597315_c1.html">click me first</a><br/>
+ <a target="c" href="browser_597315_c2.html">then click me</a><br/>
+ Close this tab.<br/>
+ Restore this tab.<br/>
+ Click back.<br/>
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_c.html b/comm/suite/components/tests/browser/browser_597315_c.html
new file mode 100755
index 0000000000..0efd7d9026
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_c.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm C!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_c1.html b/comm/suite/components/tests/browser/browser_597315_c1.html
new file mode 100755
index 0000000000..b55c1d45a9
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_c1.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm C1!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_c2.html b/comm/suite/components/tests/browser/browser_597315_c2.html
new file mode 100755
index 0000000000..aec504141b
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_c2.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ I'm C2!
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/browser_597315_index.html b/comm/suite/components/tests/browser/browser_597315_index.html
new file mode 100644
index 0000000000..1465ddf044
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_597315_index.html
@@ -0,0 +1,10 @@
+<html>
+ <frameset cols="20%,80%">
+ <frameset rows="30%,70%">
+ <frame src="browser_597315_a.html"/>
+ <frame src="browser_597315_b.html"/>
+ </frameset>
+ <frame src="browser_597315_c.html" name="c"/>
+ </frameset>
+</html>
+
diff --git a/comm/suite/components/tests/browser/browser_607016.js b/comm/suite/components/tests/browser/browser_607016.js
new file mode 100644
index 0000000000..9de13dd05a
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_607016.js
@@ -0,0 +1,120 @@
+/* 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 TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+var stateBackup = ss.getBrowserState();
+
+function cleanup() {
+ // Reset the pref
+ try {
+ Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+ } catch (e) {}
+ ss.setBrowserState(stateBackup);
+ executeSoon(finish);
+}
+
+function test() {
+ /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
+ waitForExplicitFinish();
+
+ // Set the pref to 0 so we know exactly how many tabs should be restoring at
+ // any given time. This guarantees that a finishing load won't start another.
+ Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+
+ // We have our own progress listener for this test, which we'll attach before our state is set
+ let progressListener = {
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ progressCallback(aBrowser);
+ }
+ }
+
+ let state = { windows: [{ tabs: [
+ { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } }, // overwriting
+ //{ entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } }, // hiding
+ { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }, // adding
+ { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } }, // deleting
+ { entries: [{ url: "http://example.org#6" }] } // creating
+ ], selected: 1 }] };
+
+ function progressCallback(aBrowser) {
+ // We'll remove the progress listener after the first one because we aren't
+ // loading any other tabs
+ window.getBrowser().removeTabsProgressListener(progressListener);
+
+ let curState = JSON.parse(ss.getBrowserState());
+ for (let i = 0; i < curState.windows[0].tabs.length; i++) {
+ if (state.windows[0].tabs[i].extData) {
+ is(curState.windows[0].tabs[i].extData["uniq"],
+ state.windows[0].tabs[i].extData["uniq"],
+ "sanity check that tab has correct extData");
+ }
+ else
+ ok(!("extData" in curState.windows[0].tabs[i]),
+ "sanity check that tab doesn't have extData");
+ }
+
+ // Now we'll set a new unique value on 1 of the tabs
+ let newUniq = r();
+ ss.setTabValue(getBrowser().tabs[1], "uniq", newUniq);
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ let closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ is(closedTabData.state.extData.uniq, newUniq,
+ "(overwriting) new data is stored in extData");
+
+ // hide the next tab before closing it
+ //getBrowser().hideTab(getBrowser().tabs[1]);
+ //getBrowser().removeTab(getBrowser().tabs[1]);
+ //closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ //ok(closedTabData.state.hidden, "(hiding) tab data has hidden == true");
+
+ // set data that's not in a conflicting key
+ let stillUniq = r();
+ ss.setTabValue(getBrowser().tabs[1], "stillUniq", stillUniq);
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ is(closedTabData.state.extData.stillUniq, stillUniq,
+ "(adding) new data is stored in extData");
+
+ // remove the uniq value and make sure it's not there in the closed data
+ ss.deleteTabValue(getBrowser().tabs[1], "uniq");
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ // Since Panorama might have put data in, first check if there is extData.
+ // If there is explicitly check that "uniq" isn't in it. Otherwise, we're ok
+ if ("extData" in closedTabData.state) {
+ ok(!("uniq" in closedTabData.state.extData),
+ "(deleting) uniq not in existing extData");
+ }
+ else {
+ ok(true, "(deleting) no data is stored in extData");
+ }
+
+ // set unique data on the tab that never had any set, make sure that's saved
+ let newUniq2 = r();
+ ss.setTabValue(getBrowser().tabs[1], "uniq", newUniq2);
+ getBrowser().removeTab(getBrowser().tabs[1]);
+ closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+ is(closedTabData.state.extData.uniq, newUniq2,
+ "(creating) new data is stored in extData where there was none");
+
+ cleanup();
+ }
+
+ window.getBrowser().addTabsProgressListener(progressListener);
+ ss.setBrowserState(JSON.stringify(state));
+}
+
+// Helper function to create a random value
+function r() {
+ return "" + Date.now() + Math.random();
+}
+
diff --git a/comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js b/comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js
new file mode 100644
index 0000000000..e71641d9ca
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_615394-SSWindowState_events.js
@@ -0,0 +1,362 @@
+/* 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 stateBackup = ss.getBrowserState();
+const testState = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:blank" }] },
+ { entries: [{ url: "about:logo" }] }
+ ]
+ }]
+};
+const lameMultiWindowState = { windows: [
+ {
+ tabs: [
+ { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }
+ ],
+ selected: 1
+ },
+ {
+ tabs: [
+ { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+ { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+ ],
+ selected: 3
+ }
+ ] };
+
+
+function getOuterWindowID(aWindow) {
+ return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+}
+
+function test() {
+ /** Test for Bug 615394 - Session Restore should notify when it is beginning and ending a restore **/
+ waitForExplicitFinish();
+ // Preemptively extend the timeout to prevent [orange]
+ requestLongerTimeout(2);
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+ runNextTest();
+}
+
+
+var tests = [
+ test_setTabState,
+ test_duplicateTab,
+ test_undoCloseTab,
+ test_setWindowState,
+ test_setBrowserState,
+ test_undoCloseWindow
+];
+function runNextTest() {
+ // set an empty state & run the next test, or finish
+ if (tests.length) {
+ // Enumerate windows and close everything but our primary window. We can't
+ // use waitForFocus() because apparently it's buggy. See bug 599253.
+ var windowsEnum = Services.wm.getEnumerator("navigator:browser");
+ while (windowsEnum.hasMoreElements()) {
+ var currentWindow = windowsEnum.getNext();
+ if (currentWindow != window) {
+ currentWindow.close();
+ }
+ }
+
+ let currentTest = tests.shift();
+ info("prepping for " + currentTest.name);
+ waitForBrowserState(testState, currentTest);
+ }
+ else {
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+ ss.setBrowserState(stateBackup);
+ finish();
+ }
+}
+
+/** ACTUAL TESTS **/
+
+function test_setTabState() {
+ let tab = getBrowser().tabs[1];
+ let newTabState = JSON.stringify({ entries: [{ url: "http://example.org" }], extData: { foo: "bar" } });
+ let busyEventCount = 0;
+ let readyEventCount = 0;
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(tab, "foo"), "bar");
+ ss.setTabValue(tab, "baz", "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(ss.getTabValue(tab, "baz"), "qux");
+ is(tab.linkedBrowser.currentURI.spec, "http://example.org/");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+ ss.setTabState(tab, newTabState);
+}
+
+
+function test_duplicateTab() {
+ let tab = getBrowser().tabs[1];
+ let busyEventCount = 0;
+ let readyEventCount = 0;
+ let newTab;
+
+ // We'll look to make sure this value is on the duplicated tab
+ ss.setTabValue(tab, "foo", "bar");
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ // duplicateTab is "synchronous" in tab creation. Since restoreHistory is called
+ // via setTimeout, newTab will be assigned before the SSWindowStateReady event
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(newTab, "foo"), "bar");
+ ss.setTabValue(newTab, "baz", "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(ss.getTabValue(newTab, "baz"), "qux");
+ is(newTab.linkedBrowser.currentURI.spec, "about:logo");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+
+ newTab = ss.duplicateTab(window, tab);
+}
+
+
+function test_undoCloseTab() {
+ let tab = getBrowser().tabs[1],
+ busyEventCount = 0,
+ readyEventCount = 0,
+ reopenedTab;
+
+ ss.setTabValue(tab, "foo", "bar");
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ // undoCloseTab is "synchronous" in tab creation. Since restoreHistory is called
+ // via setTimeout, reopenedTab will be assigned before the SSWindowStateReady event
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(reopenedTab, "foo"), "bar");
+ ss.setTabValue(reopenedTab, "baz", "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(ss.getTabValue(reopenedTab, "baz"), "qux");
+ is(reopenedTab.linkedBrowser.currentURI.spec, "about:logo");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+
+ getBrowser().removeTab(tab);
+ reopenedTab = ss.undoCloseTab(window, 0);
+}
+
+
+function test_setWindowState() {
+ let testState = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }], extData: { "foo": "bar" } },
+ { entries: [{ url: "http://example.org" }], extData: { "baz": "qux" } }
+ ]
+ }]
+ };
+
+ let busyEventCount = 0,
+ readyEventCount = 0,
+ tabRestoredCount = 0;
+
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ is(ss.getTabValue(gBrowser.tabs[0], "foo"), "bar");
+ is(ss.getTabValue(gBrowser.tabs[1], "baz"), "qux");
+ }
+
+ function onSSTabRestored(aEvent) {
+ if (++tabRestoredCount < 2)
+ return;
+
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+ is(getBrowser().tabs[0].linkedBrowser.currentURI.spec, "about:mozilla");
+ is(getBrowser().tabs[1].linkedBrowser.currentURI.spec, "http://example.org/");
+
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ runNextTest();
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+
+ ss.setWindowState(window, JSON.stringify(testState), true);
+}
+
+
+function test_setBrowserState() {
+ // We'll track events per window so we are sure that they are each happening once
+ // pre window.
+ let windowEvents = {};
+ windowEvents[getOuterWindowID(window)] = { busyEventCount: 0, readyEventCount: 0 };
+
+ // waitForBrowserState does it's own observing for windows, but doesn't attach
+ // the listeners we want here, so do it ourselves.
+ let newWindow;
+ function windowObserver(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowopened") {
+ newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ newWindow.addEventListener("load", function newWindowLoad() {
+ newWindow.removeEventListener("load", newWindowLoad);
+
+ Services.ww.unregisterNotification(windowObserver);
+
+ windowEvents[getOuterWindowID(newWindow)] = { busyEventCount: 0, readyEventCount: 0 };
+
+ newWindow.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ newWindow.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ });
+ }
+ }
+
+ function onSSWindowStateBusy(aEvent) {
+ windowEvents[getOuterWindowID(aEvent.originalTarget)].busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ windowEvents[getOuterWindowID(aEvent.originalTarget)].readyEventCount++;
+ }
+
+ window.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+ Services.ww.registerNotification(windowObserver);
+
+ waitForBrowserState(lameMultiWindowState, function() {
+ let checkedWindows = 0;
+ for (let [id, winEvents] of Object.entries(windowEvents)) {
+ is(winEvents.busyEventCount, 1,
+ "[test_setBrowserState] window" + id + " busy event count correct");
+ is(winEvents.readyEventCount, 1,
+ "[test_setBrowserState] window" + id + " ready event count correct");
+ checkedWindows++;
+ }
+ is(checkedWindows, 2,
+ "[test_setBrowserState] checked 2 windows");
+ window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ newWindow.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ newWindow.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ runNextTest();
+ });
+}
+
+
+function test_undoCloseWindow() {
+ let newWindow, reopenedWindow;
+
+ function firstWindowObserver(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowopened") {
+ newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ Services.ww.unregisterNotification(firstWindowObserver);
+ }
+ }
+ Services.ww.registerNotification(firstWindowObserver);
+
+ waitForBrowserState(lameMultiWindowState, function() {
+ // Close the window which isn't window
+ newWindow.close();
+ reopenedWindow = ss.undoCloseWindow(0);
+ reopenedWindow.addEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ reopenedWindow.addEventListener("SSWindowStateReady", onSSWindowStateReady);
+
+ reopenedWindow.addEventListener("load", function reopenWindowLoad() {
+ reopenedWindow.removeEventListener("load", reopenWindowLoad);
+
+ reopenedWindow.getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored);
+ });
+ });
+
+ let busyEventCount = 0,
+ readyEventCount = 0,
+ tabRestoredCount = 0;
+ // These will listen to the reopened closed window...
+ function onSSWindowStateBusy(aEvent) {
+ busyEventCount++;
+ }
+
+ function onSSWindowStateReady(aEvent) {
+ readyEventCount++;
+ }
+
+ function onSSTabRestored(aEvent) {
+ if (++tabRestoredCount < 4)
+ return;
+
+ is(busyEventCount, 1);
+ is(readyEventCount, 1);
+
+ reopenedWindow.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+ reopenedWindow.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ reopenedWindow.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored);
+
+ reopenedWindow.close();
+
+ runNextTest();
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_625257.js b/comm/suite/components/tests/browser/browser_625257.js
new file mode 100644
index 0000000000..b8dff1d233
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_625257.js
@@ -0,0 +1,87 @@
+/* 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/. */
+
+// This tests that a tab which is closed while loading is not lost.
+// Specifically, that session store does not rely on an invalid cache when
+// constructing data for a tab which is loading.
+
+// The newly created tab which we load a URL into and try closing/undoing.
+var tab;
+
+// This test steps through the following parts:
+// 1. Tab has been created is loading URI_TO_LOAD.
+// 2. Before URI_TO_LOAD finishes loading, browser.currentURI has changed and
+// tab is scheduled to be removed.
+// 3. After the tab has been closed, undoCloseTab() has been called and the tab
+// should fully load.
+const URI_TO_LOAD = "about:logo";
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("browser.tabs.max_tabs_undo", 0);
+ getBrowser().addTabsProgressListener(tabsListener);
+
+ tab = getBrowser().addTab();
+
+ tab.linkedBrowser.addEventListener("load", firstOnLoad, true);
+
+ getBrowser().tabContainer.addEventListener("TabClose", onTabClose, true);
+}
+
+function firstOnLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener("load", firstOnLoad, true);
+
+ let uri = aEvent.target.location;
+ is(uri, "about:blank", "first load should be for about:blank");
+
+ // Trigger a save state.
+ ss.getBrowserState();
+
+ is(getBrowser().tabs[1], tab, "newly created tab should exist by now");
+ ok(tab.linkedBrowser.__SS_data, "newly created tab should be in save state");
+
+ tab.linkedBrowser.loadURI(URI_TO_LOAD);
+}
+
+var tabsListener = {
+ onLocationChange: function onLocationChange(aBrowser) {
+ getBrowser().removeTabsProgressListener(tabsListener);
+
+ is(aBrowser.currentURI.spec, URI_TO_LOAD,
+ "should occur after about:blank load and be loading next page");
+
+ // Since we are running in the context of tabs listeners, we do not
+ // want to disrupt other tabs listeners.
+ executeSoon(function() {
+ getBrowser().removeTab(tab);
+ });
+ }
+};
+
+function onTabClose(aEvent) {
+ getBrowser().tabContainer.removeEventListener("TabClose", onTabClose, true);
+
+ is(tab.linkedBrowser.currentURI.spec, URI_TO_LOAD,
+ "should only remove when loading page");
+
+ executeSoon(function() {
+ tab = ss.undoCloseTab(window, 0);
+ tab.linkedBrowser.addEventListener("load", secondOnLoad, true);
+ });
+}
+
+function secondOnLoad(aEvent) {
+ let uri = aEvent.target.location;
+ is(uri, URI_TO_LOAD, "should load page from undoCloseTab");
+ done();
+}
+
+function done() {
+ tab.linkedBrowser.removeEventListener("load", secondOnLoad, true);
+ getBrowser().removeTab(tab);
+ Services.prefs.clearUserPref("browser.tabs.max_tabs_undo");
+
+ executeSoon(finish);
+}
diff --git a/comm/suite/components/tests/browser/browser_636279.js b/comm/suite/components/tests/browser/browser_636279.js
new file mode 100644
index 0000000000..57d976a760
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_636279.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+var stateBackup = ss.getBrowserState();
+
+var statePinned = {windows:[{tabs:[
+ {entries:[{url:"http://example.com#1"}], pinned: true}
+]}]};
+
+var state = {windows:[{tabs:[
+ {entries:[{url:"http://example.com#1"}]},
+ {entries:[{url:"http://example.com#2"}]},
+ {entries:[{url:"http://example.com#3"}]},
+ {entries:[{url:"http://example.com#4"}]},
+]}]};
+
+function test() {
+ waitForExplicitFinish();
+
+ registerCleanupFunction(function () {
+ TabsProgressListener.uninit();
+ ss.setBrowserState(stateBackup);
+ });
+
+
+ TabsProgressListener.init();
+
+ window.addEventListener("SSWindowStateReady", function onReady() {
+ window.removeEventListener("SSWindowStateReady", onReady);
+
+ let firstProgress = true;
+
+ TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
+ if (firstProgress) {
+ firstProgress = false;
+ is(isRestoring, 3, "restoring 3 tabs concurrently");
+ } else {
+ ok(isRestoring <= 3, "restoring max. 2 tabs concurrently");
+ }
+
+ if (0 == needsRestore) {
+ TabsProgressListener.unsetCallback();
+ waitForFocus(finish);
+ }
+ });
+
+ ss.setBrowserState(JSON.stringify(state));
+ });
+
+ ss.setBrowserState(JSON.stringify(statePinned));
+}
+
+function countTabs() {
+ let needsRestore = 0, isRestoring = 0;
+ let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+
+ while (windowsEnum.hasMoreElements()) {
+ let window = windowsEnum.getNext();
+ if (window.closed)
+ continue;
+
+ for (let i = 0; i < window.getBrowser().tabs.length; i++) {
+ let browser = window.getBrowser().tabs[i].linkedBrowser;
+ if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ isRestoring++;
+ else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ needsRestore++;
+ }
+ }
+
+ return [needsRestore, isRestoring];
+}
+
+var TabsProgressListener = {
+ init: function () {
+ getBrowser().addTabsProgressListener(this);
+ },
+
+ uninit: function () {
+ this.unsetCallback();
+ getBrowser().removeTabsProgressListener(this);
+ },
+
+ setCallback: function (callback) {
+ this.callback = callback;
+ },
+
+ unsetCallback: function () {
+ delete this.callback;
+ },
+
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+ this.callback.apply(null, countTabs());
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_637020.js b/comm/suite/components/tests/browser/browser_637020.js
new file mode 100644
index 0000000000..b035a8c1f8
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_637020.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_URL = "http://mochi.test:8888/browser/browser/components/" +
+ "sessionstore/test/browser_637020_slow.sjs";
+
+const TEST_STATE = {
+ windows: [{
+ tabs: [
+ { entries: [{ url: "about:mozilla" }] },
+ { entries: [{ url: "about:robots" }] }
+ ]
+ }, {
+ tabs: [
+ { entries: [{ url: TEST_URL }] },
+ { entries: [{ url: TEST_URL }] }
+ ]
+ }]
+};
+
+function test() {
+ TestRunner.run();
+}
+
+/**
+ * This test ensures that windows that have just been restored will be marked
+ * as dirty, otherwise _getCurrentState() might ignore them when collecting
+ * state for the first time and we'd just save them as empty objects.
+ *
+ * The dirty state acts as a cache to not collect data from all windows all the
+ * time, so at the beginning, each window must be dirty so that we collect
+ * their state at least once.
+ */
+
+async function runTests() {
+ let win;
+
+ // Wait until the new window has been opened.
+ Services.obs.addObserver(function onOpened(subject) {
+ Services.obs.removeObserver(onOpened, "domwindowopened");
+ win = subject;
+ executeSoon(next);
+ }, "domwindowopened");
+
+ // Set the new browser state that will
+ // restore a window with two slowly loading tabs.
+ await SessionStore.setBrowserState(JSON.stringify(TEST_STATE));
+
+ // The window has now been opened. Check the state that is returned,
+ // this should come from the cache while the window isn't restored, yet.
+ info("the window has been opened");
+ checkWindows();
+
+ // The history has now been restored and the tabs are loading. The data must
+ // now come from the window, if it's correctly been marked as dirty before.
+ await whenDelayedStartupFinished(win, next);
+ info("the delayed startup has finished");
+ checkWindows();
+}
+
+function checkWindows() {
+ let state = JSON.parse(SessionStore.getBrowserState());
+ is(state.windows[0].tabs.length, 2, "first window has two tabs");
+ is(state.windows[1].tabs.length, 2, "second window has two tabs");
+}
diff --git a/comm/suite/components/tests/browser/browser_637020_slow.sjs b/comm/suite/components/tests/browser/browser_637020_slow.sjs
new file mode 100644
index 0000000000..63953b762f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_637020_slow.sjs
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const DELAY_MS = "2000";
+
+let timer;
+
+function handleRequest(req, resp) {
+ resp.processAsync();
+ resp.setHeader("Cache-Control", "no-cache", false);
+ resp.setHeader("Content-Type", "text/html;charset=utf-8", false);
+
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.init(() => {
+ resp.write("hi");
+ resp.finish();
+ }, DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
+}
diff --git a/comm/suite/components/tests/browser/browser_645428.js b/comm/suite/components/tests/browser/browser_645428.js
new file mode 100644
index 0000000000..bbb3b1b299
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_645428.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const NOTIFICATION = "sessionstore-browser-state-restored";
+
+function test() {
+ waitForExplicitFinish();
+
+ function observe(subject, topic, data) {
+ if (NOTIFICATION == topic) {
+ finish();
+ ok(true, "TOPIC received");
+ }
+ }
+
+ Services.obs.addObserver(observe, NOTIFICATION);
+ registerCleanupFunction(function () {
+ Services.obs.removeObserver(observe, NOTIFICATION);
+ });
+
+ ss.setBrowserState(JSON.stringify({ windows: [] }));
+}
diff --git a/comm/suite/components/tests/browser/browser_665702-state_session.js b/comm/suite/components/tests/browser/browser_665702-state_session.js
new file mode 100644
index 0000000000..e467337313
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_665702-state_session.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function compareArray(a, b) {
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function test() {
+ let currentState = JSON.parse(ss.getBrowserState());
+ ok(currentState.session, "session data returned by getBrowserState");
+
+ let keys = Object.keys(currentState.session);
+ let expectedKeys = ["state", "lastUpdate", "startTime", "recentCrashes"];
+ info("keys "+JSON.stringify(keys.sort())+" expectedKeys "+JSON.stringify(expectedKeys.sort()));
+ ok(compareArray(keys.sort(), expectedKeys.sort()),
+ "session object from getBrowserState has correct keys");
+}
diff --git a/comm/suite/components/tests/browser/browser_687710.js b/comm/suite/components/tests/browser/browser_687710.js
new file mode 100644
index 0000000000..372ecf7ae5
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_687710.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that sessionrestore handles cycles in the shentry graph properly.
+//
+// These cycles shouldn't be there in the first place, but they cause hangs
+// when they mysteriously appear (bug 687710). Docshell code assumes this
+// graph is a tree and tires to walk to the root. But if there's a cycle,
+// there is no root, and we loop forever.
+
+var stateBackup = ss.getBrowserState();
+
+var state = {windows:[{tabs:[{entries:[
+ {
+ docIdentifier: 1,
+ url: "http://example.com",
+ children: [
+ {
+ docIdentifier: 2,
+ url: "http://example.com"
+ }
+ ]
+ },
+ {
+ docIdentifier: 2,
+ url: "http://example.com",
+ children: [
+ {
+ docIdentifier: 1,
+ url: "http://example.com"
+ }
+ ]
+ }
+]}]}]}
+
+function test() {
+ registerCleanupFunction(function () {
+ ss.setBrowserState(stateBackup);
+ });
+
+ /* This test fails by hanging. */
+ ss.setBrowserState(JSON.stringify(state));
+ ok(true, "Didn't hang!");
+}
diff --git a/comm/suite/components/tests/browser/browser_687710_2.js b/comm/suite/components/tests/browser/browser_687710_2.js
new file mode 100644
index 0000000000..5d46bd94d6
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_687710_2.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the fix for bug 687710 isn't too aggressive -- shentries which are
+// cousins should be able to share bfcache entries.
+
+var stateBackup = ss.getBrowserState();
+
+var state = {entries:[
+ {
+ docIdentifier: 1,
+ url: "http://example.com?1",
+ children: [{ docIdentifier: 10,
+ url: "http://example.com?10" }]
+ },
+ {
+ docIdentifier: 1,
+ url: "http://example.com?1#a",
+ children: [{ docIdentifier: 10,
+ url: "http://example.com?10#aa" }]
+ }
+]};
+
+function test()
+{
+ registerCleanupFunction(function () {
+ ss.setBrowserState(stateBackup);
+ });
+
+ let tab = getBrowser().addTab("about:blank");
+ ss.setTabState(tab, JSON.stringify(state));
+ let history = tab.linkedBrowser.webNavigation.sessionHistory;
+
+ is(history.count, 2, "history.count");
+ for (let i = 0; i < history.count; i++) {
+ for (let j = 0; j < history.count; j++) {
+ compareEntries(i, j, history);
+ }
+ }
+}
+
+function compareEntries(i, j, history)
+{
+ let e1 = history.getEntryAtIndex(i)
+ .QueryInterface(Ci.nsISHEntry)
+ .QueryInterface(Ci.nsISHContainer);
+
+ let e2 = history.getEntryAtIndex(j)
+ .QueryInterface(Ci.nsISHEntry)
+ .QueryInterface(Ci.nsISHContainer);
+
+ ok(e1.sharesDocumentWith(e2),
+ i + ' should share doc with ' + j);
+ is(e1.childCount, e2.childCount,
+ 'Child count mismatch (' + i + ', ' + j + ')');
+
+ for (let c = 0; c < e1.childCount; c++) {
+ let c1 = e1.GetChildAt(c);
+ let c2 = e2.GetChildAt(c);
+
+ ok(c1.sharesDocumentWith(c2),
+ 'Cousins should share documents. (' + i + ', ' + j + ', ' + c + ')');
+ }
+}
diff --git a/comm/suite/components/tests/browser/browser_694378.js b/comm/suite/components/tests/browser/browser_694378.js
new file mode 100644
index 0000000000..8578428d8f
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_694378.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test Summary:
+// 1. call ss.setWindowState with a broken state
+// 1a. ensure that it doesn't throw.
+
+function test() {
+ waitForExplicitFinish();
+
+ let brokenState = {
+ windows: [
+ { tabs: [{ entries: [{ url: "about:mozilla" }] }] }
+ ],
+ selectedWindow: 2
+ };
+ let brokenStateString = JSON.stringify(brokenState);
+
+ let gotError = false;
+ try {
+ ss.setWindowState(window, brokenStateString, true);
+ }
+ catch (ex) {
+ gotError = true;
+ info(ex);
+ }
+
+ ok(!gotError, "ss.setWindowState did not throw an error");
+
+ // Make sure that we reset the state. Use a full state just in case things get crazy.
+ let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank" }] }]}]};
+ waitForBrowserState(blankState, finish);
+}
diff --git a/comm/suite/components/tests/browser/browser_bug431826.js b/comm/suite/components/tests/browser/browser_bug431826.js
new file mode 100644
index 0000000000..f4430fc95d
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_bug431826.js
@@ -0,0 +1,42 @@
+function test() {
+ waitForExplicitFinish();
+
+ getBrowser().selectedTab = gBrowser.addTab();
+
+ // Navigate to a site with a broken cert
+ window.addEventListener("DOMContentLoaded", testBrokenCert, true);
+ content.location = "https://nocert.example.com/";
+}
+
+function testBrokenCert() {
+ window.removeEventListener("DOMContentLoaded", testBrokenCert, true);
+
+ // Confirm that we are displaying the contributed error page, not the default
+ ok(/^about:certerror/.test(gBrowser.contentDocument.documentURI), "Broken page should go to about:certerror, not about:neterror");
+
+ // Confirm that the expert section is collapsed
+ var expertDiv = gBrowser.contentDocument.getElementById("expertContent");
+ ok(expertDiv, "Expert content div should exist");
+ ok(expertDiv.hasAttribute("collapsed"), "Expert content should be collapsed by default");
+
+ // Tweak the expert mode pref
+ Services.prefs.setBoolPref("browser.xul.error_pages.expert_bad_cert", true);
+
+ window.addEventListener("DOMContentLoaded", testExpertPref, true);
+ getBrowser().reload();
+}
+
+function testExpertPref() {
+ window.removeEventListener("DOMContentLoaded", testExpertPref, true);
+
+ var expertDiv = gBrowser.contentDocument.getElementById("expertContent");
+ var technicalDiv = gBrowser.contentDocument.getElementById("technicalContent");
+ ok(!expertDiv.hasAttribute("collapsed"), "Expert content should not be collapsed with the expert mode pref set");
+ ok(!technicalDiv.hasAttribute("collapsed"), "Technical content should not be collapsed with the expert mode pref set");
+
+ // Clean up
+ getBrowser().removeCurrentTab();
+ if (Services.prefs.prefHasUserValue("browser.xul.error_pages.expert_bad_cert"))
+ Services.prefs.clearUserPref("browser.xul.error_pages.expert_bad_cert");
+ finish();
+}
diff --git a/comm/suite/components/tests/browser/browser_isempty.js b/comm/suite/components/tests/browser/browser_isempty.js
new file mode 100644
index 0000000000..84ae62b4cb
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_isempty.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 589659 - Lots of mozapps/extensions/test/ failures
+// This introduced isTabEmpty() and isBrowserEmpty() functions, the latter
+// being used in openUILinkIn() which in turn is used by switchToTabHavingURI()
+
+var gWindowObject;
+var gTabCount;
+
+function test() {
+ waitForExplicitFinish();
+ gTabCount = gBrowser.tabs.length;
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ is(isTabEmpty(gBrowser.selectedTab), true, "Added tab is empty");
+ switchToTabHavingURI("about:", true, function(aBrowser) {
+ gWindowObject = aBrowser.contentWindow.wrappedJSObject;
+ end_test();
+ });
+}
+
+function end_test() {
+ gWindowObject.close();
+ is(gBrowser.tabs.length, gTabCount, "We're still at the same number of tabs");
+ finish();
+}
diff --git a/comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js b/comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js
new file mode 100644
index 0000000000..d8ab033cb0
--- /dev/null
+++ b/comm/suite/components/tests/browser/browser_markPageAsFollowedLink.js
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests that visits across frames are correctly represented in the database.
+ */
+
+const BASE_URL = "http://mochi.test:8888/browser/suite/common/tests/browser";
+const PAGE_URL = BASE_URL + "/framedPage.html";
+const LEFT_URL = BASE_URL + "/frameLeft.html";
+const RIGHT_URL = BASE_URL + "/frameRight.html";
+
+var gTabLoaded = false;
+var gLeftFrameVisited = false;
+
+var observer = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ let url = aSubject.QueryInterface(Ci.nsIURI).spec;
+ if (url == LEFT_URL ) {
+ is(getTransitionForUrl(url), null,
+ "Embed visits should not get a database entry.");
+ gLeftFrameVisited = true;
+ maybeClickLink();
+ }
+ else if (url == RIGHT_URL ) {
+ is(getTransitionForUrl(url), PlacesUtils.history.TRANSITION_FRAMED_LINK,
+ "User activated visits should get a FRAMED_LINK transition.");
+ finish();
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+};
+Services.obs.addObserver(observer, "uri-visit-saved");
+
+function test()
+{
+ waitForExplicitFinish();
+ gBrowser.selectedTab = gBrowser.addTab(PAGE_URL);
+ let frameCount = 0;
+ gBrowser.selectedTab.linkedBrowser.addEventListener("DOMContentLoaded",
+ function gBrowserDOMContentLoaded(event)
+ {
+ // Wait for all the frames.
+ if (frameCount++ < 2)
+ return;
+ gBrowser.selectedTab.linkedBrowser.removeEventListener("DOMContentLoaded",
+ gBrowserDOMContentLoaded)
+ gTabLoaded = true;
+ maybeClickLink();
+ }
+ );
+}
+
+function maybeClickLink() {
+ if (gTabLoaded && gLeftFrameVisited) {
+ // Click on the link in the left frame to cause a page load in the
+ // right frame.
+ EventUtils.sendMouseEvent({type: "click"}, "clickme", content.frames[0]);
+ }
+}
+
+function getTransitionForUrl(aUrl)
+{
+ let dbConn = PlacesUtils.history
+ .QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
+ let stmt = dbConn.createStatement(
+ "SELECT visit_type FROM moz_historyvisits WHERE place_id = " +
+ "(SELECT id FROM moz_places WHERE url = :page_url)");
+ stmt.params.page_url = aUrl;
+ try {
+ if (!stmt.executeStep()) {
+ return null;
+ }
+ return stmt.row.visit_type;
+ }
+ finally {
+ stmt.finalize();
+ }
+}
+
+registerCleanupFunction(function ()
+{
+ gBrowser.removeTab(gBrowser.selectedTab);
+ Services.obs.removeObserver(observer, "uri-visit-saved");
+})
diff --git a/comm/suite/components/tests/browser/frameLeft.html b/comm/suite/components/tests/browser/frameLeft.html
new file mode 100644
index 0000000000..5a54fe353b
--- /dev/null
+++ b/comm/suite/components/tests/browser/frameLeft.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>Left frame</title>
+ </head>
+ <body>
+ <a id="clickme" href="frameRight.html" target="right">Open page in the right frame.</a>
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/frameRight.html b/comm/suite/components/tests/browser/frameRight.html
new file mode 100644
index 0000000000..226accc349
--- /dev/null
+++ b/comm/suite/components/tests/browser/frameRight.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>Right Frame</title>
+ </head>
+ <body>
+ This is the right frame.
+ </body>
+</html>
diff --git a/comm/suite/components/tests/browser/framedPage.html b/comm/suite/components/tests/browser/framedPage.html
new file mode 100644
index 0000000000..d388562e6e
--- /dev/null
+++ b/comm/suite/components/tests/browser/framedPage.html
@@ -0,0 +1,9 @@
+<html>
+ <head>
+ <title>Framed page</title>
+ </head>
+ <frameset cols="*,*">
+ <frame name="left" src="frameLeft.html">
+ <frame name="right" src="about:mozilla">
+ </frameset>
+</html>
diff --git a/comm/suite/components/tests/browser/head.js b/comm/suite/components/tests/browser/head.js
new file mode 100644
index 0000000000..531319d3ff
--- /dev/null
+++ b/comm/suite/components/tests/browser/head.js
@@ -0,0 +1,151 @@
+/* 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 ss = Cc["@mozilla.org/suite/sessionstore;1"]
+ .getService(Ci.nsISessionStore);
+
+function provideWindow(aCallback, aURL, aFeatures) {
+ function callbackSoon(aWindow) {
+ executeSoon(function executeCallbackSoon() {
+ aCallback(aWindow);
+ });
+ }
+
+ let win = openDialog(getBrowserURL(), "", aFeatures || "chrome,all,dialog=no", aURL);
+ whenWindowLoaded(win, function onWindowLoaded(aWin) {
+ if (!aURL) {
+ info("Loaded a blank window.");
+ callbackSoon(aWin);
+ return;
+ }
+
+ aWin.gBrowser.selectedBrowser.addEventListener("load", function selectedBrowserLoadListener() {
+ aWin.gBrowser.selectedBrowser.removeEventListener("load", selectedBrowserLoadListener, true);
+ callbackSoon(aWin);
+ }, true);
+ });
+}
+
+// This assumes that tests will at least have some state/entries
+function waitForBrowserState(aState, aSetStateCallback) {
+ let windows = [window];
+ let tabsRestored = 0;
+ let expectedTabsRestored = 0;
+ let expectedWindows = aState.windows.length;
+ let windowsOpen = 1;
+ let listening = false;
+ let windowObserving = false;
+
+ aState.windows.forEach(winState => expectedTabsRestored += winState.tabs.length);
+
+ function onSSTabRestored(aEvent) {
+ if (++tabsRestored == expectedTabsRestored) {
+ // Remove the event listener from each window
+ windows.forEach(function(win) {
+ win.getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true);
+ });
+ listening = false;
+ info("running " + aSetStateCallback.name);
+ executeSoon(aSetStateCallback);
+ }
+ }
+
+ // Used to add our listener to further windows so we can catch SSTabRestored
+ // coming from them when creating a multi-window state.
+ function windowObserver(aSubject, aTopic, aData) {
+ if (aTopic == "domwindowopened") {
+ let newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
+ newWindow.addEventListener("load", function newWindowLoad() {
+ newWindow.removeEventListener("load", newWindowLoad);
+
+ if (++windowsOpen == expectedWindows) {
+ Services.ww.unregisterNotification(windowObserver);
+ windowObserving = false;
+ }
+
+ // Track this window so we can remove the progress listener later
+ windows.push(newWindow);
+ // Add the progress listener
+ newWindow.getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true);
+ });
+ }
+ }
+
+ // We only want to register the notification if we expect more than 1 window
+ if (expectedWindows > 1) {
+ registerCleanupFunction(function() {
+ if (windowObserving) {
+ Services.ww.unregisterNotification(windowObserver);
+ }
+ });
+ windowObserving = true;
+ Services.ww.registerNotification(windowObserver);
+ }
+
+ registerCleanupFunction(function() {
+ if (listening) {
+ windows.forEach(function(win) {
+ win.getBrowser().tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, true);
+ });
+ }
+ });
+ // Add the event listener for this window as well.
+ listening = true;
+ getBrowser().tabContainer.addEventListener("SSTabRestored", onSSTabRestored, true);
+
+ // Finally, call setBrowserState
+ ss.setBrowserState(JSON.stringify(aState));
+}
+
+// waitForSaveState waits for a state write but not necessarily for the state to
+// turn dirty.
+function waitForSaveState(aSaveStateCallback) {
+ let observing = false;
+ let topic = "sessionstore-state-write";
+
+ let sessionSaveTimeout = 1000 +
+ Services.prefs.getIntPref("browser.sessionstore.interval");
+
+ function removeObserver() {
+ if (!observing)
+ return;
+ Services.obs.removeObserver(observer, topic, false);
+ observing = false;
+ }
+
+ let timeout = setTimeout(function () {
+ removeObserver();
+ aSaveStateCallback();
+ }, sessionSaveTimeout);
+
+ function observer(aSubject, aTopic, aData) {
+ removeObserver();
+ timeout = clearTimeout(timeout);
+ executeSoon(aSaveStateCallback);
+ }
+
+ registerCleanupFunction(function() {
+ removeObserver();
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ });
+
+ observing = true;
+ Services.obs.addObserver(observer, topic);
+};
+
+function whenWindowLoaded(aWindow, aCallback) {
+ aWindow.addEventListener("load", function windowLoadListener() {
+ aWindow.removeEventListener("load", windowLoadListener);
+ executeSoon(function executeWhenWindowLoaded() {
+ aCallback(aWindow);
+ });
+ });
+}
+
+var gUniqueCounter = 0;
+function r() {
+ return Date.now() + "-" + (++gUniqueCounter);
+}
diff --git a/comm/suite/components/tests/chrome/chrome.ini b/comm/suite/components/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..cf58b1085c
--- /dev/null
+++ b/comm/suite/components/tests/chrome/chrome.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[test_idcheck.xul]
+support-files = ../../../../mailnews/test/resources/mailTestUtils.js
diff --git a/comm/suite/components/tests/chrome/test_idcheck.xul b/comm/suite/components/tests/chrome/test_idcheck.xul
new file mode 100644
index 0000000000..213fda008a
--- /dev/null
+++ b/comm/suite/components/tests/chrome/test_idcheck.xul
@@ -0,0 +1,302 @@
+<?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://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Chrome Window ID Checking Tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTest();">
+ <description>Chrome Window ID Checking Tests</description>
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/chrome-harness.js"/>
+
+ <script>
+ <![CDATA[
+ let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ getService(Ci.mozIJSSubScriptLoader);
+
+ let rootDir = getRootDirectory(window.location.href);
+ scriptLoader.loadSubScript(rootDir + "mailTestUtils.js", this);
+
+ var gLoadedWindows = {};
+
+ // Have all loaded windows been closed again?
+ function AllWindowsClosed()
+ {
+ return Object.keys(window.gLoadedWindows).length == 0;
+ }
+
+ function DumpElementTree(aElement)
+ {
+ // walk upwards from element to DOM root
+ while (aElement)
+ {
+ // print nodeName, id and index in parent
+ let s = " " + aElement.nodeName + " " + aElement.id + " ";
+ let ix = 0;
+ while (aElement.previousSibling)
+ {
+ aElement = aElement.previousSibling;
+ ++ix;
+ }
+ dump(s + "[" + ix + "]\n");
+ aElement = aElement.parentNode;
+ }
+ }
+
+ function CheckIDs(aDocument, aIgnorableIDs)
+ {
+ var filename = aDocument.location.href.match(/[^/]+$/);
+ // all panes are loaded, now check the ids
+ // first, get the list of all used ids
+ var idList = aDocument.getElementsByAttribute("id", "*");
+ // then store them in another object, checking if it's already there
+ var checkedList = {};
+ var ignoredList = {};
+ for (let i = 0; i < idList.length; ++i)
+ {
+ let id = idList[i].id;
+ let duplicate = (id in checkedList);
+ if (!duplicate)
+ {
+ checkedList[id] = idList[i];
+ }
+ else
+ {
+ // always dump DOM trees of the conflicting elements
+ dump("Double id='" + id + "' detected in " + aDocument.location.href + ":\n");
+ dump(" Tree 0:\n");
+ DumpElementTree(checkedList[id]);
+ dump(" Tree 1:\n");
+ DumpElementTree(idList[i]);
+ }
+ if (!aIgnorableIDs.includes(id))
+ {
+ // if the id is not in our ignore list, show its status
+ ok(!duplicate, "check id: " + filename + "#" + id);
+ }
+ else if (!(id in ignoredList))
+ {
+ // mark ignored id tests as todo,
+ // even though we may never (be able to) fix them
+ ignoredList[id] = idList[i];
+ todo(false, "disabled id checks: " + filename + "#" + id);
+ }
+ }
+
+ // finally, close the loaded window
+ aDocument.defaultView.close();
+ }
+
+ function DisambiguateCharsetMenulist(aDocument, aListID, aPrefix)
+ {
+ let menulist = aDocument.getElementById(aListID)
+ .getElementsByTagName("menuitem");
+ for (let menuitem of menulist)
+ {
+ menuitem.id = aPrefix + menuitem.id;
+ menuitem = menuitem.nextSibling;
+ }
+ }
+
+ function LoadPaneLoop(aDocument, aPanes, aPaneIndex, aForceLoad)
+ {
+ if (aPaneIndex < aPanes.length)
+ {
+ const WAIT_CYCLE = 10;
+ // may need to load this pane
+ let pane = aPanes[aPaneIndex];
+ if (pane.loaded)
+ {
+ // okay, check/load next one
+ setTimeout(LoadPaneLoop, WAIT_CYCLE, aDocument, aPanes, aPaneIndex + 1, true);
+ }
+ else
+ {
+ // force load once and wait until done
+ if (aForceLoad)
+ {
+ try
+ {
+ aDocument.documentElement.showPane.call(aDocument.documentElement, pane);
+ }
+ catch (ignored) {}
+ }
+ setTimeout(LoadPaneLoop, WAIT_CYCLE, aDocument, aPanes, aPaneIndex, false);
+ }
+ }
+ else
+ {
+ // All preference panes are loaded now!
+
+ // The character_encoding_pane contains two template driven menulists
+ // (viewDefaultCharsetList and sendDefaultCharsetList),
+ // both of which autogenerate the *same* ids for their menuitems.
+ // The same ids are generated by the charset list (defaultCharsetList)
+ // on the languages_pane, too.
+ // We alter two of these sets here to avoid unnecessary test failure.
+ // (We probably should remove those RDF templates?)
+ DisambiguateCharsetMenulist(aDocument, "viewDefaultCharsetList", "test_idcheck.1.");
+ DisambiguateCharsetMenulist(aDocument, "sendDefaultCharsetList", "test_idcheck.2.");
+
+ // now check the ids
+ CheckIDs(aDocument, window.gLoadedWindows[aDocument.location.href]);
+ }
+ }
+
+ function CheckPreferences()
+ {
+ this.removeEventListener("load", window.CheckPreferences, false);
+
+ // Prefpanes are loaded lazily, thus we need to trigger each panel manually
+ // before we can check for doubled ids...
+ var panes = this.document.getElementsByTagName("prefpane");
+ setTimeout(LoadPaneLoop, 0, this.document, panes, 0, true);
+ }
+
+ function CheckGenerics()
+ {
+ this.removeEventListener("load", window.CheckGenerics, false);
+ CheckIDs(this.document, window.gLoadedWindows[this.location.href]);
+ }
+
+ function UncountWindow()
+ {
+ if (this.location.href in window.gLoadedWindows)
+ {
+ this.removeEventListener("unload", window.UncountWindow, false);
+ delete window.gLoadedWindows[this.location.href];
+ }
+ }
+
+ function InitTest()
+ {
+ // fake a mail account to avoid the account creation wizard
+ loadLocalMailAccount();
+ }
+
+ function ExitTest()
+ {
+ // remove the mailnews data from the test profile
+ Services.prefs.resetPrefs();
+ }
+
+ function FinishTest()
+ {
+ if (AllWindowsClosed())
+ {
+ // commented out to fix test failures after this due to missing prefs
+ //ExitTest();
+ SimpleTest.finish();
+ }
+ else
+ {
+ setTimeout(FinishTest, 1000);
+ }
+ }
+
+ function RunTest()
+ {
+ SimpleTest.waitForExplicitFinish();
+ InitTest();
+
+ // Basically, this test framework is generic enough to check arbitrary
+ // chrome windows for doubled ids. But certain stuff like preferences
+ // needs some extra processing.
+ // The uriList members have the following format:
+ // "chrome://uri/of/xul.window":
+ // [
+ // check function,
+ // array of IDs to be ignored during in the test
+ // ],
+ var uriList =
+ {
+ // Preferences
+ "chrome://communicator/content/pref/preferences.xul":
+ [
+ window.CheckPreferences,
+ []
+ ],
+
+ // Browser
+ "chrome://navigator/content/navigator.xul":
+ [
+ window.CheckGenerics,
+ ["contentAreaContextSet"]
+ ],
+
+ // MailNews (needs at least one mail account)
+ "chrome://messenger/content/messenger.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+ "chrome://messenger/content/messageWindow.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+ "chrome://messenger/content/messengercompose/messengercompose.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Addressbook (needs at least one mail account)
+ "chrome://messenger/content/addressbook/addressbook.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Composer
+ "chrome://editor/content/editor.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Error Console
+ "chrome://communicator/content/console/console.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+
+ // Chatzilla
+ "chrome://chatzilla/content/chatzilla.xul":
+ [
+ window.CheckGenerics,
+ []
+ ],
+ };
+
+ // run test
+ for (var uri in uriList)
+ {
+ // load the window, but postpone the id check until it's fully loaded
+ window.gLoadedWindows[uri] = uriList[uri][1]; // ignore these ids
+ var win = openDialog(uri, "", "chrome,titlebar,dialog=no,resizable");
+ win.addEventListener("load", uriList[uri][0], false);
+ win.addEventListener("unload", window.UncountWindow, false);
+ }
+
+ // wait for all tests to finish
+ SimpleTest.executeSoon(FinishTest);
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+
+</window>
diff --git a/comm/suite/config/mozconfigs/common b/comm/suite/config/mozconfigs/common
new file mode 100644
index 0000000000..36f268a11c
--- /dev/null
+++ b/comm/suite/config/mozconfigs/common
@@ -0,0 +1,27 @@
+# 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/.
+
+# This file is included by all suite mozconfigs
+
+# Disable checking that add-ons are signed by the trusted root
+MOZ_ADDON_SIGNING=0
+# Disable enforcing that add-ons are signed by the trusted root
+MOZ_REQUIRE_SIGNING=0
+
+mk_add_options "export MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS}"
+mk_add_options "export MOZ_SCM_LEVEL=3"
+# Post bug 1422735, we now send symbols to Tecken.
+# Post bug 1422735, we now send symbols to Tecken.
+# See bug 1422737. Despite it saying SOCORRO, it's actually using
+# the new Tecken symbol token.
+
+case "$platform" in
+ win*)
+ mk_add_options "export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=c:/builds/crash-stats-api.token"
+ ;;
+ *)
+ mk_add_options "export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token"
+ ;;
+esac
+
diff --git a/comm/suite/config/mozconfigs/linux32/debug b/comm/suite/config/mozconfigs/linux32/debug
new file mode 100644
index 0000000000..d4392a64f2
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux32/debug
@@ -0,0 +1,21 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux32
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-debug
+ac_add_options --enable-calendar
+
+mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON3) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+#Use ccache
+ac_add_options --with-ccache=/usr/bin/ccache
diff --git a/comm/suite/config/mozconfigs/linux32/l10n-mozconfig b/comm/suite/config/mozconfigs/linux32/l10n-mozconfig
new file mode 100644
index 0000000000..e9dbbbf021
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux32/l10n-mozconfig
@@ -0,0 +1,4 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux32
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common
diff --git a/comm/suite/config/mozconfigs/linux32/nightly b/comm/suite/config/mozconfigs/linux32/nightly
new file mode 100644
index 0000000000..364eab3a08
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux32/nightly
@@ -0,0 +1,25 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux32
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-profiling
+ac_add_options --enable-calendar
+
+# Bug 1441155 - Disable the generation of Rust debug symbols on Linux32
+ac_add_options --disable-debug-symbols
+
+mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON3) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+#Use ccache
+ac_add_options --with-ccache=/usr/bin/ccache
diff --git a/comm/suite/config/mozconfigs/linux32/release b/comm/suite/config/mozconfigs/linux32/release
new file mode 100644
index 0000000000..8795b21585
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux32/release
@@ -0,0 +1,25 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux32
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-official-branding
+ac_add_options --enable-calendar
+
+# Bug 1441155 - Disable the generation of Rust debug symbols on Linux32
+ac_add_options --disable-debug-symbols
+
+mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON3) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+#Use ccache
+ac_add_options --with-ccache=/usr/bin/ccache
diff --git a/comm/suite/config/mozconfigs/linux32/release-l10n b/comm/suite/config/mozconfigs/linux32/release-l10n
new file mode 100644
index 0000000000..e9dbbbf021
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux32/release-l10n
@@ -0,0 +1,4 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux32
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common
diff --git a/comm/suite/config/mozconfigs/linux64/debug b/comm/suite/config/mozconfigs/linux64/debug
new file mode 100644
index 0000000000..9c0310bc5c
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux64/debug
@@ -0,0 +1,21 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-debug
+ac_add_options --enable-calendar
+
+mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON3) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+#Use ccache
+ac_add_options --with-ccache=/usr/bin/ccache
diff --git a/comm/suite/config/mozconfigs/linux64/l10n-mozconfig b/comm/suite/config/mozconfigs/linux64/l10n-mozconfig
new file mode 100644
index 0000000000..87b0df3fad
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux64/l10n-mozconfig
@@ -0,0 +1,4 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common
diff --git a/comm/suite/config/mozconfigs/linux64/nightly b/comm/suite/config/mozconfigs/linux64/nightly
new file mode 100644
index 0000000000..8b2750f86d
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux64/nightly
@@ -0,0 +1,22 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-profiling
+ac_add_options --enable-calendar
+
+mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON3) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+#Use ccache
+ac_add_options --with-ccache=/usr/bin/ccache
diff --git a/comm/suite/config/mozconfigs/linux64/release b/comm/suite/config/mozconfigs/linux64/release
new file mode 100644
index 0000000000..e6fbf8ffd9
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux64/release
@@ -0,0 +1,22 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-official-branding
+ac_add_options --enable-calendar
+
+mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON3) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+#Use ccache
+ac_add_options --with-ccache=/usr/bin/ccache
diff --git a/comm/suite/config/mozconfigs/linux64/release-l10n b/comm/suite/config/mozconfigs/linux64/release-l10n
new file mode 100644
index 0000000000..87b0df3fad
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux64/release-l10n
@@ -0,0 +1,4 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/unix/mozconfig.linux
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common
diff --git a/comm/suite/config/mozconfigs/linux64/source b/comm/suite/config/mozconfigs/linux64/source
new file mode 100644
index 0000000000..f2a9abc640
--- /dev/null
+++ b/comm/suite/config/mozconfigs/linux64/source
@@ -0,0 +1,7 @@
+# The source "build" only needs a mozconfig because we use the build system as
+# our script for generating it. This allows us to run configure without any
+# extra dependencies on specific toolchains, e.g. gtk3.
+ac_add_options --disable-compile-environment
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-calendar
diff --git a/comm/suite/config/mozconfigs/macosx64/debug b/comm/suite/config/mozconfigs/macosx64/debug
new file mode 100644
index 0000000000..e26ac69194
--- /dev/null
+++ b/comm/suite/config/mozconfigs/macosx64/debug
@@ -0,0 +1,17 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/macosx/mozconfig.common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-debug
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+ac_add_options --with-ccache
diff --git a/comm/suite/config/mozconfigs/macosx64/l10n-mozconfig b/comm/suite/config/mozconfigs/macosx64/l10n-mozconfig
new file mode 100644
index 0000000000..64d4cfaad1
--- /dev/null
+++ b/comm/suite/config/mozconfigs/macosx64/l10n-mozconfig
@@ -0,0 +1,16 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/macosx/mozconfig.common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --disable-install-strip
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
diff --git a/comm/suite/config/mozconfigs/macosx64/nightly b/comm/suite/config/mozconfigs/macosx64/nightly
new file mode 100644
index 0000000000..b1dce5005f
--- /dev/null
+++ b/comm/suite/config/mozconfigs/macosx64/nightly
@@ -0,0 +1,20 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/macosx/mozconfig.common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.macosx.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --disable-install-strip
+ac_add_options --enable-profiling
+ac_add_options --enable-calendar
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --with-ccache
diff --git a/comm/suite/config/mozconfigs/macosx64/release b/comm/suite/config/mozconfigs/macosx64/release
new file mode 100644
index 0000000000..b036ec99db
--- /dev/null
+++ b/comm/suite/config/mozconfigs/macosx64/release
@@ -0,0 +1,17 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/macosx/mozconfig.common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.macosx.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-official-branding
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
diff --git a/comm/suite/config/mozconfigs/macosx64/release-l10n b/comm/suite/config/mozconfigs/macosx64/release-l10n
new file mode 100644
index 0000000000..64d4cfaad1
--- /dev/null
+++ b/comm/suite/config/mozconfigs/macosx64/release-l10n
@@ -0,0 +1,16 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/macosx/mozconfig.common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --disable-install-strip
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
diff --git a/comm/suite/config/mozconfigs/mozconfig.gtk b/comm/suite/config/mozconfigs/mozconfig.gtk
new file mode 100644
index 0000000000..803c3e62b4
--- /dev/null
+++ b/comm/suite/config/mozconfigs/mozconfig.gtk
@@ -0,0 +1,22 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} # $TOOLTOOL_DIR/gtk3 comes from tooltool, and must be included in the tooltool manifest.
+if [ -z "$PKG_CONFIG_LIBDIR" ]; then
+ echo PKG_CONFIG_LIBDIR must be set >&2
+ exit 1
+fi
+export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3"
+export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig"
+PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:$TOOLTOOL_DIR/gtk3/usr/local/bin/pkg-config"
+export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}"
+# Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
+LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}"
+ac_add_options --enable-default-toolkit=cairo-gtk3
+
+# Set things up to use Gtk+3 from the tooltool package
+mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts"
+mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc"
+mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib"
+mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
+mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
+
+LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/gtk3/usr/local/lib
+mk_add_options "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
diff --git a/comm/suite/config/mozconfigs/mozconfig.linux.common b/comm/suite/config/mozconfigs/mozconfig.linux.common
new file mode 100644
index 0000000000..b08efbaed0
--- /dev/null
+++ b/comm/suite/config/mozconfigs/mozconfig.linux.common
@@ -0,0 +1,14 @@
+# Common statements that are applicable to both Linux32 and Linux64.
+
+export PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig
+export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:/usr/lib64/pkgconfig:/usr/lib/pkgconfig"
+# Due to bug 1426785, we need to re-include mozconfig.gtk with our
+# linux* builders.
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.gtk
+
+ac_add_options --with-google-location-service-api-keyfile=/builds/google-api.key
+ac_add_options --with-google-safebrowsing-api-keyfile=/builds/google-api.key
+
+# SeaMonkey uses the google api key for Geolocation services
+# See Bug 1352850 - Switch to Google for Geolocation services
+# ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
diff --git a/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common b/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common
new file mode 100644
index 0000000000..02d8e1b2cb
--- /dev/null
+++ b/comm/suite/config/mozconfigs/mozconfig.linux.l10n.common
@@ -0,0 +1,20 @@
+# Common statements that are applicable to both l10n Linux32 and Linux64.
+
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+# Due to bug 1426785, we need to re-include mozconfig.gtk with our
+# linux* builders.
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.gtk
+
+export PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
diff --git a/comm/suite/config/mozconfigs/mozconfig.macosx.common b/comm/suite/config/mozconfigs/mozconfig.macosx.common
new file mode 100644
index 0000000000..62bc61320f
--- /dev/null
+++ b/comm/suite/config/mozconfigs/mozconfig.macosx.common
@@ -0,0 +1,10 @@
+# Common statements that are applicable to macOS x64.
+
+# Same location as on Linux so no need to distingush when doing
+# a macOS cross compile on Linux.
+ac_add_options --with-google-location-service-api-keyfile=/builds/google-api.key
+ac_add_options --with-google-safebrowsing-api-keyfile=/builds/google-api.key
+
+# SeaMonkey uses the google api key for Geolocation services.
+# See Bug 1352850 - Switch to Google for Geolocation services.
+# ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
diff --git a/comm/suite/config/mozconfigs/mozconfig.win.common b/comm/suite/config/mozconfigs/mozconfig.win.common
new file mode 100644
index 0000000000..8b6c436bf8
--- /dev/null
+++ b/comm/suite/config/mozconfigs/mozconfig.win.common
@@ -0,0 +1,8 @@
+# Common statements that are applicable to Windows x86 and x64.
+
+ac_add_options --with-google-location-service-api-keyfile=/builds/google-api.key
+ac_add_options --with-google-safebrowsing-api-keyfile=/builds/google-api.key
+
+# SeaMonkey uses the google api key for Geolocation services
+# See Bug 1352850 - Switch to Google for Geolocation services
+# ac_add_options --with-mozilla-api-keyfile=c:/builds/mozilla-desktop-geoloc-api.key
diff --git a/comm/suite/config/mozconfigs/win32/debug b/comm/suite/config/mozconfigs/win32/debug
new file mode 100644
index 0000000000..c1597c29b6
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win32/debug
@@ -0,0 +1,21 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win32/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-debug
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win32/l10n-mozconfig b/comm/suite/config/mozconfigs/win32/l10n-mozconfig
new file mode 100644
index 0000000000..df16292852
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win32/l10n-mozconfig
@@ -0,0 +1,20 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win32/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win32/nightly b/comm/suite/config/mozconfigs/win32/nightly
new file mode 100644
index 0000000000..dea90ab6dc
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win32/nightly
@@ -0,0 +1,26 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win32/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.win.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-jemalloc
+ac_add_options --enable-profiling
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+. $TOOLTOOL_DIR/build/win32/mozconfig.vs-latest
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win32/release b/comm/suite/config/mozconfigs/win32/release
new file mode 100644
index 0000000000..9f2f9a5e65
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win32/release
@@ -0,0 +1,24 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win32/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.win.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-official-branding
+ac_add_options --enable-jemalloc
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win32/release-l10n b/comm/suite/config/mozconfigs/win32/release-l10n
new file mode 100644
index 0000000000..24c371fb41
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win32/release-l10n
@@ -0,0 +1,21 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win32/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win64/debug b/comm/suite/config/mozconfigs/win64/debug
new file mode 100644
index 0000000000..510502b0b3
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win64/debug
@@ -0,0 +1,28 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win64/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.win.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --target=x86_64-pc-mingw32
+ac_add_options --host=x86_64-pc-mingw32
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-debug
+ac_add_options --enable-calendar
+
+# Disable MAPI in x64 builds until Bug 393302 is resolved.
+ac_add_options --disable-mapi
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win64/l10n-mozconfig b/comm/suite/config/mozconfigs/win64/l10n-mozconfig
new file mode 100644
index 0000000000..237aa7a3b8
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win64/l10n-mozconfig
@@ -0,0 +1,26 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win64/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --target=x86_64-pc-mingw32
+ac_add_options --host=x86_64-pc-mingw32
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+
+# Disable MAPI in x64 builds until Bug 393302 is resolved.
+ac_add_options --disable-mapi
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win64/nightly b/comm/suite/config/mozconfigs/win64/nightly
new file mode 100644
index 0000000000..722560cbb9
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win64/nightly
@@ -0,0 +1,30 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win64/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.win.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --target=x86_64-pc-mingw32
+ac_add_options --host=x86_64-pc-mingw32
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-jemalloc
+ac_add_options --enable-profiling
+ac_add_options --enable-calendar
+
+# Disable MAPI in x64 builds until Bug 393302 is resolved.
+ac_add_options --disable-mapi
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win64/release b/comm/suite/config/mozconfigs/win64/release
new file mode 100644
index 0000000000..23fb727e9f
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win64/release
@@ -0,0 +1,30 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win64/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/mozconfig.win.common
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --target=x86_64-pc-mingw32
+ac_add_options --host=x86_64-pc-mingw32
+
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-official-branding
+ac_add_options --enable-jemalloc
+ac_add_options --enable-calendar
+
+# Disable MAPI in x64 builds until Bug 393302 is resolved.
+ac_add_options --disable-mapi
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Package js shell
+export MOZ_PACKAGE_JSSHELL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/mozconfigs/win64/release-l10n b/comm/suite/config/mozconfigs/win64/release-l10n
new file mode 100644
index 0000000000..dd97bf0779
--- /dev/null
+++ b/comm/suite/config/mozconfigs/win64/release-l10n
@@ -0,0 +1,27 @@
+TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+
+. $TOOLTOOL_DIR/build/mozconfig.win-common
+. $TOOLTOOL_DIR/build/mozconfig.stylo
+. $TOOLTOOL_DIR/build/mozconfig.common
+. $TOOLTOOL_DIR/build/win64/mozconfig.vs-latest
+. $TOOLTOOL_DIR/comm/suite/config/mozconfigs/common
+
+ac_add_options --target=x86_64-pc-mingw32
+ac_add_options --host=x86_64-pc-mingw32
+
+ac_add_options --with-l10n-base=../../l10n
+ac_add_options --enable-application=comm/suite
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+
+# Disable MAPI in x64 builds until Bug 393302 is resolved.
+ac_add_options --disable-mapi
+
+# Build lightning locales
+ac_add_options --enable-calendar
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Set up mapi includes (must be done after visual studio setup)
+export INCLUDE=$INCLUDE:/c/Office\ 2010\ Developer\ Resources/Outlook\ 2010\ MAPI\ Headers
+mk_export_correct_style INCLUDE
diff --git a/comm/suite/config/version.txt b/comm/suite/config/version.txt
new file mode 100644
index 0000000000..a6a8d58f10
--- /dev/null
+++ b/comm/suite/config/version.txt
@@ -0,0 +1 @@
+2.112
diff --git a/comm/suite/config/version_display.txt b/comm/suite/config/version_display.txt
new file mode 100644
index 0000000000..1d0091cc6c
--- /dev/null
+++ b/comm/suite/config/version_display.txt
@@ -0,0 +1 @@
+2.112b1
diff --git a/comm/suite/confvars.sh b/comm/suite/confvars.sh
new file mode 100755
index 0000000000..f4ef325fd7
--- /dev/null
+++ b/comm/suite/confvars.sh
@@ -0,0 +1,30 @@
+#! /bin/sh
+# 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/.
+
+MOZ_APP_VENDOR=Mozilla
+MOZ_APP_NAME=seamonkey
+MOZ_APP_DISPLAYNAME=SeaMonkey
+
+MOZ_APP_VERSION=$SEAMONKEY_VERSION
+MOZ_APP_VERSION_DISPLAY=$SEAMONKEY_VERSION_DISPLAY
+# MOZ_PKG_VERSION=$SEAMONKEY_VERSION_PACKAGE
+
+MOZ_BRANDING_DIRECTORY=comm/suite/branding/seamonkey
+MOZ_OFFICIAL_BRANDING_DIRECTORY=comm/suite/branding/seamonkey
+MOZ_UPDATER=1
+# This should usually be the same as the value MAR_CHANNEL_ID.
+# If more than one ID is needed, then you should use a comma separated list
+# of values.
+ACCEPTED_MAR_CHANNEL_IDS=seamonkey-comm-central
+# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
+MAR_CHANNEL_ID=seamonkey-comm-central
+
+MOZ_APP_ID={92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}
+MOZ_PROFILE_MIGRATOR=1
+
+# Include the DevTools client, not just the server (which is the default)
+MOZ_DEVTOOLS=all
+
+NSS_EXTRA_SYMBOLS_FILE=../comm/mailnews/nss-extra.symbols
diff --git a/comm/suite/editor/base/content/ComposerCommands.js b/comm/suite/editor/base/content/ComposerCommands.js
new file mode 100644
index 0000000000..daff0d4563
--- /dev/null
+++ b/comm/suite/editor/base/content/ComposerCommands.js
@@ -0,0 +1,4051 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/* Implementations of nsIControllerCommand for composer commands */
+
+// Linting is disabled in chunks of this file because it contains code that never
+// runs in Thunderbird, and references things that don't exist in Thunderbird.
+
+/* import-globals-from editor.js */
+/* import-globals-from editorUtilities.js */
+/* globals CreatePublishDataFromUrl editPage FormatDirForPublishing getTopWin
+ goPreferences nsIPromptService openComposeWindow openNewPrivateWith
+ PrintPreviewListener SavePublishDataToPrefs SavePassword savePWObj */
+
+var gComposerJSCommandControllerID = 0;
+
+function SetupHTMLEditorCommands() {
+ var commandTable = GetComposerCommandTable();
+ if (!commandTable) {
+ return;
+ }
+
+ // Include everything a text editor does
+ SetupTextEditorCommands();
+
+ // dump("Registering HTML editor commands\n");
+
+ commandTable.registerCommand("cmd_renderedHTMLEnabler", nsDummyHTMLCommand);
+
+ commandTable.registerCommand("cmd_grid", nsGridCommand);
+
+ commandTable.registerCommand("cmd_listProperties", nsListPropertiesCommand);
+ commandTable.registerCommand("cmd_pageProperties", nsPagePropertiesCommand);
+ commandTable.registerCommand("cmd_colorProperties", nsColorPropertiesCommand);
+ commandTable.registerCommand("cmd_increaseFontStep", nsIncreaseFontCommand);
+ commandTable.registerCommand("cmd_decreaseFontStep", nsDecreaseFontCommand);
+ commandTable.registerCommand(
+ "cmd_advancedProperties",
+ nsAdvancedPropertiesCommand
+ );
+ commandTable.registerCommand(
+ "cmd_objectProperties",
+ nsObjectPropertiesCommand
+ );
+ commandTable.registerCommand(
+ "cmd_removeNamedAnchors",
+ nsRemoveNamedAnchorsCommand
+ );
+ commandTable.registerCommand("cmd_editLink", nsEditLinkCommand);
+
+ commandTable.registerCommand("cmd_form", nsFormCommand);
+ commandTable.registerCommand("cmd_inputtag", nsInputTagCommand);
+ commandTable.registerCommand("cmd_inputimage", nsInputImageCommand);
+ commandTable.registerCommand("cmd_textarea", nsTextAreaCommand);
+ commandTable.registerCommand("cmd_select", nsSelectCommand);
+ commandTable.registerCommand("cmd_button", nsButtonCommand);
+ commandTable.registerCommand("cmd_label", nsLabelCommand);
+ commandTable.registerCommand("cmd_fieldset", nsFieldSetCommand);
+ commandTable.registerCommand("cmd_image", nsImageCommand);
+ commandTable.registerCommand("cmd_hline", nsHLineCommand);
+ commandTable.registerCommand("cmd_link", nsLinkCommand);
+ commandTable.registerCommand("cmd_anchor", nsAnchorCommand);
+ commandTable.registerCommand(
+ "cmd_insertHTMLWithDialog",
+ nsInsertHTMLWithDialogCommand
+ );
+ commandTable.registerCommand(
+ "cmd_insertMathWithDialog",
+ nsInsertMathWithDialogCommand
+ );
+ commandTable.registerCommand("cmd_insertBreak", nsInsertBreakCommand);
+ commandTable.registerCommand("cmd_insertBreakAll", nsInsertBreakAllCommand);
+
+ commandTable.registerCommand("cmd_table", nsInsertOrEditTableCommand);
+ commandTable.registerCommand("cmd_editTable", nsEditTableCommand);
+ commandTable.registerCommand("cmd_SelectTable", nsSelectTableCommand);
+ commandTable.registerCommand("cmd_SelectRow", nsSelectTableRowCommand);
+ commandTable.registerCommand("cmd_SelectColumn", nsSelectTableColumnCommand);
+ commandTable.registerCommand("cmd_SelectCell", nsSelectTableCellCommand);
+ commandTable.registerCommand(
+ "cmd_SelectAllCells",
+ nsSelectAllTableCellsCommand
+ );
+ commandTable.registerCommand("cmd_InsertTable", nsInsertTableCommand);
+ commandTable.registerCommand(
+ "cmd_InsertRowAbove",
+ nsInsertTableRowAboveCommand
+ );
+ commandTable.registerCommand(
+ "cmd_InsertRowBelow",
+ nsInsertTableRowBelowCommand
+ );
+ commandTable.registerCommand(
+ "cmd_InsertColumnBefore",
+ nsInsertTableColumnBeforeCommand
+ );
+ commandTable.registerCommand(
+ "cmd_InsertColumnAfter",
+ nsInsertTableColumnAfterCommand
+ );
+ commandTable.registerCommand(
+ "cmd_InsertCellBefore",
+ nsInsertTableCellBeforeCommand
+ );
+ commandTable.registerCommand(
+ "cmd_InsertCellAfter",
+ nsInsertTableCellAfterCommand
+ );
+ commandTable.registerCommand("cmd_DeleteTable", nsDeleteTableCommand);
+ commandTable.registerCommand("cmd_DeleteRow", nsDeleteTableRowCommand);
+ commandTable.registerCommand("cmd_DeleteColumn", nsDeleteTableColumnCommand);
+ commandTable.registerCommand("cmd_DeleteCell", nsDeleteTableCellCommand);
+ commandTable.registerCommand(
+ "cmd_DeleteCellContents",
+ nsDeleteTableCellContentsCommand
+ );
+ commandTable.registerCommand("cmd_JoinTableCells", nsJoinTableCellsCommand);
+ commandTable.registerCommand("cmd_SplitTableCell", nsSplitTableCellCommand);
+ commandTable.registerCommand(
+ "cmd_TableOrCellColor",
+ nsTableOrCellColorCommand
+ );
+ commandTable.registerCommand("cmd_NormalizeTable", nsNormalizeTableCommand);
+ commandTable.registerCommand("cmd_smiley", nsSetSmiley);
+ commandTable.registerCommand("cmd_ConvertToTable", nsConvertToTable);
+}
+
+function SetupTextEditorCommands() {
+ var commandTable = GetComposerCommandTable();
+ if (!commandTable) {
+ return;
+ }
+
+ // dump("Registering plain text editor commands\n");
+
+ commandTable.registerCommand("cmd_findReplace", nsFindReplaceCommand);
+ commandTable.registerCommand("cmd_find", nsFindCommand);
+ commandTable.registerCommand("cmd_findNext", nsFindAgainCommand);
+ commandTable.registerCommand("cmd_findPrev", nsFindAgainCommand);
+ commandTable.registerCommand("cmd_rewrap", nsRewrapCommand);
+ commandTable.registerCommand("cmd_spelling", nsSpellingCommand);
+ commandTable.registerCommand("cmd_validate", nsValidateCommand);
+ commandTable.registerCommand("cmd_insertChars", nsInsertCharsCommand);
+}
+
+function SetupComposerWindowCommands() {
+ // Don't need to do this if already done
+ if (gComposerWindowControllerID) {
+ return;
+ }
+
+ // Create a command controller and register commands
+ // specific to Web Composer window (file-related commands, HTML Source...)
+ // We can't use the composer controller created on the content window else
+ // we can't process commands when in HTMLSource editor
+ // IMPORTANT: For each of these commands, the doCommand method
+ // must first call SetEditMode(gPreviousNonSourceDisplayMode);
+ // to go from HTML Source mode to any other edit mode
+
+ var windowControllers = window.controllers;
+
+ if (!windowControllers) {
+ return;
+ }
+
+ var commandTable;
+ var composerController;
+ var editorController;
+ try {
+ composerController = Cc[
+ "@mozilla.org/embedcomp/base-command-controller;1"
+ ].createInstance();
+
+ editorController = composerController.QueryInterface(
+ Ci.nsIControllerContext
+ );
+
+ // Get the nsIControllerCommandTable interface we need to register commands
+ var interfaceRequestor = composerController.QueryInterface(
+ Ci.nsIInterfaceRequestor
+ );
+ commandTable = interfaceRequestor.getInterface(
+ Ci.nsIControllerCommandTable
+ );
+ } catch (e) {
+ dump("Failed to create composerController\n");
+ return;
+ }
+
+ if (!commandTable) {
+ dump("Failed to get interface for nsIControllerCommandManager\n");
+ return;
+ }
+
+ // File-related commands
+ commandTable.registerCommand("cmd_open", nsOpenCommand);
+ commandTable.registerCommand("cmd_save", nsSaveCommand);
+ commandTable.registerCommand("cmd_saveAs", nsSaveAsCommand);
+ commandTable.registerCommand("cmd_exportToText", nsExportToTextCommand);
+ commandTable.registerCommand(
+ "cmd_saveAndChangeEncoding",
+ nsSaveAndChangeEncodingCommand
+ );
+ commandTable.registerCommand("cmd_publish", nsPublishCommand);
+ commandTable.registerCommand("cmd_publishAs", nsPublishAsCommand);
+ commandTable.registerCommand("cmd_publishSettings", nsPublishSettingsCommand);
+ commandTable.registerCommand("cmd_revert", nsRevertCommand);
+ commandTable.registerCommand("cmd_openRemote", nsOpenRemoteCommand);
+ commandTable.registerCommand("cmd_preview", nsPreviewCommand);
+ commandTable.registerCommand("cmd_editSendPage", nsSendPageCommand);
+ commandTable.registerCommand("cmd_print", nsPrintCommand);
+ commandTable.registerCommand("cmd_printpreview", nsPrintPreviewCommand);
+ commandTable.registerCommand("cmd_printSetup", nsPrintSetupCommand);
+ commandTable.registerCommand("cmd_close", nsCloseCommand);
+ commandTable.registerCommand("cmd_preferences", nsPreferencesCommand);
+
+ // Edit Mode commands
+ if (GetCurrentEditorType() == "html") {
+ commandTable.registerCommand("cmd_NormalMode", nsNormalModeCommand);
+ commandTable.registerCommand("cmd_AllTagsMode", nsAllTagsModeCommand);
+ commandTable.registerCommand("cmd_HTMLSourceMode", nsHTMLSourceModeCommand);
+ commandTable.registerCommand("cmd_PreviewMode", nsPreviewModeCommand);
+ commandTable.registerCommand("cmd_FinishHTMLSource", nsFinishHTMLSource);
+ commandTable.registerCommand("cmd_CancelHTMLSource", nsCancelHTMLSource);
+ commandTable.registerCommand(
+ "cmd_updateStructToolbar",
+ nsUpdateStructToolbarCommand
+ );
+ }
+
+ windowControllers.insertControllerAt(0, editorController);
+
+ // Store the controller ID so we can be sure to get the right one later
+ gComposerWindowControllerID = windowControllers.getControllerId(
+ editorController
+ );
+}
+
+function GetComposerCommandTable() {
+ var controller;
+ if (gComposerJSCommandControllerID) {
+ try {
+ controller = window.content.controllers.getControllerById(
+ gComposerJSCommandControllerID
+ );
+ } catch (e) {}
+ }
+ if (!controller) {
+ // create it
+ controller = Cc[
+ "@mozilla.org/embedcomp/base-command-controller;1"
+ ].createInstance();
+
+ var editorController = controller.QueryInterface(Ci.nsIControllerContext);
+ editorController.setCommandContext(GetCurrentEditorElement());
+ window.content.controllers.insertControllerAt(0, controller);
+
+ // Store the controller ID so we can be sure to get the right one later
+ gComposerJSCommandControllerID = window.content.controllers.getControllerId(
+ controller
+ );
+ }
+
+ if (controller) {
+ var interfaceRequestor = controller.QueryInterface(
+ Ci.nsIInterfaceRequestor
+ );
+ return interfaceRequestor.getInterface(Ci.nsIControllerCommandTable);
+ }
+ return null;
+}
+
+/* eslint-disable complexity */
+function goUpdateCommandState(command) {
+ try {
+ var controller = top.document.commandDispatcher.getControllerForCommand(
+ command
+ );
+ if (!(controller instanceof Ci.nsICommandController)) {
+ return;
+ }
+
+ var params = newCommandParams();
+ if (!params) {
+ return;
+ }
+
+ controller.getCommandStateWithParams(command, params);
+
+ switch (command) {
+ case "cmd_bold":
+ case "cmd_italic":
+ case "cmd_underline":
+ case "cmd_var":
+ case "cmd_samp":
+ case "cmd_code":
+ case "cmd_acronym":
+ case "cmd_abbr":
+ case "cmd_cite":
+ case "cmd_strong":
+ case "cmd_em":
+ case "cmd_superscript":
+ case "cmd_subscript":
+ case "cmd_strikethrough":
+ case "cmd_tt":
+ case "cmd_nobreak":
+ case "cmd_ul":
+ case "cmd_ol":
+ pokeStyleUI(command, params.getBooleanValue("state_all"));
+ break;
+
+ case "cmd_paragraphState":
+ case "cmd_align":
+ case "cmd_highlight":
+ case "cmd_backgroundColor":
+ case "cmd_fontColor":
+ case "cmd_fontFace":
+ case "cmd_fontSize":
+ case "cmd_absPos":
+ pokeMultiStateUI(command, params);
+ break;
+
+ case "cmd_decreaseZIndex":
+ case "cmd_increaseZIndex":
+ case "cmd_indent":
+ case "cmd_outdent":
+ case "cmd_increaseFont":
+ case "cmd_decreaseFont":
+ case "cmd_increaseFontStep":
+ case "cmd_decreaseFontStep":
+ case "cmd_removeStyles":
+ case "cmd_smiley":
+ break;
+
+ default:
+ dump("no update for command: " + command + "\n");
+ }
+ } catch (e) {
+ dump(
+ "An error occurred updating the " + command + " command: \n" + e + "\n"
+ );
+ }
+}
+/* eslint-enable complexity */
+
+function goUpdateComposerMenuItems(commandset) {
+ // dump("Updating commands for " + commandset.id + "\n");
+
+ for (var i = 0; i < commandset.childNodes.length; i++) {
+ var commandNode = commandset.childNodes[i];
+ var commandID = commandNode.id;
+ if (commandID) {
+ goUpdateCommand(commandID); // enable or disable
+ if (commandNode.hasAttribute("state")) {
+ goUpdateCommandState(commandID);
+ }
+ }
+ }
+}
+
+function goDoCommandParams(command, params) {
+ try {
+ var controller = top.document.commandDispatcher.getControllerForCommand(
+ command
+ );
+ if (controller && controller.isCommandEnabled(command)) {
+ if (controller instanceof Ci.nsICommandController) {
+ controller.doCommandWithParams(command, params);
+
+ // the following two lines should be removed when we implement observers
+ if (params) {
+ controller.getCommandStateWithParams(command, params);
+ }
+ } else {
+ controller.doCommand(command);
+ }
+ ResetStructToolbar();
+ }
+ } catch (e) {
+ dump("An error occurred executing the " + command + " command\n");
+ }
+}
+
+function pokeStyleUI(uiID, aDesiredState) {
+ try {
+ var commandNode = top.document.getElementById(uiID);
+ if (!commandNode) {
+ return;
+ }
+
+ var uiState = "true" == commandNode.getAttribute("state");
+ if (aDesiredState != uiState) {
+ commandNode.setAttribute("state", aDesiredState ? "true" : "false");
+ }
+ } catch (e) {
+ dump("poking UI for " + uiID + " failed: " + e + "\n");
+ }
+}
+
+function doStyleUICommand(cmdStr) {
+ try {
+ var cmdParams = newCommandParams();
+ goDoCommandParams(cmdStr, cmdParams);
+ if (cmdParams) {
+ pokeStyleUI(cmdStr, cmdParams.getBooleanValue("state_all"));
+ }
+
+ ResetStructToolbar();
+ } catch (e) {}
+}
+
+// Copied from jsmime.js.
+function stringToTypedArray(buffer) {
+ var typedarray = new Uint8Array(buffer.length);
+ for (var i = 0; i < buffer.length; i++) {
+ typedarray[i] = buffer.charCodeAt(i);
+ }
+ return typedarray;
+}
+
+function pokeMultiStateUI(uiID, cmdParams) {
+ try {
+ var commandNode = document.getElementById(uiID);
+ if (!commandNode) {
+ return;
+ }
+
+ var isMixed = cmdParams.getBooleanValue("state_mixed");
+ var desiredAttrib;
+ if (isMixed) {
+ desiredAttrib = "mixed";
+ } else {
+ var valuetype = cmdParams.getValueType("state_attribute");
+ if (valuetype == Ci.nsICommandParams.eStringType) {
+ desiredAttrib = cmdParams.getCStringValue("state_attribute");
+ // Decode UTF-8, for example for font names in Japanese.
+ desiredAttrib = new TextDecoder("UTF-8").decode(
+ stringToTypedArray(desiredAttrib)
+ );
+ } else {
+ desiredAttrib = cmdParams.getStringValue("state_attribute");
+ }
+ }
+
+ var uiState = commandNode.getAttribute("state");
+ if (desiredAttrib != uiState) {
+ commandNode.setAttribute("state", desiredAttrib);
+ }
+ } catch (e) {}
+}
+
+function doStatefulCommand(commandID, newState) {
+ var commandNode = document.getElementById(commandID);
+ if (commandNode) {
+ commandNode.setAttribute("state", newState);
+ }
+ gContentWindow.focus(); // needed for command dispatch to work
+
+ try {
+ var cmdParams = newCommandParams();
+ if (!cmdParams) {
+ return;
+ }
+
+ cmdParams.setStringValue("state_attribute", newState);
+ goDoCommandParams(commandID, cmdParams);
+
+ pokeMultiStateUI(commandID, cmdParams);
+
+ ResetStructToolbar();
+ } catch (e) {
+ dump("error thrown in doStatefulCommand: " + e + "\n");
+ }
+}
+
+function PrintObject(obj) {
+ dump("-----" + obj + "------\n");
+ var names = "";
+ for (var i in obj) {
+ if (i == "value") {
+ names += i + ": " + obj.value + "\n";
+ } else if (i == "id") {
+ names += i + ": " + obj.id + "\n";
+ } else {
+ names += i + "\n";
+ }
+ }
+
+ dump(names + "-----------\n");
+}
+
+function PrintNodeID(id) {
+ PrintObject(document.getElementById(id));
+}
+
+var nsDummyHTMLCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // do nothing
+ dump("Hey, who's calling the dummy command?\n");
+ },
+};
+
+var nsOpenCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ // We can always do this.
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var fileType = IsHTMLEditor() ? "html" : "text";
+ var title = GetString(IsHTMLEditor() ? "OpenHTMLFile" : "OpenTextFile");
+
+ var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ fp.init(window, title, nsIFilePicker.modeOpen);
+
+ SetFilePickerDirectory(fp, fileType);
+
+ // Direct user to prefer HTML files and/or text files depending on whether
+ // loading into Composer or Text editor, so we call separately to control
+ // the order of the filter list.
+ if (fileType == "html") {
+ fp.appendFilters(nsIFilePicker.filterHTML);
+ }
+ fp.appendFilters(nsIFilePicker.filterText);
+ fp.appendFilters(nsIFilePicker.filterAll);
+
+ fp.open(rv => {
+ if (rv == nsIFilePicker.returnCancel) {
+ return;
+ }
+ // editPage checks for already open window and activates it.
+ if (fp.fileURL.spec) {
+ SaveFilePickerDirectory(fp, fileType);
+ editPage(fp.fileURL.spec, fileType);
+ }
+ });
+ },
+};
+
+// STRUCTURE TOOLBAR
+//
+var nsUpdateStructToolbarCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ UpdateStructToolbar();
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+ doCommand(aCommand) {},
+};
+
+// ******* File output commands and utilities ******** //
+var nsSaveCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ // Always allow saving when editing a remote document,
+ // otherwise the document modified state would prevent that
+ // when you first open a remote file.
+ try {
+ var docUrl = GetDocumentUrl();
+ return (
+ IsDocumentEditable() &&
+ (IsDocumentModified() ||
+ IsHTMLSourceChanged() ||
+ IsUrlAboutBlank(docUrl) ||
+ GetScheme(docUrl) != "file")
+ );
+ } catch (e) {
+ return false;
+ }
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var editor = GetCurrentEditor();
+ if (editor) {
+ if (IsHTMLEditor()) {
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ }
+ SaveDocument(
+ IsUrlAboutBlank(GetDocumentUrl()),
+ false,
+ editor.contentsMIMEType
+ );
+ }
+ },
+};
+
+var nsSaveAsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var editor = GetCurrentEditor();
+ if (editor) {
+ if (IsHTMLEditor()) {
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ }
+ SaveDocument(true, false, editor.contentsMIMEType);
+ }
+ },
+};
+
+var nsExportToTextCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ if (GetCurrentEditor()) {
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ SaveDocument(true, true, "text/plain");
+ }
+ },
+};
+
+var nsSaveAndChangeEncodingCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ window.ok = false;
+ window.exportToText = false;
+ var oldTitle = GetDocumentTitle();
+ window.openDialog(
+ "chrome://editor/content/EditorSaveAsCharset.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable=yes"
+ );
+
+ if (GetDocumentTitle() != oldTitle) {
+ UpdateWindowTitle();
+ }
+
+ if (window.ok) {
+ if (window.exportToText) {
+ SaveDocument(true, true, "text/plain");
+ } else {
+ var editor = GetCurrentEditor();
+ SaveDocument(true, false, editor ? editor.contentsMIMEType : null);
+ }
+ }
+ },
+};
+
+var nsPublishCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ if (IsDocumentEditable()) {
+ // Always allow publishing when editing a local document,
+ // otherwise the document modified state would prevent that
+ // when you first open any local file.
+ try {
+ var docUrl = GetDocumentUrl();
+ return (
+ IsDocumentModified() ||
+ IsHTMLSourceChanged() ||
+ IsUrlAboutBlank(docUrl) ||
+ GetScheme(docUrl) == "file"
+ );
+ } catch (e) {
+ return false;
+ }
+ }
+ return false;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ if (GetCurrentEditor()) {
+ let docUrl = GetDocumentUrl();
+ let filename = GetFilename(docUrl);
+ let publishData;
+
+ // First check pref to always show publish dialog
+ let showPublishDialog = Services.prefs.getBoolPref(
+ "editor.always_show_publish_dialog"
+ );
+
+ if (!showPublishDialog && filename) {
+ // Try to get publish data from the document url
+ publishData = CreatePublishDataFromUrl(docUrl);
+
+ // If none, use default publishing site? Need a pref for this
+ // if (!publishData)
+ // publishData = GetPublishDataFromSiteName(GetDefaultPublishSiteName(), filename);
+ }
+
+ if (showPublishDialog || !publishData) {
+ // Show the publish dialog
+ publishData = {};
+ window.ok = false;
+ let oldTitle = GetDocumentTitle();
+ window.openDialog(
+ "chrome://editor/content/EditorPublish.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ "",
+ publishData
+ );
+ if (GetDocumentTitle() != oldTitle) {
+ UpdateWindowTitle();
+ }
+
+ if (!window.ok) {
+ return false;
+ }
+ }
+ if (publishData) {
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ return Publish(publishData);
+ }
+ }
+ return false;
+ },
+};
+
+var nsPublishAsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ if (GetCurrentEditor()) {
+ SetEditMode(gPreviousNonSourceDisplayMode);
+
+ window.ok = false;
+ var publishData = {};
+ var oldTitle = GetDocumentTitle();
+ window.openDialog(
+ "chrome://editor/content/EditorPublish.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ "",
+ publishData
+ );
+ if (GetDocumentTitle() != oldTitle) {
+ UpdateWindowTitle();
+ }
+
+ if (window.ok) {
+ return Publish(publishData);
+ }
+ }
+ return false;
+ },
+};
+
+// ------- output utilities ----- //
+
+// returns a fileExtension string
+function GetExtensionBasedOnMimeType(aMIMEType) {
+ try {
+ var mimeService = null;
+ mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+
+ var fileExtension = mimeService.getPrimaryExtension(aMIMEType, null);
+
+ // the MIME service likes to give back ".htm" for text/html files,
+ // so do a special-case fix here.
+ if (fileExtension == "htm") {
+ fileExtension = "html";
+ }
+
+ return fileExtension;
+ } catch (e) {}
+ return "";
+}
+
+function GetSuggestedFileName(aDocumentURLString, aMIMEType) {
+ var extension = GetExtensionBasedOnMimeType(aMIMEType);
+ if (extension) {
+ extension = "." + extension;
+ }
+
+ // check for existing file name we can use
+ if (aDocumentURLString && !IsUrlAboutBlank(aDocumentURLString)) {
+ try {
+ let docURI = Services.io.newURI(
+ aDocumentURLString,
+ GetCurrentEditor().documentCharacterSet
+ );
+ docURI = docURI.QueryInterface(Ci.nsIURL);
+
+ // grab the file name
+ let url = validateFileName(decodeURIComponent(docURI.fileBaseName));
+ if (url) {
+ return url + extension;
+ }
+ } catch (e) {}
+ }
+
+ // Check if there is a title we can use to generate a valid filename,
+ // if we can't, use the default filename.
+ var title =
+ validateFileName(GetDocumentTitle()) ||
+ GetString("untitledDefaultFilename");
+ return title + extension;
+}
+
+/**
+ * @return {Promise} dialogResult
+ */
+function PromptForSaveLocation(
+ aDoSaveAsText,
+ aEditorType,
+ aMIMEType,
+ aDocumentURLString
+) {
+ var dialogResult = {};
+ dialogResult.filepickerClick = nsIFilePicker.returnCancel;
+ dialogResult.resultingURI = "";
+ dialogResult.resultingLocalFile = null;
+
+ var fp = null;
+ try {
+ fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ } catch (e) {}
+ if (!fp) {
+ return dialogResult;
+ }
+
+ // determine prompt string based on type of saving we'll do
+ var promptString;
+ if (aDoSaveAsText || aEditorType == "text") {
+ promptString = GetString("SaveTextAs");
+ } else {
+ promptString = GetString("SaveDocumentAs");
+ }
+
+ fp.init(window, promptString, nsIFilePicker.modeSave);
+
+ // Set filters according to the type of output
+ if (aDoSaveAsText) {
+ fp.appendFilters(nsIFilePicker.filterText);
+ } else {
+ fp.appendFilters(nsIFilePicker.filterHTML);
+ }
+ fp.appendFilters(nsIFilePicker.filterAll);
+
+ // now let's actually set the filepicker's suggested filename
+ var suggestedFileName = GetSuggestedFileName(aDocumentURLString, aMIMEType);
+ if (suggestedFileName) {
+ fp.defaultString = suggestedFileName;
+ }
+
+ // set the file picker's current directory
+ // assuming we have information needed (like prior saved location)
+ try {
+ var fileHandler = GetFileProtocolHandler();
+
+ var isLocalFile = true;
+ try {
+ let docURI = Services.io.newURI(
+ aDocumentURLString,
+ GetCurrentEditor().documentCharacterSet
+ );
+ isLocalFile = docURI.schemeIs("file");
+ } catch (e) {}
+
+ var parentLocation = null;
+ if (isLocalFile) {
+ var fileLocation = fileHandler.getFileFromURLSpec(aDocumentURLString); // this asserts if url is not local
+ parentLocation = fileLocation.parent;
+ }
+ if (parentLocation) {
+ // Save current filepicker's default location
+ if ("gFilePickerDirectory" in window) {
+ gFilePickerDirectory = fp.displayDirectory;
+ }
+
+ fp.displayDirectory = parentLocation;
+ } else {
+ // Initialize to the last-used directory for the particular type (saved in prefs)
+ SetFilePickerDirectory(fp, aEditorType);
+ }
+ } catch (e) {}
+
+ return new Promise(resolve => {
+ fp.open(rv => {
+ dialogResult.filepickerClick = rv;
+ if (rv != nsIFilePicker.returnCancel && fp.file) {
+ // Allow OK and replace.
+ // reset urlstring to new save location
+ dialogResult.resultingURIString = fileHandler.getURLSpecFromFile(
+ fp.file
+ );
+ dialogResult.resultingLocalFile = fp.file;
+ SaveFilePickerDirectory(fp, aEditorType);
+ resolve(dialogResult);
+ } else if ("gFilePickerDirectory" in window && gFilePickerDirectory) {
+ fp.displayDirectory = gFilePickerDirectory;
+ resolve(null);
+ }
+ });
+ });
+}
+
+/**
+ * If needed, prompt for document title and set the document title to the
+ * preferred value.
+ * @return true if the title was set up successfully;
+ * false if the user cancelled the title prompt
+ */
+function PromptAndSetTitleIfNone() {
+ if (GetDocumentTitle()) {
+ // we have a title; no need to prompt!
+ return true;
+ }
+
+ let result = { value: null };
+ let captionStr = GetString("DocumentTitle");
+ let msgStr = GetString("NeedDocTitle") + "\n" + GetString("DocTitleHelp");
+ let confirmed = Services.prompt.prompt(
+ window,
+ captionStr,
+ msgStr,
+ result,
+ null,
+ { value: 0 }
+ );
+ if (confirmed) {
+ SetDocumentTitle(TrimString(result.value));
+ }
+
+ return confirmed;
+}
+
+var gPersistObj;
+
+// Don't forget to do these things after calling OutputFileWithPersistAPI:
+// we need to update the uri before notifying listeners
+// if (doUpdateURI)
+// SetDocumentURI(docURI);
+// UpdateWindowTitle();
+// if (!aSaveCopy)
+// editor.resetModificationCount();
+// this should cause notification to listeners that document has changed
+
+const webPersist = Ci.nsIWebBrowserPersist;
+function OutputFileWithPersistAPI(
+ editorDoc,
+ aDestinationLocation,
+ aRelatedFilesParentDir,
+ aMimeType
+) {
+ gPersistObj = null;
+ var editor = GetCurrentEditor();
+ try {
+ editor.forceCompositionEnd();
+ } catch (e) {}
+
+ var isLocalFile = false;
+ try {
+ aDestinationLocation.QueryInterface(Ci.nsIFile);
+ isLocalFile = true;
+ } catch (e) {
+ try {
+ var tmp = aDestinationLocation.QueryInterface(Ci.nsIURI);
+ isLocalFile = tmp.schemeIs("file");
+ } catch (e) {}
+ }
+
+ try {
+ // we should supply a parent directory if/when we turn on functionality to save related documents
+ var persistObj = Cc[
+ "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"
+ ].createInstance(webPersist);
+ persistObj.progressListener = gEditorOutputProgressListener;
+
+ var wrapColumn = GetWrapColumn();
+ var outputFlags = GetOutputFlags(aMimeType, wrapColumn);
+
+ // for 4.x parity as well as improving readability of file locally on server
+ // this will always send crlf for upload (http/ftp)
+ if (!isLocalFile) {
+ // if we aren't saving locally then send both cr and lf
+ outputFlags |=
+ webPersist.ENCODE_FLAGS_CR_LINEBREAKS |
+ webPersist.ENCODE_FLAGS_LF_LINEBREAKS;
+
+ // we want to serialize the output for all remote publishing
+ // some servers can handle only one connection at a time
+ // some day perhaps we can make this user-configurable per site?
+ persistObj.persistFlags =
+ persistObj.persistFlags | webPersist.PERSIST_FLAGS_SERIALIZE_OUTPUT;
+ }
+
+ // note: we always want to set the replace existing files flag since we have
+ // already given user the chance to not replace an existing file (file picker)
+ // or the user picked an option where the file is implicitly being replaced (save)
+ persistObj.persistFlags =
+ persistObj.persistFlags |
+ webPersist.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS |
+ webPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ webPersist.PERSIST_FLAGS_DONT_FIXUP_LINKS |
+ webPersist.PERSIST_FLAGS_DONT_CHANGE_FILENAMES |
+ webPersist.PERSIST_FLAGS_FIXUP_ORIGINAL_DOM;
+ persistObj.saveDocument(
+ editorDoc,
+ aDestinationLocation,
+ aRelatedFilesParentDir,
+ aMimeType,
+ outputFlags,
+ wrapColumn
+ );
+ gPersistObj = persistObj;
+ } catch (e) {
+ dump("caught an error, bail\n");
+ return false;
+ }
+
+ return true;
+}
+
+// returns output flags based on mimetype, wrapCol and prefs
+function GetOutputFlags(aMimeType, aWrapColumn) {
+ var outputFlags = 0;
+ var editor = GetCurrentEditor();
+ var outputEntity =
+ editor && editor.documentCharacterSet == "ISO-8859-1"
+ ? webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES
+ : webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
+ if (aMimeType == "text/plain") {
+ // When saving in "text/plain" format, always do formatting
+ outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
+ } else {
+ // Should we prettyprint? Check the pref
+ if (Services.prefs.getBoolPref("editor.prettyprint")) {
+ outputFlags |= webPersist.ENCODE_FLAGS_FORMATTED;
+ }
+
+ try {
+ // How much entity names should we output? Check the pref
+ switch (Services.prefs.getCharPref("editor.encode_entity")) {
+ case "basic":
+ outputEntity = webPersist.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
+ break;
+ case "latin1":
+ outputEntity = webPersist.ENCODE_FLAGS_ENCODE_LATIN1_ENTITIES;
+ break;
+ case "html":
+ outputEntity = webPersist.ENCODE_FLAGS_ENCODE_HTML_ENTITIES;
+ break;
+ case "none":
+ outputEntity = 0;
+ break;
+ }
+ } catch (e) {}
+ }
+ outputFlags |= outputEntity;
+
+ if (aWrapColumn > 0) {
+ outputFlags |= webPersist.ENCODE_FLAGS_WRAP;
+ }
+
+ return outputFlags;
+}
+
+// returns number of column where to wrap
+const nsIWebBrowserPersist = Ci.nsIWebBrowserPersist;
+function GetWrapColumn() {
+ try {
+ return GetCurrentEditor().wrapWidth;
+ } catch (e) {}
+ return 0;
+}
+
+const gShowDebugOutputStateChange = false;
+const gShowDebugOutputProgress = false;
+const gShowDebugOutputStatusChange = false;
+
+const gShowDebugOutputLocationChange = false;
+const gShowDebugOutputSecurityChange = false;
+
+const nsIWebProgressListener = Ci.nsIWebProgressListener;
+const nsIChannel = Ci.nsIChannel;
+
+const kErrorBindingAborted = 2152398850;
+const kErrorBindingRedirected = 2152398851;
+const kFileNotFound = 2152857618;
+
+var gEditorOutputProgressListener = {
+ /* eslint-disable complexity */
+ onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+ var editor = GetCurrentEditor();
+
+ // Use this to access onStateChange flags
+ var requestSpec;
+ try {
+ var channel = aRequest.QueryInterface(nsIChannel);
+ requestSpec = StripUsernamePasswordFromURI(channel.URI);
+ } catch (e) {
+ if (gShowDebugOutputStateChange) {
+ dump("***** onStateChange; NO REQUEST CHANNEL\n");
+ }
+ }
+
+ var pubSpec;
+ if (gPublishData) {
+ pubSpec =
+ gPublishData.publishUrl + gPublishData.docDir + gPublishData.filename;
+ }
+
+ if (gShowDebugOutputStateChange) {
+ dump("\n***** onStateChange request: " + requestSpec + "\n");
+ dump(" state flags: ");
+
+ if (aStateFlags & nsIWebProgressListener.STATE_START) {
+ dump(" STATE_START, ");
+ }
+ if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
+ dump(" STATE_STOP, ");
+ }
+ if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+ dump(" STATE_IS_NETWORK ");
+ }
+
+ dump(
+ `\n * requestSpec=${requestSpec}, pubSpec=${pubSpec}, aStatus=${aStatus}\n`
+ );
+
+ DumpDebugStatus(aStatus);
+ }
+ // The rest only concerns publishing, so bail out if no dialog
+ if (!gProgressDialog) {
+ return;
+ }
+
+ // Detect start of file upload of any file:
+ // (We ignore any START messages after gPersistObj says publishing is finished
+ if (
+ aStateFlags & nsIWebProgressListener.STATE_START &&
+ gPersistObj &&
+ requestSpec &&
+ gPersistObj.currentState != gPersistObj.PERSIST_STATE_FINISHED
+ ) {
+ document
+ .getElementById("navigator-throbber")
+ .setAttribute("busy", "true");
+ try {
+ // Add url to progress dialog's list showing each file uploading
+ gProgressDialog.SetProgressStatus(GetFilename(requestSpec), "busy");
+ } catch (e) {}
+ }
+
+ // Detect end of file upload of any file:
+ if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
+ document.getElementById("navigator-throbber").removeAttribute("busy");
+ // ignore aStatus == kErrorBindingAborted; check http response for possible errors
+ try {
+ // check http channel for response: 200 range is ok; other ranges are not
+ var httpChannel = aRequest.QueryInterface(Ci.nsIHttpChannel);
+ var httpResponse = httpChannel.responseStatus;
+ if (httpResponse < 200 || httpResponse >= 300) {
+ // Not a real error but enough to pass check below.
+ aStatus = httpResponse;
+ } else if (aStatus == kErrorBindingAborted) {
+ aStatus = 0;
+ }
+
+ if (gShowDebugOutputStateChange) {
+ dump("http response is: " + httpResponse + "\n");
+ }
+ } catch (e) {
+ if (aStatus == kErrorBindingAborted) {
+ aStatus = 0;
+ }
+ }
+
+ // We abort publishing for all errors except if image src file is not found
+ var abortPublishing = aStatus != 0 && aStatus != kFileNotFound;
+
+ // Notify progress dialog when we receive the STOP
+ // notification for a file if there was an error
+ // or a successful finish
+ // (Check requestSpec to be sure message is for destination url)
+ if (
+ aStatus != 0 ||
+ (requestSpec &&
+ requestSpec.startsWith(GetScheme(gPublishData.publishUrl)))
+ ) {
+ try {
+ gProgressDialog.SetProgressFinished(
+ GetFilename(requestSpec),
+ aStatus
+ );
+ } catch (e) {}
+ }
+
+ if (abortPublishing) {
+ // Cancel publishing
+ gPersistObj.cancelSave();
+
+ // Don't do any commands after failure
+ gCommandAfterPublishing = null;
+
+ // Restore original document to undo image src url adjustments
+ if (gRestoreDocumentSource) {
+ try {
+ editor.rebuildDocumentFromSource(gRestoreDocumentSource);
+
+ // Clear transaction cache since we just did a potentially
+ // very large insert and this will eat up memory
+ editor.clearUndoRedo();
+ } catch (e) {}
+ }
+
+ // Notify progress dialog that we're finished
+ // and keep open to show error
+ gProgressDialog.SetProgressFinished(null, 0);
+
+ // We don't want to change location or reset mod count, etc.
+ return;
+ }
+
+ // XXX HACK: "file://" protocol is not supported in network code
+ // (bug 151867 filed to add this support, bug 151869 filed
+ // to remove this and other code in nsIWebBrowserPersist)
+ // nsIWebBrowserPersist *does* copy the file(s), but we don't
+ // get normal onStateChange messages.
+
+ // Case 1: If images are included, we get fairly normal
+ // STATE_START/STATE_STOP & STATE_IS_NETWORK messages associated with the image files,
+ // thus we must finish HTML file progress below
+
+ // Case 2: If just HTML file is uploaded, we get STATE_START and STATE_STOP
+ // notification with a null "requestSpec", and
+ // the gPersistObj is destroyed before we get here!
+ // So create an new object so we can flow through normal processing below
+ if (
+ !requestSpec &&
+ GetScheme(gPublishData.publishUrl) == "file" &&
+ (!gPersistObj ||
+ gPersistObj.currentState ==
+ nsIWebBrowserPersist.PERSIST_STATE_FINISHED)
+ ) {
+ aStateFlags |= nsIWebProgressListener.STATE_IS_NETWORK;
+ if (!gPersistObj) {
+ gPersistObj = {
+ result: aStatus,
+ currentState: nsIWebBrowserPersist.PERSIST_STATE_FINISHED,
+ };
+ }
+ }
+
+ // STATE_IS_NETWORK signals end of publishing, as does the gPersistObj.currentState
+ if (
+ aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
+ gPersistObj.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED
+ ) {
+ if (GetScheme(gPublishData.publishUrl) == "file") {
+ // XXX "file://" hack: We don't get notified about the HTML file, so end progress for it
+ // (This covers both "Case 1 and 2" described above)
+ gProgressDialog.SetProgressFinished(
+ gPublishData.filename,
+ gPersistObj.result
+ );
+ }
+
+ if (gPersistObj.result == 0) {
+ // All files are finished and publishing succeeded (some images may have failed)
+ try {
+ // Make a new docURI from the "browse location" in case "publish location" was FTP
+ // We need to set document uri before notifying listeners
+ var docUrl = GetDocUrlFromPublishData(gPublishData);
+ SetDocumentURI(
+ Services.io.newURI(docUrl, editor.documentCharacterSet)
+ );
+
+ UpdateWindowTitle();
+
+ // this should cause notification to listeners that doc has changed
+ editor.resetModificationCount();
+
+ // Set UI based on whether we're editing a remote or local url
+ // Why is urlstring undefined?
+ /* eslint-disable-next-line no-undef */
+ SetSaveAndPublishUI(urlstring);
+ } catch (e) {}
+
+ // Save publishData to prefs
+ if (gPublishData) {
+ if (gPublishData.savePublishData) {
+ // We published successfully, so we can safely
+ // save docDir and otherDir to prefs
+ gPublishData.saveDirs = true;
+ SavePublishDataToPrefs(gPublishData);
+ } else {
+ SavePassword(gPublishData);
+ }
+ }
+
+ // Ask progress dialog to close, but it may not
+ // if user checked checkbox to keep it open
+ gProgressDialog.RequestCloseDialog();
+ } else {
+ // We previously aborted publishing because of error:
+ // Calling gPersistObj.cancelSave() resulted in a non-zero gPersistObj.result,
+ // so notify progress dialog we're finished
+ gProgressDialog.SetProgressFinished(null, 0);
+ }
+ }
+ }
+ },
+ /* eslint-enable complexity */
+
+ onProgressChange(
+ aWebProgress,
+ aRequest,
+ aCurSelfProgress,
+ aMaxSelfProgress,
+ aCurTotalProgress,
+ aMaxTotalProgress
+ ) {
+ if (!gPersistObj) {
+ return;
+ }
+
+ if (gShowDebugOutputProgress) {
+ dump(
+ "\n onProgressChange: gPersistObj.result=" + gPersistObj.result + "\n"
+ );
+ try {
+ var channel = aRequest.QueryInterface(nsIChannel);
+ dump("***** onProgressChange request: " + channel.URI.spec + "\n");
+ } catch (e) {}
+ dump(
+ "***** self: " +
+ aCurSelfProgress +
+ " / " +
+ aMaxSelfProgress +
+ "\n"
+ );
+ dump(
+ "***** total: " +
+ aCurTotalProgress +
+ " / " +
+ aMaxTotalProgress +
+ "\n\n"
+ );
+
+ if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY) {
+ dump(" Persister is ready to save data\n\n");
+ } else if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING) {
+ dump(" Persister is saving data.\n\n");
+ } else if (
+ gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED
+ ) {
+ dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
+ }
+ }
+ },
+
+ onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+ if (gShowDebugOutputLocationChange) {
+ dump("***** onLocationChange: " + aLocation.spec + "\n");
+ try {
+ var channel = aRequest.QueryInterface(nsIChannel);
+ dump("***** request: " + channel.URI.spec + "\n");
+ } catch (e) {}
+ }
+ },
+
+ onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
+ if (gShowDebugOutputStatusChange) {
+ dump("***** onStatusChange: " + aMessage + "\n");
+ try {
+ var channel = aRequest.QueryInterface(nsIChannel);
+ dump("***** request: " + channel.URI.spec + "\n");
+ } catch (e) {
+ dump(" couldn't get request\n");
+ }
+
+ DumpDebugStatus(aStatus);
+
+ if (gPersistObj) {
+ if (gPersistObj.currentState == gPersistObj.PERSIST_STATE_READY) {
+ dump(" Persister is ready to save data\n\n");
+ } else if (
+ gPersistObj.currentState == gPersistObj.PERSIST_STATE_SAVING
+ ) {
+ dump(" Persister is saving data.\n\n");
+ } else if (
+ gPersistObj.currentState == gPersistObj.PERSIST_STATE_FINISHED
+ ) {
+ dump(" PERSISTER HAS FINISHED SAVING DATA\n\n\n");
+ }
+ }
+ }
+ },
+
+ onSecurityChange(aWebProgress, aRequest, state) {
+ if (gShowDebugOutputSecurityChange) {
+ try {
+ var channel = aRequest.QueryInterface(nsIChannel);
+ dump("***** onSecurityChange request: " + channel.URI.spec + "\n");
+ } catch (e) {}
+ }
+ },
+
+ onContentBlockingEvent(aWebProgress, aRequest, aEvent) {},
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ "nsIPrompt",
+ "nsIAuthPrompt",
+ ]),
+
+ // nsIPrompt
+ alert(dlgTitle, text) {
+ Services.prompt.alert(
+ gProgressDialog ? gProgressDialog : window,
+ dlgTitle,
+ text
+ );
+ },
+ alertCheck(dialogTitle, text, checkBoxLabel, checkObj) {
+ Services.prompt.alert(window, dialogTitle, text);
+ },
+ confirm(dlgTitle, text) {
+ return ConfirmWithTitle(dlgTitle, text, null, null);
+ },
+ confirmCheck(dlgTitle, text, checkBoxLabel, checkObj) {
+ Services.prompt.confirmEx(
+ window,
+ dlgTitle,
+ text,
+ nsIPromptService.STD_OK_CANCEL_BUTTONS,
+ "",
+ "",
+ "",
+ checkBoxLabel,
+ checkObj
+ );
+ },
+ confirmEx(
+ dlgTitle,
+ text,
+ btnFlags,
+ btn0Title,
+ btn1Title,
+ btn2Title,
+ checkBoxLabel,
+ checkVal
+ ) {
+ return Services.prompt.confirmEx(
+ window,
+ dlgTitle,
+ text,
+ btnFlags,
+ btn0Title,
+ btn1Title,
+ btn2Title,
+ checkBoxLabel,
+ checkVal
+ );
+ },
+
+ /** ***********************************************************************
+ * gEditorOutputProgressListener needs to implement both nsIPrompt *
+ * (providing alert) and nsIAuthPrompt (providing password saving). *
+ * Unfortunately, both interfaces specify prompt/promptPassword/ *
+ * promptUsernameAndPassword, albeit with conflicting method signatures. *
+ * Luckily, though, we only make use of their nsIAuthPrompt variants, *
+ * hence we can comment out the nsIPrompt ones here to avoid JavaScript *
+ * strict mode clutter. See bug 371174 for more information. *
+ *************************************************************************
+ prompt : function(dlgTitle, text, inoutText, checkBoxLabel, checkObj)
+ {
+ return Services.prompt.prompt(window, dlgTitle, text, inoutText, checkBoxLabel, checkObj);
+ },
+ promptPassword : function(dlgTitle, text, pwObj, checkBoxLabel, savePWObj)
+ {
+ var ret = false;
+ try {
+ // Note difference with nsIAuthPrompt::promptPassword, which has
+ // just "in" savePassword param, while nsIPrompt is "inout"
+ // Initialize with user's previous preference for this site
+ if (gPublishData)
+ savePWObj.value = gPublishData.savePassword;
+
+ ret = Services.prompt.promptPassword(gProgressDialog ? gProgressDialog : window,
+ dlgTitle, text, pwObj, checkBoxLabel, savePWObj);
+
+ if (!ret)
+ setTimeout(CancelPublishing, 0);
+
+ if (ret && gPublishData)
+ UpdateUsernamePasswordFromPrompt(gPublishData, gPublishData.username, pwObj.value, savePWObj.value);
+ } catch(e) {}
+
+ return ret;
+ },
+ promptUsernameAndPassword : function(dlgTitle, text, userObj, pwObj, checkBoxLabel, savePWObj)
+ {
+ var ret = PromptUsernameAndPassword(dlgTitle, text, savePWObj.value, userObj, pwObj);
+ if (!ret)
+ setTimeout(CancelPublishing, 0);
+
+ return ret;
+ },
+ *************************************************************************/
+
+ select(dlgTitle, text, selectList, outSelection) {
+ return Services.prompt.select(
+ window,
+ dlgTitle,
+ text,
+ selectList,
+ outSelection
+ );
+ },
+
+ // nsIAuthPrompt
+ prompt(dlgTitle, text, pwrealm, savePW, defaultText, result) {
+ var ret = Services.prompt.prompt(
+ gProgressDialog ? gProgressDialog : window,
+ dlgTitle,
+ text,
+ defaultText,
+ pwrealm,
+ savePWObj
+ );
+ if (!ret) {
+ setTimeout(CancelPublishing, 0);
+ }
+ return ret;
+ },
+
+ promptUsernameAndPassword(dlgTitle, text, pwrealm, savePW, userObj, pwObj) {
+ var ret = PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj);
+ if (!ret) {
+ setTimeout(CancelPublishing, 0);
+ }
+ return ret;
+ },
+
+ promptPassword(dlgTitle, text, pwrealm, savePW, pwObj) {
+ var ret = false;
+ try {
+ // Note difference with nsIPrompt::promptPassword, which has
+ // "inout" savePassword param, while nsIAuthPrompt is just "in"
+ // Also nsIAuth doesn't supply "checkBoxLabel"
+ // Initialize with user's previous preference for this site
+ var savePWObj = { value: savePW };
+ // Initialize with user's previous preference for this site
+ if (gPublishData) {
+ savePWObj.value = gPublishData.savePassword;
+ }
+
+ ret = Services.prompt.promptPassword(
+ gProgressDialog ? gProgressDialog : window,
+ dlgTitle,
+ text,
+ pwObj,
+ GetString("SavePassword"),
+ savePWObj
+ );
+
+ if (!ret) {
+ setTimeout(CancelPublishing, 0);
+ }
+
+ if (ret && gPublishData) {
+ UpdateUsernamePasswordFromPrompt(
+ gPublishData,
+ gPublishData.username,
+ pwObj.value,
+ savePWObj.value
+ );
+ }
+ } catch (e) {}
+
+ return ret;
+ },
+};
+
+function PromptUsernameAndPassword(dlgTitle, text, savePW, userObj, pwObj) {
+ // HTTP prompts us twice even if user Cancels from 1st attempt!
+ // So never put up dialog if there's no publish data
+ if (!gPublishData) {
+ return false;
+ }
+
+ var ret = false;
+ try {
+ var savePWObj = { value: savePW };
+
+ // Initialize with user's previous preference for this site
+ if (gPublishData) {
+ // HTTP put uses this dialog if either username or password is bad,
+ // so prefill username input field with the previous value for modification
+ savePWObj.value = gPublishData.savePassword;
+ if (!userObj.value) {
+ userObj.value = gPublishData.username;
+ }
+ }
+
+ ret = Services.prompt.promptUsernameAndPassword(
+ gProgressDialog ? gProgressDialog : window,
+ dlgTitle,
+ text,
+ userObj,
+ pwObj,
+ GetString("SavePassword"),
+ savePWObj
+ );
+ if (ret && gPublishData) {
+ UpdateUsernamePasswordFromPrompt(
+ gPublishData,
+ userObj.value,
+ pwObj.value,
+ savePWObj.value
+ );
+ }
+ } catch (e) {}
+
+ return ret;
+}
+
+/* eslint-disable complexity */
+function DumpDebugStatus(aStatus) {
+ // see nsError.h and netCore.h and ftpCore.h
+
+ if (aStatus == kErrorBindingAborted) {
+ dump("***** status is NS_BINDING_ABORTED\n");
+ } else if (aStatus == kErrorBindingRedirected) {
+ dump("***** status is NS_BINDING_REDIRECTED\n");
+ } else if (aStatus == 2152398859) {
+ // in netCore.h 11
+ dump("***** status is ALREADY_CONNECTED\n");
+ } else if (aStatus == 2152398860) {
+ // in netCore.h 12
+ dump("***** status is NOT_CONNECTED\n");
+ } else if (aStatus == 2152398861) {
+ // in nsISocketTransportService.idl 13
+ dump("***** status is CONNECTION_REFUSED\n");
+ } else if (aStatus == 2152398862) {
+ // in nsISocketTransportService.idl 14
+ dump("***** status is NET_TIMEOUT\n");
+ } else if (aStatus == 2152398863) {
+ // in netCore.h 15
+ dump("***** status is IN_PROGRESS\n");
+ } else if (aStatus == 2152398864) {
+ // 0x804b0010 in netCore.h 16
+ dump("***** status is OFFLINE\n");
+ } else if (aStatus == 2152398865) {
+ // in netCore.h 17
+ dump("***** status is NO_CONTENT\n");
+ } else if (aStatus == 2152398866) {
+ // in netCore.h 18
+ dump("***** status is UNKNOWN_PROTOCOL\n");
+ } else if (aStatus == 2152398867) {
+ // in netCore.h 19
+ dump("***** status is PORT_ACCESS_NOT_ALLOWED\n");
+ } else if (aStatus == 2152398868) {
+ // in nsISocketTransportService.idl 20
+ dump("***** status is NET_RESET\n");
+ } else if (aStatus == 2152398869) {
+ // in ftpCore.h 21
+ dump("***** status is FTP_LOGIN\n");
+ } else if (aStatus == 2152398870) {
+ // in ftpCore.h 22
+ dump("***** status is FTP_CWD\n");
+ } else if (aStatus == 2152398871) {
+ // in ftpCore.h 23
+ dump("***** status is FTP_PASV\n");
+ } else if (aStatus == 2152398872) {
+ // in ftpCore.h 24
+ dump("***** status is FTP_PWD\n");
+ } else if (aStatus == 2152857601) {
+ dump("***** status is UNRECOGNIZED_PATH\n");
+ } else if (aStatus == 2152857602) {
+ dump("***** status is UNRESOLABLE SYMLINK\n");
+ } else if (aStatus == 2152857604) {
+ dump("***** status is UNKNOWN_TYPE\n");
+ } else if (aStatus == 2152857605) {
+ dump("***** status is DESTINATION_NOT_DIR\n");
+ } else if (aStatus == 2152857606) {
+ dump("***** status is TARGET_DOES_NOT_EXIST\n");
+ } else if (aStatus == 2152857608) {
+ dump("***** status is ALREADY_EXISTS\n");
+ } else if (aStatus == 2152857609) {
+ dump("***** status is INVALID_PATH\n");
+ } else if (aStatus == 2152857610) {
+ dump("***** status is DISK_FULL\n");
+ } else if (aStatus == 2152857612) {
+ dump("***** status is NOT_DIRECTORY\n");
+ } else if (aStatus == 2152857613) {
+ dump("***** status is IS_DIRECTORY\n");
+ } else if (aStatus == 2152857614) {
+ dump("***** status is IS_LOCKED\n");
+ } else if (aStatus == 2152857615) {
+ dump("***** status is TOO_BIG\n");
+ } else if (aStatus == 2152857616) {
+ dump("***** status is NO_DEVICE_SPACE\n");
+ } else if (aStatus == 2152857617) {
+ dump("***** status is NAME_TOO_LONG\n");
+ } else if (aStatus == 2152857618) {
+ // 80520012
+ dump("***** status is FILE_NOT_FOUND\n");
+ } else if (aStatus == 2152857619) {
+ dump("***** status is READ_ONLY\n");
+ } else if (aStatus == 2152857620) {
+ dump("***** status is DIR_NOT_EMPTY\n");
+ } else if (aStatus == 2152857621) {
+ dump("***** status is ACCESS_DENIED\n");
+ } else if (aStatus == 2152398878) {
+ dump("***** status is ? (No connection or time out?)\n");
+ } else {
+ dump("***** status is " + aStatus + "\n");
+ }
+}
+/* eslint-enable complexity */
+
+// Update any data that the user supplied in a prompt dialog
+function UpdateUsernamePasswordFromPrompt(
+ publishData,
+ username,
+ password,
+ savePassword
+) {
+ if (!publishData) {
+ return;
+ }
+
+ // Set flag to save publish data after publishing if it changed in dialog
+ // and the "SavePassword" checkbox was checked
+ // or we already had site data for this site
+ // (Thus we don't automatically create a site until user brings up Publish As dialog)
+ publishData.savePublishData =
+ (gPublishData.username != username || gPublishData.password != password) &&
+ (savePassword || !publishData.notInSiteData);
+
+ publishData.username = username;
+ publishData.password = password;
+ publishData.savePassword = savePassword;
+}
+
+const kSupportedTextMimeTypes = [
+ "text/plain",
+ "text/css",
+ "text/rdf",
+ "text/xsl",
+ "text/javascript", // obsolete type
+ "text/ecmascript", // obsolete type
+ "application/javascript",
+ "application/ecmascript",
+ "application/x-javascript", // obsolete type
+ "text/xul", // obsolete type
+ "application/vnd.mozilla.xul+xml", // obsolete type
+ "application/xhtml+xml",
+];
+
+function IsSupportedTextMimeType(aMimeType) {
+ for (var i = 0; i < kSupportedTextMimeTypes.length; i++) {
+ if (kSupportedTextMimeTypes[i] == aMimeType) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* eslint-disable complexity */
+// throws an error or returns true if user attempted save; false if user canceled save
+async function SaveDocument(aSaveAs, aSaveCopy, aMimeType) {
+ var editor = GetCurrentEditor();
+ if (!aMimeType || !editor) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
+ }
+
+ var editorDoc = editor.document;
+ if (!editorDoc) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
+ }
+
+ // if we don't have the right editor type bail (we handle text and html)
+ var editorType = GetCurrentEditorType();
+ if (!["text", "html", "htmlmail", "textmail"].includes(editorType)) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ var saveAsTextFile = IsSupportedTextMimeType(aMimeType);
+
+ // check if the file is to be saved is a format we don't understand; if so, bail
+ if (
+ aMimeType != kHTMLMimeType &&
+ aMimeType != kXHTMLMimeType &&
+ !saveAsTextFile
+ ) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ if (saveAsTextFile) {
+ aMimeType = "text/plain";
+ }
+
+ var urlstring = GetDocumentUrl();
+ var mustShowFileDialog =
+ aSaveAs || IsUrlAboutBlank(urlstring) || urlstring == "";
+
+ // If editing a remote URL, force SaveAs dialog
+ if (!mustShowFileDialog && GetScheme(urlstring) != "file") {
+ mustShowFileDialog = true;
+ }
+
+ var doUpdateURI = false;
+ var tempLocalFile = null;
+
+ if (mustShowFileDialog) {
+ try {
+ // Prompt for title if we are saving to HTML
+ if (!saveAsTextFile && editorType == "html") {
+ var userContinuing = PromptAndSetTitleIfNone(); // not cancel
+ if (!userContinuing) {
+ return false;
+ }
+ }
+
+ var dialogResult = await PromptForSaveLocation(
+ saveAsTextFile,
+ editorType,
+ aMimeType,
+ urlstring
+ );
+ if (!dialogResult) {
+ return false;
+ }
+
+ // What is this unused 'replacing' var supposed to be doing?
+ /* eslint-disable-next-line no-unused-vars */
+ var replacing =
+ dialogResult.filepickerClick == nsIFilePicker.returnReplace;
+
+ urlstring = dialogResult.resultingURIString;
+ tempLocalFile = dialogResult.resultingLocalFile;
+
+ // update the new URL for the webshell unless we are saving a copy
+ if (!aSaveCopy) {
+ doUpdateURI = true;
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ return false;
+ }
+ } // mustShowFileDialog
+
+ var success = true;
+ try {
+ // if somehow we didn't get a local file but we did get a uri,
+ // attempt to create the localfile if it's a "file" url
+ var docURI;
+ if (!tempLocalFile) {
+ docURI = Services.io.newURI(urlstring, editor.documentCharacterSet);
+
+ if (docURI.schemeIs("file")) {
+ var fileHandler = GetFileProtocolHandler();
+ tempLocalFile = fileHandler
+ .getFileFromURLSpec(urlstring)
+ .QueryInterface(Ci.nsIFile);
+ }
+ }
+
+ // this is the location where the related files will go
+ var relatedFilesDir = null;
+
+ // Only change links or move files if pref is set
+ // and we are saving to a new location
+ if (Services.prefs.getBoolPref("editor.save_associated_files") && aSaveAs) {
+ try {
+ if (tempLocalFile) {
+ // if we are saving to the same parent directory, don't set relatedFilesDir
+ // grab old location, chop off file
+ // grab new location, chop off file, compare
+ var oldLocation = GetDocumentUrl();
+ var oldLocationLastSlash = oldLocation.lastIndexOf("/");
+ if (oldLocationLastSlash != -1) {
+ oldLocation = oldLocation.slice(0, oldLocationLastSlash);
+ }
+
+ var relatedFilesDirStr = urlstring;
+ var newLocationLastSlash = relatedFilesDirStr.lastIndexOf("/");
+ if (newLocationLastSlash != -1) {
+ relatedFilesDirStr = relatedFilesDirStr.slice(
+ 0,
+ newLocationLastSlash
+ );
+ }
+ if (
+ oldLocation == relatedFilesDirStr ||
+ IsUrlAboutBlank(oldLocation)
+ ) {
+ relatedFilesDir = null;
+ } else {
+ relatedFilesDir = tempLocalFile.parent;
+ }
+ } else {
+ var lastSlash = urlstring.lastIndexOf("/");
+ if (lastSlash != -1) {
+ var relatedFilesDirString = urlstring.slice(0, lastSlash + 1); // include last slash
+ relatedFilesDir = Services.io.newURI(
+ relatedFilesDirString,
+ editor.documentCharacterSet
+ );
+ }
+ }
+ } catch (e) {
+ relatedFilesDir = null;
+ }
+ }
+
+ let destinationLocation = tempLocalFile ? tempLocalFile : docURI;
+
+ success = OutputFileWithPersistAPI(
+ editorDoc,
+ destinationLocation,
+ relatedFilesDir,
+ aMimeType
+ );
+ } catch (e) {
+ success = false;
+ }
+
+ if (success) {
+ try {
+ if (doUpdateURI) {
+ // If a local file, we must create a new uri from nsIFile
+ if (tempLocalFile) {
+ docURI = GetFileProtocolHandler().newFileURI(tempLocalFile);
+ }
+
+ // We need to set new document uri before notifying listeners
+ SetDocumentURI(docURI);
+ }
+
+ // Update window title to show possibly different filename
+ // This also covers problem that after undoing a title change,
+ // window title loses the extra [filename] part that this adds
+ UpdateWindowTitle();
+
+ if (!aSaveCopy) {
+ editor.resetModificationCount();
+ }
+ // this should cause notification to listeners that document has changed
+
+ // Set UI based on whether we're editing a remote or local url
+ SetSaveAndPublishUI(urlstring);
+ } catch (e) {}
+ } else {
+ Services.prompt.alert(
+ window,
+ GetString("SaveDocument"),
+ GetString("SaveFileFailed")
+ );
+ }
+ return success;
+}
+/* eslint-enable complexity */
+
+function SetDocumentURI(uri) {
+ try {
+ // XXX WE'LL NEED TO GET "CURRENT" CONTENT FRAME ONCE MULTIPLE EDITORS ARE ALLOWED
+ GetCurrentEditorElement().docShell.setCurrentURI(uri);
+ } catch (e) {
+ dump("SetDocumentURI:\n" + e + "\n");
+ }
+}
+
+// ------------------------------- Publishing
+var gPublishData;
+var gProgressDialog;
+var gCommandAfterPublishing = null;
+var gRestoreDocumentSource;
+
+function Publish(publishData) {
+ if (!publishData) {
+ return false;
+ }
+
+ // Set data in global for username password requests
+ // and to do "post saving" actions after monitoring nsIWebProgressListener messages
+ // and we are sure file transfer was successful
+ gPublishData = publishData;
+
+ gPublishData.docURI = CreateURIFromPublishData(publishData, true);
+ if (!gPublishData.docURI) {
+ Services.prompt.alert(
+ window,
+ GetString("Publish"),
+ GetString("PublishFailed")
+ );
+ return false;
+ }
+
+ if (gPublishData.publishOtherFiles) {
+ gPublishData.otherFilesURI = CreateURIFromPublishData(publishData, false);
+ } else {
+ gPublishData.otherFilesURI = null;
+ }
+
+ if (gShowDebugOutputStateChange) {
+ dump(
+ "\n *** publishData: PublishUrl=" +
+ publishData.publishUrl +
+ ", BrowseUrl=" +
+ publishData.browseUrl +
+ ", Username=" +
+ publishData.username +
+ ", Dir=" +
+ publishData.docDir +
+ ", Filename=" +
+ publishData.filename +
+ "\n"
+ );
+ dump(
+ " * gPublishData.docURI.spec w/o pass=" +
+ StripPassword(gPublishData.docURI.spec) +
+ ", PublishOtherFiles=" +
+ gPublishData.publishOtherFiles +
+ "\n"
+ );
+ }
+
+ // XXX Missing username will make FTP fail
+ // and it won't call us for prompt dialog (bug 132320)
+ // (It does prompt if just password is missing)
+ // So we should do the prompt ourselves before trying to publish
+ if (GetScheme(publishData.publishUrl) == "ftp" && !publishData.username) {
+ var message = GetString("PromptFTPUsernamePassword").replace(
+ /%host%/,
+ GetHost(publishData.publishUrl)
+ );
+ var savePWobj = { value: publishData.savePassword };
+ var userObj = { value: publishData.username };
+ var pwObj = { value: publishData.password };
+ if (
+ !PromptUsernameAndPassword(
+ GetString("Prompt"),
+ message,
+ savePWobj,
+ userObj,
+ pwObj
+ )
+ ) {
+ // User canceled out of dialog.
+ return false;
+ }
+
+ // Reset data in URI objects
+ gPublishData.docURI.username = publishData.username;
+ gPublishData.docURI.password = publishData.password;
+
+ if (gPublishData.otherFilesURI) {
+ gPublishData.otherFilesURI.username = publishData.username;
+ gPublishData.otherFilesURI.password = publishData.password;
+ }
+ }
+
+ try {
+ // We launch dialog as a dependent
+ // Don't allow editing document!
+ SetDocumentEditable(false);
+
+ // Start progress monitoring
+ gProgressDialog = window.openDialog(
+ "chrome://editor/content/EditorPublishProgress.xhtml",
+ "_blank",
+ "chrome,dependent,titlebar",
+ gPublishData,
+ gPersistObj
+ );
+ } catch (e) {}
+
+ // Network transfer is often too quick for the progress dialog to be initialized
+ // and we can completely miss messages for quickly-terminated bad URLs,
+ // so we can't call OutputFileWithPersistAPI right away.
+ // StartPublishing() is called at the end of the dialog's onload method
+ return true;
+}
+
+function StartPublishing() {
+ var editor = GetCurrentEditor();
+ if (editor && gPublishData && gPublishData.docURI && gProgressDialog) {
+ gRestoreDocumentSource = null;
+
+ // Save backup document since nsIWebBrowserPersist changes image src urls
+ // but we only need to do this if publishing images and other related files
+ if (gPublishData.otherFilesURI) {
+ try {
+ gRestoreDocumentSource = editor.outputToString(
+ editor.contentsMIMEType,
+ kOutputEncodeW3CEntities
+ );
+ } catch (e) {}
+ }
+
+ OutputFileWithPersistAPI(
+ editor.document,
+ gPublishData.docURI,
+ gPublishData.otherFilesURI,
+ editor.contentsMIMEType
+ );
+ return gPersistObj;
+ }
+ return null;
+}
+
+function CancelPublishing() {
+ try {
+ gPersistObj.cancelSave();
+ gProgressDialog.SetProgressStatusCancel();
+ } catch (e) {}
+
+ // If canceling publishing do not do any commands after this
+ gCommandAfterPublishing = null;
+
+ if (gProgressDialog) {
+ // Close Progress dialog
+ // (this will call FinishPublishing())
+ gProgressDialog.CloseDialog();
+ } else {
+ FinishPublishing();
+ }
+}
+
+function FinishPublishing() {
+ SetDocumentEditable(true);
+ gProgressDialog = null;
+ gPublishData = null;
+ gRestoreDocumentSource = null;
+
+ if (gCommandAfterPublishing) {
+ // Be sure to null out the global now in case of trouble when executing command
+ var command = gCommandAfterPublishing;
+ gCommandAfterPublishing = null;
+ goDoCommand(command);
+ }
+}
+
+// Create a nsIURI object filled in with all required publishing info
+function CreateURIFromPublishData(publishData, doDocUri) {
+ if (!publishData || !publishData.publishUrl) {
+ return null;
+ }
+
+ var URI;
+ try {
+ var spec = publishData.publishUrl;
+ if (doDocUri) {
+ spec += FormatDirForPublishing(publishData.docDir) + publishData.filename;
+ } else {
+ spec += FormatDirForPublishing(publishData.otherDir);
+ }
+
+ URI = Services.io.newURI(spec, GetCurrentEditor().documentCharacterSet);
+
+ if (publishData.username) {
+ URI.username = publishData.username;
+ }
+ if (publishData.password) {
+ URI.password = publishData.password;
+ }
+ } catch (e) {}
+
+ return URI;
+}
+
+// Resolve the correct "http:" document URL when publishing via ftp
+function GetDocUrlFromPublishData(publishData) {
+ if (!publishData || !publishData.filename || !publishData.publishUrl) {
+ return "";
+ }
+
+ // If user was previously editing an "ftp" url, then keep that as the new scheme
+ var url;
+
+ // Always use the "HTTP" address if available
+ // XXX Should we do some more validation here for bad urls???
+ // Let's at least check for a scheme!
+ if (!GetScheme(publishData.browseUrl)) {
+ url = publishData.publishUrl;
+ } else {
+ url = publishData.browseUrl;
+ }
+
+ url += FormatDirForPublishing(publishData.docDir) + publishData.filename;
+
+ if (GetScheme(url) == "ftp") {
+ url = InsertUsernameIntoUrl(url, publishData.username);
+ }
+
+ return url;
+}
+
+function SetSaveAndPublishUI(urlstring) {
+ // Be sure enabled state of toolbar buttons are correct
+ goUpdateCommand("cmd_save");
+ goUpdateCommand("cmd_publish");
+}
+
+function SetDocumentEditable(isDocEditable) {
+ var editor = GetCurrentEditor();
+ if (editor && editor.document) {
+ try {
+ var flags = editor.flags;
+ editor.flags = isDocEditable
+ ? (flags &= ~Ci.nsIEditor.eEditorReadonlyMask)
+ : flags | Ci.nsIEditor.eEditorReadonlyMask;
+ } catch (e) {}
+
+ // update all commands
+ window.updateCommands("create");
+ }
+}
+
+// ****** end of save / publish **********//
+
+var nsPublishSettingsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ if (GetCurrentEditor()) {
+ // Launch Publish Settings dialog
+
+ window.ok = window.openDialog(
+ "chrome://editor/content/EditorPublishSettings.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ ""
+ );
+ return window.ok;
+ }
+ return false;
+ },
+};
+
+var nsRevertCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return (
+ IsDocumentEditable() &&
+ IsDocumentModified() &&
+ !IsUrlAboutBlank(GetDocumentUrl())
+ );
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // Confirm with the user to abandon current changes
+ // Put the page title in the message string
+ let title = GetDocumentTitle();
+ let msg = GetString("AbandonChanges").replace(/%title%/, title);
+
+ let result = Services.prompt.confirmEx(
+ window,
+ GetString("RevertCaption"),
+ msg,
+ Services.prompt.BUTTON_TITLE_REVERT * Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1,
+ null,
+ null,
+ null,
+ null,
+ { value: 0 }
+ );
+
+ // Reload page if first button (Revert) was pressed
+ if (result == 0) {
+ CancelHTMLSource();
+ EditorLoadUrl(GetDocumentUrl());
+ }
+ },
+};
+
+var nsCloseCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return GetCurrentEditor() != null;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ CloseWindow();
+ },
+};
+
+async function CloseWindow() {
+ // Check to make sure document is saved. "true" means allow "Don't Save" button,
+ // so user can choose to close without saving
+ if (await CheckAndSaveDocument("cmd_close", true)) {
+ if (window.InsertCharWindow) {
+ SwitchInsertCharToAnotherEditorOrClose();
+ }
+
+ try {
+ var basewin = window
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIBaseWindow);
+ basewin.destroy();
+ } catch (e) {}
+ }
+}
+
+var nsOpenRemoteCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ // We can always do this.
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var params = { action: "2", url: "" };
+ openDialog(
+ "chrome://communicator/content/openLocation.xhtml",
+ "_blank",
+ "chrome,modal,titlebar",
+ params
+ );
+ var win = getTopWin();
+ switch (params.action) {
+ case "0": // current window
+ win.focus();
+ win.loadURI(params.url, null, null, true);
+ break;
+ case "1": // new window
+ openDialog(
+ getBrowserURL(),
+ "_blank",
+ "all,dialog=no",
+ params.url,
+ null,
+ null,
+ null,
+ true
+ );
+ break;
+ case "2": // edit
+ editPage(params.url);
+ break;
+ case "3": // new tab
+ win.focus();
+ var browser = win.getBrowser();
+ browser.selectedTab = browser.addTab(params.url, {
+ allowThirdPartyFixup: true,
+ });
+ break;
+ case "4": // private
+ openNewPrivateWith(params.url);
+ break;
+ default:
+ break;
+ }
+ },
+};
+
+var nsPreviewCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return (
+ IsDocumentEditable() &&
+ IsHTMLEditor() &&
+ (DocumentHasBeenSaved() || IsDocumentModified())
+ );
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ async doCommand(aCommand) {
+ // Don't continue if user canceled during prompt for saving
+ // DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
+ if (!(await CheckAndSaveDocument("cmd_preview", DocumentHasBeenSaved()))) {
+ return;
+ }
+
+ // Check if we saved again just in case?
+ if (DocumentHasBeenSaved()) {
+ let browser;
+ try {
+ // Find a browser with this URL
+ let enumerator = Services.wm.getEnumerator("navigator:browser");
+
+ var documentURI = GetDocumentUrl();
+ while (enumerator.hasMoreElements()) {
+ browser = enumerator.getNext();
+ if (
+ browser &&
+ !browser.closed &&
+ documentURI == browser.getBrowser().currentURI.spec
+ ) {
+ break;
+ }
+
+ browser = null;
+ }
+ } catch (ex) {}
+
+ // If none found, open a new browser
+ if (!browser) {
+ browser = window.openDialog(
+ getBrowserURL(),
+ "_blank",
+ "chrome,all,dialog=no",
+ documentURI
+ );
+ } else {
+ try {
+ browser.BrowserReloadSkipCache();
+ browser.focus();
+ } catch (ex) {}
+ }
+ }
+ },
+};
+
+var nsSendPageCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return (
+ IsDocumentEditable() && (DocumentHasBeenSaved() || IsDocumentModified())
+ );
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ async doCommand(aCommand) {
+ // Don't continue if user canceled during prompt for saving
+ // DocumentHasBeenSaved will test if we have a URL and suppress "Don't Save" button if not
+ if (
+ !(await CheckAndSaveDocument("cmd_editSendPage", DocumentHasBeenSaved()))
+ ) {
+ return;
+ }
+
+ // Check if we saved again just in case?
+ if (DocumentHasBeenSaved()) {
+ // Launch Messenger Composer window with current page as contents
+ try {
+ openComposeWindow(GetDocumentUrl(), GetDocumentTitle());
+ } catch (ex) {
+ dump("Cannot Send Page: " + ex + "\n");
+ }
+ }
+ },
+};
+
+var nsPrintCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return true; // we can always do this
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // In editor.js
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ try {
+ let browser = GetCurrentEditorElement();
+ PrintUtils.printWindow(browser.outerWindowID, browser);
+ } catch (e) {}
+ },
+};
+
+var nsPrintPreviewCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ // We can always do this.
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // In editor.js
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ try {
+ PrintUtils.printPreview("editor", PrintPreviewListener);
+ } catch (e) {}
+ },
+};
+
+var nsPrintSetupCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return true; // we can always do this
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // In editor.js
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ PrintUtils.showPageSetup();
+ },
+};
+
+var nsFindReplaceCommand = {
+ isCommandEnabled(aCommand, editorElement) {
+ return editorElement.getEditor(editorElement.contentWindow) != null;
+ },
+
+ getCommandStateParams(aCommand, aParams, editorElement) {},
+ doCommandParams(aCommand, aParams, editorElement) {},
+
+ doCommand(aCommand, editorElement) {
+ window.openDialog(
+ "chrome://editor/content/EdReplace.xhtml",
+ "_blank",
+ "chrome,modal,titlebar",
+ editorElement
+ );
+ },
+};
+
+var nsFindCommand = {
+ isCommandEnabled(aCommand, editorElement) {
+ return editorElement.getEditor(editorElement.contentWindow) != null;
+ },
+
+ getCommandStateParams(aCommand, aParams, editorElement) {},
+ doCommandParams(aCommand, aParams, editorElement) {},
+
+ doCommand(aCommand, editorElement) {
+ document.getElementById("FindToolbar").onFindCommand();
+ },
+};
+
+var nsFindAgainCommand = {
+ isCommandEnabled(aCommand, editorElement) {
+ // we can only do this if the search pattern is non-empty. Not sure how
+ // to get that from here
+ return editorElement.getEditor(editorElement.contentWindow) != null;
+ },
+
+ getCommandStateParams(aCommand, aParams, editorElement) {},
+ doCommandParams(aCommand, aParams, editorElement) {},
+
+ doCommand(aCommand, editorElement) {
+ let findPrev = aCommand == "cmd_findPrev";
+ document.getElementById("FindToolbar").onFindAgainCommand(findPrev);
+ },
+};
+
+var nsRewrapCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return (
+ IsDocumentEditable() &&
+ !IsInHTMLSourceMode() &&
+ GetCurrentEditor() instanceof Ci.nsIEditorMailSupport
+ );
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // We only want to respect new lines when using the web composer.
+ let respectNewLines = IsWebComposer();
+ GetCurrentEditor()
+ .QueryInterface(Ci.nsIEditorMailSupport)
+ .rewrap(respectNewLines);
+ },
+};
+
+var nsSpellingCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return (
+ IsDocumentEditable() && !IsInHTMLSourceMode() && IsSpellCheckerInstalled()
+ );
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.cancelSendMessage = false;
+ try {
+ var skipBlockQuotes =
+ window.document.documentElement.getAttribute("windowtype") ==
+ "msgcompose";
+ window.openDialog(
+ "chrome://editor/content/EdSpellCheck.xhtml",
+ "_blank",
+ "dialog,close,titlebar,modal,resizable",
+ false,
+ skipBlockQuotes,
+ true
+ );
+ } catch (ex) {}
+ },
+};
+
+// Validate using http://validator.w3.org/file-upload.html
+var URL2Validate;
+var nsValidateCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return GetCurrentEditor() != null;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ async doCommand(aCommand) {
+ // If the document hasn't been modified,
+ // then just validate the current url.
+ if (IsDocumentModified() || IsHTMLSourceChanged()) {
+ if (!(await CheckAndSaveDocument("cmd_validate", false))) {
+ return;
+ }
+
+ // Check if we saved again just in case?
+ if (!DocumentHasBeenSaved()) {
+ // user hit cancel?
+ return;
+ }
+ }
+
+ URL2Validate = GetDocumentUrl();
+ // See if it's a file:
+ var ifile;
+ try {
+ var fileHandler = GetFileProtocolHandler();
+ ifile = fileHandler.getFileFromURLSpec(URL2Validate);
+ // nsIFile throws an exception if it's not a file url
+ } catch (e) {
+ ifile = null;
+ }
+ if (ifile) {
+ URL2Validate = ifile.path;
+ var vwin = window.open(
+ "http://validator.w3.org/file-upload.html",
+ "EditorValidate"
+ );
+ // Window loads asynchronously, so pass control to the load listener:
+ vwin.addEventListener("load", this.validateFilePageLoaded);
+ } else {
+ window.open(
+ `http://validator.w3.org/check?uri=${URL2Validate}&doctype=Inline`,
+ "EditorValidate"
+ );
+ // This does the validation, no need to wait for page loaded.
+ }
+ },
+ validateFilePageLoaded(event) {
+ event.target.forms[0].uploaded_file.value = URL2Validate;
+ },
+};
+
+var nsFormCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdFormProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsInputTagCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdInputProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsInputImageCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdInputImage.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsTextAreaCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdTextAreaProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsSelectCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdSelectProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsButtonCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdButtonProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsLabelCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var tagName = "label";
+ try {
+ var editor = GetCurrentEditor();
+ // Find selected label or if start/end of selection is in label
+ var labelElement = editor.getSelectedElement(tagName);
+ if (!labelElement) {
+ labelElement = editor.getElementOrParentByTagName(
+ tagName,
+ editor.selection.anchorNode
+ );
+ }
+ if (!labelElement) {
+ labelElement = editor.getElementOrParentByTagName(
+ tagName,
+ editor.selection.focusNode
+ );
+ }
+ if (labelElement) {
+ // We only open the dialog for an existing label
+ window.openDialog(
+ "chrome://editor/content/EdLabelProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ labelElement
+ );
+ } else {
+ EditorSetTextProperty(tagName, "", "");
+ }
+ } catch (e) {}
+ },
+};
+
+var nsFieldSetCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdFieldSetProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsImageCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdImageProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsHLineCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // Inserting an HLine is different in that we don't use properties dialog
+ // unless we are editing an existing line's attributes
+ // We get the last-used attributes from the prefs and insert immediately
+
+ var tagName = "hr";
+ var editor = GetCurrentEditor();
+
+ var hLine;
+ try {
+ hLine = editor.getSelectedElement(tagName);
+ } catch (e) {
+ return;
+ }
+
+ if (hLine) {
+ // We only open the dialog for an existing HRule
+ window.openDialog(
+ "chrome://editor/content/EdHLineProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ } else {
+ try {
+ hLine = editor.createElementWithDefaults(tagName);
+
+ // We change the default attributes to those saved in the user prefs
+ let align = Services.prefs.getIntPref("editor.hrule.align");
+ if (align == 0) {
+ editor.setAttributeOrEquivalent(hLine, "align", "left", true);
+ } else if (align == 2) {
+ editor.setAttributeOrEquivalent(hLine, "align", "right", true);
+ }
+
+ // Note: Default is center (don't write attribute)
+
+ let width = Services.prefs.getIntPref("editor.hrule.width");
+ if (Services.prefs.getBoolPref("editor.hrule.width_percent")) {
+ width = width + "%";
+ }
+
+ editor.setAttributeOrEquivalent(hLine, "width", width, true);
+
+ let height = Services.prefs.getIntPref("editor.hrule.height");
+ editor.setAttributeOrEquivalent(hLine, "size", String(height), true);
+
+ if (Services.prefs.getBoolPref("editor.hrule.shading")) {
+ hLine.removeAttribute("noshade");
+ } else {
+ hLine.setAttribute("noshade", "noshade");
+ }
+
+ editor.insertElementAtSelection(hLine, true);
+ } catch (e) {}
+ }
+ },
+};
+
+var nsLinkCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // If selected element is an image, launch that dialog instead
+ // since last tab panel handles link around an image
+ var element = GetObjectForProperties();
+ if (element && element.nodeName.toLowerCase() == "img") {
+ window.openDialog(
+ "chrome://editor/content/EdImageProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ null,
+ true
+ );
+ } else {
+ window.openDialog(
+ "chrome://editor/content/EdLinkProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ }
+ },
+};
+
+var nsAnchorCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdNamedAnchorProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ ""
+ );
+ },
+};
+
+var nsInsertHTMLWithDialogCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdInsSrc.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable",
+ ""
+ );
+ },
+};
+
+var nsInsertMathWithDialogCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdInsertMath.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable",
+ ""
+ );
+ },
+};
+
+var nsInsertCharsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ EditorFindOrCreateInsertCharWindow();
+ },
+};
+
+var nsInsertBreakCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentEditor().insertHTML("<br>");
+ } catch (e) {}
+ },
+};
+
+var nsInsertBreakAllCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentEditor().insertHTML("<br clear='all'>");
+ } catch (e) {}
+ },
+};
+
+var nsGridCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdSnapToGrid.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsListPropertiesCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdListProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ },
+};
+
+var nsPagePropertiesCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var oldTitle = GetDocumentTitle();
+ window.openDialog(
+ "chrome://editor/content/EdPageProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ ""
+ );
+
+ // Update main window title and
+ // recent menu data in prefs if doc title changed
+ if (GetDocumentTitle() != oldTitle) {
+ UpdateWindowTitle();
+ }
+ },
+};
+
+var nsObjectPropertiesCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ var isEnabled = false;
+ if (IsDocumentEditable() && IsEditingRenderedHTML()) {
+ isEnabled =
+ GetObjectForProperties() != null ||
+ GetCurrentEditor().getSelectedElement("href") != null;
+ }
+ return isEnabled;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // Launch Object properties for appropriate selected element
+ var element = GetObjectForProperties();
+ if (element) {
+ var name = element.nodeName.toLowerCase();
+ switch (name) {
+ case "img":
+ goDoCommand("cmd_image");
+ break;
+ case "hr":
+ goDoCommand("cmd_hline");
+ break;
+ case "form":
+ goDoCommand("cmd_form");
+ break;
+ case "input":
+ var type = element.getAttribute("type");
+ if (type && type.toLowerCase() == "image") {
+ goDoCommand("cmd_inputimage");
+ } else {
+ goDoCommand("cmd_inputtag");
+ }
+ break;
+ case "textarea":
+ goDoCommand("cmd_textarea");
+ break;
+ case "select":
+ goDoCommand("cmd_select");
+ break;
+ case "button":
+ goDoCommand("cmd_button");
+ break;
+ case "label":
+ goDoCommand("cmd_label");
+ break;
+ case "fieldset":
+ goDoCommand("cmd_fieldset");
+ break;
+ case "table":
+ EditorInsertOrEditTable(false);
+ break;
+ case "td":
+ case "th":
+ EditorTableCellProperties();
+ break;
+ case "ol":
+ case "ul":
+ case "dl":
+ case "li":
+ goDoCommand("cmd_listProperties");
+ break;
+ case "a":
+ if (element.name) {
+ goDoCommand("cmd_anchor");
+ } else if (element.href) {
+ goDoCommand("cmd_link");
+ }
+ break;
+ case "math":
+ goDoCommand("cmd_insertMathWithDialog");
+ break;
+ default:
+ doAdvancedProperties(element);
+ break;
+ }
+ } else {
+ // We get a partially-selected link if asked for specifically
+ try {
+ element = GetCurrentEditor().getSelectedElement("href");
+ } catch (e) {}
+ if (element) {
+ goDoCommand("cmd_link");
+ }
+ }
+ },
+};
+
+var nsSetSmiley = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {
+ var smileyCode = aParams.getStringValue("state_attribute");
+
+ var strSml;
+ switch (smileyCode) {
+ case ":-)":
+ strSml = "s1";
+ break;
+ case ":-(":
+ strSml = "s2";
+ break;
+ case ";-)":
+ strSml = "s3";
+ break;
+ case ":-P":
+ case ":-p":
+ case ":-b":
+ strSml = "s4";
+ break;
+ case ":-D":
+ strSml = "s5";
+ break;
+ case ":-[":
+ strSml = "s6";
+ break;
+ case ":-/":
+ case ":/":
+ case ":-\\":
+ case ":\\":
+ strSml = "s7";
+ break;
+ case "=-O":
+ case "=-o":
+ strSml = "s8";
+ break;
+ case ":-*":
+ strSml = "s9";
+ break;
+ case ">:o":
+ case ">:-o":
+ strSml = "s10";
+ break;
+ case "8-)":
+ strSml = "s11";
+ break;
+ case ":-$":
+ strSml = "s12";
+ break;
+ case ":-!":
+ strSml = "s13";
+ break;
+ case "O:-)":
+ case "o:-)":
+ strSml = "s14";
+ break;
+ case ":'(":
+ strSml = "s15";
+ break;
+ case ":-X":
+ case ":-x":
+ strSml = "s16";
+ break;
+ default:
+ strSml = "";
+ break;
+ }
+
+ try {
+ var editor = GetCurrentEditor();
+ var extElement = editor.createElementWithDefaults("span");
+ extElement.setAttribute("class", "moz-smiley-" + strSml);
+
+ var intElement = editor.createElementWithDefaults("span");
+ if (!intElement) {
+ return;
+ }
+
+ var txtElement = editor.document.createTextNode(smileyCode);
+ if (!txtElement) {
+ return;
+ }
+
+ intElement.appendChild(txtElement);
+ extElement.appendChild(intElement);
+
+ editor.insertElementAtSelection(extElement, true);
+ window.content.focus();
+ } catch (e) {
+ dump("Exception occurred in smiley InsertElementAtSelection\n");
+ }
+ },
+ // This is now deprecated in favor of "doCommandParams"
+ doCommand(aCommand) {},
+};
+
+function doAdvancedProperties(element) {
+ if (element) {
+ window.openDialog(
+ "chrome://editor/content/EdAdvancedEdit.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable=yes",
+ "",
+ element
+ );
+ }
+}
+
+var nsAdvancedPropertiesCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // Launch AdvancedEdit dialog for the selected element
+ try {
+ var element = GetCurrentEditor().getSelectedElement("");
+ doAdvancedProperties(element);
+ } catch (e) {}
+ },
+};
+
+var nsColorPropertiesCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ window.openDialog(
+ "chrome://editor/content/EdColorProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ ""
+ );
+ UpdateDefaultColors();
+ },
+};
+
+var nsIncreaseFontCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ if (!(IsDocumentEditable() && IsEditingRenderedHTML())) {
+ return false;
+ }
+ var setIndex = getFontSizeIndex();
+ return setIndex >= 0 && setIndex < 5;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var setIndex = getFontSizeIndex();
+ if (setIndex < 0 || setIndex >= 5) {
+ return;
+ }
+ var sizes = ["x-small", "small", "medium", "large", "x-large", "xx-large"];
+ EditorSetFontSize(sizes[setIndex + 1]);
+ },
+};
+
+var nsDecreaseFontCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ if (!(IsDocumentEditable() && IsEditingRenderedHTML())) {
+ return false;
+ }
+ var setIndex = getFontSizeIndex();
+ return setIndex > 0;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var setIndex = getFontSizeIndex();
+ if (setIndex <= 0) {
+ return;
+ }
+ var sizes = ["x-small", "small", "medium", "large", "x-large", "xx-large"];
+ EditorSetFontSize(sizes[setIndex - 1]);
+ },
+};
+
+var nsRemoveNamedAnchorsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ // We could see if there's any link in selection, but it doesn't seem worth the work!
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ EditorRemoveTextProperty("name", "");
+ window.content.focus();
+ },
+};
+
+var nsEditLinkCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ // Not really used -- this command is only in context menu, and we do enabling there
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ var element = GetCurrentEditor().getSelectedElement("href");
+ if (element) {
+ editPage(element.href);
+ }
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsNormalModeCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsHTMLEditor() && IsDocumentEditable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ SetEditMode(kDisplayModeNormal);
+ },
+};
+
+var nsAllTagsModeCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsHTMLEditor();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ SetEditMode(kDisplayModeAllTags);
+ },
+};
+
+var nsHTMLSourceModeCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsHTMLEditor();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ SetEditMode(kDisplayModeSource);
+ },
+};
+
+var nsPreviewModeCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsHTMLEditor();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ SetEditMode(kDisplayModePreview);
+ },
+};
+
+var nsInsertOrEditTableCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ if (IsInTableCell()) {
+ EditorTableCellProperties();
+ } else {
+ EditorInsertOrEditTable(true);
+ }
+ },
+};
+
+var nsEditTableCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ EditorInsertOrEditTable(false);
+ },
+};
+
+var nsSelectTableCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().selectTable();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsSelectTableRowCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().selectTableRow();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsSelectTableColumnCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().selectTableColumn();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsSelectTableCellCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().selectTableCell();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsSelectAllTableCellsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().selectAllTableCells();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsInsertTableCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsDocumentEditable() && IsEditingRenderedHTML();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ EditorInsertTable();
+ },
+};
+
+var nsInsertTableRowAboveCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().insertTableRow(1, false);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsInsertTableRowBelowCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().insertTableRow(1, true);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsInsertTableColumnBeforeCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().insertTableColumn(1, false);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsInsertTableColumnAfterCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().insertTableColumn(1, true);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsInsertTableCellBeforeCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().insertTableCell(1, false);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsInsertTableCellAfterCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().insertTableCell(1, true);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsDeleteTableCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().deleteTable();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsDeleteTableRowCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var rows = GetNumberOfContiguousSelectedRows();
+ // Delete at least one row
+ if (rows == 0) {
+ rows = 1;
+ }
+
+ try {
+ var editor = GetCurrentTableEditor();
+ editor.beginTransaction();
+
+ // Loop to delete all blocks of contiguous, selected rows
+ while (rows) {
+ editor.deleteTableRow(rows);
+ rows = GetNumberOfContiguousSelectedRows();
+ }
+ } finally {
+ editor.endTransaction();
+ }
+ window.content.focus();
+ },
+};
+
+var nsDeleteTableColumnCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ var columns = GetNumberOfContiguousSelectedColumns();
+ // Delete at least one column
+ if (columns == 0) {
+ columns = 1;
+ }
+
+ try {
+ var editor = GetCurrentTableEditor();
+ editor.beginTransaction();
+
+ // Loop to delete all blocks of contiguous, selected columns
+ while (columns) {
+ editor.deleteTableColumn(columns);
+ columns = GetNumberOfContiguousSelectedColumns();
+ }
+ } finally {
+ editor.endTransaction();
+ }
+ window.content.focus();
+ },
+};
+
+var nsDeleteTableCellCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().deleteTableCell(1);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsDeleteTableCellContentsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTableCell();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().deleteTableCellContents();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsNormalizeTableCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // Use nullptr to let editor find table enclosing current selection
+ try {
+ GetCurrentTableEditor().normalizeTable(null);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsJoinTableCellsCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ if (IsDocumentEditable() && IsEditingRenderedHTML()) {
+ try {
+ var editor = GetCurrentTableEditor();
+ var tagNameObj = { value: "" };
+ var countObj = { value: 0 };
+ var cell = editor.getSelectedOrParentTableElement(tagNameObj, countObj);
+
+ // We need a cell and either > 1 selected cell or a cell to the right
+ // (this cell may originate in a row spanned from above current row)
+ // Note that editor returns "td" for "th" also.
+ // (this is a pain! Editor and gecko use lowercase tagNames, JS uses uppercase!)
+ if (cell && tagNameObj.value == "td") {
+ // Selected cells
+ if (countObj.value > 1) {
+ return true;
+ }
+
+ var colSpan = cell.getAttribute("colspan");
+
+ // getAttribute returns string, we need number
+ // no attribute means colspan = 1
+ if (!colSpan) {
+ colSpan = Number(1);
+ } else {
+ colSpan = Number(colSpan);
+ }
+
+ var rowObj = { value: 0 };
+ var colObj = { value: 0 };
+ editor.getCellIndexes(cell, rowObj, colObj);
+
+ // Test if cell exists to the right of current cell
+ // (cells with 0 span should never have cells to the right
+ // if there is, user can select the 2 cells to join them)
+ return (
+ colSpan &&
+ editor.getCellAt(null, rowObj.value, colObj.value + colSpan)
+ );
+ }
+ } catch (e) {}
+ }
+ return false;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // Param: Don't merge non-contiguous cells
+ try {
+ GetCurrentTableEditor().joinTableCells(false);
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsSplitTableCellCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ if (IsDocumentEditable() && IsEditingRenderedHTML()) {
+ var tagNameObj = { value: "" };
+ var countObj = { value: 0 };
+ var cell;
+ try {
+ cell = GetCurrentTableEditor().getSelectedOrParentTableElement(
+ tagNameObj,
+ countObj
+ );
+ } catch (e) {}
+
+ // We need a cell parent and there's just 1 selected cell
+ // or selection is entirely inside 1 cell
+ if (
+ cell &&
+ tagNameObj.value == "td" &&
+ countObj.value <= 1 &&
+ IsSelectionInOneCell()
+ ) {
+ var colSpan = cell.getAttribute("colspan");
+ var rowSpan = cell.getAttribute("rowspan");
+ if (!colSpan) {
+ colSpan = 1;
+ }
+ if (!rowSpan) {
+ rowSpan = 1;
+ }
+ return colSpan > 1 || rowSpan > 1 || colSpan == 0 || rowSpan == 0;
+ }
+ }
+ return false;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ try {
+ GetCurrentTableEditor().splitTableCell();
+ } catch (e) {}
+ window.content.focus();
+ },
+};
+
+var nsTableOrCellColorCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return IsInTable();
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ EditorSelectColor("TableOrCell");
+ },
+};
+
+var nsPreferencesCommand = {
+ isCommandEnabled(aCommand, dummy) {
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ goPreferences("composer_pane");
+ },
+};
+
+var nsFinishHTMLSource = {
+ isCommandEnabled(aCommand, dummy) {
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // In editor.js
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ },
+};
+
+var nsCancelHTMLSource = {
+ isCommandEnabled(aCommand, dummy) {
+ return true;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ // In editor.js
+ CancelHTMLSource();
+ },
+};
+
+var nsConvertToTable = {
+ isCommandEnabled(aCommand, dummy) {
+ if (IsDocumentEditable() && IsEditingRenderedHTML()) {
+ var selection;
+ try {
+ selection = GetCurrentEditor().selection;
+ } catch (e) {}
+
+ if (selection && !selection.isCollapsed) {
+ // Don't allow if table or cell is the selection
+ var element;
+ try {
+ element = GetCurrentEditor().getSelectedElement("");
+ } catch (e) {}
+ if (element) {
+ var name = element.nodeName.toLowerCase();
+ if (
+ name == "td" ||
+ name == "th" ||
+ name == "caption" ||
+ name == "table"
+ ) {
+ return false;
+ }
+ }
+
+ // Selection start and end must be in the same cell
+ // in same cell or both are NOT in a cell
+ if (
+ GetParentTableCell(selection.focusNode) !=
+ GetParentTableCell(selection.anchorNode)
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getCommandStateParams(aCommand, aParams, aRefCon) {},
+ doCommandParams(aCommand, aParams, aRefCon) {},
+
+ doCommand(aCommand) {
+ if (this.isCommandEnabled()) {
+ window.openDialog(
+ "chrome://editor/content/EdConvertToTable.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal"
+ );
+ }
+ },
+};
diff --git a/comm/suite/editor/base/content/EditorAllTags.css b/comm/suite/editor/base/content/EditorAllTags.css
new file mode 100644
index 0000000000..656dffcdee
--- /dev/null
+++ b/comm/suite/editor/base/content/EditorAllTags.css
@@ -0,0 +1,802 @@
+/* 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/. */
+
+/* Styles to alter look of things in the Editor content window
+ * for the "All Tags Edit Mode" Every HTML tag shows up as an icon.
+*/
+
+/* For "userdefined" or "unknown" tags
+ (Note that "_" must be escaped)
+*/
+
+*:not(a):not(abbr):not(acronym):not(address):not(applet):not(area):not(b):not(base):not(basefont):not(bdo):not(bgsound):not(big):not(blink):not(blockquote):not(body):not(br):not(button):not(canvas):not(caption):not(center):not(cite):not(code):not(col):not(colgroup):not(dd):not(del):not(dfn):not(dir):not(div):not(dl):not(dt):not(em):not(embed):not(fieldset):not(font):not(form):not(frame):not(frameset):not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(head):not(hr):not(html):not(i):not(iframe):not(image):not(img):not(input):not(ins):not(isindex):not(kbd):not(keygen):not(label):not(legend):not(li):not(link):not(listing):not(map):not(marquee):not(menu):not(meta):not(multicol):not(nobr):not(noembed):not(noframes):not(noscript):not(object):not(ol):not(optgroup):not(option):not(p):not(param):not(plaintext):not(pre):not(q):not(s):not(samp):not(script):not(select):not(server):not(small):not(sound):not(spacer):not(span):not(strike):not(strong):not(style):not(sub):not(sup):not(table):not(tbody):not(td):not(textarea):not(tfoot):not(th):not(thead):not(title):not(tr):not(tt):not(u):not(ul):not(var):not(wbr):not(xmp) {
+ display: inline;
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 16px;
+ background-image: url(chrome://editor/content/images/tag-userdefined.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+a:not([\_moz_anonclass]) {
+ min-height: 16px; margin-left: 2px; margin-top: 2px;
+ padding-left: 20px;
+ background-image: url(chrome://editor/content/images/tag-a.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+abbr {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 35px;
+ background-image: url(chrome://editor/content/images/tag-abr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+
+acronym {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 57px;
+ background-image: url(chrome://editor/content/images/tag-acr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+address {
+ min-height: 44px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-adr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+applet {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 47px;
+ background-image: url(chrome://editor/content/images/tag-app.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+area {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-ara.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+b {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 20px;
+ background-image: url(chrome://editor/content/images/tag-b.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+basefont {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 57px;
+ background-image: url(chrome://editor/content/images/tag-bsf.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+bdo {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-bdo.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+big {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-big.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+blockquote {
+ min-height: 44px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-blq.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+body {
+ min-height: 36px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-body.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+br {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-br.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+button {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 57px;
+ background-image: url(chrome://editor/content/images/tag-btn.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+caption {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 55px;
+ background-image: url(chrome://editor/content/images/tag-cpt.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+center {
+ min-height: 44px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-ctr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+cite {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-cit.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+code {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-cod.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+col {
+ min-height: 35px; margin-left: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-col.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+colgroup {
+ min-height: 35px; margin-left: 2px;
+ padding-left: 51px;
+ background-image: url(chrome://editor/content/images/tag-clg.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+dd {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-dd.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+del {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-del.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+dfn {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-dfn.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+dir {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-dir.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+div {
+ min-height: 24px; margin-top: 2px;
+ /* TEMPORARY TO COMPENSATE FOR BUG */
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-div.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+input div {
+ min-height: 0px; margin-left: 0px; margin-top: 0px;
+ padding-left: 0px;
+ background-image: none;
+}
+
+dl {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-dl.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+dt {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-dt.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+em {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-em.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+fieldset {
+ min-height: 44px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-fld.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+font {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-fnt.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+form {
+ min-height: 36px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-for.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+frame {
+ min-height: 40px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-frm.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+frameset {
+ min-height: 44px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-fst.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+h1 {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-h1.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+h2 {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-h2.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+h3 {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-h3.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+h4 {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-h4.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+h5 {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-h5.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+h6 {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-h6.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+hr {
+ min-height: 20px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-hr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+i {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 20px;
+ background-image: url(chrome://editor/content/images/tag-i.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+iframe {
+ min-height: 35px; margin-left: 2px;
+ padding-left: 47px;
+ background-image: url(chrome://editor/content/images/tag-ifr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+img:not([\_moz_anonclass]) {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-img.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+input {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-inp.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+ins {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-ins.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+isindex {
+ min-height: 40px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-isx.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+kbd {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-kbd.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+label {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-lbl.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+legend {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 49px;
+ background-image: url(chrome://editor/content/images/tag-lgn.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+li {
+ min-height: 35px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-li.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+listing {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 57px;
+ background-image: url(chrome://editor/content/images/tag-lst.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+map {
+ min-height: 35px; margin-left: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-map.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+menu {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-men.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+nobr {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-nbr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+noframes {
+ min-height: 44px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-nfr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+noscript {
+ min-height: 44px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-nsc.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+object {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 49px;
+ background-image: url(chrome://editor/content/images/tag-obj.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+ol {
+ min-height: 38px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-ol.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+optgroup {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 51px;
+ background-image: url(chrome://editor/content/images/tag-opg.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+option {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 47px;
+ background-image: url(chrome://editor/content/images/tag-opt.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+p {
+ min-height: 38px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-p.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+param {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 43px;
+ background-image: url(chrome://editor/content/images/tag-prm.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+plaintext {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 57px;
+ background-image: url(chrome://editor/content/images/tag-pln.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+pre {
+ min-height: 24px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-pre.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+q {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 20px;
+ background-image: url(chrome://editor/content/images/tag-q.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+s {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 20px;
+ background-image: url(chrome://editor/content/images/tag-s.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+samp {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-smp.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+script {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 45px;
+ background-image: url(chrome://editor/content/images/tag-scr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+select {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 47px;
+ background-image: url(chrome://editor/content/images/tag-slc.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+small {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 41px;
+ background-image: url(chrome://editor/content/images/tag-sml.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+span:not([\_moz_anonclass]) {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ /* TEMPORARY TO COMPENSATE FOR BUG */
+ padding-left: 39px;
+ background-image: url(chrome://editor/content/images/tag-spn.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+strike {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 45px;
+ background-image: url(chrome://editor/content/images/tag-stk.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+strong {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 51px;
+ background-image: url(chrome://editor/content/images/tag-stn.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+sub {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-sub.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+sup {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-sup.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+/* The background image technique is not working for
+ some table elements. Trying the "before" strategy
+*/
+
+table {
+ min-height: 40px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-tbl.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+tbody {
+ min-height: 42px; margin-left: 2px; margin-top: 1px;
+ padding-left: 17px;
+ content: url(chrome://editor/content/images/tag-tbd.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+td {
+ min-height: 22px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-td.png);
+
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+textarea {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 59px;
+ background-image: url(chrome://editor/content/images/tag-txt.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+tfoot {
+ min-height: 42px; margin-left: 2px; margin-top: 1px;
+ padding-left: 17px;
+ content: url(chrome://editor/content/images/tag-tft.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+th {
+ min-height: 22px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-th.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+thead {
+ min-height: 42px; margin-left: 2px; margin-top: 1px;
+ padding-left: 17px;
+ content: url(chrome://editor/content/images/tag-thd.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+tr {
+ min-height: 22px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-tr.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+tt {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-tt.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+u {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 23px;
+ background-image: url(chrome://editor/content/images/tag-u.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+ul {
+ min-height: 20px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-ul.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+var {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-var.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+xmp {
+ min-height: 35px; margin-left: 2px; margin-top: 2px;
+ padding-left: 31px;
+ background-image: url(chrome://editor/content/images/tag-xmp.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+
+/* These are tags that we DON'T want to show icons for.
+ We have images for them in case we want to utilize them
+ for some other purpose than the "All Tags" editor mode
+
+html {
+ min-height: 36px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-html.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+head {
+ min-height: 36px; margin-left: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-hed.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+These are tags that are ONLY allowed as children of HEAD:
+
+title {
+ min-height: 40px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-ttl.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+base {
+ min-height: 36px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-bas.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+style {
+ min-height: 40px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-stl.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+meta {
+ min-height: 36px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-met.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+link {
+ min-height: 30px; margin-left: 2px; margin-top: 2px;
+ padding-left: 17px;
+ background-image: url(chrome://editor/content/images/tag-lnk.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+*/
diff --git a/comm/suite/editor/base/content/EditorContent.css b/comm/suite/editor/base/content/EditorContent.css
new file mode 100644
index 0000000000..fee8af21de
--- /dev/null
+++ b/comm/suite/editor/base/content/EditorContent.css
@@ -0,0 +1,62 @@
+/* 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/. */
+
+/* Styles to alter look of things in the Editor content window
+ * for the "Normal Edit Mode" These settings will be removed
+ * when we display in completely WYSIWYG "Edit Preview" mode
+ * Anything that should never change, like cursors, should be
+ * place in EditorOverride.css, instead of here.
+*/
+
+@import url(chrome://communicator/skin/smileys.css);
+
+a[name] {
+ min-height: 17px; margin-left: 2px; margin-top: 2px;
+ padding-left: 20px;
+ background-image: url(chrome://editor/content/images/tag-anchor.png);
+ background-repeat: no-repeat;
+ background-position: top left;
+}
+
+/* Force border display for empty cells
+ and tables with 0 border
+*/
+table {
+ empty-cells: show;
+}
+
+/* give a red dotted border to tables and cells with no border
+ otherwise they are invisible
+*/
+table[empty-cells],
+ table[border="0"],
+ /* next two selectors on line below for the case where tbody is omitted */
+ table[border="0"] > tr > td, table[border="0"] > tr > th,
+ table[border="0"] > thead > tr > td, table[border="0"] > tbody > tr > td, table[border="0"] > tfoot > tr > td,
+ table[border="0"] > thead > tr > th, table[border="0"] > tbody > tr > th, table[border="0"] > tfoot > tr > th,
+ table:not([border]),
+ /* next two selectors on line below for the case where tbody is omitted */
+ table:not([border]) > tr > td, table:not([border]) > tr > th,
+ table:not([border]) > thead > tr > td, table:not([border]) > tbody > tr > td, table:not([border]) > tfoot > tr > td,
+ table:not([border]) > thead > tr > th, table:not([border]) > tbody > tr > th, table:not([border]) > tfoot > tr > th
+{
+ border: 1px dotted red;
+}
+
+/* give a green dashed border to forms otherwise they are invisible
+*/
+form
+{
+ border: 2px dashed green;
+}
+/* give a green dotted border to labels otherwise they are invisible
+*/
+label
+{
+ border: 1px dotted green;
+}
+
+img {
+ -moz-force-broken-image-icon: 1;
+}
diff --git a/comm/suite/editor/base/content/EditorContextMenu.js b/comm/suite/editor/base/content/EditorContextMenu.js
new file mode 100644
index 0000000000..93dedfc0b1
--- /dev/null
+++ b/comm/suite/editor/base/content/EditorContextMenu.js
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", function() {
+ let tmp = {};
+ ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", tmp);
+ return new tmp.InlineSpellChecker();
+});
+
+// Overrides the main contentAreaContext onpopupshowing so needs to do
+// everything that does plus call Composer specific code.
+function editorContextPopupShowing(aNode)
+{
+ gContextMenu = new nsContextMenu(aNode);
+ if (gContextMenu.shouldDisplay)
+ {
+ var showExtra = top.document.commandDispatcher.focusedWindow == content;
+ gContextMenu.initEditorItems(showExtra);
+ return true;
+ }
+ return false;
+}
+
+// Extends the main nsContextMenu for Composer.
+nsContextMenu.prototype.initEditorItems = function (aShow)
+{
+ var isInLink = false;
+ var objectName;
+ var inSourceMode = IsInHTMLSourceMode();
+ var showSpell = !inSourceMode && !IsInPreviewMode() &&
+ InlineSpellCheckerUI.canSpellCheck;
+ this.showItem("spell-check-enabled", showSpell);
+ this.showItem("spell-separator", showSpell);
+
+ aShow = aShow && !inSourceMode;
+ this.hideDisabledItem("menu_pasteNoFormatting_cm", aShow);
+
+ // Only do this stuff when not in source mode or sidebar.
+ if (aShow)
+ {
+ // Setup object property command element.
+ objectName = InitObjectPropertiesMenuitem();
+ isInLink = objectName == "href";
+
+ InitRemoveStylesMenuitems("removeStylesMenuitem_cm",
+ "removeLinksMenuitem_cm",
+ "removeNamedAnchorsMenuitem_cm");
+
+ // Set appropriate text for join cells command.
+ InitJoinCellMenuitem("joinTableCells_cm");
+
+ // Update enable states for all table commands.
+ goUpdateTableMenuItems(document.getElementById("composerTableMenuItems"));
+
+ this.hideDisabledItem("context-undo", true);
+ this.hideDisabledItem("context-redo", true);
+ this.hideDisabledItem("context-cut", true);
+ this.hideDisabledItem("context-copy", true);
+ this.hideDisabledItem("context-paste", true);
+ this.hideDisabledItem("context-delete", true);
+
+ this.showItem("context-sep-undo",
+ this.shouldShowSeparator("context-sep-undo"));
+ this.showItem("context-sep-paste",
+ this.shouldShowSeparator("context-sep-paste"));
+ }
+
+ this.hideDisabledItem("objectProperties_cm", aShow);
+
+ // Show "Create Link" if not in a link and not in source mode or sidebar.
+ this.showItem("createLink_cm", aShow && !isInLink);
+
+ // Show "Edit link in new Composer" if in a link and
+ // not in source mode or sidebar.
+ this.showItem("editLink_cm", aShow && isInLink);
+
+ this.hideDisabledItem("removeStylesMenuitem_cm", aShow);
+ this.hideDisabledItem("removeLinksMenuitem_cm", aShow);
+ this.hideDisabledItem("removeNamedAnchorsMenuitem_cm", aShow);
+
+ this.hideDisabledItem("joinTableCells_cm", aShow);
+ this.hideDisabledItem("splitTableCell_cm", aShow);
+ this.hideDisabledItem("tableOrCellColor_cm", aShow);
+
+ var inCell = aShow && IsInTableCell();
+ // Remove table submenus if not in table.
+ this.showItem("tableInsertMenu_cm", inCell);
+ this.showItem("tableSelectMenu_cm", inCell);
+ this.showItem("tableDeleteMenu_cm", inCell);
+
+ this.showItem("context-sep-selectall", aShow);
+ this.showItem("context-sep-properites", aShow && !!objectName);
+ this.showItem("frame-sep", aShow && IsInTable());
+};
+
+nsContextMenu.prototype.hideDisabledItem = function(aId, aShow)
+{
+ this.showItem(aId, aShow && IsItemOrCommandEnabled(aId));
+};
+
+function IsItemOrCommandEnabled(aId)
+{
+ var item = document.getElementById(aId);
+ if (!item)
+ return false;
+
+ var command = item.getAttribute("command");
+ if (command) {
+ // If possible, query the command controller directly
+ var controller = document.commandDispatcher
+ .getControllerForCommand(command);
+ if (controller)
+ return controller.isCommandEnabled(command);
+ }
+
+ // Fall back on the inefficient observed disabled attribute
+ return item.getAttribute("disabled") != "true";
+}
diff --git a/comm/suite/editor/base/content/EditorContextMenuOverlay.xhtml b/comm/suite/editor/base/content/EditorContextMenuOverlay.xhtml
new file mode 100644
index 0000000000..36c23f0c5e
--- /dev/null
+++ b/comm/suite/editor/base/content/EditorContextMenuOverlay.xhtml
@@ -0,0 +1,171 @@
+<?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 SYSTEM "chrome://editor/locale/editorOverlay.dtd">
+
+<overlay id="ComposerContextMenuOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+<script src="chrome://editor/content/EditorContextMenu.js"/>
+<script src="chrome://editor/content/StructBarContextMenu.js"/>
+
+ <menupopup id="contentAreaContextMenu"
+ onpopupshowing="return event.target != this ||
+ editorContextPopupShowing(this);">
+ <menuitem id="menu_pasteNoFormatting_cm"
+ insertafter="context-paste"
+ command="cmd_pasteNoFormatting"/>
+
+ <!-- label and accesskey set at runtime from strings -->
+ <menuitem id="removeStylesMenuitem_cm"
+ insertafter="context-sep-selectall"
+ command="cmd_removeStyles"/>
+ <menuitem id="createLink_cm"
+ insertafter="removeStylesMenuitem_cm"
+ label="&createLinkCmd.label;"
+ accesskey="&createLinkCmd.accesskey;"
+ command="cmd_link"/>
+ <!-- label and accesskey set at runtime from strings -->
+ <menuitem id="removeLinksMenuitem_cm"
+ insertafter="createLink_cm"
+ command="cmd_removeLinks"/>
+ <menuitem id="removeNamedAnchorsMenuitem_cm"
+ insertafter="removeLinksMenuitem_cm"
+ label="&formatRemoveNamedAnchors.label;"
+ accesskey="&formatRemoveNamedAnchors.accesskey;"
+ command="cmd_removeNamedAnchors"/>
+
+ <!-- label and accesskey are set in InitObjectProperties -->
+ <menuitem id="objectProperties_cm"
+ insertafter="context-sep-properties"
+ command="cmd_objectProperties"/>
+ <menuitem id="editLink_cm"
+ insertafter="objectProperties_cm"
+ label="&editLinkCmd.label;"
+ accesskey="&editLinkCmd.accesskey;"
+ command="cmd_editLink"/>
+
+ <!-- Can't get submenus to load from a shared overlay -->
+ <menu id="tableInsertMenu_cm"
+ insertafter="frame-sep"
+ label="&tableInsertMenu2.label;"
+ accesskey="&tableInsertMenu2.accesskey;">
+ <menupopup>
+ <menuitem label="&insertTableCmd.label;"
+ accesskey="&insertTableCmd.accesskey;"
+ command="cmd_InsertTable"/>
+ <menuseparator/>
+ <menuitem label="&tableRowAbove.label;"
+ accesskey="&tableRowAbove.accesskey;"
+ command="cmd_InsertRowAbove"/>
+ <menuitem label="&tableRowBelow.label;"
+ accesskey="&tableRowBelow.accesskey;"
+ command="cmd_InsertRowBelow"/>
+ <menuseparator/>
+ <menuitem label="&tableColumnBefore.label;"
+ accesskey="&tableColumnBefore.accesskey;"
+ command="cmd_InsertColumnBefore"/>
+ <menuitem label="&tableColumnAfter.label;"
+ accesskey="&tableColumnAfter.accesskey;"
+ command="cmd_InsertColumnAfter"/>
+ <menuseparator/>
+ <menuitem label="&tableCellBefore.label;"
+ accesskey="&tableCellBefore.accesskey;"
+ command="cmd_InsertCellBefore"/>
+ <menuitem label="&tableCellAfter.label;"
+ accesskey="&tableCellAfter.accesskey;"
+ command="cmd_InsertCellAfter"/>
+ </menupopup>
+ </menu>
+ <menu id="tableSelectMenu_cm"
+ insertafter="tableInsertMenu_cm"
+ label="&tableSelectMenu2.label;"
+ accesskey="&tableSelectMenu2.accesskey;">
+ <menupopup>
+ <menuitem id="menu_SelectTable_cm"
+ label="&tableTable.label;"
+ accesskey="&tableTable.accesskey;"
+ command="cmd_SelectTable"/>
+ <menuitem id="menu_SelectRow_cm"
+ label="&tableRow.label;"
+ accesskey="&tableRow.accesskey;"
+ command="cmd_SelectRow"/>
+ <menuitem id="menu_SelectColumn_cm"
+ label="&tableColumn.label;"
+ accesskey="&tableColumn.accesskey;"
+ command="cmd_SelectColumn"/>
+ <menuitem id="menu_SelectCell_cm"
+ label="&tableCell.label;"
+ accesskey="&tableCell.accesskey;"
+ command="cmd_SelectCell"/>
+ <menuitem id="menu_SelectAllCells_cm"
+ label="&tableAllCells.label;"
+ accesskey="&tableAllCells.accesskey;"
+ command="cmd_SelectAllCells"/>
+ </menupopup>
+ </menu>
+ <menu id="tableDeleteMenu_cm"
+ insertafter="tableSelectMenu_cm"
+ label="&tableDeleteMenu2.label;"
+ accesskey="&tableDeleteMenu2.accesskey;">
+ <menupopup>
+ <menuitem id="menu_DeleteTable_cm"
+ label="&tableTable.label;"
+ accesskey="&tableTable.accesskey;"
+ command="cmd_DeleteTable"/>
+ <menuitem id="menu_DeleteRow_cm"
+ label="&tableRows.label;"
+ accesskey="&tableRow.accesskey;"
+ command="cmd_DeleteRow"/>
+ <menuitem id="menu_DeleteColumn_cm"
+ label="&tableColumns.label;"
+ accesskey="&tableColumn.accesskey;"
+ command="cmd_DeleteColumn"/>
+ <menuitem id="menu_DeleteCell_cm"
+ label="&tableCells.label;"
+ accesskey="&tableCell.accesskey;"
+ command="cmd_DeleteCell"/>
+ <menuitem id="menu_DeleteCellContents_cm"
+ label="&tableCellContents.label;"
+ accesskey="&tableCellContents.accesskey;"
+ command="cmd_DeleteCellContents"/>
+ </menupopup>
+ </menu>
+ <!-- menu label is set in InitTableMenu -->
+ <menuitem id="joinTableCells_cm"
+ insertafter="tableDeleteMenu_cm"
+ label="&tableJoinCells.label;"
+ accesskey="&tableJoinCells.accesskey;"
+ command="cmd_JoinTableCells"/>
+ <menuitem id="splitTableCell_cm"
+ insertafter="joinTableCells_cm"
+ label="&tableSplitCell.label;"
+ accesskey="&tableSplitCell.accesskey;"
+ command="cmd_SplitTableCell"/>
+ <menuitem id="tableOrCellColor_cm"
+ insertafter="splitTableCell_cm"
+ label="&tableOrCellColor.label;"
+ accesskey="&tableOrCellColor.accesskey;"
+ command="cmd_TableOrCellColor"/>
+ </menupopup>
+
+ <menupopup id="structToolbarContext">
+ <menuitem id="structSelect" label="&structSelect.label;"
+ accesskey="&structSelect.accesskey;"
+ oncommand="StructSelectTag()"/>
+ <menuseparator/>
+ <menuitem id="structRemoveTag" label="&structRemoveTag.label;"
+ accesskey="&structRemoveTag.accesskey;"
+ oncommand="StructRemoveTag()"/>
+ <menuitem id="structChangeTag" label="&structChangeTag.label;"
+ accesskey="&structChangeTag.accesskey;"
+ oncommand="StructChangeTag()"/>
+ <menuseparator/>
+ <menuitem id="advancedPropsTag" label="&advancedPropertiesCmd.label;"
+ accesskey="&advancedPropertiesCmd.accesskey;"
+ oncommand="OpenAdvancedProperties()"/>
+ </menupopup>
+
+</overlay>
diff --git a/comm/suite/editor/base/content/StructBarContextMenu.js b/comm/suite/editor/base/content/StructBarContextMenu.js
new file mode 100644
index 0000000000..19f5498355
--- /dev/null
+++ b/comm/suite/editor/base/content/StructBarContextMenu.js
@@ -0,0 +1,179 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gContextMenuNode;
+var gContextMenuFiringDocumentElement;
+
+function InitStructBarContextMenu(button, docElement)
+{
+ gContextMenuFiringDocumentElement = docElement;
+ gContextMenuNode = button;
+
+ var tag = docElement.nodeName.toLowerCase();
+
+ var structRemoveTag = document.getElementById("structRemoveTag");
+ var enableRemove;
+
+ switch (tag) {
+ case "body":
+ case "tbody":
+ case "thead":
+ case "tfoot":
+ case "col":
+ case "colgroup":
+ case "tr":
+ case "th":
+ case "td":
+ case "caption":
+ enableRemove = false;
+ break;
+ default:
+ enableRemove = true;
+ break;
+ }
+ SetElementEnabled(structRemoveTag, enableRemove);
+
+ var structChangeTag = document.getElementById("structChangeTag");
+ SetElementEnabled(structChangeTag, (tag != "body"));
+}
+
+function TableCellFilter(node)
+{
+ switch (node.nodeName.toLowerCase())
+ {
+ case "td":
+ case "th":
+ case "caption":
+ return NodeFilter.FILTER_ACCEPT;
+ break;
+ default:
+ return NodeFilter.FILTER_SKIP;
+ break;
+ }
+ return NodeFilter.FILTER_SKIP;
+}
+
+function StructRemoveTag()
+{
+ var editor = GetCurrentEditor();
+ if (!editor) return;
+
+ var element = gContextMenuFiringDocumentElement;
+ var offset = 0;
+ var childNodes = element.parentNode.childNodes;
+
+ while (childNodes[offset] != element) {
+ ++offset;
+ }
+
+ editor.beginTransaction();
+
+ try {
+
+ var tag = element.nodeName.toLowerCase();
+ if (tag != "table") {
+ MoveChildNodesAfterElement(editor, element, element, offset);
+ }
+ else {
+
+ var nodeIterator = document.createTreeWalker(element,
+ NodeFilter.SHOW_ELEMENT,
+ TableCellFilter,
+ true);
+ var node = nodeIterator.lastChild();
+ while (node) {
+ MoveChildNodesAfterElement(editor, node, element, offset);
+ node = nodeIterator.previousSibling();
+ }
+
+ }
+ editor.deleteNode(element);
+ }
+ catch (e) {};
+
+ editor.endTransaction();
+}
+
+function MoveChildNodesAfterElement(editor, element, targetElement, targetOffset)
+{
+ var childNodes = element.childNodes;
+ var childNodesLength = childNodes.length;
+ var i;
+ for (i = childNodesLength - 1; i >= 0; i--) {
+ var clone = childNodes.item(i).cloneNode(true);
+ editor.insertNode(clone, targetElement.parentNode, targetOffset + 1);
+ }
+}
+
+function StructChangeTag()
+{
+ var textbox = document.createXULElement("textbox");
+ textbox.setAttribute("value", gContextMenuNode.getAttribute("value"));
+ textbox.setAttribute("width", gContextMenuNode.getBoundingClientRect().width);
+ textbox.className = "struct-textbox";
+
+ gContextMenuNode.parentNode.replaceChild(textbox, gContextMenuNode);
+
+ textbox.addEventListener("keypress", OnKeyPress);
+ textbox.addEventListener("blur", ResetStructToolbar, true);
+
+ textbox.select();
+}
+
+function StructSelectTag()
+{
+ SelectFocusNodeAncestor(gContextMenuFiringDocumentElement);
+}
+
+function OpenAdvancedProperties()
+{
+ doAdvancedProperties(gContextMenuFiringDocumentElement);
+}
+
+function OnKeyPress(event)
+{
+ var editor = GetCurrentEditor();
+
+ var keyCode = event.keyCode;
+ if (keyCode == 13) {
+ var newTag = event.target.value;
+
+ var element = gContextMenuFiringDocumentElement;
+
+ var offset = 0;
+ var childNodes = element.parentNode.childNodes;
+ while (childNodes.item(offset) != element) {
+ offset++;
+ }
+
+ editor.beginTransaction();
+
+ try {
+ var newElt = editor.document.createXULElement(newTag);
+ if (newElt) {
+ childNodes = element.childNodes;
+ var childNodesLength = childNodes.length;
+ var i;
+ for (i = 0; i < childNodesLength; i++) {
+ var clone = childNodes.item(i).cloneNode(true);
+ newElt.appendChild(clone);
+ }
+ editor.insertNode(newElt, element.parentNode, offset+1);
+ editor.deleteNode(element);
+ editor.selectElement(newElt);
+
+ window.content.focus();
+ }
+ }
+ catch (e) {}
+
+ editor.endTransaction();
+
+ }
+ else if (keyCode == 27) {
+ // if the user hits Escape, we discard the changes
+ window.content.focus();
+ }
+}
diff --git a/comm/suite/editor/base/content/composerOverlay.xhtml b/comm/suite/editor/base/content/composerOverlay.xhtml
new file mode 100644
index 0000000000..1d36db50b1
--- /dev/null
+++ b/comm/suite/editor/base/content/composerOverlay.xhtml
@@ -0,0 +1,28 @@
+<?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 % editorDTD SYSTEM "chrome://editor/locale/editor.dtd">
+%editorDTD;
+]>
+
+<overlay id="composerOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <!-- Items in the File menu used only by Composer app -->
+ <menupopup id="menu_FilePopup">
+ <menuitem id="fileExportToText"
+ insertafter="sep_print"
+ command="cmd_exportToText"/>
+ <menuitem id="previewInBrowser"
+ label="&previewCmd.label;"
+ accesskey="&previewCmd.accesskey;"
+ insertafter="fileExportToText"
+ command="cmd_preview"/>
+ <!-- menuitem id="menu_SendPage" is merged here from mailEditorOverlay.xhtml,
+ where "position" is assumed to be just after 'previewInBrowser' -->
+ </menupopup>
+
+</overlay>
diff --git a/comm/suite/editor/base/content/editingOverlay.js b/comm/suite/editor/base/content/editingOverlay.js
new file mode 100644
index 0000000000..19b41d321b
--- /dev/null
+++ b/comm/suite/editor/base/content/editingOverlay.js
@@ -0,0 +1,387 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gUntitledString;
+
+function TextEditorOnLoad()
+{
+ var url = "about:blank";
+ // See if argument was passed.
+ if (window.arguments && window.arguments[0])
+ {
+ // Opened via window.openDialog with URL as argument.
+ url = window.arguments[0];
+ }
+ // Continue with normal startup.
+ EditorStartup(url);
+}
+
+function EditorOnLoad()
+{
+ var url = "about:blank";
+ var charset;
+ // See if argument was passed.
+ if (window.arguments)
+ {
+ if (window.arguments[0])
+ {
+ // Opened via window.openDialog with URL as argument.
+ url = window.arguments[0];
+ }
+
+ // get default character set if provided
+ if (window.arguments.length > 1 && window.arguments[1])
+ {
+ if (window.arguments[1].includes("charset="))
+ {
+ var arrayArgComponents = window.arguments[1].split("=");
+ if (arrayArgComponents)
+ charset = arrayArgComponents[1];
+ }
+ }
+ }
+
+ // XUL elements we use when switching from normal editor to edit source.
+ gContentWindowDeck = document.getElementById("ContentWindowDeck");
+ gFormatToolbar = document.getElementById("FormatToolbar");
+
+ // Continue with normal startup.
+ EditorStartup(url, charset);
+
+ // Hide Highlight button if we are in an HTML editor with CSS mode off
+ // and tell the editor if a CR in a paragraph creates a new paragraph.
+ var cmd = document.getElementById("cmd_highlight");
+ if (cmd) {
+ if (!Services.prefs.getBoolPref(kUseCssPref))
+ cmd.collapsed = true;
+ }
+
+ // Initialize our source text <editor>
+ try {
+ gSourceContentWindow = document.getElementById("content-source");
+ gSourceContentWindow.makeEditable("text", false);
+ gSourceTextEditor = gSourceContentWindow.getEditor(gSourceContentWindow.contentWindow);
+ gSourceTextEditor.enableUndo(false);
+ gSourceTextEditor.rootElement.style.fontFamily = "-moz-fixed";
+ gSourceTextEditor.rootElement.style.whiteSpace = "pre";
+ gSourceTextEditor.rootElement.style.margin = 0;
+ var controller = Cc["@mozilla.org/embedcomp/base-command-controller;1"]
+ .createInstance(Ci.nsIControllerContext);
+ controller.setCommandContext(gSourceContentWindow);
+ gSourceContentWindow.contentWindow.controllers.insertControllerAt(0, controller);
+ var commandTable = controller.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIControllerCommandTable);
+ commandTable.registerCommand("cmd_findReplace", nsFindReplaceCommand);
+ commandTable.registerCommand("cmd_find", nsFindCommand);
+ commandTable.registerCommand("cmd_findNext", nsFindAgainCommand);
+ commandTable.registerCommand("cmd_findPrev", nsFindAgainCommand);
+ } catch (e) {
+ dump("makeEditable failed: "+e+"\n");
+ }
+}
+
+function toggleAffectedChrome(aHide)
+{
+ // chrome to toggle includes:
+ // (*) menubar
+ // (*) toolbox
+ // (*) sidebar
+ // (*) statusbar
+
+ if (!gChromeState)
+ gChromeState = new Object;
+
+ var statusbar = document.getElementById("status-bar");
+
+ // sidebar states map as follows:
+ // hidden => hide/show nothing
+ // collapsed => hide/show only the splitter
+ // shown => hide/show the splitter and the box
+ if (aHide)
+ {
+ // going into print preview mode
+ gChromeState.sidebar = SidebarGetState();
+ SidebarSetState("hidden");
+
+ // deal with the Status Bar
+ gChromeState.statusbarWasHidden = statusbar.hidden;
+ statusbar.hidden = true;
+ }
+ else
+ {
+ // restoring normal mode (i.e., leaving print preview mode)
+ SidebarSetState(gChromeState.sidebar);
+
+ // restore the Status Bar
+ statusbar.hidden = gChromeState.statusbarWasHidden;
+ }
+
+ // if we are unhiding and sidebar used to be there rebuild it
+ if (!aHide && gChromeState.sidebar == "visible")
+ SidebarRebuild();
+
+ document.getElementById("EditorToolbox").hidden = aHide;
+ document.getElementById("appcontent").collapsed = aHide;
+}
+
+var PrintPreviewListener = {
+ getPrintPreviewBrowser: function () {
+ var browser = document.getElementById("ppBrowser");
+ if (!browser) {
+ browser = document.createXULElement("browser");
+ browser.setAttribute("id", "ppBrowser");
+ browser.setAttribute("flex", "1");
+ browser.setAttribute("disablehistory", "true");
+ browser.setAttribute("disablesecurity", "true");
+ browser.setAttribute("type", "content");
+ document.getElementById("sidebar-parent").
+ insertBefore(browser, document.getElementById("appcontent"));
+ }
+ return browser;
+ },
+ getSourceBrowser: function () {
+ return GetCurrentEditorElement();
+ },
+ getNavToolbox: function () {
+ return document.getElementById("EditorToolbox");
+ },
+ onEnter: function () {
+ toggleAffectedChrome(true);
+ },
+ onExit: function () {
+ document.getElementById("ppBrowser").collapsed = true;
+ toggleAffectedChrome(false);
+ }
+}
+
+function EditorStartup(aUrl, aCharset)
+{
+ gUntitledString = GetFormattedString("untitledTitle", GetNextUntitledValue());
+
+ var ds = GetCurrentEditorElement().docShell;
+ ds.useErrorPages = false;
+ var root = ds.QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem.QueryInterface(Ci.nsIDocShell);
+
+ root.QueryInterface(Ci.nsIDocShell).appType =
+ Ci.nsIDocShell.APP_TYPE_EDITOR;
+
+ // EditorSharedStartup also used by Message Composer.
+ EditorSharedStartup();
+
+ // Commands specific to the Composer Application window,
+ // (i.e., not embedded editors)
+ // such as file-related commands, HTML Source editing, Edit Modes...
+ SetupComposerWindowCommands();
+
+ gCSSPrefListener = new nsPrefListener(kUseCssPref);
+ gReturnInParagraphPrefListener = new nsPrefListener(kCRInParagraphsPref);
+ Services.obs.addObserver(EditorCanClose, "quit-application-requested");
+
+ root.charset = aCharset;
+
+ // Get url for editor content and load it. The editor gets instantiated by
+ // the editingSession when the URL has finished loading.
+ EditorLoadUrl(aUrl);
+
+ // Before and after callbacks for the customizeToolbar code.
+ var editorToolbox = getEditorToolbox();
+ editorToolbox.customizeInit = EditorToolboxCustomizeInit;
+ editorToolbox.customizeDone = EditorToolboxCustomizeDone;
+ editorToolbox.customizeChange = EditorToolboxCustomizeChange;
+}
+
+function EditorShutdown()
+{
+ Services.obs.removeObserver(EditorCanClose, "quit-application-requested");
+
+ gCSSPrefListener.shutdown();
+ gReturnInParagraphPrefListener.shutdown();
+
+ try
+ {
+ var commandManager = GetCurrentCommandManager();
+ commandManager.removeCommandObserver(gEditorDocumentObserver,
+ "obs_documentCreated");
+ commandManager.removeCommandObserver(gEditorDocumentObserver,
+ "obs_documentWillBeDestroyed");
+ commandManager.removeCommandObserver(gEditorDocumentObserver,
+ "obs_documentLocationChanged");
+ } catch (e) { dump (e); }
+}
+
+// --------------------------- File menu ---------------------------
+
+// Check for changes to document and allow saving before closing
+// This is hooked up to the OS's window close widget (e.g., "X" for Windows)
+async function EditorCanClose(aCancelQuit, aTopic, aData)
+{
+ if (aTopic == "quit-application-requested" &&
+ aCancelQuit instanceof Ci.nsISupportsPRBool &&
+ aCancelQuit.data)
+ return false;
+
+ // Returns FALSE only if user cancels save action
+
+ // "true" means allow "Don't Save" button
+ var canClose = await CheckAndSaveDocument("cmd_close", true);
+
+ // This is our only hook into closing via the "X" in the caption
+ // or "Quit" (or other paths?)
+ // so we must shift association to another
+ // editor or close any non-modal windows now
+ if (canClose && "InsertCharWindow" in window && window.InsertCharWindow)
+ SwitchInsertCharToAnotherEditorOrClose();
+
+ if (!canClose && aTopic == "quit-application-requested")
+ aCancelQuit.data = true;
+
+ return canClose;
+}
+
+function BuildRecentPagesMenu()
+{
+ var editor = GetCurrentEditor();
+ if (!editor)
+ return;
+
+ var popup = document.getElementById("menupopup_RecentFiles");
+ if (!popup || !editor.document)
+ return;
+
+ // Delete existing menu
+ while (popup.hasChildNodes())
+ popup.lastChild.remove();
+
+ // Current page is the "0" item in the list we save in prefs,
+ // but we don't include it in the menu.
+ var curUrl = StripPassword(GetDocumentUrl());
+ var historyCount = Services.prefs.getIntPref("editor.history.url_maximum", 10);
+
+ var menuIndex = 1;
+ for (var i = 0; i < historyCount; i++)
+ {
+ var url = Services.prefs.getStringPref("editor.history_url_" + i, "");
+
+ // Skip over current url
+ if (url && url != curUrl)
+ {
+ // Build the menu
+ var title = Services.prefs.getStringPref("editor.history_title_" + i, "");
+ var fileType = Services.prefs.getStringPref("editor.history_type_" + i, "");
+ AppendRecentMenuitem(popup, title, url, fileType, menuIndex);
+ menuIndex++;
+ }
+ }
+}
+
+function AppendRecentMenuitem(aPopup, aTitle, aUrl, aFileType, aIndex)
+{
+ if (!aPopup)
+ return;
+
+ var menuItem = document.createXULElement("menuitem");
+ if (!menuItem)
+ return;
+
+ var accessKey = aIndex <= 10 ? String(aIndex % 10) : " ";
+
+ // Show "title [url]" or just the URL.
+ var itemString = aTitle ? aTitle + " [" + aUrl + "]" : aUrl;
+
+ menuItem.setAttribute("label", accessKey + " " + itemString);
+ menuItem.setAttribute("crop", "center");
+ menuItem.setAttribute("tooltiptext", aUrl);
+ menuItem.setAttribute("value", aUrl);
+ menuItem.setAttribute("fileType", aFileType);
+ if (accessKey != " ")
+ menuItem.setAttribute("accesskey", accessKey);
+ aPopup.appendChild(menuItem);
+}
+
+function EditorInitFileMenu()
+{
+ // Disable "Save" menuitem when editing remote url. User should use "Save As"
+
+ var docUrl = GetDocumentUrl();
+ var scheme = GetScheme(docUrl);
+ if (scheme && scheme != "file")
+ SetElementEnabledById("menu_saveCmd", false);
+
+ // Enable recent pages submenu if there are any history entries in prefs.
+ var historyUrl = "";
+
+ if (Services.prefs.getIntPref("editor.history.url_maximum", 10))
+ {
+ historyUrl = Services.prefs.getStringPref("editor.history_url_0", "");
+
+ // See if there's more if current file is only entry in history list.
+ if (historyUrl && historyUrl == docUrl)
+ historyUrl = Services.prefs.getStringPref("editor.history_url_1", "");
+ }
+ SetElementEnabledById("menu_RecentFiles", historyUrl != "");
+}
+
+function EditorUpdateCharsetMenu(aMenuPopup)
+{
+ if (IsDocumentModified() && !IsDocumentEmpty())
+ {
+ for (var i = 0; i < aMenuPopup.childNodes.length; i++)
+ aMenuPopup.childNodes[i].setAttribute("disabled", "true");
+ }
+
+ UpdateCharsetMenu(content.document.characterSet, aMenuPopup);
+}
+
+// Zoom support.
+function getBrowser()
+{
+ return IsInHTMLSourceMode() ? gSourceContentWindow : GetCurrentEditorElement();
+}
+
+// override the site-specific zoom object in viewZoomOverlay.js
+var FullZoom = {
+ init: function() {},
+ reduce: function() { ZoomManager.reduce(); },
+ enlarge: function() { ZoomManager.enlarge(); },
+ zoom: function(aZoomValue) { ZoomManager.zoom = aZoomValue; },
+ reset: function() { ZoomManager.zoom = 1; },
+ setOther: function() { openZoomDialog(); }
+};
+
+function hideEditorUI(aHide) {
+ for (let id of ["EditModeToolbar", "content-source", "content-frame"]) {
+ let element = document.getElementById(id);
+ if (!element)
+ continue;
+
+ if (aHide) {
+ element.setAttribute("moz-collapsed", true);
+ } else {
+ element.removeAttribute("moz-collapsed");
+ }
+ }
+}
+
+function getEditorToolbox() {
+ return document.getElementById("EditorToolbox");
+}
+
+function EditorToolboxCustomizeInit() {
+ if (document.commandDispatcher.focusedWindow == content)
+ window.focus();
+ hideEditorUI(true);
+ toolboxCustomizeInit("main-menubar");
+}
+
+function EditorToolboxCustomizeDone(aToolboxChanged) {
+ toolboxCustomizeDone("main-menubar", getEditorToolbox(), aToolboxChanged);
+ hideEditorUI(false);
+ gContentWindow.focus();
+}
+
+function EditorToolboxCustomizeChange(aEvent) {
+ toolboxCustomizeChange(getEditorToolbox(), aEvent);
+}
diff --git a/comm/suite/editor/base/content/editingOverlay.xhtml b/comm/suite/editor/base/content/editingOverlay.xhtml
new file mode 100644
index 0000000000..21833857a6
--- /dev/null
+++ b/comm/suite/editor/base/content/editingOverlay.xhtml
@@ -0,0 +1,247 @@
+<?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/. -->
+
+<?xul-overlay href="chrome://communicator/content/viewZoomOverlay.xhtml"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://editor/locale/editingOverlay.dtd">
+
+<overlay id="editingOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <script src="chrome://editor/content/editingOverlay.js"/>
+
+ <stringbundleset>
+ <stringbundle id="bundle_viewZoom"/>
+ </stringbundleset>
+
+ <keyset id="editorKeys">
+ <key id="key_openRemoteEditor"
+ key="&openRemoteCmd.key;"
+ command="cmd_openRemote"
+ modifiers="accel,shift"/>
+ <key id="key_openEditor"
+ key="&openFileCmd.key;"
+ command="cmd_open"
+ modifiers="accel"/>
+ <key id="key_publish"
+ key="&publishCmd.key;"
+ command="cmd_publish"
+ modifiers="accel,shift"/>
+ <keyset id="viewZoomKeys"/>
+ </keyset>
+
+ <!-- commands updated when the editor gets created -->
+ <commandset id="commonEditorMenuItems">
+ <command id="cmd_open"
+ oncommand="goDoCommand('cmd_open');"/>
+ <command id="cmd_openRemote"
+ oncommand="goDoCommand('cmd_openRemote');"/>
+ </commandset>
+
+ <commandset id="composerSaveMenuItems"
+ commandupdater="true"
+ events="create, save"
+ oncommandupdate="goUpdateComposerMenuItems(this);">
+ <command id="cmd_save"
+ label="&saveCmd.label;"
+ oncommand="goDoCommand('cmd_save');"/>
+ <command id="cmd_saveAs"
+ oncommand="goDoCommand('cmd_saveAs');"/>
+ <command id="cmd_saveAndChangeEncoding"
+ oncommand="goDoCommand('cmd_saveAndChangeEncoding');"/>
+ <command id="cmd_publish"
+ label="&publishCmd.label;"
+ oncommand="goDoCommand('cmd_publish');"/>
+ <command id="cmd_publishAs"
+ oncommand="goDoCommand('cmd_publishAs');"/>
+ <command id="cmd_revert"
+ oncommand="goDoCommand('cmd_revert');"/>
+ </commandset>
+
+ <commandset id="composerEditMenuItems">
+ <command id="cmd_publishSettings"
+ oncommand="goDoCommand('cmd_publishSettings');"/>
+ </commandset>
+
+ <commandset id="composerMenuItems">
+ <command id="cmd_form"
+ oncommand="goDoCommand('cmd_form');"/>
+ <command id="cmd_inputtag"
+ oncommand="goDoCommand('cmd_inputtag');"/>
+ <command id="cmd_inputimage"
+ oncommand="goDoCommand('cmd_inputimage');"/>
+ <command id="cmd_textarea"
+ oncommand="goDoCommand('cmd_textarea');"/>
+ <command id="cmd_select"
+ oncommand="goDoCommand('cmd_select');"/>
+ <command id="cmd_button"
+ oncommand="goDoCommand('cmd_button');"/>
+ <command id="cmd_label"
+ oncommand="goDoCommand('cmd_label');"/>
+ <command id="cmd_fieldset"
+ oncommand="goDoCommand('cmd_fieldset');"/>
+ </commandset>
+
+ <commandset id="editorCommands">
+ <command id="cmd_CustomizeToolbars"
+ oncommand="goCustomizeToolbar(getEditorToolbox());"/>
+ <commandset id="viewZoomCommands"/>
+ </commandset>
+
+ <!-- File menu items -->
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup" onpopupshowing="EditorInitFileMenu();">
+ <menu id="menu_New" class="menu-iconic">
+ <menupopup id="menu_NewPopup">
+ <menuitem id="menu_newEditor"/>
+ <menuseparator id="sep_NewPopup"/>
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_openRemote"
+ label="&openRemoteCmd.label;"
+ accesskey="&openRemoteCmd.accesskey;"
+ key="key_openRemoteEditor"
+ command="cmd_openRemote"/>
+ <menuitem id="menu_openFile"
+ label="&openFileCmd.label;"
+ accesskey="&openFileCmd.accesskey;"
+ key="key_openEditor"
+ command="cmd_open"/>
+ <menu id="menu_RecentFiles"
+ label="&fileRecentMenu.label;"
+ accesskey="&fileRecentMenu.accesskey;"
+ onpopupshowing="BuildRecentPagesMenu();">
+ <menupopup id="menupopup_RecentFiles"
+ oncommand="editPage(event.target.getAttribute('value'),
+ event.target.getAttribute('fileType'));"/>
+ <!-- menuitems appended at runtime -->
+ </menu>
+ <menuitem id="menu_close" class="menuitem-iconic"/>
+ <menuseparator id="sep_close"/>
+ <menuitem id="menu_saveCmd"
+ accesskey="&saveCmd.accesskey;"
+ key="key_save"
+ command="cmd_save"
+ class="menuitem-iconic"/>
+ <menuitem id="menu_saveAsCmd"
+ label="&saveAsCmd.label;"
+ accesskey="&saveAsCmd.accesskey;"
+ command="cmd_saveAs"
+ class="menuitem-iconic"/>
+ <menuitem id="menu_saveAsChangeEncoding"
+ label="&saveAsChangeEncodingCmd2.label;"
+ accesskey="&saveAsChangeEncodingCmd2.accesskey;"
+ command="cmd_saveAndChangeEncoding"/>
+ <menuseparator id="sep_saveCmd"/>
+ <menuitem id="menu_publish"
+ accesskey="&publishCmd.accesskey;"
+ key="key_publish"
+ command="cmd_publish"/>
+ <menuitem id="menu_publishAs"
+ label="&publishAsCmd.label;"
+ accesskey="&publishAsCmd.accesskey;"
+ command="cmd_publishAs"/>
+ <menuseparator id="sep_publishAs"/>
+ <menuitem id="menu_fileRevert"
+ label="&fileRevert.label;"
+ accesskey="&fileRevert.accesskey;"
+ command="cmd_revert"/>
+ <menuseparator id="sep_print"/>
+ <!-- menuitems are merged in here from composerOverlay.xhtml -->
+ <menuitem id="menu_printSetup"/>
+ <menuitem id="menu_printPreview" class="menuitem-iconic"/>
+ <menuitem id="menu_print" class="menuitem-iconic"/>
+ <!-- The Exit/Quit item is merged from platformGlobalOverlay.xhtml -->
+ </menupopup>
+ </menu>
+
+ <!-- Edit menu items -->
+ <menupopup id="menu_EditPopup">
+ <menuitem id="menu_inlineSpellCheck"
+ oncommand="InlineSpellCheckerUI.enabled = !InlineSpellCheckerUI.enabled"
+ class="menuitem-iconic"/>
+ <menuitem id="menu_publishSettings"
+ insertafter="sep_preferences"
+ label="&publishSettings.label;"
+ accesskey="&publishSettings.accesskey;"
+ command="cmd_publishSettings"/>
+ </menupopup>
+
+ <menupopup id="menu_View_Popup">
+ <menu id="menu_zoom" insertbefore="charsetMenu"/>
+ </menupopup>
+
+ <menupopup id="insertMenuPopup">
+ <menu id="insertFormMenu"
+ insertafter="insertTOC"
+ label="&insertFormMenu.label;"
+ accesskey="&insertFormMenu.accesskey;">
+ <menupopup id="formMenuPopup">
+ <menuitem label="&insertFormCmd.label;"
+ accesskey="&insertFormCmd.accesskey;"
+ command="cmd_form"/>
+ <menuseparator/>
+ <menuitem label="&insertInputTagCmd.label;"
+ accesskey="&insertInputTagCmd.accesskey;"
+ command="cmd_inputtag"/>
+ <menuitem label="&insertInputImageCmd.label;"
+ accesskey="&insertInputImageCmd.accesskey;"
+ command="cmd_inputimage"/>
+ <menuitem label="&insertTextAreaCmd.label;"
+ accesskey="&insertTextAreaCmd.accesskey;"
+ command="cmd_textarea"/>
+ <menuitem label="&insertSelectCmd.label;"
+ accesskey="&insertSelectCmd.accesskey;"
+ command="cmd_select"/>
+ <menuitem label="&insertButtonCmd.label;"
+ accesskey="&insertButtonCmd.accesskey;"
+ command="cmd_button"/>
+ <menuitem label="&insertLabelCmd.label;"
+ accesskey="&insertLabelCmd.accesskey;"
+ command="cmd_label"/>
+ <menuitem label="&insertFieldSetCmd.label;"
+ accesskey="&insertFieldSetCmd.accesskey;"
+ command="cmd_fieldset"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+
+ <!-- Toolbar buttons/items -->
+ <toolbarbutton id="newButton"
+ class="toolbarbutton-1"
+ label="&newToolbarCmd.label;"
+ removable="true"
+ command="cmd_newEditor"
+ tooltiptext="&newToolbarCmd.tooltip;"/>
+ <toolbarbutton id="openButton"
+ class="toolbarbutton-1"
+ label="&openToolbarCmd.label;"
+ removable="true"
+ command="cmd_open"
+ tooltiptext="&openToolbarCmd.tooltip;"/>
+ <toolbarbutton id="saveButton"
+ class="toolbarbutton-1"
+ removable="true"
+ command="cmd_save"
+ tooltiptext="&saveToolbarCmd.tooltip;"/>
+ <toolbarbutton id="publishButton"
+ class="toolbarbutton-1"
+ removable="true"
+ command="cmd_publish"
+ tooltiptext="&publishToolbarCmd.tooltip;"/>
+ <toolbarbutton id="print-button"
+ label="&printToolbarCmd.label;"
+ removable="true"
+ tooltiptext="&printToolbarCmd.tooltip;"/>
+ <!-- 'print-button' is merged in here from utilityOverlay.xhtml -->
+ <toolbarbutton id="formButton"
+ class="toolbarbutton-1"
+ removable="true"
+ label="&formToolbarCmd.label;"
+ command="cmd_form"
+ tooltiptext="&formToolbarCmd.tooltip;"/>
+</overlay>
diff --git a/comm/suite/editor/base/content/editor.js b/comm/suite/editor/base/content/editor.js
new file mode 100644
index 0000000000..34270d057a
--- /dev/null
+++ b/comm/suite/editor/base/content/editor.js
@@ -0,0 +1,3383 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/* import-globals-from ../../../../mail/base/content/utilityOverlay.js */
+/* import-globals-from ComposerCommands.js */
+/* import-globals-from editorUtilities.js */
+/* globals InlineSpellCheckerUI */
+
+var { GetNextUntitledValue } = ChromeUtils.import(
+ "resource:///modules/editorUtilities.jsm"
+);
+var { Async } = ChromeUtils.import("resource://services-common/async.js");
+var { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+/* Main Composer window UI control */
+
+var gComposerWindowControllerID = 0;
+var prefAuthorString = "";
+
+var kDisplayModeNormal = 0;
+var kDisplayModeAllTags = 1;
+var kDisplayModeSource = 2;
+var kDisplayModePreview = 3;
+
+const kDisplayModeMenuIDs = [
+ "viewNormalMode",
+ "viewAllTagsMode",
+ "viewSourceMode",
+ "viewPreviewMode",
+];
+const kDisplayModeTabIDS = [
+ "NormalModeButton",
+ "TagModeButton",
+ "SourceModeButton",
+ "PreviewModeButton",
+];
+const kNormalStyleSheet = "chrome://editor/content/EditorContent.css";
+const kAllTagsStyleSheet = "chrome://editor/content/EditorAllTags.css";
+const kContentEditableStyleSheet = "resource://gre/res/contenteditable.css";
+
+var kTextMimeType = "text/plain";
+var kHTMLMimeType = "text/html";
+var kXHTMLMimeType = "application/xhtml+xml";
+
+var gPreviousNonSourceDisplayMode = 1;
+var gEditorDisplayMode = -1;
+var gDocWasModified = false; // Check if clean document, if clean then unload when user "Opens"
+var gContentWindow = 0;
+var gSourceContentWindow = 0;
+var gSourceTextEditor = null;
+var gContentWindowDeck;
+var gFormatToolbar;
+var gFormatToolbarHidden = false;
+var gChromeState;
+var gColorObj = {
+ LastTextColor: "",
+ LastBackgroundColor: "",
+ LastHighlightColor: "",
+ Type: "",
+ SelectedType: "",
+ NoDefault: false,
+ Cancel: false,
+ HighlightColor: "",
+ BackgroundColor: "",
+ PageColor: "",
+ TextColor: "",
+ TableColor: "",
+ CellColor: "",
+};
+var gDefaultTextColor = "";
+var gDefaultBackgroundColor = "";
+var gCSSPrefListener;
+var gReturnInParagraphPrefListener;
+var gLocalFonts = null;
+
+var gLastFocusNode = null;
+var gLastFocusNodeWasSelected = false;
+
+// These must be kept in synch with the XUL <options> lists
+var gFontSizeNames = [
+ "xx-small",
+ "x-small",
+ "small",
+ "medium",
+ "large",
+ "x-large",
+ "xx-large",
+];
+
+var nsIFilePicker = Ci.nsIFilePicker;
+
+var kUseCssPref = "editor.use_css";
+var kCRInParagraphsPref = "editor.CR_creates_new_p";
+
+function nsPrefListener(prefName) {
+ this.startup(prefName);
+}
+
+// implements nsIObserver
+nsPrefListener.prototype = {
+ domain: "",
+ startup(prefName) {
+ this.domain = prefName;
+ try {
+ Services.prefs.addObserver(this.domain, this);
+ } catch (ex) {
+ dump("Failed to observe prefs: " + ex + "\n");
+ }
+ },
+ shutdown() {
+ try {
+ Services.prefs.removeObserver(this.domain, this);
+ } catch (ex) {
+ dump("Failed to remove pref observers: " + ex + "\n");
+ }
+ },
+ observe(subject, topic, prefName) {
+ if (!IsHTMLEditor()) {
+ return;
+ }
+ // verify that we're changing a button pref
+ if (topic != "nsPref:changed") {
+ return;
+ }
+
+ let editor = GetCurrentEditor();
+ if (prefName == kUseCssPref) {
+ let cmd = document.getElementById("cmd_highlight");
+ if (cmd) {
+ let useCSS = Services.prefs.getBoolPref(prefName);
+
+ if (useCSS && editor) {
+ let mixedObj = {};
+ let state = editor.getHighlightColorState(mixedObj);
+ cmd.setAttribute("state", state);
+ cmd.collapsed = false;
+ } else {
+ cmd.setAttribute("state", "transparent");
+ cmd.collapsed = true;
+ }
+
+ if (editor) {
+ editor.isCSSEnabled = useCSS;
+ }
+ }
+ } else if (editor && prefName == kCRInParagraphsPref) {
+ editor.returnInParagraphCreatesNewParagraph = Services.prefs.getBoolPref(
+ prefName
+ );
+ }
+ },
+};
+
+const gSourceTextListener = {
+ NotifyDocumentCreated() {},
+ NotifyDocumentWillBeDestroyed() {},
+ NotifyDocumentStateChanged(isChanged) {
+ window.updateCommands("save");
+ },
+};
+
+const gSourceTextObserver = {
+ observe(aSubject, aTopic, aData) {
+ // we currently only use this to update undo
+ window.updateCommands("undo");
+ },
+};
+
+// This should be called by all editor users when they close their window.
+function EditorCleanup() {
+ SwitchInsertCharToAnotherEditorOrClose();
+}
+
+var DocumentReloadListener = {
+ NotifyDocumentCreated() {},
+ NotifyDocumentWillBeDestroyed() {},
+
+ NotifyDocumentStateChanged(isNowDirty) {
+ var editor = GetCurrentEditor();
+ try {
+ // unregister the listener to prevent multiple callbacks
+ editor.removeDocumentStateListener(DocumentReloadListener);
+
+ var charset = editor.documentCharacterSet;
+
+ // update the META charset with the current presentation charset
+ editor.documentCharacterSet = charset;
+ } catch (e) {}
+ },
+};
+
+// implements nsIObserver
+var gEditorDocumentObserver = {
+ observe(aSubject, aTopic, aData) {
+ // Should we allow this even if NOT the focused editor?
+ var commandManager = GetCurrentCommandManager();
+ if (commandManager != aSubject) {
+ return;
+ }
+
+ var editor = GetCurrentEditor();
+ switch (aTopic) {
+ case "obs_documentCreated":
+ // Just for convenience
+ gContentWindow = window.content;
+
+ // Get state to see if document creation succeeded
+ var params = newCommandParams();
+ if (!params) {
+ return;
+ }
+
+ try {
+ commandManager.getCommandState(aTopic, gContentWindow, params);
+ var errorStringId = 0;
+ var editorStatus = params.getLongValue("state_data");
+ if (!editor && editorStatus == nsIEditingSession.eEditorOK) {
+ dump(
+ "\n ****** NO EDITOR BUT NO EDITOR ERROR REPORTED ******* \n\n"
+ );
+ editorStatus = nsIEditingSession.eEditorErrorUnknown;
+ }
+
+ switch (editorStatus) {
+ case nsIEditingSession.eEditorErrorCantEditFramesets:
+ errorStringId = "CantEditFramesetMsg";
+ break;
+ case nsIEditingSession.eEditorErrorCantEditMimeType:
+ errorStringId = "CantEditMimeTypeMsg";
+ break;
+ case nsIEditingSession.eEditorErrorUnknown:
+ errorStringId = "CantEditDocumentMsg";
+ break;
+ // Note that for "eEditorErrorFileNotFound,
+ // network code popped up an alert dialog, so we don't need to
+ }
+ if (errorStringId) {
+ Services.prompt.alert(window, "", GetString(errorStringId));
+ }
+ } catch (e) {
+ dump("EXCEPTION GETTING obs_documentCreated state " + e + "\n");
+ }
+
+ // We have a bad editor -- nsIEditingSession will rebuild an editor
+ // with a blank page, so simply abort here
+ if (editorStatus) {
+ return;
+ }
+
+ if (!("InsertCharWindow" in window)) {
+ window.InsertCharWindow = null;
+ }
+
+ try {
+ editor.QueryInterface(nsIEditorStyleSheets);
+
+ // and extra styles for showing anchors, table borders, smileys, etc
+ editor.addOverrideStyleSheet(kNormalStyleSheet);
+
+ // remove contenteditable stylesheets if they were applied by the
+ // editingSession
+ editor.removeOverrideStyleSheet(kContentEditableStyleSheet);
+ } catch (e) {}
+
+ // Things for just the Web Composer application
+ if (IsWebComposer()) {
+ InlineSpellCheckerUI.init(editor);
+ document
+ .getElementById("menu_inlineSpellCheck")
+ .setAttribute("disabled", !InlineSpellCheckerUI.canSpellCheck);
+
+ editor.returnInParagraphCreatesNewParagraph = Services.prefs.getBoolPref(
+ kCRInParagraphsPref
+ );
+
+ // Set focus to content window if not a mail composer
+ // Race conditions prevent us from setting focus here
+ // when loading a url into blank window
+ setTimeout(SetFocusOnStartup, 0);
+
+ // Call EditorSetDefaultPrefsAndDoctype first so it gets the default author before initing toolbars
+ editor.enableUndo(false);
+ EditorSetDefaultPrefsAndDoctype();
+ editor.resetModificationCount();
+ editor.enableUndo(true);
+
+ // We may load a text document into an html editor,
+ // so be sure editortype is set correctly
+ // XXX We really should use the "real" plaintext editor for this!
+ if (editor.contentsMIMEType == "text/plain") {
+ try {
+ GetCurrentEditorElement().editortype = "text";
+ } catch (e) {
+ dump(e) + "\n";
+ }
+
+ // Hide or disable UI not used for plaintext editing
+ HideItem("FormatToolbar");
+ HideItem("EditModeToolbar");
+ HideItem("formatMenu");
+ HideItem("tableMenu");
+ HideItem("menu_validate");
+ HideItem("sep_validate");
+ HideItem("previewButton");
+ HideItem("imageButton");
+ HideItem("linkButton");
+ HideItem("namedAnchorButton");
+ HideItem("hlineButton");
+ HideItem("tableButton");
+
+ HideItem("fileExportToText");
+ HideItem("previewInBrowser");
+
+ /* XXX When paste actually converts formatted rich text to pretty formatted plain text
+ and pasteNoFormatting is fixed to paste the text without formatting (what paste
+ currently does), then this item shouldn't be hidden: */
+ HideItem("menu_pasteNoFormatting");
+
+ HideItem("cmd_viewEditModeToolbar");
+
+ HideItem("viewSep1");
+ HideItem("viewNormalMode");
+ HideItem("viewAllTagsMode");
+ HideItem("viewSourceMode");
+ HideItem("viewPreviewMode");
+
+ HideItem("structSpacer");
+
+ // Hide everything in "Insert" except for "Symbols"
+ let menuPopupChildren = document.querySelectorAll(
+ '[id="insertMenuPopup"] > :not(#insertChars)'
+ );
+ for (let i = 0; i < menuPopupChildren.length; i++) {
+ menuPopupChildren.item(i).hidden = true;
+ }
+ }
+
+ // Set window title
+ UpdateWindowTitle();
+
+ // We must wait until document is created to get proper Url
+ // (Windows may load with local file paths)
+ SetSaveAndPublishUI(GetDocumentUrl());
+
+ // Start in "Normal" edit mode
+ SetDisplayMode(kDisplayModeNormal);
+ }
+
+ // Add mouse click watcher if right type of editor
+ if (IsHTMLEditor()) {
+ // Force color widgets to update
+ onFontColorChange();
+ onBackgroundColorChange();
+ }
+ break;
+
+ case "cmd_setDocumentModified":
+ window.updateCommands("save");
+ break;
+
+ case "obs_documentWillBeDestroyed":
+ dump("obs_documentWillBeDestroyed notification\n");
+ break;
+
+ case "obs_documentLocationChanged":
+ // Ignore this when editor doesn't exist,
+ // which happens once when page load starts
+ if (editor) {
+ try {
+ editor.updateBaseURL();
+ } catch (e) {
+ dump(e);
+ }
+ }
+ break;
+
+ case "cmd_bold":
+ // Update all style items
+ // cmd_bold is a proxy; see EditorSharedStartup (above) for details
+ window.updateCommands("style");
+ window.updateCommands("undo");
+ break;
+ }
+ },
+};
+
+function SetFocusOnStartup() {
+ gContentWindow.focus();
+}
+
+function EditorLoadUrl(url) {
+ try {
+ if (url) {
+ let loadURIOptions = {
+ loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE,
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ };
+ GetCurrentEditorElement().webNavigation.loadURI(url, loadURIOptions);
+ }
+ } catch (e) {
+ dump(" EditorLoadUrl failed: " + e + "\n");
+ }
+}
+
+// This should be called by all Composer types
+function EditorSharedStartup() {
+ // Just for convenience
+ gContentWindow = window.content;
+
+ // Disable DNS Prefetching on the docshell - we don't need it for composer
+ // type windows.
+ GetCurrentEditorElement().docShell.allowDNSPrefetch = false;
+
+ // Set up the mime type and register the commands.
+ if (IsHTMLEditor()) {
+ SetupHTMLEditorCommands();
+ } else {
+ SetupTextEditorCommands();
+ }
+
+ // add observer to be called when document is really done loading
+ // and is modified
+ // Note: We're really screwed if we fail to install this observer!
+ try {
+ var commandManager = GetCurrentCommandManager();
+ commandManager.addCommandObserver(
+ gEditorDocumentObserver,
+ "obs_documentCreated"
+ );
+ commandManager.addCommandObserver(
+ gEditorDocumentObserver,
+ "cmd_setDocumentModified"
+ );
+ commandManager.addCommandObserver(
+ gEditorDocumentObserver,
+ "obs_documentWillBeDestroyed"
+ );
+ commandManager.addCommandObserver(
+ gEditorDocumentObserver,
+ "obs_documentLocationChanged"
+ );
+
+ // Until nsIControllerCommandGroup-based code is implemented,
+ // we will observe just the bold command to trigger update of
+ // all toolbar style items
+ commandManager.addCommandObserver(gEditorDocumentObserver, "cmd_bold");
+ } catch (e) {
+ dump(e);
+ }
+
+ var isMac = AppConstants.platform == "macosx";
+
+ // Set platform-specific hints for how to select cells
+ // Mac uses "Cmd", all others use "Ctrl"
+ var tableKey = GetString(isMac ? "XulKeyMac" : "TableSelectKey");
+ var dragStr = tableKey + GetString("Drag");
+ var clickStr = tableKey + GetString("Click");
+
+ var delStr = GetString(isMac ? "Clear" : "Del");
+
+ SafeSetAttribute("menu_SelectCell", "acceltext", clickStr);
+ SafeSetAttribute("menu_SelectRow", "acceltext", dragStr);
+ SafeSetAttribute("menu_SelectColumn", "acceltext", dragStr);
+ SafeSetAttribute("menu_SelectAllCells", "acceltext", dragStr);
+ // And add "Del" or "Clear"
+ SafeSetAttribute("menu_DeleteCellContents", "acceltext", delStr);
+
+ // Set text for indent, outdent keybinding
+
+ // hide UI that we don't have components for
+ RemoveInapplicableUIElements();
+
+ // Use browser colors as initial values for editor's default colors
+ var BrowserColors = GetDefaultBrowserColors();
+ if (BrowserColors) {
+ gDefaultTextColor = BrowserColors.TextColor;
+ gDefaultBackgroundColor = BrowserColors.BackgroundColor;
+ }
+
+ // For new window, no default last-picked colors
+ gColorObj.LastTextColor = "";
+ gColorObj.LastBackgroundColor = "";
+ gColorObj.LastHighlightColor = "";
+}
+
+function SafeSetAttribute(nodeID, attributeName, attributeValue) {
+ var theNode = document.getElementById(nodeID);
+ if (theNode) {
+ theNode.setAttribute(attributeName, attributeValue);
+ }
+}
+
+function DocumentHasBeenSaved() {
+ var fileurl = "";
+ try {
+ fileurl = GetDocumentUrl();
+ } catch (e) {
+ return false;
+ }
+
+ if (!fileurl || IsUrlAboutBlank(fileurl)) {
+ return false;
+ }
+
+ // We have a file URL already
+ return true;
+}
+
+async function CheckAndSaveDocument(command, allowDontSave) {
+ var document;
+ try {
+ // if we don't have an editor or an document, bail
+ var editor = GetCurrentEditor();
+ document = editor.document;
+ if (!document) {
+ return true;
+ }
+ } catch (e) {
+ return true;
+ }
+
+ if (!IsDocumentModified() && !IsHTMLSourceChanged()) {
+ return true;
+ }
+
+ // call window.focus, since we need to pop up a dialog
+ // and therefore need to be visible (to prevent user confusion)
+ top.document.commandDispatcher.focusedWindow.focus();
+
+ var scheme = GetScheme(GetDocumentUrl());
+ var doPublish = scheme && scheme != "file";
+
+ var strID;
+ switch (command) {
+ case "cmd_close":
+ strID = "BeforeClosing";
+ break;
+ case "cmd_preview":
+ strID = "BeforePreview";
+ break;
+ case "cmd_editSendPage":
+ strID = "SendPageReason";
+ break;
+ case "cmd_validate":
+ strID = "BeforeValidate";
+ break;
+ }
+
+ var reasonToSave = strID ? GetString(strID) : "";
+
+ var title = document.title || GetString("untitledDefaultFilename");
+
+ var dialogTitle = GetString(doPublish ? "PublishPage" : "SaveDocument");
+ var dialogMsg = GetString(doPublish ? "PublishPrompt" : "SaveFilePrompt");
+ dialogMsg = dialogMsg
+ .replace(/%title%/, title)
+ .replace(/%reason%/, reasonToSave);
+
+ let result = { value: 0 };
+ let promptFlags =
+ Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1;
+ let button1Title = null;
+ let button3Title = null;
+
+ if (doPublish) {
+ promptFlags +=
+ Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0;
+ button1Title = GetString("Publish");
+ button3Title = GetString("DontPublish");
+ } else {
+ promptFlags +=
+ Services.prompt.BUTTON_TITLE_SAVE * Services.prompt.BUTTON_POS_0;
+ }
+
+ // If allowing "Don't..." button, add that
+ if (allowDontSave) {
+ promptFlags += doPublish
+ ? Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2
+ : Services.prompt.BUTTON_TITLE_DONT_SAVE * Services.prompt.BUTTON_POS_2;
+ }
+
+ result = Services.prompt.confirmEx(
+ window,
+ dialogTitle,
+ dialogMsg,
+ promptFlags,
+ button1Title,
+ null,
+ button3Title,
+ null,
+ { value: 0 }
+ );
+
+ if (result == 0) {
+ // Save, but first finish HTML source mode
+ SetEditMode(gPreviousNonSourceDisplayMode);
+ if (doPublish) {
+ // We save the command the user wanted to do in a global
+ // and return as if user canceled because publishing is asynchronous
+ // This command will be fired when publishing finishes
+ gCommandAfterPublishing = command;
+ goDoCommand("cmd_publish");
+ return false;
+ }
+
+ // Save to local disk
+ return SaveDocument(false, false, editor.contentsMIMEType);
+ }
+
+ if (result == 2) {
+ // "Don't Save"
+ return true;
+ }
+
+ // Default or result == 1 (Cancel)
+ return false;
+}
+
+// --------------------------- View menu ---------------------------
+
+function EditorSetCharacterSet(aEvent) {
+ try {
+ var editor = GetCurrentEditor();
+ if (aEvent.target.hasAttribute("charset")) {
+ editor.documentCharacterSet = aEvent.target.getAttribute("charset");
+ }
+ var docUrl = GetDocumentUrl();
+ if (!IsUrlAboutBlank(docUrl)) {
+ // reloading the document will reverse any changes to the META charset,
+ // we need to put them back in, which is achieved by a dedicated listener
+ editor.addDocumentStateListener(DocumentReloadListener);
+ EditorLoadUrl(docUrl);
+ }
+ } catch (e) {}
+}
+
+// --------------------------- Text style ---------------------------
+
+function onParagraphFormatChange(paraMenuList, commandID) {
+ if (!paraMenuList) {
+ return;
+ }
+
+ var commandNode = document.getElementById(commandID);
+ var state = commandNode.getAttribute("state");
+
+ // force match with "normal"
+ if (state == "body") {
+ state = "";
+ }
+
+ if (state == "mixed") {
+ // Selection is the "mixed" ( > 1 style) state
+ paraMenuList.selectedItem = null;
+ paraMenuList.setAttribute("label", GetString("Mixed"));
+ } else {
+ var menuPopup = document.getElementById("ParagraphPopup");
+ var menuItems = menuPopup.childNodes;
+ for (var i = 0; i < menuItems.length; i++) {
+ var menuItem = menuItems.item(i);
+ if ("value" in menuItem && menuItem.value == state) {
+ paraMenuList.selectedItem = menuItem;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Selects the current font face in the menulist.
+ *
+ * @param fontFaceMenuList The menulist element containing the list of fonts.
+ * @param commandID The commandID which holds the current font name
+ * in its "state" attribute.
+ */
+function onFontFaceChange(fontFaceMenuList, commandID) {
+ var commandNode = document.getElementById(commandID);
+ var editorFont = commandNode.getAttribute("state");
+
+ // Strip quotes in font names. Experiments have shown that we only
+ // ever get double quotes around the font name, never single quotes,
+ // even if they were in the HTML source. Also single or double
+ // quotes within the font name are never returned.
+ editorFont = editorFont.replace(/"/g, "");
+
+ switch (editorFont) {
+ case "mixed":
+ // Selection is the "mixed" ( > 1 style) state.
+ fontFaceMenuList.selectedItem = null;
+ fontFaceMenuList.setAttribute("label", GetString("Mixed"));
+ return;
+ case "":
+ case "serif":
+ case "sans-serif":
+ // Generic variable width.
+ fontFaceMenuList.selectedIndex = 0;
+ return;
+ case "tt":
+ case "monospace":
+ // Generic fixed width.
+ fontFaceMenuList.selectedIndex = 1;
+ return;
+ default:
+ }
+
+ let menuPopup = fontFaceMenuList.menupopup;
+ let menuItems = menuPopup.childNodes;
+
+ const genericFamilies = [
+ "serif",
+ "sans-serif",
+ "monospace",
+ "fantasy",
+ "cursive",
+ ];
+ // Bug 1139524: Normalise before we compare: Make it lower case
+ // and replace ", " with "," so that entries like
+ // "Helvetica, Arial, sans-serif" are always recognised correctly
+ let editorFontToLower = editorFont.toLowerCase().replace(/, /g, ",");
+ let foundFont = null;
+ let exactMatch = false;
+ let usedFontsSep = menuPopup.querySelector(
+ "menuseparator.fontFaceMenuAfterUsedFonts"
+ );
+ let editorFontOptions = editorFontToLower.split(",");
+ let editorOptionsCount = editorFontOptions.length;
+ let matchedFontIndex = editorOptionsCount; // initialise to high invalid value
+
+ // The font menu has this structure:
+ // 0: Variable Width
+ // 1: Fixed Width
+ // 2: Separator
+ // 3: Helvetica, Arial (stored as Helvetica, Arial, sans-serif)
+ // 4: Times (stored as Times New Roman, Times, serif)
+ // 5: Courier (stored as Courier New, Courier, monospace)
+ // 6: Separator, "menuseparator.fontFaceMenuAfterDefaultFonts"
+ // from 7: Used Font Section (for quick selection)
+ // followed by separator, "menuseparator.fontFaceMenuAfterUsedFonts"
+ // followed by all other available fonts.
+ // The following variable keeps track of where we are when we loop over the menu.
+ let afterUsedFontSection = false;
+
+ // The menu items not only have "label" and "value", but also some other attributes:
+ // "value_parsed": Is the toLowerCase() and space-stripped value.
+ // "value_cache": Is a concatenation of all editor fonts that were ever mapped
+ // onto this menu item. This is done for optimization.
+ // "used": This item is in the used font section.
+
+ for (let i = 0; i < menuItems.length; i++) {
+ let menuItem = menuItems.item(i);
+ if (
+ menuItem.hasAttribute("label") &&
+ menuItem.hasAttribute("value_parsed")
+ ) {
+ // The element seems to represent a font <menuitem>.
+ let fontMenuValue = menuItem.getAttribute("value_parsed");
+ if (
+ fontMenuValue == editorFontToLower ||
+ (menuItem.hasAttribute("value_cache") &&
+ menuItem
+ .getAttribute("value_cache")
+ .split("|")
+ .includes(editorFontToLower))
+ ) {
+ // This menuitem contains the font we are looking for.
+ foundFont = menuItem;
+ exactMatch = true;
+ break;
+ } else if (editorOptionsCount > 1 && afterUsedFontSection) {
+ // Once we are in the list of all other available fonts,
+ // we will find the one that best matches one of the options.
+ let matchPos = editorFontOptions.indexOf(fontMenuValue);
+ if (matchPos >= 0 && matchPos < matchedFontIndex) {
+ // This menu font comes earlier in the list of options,
+ // so prefer it.
+ matchedFontIndex = matchPos;
+ foundFont = menuItem;
+ // If we matched the first option, we don't need to look for
+ // a better match.
+ if (matchPos == 0) {
+ break;
+ }
+ }
+ }
+ } else if (menuItem == usedFontsSep) {
+ // Some other element type.
+ // We have now passed the section of used fonts and are now in the list of all.
+ afterUsedFontSection = true;
+ }
+ }
+
+ if (foundFont) {
+ let defaultFontsSep = menuPopup.querySelector(
+ "menuseparator.fontFaceMenuAfterDefaultFonts"
+ );
+ if (exactMatch) {
+ if (afterUsedFontSection) {
+ // Copy the matched font into the section of used fonts.
+ // We insert after the separator following the default fonts,
+ // so right at the beginning of the used fonts section.
+ let copyItem = foundFont.cloneNode(true);
+ menuPopup.insertBefore(copyItem, defaultFontsSep.nextSibling);
+ usedFontsSep.hidden = false;
+ foundFont = copyItem;
+ foundFont.setAttribute("used", "true");
+ }
+ } else {
+ // Keep only the found font and generic families in the font string.
+ editorFont = editorFont
+ .replace(/, /g, ",")
+ .split(",")
+ .filter(
+ font =>
+ font.toLowerCase() == foundFont.getAttribute("value_parsed") ||
+ genericFamilies.includes(font)
+ )
+ .join(",");
+
+ // Check if such an item is already in the used font section.
+ if (afterUsedFontSection) {
+ foundFont = menuPopup.querySelector(
+ 'menuitem[used="true"][value_parsed="' +
+ editorFont.toLowerCase() +
+ '"]'
+ );
+ }
+ // If not, create a new entry which will be inserted into that section.
+ if (!foundFont) {
+ foundFont = createFontFaceMenuitem(editorFont, editorFont, menuPopup);
+ }
+
+ // Add the editor font string into the 'cache' attribute in the element
+ // so we can later find it quickly without building the reduced string again.
+ let fontCache = "";
+ if (foundFont.hasAttribute("value_cache")) {
+ fontCache = foundFont.getAttribute("value_cache");
+ }
+ foundFont.setAttribute(
+ "value_cache",
+ fontCache + "|" + editorFontToLower
+ );
+
+ // If we created a new item, set it up and insert.
+ if (!foundFont.hasAttribute("used")) {
+ foundFont.setAttribute("used", "true");
+ usedFontsSep.hidden = false;
+ menuPopup.insertBefore(foundFont, defaultFontsSep.nextSibling);
+ }
+ }
+ } else {
+ // The editor encountered a font that is not installed on this system.
+ // Add it to the font menu now, in the used-fonts section right at the
+ // bottom before the separator of the section.
+ let fontLabel = GetFormattedString("NotInstalled", editorFont);
+ foundFont = createFontFaceMenuitem(fontLabel, editorFont, menuPopup);
+ foundFont.setAttribute("used", "true");
+ usedFontsSep.hidden = false;
+ menuPopup.insertBefore(foundFont, usedFontsSep);
+ }
+ fontFaceMenuList.selectedItem = foundFont;
+}
+
+/**
+ * Clears the used fonts list from all the font face menulists.
+ */
+function ClearUsedFonts() {
+ let userFontSeps = document.querySelectorAll(
+ "menuseparator.fontFaceMenuAfterDefaultFonts"
+ );
+ for (let userFontSep of userFontSeps) {
+ while (true) {
+ let nextNode = userFontSep.nextSibling;
+ if (nextNode.tagName != "menuseparator") {
+ nextNode.remove();
+ } else if (nextNode.classList.contains("fontFaceMenuAfterUsedFonts")) {
+ nextNode.hidden = true;
+ break;
+ }
+ }
+ }
+}
+
+function EditorSelectFontSize() {
+ var select = document.getElementById("FontSizeSelect");
+ if (select) {
+ if (select.selectedIndex == -1) {
+ return;
+ }
+
+ EditorSetFontSize(gFontSizeNames[select.selectedIndex]);
+ }
+}
+
+function onFontSizeChange(fontSizeMenulist, commandID) {
+ // If we don't match anything, set to "0 (normal)"
+ var newIndex = 2;
+ var size = fontSizeMenulist.getAttribute("size");
+ if (size == "mixed") {
+ // No single type selected
+ newIndex = -1;
+ } else {
+ for (var i = 0; i < gFontSizeNames.length; i++) {
+ if (gFontSizeNames[i] == size) {
+ newIndex = i;
+ break;
+ }
+ }
+ }
+ if (fontSizeMenulist.selectedIndex != newIndex) {
+ fontSizeMenulist.selectedIndex = newIndex;
+ }
+}
+
+function EditorSetFontSize(size) {
+ if (size == "0" || size == "normal" || size == "medium") {
+ EditorRemoveTextProperty("font", "size");
+ // Also remove big and small,
+ // else it will seem like size isn't changing correctly
+ EditorRemoveTextProperty("small", "");
+ EditorRemoveTextProperty("big", "");
+ } else {
+ // Temp: convert from new CSS size strings to old HTML size strings
+ switch (size) {
+ case "xx-small":
+ case "x-small":
+ size = "-2";
+ break;
+ case "small":
+ size = "-1";
+ break;
+ case "large":
+ size = "+1";
+ break;
+ case "x-large":
+ size = "+2";
+ break;
+ case "xx-large":
+ size = "+3";
+ break;
+ }
+ EditorSetTextProperty("font", "size", size);
+ }
+ gContentWindow.focus();
+}
+
+function initFontFaceMenu(menuPopup) {
+ initLocalFontFaceMenu(menuPopup);
+
+ if (menuPopup) {
+ var children = menuPopup.childNodes;
+ if (!children) {
+ return;
+ }
+
+ var mixed = { value: false };
+ var editorFont = GetCurrentEditor().getFontFaceState(mixed);
+
+ // Strip quotes in font names. Experiments have shown that we only
+ // ever get double quotes around the font name, never single quotes,
+ // even if they were in the HTML source. Also single or double
+ // quotes within the font name are never returned.
+ editorFont = editorFont.replace(/"/g, "");
+
+ if (!mixed.value) {
+ switch (editorFont) {
+ case "":
+ case "serif":
+ case "sans-serif":
+ // Generic variable width.
+ editorFont = "";
+ break;
+ case "tt":
+ case "monospace":
+ // Generic fixed width.
+ editorFont = "tt";
+ break;
+ default:
+ editorFont = editorFont.toLowerCase().replace(/, /g, ","); // bug 1139524
+ }
+ }
+
+ var editorFontOptions = editorFont.split(",");
+ var matchedOption = editorFontOptions.length; // initialise to high invalid value
+ for (var i = 0; i < children.length; i++) {
+ var menuItem = children[i];
+ if (menuItem.localName == "menuitem") {
+ var matchFound = false;
+ if (!mixed.value) {
+ var menuFont = menuItem
+ .getAttribute("value")
+ .toLowerCase()
+ .replace(/, /g, ",");
+
+ // First compare the entire font string to match items that contain commas.
+ if (menuFont == editorFont) {
+ menuItem.setAttribute("checked", "true");
+ break;
+ } else if (editorFontOptions.length > 1) {
+ // Next compare the individual options.
+ var matchPos = editorFontOptions.indexOf(menuFont);
+ if (matchPos >= 0 && matchPos < matchedOption) {
+ // This menu font comes earlier in the list of options,
+ // so prefer it.
+ menuItem.setAttribute("checked", "true");
+
+ // If we matched the first option, we don't need to look for
+ // a better match.
+ if (matchPos == 0) {
+ break;
+ }
+
+ matchedOption = matchPos;
+ matchFound = true;
+ }
+ }
+ }
+
+ // In case this item doesn't match, make sure we've cleared the checkmark.
+ if (!matchFound) {
+ menuItem.removeAttribute("checked");
+ }
+ }
+ }
+ }
+}
+
+// Number of fixed font face menuitems, these are:
+// Variable Width
+// Fixed Width
+// ==separator
+// Helvetica, Arial
+// Times
+// Courier
+// ==separator
+// ==separator
+const kFixedFontFaceMenuItems = 8;
+
+function initLocalFontFaceMenu(menuPopup) {
+ if (!gLocalFonts) {
+ // Build list of all local fonts once per editor
+ try {
+ var enumerator = Cc["@mozilla.org/gfx/fontenumerator;1"].getService(
+ Ci.nsIFontEnumerator
+ );
+ gLocalFonts = enumerator.EnumerateAllFonts();
+ } catch (e) {}
+ }
+
+ // Don't use radios for menulists.
+ let useRadioMenuitems = menuPopup.parentNode.localName == "menu";
+ menuPopup.setAttribute("useRadios", useRadioMenuitems);
+ if (menuPopup.childNodes.length == kFixedFontFaceMenuItems) {
+ if (gLocalFonts.length == 0) {
+ menuPopup.querySelector(".fontFaceMenuAfterDefaultFonts").hidden = true;
+ }
+ for (let i = 0; i < gLocalFonts.length; ++i) {
+ // Remove Linux system generic fonts that collide with CSS generic fonts.
+ if (
+ gLocalFonts[i] != "" &&
+ gLocalFonts[i] != "serif" &&
+ gLocalFonts[i] != "sans-serif" &&
+ gLocalFonts[i] != "monospace"
+ ) {
+ let itemNode = createFontFaceMenuitem(
+ gLocalFonts[i],
+ gLocalFonts[i],
+ menuPopup
+ );
+ menuPopup.appendChild(itemNode);
+ }
+ }
+ }
+}
+
+/**
+ * Creates a menuitem element for the font faces menulist. Returns the menuitem
+ * but does not add it automatically to the menupopup.
+ *
+ * @param aFontLabel Label to be displayed for the item.
+ * @param aFontName The font face value to be used for the item.
+ * Will be used in <font face="value"> in the edited document.
+ * @param aMenuPopup The menupopup for which this menuitem is created.
+ */
+function createFontFaceMenuitem(aFontLabel, aFontName, aMenuPopup) {
+ let itemNode = document.createXULElement("menuitem");
+ itemNode.setAttribute("label", aFontLabel);
+ itemNode.setAttribute("value", aFontName);
+ itemNode.setAttribute(
+ "value_parsed",
+ aFontName.toLowerCase().replace(/, /g, ",")
+ );
+ itemNode.setAttribute("tooltiptext", aFontLabel);
+ if (aMenuPopup.getAttribute("useRadios") == "true") {
+ itemNode.setAttribute("type", "radio");
+ itemNode.setAttribute("observes", "cmd_renderedHTMLEnabler");
+ }
+ return itemNode;
+}
+
+/**
+ * Helper function
+ */
+function getFontSizeIndex() {
+ var firstHas = { value: false };
+ var anyHas = { value: false };
+ var allHas = { value: false };
+
+ var fontSize = EditorGetTextProperty(
+ "font",
+ "size",
+ null,
+ firstHas,
+ anyHas,
+ allHas
+ );
+
+ // If the element has no size attribute and no size was found at all,
+ // we assume "medium" size. This is highly problematic since
+ // CSS sizes are not recognised and will show as "medium" as well.
+ // Currently we can't distinguish between "no attribute" which
+ // can imply "medium" and "CSS attribute present" which should not
+ // imply "medium".
+ if (!anyHas.value) {
+ return 2;
+ }
+
+ // Mixed selection.
+ if (!allHas.value) {
+ return -1;
+ }
+
+ switch (fontSize) {
+ case "-3":
+ case "-2":
+ case "0":
+ case "1":
+ // x-small.
+ return 0;
+ case "-1":
+ case "2":
+ // small.
+ return 1;
+ case "3":
+ // medium.
+ return 2;
+ case "+1":
+ case "4":
+ // large.
+ return 3;
+ case "+2":
+ case "5":
+ // x-large.
+ return 4;
+ case "+3":
+ case "+4":
+ case "6":
+ case "7":
+ // xx-large.
+ return 5;
+ }
+
+ // We shouldn't get here. All the selection has a value we don't understand.
+ return -1;
+}
+
+function initFontSizeMenu(menuPopup, fullMenu) {
+ if (menuPopup) {
+ var children = menuPopup.childNodes;
+ if (!children) {
+ return;
+ }
+
+ // Fixed size items start after menu separator depending on whether it is
+ // a full menu.
+ var menuIndex = fullMenu ? 3 : 0;
+
+ var setIndex = getFontSizeIndex();
+ if (setIndex >= 0) {
+ children[menuIndex + setIndex].setAttribute("checked", true);
+ } else {
+ // In case of mixed, clear all items.
+ for (var i = menuIndex; i < children.length; i++) {
+ children[i].setAttribute("checked", false);
+ }
+ }
+
+ // Some configurations might not have the "small/big" indicator as
+ // last item. If there is no indicator, we are done.
+ if (!menuPopup.lastChild.id.includes("smallBigInfo")) {
+ return;
+ }
+
+ // While it would be better to show the number of levels,
+ // at least this tells user if either of them are set.
+ var firstHas = { value: false };
+ var anyHas = { value: false };
+ var allHas = { value: false };
+
+ // Show "small"/"big" indicator.
+ var htmlInfo = "";
+ EditorGetTextProperty("small", "", "", firstHas, anyHas, allHas);
+ if (anyHas.value) {
+ htmlInfo = "<small>";
+ }
+ EditorGetTextProperty("big", "", "", firstHas, anyHas, allHas);
+ if (anyHas.value) {
+ htmlInfo += "<big>";
+ }
+
+ if (htmlInfo) {
+ menuPopup.lastChild.hidden = false;
+ menuPopup.lastChild.setAttribute("label", "HTML: " + htmlInfo);
+ menuPopup.lastChild.setAttribute("checked", true);
+ } else {
+ menuPopup.lastChild.hidden = true;
+ }
+ }
+}
+
+function onHighlightColorChange() {
+ ChangeButtonColor("cmd_highlight", "HighlightColorButton", "transparent");
+}
+
+function onFontColorChange() {
+ ChangeButtonColor("cmd_fontColor", "TextColorButton", gDefaultTextColor);
+}
+
+function onBackgroundColorChange() {
+ ChangeButtonColor(
+ "cmd_backgroundColor",
+ "BackgroundColorButton",
+ gDefaultBackgroundColor
+ );
+}
+
+/* Helper function that changes the button color.
+ * commandID - The ID of the command element.
+ * id - The ID of the button needing to be changed.
+ * defaultColor - The default color the button gets set to.
+ */
+function ChangeButtonColor(commandID, id, defaultColor) {
+ var commandNode = document.getElementById(commandID);
+ if (commandNode) {
+ var color = commandNode.getAttribute("state");
+ var button = document.getElementById(id);
+ if (button) {
+ button.setAttribute("color", color);
+
+ // No color or a mixed color - get color set on page or other defaults.
+ if (!color || color == "mixed") {
+ color = defaultColor;
+ }
+
+ button.setAttribute("style", "background-color:" + color + " !important");
+ }
+ }
+}
+
+// Call this when user changes text and/or background colors of the page
+function UpdateDefaultColors() {
+ var BrowserColors = GetDefaultBrowserColors();
+ var bodyelement = GetBodyElement();
+ var defTextColor = gDefaultTextColor;
+ var defBackColor = gDefaultBackgroundColor;
+
+ if (bodyelement) {
+ var color = bodyelement.getAttribute("text");
+ if (color) {
+ gDefaultTextColor = color;
+ } else if (BrowserColors) {
+ gDefaultTextColor = BrowserColors.TextColor;
+ }
+
+ color = bodyelement.getAttribute("bgcolor");
+ if (color) {
+ gDefaultBackgroundColor = color;
+ } else if (BrowserColors) {
+ gDefaultBackgroundColor = BrowserColors.BackgroundColor;
+ }
+ }
+
+ // Trigger update on toolbar
+ if (defTextColor != gDefaultTextColor) {
+ goUpdateCommandState("cmd_fontColor");
+ onFontColorChange();
+ }
+ if (defBackColor != gDefaultBackgroundColor) {
+ goUpdateCommandState("cmd_backgroundColor");
+ onBackgroundColorChange();
+ }
+}
+
+function GetBackgroundElementWithColor() {
+ var editor = GetCurrentTableEditor();
+ if (!editor) {
+ return null;
+ }
+
+ gColorObj.Type = "";
+ gColorObj.PageColor = "";
+ gColorObj.TableColor = "";
+ gColorObj.CellColor = "";
+ gColorObj.BackgroundColor = "";
+ gColorObj.SelectedType = "";
+
+ var tagNameObj = { value: "" };
+ var element;
+ try {
+ element = editor.getSelectedOrParentTableElement(tagNameObj, { value: 0 });
+ } catch (e) {}
+
+ if (element && tagNameObj && tagNameObj.value) {
+ gColorObj.BackgroundColor = GetHTMLOrCSSStyleValue(
+ element,
+ "bgcolor",
+ "background-color"
+ );
+ gColorObj.BackgroundColor = ConvertRGBColorIntoHEXColor(
+ gColorObj.BackgroundColor
+ );
+ if (tagNameObj.value.toLowerCase() == "td") {
+ gColorObj.Type = "Cell";
+ gColorObj.CellColor = gColorObj.BackgroundColor;
+
+ // Get any color that might be on parent table
+ var table = GetParentTable(element);
+ gColorObj.TableColor = GetHTMLOrCSSStyleValue(
+ table,
+ "bgcolor",
+ "background-color"
+ );
+ gColorObj.TableColor = ConvertRGBColorIntoHEXColor(gColorObj.TableColor);
+ } else {
+ gColorObj.Type = "Table";
+ gColorObj.TableColor = gColorObj.BackgroundColor;
+ }
+ gColorObj.SelectedType = gColorObj.Type;
+ } else {
+ let IsCSSPrefChecked = Services.prefs.getBoolPref(kUseCssPref);
+ if (IsCSSPrefChecked && IsHTMLEditor()) {
+ let selection = editor.selection;
+ if (selection) {
+ element = selection.focusNode;
+ while (!editor.nodeIsBlock(element)) {
+ element = element.parentNode;
+ }
+ } else {
+ element = GetBodyElement();
+ }
+ } else {
+ element = GetBodyElement();
+ }
+ if (element) {
+ gColorObj.Type = "Page";
+ gColorObj.BackgroundColor = GetHTMLOrCSSStyleValue(
+ element,
+ "bgcolor",
+ "background-color"
+ );
+ if (gColorObj.BackgroundColor == "") {
+ gColorObj.BackgroundColor = "transparent";
+ } else {
+ gColorObj.BackgroundColor = ConvertRGBColorIntoHEXColor(
+ gColorObj.BackgroundColor
+ );
+ }
+ gColorObj.PageColor = gColorObj.BackgroundColor;
+ }
+ }
+ return element;
+}
+
+function SetSmiley(smileyText) {
+ try {
+ GetCurrentEditor().insertText(smileyText);
+ gContentWindow.focus();
+ } catch (e) {}
+}
+
+/* eslint-disable complexity */
+function EditorSelectColor(colorType, mouseEvent) {
+ var editor = GetCurrentEditor();
+ if (!editor || !gColorObj) {
+ return;
+ }
+
+ // Shift + mouse click automatically applies last color, if available
+ var useLastColor = mouseEvent
+ ? mouseEvent.button == 0 && mouseEvent.shiftKey
+ : false;
+ var element;
+ var table;
+ var currentColor = "";
+ var commandNode;
+
+ if (!colorType) {
+ colorType = "";
+ }
+
+ if (colorType == "Text") {
+ gColorObj.Type = colorType;
+
+ // Get color from command node state
+ commandNode = document.getElementById("cmd_fontColor");
+ currentColor = commandNode.getAttribute("state");
+ currentColor = ConvertRGBColorIntoHEXColor(currentColor);
+ gColorObj.TextColor = currentColor;
+
+ if (useLastColor && gColorObj.LastTextColor) {
+ gColorObj.TextColor = gColorObj.LastTextColor;
+ } else {
+ useLastColor = false;
+ }
+ } else if (colorType == "Highlight") {
+ gColorObj.Type = colorType;
+
+ // Get color from command node state
+ commandNode = document.getElementById("cmd_highlight");
+ currentColor = commandNode.getAttribute("state");
+ currentColor = ConvertRGBColorIntoHEXColor(currentColor);
+ gColorObj.HighlightColor = currentColor;
+
+ if (useLastColor && gColorObj.LastHighlightColor) {
+ gColorObj.HighlightColor = gColorObj.LastHighlightColor;
+ } else {
+ useLastColor = false;
+ }
+ } else {
+ element = GetBackgroundElementWithColor();
+ if (!element) {
+ return;
+ }
+
+ // Get the table if we found a cell
+ if (gColorObj.Type == "Table") {
+ table = element;
+ } else if (gColorObj.Type == "Cell") {
+ table = GetParentTable(element);
+ }
+
+ // Save to avoid resetting if not necessary
+ currentColor = gColorObj.BackgroundColor;
+
+ if (colorType == "TableOrCell" || colorType == "Cell") {
+ if (gColorObj.Type == "Cell") {
+ gColorObj.Type = colorType;
+ } else if (gColorObj.Type != "Table") {
+ return;
+ }
+ } else if (colorType == "Table" && gColorObj.Type == "Page") {
+ return;
+ }
+
+ if (colorType == "" && gColorObj.Type == "Cell") {
+ // Using empty string for requested type means
+ // we can let user select cell or table
+ gColorObj.Type = "TableOrCell";
+ }
+
+ if (useLastColor && gColorObj.LastBackgroundColor) {
+ gColorObj.BackgroundColor = gColorObj.LastBackgroundColor;
+ } else {
+ useLastColor = false;
+ }
+ }
+ // Save the type we are really requesting
+ colorType = gColorObj.Type;
+
+ if (!useLastColor) {
+ // Avoid the JS warning
+ gColorObj.NoDefault = false;
+
+ // Launch the ColorPicker dialog
+ // TODO: Figure out how to position this under the color buttons on the toolbar
+ window.openDialog(
+ "chrome://editor/content/EdColorPicker.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ gColorObj
+ );
+
+ // User canceled the dialog
+ if (gColorObj.Cancel) {
+ return;
+ }
+ }
+
+ if (gColorObj.Type == "Text") {
+ if (currentColor != gColorObj.TextColor) {
+ if (gColorObj.TextColor) {
+ EditorSetTextProperty("font", "color", gColorObj.TextColor);
+ } else {
+ EditorRemoveTextProperty("font", "color");
+ }
+ }
+ // Update the command state (this will trigger color button update)
+ goUpdateCommandState("cmd_fontColor");
+ } else if (gColorObj.Type == "Highlight") {
+ if (currentColor != gColorObj.HighlightColor) {
+ if (gColorObj.HighlightColor) {
+ EditorSetTextProperty("font", "bgcolor", gColorObj.HighlightColor);
+ } else {
+ EditorRemoveTextProperty("font", "bgcolor");
+ }
+ }
+ // Update the command state (this will trigger color button update)
+ goUpdateCommandState("cmd_highlight");
+ } else if (element) {
+ if (gColorObj.Type == "Table") {
+ // Set background on a table
+ // Note that we shouldn't trust "currentColor" because of "TableOrCell" behavior
+ if (table) {
+ var bgcolor = table.getAttribute("bgcolor");
+ if (bgcolor != gColorObj.BackgroundColor) {
+ try {
+ if (gColorObj.BackgroundColor) {
+ editor.setAttributeOrEquivalent(
+ table,
+ "bgcolor",
+ gColorObj.BackgroundColor,
+ false
+ );
+ } else {
+ editor.removeAttributeOrEquivalent(table, "bgcolor", false);
+ }
+ } catch (e) {}
+ }
+ }
+ } else if (currentColor != gColorObj.BackgroundColor && IsHTMLEditor()) {
+ editor.beginTransaction();
+ try {
+ editor.setBackgroundColor(gColorObj.BackgroundColor);
+
+ if (gColorObj.Type == "Page" && gColorObj.BackgroundColor) {
+ // Set all page colors not explicitly set,
+ // else you can end up with unreadable pages
+ // because viewer's default colors may not be same as page author's
+ var bodyelement = GetBodyElement();
+ if (bodyelement) {
+ var defColors = GetDefaultBrowserColors();
+ if (defColors) {
+ if (!bodyelement.getAttribute("text")) {
+ editor.setAttributeOrEquivalent(
+ bodyelement,
+ "text",
+ defColors.TextColor,
+ false
+ );
+ }
+
+ // The following attributes have no individual CSS declaration counterparts
+ // Getting rid of them in favor of CSS implies CSS rules management
+ if (!bodyelement.getAttribute("link")) {
+ editor.setAttribute(bodyelement, "link", defColors.LinkColor);
+ }
+
+ if (!bodyelement.getAttribute("alink")) {
+ editor.setAttribute(
+ bodyelement,
+ "alink",
+ defColors.ActiveLinkColor
+ );
+ }
+
+ if (!bodyelement.getAttribute("vlink")) {
+ editor.setAttribute(
+ bodyelement,
+ "vlink",
+ defColors.VisitedLinkColor
+ );
+ }
+ }
+ }
+ }
+ } catch (e) {}
+
+ editor.endTransaction();
+ }
+
+ goUpdateCommandState("cmd_backgroundColor");
+ }
+ gContentWindow.focus();
+}
+/* eslint-enable complexity */
+
+function GetParentTable(element) {
+ var node = element;
+ while (node) {
+ if (node.nodeName.toLowerCase() == "table") {
+ return node;
+ }
+
+ node = node.parentNode;
+ }
+ return node;
+}
+
+function GetParentTableCell(element) {
+ var node = element;
+ while (node) {
+ if (
+ node.nodeName.toLowerCase() == "td" ||
+ node.nodeName.toLowerCase() == "th"
+ ) {
+ return node;
+ }
+
+ node = node.parentNode;
+ }
+ return node;
+}
+
+function EditorDblClick(event) {
+ // We check event.explicitOriginalTarget here because .target will never
+ // be a textnode (bug 193689)
+ if (event.explicitOriginalTarget) {
+ // Only bring up properties if clicked on an element or selected link
+ var element;
+ try {
+ element = event.explicitOriginalTarget;
+ } catch (e) {}
+
+ // We use "href" instead of "a" to not be fooled by named anchor
+ if (!element) {
+ try {
+ element = GetCurrentEditor().getSelectedElement("href");
+ } catch (e) {}
+ }
+
+ // Don't fire for body/p and other block elements.
+ // It's common that people try to double-click
+ // to select a word, but the click hits an empty area.
+ if (
+ element &&
+ ![
+ "body",
+ "p",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "blockquote",
+ "div",
+ "pre",
+ ].includes(element.nodeName.toLowerCase())
+ ) {
+ goDoCommand("cmd_objectProperties");
+ event.preventDefault();
+ }
+ }
+}
+
+function EditorClick(event) {
+ // For Web Composer: In Show All Tags Mode,
+ // single click selects entire element,
+ // except for body and table elements
+ if (gEditorDisplayMode == kDisplayModeAllTags) {
+ try {
+ // We check event.explicitOriginalTarget here because .target will never
+ // be a textnode (bug 193689)
+ var element = event.explicitOriginalTarget;
+ var name = element.localName;
+ if (!["body", "caption", "table", "td", "th", "tr"].includes(name)) {
+ GetCurrentEditor().selectElement(event.explicitOriginalTarget);
+ event.preventDefault();
+ }
+ } catch (e) {}
+ }
+}
+
+/* TODO: We need an oncreate hook to do enabling/disabling for the
+ Format menu. There should be code like this for the
+ object-specific "Properties" item
+*/
+// For property dialogs, we want the selected element,
+// but will accept a parent link, list, or table cell if inside one
+function GetObjectForProperties() {
+ var editor = GetCurrentEditor();
+ if (!editor || !IsHTMLEditor()) {
+ return null;
+ }
+
+ var element;
+ try {
+ element = editor.getSelectedElement("");
+ } catch (e) {}
+ if (element) {
+ if (element.namespaceURI == "http://www.w3.org/1998/Math/MathML") {
+ // If the object is a MathML element, we collapse the selection on it and
+ // we return its <math> ancestor. Hence the math dialog will be used.
+ GetCurrentEditor().selection.collapse(element, 0);
+ } else {
+ return element;
+ }
+ }
+
+ // Find nearest parent of selection anchor node
+ // that is a link, list, table cell, or table
+
+ var anchorNode;
+ var node;
+ try {
+ anchorNode = editor.selection.anchorNode;
+ if (anchorNode.firstChild) {
+ // Start at actual selected node
+ var offset = editor.selection.anchorOffset;
+ // Note: If collapsed, offset points to element AFTER caret,
+ // thus node may be null
+ node = anchorNode.childNodes.item(offset);
+ }
+ if (!node) {
+ node = anchorNode;
+ }
+ } catch (e) {}
+
+ while (node) {
+ if (node.nodeName) {
+ var nodeName = node.nodeName.toLowerCase();
+
+ // Done when we hit the body or #text.
+ if (nodeName == "body" || nodeName == "#text") {
+ break;
+ }
+
+ if (
+ (nodeName == "a" && node.href) ||
+ nodeName == "ol" ||
+ nodeName == "ul" ||
+ nodeName == "dl" ||
+ nodeName == "td" ||
+ nodeName == "th" ||
+ nodeName == "table" ||
+ nodeName == "math"
+ ) {
+ return node;
+ }
+ }
+ node = node.parentNode;
+ }
+ return null;
+}
+
+function SetEditMode(mode) {
+ if (!IsHTMLEditor()) {
+ return;
+ }
+
+ var bodyElement = GetBodyElement();
+ if (!bodyElement) {
+ dump("SetEditMode: We don't have a body node!\n");
+ return;
+ }
+
+ // must have editor if here!
+ var editor = GetCurrentEditor();
+ var inlineSpellCheckItem = document.getElementById("menu_inlineSpellCheck");
+
+ // Switch the UI mode before inserting contents
+ // so user can't type in source window while new window is being filled
+ var previousMode = gEditorDisplayMode;
+ if (!SetDisplayMode(mode)) {
+ return;
+ }
+
+ if (mode == kDisplayModeSource) {
+ // Display the DOCTYPE as a non-editable string above edit area
+ var domdoc;
+ try {
+ domdoc = editor.document;
+ } catch (e) {
+ dump(e + "\n");
+ }
+ if (domdoc) {
+ var doctypeNode = document.getElementById("doctype-text");
+ var dt = domdoc.doctype;
+ if (doctypeNode) {
+ if (dt) {
+ doctypeNode.collapsed = false;
+ var doctypeText = "<!DOCTYPE " + domdoc.doctype.name;
+ if (dt.publicId) {
+ doctypeText += ' PUBLIC "' + domdoc.doctype.publicId;
+ }
+ if (dt.systemId) {
+ doctypeText += ' "' + dt.systemId;
+ }
+ doctypeText += '">';
+ doctypeNode.setAttribute("value", doctypeText);
+ } else {
+ doctypeNode.collapsed = true;
+ }
+ }
+ }
+ // Get the entire document's source string
+
+ var flags =
+ editor.documentCharacterSet == "ISO-8859-1"
+ ? kOutputEncodeLatin1Entities
+ : kOutputEncodeBasicEntities;
+ try {
+ let encodeEntity = Services.prefs.getCharPref("editor.encode_entity");
+ switch (encodeEntity) {
+ case "basic":
+ flags = kOutputEncodeBasicEntities;
+ break;
+ case "latin1":
+ flags = kOutputEncodeLatin1Entities;
+ break;
+ case "html":
+ flags = kOutputEncodeHTMLEntities;
+ break;
+ case "none":
+ flags = 0;
+ break;
+ }
+ } catch (e) {}
+
+ if (Services.prefs.getBoolPref("editor.prettyprint")) {
+ flags |= kOutputFormatted;
+ }
+
+ flags |= kOutputLFLineBreak;
+ var source = editor.outputToString(editor.contentsMIMEType, flags);
+ var start = source.search(/<html/i);
+ if (start == -1) {
+ start = 0;
+ }
+ gSourceTextEditor.insertText(source.slice(start));
+ gSourceTextEditor.resetModificationCount();
+ gSourceTextEditor.addDocumentStateListener(gSourceTextListener);
+ gSourceTextEditor.enableUndo(true);
+ gSourceContentWindow.commandManager.addCommandObserver(
+ gSourceTextObserver,
+ "cmd_undo"
+ );
+ gSourceContentWindow.contentWindow.focus();
+ goDoCommand("cmd_moveTop");
+ } else if (previousMode == kDisplayModeSource) {
+ // Only rebuild document if a change was made in source window
+ if (IsHTMLSourceChanged()) {
+ // Disable spell checking when rebuilding source
+ InlineSpellCheckerUI.enabled = false;
+ inlineSpellCheckItem.removeAttribute("checked");
+
+ // Reduce the undo count so we don't use too much memory
+ // during multiple uses of source window
+ // (reinserting entire doc caches all nodes)
+ editor.clearUndoRedo();
+
+ editor.beginTransaction();
+ try {
+ // We are coming from edit source mode,
+ // so transfer that back into the document
+ source = gSourceTextEditor
+ .outputToString(kTextMimeType, kOutputLFLineBreak)
+ .trim();
+ if (editor.contentsMIMEType != kXHTMLMimeType) {
+ editor.rebuildDocumentFromSource(source); // This is undoable
+ } else {
+ /* eslint-disable-next-line no-unsanitized/method */
+ var fragment = editor.document
+ .createRange()
+ .createContextualFragment(source);
+ GetBodyElement().remove();
+ editor.document.replaceChild(
+ fragment.firstChild,
+ editor.document.documentElement
+ );
+ // We touched the DOM tree without a transaction here so that we
+ // broke undoable transactions. However, we cleared all undoable
+ // things above. Therefore nothing must be in the undo stack.
+ }
+
+ // Get the text for the <title> from the newly-parsed document
+ // (must do this for proper conversion of "escaped" characters)
+ let titleNode = editor.document.querySelector("title");
+ SetDocumentTitle(titleNode ? titleNode.textContent : "");
+ } catch (ex) {
+ dump(ex);
+ }
+ // If the MIME type is kXHTMLMimeType, we don't put any undoable
+ // transaction. Then, this endTransaction() call does not allow to
+ // live empty transaction. Therefore, the unnecessary empty transaction
+ // will be cleared here automatically.
+ editor.endTransaction();
+ }
+
+ // Clear out the string buffers
+ gSourceContentWindow.commandManager.removeCommandObserver(
+ gSourceTextObserver,
+ "cmd_undo"
+ );
+ gSourceTextEditor.removeDocumentStateListener(gSourceTextListener);
+ gSourceTextEditor.enableUndo(false);
+ gSourceTextEditor.selectAll();
+ gSourceTextEditor.deleteSelection(
+ gSourceTextEditor.eNone,
+ gSourceTextEditor.eStrip
+ );
+ gSourceTextEditor.resetModificationCount();
+
+ gContentWindow.focus();
+ // goDoCommand("cmd_moveTop");
+ }
+
+ switch (mode) {
+ case kDisplayModePreview:
+ // Disable spell checking when previewing
+ InlineSpellCheckerUI.enabled = false;
+ inlineSpellCheckItem.removeAttribute("checked");
+ inlineSpellCheckItem.setAttribute("disabled", true);
+ break;
+ case kDisplayModeSource:
+ inlineSpellCheckItem.setAttribute("disabled", true);
+ goSetCommandEnabled("cmd_pasteQuote", false);
+ break;
+ default:
+ inlineSpellCheckItem.setAttribute(
+ "disabled",
+ !InlineSpellCheckerUI.canSpellCheck
+ );
+ break;
+ }
+}
+
+function CancelHTMLSource() {
+ // Don't convert source text back into the DOM document
+ gSourceTextEditor.resetModificationCount();
+ SetDisplayMode(gPreviousNonSourceDisplayMode);
+}
+
+function SetDisplayMode(mode) {
+ if (!IsHTMLEditor()) {
+ return false;
+ }
+
+ // Already in requested mode:
+ // return false to indicate we didn't switch
+ if (mode == gEditorDisplayMode) {
+ return false;
+ }
+
+ var previousMode = gEditorDisplayMode;
+ gEditorDisplayMode = mode;
+
+ ResetStructToolbar();
+ if (mode == kDisplayModeSource) {
+ // Switch to the sourceWindow (second in the deck)
+ gContentWindowDeck.selectedIndex = 1;
+
+ // Hide the formatting toolbar if not already hidden
+ gFormatToolbarHidden = gFormatToolbar.hidden;
+ gFormatToolbar.hidden = true;
+ gFormatToolbar.setAttribute("hideinmenu", "true");
+
+ gSourceContentWindow.contentWindow.focus();
+ } else {
+ // Save the last non-source mode so we can cancel source editing easily
+ gPreviousNonSourceDisplayMode = mode;
+
+ // Load/unload appropriate override style sheet
+ try {
+ var editor = GetCurrentEditor();
+ editor.QueryInterface(nsIEditorStyleSheets);
+ editor instanceof Ci.nsIHTMLObjectResizer;
+
+ switch (mode) {
+ case kDisplayModePreview:
+ // Disable all extra "edit mode" style sheets
+ editor.enableStyleSheet(kNormalStyleSheet, false);
+ editor.enableStyleSheet(kAllTagsStyleSheet, false);
+ editor.objectResizingEnabled = true;
+ break;
+
+ case kDisplayModeNormal:
+ editor.addOverrideStyleSheet(kNormalStyleSheet);
+ // Disable ShowAllTags mode
+ editor.enableStyleSheet(kAllTagsStyleSheet, false);
+ editor.objectResizingEnabled = true;
+ break;
+
+ case kDisplayModeAllTags:
+ editor.addOverrideStyleSheet(kNormalStyleSheet);
+ editor.addOverrideStyleSheet(kAllTagsStyleSheet);
+ // don't allow resizing in AllTags mode because the visible tags
+ // change the computed size of images and tables...
+ if (editor.resizedObject) {
+ editor.hideResizers();
+ }
+ editor.objectResizingEnabled = false;
+ break;
+ }
+ } catch (e) {}
+
+ // Switch to the normal editor (first in the deck)
+ gContentWindowDeck.selectedIndex = 0;
+
+ // Restore menus and toolbars
+ if (previousMode == kDisplayModeSource) {
+ gFormatToolbar.hidden = gFormatToolbarHidden;
+ gFormatToolbar.removeAttribute("hideinmenu");
+ }
+
+ gContentWindow.focus();
+ }
+
+ // update commands to disable or re-enable stuff
+ window.updateCommands("mode_switch");
+
+ // Set the selected tab at bottom of window:
+ // (Note: Setting "selectedIndex = mode" won't redraw tabs when menu is used.)
+ document.getElementById(
+ "EditModeTabs"
+ ).selectedItem = document.getElementById(kDisplayModeTabIDS[mode]);
+
+ // Uncheck previous menuitem and set new check since toolbar may have been used
+ if (previousMode >= 0) {
+ document
+ .getElementById(kDisplayModeMenuIDs[previousMode])
+ .setAttribute("checked", "false");
+ }
+ document
+ .getElementById(kDisplayModeMenuIDs[mode])
+ .setAttribute("checked", "true");
+
+ return true;
+}
+
+function UpdateWindowTitle() {
+ try {
+ var filename = "";
+ var windowTitle = "";
+ var title = GetDocumentTitle();
+
+ // Append just the 'leaf' filename to the Doc. Title for the window caption
+ var docUrl = GetDocumentUrl();
+ if (docUrl && !IsUrlAboutBlank(docUrl)) {
+ var scheme = GetScheme(docUrl);
+ filename = GetFilename(docUrl);
+ if (filename) {
+ windowTitle = " [" + scheme + ":/.../" + filename + "]";
+ }
+
+ var fileType = IsHTMLEditor() ? "html" : "text";
+ // Save changed title in the recent pages data in prefs
+ SaveRecentFilesPrefs(title, fileType);
+ }
+
+ // Set window title with " - Composer" or " - Text Editor" appended.
+ var xulWin = document.documentElement;
+
+ document.title =
+ (title || filename || window.gUntitledString) +
+ windowTitle +
+ xulWin.getAttribute("titlemenuseparator") +
+ xulWin.getAttribute("titlemodifier");
+ } catch (e) {
+ dump(e);
+ }
+}
+
+function SaveRecentFilesPrefs(aTitle, aFileType) {
+ var curUrl = StripPassword(GetDocumentUrl());
+ var historyCount = Services.prefs.getIntPref("editor.history.url_maximum");
+
+ var titleArray = [];
+ var urlArray = [];
+ var typeArray = [];
+
+ if (historyCount && !IsUrlAboutBlank(curUrl) && GetScheme(curUrl) != "data") {
+ titleArray.push(aTitle);
+ urlArray.push(curUrl);
+ typeArray.push(aFileType);
+ }
+
+ for (let i = 0; i < historyCount && urlArray.length < historyCount; i++) {
+ let url = Services.prefs.getStringPref("editor.history_url_" + i, "");
+
+ // Continue if URL pref is missing because
+ // a URL not found during loading may have been removed
+
+ // Skip over current an "data" URLs
+ if (url && url != curUrl && GetScheme(url) != "data") {
+ let title = Services.prefs.getStringPref("editor.history_title_" + i, "");
+ let fileType = Services.prefs.getStringPref(
+ "editor.history_type_" + i,
+ ""
+ );
+ titleArray.push(title);
+ urlArray.push(url);
+ typeArray.push(fileType);
+ }
+ }
+
+ // Resave the list back to prefs in the new order
+ for (let i = 0; i < urlArray.length; i++) {
+ SetStringPref("editor.history_title_" + i, titleArray[i]);
+ SetStringPref("editor.history_url_" + i, urlArray[i]);
+ SetStringPref("editor.history_type_" + i, typeArray[i]);
+ }
+}
+
+function EditorInitFormatMenu() {
+ try {
+ InitObjectPropertiesMenuitem();
+ InitRemoveStylesMenuitems(
+ "removeStylesMenuitem",
+ "removeLinksMenuitem",
+ "removeNamedAnchorsMenuitem"
+ );
+ } catch (ex) {}
+}
+
+function InitObjectPropertiesMenuitem() {
+ // Set strings and enable for the [Object] Properties item
+ // Note that we directly do the enabling instead of
+ // using goSetCommandEnabled since we already have the command.
+ var cmd = document.getElementById("cmd_objectProperties");
+ if (!cmd) {
+ return null;
+ }
+
+ var element;
+ var menuStr = GetString("AdvancedProperties");
+ var name;
+
+ if (IsEditingRenderedHTML()) {
+ element = GetObjectForProperties();
+ }
+
+ if (element && element.nodeName) {
+ var objStr = "";
+ cmd.removeAttribute("disabled");
+ name = element.nodeName.toLowerCase();
+ switch (name) {
+ case "img":
+ // Check if img is enclosed in link
+ // (use "href" to not be fooled by named anchor)
+ try {
+ if (GetCurrentEditor().getElementOrParentByTagName("href", element)) {
+ objStr = GetString("ImageAndLink");
+ // Return "href" so it is detected as a link.
+ name = "href";
+ }
+ } catch (e) {}
+
+ if (objStr == "") {
+ objStr = GetString("Image");
+ }
+ break;
+ case "hr":
+ objStr = GetString("HLine");
+ break;
+ case "table":
+ objStr = GetString("Table");
+ break;
+ case "th":
+ name = "td";
+ // Falls through
+ case "td":
+ objStr = GetString("TableCell");
+ break;
+ case "ol":
+ case "ul":
+ case "dl":
+ objStr = GetString("List");
+ break;
+ case "li":
+ objStr = GetString("ListItem");
+ break;
+ case "form":
+ objStr = GetString("Form");
+ break;
+ case "input":
+ var type = element.getAttribute("type");
+ if (type && type.toLowerCase() == "image") {
+ objStr = GetString("InputImage");
+ } else {
+ objStr = GetString("InputTag");
+ }
+ break;
+ case "textarea":
+ objStr = GetString("TextArea");
+ break;
+ case "select":
+ objStr = GetString("Select");
+ break;
+ case "button":
+ objStr = GetString("Button");
+ break;
+ case "label":
+ objStr = GetString("Label");
+ break;
+ case "fieldset":
+ objStr = GetString("FieldSet");
+ break;
+ case "a":
+ if (element.name) {
+ objStr = GetString("NamedAnchor");
+ name = "anchor";
+ } else if (element.href) {
+ objStr = GetString("Link");
+ name = "href";
+ }
+ break;
+ }
+ if (objStr) {
+ menuStr = GetString("ObjectProperties").replace(/%obj%/, objStr);
+ }
+ } else {
+ // We show generic "Properties" string, but disable the command.
+ cmd.setAttribute("disabled", "true");
+ }
+ cmd.setAttribute("label", menuStr);
+ cmd.setAttribute("accesskey", GetString("ObjectPropertiesAccessKey"));
+ return name;
+}
+
+function InitParagraphMenu() {
+ var mixedObj = { value: null };
+ var state;
+ try {
+ state = GetCurrentEditor().getParagraphState(mixedObj);
+ } catch (e) {}
+ var IDSuffix;
+
+ // PROBLEM: When we get blockquote, it masks other styles contained by it
+ // We need a separate method to get blockquote state
+
+ // We use "x" as uninitialized paragraph state
+ if (!state || state == "x") {
+ // No paragraph container.
+ IDSuffix = "bodyText";
+ } else {
+ IDSuffix = state;
+ }
+
+ // Set "radio" check on one item, but...
+ var menuItem = document.getElementById("menu_" + IDSuffix);
+ menuItem.setAttribute("checked", "true");
+
+ // ..."bodyText" is returned if mixed selection, so remove checkmark
+ if (mixedObj.value) {
+ menuItem.setAttribute("checked", "false");
+ }
+}
+
+function GetListStateString() {
+ try {
+ var editor = GetCurrentEditor();
+
+ var mixedObj = { value: null };
+ var hasOL = { value: false };
+ var hasUL = { value: false };
+ var hasDL = { value: false };
+ editor.getListState(mixedObj, hasOL, hasUL, hasDL);
+
+ if (mixedObj.value) {
+ return "mixed";
+ }
+ if (hasOL.value) {
+ return "ol";
+ }
+ if (hasUL.value) {
+ return "ul";
+ }
+
+ if (hasDL.value) {
+ var hasLI = { value: false };
+ var hasDT = { value: false };
+ var hasDD = { value: false };
+ editor.getListItemState(mixedObj, hasLI, hasDT, hasDD);
+ if (mixedObj.value) {
+ return "mixed";
+ }
+ if (hasLI.value) {
+ return "li";
+ }
+ if (hasDT.value) {
+ return "dt";
+ }
+ if (hasDD.value) {
+ return "dd";
+ }
+ }
+ } catch (e) {}
+
+ // return "noList" if we aren't in a list at all
+ return "noList";
+}
+
+function InitListMenu() {
+ if (!IsHTMLEditor()) {
+ return;
+ }
+
+ var IDSuffix = GetListStateString();
+
+ // Set enable state for the "None" menuitem
+ goSetCommandEnabled("cmd_removeList", IDSuffix != "noList");
+
+ // Set "radio" check on one item, but...
+ // we won't find a match if it's "mixed"
+ var menuItem = document.getElementById("menu_" + IDSuffix);
+ if (menuItem) {
+ menuItem.setAttribute("checked", "true");
+ }
+}
+
+function GetAlignmentString() {
+ var mixedObj = { value: null };
+ var alignObj = { value: null };
+ try {
+ GetCurrentEditor().getAlignment(mixedObj, alignObj);
+ } catch (e) {}
+
+ if (mixedObj.value) {
+ return "mixed";
+ }
+ if (alignObj.value == nsIHTMLEditor.eLeft) {
+ return "left";
+ }
+ if (alignObj.value == nsIHTMLEditor.eCenter) {
+ return "center";
+ }
+ if (alignObj.value == nsIHTMLEditor.eRight) {
+ return "right";
+ }
+ if (alignObj.value == nsIHTMLEditor.eJustify) {
+ return "justify";
+ }
+
+ // return "left" if we got here
+ return "left";
+}
+
+function InitAlignMenu() {
+ if (!IsHTMLEditor()) {
+ return;
+ }
+
+ var IDSuffix = GetAlignmentString();
+
+ // we won't find a match if it's "mixed"
+ var menuItem = document.getElementById("menu_" + IDSuffix);
+ if (menuItem) {
+ menuItem.setAttribute("checked", "true");
+ }
+}
+
+function EditorSetDefaultPrefsAndDoctype() {
+ var editor = GetCurrentEditor();
+
+ var domdoc;
+ try {
+ domdoc = editor.document;
+ } catch (e) {
+ dump(e + "\n");
+ }
+ if (!domdoc) {
+ dump("EditorSetDefaultPrefsAndDoctype: EDITOR DOCUMENT NOT FOUND\n");
+ return;
+ }
+
+ // Insert a doctype element
+ // if it is missing from existing doc
+ if (!domdoc.doctype) {
+ var newdoctype = domdoc.implementation.createDocumentType(
+ "HTML",
+ "-//W3C//DTD HTML 4.01 Transitional//EN",
+ ""
+ );
+ if (newdoctype) {
+ domdoc.insertBefore(newdoctype, domdoc.firstChild);
+ }
+ }
+
+ // search for head; we'll need this for meta tag additions
+ let headelement = domdoc.querySelector("head");
+ if (!headelement) {
+ headelement = domdoc.createElement("head");
+ if (headelement) {
+ domdoc.insertAfter(headelement, domdoc.firstChild);
+ }
+ }
+
+ /* only set default prefs for new documents */
+ if (!IsUrlAboutBlank(GetDocumentUrl())) {
+ return;
+ }
+
+ // search for author meta tag.
+ // if one is found, don't do anything.
+ // if not, create one and make it a child of the head tag
+ // and set its content attribute to the value of the editor.author preference.
+
+ if (domdoc.querySelector("meta")) {
+ // we should do charset first since we need to have charset before
+ // hitting other 8-bit char in other meta tags
+ // grab charset pref and make it the default charset
+ var element;
+ var prefCharsetString = Services.prefs.getCharPref(
+ "intl.charset.fallback.override"
+ );
+ if (prefCharsetString) {
+ editor.documentCharacterSet = prefCharsetString;
+ }
+
+ // let's start by assuming we have an author in case we don't have the pref
+
+ var prefAuthorString = null;
+ let authorFound = domdoc.querySelector('meta[name="author"]');
+ try {
+ prefAuthorString = Services.prefs.getStringPref("editor.author");
+ } catch (ex) {}
+ if (
+ prefAuthorString &&
+ prefAuthorString != 0 &&
+ !authorFound &&
+ headelement
+ ) {
+ // create meta tag with 2 attributes
+ element = domdoc.createElement("meta");
+ if (element) {
+ element.setAttribute("name", "author");
+ element.setAttribute("content", prefAuthorString);
+ headelement.appendChild(element);
+ }
+ }
+ }
+
+ // add title tag if not present
+ if (headelement && !editor.document.querySelector("title")) {
+ var titleElement = domdoc.createElement("title");
+ if (titleElement) {
+ headelement.appendChild(titleElement);
+ }
+ }
+
+ // find body node
+ var bodyelement = GetBodyElement();
+ if (bodyelement) {
+ if (Services.prefs.getBoolPref("editor.use_custom_colors")) {
+ let text_color = Services.prefs.getCharPref("editor.text_color");
+ let background_color = Services.prefs.getCharPref(
+ "editor.background_color"
+ );
+
+ // add the color attributes to the body tag.
+ // and use them for the default text and background colors if not empty
+ editor.setAttributeOrEquivalent(bodyelement, "text", text_color, true);
+ gDefaultTextColor = text_color;
+ editor.setAttributeOrEquivalent(
+ bodyelement,
+ "bgcolor",
+ background_color,
+ true
+ );
+ gDefaultBackgroundColor = background_color;
+ bodyelement.setAttribute(
+ "link",
+ Services.prefs.getCharPref("editor.link_color")
+ );
+ bodyelement.setAttribute(
+ "alink",
+ Services.prefs.getCharPref("editor.active_link_color")
+ );
+ bodyelement.setAttribute(
+ "vlink",
+ Services.prefs.getCharPref("editor.followed_link_color")
+ );
+ }
+ // Default image is independent of Custom colors???
+ try {
+ let background_image = Services.prefs.getCharPref(
+ "editor.default_background_image"
+ );
+ if (background_image) {
+ editor.setAttributeOrEquivalent(
+ bodyelement,
+ "background",
+ background_image,
+ true
+ );
+ }
+ } catch (e) {
+ dump("BACKGROUND EXCEPTION: " + e + "\n");
+ }
+ }
+ // auto-save???
+}
+
+function GetBodyElement() {
+ try {
+ return GetCurrentEditor().rootElement;
+ } catch (ex) {
+ dump("no body tag found?!\n");
+ // better have one, how can we blow things up here?
+ }
+ return null;
+}
+
+// --------------------------- Logging stuff ---------------------------
+
+function EditorGetNodeFromOffsets(offsets) {
+ var node = null;
+ try {
+ node = GetCurrentEditor().document;
+
+ for (var i = 0; i < offsets.length; i++) {
+ node = node.childNodes[offsets[i]];
+ }
+ } catch (e) {}
+ return node;
+}
+
+function EditorSetSelectionFromOffsets(selRanges) {
+ try {
+ var editor = GetCurrentEditor();
+ var selection = editor.selection;
+ selection.removeAllRanges();
+
+ var rangeArr, start, end, node, offset;
+ for (var i = 0; i < selRanges.length; i++) {
+ rangeArr = selRanges[i];
+ start = rangeArr[0];
+ end = rangeArr[1];
+
+ var range = editor.document.createRange();
+
+ node = EditorGetNodeFromOffsets(start[0]);
+ offset = start[1];
+
+ range.setStart(node, offset);
+
+ node = EditorGetNodeFromOffsets(end[0]);
+ offset = end[1];
+
+ range.setEnd(node, offset);
+
+ selection.addRange(range);
+ }
+ } catch (e) {}
+}
+
+// --------------------------------------------------------------------
+function initFontStyleMenu(menuPopup) {
+ for (var i = 0; i < menuPopup.childNodes.length; i++) {
+ var menuItem = menuPopup.childNodes[i];
+ var theStyle = menuItem.getAttribute("state");
+ if (theStyle) {
+ menuItem.setAttribute("checked", theStyle);
+ }
+ }
+}
+
+// --------------------------------------------------------------------
+function onButtonUpdate(button, commmandID) {
+ var commandNode = document.getElementById(commmandID);
+ var state = commandNode.getAttribute("state");
+ button.checked = state == "true";
+}
+
+// --------------------------------------------------------------------
+function onStateButtonUpdate(button, commmandID, onState) {
+ var commandNode = document.getElementById(commmandID);
+ var state = commandNode.getAttribute("state");
+
+ button.checked = state == onState;
+}
+
+// --------------------------- Status calls ---------------------------
+function getColorAndSetColorWell(ColorPickerID, ColorWellID) {
+ var colorWell;
+ if (ColorWellID) {
+ colorWell = document.getElementById(ColorWellID);
+ }
+
+ var colorPicker = document.getElementById(ColorPickerID);
+ if (colorPicker) {
+ // Extract color from colorPicker and assign to colorWell.
+ var color = colorPicker.getAttribute("color");
+
+ if (colorWell && color) {
+ // Use setAttribute so colorwell can be a XUL element, such as button
+ colorWell.setAttribute("style", "background-color: " + color);
+ }
+ }
+ return color;
+}
+
+// -----------------------------------------------------------------------------------
+function IsSpellCheckerInstalled() {
+ return true; // Always installed.
+}
+
+// -----------------------------------------------------------------------------------
+function IsFindInstalled() {
+ return (
+ "@mozilla.org/embedcomp/rangefind;1" in Cc &&
+ "@mozilla.org/find/find_service;1" in Cc
+ );
+}
+
+// -----------------------------------------------------------------------------------
+function RemoveInapplicableUIElements() {
+ // For items that are in their own menu block, remove associated separator
+ // (we can't use "hidden" since class="hide-in-IM" CSS rule interferes)
+
+ // if no find, remove find ui
+ if (!IsFindInstalled()) {
+ HideItem("menu_find");
+ HideItem("menu_findnext");
+ HideItem("menu_replace");
+ HideItem("menu_find");
+ RemoveItem("sep_find");
+ }
+
+ // if no spell checker, remove spell checker ui
+ if (!IsSpellCheckerInstalled()) {
+ HideItem("spellingButton");
+ HideItem("menu_checkspelling");
+ RemoveItem("sep_checkspelling");
+ }
+
+ // Remove menu items (from overlay shared with HTML editor) in non-HTML.
+ if (!IsHTMLEditor()) {
+ HideItem("insertAnchor");
+ HideItem("insertImage");
+ HideItem("insertHline");
+ HideItem("insertTable");
+ HideItem("insertHTML");
+ HideItem("insertFormMenu");
+ HideItem("fileExportToText");
+ HideItem("viewEditModeToolbar");
+ }
+}
+
+function HideItem(id) {
+ var item = document.getElementById(id);
+ if (item) {
+ item.hidden = true;
+ }
+}
+
+function RemoveItem(id) {
+ var item = document.getElementById(id);
+ if (item) {
+ item.remove();
+ }
+}
+
+// Command Updating Strategy:
+// Don't update on on selection change, only when menu is displayed,
+// with this "oncreate" handler:
+function EditorInitTableMenu() {
+ try {
+ InitJoinCellMenuitem("menu_JoinTableCells");
+ } catch (ex) {}
+
+ // Set enable states for all table commands
+ goUpdateTableMenuItems(document.getElementById("composerTableMenuItems"));
+}
+
+function InitJoinCellMenuitem(id) {
+ // Change text on the "Join..." item depending if we
+ // are joining selected cells or just cell to right
+ // TODO: What to do about normal selection that crosses
+ // table border? Try to figure out all cells
+ // included in the selection?
+ var menuText;
+ var menuItem = document.getElementById(id);
+ if (!menuItem) {
+ return;
+ }
+
+ // Use "Join selected cells if there's more than 1 cell selected
+ var numSelected;
+ var foundElement;
+
+ try {
+ var tagNameObj = {};
+ var countObj = { value: 0 };
+ foundElement = GetCurrentTableEditor().getSelectedOrParentTableElement(
+ tagNameObj,
+ countObj
+ );
+ numSelected = countObj.value;
+ } catch (e) {}
+ if (foundElement && numSelected > 1) {
+ menuText = GetString("JoinSelectedCells");
+ } else {
+ menuText = GetString("JoinCellToRight");
+ }
+
+ menuItem.setAttribute("label", menuText);
+ menuItem.setAttribute("accesskey", GetString("JoinCellAccesskey"));
+}
+
+function InitRemoveStylesMenuitems(
+ removeStylesId,
+ removeLinksId,
+ removeNamedAnchorsId
+) {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ return;
+ }
+
+ // Change wording of menuitems depending on selection
+ var stylesItem = document.getElementById(removeStylesId);
+ var linkItem = document.getElementById(removeLinksId);
+
+ var isCollapsed = editor.selection.isCollapsed;
+ if (stylesItem) {
+ stylesItem.setAttribute(
+ "label",
+ isCollapsed ? GetString("StopTextStyles") : GetString("RemoveTextStyles")
+ );
+ stylesItem.setAttribute(
+ "accesskey",
+ GetString("RemoveTextStylesAccesskey")
+ );
+ }
+ if (linkItem) {
+ linkItem.setAttribute(
+ "label",
+ isCollapsed ? GetString("StopLinks") : GetString("RemoveLinks")
+ );
+ linkItem.setAttribute("accesskey", GetString("RemoveLinksAccesskey"));
+ // Note: disabling text style is a pain since there are so many - forget it!
+
+ // Disable if not in a link, but always allow "Remove"
+ // if selection isn't collapsed since we only look at anchor node
+ try {
+ SetElementEnabled(
+ linkItem,
+ !isCollapsed || editor.getElementOrParentByTagName("href", null)
+ );
+ } catch (e) {}
+ }
+ // Disable if selection is collapsed
+ SetElementEnabledById(removeNamedAnchorsId, !isCollapsed);
+}
+
+function goUpdateTableMenuItems(commandset) {
+ var editor = GetCurrentTableEditor();
+ if (!editor) {
+ dump("goUpdateTableMenuItems: too early, not initialized\n");
+ return;
+ }
+
+ var enabled = false;
+ var enabledIfTable = false;
+
+ var flags = editor.flags;
+ if (!(flags & Ci.nsIEditor.eEditorReadonlyMask) && IsEditingRenderedHTML()) {
+ var tagNameObj = { value: "" };
+ var element;
+ try {
+ element = editor.getSelectedOrParentTableElement(tagNameObj, {
+ value: 0,
+ });
+ } catch (e) {}
+
+ if (element) {
+ // Value when we need to have a selected table or inside a table
+ enabledIfTable = true;
+
+ // All others require being inside a cell or selected cell
+ enabled = tagNameObj.value == "td";
+ }
+ }
+
+ // Loop through command nodes
+ for (var i = 0; i < commandset.childNodes.length; i++) {
+ var commandID = commandset.childNodes[i].getAttribute("id");
+ if (commandID) {
+ if (
+ commandID == "cmd_InsertTable" ||
+ commandID == "cmd_JoinTableCells" ||
+ commandID == "cmd_SplitTableCell" ||
+ commandID == "cmd_ConvertToTable"
+ ) {
+ // Call the update method in the command class
+ goUpdateCommand(commandID);
+ } else if (
+ commandID == "cmd_DeleteTable" ||
+ commandID == "cmd_NormalizeTable" ||
+ commandID == "cmd_editTable" ||
+ commandID == "cmd_TableOrCellColor" ||
+ commandID == "cmd_SelectTable"
+ ) {
+ // Directly set with the values calculated here
+ goSetCommandEnabled(commandID, enabledIfTable);
+ } else {
+ goSetCommandEnabled(commandID, enabled);
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------------
+// Helpers for inserting and editing tables:
+
+function IsInTable() {
+ var editor = GetCurrentEditor();
+ try {
+ var flags = editor.flags;
+ return (
+ IsHTMLEditor() &&
+ !(flags & Ci.nsIEditor.eEditorReadonlyMask) &&
+ IsEditingRenderedHTML() &&
+ null != editor.getElementOrParentByTagName("table", null)
+ );
+ } catch (e) {}
+ return false;
+}
+
+function IsInTableCell() {
+ try {
+ var editor = GetCurrentEditor();
+ var flags = editor.flags;
+ return (
+ IsHTMLEditor() &&
+ !(flags & Ci.nsIEditor.eEditorReadonlyMask) &&
+ IsEditingRenderedHTML() &&
+ null != editor.getElementOrParentByTagName("td", null)
+ );
+ } catch (e) {}
+ return false;
+}
+
+function IsSelectionInOneCell() {
+ try {
+ var editor = GetCurrentEditor();
+ var selection = editor.selection;
+
+ if (selection.rangeCount == 1) {
+ // We have a "normal" single-range selection
+ if (
+ !selection.isCollapsed &&
+ selection.anchorNode != selection.focusNode
+ ) {
+ // Check if both nodes are within the same cell
+ var anchorCell = editor.getElementOrParentByTagName(
+ "td",
+ selection.anchorNode
+ );
+ var focusCell = editor.getElementOrParentByTagName(
+ "td",
+ selection.focusNode
+ );
+ return (
+ focusCell != null && anchorCell != null && focusCell == anchorCell
+ );
+ }
+ // Collapsed selection or anchor == focus (thus must be in 1 cell)
+ return true;
+ }
+ } catch (e) {}
+ return false;
+}
+
+// Call this with insertAllowed = true to allow inserting if not in existing table,
+// else use false to do nothing if not in a table
+function EditorInsertOrEditTable(insertAllowed) {
+ if (IsInTable()) {
+ // Edit properties of existing table
+ window.openDialog(
+ "chrome://editor/content/EdTableProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ "TablePanel"
+ );
+ gContentWindow.focus();
+ } else if (insertAllowed) {
+ try {
+ if (GetCurrentEditor().selection.isCollapsed) {
+ // If we have a caret, insert a blank table...
+ EditorInsertTable();
+ } else {
+ // Else convert the selection into a table.
+ goDoCommand("cmd_ConvertToTable");
+ }
+ } catch (e) {}
+ }
+}
+
+function EditorInsertTable() {
+ // Insert a new table
+ window.openDialog(
+ "chrome://editor/content/EdInsertTable.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ ""
+ );
+ gContentWindow.focus();
+}
+
+function EditorTableCellProperties() {
+ if (!IsHTMLEditor()) {
+ return;
+ }
+
+ try {
+ var cell = GetCurrentEditor().getElementOrParentByTagName("td", null);
+ if (cell) {
+ // Start Table Properties dialog on the "Cell" panel
+ window.openDialog(
+ "chrome://editor/content/EdTableProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ "CellPanel"
+ );
+ gContentWindow.focus();
+ }
+ } catch (e) {}
+}
+
+function GetNumberOfContiguousSelectedRows() {
+ if (!IsHTMLEditor()) {
+ return 0;
+ }
+
+ var rows = 0;
+ try {
+ var editor = GetCurrentTableEditor();
+ var rowObj = { value: 0 };
+ var colObj = { value: 0 };
+ var cell = editor.getFirstSelectedCellInTable(rowObj, colObj);
+ if (!cell) {
+ return 0;
+ }
+
+ // We have at least one row
+ rows++;
+
+ var lastIndex = rowObj.value;
+ do {
+ cell = editor.getNextSelectedCell({ value: 0 });
+ if (cell) {
+ editor.getCellIndexes(cell, rowObj, colObj);
+ var index = rowObj.value;
+ if (index == lastIndex + 1) {
+ lastIndex = index;
+ rows++;
+ }
+ }
+ } while (cell);
+ } catch (e) {}
+
+ return rows;
+}
+
+function GetNumberOfContiguousSelectedColumns() {
+ if (!IsHTMLEditor()) {
+ return 0;
+ }
+
+ var columns = 0;
+ try {
+ var editor = GetCurrentTableEditor();
+ var colObj = { value: 0 };
+ var rowObj = { value: 0 };
+ var cell = editor.getFirstSelectedCellInTable(rowObj, colObj);
+ if (!cell) {
+ return 0;
+ }
+
+ // We have at least one column
+ columns++;
+
+ var lastIndex = colObj.value;
+ do {
+ cell = editor.getNextSelectedCell({ value: 0 });
+ if (cell) {
+ editor.getCellIndexes(cell, rowObj, colObj);
+ var index = colObj.value;
+ if (index == lastIndex + 1) {
+ lastIndex = index;
+ columns++;
+ }
+ }
+ } while (cell);
+ } catch (e) {}
+
+ return columns;
+}
+
+function EditorOnFocus() {
+ // Current window already has the InsertCharWindow
+ if ("InsertCharWindow" in window && window.InsertCharWindow) {
+ return;
+ }
+
+ // Find window with an InsertCharsWindow and switch association to this one
+ var windowWithDialog = FindEditorWithInsertCharDialog();
+ if (windowWithDialog) {
+ // Switch the dialog to current window
+ // this sets focus to dialog, so bring focus back to editor window
+ if (SwitchInsertCharToThisWindow(windowWithDialog)) {
+ top.document.commandDispatcher.focusedWindow.focus();
+ }
+ }
+}
+
+function SwitchInsertCharToThisWindow(windowWithDialog) {
+ if (
+ windowWithDialog &&
+ "InsertCharWindow" in windowWithDialog &&
+ windowWithDialog.InsertCharWindow
+ ) {
+ // Move dialog association to the current window
+ window.InsertCharWindow = windowWithDialog.InsertCharWindow;
+ windowWithDialog.InsertCharWindow = null;
+
+ // Switch the dialog's opener to current window's
+ window.InsertCharWindow.opener = window;
+
+ // Bring dialog to the foreground
+ window.InsertCharWindow.focus();
+ return true;
+ }
+ return false;
+}
+
+function FindEditorWithInsertCharDialog() {
+ try {
+ // Find window with an InsertCharsWindow and switch association to this one
+ let enumerator = Services.wm.getEnumerator(null);
+
+ while (enumerator.hasMoreElements()) {
+ var tempWindow = enumerator.getNext();
+
+ if (
+ !tempWindow.closed &&
+ tempWindow != window &&
+ "InsertCharWindow" in tempWindow &&
+ tempWindow.InsertCharWindow
+ ) {
+ return tempWindow;
+ }
+ }
+ } catch (e) {}
+ return null;
+}
+
+function EditorFindOrCreateInsertCharWindow() {
+ if ("InsertCharWindow" in window && window.InsertCharWindow) {
+ window.InsertCharWindow.focus();
+ } else {
+ // Since we switch the dialog during EditorOnFocus(),
+ // this should really never be found, but it's good to be sure
+ var windowWithDialog = FindEditorWithInsertCharDialog();
+ if (windowWithDialog) {
+ SwitchInsertCharToThisWindow(windowWithDialog);
+ } else {
+ // The dialog will set window.InsertCharWindow to itself
+ window.openDialog(
+ "chrome://editor/content/EdInsertChars.xhtml",
+ "_blank",
+ "chrome,close,titlebar",
+ ""
+ );
+ }
+ }
+}
+
+// Find another HTML editor window to associate with the InsertChar dialog
+// or close it if none found (May be a mail composer)
+function SwitchInsertCharToAnotherEditorOrClose() {
+ if ("InsertCharWindow" in window && window.InsertCharWindow) {
+ var enumerator;
+ try {
+ enumerator = Services.wm.getEnumerator(null);
+ } catch (e) {}
+ if (!enumerator) {
+ return;
+ }
+
+ // TODO: Fix this to search for command controllers and look for "cmd_InsertChars"
+ // For now, detect just Web Composer and HTML Mail Composer
+ while (enumerator.hasMoreElements()) {
+ var tempWindow = enumerator.getNext();
+ if (
+ !tempWindow.closed &&
+ tempWindow != window &&
+ tempWindow != window.InsertCharWindow &&
+ "GetCurrentEditor" in tempWindow &&
+ tempWindow.GetCurrentEditor()
+ ) {
+ tempWindow.InsertCharWindow = window.InsertCharWindow;
+ window.InsertCharWindow = null;
+ tempWindow.InsertCharWindow.opener = tempWindow;
+ return;
+ }
+ }
+ // Didn't find another editor - close the dialog
+ window.InsertCharWindow.close();
+ }
+}
+
+function ResetStructToolbar() {
+ gLastFocusNode = null;
+ UpdateStructToolbar();
+}
+
+function newCommandListener(element) {
+ return function() {
+ return SelectFocusNodeAncestor(element);
+ };
+}
+
+function newContextmenuListener(button, element) {
+ /* globals InitStructBarContextMenu */ // SeaMonkey only.
+ return function() {
+ return InitStructBarContextMenu(button, element);
+ };
+}
+
+function UpdateStructToolbar() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ return;
+ }
+
+ var mixed = GetSelectionContainer();
+ if (!mixed) {
+ return;
+ }
+ var element = mixed.node;
+ var oneElementSelected = mixed.oneElementSelected;
+
+ if (!element) {
+ return;
+ }
+
+ if (
+ element == gLastFocusNode &&
+ oneElementSelected == gLastFocusNodeWasSelected
+ ) {
+ return;
+ }
+
+ gLastFocusNode = element;
+ gLastFocusNodeWasSelected = mixed.oneElementSelected;
+
+ var toolbar = document.getElementById("structToolbar");
+ if (!toolbar) {
+ return;
+ }
+ // We need to leave the <label> to flex the buttons to the left.
+ for (let node of toolbar.querySelectorAll("toolbarbutton,textbox")) {
+ node.remove();
+ }
+
+ toolbar.removeAttribute("label");
+
+ if (IsInHTMLSourceMode()) {
+ // we have destroyed the contents of the status bar and are
+ // about to recreate it ; but we don't want to do that in
+ // Source mode
+ return;
+ }
+
+ var tag, button;
+ var bodyElement = GetBodyElement();
+ var isFocusNode = true;
+ var tmp;
+ do {
+ tag = element.nodeName.toLowerCase();
+
+ button = document.createXULElement("toolbarbutton");
+ button.setAttribute("label", "<" + tag + ">");
+ button.setAttribute("value", tag);
+ button.setAttribute("context", "structToolbarContext");
+ button.className = "struct-button";
+
+ toolbar.insertBefore(button, toolbar.firstChild);
+
+ button.addEventListener("command", newCommandListener(element));
+
+ button.addEventListener(
+ "contextmenu",
+ newContextmenuListener(button, element)
+ );
+
+ if (isFocusNode && oneElementSelected) {
+ button.setAttribute("checked", "true");
+ isFocusNode = false;
+ }
+
+ tmp = element;
+ element = element.parentNode;
+ } while (element && tmp != bodyElement);
+}
+
+function SelectFocusNodeAncestor(element) {
+ var editor = GetCurrentEditor();
+ if (editor) {
+ if (element == GetBodyElement()) {
+ editor.selectAll();
+ } else {
+ editor.selectElement(element);
+ }
+ }
+ ResetStructToolbar();
+}
+
+function GetSelectionContainer() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ return null;
+ }
+
+ var selection;
+ try {
+ selection = editor.selection;
+ if (!selection) {
+ return null;
+ }
+ } catch (e) {
+ return null;
+ }
+
+ var result = { oneElementSelected: false };
+
+ if (selection.isCollapsed) {
+ result.node = selection.focusNode;
+ } else {
+ var rangeCount = selection.rangeCount;
+ if (rangeCount == 1) {
+ result.node = editor.getSelectedElement("");
+ var range = selection.getRangeAt(0);
+
+ // check for a weird case : when we select a piece of text inside
+ // a text node and apply an inline style to it, the selection starts
+ // at the end of the text node preceding the style and ends after the
+ // last char of the style. Assume the style element is selected for
+ // user's pleasure
+ if (
+ !result.node &&
+ range.startContainer.nodeType == Node.TEXT_NODE &&
+ range.startOffset == range.startContainer.length &&
+ range.endContainer.nodeType == Node.TEXT_NODE &&
+ range.endOffset == range.endContainer.length &&
+ range.endContainer.nextSibling == null &&
+ range.startContainer.nextSibling == range.endContainer.parentNode
+ ) {
+ result.node = range.endContainer.parentNode;
+ }
+
+ if (!result.node) {
+ // let's rely on the common ancestor of the selection
+ result.node = range.commonAncestorContainer;
+ } else {
+ result.oneElementSelected = true;
+ }
+ } else {
+ // assume table cells !
+ var i,
+ container = null;
+ for (i = 0; i < rangeCount; i++) {
+ range = selection.getRangeAt(i);
+ if (!container) {
+ container = range.startContainer;
+ } else if (container != range.startContainer) {
+ // all table cells don't belong to same row so let's
+ // select the parent of all rows
+ result.node = container.parentNode;
+ break;
+ }
+ result.node = container;
+ }
+ }
+ }
+
+ // make sure we have an element here
+ while (result.node.nodeType != Node.ELEMENT_NODE) {
+ result.node = result.node.parentNode;
+ }
+
+ // and make sure the element is not a special editor node like
+ // the <br> we insert in blank lines
+ // and don't select anonymous content !!! (fix for bug 190279)
+ while (
+ result.node.hasAttribute("_moz_editor_bogus_node") ||
+ editor.isAnonymousElement(result.node)
+ ) {
+ result.node = result.node.parentNode;
+ }
+
+ return result;
+}
+
+function FillInHTMLTooltipEditor(tooltip) {
+ const XLinkNS = "http://www.w3.org/1999/xlink";
+ var tooltipText = null;
+ var node;
+ if (IsInPreviewMode()) {
+ for (node = document.tooltipNode; node; node = node.parentNode) {
+ if (node.nodeType == Node.ELEMENT_NODE) {
+ tooltipText = node.getAttributeNS(XLinkNS, "title");
+ if (tooltipText && /\S/.test(tooltipText)) {
+ tooltip.setAttribute("label", tooltipText);
+ return true;
+ }
+ tooltipText = node.getAttribute("title");
+ if (tooltipText && /\S/.test(tooltipText)) {
+ tooltip.setAttribute("label", tooltipText);
+ return true;
+ }
+ }
+ }
+ } else {
+ for (node = document.tooltipNode; node; node = node.parentNode) {
+ if (
+ ChromeUtils.getClassName(node) === "HTMLImageElement" ||
+ ChromeUtils.getClassName(node) === "HTMLInputElement"
+ ) {
+ tooltipText = node.getAttribute("src");
+ } else if (ChromeUtils.getClassName(node) === "HTMLAnchorElement") {
+ tooltipText = node.getAttribute("href") || node.getAttribute("name");
+ }
+ if (tooltipText) {
+ tooltip.setAttribute("label", tooltipText);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+function UpdateTOC() {
+ window.openDialog(
+ "chrome://editor/content/EdInsertTOC.xhtml",
+ "_blank",
+ "chrome,close,modal,titlebar"
+ );
+ window.content.focus();
+}
+
+function InitTOCMenu() {
+ var elt = GetCurrentEditor().document.getElementById("mozToc");
+ var createMenuitem = document.getElementById("insertTOCMenuitem");
+ var updateMenuitem = document.getElementById("updateTOCMenuitem");
+ var removeMenuitem = document.getElementById("removeTOCMenuitem");
+ if (removeMenuitem && createMenuitem && updateMenuitem) {
+ if (elt) {
+ createMenuitem.setAttribute("disabled", "true");
+ updateMenuitem.removeAttribute("disabled");
+ removeMenuitem.removeAttribute("disabled");
+ } else {
+ createMenuitem.removeAttribute("disabled");
+ removeMenuitem.setAttribute("disabled", "true");
+ updateMenuitem.setAttribute("disabled", "true");
+ }
+ }
+}
+
+function RemoveTOC() {
+ var theDocument = GetCurrentEditor().document;
+ var elt = theDocument.getElementById("mozToc");
+ if (elt) {
+ elt.remove();
+ }
+
+ let anchorNodes = theDocument.querySelectorAll('a[name^="mozTocId"]');
+ for (let node of anchorNodes) {
+ if (node.parentNode) {
+ node.remove();
+ }
+ }
+}
diff --git a/comm/suite/editor/base/content/editor.xhtml b/comm/suite/editor/base/content/editor.xhtml
new file mode 100644
index 0000000000..033185592f
--- /dev/null
+++ b/comm/suite/editor/base/content/editor.xhtml
@@ -0,0 +1,402 @@
+<?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://editor/skin/" type="text/css"?>
+
+<?xml-stylesheet href="chrome://editor/skin/editorPrimaryToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/editorFormatToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/editorModeToolbar.css" type="text/css"?>
+<?xul-overlay href="chrome://editor/content/editorOverlay.xhtml"?>
+<?xul-overlay href="chrome://editor/content/editingOverlay.xhtml"?>
+<?xul-overlay href="chrome://editor/content/composerOverlay.xhtml"?>
+<?xul-overlay href="chrome://communicator/content/contentAreaContextOverlay.xhtml"?>
+<?xul-overlay href="chrome://editor/content/EditorContextMenuOverlay.xhtml"?>
+<?xul-overlay href="chrome://communicator/content/charsetOverlay.xhtml"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xhtml"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xhtml"?>
+<?xul-overlay href="chrome://communicator/content/sidebar/sidebarOverlay.xhtml"?>
+
+<!DOCTYPE window [
+<!ENTITY % editorDTD SYSTEM "chrome://editor/locale/editor.dtd" >
+%editorDTD;
+<!ENTITY % editorOverlayDTD SYSTEM "chrome://editor/locale/editorOverlay.dtd" >
+%editorOverlayDTD;
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+]>
+
+<window id="editorWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="EditorOnLoad()"
+ onunload="EditorShutdown()"
+ onclose="return Async.promiseSpinningly(EditorCanClose())"
+ onfocus="EditorOnFocus()"
+ title="&editorWindow.titlemodifier;"
+ titlemodifier="&editorWindow.titlemodifier;"
+ titlemenuseparator="&editorWindow.titlemodifiermenuseparator;"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ windowtype="composer:html"
+ macanimationtype="document"
+ drawtitle="true"
+ width="640" height="480"
+ screenX="10" screenY="10"
+ persist="screenX screenY width height sizemode">
+
+ <script src="chrome://editor/content/editor.js"/>
+ <script src="chrome://editor/content/publishprefs.js"/>
+ <script src="chrome://communicator/content/contentAreaClick.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+ <script src="chrome://global/content/nsDragAndDrop.js"/>
+
+ <popupset id="contentAreaContextSet"/>
+ <popupset id="editorPopupSet">
+ <menupopup id="structToolbarContext"/>
+ <menupopup id="sidebarPopup"/>
+ </popupset>
+
+ <commandset id="editorCommands">
+ <commandset id="commonEditorMenuItems"/>
+ <commandset id="composerMenuItems"/>
+ <commandset id="composerOnlyMenuItems"
+ commandupdater="true"
+ events="create, mode_switch"
+ oncommandupdate="goUpdateComposerMenuItems(this);">
+ <!-- file menu -->
+ <command id="cmd_exportToText"
+ label="&exportToTextCmd.label;"
+ accesskey="&exportToTextCmd.accesskey;"
+ oncommand="goDoCommand('cmd_exportToText');"/>
+ <command id="cmd_preview"
+ oncommand="goDoCommand('cmd_preview');"/>
+ <command id="cmd_editSendPage"
+ label="&sendPageCmd.label;"
+ accesskey="&sendPageCmd.accesskey;"
+ oncommand="goDoCommand('cmd_editSendPage');"/>
+ <!-- format menu -->
+ <command id="cmd_pageProperties"
+ oncommand="goDoCommand('cmd_pageProperties');"/>
+ <!-- tools menu -->
+ <command id="cmd_validate"
+ oncommand="goDoCommand('cmd_validate');"/>
+ <!-- toolbars -->
+ <command id="cmd_NormalMode"
+ oncommand="goDoCommand('cmd_NormalMode');"/>
+ <command id="cmd_AllTagsMode"
+ oncommand="goDoCommand('cmd_AllTagsMode');"/>
+ <command id="cmd_HTMLSourceMode"
+ oncommand="goDoCommand('cmd_HTMLSourceMode');"/>
+ <command id="cmd_PreviewMode"
+ oncommand="goDoCommand('cmd_PreviewMode');"/>
+ </commandset>
+ <commandset id="composerEditMenuItems"/>
+ <commandset id="composerSaveMenuItems"/>
+ <commandset id="composerStyleMenuItems">
+ <command id="cmd_updateStructToolbar"
+ oncommand="goDoCommand('cmd_updateStructToolbar');"/>
+ </commandset>
+ <commandset id="composerTableMenuItems"/>
+ <commandset id="composerListMenuItems"/>
+ <commandset id="tasksCommands"/>
+ <!-- view menu -->
+ <command id="cmd_viewEditModeToolbar"
+ oncommand="goToggleToolbar('EditModeToolbar','cmd_viewEditModeToolbar');"
+ checked="true"/>
+ </commandset>
+
+ <tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltipEditor(this);"/>
+
+ <!-- keys are appended from the overlay -->
+ <keyset id="editorKeys">
+ <keyset id="tasksKeys"/>
+ <key id="showHideSidebar"/>
+ <!-- eat these tab events here to stop focus from moving -->
+ <key keycode="VK_TAB" oncommand="return true;"/>
+ <key keycode="VK_TAB" modifiers="shift" oncommand="return true;"/>
+ <key keycode="VK_TAB" modifiers="control" oncommand="return true;"/>
+ <key keycode="VK_TAB" modifiers="control,shift" oncommand="return true;"/>
+ </keyset>
+
+ <vbox id="titlebar"/>
+
+<toolbox id="EditorToolbox"
+ class="toolbox-top"
+ mode="full"
+ defaultmode="full">
+ <toolbar id="toolbar-menubar"
+ type="menubar"
+ class="chromeclass-menubar"
+ persist="collapsed"
+ grippytooltiptext="&menuBar.tooltip;"
+ customizable="true"
+ defaultset="menubar-items"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu">
+ <toolbaritem id="menubar-items"
+ class="menubar-items"
+ align="center">
+ <menubar id="main-menubar">
+ <menu id="menu_File"/>
+ <menu id="menu_Edit"/>
+
+ <menu id="menu_View">
+ <!-- id pulls in "Show Sidebar" item from sidebarOverlay -->
+ <menupopup id="menu_View_Popup">
+ <menu id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup"
+ onpopupshowing="onViewToolbarsPopupShowing(event);"
+ oncommand="onViewToolbarCommand(event);">
+ <menuitem id="viewEditModeToolbar"
+ label="&editmodeToolbarCmd.label;"
+ accesskey="&editmodeToolbarCmd.accesskey;"
+ type="checkbox"
+ command="cmd_viewEditModeToolbar"/>
+ <menuitem id="menu_showTaskbar"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="viewSep1"/>
+ <menuitem id="viewNormalMode"
+ type="radio"
+ group="mode"
+ checked="true"
+ label="&NormalMode.label;"
+ accesskey="&NormalMode.accesskey;"
+ command="cmd_NormalMode"/>
+ <menuitem id="viewAllTagsMode"
+ type="radio"
+ group="mode"
+ label="&AllTagsMode.label;"
+ accesskey="&AllTagsMode.accesskey;"
+ command="cmd_AllTagsMode"/>
+ <menuitem id="viewSourceMode"
+ type="radio"
+ group="mode"
+ label="&HTMLSourceMode.label;"
+ accesskey="&HTMLSourceMode.accesskey;"
+ command="cmd_HTMLSourceMode"/>
+ <menuitem id="viewPreviewMode"
+ type="radio"
+ group="mode"
+ label="&PreviewMode.label;"
+ accesskey="&PreviewMode.accesskey;"
+ command="cmd_PreviewMode"/>
+ <menuseparator id="viewSep2"/>
+ <menu id="charsetMenu"
+ onpopupshowing="EditorUpdateCharsetMenu(event.target);"
+ oncommand="EditorSetCharacterSet(event);"/>
+ </menupopup>
+ </menu>
+
+ <menu id="insertMenu"/>
+
+ <menu id="formatMenu"
+ label="&formatMenu.label;"
+ accesskey="&formatMenu.accesskey;">
+ <menupopup id="formatMenuPopup">
+ <menuitem id="snapToGrid"
+ label="&grid.label;"
+ accesskey="&grid.accesskey;"
+ oncommand="goDoCommand('cmd_grid');"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuseparator/>
+ <menuitem id="objectProperties"/>
+ <menuitem id="colorsAndBackground"/>
+ <!-- Don't use 'observes', must call command correctly -->
+ <menuitem id="pageProperties"
+ label="&pageProperties.label;"
+ accesskey="&pageProperties.accesskey;"
+ oncommand="goDoCommand('cmd_pageProperties');"
+ observes="cmd_renderedHTMLEnabler"/>
+ </menupopup>
+ </menu>
+
+ <menu id="tableMenu"/>
+
+ <!-- tasks menu filled from tasksOverlay -->
+ <menu id="tasksMenu">
+ <menupopup id="taskPopup">
+ <menuitem id="menu_validate"
+ label="&validateCmd.label;"
+ accesskey="&validateCmd.accesskey;"
+ command="cmd_validate"/>
+ <menuseparator id="sep_validate"/>
+ </menupopup>
+ </menu>
+
+ <menu id="windowMenu"/>
+
+ <!-- help menu filled from globalOverlay -->
+ <menu id="menu_Help"/>
+ </menubar>
+ </toolbaritem>
+ </toolbar>
+
+ <!-- toolbar mostly filled out from editorOverlay -->
+ <!-- add class="standard" for dark blue background, icons need rework first -->
+ <toolbar id="EditToolbar"
+ class="chromeclass-toolbar toolbar-primary"
+ persist="collapsed"
+ grippytooltiptext="&compositionToolbar.tooltip;"
+ toolbarname="&compositionToolbarCmd.label;"
+ accesskey="&compositionToolbarCmd.accesskey;"
+ customizable="true"
+ defaultset="newButton,openButton,saveButton,publishButton,previewButton,print-button,separator,linkButton,imageButton,tableButton,spellingButton,spring,throbber-box"
+ context="toolbar-context-menu">
+ <toolbarbutton id="newButton"/>
+ <toolbarbutton id="openButton"/>
+ <toolbarbutton id="saveButton"/>
+ <toolbarbutton id="publishButton"/>
+ <toolbarbutton id="previewButton"
+ class="toolbarbutton-1"
+ label="&previewToolbarCmd.label;"
+ removable="true"
+ command="cmd_preview"
+ tooltiptext="&previewToolbarCmd.tooltip;"/>
+ <toolbarbutton id="cutButton"/>
+ <toolbarbutton id="copyButton"/>
+ <toolbarbutton id="pasteButton"/>
+ <toolbarbutton id="print-button"/>
+ <toolbarbutton id="findButton"/>
+ <toolbarbutton id="linkButton"/>
+ <toolbarbutton id="namedAnchorButton"/>
+ <toolbarbutton id="imageButton"/>
+ <toolbarbutton id="formButton"/>
+ <toolbarbutton id="hlineButton"/>
+ <toolbarbutton id="tableButton"/>
+ <toolbarbutton id="spellingButton"/>
+ <toolbaritem id="throbber-box"/>
+ </toolbar>
+
+ <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+
+ <toolbarpalette id="EditToolbarPalette"/>
+
+ <toolbar id="FormatToolbar"
+ class="chromeclass-toolbar"
+ persist="collapsed"
+ grippytooltiptext="&formatToolbar.tooltip;"
+ toolbarname="&formattingToolbarCmd.label;"
+ accesskey="&formattingToolbarCmd.accesskey;"
+ customizable="true"
+ defaultset="paragraph-select-container,color-buttons-container,HighlightColorButton,separator,DecreaseFontSizeButton,IncreaseFontSizeButton,separator,boldButton,italicButton,underlineButton,separator,ulButton,olButton,outdentButton,indentButton,separator,align-left-button,align-center-button,align-right-button,align-justify-button,absolutePositionButton,decreaseZIndexButton,increaseZIndexButton"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu"
+ nowindowdrag="true">
+ <!-- from editorOverlay -->
+ <toolbaritem id="paragraph-select-container"/>
+ <toolbaritem id="color-buttons-container"
+ disableoncustomize="true"/>
+ <toolbarbutton id="HighlightColorButton"/>
+ <!-- Enable if required for SeaMonkey.
+ <toolbarbutton id="AbsoluteFontSizeButton"/>
+ -->
+ <toolbarbutton id="DecreaseFontSizeButton"/>
+ <toolbarbutton id="IncreaseFontSizeButton"/>
+ <toolbarbutton id="boldButton"/>
+ <toolbarbutton id="italicButton"/>
+ <toolbarbutton id="underlineButton"/>
+ <toolbarbutton id="ulButton"/>
+ <toolbarbutton id="olButton"/>
+ <toolbarbutton id="outdentButton"/>
+ <toolbarbutton id="indentButton"/>
+ <toolbarbutton id="align-left-button"/>
+ <toolbarbutton id="align-center-button"/>
+ <toolbarbutton id="align-right-button"/>
+ <toolbarbutton id="align-justify-button"/>
+ <toolbarbutton id="absolutePositionButton"/>
+ <toolbarbutton id="decreaseZIndexButton"/>
+ <toolbarbutton id="increaseZIndexButton"/>
+
+ <!-- TODO: Change to a menulist? -->
+ <!-- menu>
+ <button id="AlignPopupButton"/>
+ <menupopup id="AlignmentPopup"/>
+ </menu -->
+
+
+ <spacer flex="1"/>
+ </toolbar>
+</toolbox>
+
+<!-- sidebar/toolbar/content/status -->
+<hbox id="sidebar-parent" flex="1">
+ <!-- From sidebarOverlay.xhtml -->
+ <vbox id="sidebar-box" class="chromeclass-extrachrome" hidden="true"/>
+ <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
+
+ <vbox id="appcontent" flex="1">
+ <deck id="ContentWindowDeck" selectedIndex="0" flex="1">
+ <vbox>
+ <findbar id="FindToolbar" browserid="content-frame"/>
+ <editor editortype="html"
+ type="content"
+ primary="true"
+ id="content-frame"
+ onclick="EditorClick(event);"
+ ondblclick="EditorDblClick(event);"
+ context="contentAreaContextMenu"
+ flex="1"
+ tooltip="aHTMLTooltip"/>
+ </vbox>
+ <vbox>
+ <label id="doctype-text" crop="right"/>
+ <editor type="content"
+ id="content-source"
+ context="contentAreaContextMenu"
+ flex="1"/>
+ </vbox>
+ </deck>
+
+ <!-- Edit Mode toolbar -->
+ <tabbox id="EditModeToolbar"
+ persist="collapsed">
+ <tabs id="EditModeTabs"
+ class="tabs-bottom"
+ flex="1"
+ onselect="this.selectedItem.doCommand();">
+ <tab id="NormalModeButton"
+ class="tab-bottom edit-mode"
+ label="&NormalModeTab.label;"
+ tooltiptext="&NormalMode.tooltip;"
+ command="cmd_NormalMode"/>
+ <tab id="TagModeButton"
+ class="tab-bottom edit-mode"
+ label="&AllTagsModeTab.label;"
+ tooltiptext="&AllTagsMode.tooltip;"
+ command="cmd_AllTagsMode"/>
+ <tab id="SourceModeButton"
+ class="tab-bottom edit-mode"
+ label="&HTMLSourceModeTab.label;"
+ tooltiptext="&HTMLSourceMode.tooltip;"
+ dir="&HTMLSourceModeTab.dir;"
+ command="cmd_HTMLSourceMode"/>
+ <tab id="PreviewModeButton"
+ class="tab-bottom edit-mode"
+ label="&PreviewModeTab.label;"
+ tooltiptext="&PreviewMode.tooltip;"
+ command="cmd_PreviewMode"/>
+ </tabs>
+ </tabbox>
+
+ </vbox> <!-- appcontent -->
+</hbox><!-- sidebar-parent -->
+
+ <!-- Some of this is from globalOverlay.xhtml -->
+ <hbox class="statusbar chromeclass-status" id="status-bar">
+ <statusbarpanel id="component-bar"/>
+ <hbox id="structToolbar" class="statusbarpanel" flex="1" pack="end">
+ <label id="structSpacer" value="" flex="1"/>
+ </hbox>
+ <statusbarpanel id="offline-status" class="statusbarpanel-iconic"/>
+ </hbox>
+</window>
diff --git a/comm/suite/editor/base/content/editorApplicationOverlay.js b/comm/suite/editor/base/content/editorApplicationOverlay.js
new file mode 100644
index 0000000000..17d02a510b
--- /dev/null
+++ b/comm/suite/editor/base/content/editorApplicationOverlay.js
@@ -0,0 +1,161 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/* Implementations of nsIControllerCommand for composer commands */
+
+function initEditorContextMenuItems(aEvent)
+{
+ var shouldShowEditPage = !gContextMenu.onImage && !gContextMenu.onLink && !gContextMenu.onTextInput && !gContextMenu.inDirList;
+ gContextMenu.showItem( "context-editpage", shouldShowEditPage );
+
+ var shouldShowEditLink = gContextMenu.onSaveableLink;
+ gContextMenu.showItem( "context-editlink", shouldShowEditLink );
+
+ // Hide the applications separator if there's no add-on apps present.
+ gContextMenu.showItem("context-sep-apps", gContextMenu.shouldShowSeparator("context-sep-apps"));
+}
+
+function initEditorContextMenuListener(aEvent)
+{
+ var popup = document.getElementById("contentAreaContextMenu");
+ if (popup)
+ popup.addEventListener("popupshowing", initEditorContextMenuItems);
+}
+
+addEventListener("load", initEditorContextMenuListener, false);
+
+function editDocument(aDocument)
+{
+ if (!aDocument)
+ aDocument = window.content.document;
+
+ editPage(aDocument.URL);
+}
+
+function editPageOrFrame()
+{
+ var focusedWindow = document.commandDispatcher.focusedWindow;
+
+ // if the uri is a specific frame, grab it, else use the frameset uri
+ // and let Composer handle error if necessary
+ editPage(getContentFrameURI(focusedWindow));
+}
+
+function getContentFrameURI(aFocusedWindow)
+{
+ let isContentFrame = aFocusedWindow ?
+ (aFocusedWindow.top == window.content) : false;
+
+ let contentFrame = isContentFrame ?
+ aFocusedWindow : window.content;
+
+ return contentFrame.location.href;
+}
+
+// Any non-editor window wanting to create an editor with a URL
+// should use this instead of "window.openDialog..."
+// We must always find an existing window with requested URL
+function editPage(url, aFileType)
+{
+ // aFileType is optional and needs to default to html.
+ aFileType = aFileType || "html";
+
+ // Always strip off "view-source:" and #anchors
+ url = url.replace(/^view-source:/, "").replace(/#.*/, "");
+
+ // if the current window is a browser window, then extract the current charset menu setting from the current
+ // document and use it to initialize the new composer window...
+
+ var wintype = document.documentElement.getAttribute('windowtype');
+ var charsetArg;
+
+ if (wintype == "navigator:browser" && content.document)
+ charsetArg = "charset=" + content.document.characterSet;
+
+ try {
+ let uri = createURI(url, null, null);
+
+ let enumerator = Services.wm.getEnumerator("composer:" + aFileType);
+ let emptyWindow;
+ while ( enumerator.hasMoreElements() )
+ {
+ var win = enumerator.getNext();
+ if (win && !win.closed && win.IsWebComposer())
+ {
+ if (CheckOpenWindowForURIMatch(uri, win))
+ {
+ // We found an editor with our url
+ win.focus();
+ return;
+ }
+ else if (!emptyWindow && win.PageIsEmptyAndUntouched())
+ {
+ emptyWindow = win;
+ }
+ }
+ }
+
+ if (emptyWindow)
+ {
+ // we have an empty window we can use
+ if (aFileType == "html" && emptyWindow.IsInHTMLSourceMode())
+ emptyWindow.SetEditMode(emptyWindow.PreviousNonSourceDisplayMode);
+ emptyWindow.EditorLoadUrl(url);
+ emptyWindow.focus();
+ emptyWindow.SetSaveAndPublishUI(url);
+ return;
+ }
+
+ // Create new Composer / Text Editor window.
+ if (aFileType == "text" && ("EditorNewPlaintext" in window))
+ EditorNewPlaintext(url, charsetArg);
+ else
+ NewEditorWindow(url, charsetArg);
+
+ } catch(e) {}
+}
+
+function createURI(urlstring)
+{
+ try {
+ return Services.io.newURI(urlstring);
+ } catch (e) {}
+
+ return null;
+}
+
+function CheckOpenWindowForURIMatch(uri, win)
+{
+ try {
+ return createURI(win.content.document.URL).equals(uri);
+ } catch (e) {}
+
+ return false;
+}
+
+function toEditor()
+{
+ if (!CycleWindow("composer:html"))
+ NewEditorWindow();
+}
+
+function NewEditorWindow(aUrl, aCharsetArg)
+{
+ window.openDialog("chrome://editor/content",
+ "_blank",
+ "chrome,all,dialog=no",
+ aUrl || "about:blank",
+ aCharsetArg);
+}
+
+function NewEditorFromTemplate()
+{
+ // XXX not implemented
+}
+
+function NewEditorFromDraft()
+{
+ // XXX not implemented
+}
diff --git a/comm/suite/editor/base/content/editorOverlay.xhtml b/comm/suite/editor/base/content/editorOverlay.xhtml
new file mode 100644
index 0000000000..7a5d3900f0
--- /dev/null
+++ b/comm/suite/editor/base/content/editorOverlay.xhtml
@@ -0,0 +1,1504 @@
+<?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 % editorOverlayDTD SYSTEM "chrome://editor/locale/editorOverlay.dtd">
+%editorOverlayDTD;
+<!ENTITY % editorSmileyOverlayDTD SYSTEM
+ "chrome://editor/locale/editorSmileyOverlay.dtd">
+%editorSmileyOverlayDTD;
+]>
+
+<overlay id="editorOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+<script src="chrome://editor/content/editorUtilities.js"/>
+<script src="chrome://editor/content/ComposerCommands.js"/>
+
+ <keyset id="editorKeys">
+ <!-- defined in globalOverlay -->
+ <key id="key_newNavigator"/>
+ <key id="key_newPrivateWindow"/>
+ <key id="key_newBlankPage"/>
+ <key id="key_save"
+ key="&saveCmd.key;"
+ command="cmd_save"
+ modifiers="accel"/>
+ <key id="key_print"/>
+ <key id="key_close"/>
+ <key id="key_undo"/>
+ <key id="key_redo"/>
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"/>
+ <key id="key_delete"/>
+ <key id="key_delete2"/>
+ <key id="key_selectAll"/>
+ <key id="pastequotationkb"
+ key="&pasteAsQuotationCmd.key;"
+ command="cmd_pasteQuote"
+ modifiers="accel, shift"/>
+ <key id="pastenoformattingkb"
+ key="&pasteNoFormatting.key;"
+ command="cmd_pasteNoFormatting"
+ modifiers="accel, shift"/>
+ <key id="key_rewrap"
+ key="&editRewrapCmd.key;"
+ command="cmd_rewrap"
+ modifiers="accel"/>
+ <keyset id="findKeys"/>
+ <key id="key_checkspelling"
+ key="&checkSpellingCmd2.key;"
+ command="cmd_spelling"
+ modifiers="accel,shift"/>
+
+ <key id="boldkb"
+ key="&styleBoldCmd.key;"
+ command="cmd_bold"
+ modifiers="accel"/>
+ <key id="italickb"
+ key="&styleItalicCmd.key;"
+ command="cmd_italic"
+ modifiers="accel"/>
+ <key id="underlinekb"
+ key="&styleUnderlineCmd.key;"
+ command="cmd_underline"
+ modifiers="accel"/>
+ <key id="fixedwidthkb"
+ key="&fontFixedWidth.key;"
+ command="cmd_tt"
+ modifiers="accel"/>
+
+ <key id="increaseindentkb"
+ key="&increaseIndent.key;"
+ command="cmd_indent"
+ modifiers="accel"/>
+ <key id="decreaseindentkb"
+ key="&decreaseIndent.key;"
+ command="cmd_outdent"
+ modifiers="accel"/>
+
+ <key id="removestyleskb"
+ key="&formatRemoveStyles.key;"
+ command="cmd_removeStyles"
+ modifiers="accel, shift"/>
+ <key id="removestyleskb2"
+ key=" "
+ command="cmd_removeStyles"
+ modifiers="accel"/>
+ <key id="removelinkskb"
+ key="&formatRemoveLinks.key;"
+ command="cmd_removeLinks"
+ modifiers="accel, shift"/>
+ <key id="removenamedanchorskb"
+ key="&formatRemoveNamedAnchors2.key;"
+ command="cmd_removeNamedAnchors"
+ modifiers="accel, shift"/>
+ <key id="decreasefontsizekb"
+ key="&decrementFontSize.key;"
+ command="cmd_decreaseFontStep"
+ modifiers="accel"/>
+ <key id="increasefontsizekb"
+ key="&incrementFontSize.key;"
+ command="cmd_increaseFontStep"
+ modifiers="accel"/>
+ <key key="&incrementFontSize.key;"
+ command="cmd_increaseFontStep"
+ modifiers="accel,shift"/>
+ <key key="&incrementFontSize.key2;"
+ command="cmd_increaseFontStep"
+ modifiers="accel"/>
+
+ <key id="insertlinkkb"
+ key="&insertLinkCmd2.key;"
+ command="cmd_link"
+ modifiers="accel"/>
+ </keyset>
+
+ <!-- commands updated when the editor gets created -->
+ <commandset id="commonEditorMenuItems"
+ commandupdater="true"
+ events="create"
+ oncommandupdate="goUpdateComposerMenuItems(this)">
+ <command id="cmd_printSetup" oncommand="goDoCommand('cmd_printSetup');"/>
+ <command id="cmd_printpreview" oncommand="goDoCommand('cmd_printpreview');"/>
+ <command id="cmd_print" oncommand="goDoCommand('cmd_print');"/>
+ <command id="cmd_close" oncommand="goDoCommand('cmd_close');"/>
+ </commandset>
+
+ <commandset id="composerMenuItems"
+ commandupdater="true"
+ events="create, mode_switch"
+ oncommandupdate="goUpdateComposerMenuItems(this)">
+ <!-- format menu -->
+ <command id="cmd_listProperties" oncommand="goDoCommand('cmd_listProperties')"/>
+ <command id="cmd_colorProperties" oncommand="goDoCommand('cmd_colorProperties')"/>
+
+ <command id="cmd_link" oncommand="goDoCommand('cmd_link')"/>
+ <command id="cmd_anchor" oncommand="goDoCommand('cmd_anchor')"/>
+ <command id="cmd_image" oncommand="goDoCommand('cmd_image')"/>
+ <command id="cmd_hline" oncommand="goDoCommand('cmd_hline')"/>
+ <command id="cmd_table" oncommand="goDoCommand('cmd_table')"/>
+ <command id="cmd_objectProperties" oncommand="goDoCommand('cmd_objectProperties')"/>
+ <command id="cmd_insertChars" oncommand="goDoCommand('cmd_insertChars')" label="&insertCharsCmd.label;"/>
+ <command id="cmd_insertHTMLWithDialog" oncommand="goDoCommand('cmd_insertHTMLWithDialog')" label="&insertHTMLCmd.label;"/>
+ <command id="cmd_insertMathWithDialog" oncommand="goDoCommand('cmd_insertMathWithDialog')" label="&insertMathCmd.label;"/>
+ <command id="cmd_insertBreak" oncommand="goDoCommand('cmd_insertBreak')"/>
+ <command id="cmd_insertBreakAll" oncommand="goDoCommand('cmd_insertBreakAll')"/>
+
+ <!-- only used in context popup menu -->
+ <command id="cmd_editLink" oncommand="goDoCommand('cmd_editLink')"/>
+
+ <!-- dummy command used just to disable things in non-HTML modes -->
+ <command id="cmd_renderedHTMLEnabler"/>
+ </commandset>
+
+ <!-- edit menu commands. These get updated by code in globalOverlay.js -->
+ <commandset id="composerEditMenuItems"
+ commandupdater="true"
+ events="create, mode_switch"
+ oncommandupdate="goUpdateComposerMenuItems(this)">
+ <command id="cmd_undo"/>
+ <command id="cmd_redo"/>
+ <command id="cmd_cut"/>
+ <command id="cmd_copy"/>
+ <command id="cmd_paste"/>
+ <command id="cmd_pasteNoFormatting"
+ label="&pasteNoFormatting.label;"
+ accesskey="&pasteNoFormatting.accesskey;"
+ oncommand="goDoCommand('cmd_pasteNoFormatting');"/>
+ <command id="cmd_delete"/>
+ <command id="cmd_selectAll"/>
+ <command id="cmd_preferences" oncommand="goDoCommand('cmd_preferences')"/>
+ <command id="cmd_findReplace" oncommand="goDoCommand('cmd_findReplace')"/>
+ <command id="cmd_find" oncommand="goDoCommand('cmd_find')"/>
+ <command id="cmd_findNext" oncommand="goDoCommand('cmd_findNext');"/>
+ <command id="cmd_findPrev" oncommand="goDoCommand('cmd_findPrev');"/>
+ <command id="cmd_spelling" oncommand="goDoCommand('cmd_spelling')"/>
+ <command id="cmd_pasteQuote"
+ label="&pasteAsQuotationCmd.label;"
+ accesskey="&pasteAsQuotationCmd.accesskey;"
+ oncommand="goDoCommand('cmd_pasteQuote');"/>
+ <command id="cmd_rewrap" oncommand="goDoCommand('cmd_rewrap');"/>
+ </commandset>
+
+ <!-- style related commands that update on creation, and on selection change -->
+ <commandset id="composerStyleMenuItems"
+ commandupdater="true"
+ events="create, style, mode_switch"
+ oncommandupdate="goUpdateComposerMenuItems(this)">
+ <command id="cmd_bold" state="false" oncommand="doStyleUICommand('cmd_bold')"/>
+ <command id="cmd_italic" state="false" oncommand="doStyleUICommand('cmd_italic')"/>
+ <command id="cmd_underline" state="false" oncommand="doStyleUICommand('cmd_underline')"/>
+ <command id="cmd_tt" state="false" oncommand="doStyleUICommand('cmd_tt')"/>
+ <command id="cmd_smiley"/>
+ <command id="cmd_strikethrough" state="false" oncommand="doStyleUICommand('cmd_strikethrough');"/>
+ <command id="cmd_superscript" state="false" oncommand="doStyleUICommand('cmd_superscript');"/>
+ <command id="cmd_subscript" state="false" oncommand="doStyleUICommand('cmd_subscript');"/>
+ <command id="cmd_nobreak" state="false" oncommand="doStyleUICommand('cmd_nobreak');"/>
+ <command id="cmd_em" state="false" oncommand="doStyleUICommand('cmd_em')"/>
+ <command id="cmd_strong" state="false" oncommand="doStyleUICommand('cmd_strong')"/>
+ <command id="cmd_cite" state="false" oncommand="doStyleUICommand('cmd_cite')"/>
+ <command id="cmd_abbr" state="false" oncommand="doStyleUICommand('cmd_abbr')"/>
+ <command id="cmd_acronym" state="false" oncommand="doStyleUICommand('cmd_acronym')"/>
+ <command id="cmd_code" state="false" oncommand="doStyleUICommand('cmd_code')"/>
+ <command id="cmd_samp" state="false" oncommand="doStyleUICommand('cmd_samp')"/>
+ <command id="cmd_var" state="false" oncommand="doStyleUICommand('cmd_var')"/>
+ <command id="cmd_ul" state="false" oncommand="doStyleUICommand('cmd_ul')"/>
+ <command id="cmd_ol" state="false" oncommand="doStyleUICommand('cmd_ol')"/>
+ <command id="cmd_indent" oncommand="goDoCommand('cmd_indent')"/>
+ <command id="cmd_outdent" oncommand="goDoCommand('cmd_outdent')"/>
+
+ <!-- the state attribute gets filled with the paragraph format before the command is executed -->
+ <command id="cmd_paragraphState" state="" oncommand="doStatefulCommand('cmd_paragraphState', event.target.value)"/>
+ <command id="cmd_fontFace" state="" oncommand="doStatefulCommand('cmd_fontFace', event.target.value)"/>
+
+ <!-- No "oncommand", use EditorSelectColor() to bring up color dialog -->
+ <command id="cmd_fontColor" state=""/>
+ <command id="cmd_backgroundColor" state=""/>
+ <command id="cmd_highlight" state="transparent" oncommand="EditorSelectColor('Highlight', event);"/>
+ <command id="cmd_fontSize" oncommand="goDoCommand('cmd_fontSize')"/>
+ <command id="cmd_align" state=""/>
+ <command id="cmd_absPos" state="" oncommand="goDoCommand('cmd_absPos')"/>
+ <command id="cmd_increaseZIndex" state="" oncommand="goDoCommand('cmd_increaseZIndex')"/>
+ <command id="cmd_decreaseZIndex" state="" oncommand="goDoCommand('cmd_decreaseZIndex')"/>
+ <command id="cmd_advancedProperties" oncommand="goDoCommand('cmd_advancedProperties')"/>
+ <command id="cmd_increaseFontStep" oncommand="goDoCommand('cmd_increaseFontStep')"/>
+ <command id="cmd_decreaseFontStep" oncommand="goDoCommand('cmd_decreaseFontStep')"/>
+ <command id="cmd_removeStyles" oncommand="goDoCommand('cmd_removeStyles')"/>
+ <command id="cmd_removeLinks" oncommand="goDoCommand('cmd_removeLinks')"/>
+ <command id="cmd_removeNamedAnchors" oncommand="goDoCommand('cmd_removeNamedAnchors')"/>
+ </commandset>
+
+ <!-- commands updated only when the menu gets created -->
+ <commandset id="composerListMenuItems"
+ commandupdater="true"
+ events="create, mode_switch"
+ oncommandupdate="goUpdateComposerMenuItems(this)">
+ <!-- List menu -->
+ <command id="cmd_dt" oncommand="doStyleUICommand('cmd_dt')"/>
+ <command id="cmd_dd" oncommand="doStyleUICommand('cmd_dd')"/>
+ <command id="cmd_removeList" oncommand="goDoCommand('cmd_removeList')"/>
+ <!-- cmd_ul and cmd_ol are shared with toolbar and are in composerStyleMenuItems commandset -->
+ </commandset>
+
+ <commandset id="composerTableMenuItems"
+ commandupdater="true"
+ events="create, mode_switch"
+ oncommandupdate="goUpdateTableMenuItems(this)">
+ <!-- Table menu -->
+ <command id="cmd_SelectTable" oncommand="goDoCommand('cmd_SelectTable')"/>
+ <command id="cmd_SelectRow" oncommand="goDoCommand('cmd_SelectRow')"/>
+ <command id="cmd_SelectColumn" oncommand="goDoCommand('cmd_SelectColumn')"/>
+ <command id="cmd_SelectCell" oncommand="goDoCommand('cmd_SelectCell')"/>
+ <command id="cmd_SelectAllCells" oncommand="goDoCommand('cmd_SelectAllCells')"/>
+ <command id="cmd_InsertTable" oncommand="goDoCommand('cmd_InsertTable')"/>
+ <command id="cmd_InsertRowAbove" oncommand="goDoCommand('cmd_InsertRowAbove')"/>
+ <command id="cmd_InsertRowBelow" oncommand="goDoCommand('cmd_InsertRowBelow')"/>
+ <command id="cmd_InsertColumnBefore" oncommand="goDoCommand('cmd_InsertColumnBefore')"/>
+ <command id="cmd_InsertColumnAfter" oncommand="goDoCommand('cmd_InsertColumnAfter')"/>
+ <command id="cmd_InsertCellBefore" oncommand="goDoCommand('cmd_InsertCellBefore')"/>
+ <command id="cmd_InsertCellAfter" oncommand="goDoCommand('cmd_InsertCellAfter')"/>
+ <command id="cmd_DeleteTable" oncommand="goDoCommand('cmd_DeleteTable')"/>
+ <command id="cmd_DeleteRow" oncommand="goDoCommand('cmd_DeleteRow')"/>
+ <command id="cmd_DeleteColumn" oncommand="goDoCommand('cmd_DeleteColumn')"/>
+ <command id="cmd_DeleteCell" oncommand="goDoCommand('cmd_DeleteCell')"/>
+ <command id="cmd_DeleteCellContents" oncommand="goDoCommand('cmd_DeleteCellContents')"/>
+ <command id="cmd_NormalizeTable" oncommand="goDoCommand('cmd_NormalizeTable')"/>
+ <command id="cmd_JoinTableCells" oncommand="goDoCommand('cmd_JoinTableCells')"/>
+ <command id="cmd_SplitTableCell" oncommand="goDoCommand('cmd_SplitTableCell')"/>
+ <command id="cmd_ConvertToTable" oncommand="goDoCommand('cmd_ConvertToTable')"/>
+ <command id="cmd_TableOrCellColor" oncommand="goDoCommand('cmd_TableOrCellColor')"/>
+ <command id="cmd_editTable" oncommand="goDoCommand('cmd_editTable')"/>
+ </commandset>
+
+ <commandset id="editorCommands">
+ <commandset id="globalEditMenuItems"/>
+ <commandset id="selectEditMenuItems"/>
+ <commandset id="undoEditMenuItems"/>
+ <commandset id="clipboardEditMenuItems"/>
+ <command id="toggleSidebar"/>
+ <!-- file menu -->
+ <command id="cmd_newNavigator"/>
+ <command id="cmd_newPrivateWindow"/>
+ <command id="cmd_newEditor"/>
+ <command id="cmd_newEditorTemplate"/>
+ <command id="cmd_newEditorDraft"/>
+ </commandset>
+
+ <popupset id="editorPopupSet">
+ <menupopup id="popupNotificationMenu"/>
+ <menupopup id="toolbar-context-menu"/>
+ <panel id="customizeToolbarSheetPopup"/>
+ </popupset>
+
+ <menu id="menu_Edit">
+ <menupopup id="menu_EditPopup">
+ <!-- from utilityOverlay.xhtml -->
+ <menuitem id="menu_undo"/>
+ <menuitem id="menu_redo"/>
+ <menuseparator id="sep_cut"/>
+ <menuitem id="menu_cut"/>
+ <menuitem id="menu_copy"/>
+ <menuitem id="menu_paste"/>
+ <menuitem id="menu_pasteNoFormatting"
+ key="pastenoformattingkb"
+ command="cmd_pasteNoFormatting"/>
+ <menuitem id="menu_pasteQuote"
+ key="pastequotationkb"
+ command="cmd_pasteQuote"/>
+ <menuitem id="menu_rewrap"
+ label="&editRewrapCmd.label;"
+ accesskey="&editRewrapCmd.accesskey;"
+ key="key_rewrap"
+ command="cmd_rewrap"/>
+ <menuitem id="menu_delete"/>
+ <menuseparator id="sep_selectAll"/>
+ <menuitem id="menu_selectAll"/>
+ <menuseparator id="sep_find"/>
+ <menuitem id="menu_find"
+ label="&findBarCmd.label;"/>
+ <menuitem id="menu_findReplace"
+ label="&findReplaceCmd.label;"/>
+ <menuitem id="menu_findNext"/>
+ <menuitem id="menu_findPrev"/>
+ <menuseparator id="sep_checkspelling"/>
+ <menuitem id="menu_checkspelling"
+ label="&checkSpellingCmd2.label;"
+ accesskey="&checkSpellingCmd2.accesskey;"
+ key="key_checkspelling"
+ command="cmd_spelling"/>
+ <menuitem id="menu_inlineSpellCheck"
+ type="checkbox"
+ label="&enableInlineSpellChecker.label;"
+ accesskey="&enableInlineSpellChecker.accesskey;"/>
+ <menuseparator id="sep_preferences"/>
+ <menuitem id="menu_preferences"
+ command="cmd_preferences"/>
+ </menupopup>
+ </menu>
+
+ <!-- Insert menu -->
+ <menu id="insertMenu"
+ label="&insertMenu.label;"
+ accesskey="&insertMenu.accesskey;">
+ <menupopup id="insertMenuPopup">
+ <menuitem id="insertImage"
+ label="&insertImageCmd.label;"
+ accesskey="&insertImageCmd.accesskey;"
+ command="cmd_image"/>
+ <menuitem id="insertTable"
+ label="&insertTableCmd.label;"
+ accesskey="&insertTableCmd.accesskey;"
+ command="cmd_InsertTable"/>
+ <menuitem id="insertLink"
+ label="&insertLinkCmd2.label;"
+ accesskey="&insertLinkCmd2.accesskey;"
+ command="cmd_link"
+ key="insertlinkkb"/>
+ <menuitem id="insertAnchor"
+ label="&insertAnchorCmd.label;"
+ accesskey="&insertAnchorCmd.accesskey;"
+ command="cmd_anchor"/>
+ <menuitem id="insertHline"
+ label="&insertHLineCmd.label;"
+ accesskey="&insertHLineCmd.accesskey;"
+ command="cmd_hline"/>
+ <menuitem id="insertHTMLSource"
+ accesskey="&insertHTMLCmd.accesskey;"
+ command="cmd_insertHTMLWithDialog"/>
+ <menuitem id="insertMath"
+ accesskey="&insertMathCmd.accesskey;"
+ command="cmd_insertMathWithDialog"/>
+ <menuitem id="insertChars"
+ accesskey="&insertCharsCmd.accesskey;"
+ command="cmd_insertChars"/>
+ <menu id="insertTOC" label="&tocMenu.label;" accesskey="&tocMenu.accesskey;">
+ <menupopup id="insertTOCPopup" onpopupshowing="InitTOCMenu()">
+ <menuitem id="insertTOCMenuitem"
+ label="&insertTOC.label;"
+ accesskey="&insertTOC.accesskey;"
+ oncommand="UpdateTOC()"/>
+ <menuitem id="updateTOCMenuitem"
+ label="&updateTOC.label;"
+ accesskey="&updateTOC.accesskey;"
+ oncommand="UpdateTOC()"/>
+ <menuitem id="removeTOCMenuitem"
+ label="&removeTOC.label;"
+ accesskey="&removeTOC.accesskey;"
+ oncommand="RemoveTOC()"/>
+ </menupopup>
+ </menu>
+ <menu id="insertSmiley"
+ label="&insertSmiley.label;"
+ accesskey="&insertSmiley.accesskey;">
+ <menupopup id="smilyMenuPopup">
+ <menuitem class="smiley insert-smile menuitem-iconic"
+ label="&smiley1Cmd.label;"
+ accesskey="&smiley1Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-)');"/>
+ <menuitem class="smiley insert-frown menuitem-iconic"
+ label="&smiley2Cmd.label;"
+ accesskey="&smiley2Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-(');"/>
+ <menuitem class="smiley insert-wink menuitem-iconic"
+ label="&smiley3Cmd.label;"
+ accesskey="&smiley3Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ';-)');"/>
+ <menuitem class="smiley insert-tongue menuitem-iconic"
+ label="&smiley4Cmd.label;"
+ accesskey="&smiley4Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-P');"/>
+ <menuitem class="smiley insert-laughing menuitem-iconic"
+ label="&smiley5Cmd.label;"
+ accesskey="&smiley5Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-D');"/>
+ <menuitem class="smiley insert-embarrassed menuitem-iconic"
+ label="&smiley6Cmd.label;"
+ accesskey="&smiley6Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-[');"/>
+ <menuitem class="smiley insert-undecided menuitem-iconic"
+ label="&smiley7Cmd.label;"
+ accesskey="&smiley7Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-\\');"/>
+ <menuitem class="smiley insert-surprise menuitem-iconic"
+ label="&smiley8Cmd.label;"
+ accesskey="&smiley8Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', '=-O');"/>
+ <menuitem class="smiley insert-kiss menuitem-iconic"
+ label="&smiley9Cmd.label;"
+ accesskey="&smiley9Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-*');"/>
+ <menuitem class="smiley insert-yell menuitem-iconic"
+ label="&smiley10Cmd.label;"
+ accesskey="&smiley10Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', '>:o');"/>
+ <menuitem class="smiley insert-cool menuitem-iconic"
+ label="&smiley11Cmd.label;"
+ accesskey="&smiley11Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', '8-)');"/>
+ <menuitem class="smiley insert-money menuitem-iconic"
+ label="&smiley12Cmd.label;"
+ accesskey="&smiley12Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-$');"/>
+ <menuitem class="smiley insert-foot menuitem-iconic"
+ label="&smiley13Cmd.label;"
+ accesskey="&smiley13Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-!');"/>
+ <menuitem class="smiley insert-innocent menuitem-iconic"
+ label="&smiley14Cmd.label;"
+ accesskey="&smiley14Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', 'O:-)');"/>
+ <menuitem class="smiley insert-cry menuitem-iconic"
+ label="&smiley15Cmd.label;"
+ accesskey="&smiley15Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':\'(');"/>
+ <menuitem class="smiley insert-sealed menuitem-iconic"
+ label="&smiley16Cmd.label;"
+ accesskey="&smiley16Cmd.accesskey;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-X');"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="insertMenuSeparator"/>
+ <menuitem id="insertBreakAll"
+ accesskey="&insertBreakAllCmd.accesskey;"
+ command="cmd_insertBreakAll"
+ label="&insertBreakAllCmd.label;"/>
+ </menupopup>
+ </menu>
+
+ <!-- Format Menu -->
+ <menupopup id="formatMenuPopup" onpopupshowing="EditorInitFormatMenu()">
+ <!-- Font face submenu -->
+ <menu id="fontFaceMenu"
+ label="&fontfaceMenu.label;"
+ accesskey="&fontfaceMenu.accesskey;"
+ position="1">
+ <menupopup id="fontFaceMenuPopup"
+ oncommand="if (event.target.localName == 'menuitem')
+ doStatefulCommand('cmd_fontFace', event.target.getAttribute('value'));"
+ onpopupshowing="initFontFaceMenu(this);">
+ <menuitem id="menu_fontFaceVarWidth"
+ label="&fontVarWidth.label;"
+ accesskey="&fontVarWidth.accesskey;"
+ value=""
+ type="radio"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_fontFaceFixedWidth"
+ label="&fontFixedWidth.label;"
+ accesskey="&fontFixedWidth.accesskey;"
+ value="tt"
+ type="radio"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuseparator id="fontFaceMenuAfterGenericFontsSeparator"/>
+ <menuitem id="menu_fontFaceHelvetica"
+ label="&fontHelvetica.label;"
+ accesskey="&fontHelvetica.accesskey;"
+ value="Helvetica, Arial, sans-serif"
+ value_parsed="helvetica,arial,sans-serif"
+ type="radio"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_fontFaceTimes"
+ label="&fontTimes.label;"
+ accesskey="&fontTimes.accesskey;"
+ value="Times New Roman, Times, serif"
+ value_parsed="times new roman,times,serif"
+ type="radio"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_fontFaceCourier"
+ label="&fontCourier.label;"
+ accesskey="&fontCourier.accesskey;"
+ value="Courier New, Courier, monospace"
+ value_parsed="courier new,courier,monospace"
+ type="radio"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuseparator id="fontFaceMenuAfterDefaultFontsSeparator"
+ class="fontFaceMenuAfterDefaultFonts"/>
+ <menuseparator id="fontFaceMenuAfterUsedFontsSeparator"
+ class="fontFaceMenuAfterUsedFonts"
+ collapsed="true"/>
+ <!-- Local font face items added here by initLocalFontFaceMenu() -->
+ </menupopup>
+ </menu>
+
+ <!-- Font size submenu -->
+ <menu id="fontSizeMenu" label="&fontSizeMenu.label;"
+ accesskey="&fontSizeMenu.accesskey;"
+ position="2">
+ <menupopup id="fontSizeMenuPopup" onpopupshowing="initFontSizeMenu(this, true)">
+ <menuitem id="menu_decreaseFontSize"
+ label="&decreaseFontSize.label;"
+ accesskey="&decreaseFontSize.accesskey;"
+ command="cmd_decreaseFontStep"
+ type="radio" name="1" autocheck="false"
+ key="decreasefontsizekb"/>
+ <menuitem id="menu_increaseFontSize"
+ label="&increaseFontSize.label;"
+ accesskey="&increaseFontSize.accesskey;"
+ command="cmd_increaseFontStep"
+ type="radio" name="1" autocheck="false"
+ key="increasefontsizekb"/>
+ <menuseparator id="fontSizeMenuAfterIncreaseFontSizeSeparator"/>
+ <menuitem id="menu_x-small"
+ label="&size-tinyCmd.label;"
+ accesskey="&size-tinyCmd.accesskey;"
+ oncommand="EditorSetFontSize('x-small')"
+ type="radio" name="1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_size-small"
+ label="&size-smallCmd.label;"
+ accesskey="&size-smallCmd.accesskey;"
+ oncommand="EditorSetFontSize('small')"
+ type="radio" name="1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_size-medium"
+ label="&size-mediumCmd.label;"
+ accesskey="&size-mediumCmd.accesskey;"
+ oncommand="EditorSetFontSize('medium')"
+ type="radio" name="1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_size-large"
+ label="&size-largeCmd.label;"
+ accesskey="&size-largeCmd.accesskey;"
+ oncommand="EditorSetFontSize('large')"
+ type="radio" name="1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_size-x-large"
+ label="&size-extraLargeCmd.label;"
+ accesskey="&size-extraLargeCmd.accesskey;"
+ oncommand="EditorSetFontSize('x-large')"
+ type="radio" name="1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_size-xx-large"
+ label="&size-hugeCmd.label;"
+ accesskey="&size-hugeCmd.accesskey;"
+ oncommand="EditorSetFontSize('xx-large')"
+ type="radio" name="1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <!-- Enable if required for SeaMonkey.
+ <menuitem id="fontSizeMenu_smallBigInfo"
+ type="checkbox" name="2" disabled="true" hidden="true"/>
+ -->
+ </menupopup>
+ </menu>
+
+ <!-- Font style submenu -->
+ <menu id="fontStyleMenu" label="&fontStyleMenu.label;"
+ accesskey="&fontStyleMenu.accesskey;"
+ position="3">
+ <menupopup id="fontStyleMenuPopup" onpopupshowing="initFontStyleMenu(this)">
+ <menuitem id="menu_styleBold"
+ label="&styleBoldCmd.label;"
+ accesskey="&styleBoldCmd.accesskey;"
+ observes="cmd_bold"
+ type="checkbox"
+ key="boldkb"/>
+ <menuitem id="menu_styleItalic"
+ label="&styleItalicCmd.label;"
+ accesskey="&styleItalicCmd.accesskey;"
+ observes="cmd_italic"
+ type="checkbox"
+ key="italickb"/>
+ <menuitem id="menu_styleUnderline"
+ label="&styleUnderlineCmd.label;"
+ accesskey="&styleUnderlineCmd.accesskey;"
+ observes="cmd_underline"
+ type="checkbox"
+ key="underlinekb"/>
+ <menuitem id="menu_styleStrikeThru"
+ label="&styleStrikeThruCmd.label;"
+ accesskey="&styleStrikeThruCmd.accesskey;"
+ observes="cmd_strikethrough"
+ type="checkbox"/>
+ <menuitem id="menu_styleSuperscript"
+ label="&styleSuperscriptCmd.label;"
+ accesskey="&styleSuperscriptCmd.accesskey;"
+ observes="cmd_superscript"
+ type="checkbox"/>
+ <menuitem id="menu_styleSubscript"
+ label="&styleSubscriptCmd.label;"
+ accesskey="&styleSubscriptCmd.accesskey;"
+ observes="cmd_subscript"
+ type="checkbox"/>
+ <menuitem id="menu_fontFixedWidth"
+ label="&fontFixedWidth.label;"
+ accesskey="&fontFixedWidth.accesskey;"
+ observes="cmd_tt"
+ type="checkbox"
+ key="fixedwidthkb"/>
+ <menuitem id="menu_styleNonbreaking"
+ label="&styleNonbreakingCmd.label;"
+ accesskey="&styleNonbreakingCmd.accesskey;"
+ observes="cmd_nobreak"
+ type="checkbox"/>
+ <menuseparator id="fontStyleMenuAfterNonbreakingSeparator"/>
+ <menuitem id="menu_styleEm"
+ label="&styleEm.label;"
+ accesskey="&styleEm.accesskey;"
+ observes="cmd_em"
+ type="checkbox"/>
+ <menuitem id="menu_styleStrong"
+ label="&styleStrong.label;"
+ accesskey="&styleStrong.accesskey;"
+ observes="cmd_strong"
+ type="checkbox"/>
+ <menuitem id="menu_styleCite"
+ label="&styleCite.label;"
+ accesskey="&styleCite.accesskey;"
+ observes="cmd_cite"
+ type="checkbox"/>
+ <menuitem id="menu_styleAbbr"
+ label="&styleAbbr.label;"
+ accesskey="&styleAbbr.accesskey;"
+ observes="cmd_abbr"
+ type="checkbox"/>
+ <menuitem id="menu_styleAcronym"
+ label="&styleAcronym.label;"
+ accesskey="&styleAcronym.accesskey;"
+ observes="cmd_acronym"
+ type="checkbox"/>
+ <menuitem id="menu_styleCode"
+ label="&styleCode.label;"
+ accesskey="&styleCode.accesskey;"
+ observes="cmd_code"
+ type="checkbox"/>
+ <menuitem id="menu_styleSamp"
+ label="&styleSamp.label;"
+ accesskey="&styleSamp.accesskey;"
+ observes="cmd_samp"
+ type="checkbox"/>
+ <menuitem id="menu_styleVar"
+ label="&styleVar.label;"
+ accesskey="&styleVar.accesskey;"
+ observes="cmd_var"
+ type="checkbox"/>
+ </menupopup>
+ </menu>
+
+ <!-- Note: "cmd_fontColor" only monitors color state, it doesn't execute the command
+ (We should use "cmd_fontColorState" and "cmd_backgroundColorState" ?) -->
+ <menuitem id="fontColor"
+ label="&formatFontColor.label;"
+ accesskey="&formatFontColor.accesskey;"
+ observes="cmd_fontColor"
+ oncommand="EditorSelectColor('Text', null);"
+ position="4"/>
+ <menuseparator id="removeSep" position="5"/>
+
+ <!-- label and accesskey set at runtime from strings -->
+ <menuitem id="removeStylesMenuitem"
+ key="removestyleskb"
+ observes="cmd_removeStyles"
+ position="6"/>
+ <menuitem id="removeLinksMenuitem"
+ key="removelinkskb"
+ observes="cmd_removeLinks"
+ position="7"/>
+ <menuitem id="removeNamedAnchorsMenuitem"
+ label="&formatRemoveNamedAnchors.label;"
+ key="removenamedanchorskb"
+ accesskey="&formatRemoveNamedAnchors.accesskey;"
+ observes="cmd_removeNamedAnchors"
+ position="8"/>
+ <menuseparator id="tabSep" position="9"/>
+
+ <!-- Note: the 'Init' menu methods for Paragraph, List, and Align
+ assume that the id = 'menu_'+tagName (the 'value' label),
+ except for the first ('none') item
+ -->
+ <!-- Paragraph Style submenu -->
+ <menu id="paragraphMenu" label="&paragraphMenu.label;"
+ accesskey="&paragraphMenu.accesskey;"
+ position="10" onpopupshowing="InitParagraphMenu()">
+ <menupopup id="paragraphMenuPopup"
+ oncommand="doStatefulCommand('cmd_paragraphState', event.target.getAttribute('value'))">
+ <menuitem id="menu_bodyText"
+ type="radio"
+ name="1"
+ label="&bodyTextCmd.label;"
+ accesskey="&bodyTextCmd.accesskey;"
+ value=""
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_p"
+ type="radio"
+ name="1"
+ label="&paragraphParagraphCmd.label;"
+ accesskey="&paragraphParagraphCmd.accesskey;"
+ value="p"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_h1"
+ type="radio"
+ name="1"
+ label="&heading1Cmd.label;"
+ accesskey="&heading1Cmd.accesskey;"
+ value="h1"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_h2"
+ type="radio"
+ name="1"
+ label="&heading2Cmd.label;"
+ accesskey="&heading2Cmd.accesskey;"
+ value="h2"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_h3"
+ type="radio"
+ name="1" label="&heading3Cmd.label;"
+ accesskey="&heading3Cmd.accesskey;"
+ value="h3"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_h4"
+ type="radio"
+ name="1" label="&heading4Cmd.label;"
+ accesskey="&heading4Cmd.accesskey;"
+ value="h4"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_h5"
+ type="radio"
+ name="1" label="&heading5Cmd.label;"
+ accesskey="&heading5Cmd.accesskey;"
+ value="h5"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_h6"
+ type="radio"
+ name="1"
+ label="&heading6Cmd.label;"
+ accesskey="&heading6Cmd.accesskey;"
+ value="h6"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_address"
+ type="radio"
+ name="1"
+ label="&paragraphAddressCmd.label;"
+ accesskey="&paragraphAddressCmd.accesskey;"
+ value="address"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_pre"
+ type="radio"
+ name="1"
+ label="&paragraphPreformatCmd.label;"
+ accesskey="&paragraphPreformatCmd.accesskey;"
+ value="pre"
+ observes="cmd_renderedHTMLEnabler"/>
+ </menupopup>
+ </menu>
+
+ <!-- List Style submenu -->
+ <menu id="listMenu" label="&formatlistMenu.label;"
+ accesskey="&formatlistMenu.accesskey;"
+ position="11" onpopupshowing="InitListMenu()">
+ <menupopup>
+ <menuitem id="menu_noList"
+ type="radio"
+ name="1"
+ label="&noneCmd.label;"
+ accesskey="&noneCmd.accesskey;"
+ command="cmd_removeList"/>
+ <menuitem id="menu_ul"
+ type="radio"
+ name="1"
+ label="&listBulletCmd.label;"
+ accesskey="&listBulletCmd.accesskey;"
+ command="cmd_ul"/>
+ <menuitem id="menu_ol"
+ type="radio"
+ name="1"
+ label="&listNumberedCmd.label;"
+ accesskey="&listNumberedCmd.accesskey;"
+ command="cmd_ol"/>
+ <menuitem id="menu_dt"
+ type="radio"
+ name="1"
+ label="&listTermCmd.label;"
+ accesskey="&listTermCmd.accesskey;"
+ command="cmd_dt"/>
+ <menuitem id="menu_dd"
+ type="radio"
+ name="1"
+ label="&listDefinitionCmd.label;"
+ accesskey="&listDefinitionCmd.accesskey;"
+ command="cmd_dd"/>
+ <menuseparator/>
+ <menuitem id="listProps"
+ label="&listPropsCmd.label;"
+ accesskey="&listPropsCmd.accesskey;"
+ command="cmd_listProperties"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="identingSep" position="12"/>
+
+ <menuitem id="increaseIndent"
+ label="&increaseIndent.label;"
+ accesskey="&increaseIndent.accesskey;"
+ key="increaseindentkb"
+ command="cmd_indent"
+ position="13"/>
+ <menuitem id="decreaseIndent"
+ label="&decreaseIndent.label;"
+ accesskey="&decreaseIndent.accesskey;"
+ key="decreaseindentkb"
+ command="cmd_outdent"
+ position="14"/>
+
+ <menu id="alignMenu" label="&alignMenu.label;" accesskey="&alignMenu.accesskey;"
+ onpopupshowing="InitAlignMenu()"
+ position="15">
+ <!-- Align submenu -->
+ <menupopup id="alignMenuPopup"
+ oncommand="doStatefulCommand('cmd_align', event.target.getAttribute('value'))">
+ <menuitem id="menu_left"
+ label="&alignLeft.label;"
+ accesskey="&alignLeft.accesskey;"
+ type="radio"
+ name="1"
+ value="left"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_center"
+ label="&alignCenter.label;"
+ accesskey="&alignCenter.accesskey;"
+ type="radio"
+ name="1"
+ value="center"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_right"
+ label="&alignRight.label;"
+ accesskey="&alignRight.accesskey;"
+ type="radio"
+ name="1"
+ value="right"
+ observes="cmd_renderedHTMLEnabler"/>
+ <menuitem id="menu_justify"
+ label="&alignJustify.label;"
+ accesskey="&alignJustify.accesskey;"
+ type="radio"
+ name="1"
+ value="justify"
+ observes="cmd_renderedHTMLEnabler"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="tableSep" position="16"/>
+ <!-- Merge Table Menu and separator in Messenger Composer here -->
+ <!-- Merge property items here -->
+ </menupopup>
+
+ <!-- Next 2 are items to append at the bottom of the formatMenuPopup -->
+ <!-- label and accesskey filled in during menu creation -->
+ <menuitem id="objectProperties" command="cmd_objectProperties"/>
+ <!-- Don't use 'observes', must call command correctly -->
+ <menuitem id="colorsAndBackground"
+ label="&colorsAndBackground.label;"
+ accesskey="&colorsAndBackground.accesskey;"
+ oncommand="goDoCommand('cmd_colorProperties')"
+ observes="cmd_renderedHTMLEnabler"/>
+
+ <menu id="tableMenu" label="&tableMenu.label;" accesskey="&tableMenu.accesskey;">
+ <menupopup id="tableMenuPopup" onpopupshowing="EditorInitTableMenu()">
+ <!-- From EditorCommandOverlay.xhtml (shared with context popup) -->
+ <menu id="tableInsertMenu" label="&tableInsertMenu.label;" accesskey="&tableInsertMenu.accesskey;">
+ <menupopup id="tableInsertMenuPopup">
+ <menuitem id="menu_insertTable"
+ label="&insertTableCmd.label;"
+ accesskey="&insertTableCmd.accesskey;"
+ command="cmd_InsertTable"/>
+ <menuseparator id="tableMenuAfterInsertTableSeparator"/>
+ <menuitem id="menu_tableRowAbove"
+ label="&tableRowAbove.label;"
+ accesskey="&tableRowAbove.accesskey;"
+ command="cmd_InsertRowAbove"/>
+ <menuitem id="menu_tableRowBelow"
+ label="&tableRowBelow.label;"
+ accesskey="&tableRowBelow.accesskey;"
+ command="cmd_InsertRowBelow"/>
+ <menuseparator id="tableMenuAfterTableRowSeparator"/>
+ <menuitem id="menu_tableColumnBefore"
+ label="&tableColumnBefore.label;"
+ accesskey="&tableColumnBefore.accesskey;"
+ command="cmd_InsertColumnBefore"/>
+ <menuitem id="menu_tableColumnAfter"
+ label="&tableColumnAfter.label;"
+ accesskey="&tableColumnAfter.accesskey;"
+ command="cmd_InsertColumnAfter"/>
+ <menuseparator id="tableMenuAfterInsertColumnSeparator"/>
+ <menuitem id="menu_tableCellBefore"
+ label="&tableCellBefore.label;"
+ accesskey="&tableCellBefore.accesskey;"
+ command="cmd_InsertCellBefore"/>
+ <menuitem id="menu_tableCellAfter"
+ label="&tableCellAfter.label;"
+ accesskey="&tableCellAfter.accesskey;"
+ command="cmd_InsertCellAfter"/>
+ </menupopup>
+ </menu>
+ <menu id="tableSelectMenu" label="&tableSelectMenu.label;" accesskey="&tableSelectMenu.accesskey;">
+ <menupopup id="tableSelectPopup">
+ <menuitem id="menu_SelectTable"
+ label="&tableTable.label;"
+ accesskey="&tableTable.accesskey;"
+ command="cmd_SelectTable"/>
+ <menuitem id="menu_SelectRow"
+ label="&tableRow.label;"
+ accesskey="&tableRow.accesskey;"
+ command="cmd_SelectRow"/>
+ <menuitem id="menu_SelectColumn"
+ label="&tableColumn.label;"
+ accesskey="&tableColumn.accesskey;"
+ command="cmd_SelectColumn"/>
+ <menuitem id="menu_SelectCell"
+ label="&tableCell.label;"
+ accesskey="&tableCell.accesskey;"
+ command="cmd_SelectCell"/>
+ <menuitem id="menu_SelectAllCells"
+ label="&tableAllCells.label;"
+ accesskey="&tableAllCells.accesskey;"
+ command="cmd_SelectAllCells"/>
+ </menupopup>
+ </menu>
+ <menu id="tableDeleteMenu" label="&tableDeleteMenu.label;" accesskey="&tableDeleteMenu.accesskey;">
+ <menupopup id="tableDeletePopup">
+ <menuitem id="menu_DeleteTable"
+ label="&tableTable.label;"
+ accesskey="&tableTable.accesskey;"
+ command="cmd_DeleteTable"/>
+ <menuitem id="menu_DeleteRow"
+ label="&tableRows.label;"
+ accesskey="&tableRow.accesskey;"
+ command="cmd_DeleteRow"/>
+ <menuitem id="menu_DeleteColumn"
+ label="&tableColumns.label;"
+ accesskey="&tableColumn.accesskey;"
+ command="cmd_DeleteColumn"/>
+ <menuitem id="menu_DeleteCell"
+ label="&tableCells.label;"
+ accesskey="&tableCell.accesskey;"
+ command="cmd_DeleteCell"/>
+ <menuitem id="menu_DeleteCellContents"
+ label="&tableCellContents.label;"
+ accesskey="&tableCellContents.accesskey;"
+ command="cmd_DeleteCellContents"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="tableDeleteMenuAfterDeleteCellSeparator"/>
+ <!-- menu label is set in InitTableMenu -->
+ <menuitem id="menu_JoinTableCells"
+ label="&tableJoinCells.label;"
+ accesskey="&tableJoinCells.accesskey;"
+ command="cmd_JoinTableCells"/>
+ <menuitem id="menu_SlitTableCell"
+ label="&tableSplitCell.label;"
+ accesskey="&tableSplitCell.accesskey;"
+ command="cmd_SplitTableCell"/>
+ <menuitem id="menu_ConvertToTable"
+ label="&convertToTable.label;"
+ accesskey="&convertToTable.accesskey;"
+ command="cmd_ConvertToTable"/>
+ <menuseparator id="tableDeleteMenuAfterConvertToTableSeparator"/>
+ <menuitem id="menu_TableOrCellColor"
+ label="&tableOrCellColor.label;"
+ accesskey="&tableOrCellColor.accesskey;"
+ command="cmd_TableOrCellColor"/>
+ <menuitem id="menu_tableProperties"
+ label="&tableProperties.label;"
+ accesskey="&tableProperties.accesskey;"
+ command="cmd_editTable"/>
+ </menupopup>
+ </menu>
+
+ <toolbarbutton id="AlignPopupButton"
+ class="formatting-button"
+ label="&AlignPopupButton.label;"
+ removable="true"
+ tooltiptext="&AlignPopupButton.tooltip;"
+ type="menu"
+ observes="cmd_align">
+ <menupopup id="AlignPopup">
+ <menuitem id="AlignLeftItem" class="menuitem-iconic" label="&alignLeft.label;"
+ oncommand="doStatefulCommand('cmd_align', 'left')"
+ tooltiptext="&alignLeftButton.tooltip;" />
+ <menuitem id="AlignCenterItem" class="menuitem-iconic" label="&alignCenter.label;"
+ oncommand="doStatefulCommand('cmd_align', 'center')"
+ tooltiptext="&alignCenterButton.tooltip;" />
+ <menuitem id="AlignRightItem" class="menuitem-iconic" label="&alignRight.label;"
+ oncommand="doStatefulCommand('cmd_align', 'right')"
+ tooltiptext="&alignRightButton.tooltip;" />
+ <menuitem id="AlignJustifyItem" class="menuitem-iconic" label="&alignJustify.label;"
+ oncommand="doStatefulCommand('cmd_align', 'justify')"
+ tooltiptext="&alignJustifyButton.tooltip;"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbarbutton id="InsertPopupButton"
+ class="formatting-button"
+ label="&InsertPopupButton.label;"
+ removable="true"
+ tooltiptext="&InsertPopupButton.tooltip;"
+ type="menu"
+ observes="cmd_renderedHTMLEnabler">
+ <menupopup id="InsertPopup">
+ <menuitem id="InsertLinkItem"
+ class="menuitem-iconic"
+ command="cmd_link"
+ label="&linkToolbarCmd.label;"
+ tooltiptext="&linkToolbarCmd.tooltip;"/>
+ <menuitem id="InsertAnchorItem"
+ class="menuitem-iconic"
+ command="cmd_anchor"
+ label="&anchorToolbarCmd.label;"
+ tooltiptext="&anchorToolbarCmd.tooltip;"/>
+ <menuitem id="InsertImageItem"
+ class="menuitem-iconic"
+ command="cmd_image"
+ label="&imageToolbarCmd.label;"
+ tooltiptext="&imageToolbarCmd.tooltip;"/>
+ <menuitem id="InsertHRuleItem"
+ class="menuitem-iconic"
+ command="cmd_hline"
+ label="&hruleToolbarCmd.label;"
+ tooltiptext="&hruleToolbarCmd.tooltip;"/>
+ <menuitem id="InsertTableItem"
+ class="menuitem-iconic"
+ command="cmd_table"
+ label="&tableToolbarCmd.label;"
+ tooltiptext="&tableToolbarCmd.tooltip;"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <!-- Editor toolbar items -->
+ <!-- note that we override the submenu item label "Blank Window" with "New" used for the menu -->
+ <toolbarbutton id="cutButton" class="toolbarbutton-1"
+ removable="true"
+ command="cmd_cut"
+ tooltiptext="&cutToolbarCmd.tooltip;"/>
+ <toolbarbutton id="copyButton" class="toolbarbutton-1"
+ removable="true"
+ command="cmd_copy"
+ tooltiptext="&copyToolbarCmd.tooltip;"/>
+ <toolbarbutton id="pasteButton" class="toolbarbutton-1"
+ removable="true"
+ command="cmd_paste"
+ tooltiptext="&pasteToolbarCmd.tooltip;"/>
+
+ <toolbarbutton id="findButton"
+ class="toolbarbutton-1"
+ label="&findToolbarCmd.label;"
+ removable="true"
+ command="cmd_find"
+ tooltiptext="&findToolbarCmd.tooltip;"/>
+ <toolbarbutton id="spellingButton"
+ class="toolbarbutton-1"
+ label="&spellToolbarCmd.label;"
+ removable="true"
+ command="cmd_spelling"
+ tooltiptext="&spellToolbarCmd.tooltip;"/>
+ <toolbarbutton id="imageButton"
+ class="toolbarbutton-1"
+ removable="true"
+ label="&imageToolbarCmd.label;"
+ command="cmd_image"
+ tooltiptext="&imageToolbarCmd.tooltip;"/>
+ <toolbarbutton id="hlineButton"
+ class="toolbarbutton-1"
+ removable="true"
+ label="&hruleToolbarCmd.label;"
+ command="cmd_hline"
+ tooltiptext="&hruleToolbarCmd.tooltip;"/>
+ <toolbarbutton id="tableButton"
+ class="toolbarbutton-1"
+ removable="true"
+ label="&tableToolbarCmd.label;"
+ command="cmd_table"
+ tooltiptext="&tableToolbarCmd.tooltip;"/>
+ <toolbarbutton id="linkButton"
+ class="toolbarbutton-1"
+ removable="true"
+ label="&linkToolbarCmd.label;"
+ command="cmd_link"
+ tooltiptext="&linkToolbarCmd.tooltip;"/>
+ <toolbarbutton id="namedAnchorButton"
+ class="toolbarbutton-1"
+ removable="true"
+ label="&anchorToolbarCmd.label;"
+ command="cmd_anchor"
+ tooltiptext="&anchorToolbarCmd.tooltip;"/>
+
+ <!-- Formatting toolbar items. "value" are HTML tagnames, don't translate -->
+<toolbaritem id="paragraph-select-container"
+ class="formatting-button"
+ title="&ParagraphSelect.title;"
+ align="center"
+ removable="true"
+ tooltiptext="&ParagraphSelect.tooltip;"
+ observes="cmd_renderedHTMLEnabler">
+ <menulist id="ParagraphSelect"
+ class="toolbar-focustarget"
+ crop="right">
+ <observes element="paragraph-select-container" attribute="disabled"/>
+ <observes element="cmd_paragraphState" attribute="state" onbroadcast="onParagraphFormatChange(this.parentNode, 'cmd_paragraphState')"/>
+ <menupopup id="ParagraphPopup" oncommand="doStatefulCommand('cmd_paragraphState', event.target.value)">
+ <menuitem id="toolbarmenu_bodyText" label="&bodyTextCmd.label;" value=""/>
+ <menuitem id="toolbarmenu_paragraph" label="&paragraphParagraphCmd.label;" value="p"/>
+ <menuitem id="toolbarmenu_h1" label="&heading1Cmd.label;" value="h1"/>
+ <menuitem id="toolbarmenu_h2" label="&heading2Cmd.label;" value="h2"/>
+ <menuitem id="toolbarmenu_h3" label="&heading3Cmd.label;" value="h3"/>
+ <menuitem id="toolbarmenu_h4" label="&heading4Cmd.label;" value="h4"/>
+ <menuitem id="toolbarmenu_h5" label="&heading5Cmd.label;" value="h5"/>
+ <menuitem id="toolbarmenu_h6" label="&heading6Cmd.label;" value="h6"/>
+ <menuitem id="toolbarmenu_address" label="&paragraphAddressCmd.label;" value="address"/>
+ <menuitem id="toolbarmenu_pre" label="&paragraphPreformatCmd.label;" value="pre"/>
+ </menupopup>
+ </menulist>
+</toolbaritem>
+
+ <!-- "value" are HTML tagnames, don't translate -->
+<toolbaritem id="font-face-select-container"
+ class="formatting-button"
+ title="&FontFaceSelect.title;"
+ align="center"
+ removable="true"
+ tooltiptext="&FontFaceSelect.tooltip;"
+ observes="cmd_renderedHTMLEnabler">
+ <menulist id="FontFaceSelect"
+ class="toolbar-focustarget"
+ crop="center"
+ sizetopopup="pref">
+ <observes element="font-face-select-container" attribute="disabled"/>
+ <observes element="cmd_fontFace" attribute="state" onbroadcast="onFontFaceChange(this.parentNode, 'cmd_fontFace')"/>
+ <menupopup id="FontFacePopup" oncommand="doStatefulCommand('cmd_fontFace', event.target.value)">
+ <menuitem id="toolbarmenu_fontVarWidth" label="&fontVarWidth.label;" value=""/>
+ <menuitem id="toolbarmenu_fontFixedWidth" label="&fontFixedWidth.label;" value="tt"/>
+ <menuseparator id="toolbarmenuAfterGenericFontsSeparator"/>
+ <menuitem id="toolbarmenu_fontHelvetica" label="&fontHelvetica.label;"
+ value="Helvetica, Arial, sans-serif"
+ value_parsed="helvetica,arial,sans-serif"/>
+ <menuitem id="toolbarmenu_fontTimes" label="&fontTimes.label;"
+ value="Times New Roman, Times, serif"
+ value_parsed="times new roman,times,serif"/>
+ <menuitem id="toolbarmenu_fontCourier" label="&fontCourier.label;"
+ value="Courier New, Courier, monospace"
+ value_parsed="courier new,courier,monospace"/>
+ <menuseparator id="toolbarmenuAfterDefaultFontsSeparator"
+ class="fontFaceMenuAfterDefaultFonts"/>
+ <menuseparator id="toolbarmenuAfterUsedFontsSeparator"
+ class="fontFaceMenuAfterUsedFonts"
+ hidden="true"/>
+ <!-- Local font face items added here by initLocalFontFaceMenu() -->
+ </menupopup>
+ </menulist>
+</toolbaritem>
+
+<toolbaritem id="font-size-select-container"
+ class="formatting-button"
+ title="&FontSizeSelect.title;"
+ align="center"
+ removable="true"
+ tooltiptext="&FontSizeSelect.tooltip;">
+ <menulist id="FontSizeSelect"
+ class="toolbar-focustarget"
+ crop="right"
+ oncommand="EditorSelectFontSize();">
+ <observes element="font-size-select-container" attribute="disabled"/>
+ <observes element="cmd_fontSize" attribute="state" onbroadcast="onFontSizeChange(this.parentNode, 'cmd_fontSize')"/>
+ <menupopup id="FontSizePopup">
+ <menuitem id="toobarmenu_fontSize_x-small" label="&size-tinyCmd.label;"/>
+ <menuitem id="toobarmenu_fontSize_small" label="&size-smallCmd.label;"/>
+ <menuitem id="toobarmenu_fontSize_medium" label="&size-mediumCmd.label;"/>
+ <menuitem id="toobarmenu_fontSize_large" label="&size-largeCmd.label;"/>
+ <menuitem id="toobarmenu_fontSize_x-large" label="&size-extraLargeCmd.label;"/>
+ <menuitem id="toobarmenu_fontSize_xx-large" label="&size-hugeCmd.label;"/>
+ </menupopup>
+ </menulist>
+</toolbaritem>
+
+<toolbaritem id="color-buttons-container"
+ align="center"
+ title="&colorButtons.title;"
+ removable="true"
+ class="formatting-button">
+ <stack id="ColorButtons" align="center">
+ <observes element="cmd_fontColor" attribute="state" onbroadcast="onFontColorChange()"/>
+ <observes element="cmd_backgroundColor" attribute="state" onbroadcast="onBackgroundColorChange()"/>
+ <box class="color-button" id="BackgroundColorButton"
+ onclick="EditorSelectColor('', event);"
+ tooltiptext="&BackgroundColorButton.tooltip;"/>
+ <box class="color-button" id="TextColorButton"
+ onclick="EditorSelectColor('Text', event);"
+ tooltiptext="&TextColorButton.tooltip;"/>
+ </stack>
+</toolbaritem>
+ <toolbarbutton id="HighlightColorButton"
+ class="formatting-button"
+ label="&HighlightColorButton.label;"
+ removable="true"
+ tooltiptext="&HighlightColorButton.tooltip;"
+ command="cmd_highlight">
+ <observes element="cmd_highlight" attribute="state" onbroadcast="onHighlightColorChange()"/>
+ <observes element="cmd_highlight" attribute="collapsed"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="AbsoluteFontSizeButton"
+ class="formatting-button"
+ label="&absoluteFontSize.label;"
+ removable="true"
+ tooltiptext="&absoluteFontSizeToolbarCmd.tooltip;"
+ type="menu"
+ observes="cmd_renderedHTMLEnabler">
+ <menupopup id="AbsoluteFontSizeButtonPopup"
+ onpopupshowing="initFontSizeMenu(this, false);">
+ <menuitem id="toobarmenu_fontSize_x-small"
+ label="&size-tinyCmd.label;"
+ type="radio" name="1"
+ oncommand="EditorSetFontSize('x-small')"/>
+ <menuitem id="toobarmenu_fontSize_small"
+ label="&size-smallCmd.label;"
+ type="radio" name="1"
+ oncommand="EditorSetFontSize('small')"/>
+ <menuitem id="toobarmenu_fontSize_medium"
+ label="&size-mediumCmd.label;"
+ type="radio" name="1"
+ oncommand="EditorSetFontSize('medium')"/>
+ <menuitem id="toobarmenu_fontSize_large"
+ label="&size-largeCmd.label;"
+ type="radio" name="1"
+ oncommand="EditorSetFontSize('large')"/>
+ <menuitem id="toobarmenu_fontSize_x-large"
+ label="&size-extraLargeCmd.label;"
+ type="radio" name="1"
+ oncommand="EditorSetFontSize('x-large')"/>
+ <menuitem id="toobarmenu_fontSize_xx-large"
+ label="&size-hugeCmd.label;"
+ type="radio" name="1"
+ oncommand="EditorSetFontSize('xx-large')"/>
+ <!-- Enable if required for SeaMonkey.
+ <menuitem id="toobarmenu_fontSize_smallBigInfo"
+ type="checkbox" name="2" disabled="true" hidden="true"/>
+ -->
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="DecreaseFontSizeButton"
+ class="formatting-button"
+ label="&smaller.label;"
+ removable="true"
+ tooltiptext="&decreaseFontSizeToolbarCmd.tooltip;"
+ command="cmd_decreaseFontStep"/>
+ <toolbarbutton id="IncreaseFontSizeButton"
+ class="formatting-button"
+ label="&larger.label;"
+ removable="true"
+ tooltiptext="&increaseFontSizeToolbarCmd.tooltip;"
+ command="cmd_increaseFontStep"/>
+ <toolbarbutton id="boldButton"
+ class="formatting-button"
+ label="&bold.label;"
+ removable="true"
+ tooltiptext="&boldToolbarCmd.tooltip;"
+ type="checkbox"
+ autoCheck="false"
+ command="cmd_bold">
+ <observes element="cmd_bold" type="checkbox" attribute="state" onbroadcast="onButtonUpdate(this.parentNode, 'cmd_bold')"/>
+ </toolbarbutton>
+ <toolbarbutton id="italicButton"
+ class="formatting-button"
+ label="&italic.label;"
+ removable="true"
+ tooltiptext="&italicToolbarCmd.tooltip;"
+ type="checkbox"
+ autoCheck="false"
+ command="cmd_italic">
+ <observes element="cmd_italic" attribute="state" onbroadcast="onButtonUpdate(this.parentNode, 'cmd_italic')"/>
+ </toolbarbutton>
+ <toolbarbutton id="underlineButton"
+ class="formatting-button"
+ label="&underline.label;"
+ removable="true"
+ tooltiptext="&underlineToolbarCmd.tooltip;"
+ type="checkbox"
+ autoCheck="false"
+ command="cmd_underline">
+ <observes element="cmd_underline" attribute="state" onbroadcast="onButtonUpdate(this.parentNode, 'cmd_underline')"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="ulButton"
+ class="formatting-button"
+ label="&bullets.label;"
+ removable="true"
+ tooltiptext="&bulletListToolbarCmd.tooltip;"
+ type="radio"
+ group="lists"
+ autoCheck="false"
+ command="cmd_ul">
+ <observes element="cmd_ul" attribute="state" onbroadcast="onButtonUpdate(this.parentNode, 'cmd_ul')"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="olButton"
+ class="formatting-button"
+ label="&numbers.label;"
+ removable="true"
+ tooltiptext="&numberListToolbarCmd.tooltip;"
+ type="radio"
+ group="lists"
+ autoCheck="false"
+ command="cmd_ol">
+ <observes element="cmd_ol" attribute="state" onbroadcast="onButtonUpdate(this.parentNode, 'cmd_ol')"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="outdentButton"
+ class="formatting-button"
+ label="&outdent.label;"
+ removable="true"
+ tooltiptext="&outdentToolbarCmd.tooltip;"
+ command="cmd_outdent"/>
+ <toolbarbutton id="indentButton"
+ class="formatting-button"
+ label="&indent.label;"
+ removable="true"
+ tooltiptext="&indentToolbarCmd.tooltip;"
+ command="cmd_indent"/>
+
+ <!-- alignment buttons -->
+ <toolbarbutton id="align-left-button"
+ class="formatting-button"
+ label="&alignLeftButton.label;"
+ removable="true"
+ tooltiptext="&alignLeftButton.tooltip;"
+ type="radio"
+ group="align"
+ autoCheck="false"
+ oncommand="doStatefulCommand('cmd_align', 'left')">
+ <observes element="cmd_align" attribute="state"
+ onbroadcast="onStateButtonUpdate(this.parentNode, 'cmd_align', 'left')" />
+ </toolbarbutton>
+ <toolbarbutton id="align-center-button"
+ class="formatting-button"
+ label="&alignCenterButton.label;"
+ removable="true"
+ tooltiptext="&alignCenterButton.tooltip;"
+ type="radio"
+ group="align"
+ autoCheck="false"
+ oncommand="doStatefulCommand('cmd_align', 'center')">
+ <observes element="cmd_align" attribute="state"
+ onbroadcast="onStateButtonUpdate(this.parentNode, 'cmd_align', 'center')"/>
+ </toolbarbutton>
+ <toolbarbutton id="align-right-button"
+ class="formatting-button"
+ label="&alignRightButton.label;"
+ removable="true"
+ tooltiptext="&alignRightButton.tooltip;"
+ type="radio"
+ group="align"
+ autoCheck="false"
+ oncommand="doStatefulCommand('cmd_align', 'right')">
+ <observes element="cmd_align" attribute="state"
+ onbroadcast="onStateButtonUpdate(this.parentNode, 'cmd_align', 'right')"/>
+ </toolbarbutton>
+ <toolbarbutton id="align-justify-button"
+ class="formatting-button"
+ label="&alignJustifyButton.label;"
+ removable="true"
+ tooltiptext="&alignJustifyButton.tooltip;"
+ type="radio"
+ group="align"
+ autoCheck="false"
+ oncommand="doStatefulCommand('cmd_align', 'justify')">
+ <observes element="cmd_align" attribute="state"
+ onbroadcast="onStateButtonUpdate(this.parentNode, 'cmd_align', 'justify')"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="absolutePositionButton"
+ class="formatting-button"
+ label="&absolutePosition.label;"
+ removable="true"
+ tooltiptext="&layer.tooltip;"
+ type="checkbox"
+ command="cmd_absPos">
+ <observes element="cmd_absPos" attribute="state" onbroadcast="onStateButtonUpdate(this.parentNode, 'cmd_absPos', 'absolute')"/>
+ </toolbarbutton>
+ <toolbarbutton id="decreaseZIndexButton"
+ class="formatting-button"
+ label="&decreaseZIndex.label;"
+ removable="true"
+ tooltiptext="&layerSendToBack.tooltip;"
+ command="cmd_decreaseZIndex"/>
+ <toolbarbutton id="increaseZIndexButton"
+ class="formatting-button"
+ label="&increaseZIndex.label;"
+ removable="true"
+ tooltiptext="&layerBringToFront.tooltip;"
+ command="cmd_increaseZIndex"/>
+
+ <!-- smiley menu -->
+ <toolbarbutton id="smileButtonMenu"
+ class="formatting-button"
+ label="&SmileButton.label;"
+ removable="true"
+ tooltiptext="&SmileButton.tooltip;"
+ type="menu"
+ observes="cmd_smiley">
+ <menupopup id="smilyPopup">
+ <menuitem class="smiley insert-smile menuitem-iconic"
+ label="&smiley1Cmd.label;"
+ tooltiptext="&smiley1Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-)');"/>
+ <menuitem class="smiley insert-frown menuitem-iconic"
+ label="&smiley2Cmd.label;"
+ tooltiptext="&smiley2Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-(');"/>
+ <menuitem class="smiley insert-wink menuitem-iconic"
+ label="&smiley3Cmd.label;"
+ tooltiptext="&smiley3Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ';-)');"/>
+ <menuitem class="smiley insert-tongue menuitem-iconic"
+ label="&smiley4Cmd.label;"
+ tooltiptext="&smiley4Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-P');"/>
+ <menuitem class="smiley insert-laughing menuitem-iconic"
+ label="&smiley5Cmd.label;"
+ tooltiptext="&smiley5Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-D');"/>
+ <menuitem class="smiley insert-embarrassed menuitem-iconic"
+ label="&smiley6Cmd.label;"
+ tooltiptext="&smiley6Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-[');"/>
+ <menuitem class="smiley insert-undecided menuitem-iconic"
+ label="&smiley7Cmd.label;"
+ tooltiptext="&smiley7Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-\\');"/>
+ <menuitem class="smiley insert-surprise menuitem-iconic"
+ label="&smiley8Cmd.label;"
+ tooltiptext="&smiley8Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', '=-O');"/>
+ <menuitem class="smiley insert-kiss menuitem-iconic"
+ label="&smiley9Cmd.label;"
+ tooltiptext="&smiley9Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-*');"/>
+ <menuitem class="smiley insert-yell menuitem-iconic"
+ label="&smiley10Cmd.label;"
+ tooltiptext="&smiley10Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', '>:o');"/>
+ <menuitem class="smiley insert-cool menuitem-iconic"
+ label="&smiley11Cmd.label;"
+ tooltiptext="&smiley11Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', '8-)');"/>
+ <menuitem class="smiley insert-money menuitem-iconic"
+ label="&smiley12Cmd.label;"
+ tooltiptext="&smiley12Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-$');"/>
+ <menuitem class="smiley insert-foot menuitem-iconic"
+ label="&smiley13Cmd.label;"
+ tooltiptext="&smiley13Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-!');"/>
+ <menuitem class="smiley insert-innocent menuitem-iconic"
+ label="&smiley14Cmd.label;"
+ tooltiptext="&smiley14Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', 'O:-)');"/>
+ <menuitem class="smiley insert-cry menuitem-iconic"
+ label="&smiley15Cmd.label;"
+ tooltiptext="&smiley15Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':\'(');"/>
+ <menuitem class="smiley insert-sealed menuitem-iconic"
+ label="&smiley16Cmd.label;"
+ tooltiptext="&smiley16Cmd.tooltip;"
+ oncommand="doStatefulCommand('cmd_smiley', ':-X');"/>
+ </menupopup>
+ </toolbarbutton>
+</overlay>
diff --git a/comm/suite/editor/base/content/editorTasksOverlay.xhtml b/comm/suite/editor/base/content/editorTasksOverlay.xhtml
new file mode 100644
index 0000000000..d3fcb2d4b2
--- /dev/null
+++ b/comm/suite/editor/base/content/editorTasksOverlay.xhtml
@@ -0,0 +1,31 @@
+<?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 SYSTEM "chrome://communicator/locale/tasksOverlay.dtd">
+
+<overlay id="editorTasksOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <script src="chrome://editor/content/editorApplicationOverlay.js"/>
+
+ <keyset id="tasksKeys">
+ <key id="key_editor" key="&editorCmd.commandkey;" command="Tasks:Editor" modifiers="accel"/>
+ </keyset>
+
+ <commandset id="tasksCommands">
+ <command id="Tasks:Editor" oncommand="toEditor();"/>
+ </commandset>
+
+ <hbox id="component-bar" class="statusbarpanel">
+ <toolbarbutton class="taskbutton" id="mini-comp" command="Tasks:Editor"
+ tooltiptext="&taskComposer.tooltip;" insertafter="mini-nav"/>
+ </hbox>
+
+ <menupopup id="windowPopup">
+ <menuitem label="&editorCmd.label;" accesskey="&editorCmd.accesskey;" key="key_editor" command="Tasks:Editor" id="tasksMenuEditor" insertafter="IMMenuItem,tasksMenuNavigator" class="menuitem-iconic icon-composer16 menu-iconic"/>
+ </menupopup>
+
+</overlay>
+
diff --git a/comm/suite/editor/base/content/editorUtilities.js b/comm/suite/editor/base/content/editorUtilities.js
new file mode 100644
index 0000000000..ea06446e45
--- /dev/null
+++ b/comm/suite/editor/base/content/editorUtilities.js
@@ -0,0 +1,1014 @@
+/* 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/. */
+
+/* import-globals-from editor.js */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+// Each editor window must include this file
+// Variables shared by all dialogs:
+
+// Object to attach commonly-used widgets (all dialogs should use this)
+var gDialog = {};
+
+var kOutputEncodeBasicEntities =
+ Ci.nsIDocumentEncoder.OutputEncodeBasicEntities;
+var kOutputEncodeHTMLEntities = Ci.nsIDocumentEncoder.OutputEncodeHTMLEntities;
+var kOutputEncodeLatin1Entities =
+ Ci.nsIDocumentEncoder.OutputEncodeLatin1Entities;
+var kOutputEncodeW3CEntities = Ci.nsIDocumentEncoder.OutputEncodeW3CEntities;
+var kOutputFormatted = Ci.nsIDocumentEncoder.OutputFormatted;
+var kOutputLFLineBreak = Ci.nsIDocumentEncoder.OutputLFLineBreak;
+var kOutputSelectionOnly = Ci.nsIDocumentEncoder.OutputSelectionOnly;
+var kOutputWrap = Ci.nsIDocumentEncoder.OutputWrap;
+
+var gStringBundle;
+var gFilePickerDirectory;
+
+/** *********** Message dialogs ***************/
+
+// Optional: Caller may supply text to substitute for "Ok" and/or "Cancel"
+function ConfirmWithTitle(title, message, okButtonText, cancelButtonText) {
+ let okFlag = okButtonText
+ ? Services.prompt.BUTTON_TITLE_IS_STRING
+ : Services.prompt.BUTTON_TITLE_OK;
+ let cancelFlag = cancelButtonText
+ ? Services.prompt.BUTTON_TITLE_IS_STRING
+ : Services.prompt.BUTTON_TITLE_CANCEL;
+
+ return (
+ Services.prompt.confirmEx(
+ window,
+ title,
+ message,
+ okFlag * Services.prompt.BUTTON_POS_0 +
+ cancelFlag * Services.prompt.BUTTON_POS_1,
+ okButtonText,
+ cancelButtonText,
+ null,
+ null,
+ { value: 0 }
+ ) == 0
+ );
+}
+
+/** *********** String Utilities ***************/
+
+function GetString(name) {
+ if (!gStringBundle) {
+ try {
+ gStringBundle = Services.strings.createBundle(
+ "chrome://editor/locale/editor.properties"
+ );
+ } catch (ex) {}
+ }
+ if (gStringBundle) {
+ try {
+ return gStringBundle.GetStringFromName(name);
+ } catch (e) {}
+ }
+ return null;
+}
+
+function GetFormattedString(aName, aVal) {
+ if (!gStringBundle) {
+ try {
+ gStringBundle = Services.strings.createBundle(
+ "chrome://editor/locale/editor.properties"
+ );
+ } catch (ex) {}
+ }
+ if (gStringBundle) {
+ try {
+ return gStringBundle.formatStringFromName(aName, [aVal]);
+ } catch (e) {}
+ }
+ return null;
+}
+
+function TrimStringLeft(string) {
+ if (!string) {
+ return "";
+ }
+ return string.trimLeft();
+}
+
+function TrimStringRight(string) {
+ if (!string) {
+ return "";
+ }
+ return string.trimRight();
+}
+
+// Remove whitespace from both ends of a string
+function TrimString(string) {
+ if (!string) {
+ return "";
+ }
+ return string.trim();
+}
+
+function TruncateStringAtWordEnd(string, maxLength, addEllipses) {
+ // Return empty if string is null, undefined, or the empty string
+ if (!string) {
+ return "";
+ }
+
+ // We assume they probably don't want whitespace at the beginning
+ string = string.trimLeft();
+ if (string.length <= maxLength) {
+ return string;
+ }
+
+ // We need to truncate the string to maxLength or fewer chars
+ if (addEllipses) {
+ maxLength -= 3;
+ }
+ string = string.replace(RegExp("(.{0," + maxLength + "})\\s.*"), "$1");
+
+ if (string.length > maxLength) {
+ string = string.slice(0, maxLength);
+ }
+
+ if (addEllipses) {
+ string += "...";
+ }
+ return string;
+}
+
+// Replace all whitespace characters with supplied character
+// E.g.: Use charReplace = " ", to "unwrap" the string by removing line-end chars
+// Use charReplace = "_" when you don't want spaces (like in a URL)
+function ReplaceWhitespace(string, charReplace) {
+ return string.trim().replace(/\s+/g, charReplace);
+}
+
+// Replace whitespace with "_" and allow only HTML CDATA
+// characters: "a"-"z","A"-"Z","0"-"9", "_", ":", "-", ".",
+// and characters above ASCII 127
+function ConvertToCDATAString(string) {
+ return string
+ .replace(/\s+/g, "_")
+ .replace(/[^a-zA-Z0-9_\.\-\:\u0080-\uFFFF]+/g, "");
+}
+
+function GetSelectionAsText() {
+ try {
+ return GetCurrentEditor().outputToString(
+ "text/plain",
+ kOutputSelectionOnly
+ );
+ } catch (e) {}
+
+ return "";
+}
+
+/** *********** Get Current Editor and associated interfaces or info ***************/
+const nsIHTMLEditor = Ci.nsIHTMLEditor;
+const nsITableEditor = Ci.nsITableEditor;
+const nsIEditorStyleSheets = Ci.nsIEditorStyleSheets;
+const nsIEditingSession = Ci.nsIEditingSession;
+
+function GetCurrentEditor() {
+ // Get the active editor from the <editor> tag
+ // XXX This will probably change if we support > 1 editor in main Composer window
+ // (e.g. a plaintext editor for HTMLSource)
+
+ // For dialogs: Search up parent chain to find top window with editor
+ var editor;
+ try {
+ var editorElement = GetCurrentEditorElement();
+ editor = editorElement.getEditor(editorElement.contentWindow);
+
+ // Do QIs now so editor users won't have to figure out which interface to use
+ // Using "instanceof" does the QI for us.
+ editor instanceof Ci.nsIHTMLEditor;
+ } catch (e) {
+ dump(e) + "\n";
+ }
+
+ return editor;
+}
+
+function GetCurrentTableEditor() {
+ var editor = GetCurrentEditor();
+ return editor && editor instanceof nsITableEditor ? editor : null;
+}
+
+function GetCurrentEditorElement() {
+ var tmpWindow = window;
+
+ do {
+ // Get the <editor> element(s)
+ let editorItem = tmpWindow.document.querySelector("editor");
+
+ // This will change if we support > 1 editor element
+ if (editorItem) {
+ return editorItem;
+ }
+
+ tmpWindow = tmpWindow.opener;
+ } while (tmpWindow);
+
+ return null;
+}
+
+function GetCurrentCommandManager() {
+ try {
+ return GetCurrentEditorElement().commandManager;
+ } catch (e) {
+ dump(e) + "\n";
+ }
+
+ return null;
+}
+
+function GetCurrentEditorType() {
+ try {
+ return GetCurrentEditorElement().editortype;
+ } catch (e) {
+ dump(e) + "\n";
+ }
+
+ return "";
+}
+
+function IsHTMLEditor() {
+ // We don't have an editorElement, just return false
+ if (!GetCurrentEditorElement()) {
+ return false;
+ }
+
+ var editortype = GetCurrentEditorType();
+ switch (editortype) {
+ case "html":
+ case "htmlmail":
+ return true;
+
+ case "text":
+ case "textmail":
+ return false;
+
+ default:
+ dump("INVALID EDITOR TYPE: " + editortype + "\n");
+ break;
+ }
+ return false;
+}
+
+function PageIsEmptyAndUntouched() {
+ return IsDocumentEmpty() && !IsDocumentModified() && !IsHTMLSourceChanged();
+}
+
+function IsInHTMLSourceMode() {
+ return gEditorDisplayMode == kDisplayModeSource;
+}
+
+function IsInPreviewMode() {
+ return gEditorDisplayMode == kDisplayModePreview;
+}
+
+// are we editing HTML (i.e. neither in HTML source mode, nor editing a text file)
+function IsEditingRenderedHTML() {
+ return IsHTMLEditor() && !IsInHTMLSourceMode();
+}
+
+function IsWebComposer() {
+ return document.documentElement.id == "editorWindow";
+}
+
+function IsDocumentEditable() {
+ try {
+ return GetCurrentEditor().isDocumentEditable;
+ } catch (e) {}
+ return false;
+}
+
+function IsDocumentEmpty() {
+ try {
+ return GetCurrentEditor().documentIsEmpty;
+ } catch (e) {}
+ return false;
+}
+
+function IsDocumentModified() {
+ try {
+ return GetCurrentEditor().documentModified;
+ } catch (e) {}
+ return false;
+}
+
+function IsHTMLSourceChanged() {
+ // gSourceTextEditor will not be defined if we're just a text editor.
+ return gSourceTextEditor ? gSourceTextEditor.documentModified : false;
+}
+
+function newCommandParams() {
+ try {
+ return Cu.createCommandParams();
+ } catch (e) {
+ dump("error thrown in newCommandParams: " + e + "\n");
+ }
+ return null;
+}
+
+/** *********** General editing command utilities ***************/
+
+function GetDocumentTitle() {
+ try {
+ return GetCurrentEditorElement().contentDocument.title;
+ } catch (e) {}
+
+ return "";
+}
+
+function SetDocumentTitle(title) {
+ try {
+ GetCurrentEditorElement().contentDocument.title = title;
+
+ // Update window title (doesn't work if called from a dialog)
+ if ("UpdateWindowTitle" in window) {
+ window.UpdateWindowTitle();
+ }
+ } catch (e) {}
+}
+
+function EditorGetTextProperty(
+ property,
+ attribute,
+ value,
+ firstHas,
+ anyHas,
+ allHas
+) {
+ try {
+ return GetCurrentEditor().getInlinePropertyWithAttrValue(
+ property,
+ attribute,
+ value,
+ firstHas,
+ anyHas,
+ allHas
+ );
+ } catch (e) {}
+}
+
+function EditorSetTextProperty(property, attribute, value) {
+ try {
+ GetCurrentEditor().setInlineProperty(property, attribute, value);
+ if ("gContentWindow" in window) {
+ window.gContentWindow.focus();
+ }
+ } catch (e) {}
+}
+
+function EditorRemoveTextProperty(property, attribute) {
+ try {
+ GetCurrentEditor().removeInlineProperty(property, attribute);
+ if ("gContentWindow" in window) {
+ window.gContentWindow.focus();
+ }
+ } catch (e) {}
+}
+
+/** *********** Element enbabling/disabling ***************/
+
+// this function takes an elementID and a flag
+// if the element can be found by ID, then it is either enabled (by removing "disabled" attr)
+// or disabled (setAttribute) as specified in the "doEnable" parameter
+function SetElementEnabledById(elementID, doEnable) {
+ SetElementEnabled(document.getElementById(elementID), doEnable);
+}
+
+function SetElementEnabled(element, doEnable) {
+ if (element) {
+ if (doEnable) {
+ element.removeAttribute("disabled");
+ } else {
+ element.setAttribute("disabled", "true");
+ }
+ } else {
+ dump("Element not found in SetElementEnabled\n");
+ }
+}
+
+/** *********** Services / Prefs ***************/
+
+function GetFileProtocolHandler() {
+ let handler = Services.io.getProtocolHandler("file");
+ return handler.QueryInterface(Ci.nsIFileProtocolHandler);
+}
+
+function SetStringPref(aPrefName, aPrefValue) {
+ try {
+ Services.prefs.setStringPref(aPrefName, aPrefValue);
+ } catch (e) {}
+}
+
+// Set initial directory for a filepicker from URLs saved in prefs
+function SetFilePickerDirectory(filePicker, fileType) {
+ if (filePicker) {
+ try {
+ // Save current directory so we can reset it in SaveFilePickerDirectory
+ gFilePickerDirectory = filePicker.displayDirectory;
+
+ let location = Services.prefs.getComplexValue(
+ "editor.lastFileLocation." + fileType,
+ Ci.nsIFile
+ );
+ if (location) {
+ filePicker.displayDirectory = location;
+ }
+ } catch (e) {}
+ }
+}
+
+// Save the directory of the selected file to prefs
+function SaveFilePickerDirectory(filePicker, fileType) {
+ if (filePicker && filePicker.file) {
+ try {
+ var fileDir;
+ if (filePicker.file.parent) {
+ fileDir = filePicker.file.parent.QueryInterface(Ci.nsIFile);
+ }
+
+ Services.prefs.setComplexValue(
+ "editor.lastFileLocation." + fileType,
+ Ci.nsIFile,
+ fileDir
+ );
+
+ Services.prefs.savePrefFile(null);
+ } catch (e) {}
+ }
+
+ // Restore the directory used before SetFilePickerDirectory was called;
+ // This reduces interference with Browser and other module directory defaults
+ if (gFilePickerDirectory) {
+ filePicker.displayDirectory = gFilePickerDirectory;
+ }
+
+ gFilePickerDirectory = null;
+}
+
+function GetDefaultBrowserColors() {
+ var colors = {
+ TextColor: 0,
+ BackgroundColor: 0,
+ LinkColor: 0,
+ ActiveLinkColor: 0,
+ VisitedLinkColor: 0,
+ };
+ var useSysColors = Services.prefs.getBoolPref(
+ "browser.display.use_system_colors",
+ false
+ );
+
+ if (!useSysColors) {
+ colors.TextColor = Services.prefs.getCharPref(
+ "browser.display.foreground_color",
+ 0
+ );
+ colors.BackgroundColor = Services.prefs.getCharPref(
+ "browser.display.background_color",
+ 0
+ );
+ }
+ // Use OS colors for text and background if explicitly asked or pref is not set
+ if (!colors.TextColor) {
+ colors.TextColor = "windowtext";
+ }
+
+ if (!colors.BackgroundColor) {
+ colors.BackgroundColor = "window";
+ }
+
+ colors.LinkColor = Services.prefs.getCharPref("browser.anchor_color");
+ colors.ActiveLinkColor = Services.prefs.getCharPref("browser.active_color");
+ colors.VisitedLinkColor = Services.prefs.getCharPref("browser.visited_color");
+
+ return colors;
+}
+
+/** *********** URL handling ***************/
+
+function TextIsURI(selectedText) {
+ return (
+ selectedText &&
+ /^http:\/\/|^https:\/\/|^file:\/\/|^ftp:\/\/|^about:|^mailto:|^news:|^snews:|^telnet:|^ldap:|^ldaps:|^gopher:|^finger:|^javascript:/i.test(
+ selectedText
+ )
+ );
+}
+
+function IsUrlAboutBlank(urlString) {
+ return urlString == "about:blank";
+}
+
+function MakeRelativeUrl(url) {
+ let inputUrl = url.trim();
+ if (!inputUrl) {
+ return inputUrl;
+ }
+
+ // Get the filespec relative to current document's location
+ // NOTE: Can't do this if file isn't saved yet!
+ var docUrl = GetDocumentBaseUrl();
+ var docScheme = GetScheme(docUrl);
+
+ // Can't relativize if no doc scheme (page hasn't been saved)
+ if (!docScheme) {
+ return inputUrl;
+ }
+
+ var urlScheme = GetScheme(inputUrl);
+
+ // Do nothing if not the same scheme or url is already relativized
+ if (docScheme != urlScheme) {
+ return inputUrl;
+ }
+
+ // Host must be the same
+ var docHost = GetHost(docUrl);
+ var urlHost = GetHost(inputUrl);
+ if (docHost != urlHost) {
+ return inputUrl;
+ }
+
+ // Get just the file path part of the urls
+ // XXX Should we use GetCurrentEditor().documentCharacterSet for 2nd param ?
+ let docPath = Services.io.newURI(
+ docUrl,
+ GetCurrentEditor().documentCharacterSet
+ ).pathQueryRef;
+ let urlPath = Services.io.newURI(
+ inputUrl,
+ GetCurrentEditor().documentCharacterSet
+ ).pathQueryRef;
+
+ // We only return "urlPath", so we can convert the entire docPath for
+ // case-insensitive comparisons.
+ var doCaseInsensitive = docScheme == "file" && AppConstants.platform == "win";
+ if (doCaseInsensitive) {
+ docPath = docPath.toLowerCase();
+ }
+
+ // Get document filename before we start chopping up the docPath
+ var docFilename = GetFilename(docPath);
+
+ // Both url and doc paths now begin with "/"
+ // Look for shared dirs starting after that
+ urlPath = urlPath.slice(1);
+ docPath = docPath.slice(1);
+
+ var firstDirTest = true;
+ var nextDocSlash = 0;
+ var done = false;
+
+ // Remove all matching subdirs common to both doc and input urls
+ do {
+ nextDocSlash = docPath.indexOf("/");
+ var nextUrlSlash = urlPath.indexOf("/");
+
+ if (nextUrlSlash == -1) {
+ // We're done matching and all dirs in url
+ // what's left is the filename
+ done = true;
+
+ // Remove filename for named anchors in the same file
+ if (nextDocSlash == -1 && docFilename) {
+ var anchorIndex = urlPath.indexOf("#");
+ if (anchorIndex > 0) {
+ var urlFilename = doCaseInsensitive ? urlPath.toLowerCase() : urlPath;
+
+ if (urlFilename.startsWith(docFilename)) {
+ urlPath = urlPath.slice(anchorIndex);
+ }
+ }
+ }
+ } else if (nextDocSlash >= 0) {
+ // Test for matching subdir
+ var docDir = docPath.slice(0, nextDocSlash);
+ var urlDir = urlPath.slice(0, nextUrlSlash);
+ if (doCaseInsensitive) {
+ urlDir = urlDir.toLowerCase();
+ }
+
+ if (urlDir == docDir) {
+ // Remove matching dir+"/" from each path
+ // and continue to next dir.
+ docPath = docPath.slice(nextDocSlash + 1);
+ urlPath = urlPath.slice(nextUrlSlash + 1);
+ } else {
+ // No match, we're done.
+ done = true;
+
+ // Be sure we are on the same local drive or volume
+ // (the first "dir" in the path) because we can't
+ // relativize to different drives/volumes.
+ // UNIX doesn't have volumes, so we must not do this else
+ // the first directory will be misinterpreted as a volume name.
+ if (
+ firstDirTest &&
+ docScheme == "file" &&
+ AppConstants.platform != "unix"
+ ) {
+ return inputUrl;
+ }
+ }
+ } else {
+ // No more doc dirs left, we're done
+ done = true;
+ }
+
+ firstDirTest = false;
+ } while (!done);
+
+ // Add "../" for each dir left in docPath
+ while (nextDocSlash > 0) {
+ urlPath = "../" + urlPath;
+ nextDocSlash = docPath.indexOf("/", nextDocSlash + 1);
+ }
+ return urlPath;
+}
+
+function MakeAbsoluteUrl(url) {
+ let resultUrl = TrimString(url);
+ if (!resultUrl) {
+ return resultUrl;
+ }
+
+ // Check if URL is already absolute, i.e., it has a scheme
+ let urlScheme = GetScheme(resultUrl);
+
+ if (urlScheme) {
+ return resultUrl;
+ }
+
+ let docUrl = GetDocumentBaseUrl();
+ let docScheme = GetScheme(docUrl);
+
+ // Can't relativize if no doc scheme (page hasn't been saved)
+ if (!docScheme) {
+ return resultUrl;
+ }
+
+ // Make a URI object to use its "resolve" method
+ let absoluteUrl = resultUrl;
+ let docUri = Services.io.newURI(
+ docUrl,
+ GetCurrentEditor().documentCharacterSet
+ );
+
+ try {
+ absoluteUrl = docUri.resolve(resultUrl);
+ // This is deprecated and buggy!
+ // If used, we must make it a path for the parent directory (remove filename)
+ // absoluteUrl = IOService.resolveRelativePath(resultUrl, docUrl);
+ } catch (e) {}
+
+ return absoluteUrl;
+}
+
+// Get the HREF of the page's <base> tag or the document location
+// returns empty string if no base href and document hasn't been saved yet
+function GetDocumentBaseUrl() {
+ try {
+ var docUrl;
+
+ // if document supplies a <base> tag, use that URL instead
+ let base = GetCurrentEditor().document.querySelector("base");
+ if (base) {
+ docUrl = base.getAttribute("href");
+ }
+ if (!docUrl) {
+ docUrl = GetDocumentUrl();
+ }
+
+ if (!IsUrlAboutBlank(docUrl)) {
+ return docUrl;
+ }
+ } catch (e) {}
+ return "";
+}
+
+function GetDocumentUrl() {
+ try {
+ return GetCurrentEditor().document.URL;
+ } catch (e) {}
+ return "";
+}
+
+// Extract the scheme (e.g., 'file', 'http') from a URL string
+function GetScheme(urlspec) {
+ var resultUrl = TrimString(urlspec);
+ // Unsaved document URL has no acceptable scheme yet
+ if (!resultUrl || IsUrlAboutBlank(resultUrl)) {
+ return "";
+ }
+
+ var scheme = "";
+ try {
+ // This fails if there's no scheme
+ scheme = Services.io.extractScheme(resultUrl);
+ } catch (e) {}
+
+ return scheme ? scheme.toLowerCase() : "";
+}
+
+function GetHost(urlspec) {
+ if (!urlspec) {
+ return "";
+ }
+
+ var host = "";
+ try {
+ host = Services.io.newURI(urlspec).host;
+ } catch (e) {}
+
+ return host;
+}
+
+function GetUsername(urlspec) {
+ if (!urlspec) {
+ return "";
+ }
+
+ var username = "";
+ try {
+ username = Services.io.newURI(urlspec).username;
+ } catch (e) {}
+
+ return username;
+}
+
+function GetFilename(urlspec) {
+ if (!urlspec || IsUrlAboutBlank(urlspec)) {
+ return "";
+ }
+
+ var filename;
+
+ try {
+ let uri = Services.io.newURI(urlspec);
+ if (uri) {
+ let url = uri.QueryInterface(Ci.nsIURL);
+ if (url) {
+ filename = url.fileName;
+ }
+ }
+ } catch (e) {}
+
+ return filename ? filename : "";
+}
+
+// Return the url without username and password
+// Optional output objects return extracted username and password strings
+// This uses just string routines via nsIIOServices
+function StripUsernamePassword(urlspec, usernameObj, passwordObj) {
+ urlspec = TrimString(urlspec);
+ if (!urlspec || IsUrlAboutBlank(urlspec)) {
+ return urlspec;
+ }
+
+ if (usernameObj) {
+ usernameObj.value = "";
+ }
+ if (passwordObj) {
+ passwordObj.value = "";
+ }
+
+ // "@" must exist else we will never detect username or password
+ var atIndex = urlspec.indexOf("@");
+ if (atIndex > 0) {
+ try {
+ let uri = Services.io.newURI(urlspec);
+ let username = uri.username;
+ let password = uri.password;
+
+ if (usernameObj && username) {
+ usernameObj.value = username;
+ }
+ if (passwordObj && password) {
+ passwordObj.value = password;
+ }
+ if (username) {
+ let usernameStart = urlspec.indexOf(username);
+ if (usernameStart != -1) {
+ return urlspec.slice(0, usernameStart) + urlspec.slice(atIndex + 1);
+ }
+ }
+ } catch (e) {}
+ }
+ return urlspec;
+}
+
+function StripPassword(urlspec, passwordObj) {
+ urlspec = TrimString(urlspec);
+ if (!urlspec || IsUrlAboutBlank(urlspec)) {
+ return urlspec;
+ }
+
+ if (passwordObj) {
+ passwordObj.value = "";
+ }
+
+ // "@" must exist else we will never detect password
+ var atIndex = urlspec.indexOf("@");
+ if (atIndex > 0) {
+ try {
+ let password = Services.io.newURI(urlspec).password;
+
+ if (passwordObj && password) {
+ passwordObj.value = password;
+ }
+ if (password) {
+ // Find last ":" before "@"
+ let colon = urlspec.lastIndexOf(":", atIndex);
+ if (colon != -1) {
+ // Include the "@"
+ return urlspec.slice(0, colon) + urlspec.slice(atIndex);
+ }
+ }
+ } catch (e) {}
+ }
+ return urlspec;
+}
+
+// Version to use when you have an nsIURI object
+function StripUsernamePasswordFromURI(uri) {
+ var urlspec = "";
+ if (uri) {
+ try {
+ urlspec = uri.spec;
+ var userPass = uri.userPass;
+ if (userPass) {
+ let start = urlspec.indexOf(userPass);
+ urlspec =
+ urlspec.slice(0, start) + urlspec.slice(start + userPass.length + 1);
+ }
+ } catch (e) {}
+ }
+ return urlspec;
+}
+
+function InsertUsernameIntoUrl(urlspec, username) {
+ if (!urlspec || !username) {
+ return urlspec;
+ }
+
+ try {
+ let URI = Services.io.newURI(
+ urlspec,
+ GetCurrentEditor().documentCharacterSet
+ );
+ URI.username = username;
+ return URI.spec;
+ } catch (e) {}
+
+ return urlspec;
+}
+
+function ConvertRGBColorIntoHEXColor(color) {
+ if (/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/.test(color)) {
+ var r = Number(RegExp.$1).toString(16);
+ if (r.length == 1) {
+ r = "0" + r;
+ }
+ var g = Number(RegExp.$2).toString(16);
+ if (g.length == 1) {
+ g = "0" + g;
+ }
+ var b = Number(RegExp.$3).toString(16);
+ if (b.length == 1) {
+ b = "0" + b;
+ }
+ return "#" + r + g + b;
+ }
+
+ return color;
+}
+
+/** *********** CSS ***************/
+
+function GetHTMLOrCSSStyleValue(element, attrName, cssPropertyName) {
+ var value;
+ if (Services.prefs.getBoolPref("editor.use_css") && IsHTMLEditor()) {
+ value = element.style.getPropertyValue(cssPropertyName);
+ }
+
+ if (!value) {
+ value = element.getAttribute(attrName);
+ }
+
+ if (!value) {
+ return "";
+ }
+
+ return value;
+}
+
+/** *********** Miscellaneous ***************/
+// Clone simple JS objects
+function Clone(obj) {
+ var clone = {};
+ for (var i in obj) {
+ if (typeof obj[i] == "object") {
+ clone[i] = Clone(obj[i]);
+ } else {
+ clone[i] = obj[i];
+ }
+ }
+ return clone;
+}
+
+/**
+ * Utility functions to handle shortended data: URLs in EdColorProps.js and EdImageOverlay.js.
+ */
+
+/**
+ * Is the passed in image URI a shortened data URI?
+ * @return {bool}
+ */
+function isImageDataShortened(aImageData) {
+ return /^data:/i.test(aImageData) && aImageData.includes("…");
+}
+
+/**
+ * Event handler for Copy or Cut
+ * @param aEvent the event
+ */
+function onCopyOrCutShortened(aEvent) {
+ // Put the original data URI onto the clipboard in case the value
+ // is a shortened data URI.
+ let field = aEvent.target;
+ let startPos = field.selectionStart;
+ if (startPos == undefined) {
+ return;
+ }
+ let endPos = field.selectionEnd;
+ let selection = field.value.substring(startPos, endPos).trim();
+
+ // Test that a) the user selected the whole value,
+ // b) the value is a data URI,
+ // c) it contains the ellipsis we added. Otherwise it could be
+ // a new value that the user pasted in.
+ if (selection == field.value.trim() && isImageDataShortened(selection)) {
+ aEvent.clipboardData.setData("text/plain", field.fullDataURI);
+ if (aEvent.type == "cut") {
+ // We have to cut the selection manually. Since we tested that
+ // everything was selected, we can just reset the field.
+ field.value = "";
+ }
+ aEvent.preventDefault();
+ }
+}
+
+/**
+ * Set up element showing an image URI with a shortened version.
+ * and add event handler for Copy or Cut.
+ *
+ * @param aImageData the data: URL of the image to be shortened.
+ * Note: Original stored in 'aDialogField.fullDataURI'.
+ * @param aDialogField The field of the dialog to contain the data.
+ * @return {bool} URL was shortened?
+ */
+function shortenImageData(aImageData, aDialogField) {
+ let shortened = false;
+ aDialogField.value = aImageData.replace(/^(data:.+;base64,)(.*)/i, function(
+ match,
+ nonDataPart,
+ dataPart
+ ) {
+ if (dataPart.length <= 35) {
+ return match;
+ }
+
+ shortened = true;
+ aDialogField.addEventListener("copy", onCopyOrCutShortened);
+ aDialogField.addEventListener("cut", onCopyOrCutShortened);
+ aDialogField.fullDataURI = aImageData;
+ aDialogField.removeAttribute("tooltiptext");
+ aDialogField.setAttribute("tooltip", "shortenedDataURI");
+ return (
+ nonDataPart +
+ dataPart.substring(0, 5) +
+ "…" +
+ dataPart.substring(dataPart.length - 30)
+ );
+ });
+ return shortened;
+}
+
+/**
+ * Return full data URIs for a shortened element.
+ *
+ * @param aDialogField The field of the dialog containing the data.
+ */
+function restoredImageData(aDialogField) {
+ return aDialogField.fullDataURI;
+}
diff --git a/comm/suite/editor/base/content/images/bringtofront-disabled.png b/comm/suite/editor/base/content/images/bringtofront-disabled.png
new file mode 100644
index 0000000000..ee8bfb0185
--- /dev/null
+++ b/comm/suite/editor/base/content/images/bringtofront-disabled.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/bringtofront.png b/comm/suite/editor/base/content/images/bringtofront.png
new file mode 100644
index 0000000000..ab22be7e66
--- /dev/null
+++ b/comm/suite/editor/base/content/images/bringtofront.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/sendtoback-disabled.png b/comm/suite/editor/base/content/images/sendtoback-disabled.png
new file mode 100644
index 0000000000..fe1e0502b2
--- /dev/null
+++ b/comm/suite/editor/base/content/images/sendtoback-disabled.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/sendtoback.png b/comm/suite/editor/base/content/images/sendtoback.png
new file mode 100644
index 0000000000..5aa02b7f0b
--- /dev/null
+++ b/comm/suite/editor/base/content/images/sendtoback.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-a.png b/comm/suite/editor/base/content/images/tag-a.png
new file mode 100644
index 0000000000..e66eb1db47
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-a.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-abr.png b/comm/suite/editor/base/content/images/tag-abr.png
new file mode 100644
index 0000000000..a04af50b35
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-abr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-acr.png b/comm/suite/editor/base/content/images/tag-acr.png
new file mode 100644
index 0000000000..75cc3a5a9a
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-acr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-adr.png b/comm/suite/editor/base/content/images/tag-adr.png
new file mode 100644
index 0000000000..63b95a3e02
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-adr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-anchor.png b/comm/suite/editor/base/content/images/tag-anchor.png
new file mode 100644
index 0000000000..5b116c668c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-anchor.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-app.png b/comm/suite/editor/base/content/images/tag-app.png
new file mode 100644
index 0000000000..ad0c0cac30
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-app.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ara.png b/comm/suite/editor/base/content/images/tag-ara.png
new file mode 100644
index 0000000000..6c8354fa45
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ara.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-b.png b/comm/suite/editor/base/content/images/tag-b.png
new file mode 100644
index 0000000000..0a40231180
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-b.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-bas.png b/comm/suite/editor/base/content/images/tag-bas.png
new file mode 100644
index 0000000000..d86b376ed1
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-bas.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-bdo.png b/comm/suite/editor/base/content/images/tag-bdo.png
new file mode 100644
index 0000000000..13a0db68cd
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-bdo.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-big.png b/comm/suite/editor/base/content/images/tag-big.png
new file mode 100644
index 0000000000..1bf075320c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-big.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-blq.png b/comm/suite/editor/base/content/images/tag-blq.png
new file mode 100644
index 0000000000..7faa4c2846
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-blq.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-body.png b/comm/suite/editor/base/content/images/tag-body.png
new file mode 100644
index 0000000000..df47443823
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-body.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-br.png b/comm/suite/editor/base/content/images/tag-br.png
new file mode 100644
index 0000000000..8e93c47db3
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-br.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-bsf.png b/comm/suite/editor/base/content/images/tag-bsf.png
new file mode 100644
index 0000000000..8b2b078619
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-bsf.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-btn.png b/comm/suite/editor/base/content/images/tag-btn.png
new file mode 100644
index 0000000000..2996ff9a74
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-btn.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-cit.png b/comm/suite/editor/base/content/images/tag-cit.png
new file mode 100644
index 0000000000..37624fe222
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-cit.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-clg.png b/comm/suite/editor/base/content/images/tag-clg.png
new file mode 100644
index 0000000000..1c912ef1be
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-clg.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-cod.png b/comm/suite/editor/base/content/images/tag-cod.png
new file mode 100644
index 0000000000..5b7831f386
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-cod.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-col.png b/comm/suite/editor/base/content/images/tag-col.png
new file mode 100644
index 0000000000..834b57bb7b
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-col.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-cpt.png b/comm/suite/editor/base/content/images/tag-cpt.png
new file mode 100644
index 0000000000..4bcba8bf33
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-cpt.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ctr.png b/comm/suite/editor/base/content/images/tag-ctr.png
new file mode 100644
index 0000000000..3e6aee0663
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ctr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-dd.png b/comm/suite/editor/base/content/images/tag-dd.png
new file mode 100644
index 0000000000..0b192b50ac
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-dd.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-del.png b/comm/suite/editor/base/content/images/tag-del.png
new file mode 100644
index 0000000000..0dd897c7be
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-del.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-dfn.png b/comm/suite/editor/base/content/images/tag-dfn.png
new file mode 100644
index 0000000000..ea820aeecc
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-dfn.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-dir.png b/comm/suite/editor/base/content/images/tag-dir.png
new file mode 100644
index 0000000000..3f20e2dd70
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-dir.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-div.png b/comm/suite/editor/base/content/images/tag-div.png
new file mode 100644
index 0000000000..8478e20f03
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-div.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-dl.png b/comm/suite/editor/base/content/images/tag-dl.png
new file mode 100644
index 0000000000..576b6f3968
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-dl.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-dt.png b/comm/suite/editor/base/content/images/tag-dt.png
new file mode 100644
index 0000000000..4c9121ebd5
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-dt.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-em.png b/comm/suite/editor/base/content/images/tag-em.png
new file mode 100644
index 0000000000..1a5f24551e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-em.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-fld.png b/comm/suite/editor/base/content/images/tag-fld.png
new file mode 100644
index 0000000000..c299e5cad1
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-fld.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-fnt.png b/comm/suite/editor/base/content/images/tag-fnt.png
new file mode 100644
index 0000000000..eb8dba7c9e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-fnt.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-for.png b/comm/suite/editor/base/content/images/tag-for.png
new file mode 100644
index 0000000000..bb38c428d0
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-for.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-frm.png b/comm/suite/editor/base/content/images/tag-frm.png
new file mode 100644
index 0000000000..5bd4689246
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-frm.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-fst.png b/comm/suite/editor/base/content/images/tag-fst.png
new file mode 100644
index 0000000000..269d5505f4
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-fst.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-h1.png b/comm/suite/editor/base/content/images/tag-h1.png
new file mode 100644
index 0000000000..2edff90dfe
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-h1.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-h2.png b/comm/suite/editor/base/content/images/tag-h2.png
new file mode 100644
index 0000000000..a55fb07cd4
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-h2.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-h3.png b/comm/suite/editor/base/content/images/tag-h3.png
new file mode 100644
index 0000000000..c8aa875994
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-h3.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-h4.png b/comm/suite/editor/base/content/images/tag-h4.png
new file mode 100644
index 0000000000..dd73041ff3
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-h4.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-h5.png b/comm/suite/editor/base/content/images/tag-h5.png
new file mode 100644
index 0000000000..1f3e94d5e3
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-h5.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-h6.png b/comm/suite/editor/base/content/images/tag-h6.png
new file mode 100644
index 0000000000..c2153ea2cc
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-h6.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-hed.png b/comm/suite/editor/base/content/images/tag-hed.png
new file mode 100644
index 0000000000..c1b87f447c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-hed.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-hr.png b/comm/suite/editor/base/content/images/tag-hr.png
new file mode 100644
index 0000000000..b9d6a35a58
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-hr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-html.png b/comm/suite/editor/base/content/images/tag-html.png
new file mode 100644
index 0000000000..0d1c9b361c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-html.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-i.png b/comm/suite/editor/base/content/images/tag-i.png
new file mode 100644
index 0000000000..e75db74169
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-i.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ifr.png b/comm/suite/editor/base/content/images/tag-ifr.png
new file mode 100644
index 0000000000..f212680ea4
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ifr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-img.png b/comm/suite/editor/base/content/images/tag-img.png
new file mode 100644
index 0000000000..f0b458e356
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-img.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-inp.png b/comm/suite/editor/base/content/images/tag-inp.png
new file mode 100644
index 0000000000..d9e81ea407
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-inp.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ins.png b/comm/suite/editor/base/content/images/tag-ins.png
new file mode 100644
index 0000000000..a477f94b88
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ins.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-isx.png b/comm/suite/editor/base/content/images/tag-isx.png
new file mode 100644
index 0000000000..4f53e9bf1d
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-isx.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-kbd.png b/comm/suite/editor/base/content/images/tag-kbd.png
new file mode 100644
index 0000000000..4945dfbd74
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-kbd.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-lbl.png b/comm/suite/editor/base/content/images/tag-lbl.png
new file mode 100644
index 0000000000..b1533723f1
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-lbl.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-lgn.png b/comm/suite/editor/base/content/images/tag-lgn.png
new file mode 100644
index 0000000000..c9d3149a9f
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-lgn.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-li.png b/comm/suite/editor/base/content/images/tag-li.png
new file mode 100644
index 0000000000..1d63b29e7c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-li.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-lnk.png b/comm/suite/editor/base/content/images/tag-lnk.png
new file mode 100644
index 0000000000..58194ca38f
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-lnk.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-lst.png b/comm/suite/editor/base/content/images/tag-lst.png
new file mode 100644
index 0000000000..f79929c047
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-lst.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-map.png b/comm/suite/editor/base/content/images/tag-map.png
new file mode 100644
index 0000000000..9fc0dfe028
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-map.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-men.png b/comm/suite/editor/base/content/images/tag-men.png
new file mode 100644
index 0000000000..ccde7feec1
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-men.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-met.png b/comm/suite/editor/base/content/images/tag-met.png
new file mode 100644
index 0000000000..b6d86a7946
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-met.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-nbr.png b/comm/suite/editor/base/content/images/tag-nbr.png
new file mode 100644
index 0000000000..80ee8fd90c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-nbr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-nfr.png b/comm/suite/editor/base/content/images/tag-nfr.png
new file mode 100644
index 0000000000..885c530bf8
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-nfr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-nsc.png b/comm/suite/editor/base/content/images/tag-nsc.png
new file mode 100644
index 0000000000..fdcde6f81e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-nsc.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-obj.png b/comm/suite/editor/base/content/images/tag-obj.png
new file mode 100644
index 0000000000..05f80f0c87
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-obj.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ol.png b/comm/suite/editor/base/content/images/tag-ol.png
new file mode 100644
index 0000000000..22456f8d2d
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ol.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-opg.png b/comm/suite/editor/base/content/images/tag-opg.png
new file mode 100644
index 0000000000..9bcb0948fa
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-opg.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-opt.png b/comm/suite/editor/base/content/images/tag-opt.png
new file mode 100644
index 0000000000..46ec67560e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-opt.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-p.png b/comm/suite/editor/base/content/images/tag-p.png
new file mode 100644
index 0000000000..0f49a89eec
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-p.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-pln.png b/comm/suite/editor/base/content/images/tag-pln.png
new file mode 100644
index 0000000000..e6d49b442c
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-pln.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-pre.png b/comm/suite/editor/base/content/images/tag-pre.png
new file mode 100644
index 0000000000..84423c484e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-pre.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-prm.png b/comm/suite/editor/base/content/images/tag-prm.png
new file mode 100644
index 0000000000..e65d57ec1e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-prm.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-q.png b/comm/suite/editor/base/content/images/tag-q.png
new file mode 100644
index 0000000000..a34e65d542
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-q.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-s.png b/comm/suite/editor/base/content/images/tag-s.png
new file mode 100644
index 0000000000..37564252ee
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-s.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-scr.png b/comm/suite/editor/base/content/images/tag-scr.png
new file mode 100644
index 0000000000..c8df1cefe1
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-scr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-slc.png b/comm/suite/editor/base/content/images/tag-slc.png
new file mode 100644
index 0000000000..837b0eab89
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-slc.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-sml.png b/comm/suite/editor/base/content/images/tag-sml.png
new file mode 100644
index 0000000000..4df1639861
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-sml.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-smp.png b/comm/suite/editor/base/content/images/tag-smp.png
new file mode 100644
index 0000000000..e95e85d75f
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-smp.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-spn.png b/comm/suite/editor/base/content/images/tag-spn.png
new file mode 100644
index 0000000000..d1066e5248
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-spn.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-stk.png b/comm/suite/editor/base/content/images/tag-stk.png
new file mode 100644
index 0000000000..5700f9ed6e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-stk.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-stl.png b/comm/suite/editor/base/content/images/tag-stl.png
new file mode 100644
index 0000000000..22fead4662
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-stl.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-stn.png b/comm/suite/editor/base/content/images/tag-stn.png
new file mode 100644
index 0000000000..9155590bfd
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-stn.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-sub.png b/comm/suite/editor/base/content/images/tag-sub.png
new file mode 100644
index 0000000000..f1ea4abbab
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-sub.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-sup.png b/comm/suite/editor/base/content/images/tag-sup.png
new file mode 100644
index 0000000000..a814d9f815
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-sup.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-tbd.png b/comm/suite/editor/base/content/images/tag-tbd.png
new file mode 100644
index 0000000000..e46c1931b3
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-tbd.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-tbl.png b/comm/suite/editor/base/content/images/tag-tbl.png
new file mode 100644
index 0000000000..cb553528f0
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-tbl.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-td.png b/comm/suite/editor/base/content/images/tag-td.png
new file mode 100644
index 0000000000..beebc393a4
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-td.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-tft.png b/comm/suite/editor/base/content/images/tag-tft.png
new file mode 100644
index 0000000000..cb0db0fe21
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-tft.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-th.png b/comm/suite/editor/base/content/images/tag-th.png
new file mode 100644
index 0000000000..dac140f41e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-th.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-thd.png b/comm/suite/editor/base/content/images/tag-thd.png
new file mode 100644
index 0000000000..7b7325c2af
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-thd.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-tr.png b/comm/suite/editor/base/content/images/tag-tr.png
new file mode 100644
index 0000000000..5ab2fc0e85
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-tr.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-tt.png b/comm/suite/editor/base/content/images/tag-tt.png
new file mode 100644
index 0000000000..61108f6366
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-tt.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ttl.png b/comm/suite/editor/base/content/images/tag-ttl.png
new file mode 100644
index 0000000000..2cbdbe3943
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ttl.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-txt.png b/comm/suite/editor/base/content/images/tag-txt.png
new file mode 100644
index 0000000000..2ec48df034
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-txt.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-u.png b/comm/suite/editor/base/content/images/tag-u.png
new file mode 100644
index 0000000000..c435789a59
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-u.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-ul.png b/comm/suite/editor/base/content/images/tag-ul.png
new file mode 100644
index 0000000000..5bdee5d496
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-ul.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-userdefined.png b/comm/suite/editor/base/content/images/tag-userdefined.png
new file mode 100644
index 0000000000..1b36f9f259
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-userdefined.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-var.png b/comm/suite/editor/base/content/images/tag-var.png
new file mode 100644
index 0000000000..aa8200597b
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-var.png
Binary files differ
diff --git a/comm/suite/editor/base/content/images/tag-xmp.png b/comm/suite/editor/base/content/images/tag-xmp.png
new file mode 100644
index 0000000000..3c66fa0d9e
--- /dev/null
+++ b/comm/suite/editor/base/content/images/tag-xmp.png
Binary files differ
diff --git a/comm/suite/editor/base/content/publishprefs.js b/comm/suite/editor/base/content/publishprefs.js
new file mode 100644
index 0000000000..4de3b4f282
--- /dev/null
+++ b/comm/suite/editor/base/content/publishprefs.js
@@ -0,0 +1,867 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/****************** Get publishing data methods *******************/
+
+// Build an array of all publish site data obtained from prefs
+function GetPublishSiteData()
+{
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return null;
+
+ // Array of site names - sorted, but don't put default name first
+ var siteNameList = GetSiteNameList(true, false);
+ if (!siteNameList)
+ return null;
+
+ // Array of all site data
+ var siteArray = [];
+
+ // We rewrite siteName prefs to eliminate names if data is bad
+ // and to be sure order is the same as sorted name list
+ try {
+ publishBranch.deleteBranch("site_name.");
+ } catch (e) {}
+
+ // Get publish data using siteName as the key
+ var index = 0;
+ for (var i = 0; i < siteNameList.length; i++)
+ {
+ // Associated data uses site name as key
+ var publishData = GetPublishData_internal(publishBranch, siteNameList[i]);
+ if (publishData)
+ {
+ siteArray[index] = publishData;
+ SetPublishStringPref(publishBranch, "site_name."+index, siteNameList[i]);
+ index++;
+ }
+ else
+ {
+ try {
+ // Remove bad site prefs now
+ publishBranch.deleteBranch("site_data." + siteNameList[i] + ".");
+ } catch (e) {}
+ }
+ }
+
+ SavePrefFile();
+
+ if (index == 0) // No Valid pref records found!
+ return null;
+
+
+ return siteArray;
+}
+
+function GetDefaultPublishSiteName()
+{
+ var publishBranch = GetPublishPrefsBranch();
+ var name = "";
+ if (publishBranch)
+ name = GetPublishStringPref(publishBranch, "default_site");
+
+ return name;
+}
+
+// Return object with all info needed to publish
+// from database of sites previously published to.
+function CreatePublishDataFromUrl(docUrl)
+{
+ if (!docUrl || IsUrlAboutBlank(docUrl) || GetScheme(docUrl) == "file")
+ return null;
+
+ var pubSiteData = GetPublishSiteData();
+ if (pubSiteData)
+ {
+ var dirObj = {};
+ var index = FindSiteIndexAndDocDir(pubSiteData, docUrl, dirObj);
+ var publishData;
+ if (index != -1)
+ {
+ publishData = pubSiteData[index];
+ publishData.docDir = FormatDirForPublishing(dirObj.value)
+
+ //XXX Problem: OtherDir: How do we decide when to use the dir in
+ // publishSiteData (default DocDir) or docDir from current filepath?
+ publishData.otherDir = FormatDirForPublishing(pubSiteData[index].otherDir);
+
+ publishData.filename = GetFilename(docUrl);
+ publishData.notInSiteData = false;
+ return publishData;
+ }
+ }
+
+ // Document wasn't found in publish site database
+ // Create data just from URL
+
+ // Extract username and password from docUrl
+ var userObj = {};
+ var passObj = {};
+ var pubUrl = StripUsernamePassword(docUrl, userObj, passObj);
+
+ // Strip off filename
+ var lastSlash = pubUrl.lastIndexOf("\/");
+ //XXX Look for "?", "=", and "&" ?
+ pubUrl = pubUrl.slice(0, lastSlash+1);
+
+ var siteName = CreateSiteNameFromUrl(pubUrl, pubSiteData);
+
+ publishData = {
+ siteName : siteName,
+ previousSiteName : siteName,
+ filename : GetFilename(docUrl),
+ username : userObj.value,
+ password : passObj.value,
+ savePassword : false,
+ publishUrl : pubUrl,
+ browseUrl : pubUrl,
+ docDir : "",
+ otherDir : "",
+ publishOtherFiles : true,
+ dirList : [""],
+ saveDirs : false,
+ notInSiteData : true
+ }
+
+ return publishData;
+}
+
+function CreateSiteNameFromUrl(url, publishSiteData)
+{
+ var host = GetHost(url);
+ var schemePostfix = " (" + GetScheme(url) + ")";
+ var siteName = host + schemePostfix;
+
+ if (publishSiteData)
+ {
+ // Look for duplicates. Append "-1" etc until unique name found
+ var i = 1;
+ var exists = false;
+ do {
+ exists = PublishSiteNameExists(siteName, publishSiteData, -1)
+ if (exists)
+ siteName = host + "-" + i + schemePostfix;
+ i++;
+ }
+ while (exists);
+ }
+ return siteName;
+}
+
+// Similar to above, but in param is a site profile name
+// Note that this is more efficient than getting from a URL,
+// since we don't have to get all the sitedata but can key off of sitename.
+// Caller must supply the current docUrl or just a filename
+// If doc URL is supplied, we find the publish subdirectory if publishUrl is part of docUrl
+function GetPublishDataFromSiteName(siteName, docUrlOrFilename)
+{
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return null;
+
+ var siteNameList = GetSiteNameList(false, false);
+ if (!siteNameList)
+ return null;
+ for (var i = 0; i < siteNameList.length; i++)
+ {
+ if (siteNameList[i] == siteName)
+ {
+ var publishData = GetPublishData_internal(publishBranch, siteName);
+ if (GetScheme(docUrlOrFilename))
+ FillInMatchingPublishData(publishData, docUrlOrFilename);
+ else
+ publishData.filename = docUrlOrFilename;
+
+ return publishData;
+ }
+ }
+ return null;
+}
+
+function GetDefaultPublishData()
+{
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return null;
+
+ var siteName = GetPublishStringPref(publishBranch, "default_site");
+ if (!siteName)
+ return null;
+
+ return GetPublishData_internal(publishBranch, siteName);
+}
+
+function GetPublishData_internal(publishBranch, siteName)
+{
+ if (!publishBranch || !siteName)
+ return null;
+
+ var prefPrefix = "site_data." + siteName + ".";
+
+ // We must have a publish url, else we ignore this site
+ // (siteData and siteNames for sites with incomplete data
+ // will get deleted by SavePublishSiteDataToPrefs)
+ var publishUrl = GetPublishStringPref(publishBranch, prefPrefix+"url");
+ if (!publishUrl)
+ return null;
+
+ var savePassword = false;
+ var publishOtherFiles = true;
+ try {
+ savePassword = publishBranch.getBoolPref(prefPrefix+"save_password");
+ publishOtherFiles = publishBranch.getBoolPref(prefPrefix+"publish_other_files");
+ } catch (e) {}
+
+ var publishData = {
+ siteName : siteName,
+ previousSiteName : siteName,
+ filename : "",
+ username : GetPublishStringPref(publishBranch, prefPrefix+"username"),
+ savePassword : savePassword,
+ publishUrl : publishUrl,
+ browseUrl : GetPublishStringPref(publishBranch, prefPrefix+"browse_url"),
+ docDir : FormatDirForPublishing(GetPublishStringPref(publishBranch, prefPrefix+"doc_dir")),
+ otherDir : FormatDirForPublishing(GetPublishStringPref(publishBranch, prefPrefix+"other_dir")),
+ publishOtherFiles : publishOtherFiles,
+ saveDirs : false
+ }
+
+ // Get password from PasswordManager
+ publishData.password = GetSavedPassword(publishData);
+
+ // If password was found, user must have checked "Save password"
+ // checkbox in prompt outside of publishing, so override the pref we stored
+ if (publishData.password)
+ {
+ if (!savePassword)
+ {
+ try {
+ publishPrefsBranch.setBoolPref(prefPrefix+"save_password", true);
+ } catch (e) {}
+ }
+ publishData.savePassword = true;
+ }
+
+ // Build history list of directories
+ // Always supply the root dir
+ publishData.dirList = [""];
+
+ // Get the rest from prefs
+ var dirPrefs;
+ try {
+ dirPrefs = publishBranch.getChildList(prefPrefix+"dir.");
+ } catch (e) {}
+
+ if (dirPrefs && dirPrefs.length > 0)
+ {
+ if (dirPrefs.length > 1)
+ dirPrefs.sort();
+
+ for (var j = 0; j < dirPrefs.length; j++)
+ {
+ var dirName = GetPublishStringPref(publishBranch, dirPrefs[j]);
+ if (dirName)
+ publishData.dirList[j+1] = dirName;
+ }
+ }
+
+ return publishData;
+}
+
+/****************** Save publishing data methods *********************/
+
+// Save the siteArray containing all current publish site data
+function SavePublishSiteDataToPrefs(siteArray, defaultName)
+{
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return false;
+
+ try {
+ if (siteArray)
+ {
+ var defaultFound = false;
+
+ // Clear existing names and data -- rebuild all site prefs
+ publishBranch.deleteBranch("site_name.");
+ publishBranch.deleteBranch("site_data.");
+
+ for (var i = 0; i < siteArray.length; i++)
+ {
+ SavePublishData_Internal(publishBranch, siteArray[i], i);
+ if (!defaultFound)
+ defaultFound = defaultName == siteArray[i].siteName;
+ }
+ // Assure that we have a default name
+ if (siteArray.length && !defaultFound)
+ defaultName = siteArray[0].siteName;
+ }
+
+ // Save default site name
+ SetPublishStringPref(publishBranch, "default_site", defaultName);
+
+ // Force saving to file so next page edited finds these values
+ SavePrefFile();
+ }
+ catch (ex) { return false; }
+
+ return true;
+}
+
+// Update prefs if publish site already exists
+// or add prefs for a new site
+function SavePublishDataToPrefs(publishData)
+{
+ if (!publishData || !publishData.publishUrl)
+ return false;
+
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return false;
+
+ // Create name from URL if no site name is provided
+ if (!publishData.siteName)
+ publishData.siteName = CreateSiteNameFromUrl(publishData.publishUrl, publishData);
+
+ var siteNamePrefs;
+ try {
+ siteNamePrefs = publishBranch.getChildList("site_name.");
+ } catch (e) {}
+
+ if (!siteNamePrefs || siteNamePrefs.length == 0)
+ {
+ // We currently have no site prefs, so create them
+ var siteData = [publishData];
+ return SavePublishSiteDataToPrefs(siteData, publishData.siteName);
+ }
+
+ // Use "previous" name if available in case it was changed
+ var previousSiteName = ("previousSiteName" in publishData && publishData.previousSiteName) ?
+ publishData.previousSiteName : publishData.siteName;
+
+ // Find site number of existing site or fall through at next available one
+ // (Number is arbitrary; needed to construct unique "site_name.x" pref string)
+ for (var i = 0; i < siteNamePrefs.length; i++)
+ {
+ var siteName = GetPublishStringPref(publishBranch, "site_name."+i);
+
+ if (siteName == previousSiteName)
+ {
+ // Delete prefs for an existing site
+ try {
+ publishBranch.deleteBranch("site_data." + siteName + ".");
+ } catch (e) {}
+ break;
+ }
+ }
+
+ // We've taken care of finding old duplicate, so be sure 'previous name' is current
+ publishData.previousSiteName = publishData.siteName;
+
+ var ret = SavePublishData_Internal(publishBranch, publishData, i);
+ if (ret)
+ {
+ // Check if siteName was the default and we need to update that
+ var defaultSiteName = GetPublishStringPref(publishBranch, "default_site");
+ if (previousSiteName == defaultSiteName
+ && publishData.siteName != defaultSiteName)
+ SetPublishStringPref(publishBranch, "default_site", publishData.siteName);
+
+ SavePrefFile();
+
+ // Clear signal to save these data
+ if ("notInSiteData" in publishData && publishData.notInSiteData)
+ publishData.notInSiteData = false;
+ }
+ return ret;
+}
+
+// Save data at a particular site number
+function SavePublishData_Internal(publishPrefsBranch, publishData, siteIndex)
+{
+ if (!publishPrefsBranch || !publishData)
+ return false;
+
+ SetPublishStringPref(publishPrefsBranch, "site_name."+siteIndex, publishData.siteName);
+
+ FixupUsernamePasswordInPublishData(publishData);
+
+ var prefPrefix = "site_data." + publishData.siteName + "."
+
+ SetPublishStringPref(publishPrefsBranch, prefPrefix+"url", publishData.publishUrl);
+ SetPublishStringPref(publishPrefsBranch, prefPrefix+"browse_url", publishData.browseUrl);
+ SetPublishStringPref(publishPrefsBranch, prefPrefix+"username", publishData.username);
+
+ try {
+ publishPrefsBranch.setBoolPref(prefPrefix+"save_password", publishData.savePassword);
+ publishPrefsBranch.setBoolPref(prefPrefix+"publish_other_files", publishData.publishOtherFiles);
+ } catch (e) {}
+
+ // Save password using PasswordManager
+ // (If publishData.savePassword = false, this clears existing password)
+ SavePassword(publishData);
+
+ SetPublishStringPref(publishPrefsBranch, prefPrefix+"doc_dir",
+ FormatDirForPublishing(publishData.docDir));
+
+ if (publishData.publishOtherFiles && publishData.otherDir)
+ SetPublishStringPref(publishPrefsBranch, prefPrefix+"other_dir",
+ FormatDirForPublishing(publishData.otherDir));
+
+ if ("saveDirs" in publishData && publishData.saveDirs)
+ {
+ if (publishData.docDir)
+ AppendNewDirToList(publishData, publishData.docDir);
+
+ if (publishData.publishOtherFiles && publishData.otherDir
+ && publishData.otherDir != publishData.docDir)
+ AppendNewDirToList(publishData, publishData.otherDir);
+ }
+
+ // Save array of subdirectories with site
+ if (publishData.dirList.length)
+ {
+ publishData.dirList.sort();
+ var dirIndex = 0;
+ for (var j = 0; j < publishData.dirList.length; j++)
+ {
+ var dir = publishData.dirList[j];
+
+ // Don't store the root dir
+ if (dir && dir != "/")
+ {
+ SetPublishStringPref(publishPrefsBranch, prefPrefix + "dir." + dirIndex, dir);
+ dirIndex++;
+ }
+ }
+ }
+
+ return true;
+}
+
+function AppendNewDirToList(publishData, newDir)
+{
+ newDir = FormatDirForPublishing(newDir);
+ if (!publishData || !newDir)
+ return;
+
+ if (!publishData.dirList)
+ {
+ publishData.dirList = [newDir];
+ return;
+ }
+
+ // Check if already in the list
+ for (var i = 0; i < publishData.dirList.length; i++)
+ {
+ // Don't add if already in the list
+ if (newDir == publishData.dirList[i])
+ return;
+ }
+ // Add to end of list
+ publishData.dirList[publishData.dirList.length] = newDir;
+}
+
+function RemovePublishSubdirectoryFromPrefs(publishData, removeDir)
+{
+ removeDir = FormatDirForPublishing(removeDir);
+ if (!publishData || !publishData.siteName || !removeDir)
+ return false;
+
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return false;
+
+ var prefPrefix = "site_data." + publishData.siteName + ".";
+
+ // Remove dir from the default dir prefs
+ if (publishData.docDir == removeDir)
+ {
+ publishData.docDir = "";
+ SetPublishStringPref(publishBranch, prefPrefix+"doc_dir", "");
+ }
+
+ if (publishData.otherDir == removeDir)
+ {
+ publishData.otherDir = "";
+ SetPublishStringPref(publishBranch, prefPrefix+"other_dir", "");
+ }
+
+ prefPrefix += "dir.";
+
+ // Delete entire subdir list
+ try {
+ publishBranch.deleteBranch(prefPrefix);
+ } catch (e) {}
+
+ // Rebuild prefs, skipping over site to remove
+ if (publishData.dirList.length)
+ {
+ var dirIndex = 0;
+ var docDirInList = false;
+ var otherDirInList = false;
+ for (var i = 0; i < publishData.dirList.length; i++)
+ {
+ var dir = publishData.dirList[i];
+ if (dir == removeDir)
+ {
+ // Remove item from the dirList array
+ publishData.dirList.splice(i, 1);
+ --i;
+ }
+ else if (dir && dir != "/") // skip empty or root dir
+ {
+ // Save to prefs
+ SetPublishStringPref(publishBranch, prefPrefix + dirIndex, dir);
+ dirIndex++;
+ }
+ }
+ }
+ SavePrefFile();
+ return true;
+}
+
+function SetDefaultSiteName(name)
+{
+ if (name)
+ {
+ var publishBranch = GetPublishPrefsBranch();
+ if (publishBranch)
+ SetPublishStringPref(publishBranch, "default_site", name);
+
+ SavePrefFile();
+ }
+}
+
+function SavePrefFile()
+{
+ try {
+ Services.prefs.savePrefFile(null);
+ }
+ catch (e) {}
+}
+
+/***************** Helper / utility methods ********************/
+
+function GetPublishPrefsBranch()
+{
+ return Services.prefs.getBranch("editor.publish.");
+}
+
+function GetSiteNameList(doSort, defaultFirst)
+{
+ var publishBranch = GetPublishPrefsBranch();
+ if (!publishBranch)
+ return null;
+
+ var siteNamePrefs;
+ try {
+ siteNamePrefs = publishBranch.getChildList("site_name.");
+ } catch (e) {}
+
+ if (!siteNamePrefs || siteNamePrefs.length == 0)
+ return null;
+
+ // Array of site names
+ var siteNameList = [];
+ var index = 0;
+ var defaultName = "";
+ if (defaultFirst)
+ {
+ defaultName = GetPublishStringPref(publishBranch, "default_site");
+ // This always sorts to top -- replace with real string below
+ siteNameList[0] = "";
+ index++;
+ }
+
+ for (var i = 0; i < siteNamePrefs.length; i++)
+ {
+ var siteName = GetPublishStringPref(publishBranch, siteNamePrefs[i]);
+ // Skip if siteName pref is empty or is default name
+ if (siteName && siteName != defaultName)
+ {
+ siteNameList[index] = siteName;
+ index++;
+ }
+ }
+
+ if (siteNameList.length && doSort)
+ siteNameList.sort();
+
+ if (defaultName)
+ {
+ siteNameList[0] = defaultName;
+ index++;
+ }
+
+ return siteNameList.length? siteNameList : null;
+}
+
+function PublishSiteNameExists(name, publishSiteData, skipSiteIndex)
+{
+ if (!name)
+ return false;
+
+ if (!publishSiteData)
+ {
+ publishSiteData = GetPublishSiteData();
+ skipSiteIndex = -1;
+ }
+
+ if (!publishSiteData)
+ return false;
+
+ // Array of site names - sorted, but don't put default name first
+ for (var i = 0; i < publishSiteData.length; i++)
+ {
+ if (i != skipSiteIndex && name == publishSiteData[i].siteName)
+ return true;
+ }
+ return false;
+}
+
+// Find index of a site record in supplied publish site database
+// docUrl: Document URL with or without filename
+// (Must end in "/" if no filename)
+// dirObj.value = the directory of the document URL
+// relative to the base publishing URL, using "" if none
+//
+// XXX: Currently finds the site with the longest-matching url;
+// should we look for the shortest instead? Or match just the host portion?
+function FindSiteIndexAndDocDir(publishSiteData, docUrl, dirObj)
+{
+ if (dirObj)
+ dirObj.value = "";
+
+ if (!publishSiteData || !docUrl || GetScheme(docUrl) == "file")
+ return -1;
+
+ var siteIndex = -1;
+ var siteUrlLen = 0;
+
+ for (var i = 0; i < publishSiteData.length; i++)
+ {
+ // Site publish or browse url needs to be contained in document URL,
+ // but that may also have a directory after the site base URL
+ // So we must examine all records to find the site URL that best
+ // matches the document URL: the longest-matching substring (XXX is this right?)
+ var lenObj = {value:0};
+ var tempData = Clone(publishSiteData[i]);
+
+ // Check if this site matches docUrl (returns length of match if found)
+ var len = FillInMatchingPublishData(tempData, docUrl);
+
+ if (len > siteUrlLen)
+ {
+ siteIndex = i;
+ siteUrlLen = len;
+ if (dirObj)
+ dirObj.value = tempData.docDir;
+
+ // Continue to find the site with longest-matching publishUrl
+ }
+ }
+ return siteIndex;
+}
+
+// Look for a matching publish url within the document url
+// (We need to look at both "publishUrl" and "browseUrl" in case we are editing
+// an http: document but using ftp: to publish.)
+// If match is found:
+// Fill in the filename and subdirectory based on the docUrl and
+// return the length of the docUrl with username+password stripped out
+function FillInMatchingPublishData(publishData, docUrl)
+{
+ if (!publishData || !docUrl)
+ return 0;
+
+ // Separate docUrl into the base url and filename
+ var lastSlash = docUrl.lastIndexOf("\/");
+ var baseUrl = docUrl.slice(0, lastSlash+1);
+ var filename = docUrl.slice(lastSlash+1);
+
+ // Strip username+password from docUrl because these
+ // are stored separately in publishData, never embedded in the publishUrl
+ // If both docUrl and publishData contain usernames,
+ // we must match that as well as the url
+ var username = {value:""};
+ baseUrl = StripUsernamePassword(baseUrl, username);
+ username = username.value;
+
+ var matchedLength = 0;
+ let pubUrlFound = publishData.publishUrl && baseUrl.startsWith(publishData.publishUrl);
+ let browseUrlFound = publishData.browseUrl && baseUrl.startsWith(publishData.browseUrl);
+
+ if ((pubUrlFound || browseUrlFound)
+ && (!username || !publishData.username || username == publishData.username))
+ {
+ // We found a match
+ matchedLength = pubUrlFound ? publishData.publishUrl.length
+ : publishData.browseUrl.length;
+
+ if (matchedLength > 0)
+ {
+ publishData.filename = filename;
+
+ // Subdirectory within the site is what's left in baseUrl after the matched portion
+ publishData.docDir = FormatDirForPublishing(baseUrl.slice(matchedLength));
+ }
+ }
+ return matchedLength;
+}
+
+// Prefs that don't exist will through an exception,
+// so just return an empty string
+function GetPublishStringPref(prefBranch, name)
+{
+ if (prefBranch && name)
+ {
+ try {
+ return prefBranch.getStringPref(name);
+ } catch (e) {}
+ }
+ return "";
+}
+
+function SetPublishStringPref(prefBranch, name, value)
+{
+ if (prefBranch && name)
+ {
+ try {
+ prefBranch.setStringPref(name, value);
+ } catch (e) {}
+ }
+}
+
+// Assure that a publishing URL ends in "/", "=", "&" or "?"
+// Username and password should always be extracted as separate fields
+// and are not allowed to remain embedded in publishing URL
+function FormatUrlForPublishing(url)
+{
+ url = TrimString(StripUsernamePassword(url));
+ if (url)
+ {
+ var lastChar = url.charAt(url.length-1);
+ if (lastChar != "/" && lastChar != "=" && lastChar != "&" && lastChar != "?")
+ return (url + "/");
+ }
+ return url;
+}
+
+// Username and password present in publish url are
+// extracted into the separate "username" and "password" fields
+// of the publishData object
+// Returns true if we did change the publishData
+function FixupUsernamePasswordInPublishData(publishData)
+{
+ var ret = false;
+ if (publishData && publishData.publishUrl)
+ {
+ var userObj = {value:""};
+ var passObj = {value:""};
+ publishData.publishUrl = FormatUrlForPublishing(StripUsernamePassword(publishData.publishUrl, userObj, passObj));
+ if (userObj.value)
+ {
+ publishData.username = userObj.value;
+ ret = true;
+ }
+ if (passObj.value)
+ {
+ publishData.password = passObj.value;
+ ret = true;
+ }
+ // While we're at it, be sure browse URL is proper format
+ publishData.browseUrl = FormatUrlForPublishing(publishData.browseUrl);
+ }
+ return ret;
+}
+
+// Assure that a publishing directory ends with "/" and does not begin with "/"
+// Input dir is assumed to be a subdirectory string, not a full URL or pathname
+function FormatDirForPublishing(dir)
+{
+ dir = TrimString(dir);
+
+ // The "//" case is an expected "typo" filter
+ // that simplifies code below!
+ if (!dir || dir == "/" || dir == "//")
+ return "";
+
+ // Remove leading "/"
+ if (dir.startsWith("/"))
+ dir = dir.slice(1);
+
+ // Append "/" at the end if necessary
+ var dirLen = dir.length;
+ var lastChar = dir.charAt(dirLen-1);
+ if (dirLen > 1 && ["/", "=", "&", "?"].indexOf(lastChar) == -1)
+ dir += "/";
+
+ return dir;
+}
+
+function GetSavedPassword(publishData)
+{
+ if (!publishData || !publishData.publishUrl)
+ return "";
+
+ let url = GetUrlForPasswordManager(publishData);
+ let logins = Services.logins.findLogins(url, null, url);
+
+ for (let i = 0; i < logins.length; i++) {
+ if (logins[i].username == publishData.username)
+ return logins[i].password;
+ }
+
+ return "";
+}
+
+function SavePassword(publishData)
+{
+ if (!publishData || !publishData.publishUrl || !publishData.username)
+ return false;
+
+ let url = GetUrlForPasswordManager(publishData);
+
+ // Remove existing entry by finding all logins that match.
+ let logins = Services.logins.findLogins(url, null, url);
+
+ for (let i = 0; i < logins.length; i++) {
+ if (logins[i].username == publishData.username) {
+ Services.logins.removeLogin(logins[i]);
+ break;
+ }
+ }
+
+ // If SavePassword is true, add new password.
+ if (publishData.savePassword)
+ {
+ let authInfo = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ authInfo.init(url, null, url, publishData.username, publishData.password,
+ "", "");
+ Services.logins.addLogin(authInfo);
+ }
+
+ return true;
+}
+
+function GetUrlForPasswordManager(publishData)
+{
+ if (!publishData || !publishData.publishUrl)
+ return false;
+
+ let url = Services.io.newURI(publishData.publishUrl);
+
+ if (url.scheme == "ftp" && publishData.username)
+ // Include username in the URL so we can handle multiple users per server
+ // in the password manager
+ url = url.scheme + "://" + publishData.username + "@" + url.hostPort;
+ else
+ url = url.scheme + "://" + url.hostPort;
+
+ return url;
+}
diff --git a/comm/suite/editor/base/jar.mn b/comm/suite/editor/base/jar.mn
new file mode 100644
index 0000000000..35ed2406b2
--- /dev/null
+++ b/comm/suite/editor/base/jar.mn
@@ -0,0 +1,124 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+% content editor %content/editor/
+% overlay chrome://communicator/content/tasksOverlay.xhtml chrome://editor/content/editorTasksOverlay.xhtml
+ content/editor/ComposerCommands.js (content/ComposerCommands.js)
+ content/editor/composerOverlay.xhtml (content/composerOverlay.xhtml)
+ content/editor/editingOverlay.js (content/editingOverlay.js)
+ content/editor/editingOverlay.xhtml (content/editingOverlay.xhtml)
+ content/editor/editor.js (content/editor.js)
+ content/editor/editor.xhtml (content/editor.xhtml)
+ content/editor/EditorAllTags.css (content/EditorAllTags.css)
+ content/editor/editorApplicationOverlay.js (content/editorApplicationOverlay.js)
+ content/editor/EditorContent.css (content/EditorContent.css)
+ content/editor/EditorContextMenu.js (content/EditorContextMenu.js)
+ content/editor/EditorContextMenuOverlay.xhtml (content/EditorContextMenuOverlay.xhtml)
+ content/editor/editorOverlay.xhtml (content/editorOverlay.xhtml)
+ content/editor/editorTasksOverlay.xhtml (content/editorTasksOverlay.xhtml)
+ content/editor/editorUtilities.js (content/editorUtilities.js)
+ content/editor/publishprefs.js (content/publishprefs.js)
+ content/editor/StructBarContextMenu.js (content/StructBarContextMenu.js)
+ content/editor/images/bringtofront.png (content/images/bringtofront.png)
+ content/editor/images/bringtofront-disabled.png (content/images/bringtofront-disabled.png)
+ content/editor/images/sendtoback.png (content/images/sendtoback.png)
+ content/editor/images/sendtoback-disabled.png (content/images/sendtoback-disabled.png)
+ content/editor/images/tag-a.png (content/images/tag-a.png)
+ content/editor/images/tag-abr.png (content/images/tag-abr.png)
+ content/editor/images/tag-acr.png (content/images/tag-acr.png)
+ content/editor/images/tag-adr.png (content/images/tag-adr.png)
+ content/editor/images/tag-anchor.png (content/images/tag-anchor.png)
+ content/editor/images/tag-app.png (content/images/tag-app.png)
+ content/editor/images/tag-ara.png (content/images/tag-ara.png)
+ content/editor/images/tag-b.png (content/images/tag-b.png)
+ content/editor/images/tag-bas.png (content/images/tag-bas.png)
+ content/editor/images/tag-bdo.png (content/images/tag-bdo.png)
+ content/editor/images/tag-big.png (content/images/tag-big.png)
+ content/editor/images/tag-blq.png (content/images/tag-blq.png)
+ content/editor/images/tag-body.png (content/images/tag-body.png)
+ content/editor/images/tag-br.png (content/images/tag-br.png)
+ content/editor/images/tag-bsf.png (content/images/tag-bsf.png)
+ content/editor/images/tag-btn.png (content/images/tag-btn.png)
+ content/editor/images/tag-cit.png (content/images/tag-cit.png)
+ content/editor/images/tag-clg.png (content/images/tag-clg.png)
+ content/editor/images/tag-cod.png (content/images/tag-cod.png)
+ content/editor/images/tag-col.png (content/images/tag-col.png)
+ content/editor/images/tag-cpt.png (content/images/tag-cpt.png)
+ content/editor/images/tag-ctr.png (content/images/tag-ctr.png)
+ content/editor/images/tag-dd.png (content/images/tag-dd.png)
+ content/editor/images/tag-del.png (content/images/tag-del.png)
+ content/editor/images/tag-dfn.png (content/images/tag-dfn.png)
+ content/editor/images/tag-dir.png (content/images/tag-dir.png)
+ content/editor/images/tag-div.png (content/images/tag-div.png)
+ content/editor/images/tag-dl.png (content/images/tag-dl.png)
+ content/editor/images/tag-dt.png (content/images/tag-dt.png)
+ content/editor/images/tag-em.png (content/images/tag-em.png)
+ content/editor/images/tag-fld.png (content/images/tag-fld.png)
+ content/editor/images/tag-fnt.png (content/images/tag-fnt.png)
+ content/editor/images/tag-for.png (content/images/tag-for.png)
+ content/editor/images/tag-frm.png (content/images/tag-frm.png)
+ content/editor/images/tag-fst.png (content/images/tag-fst.png)
+ content/editor/images/tag-h1.png (content/images/tag-h1.png)
+ content/editor/images/tag-h2.png (content/images/tag-h2.png)
+ content/editor/images/tag-h3.png (content/images/tag-h3.png)
+ content/editor/images/tag-h4.png (content/images/tag-h4.png)
+ content/editor/images/tag-h5.png (content/images/tag-h5.png)
+ content/editor/images/tag-h6.png (content/images/tag-h6.png)
+ content/editor/images/tag-hed.png (content/images/tag-hed.png)
+ content/editor/images/tag-hr.png (content/images/tag-hr.png)
+ content/editor/images/tag-html.png (content/images/tag-html.png)
+ content/editor/images/tag-i.png (content/images/tag-i.png)
+ content/editor/images/tag-ifr.png (content/images/tag-ifr.png)
+ content/editor/images/tag-img.png (content/images/tag-img.png)
+ content/editor/images/tag-inp.png (content/images/tag-inp.png)
+ content/editor/images/tag-ins.png (content/images/tag-ins.png)
+ content/editor/images/tag-isx.png (content/images/tag-isx.png)
+ content/editor/images/tag-kbd.png (content/images/tag-kbd.png)
+ content/editor/images/tag-lbl.png (content/images/tag-lbl.png)
+ content/editor/images/tag-lgn.png (content/images/tag-lgn.png)
+ content/editor/images/tag-li.png (content/images/tag-li.png)
+ content/editor/images/tag-lnk.png (content/images/tag-lnk.png)
+ content/editor/images/tag-lst.png (content/images/tag-lst.png)
+ content/editor/images/tag-map.png (content/images/tag-map.png)
+ content/editor/images/tag-men.png (content/images/tag-men.png)
+ content/editor/images/tag-met.png (content/images/tag-met.png)
+ content/editor/images/tag-nbr.png (content/images/tag-nbr.png)
+ content/editor/images/tag-nfr.png (content/images/tag-nfr.png)
+ content/editor/images/tag-nsc.png (content/images/tag-nsc.png)
+ content/editor/images/tag-obj.png (content/images/tag-obj.png)
+ content/editor/images/tag-ol.png (content/images/tag-ol.png)
+ content/editor/images/tag-opg.png (content/images/tag-opg.png)
+ content/editor/images/tag-opt.png (content/images/tag-opt.png)
+ content/editor/images/tag-p.png (content/images/tag-p.png)
+ content/editor/images/tag-pln.png (content/images/tag-pln.png)
+ content/editor/images/tag-pre.png (content/images/tag-pre.png)
+ content/editor/images/tag-prm.png (content/images/tag-prm.png)
+ content/editor/images/tag-q.png (content/images/tag-q.png)
+ content/editor/images/tag-s.png (content/images/tag-s.png)
+ content/editor/images/tag-scr.png (content/images/tag-scr.png)
+ content/editor/images/tag-slc.png (content/images/tag-slc.png)
+ content/editor/images/tag-sml.png (content/images/tag-sml.png)
+ content/editor/images/tag-smp.png (content/images/tag-smp.png)
+ content/editor/images/tag-spn.png (content/images/tag-spn.png)
+ content/editor/images/tag-stk.png (content/images/tag-stk.png)
+ content/editor/images/tag-stl.png (content/images/tag-stl.png)
+ content/editor/images/tag-stn.png (content/images/tag-stn.png)
+ content/editor/images/tag-sub.png (content/images/tag-sub.png)
+ content/editor/images/tag-sup.png (content/images/tag-sup.png)
+ content/editor/images/tag-tbd.png (content/images/tag-tbd.png)
+ content/editor/images/tag-tbl.png (content/images/tag-tbl.png)
+ content/editor/images/tag-td.png (content/images/tag-td.png)
+ content/editor/images/tag-tft.png (content/images/tag-tft.png)
+ content/editor/images/tag-th.png (content/images/tag-th.png)
+ content/editor/images/tag-thd.png (content/images/tag-thd.png)
+ content/editor/images/tag-tr.png (content/images/tag-tr.png)
+ content/editor/images/tag-tt.png (content/images/tag-tt.png)
+ content/editor/images/tag-ttl.png (content/images/tag-ttl.png)
+ content/editor/images/tag-txt.png (content/images/tag-txt.png)
+ content/editor/images/tag-u.png (content/images/tag-u.png)
+ content/editor/images/tag-ul.png (content/images/tag-ul.png)
+ content/editor/images/tag-userdefined.png (content/images/tag-userdefined.png)
+ content/editor/images/tag-var.png (content/images/tag-var.png)
+ content/editor/images/tag-xmp.png (content/images/tag-xmp.png)
diff --git a/comm/suite/editor/base/moz.build b/comm/suite/editor/base/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/editor/base/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/editor/components/dialogs/content/EdAEAttributes.js b/comm/suite/editor/components/dialogs/content/EdAEAttributes.js
new file mode 100644
index 0000000000..52b7e30fac
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAEAttributes.js
@@ -0,0 +1,973 @@
+/* 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/. */
+
+// HTML Attributes object for "Name" menulist
+var gHTMLAttr = {};
+
+// JS Events Attributes object for "Name" menulist
+var gJSAttr = {};
+
+// Core HTML attribute values //
+// This is appended to Name menulist when "_core" is attribute name
+var gCoreHTMLAttr = ["^id", "class", "title"];
+
+// Core event attribute values //
+// This is appended to all JS menulists
+// except those elements having "noJSEvents"
+// as a value in their gJSAttr array.
+var gCoreJSEvents = [
+ "onclick",
+ "ondblclick",
+ "onmousedown",
+ "onmouseup",
+ "onmouseover",
+ "onmousemove",
+ "onmouseout",
+ "-",
+ "onkeypress",
+ "onkeydown",
+ "onkeyup",
+];
+
+// Following are commonly-used strings
+
+// Also accept: sRGB: #RRGGBB //
+var gHTMLColors = [
+ "Aqua",
+ "Black",
+ "Blue",
+ "Fuchsia",
+ "Gray",
+ "Green",
+ "Lime",
+ "Maroon",
+ "Navy",
+ "Olive",
+ "Purple",
+ "Red",
+ "Silver",
+ "Teal",
+ "White",
+ "Yellow",
+];
+
+var gHAlign = ["left", "center", "right"];
+
+var gHAlignJustify = ["left", "center", "right", "justify"];
+
+var gHAlignTableContent = ["left", "center", "right", "justify", "char"];
+
+var gVAlignTable = ["top", "middle", "bottom", "baseline"];
+
+var gTarget = ["_blank", "_self", "_parent", "_top"];
+
+// ================ HTML Attributes ================ //
+/* For each element, there is an array of attributes,
+ whose name is the element name,
+ used to fill the "Attribute Name" menulist.
+ For each of those attributes, if they have a specific
+ set of values, those are listed in an array named:
+ "elementName_attName".
+
+ In each values string, the following characters
+ are signal to do input filtering:
+ "#" Allow only integer values
+ "%" Allow integer values or a number ending in "%"
+ "+" Allow integer values and allow "+" or "-" as first character
+ "!" Allow only one character
+ "^" The first character can be only be A-Z, a-z, hyphen, underscore, colon or period
+ "$" is an attribute required by HTML DTD
+*/
+
+/*
+ Most elements have the "dir" attribute,
+ so we use this value array
+ for all elements instead of specifying
+ separately for each element
+*/
+gHTMLAttr.all_dir = ["ltr", "rtl"];
+
+gHTMLAttr.a = [
+ "charset",
+ "type",
+ "name",
+ "href",
+ "^hreflang",
+ "target",
+ "rel",
+ "rev",
+ "!accesskey",
+ "shape", // with imagemap //
+ "coords", // with imagemap //
+ "#tabindex",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.a_target = gTarget;
+
+gHTMLAttr.a_rel = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.a_rev = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.a_shape = ["rect", "circle", "poly", "default"];
+
+gHTMLAttr.abbr = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.acronym = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.address = ["_core", "-", "^lang", "dir"];
+
+// this is deprecated //
+gHTMLAttr.applet = [
+ "codebase",
+ "archive",
+ "code",
+ "object",
+ "alt",
+ "name",
+ "%$width",
+ "%$height",
+ "align",
+ "#hspace",
+ "#vspace",
+ "-",
+ "_core",
+];
+
+gHTMLAttr.applet_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.area = [
+ "shape",
+ "coords",
+ "href",
+ "nohref",
+ "target",
+ "$alt",
+ "#tabindex",
+ "!accesskey",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.area_target = gTarget;
+
+gHTMLAttr.area_shape = ["rect", "circle", "poly", "default"];
+
+gHTMLAttr.area_nohref = ["nohref"];
+
+gHTMLAttr.b = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.base = ["href", "target"];
+
+gHTMLAttr.base_target = gTarget;
+
+// this is deprecated //
+gHTMLAttr.basefont = ["^id", "$size", "color", "face"];
+
+gHTMLAttr.basefont_color = gHTMLColors;
+
+gHTMLAttr.bdo = ["_core", "-", "^lang", "$dir"];
+
+gHTMLAttr.bdo_dir = ["ltr", "rtl"];
+
+gHTMLAttr.big = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.blockquote = ["cite", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.body = [
+ "background",
+ "bgcolor",
+ "text",
+ "link",
+ "vlink",
+ "alink",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.body_bgcolor = gHTMLColors;
+
+gHTMLAttr.body_text = gHTMLColors;
+
+gHTMLAttr.body_link = gHTMLColors;
+
+gHTMLAttr.body_vlink = gHTMLColors;
+
+gHTMLAttr.body_alink = gHTMLColors;
+
+gHTMLAttr.br = ["clear", "-", "_core"];
+
+gHTMLAttr.br_clear = ["none", "left", "all", "right"];
+
+gHTMLAttr.button = [
+ "name",
+ "value",
+ "$type",
+ "disabled",
+ "#tabindex",
+ "!accesskey",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.button_type = ["submit", "button", "reset"];
+
+gHTMLAttr.button_disabled = ["disabled"];
+
+gHTMLAttr.caption = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.caption_align = ["top", "bottom", "left", "right"];
+
+// this is deprecated //
+gHTMLAttr.center = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.cite = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.code = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.col = [
+ "#$span",
+ "%width",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "char",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.col_span = [
+ "1", // default
+];
+
+gHTMLAttr.col_align = gHAlignTableContent;
+
+gHTMLAttr.col_valign = ["top", "middle", "bottom", "baseline"];
+
+gHTMLAttr.colgroup = [
+ "#$span",
+ "%width",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.colgroup_span = [
+ "1", // default
+];
+
+gHTMLAttr.colgroup_align = gHAlignTableContent;
+
+gHTMLAttr.colgroup_valign = ["top", "middle", "bottom", "baseline"];
+
+gHTMLAttr.dd = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.del = ["cite", "datetime", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.dfn = ["_core", "-", "^lang", "dir"];
+
+// this is deprecated //
+gHTMLAttr.dir = ["compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.dir_compact = ["compact"];
+
+gHTMLAttr.div = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.div_align = gHAlignJustify;
+
+gHTMLAttr.dl = ["compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.dl_compact = ["compact"];
+
+gHTMLAttr.dt = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.em = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.fieldset = ["_core", "-", "^lang", "dir"];
+
+// this is deprecated //
+gHTMLAttr.font = ["+size", "color", "face", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.font_color = gHTMLColors;
+
+gHTMLAttr.form = [
+ "$action",
+ "$method",
+ "enctype",
+ "accept",
+ "name",
+ "accept-charset",
+ "target",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.form_method = ["get", "post"];
+
+gHTMLAttr.form_enctype = ["application/x-www-form-urlencoded"];
+
+gHTMLAttr.form_target = gTarget;
+
+gHTMLAttr.frame = [
+ "longdesc",
+ "name",
+ "src",
+ "#frameborder",
+ "#marginwidth",
+ "#marginheight",
+ "noresize",
+ "$scrolling",
+];
+
+gHTMLAttr.frame_frameborder = ["1", "0"];
+
+gHTMLAttr.frame_noresize = ["noresize"];
+
+gHTMLAttr.frame_scrolling = ["auto", "yes", "no"];
+
+gHTMLAttr.frameset = ["rows", "cols", "-", "_core"];
+
+gHTMLAttr.h1 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h1_align = gHAlignJustify;
+
+gHTMLAttr.h2 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h2_align = gHAlignJustify;
+
+gHTMLAttr.h3 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h3_align = gHAlignJustify;
+
+gHTMLAttr.h4 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h4_align = gHAlignJustify;
+
+gHTMLAttr.h5 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h5_align = gHAlignJustify;
+
+gHTMLAttr.h6 = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.h6_align = gHAlignJustify;
+
+gHTMLAttr.head = ["profile", "-", "^lang", "dir"];
+
+gHTMLAttr.hr = [
+ "align",
+ "noshade",
+ "#size",
+ "%width",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.hr_align = gHAlign;
+
+gHTMLAttr.hr_noshade = ["noshade"];
+
+gHTMLAttr.html = ["version", "-", "^lang", "dir"];
+
+gHTMLAttr.i = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.iframe = [
+ "longdesc",
+ "name",
+ "src",
+ "$frameborder",
+ "marginwidth",
+ "marginheight",
+ "$scrolling",
+ "align",
+ "%height",
+ "%width",
+ "-",
+ "_core",
+];
+
+gHTMLAttr.iframe_frameborder = ["1", "0"];
+
+gHTMLAttr.iframe_scrolling = ["auto", "yes", "no"];
+
+gHTMLAttr.iframe_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.img = [
+ "$src",
+ "$alt",
+ "longdesc",
+ "name",
+ "%height",
+ "%width",
+ "usemap",
+ "ismap",
+ "align",
+ "#border",
+ "#hspace",
+ "#vspace",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.img_ismap = ["ismap"];
+
+gHTMLAttr.img_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.input = [
+ "$type",
+ "name",
+ "value",
+ "checked",
+ "disabled",
+ "readonly",
+ "#size",
+ "#maxlength",
+ "src",
+ "alt",
+ "usemap",
+ "ismap",
+ "#tabindex",
+ "!accesskey",
+ "accept",
+ "align",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.input_type = [
+ "text",
+ "password",
+ "checkbox",
+ "radio",
+ "submit",
+ "reset",
+ "file",
+ "hidden",
+ "image",
+ "button",
+];
+
+gHTMLAttr.input_checked = ["checked"];
+
+gHTMLAttr.input_disabled = ["disabled"];
+
+gHTMLAttr.input_readonly = ["readonly"];
+
+gHTMLAttr.input_ismap = ["ismap"];
+
+gHTMLAttr.input_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.ins = ["cite", "datetime", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.isindex = ["prompt", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.kbd = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.label = ["for", "!accesskey", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.legend = ["!accesskey", "align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.legend_align = ["top", "bottom", "left", "right"];
+
+gHTMLAttr.li = ["type", "#value", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.li_type = ["disc", "square", "circle", "-", "1", "a", "A", "i", "I"];
+
+gHTMLAttr.link = [
+ "charset",
+ "href",
+ "^hreflang",
+ "type",
+ "rel",
+ "rev",
+ "media",
+ "target",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.link_target = gTarget;
+
+gHTMLAttr.link_rel = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.link_rev = [
+ "alternate",
+ "stylesheet",
+ "start",
+ "next",
+ "prev",
+ "contents",
+ "index",
+ "glossary",
+ "copyright",
+ "chapter",
+ "section",
+ "subsection",
+ "appendix",
+ "help",
+ "bookmark",
+];
+
+gHTMLAttr.map = ["$name", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.menu = ["compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.menu_compact = ["compact"];
+
+gHTMLAttr.meta = [
+ "http-equiv",
+ "name",
+ "$content",
+ "scheme",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.noframes = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.noscript = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.object = [
+ "declare",
+ "classid",
+ "codebase",
+ "data",
+ "type",
+ "codetype",
+ "archive",
+ "standby",
+ "%height",
+ "%width",
+ "usemap",
+ "name",
+ "#tabindex",
+ "align",
+ "#border",
+ "#hspace",
+ "#vspace",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.object_declare = ["declare"];
+
+gHTMLAttr.object_align = ["top", "middle", "bottom", "left", "right"];
+
+gHTMLAttr.ol = ["type", "compact", "#start", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.ol_type = ["1", "a", "A", "i", "I"];
+
+gHTMLAttr.ol_compact = ["compact"];
+
+gHTMLAttr.optgroup = ["disabled", "$label", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.optgroup_disabled = ["disabled"];
+
+gHTMLAttr.option = [
+ "selected",
+ "disabled",
+ "label",
+ "value",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.option_selected = ["selected"];
+
+gHTMLAttr.option_disabled = ["disabled"];
+
+gHTMLAttr.p = ["align", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.p_align = gHAlignJustify;
+
+gHTMLAttr.param = ["^id", "$name", "value", "$valuetype", "type"];
+
+gHTMLAttr.param_valuetype = ["data", "ref", "object"];
+
+gHTMLAttr.pre = ["%width", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.q = ["cite", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.s = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.samp = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.script = ["charset", "$type", "language", "src", "defer"];
+
+gHTMLAttr.script_defer = ["defer"];
+
+gHTMLAttr.select = [
+ "name",
+ "#size",
+ "multiple",
+ "disabled",
+ "#tabindex",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.select_multiple = ["multiple"];
+
+gHTMLAttr.select_disabled = ["disabled"];
+
+gHTMLAttr.small = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.span = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.strike = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.strong = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.style = ["$type", "media", "title", "-", "^lang", "dir"];
+
+gHTMLAttr.sub = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.sup = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.table = [
+ "summary",
+ "%width",
+ "#border",
+ "frame",
+ "rules",
+ "#cellspacing",
+ "#cellpadding",
+ "align",
+ "bgcolor",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.table_frame = [
+ "void",
+ "above",
+ "below",
+ "hsides",
+ "lhs",
+ "rhs",
+ "vsides",
+ "box",
+ "border",
+];
+
+gHTMLAttr.table_rules = ["none", "groups", "rows", "cols", "all"];
+
+// Note; This is alignment of the table,
+// not table contents, like all other table child elements
+gHTMLAttr.table_align = gHAlign;
+
+gHTMLAttr.table_bgcolor = gHTMLColors;
+
+gHTMLAttr.tbody = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.tbody_align = gHAlignTableContent;
+
+gHTMLAttr.tbody_valign = gVAlignTable;
+
+gHTMLAttr.td = [
+ "abbr",
+ "axis",
+ "headers",
+ "scope",
+ "$#rowspan",
+ "$#colspan",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "nowrap",
+ "bgcolor",
+ "%width",
+ "%height",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.td_scope = ["row", "col", "rowgroup", "colgroup"];
+
+gHTMLAttr.td_rowspan = [
+ "1", // default
+];
+
+gHTMLAttr.td_colspan = [
+ "1", // default
+];
+
+gHTMLAttr.td_align = gHAlignTableContent;
+
+gHTMLAttr.td_valign = gVAlignTable;
+
+gHTMLAttr.td_nowrap = ["nowrap"];
+
+gHTMLAttr.td_bgcolor = gHTMLColors;
+
+gHTMLAttr.textarea = [
+ "name",
+ "$#rows",
+ "$#cols",
+ "disabled",
+ "readonly",
+ "#tabindex",
+ "!accesskey",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.textarea_disabled = ["disabled"];
+
+gHTMLAttr.textarea_readonly = ["readonly"];
+
+gHTMLAttr.tfoot = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.tfoot_align = gHAlignTableContent;
+
+gHTMLAttr.tfoot_valign = gVAlignTable;
+
+gHTMLAttr.th = [
+ "abbr",
+ "axis",
+ "headers",
+ "scope",
+ "$#rowspan",
+ "$#colspan",
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "nowrap",
+ "bgcolor",
+ "%width",
+ "%height",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.th_scope = ["row", "col", "rowgroup", "colgroup"];
+
+gHTMLAttr.th_rowspan = [
+ "1", // default
+];
+
+gHTMLAttr.th_colspan = [
+ "1", // default
+];
+
+gHTMLAttr.th_align = gHAlignTableContent;
+
+gHTMLAttr.th_valign = gVAlignTable;
+
+gHTMLAttr.th_nowrap = ["nowrap"];
+
+gHTMLAttr.th_bgcolor = gHTMLColors;
+
+gHTMLAttr.thead = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.thead_align = gHAlignTableContent;
+
+gHTMLAttr.thead_valign = gVAlignTable;
+
+gHTMLAttr.title = ["^lang", "dir"];
+
+gHTMLAttr.tr = [
+ "align",
+ "!char",
+ "#charoff",
+ "valign",
+ "bgcolor",
+ "-",
+ "_core",
+ "-",
+ "^lang",
+ "dir",
+];
+
+gHTMLAttr.tr_align = gHAlignTableContent;
+
+gHTMLAttr.tr_valign = gVAlignTable;
+
+gHTMLAttr.tr_bgcolor = gHTMLColors;
+
+gHTMLAttr.tt = ["_core", "-", "^lang", "dir"];
+
+gHTMLAttr.u = ["_core", "-", "^lang", "dir"];
+gHTMLAttr.ul = ["type", "compact", "-", "_core", "-", "^lang", "dir"];
+
+gHTMLAttr.ul_type = ["disc", "square", "circle"];
+
+gHTMLAttr.ul_compact = ["compact"];
+
+// Prefix with "_" since this is reserved (it's stripped out)
+gHTMLAttr._var = ["_core", "-", "^lang", "dir"];
+
+// ================ JS Attributes ================ //
+// These are element specific even handlers.
+/* Most all elements use gCoreJSEvents, so those
+ are assumed except for those listed here with "noEvents"
+*/
+
+gJSAttr.a = ["onfocus", "onblur"];
+
+gJSAttr.area = ["onfocus", "onblur"];
+
+gJSAttr.body = ["onload", "onupload"];
+
+gJSAttr.button = ["onfocus", "onblur"];
+
+gJSAttr.form = ["onsubmit", "onreset"];
+
+gJSAttr.frameset = ["onload", "onunload"];
+
+gJSAttr.input = ["onfocus", "onblur", "onselect", "onchange"];
+
+gJSAttr.label = ["onfocus", "onblur"];
+
+gJSAttr.select = ["onfocus", "onblur", "onchange"];
+
+gJSAttr.textarea = ["onfocus", "onblur", "onselect", "onchange"];
+
+// Elements that don't have JSEvents:
+gJSAttr.font = ["noJSEvents"];
+
+gJSAttr.applet = ["noJSEvents"];
+
+gJSAttr.isindex = ["noJSEvents"];
+
+gJSAttr.iframe = ["noJSEvents"];
diff --git a/comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js b/comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js
new file mode 100644
index 0000000000..977068bd70
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAECSSAttributes.js
@@ -0,0 +1,146 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAdvancedEdit.js */
+/* import-globals-from EdDialogCommon.js */
+
+// build attribute list in tree form from element attributes
+function BuildCSSAttributeTable() {
+ var style = gElement.style;
+ if (style == undefined) {
+ dump("Inline styles undefined\n");
+ return;
+ }
+
+ var declLength = style.length;
+
+ if (declLength == undefined || declLength == 0) {
+ if (declLength == undefined) {
+ dump("Failed to query the number of inline style declarations\n");
+ }
+
+ return;
+ }
+
+ if (declLength > 0) {
+ for (var i = 0; i < declLength; ++i) {
+ var name = style.item(i);
+ var value = style.getPropertyValue(name);
+ AddTreeItem(name, value, "CSSAList", CSSAttrs);
+ }
+ }
+
+ ClearCSSInputWidgets();
+}
+
+function onChangeCSSAttribute() {
+ var name = TrimString(gDialog.AddCSSAttributeNameInput.value);
+ if (!name) {
+ return;
+ }
+
+ var value = TrimString(gDialog.AddCSSAttributeValueInput.value);
+
+ // First try to update existing attribute
+ // If not found, add new attribute
+ if (!UpdateExistingAttribute(name, value, "CSSAList") && value) {
+ AddTreeItem(name, value, "CSSAList", CSSAttrs);
+ }
+}
+
+function ClearCSSInputWidgets() {
+ gDialog.AddCSSAttributeTree.view.selection.clearSelection();
+ gDialog.AddCSSAttributeNameInput.value = "";
+ gDialog.AddCSSAttributeValueInput.value = "";
+ SetTextboxFocus(gDialog.AddCSSAttributeNameInput);
+}
+
+function onSelectCSSTreeItem() {
+ if (!gDoOnSelectTree) {
+ return;
+ }
+
+ var tree = gDialog.AddCSSAttributeTree;
+ if (tree && tree.view.selection.count) {
+ gDialog.AddCSSAttributeNameInput.value = GetTreeItemAttributeStr(
+ getSelectedItem(tree)
+ );
+ gDialog.AddCSSAttributeValueInput.value = GetTreeItemValueStr(
+ getSelectedItem(tree)
+ );
+ }
+}
+
+function onInputCSSAttributeName() {
+ var attName = TrimString(
+ gDialog.AddCSSAttributeNameInput.value
+ ).toLowerCase();
+ var newValue = "";
+
+ var existingValue = GetAndSelectExistingAttributeValue(attName, "CSSAList");
+ if (existingValue) {
+ newValue = existingValue;
+ }
+
+ gDialog.AddCSSAttributeValueInput.value = newValue;
+}
+
+function editCSSAttributeValue(targetCell) {
+ if (IsNotTreeHeader(targetCell)) {
+ gDialog.AddCSSAttributeValueInput.inputField.select();
+ }
+}
+
+function UpdateCSSAttributes() {
+ var CSSAList = document.getElementById("CSSAList");
+ var styleString = "";
+ for (var i = 0; i < CSSAList.childNodes.length; i++) {
+ var item = CSSAList.childNodes[i];
+ var name = GetTreeItemAttributeStr(item);
+ var value = GetTreeItemValueStr(item);
+ // this code allows users to be sloppy in typing in values, and enter
+ // things like "foo: " and "bar;". This will trim off everything after the
+ // respective character.
+ if (name.includes(":")) {
+ name = name.substring(0, name.lastIndexOf(":"));
+ }
+ if (value.includes(";")) {
+ value = value.substring(0, value.lastIndexOf(";"));
+ }
+ if (i == CSSAList.childNodes.length - 1) {
+ // Last property.
+ styleString += name + ": " + value + ";";
+ } else {
+ styleString += name + ": " + value + "; ";
+ }
+ }
+ if (styleString) {
+ // Use editor transactions if modifying the element directly in the document
+ doRemoveAttribute("style");
+ doSetAttribute("style", styleString); // NOTE BUG 18894!!!
+ } else if (gElement.getAttribute("style")) {
+ doRemoveAttribute("style");
+ }
+}
+
+function RemoveCSSAttribute() {
+ // We only allow 1 selected item
+ if (gDialog.AddCSSAttributeTree.view.selection.count) {
+ // Remove the item from the tree
+ // We always rebuild complete "style" string,
+ // so no list of "removed" items
+ getSelectedItem(gDialog.AddCSSAttributeTree).remove();
+
+ ClearCSSInputWidgets();
+ }
+}
+
+function SelectCSSTree(index) {
+ gDoOnSelectTree = false;
+ try {
+ gDialog.AddCSSAttributeTree.selectedIndex = index;
+ } catch (e) {}
+ gDoOnSelectTree = true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js b/comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js
new file mode 100644
index 0000000000..1f96762754
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAEHTMLAttributes.js
@@ -0,0 +1,367 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAdvancedEdit.js */
+/* import-globals-from EdDialogCommon.js */
+
+function BuildHTMLAttributeNameList() {
+ gDialog.AddHTMLAttributeNameInput.removeAllItems();
+
+ var elementName = gElement.localName;
+ var attNames = gHTMLAttr[elementName];
+
+ if (attNames && attNames.length) {
+ var menuitem;
+
+ for (var i = 0; i < attNames.length; i++) {
+ var name = attNames[i];
+
+ if (name == "_core") {
+ // Signal to append the common 'core' attributes.
+ for (var j = 0; j < gCoreHTMLAttr.length; j++) {
+ name = gCoreHTMLAttr[j];
+
+ // only filtering rule used for core attributes as of 8-20-01
+ // Add more rules if necessary.
+ if (name.includes("^")) {
+ name = name.replace(/\^/g, "");
+ menuitem = gDialog.AddHTMLAttributeNameInput.appendItem(name, name);
+ menuitem.setAttribute("limitFirstChar", "true");
+ } else {
+ gDialog.AddHTMLAttributeNameInput.appendItem(name, name);
+ }
+ }
+ } else if (name == "-") {
+ // Signal for separator
+ var popup = gDialog.AddHTMLAttributeNameInput.menupopup;
+ if (popup) {
+ var sep = document.createXULElement("menuseparator");
+ if (sep) {
+ popup.appendChild(sep);
+ }
+ }
+ } else {
+ // Get information about value filtering
+ let forceOneChar = name.includes("!");
+ let forceInteger = name.includes("#");
+ let forceSignedInteger = name.includes("+");
+ let forceIntOrPercent = name.includes("%");
+ let limitFirstChar = name.includes("^");
+ // let required = name.includes("$");
+
+ // Strip flag characters
+ name = name.replace(/[!^#%$+]/g, "");
+
+ menuitem = gDialog.AddHTMLAttributeNameInput.appendItem(name, name);
+ if (menuitem) {
+ // Signify "required" attributes by special style
+ // TODO: Don't do this until next version, when we add
+ // explanatory text and an 'Autofill Required Attributes' button
+ // if (required)
+ // menuitem.setAttribute("class", "menuitem-highlight-1");
+
+ // Set flags to filter value input
+ if (forceOneChar) {
+ menuitem.setAttribute("forceOneChar", "true");
+ }
+ if (limitFirstChar) {
+ menuitem.setAttribute("limitFirstChar", "true");
+ }
+ if (forceInteger) {
+ menuitem.setAttribute("forceInteger", "true");
+ }
+ if (forceSignedInteger) {
+ menuitem.setAttribute("forceSignedInteger", "true");
+ }
+ if (forceIntOrPercent) {
+ menuitem.setAttribute("forceIntOrPercent", "true");
+ }
+ }
+ }
+ }
+ }
+}
+
+// build attribute list in tree form from element attributes
+function BuildHTMLAttributeTable() {
+ var nodeMap = gElement.attributes;
+ var i;
+ if (nodeMap.length > 0) {
+ var added = false;
+ for (i = 0; i < nodeMap.length; i++) {
+ let name = nodeMap[i].name.trim().toLowerCase();
+ if (
+ CheckAttributeNameSimilarity(nodeMap[i].nodeName, HTMLAttrs) ||
+ name.startsWith("on") ||
+ name == "style"
+ ) {
+ continue; // repeated or non-HTML attribute, ignore this one and go to next
+ }
+ if (
+ !name.startsWith("_moz") &&
+ AddTreeItem(name, nodeMap[i].value, "HTMLAList", HTMLAttrs)
+ ) {
+ added = true;
+ }
+ }
+
+ if (added) {
+ SelectHTMLTree(0);
+ }
+ }
+}
+
+function ClearHTMLInputWidgets() {
+ gDialog.AddHTMLAttributeTree.view.selection.clearSelection();
+ gDialog.AddHTMLAttributeNameInput.value = "";
+ gDialog.AddHTMLAttributeValueInput.value = "";
+ SetTextboxFocus(gDialog.AddHTMLAttributeNameInput);
+}
+
+function onSelectHTMLTreeItem() {
+ if (!gDoOnSelectTree) {
+ return;
+ }
+
+ var tree = gDialog.AddHTMLAttributeTree;
+ if (tree && tree.view.selection.count) {
+ var inputName = TrimString(
+ gDialog.AddHTMLAttributeNameInput.value
+ ).toLowerCase();
+ var selectedItem = getSelectedItem(tree);
+ var selectedName = selectedItem.firstChild.firstChild.getAttribute("label");
+
+ if (inputName == selectedName) {
+ // Already editing selected name - just update the value input
+ gDialog.AddHTMLAttributeValueInput.value = GetTreeItemValueStr(
+ selectedItem
+ );
+ } else {
+ gDialog.AddHTMLAttributeNameInput.value = selectedName;
+
+ // Change value input based on new selected name
+ onInputHTMLAttributeName();
+ }
+ }
+}
+
+function onInputHTMLAttributeName() {
+ let attName = gDialog.AddHTMLAttributeNameInput.value.toLowerCase().trim();
+
+ // Clear value widget, but prevent triggering update in tree
+ gUpdateTreeValue = false;
+ gDialog.AddHTMLAttributeValueInput.value = "";
+ gUpdateTreeValue = true;
+
+ if (attName) {
+ // Get value list for current attribute name
+ var valueListName;
+
+ // Most elements have the "dir" attribute,
+ // so we have just one array for the allowed values instead
+ // requiring duplicate entries for each element in EdAEAttributes.js
+ if (attName == "dir") {
+ valueListName = "all_dir";
+ } else {
+ valueListName = gElement.localName + "_" + attName;
+ }
+
+ // Strip off leading "_" we sometimes use (when element name is reserved word)
+ if (valueListName.startsWith("_")) {
+ valueListName = valueListName.slice(1);
+ }
+
+ var newValue = "";
+ var listLen = 0;
+
+ // Index to which widget we were using to edit the value
+ var deckIndex = gDialog.AddHTMLAttributeValueDeck.getAttribute(
+ "selectedIndex"
+ );
+
+ if (valueListName in gHTMLAttr) {
+ var valueList = gHTMLAttr[valueListName];
+
+ listLen = valueList.length;
+ if (listLen == 1) {
+ newValue = valueList[0];
+ }
+
+ // Note: For case where "value list" is actually just
+ // one (default) item, don't use menulist for that
+ if (listLen > 1) {
+ gDialog.AddHTMLAttributeValueMenulist.removeAllItems();
+
+ if (deckIndex != "1") {
+ // Switch to using editable menulist
+ gDialog.AddHTMLAttributeValueInput =
+ gDialog.AddHTMLAttributeValueMenulist;
+ gDialog.AddHTMLAttributeValueDeck.setAttribute("selectedIndex", "1");
+ }
+ // Rebuild the list
+ for (var i = 0; i < listLen; i++) {
+ if (valueList[i] == "-") {
+ // Signal for separator
+ var popup = gDialog.AddHTMLAttributeValueInput.menupopup;
+ if (popup) {
+ var sep = document.createXULElement("menuseparator");
+ if (sep) {
+ popup.appendChild(sep);
+ }
+ }
+ } else {
+ gDialog.AddHTMLAttributeValueMenulist.appendItem(
+ valueList[i],
+ valueList[i]
+ );
+ }
+ }
+ }
+ }
+
+ if (listLen <= 1 && deckIndex != "0") {
+ // No list: Use textbox for input instead
+ gDialog.AddHTMLAttributeValueInput = gDialog.AddHTMLAttributeValueTextbox;
+ gDialog.AddHTMLAttributeValueDeck.setAttribute("selectedIndex", "0");
+ }
+
+ // If attribute already exists in tree, use associated value,
+ // else use default found above
+ var existingValue = GetAndSelectExistingAttributeValue(
+ attName,
+ "HTMLAList"
+ );
+ if (existingValue) {
+ newValue = existingValue;
+ }
+
+ gDialog.AddHTMLAttributeValueInput.value = newValue;
+
+ if (!existingValue) {
+ onInputHTMLAttributeValue();
+ }
+ }
+}
+
+function onInputHTMLAttributeValue() {
+ if (!gUpdateTreeValue) {
+ return;
+ }
+
+ var name = TrimString(gDialog.AddHTMLAttributeNameInput.value);
+ if (!name) {
+ return;
+ }
+
+ // Trim spaces only from left since we must allow spaces within the string
+ // (we always reset the input field's value below)
+ var value = TrimStringLeft(gDialog.AddHTMLAttributeValueInput.value);
+ if (value) {
+ // Do value filtering based on type of attribute
+ // (Do not use "forceInteger()" to avoid multiple
+ // resetting of input's value and flickering)
+ var selectedItem = gDialog.AddHTMLAttributeNameInput.selectedItem;
+
+ if (selectedItem) {
+ if (
+ selectedItem.getAttribute("forceOneChar") == "true" &&
+ value.length > 1
+ ) {
+ value = value.slice(0, 1);
+ }
+
+ if (selectedItem.getAttribute("forceIntOrPercent") == "true") {
+ // Allow integer with optional "%" as last character
+ var percent = TrimStringRight(value).slice(-1);
+ value = value.replace(/\D+/g, "");
+ if (percent == "%") {
+ value += percent;
+ }
+ } else if (selectedItem.getAttribute("forceInteger") == "true") {
+ value = value.replace(/\D+/g, "");
+ } else if (selectedItem.getAttribute("forceSignedInteger") == "true") {
+ // Allow integer with optional "+" or "-" as first character
+ var sign = value[0];
+ value = value.replace(/\D+/g, "");
+ if (sign == "+" || sign == "-") {
+ value = sign + value;
+ }
+ }
+
+ // Special case attributes
+ if (selectedItem.getAttribute("limitFirstChar") == "true") {
+ // Limit first character to letter, and all others to
+ // letters, numbers, and a few others
+ value = value
+ .replace(/^[^a-zA-Z\u0080-\uFFFF]/, "")
+ .replace(/[^a-zA-Z0-9_\.\-\:\u0080-\uFFFF]+/g, "");
+ }
+
+ // Update once only if it changed
+ if (value != gDialog.AddHTMLAttributeValueInput.value) {
+ gDialog.AddHTMLAttributeValueInput.value = value;
+ }
+ }
+ }
+
+ // Update value in the tree list
+ // If not found, add new attribute
+ if (!UpdateExistingAttribute(name, value, "HTMLAList") && value) {
+ AddTreeItem(name, value, "HTMLAList", HTMLAttrs);
+ }
+}
+
+function editHTMLAttributeValue(targetCell) {
+ if (IsNotTreeHeader(targetCell)) {
+ gDialog.AddHTMLAttributeValueInput.select();
+ }
+}
+
+// update the object with added and removed attributes
+function UpdateHTMLAttributes() {
+ var HTMLAList = document.getElementById("HTMLAList");
+ var i;
+
+ // remove removed attributes
+ for (i = 0; i < HTMLRAttrs.length; i++) {
+ var name = HTMLRAttrs[i];
+
+ if (gElement.hasAttribute(name)) {
+ doRemoveAttribute(name);
+ }
+ }
+
+ // Set added or changed attributes
+ for (i = 0; i < HTMLAList.childNodes.length; i++) {
+ var item = HTMLAList.childNodes[i];
+ doSetAttribute(GetTreeItemAttributeStr(item), GetTreeItemValueStr(item));
+ }
+}
+
+function RemoveHTMLAttribute() {
+ // We only allow 1 selected item
+ if (gDialog.AddHTMLAttributeTree.view.selection.count) {
+ var item = getSelectedItem(gDialog.AddHTMLAttributeTree);
+ var attr = GetTreeItemAttributeStr(item);
+
+ // remove the item from the attribute array
+ HTMLRAttrs[HTMLRAttrs.length] = attr;
+ RemoveNameFromAttArray(attr, HTMLAttrs);
+
+ // Remove the item from the tree
+ item.remove();
+
+ // Clear inputs and selected item in tree
+ ClearHTMLInputWidgets();
+ }
+}
+
+function SelectHTMLTree(index) {
+ gDoOnSelectTree = false;
+ try {
+ gDialog.AddHTMLAttributeTree.selectedIndex = index;
+ } catch (e) {}
+ gDoOnSelectTree = true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js b/comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js
new file mode 100644
index 0000000000..c15c938b3e
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAEJSEAttributes.js
@@ -0,0 +1,200 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAdvancedEdit.js */
+/* import-globals-from EdDialogCommon.js */
+
+function BuildJSEAttributeNameList() {
+ gDialog.AddJSEAttributeNameList.removeAllItems();
+
+ // Get events specific to current element
+ var elementName = gElement.localName;
+ if (elementName in gJSAttr) {
+ var attNames = gJSAttr[elementName];
+ var i;
+ var popup;
+ var sep;
+
+ if (attNames && attNames.length) {
+ // Since we don't allow user-editable JS events yet (but we will soon)
+ // simply remove the JS tab to not allow adding JS events
+ if (attNames[0] == "noJSEvents") {
+ var tab = document.getElementById("tabJSE");
+ if (tab) {
+ tab.remove();
+ }
+
+ return;
+ }
+
+ for (i = 0; i < attNames.length; i++) {
+ gDialog.AddJSEAttributeNameList.appendItem(attNames[i], attNames[i]);
+ }
+
+ popup = gDialog.AddJSEAttributeNameList.firstChild;
+ if (popup) {
+ sep = document.createXULElement("menuseparator");
+ if (sep) {
+ popup.appendChild(sep);
+ }
+ }
+ }
+ }
+
+ // Always add core JS events unless we aborted above
+ for (i = 0; i < gCoreJSEvents.length; i++) {
+ if (gCoreJSEvents[i] == "-") {
+ if (!popup) {
+ popup = gDialog.AddJSEAttributeNameList.firstChild;
+ }
+
+ sep = document.createXULElement("menuseparator");
+
+ if (popup && sep) {
+ popup.appendChild(sep);
+ }
+ } else {
+ gDialog.AddJSEAttributeNameList.appendItem(
+ gCoreJSEvents[i],
+ gCoreJSEvents[i]
+ );
+ }
+ }
+
+ gDialog.AddJSEAttributeNameList.selectedIndex = 0;
+
+ // Use current name and value of first tree item if it exists
+ onSelectJSETreeItem();
+}
+
+// build attribute list in tree form from element attributes
+function BuildJSEAttributeTable() {
+ var nodeMap = gElement.attributes;
+ if (nodeMap.length > 0) {
+ var added = false;
+ for (var i = 0; i < nodeMap.length; i++) {
+ let name = nodeMap[i].nodeName.toLowerCase();
+ if (CheckAttributeNameSimilarity(nodeMap[i].nodeName, JSEAttrs)) {
+ // Repeated or non-JS handler, ignore this one and go to next.
+ continue;
+ }
+ if (!name.startsWith("on")) {
+ // Attribute isn't an event handler.
+ continue;
+ }
+ var value = gElement.getAttribute(nodeMap[i].nodeName);
+ if (AddTreeItem(name, value, "JSEAList", JSEAttrs)) {
+ // add item to tree
+ added = true;
+ }
+ }
+
+ // Select first item
+ if (added) {
+ gDialog.AddJSEAttributeTree.selectedIndex = 0;
+ }
+ }
+}
+
+function onSelectJSEAttribute() {
+ if (!gDoOnSelectTree) {
+ return;
+ }
+
+ gDialog.AddJSEAttributeValueInput.value = GetAndSelectExistingAttributeValue(
+ gDialog.AddJSEAttributeNameList.label,
+ "JSEAList"
+ );
+}
+
+function onSelectJSETreeItem() {
+ var tree = gDialog.AddJSEAttributeTree;
+ if (tree && tree.view.selection.count) {
+ // Select attribute name in list
+ gDialog.AddJSEAttributeNameList.value = GetTreeItemAttributeStr(
+ getSelectedItem(tree)
+ );
+
+ // Set value input to that in tree (no need to update this in the tree)
+ gUpdateTreeValue = false;
+ gDialog.AddJSEAttributeValueInput.value = GetTreeItemValueStr(
+ getSelectedItem(tree)
+ );
+ gUpdateTreeValue = true;
+ }
+}
+
+function onInputJSEAttributeValue() {
+ if (gUpdateTreeValue) {
+ var name = TrimString(gDialog.AddJSEAttributeNameList.label);
+ var value = TrimString(gDialog.AddJSEAttributeValueInput.value);
+
+ // Update value in the tree list
+ // Since we have a non-editable menulist,
+ // we MUST automatically add the event attribute if it doesn't exist
+ if (!UpdateExistingAttribute(name, value, "JSEAList") && value) {
+ AddTreeItem(name, value, "JSEAList", JSEAttrs);
+ }
+ }
+}
+
+function editJSEAttributeValue(targetCell) {
+ if (IsNotTreeHeader(targetCell)) {
+ gDialog.AddJSEAttributeValueInput.inputField.select();
+ }
+}
+
+function UpdateJSEAttributes() {
+ var JSEAList = document.getElementById("JSEAList");
+ var i;
+
+ // remove removed attributes
+ for (i = 0; i < JSERAttrs.length; i++) {
+ var name = JSERAttrs[i];
+
+ if (gElement.hasAttribute(name)) {
+ doRemoveAttribute(name);
+ }
+ }
+
+ // Add events
+ for (i = 0; i < JSEAList.childNodes.length; i++) {
+ var item = JSEAList.childNodes[i];
+
+ // set the event handler
+ doSetAttribute(GetTreeItemAttributeStr(item), GetTreeItemValueStr(item));
+ }
+}
+
+function RemoveJSEAttribute() {
+ // This differs from HTML and CSS panels:
+ // We reselect after removing, because there is not
+ // editable attribute name input, so we can't clear that
+ // like we do in other panels
+ var newIndex = gDialog.AddJSEAttributeTree.selectedIndex;
+
+ // We only allow 1 selected item
+ if (gDialog.AddJSEAttributeTree.view.selection.count) {
+ var item = getSelectedItem(gDialog.AddJSEAttributeTree);
+
+ // Name is the text of the treecell
+ var attr = GetTreeItemAttributeStr(item);
+
+ // remove the item from the attribute array
+ if (newIndex >= JSEAttrs.length - 1) {
+ newIndex--;
+ }
+
+ // remove the item from the attribute array
+ JSERAttrs[JSERAttrs.length] = attr;
+ RemoveNameFromAttArray(attr, JSEAttrs);
+
+ // Remove the item from the tree
+ item.remove();
+
+ // Reselect an item
+ gDialog.AddJSEAttributeTree.selectedIndex = newIndex;
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js
new file mode 100644
index 0000000000..60e9009905
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.js
@@ -0,0 +1,342 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdAEAttributes.js */
+/* import-globals-from EdAECSSAttributes.js */
+/* import-globals-from EdAEHTMLAttributes.js */
+/* import-globals-from EdAEJSEAttributes.js */
+/* import-globals-from EdDialogCommon.js */
+
+/** ************ GLOBALS **************/
+var gElement = null; // handle to actual element edited
+
+var HTMLAttrs = []; // html attributes
+var CSSAttrs = []; // css attributes
+var JSEAttrs = []; // js events
+
+var HTMLRAttrs = []; // removed html attributes
+var JSERAttrs = []; // removed js events
+
+/* Set false to allow changing selection in tree
+ without doing "onselect" handler actions
+*/
+var gDoOnSelectTree = true;
+var gUpdateTreeValue = true;
+
+/** ************ INITIALISATION && SETUP **************/
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+/**
+ * function : void Startup();
+ * parameters : none
+ * returns : none
+ * desc. : startup and initialisation, prepares dialog.
+ **/
+function Startup() {
+ var editor = GetCurrentEditor();
+
+ // Element to edit is passed in
+ if (!editor || !window.arguments[1]) {
+ dump("Advanced Edit: No editor or element to edit not supplied\n");
+ window.close();
+ return;
+ }
+ // This is the return value for the parent,
+ // who only needs to know if OK was clicked
+ window.opener.AdvancedEditOK = false;
+
+ // The actual element edited (not a copy!)
+ gElement = window.arguments[1];
+
+ // place the tag name in the header
+ var tagLabel = document.getElementById("tagLabel");
+ tagLabel.setAttribute("value", "<" + gElement.localName + ">");
+
+ // Create dialog object to store controls for easy access
+ gDialog.AddHTMLAttributeNameInput = document.getElementById(
+ "AddHTMLAttributeNameInput"
+ );
+
+ // We use a <deck> to switch between editable menulist and textbox
+ gDialog.AddHTMLAttributeValueDeck = document.getElementById(
+ "AddHTMLAttributeValueDeck"
+ );
+ gDialog.AddHTMLAttributeValueMenulist = document.getElementById(
+ "AddHTMLAttributeValueMenulist"
+ );
+ gDialog.AddHTMLAttributeValueTextbox = document.getElementById(
+ "AddHTMLAttributeValueTextbox"
+ );
+ gDialog.AddHTMLAttributeValueInput = gDialog.AddHTMLAttributeValueTextbox;
+
+ gDialog.AddHTMLAttributeTree = document.getElementById("HTMLATree");
+ gDialog.AddCSSAttributeNameInput = document.getElementById(
+ "AddCSSAttributeNameInput"
+ );
+ gDialog.AddCSSAttributeValueInput = document.getElementById(
+ "AddCSSAttributeValueInput"
+ );
+ gDialog.AddCSSAttributeTree = document.getElementById("CSSATree");
+ gDialog.AddJSEAttributeNameList = document.getElementById(
+ "AddJSEAttributeNameList"
+ );
+ gDialog.AddJSEAttributeValueInput = document.getElementById(
+ "AddJSEAttributeValueInput"
+ );
+ gDialog.AddJSEAttributeTree = document.getElementById("JSEATree");
+ gDialog.okButton = document.documentElement.getButton("accept");
+
+ // build the attribute trees
+ BuildHTMLAttributeTable();
+ BuildCSSAttributeTable();
+ BuildJSEAttributeTable();
+
+ // Build attribute name arrays for menulists
+ BuildJSEAttributeNameList();
+ BuildHTMLAttributeNameList();
+ // No menulists for CSS panel (yet)
+
+ // Set focus to Name editable menulist in HTML panel
+ SetTextboxFocus(gDialog.AddHTMLAttributeNameInput);
+
+ // size the dialog properly
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+/**
+ * function : bool onAccept ( void );
+ * parameters : none
+ * returns : boolean true to close the window
+ * desc. : event handler for ok button
+ **/
+function onAccept() {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+ try {
+ // Update our gElement attributes
+ UpdateHTMLAttributes();
+ UpdateCSSAttributes();
+ UpdateJSEAttributes();
+ } catch (ex) {
+ dump(ex);
+ }
+ editor.endTransaction();
+
+ window.opener.AdvancedEditOK = true;
+ SaveWindowLocation();
+}
+
+// Helpers for removing and setting attributes
+// Use editor transactions if modifying the element already in the document
+// (Temporary element from a property dialog won't have a parent node)
+function doRemoveAttribute(attrib) {
+ try {
+ var editor = GetCurrentEditor();
+ if (gElement.parentNode) {
+ editor.removeAttribute(gElement, attrib);
+ } else {
+ gElement.removeAttribute(attrib);
+ }
+ } catch (ex) {}
+}
+
+function doSetAttribute(attrib, value) {
+ try {
+ var editor = GetCurrentEditor();
+ if (gElement.parentNode) {
+ editor.setAttribute(gElement, attrib, value);
+ } else {
+ gElement.setAttribute(attrib, value);
+ }
+ } catch (ex) {}
+}
+
+/**
+ * function : bool CheckAttributeNameSimilarity ( string attName, array attArray );
+ * parameters : attribute to look for, array of current attributes
+ * returns : true if attribute already exists, false if it does not
+ * desc. : checks to see if any other attributes by the same name as the arg supplied
+ * already exist.
+ **/
+function CheckAttributeNameSimilarity(attName, attArray) {
+ for (var i = 0; i < attArray.length; i++) {
+ if (attName.toLowerCase() == attArray[i].toLowerCase()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * function : bool UpdateExistingAttribute ( string attName, string attValue, string treeChildrenId );
+ * parameters : attribute to look for, new value, ID of <treeChildren> node in XUL tree
+ * returns : true if attribute already exists in tree, false if it does not
+ * desc. : checks to see if any other attributes by the same name as the arg supplied
+ * already exist while setting the associated value if different from current value
+ **/
+function UpdateExistingAttribute(attName, attValue, treeChildrenId) {
+ var treeChildren = document.getElementById(treeChildrenId);
+ if (!treeChildren) {
+ return false;
+ }
+
+ var name;
+ var i;
+ attName = TrimString(attName).toLowerCase();
+ attValue = TrimString(attValue);
+
+ for (i = 0; i < treeChildren.childNodes.length; i++) {
+ var item = treeChildren.childNodes[i];
+ name = GetTreeItemAttributeStr(item);
+ if (name.toLowerCase() == attName) {
+ // Set the text in the "value' column treecell
+ SetTreeItemValueStr(item, attValue);
+
+ // Select item just changed,
+ // but don't trigger the tree's onSelect handler
+ gDoOnSelectTree = false;
+ try {
+ selectTreeItem(treeChildren, item);
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * function : string GetAndSelectExistingAttributeValue ( string attName, string treeChildrenId );
+ * parameters : attribute to look for, ID of <treeChildren> node in XUL tree
+ * returns : value in from the tree or empty string if name not found
+ **/
+function GetAndSelectExistingAttributeValue(attName, treeChildrenId) {
+ if (!attName) {
+ return "";
+ }
+
+ var treeChildren = document.getElementById(treeChildrenId);
+ var name;
+ var i;
+
+ for (i = 0; i < treeChildren.childNodes.length; i++) {
+ var item = treeChildren.childNodes[i];
+ name = GetTreeItemAttributeStr(item);
+ if (name.toLowerCase() == attName.toLowerCase()) {
+ // Select item in the tree
+ // but don't trigger the tree's onSelect handler
+ gDoOnSelectTree = false;
+ try {
+ selectTreeItem(treeChildren, item);
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ // Get the text in the "value' column treecell
+ return GetTreeItemValueStr(item);
+ }
+ }
+
+ // Attribute doesn't exist in tree, so remove selection
+ gDoOnSelectTree = false;
+ try {
+ treeChildren.parentNode.view.selection.clearSelection();
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ return "";
+}
+
+/* Tree structure:
+ <treeItem>
+ <treeRow>
+ <treeCell> // Name Cell
+ <treeCell // Value Cell
+*/
+function GetTreeItemAttributeStr(treeItem) {
+ if (treeItem) {
+ return TrimString(treeItem.firstChild.firstChild.getAttribute("label"));
+ }
+
+ return "";
+}
+
+function GetTreeItemValueStr(treeItem) {
+ if (treeItem) {
+ return TrimString(treeItem.firstChild.lastChild.getAttribute("label"));
+ }
+
+ return "";
+}
+
+function SetTreeItemValueStr(treeItem, value) {
+ if (treeItem && GetTreeItemValueStr(treeItem) != value) {
+ treeItem.firstChild.lastChild.setAttribute("label", value);
+ }
+}
+
+function IsNotTreeHeader(treeCell) {
+ if (treeCell) {
+ return treeCell.parentNode.parentNode.nodeName != "treehead";
+ }
+
+ return false;
+}
+
+function RemoveNameFromAttArray(attName, attArray) {
+ for (var i = 0; i < attArray.length; i++) {
+ if (attName.toLowerCase() == attArray[i].toLowerCase()) {
+ // Remove 1 array item
+ attArray.splice(i, 1);
+ break;
+ }
+ }
+}
+
+// adds a generalised treeitem.
+function AddTreeItem(name, value, treeChildrenId, attArray) {
+ attArray[attArray.length] = name;
+ var treeChildren = document.getElementById(treeChildrenId);
+ var treeitem = document.createXULElement("treeitem");
+ var treerow = document.createXULElement("treerow");
+
+ var attrCell = document.createXULElement("treecell");
+ attrCell.setAttribute("class", "propertylist");
+ attrCell.setAttribute("label", name);
+
+ var valueCell = document.createXULElement("treecell");
+ valueCell.setAttribute("class", "propertylist");
+ valueCell.setAttribute("label", value);
+
+ treerow.appendChild(attrCell);
+ treerow.appendChild(valueCell);
+ treeitem.appendChild(treerow);
+ treeChildren.appendChild(treeitem);
+
+ // Select item just added, but suppress calling the onSelect handler.
+ gDoOnSelectTree = false;
+ try {
+ selectTreeItem(treeChildren, treeitem);
+ } catch (e) {}
+ gDoOnSelectTree = true;
+
+ return treeitem;
+}
+
+function selectTreeItem(treeChildren, item) {
+ var index = treeChildren.parentNode.view.getIndexOfItem(item);
+ treeChildren.parentNode.view.selection.select(index);
+}
+
+function getSelectedItem(tree) {
+ if (tree.view.selection.count == 1) {
+ return tree.view.getItemAtIndex(tree.currentIndex);
+ }
+ return null;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml
new file mode 100644
index 0000000000..94942709a1
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdAdvancedEdit.xhtml
@@ -0,0 +1,182 @@
+<?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/. -->
+
+<!-- first checkin of the year 2000! -->
+<!-- Ben Goodger, 12:50AM, 01/00/00 NZST -->
+
+<?xml-stylesheet href="chrome://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EdAdvancedEdit.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ id="advancedEditDlg"
+ style="width: 40em;"
+ title="&WindowTitle.label;"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <!-- element page functions -->
+ <script src="chrome://editor/content/EdAEHTMLAttributes.js"/>
+ <script src="chrome://editor/content/EdAECSSAttributes.js"/>
+ <script src="chrome://editor/content/EdAEJSEAttributes.js"/>
+ <script src="chrome://editor/content/EdAEAttributes.js"/>
+
+ <!-- global dialog functions -->
+ <script src="chrome://editor/content/EdAdvancedEdit.js"/>
+
+ <script src="chrome://messenger/content/customElements.js"/>
+
+ <hbox>
+ <label value="&currentattributesfor.label;"/>
+ <label class="header" id="tagLabel"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <tabbox flex="1">
+ <tabs>
+ <tab label="&tabHTML.label;"/>
+ <tab label="&tabCSS.label;"/>
+ <tab label="&tabJSE.label;" id="tabJSE"/>
+ </tabs>
+ <tabpanels flex="1">
+ <!-- ============================================================== -->
+ <!-- HTML Attributes -->
+ <!-- ============================================================== -->
+ <vbox>
+ <tree id="HTMLATree" class="AttributesTree" flex="1"
+ hidecolumnpicker="true" seltype="single"
+ onselect="onSelectHTMLTreeItem();"
+ onclick="onSelectHTMLTreeItem();"
+ ondblclick="editHTMLAttributeValue(event.target);">
+ <treecols>
+ <treecol id="HTMLAttrCol" flex="35" label="&tree.attributeHeader.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="HTMLValCol" flex="65" label="&tree.valueHeader.label;"/>
+ </treecols>
+ <treechildren id="HTMLAList" flex="1"/>
+ </tree>
+ <hbox align="center">
+ <label value="&editAttribute.label;"/>
+ <spacer flex="1"/>
+ <button label="&removeAttribute.label;" oncommand="RemoveHTMLAttribute();"/>
+ </hbox>
+ <grid>
+ <columns>
+ <column flex="1"/><column flex="1"/>
+ </columns>
+ <rows>
+ <row equalsize="always">
+ <label control="AddHTMLAttributeNameInput" value="&AttName.label;"/>
+ <label control="AddHTMLAttributeValueInput" value="&AttValue.label;"/>
+ </row>
+ <row align="top" equalsize="always">
+ <!-- Lists are built at runtime -->
+ <menulist is="menulist-editable" id="AddHTMLAttributeNameInput"
+ editable="true" flex="1"
+ oninput="onInputHTMLAttributeName();"
+ oncommand="onInputHTMLAttributeName();"/>
+ <deck id="AddHTMLAttributeValueDeck" selectedIndex="0">
+ <hbox align="top">
+ <textbox id="AddHTMLAttributeValueTextbox" flex="1"
+ oninput="onInputHTMLAttributeValue();"/>
+ </hbox>
+ <hbox align="top">
+ <menulist is="menulist-editable" id="AddHTMLAttributeValueMenulist"
+ editable="true" flex="1"
+ oninput="onInputHTMLAttributeValue();"
+ oncommand="onInputHTMLAttributeValue();"/>
+ </hbox>
+ </deck>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ <!-- ============================================================== -->
+ <!-- CSS Attributes -->
+ <!-- ============================================================== -->
+ <vbox>
+ <tree id="CSSATree" class="AttributesTree" flex="1"
+ hidecolumnpicker="true" seltype="single"
+ onselect="onSelectCSSTreeItem();"
+ onclick="onSelectCSSTreeItem();"
+ ondblclick="editCSSAttributeValue(event.target);">
+ <treecols>
+ <treecol id="CSSPropCol" flex="35" label="&tree.propertyHeader.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="CSSValCol" flex="65" label="&tree.valueHeader.label;"/>
+ </treecols>
+ <treechildren id="CSSAList" flex="1"/>
+ </tree>
+ <hbox align="center">
+ <label value="&editAttribute.label;"/>
+ <spacer flex="1"/>
+ <button label="&removeAttribute.label;" oncommand="RemoveCSSAttribute();"/>
+ </hbox>
+ <grid>
+ <columns>
+ <column flex="1"/><column flex="1"/>
+ </columns>
+ <rows>
+ <row equalsize="always">
+ <label value="&PropertyName.label;"/>
+ <label value="&AttValue.label;"/>
+ </row>
+ <row align="top" equalsize="always">
+ <textbox id="AddCSSAttributeNameInput" flex="1"
+ oninput="onInputCSSAttributeName();"/>
+ <textbox id="AddCSSAttributeValueInput" flex="1"
+ oninput="onChangeCSSAttribute();"/>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ <!-- ============================================================== -->
+ <!-- JavaScript Event Handlers -->
+ <!-- ============================================================== -->
+ <vbox>
+ <tree id="JSEATree" class="AttributesTree" flex="1"
+ hidecolumnpicker="true" seltype="single"
+ onselect="onSelectJSETreeItem();"
+ onclick="onSelectJSETreeItem();"
+ ondblclick="editJSEAttributeValue(event.target);">
+ <treecols>
+ <treecol id="AttrCol" flex="35" label="&tree.attributeHeader.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="HeaderCol" flex="65" label="&tree.valueHeader.label;"/>
+ </treecols>
+ <treechildren id="JSEAList" flex="1"/>
+ </tree>
+ <hbox align="center">
+ <label value="&editAttribute.label;"/>
+ <spacer flex="1"/>
+ <button label="&removeAttribute.label;" oncommand="RemoveJSEAttribute()"/>
+ </hbox>
+ <grid>
+ <columns>
+ <column flex="1"/><column flex="1"/>
+ </columns>
+ <rows>
+ <row equalsize="always">
+ <label value="&AttName.label;"/>
+ <label value="&AttValue.label;"/>
+ </row>
+ <row align="top" equalsize="always">
+ <!-- List is built at runtime -->
+ <menulist id="AddJSEAttributeNameList" flex="1"
+ oncommand="onSelectJSEAttribute();"/>
+ <textbox id="AddJSEAttributeValueInput" flex="1"
+ oninput="onInputJSEAttributeValue();"/>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ </tabpanels>
+ </tabbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdButtonProps.js b/comm/suite/editor/components/dialogs/content/EdButtonProps.js
new file mode 100644
index 0000000000..1cd0ee7365
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdButtonProps.js
@@ -0,0 +1,146 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var buttonElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ buttonType: document.getElementById("ButtonType"),
+ buttonName: document.getElementById("ButtonName"),
+ buttonValue: document.getElementById("ButtonValue"),
+ buttonDisabled: document.getElementById("ButtonDisabled"),
+ buttonTabIndex: document.getElementById("ButtonTabIndex"),
+ buttonAccessKey: document.getElementById("ButtonAccessKey"),
+ MoreSection: document.getElementById("MoreSection"),
+ MoreFewerButton: document.getElementById("MoreFewerButton"),
+ RemoveButton: document.getElementById("RemoveButton"),
+ };
+
+ // Get a single selected button element
+ const kTagName = "button";
+ try {
+ buttonElement = editor.getSelectedElement(kTagName);
+ } catch (e) {}
+
+ if (buttonElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ buttonElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!buttonElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ // Hide button removing existing button
+ gDialog.RemoveButton.hidden = true;
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = buttonElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ gDialog.buttonType.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ var type = globalElement.getAttribute("type");
+ var index = 0;
+ switch (type) {
+ case "button":
+ index = 2;
+ break;
+ case "reset":
+ index = 1;
+ break;
+ }
+ gDialog.buttonType.selectedIndex = index;
+ gDialog.buttonName.value = globalElement.getAttribute("name");
+ gDialog.buttonValue.value = globalElement.getAttribute("value");
+ gDialog.buttonDisabled.setAttribute(
+ "checked",
+ globalElement.hasAttribute("disabled")
+ );
+ gDialog.buttonTabIndex.value = globalElement.getAttribute("tabindex");
+ gDialog.buttonAccessKey.value = globalElement.getAttribute("accesskey");
+}
+
+function RemoveButton() {
+ RemoveContainer(buttonElement);
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ var attributes = {
+ type: ["", "reset", "button"][gDialog.buttonType.selectedIndex],
+ name: gDialog.buttonName.value,
+ value: gDialog.buttonValue.value,
+ tabindex: gDialog.buttonTabIndex.value,
+ accesskey: gDialog.buttonAccessKey.value,
+ };
+ for (var a in attributes) {
+ if (attributes[a]) {
+ globalElement.setAttribute(a, attributes[a]);
+ } else {
+ globalElement.removeAttribute(a);
+ }
+ }
+ if (gDialog.buttonDisabled.checked) {
+ globalElement.setAttribute("disabled", "");
+ } else {
+ globalElement.removeAttribute("disabled");
+ }
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.cloneAttributes(buttonElement, globalElement);
+
+ if (insertNew) {
+ if (!InsertElementAroundSelection(buttonElement)) {
+ /* eslint-disable-next-line no-unsanitized/property */
+ buttonElement.innerHTML = editor.outputToString(
+ "text/html",
+ kOutputSelectionOnly
+ );
+ editor.insertElementAtSelection(buttonElement, true);
+ }
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml b/comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml
new file mode 100644
index 0000000000..70e4774f13
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdButtonProps.xhtml
@@ -0,0 +1,92 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edButtonProperties SYSTEM "chrome://editor/locale/EditorButtonProperties.dtd">
+%edButtonProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdButtonProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&Settings.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="ButtonType" value="&ButtonType.label;" accesskey="&ButtonType.accesskey;"/>
+ <menulist id="ButtonType">
+ <menupopup>
+ <menuitem label="&submit.value;"/>
+ <menuitem label="&reset.value;"/>
+ <menuitem label="&button.value;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <label control="ButtonName" value="&ButtonName.label;" accesskey="&ButtonName.accesskey;"/>
+ <textbox id="ButtonName"/>
+ </row>
+ <row align="center">
+ <label control="ButtonValue" value="&ButtonValue.label;" accesskey="&ButtonValue.accesskey;"/>
+ <textbox id="ButtonValue"/>
+ </row>
+ </rows>
+ </grid>
+ <hbox>
+ <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/>
+ </hbox>
+ <grid id="MoreSection"><columns><column/><column/></columns>
+ <rows>
+ <row>
+ <spacer/>
+ <checkbox id="ButtonDisabled" label="&ButtonDisabled.label;" accesskey="&ButtonDisabled.accesskey;"/>
+ </row>
+ <row align="center">
+ <label control="ButtonTabIndex" value="&tabIndex.label;" accesskey="&tabIndex.accesskey;"/>
+ <hbox>
+ <textbox id="ButtonTabIndex" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="ButtonAccessKey" value="&AccessKey.label;" accesskey="&AccessKey.accesskey;"/>
+ <hbox>
+ <textbox id="ButtonAccessKey" class="narrow"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- from EdDialogOverlay -->
+ <hbox flex="1" style="margin-top: 0.2em">
+ <button id="RemoveButton" label="&RemoveButton.label;" accesskey="&RemoveButton.accesskey;" oncommand="RemoveButton();"/>
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator class="groove"/>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdColorPicker.js b/comm/suite/editor/components/dialogs/content/EdColorPicker.js
new file mode 100644
index 0000000000..95ce279368
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorPicker.js
@@ -0,0 +1,297 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+
+var insertNew = true;
+var tagname = "TAG NAME";
+var gColor = "";
+var LastPickedColor = "";
+var ColorType = "Text";
+var TextType = false;
+var HighlightType = false;
+var TableOrCell = false;
+var LastPickedIsDefault = true;
+var NoDefault = false;
+var gColorObj;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancelColor);
+
+function Startup() {
+ if (!window.arguments[1]) {
+ dump("EdColorPicker: Missing color object param\n");
+ return;
+ }
+
+ // window.arguments[1] is object to get initial values and return color data
+ gColorObj = window.arguments[1];
+ gColorObj.Cancel = false;
+
+ gDialog.ColorPicker = document.getElementById("ColorPicker");
+ gDialog.ColorInput = document.getElementById("ColorInput");
+ gDialog.LastPickedButton = document.getElementById("LastPickedButton");
+ gDialog.LastPickedColor = document.getElementById("LastPickedColor");
+ gDialog.CellOrTableGroup = document.getElementById("CellOrTableGroup");
+ gDialog.TableRadio = document.getElementById("TableRadio");
+ gDialog.CellRadio = document.getElementById("CellRadio");
+ gDialog.ColorSwatch = document.getElementById("ColorPickerSwatch");
+ gDialog.Ok = document.documentElement.getButton("accept");
+
+ // The type of color we are setting:
+ // text: Text, Link, ActiveLink, VisitedLink,
+ // or background: Page, Table, or Cell
+ if (gColorObj.Type) {
+ ColorType = gColorObj.Type;
+ // Get string for dialog title from passed-in type
+ // (note constraint on editor.properties string name)
+ let IsCSSPrefChecked = Services.prefs.getBoolPref("editor.use_css");
+
+ if (GetCurrentEditor()) {
+ if (ColorType == "Page" && IsCSSPrefChecked && IsHTMLEditor()) {
+ document.title = GetString("BlockColor");
+ } else {
+ document.title = GetString(ColorType + "Color");
+ }
+ }
+ }
+
+ gDialog.ColorInput.value = "";
+ var tmpColor;
+ var haveTableRadio = false;
+
+ switch (ColorType) {
+ case "Page":
+ tmpColor = gColorObj.PageColor;
+ if (tmpColor && tmpColor.toLowerCase() != "window") {
+ gColor = tmpColor;
+ }
+ break;
+ case "Table":
+ if (gColorObj.TableColor) {
+ gColor = gColorObj.TableColor;
+ }
+ break;
+ case "Cell":
+ if (gColorObj.CellColor) {
+ gColor = gColorObj.CellColor;
+ }
+ break;
+ case "TableOrCell":
+ TableOrCell = true;
+ document.getElementById("TableOrCellGroup").collapsed = false;
+ haveTableRadio = true;
+ if (gColorObj.SelectedType == "Cell") {
+ gColor = gColorObj.CellColor;
+ gDialog.CellOrTableGroup.selectedItem = gDialog.CellRadio;
+ gDialog.CellRadio.focus();
+ } else {
+ gColor = gColorObj.TableColor;
+ gDialog.CellOrTableGroup.selectedItem = gDialog.TableRadio;
+ gDialog.TableRadio.focus();
+ }
+ break;
+ case "Highlight":
+ HighlightType = true;
+ if (gColorObj.HighlightColor) {
+ gColor = gColorObj.HighlightColor;
+ }
+ break;
+ default:
+ // Any other type will change some kind of text,
+ TextType = true;
+ tmpColor = gColorObj.TextColor;
+ if (tmpColor && tmpColor.toLowerCase() != "windowtext") {
+ gColor = gColorObj.TextColor;
+ }
+ break;
+ }
+
+ // Set initial color in input field and in the colorpicker
+ SetCurrentColor(gColor);
+ gDialog.ColorPicker.value = gColor;
+
+ // Use last-picked colors passed in, or those persistent on dialog
+ if (TextType) {
+ if (!("LastTextColor" in gColorObj) || !gColorObj.LastTextColor) {
+ gColorObj.LastTextColor = gDialog.LastPickedColor.getAttribute(
+ "LastTextColor"
+ );
+ }
+ LastPickedColor = gColorObj.LastTextColor;
+ } else if (HighlightType) {
+ if (!("LastHighlightColor" in gColorObj) || !gColorObj.LastHighlightColor) {
+ gColorObj.LastHighlightColor = gDialog.LastPickedColor.getAttribute(
+ "LastHighlightColor"
+ );
+ }
+ LastPickedColor = gColorObj.LastHighlightColor;
+ } else {
+ if (
+ !("LastBackgroundColor" in gColorObj) ||
+ !gColorObj.LastBackgroundColor
+ ) {
+ gColorObj.LastBackgroundColor = gDialog.LastPickedColor.getAttribute(
+ "LastBackgroundColor"
+ );
+ }
+ LastPickedColor = gColorObj.LastBackgroundColor;
+ }
+
+ // Set method to detect clicking on OK button
+ // so we don't get fooled by changing "default" behavior
+ gDialog.Ok.setAttribute("onclick", "SetDefaultToOk()");
+
+ if (!LastPickedColor) {
+ // Hide the button, as there is no last color available.
+ gDialog.LastPickedButton.hidden = true;
+ } else {
+ gDialog.LastPickedColor.setAttribute(
+ "style",
+ "background-color: " + LastPickedColor
+ );
+
+ // Make "Last-picked" the default button, until the user selects a color.
+ gDialog.Ok.removeAttribute("default");
+ gDialog.LastPickedButton.setAttribute("default", "true");
+ }
+
+ // Caller can prevent user from submitting an empty, i.e., default color
+ NoDefault = gColorObj.NoDefault;
+ if (NoDefault) {
+ // Hide the "Default button -- user must pick a color
+ document.getElementById("DefaultColorButton").collapsed = true;
+ }
+
+ // Set focus to colorpicker if not set to table radio buttons above
+ if (!haveTableRadio) {
+ gDialog.ColorPicker.focus();
+ }
+
+ SetWindowLocation();
+}
+
+function SelectColor() {
+ var color = gDialog.ColorPicker.value;
+ if (color) {
+ SetCurrentColor(color);
+ }
+}
+
+function RemoveColor() {
+ SetCurrentColor("");
+ gDialog.ColorInput.focus();
+ SetDefaultToOk();
+}
+
+function SelectColorByKeypress(aEvent) {
+ if (aEvent.charCode == aEvent.DOM_VK_SPACE) {
+ SelectColor();
+ SetDefaultToOk();
+ }
+}
+
+function SelectLastPickedColor() {
+ SetCurrentColor(LastPickedColor);
+ if (onAccept()) {
+ // window.close();
+ return true;
+ }
+
+ return false;
+}
+
+function SetCurrentColor(color) {
+ // TODO: Validate color?
+ if (!color) {
+ color = "";
+ }
+ gColor = TrimString(color).toLowerCase();
+ if (gColor == "mixed") {
+ gColor = "";
+ }
+ gDialog.ColorInput.value = gColor;
+ SetColorSwatch();
+}
+
+function SetColorSwatch() {
+ // TODO: DON'T ALLOW SPACES?
+ var color = TrimString(gDialog.ColorInput.value);
+ if (color) {
+ gDialog.ColorSwatch.setAttribute("style", "background-color:" + color);
+ gDialog.ColorSwatch.removeAttribute("default");
+ } else {
+ gDialog.ColorSwatch.setAttribute("style", "background-color:inherit");
+ gDialog.ColorSwatch.setAttribute("default", "true");
+ }
+}
+
+function SetDefaultToOk() {
+ gDialog.LastPickedButton.removeAttribute("default");
+ gDialog.Ok.setAttribute("default", "true");
+ LastPickedIsDefault = false;
+}
+
+function ValidateData() {
+ if (LastPickedIsDefault) {
+ gColor = LastPickedColor;
+ } else {
+ gColor = gDialog.ColorInput.value;
+ }
+
+ gColor = TrimString(gColor).toLowerCase();
+
+ // TODO: Validate the color string!
+
+ if (NoDefault && !gColor) {
+ ShowInputErrorMessage(GetString("NoColorError"));
+ SetTextboxFocus(gDialog.ColorInput);
+ return false;
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (!ValidateData()) {
+ event.preventDefault();
+ return;
+ }
+
+ // Set return values and save in persistent color attributes
+ if (TextType) {
+ gColorObj.TextColor = gColor;
+ if (gColor.length > 0) {
+ gDialog.LastPickedColor.setAttribute("LastTextColor", gColor);
+ gColorObj.LastTextColor = gColor;
+ }
+ } else if (HighlightType) {
+ gColorObj.HighlightColor = gColor;
+ if (gColor.length > 0) {
+ gDialog.LastPickedColor.setAttribute("LastHighlightColor", gColor);
+ gColorObj.LastHighlightColor = gColor;
+ }
+ } else {
+ gColorObj.BackgroundColor = gColor;
+ if (gColor.length > 0) {
+ gDialog.LastPickedColor.setAttribute("LastBackgroundColor", gColor);
+ gColorObj.LastBackgroundColor = gColor;
+ }
+ // If table or cell requested, tell caller which element to set on
+ if (TableOrCell && gDialog.TableRadio.selected) {
+ gColorObj.Type = "Table";
+ }
+ }
+ SaveWindowLocation();
+}
+
+function onCancelColor() {
+ // Tells caller that user canceled
+ gColorObj.Cancel = true;
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml b/comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml
new file mode 100644
index 0000000000..c18bc90e62
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorPicker.xhtml
@@ -0,0 +1,56 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EdColorPicker.dtd">
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdColorPicker.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <hbox id="TableOrCellGroup" align="center" collapsed="true">
+ <label control="CellOrTableGroup" value="&background.label;" accesskey="&background.accessKey;"/>
+ <radiogroup id="CellOrTableGroup" orient="horizontal">
+ <radio id="TableRadio" label="&table.label;" accesskey="&table.accessKey;"/>
+ <radio id="CellRadio" label="&cell.label;" accesskey="&cell.accessKey;"/>
+ </radiogroup>
+ </hbox>
+ <label value="&chooseColor1.label;"/>
+ <html:input type="color" id="ColorPicker"
+ onclick="SetDefaultToOk();"
+ ondblclick="if (onAccept()) { window.close(); }"
+ onkeypress="SelectColorByKeypress(event);"
+ onchange="SelectColor();"/>
+
+ <spacer class="spacer"/>
+ <vbox flex="1">
+ <button id="LastPickedButton" crop="right" oncommand="SelectLastPickedColor();">
+ <spacer id="LastPickedColor"
+ LastTextColor="" LastBackgroundColor=""
+ persist="LastTextColor LastBackgroundColor"/>
+ <label value="&lastPickedColor.label;" accesskey="&lastPickedColor.accessKey;" flex="1" style="text-align: center;"/>
+ </button>
+ <label value="&chooseColor2.label;" accesskey="&chooseColor2.accessKey;" control="ColorInput"/>
+ <label value="&setColorExample.label;"/>
+ <hbox align="center" flex="1=">
+ <textbox id="ColorInput" style="width: 8em" oninput="SetColorSwatch(); SetDefaultToOk();"/>
+ <spacer flex="1"/>
+ <spacer id="ColorPickerSwatch"/>
+ <spacer flex="1"/>
+ <button id="DefaultColorButton" label="&default.label;" accesskey="&default.accessKey;"
+ style="margin-right:0px;" oncommand="RemoveColor()"/>
+ </hbox>
+ </vbox>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdColorProps.js b/comm/suite/editor/components/dialogs/content/EdColorProps.js
new file mode 100644
index 0000000000..62d3f29c9a
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorProps.js
@@ -0,0 +1,476 @@
+/* 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/. */
+
+/*
+ Behavior notes:
+ Radio buttons select "UseDefaultColors" vs. "UseCustomColors" modes.
+ If any color attribute is set in the body, mode is "Custom Colors",
+ even if 1 or more (but not all) are actually null (= "use default")
+ When in "Custom Colors" mode, all colors will be set on body tag,
+ even if they are just default colors, to assure compatible colors in page.
+ User cannot select "use default" for individual colors
+*/
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+var gBodyElement;
+var prefs;
+var gBackgroundImage;
+
+// Initialize in case we can't get them from prefs???
+var defaultTextColor = "#000000";
+var defaultLinkColor = "#000099";
+var defaultActiveColor = "#000099";
+var defaultVisitedColor = "#990099";
+var defaultBackgroundColor = "#FFFFFF";
+const styleStr = "style";
+const textStr = "text";
+const linkStr = "link";
+const vlinkStr = "vlink";
+const alinkStr = "alink";
+const bgcolorStr = "bgcolor";
+const backgroundStr = "background";
+const cssColorStr = "color";
+const cssBackgroundColorStr = "background-color";
+const cssBackgroundImageStr = "background-image";
+const colorStyle = cssColorStr + ": ";
+const backColorStyle = cssBackgroundColorStr + ": ";
+const backImageStyle = "; " + cssBackgroundImageStr + ": url(";
+
+var customTextColor;
+var customLinkColor;
+var customActiveColor;
+var customVisitedColor;
+var customBackgroundColor;
+var previewBGColor;
+
+// dialog initialization code
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog.ColorPreview = document.getElementById("ColorPreview");
+ gDialog.NormalText = document.getElementById("NormalText");
+ gDialog.LinkText = document.getElementById("LinkText");
+ gDialog.ActiveLinkText = document.getElementById("ActiveLinkText");
+ gDialog.VisitedLinkText = document.getElementById("VisitedLinkText");
+ gDialog.PageColorGroup = document.getElementById("PageColorGroup");
+ gDialog.DefaultColorsRadio = document.getElementById("DefaultColorsRadio");
+ gDialog.CustomColorsRadio = document.getElementById("CustomColorsRadio");
+ gDialog.BackgroundImageInput = document.getElementById(
+ "BackgroundImageInput"
+ );
+
+ try {
+ gBodyElement = editor.rootElement;
+ } catch (e) {}
+
+ if (!gBodyElement) {
+ dump("Failed to get BODY element!\n");
+ window.close();
+ }
+
+ // Set element we will edit
+ globalElement = gBodyElement.cloneNode(false);
+
+ // Initialize default colors from browser prefs
+ var browserColors = GetDefaultBrowserColors();
+ if (browserColors) {
+ // Use author's browser pref colors passed into dialog
+ defaultTextColor = browserColors.TextColor;
+ defaultLinkColor = browserColors.LinkColor;
+ defaultActiveColor = browserColors.ActiveLinkColor;
+ defaultVisitedColor = browserColors.VisitedLinkColor;
+ defaultBackgroundColor = browserColors.BackgroundColor;
+ }
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ InitDialog();
+
+ gDialog.PageColorGroup.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ // Get image from document
+ gBackgroundImage = GetHTMLOrCSSStyleValue(
+ globalElement,
+ backgroundStr,
+ cssBackgroundImageStr
+ );
+ if (/url\((.*)\)/.test(gBackgroundImage)) {
+ gBackgroundImage = RegExp.$1;
+ }
+
+ if (gBackgroundImage) {
+ // Shorten data URIs for display.
+ shortenImageData(gBackgroundImage, gDialog.BackgroundImageInput);
+ gDialog.ColorPreview.setAttribute(
+ styleStr,
+ backImageStyle + gBackgroundImage + ");"
+ );
+ }
+
+ SetRelativeCheckbox();
+
+ customTextColor = GetHTMLOrCSSStyleValue(globalElement, textStr, cssColorStr);
+ customTextColor = ConvertRGBColorIntoHEXColor(customTextColor);
+ customLinkColor = globalElement.getAttribute(linkStr);
+ customActiveColor = globalElement.getAttribute(alinkStr);
+ customVisitedColor = globalElement.getAttribute(vlinkStr);
+ customBackgroundColor = GetHTMLOrCSSStyleValue(
+ globalElement,
+ bgcolorStr,
+ cssBackgroundColorStr
+ );
+ customBackgroundColor = ConvertRGBColorIntoHEXColor(customBackgroundColor);
+
+ var haveCustomColor =
+ customTextColor ||
+ customLinkColor ||
+ customVisitedColor ||
+ customActiveColor ||
+ customBackgroundColor;
+
+ // Set default color explicitly for any that are missing
+ // PROBLEM: We are using "windowtext" and "window" for the Windows OS
+ // default color values. This works with CSS in preview window,
+ // but we should NOT use these as values for HTML attributes!
+
+ if (!customTextColor) {
+ customTextColor = defaultTextColor;
+ }
+ if (!customLinkColor) {
+ customLinkColor = defaultLinkColor;
+ }
+ if (!customActiveColor) {
+ customActiveColor = defaultActiveColor;
+ }
+ if (!customVisitedColor) {
+ customVisitedColor = defaultVisitedColor;
+ }
+ if (!customBackgroundColor) {
+ customBackgroundColor = defaultBackgroundColor;
+ }
+
+ if (haveCustomColor) {
+ // If any colors are set, then check the "Custom" radio button
+ gDialog.PageColorGroup.selectedItem = gDialog.CustomColorsRadio;
+ UseCustomColors();
+ } else {
+ gDialog.PageColorGroup.selectedItem = gDialog.DefaultColorsRadio;
+ UseDefaultColors();
+ }
+}
+
+function GetColorAndUpdate(ColorWellID) {
+ // Only allow selecting when in custom mode
+ if (!gDialog.CustomColorsRadio.selected) {
+ return;
+ }
+
+ var colorWell = document.getElementById(ColorWellID);
+ if (!colorWell) {
+ return;
+ }
+
+ // Don't allow a blank color, i.e., using the "default"
+ var colorObj = {
+ NoDefault: true,
+ Type: "",
+ TextColor: 0,
+ PageColor: 0,
+ Cancel: false,
+ };
+
+ switch (ColorWellID) {
+ case "textCW":
+ colorObj.Type = "Text";
+ colorObj.TextColor = customTextColor;
+ break;
+ case "linkCW":
+ colorObj.Type = "Link";
+ colorObj.TextColor = customLinkColor;
+ break;
+ case "activeCW":
+ colorObj.Type = "ActiveLink";
+ colorObj.TextColor = customActiveColor;
+ break;
+ case "visitedCW":
+ colorObj.Type = "VisitedLink";
+ colorObj.TextColor = customVisitedColor;
+ break;
+ case "backgroundCW":
+ colorObj.Type = "Page";
+ colorObj.PageColor = customBackgroundColor;
+ break;
+ }
+
+ window.openDialog(
+ "chrome://editor/content/EdColorPicker.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ colorObj
+ );
+
+ // User canceled the dialog
+ if (colorObj.Cancel) {
+ return;
+ }
+
+ var color = "";
+ switch (ColorWellID) {
+ case "textCW":
+ color = customTextColor = colorObj.TextColor;
+ break;
+ case "linkCW":
+ color = customLinkColor = colorObj.TextColor;
+ break;
+ case "activeCW":
+ color = customActiveColor = colorObj.TextColor;
+ break;
+ case "visitedCW":
+ color = customVisitedColor = colorObj.TextColor;
+ break;
+ case "backgroundCW":
+ color = customBackgroundColor = colorObj.BackgroundColor;
+ break;
+ }
+
+ setColorWell(ColorWellID, color);
+ SetColorPreview(ColorWellID, color);
+}
+
+function SetColorPreview(ColorWellID, color) {
+ switch (ColorWellID) {
+ case "textCW":
+ gDialog.NormalText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "linkCW":
+ gDialog.LinkText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "activeCW":
+ gDialog.ActiveLinkText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "visitedCW":
+ gDialog.VisitedLinkText.setAttribute(styleStr, colorStyle + color);
+ break;
+ case "backgroundCW":
+ // Must combine background color and image style values
+ var styleValue = backColorStyle + color;
+ if (gBackgroundImage) {
+ styleValue += ";" + backImageStyle + gBackgroundImage + ");";
+ }
+
+ gDialog.ColorPreview.setAttribute(styleStr, styleValue);
+ previewBGColor = color;
+ break;
+ }
+}
+
+function UseCustomColors() {
+ SetElementEnabledById("TextButton", true);
+ SetElementEnabledById("LinkButton", true);
+ SetElementEnabledById("ActiveLinkButton", true);
+ SetElementEnabledById("VisitedLinkButton", true);
+ SetElementEnabledById("BackgroundButton", true);
+ SetElementEnabledById("Text", true);
+ SetElementEnabledById("Link", true);
+ SetElementEnabledById("Active", true);
+ SetElementEnabledById("Visited", true);
+ SetElementEnabledById("Background", true);
+
+ SetColorPreview("textCW", customTextColor);
+ SetColorPreview("linkCW", customLinkColor);
+ SetColorPreview("activeCW", customActiveColor);
+ SetColorPreview("visitedCW", customVisitedColor);
+ SetColorPreview("backgroundCW", customBackgroundColor);
+
+ setColorWell("textCW", customTextColor);
+ setColorWell("linkCW", customLinkColor);
+ setColorWell("activeCW", customActiveColor);
+ setColorWell("visitedCW", customVisitedColor);
+ setColorWell("backgroundCW", customBackgroundColor);
+}
+
+function UseDefaultColors() {
+ SetColorPreview("textCW", defaultTextColor);
+ SetColorPreview("linkCW", defaultLinkColor);
+ SetColorPreview("activeCW", defaultActiveColor);
+ SetColorPreview("visitedCW", defaultVisitedColor);
+ SetColorPreview("backgroundCW", defaultBackgroundColor);
+
+ // Setting to blank color will remove color from buttons,
+ setColorWell("textCW", "");
+ setColorWell("linkCW", "");
+ setColorWell("activeCW", "");
+ setColorWell("visitedCW", "");
+ setColorWell("backgroundCW", "");
+
+ // Disable color buttons and labels
+ SetElementEnabledById("TextButton", false);
+ SetElementEnabledById("LinkButton", false);
+ SetElementEnabledById("ActiveLinkButton", false);
+ SetElementEnabledById("VisitedLinkButton", false);
+ SetElementEnabledById("BackgroundButton", false);
+ SetElementEnabledById("Text", false);
+ SetElementEnabledById("Link", false);
+ SetElementEnabledById("Active", false);
+ SetElementEnabledById("Visited", false);
+ SetElementEnabledById("Background", false);
+}
+
+function chooseFile() {
+ // Get a local image file, converted into URL format
+ GetLocalFileURL("img").then(fileURL => {
+ // Always try to relativize local file URLs
+ if (gHaveDocumentUrl) {
+ fileURL = MakeRelativeUrl(fileURL);
+ }
+
+ gDialog.BackgroundImageInput.value = fileURL;
+
+ SetRelativeCheckbox();
+ ValidateAndPreviewImage(true);
+ SetTextboxFocus(gDialog.BackgroundImageInput);
+ });
+}
+
+function ChangeBackgroundImage() {
+ // Don't show error message for image while user is typing
+ ValidateAndPreviewImage(false);
+ SetRelativeCheckbox();
+}
+
+function ValidateAndPreviewImage(ShowErrorMessage) {
+ // First make a string with just background color
+ var styleValue = backColorStyle + previewBGColor + ";";
+
+ var retVal = true;
+ var image = TrimString(gDialog.BackgroundImageInput.value);
+ if (image) {
+ if (isImageDataShortened(image)) {
+ gBackgroundImage = restoredImageData(gDialog.BackgroundImageInput);
+ } else {
+ gBackgroundImage = image;
+
+ // Display must use absolute URL if possible
+ var displayImage = gHaveDocumentUrl ? MakeAbsoluteUrl(image) : image;
+ styleValue += backImageStyle + displayImage + ");";
+ }
+ } else {
+ gBackgroundImage = null;
+ }
+
+ // Set style on preview (removes image if not valid)
+ gDialog.ColorPreview.setAttribute(styleStr, styleValue);
+
+ // Note that an "empty" string is valid
+ return retVal;
+}
+
+function ValidateData() {
+ var editor = GetCurrentEditor();
+ try {
+ // Colors values are updated as they are picked, no validation necessary
+ if (gDialog.DefaultColorsRadio.selected) {
+ editor.removeAttributeOrEquivalent(globalElement, textStr, true);
+ globalElement.removeAttribute(linkStr);
+ globalElement.removeAttribute(vlinkStr);
+ globalElement.removeAttribute(alinkStr);
+ editor.removeAttributeOrEquivalent(globalElement, bgcolorStr, true);
+ } else {
+ // Do NOT accept the CSS "WindowsOS" color strings!
+ // Problem: We really should try to get the actual color values
+ // from windows, but I don't know how to do that!
+ var tmpColor = customTextColor.toLowerCase();
+ if (tmpColor != "windowtext") {
+ editor.setAttributeOrEquivalent(
+ globalElement,
+ textStr,
+ customTextColor,
+ true
+ );
+ } else {
+ editor.removeAttributeOrEquivalent(globalElement, textStr, true);
+ }
+
+ tmpColor = customBackgroundColor.toLowerCase();
+ if (tmpColor != "window") {
+ editor.setAttributeOrEquivalent(
+ globalElement,
+ bgcolorStr,
+ customBackgroundColor,
+ true
+ );
+ } else {
+ editor.removeAttributeOrEquivalent(globalElement, bgcolorStr, true);
+ }
+
+ globalElement.setAttribute(linkStr, customLinkColor);
+ globalElement.setAttribute(vlinkStr, customVisitedColor);
+ globalElement.setAttribute(alinkStr, customActiveColor);
+ }
+
+ if (ValidateAndPreviewImage(true)) {
+ // A valid image may be null for no image
+ if (gBackgroundImage) {
+ globalElement.setAttribute(backgroundStr, gBackgroundImage);
+ } else {
+ editor.removeAttributeOrEquivalent(globalElement, backgroundStr, true);
+ }
+
+ return true;
+ }
+ } catch (e) {}
+ return false;
+}
+
+function onAccept(event) {
+ // If it's a file, convert to a data URL.
+ if (gBackgroundImage && /^file:/i.test(gBackgroundImage)) {
+ let nsFile = Services.io
+ .newURI(gBackgroundImage)
+ .QueryInterface(Ci.nsIFileURL).file;
+ if (nsFile.exists()) {
+ let reader = new FileReader();
+ reader.addEventListener("load", function() {
+ gBackgroundImage = reader.result;
+ gDialog.BackgroundImageInput.value = reader.result;
+ if (onAccept(event)) {
+ window.close();
+ }
+ });
+ File.createFromNsIFile(nsFile).then(file => {
+ reader.readAsDataURL(file);
+ });
+ event.preventDefault(); // Don't close just yet...
+ return false;
+ }
+ }
+ if (ValidateData()) {
+ // Copy attributes to element we are changing
+ try {
+ GetCurrentEditor().cloneAttributes(gBodyElement, globalElement);
+ } catch (e) {}
+
+ SaveWindowLocation();
+ return true; // do close the window
+ }
+ event.preventDefault();
+ return false;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdColorProps.xhtml b/comm/suite/editor/components/dialogs/content/EdColorProps.xhtml
new file mode 100644
index 0000000000..85393ed209
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdColorProps.xhtml
@@ -0,0 +1,134 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edColorPropertiesDTD SYSTEM "chrome://editor/locale/EditorColorProperties.dtd">
+%edColorPropertiesDTD;
+<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd">
+%composeEditorOverlayDTD;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdColorProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox align="start">
+ <hbox class="groupbox-title">
+ <label class="header">&pageColors.label;</label>
+ </hbox>
+ <radiogroup id="PageColorGroup">
+ <radio id="DefaultColorsRadio" label="&defaultColorsRadio.label;" oncommand="UseDefaultColors()"
+ accesskey="&defaultColorsRadio.accessKey;"
+ tooltiptext="&defaultColorsRadio.tooltip;" />
+ <radio id="CustomColorsRadio" label="&customColorsRadio.label;" oncommand="UseCustomColors()"
+ accesskey="&customColorsRadio.accessKey;"
+ tooltiptext="&customColorsRadio.tooltip;" />
+ </radiogroup>
+ <hbox class="indent">
+ <grid>
+ <columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label id="Text" control="TextButton"
+ value="&normalText.label;&colon.character;"
+ accesskey="&normalText.accessKey;"/>
+ <button id="TextButton" class="color-button" oncommand="GetColorAndUpdate('textCW');">
+ <spacer id="textCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="Link" control="LinkButton"
+ value="&linkText.label;&colon.character;"
+ accesskey="&linkText.accessKey;"/>
+ <button id="LinkButton" class="color-button" oncommand="GetColorAndUpdate('linkCW');">
+ <spacer id="linkCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="Active" control="ActiveLinkButton"
+ value="&activeLinkText.label;&colon.character;"
+ accesskey="&activeLinkText.accessKey;"/>
+ <button id="ActiveLinkButton" class="color-button" oncommand="GetColorAndUpdate('activeCW');">
+ <spacer id="activeCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="Visited" control="VisitedLinkButton"
+ value="&visitedLinkText.label;&colon.character;"
+ accesskey="&visitedLinkText.accessKey;"/>
+ <button id="VisitedLinkButton" class="color-button" oncommand="GetColorAndUpdate('visitedCW');">
+ <spacer id="visitedCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="Background" control="BackgroundButton"
+ value="&background.label;"
+ accesskey="&background.accessKey;"/>
+ <button id="BackgroundButton" class="color-button" oncommand="GetColorAndUpdate('backgroundCW');">
+ <spacer id="backgroundCW" class="color-well"/>
+ </button>
+ </row>
+ </rows>
+ </grid>
+ <vbox id="ColorPreview" flex="1">
+ <spacer flex="1"/>
+ <label class="larger" id="NormalText" value="&normalText.label;"/>
+ <spacer flex="1"/>
+ <label class="larger" id="LinkText" value="&linkText.label;"/>
+ <spacer flex="1"/>
+ <label class="larger" id="ActiveLinkText" value="&activeLinkText.label;"/>
+ <spacer flex="1"/>
+ <label class="larger" id="VisitedLinkText" value="&visitedLinkText.label;"/>
+ <spacer flex="1"/>
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+ <spacer class="spacer"/>
+ </groupbox>
+ <spacer class="spacer"/>
+ <label control="BackgroundImageInput"
+ value="&backgroundImage.label;"
+ tooltiptext="&backgroundImage.tooltip;"
+ accesskey="&backgroundImage.accessKey;"/>
+ <tooltip id="shortenedDataURI">
+ <label value="&backgroundImage.shortenedDataURI;"/>
+ </tooltip>
+ <textbox id="BackgroundImageInput" class="uri-element" oninput="ChangeBackgroundImage()"
+ tooltiptext="&backgroundImage.tooltip;" flex="1"/>
+ <hbox align="center">
+ <checkbox id="MakeRelativeCheckbox"
+ for="BackgroundImageInput"
+ label="&makeUrlRelative.label;"
+ accesskey="&makeUrlRelative.accessKey;"
+ oncommand="MakeInputValueRelativeOrAbsolute(this);"
+ tooltiptext="&makeUrlRelative.tooltip;"/>
+ <spacer flex="1"/>
+ <button id="ChooseFile"
+ oncommand="chooseFile()"
+ label="&chooseFileButton.label;"
+ accesskey="&chooseFileButton.accessKey;"/>
+ </hbox>
+ <spacer class="smallspacer"/>
+ <hbox>
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdConvertToTable.js b/comm/suite/editor/components/dialogs/content/EdConvertToTable.js
new file mode 100644
index 0000000000..a149e708f8
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdConvertToTable.js
@@ -0,0 +1,326 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+var gIndex;
+var gCommaIndex = "0";
+var gSpaceIndex = "1";
+var gOtherIndex = "2";
+
+// dialog initialization code
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+
+ gDialog.sepRadioGroup = document.getElementById("SepRadioGroup");
+ gDialog.sepCharacterInput = document.getElementById("SepCharacterInput");
+ gDialog.deleteSepCharacter = document.getElementById("DeleteSepCharacter");
+ gDialog.collapseSpaces = document.getElementById("CollapseSpaces");
+
+ // We persist the user's separator character
+ gDialog.sepCharacterInput.value = gDialog.sepRadioGroup.getAttribute(
+ "character"
+ );
+
+ gIndex = gDialog.sepRadioGroup.getAttribute("index");
+
+ switch (gIndex) {
+ case gCommaIndex:
+ default:
+ gDialog.sepRadioGroup.selectedItem = document.getElementById("comma");
+ break;
+ case gSpaceIndex:
+ gDialog.sepRadioGroup.selectedItem = document.getElementById("space");
+ break;
+ case gOtherIndex:
+ gDialog.sepRadioGroup.selectedItem = document.getElementById("other");
+ break;
+ }
+
+ // Set initial enable state on character input and "collapse" checkbox
+ SelectCharacter(gIndex);
+
+ SetWindowLocation();
+}
+
+function InputSepCharacter() {
+ var str = gDialog.sepCharacterInput.value;
+
+ // Limit input to 1 character
+ if (str.length > 1) {
+ str = str.slice(0, 1);
+ }
+
+ // We can never allow tag or entity delimiters for separator character
+ if (str == "<" || str == ">" || str == "&" || str == ";" || str == " ") {
+ str = "";
+ }
+
+ gDialog.sepCharacterInput.value = str;
+}
+
+function SelectCharacter(radioGroupIndex) {
+ gIndex = radioGroupIndex;
+ SetElementEnabledById("SepCharacterInput", gIndex == gOtherIndex);
+ SetElementEnabledById("CollapseSpaces", gIndex == gSpaceIndex);
+}
+
+/* eslint-disable complexity */
+function onAccept() {
+ var sepCharacter = "";
+ switch (gIndex) {
+ case gCommaIndex:
+ sepCharacter = ",";
+ break;
+ case gSpaceIndex:
+ sepCharacter = " ";
+ break;
+ case gOtherIndex:
+ sepCharacter = gDialog.sepCharacterInput.value.slice(0, 1);
+ break;
+ }
+
+ var editor = GetCurrentEditor();
+ var str;
+ try {
+ str = editor.outputToString(
+ "text/html",
+ kOutputLFLineBreak | kOutputSelectionOnly
+ );
+ } catch (e) {}
+ if (!str) {
+ SaveWindowLocation();
+ return;
+ }
+
+ // Replace nbsp with spaces:
+ str = str.replace(/\u00a0/g, " ");
+
+ // Strip out </p> completely
+ str = str.replace(/\s*<\/p>\s*/g, "");
+
+ // Trim whitespace adjacent to <p> and <br> tags
+ // and replace <p> with <br>
+ // (which will be replaced with </tr> below)
+ str = str.replace(/\s*<p>\s*|\s*<br>\s*/g, "<br>");
+
+ // Trim leading <br>s
+ str = str.replace(/^(<br>)+/, "");
+
+ // Trim trailing <br>s
+ str = str.replace(/(<br>)+$/, "");
+
+ // Reduce multiple internal <br> to just 1
+ // TODO: Maybe add a checkbox to let user decide
+ // str = str.replace(/(<br>)+/g, "<br>");
+
+ // Trim leading and trailing spaces
+ str = str.trim();
+
+ // Remove all tag contents so we don't replace
+ // separator character within tags
+ // Also converts lists to something useful
+ var stack = [];
+ var start;
+ var end;
+ var searchStart = 0;
+ var listSeparator = "";
+ var listItemSeparator = "";
+ var endList = false;
+
+ do {
+ start = str.indexOf("<", searchStart);
+
+ if (start >= 0) {
+ end = str.indexOf(">", start + 1);
+ if (end > start) {
+ let tagContent = str.slice(start + 1, end).trim();
+
+ if (/^ol|^ul|^dl/.test(tagContent)) {
+ // Replace list tag with <BR> to start new row
+ // at beginning of second or greater list tag
+ str = str.slice(0, start) + listSeparator + str.slice(end + 1);
+ if (listSeparator == "") {
+ listSeparator = "<br>";
+ }
+
+ // Reset for list item separation into cells
+ listItemSeparator = "";
+ } else if (/^li|^dt|^dd/.test(tagContent)) {
+ // Start a new row if this is first item after the ending the last list
+ if (endList) {
+ listItemSeparator = "<br>";
+ }
+
+ // Start new cell at beginning of second or greater list items
+ str = str.slice(0, start) + listItemSeparator + str.slice(end + 1);
+
+ if (endList || listItemSeparator == "") {
+ listItemSeparator = sepCharacter;
+ }
+
+ endList = false;
+ } else {
+ // Find end tags
+ endList = /^\/ol|^\/ul|^\/dl/.test(tagContent);
+ if (endList || /^\/li|^\/dt|^\/dd/.test(tagContent)) {
+ // Strip out tag
+ str = str.slice(0, start) + str.slice(end + 1);
+ } else {
+ // Not a list-related tag: Store tag contents in an array
+ stack.push(tagContent);
+
+ // Keep the "<" and ">" while removing from source string
+ start++;
+ str = str.slice(0, start) + str.slice(end);
+ }
+ }
+ }
+ searchStart = start + 1;
+ }
+ } while (start >= 0);
+
+ // Replace separator characters with table cells
+ var replaceString;
+ if (gDialog.deleteSepCharacter.checked) {
+ replaceString = "";
+ } else {
+ // Don't delete separator character,
+ // so include it at start of string to replace
+ replaceString = sepCharacter;
+ }
+
+ replaceString += "<td>";
+
+ if (sepCharacter.length > 0) {
+ var tempStr = sepCharacter;
+ var regExpChars = ".!@#$%^&*-+[]{}()|\\/";
+ if (regExpChars.includes(sepCharacter)) {
+ tempStr = "\\" + sepCharacter;
+ }
+
+ if (gIndex == gSpaceIndex) {
+ // If checkbox is checked,
+ // one or more adjacent spaces are one separator
+ if (gDialog.collapseSpaces.checked) {
+ tempStr = "\\s+";
+ } else {
+ tempStr = "\\s";
+ }
+ }
+ var pattern = new RegExp(tempStr, "g");
+ str = str.replace(pattern, replaceString);
+ }
+
+ // Put back tag contents that we removed above
+ searchStart = 0;
+ var stackIndex = 0;
+ do {
+ start = str.indexOf("<", searchStart);
+ end = start + 1;
+ if (start >= 0 && str.charAt(end) == ">") {
+ // We really need a FIFO stack!
+ str = str.slice(0, end) + stack[stackIndex++] + str.slice(end);
+ }
+ searchStart = end;
+ } while (start >= 0);
+
+ // End table row and start another for each br or p
+ str = str.replace(/\s*<br>\s*/g, "</tr>\n<tr><td>");
+
+ // Add the table tags and the opening and closing tr/td tags
+ // Default table attributes should be same as those used in nsHTMLEditor::CreateElementWithDefaults()
+ // (Default width="100%" is used in EdInsertTable.js)
+ str =
+ '<table border="1" width="100%" cellpadding="2" cellspacing="2">\n<tr><td>' +
+ str +
+ "</tr>\n</table>\n";
+
+ editor.beginTransaction();
+
+ // Delete the selection -- makes it easier to find where table will insert
+ var nodeBeforeTable = null;
+ var nodeAfterTable = null;
+ try {
+ editor.deleteSelection(editor.eNone, editor.eStrip);
+
+ var anchorNodeBeforeInsert = editor.selection.anchorNode;
+ var offset = editor.selection.anchorOffset;
+ if (anchorNodeBeforeInsert.nodeType == Node.TEXT_NODE) {
+ // Text was split. Table should be right after the first or before
+ nodeBeforeTable = anchorNodeBeforeInsert.previousSibling;
+ nodeAfterTable = anchorNodeBeforeInsert;
+ } else {
+ // Table should be inserted right after node pointed to by selection
+ if (offset > 0) {
+ nodeBeforeTable = anchorNodeBeforeInsert.childNodes.item(offset - 1);
+ }
+
+ nodeAfterTable = anchorNodeBeforeInsert.childNodes.item(offset);
+ }
+
+ editor.insertHTML(str);
+ } catch (e) {}
+
+ var table = null;
+ if (nodeAfterTable) {
+ var previous = nodeAfterTable.previousSibling;
+ if (previous && previous.nodeName.toLowerCase() == "table") {
+ table = previous;
+ }
+ }
+ if (!table && nodeBeforeTable) {
+ var next = nodeBeforeTable.nextSibling;
+ if (next && next.nodeName.toLowerCase() == "table") {
+ table = next;
+ }
+ }
+
+ if (table) {
+ // Fixup table only if pref is set
+ var firstRow;
+ try {
+ if (Services.prefs.getBoolPref("editor.table.maintain_structure")) {
+ editor.normalizeTable(table);
+ }
+
+ firstRow = editor.getFirstRow(table);
+ } catch (e) {}
+
+ // Put caret in first cell
+ if (firstRow) {
+ var node2 = firstRow.firstChild;
+ do {
+ if (
+ node2.nodeName.toLowerCase() == "td" ||
+ node2.nodeName.toLowerCase() == "th"
+ ) {
+ try {
+ editor.selection.collapse(node2, 0);
+ } catch (e) {}
+ break;
+ }
+ node2 = node2.nextSibling;
+ } while (node2);
+ }
+ }
+
+ editor.endTransaction();
+
+ // Save persisted attributes
+ gDialog.sepRadioGroup.setAttribute("index", gIndex);
+ if (gIndex == gOtherIndex) {
+ gDialog.sepRadioGroup.setAttribute("character", sepCharacter);
+ }
+
+ SaveWindowLocation();
+}
+/* eslint-enable complexity */
diff --git a/comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml b/comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml
new file mode 100644
index 0000000000..d3d5c4a465
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdConvertToTable.xhtml
@@ -0,0 +1,43 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EdConvertToTable.dtd">
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload = "Startup()"
+ style="min-width:20em">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <!--- Element-specific methods -->
+ <script src="chrome://editor/content/EdConvertToTable.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+ <description class="wrap" flex="1">&instructions1.label;</description>
+ <description class="wrap" flex="1">&instructions2.label;</description>
+ <radiogroup id="SepRadioGroup" persist="index character" index="0" character="">
+ <radio id="comma" label="&commaRadio.label;" oncommand="SelectCharacter('0');"/>
+ <radio id="space" label="&spaceRadio.label;" oncommand="SelectCharacter('1');"/>
+ <hbox>
+ <spacer class="radio-spacer"/>
+ <checkbox id="CollapseSpaces" label="&collapseSpaces.label;"
+ checked="true" persist="checked"
+ tooltiptext="&collapseSpaces.tooltip;"/>
+ </hbox>
+ <hbox align="center">
+ <radio id="other" label="&otherRadio.label;" oncommand="SelectCharacter('2');"/>
+ <textbox class="narrow" id="SepCharacterInput" oninput="InputSepCharacter()"/>
+ </hbox>
+ </radiogroup>
+ <spacer class="spacer"/>
+ <checkbox id="DeleteSepCharacter" label="&deleteCharCheck.label;" persist="checked"/>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdDialogCommon.js b/comm/suite/editor/components/dialogs/content/EdDialogCommon.js
new file mode 100644
index 0000000000..c6b7c63778
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDialogCommon.js
@@ -0,0 +1,1038 @@
+/* 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/. */
+
+// Each editor window must include this file
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* globals InitDialog, ChangeLinkLocation, ValidateData */
+
+// Object to attach commonly-used widgets (all dialogs should use this)
+var gDialog = {};
+
+var gHaveDocumentUrl = false;
+var gValidationError = false;
+
+// Use for 'defaultIndex' param in InitPixelOrPercentMenulist
+const gPixel = 0;
+const gPercent = 1;
+
+const gMaxPixels = 100000; // Used for image size, borders, spacing, and padding
+// Gecko code uses 1000 for maximum rowspan, colspan
+// Also, editing performance is really bad above this
+const gMaxRows = 1000;
+const gMaxColumns = 1000;
+const gMaxTableSize = 1000000; // Width or height of table or cells
+
+// For dialogs that expand in size. Default is smaller size see "onMoreFewer()" below
+var SeeMore = false;
+
+// A XUL element with id="location" for managing
+// dialog location relative to parent window
+var gLocation;
+
+// The element being edited - so AdvancedEdit can have access to it
+var globalElement;
+
+/* Validate contents of an input field
+ *
+ * inputWidget The 'textbox' XUL element for text input of the attribute's value
+ * listWidget The 'menulist' XUL element for choosing "pixel" or "percent"
+ * May be null when no pixel/percent is used.
+ * minVal minimum allowed for input widget's value
+ * maxVal maximum allowed for input widget's value
+ * (when "listWidget" is used, maxVal is used for "pixel" maximum,
+ * 100% is assumed if "percent" is the user's choice)
+ * element The DOM element that we set the attribute on. May be null.
+ * attName Name of the attribute to set. May be null or ignored if "element" is null
+ * mustHaveValue If true, error dialog is displayed if "value" is empty string
+ *
+ * This calls "ValidateNumberRange()", which puts up an error dialog to inform the user.
+ * If error, we also:
+ * Shift focus and select contents of the inputWidget,
+ * Switch to appropriate panel of tabbed dialog if user implements "SwitchToValidate()",
+ * and/or will expand the dialog to full size if "More / Fewer" feature is implemented
+ *
+ * Returns the "value" as a string, or "" if error or input contents are empty
+ * The global "gValidationError" variable is set true if error was found
+ */
+function ValidateNumber(
+ inputWidget,
+ listWidget,
+ minVal,
+ maxVal,
+ element,
+ attName,
+ mustHaveValue,
+ mustShowMoreSection
+) {
+ if (!inputWidget) {
+ gValidationError = true;
+ return "";
+ }
+
+ // Global error return value
+ gValidationError = false;
+ var maxLimit = maxVal;
+ var isPercent = false;
+
+ var numString = TrimString(inputWidget.value);
+ if (numString || mustHaveValue) {
+ if (listWidget) {
+ isPercent = listWidget.selectedIndex == 1;
+ }
+ if (isPercent) {
+ maxLimit = 100;
+ }
+
+ // This method puts up the error message
+ numString = ValidateNumberRange(numString, minVal, maxLimit, mustHaveValue);
+ if (!numString) {
+ // Switch to appropriate panel for error reporting
+ SwitchToValidatePanel();
+
+ // or expand dialog for users of "More / Fewer" button
+ if (
+ "dialog" in window &&
+ window.dialog &&
+ "MoreSection" in gDialog &&
+ gDialog.MoreSection
+ ) {
+ if (!SeeMore) {
+ onMoreFewer();
+ }
+ }
+
+ // Error - shift to offending input widget
+ SetTextboxFocus(inputWidget);
+ gValidationError = true;
+ } else {
+ if (isPercent) {
+ numString += "%";
+ }
+ if (element) {
+ GetCurrentEditor().setAttributeOrEquivalent(
+ element,
+ attName,
+ numString,
+ true
+ );
+ }
+ }
+ } else if (element) {
+ GetCurrentEditor().removeAttributeOrEquivalent(element, attName, true);
+ }
+ return numString;
+}
+
+/* Validate contents of an input field
+ *
+ * value number to validate
+ * minVal minimum allowed for input widget's value
+ * maxVal maximum allowed for input widget's value
+ * (when "listWidget" is used, maxVal is used for "pixel" maximum,
+ * 100% is assumed if "percent" is the user's choice)
+ * mustHaveValue If true, error dialog is displayed if "value" is empty string
+ *
+ * If inputWidget's value is outside of range, or is empty when "mustHaveValue" = true,
+ * an error dialog is popuped up to inform the user. The focus is shifted
+ * to the inputWidget.
+ *
+ * Returns the "value" as a string, or "" if error or input contents are empty
+ * The global "gValidationError" variable is set true if error was found
+ */
+function ValidateNumberRange(value, minValue, maxValue, mustHaveValue) {
+ // Initialize global error flag
+ gValidationError = false;
+ value = TrimString(String(value));
+
+ // We don't show error for empty string unless caller wants to
+ if (!value && !mustHaveValue) {
+ return "";
+ }
+
+ var numberStr = "";
+
+ if (value.length > 0) {
+ // Extract just numeric characters
+ var number = Number(value.replace(/\D+/g, ""));
+ if (number >= minValue && number <= maxValue) {
+ // Return string version of the number
+ return String(number);
+ }
+ numberStr = String(number);
+ }
+
+ var message = "";
+
+ if (numberStr.length > 0) {
+ // We have a number from user outside of allowed range
+ message = GetString("ValidateRangeMsg");
+ message = message.replace(/%n%/, numberStr);
+ message += "\n ";
+ }
+ message += GetString("ValidateNumberMsg");
+
+ // Replace variable placeholders in message with number values
+ message = message.replace(/%min%/, minValue).replace(/%max%/, maxValue);
+ ShowInputErrorMessage(message);
+
+ // Return an empty string to indicate error
+ gValidationError = true;
+ return "";
+}
+
+function SetTextboxFocusById(id) {
+ SetTextboxFocus(document.getElementById(id));
+}
+
+function SetTextboxFocus(textbox) {
+ if (textbox) {
+ // XXX Using the setTimeout is hacky workaround for bug 103197
+ // Must create a new function to keep "textbox" in scope
+ setTimeout(
+ function(textbox) {
+ textbox.focus();
+ textbox.select();
+ },
+ 0,
+ textbox
+ );
+ }
+}
+
+function ShowInputErrorMessage(message) {
+ Services.prompt.alert(window, GetString("InputError"), message);
+ window.focus();
+}
+
+// Get the text appropriate to parent container
+// to determine what a "%" value is referring to.
+// elementForAtt is element we are actually setting attributes on
+// (a temporary copy of element in the doc to allow canceling),
+// but elementInDoc is needed to find parent context in document
+function GetAppropriatePercentString(elementForAtt, elementInDoc) {
+ var editor = GetCurrentEditor();
+ try {
+ var name = elementForAtt.nodeName.toLowerCase();
+ if (name == "td" || name == "th") {
+ return GetString("PercentOfTable");
+ }
+
+ // Check if element is within a table cell
+ if (editor.getElementOrParentByTagName("td", elementInDoc)) {
+ return GetString("PercentOfCell");
+ }
+ return GetString("PercentOfWindow");
+ } catch (e) {
+ return "";
+ }
+}
+
+function ClearListbox(listbox) {
+ if (listbox) {
+ listbox.clearSelection();
+ while (listbox.hasChildNodes()) {
+ listbox.lastChild.remove();
+ }
+ }
+}
+
+function forceInteger(elementID) {
+ var editField = document.getElementById(elementID);
+ if (!editField) {
+ return;
+ }
+
+ var stringIn = editField.value;
+ if (stringIn && stringIn.length > 0) {
+ // Strip out all nonnumeric characters
+ stringIn = stringIn.replace(/\D+/g, "");
+ if (!stringIn) {
+ stringIn = "";
+ }
+
+ // Write back only if changed
+ if (stringIn != editField.value) {
+ editField.value = stringIn;
+ }
+ }
+}
+
+function InitPixelOrPercentMenulist(
+ elementForAtt,
+ elementInDoc,
+ attribute,
+ menulistID,
+ defaultIndex
+) {
+ if (!defaultIndex) {
+ defaultIndex = gPixel;
+ }
+
+ // var size = elementForAtt.getAttribute(attribute);
+ var size = GetHTMLOrCSSStyleValue(elementForAtt, attribute, attribute);
+ var menulist = document.getElementById(menulistID);
+ var pixelItem;
+ var percentItem;
+
+ if (!menulist) {
+ dump("NO MENULIST found for ID=" + menulistID + "\n");
+ return size;
+ }
+
+ menulist.removeAllItems();
+ pixelItem = menulist.appendItem(GetString("Pixels"));
+
+ if (!pixelItem) {
+ return 0;
+ }
+
+ percentItem = menulist.appendItem(
+ GetAppropriatePercentString(elementForAtt, elementInDoc)
+ );
+ if (size && size.length > 0) {
+ // Search for a "%" or "px"
+ if (size.includes("%")) {
+ // Strip out the %
+ size = size.substr(0, size.indexOf("%"));
+ if (percentItem) {
+ menulist.selectedItem = percentItem;
+ }
+ } else {
+ if (size.includes("px")) {
+ // Strip out the px
+ size = size.substr(0, size.indexOf("px"));
+ }
+ menulist.selectedItem = pixelItem;
+ }
+ } else {
+ menulist.selectedIndex = defaultIndex;
+ }
+
+ return size;
+}
+
+function onAdvancedEdit() {
+ // First validate data from widgets in the "simpler" property dialog
+ if (ValidateData()) {
+ // Set true if OK is clicked in the Advanced Edit dialog
+ window.AdvancedEditOK = false;
+ // Open the AdvancedEdit dialog, passing in the element to be edited
+ // (the copy named "globalElement")
+ window.openDialog(
+ "chrome://editor/content/EdAdvancedEdit.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable=yes",
+ "",
+ globalElement
+ );
+ window.focus();
+ if (window.AdvancedEditOK) {
+ // Copy edited attributes to the dialog widgets:
+ InitDialog();
+ }
+ }
+}
+
+function getColor(ColorPickerID) {
+ var colorPicker = document.getElementById(ColorPickerID);
+ var color;
+ if (colorPicker) {
+ // Extract color from colorPicker and assign to colorWell.
+ color = colorPicker.getAttribute("color");
+ if (color && color == "") {
+ return null;
+ }
+ // Clear color so next if it's called again before
+ // color picker is actually used, we dedect the "don't set color" state
+ colorPicker.setAttribute("color", "");
+ }
+
+ return color;
+}
+
+function setColorWell(ColorWellID, color) {
+ var colorWell = document.getElementById(ColorWellID);
+ if (colorWell) {
+ if (!color || color == "") {
+ // Don't set color (use default)
+ // Trigger change to not show color swatch
+ colorWell.setAttribute("default", "true");
+ // Style in CSS sets "background-color",
+ // but color won't clear unless we do this:
+ colorWell.removeAttribute("style");
+ } else {
+ colorWell.removeAttribute("default");
+ // Use setAttribute so colorwell can be a XUL element, such as button
+ colorWell.setAttribute("style", "background-color:" + color);
+ }
+ }
+}
+
+function getColorAndSetColorWell(ColorPickerID, ColorWellID) {
+ var color = getColor(ColorPickerID);
+ setColorWell(ColorWellID, color);
+ return color;
+}
+
+function InitMoreFewer() {
+ // Set SeeMore bool to the OPPOSITE of the current state,
+ // which is automatically saved by using the 'persist="more"'
+ // attribute on the gDialog.MoreFewerButton button
+ // onMoreFewer will toggle it and redraw the dialog
+ SeeMore = gDialog.MoreFewerButton.getAttribute("more") != "1";
+ onMoreFewer();
+ gDialog.MoreFewerButton.setAttribute(
+ "accesskey",
+ GetString("PropertiesAccessKey")
+ );
+}
+
+function onMoreFewer() {
+ if (SeeMore) {
+ gDialog.MoreSection.collapsed = true;
+ gDialog.MoreFewerButton.setAttribute("more", "0");
+ gDialog.MoreFewerButton.setAttribute("label", GetString("MoreProperties"));
+ SeeMore = false;
+ } else {
+ gDialog.MoreSection.collapsed = false;
+ gDialog.MoreFewerButton.setAttribute("more", "1");
+ gDialog.MoreFewerButton.setAttribute("label", GetString("FewerProperties"));
+ SeeMore = true;
+ }
+ window.sizeToContent();
+}
+
+function SwitchToValidatePanel() {
+ // no default implementation
+ // Only EdTableProps.js currently implements this
+}
+
+const nsIFilePicker = Ci.nsIFilePicker;
+
+/**
+ * @return {Promise} URL spec of the file chosen, or null
+ */
+function GetLocalFileURL(filterType) {
+ var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+ var fileType = "html";
+
+ if (filterType == "img") {
+ fp.init(window, GetString("SelectImageFile"), nsIFilePicker.modeOpen);
+ fp.appendFilters(nsIFilePicker.filterImages);
+ fileType = "image";
+ } else if (filterType.startsWith("html")) {
+ // Current usage of this is in Link dialog,
+ // where we always want HTML first
+ fp.init(window, GetString("OpenHTMLFile"), nsIFilePicker.modeOpen);
+
+ // When loading into Composer, direct user to prefer HTML files and text files,
+ // so we call separately to control the order of the filter list
+ fp.appendFilters(nsIFilePicker.filterHTML);
+ fp.appendFilters(nsIFilePicker.filterText);
+
+ // Link dialog also allows linking to images
+ if (filterType.includes("img", 1)) {
+ fp.appendFilters(nsIFilePicker.filterImages);
+ }
+ }
+ // Default or last filter is "All Files"
+ fp.appendFilters(nsIFilePicker.filterAll);
+
+ // set the file picker's current directory to last-opened location saved in prefs
+ SetFilePickerDirectory(fp, fileType);
+
+ return new Promise(resolve => {
+ fp.open(rv => {
+ if (rv != nsIFilePicker.returnOK || !fp.file) {
+ resolve(null);
+ return;
+ }
+ SaveFilePickerDirectory(fp, fileType);
+ resolve(fp.fileURL.spec);
+ });
+ });
+}
+
+function GetMetaElementByAttribute(name, value) {
+ if (name) {
+ name = name.toLowerCase();
+ let editor = GetCurrentEditor();
+ try {
+ return editor.document.querySelector(
+ "meta[" + name + '="' + value + '"]'
+ );
+ } catch (e) {}
+ }
+ return null;
+}
+
+function CreateMetaElementWithAttribute(name, value) {
+ let editor = GetCurrentEditor();
+ try {
+ let metaElement = editor.createElementWithDefaults("meta");
+ if (name) {
+ metaElement.setAttribute(name, value);
+ }
+ return metaElement;
+ } catch (e) {}
+ return null;
+}
+
+// Change "content" attribute on a META element,
+// or delete entire element it if content is empty
+// This uses undoable editor transactions
+function SetMetaElementContent(metaElement, content, insertNew, prepend) {
+ if (metaElement) {
+ var editor = GetCurrentEditor();
+ try {
+ if (!content || content == "") {
+ if (!insertNew) {
+ editor.deleteNode(metaElement);
+ }
+ } else if (insertNew) {
+ metaElement.setAttribute("content", content);
+ if (prepend) {
+ PrependHeadElement(metaElement);
+ } else {
+ AppendHeadElement(metaElement);
+ }
+ } else {
+ editor.setAttribute(metaElement, "content", content);
+ }
+ } catch (e) {}
+ }
+}
+
+function GetHeadElement() {
+ var editor = GetCurrentEditor();
+ try {
+ return editor.document.querySelector("head");
+ } catch (e) {}
+
+ return null;
+}
+
+function PrependHeadElement(element) {
+ var head = GetHeadElement();
+ if (head) {
+ var editor = GetCurrentEditor();
+ try {
+ // Use editor's undoable transaction
+ // XXX Here tried to prevent updating Selection with unknown 4th argument,
+ // but nsIEditor.setShouldTxnSetSelection is not used for that.
+ editor.insertNode(element, head, 0);
+ } catch (e) {}
+ }
+}
+
+function AppendHeadElement(element) {
+ var head = GetHeadElement();
+ if (head) {
+ var position = 0;
+ if (head.hasChildNodes()) {
+ position = head.childNodes.length;
+ }
+
+ var editor = GetCurrentEditor();
+ try {
+ // Use editor's undoable transaction
+ // XXX Here tried to prevent updating Selection with unknown 4th argument,
+ // but nsIEditor.setShouldTxnSetSelection is not used for that.
+ editor.insertNode(element, head, position);
+ } catch (e) {}
+ }
+}
+
+function SetWindowLocation() {
+ gLocation = document.getElementById("location");
+ if (gLocation) {
+ window.screenX = Math.max(
+ 0,
+ Math.min(
+ window.opener.screenX + Number(gLocation.getAttribute("offsetX")),
+ screen.availWidth - window.outerWidth
+ )
+ );
+ window.screenY = Math.max(
+ 0,
+ Math.min(
+ window.opener.screenY + Number(gLocation.getAttribute("offsetY")),
+ screen.availHeight - window.outerHeight
+ )
+ );
+ }
+}
+
+function SaveWindowLocation() {
+ if (gLocation) {
+ gLocation.setAttribute("offsetX", window.screenX - window.opener.screenX);
+ gLocation.setAttribute("offsetY", window.screenY - window.opener.screenY);
+ }
+}
+
+function onCancel() {
+ SaveWindowLocation();
+}
+
+function SetRelativeCheckbox(checkbox) {
+ if (!checkbox) {
+ checkbox = document.getElementById("MakeRelativeCheckbox");
+ if (!checkbox) {
+ return;
+ }
+ }
+
+ var editor = GetCurrentEditor();
+ // Mail never allows relative URLs, so hide the checkbox
+ if (editor && editor.flags & Ci.nsIEditor.eEditorMailMask) {
+ checkbox.collapsed = true;
+ return;
+ }
+
+ var input = document.getElementById(checkbox.getAttribute("for"));
+ if (!input) {
+ return;
+ }
+
+ var url = TrimString(input.value);
+ var urlScheme = GetScheme(url);
+
+ // Check it if url is relative (no scheme).
+ checkbox.checked = url.length > 0 && !urlScheme;
+
+ // Now do checkbox enabling:
+ var enable = false;
+
+ var docUrl = GetDocumentBaseUrl();
+ var docScheme = GetScheme(docUrl);
+
+ if (url && docUrl && docScheme) {
+ if (urlScheme) {
+ // Url is absolute
+ // If we can make a relative URL, then enable must be true!
+ // (this lets the smarts of MakeRelativeUrl do all the hard work)
+ enable = GetScheme(MakeRelativeUrl(url)).length == 0;
+ } else if (url[0] == "#") {
+ // Url is relative
+ // Check if url is a named anchor
+ // but document doesn't have a filename
+ // (it's probably "index.html" or "index.htm",
+ // but we don't want to allow a malformed URL)
+ var docFilename = GetFilename(docUrl);
+ enable = docFilename.length > 0;
+ } else {
+ // Any other url is assumed
+ // to be ok to try to make absolute
+ enable = true;
+ }
+ }
+
+ SetElementEnabled(checkbox, enable);
+}
+
+// oncommand handler for the Relativize checkbox in EditorOverlay.xhtml
+function MakeInputValueRelativeOrAbsolute(checkbox) {
+ var input = document.getElementById(checkbox.getAttribute("for"));
+ if (!input) {
+ return;
+ }
+
+ var docUrl = GetDocumentBaseUrl();
+ if (!docUrl) {
+ // Checkbox should be disabled if not saved,
+ // but keep this error message in case we change that
+ Services.prompt.alert(window, "", GetString("SaveToUseRelativeUrl"));
+ window.focus();
+ } else {
+ // Note that "checked" is opposite of its last state,
+ // which determines what we want to do here
+ if (checkbox.checked) {
+ input.value = MakeRelativeUrl(input.value);
+ } else {
+ input.value = MakeAbsoluteUrl(input.value);
+ }
+
+ // Reset checkbox to reflect url state
+ SetRelativeCheckbox(checkbox);
+ }
+}
+
+var IsBlockParent = [
+ "applet",
+ "blockquote",
+ "body",
+ "center",
+ "dd",
+ "div",
+ "form",
+ "li",
+ "noscript",
+ "object",
+ "td",
+ "th",
+];
+
+var NotAnInlineParent = [
+ "col",
+ "colgroup",
+ "dl",
+ "dir",
+ "menu",
+ "ol",
+ "table",
+ "tbody",
+ "tfoot",
+ "thead",
+ "tr",
+ "ul",
+];
+
+function nodeIsBreak(editor, node) {
+ return !node || node.localName == "br" || editor.nodeIsBlock(node);
+}
+
+function InsertElementAroundSelection(element) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ // First get the selection as a single range
+ var range, start, end, offset;
+ var count = editor.selection.rangeCount;
+ if (count == 1) {
+ range = editor.selection.getRangeAt(0).cloneRange();
+ } else {
+ range = editor.document.createRange();
+ start = editor.selection.getRangeAt(0);
+ range.setStart(start.startContainer, start.startOffset);
+ end = editor.selection.getRangeAt(--count);
+ range.setEnd(end.endContainer, end.endOffset);
+ }
+
+ // Flatten the selection to child nodes of the common ancestor
+ while (range.startContainer != range.commonAncestorContainer) {
+ range.setStartBefore(range.startContainer);
+ }
+ while (range.endContainer != range.commonAncestorContainer) {
+ range.setEndAfter(range.endContainer);
+ }
+
+ if (editor.nodeIsBlock(element)) {
+ // Block element parent must be a valid block
+ while (!IsBlockParent.includes(range.commonAncestorContainer.localName)) {
+ range.selectNode(range.commonAncestorContainer);
+ }
+ } else {
+ if (!nodeIsBreak(editor, range.commonAncestorContainer)) {
+ // Fail if we're not inserting a block (use setInlineProperty instead)
+ return false;
+ }
+ if (NotAnInlineParent.includes(range.commonAncestorContainer.localName)) {
+ // Inline element parent must not be an invalid block
+ do {
+ range.selectNode(range.commonAncestorContainer);
+ } while (
+ NotAnInlineParent.includes(range.commonAncestorContainer.localName)
+ );
+ } else {
+ // Further insert block check
+ for (var i = range.startOffset; ; i++) {
+ if (i == range.endOffset) {
+ return false;
+ }
+ if (
+ nodeIsBreak(editor, range.commonAncestorContainer.childNodes[i])
+ ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // The range may be contained by body text, which should all be selected.
+ offset = range.startOffset;
+ start = range.startContainer.childNodes[offset];
+ if (!nodeIsBreak(editor, start)) {
+ while (!nodeIsBreak(editor, start.previousSibling)) {
+ start = start.previousSibling;
+ offset--;
+ }
+ }
+ end = range.endContainer.childNodes[range.endOffset];
+ if (end && !nodeIsBreak(editor, end.previousSibling)) {
+ while (!nodeIsBreak(editor, end)) {
+ end = end.nextSibling;
+ }
+ }
+
+ // Now insert the node
+ // XXX Here tried to prevent updating Selection with unknown 4th argument,
+ // but nsIEditor.setShouldTxnSetSelection is not used for that.
+ editor.insertNode(element, range.commonAncestorContainer, offset);
+ offset = element.childNodes.length;
+ if (!editor.nodeIsBlock(element)) {
+ editor.setShouldTxnSetSelection(false);
+ }
+
+ // Move all the old child nodes to the element
+ var empty = true;
+ while (start != end) {
+ var next = start.nextSibling;
+ editor.deleteNode(start);
+ editor.insertNode(start, element, element.childNodes.length);
+ empty = false;
+ start = next;
+ }
+ if (!editor.nodeIsBlock(element)) {
+ editor.setShouldTxnSetSelection(true);
+ } else {
+ // Also move a trailing <br>
+ if (start && start.localName == "br") {
+ editor.deleteNode(start);
+ editor.insertNode(start, element, element.childNodes.length);
+ empty = false;
+ }
+ // Still nothing? Insert a <br> so the node is not empty
+ if (empty) {
+ editor.insertNode(
+ editor.createElementWithDefaults("br"),
+ element,
+ element.childNodes.length
+ );
+ }
+
+ // Hack to set the selection just inside the element
+ editor.insertNode(editor.document.createTextNode(""), element, offset);
+ }
+ } finally {
+ editor.endTransaction();
+ }
+
+ return true;
+}
+
+function nodeIsBlank(node) {
+ return node && node.nodeType == Node.TEXT_NODE && !/\S/.test(node.data);
+}
+
+function nodeBeginsBlock(editor, node) {
+ while (nodeIsBlank(node)) {
+ node = node.nextSibling;
+ }
+ return nodeIsBreak(editor, node);
+}
+
+function nodeEndsBlock(editor, node) {
+ while (nodeIsBlank(node)) {
+ node = node.previousSibling;
+ }
+ return nodeIsBreak(editor, node);
+}
+
+// C++ function isn't exposed to JS :-(
+function RemoveBlockContainer(element) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ var range = editor.document.createRange();
+ range.selectNode(element);
+ var offset = range.startOffset;
+ var parent = element.parentNode;
+
+ // May need to insert a break after the removed element
+ if (
+ !nodeBeginsBlock(editor, element.nextSibling) &&
+ !nodeEndsBlock(editor, element.lastChild)
+ ) {
+ editor.insertNode(
+ editor.createElementWithDefaults("br"),
+ parent,
+ range.endOffset
+ );
+ }
+
+ // May need to insert a break before the removed element, or if it was empty
+ if (
+ !nodeEndsBlock(editor, element.previousSibling) &&
+ !nodeBeginsBlock(editor, element.firstChild || element.nextSibling)
+ ) {
+ editor.insertNode(
+ editor.createElementWithDefaults("br"),
+ parent,
+ offset++
+ );
+ }
+
+ // Now remove the element
+ editor.deleteNode(element);
+
+ // Need to copy the contained nodes?
+ for (var i = 0; i < element.childNodes.length; i++) {
+ editor.insertNode(
+ element.childNodes[i].cloneNode(true),
+ parent,
+ offset++
+ );
+ }
+ } finally {
+ editor.endTransaction();
+ }
+}
+
+// C++ function isn't exposed to JS :-(
+function RemoveContainer(element) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ var range = editor.document.createRange();
+ var parent = element.parentNode;
+ // Allow for automatic joining of text nodes
+ // so we can't delete the container yet
+ // so we need to copy the contained nodes
+ for (var i = 0; i < element.childNodes.length; i++) {
+ range.selectNode(element);
+ editor.insertNode(
+ element.childNodes[i].cloneNode(true),
+ parent,
+ range.startOffset
+ );
+ }
+ // Now remove the element
+ editor.deleteNode(element);
+ } finally {
+ editor.endTransaction();
+ }
+}
+
+function FillLinkMenulist(linkMenulist, headingsArray) {
+ var menupopup = linkMenulist.firstChild;
+ var editor = GetCurrentEditor();
+ try {
+ var treeWalker = editor.document.createTreeWalker(
+ editor.document,
+ 1,
+ null,
+ true
+ );
+ var headingList = [];
+ var anchorList = []; // for sorting
+ var anchorMap = {}; // for weeding out duplicates and making heading anchors unique
+ var anchor;
+ var i;
+ for (
+ var element = treeWalker.nextNode();
+ element;
+ element = treeWalker.nextNode()
+ ) {
+ // grab headings
+ // Skip headings that already have a named anchor as their first child
+ // (this may miss nearby anchors, but at least we don't insert another
+ // under the same heading)
+ if (
+ element instanceof HTMLHeadingElement &&
+ element.textContent &&
+ !(
+ element.firstChild instanceof HTMLAnchorElement &&
+ element.firstChild.name
+ )
+ ) {
+ headingList.push(element);
+ }
+
+ // grab named anchors
+ if (element instanceof HTMLAnchorElement && element.name) {
+ anchor = "#" + element.name;
+ if (!(anchor in anchorMap)) {
+ anchorList.push({ anchor, sortkey: anchor.toLowerCase() });
+ anchorMap[anchor] = true;
+ }
+ }
+
+ // grab IDs
+ if (element.id) {
+ anchor = "#" + element.id;
+ if (!(anchor in anchorMap)) {
+ anchorList.push({ anchor, sortkey: anchor.toLowerCase() });
+ anchorMap[anchor] = true;
+ }
+ }
+ }
+ // add anchor for headings
+ for (i = 0; i < headingList.length; i++) {
+ var heading = headingList[i];
+
+ // Use just first 40 characters, don't add "...",
+ // and replace whitespace with "_" and strip non-word characters
+ anchor =
+ "#" +
+ ConvertToCDATAString(
+ TruncateStringAtWordEnd(heading.textContent, 40, false)
+ );
+
+ // Append "_" to any name already in the list
+ while (anchor in anchorMap) {
+ anchor += "_";
+ }
+ anchorList.push({ anchor, sortkey: anchor.toLowerCase() });
+ anchorMap[anchor] = true;
+
+ // Save nodes in an array so we can create anchor node under it later
+ headingsArray[anchor] = heading;
+ }
+ if (anchorList.length) {
+ // case insensitive sort
+ anchorList.sort((a, b) => {
+ if (a.sortkey < b.sortkey) {
+ return -1;
+ }
+ if (a.sortkey > b.sortkey) {
+ return 1;
+ }
+ return 0;
+ });
+
+ for (i = 0; i < anchorList.length; i++) {
+ createMenuItem(menupopup, anchorList[i].anchor);
+ }
+ } else {
+ // Don't bother with named anchors in Mail.
+ if (editor && editor.flags & Ci.nsIEditor.eEditorMailMask) {
+ menupopup.remove();
+ linkMenulist.removeAttribute("enablehistory");
+ return;
+ }
+ var item = createMenuItem(
+ menupopup,
+ GetString("NoNamedAnchorsOrHeadings")
+ );
+ item.setAttribute("disabled", "true");
+ }
+ } catch (e) {}
+}
+
+function createMenuItem(aMenuPopup, aLabel) {
+ var menuitem = document.createXULElement("menuitem");
+ menuitem.setAttribute("label", aLabel);
+ aMenuPopup.appendChild(menuitem);
+ return menuitem;
+}
+
+// Shared by Image and Link dialogs for the "Choose" button for links
+function chooseLinkFile() {
+ GetLocalFileURL("html, img").then(fileURL => {
+ // Always try to relativize local file URLs
+ if (gHaveDocumentUrl) {
+ fileURL = MakeRelativeUrl(fileURL);
+ }
+
+ gDialog.hrefInput.value = fileURL;
+
+ // Do stuff specific to a particular dialog
+ // (This is defined separately in Image and Link dialogs)
+ ChangeLinkLocation();
+ });
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdDialogTemplate.js b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.js
new file mode 100644
index 0000000000..ee74e8c871
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+var insertNew = true;
+var tagname = "TAG NAME";
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+ // gDialog is declared in EdDialogCommon.js
+ // Set commonly-used widgets like this:
+ gDialog.fooButton = document.getElementById("fooButton");
+
+ InitDialog();
+
+ // Set window location relative to parent window (based on persisted attributes)
+ SetWindowLocation();
+
+ // Set focus to first widget in dialog, e.g.:
+ SetTextboxFocus(gDialog.fooButton);
+}
+
+function InitDialog() {
+ // Initialize all dialog widgets here,
+ // e.g., get attributes from an element for property dialog
+}
+
+function onAccept() {
+ // Validate all user data and set attributes and possibly insert new element here
+ // If there's an error the user must correct, return false to keep dialog open.
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml
new file mode 100644
index 0000000000..8f76af4654
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDialogTemplate.xhtml
@@ -0,0 +1,23 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<?xul-overlay href="chrome://editor/content/EdDialogOverlay.xhtml"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/Ed?????????.dtd">
+<!-- dialog containing a control requiring initial setup -->
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/Ed?????.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdDictionary.js b/comm/suite/editor/components/dialogs/content/EdDictionary.js
new file mode 100644
index 0000000000..892e92dd1a
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDictionary.js
@@ -0,0 +1,164 @@
+/* -*- 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gSpellChecker;
+var gWordToAdd;
+
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+ // Get the SpellChecker shell
+ if ("gSpellChecker" in window.opener && window.opener.gSpellChecker) {
+ gSpellChecker = window.opener.gSpellChecker;
+ }
+
+ if (!gSpellChecker) {
+ dump("SpellChecker not found!!!\n");
+ window.close();
+ return;
+ }
+ // The word to add word is passed as the 2nd extra parameter in window.openDialog()
+ gWordToAdd = window.arguments[1];
+
+ gDialog.WordInput = document.getElementById("WordInput");
+ gDialog.DictionaryList = document.getElementById("DictionaryList");
+
+ gDialog.WordInput.value = gWordToAdd;
+ FillDictionaryList();
+
+ // Select the supplied word if it is already in the list
+ SelectWordToAddInList();
+ SetTextboxFocus(gDialog.WordInput);
+}
+
+function ValidateWordToAdd() {
+ gWordToAdd = TrimString(gDialog.WordInput.value);
+ if (gWordToAdd.length > 0) {
+ return true;
+ }
+ return false;
+}
+
+function SelectWordToAddInList() {
+ for (var i = 0; i < gDialog.DictionaryList.getRowCount(); i++) {
+ var wordInList = gDialog.DictionaryList.getItemAtIndex(i);
+ if (wordInList && gWordToAdd == wordInList.label) {
+ gDialog.DictionaryList.selectedIndex = i;
+ break;
+ }
+ }
+}
+
+function AddWord() {
+ if (ValidateWordToAdd()) {
+ try {
+ gSpellChecker.AddWordToDictionary(gWordToAdd);
+ } catch (e) {
+ dump(
+ "Exception occurred in gSpellChecker.AddWordToDictionary\nWord to add probably already existed\n"
+ );
+ }
+
+ // Rebuild the dialog list
+ FillDictionaryList();
+
+ SelectWordToAddInList();
+ gDialog.WordInput.value = "";
+ }
+}
+
+function ReplaceWord() {
+ if (ValidateWordToAdd()) {
+ var selItem = gDialog.DictionaryList.selectedItem;
+ if (selItem) {
+ try {
+ gSpellChecker.RemoveWordFromDictionary(selItem.label);
+ } catch (e) {}
+
+ try {
+ // Add to the dictionary list
+ gSpellChecker.AddWordToDictionary(gWordToAdd);
+
+ // Just change the text on the selected item instead of rebuilding the list.
+ // The items are richlist items, so the label sits in the first child.
+ selItem.firstChild.setAttribute("value", gWordToAdd);
+ } catch (e) {
+ // Rebuild list and select the word - it was probably already in the list
+ dump("Exception occurred adding word in ReplaceWord\n");
+ FillDictionaryList();
+ SelectWordToAddInList();
+ }
+ }
+ }
+}
+
+function RemoveWord() {
+ var selIndex = gDialog.DictionaryList.selectedIndex;
+ if (selIndex >= 0) {
+ var word = gDialog.DictionaryList.selectedItem.label;
+
+ // Remove word from list
+ gDialog.DictionaryList.selectedItem.remove();
+
+ // Remove from dictionary
+ try {
+ // Not working: BUG 43348
+ gSpellChecker.RemoveWordFromDictionary(word);
+ } catch (e) {
+ dump("Failed to remove word from dictionary\n");
+ }
+
+ ResetSelectedItem(selIndex);
+ }
+}
+
+function FillDictionaryList() {
+ var selIndex = gDialog.DictionaryList.selectedIndex;
+
+ // Clear the current contents of the list
+ ClearListbox(gDialog.DictionaryList);
+
+ // Get the list from the spell checker
+ gSpellChecker.GetPersonalDictionary();
+
+ var haveList = false;
+
+ // Get words until an empty string is returned
+ do {
+ var word = gSpellChecker.GetPersonalDictionaryWord();
+ if (word != "") {
+ gDialog.DictionaryList.appendItem(word, "");
+ haveList = true;
+ }
+ } while (word != "");
+
+ // XXX: BUG 74467: If list is empty, it doesn't layout to full height correctly
+ // (ignores "rows" attribute) (bug is latered, so we are fixing here for now)
+ if (!haveList) {
+ gDialog.DictionaryList.appendItem("", "");
+ }
+
+ ResetSelectedItem(selIndex);
+}
+
+function ResetSelectedItem(index) {
+ var lastIndex = gDialog.DictionaryList.getRowCount() - 1;
+ if (index > lastIndex) {
+ index = lastIndex;
+ }
+
+ // If we didn't have a selected item,
+ // set it to the first item
+ if (index == -1 && lastIndex >= 0) {
+ index = 0;
+ }
+
+ gDialog.DictionaryList.selectedIndex = index;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdDictionary.xhtml b/comm/suite/editor/components/dialogs/content/EdDictionary.xhtml
new file mode 100644
index 0000000000..cebbe1c192
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdDictionary.xhtml
@@ -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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPersonalDictionary.dtd">
+<dialog buttons="cancel" title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ persist="screenX screenY"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdDictionary.js"/>
+
+ <grid>
+ <columns><column style="width: 15em" flex="1"/><column flex="1"/></columns>
+ <rows>
+ <row>
+ <label value="&wordEditField.label;"
+ control="WordInput"
+ accesskey="&wordEditField.accessKey;"/>
+ <spacer/>
+ </row>
+ <row>
+ <textbox id="WordInput" flex="1"/>
+ <button id="AddWord" oncommand="AddWord()" label="&AddButton.label;"
+ accesskey="&AddButton.accessKey;"/>
+ </row>
+ <row>
+ <label value="&DictionaryList.label;"
+ control="DictionaryList"
+ accesskey="&DictionaryList.accessKey;"/>
+ <spacer/>
+ </row>
+ <row>
+ <richlistbox id="DictionaryList"
+ class="theme-listbox"
+ flex="1"
+ height="150px"/>
+ <vbox flex="1">
+ <button id="ReplaceWord" oncommand="ReplaceWord()" label="&ReplaceButton.label;"
+ accesskey="&ReplaceButton.accessKey;"/>
+ <spacer class="spacer"/>
+ <button id="RemoveWord" oncommand="RemoveWord()" label="&RemoveButton.label;"
+ accesskey="&RemoveButton.accessKey;"/>
+ <spacer class="spacer"/>
+ <spacer flex="1"/>
+ <button dlgtype="cancel" class="exit-dialog" id="close" label="&CloseButton.label;"
+ default="true" oncommand="onClose();"
+ accesskey="&CloseButton.accessKey;"/>
+ </vbox>
+ </row>
+ </rows>
+ </grid>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdFieldSetProps.js b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.js
new file mode 100644
index 0000000000..1799a2b28f
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.js
@@ -0,0 +1,196 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var fieldsetElement;
+var newLegend;
+var legendElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gDialog.editText = document.getElementById("EditText");
+ gDialog.legendText = document.getElementById("LegendText");
+ gDialog.legendAlign = document.getElementById("LegendAlign");
+ gDialog.RemoveFieldSet = document.getElementById("RemoveFieldSet");
+
+ // Get a single selected field set element
+ const kTagName = "fieldset";
+ try {
+ // Find a selected fieldset, or if one is at start or end of selection.
+ fieldsetElement = editor.getSelectedElement(kTagName);
+ if (!fieldsetElement) {
+ fieldsetElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.anchorNode
+ );
+ }
+ if (!fieldsetElement) {
+ fieldsetElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.focusNode
+ );
+ }
+ } catch (e) {}
+
+ if (fieldsetElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ fieldsetElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!fieldsetElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ // Hide button removing existing fieldset
+ gDialog.RemoveFieldSet.hidden = true;
+ }
+
+ legendElement = fieldsetElement.querySelector("legend");
+ if (legendElement) {
+ newLegend = false;
+ var range = editor.document.createRange();
+ range.selectNode(legendElement);
+ gDialog.legendText.value = range.toString();
+ if (legendElement.innerHTML.includes("<")) {
+ gDialog.editText.checked = false;
+ gDialog.editText.disabled = false;
+ gDialog.legendText.disabled = true;
+ gDialog.editText.addEventListener(
+ "command",
+ () =>
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("EditTextWarning")
+ ),
+ { capture: false, once: true }
+ );
+ gDialog.RemoveFieldSet.focus();
+ } else {
+ SetTextboxFocus(gDialog.legendText);
+ }
+ } else {
+ newLegend = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+
+ legendElement = editor.createElementWithDefaults("legend");
+ if (!legendElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ SetTextboxFocus(gDialog.legendText);
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = legendElement.cloneNode(false);
+
+ InitDialog();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ gDialog.legendAlign.value = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "align",
+ "caption-side"
+ );
+}
+
+function RemoveFieldSet() {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+ try {
+ if (!newLegend) {
+ editor.deleteNode(legendElement);
+ }
+ RemoveBlockContainer(fieldsetElement);
+ } finally {
+ editor.endTransaction();
+ }
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ if (gDialog.legendAlign.value) {
+ globalElement.setAttribute("align", gDialog.legendAlign.value);
+ } else {
+ globalElement.removeAttribute("align");
+ }
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ try {
+ editor.cloneAttributes(legendElement, globalElement);
+
+ if (insertNew) {
+ if (gDialog.legendText.value) {
+ fieldsetElement.appendChild(legendElement);
+ legendElement.appendChild(
+ editor.document.createTextNode(gDialog.legendText.value)
+ );
+ }
+ InsertElementAroundSelection(fieldsetElement);
+ } else if (gDialog.editText.checked) {
+ editor.setShouldTxnSetSelection(false);
+
+ if (gDialog.legendText.value) {
+ if (newLegend) {
+ editor.insertNode(legendElement, fieldsetElement, 0);
+ } else {
+ while (legendElement.firstChild) {
+ editor.deleteNode(legendElement.lastChild);
+ }
+ }
+ editor.insertNode(
+ editor.document.createTextNode(gDialog.legendText.value),
+ legendElement,
+ 0
+ );
+ } else if (!newLegend) {
+ editor.deleteNode(legendElement);
+ }
+
+ editor.setShouldTxnSetSelection(true);
+ }
+ } finally {
+ editor.endTransaction();
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml
new file mode 100644
index 0000000000..0d67fad276
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFieldSetProps.xhtml
@@ -0,0 +1,67 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edFieldSetProperties SYSTEM "chrome://editor/locale/EditorFieldSetProperties.dtd">
+%edFieldSetProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdFieldSetProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header" accesskey="&Legend.accesskey;">&Legend.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <checkbox id="EditText" label="&EditLegendText.label;" accesskey="&EditLegendText.accesskey;" checked="true" disabled="true"
+ oncommand="gDialog.legendText.disabled = !gDialog.editText.checked;"/>
+ <textbox id="LegendText" accesskey="&Legend.accesskey;"/>
+ </row>
+ <row align="center">
+ <label control="LegendAlign" value="&LegendAlign.label;" accesskey="&LegendAlign.accesskey;"/>
+ <menulist id="LegendAlign">
+ <menupopup>
+ <menuitem label="&AlignDefault.label;"/>
+ <menuitem label="&AlignLeft.label;" value="left"/>
+ <menuitem label="&AlignCenter.label;" value="center"/>
+ <menuitem label="&AlignRight.label;" value="right"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- from EdDialogOverlay -->
+ <hbox flex="1" style="margin-top: 0.2em">
+ <button id="RemoveFieldSet" label="&RemoveFieldSet.label;" accesskey="&RemoveFieldSet.accesskey;" oncommand="RemoveFieldSet();"/>
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator class="groove"/>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdFormProps.js b/comm/suite/editor/components/dialogs/content/EdFormProps.js
new file mode 100644
index 0000000000..9391fa4316
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFormProps.js
@@ -0,0 +1,136 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gForm;
+var insertNew;
+var formElement;
+var formActionWarning;
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gForm = {
+ Name: document.getElementById("FormName"),
+ Action: document.getElementById("FormAction"),
+ Method: document.getElementById("FormMethod"),
+ EncType: document.getElementById("FormEncType"),
+ Target: document.getElementById("FormTarget"),
+ };
+ gDialog.MoreSection = document.getElementById("MoreSection");
+ gDialog.MoreFewerButton = document.getElementById("MoreFewerButton");
+ gDialog.RemoveForm = document.getElementById("RemoveForm");
+
+ // Get a single selected form element
+ const kTagName = "form";
+ try {
+ formElement = editor.getSelectedElement(kTagName);
+ if (!formElement) {
+ formElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.anchorNode
+ );
+ }
+ if (!formElement) {
+ formElement = editor.getElementOrParentByTagName(
+ kTagName,
+ editor.selection.focusNode
+ );
+ }
+ } catch (e) {}
+
+ if (formElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ formActionWarning = formElement.hasAttribute("action");
+ } else {
+ insertNew = true;
+ formActionWarning = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ formElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!formElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ // Hide button removing existing form
+ gDialog.RemoveForm.hidden = true;
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = formElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ SetTextboxFocus(gForm.Name);
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ for (var attribute in gForm) {
+ gForm[attribute].value = globalElement.getAttribute(attribute);
+ }
+}
+
+function RemoveForm() {
+ RemoveBlockContainer(formElement);
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ for (var attribute in gForm) {
+ if (gForm[attribute].value) {
+ globalElement.setAttribute(attribute, gForm[attribute].value);
+ } else {
+ globalElement.removeAttribute(attribute);
+ }
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (formActionWarning && !gForm.Action.value) {
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("NoFormAction")
+ );
+ gForm.Action.focus();
+ formActionWarning = false;
+ event.preventDefault();
+ return;
+ }
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.cloneAttributes(formElement, globalElement);
+
+ if (insertNew) {
+ InsertElementAroundSelection(formElement);
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdFormProps.xhtml b/comm/suite/editor/components/dialogs/content/EdFormProps.xhtml
new file mode 100644
index 0000000000..275daef785
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdFormProps.xhtml
@@ -0,0 +1,98 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edFormProperties SYSTEM "chrome://editor/locale/EditorFormProperties.dtd">
+%edFormProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdFormProps.js"/>
+
+ <script src="chrome://messenger/content/customElements.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&Settings.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="FormName" value="&FormName.label;" accesskey="&FormName.accesskey;"/>
+ <textbox id="FormName"/>
+ </row>
+ <row align="center">
+ <label control="FormAction" value="&FormAction.label;" accesskey="&FormAction.accesskey;"/>
+ <textbox id="FormAction"/>
+ </row>
+ <row align="center">
+ <label control="FormMethod" value="&FormMethod.label;" accesskey="&FormMethod.accesskey;"/>
+ <hbox>
+ <menulist is="menulist-editable" id="FormMethod" editable="true" autoSelectMenuitem="true">
+ <menupopup>
+ <menuitem label="GET"/>
+ <menuitem label="POST"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </row>
+ <hbox>
+ <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/>
+ </hbox>
+ <rows id="MoreSection">
+ <row align="center">
+ <label control="FormEncType" value="&FormEncType.label;" accesskey="&FormEncType.accesskey;"/>
+ <menulist is="menulist-editable" id="FormEncType" editable="true" autoSelectMenuitem="true">
+ <menupopup>
+ <menuitem label="application/x-www-form-urlencoded"/>
+ <menuitem label="multipart/form-data"/>
+ <menuitem label="text/plain"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <label control="FormTarget" value="&FormTarget.label;" accesskey="&FormTarget.accesskey;"/>
+ <menulist is="menulist-editable" id="FormTarget" editable="true" autoSelectMenuitem="true">
+ <menupopup>
+ <menuitem label="_blank"/>
+ <menuitem label="_self"/>
+ <menuitem label="_parent"/>
+ <menuitem label="_top"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- from EdDialogOverlay -->
+ <hbox flex="1" style="margin-top: 0.2em">
+ <button id="RemoveForm" label="&RemoveForm.label;" accesskey="&RemoveForm.accesskey;" oncommand="RemoveForm();"/>
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdHLineProps.js b/comm/suite/editor/components/dialogs/content/EdHLineProps.js
new file mode 100644
index 0000000000..eff9c06a41
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdHLineProps.js
@@ -0,0 +1,227 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var tagName = "hr";
+var gHLineElement;
+var width;
+var height;
+var align;
+var shading;
+const gMaxHRSize = 1000; // This is hard-coded in nsHTMLHRElement::StringToAttribute()
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+ try {
+ // Get the selected horizontal line
+ gHLineElement = editor.getSelectedElement(tagName);
+ } catch (e) {}
+
+ if (!gHLineElement) {
+ // We should never be here if not editing an existing HLine
+ window.close();
+ return;
+ }
+ gDialog.heightInput = document.getElementById("height");
+ gDialog.widthInput = document.getElementById("width");
+ gDialog.leftAlign = document.getElementById("leftAlign");
+ gDialog.centerAlign = document.getElementById("centerAlign");
+ gDialog.rightAlign = document.getElementById("rightAlign");
+ gDialog.alignGroup = gDialog.rightAlign.radioGroup;
+ gDialog.shading = document.getElementById("3dShading");
+ gDialog.pixelOrPercentMenulist = document.getElementById(
+ "pixelOrPercentMenulist"
+ );
+
+ // Make a copy to use for AdvancedEdit and onSaveDefault
+ globalElement = gHLineElement.cloneNode(false);
+
+ // Initialize control values based on existing attributes
+ InitDialog();
+
+ // SET FOCUS TO FIRST CONTROL
+ SetTextboxFocus(gDialog.widthInput);
+
+ // Resize window
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog() {
+ // Just to be confusing, "size" is used instead of height because it does
+ // not accept % values, only pixels
+ var height = GetHTMLOrCSSStyleValue(globalElement, "size", "height");
+ if (height.includes("px")) {
+ height = height.substr(0, height.indexOf("px"));
+ }
+ if (!height) {
+ height = 2; // Default value
+ }
+
+ // We will use "height" here and in UI
+ gDialog.heightInput.value = height;
+
+ // Get the width attribute of the element, stripping out "%"
+ // This sets contents of menulist (adds pixel and percent menuitems elements)
+ gDialog.widthInput.value = InitPixelOrPercentMenulist(
+ globalElement,
+ gHLineElement,
+ "width",
+ "pixelOrPercentMenulist"
+ );
+
+ var marginLeft = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "align",
+ "margin-left"
+ ).toLowerCase();
+ var marginRight = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "align",
+ "margin-right"
+ ).toLowerCase();
+ align = marginLeft + " " + marginRight;
+ gDialog.leftAlign.checked = align == "left left" || align == "0px auto";
+ gDialog.centerAlign.checked =
+ align == "center center" || align == "auto auto" || align == " ";
+ gDialog.rightAlign.checked = align == "right right" || align == "auto 0px";
+
+ if (gDialog.centerAlign.checked) {
+ gDialog.alignGroup.selectedItem = gDialog.centerAlign;
+ } else if (gDialog.rightAlign.checked) {
+ gDialog.alignGroup.selectedItem = gDialog.rightAlign;
+ } else {
+ gDialog.alignGroup.selectedItem = gDialog.leftAlign;
+ }
+
+ gDialog.shading.checked = !globalElement.hasAttribute("noshade");
+}
+
+function onSaveDefault() {
+ // "false" means set attributes on the globalElement,
+ // not the real element being edited
+ if (ValidateData()) {
+ var alignInt;
+ if (align == "left") {
+ alignInt = 0;
+ } else if (align == "right") {
+ alignInt = 2;
+ } else {
+ alignInt = 1;
+ }
+ Services.prefs.setIntPref("editor.hrule.align", alignInt);
+
+ var percent;
+ var widthInt;
+ var heightInt;
+
+ if (width) {
+ if (width.includes("%")) {
+ percent = true;
+ widthInt = Number(width.substr(0, width.indexOf("%")));
+ } else {
+ percent = false;
+ widthInt = Number(width);
+ }
+ } else {
+ percent = true;
+ widthInt = Number(100);
+ }
+
+ heightInt = height ? Number(height) : 2;
+
+ Services.prefs.setIntPref("editor.hrule.width", widthInt);
+ Services.prefs.setBoolPref("editor.hrule.width_percent", percent);
+ Services.prefs.setIntPref("editor.hrule.height", heightInt);
+ Services.prefs.setBoolPref("editor.hrule.shading", shading);
+
+ // Write the prefs out NOW!
+ Services.prefs.savePrefFile(null);
+ }
+}
+
+// Get and validate data from widgets.
+// Set attributes on globalElement so they can be accessed by AdvancedEdit()
+function ValidateData() {
+ // Height is always pixels
+ height = ValidateNumber(
+ gDialog.heightInput,
+ null,
+ 1,
+ gMaxHRSize,
+ globalElement,
+ "size",
+ false
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ width = ValidateNumber(
+ gDialog.widthInput,
+ gDialog.pixelOrPercentMenulist,
+ 1,
+ gMaxPixels,
+ globalElement,
+ "width",
+ false
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ align = "left";
+ if (gDialog.centerAlign.selected) {
+ // Don't write out default attribute
+ align = "";
+ } else if (gDialog.rightAlign.selected) {
+ align = "right";
+ }
+ if (align) {
+ globalElement.setAttribute("align", align);
+ } else {
+ try {
+ GetCurrentEditor().removeAttributeOrEquivalent(
+ globalElement,
+ "align",
+ true
+ );
+ } catch (e) {}
+ }
+
+ if (gDialog.shading.checked) {
+ shading = true;
+ globalElement.removeAttribute("noshade");
+ } else {
+ shading = false;
+ globalElement.setAttribute("noshade", "noshade");
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ // Copy attributes from the globalElement to the document element
+ try {
+ GetCurrentEditor().cloneAttributes(gHLineElement, globalElement);
+ } catch (e) {}
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml b/comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml
new file mode 100644
index 0000000000..fbcfa3b594
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdHLineProps.xhtml
@@ -0,0 +1,80 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edHLineProperties SYSTEM "chrome://editor/locale/EditorHLineProperties.dtd">
+%edHLineProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <!--- Element-specific methods -->
+ <script src="chrome://editor/content/EdHLineProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&dimensionsBox.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column/><column /></columns>
+ <rows>
+ <row align="center">
+ <label control="width"
+ value="&widthEditField.label;"
+ accesskey="&widthEditField.accessKey;"/>
+ <textbox class="narrow" id="width" flex="1" oninput="forceInteger('width')"/>
+ <menulist id="pixelOrPercentMenulist" />
+ <!-- menupopup and menuitems added by JS -->
+ </row>
+ <row align="center">
+ <label control="height"
+ value="&heightEditField.label;"
+ accesskey="&heightEditField.accessKey;"/>
+ <textbox class="narrow" id="height" oninput="forceInteger('height')"/>
+ <label value="&pixelsPopup.value;" />
+ </row>
+ </rows>
+ </grid>
+ <checkbox id="3dShading" label="&threeDShading.label;" accesskey="&threeDShading.accessKey;"/>
+ </groupbox>
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&alignmentBox.label;</label>
+ </hbox>
+ <radiogroup id="alignmentGroup" orient="horizontal">
+ <spacer class="spacer"/>
+ <radio id="leftAlign" label="&leftRadio.label;" accesskey="&leftRadio.accessKey;"/>
+ <radio id="centerAlign" label="&centerRadio.label;" accesskey="&centerRadio.accessKey;"/>
+ <radio id="rightAlign" label="&rightRadio.label;" accesskey="&rightRadio.accessKey;"/>
+ </radiogroup>
+ </groupbox>
+ <spacer class="spacer"/>
+ <hbox>
+ <button id="SaveDefault" label="&saveSettings.label;"
+ accesskey="&saveSettings.accessKey;"
+ oncommand="onSaveDefault()"
+ tooltiptext="&saveSettings.tooltip;" />
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdImageDialog.js b/comm/suite/editor/components/dialogs/content/EdImageDialog.js
new file mode 100644
index 0000000000..0e697dbd6f
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageDialog.js
@@ -0,0 +1,661 @@
+/* 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/. */
+
+/*
+ Note: We encourage non-empty alt text for images inserted into a page.
+ When there's no alt text, we always write 'alt=""' as the attribute, since "alt" is a required attribute.
+ We allow users to not have alt text by checking a "Don't use alterate text" radio button,
+ and we don't accept spaces as valid alt text. A space used to be required to avoid the error message
+ if user didn't enter alt text, but is unnecessary now that we no longer annoy the user
+ with the error dialog if alt="" is present on an img element.
+ We trim all spaces at the beginning and end of user's alt text
+*/
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gInsertNewImage = true;
+var gDoAltTextError = false;
+var gConstrainOn = false;
+// Note used in current version, but these are set correctly
+// and could be used to reset width and height used for constrain ratio
+var gConstrainWidth = 0;
+var gConstrainHeight = 0;
+var imageElement;
+var gImageMap = 0;
+var gCanRemoveImageMap = false;
+var gRemoveImageMap = false;
+var gImageMapDisabled = false;
+var gActualWidth = "";
+var gActualHeight = "";
+var gOriginalSrc = "";
+var gTimerID;
+var gValidateTab;
+var gInsertNewIMap;
+
+// These must correspond to values in EditorDialog.css for each theme
+// (unfortunately, setting "style" attribute here doesn't work!)
+var gPreviewImageWidth = 80;
+var gPreviewImageHeight = 50;
+
+// dialog initialization code
+
+function ImageStartup() {
+ gDialog.tabBox = document.getElementById("TabBox");
+ gDialog.tabLocation = document.getElementById("imageLocationTab");
+ gDialog.tabDimensions = document.getElementById("imageDimensionsTab");
+ gDialog.tabBorder = document.getElementById("imageBorderTab");
+ gDialog.srcInput = document.getElementById("srcInput");
+ gDialog.titleInput = document.getElementById("titleInput");
+ gDialog.altTextInput = document.getElementById("altTextInput");
+ gDialog.altTextRadioGroup = document.getElementById("altTextRadioGroup");
+ gDialog.altTextRadio = document.getElementById("altTextRadio");
+ gDialog.noAltTextRadio = document.getElementById("noAltTextRadio");
+ gDialog.actualSizeRadio = document.getElementById("actualSizeRadio");
+ gDialog.constrainCheckbox = document.getElementById("constrainCheckbox");
+ gDialog.widthInput = document.getElementById("widthInput");
+ gDialog.heightInput = document.getElementById("heightInput");
+ gDialog.widthUnitsMenulist = document.getElementById("widthUnitsMenulist");
+ gDialog.heightUnitsMenulist = document.getElementById("heightUnitsMenulist");
+ gDialog.imagelrInput = document.getElementById("imageleftrightInput");
+ gDialog.imagetbInput = document.getElementById("imagetopbottomInput");
+ gDialog.border = document.getElementById("border");
+ gDialog.alignTypeSelect = document.getElementById("alignTypeSelect");
+ gDialog.ImageHolder = document.getElementById("preview-image-holder");
+ gDialog.PreviewWidth = document.getElementById("PreviewWidth");
+ gDialog.PreviewHeight = document.getElementById("PreviewHeight");
+ gDialog.PreviewSize = document.getElementById("PreviewSize");
+ gDialog.PreviewImage = null;
+ gDialog.OkButton = document.documentElement.getButton("accept");
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitImage() {
+ // Set the controls to the image's attributes
+ var src = globalElement.getAttribute("src");
+
+ // For image insertion the 'src' attribute is null.
+ if (src) {
+ // Shorten data URIs for display.
+ shortenImageData(src, gDialog.srcInput);
+ }
+
+ // Set "Relativize" checkbox according to current URL state
+ SetRelativeCheckbox();
+
+ // Force loading of image from its source and show preview image
+ LoadPreviewImage();
+
+ gDialog.titleInput.value = globalElement.getAttribute("title");
+
+ var hasAltText = globalElement.hasAttribute("alt");
+ var altText = globalElement.getAttribute("alt");
+ gDialog.altTextInput.value = altText;
+ if (altText || (!hasAltText && globalElement.hasAttribute("src"))) {
+ gDialog.altTextRadioGroup.selectedItem = gDialog.altTextRadio;
+ } else if (hasAltText) {
+ gDialog.altTextRadioGroup.selectedItem = gDialog.noAltTextRadio;
+ }
+ SetAltTextDisabled(
+ gDialog.altTextRadioGroup.selectedItem == gDialog.noAltTextRadio
+ );
+
+ // setup the height and width widgets
+ var width = InitPixelOrPercentMenulist(
+ globalElement,
+ gInsertNewImage ? null : imageElement,
+ "width",
+ "widthUnitsMenulist",
+ gPixel
+ );
+ var height = InitPixelOrPercentMenulist(
+ globalElement,
+ gInsertNewImage ? null : imageElement,
+ "height",
+ "heightUnitsMenulist",
+ gPixel
+ );
+
+ // Set actual radio button if both set values are the same as actual
+ SetSizeWidgets(width, height);
+
+ gDialog.widthInput.value = gConstrainWidth = width || gActualWidth || "";
+ gDialog.heightInput.value = gConstrainHeight = height || gActualHeight || "";
+
+ // set spacing editfields
+ gDialog.imagelrInput.value = globalElement.getAttribute("hspace");
+ gDialog.imagetbInput.value = globalElement.getAttribute("vspace");
+
+ // dialog.border.value = globalElement.getAttribute("border");
+ var bv = GetHTMLOrCSSStyleValue(globalElement, "border", "border-top-width");
+ if (bv.includes("px")) {
+ // Strip out the px
+ bv = bv.substr(0, bv.indexOf("px"));
+ } else if (bv == "thin") {
+ bv = "1";
+ } else if (bv == "medium") {
+ bv = "3";
+ } else if (bv == "thick") {
+ bv = "5";
+ }
+ gDialog.border.value = bv;
+
+ // Get alignment setting
+ var align = globalElement.getAttribute("align");
+ if (align) {
+ align = align.toLowerCase();
+ }
+
+ switch (align) {
+ case "top":
+ case "middle":
+ case "right":
+ case "left":
+ gDialog.alignTypeSelect.value = align;
+ break;
+ default:
+ // Default or "bottom"
+ gDialog.alignTypeSelect.value = "bottom";
+ }
+
+ // Get image map for image
+ gImageMap = GetImageMap();
+
+ doOverallEnabling();
+ doDimensionEnabling();
+}
+
+function SetSizeWidgets(width, height) {
+ if (
+ !(width || height) ||
+ (gActualWidth &&
+ gActualHeight &&
+ width == gActualWidth &&
+ height == gActualHeight)
+ ) {
+ gDialog.actualSizeRadio.radioGroup.selectedItem = gDialog.actualSizeRadio;
+ }
+
+ if (!gDialog.actualSizeRadio.selected) {
+ // Decide if user's sizes are in the same ratio as actual sizes
+ if (gActualWidth && gActualHeight) {
+ if (gActualWidth > gActualHeight) {
+ gDialog.constrainCheckbox.checked =
+ Math.round((gActualHeight * width) / gActualWidth) == height;
+ } else {
+ gDialog.constrainCheckbox.checked =
+ Math.round((gActualWidth * height) / gActualHeight) == width;
+ }
+ }
+ }
+}
+
+// Disable alt text input when "Don't use alt" radio is checked
+function SetAltTextDisabled(disable) {
+ gDialog.altTextInput.disabled = disable;
+}
+
+function GetImageMap() {
+ var usemap = globalElement.getAttribute("usemap");
+ if (usemap) {
+ gCanRemoveImageMap = true;
+ let mapname = usemap.substr(1);
+ try {
+ return GetCurrentEditor().document.querySelector(
+ '[name="' + mapname + '"]'
+ );
+ } catch (e) {}
+ } else {
+ gCanRemoveImageMap = false;
+ }
+
+ return null;
+}
+
+function chooseFile() {
+ if (gTimerID) {
+ clearTimeout(gTimerID);
+ }
+
+ // Put focus into the input field
+ SetTextboxFocus(gDialog.srcInput);
+
+ GetLocalFileURL("img").then(fileURL => {
+ // Always try to relativize local file URLs
+ if (gHaveDocumentUrl) {
+ fileURL = MakeRelativeUrl(fileURL);
+ }
+
+ gDialog.srcInput.value = fileURL;
+
+ SetRelativeCheckbox();
+ doOverallEnabling();
+ LoadPreviewImage();
+ });
+}
+
+function PreviewImageLoaded() {
+ if (gDialog.PreviewImage) {
+ // Image loading has completed -- we can get actual width
+ gActualWidth = gDialog.PreviewImage.naturalWidth;
+ gActualHeight = gDialog.PreviewImage.naturalHeight;
+
+ if (gActualWidth && gActualHeight) {
+ // Use actual size or scale to fit preview if either dimension is too large
+ var width = gActualWidth;
+ var height = gActualHeight;
+ if (gActualWidth > gPreviewImageWidth) {
+ width = gPreviewImageWidth;
+ height = gActualHeight * (gPreviewImageWidth / gActualWidth);
+ }
+ if (height > gPreviewImageHeight) {
+ height = gPreviewImageHeight;
+ width = gActualWidth * (gPreviewImageHeight / gActualHeight);
+ }
+ gDialog.PreviewImage.width = width;
+ gDialog.PreviewImage.height = height;
+
+ gDialog.PreviewWidth.setAttribute("value", gActualWidth);
+ gDialog.PreviewHeight.setAttribute("value", gActualHeight);
+
+ gDialog.PreviewSize.collapsed = false;
+ gDialog.ImageHolder.collapsed = false;
+
+ SetSizeWidgets(gDialog.widthInput.value, gDialog.heightInput.value);
+ }
+
+ if (gDialog.actualSizeRadio.selected) {
+ SetActualSize();
+ }
+ }
+}
+
+function LoadPreviewImage() {
+ gDialog.PreviewSize.collapsed = true;
+ // XXXbz workaround for bug 265416 / bug 266284
+ gDialog.ImageHolder.collapsed = true;
+
+ var imageSrc = TrimString(gDialog.srcInput.value);
+ if (!imageSrc) {
+ return;
+ }
+ if (isImageDataShortened(imageSrc)) {
+ imageSrc = restoredImageData(gDialog.srcInput);
+ }
+
+ try {
+ // Remove the image URL from image cache so it loads fresh
+ // (if we don't do this, loads after the first will always use image cache
+ // and we won't see image edit changes or be able to get actual width and height)
+
+ // We must have an absolute URL to preview it or remove it from the cache
+ imageSrc = MakeAbsoluteUrl(imageSrc);
+
+ if (GetScheme(imageSrc)) {
+ let uri = Services.io.newURI(imageSrc);
+ if (uri) {
+ let imgCache = Cc["@mozilla.org/image/cache;1"].getService(
+ Ci.imgICache
+ );
+
+ // This returns error if image wasn't in the cache; ignore that
+ imgCache.removeEntry(uri);
+ }
+ }
+ } catch (e) {}
+
+ if (gDialog.PreviewImage) {
+ removeEventListener("load", PreviewImageLoaded, true);
+ }
+
+ if (gDialog.ImageHolder.hasChildNodes()) {
+ gDialog.ImageHolder.firstChild.remove();
+ }
+
+ gDialog.PreviewImage = document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "img"
+ );
+ if (gDialog.PreviewImage) {
+ // set the src before appending to the document -- see bug 198435 for why
+ // this is needed.
+ // XXXbz that bug is long-since fixed. Is this still needed?
+ gDialog.PreviewImage.addEventListener("load", PreviewImageLoaded, true);
+ gDialog.PreviewImage.src = imageSrc;
+ gDialog.ImageHolder.appendChild(gDialog.PreviewImage);
+ }
+}
+
+function SetActualSize() {
+ gDialog.widthInput.value = gActualWidth ? gActualWidth : "";
+ gDialog.widthUnitsMenulist.selectedIndex = 0;
+ gDialog.heightInput.value = gActualHeight ? gActualHeight : "";
+ gDialog.heightUnitsMenulist.selectedIndex = 0;
+ doDimensionEnabling();
+}
+
+function ChangeImageSrc() {
+ if (gTimerID) {
+ clearTimeout(gTimerID);
+ }
+
+ gTimerID = setTimeout(LoadPreviewImage, 800);
+
+ SetRelativeCheckbox();
+ doOverallEnabling();
+}
+
+function doDimensionEnabling() {
+ // Enabled unless "Actual Size" is selected
+ var enable = !gDialog.actualSizeRadio.selected;
+
+ // BUG 74145: After input field is disabled,
+ // setting it enabled causes blinking caret to appear
+ // even though focus isn't set to it.
+ SetElementEnabledById("heightInput", enable);
+ SetElementEnabledById("heightLabel", enable);
+ SetElementEnabledById("heightUnitsMenulist", enable);
+
+ SetElementEnabledById("widthInput", enable);
+ SetElementEnabledById("widthLabel", enable);
+ SetElementEnabledById("widthUnitsMenulist", enable);
+
+ var constrainEnable =
+ enable &&
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0;
+
+ SetElementEnabledById("constrainCheckbox", constrainEnable);
+}
+
+function doOverallEnabling() {
+ var enabled = TrimString(gDialog.srcInput.value) != "";
+
+ SetElementEnabled(gDialog.OkButton, enabled);
+ SetElementEnabledById("AdvancedEditButton1", enabled);
+ SetElementEnabledById("imagemapLabel", enabled);
+ SetElementEnabledById("removeImageMap", gCanRemoveImageMap);
+}
+
+function ToggleConstrain() {
+ // If just turned on, save the current width and height as basis for constrain ratio
+ // Thus clicking on/off lets user say "Use these values as aspect ration"
+ if (
+ gDialog.constrainCheckbox.checked &&
+ !gDialog.constrainCheckbox.disabled &&
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0
+ ) {
+ gConstrainWidth = Number(TrimString(gDialog.widthInput.value));
+ gConstrainHeight = Number(TrimString(gDialog.heightInput.value));
+ }
+}
+
+function constrainProportions(srcID, destID) {
+ var srcElement = document.getElementById(srcID);
+ if (!srcElement) {
+ return;
+ }
+
+ var destElement = document.getElementById(destID);
+ if (!destElement) {
+ return;
+ }
+
+ // always force an integer (whether we are constraining or not)
+ forceInteger(srcID);
+
+ if (
+ !gActualWidth ||
+ !gActualHeight ||
+ !(gDialog.constrainCheckbox.checked && !gDialog.constrainCheckbox.disabled)
+ ) {
+ return;
+ }
+
+ // double-check that neither width nor height is in percent mode; bail if so!
+ if (
+ gDialog.widthUnitsMenulist.selectedIndex != 0 ||
+ gDialog.heightUnitsMenulist.selectedIndex != 0
+ ) {
+ return;
+ }
+
+ // This always uses the actual width and height ratios
+ // which is kind of funky if you change one number without the constrain
+ // and then turn constrain on and change a number
+ // I prefer the old strategy (below) but I can see some merit to this solution
+ if (srcID == "widthInput") {
+ destElement.value = Math.round(
+ (srcElement.value * gActualHeight) / gActualWidth
+ );
+ } else {
+ destElement.value = Math.round(
+ (srcElement.value * gActualWidth) / gActualHeight
+ );
+ }
+
+ /*
+ // With this strategy, the width and height ratio
+ // can be reset to whatever the user entered.
+ if (srcID == "widthInput") {
+ destElement.value = Math.round( srcElement.value * gConstrainHeight / gConstrainWidth );
+ } else {
+ destElement.value = Math.round( srcElement.value * gConstrainWidth / gConstrainHeight );
+ }
+ */
+}
+
+function removeImageMap() {
+ gRemoveImageMap = true;
+ gCanRemoveImageMap = false;
+ SetElementEnabledById("removeImageMap", false);
+}
+
+function SwitchToValidatePanel() {
+ if (
+ gDialog.tabBox &&
+ gValidateTab &&
+ gDialog.tabBox.selectedTab != gValidateTab
+ ) {
+ gDialog.tabBox.selectedTab = gValidateTab;
+ }
+}
+
+// Get data from widgets, validate, and set for the global element
+// accessible to AdvancedEdit() [in EdDialogCommon.js]
+function ValidateImage() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ return false;
+ }
+
+ gValidateTab = gDialog.tabLocation;
+ if (!gDialog.srcInput.value) {
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("MissingImageError")
+ );
+ SwitchToValidatePanel();
+ gDialog.srcInput.focus();
+ return false;
+ }
+
+ // We must convert to "file:///" or "http://" format else image doesn't load!
+ let src = gDialog.srcInput.value.trim();
+
+ if (isImageDataShortened(src)) {
+ src = restoredImageData(gDialog.srcInput);
+ } else {
+ var checkbox = document.getElementById("MakeRelativeCheckbox");
+ try {
+ if (checkbox && !checkbox.checked) {
+ src = Services.uriFixup.getFixupURIInfo(
+ src,
+ Ci.nsIURIFixup.FIXUP_FLAG_NONE
+ ).preferredURI.spec;
+ }
+ } catch (e) {}
+
+ globalElement.setAttribute("src", src);
+ }
+
+ let title = gDialog.titleInput.value.trim();
+ if (title) {
+ globalElement.setAttribute("title", title);
+ } else {
+ globalElement.removeAttribute("title");
+ }
+
+ // Force user to enter Alt text only if "Alternate text" radio is checked
+ // Don't allow just spaces in alt text
+ var alt = "";
+ var useAlt = gDialog.altTextRadioGroup.selectedItem == gDialog.altTextRadio;
+ if (useAlt) {
+ alt = TrimString(gDialog.altTextInput.value);
+ }
+
+ if (alt || !useAlt) {
+ globalElement.setAttribute("alt", alt);
+ } else if (!gDoAltTextError) {
+ globalElement.removeAttribute("alt");
+ } else {
+ Services.prompt.alert(window, GetString("Alert"), GetString("NoAltText"));
+ SwitchToValidatePanel();
+ gDialog.altTextInput.focus();
+ return false;
+ }
+
+ var width = "";
+ var height = "";
+
+ gValidateTab = gDialog.tabDimensions;
+ if (!gDialog.actualSizeRadio.selected) {
+ // Get user values for width and height
+ width = ValidateNumber(
+ gDialog.widthInput,
+ gDialog.widthUnitsMenulist,
+ 1,
+ gMaxPixels,
+ globalElement,
+ "width",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ height = ValidateNumber(
+ gDialog.heightInput,
+ gDialog.heightUnitsMenulist,
+ 1,
+ gMaxPixels,
+ globalElement,
+ "height",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+ }
+
+ // We always set the width and height attributes, even if same as actual.
+ // This speeds up layout of pages since sizes are known before image is loaded
+ if (!width) {
+ width = gActualWidth;
+ }
+ if (!height) {
+ height = gActualHeight;
+ }
+
+ // Remove existing width and height only if source changed
+ // and we couldn't obtain actual dimensions
+ var srcChanged = src != gOriginalSrc;
+ if (width) {
+ editor.setAttributeOrEquivalent(globalElement, "width", width, true);
+ } else if (srcChanged) {
+ editor.removeAttributeOrEquivalent(globalElement, "width", true);
+ }
+
+ if (height) {
+ editor.setAttributeOrEquivalent(globalElement, "height", height, true);
+ } else if (srcChanged) {
+ editor.removeAttributeOrEquivalent(globalElement, "height", true);
+ }
+
+ // spacing attributes
+ gValidateTab = gDialog.tabBorder;
+ ValidateNumber(
+ gDialog.imagelrInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "hspace",
+ false,
+ true,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ ValidateNumber(
+ gDialog.imagetbInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "vspace",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ // note this is deprecated and should be converted to stylesheets
+ ValidateNumber(
+ gDialog.border,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "border",
+ false,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ // Default or setting "bottom" means don't set the attribute
+ // Note that the attributes "left" and "right" are opposite
+ // of what we use in the UI, which describes where the TEXT wraps,
+ // not the image location (which is what the HTML describes)
+ switch (gDialog.alignTypeSelect.value) {
+ case "top":
+ case "middle":
+ case "right":
+ case "left":
+ editor.setAttributeOrEquivalent(
+ globalElement,
+ "align",
+ gDialog.alignTypeSelect.value,
+ true
+ );
+ break;
+ default:
+ try {
+ editor.removeAttributeOrEquivalent(globalElement, "align", true);
+ } catch (e) {}
+ }
+
+ return true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js b/comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js
new file mode 100755
index 0000000000..5b88a5703f
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageLinkLoader.js
@@ -0,0 +1,145 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var gMsgCompProcessLink = false;
+var gMsgCompInputElement = null;
+var gMsgCompPrevInputValue = null;
+var gMsgCompPrevMozDoNotSendAttribute;
+var gMsgCompAttachSourceElement = null;
+
+function OnLoadDialog() {
+ gMsgCompAttachSourceElement = document.getElementById("AttachSourceToMail");
+ var editor = GetCurrentEditor();
+ if (
+ gMsgCompAttachSourceElement &&
+ editor &&
+ editor.flags & Ci.nsIEditor.eEditorMailMask
+ ) {
+ SetRelativeCheckbox = function() {
+ SetAttachCheckbox();
+ };
+ // initialize the AttachSourceToMail checkbox
+ gMsgCompAttachSourceElement.hidden = false;
+
+ switch (document.documentElement.id) {
+ case "imageDlg":
+ gMsgCompInputElement = gDialog.srcInput;
+ gMsgCompProcessLink = false;
+ break;
+ case "linkDlg":
+ gMsgCompInputElement = gDialog.hrefInput;
+ gMsgCompProcessLink = true;
+ break;
+ }
+ if (gMsgCompInputElement) {
+ SetAttachCheckbox();
+ gMsgCompPrevMozDoNotSendAttribute = globalElement.getAttribute(
+ "moz-do-not-send"
+ );
+ }
+ }
+}
+addEventListener("load", OnLoadDialog, false);
+
+function OnAcceptDialog() {
+ // Auto-convert file URLs to data URLs. If we're in the link properties
+ // dialog convert only when requested - for the image dialog do it always.
+ if (gMsgCompInputElement &&
+ /^file:/i.test(gMsgCompInputElement.value.trim()) &&
+ (gMsgCompAttachSourceElement.checked || !gMsgCompProcessLink)) {
+ var dataURI = GenerateDataURL(gMsgCompInputElement.value.trim());
+ gMsgCompInputElement.value = dataURI;
+ gMsgCompAttachSourceElement.checked = true;
+ }
+ DoAttachSourceCheckbox();
+}
+document.addEventListener("dialogaccept", OnAcceptDialog, true);
+
+function SetAttachCheckbox() {
+ var resetCheckbox = false;
+ var mozDoNotSend = globalElement.getAttribute("moz-do-not-send");
+
+ // In case somebody played with the advanced property and changed the moz-do-not-send attribute
+ if (mozDoNotSend != gMsgCompPrevMozDoNotSendAttribute) {
+ gMsgCompPrevMozDoNotSendAttribute = mozDoNotSend;
+ resetCheckbox = true;
+ }
+
+ // Has the URL changed
+ if (
+ gMsgCompInputElement &&
+ gMsgCompInputElement.value != gMsgCompPrevInputValue
+ ) {
+ gMsgCompPrevInputValue = gMsgCompInputElement.value;
+ resetCheckbox = true;
+ }
+
+ if (gMsgCompInputElement && resetCheckbox) {
+ // Here is the rule about how to set the checkbox Attach Source To Message:
+ // If the attribute "moz-do-not-send" has not been set, we look at the scheme of the URL
+ // and at some preference to decide what is the best for the user.
+ // If it is set to "false", the checkbox is checked, otherwise unchecked.
+ var attach = false;
+ if (mozDoNotSend == null) {
+ // We haven't yet set the "moz-do-not-send" attribute.
+ var inputValue = gMsgCompInputElement.value.trim();
+ if (/^(file|data):/i.test(inputValue)) {
+ // For files or data URLs, default to attach them.
+ attach = true;
+ } else if (
+ !gMsgCompProcessLink && // Implies image dialogue.
+ /^https?:/i.test(inputValue)
+ ) {
+ // For images loaded via http(s) we default to the preference value.
+ attach = Services.prefs.getBoolPref("mail.compose.attach_http_images");
+ }
+ } else {
+ attach = mozDoNotSend == "false";
+ }
+
+ gMsgCompAttachSourceElement.checked = attach;
+ }
+}
+
+function DoAttachSourceCheckbox() {
+ gMsgCompPrevMozDoNotSendAttribute = (!gMsgCompAttachSourceElement.checked).toString();
+ globalElement.setAttribute(
+ "moz-do-not-send",
+ gMsgCompPrevMozDoNotSendAttribute
+ );
+}
+
+function GenerateDataURL(url) {
+ var file = Services.io.newURI(url).QueryInterface(Ci.nsIFileURL).file;
+ var contentType = Cc["@mozilla.org/mime;1"]
+ .getService(Ci.nsIMIMEService)
+ .getTypeFromFile(file);
+ var inputStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ inputStream.init(file, 0x01, 0o600, 0);
+ var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIBinaryInputStream
+ );
+ stream.setInputStream(inputStream);
+ let data = "";
+ while (stream.available() > 0) {
+ data += stream.readBytes(stream.available());
+ }
+ let encoded = btoa(data);
+ stream.close();
+ return (
+ "data:" +
+ contentType +
+ ";filename=" +
+ encodeURIComponent(file.leafName) +
+ ";base64," +
+ encoded
+ );
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdImageProps.js b/comm/suite/editor/components/dialogs/content/EdImageProps.js
new file mode 100644
index 0000000000..5b73488dcb
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageProps.js
@@ -0,0 +1,293 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+/* import-globals-from EdImageDialog.js */
+
+var gAnchorElement = null;
+var gLinkElement = null;
+var gOriginalHref = "";
+var gHNodeArray = {};
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ ImageStartup();
+ gDialog.hrefInput = document.getElementById("hrefInput");
+ gDialog.makeRelativeLink = document.getElementById("MakeRelativeLink");
+ gDialog.showLinkBorder = document.getElementById("showLinkBorder");
+ gDialog.linkTab = document.getElementById("imageLinkTab");
+ gDialog.linkAdvanced = document.getElementById("LinkAdvancedEditButton");
+
+ // Get a single selected image element
+ var tagName = "img";
+ if ("arguments" in window && window.arguments[0]) {
+ imageElement = window.arguments[0];
+ // We've been called from form field properties, so we can't insert a link
+ gDialog.linkTab.remove();
+ gDialog.linkTab = null;
+ } else {
+ // First check for <input type="image">
+ try {
+ imageElement = editor.getSelectedElement("input");
+
+ if (!imageElement || imageElement.getAttribute("type") != "image") {
+ // Get a single selected image element
+ imageElement = editor.getSelectedElement(tagName);
+ if (imageElement) {
+ gAnchorElement = editor.getElementOrParentByTagName(
+ "href",
+ imageElement
+ );
+ }
+ }
+ } catch (e) {}
+ }
+
+ if (imageElement) {
+ // We found an element and don't need to insert one
+ if (imageElement.hasAttribute("src")) {
+ gInsertNewImage = false;
+ gActualWidth = imageElement.naturalWidth;
+ gActualHeight = imageElement.naturalHeight;
+ }
+ } else {
+ gInsertNewImage = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ imageElement = editor.createElementWithDefaults(tagName);
+ } catch (e) {}
+
+ if (!imageElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ try {
+ gAnchorElement = editor.getSelectedElement("href");
+ } catch (e) {}
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = imageElement.cloneNode(false);
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ InitDialog();
+ if (gAnchorElement) {
+ gOriginalHref = gAnchorElement.getAttribute("href");
+ // Make a copy to use for AdvancedEdit
+ gLinkElement = gAnchorElement.cloneNode(false);
+ } else {
+ gLinkElement = editor.createElementWithDefaults("a");
+ }
+ gDialog.hrefInput.value = gOriginalHref;
+
+ FillLinkMenulist(gDialog.hrefInput, gHNodeArray);
+ ChangeLinkLocation();
+
+ // Save initial source URL
+ gOriginalSrc = gDialog.srcInput.value;
+
+ // By default turn constrain on, but both width and height must be in pixels
+ gDialog.constrainCheckbox.checked =
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0;
+
+ // Start in "Link" tab if 2nd argument is true
+ if (gDialog.linkTab && "arguments" in window && window.arguments[1]) {
+ document.getElementById("TabBox").selectedTab = gDialog.linkTab;
+ SetTextboxFocus(gDialog.hrefInput);
+ } else {
+ SetTextboxFocus(gDialog.srcInput);
+ }
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog() {
+ InitImage();
+ var border = TrimString(gDialog.border.value);
+ gDialog.showLinkBorder.checked = border != "" && border > 0;
+}
+
+function ChangeLinkLocation() {
+ var href = TrimString(gDialog.hrefInput.value);
+ SetRelativeCheckbox(gDialog.makeRelativeLink);
+ gDialog.showLinkBorder.disabled = !href;
+ gDialog.linkAdvanced.disabled = !href;
+ gLinkElement.setAttribute("href", href);
+}
+
+function ToggleShowLinkBorder() {
+ if (gDialog.showLinkBorder.checked) {
+ var border = TrimString(gDialog.border.value);
+ if (!border || border == "0") {
+ gDialog.border.value = "2";
+ }
+ } else {
+ gDialog.border.value = "0";
+ }
+}
+
+// Get data from widgets, validate, and set for the global element
+// accessible to AdvancedEdit() [in EdDialogCommon.js]
+function ValidateData() {
+ return ValidateImage();
+}
+
+function onAccept(event) {
+ // Use this now (default = false) so Advanced Edit button dialog doesn't trigger error message
+ gDoAltTextError = true;
+
+ if (ValidateData()) {
+ if ("arguments" in window && window.arguments[0]) {
+ SaveWindowLocation();
+ return;
+ }
+
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ try {
+ if (gRemoveImageMap) {
+ globalElement.removeAttribute("usemap");
+ if (gImageMap) {
+ editor.deleteNode(gImageMap);
+ gInsertNewIMap = true;
+ gImageMap = null;
+ }
+ } else if (gImageMap) {
+ // un-comment to see that inserting image maps does not work!
+ /*
+ gImageMap = editor.createElementWithDefaults("map");
+ gImageMap.setAttribute("name", "testing");
+ var testArea = editor.createElementWithDefaults("area");
+ testArea.setAttribute("shape", "circle");
+ testArea.setAttribute("coords", "86,102,52");
+ testArea.setAttribute("href", "test");
+ gImageMap.appendChild(testArea);
+ */
+
+ // Assign to map if there is one
+ var mapName = gImageMap.getAttribute("name");
+ if (mapName != "") {
+ globalElement.setAttribute("usemap", "#" + mapName);
+ if (globalElement.getAttribute("border") == "") {
+ globalElement.setAttribute("border", 0);
+ }
+ }
+ }
+
+ // Create or remove the link as appropriate
+ var href = gDialog.hrefInput.value;
+ if (href != gOriginalHref) {
+ if (href && !gInsertNewImage) {
+ EditorSetTextProperty("a", "href", href);
+ // gAnchorElement is needed for cloning attributes later.
+ if (!gAnchorElement) {
+ gAnchorElement = editor.getElementOrParentByTagName(
+ "href",
+ imageElement
+ );
+ }
+ } else {
+ EditorRemoveTextProperty("href", "");
+ }
+ }
+
+ // If inside a link, always write the 'border' attribute
+ if (href) {
+ if (gDialog.showLinkBorder.checked) {
+ // Use default = 2 if border attribute is empty
+ if (!globalElement.hasAttribute("border")) {
+ globalElement.setAttribute("border", "2");
+ }
+ } else {
+ globalElement.setAttribute("border", "0");
+ }
+ }
+
+ if (gInsertNewImage) {
+ if (href) {
+ gLinkElement.appendChild(imageElement);
+ editor.insertElementAtSelection(gLinkElement, true);
+ } else {
+ // 'true' means delete the selection before inserting
+ editor.insertElementAtSelection(imageElement, true);
+ }
+ }
+
+ // Check to see if the link was to a heading
+ // Do this last because it moves the caret (BAD!)
+ if (href in gHNodeArray) {
+ var anchorNode = editor.createElementWithDefaults("a");
+ if (anchorNode) {
+ anchorNode.name = href.substr(1);
+ // Remember to use editor method so it is undoable!
+ editor.insertNode(anchorNode, gHNodeArray[href], 0);
+ }
+ }
+ // All values are valid - copy to actual element in doc or
+ // element we just inserted
+ editor.cloneAttributes(imageElement, globalElement);
+ if (gAnchorElement) {
+ editor.cloneAttributes(gAnchorElement, gLinkElement);
+ }
+
+ // If document is empty, the map element won't insert,
+ // so always insert the image first
+ if (gImageMap && gInsertNewIMap) {
+ // Insert the ImageMap element at beginning of document
+ var body = editor.rootElement;
+ editor.setShouldTxnSetSelection(false);
+ editor.insertNode(gImageMap, body, 0);
+ editor.setShouldTxnSetSelection(true);
+ }
+ } catch (e) {
+ dump(e);
+ }
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+ return;
+ }
+
+ gDoAltTextError = false;
+
+ event.preventDefault();
+}
+
+function onLinkAdvancedEdit() {
+ window.AdvancedEditOK = false;
+ window.openDialog(
+ "chrome://editor/content/EdAdvancedEdit.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal,resizable=yes",
+ "",
+ gLinkElement
+ );
+ window.focus();
+ if (window.AdvancedEditOK) {
+ gDialog.hrefInput.value = gLinkElement.getAttribute("href");
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdImageProps.xhtml b/comm/suite/editor/components/dialogs/content/EdImageProps.xhtml
new file mode 100644
index 0000000000..11d7d03026
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdImageProps.xhtml
@@ -0,0 +1,116 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edImageProperties SYSTEM "chrome://editor/locale/EditorImageProperties.dtd">
+%edImageProperties;
+<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd">
+%composeEditorOverlayDTD;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<!-- dialog containing a control requiring initial setup -->
+<dialog id="imageDlg" title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()"
+ buttons="accept,cancel">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdImageProps.js"/>
+ <script src="chrome://editor/content/EdImageDialog.js"/>
+ <script src="chrome://editor/content/EdImageLinkLoader.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <tabbox id="TabBox">
+ <tabs flex="1">
+ <tab id="imageLocationTab" label="&imageLocationTab.label;"/>
+ <tab id="imageDimensionsTab" label="&imageDimensionsTab.label;"/>
+ <tab id="imageAppearanceTab" label="&imageAppearanceTab.label;"/>
+ <tab id="imageLinkTab" label="&imageLinkTab.label;"/>
+ </tabs>
+ <tabpanels>
+#include edImage.inc.xhtml
+ <vbox>
+ <spacer class="spacer"/>
+ <vbox id="LinkLocationBox">
+ <label control="hrefInput"
+ accesskey="&LinkURLEditField2.accessKey;"
+ width="1">&LinkURLEditField2.label;</label>
+ <textbox id="hrefInput" type="text"
+ class="uri-element padded" oninput="ChangeLinkLocation();"/>
+ <hbox align="center">
+ <checkbox id="MakeRelativeLink"
+ for="hrefInput"
+ label="&makeUrlRelative.label;"
+ accesskey="&makeUrlRelative.accessKey;"
+ oncommand="MakeInputValueRelativeOrAbsolute(this);"
+ tooltiptext="&makeUrlRelative.tooltip;"/>
+ <spacer flex="1"/>
+ <button label="&chooseFileLinkButton.label;" accesskey="&chooseFileLinkButton.accessKey;"
+ oncommand="chooseLinkFile();"/>
+ </hbox>
+ </vbox>
+ <spacer class="spacer"/>
+ <hbox>
+ <checkbox id="showLinkBorder"
+ label="&showImageLinkBorder.label;"
+ accesskey="&showImageLinkBorder.accessKey;"
+ oncommand="ToggleShowLinkBorder();"/>
+ <spacer flex="1"/>
+ <button id="LinkAdvancedEditButton"
+ label="&LinkAdvancedEditButton.label;"
+ accesskey="&LinkAdvancedEditButton.accessKey;"
+ tooltiptext="&LinkAdvancedEditButton.tooltip;"
+ oncommand="onLinkAdvancedEdit();"/>
+ </hbox>
+ </vbox>
+ </tabpanels>
+ </tabbox>
+
+ <hbox align="end">
+ <groupbox id="imagePreview" orient="horizontal" flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&previewBox.label;</label>
+ </hbox>
+ <hbox id="preview-image-box" align="center">
+ <spacer flex="1"/>
+ <description id="preview-image-holder"/>
+ <spacer flex="1"/>
+ </hbox>
+ <vbox id="PreviewSize" collapsed="true">
+ <spacer flex="1"/>
+ <label value="&actualSize.label;"/>
+ <hbox>
+ <label value="&widthEditField.label;"/>
+ <spacer flex="1"/>
+ <label id="PreviewWidth"/>
+ </hbox>
+ <hbox>
+ <label value="&heightEditField.label;"/>
+ <spacer flex="1"/>
+ <label id="PreviewHeight"/>
+ </hbox>
+ <spacer flex="1"/>
+ </vbox>
+ </groupbox>
+
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ </vbox>
+ </hbox>
+ <separator class="groove"/>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInputImage.js b/comm/suite/editor/components/dialogs/content/EdInputImage.js
new file mode 100644
index 0000000000..556acc7b13
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputImage.js
@@ -0,0 +1,189 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+/* import-globals-from EdImageDialog.js */
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ inputName: document.getElementById("InputName"),
+ inputDisabled: document.getElementById("InputDisabled"),
+ inputTabIndex: document.getElementById("InputTabIndex"),
+ };
+
+ ImageStartup();
+
+ // Get a single selected input element
+ var tagName = "input";
+ try {
+ imageElement = editor.getSelectedElement(tagName);
+ } catch (e) {}
+
+ if (imageElement) {
+ // We found an element and don't need to insert one
+ gInsertNewImage = false;
+ } else {
+ gInsertNewImage = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ imageElement = editor.createElementWithDefaults(tagName);
+ } catch (e) {}
+
+ if (!imageElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ var imgElement;
+ try {
+ imgElement = editor.getSelectedElement("img");
+ } catch (e) {}
+
+ if (imgElement) {
+ // We found an image element, convert it to an input type="image"
+ var attributes = [
+ "src",
+ "alt",
+ "width",
+ "height",
+ "hspace",
+ "vspace",
+ "border",
+ "align",
+ "usemap",
+ "ismap",
+ ];
+ for (let i in attributes) {
+ imageElement.setAttribute(
+ attributes[i],
+ imgElement.getAttribute(attributes[i])
+ );
+ }
+ }
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = imageElement.cloneNode(false);
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ InitDialog();
+
+ // Save initial source URL
+ gOriginalSrc = gDialog.srcInput.value;
+
+ // By default turn constrain on, but both width and height must be in pixels
+ gDialog.constrainCheckbox.checked =
+ gDialog.widthUnitsMenulist.selectedIndex == 0 &&
+ gDialog.heightUnitsMenulist.selectedIndex == 0;
+
+ SetTextboxFocus(gDialog.inputName);
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ InitImage();
+ gDialog.inputName.value = globalElement.getAttribute("name");
+ gDialog.inputDisabled.setAttribute(
+ "checked",
+ globalElement.hasAttribute("disabled")
+ );
+ gDialog.inputTabIndex.value = globalElement.getAttribute("tabindex");
+}
+
+function ValidateData() {
+ if (!ValidateImage()) {
+ return false;
+ }
+ if (gDialog.inputName.value) {
+ globalElement.setAttribute("name", gDialog.inputName.value);
+ } else {
+ globalElement.removeAttribute("name");
+ }
+ if (gDialog.inputTabIndex.value) {
+ globalElement.setAttribute("tabindex", gDialog.inputTabIndex.value);
+ } else {
+ globalElement.removeAttribute("tabindex");
+ }
+ if (gDialog.inputDisabled.checked) {
+ globalElement.setAttribute("disabled", "");
+ } else {
+ globalElement.removeAttribute("disabled");
+ }
+ globalElement.setAttribute("type", "image");
+ return true;
+}
+
+function onAccept(event) {
+ // Show alt text error only once
+ // (we don't initialize doAltTextError=true
+ // so Advanced edit button dialog doesn't trigger that error message)
+ // Use this now (default = false) so Advanced Edit button dialog doesn't trigger error message
+ gDoAltTextError = true;
+
+ if (ValidateData()) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ if (gRemoveImageMap) {
+ globalElement.removeAttribute("usemap");
+ if (gImageMap) {
+ editor.deleteNode(gImageMap);
+ gInsertNewIMap = true;
+ gImageMap = null;
+ }
+ } else if (gImageMap) {
+ // Assign to map if there is one
+ var mapName = gImageMap.getAttribute("name");
+ if (mapName != "") {
+ globalElement.setAttribute("usemap", "#" + mapName);
+ if (globalElement.getAttribute("border") == "") {
+ globalElement.setAttribute("border", 0);
+ }
+ }
+ }
+
+ if (gInsertNewImage) {
+ // 'true' means delete the selection before inserting
+ // in case were are converting an image to an input type="image"
+ editor.insertElementAtSelection(imageElement, true);
+ }
+ editor.cloneAttributes(imageElement, globalElement);
+
+ // If document is empty, the map element won't insert,
+ // so always insert the image element first
+ if (gImageMap && gInsertNewIMap) {
+ // Insert the ImageMap element at beginning of document
+ var body = editor.rootElement;
+ editor.setShouldTxnSetSelection(false);
+ editor.insertNode(gImageMap, body, 0);
+ editor.setShouldTxnSetSelection(true);
+ }
+ } catch (e) {}
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInputImage.xhtml b/comm/suite/editor/components/dialogs/content/EdInputImage.xhtml
new file mode 100644
index 0000000000..d3fc8c8270
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputImage.xhtml
@@ -0,0 +1,104 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edInputProperties SYSTEM "chrome://editor/locale/EditorInputProperties.dtd">
+%edInputProperties;
+<!ENTITY % edImageProperties SYSTEM "chrome://editor/locale/EditorImageProperties.dtd">
+%edImageProperties;
+<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd">
+%composeEditorOverlayDTD;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitleImage.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInputImage.js"/>
+ <script src="chrome://editor/content/EdImageDialog.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <tabbox id="TabBox">
+ <tabs flex="1">
+ <tab id="imageInputTab" label="&imageInputTab.label;"/>
+ <tab id="imageLocationTab" label="&imageLocationTab.label;"/>
+ <tab id="imageDimensionsTab" label="&imageDimensionsTab.label;"/>
+ <tab id="imageAppearanceTab" label="&imageAppearanceTab.label;"/>
+ </tabs>
+ <tabpanels>
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&InputSettings.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label value="&InputName.label;"/>
+ <textbox id="InputName"/>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="InputDisabled" label="&InputDisabled.label;"/>
+ </row>
+ <row align="center">
+ <label value="&tabIndex.label;"/>
+ <hbox>
+ <textbox id="InputTabIndex" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+#include edImage.inc.xhtml
+ </tabpanels>
+ </tabbox>
+
+ <hbox align="end">
+ <groupbox id="imagePreview" orient="horizontal" flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&previewBox.label;</label>
+ </hbox>
+ <hbox id="preview-image-box" align="center">
+ <spacer flex="1"/>
+ <description id="preview-image-holder"/>
+ <spacer flex="1"/>
+ </hbox>
+ <vbox id="PreviewSize" collapsed="true">
+ <spacer flex="1"/>
+ <label value="&actualSize.label;"/>
+ <hbox>
+ <label value="&widthEditField.label;"/>
+ <spacer flex="1"/>
+ <label id="PreviewWidth"/>
+ </hbox>
+ <hbox>
+ <label value="&heightEditField.label;"/>
+ <spacer flex="1"/>
+ <label id="PreviewHeight"/>
+ </hbox>
+ <spacer flex="1"/>
+ </vbox>
+ </groupbox>
+
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator id="advancedSeparator" class="groove"/>
+ </vbox>
+ </hbox>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInputProps.js b/comm/suite/editor/components/dialogs/content/EdInputProps.js
new file mode 100644
index 0000000000..a737e263c7
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputProps.js
@@ -0,0 +1,345 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var inputElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ accept: document.documentElement.getButton("accept"),
+ inputType: document.getElementById("InputType"),
+ inputNameDeck: document.getElementById("InputNameDeck"),
+ inputName: document.getElementById("InputName"),
+ inputValueDeck: document.getElementById("InputValueDeck"),
+ inputValue: document.getElementById("InputValue"),
+ inputDeck: document.getElementById("InputDeck"),
+ inputChecked: document.getElementById("InputChecked"),
+ inputSelected: document.getElementById("InputSelected"),
+ inputReadOnly: document.getElementById("InputReadOnly"),
+ inputDisabled: document.getElementById("InputDisabled"),
+ inputTabIndex: document.getElementById("InputTabIndex"),
+ inputAccessKey: document.getElementById("InputAccessKey"),
+ inputSize: document.getElementById("InputSize"),
+ inputMaxLength: document.getElementById("InputMaxLength"),
+ inputAccept: document.getElementById("InputAccept"),
+ MoreSection: document.getElementById("MoreSection"),
+ MoreFewerButton: document.getElementById("MoreFewerButton"),
+ AdvancedEditButton: document.getElementById("AdvancedEditButton"),
+ AdvancedEditDeck: document.getElementById("AdvancedEditDeck"),
+ };
+
+ // Get a single selected input element
+ const kTagName = "input";
+ try {
+ inputElement = editor.getSelectedElement(kTagName);
+ } catch (e) {}
+
+ if (inputElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ inputElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!inputElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+
+ var imgElement = editor.getSelectedElement("img");
+ if (imgElement) {
+ // We found an image element, convert it to an input type="image"
+ inputElement.setAttribute("type", "image");
+
+ var attributes = [
+ "src",
+ "alt",
+ "width",
+ "height",
+ "hspace",
+ "vspace",
+ "border",
+ "align",
+ ];
+ for (let i in attributes) {
+ inputElement.setAttribute(
+ attributes[i],
+ imgElement.getAttribute(attributes[i])
+ );
+ }
+ } else {
+ inputElement.setAttribute("value", GetSelectionAsText());
+ }
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = inputElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ gDialog.inputType.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ var type = globalElement.getAttribute("type");
+ var index = 0;
+ switch (type) {
+ case "button":
+ index = 9;
+ break;
+ case "checkbox":
+ index = 2;
+ break;
+ case "file":
+ index = 6;
+ break;
+ case "hidden":
+ index = 7;
+ break;
+ case "image":
+ index = 8;
+ break;
+ case "password":
+ index = 1;
+ break;
+ case "radio":
+ index = 3;
+ break;
+ case "reset":
+ index = 5;
+ break;
+ case "submit":
+ index = 4;
+ break;
+ }
+ gDialog.inputType.selectedIndex = index;
+ gDialog.inputName.value = globalElement.getAttribute("name");
+ gDialog.inputValue.value = globalElement.getAttribute("value");
+ gDialog.inputChecked.setAttribute(
+ "checked",
+ globalElement.hasAttribute("checked")
+ );
+ gDialog.inputSelected.setAttribute(
+ "checked",
+ globalElement.hasAttribute("checked")
+ );
+ gDialog.inputReadOnly.setAttribute(
+ "checked",
+ globalElement.hasAttribute("readonly")
+ );
+ gDialog.inputDisabled.setAttribute(
+ "checked",
+ globalElement.hasAttribute("disabled")
+ );
+ gDialog.inputTabIndex.value = globalElement.getAttribute("tabindex");
+ gDialog.inputAccessKey.value = globalElement.getAttribute("accesskey");
+ gDialog.inputSize.value = globalElement.getAttribute("size");
+ gDialog.inputMaxLength.value = globalElement.getAttribute("maxlength");
+ gDialog.inputAccept.value = globalElement.getAttribute("accept");
+ SelectInputType();
+}
+
+function SelectInputType() {
+ var index = gDialog.inputType.selectedIndex;
+ gDialog.AdvancedEditDeck.setAttribute("selectedIndex", 0);
+ gDialog.inputNameDeck.setAttribute("selectedIndex", 0);
+ gDialog.inputValueDeck.setAttribute("selectedIndex", 0);
+ gDialog.inputValue.disabled = false;
+ gDialog.inputChecked.disabled = index != 2;
+ gDialog.inputSelected.disabled = index != 3;
+ gDialog.inputReadOnly.disabled = index > 1;
+ gDialog.inputTabIndex.disabled = index == 7;
+ gDialog.inputAccessKey.disabled = index == 7;
+ gDialog.inputSize.disabled = index > 1;
+ gDialog.inputMaxLength.disabled = index > 1;
+ gDialog.inputAccept.disabled = index != 6;
+ switch (index) {
+ case 0:
+ case 1:
+ gDialog.inputValueDeck.setAttribute("selectedIndex", 1);
+ gDialog.inputDeck.setAttribute("selectedIndex", 2);
+ break;
+ case 2:
+ gDialog.inputDeck.setAttribute("selectedIndex", 0);
+ break;
+ case 3:
+ gDialog.inputDeck.setAttribute("selectedIndex", 1);
+ gDialog.inputNameDeck.setAttribute("selectedIndex", 1);
+ break;
+ case 6:
+ gDialog.inputValue.disabled = true;
+ gDialog.inputAccept.disabled = false;
+ break;
+ case 8:
+ gDialog.inputValue.disabled = true;
+ gDialog.AdvancedEditDeck.setAttribute("selectedIndex", 1);
+ gDialog.inputName.removeEventListener("input", onInput);
+ break;
+ case 7:
+ gDialog.inputValueDeck.setAttribute("selectedIndex", 1);
+ break;
+ }
+ onInput();
+}
+
+function onInput() {
+ var disabled = false;
+ switch (gDialog.inputType.selectedIndex) {
+ case 3:
+ disabled = disabled || !gDialog.inputValue.value;
+ break;
+ case 4:
+ case 5:
+ break;
+ case 8:
+ disabled = !globalElement.hasAttribute("src");
+ break;
+ default:
+ disabled = !gDialog.inputName.value;
+ break;
+ }
+ if (gDialog.accept.disabled != disabled) {
+ gDialog.accept.disabled = disabled;
+ gDialog.AdvancedEditButton.disabled = disabled;
+ }
+}
+
+function doImageProperties() {
+ window.openDialog(
+ "chrome://editor/content/EdImageProps.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ globalElement
+ );
+ window.focus();
+ onInput();
+}
+
+function ValidateData() {
+ var attributes = {
+ type: "",
+ name: gDialog.inputName.value,
+ value: gDialog.inputValue.value,
+ tabindex: gDialog.inputTabIndex.value,
+ accesskey: "",
+ size: "",
+ maxlength: "",
+ accept: "",
+ };
+ var index = gDialog.inputType.selectedIndex;
+ var flags = {
+ checked: false,
+ readonly: false,
+ disabled: gDialog.inputDisabled.checked,
+ };
+ switch (index) {
+ case 1:
+ attributes.type = "password";
+ // Falls through
+ case 0:
+ flags.readonly = gDialog.inputReadOnly.checked;
+ attributes.size = gDialog.inputSize.value;
+ attributes.maxlength = gDialog.inputMaxLength.value;
+ break;
+ case 2:
+ attributes.type = "checkbox";
+ flags.checked = gDialog.inputChecked.checked;
+ break;
+ case 3:
+ attributes.type = "radio";
+ flags.checked = gDialog.inputSelected.checked;
+ break;
+ case 4:
+ attributes.type = "submit";
+ attributes.accesskey = gDialog.inputAccessKey.value;
+ break;
+ case 5:
+ attributes.type = "reset";
+ attributes.accesskey = gDialog.inputAccessKey.value;
+ break;
+ case 6:
+ attributes.type = "file";
+ attributes.accept = gDialog.inputAccept.value;
+ attributes.value = "";
+ break;
+ case 7:
+ attributes.type = "hidden";
+ attributes.tabindex = "";
+ break;
+ case 8:
+ attributes.type = "image";
+ attributes.value = "";
+ break;
+ case 9:
+ attributes.type = "button";
+ attributes.accesskey = gDialog.inputAccessKey.value;
+ break;
+ }
+ for (var a in attributes) {
+ if (attributes[a]) {
+ globalElement.setAttribute(a, attributes[a]);
+ } else {
+ globalElement.removeAttribute(a);
+ }
+ }
+ for (var f in flags) {
+ if (flags[f]) {
+ globalElement.setAttribute(f, "");
+ } else {
+ globalElement.removeAttribute(f);
+ }
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+
+ var editor = GetCurrentEditor();
+
+ editor.cloneAttributes(inputElement, globalElement);
+
+ if (insertNew) {
+ try {
+ // 'true' means delete the selection before inserting
+ // in case were are converting an image to an input type="image"
+ editor.insertElementAtSelection(inputElement, true);
+ } catch (e) {
+ dump(e);
+ }
+ }
+
+ SaveWindowLocation();
+
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInputProps.xhtml b/comm/suite/editor/components/dialogs/content/EdInputProps.xhtml
new file mode 100644
index 0000000000..c6011ee896
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInputProps.xhtml
@@ -0,0 +1,135 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edInputProperties SYSTEM "chrome://editor/locale/EditorInputProperties.dtd">
+%edInputProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInputProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header" control="InputType" accesskey="&InputType.accesskey;">&InputType.label;</label>
+ </hbox>
+ <menulist id="InputType" oncommand="SelectInputType();">
+ <menupopup>
+ <menuitem label="&text.value;"/>
+ <menuitem label="&password.value;"/>
+ <menuitem label="&checkbox.value;"/>
+ <menuitem label="&radio.value;"/>
+ <menuitem label="&submit.value;"/>
+ <menuitem label="&reset.value;"/>
+ <menuitem label="&file.value;"/>
+ <menuitem label="&hidden.value;"/>
+ <menuitem label="&image.value;"/>
+ <menuitem label="&button.value;"/>
+ </menupopup>
+ </menulist>
+ </groupbox>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&InputSettings.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <deck id="InputNameDeck">
+ <label control="InputName" value="&InputName.label;" accesskey="&InputName.accesskey;"/>
+ <label control="InputName" value="&GroupName.label;" accesskey="&GroupName.accesskey;"/>
+ </deck>
+ <textbox id="InputName" oninput="onInput();"/>
+ </row>
+ <row align="center">
+ <deck id="InputValueDeck">
+ <label control="InputValue" value="&InputValue.label;" accesskey="&InputValue.accesskey;"/>
+ <label control="InputValue" value="&InitialValue.label;" accesskey="&InitialValue.accesskey;"/>
+ </deck>
+ <textbox id="InputValue" oninput="onInput();"/>
+ </row>
+ <row>
+ <spacer/>
+ <deck id="InputDeck" persist="index">
+ <checkbox id="InputChecked" label="&InputChecked.label;" accesskey="&InputChecked.accesskey;"/>
+ <checkbox id="InputSelected" label="&InputSelected.label;" accesskey="&InputSelected.accesskey;"/>
+ <checkbox id="InputReadOnly" label="&InputReadOnly.label;" accesskey="&InputReadOnly.accesskey;"/>
+ </deck>
+ </row>
+ </rows>
+ </grid>
+ <hbox>
+ <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/>
+ </hbox>
+ <grid id="MoreSection" align="start">
+ <columns><column/><column/></columns>
+ <rows>
+ <row>
+ <spacer/>
+ <checkbox id="InputDisabled" label="&InputDisabled.label;" accesskey="&InputDisabled.accesskey;"/>
+ </row>
+ <row align="center">
+ <label control="InputTabIndex" value="&tabIndex.label;" accesskey="&tabIndex.accesskey;"/>
+ <hbox>
+ <textbox id="InputTabIndex" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="InputAccessKey" value="&AccessKey.label;" accesskey="&AccessKey.accesskey;"/>
+ <hbox>
+ <textbox id="InputAccessKey" class="narrow"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="InputSize" value="&TextSize.label;" accesskey="&TextSize.accesskey;"/>
+ <hbox>
+ <textbox id="InputSize" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="InputMaxLength" value="&TextLength.label;" accesskey="&TextLength.accesskey;"/>
+ <hbox>
+ <textbox id="InputMaxLength" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="InputAccept" value="&Accept.label;" accesskey="&Accept.accesskey;"/>
+ <textbox id="InputAccept"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- from EdDialogOverlay -->
+ <hbox flex="1" style="margin-top: 0.2em">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <deck id="AdvancedEditDeck">
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ <button label="&ImageProperties.label;" accesskey="&ImageProperties.accesskey;" oncommand="doImageProperties();"/>
+ </deck>
+ </hbox>
+ <separator class="groove"/>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInsSrc.js b/comm/suite/editor/components/dialogs/content/EdInsSrc.js
new file mode 100644
index 0000000000..0f0304ef1e
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsSrc.js
@@ -0,0 +1,160 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/* Insert Source HTML dialog */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gFullDataStrings = new Map();
+var gShortDataStrings = new Map();
+var gListenerAttached = false;
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ let editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ document.documentElement.getButton("accept").removeAttribute("default");
+
+ // Create dialog object to store controls for easy access
+ gDialog.srcInput = document.getElementById("srcInput");
+
+ // Attach a paste listener so we can detect pasted data URIs we need to shorten.
+ gDialog.srcInput.addEventListener("paste", onPaste);
+
+ let selection;
+ try {
+ selection = editor.outputToString(
+ "text/html",
+ kOutputFormatted | kOutputSelectionOnly | kOutputWrap
+ );
+ } catch (e) {}
+ if (selection) {
+ selection = selection.replace(/<body[^>]*>/, "").replace(/<\/body>/, "");
+
+ // Shorten data URIs for display.
+ selection = replaceDataURIs(selection);
+
+ if (selection) {
+ gDialog.srcInput.value = selection;
+ }
+ }
+ // Set initial focus
+ gDialog.srcInput.focus();
+ SetWindowLocation();
+}
+
+function replaceDataURIs(input) {
+ return input.replace(/(data:.+;base64,)([^"' >]+)/gi, function(
+ match,
+ nonDataPart,
+ dataPart
+ ) {
+ if (gShortDataStrings.has(dataPart)) {
+ // We found the exact same data URI, just return the shortened URI.
+ return nonDataPart + gShortDataStrings.get(dataPart);
+ }
+
+ let l = 5;
+ let key;
+ // Normally we insert the ellipsis after five characters but if it's not unique
+ // we include more data.
+ do {
+ key = dataPart.substr(0, l) + "…" + dataPart.substr(dataPart.length - 10);
+ l++;
+ } while (gFullDataStrings.has(key) && l < dataPart.length - 10);
+ gFullDataStrings.set(key, dataPart);
+ gShortDataStrings.set(dataPart, key);
+
+ // Attach listeners. In case anyone copies/cuts from the HTML window,
+ // we want to restore the data URI on the clipboard.
+ if (!gListenerAttached) {
+ gDialog.srcInput.addEventListener("copy", onCopyOrCut);
+ gDialog.srcInput.addEventListener("cut", onCopyOrCut);
+ gListenerAttached = true;
+ }
+
+ return nonDataPart + key;
+ });
+}
+
+function onCopyOrCut(event) {
+ let startPos = gDialog.srcInput.selectionStart;
+ if (startPos == undefined) {
+ return;
+ }
+ let endPos = gDialog.srcInput.selectionEnd;
+ let clipboard = gDialog.srcInput.value.substring(startPos, endPos);
+
+ // Add back the original data URIs we stashed away earlier.
+ clipboard = clipboard.replace(/(data:.+;base64,)([^"' >]+)/gi, function(
+ match,
+ nonDataPart,
+ key
+ ) {
+ if (!gFullDataStrings.has(key)) {
+ // User changed data URI.
+ return match;
+ }
+ return nonDataPart + gFullDataStrings.get(key);
+ });
+ event.clipboardData.setData("text/plain", clipboard);
+ if (event.type == "cut") {
+ // We have to cut the selection manually.
+ gDialog.srcInput.value =
+ gDialog.srcInput.value.substr(0, startPos) +
+ gDialog.srcInput.value.substr(endPos);
+ }
+ event.preventDefault();
+}
+
+function onPaste(event) {
+ let startPos = gDialog.srcInput.selectionStart;
+ if (startPos == undefined) {
+ return;
+ }
+ let endPos = gDialog.srcInput.selectionEnd;
+ let clipboard = event.clipboardData.getData("text/plain");
+
+ // We do out own paste by replacing the selection with the pre-processed
+ // clipboard data.
+ gDialog.srcInput.value =
+ gDialog.srcInput.value.substr(0, startPos) +
+ replaceDataURIs(clipboard) +
+ gDialog.srcInput.value.substr(endPos);
+ event.preventDefault();
+}
+
+function onAccept(event) {
+ let html = gDialog.srcInput.value;
+ if (!html) {
+ event.preventDefault();
+ return;
+ }
+
+ // Add back the original data URIs we stashed away earlier.
+ html = html.replace(/(data:.+;base64,)([^"' >]+)/gi, function(
+ match,
+ nonDataPart,
+ key
+ ) {
+ if (!gFullDataStrings.has(key)) {
+ // User changed data URI.
+ return match;
+ }
+ return nonDataPart + gFullDataStrings.get(key);
+ });
+
+ try {
+ GetCurrentEditor().insertHTML(html);
+ } catch (e) {}
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml b/comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml
new file mode 100644
index 0000000000..32b89ebefd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsSrc.xhtml
@@ -0,0 +1,42 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertSource.dtd">
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload = "Startup()"
+ buttonlabelaccept="&insertButton.label;"
+ buttonaccesskeyaccept="&insertButton.accesskey;">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInsSrc.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <label id="srcMessage" value="&sourceEditField.label;"/>
+ <vbox flex="1" style="width: 30em; height: 20em;">
+ <html:textarea id="srcInput" rows="18" flex="1"/>
+ </vbox>
+ <!-- Will this accept the embedded HTML tags? -->
+ <hbox>
+ <spacer class="bigspacer"/>
+ <label value="&example.label;"/>
+ <label class="bold" value="&exampleOpenTag.label;"/>
+ <label class="bold italic" value="&exampleText.label;"/>
+ <label class="bold" value="&exampleCloseTag.label;"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertChars.js b/comm/suite/editor/components/dialogs/content/EdInsertChars.js
new file mode 100644
index 0000000000..6ee88afcdd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertChars.js
@@ -0,0 +1,409 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// ------------------------------------------------------------------
+// From Unicode 3.0 Page 54. 3.11 Conjoining Jamo Behavior
+var SBase = 0xac00;
+var LBase = 0x1100;
+var VBase = 0x1161;
+var TBase = 0x11a7;
+var LCount = 19;
+var VCount = 21;
+var TCount = 28;
+var NCount = VCount * TCount;
+// End of Unicode 3.0
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onClose);
+
+// dialog initialization code
+function Startup() {
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+
+ StartupLatin();
+
+ // Set a variable on the opener window so we
+ // can track ownership of close this window with it
+ window.opener.InsertCharWindow = window;
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+function onAccept(event) {
+ // Insert the character
+ try {
+ GetCurrentEditor().insertText(LatinM.label);
+ } catch (e) {}
+
+ // Set persistent attributes to save
+ // which category, letter, and character modifier was used
+ CategoryGroup.setAttribute("category", category);
+ CategoryGroup.setAttribute("letter_index", indexL);
+ CategoryGroup.setAttribute("char_index", indexM);
+
+ // Don't close the dialog
+ event.preventDefault();
+}
+
+// Don't allow inserting in HTML Source Mode
+function onFocus() {
+ var enable = true;
+ if ("gEditorDisplayMode" in window.opener) {
+ enable = !window.opener.IsInHTMLSourceMode();
+ }
+
+ SetElementEnabled(document.documentElement.getButton("accept"), enable);
+}
+
+function onClose() {
+ window.opener.InsertCharWindow = null;
+ SaveWindowLocation();
+}
+
+// ------------------------------------------------------------------
+var LatinL;
+var LatinM;
+var LatinL_Label;
+var LatinM_Label;
+var indexL = 0;
+var indexM = 0;
+var indexM_AU = 0;
+var indexM_AL = 0;
+var indexM_U = 0;
+var indexM_L = 0;
+var indexM_S = 0;
+var LItems = 0;
+var category;
+var CategoryGroup;
+var initialize = true;
+
+function StartupLatin() {
+ LatinL = document.getElementById("LatinL");
+ LatinM = document.getElementById("LatinM");
+ LatinL_Label = document.getElementById("LatinL_Label");
+ LatinM_Label = document.getElementById("LatinM_Label");
+
+ var Symbol = document.getElementById("Symbol");
+ var AccentUpper = document.getElementById("AccentUpper");
+ var AccentLower = document.getElementById("AccentLower");
+ var Upper = document.getElementById("Upper");
+ var Lower = document.getElementById("Lower");
+ CategoryGroup = document.getElementById("CatGrp");
+
+ // Initialize which radio button is set from persistent attribute...
+ var category = CategoryGroup.getAttribute("category");
+
+ // ...as well as indexes into the letter and character lists
+ var index = Number(CategoryGroup.getAttribute("letter_index"));
+ if (index && index >= 0) {
+ indexL = index;
+ }
+ index = Number(CategoryGroup.getAttribute("char_index"));
+ if (index && index >= 0) {
+ indexM = index;
+ }
+
+ switch (category) {
+ case "AccentUpper": // Uppercase Diacritical
+ CategoryGroup.selectedItem = AccentUpper;
+ indexM_AU = indexM;
+ break;
+ case "AccentLower": // Lowercase Diacritical
+ CategoryGroup.selectedItem = AccentLower;
+ indexM_AL = indexM;
+ break;
+ case "Upper": // Uppercase w/o Diacritical
+ CategoryGroup.selectedItem = Upper;
+ indexM_U = indexM;
+ break;
+ case "Lower": // Lowercase w/o Diacritical
+ CategoryGroup.selectedItem = Lower;
+ indexM_L = indexM;
+ break;
+ default:
+ category = "Symbol";
+ CategoryGroup.selectedItem = Symbol;
+ indexM_S = indexM;
+ break;
+ }
+
+ ChangeCategory(category);
+ initialize = false;
+}
+
+function ChangeCategory(newCategory) {
+ if (category != newCategory || initialize) {
+ category = newCategory;
+ // Note: Must do L before M to set LatinL.selectedIndex
+ UpdateLatinL();
+ UpdateLatinM();
+ UpdateCharacter();
+ }
+}
+
+function SelectLatinLetter() {
+ if (LatinL.selectedIndex != indexL) {
+ indexL = LatinL.selectedIndex;
+ UpdateLatinM();
+ UpdateCharacter();
+ }
+}
+
+function SelectLatinModifier() {
+ if (LatinM.selectedIndex != indexM) {
+ indexM = LatinM.selectedIndex;
+ UpdateCharacter();
+ }
+}
+function DisableLatinL(disable) {
+ if (disable) {
+ LatinL_Label.setAttribute("disabled", "true");
+ LatinL.setAttribute("disabled", "true");
+ } else {
+ LatinL_Label.removeAttribute("disabled");
+ LatinL.removeAttribute("disabled");
+ }
+}
+
+function UpdateLatinL() {
+ LatinL.removeAllItems();
+ if (category == "AccentUpper" || category == "AccentLower") {
+ DisableLatinL(false);
+ // No Q or q
+ var alphabet =
+ category == "AccentUpper"
+ ? "ABCDEFGHIJKLMNOPRSTUVWXYZ"
+ : "abcdefghijklmnoprstuvwxyz";
+ for (var letter = 0; letter < alphabet.length; letter++) {
+ LatinL.appendItem(alphabet.charAt(letter));
+ }
+
+ LatinL.selectedIndex = indexL;
+ } else {
+ // Other categories don't hinge on a "letter"
+ DisableLatinL(true);
+ // Note: don't change the indexL so it can be used next time
+ }
+}
+
+function UpdateLatinM() {
+ LatinM.removeAllItems();
+ var i, accent;
+ switch (category) {
+ case "AccentUpper": // Uppercase Diacritical
+ accent = upper[indexL];
+ for (i = 0; i < accent.length; i++) {
+ LatinM.appendItem(accent.charAt(i));
+ }
+
+ if (indexM_AU < accent.length) {
+ indexM = indexM_AU;
+ } else {
+ indexM = accent.length - 1;
+ }
+ indexM_AU = indexM;
+ break;
+
+ case "AccentLower": // Lowercase Diacritical
+ accent = lower[indexL];
+ for (i = 0; i < accent.length; i++) {
+ LatinM.appendItem(accent.charAt(i));
+ }
+
+ if (indexM_AL < accent.length) {
+ indexM = indexM_AL;
+ } else {
+ indexM = lower[indexL].length - 1;
+ }
+ indexM_AL = indexM;
+ break;
+
+ case "Upper": // Uppercase w/o Diacritical
+ for (i = 0; i < otherupper.length; i++) {
+ LatinM.appendItem(otherupper.charAt(i));
+ }
+
+ if (indexM_U < otherupper.length) {
+ indexM = indexM_U;
+ } else {
+ indexM = otherupper.length - 1;
+ }
+ indexM_U = indexM;
+ break;
+
+ case "Lower": // Lowercase w/o Diacritical
+ for (i = 0; i < otherlower.length; i++) {
+ LatinM.appendItem(otherlower.charAt(i));
+ }
+
+ if (indexM_L < otherlower.length) {
+ indexM = indexM_L;
+ } else {
+ indexM = otherlower.length - 1;
+ }
+ indexM_L = indexM;
+ break;
+
+ case "Symbol": // Symbol
+ for (i = 0; i < symbol.length; i++) {
+ LatinM.appendItem(symbol.charAt(i));
+ }
+
+ if (indexM_S < symbol.length) {
+ indexM = indexM_S;
+ } else {
+ indexM = symbol.length - 1;
+ }
+ indexM_S = indexM;
+ break;
+ }
+ LatinM.selectedIndex = indexM;
+}
+
+function UpdateCharacter() {
+ indexM = LatinM.selectedIndex;
+
+ switch (category) {
+ case "AccentUpper": // Uppercase Diacritical
+ indexM_AU = indexM;
+ break;
+ case "AccentLower": // Lowercase Diacritical
+ indexM_AL = indexM;
+ break;
+ case "Upper": // Uppercase w/o Diacritical
+ indexM_U = indexM;
+ break;
+ case "Lower": // Lowercase w/o Diacritical
+ indexM_L = indexM;
+ break;
+ case "Symbol":
+ indexM_S = indexM;
+ break;
+ }
+ // dump("Letter Index="+indexL+", Character Index="+indexM+", Character = "+LatinM.label+"\n");
+}
+
+const upper = [
+ // A
+ "\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u0100\u0102\u0104\u01cd\u01de\u01de\u01e0\u01fa\u0200\u0202\u0226\u1e00\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6",
+ // B
+ "\u0181\u0182\u0184\u1e02\u1e04\u1e06",
+ // C
+ "\u00c7\u0106\u0108\u010a\u010c\u0187\u1e08",
+ // D
+ "\u010e\u0110\u0189\u018a\u1e0a\u1e0c\u1e0e\u1e10\u1e12",
+ // E
+ "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6",
+ // F
+ "\u1e1e",
+ // G
+ "\u011c\u011E\u0120\u0122\u01e4\u01e6\u01f4\u1e20",
+ // H
+ "\u0124\u0126\u021e\u1e22\u1e24\u1e26\u1e28\u1e2a",
+ // I
+ "\u00CC\u00CD\u00CE\u00CF\u0128\u012a\u012C\u012e\u0130\u0208\u020a\u1e2c\u1e2e\u1ec8\u1eca",
+ // J
+ "\u0134\u01f0",
+ // K
+ "\u0136\u0198\u01e8\u1e30\u1e32\u1e34",
+ // L
+ "\u0139\u013B\u013D\u013F\u0141\u1e36\u1e38\u1e3a\u1e3c",
+ // M
+ "\u1e3e\u1e40\u1e42",
+ // N
+ "\u00D1\u0143\u0145\u0147\u014A\u01F8\u1e44\u1e46\u1e48\u1e4a",
+ // O
+ "\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u01ea\u01ec\u020c\u020e\u022A\u022C\u022E\u0230\u1e4c\u1e4e\u1e50\u1e52\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2",
+ // P
+ "\u1e54\u1e56",
+ // No Q
+ // R
+ "\u0154\u0156\u0158\u0210\u0212\u1e58\u1e5a\u1e5c\u1e5e",
+ // S
+ "\u015A\u015C\u015E\u0160\u0218\u1e60\u1e62\u1e64\u1e66\u1e68",
+ // T
+ "\u0162\u0164\u0166\u021A\u1e6a\u1e6c\u1e6e\u1e70",
+ // U
+ "\u00D9\u00DA\u00DB\u00DC\u0168\u016A\u016C\u016E\u0170\u0172\u0214\u0216\u1e72\u1e74\u1e76\u1e78\u1e7a\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0",
+ // V
+ "\u1e7c\u1e7e",
+ // W
+ "\u0174\u1e80\u1e82\u1e84\u1e86\u1e88",
+ // X
+ "\u1e8a\u1e8c",
+ // Y
+ "\u00DD\u0176\u0178\u0232\u1e8e\u1ef2\u1ef4\u1ef6\u1ef8",
+ // Z
+ "\u0179\u017B\u017D\u0224\u1e90\u1e92\u1e94",
+];
+
+const lower = [
+ // a
+ "\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u0101\u0103\u0105\u01ce\u01df\u01e1\u01fb\u0201\u0203\u0227\u1e01\u1e9a\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7",
+ // b
+ "\u0180\u0183\u0185\u1e03\u1e05\u1e07",
+ // c
+ "\u00e7\u0107\u0109\u010b\u010d\u0188\u1e09",
+ // d
+ "\u010f\u0111\u1e0b\u1e0d\u1e0f\u1e11\u1e13",
+ // e
+ "\u00e8\u00e9\u00ea\u00eb\u0113\u0115\u0117\u0119\u011b\u0205\u0207\u0229\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7",
+ // f
+ "\u1e1f",
+ // g
+ "\u011d\u011f\u0121\u0123\u01e5\u01e7\u01f5\u1e21",
+ // h
+ "\u0125\u0127\u021f\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e96",
+ // i
+ "\u00ec\u00ed\u00ee\u00ef\u0129\u012b\u012d\u012f\u0131\u01d0\u0209\u020b\u1e2d\u1e2f\u1ec9\u1ecb",
+ // j
+ "\u0135",
+ // k
+ "\u0137\u0138\u01e9\u1e31\u1e33\u1e35",
+ // l
+ "\u013a\u013c\u013e\u0140\u0142\u1e37\u1e39\u1e3b\u1e3d",
+ // m
+ "\u1e3f\u1e41\u1e43",
+ // n
+ "\u00f1\u0144\u0146\u0148\u0149\u014b\u01f9\u1e45\u1e47\u1e49\u1e4b",
+ // o
+ "\u00f2\u00f3\u00f4\u00f5\u00f6\u014d\u014f\u0151\u01d2\u01eb\u01ed\u020d\u020e\u022b\u022d\u022f\u0231\u1e4d\u1e4f\u1e51\u1e53\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3",
+ // p
+ "\u1e55\u1e57",
+ // No q
+ // r
+ "\u0155\u0157\u0159\u0211\u0213\u1e59\u1e5b\u1e5d\u1e5f",
+ // s
+ "\u015b\u015d\u015f\u0161\u0219\u1e61\u1e63\u1e65\u1e67\u1e69",
+ // t
+ "\u0162\u0163\u0165\u0167\u021b\u1e6b\u1e6d\u1e6f\u1e71\u1e97",
+ // u
+ "\u00f9\u00fa\u00fb\u00fc\u0169\u016b\u016d\u016f\u0171\u0173\u01d4\u01d6\u01d8\u01da\u01dc\u0215\u0217\u1e73\u1e75\u1e77\u1e79\u1e7b\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1",
+ // v
+ "\u1e7d\u1e7f",
+ // w
+ "\u0175\u1e81\u1e83\u1e85\u1e87\u1e89\u1e98",
+ // x
+ "\u1e8b\u1e8d",
+ // y
+ "\u00fd\u00ff\u0177\u0233\u1e8f\u1e99\u1ef3\u1ef5\u1ef7\u1ef9",
+ // z
+ "\u017a\u017c\u017e\u0225\u1e91\u1e93\u1e95",
+];
+
+const symbol =
+ "\u00a1\u00a2\u00a3\u00a4\u00a5\u20ac\u00a6\u00a7\u00a8\u00a9\u00aa\u00ab\u00ac\u00ae\u00af\u00b0\u00b1\u00b2\u00b3\u00b4\u00b5\u00b6\u00b7\u00b8\u00b9\u00ba\u00bb\u00bc\u00bd\u00be\u00bf\u00d7\u00f7";
+
+const otherupper =
+ "\u00c6\u00d0\u00d8\u00de\u0132\u0152\u0186\u01c4\u01c5\u01c7\u01c8\u01ca\u01cb\u01F1\u01f2";
+
+const otherlower =
+ "\u00e6\u00f0\u00f8\u00fe\u00df\u0133\u0153\u01c6\u01c9\u01cc\u01f3";
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml
new file mode 100644
index 0000000000..4e6c1020fa
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertChars.xhtml
@@ -0,0 +1,55 @@
+<?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://editor/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertChars.dtd">
+
+<dialog id="insertCharsDlg" title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload = "Startup()"
+ onfocus = "onFocus()"
+ buttonlabelaccept="&insertButton.label;"
+ buttonlabelcancel="&closeButton.label;"
+ style = "width: 20em">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInsertChars.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&category.label;</label>
+ </hbox>
+ <radiogroup id="CatGrp" persist="category letter_index char_index">
+ <radio id="AccentUpper" label="&accentUpper.label;" oncommand="ChangeCategory(this.id)"/>
+ <radio id="AccentLower" label="&accentLower.label;" oncommand="ChangeCategory(this.id)"/>
+ <radio id="Upper" label="&otherUpper.label;" oncommand="ChangeCategory(this.id)"/>
+ <radio id="Lower" label="&otherLower.label;" oncommand="ChangeCategory(this.id)"/>
+ <radio id="Symbol" label="&commonSymbols.label;" oncommand="ChangeCategory(this.id)"/>
+ </radiogroup>
+ <spacer class="spacer"/>
+ </groupbox>
+ <hbox equalsize="always">
+ <vbox flex="1">
+ <!-- value is set in JS from editor.properties strings -->
+ <label id="LatinL_Label" control="LatinL" value="&letter.label;" accesskey="&letter.accessKey;"/>
+ <menulist class="larger" flex="1" id="LatinL" oncommand="SelectLatinLetter()">
+ <menupopup/>
+ </menulist>
+ </vbox>
+ <vbox flex="1">
+ <label id="LatinM_Label" control="LatinM" value="&character.label;" accesskey="&character.accessKey;"/>
+ <menulist class="larger" flex="1" id="LatinM" oncommand="SelectLatinModifier()">
+ <menupopup/>
+ </menulist>
+ </vbox>
+ </hbox>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertMath.js b/comm/suite/editor/components/dialogs/content/EdInsertMath.js
new file mode 100644
index 0000000000..c99bf8edac
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertMath.js
@@ -0,0 +1,330 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/* Insert MathML dialog */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ // Create dialog object for easy access
+ gDialog.accept = document.documentElement.getButton("accept");
+ gDialog.mode = document.getElementById("optionMode");
+ gDialog.direction = document.getElementById("optionDirection");
+ gDialog.input = document.getElementById("input");
+ gDialog.output = document.getElementById("output");
+ gDialog.tabbox = document.getElementById("tabboxInsertLaTeXCommand");
+
+ // Set initial focus
+ gDialog.input.focus();
+
+ // Load TeXZilla
+ // TeXZilla.js contains non-ASCII characters and explicitly sets
+ // window.TeXZilla, so we have to specify the charset parameter but don't
+ // need to worry about the targetObj parameter.
+ /* globals TeXZilla */
+ Services.scriptloader.loadSubScript(
+ "chrome://editor/content/TeXZilla.js",
+ {},
+ "UTF-8"
+ );
+
+ // Verify if the selection is on a <math> and initialize the dialog.
+ gDialog.oldMath = editor.getElementOrParentByTagName("math", null);
+ if (gDialog.oldMath) {
+ // When these attributes are absent or invalid, they default to "inline" and "ltr" respectively.
+ gDialog.mode.selectedIndex =
+ gDialog.oldMath.getAttribute("display") == "block" ? 1 : 0;
+ gDialog.direction.selectedIndex =
+ gDialog.oldMath.getAttribute("dir") == "rtl" ? 1 : 0;
+ gDialog.input.value = TeXZilla.getTeXSource(gDialog.oldMath);
+ }
+
+ // Create the tabbox with LaTeX commands.
+ createCommandPanel({
+ "√⅗²": [
+ "{⋯}^{⋯}",
+ "{⋯}_{⋯}",
+ "{⋯}_{⋯}^{⋯}",
+ "\\underset{⋯}{⋯}",
+ "\\overset{⋯}{⋯}",
+ "\\underoverset{⋯}{⋯}{⋯}",
+ "\\left(⋯\\right)",
+ "\\left[⋯\\right]",
+ "\\frac{⋯}{⋯}",
+ "\\binom{⋯}{⋯}",
+ "\\sqrt{⋯}",
+ "\\sqrt[⋯]{⋯}",
+ "\\cos\\left({⋯}\\right)",
+ "\\sin\\left({⋯}\\right)",
+ "\\tan\\left({⋯}\\right)",
+ "\\exp\\left({⋯}\\right)",
+ "\\ln\\left({⋯}\\right)",
+ "\\underbrace{⋯}",
+ "\\underline{⋯}",
+ "\\overbrace{⋯}",
+ "\\widevec{⋯}",
+ "\\widetilde{⋯}",
+ "\\widehat{⋯}",
+ "\\widecheck{⋯}",
+ "\\widebar{⋯}",
+ "\\dot{⋯}",
+ "\\ddot{⋯}",
+ "\\boxed{⋯}",
+ "\\slash{⋯}",
+ ],
+ "(â–¦)": [
+ "\\begin{matrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{matrix}",
+ "\\begin{pmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{pmatrix}",
+ "\\begin{bmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{bmatrix}",
+ "\\begin{Bmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{Bmatrix}",
+ "\\begin{vmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{vmatrix}",
+ "\\begin{Vmatrix} ⋯ & ⋯ \\\\ ⋯ & ⋯ \\end{Vmatrix}",
+ "\\begin{cases} ⋯ \\\\ ⋯ \\end{cases}",
+ "\\begin{aligned} ⋯ &= ⋯ \\\\ ⋯ &= ⋯ \\end{aligned}",
+ ],
+ });
+ createSymbolPanels([
+ "âˆâˆâˆ‘∫∬∭⨌∮⊎⊕⊖⊗⊘⊙⋀â‹â‹‚⋃⌈⌉⌊⌋⎰⎱⟨⟩⟪⟫∥⫼⨀â¨â¨‚⨄⨅⨆ðıȷâ„ℑℓ℘ℜℵℶ",
+ "∀∃∄∅∉∊∋∌⊂⊃⊄⊅⊆⊇⊈⊈⊉⊊⊊⊋⊋âŠâŠâŠ‘⊒⊓⊔⊥â‹â‹‘⋔⫅⫆⫋⫋⫌⫌…⋮⋯⋰⋱♭♮♯∂∇",
+ "±×÷†‡•∓∔∗∘âˆâˆ âˆ¡âˆ¢âˆ§âˆ¨âˆ´âˆµâˆ¼âˆ½â‰â‰ƒâ‰…≇≈≈≊â‰â‰Žâ‰â‰â‰‘≒≓≖≗≜≡≢≬⊚⊛⊞⊡⊢⊣⊤⊥",
+ "⊨⊩⊪⊫⊬⊭⊯⊲⊲⊳⊴⊵⊸⊻⋄⋅⋇⋈⋉⋊⋋⋌â‹â‹Žâ‹â‹’⋓⌅⌆⌣△▴▵▸▹▽▾▿◂◃◊○★♠♡♢♣⧫",
+ "≦≧≨≩≩≪≫≮≯≰≱≲≳≶≷≺≻≼≽≾≿⊀âŠâ‹–⋗⋘⋙⋚⋛⋞⋟⋦⋧⋨⋩⩽⩾⪅⪆⪇⪈⪉⪊⪋⪌⪕⪯⪰⪷⪸⪹⪺",
+ "â†â†‘→↓↔↕↖↗↘↙↜â†â†žâ† â†¢â†£â†¦â†©â†ªâ†«â†¬â†­â†­â†°â†±â†¼â†½â†¾â†¿â‡€â‡â‡‚⇃⇄⇆⇇⇈⇉⇊⇋⇌â‡â‡‘⇒⇓⇕⇖⇗⇘⇙⟺",
+ "αβγδϵ϶εζηθϑικϰλμνξℴπϖÏϱσςτυϕφχψωΓΔΘΛΞΠΣϒΦΨΩÏ℧",
+ "ð•’ð•“ð•”ð••ð•–ð•—ð•˜ð•™ð•šð•›ð•œð•ð•žð•Ÿð• ð•¡ð•¢ð•£ð•¤ð•¥ð•¦ð•§ð•¨ð•©ð•ªð•«ð”¸ð”¹â„‚ð”»ð”¼ð”½ð”¾â„ð•€ð•ð•‚ð•ƒð•„â„•ð•†â„™â„šâ„ð•Šð•‹ð•Œð•ð•Žð•ð•â„¤",
+ "ð’¶ð’·ð’¸ð’¹â„¯ð’»â„Šð’½ð’¾ð’¿ð“€ð“ð“‚ð“ƒâ„´ð“…ð“†ð“‡ð“ˆð“‰ð“Šð“‹ð“Œð“ð“Žð“ð’œâ„¬ð’žð’Ÿâ„°â„±ð’¢â„‹â„ð’¥ð’¦â„’ℳð’©ð’ªð’«ð’¬â„›ð’®ð’¯ð’°ð’±ð’²ð’³ð’´ð’µ",
+ "ð”žð”Ÿð” ð”¡ð”¢ð”£ð”¤ð”¥ð”¦ð”§ð”¨ð”©ð”ªð”«ð”¬ð”­ð”®ð”¯ð”°ð”±ð”²ð”³ð”´ð”µð”¶ð”·ð”„ð”…â„­ð”‡ð”ˆð”‰ð”Šâ„Œâ„‘ð”ð”Žð”ð”ð”‘ð”’ð”“ð””â„œð”–ð”—ð”˜ð”™ð”šð”›ð”œâ„¨",
+ ]);
+ gDialog.tabbox.selectedIndex = 0;
+
+ updateMath();
+
+ SetWindowLocation();
+}
+
+function insertLaTeXCommand(aButton) {
+ gDialog.input.focus();
+
+ // For a single math symbol, just use the insertText command.
+ if (aButton.label) {
+ gDialog.input.editor.insertText(aButton.label);
+ return;
+ }
+
+ // Otherwise, it's a LaTeX command with at least one argument...
+ var latex = TeXZilla.getTeXSource(aButton.firstChild);
+ var selectionStart = gDialog.input.selectionStart;
+ var selectionEnd = gDialog.input.selectionEnd;
+
+ // If the selection is not empty, we replace the first argument of the LaTeX
+ // command with the current selection.
+ var selection = gDialog.input.value.substring(selectionStart, selectionEnd);
+ if (selection != "") {
+ latex = latex.replace("⋯", selection);
+ }
+
+ // Try and move to the next position.
+ var latexNewStart = latex.indexOf("⋯"),
+ latexNewEnd;
+ if (latexNewStart == -1) {
+ // This is a unary function and the selection was used as an argument above.
+ // We select the expression again so that one can choose to apply further
+ // command to it or just move the caret after that text.
+ latexNewStart = 0;
+ latexNewEnd = latex.length;
+ } else {
+ // Otherwise, select the dots representing the next argument.
+ latexNewEnd = latexNewStart + 1;
+ }
+
+ // Update the input text and selection.
+ gDialog.input.editor.insertText(latex);
+ gDialog.input.setSelectionRange(
+ selectionStart + latexNewStart,
+ selectionStart + latexNewEnd
+ );
+
+ updateMath();
+}
+
+function createCommandPanel(aCommandPanelList) {
+ const columnCount = 10;
+
+ for (var label in aCommandPanelList) {
+ var commands = aCommandPanelList[label];
+
+ // Create a <rows> element with some LaTeX commands.
+ var rows = document.createXULElement("rows");
+
+ var i = 0,
+ row;
+ for (var command of commands) {
+ if (i % columnCount == 0) {
+ // Create a new row.
+ row = document.createXULElement("row");
+ rows.appendChild(row);
+ }
+
+ // Create a new button to insert the symbol.
+ var button = document.createXULElement("toolbarbutton");
+ button.setAttribute("class", "tabbable");
+ button.appendChild(TeXZilla.toMathML(command));
+ row.appendChild(button);
+
+ i++;
+ }
+
+ // Create a <columns> element with the desired number of columns.
+ var columns = document.createXULElement("columns");
+ for (i = 0; i < columnCount; i++) {
+ var column = document.createXULElement("column");
+ column.setAttribute("flex", "1");
+ columns.appendChild(column);
+ }
+
+ // Create the <grid> element with the <rows> and <columns> children.
+ var grid = document.createXULElement("grid");
+ grid.appendChild(columns);
+ grid.appendChild(rows);
+
+ // Create a new <tab> element.
+ var tab = document.createXULElement("tab");
+ tab.setAttribute("label", label);
+ gDialog.tabbox.tabs.appendChild(tab);
+
+ // Append the new tab panel.
+ gDialog.tabbox.tabpanels.appendChild(grid);
+ }
+}
+
+function createSymbolPanels(aSymbolPanelList) {
+ const columnCount = 13,
+ tabLabelLength = 3;
+
+ for (var symbols of aSymbolPanelList) {
+ // Create a <rows> element with the symbols of the i-th panel.
+ var rows = document.createXULElement("rows");
+ var i = 0,
+ tabLabel = "",
+ row;
+ for (var symbol of symbols) {
+ if (i % columnCount == 0) {
+ // Create a new row.
+ row = document.createXULElement("row");
+ rows.appendChild(row);
+ }
+
+ // Build the tab label from the first symbols of this tab.
+ if (i < tabLabelLength) {
+ tabLabel += symbol;
+ }
+
+ // Create a new button to insert the symbol.
+ var button = document.createXULElement("toolbarbutton");
+ button.setAttribute("label", symbol);
+ button.setAttribute("class", "tabbable");
+ row.appendChild(button);
+
+ i++;
+ }
+
+ // Create a <columns> element with the desired number of columns.
+ var columns = document.createXULElement("columns");
+ for (i = 0; i < columnCount; i++) {
+ var column = document.createXULElement("column");
+ column.setAttribute("flex", "1");
+ columns.appendChild(column);
+ }
+
+ // Create the <grid> element with the <rows> and <columns> children.
+ var grid = document.createXULElement("grid");
+ grid.appendChild(columns);
+ grid.appendChild(rows);
+
+ // Create a new <tab> element with the label determined above.
+ var tab = document.createXULElement("tab");
+ tab.setAttribute("label", tabLabel);
+ gDialog.tabbox.tabs.appendChild(tab);
+
+ // Append the new tab panel.
+ gDialog.tabbox.tabpanels.appendChild(grid);
+ }
+}
+
+function onAccept(event) {
+ if (gDialog.output.firstChild) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ var newMath = editor.document.importNode(gDialog.output.firstChild, true);
+ if (gDialog.oldMath) {
+ // Replace the old <math> element with the new one.
+ editor.selectElement(gDialog.oldMath);
+ editor.insertElementAtSelection(newMath, true);
+ } else {
+ // Insert the new <math> element.
+ editor.insertElementAtSelection(newMath, false);
+ }
+ } catch (e) {}
+
+ editor.endTransaction();
+ } else {
+ dump("Null value -- not inserting in MathML Source dialog\n");
+ event.preventDefault();
+ }
+ SaveWindowLocation();
+}
+
+function updateMath() {
+ // Remove the preview, if any.
+ if (gDialog.output.firstChild) {
+ gDialog.output.firstChild.remove();
+ }
+
+ // Try to convert the LaTeX source into MathML using TeXZilla.
+ // We use the placeholder text if no input is provided.
+ try {
+ var input = gDialog.input.value || gDialog.input.placeholder;
+ var newMath = TeXZilla.toMathML(
+ input,
+ gDialog.mode.selectedIndex,
+ gDialog.direction.selectedIndex,
+ true
+ );
+ gDialog.output.appendChild(document.importNode(newMath, true));
+ gDialog.output.style.opacity = gDialog.input.value ? 1 : 0.5;
+ } catch (e) {}
+ // Disable the accept button if parsing fails or when the placeholder is used.
+ gDialog.accept.disabled = !gDialog.input.value || !gDialog.output.firstChild;
+}
+
+function updateMode() {
+ if (gDialog.output.firstChild) {
+ gDialog.output.firstChild.setAttribute(
+ "display",
+ gDialog.mode.selectedIndex ? "block" : "inline"
+ );
+ }
+}
+
+function updateDirection() {
+ if (gDialog.output.firstChild) {
+ gDialog.output.firstChild.setAttribute(
+ "dir",
+ gDialog.direction.selectedIndex ? "rtl" : "ltr"
+ );
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml
new file mode 100644
index 0000000000..9138d00846
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertMath.xhtml
@@ -0,0 +1,60 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertMath.dtd">
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttonlabelaccept="&insertButton.label;"
+ buttonaccesskeyaccept="&insertButton.accesskey;">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInsertMath.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <label id="srcMessage" value="&sourceEditField.label;"/>
+ <html:textarea id="input" rows="5" oninput="updateMath();"
+ placeholder="\sqrt{x_1} + \frac{Ï€^3}{2}"/>
+ <vbox flex="1" style="overflow: auto; width: 30em; height: 5em;">
+ <description id="output"/>
+ </vbox>
+ <tabbox id="tabboxInsertLaTeXCommand">
+ <tabs/>
+ <tabpanels oncommand="insertLaTeXCommand(event.target);"/>
+ </tabbox>
+ <spacer class="spacer"/>
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&options.label;</label>
+ </hbox>
+ <hbox>
+ <radiogroup id="optionMode" oncommand="updateMode();">
+ <radio label="&optionInline.label;"
+ accesskey="&optionInline.accesskey;"/>
+ <radio label="&optionDisplay.label;"
+ accesskey="&optionDisplay.accesskey;"/>
+ </radiogroup>
+ <radiogroup id="optionDirection" oncommand="updateDirection();">
+ <radio label="&optionLTR.label;"
+ accesskey="&optionLTR.accesskey;"/>
+ <radio label="&optionRTL.label;"
+ accesskey="&optionRTL.accesskey;"/>
+ </radiogroup>
+ </hbox>
+ </groupbox>
+ <spacer class="spacer"/>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTOC.js b/comm/suite/editor/components/dialogs/content/EdInsertTOC.js
new file mode 100644
index 0000000000..3ec386f7c9
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertTOC.js
@@ -0,0 +1,378 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// tocHeadersArray is the array containing the pairs tag/class
+// defining TOC entries
+var tocHeadersArray = new Array(6);
+
+// a global used when building the TOC
+var currentHeaderLevel = 0;
+
+// a global set to true if the TOC is to be readonly
+var readonly = false;
+
+// a global set to true if user wants indexes in the TOC
+var orderedList = true;
+
+// constants
+const kMozToc = "mozToc";
+const kMozTocLength = 6;
+const kMozTocIdPrefix = "mozTocId";
+const kMozTocIdPrefixLength = 8;
+const kMozTocClassPrefix = "mozToc";
+const kMozTocClassPrefixLength = 6;
+
+document.addEventListener("dialogaccept", () => BuildTOC(true));
+
+// Startup() is called when EdInsertTOC.xhtml is opened
+function Startup() {
+ // early way out if if we have no editor
+ if (!GetCurrentEditor()) {
+ window.close();
+ return;
+ }
+
+ var i;
+ // clean the table of tag/class pairs we look for
+ for (i = 0; i < 6; ++i) {
+ tocHeadersArray[i] = ["", ""];
+ }
+
+ // reset all settings
+ for (i = 1; i < 7; ++i) {
+ var menulist = document.getElementById("header" + i + "Menulist");
+ var menuitem = document.getElementById("header" + i + "none");
+ var textbox = document.getElementById("header" + i + "Class");
+ menulist.selectedItem = menuitem;
+ textbox.setAttribute("disabled", "true");
+ }
+
+ var theDocument = GetCurrentEditor().document;
+
+ // do we already have a TOC in the document ? It should have "mozToc" ID
+ var toc = theDocument.getElementById(kMozToc);
+
+ // default TOC definition, use h1-h6 for TOC entry levels 1-6
+ var headers = "h1 1 h2 2 h3 3 h4 4 h5 5 h6 6";
+
+ var orderedListCheckbox = document.getElementById("orderedListCheckbox");
+ orderedListCheckbox.checked = true;
+
+ if (toc) {
+ // man, there is already a TOC here
+
+ if (toc.getAttribute("class") == "readonly") {
+ // and it's readonly
+ var checkbox = document.getElementById("readOnlyCheckbox");
+ checkbox.checked = true;
+ readonly = true;
+ }
+
+ // let's see if it's an OL or an UL
+ orderedList = toc.nodeName.toLowerCase() == "ol";
+ orderedListCheckbox.checked = orderedList;
+
+ var nodeList = toc.childNodes;
+ // let's look at the children of the TOC ; if we find a comment beginning
+ // with "mozToc", it contains the TOC definition
+ for (i = 0; i < nodeList.length; ++i) {
+ if (
+ nodeList.item(i).nodeType == Node.COMMENT_NODE &&
+ nodeList.item(i).data.startsWith(kMozToc)
+ ) {
+ // yep, there is already a definition here; parse it !
+ headers = nodeList
+ .item(i)
+ .data.substr(
+ kMozTocLength + 1,
+ nodeList.item(i).length - kMozTocLength - 1
+ );
+ break;
+ }
+ }
+ }
+
+ // let's get an array filled with the (tag.class, index level) pairs
+ var headersArray = headers.split(" ");
+
+ for (i = 0; i < headersArray.length; i += 2) {
+ var tag = headersArray[i],
+ className = "";
+ var index = headersArray[i + 1];
+ menulist = document.getElementById("header" + index + "Menulist");
+ if (menulist) {
+ var sep = tag.indexOf(".");
+ if (sep != -1) {
+ // the tag variable contains in fact "tag.className", let's parse
+ // the class and get the real tag name
+ var tmp = tag.substr(0, sep);
+ className = tag.substr(sep + 1, tag.length - sep - 1);
+ tag = tmp;
+ }
+
+ // update the dialog
+ menuitem = document.getElementById("header" + index + tag.toUpperCase());
+ textbox = document.getElementById("header" + index + "Class");
+ menulist.selectedItem = menuitem;
+ if (tag != "") {
+ textbox.removeAttribute("disabled");
+ }
+ if (className != "") {
+ textbox.value = className;
+ }
+ tocHeadersArray[index - 1] = [tag, className];
+ }
+ }
+}
+
+function BuildTOC(update) {
+ // controlClass() is a node filter that accepts a node if
+ // (a) we don't look for a class (b) we look for a class and
+ // node has it
+ function controlClass(node, index) {
+ currentHeaderLevel = index + 1;
+ if (tocHeadersArray[index][1] == "") {
+ // we are not looking for a specific class, this node is ok
+ return NodeFilter.FILTER_ACCEPT;
+ }
+ if (node.getAttribute("class")) {
+ // yep, we look for a class, let's look at all the classes
+ // the node has
+ var classArray = node.getAttribute("class").split(" ");
+ for (var j = 0; j < classArray.length; j++) {
+ if (classArray[j] == tocHeadersArray[index][1]) {
+ // hehe, we found it...
+ return NodeFilter.FILTER_ACCEPT;
+ }
+ }
+ }
+ return NodeFilter.FILTER_SKIP;
+ }
+
+ // the main node filter for our node iterator
+ // it selects the tag names as specified in the dialog
+ // then calls the controlClass filter above
+ function acceptNode(node) {
+ switch (node.nodeName.toLowerCase()) {
+ case tocHeadersArray[0][0]:
+ return controlClass(node, 0);
+ case tocHeadersArray[1][0]:
+ return controlClass(node, 1);
+ case tocHeadersArray[2][0]:
+ return controlClass(node, 2);
+ case tocHeadersArray[3][0]:
+ return controlClass(node, 3);
+ case tocHeadersArray[4][0]:
+ return controlClass(node, 4);
+ case tocHeadersArray[5][0]:
+ return controlClass(node, 5);
+ default:
+ return NodeFilter.FILTER_SKIP;
+ }
+ }
+
+ var editor = GetCurrentEditor();
+ var theDocument = editor.document;
+ // let's create a TreeWalker to look for our nodes
+ var treeWalker = theDocument.createTreeWalker(
+ theDocument.documentElement,
+ NodeFilter.SHOW_ELEMENT,
+ acceptNode,
+ true
+ );
+ // we need an array to store all TOC entries we find in the document
+ var tocArray = [];
+ if (treeWalker) {
+ var tocSourceNode = treeWalker.nextNode();
+ while (tocSourceNode) {
+ var headerIndex = currentHeaderLevel;
+
+ // we have a node, we need to get all its textual contents
+ var textTreeWalker = theDocument.createTreeWalker(
+ tocSourceNode,
+ NodeFilter.SHOW_TEXT,
+ null,
+ true
+ );
+ var textNode = textTreeWalker.nextNode(),
+ headerText = "";
+ while (textNode) {
+ headerText += textNode.data;
+ textNode = textTreeWalker.nextNode();
+ }
+
+ var anchor = tocSourceNode.firstChild,
+ id;
+ // do we have a named anchor as 1st child of our node ?
+ if (
+ anchor.nodeName.toLowerCase() == "a" &&
+ anchor.hasAttribute("name") &&
+ anchor.getAttribute("name").startsWith(kMozTocIdPrefix)
+ ) {
+ // yep, get its name
+ id = anchor.getAttribute("name");
+ } else {
+ // no we don't and we need to create one
+ anchor = theDocument.createElement("a");
+ tocSourceNode.insertBefore(anchor, tocSourceNode.firstChild);
+ // let's give it a random ID
+ var c = 1000000 * Math.random();
+ id = kMozTocIdPrefix + Math.round(c);
+ anchor.setAttribute("name", id);
+ anchor.setAttribute(
+ "class",
+ kMozTocClassPrefix + tocSourceNode.nodeName.toUpperCase()
+ );
+ }
+ // and store that new entry in our array
+ tocArray.push(headerIndex, headerText, id);
+ tocSourceNode = treeWalker.nextNode();
+ }
+ }
+
+ /* generate the TOC itself */
+ headerIndex = 0;
+ var item, toc;
+ for (var i = 0; i < tocArray.length; i += 3) {
+ if (!headerIndex) {
+ // do we need to create an ol/ul container for the first entry ?
+ ++headerIndex;
+ toc = theDocument.getElementById(kMozToc);
+ if (!toc || !update) {
+ // we need to create a list container for the table of contents
+ toc = GetCurrentEditor().createElementWithDefaults(
+ orderedList ? "ol" : "ul"
+ );
+ // grrr, we need to create a LI inside the list otherwise
+ // Composer will refuse an empty list and will remove it !
+ var pit = theDocument.createElement("li");
+ toc.appendChild(pit);
+ GetCurrentEditor().insertElementAtSelection(toc, true);
+ // ah, now it's inserted so let's remove the useless list item...
+ toc.removeChild(pit);
+ // we need to recognize later that this list is our TOC
+ toc.setAttribute("id", kMozToc);
+ } else if (orderedList != (toc.nodeName.toLowerCase() == "ol")) {
+ // we have to update an existing TOC, is the existing TOC of the
+ // desired type (ordered or not) ?
+
+ // nope, we have to recreate the list
+ var newToc = GetCurrentEditor().createElementWithDefaults(
+ orderedList ? "ol" : "ul"
+ );
+ toc.parentNode.insertBefore(newToc, toc);
+ // and remove the old one
+ toc.remove();
+ toc = newToc;
+ toc.setAttribute("id", kMozToc);
+ } else {
+ // we can keep the list itself but let's get rid of the TOC entries
+ while (toc.hasChildNodes()) {
+ toc.lastChild.remove();
+ }
+ }
+
+ var commentText = "mozToc ";
+ for (var j = 0; j < 6; j++) {
+ if (tocHeadersArray[j][0] != "") {
+ commentText += tocHeadersArray[j][0];
+ if (tocHeadersArray[j][1] != "") {
+ commentText += "." + tocHeadersArray[j][1];
+ }
+ commentText += " " + (j + 1) + " ";
+ }
+ }
+ // important, we have to remove trailing spaces
+ commentText = TrimStringRight(commentText);
+
+ // forge a comment we'll insert in the TOC ; that comment will hold
+ // the TOC definition for us
+ var ct = theDocument.createComment(commentText);
+ toc.appendChild(ct);
+
+ // assign a special class to the TOC top element if the TOC is readonly
+ // the definition of this class is in EditorOverride.css
+ if (readonly) {
+ toc.setAttribute("class", "readonly");
+ } else {
+ toc.removeAttribute("class");
+ }
+
+ // We need a new variable to hold the local ul/ol container
+ // The toplevel TOC element is not the parent element of a
+ // TOC entry if its depth is > 1...
+ var tocList = toc;
+ // create a list item
+ var tocItem = theDocument.createElement("li");
+ // and an anchor in this list item
+ var tocAnchor = theDocument.createElement("a");
+ // make it target the source of the TOC entry
+ tocAnchor.setAttribute("href", "#" + tocArray[i + 2]);
+ // and put the textual contents of the TOC entry in that anchor
+ var tocEntry = theDocument.createTextNode(tocArray[i + 1]);
+ // now, insert everything where it has to be inserted
+ tocAnchor.appendChild(tocEntry);
+ tocItem.appendChild(tocAnchor);
+ tocList.appendChild(tocItem);
+ item = tocList;
+ } else {
+ if (tocArray[i] < headerIndex) {
+ // if the depth of the new TOC entry is less than the depth of the
+ // last entry we created, find the good ul/ol ancestor
+ for (j = headerIndex - tocArray[i]; j > 0; --j) {
+ if (item != toc) {
+ item = item.parentNode.parentNode;
+ }
+ }
+ tocItem = theDocument.createElement("li");
+ } else if (tocArray[i] > headerIndex) {
+ // to the contrary, it's deeper than the last one
+ // we need to create sub ul/ol's and li's
+ for (j = tocArray[i] - headerIndex; j > 0; --j) {
+ tocList = theDocument.createElement(orderedList ? "ol" : "ul");
+ item.lastChild.appendChild(tocList);
+ tocItem = theDocument.createElement("li");
+ tocList.appendChild(tocItem);
+ item = tocList;
+ }
+ } else {
+ tocItem = theDocument.createElement("li");
+ }
+ tocAnchor = theDocument.createElement("a");
+ tocAnchor.setAttribute("href", "#" + tocArray[i + 2]);
+ tocEntry = theDocument.createTextNode(tocArray[i + 1]);
+ tocAnchor.appendChild(tocEntry);
+ tocItem.appendChild(tocAnchor);
+ item.appendChild(tocItem);
+ headerIndex = tocArray[i];
+ }
+ }
+ SaveWindowLocation();
+}
+
+function selectHeader(elt, index) {
+ var tag = elt.value;
+ tocHeadersArray[index - 1][0] = tag;
+ var textbox = document.getElementById("header" + index + "Class");
+ if (tag == "") {
+ textbox.setAttribute("disabled", "true");
+ } else {
+ textbox.removeAttribute("disabled");
+ }
+}
+
+function changeClass(elt, index) {
+ tocHeadersArray[index - 1][1] = elt.value;
+}
+
+function ToggleReadOnlyToc(elt) {
+ readonly = elt.checked;
+}
+
+function ToggleOrderedList(elt) {
+ orderedList = elt.checked;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml
new file mode 100644
index 0000000000..8d82bac046
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertTOC.xhtml
@@ -0,0 +1,225 @@
+<?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" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorInsertTOC.dtd">
+
+<dialog title="&Window.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ oncancel="window.close(); return true;">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInsertTOC.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+ <spacer id="dummy" style="display:none"/>
+ <vbox flex="1">
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&buildToc.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column style="min-width: 6em"/><column/></columns>
+ <rows>
+ <row align="center">
+ <spacer/>
+ <label value="&tag.label;"/>
+ <label value="&class.label;"/>
+ </row>
+ <row align="center">
+ <label value="&header1.label;"/>
+ <menulist id="header1Menulist">
+ <menupopup>
+ <menuitem id="header1none" label="--" value=""
+ oncommand="selectHeader(this, 1)"/>
+ <menuseparator/>
+ <menuitem id="header1H1" label="h1" value="h1"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1H2" label="h2" value="h2"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1H3" label="h3" value="h3"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1H4" label="h4" value="h4"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1H5" label="h5" value="h5"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1H6" label="h6" value="h6"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1DIV" label="div" value="div"
+ oncommand="selectHeader(this, 1)"/>
+ <menuitem id="header1P" label="p" value="p"
+ oncommand="selectHeader(this, 1)"/>
+ </menupopup>
+ </menulist>
+ <textbox id="header1Class" size="10"
+ oninput="changeClass(this, 1)"/>
+ </row>
+
+ <row align="center">
+ <label value="&header2.label;"/>
+ <menulist id="header2Menulist">
+ <menupopup>
+ <menuitem id="header2none" label="--" value=""
+ oncommand="selectHeader(this, 2)"/>
+ <menuseparator/>
+ <menuitem id="header2H1" label="h1" value="h1"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2H2" label="h2" value="h2"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2H3" label="h3" value="h3"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2H4" label="h4" value="h4"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2H5" label="h5" value="h5"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2H6" label="h6" value="h6"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2DIV" label="div" value="div"
+ oncommand="selectHeader(this, 2)"/>
+ <menuitem id="header2P" label="p" value="p"
+ oncommand="selectHeader(this, 2)"/>
+ </menupopup>
+ </menulist>
+ <textbox id="header2Class" size="10"
+ oninput="changeClass(this, 2)"/>
+ </row>
+
+ <row align="center">
+ <label value="&header3.label;"/>
+ <menulist id="header3Menulist">
+ <menupopup>
+ <menuitem id="header3none" label="--" value=""
+ oncommand="selectHeader(this, 3)"/>
+ <menuseparator/>
+ <menuitem id="header3H1" label="h1" value="h1"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3H2" label="h2" value="h2"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3H3" label="h3" value="h3"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3H4" label="h4" value="h4"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3H5" label="h5" value="h5"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3H6" label="h6" value="h6"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3DIV" label="div" value="div"
+ oncommand="selectHeader(this, 3)"/>
+ <menuitem id="header3P" label="p" value="p"
+ oncommand="selectHeader(this, 3)"/>
+ </menupopup>
+ </menulist>
+ <textbox id="header3Class" size="10"
+ oninput="changeClass(this, 3)"/>
+ </row>
+
+ <row align="center">
+ <label value="&header4.label;"/>
+ <menulist id="header4Menulist">
+ <menupopup>
+ <menuitem id="header4none" label="--" value=""
+ oncommand="selectHeader(this, 4)"/>
+ <menuseparator/>
+ <menuitem id="header4H1" label="h1" value="h1"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4H2" label="h2" value="h2"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4H3" label="h3" value="h3"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4H4" label="h4" value="h4"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4H5" label="h5" value="h5"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4H6" label="h6" value="h6"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4DIV" label="div" value="div"
+ oncommand="selectHeader(this, 4)"/>
+ <menuitem id="header4P" label="p" value="p"
+ oncommand="selectHeader(this, 4)"/>
+ </menupopup>
+ </menulist>
+ <textbox id="header4Class" size="10"
+ oninput="changeClass(this, 4)"/>
+ </row>
+
+ <row align="center">
+ <label value="&header5.label;"/>
+ <menulist id="header5Menulist">
+ <menupopup>
+ <menuitem id="header5none" label="--" value=""
+ oncommand="selectHeader(this, 5)"/>
+ <menuseparator/>
+ <menuitem id="header5H1" label="h1" value="h1"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5H2" label="h2" value="h2"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5H3" label="h3" value="h3"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5H4" label="h4" value="h4"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5H5" label="h5" value="h5"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5H6" label="h6" value="h6"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5DIV" label="div" value="div"
+ oncommand="selectHeader(this, 5)"/>
+ <menuitem id="header5P" label="p" value="p"
+ oncommand="selectHeader(this, 5)"/>
+ </menupopup>
+ </menulist>
+ <textbox id="header5Class" size="10"
+ oninput="changeClass(this, 5)"/>
+ </row>
+
+ <row align="center">
+ <label value="&header6.label;"/>
+ <menulist id="header6Menulist">
+ <menupopup>
+ <menuitem id="header6none" label="--" value=""
+ oncommand="selectHeader(this, 6)"/>
+ <menuseparator/>
+ <menuitem id="header6H1" label="h1" value="h1"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6H2" label="h2" value="h2"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6H3" label="h3" value="h3"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6H4" label="h4" value="h4"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6H5" label="h5" value="h5"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6H6" label="h6" value="h6"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6DIV" label="div" value="div"
+ oncommand="selectHeader(this, 6)"/>
+ <menuitem id="header6P" label="p" value="p"
+ oncommand="selectHeader(this, 6)"/>
+ </menupopup>
+ </menulist>
+ <textbox id="header6Class" size="10"
+ oninput="changeClass(this, 6)"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ <vbox>
+ <checkbox id="orderedListCheckbox"
+ label="&orderedList.label;"
+ oncommand="ToggleOrderedList(this)"/>
+ <checkbox id="readOnlyCheckbox"
+ label="&makeReadOnly.label;"
+ oncommand="ToggleReadOnlyToc(this)"/>
+ </vbox>
+ <separator class="groove"/>
+ </vbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTable.js b/comm/suite/editor/components/dialogs/content/EdInsertTable.js
new file mode 100644
index 0000000000..0053f9fd94
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertTable.js
@@ -0,0 +1,254 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+
+var gTableElement = null;
+var gRows;
+var gColumns;
+var gActiveEditor;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ gActiveEditor = GetCurrentTableEditor();
+ if (!gActiveEditor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ try {
+ gTableElement = gActiveEditor.createElementWithDefaults("table");
+ } catch (e) {}
+
+ if (!gTableElement) {
+ dump("Failed to create a new table!\n");
+ window.close();
+ return;
+ }
+ gDialog.rowsInput = document.getElementById("rowsInput");
+ gDialog.columnsInput = document.getElementById("columnsInput");
+ gDialog.widthInput = document.getElementById("widthInput");
+ gDialog.borderInput = document.getElementById("borderInput");
+ gDialog.widthPixelOrPercentMenulist = document.getElementById(
+ "widthPixelOrPercentMenulist"
+ );
+ gDialog.OkButton = document.documentElement.getButton("accept");
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = gTableElement.cloneNode(false);
+ try {
+ if (
+ Services.prefs.getBoolPref("editor.use_css") &&
+ IsHTMLEditor() &&
+ !(gActiveEditor.flags & Ci.nsIEditor.eEditorMailMask)
+ ) {
+ // only for Composer and not for htmlmail
+ globalElement.setAttribute("style", "text-align: left;");
+ }
+ } catch (e) {}
+
+ // Initialize all widgets with image attributes
+ InitDialog();
+
+ // Set initial number to 2 rows, 2 columns:
+ // Note, these are not attributes on the table,
+ // so don't put them in InitDialog(),
+ // else the user's values will be trashed when they use
+ // the Advanced Edit dialog
+ gDialog.rowsInput.value = 2;
+ gDialog.columnsInput.value = 2;
+
+ // If no default value on the width, set to 100%
+ if (gDialog.widthInput.value.length == 0) {
+ gDialog.widthInput.value = "100";
+ gDialog.widthPixelOrPercentMenulist.selectedIndex = 1;
+ }
+
+ SetTextboxFocusById("rowsInput");
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog() {
+ // Get default attributes set on the created table:
+ // Get the width attribute of the element, stripping out "%"
+ // This sets contents of menu combobox list
+ // 2nd param = null: Use current selection to find if parent is table cell or window
+ gDialog.widthInput.value = InitPixelOrPercentMenulist(
+ globalElement,
+ null,
+ "width",
+ "widthPixelOrPercentMenulist",
+ gPercent
+ );
+ gDialog.borderInput.value = globalElement.getAttribute("border");
+}
+
+function ChangeRowOrColumn(id) {
+ // Allow only integers
+ forceInteger(id);
+
+ // Enable OK only if both rows and columns have a value > 0
+ var enable =
+ gDialog.rowsInput.value.length > 0 &&
+ gDialog.rowsInput.value > 0 &&
+ gDialog.columnsInput.value.length > 0 &&
+ gDialog.columnsInput.value > 0;
+
+ SetElementEnabled(gDialog.OkButton, enable);
+ SetElementEnabledById("AdvancedEditButton1", enable);
+}
+
+// Get and validate data from widgets.
+// Set attributes on globalElement so they can be accessed by AdvancedEdit()
+function ValidateData() {
+ gRows = ValidateNumber(
+ gDialog.rowsInput,
+ null,
+ 1,
+ gMaxRows,
+ null,
+ null,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ gColumns = ValidateNumber(
+ gDialog.columnsInput,
+ null,
+ 1,
+ gMaxColumns,
+ null,
+ null,
+ true
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ // Set attributes: NOTE: These may be empty strings (last param = false)
+ ValidateNumber(
+ gDialog.borderInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalElement,
+ "border",
+ false
+ );
+ // TODO: Deal with "BORDER" without value issue
+ if (gValidationError) {
+ return false;
+ }
+
+ ValidateNumber(
+ gDialog.widthInput,
+ gDialog.widthPixelOrPercentMenulist,
+ 1,
+ gMaxTableSize,
+ globalElement,
+ "width",
+ false
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ gActiveEditor.beginTransaction();
+ try {
+ gActiveEditor.cloneAttributes(gTableElement, globalElement);
+
+ // Create necessary rows and cells for the table
+ var tableBody = gActiveEditor.createElementWithDefaults("tbody");
+ if (tableBody) {
+ gTableElement.appendChild(tableBody);
+
+ // Create necessary rows and cells for the table
+ for (var i = 0; i < gRows; i++) {
+ var newRow = gActiveEditor.createElementWithDefaults("tr");
+ if (newRow) {
+ tableBody.appendChild(newRow);
+ for (var j = 0; j < gColumns; j++) {
+ var newCell = gActiveEditor.createElementWithDefaults("td");
+ if (newCell) {
+ newRow.appendChild(newCell);
+ }
+ }
+ }
+ }
+ }
+ // Detect when entire cells are selected:
+ // Get number of cells selected
+ var tagNameObj = { value: "" };
+ var countObj = { value: 0 };
+ var element = gActiveEditor.getSelectedOrParentTableElement(
+ tagNameObj,
+ countObj
+ );
+ var deletePlaceholder = false;
+
+ if (tagNameObj.value == "table") {
+ // Replace entire selected table with new table, so delete the table
+ gActiveEditor.deleteTable();
+ } else if (tagNameObj.value == "td") {
+ if (countObj.value >= 1) {
+ if (countObj.value > 1) {
+ // Assume user wants to replace a block of
+ // contiguous cells with a table, so
+ // join the selected cells
+ gActiveEditor.joinTableCells(false);
+
+ // Get the cell everything was merged into
+ element = gActiveEditor.getFirstSelectedCell();
+
+ // Collapse selection into just that cell
+ gActiveEditor.selection.collapse(element, 0);
+ }
+
+ if (element) {
+ // Empty just the contents of the cell
+ gActiveEditor.deleteTableCellContents();
+
+ // Collapse selection to start of empty cell...
+ gActiveEditor.selection.collapse(element, 0);
+ // ...but it will contain a <br> placeholder
+ deletePlaceholder = true;
+ }
+ }
+ }
+
+ // true means delete selection when inserting
+ gActiveEditor.insertElementAtSelection(gTableElement, true);
+
+ if (deletePlaceholder && gTableElement && gTableElement.nextSibling) {
+ // Delete the placeholder <br>
+ gActiveEditor.deleteNode(gTableElement.nextSibling);
+ }
+ } catch (e) {}
+
+ gActiveEditor.endTransaction();
+
+ SaveWindowLocation();
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml b/comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml
new file mode 100644
index 0000000000..b376d5f0be
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdInsertTable.xhtml
@@ -0,0 +1,82 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edInsertTable SYSTEM "chrome://editor/locale/EditorInsertTable.dtd">
+%edInsertTable;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload = "Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdInsertTable.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&size.label;</label>
+ </hbox>
+ <grid>
+ <columns>
+ <column flex="1"/>
+ <column flex="1"/>
+ <column flex="6"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label control="rowsInput" class="align-right"
+ value="&numRowsEditField.label;"
+ accesskey="&numRowsEditField.accessKey;"/>
+ <textbox class="narrow" id="rowsInput" oninput="ChangeRowOrColumn(this.id)" />
+ <spacer/>
+ </row>
+ <row align="center">
+ <label control="columnsInput" class="align-right"
+ value="&numColumnsEditField.label;"
+ accesskey="&numColumnsEditField.accessKey;"/>
+ <textbox class="narrow" id="columnsInput" oninput="ChangeRowOrColumn(this.id)" />
+ <spacer/>
+ </row>
+ <row align="center">
+ <label control="widthInput" class="align-right"
+ value="&widthEditField.label;"
+ accesskey="&widthEditField.accessKey;"/>
+ <textbox class="narrow" id="widthInput" oninput="forceInteger(this.id)" />
+ <menulist id="widthPixelOrPercentMenulist" flex="1"/>
+ <!-- child elements are appended by JS -->
+ </row>
+ </rows>
+ </grid>
+ <spacer class="spacer"/>
+ </groupbox>
+ <spacer class="spacer"/>
+ <hbox align="center">
+ <label control="borderInput" class="align-right"
+ value="&borderEditField.label;"
+ accesskey="&borderEditField.accessKey;"
+ tooltiptext="&borderEditField.tooltip;" />
+ <textbox class="narrow" id="borderInput" oninput="forceInteger(this.id)" />
+ <label value="&pixels.label;"/>
+ </hbox>
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator id="advancedSeparator" class="groove"/>
+ </vbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdLabelProps.js b/comm/suite/editor/components/dialogs/content/EdLabelProps.js
new file mode 100644
index 0000000000..ec96878ea6
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdLabelProps.js
@@ -0,0 +1,118 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var labelElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gDialog.editText = document.getElementById("EditText");
+ gDialog.labelText = document.getElementById("LabelText");
+ gDialog.labelFor = document.getElementById("LabelFor");
+ gDialog.labelAccessKey = document.getElementById("LabelAccessKey");
+
+ labelElement = window.arguments[0];
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = labelElement.cloneNode(false);
+
+ InitDialog();
+
+ var range = editor.document.createRange();
+ range.selectNode(labelElement);
+ gDialog.labelText.value = range.toString();
+
+ if (labelElement.innerHTML.includes("<")) {
+ gDialog.editText.checked = false;
+ gDialog.editText.disabled = false;
+ gDialog.labelText.disabled = true;
+ gDialog.editText.addEventListener(
+ "command",
+ () =>
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ GetString("EditTextWarning")
+ ),
+ { capture: false, once: true }
+ );
+ SetTextboxFocus(gDialog.labelFor);
+ } else {
+ SetTextboxFocus(gDialog.labelText);
+ }
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ gDialog.labelFor.value = globalElement.getAttribute("for");
+ gDialog.labelAccessKey.value = globalElement.getAttribute("accesskey");
+}
+
+function RemoveLabel() {
+ RemoveContainer(labelElement);
+ SaveWindowLocation();
+ window.close();
+}
+
+function ValidateData() {
+ if (gDialog.labelFor.value) {
+ globalElement.setAttribute("for", gDialog.labelFor.value);
+ } else {
+ globalElement.removeAttribute("for");
+ }
+ if (gDialog.labelAccessKey.value) {
+ globalElement.setAttribute("accesskey", gDialog.labelAccessKey.value);
+ } else {
+ globalElement.removeAttribute("accesskey");
+ }
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ try {
+ if (gDialog.editText.checked) {
+ editor.setShouldTxnSetSelection(false);
+
+ while (labelElement.firstChild) {
+ editor.deleteNode(labelElement.firstChild);
+ }
+ if (gDialog.labelText.value) {
+ editor.insertNode(
+ editor.document.createTextNode(gDialog.labelText.value),
+ labelElement,
+ 0
+ );
+ }
+
+ editor.setShouldTxnSetSelection(true);
+ }
+
+ editor.cloneAttributes(labelElement, globalElement);
+ } catch (e) {}
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml b/comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml
new file mode 100644
index 0000000000..21d964f1fd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdLabelProps.xhtml
@@ -0,0 +1,66 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edLabelProperties SYSTEM "chrome://editor/locale/EditorLabelProperties.dtd">
+%edLabelProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdLabelProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header" accesskey="&Settings.accesskey;">&Settings.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <checkbox id="EditText" label="&EditLabelText.label;" accesskey="&EditLabelText.accesskey;" checked="true" disabled="true"
+ oncommand="gDialog.labelText.disabled = !gDialog.editText.checked;"/>
+ <textbox id="LabelText" accesskey="&Settings.accesskey;"/>
+ </row>
+ <row align="center">
+ <label control="LabelFor" value="&LabelFor.label;" accesskey="&LabelFor.accesskey;"/>
+ <textbox id="LabelFor"/>
+ </row>
+ <row align="center">
+ <label control="LabelAccessKey" value="&AccessKey.label;" accesskey="&AccessKey.accesskey;"/>
+ <hbox>
+ <textbox id="LabelAccessKey" class="narrow"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <!-- from EdDialogOverlay -->
+ <hbox flex="1" style="margin-top: 0.2em">
+ <button id="RemoveLabel" label="&RemoveLabel.label;" accesskey="&RemoveLabel.accesskey;" oncommand="RemoveLabel();"/>
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator class="groove"/>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdLinkProps.js b/comm/suite/editor/components/dialogs/content/EdLinkProps.js
new file mode 100644
index 0000000000..6504496a51
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdLinkProps.js
@@ -0,0 +1,331 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gActiveEditor;
+var anchorElement = null;
+var imageElement = null;
+var insertNew = false;
+var replaceExistingLink = false;
+var insertLinkAtCaret;
+var needLinkText = false;
+var href;
+var newLinkText;
+var gHNodeArray = {};
+var gHaveNamedAnchors = false;
+var gHaveHeadings = false;
+var gCanChangeHeadingSelected = true;
+var gCanChangeAnchorSelected = true;
+
+// NOTE: Use "href" instead of "a" to distinguish from Named Anchor
+// The returned node is has an "a" tagName
+var tagName = "href";
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ gActiveEditor = GetCurrentEditor();
+ if (!gActiveEditor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+ // Message was wrapped in a <label> or <div>, so actual text is a child text node
+ gDialog.linkTextCaption = document.getElementById("linkTextCaption");
+ gDialog.linkTextMessage = document.getElementById("linkTextMessage");
+ gDialog.linkTextInput = document.getElementById("linkTextInput");
+ gDialog.hrefInput = document.getElementById("hrefInput");
+ gDialog.makeRelativeLink = document.getElementById("MakeRelativeLink");
+ gDialog.AdvancedEditSection = document.getElementById("AdvancedEdit");
+
+ // See if we have a single selected image
+ imageElement = gActiveEditor.getSelectedElement("img");
+
+ if (imageElement) {
+ // Get the parent link if it exists -- more efficient than GetSelectedElement()
+ anchorElement = gActiveEditor.getElementOrParentByTagName(
+ "href",
+ imageElement
+ );
+ if (anchorElement) {
+ if (anchorElement.childNodes.length > 1) {
+ // If there are other children, then we want to break
+ // this image away by inserting a new link around it,
+ // so make a new node and copy existing attributes
+ anchorElement = anchorElement.cloneNode(false);
+ // insertNew = true;
+ replaceExistingLink = true;
+ }
+ }
+ } else {
+ // Get an anchor element if caret or
+ // entire selection is within the link.
+ anchorElement = gActiveEditor.getSelectedElement(tagName);
+
+ if (anchorElement) {
+ // Select the entire link
+ gActiveEditor.selectElement(anchorElement);
+ } else {
+ // If selection starts in a link, but extends beyond it,
+ // the user probably wants to extend existing link to new selection,
+ // so check if either end of selection is within a link
+ // POTENTIAL PROBLEM: This prevents user from selecting text in an existing
+ // link and making 2 links.
+ // Note that this isn't a problem with images, handled above
+
+ anchorElement = gActiveEditor.getElementOrParentByTagName(
+ "href",
+ gActiveEditor.selection.anchorNode
+ );
+ if (!anchorElement) {
+ anchorElement = gActiveEditor.getElementOrParentByTagName(
+ "href",
+ gActiveEditor.selection.focusNode
+ );
+ }
+
+ if (anchorElement) {
+ // But clone it for reinserting/merging around existing
+ // link that only partially overlaps the selection
+ anchorElement = anchorElement.cloneNode(false);
+ // insertNew = true;
+ replaceExistingLink = true;
+ }
+ }
+ }
+
+ if (!anchorElement) {
+ // No existing link -- create a new one
+ anchorElement = gActiveEditor.createElementWithDefaults(tagName);
+ insertNew = true;
+ // Hide message about removing existing link
+ // document.getElementById("RemoveLinkMsg").hidden = true;
+ }
+ if (!anchorElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+
+ // We insert at caret only when nothing is selected
+ insertLinkAtCaret = gActiveEditor.selection.isCollapsed;
+
+ var selectedText;
+ if (insertLinkAtCaret) {
+ // Groupbox caption:
+ gDialog.linkTextCaption.setAttribute("label", GetString("LinkText"));
+
+ // Message above input field:
+ gDialog.linkTextMessage.setAttribute("value", GetString("EnterLinkText"));
+ gDialog.linkTextMessage.setAttribute(
+ "accesskey",
+ GetString("EnterLinkTextAccessKey")
+ );
+ } else {
+ if (!imageElement) {
+ // We get here if selection is exactly around a link node
+ // Check if selection has some text - use that first
+ selectedText = GetSelectionAsText();
+ if (!selectedText) {
+ // No text, look for first image in the selection
+ var children = anchorElement.childNodes;
+ if (children) {
+ for (var i = 0; i < children.length; i++) {
+ var nodeName = children.item(i).nodeName.toLowerCase();
+ if (nodeName == "img") {
+ imageElement = children.item(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+ // Set "caption" for link source and the source text or image URL
+ if (imageElement) {
+ gDialog.linkTextCaption.setAttribute("label", GetString("LinkImage"));
+ // Link source string is the source URL of image
+ // TODO: THIS DOESN'T HANDLE MULTIPLE SELECTED IMAGES!
+ gDialog.linkTextMessage.setAttribute("value", imageElement.src);
+ } else {
+ gDialog.linkTextCaption.setAttribute("label", GetString("LinkText"));
+ if (selectedText) {
+ // Use just the first 60 characters and add "..."
+ gDialog.linkTextMessage.setAttribute(
+ "value",
+ TruncateStringAtWordEnd(
+ ReplaceWhitespace(selectedText, " "),
+ 60,
+ true
+ )
+ );
+ } else {
+ gDialog.linkTextMessage.setAttribute(
+ "value",
+ GetString("MixedSelection")
+ );
+ }
+ }
+ }
+
+ // Make a copy to use for AdvancedEdit and onSaveDefault
+ globalElement = anchorElement.cloneNode(false);
+
+ // Get the list of existing named anchors and headings
+ FillLinkMenulist(gDialog.hrefInput, gHNodeArray);
+
+ // We only need to test for this once per dialog load
+ gHaveDocumentUrl = GetDocumentBaseUrl();
+
+ // Set data for the dialog controls
+ InitDialog();
+
+ // Search for a URI pattern in the selected text
+ // as candidate href
+ selectedText = TrimString(selectedText);
+ if (!gDialog.hrefInput.value && TextIsURI(selectedText)) {
+ gDialog.hrefInput.value = selectedText;
+ }
+
+ // Set initial focus
+ if (insertLinkAtCaret) {
+ // We will be using the HREF inputbox, so text message
+ SetTextboxFocus(gDialog.linkTextInput);
+ } else {
+ SetTextboxFocus(gDialog.hrefInput);
+
+ // We will not insert a new link at caret, so remove link text input field
+ gDialog.linkTextInput.hidden = true;
+ gDialog.linkTextInput = null;
+ }
+
+ // This sets enable state on OK button
+ doEnabling();
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog() {
+ // Must use getAttribute, not "globalElement.href",
+ // or foreign chars aren't converted correctly!
+ gDialog.hrefInput.value = globalElement.getAttribute("href");
+
+ // Set "Relativize" checkbox according to current URL state
+ SetRelativeCheckbox(gDialog.makeRelativeLink);
+}
+
+function doEnabling() {
+ // We disable Ok button when there's no href text only if inserting a new link
+ var enable = insertNew
+ ? TrimString(gDialog.hrefInput.value).length > 0
+ : true;
+
+ // anon. content, so can't use SetElementEnabledById here
+ var dialogNode = document.getElementById("linkDlg");
+ dialogNode.getButton("accept").disabled = !enable;
+
+ SetElementEnabledById("AdvancedEditButton1", enable);
+}
+
+function ChangeLinkLocation() {
+ SetRelativeCheckbox(gDialog.makeRelativeLink);
+ // Set OK button enable state
+ doEnabling();
+}
+
+// Get and validate data from widgets.
+// Set attributes on globalElement so they can be accessed by AdvancedEdit()
+function ValidateData() {
+ href = TrimString(gDialog.hrefInput.value);
+ if (href) {
+ // Set the HREF directly on the editor document's anchor node
+ // or on the newly-created node if insertNew is true
+ globalElement.setAttribute("href", href);
+ } else if (insertNew) {
+ // We must have a URL to insert a new link
+ // NOTE: We accept an empty HREF on existing link to indicate removing the link
+ ShowInputErrorMessage(GetString("EmptyHREFError"));
+ return false;
+ }
+ if (gDialog.linkTextInput) {
+ // The text we will insert isn't really an attribute,
+ // but it makes sense to validate it
+ newLinkText = TrimString(gDialog.linkTextInput.value);
+ if (!newLinkText) {
+ if (href) {
+ newLinkText = href;
+ } else {
+ ShowInputErrorMessage(GetString("EmptyLinkTextError"));
+ SetTextboxFocus(gDialog.linkTextInput);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ if (href.length > 0) {
+ // Copy attributes to element we are changing or inserting
+ gActiveEditor.cloneAttributes(anchorElement, globalElement);
+
+ // Coalesce into one undo transaction
+ gActiveEditor.beginTransaction();
+
+ // Get text to use for a new link
+ if (insertLinkAtCaret) {
+ // Append the link text as the last child node
+ // of the anchor node
+ var textNode = gActiveEditor.document.createTextNode(newLinkText);
+ if (textNode) {
+ anchorElement.appendChild(textNode);
+ }
+ try {
+ gActiveEditor.insertElementAtSelection(anchorElement, false);
+ } catch (e) {
+ dump("Exception occurred in InsertElementAtSelection\n");
+ return;
+ }
+ } else if (insertNew || replaceExistingLink) {
+ // Link source was supplied by the selection,
+ // so insert a link node as parent of this
+ // (may be text, image, or other inline content)
+ try {
+ gActiveEditor.insertLinkAroundSelection(anchorElement);
+ } catch (e) {
+ dump("Exception occurred in InsertElementAtSelection\n");
+ return;
+ }
+ }
+ // Check if the link was to a heading
+ if (href in gHNodeArray) {
+ var anchorNode = gActiveEditor.createElementWithDefaults("a");
+ if (anchorNode) {
+ anchorNode.name = href.substr(1);
+
+ // Insert the anchor into the document,
+ // but don't let the transaction change the selection
+ gActiveEditor.setShouldTxnSetSelection(false);
+ gActiveEditor.insertNode(anchorNode, gHNodeArray[href], 0);
+ gActiveEditor.setShouldTxnSetSelection(true);
+ }
+ }
+ gActiveEditor.endTransaction();
+ } else if (!insertNew) {
+ // We already had a link, but empty HREF means remove it
+ EditorRemoveTextProperty("href", "");
+ }
+ SaveWindowLocation();
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml b/comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml
new file mode 100644
index 0000000000..04e147db4c
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdLinkProps.xhtml
@@ -0,0 +1,79 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % linkPropertiesDTD SYSTEM "chrome://editor/locale/EditorLinkProperties.dtd">
+%linkPropertiesDTD;
+<!ENTITY % composeEditorOverlayDTD SYSTEM "chrome://messenger/locale/messengercompose/mailComposeEditorOverlay.dtd">
+%composeEditorOverlayDTD;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog id="linkDlg" title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload = "Startup()">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdLinkProps.js"/>
+ <script src="chrome://editor/content/EdImageLinkLoader.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <vbox style="min-width: 20em">
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label id="linkTextCaption" class="header"/>
+ </hbox>
+ <vbox>
+ <label id="linkTextMessage" control="linkTextInput"/>
+ <textbox id="linkTextInput"/>
+ </vbox>
+ </groupbox>
+
+ <groupbox id="LinkURLBox">
+ <hbox class="groupbox-title">
+ <label class="header">&LinkURLBox.label;</label>
+ </hbox>
+ <vbox id="LinkLocationBox">
+ <label control="hrefInput"
+ accesskey="&LinkURLEditField2.accessKey;"
+ width="1">&LinkURLEditField2.label;</label>
+ <textbox id="hrefInput" type="text"
+ class="uri-element padded" oninput="ChangeLinkLocation();"/>
+ <hbox align="center">
+ <checkbox id="MakeRelativeLink"
+ for="hrefInput"
+ label="&makeUrlRelative.label;"
+ accesskey="&makeUrlRelative.accessKey;"
+ oncommand="MakeInputValueRelativeOrAbsolute(this);"
+ tooltiptext="&makeUrlRelative.tooltip;"/>
+ <spacer flex="1"/>
+ <button label="&chooseFileLinkButton.label;" accesskey="&chooseFileLinkButton.accessKey;"
+ oncommand="chooseLinkFile();"/>
+ </hbox>
+ </vbox>
+ <checkbox id="AttachSourceToMail"
+ hidden="true"
+ label="&attachLinkSource.label;"
+ accesskey="&attachLinkSource.accesskey;"
+ oncommand="DoAttachSourceCheckbox()"/>
+ </groupbox>
+ </vbox>
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator id="advancedSeparator" class="groove"/>
+ </vbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdListProps.js b/comm/suite/editor/components/dialogs/content/EdListProps.js
new file mode 100644
index 0000000000..8d4b78536b
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdListProps.js
@@ -0,0 +1,455 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+var gBulletStyleType = "";
+var gNumberStyleType = "";
+var gListElement;
+var gOriginalListType = "";
+var gListType = "";
+var gMixedListSelection = false;
+var gStyleType = "";
+var gOriginalStyleType = "";
+const gOnesArray = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
+const gTensArray = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"];
+const gHundredsArray = [
+ "",
+ "C",
+ "CC",
+ "CCC",
+ "CD",
+ "D",
+ "DC",
+ "DCC",
+ "DCCC",
+ "CM",
+];
+const gThousandsArray = [
+ "",
+ "M",
+ "MM",
+ "MMM",
+ "MMMM",
+ "MMMMM",
+ "MMMMMM",
+ "MMMMMMM",
+ "MMMMMMMM",
+ "MMMMMMMMM",
+];
+const gRomanDigits = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 };
+const A = "A".charCodeAt(0);
+const gArabic = "1";
+const gUpperRoman = "I";
+const gLowerRoman = "i";
+const gUpperLetters = "A";
+const gLowerLetters = "a";
+const gDecimalCSS = "decimal";
+const gUpperRomanCSS = "upper-roman";
+const gLowerRomanCSS = "lower-roman";
+const gUpperAlphaCSS = "upper-alpha";
+const gLowerAlphaCSS = "lower-alpha";
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+ gDialog.ListTypeList = document.getElementById("ListType");
+ gDialog.BulletStyleList = document.getElementById("BulletStyle");
+ gDialog.BulletStyleLabel = document.getElementById("BulletStyleLabel");
+ gDialog.StartingNumberInput = document.getElementById("StartingNumber");
+ gDialog.StartingNumberLabel = document.getElementById("StartingNumberLabel");
+ gDialog.AdvancedEditButton = document.getElementById("AdvancedEditButton1");
+ gDialog.RadioGroup = document.getElementById("RadioGroup");
+ gDialog.ChangeAllRadio = document.getElementById("ChangeAll");
+ gDialog.ChangeSelectedRadio = document.getElementById("ChangeSelected");
+
+ // Try to get an existing list(s)
+ var mixedObj = { value: null };
+ try {
+ gListType = editor.getListState(mixedObj, {}, {}, {});
+
+ // We may have mixed list and non-list, or > 1 list type in selection
+ gMixedListSelection = mixedObj.value;
+
+ // Get the list element at the anchor node
+ gListElement = editor.getElementOrParentByTagName("list", null);
+ } catch (e) {}
+
+ // The copy to use in AdvancedEdit
+ if (gListElement) {
+ globalElement = gListElement.cloneNode(false);
+ }
+
+ // Show extra options for changing entire list if we have one already.
+ gDialog.RadioGroup.collapsed = !gListElement;
+ if (gListElement) {
+ // Radio button index is persistent
+ if (gDialog.RadioGroup.getAttribute("index") == "1") {
+ gDialog.RadioGroup.selectedItem = gDialog.ChangeSelectedRadio;
+ } else {
+ gDialog.RadioGroup.selectedItem = gDialog.ChangeAllRadio;
+ }
+ }
+
+ InitDialog();
+
+ gOriginalListType = gListType;
+
+ gDialog.ListTypeList.focus();
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ // Note that if mixed, we we pay attention
+ // only to the anchor node's list type
+ // (i.e., don't confuse user with "mixed" designation)
+ if (gListElement) {
+ gListType = gListElement.nodeName.toLowerCase();
+ } else {
+ gListType = "";
+ }
+
+ gDialog.ListTypeList.value = gListType;
+ gDialog.StartingNumberInput.value = "";
+
+ // Last param = true means attribute value is case-sensitive
+ var type = globalElement
+ ? GetHTMLOrCSSStyleValue(globalElement, "type", "list-style-type")
+ : null;
+
+ if (gListType == "ul") {
+ if (type) {
+ type = type.toLowerCase();
+ gBulletStyleType = type;
+ gOriginalStyleType = type;
+ }
+ } else if (gListType == "ol") {
+ // Translate CSS property strings
+ switch (type.toLowerCase()) {
+ case gDecimalCSS:
+ type = gArabic;
+ break;
+ case gUpperRomanCSS:
+ type = gUpperRoman;
+ break;
+ case gLowerRomanCSS:
+ type = gLowerRoman;
+ break;
+ case gUpperAlphaCSS:
+ type = gUpperLetters;
+ break;
+ case gLowerAlphaCSS:
+ type = gLowerLetters;
+ break;
+ }
+ if (type) {
+ gNumberStyleType = type;
+ gOriginalStyleType = type;
+ }
+
+ // Convert attribute number to appropriate letter or roman numeral
+ gDialog.StartingNumberInput.value = ConvertStartAttrToUserString(
+ globalElement.getAttribute("start"),
+ type
+ );
+ }
+ BuildBulletStyleList();
+}
+
+// Convert attribute number to appropriate letter or roman numeral
+function ConvertStartAttrToUserString(startAttr, type) {
+ switch (type) {
+ case gUpperRoman:
+ startAttr = ConvertArabicToRoman(startAttr);
+ break;
+ case gLowerRoman:
+ startAttr = ConvertArabicToRoman(startAttr).toLowerCase();
+ break;
+ case gUpperLetters:
+ startAttr = ConvertArabicToLetters(startAttr);
+ break;
+ case gLowerLetters:
+ startAttr = ConvertArabicToLetters(startAttr).toLowerCase();
+ break;
+ }
+ return startAttr;
+}
+
+function BuildBulletStyleList() {
+ gDialog.BulletStyleList.removeAllItems();
+ var label;
+
+ if (gListType == "ul") {
+ gDialog.BulletStyleList.removeAttribute("disabled");
+ gDialog.BulletStyleLabel.removeAttribute("disabled");
+ gDialog.StartingNumberInput.setAttribute("disabled", "true");
+ gDialog.StartingNumberLabel.setAttribute("disabled", "true");
+
+ label = GetString("BulletStyle");
+
+ gDialog.BulletStyleList.appendItem(GetString("Automatic"), "");
+ gDialog.BulletStyleList.appendItem(GetString("SolidCircle"), "disc");
+ gDialog.BulletStyleList.appendItem(GetString("OpenCircle"), "circle");
+ gDialog.BulletStyleList.appendItem(GetString("SolidSquare"), "square");
+
+ gDialog.BulletStyleList.value = gBulletStyleType;
+ } else if (gListType == "ol") {
+ gDialog.BulletStyleList.removeAttribute("disabled");
+ gDialog.BulletStyleLabel.removeAttribute("disabled");
+ gDialog.StartingNumberInput.removeAttribute("disabled");
+ gDialog.StartingNumberLabel.removeAttribute("disabled");
+ label = GetString("NumberStyle");
+
+ gDialog.BulletStyleList.appendItem(GetString("Automatic"), "");
+ gDialog.BulletStyleList.appendItem(GetString("Style_1"), gArabic);
+ gDialog.BulletStyleList.appendItem(GetString("Style_I"), gUpperRoman);
+ gDialog.BulletStyleList.appendItem(GetString("Style_i"), gLowerRoman);
+ gDialog.BulletStyleList.appendItem(GetString("Style_A"), gUpperLetters);
+ gDialog.BulletStyleList.appendItem(GetString("Style_a"), gLowerLetters);
+
+ gDialog.BulletStyleList.value = gNumberStyleType;
+ } else {
+ gDialog.BulletStyleList.setAttribute("disabled", "true");
+ gDialog.BulletStyleLabel.setAttribute("disabled", "true");
+ gDialog.StartingNumberInput.setAttribute("disabled", "true");
+ gDialog.StartingNumberLabel.setAttribute("disabled", "true");
+ }
+
+ // Disable advanced edit button if changing to "normal"
+ if (gListType) {
+ gDialog.AdvancedEditButton.removeAttribute("disabled");
+ } else {
+ gDialog.AdvancedEditButton.setAttribute("disabled", "true");
+ }
+
+ if (label) {
+ gDialog.BulletStyleLabel.setAttribute("label", label);
+ }
+}
+
+function SelectListType() {
+ // Each list type is stored in the "value" of each menuitem
+ var NewType = gDialog.ListTypeList.value;
+
+ if (NewType == "ol") {
+ SetTextboxFocus(gDialog.StartingNumberInput);
+ }
+
+ if (gListType != NewType) {
+ gListType = NewType;
+
+ // Create a newlist object for Advanced Editing
+ try {
+ if (gListType) {
+ globalElement = GetCurrentEditor().createElementWithDefaults(gListType);
+ }
+ } catch (e) {}
+
+ BuildBulletStyleList();
+ }
+}
+
+function SelectBulletStyle() {
+ // Save the selected index so when user changes
+ // list style, restore index to associated list
+ // Each bullet or number type is stored in the "value" of each menuitem
+ if (gListType == "ul") {
+ gBulletStyleType = gDialog.BulletStyleList.value;
+ } else if (gListType == "ol") {
+ var type = gDialog.BulletStyleList.value;
+ if (gNumberStyleType != type) {
+ // Convert existing input value to attr number first,
+ // then convert to the appropriate format for the newly-selected
+ gDialog.StartingNumberInput.value = ConvertStartAttrToUserString(
+ ConvertUserStringToStartAttr(gNumberStyleType),
+ type
+ );
+
+ gNumberStyleType = type;
+ SetTextboxFocus(gDialog.StartingNumberInput);
+ }
+ }
+}
+
+function ValidateData() {
+ gBulletStyleType = gDialog.BulletStyleList.value;
+ // globalElement should already be of the correct type
+
+ if (globalElement) {
+ var editor = GetCurrentEditor();
+ if (gListType == "ul") {
+ if (gBulletStyleType && gDialog.ChangeAllRadio.selected) {
+ globalElement.setAttribute("type", gBulletStyleType);
+ } else {
+ try {
+ editor.removeAttributeOrEquivalent(globalElement, "type", true);
+ } catch (e) {}
+ }
+ } else if (gListType == "ol") {
+ if (gBulletStyleType) {
+ globalElement.setAttribute("type", gBulletStyleType);
+ } else {
+ try {
+ editor.removeAttributeOrEquivalent(globalElement, "type", true);
+ } catch (e) {}
+ }
+
+ var startingNumber = ConvertUserStringToStartAttr(gBulletStyleType);
+ if (startingNumber) {
+ globalElement.setAttribute("start", startingNumber);
+ } else {
+ globalElement.removeAttribute("start");
+ }
+ }
+ }
+ return true;
+}
+
+function ConvertUserStringToStartAttr(type) {
+ var startingNumber = TrimString(gDialog.StartingNumberInput.value);
+
+ switch (type) {
+ case gUpperRoman:
+ case gLowerRoman:
+ // If the input isn't an integer, assume it's a roman numeral. Convert it.
+ if (!Number(startingNumber)) {
+ startingNumber = ConvertRomanToArabic(startingNumber);
+ }
+ break;
+ case gUpperLetters:
+ case gLowerLetters:
+ // Get the number equivalent of the letters
+ if (!Number(startingNumber)) {
+ startingNumber = ConvertLettersToArabic(startingNumber);
+ }
+ break;
+ }
+ return startingNumber;
+}
+
+function ConvertRomanToArabic(num) {
+ num = num.toUpperCase();
+ if (num && !/[^MDCLXVI]/i.test(num)) {
+ var Arabic = 0;
+ var last_digit = 1000;
+ for (var i = 0; i < num.length; i++) {
+ var digit = gRomanDigits[num.charAt(i)];
+ if (last_digit < digit) {
+ Arabic -= 2 * last_digit;
+ }
+
+ last_digit = digit;
+ Arabic += last_digit;
+ }
+ return Arabic;
+ }
+
+ return "";
+}
+
+function ConvertArabicToRoman(num) {
+ if (/^\d{1,4}$/.test(num)) {
+ var digits = ("000" + num).substr(-4);
+ return (
+ gThousandsArray[digits.charAt(0)] +
+ gHundredsArray[digits.charAt(1)] +
+ gTensArray[digits.charAt(2)] +
+ gOnesArray[digits.charAt(3)]
+ );
+ }
+ return "";
+}
+
+function ConvertLettersToArabic(letters) {
+ letters = letters.toUpperCase();
+ if (!letters || /[^A-Z]/.test(letters)) {
+ return "";
+ }
+
+ var num = 0;
+ for (var i = 0; i < letters.length; i++) {
+ num = num * 26 + letters.charCodeAt(i) - A + 1;
+ }
+ return num;
+}
+
+function ConvertArabicToLetters(num) {
+ var letters = "";
+ while (num) {
+ num--;
+ letters = String.fromCharCode(A + (num % 26)) + letters;
+ num = Math.floor(num / 26);
+ }
+ return letters;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ // Coalesce into one undo transaction
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ var changeEntireList =
+ gDialog.RadioGroup.selectedItem == gDialog.ChangeAllRadio;
+
+ // Remember which radio button was selected
+ if (gListElement) {
+ gDialog.RadioGroup.setAttribute("index", changeEntireList ? "0" : "1");
+ }
+
+ var changeList;
+ if (gListElement && gDialog.ChangeAllRadio.selected) {
+ changeList = true;
+ } else {
+ changeList =
+ gMixedListSelection ||
+ gListType != gOriginalListType ||
+ gBulletStyleType != gOriginalStyleType;
+ }
+ if (changeList) {
+ try {
+ if (gListType) {
+ editor.makeOrChangeList(
+ gListType,
+ changeEntireList,
+ gBulletStyleType != gOriginalStyleType ? gBulletStyleType : null
+ );
+
+ // Get the new list created:
+ gListElement = editor.getElementOrParentByTagName(gListType, null);
+
+ editor.cloneAttributes(gListElement, globalElement);
+ } else {
+ // Remove all existing lists
+ if (gListElement && changeEntireList) {
+ editor.selectElement(gListElement);
+ }
+
+ editor.removeList("ol");
+ editor.removeList("ul");
+ editor.removeList("dl");
+ }
+ } catch (e) {}
+ }
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdListProps.xhtml b/comm/suite/editor/components/dialogs/content/EdListProps.xhtml
new file mode 100644
index 0000000000..cae9829c97
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdListProps.xhtml
@@ -0,0 +1,73 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edListProperties SYSTEM "chrome://editor/locale/EditorListProperties.dtd">
+%edListProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdListProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&ListType.label;</label>
+ </hbox>
+ <menulist id="ListType" oncommand="SelectListType()">
+ <menupopup>
+ <menuitem label="&none.value;"/>
+ <menuitem value="ul" label="&bulletList.value;"/>
+ <menuitem value="ol" label="&numberList.value;"/>
+ <menuitem value="dl" label="&definitionList.value;"/>
+ </menupopup>
+ </menulist>
+ </groupbox>
+ <spacer class="spacer"/>
+
+ <!-- message text and list items are set in JS
+ text value should be identical to string with id=BulletStyle in editor.properties
+ -->
+ <groupbox flex="1">
+ <hbox class="groupbox-title">
+ <label id="BulletStyleLabel" class="header">&bulletStyle.label;</label>
+ </hbox>
+ <menulist class="MinWidth10em" id="BulletStyle" oncommand="SelectBulletStyle()">
+ <menupopup/>
+ </menulist>
+ <spacer class="spacer"/>
+ <hbox>
+ <label id="StartingNumberLabel" control="StartingNumber"
+ value="&startingNumber.label;" accesskey="&startingNumber.accessKey;"/>
+ <textbox class="narrow" id="StartingNumber"/>
+ <spacer/>
+ </hbox>
+ </groupbox>
+ <radiogroup id="RadioGroup" index="0" persist="index">
+ <radio id="ChangeAll" label="&changeEntireListRadio.label;" accesskey="&changeEntireListRadio.accessKey;"/>
+ <radio id="ChangeSelected" label="&changeSelectedRadio.label;" accesskey="&changeSelectedRadio.accessKey;"/>
+ </radiogroup>
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator id="advancedSeparator" class="groove"/>
+ </vbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js
new file mode 100644
index 0000000000..0433e58872
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.js
@@ -0,0 +1,159 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gInsertNew = true;
+var gAnchorElement = null;
+var gOriginalName = "";
+const kTagName = "anchor";
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog.OkButton = document.documentElement.getButton("accept");
+ gDialog.NameInput = document.getElementById("nameInput");
+
+ // Get a single selected element of the desired type
+ gAnchorElement = editor.getSelectedElement(kTagName);
+
+ if (gAnchorElement) {
+ // We found an element and don't need to insert one
+ gInsertNew = false;
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = gAnchorElement.cloneNode(false);
+ gOriginalName = ConvertToCDATAString(gAnchorElement.name);
+ } else {
+ gInsertNew = true;
+ // We don't have an element selected,
+ // so create one with default attributes
+ gAnchorElement = editor.createElementWithDefaults(kTagName);
+ if (gAnchorElement) {
+ // Use the current selection as suggested name
+ var name = GetSelectionAsText();
+ // Get 40 characters of the selected text and don't add "...",
+ // replace whitespace with "_" and strip non-word characters
+ name = ConvertToCDATAString(TruncateStringAtWordEnd(name, 40, false));
+ // Be sure the name is unique to the document
+ if (AnchorNameExists(name)) {
+ name += "_";
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = gAnchorElement.cloneNode(false);
+ globalElement.setAttribute("name", name);
+ }
+ }
+ if (!gAnchorElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+
+ InitDialog();
+
+ DoEnabling();
+ SetTextboxFocus(gDialog.NameInput);
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ gDialog.NameInput.value = globalElement.getAttribute("name");
+}
+
+function ChangeName() {
+ if (gDialog.NameInput.value.length > 0) {
+ // Replace spaces with "_" and strip other non-URL characters
+ // Note: we could use ConvertAndEscape, but then we'd
+ // have to UnEscapeAndConvert beforehand - too messy!
+ gDialog.NameInput.value = ConvertToCDATAString(gDialog.NameInput.value);
+ }
+ DoEnabling();
+}
+
+function DoEnabling() {
+ var enable = gDialog.NameInput.value.length > 0;
+ SetElementEnabled(gDialog.OkButton, enable);
+ SetElementEnabledById("AdvancedEditButton1", enable);
+}
+
+function AnchorNameExists(name) {
+ var anchorList;
+ try {
+ anchorList = GetCurrentEditor().document.anchors;
+ } catch (e) {}
+
+ if (anchorList) {
+ for (var i = 0; i < anchorList.length; i++) {
+ if (anchorList[i].name == name) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Get and validate data from widgets.
+// Set attributes on globalElement so they can be accessed by AdvancedEdit()
+function ValidateData() {
+ var name = TrimString(gDialog.NameInput.value);
+ if (!name) {
+ ShowInputErrorMessage(GetString("MissingAnchorNameError"));
+ SetTextboxFocus(gDialog.NameInput);
+ return false;
+ }
+ // Replace spaces with "_" and strip other characters
+ // Note: we could use ConvertAndEscape, but then we'd
+ // have to UnConverAndEscape beforehand - too messy!
+ name = ConvertToCDATAString(name);
+
+ if (gOriginalName != name && AnchorNameExists(name)) {
+ ShowInputErrorMessage(
+ GetString("DuplicateAnchorNameError").replace(/%name%/, name)
+ );
+ SetTextboxFocus(gDialog.NameInput);
+ return false;
+ }
+ globalElement.name = name;
+
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ if (gOriginalName != globalElement.name) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ try {
+ // "false" = don't delete selected text when inserting
+ if (gInsertNew) {
+ // We must insert element before copying CSS style attribute,
+ // but we must set the name else it won't insert at all
+ gAnchorElement.name = globalElement.name;
+ editor.insertElementAtSelection(gAnchorElement, false);
+ }
+
+ // Copy attributes to element we are changing or inserting
+ editor.cloneAttributes(gAnchorElement, globalElement);
+ } catch (e) {}
+
+ editor.endTransaction();
+ }
+ SaveWindowLocation();
+ return;
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml
new file mode 100644
index 0000000000..3a38256222
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdNamedAnchorProps.xhtml
@@ -0,0 +1,43 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edNamedAnchorProperties SYSTEM "chrome://editor/locale/EdNamedAnchorProperties.dtd">
+%edNamedAnchorProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdNamedAnchorProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <label control="nameInput"
+ value="&anchorNameEditField.label;"
+ accesskey="&anchorNameEditField.accessKey;"/>
+ <textbox class="MinWidth20em" id="nameInput" oninput="ChangeName()"
+ tooltiptext="&nameInput.tooltip;"/>
+ <spacer class="spacer"/>
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator id="advancedSeparator" class="groove"/>
+ </vbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdPageProps.js b/comm/suite/editor/components/dialogs/content/EdPageProps.js
new file mode 100644
index 0000000000..568eff66ec
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdPageProps.js
@@ -0,0 +1,159 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gNewTitle = "";
+var gAuthor = "";
+var gDescription = "";
+var gAuthorElement;
+var gDescriptionElement;
+var gInsertNewAuthor = false;
+var gInsertNewDescription = false;
+var gTitleWasEdited = false;
+var gAuthorWasEdited = false;
+var gDescWasEdited = false;
+
+// Cancel() is in EdDialogCommon.js
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ gDialog.PageLocation = document.getElementById("PageLocation");
+ gDialog.PageModDate = document.getElementById("PageModDate");
+ gDialog.TitleInput = document.getElementById("TitleInput");
+ gDialog.AuthorInput = document.getElementById("AuthorInput");
+ gDialog.DescriptionInput = document.getElementById("DescriptionInput");
+
+ // Default string for new page is set from DTD string in XUL,
+ // so set only if not new doc URL
+ var location = GetDocumentUrl();
+ var lastmodString = GetString("Unknown");
+
+ if (!IsUrlAboutBlank(location)) {
+ // NEVER show username and password in clear text
+ gDialog.PageLocation.setAttribute("value", StripPassword(location));
+
+ // Get last-modified file date+time
+ // TODO: Convert this to local time?
+ var lastmod;
+ try {
+ lastmod = editor.document.lastModified; // get string of last modified date
+ } catch (e) {}
+ // Convert modified string to date (0 = unknown date or January 1, 1970 GMT)
+ if (Date.parse(lastmod)) {
+ try {
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "long",
+ timeStyle: "short",
+ });
+
+ var lastModDate = new Date();
+ lastModDate.setTime(Date.parse(lastmod));
+ lastmodString = dateTimeFormatter.format(lastModDate);
+ } catch (e) {}
+ }
+ }
+ gDialog.PageModDate.value = lastmodString;
+
+ gAuthorElement = GetMetaElementByAttribute("name", "author");
+ if (!gAuthorElement) {
+ gAuthorElement = CreateMetaElementWithAttribute("name", "author");
+ if (!gAuthorElement) {
+ window.close();
+ return;
+ }
+ gInsertNewAuthor = true;
+ }
+
+ gDescriptionElement = GetMetaElementByAttribute("name", "description");
+ if (!gDescriptionElement) {
+ gDescriptionElement = CreateMetaElementWithAttribute("name", "description");
+ if (!gDescriptionElement) {
+ window.close();
+ }
+
+ gInsertNewDescription = true;
+ }
+
+ InitDialog();
+
+ SetTextboxFocus(gDialog.TitleInput);
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ gDialog.TitleInput.value = GetDocumentTitle();
+
+ var gAuthor = TrimString(gAuthorElement.getAttribute("content"));
+ if (!gAuthor) {
+ // Fill in with value from editor prefs
+ gAuthor = Services.prefs.getCharPref("editor.author");
+ }
+ gDialog.AuthorInput.value = gAuthor;
+ gDialog.DescriptionInput.value = gDescriptionElement.getAttribute("content");
+}
+
+function TextboxChanged(ID) {
+ switch (ID) {
+ case "TitleInput":
+ gTitleWasEdited = true;
+ break;
+ case "AuthorInput":
+ gAuthorWasEdited = true;
+ break;
+ case "DescriptionInput":
+ gDescWasEdited = true;
+ break;
+ }
+}
+
+function ValidateData() {
+ gNewTitle = TrimString(gDialog.TitleInput.value);
+ gAuthor = TrimString(gDialog.AuthorInput.value);
+ gDescription = TrimString(gDialog.DescriptionInput.value);
+ return true;
+}
+
+function onAccept(event) {
+ if (ValidateData()) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ // Set title contents even if string is empty
+ // because TITLE is a required HTML element
+ if (gTitleWasEdited) {
+ SetDocumentTitle(gNewTitle);
+ }
+
+ if (gAuthorWasEdited) {
+ SetMetaElementContent(gAuthorElement, gAuthor, gInsertNewAuthor, false);
+ }
+
+ if (gDescWasEdited) {
+ SetMetaElementContent(
+ gDescriptionElement,
+ gDescription,
+ gInsertNewDescription,
+ false
+ );
+ }
+
+ editor.endTransaction();
+
+ SaveWindowLocation();
+ return; // do close the window
+ }
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdPageProps.xhtml b/comm/suite/editor/components/dialogs/content/EdPageProps.xhtml
new file mode 100644
index 0000000000..4891baa04a
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdPageProps.xhtml
@@ -0,0 +1,50 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPageProperties.dtd">
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdPageProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+ <grid>
+ <columns><column flex="1"/><column flex="2"/></columns>
+ <rows>
+ <row>
+ <label value="&location.label;"/>
+ <label value="&locationNewPage.label;" id="PageLocation"/>
+ </row>
+ <row>
+ <label value="&lastModified.label;"/>
+ <label id="PageModDate"/>
+ </row>
+ <spacer class="spacer"/>
+ <row align="center">
+ <label value="&titleInput.label;" accesskey="&titleInput.accessKey;" control="TitleInput"/>
+ <textbox class="MinWidth20em" id="TitleInput" oninput="TextboxChanged(this.id)"/>
+ </row>
+ <row align="center">
+ <label value="&authorInput.label;" accesskey="&authorInput.accessKey;" control="AuthorInput"/>
+ <textbox class="MinWidth20em" id="AuthorInput" oninput="TextboxChanged(this.id)"/>
+ </row>
+ <row align="center">
+ <label value="&descriptionInput.label;" accesskey="&descriptionInput.accessKey;" control="DescriptionInput"/>
+ <textbox class="MinWidth20em" id="DescriptionInput" oninput="TextboxChanged(this.id)"/>
+ </row>
+ </rows>
+ </grid>
+ <spacer class="bigspacer"/>
+ <label value="&EditHEADSource1.label;"/>
+ <description class="wrap" flex="1">&EditHEADSource2.label;</description>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdReplace.js b/comm/suite/editor/components/dialogs/content/EdReplace.js
new file mode 100644
index 0000000000..c0daea29de
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdReplace.js
@@ -0,0 +1,382 @@
+/* -*- 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var gReplaceDialog; // Quick access to document/form elements.
+var gFindInst; // nsIWebBrowserFind that we're going to use
+var gFindService; // Global service which remembers find params
+var gEditor; // the editor we're using
+
+document.addEventListener("dialogaccept", event => {
+ onFindNext();
+ event.preventDefault();
+});
+
+function initDialogObject() {
+ // Create gReplaceDialog object and initialize.
+ gReplaceDialog = {};
+ gReplaceDialog.findInput = document.getElementById("dialog.findInput");
+ gReplaceDialog.replaceInput = document.getElementById("dialog.replaceInput");
+ gReplaceDialog.caseSensitive = document.getElementById(
+ "dialog.caseSensitive"
+ );
+ gReplaceDialog.wrap = document.getElementById("dialog.wrap");
+ gReplaceDialog.searchBackwards = document.getElementById(
+ "dialog.searchBackwards"
+ );
+ gReplaceDialog.findNext = document.getElementById("findNext");
+ gReplaceDialog.replace = document.getElementById("replace");
+ gReplaceDialog.replaceAndFind = document.getElementById("replaceAndFind");
+ gReplaceDialog.replaceAll = document.getElementById("replaceAll");
+}
+
+function loadDialog() {
+ // Set initial dialog field contents.
+ // Set initial dialog field contents. Use the gFindInst attributes first,
+ // this is necessary for window.find()
+ gReplaceDialog.findInput.value = gFindInst.searchString
+ ? gFindInst.searchString
+ : gFindService.searchString;
+ gReplaceDialog.replaceInput.value = gFindService.replaceString;
+ gReplaceDialog.caseSensitive.checked = gFindInst.matchCase
+ ? gFindInst.matchCase
+ : gFindService.matchCase;
+ gReplaceDialog.wrap.checked = gFindInst.wrapFind
+ ? gFindInst.wrapFind
+ : gFindService.wrapFind;
+ gReplaceDialog.searchBackwards.checked = gFindInst.findBackwards
+ ? gFindInst.findBackwards
+ : gFindService.findBackwards;
+
+ doEnabling();
+}
+
+function onLoad() {
+ // Get the xul <editor> element:
+ var editorElement = window.arguments[0];
+
+ // If we don't get the editor, then we won't allow replacing.
+ gEditor = editorElement.getEditor(editorElement.contentWindow);
+ if (!gEditor) {
+ window.close();
+ return;
+ }
+
+ // Get the nsIWebBrowserFind service:
+ gFindInst = editorElement.webBrowserFind;
+
+ try {
+ // get the find service, which stores global find state
+ gFindService = Cc["@mozilla.org/find/find_service;1"].getService(
+ Ci.nsIFindService
+ );
+ } catch (e) {
+ dump("No find service!\n");
+ gFindService = 0;
+ }
+
+ // Init gReplaceDialog.
+ initDialogObject();
+
+ // Change "OK" to "Find".
+ // dialog.find.label = document.getElementById("fBLT").getAttribute("label");
+
+ // Fill dialog.
+ loadDialog();
+
+ if (gReplaceDialog.findInput.value) {
+ gReplaceDialog.findInput.select();
+ } else {
+ gReplaceDialog.findInput.focus();
+ }
+}
+
+function saveFindData() {
+ // Set data attributes per user input.
+ if (gFindService) {
+ gFindService.searchString = gReplaceDialog.findInput.value;
+ gFindService.matchCase = gReplaceDialog.caseSensitive.checked;
+ gFindService.wrapFind = gReplaceDialog.wrap.checked;
+ gFindService.findBackwards = gReplaceDialog.searchBackwards.checked;
+ }
+}
+
+function setUpFindInst() {
+ gFindInst.searchString = gReplaceDialog.findInput.value;
+ gFindInst.matchCase = gReplaceDialog.caseSensitive.checked;
+ gFindInst.wrapFind = gReplaceDialog.wrap.checked;
+ gFindInst.findBackwards = gReplaceDialog.searchBackwards.checked;
+}
+
+function onFindNext() {
+ // Transfer dialog contents to the find service.
+ saveFindData();
+ // set up the find instance
+ setUpFindInst();
+
+ // Search.
+ var result = gFindInst.findNext();
+
+ if (!result) {
+ var bundle = document.getElementById("findBundle");
+ Services.prompt.alert(
+ window,
+ GetString("Alert"),
+ bundle.getString("notFoundWarning")
+ );
+ SetTextboxFocus(gReplaceDialog.findInput);
+ gReplaceDialog.findInput.select();
+ gReplaceDialog.findInput.focus();
+ return false;
+ }
+ return true;
+}
+
+function onReplace() {
+ if (!gEditor) {
+ return false;
+ }
+
+ // Does the current selection match the find string?
+ var selection = gEditor.selection;
+
+ var selStr = selection.toString();
+ var specStr = gReplaceDialog.findInput.value;
+ if (!gReplaceDialog.caseSensitive.checked) {
+ selStr = selStr.toLowerCase();
+ specStr = specStr.toLowerCase();
+ }
+ // Unfortunately, because of whitespace we can't just check
+ // whether (selStr == specStr), but have to loop ourselves.
+ // N chars of whitespace in specStr can match any M >= N in selStr.
+ var matches = true;
+ var specLen = specStr.length;
+ var selLen = selStr.length;
+ if (selLen < specLen) {
+ matches = false;
+ } else {
+ var specArray = specStr.match(/\S+|\s+/g);
+ var selArray = selStr.match(/\S+|\s+/g);
+ if (specArray.length != selArray.length) {
+ matches = false;
+ } else {
+ for (var i = 0; i < selArray.length; i++) {
+ if (selArray[i] != specArray[i]) {
+ if (/\S/.test(selArray[i][0]) || /\S/.test(specArray[i][0])) {
+ // not a space chunk -- match fails
+ matches = false;
+ break;
+ } else if (selArray[i].length < specArray[i].length) {
+ // if it's a space chunk then we only care that sel be
+ // at least as long as spec
+ matches = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // If the current selection doesn't match the pattern,
+ // then we want to find the next match, but not do the replace.
+ // That's what most other apps seem to do.
+ // So here, just return.
+ if (!matches) {
+ return false;
+ }
+
+ // Transfer dialog contents to the find service.
+ saveFindData();
+
+ // For reverse finds, need to remember the caret position
+ // before current selection
+ var newRange;
+ if (gReplaceDialog.searchBackwards.checked && selection.rangeCount > 0) {
+ newRange = selection.getRangeAt(0).cloneRange();
+ newRange.collapse(true);
+ }
+
+ // nsPlaintextEditor::InsertText fails if the string is empty,
+ // so make that a special case:
+ var replStr = gReplaceDialog.replaceInput.value;
+ if (replStr == "") {
+ gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip);
+ } else {
+ gEditor.insertText(replStr);
+ }
+
+ // For reverse finds, need to move caret just before the replaced text
+ if (gReplaceDialog.searchBackwards.checked && newRange) {
+ gEditor.selection.removeAllRanges();
+ gEditor.selection.addRange(newRange);
+ }
+
+ return true;
+}
+
+function onReplaceAll() {
+ if (!gEditor) {
+ return;
+ }
+
+ var findStr = gReplaceDialog.findInput.value;
+ var repStr = gReplaceDialog.replaceInput.value;
+
+ // Transfer dialog contents to the find service.
+ saveFindData();
+
+ var finder = Cc["@mozilla.org/embedcomp/rangefind;1"]
+ .createInstance()
+ .QueryInterface(Ci.nsIFind);
+
+ finder.caseSensitive = gReplaceDialog.caseSensitive.checked;
+ finder.findBackwards = gReplaceDialog.searchBackwards.checked;
+
+ // We want the whole operation to be undoable in one swell foop,
+ // so start a transaction:
+ gEditor.beginTransaction();
+
+ // and to make sure we close the transaction, guard against exceptions:
+ try {
+ // Make a range containing the current selection,
+ // so we don't go past it when we wrap.
+ var selection = gEditor.selection;
+ var selecRange;
+ if (selection.rangeCount > 0) {
+ selecRange = selection.getRangeAt(0);
+ }
+ var origRange = selecRange.cloneRange();
+
+ // We'll need a range for the whole document:
+ var wholeDocRange = gEditor.document.createRange();
+ var rootNode = gEditor.rootElement;
+ wholeDocRange.selectNodeContents(rootNode);
+
+ // And start and end points:
+ var endPt = gEditor.document.createRange();
+
+ if (gReplaceDialog.searchBackwards.checked) {
+ endPt.setStart(wholeDocRange.startContainer, wholeDocRange.startOffset);
+ endPt.setEnd(wholeDocRange.startContainer, wholeDocRange.startOffset);
+ } else {
+ endPt.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset);
+ endPt.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset);
+ }
+
+ // Find and replace from here to end (start) of document:
+ var foundRange;
+ var searchRange = wholeDocRange.cloneRange();
+ while (
+ (foundRange = finder.Find(findStr, searchRange, selecRange, endPt)) !=
+ null
+ ) {
+ gEditor.selection.removeAllRanges();
+ gEditor.selection.addRange(foundRange);
+
+ // The editor will leave the caret at the end of the replaced text.
+ // For reverse finds, we need it at the beginning,
+ // so save the next position now.
+ if (gReplaceDialog.searchBackwards.checked) {
+ selecRange = foundRange.cloneRange();
+ selecRange.setEnd(selecRange.startContainer, selecRange.startOffset);
+ }
+
+ // nsPlaintextEditor::InsertText fails if the string is empty,
+ // so make that a special case:
+ if (repStr == "") {
+ gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip);
+ } else {
+ gEditor.insertText(repStr);
+ }
+
+ // If we're going forward, we didn't save selecRange before, so do it now:
+ if (!gReplaceDialog.searchBackwards.checked) {
+ selection = gEditor.selection;
+ if (selection.rangeCount <= 0) {
+ gEditor.endTransaction();
+ return;
+ }
+ selecRange = selection.getRangeAt(0).cloneRange();
+ }
+ }
+
+ // If no wrapping, then we're done
+ if (!gReplaceDialog.wrap.checked) {
+ gEditor.endTransaction();
+ return;
+ }
+
+ // If wrapping, find from start/end of document back to start point.
+ if (gReplaceDialog.searchBackwards.checked) {
+ // Collapse origRange to end
+ origRange.setStart(origRange.endContainer, origRange.endOffset);
+ // Set current position to document end
+ selecRange.setEnd(wholeDocRange.endContainer, wholeDocRange.endOffset);
+ selecRange.setStart(wholeDocRange.endContainer, wholeDocRange.endOffset);
+ } else {
+ // Collapse origRange to start
+ origRange.setEnd(origRange.startContainer, origRange.startOffset);
+ // Set current position to document start
+ selecRange.setStart(
+ wholeDocRange.startContainer,
+ wholeDocRange.startOffset
+ );
+ selecRange.setEnd(
+ wholeDocRange.startContainer,
+ wholeDocRange.startOffset
+ );
+ }
+
+ while (
+ (foundRange = finder.Find(
+ findStr,
+ wholeDocRange,
+ selecRange,
+ origRange
+ )) != null
+ ) {
+ gEditor.selection.removeAllRanges();
+ gEditor.selection.addRange(foundRange);
+
+ // Save insert point for backward case
+ if (gReplaceDialog.searchBackwards.checked) {
+ selecRange = foundRange.cloneRange();
+ selecRange.setEnd(selecRange.startContainer, selecRange.startOffset);
+ }
+
+ // nsPlaintextEditor::InsertText fails if the string is empty,
+ // so make that a special case:
+ if (repStr == "") {
+ gEditor.deleteSelection(gEditor.eNone, gEditor.eStrip);
+ } else {
+ gEditor.insertText(repStr);
+ }
+
+ // Get insert point for forward case
+ if (!gReplaceDialog.searchBackwards.checked) {
+ selection = gEditor.selection;
+ if (selection.rangeCount <= 0) {
+ gEditor.endTransaction();
+ return;
+ }
+ selecRange = selection.getRangeAt(0);
+ }
+ }
+ } catch (e) {}
+
+ gEditor.endTransaction();
+}
+
+function doEnabling() {
+ var findStr = gReplaceDialog.findInput.value;
+ gReplaceDialog.enabled = findStr;
+ gReplaceDialog.findNext.disabled = !findStr;
+ gReplaceDialog.replace.disabled = !findStr;
+ gReplaceDialog.replaceAndFind.disabled = !findStr;
+ gReplaceDialog.replaceAll.disabled = !findStr;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdReplace.xhtml b/comm/suite/editor/components/dialogs/content/EdReplace.xhtml
new file mode 100644
index 0000000000..54a9d81ba3
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdReplace.xhtml
@@ -0,0 +1,65 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorReplace.dtd">
+
+<dialog id="replaceDlg" title="&replaceDialog.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ persist="screenX screenY"
+ buttons="cancel"
+ onload="onLoad()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdReplace.js"/>
+ <stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
+
+ <hbox>
+ <vbox>
+ <spacer class="spacer"/>
+ <grid align="start">
+ <columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label value="&findField.label;" accesskey="&findField.accesskey;" control="dialog.findInput"/>
+ <textbox id="dialog.findInput" oninput="doEnabling();"/>
+ </row>
+ <row align="center">
+ <label value="&replaceField.label;" accesskey="&replaceField.accesskey;" control="dialog.replaceInput"/>
+ <textbox id="dialog.replaceInput" oninput="doEnabling();"/>
+ </row>
+ <row align="start">
+ <spacer/>
+ <vbox align="start">
+ <spacer class="bigspacer"/>
+ <checkbox id="dialog.caseSensitive" label="&caseSensitiveCheckbox.label;"
+ accesskey="&caseSensitiveCheckbox.accesskey;"/>
+ <checkbox id="dialog.wrap" label="&wrapCheckbox.label;"
+ accesskey="&wrapCheckbox.accesskey;"/>
+ <checkbox id="dialog.searchBackwards" label="&backwardsCheckbox.label;"
+ accesskey="&backwardsCheckbox.accesskey;"/>
+ </vbox>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ <vbox>
+ <button id="findNext" label="&findNextButton.label;" accesskey="&findNextButton.accesskey;"
+ oncommand="onFindNext();" default="true"/>
+ <button id="replace" label="&replaceButton.label;" accesskey="&replaceButton.accesskey;"
+ oncommand="onReplace();"/>
+ <button id="replaceAndFind" label="&replaceAndFindButton.label;"
+ accesskey="&replaceAndFindButton.accesskey;" oncommand="onReplace(); onFindNext();"/>
+ <button id="replaceAll" label="&replaceAllButton.label;"
+ accesskey="&replaceAllButton.accesskey;" oncommand="onReplaceAll();"/>
+ <button dlgtype="cancel" label="&closeButton.label;" accesskey="&closeButton.accesskey;"/>
+ </vbox>
+ </hbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdSelectProps.js b/comm/suite/editor/components/dialogs/content/EdSelectProps.js
new file mode 100644
index 0000000000..c03fd73a67
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdSelectProps.js
@@ -0,0 +1,770 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Global variables
+
+var hasValue;
+var oldValue;
+var insertNew;
+var itemArray;
+var theTree;
+var treeSelection;
+var selectElement;
+var currentItem = null;
+var selectedOption = null;
+var selectedOptionCount = 0;
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+// Utility functions
+
+function getParentIndex(index) {
+ switch (itemArray[index].level) {
+ case 0:
+ return -1;
+ case 1:
+ return 0;
+ }
+ // eslint-disable-next-line curly
+ while (itemArray[--index].level > 1);
+ return index;
+}
+
+function UpdateSelectMultiple() {
+ if (selectedOptionCount > 1) {
+ gDialog.selectMultiple.checked = true;
+ gDialog.selectMultiple.disabled = true;
+ } else {
+ gDialog.selectMultiple.disabled = false;
+ }
+}
+
+/* wrapper objects:
+ * readonly attribute Node element; // DOM node (select/optgroup/option)
+ * readonly attribute int level; // tree depth
+ * readonly attribute boolean container; // can contain options
+ * string getCellText(string col); // tree view helper
+ * string cycleCell(int currentIndex); // tree view helper
+ * void onFocus(); // load data into deck
+ * void onBlur(); // save data from deck
+ * boolean canDestroy(boolean prompt); // NB prompt not used
+ * void destroy(); // post remove callback
+ * void moveUp();
+ * boolean canMoveDown();
+ * void moveDown();
+ * void appendOption(newElement, currentIndex);
+ */
+
+// OPTION element wrapper object
+
+// Create a wrapper for the given element at the given level
+function optionObject(option, level) {
+ // select an added option (when loading from document)
+ if (option.hasAttribute("selected")) {
+ selectedOptionCount++;
+ }
+ this.level = level;
+ this.element = option;
+}
+
+optionObject.prototype.container = false;
+
+optionObject.prototype.getCellText = function(column) {
+ if (column.id == "SelectSelCol") {
+ return "";
+ }
+ if (column.id == "SelectValCol" && this.element.hasAttribute("value")) {
+ return this.element.getAttribute("value");
+ }
+ return this.element.text;
+};
+
+optionObject.prototype.cycleCell = function(index) {
+ if (this.element.hasAttribute("selected")) {
+ this.element.removeAttribute("selected");
+ selectedOptionCount--;
+ selectedOption = null;
+ } else {
+ // Different handling for multiselect lists
+ if (gDialog.selectMultiple.checked || !selectedOption) {
+ selectedOptionCount++;
+ } else if (selectedOption) {
+ selectedOption.removeAttribute("selected");
+ let column = theTree.columns.SelectSelCol;
+ theTree.invalidateColumn(column);
+ selectedOption = null;
+ }
+ this.element.setAttribute("selected", "");
+ selectedOption = this.element;
+ let column = theTree.columns.SelectSelCol;
+ theTree.invalidateCell(index, column);
+ }
+ if (currentItem == this) {
+ // Also update the deck
+ gDialog.optionSelected.setAttribute(
+ "checked",
+ this.element.hasAttribute("selected")
+ );
+ }
+ UpdateSelectMultiple();
+};
+
+optionObject.prototype.onFocus = function() {
+ gDialog.optionText.value = this.element.text;
+ hasValue = this.element.hasAttribute("value");
+ oldValue = this.element.value;
+ gDialog.optionHasValue.checked = hasValue;
+ gDialog.optionValue.value = hasValue ? this.element.value : this.element.text;
+ gDialog.optionSelected.checked = this.element.hasAttribute("selected");
+ gDialog.optionDisabled.checked = this.element.hasAttribute("disabled");
+ gDialog.selectDeck.setAttribute("selectedIndex", "2");
+};
+
+optionObject.prototype.onBlur = function() {
+ this.element.text = gDialog.optionText.value;
+ if (gDialog.optionHasValue.checked) {
+ this.element.value = gDialog.optionValue.value;
+ } else {
+ this.element.removeAttribute("value");
+ }
+ if (gDialog.optionSelected.checked) {
+ this.element.setAttribute("selected", "");
+ } else {
+ this.element.removeAttribute("selected");
+ }
+ if (gDialog.optionDisabled.checked) {
+ this.element.setAttribute("disabled", "");
+ } else {
+ this.element.removeAttribute("disabled");
+ }
+};
+
+optionObject.prototype.canDestroy = function(prompt) {
+ return true;
+ /* return !prompt ||
+ ConfirmWithTitle(GetString("DeleteOption"),
+ GetString("DeleteOptionMsg"),
+ GetString("DeleteOption"));*/
+};
+
+optionObject.prototype.destroy = function() {
+ // Deselect a removed option
+ if (this.element.hasAttribute("selected")) {
+ selectedOptionCount--;
+ selectedOption = null;
+ UpdateSelectMultiple();
+ }
+};
+
+/* 4 cases:
+ * a) optgroup -> optgroup
+ * ... ...
+ * option option
+ * b) optgroup -> option
+ * option optgroup
+ * ... ...
+ * c) option
+ * option
+ * d) option
+ * option
+ */
+
+optionObject.prototype.moveUp = function() {
+ var index = treeSelection.currentIndex;
+ if (
+ itemArray[index].level <
+ itemArray[index - 1].level + itemArray[index - 1].container
+ ) {
+ // we need to repaint the tree's lines
+ theTree.invalidateRange(getParentIndex(index), index);
+ // a) option is just after an optgroup, so it becomes the last child
+ itemArray[index].level = 2;
+ theTree.view.selectionChanged();
+ } else {
+ // otherwise new option level is now the same as the previous item
+ itemArray[index].level = itemArray[index - 1].level;
+ // swap the option with the previous item
+ itemArray.splice(index, 0, itemArray.splice(--index, 1)[0]);
+ }
+ selectTreeIndex(index, true);
+};
+
+optionObject.prototype.canMoveDown = function() {
+ // move down is not allowed on the last option if its level is 1
+ return this.level > 1 || itemArray.length - treeSelection.currentIndex > 1;
+};
+
+optionObject.prototype.moveDown = function() {
+ var index = treeSelection.currentIndex;
+ if (
+ index + 1 == itemArray.length ||
+ itemArray[index].level > itemArray[index + 1].level
+ ) {
+ // we need to repaint the tree's lines
+ theTree.invalidateRange(getParentIndex(index), index);
+ // a) option is last child of an optgroup, so it moves just after
+ itemArray[index].level = 1;
+ theTree.view.selectionChanged();
+ } else {
+ // level increases if the option was preceding an optgroup
+ itemArray[index].level += itemArray[index + 1].container;
+ // swap the option with the next item
+ itemArray.splice(index, 0, itemArray.splice(++index, 1)[0]);
+ }
+ selectTreeIndex(index, true);
+};
+
+optionObject.prototype.appendOption = function(child, parent) {
+ // special case quick check
+ if (this.level == 1) {
+ return gDialog.appendOption(child, 0);
+ }
+
+ // append the option to the parent element
+ parent = getParentIndex(parent);
+ return itemArray[parent].appendOption(child, parent);
+};
+
+// OPTGROUP element wrapper object
+
+function optgroupObject(optgroup) {
+ this.element = optgroup;
+}
+
+optgroupObject.prototype.level = 1;
+
+optgroupObject.prototype.container = true;
+
+optgroupObject.prototype.getCellText = function(column) {
+ return column.id == "SelectTextCol" ? this.element.label : "";
+};
+
+optgroupObject.prototype.cycleCell = function(index) {};
+
+optgroupObject.prototype.onFocus = function() {
+ gDialog.optgroupLabel.value = this.element.label;
+ gDialog.optgroupDisabled.checked = this.element.disabled;
+ gDialog.selectDeck.setAttribute("selectedIndex", "1");
+};
+
+optgroupObject.prototype.onBlur = function() {
+ this.element.label = gDialog.optgroupLabel.value;
+ this.element.disabled = gDialog.optgroupDisabled.checked;
+};
+
+optgroupObject.prototype.canDestroy = function(prompt) {
+ // Only removing empty option groups for now
+ return (
+ gDialog.nextChild(treeSelection.currentIndex) -
+ treeSelection.currentIndex ==
+ 1
+ );
+ /* && (!prompt ||
+ ConfirmWithTitle(GetString("DeleteOptGroup"),
+ GetString("DeleteOptGroupMsg"),
+ GetString("DeleteOptGroup")));
+*/
+};
+
+optgroupObject.prototype.destroy = function() {};
+
+optgroupObject.prototype.moveUp = function() {
+ // Find the index of the previous and next elements at the same level
+ var index = treeSelection.currentIndex;
+ var i = index;
+ // eslint-disable-next-line curly
+ while (itemArray[--index].level > 1);
+ var j = gDialog.nextChild(i);
+ // Cut out the element, cut the array in two, then join together
+ var movedItems = itemArray.splice(i, j - i);
+ var endItems = itemArray.splice(index);
+ itemArray = itemArray.concat(movedItems).concat(endItems);
+ // Repaint the lot
+ theTree.invalidateRange(index, j);
+ selectTreeIndex(index, true);
+};
+
+optgroupObject.prototype.canMoveDown = function() {
+ return gDialog.lastChild() > treeSelection.currentIndex;
+};
+
+optgroupObject.prototype.moveDown = function() {
+ // Find the index of the next two elements at the same level
+ var index = treeSelection.currentIndex;
+ var i = gDialog.nextChild(index);
+ var j = gDialog.nextChild(i);
+ // Cut out the element, cut the array in two, then join together
+ var movedItems = itemArray.splice(i, j - 1);
+ var endItems = itemArray.splice(index);
+ itemArray = itemArray.concat(movedItems).concat(endItems);
+ // Repaint the lot
+ theTree.invalidateRange(index, j);
+ index += j - i;
+ selectTreeIndex(index, true);
+};
+
+optgroupObject.prototype.appendOption = function(child, parent) {
+ var index = gDialog.nextChild(parent);
+ // XXX need to repaint the lines, tree won't do this
+ var primaryCol = theTree.columns.getPrimaryColumn();
+ theTree.invalidateCell(index - 1, primaryCol);
+ // insert the wrapped object as the last child
+ itemArray.splice(index, 0, new optionObject(child, 2));
+ theTree.rowCountChanged(index, 1);
+ selectTreeIndex(index, false);
+};
+
+// dialog initialization code
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ // Get a single selected select element
+ const kTagName = "select";
+ try {
+ selectElement = editor.getSelectedElement(kTagName);
+ } catch (e) {}
+
+ if (selectElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ selectElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!selectElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ }
+
+ // SELECT element wrapper object
+ gDialog = {
+ // useful elements
+ accept: document.documentElement.getButton("accept"),
+ selectDeck: document.getElementById("SelectDeck"),
+ selectName: document.getElementById("SelectName"),
+ selectSize: document.getElementById("SelectSize"),
+ selectMultiple: document.getElementById("SelectMultiple"),
+ selectDisabled: document.getElementById("SelectDisabled"),
+ selectTabIndex: document.getElementById("SelectTabIndex"),
+ optgroupLabel: document.getElementById("OptGroupLabel"),
+ optgroupDisabled: document.getElementById("OptGroupDisabled"),
+ optionText: document.getElementById("OptionText"),
+ optionHasValue: document.getElementById("OptionHasValue"),
+ optionValue: document.getElementById("OptionValue"),
+ optionSelected: document.getElementById("OptionSelected"),
+ optionDisabled: document.getElementById("OptionDisabled"),
+ removeButton: document.getElementById("RemoveButton"),
+ previousButton: document.getElementById("PreviousButton"),
+ nextButton: document.getElementById("NextButton"),
+ tree: document.getElementById("SelectTree"),
+ // wrapper methods (except MoveUp and MoveDown)
+ element: selectElement.cloneNode(false),
+ level: 0,
+ container: true,
+ getCellText(column) {
+ return column.id == "SelectTextCol"
+ ? this.element.getAttribute("name")
+ : "";
+ },
+ cycleCell(index) {},
+ onFocus() {
+ gDialog.selectName.value = this.element.getAttribute("name");
+ gDialog.selectSize.value = this.element.getAttribute("size");
+ gDialog.selectMultiple.checked = this.element.hasAttribute("multiple");
+ gDialog.selectDisabled.checked = this.element.hasAttribute("disabled");
+ gDialog.selectTabIndex.value = this.element.getAttribute("tabindex");
+ this.selectDeck.setAttribute("selectedIndex", "0");
+ onNameInput();
+ },
+ onBlur() {
+ this.element.setAttribute("name", gDialog.selectName.value);
+ if (gDialog.selectSize.value) {
+ this.element.setAttribute("size", gDialog.selectSize.value);
+ } else {
+ this.element.removeAttribute("size");
+ }
+ if (gDialog.selectMultiple.checked) {
+ this.element.setAttribute("multiple", "");
+ } else {
+ this.element.removeAttribute("multiple");
+ }
+ if (gDialog.selectDisabled.checked) {
+ this.element.setAttribute("disabled", "");
+ } else {
+ this.element.removeAttribute("disabled");
+ }
+ if (gDialog.selectTabIndex.value) {
+ this.element.setAttribute("tabindex", gDialog.selectTabIndex.value);
+ } else {
+ this.element.removeAttribute("tabindex");
+ }
+ },
+ appendOption(child, parent) {
+ var index = itemArray.length;
+ // XXX need to repaint the lines, tree won't do this
+ theTree.invalidateRange(this.lastChild(), index);
+ // append the wrapped object
+ itemArray.push(new optionObject(child, 1));
+ theTree.rowCountChanged(index, 1);
+ selectTreeIndex(index, false);
+ },
+ canDestroy(prompt) {
+ return false;
+ },
+ canMoveDown() {
+ return false;
+ },
+ // helper methods
+ // Find the index of the next immediate child of the select
+ nextChild(index) {
+ // eslint-disable-next-line curly
+ while (++index < itemArray.length && itemArray[index].level > 1);
+ return index;
+ },
+ // Find the index of the last immediate child of the select
+ lastChild() {
+ var index = itemArray.length;
+ // eslint-disable-next-line curly
+ while (itemArray[--index].level > 1);
+ return index;
+ },
+ };
+ // Start with the <select> wrapper
+ itemArray = [gDialog];
+
+ // We modify the actual option and optgroup elements so clone them first
+ for (var child = selectElement.firstChild; child; child = child.nextSibling) {
+ if (child.tagName == "OPTION") {
+ itemArray.push(new optionObject(child.cloneNode(true), 1));
+ } else if (child.tagName == "OPTGROUP") {
+ itemArray.push(new optgroupObject(child.cloneNode(false)));
+ for (
+ var grandchild = child.firstChild;
+ grandchild;
+ grandchild = grandchild.nextSibling
+ ) {
+ if (grandchild.tagName == "OPTION") {
+ itemArray.push(new optionObject(grandchild.cloneNode(true), 2));
+ }
+ }
+ }
+ }
+
+ UpdateSelectMultiple();
+
+ // Define a custom view for the tree
+ theTree = gDialog.tree;
+ theTree.view = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsITreeView",
+ "nsISupportsWeakReference",
+ ]),
+ // useful for debugging
+ get wrappedJSObject() {
+ return this;
+ },
+ get rowCount() {
+ return itemArray.length;
+ },
+ get selection() {
+ return treeSelection;
+ },
+ set selection(selection) {
+ return (treeSelection = selection);
+ },
+ getRowProperties(index) {
+ return "";
+ },
+ // could have used a wrapper for this
+ getCellProperties(index, column) {
+ if (column.id == "SelectSelCol" && !itemArray[index].container) {
+ return "checked-" + itemArray[index].element.hasAttribute("selected");
+ }
+ return "";
+ },
+ getColumnProperties(column) {
+ return "";
+ },
+ // get info from wrapper
+ isContainer(index) {
+ return itemArray[index].container;
+ },
+ isContainerOpen(index) {
+ return true;
+ },
+ isContainerEmpty(index) {
+ return true;
+ },
+ isSeparator(index) {
+ return false;
+ },
+ isSorted() {
+ return false;
+ },
+ // d&d not implemented yet!
+ canDrop(index, orientation) {
+ return false;
+ },
+ drop(index, orientation) {
+ alert("drop:" + index + "," + orientation);
+ },
+ // same as the global helper
+ getParentIndex,
+ // tree needs to know when to paint lines
+ hasNextSibling(index, after) {
+ if (!index) {
+ return false;
+ }
+ var level = itemArray[index].level;
+ while (++after < itemArray.length) {
+ switch (level - itemArray[after].level) {
+ case 1:
+ return false;
+ case 0:
+ return true;
+ }
+ }
+ return false;
+ },
+ getLevel(index) {
+ return itemArray[index].level;
+ },
+ getImageSrc(index, column) {},
+ getProgressMode(index, column) {},
+ getCellValue(index, column) {},
+ getCellText(index, column) {
+ return itemArray[index].getCellText(column);
+ },
+ setTree(tree) {
+ this.tree = tree;
+ },
+ toggleOpenState(index) {},
+ cycleHeader(col) {},
+ selectionChanged() {
+ // Save current values and update buttons and deck
+ if (currentItem) {
+ currentItem.onBlur();
+ }
+ var currentIndex = treeSelection.currentIndex;
+ currentItem = itemArray[currentIndex];
+ gDialog.removeButton.disabled = !currentItem.canDestroy();
+ gDialog.previousButton.disabled = currentIndex < 2;
+ gDialog.nextButton.disabled = !currentItem.canMoveDown();
+ // For Advanced Edit
+ globalElement = currentItem.element;
+ currentItem.onFocus();
+ },
+ cycleCell(index, column) {
+ itemArray[index].cycleCell(index);
+ },
+ isEditable(index, column) {
+ return false;
+ },
+ };
+ treeSelection.select(0);
+ currentItem = gDialog;
+ // onNameInput();
+
+ SetTextboxFocus(gDialog.selectName);
+
+ SetWindowLocation();
+}
+
+// Called from Advanced Edit
+function InitDialog() {
+ currentItem.onFocus();
+}
+
+// Called from Advanced Edit
+function ValidateData() {
+ currentItem.onBlur();
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ // Coalesce into one undo transaction
+ editor.beginTransaction();
+
+ try {
+ editor.cloneAttributes(selectElement, gDialog.element);
+
+ if (insertNew) {
+ // 'true' means delete the selection before inserting
+ editor.insertElementAtSelection(selectElement, true);
+ }
+
+ editor.setShouldTxnSetSelection(false);
+
+ while (selectElement.lastChild) {
+ editor.deleteNode(selectElement.lastChild);
+ }
+
+ var offset = 0;
+ for (var i = 1; i < itemArray.length; i++) {
+ if (itemArray[i].level > 1) {
+ selectElement.lastChild.appendChild(itemArray[i].element);
+ } else {
+ editor.insertNode(itemArray[i].element, selectElement, offset++);
+ }
+ }
+
+ editor.setShouldTxnSetSelection(true);
+ } finally {
+ editor.endTransaction();
+ }
+
+ SaveWindowLocation();
+}
+
+// Button actions
+function AddOption() {
+ currentItem.appendOption(
+ GetCurrentEditor().createElementWithDefaults("option"),
+ treeSelection.currentIndex
+ );
+ SetTextboxFocus(gDialog.optionText);
+}
+
+function AddOptGroup() {
+ var optgroupElement = GetCurrentEditor().createElementWithDefaults(
+ "optgroup"
+ );
+ var index = itemArray.length;
+ // XXX need to repaint the lines, tree won't do this
+ theTree.invalidateRange(gDialog.lastChild(), index);
+ // append the wrapped object
+ itemArray.push(new optgroupObject(optgroupElement));
+ theTree.rowCountChanged(index, 1);
+ selectTreeIndex(index, false);
+ SetTextboxFocus(gDialog.optgroupLabel);
+}
+
+function RemoveElement() {
+ if (currentItem.canDestroy(true)) {
+ // Only removing empty option groups for now
+ var index = treeSelection.currentIndex;
+ var level = itemArray[index].level;
+ // Perform necessary cleanup and remove the wrapper
+ itemArray[index].destroy();
+ itemArray.splice(index, 1);
+ --index;
+ // XXX need to repaint the lines, tree won't do this
+ if (level == 1) {
+ var last = gDialog.lastChild();
+ if (index > last) {
+ theTree.invalidateRange(last, index);
+ }
+ }
+ selectTreeIndex(index, true);
+ theTree.rowCountChanged(++index, -1);
+ }
+}
+
+// Event handler
+function onTreeKeyUp(event) {
+ if (event.keyCode == event.DOM_VK_SPACE) {
+ currentItem.cycleCell();
+ }
+}
+
+function onNameInput() {
+ var disabled = !gDialog.selectName.value;
+ if (gDialog.accept.disabled != disabled) {
+ gDialog.accept.disabled = disabled;
+ }
+ gDialog.element.setAttribute("name", gDialog.selectName.value);
+ // repaint the tree
+ var primaryCol = theTree.columns.getPrimaryColumn();
+ theTree.invalidateCell(treeSelection.currentIndex, primaryCol);
+}
+
+function onLabelInput() {
+ currentItem.element.setAttribute("label", gDialog.optgroupLabel.value);
+ // repaint the tree
+ var primaryCol = theTree.columns.getPrimaryColumn();
+ theTree.invalidateCell(treeSelection.currentIndex, primaryCol);
+}
+
+function onTextInput() {
+ currentItem.element.text = gDialog.optionText.value;
+ // repaint the tree
+ if (hasValue) {
+ var primaryCol = theTree.columns.getPrimaryColumn();
+ theTree.invalidateCell(treeSelection.currentIndex, primaryCol);
+ } else {
+ gDialog.optionValue.value = gDialog.optionText.value;
+ theTree.invalidateRow(treeSelection.currentIndex);
+ }
+}
+
+function onValueInput() {
+ gDialog.optionHasValue.checked = hasValue = true;
+ oldValue = gDialog.optionValue.value;
+ currentItem.element.setAttribute("value", oldValue);
+ // repaint the tree
+ var column = theTree.columns.SelectValCol;
+ theTree.invalidateCell(treeSelection.currentIndex, column);
+}
+
+function onHasValueClick() {
+ hasValue = gDialog.optionHasValue.checked;
+ if (hasValue) {
+ gDialog.optionValue.value = oldValue;
+ currentItem.element.setAttribute("value", oldValue);
+ } else {
+ oldValue = gDialog.optionValue.value;
+ gDialog.optionValue.value = gDialog.optionText.value;
+ currentItem.element.removeAttribute("value");
+ }
+ // repaint the tree
+ var column = theTree.columns.SelectValCol;
+ theTree.invalidateCell(treeSelection.currentIndex, column);
+}
+
+function onSelectMultipleClick() {
+ // Recalculate the unique selected option if we need it and have lost it
+ if (
+ !gDialog.selectMultiple.checked &&
+ selectedOptionCount == 1 &&
+ !selectedOption
+ ) {
+ // eslint-disable-next-line curly
+ for (
+ var i = 1;
+ !(selectedOption = itemArray[i].element).hasAttribute("selected");
+ i++
+ );
+ }
+}
+
+function selectTreeIndex(index, focus) {
+ treeSelection.select(index);
+ theTree.ensureRowIsVisible(index);
+ if (focus) {
+ gDialog.tree.focus();
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml b/comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml
new file mode 100644
index 0000000000..ff91b02e28
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdSelectProps.xhtml
@@ -0,0 +1,143 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edSelectProperties SYSTEM "chrome://editor/locale/EditorSelectProperties.dtd">
+%edSelectProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdSelectProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <!-- Setting rows="7" on tree isn't working, equalsize vbox sets tree height. -->
+ <vbox equalsize="always">
+ <tree id="SelectTree" onselect="treeBoxObject.view.selectionChanged();" onkeyup="onTreeKeyUp(event);">
+ <treecols id="SelectCols">
+ <treecol id="SelectTextCol" flex="3" label="&TextHeader.label;" primary="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="SelectValCol" flex="2" label="&ValueHeader.label;"/>
+ <treecol id="SelectSelCol" label="&SelectedHeader.label;" cycler="true"/>
+ </treecols>
+
+ <treechildren id="SelectTreeChildren"/>
+ </tree>
+
+ <hbox flex="1">
+ <deck flex="1" id="SelectDeck" index="0">
+ <groupbox flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&Select.label;</label>
+ </hbox>
+ <grid flex="1"><columns><column flex="1"/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="SelectName" value="&SelectName.label;" accesskey="&SelectName.accesskey;"/>
+ <textbox id="SelectName" flex="1" oninput="onNameInput();"/>
+ </row>
+ <row align="center">
+ <label control="SelectSize" value="&SelectSize.label;" accesskey="&SelectSize.accesskey;"/>
+ <hbox>
+ <textbox id="SelectSize" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="SelectMultiple" flex="1" label="&SelectMultiple.label;" accesskey="&SelectMultiple.accesskey;" oncommand="onSelectMultipleClick();"/>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="SelectDisabled" flex="1" label="&SelectDisabled.label;" accesskey="&SelectDisabled.accesskey;"/>
+ </row>
+ <row align="center">
+ <label control="SelectTabIndex" value="&SelectTabIndex.label;" accesskey="&SelectTabIndex.accesskey;"/>
+ <hbox>
+ <textbox id="SelectTabIndex" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <groupbox flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&OptGroup.label;</label>
+ </hbox>
+ <grid flex="1"><columns><column flex="1"/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="OptGroupLabel" value="&OptGroupLabel.label;" accesskey="&OptGroupLabel.accesskey;"/>
+ <textbox id="OptGroupLabel" oninput="onLabelInput();"/>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="OptGroupDisabled" label="&OptGroupDisabled.label;" accesskey="&OptGroupDisabled.accesskey;"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <groupbox flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&Option.label;</label>
+ </hbox>
+ <grid flex="1"><columns><column flex="1"/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="OptionText" value="&OptionText.label;" accesskey="&OptionText.accesskey;"/>
+ <textbox id="OptionText" oninput="onTextInput();"/>
+ </row>
+ <row align="center">
+ <checkbox id="OptionHasValue" label="&OptionValue.label;" accesskey="&OptionValue.accesskey;" oncommand="onHasValueClick();"/>
+ <textbox id="OptionValue" oninput="onValueInput();"/>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="OptionSelected" label="&OptionSelected.label;" accesskey="&OptionSelected.accesskey;" oncommand="currentItem.cycleCell();"/>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="OptionDisabled" label="&OptionDisabled.label;" accesskey="&OptionDisabled.accesskey;"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ </deck>
+
+ <vbox>
+ <button label="&AddOption.label;" accesskey="&AddOption.accesskey;" oncommand="AddOption();"/>
+ <button label="&AddOptGroup.label;" accesskey="&AddOptGroup.accesskey;" oncommand="AddOptGroup();"/>
+ <button id="RemoveButton" label="&RemoveElement.label;" accesskey="&RemoveElement.accesskey;"
+ oncommand="RemoveElement();" disabled="true"/>
+ <button id="PreviousButton" label="&MoveElementUp.label;" accesskey="&MoveElementUp.accesskey;"
+ oncommand="currentItem.moveUp();" disabled="true" type="row"/>
+ <button id="NextButton" label="&MoveElementDown.label;" accesskey="&MoveElementDown.accesskey;"
+ oncommand="currentItem.moveDown();" disabled="true" type="row"/>
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </vbox>
+ </hbox>
+ </vbox>
+
+ <separator class="groove"/>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdSnapToGrid.js b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.js
new file mode 100644
index 0000000000..ed546d3540
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.js
@@ -0,0 +1,62 @@
+/* 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 gEditor;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup()
+{
+ gEditor = GetCurrentEditor();
+ if (!gEditor)
+ {
+ window.close();
+ return;
+ }
+
+ gEditor instanceof Ci.nsIHTMLAbsPosEditor;
+
+ gDialog.enableSnapToGrid = document.getElementById("enableSnapToGrid");
+ gDialog.sizeInput = document.getElementById("size");
+ gDialog.sizeLabel = document.getElementById("sizeLabel");
+ gDialog.unitLabel = document.getElementById("unitLabel");
+
+ // Initialize control values based on existing attributes
+ InitDialog()
+
+ // SET FOCUS TO FIRST CONTROL
+ SetTextboxFocus(gDialog.sizeInput);
+
+ // Resize window
+ window.sizeToContent();
+
+ SetWindowLocation();
+}
+
+// Set dialog widgets with attribute data
+// We get them from globalElement copy so this can be used
+// by AdvancedEdit(), which is shared by all property dialogs
+function InitDialog()
+{
+ gDialog.enableSnapToGrid.checked = gEditor.snapToGridEnabled;
+ toggleSnapToGrid();
+
+ gDialog.sizeInput.value = gEditor.gridSize;
+}
+
+function onAccept()
+{
+ gEditor.snapToGridEnabled = gDialog.enableSnapToGrid.checked;
+ gEditor.gridSize = gDialog.sizeInput.value;
+}
+
+function toggleSnapToGrid()
+{
+ SetElementEnabledById("size", gDialog.enableSnapToGrid.checked)
+ SetElementEnabledById("sizeLabel", gDialog.enableSnapToGrid.checked)
+ SetElementEnabledById("unitLabel", gDialog.enableSnapToGrid.checked)
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml
new file mode 100644
index 0000000000..9ae6643975
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdSnapToGrid.xhtml
@@ -0,0 +1,47 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorSnapToGrid.dtd">
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <!--- Element-specific methods -->
+ <script src="chrome://editor/content/EdSnapToGrid.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <checkbox id="enableSnapToGrid"
+ label="&enableSnapToGrid.label;"
+ accesskey="&enableSnapToGrid.accessKey;"
+ oncommand="toggleSnapToGrid();"/>
+
+ <spacer class="spacer"/>
+
+ <grid>
+ <columns><column/><column/><column /></columns>
+ <rows>
+ <row align="center">
+ <label value="&sizeEditField.label;"
+ id="sizeLabel"
+ control="size"
+ accesskey="&sizeEditField.accessKey;"/>
+ <textbox class="narrow" id="size" oninput="forceInteger('size')"/>
+ <label id="unitLabel"
+ value="&pixelsLabel.value;" />
+ </row>
+ </rows>
+ </grid>
+
+ <spacer class="spacer"/>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdSpellCheck.js b/comm/suite/editor/components/dialogs/content/EdSpellCheck.js
new file mode 100644
index 0000000000..ddb23726cd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdSpellCheck.js
@@ -0,0 +1,495 @@
+/* -*- 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/. */
+
+/* import-globals-from ../../../../mail/base/content/utilityOverlay.js */
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var { InlineSpellChecker } = ChromeUtils.import(
+ "resource://gre/modules/InlineSpellChecker.jsm"
+);
+
+var gMisspelledWord;
+var gSpellChecker = null;
+var gAllowSelectWord = true;
+var gPreviousReplaceWord = "";
+var gFirstTime = true;
+var gLastSelectedLang = null;
+var gDictCount = 0;
+
+document.addEventListener("dialogaccept", doDefault);
+document.addEventListener("dialogcancel", CancelSpellCheck);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ window.close();
+ return;
+ }
+
+ // Get the spellChecker shell
+ gSpellChecker = Cu.createSpellChecker();
+ if (!gSpellChecker) {
+ dump("SpellChecker not found!!!\n");
+ window.close();
+ return;
+ }
+
+ // Start the spell checker module.
+ try {
+ var skipBlockQuotes = window.arguments[1];
+ var enableSelectionChecking = window.arguments[2];
+
+ gSpellChecker.setFilterType(
+ skipBlockQuotes
+ ? Ci.nsIEditorSpellCheck.FILTERTYPE_MAIL
+ : Ci.nsIEditorSpellCheck.FILTERTYPE_NORMAL
+ );
+ gSpellChecker.InitSpellChecker(
+ editor,
+ enableSelectionChecking,
+ spellCheckStarted
+ );
+ } catch (ex) {
+ dump("*** Exception error: InitSpellChecker\n");
+ window.close();
+ }
+}
+
+function spellCheckStarted() {
+ gDialog.MisspelledWordLabel = document.getElementById("MisspelledWordLabel");
+ gDialog.MisspelledWord = document.getElementById("MisspelledWord");
+ gDialog.ReplaceButton = document.getElementById("Replace");
+ gDialog.IgnoreButton = document.getElementById("Ignore");
+ gDialog.StopButton = document.getElementById("Stop");
+ gDialog.CloseButton = document.getElementById("Close");
+ gDialog.ReplaceWordInput = document.getElementById("ReplaceWordInput");
+ gDialog.SuggestedList = document.getElementById("SuggestedList");
+ gDialog.LanguageMenulist = document.getElementById("LanguageMenulist");
+
+ // Fill in the language menulist and sync it up
+ // with the spellchecker's current language.
+
+ var curLang;
+
+ try {
+ curLang = gSpellChecker.GetCurrentDictionary();
+ } catch (ex) {
+ curLang = "";
+ }
+
+ InitLanguageMenu(curLang);
+
+ // Get the first misspelled word and setup all UI
+ NextWord();
+
+ // When startup param is true, setup different UI when spell checking
+ // just before sending mail message
+ if (window.arguments[0]) {
+ // If no misspelled words found, simply close dialog and send message
+ if (!gMisspelledWord) {
+ onClose();
+ return;
+ }
+
+ // Hide "Close" button and use "Send" instead
+ gDialog.CloseButton.hidden = true;
+ gDialog.CloseButton = document.getElementById("Send");
+ gDialog.CloseButton.hidden = false;
+ } else {
+ // Normal spell checking - hide the "Stop" button
+ // (Note that this button is the "Cancel" button for
+ // Esc keybinding and related window close actions)
+ gDialog.StopButton.hidden = true;
+ }
+
+ // Clear flag that determines message when
+ // no misspelled word is found
+ // (different message when used for the first time)
+ gFirstTime = false;
+
+ window.sizeToContent();
+}
+
+function InitLanguageMenu(aCurLang) {
+ // Get the list of dictionaries from
+ // the spellchecker.
+
+ var dictList;
+ try {
+ dictList = gSpellChecker.GetDictionaryList();
+ } catch (ex) {
+ dump("Failed to get DictionaryList!\n");
+ return;
+ }
+
+ // If we're not just starting up and dictionary count
+ // hasn't changed then no need to update the menu.
+ if (gDictCount == dictList.length) {
+ return;
+ }
+
+ // Store current dictionary count.
+ gDictCount = dictList.length;
+
+ var inlineSpellChecker = new InlineSpellChecker();
+ var sortedList = inlineSpellChecker.sortDictionaryList(dictList);
+
+ // Remove any languages from the list.
+ var languageMenuPopup = gDialog.LanguageMenulist.menupopup;
+ while (languageMenuPopup.firstChild.localName != "menuseparator") {
+ languageMenuPopup.firstChild.remove();
+ }
+
+ var defaultItem = null;
+
+ for (var i = 0; i < gDictCount; i++) {
+ let item = document.createXULElement("menuitem");
+ item.setAttribute("label", sortedList[i].displayName);
+ item.setAttribute("value", sortedList[i].localeCode);
+ let beforeItem = gDialog.LanguageMenulist.getItemAtIndex(i);
+ languageMenuPopup.insertBefore(item, beforeItem);
+
+ if (aCurLang && sortedList[i].localeCode == aCurLang) {
+ defaultItem = item;
+ }
+ }
+
+ // Now make sure the correct item in the menu list is selected.
+ if (defaultItem) {
+ gDialog.LanguageMenulist.selectedItem = defaultItem;
+ gLastSelectedLang = defaultItem;
+ }
+}
+
+function DoEnabling() {
+ if (!gMisspelledWord) {
+ // No more misspelled words
+ gDialog.MisspelledWord.setAttribute(
+ "value",
+ GetString(gFirstTime ? "NoMisspelledWord" : "CheckSpellingDone")
+ );
+
+ gDialog.ReplaceButton.removeAttribute("default");
+ gDialog.IgnoreButton.removeAttribute("default");
+
+ gDialog.CloseButton.setAttribute("default", "true");
+ // Shouldn't have to do this if "default" is true?
+ gDialog.CloseButton.focus();
+
+ SetElementEnabledById("MisspelledWordLabel", false);
+ SetElementEnabledById("ReplaceWordLabel", false);
+ SetElementEnabledById("ReplaceWordInput", false);
+ SetElementEnabledById("CheckWord", false);
+ SetElementEnabledById("SuggestedListLabel", false);
+ SetElementEnabledById("SuggestedList", false);
+ SetElementEnabledById("Ignore", false);
+ SetElementEnabledById("IgnoreAll", false);
+ SetElementEnabledById("Replace", false);
+ SetElementEnabledById("ReplaceAll", false);
+ SetElementEnabledById("AddToDictionary", false);
+ } else {
+ SetElementEnabledById("MisspelledWordLabel", true);
+ SetElementEnabledById("ReplaceWordLabel", true);
+ SetElementEnabledById("ReplaceWordInput", true);
+ SetElementEnabledById("CheckWord", true);
+ SetElementEnabledById("SuggestedListLabel", true);
+ SetElementEnabledById("SuggestedList", true);
+ SetElementEnabledById("Ignore", true);
+ SetElementEnabledById("IgnoreAll", true);
+ SetElementEnabledById("AddToDictionary", true);
+
+ gDialog.CloseButton.removeAttribute("default");
+ SetReplaceEnable();
+ }
+}
+
+function NextWord() {
+ gMisspelledWord = gSpellChecker.GetNextMisspelledWord();
+ SetWidgetsForMisspelledWord();
+}
+
+function SetWidgetsForMisspelledWord() {
+ gDialog.MisspelledWord.setAttribute("value", gMisspelledWord);
+
+ // Initial replace word is misspelled word
+ gDialog.ReplaceWordInput.value = gMisspelledWord;
+ gPreviousReplaceWord = gMisspelledWord;
+
+ // This sets gDialog.ReplaceWordInput to first suggested word in list
+ FillSuggestedList(gMisspelledWord);
+
+ DoEnabling();
+
+ if (gMisspelledWord) {
+ SetTextboxFocus(gDialog.ReplaceWordInput);
+ }
+}
+
+function CheckWord() {
+ var word = gDialog.ReplaceWordInput.value;
+ if (word) {
+ if (gSpellChecker.CheckCurrentWord(word)) {
+ FillSuggestedList(word);
+ SetReplaceEnable();
+ } else {
+ ClearListbox(gDialog.SuggestedList);
+ var item = gDialog.SuggestedList.appendItem(
+ GetString("CorrectSpelling"),
+ ""
+ );
+ if (item) {
+ item.setAttribute("disabled", "true");
+ }
+ // Suppress being able to select the message text
+ gAllowSelectWord = false;
+ }
+ }
+}
+
+function SelectSuggestedWord() {
+ if (gAllowSelectWord) {
+ if (gDialog.SuggestedList.selectedItem) {
+ var selValue = gDialog.SuggestedList.selectedItem.label;
+ gDialog.ReplaceWordInput.value = selValue;
+ gPreviousReplaceWord = selValue;
+ } else {
+ gDialog.ReplaceWordInput.value = gPreviousReplaceWord;
+ }
+ SetReplaceEnable();
+ }
+}
+
+function ChangeReplaceWord() {
+ // Calling this triggers SelectSuggestedWord(),
+ // so temporarily suppress the effect of that
+ var saveAllow = gAllowSelectWord;
+ gAllowSelectWord = false;
+
+ // Select matching word in list
+ var newSelectedItem;
+ var replaceWord = TrimString(gDialog.ReplaceWordInput.value);
+ if (replaceWord) {
+ for (var i = 0; i < gDialog.SuggestedList.getRowCount(); i++) {
+ var item = gDialog.SuggestedList.getItemAtIndex(i);
+ if (item.label == replaceWord) {
+ newSelectedItem = item;
+ break;
+ }
+ }
+ }
+ gDialog.SuggestedList.selectedItem = newSelectedItem;
+
+ gAllowSelectWord = saveAllow;
+
+ // Remember the new word
+ gPreviousReplaceWord = gDialog.ReplaceWordInput.value;
+
+ SetReplaceEnable();
+}
+
+function Ignore() {
+ NextWord();
+}
+
+function IgnoreAll() {
+ if (gMisspelledWord) {
+ gSpellChecker.IgnoreWordAllOccurrences(gMisspelledWord);
+ }
+ NextWord();
+}
+
+function Replace(newWord) {
+ if (!newWord) {
+ return;
+ }
+
+ if (gMisspelledWord && gMisspelledWord != newWord) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+ try {
+ gSpellChecker.ReplaceWord(gMisspelledWord, newWord, false);
+ } catch (e) {}
+ editor.endTransaction();
+ }
+ NextWord();
+}
+
+function ReplaceAll() {
+ var newWord = gDialog.ReplaceWordInput.value;
+ if (gMisspelledWord && gMisspelledWord != newWord) {
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+ try {
+ gSpellChecker.ReplaceWord(gMisspelledWord, newWord, true);
+ } catch (e) {}
+ editor.endTransaction();
+ }
+ NextWord();
+}
+
+function AddToDictionary() {
+ if (gMisspelledWord) {
+ gSpellChecker.AddWordToDictionary(gMisspelledWord);
+ }
+ NextWord();
+}
+
+function EditDictionary() {
+ window.openDialog(
+ "chrome://editor/content/EdDictionary.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ gMisspelledWord
+ );
+}
+
+function SelectLanguage() {
+ var item = gDialog.LanguageMenulist.selectedItem;
+ if (item.value != "more-cmd") {
+ gSpellChecker.SetCurrentDictionary(item.value);
+ // For compose windows we need to set the "lang" attribute so the
+ // core editor uses the correct dictionary for the inline spell check.
+ if (window.arguments[1]) {
+ if ("ComposeChangeLanguage" in window.opener) {
+ // We came here from a compose window.
+ window.opener.ComposeChangeLanguage(item.value);
+ } else {
+ window.opener.document.documentElement.setAttribute("lang", item.value);
+ }
+ }
+ gLastSelectedLang = item;
+ } else {
+ openDictionaryList();
+
+ if (gLastSelectedLang) {
+ gDialog.LanguageMenulist.selectedItem = gLastSelectedLang;
+ }
+ }
+}
+
+function Recheck() {
+ var recheckLanguage;
+
+ function finishRecheck() {
+ gSpellChecker.SetCurrentDictionary(recheckLanguage);
+ gMisspelledWord = gSpellChecker.GetNextMisspelledWord();
+ SetWidgetsForMisspelledWord();
+ }
+
+ // TODO: Should we bother to add a "Recheck" method to interface?
+ try {
+ recheckLanguage = gSpellChecker.GetCurrentDictionary();
+ gSpellChecker.UninitSpellChecker();
+ // Clear the ignore all list.
+ Cc["@mozilla.org/spellchecker/personaldictionary;1"]
+ .getService(Ci.mozIPersonalDictionary)
+ .endSession();
+ gSpellChecker.InitSpellChecker(GetCurrentEditor(), false, finishRecheck);
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+}
+
+function FillSuggestedList(misspelledWord) {
+ var list = gDialog.SuggestedList;
+
+ // Clear the current contents of the list
+ gAllowSelectWord = false;
+ ClearListbox(list);
+ var item;
+
+ if (misspelledWord.length > 0) {
+ // Get suggested words until an empty string is returned
+ var count = 0;
+ do {
+ var word = gSpellChecker.GetSuggestedWord();
+ if (word.length > 0) {
+ list.appendItem(word, "");
+ count++;
+ }
+ } while (word.length > 0);
+
+ if (count == 0) {
+ // No suggestions - show a message but don't let user select it
+ item = list.appendItem(GetString("NoSuggestedWords"));
+ if (item) {
+ item.setAttribute("disabled", "true");
+ }
+ gAllowSelectWord = false;
+ } else {
+ gAllowSelectWord = true;
+ // Initialize with first suggested list by selecting it
+ gDialog.SuggestedList.selectedIndex = 0;
+ }
+ } else {
+ item = list.appendItem("", "");
+ if (item) {
+ item.setAttribute("disabled", "true");
+ }
+ }
+}
+
+function SetReplaceEnable() {
+ // Enable "Change..." buttons only if new word is different than misspelled
+ var newWord = gDialog.ReplaceWordInput.value;
+ var enable = newWord.length > 0 && newWord != gMisspelledWord;
+ SetElementEnabledById("Replace", enable);
+ SetElementEnabledById("ReplaceAll", enable);
+ if (enable) {
+ gDialog.ReplaceButton.setAttribute("default", "true");
+ gDialog.IgnoreButton.removeAttribute("default");
+ } else {
+ gDialog.IgnoreButton.setAttribute("default", "true");
+ gDialog.ReplaceButton.removeAttribute("default");
+ }
+}
+
+function doDefault(event) {
+ if (gDialog.ReplaceButton.getAttribute("default") == "true") {
+ Replace(gDialog.ReplaceWordInput.value);
+ } else if (gDialog.IgnoreButton.getAttribute("default") == "true") {
+ Ignore();
+ } else if (gDialog.CloseButton.getAttribute("default") == "true") {
+ onClose();
+ }
+
+ event.preventDefault();
+}
+
+function ExitSpellChecker() {
+ if (gSpellChecker) {
+ try {
+ gSpellChecker.UninitSpellChecker();
+ // now check the document over again with the new dictionary
+ // if we have an inline spellchecker
+ if (
+ "InlineSpellCheckerUI" in window.opener &&
+ window.opener.InlineSpellCheckerUI.enabled
+ ) {
+ window.opener.InlineSpellCheckerUI.mInlineSpellChecker.spellCheckRange(
+ null
+ );
+ }
+ } finally {
+ gSpellChecker = null;
+ }
+ }
+}
+
+function CancelSpellCheck() {
+ ExitSpellChecker();
+
+ // Signal to calling window that we canceled
+ window.opener.cancelSendMessage = true;
+}
+
+function onClose() {
+ ExitSpellChecker();
+
+ window.opener.cancelSendMessage = false;
+ window.close();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml b/comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml
new file mode 100644
index 0000000000..4cb9f49198
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdSpellCheck.xhtml
@@ -0,0 +1,113 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorSpellCheck.dtd">
+
+<!-- dialog containing a control requiring initial setup -->
+<dialog id="spellCheckDlg" buttons="cancel" title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ persist="screenX screenY"
+ onload="Startup()">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+ <script src="chrome://editor/content/EdSpellCheck.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+
+ <stringbundle id="languageBundle" src="chrome://global/locale/languageNames.properties"/>
+ <stringbundle id="regionBundle" src="chrome://global/locale/regionNames.properties"/>
+
+ <grid>
+ <columns>
+ <column class="spell-check"/>
+ <column class="spell-check" flex="1"/>
+ <column class="spell-check"/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label id="MisspelledWordLabel" value="&misspelledWord.label;"/>
+ <label class="bold" id="MisspelledWord" crop="end"/>
+ <button class="spell-check" label="&recheckButton2.label;" oncommand="Recheck();"
+ accesskey="&recheckButton2.accessKey;"/>
+ </row>
+ <row align="center">
+ <label id="ReplaceWordLabel" value="&wordEditField.label;"
+ control="ReplaceWordInput"
+ accesskey="&wordEditField.accessKey;"/>
+ <textbox id="ReplaceWordInput" oninput="ChangeReplaceWord()" flex="1"/>
+ <button id="CheckWord" oncommand="CheckWord()" label="&checkwordButton.label;"
+ accesskey="&checkwordButton.accessKey;"/>
+ </row>
+ </rows>
+ </grid>
+ <label id="SuggestedListLabel" value="&suggestions.label;"
+ control="SuggestedList"
+ accesskey="&suggestions.accessKey;"/>
+ <grid flex="1">
+ <columns><column flex="1"/><column/></columns>
+ <rows>
+ <row flex="1">
+ <!-- BUG! setting class="MinWidth20em" on tree doesn't work (width=0) -->
+ <richlistbox id="SuggestedList"
+ class="theme-listbox"
+ onselect="SelectSuggestedWord()"
+ ondblclick="if (gAllowSelectWord) { Replace(event.target.value); }"/>
+ <vbox>
+ <grid>
+ <columns><column class="spell-check" flex="1"/><column class="spell-check" flex="1"/></columns>
+ <rows>
+ <row>
+ <button id="Replace" label="&replaceButton.label;"
+ oncommand="Replace(gDialog.ReplaceWordInput.value);"
+ accesskey="&replaceButton.accessKey;"/>
+ <button id="Ignore" oncommand="Ignore();" label="&ignoreButton.label;"
+ accesskey="&ignoreButton.accessKey;"/>
+ </row>
+ <row>
+ <button id="ReplaceAll" oncommand="ReplaceAll();" label="&replaceAllButton.label;"
+ accesskey="&replaceAllButton.accessKey;"/>
+ <button id="IgnoreAll" oncommand="IgnoreAll();" label="&ignoreAllButton.label;"
+ accesskey="&ignoreAllButton.accessKey;"/>
+ </row>
+ </rows>
+ </grid>
+ <separator/>
+ <label value="&userDictionary.label;"/>
+ <hbox align="start">
+ <button class="spell-check" id="AddToDictionary" oncommand="AddToDictionary()" label="&addToUserDictionaryButton.label;"
+ accesskey="&addToUserDictionaryButton.accessKey;"/>
+ <button class="spell-check" id="EditDictionary" oncommand="EditDictionary()" label="&editUserDictionaryButton.label;"
+ accesskey="&editUserDictionaryButton.accessKey;"/>
+ </hbox>
+ </vbox>
+ </row>
+ <label value ="&languagePopup.label;"
+ control="LanguageMenulist"
+ accesskey="&languagePopup.accessKey;"/>
+ <row>
+ <menulist id="LanguageMenulist" oncommand="SelectLanguage()">
+ <menupopup onpopupshowing="InitLanguageMenu(gDialog.LanguageMenulist.selectedItem.value);">
+ <!-- dynamic content populated by JS -->
+ <menuseparator/>
+ <menuitem value="more-cmd" label="&moreDictionaries.label;"/>
+ </menupopup>
+ </menulist>
+ <hbox flex="1">
+ <button class="spell-check" dlgtype="cancel" id="Stop" label="&stopButton.label;" oncommand="CancelSpellCheck();"
+ accesskey="&stopButton.accessKey;"/>
+ <spacer flex="1"/>
+ <button class="spell-check" id="Close" label="&closeButton.label;" oncommand="onClose();"
+ accesskey="&closeButton.accessKey;"/>
+ <button class="spell-check" id="Send" label="&sendButton.label;" oncommand="onClose();"
+ accesskey="&sendButton.accessKey;" hidden="true"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdTableProps.js b/comm/suite/editor/components/dialogs/content/EdTableProps.js
new file mode 100644
index 0000000000..e78a89bc41
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdTableProps.js
@@ -0,0 +1,1439 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+// Cancel() is in EdDialogCommon.js
+
+var gTableElement;
+var gCellElement;
+var gTableCaptionElement;
+var globalCellElement;
+var globalTableElement;
+var gValidateTab;
+const defHAlign = "left";
+const centerStr = "center"; // Index=1
+const rightStr = "right"; // 2
+const justifyStr = "justify"; // 3
+const charStr = "char"; // 4
+const defVAlign = "middle";
+const topStr = "top";
+const bottomStr = "bottom";
+const bgcolor = "bgcolor";
+var gTableColor;
+var gCellColor;
+
+const cssBackgroundColorStr = "background-color";
+
+var gRowCount = 1;
+var gColCount = 1;
+var gLastRowIndex;
+var gLastColIndex;
+var gNewRowCount;
+var gNewColCount;
+var gCurRowIndex;
+var gCurColIndex;
+var gCurColSpan;
+var gSelectedCellsType = 1;
+const SELECT_CELL = 1;
+const SELECT_ROW = 2;
+const SELECT_COLUMN = 3;
+const RESET_SELECTION = 0;
+var gCellData = {
+ value: null,
+ startRowIndex: 0,
+ startColIndex: 0,
+ rowSpan: 0,
+ colSpan: 0,
+ actualRowSpan: 0,
+ actualColSpan: 0,
+ isSelected: false,
+};
+var gAdvancedEditUsed;
+var gAlignWasChar = false;
+
+/*
+From C++:
+ 0 TABLESELECTION_TABLE
+ 1 TABLESELECTION_CELL There are 1 or more cells selected
+ but complete rows or columns are not selected
+ 2 TABLESELECTION_ROW All cells are in 1 or more rows
+ and in each row, all cells selected
+ Note: This is the value if all rows (thus all cells) are selected
+ 3 TABLESELECTION_COLUMN All cells are in 1 or more columns
+*/
+
+var gSelectedCellCount = 0;
+var gApplyUsed = false;
+var gSelection;
+var gCellDataChanged = false;
+var gCanDelete = false;
+var gUseCSS = true;
+var gActiveEditor;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogextra1", Apply);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ gActiveEditor = GetCurrentTableEditor();
+ if (!gActiveEditor) {
+ window.close();
+ return;
+ }
+
+ try {
+ gSelection = gActiveEditor.selection;
+ } catch (e) {}
+ if (!gSelection) {
+ return;
+ }
+
+ // Get dialog widgets - Table Panel
+ gDialog.TableRowsInput = document.getElementById("TableRowsInput");
+ gDialog.TableColumnsInput = document.getElementById("TableColumnsInput");
+ gDialog.TableWidthInput = document.getElementById("TableWidthInput");
+ gDialog.TableWidthUnits = document.getElementById("TableWidthUnits");
+ gDialog.TableHeightInput = document.getElementById("TableHeightInput");
+ gDialog.TableHeightUnits = document.getElementById("TableHeightUnits");
+ try {
+ if (
+ !Services.prefs.getBoolPref("editor.use_css") ||
+ gActiveEditor.flags & 1
+ ) {
+ gUseCSS = false;
+ var tableHeightLabel = document.getElementById("TableHeightLabel");
+ tableHeightLabel.remove();
+ gDialog.TableHeightInput.remove();
+ gDialog.TableHeightUnits.remove();
+ }
+ } catch (e) {}
+ gDialog.BorderWidthInput = document.getElementById("BorderWidthInput");
+ gDialog.SpacingInput = document.getElementById("SpacingInput");
+ gDialog.PaddingInput = document.getElementById("PaddingInput");
+ gDialog.TableAlignList = document.getElementById("TableAlignList");
+ gDialog.TableCaptionList = document.getElementById("TableCaptionList");
+ gDialog.TableInheritColor = document.getElementById("TableInheritColor");
+ gDialog.TabBox = document.getElementById("TabBox");
+
+ // Cell Panel
+ gDialog.SelectionList = document.getElementById("SelectionList");
+ gDialog.PreviousButton = document.getElementById("PreviousButton");
+ gDialog.NextButton = document.getElementById("NextButton");
+ // Currently, we always apply changes and load new attributes when changing selection
+ // (Let's keep this for possible future use)
+ // gDialog.ApplyBeforeMove = document.getElementById("ApplyBeforeMove");
+ // gDialog.KeepCurrentData = document.getElementById("KeepCurrentData");
+
+ gDialog.CellHeightInput = document.getElementById("CellHeightInput");
+ gDialog.CellHeightUnits = document.getElementById("CellHeightUnits");
+ gDialog.CellWidthInput = document.getElementById("CellWidthInput");
+ gDialog.CellWidthUnits = document.getElementById("CellWidthUnits");
+ gDialog.CellHAlignList = document.getElementById("CellHAlignList");
+ gDialog.CellVAlignList = document.getElementById("CellVAlignList");
+ gDialog.CellInheritColor = document.getElementById("CellInheritColor");
+ gDialog.CellStyleList = document.getElementById("CellStyleList");
+ gDialog.TextWrapList = document.getElementById("TextWrapList");
+
+ // In cell panel, user must tell us which attributes to apply via checkboxes,
+ // else we would apply values from one cell to ALL in selection
+ // and that's probably not what they expect!
+ gDialog.CellHeightCheckbox = document.getElementById("CellHeightCheckbox");
+ gDialog.CellWidthCheckbox = document.getElementById("CellWidthCheckbox");
+ gDialog.CellHAlignCheckbox = document.getElementById("CellHAlignCheckbox");
+ gDialog.CellVAlignCheckbox = document.getElementById("CellVAlignCheckbox");
+ gDialog.CellStyleCheckbox = document.getElementById("CellStyleCheckbox");
+ gDialog.TextWrapCheckbox = document.getElementById("TextWrapCheckbox");
+ gDialog.CellColorCheckbox = document.getElementById("CellColorCheckbox");
+ gDialog.TableTab = document.getElementById("TableTab");
+ gDialog.CellTab = document.getElementById("CellTab");
+ gDialog.AdvancedEditCell = document.getElementById("AdvancedEditButton2");
+ // Save "normal" tooltip message for Advanced Edit button
+ gDialog.AdvancedEditCellToolTipText = gDialog.AdvancedEditCell.getAttribute(
+ "tooltiptext"
+ );
+
+ try {
+ gTableElement = gActiveEditor.getElementOrParentByTagName("table", null);
+ } catch (e) {}
+ if (!gTableElement) {
+ dump("Failed to get table element!\n");
+ window.close();
+ return;
+ }
+ globalTableElement = gTableElement.cloneNode(false);
+
+ var tagNameObj = { value: "" };
+ var countObj = { value: 0 };
+ var tableOrCellElement;
+ try {
+ tableOrCellElement = gActiveEditor.getSelectedOrParentTableElement(
+ tagNameObj,
+ countObj
+ );
+ } catch (e) {}
+
+ if (tagNameObj.value == "td") {
+ // We are in a cell
+ gSelectedCellCount = countObj.value;
+ gCellElement = tableOrCellElement;
+ globalCellElement = gCellElement.cloneNode(false);
+
+ // Tells us whether cell, row, or column is selected
+ try {
+ gSelectedCellsType = gActiveEditor.getSelectedCellsType(gTableElement);
+ } catch (e) {}
+
+ // Ignore types except Cell, Row, and Column
+ if (
+ gSelectedCellsType < SELECT_CELL ||
+ gSelectedCellsType > SELECT_COLUMN
+ ) {
+ gSelectedCellsType = SELECT_CELL;
+ }
+
+ // Be sure at least 1 cell is selected.
+ // (If the count is 0, then we were inside the cell.)
+ if (gSelectedCellCount == 0) {
+ DoCellSelection();
+ }
+
+ // Get location in the cell map
+ var rowIndexObj = { value: 0 };
+ var colIndexObj = { value: 0 };
+ try {
+ gActiveEditor.getCellIndexes(gCellElement, rowIndexObj, colIndexObj);
+ } catch (e) {}
+ gCurRowIndex = rowIndexObj.value;
+ gCurColIndex = colIndexObj.value;
+
+ // We save the current colspan to quickly
+ // move selection from from cell to cell
+ if (GetCellData(gCurRowIndex, gCurColIndex)) {
+ gCurColSpan = gCellData.colSpan;
+ }
+
+ // Starting TabPanel name is passed in
+ if (window.arguments[1] == "CellPanel") {
+ gDialog.TabBox.selectedTab = gDialog.CellTab;
+ }
+ }
+
+ if (gDialog.TabBox.selectedTab == gDialog.TableTab) {
+ // We may call this with table selected, but no cell,
+ // so disable the Cell Properties tab
+ if (!gCellElement) {
+ // XXX: Disabling of tabs is currently broken, so for
+ // now we'll just remove the tab completely.
+ // gDialog.CellTab.disabled = true;
+ gDialog.CellTab.remove();
+ }
+ }
+
+ // Note: we must use gTableElement, not globalTableElement for these,
+ // thus we should not put this in InitDialog.
+ // Instead, monitor desired counts with separate globals
+ var rowCountObj = { value: 0 };
+ var colCountObj = { value: 0 };
+ try {
+ gActiveEditor.getTableSize(gTableElement, rowCountObj, colCountObj);
+ } catch (e) {}
+
+ gRowCount = rowCountObj.value;
+ gLastRowIndex = gRowCount - 1;
+ gColCount = colCountObj.value;
+ gLastColIndex = gColCount - 1;
+
+ // Set appropriate icons and enable state for the Previous/Next buttons
+ SetSelectionButtons();
+
+ // If only one cell in table, disable change-selection widgets
+ if (gRowCount == 1 && gColCount == 1) {
+ gDialog.SelectionList.setAttribute("disabled", "true");
+ }
+
+ // User can change these via textboxes
+ gNewRowCount = gRowCount;
+ gNewColCount = gColCount;
+
+ // This flag is used to control whether set check state
+ // on "set attribute" checkboxes
+ // (Advanced Edit dialog use calls InitDialog when done)
+ gAdvancedEditUsed = false;
+ InitDialog();
+ gAdvancedEditUsed = true;
+
+ // If first initializing, we really aren't changing anything
+ gCellDataChanged = false;
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ // Get Table attributes
+ gDialog.TableRowsInput.value = gRowCount;
+ gDialog.TableColumnsInput.value = gColCount;
+ gDialog.TableWidthInput.value = InitPixelOrPercentMenulist(
+ globalTableElement,
+ gTableElement,
+ "width",
+ "TableWidthUnits",
+ gPercent
+ );
+ if (gUseCSS) {
+ gDialog.TableHeightInput.value = InitPixelOrPercentMenulist(
+ globalTableElement,
+ gTableElement,
+ "height",
+ "TableHeightUnits",
+ gPercent
+ );
+ }
+ gDialog.BorderWidthInput.value = globalTableElement.border;
+ gDialog.SpacingInput.value = globalTableElement.cellSpacing;
+ gDialog.PaddingInput.value = globalTableElement.cellPadding;
+
+ var marginLeft = GetHTMLOrCSSStyleValue(
+ globalTableElement,
+ "align",
+ "margin-left"
+ );
+ var marginRight = GetHTMLOrCSSStyleValue(
+ globalTableElement,
+ "align",
+ "margin-right"
+ );
+ var halign = marginLeft.toLowerCase() + " " + marginRight.toLowerCase();
+ if (halign == "center center" || halign == "auto auto") {
+ gDialog.TableAlignList.value = "center";
+ } else if (halign == "right right" || halign == "auto 0px") {
+ gDialog.TableAlignList.value = "right";
+ } else {
+ // Default is left.
+ gDialog.TableAlignList.value = "left";
+ }
+
+ // Be sure to get caption from table in doc, not the copied "globalTableElement"
+ gTableCaptionElement = gTableElement.caption;
+ if (gTableCaptionElement) {
+ var align = GetHTMLOrCSSStyleValue(
+ gTableCaptionElement,
+ "align",
+ "caption-side"
+ );
+ if (align != "bottom" && align != "left" && align != "right") {
+ align = "top";
+ }
+ gDialog.TableCaptionList.value = align;
+ }
+
+ gTableColor = GetHTMLOrCSSStyleValue(
+ globalTableElement,
+ bgcolor,
+ cssBackgroundColorStr
+ );
+ gTableColor = ConvertRGBColorIntoHEXColor(gTableColor);
+ SetColor("tableBackgroundCW", gTableColor);
+
+ InitCellPanel();
+}
+
+function InitCellPanel() {
+ // Get cell attributes
+ if (globalCellElement) {
+ // This assumes order of items is Cell, Row, Column
+ gDialog.SelectionList.value = gSelectedCellsType;
+
+ var previousValue = gDialog.CellHeightInput.value;
+ gDialog.CellHeightInput.value = InitPixelOrPercentMenulist(
+ globalCellElement,
+ gCellElement,
+ "height",
+ "CellHeightUnits",
+ gPixel
+ );
+ gDialog.CellHeightCheckbox.checked =
+ gAdvancedEditUsed && previousValue != gDialog.CellHeightInput.value;
+
+ previousValue = gDialog.CellWidthInput.value;
+ gDialog.CellWidthInput.value = InitPixelOrPercentMenulist(
+ globalCellElement,
+ gCellElement,
+ "width",
+ "CellWidthUnits",
+ gPixel
+ );
+ gDialog.CellWidthCheckbox.checked =
+ gAdvancedEditUsed && previousValue != gDialog.CellWidthInput.value;
+
+ var previousIndex = gDialog.CellVAlignList.selectedIndex;
+ var valign = GetHTMLOrCSSStyleValue(
+ globalCellElement,
+ "valign",
+ "vertical-align"
+ ).toLowerCase();
+ if (valign == topStr || valign == bottomStr) {
+ gDialog.CellVAlignList.value = valign;
+ } else {
+ // Default is middle.
+ gDialog.CellVAlignList.value = defVAlign;
+ }
+
+ gDialog.CellVAlignCheckbox.checked =
+ gAdvancedEditUsed &&
+ previousIndex != gDialog.CellVAlignList.selectedIndex;
+
+ previousIndex = gDialog.CellHAlignList.selectedIndex;
+
+ gAlignWasChar = false;
+
+ var halign = GetHTMLOrCSSStyleValue(
+ globalCellElement,
+ "align",
+ "text-align"
+ ).toLowerCase();
+ switch (halign) {
+ case centerStr:
+ case rightStr:
+ case justifyStr:
+ gDialog.CellHAlignList.value = halign;
+ break;
+ case charStr:
+ // We don't support UI for this because layout doesn't work: bug 2212.
+ // Remember that's what they had so we don't change it
+ // unless they change the alignment by using the menulist
+ gAlignWasChar = true;
+ // Fall through to use show default alignment in menu
+ default:
+ // Default depends on cell type (TH is "center", TD is "left")
+ gDialog.CellHAlignList.value =
+ globalCellElement.nodeName.toLowerCase() == "th" ? "center" : "left";
+ break;
+ }
+
+ gDialog.CellHAlignCheckbox.checked =
+ gAdvancedEditUsed &&
+ previousIndex != gDialog.CellHAlignList.selectedIndex;
+
+ previousIndex = gDialog.CellStyleList.selectedIndex;
+ gDialog.CellStyleList.value = globalCellElement.nodeName.toLowerCase();
+ gDialog.CellStyleCheckbox.checked =
+ gAdvancedEditUsed && previousIndex != gDialog.CellStyleList.selectedIndex;
+
+ previousIndex = gDialog.TextWrapList.selectedIndex;
+ if (
+ GetHTMLOrCSSStyleValue(globalCellElement, "nowrap", "white-space") ==
+ "nowrap"
+ ) {
+ gDialog.TextWrapList.value = "nowrap";
+ } else {
+ gDialog.TextWrapList.value = "wrap";
+ }
+ gDialog.TextWrapCheckbox.checked =
+ gAdvancedEditUsed && previousIndex != gDialog.TextWrapList.selectedIndex;
+
+ previousValue = gCellColor;
+ gCellColor = GetHTMLOrCSSStyleValue(
+ globalCellElement,
+ bgcolor,
+ cssBackgroundColorStr
+ );
+ gCellColor = ConvertRGBColorIntoHEXColor(gCellColor);
+ SetColor("cellBackgroundCW", gCellColor);
+ gDialog.CellColorCheckbox.checked =
+ gAdvancedEditUsed && previousValue != gCellColor;
+
+ // We want to set this true in case changes came
+ // from Advanced Edit dialog session (must assume something changed)
+ gCellDataChanged = true;
+ }
+}
+
+function GetCellData(rowIndex, colIndex) {
+ // Get actual rowspan and colspan
+ var startRowIndexObj = { value: 0 };
+ var startColIndexObj = { value: 0 };
+ var rowSpanObj = { value: 0 };
+ var colSpanObj = { value: 0 };
+ var actualRowSpanObj = { value: 0 };
+ var actualColSpanObj = { value: 0 };
+ var isSelectedObj = { value: false };
+
+ try {
+ gActiveEditor.getCellDataAt(
+ gTableElement,
+ rowIndex,
+ colIndex,
+ gCellData,
+ startRowIndexObj,
+ startColIndexObj,
+ rowSpanObj,
+ colSpanObj,
+ actualRowSpanObj,
+ actualColSpanObj,
+ isSelectedObj
+ );
+ // We didn't find a cell
+ if (!gCellData.value) {
+ return false;
+ }
+ } catch (ex) {
+ return false;
+ }
+
+ gCellData.startRowIndex = startRowIndexObj.value;
+ gCellData.startColIndex = startColIndexObj.value;
+ gCellData.rowSpan = rowSpanObj.value;
+ gCellData.colSpan = colSpanObj.value;
+ gCellData.actualRowSpan = actualRowSpanObj.value;
+ gCellData.actualColSpan = actualColSpanObj.value;
+ gCellData.isSelected = isSelectedObj.value;
+ return true;
+}
+
+function SelectCellHAlign() {
+ SetCheckbox("CellHAlignCheckbox");
+ // Once user changes the alignment,
+ // we lose their original "CharAt" alignment"
+ gAlignWasChar = false;
+}
+
+function GetColorAndUpdate(ColorWellID) {
+ var colorWell = document.getElementById(ColorWellID);
+ if (!colorWell) {
+ return;
+ }
+
+ var colorObj = {
+ Type: "",
+ TableColor: 0,
+ CellColor: 0,
+ NoDefault: false,
+ Cancel: false,
+ BackgroundColor: 0,
+ };
+
+ switch (ColorWellID) {
+ case "tableBackgroundCW":
+ colorObj.Type = "Table";
+ colorObj.TableColor = gTableColor;
+ break;
+ case "cellBackgroundCW":
+ colorObj.Type = "Cell";
+ colorObj.CellColor = gCellColor;
+ break;
+ }
+ window.openDialog(
+ "chrome://editor/content/EdColorPicker.xhtml",
+ "_blank",
+ "chrome,close,titlebar,modal",
+ "",
+ colorObj
+ );
+
+ // User canceled the dialog
+ if (colorObj.Cancel) {
+ return;
+ }
+
+ switch (ColorWellID) {
+ case "tableBackgroundCW":
+ gTableColor = colorObj.BackgroundColor;
+ SetColor(ColorWellID, gTableColor);
+ break;
+ case "cellBackgroundCW":
+ gCellColor = colorObj.BackgroundColor;
+ SetColor(ColorWellID, gCellColor);
+ SetCheckbox("CellColorCheckbox");
+ break;
+ }
+}
+
+function SetColor(ColorWellID, color) {
+ // Save the color
+ if (ColorWellID == "cellBackgroundCW") {
+ if (color) {
+ try {
+ gActiveEditor.setAttributeOrEquivalent(
+ globalCellElement,
+ bgcolor,
+ color,
+ true
+ );
+ } catch (e) {}
+ gDialog.CellInheritColor.collapsed = true;
+ } else {
+ try {
+ gActiveEditor.removeAttributeOrEquivalent(
+ globalCellElement,
+ bgcolor,
+ true
+ );
+ } catch (e) {}
+ // Reveal addition message explaining "default" color
+ gDialog.CellInheritColor.collapsed = false;
+ }
+ } else {
+ if (color) {
+ try {
+ gActiveEditor.setAttributeOrEquivalent(
+ globalTableElement,
+ bgcolor,
+ color,
+ true
+ );
+ } catch (e) {}
+ gDialog.TableInheritColor.collapsed = true;
+ } else {
+ try {
+ gActiveEditor.removeAttributeOrEquivalent(
+ globalTableElement,
+ bgcolor,
+ true
+ );
+ } catch (e) {}
+ gDialog.TableInheritColor.collapsed = false;
+ }
+ SetCheckbox("CellColorCheckbox");
+ }
+
+ setColorWell(ColorWellID, color);
+}
+
+function ChangeSelectionToFirstCell() {
+ if (!GetCellData(0, 0)) {
+ dump("Can't find first cell in table!\n");
+ return;
+ }
+ gCellElement = gCellData.value;
+ globalCellElement = gCellElement;
+
+ gCurRowIndex = 0;
+ gCurColIndex = 0;
+ ChangeSelection(RESET_SELECTION);
+}
+
+function ChangeSelection(newType) {
+ newType = Number(newType);
+
+ if (gSelectedCellsType == newType) {
+ return;
+ }
+
+ if (newType == RESET_SELECTION) {
+ // Restore selection to existing focus cell
+ gSelection.collapse(gCellElement, 0);
+ } else {
+ gSelectedCellsType = newType;
+ }
+
+ // Keep the same focus gCellElement, just change the type
+ DoCellSelection();
+ SetSelectionButtons();
+
+ // Note: globalCellElement should still be a clone of gCellElement
+}
+
+function MoveSelection(forward) {
+ var newRowIndex = gCurRowIndex;
+ var newColIndex = gCurColIndex;
+ var inRow = false;
+
+ if (gSelectedCellsType == SELECT_ROW) {
+ newRowIndex += forward ? 1 : -1;
+
+ // Wrap around if before first or after last row
+ if (newRowIndex < 0) {
+ newRowIndex = gLastRowIndex;
+ } else if (newRowIndex > gLastRowIndex) {
+ newRowIndex = 0;
+ }
+ inRow = true;
+
+ // Use first cell in row for focus cell
+ newColIndex = 0;
+ } else {
+ // Cell or column:
+ if (!forward) {
+ newColIndex--;
+ }
+
+ if (gSelectedCellsType == SELECT_CELL) {
+ // Skip to next cell
+ if (forward) {
+ newColIndex += gCurColSpan;
+ }
+ } else {
+ // SELECT_COLUMN
+ // Use first cell in column for focus cell
+ newRowIndex = 0;
+
+ // Don't skip by colspan,
+ // but find first cell in next cellmap column
+ if (forward) {
+ newColIndex++;
+ }
+ }
+
+ if (newColIndex < 0) {
+ // Request is before the first cell in column
+
+ // Wrap to last cell in column
+ newColIndex = gLastColIndex;
+
+ if (gSelectedCellsType == SELECT_CELL) {
+ // If moving by cell, also wrap to previous...
+ if (newRowIndex > 0) {
+ newRowIndex -= 1;
+ } else {
+ // ...or the last row.
+ newRowIndex = gLastRowIndex;
+ }
+
+ inRow = true;
+ }
+ } else if (newColIndex > gLastColIndex) {
+ // Request is after the last cell in column
+
+ // Wrap to first cell in column
+ newColIndex = 0;
+
+ if (gSelectedCellsType == SELECT_CELL) {
+ // If moving by cell, also wrap to next...
+ if (newRowIndex < gLastRowIndex) {
+ newRowIndex++;
+ } else {
+ // ...or the first row.
+ newRowIndex = 0;
+ }
+
+ inRow = true;
+ }
+ }
+ }
+
+ // Get the cell at the new location
+ do {
+ if (!GetCellData(newRowIndex, newColIndex)) {
+ dump("MoveSelection: CELL NOT FOUND\n");
+ return;
+ }
+ if (inRow) {
+ if (gCellData.startRowIndex == newRowIndex) {
+ break;
+ } else {
+ // Cell spans from a row above, look for the next cell in row.
+ newRowIndex += gCellData.actualRowSpan;
+ }
+ } else if (gCellData.startColIndex == newColIndex) {
+ break;
+ } else {
+ // Cell spans from a Col above, look for the next cell in column
+ newColIndex += gCellData.actualColSpan;
+ }
+ } while (true);
+
+ // Save data for current selection before changing
+ if (gCellDataChanged) {
+ // && gDialog.ApplyBeforeMove.checked)
+ if (!ValidateCellData()) {
+ return;
+ }
+
+ gActiveEditor.beginTransaction();
+ // Apply changes to all selected cells
+ ApplyCellAttributes();
+ gActiveEditor.endTransaction();
+
+ SetCloseButton();
+ }
+
+ // Set cell and other data for new selection
+ gCellElement = gCellData.value;
+
+ // Save globals for new current cell
+ gCurRowIndex = gCellData.startRowIndex;
+ gCurColIndex = gCellData.startColIndex;
+ gCurColSpan = gCellData.actualColSpan;
+
+ // Copy for new global cell
+ globalCellElement = gCellElement.cloneNode(false);
+
+ // Change the selection
+ DoCellSelection();
+
+ // Scroll page so new selection is visible
+ // Using SELECTION_ANCHOR_REGION makes the upper-left corner of first selected cell
+ // the point to bring into view.
+ try {
+ var selectionController = gActiveEditor.selectionController;
+ selectionController.scrollSelectionIntoView(
+ selectionController.SELECTION_NORMAL,
+ selectionController.SELECTION_ANCHOR_REGION,
+ true
+ );
+ } catch (e) {}
+
+ // Reinitialize dialog using new cell
+ // if (!gDialog.KeepCurrentData.checked)
+ // Setting this false unchecks all "set attributes" checkboxes
+ gAdvancedEditUsed = false;
+ InitCellPanel();
+ gAdvancedEditUsed = true;
+}
+
+function DoCellSelection() {
+ // Collapse selection into to the focus cell
+ // so editor uses that as start cell
+ gSelection.collapse(gCellElement, 0);
+
+ var tagNameObj = { value: "" };
+ var countObj = { value: 0 };
+ try {
+ switch (gSelectedCellsType) {
+ case SELECT_CELL:
+ gActiveEditor.selectTableCell();
+ break;
+ case SELECT_ROW:
+ gActiveEditor.selectTableRow();
+ break;
+ default:
+ gActiveEditor.selectTableColumn();
+ break;
+ }
+ // Get number of cells selected
+ gActiveEditor.getSelectedOrParentTableElement(tagNameObj, countObj);
+ } catch (e) {}
+
+ if (tagNameObj.value == "td") {
+ gSelectedCellCount = countObj.value;
+ } else {
+ gSelectedCellCount = 0;
+ }
+
+ // Currently, we can only allow advanced editing on ONE cell element at a time
+ // else we ignore CSS, JS, and HTML attributes not already in dialog
+ SetElementEnabled(gDialog.AdvancedEditCell, gSelectedCellCount == 1);
+
+ gDialog.AdvancedEditCell.setAttribute(
+ "tooltiptext",
+ gSelectedCellCount > 1
+ ? GetString("AdvancedEditForCellMsg")
+ : gDialog.AdvancedEditCellToolTipText
+ );
+}
+
+function SetSelectionButtons() {
+ if (gSelectedCellsType == SELECT_ROW) {
+ // Trigger CSS to set images of up and down arrows
+ gDialog.PreviousButton.setAttribute("type", "row");
+ gDialog.NextButton.setAttribute("type", "row");
+ } else {
+ // or images of left and right arrows
+ gDialog.PreviousButton.setAttribute("type", "col");
+ gDialog.NextButton.setAttribute("type", "col");
+ }
+ DisableSelectionButtons(
+ (gSelectedCellsType == SELECT_ROW && gRowCount == 1) ||
+ (gSelectedCellsType == SELECT_COLUMN && gColCount == 1) ||
+ (gRowCount == 1 && gColCount == 1)
+ );
+}
+
+function DisableSelectionButtons(disable) {
+ gDialog.PreviousButton.setAttribute("disabled", disable ? "true" : "false");
+ gDialog.NextButton.setAttribute("disabled", disable ? "true" : "false");
+}
+
+function SwitchToValidatePanel() {
+ if (gDialog.TabBox.selectedTab != gValidateTab) {
+ gDialog.TabBox.selectedTab = gValidateTab;
+ }
+}
+
+function SetAlign(listID, defaultValue, element, attName) {
+ var value = document.getElementById(listID).value;
+ if (value == defaultValue) {
+ try {
+ gActiveEditor.removeAttributeOrEquivalent(element, attName, true);
+ } catch (e) {}
+ } else {
+ try {
+ gActiveEditor.setAttributeOrEquivalent(element, attName, value, true);
+ } catch (e) {}
+ }
+}
+
+function ValidateTableData() {
+ gValidateTab = gDialog.TableTab;
+ gNewRowCount = Number(
+ ValidateNumber(gDialog.TableRowsInput, null, 1, gMaxRows, null, true, true)
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ gNewColCount = Number(
+ ValidateNumber(
+ gDialog.TableColumnsInput,
+ null,
+ 1,
+ gMaxColumns,
+ null,
+ true,
+ true
+ )
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ // If user is deleting any cells, get confirmation
+ // (This is a global to the dialog and we ask only once per dialog session)
+ if (!gCanDelete && (gNewRowCount < gRowCount || gNewColCount < gColCount)) {
+ if (
+ ConfirmWithTitle(
+ GetString("DeleteTableTitle"),
+ GetString("DeleteTableMsg"),
+ GetString("DeleteCells")
+ )
+ ) {
+ gCanDelete = true;
+ } else {
+ SetTextboxFocus(
+ gNewRowCount < gRowCount
+ ? gDialog.TableRowsInput
+ : gDialog.TableColumnsInput
+ );
+ return false;
+ }
+ }
+
+ ValidateNumber(
+ gDialog.TableWidthInput,
+ gDialog.TableWidthUnits,
+ 1,
+ gMaxTableSize,
+ globalTableElement,
+ "width"
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ if (gUseCSS) {
+ ValidateNumber(
+ gDialog.TableHeightInput,
+ gDialog.TableHeightUnits,
+ 1,
+ gMaxTableSize,
+ globalTableElement,
+ "height"
+ );
+ if (gValidationError) {
+ return false;
+ }
+ }
+
+ ValidateNumber(
+ gDialog.BorderWidthInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalTableElement,
+ "border"
+ );
+ // TODO: Deal with "BORDER" without value issue
+ if (gValidationError) {
+ return false;
+ }
+
+ ValidateNumber(
+ gDialog.SpacingInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalTableElement,
+ "cellspacing"
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ ValidateNumber(
+ gDialog.PaddingInput,
+ null,
+ 0,
+ gMaxPixels,
+ globalTableElement,
+ "cellpadding"
+ );
+ if (gValidationError) {
+ return false;
+ }
+
+ SetAlign("TableAlignList", defHAlign, globalTableElement, "align");
+
+ // Color is set on globalCellElement immediately
+ return true;
+}
+
+function ValidateCellData() {
+ gValidateTab = gDialog.CellTab;
+
+ if (gDialog.CellHeightCheckbox.checked) {
+ ValidateNumber(
+ gDialog.CellHeightInput,
+ gDialog.CellHeightUnits,
+ 1,
+ gMaxTableSize,
+ globalCellElement,
+ "height"
+ );
+ if (gValidationError) {
+ return false;
+ }
+ }
+
+ if (gDialog.CellWidthCheckbox.checked) {
+ ValidateNumber(
+ gDialog.CellWidthInput,
+ gDialog.CellWidthUnits,
+ 1,
+ gMaxTableSize,
+ globalCellElement,
+ "width"
+ );
+ if (gValidationError) {
+ return false;
+ }
+ }
+
+ if (gDialog.CellHAlignCheckbox.checked) {
+ var hAlign = gDialog.CellHAlignList.value;
+
+ // Horizontal alignment is complicated by "char" type
+ // We don't change current values if user didn't edit alignment
+ if (!gAlignWasChar) {
+ globalCellElement.removeAttribute(charStr);
+
+ // Always set "align" attribute,
+ // so the default "left" is effective in a cell
+ // when parent row has align set.
+ globalCellElement.setAttribute("align", hAlign);
+ }
+ }
+
+ if (gDialog.CellVAlignCheckbox.checked) {
+ // Always set valign (no default in 2nd param) so
+ // the default "middle" is effective in a cell
+ // when parent row has valign set.
+ SetAlign("CellVAlignList", "", globalCellElement, "valign");
+ }
+
+ if (gDialog.TextWrapCheckbox.checked) {
+ if (gDialog.TextWrapList.value == "nowrap") {
+ try {
+ gActiveEditor.setAttributeOrEquivalent(
+ globalCellElement,
+ "nowrap",
+ "nowrap",
+ true
+ );
+ } catch (e) {}
+ } else {
+ try {
+ gActiveEditor.removeAttributeOrEquivalent(
+ globalCellElement,
+ "nowrap",
+ true
+ );
+ } catch (e) {}
+ }
+ }
+
+ return true;
+}
+
+function ValidateData() {
+ var result;
+
+ // Validate current panel first
+ if (gDialog.TabBox.selectedTab == gDialog.TableTab) {
+ result = ValidateTableData();
+ if (result) {
+ result = ValidateCellData();
+ }
+ } else {
+ result = ValidateCellData();
+ if (result) {
+ result = ValidateTableData();
+ }
+ }
+ if (!result) {
+ return false;
+ }
+
+ // Set global element for AdvancedEdit
+ if (gDialog.TabBox.selectedTab == gDialog.TableTab) {
+ globalElement = globalTableElement;
+ } else {
+ globalElement = globalCellElement;
+ }
+
+ return true;
+}
+
+function ChangeCellTextbox(textboxID) {
+ // Filter input for just integers
+ forceInteger(textboxID);
+
+ if (gDialog.TabBox.selectedTab == gDialog.CellTab) {
+ gCellDataChanged = true;
+ }
+}
+
+// Call this when a textbox or menulist is changed
+// so the checkbox is automatically set
+function SetCheckbox(checkboxID) {
+ if (checkboxID && checkboxID.length > 0) {
+ // Set associated checkbox
+ document.getElementById(checkboxID).checked = true;
+ }
+ gCellDataChanged = true;
+}
+
+function ChangeIntTextbox(textboxID, checkboxID) {
+ // Filter input for just integers
+ forceInteger(textboxID);
+
+ // Set associated checkbox
+ SetCheckbox(checkboxID);
+}
+
+function CloneAttribute(destElement, srcElement, attr) {
+ var value = srcElement.getAttribute(attr);
+ // Use editor methods since we are always
+ // modifying a table in the document and
+ // we need transaction system for undo
+ try {
+ if (!value || value.length == 0) {
+ gActiveEditor.removeAttributeOrEquivalent(destElement, attr, false);
+ } else {
+ gActiveEditor.setAttributeOrEquivalent(destElement, attr, value, false);
+ }
+ } catch (e) {}
+}
+
+/* eslint-disable complexity */
+function ApplyTableAttributes() {
+ var newAlign = gDialog.TableCaptionList.value;
+ if (!newAlign) {
+ newAlign = "";
+ }
+
+ if (gTableCaptionElement) {
+ // Get current alignment
+ var align = GetHTMLOrCSSStyleValue(
+ gTableCaptionElement,
+ "align",
+ "caption-side"
+ ).toLowerCase();
+ // This is the default
+ if (!align) {
+ align = "top";
+ }
+
+ if (newAlign == "") {
+ // Remove existing caption
+ try {
+ gActiveEditor.deleteNode(gTableCaptionElement);
+ } catch (e) {}
+ gTableCaptionElement = null;
+ } else if (newAlign != align) {
+ try {
+ if (newAlign == "top") {
+ // This is default, so don't explicitly set it
+ gActiveEditor.removeAttributeOrEquivalent(
+ gTableCaptionElement,
+ "align",
+ false
+ );
+ } else {
+ gActiveEditor.setAttributeOrEquivalent(
+ gTableCaptionElement,
+ "align",
+ newAlign,
+ false
+ );
+ }
+ } catch (e) {}
+ }
+ } else if (newAlign != "") {
+ // Create and insert a caption:
+ try {
+ gTableCaptionElement = gActiveEditor.createElementWithDefaults("caption");
+ } catch (e) {}
+ if (gTableCaptionElement) {
+ if (newAlign != "top") {
+ gTableCaptionElement.setAttribute("align", newAlign);
+ }
+
+ // Insert it into the table - caption is always inserted as first child
+ try {
+ gActiveEditor.insertNode(gTableCaptionElement, gTableElement, 0);
+ } catch (e) {}
+
+ // Put selection back where it was
+ ChangeSelection(RESET_SELECTION);
+ }
+ }
+
+ var countDelta;
+ var foundCell;
+ var i;
+
+ if (gNewRowCount != gRowCount) {
+ countDelta = gNewRowCount - gRowCount;
+ if (gNewRowCount > gRowCount) {
+ // Append new rows
+ // Find first cell in last row
+ if (GetCellData(gLastRowIndex, 0)) {
+ try {
+ // Move selection to the last cell
+ gSelection.collapse(gCellData.value, 0);
+ // Insert new rows after it
+ gActiveEditor.insertTableRow(countDelta, true);
+ gRowCount = gNewRowCount;
+ gLastRowIndex = gRowCount - 1;
+ // Put selection back where it was
+ ChangeSelection(RESET_SELECTION);
+ } catch (ex) {
+ dump("FAILED TO FIND FIRST CELL IN LAST ROW\n");
+ }
+ }
+ } else if (gCanDelete) {
+ // Delete rows
+ // Find first cell starting in first row we delete
+ var firstDeleteRow = gRowCount + countDelta;
+ foundCell = false;
+ for (i = 0; i <= gLastColIndex; i++) {
+ if (!GetCellData(firstDeleteRow, i)) {
+ // We failed to find a cell.
+ break;
+ }
+
+ if (gCellData.startRowIndex == firstDeleteRow) {
+ foundCell = true;
+ break;
+ }
+ }
+ if (foundCell) {
+ try {
+ // Move selection to the cell we found
+ gSelection.collapse(gCellData.value, 0);
+ gActiveEditor.deleteTableRow(-countDelta);
+ gRowCount = gNewRowCount;
+ gLastRowIndex = gRowCount - 1;
+ if (gCurRowIndex > gLastRowIndex) {
+ // We are deleting our selection
+ // move it to start of table
+ ChangeSelectionToFirstCell();
+ } else {
+ // Put selection back where it was.
+ ChangeSelection(RESET_SELECTION);
+ }
+ } catch (ex) {
+ dump("FAILED TO FIND FIRST CELL IN LAST ROW\n");
+ }
+ }
+ }
+ }
+
+ if (gNewColCount != gColCount) {
+ countDelta = gNewColCount - gColCount;
+
+ if (gNewColCount > gColCount) {
+ // Append new columns
+ // Find last cell in first column
+ if (GetCellData(0, gLastColIndex)) {
+ try {
+ // Move selection to the last cell
+ gSelection.collapse(gCellData.value, 0);
+ gActiveEditor.insertTableColumn(countDelta, true);
+ gColCount = gNewColCount;
+ gLastColIndex = gColCount - 1;
+ // Restore selection
+ ChangeSelection(RESET_SELECTION);
+ } catch (ex) {
+ dump("FAILED TO FIND FIRST CELL IN LAST COLUMN\n");
+ }
+ }
+ } else if (gCanDelete) {
+ // Delete columns
+ var firstDeleteCol = gColCount + countDelta;
+ foundCell = false;
+ for (i = 0; i <= gLastRowIndex; i++) {
+ // Find first cell starting in first column we delete
+ if (!GetCellData(i, firstDeleteCol)) {
+ // We failed to find a cell.
+ break;
+ }
+
+ if (gCellData.startColIndex == firstDeleteCol) {
+ foundCell = true;
+ break;
+ }
+ }
+ if (foundCell) {
+ try {
+ // Move selection to the cell we found
+ gSelection.collapse(gCellData.value, 0);
+ gActiveEditor.deleteTableColumn(-countDelta);
+ gColCount = gNewColCount;
+ gLastColIndex = gColCount - 1;
+ if (gCurColIndex > gLastColIndex) {
+ ChangeSelectionToFirstCell();
+ } else {
+ ChangeSelection(RESET_SELECTION);
+ }
+ } catch (ex) {
+ dump("FAILED TO FIND FIRST CELL IN LAST ROW\n");
+ }
+ }
+ }
+ }
+
+ // Clone all remaining attributes to pick up
+ // anything changed by Advanced Edit Dialog
+ try {
+ gActiveEditor.cloneAttributes(gTableElement, globalTableElement);
+ } catch (e) {}
+}
+/* eslint-enable complexity */
+
+function ApplyCellAttributes() {
+ var rangeObj = { value: null };
+ var selectedCell;
+ try {
+ selectedCell = gActiveEditor.getFirstSelectedCell(rangeObj);
+ } catch (e) {}
+
+ if (!selectedCell) {
+ return;
+ }
+
+ if (gSelectedCellCount == 1) {
+ // When only one cell is selected, simply clone entire element,
+ // thus CSS and JS from Advanced edit is copied
+ try {
+ gActiveEditor.cloneAttributes(selectedCell, globalCellElement);
+ } catch (e) {}
+
+ if (gDialog.CellStyleCheckbox.checked) {
+ var currentStyleIndex =
+ selectedCell.nodeName.toLowerCase() == "th" ? 1 : 0;
+ if (gDialog.CellStyleList.selectedIndex != currentStyleIndex) {
+ // Switch cell types
+ // (replaces with new cell and copies attributes and contents)
+ try {
+ selectedCell = gActiveEditor.switchTableCellHeaderType(selectedCell);
+ } catch (e) {}
+ }
+ }
+ } else {
+ // Apply changes to all selected cells
+ // XXX THIS DOESN'T COPY ADVANCED EDIT CHANGES!
+ try {
+ while (selectedCell) {
+ ApplyAttributesToOneCell(selectedCell);
+ selectedCell = gActiveEditor.getNextSelectedCell(rangeObj);
+ }
+ } catch (e) {}
+ }
+ gCellDataChanged = false;
+}
+
+function ApplyAttributesToOneCell(destElement) {
+ if (gDialog.CellHeightCheckbox.checked) {
+ CloneAttribute(destElement, globalCellElement, "height");
+ }
+
+ if (gDialog.CellWidthCheckbox.checked) {
+ CloneAttribute(destElement, globalCellElement, "width");
+ }
+
+ if (gDialog.CellHAlignCheckbox.checked) {
+ CloneAttribute(destElement, globalCellElement, "align");
+ CloneAttribute(destElement, globalCellElement, charStr);
+ }
+
+ if (gDialog.CellVAlignCheckbox.checked) {
+ CloneAttribute(destElement, globalCellElement, "valign");
+ }
+
+ if (gDialog.TextWrapCheckbox.checked) {
+ CloneAttribute(destElement, globalCellElement, "nowrap");
+ }
+
+ if (gDialog.CellStyleCheckbox.checked) {
+ var newStyleIndex = gDialog.CellStyleList.selectedIndex;
+ var currentStyleIndex = destElement.nodeName.toLowerCase() == "th" ? 1 : 0;
+
+ if (newStyleIndex != currentStyleIndex) {
+ // Switch cell types
+ // (replaces with new cell and copies attributes and contents)
+ try {
+ destElement = gActiveEditor.switchTableCellHeaderType(destElement);
+ } catch (e) {}
+ }
+ }
+
+ if (gDialog.CellColorCheckbox.checked) {
+ CloneAttribute(destElement, globalCellElement, "bgcolor");
+ }
+}
+
+function SetCloseButton() {
+ // Change text on "Cancel" button after Apply is used
+ if (!gApplyUsed) {
+ document.documentElement.setAttribute(
+ "buttonlabelcancel",
+ document.documentElement.getAttribute("buttonlabelclose")
+ );
+ gApplyUsed = true;
+ }
+}
+
+function Apply() {
+ if (ValidateData()) {
+ gActiveEditor.beginTransaction();
+
+ ApplyTableAttributes();
+
+ // We may have just a table, so check for cell element
+ if (globalCellElement) {
+ ApplyCellAttributes();
+ }
+
+ gActiveEditor.endTransaction();
+
+ SetCloseButton();
+ return true;
+ }
+ return false;
+}
+
+function onAccept(event) {
+ // Do same as Apply and close window if ValidateData succeeded
+ var retVal = Apply();
+ if (retVal) {
+ SaveWindowLocation();
+ } else {
+ event.preventDefault();
+ }
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdTableProps.xhtml b/comm/suite/editor/components/dialogs/content/EdTableProps.xhtml
new file mode 100644
index 0000000000..30979acb4d
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdTableProps.xhtml
@@ -0,0 +1,287 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edTableProperties SYSTEM "chrome://editor/locale/EditorTableProperties.dtd">
+%edTableProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&tableWindow.title;"
+ id="tableDlg"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()"
+ buttons="accept,extra1,cancel"
+ buttonlabelclose="&closeButton.label;"
+ buttonlabelextra1="&applyButton.label;"
+ buttonaccesskeyextra1="&applyButton.accesskey;">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdTableProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <tabbox id="TabBox">
+ <tabs flex="1">
+ <tab id="TableTab" label="&tableTab.label;"/>
+ <tab id="CellTab" label="&cellTab.label;"/>
+ </tabs>
+ <tabpanels>
+
+ <!-- TABLE PANEL -->
+ <vbox>
+ <groupbox orient="horizontal">
+ <hbox class="groupbox-title">
+ <label class="header">&size.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column/><column/><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label value="&tableRows.label;" accesskey="&tableRows.accessKey;" control="TableRowsInput"/>
+ <textbox class="narrow" id="TableRowsInput" oninput="forceInteger(this.id);"/>
+ <spring class="bigspacer"/>
+ <label value="&tableHeight.label;" accesskey="&tableHeight.accessKey;"
+ id="TableHeightLabel" control="TableHeightInput"/>
+ <textbox class="narrow" id="TableHeightInput" oninput="forceInteger(this.id);"/>
+ <menulist id="TableHeightUnits"/>
+ </row>
+ <row align="center">
+ <label value="&tableColumns.label;" accesskey="&tableColumns.accessKey;" control="TableColumnsInput"/>
+ <textbox class="narrow" id="TableColumnsInput" oninput="forceInteger(this.id);"/>
+ <spring class="bigspacer"/>
+ <label value="&tableWidth.label;" accesskey="&tableWidth.accessKey;" control="TableWidthInput"/>
+ <textbox class="narrow" id="TableWidthInput" oninput="forceInteger(this.id);"/>
+ <menulist id="TableWidthUnits"/>
+ </row>
+ </rows>
+ <!-- KEEP GRID LAYOUT here since we will be adding back support for table HEIGHT via CSS -->
+ </grid>
+ </groupbox>
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&tableBorderSpacing.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="BorderWidthInput"
+ value="&tableBorderWidth.label;"
+ accesskey="&tableBorderWidth.accessKey;"/>
+ <textbox class="narrow" id="BorderWidthInput" oninput="forceInteger(this.id);"/>
+ <label align="left" value="&pixels.label;"/>
+ </row>
+ <row align="center">
+ <label control="SpacingInput"
+ value="&tableSpacing.label;"
+ accesskey="&tableSpacing.accessKey;"/>
+ <textbox class="narrow" id="SpacingInput" oninput="forceInteger(this.id);"/>
+ <label value="&tablePxBetwCells.label;"/>
+ </row>
+ <row align="center">
+ <label control="PaddingInput"
+ value="&tablePadding.label;"
+ accesskey="&tablePadding.accessKey;"/>
+ <textbox class="narrow" id="PaddingInput" oninput="forceInteger(this.id);"/>
+ <label value="&tablePxBetwBrdrCellContent.label;"/>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ <!-- Table Alignment and Caption -->
+ <hbox flex="1" align="center">
+ <label control="TableAlignList"
+ value="&tableAlignment.label;"
+ accesskey="&tableAlignment.accessKey;"/>
+ <menulist id="TableAlignList">
+ <menupopup>
+ <menuitem label="&AlignLeft.label;" value="left"/>
+ <menuitem label="&AlignCenter.label;" value="center"/>
+ <menuitem label="&AlignRight.label;" value="right"/>
+ </menupopup>
+ </menulist>
+ <spacer class="spacer"/>
+ <label control="TableCaptionList"
+ value="&tableCaption.label;"
+ accesskey="&tableCaption.accessKey;"/>
+ <menulist id="TableCaptionList">
+ <menupopup>
+ <menuitem label="&tableCaptionNone.label;" value=""/>
+ <menuitem label="&tableCaptionAbove.label;" value="top"/>
+ <menuitem label="&tableCaptionBelow.label;" value="bottom"/>
+ <menuitem label="&tableCaptionLeft.label;" value="left"/>
+ <menuitem label="&tableCaptionRight.label;" value="right"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <separator class="groove"/>
+ <hbox align="center">
+ <label value="&backgroundColor.label;"/>
+ <button id="tableBackground" class="color-button" oncommand="GetColorAndUpdate('tableBackgroundCW');">
+ <spacer id="tableBackgroundCW" class="color-well"/>
+ </button>
+ <spacer class="spacer"/>
+ <label id="TableInheritColor" value="&tableInheritColor.label;" collapsed="true"/>
+ </hbox>
+ <separator class="groove"/>
+ <hbox flex="1" align="center">
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton"
+ oncommand="onAdvancedEdit();"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <spacer flex="1"/>
+ </vbox><!-- Table Panel -->
+
+ <!-- CELL PANEL -->
+ <vbox>
+ <groupbox orient="horizontal" align="center">
+ <hbox class="groupbox-title">
+ <label class="header">&cellSelection.label;</label>
+ </hbox>
+ <vbox>
+ <menulist id="SelectionList" oncommand="ChangeSelection(event.target.value)" flex="1">
+ <menupopup>
+ <!-- JS code assumes order is Cell, Row, Column -->
+ <menuitem label="&cellSelectCell.label;" value="1"/>
+ <menuitem label="&cellSelectRow.label;" value="2"/>
+ <menuitem label="&cellSelectColumn.label;" value="3"/>
+ </menupopup>
+ </menulist>
+ <hbox flex="1">
+ <button id="PreviousButton"
+ oncommand="MoveSelection(0)"
+ flex="1"
+ align="center">
+ <image/>
+ <label value="&cellSelectPrevious.label;"
+ accesskey="&cellSelectPrevious.accessKey;"
+ control="PreviousButton"/>
+ </button>
+ <button id="NextButton"
+ oncommand="MoveSelection(1)"
+ class="align-right"
+ flex="1"
+ align="center">
+ <image/>
+ <label value="&cellSelectNext.label;"
+ accesskey="&cellSelectNext.accessKey;"
+ control="NextButton"/>
+ </button>
+ </hbox>
+ </vbox>
+ <spacer class="bigspacer"/>
+ <description class="wrap" flex="1">&applyBeforeChange.label;</description>
+ </groupbox>
+ <hbox align="center">
+ <!-- cell size groupbox -->
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&size.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column/><column flex="1"/></columns>
+ <rows>
+ <row align="center">
+ <checkbox id="CellHeightCheckbox" label="&tableHeight.label;" accesskey="&tableHeight.accessKey;"/>
+ <textbox class="narrow" id="CellHeightInput"
+ oninput="ChangeIntTextbox(this.id, 'CellHeightCheckbox');"/>
+ <menulist id="CellHeightUnits" oncommand="SetCheckbox('CellHeightCheckbox');"/>
+ </row>
+ <row align="center">
+ <checkbox id="CellWidthCheckbox" label="&tableWidth.label;" accesskey="&tableWidth.accessKey;"/>
+ <textbox class="narrow" id="CellWidthInput"
+ oninput="ChangeIntTextbox(this.id, 'CellWidthCheckbox');"/>
+ <menulist id="CellWidthUnits" oncommand="SetCheckbox('CellWidthCheckbox');"/>
+ </row>
+ </rows>
+ </grid>
+ <spacer class="bigspacer"/>
+ </groupbox>
+ <!-- Alignment -->
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&cellContentAlignment.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column flex="1"/><column/></columns>
+ <rows>
+ <row align="center">
+ <checkbox id="CellVAlignCheckbox" label="&cellVertical.label;" accesskey="&cellVertical.accessKey;"/>
+ <menulist id="CellVAlignList" oncommand="SetCheckbox('CellVAlignCheckbox');">
+ <menupopup>
+ <menuitem label="&cellAlignTop.label;" value="top"/>
+ <menuitem label="&cellAlignMiddle.label;" value="middle"/>
+ <menuitem label="&cellAlignBottom.label;" value="bottom"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <checkbox id="CellHAlignCheckbox" label="&cellHorizontal.label;" accesskey="&cellHorizontal.accessKey;"/>
+ <menulist id="CellHAlignList" oncommand="SelectCellHAlign()">
+ <menupopup>
+ <menuitem label="&AlignLeft.label;" value="left"/>
+ <menuitem label="&AlignCenter.label;" value="center"/>
+ <menuitem label="&AlignRight.label;" value="right"/>
+ <menuitem label="&cellAlignJustify.label;" value="justify"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+ </hbox>
+ <spacer class="spacer"/>
+ <hbox align="center">
+ <checkbox id="CellStyleCheckbox" label="&cellStyle.label;" accesskey="&cellStyle.accessKey;"/>
+ <menulist id="CellStyleList" oncommand="SetCheckbox('CellStyleCheckbox');">
+ <menupopup>
+ <menuitem label="&cellNormal.label;" value="td"/>
+ <menuitem label="&cellHeader.label;" value="th"/>
+ </menupopup>
+ </menulist>
+ <spacer class="bigspacer"/>
+ <checkbox id="TextWrapCheckbox" label="&cellTextWrap.label;" accesskey="&cellTextWrap.accessKey;"/>
+ <menulist id="TextWrapList" oncommand="SetCheckbox('TextWrapCheckbox');">
+ <menupopup>
+ <menuitem label="&cellWrap.label;" value="wrap"/>
+ <menuitem label="&cellNoWrap.label;" value="nowrap"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <separator class="groove"/>
+ <hbox align="center">
+ <checkbox id="CellColorCheckbox" label="&backgroundColor.label;" accesskey="&backgroundColor.accessKey;"/>
+ <button class="color-button" oncommand="GetColorAndUpdate('cellBackgroundCW');">
+ <spacer id="cellBackgroundCW" class="color-well"/>
+ </button>
+ <spacer class="spacer"/>
+ <label id="CellInheritColor" value="&cellInheritColor.label;" collapsed="true"/>
+ </hbox>
+ <separator class="groove"/>
+ <hbox align="center">
+ <description class="wrap" flex="1" style="width: 1em">&cellUseCheckboxHelp.label;</description>
+ <button id="AdvancedEditButton2"
+ oncommand="onAdvancedEdit()"
+ label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;"
+ tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <spacer flex="1"/>
+ </vbox><!-- Cell Panel -->
+ </tabpanels>
+ </tabbox>
+ <spacer class="spacer"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EdTextAreaProps.js b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.js
new file mode 100644
index 0000000000..da33ab60c9
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.js
@@ -0,0 +1,171 @@
+/* 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/. */
+
+/* import-globals-from ../../composer/content/editorUtilities.js */
+/* import-globals-from EdDialogCommon.js */
+
+var insertNew;
+var textareaElement;
+
+// dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup() {
+ var editor = GetCurrentEditor();
+ if (!editor) {
+ dump("Failed to get active editor!\n");
+ window.close();
+ return;
+ }
+
+ gDialog = {
+ accept: document.documentElement.getButton("accept"),
+ textareaName: document.getElementById("TextAreaName"),
+ textareaRows: document.getElementById("TextAreaRows"),
+ textareaCols: document.getElementById("TextAreaCols"),
+ textareaWrap: document.getElementById("TextAreaWrap"),
+ textareaReadOnly: document.getElementById("TextAreaReadOnly"),
+ textareaDisabled: document.getElementById("TextAreaDisabled"),
+ textareaTabIndex: document.getElementById("TextAreaTabIndex"),
+ textareaAccessKey: document.getElementById("TextAreaAccessKey"),
+ textareaValue: document.getElementById("TextAreaValue"),
+ MoreSection: document.getElementById("MoreSection"),
+ MoreFewerButton: document.getElementById("MoreFewerButton"),
+ };
+
+ // Get a single selected text area element
+ const kTagName = "textarea";
+ try {
+ textareaElement = editor.getSelectedElement(kTagName);
+ } catch (e) {}
+
+ if (textareaElement) {
+ // We found an element and don't need to insert one
+ insertNew = false;
+
+ gDialog.textareaValue.value = textareaElement.value;
+ } else {
+ insertNew = true;
+
+ // We don't have an element selected,
+ // so create one with default attributes
+ try {
+ textareaElement = editor.createElementWithDefaults(kTagName);
+ } catch (e) {}
+
+ if (!textareaElement) {
+ dump("Failed to get selected element or create a new one!\n");
+ window.close();
+ return;
+ }
+ gDialog.textareaValue.value = GetSelectionAsText();
+ }
+
+ // Make a copy to use for AdvancedEdit
+ globalElement = textareaElement.cloneNode(false);
+
+ InitDialog();
+
+ InitMoreFewer();
+
+ SetTextboxFocus(gDialog.textareaName);
+
+ SetWindowLocation();
+}
+
+function InitDialog() {
+ gDialog.textareaName.value = globalElement.getAttribute("name");
+ gDialog.textareaRows.value = globalElement.getAttribute("rows");
+ gDialog.textareaCols.value = globalElement.getAttribute("cols");
+ gDialog.textareaWrap.value = GetHTMLOrCSSStyleValue(
+ globalElement,
+ "wrap",
+ "white-space"
+ );
+ gDialog.textareaReadOnly.checked = globalElement.hasAttribute("readonly");
+ gDialog.textareaDisabled.checked = globalElement.hasAttribute("disabled");
+ gDialog.textareaTabIndex.value = globalElement.getAttribute("tabindex");
+ gDialog.textareaAccessKey.value = globalElement.getAttribute("accesskey");
+ onInput();
+}
+
+function onInput() {
+ var disabled =
+ !gDialog.textareaName.value ||
+ !gDialog.textareaRows.value ||
+ !gDialog.textareaCols.value;
+ if (gDialog.accept.disabled != disabled) {
+ gDialog.accept.disabled = disabled;
+ }
+}
+
+function ValidateData() {
+ var attributes = {
+ name: gDialog.textareaName.value,
+ rows: gDialog.textareaRows.value,
+ cols: gDialog.textareaCols.value,
+ wrap: gDialog.textareaWrap.value,
+ tabindex: gDialog.textareaTabIndex.value,
+ accesskey: gDialog.textareaAccessKey.value,
+ };
+ var flags = {
+ readonly: gDialog.textareaReadOnly.checked,
+ disabled: gDialog.textareaDisabled.checked,
+ };
+ for (var a in attributes) {
+ if (attributes[a]) {
+ globalElement.setAttribute(a, attributes[a]);
+ } else {
+ globalElement.removeAttribute(a);
+ }
+ }
+ for (var f in flags) {
+ if (flags[f]) {
+ globalElement.setAttribute(f, "");
+ } else {
+ globalElement.removeAttribute(f);
+ }
+ }
+ return true;
+}
+
+function onAccept() {
+ // All values are valid - copy to actual element in doc or
+ // element created to insert
+ ValidateData();
+
+ var editor = GetCurrentEditor();
+
+ editor.beginTransaction();
+
+ try {
+ editor.cloneAttributes(textareaElement, globalElement);
+
+ if (insertNew) {
+ editor.insertElementAtSelection(textareaElement, true);
+ }
+
+ // undoably set value
+ var initialText = gDialog.textareaValue.value;
+ if (initialText != textareaElement.value) {
+ editor.setShouldTxnSetSelection(false);
+
+ while (textareaElement.hasChildNodes()) {
+ editor.deleteNode(textareaElement.lastChild);
+ }
+ if (initialText) {
+ var textNode = editor.document.createTextNode(initialText);
+ editor.insertNode(textNode, textareaElement, 0);
+ }
+
+ editor.setShouldTxnSetSelection(true);
+ }
+ } finally {
+ editor.endTransaction();
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml
new file mode 100644
index 0000000000..9ab91664e8
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EdTextAreaProps.xhtml
@@ -0,0 +1,115 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % edTextAreaProperties SYSTEM "chrome://editor/locale/EditorTextAreaProperties.dtd">
+%edTextAreaProperties;
+<!ENTITY % edDialogOverlay SYSTEM "chrome://editor/locale/EdDialogOverlay.dtd">
+%edDialogOverlay;
+]>
+
+<dialog title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup();"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EdTextAreaProps.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&Settings.label;</label>
+ </hbox>
+ <grid><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="TextAreaName" value="&TextAreaName.label;" accesskey="&TextAreaName.accessKey;"/>
+ <textbox id="TextAreaName" oninput="onInput();"/>
+ </row>
+ <row align="center">
+ <label control="TextAreaRows" value="&TextAreaRows.label;" accesskey="&TextAreaRows.accessKey;"/>
+ <hbox>
+ <textbox id="TextAreaRows" class="narrow" oninput="forceInteger(this.id); onInput();"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="TextAreaCols" value="&TextAreaCols.label;" accesskey="&TextAreaCols.accessKey;"/>
+ <hbox>
+ <textbox id="TextAreaCols" class="narrow" oninput="forceInteger(this.id); onInput();"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ <hbox>
+ <button id="MoreFewerButton" oncommand="onMoreFewer();" persist="more"/>
+ </hbox>
+ <grid id="MoreSection"><columns><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label control="TextAreaWrap" value="&TextAreaWrap.label;" accesskey="&TextAreaWrap.accessKey;"/>
+ <menulist id="TextAreaWrap">
+ <menupopup>
+ <menuitem label="&WrapDefault.value;"/>
+ <menuitem label="&WrapOff.value;" value="off"/>
+ <menuseparator/>
+ <menuitem label="&WrapSoft.value;" value="soft"/>
+ <menuitem label="&WrapHard.value;" value="hard"/>
+ <menuseparator/>
+ <menuitem label="&WrapPhysical.value;" value="physical"/>
+ <menuitem label="&WrapVirtual.value;" value="virtual"/>
+ <menuseparator/>
+ <menuitem label="normal" value="normal"/>
+ <menuitem label="nowrap" value="nowrap"/>
+ <menuitem label="pre" value="pre"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="TextAreaReadOnly" label="&TextAreaReadOnly.label;" accesskey="&TextAreaReadOnly.accessKey;"/>
+ </row>
+ <row>
+ <spacer/>
+ <checkbox id="TextAreaDisabled" label="&TextAreaDisabled.label;" accesskey="&TextAreaDisabled.accessKey;"/>
+ </row>
+ <row align="center">
+ <label control="TextAreaTabIndex" value="&TextAreaTabIndex.label;" accesskey="&TextAreaTabIndex.accessKey;"/>
+ <hbox>
+ <textbox id="TextAreaTabIndex" class="narrow" oninput="forceInteger(this.id);"/>
+ </hbox>
+ </row>
+ <row align="center">
+ <label control="TextAreaAccessKey" value="&TextAreaAccessKey.label;" accesskey="&TextAreaAccessKey.accessKey;"/>
+ <hbox>
+ <textbox id="TextAreaAccessKey" class="narrow" maxlength="1"/>
+ </hbox>
+ </row>
+ <row>
+ <label control="TextAreaValue" value="&InitialText.label;" accesskey="&InitialText.accessKey;"/>
+ </row>
+ <html:textarea id="TextAreaValue" flex="1" rows="5"/>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <vbox id="AdvancedEdit">
+ <hbox flex="1" style="margin-top: 0.2em" align="center">
+ <!-- This will right-align the button -->
+ <spacer flex="1"/>
+ <button id="AdvancedEditButton1" oncommand="onAdvancedEdit()" label="&AdvancedEditButton.label;"
+ accesskey="&AdvancedEditButton.accessKey;" tooltiptext="&AdvancedEditButton.tooltip;"/>
+ </hbox>
+ <separator id="advancedSeparator" class="groove"/>
+ </vbox>
+
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EditConflict.js b/comm/suite/editor/components/dialogs/content/EditConflict.js
new file mode 100644
index 0000000000..28611796cd
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditConflict.js
@@ -0,0 +1,42 @@
+/* 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/. */
+
+// dialog initialization code
+
+document.addEventListener("dialogcancel", onClose);
+
+function Startup()
+{
+ if (!GetCurrentEditor())
+ {
+ window.close();
+ return;
+ }
+
+ SetWindowLocation();
+}
+
+function KeepCurrentPage()
+{
+ // Simply close dialog and don't change current page
+ //TODO: Should we force saving of the current page?
+ SaveWindowLocation();
+ return true;
+}
+
+function UseOtherPage()
+{
+ // Reload the URL -- that will get other editor's contents
+ window.opener.setTimeout(window.opener.EditorLoadUrl, 0, GetDocumentUrl());
+ SaveWindowLocation();
+ return true;
+}
+
+function PreventCancel()
+{
+ SaveWindowLocation();
+
+ // Don't let Esc key close the dialog!
+ return false;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EditConflict.xhtml b/comm/suite/editor/components/dialogs/content/EditConflict.xhtml
new file mode 100644
index 0000000000..fce32792d2
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditConflict.xhtml
@@ -0,0 +1,40 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditConflict.dtd">
+
+<dialog buttons="cancel" title="&windowTitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EditConflict.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <label value ="&conflictWarning.label;"/>
+ <spacer class="bigspacer"/>
+ <label value ="&conflictResolve.label;"/>
+ <spacer class="bigspacer"/>
+ <hbox flex="1">
+ <spacer class="bigspacer"/>
+ <button label="&keepCurrentPageButton.label;"
+ oncommand="KeepCurrentPage()"/>
+ <spacer class="bigspacer"/>
+ </hbox>
+ <hbox flex="1">
+ <spacer class="bigspacer"/>
+ <button dlgtype="cancel"
+ label="&useOtherPageButton.label;"
+ oncommand="UseOtherPage()"/>
+ <spacer class="bigspacer"/>
+ </hbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublish.js b/comm/suite/editor/components/dialogs/content/EditorPublish.js
new file mode 100644
index 0000000000..6947a439ee
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublish.js
@@ -0,0 +1,558 @@
+/* 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 gPublishSiteData;
+var gReturnData;
+var gDefaultSiteIndex = -1;
+var gDefaultSiteName;
+var gPreviousDefaultDir;
+var gPreviousTitle;
+var gSettingsChanged = false;
+var gInitialSiteName;
+var gInitialSiteIndex = -1;
+var gPasswordManagerOn = true;
+
+// Dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup()
+{
+ window.opener.ok = false;
+
+ // Element to edit is passed in
+ gInitialSiteName = window.arguments[1];
+ gReturnData = window.arguments[2];
+ if (!gReturnData || !GetCurrentEditor())
+ {
+ dump("Publish: No editor or return data object not supplied\n");
+ window.close();
+ return;
+ }
+
+ gDialog.TabBox = document.getElementById("TabBox");
+ gDialog.PublishTab = document.getElementById("PublishTab");
+ gDialog.SettingsTab = document.getElementById("SettingsTab");
+
+ // Publish panel
+ gDialog.PageTitleInput = document.getElementById("PageTitleInput");
+ gDialog.FilenameInput = document.getElementById("FilenameInput");
+ gDialog.SiteList = document.getElementById("SiteList");
+ gDialog.DocDirList = document.getElementById("DocDirList");
+ gDialog.OtherDirCheckbox = document.getElementById("OtherDirCheckbox");
+ gDialog.OtherDirRadiogroup = document.getElementById("OtherDirRadiogroup");
+ gDialog.SameLocationRadio = document.getElementById("SameLocationRadio");
+ gDialog.UseSubdirRadio = document.getElementById("UseSubdirRadio");
+ gDialog.OtherDirList = document.getElementById("OtherDirList");
+
+ // Settings Panel
+ gDialog.SiteNameInput = document.getElementById("SiteNameInput");
+ gDialog.PublishUrlInput = document.getElementById("PublishUrlInput");
+ gDialog.BrowseUrlInput = document.getElementById("BrowseUrlInput");
+ gDialog.UsernameInput = document.getElementById("UsernameInput");
+ gDialog.PasswordInput = document.getElementById("PasswordInput");
+ gDialog.SavePassword = document.getElementById("SavePassword");
+
+ gPasswordManagerOn = Services.prefs.getBoolPref("signon.rememberSignons");
+ gDialog.SavePassword.disabled = !gPasswordManagerOn;
+
+ gPublishSiteData = GetPublishSiteData();
+ gDefaultSiteName = GetDefaultPublishSiteName();
+
+ var addNewSite = false;
+ if (gPublishSiteData)
+ {
+ FillSiteList();
+ }
+ else
+ {
+ // No current site data, start a new item in the Settings panel
+ AddNewSite();
+ addNewSite = true;
+ }
+
+ var docUrl = GetDocumentUrl();
+ var scheme = GetScheme(docUrl);
+ var filename = "";
+
+ if (scheme)
+ {
+ filename = GetFilename(docUrl);
+
+ if (scheme != "file")
+ {
+ var siteFound = false;
+
+ // Editing a remote URL.
+ // Attempt to find doc URL in Site Data
+ if (gPublishSiteData)
+ {
+ var dirObj = {};
+ var siteIndex = FindSiteIndexAndDocDir(gPublishSiteData, docUrl, dirObj);
+
+ // Select this site only if the same as user's intended site, or there wasn't one
+ if (siteIndex != -1 && (gInitialSiteIndex == -1 || siteIndex == gInitialSiteIndex))
+ {
+ siteFound = true;
+
+ // Select the site we found
+ gDialog.SiteList.selectedIndex = siteIndex;
+ var docDir = dirObj.value;
+
+ // Use the directory within site in the editable menulist
+ gPublishSiteData[siteIndex].docDir = docDir;
+
+ //XXX HOW DO WE DECIDE WHAT "OTHER" DIR TO USE?
+ //gPublishSiteData[siteIndex].otherDir = docDir;
+ }
+ }
+ if (!siteFound)
+ {
+ // Not found in site database
+ // Setup for a new site and use data from a remote URL
+ if (!addNewSite)
+ AddNewSite();
+
+ addNewSite = true;
+
+ var publishData = CreatePublishDataFromUrl(docUrl);
+ if (publishData)
+ {
+ filename = publishData.filename;
+ gDialog.SiteNameInput.value = publishData.siteName;
+ gDialog.PublishUrlInput.value = publishData.publishUrl;
+ gDialog.BrowseUrlInput.value = publishData.browseUrl;
+ gDialog.UsernameInput.value = publishData.username;
+ gDialog.PasswordInput.value = publishData.password;
+ gDialog.SavePassword.checked = false;
+ }
+ }
+ }
+ }
+ try {
+ gPreviousTitle = GetDocumentTitle();
+ } catch (e) {}
+
+ gDialog.PageTitleInput.value = gPreviousTitle;
+ gDialog.FilenameInput.value = decodeURIComponent(filename);
+
+ if (!addNewSite)
+ {
+ // If not adding a site and we haven't selected a site -- use initial or default site
+ if (gDialog.SiteList.selectedIndex == -1)
+ gDialog.SiteList.selectedIndex = (gInitialSiteIndex != -1) ? gInitialSiteIndex : gDefaultSiteIndex;
+
+ // Fill in all the site data for currently-selected site
+ SelectSiteList();
+ SetTextboxFocus(gDialog.PageTitleInput);
+ }
+
+ if (gDialog.SiteList.selectedIndex == -1)
+ {
+ // No selected site -- assume same directory
+ gDialog.OtherDirRadiogroup.selectedItem = gDialog.SameLocationRadio;
+ }
+ else if (gPublishSiteData[gDialog.SiteList.selectedIndex].docDir ==
+ gPublishSiteData[gDialog.SiteList.selectedIndex].otherDir)
+ {
+ // For now, check "same location" if dirs are already set to same directory
+ gDialog.OtherDirRadiogroup.selectedItem = gDialog.SameLocationRadio;
+ }
+ else
+ {
+ gDialog.OtherDirRadiogroup.selectedItem = gDialog.UseSubdirRadio;
+ }
+
+ doEnabling();
+
+ SetWindowLocation();
+}
+
+function FillSiteList()
+{
+ gDialog.SiteList.removeAllItems();
+ gDefaultSiteIndex = -1;
+
+ // Fill the site lists
+ var count = gPublishSiteData.length;
+ var i;
+
+ for (i = 0; i < count; i++)
+ {
+ var name = gPublishSiteData[i].siteName;
+ var menuitem = gDialog.SiteList.appendItem(name);
+ // Highlight the default site
+ if (name == gDefaultSiteName)
+ {
+ gDefaultSiteIndex = i;
+ if (menuitem)
+ {
+ menuitem.setAttribute("class", "menuitem-highlight-1");
+ menuitem.setAttribute("default", "true");
+ }
+ }
+ // Find initial site location
+ if (name == gInitialSiteName)
+ gInitialSiteIndex = i;
+ }
+}
+
+function doEnabling()
+{
+ var disableOther = !gDialog.OtherDirCheckbox.checked;
+ gDialog.SameLocationRadio.disabled = disableOther;
+ gDialog.UseSubdirRadio.disabled = disableOther;
+ gDialog.OtherDirList.disabled = (disableOther || gDialog.SameLocationRadio.selected);
+}
+
+function SelectSiteList()
+{
+ var selectedSiteIndex = gDialog.SiteList.selectedIndex;
+
+ var siteName = "";
+ var publishUrl = "";
+ var browseUrl = "";
+ var username = "";
+ var password = "";
+ var savePassword = false;
+ var publishOtherFiles = true;
+
+ gDialog.DocDirList.removeAllItems();
+ gDialog.OtherDirList.removeAllItems();
+
+ if (gPublishSiteData && selectedSiteIndex != -1)
+ {
+ siteName = gPublishSiteData[selectedSiteIndex].siteName;
+ publishUrl = gPublishSiteData[selectedSiteIndex].publishUrl;
+ browseUrl = gPublishSiteData[selectedSiteIndex].browseUrl;
+ username = gPublishSiteData[selectedSiteIndex].username;
+ savePassword = gPasswordManagerOn ? gPublishSiteData[selectedSiteIndex].savePassword : false;
+ if (savePassword)
+ password = gPublishSiteData[selectedSiteIndex].password;
+
+ // Fill the directory menulists
+ if (gPublishSiteData[selectedSiteIndex].dirList.length)
+ {
+ for (var i = 0; i < gPublishSiteData[selectedSiteIndex].dirList.length; i++)
+ {
+ gDialog.DocDirList.appendItem(gPublishSiteData[selectedSiteIndex].dirList[i]);
+ gDialog.OtherDirList.appendItem(gPublishSiteData[selectedSiteIndex].dirList[i]);
+ }
+ }
+ gDialog.DocDirList.value = FormatDirForPublishing(gPublishSiteData[selectedSiteIndex].docDir);
+ gDialog.OtherDirList.value = FormatDirForPublishing(gPublishSiteData[selectedSiteIndex].otherDir);
+ publishOtherFiles = gPublishSiteData[selectedSiteIndex].publishOtherFiles;
+
+ }
+ else
+ {
+ gDialog.DocDirList.value = "";
+ gDialog.OtherDirList.value = "";
+ }
+
+ gDialog.SiteNameInput.value = siteName;
+ gDialog.PublishUrlInput.value = publishUrl;
+ gDialog.BrowseUrlInput.value = browseUrl;
+ gDialog.UsernameInput.value = username;
+ gDialog.PasswordInput.value = password;
+ gDialog.SavePassword.checked = savePassword;
+ gDialog.OtherDirCheckbox.checked = publishOtherFiles;
+
+ doEnabling();
+}
+
+function AddNewSite()
+{
+ // Button in Publish panel allows user
+ // to automatically switch to "Settings" panel
+ // to enter data for new site
+ SwitchPanel(gDialog.SettingsTab);
+
+ gDialog.SiteList.selectedIndex = -1;
+
+ SelectSiteList();
+
+ gSettingsChanged = true;
+
+ SetTextboxFocus(gDialog.SiteNameInput);
+}
+
+function SelectPublishTab()
+{
+ if (gSettingsChanged && !ValidateSettings())
+ return;
+
+ SwitchPanel(gDialog.PublishTab);
+ SetTextboxFocus(gDialog.PageTitleInput);
+}
+
+function SelectSettingsTab()
+{
+ SwitchPanel(gDialog.SettingsTab);
+ SetTextboxFocus(gDialog.SiteNameInput);
+}
+
+function SwitchPanel(tab)
+{
+ if (gDialog.TabBox.selectedTab != tab)
+ gDialog.TabBox.selectedTab = tab;
+}
+
+function onInputSettings()
+{
+ // TODO: Save current data during SelectSite and compare here
+ // to detect if real change has occurred?
+ gSettingsChanged = true;
+}
+
+function GetPublishUrlInput()
+{
+ gDialog.PublishUrlInput.value = FormatUrlForPublishing(gDialog.PublishUrlInput.value);
+ return gDialog.PublishUrlInput.value;
+}
+
+function GetBrowseUrlInput()
+{
+ gDialog.BrowseUrlInput.value = FormatUrlForPublishing(gDialog.BrowseUrlInput.value);
+ return gDialog.BrowseUrlInput.value;
+}
+
+function GetDocDirInput()
+{
+ gDialog.DocDirList.value = FormatDirForPublishing(gDialog.DocDirList.value);
+ return gDialog.DocDirList.value;
+}
+
+function GetOtherDirInput()
+{
+ gDialog.OtherDirList.value = FormatDirForPublishing(gDialog.OtherDirList.value);
+ return gDialog.OtherDirList.value;
+}
+
+function ChooseDir(menulist)
+{
+ //TODO: For FTP publish destinations, get file listing of just dirs
+ // and build a tree to let user select dir
+}
+
+function ValidateSettings()
+{
+ var siteName = TrimString(gDialog.SiteNameInput.value);
+ if (!siteName)
+ {
+ ShowErrorInPanel(gDialog.SettingsTab, "MissingSiteNameError", gDialog.SiteNameInput);
+ return false;
+ }
+ if (PublishSiteNameExists(siteName, gPublishSiteData, gDialog.SiteList.selectedIndex))
+ {
+ SwitchPanel(gDialog.SettingsTab);
+ ShowInputErrorMessage(GetString("DuplicateSiteNameError").replace(/%name%/, siteName));
+ SetTextboxFocus(gDialog.SiteNameInput);
+ return false;
+ }
+
+ // Extract username and password while removing them from publishingUrl
+ var urlUserObj = {};
+ var urlPassObj = {};
+ var publishUrl = StripUsernamePassword(gDialog.PublishUrlInput.value, urlUserObj, urlPassObj);
+ if (publishUrl)
+ {
+ publishUrl = FormatUrlForPublishing(publishUrl);
+
+ // Assume scheme = "ftp://" if missing
+ // This compensates when user enters hostname w/o scheme (as most ISPs provide)
+ if (!GetScheme(publishUrl))
+ publishUrl = "ftp://" + publishUrl;
+
+ gDialog.PublishUrlInput.value = publishUrl;
+ }
+ else
+ {
+ ShowErrorInPanel(gDialog.SettingsTab, "MissingPublishUrlError", gDialog.PublishUrlInput);
+ return false;
+ }
+ var browseUrl = GetBrowseUrlInput();
+
+ var username = TrimString(gDialog.UsernameInput.value);
+ var savePassword = gDialog.SavePassword.checked;
+ var password = gDialog.PasswordInput.value;
+ var publishOtherFiles = gDialog.OtherDirCheckbox.checked;
+
+ //XXX If there was a username and/or password in the publishUrl
+ // AND in the input field, which do we use?
+ // Let's use those in url only if input is empty
+ if (!username)
+ {
+ username = urlUserObj.value;
+ gDialog.UsernameInput.value = username;
+ gSettingsChanged = true;
+ }
+ if (!password)
+ {
+ password = urlPassObj.value;
+ gDialog.PasswordInput.value = password;
+ gSettingsChanged = true;
+ }
+
+ // Update or add data for a site
+ var siteIndex = gDialog.SiteList.selectedIndex;
+ var newSite = false;
+
+ if (siteIndex == -1)
+ {
+ // No site is selected, add a new site at the end
+ if (gPublishSiteData)
+ {
+ siteIndex = gPublishSiteData.length;
+ }
+ else
+ {
+ // First time: start entire site array
+ gPublishSiteData = new Array(1);
+ siteIndex = 0;
+ gDefaultSiteIndex = 0;
+ gDefaultSiteName = siteName;
+ }
+ gPublishSiteData[siteIndex] = {};
+ gPublishSiteData[siteIndex].docDir = "";
+ gPublishSiteData[siteIndex].otherDir = "";
+ gPublishSiteData[siteIndex].dirList = [""];
+ gPublishSiteData[siteIndex].publishOtherFiles = true;
+ gPublishSiteData[siteIndex].previousSiteName = siteName;
+ newSite = true;
+ }
+ gPublishSiteData[siteIndex].siteName = siteName;
+ gPublishSiteData[siteIndex].publishUrl = publishUrl;
+ gPublishSiteData[siteIndex].browseUrl = browseUrl;
+ gPublishSiteData[siteIndex].username = username;
+ // Don't save password in data that will be saved in prefs
+ gPublishSiteData[siteIndex].password = savePassword ? password : "";
+ gPublishSiteData[siteIndex].savePassword = savePassword;
+
+ if (publishOtherFiles != gPublishSiteData[siteIndex].publishOtherFiles)
+ gSettingsChanged = true;
+
+ gPublishSiteData[siteIndex].publishOtherFiles = publishOtherFiles;
+
+ gDialog.SiteList.selectedIndex = siteIndex;
+ if (siteIndex == gDefaultSiteIndex)
+ gDefaultSiteName = siteName;
+
+ // Should never be empty, but be sure we have a default site
+ if (!gDefaultSiteName)
+ {
+ gDefaultSiteName = gPublishSiteData[0].siteName;
+ gDefaultSiteIndex = 0;
+ }
+
+ // Rebuild the site menulist if we added a new site
+ if (newSite)
+ {
+ FillSiteList();
+ gDialog.SiteList.selectedIndex = siteIndex;
+ }
+ else
+ {
+ // Update selected item if sitename changed
+ var selectedItem = gDialog.SiteList.selectedItem;
+ if (selectedItem)
+ {
+ var oldName = selectedItem.getAttribute("label");
+ if (oldName != siteName)
+ {
+ selectedItem.setAttribute("label", siteName);
+ gDialog.SiteList.setAttribute("label", siteName);
+ gSettingsChanged = true;
+ if (oldName == gDefaultSiteName)
+ gDefaultSiteName = siteName;
+ }
+ }
+ }
+
+ // Get the directory name in site to publish to
+ var docDir = GetDocDirInput();
+
+ gPublishSiteData[siteIndex].docDir = docDir;
+
+ // And directory for images and other files
+ var otherDir = GetOtherDirInput();
+ if (gDialog.SameLocationRadio.selected)
+ otherDir = docDir;
+ else
+ otherDir = GetOtherDirInput();
+
+ gPublishSiteData[siteIndex].otherDir = otherDir;
+
+ // Fill return data object
+ gReturnData.siteName = siteName;
+ gReturnData.previousSiteName = gPublishSiteData[siteIndex].previousSiteName;
+ gReturnData.publishUrl = publishUrl;
+ gReturnData.browseUrl = browseUrl;
+ gReturnData.username = username;
+ // Note that we use the password for the next publish action
+ // even if savePassword is false; but we won't save it in PasswordManager database
+ gReturnData.password = password;
+ gReturnData.savePassword = savePassword;
+ gReturnData.docDir = gPublishSiteData[siteIndex].docDir;
+ gReturnData.otherDir = gPublishSiteData[siteIndex].otherDir;
+ gReturnData.publishOtherFiles = publishOtherFiles;
+ gReturnData.dirList = gPublishSiteData[siteIndex].dirList;
+ return true;
+}
+
+function ValidateData()
+{
+ if (!ValidateSettings())
+ return false;
+
+ var siteIndex = gDialog.SiteList.selectedIndex;
+ if (siteIndex == -1)
+ return false;
+
+ var filename = TrimString(gDialog.FilenameInput.value);
+ if (!filename)
+ {
+ ShowErrorInPanel(gDialog.PublishTab, "MissingPublishFilename", gDialog.FilenameInput);
+ return false;
+ }
+ gReturnData.filename = filename;
+
+ return true;
+}
+
+function ShowErrorInPanel(tab, errorMsgId, widgetWithError)
+{
+ SwitchPanel(tab);
+ ShowInputErrorMessage(GetString(errorMsgId));
+ if (widgetWithError)
+ SetTextboxFocus(widgetWithError);
+}
+
+function onAccept(event)
+{
+ if (ValidateData())
+ {
+ // DON'T save the docDir and otherDir before trying to publish
+ gReturnData.saveDirs = false;
+
+ // We save new site data to prefs only if we are attempting to publish
+ if (gSettingsChanged)
+ SavePublishDataToPrefs(gReturnData);
+
+ // Set flag to resave data after publishing
+ // so we save docDir and otherDir if we published successfully
+ gReturnData.savePublishData = true;
+
+ var title = TrimString(gDialog.PageTitleInput.value);
+ if (title != gPreviousTitle)
+ SetDocumentTitle(title);
+
+ SaveWindowLocation();
+ window.opener.ok = true;
+ return;
+ }
+
+ event.preventDefault();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublish.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublish.xhtml
new file mode 100644
index 0000000000..ed15c9f7d2
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublish.xhtml
@@ -0,0 +1,132 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?>
+
+<?xul-overlay href="chrome://editor/content/EditorPublishOverlay.xhtml"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPublish.dtd">
+
+<dialog title="&windowTitle.label;"
+ id="publishDlg"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()"
+ buttons="accept,cancel"
+ buttonlabelaccept="&publishButton.label;">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EditorPublish.js"/>
+ <script src="chrome://editor/content/publishprefs.js"/>
+ <script src="chrome://messenger/content/customElements.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <tabbox id="TabBox">
+ <tabs flex="1">
+ <tab id="PublishTab" oncommand="SelectPublishTab()" label="&publishTab.label;"/>
+ <tab id="SettingsTab" oncommand="SelectSettingsTab()" label="&settingsTab.label;"/>
+ </tabs>
+ <tabpanels>
+ <!-- PUBLISH PANEL -->
+ <vbox>
+ <spacer class="spacer"/>
+ <grid pack="start">
+ <columns><column/><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label value="&siteList.label;"
+ accesskey="&siteList.accesskey;"
+ control="SiteList"/>
+ <!-- Contents filled in at runtime -->
+ <menulist id="SiteList"
+ style="min-width:18em; max-width:18em;" crop="right"
+ tooltiptext="&siteList.tooltip;"
+ oncommand="SelectSiteList();"/>
+ <hbox>
+ <button label="&newSiteButton.label;"
+ accesskey="&newSiteButton.accesskey;"
+ oncommand="AddNewSite();"/>
+ <spacer flex="1"/>
+ </hbox>
+ </row>
+ <spacer class="spacer"/>
+ <row align="center">
+ <label value="&pageTitle.label;" accesskey="&pageTitle.accesskey;"
+ control="PageTitleInput"/>
+ <textbox id="PageTitleInput"
+ tooltiptext="&pageTitle.tooltip;" class="minWidth15"/>
+ <label value="&pageTitleExample.label;"/>
+ </row>
+ <row align="center">
+ <label value="&filename.label;" accesskey="&filename.accesskey;"
+ control="FilenameInput"/>
+ <textbox id="FilenameInput"
+ tooltiptext="&filename.tooltip;" class="minWidth15 uri-element"/>
+ <label value="&filenameExample.label;"/>
+ </row>
+ </rows>
+ </grid>
+ <spacer class="spacer"/>
+ <label value="&docDirList.label;"
+ accesskey="&docDirList.accesskey;"
+ control="DocDirList"/>
+ <hbox align="center">
+ <!-- Contents filled in at runtime -->
+ <menulist is="menulist-editable" id="DocDirList"
+ class="minWidth20 uri-element" editable="true" flex="1"
+ tooltiptext="&docDirList.tooltip;" oninput="onInputSettings();"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <groupbox>
+ <caption>
+ <checkbox id="OtherDirCheckbox" label="&publishImgCheckbox.label;"
+ accesskey="&publishImgCheckbox.accesskey;"
+ tooltiptext="&publishImgCheckbox.tooltip;"
+ oncommand="doEnabling();"/>
+ </caption>
+ <vbox>
+ <radiogroup id="OtherDirRadiogroup">
+ <hbox>
+ <spacer class="checkbox-spacer"/>
+ <radio id="SameLocationRadio" label="&sameLocationRadio.label;"
+ accesskey="&sameLocationRadio.accesskey;"
+ tooltiptext="&sameLocationRadio.tooltip;"
+ oncommand="doEnabling();"/>
+ </hbox>
+ <hbox>
+ <spacer class="checkbox-spacer"/>
+ <radio id="UseSubdirRadio" label="&useSubdirRadio.label;"
+ accesskey="&useSubdirRadio.accesskey;"
+ tooltiptext="&useSubdirRadio.tooltip;"
+ oncommand="doEnabling();"/>
+ </hbox>
+ </radiogroup>
+ </vbox>
+ <hbox>
+ <spacer class="checkbox-spacer"/>
+ <spacer class="radio-spacer"/>
+ <!-- Contents filled in at runtime -->
+ <menulist is="menulist-editable" id="OtherDirList"
+ class="minWidth20 uri-element"
+ editable="true" flex="1" tooltiptext="&otherDirList.tooltip;"
+ oninput="onInputSettings();"/>
+ </hbox>
+ </groupbox>
+ <spacer flex="1"/>
+ </vbox><!-- Publish Panel -->
+
+ <!-- SETTINGS PANEL -->
+ <hbox id="SettingsPanel">
+ <!-- from EditorPublishOverlay.xhtml -->
+ <vbox id="PublishSettingsInputs" flex="1"/>
+ </hbox><!-- Settings Panel -->
+ </tabpanels>
+ </tabbox>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml
new file mode 100644
index 0000000000..136a75623c
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublishOverlay.xhtml
@@ -0,0 +1,66 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://editor/locale/EditorPublish.dtd">
+
+<overlay id="EditorPublishOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+<vbox id="PublishSettingsInputs">
+ <groupbox id="ServerSettingsBox">
+ <label class="header">&serverInfo.label;</label>
+ <hbox align="center">
+ <label value="&siteName.label;" accesskey="&siteName.accesskey;"
+ control="SiteNameInput"/>
+ <textbox id="SiteNameInput" class="MinWidth20em" flex="1"
+ tooltiptext="&siteName.tooltip;" oninput="onInputSettings();"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <label value="&siteUrl.label;" accesskey="&siteUrl.accesskey;"
+ control="PublishUrlInput"/>
+ <textbox id="PublishUrlInput" class="MinWidth20em uri-element"
+ tooltiptext="&siteUrl.tooltip;" oninput="onInputSettings();"/>
+ <spacer class="spacer"/>
+ <label value="&browseUrl.label;" accesskey="&browseUrl.accesskey;"
+ control="BrowseUrlInput"/>
+ <textbox id="BrowseUrlInput" class="MinWidth20em uri-element"
+ tooltiptext="&browseUrl.tooltip;" oninput="onInputSettings();"/>
+ <spacer class="spacer"/>
+ </groupbox>
+ <groupbox id="LoginSettingsBox">
+ <label class="header">&loginInfo.label;</label>
+ <grid>
+ <columns><column flex="1"/><column flex="3"/></columns>
+ <rows>
+ <row align="center">
+ <label value="&username.label;" accesskey="&username.accesskey;"
+ control="UsernameInput"/>
+ <textbox id="UsernameInput" class="MinWidth10em" flex="1"
+ tooltiptext="&username.tooltip;" oninput="onInputSettings();"/>
+ </row>
+ <row align="center">
+ <label value="&password.label;" accesskey="&password.accesskey;"
+ control="PasswordInput"/>
+ <hbox>
+ <textbox id="PasswordInput" type="password" class="MinWidth5em"
+ oninput="onInputSettings();"
+ tooltiptext="&password.tooltip;"/>
+ <checkbox id="SavePassword" label="&savePassword.label;"
+ accesskey="&savePassword.accesskey;"
+ tooltiptext="&savePassword.tooltip;"
+ oncommand="onInputSettings();"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ <spacer class="spacer"/>
+ </groupbox>
+</vbox>
+
+</overlay>
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishProgress.js b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.js
new file mode 100644
index 0000000000..dafa053661
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.js
@@ -0,0 +1,391 @@
+/* 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 gInProgress = true;
+var gPublishData;
+var gPersistObj;
+var gTotalFileCount = 0;
+var gSucceededCount = 0;
+var gFinished = false;
+var gPublishingFailed = false;
+var gFileNotFound = false;
+var gStatusMessage="";
+
+var gTimerID;
+var gAllowEnterKey = false;
+
+// Publishing error codes
+// These are translated from C++ error code strings like this:
+// kFileNotFound = "FILE_NOT_FOUND",
+const kNetReset = 2152398868; // nsISocketTransportService.idl
+const kFileNotFound = 2152857618;
+const kNotConnected = 2152398860; // in netCore.h
+const kConnectionRefused = 2152398861; // nsISocketTransportService.idl
+const kNetTimeout = 2152398862; // nsISocketTransportService.idl
+const kNoConnectionOrTimeout = 2152398878;
+const kPortAccessNotAllowed = 2152398867; // netCore.h
+const kOffline = 2152398865; // netCore.h
+const kDiskFull = 2152857610;
+const kNoDeviceSpace = 2152857616;
+const kNameTooLong = 2152857617;
+const kAccessDenied = 2152857621;
+
+// These are more errors that I don't think we encounter during publishing,
+// so we don't have error strings yet. Let's keep them here for future reference
+//const kUnrecognizedPath = 2152857601;
+//const kUnresolvableSymlink = 2152857602;
+//const kUnknownType = 2152857604;
+//const kDestinationNotDir = 2152857605;
+//const kTargetDoesNotExist = 2152857606;
+//const kAlreadyExists = 2152857608;
+//const kInvalidPath = 2152857609;
+//const kNotDirectory = 2152857612;
+//const kIsDirectory = 2152857613;
+//const kIsLocked = 2152857614;
+//const kTooBig = 2152857615;
+//const kReadOnly = 2152857619;
+//const kDirectoryNotEmpty = 2152857620;
+//const kErrorBindingRedirected = 2152398851;
+//const kAlreadyConnected = 2152398859; // in netCore.h
+//const kInProgress = 2152398863; // netCore.h
+//const kNoContent = 2152398865; // netCore.h
+//const kUnknownProtocol = 2152398866 // netCore.h
+//const kFtpLogin = 2152398869; // ftpCore.h
+//const kFtpCWD = 2152398870; // ftpCore.h
+//const kFtpPasv = 2152398871; // ftpCore.h
+//const kFtpPwd = 2152398872; // ftpCore.h
+
+document.addEventListener("dialogaccept", onEnterKey);
+document.addEventListener("dialogcancel", onClose);
+
+function Startup()
+{
+ gPublishData = window.arguments[0];
+ if (!gPublishData)
+ {
+ dump("No publish data!\n");
+ window.close();
+ return;
+ }
+
+ gDialog.FileList = document.getElementById("FileList");
+ gDialog.FinalStatusMessage = document.getElementById("FinalStatusMessage");
+ gDialog.StatusMessage = document.getElementById("StatusMessage");
+ gDialog.KeepOpen = document.getElementById("KeepOpen");
+ gDialog.Close = document.documentElement.getButton("cancel");
+
+ SetWindowLocation();
+ var title = GetDocumentTitle();
+ if (!title)
+ title = "(" + opener.gUntitledString + ")";
+ document.title = GetString("PublishProgressCaption").replace(/%title%/, title);
+
+ document.getElementById("PublishToSite").value =
+ GetString("PublishToSite").replace(/%title%/, TruncateStringAtWordEnd(gPublishData.siteName, 25));
+
+ // Show publishing destination URL
+ document.getElementById("PublishUrl").value = gPublishData.publishUrl;
+
+ // Show subdirectories only if not empty
+ if (gPublishData.docDir || gPublishData.otherDir)
+ {
+ if (gPublishData.docDir)
+ document.getElementById("docDir").value = gPublishData.docDir;
+ else
+ document.getElementById("DocSubdir").hidden = true;
+
+ if (gPublishData.publishOtherFiles && gPublishData.otherDir)
+ document.getElementById("otherDir").value = gPublishData.otherDir;
+ else
+ document.getElementById("OtherSubdir").hidden = true;
+ }
+ else
+ document.getElementById("Subdirectories").hidden = true;
+
+ // Add the document to the "publish to" list as quick as possible!
+ SetProgressStatus(gPublishData.filename, "busy");
+
+ if (gPublishData.publishOtherFiles)
+ {
+ // When publishing images as well, expand list to show more items
+ gDialog.FileList.setAttribute("rows", 5);
+ window.sizeToContent();
+ }
+
+ // Now that dialog is initialized, we can start publishing
+ gPersistObj = window.opener.StartPublishing();
+}
+
+// this function is to be used when we cancel persist's saving
+// since not all messages will be returned to us if we cancel
+// this function changes status for all non-done/non-failure to failure
+function SetProgressStatusCancel()
+{
+ let listitems = document.querySelectorAll('listitem:not([progress="done"]):not([progress="failed"])');
+ if (!listitems)
+ return;
+
+ for (var i=0; i < listitems.length; i++)
+ {
+ listitems[i].setAttribute("progress", "failed");
+ }
+}
+
+// Add filename to list of files to publish
+// or set status for file already in the list
+// Returns true if file was in the list
+function SetProgressStatus(filename, status)
+{
+ if (!filename)
+ return false;
+
+ if (!status)
+ status = "busy";
+
+ // Just set attribute for status icon if we already have this filename.
+ let listitem = document.querySelector('listitem[label="' + filename + '"]');
+ if (listitem)
+ {
+ listitem.setAttribute("progress", status);
+ return true;
+ }
+ // We're adding a new file item to list
+ gTotalFileCount++;
+
+ listitem = document.createXULElement("listitem");
+ if (listitem)
+ {
+ listitem.setAttribute("class", "listitem-iconic progressitem");
+ // This triggers CSS to show icon for each status state
+ listitem.setAttribute("progress", status);
+ listitem.setAttribute("label", filename);
+ gDialog.FileList.appendChild(listitem);
+ }
+ return false;
+}
+
+function SetProgressFinished(filename, networkStatus)
+{
+ var abortPublishing = false;
+ if (filename)
+ {
+ var status = networkStatus ? "failed" : "done";
+ if (networkStatus == 0)
+ gSucceededCount++;
+
+ SetProgressStatus(filename, status);
+ }
+
+ if (networkStatus != 0) // Error condition
+ {
+ // We abort on all errors except if image file was not found
+ abortPublishing = networkStatus != kFileNotFound;
+
+ // Mark all remaining files as "failed"
+ if (abortPublishing)
+ {
+ gPublishingFailed = true;
+ SetProgressStatusCancel();
+ gDialog.FinalStatusMessage.value = GetString("PublishFailed");
+ }
+
+ switch (networkStatus)
+ {
+ case kFileNotFound:
+ gFileNotFound = true;
+ if (filename)
+ gStatusMessage = GetString("FileNotFound").replace(/%file%/, filename);
+ break;
+ case kNetReset:
+ // We get this when subdir doesn't exist AND
+ // if filename used is same as an existing subdir
+ var dir = (gPublishData.filename == filename) ?
+ gPublishData.docDir : gPublishData.otherDir;
+
+ if (dir)
+ {
+ // This is the ambiguous case when we can't tell if subdir or filename is bad
+ // Remove terminal "/" from dir string and insert into message
+ gStatusMessage = GetString("SubdirDoesNotExist").replace(/%dir%/, dir.slice(0, dir.length-1));
+ gStatusMessage = gStatusMessage.replace(/%file%/, filename);
+
+ // Remove directory from saved prefs
+ // XXX Note that if subdir is good,
+ // but filename = next level subdirectory name,
+ // we really shouldn't remove subdirectory,
+ // but it's impossible to differentiate this case!
+ RemovePublishSubdirectoryFromPrefs(gPublishData, dir);
+ }
+ else if (filename)
+ gStatusMessage = GetString("FilenameIsSubdir").replace(/%file%/, filename);
+
+ break;
+ case kNotConnected:
+ case kConnectionRefused:
+ case kNetTimeout:
+ case kNoConnectionOrTimeout:
+ case kPortAccessNotAllowed:
+ gStatusMessage = GetString("ServerNotAvailable");
+ break;
+ case kOffline:
+ gStatusMessage = GetString("Offline");
+ break;
+ case kDiskFull:
+ case kNoDeviceSpace:
+ if (filename)
+ gStatusMessage = GetString("DiskFull").replace(/%file%/, filename);
+ break;
+ case kNameTooLong:
+ if (filename)
+ gStatusMessage = GetString("NameTooLong").replace(/%file%/, filename);
+ break;
+ case kAccessDenied:
+ if (filename)
+ gStatusMessage = GetString("AccessDenied").replace(/%file%/, filename);
+ break;
+ case kUnknownType:
+ default:
+ gStatusMessage = GetString("UnknownPublishError")
+ break;
+ }
+ }
+ else if (!filename)
+ {
+ gFinished = true;
+
+ document.documentElement.setAttribute("buttonlabelcancel",
+ document.documentElement.getAttribute("buttonlabelclose"));
+
+ if (!gStatusMessage)
+ gStatusMessage = GetString(gPublishingFailed ? "UnknownPublishError" : "AllFilesPublished");
+
+ // Now allow "Enter/Return" key to close the dialog
+ AllowDefaultButton();
+
+ if (gPublishingFailed || gFileNotFound)
+ {
+ // Show "Troubleshooting" button to help solving problems
+ // and key for successful / failed files
+ document.getElementById("failureBox").hidden = false;
+ }
+ }
+
+ if (gStatusMessage)
+ SetStatusMessage(gStatusMessage);
+}
+
+function CheckKeepOpen()
+{
+ if (gTimerID)
+ {
+ clearTimeout(gTimerID);
+ gTimerID = null;
+ }
+}
+
+function onClose()
+{
+ if (!gFinished)
+ {
+ const buttonFlags = (Services.prompt.BUTTON_TITLE_IS_STRING *
+ Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL *
+ Services.prompt.BUTTON_POS_1);
+ let button = Services.prompt.confirmEx(window,
+ GetString("CancelPublishTitle"),
+ GetString("CancelPublishMessage"),
+ buttonFlags,
+ GetString("CancelPublishContinue"),
+ null, null, null, {});
+ if (button == 0)
+ return false;
+ }
+
+ if (gTimerID)
+ {
+ clearTimeout(gTimerID);
+ gTimerID = null;
+ }
+
+ if (!gFinished && gPersistObj)
+ {
+ try {
+ gPersistObj.cancelSave();
+ } catch (e) {}
+ }
+ SaveWindowLocation();
+
+ // Tell caller so they can cleanup and restore editability
+ window.opener.FinishPublishing();
+ return true;
+}
+
+function AllowDefaultButton()
+{
+ gDialog.Close.setAttribute("default","true");
+ gAllowEnterKey = true;
+}
+
+function onEnterKey(event)
+{
+ if (gAllowEnterKey)
+ return CloseDialog();
+
+ event.preventDefault();
+}
+
+function RequestCloseDialog()
+{
+ // Finish progress messages, settings buttons etc.
+ SetProgressFinished(null, 0);
+
+ if (!gDialog.KeepOpen.checked)
+ {
+ // Leave window open a minimum amount of time
+ gTimerID = setTimeout(CloseDialog, 3000);
+ }
+
+ // Set "completed" message if we succeeded
+ // (Some image files may have failed,
+ // but we don't abort publishing for that)
+ if (!gPublishingFailed)
+ {
+ gDialog.FinalStatusMessage.value = GetString("PublishCompleted");
+ if (gFileNotFound && gTotalFileCount-gSucceededCount)
+ {
+ // Show number of files that failed to upload
+ gStatusMessage =
+ (GetString("FailedFileMsg").replace(/%x%/,(gTotalFileCount-gSucceededCount)))
+ .replace(/%total%/,gTotalFileCount);
+
+ SetStatusMessage(gStatusMessage);
+ }
+ }
+}
+
+function SetStatusMessage(message)
+{
+ // Status message is a child of <description> element
+ // so text can wrap to multiple lines if necessary
+ if (gDialog.StatusMessage.firstChild)
+ {
+ gDialog.StatusMessage.firstChild.data = message;
+ }
+ else
+ {
+ var textNode = document.createTextNode(message);
+ if (textNode)
+ gDialog.StatusMessage.appendChild(textNode);
+ }
+ window.sizeToContent();
+}
+
+function CloseDialog()
+{
+ SaveWindowLocation();
+ window.opener.FinishPublishing();
+ try {
+ window.close();
+ } catch (e) {}
+}
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml
new file mode 100644
index 0000000000..2cf422c8b5
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublishProgress.xhtml
@@ -0,0 +1,66 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPublishProgress.dtd">
+
+<dialog title=""
+ id="publishProgressDlg"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ buttons="cancel"
+ buttonlabelclose="&closeButton.label;"
+ onload="Startup()">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/publishprefs.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EditorPublishProgress.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <caption><label id="PublishToSite"/></caption>
+ <label value="&siteUrl.label;"/>
+ <hbox>
+ <label class="indent bold" id="PublishUrl"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <grid id="Subdirectories">
+ <columns><column/><column/></columns>
+ <rows>
+ <row id="DocSubdir">
+ <label value="&docSubdir.label;"/>
+ <label id="docDir"/>
+ </row>
+ <row id="OtherSubdir">
+ <label value="&otherSubdir.label;"/>
+ <label id="otherDir"/>
+ </row>
+ </rows>
+ </grid>
+ <label id="OtherUrl" class="bold" style="margin-left:3em"/>
+ </groupbox>
+ <groupbox>
+ <caption><label value="&fileList.label;"/></caption>
+ <vbox align="center" style="max-width:30em">
+ <label id="FinalStatusMessage" class="bold" value="&status.label;"/>
+ </vbox>
+ <description id="StatusMessage" class="wrap" style="max-width:30em; min-height: 1em"/>
+ <vbox flex="1">
+ <listbox id="FileList" rows="1"/>
+ </vbox>
+ <hbox align="center" id="failureBox" hidden="true">
+ <image class="progressitem" progress="done"/>
+ <label value="&succeeded.label;"/>
+ <spacer class="bigspacer"/>
+ <image class="progressitem" progress="failed"/>
+ <label value="&failed.label;"/>
+ </hbox>
+ </groupbox>
+ <checkbox id="KeepOpen" label="&keepOpen;" oncommand="CheckKeepOpen();" persist="checked"/>
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishSettings.js b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.js
new file mode 100644
index 0000000000..01677ae8c0
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.js
@@ -0,0 +1,343 @@
+/* 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 gPublishSiteData;
+var gPublishDataChanged = false;
+var gDefaultSiteIndex = -1;
+var gDefaultSiteName;
+var gPreviousDefaultSite;
+var gPreviousTitle;
+var gSettingsChanged = false;
+var gSiteDataChanged = false;
+var gAddNewSite = false;
+var gCurrentSiteIndex = -1;
+var gPasswordManagerOn = true;
+
+// Dialog initialization code
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+function Startup()
+{
+ if (!GetCurrentEditor())
+ {
+ window.close();
+ return;
+ }
+
+ gDialog.SiteList = document.getElementById("SiteList");
+ gDialog.SiteNameInput = document.getElementById("SiteNameInput");
+ gDialog.PublishUrlInput = document.getElementById("PublishUrlInput");
+ gDialog.BrowseUrlInput = document.getElementById("BrowseUrlInput");
+ gDialog.UsernameInput = document.getElementById("UsernameInput");
+ gDialog.PasswordInput = document.getElementById("PasswordInput");
+ gDialog.SavePassword = document.getElementById("SavePassword");
+ gDialog.SetDefaultButton = document.getElementById("SetDefaultButton");
+ gDialog.RemoveSiteButton = document.getElementById("RemoveSiteButton");
+ gDialog.OkButton = document.documentElement.getButton("accept");
+
+ gPublishSiteData = GetPublishSiteData();
+ gDefaultSiteName = GetDefaultPublishSiteName();
+ gPreviousDefaultSite = gDefaultSiteName;
+
+ gPasswordManagerOn = Services.prefs.getBoolPref("signon.rememberSignons");
+ gDialog.SavePassword.disabled = !gPasswordManagerOn;
+
+ InitDialog();
+
+ SetWindowLocation();
+}
+
+function InitDialog()
+{
+ // If there's no current site data, start a new item in the Settings panel
+ if (!gPublishSiteData)
+ {
+ AddNewSite();
+ }
+ else
+ {
+ FillSiteList();
+
+ // uncomment next code line if you want preselection of the default
+ // publishing site
+ //InitSiteSettings(gDefaultSiteIndex);
+
+ SetTextboxFocus(gDialog.SiteNameInput);
+ }
+}
+
+function FillSiteList()
+{
+ // Prevent triggering SelectSiteList() actions
+ gIsSelecting = true;
+ ClearListbox(gDialog.SiteList);
+ gIsSelecting = false;
+ gDefaultSiteIndex = -1;
+
+ // Fill the site list
+ var count = gPublishSiteData.length;
+ for (var i = 0; i < count; i++)
+ {
+ var name = gPublishSiteData[i].siteName;
+ var item = gDialog.SiteList.appendItem(name);
+ SetPublishItemStyle(item);
+ if (name == gDefaultSiteName)
+ gDefaultSiteIndex = i;
+ }
+}
+
+function SetPublishItemStyle(item)
+{
+ // Display default site with bold style
+ if (item)
+ {
+ if (item.getAttribute("label") == gDefaultSiteName)
+ item.setAttribute("class", "bold");
+ else
+ item.removeAttribute("class");
+ }
+}
+
+function AddNewSite()
+{
+ // Save any pending changes locally first
+ if (!ApplyChanges())
+ return;
+
+ // Initialize Setting widgets to none of the selected sites
+ InitSiteSettings(-1);
+ gAddNewSite = true;
+
+ SetTextboxFocus(gDialog.SiteNameInput);
+}
+
+function RemoveSite()
+{
+ if (!gPublishSiteData)
+ return;
+
+ var index = gDialog.SiteList.selectedIndex;
+ if (index != -1)
+ {
+ let item = gDialog.SiteList.selectedItem;
+ var nameToRemove = item.getAttribute("label");
+
+ // Remove one item from site data array
+ gPublishSiteData.splice(index, 1);
+ // Remove item from site list
+ gDialog.SiteList.clearSelection();
+ item.remove();
+
+ // Adjust if we removed last item and reselect a site
+ if (index >= gPublishSiteData.length)
+ index--;
+ InitSiteSettings(index);
+
+ if (nameToRemove == gDefaultSiteName)
+ {
+ // Deleting current default -- set to new selected item
+ // Arbitrary, but what else to do?
+ SetDefault();
+ }
+ gSiteDataChanged = true;
+ }
+}
+
+function SetDefault()
+{
+ if (!gPublishSiteData)
+ return;
+
+ var index = gDialog.SiteList.selectedIndex;
+ if (index != -1)
+ {
+ gDefaultSiteIndex = index;
+ gDefaultSiteName = gPublishSiteData[index].siteName;
+
+ // Set bold style on new default
+ var item = gDialog.SiteList.firstChild;
+ while (item)
+ {
+ SetPublishItemStyle(item);
+ item = item.nextSibling;
+ }
+ }
+}
+
+// Recursion prevention:
+// Use when you don't want to trigger ApplyChanges and InitSiteSettings
+var gIsSelecting = false;
+
+function SelectSiteList()
+{
+ if (gIsSelecting)
+ return;
+
+ gIsSelecting = true;
+ var newIndex = gDialog.SiteList.selectedIndex;
+
+ // Save any pending changes locally first
+ if (!ApplyChanges())
+ return;
+
+ InitSiteSettings(newIndex);
+
+ gIsSelecting = false;
+}
+
+// Use this to prevent recursion in SelectSiteList
+function SetSelectedSiteIndex(index)
+{
+ gIsSelecting = true;
+ gDialog.SiteList.selectedIndex = index;
+ gIsSelecting = false;
+}
+
+function InitSiteSettings(selectedSiteIndex)
+{
+ // Index to the site we will need to update if settings changed
+ gCurrentSiteIndex = selectedSiteIndex;
+
+ SetSelectedSiteIndex(selectedSiteIndex);
+ var haveData = (gPublishSiteData && selectedSiteIndex != -1);
+
+ gDialog.SiteNameInput.value = haveData ? gPublishSiteData[selectedSiteIndex].siteName : "";
+ gDialog.PublishUrlInput.value = haveData ? gPublishSiteData[selectedSiteIndex].publishUrl : "";
+ gDialog.BrowseUrlInput.value = haveData ? gPublishSiteData[selectedSiteIndex].browseUrl : "";
+ gDialog.UsernameInput.value = haveData ? gPublishSiteData[selectedSiteIndex].username : "";
+
+ var savePassord = haveData && gPasswordManagerOn;
+ gDialog.PasswordInput.value = savePassord ? gPublishSiteData[selectedSiteIndex].password : "";
+ gDialog.SavePassword.checked = savePassord ? gPublishSiteData[selectedSiteIndex].savePassword : false;
+
+ gDialog.SetDefaultButton.disabled = !haveData;
+ gDialog.RemoveSiteButton.disabled = !haveData;
+ gSettingsChanged = false;
+}
+
+function onInputSettings()
+{
+ // TODO: Save current data during SelectSite1 and compare here
+ // to detect if real change has occurred?
+ gSettingsChanged = true;
+}
+
+function ApplyChanges()
+{
+ if (gSettingsChanged && !UpdateSettings())
+ {
+ // Restore selection to previously current site
+ SetSelectedSiteIndex(gCurrentSiteIndex);
+ return false;
+ }
+ return true;
+}
+
+function UpdateSettings()
+{
+ // Validate and add new site
+ var newName = TrimString(gDialog.SiteNameInput.value);
+ if (!newName)
+ {
+ ShowInputErrorMessage(GetString("MissingSiteNameError"), gDialog.SiteNameInput);
+ return false;
+ }
+ if (PublishSiteNameExists(newName, gPublishSiteData, gCurrentSiteIndex))
+ {
+ ShowInputErrorMessage(GetString("DuplicateSiteNameError").replace(/%name%/, newName));
+ SetTextboxFocus(gDialog.SiteNameInput);
+ return false;
+ }
+
+ var newUrl = FormatUrlForPublishing(gDialog.PublishUrlInput.value);
+ if (!newUrl)
+ {
+ ShowInputErrorMessage(GetString("MissingPublishUrlError"), gDialog.PublishUrlInput);
+ return false;
+ }
+
+ // Start assuming we're updating existing site at gCurrentSiteIndex
+ var newSiteData = false;
+
+ if (!gPublishSiteData)
+ {
+ // First time used - Create the first site profile
+ gPublishSiteData = new Array(1);
+ gCurrentSiteIndex = 0;
+ newSiteData = true;
+ }
+ else if (gCurrentSiteIndex == -1)
+ {
+ // No currently-selected site,
+ // must be adding a new site
+ // Add new data at the end of list
+ gCurrentSiteIndex = gPublishSiteData.length;
+ newSiteData = true;
+ }
+
+ if (newSiteData)
+ {
+ // Init new site profile
+ gPublishSiteData[gCurrentSiteIndex] = {};
+ gPublishSiteData[gCurrentSiteIndex].docDir = "";
+ gPublishSiteData[gCurrentSiteIndex].otherDir = "";
+ gPublishSiteData[gCurrentSiteIndex].dirList = [""];
+ gPublishSiteData[gCurrentSiteIndex].previousSiteName = newName;
+ }
+
+ gPublishSiteData[gCurrentSiteIndex].siteName = newName;
+ gPublishSiteData[gCurrentSiteIndex].publishUrl = newUrl;
+ gPublishSiteData[gCurrentSiteIndex].browseUrl = FormatUrlForPublishing(gDialog.BrowseUrlInput.value);
+ gPublishSiteData[gCurrentSiteIndex].username = TrimString(gDialog.UsernameInput.value);
+ gPublishSiteData[gCurrentSiteIndex].password= gDialog.PasswordInput.value;
+ gPublishSiteData[gCurrentSiteIndex].savePassword = gDialog.SavePassword.checked;
+
+ if (gCurrentSiteIndex == gDefaultSiteIndex)
+ gDefaultSiteName = newName;
+
+ // When adding the very first site, assume that's the default
+ if (gPublishSiteData.length == 1 && !gDefaultSiteName)
+ {
+ gDefaultSiteName = gPublishSiteData[0].siteName;
+ gDefaultSiteIndex = 0;
+ }
+
+ FillSiteList();
+
+ // Select current site in list
+ SetSelectedSiteIndex(gCurrentSiteIndex);
+
+ // Signal saving data to prefs
+ gSiteDataChanged = true;
+
+ // Clear current site flags
+ gSettingsChanged = false;
+ gAddNewSite = false;
+
+ return true;
+}
+
+function onAccept(event)
+{
+ // Save any pending changes locally first
+ if (!ApplyChanges()) {
+ event.preventDefault();
+ return;
+ }
+
+ if (gSiteDataChanged)
+ {
+ // Save all local data to prefs
+ SavePublishSiteDataToPrefs(gPublishSiteData, gDefaultSiteName);
+ }
+ else if (gPreviousDefaultSite != gDefaultSiteName)
+ {
+ // only the default site was changed
+ SetDefaultSiteName(gDefaultSiteName);
+ }
+
+ SaveWindowLocation();
+}
diff --git a/comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml
new file mode 100644
index 0000000000..ace6c2fc5d
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorPublishSettings.xhtml
@@ -0,0 +1,50 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<?xul-overlay href="chrome://editor/content/EditorPublishOverlay.xhtml"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorPublish.dtd">
+
+<dialog title="&windowTitleSettings.label;"
+ id="publishSettingsDlg"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="Startup()"
+ buttons="accept,cancel">
+
+ <!-- Methods common to all editor dialogs -->
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EditorPublishSettings.js"/>
+ <script src="chrome://editor/content/publishprefs.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <hbox id="SettingsPanel">
+ <groupbox align="center">
+ <label class="header">&publishSites.label;</label>
+ <!-- XXX: If tree isn't wrapped in vbox, it appears BELOW next vbox -->
+ <vbox flex="1">
+ <listbox rows="4" id="SiteList" flex="1" onselect="SelectSiteList();"/>
+ </vbox>
+ <hbox pack="center">
+ <vbox>
+ <button id="NewSiteButton" label="&newSiteButton.label;"
+ accesskey="&newSiteButton.accesskey;" oncommand="AddNewSite();"/>
+ <button id="SetDefaultButton" label="&setDefaultButton.label;"
+ accesskey="&setDefaultButton.accesskey;" oncommand="SetDefault();"/>
+ <button id="RemoveSiteButton" label="&removeButton.label;"
+ accesskey="&removeButton.accesskey;" oncommand="RemoveSite();"/>
+ </vbox>
+ </hbox>
+ </groupbox>
+ <!-- from EditorPublishOverlay.xhtml -->
+ <vbox id="PublishSettingsInputs"/>
+ </hbox>
+ <spacer class="spacer"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js
new file mode 100644
index 0000000000..745a8bfd30
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.js
@@ -0,0 +1,155 @@
+/* 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/. */
+
+document.addEventListener("dialogaccept", onAccept);
+document.addEventListener("dialogcancel", onCancel);
+
+var {CharsetMenu} = ChromeUtils.import("resource://gre/modules/CharsetMenu.jsm");
+
+var gCharset="";
+var gTitleWasEdited = false;
+var gCharsetWasChanged = false;
+var gInsertNewContentType = false;
+var gContenttypeElement;
+var gInitDone = false;
+var gCharsetInfo;
+
+//Cancel() is in EdDialogCommon.js
+
+var gCharsetView = {
+ get rowCount() { return gCharsetInfo.length; },
+ selection: null,
+ getRowProperties: function(index) { return ""; },
+ getCellProperties: function(index, column) { return ""; },
+ getColumnProperties: function(columm) { return ""; },
+ isContainer: function() { return false; },
+ isContainerOpen: function() { return false; },
+ isContainerEmpty: function() { return true; },
+ isSeparator: function() { return false; },
+ isSorted: function() { return false; },
+ canDrop: function(index, orientation) { return false; },
+ drop: function(index, orientation) {},
+ getParentIndex: function(index) { return -1; },
+ hasNextSibling: function(index, after) { return false; },
+ getLevel: function(index) { return 1; },
+ getImageSrc: function(index) { return null; },
+ getProgressMode: function(index) { return 0; },
+ getCellValue: function(index) { return ""; },
+ getCellText: function(index) { return gCharsetInfo[index].label; },
+ toggleOpenState: function(index) {},
+ cycleHeader: function(column) {},
+ selectionChanged: function() {},
+ cycleCell: function(index, column) {},
+ isEditable: function isEditable(index, column) { return false; },
+};
+
+function Startup()
+{
+ var editor = GetCurrentEditor();
+ if (!editor)
+ {
+ window.close();
+ return;
+ }
+
+ gDialog.TitleInput = document.getElementById("TitleInput");
+ gDialog.charsetTree = document.getElementById('CharsetTree');
+ gDialog.exportToText = document.getElementById('ExportToText');
+
+ gContenttypeElement = GetMetaElementByAttribute("http-equiv", "content-type");
+ if (!gContenttypeElement && (editor.contentsMIMEType != 'text/plain'))
+ {
+ gContenttypeElement = CreateMetaElementWithAttribute("http-equiv", "content-type");
+ if (!gContenttypeElement )
+ {
+ window.close();
+ return;
+ }
+ gInsertNewContentType = true;
+ }
+
+ try {
+ gCharset = editor.documentCharacterSet;
+ } catch (e) {}
+
+ var data = CharsetMenu.getData();
+ var charsets = data.pinnedCharsets.concat(data.otherCharsets);
+ gCharsetInfo = CharsetMenu.getCharsetInfo(charsets.map(info => info.value));
+ gDialog.charsetTree.view = gCharsetView;
+
+ InitDialog();
+
+ // Use the same text as the messagebox for getting title by regular "Save"
+ document.getElementById("EnterTitleLabel").setAttribute("value",GetString("NeedDocTitle"));
+ // This is an <HTML> element so it wraps -- append a child textnode
+ var helpTextParent = document.getElementById("TitleHelp");
+ var helpText = document.createTextNode(GetString("DocTitleHelp"));
+ if (helpTextParent)
+ helpTextParent.appendChild(helpText);
+
+ // SET FOCUS TO FIRST CONTROL
+ SetTextboxFocus(gDialog.TitleInput);
+
+ gInitDone = true;
+
+ SetWindowLocation();
+}
+
+
+function InitDialog()
+{
+ gDialog.TitleInput.value = GetDocumentTitle();
+
+ var tree = gDialog.charsetTree;
+ var index = gCharsetInfo.map(info => info.value).indexOf(gCharset);
+ if (index >= 0) {
+ tree.view.selection.select(index);
+ tree.ensureRowIsVisible(index);
+ }
+}
+
+
+function onAccept()
+{
+ var editor = GetCurrentEditor();
+ editor.beginTransaction();
+
+ if(gCharsetWasChanged)
+ {
+ try {
+ SetMetaElementContent(gContenttypeElement, "text/html; charset=" + gCharset, gInsertNewContentType, true);
+ editor.documentCharacterSet = gCharset;
+ } catch (e) {}
+ }
+
+ editor.endTransaction();
+
+ if(gTitleWasEdited)
+ SetDocumentTitle(TrimString(gDialog.TitleInput.value));
+
+ window.opener.ok = true;
+ window.opener.exportToText = gDialog.exportToText.checked;
+ SaveWindowLocation();
+}
+
+
+function SelectCharset()
+{
+ if(gInitDone)
+ {
+ try
+ {
+ gCharset = gCharsetInfo[gDialog.charsetTree.currentIndex].value;
+ if (gCharset)
+ gCharsetWasChanged = true;
+ }
+ catch(e) {}
+ }
+}
+
+
+function TitleChanged()
+{
+ gTitleWasEdited = true;
+}
diff --git a/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml
new file mode 100644
index 0000000000..815a56dfe3
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/EditorSaveAsCharset.xhtml
@@ -0,0 +1,46 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://editor/locale/EditorSaveAsCharset.dtd">
+
+<dialog title="&windowTitle2.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ onload = "Startup()"
+ style="width: 32em;">
+
+ <script src="chrome://editor/content/editorUtilities.js"/>
+ <script src="chrome://editor/content/EdDialogCommon.js"/>
+ <script src="chrome://editor/content/EditorSaveAsCharset.js"/>
+
+ <spacer id="location" offsetY="50" persist="offsetX offsetY"/>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label class="header">&documentTitleTitle.label;</label>
+ </hbox>
+ <label id="EnterTitleLabel"/>
+ <textbox id="TitleInput" oninput="TitleChanged();"/>
+ <description id="TitleHelp" class="wrap" style="width:1em" />
+ </groupbox>
+
+ <groupbox flex="1">
+ <hbox class="groupbox-title">
+ <label class="header">&documentCharsetTitle2.label;</label>
+ </hbox>
+ <label value="&documentCharsetDesc2.label;"/>
+ <tree id="CharsetTree" rows="8" hidecolumnpicker="true" onselect="SelectCharset();">
+ <treecols>
+ <treecol id="CharsetCol" flex="1" hideheader="true"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ </groupbox>
+
+ <checkbox id="ExportToText" label="&documentExportToText.label;" />
+ <separator class="groove"/>
+</dialog>
diff --git a/comm/suite/editor/components/dialogs/content/edImage.inc.xhtml b/comm/suite/editor/components/dialogs/content/edImage.inc.xhtml
new file mode 100644
index 0000000000..e80fb0457c
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/content/edImage.inc.xhtml
@@ -0,0 +1,248 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+ <vbox id="imageLocation">
+ <spacer class="spacer"/>
+ <label control = "srcInput"
+ value = "&locationEditField.label;"
+ accesskey="&locationEditField.accessKey;"
+ tooltiptext="&locationEditField.tooltip;"
+ />
+ <tooltip id="shortenedDataURI">
+ <label value="&locationEditField.shortenedDataURI;"/>
+ </tooltip>
+ <textbox id="srcInput" oninput="ChangeImageSrc();" tabindex="1" class="uri-element"
+ tooltiptext="&locationEditField.tooltip;"/>
+ <hbox id="MakeRelativeHbox">
+ <checkbox id="MakeRelativeCheckbox"
+ for="srcInput"
+ tabindex="2"
+ label="&makeUrlRelative.label;"
+ accesskey="&makeUrlRelative.accessKey;"
+ oncommand="MakeInputValueRelativeOrAbsolute(this);"
+ tooltiptext="&makeUrlRelative.tooltip;"/>
+ <checkbox id="AttachSourceToMail"
+ hidden="true"
+ label="&attachImageSource.label;"
+ accesskey="&attachImageSource.accesskey;"
+ oncommand="DoAttachSourceCheckbox()"/>
+ <spacer flex="1"/>
+ <button id="ChooseFile"
+ tabindex="3"
+ oncommand="chooseFile()"
+ label="&chooseFileButton.label;"
+ accesskey="&chooseFileButton.accessKey;"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <radiogroup id="altTextRadioGroup" flex="1">
+ <grid>
+ <columns><column/><column flex="1"/></columns>
+ <rows>
+ <row align="center">
+ <label
+ style = "margin-left: 26px"
+ control = "titleInput"
+ accesskey = "&title.accessKey;"
+ value ="&title.label;"
+ tooltiptext="&title.tooltip;"
+ for = "titleInput"/>
+ <textbox flex="1"
+ id = "titleInput"
+ class = "MinWidth20em"
+ tooltiptext="&title.tooltip;"
+ tabindex="4"/>
+ </row>
+ <row align="center">
+ <radio id="altTextRadio" value="usealt-yes"
+ label="&altText.label;"
+ accesskey="&altText.accessKey;"
+ tooltiptext="&altTextEditField.tooltip;"
+#ifndef MOZ_SUITE
+ persist="selected"
+#endif
+ oncommand = "SetAltTextDisabled(false);"
+ tabindex="5"/>
+ <textbox flex="1"
+ id = "altTextInput"
+ class = "MinWidth20em"
+ tooltiptext="&altTextEditField.tooltip;"
+ oninput = "SetAltTextDisabled(false);"
+ tabindex="6"/>
+ </row>
+ </rows>
+ </grid>
+
+ <radio id="noAltTextRadio" value="usealt-no"
+ label="&noAltText.label;"
+ accesskey = "&noAltText.accessKey;"
+#ifndef MOZ_SUITE
+ persist="selected"
+#endif
+ oncommand = "SetAltTextDisabled(true);"/>
+ </radiogroup>
+ </vbox>
+
+ <vbox id="imageDimensions" align="start">
+ <spacer class="spacer"/>
+ <hbox>
+ <radiogroup id="imgSizeGroup">
+ <radio
+ id = "actualSizeRadio"
+ label = "&actualSizeRadio.label;"
+ accesskey = "&actualSizeRadio.accessKey;"
+ tooltiptext="&actualSizeRadio.tooltip;"
+ oncommand = "SetActualSize()"
+ value="actual"/>
+ <radio
+ id = "customSizeRadio"
+ label = "&customSizeRadio.label;"
+ selected = "true"
+ accesskey = "&customSizeRadio.accessKey;"
+ tooltiptext="&customSizeRadio.tooltip;"
+ oncommand = "doDimensionEnabling();"
+ value="custom"/>
+ </radiogroup>
+ <spacer flex="1"/>
+ <vbox>
+ <spacer flex="1"/>
+ <checkbox id="constrainCheckbox" label="&constrainCheckbox.label;"
+ accesskey="&constrainCheckbox.accessKey;"
+ oncommand="ToggleConstrain()"
+ tooltiptext="&constrainCheckbox.tooltip;"/>
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <grid class="indent">
+ <columns><column/><column/><column flex="1"/></columns>
+ <rows>
+ <row align="center">
+ <label id = "widthLabel"
+ control = "widthInput"
+ accesskey = "&widthEditField.accessKey;"
+ value = "&widthEditField.label;" />
+ <textbox
+ id = "widthInput"
+ class = "narrow"
+ oninput = "constrainProportions(this.id, 'heightInput')"/>
+ <menulist id = "widthUnitsMenulist"
+ oncommand = "doDimensionEnabling();" />
+ <!-- contents are appended by JS -->
+ </row>
+ <row align="center">
+ <label id = "heightLabel"
+ control = "heightInput"
+ accesskey = "&heightEditField.accessKey;"
+ value = "&heightEditField.label;" />
+ <textbox
+ id = "heightInput"
+ class = "narrow"
+ oninput = "constrainProportions(this.id, 'widthInput')"/>
+ <menulist id = "heightUnitsMenulist"
+ oncommand = "doDimensionEnabling();" />
+ <!-- contents are appended by JS -->
+ </row>
+ </rows>
+ </grid>
+ <spacer flex="1"/>
+ </vbox>
+
+ <hbox id="imageAppearance">
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label id="spacingLabel" class="header">&spacingBox.label;</label>
+ </hbox>
+ <grid>
+ <columns><column/><column/><column/></columns>
+ <rows>
+ <row align="center">
+ <label
+ class = "align-right"
+ id = "leftrightLabel"
+ control = "imageleftrightInput"
+ accesskey = "&leftRightEditField.accessKey;"
+ value = "&leftRightEditField.label;"/>
+ <textbox
+ class = "narrow"
+ id = "imageleftrightInput"
+ oninput = "forceInteger(this.id)"/>
+ <label
+ id = "leftrighttypeLabel"
+ value = "&pixelsPopup.value;" />
+ </row>
+ <spacer class="spacer"/>
+ <row align="center">
+ <label
+ class = "align-right"
+ id = "topbottomLabel"
+ control = "imagetopbottomInput"
+ accesskey = "&topBottomEditField.accessKey;"
+ value = "&topBottomEditField.label;"/>
+ <textbox
+ class = "narrow"
+ id = "imagetopbottomInput"
+ oninput = "forceInteger(this.id)"/>
+ <label id="topbottomtypeLabel"
+ value="&pixelsPopup.value;" />
+ </row>
+ <spacer class="spacer"/>
+ <row align="center">
+ <label class="align-right"
+ id="borderLabel"
+ control="border"
+ accesskey="&borderEditField.accessKey;"
+ value="&borderEditField.label;"/>
+ <textbox
+ class = "narrow"
+ id = "border"
+ oninput = "forceInteger(this.id)"/>
+ <label id="bordertypeLabel"
+ value="&pixelsPopup.value;" />
+ </row>
+ </rows>
+ </grid>
+ </groupbox>
+
+ <vbox>
+ <groupbox align="start">
+ <hbox class="groupbox-title">
+ <label id="alignLabel" class="header">&alignment.label;</label>
+ </hbox>
+ <menulist id="alignTypeSelect" class="align-menu">
+ <menupopup>
+ <menuitem class="align-menu menuitem-iconic"
+ value="top"
+ label="&topPopup.value;"/>
+ <menuitem class="align-menu menuitem-iconic"
+ value="middle"
+ label="&centerPopup.value;"/>
+ <menuitem class="align-menu menuitem-iconic"
+ value="bottom"
+ label="&bottomPopup.value;"/>
+ <!-- HTML attribute value is opposite of the button label on purpose -->
+ <menuitem class="align-menu menuitem-iconic"
+ value="right"
+ label="&wrapLeftPopup.value;"/>
+ <menuitem class="align-menu menuitem-iconic"
+ value="left"
+ label="&wrapRightPopup.value;"/>
+ </menupopup>
+ </menulist>
+ </groupbox>
+
+ <groupbox>
+ <hbox class="groupbox-title">
+ <label id="imagemapLabel" class="header">&imagemapBox.label;</label>
+ </hbox>
+ <hbox equalsize="always">
+ <button id="removeImageMap"
+ oncommand="removeImageMap()"
+ accesskey="&removeImageMapButton.accessKey;"
+ label="&removeImageMapButton.label;"
+ flex="1"/>
+ <spacer flex="1"/><!-- remove when we restore Image Map Editor -->
+ </hbox>
+ </groupbox>
+ </vbox>
+ </hbox>
diff --git a/comm/suite/editor/components/dialogs/jar.mn b/comm/suite/editor/components/dialogs/jar.mn
new file mode 100644
index 0000000000..705fe0068c
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/jar.mn
@@ -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/.
+
+comm.jar:
+ content/editor/EdAdvancedEdit.js (content/EdAdvancedEdit.js)
+ content/editor/EdAdvancedEdit.xhtml (content/EdAdvancedEdit.xhtml)
+ content/editor/EdAEAttributes.js (content/EdAEAttributes.js)
+ content/editor/EdAECSSAttributes.js (content/EdAECSSAttributes.js)
+ content/editor/EdAEHTMLAttributes.js (content/EdAEHTMLAttributes.js)
+ content/editor/EdAEJSEAttributes.js (content/EdAEJSEAttributes.js)
+ content/editor/EdButtonProps.js (content/EdButtonProps.js)
+ content/editor/EdButtonProps.xhtml (content/EdButtonProps.xhtml)
+ content/editor/EdColorPicker.js (content/EdColorPicker.js)
+ content/editor/EdColorPicker.xhtml (content/EdColorPicker.xhtml)
+ content/editor/EdColorProps.js (content/EdColorProps.js)
+ content/editor/EdColorProps.xhtml (content/EdColorProps.xhtml)
+ content/editor/EdConvertToTable.js (content/EdConvertToTable.js)
+ content/editor/EdConvertToTable.xhtml (content/EdConvertToTable.xhtml)
+ content/editor/EdDialogCommon.js (content/EdDialogCommon.js)
+ content/editor/EdDialogTemplate.js (content/EdDialogTemplate.js)
+ content/editor/EdDialogTemplate.xhtml (content/EdDialogTemplate.xhtml)
+ content/editor/EdDictionary.js (content/EdDictionary.js)
+ content/editor/EdDictionary.xhtml (content/EdDictionary.xhtml)
+ content/editor/EdFieldSetProps.js (content/EdFieldSetProps.js)
+ content/editor/EdFieldSetProps.xhtml (content/EdFieldSetProps.xhtml)
+ content/editor/EdFormProps.js (content/EdFormProps.js)
+ content/editor/EdFormProps.xhtml (content/EdFormProps.xhtml)
+ content/editor/EdHLineProps.js (content/EdHLineProps.js)
+ content/editor/EdHLineProps.xhtml (content/EdHLineProps.xhtml)
+ content/editor/edImage.inc.xhtml (content/edImage.inc.xhtml)
+ content/editor/EdImageDialog.js (content/EdImageDialog.js)
+ content/editor/EdImageLinkLoader.js (content/EdImageLinkLoader.js)
+ content/editor/EdImageProps.js (content/EdImageProps.js)
+* content/editor/EdImageProps.xhtml (content/EdImageProps.xhtml)
+ content/editor/EdInputImage.js (content/EdInputImage.js)
+* content/editor/EdInputImage.xhtml (content/EdInputImage.xhtml)
+ content/editor/EdInputProps.js (content/EdInputProps.js)
+ content/editor/EdInputProps.xhtml (content/EdInputProps.xhtml)
+ content/editor/EdInsertChars.js (content/EdInsertChars.js)
+ content/editor/EdInsertChars.xhtml (content/EdInsertChars.xhtml)
+ content/editor/EdInsertMath.js (content/EdInsertMath.js)
+ content/editor/EdInsertMath.xhtml (content/EdInsertMath.xhtml)
+ content/editor/EdInsertTable.js (content/EdInsertTable.js)
+ content/editor/EdInsertTable.xhtml (content/EdInsertTable.xhtml)
+ content/editor/EdInsertTOC.js (content/EdInsertTOC.js)
+ content/editor/EdInsertTOC.xhtml (content/EdInsertTOC.xhtml)
+ content/editor/EdInsSrc.js (content/EdInsSrc.js)
+ content/editor/EdInsSrc.xhtml (content/EdInsSrc.xhtml)
+ content/editor/EditConflict.js (content/EditConflict.js)
+ content/editor/EditConflict.xhtml (content/EditConflict.xhtml)
+ content/editor/EditorPublish.js (content/EditorPublish.js)
+ content/editor/EditorPublish.xhtml (content/EditorPublish.xhtml)
+ content/editor/EditorPublishOverlay.xhtml (content/EditorPublishOverlay.xhtml)
+ content/editor/EditorPublishProgress.js (content/EditorPublishProgress.js)
+ content/editor/EditorPublishProgress.xhtml (content/EditorPublishProgress.xhtml)
+ content/editor/EditorPublishSettings.js (content/EditorPublishSettings.js)
+ content/editor/EditorPublishSettings.xhtml (content/EditorPublishSettings.xhtml)
+ content/editor/EditorSaveAsCharset.js (content/EditorSaveAsCharset.js)
+ content/editor/EditorSaveAsCharset.xhtml (content/EditorSaveAsCharset.xhtml)
+ content/editor/EdLabelProps.js (content/EdLabelProps.js)
+ content/editor/EdLabelProps.xhtml (content/EdLabelProps.xhtml)
+ content/editor/EdLinkProps.js (content/EdLinkProps.js)
+ content/editor/EdLinkProps.xhtml (content/EdLinkProps.xhtml)
+ content/editor/EdListProps.js (content/EdListProps.js)
+ content/editor/EdListProps.xhtml (content/EdListProps.xhtml)
+ content/editor/EdNamedAnchorProps.js (content/EdNamedAnchorProps.js)
+ content/editor/EdNamedAnchorProps.xhtml (content/EdNamedAnchorProps.xhtml)
+ content/editor/EdPageProps.js (content/EdPageProps.js)
+ content/editor/EdPageProps.xhtml (content/EdPageProps.xhtml)
+ content/editor/EdReplace.js (content/EdReplace.js)
+ content/editor/EdReplace.xhtml (content/EdReplace.xhtml)
+ content/editor/EdSelectProps.js (content/EdSelectProps.js)
+ content/editor/EdSelectProps.xhtml (content/EdSelectProps.xhtml)
+ content/editor/EdSnapToGrid.js (content/EdSnapToGrid.js)
+ content/editor/EdSnapToGrid.xhtml (content/EdSnapToGrid.xhtml)
+ content/editor/EdSpellCheck.js (content/EdSpellCheck.js)
+ content/editor/EdSpellCheck.xhtml (content/EdSpellCheck.xhtml)
+ content/editor/EdTableProps.js (content/EdTableProps.js)
+ content/editor/EdTableProps.xhtml (content/EdTableProps.xhtml)
+ content/editor/EdTextAreaProps.js (content/EdTextAreaProps.js)
+ content/editor/EdTextAreaProps.xhtml (content/EdTextAreaProps.xhtml)
diff --git a/comm/suite/editor/components/dialogs/moz.build b/comm/suite/editor/components/dialogs/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/editor/components/dialogs/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/editor/components/moz.build b/comm/suite/editor/components/moz.build
new file mode 100644
index 0000000000..cc5a5d1960
--- /dev/null
+++ b/comm/suite/editor/components/moz.build
@@ -0,0 +1,10 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ "dialogs",
+ "prefs",
+ "texzilla",
+]
diff --git a/comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml b/comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml
new file mode 100644
index 0000000000..41112036ca
--- /dev/null
+++ b/comm/suite/editor/components/prefs/content/editorPrefsOverlay.xhtml
@@ -0,0 +1,50 @@
+<?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 % editorPrefsOverlayDTD SYSTEM "chrome://editor/locale/editorPrefsOverlay.dtd" >
+%editorPrefsOverlayDTD;
+]>
+
+<overlay id="editorPrefsOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <preferences id="appearance_preferences">
+ <preference id="general.startup.editor"
+ name="general.startup.editor"
+ type="bool"/>
+ </preferences>
+
+ <!-- editor startup toggle -->
+ <groupbox id="generalStartupPreferences">
+ <checkbox id="generalStartupEditor"
+ insertafter="generalStartupMail,generalStartupBrowser"
+ label="&editorCheck.label;"
+ accesskey="&editorCheck.accesskey;"
+ preference="general.startup.editor"/>
+ </groupbox>
+ <!-- category tree entries for editor -->
+ <treechildren id="prefsPanelChildren">
+ <treeitem container="true"
+ id="composerItem"
+ insertbefore="securityItem"
+ label="&compose.label;"
+ prefpane="composer_pane"
+ url="chrome://editor/content/pref-composer.xhtml"
+ helpTopic="composer_prefs_general">
+ <treechildren id="composerChildren">
+ <treeitem id="editingItem"
+ label="&editing.label;"
+ prefpane="editing_pane"
+ url="chrome://editor/content/pref-editing.xhtml"
+ helpTopic="composer_prefs_newpage"/>
+ </treechildren>
+ </treeitem>
+ </treechildren>
+
+</overlay>
diff --git a/comm/suite/editor/components/prefs/content/pref-composer.xhtml b/comm/suite/editor/components/prefs/content/pref-composer.xhtml
new file mode 100644
index 0000000000..79b4a1514c
--- /dev/null
+++ b/comm/suite/editor/components/prefs/content/pref-composer.xhtml
@@ -0,0 +1,84 @@
+<?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 SYSTEM "chrome://editor/locale/pref-composer.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+ <prefpane id="composer_pane" label="&pref.composer.title;">
+
+ <preferences id="composer_preferences">
+ <preference id="editor.history.url_maximum"
+ name="editor.history.url_maximum"
+ type="int"/>
+ <preference id="editor.prettyprint"
+ name="editor.prettyprint"
+ type="bool"
+ inverted="true"/>
+ <preference id="editor.save_associated_files"
+ name="editor.save_associated_files"
+ type="bool"/>
+ <preference id="editor.always_show_publish_dialog"
+ name="editor.always_show_publish_dialog"
+ type="bool"/>
+ <preference id="editor.table.maintain_structure"
+ name="editor.table.maintain_structure"
+ type="bool"/>
+ <preference id="editor.use_css"
+ name="editor.use_css"
+ type="bool"/>
+ <preference id="editor.CR_creates_new_p"
+ name="editor.CR_creates_new_p"
+ type="bool"/>
+ </preferences>
+
+ <!-- Recent files menu -->
+ <groupbox>
+ <label class="header">&recentFiles.title;</label>
+ <hbox align="center">
+ <label value="&documentsInMenu.label;"
+ accesskey="&documentsInMenu.accesskey;"
+ control="recentFiles"/>
+ <html:input id="recentFiles" type="number" class="size3"
+ min="0" max="99" value="10"
+ preference="editor.history.url_maximum"/>
+ </hbox>
+ </groupbox>
+
+ <!-- HTML formatting on output -->
+ <groupbox>
+ <label class="header">&savingFiles.title;</label>
+ <checkbox id="preserveFormatting"
+ label="&preserveExisting.label;"
+ accesskey="&preserveExisting.accesskey;"
+ tooltiptext="&preserveExisting.tooltip;"
+ preference="editor.prettyprint"/>
+ <checkbox id="saveAssociatedFiles"
+ label="&saveAssociatedFiles.label;"
+ accesskey="&saveAssociatedFiles.accesskey;"
+ preference="editor.save_associated_files"/>
+ <checkbox id="showPublishDialog"
+ label="&showPublishDialog.label;"
+ accesskey="&showPublishDialog.accesskey;"
+ preference="editor.always_show_publish_dialog"/>
+ </groupbox>
+
+ <groupbox align="start">
+ <label class="header">&composerEditing.label;</label>
+ <checkbox id="maintainTableStructure"
+ label="&maintainStructure.label;"
+ accesskey="&maintainStructure.accesskey;"
+ tooltiptext="&maintainStructure.tooltip;"
+ preference="editor.table.maintain_structure"/>
+ <checkbox id="useCSS"
+ label="&useCSS.label;"
+ accesskey="&useCSS.accesskey;"
+ preference="editor.use_css"/>
+ <checkbox id="crInPCreatesNewP"
+ label="&crInPCreatesNewP.label;"
+ accesskey="&crInPCreatesNewP.accesskey;"
+ preference="editor.CR_creates_new_p"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/editor/components/prefs/content/pref-editing.js b/comm/suite/editor/components/prefs/content/pref-editing.js
new file mode 100644
index 0000000000..61f4c1edef
--- /dev/null
+++ b/comm/suite/editor/components/prefs/content/pref-editing.js
@@ -0,0 +1,187 @@
+/* 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 browserPrefsObserver =
+{
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != "nsPref:changed" || document.getElementById("editor.use_custom_colors").value)
+ return;
+
+ switch (aData)
+ {
+ case "browser.anchor_color":
+ SetColorPreview("linkText", Services.prefs.getCharPref(aData));
+ break;
+ case "browser.active_color":
+ SetColorPreview("activeLinkText", Services.prefs.getCharPref(aData));
+ break;
+ case "browser.visited_color":
+ SetColorPreview("visitedLinkText", Services.prefs.getCharPref(aData));
+ break;
+ default:
+ SetBgAndFgColors(Services.prefs.getBoolPref("browser.display.use_system_colors"))
+ }
+ }
+};
+
+function Startup()
+{
+ // Add browser prefs observers
+ Services.prefs.addObserver("browser.display.use_system_colors", browserPrefsObserver);
+ Services.prefs.addObserver("browser.display.foreground_color", browserPrefsObserver);
+ Services.prefs.addObserver("browser.display.background_color", browserPrefsObserver);
+ Services.prefs.addObserver("browser.anchor_color", browserPrefsObserver);
+ Services.prefs.addObserver("browser.active_color", browserPrefsObserver);
+ Services.prefs.addObserver("browser.visited_color", browserPrefsObserver);
+
+ // Add event listener so we can remove our observers
+ window.addEventListener("unload", WindowOnUnload, {capture: false, once: true});
+ UpdateDependent(document.getElementById("editor.use_custom_colors").value);
+}
+
+function GetColorAndUpdatePref(aType, aButtonID)
+{
+ // Don't allow a blank color, i.e., using the "default"
+ var colorObj = { NoDefault:true, Type:"", TextColor:0, PageColor:0, Cancel:false };
+ var preference = document.getElementById("editor." + aButtonID + "_color");
+
+ if (aButtonID == "background")
+ colorObj.PageColor = preference.value;
+ else
+ colorObj.TextColor = preference.value;
+
+ colorObj.Type = aType;
+
+ window.openDialog("chrome://editor/content/EdColorPicker.xhtml", "_blank", "chrome,close,titlebar,modal", "", colorObj);
+
+ // User canceled the dialog
+ if (colorObj.Cancel)
+ return;
+
+ // Update preference with picked color
+ if (aType == "Page")
+ preference.value = colorObj.BackgroundColor;
+ else
+ preference.value = colorObj.TextColor;
+}
+
+function UpdateDependent(aCustomEnabled)
+{
+ ToggleElements(aCustomEnabled);
+
+ if (aCustomEnabled)
+ { // Set current editor colors on preview and buttons
+ SetColors("textCW", "normalText", document.getElementById("editor.text_color").value);
+ SetColors("linkCW", "linkText", document.getElementById("editor.link_color").value);
+ SetColors("activeCW", "activeLinkText", document.getElementById("editor.active_link_color").value);
+ SetColors("visitedCW", "visitedLinkText", document.getElementById("editor.followed_link_color").value);
+ SetColors("backgroundCW", "ColorPreview", document.getElementById("editor.background_color").value);
+ }
+ else
+ { // Set current browser colors on preview
+ SetBgAndFgColors(Services.prefs.getBoolPref("browser.display.use_system_colors"));
+ SetColorPreview("linkText", Services.prefs.getCharPref("browser.anchor_color"));
+ SetColorPreview("activeLinkText", Services.prefs.getCharPref("browser.active_color"));
+ SetColorPreview("visitedLinkText", Services.prefs.getCharPref("browser.visited_color"));
+ }
+}
+
+function ToggleElements(aCustomEnabled)
+{
+ var buttons = document.getElementById("color-rows").getElementsByTagName("button");
+
+ for (var i = 0; i < buttons.length; i++)
+ {
+ let isLocked = CheckLocked(buttons[i].id);
+ buttons[i].disabled = !aCustomEnabled || isLocked;
+ buttons[i].previousSibling.disabled = !aCustomEnabled || isLocked;
+ buttons[i].firstChild.setAttribute("default", !aCustomEnabled || isLocked);
+ }
+}
+
+function CheckLocked(aButtonID)
+{
+ return document.getElementById("editor." + aButtonID + "_color").locked;
+}
+
+// Updates preview and button color when a editor color pref change
+function UpdateColors(aColorWellID, aPreviewID, aColor)
+{
+ // Only show editor colors from prefs if we're in custom mode
+ if (!document.getElementById("editor.use_custom_colors").value)
+ return;
+
+ SetColors(aColorWellID, aPreviewID, aColor)
+}
+
+function SetColors(aColorWellID, aPreviewID, aColor)
+{
+ SetColorWell(aColorWellID, aColor);
+ SetColorPreview(aPreviewID, aColor);
+}
+
+function SetColorWell(aColorWellID, aColor)
+{
+ document.getElementById(aColorWellID).style.backgroundColor = aColor;
+}
+
+function SetColorPreview(aPreviewID, aColor)
+{
+ if (aPreviewID == "ColorPreview")
+ document.getElementById(aPreviewID).style.backgroundColor = aColor;
+ else
+ document.getElementById(aPreviewID).style.color = aColor;
+}
+
+function UpdateBgImagePreview(aImage)
+{
+ var colorPreview = document.getElementById("ColorPreview");
+ colorPreview.style.backgroundImage = aImage && "url(" + aImage + ")";
+}
+
+// Sets browser background/foreground colors
+function SetBgAndFgColors(aSysPrefEnabled)
+{
+ if (aSysPrefEnabled)
+ { // Use system colors
+ SetColorPreview("normalText", "windowtext");
+ SetColorPreview("ColorPreview", "window");
+ }
+ else
+ {
+ SetColorPreview("normalText", Services.prefs.getCharPref("browser.display.foreground_color"));
+ SetColorPreview("ColorPreview", Services.prefs.getCharPref("browser.display.background_color"));
+ }
+}
+
+function ChooseImageFile()
+{
+ const nsIFilePicker = Ci.nsIFilePicker;
+ var fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(nsIFilePicker);
+ var editorBundle = document.getElementById("bundle_editor");
+ var title = editorBundle.getString("SelectImageFile");
+ fp.init(window, title, nsIFilePicker.modeOpen);
+ fp.appendFilters(nsIFilePicker.filterImages);
+ fp.open(rv => {
+ if (rv != nsIFilePicker.returnOK || !fp.file) {
+ return;
+ }
+ document.getElementById("editor.default_background_image").value = fp.fileURL.spec;
+ let textbox = document.getElementById("backgroundImageInput");
+ textbox.focus();
+ textbox.select();
+ });
+}
+
+function WindowOnUnload()
+{
+ Services.prefs.removeObserver("browser.display.use_system_colors", browserPrefsObserver, false);
+ Services.prefs.removeObserver("browser.display.foreground_color", browserPrefsObserver, false);
+ Services.prefs.removeObserver("browser.display.background_color", browserPrefsObserver, false);
+ Services.prefs.removeObserver("browser.anchor_color", browserPrefsObserver, false);
+ Services.prefs.removeObserver("browser.active_color", browserPrefsObserver, false);
+ Services.prefs.removeObserver("browser.visited_color", browserPrefsObserver, false);
+}
diff --git a/comm/suite/editor/components/prefs/content/pref-editing.xhtml b/comm/suite/editor/components/prefs/content/pref-editing.xhtml
new file mode 100644
index 0000000000..140ff320ce
--- /dev/null
+++ b/comm/suite/editor/components/prefs/content/pref-editing.xhtml
@@ -0,0 +1,181 @@
+<?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://editor/skin/EditorDialog.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://editor/locale/pref-editing.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+ <prefpane id="editing_pane"
+ label="&pref.editing.title;"
+ script="chrome://editor/content/pref-editing.js">
+
+ <preferences id="editing_preferences">
+ <preference id="editor.author"
+ name="editor.author"
+ type="string"/>
+ <preference id="editor.use_custom_colors"
+ name="editor.use_custom_colors"
+ type="bool"
+ onchange="UpdateDependent(this.value);"/>
+ <preference id="editor.text_color"
+ name="editor.text_color"
+ type="string"
+ onchange="UpdateColors('textCW', 'normalText', this.value);"/>
+ <preference id="editor.link_color"
+ name="editor.link_color"
+ type="string"
+ onchange="UpdateColors('linkCW', 'linkText', this.value);"/>
+ <preference id="editor.active_link_color"
+ name="editor.active_link_color"
+ type="string"
+ onchange="UpdateColors('activeCW', 'activeLinkText', this.value);"/>
+ <preference id="editor.followed_link_color"
+ name="editor.followed_link_color"
+ type="string"
+ onchange="UpdateColors('visitedCW', 'visitedLinkText', this.value);"/>
+ <preference id="editor.background_color"
+ name="editor.background_color"
+ type="string"
+ onchange="UpdateColors('backgroundCW', 'ColorPreview', this.value);"/>
+ <preference id="editor.default_background_image"
+ name="editor.default_background_image"
+ type="string"
+ onchange="UpdateBgImagePreview(this.value);"/>
+ </preferences>
+
+ <stringbundle id="bundle_editor"
+ src="chrome://editor/locale/editor.properties"/>
+
+ <vbox>
+ <label value="&authorName.label;"
+ accesskey="&authorName.accesskey;"
+ control="editorAuthor">
+ </label>
+ <hbox>
+ <textbox id="editorAuthor"
+ flex="1"
+ preference="editor.author"/>
+ <spacer flex="1"/>
+ </hbox>
+ </vbox>
+ <spacer class="smallspacer"/>
+ <groupbox align="start">
+ <label class="header">&pageColorHeader;</label>
+ <radiogroup id="useCustomColors"
+ preference="editor.use_custom_colors">
+ <radio id="defaultColorsRadio"
+ value="false"
+ label="&defaultColors.label;"
+ accesskey="&defaultColors.accesskey;"/>
+ <radio id="customColorsRadio"
+ value="true"
+ label="&customColors.label;"
+ accesskey="&customColors.accesskey;"/>
+ </radiogroup>
+ <hbox class="indent">
+ <grid>
+ <columns><column/><column/></columns>
+ <rows id="color-rows">
+ <row align="center">
+ <label id="textLabel"
+ value="&normalText.label;&colon.character;"
+ accesskey="&normalText.accesskey;"
+ control="text"/>
+ <button id="text"
+ class="color-button"
+ oncommand="GetColorAndUpdatePref('Text', 'text');">
+ <spacer id="textCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="linkLabel"
+ value="&linkText.label;&colon.character;"
+ accesskey="&linkText.accesskey;"
+ control="link"/>
+ <button id="link"
+ class="color-button"
+ oncommand="GetColorAndUpdatePref('Link', 'link');">
+ <spacer id="linkCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="activeLinkLabel"
+ value="&activeLinkText.label;&colon.character;"
+ accesskey="&activeLinkText.accesskey;"
+ control="active_link"/>
+ <button id="active_link"
+ class="color-button"
+ oncommand="GetColorAndUpdatePref('ActiveLink', 'active_link');">
+ <spacer id="activeCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="visitedLinkLabel"
+ value ="&visitedLinkText.label;&colon.character;"
+ accesskey="&visitedLinkText.accesskey;"
+ control="followed_link"/>
+ <button id="followed_link"
+ class="color-button"
+ oncommand="GetColorAndUpdatePref('VisitedLink', 'followed_link');">
+ <spacer id="visitedCW" class="color-well"/>
+ </button>
+ </row>
+ <row align="center">
+ <label id="backgroundLabel"
+ value="&background.label;"
+ accesskey="&background.accesskey;"
+ control="background"/>
+ <button id="background"
+ class="color-button"
+ oncommand="GetColorAndUpdatePref('Page', 'background');">
+ <spacer id="backgroundCW" class="color-well"/>
+ </button>
+ </row>
+ </rows>
+ </grid>
+ <vbox id="ColorPreview"
+ flex="1">
+ <spacer flex="1"/>
+ <label id="normalText"
+ class="larger"
+ value="&normalText.label;"/>
+ <spacer flex="1"/>
+ <label id="linkText"
+ class="larger"
+ value="&linkText.label;"/>
+ <spacer flex="1"/>
+ <label id="activeLinkText"
+ class="larger"
+ value="&activeLinkText.label;"/>
+ <spacer flex="1"/>
+ <label id="visitedLinkText"
+ class="larger"
+ value="&visitedLinkText.label;"/>
+ <spacer flex="1"/>
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+ <spacer class="spacer"/>
+ <label id="backgroundImageLabel"
+ value="&backgroundImage.label;"
+ accesskey="&backgroundImage.accesskey;"
+ control="backgroundImageInput">
+ </label>
+ <hbox align="center">
+ <textbox id="backgroundImageInput"
+ class="uri-element"
+ preference="editor.default_background_image"
+ style="min-width: 23em;"
+ flex="1"/>
+ <button label="&chooseFile.label;"
+ accesskey="&chooseFile.accesskey;"
+ oncommand="ChooseImageFile();">
+ <observes element="backgroundImageInput" attribute="disabled"/>
+ </button>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/editor/components/prefs/jar.mn b/comm/suite/editor/components/prefs/jar.mn
new file mode 100644
index 0000000000..308da7340c
--- /dev/null
+++ b/comm/suite/editor/components/prefs/jar.mn
@@ -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/.
+
+comm.jar:
+% overlay chrome://communicator/content/pref/preferences.xhtml chrome://editor/content/editorPrefsOverlay.xhtml
+% overlay chrome://communicator/content/pref/pref-appearance.xhtml chrome://editor/content/editorPrefsOverlay.xhtml
+ content/editor/editorPrefsOverlay.xhtml (content/editorPrefsOverlay.xhtml)
+ content/editor/pref-composer.xhtml (content/pref-composer.xhtml)
+ content/editor/pref-editing.js (content/pref-editing.js)
+ content/editor/pref-editing.xhtml (content/pref-editing.xhtml)
diff --git a/comm/suite/editor/components/prefs/moz.build b/comm/suite/editor/components/prefs/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/editor/components/prefs/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/editor/components/texzilla/content/TeXZilla.js b/comm/suite/editor/components/texzilla/content/TeXZilla.js
new file mode 100644
index 0000000000..0f8e3e5b29
--- /dev/null
+++ b/comm/suite/editor/components/texzilla/content/TeXZilla.js
@@ -0,0 +1,339 @@
+/* THIS IS A GENERATED FILE. DO NOT EDIT THIS DIRECTLY. */
+/* 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/. */
+
+(function() {
+"using strict";
+var nb=void 0,tb=!0,xb=null,yb=!1,zb=function(){function c(b,a,c){var $a;c=c||{};for($a=b.length;$a--;c[b[$a]]=a);return c}function Fb(b){return b.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function sb(b){b="negativeveryverythinmathspace negativeverythinmathspace negativemediummathspace negativethickmathspace negativeverythickmathspace negativeveryverythickmathspace veryverythinmathspace verythinmathspace thinmathspace mediummathspace thickmathspace verythickmathspace veryverythickmathspace".split(" ").indexOf(b);
+return(-1===b?0:b-6)/18}function tc(b){b=b.trim();var a=/(-?[0-9]*(?:[0-9]\.?|\.[0-9])[0-9]*)(e[mx]|in|cm|mm|p[xtc]|%)?/.exec(b);return a?(a[1]=parseFloat(a[1]),a[2]||(a[1]*=100,a[2]="%"),{i:a[1],k:a[2]}):{i:sb(b),k:"em"}}function Pb(b){var a="<"+b.tag,c;for(c in b.attributes)b.attributes[c]!==nb&&(a+=" "+c+'="'+b.attributes[c]+'"');b.content?(a+=">",Array.isArray(b.content)?b.content.forEach(function(b){a+=Pb(b)}):a+=b.content,a+="</"+b.tag+">"):a+="/>";return a}function e(b,a,c){return{tag:b,content:a,
+attributes:c}}function ab(b,a,c){return e("mo",Fb(b),{lspace:a!==nb?a+"em":nb,rspace:c!==nb?c+"em":nb})}function Ub(b,a){return e("mi",Fb(b),a?{mathvariant:"normal"}:nb)}function Gb(b){return e("mspace",xb,{width:b+"em"})}function uc(b,a){var c="bold italic bold-italic script bold-script fraktur double-struck bold-fraktur sans-serif bold-sans-serif sans-serif-italic sans-serif-bold-italic monospace initial tailed looped stretched".split(" ").indexOf(a);if(930==b)return b;if(988==b)return 0==c?120778:
+b;if(989==b)return 0==c?120779:b;if(305==b)return 1==c?120484:b;if(567==b)return 1==c?120485:b;var $a;if(65<=b&&90>=b||97<=b&&122>=b){if(12<c)return b;c=(90>=b?b-65:26+b-97)+119808+52*c;$a={119893:8462,119965:8492,119968:8496,119969:8497,119971:8459,119972:8464,119975:8466,119976:8499,119981:8475,119994:8495,119996:8458,120004:8500,120070:8493,120075:8460,120076:8465,120085:8476,120093:8488,120122:8450,120127:8461,120133:8469,120135:8473,120136:8474,120137:8477,120145:8484};return $a[c]?$a[c]:c}if(48<=
+b&&57>=b){switch(c){case 0:c=0;break;case 6:c=1;break;case 8:c=2;break;case 9:c=3;break;case 12:c=4;break;default:return b}return b-48+10*c+120782}if(1536<=b&&1791>=b){switch(c){case 13:$a={1576:126497,1578:126517,1579:126518,1580:126498,1581:126503,1582:126519,1587:126510,1588:126516,1589:126513,1590:126521,1593:126511,1594:126523,1601:126512,1602:126514,1603:126506,1604:126507,1605:126508,1606:126509,1607:126500,1610:126505};break;case 14:$a={1580:126530,1581:126535,1582:126551,1587:126542,1588:126548,
+1589:126545,1590:126553,1593:126543,1594:126555,1602:126546,1604:126539,1606:126541,1610:126537,1647:126559,1722:126557};break;case 16:$a={1576:126561,1578:126581,1579:126582,1580:126562,1581:126567,1582:126583,1587:126574,1588:126580,1589:126577,1590:126585,1591:126568,1592:126586,1593:126575,1594:126587,1601:126576,1602:126578,1603:126570,1605:126572,1606:126573,1607:126564,1610:126569,1646:126588,1697:126590};break;case 15:$a={1575:126592,1576:126593,1578:126613,1579:126614,1580:126594,1581:126599,
+1582:126615,1583:126595,1584:126616,1585:126611,1586:126598,1587:126606,1588:126612,1589:126609,1590:126617,1591:126600,1592:126618,1593:126607,1594:126619,1601:126608,1602:126610,1604:126603,1605:126604,1606:126605,1607:126596,1608:126597,1610:126601};break;case 6:$a={1576:126625,1578:126645,1579:126646,1580:126626,1581:126631,1582:126647,1583:126627,1584:126648,1585:126643,1586:126630,1587:126638,1588:126644,1589:126641,1590:126649,1591:126632,1592:126650,1593:126639,1594:126651,1601:126640,1602:126642,
+1604:126635,1605:126636,1606:126637,1608:126629,1610:126633};break;default:return b}return $a[b]?$a[b]:b}if(913<=b&&937>=b)$a=b-913;else if(945<=b&&969>=b)$a=26+b-945;else switch(b){case 1012:$a=17;break;case 8711:$a=25;break;case 8706:$a=51;break;case 1013:$a=52;break;case 977:$a=53;break;case 1008:$a=54;break;case 981:$a=55;break;case 1009:$a=56;break;case 982:$a=57;break;default:return b}switch(c){case 0:c=0;break;case 1:c=1;break;case 2:c=2;break;case 9:c=3;break;case 11:c=4;break;default:return b}return $a+
+120488+58*c}function vc(b,a){var c=tb,$a;for($a in a)-1!==["mathcolor","mathbackground","mathvariant"].indexOf($a)?"mathvariant"!==$a&&1!=b.length?c=yb:b.forEach(function(b){if(-1!==["mi","mn","mo","mtext","ms"].indexOf(b.tag)){if(b.attributes||(b.attributes={}),!b.attributes[$a])if("mathvariant"===$a){var d;if(!(d="normal"!==a[$a])){if("mi"!==b.tag)d=yb;else{d=b.content;var e=d.codePointAt(0);d=1===d.length&&65535>=e||2===d.length&&65535<e}d=!d}if(d){if(d=a[$a],"normal"!==d){for(var e=b.content,
+g="",m=0;m<e.length;m++){var s=e.codePointAt(m);65535<s?(g+=e[m],m++,g+=e[m]):g+=String.fromCodePoint(uc(s,d))}b.content=g}}else b.attributes[$a]=a[$a]}else b.attributes[$a]=a[$a]}else c=yb}):c=yb;return c}function db(b,a,c){a=a||"mrow";if("mstyle"===a){if(1==b.length&&"mrow"===b[0].tag&&!b[0].attributes)return db(b[0].content,a,c);if(vc(b,c))return db(b)}return 1==b.length&&"mrow"===a&&!c?b[0]:e(a,b,c)}function Ib(b,a,c,$a){return e("math",[e("semantics",[db(b),e("annotation",Fb($a),{encoding:"TeX"})])],
+{xmlns:Qb,display:a?"block":nb,dir:c?"rtl":nb})}function Vb(b){if(!b||b.namespaceURI!==Qb)return xb;if("semantics"===b.tagName)for(b=b.firstElementChild;b;b=b.nextElementSibling){if(b.namespaceURI===Qb&&"annotation"===b.localName&&-1!==wc.indexOf(b.getAttribute("encoding")))return b.textContent}else if(1===b.childElementCount)return Vb(b.firstElementChild);return xb}function xc(b){for(var a="",c,$a,e=0;e<b.length;e++)c=b.charCodeAt(e),128>c?a+=b.charAt(e):55296<=c&&56319>=c?(e++,$a=b.charCodeAt(e),
+a+="&#x"+(1024*(c-55296)+$a-56320+65536).toString(16)+";"):a+="&#x"+c.toString(16)+";";return a}function Rb(){this.e={}}var Wb=[1,4],Xb=[1,6],Yb=[1,7],Zb=[1,8],$b=[1,9],Ab=[68,195,198,200,202,204],m=[1,27],s=[1,124],v=[1,52],x=[1,48],h=[1,28],q=[1,29],p=[1,30],y=[1,31],f=[1,32],u=[1,33],n=[1,34],k=[1,35],r=[1,37],t=[1,38],l=[1,39],w=[1,40],z=[1,41],A=[1,42],B=[1,43],C=[1,44],D=[1,45],E=[1,46],F=[1,47],G=[1,49],H=[1,50],I=[1,51],J=[1,53],K=[1,54],L=[1,55],M=[1,56],N=[1,57],O=[1,58],P=[1,59],Q=[1,60],
+R=[1,61],S=[1,62],T=[1,63],U=[1,64],V=[1,65],W=[1,66],X=[1,67],Y=[1,68],Z=[1,69],$=[1,70],aa=[1,71],ba=[1,72],ca=[1,73],da=[1,74],ea=[1,75],fa=[1,76],ga=[1,77],ha=[1,78],ia=[1,79],ja=[1,80],ka=[1,81],la=[1,82],ma=[1,83],na=[1,84],oa=[1,85],pa=[1,86],qa=[1,87],ra=[1,88],sa=[1,89],ta=[1,90],ua=[1,91],va=[1,92],wa=[1,93],xa=[1,94],ya=[1,95],za=[1,96],Aa=[1,97],Ba=[1,98],Ca=[1,99],Da=[1,100],Ea=[1,101],Fa=[1,102],Ga=[1,103],Ha=[1,104],Ia=[1,105],Ja=[1,106],Ka=[1,107],eb=[1,24],La=[1,108],Ma=[1,109],Na=
+[1,110],Oa=[1,111],Pa=[1,112],Qa=[1,113],Ra=[1,114],Sa=[1,115],Ta=[1,116],Ua=[1,117],Va=[1,118],Wa=[1,119],Xa=[1,120],Ya=[1,121],bb=[1,122],cb=[1,123],fb=[1,16],gb=[1,17],hb=[1,18],ib=[1,19],jb=[1,20],kb=[1,21],lb=[1,22],ac=[6,10,53,64,65,66,144,146,148,150,152,154,156,158,160,162,164,189,192,199,201,203,205],Db=[8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,
+114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,145,147,149,151,153,155,157,159,161,163,165,166,173,174,179,180,181,182,183,184,185],mb=[1,134],ub=[6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,
+141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,173,174,189,192,199,201,203,205],Za=[1,137],g=[6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,141,142,144,145,146,147,148,149,150,151,152,153,154,
+155,156,157,158,159,160,161,162,163,164,165,166,169,170,171,173,174,189,192,199,201,203,205],Sb=[1,161],pb=[2,197],qb=[1,217],vb=[1,214],Eb=[6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,
+157,158,159,160,161,162,163,164,165,166,169,170,173,174,189,192,199,201,203,205],bc=[1,241],Bb=[1,243],Cb=[1,244],Mb=[1,259],cc=[4,8],dc=[1,275],ec=[8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,141,142,145,147,149,151,153,155,157,159,161,163,165,166],wb=[1,286],
+Nb=[10,144,146,148,150,152,154,156,158,160,162,164,192],fc=[1,288],Jb=[10,144,146,148,150,152,154,156,158,160,162,164,189,192],gc=[164,189,192],Tb=[10,189,192],Kb=[1,343],Lb=[1,344],hc=[1,352],ic=[1,353],jc=[4,8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,
+145,147,149,151,153,155,157,159,161,163,165,166],Ob=[10,21,23],Hb=[10,21,23,25,27],kc=[1,399],lc=[1,400],mc=[1,401],nc=[1,402],oc=[1,403],pc=[1,404],qc=[1,405],rc=[1,406],sc=[10,19,21,23,25,27,29,31,33,35,37,39,41],ob=[10,19,21,23,29,31,33,35,37,39,41],rb={trace:function(){},e:{},la:{error:2,textOptArg:3,"[":4,TEXTOPTARG:5,"]":6,textArg:7,"{":8,TEXTARG:9,"}":10,lengthOptArg:11,lengthArg:12,attrOptArg:13,attrArg:14,tokenContent:15,arrayAlign:16,columnAlign:17,collayout:18,COLLAYOUT:19,colalign:20,
+COLALIGN:21,rowalign:22,ROWALIGN:23,rowspan:24,ROWSPAN:25,colspan:26,COLSPAN:27,align:28,ALIGN:29,eqrows:30,EQROWS:31,eqcols:32,EQCOLS:33,rowlines:34,ROWLINES:35,collines:36,COLLINES:37,frame:38,FRAME:39,padding:40,PADDING:41,cellopt:42,celloptList:43,rowopt:44,arrayopt:45,arrayoptList:46,rowoptList:47,left:48,LEFT:49,OPFS:50,".":51,right:52,RIGHT:53,closedTerm:54,styledExpression:55,BIG:56,BBIG:57,BIGG:58,BBIGG:59,BIGL:60,BBIGL:61,BIGGL:62,BBIGGL:63,TEXATOP:64,TEXOVER:65,TEXCHOOSE:66,NUM:67,TEXT:68,
+A:69,AILL:70,AIUL:71,AILG:72,AIUG:73,F:74,MI:75,MN:76,MO:77,OP:78,OPS:79,OPAS:80,MS:81,MTEXT:82,HIGH_SURROGATE:83,LOW_SURROGATE:84,BMP_CHARACTER:85,OPERATORNAME:86,MATHOP:87,MATHBIN:88,MATHREL:89,FRAC:90,ROOT:91,SQRT:92,UNDERSET:93,OVERSET:94,UNDEROVERSET:95,XARROW:96,MATHRLAP:97,MATHLLAP:98,MATHCLAP:99,PHANTOM:100,TFRAC:101,BINOM:102,TBINOM:103,PMOD:104,UNDERBRACE:105,UNDERLINE:106,OVERBRACE:107,ACCENT:108,ACCENTNS:109,BOXED:110,SLASH:111,QUAD:112,QQUAD:113,NEGSPACE:114,NEGMEDSPACE:115,NEGTHICKSPACE:116,
+THINSPACE:117,MEDSPACE:118,THICKSPACE:119,SPACE:120,MATHRAISEBOX:121,MATHBB:122,MATHBF:123,MATHBIT:124,MATHSCR:125,MATHBSCR:126,MATHSF:127,MATHFRAK:128,MATHIT:129,MATHTT:130,MATHRM:131,HREF:132,STATUSLINE:133,TOOLTIP:134,TOGGLE:135,BTOGGLE:136,closedTermList:137,ETOGGLE:138,TENSOR:139,subsupList:140,MULTI:141,BMATRIX:142,tableRowList:143,EMATRIX:144,BGATHERED:145,EGATHERED:146,BPMATRIX:147,EPMATRIX:148,BBMATRIX:149,EBMATRIX:150,BVMATRIX:151,EVMATRIX:152,BBBMATRIX:153,EBBMATRIX:154,BVVMATRIX:155,EVVMATRIX:156,
+BSMALLMATRIX:157,ESMALLMATRIX:158,BCASES:159,ECASES:160,BALIGNED:161,EALIGNED:162,BARRAY:163,EARRAY:164,SUBSTACK:165,ARRAY:166,ARRAYOPTS:167,compoundTerm:168,_:169,"^":170,OPP:171,opm:172,OPM:173,FM:174,compoundTermList:175,subsupTermScript:176,subsupTerm:177,textstyle:178,DISPLAYSTYLE:179,TEXTSTYLE:180,TEXTSIZE:181,SCRIPTSIZE:182,SCRIPTSCRIPTSIZE:183,COLOR:184,BGCOLOR:185,tableCell:186,CELLOPTS:187,tableCellList:188,COLSEP:189,tableRow:190,ROWOPTS:191,ROWSEP:192,document:193,documentItemList:194,
+EOF:195,documentItem:196,mathItem:197,STARTMATH0:198,ENDMATH0:199,STARTMATH1:200,ENDMATH1:201,STARTMATH2:202,ENDMATH2:203,STARTMATH3:204,ENDMATH3:205,$accept:0,$end:1},z:{2:"error",4:"[",5:"TEXTOPTARG",6:"]",8:"{",9:"TEXTARG",10:"}",19:"COLLAYOUT",21:"COLALIGN",23:"ROWALIGN",25:"ROWSPAN",27:"COLSPAN",29:"ALIGN",31:"EQROWS",33:"EQCOLS",35:"ROWLINES",37:"COLLINES",39:"FRAME",41:"PADDING",49:"LEFT",50:"OPFS",51:".",53:"RIGHT",56:"BIG",57:"BBIG",58:"BIGG",59:"BBIGG",60:"BIGL",61:"BBIGL",62:"BIGGL",63:"BBIGGL",
+64:"TEXATOP",65:"TEXOVER",66:"TEXCHOOSE",67:"NUM",68:"TEXT",69:"A",70:"AILL",71:"AIUL",72:"AILG",73:"AIUG",74:"F",75:"MI",76:"MN",77:"MO",78:"OP",79:"OPS",80:"OPAS",81:"MS",82:"MTEXT",83:"HIGH_SURROGATE",84:"LOW_SURROGATE",85:"BMP_CHARACTER",86:"OPERATORNAME",87:"MATHOP",88:"MATHBIN",89:"MATHREL",90:"FRAC",91:"ROOT",92:"SQRT",93:"UNDERSET",94:"OVERSET",95:"UNDEROVERSET",96:"XARROW",97:"MATHRLAP",98:"MATHLLAP",99:"MATHCLAP",100:"PHANTOM",101:"TFRAC",102:"BINOM",103:"TBINOM",104:"PMOD",105:"UNDERBRACE",
+106:"UNDERLINE",107:"OVERBRACE",108:"ACCENT",109:"ACCENTNS",110:"BOXED",111:"SLASH",112:"QUAD",113:"QQUAD",114:"NEGSPACE",115:"NEGMEDSPACE",116:"NEGTHICKSPACE",117:"THINSPACE",118:"MEDSPACE",119:"THICKSPACE",120:"SPACE",121:"MATHRAISEBOX",122:"MATHBB",123:"MATHBF",124:"MATHBIT",125:"MATHSCR",126:"MATHBSCR",127:"MATHSF",128:"MATHFRAK",129:"MATHIT",130:"MATHTT",131:"MATHRM",132:"HREF",133:"STATUSLINE",134:"TOOLTIP",135:"TOGGLE",136:"BTOGGLE",138:"ETOGGLE",139:"TENSOR",141:"MULTI",142:"BMATRIX",144:"EMATRIX",
+145:"BGATHERED",146:"EGATHERED",147:"BPMATRIX",148:"EPMATRIX",149:"BBMATRIX",150:"EBMATRIX",151:"BVMATRIX",152:"EVMATRIX",153:"BBBMATRIX",154:"EBBMATRIX",155:"BVVMATRIX",156:"EVVMATRIX",157:"BSMALLMATRIX",158:"ESMALLMATRIX",159:"BCASES",160:"ECASES",161:"BALIGNED",162:"EALIGNED",163:"BARRAY",164:"EARRAY",165:"SUBSTACK",166:"ARRAY",167:"ARRAYOPTS",169:"_",170:"^",171:"OPP",173:"OPM",174:"FM",179:"DISPLAYSTYLE",180:"TEXTSTYLE",181:"TEXTSIZE",182:"SCRIPTSIZE",183:"SCRIPTSCRIPTSIZE",184:"COLOR",185:"BGCOLOR",
+187:"CELLOPTS",189:"COLSEP",191:"ROWOPTS",192:"ROWSEP",195:"EOF",198:"STARTMATH0",199:"ENDMATH0",200:"STARTMATH1",201:"ENDMATH1",202:"STARTMATH2",203:"ENDMATH2",204:"STARTMATH3",205:"ENDMATH3"},W:[0,[3,3],[7,3],[11,3],[12,3],[13,1],[14,1],[15,1],[16,1],[17,1],[18,2],[20,2],[22,2],[24,2],[26,2],[28,2],[30,2],[32,2],[34,2],[36,2],[38,2],[40,2],[42,1],[42,1],[42,1],[42,1],[43,1],[43,2],[44,1],[44,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[46,1],[46,2],[47,1],[47,2],[48,
+2],[48,2],[52,2],[52,2],[54,2],[54,3],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,3],[54,5],[54,5],[54,5],[54,5],[54,5],[54,5],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,2],[54,2],[54,2],[54,1],[54,1],[54,1],[54,1],[54,1],[54,2],[54,4],[54,2],[54,2],[54,1],[54,2],[54,2],[54,2],[54,2],[54,3],[54,3],[54,2],[54,5],[54,3],[54,3],[54,4],[54,5],[54,2],[54,2],[54,2],[54,2],[54,2],[54,3],[54,3],[54,3],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,1],[54,1],[54,
+1],[54,1],[54,1],[54,1],[54,1],[54,1],[54,4],[54,5],[54,4],[54,3],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,2],[54,3],[54,3],[54,3],[54,3],[54,3],[54,5],[54,8],[54,7],[54,7],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,3],[54,5],[54,4],[54,4],[54,4],[54,8],[137,1],[137,2],[168,3],[168,5],[168,4],[168,5],[168,4],[168,3],[168,3],[168,2],[168,1],[168,5],[168,5],[168,3],[168,3],[168,1],[172,1],[172,1],[175,1],[175,2],[176,1],[176,1],[177,4],[177,2],[177,
+2],[177,3],[140,1],[140,2],[178,1],[178,1],[178,1],[178,1],[178,1],[178,2],[178,2],[55,2],[55,1],[186,0],[186,5],[186,1],[188,1],[188,3],[190,5],[190,1],[143,1],[143,3],[193,2],[194,1],[194,2],[196,1],[196,1],[197,2],[197,3],[197,2],[197,3],[197,3],[197,3]],H:function(b,a,c,$a,g,d){b=d.length-1;switch(g){case 1:this.b=d[b-1].replace(/\\[\\\]]/g,function(a){return a.slice(1)});this.b=Fb(this.b);break;case 2:this.b=d[b-1].replace(/\\[\\\}]/g,function(a){return a.slice(1)});this.b=Fb(this.b);break;case 3:case 4:this.b=
+tc(d[b-1]);break;case 5:case 6:this.b=d[b].replace(/"/g,"&#x22;");break;case 7:this.b=d[b].replace(/\s+/g," ").replace(/^ | $/g," ");break;case 8:d[b]=d[b].trim();if("t"===d[b])this.b="axis 1";else if("c"===d[b])this.b="center";else if("b"===d[b])this.b="axis -1";else throw"Unknown array alignment";break;case 9:this.b="";d[b]=d[b].replace(/\s+/g,"");for($a=0;$a<d[b].length;$a++)"c"===d[b][$a]?this.b+=" center":"l"===d[b][$a]?this.b+=" left":"r"===d[b][$a]&&(this.b+=" right");if(this.b.length)this.b=
+this.b.slice(1);else throw"Invalid column alignments";break;case 10:case 11:this.b={columnalign:d[b]};break;case 12:this.b={rowalign:d[b]};break;case 13:this.b={rowspan:d[b]};break;case 14:this.b={colspan:d[b]};break;case 15:this.b={align:d[b]};break;case 16:this.b={equalrows:d[b]};break;case 17:this.b={equalcolumns:d[b]};break;case 18:this.b={rowlines:d[b]};break;case 19:this.b={columnlines:d[b]};break;case 20:this.b={frame:d[b]};break;case 21:this.b={rowspacing:d[b],columnspacing:d[b]};break;case 22:case 23:case 24:case 25:case 26:case 28:case 29:case 30:case 31:case 32:case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 42:case 170:case 175:case 180:case 181:case 186:case 196:case 207:case 209:this.b=
+d[b];break;case 27:case 41:case 43:this.b=Object.assign(d[b-1],d[b]);break;case 44:case 46:this.b=ab(d[b]);break;case 45:case 47:this.b="";break;case 48:this.b=e("mrow");break;case 49:this.b=db(d[b-1]);break;case 50:case 54:this.b=e("mo",d[b],{maxsize:"1.2em",minsize:"1.2em"});break;case 51:case 55:this.b=e("mo",d[b],{maxsize:"1.8em",minsize:"1.8em"});break;case 52:case 56:this.b=e("mo",d[b],{maxsize:"2.4em",minsize:"2.4em"});break;case 53:case 57:this.b=e("mo",d[b],{maxsize:"3em",minsize:"3em"});
+break;case 58:this.b=e("mrow",[d[b-2],db(d[b-1]),d[b]]);break;case 59:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});break;case 60:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});this.b=e("mrow",[d[b-4],this.b,d[b]]);break;case 61:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])]);break;case 62:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])]);this.b=e("mrow",[d[b-4],this.b,d[b]]);break;case 63:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});this.b=e("mrow",[ab("("),
+this.b,ab(")")]);break;case 64:this.b=e("mfrac",[db(d[b-3]),db(d[b-1])],{linethickness:"0px"});this.b=e("mrow",[d[b-4],this.b,d[b]]);this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 65:case 74:this.b=e("mn",d[b]);break;case 66:case 83:case 85:this.b=e("mtext",d[b]);break;case 67:case 68:case 69:case 70:this.b=Ub(d[b]);break;case 71:this.b=Ub(d[b],tb);break;case 72:case 177:this.b=ab(d[b],0,0);break;case 73:this.b=e("mi",d[b]);break;case 75:case 76:case 77:case 176:this.b=ab(d[b]);break;case 78:case 79:case 80:this.b=
+e("mo",d[b],{stretchy:"false"});break;case 81:this.b=e("ms",d[b]);break;case 82:this.b=e("ms",d[b],{lquote:d[b-2],rquote:d[b-1]});break;case 84:this.b=e("mtext",d[b-1]+d[b]);break;case 86:this.b=ab(d[b],0,sb("thinmathspace"));break;case 87:this.b=ab(d[b],sb("thinmathspace"),sb("thinmathspace"));break;case 88:this.b=ab(d[b],sb("mediummathspace"),sb("mediummathspace"));break;case 89:this.b=ab(d[b],sb("thickmathspace"),sb("thickmathspace"));break;case 90:this.b=e("mfrac",[d[b-1],d[b]]);break;case 91:this.b=
+e("mroot",[d[b],d[b-1]]);break;case 92:this.b=e("msqrt",[d[b]]);break;case 93:this.b=e("mroot",[d[b],db(d[b-2])]);break;case 94:this.b=e("munder",[d[b],d[b-1]]);break;case 95:this.b=e("mover",[d[b],d[b-1]]);break;case 96:this.b=e("munderover",[d[b],d[b-2],d[b-1]]);break;case 97:this.b="mrow"===d[b].tag&&!d[b].content&&!d[b].attributes?e("munder",[ab(d[b-4]),db(d[b-2])]):e("munderover",[ab(d[b-4]),db(d[b-2]),d[b]]);break;case 98:this.b=e("mover",[ab(d[b-1]),d[b]]);break;case 99:this.b=e("mpadded",
+[d[b]],{width:"0em"});break;case 100:this.b=e("mpadded",[d[b]],{width:"0em",lspace:"-100%width"});break;case 101:this.b=e("mpadded",[d[b]],{width:"0em",lspace:"-50%width"});break;case 102:this.b=e("mphantom",[d[b]]);break;case 103:this.b=e("mfrac",[d[b-1],d[b]]);this.b=db([this.b],"mstyle",{displaystyle:"false"});break;case 104:this.b=e("mfrac",[d[b-1],d[b]],{linethickness:"0px"});this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 105:this.b=e("mfrac",[d[b-1],d[b]],{linethickness:"0px"});this.b=
+db([this.b],"mstyle",{displaystyle:"false"});this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 106:this.b=e("mrow",[ab("(",sb("mediummathspace")),ab("mod",nb,sb("thinmathspace")),d[b],ab(")",nb,sb("mediummathspace"))]);break;case 107:this.b=e("munder",[d[b],ab("âŸ")]);break;case 108:this.b=e("munder",[d[b],ab("_")]);break;case 109:this.b=e("mover",[d[b],ab("âž")]);break;case 110:this.b=e("mover",[d[b],ab(d[b-1])]);break;case 111:this.b=e("mover",[d[b],e("mo",d[b-1],{stretchy:"false"})]);break;case 112:this.b=
+e("menclose",[d[b]],{notation:"box"});break;case 113:this.b=e("menclose",[d[b]],{notation:"updiagonalstrike"});break;case 114:this.b=Gb(1);break;case 115:this.b=Gb(2);break;case 116:this.b=Gb(sb("negativethinmathspace"));break;case 117:this.b=Gb(sb("negativemediummathspace"));break;case 118:this.b=Gb(sb("negativethickmathspace"));break;case 119:this.b=Gb(sb("thinmathspace"));break;case 120:this.b=Gb(sb("mediummathspace"));break;case 121:this.b=Gb(sb("thickmathspace"));break;case 122:this.b=e("mspace",
+xb,{height:"."+d[b-2]+"ex",depth:"."+d[b-1]+"ex",width:"."+d[b]+"em"});break;case 123:this.b=e("mpadded",[d[b]],{voffset:d[b-3].i+d[b-3].k,height:d[b-2].i+d[b-2].k,depth:d[b-1].i+d[b-1].k});break;case 124:this.b=e("mpadded",[d[b]],{voffset:d[b-2].i+d[b-2].k,height:d[b-1].i+d[b-1].k,depth:0>d[b-2].i?"+"+-d[b-2].i+d[b-2].k:"depth"});break;case 125:$a={voffset:d[b-1].i+d[b-1].k};0<=d[b-1].i?$a.height="+"+d[b-1].i+d[b-1].k:($a.height="0pt",$a.depth="+"+-d[b-1].i+d[b-1].k);this.b=e("mpadded",[d[b]],$a);
+break;case 126:this.b=db([d[b]],"mstyle",{mathvariant:"double-struck"});break;case 127:this.b=db([d[b]],"mstyle",{mathvariant:"bold"});break;case 128:this.b=db([d[b]],"mstyle",{mathvariant:"bold-italic"});break;case 129:this.b=db([d[b]],"mstyle",{mathvariant:"script"});break;case 130:this.b=db([d[b]],"mstyle",{mathvariant:"bold-script"});break;case 131:this.b=db([d[b]],"mstyle",{mathvariant:"sans-serif"});break;case 132:this.b=db([d[b]],"mstyle",{mathvariant:"fraktur"});break;case 133:this.b=db([d[b]],
+"mstyle",{mathvariant:"italic"});break;case 134:this.b=db([d[b]],"mstyle",{mathvariant:"monospace"});break;case 135:this.b=db([d[b]],"mstyle",{mathvariant:"normal"});break;case 136:this.b=e("mrow",[d[b]],$a.v?xb:{href:d[b-1]});break;case 137:this.b=$a.v?d[b]:e("maction",[d[b],e("mtext",d[b-1])],{actiontype:"statusline"});break;case 138:this.b=$a.v?d[b]:e("maction",[d[b],e("mtext",d[b-1])],{actiontype:"tooltip"});break;case 139:this.b=$a.v?d[b]:e("maction",[d[b-1],d[b]],{actiontype:"toggle",selection:"2"});
+break;case 140:this.b=$a.v?e("mrow",d[b-1]):e("maction",d[b-1],{actiontype:"toggle"});break;case 141:case 144:this.b=e("mmultiscripts",[d[b-3]].concat(d[b-1]));break;case 142:this.b=e("mmultiscripts",[d[b-3]].concat(d[b-1]).concat(e("mprescripts")).concat(d[b-5]));break;case 143:this.b=e("mmultiscripts",[d[b-2],e("mprescripts")].concat(d[b-4]));break;case 145:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});break;case 146:this.b=e("mtable",d[b-1],{displaystyle:"true",rowspacing:"1.0ex"});
+break;case 147:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("("),this.b,ab(")")]);break;case 148:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("["),this.b,ab("]")]);break;case 149:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("|"),this.b,ab("|")]);break;case 150:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("{"),this.b,ab("}")]);break;
+case 151:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=e("mrow",[ab("‖"),this.b,ab("‖")]);break;case 152:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex"});this.b=db([this.b],"mstyle",{scriptlevel:"2"});break;case 153:this.b=e("mtable",d[b-1],{displaystyle:"false",columnalign:"left left"});this.b=e("mrow",[ab("{"),this.b]);break;case 154:this.b=e("mtable",d[b-1],{displaystyle:"true",columnalign:"right left right left right left right left right left",
+columnspacing:"0em"});break;case 155:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex",align:d[b-3],columnalign:d[b-2]});break;case 156:this.b=e("mtable",d[b-1],{displaystyle:"false",rowspacing:"0.5ex",columnalign:d[b-2]});break;case 157:this.b=e("mtable",d[b-1],{displaystyle:"false",columnalign:"center",rowspacing:"0.5ex"});break;case 158:this.b=e("mtable",d[b-1],{displaystyle:"false"});break;case 159:this.b=e("mtable",d[b-1],Object.assign(d[b-3],{displaystyle:"false"}));break;case 160:this.b=
+[d[b]];break;case 161:this.b=d[b-1].concat([d[b]]);break;case 162:this.b=e("mmultiscripts",[d[b-1]].concat(d[b]));break;case 163:this.b=e("msubsup",[d[b-4],d[b-2],d[b]]);break;case 164:this.b=e("msubsup",[d[b-3],d[b-1],ab(d[b])]);break;case 165:this.b=e("msubsup",[d[b-4],d[b],d[b-2]]);break;case 166:this.b=e("msubsup",[d[b-3],d[b],ab(d[b-2])]);break;case 167:this.b=e("msub",[d[b-2],d[b]]);break;case 168:this.b=e("msup",[d[b-2],d[b]]);break;case 169:this.b=e("msup",[d[b-1],ab(d[b])]);break;case 171:this.b=
+e("munderover",[d[b-4],d[b-2],d[b]]);break;case 172:this.b=e("munderover",[d[b-4],d[b],d[b-2]]);break;case 173:this.b=e("munder",[d[b-2],d[b]]);break;case 174:this.b=e("mover",[d[b-2],d[b]]);break;case 178:case 200:case 204:this.b=[d[b]];break;case 179:this.b=d[b-1].concat([d[b]]);break;case 182:this.b=[d[b-2],d[b]];break;case 183:this.b=[d[b],e("none")];break;case 184:case 185:this.b=[e("none"),d[b]];break;case 187:this.b=d[b-1].concat(d[b]);break;case 188:this.b={displaystyle:"true"};break;case 189:this.b=
+{displaystyle:"false"};break;case 190:this.b={scriptlevel:"0"};break;case 191:this.b={scriptlevel:"1"};break;case 192:this.b={scriptlevel:"2"};break;case 193:this.b={mathcolor:d[b]};break;case 194:this.b={mathbackground:d[b]};break;case 195:this.b=[db(d[b],"mstyle",d[b-1])];break;case 197:this.b=e("mtd",[]);break;case 198:this.b=db(d[b],"mtd",d[b-2]);break;case 199:this.b=db(d[b],"mtd");break;case 201:case 205:this.b=d[b-2].concat([d[b]]);break;case 202:this.b=this.b=e("mtr",d[b],d[b-2]);break;case 203:this.b=
+e("mtr",d[b]);break;case 206:return this.b=d[b-1];case 208:this.b=d[b-1]+d[b];break;case 210:this.b=Pb(d[b]);break;case 211:this.b=Ib([e("mrow")],yb,yb,$a.t);break;case 212:this.b=Ib(d[b-1],yb,yb,$a.t);break;case 213:this.b=Ib([e("mrow")],tb,yb,$a.t);break;case 214:this.b=Ib(d[b-1],tb,yb,$a.t);break;case 215:this.b=Ib(d[b-1],yb,yb,$a.t);break;case 216:this.b=Ib(d[b-1],tb,yb,$a.t)}},ma:[{68:Wb,193:1,194:2,196:3,197:5,198:Xb,200:Yb,202:Zb,204:$b},{1:[3]},{68:Wb,195:[1,10],196:11,197:5,198:Xb,200:Yb,
+202:Zb,204:$b},c(Ab,[2,207]),c(Ab,[2,209]),c(Ab,[2,210]),{8:m,48:36,49:s,50:v,51:x,54:25,55:13,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,
+130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,199:[1,12]},{8:m,48:36,49:s,50:v,51:x,54:25,55:126,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,
+101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,201:[1,125]},{8:m,48:36,49:s,50:v,51:x,54:25,55:127,56:h,
+57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,
+153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:128,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,
+117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{1:[2,206]},c(Ab,[2,208]),c(Ab,[2,211]),{199:[1,129]},{8:m,48:36,49:s,50:v,51:x,54:25,55:130,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,
+74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,
+173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(ac,[2,196],{54:25,172:26,48:36,168:131,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,
+124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb}),c(Db,[2,188]),c(Db,[2,189]),c(Db,[2,190]),c(Db,[2,191]),c(Db,[2,192]),{7:133,8:mb,14:132},{7:133,8:mb,14:135},c(ub,[2,178]),{8:m,48:36,49:s,50:v,51:x,54:136,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,
+87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,170],{169:[1,138],170:[1,139],171:[1,140]}),c(ub,[2,175],{169:[1,
+141],170:[1,142]}),{8:m,10:[1,143],48:36,49:s,50:v,51:x,54:25,55:144,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,
+134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{50:[1,145]},{50:[1,146]},{50:[1,147]},{50:[1,148]},{50:[1,149]},{50:[1,150]},{50:[1,151]},{50:[1,152]},{8:m,48:36,49:s,50:v,51:x,54:25,55:153,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,
+88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,
+184:kb,185:lb},c(g,[2,65]),c(g,[2,66]),c(g,[2,67]),c(g,[2,68]),c(g,[2,69]),c(g,[2,70]),c(g,[2,71]),c(g,[2,72]),{7:155,8:mb,15:154},{7:155,8:mb,15:156},{7:155,8:mb,15:157},c(g,[2,76]),c(g,[2,77]),c(g,[2,78]),c(g,[2,79]),c(g,[2,80]),{3:160,4:Sb,7:155,8:mb,13:159,15:158},{7:155,8:mb,15:162},{84:[1,163]},c(g,[2,85]),{7:164,8:mb},{7:165,8:mb},{7:166,8:mb},{7:167,8:mb},{8:m,48:36,49:s,50:v,51:x,54:168,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,
+80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:169,56:h,57:q,
+58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,
+155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{4:[1,171],8:m,48:36,49:s,50:v,51:x,54:170,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,
+130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:172,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,
+117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:173,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,
+104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:174,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,
+87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{4:[1,175],8:m,48:36,49:s,50:v,51:x,54:176,56:h,57:q,58:p,59:y,60:f,61:u,
+62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,
+161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:177,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,
+135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:178,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,
+122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:179,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,
+109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:180,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,
+94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:181,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,
+74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,
+49:s,50:v,51:x,54:182,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,
+145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:183,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,
+127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:184,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,
+114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:185,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,
+101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:186,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,
+81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:187,56:h,57:q,58:p,
+59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,
+157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:188,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,
+133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:189,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,
+120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:190,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,
+107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:191,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,
+91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,114]),c(g,[2,115]),c(g,[2,116]),c(g,[2,117]),c(g,[2,118]),c(g,[2,119]),c(g,[2,120]),
+c(g,[2,121]),{7:192,8:mb},{8:[1,194],12:193},{8:m,48:36,49:s,50:v,51:x,54:195,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,
+132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:196,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,
+119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:197,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,
+106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:198,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,
+89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:199,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,
+69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,
+166:Ya},{8:m,48:36,49:s,50:v,51:x,54:200,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,
+141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:201,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,
+125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:202,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,
+112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:203,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,
+98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:204,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,
+78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{7:133,8:mb,14:205},{7:206,8:mb},
+{7:207,8:mb},{8:m,48:36,49:s,50:v,51:x,54:208,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,
+139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:210,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,
+124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,137:209,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:[1,211]},c([144,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,143:212,190:213,188:215,186:216,55:218,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,
+94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([146,189,192],pb,{178:14,175:15,
+168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:219,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,
+131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([148,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:220,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,
+92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([150,189,192],pb,
+{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:221,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,
+129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([152,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:222,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,
+89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([154,
+189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:223,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,
+128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([156,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:224,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,
+88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),
+c([158,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:225,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,
+127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c([160,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:226,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,
+86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,
+191:vb}),c([162,189,192],pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:227,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,
+125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{3:230,4:Sb,7:231,8:mb,16:228,17:229},{8:[1,232]},{8:[1,233]},c(Eb,[2,176]),c(Eb,[2,177]),{50:[1,234],51:[1,235]},c(Ab,[2,213]),{201:[1,236]},{203:[1,237]},{205:[1,238]},c(Ab,[2,212]),c(ac,[2,195]),c(ub,[2,179]),c(Db,[2,193]),c([8,10,
+19,21,23,25,27,29,31,33,35,37,39,41,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,145,147,149,151,153,155,157,159,161,163,165,166,173,174,179,180,181,182,183,184,185],[2,6]),{9:[1,239]},c(Db,[2,194]),{8:bc,140:240,169:Bb,170:Cb,177:242},{8:m,48:36,49:s,50:v,
+51:x,54:245,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,
+149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:246,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,
+129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:247,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,
+116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,169],{169:[1,248]}),{8:m,48:36,49:s,50:v,51:x,54:249,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,
+98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:250,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,
+78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,48]),{10:[1,251],64:[1,252],
+65:[1,253],66:[1,254]},c(g,[2,50]),c(g,[2,51]),c(g,[2,52]),c(g,[2,53]),c(g,[2,54]),c(g,[2,55]),c(g,[2,56]),c(g,[2,57]),{52:255,53:Mb,64:[1,256],65:[1,257],66:[1,258]},c(g,[2,73]),c(g,[2,7]),c(g,[2,74]),c(g,[2,75]),c(g,[2,81]),{3:160,4:Sb,13:260},c(cc,[2,5]),{5:[1,261]},c(g,[2,83]),c(g,[2,84]),c(g,[2,86]),c(g,[2,87]),c(g,[2,88]),c(g,[2,89]),{8:m,48:36,49:s,50:v,51:x,54:262,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,
+86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:263,56:h,57:q,58:p,59:y,60:f,61:u,62:n,
+63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,
+161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,92]),{8:m,48:36,49:s,50:v,51:x,54:25,55:264,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,
+132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:265,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,
+106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:266,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,
+89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:267,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,
+69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,
+166:Ya},{8:m,48:36,49:s,50:v,51:x,54:25,55:268,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,
+139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(g,[2,98]),c(g,[2,99]),c(g,[2,100]),c(g,[2,101]),c(g,[2,102]),{8:m,48:36,49:s,50:v,51:x,54:269,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,
+102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:270,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,
+83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:271,56:h,57:q,58:p,59:y,60:f,
+61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,
+159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,106]),c(g,[2,107]),c(g,[2,108]),c(g,[2,109]),c(g,[2,110]),c(g,[2,111]),c(g,[2,112]),c(g,[2,113]),{7:272,8:mb},{4:dc,8:m,11:273,48:36,49:s,50:v,51:x,54:274,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,
+115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{9:[1,276]},c(g,[2,126]),c(g,[2,127]),c(g,[2,128]),c(g,[2,129]),c(g,[2,130]),c(g,[2,131]),c(g,[2,132]),c(g,[2,133]),c(g,[2,134]),c(g,[2,135]),{8:m,48:36,49:s,50:v,51:x,54:277,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,
+73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,
+49:s,50:v,51:x,54:278,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,
+145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:279,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,
+127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:280,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,
+114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:282,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,
+101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,138:[1,281],139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ec,[2,160]),{10:[1,284],140:283,169:Bb,170:Cb,177:242},{144:[1,285],192:wb},c(Nb,[2,204]),{8:[1,287]},c(Nb,[2,203],{189:fc}),c(Jb,
+[2,200]),{8:[1,289]},c(Jb,[2,199]),{146:[1,290],192:wb},{148:[1,291],192:wb},{150:[1,292],192:wb},{152:[1,293],192:wb},{154:[1,294],192:wb},{156:[1,295],192:wb},{158:[1,296],192:wb},{160:[1,297],192:wb},{162:[1,298],192:wb},{7:231,8:mb,17:299},c(gc,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:300,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,
+90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{8:[2,8]},
+c([8,49,50,51,56,57,58,59,60,61,62,63,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,145,147,149,151,153,155,157,159,161,163,164,165,166,173,174,179,180,181,182,183,184,185,187,189,191,192],[2,9]),c(Tb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:301,8:m,49:s,
+50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,
+149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c(Tb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:302,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,
+107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,167:[1,303],173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c(Db,[2,44]),c(Db,[2,45]),c(Ab,[2,214]),c(Ab,[2,215]),c(Ab,[2,216]),{10:[1,304]},c(ub,[2,162],{177:305,
+169:Bb,170:Cb}),{140:306,169:Bb,170:Cb,177:242},c(Eb,[2,186]),{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,
+130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,170:[1,308],172:310,173:bb,174:cb,176:307},{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,
+111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,172:310,173:bb,174:cb,176:311},{8:bc},c(ub,[2,167],{170:[1,312],171:[1,313]}),c(ub,[2,168],{169:[1,314]}),{8:m,48:36,49:s,50:v,51:x,54:315,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,
+74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,173],
+{170:[1,316]}),c(ub,[2,174],{169:[1,317]}),c(g,[2,49]),{8:m,48:36,49:s,50:v,51:x,54:25,55:318,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,
+130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:319,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,
+103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:320,56:h,57:q,58:p,59:y,60:f,61:u,
+62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,
+161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(g,[2,58]),{8:m,48:36,49:s,50:v,51:x,54:25,55:321,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,
+119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:322,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,
+88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,
+184:kb,185:lb},{8:m,48:36,49:s,50:v,51:x,54:25,55:323,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,
+136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},{50:[1,324],51:[1,325]},{7:155,8:mb,15:326},{6:[1,327]},c(g,[2,90]),c(g,[2,91]),{6:[1,328]},c(g,[2,94]),c(g,[2,95]),{8:m,48:36,49:s,50:v,51:x,54:329,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,
+90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{6:[1,330]},c(g,[2,103]),c(g,[2,104]),c(g,[2,105]),{7:331,8:mb},{4:dc,8:m,11:332,48:36,
+49:s,50:v,51:x,54:333,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,
+145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,125]),{5:[1,334]},{10:[1,335]},c(g,[2,136]),c(g,[2,137]),c(g,[2,138]),c(g,[2,139]),c(g,[2,140]),c(ec,[2,161]),{10:[1,336],169:Bb,170:Cb,177:305},{8:m,48:36,49:s,50:v,51:x,54:337,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,
+105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,145]),c(Jb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,188:215,186:216,55:218,190:338,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,
+71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,
+173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{20:341,21:Kb,22:342,23:Lb,44:340,47:339},c(Jb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,55:218,186:345,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,
+114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb}),{20:348,21:Kb,22:349,23:Lb,24:350,25:hc,26:351,27:ic,42:347,43:346},c(g,[2,146]),c(g,[2,147]),c(g,[2,148]),c(g,[2,149]),c(g,[2,150]),c(g,[2,151]),c(g,[2,152]),c(g,
+[2,153]),c(g,[2,154]),c(gc,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:354,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,
+125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),{164:[1,355],192:wb},{10:[1,356],192:wb},{10:[1,357],192:wb},{8:[1,358]},c([6,8,10,19,21,23,25,27,29,31,33,35,37,39,41,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,85,86,87,88,89,90,
+91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,138,139,141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,169,170,171,173,174,179,180,181,182,183,184,185,187,189,191,192,199,201,203,205],[2,2]),c(Eb,[2,187]),{10:[1,359],169:Bb,170:Cb,177:305},c([6,8,10,49,50,51,53,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,
+76,77,78,79,80,81,82,83,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,169,173,174,189,192,199,201,203,205],[2,183],{170:[1,360]}),{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,
+77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,172:310,173:bb,174:cb,176:361},
+c(Eb,[2,180]),c(Eb,[2,181]),c(Eb,[2,184]),{8:m,48:36,49:s,50:v,51:x,54:362,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,
+133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,164]),{8:m,48:36,49:s,50:v,51:x,54:363,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,
+118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(ub,[2,166]),{8:m,48:36,49:s,50:v,51:x,54:364,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,
+103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{8:m,48:36,49:s,50:v,51:x,54:365,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,
+85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},{10:[1,366]},{10:[1,367]},{10:[1,368]},{52:369,53:Mb},{52:370,
+53:Mb},{52:371,53:Mb},c(g,[2,46]),c(g,[2,47]),c(g,[2,82]),c(cc,[2,1]),{8:m,48:36,49:s,50:v,51:x,54:372,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,
+129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,96]),{8:m,48:36,49:s,50:v,51:x,54:373,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,
+114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,122]),{8:m,48:36,49:s,50:v,51:x,54:374,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,
+98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},c(g,[2,124]),{6:[1,375]},c(jc,[2,4]),{8:m,48:36,49:s,50:v,51:x,54:376,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,
+71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya},
+{8:[1,377]},c(Nb,[2,205]),{10:[1,378],20:341,21:Kb,22:342,23:Lb,44:379},c(Ob,[2,42]),c(Ob,[2,28]),c(Ob,[2,29]),{7:133,8:mb,14:380},{7:133,8:mb,14:381},c(Jb,[2,201]),{10:[1,382],20:348,21:Kb,22:349,23:Lb,24:350,25:hc,26:351,27:ic,42:383},c(Hb,[2,26]),c(Hb,[2,22]),c(Hb,[2,23]),c(Hb,[2,24]),c(Hb,[2,25]),{7:133,8:mb,14:384},{7:133,8:mb,14:385},{164:[1,386],192:wb},c(g,[2,156]),c(g,[2,157]),c(g,[2,158]),{18:389,19:kc,20:390,21:Kb,22:391,23:Lb,28:392,29:lc,30:393,31:mc,32:394,33:nc,34:395,35:oc,36:396,
+37:pc,38:397,39:qc,40:398,41:rc,45:388,46:387},c(g,[2,141]),{8:m,48:36,49:s,50:v,51:x,54:309,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,
+130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:Za,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,172:310,173:bb,174:cb,176:407},c(Eb,[2,185]),c(ub,[2,163]),c(ub,[2,165]),c(ub,[2,171]),c(ub,[2,172]),c(g,[2,59]),c(g,[2,61]),c(g,[2,63]),c(g,[2,60]),c(g,[2,62]),c(g,[2,64]),c(g,[2,93]),c(g,[2,97]),c(g,[2,123]),c(jc,[2,3]),{8:[1,408]},{140:409,169:Bb,170:Cb,177:242},c(Jb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,186:216,55:218,188:410,8:m,49:s,
+50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,
+149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb}),c(Ob,[2,43]),c(sc,[2,11]),c(sc,[2,12]),{8:m,48:36,49:s,50:v,51:x,54:25,55:411,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,
+112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,168:23,172:26,173:bb,174:cb,175:15,178:14,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb},c(Hb,[2,27]),c(Hb,[2,13]),c(Hb,[2,14]),c(g,[2,155]),{10:[1,412],18:389,19:kc,20:390,21:Kb,22:391,23:Lb,28:392,29:lc,30:393,31:mc,
+32:394,33:nc,34:395,35:oc,36:396,37:pc,38:397,39:qc,40:398,41:rc,45:413},c(ob,[2,40]),c(ob,[2,30]),c(ob,[2,31]),c(ob,[2,32]),c(ob,[2,33]),c(ob,[2,34]),c(ob,[2,35]),c(ob,[2,36]),c(ob,[2,37]),c(ob,[2,38]),c(ob,[2,39]),{7:133,8:mb,14:414},{7:133,8:mb,14:415},{7:133,8:mb,14:416},{7:133,8:mb,14:417},{7:133,8:mb,14:418},{7:133,8:mb,14:419},{7:133,8:mb,14:420},{7:133,8:mb,14:421},c(Eb,[2,182]),{10:[1,423],140:422,169:Bb,170:Cb,177:242},{10:[1,424],169:Bb,170:Cb,177:305},c(Nb,[2,202],{189:fc}),c(Jb,[2,198]),
+c(Tb,pb,{178:14,175:15,168:23,54:25,172:26,48:36,190:213,188:215,186:216,55:218,143:425,8:m,49:s,50:v,51:x,56:h,57:q,58:p,59:y,60:f,61:u,62:n,63:k,67:r,68:t,69:l,70:w,71:z,72:A,73:B,74:C,75:D,76:E,77:F,78:G,79:H,80:I,81:J,82:K,83:L,85:M,86:N,87:O,88:P,89:Q,90:R,91:S,92:T,93:U,94:V,95:W,96:X,97:Y,98:Z,99:$,100:aa,101:ba,102:ca,103:da,104:ea,105:fa,106:ga,107:ha,108:ia,109:ja,110:ka,111:la,112:ma,113:na,114:oa,115:pa,116:qa,117:ra,118:sa,119:ta,120:ua,121:va,122:wa,123:xa,124:ya,125:za,126:Aa,127:Ba,
+128:Ca,129:Da,130:Ea,131:Fa,132:Ga,133:Ha,134:Ia,135:Ja,136:Ka,139:eb,141:La,142:Ma,145:Na,147:Oa,149:Pa,151:Qa,153:Ra,155:Sa,157:Ta,159:Ua,161:Va,163:Wa,165:Xa,166:Ya,173:bb,174:cb,179:fb,180:gb,181:hb,182:ib,183:jb,184:kb,185:lb,187:qb,191:vb}),c(ob,[2,41]),c(ob,[2,10]),c(ob,[2,15]),c(ob,[2,16]),c(ob,[2,17]),c(ob,[2,18]),c(ob,[2,19]),c(ob,[2,20]),c(ob,[2,21]),{10:[1,426],169:Bb,170:Cb,177:305},c(g,[2,143]),c(g,[2,144]),{10:[1,427],192:wb},c(g,[2,142]),c(g,[2,159])],N:{10:[2,206],230:[2,8]},parseError:function(b,
+a){if(a.va)this.trace(b);else{var c=Error(b);c.hash=a;throw c;}},parse:function(b){var a=[0],c=[xb],e=[],g=this.ma,d="",m=0,s=0,v=0,x=e.slice.call(arguments,1),h=Object.create(this.S),q={},p;for(p in this.e)Object.prototype.hasOwnProperty.call(this.e,p)&&(q[p]=this.e[p]);h.ga(b,q);q.S=h;q.V=this;"undefined"==typeof h.c&&(h.c={});p=h.c;e.push(p);var y=h.options&&h.options.w;this.parseError="function"===typeof q.parseError?q.parseError:Object.getPrototypeOf(this).parseError;for(var f,u,n,k,r={},t,l;;){n=
+a[a.length-1];if(this.N[n])k=this.N[n];else{if(f===xb||"undefined"==typeof f)f=nb,f=h.R()||1,"number"!==typeof f&&(f=this.la[f]||f);k=g[n]&&g[n][f]}if("undefined"===typeof k||!k.length||!k[0]){var w="";l=[];for(t in g[n])this.z[t]&&2<t&&l.push("'"+this.z[t]+"'");w=h.D?"Parse error on line "+(m+1)+":\n"+h.D()+"\nExpecting "+l.join(", ")+", got '"+(this.z[f]||f)+"'":"Parse error on line "+(m+1)+": Unexpected "+(1==f?"end of input":"'"+(this.z[f]||f)+"'");this.parseError(w,{text:h.match,$:this.z[f]||
+f,T:h.f,ta:p,qa:l})}if(k[0]instanceof Array&&1<k.length)throw Error("Parse Error: multiple actions possible at state: "+n+", token: "+f);switch(k[0]){case 1:a.push(f);c.push(h.a);e.push(h.c);a.push(k[1]);f=xb;u?(f=u,u=xb):(s=h.q,d=h.a,m=h.f,p=h.c,0<v&&v--);break;case 2:l=this.W[k[1]][1];r.b=c[c.length-l];r.K={r:e[e.length-(l||1)].r,o:e[e.length-1].o,l:e[e.length-(l||1)].l,m:e[e.length-1].m};y&&(r.K.n=[e[e.length-(l||1)].n[0],e[e.length-1].n[1]]);n=this.H.apply(r,[d,s,m,q,k[1],c,e].concat(x));if("undefined"!==
+typeof n)return n;l&&(a=a.slice(0,-2*l),c=c.slice(0,-1*l),e=e.slice(0,-1*l));a.push(this.W[k[1]][0]);c.push(r.b);e.push(r.K);k=g[a[a.length-2]][a[a.length-1]];a.push(k);break;case 3:return tb}}return tb}},Qb="http://www.w3.org/1998/Math/MathML",wc="TeX LaTeX text/x-tex text/x-latex application/x-tex application/x-latex".split(" ");try{rb.C=new DOMParser}catch(yc){rb.C={parseFromString:function(){throw"DOMParser undefined. Did you call TeXZilla.setDOMParser?";}}}rb.fa=function(b){this.C=b};try{rb.G=
+new XMLSerializer}catch(zc){rb.G={serializeToString:function(){throw"XMLSerializer undefined. Did you call TeXZilla.setXMLSerializer?";}}}rb.ja=function(b){this.G=b};rb.U=function(b){return this.C.parseFromString(b,"application/xml").documentElement};rb.ia=function(b){this.e.v=b};rb.ha=function(b){this.e.da=b};rb.ca=function(b){"string"===typeof b&&(b=this.U(b));return Vb(b)};rb.Z=function(b,a,c,g){var f;try{f=this.parse("\\("+b+"\\)"),c&&(f=f.replace(/^<math/,'<math dir="rtl"')),a&&(f=f.replace(/^<math/,
+'<math display="block"'))}catch(d){if(g)throw d;f=Pb(Ib([e("merror",[e("mtext",Fb(d.message))])],a,c,b))}return f};rb.Y=function(b,a,c,e){return this.U(this.Z(b,a,c,e))};rb.na=function(b,a,c,e,f){var d,g;e===nb&&(e=64);f===nb&&(f=window.document);a=this.Y(b,tb,a);a.setAttribute("mathsize",e+"px");e=document.createElement("div");e.style.visibility="hidden";e.style.position="absolute";e.appendChild(a);f.body.appendChild(e);d=a.getBoundingClientRect();f.body.removeChild(e);e.removeChild(a);c?(c=Math.pow(2,
+Math.ceil(Math.log(d.width)/Math.LN2)),f=Math.pow(2,Math.ceil(Math.log(d.height)/Math.LN2))):(c=Math.ceil(d.width),f=Math.ceil(d.height));g=document.createElementNS("http://www.w3.org/2000/svg","svg");g.setAttribute("width",c+"px");g.setAttribute("height",f+"px");e=document.createElementNS("http://www.w3.org/2000/svg","g");e.setAttribute("transform","translate("+(c-d.width)/2+","+(f-d.height)/2+")");g.appendChild(e);e=document.createElementNS("http://www.w3.org/2000/svg","foreignObject");e.setAttribute("width",
+d.width);e.setAttribute("height",d.height);e.appendChild(a);g.firstChild.appendChild(e);a=new Image;a.src="data:image/svg+xml;base64,"+window.btoa(xc(this.G.serializeToString(g)));a.width=c;a.height=f;a.alt=Fb(b);return a};rb.Q=function(b,a){try{return this.parse(b)}catch(c){if(a)throw c;return b}};rb.P=function(b,a){var c,e,f;for(f=b.firstChild;f;f=f.nextSibling)switch(f.nodeType){case 1:this.P(f,a);break;case 3:this.e.O=tb;c=this.C.parseFromString("<root>"+zb.Q(f.data,a)+"</root>","application/xml").documentElement;
+for(this.e.O=yb;e=c.firstChild;)b.insertBefore(c.removeChild(e),f);e=f.previousSibling;b.removeChild(f);f=e}};rb.S=function(){return{J:1,parseError:function(b,a){if(this.e.V)this.e.V.parseError(b,a);else throw Error(b);},ga:function(b,a){this.e=a||this.e||{};this.g=b;this.u=this.B=this.s=yb;this.f=this.q=0;this.a=this.h=this.match="";this.d=["INITIAL"];this.c={r:1,l:0,o:1,m:0};this.options.w&&(this.c.n=[0,0]);this.offset=0;return this},input:function(){var b=this.g[0];this.a+=b;this.q++;this.offset++;
+this.match+=b;this.h+=b;b.match(/(?:\r\n?|\n).*/g)?(this.f++,this.c.o++):this.c.m++;this.options.w&&this.c.n[1]++;this.g=this.g.slice(1);return b},I:function(b){var a=b.length,c=b.split(/(?:\r\n?|\n)/g);this.g=b+this.g;this.a=this.a.substr(0,this.a.length-a);this.offset-=a;b=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1);this.h=this.h.substr(0,this.h.length-1);c.length-1&&(this.f-=c.length-1);var e=this.c.n;this.c={r:this.c.r,o:this.f+1,l:this.c.l,m:c?(c.length===
+b.length?this.c.l:0)+b[b.length-c.length].length-c[0].length:this.c.l-a};this.options.w&&(this.c.n=[e[0],e[0]+this.q-a]);this.q=this.a.length;return this},ua:function(){this.u=tb;return this},wa:function(){if(this.options.L)this.B=tb;else return this.parseError("Lexical error on line "+(this.f+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.D(),{text:"",$:xb,T:this.f});return this},sa:function(b){this.I(this.match.slice(b))},
+ea:function(){var b=this.h.substr(0,this.h.length-this.match.length);return(20<b.length?"...":"")+b.substr(-20).replace(/\n/g,"")},oa:function(){var b=this.match;20>b.length&&(b+=this.g.substr(0,20-b.length));return(b.substr(0,20)+(20<b.length?"...":"")).replace(/\n/g,"")},D:function(){var b=this.ea(),a=Array(b.length+1).join("-");return b+this.oa()+"\n"+a+"^"},X:function(b,a){var c,e;this.options.L&&(e={f:this.f,c:{r:this.c.r,o:this.o,l:this.c.l,m:this.c.m},a:this.a,match:this.match,matches:this.matches,
+h:this.h,q:this.q,offset:this.offset,u:this.u,g:this.g,e:this.e,d:this.d.slice(0),s:this.s},this.options.w&&(e.c.n=this.c.n.slice(0)));if(c=b[0].match(/(?:\r\n?|\n).*/g))this.f+=c.length;this.c={r:this.c.o,o:this.f+1,l:this.c.m,m:c?c[c.length-1].length-c[c.length-1].match(/\r?\n?/)[0].length:this.c.m+b[0].length};this.a+=b[0];this.match+=b[0];this.matches=b;this.q=this.a.length;this.options.w&&(this.c.n=[this.offset,this.offset+=this.q]);this.B=this.u=yb;this.g=this.g.slice(b[0].length);this.h+=b[0];
+c=this.H.call(this,this.e,this,a,this.d[this.d.length-1]);this.s&&this.g&&(this.s=yb);if(c)return c;if(this.B)for(var f in e)this[f]=e[f];return yb},next:function(){if(this.s)return this.J;this.g||(this.s=tb);var b,a,c;this.u||(this.match=this.a="");for(var e=this.aa(),f=0;f<e.length;f++)if((a=this.g.match(this.rules[e[f]]))&&(!b||a[0].length>b[0].length))if(b=a,c=f,this.options.L){b=this.X(a,e[f]);if(b!==yb)return b;if(this.B)b=yb;else return yb}else if(!this.options.ra)break;return b?(b=this.X(b,
+e[c]),b!==yb?b:yb):""===this.g?this.J:this.parseError("Lexical error on line "+(this.f+1)+". Unrecognized text.\n"+this.D(),{text:"",$:xb,T:this.f})},R:function(){var b=this.next();return b?b:this.R()},j:function(b){this.d.push(b)},p:function(){return 0<this.d.length-1?this.d.pop():this.d[0]},aa:function(){return this.d.length&&this.d[this.d.length-1]?this.M[this.d[this.d.length-1]].rules:this.M.INITIAL.rules},ya:function(b){b=this.d.length-1-Math.abs(b||0);return 0<=b?this.d[b]:"INITIAL"},pushState:function(b){this.j(b)},
+xa:function(){return this.d.length},options:{},H:function(b,a,c){switch(c){case 0:this.I(a.a);this.pushState("DOCUMENT");break;case 1:return this.pushState("MATH"+(0+!!b.da)),b.ka=this.h.length,"STARTMATH"+(2*("$"==a.a[0])+("$"==a.a[1]||"["==a.a[1]));case 2:return this.p(),"EOF";case 3:return a.a=a.a[1],"TEXT";case 4:return b.O&&(a.a=Fb(a.a)),"TEXT";case 5:return"TEXT";case 6:return this.p(),"[";case 7:this.I(a.a);this.p();this.p();break;case 8:return"TEXTOPTARG";case 9:return this.p(),"]";case 10:return"{";
+case 11:return"TEXTARG";case 12:return this.p(),"}";case 13:return this.p(),"]";case 15:return this.p(),b.ba=this.h.length-this.match.length,b.t=this.h.substring(b.ka,b.ba),"ENDMATH"+(2*("$"==a.a[0])+("$"==a.a[1]||"]"==a.a[1]));case 16:return"{";case 17:return"}";case 18:return"^";case 19:return"_";case 20:return".";case 21:return"COLSEP";case 22:return"ROWSEP";case 23:return"NUM";case 24:return"A";case 25:return a.a="Ζ","AIUG";case 26:return a.a="ζ","AILG";case 27:return this.pushState("OPTARG"),
+this.pushState("TRYOPTARG"),a.a="⇌","XARROW";case 28:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇒","XARROW";case 29:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="→","XARROW";case 30:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="↦","XARROW";case 31:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇋","XARROW";case 32:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="⇔","XARROW";case 33:return this.pushState("OPTARG"),
+this.pushState("TRYOPTARG"),a.a="↔","XARROW";case 34:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="â‡","XARROW";case 35:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="â†","XARROW";case 36:return a.a="Ξ","AIUG";case 37:return a.a="ξ","AILG";case 38:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="↪","XARROW";case 39:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),a.a="↩","XARROW";case 40:return a.a="≀","OP";case 41:return a.a="℘","A";case 42:return a.a=
+"⇀","ACCENT";case 43:return a.a="Ëœ","ACCENT";case 44:return a.a="^","ACCENT";case 45:return a.a="ˇ","ACCENT";case 46:return a.a="¯","ACCENT";case 47:return a.a="≙","OP";case 48:return a.a="â‹€","OPM";case 49:return a.a="∧","OP";case 50:return a.a="⦀","OPFS";case 51:return a.a="⊪","OP";case 52:return a.a="‖","OPFS";case 53:return a.a="|","OPFS";case 54:return a.a="⊻","OP";case 55:return a.a="â‹","OPM";case 56:return a.a="∨","OP";case 57:return a.a="⇀","ACCENTNS";case 58:return a.a="â‹®","OP";case 59:return a.a=
+"⊫","OP";case 60:return a.a="⊩","OP";case 61:return a.a="⊨","OP";case 62:return a.a="⊢","OP";case 63:return a.a="â««","OP";case 64:return a.a="⊳","OP";case 65:return a.a="⊲","OP";case 66:return a.a="â–µ","OP";case 67:return a.a="Ï‘","AILG";case 68:return a.a="⫌︀","OP";case 69:return a.a="⊋︀","OP";case 70:return a.a="⫋︀","OP";case 71:return a.a="⊊︀","OP";case 72:return a.a="⊊︀","OP";case 73:return a.a="Ï‚","A";case 74:return a.a="ϱ","AILG";case 75:return a.a="âˆ","OP";case 76:return a.a="Ï–","AILG";case 77:return a.a=
+"φ","AILG";case 78:return a.a="∅","A";case 79:return a.a="ϰ","AILG";case 80:return a.a="ε","AILG";case 81:return a.a="⤊","OPS";case 82:return a.a="⇈","OPS";case 83:return a.a="ϒ","A";case 84:return a.a="υ","AILG";case 85:return a.a="ϒ","A";case 86:return a.a="⊎","OP";case 87:return a.a="⨛","OP";case 88:return a.a="↿","OPS";case 89:return a.a="↾","OPS";case 90:return a.a="⇕","OPS";case 91:return a.a="↕","OPS";case 92:return a.a="↕","OPS";case 93:return a.a="⇑","OPS";case 94:return a.a="↑","OPS";case 95:return a.a=
+"↑","OPS";case 96:return a.a="⊵","OP";case 97:return a.a="⊴","OP";case 98:return a.a="⋃","OPM";case 99:return a.a="∪","OP";case 100:return"UNDERSET";case 101:return"UNDEROVERSET";case 102:return"UNDERLINE";case 103:return"UNDERBRACE";case 104:return a.a="⋰","OP";case 105:return"OP";case 106:return"OP";case 107:return"OP";case 108:return"OP";case 109:return"OP";case 110:return"OP";case 111:return"OP";case 112:return"OP";case 113:return"OP";case 114:return"OP";case 115:return"OP";case 116:return"OP";
+case 117:return"OP";case 118:return"OP";case 119:return"OP";case 120:return"OP";case 121:return"OP";case 122:return"OP";case 123:return"OP";case 124:return"OP";case 125:return"OP";case 126:return"OP";case 127:return"OP";case 128:return"OP";case 129:return"OP";case 130:return"OP";case 131:return"OP";case 132:return"OP";case 133:return"OP";case 134:return"OP";case 135:return"OP";case 136:return"OP";case 137:return"OP";case 138:return"OPFS";case 139:return"OPFS";case 140:return"OP";case 141:return"OP";
+case 142:return"OP";case 143:return"OP";case 144:return"OP";case 145:return"OP";case 146:return"OP";case 147:return"OP";case 148:return"OP";case 149:return"OP";case 150:return"OP";case 151:return"OP";case 152:return"OP";case 153:return"OP";case 154:return"OP";case 155:return"OP";case 156:return"OP";case 157:return"OP";case 158:return"OP";case 159:return"OP";case 160:return"OP";case 161:return a.a="⤖","OP";case 162:return a.a="↠","OPS";case 163:return a.a="↞","OPS";case 164:return a.a="∭","OP";case 165:return a.a=
+"⊵","OP";case 166:return a.a="▹","OP";case 167:return a.a="≜","OP";case 168:return a.a="⊴","OP";case 169:return a.a="◃","OP";case 170:return a.a="▿","OP";case 171:return a.a="▵","OP";case 172:return a.a="⤪","OP";case 173:return a.a="⤩","OP";case 174:return a.a="⊤","OP";case 175:return this.pushState("TEXTARG"),"TOOLTIP";case 176:return a.a="⤧","OP";case 177:return"TOGGLE";case 178:return a.a="⤨","OP";case 179:return a.a="→","OPS";case 180:return a.a="⊠","OP";case 181:return a.a="×","OP";case 182:return a.a=
+"Ëœ","ACCENTNS";case 183:return"THINSPACE";case 184:return"THICKSPACE";case 185:return a.a="∼","OP";case 186:return a.a="≈","OP";case 187:return a.a="Θ","AIUG";case 188:return a.a="θ","AILG";case 189:return a.a="∴","OP";case 190:return"TFRAC";case 191:return"TEXTSTYLE";case 192:return"TEXTSIZE";case 193:return a.a="â€","OPF";case 194:return a.a="“","OPF";case 195:return a.a="~","OPS";case 196:return a.a="`","OP";case 197:return a.a="^","OPS";case 198:return a.a="´","OP";case 199:return this.j("TEXTARG"),
+"MTEXT";case 200:return"TENSOR";case 201:return"TBINOM";case 202:return a.a="Τ","AIUG";case 203:return a.a="τ","AILG";case 204:return a.a="⇙","OPS";case 205:return a.a="↙","OPS";case 206:return a.a="⇙","OPS";case 207:return a.a="↙","OPS";case 208:return a.a="√","OPS";case 209:return a.a="⫌","OP";case 210:return a.a="⊋","OP";case 211:return a.a="⫆","OP";case 212:return a.a="⊇","OP";case 213:return a.a="⋑","OP";case 214:return a.a="⊃","OP";case 215:return a.a="∑","OPM";case 216:return a.a="≿","OP";
+case 217:return a.a="â‹©","OP";case 218:return a.a="⪶","OP";case 219:return a.a="⪺","OP";case 220:return a.a="⪰","OP";case 221:return a.a="≽","OP";case 222:return a.a="⪸","OP";case 223:return a.a="≻","OP";case 224:return"SUBSTACK";case 225:return a.a="â«‹","OP";case 226:return a.a="⊊","OP";case 227:return a.a="â«…","OP";case 228:return a.a="⊆","OP";case 229:return a.a="â‹","OP";case 230:return a.a="⊂","OP";case 231:return this.pushState("TEXTARG"),"STATUSLINE";case 232:return a.a="⋆","OP";case 233:return"OVERSET";
+case 234:return a.a="⫽","OP";case 235:return a.a="â–¡","OP";case 236:return a.a="⊒","OP";case 237:return a.a="âŠ","OP";case 238:return a.a="⊑","OP";case 239:return a.a="âŠ","OP";case 240:return this.pushState("OPTARG"),this.pushState("TRYOPTARG"),"SQRT";case 241:return a.a="⊔","OP";case 242:return a.a="⊓","OP";case 243:return a.a="∢","OP";case 244:return a.a="â™ ","OP";case 245:return this.pushState("TEXTARG"),this.pushState("TEXTARG"),this.pushState("TEXTARG"),"SPACE";case 246:return a.a="⌣","OP";case 247:return a.a=
+"⌣","OP";case 248:return a.a="∖","OP";case 249:return a.a="⌢","OP";case 250:return"SLASH";case 251:return a.a="≃","OP";case 252:return a.a="∼","OP";case 253:return a.a="Σ","AIUG";case 254:return a.a="σ","AILG";case 255:return a.a="⧢","OP";case 256:return a.a="∥","OP";case 257:return a.a="∣","OP";case 258:return a.a="♯","OP";case 259:return a.a="∖","OP";case 260:return a.a="⤭","OP";case 261:return a.a="⇘","OPS";case 262:return a.a="↘","OPS";case 263:return a.a="⇘","OPS";case 264:return a.a="↘","OPS";
+case 265:return"SCRIPTSIZE";case 266:return"SCRIPTSCRIPTSIZE";case 267:return a.a="⋊","OP";case 268:return a.a="↱","OPS";case 269:return a.a="⇛","OPS";case 270:return a.a="⟫","OPFS";case 271:return a.a="’","OPF";case 272:return this.j("TEXTARG"),"ROWSPAN";case 273:return"ROWOPTS";case 274:return this.pushState("TEXTARG"),"ROWLINES";case 275:return this.j("TEXTARG"),"ROWALIGN";case 276:return"ROOT";case 277:return a.a="⎱","OP";case 278:return a.a="≓","OP";case 279:return a.a="⟲","OP";case 280:return a.a=
+"â‹Œ","OP";case 281:return a.a="â†","OPS";case 282:return a.a="⇉","OPS";case 283:return a.a="⇌","OPS";case 284:return a.a="⇄","OPS";case 285:return a.a="⇀","OPS";case 286:return a.a="â‡","OPS";case 287:return a.a="⇾","OPS";case 288:return a.a="↣","OPS";case 289:return a.a="⇒","OPS";case 290:return a.a="→","OPS";case 291:return"RIGHT";case 292:return a.a="Ρ","AIUG";case 293:return a.a="Ï","AILG";case 294:return a.a="⊳","OP";case 295:return a.a="⌋","OPFS";case 296:return a.a="â„œ","A";case 297:return a.a=
+"⤰","OP";case 298:return a.a="⤫","OP";case 299:return a.a="⌉","OPFS";case 300:return a.a="]","OPFS";case 301:return a.a="}","OPFS";case 302:return a.a="⟩","OPFS";case 303:return a.a="⟩","OPFS";case 304:return a.a="≟","OP";case 305:return a.a="⨌","OP";case 306:return"QUAD";case 307:return"QQUAD";case 308:return a.a="â–ª","OP";case 309:return a.a="Ψ","AIUG";case 310:return a.a="ψ","AILG";case 311:return a.a="âˆ","OP";case 312:return a.a="âˆ","OPM";case 313:return a.a="âˆ","OPM";case 314:return a.a="′","OPP";
+case 315:return a.a="≾","OP";case 316:return a.a="⋨","OP";case 317:return a.a="⪵","OP";case 318:return a.a="⪹","OP";case 319:return a.a="⪯","OP";case 320:return a.a="≼","OP";case 321:return a.a="⪷","OP";case 322:return a.a="≺","OP";case 323:return"PMOD";case 324:return a.a="±","OP";case 325:return a.a="⨥","OP";case 326:return a.a="⊞","OP";case 327:return a.a="⋔","OP";case 328:return a.a="Π","AIUG";case 329:return a.a="π","AILG";case 330:return a.a="Φ","AIUG";case 331:return a.a="ϕ","AILG";case 332:return"PHANTOM";
+case 333:return a.a="⫫","OP";case 334:return a.a="⊥","OP";case 335:return a.a="⪣","OP";case 336:return a.a="∂","OP";case 337:return a.a="⅋","OP";case 338:return a.a="∥","OP";case 339:return this.pushState("TEXTARG"),"PADDING";case 340:return"OVERSET";case 341:return a.a="¯","ACCENT";case 342:return"OVERBRACE";case 343:return"TEXOVER";case 344:return a.a="⨴","OP";case 345:return a.a="⊗","OP";case 346:return a.a="⊘","OP";case 347:return"OPS";case 348:return"OPP";case 349:return"OPM";case 350:return a.a=
+"⨭","OP";case 351:return a.a="⊕","OP";case 352:return"OPFS";case 353:return"OPF";case 354:return this.j("TEXTARG"),"OPERATORNAME";case 355:return"OP";case 356:return a.a="⊖","OP";case 357:return a.a="â„´","A";case 358:return a.a="Ω","AIUG";case 359:return a.a="ω","AILG";case 360:return a.a="∮","OP";case 361:return a.a="∯","OP";case 362:return a.a="∰","OP";case 363:return a.a="⊙","OP";case 364:return a.a="âŠ","OP";case 365:return a.a="⦸","OP";case 366:return a.a="⤲","OP";case 367:return a.a="⇖","OPS";
+case 368:return a.a="↖","OPS";case 369:return a.a="⇖","OPS";case 370:return a.a="↖","OPS";case 371:return a.a="⊯","OP";case 372:return a.a="⊮","OP";case 373:return a.a="⊭","OP";case 374:return a.a="⊬","OP";case 375:return"NUM";case 376:return a.a="Î","AIUG";case 377:return a.a="ν","AILG";case 378:return a.a="â‹­","OP";case 379:return a.a="â‹«","OP";case 380:return a.a="⋬","OP";case 381:return a.a="⋪","OP";case 382:return a.a="⊉","OP";case 383:return a.a="⊅","OP";case 384:return a.a="≿̸","OP";case 385:return a.a=
+"⪰̸","OP";case 386:return a.a="âŠ","OP";case 387:return a.a="⊈","OP";case 388:return a.a="⊈","OP";case 389:return a.a="⊄","OP";case 390:return a.a="≄","OP";case 391:return a.a="â‰","OP";case 392:return a.a="∦","OP";case 393:return a.a="∤","OP";case 394:return a.a="â‡","OP";case 395:return a.a="↛","OP";case 396:return a.a="⪯̸","OP";case 397:return a.a="⊀","OP";case 398:return a.a="∦","OP";case 399:return a.a="∌","OP";case 400:return a.a="∉","OP";case 401:return a.a="¬","OP";case 402:return a.a="∤","OP";
+case 403:return a.a="≮","OP";case 404:return a.a="⩽̸","OP";case 405:return a.a="⩽̸","OP";case 406:return a.a="≰","OP";case 407:return a.a="⇎","OP";case 408:return a.a="↮","OP";case 409:return a.a="â‡","OP";case 410:return a.a="↚","OP";case 411:return a.a="∋","OP";case 412:return a.a="≯","OP";case 413:return a.a="⩾̸","OP";case 414:return a.a="⩾̸","OP";case 415:return a.a="≱","OP";case 416:return a.a="∄","OP";case 417:return a.a="≢","OP";case 418:return a.a="≂̸","OP";case 419:return a.a="≠","OP";case 420:return a.a=
+"⤮","OP";case 421:return a.a="⤱","OP";case 422:return"NEGTHICKSPACE";case 423:return"NEGSPACE";case 424:return"NEGMEDSPACE";case 425:return a.a="¬","OP";case 426:return a.a="⇗","OPS";case 427:return a.a="↗","OPS";case 428:return a.a="⇗","OPS";case 429:return a.a="↗","OPS";case 430:return a.a="≠","OP";case 431:return a.a="≇","OP";case 432:return a.a="≎̸","OP";case 433:return a.a="â‰Ì¸","OP";case 434:return a.a="â™®","OP";case 435:return a.a="≉","OP";case 436:return a.a="∇","OP";case 437:return"MULTI";
+case 438:return a.a="⊸","OP";case 439:return a.a="Μ","AIUG";case 440:return a.a="μ","AILG";case 441:return this.j("TEXTARG"),"MTEXT";case 442:return this.pushState("TEXTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),"MS";case 443:return a.a="∓","OP";case 444:return a.a="⊧","OP";case 445:return a.a="mod","MO";case 446:return this.pushState("TEXTARG"),"MO";case 447:return this.pushState("TEXTARG"),"MN";case 448:return a.a="⫛",
+"OP";case 449:return a.a="⨪","OP";case 450:return a.a="⊟","OP";case 451:return a.a="−","OP";case 452:return a.a=a.a.slice(1),"FM";case 453:return a.a="∣","OP";case 454:return this.pushState("TEXTARG"),"MI";case 455:return a.a="℧","A";case 456:return a.a="℧","A";case 457:return"MEDSPACE";case 458:return a.a="∡","OP";case 459:return"MATHTT";case 460:return"MATHSF";case 461:return"MATHSCR";case 462:return"MATHRM";case 463:return"MATHRLAP";case 464:return this.j("TEXTARG"),"MATHREL";case 465:return this.pushState("TEXTOPTARG"),
+this.pushState("TRYOPTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),this.pushState("TEXTARG"),"MATHRAISEBOX";case 466:return this.j("TEXTARG"),"MATHOP";case 467:return"MATHIT";case 468:return"MATHLLAP";case 469:return"MATHIT";case 470:return"MATHFRAK";case 471:return"MATHFRAK";case 472:return"MATHCLAP";case 473:return"MATHSCR";case 474:return"MATHBSCR";case 475:return"MATHBIT";case 476:return this.j("TEXTARG"),"MATHBIN";case 477:return"MATHBF";case 478:return"MATHBSCR";case 479:return"MATHBB";
+case 480:return a.a="⤇","OP";case 481:return a.a="↦","OPS";case 482:return a.a="⤆","OP";case 483:return a.a="↦","OPS";case 484:return a.a="≨︀","OP";case 485:return a.a="≨︀","OP";case 486:return a.a="⋉","OP";case 487:return a.a="<","OP";case 488:return a.a="↰","OPS";case 489:return a.a="‘","OPF";case 490:return a.a="◊","OP";case 491:return a.a="⨜","OP";case 492:return a.a="↬","OPS";case 493:return a.a="↫","OPS";case 494:return a.a="⟹","OPS";case 495:return a.a="⟶","OPS";case 496:return a.a="⟼","OPS";
+case 497:return a.a="⟺","OPS";case 498:return a.a="⟷","OPS";case 499:return a.a="⟸","OPS";case 500:return a.a="⟵","OPS";case 501:return a.a="⋦","OP";case 502:return a.a="≨","OP";case 503:return a.a="⪇","OP";case 504:return a.a="⪉","OP";case 505:return a.a="⎰","OP";case 506:return a.a="⋘","OP";case 507:return a.a="⇚","OPS";case 508:return a.a="⟪","OPFS";case 509:return a.a="≪","OP";case 510:return a.a="⊲","OP";case 511:return a.a="⌊","OPFS";case 512:return a.a="≲","OP";case 513:return a.a="≶","OP";
+case 514:return a.a="⪋","OP";case 515:return a.a="⋚","OP";case 516:return a.a="⋖","OP";case 517:return a.a="⪅","OP";case 518:return a.a="<","OP";case 519:return a.a="⩽","OP";case 520:return a.a="≦","OP";case 521:return a.a="≤","OP";case 522:return a.a="⟳","OP";case 523:return a.a="⋋","OP";case 524:return a.a="↜","OPS";case 525:return a.a="↭","OPS";case 526:return a.a="⇋","OPS";case 527:return a.a="⇿","OPS";case 528:return a.a="⇆","OPS";case 529:return a.a="⇔","OPS";case 530:return a.a="↔","OPS";case 531:return a.a=
+"⇇","OPS";case 532:return a.a="↼","OPS";case 533:return a.a="↽","OPS";case 534:return a.a="⇽","OPS";case 535:return a.a="↢","OPS";case 536:return a.a="â‡","OPS";case 537:return a.a="â†","OPS";case 538:return"LEFT";case 539:return a.a="≤","OP";case 540:return a.a="…","OP";case 541:return a.a="⌈","OPFS";case 542:return a.a="[","OPFS";case 543:return a.a="{","OPFS";case 544:return a.a="⟨","OPFS";case 545:return a.a="⟨","OPFS";case 546:return a.a="Λ","AIUG";case 547:return a.a="λ","AILG";case 548:return a.a=
+"∻","OP";case 549:return a.a="Κ","AIUG";case 550:return a.a="κ","AILG";case 551:return a.a="ȷ","AILL";case 552:return this.pushState("TEXTARG"),"MN";case 553:return a.a="Ι","AIUG";case 554:return a.a="ι","AILG";case 555:return a.a="⅋","OP";case 556:return a.a="⨘","OP";case 557:return a.a="⨽","OP";case 558:return a.a="⨼","OP";case 559:return a.a="⋂","OPM";case 560:return a.a="∩","OP";case 561:return a.a="⫴","OP";case 562:return a.a="⊺","OP";case 563:return a.a="∫","OP";case 564:return a.a="⨚","OP";
+case 565:return a.a="⨙","OP";case 566:return a.a="⨎","OP";case 567:return a.a="â¨","OP";case 568:return a.a="∫","OP";case 569:return a.a="∞","NUM";case 570:return a.a="∞","NUM";case 571:return a.a=a.a.slice(1),"FM";case 572:return a.a="∊","OP";case 573:return a.a="⇒","OPS";case 574:return a.a="â‡","OPS";case 575:return a.a="ı","AILL";case 576:return a.a="â„‘","A";case 577:return a.a="∬","OP";case 578:return a.a="∭","OP";case 579:return a.a="⨌","OP";case 580:return a.a="⟺","OPS";case 581:return a.a="â„",
+"A";case 582:return this.pushState("TEXTARG"),"HREF";case 583:return a.a="↪","OPS";case 584:return a.a="↩","OPS";case 585:return a.a="⤦","OP";case 586:return a.a="⤥","OP";case 587:return a.a="♡","OP";case 588:return a.a="â„","A";case 589:return a.a="^","ACCENTNS";case 590:return a.a="≩︀","OP";case 591:return a.a="≩︀","OP";case 592:return a.a="≳","OP";case 593:return a.a="≷","OP";case 594:return a.a="⪌","OP";case 595:return a.a="â‹›","OP";case 596:return a.a="â‹—","OP";case 597:return a.a="⪆","OP";case 598:return a.a=
+">","OP";case 599:return a.a=">","OP";case 600:return a.a="⋧","OP";case 601:return a.a="≩","OP";case 602:return a.a="⪈","OP";case 603:return a.a="⪊","OP";case 604:return a.a="ℷ","A";case 605:return a.a="⋙","OP";case 606:return a.a="≫","OP";case 607:return a.a="⩾","OP";case 608:return a.a="≧","OP";case 609:return a.a="≥","OP";case 610:return a.a="≥","OP";case 611:return a.a="Γ","AIUG";case 612:return a.a="γ","AILG";case 613:return a.a="⌢","OP";case 614:return this.pushState("TEXTARG"),"FRAME";case 615:return"FRAC";
+case 616:return a.a="â«","OP";case 617:return a.a="â«Ì¸","OP";case 618:return a.a="∀","OP";case 619:return a.a="â™­","OP";case 620:return a.a="⤬","OP";case 621:return a.a="⤯","OP";case 622:return a.a="≒","OP";case 623:return a.a="∃","OP";case 624:return a.a="ð","A";case 625:return a.a="ð","A";case 626:return a.a="Η","AIUG";case 627:return a.a="η","AILG";case 628:return a.a="≡","OP";case 629:return this.pushState("TEXTARG"),"EQROWS";case 630:return this.pushState("TEXTARG"),"EQCOLS";case 631:return a.a=
+"⪕","OP";case 632:return a.a="⪖","OP";case 633:return a.a="≂","OP";case 634:return a.a="=∷","OP";case 635:return a.a="≕","OP";case 636:return a.a="−∷","OP";case 637:return a.a="=∷","OP";case 638:return a.a="=∷","OP";case 639:return a.a="=∷","OP";case 640:return a.a="≕","OP";case 641:return a.a="≖","OP";case 642:return a.a="ϵ","AILG";case 643:return"EVVMATRIX";case 644:return"EVMATRIX";case 645:return"ETOGGLE";case 646:return"EALIGNED";case 647:return"ESMALLMATRIX";case 648:return"EPMATRIX";case 649:return"EMATRIX";
+case 650:return"EGATHERED";case 651:return"ECASES";case 652:return"EBBMATRIX";case 653:return"EBMATRIX";case 654:return"EARRAY";case 655:return"EALIGNED";case 656:return a.a="∅","A";case 657:return a.a="∅","A";case 658:return a.a="↪","OPS";case 659:return a.a="â„“","A";case 660:return a.a="↕","OPS";case 661:return a.a="⧟","OP";case 662:return a.a="â¤","OPS";case 663:return a.a="↕","OPS";case 664:return a.a="⇂","OPS";case 665:return a.a="⇃","OPS";case 666:return a.a="⇊","OPS";case 667:return a.a="⇓",
+"OPS";case 668:return a.a="↓","OPS";case 669:return a.a="∬","OP";case 670:return a.a="â©ž","OP";case 671:return a.a="⌆","OP";case 672:return a.a="…","OP";case 673:return a.a="∔","OP";case 674:return a.a="∸","OP";case 675:return a.a="≑","OP";case 676:return a.a="≑","OP";case 677:return a.a="â‰","OP";case 678:return a.a="Ë™","ACCENT";case 679:return a.a="⋇","OP";case 680:return a.a="÷","OP";case 681:return"DISPLAYSTYLE";case 682:return a.a="⨈","OPM";case 683:return a.a="Ï","A";case 684:return a.a="♢","OP";
+case 685:return a.a="⋄","OP";case 686:return a.a="⋄","OP";case 687:return a.a=a.a.slice(1),"FM";case 688:return a.a="Δ","AIUG";case 689:return a.a="δ","AILG";case 690:return a.a="∇","OP";case 691:return a.a="°","OP";case 692:return a.a="⤋","OPS";case 693:return a.a="⩷","OP";case 694:return a.a="⋱","OP";case 695:return a.a="̈","ACCENT";case 696:return a.a="⃛","OP";case 697:return a.a="⃛","ACCENT";case 698:return a.a="⃜","OP";case 699:return a.a="⃜","ACCENT";case 700:return a.a="‡","OP";case 701:return a.a=
+"∷","OP";case 702:return a.a="â¤","OPS";case 703:return a.a="⫤","OP";case 704:return a.a="â«£","OP";case 705:return a.a="⊣","OP";case 706:return a.a="â¤","OPS";case 707:return a.a="⤎","OPS";case 708:return a.a="↓","OPS";case 709:return a.a="ℸ","A";case 710:return a.a="†","OP";case 711:return a.a="↷","OP";case 712:return a.a="↶","OP";case 713:return a.a="⤻","OP";case 714:return a.a="â‹","OP";case 715:return a.a="â‹Ž","OP";case 716:return a.a="â‹Ÿ","OP";case 717:return a.a="â‹ž","OP";case 718:return a.a="âŠ","OP";
+case 719:return a.a="â‹“","OP";case 720:return a.a="∪","OP";case 721:return a.a="âˆ","OPM";case 722:return a.a="âˆ","OPM";case 723:return a.a="∮","OP";case 724:return a.a="⨇","OPM";case 725:return a.a="∮","OP";case 726:return a.a="≅","OP";case 727:return a.a="âˆ","OP";case 728:return this.j("TEXTARG"),"COLSPAN";case 729:return this.pushState("TEXTARG"),"COLOR";case 730:return a.a="∷∼","OP";case 731:return a.a="∶∼","OP";case 732:return a.a="â©´","OP";case 733:return a.a="≔","OP";case 734:return a.a="∷−",
+"OP";case 735:return a.a="≔","OP";case 736:return a.a="∷≈","OP";case 737:return a.a="∶≈","OP";case 738:return a.a="∷","OP";case 739:return a.a=":","OP";case 740:return this.pushState("TEXTARG"),"COLLINES";case 741:return this.pushState("TEXTARG"),"COLLAYOUT";case 742:return this.j("TEXTARG"),"COLALIGN";case 743:return a.a="♣","OP";case 744:return a.a="¯","ACCENT";case 745:return a.a="âŠ","OP";case 746:return a.a="⊚","OP";case 747:return a.a="⊛","OP";case 748:return a.a="â¥","OP";case 749:return a.a=
+"⥀","OP";case 750:return a.a="≗","OP";case 751:return a.a="∘","OP";case 752:return"TEXCHOOSE";case 753:return a.a="χ","AILG";case 754:return a.a="ˇ","ACCENTNS";case 755:return"CELLOPTS";case 756:return a.a="⋯","OP";case 757:return a.a="·","OP";case 758:return a.a="â‹…","OP";case 759:return a.a="â‹’","OP";case 760:return a.a="∩","OP";case 761:return a.a="⪮","OP";case 762:return a.a="≎","OP";case 763:return a.a="â‰","OP";case 764:return a.a="•","OP";case 765:return a.a="⨲","OP";case 766:return a.a="⊠","OP";
+case 767:return a.a="⊞","OP";case 768:return a.a="⊟","OP";case 769:return"BOXED";case 770:return a.a="⊡","OP";case 771:return a.a="⧄","OP";case 772:return a.a="⧇","OP";case 773:return a.a="⧅","OP";case 774:return a.a="⧆","OP";case 775:return a.a="□","OP";case 776:return a.a="⋈","OP";case 777:return a.a="⊥","OP";case 778:return a.a="⊥","OP";case 779:return"MATHBF";case 780:return a.a="▸","OP";case 781:return a.a="◂","OP";case 782:return a.a="▾","OP";case 783:return a.a="▴","OP";case 784:return a.a=
+"â– ","OP";case 785:return a.a="⧫","OP";case 786:return a.a="â¤","OPS";case 787:return"BINOM";case 788:return a.a="â‹€","OPM";case 789:return a.a="â‹","OPM";case 790:return a.a="⨄","OPM";case 791:return a.a="â–³","OP";case 792:return a.a="â–½","OP";case 793:return a.a="⨉","OPM";case 794:return a.a="★","OP";case 795:return a.a="⨆","OPM";case 796:return a.a="⨅","OPM";case 797:return"BBIG";case 798:return"BIG";case 799:return a.a="⨂","OPM";case 800:return a.a="â¨","OPM";case 801:return a.a="⨀","OPM";case 802:return"BBIGL";
+case 803:return"BIGL";case 804:return a.a="⫼","OPM";case 805:return"BBIGG";case 806:return"BIGG";case 807:return"BBIGGL";case 808:return"BIGGL";case 809:return"BBIGG";case 810:return"BIGG";case 811:return a.a="⨃","OPM";case 812:return a.a="⋃","OPM";case 813:return a.a="○","OP";case 814:return a.a="⋂","OPM";case 815:return"BBIG";case 816:return"BIG";case 817:return this.pushState("TEXTARG"),"BGCOLOR";case 818:return a.a="≬","OP";case 819:return a.a="ℶ","A";case 820:return a.a="Β","AIUG";case 821:return a.a=
+"β","AILG";case 822:return"BVVMATRIX";case 823:return"BVMATRIX";case 824:return"BTOGGLE";case 825:return"BALIGNED";case 826:return"BSMALLMATRIX";case 827:return"BPMATRIX";case 828:return"BMATRIX";case 829:return"BGATHERED";case 830:return"BCASES";case 831:return"BBBMATRIX";case 832:return"BBMATRIX";case 833:return this.pushState("TEXTARG"),this.pushState("TEXTOPTARG"),this.pushState("TRYOPTARG"),"BARRAY";case 834:return"BALIGNED";case 835:return a.a="∵","OP";case 836:return a.a="ℿ","A";case 837:return a.a=
+"⌅","OP";case 838:return a.a="¯","ACCENTNS";case 839:return a.a="\\","OP";case 840:return a.a="â‹","OP";case 841:return a.a="∽","OP";case 842:return a.a="‵","OPP";case 843:return a.a="϶","OP";case 844:return"TEXATOP";case 845:return a.a="â‰","OP";case 846:return a.a="∗","OP";case 847:return"ARRAYOPTS";case 848:return"ARRAY";case 849:return a.a=a.a.slice(1),"F";case 850:return a.a="≊","OP";case 851:return a.a="≈","OP";case 852:return a.a="∠","OP";case 853:return a.a="⨿","OP";case 854:return a.a="Α",
+"AIUG";case 855:return a.a="α","AILG";case 856:return this.pushState("TEXTARG"),"ALIGN";case 857:return a.a="ℵ","A";case 858:return"AIUL";case 859:return"AIUG";case 860:return"AILL";case 861:return"AILG";case 862:return a.a="⋰","OP";case 863:return a.a="Å","A";case 864:return"A";case 865:return a.a="$","A";case 866:return a.a="}","OPFS";case 867:return a.a="‖","OPFS";case 868:return a.a="{","OPFS";case 869:return"THICKSPACE";case 870:return"MEDSPACE";case 871:return"THINSPACE";case 872:return a.a=
+"&","A";case 873:return a.a="%","A";case 874:return a.a="#","OP";case 875:return"NEGSPACE";case 876:return a.a="−","OP";case 877:return a.a="â—","OPP";case 878:return a.a="‴","OPP";case 879:return a.a="″","OPP";case 880:return a.a="′","OPP";case 881:return"HIGH_SURROGATE";case 882:return"LOW_SURROGATE";case 883:return"BMP_CHARACTER"}},rules:[/^(?:.)/,/^(?:\$\$|\\\[|\$|\\\()/,/^(?:$)/,/^(?:\\[$\\])/,/^(?:[<&>])/,/^(?:[^])/,/^(?:\s*\[)/,/^(?:.)/,/^(?:([^\\\]]|(\\[\\\]]))+)/,/^(?:\])/,/^(?:\s*\{)/,/^(?:([^\\\}]|(\\[\\\}]))+)/,
+/^(?:\})/,/^(?:\])/,/^(?:\s+)/,/^(?:\$\$|\\\]|\$|\\\))/,/^(?:\{)/,/^(?:\})/,/^(?:\^)/,/^(?:_)/,/^(?:\.)/,/^(?:&)/,/^(?:\\\\)/,/^(?:[0-9]+(?:\.[0-9]+)?|[\u0660-\u0669]+(?:\u066B[\u0660-\u0669]+)?|(?:\uD835[\uDFCE-\uDFD7])+|(?:\uD835[\uDFD8-\uDFE1])+|(?:\uD835[\uDFE2-\uDFEB])+|(?:\uD835[\uDFEC-\uDFF5])+|(?:\uD835[\uDFF6-\uDFFF])+)/,/^(?:[a-zA-Z]+)/,/^(?:\\Zeta)/,/^(?:\\zeta)/,/^(?:\\xrightleftharpoons)/,/^(?:\\xRightarrow)/,/^(?:\\xrightarrow)/,/^(?:\\xmapsto)/,/^(?:\\xleftrightharpoons)/,/^(?:\\xLeftrightarrow)/,
+/^(?:\\xleftrightarrow)/,/^(?:\\xLeftarrow)/,/^(?:\\xleftarrow)/,/^(?:\\Xi)/,/^(?:\\xi)/,/^(?:\\xhookrightarrow)/,/^(?:\\xhookleftarrow)/,/^(?:\\wr)/,/^(?:\\wp)/,/^(?:\\widevec)/,/^(?:\\widetilde)/,/^(?:\\widehat)/,/^(?:\\widecheck)/,/^(?:\\widebar)/,/^(?:\\wedgeq)/,/^(?:\\Wedge)/,/^(?:\\wedge)/,/^(?:\\Vvert)/,/^(?:\\Vvdash)/,/^(?:\\Vert)/,/^(?:\\vert)/,/^(?:\\veebar)/,/^(?:\\Vee)/,/^(?:\\vee)/,/^(?:\\vec)/,/^(?:\\vdots)/,/^(?:\\VDash)/,/^(?:\\Vdash)/,/^(?:\\vDash)/,/^(?:\\vdash)/,/^(?:\\Vbar)/,/^(?:\\vartriangleright)/,
+/^(?:\\vartriangleleft)/,/^(?:\\vartriangle)/,/^(?:\\vartheta)/,/^(?:\\varsupsetneqq)/,/^(?:\\varsupsetneq)/,/^(?:\\varsubsetneqq)/,/^(?:\\varsubsetneqq)/,/^(?:\\varsubsetneq)/,/^(?:\\varsigma)/,/^(?:\\varrho)/,/^(?:\\varpropto)/,/^(?:\\varpi)/,/^(?:\\varphi)/,/^(?:\\varnothing)/,/^(?:\\varkappa)/,/^(?:\\varepsilon)/,/^(?:\\Uuparrow)/,/^(?:\\upuparrows)/,/^(?:\\Upsilon)/,/^(?:\\upsilon)/,/^(?:\\Upsi)/,/^(?:\\uplus)/,/^(?:\\upint)/,/^(?:\\upharpoonright)/,/^(?:\\upharpoonleft)/,/^(?:\\Updownarrow)/,
+/^(?:\\updownarrow)/,/^(?:\\updarr)/,/^(?:\\Uparrow)/,/^(?:\\uparrow)/,/^(?:\\uparr)/,/^(?:\\unrhd)/,/^(?:\\unlhd)/,/^(?:\\Union)/,/^(?:\\union)/,/^(?:\\underset)/,/^(?:\\underoverset)/,/^(?:\\underline)/,/^(?:\\underbrace)/,/^(?:\\udots)/,/^(?:\u2ADD\u0338)/,/^(?:\u2ACC\uFE00)/,/^(?:\u2ACB\uFE00)/,/^(?:\u2AB0\u0338)/,/^(?:\u2AAF\u0338)/,/^(?:\u2AA2\u0338)/,/^(?:\u2AA1\u0338)/,/^(?:\u2A7E\u0338)/,/^(?:\u2A7D\u0338)/,/^(?:\u29D0\u0338)/,/^(?:\u29CF\u0338)/,/^(?:\u2290\u0338)/,/^(?:\u228F\u0338)/,/^(?:\u228B\uFE00)/,
+/^(?:\u228A\uFE00)/,/^(?:\u2283\u20D2)/,/^(?:\u2282\u20D2)/,/^(?:\u227F\u0338)/,/^(?:\u226B\u0338)/,/^(?:\u226A\u0338)/,/^(?:\u2269\uFE00)/,/^(?:\u2268\uFE00)/,/^(?:\u2266\u0338)/,/^(?:\u224F\u0338)/,/^(?:\u224E\u0338)/,/^(?:\u2242\u0338)/,/^(?:\u223D\u0331)/,/^(?:\u2237\u2248)/,/^(?:\u2237\u223C)/,/^(?:\u2237\u2212)/,/^(?:\u2236\u2248)/,/^(?:\u2236\u223C)/,/^(?:\u2212\u2237)/,/^(?:\u007C\u007C\u007C)/,/^(?:\u007C\u007C)/,/^(?:\u003E\u003D)/,/^(?:\u003D\u2237)/,/^(?:\u003D\u2237)/,/^(?:\u003D\u003D)/,
+/^(?:\u003C\u003E)/,/^(?:\u003C\u003D)/,/^(?:\u003A\u003D)/,/^(?:\u002F\u003D)/,/^(?:\u002F\u002F)/,/^(?:\u002E\u002E\u002E)/,/^(?:\u002E\u002E)/,/^(?:\u002D\u003E)/,/^(?:\u002D\u003D)/,/^(?:\u002D\u002D)/,/^(?:\u002B\u003D)/,/^(?:\u002B\u002B)/,/^(?:\u002A\u003D)/,/^(?:\u002A\u002A)/,/^(?:\u0026\u0026)/,/^(?:\u0021\u003D)/,/^(?:\u0021\u0021)/,/^(?:\\twoheadrightarrowtail)/,/^(?:\\twoheadrightarrow)/,/^(?:\\twoheadleftarrow)/,/^(?:\\tripleintegral)/,/^(?:\\trianglerighteq)/,/^(?:\\triangleright)/,
+/^(?:\\triangleq)/,/^(?:\\trianglelefteq)/,/^(?:\\triangleleft)/,/^(?:\\triangledown)/,/^(?:\\triangle)/,/^(?:\\towa)/,/^(?:\\tosa)/,/^(?:\\top)/,/^(?:\\tooltip)/,/^(?:\\tona)/,/^(?:\\toggle)/,/^(?:\\toea)/,/^(?:\\to)/,/^(?:\\timesb)/,/^(?:\\times)/,/^(?:\\tilde)/,/^(?:\\thinspace)/,/^(?:\\thickspace)/,/^(?:\\thicksim)/,/^(?:\\thickapprox)/,/^(?:\\Theta)/,/^(?:\\theta)/,/^(?:\\therefore)/,/^(?:\\tfrac)/,/^(?:\\textstyle)/,/^(?:\\textsize)/,/^(?:\\textquotedblright)/,/^(?:\\textquotedblleft)/,/^(?:\\textasciitilde)/,
+/^(?:\\textasciigrave)/,/^(?:\\textasciicircumflex)/,/^(?:\\textasciiacute)/,/^(?:\\text)/,/^(?:\\tensor)/,/^(?:\\tbinom)/,/^(?:\\Tau)/,/^(?:\\tau)/,/^(?:\\swArrow)/,/^(?:\\swarrow)/,/^(?:\\swArr)/,/^(?:\\swarr)/,/^(?:\\surd)/,/^(?:\\supsetneqq)/,/^(?:\\supsetneq)/,/^(?:\\supseteqq)/,/^(?:\\supseteq)/,/^(?:\\Supset)/,/^(?:\\supset)/,/^(?:\\sum)/,/^(?:\\succsim)/,/^(?:\\succnsim)/,/^(?:\\succneqq)/,/^(?:\\succnapprox)/,/^(?:\\succeq)/,/^(?:\\succcurlyeq)/,/^(?:\\succapprox)/,/^(?:\\succ)/,/^(?:\\substack)/,
+/^(?:\\subsetneqq)/,/^(?:\\subsetneq)/,/^(?:\\subseteqq)/,/^(?:\\subseteq)/,/^(?:\\Subset)/,/^(?:\\subset)/,/^(?:\\statusline)/,/^(?:\\star)/,/^(?:\\stackrel)/,/^(?:\\sslash)/,/^(?:\\square)/,/^(?:\\sqsupseteq)/,/^(?:\\sqsupset)/,/^(?:\\sqsubseteq)/,/^(?:\\sqsubset)/,/^(?:\\sqrt)/,/^(?:\\sqcup)/,/^(?:\\sqcap)/,/^(?:\\sphericalangle)/,/^(?:\\spadesuit)/,/^(?:\\space)/,/^(?:\\smile)/,/^(?:\\smallsmile)/,/^(?:\\smallsetminus)/,/^(?:\\smallfrown)/,/^(?:\\slash)/,/^(?:\\simeq)/,/^(?:\\sim)/,/^(?:\\Sigma)/,
+/^(?:\\sigma)/,/^(?:\\shuffle)/,/^(?:\\shortparallel)/,/^(?:\\shortmid)/,/^(?:\\sharp)/,/^(?:\\setminus)/,/^(?:\\seovnearrow)/,/^(?:\\seArrow)/,/^(?:\\searrow)/,/^(?:\\seArr)/,/^(?:\\searr)/,/^(?:\\scriptsize)/,/^(?:\\scriptscriptsize)/,/^(?:\\rtimes)/,/^(?:\\Rsh)/,/^(?:\\Rrightarrow)/,/^(?:\\rrangle)/,/^(?:\\rq)/,/^(?:\\rowspan)/,/^(?:\\rowopts)/,/^(?:\\rowlines)/,/^(?:\\rowalign)/,/^(?:\\root)/,/^(?:\\rmoustache)/,/^(?:\\risingdotseq)/,/^(?:\\righttoleftarrow)/,/^(?:\\rightthreetimes)/,/^(?:\\rightsquigarrow)/,
+/^(?:\\rightrightarrows)/,/^(?:\\rightleftharpoons)/,/^(?:\\rightleftarrows)/,/^(?:\\rightharpoonup)/,/^(?:\\rightharpoondown)/,/^(?:\\rightarrowtriangle)/,/^(?:\\rightarrowtail)/,/^(?:\\Rightarrow)/,/^(?:\\rightarrow)/,/^(?:\\right)/,/^(?:\\Rho)/,/^(?:\\rho)/,/^(?:\\rhd)/,/^(?:\\rfloor)/,/^(?:\\Re)/,/^(?:\\rdiagovsearrow)/,/^(?:\\rdiagovfdiag)/,/^(?:\\rceil)/,/^(?:\\rbrack)/,/^(?:\\rbrace)/,/^(?:\\rangle)/,/^(?:\\rang)/,/^(?:\\questeq)/,/^(?:\\quadrupleintegral)/,/^(?:\\quad)/,/^(?:\\qquad)/,/^(?:\\qed)/,
+/^(?:\\Psi)/,/^(?:\\psi)/,/^(?:\\propto)/,/^(?:\\product)/,/^(?:\\prod)/,/^(?:\\prime)/,/^(?:\\precsim)/,/^(?:\\precnsim)/,/^(?:\\precneqq)/,/^(?:\\precnapprox)/,/^(?:\\preceq)/,/^(?:\\preccurlyeq)/,/^(?:\\precapprox)/,/^(?:\\prec)/,/^(?:\\pmod)/,/^(?:\\pm)/,/^(?:\\plusdot)/,/^(?:\\plusb)/,/^(?:\\pitchfork)/,/^(?:\\Pi)/,/^(?:\\pi)/,/^(?:\\Phi)/,/^(?:\\phi)/,/^(?:\\phantom)/,/^(?:\\Perp)/,/^(?:\\perp)/,/^(?:\\partialmeetcontraction)/,/^(?:\\partial)/,/^(?:\\parr)/,/^(?:\\parallel)/,/^(?:\\padding)/,
+/^(?:\\overset)/,/^(?:\\overline)/,/^(?:\\overbrace)/,/^(?:\\over)/,/^(?:\\Otimes)/,/^(?:\\otimes)/,/^(?:\\oslash)/,/^(?:[\u007E\u00AF\u02C6\u02C7\u02C9\u02CD\u02DC\u02F7\u0302\u203E\u2044\u2190-\u2199\u219C-\u21AD\u21AF-\u21B5\u21B9\u21BC-\u21CC\u21D0-\u21DD\u21E0-\u21F0\u21F3\u21F5\u21F6\u21FD-\u21FF\u2215\u221A\u23B4\u23B5\u23DC-\u23E1\u27F0\u27F1\u27F5-\u27FF\u290A-\u2910\u2912\u2913\u2921\u2922\u294E-\u2961\u296E\u296F\u2B45\u2B46])/,/^(?:[\u2032-\u2035\u2057])/,/^(?:[\u220F-\u2211\u22C0-\u22C3\u2A00-\u2A0A\u2A10-\u2A14\u2AFC\u2AFF])/,
+/^(?:\\Oplus)/,/^(?:\\oplus)/,/^(?:[\u0028\u0029\u005B\u005D\u007C\u2016\u2308-\u230B\u2329\u232A\u2772\u2773\u27E6-\u27EF\u2980\u2983-\u2998\u29FC\u29FD])/,/^(?:[\u2018\u2019\u201C\u201D])/,/^(?:\\operatorname)/,/^(?:[\u0021-\u0023\u002A-\u002C\u002F\u003A-\u0040\u0060\u00A8\u00AA\u00AC\u00B0-\u00B4\u00B7-\u00BA\u00D7\u00F7\u02CA\u02CB\u02D8-\u02DA\u02DD\u0311\u03F6\u201A\u201B\u201E-\u2022\u2026\u2036\u2037\u2043\u2061-\u2064\u20DB\u20DC\u2145\u2146\u214B\u219A\u219B\u21AE\u21B6-\u21B8\u21BA\u21BB\u21CD-\u21CF\u21DE\u21DF\u21F1\u21F2\u21F4\u21F7-\u21FC\u2200-\u2204\u2206-\u220E\u2212-\u2214\u2216-\u2219\u221B-\u221D\u221F-\u22BF\u22C4-\u22FF\u2305\u2306\u2322\u2323\u23B0\u23B1\u25A0\u25A1\u25AA\u25AB\u25AD-\u25B9\u25BC-\u25CF\u25D6\u25D7\u25E6\u2605\u2660-\u2663\u266D-\u266F\u2758\u27F2\u27F3\u2900-\u2909\u2911\u2914-\u2920\u2923-\u294D\u2962-\u296D\u2970-\u297F\u2981\u2982\u2999-\u29D9\u29DB-\u29FB\u29FE\u29FF\u2A0B-\u2A0F\u2A15-\u2ADB\u2ADD-\u2AFB\u2AFD\u2AFE])/,
+/^(?:\\ominus)/,/^(?:\\omicron)/,/^(?:\\Omega)/,/^(?:\\omega)/,/^(?:\\oint)/,/^(?:\\oiint)/,/^(?:\\oiiint)/,/^(?:\\odot)/,/^(?:\\odash)/,/^(?:\\obslash)/,/^(?:\\nwovnearrow)/,/^(?:\\nwArrow)/,/^(?:\\nwarrow)/,/^(?:\\nwArr)/,/^(?:\\nwarr)/,/^(?:\\nVDash)/,/^(?:\\nVdash)/,/^(?:\\nvDash)/,/^(?:\\nvdash)/,/^(?:\u221E)/,/^(?:\\Nu)/,/^(?:\\nu)/,/^(?:\\ntrianglerighteq)/,/^(?:\\ntriangleright)/,/^(?:\\ntrianglelefteq)/,/^(?:\\ntriangleleft)/,/^(?:\\nsupseteq)/,/^(?:\\nsupset)/,/^(?:\\nsuccsim)/,/^(?:\\nsucceq)/,
+/^(?:\\nsucc)/,/^(?:\\nsubseteqq)/,/^(?:\\nsubseteq)/,/^(?:\\nsubset)/,/^(?:\\nsime)/,/^(?:\\nsim)/,/^(?:\\nshortparallel)/,/^(?:\\nshortmid)/,/^(?:\\nRightarrow)/,/^(?:\\nrightarrow)/,/^(?:\\npreceq)/,/^(?:\\nprec)/,/^(?:\\nparallel)/,/^(?:\\notni)/,/^(?:\\notin)/,/^(?:\\not)/,/^(?:\\nmid)/,/^(?:\\nless)/,/^(?:\\nleqslant)/,/^(?:\\nleqq)/,/^(?:\\nleq)/,/^(?:\\nLeftrightarrow)/,/^(?:\\nleftrightarrow)/,/^(?:\\nLeftarrow)/,/^(?:\\nleftarrow)/,/^(?:\\ni)/,/^(?:\\ngtr)/,/^(?:\\ngeqslant)/,/^(?:\\ngeqq)/,
+/^(?:\\ngeq)/,/^(?:\\nexists)/,/^(?:\\nequiv)/,/^(?:\\neqsim)/,/^(?:\\neq)/,/^(?:\\neovsearrow)/,/^(?:\\neovnwarrow)/,/^(?:\\negthickspace)/,/^(?:\\negspace)/,/^(?:\\negmedspace)/,/^(?:\\neg)/,/^(?:\\neArrow)/,/^(?:\\nearrow)/,/^(?:\\neArr)/,/^(?:\\nearr)/,/^(?:\\ne)/,/^(?:\\ncong)/,/^(?:\\nBumpeq)/,/^(?:\\nbumpeq)/,/^(?:\\natural)/,/^(?:\\napprox)/,/^(?:\\nabla)/,/^(?:\\multiscripts)/,/^(?:\\multimap)/,/^(?:\\Mu)/,/^(?:\\mu)/,/^(?:\\mtext)/,/^(?:\\ms)/,/^(?:\\mp)/,/^(?:\\models)/,/^(?:\\mod)/,/^(?:\\mo)/,
+/^(?:\\mn)/,/^(?:\\mlcp)/,/^(?:\\minusdot)/,/^(?:\\minusb)/,/^(?:\\minus)/,/^(?:\\min)/,/^(?:\\mid)/,/^(?:\\mi)/,/^(?:\\mho)/,/^(?:\\mho)/,/^(?:\\medspace)/,/^(?:\\measuredangle)/,/^(?:\\mathtt)/,/^(?:\\mathsf)/,/^(?:\\mathscr)/,/^(?:\\mathrm)/,/^(?:\\mathrlap)/,/^(?:\\mathrel)/,/^(?:\\mathraisebox)/,/^(?:\\mathop)/,/^(?:\\mathmit)/,/^(?:\\mathllap)/,/^(?:\\mathit)/,/^(?:\\mathfrak)/,/^(?:\\mathfr)/,/^(?:\\mathclap)/,/^(?:\\mathcal)/,/^(?:\\mathbscr)/,/^(?:\\mathbit)/,/^(?:\\mathbin)/,/^(?:\\mathbf)/,
+/^(?:\\mathbcal)/,/^(?:\\mathbb)/,/^(?:\\Mapsto)/,/^(?:\\mapsto)/,/^(?:\\Mapsfrom)/,/^(?:\\map)/,/^(?:\\lvertneqq)/,/^(?:\\lvertneqq)/,/^(?:\\ltimes)/,/^(?:\\lt)/,/^(?:\\Lsh)/,/^(?:\\lq)/,/^(?:\\lozenge)/,/^(?:\\lowint)/,/^(?:\\looparrowright)/,/^(?:\\looparrowleft)/,/^(?:\\Longrightarrow)/,/^(?:\\longrightarrow)/,/^(?:\\longmapsto)/,/^(?:\\Longleftrightarrow)/,/^(?:\\longleftrightarrow)/,/^(?:\\Longleftarrow)/,/^(?:\\longleftarrow)/,/^(?:\\lnsim)/,/^(?:\\lneqq)/,/^(?:\\lneq)/,/^(?:\\lnapprox)/,/^(?:\\lmoustache)/,
+/^(?:\\lll)/,/^(?:\\Lleftarrow)/,/^(?:\\llangle)/,/^(?:\\ll)/,/^(?:\\lhd)/,/^(?:\\lfloor)/,/^(?:\\lesssim)/,/^(?:\\lessgtr)/,/^(?:\\lesseqqgtr)/,/^(?:\\lesseqgtr)/,/^(?:\\lessdot)/,/^(?:\\lessapprox)/,/^(?:\\less)/,/^(?:\\leqslant)/,/^(?:\\leqq)/,/^(?:\\leq)/,/^(?:\\lefttorightarrow)/,/^(?:\\leftthreetimes)/,/^(?:\\leftsquigarrow)/,/^(?:\\leftrightsquigarrow)/,/^(?:\\leftrightharpoons)/,/^(?:\\leftrightarrowtria\*)/,/^(?:\\leftrightarrows)/,/^(?:\\Leftrightarrow)/,/^(?:\\leftrightarrow)/,/^(?:\\leftleftarrows)/,
+/^(?:\\leftharpoonup)/,/^(?:\\leftharpoondown)/,/^(?:\\leftarrowtriangle)/,/^(?:\\leftarrowtail)/,/^(?:\\Leftarrow)/,/^(?:\\leftarrow)/,/^(?:\\left)/,/^(?:\\le)/,/^(?:\\ldots)/,/^(?:\\lceil)/,/^(?:\\lbrack)/,/^(?:\\lbrace)/,/^(?:\\langle)/,/^(?:\\lang)/,/^(?:\\Lambda)/,/^(?:\\lambda)/,/^(?:\\kernelcontraction)/,/^(?:\\Kappa)/,/^(?:\\kappa)/,/^(?:\\jmath)/,/^(?:\\itexnum)/,/^(?:\\Iota)/,/^(?:\\iota)/,/^(?:\\invamp)/,/^(?:\\intx)/,/^(?:\\intprodr)/,/^(?:\\intprod)/,/^(?:\\Intersection)/,/^(?:\\intersection)/,
+/^(?:\\interleave)/,/^(?:\\intercal)/,/^(?:\\integral)/,/^(?:\\intcup)/,/^(?:\\intcap)/,/^(?:\\intBar)/,/^(?:\\intbar)/,/^(?:\\int)/,/^(?:\\infty)/,/^(?:\\infinity)/,/^(?:\\inf)/,/^(?:\\in)/,/^(?:\\implies)/,/^(?:\\impliedby)/,/^(?:\\imath)/,/^(?:\\Im)/,/^(?:\\iint)/,/^(?:\\iiint)/,/^(?:\\iiiint)/,/^(?:\\iff)/,/^(?:\\hslash)/,/^(?:\\href)/,/^(?:\\hookrightarrow)/,/^(?:\\hookleftarrow)/,/^(?:\\hkswarow)/,/^(?:\\hksearow)/,/^(?:\\heartsuit)/,/^(?:\\hbar)/,/^(?:\\hat)/,/^(?:\\gvertneqq)/,/^(?:\\gvertneqq)/,
+/^(?:\\gtrsim)/,/^(?:\\gtrless)/,/^(?:\\gtreqqless)/,/^(?:\\gtreqless)/,/^(?:\\gtrdot)/,/^(?:\\gtrapprox)/,/^(?:\\gt)/,/^(?:\\greater)/,/^(?:\\gnsim)/,/^(?:\\gneqq)/,/^(?:\\gneq)/,/^(?:\\gnapprox)/,/^(?:\\gimel)/,/^(?:\\ggg)/,/^(?:\\gg)/,/^(?:\\geqslant)/,/^(?:\\geqq)/,/^(?:\\geq)/,/^(?:\\ge)/,/^(?:\\Gamma)/,/^(?:\\gamma)/,/^(?:\\frown)/,/^(?:\\frame)/,/^(?:\\frac)/,/^(?:\\forksnot)/,/^(?:\\forks)/,/^(?:\\forall)/,/^(?:\\flat)/,/^(?:\\fdiagovrdiag)/,/^(?:\\fdiagovnearrow)/,/^(?:\\fallingdotseq)/,
+/^(?:\\exists)/,/^(?:\\eth)/,/^(?:\\eth)/,/^(?:\\Eta)/,/^(?:\\eta)/,/^(?:\\equiv)/,/^(?:\\equalrows)/,/^(?:\\equalcols)/,/^(?:\\eqslantless)/,/^(?:\\eqslantgtr)/,/^(?:\\eqsim)/,/^(?:\\Eqqcolon)/,/^(?:\\eqqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\Eqcolon)/,/^(?:\\eqcolon)/,/^(?:\\eqcirc)/,/^(?:\\epsilon)/,/^(?:\\end\{Vmatrix\})/,/^(?:\\end\{vmatrix\})/,/^(?:\\endtoggle)/,/^(?:\\end\{split\})/,/^(?:\\end\{smallmatrix\})/,/^(?:\\end\{pmatrix\})/,/^(?:\\end\{matrix\})/,/^(?:\\end\{gathered\})/,
+/^(?:\\end\{cases\})/,/^(?:\\end\{Bmatrix\})/,/^(?:\\end\{bmatrix\})/,/^(?:\\end\{array\})/,/^(?:\\end\{aligned\})/,/^(?:\\emptyset)/,/^(?:\\empty)/,/^(?:\\embedsin)/,/^(?:\\ell)/,/^(?:\\duparr)/,/^(?:\\dualmap)/,/^(?:\\drbkarrow)/,/^(?:\\downuparrow)/,/^(?:\\downharpoonright)/,/^(?:\\downharpoonleft)/,/^(?:\\downdownarrows)/,/^(?:\\Downarrow)/,/^(?:\\downarrow)/,/^(?:\\doubleintegral)/,/^(?:\\doublebarwedge)/,/^(?:\\doublebarwedge)/,/^(?:\\dots)/,/^(?:\\dotplus)/,/^(?:\\dotminus)/,/^(?:\\doteqdot)/,
+/^(?:\\Doteq)/,/^(?:\\doteq)/,/^(?:\\dot)/,/^(?:\\divideontimes)/,/^(?:\\div)/,/^(?:\\displaystyle)/,/^(?:\\disjquant)/,/^(?:\\digamma)/,/^(?:\\diamondsuit)/,/^(?:\\Diamond)/,/^(?:\\diamond)/,/^(?:\\det|\\gcd|\\liminf|\\limsup|\\lim|\\max|\\Pr|\\sup)/,/^(?:\\Delta)/,/^(?:\\delta)/,/^(?:\\Del)/,/^(?:\\degree)/,/^(?:\\Ddownarrow)/,/^(?:\\ddotseq)/,/^(?:\\ddots)/,/^(?:\\ddot)/,/^(?:\\dddot)/,/^(?:\\dddot)/,/^(?:\\ddddot)/,/^(?:\\ddddot)/,/^(?:\\ddagger)/,/^(?:\\dblcolon)/,/^(?:\\dbkarow)/,/^(?:\\Dashv)/,
+/^(?:\\dashV)/,/^(?:\\dashv)/,/^(?:\\dashrightarrow)/,/^(?:\\dashleftarrow)/,/^(?:\\darr)/,/^(?:\\daleth)/,/^(?:\\dagger)/,/^(?:\\curvearrowright)/,/^(?:\\curvearrowleft)/,/^(?:\\curvearrowbotright)/,/^(?:\\curlywedge)/,/^(?:\\curlyvee)/,/^(?:\\curlyeqsucc)/,/^(?:\\curlyeqprec)/,/^(?:\\cupdot)/,/^(?:\\Cup)/,/^(?:\\cup)/,/^(?:\\coproduct)/,/^(?:\\coprod)/,/^(?:\\contourintegral)/,/^(?:\\conjquant)/,/^(?:\\conint)/,/^(?:\\cong)/,/^(?:\\complement)/,/^(?:\\colspan)/,/^(?:\\color)/,/^(?:\\Colonsim)/,
+/^(?:\\colonsim)/,/^(?:\\Coloneqq)/,/^(?:\\coloneqq)/,/^(?:\\Coloneq)/,/^(?:\\coloneq)/,/^(?:\\Colonapprox)/,/^(?:\\colonapprox)/,/^(?:\\Colon)/,/^(?:\\colon)/,/^(?:\\collines)/,/^(?:\\collayout)/,/^(?:\\colalign)/,/^(?:\\clubsuit)/,/^(?:\\closure)/,/^(?:\\circleddash)/,/^(?:\\circledcirc)/,/^(?:\\circledast)/,/^(?:\\circlearrowright)/,/^(?:\\circlearrowleft)/,/^(?:\\circeq)/,/^(?:\\circ)/,/^(?:\\choose)/,/^(?:\\chi)/,/^(?:\\check)/,/^(?:\\cellopts)/,/^(?:\\cdots)/,/^(?:\\cdotp)/,/^(?:\\cdot)/,/^(?:\\Cap)/,
+/^(?:\\cap)/,/^(?:\\bumpeqq)/,/^(?:\\Bumpeq)/,/^(?:\\bumpeq)/,/^(?:\\bullet)/,/^(?:\\btimes)/,/^(?:\\boxtimes)/,/^(?:\\boxplus)/,/^(?:\\boxminus)/,/^(?:\\boxed)/,/^(?:\\boxdot)/,/^(?:\\boxdiag)/,/^(?:\\boxcircle)/,/^(?:\\boxbslash)/,/^(?:\\boxast)/,/^(?:\\Box)/,/^(?:\\bowtie)/,/^(?:\\bottom)/,/^(?:\\bot)/,/^(?:\\boldsymbol)/,/^(?:\\blacktriangleright)/,/^(?:\\blacktriangleleft)/,/^(?:\\blacktriangledown)/,/^(?:\\blacktriangle)/,/^(?:\\blacksquare)/,/^(?:\\blacklozenge)/,/^(?:\\bkarow)/,/^(?:\\binom)/,
+/^(?:\\bigwedge)/,/^(?:\\bigvee)/,/^(?:\\biguplus)/,/^(?:\\bigtriangleup)/,/^(?:\\bigtriangledown)/,/^(?:\\bigtimes)/,/^(?:\\bigstar)/,/^(?:\\bigsqcup)/,/^(?:\\bigsqcap)/,/^(?:\\Bigr)/,/^(?:\\bigr)/,/^(?:\\bigotimes)/,/^(?:\\bigoplus)/,/^(?:\\bigodot)/,/^(?:\\Bigl)/,/^(?:\\bigl)/,/^(?:\\biginterleave)/,/^(?:\\Biggr)/,/^(?:\\biggr)/,/^(?:\\Biggl)/,/^(?:\\biggl)/,/^(?:\\Bigg)/,/^(?:\\bigg)/,/^(?:\\bigcupdot)/,/^(?:\\bigcup)/,/^(?:\\bigcirc)/,/^(?:\\bigcap)/,/^(?:\\Big)/,/^(?:\\big)/,/^(?:\\bgcolor)/,
+/^(?:\\between)/,/^(?:\\beth)/,/^(?:\\Beta)/,/^(?:\\beta)/,/^(?:\\begin\{Vmatrix\})/,/^(?:\\begin\{vmatrix\})/,/^(?:\\begintoggle)/,/^(?:\\begin\{split\})/,/^(?:\\begin\{smallmatrix\})/,/^(?:\\begin\{pmatrix\})/,/^(?:\\begin\{matrix\})/,/^(?:\\begin\{gathered\})/,/^(?:\\begin\{cases\})/,/^(?:\\begin\{Bmatrix\})/,/^(?:\\begin\{bmatrix\})/,/^(?:\\begin\{array\})/,/^(?:\\begin\{aligned\})/,/^(?:\\because)/,/^(?:\\BbbPi)/,/^(?:\\barwedge)/,/^(?:\\bar)/,/^(?:\\backslash)/,/^(?:\\backsimeq)/,/^(?:\\backsim)/,
+/^(?:\\backprime)/,/^(?:\\backepsilon)/,/^(?:\\atop)/,/^(?:\\asymp)/,/^(?:\\ast)/,/^(?:\\arrayopts)/,/^(?:\\array)/,/^(?:\\arccos|\\arcsin|\\arctan|\\arg|\\cosh|\\cos|\\coth|\\cot|\\csc|\\deg|\\dim|\\exp|\\hom|\\ker|\\lg|\\ln|\\log|\\sec|\\sinh|\\sin|\\tanh|\\tan)/,/^(?:\\approxeq)/,/^(?:\\approx)/,/^(?:\\angle)/,/^(?:\\amalg)/,/^(?:\\Alpha)/,/^(?:\\alpha)/,/^(?:\\align)/,/^(?:\\aleph)/,/^(?:[\u0041-\u005A])/,/^(?:[\u0391-\u03A1\u03A3\u03A4\u03A6-\u03A9])/,/^(?:[\u0061-\u007A\u0131\u0237])/,/^(?:[\u03B1-\u03C1\u03C3-\u03C9\u03D1\u03D5\u03D6\u03F0\u03F1\u03F4\u03F5])/,
+/^(?:\\adots)/,/^(?:\\AA)/,/^(?:[\u00F0\u03C2\u03D0\u03D2\u03DA-\u03DD\u03E0\u03E1\u0428\u0608\u0627-\u063A\u2102\u210A-\u210D\u210F-\u2113\u2115\u2118-\u211D\u2124\u2127\u2128\u212B-\u212D\u212F-\u2131\u2133-\u2138\u213C\u213D\u213F\u2205]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB\uDEF0\uDEF1]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDFCB])/,
+/^(?:\\\$)/,/^(?:\\\})/,/^(?:\\\|)/,/^(?:\\\{)/,/^(?:\\;)/,/^(?:\\:)/,/^(?:\\,)/,/^(?:\\&)/,/^(?:\\%)/,/^(?:\\#)/,/^(?:\\!)/,/^(?:-)/,/^(?:'''')/,/^(?:''')/,/^(?:'')/,/^(?:')/,/^(?:[\uD800-\uDBFF])/,/^(?:[\uDC00-\uDFFF])/,/^(?:.)/],M:{MATH0:{rules:[14,15,16,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,
+99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,
+225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,
+351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,
+477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,
+603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,
+729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,
+855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883],inclusive:tb},MATH1:{rules:[14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,
+125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,
+251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,
+377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,
+503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,
+629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,
+755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,
+881,882,883],inclusive:tb},OPTARG:{rules:[13,14,15,16,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,
+151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,
+277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,
+403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,
+529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,
+655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,
+781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883],inclusive:tb},DOCUMENT:{rules:[1,2,3,4,5],inclusive:yb},TRYOPTARG:{rules:[6,7],inclusive:yb},
+TEXTOPTARG:{rules:[8,9],inclusive:yb},TEXTARG:{rules:[10,11,12],inclusive:yb},INITIAL:{rules:[0,14,15,16,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,
+138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,
+264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,
+390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,
+516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,
+642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,
+768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883],inclusive:tb}}}}();Rb.prototype=rb;rb.pa=
+Rb;return new Rb}();window.TeXZilla=zb;window.TeXZilla.setDOMParser=zb.fa;window.TeXZilla.setXMLSerializer=zb.ja;window.TeXZilla.setSafeMode=zb.ia;window.TeXZilla.setItexIdentifierMode=zb.ha;window.TeXZilla.getTeXSource=zb.ca;window.TeXZilla.toMathMLString=zb.Z;window.TeXZilla.toMathML=zb.Y;window.TeXZilla.toImage=zb.na;window.TeXZilla.filterString=zb.Q;window.TeXZilla.filterElement=zb.P;
+})();
diff --git a/comm/suite/editor/components/texzilla/jar.mn b/comm/suite/editor/components/texzilla/jar.mn
new file mode 100644
index 0000000000..28d5a4cc3e
--- /dev/null
+++ b/comm/suite/editor/components/texzilla/jar.mn
@@ -0,0 +1,6 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+comm.jar:
+ content/editor/TeXZilla.js (content/TeXZilla.js)
diff --git a/comm/suite/editor/components/texzilla/moz.build b/comm/suite/editor/components/texzilla/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/editor/components/texzilla/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/editor/modules/editorUtilities.jsm b/comm/suite/editor/modules/editorUtilities.jsm
new file mode 100644
index 0000000000..877df7d0eb
--- /dev/null
+++ b/comm/suite/editor/modules/editorUtilities.jsm
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["GetNextUntitledValue"];
+
+var sUntitledCount = 1;
+
+function GetNextUntitledValue() {
+ return sUntitledCount++;
+}
diff --git a/comm/suite/editor/moz.build b/comm/suite/editor/moz.build
new file mode 100644
index 0000000000..d3b471b23d
--- /dev/null
+++ b/comm/suite/editor/moz.build
@@ -0,0 +1,22 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ "base",
+ "components",
+]
+
+EXTRA_COMPONENTS += [
+ "nsComposerCmdLineHandler.js",
+ "nsComposerCmdLineHandler.manifest",
+]
+
+EXTRA_JS_MODULES += [
+ "modules/editorUtilities.jsm",
+]
+
+JS_PREFERENCE_PP_FILES += [
+ "profile/composer.js",
+]
diff --git a/comm/suite/editor/nsComposerCmdLineHandler.js b/comm/suite/editor/nsComposerCmdLineHandler.js
new file mode 100644
index 0000000000..2f27207b82
--- /dev/null
+++ b/comm/suite/editor/nsComposerCmdLineHandler.js
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+const nsICommandLineHandler = Ci.nsICommandLineHandler;
+const nsISupportsString = Ci.nsISupportsString;
+const nsIWindowWatcher = Ci.nsIWindowWatcher;
+
+function nsComposerCmdLineHandler() {}
+nsComposerCmdLineHandler.prototype = {
+ get wrappedJSObject() {
+ return this;
+ },
+
+ /* nsISupports */
+ QueryInterface: ChromeUtils.generateQI([nsICommandLineHandler]),
+
+ /* nsICommandLineHandler */
+ handle(cmdLine) {
+ var args = Cc["@mozilla.org/supports-string;1"].createInstance(
+ nsISupportsString
+ );
+ try {
+ var uristr = cmdLine.handleFlagWithParam("edit", false);
+ if (uristr == null) {
+ // Try the editor flag (used for general.startup.* prefs)
+ uristr = cmdLine.handleFlagWithParam("editor", false);
+ if (uristr == null) {
+ return;
+ }
+ }
+
+ try {
+ args.data = cmdLine.resolveURI(uristr).spec;
+ } catch (e) {
+ return;
+ }
+ } catch (e) {
+ // One of the flags is present but no data, so set default arg.
+ args.data = "about:blank";
+ }
+
+ Services.ww.openWindow(
+ null,
+ "chrome://editor/content",
+ "_blank",
+ "chrome,dialog=no,all",
+ args
+ );
+ cmdLine.preventDefault = true;
+ },
+
+ helpInfo: " -edit <url> Open Composer.\n",
+
+ /* XPCOMUtils */
+ classID: Components.ID("{f7d8db95-ab5d-4393-a796-9112fe758cfa}"),
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsComposerCmdLineHandler]);
diff --git a/comm/suite/editor/nsComposerCmdLineHandler.manifest b/comm/suite/editor/nsComposerCmdLineHandler.manifest
new file mode 100644
index 0000000000..8a27624af1
--- /dev/null
+++ b/comm/suite/editor/nsComposerCmdLineHandler.manifest
@@ -0,0 +1,3 @@
+component {f7d8db95-ab5d-4393-a796-9112fe758cfa} nsComposerCmdLineHandler.js
+contract @mozilla.org/commandlinehandler/general-startup;1?type=editor {f7d8db95-ab5d-4393-a796-9112fe758cfa}
+category command-line-handler m-edit @mozilla.org/commandlinehandler/general-startup;1?type=editor
diff --git a/comm/suite/editor/profile/composer.js b/comm/suite/editor/profile/composer.js
new file mode 100644
index 0000000000..e123fc7771
--- /dev/null
+++ b/comm/suite/editor/profile/composer.js
@@ -0,0 +1,70 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/**
+ * Default preferences for seamonkey composer. This file
+ * was copied from mozilla/modules/libpref/src/init/editor.js
+ *
+ * If you're looking for the default prefs of standalone
+ * composer, see mozilla/composer/app/profile/all.js
+ */
+
+pref("editor.author", "");
+
+pref("editor.text_color", "#000000");
+pref("editor.link_color", "#0000FF");
+pref("editor.active_link_color", "#000088");
+pref("editor.followed_link_color", "#FF0000");
+pref("editor.background_color", "#FFFFFF");
+pref("editor.use_background_image", false);
+pref("editor.default_background_image", "");
+pref("editor.use_custom_default_colors", 1);
+
+pref("editor.hrule.height", 2);
+pref("editor.hrule.width", 100);
+pref("editor.hrule.width_percent", true);
+pref("editor.hrule.shading", true);
+pref("editor.hrule.align", 1); // center
+
+pref("editor.table.maintain_structure", true);
+
+pref("editor.prettyprint", true);
+
+pref("editor.history.url_maximum", 10);
+
+pref("editor.publish.", "");
+pref("editor.lastFileLocation.image", "");
+pref("editor.lastFileLocation.html", "");
+pref("editor.save_associated_files", true);
+pref("editor.always_show_publish_dialog", false);
+
+/*
+ * What are the entities that you want Mozilla to save using mnemonic
+ * names rather than numeric codes? E.g. If set, we'll output &nbsp;
+ * otherwise, we may output 0xa0 depending on the charset.
+ *
+ * "none" : don't use any entity names; only use numeric codes.
+ * "basic" : use entity names just for &nbsp; &amp; &lt; &gt; &quot; for
+ * interoperability/exchange with products that don't support more
+ * than that.
+ * "latin1" : use entity names for 8bit accented letters and other special
+ * symbols between 128 and 255.
+ * "html" : use entity names for 8bit accented letters, greek letters, and
+ * other special markup symbols as defined in HTML4.
+ */
+//pref("editor.encode_entity", "html");
+
+#ifndef XP_MACOSX
+#ifdef XP_UNIX
+pref("editor.disable_spell_checker", false);
+pref("editor.dont_lock_spell_files", true);
+#endif
+#endif
+
+pref("editor.CR_creates_new_p", false);
+
+// Pasting images from the clipboard, order of encoding preference:
+// JPEG-PNG-GIF=0, PNG-JPEG-GIF=1, GIF-JPEG-PNG=2
+pref("clipboard.paste_image_type", 1);
diff --git a/comm/suite/extensions/built_in_addons.json b/comm/suite/extensions/built_in_addons.json
new file mode 100644
index 0000000000..7e28322454
--- /dev/null
+++ b/comm/suite/extensions/built_in_addons.json
@@ -0,0 +1 @@
+{ "system": [] }
diff --git a/comm/suite/extensions/debugQA/content/EditorInitPage.html b/comm/suite/extensions/debugQA/content/EditorInitPage.html
new file mode 100644
index 0000000000..bdfa194337
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/EditorInitPage.html
@@ -0,0 +1,159 @@
+<!-- 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/. -->
+
+<html>
+<head>
+<title>Ender HTML Test Page</title>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+
+<style type="text/css">
+/* Don't set attributes here, or they can't be changed! */
+p.note:before { content: "Note: "; }
+
+</style>
+
+</head>
+<body>
+
+<h2>Here's the deal...</h2>
+
+<p>This is a good place to add in <b>html</b> to aid in testing features
+under development. It's also a great place to not use latin.
+</p>
+
+<!-- This is a comment;
+ Here is more of the comment.
+-->
+
+<p>
+Here is an acute entity: "&aacute;".
+This&nbsp;&nbsp;sentence&nbsp;&nbsp;has&nbsp;&nbsp;two&nbsp;&nbsp;&amp;nbsp;&nbsp;&nbsp;tags&nbsp;&nbsp;between&nbsp;&nbsp;each&nbsp;&nbsp;word.
+</p>
+<hr size="2" width="100%"/>
+<div>
+<p>
+This paragraph is inside a &lt;div&gt;. <a href="http://www.seamonkey-project.org/">Here is some
+rather boring text that is a link.</a> Now is the time for all good men (and women)
+to come to the SeaMonkey project party. And bring the quick brown fox and the lazy dog with you.
+For your editing pleasure, here is some <span style="font-weight:bold;">inline style</span> for you.
+<a name="test1"></a>A Named Anchor is just before this sentence.
+</p>
+</div>
+
+<p class="note">
+&quot;This paragraph has &quot;class=note&quot;, so that we can play with styles.&quot;
+For example, we might insert generated content, like &quot;Note: &quot; before this
+paragraph.
+</p>
+
+<p>
+Here is some very run-o-the-mill text in a paragraph tag, for those
+of you who are into that kind of thing. I think at this point I shall
+regale you with some of my inestimable poetry, which will be presented
+in blockquote mode for your reading pleasure. This particular sample
+was written when I was eleven years old, but is remarkably similar to
+the writings of my later "hard-livin" years. It's titled "Reruns":
+</p>
+
+<blockquote class="poem" type="cite">
+<h4>Reruns</h4>
+Reruns are about as much fun,<br>
+as your dad taking all your mun,<br>
+and giving it to a nun,<br>
+as a contribution.
+</blockquote>
+
+<br>
+<br>
+<br>
+<br>
+There are 4 br tags on either side of this sentence.
+<br>
+<br>
+<br>
+<br>
+
+This text has two spaces between every word
+<u>Underlined text with space at end </u><b> and bold text with a space at front</b>
+
+<p>A random list of things to do</p>
+<ul>
+<li>Status report</li>
+<li>Fix bugs</li>
+<li>Call home</li>
+</ul>
+
+<pre>
+ Here is some
+ preformatted text.
+ Here is some
+ preformatted text.
+</pre>
+
+<ol>
+<li>list item 1</li>
+<li>list item 2</li>
+<li>list item 3</li>
+</ol>
+
+<ol class="roman">
+<li>list item with child paragraphs
+ <p>
+ First child paragraph
+ </p>
+ <p>
+ Second child paragraph
+ </p>
+ <p>
+ Third child paragraph
+ </p>
+</li>
+<li>
+Another list item
+</li>
+<li>
+Something else.
+</li>
+<li>
+Another thing.
+</li>
+</ol>
+
+
+<table border="1">
+<tr><td>cell 1 </td><td>cell 2 </td><td>cell 3 </td></tr>
+<tr><td>cell 4 </td><td>cell 5 </td><td>cell 6 </td></tr>
+<tr><td>cell 7 </td><td>cell 8 </td><td>cell 9 </td></tr>
+</table>
+
+
+<table border="1" id="bigtable">
+<tr><td>big cell 1 </td>
+<td>
+<table BORDER>
+<tr><td>nested cell 1 </td><td>nested cell 2 </td></tr>
+<tr><td>nested cell 3 </td><td>nested cell 4 </td></tr>
+</table>
+</td>
+</tr>
+<tr><td>
+<table BORDER>
+<tr><td>nested cell 5 </td><td>nested cell 6 </td></tr>
+<tr><td>nested cell 7 </td><td>nested cell 8 </td></tr>
+</table>
+</td><td>big cell 4 </td></tr>
+</table>
+
+<p>
+Japanese for ya: プロ野çƒé€Ÿå ±
+</p>
+<p>
+One more paragraph for the road. A true Klingon fears not the atrocious
+editing file format hoisted on us by html. Well, maybe he does, a little.
+But he hides it well.
+</p>
+
+</body>
+</html>
+
diff --git a/comm/suite/extensions/debugQA/content/debugQAEditorOverlay.js b/comm/suite/extensions/debugQA/content/debugQAEditorOverlay.js
new file mode 100644
index 0000000000..da9dc95ac0
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/debugQAEditorOverlay.js
@@ -0,0 +1,207 @@
+/* -*- Mode: Java; tab-width: 4; 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/. */
+
+/* Main Composer window debug menu functions */
+
+// --------------------------- Output ---------------------------
+
+
+function EditorGetText()
+{
+ try {
+ dump("Getting text\n");
+ var outputText = GetCurrentEditor().outputToString("text/plain", kOutputFormatted);
+ dump("<<" + outputText + ">>\n");
+ } catch (e) {}
+}
+
+function EditorGetHTML()
+{
+ try {
+ dump("Getting HTML\n");
+ var outputHTML = GetCurrentEditor().outputToString("text/html", kOutputEncodeW3CEntities);
+ dump(outputHTML + "\n");
+ } catch (e) {}
+}
+
+function EditorInsertText(textToInsert)
+{
+ GetCurrentEditor().insertText(textToInsert);
+}
+
+function EditorTestSelection()
+{
+ dump("Testing selection\n");
+ var selection = GetCurrentEditor().selection;
+ if (!selection)
+ {
+ dump("No selection!\n");
+ return;
+ }
+
+ dump("Selection contains:\n");
+ // 3rd param = column to wrap
+ dump(selection
+ .toStringWithFormat("text/plain",
+ kOutputFormatted | kOutputSelectionOnly,
+ 0) + "\n");
+
+ var output, i;
+
+ dump("====== Selection as node and offsets==========\n");
+ dump("rangeCount = " + selection.rangeCount + "\n");
+ for (i = 0; i < selection.rangeCount; i++)
+ {
+ var range = selection.getRangeAt(i);
+ if (range)
+ {
+ dump("Range "+i+": StartParent="+range.startContainer.nodeName+", offset="+range.startOffset+"\n");
+ dump("Range "+i+": EndParent="+range.endContainer.nodeName+", offset="+range.endOffset+"\n\n");
+ }
+ }
+
+ var editor = GetCurrentEditor();
+
+ dump("====== Selection as unformatted text ==========\n");
+ output = editor.outputToString("text/plain", kOutputSelectionOnly);
+ dump(output + "\n\n");
+
+ dump("====== Selection as formatted text ============\n");
+ output = editor.outputToString("text/plain", kOutputFormatted | kOutputSelectionOnly);
+ dump(output + "\n\n");
+
+ dump("====== Selection as HTML ======================\n");
+ output = editor.outputToString("text/html", kOutputSelectionOnly);
+ dump(output + "\n\n");
+
+ dump("====== Selection as prettyprinted HTML ========\n");
+ output = editor.outputToString("text/html", kOutputFormatted | kOutputSelectionOnly);
+ dump(output + "\n\n");
+
+ dump("====== Length and status =====================\n");
+ output = "Document is ";
+ if (editor.documentIsEmpty)
+ output += "empty\n";
+ else
+ output += "not empty\n";
+ output += "Text length is " + editor.textLength + " characters";
+ dump(output + "\n\n");
+}
+
+function EditorTestDocument()
+{
+ dump("Getting document\n");
+ var theDoc = GetCurrentEditor().document;
+ if (theDoc)
+ {
+ dump("Got the doc\n");
+ dump("Document name:" + theDoc.nodeName + "\n");
+ dump("Document type:" + theDoc.doctype + "\n");
+ }
+ else
+ {
+ dump("Failed to get the doc\n");
+ }
+}
+
+// ------------------------ 3rd Party Transaction Test ------------------------
+
+
+function sampleJSTransaction()
+{
+ this.wrappedJSObject = this;
+}
+
+sampleJSTransaction.prototype = {
+
+ isTransient: false,
+ mStrData: "[Sample-JS-Transaction-Content]",
+ mObject: null,
+ mContainer: null,
+ mOffset: null,
+
+ doTransaction: function()
+ {
+ if (this.mContainer.nodeType != Node.TEXT_NODE)
+ {
+ // We're not in a text node, so create one and
+ // we'll just insert it at (mContainer, mOffset).
+
+ this.mObject = this.mContainer.ownerDocument.createTextNode(this.mStrData);
+ }
+
+ this.redoTransaction();
+ },
+
+ undoTransaction: function()
+ {
+ if (!this.mObject)
+ this.mContainer.deleteData(this.mOffset, this.mStrData.length);
+ else
+ this.mObject.remove();
+ },
+
+ redoTransaction: function()
+ {
+ if (!this.mObject)
+ this.mContainer.insertData(this.mOffset, this.mStrData);
+ else
+ this.insert_node_at_point(this.mObject, this.mContainer, this.mOffset);
+ },
+
+ merge: function(aTxn)
+ {
+ // We don't do any merging!
+
+ return false;
+ },
+
+ QueryInterface: function(aIID, theResult)
+ {
+ if (aIID.equals(Ci.nsITransaction) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ insert_node_at_point: function(node, container, offset)
+ {
+ var childList = container.childNodes;
+
+ if (childList.length == 0 || offset >= childList.length)
+ container.appendChild(node);
+ else
+ container.insertBefore(node, childList.item(offset));
+ }
+}
+
+function ExecuteJSTransactionViaEditor()
+{
+ try {
+ var editor = GetCurrentEditor();
+
+ var selection = editor.selection;
+ var range = selection.getRangeAt(0);
+
+ var txn = new sampleJSTransaction();
+
+ txn.mContainer = range.startContainer;
+ txn.mOffset = range.startOffset;
+
+ editor.doTransaction(txn);
+ } catch (e) {
+ dump("ExecuteJSTransactionViaEditor() failed!");
+ }
+}
+
+function EditorNewPlaintext(aUrl, aCharsetArg)
+{
+ window.openDialog( "chrome://debugqa/content/debugQATextEditorShell.xul",
+ "_blank",
+ "chrome,dialog=no,all",
+ aUrl || "about:blank",
+ aCharsetArg);
+}
diff --git a/comm/suite/extensions/debugQA/content/debugQAEditorOverlay.xul b/comm/suite/extensions/debugQA/content/debugQAEditorOverlay.xul
new file mode 100644
index 0000000000..c27bc01f9f
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/debugQAEditorOverlay.xul
@@ -0,0 +1,45 @@
+<?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 % debugQAEditorOverlayDTD SYSTEM "chrome://debugqa/locale/debugQAEditorOverlay.dtd">
+%debugQAEditorOverlayDTD;
+]>
+
+<overlay id="debugQAEditorOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://debugQA/content/debugQAEditorOverlay.js"/>
+
+ <menubar id="main-menubar">
+ <menu id="debugMenu" label="&debugMenu.label;" insertbefore="menu_Help">
+ <menupopup>
+ <menuitem label="&newEditorTestPage.label;"
+ oncommand="NewEditorWindow('chrome://debugqa/content/EditorInitPage.html');"/>
+ <menuitem label="&textEditorCmd.label;"
+ oncommand="EditorNewPlaintext();" />
+ <menuitem label="&outputTextCmd.label;"
+ oncommand="EditorGetText()"/>
+ <menuitem label="&outputHTMLCmd.label;"
+ oncommand="EditorGetHTML()"/>
+ <menuseparator/>
+ <menuitem label="&insertTextCmd.label;"
+ oncommand="EditorInsertText('All good things come to those who wait. ')"/>
+ <menuseparator/>
+ <menuitem label="&testSelectionCmd.label;"
+ oncommand="EditorTestSelection()"/>
+ <menuitem label="&testDocumentCmd.label;"
+ oncommand="EditorTestDocument()"/>
+ <menuseparator/>
+ <menuitem label="&executeJSTransactionViaEditor.label;"
+ oncommand="ExecuteJSTransactionViaEditor()"/>
+ <menuseparator/>
+ <menuitem label="&setFocusCmd.label;"
+ oncommand="window.focus()"/>
+ </menupopup>
+ </menu>
+ </menubar>
+</overlay>
diff --git a/comm/suite/extensions/debugQA/content/debugQAMenuOverlay.js b/comm/suite/extensions/debugQA/content/debugQAMenuOverlay.js
new file mode 100644
index 0000000000..ab79c2b661
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/debugQAMenuOverlay.js
@@ -0,0 +1,51 @@
+/* -*- 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/. */
+
+var { Services } =
+ ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// "about:bloat" is available only when
+// (the application is) compiled with |--enable-logrefcnt|.
+if ("@mozilla.org/network/protocol/about;1?what=bloat" in Cc)
+ window.addEventListener("load", onLoadBloat);
+
+// Unhide (and enable) the Bloat menu and its associated separator.
+function onLoadBloat()
+{
+ window.removeEventListener("load", onLoadBloat);
+
+ // Ignore windows which don't get the Debug menu, like 'View Source'.
+ if (!document.getElementById("debugMenu"))
+ return;
+
+ // Enable the menu, only if its feature is currently active.
+ var envSvc = Cc["@mozilla.org/process/environment;1"]
+ .getService(Ci.nsIEnvironment);
+ // Checking the environment variables is good enough,
+ // as the Bloat service doesn't report the status of its statistics feature.
+ if (envSvc.exists("XPCOM_MEM_BLOAT_LOG") ||
+ envSvc.exists("XPCOM_MEM_LEAK_LOG"))
+ document.getElementById("bloatMenu").disabled = false;
+
+ document.getElementById("bloatSeparator").hidden = false;
+ document.getElementById("bloatMenu").hidden = false;
+}
+
+// Open a debug QA link from the menu in the current tab.
+function openQAUrl(aUrl)
+{
+ openUILinkIn(aUrl, "current",
+ { triggeringPrincipal:
+ Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+}
+
+// Flush the memory using minimizeMemoryUsage.
+function flushMemory() {
+ Services.obs.notifyObservers(null, "child-mmu-request");
+ Cc["@mozilla.org/memory-reporter-manager;1"]
+ .getService(Ci.nsIMemoryReporterManager)
+ .minimizeMemoryUsage(() => {});
+}
diff --git a/comm/suite/extensions/debugQA/content/debugQAMenuOverlay.xul b/comm/suite/extensions/debugQA/content/debugQAMenuOverlay.xul
new file mode 100644
index 0000000000..45167cbe75
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/debugQAMenuOverlay.xul
@@ -0,0 +1,150 @@
+<?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/. -->
+
+<overlay id="debugQAMenuOverlay"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://debugQA/content/debugQAMenuOverlay.js"/>
+
+ <!-- Menu -->
+ <menubar id="main-menubar">
+ <!-- Menu for testing. -->
+ <menu id="debugMenu" accesskey="U" label="Debug" insertbefore="menu_Help">
+ <menupopup id="menu_Debug_Popup">
+ <menuitem label="Verification"
+ oncommand="openQAUrl('https://www.seamonkey-project.org/dev/qa-verification');"/>
+
+ <menuseparator/>
+
+ <menu label="Viewer Demos">
+ <menupopup>
+ <menuitem label="#0 Basic Styles"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test0.html');"/>
+ <menuitem label="#1 CSS Styles"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test1.html');"/>
+ <menuitem label="#2 Images"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test2.html');"/>
+ <menuitem label="#3 Basic Tables"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test3.html');"/>
+ <menuitem label="#4 Simple Tables"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test4.html');"/>
+ <menuitem label="#5 More Styles"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test5.html');"/>
+ <menuitem label="#6 Deeply Nested Tables"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test6.html');"/>
+ <menuitem label="#7 Scaled Anim Image"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test7.html');"/>
+ <menuitem label="#8 Form"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test8.html');"/>
+ <menuitem label="#9 Frames"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test9.html');"/>
+ <menuitem label="#10 Anim Images"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test10.html');"/>
+ <menuitem label="#11 Fixed Positioning"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test11.html');"/>
+ <menuitem label="#12 More Fixed Pos"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test12.html');"/>
+ <menuitem label="#13 DHTML"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test13.html');"/>
+ <menuitem label="#16 Gfx Widgets"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/newlayout/samples/test16.html');"/>
+ </menupopup>
+ </menu>
+
+ <!-- XBL Test Suite all broken -->
+ <menu label="XBL Test Suite" hidden="true" disabled="true">
+ <menupopup>
+ <menuitem label="#0 Remote XBL"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/projects/xbl/test0/test.xul');"/>
+ <menuitem label="#1 Technicolor DIV"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/projects/xbl/test1/test.html');"/>
+ <menuitem label="#2 Rollover Madness"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/projects/xbl/test2/test.html');"/>
+ <menuitem label="#3 Popups in HTML"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/projects/xbl/test3/test.html');"/>
+ <menuitem label="#4 Partition Magic"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/projects/xbl/test4/test.html');"/>
+ <menuitem label="#5 Sticky Notes"
+ oncommand="openQAUrl('https://www-archive.mozilla.org/projects/xbl/test5/test.html');"/>
+ </menupopup>
+ </menu>
+
+ <menu label="Internal Pages">
+ <menupopup>
+ <menuitem label="Support Page"
+ oncommand="goAbout('support');"/>
+ <menuitem label="Networking Status"
+ oncommand="goAbout('networking');"/>
+ <menuitem label="Performance Status"
+ oncommand="goAbout('performance');"/>
+ <menuitem label="Memory Status"
+ oncommand="goAbout('memory');"/>
+ </menupopup>
+ </menu>
+
+ <menuseparator/>
+
+ <menuitem label="Composer (with test page)"
+ oncommand="NewEditorWindow('chrome://debugqa/content/EditorInitPage.html');"/>
+ <menuitem label="Flush Memory"
+ oncommand="flushMemory();"/>
+ <menuitem label="Show Configuration Editor"
+ oncommand="goAbout('config');"/>
+ </menupopup>
+ </menu>
+
+ <menu id="qaMenu" accesskey="Q" label="QA" insertbefore="menu_Help">
+ <menupopup id="qaMenuPopup">
+ <menuitem label="Getting Involved"
+ oncommand="openQAUrl('https://www.seamonkey-project.org/dev/get-involved');"/>
+ <menuitem label="SeaMonkey QA"
+ oncommand="openQAUrl('https://wiki.mozilla.org/SeaMonkey:QA');"/>
+ <menuitem label="Useful Sites"
+ oncommand="openQAUrl('https://www.seamonkey-project.org/dev/useful-sites');"/>
+
+ <menuseparator/>
+
+ <menuitem label="Frequently Reported Bugs"
+ oncommand="openQAUrl('https://bugzilla.mozilla.org/duplicates.cgi');"/>
+ <menuitem label="Bug Writing Guidelines"
+ oncommand="openQAUrl('https://developer.mozilla.org/docs/Mozilla/QA/Bug_writing_guidelines');"/>
+ <menuitem label="File a Bug"
+ oncommand="openQAUrl('https://bugzilla.mozilla.org/enter_bug.cgi?format=guided#h=dupes|SeaMonkey');"/>
+
+ <menuseparator/>
+
+ <menuitem label="Bugs Filed Today"
+ oncommand="openQAUrl('https://bugzilla.mozilla.org/buglist.cgi?product=Core&amp;product=MailNews+Core&amp;product=SeaMonkey&amp;chfieldfrom=0d&amp;chfieldto=Now&amp;chfield=%5BBug+creation%5D');"/>
+ <menuitem label="Tree Status"
+ oncommand="openQAUrl('https://treestatus.mozilla-releng.net/static/ui/treestatus/');"/>
+ <menuseparator/>
+ <menuitem label="Recent SeaMonkey website Checkins"
+ oncommand="openQAUrl('https://hg.mozilla.org/SeaMonkey/seamonkey-project-org/pushloghtml?startdate=2+weeks+ago&amp;enddate=now');"/>
+ <menuitem label="Recent gitlab SeaMonkey website Checkins"
+ oncommand="openQAUrl('https://gitlab.com/seamonkey-project/website/activity/');"/>
+ <menuseparator/>
+ <menuitem label="Recent comm-central Checkins"
+ oncommand="openQAUrl('https://hg.mozilla.org/comm-central/pushloghtml?startdate=24+hours+ago&amp;enddate=now');"/>
+ <menuitem label="Recent mozilla-central Checkins"
+ oncommand="openQAUrl('https://hg.mozilla.org/mozilla-central/pushloghtml?startdate=24+hours+ago&amp;enddate=now');"/>
+ <menuitem label="Recent comm-esr102 Checkins"
+ oncommand="openQAUrl('https://hg.mozilla.org/releases/comm-esr102/pushloghtml?startdate=2+weeks+ago&amp;enddate=now');"/>
+ <menuitem label="Recent mozilla-esr102 Checkins"
+ oncommand="openQAUrl('https://hg.mozilla.org/releases/mozilla-esr102/pushloghtml?startdate=2+weeks+ago&amp;enddate=now');"/>
+ <menuitem label="Recent gitlab 2.53 comm Activity"
+ oncommand="openQAUrl('https://gitlab.com/seamonkey-project/seamonkey-2.53-comm/activity/');"/>
+ <menuitem label="Recent gitlab 2.53 mozilla Activity"
+ oncommand="openQAUrl('https://gitlab.com/seamonkey-project/seamonkey-2.53-mozilla/activity/');"/>
+ <menuitem label="Recent gitlab 2.53 l10n Activity"
+ oncommand="openQAUrl('https://gitlab.com/seamonkey-project/seamonkey-2.53-l10n/activity/');"/>
+ <menuseparator/>
+ <menuitem label="Daily unofficial 2.53 Builds"
+ oncommand="openQAUrl('https://www.wg9s.com/comm-253/');"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+</overlay>
diff --git a/comm/suite/extensions/debugQA/content/debugQANavigatorOverlay.xul b/comm/suite/extensions/debugQA/content/debugQANavigatorOverlay.xul
new file mode 100644
index 0000000000..2513c3827d
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/debugQANavigatorOverlay.xul
@@ -0,0 +1,54 @@
+<?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/. -->
+
+<overlay id="debugQANavigatorOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script>
+ <![CDATA[
+ // Setting the build id is optional.
+ try {
+ if (Services.prefs.getBoolPref("debugQA.show.buildId"))
+ window.addEventListener("load", debugQABuildIDOnLoad, true);
+ } catch (e) {}
+
+ // Set the title modifer to include the build id.
+ function debugQABuildIDOnLoad() {
+ window.removeEventListener("load", debugQABuildIDOnLoad, true);
+
+ var appInfo = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULAppInfo);
+
+ var bundle = document.getElementById("debugQANavigatorBundle");
+
+ var titlemodifier =
+ document.documentElement.getAttribute("titlemodifier") ||
+ document.documentElement.getAttribute("titledefault");
+
+ titlemodifier = bundle.getFormattedString("titlemodifier",
+ [titlemodifier,
+ appInfo.appBuildID]);
+
+ // On mac, with contenttitlesetting="true", the titlemodifier is removed
+ // and replaced with a titledefault attribute that holds the value of the
+ // titlemodifier attribute. We don't use titledefault, but let's remove
+ // it since we're adding back the titlemodifier.
+ document.documentElement.removeAttribute("titledefault");
+
+ // Set the new title modifier
+ document.documentElement.setAttribute("titlemodifier", titlemodifier);
+
+ // Now set the title of the window
+ document.title = titlemodifier;
+ }
+ ]]>
+ </script>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="debugQANavigatorBundle"
+ src="chrome://debugQA/locale/debugQANavigatorOverlay.properties"/>
+ </stringbundleset>
+</overlay>
diff --git a/comm/suite/extensions/debugQA/content/debugQATextEditorShell.xul b/comm/suite/extensions/debugQA/content/debugQATextEditorShell.xul
new file mode 100644
index 0000000000..65331db3ef
--- /dev/null
+++ b/comm/suite/extensions/debugQA/content/debugQATextEditorShell.xul
@@ -0,0 +1,189 @@
+<?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://editor/skin/editor.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/editorPrimaryToolbar.css" type="text/css"?>
+<?xul-overlay href="chrome://editor/content/editorOverlay.xul"?>
+<?xul-overlay href="chrome://editor/content/editingOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/charsetOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/contentAreaContextOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/sidebar/sidebarOverlay.xul"?>
+<?xul-overlay href="chrome://debugQA/content/debugQAEditorOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % debugQAEditorOverlayDTD SYSTEM "chrome://debugqa/locale/debugQAEditorOverlay.dtd">
+%debugQAEditorOverlayDTD;
+<!ENTITY % editorDTD SYSTEM "chrome://editor/locale/editor.dtd">
+%editorDTD;
+<!ENTITY % editorOverlayDTD SYSTEM "chrome://editor/locale/editorOverlay.dtd">
+%editorOverlayDTD;
+]>
+
+<!-- NOTE: If we don't have "title" set, text editor doesn't work! -->
+<window id="editorWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="TextEditorOnLoad()"
+ onunload="EditorShutdown()"
+ onclose="return Async.promiseSpinningly(EditorCanClose());"
+ onfocus="EditorOnFocus()"
+ contenttitlesetting="true"
+ title=""
+ titlemodifier="&textEditorWindow.titlemodifier;"
+ titlemenuseparator="&editorWindow.titlemodifiermenuseparator;"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ windowtype="composer:text"
+ macanimationtype="document"
+ drawtitle="true"
+ width="640" height="480"
+ screenX="10" screenY="10"
+ persist="screenX screenY width height sizemode">
+
+ <script src="chrome://editor/content/editor.js"/>
+ <script src="chrome://editor/content/ComposerCommands.js"/>
+ <script src="chrome://communicator/content/contentAreaClick.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+ <script src="chrome://global/content/nsDragAndDrop.js"/>
+
+ <commandset id="editorCommands">
+ <commandset id="commonEditorMenuItems"/>
+ <commandset id="composerEditMenuItems"/>
+ <commandset id="composerSaveMenuItems"/>
+ <commandset id="composerMenuItems"/>
+ <commandset id="tasksCommands"/>
+ </commandset>
+
+ <!-- broadcaster nodes are appended here from the overlays -->
+ <broadcasterset id="mainBroadcasterSet"/>
+
+ <!-- keys are appended from the overlay -->
+ <keyset id="editorKeys">
+ <keyset id="tasksKeys"/>
+ <key id="showHideSidebar"/>
+ </keyset>
+
+ <popupset id="contentAreaContextSet"/>
+ <popupset id="editorPopupSet"/>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="EditorToolbox"
+ mode="full"
+ defaultmode="full">
+ <toolbar id="toolbar-menubar"
+ type="menubar"
+ class="chromeclass-menubar"
+ persist="collapsed"
+ grippytooltiptext="&menuBar.tooltip;"
+ customizable="true"
+ defaultset="menubar-items"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu">
+ <toolbaritem id="menubar-items"
+ class="menubar-items"
+ align="center">
+ <menubar id="main-menubar" class="chromeclass-menubar">
+ <menu id="menu_File"/>
+ <menu id="menu_Edit"/>
+
+ <menu id="menu_View">
+ <!-- id pulls in "Show Sidebar" item from sidebarOverlay -->
+ <menupopup id="menu_View_Popup">
+ <menu id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup"
+ onpopupshowing="onViewToolbarsPopupShowing(event);"
+ oncommand="onViewToolbarCommand(event);">
+ <menuitem id="menu_showTaskbar"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="viewSep1"/>
+ <menu id="charsetMenu"
+ onpopupshowing="EditorUpdateCharsetMenu(event.target);"
+ oncommand="EditorSetCharacterSet(event);"/>
+ </menupopup>
+ </menu>
+
+ <menu id="insertMenuPlainText"
+ label="&insertMenu.label;"
+ accesskey="&insertMenu.accesskey;">
+ <menupopup id="insertMenuPlainTextPopup">
+ <menuitem id="insertPlainTextChars"
+ accesskey="&insertCharsCmd.accesskey;"
+ command="cmd_insertChars"/>
+ </menupopup>
+ </menu>
+
+ <!-- tasks and window menus filled from tasksOverlay -->
+ <menu id="tasksMenu"/>
+ <menu id="windowMenu"/>
+
+ <!-- help menu filled from globalOverlay -->
+ <menu id="menu_Help"/>
+
+ <spacer flex="1"/>
+ </menubar>
+ </toolbaritem>
+ </toolbar>
+
+
+ <!-- toolbar filled out from editorOverlay -->
+ <toolbar id="EditToolbar"
+ class="chromeclass-toolbar toolbar-primary"
+ persist="collapsed"
+ grippytooltiptext="&compositionToolbar.tooltip;"
+ toolbarname="&compositionToolbarCmd.label;"
+ accesskey="&compositionToolbarCmd.accesskey;"
+ customizable="true"
+ defaultset="newButton,openButton,saveButton,print-button,separator,spellingButton,spring,throbber-box"
+ context="toolbar-context-menu">
+ <toolbarbutton id="newButton"/>
+ <toolbarbutton id="openButton"/>
+ <toolbarbutton id="saveButton"/>
+ <toolbarbutton id="cutButton"/>
+ <toolbarbutton id="copyButton"/>
+ <toolbarbutton id="pasteButton"/>
+ <toolbarbutton id="print-button"/>
+ <toolbarbutton id="findButton"/>
+ <toolbarbutton id="spellingButton"/>
+ <toolbaritem id="throbber-box"/>
+ </toolbar>
+
+ <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+
+ <toolbarpalette id="EditToolbarPalette"/>
+ </toolbox>
+
+ <hbox id="sidebar-parent" flex="1">
+ <!-- From sidebarOverlay.xul -->
+ <vbox id="sidebar-box" class="chromeclass-extrachrome" hidden="true"/>
+ <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
+ <vbox id="appcontent" flex="1">
+ <findbar id="FindToolbar" browserid="content-frame"/>
+ <editor editortype="text"
+ type="content"
+ primary="true"
+ id="content-frame"
+ src="about:blank" flex="1"
+ context="contentAreaContextMenu"/>
+ </vbox> <!-- appcontent -->
+ </hbox><!-- sidebar-parent -->
+
+ <statusbar id="status-bar"
+ class="chromeclass-status">
+ <statusbarpanel id="component-bar"/>
+ <statusbarpanel id="statusText"
+ label="&statusText.label;"
+ flex="1"
+ crop="right"/>
+ <statusbarpanel id="offline-status" class="statusbarpanel-iconic"/>
+ </statusbar>
+</window>
diff --git a/comm/suite/extensions/debugQA/install.rdf.in b/comm/suite/extensions/debugQA/install.rdf.in
new file mode 100644
index 0000000000..60086476b3
--- /dev/null
+++ b/comm/suite/extensions/debugQA/install.rdf.in
@@ -0,0 +1,38 @@
+<?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/. -->
+
+
+#filter substitution
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>debugQA@mozilla.org</em:id>
+ <em:version>2.0.13</em:version>
+
+ <em:targetApplication>
+ <!-- Suite -->
+ <Description>
+ <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id>
+ <em:minVersion>2.53</em:minVersion>
+ <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- EXTENSION AUTHORS!
+ DO NOT COPY THIS PROPERTY INTO YOUR INSTALL RDF FILES
+ It will cause users not to be informed of incompatibilities
+ with your extension when they are updated with Software Update
+ and your extension will become unavailable to them!
+ -->
+ <em:appManaged>true</em:appManaged>
+
+ <!-- front-end metadata -->
+ <em:name>SeaMonkey Debug and QA UI</em:name>
+ <em:description>Provides Additional Debug and QA UI for SeaMonkey development</em:description>
+ <em:creator>mozilla.org</em:creator>
+ <em:homepageURL>http://wiki.mozilla.org/SeaMonkey:Debug_And_QA_UI</em:homepageURL>
+ </Description>
+</RDF>
diff --git a/comm/suite/extensions/debugQA/jar.mn b/comm/suite/extensions/debugQA/jar.mn
new file mode 100644
index 0000000000..7f39b7ce5a
--- /dev/null
+++ b/comm/suite/extensions/debugQA/jar.mn
@@ -0,0 +1,16 @@
+# 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/.
+
+[extensions/debugQA@mozilla.org] debugqa.jar:
+% content debugqa %content/debugqa/
+% overlay chrome://navigator/content/navigatorOverlay.xul chrome://debugqa/content/debugQAMenuOverlay.xul
+% overlay chrome://editor/content/editor.xul chrome://debugqa/content/debugQAEditorOverlay.xul
+% overlay chrome://navigator/content/navigator.xul chrome://debugqa/content/debugQANavigatorOverlay.xul
+ content/debugqa/debugQAEditorOverlay.js (content/debugQAEditorOverlay.js)
+ content/debugqa/debugQAEditorOverlay.xul (content/debugQAEditorOverlay.xul)
+ content/debugqa/debugQAMenuOverlay.js (content/debugQAMenuOverlay.js)
+ content/debugqa/debugQAMenuOverlay.xul (content/debugQAMenuOverlay.xul)
+ content/debugqa/debugQANavigatorOverlay.xul (content/debugQANavigatorOverlay.xul)
+ content/debugqa/debugQATextEditorShell.xul (content/debugQATextEditorShell.xul)
+ content/debugqa/EditorInitPage.html (content/EditorInitPage.html)
diff --git a/comm/suite/extensions/debugQA/locales/en-US/debugQAEditorOverlay.dtd b/comm/suite/extensions/debugQA/locales/en-US/debugQAEditorOverlay.dtd
new file mode 100644
index 0000000000..559fe137b1
--- /dev/null
+++ b/comm/suite/extensions/debugQA/locales/en-US/debugQAEditorOverlay.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<!-- WARNING! This file contains UTF-8 encoded characters!
+ - If this ==> … <== doesn't look like an ellipsis (three dots in a row),
+ - your editor isn't using UTF-8 encoding and may munge up the document!
+ -->
+
+<!-- Debug menu items -->
+<!ENTITY debugMenu.label "Debug">
+<!ENTITY newEditorTestPage.label "Composer with Test Page">
+<!ENTITY textEditorCmd.label "Plaintext Editor">
+<!ENTITY outputTextCmd.label "Output Text">
+<!ENTITY outputHTMLCmd.label "Output HTML">
+<!ENTITY insertTextCmd.label "Insert Text">
+<!ENTITY testSelectionCmd.label "Test Selection">
+<!ENTITY testDocumentCmd.label "Test Document">
+<!-- Unused after bug 1792764 but kept for 2.53 branch. -->
+<!ENTITY executeJSTransactionViaTxmgr.label "Execute JS Transaction Via Transaction Manager">
+<!ENTITY executeJSTransactionViaEditor.label "Execute JS Transaction Via Editor">
+<!ENTITY setFocusCmd.label "Set Focus">
+
+<!-- Text editor values -->
+<!ENTITY textEditorWindow.titlemodifier "Text Editor">
+
+<!ENTITY statusText.label "Done loading page">
diff --git a/comm/suite/extensions/debugQA/locales/en-US/debugQANavigatorOverlay.properties b/comm/suite/extensions/debugQA/locales/en-US/debugQANavigatorOverlay.properties
new file mode 100644
index 0000000000..a54620256c
--- /dev/null
+++ b/comm/suite/extensions/debugQA/locales/en-US/debugQANavigatorOverlay.properties
@@ -0,0 +1,6 @@
+# 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/.
+
+# LOCALIZATION NOTE: the %S are replaced by app name and build ID respectively
+titlemodifier=%S {Build ID: %S}
diff --git a/comm/suite/extensions/debugQA/locales/jar.mn b/comm/suite/extensions/debugQA/locales/jar.mn
new file mode 100644
index 0000000000..d8f489f539
--- /dev/null
+++ b/comm/suite/extensions/debugQA/locales/jar.mn
@@ -0,0 +1,10 @@
+#filter substitution
+# 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/.
+
+
+[extensions/debugQA@mozilla.org] debugqa.jar:
+% locale debugQA @AB_CD@ %locale/@AB_CD@/debugQA/
+ locale/@AB_CD@/debugQA/debugQAEditorOverlay.dtd (en-US/debugQAEditorOverlay.dtd)
+ locale/@AB_CD@/debugQA/debugQANavigatorOverlay.properties (en-US/debugQANavigatorOverlay.properties)
diff --git a/comm/suite/extensions/debugQA/locales/moz.build b/comm/suite/extensions/debugQA/locales/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/extensions/debugQA/locales/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/extensions/debugQA/moz.build b/comm/suite/extensions/debugQA/moz.build
new file mode 100644
index 0000000000..c43aa55eff
--- /dev/null
+++ b/comm/suite/extensions/debugQA/moz.build
@@ -0,0 +1,15 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES["MOZ_APP_VERSION"] = CONFIG["MOZ_APP_VERSION"]
+DEFINES["MOZ_APP_MAXVERSION"] = CONFIG["MOZ_APP_MAXVERSION"]
+
+DIRS += ["locales"]
+
+FINAL_TARGET_PP_FILES.extensions["debugQA@mozilla.org"] += [
+ "install.rdf.in"
+]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/extensions/jar.mn b/comm/suite/extensions/jar.mn
new file mode 100644
index 0000000000..cff35a260b
--- /dev/null
+++ b/comm/suite/extensions/jar.mn
@@ -0,0 +1,7 @@
+# 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 %content/
+ content/built_in_addons.json (built_in_addons.json)
diff --git a/comm/suite/extensions/moz.build b/comm/suite/extensions/moz.build
new file mode 100644
index 0000000000..8bffaa5e51
--- /dev/null
+++ b/comm/suite/extensions/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG["MOZ_DEBUGQA"]:
+ DIRS += [
+ "debugQA",
+ ]
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/installer/Makefile.in b/comm/suite/installer/Makefile.in
new file mode 100644
index 0000000000..d4acf3e890
--- /dev/null
+++ b/comm/suite/installer/Makefile.in
@@ -0,0 +1,196 @@
+# 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/.
+
+STANDALONE_MAKEFILE := 1
+
+include $(topsrcdir)/config/rules.mk
+
+MOZ_PKG_REMOVALS = $(srcdir)/removed-files.in
+
+MOZ_PKG_MANIFEST = $(srcdir)/package-manifest.in
+# Be fatal, except when building with XULRunner which already bundles some files.
+ifndef SYSTEM_LIBXUL
+MOZ_PKG_FATAL_WARNINGS = 1
+endif
+
+DEFINES += -DPKG_LOCALE_MANIFEST=$(topobjdir)/comm/suite/installer/locale-manifest.in
+MOZ_CHROME_LOCALE_ENTRIES=@RESPATH@/chrome/
+
+MOZ_PKG_DUPEFLAGS = \
+ -w \
+ -f $(srcdir)/allowed-dupes.mn \
+ -f $(topsrcdir)/browser/installer/allowed-dupes.mn \
+ $(NULL)
+
+MOZ_NONLOCALIZED_PKG_LIST = \
+ xpcom \
+ browser \
+ mail \
+ $(NULL)
+
+MOZ_LOCALIZED_PKG_LIST = \
+ $(AB_CD) \
+ $(NULL)
+
+DEFINES += -DMOZ_APP_NAME=$(MOZ_APP_NAME) -DPREF_DIR=$(PREF_DIR)
+
+DEFINES += -DJAREXT=
+
+ifdef ENABLE_TESTS
+DEFINES += -DENABLE_TESTS=1
+endif
+
+ifdef MOZ_ANGLE_RENDERER
+DEFINES += -DMOZ_ANGLE_RENDERER=$(MOZ_ANGLE_RENDERER)
+ifdef MOZ_D3DCOMPILER_VISTA_DLL
+DEFINES += -DMOZ_D3DCOMPILER_VISTA_DLL=$(MOZ_D3DCOMPILER_VISTA_DLL)
+endif
+endif
+
+ifdef NIGHTLY_BUILD
+DEFINES += -DNIGHTLY_BUILD=1
+endif
+
+ifdef MOZ_DEBUG
+DEFINES += -DMOZ_DEBUG=1
+endif
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),gtk)
+DEFINES += -DMOZ_GTK=1
+endif
+
+ifdef MOZ_DEBUGQA
+DEFINES += -DMOZ_PACKAGE_DEBUGQA=1
+endif
+
+ifdef MOZ_IRC
+DEFINES += -DMOZ_IRC=1
+endif
+
+ifdef MOZ_UPDATER
+DEFINES += -DMOZ_UPDATER=1
+endif
+ifdef MOZ_SYSTEM_NSPR
+DEFINES += -DMOZ_SYSTEM_NSPR=1
+endif
+
+ifdef MOZ_SYSTEM_NSS
+DEFINES += -DMOZ_SYSTEM_NSS=1
+endif
+
+ifdef NECKO_WIFI
+DEFINES += -DNECKO_WIFI=1
+endif
+
+ifdef MOZ_BUNDLED_FONTS
+DEFINES += -DMOZ_BUNDLED_FONTS=1
+endif
+
+# Set MSVC dlls version to package, if any.
+# With VS2015+ it does not make sense to define the ucrt libs without
+# the base c++ libs and vice versa.
+ifdef MOZ_NO_DEBUG_RTL
+ifdef WIN32_REDIST_DIR
+ifdef WIN_UCRT_REDIST_DIR
+DEFINES += -DMOZ_PACKAGE_MSVC_DLLS=1
+DEFINES += -DMSVC_C_RUNTIME_DLL=$(MSVC_C_RUNTIME_DLL)
+DEFINES += -DMSVC_CXX_RUNTIME_DLL=$(MSVC_CXX_RUNTIME_DLL)
+DEFINES += -DMOZ_PACKAGE_WIN_UCRT_DLLS=1
+endif
+endif
+endif
+
+ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET)))
+DEFINES += -DMOZ_SHARED_MOZGLUE=1
+endif
+
+ifneq (en-US, $(AB_CD))
+DEFINES += -DLOCALE_BUILD=1
+endif
+
+DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
+
+ifdef MAKENSISU
+DEFINES += -DHAVE_MAKENSISU=1
+endif
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+MOZ_PKG_MAC_DSSTORE=$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/dsstore
+MOZ_PKG_MAC_BACKGROUND=$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/background.png
+MOZ_PKG_MAC_ICON=$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/disk.icns
+MOZ_PKG_MAC_EXTRA=--symlink "/Applications:/ "
+endif
+
+NON_OMNIJAR_FILES = \
+ defaults/messenger/mailViews.dat \
+ defaults/profile/panels.rdf \
+ defaults/profile/mimeTypes.rdf \
+ defaults/profile/chrome/userChrome-example.css \
+ defaults/profile/chrome/userContent-example.css \
+ $(NULL)
+
+include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
+
+ifeq (Darwin, $(OS_ARCH))
+BINPATH = $(_BINPATH)
+DEFINES += -DAPPNAME='$(_APPNAME)'
+else
+BINPATH = bin
+endif
+DEFINES += -DBINPATH=$(BINPATH)
+
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+RESPATH = $(_APPNAME)/Contents/Resources
+else
+RESPATH = $(BINPATH)
+endif
+DEFINES += -DRESPATH='$(RESPATH)'
+
+LPROJ_ROOT = $(firstword $(subst -, ,$(AB_CD)))
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+ifeq (zh-TW,$(AB_CD))
+LPROJ_ROOT := $(subst -,_,$(AB_CD))
+endif
+endif
+DEFINES += -DLPROJ_ROOT=$(LPROJ_ROOT)
+
+ifdef CLANG_CXX
+DEFINES += -DCLANG_CXX
+endif
+ifdef CLANG_CL
+DEFINES += -DCLANG_CL
+endif
+
+ifdef LLVM_SYMBOLIZER
+DEFINES += -DLLVM_SYMBOLIZER=$(notdir $(LLVM_SYMBOLIZER))
+endif
+ifdef MOZ_CLANG_RT_ASAN_LIB_PATH
+DEFINES += -DMOZ_CLANG_RT_ASAN_LIB=$(notdir $(MOZ_CLANG_RT_ASAN_LIB_PATH))
+endif
+
+libs::
+ $(MAKE) -C $(topobjdir)/comm/suite/locales langpack
+
+ifeq (WINNT,$(OS_ARCH))
+PKGCOMP_FIND_OPTS =
+else
+PKGCOMP_FIND_OPTS = -L
+endif
+ifeq (Darwin, $(OS_ARCH))
+FINDPATH = $(_APPNAME)/Contents/MacOS
+else
+FINDPATH=bin
+endif
+
+package-compare:: $(MOZ_PKG_MANIFEST)
+ cd $(DIST); find $(PKGCOMP_FIND_OPTS) '$(FINDPATH)' -type f | sort > bin-list.txt
+ $(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) $(MOZ_PKG_MANIFEST)) | grep '^$(BINPATH)' | sed -e 's/^\///' | sort > $(DIST)/pack-list.txt
+ -diff -u $(DIST)/pack-list.txt $(DIST)/bin-list.txt
+ rm -f $(DIST)/pack-list.txt $(DIST)/bin-list.txt
+
+# The comm-* source stamp is already there.
+PLATFORM_SOURCE_STAMP = $(firstword $(shell hg -R "$(topsrcdir)" parent --template="{node|short}\n" 2>/dev/null))
+PLATFORM_SOURCE_REPO = $(shell hg -R "$(topsrcdir)" showconfig paths.default 2>/dev/null | sed -e "s/^ssh:/https:/")
+make-sourcestamp-file::
+ @echo "$(PLATFORM_SOURCE_REPO)/rev/$(PLATFORM_SOURCE_STAMP)" >> $(MOZ_SOURCESTAMP_FILE)
diff --git a/comm/suite/installer/allowed-dupes.mn b/comm/suite/installer/allowed-dupes.mn
new file mode 100644
index 0000000000..66e730c0f1
--- /dev/null
+++ b/comm/suite/installer/allowed-dupes.mn
@@ -0,0 +1,349 @@
+# Known duplicate files
+# This file is ideally removed, but some existing files will be grandfathered in
+# See bug 1303184, bug 1313670. bug 1314892
+#
+# PLEASE DO NOT ADD MORE EXCEPTIONS TO THIS LIST UNLESS NECESSARY FOR THEMES OR EXTENSIONS
+#
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/console/error-dash.png
+chrome/classic/skin/classic/communicator/console/console-error-dash.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-radio-dis.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-radio.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/sidebar/sbpicker-arrow.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/scrollbar/btn-dn.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/arrow/arrow-lft-sharp.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/scrollbar/btn-lft.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/arrow/arrow-rit-sharp.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/scrollbar/btn-rit.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-check-dis.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-check.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/console/error-caret.png
+chrome/classic/skin/classic/communicator/console/console-error-caret.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/gtk/tree/sort-asc.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/mac/tree/sort-dsc.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/win/tree/sort-dsc.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/gtk/tree/sort-dsc.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/mac/tree/sort-asc.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/win/tree/sort-asc.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/cancel.png
+chrome/toolkit/skin/classic/mozapps/extensions/cancel.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/multicolor.png
+chrome/classic/skin/classic/editor/icons/multicolor.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/tree/checkbox.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/dot.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/navigator/icons/tab-drag-indicator.png
+chrome/classic/skin/classic/navigator/icons/tab-drag-indicator.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/viewsource/viewsource.css
+chrome/toolkit/skin/classic/mozapps/viewsource/viewsource.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/progress-failed.png
+chrome/classic/skin/classic/editor/icons/progress-failed.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/playButton.svg
+chrome/toolkit/skin/classic/global/media/playButton.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/tree/checkbox-checked.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/check.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/folderPaneExtras.css
+chrome/classic/skin/classic/messenger/folderPaneExtras.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/progress-done.png
+chrome/classic/skin/classic/editor/icons/progress-done.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/threadPaneExtras.css
+chrome/classic/skin/classic/messenger/threadPaneExtras.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/img-align-lft.png
+chrome/classic/skin/classic/editor/icons/img-align-left.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/img-align-rit.png
+chrome/classic/skin/classic/editor/icons/img-align-right.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/smime/certFetchingStatus.css
+chrome/classic/skin/classic/messenger/smime/certFetchingStatus.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/editmode-tags.png
+chrome/classic/skin/classic/editor/icons/editmode-tags.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/local-mailhost.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/server-local.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/messageKeywords.css
+chrome/classic/skin/classic/messenger/messageKeywords.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/pauseButton.svg
+chrome/toolkit/skin/classic/global/media/pauseButton.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/search/engineManager.css
+chrome/classic/skin/classic/communicator/search/engineManager.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/editmode-preview.png
+chrome/classic/skin/classic/editor/icons/editmode-preview.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/sidebar/sidebarListView.css
+chrome/classic/skin/classic/communicator/sidebar/sidebarListView.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/TopLevelVideoDocument.css
+chrome/toolkit/skin/classic/global/media/TopLevelVideoDocument.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/dialogs.css
+chrome/classic/skin/classic/communicator/dialogs.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/addressbook/sidebarPanel.css
+chrome/classic/skin/classic/messenger/addressbook/sidebarPanel.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/smime/msgCompSecurityInfo.css
+chrome/classic/skin/classic/messenger/smime/msgCompSecurityInfo.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/dataman/datamanIcon-16.png
+chrome/classic/skin/classic/communicator/dataman/datamanIcon-16.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/TopLevelImageDocument.css
+chrome/toolkit/skin/classic/global/media/TopLevelImageDocument.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/prefPanels.css
+chrome/classic/skin/classic/messenger/prefPanels.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/lock-broken.png
+chrome/classic/skin/classic/communicator/icons/lock-broken.png
+chrome/en-US/locale/en-US/global-platform/mac/intl.properties
+chrome/en-US/locale/en-US/global-platform/unix/intl.properties
+chrome/en-US/locale/en-US/global-platform/win/intl.properties
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/lock-secure.png
+chrome/classic/skin/classic/communicator/icons/lock-secure.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/lock-insecure.png
+chrome/classic/skin/classic/communicator/icons/lock-insecure.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/closeFullScreenVideo.png
+chrome/classic/skin/classic/communicator/icons/closeFullScreenVideo.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/smime/msgReadSecurityInfo.css
+chrome/classic/skin/classic/messenger/smime/msgReadSecurityInfo.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/fullscreen-video.css
+chrome/classic/skin/classic/communicator/fullscreen-video.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/smime/msgReadSMIMEOverlay.css
+chrome/classic/skin/classic/messenger/smime/msgReadSMIMEOverlay.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/smime/msgHdrViewSMIMEOverlay.css
+chrome/classic/skin/classic/messenger/smime/msgHdrViewSMIMEOverlay.css
+chrome/classic/skin/classic/communicator/downloads/dl-remove.png
+chrome/classic/skin/classic/messenger/icons/folder-trash.png
+modules/commonjs/sdk/ui/button/view/events.js
+modules/commonjs/sdk/ui/state/events.js
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/folder-inbox-open.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/folder-inbox.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/browserRequest.css
+chrome/classic/skin/classic/messenger/browserRequest.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/start.css
+chrome/classic/skin/classic/messenger/start.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/message-news-kill-offl.png
+chrome/classic/skin/classic/messenger/icons/message-news-kill-offl.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/message-news-kill.png
+chrome/classic/skin/classic/messenger/icons/message-news-kill.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/message-news-attach-kill-offl.png
+chrome/classic/skin/classic/messenger/icons/message-news-attach-kill-offl.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/message-news-attach-kill.png
+chrome/classic/skin/classic/messenger/icons/message-news-attach-kill.png
+chrome/en-US/locale/en-US/global-platform/unix/accessible.properties
+chrome/en-US/locale/en-US/global-platform/win/accessible.properties
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/notification-16.png
+chrome/classic/skin/classic/communicator/icons/notification-16.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/thread-closed-offl-kill.png
+chrome/classic/skin/classic/messenger/icons/thread-closed-offl-kill.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/thread-closed-kill.png
+chrome/classic/skin/classic/messenger/icons/thread-closed-kill.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/insecure.png
+chrome/classic/skin/classic/messenger/icons/insecure.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/plugins/pluginGeneric-16.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/thread-new-closed-offl-kill.png
+chrome/classic/skin/classic/messenger/icons/thread-new-closed-offl-kill.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/thread-new-closed-kill.png
+chrome/classic/skin/classic/messenger/icons/thread-new-closed-kill.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/secure.png
+chrome/classic/skin/classic/messenger/icons/secure.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/places/calendar.png
+chrome/classic/skin/classic/communicator/places/calendar.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/identity.png
+chrome/classic/skin/classic/communicator/icons/identity.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/information-16.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/notfound.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/addressbook/cardDialog.css
+chrome/classic/skin/classic/messenger/addressbook/cardDialog.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/addressbook/icons/contact-generic-tiny.png
+chrome/classic/skin/classic/messenger/addressbook/icons/contact-generic-tiny.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/feedIcon16.png
+chrome/classic/skin/classic/communicator/icons/feedIcon16.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/phishing.png
+chrome/classic/skin/classic/messenger/icons/phishing.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/places/history.png
+chrome/classic/skin/classic/communicator/places/history.png
+res/table-remove-column-active.gif
+res/table-remove-row-active.gif
+res/table-remove-column-hover.gif
+res/table-remove-row-hover.gif
+res/table-remove-column.gif
+res/table-remove-row.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger-newsblog/icons/rss-feed.png
+chrome/classic/skin/classic/messenger-newsblog/icons/rss-feed.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/plugins/pluginBlocked-16.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/plugins/pluginBlocked.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/plugins/pluginGeneric.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/info.png
+chrome/classic/skin/classic/messenger/icons/info.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/brand/throbber16-single.png
+chrome/classic/skin/classic/communicator/brand/throbber16-single.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-sealed.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-sealed.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/geo.png
+chrome/classic/skin/classic/communicator/icons/geo.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/save.png
+chrome/classic/skin/classic/communicator/icons/save.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-foot.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-foot.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-tongue.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-tongue.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-embarrassed.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-embarrassed.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-innocent.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-innocent.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-yell.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-yell.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-kiss.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-kiss.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-undecided.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-undecided.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/downloads/downloadButtons.png
+chrome/classic/skin/classic/communicator/downloads/downloadButtons.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-money.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-money.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-cool.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-cool.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/editor/icons/progress-busy.png
+chrome/classic/skin/classic/editor/icons/progress-busy.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-wink.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-wink.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-surprise.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-surprise.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-smile.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-smile.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-frown.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-frown.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-laughing.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-laughing.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/smileys/smiley-cry.png
+chrome/classic/skin/classic/communicator/icons/smileys/smiley-cry.png
+modules/devtools/Loader.jsm
+modules/devtools/shared/Loader.jsm
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/junk.png
+chrome/classic/skin/classic/messenger/icons/junk.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/rating-not-won.png
+chrome/toolkit/skin/classic/mozapps/extensions/rating-not-won.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/category-extensions.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/extensionGeneric.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/xpinstall/xpinstallItemGeneric.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/rating-won.png
+chrome/toolkit/skin/classic/mozapps/extensions/rating-won.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/plugins/contentPluginCrashed.png
+chrome/toolkit/skin/classic/mozapps/plugins/contentPluginCrashed.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/category-dictionaries.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/dictionaryGeneric.png
+chrome/toolkit/skin/classic/mozapps/extensions/dictionaryGeneric.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/icons/new-mail-alert.png
+chrome/classic/skin/classic/messenger/icons/new-mail-alert.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/navigator/webDeveloper.css
+chrome/classic/skin/classic/navigator/webDeveloper.css
+chrome/comm/content/communicator/places/bookmarkProperties.xul
+chrome/comm/content/communicator/places/bookmarkProperties2.xul
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/audioFeedIcon.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/feedIcon.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/videoFeedIcon.png
+chrome/classic/skin/classic/communicator/icons/audioFeedIcon.png
+chrome/classic/skin/classic/communicator/icons/feedIcon.png
+chrome/classic/skin/classic/communicator/icons/videoFeedIcon.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/brand/throbber-single.png
+chrome/classic/skin/classic/communicator/brand/throbber-single.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/notification-icons.svg
+chrome/classic/skin/classic/communicator/icons/notification-icons.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/category-languages.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/localeGeneric.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/heart.png
+chrome/toolkit/skin/classic/mozapps/extensions/heart.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/imagedoc-darknoise.png
+chrome/toolkit/skin/classic/global/media/imagedoc-darknoise.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/autoscroll.png
+chrome/toolkit/skin/classic/global/icons/autoscroll.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/notification-64.png
+chrome/classic/skin/classic/communicator/icons/notification-64.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/imagedoc-lightnoise.png
+chrome/toolkit/skin/classic/global/media/imagedoc-lightnoise.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/messenger/addressbook/icons/contact-generic.png
+chrome/classic/skin/classic/messenger/addressbook/icons/contact-generic.png
+chrome/toolkit/skin/classic/mozapps/downloads/downloadButtons.png
+chrome/toolkit/skin/classic/mozapps/update/downloadButtons.png
+chrome/devtools/modules/devtools/client/themes/toolbars.css
+chrome/devtools/skin/toolbars.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/error.png
+chrome/toolkit/skin/classic/global/media/error.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/navigator/icons/identity.png
+chrome/classic/skin/classic/navigator/icons/identity.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/loading.png
+chrome/toolkit/skin/classic/global/icons/loading.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/mozapps/extensions/discover-logo.png
+chrome/toolkit/skin/classic/mozapps/extensions/discover-logo.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/icons/connecting.png
+chrome/classic/skin/classic/communicator/icons/connecting.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/stalled.png
+chrome/toolkit/skin/classic/global/media/stalled.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/brand/throbber16-anim.png
+chrome/classic/skin/classic/communicator/brand/throbber16-anim.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/media/throbber.png
+chrome/toolkit/skin/classic/global/media/throbber.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/brand/throbber-anim.png
+chrome/classic/skin/classic/communicator/brand/throbber-anim.png
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/spinner-arrow-down.svg
+chrome/toolkit/skin/classic/global/icons/spinner-arrow-down.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/spinner-arrow-up.svg
+chrome/toolkit/skin/classic/global/icons/spinner-arrow-up.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/datetimepopup.css
+chrome/toolkit/skin/classic/global/datetimepopup.css
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/calendar-arrow-left.svg
+chrome/toolkit/skin/classic/global/icons/calendar-arrow-left.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/calendar-arrow-right.svg
+chrome/toolkit/skin/classic/global/icons/calendar-arrow-right.svg
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/icons/input-clear.svg
+chrome/toolkit/skin/classic/global/icons/input-clear.svg
+
+# Compatibility gif files for modern theme.
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/gtk/tree/sort-dsc.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/mac/tree/sort-asc.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/win/tree/sort-asc.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/gtk/tree/sort-asc.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/mac/tree/sort-dsc.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/win/tree/sort-dsc.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-radio-dis.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-radio.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-check-dis.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/menu/menu-check.gif
+
+# Lightning
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/lightning/imip.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/lightning/imip.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/calendar/calendar-invitations-dialog.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/calendar/calendar-invitations-dialog.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/lightning/lightning-widgets.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/lightning/lightning-widgets.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/calendar/calendar-daypicker.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/calendar/calendar-daypicker.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/lightning/accountCentral.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/lightning/accountCentral.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/calendar/calendar-alarm-dialog.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/calendar/calendar-alarm-dialog.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/linux/calendar/calendar-management.css
+extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/chrome/skin/windows/calendar/calendar-management.css
+
+# Linux
+chrome/comm/content/branding/icon16.png
+chrome/comm/content/branding/icon32.png
+chrome/comm/content/branding/icon48.png
+chrome/comm/content/branding/icon64.png
+chrome/comm/content/branding/icon128.png
+chrome/icons/default/default16.png
+chrome/icons/default/default32.png
+chrome/icons/default/default48.png
+chrome/icons/default/default64.png
+chrome/icons/default/default128.png
+@MOZ_APP_NAME@
+@MOZ_APP_NAME@-bin
+
+# OSX
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/communicator/sidebar/sbpicker-arrow.gif
+chrome/toolkit/skin/classic/global/arrow/arrow-dn-sharp.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/arrow/arrow-lft-sharp.gif
+chrome/toolkit/skin/classic/global/arrow/arrow-lft-sharp.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/arrow/arrow-rit-sharp.gif
+chrome/toolkit/skin/classic/global/arrow/arrow-rit-sharp.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/arrow/arrow-lft-sharp-end.gif
+chrome/toolkit/skin/classic/global/arrow/arrow-lft-sharp-end.gif
+extensions/modern@themes.mozilla.org/chrome/modern/skin/modern/global/arrow/arrow-rit-sharp-end.gif
+chrome/toolkit/skin/classic/global/arrow/arrow-rit-sharp-end.gif
+plugin-container.app/Contents/Resources/English.lproj/InfoPlist.strings
+en.lproj/InfoPlist.strings
+chrome/toolkit/skin/classic/mozapps/extensions/category-plugins.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-discover.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-recent.png
+chrome/toolkit/skin/classic/mozapps/extensions/category-available.png
+chrome/toolkit/skin/classic/mozapps/extensions/themeGeneric.png
+chrome/toolkit/skin/classic/mozapps/extensions/localeGeneric.png
diff --git a/comm/suite/installer/license.txt b/comm/suite/installer/license.txt
new file mode 100644
index 0000000000..14e2f777f6
--- /dev/null
+++ b/comm/suite/installer/license.txt
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ 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/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/comm/suite/installer/moz.build b/comm/suite/installer/moz.build
new file mode 100644
index 0000000000..89251dc396
--- /dev/null
+++ b/comm/suite/installer/moz.build
@@ -0,0 +1,4 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
diff --git a/comm/suite/installer/package-manifest.in b/comm/suite/installer/package-manifest.in
new file mode 100644
index 0000000000..c77bde7eb4
--- /dev/null
+++ b/comm/suite/installer/package-manifest.in
@@ -0,0 +1,515 @@
+; Package file for the SeaMonkey build.
+;
+; Packaging manifest is used to copy files from dist/bin
+; to the staging directory.
+; Some other files are built in the staging directory directly,
+; so they will be implicitly packaged too.
+;
+; File format:
+;
+; [] designates a toplevel component. Example: [xpcom]
+; - in front of a file specifies it to be removed from the destination
+; * wildcard support to recursively copy the entire directory
+; ; file comment
+;
+
+; Due to Apple macOS packaging requirements, files that are in the same
+; directory on other platforms must be located in different directories on
+; macOS. The following defines allow specifying the macOS bundle
+; location which also work on other platforms.
+;
+; @BINPATH@
+; Equals Contents/MacOS/ on macOS and is the path to the main binary on other
+; platforms.
+;
+; @RESPATH@
+; Equals Contents/Resources/ on macOS and is equivalent to @BINPATH@ on other
+; platforms.
+
+#filter substitution
+
+#ifdef XP_UNIX
+#ifndef XP_MACOSX
+#define UNIX_BUT_NOT_MAC
+#endif
+#endif
+
+#ifdef XP_MACOSX
+; Mac bundle stuff
+@APPNAME@/Contents/Info.plist
+@APPNAME@/Contents/PkgInfo
+@RESPATH@/seamonkey.icns
+@RESPATH@/@LPROJ_ROOT@.lproj/*
+#endif
+
+[@AB_CD@]
+@RESPATH@/defaults/messenger/mailViews.dat
+@RESPATH@/defaults/profile/bookmarks.html
+@RESPATH@/defaults/profile/chrome/*
+@RESPATH@/defaults/profile/mimeTypes.rdf
+@RESPATH@/defaults/profile/panels.rdf
+@RESPATH@/dictionaries/*
+@RESPATH@/hyphenation/*
+#ifdef MOZ_BUNDLED_FONTS
+@RESPATH@/fonts/*
+#endif
+@RESPATH@/@PREF_DIR@/suite-l10n.js
+#ifdef HAVE_MAKENSISU
+@BINPATH@/uninstall/helper.exe
+#endif
+#ifdef MOZ_UPDATER
+@RESPATH@/update.locale
+@RESPATH@/updater.ini
+#endif
+
+#ifdef PKG_LOCALE_MANIFEST
+#include @PKG_LOCALE_MANIFEST@
+#endif
+
+[xpcom]
+@RESPATH@/dependentlibs.list
+#ifdef MOZ_SHARED_MOZGLUE
+@BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
+#endif
+#ifndef MOZ_STATIC_JS
+@BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
+#endif
+#ifndef MOZ_SYSTEM_NSPR
+#ifndef MOZ_FOLD_LIBS
+@BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
+#endif
+#endif
+#ifdef XP_MACOSX
+@BINPATH@/XUL
+#else
+@BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
+#endif
+#ifdef XP_MACOSX
+@BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
+#else
+@BINPATH@/@MOZ_CHILD_PROCESS_NAME@
+#endif
+; ANGLE GLES-on-D3D rendering library
+#ifdef MOZ_ANGLE_RENDERER
+@BINPATH@/libEGL.dll
+@BINPATH@/libGLESv2.dll
+#ifdef MOZ_D3DCOMPILER_VISTA_DLL
+@BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
+#endif
+#endif # MOZ_ANGLE_RENDERER
+
+#if defined(XP_WIN) && defined(MOZ_ENABLE_SKIA_PDF)
+@BINPATH@/pdfium.dll
+#endif
+
+#ifdef XP_WIN
+#if MOZ_PACKAGE_MSVC_DLLS
+@BINPATH@/@MSVC_C_RUNTIME_DLL@
+@BINPATH@/@MSVC_CXX_RUNTIME_DLL@
+@BINPATH@/api-ms-win-*.dll
+@BINPATH@/ucrtbase.dll
+#endif
+#endif
+
+[browser]
+; [Base Browser Files]
+#ifdef XP_WIN
+@BINPATH@/@MOZ_APP_NAME@.exe
+#else
+@BINPATH@/@MOZ_APP_NAME@-bin
+@BINPATH@/@MOZ_APP_NAME@
+#endif
+@RESPATH@/application.ini
+@RESPATH@/platform.ini
+#ifdef MOZ_UPDATER
+@RESPATH@/update-settings.ini
+#endif
+#ifndef MOZ_FOLD_LIBS
+@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
+#endif
+@BINPATH@/@DLL_PREFIX@lgpllibs@DLL_SUFFIX@
+#ifdef MOZ_FFVPX
+@BINPATH@/@DLL_PREFIX@mozavutil@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@mozavcodec@DLL_SUFFIX@
+#endif
+#ifdef MOZ_GTK
+@BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
+#ifdef MOZ_WAYLAND
+@BINPATH@/@DLL_PREFIX@mozwayland@DLL_SUFFIX@
+#endif
+#endif
+@RESPATH@/license.txt
+@RESPATH@/blocklist.xml
+#ifdef XP_WIN
+#ifdef _AMD64_
+@BINPATH@/@DLL_PREFIX@qipcap64@DLL_SUFFIX@
+#else
+@BINPATH@/@DLL_PREFIX@qipcap@DLL_SUFFIX@
+#endif
+#endif
+
+; [Components]
+#ifdef ACCESSIBILITY
+#ifdef XP_WIN
+@BINPATH@/Accessible.tlb
+@BINPATH@/AccessibleHandler.dll
+@BINPATH@/AccessibleMarshal.dll
+@BINPATH@/IA2Marshal.dll
+#endif
+#endif
+@RESPATH@/components/extensions.manifest
+@RESPATH@/components/SuiteBrowser.manifest
+@RESPATH@/components/SuiteComponents.manifest
+@RESPATH@/components/SuiteFeeds.manifest
+@RESPATH@/components/SuiteSidebar.manifest
+; JavaScript components
+@RESPATH@/components/cryptoComponents.manifest
+@RESPATH@/components/FeedConverter.js
+@RESPATH@/components/FeedWriter.js
+@RESPATH@/components/jsconsole-clhandler.js
+@RESPATH@/components/jsconsole-clhandler.manifest
+@RESPATH@/components/nsAbout.js
+@RESPATH@/components/nsBrowserContentHandler.js
+@RESPATH@/components/nsComposerCmdLineHandler.js
+@RESPATH@/components/nsComposerCmdLineHandler.manifest
+@RESPATH@/components/nsGopherProtocolStubHandler.js
+@RESPATH@/components/nsPlacesAutoComplete.js
+@RESPATH@/components/nsPlacesAutoComplete.manifest
+@RESPATH@/components/nsSessionStartup.js
+@RESPATH@/components/nsSessionStartup.manifest
+@RESPATH@/components/nsSessionStore.js
+@RESPATH@/components/nsSidebar.js
+@RESPATH@/components/nsSuiteGlue.js
+@RESPATH@/components/nsSetDefault.js
+@RESPATH@/components/nsSetDefault.manifest
+@RESPATH@/components/nsTypeAheadFind.js
+#ifdef MOZ_UPDATER
+@RESPATH@/components/nsUpdateService.manifest
+#endif
+@RESPATH@/components/ProcessSingleton.manifest
+@RESPATH@/components/Push.manifest
+@RESPATH@/components/servicesComponents.manifest
+@RESPATH@/components/servicesSettings.manifest
+@RESPATH@/components/SuiteProfileMigrator.js
+@RESPATH@/components/SuiteProfileMigrator.manifest
+#if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
+@RESPATH@/components/TestInterfaceJS.js
+@RESPATH@/components/TestInterfaceJS.manifest
+@RESPATH@/components/TestInterfaceJSMaplike.js
+#endif
+@RESPATH@/components/WebContentConverter.js
+
+; Modules
+@RESPATH@/modules/*
+
+; [Extensions]
+@RESPATH@/components/extensions-toolkit.manifest
+
+; [Browser Chrome Files]
+; Browser: Hack to get built_in_addons.json packaged
+@RESPATH@/chrome/browser@JAREXT@
+@RESPATH@/chrome/browser.manifest
+@RESPATH@/chrome/comm@JAREXT@
+@RESPATH@/chrome/comm.manifest
+@RESPATH@/chrome/toolkit@JAREXT@
+@RESPATH@/chrome/toolkit.manifest
+
+; [DevTools Startup Files]
+@RESPATH@/chrome/devtools-startup@JAREXT@
+@RESPATH@/chrome/devtools-startup.manifest
+
+; DevTools
+@RESPATH@/chrome/devtools@JAREXT@
+@RESPATH@/chrome/devtools.manifest
+@RESPATH@/@PREF_DIR@/debugger.js
+
+; classic theme
+@RESPATH@/chrome/classic@JAREXT@
+@RESPATH@/chrome/classic.manifest
+@RESPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}.xpi
+
+; modern theme
+@RESPATH@/extensions/modern@themes.mozilla.org.xpi
+
+; shell icons
+#ifdef MOZ_GTK
+@RESPATH@/chrome/icons/default/bookmarkproperties.png
+@RESPATH@/chrome/icons/default/bookmarkproperties16.png
+@RESPATH@/chrome/icons/default/bookmarkproperties48.png
+@RESPATH@/chrome/icons/default/chatzilla-window.png
+@RESPATH@/chrome/icons/default/chatzilla-window16.png
+@RESPATH@/chrome/icons/default/chatzilla-window48.png
+@RESPATH@/chrome/icons/default/default16.png
+@RESPATH@/chrome/icons/default/default22.png
+@RESPATH@/chrome/icons/default/default24.png
+@RESPATH@/chrome/icons/default/default32.png
+@RESPATH@/chrome/icons/default/default48.png
+@RESPATH@/chrome/icons/default/default64.png
+@RESPATH@/chrome/icons/default/default128.png
+@RESPATH@/chrome/icons/default/default256.png
+@RESPATH@/chrome/icons/default/downloadManager.png
+@RESPATH@/chrome/icons/default/downloadManager16.png
+@RESPATH@/chrome/icons/default/downloadManager48.png
+@RESPATH@/chrome/icons/default/editorWindow.png
+@RESPATH@/chrome/icons/default/editorWindow16.png
+@RESPATH@/chrome/icons/default/editorWindow48.png
+@RESPATH@/chrome/icons/default/findBookmarkWindow.png
+@RESPATH@/chrome/icons/default/findBookmarkWindow16.png
+@RESPATH@/chrome/icons/default/findBookmarkWindow48.png
+@RESPATH@/chrome/icons/default/findHistoryWindow.png
+@RESPATH@/chrome/icons/default/findHistoryWindow16.png
+@RESPATH@/chrome/icons/default/findHistoryWindow48.png
+@RESPATH@/chrome/icons/default/history-window.png
+@RESPATH@/chrome/icons/default/history-window16.png
+@RESPATH@/chrome/icons/default/history-window48.png
+@RESPATH@/chrome/icons/default/JSConsoleWindow.png
+@RESPATH@/chrome/icons/default/JSConsoleWindow16.png
+@RESPATH@/chrome/icons/default/JSConsoleWindow48.png
+@RESPATH@/chrome/icons/default/places.png
+@RESPATH@/chrome/icons/default/places16.png
+@RESPATH@/chrome/icons/default/places48.png
+#ifdef MOZ_UPDATER
+@RESPATH@/icons/updater.png
+#endif
+#elifdef XP_WIN
+@RESPATH@/chrome/icons/default/bookmarkproperties.ico
+@RESPATH@/chrome/icons/default/chatzilla-window.ico
+@RESPATH@/chrome/icons/default/downloadManager.ico
+@RESPATH@/chrome/icons/default/editorWindow.ico
+@RESPATH@/chrome/icons/default/findBookmarkWindow.ico
+@RESPATH@/chrome/icons/default/findHistoryWindow.ico
+@RESPATH@/chrome/icons/default/gif-file.ico
+@RESPATH@/chrome/icons/default/history-window.ico
+@RESPATH@/chrome/icons/default/html-file.ico
+@RESPATH@/chrome/icons/default/image-file.ico
+@RESPATH@/chrome/icons/default/jpeg-file.ico
+@RESPATH@/chrome/icons/default/JSConsoleWindow.ico
+@RESPATH@/chrome/icons/default/main-window.ico
+@RESPATH@/chrome/icons/default/places.ico
+@RESPATH@/chrome/icons/default/script-file.ico
+@RESPATH@/chrome/icons/default/xml-file.ico
+@RESPATH@/chrome/icons/default/xul-file.ico
+#endif
+
+; [Default Preferences]
+; All the browser/general pref files must be part of base to prevent migration bugs
+@RESPATH@/@PREF_DIR@/suite-prefs.js
+@RESPATH@/@PREF_DIR@/composer.js
+@RESPATH@/greprefs.js
+@RESPATH@/defaults/autoconfig/prefcalls.js
+@RESPATH@/defaults/permissions
+@RESPATH@/defaults/settings/blocklists
+@RESPATH@/defaults/settings/main
+; Warning: changing the path to channel-prefs.js can cause bugs. (Bug 756325)
+@RESPATH@/defaults/pref/channel-prefs.js
+
+; [Layout Engine Resources]
+; Layout debug extension.
+#ifdef ENABLE_TESTS
+#ifdef MOZ_DEBUG
+@RESPATH@/chrome/layoutdebug@JAREXT@
+@RESPATH@/chrome/layoutdebug.manifest
+#endif
+#endif
+; Style Sheets, Graphics and other Resources used by the layout engine.
+@RESPATH@/res/contenteditable.css
+@RESPATH@/res/designmode.css
+@RESPATH@/res/EditorOverride.css
+@RESPATH@/res/grabber.gif
+@RESPATH@/res/language.properties
+@RESPATH@/res/svg.css
+@RESPATH@/res/table-add-column-after-active.gif
+@RESPATH@/res/table-add-column-after-hover.gif
+@RESPATH@/res/table-add-column-after.gif
+@RESPATH@/res/table-add-column-before-active.gif
+@RESPATH@/res/table-add-column-before-hover.gif
+@RESPATH@/res/table-add-column-before.gif
+@RESPATH@/res/table-add-row-after-active.gif
+@RESPATH@/res/table-add-row-after-hover.gif
+@RESPATH@/res/table-add-row-after.gif
+@RESPATH@/res/table-add-row-before-active.gif
+@RESPATH@/res/table-add-row-before-hover.gif
+@RESPATH@/res/table-add-row-before.gif
+@RESPATH@/res/table-remove-column-active.gif
+@RESPATH@/res/table-remove-column-hover.gif
+@RESPATH@/res/table-remove-column.gif
+@RESPATH@/res/table-remove-row-active.gif
+@RESPATH@/res/table-remove-row-hover.gif
+@RESPATH@/res/table-remove-row.gif
+#ifdef XP_MACOSX
+@RESPATH@/res/cursors/*
+#endif
+@RESPATH@/res/fonts/*
+@RESPATH@/res/dtd/*
+#ifdef XP_MACOSX
+@RESPATH@/res/MainMenu.nib/
+#endif
+
+; Content-accessible resources.
+@RESPATH@/contentaccessible/*
+
+; [Personal Security Manager]
+;
+; NSS libraries are signed in the staging directory,
+; meaning their .chk files are created there directly.
+;
+#ifndef MOZ_SYSTEM_NSS
+#if defined(XP_LINUX) && !defined(ANDROID)
+@BINPATH@/@DLL_PREFIX@freeblpriv3@DLL_SUFFIX@
+#else
+@BINPATH@/@DLL_PREFIX@freebl3@DLL_SUFFIX@
+#endif
+@BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
+#ifndef MOZ_FOLD_LIBS
+@BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
+#endif
+@BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@
+#endif
+@RESPATH@/chrome/pippki@JAREXT@
+@RESPATH@/chrome/pippki.manifest
+
+; For process sandboxing
+#if defined(MOZ_SANDBOX)
+#if defined(XP_LINUX)
+@BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
+#endif
+#endif
+
+; for Solaris SPARC
+#ifdef SOLARIS
+bin/libfreebl_32fpu_3.so
+bin/libfreebl_32int_3.so
+bin/libfreebl_32int64_3.so
+#endif
+
+; [MAN pages]
+#ifdef UNIX_BUT_NOT_MAC
+man/*
+#endif
+
+; [Updater]
+;
+#ifdef MOZ_UPDATER
+#ifdef XP_MACOSX
+@BINPATH@/updater.app/
+#else
+@BINPATH@/updater@BIN_SUFFIX@
+#endif
+#endif
+
+; [Crash Reporter]
+;
+#ifdef MOZ_CRASHREPORTER
+#ifdef XP_MACOSX
+@BINPATH@/crashreporter.app/
+#else
+@BINPATH@/crashreporter@BIN_SUFFIX@
+@BINPATH@/minidump-analyzer@BIN_SUFFIX@
+@RESPATH@/crashreporter.ini
+#ifdef XP_UNIX
+@RESPATH@/Throbber-small.gif
+#endif
+#endif
+@RESPATH@/crashreporter-override.ini
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+@BINPATH@/breakpadinjector.dll
+#endif
+#endif
+
+; [ Ping Sender ]
+;
+@BINPATH@/pingsender@BIN_SUFFIX@
+
+[mail]
+; MailNews components
+
+; MailNews JS components
+@RESPATH@/components/msgMime.manifest
+
+; MailNews chrome
+@RESPATH@/chrome/messenger@JAREXT@
+@RESPATH@/chrome/messenger.manifest
+@RESPATH@/chrome/newsblog@JAREXT@
+@RESPATH@/chrome/newsblog.manifest
+#ifdef MOZ_GTK
+@RESPATH@/chrome/icons/default/abcardWindow.png
+@RESPATH@/chrome/icons/default/abcardWindow16.png
+@RESPATH@/chrome/icons/default/abcardWindow48.png
+@RESPATH@/chrome/icons/default/ablistWindow.png
+@RESPATH@/chrome/icons/default/ablistWindow16.png
+@RESPATH@/chrome/icons/default/ablistWindow48.png
+@RESPATH@/chrome/icons/default/addressbookWindow.png
+@RESPATH@/chrome/icons/default/addressbookWindow16.png
+@RESPATH@/chrome/icons/default/addressbookWindow48.png
+@RESPATH@/chrome/icons/default/messengerWindow.png
+@RESPATH@/chrome/icons/default/messengerWindow16.png
+@RESPATH@/chrome/icons/default/messengerWindow48.png
+@RESPATH@/chrome/icons/default/msgcomposeWindow.png
+@RESPATH@/chrome/icons/default/msgcomposeWindow16.png
+@RESPATH@/chrome/icons/default/msgcomposeWindow48.png
+#elifdef XP_WIN
+@RESPATH@/chrome/icons/default/abcardWindow.ico
+@RESPATH@/chrome/icons/default/ablistWindow.ico
+@RESPATH@/chrome/icons/default/addressbookWindow.ico
+@RESPATH@/chrome/icons/default/messengerWindow.ico
+@RESPATH@/chrome/icons/default/msgcomposeWindow.ico
+@RESPATH@/chrome/icons/default/calendar-alarm-dialog.ico
+@RESPATH@/chrome/icons/default/calendar-event-dialog.ico
+@RESPATH@/chrome/icons/default/calendar-event-summary-dialog.ico
+@RESPATH@/chrome/icons/default/calendar-task-dialog.ico
+@RESPATH@/chrome/icons/default/calendar-task-summary-dialog.ico
+#endif
+
+; [MailNews Default Preferences]
+@RESPATH@/@PREF_DIR@/mailnews.js
+@RESPATH@/@PREF_DIR@/mdn.js
+; @RESPATH@/@PREF_DIR@/e2e-prefs.js
+
+#ifdef MOZ_MAPI_SUPPORT
+@BINPATH@/MapiProxy.dll
+@BINPATH@/mozMapi32.dll
+#endif
+
+@RESPATH@/isp/*
+
+; Gloda
+@RESPATH@/chrome/gloda@JAREXT@
+@RESPATH@/chrome/gloda.manifest
+
+; Address Sanitizer
+#ifdef LLVM_SYMBOLIZER
+@BINPATH@/@LLVM_SYMBOLIZER@
+#endif
+
+#ifdef MOZ_CLANG_RT_ASAN_LIB
+@BINPATH@/@MOZ_CLANG_RT_ASAN_LIB@
+#endif
+
+[chatzilla]
+#ifdef MOZ_IRC
+@RESPATH@/chrome/chatzilla@JAREXT@
+@RESPATH@/chrome/chatzilla.manifest
+@RESPATH@/components/chatzilla-service.js
+@RESPATH@/components/chatzilla-service.manifest
+#endif
+
+[debugqa]
+#ifdef MOZ_PACKAGE_DEBUGQA
+@RESPATH@/extensions/debugQA@mozilla.org.xpi
+#endif
+
+[calendar]
+@RESPATH@/chrome/calendar@JAREXT@
+@RESPATH@/chrome/calendar.manifest
+
+@RESPATH@/@PREF_DIR@/calendar.js
+
+# Files added to components directory via `FINAL_TARGET_FILES.components`.
+@RESPATH@/components/calCachedCalendar.js
+@RESPATH@/components/calICSService-worker.js
+@RESPATH@/components/calItemBase.js
diff --git a/comm/suite/installer/removed-files.in b/comm/suite/installer/removed-files.in
new file mode 100644
index 0000000000..b705de305e
--- /dev/null
+++ b/comm/suite/installer/removed-files.in
@@ -0,0 +1,290 @@
+# Due to Apple macOS packaging requirements files that are in the same
+# directory on other platforms must be located in different directories on
+# macOS. The following defines allow specifying the macOS bundle
+# location which also work on other platforms.
+#
+# @DIR_MACOS@
+# Equals Contents/MacOS/ on Mac OX X and is an empty string on other platforms.
+#
+# @DIR_RESOURCES@
+# Equals Contents/Resources/ on Mac OX X and is an empty string on other
+# platforms.
+
+#ifdef XP_UNIX
+#ifndef XP_MACOSX
+#define UNIX_BUT_NOT_MAC
+#endif
+#endif
+@DIR_MACOS@.autoreg
+@DIR_MACOS@chrome/icons/default/abcardWindow.xpm
+@DIR_MACOS@chrome/icons/default/abcardWindow16.xpm
+@DIR_MACOS@chrome/icons/default/ablistWindow.xpm
+@DIR_MACOS@chrome/icons/default/addressbookWindow.xpm
+@DIR_MACOS@chrome/icons/default/addressbookWindow16.xpm
+@DIR_MACOS@chrome/icons/default/bmPropsWindow.ico
+@DIR_MACOS@chrome/icons/default/bmPropsWindow.png
+@DIR_MACOS@chrome/icons/default/bmPropsWindow16.png
+@DIR_MACOS@chrome/icons/default/bmPropsWindow48.png
+@DIR_MACOS@chrome/icons/default/bmPropsWindow.xpm
+@DIR_MACOS@chrome/icons/default/bmPropsWindow16.xpm
+@DIR_MACOS@chrome/icons/default/bookmark-window.ico
+@DIR_MACOS@chrome/icons/default/bookmark-window.png
+@DIR_MACOS@chrome/icons/default/bookmark-window16.png
+@DIR_MACOS@chrome/icons/default/bookmark-window48.png
+@DIR_MACOS@chrome/icons/default/bookmark-window.xpm
+@DIR_MACOS@chrome/icons/default/bookmark-window16.xpm
+@DIR_MACOS@chrome/icons/default/calendar-window.xpm
+@DIR_MACOS@chrome/icons/default/calendar-window16.xpm
+@DIR_MACOS@chrome/icons/default/chatzilla-window.xpm
+@DIR_MACOS@chrome/icons/default/chatzilla-window16.xpm
+@DIR_MACOS@chrome/icons/default/default.xpm
+@DIR_MACOS@chrome/icons/default/default16.xpm
+@DIR_MACOS@chrome/icons/default/downloadManager.xpm
+@DIR_MACOS@chrome/icons/default/downloadManager16.xpm
+@DIR_MACOS@chrome/icons/default/editorWindow.xpm
+@DIR_MACOS@chrome/icons/default/editorWindow16.xpm
+@DIR_MACOS@chrome/icons/default/findBookmarkWindow.xpm
+@DIR_MACOS@chrome/icons/default/findBookmarkWindow16.xpm
+@DIR_MACOS@chrome/icons/default/findHistoryWindow.xpm
+@DIR_MACOS@chrome/icons/default/findHistoryWindow16.xpm
+@DIR_MACOS@chrome/icons/default/history-window.xpm
+@DIR_MACOS@chrome/icons/default/history-window16.xpm
+@DIR_MACOS@chrome/icons/default/JSConsoleWindow.xpm
+@DIR_MACOS@chrome/icons/default/jsconsoleWindow.xpm
+@DIR_MACOS@chrome/icons/default/jsconsoleWindow16.xpm
+@DIR_MACOS@chrome/icons/default/main-window.xpm
+@DIR_MACOS@chrome/icons/default/main-window16.xpm
+@DIR_MACOS@chrome/icons/default/messengerWindow.xpm
+@DIR_MACOS@chrome/icons/default/messengerWindow16.xpm
+@DIR_MACOS@chrome/icons/default/msgcomposeWindow.xpm
+@DIR_MACOS@chrome/icons/default/msgcomposeWindow16.xpm
+@DIR_MACOS@chrome/icons/default/venkman-window.xpm
+@DIR_MACOS@chrome/icons/default/venkman-window16.xpm
+#ifndef MOZ_CRASHREPORTER
+#ifdef XP_MACOSX
+@DIR_MACOS@crashreporter.app/
+#else
+@DIR_MACOS@crashreporter@BIN_SUFFIX@
+@DIR_MACOS@crashreporter.ini
+#endif
+#endif
+@DIR_MACOS@defaults/profile/localstore.rdf
+@DIR_MACOS@defaults/profile/search.rdf
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/components/components.list
+@DIR_MACOS@extensions/inspector@mozilla.org/components/components.list
+
+# Remove unpacked lightning extension.
+# Depending on the previous installed version it might reside in one of two
+# different places.
+@DIR_RESOURCES@extensions/
+@DIR_RESOURCES@extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/*
+@DIR_RESOURCES@distribution/
+@DIR_RESOURCES@distribution/extensions/
+@DIR_RESOURCES@distribution/extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}/*
+
+# Remove old extensions in distribution.
+@DIR_RESOURCES@distribution/extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}.xpi
+@DIR_RESOURCES@distribution/extensions/{e2fda1a4-762b-4020-b5ad-a41df1933103}.xpi
+@DIR_RESOURCES@distribution/extensions/debugQA@mozilla.org.xpi
+@DIR_RESOURCES@distribution/extensions/inspector@mozilla.org.xpi
+
+@DIR_MACOS@hyphenation/
+@DIR_MACOS@hyphenation/hyph_af.dic
+@DIR_MACOS@hyphenation/hyph_bg.dic
+@DIR_MACOS@hyphenation/hyph_ca.dic
+@DIR_MACOS@hyphenation/hyph_cy.dic
+@DIR_MACOS@hyphenation/hyph_da.dic
+@DIR_MACOS@hyphenation/hyph_de-1901.dic
+@DIR_MACOS@hyphenation/hyph_de-1996.dic
+@DIR_MACOS@hyphenation/hyph_de-CH.dic
+@DIR_MACOS@hyphenation/hyph_en_US.dic
+@DIR_MACOS@hyphenation/hyph_eo.dic
+@DIR_MACOS@hyphenation/hyph_es.dic
+@DIR_MACOS@hyphenation/hyph_et.dic
+@DIR_MACOS@hyphenation/hyph_fi.dic
+@DIR_MACOS@hyphenation/hyph_fr.dic
+@DIR_MACOS@hyphenation/hyph_gl.dic
+@DIR_MACOS@hyphenation/hyph_hr.dic
+@DIR_MACOS@hyphenation/hyph_hsb.dic
+@DIR_MACOS@hyphenation/hyph_hu.dic
+@DIR_MACOS@hyphenation/hyph_ia.dic
+@DIR_MACOS@hyphenation/hyph_is.dic
+@DIR_MACOS@hyphenation/hyph_it.dic
+@DIR_MACOS@hyphenation/hyph_kmr.dic
+@DIR_MACOS@hyphenation/hyph_la.dic
+@DIR_MACOS@hyphenation/hyph_lt.dic
+@DIR_MACOS@hyphenation/hyph_mn.dic
+@DIR_MACOS@hyphenation/hyph_nb.dic
+@DIR_MACOS@hyphenation/hyph_nl.dic
+@DIR_MACOS@hyphenation/hyph_nn.dic
+@DIR_MACOS@hyphenation/hyph_pt.dic
+@DIR_MACOS@hyphenation/hyph_ru.dic
+@DIR_MACOS@hyphenation/hyph_sh.dic
+@DIR_MACOS@hyphenation/hyph_sl.dic
+@DIR_MACOS@hyphenation/hyph_sv.dic
+@DIR_MACOS@hyphenation/hyph_tr.dic
+@DIR_MACOS@hyphenation/hyph_uk.dic
+@DIR_MACOS@LICENSE
+#ifdef XP_MACOSX
+ @DIR_MACOS@run-mozilla.sh
+ @DIR_MACOS@updater.app/Contents/MacOS/updater.ini
+#endif
+@DIR_MACOS@res/arrow.gif
+@DIR_MACOS@res/arrowd.gif
+@DIR_MACOS@res/broken-image.gif
+@DIR_MACOS@res/broken-image.png
+@DIR_MACOS@res/loading-image.gif
+@DIR_MACOS@res/loading-image.png
+@DIR_MACOS@searchplugins/
+#ifndef MOZ_CRASHREPORTER
+#ifdef UNIX_BUT_NOT_MAC
+@DIR_MACOS@res/Throbber-small.gif
+#endif
+#endif
+#ifndef MOZ_UPDATER
+ @DIR_MACOS@update-settings.ini
+ #ifdef XP_MACOSX
+ @DIR_MACOS@updater.app/
+ #else
+ @DIR_MACOS@updater@BIN_SUFFIX@
+ #endif
+ @DIR_MACOS@updater.ini
+#endif
+@DIR_MACOS@update.locale
+
+@DIR_MACOS@defaults/profile/bookmarks.html
+@DIR_MACOS@defaults/pref/browser-prefs.js
+@DIR_MACOS@defaults/pref/composer.js
+@DIR_MACOS@defaults/pref/mailnews.js
+@DIR_MACOS@defaults/pref/mdn.js
+@DIR_MACOS@defaults/pref/services-aitc.js
+@DIR_MACOS@defaults/pref/services-notifications.js
+@DIR_MACOS@defaults/pref/smime.js
+@DIR_MACOS@defaults/autoconfig/
+@DIR_MACOS@defaults/autoconfig/platform.js
+@DIR_MACOS@defaults/autoconfig/prefcalls.js
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/chatzilla.jar
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/icons/
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/icons/default/
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/icons/default/chatzilla-window.xpm
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/icons/default/chatzilla-window16.xpm
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome/icons/default/chatzilla-window.ico
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/chrome.manifest
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/components/
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/components/chatzilla-service.js
+@DIR_MACOS@extensions/{59c81df5-4b7a-477b-912d-4e0fdf64e5f2}/install.rdf
+@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/
+@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.manifest
+@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
+@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
+@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/preview.png
+@DIR_MACOS@extensions/debugQA@mozilla.org/
+@DIR_MACOS@extensions/debugQA@mozilla.org/install.rdf
+@DIR_MACOS@extensions/debugQA@mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/debugQA@mozilla.org/chrome/
+@DIR_MACOS@extensions/debugQA@mozilla.org/chrome/debugqa.jar
+@DIR_MACOS@extensions/inspector@mozilla.org/
+@DIR_MACOS@extensions/inspector@mozilla.org/install.rdf
+@DIR_MACOS@extensions/inspector@mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/inspector@mozilla.org/chrome/
+@DIR_MACOS@extensions/inspector@mozilla.org/chrome/inspector.jar
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/Linux/
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/Linux/chrome/
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/Linux/chrome/icons/
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/Linux/chrome/icons/default/
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/Linux/chrome/icons/default/winInspectorMain16.xpm
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/Linux/chrome/icons/default/winInspectorMain.xpm
+@DIR_MACOS@extensions/inspector@mozilla.org/platform/WINNT/chrome/icons/default/winInspectorMain.ico
+@DIR_MACOS@extensions/inspector@mozilla.org/components/
+@DIR_MACOS@extensions/inspector@mozilla.org/components/inspector-cmdline.js
+@DIR_MACOS@extensions/inspector@mozilla.org/defaults/
+@DIR_MACOS@extensions/inspector@mozilla.org/defaults/preferences/
+@DIR_MACOS@extensions/inspector@mozilla.org/defaults/preferences/inspector.js
+@DIR_MACOS@extensions/langpack-ca@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-ca@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-ca@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-ca@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-ca@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-cs@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-cs@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-cs@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-cs@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-cs@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-de@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-de@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-de@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-de@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-de@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-es-ES@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-es-ES@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-es-ES@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-es-ES@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-es-ES@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-fi@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-fi@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-fi@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-fi@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-fi@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-fr@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-fr@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-fr@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-fr@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-fr@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-it@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-it@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-it@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-it@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-it@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-ja@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-ja@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-ja@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-ja@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-ja@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-ja-JP-mac@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-ja-JP-mac@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-ja-JP-mac@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-ja-JP-mac@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-ja-JP-mac@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-nb-NO@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-nb-NO@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-nb-NO@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-nb-NO@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-nb-NO@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-pl@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-pl@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-pl@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-pl@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-pl@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-pt-BR@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-pt-BR@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-pt-BR@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-pt-BR@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-pt-BR@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-pt-PT@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-pt-PT@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-pt-PT@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-pt-PT@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-pt-PT@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-ru@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-ru@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-ru@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-ru@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-ru@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-si@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-si@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-si@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-si@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-si@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-sk@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-sk@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-sk@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-sk@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-sk@chatzilla.mozilla.org/install.rdf
+@DIR_MACOS@extensions/langpack-sv-SE@chatzilla.mozilla.org/chrome/chatzilla.jar
+@DIR_MACOS@extensions/langpack-sv-SE@chatzilla.mozilla.org/chrome/chatzilla.manifest
+@DIR_MACOS@extensions/langpack-sv-SE@chatzilla.mozilla.org/chrome.manifest
+@DIR_MACOS@extensions/langpack-sv-SE@chatzilla.mozilla.org/install.js
+@DIR_MACOS@extensions/langpack-sv-SE@chatzilla.mozilla.org/install.rdf
diff --git a/comm/suite/installer/windows/Makefile.in b/comm/suite/installer/windows/Makefile.in
new file mode 100644
index 0000000000..7bdc367ef4
--- /dev/null
+++ b/comm/suite/installer/windows/Makefile.in
@@ -0,0 +1,55 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
+
+CONFIG_DIR = instgen
+SFX_MODULE = $(topsrcdir)/comm/other-licenses/7zstub/seamonkey/7zSD.sfx
+
+EXPAND_COMM_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/comm/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
+
+INSTALLER_FILES = \
+ app.tag \
+ nsis/installer.nsi \
+ nsis/uninstaller.nsi \
+ nsis/shared.nsh \
+ nsis/custom.nsi \
+ $(NULL)
+
+BRANDING_FILES = \
+ branding.nsi \
+ wizHeader.bmp \
+ wizHeaderRTL.bmp \
+ wizWatermark.bmp \
+ $(NULL)
+
+include $(topsrcdir)/config/config.mk
+
+ifdef IS_LANGUAGE_REPACK
+PPL_LOCALE_ARGS = \
+ --l10n-dir=$(REAL_LOCALE_MERGEDIR)/suite/installer/windows \
+ --l10n-dir=$(call EXPAND_COMM_LOCALE_SRCDIR,suite/locales)/installer/windows \
+ --l10n-dir=$(topsrcdir)/comm/suite/locales/en-US/installer/windows \
+ $(NULL)
+else
+PPL_LOCALE_ARGS=$(call EXPAND_COMM_LOCALE_SRCDIR,suite/locales)/installer/windows
+endif
+
+$(CONFIG_DIR)/setup.exe::
+ $(RM) -r $(CONFIG_DIR)
+ $(MKDIR) $(CONFIG_DIR)
+ $(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
+ $(INSTALL) $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) $(CONFIG_DIR)
+ $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+ --convert-utf8-utf16le $(topsrcdir)/comm/suite/installer/license.txt $(CONFIG_DIR)/license.txt
+ $(call py_action,preprocessor,-Fsubstitution $(DEFINES) $(ACDEFINES) \
+ $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi)
+ $(PYTHON3) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+ --preprocess-locale $(topsrcdir) \
+ $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
+
+GARBARGE_DIRS += instgen
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/makensis.mk
diff --git a/comm/suite/installer/windows/app.tag b/comm/suite/installer/windows/app.tag
new file mode 100644
index 0000000000..628717e1eb
--- /dev/null
+++ b/comm/suite/installer/windows/app.tag
@@ -0,0 +1,4 @@
+;!@Install@!UTF-8!
+Title="SeaMonkey"
+RunProgram="setup.exe"
+;!@InstallEnd@!
diff --git a/comm/suite/installer/windows/moz.build b/comm/suite/installer/windows/moz.build
new file mode 100644
index 0000000000..9a388e7c2a
--- /dev/null
+++ b/comm/suite/installer/windows/moz.build
@@ -0,0 +1,10 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES["APP_VERSION"] = CONFIG["SEAMONKEY_VERSION"]
+
+DEFINES["MOZ_APP_NAME"] = CONFIG["MOZ_APP_NAME"]
+DEFINES["MOZ_APP_DISPLAYNAME"] = CONFIG["MOZ_APP_DISPLAYNAME"]
+DEFINES["MOZILLA_VERSION"] = CONFIG["MOZILLA_VERSION"]
diff --git a/comm/suite/installer/windows/nsis/custom.nsi b/comm/suite/installer/windows/nsis/custom.nsi
new file mode 100644
index 0000000000..660f4259e4
--- /dev/null
+++ b/comm/suite/installer/windows/nsis/custom.nsi
@@ -0,0 +1,68 @@
+# 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/.
+
+!macro checkSuiteComponents
+ ; If no extensions are available skip the components page
+ ${Unless} ${FileExists} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi"
+ ${AndUnless} ${FileExists} "$EXEDIR\optional\extensions\{f13b157f-b174-47e7-a34d-4815ddfdfeb8}.xpi"
+ Abort
+ ${EndUnless}
+!macroend
+
+!macro createSuiteComponentsIni
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Text "$(OPTIONAL_COMPONENTS_LABEL)"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Top "0"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "15"
+
+ StrCpy $R1 2
+ ; Top of checkbox
+ StrCpy $R2 15
+ ; Bottom of checkbox
+ StrCpy $R3 25
+ ; Seperation between titles/text
+ StrCpy $R4 25
+
+ ${If} ${FileExists} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Text "$(DEBUGQA_TITLE)"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Left "15"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Top "$R2"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Bottom "$R3"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" State "1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Flags "GROUP"
+ ${GetSize} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi" "/S=0K" $0 $8 $9
+ SectionSetSize ${DEBUG_IDX} $0
+ IntOp $R1 $R1 + 1
+ IntOp $R2 $R2 + $R4
+ IntOp $R3 $R3 + $R4
+ ${Else}
+ ; Hide debugQA in the components page if it isn't available.
+ SectionSetText ${DEBUG_IDX} ""
+ ${EndIf}
+
+ ; Set new values for the top and bottom of labels
+ ; Top of label box
+ StrCpy $R2 27
+ ; Bottom of label box
+ StrCpy $R3 47
+
+ ${If} ${FileExists} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Type "label"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Text "$(DEBUGQA_TEXT)"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Left "30"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Top "$R2"
+ WriteINIStr "$PLUGINSDIR\components.ini" "Field $R1" Bottom "$R3"
+ IntOp $R1 $R1 + 1
+ IntOp $R2 $R2 + $R4
+ IntOp $R3 $R3 + $R4
+ ${EndIf}
+
+ WriteINIStr "$PLUGINSDIR\components.ini" "Settings" NumFields "$R1"
+
+!macroend
diff --git a/comm/suite/installer/windows/nsis/defines.nsi.in b/comm/suite/installer/windows/nsis/defines.nsi.in
new file mode 100644
index 0000000000..a4814c3a69
--- /dev/null
+++ b/comm/suite/installer/windows/nsis/defines.nsi.in
@@ -0,0 +1,64 @@
+#filter substitution
+!define AppName "SeaMonkey"
+!define AppVersion "@APP_VERSION@"
+!define GREVersion @MOZILLA_VERSION@
+!define AB_CD "@AB_CD@"
+!define NO_UNINSTALL_SURVEY
+
+!define FileMainEXE "@MOZ_APP_NAME@.exe"
+!define WindowClass "SeaMonkeyMessageWindow"
+!define DDEApplication "SeaMonkey"
+
+!define AppRegName "SeaMonkey"
+!define AppRegNameMail "SeaMonkey (Mail)"
+!define AppRegNameNews "SeaMonkey (News)"
+
+!define BrandProductName "@MOZ_APP_DISPLAYNAME@"
+
+!define BrandShortName "@MOZ_APP_DISPLAYNAME@"
+!define BrandFullName "${BrandFullNameInternal}"
+
+# ARCH is used when it is necessary to differentiate the x64 registry keys from
+# the x86 registry keys (e.g. the uninstall registry key).
+#ifdef HAVE_64BIT_BUILD
+!define HAVE_64BIT_BUILD
+!define ARCH "x64"
+!define MinSupportedVer "Microsoft Windows 7 x64"
+#else
+!define ARCH "x86"
+!define MinSupportedVer "Microsoft Windows 7"
+#endif
+
+!define MinSupportedCPU "SSE2"
+
+# AccessibleHandler.dll uses a different CLSID depending on release channel.
+# These defines must match HANDLER_CLSID defined in
+# accessible/ipc/win/handler/HandlerData.idl
+
+!if "@MOZ_UPDATE_CHANNEL@" == "default"
+#ifdef DEBUG
+!define AccessibleHandlerCLSID "{398FFD8D-5382-48F7-9E3B-19012762D39A}"
+#else
+!define AccessibleHandlerCLSID "{CE573FAF-7815-4FC2-A031-B092268ACE9E}"
+#endif
+!else if "@MOZ_UPDATE_CHANNEL@" == "nightly"
+!define AccessibleHandlerCLSID "{4629216B-8753-41BF-9527-5BFF51401671}"
+!else if "@MOZ_UPDATE_CHANNEL@" == "beta"
+!define AccessibleHandlerCLSID "{21E9F98D-A6C9-4CB5-B288-AE2FD2A96C58}"
+!else if "@MOZ_UPDATE_CHANNEL@" == "release"
+!define AccessibleHandlerCLSID "{1BAA303D-B4B9-45E5-9CCB-E3FCA3E274B6}"
+!else
+!define AccessibleHandlerCLSID "{4A195748-DCA2-45FB-9295-0A139E76A9E7}"
+!endif
+
+# File details shared by both the installer and uninstaller
+VIProductVersion "1.0.0.0"
+VIAddVersionKey "ProductName" "${BrandShortName}"
+VIAddVersionKey "CompanyName" "${CompanyName}"
+VIAddVersionKey "LegalTrademarks" "${BrandShortName} is a Trademark of The Mozilla Foundation."
+VIAddVersionKey "LegalCopyright" "${CompanyName}"
+VIAddVersionKey "FileVersion" "${AppVersion}"
+VIAddVersionKey "ProductVersion" "${AppVersion}"
+# Comments is not used but left below commented out for future reference
+# VIAddVersionKey "Comments" "Comments"
+
diff --git a/comm/suite/installer/windows/nsis/installer.nsi b/comm/suite/installer/windows/nsis/installer.nsi
new file mode 100644
index 0000000000..d9ffd78069
--- /dev/null
+++ b/comm/suite/installer/windows/nsis/installer.nsi
@@ -0,0 +1,942 @@
+# 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/.
+
+# Also requires:
+# AppAssocReg http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# CityHash http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
+# ShellLink plugin http://nsis.sourceforge.net/ShellLink_plug-in
+# UAC http://nsis.sourceforge.net/UAC_plug-in
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel user
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+Var TmpVal
+Var StartMenuDir
+Var InstallType
+Var AddStartMenuSC
+Var AddTaskbarSC
+Var AddQuickLaunchSC
+Var AddDesktopSC
+Var InstallMaintenanceService
+Var InstallOptionalExtensions
+Var RegisterDefaultAgent
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include LogicLib.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+!include MUI.nsh
+
+!insertmacro StrFilter
+!insertmacro GetOptions
+!insertmacro GetParameters
+!insertmacro GetSize
+!insertmacro WordFind
+
+; The following includes are custom.
+!include branding.nsi
+!include defines.nsi
+!include common.nsh
+!include locales.nsi
+!include custom.nsi
+
+VIAddVersionKey "FileDescription" "${BrandShortName} Installer"
+VIAddVersionKey "OriginalFilename" "setup.exe"
+
+; Must be inserted before other macros that use logging
+!insertmacro _LoggingCommon
+
+; Most commonly used macros for managing shortcuts
+!insertmacro _LoggingShortcutsCommon
+
+!insertmacro AddDisabledDDEHandlerValues
+!insertmacro AddHandlerValues
+!insertmacro ChangeMUIHeaderImage
+!insertmacro CheckForFilesInUse
+!insertmacro CheckIfRegistryKeyExists
+!insertmacro CleanUpdateDirectories
+!insertmacro CopyFilesFromDir
+!insertmacro CreateRegKey
+!insertmacro FindSMProgramsDir
+!insertmacro GetPathFromString
+!insertmacro GetParent
+!insertmacro InitHashAppModelId
+!insertmacro IsHandlerForInstallDir
+!insertmacro ManualCloseAppPrompt
+!insertmacro RegCleanMain
+!insertmacro RegCleanUninstall
+!insertmacro SetBrandNameVars
+!insertmacro UnloadUAC
+!insertmacro WriteRegStr2
+!insertmacro WriteRegDWORD2
+
+!include shared.nsh
+
+; Helper macros for ui callbacks. Insert these after shared.nsh
+!insertmacro CheckCustomCommon
+!insertmacro InstallEndCleanupCommon
+!insertmacro InstallOnInitCommon
+!insertmacro InstallStartCleanupCommon
+!insertmacro LeaveDirectoryCommon
+!insertmacro LeaveOptionsCommon
+!insertmacro OnEndCommon
+!insertmacro PreDirectoryCommon
+
+Name "${BrandFullName}"
+OutFile "setup.exe"
+!ifdef HAVE_64BIT_BUILD
+ InstallDir "$PROGRAMFILES64\${BrandFullName}\"
+!else
+ InstallDir "$PROGRAMFILES32\${BrandFullName}\"
+!endif
+ShowInstDetails nevershow
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MUI_ABORTWARNING
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_RIGHT
+!define MUI_WELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+
+; Use a right to left header image when the language is right to left
+!ifdef ${AB_CD}_rtl
+!define MUI_HEADERIMAGE_BITMAP_RTL wizHeaderRTL.bmp
+!else
+!define MUI_HEADERIMAGE_BITMAP wizHeader.bmp
+!endif
+
+/**
+ * Installation Pages
+ */
+; Welcome Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preWelcome
+!insertmacro MUI_PAGE_WELCOME
+
+; License Page
+!define MUI_PAGE_CUSTOMFUNCTION_SHOW showLicense
+!define MUI_LICENSEPAGE_CHECKBOX
+!insertmacro MUI_PAGE_LICENSE license.txt
+
+; Custom Options Page
+Page custom preOptions leaveOptions
+
+; Custom Components Page
+Page custom preComponents leaveComponents
+
+; Select Install Directory Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preDirectory
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE leaveDirectory
+!define MUI_DIRECTORYPAGE_VERIFYONLEAVE
+!insertmacro MUI_PAGE_DIRECTORY
+
+; Custom Shortcuts Page
+Page custom preShortcuts leaveShortcuts
+
+; Start Menu Folder Page Configuration
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preStartMenu
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE leaveStartMenu
+!define MUI_STARTMENUPAGE_NODISABLE
+!insertmacro MUI_PAGE_STARTMENU Application $StartMenuDir
+
+; Custom Summary Page
+Page custom preSummary leaveSummary
+
+; Install Files Page
+!insertmacro MUI_PAGE_INSTFILES
+
+; Finish Page
+!define MUI_FINISHPAGE_TITLE_3LINES
+!define MUI_FINISHPAGE_RUN
+!define MUI_FINISHPAGE_RUN_FUNCTION LaunchApp
+!define MUI_FINISHPAGE_RUN_TEXT $(LAUNCH_TEXT)
+!define MUI_PAGE_CUSTOMFUNCTION_PRE preFinish
+!insertmacro MUI_PAGE_FINISH
+
+; Use the default dialog for IDD_VERIFY for a simple Banner
+ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
+
+################################################################################
+
+; Cleanup operations to perform at the start of the installation.
+Section "-InstallStartCleanup"
+ SetDetailsPrint both
+ DetailPrint $(STATUS_CLEANUP)
+ SetDetailsPrint none
+
+ SetOutPath "$INSTDIR"
+ ${StartInstallLog} "${BrandFullName}" "${AB_CD}" "${AppVersion}" "${GREVersion}"
+
+ ; Delete the app exe to prevent launching the app while we are installing.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ; If the user closed the application it can take several seconds for it to
+ ; shut down completely. If the application is being used by another user we
+ ; can rename the file and then delete is when the system is restarted.
+ Sleep 5000
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ ${EndIf}
+
+ ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
+ ; Custom installs.
+ ; If DebugQA is installed and this install includes DebugQA remove it
+ ; from the installation directory. This will remove it if the user
+ ; deselected DebugQA on the components page.
+ ${If} ${FileExists} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi"
+ ${DeleteFile} "$INSTDIR\extensions\debugQA@mozilla.org.xpi"
+ ${If} ${FileExists} "$INSTDIR\extensions\debugQA@mozilla.org"
+ RmDir /r "$INSTDIR\extensions\debugQA@mozilla.org"
+ ${EndIf}
+ ${EndIf}
+
+ ${EndIf}
+
+ ; setup the application model id registration value
+ ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ ; Remove the updates directory for Windows 7 and above
+ ${CleanUpdateDirectories} "Mozilla\SeaMonkey" "Mozilla\updates"
+
+ ${RemoveDeprecatedFiles}
+
+ ${InstallStartCleanupCommon}
+SectionEnd
+
+Section "-Application" APP_IDX
+ ${StartUninstallLog}
+
+ SetDetailsPrint both
+ DetailPrint $(STATUS_INSTALL_APP)
+ SetDetailsPrint none
+
+ ${LogHeader} "Installing Main Files"
+ ${CopyFilesFromDir} "$EXEDIR\core" "$INSTDIR" \
+ "$(ERROR_CREATE_DIRECTORY_PREFIX)" \
+ "$(ERROR_CREATE_DIRECTORY_SUFFIX)"
+
+ ; The MAPI DLL's are copied and the copies are then registered to lessen
+ ; file in use errors on application update.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\MapiProxy_InUse.dll"
+ ${If} ${Errors}
+ ; Clear the way for the new file and delete the old file on reboot
+ Rename "$INSTDIR\MapiProxy_InUse.dll" "$INSTDIR\MapiProxy_InUse.dll.moz-delete"
+ Delete /REBOOTOK "$INSTDIR\MapiProxy_InUse.dll.moz-delete"
+ ${EndIf}
+ CopyFiles /SILENT "$EXEDIR\core\MapiProxy.dll" "$INSTDIR\MapiProxy_InUse.dll"
+ ${LogMsg} "Installed File: $INSTDIR\MapiProxy_InUse.dll"
+ ${LogUninstall} "File: \MapiProxy_InUse.dll"
+
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\mozMapi32_InUse.dll"
+ ${If} ${Errors}
+ ; Clear the way for the new file and delete the old file on reboot
+ Rename "$INSTDIR\mozMapi32_InUse.dll" "$INSTDIR\mozMapi32_InUse.dll.moz-delete"
+ Delete /REBOOTOK "$INSTDIR\mozMapi32_InUse.dll.moz-delete"
+ ${EndIf}
+ CopyFiles /SILENT "$EXEDIR\core\mozMapi32.dll" "$INSTDIR\mozMapi32_InUse.dll"
+ ${LogMsg} "Installed File: $INSTDIR\mozMapi32_InUse.dll"
+ ${LogUninstall} "File: \mozMapi32_InUse.dll"
+
+ ; Register DLLs
+ ; XXXrstrong - AccessibleMarshal.dll can be used by multiple applications but
+ ; is only registered for the last application installed. When the last
+ ; application installed is uninstalled AccessibleMarshal.dll will no longer be
+ ; registered. bug 338878
+ ${LogHeader} "DLL Registration"
+
+ ClearErrors
+
+ ${RegisterDLL} "$INSTDIR\AccessibleMarshal.dll"
+ ${If} ${Errors}
+ ${LogMsg} "** ERROR Registering: $INSTDIR\AccessibleMarshal.dll **"
+ ${Else}
+ ${LogUninstall} "DLLReg: \AccessibleMarshal.dll"
+ ${LogMsg} "Registered: $INSTDIR\AccessibleMarshal.dll"
+ ${EndIf}
+
+ ClearErrors
+
+ ${RegisterDLL} "$INSTDIR\AccessibleHandler.dll"
+ ${If} ${Errors}
+ ${LogMsg} "** ERROR Registering: $INSTDIR\AccessibleHandler.dll **"
+ ${Else}
+ ${LogUninstall} "DLLReg: \AccessibleHandler.dll"
+ ${LogMsg} "Registered: $INSTDIR\AccessibleHandler.dll"
+ ${EndIf}
+
+ ; Write extra files created by the application to the uninstall log so they
+ ; will be removed when the application is uninstalled. To remove an empty
+ ; directory write a bogus filename to the deepest directory and all empty
+ ; parent directories will be removed.
+ ${LogUninstall} "File: \components\compreg.dat"
+ ${LogUninstall} "File: \components\xpti.dat"
+ ${LogUninstall} "File: \active-update.xml"
+ ${LogUninstall} "File: \install.log"
+ ${LogUninstall} "File: \install_status.log"
+ ${LogUninstall} "File: \install_wizard.log"
+ ${LogUninstall} "File: \updates.xml"
+
+ ; Default for creating Start Menu folder and shortcuts
+ ; (1 = create, 0 = don't create)
+ ${If} $AddStartMenuSC == ""
+ StrCpy $AddStartMenuSC "1"
+ ${EndIf}
+
+; Default for creating Task Bar shortcuts
+ ; (1 = create, 0 = don't create)
+ ${If} $AddTaskbarSC == ""
+ StrCpy $AddTaskbarSC "1"
+ ${EndIf}
+
+ ; Default for creating Quick Launch shortcut (1 = create, 0 = don't create)
+ ${If} $AddQuickLaunchSC == ""
+ StrCpy $AddQuickLaunchSC "1"
+ ${EndIf}
+
+ ; Default for creating Desktop shortcut (1 = create, 0 = don't create)
+ ${If} $AddDesktopSC == ""
+ StrCpy $AddDesktopSC "1"
+ ${EndIf}
+
+ ${LogHeader} "Adding Registry Entries"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU" ; used primarily for logging
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to HKLM
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $TmpVal "HKLM" ; used primarily for logging
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+ ${EndIf}
+
+ ; The previous installer adds several registry values to both HKLM and HKCU.
+ ; We now try to add to HKLM and if that fails to HKCU
+
+ ; The order that reg keys and values are added is important if you use the
+ ; uninstall log to remove them on uninstall. When using the uninstall log you
+ ; MUST add children first so they will be removed first on uninstall so they
+ ; will be empty when the key is deleted. This allows the uninstaller to
+ ; specify that only empty keys will be deleted.
+ ${SetAppKeys}
+
+ ${FixClassKeys}
+
+ StrCpy $1 "$\"$8$\" -requestPending -osint -url $\"%1$\""
+ StrCpy $2 "$\"%1$\",,0,0,,,,"
+ StrCpy $3 "$\"$8$\" -url $\"%1$\""
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+
+ ; An empty string is used for the 5th param because SeaMonkeyHTML is not a
+ ; protocol handler
+ ${AddHandlerValues} "SOFTWARE\Classes\SeaMonkeyHTML" "$3" \
+ "$INSTDIR\chrome\icons\default\html-file.ico,0" \
+ "${AppRegName} Document" "" ""
+ ${AddDisabledDDEHandlerValues} "SeaMonkeyURL" "$1" "$8,0" \
+ "${AppRegName} URL" ""
+
+ ${FixShellIconHandler}
+
+ ; The following keys should only be set if we can write to HKLM
+ ${If} $TmpVal == "HKLM"
+ ; Uninstall keys can only exist under HKLM on some versions of windows.
+ ${SetUninstallKeys}
+
+ ; Set the Start Menu Internet and Windows 7 Registered App HKLM registry keys.
+ ${SetStartMenuInternet}
+ ${SetClientsMail}
+
+ ; If we are writing to HKLM and create the quick launch and the desktop
+ ; shortcuts set IconsVisible to 1 otherwise to 0.
+ ; Taskbar shortcuts imply having a start menu shortcut.
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ ${If} $AddQuickLaunchSC == 1
+ ${OrIf} $AddDesktopSC == 1
+ ${OrIf} $AddTaskbarSC == 1
+ StrCpy $0 "Software\Clients\StartMenuInternet\$R9\InstallInfo"
+ WriteRegDWORD HKLM "$0" "IconsVisible" 1
+ StrCpy $0 "Software\Clients\Mail\${BrandFullNameInternal}\InstallInfo"
+ WriteRegDWORD HKLM "$0" "IconsVisible" 1
+ ${Else}
+ StrCpy $0 "Software\Clients\StartMenuInternet\$R9\InstallInfo"
+ WriteRegDWORD HKLM "$0" "IconsVisible" 0
+ StrCpy $0 "Software\Clients\Mail\${BrandFullNameInternal}\InstallInfo"
+ WriteRegDWORD HKLM "$0" "IconsVisible" 0
+ ${EndIf}
+ ${EndIf}
+
+ ; These need special handling on uninstall since they may be overwritten by
+ ; an install into a different location.
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
+ ${WriteRegStr2} $TmpVal "$0" "" "$INSTDIR\${FileMainEXE}" 0
+ ${WriteRegStr2} $TmpVal "$0" "Path" "$INSTDIR" 0
+
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\$R9"
+ ${CreateRegKey} "$TmpVal" "$0" 0
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
+ ${CreateRegKey} "$TmpVal" "$0" 0
+
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+
+ ; Create shortcuts
+ ${LogHeader} "Adding Shortcuts"
+
+ ; Always add the relative path to the application's Start Menu directory and
+ ; the application's shortcuts to the shortcuts log ini file. The
+ ; DeleteShortcuts macro will do the right thing on uninstall if they don't
+ ; exist.
+ ${LogSMProgramsDirRelPath} "$StartMenuDir"
+ ${LogSMProgramsShortcut} "${BrandFullName}.lnk"
+ ${LogSMProgramsShortcut} "${BrandFullName} ($(SAFE_MODE)).lnk"
+ ${LogSMProgramsShortcut} "${BrandFullNameInternal} $(MAILNEWS_TEXT).lnk"
+ ${LogSMProgramsShortcut} "$(PROFILE_TEXT).lnk"
+ ${LogQuickLaunchShortcut} "${BrandFullName}.lnk"
+ ${LogDesktopShortcut} "${BrandFullName}.lnk"
+
+ ${If} $AddStartMenuSC == 1
+ ${Unless} ${FileExists} "$SMPROGRAMS\$StartMenuDir"
+ CreateDirectory "$SMPROGRAMS\$StartMenuDir"
+ ${LogMsg} "Added Start Menu Directory: $SMPROGRAMS\$StartMenuDir"
+ ${EndUnless}
+ CreateShortCut "$SMPROGRAMS\$StartMenuDir\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$SMPROGRAMS\$StartMenuDir\${BrandFullName}.lnk" "$AppUserModelID"
+ ${EndIf}
+ ${LogMsg} "Added Shortcut: $SMPROGRAMS\$StartMenuDir\${BrandFullName}.lnk"
+ CreateShortCut "$SMPROGRAMS\$StartMenuDir\${BrandFullName} ($(SAFE_MODE)).lnk" "$INSTDIR\${FileMainEXE}" "-safe-mode" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$SMPROGRAMS\$StartMenuDir\${BrandFullName} ($(SAFE_MODE)).lnk" "$AppUserModelID"
+ ${EndIf}
+ ${LogMsg} "Added Shortcut: $SMPROGRAMS\$StartMenuDir\${BrandFullName} ($(SAFE_MODE)).lnk"
+ CreateShortCut "$SMPROGRAMS\$StartMenuDir\${BrandFullName} $(MAILNEWS_TEXT).lnk" "$INSTDIR\${FileMainEXE}" "-mail" "$INSTDIR\chrome\icons\default\messengerWindow.ico" 0
+ ${LogMsg} "Added Shortcut: $SMPROGRAMS\$StartMenuDir\${BrandFullName} $(MAILNEWS_TEXT).lnk"
+ CreateShortCut "$SMPROGRAMS\$StartMenuDir\$(PROFILE_TEXT).lnk" "$INSTDIR\${FileMainEXE}" "-profileManager" "$INSTDIR\${FileMainEXE}" 0
+ ${LogMsg} "Added Shortcut: $SMPROGRAMS\$StartMenuDir\$(PROFILE_TEXT).lnk"
+ ${EndIf}
+
+ ${If} $AddQuickLaunchSC == 1
+ CreateShortCut "$QUICKLAUNCH\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$QUICKLAUNCH\${BrandFullName}.lnk" "$AppUserModelID"
+ ${EndIf}
+ ${LogMsg} "Added Shortcut: $QUICKLAUNCH\${BrandFullName}.lnk"
+ ${EndIf}
+
+ ${If} $AddDesktopSC == 1
+ CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"
+ ${EndIf}
+ ${LogMsg} "Added Shortcut: $DESKTOP\${BrandFullName}.lnk"
+ ${EndIf}
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+SectionEnd
+
+Section /o "Debug and QA Tools" DEBUG_IDX
+ ${If} ${FileExists} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi"
+ SetDetailsPrint both
+ DetailPrint $(STATUS_INSTALL_OPTIONAL)
+ SetDetailsPrint none
+
+ ${RemoveDir} "$INSTDIR\extensions\debugQA@mozilla.org"
+ ${DeleteFile} "$INSTDIR\extensions\debugQA@mozilla.org.xpi"
+ ${DeleteFile} "$INSTDIR\distribution\extensions\debugQA@mozilla.org.xpi"
+ ClearErrors
+ ${LogHeader} "Installing Debug and QA Tools"
+ CopyFiles /SILENT "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi" \
+ "$INSTDIR\extensions\"
+ ${EndIf}
+SectionEnd
+
+; Cleanup operations to perform at the end of the installation.
+Section "-InstallEndCleanup"
+ SetDetailsPrint both
+ DetailPrint "$(STATUS_CLEANUP)"
+ SetDetailsPrint none
+
+ ; Refresh desktop icons
+ System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)"
+
+ ${InstallEndCleanupCommon}
+
+ ; If we have to reboot give SHChangeNotify time to finish the refreshing
+ ; the icons so the OS doesn't display the icons from helper.exe
+ ${If} ${RebootFlag}
+ Sleep 10000
+ ${LogHeader} "Reboot Required To Finish Installation"
+ ; ${FileMainEXE}.moz-upgrade should never exist but just in case...
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
+ Rename "$INSTDIR\${FileMainEXE}" "$INSTDIR\${FileMainEXE}.moz-upgrade"
+ ${EndUnless}
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ Rename "$INSTDIR\${FileMainEXE}" "$INSTDIR\${FileMainEXE}.moz-delete"
+ ${Unless} ${Errors}
+ Delete /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-delete"
+ ${EndUnless}
+ ${EndUnless}
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ CopyFiles /SILENT "$INSTDIR\uninstall\helper.exe" "$INSTDIR"
+ FileOpen $0 "$INSTDIR\${FileMainEXE}" w
+ FileWrite $0 "Will be deleted on restart"
+ Rename /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
+ FileClose $0
+ Delete "$INSTDIR\${FileMainEXE}"
+ Rename "$INSTDIR\helper.exe" "$INSTDIR\${FileMainEXE}"
+ ${EndUnless}
+ ${EndIf}
+SectionEnd
+
+Function CheckExistingInstall
+ ; If there is a pending file copy from a previous uninstall don't allow
+ ; installing until after the system has rebooted.
+ IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4
+ MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
+ Reboot
+ Quit
+
+ ; If there is a pending file deletion from a previous uninstall don't allow
+ ; installing until after the system has rebooted.
+ IfFileExists "$INSTDIR\${FileMainEXE}.moz-delete" +1 +4
+ MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UNINSTALL)" IDNO +2
+ Reboot
+ Quit
+
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)"
+ ${If} "$TmpVal" == "FoundMessageWindow"
+ Sleep 5000
+ ${EndIf}
+ ${PushFilesToCheck}
+ ; Store the return value in $TmpVal so it is less likely to be accidentally
+ ; overwritten elsewhere.
+ ${CheckForFilesInUse} $TmpVal
+
+ Banner::destroy
+
+ ${If} "$TmpVal" == "true"
+ StrCpy $TmpVal "FoundMessageWindow"
+ ${ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)"
+ StrCpy $TmpVal "true"
+ ${EndIf}
+ ${EndIf}
+FunctionEnd
+
+Function LaunchApp
+ GetFunctionAddress $0 LaunchAppFromElevatedProcess
+ UAC::ExecCodeSegment $0
+FunctionEnd
+
+Function LaunchAppFromElevatedProcess
+ ${ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_LAUNCH)"
+ ; Find the installation directory when launching using GetFunctionAddress
+ ; from an elevated installer since $INSTDIR will not be set in this installer
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $1
+ ; Set our current working directory to the application's install directory
+ ; otherwise the 7-Zip temp directory will be in use and won't be deleted.
+ SetOutPath "$1"
+ Exec "$\"$0$\""
+FunctionEnd
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale
+; using " " for BrandingText will hide the "Nullsoft Install System..." branding
+BrandingText " "
+
+################################################################################
+# Page pre and leave functions
+
+Function preWelcome
+ ${If} ${FileExists} "$EXEDIR\core\distribution\modern-wizard.bmp"
+ Delete "$PLUGINSDIR\modern-wizard.bmp"
+ CopyFiles /SILENT "$EXEDIR\core\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
+ ${EndIf}
+FunctionEnd
+
+Function showLicense
+ ${If} ${FileExists} "$EXEDIR\core\distribution\modern-header.bmp"
+ ${AndIf} $hHeaderBitmap == ""
+ Delete "$PLUGINSDIR\modern-header.bmp"
+ CopyFiles /SILENT "$EXEDIR\core\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
+ ${ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
+ ${EndIf}
+FunctionEnd
+
+Function preOptions
+ !insertmacro MUI_HEADER_TEXT "$(OPTIONS_PAGE_TITLE)" "$(OPTIONS_PAGE_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "options.ini"
+FunctionEnd
+
+Function leaveOptions
+ ${MUI_INSTALLOPTIONS_READ} $0 "options.ini" "Settings" "State"
+ ${If} $0 != 0
+ Abort
+ ${EndIf}
+ ${MUI_INSTALLOPTIONS_READ} $R0 "options.ini" "Field 2" "State"
+ StrCmp $R0 "1" +1 +2
+ StrCpy $InstallType ${INSTALLTYPE_BASIC}
+ ${MUI_INSTALLOPTIONS_READ} $R0 "options.ini" "Field 3" "State"
+ StrCmp $R0 "1" +1 +2
+ StrCpy $InstallType ${INSTALLTYPE_CUSTOM}
+
+ ${LeaveOptionsCommon}
+
+ ${If} $InstallType == ${INSTALLTYPE_BASIC}
+ Call CheckExistingInstall
+ ${EndIf}
+FunctionEnd
+
+Function preComponents
+ ${CheckCustomCommon}
+ !insertmacro checkSuiteComponents
+ !insertmacro MUI_HEADER_TEXT "$(OPTIONAL_COMPONENTS_TITLE)" "$(OPTIONAL_COMPONENTS_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "components.ini"
+FunctionEnd
+
+Function leaveComponents
+ ; If debugQA exists then it will be Field 2.
+ StrCpy $R1 2
+
+ ${If} ${FileExists} "$EXEDIR\optional\extensions\debugQA@mozilla.org.xpi"
+ ${MUI_INSTALLOPTIONS_READ} $R0 "components.ini" "Field $R1" "State"
+ ; State will be 1 for checked and 0 for unchecked so we can use that to set
+ ; the section flags for installation.
+ SectionSetFlags ${DEBUG_IDX} $R0
+ IntOp $R1 $R1 + 1
+ ${Else}
+ SectionSetFlags ${DEBUG_IDX} 0 ; Disable install for debugQA
+ ${EndIf}
+
+FunctionEnd
+
+Function preDirectory
+ ${PreDirectoryCommon}
+FunctionEnd
+
+Function leaveDirectory
+ ${If} $InstallType == ${INSTALLTYPE_BASIC}
+ Call CheckExistingInstall
+ ${EndIf}
+ ${LeaveDirectoryCommon} "$(WARN_DISK_SPACE)" "$(WARN_WRITE_ACCESS)"
+FunctionEnd
+
+Function preShortcuts
+ ${CheckCustomCommon}
+ !insertmacro MUI_HEADER_TEXT "$(SHORTCUTS_PAGE_TITLE)" "$(SHORTCUTS_PAGE_SUBTITLE)"
+ !insertmacro MUI_INSTALLOPTIONS_DISPLAY "shortcuts.ini"
+FunctionEnd
+
+Function leaveShortcuts
+ ${MUI_INSTALLOPTIONS_READ} $0 "shortcuts.ini" "Settings" "State"
+ ${If} $0 != 0
+ Abort
+ ${EndIf}
+ ${MUI_INSTALLOPTIONS_READ} $AddDesktopSC "shortcuts.ini" "Field 2" "State"
+ ${MUI_INSTALLOPTIONS_READ} $AddStartMenuSC "shortcuts.ini" "Field 3" "State"
+ ${MUI_INSTALLOPTIONS_READ} $AddQuickLaunchSC "shortcuts.ini" "Field 4" "State"
+
+ ; If Start Menu shortcuts won't be created call CheckExistingInstall here
+ ; since leaveStartMenu will not be called.
+ ${If} $AddStartMenuSC != 1
+ ${AndIf} $InstallType == ${INSTALLTYPE_CUSTOM}
+ Call CheckExistingInstall
+ ${EndIf}
+FunctionEnd
+
+Function preStartMenu
+ ; With the Unicode installer the path to the application's Start Menu
+ ; directory relative to the Start Menu's Programs directory is written to the
+ ; shortcuts log ini file and is used to set the default Start Menu directory.
+ ${GetSMProgramsDirRelPath} $0
+ ${If} "$0" != ""
+ StrCpy $StartMenuDir "$0"
+ ${Else}
+ ; Prior to the Unicode installer the path to the application's Start Menu
+ ; directory relative to the Start Menu's Programs directory was written to
+ ; the registry and use this value to set the default Start Menu directory.
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\Mozilla\${BrandFullNameInternal}\${AppVersion} (${AB_CD})\Main" "Start Menu Folder"
+ ${If} ${Errors}
+ ; Use the FindSMProgramsDir macro to find a previously used path to the
+ ; application's Start Menu directory relative to the Start Menu's Programs
+ ; directory in the uninstall log and use this value to set the default
+ ; Start Menu directory.
+ ${FindSMProgramsDir} $0
+ ${If} "$0" != ""
+ StrCpy $StartMenuDir "$0"
+ ${EndIf}
+ ${Else}
+ StrCpy $StartMenuDir "$0"
+ ${EndUnless}
+ ${EndIf}
+
+ ${CheckCustomCommon}
+ ${If} $AddStartMenuSC != 1
+ Abort
+ ${EndIf}
+FunctionEnd
+
+Function leaveStartMenu
+ ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
+ Call CheckExistingInstall
+ ${EndIf}
+FunctionEnd
+
+Function preSummary
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "3"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Text "$(SUMMARY_INSTALLED_TO)"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Type "text"
+ ; The contents of this control must be set as follows in the pre function
+ ; ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND"
+ ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" state ""
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Top "17"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" flags "READONLY"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Type "label"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Text "$(SUMMARY_CLICK)"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Top "130"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Bottom "150"
+
+ ${If} "$TmpVal" == "true"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Type "label"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Text "$(SUMMARY_REBOOT_REQUIRED_INSTALL)"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Left "0"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Top "35"
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Bottom "45"
+
+ WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "4"
+ ${EndIf}
+
+ !insertmacro MUI_HEADER_TEXT "$(SUMMARY_PAGE_TITLE)" "$(SUMMARY_PAGE_SUBTITLE)"
+
+ ; The Summary custom page has a textbox that will automatically receive
+ ; focus. This sets the focus to the Install button instead.
+ !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "summary.ini"
+ GetDlgItem $0 $HWNDPARENT 1
+ System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i"
+ ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND"
+ SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ !insertmacro MUI_INSTALLOPTIONS_SHOW
+FunctionEnd
+
+Function leaveSummary
+ ; Try to delete the app executable and if we can't delete it try to find the
+ ; app's message window and prompt the user to close the app. This allows
+ ; running an instance that is located in another directory. If for whatever
+ ; reason there is no message window we will just rename the app's files and
+ ; then remove them on restart.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ${ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)"
+ ${EndIf}
+FunctionEnd
+
+; When we add an optional action to the finish page the cancel button is
+; enabled. This disables it and leaves the finish button as the only choice.
+Function preFinish
+ ${EndInstallLog} "${BrandFullName}"
+ !insertmacro MUI_INSTALLOPTIONS_WRITE "ioSpecial.ini" "settings" "cancelenabled" "0"
+FunctionEnd
+
+################################################################################
+# Initialization Functions
+
+Function .onInit
+ StrCpy $LANGUAGE 0
+ ${SetBrandNameVars} "$EXEDIR\core\distribution\setup.ini"
+
+ ; Don't install on systems that don't support SSE2. The parameter value of
+ ; 10 is for PF_XMMI64_INSTRUCTIONS_AVAILABLE which will check whether the
+ ; SSE2 instruction set is available. Result returned in $R7.
+ System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R7"
+
+ ; Windows NT 6.0 (Vista/Server 2008) and lower are not supported.
+ ${Unless} ${AtLeastWin7}
+ ${If} "$R7" == "0"
+ strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
+ ${Else}
+ strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_MSG)"
+ ${EndIf}
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$R7" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndUnless}
+
+ ; SSE2 CPU support
+ ${If} "$R7" == "0"
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndIf}
+
+!ifdef HAVE_64BIT_BUILD
+ ${Unless} ${RunningX64}
+ MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_OSVER_MSG)" IDCANCEL +2
+ ExecShell "open" "${URLSystemRequirements}"
+ Quit
+ ${EndUnless}
+ SetRegView 64
+!endif
+
+ ${InstallOnInitCommon} "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
+
+
+ !insertmacro InitInstallOptionsFile "options.ini"
+ !insertmacro InitInstallOptionsFile "components.ini"
+ !insertmacro InitInstallOptionsFile "shortcuts.ini"
+ !insertmacro InitInstallOptionsFile "summary.ini"
+
+ ; Setup the options.ini file for the Custom Options Page
+ WriteINIStr "$PLUGINSDIR\options.ini" "Settings" NumFields "5"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Text "$(OPTIONS_SUMMARY)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Top "0"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 1" Bottom "10"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Type "RadioButton"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Text "$(OPTION_STANDARD_RADIO)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Left "15"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Top "25"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Bottom "35"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" State "1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 2" Flags "GROUP"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Type "RadioButton"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Text "$(OPTION_CUSTOM_RADIO)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Left "15"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Top "55"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" Bottom "65"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 3" State "0"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Type "label"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Text "$(OPTION_STANDARD_DESC)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Left "30"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Top "37"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 4" Bottom "57"
+
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Type "label"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Text "$(OPTION_CUSTOM_DESC)"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Left "30"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Right "-1"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Top "67"
+ WriteINIStr "$PLUGINSDIR\options.ini" "Field 5" Bottom "87"
+
+ ; Setup the components.ini file for the Components page
+ !insertmacro createSuiteComponentsINI
+
+ ; Setup the shortcuts.ini file for the Custom Shortcuts Page
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Settings" NumFields "4"
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Text "$(CREATE_ICONS_DESC)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Text "$(ICONS_DESKTOP)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Left "15"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Top "20"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" State "1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 2" Flags "GROUP"
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Text "$(ICONS_STARTMENU)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Left "15"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Top "40"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" Bottom "50"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 3" State "1"
+
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Type "checkbox"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Text "$(ICONS_QUICKLAUNCH)"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Left "15"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Top "60"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" Bottom "70"
+ WriteINIStr "$PLUGINSDIR\shortcuts.ini" "Field 4" State "1"
+
+ ; There must always be a core directory
+ ${GetSize} "$EXEDIR\core\" "/S=0K" $R5 $R7 $R8
+ ; Add 1024 Kb to the diskspace requirement since the installer makes a copy
+ ; of the MAPI dll's (around 20 Kb)... also, see Bug 434338.
+ IntOp $R5 $R5 + 1024
+ SectionSetSize ${APP_IDX} $R5
+
+ ; Initialize $hHeaderBitmap to prevent redundant changing of the bitmap if
+ ; the user clicks the back button
+ StrCpy $hHeaderBitmap ""
+FunctionEnd
+
+Function .onGUIEnd
+ ${OnEndCommon}
+FunctionEnd
diff --git a/comm/suite/installer/windows/nsis/shared.nsh b/comm/suite/installer/windows/nsis/shared.nsh
new file mode 100644
index 0000000000..5830eb8411
--- /dev/null
+++ b/comm/suite/installer/windows/nsis/shared.nsh
@@ -0,0 +1,1069 @@
+# 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/.
+
+!macro PostUpdate
+ ${CreateShortcutsLog}
+
+ ; Remove registry entries for non-existent apps and for apps that point to our
+ ; install location in the Software\Mozilla key and uninstall registry entries
+ ; that point to our install location for both HKCU and HKLM.
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${UpdateProtocolHandlers}
+
+ ; Win7 taskbar and start menu link maintenance
+ ${If} "$AppUserModelID" != ""
+ ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "$AppUserModelID" $0
+ ${EndIf}
+
+ ; Upgrade the copies of the MAPI DLLs
+ ${UpgradeMapiDLLs}
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU" ; used primarily for logging
+ ${Else}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ StrCpy $TmpVal "HKLM" ; used primarily for logging
+ ${RegCleanMain} "Software\Mozilla"
+ ${RegCleanUninstall}
+ ${SetStartMenuInternet}
+ ${FixShellIconHandler}
+ ${SetUninstallKeys}
+ ${UpdateProtocolHandlers}
+
+ ReadRegStr $0 HKLM "Software\mozilla.org\Mozilla" "CurrentVersion"
+ ${If} "$0" != "${GREVersion}"
+ WriteRegStr HKLM "Software\mozilla.org\Mozilla" "CurrentVersion" "${GREVersion}"
+ ${EndIf}
+
+ ; Only update the Clients\Mail registry key values if they don't exist or
+ ; this installation is the same as the one set in those keys.
+ ReadRegStr $0 HKLM "Software\Clients\Mail\${BrandFullNameInternal}\DefaultIcon" ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $0
+ ${If} ${FileExists} "$0"
+ ${GetLongPath} "$0" $0
+ ${EndIf}
+ ${If} "$0" == "$INSTDIR"
+ ${SetClientsMail}
+ ${EndIf}
+
+ ; Only update the Clients\News registry key values if they don't exist or
+ ; this installation is the same as the one set in those keys.
+ ReadRegStr $0 HKLM "Software\Clients\News\${BrandFullNameInternal}\DefaultIcon" ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $0
+ ${If} ${FileExists} "$0"
+ ${GetLongPath} "$0" $0
+ ${EndIf}
+ ${If} "$0" == "$INSTDIR"
+ ${SetClientsNews}
+ ${EndIf}
+ ${EndIf}
+
+ ${RemoveDeprecatedKeys}
+ ; Add Software\Mozilla\ registry entries
+ ${SetAppKeys}
+
+ ${FixClassKeys}
+
+ ; Remove files that may be left behind by the application in the
+ ; VirtualStore directory.
+ ${CleanVirtualStore}
+
+ ${RemoveDeprecatedFiles}
+
+ ; Register AccessibleHandler.dll with COM (this writes to HKLM)
+ ${RegisterAccessibleHandler}
+!macroend
+!define PostUpdate "!insertmacro PostUpdate"
+
+!macro SetAsDefaultAppUser
+ SetShellVarContext current
+
+ ; It is only possible to set this installation of the application as the
+ ; handler for the various types if those types were added to the respective
+ ; HKLM\Clients registry keys.
+ ; http://support.microsoft.com/kb/297878
+ ${GetParameters} $R0
+
+ ClearErrors
+ ${GetOptions} "$R0" "Browser" $R1
+ ${Unless} ${Errors}
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $0
+ ${If} ${FileExists} "$0"
+ ${GetLongPath} "$0" $0
+ ${EndIf}
+ ${If} "$0" != "$INSTDIR"
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ ; Prevent multiple elevation requests
+ ClearErrors
+ ${GetOptions} "$R0" "/UAC:" $R1
+ ${Unless} ${Errors}
+ Quit
+ ${EndUnless}
+ ${ElevateUAC}
+ ${EndIf}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ ${SetStartMenuInternet}
+ ${EndIf}
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ${FixShellIconHandler}
+ WriteRegStr HKCU "Software\Clients\StartMenuInternet" "" "$R9"
+
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\RegisteredApplications" "${AppRegName}"
+ ; Only register as the handler on 7 if the app registry name exists
+ ; under the RegisteredApplications registry key.
+ ${Unless} ${Errors}
+ AppAssocReg::SetAppAsDefaultAll "${AppRegName}"
+ ${EndUnless}
+
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${SetHandlersBrowser}
+ ${EndUnless}
+
+ ClearErrors
+ ${GetOptions} "$R0" "Mail" $R1
+ ${Unless} ${Errors}
+ ReadRegStr $0 HKLM "Software\Clients\Mail\${BrandFullNameInternal}\DefaultIcon" ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $0
+ ${If} ${FileExists} "$0"
+ ${GetLongPath} "$0" $0
+ ${EndIf}
+ ${If} "$0" != "$INSTDIR"
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ ; Prevent multiple elevation requests
+ ClearErrors
+ ${GetOptions} "$R0" "/UAC:" $R1
+ ${Unless} ${Errors}
+ Quit
+ ${EndUnless}
+ ${ElevateUAC}
+ ${EndIf}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ${SetClientsMail}
+ ${EndIf}
+ WriteRegStr HKCU "Software\Clients\Mail" "" "${BrandFullNameInternal}"
+ GetFunctionAddress $0 SetAsDefaultMailAppUser
+ UAC::ExecCodeSegment $0
+ ${EndUnless}
+
+ ClearErrors
+ ${GetOptions} "$R0" "News" $R1
+ ${Unless} ${Errors}
+ ReadRegStr $0 HKLM "Software\Clients\News\${BrandFullNameInternal}\DefaultIcon" ""
+ ${GetPathFromString} "$0" $0
+ ${GetParent} "$0" $0
+ ${If} ${FileExists} "$0"
+ ${GetLongPath} "$0" $0
+ ${EndIf}
+ ${If} "$0" != "$INSTDIR"
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ ; Prevent multiple elevation requests
+ ClearErrors
+ ${GetOptions} "$R0" "/UAC:" $R1
+ ${Unless} ${Errors}
+ Quit
+ ${EndUnless}
+ ${ElevateUAC}
+ ${EndIf}
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ${SetClientsNews}
+ ${EndIf}
+ WriteRegStr HKCU "Software\Clients\News" "" "${BrandFullNameInternal}"
+ GetFunctionAddress $0 SetAsDefaultNewsAppUser
+ UAC::ExecCodeSegment $0
+ ${EndUnless}
+!macroend
+!define SetAsDefaultAppUser "!insertmacro SetAsDefaultAppUser"
+
+!macro SetAsDefaultAppGlobal
+ ${RemoveDeprecatedKeys}
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ; Make sure that the MapiProxy and the mozMapi32 dll copies exist as we will
+ ; use those to register as default mail app. When using a ZIP build, the DLL
+ ; copies might not exist yet
+ IfFileExists "$INSTDIR\MapiProxy_InUse.dll" +2 +1
+ CopyFiles /SILENT "$INSTDIR\MapiProxy.dll" "$INSTDIR\MapiProxy_InUse.dll"
+ IfFileExists "$INSTDIR\mozMapi32_InUse.dll" +2 +1
+ CopyFiles /SILENT "$INSTDIR\mozMapi32.dll" "$INSTDIR\mozMapi32_InUse.dll"
+
+ ${SetHandlersBrowser}
+ ${SetHandlersMail}
+ ${SetHandlersNews}
+ ${SetStartMenuInternet}
+ ${SetClientsMail}
+ ${SetClientsNews}
+ ${FixShellIconHandler}
+ ${ShowShortcuts}
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ WriteRegStr HKLM "Software\Clients\StartMenuInternet" "" "$R9"
+ WriteRegStr HKLM "Software\Clients\Mail" "" "${BrandFullNameInternal}"
+!macroend
+!define SetAsDefaultAppGlobal "!insertmacro SetAsDefaultAppGlobal"
+
+!macro HideShortcuts
+ StrCpy $R1 "Software\Clients\Mail\${BrandFullNameInternal}\InstallInfo"
+ WriteRegDWORD HKLM $R1 "IconsVisible" 0
+ SetShellVarContext all ; Set $DESKTOP to All Users
+ ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+ SetShellVarContext current ; Set $DESKTOP to the current user's desktop
+ ${EndUnless}
+
+ ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+ ShellLink::GetShortCutArgs "$DESKTOP\${BrandFullName}.lnk"
+ Pop $0
+ ${If} "$0" == ""
+ ShellLink::GetShortCutTarget "$DESKTOP\${BrandFullName}.lnk"
+ Pop $0
+ ; Needs to handle short paths
+ ${If} "$0" == "$INSTDIR\${FileMainEXE}"
+ Delete "$DESKTOP\${BrandFullName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+
+ ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
+ ShellLink::GetShortCutArgs "$QUICKLAUNCH\${BrandFullName}.lnk"
+ Pop $0
+ ${If} "$0" == ""
+ ShellLink::GetShortCutTarget "$QUICKLAUNCH\${BrandFullName}.lnk"
+ Pop $0
+ ; Needs to handle short paths
+ ${If} "$0" == "$INSTDIR\${FileMainEXE}"
+ Delete "$QUICKLAUNCH\${BrandFullName}.lnk"
+ ${EndIf}
+ ${EndIf}
+ ${EndIf}
+!macroend
+!define HideShortcuts "!insertmacro HideShortcuts"
+
+!macro ShowShortcuts
+ StrCpy $R1 "Software\Clients\Mail\${BrandFullNameInternal}\InstallInfo"
+ WriteRegDWORD HKLM $R1 "IconsVisible" 1
+ SetShellVarContext all ; Set $DESKTOP to All Users
+ ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+ CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"
+ ${EndIf}
+ ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR"
+ ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+ SetShellVarContext current ; Set $DESKTOP to the current user's desktop
+ ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+ CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"
+ ${EndIf}
+ ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR"
+ ${EndUnless}
+ ${EndUnless}
+ ${EndUnless}
+ ${Unless} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
+ CreateShortCut "$QUICKLAUNCH\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\${FileMainEXE}" 0
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::Set "$QUICKLAUNCH\${BrandFullName}.lnk" "$AppUserModelID"
+ ${EndIf}
+ ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandFullName}.lnk" "$INSTDIR"
+ ${EndUnless}
+!macroend
+!define ShowShortcuts "!insertmacro ShowShortcuts"
+
+!macro SetHandlersBrowser
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+
+ StrCpy $0 "SOFTWARE\Classes"
+ StrCpy $1 "$\"$8$\" -requestPending -osint -url $\"%1$\""
+ StrCpy $2 "$\"$8$\" -url $\"%1$\""
+ StrCpy $3 "$\"%1$\",,0,0,,,,"
+
+ ; An empty string is used for the 5th param because SeaMonkeyHTML is not a
+ ; protocol handler
+ ${AddHandlerValues} "$0\SeaMonkeyHTML" "$2" \
+ "$INSTDIR\chrome\icons\default\html-file.ico,0" \
+ "${AppRegName} Document" "" ""
+ ${AddDisabledDDEHandlerValues} "SeaMonkeyURL" "$1" "$8,0" \
+ "${AppRegName} URL" "delete"
+
+ ; An empty string is used for the 4th & 5th params because the following
+ ; protocol handlers already have a display name and the additional keys
+ ; required for a protocol handler.
+ ${AddDisabledDDEHandlerValues} "ftp" "$1" "$8,0" "" ""
+ ${AddDisabledDDEHandlerValues} "http" "$1" "$8,0" "" ""
+ ${AddDisabledDDEHandlerValues} "https" "$1" "$8,0" "" ""
+
+ ReadRegStr $6 HKCR ".htm" ""
+ ${If} "$6" != "SeaMonkeyHTML"
+ WriteRegStr SHCTX "$0\.htm" "" "SeaMonkeyHTML"
+ ${EndIf}
+
+ ReadRegStr $6 HKCR ".html" ""
+ ${If} "$6" != "SeaMonkeyHTML"
+ WriteRegStr SHCTX "$0\.html" "" "SeaMonkeyHTML"
+ ${EndIf}
+
+ ReadRegStr $6 HKCR ".shtml" ""
+ ${If} "$6" != "SeaMonkeyHTML"
+ WriteRegStr SHCTX "$0\.shtml" "" "SeaMonkeyHTML"
+ ${EndIf}
+
+ ReadRegStr $6 HKCR ".xht" ""
+ ${If} "$6" != "SeaMonkeyHTML"
+ WriteRegStr SHCTX "$0\.xht" "" "SeaMonkeyHTML"
+ ${EndIf}
+
+ ReadRegStr $6 HKCR ".xhtml" ""
+ ${If} "$6" != "SeaMonkeyHTML"
+ WriteRegStr SHCTX "$0\.xhtml" "" "SeaMonkeyHTML"
+ ${EndIf}
+
+ ; Only add webm if it's not present
+ ${CheckIfRegistryKeyExists} "$0" ".webm" $7
+ ${If} $7 == "false"
+ WriteRegStr SHCTX "$0\.webm" "" "SeaMonkeyHTML"
+ ${EndIf}
+!macroend
+!define SetHandlersBrowser "!insertmacro SetHandlersBrowser"
+
+!macro SetHandlersMail
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+
+ StrCpy $0 "SOFTWARE\Classes"
+ StrCpy $1 "$\"$8$\" $\"%1$\""
+ StrCpy $2 "$\"$8$\" -osint -compose $\"%1$\""
+
+ ; An empty string is used for the 5th param because SeaMonkeyEML is not a
+ ; protocol handler
+ ${AddHandlerValues} "$0\SeaMonkeyEML" "$1" "$INSTDIR\chrome\icons\default\html-file.ico,0" "${AppRegNameMail} Document" "" ""
+
+ ${AddHandlerValues} "$0\SeaMonkeyCOMPOSE" "$2" "$8,0" "${AppRegNameMail} URL" "delete" ""
+
+ ; An empty string is used for the 4th & 5th params because the following
+ ; protocol handler already has a display name and additional keys required
+ ; for a protocol handler.
+ ${AddHandlerValues} "$0\mailto" "$2" "$8,0" "${AppRegNameMail} URL" "true" ""
+
+ ; Associate the file handlers with SeaMonkeyEML
+ ReadRegStr $6 HKCR ".eml" ""
+ ${If} "$6" != "SeaMonkeyEML"
+ WriteRegStr SHCTX "$0\.eml" "" "SeaMonkeyEML"
+ ${EndIf}
+!macroend
+!define SetHandlersMail "!insertmacro SetHandlersMail"
+
+!macro SetHandlersNews
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ StrCpy $0 "SOFTWARE\Classes"
+ StrCpy $1 "$\"$8$\" -osint -mail $\"%1$\""
+
+ ${AddHandlerValues} "$0\SeaMonkeyNEWS" "$1" "$8,0" "${AppRegNameNews} URL" "delete" ""
+ ; An empty string is used for the 4th & 5th params because the following
+ ; protocol handlers already have a display name and additional keys required
+ ; for a protocol handler.
+ ${AddHandlerValues} "$0\news" "$1" "$8,0" "${AppRegNameNews} URL" "true" ""
+ ${AddHandlerValues} "$0\nntp" "$1" "$8,0" "${AppRegNameNews} URL" "true" ""
+ ${AddHandlerValues} "$0\snews" "$1" "$8,0" "${AppRegNameNews} URL" "true" ""
+!macroend
+!define SetHandlersNews "!insertmacro SetHandlersNews"
+
+; XXXrstrong - there are several values that will be overwritten by and
+; overwrite other installs of the same application.
+!macro SetStartMenuInternet
+ GetFullPathName $8 "$INSTDIR\${FileMainEXE}"
+ GetFullPathName $7 "$INSTDIR\uninstall\helper.exe"
+
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+
+ StrCpy $0 "Software\Clients\StartMenuInternet\$R9"
+ WriteRegStr HKLM "$0" "" "${BrandFullName}"
+
+ WriteRegStr HKLM "$0\DefaultIcon" "" "$8,0"
+
+ ; The Reinstall Command is defined at
+ ; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_adv/registeringapps.asp
+ WriteRegStr HKLM "$0\InstallInfo" "HideIconsCommand" "$\"$7$\" /HideShortcuts"
+ WriteRegStr HKLM "$0\InstallInfo" "ShowIconsCommand" "$\"$7$\" /ShowShortcuts"
+ WriteRegStr HKLM "$0\InstallInfo" "ReinstallCommand" "$\"$7$\" /SetAsDefaultAppGlobal"
+
+ WriteRegStr HKLM "$0\shell\open\command" "" "$\"$8$\""
+
+ WriteRegStr HKLM "$0\shell\properties" "" "$(CONTEXT_OPTIONS)"
+ WriteRegStr HKLM "$0\shell\properties\command" "" "$\"$8$\" -preferences"
+
+ WriteRegStr HKLM "$0\shell\safemode" "" "$(CONTEXT_SAFE_MODE)"
+ WriteRegStr HKLM "$0\shell\safemode\command" "" "$\"$8$\" -safe-mode"
+
+ ; Vista Capabilities registry keys
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationDescription" "$(REG_APP_DESC)"
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationIcon" "$8,0"
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationName" "${BrandShortName}"
+
+ WriteRegStr HKLM "$0\Capabilities\FileAssociations" ".htm" "SeaMonkeyHTML"
+ WriteRegStr HKLM "$0\Capabilities\FileAssociations" ".html" "SeaMonkeyHTML"
+ WriteRegStr HKLM "$0\Capabilities\FileAssociations" ".shtml" "SeaMonkeyHTML"
+ WriteRegStr HKLM "$0\Capabilities\FileAssociations" ".xht" "SeaMonkeyHTML"
+ WriteRegStr HKLM "$0\Capabilities\FileAssociations" ".xhtml" "SeaMonkeyHTML"
+
+ WriteRegStr HKLM "$0\Capabilities\StartMenu" "StartMenuInternet" "$R9"
+
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "ftp" "SeaMonkeyURL"
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "http" "SeaMonkeyURL"
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "https" "SeaMonkeyURL"
+
+ ; Vista Registered Application
+ WriteRegStr HKLM "Software\RegisteredApplications" "${AppRegName}" "$0\Capabilities"
+!macroend
+!define SetStartMenuInternet "!insertmacro SetStartMenuInternet"
+
+!macro FixShellIconHandler
+ ; The IconHandler reference for SeaMonkeyHTML can end up in an inconsistent
+ ; state due to changes not being detected by the IconHandler for side by side
+ ; installs. The symptoms can be either an incorrect icon or no icon being
+ ; displayed for files associated with SeaMonkey. By setting it here it will
+ ; always reference the install referenced in the
+ ; HKLM\Software\Classes\SeaMonkeyHTML registry key.
+ ClearErrors
+ ReadRegStr $2 HKLM "Software\Classes\SeaMonkeyHTML\ShellEx\IconHandler" ""
+ ${Unless} ${Errors}
+ ClearErrors
+ ReadRegStr $3 HKLM "Software\Classes\CLSID\$2\Old Icon\SeaMonkeyHTML\DefaultIcon" ""
+ ${Unless} ${Errors}
+ WriteRegStr HKLM "Software\Classes\CLSID\$2\Old Icon\SeaMonkeyHTML\DefaultIcon" "" "$INSTDIR\chrome\icons\default\html-file.ico,0"
+ ${EndUnless}
+ ${EndUnless}
+!macroend
+!define FixShellIconHandler "!insertmacro FixShellIconHandler"
+
+; XXXrstrong - there are several values that will be overwritten by and
+; overwrite other installs of the same application.
+!macro SetClientsMail
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ ${GetLongPath} "$INSTDIR\uninstall\helper.exe" $7
+ ${GetLongPath} "$INSTDIR\mozMapi32_InUse.dll" $6
+
+ StrCpy $0 "Software\Clients\Mail\${BrandFullNameInternal}"
+ WriteRegStr HKLM "$0" "" "${BrandFullNameInternal}"
+ WriteRegStr HKLM "$0\DefaultIcon" "" "$8,0"
+ WriteRegStr HKLM "$0" "DLLPath" "$6"
+
+ ; The MapiProxy dll can be used by multiple applications but
+ ; is only registered for the last application installed. When the last
+ ; application installed is uninstalled MapiProxy.dll will no longer be
+ ; registered.
+ !ifndef NO_LOG
+ ${LogHeader} "DLL Registration"
+ !endif
+ ClearErrors
+ ${RegisterDLL} "$INSTDIR\MapiProxy_InUse.dll"
+ !ifndef NO_LOG
+ ${If} ${Errors}
+ ${LogMsg} "** ERROR Registering: $INSTDIR\MapiProxy_InUse.dll **"
+ ${Else}
+ ${LogUninstall} "DLLReg: \MapiProxy_InUse.dll"
+ ${LogMsg} "Registered: $INSTDIR\MapiProxy_InUse.dll"
+ ${EndIf}
+ !endif
+
+ StrCpy $1 "Software\Classes\CLSID\{29F458BE-8866-11D5-A3DD-00B0D0F3BAA7}"
+ WriteRegStr HKLM "$1\LocalServer32" "" "$\"$8$\" /MAPIStartup"
+ WriteRegStr HKLM "$1\ProgID" "" "MozillaMapi.1"
+ WriteRegStr HKLM "$1\VersionIndependentProgID" "" "MozillaMAPI"
+ StrCpy $1 "SOFTWARE\Classes"
+ WriteRegStr HKLM "$1\MozillaMapi" "" "Mozilla MAPI"
+ WriteRegStr HKLM "$1\MozillaMapi\CLSID" "" "{29F458BE-8866-11D5-A3DD-00B0D0F3BAA7}"
+ WriteRegStr HKLM "$1\MozillaMapi\CurVer" "" "MozillaMapi.1"
+ WriteRegStr HKLM "$1\MozillaMapi.1" "" "Mozilla MAPI"
+ WriteRegStr HKLM "$1\MozillaMapi.1\CLSID" "" "{29F458BE-8866-11D5-A3DD-00B0D0F3BAA7}"
+
+ ; The Reinstall Command is defined at
+ ; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_adv/registeringapps.asp
+ WriteRegStr HKLM "$0\InstallInfo" "HideIconsCommand" "$\"$7$\" /HideShortcuts"
+ WriteRegStr HKLM "$0\InstallInfo" "ShowIconsCommand" "$\"$7$\" /ShowShortcuts"
+ WriteRegStr HKLM "$0\InstallInfo" "ReinstallCommand" "$\"$7$\" /SetAsDefaultAppGlobal"
+
+ ClearErrors
+ ReadRegDWORD $1 HKLM "$0\InstallInfo" "IconsVisible"
+ ; If the IconsVisible name value pair doesn't exist add it otherwise the
+ ; application won't be displayed in Set Program Access and Defaults.
+ ${If} ${Errors}
+ ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
+ WriteRegDWORD HKLM "$0\InstallInfo" "IconsVisible" 1
+ ${Else}
+ WriteRegDWORD HKLM "$0\InstallInfo" "IconsVisible" 0
+ ${EndIf}
+ ${EndIf}
+
+ ; Mail shell/open/command
+ WriteRegStr HKLM "$0\shell\open\command" "" "$\"$8$\" -mail"
+
+ ; options
+ WriteRegStr HKLM "$0\shell\properties" "" "$(CONTEXT_OPTIONS)"
+ WriteRegStr HKLM "$0\shell\properties\command" "" "$\"$8$\" -options"
+
+ ; safemode
+ WriteRegStr HKLM "$0\shell\safemode" "" "$(CONTEXT_SAFE_MODE)"
+ WriteRegStr HKLM "$0\shell\safemode\command" "" "$\"$8$\" -safe-mode"
+
+ ; Protocols
+ StrCpy $1 "$\"$8$\" -compose $\"%1$\""
+ ${AddHandlerValues} "$0\Protocols\mailto" "$1" "$8,0" "${AppRegNameMail} URL" "true" ""
+
+ ; Vista Capabilities registry keys
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationDescription" "$(REG_APP_DESC)"
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationIcon" "$8,0"
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationName" "${AppRegNameMail}"
+ WriteRegStr HKLM "$0\Capabilities\FileAssociations" ".eml" "SeaMonkeyEML"
+ WriteRegStr HKLM "$0\Capabilities\StartMenu" "Mail" "${BrandFullNameInternal}"
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "mailto" "SeaMonkeyCOMPOSE"
+
+ ; Vista Registered Application
+ WriteRegStr HKLM "Software\RegisteredApplications" "${AppRegNameMail}" "$0\Capabilities"
+!macroend
+!define SetClientsMail "!insertmacro SetClientsMail"
+
+; XXXrstrong - there are several values that will be overwritten by and
+; overwrite other installs of the same application.
+!macro SetClientsNews
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ ${GetLongPath} "$INSTDIR\uninstall\helper.exe" $7
+ ${GetLongPath} "$INSTDIR\mozMapi32_InUse.dll" $6
+
+ StrCpy $0 "Software\Clients\News\${BrandFullNameInternal}"
+ WriteRegStr HKLM "$0" "" "${BrandFullNameInternal}"
+ WriteRegStr HKLM "$0\DefaultIcon" "" "$8,0"
+ WriteRegStr HKLM "$0" "DLLPath" "$6"
+
+ ; The MapiProxy dll can exist in multiple installs of the application.
+ ; Registration occurs as follows with the last action to occur being the one
+ ; that wins: On install and software update when helper.exe runs with the
+ ; /PostUpdate argument. On setting the application as the system's default
+ ; application using Window's "Set program access and defaults".
+ !ifndef NO_LOG
+ ${LogHeader} "DLL Registration"
+ !endif
+ ClearErrors
+ ${RegisterDLL} "$INSTDIR\MapiProxy_InUse.dll"
+ !ifndef NO_LOG
+ ${If} ${Errors}
+ ${LogMsg} "** ERROR Registering: $INSTDIR\MapiProxy_InUse.dll **"
+ ${Else}
+ ${LogUninstall} "DLLReg: \MapiProxy_InUse.dll"
+ ${LogMsg} "Registered: $INSTDIR\MapiProxy_InUse.dll"
+ ${EndIf}
+ !endif
+
+ StrCpy $1 "Software\Classes\CLSID\{29F458BE-8866-11D5-A3DD-00B0D0F3BAA7}"
+ WriteRegStr HKLM "$1\LocalServer32" "" "$\"$8$\" /MAPIStartup"
+ WriteRegStr HKLM "$1\ProgID" "" "MozillaMapi.1"
+ WriteRegStr HKLM "$1\VersionIndependentProgID" "" "MozillaMAPI"
+ StrCpy $1 "SOFTWARE\Classes"
+ WriteRegStr HKLM "$1\MozillaMapi" "" "Mozilla MAPI"
+ WriteRegStr HKLM "$1\MozillaMapi\CLSID" "" "{29F458BE-8866-11D5-A3DD-00B0D0F3BAA7}"
+ WriteRegStr HKLM "$1\MozillaMapi\CurVer" "" "MozillaMapi.1"
+ WriteRegStr HKLM "$1\MozillaMapi.1" "" "Mozilla MAPI"
+ WriteRegStr HKLM "$1\MozillaMapi.1\CLSID" "" "{29F458BE-8866-11D5-A3DD-00B0D0F3BAA7}"
+
+ ; Mail shell/open/command
+ WriteRegStr HKLM "$0\shell\open\command" "" "$\"$8$\" -mail"
+
+ ; Vista Capabilities registry keys
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationDescription" "$(REG_APP_DESC)"
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationIcon" "$8,0"
+ WriteRegStr HKLM "$0\Capabilities" "ApplicationName" "${AppRegNameNews}"
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "nntp" "SeaMonkeyNEWS"
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "news" "SeaMonkeyNEWS"
+ WriteRegStr HKLM "$0\Capabilities\URLAssociations" "snews" "SeaMonkeyNEWS"
+
+ ; Protocols
+ StrCpy $1 "$\"$8$\" -osint -mail $\"%1$\""
+ ${AddHandlerValues} "$0\Protocols\nntp" "$1" "$8,0" "${AppRegNameNews} URL" "true" ""
+ ${AddHandlerValues} "$0\Protocols\news" "$1" "$8,0" "${AppRegNameNews} URL" "true" ""
+ ${AddHandlerValues} "$0\Protocols\snews" "$1" "$8,0" "${AppRegNameNews} URL" "true" ""
+
+ ; Vista Registered Application
+ WriteRegStr HKLM "Software\RegisteredApplications" "${AppRegNameNews}" "$0\Capabilities"
+!macroend
+!define SetClientsNews "!insertmacro SetClientsNews"
+
+!macro SetAppKeys
+ ${GetLongPath} "$INSTDIR" $8
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}\${AppVersion} (${AB_CD})\Main"
+ ${WriteRegStr2} $TmpVal "$0" "Install Directory" "$8" 0
+ ${WriteRegStr2} $TmpVal "$0" "PathToExe" "$8\${FileMainEXE}" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}\${AppVersion} (${AB_CD})\Uninstall"
+ ${WriteRegStr2} $TmpVal "$0" "Description" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}\${AppVersion} (${AB_CD})"
+ ${WriteRegStr2} $TmpVal "$0" "" "${AppVersion} (${AB_CD})" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal} ${AppVersion}\bin"
+ ${WriteRegStr2} $TmpVal "$0" "PathToExe" "$8\${FileMainEXE}" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal} ${AppVersion}\extensions"
+ ${WriteRegStr2} $TmpVal "$0" "Components" "$8\components" 0
+ ${WriteRegStr2} $TmpVal "$0" "Plugins" "$8\plugins" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal} ${AppVersion}"
+ ${WriteRegStr2} $TmpVal "$0" "GeckoVer" "${GREVersion}" 0
+
+ StrCpy $0 "Software\Mozilla\${BrandFullNameInternal}"
+ ${WriteRegStr2} $TmpVal "$0" "" "${GREVersion}" 0
+ ${WriteRegStr2} $TmpVal "$0" "CurrentVersion" "${AppVersion} (${AB_CD})" 0
+!macroend
+!define SetAppKeys "!insertmacro SetAppKeys"
+
+!macro SetUninstallKeys
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\Uninstall\${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})"
+
+ WriteRegStr HKLM "$0" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $1 "HKCU"
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${Else}
+ StrCpy $1 "HKLM"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ DeleteRegValue HKLM "$0" "${BrandShortName}InstallerTest"
+ ${EndIf}
+
+ ${GetLongPath} "$INSTDIR" $8
+
+ ; Write the uninstall registry keys
+ ${WriteRegStr2} $1 "$0" "Comments" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
+ ${WriteRegStr2} $1 "$0" "DisplayIcon" "$8\${FileMainEXE},0" 0
+ ${WriteRegStr2} $1 "$0" "DisplayName" "${BrandFullNameInternal} ${AppVersion} (${ARCH} ${AB_CD})" 0
+ ${WriteRegStr2} $1 "$0" "DisplayVersion" "${AppVersion}" 0
+ ${WriteRegStr2} $1 "$0" "InstallLocation" "$8" 0
+ ${WriteRegStr2} $1 "$0" "Publisher" "Mozilla" 0
+ ${WriteRegStr2} $1 "$0" "UninstallString" "$8\uninstall\helper.exe" 0
+ ${WriteRegStr2} $1 "$0" "URLInfoAbout" "${URLInfoAbout}" 0
+ ${WriteRegStr2} $1 "$0" "URLUpdateInfo" "${URLUpdateInfo}" 0
+ ${WriteRegDWORD2} $1 "$0" "NoModify" 1 0
+ ${WriteRegDWORD2} $1 "$0" "NoRepair" 1 0
+
+ ${GetSize} "$8" "/S=0K" $R2 $R3 $R4
+ ${WriteRegDWORD2} $1 "$0" "EstimatedSize" $R2 0
+
+ ${If} "$TmpVal" == "HKLM"
+ SetShellVarContext all ; Set SHCTX to all users (e.g. HKLM)
+ ${Else}
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${EndIf}
+!macroend
+!define SetUninstallKeys "!insertmacro SetUninstallKeys"
+
+; Updates protocol handlers if their registry open command value is for this
+; install location
+!macro UpdateProtocolHandlers
+ ; Store the command to open the app with an url in a register for easy access.
+ ${GetLongPath} "$INSTDIR\${FileMainEXE}" $8
+ StrCpy $0 "SOFTWARE\Classes"
+ StrCpy $1 "$\"$8$\" -osint -compose $\"%1$\""
+ StrCpy $2 "$\"$8$\" -osint -mail $\"%1$\""
+ StrCpy $3 "$\"$8$\" -requestPending -osint -url $\"%1$\""
+ StrCpy $4 "$\"%1$\",,0,0,,,,"
+ StrCpy $5 "$\"$8$\" -url $\"%1$\""
+
+ ; Only set the file and protocol handlers if the existing one under HKCR is
+ ; for this install location.
+ ${IsHandlerForInstallDir} "SeaMonkeyHTML" $R9
+ ${If} "$R9" == "true"
+ ; An empty string is used for the 5th param because SeaMonkeyHTML is not a
+ ; protocol handler.
+ ${AddHandlerValues} "$0\SeaMonkeyHTML" "$5" \
+ "$INSTDIR\chrome\icons\default\html-file.ico,0" \
+ "${AppRegName} Document" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "SeaMonkeyURL" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "SeaMonkeyURL" "$3" "$8,0" \
+ "${AppRegName} URL" "delete"
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "ftp" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "ftp" "$3" "$8,0" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "http" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "http" "$3" "$8,0" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "https" $R9
+ ${If} "$R9" == "true"
+ ${AddDisabledDDEHandlerValues} "https" "$3" "$8,0" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "SeaMonkeyEML" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\SeaMonkeyEML" "$2" \
+ "$INSTDIR\chrome\icons\default\html-file.ico,0" \
+ "${AppRegNameMail} Document" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "SeaMonkeyMAIL" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\SeaMonkeyMAIL" "$2" "$8,0" \
+ "${AppRegNameMail} URL" "delete" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "mailto" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\mailto" "$1" "$8,0" "" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "SeaMonkeyNEWS" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\SeaMonkeyNEWS" "$2" "$8,0" \
+ "${AppRegNameMail} URL" "delete" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "news" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\news" "$2" "$8,0" "" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "snews" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\snews" "$2" "$8,0" "" "" ""
+ ${EndIf}
+
+ ${IsHandlerForInstallDir} "nntp" $R9
+ ${If} "$R9" == "true"
+ ${AddHandlerValues} "SOFTWARE\Classes\nntp" "$2" "$8,0" "" "" ""
+ ${EndIf}
+!macroend
+!define UpdateProtocolHandlers "!insertmacro UpdateProtocolHandlers"
+!insertmacro RegCleanAppHandler
+
+!macro RegisterAccessibleHandler
+ ${RegisterDLL} "$INSTDIR\AccessibleHandler.dll"
+!macroend
+!define RegisterAccessibleHandler "!insertmacro RegisterAccessibleHandler"
+
+; Removes various registry entries for reasons noted below (does not use SHCTX).
+!macro RemoveDeprecatedKeys
+ StrCpy $0 "SOFTWARE\Classes"
+ ; Remove support for launching gopher urls from the shell during install or
+ ; update if the DefaultIcon is from seamonkey.exe.
+ ${RegCleanAppHandler} "gopher"
+
+ ; Remove support for launching chrome urls from the shell during install or
+ ; update if the DefaultIcon is from seamonkey.exe (Bug 301073).
+ ${RegCleanAppHandler} "chrome"
+
+ ; Delete gopher from Capabilities\URLAssociations if it is present.
+ ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
+ StrCpy $0 "Software\Clients\StartMenuInternet\$R9"
+ ClearErrors
+ ReadRegStr $2 HKLM "$0\Capabilities\URLAssociations" "gopher"
+ ${Unless} ${Errors}
+ DeleteRegValue HKLM "$0\Capabilities\URLAssociations" "gopher"
+ ${EndUnless}
+
+ ; Delete gopher from the user's UrlAssociations if it points to SeamonkeyURL.
+ StrCpy $0 "Software\Microsoft\Windows\Shell\Associations\UrlAssociations\gopher"
+ ReadRegStr $2 HKCU "$0\UserChoice" "Progid"
+ ${If} "$2" == "SeamonkeyURL"
+ DeleteRegKey HKCU "$0"
+ ${EndIf}
+
+ ; Remove the SupportUTF8 registry value as it causes MAPI issues on some locales
+ ; with non-ASCII characters in file names.
+ StrCpy $0 "Software\Clients\Mail\${ClientsRegName}"
+ DeleteRegValue HKLM $0 "SupportUTF8"
+!macroend
+!define RemoveDeprecatedKeys "!insertmacro RemoveDeprecatedKeys"
+
+; Removes various directories and files for reasons noted below.
+!macro RemoveDeprecatedFiles
+ ; Remove the Java Console extension (bug 1165156)
+ FindFirst $0 $1 "$INSTDIR\extensions\{CAFEEFAC-00*-0000-*-ABCDEFFEDCBA}"
+ loopDirs:
+ StrCmp $1 "" doneDirs
+ ${If} ${FileExists} "$INSTDIR\extensions\$1"
+ !ifndef NO_LOG
+ ${LogMsg} "Removing Java console edition in: $INSTDIR\extensions\$1"
+ !endif
+ RmDir /r /REBOOTOK "$INSTDIR\extensions\$1"
+ ${EndIf}
+ FindNext $0 $1
+ Goto loopDirs
+ doneDirs:
+ FindClose $0
+!macroend
+!define RemoveDeprecatedFiles "!insertmacro RemoveDeprecatedFiles"
+
+!macro FixClassKeys
+ StrCpy $0 "SOFTWARE\Classes"
+
+ ; BROWSER part
+ ; File handler keys and name value pairs that may need to be created during
+ ; install or upgrade.
+ ReadRegStr $2 SHCTX "$0\.shtml" "Content Type"
+ ${If} $2 == ""
+ StrCpy $2 "$0\.shtml"
+ ${WriteRegStr2} $TmpVal "$0\.shtml" "" "shtmlfile" 0
+ ${WriteRegStr2} $TmpVal "$0\.shtml" "Content Type" "text/html" 0
+ ${WriteRegStr2} $TmpVal "$0\.shtml" "PerceivedType" "text" 0
+ ${EndIf}
+
+ ReadRegStr $2 SHCTX "$0\.xht" "Content Type"
+ ${If} $2 == ""
+ ${WriteRegStr2} $TmpVal "$0\.xht" "" "xhtfile" 0
+ ${WriteRegStr2} $TmpVal "$0\.xht" "Content Type" "application/xhtml+xml" 0
+ ${EndIf}
+
+ ReadRegStr $2 SHCTX "$0\.xhtml" "Content Type"
+ ${If} $2 == ""
+ ${WriteRegStr2} $TmpVal "$0\.xhtml" "" "xhtmlfile" 0
+ ${WriteRegStr2} $TmpVal "$0\.xhtml" "Content Type" "application/xhtml+xml" 0
+ ${EndIf}
+
+ ; Protocol handler keys and name value pairs that may need to be updated during
+ ; install or upgrade.
+
+ ; Store the command to open the app with an url in a register for easy access.
+ GetFullPathName $8 "$INSTDIR\${FileMainEXE}"
+ StrCpy $1 "$\"$8$\" -requestPending -osint -url $\"%1$\""
+ StrCpy $2 "$\"$8$\" -url $\"%1$\""
+
+ ; Always set the file and protocol handlers since they may specify a
+ ; different path and the path is used by Vista when setting associations.
+ ${AddHandlerValues} "$0\SeaMonkeyURL" "$1" "$8,0" "${AppRegName} URL" "delete" "true"
+
+ ; An empty string is used for the 5th param because SeaMonkeyHTML is not a
+ ; protocol handler
+ ${AddHandlerValues} "$0\SeaMonkeyHTML" "$2" \
+ "$INSTDIR\chrome\icons\default\html-file.ico,0" \
+ "${AppRegName} Document" "" ""
+
+ ReadRegStr $2 SHCTX "$0\http\shell\open\command" ""
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\http" "$1" "$8,0" "" "" "true"
+ ${EndUnless}
+
+ ReadRegStr $2 SHCTX "$0\https\shell\open\command" ""
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\https" "$1" "$8,0" "" "" "true"
+ ${EndUnless}
+
+ ReadRegStr $2 SHCTX "$0\ftp\shell\open\command" ""
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\ftp" "$1" "$8,0" "" "" "true"
+ ${EndUnless}
+
+ ; MAIL/NEWS part
+ GetFullPathName $8 "$INSTDIR\${FileMainEXE}"
+
+ StrCpy $1 "$\"$8$\" -compose $\"%1$\""
+ ${AddHandlerValues} "$0\SeaMonkeyCOMPOSE" "$1" "$8,0" "${AppRegNameMail} URL" "delete" ""
+
+ ReadRegStr $2 SHCTX "$0\mailto\shell\open\command" ""
+ ${GetPathFromString} "$2" $3
+ GetFullPathName $2 "$3"
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\mailto" "$1" "$8,0" "" "" ""
+ ${EndUnless}
+
+ StrCpy $1 "$\"$8$\" $\"%1$\""
+ ${AddHandlerValues} "$0\SeaMonkeyEML" "$1" "$INSTDIR\chrome\icons\default\html-file.ico,0" "${AppRegNameMail} Document" "" ""
+
+ StrCpy $1 "$\"$8$\" -osint -mail $\"%1$\""
+ ${AddHandlerValues} "$0\SeaMonkeyNEWS" "$1" "$8,0" "${AppRegNameNews} URL" "delete" ""
+
+ ReadRegStr $2 SHCTX "$0\news\shell\open\command" ""
+ ${GetPathFromString} "$2" $3
+ GetFullPathName $2 "$3"
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\news" "$1" "$8,0" "" "" ""
+ ${EndUnless}
+
+ ReadRegStr $2 SHCTX "$0\snews\shell\open\command" ""
+ ${GetPathFromString} "$2" $3
+ GetFullPathName $2 "$3"
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\snews" "$1" "$8,0" "" "" ""
+ ${EndUnless}
+
+ ReadRegStr $2 SHCTX "$0\nntp\shell\open\command" ""
+ ${GetPathFromString} "$2" $3
+ GetFullPathName $2 "$3"
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ ${AddHandlerValues} "$0\nntp" "$1" "$8,0" "" "" ""
+ ${EndUnless}
+
+ ; remove DI and SOC from the .eml class if it exists
+ ReadRegStr $2 SHCTX "$0\.eml\shell\open\command" ""
+ ${GetPathFromString} "$2" $3
+ GetFullPathName $2 "$3"
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ DeleteRegKey HKLM "$0\.eml\shell\open\command"
+ ${EndUnless}
+
+ ReadRegStr $2 SHCTX "$0\.eml\DefaultIcon" ""
+ ${GetPathFromString} "$2" $3
+ GetFullPathName $2 "$3"
+ ClearErrors
+ ${WordFind} "$2" "${FileMainEXE}" "E+1{" $R1
+ ${Unless} ${Errors}
+ DeleteRegKey HKLM "$0\.eml\DefaultIcon"
+ ${EndUnless}
+
+!macroend
+!define FixClassKeys "!insertmacro FixClassKeys"
+
+; Creates the shortcuts log ini file with the appropriate entries if it doesn't
+; already exist.
+!macro CreateShortcutsLog
+ ${GetShortcutsLogPath} $0
+ ${Unless} ${FileExists} "$0"
+ ; Default to ${BrandFullName} for the Start Menu Folder
+ StrCpy $TmpVal "${BrandFullName}"
+ ; Prior to Unicode installer the Start Menu directory was written to the
+ ; registry and this value can be used to set the Start Menu directory.
+ ClearErrors
+ ReadRegStr $0 SHCTX "Software\Mozilla\${BrandFullNameInternal}\${AppVersion} (${AB_CD})\Main" "Start Menu Folder"
+ ${If} ${Errors}
+ ${FindSMProgramsDir} $0
+ ${If} "$0" != ""
+ StrCpy $TmpVal "$0"
+ ${EndIf}
+ ${Else}
+ StrCpy $TmpVal "$0"
+ ${EndUnless}
+
+ ${LogSMProgramsDirRelPath} "$TmpVal"
+ ${LogSMProgramsShortcut} "${BrandFullName}.lnk"
+ ${LogSMProgramsShortcut} "${BrandFullName} ($(SAFE_MODE)).lnk"
+ ${LogSMProgramsShortcut} "${BrandFullNameInternal} $(MAILNEWS_TEXT).lnk"
+ ${LogSMProgramsShortcut} "$(PROFILE_TEXT).lnk"
+ ${LogQuickLaunchShortcut} "${BrandFullName}.lnk"
+ ${LogDesktopShortcut} "${BrandFullName}.lnk"
+ ${EndUnless}
+!macroend
+!define CreateShortcutsLog "!insertmacro CreateShortcutsLog"
+
+; The MAPI DLLs are copied and the copies are used for the MAPI registration
+; to lessen file in use errors on application update.
+!macro UpgradeMapiDLLs
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\MapiProxy_InUse.dll"
+ ${If} ${Errors}
+ ${DeleteFile} "$INSTDIR\MapiProxy_InUse.dll.moz-delete" ; shouldn't exist
+ Rename "$INSTDIR\MapiProxy_InUse.dll" "$INSTDIR\MapiProxy_InUse.dll.moz-delete"
+ Delete /REBOOTOK "$INSTDIR\MapiProxy_InUse.dll.moz-delete"
+ ${EndIf}
+ CopyFiles /SILENT "$INSTDIR\MapiProxy.dll" "$INSTDIR\MapiProxy_InUse.dll"
+
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\mozMapi32_InUse.dll"
+ ${If} ${Errors}
+ ${DeleteFile} "$INSTDIR\mozMapi32_InUse.dll.moz-delete" ; shouldn't exist
+ Rename "$INSTDIR\mozMapi32_InUse.dll" "$INSTDIR\mozMapi32_InUse.dll.moz-delete"
+ Delete /REBOOTOK "$INSTDIR\mozMapi32_InUse.dll.moz-delete"
+ ${EndIf}
+ CopyFiles /SILENT "$INSTDIR\mozMapi32.dll" "$INSTDIR\mozMapi32_InUse.dll"
+!macroend
+!define UpgradeMapiDLLs "!insertmacro UpgradeMapiDLLs"
+
+; The files to check if they are in use during (un)install so the restart is
+; required message is displayed. All files must be located in the $INSTDIR
+; directory.
+!macro PushFilesToCheck
+ ; The first string to be pushed onto the stack MUST be "end" to indicate
+ ; that there are no more files to check in $INSTDIR and the last string
+ ; should be ${FileMainEXE} so if it is in use the CheckForFilesInUse macro
+ ; returns after the first check.
+ Push "end"
+ Push "AccessibleHandler.dll"
+ Push "AccessibleMarshal.dll"
+ Push "IA2Marshal.dll"
+ Push "freebl3.dll"
+ Push "nssckbi.dll"
+ Push "nspr4.dll"
+ Push "nssdbm3.dll"
+ Push "sqlite3.dll"
+ Push "mozsqlite3.dll"
+ Push "xpcom.dll"
+ Push "crashreporter.exe"
+ Push "minidump-analyzer.exe"
+ Push "pingsender.exe"
+ Push "updater.exe"
+ Push "xpicleanup.exe"
+ Push "MapiProxy.dll"
+ Push "MapiProxy_InUse.dll"
+ Push "mozMapi32.dll"
+ Push "mozMapi32_InUse.dll"
+ Push "${FileMainEXE}"
+!macroend
+!define PushFilesToCheck "!insertmacro PushFilesToCheck"
+
+; The !ifdef NO_LOG prevents warnings when compiling the installer since these
+; functions are currently only used by the uninstaller.
+!ifdef NO_LOG
+Function SetAsDefaultMailAppUser
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${SetHandlersMail}
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\RegisteredApplications" "${AppRegNameMail}"
+ ; Only register as the handler if the app registry name exists
+ ; under the RegisteredApplications registry key.
+ ${Unless} ${Errors}
+ AppAssocReg::SetAppAsDefaultAll "${AppRegNameMail}"
+ ${EndUnless}
+FunctionEnd
+
+Function SetAsDefaultNewsAppUser
+ SetShellVarContext current ; Set SHCTX to the current user (e.g. HKCU)
+ ${SetHandlersNews}
+ ClearErrors
+ ReadRegStr $0 HKLM "Software\RegisteredApplications" "${AppRegNameNews}"
+ ; Only register as the handler if the app registry name exists
+ ; under the RegisteredApplications registry key.
+ ${Unless} ${Errors}
+ AppAssocReg::SetAppAsDefaultAll "${AppRegNameNews}"
+ ${EndUnless}
+FunctionEnd
+!endif
+
diff --git a/comm/suite/installer/windows/nsis/uninstaller.nsi b/comm/suite/installer/windows/nsis/uninstaller.nsi
new file mode 100644
index 0000000000..8a6a312e59
--- /dev/null
+++ b/comm/suite/installer/windows/nsis/uninstaller.nsi
@@ -0,0 +1,580 @@
+# 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/.
+
+# Also requires:
+# AppAssocReg http://nsis.sourceforge.net/Application_Association_Registration_plug-in
+# CityHash http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
+# ShellLink http://nsis.sourceforge.net/ShellLink_plug-in
+# UAC http://nsis.sourceforge.net/UAC_plug-in
+
+
+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
+!verbose 3
+
+; 7-Zip provides better compression than the lzma from NSIS so we add the files
+; uncompressed and use 7-Zip to create a SFX archive of it
+SetDatablockOptimize on
+SetCompress off
+CRCCheck on
+
+RequestExecutionLevel user
+
+Unicode true
+ManifestSupportedOS all
+ManifestDPIAware true
+
+!addplugindir ./
+
+; prevents compiling of the reg write logging.
+!define NO_LOG
+
+Var TmpVal
+
+; Other included files may depend upon these includes!
+; The following includes are provided by NSIS.
+!include FileFunc.nsh
+!include LogicLib.nsh
+!include MUI.nsh
+!include WinMessages.nsh
+!include WinVer.nsh
+!include WordFunc.nsh
+
+!insertmacro GetSize
+!insertmacro GetOptions
+!insertmacro GetParameters
+!insertmacro GetParent
+!insertmacro StrFilter
+!insertmacro WordFind
+
+!insertmacro un.GetParent
+
+; The following includes are custom.
+!include branding.nsi
+!include defines.nsi
+!include common.nsh
+!include locales.nsi
+
+; This is named BrandShortName helper because we use this for software update
+; post update cleanup.
+VIAddVersionKey "FileDescription" "${BrandShortName} Helper"
+VIAddVersionKey "OriginalFilename" "helper.exe"
+
+; Most commonly used macros for managing shortcuts
+!insertmacro _LoggingShortcutsCommon
+
+!insertmacro AddDisabledDDEHandlerValues
+!insertmacro AddHandlerValues
+!insertmacro CheckIfRegistryKeyExists
+!insertmacro CleanUpdateDirectories
+!insertmacro CleanVirtualStore
+!insertmacro FindSMProgramsDir
+!insertmacro GetLongPath
+!insertmacro GetPathFromString
+!insertmacro InitHashAppModelId
+!insertmacro IsHandlerForInstallDir
+!insertmacro RegCleanMain
+!insertmacro RegCleanUninstall
+!insertmacro SetBrandNameVars
+!insertmacro UnloadUAC
+!insertmacro UpdateShortcutAppModelIDs
+!insertmacro WordReplace
+!insertmacro WriteRegDWORD2
+!insertmacro WriteRegStr2
+
+!insertmacro un.ChangeMUIHeaderImage
+!insertmacro un.CheckForFilesInUse
+!insertmacro un.CleanUpdateDirectories
+!insertmacro un.CleanVirtualStore
+!insertmacro un.DeleteShortcuts
+!insertmacro un.GetLongPath
+!insertmacro un.GetSecondInstallPath
+!insertmacro un.InitHashAppModelId
+!insertmacro un.ManualCloseAppPrompt
+!insertmacro un.ParseUninstallLog
+!insertmacro un.RegCleanAppHandler
+!insertmacro un.RegCleanFileHandler
+!insertmacro un.RegCleanMain
+!insertmacro un.RegCleanProtocolHandler
+!insertmacro un.RegCleanUninstall
+!insertmacro un.RemoveQuotesFromPath
+!insertmacro un.SetBrandNameVars
+
+!include shared.nsh
+
+; Helper macros for ui callbacks. Insert these after shared.nsh
+!insertmacro OnEndCommon
+!insertmacro UninstallOnInitCommon
+
+!insertmacro un.OnEndCommon
+!insertmacro un.UninstallUnOnInitCommon
+
+Name "${BrandFullName}"
+OutFile "helper.exe"
+!ifdef HAVE_64BIT_BUILD
+ InstallDir "$PROGRAMFILES64\${BrandFullName}\"
+!else
+ InstallDir "$PROGRAMFILES32\${BrandFullName}\"
+!endif
+ShowUnInstDetails nevershow
+
+################################################################################
+# Modern User Interface - MUI
+
+!define MUI_ABORTWARNING
+!define MUI_ICON setup.ico
+!define MUI_UNICON setup.ico
+!define MUI_WELCOMEPAGE_TITLE_3LINES
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_RIGHT
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
+
+; Use a right to left header image when the language is right to left
+!ifdef ${AB_CD}_rtl
+!define MUI_HEADERIMAGE_BITMAP_RTL wizHeaderRTL.bmp
+!else
+!define MUI_HEADERIMAGE_BITMAP wizHeader.bmp
+!endif
+
+/**
+ * Uninstall Pages
+ */
+; Welcome Page
+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome
+!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome
+!insertmacro MUI_UNPAGE_WELCOME
+
+; Uninstall Confirm Page
+UninstPage custom un.preConfirm un.leaveConfirm
+
+; Remove Files Page
+!insertmacro MUI_UNPAGE_INSTFILES
+
+; Don't setup the survey controls, functions, etc. when the application has
+; defined NO_UNINSTALL_SURVEY
+!ifndef NO_UNINSTALL_SURVEY
+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preFinish
+!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
+!define MUI_FINISHPAGE_SHOWREADME ""
+!define MUI_FINISHPAGE_SHOWREADME_TEXT $(SURVEY_TEXT)
+!define MUI_FINISHPAGE_SHOWREADME_FUNCTION un.Survey
+!endif
+
+!insertmacro MUI_UNPAGE_FINISH
+
+; Use the default dialog for IDD_VERIFY for a simple Banner
+ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
+
+################################################################################
+# Install Sections
+; Empty section required for the installer to compile as an uninstaller
+Section ""
+SectionEnd
+
+################################################################################
+# Uninstall Sections
+
+Section "Uninstall"
+ SetDetailsPrint textonly
+ DetailPrint $(STATUS_UNINSTALL_MAIN)
+ SetDetailsPrint none
+
+ ; Delete the app exe to prevent launching the app while we are uninstalling.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ; If the user closed the application it can take several seconds for it to
+ ; shut down completely. If the application is being used by another user we
+ ; can still delete the files when the system is restarted.
+ Sleep 5000
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ClearErrors
+ ${EndIf}
+
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${un.RegCleanMain} "Software\Mozilla"
+ ${un.RegCleanUninstall}
+ ${un.DeleteShortcuts}
+
+ ; setup the application model id registration value
+ ${un.InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+
+ ; Unregister resources associated with Win7 taskbar jump lists.
+ ${If} "$AppUserModelID" != ""
+ ApplicationID::UninstallJumpLists "$AppUserModelID"
+ ${EndIf}
+
+ ; Remove the updates directory for Vista and above
+ ${un.CleanUpdateDirectories} "Mozilla\SeaMonkey" "Mozilla\updates"
+
+ ; Remove any app model id's stored in the registry for this install path
+ DeleteRegValue HKCU "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+ DeleteRegValue HKLM "Software\Mozilla\${AppName}\TaskBarIDs" "$INSTDIR"
+
+ ClearErrors
+ WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
+ ${If} ${Errors}
+ StrCpy $TmpVal "HKCU" ; used primarily for logging
+ ${Else}
+ SetShellVarContext all ; Set SHCTX to HKLM
+ DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+ StrCpy $TmpVal "HKLM" ; used primarily for logging
+ ${un.RegCleanMain} "Software\Mozilla"
+ ${un.RegCleanUninstall}
+ ${un.DeleteShortcuts}
+ ${EndIf}
+
+ ${un.RegCleanAppHandler} "SeaMonkeyURL"
+ ${un.RegCleanAppHandler} "SeaMonkeyHTML"
+ ${un.RegCleanAppHandler} "SeaMonkeyMAIL"
+ ${un.RegCleanAppHandler} "SeaMonkeyNEWS"
+ ${un.RegCleanAppHandler} "SeaMonkeyEML"
+ ${un.RegCleanProtocolHandler} "http"
+ ${un.RegCleanProtocolHandler} "https"
+ ${un.RegCleanProtocolHandler} "ftp"
+ ${un.RegCleanProtocolHandler} "mailto"
+ ${un.RegCleanProtocolHandler} "news"
+ ${un.RegCleanProtocolHandler} "nntp"
+ ${un.RegCleanProtocolHandler} "snews"
+
+ ClearErrors
+ ReadRegStr $R9 HKCR "SeaMonkeyEML" ""
+ ; Don't clean up the file handlers if the SeaMonkeyEML key still exists since
+ ; there could be a second installation that may be the default file handler
+ ${If} ${Errors}
+ ${un.RegCleanFileHandler} ".eml" "SeaMonkeyEML"
+ ${EndIf}
+
+ ClearErrors
+ ReadRegStr $R9 HKCR "SeaMonkeyHTML" ""
+ ; Don't clean up the file handlers if the SeaMonkeyHTML key still exists since
+ ; there could be a second installation that may be the default file handler
+ ${If} ${Errors}
+ ${un.RegCleanFileHandler} ".htm" "SeaMonkeyHTML"
+ ${un.RegCleanFileHandler} ".html" "SeaMonkeyHTML"
+ ${un.RegCleanFileHandler} ".shtml" "SeaMonkeyHTML"
+ ${un.RegCleanFileHandler} ".webm" "SeaMonkeyHTML"
+ ${un.RegCleanFileHandler} ".xht" "SeaMonkeyHTML"
+ ${un.RegCleanFileHandler} ".xhtml" "SeaMonkeyHTML"
+ ${EndIf}
+
+ SetShellVarContext all ; Set SHCTX to HKLM
+ ${un.GetSecondInstallPath} "Software\Mozilla" $R9
+ ${If} $R9 == "false"
+ SetShellVarContext current ; Set SHCTX to HKCU
+ ${un.GetSecondInstallPath} "Software\Mozilla" $R9
+ ${EndIf}
+
+ StrCpy $0 "Software\Clients\StartMenuInternet\${FileMainEXE}\shell\open\command"
+ ReadRegStr $R1 HKLM "$0" ""
+ ${un.RemoveQuotesFromPath} "$R1" $R1
+ ${un.GetParent} "$R1" $R1
+
+ ; Only remove the StartMenuInternet key if it refers to this install location.
+ ; The StartMenuInternet registry key is independent of the default browser
+ ; settings. The XPInstall base un-installer always removes this key if it is
+ ; uninstalling the default browser and it will always replace the keys when
+ ; installing even if there is another install of SeaMonkey that is set as the
+ ; default browser. Now the key is always updated on install but it is only
+ ; removed if it refers to this install location.
+ ${If} "$INSTDIR" == "$R1"
+ DeleteRegKey HKLM "Software\Clients\StartMenuInternet\${FileMainEXE}"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegName}"
+ ${EndIf}
+
+ StrCpy $0 "Software\Clients\Mail\${BrandFullNameInternal}\shell\open\command"
+ ReadRegStr $R1 HKLM "$0" ""
+ ${un.RemoveQuotesFromPath} "$R1" $R1
+ ${un.GetParent} "$R1" $R1
+
+ ; Only remove the Clients\Mail and Clients\News key if it refers to this
+ ; install location. The Clients\Mail & Clients\News keys are independent
+ ; of the default app for the OS settings. The XPInstall base un-installer
+ ; always removes these keys if it is uninstalling the default app and it
+ ; will always replace the keys when installing even if there is another
+ ; install of SeaMonkey that is set as the default app. Now the keys are always
+ ; updated on install but are only removed if they refer to this install
+ ; location.
+ ${If} "$INSTDIR" == "$R1"
+ DeleteRegKey HKLM "Software\Clients\Mail\${BrandFullNameInternal}"
+ DeleteRegKey HKLM "Software\Clients\News\${BrandFullNameInternal}"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegNameMail}"
+ DeleteRegValue HKLM "Software\RegisteredApplications" "${AppRegNameNews}"
+ ${EndIf}
+
+ StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
+ ${If} $R9 == "false"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\${FileMainEXE}"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
+ DeleteRegKey HKLM "$0"
+ DeleteRegKey HKCU "$0"
+ ${Else}
+ ReadRegStr $R1 HKLM "$0" ""
+ ${un.RemoveQuotesFromPath} "$R1" $R1
+ ${un.GetParent} "$R1" $R1
+ ${If} "$INSTDIR" == "$R1"
+ WriteRegStr HKLM "$0" "" "$R9"
+ ${un.GetParent} "$R9" $R1
+ WriteRegStr HKLM "$0" "Path" "$R1"
+ ${EndIf}
+ ${EndIf}
+
+ ; Remove directories and files we always control before parsing the uninstall
+ ; log so empty directories can be removed.
+ ${If} ${FileExists} "$INSTDIR\updates"
+ RmDir /r /REBOOTOK "$INSTDIR\updates"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\defaults\shortcuts"
+ RmDir /r /REBOOTOK "$INSTDIR\defaults\shortcuts"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\distribution"
+ RmDir /r /REBOOTOK "$INSTDIR\distribution"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\removed-files"
+ Delete /REBOOTOK "$INSTDIR\removed-files"
+ ${EndIf}
+
+ ; Application update won't add these files to the uninstall log so delete
+ ; them if they still exist.
+ ${If} ${FileExists} "$INSTDIR\MapiProxy_InUse.dll"
+ Delete /REBOOTOK "$INSTDIR\MapiProxy_InUse.dll"
+ ${EndIf}
+ ${If} ${FileExists} "$INSTDIR\mozMapi32_InUse.dll"
+ Delete /REBOOTOK "$INSTDIR\mozMapi32_InUse.dll"
+ ${EndIf}
+
+ ; Remove files that may be left behind by the application in the
+ ; VirtualStore directory.
+ ${un.CleanVirtualStore}
+
+ ; Only unregister the dll if the registration points to this installation
+ ReadRegStr $R1 HKCR "CLSID\{0D68D6D0-D93D-4D08-A30D-F00DD1F45B24}\InProcServer32" ""
+ ${If} "$INSTDIR\AccessibleMarshal.dll" == "$R1"
+ ${UnregisterDLL} "$INSTDIR\AccessibleMarshal.dll"
+ ${EndIf}
+
+ ; Only unregister the dll if the registration points to this installation
+ ReadRegStr $R1 HKCR "CLSID\${AccessibleHandlerCLSID}\InProcHandler32" ""
+ ${If} "$INSTDIR\AccessibleHandler.dll" == "$R1"
+ ${UnregisterDLL} "$INSTDIR\AccessibleHandler.dll"
+ ${EndIf}
+
+ ; Parse the uninstall log to unregister dll's and remove all installed
+ ; files / directories this install is responsible for.
+ ${un.ParseUninstallLog}
+
+ ; Remove the uninstall directory that we control
+ RmDir /r /REBOOTOK "$INSTDIR\uninstall"
+
+ ; Remove the installation directory if it is empty
+ ${RemoveDir} "$INSTDIR"
+
+ ; If seamonkey.exe was successfully deleted yet we still need to restart to
+ ; remove other files create a dummy seamonkey.exe.moz-delete to prevent the
+ ; installer from allowing an install without restart when it is required
+ ; to complete an uninstall.
+ ${If} ${RebootFlag}
+ ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-delete"
+ FileOpen $0 "$INSTDIR\${FileMainEXE}.moz-delete" w
+ FileWrite $0 "Will be deleted on restart"
+ Delete /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-delete"
+ FileClose $0
+ ${EndUnless}
+ ${EndIf}
+
+
+ ; Refresh desktop icons otherwise the start menu internet item won't be
+ ; removed and other ugly things will happen like recreation of the app's
+ ; clients registry key by the OS under some conditions.
+ System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)"
+SectionEnd
+
+################################################################################
+# Helper Functions
+
+; Don't setup the survey controls, functions, etc. when the application has
+; defined NO_UNINSTALL_SURVEY
+!ifndef NO_UNINSTALL_SURVEY
+Function un.Survey
+ Exec "$\"$TmpVal$\" $\"${SurveyURL}$\""
+FunctionEnd
+!endif
+
+################################################################################
+# Language
+
+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
+!verbose push
+!verbose 3
+!include "overrideLocale.nsh"
+!include "customLocale.nsh"
+!verbose pop
+
+; Set this after the locale files to override it if it is in the locale. Using
+; " " for BrandingText will hide the "Nullsoft Install System..." branding.
+BrandingText " "
+
+################################################################################
+# Page pre, show, and leave functions
+
+Function un.preWelcome
+ ${If} ${FileExists} "$INSTDIR\distribution\modern-wizard.bmp"
+ Delete "$PLUGINSDIR\modern-wizard.bmp"
+ CopyFiles /SILENT "$INSTDIR\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
+ ${EndIf}
+FunctionEnd
+
+Function un.leaveWelcome
+ ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
+ Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)"
+
+ ${If} "$TmpVal" == "FoundMessageWindow"
+ Sleep 5000
+ ${EndIf}
+
+ ${PushFilesToCheck}
+
+ ${un.CheckForFilesInUse} $TmpVal
+
+ Banner::destroy
+
+ ${If} "$TmpVal" == "true"
+ StrCpy $TmpVal "FoundMessageWindow"
+ ${un.ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)"
+ StrCpy $TmpVal "true"
+ ${EndIf}
+ ${EndIf}
+FunctionEnd
+
+Function un.preConfirm
+ ${If} ${FileExists} "$INSTDIR\distribution\modern-header.bmp"
+ ${AndIf} $hHeaderBitmap == ""
+ Delete "$PLUGINSDIR\modern-header.bmp"
+ CopyFiles /SILENT "$INSTDIR\distribution\modern-header.bmp" "$PLUGINSDIR\modern-header.bmp"
+ ${un.ChangeMUIHeaderImage} "$PLUGINSDIR\modern-header.bmp"
+ ${EndIf}
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "3"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Text "$(UN_CONFIRM_UNINSTALLED_FROM)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Top "5"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Bottom "15"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Type "text"
+ ; The contents of this control must be set as follows in the pre function
+ ; ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
+ ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" State ""
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Top "17"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Bottom "30"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" flags "READONLY"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Text "$(UN_CONFIRM_CLICK)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Top "130"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Bottom "150"
+
+ ${If} "$TmpVal" == "true"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Type "label"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Text "$(SUMMARY_REBOOT_REQUIRED_UNINSTALL)"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Left "0"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Right "-1"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Top "35"
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Bottom "45"
+
+ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "4"
+ ${EndIf}
+
+ !insertmacro MUI_HEADER_TEXT "$(UN_CONFIRM_PAGE_TITLE)" "$(UN_CONFIRM_PAGE_SUBTITLE)"
+ ; The Summary custom page has a textbox that will automatically receive
+ ; focus. This sets the focus to the Install button instead.
+ !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "unconfirm.ini"
+ GetDlgItem $0 $HWNDPARENT 1
+ System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i"
+ ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND"
+ SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR"
+ !insertmacro MUI_INSTALLOPTIONS_SHOW
+FunctionEnd
+
+Function un.leaveConfirm
+ ; Try to delete the app executable and if we can't delete it try to find the
+ ; app's message window and prompt the user to close the app. This allows
+ ; running an instance that is located in another directory. If for whatever
+ ; reason there is no message window we will just rename the app's files and
+ ; then remove them on restart if they are in use.
+ ClearErrors
+ ${DeleteFile} "$INSTDIR\${FileMainEXE}"
+ ${If} ${Errors}
+ ${un.ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)"
+ ${EndIf}
+FunctionEnd
+
+!ifndef NO_UNINSTALL_SURVEY
+Function un.preFinish
+ ; Do not modify the finish page if there is a reboot pending
+ ${Unless} ${RebootFlag}
+ ; Setup the survey controls, functions, etc.
+ StrCpy $TmpVal "SOFTWARE\Microsoft\IE Setup\Setup"
+ ClearErrors
+ ReadRegStr $0 HKLM $TmpVal "Path"
+ ${If} ${Errors}
+ !insertmacro MUI_INSTALLOPTIONS_WRITE "ioSpecial.ini" "settings" "NumFields" "3"
+ ${Else}
+ ExpandEnvStrings $0 "$0" ; this value will usually contain %programfiles%
+ ${If} $0 != "\"
+ StrCpy $0 "$0\"
+ ${EndIf}
+ StrCpy $0 "$0\iexplore.exe"
+ ClearErrors
+ GetFullPathName $TmpVal $0
+ ${If} ${Errors}
+ !insertmacro MUI_INSTALLOPTIONS_WRITE "ioSpecial.ini" "settings" "NumFields" "3"
+ ${Else}
+ ; When we add an optional action to the finish page the cancel button
+ ; is enabled. This disables it and leaves the finish button as the
+ ; only choice.
+ !insertmacro MUI_INSTALLOPTIONS_WRITE "ioSpecial.ini" "settings" "cancelenabled" "0"
+ ${EndIf}
+ ${EndIf}
+ ${EndUnless}
+FunctionEnd
+!endif
+
+################################################################################
+# Initialization Functions
+
+Function .onInit
+ ; We need this set up for most of the helper.exe operations.
+ !ifdef AppName
+ ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
+ !endif
+ ${UninstallOnInitCommon}
+FunctionEnd
+
+Function un.onInit
+ StrCpy $LANGUAGE 0
+
+ ${un.UninstallUnOnInitCommon}
+
+ !insertmacro InitInstallOptionsFile "unconfirm.ini"
+FunctionEnd
+
+Function .onGUIEnd
+ ${OnEndCommon}
+FunctionEnd
+
+Function un.onGUIEnd
+ ${un.OnEndCommon}
+FunctionEnd
+
diff --git a/comm/suite/installer/windows/nsis/updater_append.ini b/comm/suite/installer/windows/nsis/updater_append.ini
new file mode 100644
index 0000000000..1b4bc1be7d
--- /dev/null
+++ b/comm/suite/installer/windows/nsis/updater_append.ini
@@ -0,0 +1,16 @@
+; 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/.
+
+
+; IMPORTANT: This file should always start with a newline in case a locale
+; provided updater.ini does not end with a newline.
+; Application to launch after an update has been successfully applied. This
+; must be in the same directory or a sub-directory of the directory of the
+; application executable that initiated the software update.
+[PostUpdateWin]
+; ExeRelPath is the path to the PostUpdateWin executable relative to the
+; application executable.
+ExeRelPath=uninstall\helper.exe
+; ExeArg is the argument to pass to the PostUpdateWin exe
+ExeArg=/PostUpdate
diff --git a/comm/suite/locales/Makefile.in b/comm/suite/locales/Makefile.in
new file mode 100644
index 0000000000..e55b5a9683
--- /dev/null
+++ b/comm/suite/locales/Makefile.in
@@ -0,0 +1,165 @@
+# 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/.
+
+LOCALE_TOPDIR=$(topsrcdir)/comm
+LOCALE_RELATIVEDIR=suite/locales
+
+include $(topsrcdir)/config/config.mk
+
+MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f)))
+
+SUBMAKEFILES += \
+ $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/Makefile \
+ $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/Makefile \
+ $(NULL)
+
+# This makefile uses variable overrides from the l10n-% target to
+# build non-default locales to non-default dist/ locations. Be aware!
+
+PWD := $(CURDIR)
+
+# These are defaulted to be compatible with the files the wget-en-US target
+# pulls. You may override them if you provide your own files.
+ZIP_IN ?= $(ABS_DIST)/$(PACKAGE)
+
+MOZ_LANGPACK_EID=langpack-$(AB_CD)@seamonkey.mozilla.org
+# For Nightly, we know where to get the builds from to do local repacks
+ifdef NIGHTLY_BUILD
+export EN_US_BINARY_URL ?= https://archive.mozilla.org/pub/seamonkey/nightly/latest-comm-central-trunk
+endif
+
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+MOZ_PKG_MAC_DSSTORE=$(ABS_DIST)/branding/dsstore
+MOZ_PKG_MAC_BACKGROUND=$(ABS_DIST)/branding/background.png
+MOZ_PKG_MAC_ICON=$(ABS_DIST)/branding/disk.icns
+MOZ_PKG_MAC_EXTRA=--symlink '/Applications:/ '
+endif
+
+MOZ_SFX_PACKAGE=$(topsrcdir)/comm/other-licenses/7zstub/seamonkey/7zSD.sfx
+
+NON_OMNIJAR_FILES = \
+ defaults/messenger/mailViews.dat \
+ defaults/profile/panels.rdf \
+ defaults/profile/mimeTypes.rdf \
+ defaults/profile/chrome/userChrome-example.css \
+ defaults/profile/chrome/userContent-example.css \
+ $(NULL)
+
+# Required for l10n.mk - defines a list of app sub dirs that should
+# be included in langpack xpis.
+# Currently not set in SeaMonkey.
+DIST_SUBDIRS = $(DIST_SUBDIR)
+
+include $(topsrcdir)/config/rules.mk
+
+include $(topsrcdir)/toolkit/locales/l10n.mk
+
+PROFILE_CHROME = userChrome-example.css userContent-example.css
+
+NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD))
+
+BOOKMARKS_INC_FILE = $(call MERGE_FILE,profile/bookmarks.inc)
+BOOKMARKS_XTRA_FILE = $(call MERGE_FILE,profile/bookmarks.extra)
+
+# the #include in the .in file requires all to be in the same dir, sadly.
+$(FINAL_TARGET)/defaults/profile/bookmarks.html: $(BOOKMARKS_INC_FILE) $(BOOKMARKS_XTRA_FILE) $(srcdir)/generic/profile/bookmarks.html.in
+ $(SYSINSTALL) -D $(dir $@)
+ $(RM) -rf profile/*bookmarks*
+ $(NSINSTALL) -D profile
+ cp $^ profile/
+ $(call py_action,preprocessor, \
+ -DAB_CD=$(NO_JA_JP_MAC_AB_CD) \
+ profile/bookmarks.html.in \
+ -o $@)
+
+PANELS_XTRA_FILE = $(call MERGE_FILE,profile/panels.extra)
+
+# the #include in the .in file requires all to be in the same dir, sadly.
+$(FINAL_TARGET)/defaults/profile/panels.rdf: $(PANELS_XTRA_FILE) $(srcdir)/generic/profile/panels.rdf.in
+ $(SYSINSTALL) -D $(dir $@)
+ $(RM) -rf profile/panels*
+ $(NSINSTALL) -D profile
+ cp $^ profile/
+ sed \
+ -n 's/.*<RDF:Description about="\(.*\)">.*/ <RDF:li resource="\1"\/>/p' \
+ profile/panels.extra > profile/panels-urn.inc
+ $(call py_action,preprocessor, \
+ profile/panels.rdf.in -o $@)
+
+misc:: $(FINAL_TARGET)/defaults/profile/bookmarks.html ;
+misc:: $(FINAL_TARGET)/defaults/profile/panels.rdf ;
+
+misc:: $(call MERGE_FILES,$(addprefix profile/chrome/,$(PROFILE_CHROME)))
+ $(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/defaults/profile/chrome
+
+l10n-%: AB_CD=$*
+l10n-%:
+# merge if we're not en-US. Conditional function because
+# we need the current value of AB_CD.
+ $(if $(filter en-US,$(AB_CD)),, @$(MAKE) merge-$*)
+ $(NSINSTALL) -D $(DIST)/install
+ @$(MAKE) -C $(DEPTH)/toolkit/locales l10n-$*
+# @$(MAKE) -C $(DEPTH)/services/sync/locales AB_CD=$* XPI_NAME=locale-$*
+ @$(MAKE) -C ../../calendar/locales AB_CD=$* XPI_NAME=locale-$*
+ifdef MOZ_IRC
+ @$(MAKE) -C ../chatzilla/locales AB_CD=$* XPI_NAME=locale-$*
+endif
+ifdef MOZ_DEBUGQA
+ @$(MAKE) -C ../extensions/debugQA/locales AB_CD=$* XPI_NAME=locale-$*
+endif
+ @$(MAKE) -C $(DEPTH)/extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
+ @$(MAKE) -C $(DEPTH)/devtools/client/locales AB_CD=$* XPI_NAME=locale-$*
+ @$(MAKE) -C $(DEPTH)/devtools/startup/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
+ @$(MAKE) l10n AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/pref
+ @$(MAKE) multilocale.txt-$* AB_CD=$* XPI_NAME=locale-$*
+ @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$*
+
+package-win32-installer: $(SUBMAKEFILES)
+ $(MAKE) -C ../installer/windows CONFIG_DIR=l10ngen ZIP_IN='$(ZIP_OUT)' installer
+
+repackage-extensions: $(STAGEDIST)
+ $(MAKE) -C ../app pack-ext AB_CD=$(AB_CD) STAGEDIST=$(STAGEDIST)/extensions;
+
+langpack: langpack-$(AB_CD)
+
+# This is a generic target that will make a langpack, repack ZIP (+tarball)
+# builds, and repack and installer if applicable. It is called from the
+# tinderbox scripts. Alter it with caution.
+installers-%: IS_LANGUAGE_REPACK=1
+installers-%:
+ @$(MAKE) clobber-$*
+ @$(MAKE) l10n-$*
+ @$(MAKE) package-langpack-$*
+ @$(MAKE) repackage-zip-$*
+ @$(MAKE) repackage-extensions AB_CD=$*
+ifeq (WINNT,$(OS_ARCH))
+ @$(MAKE) package-win32-installer AB_CD=$*
+endif
+ @echo 'repackaging done'
+
+ifdef MOZ_UPDATER
+misc:: $(call MERGE_FILE,updater/updater.ini) $(call mkdir_deps,$(DIST)/bin)
+ifeq ($(OS_ARCH),WINNT)
+ cat $< $(srcdir)/../installer/windows/nsis/updater_append.ini | \
+ sed -e 's/^InfoText=/Info=/' -e 's/^TitleText=/Title=/' | \
+ sed -e 's/%MOZ_APP_DISPLAYNAME%/$(MOZ_APP_DISPLAYNAME)/' > \
+ $(FINAL_TARGET)/updater.ini
+else
+ cat $< | \
+ sed -e 's/^InfoText=/Info=/' -e 's/^TitleText=/Title=/' | \
+ sed -e 's/%MOZ_APP_DISPLAYNAME%/$(MOZ_APP_DISPLAYNAME)/' > \
+ $(FINAL_TARGET)/updater.ini
+endif
+endif
+
+ident:
+ @printf 'comm_revision '
+ @$(PYTHON3) $(topsrcdir)/config/printconfigsetting.py \
+ '$(STAGEDIST)/application.ini' App SourceStamp
+ @printf 'moz_revision '
+ @$(PYTHON3) $(topsrcdir)/config/printconfigsetting.py \
+ '$(STAGEDIST)/platform.ini' Build SourceStamp
+ @printf 'buildid '
+ @$(PYTHON3) $(topsrcdir)/config/printconfigsetting.py \
+ '$(STAGEDIST)/application.ini' App BuildID
diff --git a/comm/suite/locales/all-locales b/comm/suite/locales/all-locales
new file mode 100644
index 0000000000..fe69b87a86
--- /dev/null
+++ b/comm/suite/locales/all-locales
@@ -0,0 +1,30 @@
+ca
+cs
+da
+de
+el
+en-GB
+es-AR
+es-ES
+fi
+fr
+hu
+it
+ja
+ja-JP-mac
+ka
+lt
+ms
+nb-NO
+nl
+pl
+pt-BR
+pt-PT
+ro
+ru
+si
+sk
+sv-SE
+tr
+zh-CN
+zh-TW
diff --git a/comm/suite/locales/en-US/chrome/branding/aboutRights.dtd b/comm/suite/locales/en-US/chrome/branding/aboutRights.dtd
new file mode 100644
index 0000000000..eb5956b9c7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/branding/aboutRights.dtd
@@ -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/. -->
+<!-- LOCALIZATION NOTE This file is very similar to the one in Firefox from browser/locales/en-US/chrome/browser/aboutRights.dtd so you can use that file as a starting point -->
+<!-- rights.locale-direction instead of the usual local.dir entity, so RTL can skip translating page. -->
+<!ENTITY rights.locale-direction "ltr">
+<!ENTITY rights.pagetitle "about:rights">
+<!ENTITY rights.intro-header "About Your Rights">
+<!ENTITY rights.intro "&brandFullName; is free and open source software, built by a community of thousands from all over the world. There are a few things you should know:">
+
+<!-- Note on pointa / pointb / pointc form:
+ These points each have an embedded link in the HTML, so each point is
+ split into chunks for text before the link, the link text, and the text
+ after the link. If a localized grammar doesn't need the before or after
+ chunk, it can be left blank.
+
+ Also note the leading/trailing whitespace in strings here, which is
+ deliberate for formatting around the embedded links. -->
+<!ENTITY rights.intro-point1a "&brandShortName; is made available to you under the terms of the ">
+<!ENTITY rights.intro-point1b "Mozilla Public License">
+<!ENTITY rights.intro-point1c ". This means you may use, copy and distribute &brandShortName; to others. You are also welcome to modify the source code of &brandShortName; as you want to meet your needs. The Mozilla Public License also gives you the right to distribute your modified versions.">
+
+<!ENTITY rights.intro-point2aa "&vendorShortName; does not grant you any rights to the &quot;&brandFullName;&quot; trademarks or logos. Additional information on Trademarks may be found ">
+<!ENTITY rights.intro-point2b "here">
+<!ENTITY rights.intro-point2c ".">
+
+<!-- point 2da is technically point 3. in the list -->
+<!ENTITY rights.intro-point2da "Some features in &brandShortName;, such as the Crash Reporter, give you the option to provide feedback to &vendorShortName;. By choosing to submit feedback, you give &vendorShortName; permission to use the feedback to improve their applications, to publish the feedback on their websites, and to distribute the feedback.">
+
+<!-- point 3 text for official branded builds -->
+<!ENTITY rights.intro-point3a "Privacy policies for &vendorShortName;'s products may be found ">
+<!ENTITY rights.intro-point3b "here">
+<!ENTITY rights.intro-point3c ".">
+
+<!-- point 4 text for official branded builds -->
+<!ENTITY rights2.intro-point4a "&brandShortName; offers optional website information services, such as Add-on or Safe Browsing services; however, we cannot guarantee that they are 100&#37; accurate or error-free. More details, including information on how to disable the services, can be found in the ">
+<!ENTITY rights.intro-point4b "service terms">
+<!ENTITY rights.intro-point4c ".">
+
+<!ENTITY rights.webservices-header "&brandFullName; Website Services">
+
+<!-- Note that this paragraph references entities from extensions.dtd,
+ preferences.dtd, pref-smartupdate.dtd, and pref-security.dtd,
+ so that we can refer to text the user sees in the UI, without
+ this page being forgotten every time those strings are updated. -->
+<!-- intro paragraph for branded builds -->
+<!ENTITY rights3.webservices-aa "&vendorShortName; offers optional web-based services (&quot;Services&quot;) that are available for your use with this binary version of &brandShortName; as described below. Some of these services (such as Add-on suggestion and update services, the Safe Browsing service, or the Location Aware Browsing notification feature) are enabled by default. If you do not want to use any of these Services or the terms below are unacceptable, instructions on how to disable a particular feature or Service may be found ">
+<!ENTITY rights3.webservices-b "here">
+<!ENTITY rights3.webservices-c ". Other features and Services can be enabled or disabled in the application preferences.">
+
+<!-- add-on related points for branded builds -->
+<!ENTITY rights.addons-a "Add-on Services: ">
+<!ENTITY rights.addons-b "By default, &brandShortName; will suggest add-ons on the &view.discover.label; page and check for updates in regular intervals. If you wish to disable these features to avoid communication of information on installed add-ons to the server, follow these steps:">
+<!ENTITY rights.addons-term1 "Open the application preferences">
+<!ENTITY rights.addons-term2 "Select the &smart.label; panel in the &advance.label; category">
+<!ENTITY rights.addons-term3 "Uncheck the options to &quot;&autoAddOnsUpdates.label;&quot; and &quot;&enablePersonalized.label;&quot;">
+<!ENTITY rights.addons-term4 "The Add-on Services are now disabled">
+
+<!-- safe browsing points for branded builds -->
+<!ENTITY rights.safebrowsing-a "Safe Browsing: ">
+<!ENTITY rights.safebrowsing-b "Disabling the Safe Browsing feature is not recommended as it may result in you going to unsafe sites. If you wish to disable the feature completely, follow these steps:">
+<!ENTITY rights.safebrowsing-term1 "Open the application preferences">
+<!ENTITY rights.safebrowsing-term2 "Select the main &security.label; panel">
+<!ENTITY rights.safebrowsing-term3 "Uncheck the options to &quot;&blockAttackSites.label;&quot; and &quot;&blockWebForgeries.label;&quot;">
+<!ENTITY rights.safebrowsing-term4 "Safe Browsing is now disabled">
+
+<!-- location aware browsing points for branded builds -->
+<!ENTITY rights.locationawarebrowsing-a "Location Aware Browsing: ">
+<!ENTITY rights.locationawarebrowsing-b "is always opt-in. No location information is ever sent without your permission. If you wish to disable the feature completely, follow these steps:">
+<!ENTITY rights.locationawarebrowsing-term1 "Open the application preferences">
+<!ENTITY rights.locationawarebrowsing-term2 "Select the main &security.label; panel">
+<!ENTITY rights.locationawarebrowsing-term3 "Select the option to &quot;&geoDisabled.label;&quot;">
+<!ENTITY rights.locationawarebrowsing-term4 "Location Aware Browsing is now disabled">
+
+<!-- points 0-6 text for branded builds -->
+<!ENTITY rights.webservices-term0a "&vendorShortName; along with their contributors, licensors, and partners work to provide the most accurate and up-to-date Services. However, we cannot guarantee that this information is comprehensive and error-free. For example, the Safe Browsing Service may not identify some risky sites and may identify some safe sites in error and for the Location Aware Service all locations returned by our service providers are estimates only. Neither we nor our service providers guarantee the accuracy of the locations provided.">
+<!ENTITY rights.webservices-term1 "&vendorShortName; may discontinue or change the Services at its discretion.">
+<!ENTITY rights.webservices-term2a "You are welcome to use these Services with the accompanying version of &brandShortName;, and you have all the rights necessary to do so. &vendorShortName; and its licensors reserve all other rights in the Services. These terms are not intended to limit any rights granted under open source licenses applicable to &brandShortName; and to corresponding source code versions of &brandShortName;.">
+<!ENTITY rights.webservices-term3 "The Services are provided &quot;as-is.&quot; &vendorShortName;, its contributors, licensors, and distributors, disclaim all warranties, whether express or implied, including without limitation, warranties that the Services are merchantable and fit for your particular purposes. You bear the entire risk as to selecting the Services for your purposes and as to the quality and performance of the Services. Some jurisdictions do not allow the exclusion or limitation of implied warranties, so this disclaimer may not apply to you.">
+<!ENTITY rights.webservices-term4 "Except as required by law, &vendorShortName;, its contributors, licensors, and distributors will not be liable for any indirect, special, incidental, consequential, punitive, or exemplary damages arising out of or in any way relating to the use of &brandShortName; and the Services. The collective liability under these terms will not exceed $500 (five hundred dollars). Some jurisdictions do not allow the exclusion or limitation of certain damages, so this exclusion and limitation may not apply to you.">
+<!ENTITY rights.webservices-term5 "&vendorShortName; may update these terms as necessary from time to time. These terms may not be modified or cancelled without &vendorShortName;'s written agreement.">
+<!ENTITY rights.webservices-term6a "These terms are governed by and construed in accordance with the laws of Germany and all disputes arising in connection with these Terms shall be submitted to the exclusive jurisdiction of the competent court in Germany. If any portion of these terms is held to be invalid or unenforceable, the remaining portions will remain in full force and effect. In the event of a conflict between a translated version of these terms and the English language version, the English language version shall control.">
diff --git a/comm/suite/locales/en-US/chrome/branding/aboutRights.properties b/comm/suite/locales/en-US/chrome/branding/aboutRights.properties
new file mode 100644
index 0000000000..834761425d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/branding/aboutRights.properties
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+buttonLabel = Know your rights
+buttonAccessKey = K
+# LOCALIZATION NOTE (notifyRightsText2): Please check if the usage of vendorShortName in your translated
+# content is still correct too.
+notifyRightsText2 = %S is free and open software from the German registered association SeaMonkey e.V.
diff --git a/comm/suite/locales/en-US/chrome/browser/linkToolbar.dtd b/comm/suite/locales/en-US/chrome/browser/linkToolbar.dtd
new file mode 100644
index 0000000000..b2d28e42f4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/linkToolbar.dtd
@@ -0,0 +1,48 @@
+<!-- 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/. -->
+
+<!-- Link Toolbar Title -->
+<!ENTITY linkToolbar.label "Website Navigation Bar">
+<!ENTITY linkToolbar.accesskey "a">
+<!ENTITY linkToolbar.tooltip "Website Navigation Bar">
+
+<!-- Link Toolbar visibility options -->
+<!ENTITY linkToolbarAlways.label "Show Always">
+<!ENTITY linkToolbarAlways.accesskey "S">
+<!ENTITY linkToolbarAsNeeded.label "Show Only As Needed">
+<!ENTITY linkToolbarAsNeeded.accesskey "O">
+<!ENTITY linkToolbarNever.label "Hide Always">
+<!ENTITY linkToolbarNever.accesskey "H">
+
+<!-- Toolbar buttons, menus, and menuitems -->
+<!ENTITY topButton.label "Top">
+<!ENTITY upButton.label "Up">
+<!ENTITY firstButton.label "First">
+<!ENTITY prevButton.label "Previous">
+<!ENTITY nextButton.label "Next">
+<!ENTITY lastButton.label "Last">
+
+<!ENTITY documentButton.label "Document">
+
+<!ENTITY tocButton.label "Table of Contents">
+<!ENTITY chapterButton.label "Chapters">
+<!ENTITY sectionButton.label "Sections">
+<!ENTITY subSectionButton.label "Subsections">
+<!ENTITY appendixButton.label "Appendices">
+<!ENTITY glossaryButton.label "Glossary">
+<!ENTITY indexButton.label "Index">
+
+<!ENTITY moreButton.label "More">
+
+<!ENTITY helpButton.label "Help">
+<!ENTITY searchButton.label "Search">
+
+<!ENTITY authorButton.label "Author(s)">
+<!ENTITY copyrightButton.label "Copyright">
+
+<!ENTITY bookmarkButton.label "Bookmarks">
+
+<!ENTITY alternateButton.label "Other Versions">
+
+<!ENTITY feedButton.label "Subscribe">
diff --git a/comm/suite/locales/en-US/chrome/browser/mailNavigatorOverlay.dtd b/comm/suite/locales/en-US/chrome/browser/mailNavigatorOverlay.dtd
new file mode 100644
index 0000000000..f650598640
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/mailNavigatorOverlay.dtd
@@ -0,0 +1,30 @@
+<!-- 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/. -->
+
+<!ENTITY newMessageCmd.label "Message">
+<!ENTITY newMessageCmd.accesskey "M">
+<!ENTITY newMessageCmd.key "M">
+
+<!ENTITY newContactCmd.label "Contact…">
+<!ENTITY newContactCmd.accesskey "C">
+
+<!ENTITY sendPage.label "Send Page…">
+<!ENTITY sendPage.accesskey "S">
+
+<!ENTITY contextSendThisPage.label "Send This Page…">
+<!ENTITY contextSendThisPage.accesskey "d">
+<!ENTITY contextSendThisLink.label "Send This Link…">
+<!ENTITY contextSendThisLink.accesskey "k">
+<!ENTITY contextSendImage.label "Send Image…">
+<!ENTITY contextSendImage.accesskey "n">
+<!ENTITY contextSendVideo.label "Send Video…">
+<!ENTITY contextSendVideo.accesskey "n">
+<!ENTITY contextSendAudio.label "Send Audio…">
+<!ENTITY contextSendAudio.accesskey "n">
+<!ENTITY contextSendFrame.label "Send Frame…">
+<!ENTITY contextSendFrame.accesskey "d">
+
+<!ENTITY sendLinkCmd.label "Send Link…">
+<!ENTITY sendLinkCmd.accesskey "d">
+
diff --git a/comm/suite/locales/en-US/chrome/browser/metadata.dtd b/comm/suite/locales/en-US/chrome/browser/metadata.dtd
new file mode 100644
index 0000000000..faa4d1b0b8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/metadata.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!ENTITY no-properties.label "No properties set.">
+
+<!ENTITY caption.label "Element Properties">
+<!ENTITY image-sec.label "Image Properties">
+<!ENTITY image-url.label "Location:">
+<!ENTITY image-desc.label "Description:">
+<!ENTITY image-alt.label "Alternate text:">
+<!ENTITY image-width.label "Width:">
+<!ENTITY image-height.label "Height:">
+<!ENTITY image-filesize.label "Size of File:">
+<!ENTITY image-filesize.value "Unknown">
+<!ENTITY insdel-sec.label "Insert/Delete Properties">
+<!ENTITY insdel-cite.label "Info:">
+<!ENTITY insdel-date.label "Date:">
+<!ENTITY link-sec.label "Link Properties">
+<!ENTITY link-url.label "Address:">
+<!ENTITY link-target.label "Will open in:">
+<!ENTITY link-type.label "Target type:">
+<!ENTITY link-lang.label "Target language:">
+<!ENTITY link-rel.label "Relation:">
+<!ENTITY link-rev.label "Reversed relation:">
+<!ENTITY misc-sec.label "Miscellaneous Properties">
+<!ENTITY misc-lang.label "Text language:">
+<!ENTITY misc-title.label "Title:">
+<!ENTITY misc-tblsummary.label "Table summary:">
+<!ENTITY quote-sec.label "Quote Properties">
+<!ENTITY quote-cite.label "Info:">
diff --git a/comm/suite/locales/en-US/chrome/browser/metadata.properties b/comm/suite/locales/en-US/chrome/browser/metadata.properties
new file mode 100644
index 0000000000..45b9f76abe
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/metadata.properties
@@ -0,0 +1,19 @@
+# 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/.
+
+sameWindowText=Same window
+newWindowText=New window
+parentFrameText=Parent frame
+sameFrameText=Same frame
+embeddedText=Embedded
+unableToShowProps=No properties available.
+altTextMissing=Missing
+altTextBlank=Blank
+imageSize=%S KB (%S bytes)
+imageSizeUnknown=Unknown (not cached)
+imageWidth=%Spx
+imageHeight=%Spx
+# LOCALIZATION NOTE: Next two strings are for language name representations
+# %1$S = language name, %2$S = region name
+languageRegionFormat=%1$S (%2$S)
diff --git a/comm/suite/locales/en-US/chrome/browser/navigator.dtd b/comm/suite/locales/en-US/chrome/browser/navigator.dtd
new file mode 100644
index 0000000000..5d16671599
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/navigator.dtd
@@ -0,0 +1,87 @@
+<!-- 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/. -->
+
+<!-- extracted from content/navigator.xul -->
+
+<!-- LOCALIZATION NOTE : FILE This file contains the browser main menu items -->
+<!-- LOCALIZATION NOTE : FILE Do not translate commandkeys -->
+
+<!-- LOCALIZATION NOTE (mainWindow.title): DONT_TRANSLATE -->
+<!ENTITY mainWindow.title "&brandShortName;">
+<!-- LOCALIZATION NOTE (mainWindow.titlemodifier) : DONT_TRANSLATE -->
+<!ENTITY mainWindow.titlemodifier "&brandShortName;">
+<!-- LOCALIZATION NOTE (mainWindow.titlemodifiermenuseparator): DONT_TRANSLATE -->
+<!ENTITY mainWindow.titlemodifiermenuseparator " - ">
+
+<!ENTITY mainWindow.titleprivate "Private Browsing">
+
+<!ENTITY editPageCmd.label "Edit Page">
+<!ENTITY editPageCmd.accesskey "E">
+<!ENTITY editPageCmd.commandkey "e">
+
+<!ENTITY navbarCmd.label "Navigation Toolbar">
+<!ENTITY navbarCmd.accesskey "N">
+<!ENTITY bookmarksToolbarCmd.label "Bookmarks Toolbar">
+<!ENTITY bookmarksToolbarCmd.accesskey "m">
+
+<!ENTITY closeWindow.label "Close Window">
+<!ENTITY closeWindow.accesskey "W">
+
+<!ENTITY minimizeButton.tooltip "Minimize">
+<!ENTITY restoreButton.tooltip "Restore">
+
+<!ENTITY feedsMenu.label "Subscribe to This Page">
+<!ENTITY feedsMenu.accesskey "S">
+
+<!ENTITY menuBar.tooltip "Menu Bar">
+<!ENTITY bookmarksToolbar.tooltip "Bookmarks Toolbar">
+<!ENTITY navigationToolbar.tooltip "Navigation Toolbar">
+
+<!ENTITY editBookmark.done.label "Done">
+<!ENTITY editBookmark.cancel.label "Cancel">
+<!ENTITY editBookmark.removeBookmark.accessKey "R">
+
+<!-- Toolbar items -->
+<!ENTITY backButton.label "Back">
+<!ENTITY backButton.tooltip "Go back one page">
+<!ENTITY forwardButton.label "Forward">
+<!ENTITY forwardButton.tooltip "Go forward one page">
+<!ENTITY reloadButton.label "Reload">
+<!ENTITY reloadButton.tooltip "Reload current page">
+<!ENTITY stopButton.label "Stop">
+<!ENTITY stopButton.tooltip "Stop loading this page">
+<!ENTITY searchButton.label "Search">
+<!ENTITY searchButton.tooltip "Type a word in the field to the left, then click Search">
+<!ENTITY goButton.label "Go">
+<!ENTITY goButton.tooltip "Type a location in the field to the left, then click Go">
+<!ENTITY printButton.label "Print">
+<!ENTITY printButton.tooltip "Print this page">
+
+<!ENTITY locationBar.tooltip "Enter search term, keyword, or web address">
+<!ENTITY locationBar.accesskey "d">
+<!ENTITY locationBar.title "Location">
+<!ENTITY proxyIcon.tooltip "Drag and drop this icon to create a link to this page">
+
+<!ENTITY searchItem.title "Search">
+
+<!ENTITY bookmarksButton.label "Bookmarks">
+<!ENTITY bookmarksButton.tooltip "Bookmarks list">
+<!ENTITY homeButton.label "Home">
+<!ENTITY bookmarksToolbarItem.label "Bookmarks Toolbar Items">
+<!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">
+
+<!-- Statusbar -->
+<!ENTITY statusText.label "Done">
+
+<!ENTITY popupIcon.tooltiptext "Unblock this website's popups">
+
+<!ENTITY viewSecurityInfo.label "View Security Info">
+<!ENTITY viewSecurityInfo.accesskey "S">
+<!ENTITY viewCertificate.label "View Certificate">
+<!ENTITY viewCertificate.accesskey "C">
+<!ENTITY viewCertManager.label "Open Certificate Manager">
+<!ENTITY viewCertManager.accesskey "M">
+
+<!ENTITY zoomIn.tooltiptext "Larger">
+<!ENTITY zoomOut.tooltiptext "Smaller">
diff --git a/comm/suite/locales/en-US/chrome/browser/navigator.properties b/comm/suite/locales/en-US/chrome/browser/navigator.properties
new file mode 100644
index 0000000000..cb42bcd998
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/navigator.properties
@@ -0,0 +1,79 @@
+# 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/.
+
+nv_done=Done
+nv_timeout=Timed Out
+nv_stopped=Stopped
+openFile=Open File
+uploadFile=Upload File
+
+droponhomebutton=Drop a link or file to make it your home page
+droponhometitle=Set Home Page
+droponhomemsg=Do you want this document to be your new home page?
+droponhomeokbutton=Set Home Page
+
+jserror=An error has occurred on this page. Double click here for details.
+
+#SessionHistory.js
+nothingAvailable=(Nothing Available)
+
+#nsBrowserStatusHandler.js
+# LOCALIZATION NOTE: %S is the security certifiate issuer
+securityButtonTooltipSecure=Signed by %S
+securityButtonTooltipMixedContent=Warning: Contains unauthenticated content
+securityButtonTooltipInsecure=Displays security information about the current page
+
+# menu_close labels
+tabs.closeTab.label=Close Tab
+tabs.closeTab.accesskey=C
+tabs.close.label=Close
+tabs.close.accesskey=C
+
+tabs.recentlyClosed.format=%1$S %2$S
+
+windows.recentlyClosed.format=%1$S %2$S
+
+tabs.historyItem=Group of Tabs
+
+menuOpenAllInTabs.label=Open All in Tabs
+
+# urlbarBindings.xml
+# LOCALIZATION NOTE: This is for the location bar drop-down string:
+# "Search " + search_engine_name + " for " + user_input
+# e.g. "Search Google for abc"
+# DO NOT change the %S order when translate, the first %S must be the search engine name.
+searchFor=Search %S for "%S"
+
+# Star button
+starButtonOn.tooltip=Edit this bookmark
+starButtonOff.tooltip=Bookmark this page
+
+# Edit Bookmark UI
+editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
+editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
+editBookmarkPanel.bookmarkedRemovedTitle=Bookmark Removed
+editBookmarkPanel.editBookmarkTitle=Edit This Bookmark
+
+# LOCALIZATION NOTE (editBookmark.removeBookmarks.label)
+# Semi-colon list of plural forms. Replacement for #1 is
+# the number of bookmarks to be removed.
+# If this causes problems with localization you can also do "Remove Bookmarks (#1)"
+# instead of "Remove #1 Bookmarks".
+editBookmark.removeBookmarks.label=Remove Bookmark;Remove #1 Bookmarks
+
+# bookmark dialog strings
+
+# LOCALIZATION NOTE (addKeywordTitleAutoFill): %S will be replaced by the page's title
+# Used as the bookmark name when saving a keyword for a search field.
+addKeywordTitleAutoFill=Search %S
+
+extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name=SeaMonkey Default Theme
+extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description=This theme uses styles and colors from the system to fit in with other applications.
+
+extensions.modern@themes.mozilla.org.name=SeaMonkey Modern
+extensions.modern@themes.mozilla.org.description=A contemporary theme for all components.
+
+# LOCALIZATION NOTE (unknownLanguage)
+# %S will be replaced by the unrecognised language and region.
+unknownLanguage=Unknown (%S)
diff --git a/comm/suite/locales/en-US/chrome/browser/navigatorOverlay.dtd b/comm/suite/locales/en-US/chrome/browser/navigatorOverlay.dtd
new file mode 100644
index 0000000000..8ef749dc57
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/navigatorOverlay.dtd
@@ -0,0 +1,157 @@
+<!-- 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/. -->
+
+<!-- File Menu -->
+<!ENTITY tabCmd.label "Browser Tab">
+<!ENTITY tabCmd.accesskey "T">
+<!ENTITY tabCmd.commandkey "t">
+<!ENTITY openCmd.label "Open Web Location…">
+<!ENTITY openCmd.accesskey "L">
+<!ENTITY openCmd.commandkey "l">
+<!ENTITY openFileCmd.label "Open File…">
+<!ENTITY openFileCmd.accesskey "O">
+<!ENTITY openFileCmd.commandkey "o">
+
+<!ENTITY closeOtherTabs.label "Close Other Tabs">
+<!ENTITY closeOtherTabs.accesskey "T">
+<!-- LOCALIZATION NOTE (closeTabsToTheEnd.label): This should indicate the
+direction in which tabs are closed, i.e. locales that use RTL mode should say
+left instead of right. -->
+<!ENTITY closeTabsToTheEnd.label "Close Tabs to the Right">
+<!ENTITY closeTabsToTheEnd.accesskey "i">
+
+<!ENTITY uploadFile.label "Upload File…">
+<!ENTITY uploadFile.accesskey "F">
+
+<!-- Edit Menu -->
+<!ENTITY findOnCmd.label "Find in This Page…">
+
+<!-- View Menu -->
+<!ENTITY toolbarsCmd.label "Show/Hide">
+<!ENTITY toolbarsCmd.accesskey "w">
+<!ENTITY tabbarCmd.label "Tab Bar">
+<!ENTITY tabbarCmd.accesskey "T">
+<!ENTITY taskbarCmd.label "Status Bar">
+<!ENTITY taskbarCmd.accesskey "S">
+<!ENTITY componentbarCmd.label "Component Bar">
+<!ENTITY componentbarCmd.accesskey "C">
+
+<!ENTITY fullScreenCmd.label "Full Screen">
+<!ENTITY fullScreenCmd.accesskey "F">
+
+<!ENTITY useStyleSheetMenu.label "Use Style">
+<!ENTITY useStyleSheetMenu.accesskey "U">
+<!ENTITY useStyleSheetNone.label "None">
+<!ENTITY useStyleSheetNone.accesskey "N">
+<!ENTITY useStyleSheetPersistentOnly.label "Default Style">
+<!ENTITY useStyleSheetPersistentOnly.accesskey "D">
+<!ENTITY bidiSwitchPageDirectionItem.label "Switch Page Direction">
+<!ENTITY bidiSwitchPageDirectionItem.accesskey "g">
+<!ENTITY pageSourceCmd.label "Page Source">
+<!ENTITY pageSourceCmd.accesskey "o">
+<!ENTITY pageSourceCmd.commandkey "u">
+<!ENTITY pageInfoCmd.label "Page Info">
+<!ENTITY pageInfoCmd.accesskey "I">
+<!ENTITY pageInfoCmd.commandkey "i">
+
+<!-- Go Menu -->
+<!ENTITY goMenu.label "Go">
+<!ENTITY goMenu.accesskey "G">
+<!ENTITY goBackCmd.commandKey "[">
+<!ENTITY goForwardCmd.commandKey "]">
+<!ENTITY goHomeCmd.label "Home">
+<!ENTITY goHomeCmd.accesskey "H">
+<!ENTITY historyCmd.label "History">
+<!ENTITY historyCmd.accesskey "s">
+<!-- LOCALIZATION NOTE (historyCmd.key): This is used only on the mac. -->
+<!ENTITY historyCmd.key "H">
+<!-- LOCALIZATION NOTE (history.commandKey): This is used on platforms other
+ than the mac instead of historyCmd.key. -->
+<!ENTITY history.commandKey "h">
+<!ENTITY recentTabs.label "Recently Closed Tabs">
+<!ENTITY recentTabs.accesskey "C">
+<!ENTITY recentTabs.commandkey "t">
+<!ENTITY recentWindows.label "Recently Closed Windows">
+<!ENTITY recentWindows.accesskey "W">
+<!ENTITY recentWindows.commandkey "y">
+<!ENTITY historyRestoreLastSession.label "Restore Previous Session">
+<!ENTITY historyRestoreLastSession.accesskey "R">
+<!ENTITY syncTabsMenu.label "Tabs From Other Computers">
+<!ENTITY syncTabsMenu.accesskey "O">
+
+<!-- Bookmarks Menu -->
+<!ENTITY bookmarksMenu.label "Bookmarks">
+<!ENTITY bookmarksMenu.accesskey "B">
+<!ENTITY addCurPageCmd.label "Bookmark This Page">
+<!ENTITY addCurPageCmd.accesskey "B">
+<!ENTITY addCurPageAsCmd.label "File Bookmark…">
+<!ENTITY addCurPageAsCmd.accesskey "F">
+<!ENTITY addCurPageAsCmd.commandkey "d">
+<!ENTITY addCurTabsAsCmd.label "Bookmark This Group of Tabs…">
+<!ENTITY addCurTabsAsCmd.accesskey "G">
+<!ENTITY manBookmarksCmd.label "Manage Bookmarks…">
+<!ENTITY manBookmarksCmd.accesskey "M">
+<!ENTITY manBookmarksCmd.commandkey "b">
+
+<!-- Tools Menu -->
+<!ENTITY searchInternetCmd.label "Search the Web">
+<!ENTITY searchInternetCmd.accesskey "b">
+<!ENTITY searchInternet.commandKey "s">
+<!ENTITY translateMenu.label "Translate Page">
+<!ENTITY translateMenu.accesskey "T">
+
+<!ENTITY cookieMessageTitle.label "Cookie Permissions Changed">
+<!ENTITY cookieDisplayCookiesCmd.label "Manage Stored Cookies">
+<!ENTITY cookieDisplayCookiesCmd.accesskey "M">
+<!ENTITY cookieAllowCookiesCmd.label "Allow Cookies from This Website">
+<!ENTITY cookieAllowCookiesCmd.accesskey "A">
+<!ENTITY cookieAllowCookiesMsg.label "Cookies from this website will always be allowed.">
+<!ENTITY cookieAllowSessionCookiesCmd.label "Allow Session Cookies from This Website">
+<!ENTITY cookieAllowSessionCookiesCmd.accesskey "S">
+<!ENTITY cookieAllowSessionCookiesMsg.label "This website will be able to set cookies for the current session only.">
+<!ENTITY cookieCookiesDefaultCmd.label "Use Default Cookie Permissions">
+<!ENTITY cookieCookiesDefaultCmd.accesskey "U">
+<!ENTITY cookieCookiesDefaultMsg.label "Cookies from this website will be accepted or rejected based on default settings.">
+<!ENTITY cookieBlockCookiesCmd.label "Block Cookies from This Website">
+<!ENTITY cookieBlockCookiesCmd.accesskey "B">
+<!ENTITY cookieBlockCookiesMsg.label "Cookies from this website will always be rejected.">
+
+<!ENTITY cookieImageMessageTitle.label "Image Permissions Changed">
+<!ENTITY cookieDisplayImagesCmd.label "Manage Image Permissions">
+<!ENTITY cookieDisplayImagesCmd.accesskey "M">
+<!ENTITY cookieAllowImagesCmd.label "Allow Images from This Website">
+<!ENTITY cookieAllowImagesCmd.accesskey "A">
+<!ENTITY cookieAllowImagesMsg.label "Images from this website will always be downloaded.">
+<!ENTITY cookieImagesDefaultCmd.label "Use Default Image Permissions">
+<!ENTITY cookieImagesDefaultCmd.accesskey "U">
+<!ENTITY cookieImagesDefaultMsg.label "Images from this website will be downloaded based on default settings.">
+<!ENTITY cookieBlockImagesCmd.label "Block Images from This Website">
+<!ENTITY cookieBlockImagesCmd.accesskey "B">
+<!ENTITY cookieBlockImagesMsg.label "Images from this website will never be downloaded.">
+
+<!ENTITY popupsMessageChangeTitle.label "Popup Permissions Changed">
+<!ENTITY popupAllowCmd.label "Allow Popups from This Website">
+<!ENTITY popupAllowCmd.accesskey "A">
+<!ENTITY popupAllowMsg.label "Popups from this website will always be shown.">
+<!ENTITY popupDefaultCmd.label "Use Default Popup Permissions">
+<!ENTITY popupDefaultCmd.accesskey "U">
+<!ENTITY popupDefaultMsg.label "Popups from this website will be shown based on default settings.">
+<!ENTITY popupBlockCmd.label "Block Popups from This Website">
+<!ENTITY popupBlockCmd.accesskey "B">
+<!ENTITY popupBlockMsg.label "Popups from this website will not be shown.">
+<!ENTITY popupsManage.label "Manage Popups">
+<!ENTITY popupsManage.accesskey "M">
+
+<!ENTITY cookieCookieManager.label "Cookie Manager">
+<!ENTITY cookieCookieManager.accesskey "C">
+<!ENTITY cookieImageManager.label "Image Manager">
+<!ENTITY cookieImageManager.accesskey "I">
+<!ENTITY popupsManager.label "Popup Manager">
+<!ENTITY popupsManager.accesskey "o">
+
+<!ENTITY clearPrivateDataCmd.label "Clear Private Data…">
+<!ENTITY clearPrivateDataCmd.accesskey "e">
+
+<!ENTITY stopCmd.macCommandKey ".">
+<!ENTITY fullScreenCmd.commandKey "f">
diff --git a/comm/suite/locales/en-US/chrome/browser/pageInfo.dtd b/comm/suite/locales/en-US/chrome/browser/pageInfo.dtd
new file mode 100644
index 0000000000..283e0e319d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/pageInfo.dtd
@@ -0,0 +1,111 @@
+<!--
+# 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/.
+-->
+
+<!-- Note to localizers, don't localize the strings 'width' or 'height' -->
+<!ENTITY pageInfoWindow.dimensions "width: 100ch; height: 38em;">
+
+<!ENTITY copy.key "C">
+<!ENTITY copy.label "Copy">
+<!ENTITY copy.accesskey "C">
+<!ENTITY selectall.key "A">
+<!ENTITY selectall.label "Select All">
+<!ENTITY selectall.accesskey "A">
+<!ENTITY openHelpMac.key "?">
+<!ENTITY closeWindow.key "w">
+<!ENTITY copyLinks.label "Copy Link(s)">
+<!ENTITY copyLinks.accesskey "L">
+<!ENTITY openInNewTab.label "Open in New Tab">
+<!ENTITY openInNewTab.accesskey "T">
+<!ENTITY openInNewWindow.label "Open in New Window">
+<!ENTITY openInNewWindow.accesskey "W">
+
+<!ENTITY generalTab "General">
+<!ENTITY generalTab.accesskey "G">
+<!ENTITY generalTitle "Title:">
+<!ENTITY generalURL "Address:">
+<!ENTITY generalType "Type:">
+<!ENTITY generalMode "Render Mode:">
+<!ENTITY generalSize "Size:">
+<!ENTITY generalReferrer "Referring URL:">
+<!ENTITY generalSource "Cache Source:">
+<!ENTITY generalModified "Modified:">
+<!ENTITY generalEncoding2 "Text Encoding:">
+<!ENTITY generalMetaName "Name">
+<!ENTITY generalMetaContent "Content">
+<!ENTITY generalSecurityDetails "Details">
+<!ENTITY generalSecurityDetails.accesskey "D">
+
+<!ENTITY formsTab "Forms">
+<!ENTITY formsTab.accesskey "F">
+<!ENTITY formAction "Form Action">
+<!ENTITY formMethod "Method">
+<!ENTITY formName "Name">
+<!ENTITY formEncoding "Encoding:">
+<!ENTITY formTarget "Target:">
+<!ENTITY formFields "Fields:">
+<!ENTITY formLabel "Label">
+<!ENTITY formFName "Field Name">
+<!ENTITY formType "Type">
+<!ENTITY formCValue "Current Value">
+
+<!ENTITY linksTab "Links">
+<!ENTITY linksTab.accesskey "L">
+<!ENTITY linkName "Name">
+<!ENTITY linkAddress "Address">
+<!ENTITY linkType "Type">
+<!ENTITY linkTarget "Target">
+<!ENTITY linkAccessKey "Access Key">
+
+<!ENTITY mediaTab "Media">
+<!ENTITY mediaTab.accesskey "M">
+<!ENTITY mediaLocation "Location:">
+<!ENTITY mediaText "Associated Text:">
+<!ENTITY mediaAltHeader "Alternate Text">
+<!ENTITY mediaAddress "Address">
+<!ENTITY mediaType "Type">
+<!ENTITY mediaSize "Size">
+<!ENTITY mediaCount "Count">
+<!ENTITY mediaDimension "Dimensions:">
+<!ENTITY mediaLongdesc "Long Description:">
+<!ENTITY mediaBlockImage.accesskey "B">
+<!ENTITY mediaSaveAs "Save As…">
+<!ENTITY mediaSaveAs.accesskey "A">
+<!ENTITY mediaSaveAs2.accesskey "e">
+<!ENTITY mediaPreview "Media Preview:">
+
+<!ENTITY feedTab "Feeds">
+<!ENTITY feedTab.accesskey "F">
+<!ENTITY feedSubscribe "Subscribe">
+<!ENTITY feedSubscribe.accesskey "u">
+
+<!ENTITY permTab "Permissions">
+<!ENTITY permTab.accesskey "P">
+<!ENTITY permissionsFor "Permissions for:">
+
+<!ENTITY securityTab "Security">
+<!ENTITY securityTab.accesskey "S">
+<!ENTITY securityHeader "Security information for this page">
+<!ENTITY securityView.certView "View Certificate">
+<!ENTITY securityView.accesskey "V">
+<!ENTITY securityView.unknown "Unknown">
+
+
+<!ENTITY securityView.identity.header "Website Identity">
+<!ENTITY securityView.identity.owner "Owner:">
+<!ENTITY securityView.identity.domain "Website:">
+<!ENTITY securityView.identity.verifier "Verified by:">
+<!ENTITY securityView.identity.validity "Expires on:">
+
+<!ENTITY securityView.privacy.header "Privacy &amp; History">
+<!ENTITY securityView.privacy.history "Have I visited this website before today?">
+<!ENTITY securityView.privacy.cookies "Is this website storing information (cookies) on my computer?">
+<!ENTITY securityView.privacy.viewCookies "View Cookies">
+<!ENTITY securityView.privacy.viewCookies.accessKey "k">
+<!ENTITY securityView.privacy.passwords "Have I saved any passwords for this website?">
+<!ENTITY securityView.privacy.viewPasswords "View Saved Passwords">
+<!ENTITY securityView.privacy.viewPasswords.accessKey "w">
+
+<!ENTITY securityView.technical.header "Technical Details">
diff --git a/comm/suite/locales/en-US/chrome/browser/pageInfo.properties b/comm/suite/locales/en-US/chrome/browser/pageInfo.properties
new file mode 100644
index 0000000000..7d5a805c89
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/pageInfo.properties
@@ -0,0 +1,77 @@
+# 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/.
+
+pageInfo.page.title=Page Info - %S
+pageInfo.frame.title=Frame Info - %S
+
+noPageTitle=Untitled Page:
+unknown=Unknown
+default=Default
+notSet=Not specified
+yes=Yes
+no=No
+
+mediaImg=Image
+mediaVideo=Video
+mediaAudio=Audio
+mediaBGImg=Background
+mediaBorderImg=Border
+mediaListImg=Bullet
+mediaCursor=Cursor
+mediaObject=Object
+mediaEmbed=Embed
+mediaLink=Icon
+mediaInput=Input
+mediaFileSize=%S KB
+mediaSize=%Spx \u00D7 %Spx
+mediaSelectFolder=Select a Folder to Save the Images
+mediaBlockImage=Block Images from %S
+mediaUnknownNotCached=Unknown (not cached)
+mediaImageType=%S Image
+mediaAnimatedImageType=%S Image (animated, %S frames)
+mediaDimensions=%Spx \u00D7 %Spx
+mediaDimensionsScaled=%Spx \u00D7 %Spx (scaled to %Spx \u00D7 %Spx)
+
+generalQuirksMode=Quirks mode
+generalStrictMode=Standards compliance mode
+generalNotCached=Not cached
+generalDiskCache=Disk cache
+generalMemoryCache=Memory cache
+generalSize=%S KB (%S bytes)
+generalMetaTag=Meta (1 tag)
+generalMetaTags=Meta (%S tags)
+generalSiteIdentity=This website is owned by %S\nThis has been verified by %S
+
+formTitle=Form %S:
+formUntitled=Unnamed Form:
+formDefaultTarget=None (opens in same window)
+formChecked=Checked
+formUnchecked=Unchecked
+formPassword=••••••••
+
+linkAnchor=Anchor
+linkArea=Area
+linkSubmission=Form Submission
+linkSubmit=Submit Query
+linkRel=Related Item
+linkStylesheet=Stylesheet
+linkRev=Reverse Link
+linkX=Simple XLink
+linkScript=Script
+linkScriptInline=inline
+
+feedRss=RSS
+feedAtom=Atom
+feedXML=XML
+
+securityNoOwner=This website does not supply ownership information.
+# LOCALIZATION NOTE (securityVisitsNumber):
+# Semi-colon list of plural forms.
+# See: https://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of visits and can be used in all plural forms as needed, e.g.
+# for '1': 'Yes, #1 time'
+securityVisitsNumber=Yes, once;Yes, #1 times
+securityNoVisits=No
+
+permissions.useDefault=Use Default
diff --git a/comm/suite/locales/en-US/chrome/browser/region.properties b/comm/suite/locales/en-US/chrome/browser/region.properties
new file mode 100644
index 0000000000..37f28f99fe
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/region.properties
@@ -0,0 +1,26 @@
+# 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/.
+
+# navigator.properties
+#
+# all.js
+#
+browser.startup.homepage=https://www.seamonkey-project.org/start/
+
+browser.translation.service=https://translate.google.com/translate?prev=/language_tools&u=
+browser.translation.serviceDomain=translate.google.com
+browser.validate.html.service=https://validator.w3.org/check?charset=%28detect+automatically%29&doctype=Inline&uri=
+
+#config.js
+#
+startup.homepage_override_url=https://www.seamonkey-project.org/releases/seamonkey%VERSION%/
+
+# This is the default set of web based feed handlers shown in the reader
+# selection UI
+browser.contentHandlers.types.0.title=Feedly
+browser.contentHandlers.types.0.uri=https://feedly.com/i/subscription/feed/%s
+browser.contentHandlers.types.1.title=Inoreader
+browser.contentHandlers.types.1.uri=https://www.inoreader.com/?add_feed=%s
+browser.contentHandlers.types.2.title=My Yahoo!
+browser.contentHandlers.types.2.uri=https://add.my.yahoo.com/rss?url=%s
diff --git a/comm/suite/locales/en-US/chrome/browser/tabbrowser.dtd b/comm/suite/locales/en-US/chrome/browser/tabbrowser.dtd
new file mode 100644
index 0000000000..62f9612558
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/tabbrowser.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<!ENTITY untitledTab "(Untitled)">
+<!ENTITY newTab.label "New Tab">
+<!ENTITY newTab.accesskey "N">
+<!ENTITY closeTab.label "Close Tab">
+<!ENTITY closeTab.accesskey "C">
+<!ENTITY closeOtherTabs.label "Close Other Tabs">
+<!ENTITY closeOtherTabs.accesskey "O">
+<!-- LOCALIZATION NOTE (closeTabsToTheEnd.label): This should indicate the
+direction in which tabs are closed, i.e. locales that use RTL mode should say
+left instead of right. -->
+<!ENTITY closeTabsToTheEnd.label "Close Tabs to the Right">
+<!ENTITY closeTabsToTheEnd.accesskey "i">
+<!ENTITY reloadAllTabs.label "Reload All Tabs">
+<!ENTITY reloadAllTabs.accesskey "A">
+<!ENTITY reloadTab.label "Reload Tab">
+<!ENTITY reloadTab.accesskey "R">
+<!ENTITY bookmarkGroup.label "Bookmark This Group of Tabs">
+<!ENTITY bookmarkGroup.accesskey "B">
+<!ENTITY closeTabButton.tooltip "Close current tab">
+<!ENTITY newTabButton.tooltip "Open a new tab">
+<!ENTITY listAllTabs.tooltip "List all tabs">
+<!ENTITY undoCloseTab.label "Undo Close Tab">
+<!ENTITY undoCloseTab.accesskey "U">
diff --git a/comm/suite/locales/en-US/chrome/browser/tabbrowser.properties b/comm/suite/locales/en-US/chrome/browser/tabbrowser.properties
new file mode 100644
index 0000000000..b766f6de09
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/tabbrowser.properties
@@ -0,0 +1,32 @@
+# 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/.
+
+tabs.loading=Loading…
+tabs.untitled=Untitled
+tabs.closeWarningTitle=Confirm Closing Other Tabs
+# LOCALIZATION NOTE (tabs.closeWarningOther):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The singular form is not considered since this string is used only for
+# multiple tabs.
+tabs.closeWarningOther=;You are about to close #1 other tabs. Are you sure you want to continue?
+tabs.closeButton=Close other tabs
+tabs.closeWarningPromptMe=Warn me when I attempt to close other tabs
+tabs.closeWarningTitleAll=Confirm close
+# LOCALIZATION NOTE (tabs.closeWarningAll):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The singular form is not considered since this string is used only for
+# multiple tabs.
+tabs.closeWarningAll=;This browser window has #1 tabs open. Do you want to close it and all its tabs?
+tabs.closeButtonAll=Close all tabs
+tabs.closeWarningPromptMeAll=Warn me when closing multiple tabs
+
+# LOCALIZATION NOTE (tabs.openWarningTitle, tabs.openWarningMultipleBranded, tabs.openButtonMultiple, tabs.openWarningPromptMeBranded):
+# These items were moved from /chrome/common/places/places.properties.
+# Now they are not specific to bookmarks.
+tabs.openWarningTitle=Confirm open
+tabs.openWarningMultipleBranded=You are about to open %S tabs. This might slow down %S while the pages are loading. Are you sure you want to continue?
+tabs.openButtonMultiple=Open tabs
+tabs.openWarningPromptMeBranded=Warn me when opening multiple tabs might slow down %S
diff --git a/comm/suite/locales/en-US/chrome/browser/taskbar.properties b/comm/suite/locales/en-US/chrome/browser/taskbar.properties
new file mode 100644
index 0000000000..1247df1813
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/taskbar.properties
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+taskbar.tasks.newTab.label=Open new tab
+taskbar.tasks.newTab.description=Open a new browser tab.
+taskbar.tasks.newWindow.label=Open new window
+taskbar.tasks.newWindow.description=Open a new browser window.
+taskbar.tasks.newPrivate.label=Open private window
+taskbar.tasks.newPrivate.description=Open a new private browsing window.
+taskbar.tasks.mailWindow.label=Open Mail & Newsgroups
+taskbar.tasks.mailWindow.description=Open the Mail & Newsgroups window.
+taskbar.tasks.composeMessage.label=Compose new message
+taskbar.tasks.composeMessage.description=Compose a new message.
+taskbar.tasks.openAddressBook.label=Open Address Book
+taskbar.tasks.openAddressBook.description=Open your Address Book.
+taskbar.tasks.openEditor.label=Open new Composer page
+taskbar.tasks.openEditor.description=Open a new Composer page.
+taskbar.frequent.label=Frequent
+taskbar.recent.label=Recent
+
diff --git a/comm/suite/locales/en-US/chrome/browser/webDeveloper.dtd b/comm/suite/locales/en-US/chrome/browser/webDeveloper.dtd
new file mode 100644
index 0000000000..f14f7f1eeb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/browser/webDeveloper.dtd
@@ -0,0 +1,72 @@
+<!-- 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/. -->
+
+<!ENTITY validatePage.label "Validate This Page">
+<!ENTITY validatePage.accesskey "V">
+<!ENTITY allowRemoteDebugging.label "Allow Remote Debugging">
+<!ENTITY allowRemoteDebugging.accesskey "A">
+
+<!ENTITY devToolsCmd.keycode "VK_F12">
+<!ENTITY devToolsCmd.keytext "F12">
+
+<!ENTITY devtoolsConnect.label "Connect…">
+<!ENTITY devtoolsConnect.accesskey "e">
+
+<!ENTITY browserConsoleCmd.label "Browser Console">
+<!ENTITY browserConsoleCmd.commandkey "Ζ">
+<!ENTITY browserConsoleCmd.accesskey "B">
+
+<!ENTITY responsiveDesignTool.label "Responsive Design View">
+<!ENTITY responsiveDesignTool.accesskey "R">
+
+<!ENTITY eyedropper.label "Eyedropper">
+<!ENTITY eyedropper.accesskey "y">
+
+<!-- LOCALIZATION NOTE (scratchpad.label): This menu item label appears
+ - in the Tools menu. See bug 653093.
+ - The Scratchpad is intended to provide a simple text editor for creating
+ - and evaluating bits of JavaScript code for the purposes of function
+ - prototyping, experimentation and convenient scripting.
+ -
+ - It's quite possible that you won't have a good analogue for the word
+ - "Scratchpad" in your locale. You should feel free to find a close
+ - approximation to it or choose a word (or words) that means
+ - "simple discardable text editor". -->
+<!ENTITY scratchpad.label "Scratchpad">
+<!ENTITY scratchpad.accesskey "S">
+<!ENTITY scratchpad.keycode "VK_F4">
+<!ENTITY scratchpad.keytext "F4">
+
+<!-- LOCALIZATION NOTE (browserToolboxMenu.label): This is the label for the
+ - application menu item that opens the browser toolbox UI in the Tools menu. -->
+<!ENTITY browserToolboxMenu.label "Browser Toolbox">
+<!ENTITY browserToolboxMenu.accesskey "e">
+<!ENTITY browserToolboxCmd.commandkey "I">
+
+<!-- LOCALIZATION NOTE (browserContentToolboxMenu.label): This is the label for the
+ - application menu item that opens the browser content toolbox UI in the Tools menu.
+ - This toolbox allows to debug the chrome of the content process in multiprocess builds. -->
+<!ENTITY browserContentToolboxMenu.label "Browser Content Toolbox">
+<!ENTITY browserContentToolboxMenu.accesskey "x">
+
+<!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
+<!ENTITY devToolbarMenu.label "Developer Toolbar">
+<!ENTITY devToolbarMenu.accesskey "D">
+
+<!ENTITY webide.label "WebIDE">
+<!ENTITY webide.accesskey "W">
+<!ENTITY webide.keycode "VK_F8">
+<!ENTITY webide.keytext "F8">
+
+<!ENTITY devToolbar.keycode "VK_F2">
+<!ENTITY devToolbar.keytext "F2">
+
+<!ENTITY devToolboxMenuItem.label "Toggle Tools">
+<!ENTITY devToolboxMenuItem.accesskey "T">
+<!ENTITY devToolboxMenuItem.keytext "I">
+
+<!ENTITY devToolbarToolsButton.tooltip "Toggle developer tools">
+
+<!ENTITY getMoreDevtoolsCmd.label "Get More Tools">
+<!ENTITY getMoreDevtoolsCmd.accesskey "M">
diff --git a/comm/suite/locales/en-US/chrome/common/about.dtd b/comm/suite/locales/en-US/chrome/common/about.dtd
new file mode 100644
index 0000000000..1ddc1daa17
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/about.dtd
@@ -0,0 +1,70 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE the following declarations were copied from
+ mozilla/toolkit/locales/en-US/chrome/global/about.dtd which was removed
+ by bug 1408044. -->
+
+<!ENTITY about.version "version">
+
+<!-- LOCALIZATION NOTE (about.credits.beforeLink): note that there is no space
+ between this phrase and the linked about.credits.linkTitle phrase, so if
+ your locale needs a space between words, add it at the end of this
+ entity. -->
+<!ENTITY about.credits.beforeLink "See a list of ">
+<!ENTITY about.credits.linkTitle "contributors">
+<!-- LOCALIZATION NOTE (about.credits.afterLink): note that there is no space
+ between the linked about.credits.linkTitle phrase and this phrase, so if
+ your locale needs a space between words, add it at the start of this
+ entity. -->
+<!ENTITY about.credits.afterLink " to the Mozilla Project.">
+
+<!-- LOCALIZATION NOTE (about.license.beforeTheLink): note that there is no
+ space between this phrase and the linked about.license.linkTitle phrase,
+ so if your locale needs a space between words, add it at the end of this
+ entity. -->
+<!ENTITY about.license.beforeTheLink "Read the ">
+<!ENTITY about.license.linkTitle "licensing information">
+<!-- LOCALIZATION NOTE (about.license.afterTheLink): note that there is no
+ space between the linked about.license.linkTitle phrase and this phrase,
+ so if your locale needs a space between words, add it at the start of
+ this entity. -->
+<!ENTITY about.license.afterTheLink " for this product.">
+
+<!-- LOCALIZATION NOTE (about.relnotes.beforeTheLink): note that there is no
+ space between this phrase and the linked about.relnotes.linkTitle phrase,
+ so if your locale needs a space between words, add it at the end of this
+ entity. -->
+<!ENTITY about.relnotes.beforeTheLink "Read the ">
+<!ENTITY about.relnotes.linkTitle "release notes">
+<!-- LOCALIZATION NOTE (about.relnotes.afterTheLink): note that there is no
+ space between the linked about.relnotes.linkTitle phrase and this phrase,
+ so if your locale needs a space between words, add it at the start of
+ this entity. -->
+<!ENTITY about.relnotes.afterTheLink " for this version.">
+
+<!-- LOCALIZATION NOTE (about.buildconfig.beforeTheLink): note that there is
+ no space between this phrase and the linked about.buildconfig.linkTitle
+ phrase, so if your locale needs a space between words, add it at the end
+ of this entity. -->
+<!ENTITY about.buildconfig.beforeTheLink "See the ">
+<!ENTITY about.buildconfig.linkTitle "build configuration">
+<!-- LOCALIZATION NOTE (about.buildconfig.afterTheLink): note that there is no
+ space between the linked about.buildconfig.linkTitle phrase and this
+ phrase, so if your locale needs a space between words, add it at the
+ start of this entity. -->
+<!ENTITY about.buildconfig.afterTheLink " used for this version.">
+
+<!ENTITY about.buildIdentifier "Build identifier: ">
+
+<!-- LOCALIZATION NOTE end of declarations that were copied from
+ mozilla/toolkit/locales/en-US/chrome/global/about.dtd -->
+
+<!-- LOCALIZATION NOTE (channel.description.start,channel.description.end):
+ channel.description.start and channel.description.end create one sentence,
+ with the current channel label inserted in between.
+ example: You are currently on the _Stable_ update channel. -->
+<!ENTITY channel.description.start "You are currently on the ">
+<!ENTITY channel.description.end " update channel.">
+<!ENTITY about.userAgent "User agent: ">
diff --git a/comm/suite/locales/en-US/chrome/common/aboutPrivateBrowsing.dtd b/comm/suite/locales/en-US/chrome/common/aboutPrivateBrowsing.dtd
new file mode 100644
index 0000000000..b450f0c533
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/aboutPrivateBrowsing.dtd
@@ -0,0 +1,26 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE: where ".private" and ".normal" variants exist,
+ - the former is shown in a private browsing window and the latter in
+ - a regular (non-private) browsing window. -->
+
+<!ENTITY privatebrowsingpage.title.private "You are in a Private Browsing window">
+<!ENTITY privatebrowsingpage.title.normal "Would you like to start Private Browsing?">
+
+<!ENTITY privatebrowsingpage.status.private "&brandShortName; won't remember any history for this window.">
+<!ENTITY privatebrowsingpage.status.normal "You are not currently in a private window.">
+
+<!ENTITY privatebrowsingpage.common.description "In a Private Browsing window, &brandShortName; won't keep any browser history, search history, download history, web form history, cookies, or temporary internet files. However, created bookmarks and downloaded files will be kept.">
+<!ENTITY privatebrowsingpage.track.warn "While this computer won't have a record of your browsing history, your employer or Internet service provider might still be able to track the pages you visit.">
+<!ENTITY privatebrowsingpage.learnmore.label "Learn more">
+<!ENTITY privatebrowsingpage.learnmore.accesskey "L">
+
+<!ENTITY privatebrowsingpage.close.info "Once done, close the window to stop Private Browsing.">
+<!ENTITY privatebrowsingpage.close.label "Close this window now">
+<!ENTITY privatebrowsingpage.close.accesskey "C">
+
+<!ENTITY privatebrowsingpage.start.info "To start Private Browsing, click the button below or select File â–¶ New â–¶ Private Window from the menu.">
+<!ENTITY privatebrowsingpage.private.label "Open a new private window">
+<!ENTITY privatebrowsingpage.private.accesskey "O">
diff --git a/comm/suite/locales/en-US/chrome/common/aboutSessionRestore.dtd b/comm/suite/locales/en-US/chrome/common/aboutSessionRestore.dtd
new file mode 100644
index 0000000000..bca31b095a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/aboutSessionRestore.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY restorepage.tabtitle "Restore Session">
+<!ENTITY restorepage.pagetitle "Would you like to restore your session?">
+<!-- LOCALIZATION NOTE: If "closed unexpectedly" sounds too awkward in the translation,
+ you may translate "crash" instead (even though it's IT-speak) -->
+<!ENTITY restorepage.issueDesc "Your previous &brandShortName; session closed unexpectedly. We sincerely apologize for the inconvenience. You can restore the tabs and windows from your previous session, or start a new session if they are no longer needed.">
+<!ENTITY restorepage.remedies "If &brandShortName; closes repeatedly:">
+<!ENTITY restorepage.dueToChrome "Try disabling any recently added extensions in the Add-ons Manager.">
+<!ENTITY restorepage.dueToContent "Try restoring your session without any Web pages you suspect might be causing the problem:">
+
+<!ENTITY restorepage.restoreButton "Restore Previous Session">
+<!ENTITY restorepage.restore.access "R">
+<!ENTITY restorepage.cancelButton "Start New Session">
+<!ENTITY restorepage.cancel.access "S">
+
+<!ENTITY restorepage.restoreHeader "Restore">
+<!ENTITY restorepage.listHeader "Windows and Tabs">
+<!-- LOCALIZATION NOTE: &#37;S will be replaced with a number. -->
+<!ENTITY restorepage.windowLabel "Window &#37;S">
diff --git a/comm/suite/locales/en-US/chrome/common/aboutSyncTabs.dtd b/comm/suite/locales/en-US/chrome/common/aboutSyncTabs.dtd
new file mode 100644
index 0000000000..abc8b9d60e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/aboutSyncTabs.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE (tabs.otherComputers.label): Keep this in sync with syncTabsMenu.label from navigator.dtd -->
+<!ENTITY tabs.otherComputers.label "Tabs From Other Computers">
+
+<!ENTITY tabs.searchText.label "Type here to find tabs…">
+
+<!-- LOCALIZATION NOTE (tabs.context.openTab.accesskey, tabs.context.openMultipleTabs.accesskey;
+ tabs.context.bookmarkSingleTab.accesskey, tabs.context.bookmarkMultipleTabs.accesskey):
+ Only one of each of these pairs will show at a time (based on selection), so reusing accesskey is ok. -->
+<!ENTITY tabs.context.openTab.label "Open This Tab">
+<!ENTITY tabs.context.openTab.accesskey "O">
+<!ENTITY tabs.context.openMultipleTabs.label "Open Selected Tabs">
+<!ENTITY tabs.context.openMultipleTabs.accesskey "O">
+<!ENTITY tabs.context.bookmarkSingleTab.label "Bookmark This Tab…">
+<!ENTITY tabs.context.bookmarkSingleTab.accesskey "B">
+<!ENTITY tabs.context.bookmarkMultipleTabs.label "Bookmark Selected Tabs…">
+<!ENTITY tabs.context.bookmarkMultipleTabs.accesskey "B">
+<!ENTITY tabs.context.refreshList.label "Refresh List">
+<!ENTITY tabs.context.refreshList.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/common/askViewZoom.dtd b/comm/suite/locales/en-US/chrome/common/askViewZoom.dtd
new file mode 100644
index 0000000000..eb54b669a3
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/askViewZoom.dtd
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+
+<!ENTITY askViewZoom.title "Zoom">
+<!ENTITY selectZoom.label "Select zoom (&#37;):">
diff --git a/comm/suite/locales/en-US/chrome/common/certError.dtd b/comm/suite/locales/en-US/chrome/common/certError.dtd
new file mode 100644
index 0000000000..22a47ea366
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/certError.dtd
@@ -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/. -->
+
+<!-- These strings are used by SeaMonkey's custom about:certerror page,
+a replacement for the standard security certificate errors produced
+by NSS/PSM via netError.xhtml. -->
+
+<!ENTITY certerror.pagetitle "Untrusted Connection">
+<!ENTITY certerror.longpagetitle "This Connection is Untrusted">
+
+<!-- Localization note (certerror.introPara1a) - The text content of the span
+tag will be replaced at runtime with the name of the server to which the user
+was trying to connect. -->
+<!ENTITY certerror.introPara1a "You have asked &brandShortName; to connect
+securely to <span class='hostname'/>, but we can't confirm that your connection
+is secure.">
+<!ENTITY certerror.introPara2 "Normally, when you try to connect securely,
+websites will present trusted identification to prove that you are
+going to the right place. However, this website's identity can't be verified.">
+
+<!ENTITY certerror.whatShouldIDo.heading "What Should I Do?">
+<!ENTITY certerror.whatShouldIDo.content "If you usually connect to
+this website without problems, this error could mean that someone is
+trying to impersonate the website, and you shouldn't continue.">
+<!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
+Strict Transport Security (HSTS) to specify that &brandShortName; only connect
+to it securely. As a result, it is not possible to add an exception for this
+certificate.">
+<!ENTITY certerror.getMeOutOfHere.label "This sounds bad, take me to my home page instead">
+
+<!ENTITY certerror.expert.heading "I Understand the Risks">
+<!ENTITY certerror.expert.content "If you understand what's going on, you
+can tell &brandShortName; to start trusting this website's identification.
+<b>Even if you trust the website, this error could mean that someone is
+tampering with your connection.</b>">
+<!ENTITY certerror.expert.contentPara2 "Don't add an exception unless
+you know there's a good reason why this website doesn't use trusted identification.">
+<!ENTITY certerror.addException.label "Add Exception…">
+
+<!ENTITY certerror.technical.heading "Technical Details">
+
+<!ENTITY dnsNotFound.pageTitle "Server Not Found">
+<!ENTITY malformedURI.pageTitle "Invalid URL">
diff --git a/comm/suite/locales/en-US/chrome/common/console/console.dtd b/comm/suite/locales/en-US/chrome/common/console/console.dtd
new file mode 100644
index 0000000000..f9320cacbc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/console/console.dtd
@@ -0,0 +1,47 @@
+<!-- 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/. -->
+
+<!ENTITY errorConsole.title "Error Console">
+
+<!ENTITY errFile.label "Source File:">
+<!ENTITY errLine.label "Line:">
+<!ENTITY errColumn.label "Column:">
+
+<!ENTITY all.label "All">
+<!ENTITY all.accesskey "A">
+<!ENTITY errors.label "Errors">
+<!ENTITY errors.accesskey "r">
+<!ENTITY warnings.label "Warnings">
+<!ENTITY warnings.accesskey "n">
+<!ENTITY messages.label "Messages">
+<!ENTITY messages.accesskey "M">
+<!ENTITY clear.label "Clear">
+<!ENTITY clear.accesskey "l">
+<!ENTITY codeEval.label "Code:">
+<!ENTITY codeEval.accesskey "o">
+<!ENTITY evaluate.label "Evaluate">
+<!ENTITY evaluate.accesskey "u">
+<!ENTITY filter2.label "Filter:">
+<!ENTITY filter2.accesskey "i">
+
+<!ENTITY copyCmd.label "Copy">
+<!ENTITY copyCmd.accesskey "C">
+<!ENTITY copyCmd.commandkey "C">
+<!ENTITY sortFirst.label "First > Last Sort Order">
+<!ENTITY sortFirst.accesskey "f">
+<!ENTITY sortLast.label "Last > First Sort Order">
+<!ENTITY sortLast.accesskey "l">
+<!ENTITY closeCmd.commandkey "w">
+<!ENTITY focus1.commandkey "l">
+<!ENTITY focus2.commandkey "d">
+
+<!ENTITY menuBar.tooltip "Menu Bar">
+<!ENTITY modeToolbar.tooltip "Mode Toolbar">
+<!ENTITY entryToolbar.tooltip "Javascript Entry Toolbar">
+<!ENTITY toolbarsCmd.label "Show/Hide">
+<!ENTITY toolbarsCmd.accesskey "w">
+<!ENTITY toolbarMode.label "Mode">
+<!ENTITY toolbarMode.accesskey "M">
+<!ENTITY toolbarEval.label "JavaScript Entry">
+<!ENTITY toolbarEval.accesskey "J">
diff --git a/comm/suite/locales/en-US/chrome/common/console/console.properties b/comm/suite/locales/en-US/chrome/common/console/console.properties
new file mode 100644
index 0000000000..a17a250b38
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/console/console.properties
@@ -0,0 +1,17 @@
+# 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/.
+
+typeError=Error:
+typeWarning=Warning:
+typeMessage=Message:
+errFile=Source File: %S
+errLine=Line: %S
+errLineCol=Line: %S, Column: %S
+errCode=Source Code:
+errTime=Timestamp: %S
+
+# LOCALIZATION NOTE (evaluationContextChanged): The message displayed when the
+# browser console's evaluation context (window against which input is evaluated)
+# changes.
+evaluationContextChanged=The console’s evaluation context changed, probably because the target window was closed or because you opened a main window from the browser console’s window.
diff --git a/comm/suite/locales/en-US/chrome/common/contentAreaCommands.dtd b/comm/suite/locales/en-US/chrome/common/contentAreaCommands.dtd
new file mode 100644
index 0000000000..a4c1c9bf16
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/contentAreaCommands.dtd
@@ -0,0 +1,166 @@
+<!-- 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/. -->
+
+<!-- Context Menu -->
+<!ENTITY openLinkCmdInTab.label "Open Link in New Tab">
+<!ENTITY openLinkCmdInTab.accesskey "T">
+<!ENTITY openLinkCmd.label "Open Link in New Window">
+<!ENTITY openLinkCmd.accesskey "W">
+<!ENTITY openLinkCmdInPrivateWindow.label "Open Link in Private Window">
+<!ENTITY openLinkCmdInPrivateWindow.accesskey "d">
+<!ENTITY openLinkInWindowCmd.label "Open">
+<!ENTITY openLinkInWindowCmd.accesskey "p">
+<!ENTITY openFrameCmd.label "Open Frame in New Window">
+<!ENTITY openFrameCmd.accesskey "W">
+<!ENTITY openFrameCmdInTab.label "Open Frame in New Tab">
+<!ENTITY openFrameCmdInTab.accesskey "T">
+<!ENTITY keywordfield.label "Add a Keyword for this Search…">
+<!ENTITY keywordfield.accesskey "K">
+<!ENTITY showOnlyThisFrameCmd.label "Show Only This Frame">
+<!ENTITY showOnlyThisFrameCmd.accesskey "S">
+<!ENTITY goBackCmd.label "Back">
+<!ENTITY goBackCmd.accesskey "B">
+<!ENTITY goForwardCmd.label "Forward">
+<!ENTITY goForwardCmd.accesskey "F">
+<!ENTITY goUpCmd.label "Up">
+<!ENTITY goUpCmd.accesskey "U">
+<!ENTITY reloadCmd.label "Reload">
+<!ENTITY reloadCmd.accesskey "R">
+<!ENTITY reloadCmd.commandkey "r">
+<!ENTITY stopCmd.label "Stop">
+<!ENTITY stopCmd.accesskey "S">
+<!ENTITY reloadFrameCmd.label "Reload Frame">
+<!ENTITY reloadFrameCmd.accesskey "R">
+<!ENTITY viewPartialSourceForSelectionCmd.label "View Selection Source">
+<!ENTITY viewPartialSourceForMathMLCmd.label "View MathML Source">
+<!ENTITY viewPartialSourceCmd.accesskey "u">
+<!ENTITY viewPageSourceCmd.label "View Page Source">
+<!ENTITY viewPageSourceCmd.accesskey "V">
+<!ENTITY viewFrameSourceCmd.label "View Frame Source">
+<!ENTITY viewFrameSourceCmd.accesskey "V">
+<!ENTITY viewPageInfoCmd.label "View Page Info">
+<!ENTITY viewPageInfoCmd.accesskey "I">
+<!ENTITY viewFrameInfoCmd.label "View Frame Info">
+<!ENTITY viewFrameInfoCmd.accesskey "I">
+<!ENTITY fitImageCmd.label "Fit Image to Window">
+<!ENTITY fitImageCmd.accesskey "F">
+<!ENTITY reloadImageCmd.label "Reload Image">
+<!ENTITY reloadImageCmd.accesskey "R">
+<!ENTITY viewImageCmd.label "View Image">
+<!ENTITY viewImageCmd.accesskey "I">
+<!ENTITY viewImageInfoCmd.label "View Image Info">
+<!ENTITY viewImageInfoCmd.accesskey "f">
+<!ENTITY viewVideoCmd.label "View Video">
+<!ENTITY viewVideoCmd.accesskey "i">
+<!ENTITY viewBGImageCmd.label "View Background Image">
+<!ENTITY viewBGImageCmd.accesskey "w">
+<!ENTITY setDesktopBackgroundCmd.label "Set Desktop Background…">
+<!ENTITY setDesktopBackgroundCmd.accesskey "a">
+<!ENTITY bookmarkPageCmd.label "Bookmark This Page…">
+<!ENTITY bookmarkPageCmd.accesskey "m">
+<!ENTITY bookmarkLinkCmd.label "Bookmark This Link…">
+<!ENTITY bookmarkLinkCmd.accesskey "B">
+<!ENTITY bookmarkFrameCmd.label "Bookmark This Frame…">
+<!ENTITY bookmarkFrameCmd.accesskey "m">
+<!ENTITY savePageAsCmd.label "Save Page As…">
+<!ENTITY savePageCmd.label "Save Page">
+<!ENTITY savePageCmd.accesskey "e">
+<!ENTITY savePageCmd.commandkey "s">
+<!ENTITY saveFrameAsCmd.label "Save Frame As…">
+<!ENTITY saveFrameCmd.label "Save Frame">
+<!ENTITY saveFrameCmd.accesskey "a">
+<!ENTITY printFrameCmd.label "Print Frame…">
+<!ENTITY printFrameCmd.accesskey "P">
+<!ENTITY saveLinkAsCmd.label "Save Link Target As…">
+<!ENTITY saveLinkCmd.label "Save Link Target">
+<!ENTITY saveLinkCmd.accesskey "S">
+<!ENTITY saveImageAsCmd.label "Save Image As…">
+<!ENTITY saveImageCmd.label "Save Image">
+<!ENTITY saveImageCmd.accesskey "v">
+<!ENTITY saveVideoCmd.label "Save Video As…">
+<!ENTITY saveVideoCmd.accesskey "v">
+<!ENTITY saveAudioCmd.label "Save Audio As…">
+<!ENTITY saveAudioCmd.accesskey "v">
+<!ENTITY copyLinkCmd.label "Copy Link Location">
+<!ENTITY copyLinkCmd.accesskey "L">
+<!ENTITY copyImageCmd.label "Copy Image">
+<!ENTITY copyImageCmd.accesskey "y">
+<!ENTITY copyVideoURLCmd.label "Copy Video Location">
+<!ENTITY copyVideoURLCmd.accesskey "L">
+<!ENTITY copyAudioURLCmd.label "Copy Audio Location">
+<!ENTITY copyAudioURLCmd.accesskey "L">
+<!ENTITY metadataCmd.label "Properties">
+<!ENTITY metadataCmd.accesskey "P">
+<!ENTITY copyEmailCmd.label "Copy Email Address">
+<!ENTITY copyEmailCmd.accesskey "s">
+<!ENTITY thisFrameMenu.label "This Frame">
+<!ENTITY thisFrameMenu.accesskey "h">
+<!ENTITY mediaPlay.label "Play">
+<!ENTITY mediaPlay.accesskey "P">
+<!ENTITY mediaPause.label "Pause">
+<!ENTITY mediaPause.accesskey "P">
+<!ENTITY mediaMute.label "Mute">
+<!ENTITY mediaMute.accesskey "M">
+<!ENTITY mediaUnmute.label "Unmute">
+<!ENTITY mediaUnmute.accesskey "m">
+<!ENTITY mediaPlaybackRate.label "Playback Speed">
+<!ENTITY mediaPlaybackRate.accesskey "b">
+<!ENTITY mediaPlaybackRate050.label "Slow Motion (½×)">
+<!ENTITY mediaPlaybackRate050.accesskey "S">
+<!ENTITY mediaPlaybackRate100.label "Normal Speed">
+<!ENTITY mediaPlaybackRate100.accesskey "N">
+<!ENTITY mediaPlaybackRate125.label "Fast Speed (1¼×)">
+<!ENTITY mediaPlaybackRate125.accesskey "F">
+<!ENTITY mediaPlaybackRate150.label "High Speed (1½×)">
+<!ENTITY mediaPlaybackRate150.accesskey "H">
+<!ENTITY mediaPlaybackRate200.label "Double Speed">
+<!ENTITY mediaPlaybackRate200.accesskey "D">
+<!ENTITY mediaLoop.label "Loop">
+<!ENTITY mediaLoop.accesskey "o">
+<!-- LOCALIZATION NOTE: The access keys for "Show Controls" and
+"Hide Controls" are the same because the two context-menu
+items are mutually exclusive. -->
+<!ENTITY mediaShowControls.label "Show Media Controls">
+<!ENTITY mediaShowControls.accesskey "a">
+<!ENTITY mediaHideControls.label "Hide Media Controls">
+<!ENTITY mediaHideControls.accesskey "a">
+<!ENTITY videoFullScreen.label "Full Screen">
+<!ENTITY videoFullScreen.accesskey "F">
+<!ENTITY videoSaveImage.label "Save Snapshot As…">
+<!ENTITY videoSaveImage.accesskey "S">
+<!ENTITY videoShowStats.label "Show Statistics">
+<!ENTITY videoShowStats.accesskey "t">
+<!ENTITY videoHideStats.label "Hide Statistics">
+<!ENTITY videoHideStats.accesskey "t">
+<!ENTITY search.accesskey "W">
+<!ENTITY bidiSwitchPageDirectionItem.label "Switch Page Direction">
+<!ENTITY bidiSwitchPageDirectionItem.accesskey "g">
+<!ENTITY bidiSwitchTextDirectionItem.label "Switch Text Direction">
+<!ENTITY bidiSwitchTextDirectionItem.accesskey "x">
+<!ENTITY spellAddToDictionary.label "Add to Dictionary">
+<!ENTITY spellAddToDictionary.accesskey "n">
+<!ENTITY spellUndoAddToDictionary.label "Undo Add to Dictionary">
+<!ENTITY spellUndoAddToDictionary.accesskey "i">
+<!ENTITY spellIgnoreWord.label "Ignore Word">
+<!ENTITY spellIgnoreWord.accesskey "W">
+<!ENTITY spellCheckEnable.label "Check Spelling">
+<!ENTITY spellCheckEnable.accesskey "S">
+<!ENTITY spellNoSuggestions.label "(No Spelling Suggestions)">
+<!ENTITY spellDictionaries.label "Languages">
+<!ENTITY spellDictionaries.accesskey "L">
+<!ENTITY spellAddDictionaries.label "Download More Dictionaries…">
+<!ENTITY spellAddDictionaries.accesskey "D">
+
+<!ENTITY fillLoginMenu.label "Fill Login">
+<!ENTITY fillLoginMenu.accesskey "F">
+<!ENTITY fillPasswordMenu.label "Fill Password">
+<!ENTITY fillPasswordMenu.accesskey "F">
+<!ENTITY fillUsernameMenu.label "Fill Username">
+<!ENTITY fillUsernameMenu.accesskey "F">
+<!ENTITY noLoginSuggestions.label "(No Login Suggestions)">
+<!ENTITY viewSavedLogins.label "View Saved Logins">
+
+<!-- Developer Tools -->
+<!ENTITY devtoolsInspect.label "Inspect Element">
+<!ENTITY devtoolsInspect.accesskey "n">
diff --git a/comm/suite/locales/en-US/chrome/common/contentAreaCommands.properties b/comm/suite/locales/en-US/chrome/common/contentAreaCommands.properties
new file mode 100644
index 0000000000..28b6fce6f0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/contentAreaCommands.properties
@@ -0,0 +1,15 @@
+# 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/.
+
+# context menu strings
+
+# LOCALIZATION NOTE (searchSelected): %1$S is the search engine,
+# %2$S is the selection string.
+searchSelected=Search %1$S for "%2$S"
+searchSelected.accesskey=e
+
+blockImage=Block Images from %S
+blockImage.accesskey=c
+unblockImage=Unblock Images from %S
+unblockImage.accesskey=c
diff --git a/comm/suite/locales/en-US/chrome/common/customizeToolbar.dtd b/comm/suite/locales/en-US/chrome/common/customizeToolbar.dtd
new file mode 100644
index 0000000000..7915d26470
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/customizeToolbar.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE: Strings below used to be in mozilla-central's
+ toolkit/locales/en-US/chrome/global/customizeToolbar.dtd -->
+<!ENTITY dialog.title "Customize Toolbar">
+<!ENTITY dialog.dimensions "width: 92ch; height: 36em;">
+<!ENTITY instructions.description "You can add or remove items by dragging to or from the toolbars.">
+<!ENTITY show.label "Show:">
+<!ENTITY iconsAndText.label "Icons and Text">
+<!ENTITY icons.label "Icons">
+<!ENTITY text.label "Text">
+<!ENTITY useSmallIcons.label "Use Small Icons">
+<!ENTITY restoreDefaultSet.label "Restore Default Set">
+<!ENTITY addNewToolbar.label "Add New Toolbar">
+<!ENTITY saveChanges.label "Done">
+<!ENTITY undoChanges.label "Undo Changes">
diff --git a/comm/suite/locales/en-US/chrome/common/customizeToolbar.properties b/comm/suite/locales/en-US/chrome/common/customizeToolbar.properties
new file mode 100644
index 0000000000..41509bf9b6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/customizeToolbar.properties
@@ -0,0 +1,13 @@
+# 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/.
+
+# LOCALIZATION NOTE: Strings below used to be in mozilla-central's
+# toolkit/locales/en-US/chrome/global/customizeToolbar.properties
+enterToolbarTitle=New Toolbar
+enterToolbarName=Enter a name for this toolbar:
+enterToolbarDup=There is already a toolbar with the name “%Sâ€. Please enter a different name.
+enterToolbarBlank=You must enter a name to create a new toolbar.
+separatorTitle=Separator
+springTitle=Flexible Space
+spacerTitle=Space
diff --git a/comm/suite/locales/en-US/chrome/common/dataman/dataman.dtd b/comm/suite/locales/en-US/chrome/common/dataman/dataman.dtd
new file mode 100644
index 0000000000..9d01dd1258
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/dataman/dataman.dtd
@@ -0,0 +1,156 @@
+<!-- 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/. -->
+
+<!ENTITY dataman.windowTitle "Data Manager">
+
+<!ENTITY selectAll.key "a">
+
+<!ENTITY select.all.label "All data types">
+<!ENTITY select.cookies.label "Cookies only">
+<!ENTITY select.permissions.label "Permissions only">
+<!ENTITY select.preferences.label "Preferences only">
+<!ENTITY select.passwords.label "Passwords only">
+<!ENTITY select.storage.label "Storage only">
+
+<!ENTITY domain.search.placeholder "Search Domains">
+<!ENTITY domain.search.key "f">
+
+<!ENTITY domain.tree.domain.label "Domain">
+
+<!ENTITY domain.ctx.forgetdomain.label "Forget About This Domain">
+<!ENTITY domain.ctx.forgetdomain.accesskey "F">
+<!ENTITY domain.ctx.forgetglobal.label "Forget Global Data">
+<!ENTITY domain.ctx.forgetglobal.accesskey "F">
+
+<!ENTITY data.search.key "k">
+
+<!ENTITY tab.cookies.label "Cookies">
+<!ENTITY tab.permissions.label "Permissions">
+<!ENTITY tab.preferences.label "Preferences">
+<!ENTITY tab.passwords.label "Passwords">
+<!ENTITY tab.storage.label "Storage">
+<!ENTITY tab.formdata.label "Form Data">
+<!ENTITY tab.forget.label "Forget">
+
+<!-- cookies -->
+<!ENTITY cookies.description "This domain has stored the following cookies on your computer:">
+
+<!ENTITY cookies.tree.host.label "Website">
+<!ENTITY cookies.tree.name.label "Cookie Name">
+<!ENTITY cookies.tree.expires.label "Expires">
+
+<!ENTITY cookies.infobox.label "Information about the selected Cookie">
+
+<!ENTITY cookies.info.name.label "Name:">
+<!ENTITY cookies.info.value.label "Content:">
+<!ENTITY cookies.info.host.label "Host:">
+<!ENTITY cookies.info.domain.label "Domain:">
+<!ENTITY cookies.info.path.label "Path:">
+<!ENTITY cookies.info.sendtype.label "Send For:">
+<!ENTITY cookies.info.expires.label "Expires:">
+
+<!ENTITY cookies.ctx.remove.label "Remove">
+<!ENTITY cookies.ctx.remove.accesskey "R">
+<!ENTITY cookies.ctx.selectAll.label "Select All">
+<!ENTITY cookies.ctx.selectAll.accesskey "A">
+
+<!ENTITY cookies.button.remove.label "Remove">
+<!ENTITY cookies.button.remove.accesskey "R">
+
+<!ENTITY cookies.blockOnRemove.label "When removing, block the listed websites from setting future cookies">
+<!ENTITY cookies.blockOnRemove.accesskey "W">
+
+<!-- permissions -->
+<!ENTITY perm.UseDefault "Use Default">
+<!ENTITY perm.AskAlways "Always ask">
+<!ENTITY perm.NeverSave "Never save">
+<!ENTITY perm.Allow "Allow">
+<!ENTITY perm.AllowSameDomain "Allow for Same Domain">
+<!ENTITY perm.AllowSession "Allow for Session">
+<!ENTITY perm.Block "Block">
+
+<!ENTITY perm.host.placeholder "Enter a host name">
+<!ENTITY perm.button.add.label "Add">
+<!ENTITY perm.button.add.accesskey "A">
+
+<!-- preferences -->
+<!ENTITY prefs.description "Content preferences are a way for &brandShortName; to save its settings, like zoom levels, specifically for a website.">
+
+<!ENTITY prefs.tree.host.label "Website">
+<!ENTITY prefs.tree.name.label "Preference Name">
+<!ENTITY prefs.tree.value.label "Value">
+
+<!ENTITY prefs.ctx.remove.label "Remove">
+<!ENTITY prefs.ctx.remove.accesskey "R">
+<!ENTITY prefs.ctx.selectAll.label "Select All">
+<!ENTITY prefs.ctx.selectAll.accesskey "A">
+
+<!ENTITY prefs.button.remove.label "Remove">
+<!ENTITY prefs.button.remove.accesskey "R">
+
+<!-- passwords -->
+<!ENTITY pwd.description "You have the following passwords stored for this domain:">
+
+<!ENTITY pwd.tree.host.label "Website">
+<!ENTITY pwd.tree.username.label "Username">
+<!ENTITY pwd.tree.password.label "Password">
+
+<!ENTITY pwd.ctx.remove.label "Remove">
+<!ENTITY pwd.ctx.remove.accesskey "R">
+<!ENTITY pwd.ctx.copyPasswordCmd.label "Copy Password">
+<!ENTITY pwd.ctx.copyPasswordCmd.accesskey "C">
+<!ENTITY pwd.ctx.selectAll.label "Select All">
+<!ENTITY pwd.ctx.selectAll.accesskey "A">
+
+<!ENTITY pwd.button.remove.label "Remove">
+<!ENTITY pwd.button.remove.accesskey "R">
+
+<!-- storage -->
+<!ENTITY storage.description "This domain uses the following web storages on your computer:">
+
+<!ENTITY storage.tree.host.label "Website">
+<!ENTITY storage.tree.type.label "Type">
+<!ENTITY storage.tree.size.label "Size">
+
+<!ENTITY storage.ctx.remove.label "Remove">
+<!ENTITY storage.ctx.remove.accesskey "R">
+<!ENTITY storage.ctx.selectAll.label "Select All">
+<!ENTITY storage.ctx.selectAll.accesskey "A">
+
+<!ENTITY storage.button.remove.label "Remove">
+<!ENTITY storage.button.remove.accesskey "R">
+
+<!-- form data -->
+<!ENTITY fdata.search.placeholder "Search Field Data">
+
+<!ENTITY fdata.tree.fieldname.label "Field Name">
+<!ENTITY fdata.tree.value.label "Entered Value">
+<!ENTITY fdata.tree.usecount.label "Use Count">
+<!ENTITY fdata.tree.firstused.label "First Used">
+<!ENTITY fdata.tree.lastused.label "Last Used">
+
+<!ENTITY fdata.ctx.remove.label "Remove">
+<!ENTITY fdata.ctx.remove.accesskey "R">
+<!ENTITY fdata.ctx.selectAll.label "Select All">
+<!ENTITY fdata.ctx.selectAll.accesskey "A">
+
+<!ENTITY fdata.button.remove.label "Remove">
+<!ENTITY fdata.button.remove.accesskey "R">
+
+<!-- forget -->
+<!ENTITY forget.cookies.label "Cookies">
+<!ENTITY forget.cookies.accesskey "C">
+<!ENTITY forget.permissions.label "Permissions">
+<!ENTITY forget.permissions.accesskey "P">
+<!ENTITY forget.preferences.label "Content Preferences">
+<!ENTITY forget.preferences.accesskey "o">
+<!ENTITY forget.passwords.label "Passwords">
+<!ENTITY forget.passwords.accesskey "a">
+<!ENTITY forget.storage.label "Storage">
+<!ENTITY forget.storage.accesskey "S">
+<!ENTITY forget.formdata.label "Form Data">
+<!ENTITY forget.formdata.accesskey "D">
+
+<!ENTITY forget.button.label "Forget This Data">
+<!ENTITY forget.button.accesskey "F">
diff --git a/comm/suite/locales/en-US/chrome/common/dataman/dataman.properties b/comm/suite/locales/en-US/chrome/common/dataman/dataman.properties
new file mode 100644
index 0000000000..0d39d9fc79
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/dataman/dataman.properties
@@ -0,0 +1,73 @@
+# 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/.
+
+# cookies
+cookies.expireAtEndOfSession=At end of session
+
+cookies.secureOnly.httponly=Encrypted connections only and no script access
+cookies.secureOnly.all=Encrypted connections only
+cookies.anyConnection.httponly=Any type of connection, no script access
+cookies.anyConnection.all=Any type of connection
+
+cookies.deleteSelected=Are you sure you want to delete the selected cookies?
+cookies.deleteSelectedTitle=Remove Selected Cookies
+cookies.deleteSelectedYes=&Remove
+
+# permissions
+perm.allowXULXBL.label=Use XUL/XBL Markup
+perm.cookie.label=Set Cookies
+perm.geo.label=Share Location
+perm.image.label=Load Images
+perm.indexedDB.label=Store Local Databases
+perm.install.label=Install Add-ons
+perm.offline-app.label=Offline Web Applications
+perm.object.label=Run Plugins
+perm.login-saving.label=Save Passwords
+perm.plugins.label=Activate Plugins
+perm.popup.label=Open Popup Windows
+perm.script.label=Run Scripts
+perm.stylesheet.label=Load Stylesheets
+perm.trackingprotection.label=Tracking Activity
+
+perm.type.default=Select a type
+perm.validation.invalidurl=The url entered is not valid
+
+# passwords
+pwd.hidePasswords=Hide Passwords
+pwd.hidePasswords.accesskey=P
+pwd.showPasswords=Show Passwords
+pwd.showPasswords.accesskey=P
+pwd.noMasterPasswordPrompt=Are you sure you wish to show your passwords?
+
+pwd.deleteSelected=Are you sure you want to delete the selected passwords?
+pwd.deleteSelectedTitle=Remove Selected Passwords
+pwd.deleteSelectedYes=&Remove
+
+# preferences
+prefs.deleteSelected=Are you sure you want to delete the selected preferences?
+prefs.deleteSelectedTitle=Remove Selected Preferences
+prefs.deleteSelectedYes=&Remove
+
+# storage
+
+# LOCALIZATION NOTE: The next string is for disk usage of any storage
+# e.g. storageUsage: "50.23 MB"
+# %1$S = size (in bytes or megabytes, ...)
+# %2$S = unit of measure (bytes, KB, MB, ...)
+storageUsage=%1$S %2$S
+
+storage.deleteSelected=Are you sure you want to delete the selected web storages?
+storage.deleteSelectedTitle=Remove Selected Storages
+storage.deleteSelectedYes=&Remove
+
+# form data
+fdata.deleteSelected=Are you sure you want to delete the selected form history entries?
+fdata.deleteSelectedTitle=Remove Selected Form Data
+fdata.deleteSelectedYes=&Remove
+
+# forget
+forget.desc.domain.pre=Forget about all data of the following types associated with the domain "%S":
+forget.desc.domain.post=All data associated with the domain "%S" of the following types has been deleted:
+forget.desc.global.pre=Forget about all global data of the following types:
+forget.desc.global.post=All global data of the following types has been deleted:
diff --git a/comm/suite/locales/en-US/chrome/common/datetimepicker.dtd b/comm/suite/locales/en-US/chrome/common/datetimepicker.dtd
new file mode 100644
index 0000000000..b010c48313
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/datetimepicker.dtd
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+
+<!-- first day of week to display in datepicker, a value from 0 to 6,
+ 0 = Sunday, 1 = Monday, etc. -->
+<!ENTITY firstdayofweek.default "0">
diff --git a/comm/suite/locales/en-US/chrome/common/defaultClientDialog.dtd b/comm/suite/locales/en-US/chrome/common/defaultClientDialog.dtd
new file mode 100644
index 0000000000..fb4bac7340
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/defaultClientDialog.dtd
@@ -0,0 +1,14 @@
+<!-- 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/. -->
+
+<!ENTITY defaultClient.title "Default Client">
+<!ENTITY defaultClient.intro "Use &brandShortName; as the default client for:">
+
+<!ENTITY browser.label "Browser">
+<!ENTITY email.label "E-Mail">
+<!ENTITY newsgroups.label "Newsgroups">
+<!ENTITY feeds.label "Feeds">
+
+<!ENTITY checkOnStartup.label "Always perform this check when starting &brandShortName;">
+<!ENTITY checkOnStartup.accesskey "A">
diff --git a/comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.dtd b/comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.dtd
new file mode 100644
index 0000000000..f285278f0b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.dtd
@@ -0,0 +1,95 @@
+<!-- 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/. -->
+
+<!ENTITY downloadManager.title "Download Manager">
+
+<!ENTITY menuBar.tooltip "Menu Bar">
+<!ENTITY searchBar.tooltip "Search Bar">
+
+<!ENTITY search.placeholder "Search Downloads">
+<!ENTITY search.label "Search Downloads">
+<!ENTITY search.accesskey "S">
+<!ENTITY search.key "f">
+
+<!ENTITY cmd.clearList.label "Clear List">
+<!ENTITY cmd.clearList.tooltip "Removes completed, canceled, and failed downloads from the list">
+<!ENTITY cmd.clearList.accesskey "C">
+
+<!ENTITY col.name.label "Name">
+<!ENTITY col.name.accesskey "N">
+<!ENTITY col.name.tooltip "File Name">
+<!ENTITY col.status.label "Status">
+<!ENTITY col.status.accesskey "S">
+<!ENTITY col.status.tooltip "Status">
+<!ENTITY col.actionPlay.label "Pause/Resume/Retry">
+<!ENTITY col.actionPlay.accesskey "u">
+<!ENTITY col.actionPlay.tooltip "Pause/Resume/Retry">
+<!ENTITY col.actionStop.label "Cancel/Remove">
+<!ENTITY col.actionStop.accesskey "C">
+<!ENTITY col.actionStop.tooltip "Cancel/Remove">
+<!ENTITY col.progress.label "Progress">
+<!ENTITY col.progress.accesskey "P">
+<!ENTITY col.progress.tooltip "Progress">
+<!ENTITY col.timeremaining.label "Time Left">
+<!ENTITY col.timeremaining.accesskey "L">
+<!ENTITY col.timeremaining.tooltip "Time Left">
+<!ENTITY col.transferred.label "Transferred">
+<!ENTITY col.transferred.accesskey "T">
+<!ENTITY col.transferred.tooltip "Transferred">
+<!ENTITY col.transferrate.label "Speed">
+<!ENTITY col.transferrate.accesskey "d">
+<!ENTITY col.transferrate.tooltip "Speed">
+<!ENTITY col.timeelapsed.label "Time Elapsed">
+<!ENTITY col.timeelapsed.accesskey "E">
+<!ENTITY col.timeelapsed.tooltip "Time Elapsed">
+<!ENTITY col.starttime.label "Start Time">
+<!ENTITY col.starttime.accesskey "a">
+<!ENTITY col.starttime.tooltip "Start Time">
+<!ENTITY col.endtime.label "End Time">
+<!ENTITY col.endtime.accesskey "i">
+<!ENTITY col.endtime.tooltip "End Time">
+<!ENTITY col.progresstext.label "&#37;">
+<!ENTITY col.progresstext.accesskey "&#37;">
+<!ENTITY col.progresstext.tooltip "Progress (&#37;)">
+<!ENTITY col.source.label "Source">
+<!ENTITY col.source.accesskey "o">
+<!ENTITY col.source.tooltip "Source">
+
+<!ENTITY view.columns.label "Show Columns">
+<!ENTITY view.columns.accesskey "C">
+<!ENTITY view.sortBy.label "Sort by">
+<!ENTITY view.sortBy.accesskey "S">
+
+<!ENTITY view.unsorted.label "Unsorted">
+<!ENTITY view.unsorted.accesskey "U">
+<!ENTITY view.sortAscending.label "A > Z Sort Order">
+<!ENTITY view.sortAscending.accesskey "A">
+<!ENTITY view.sortDescending.label "Z > A Sort Order">
+<!ENTITY view.sortDescending.accesskey "Z">
+
+<!ENTITY cmd.pause.label "Pause">
+<!ENTITY cmd.pause.accesskey "P">
+<!ENTITY cmd.resume.label "Resume">
+<!ENTITY cmd.resume.accesskey "R">
+<!ENTITY cmd.retry.label "Retry">
+<!ENTITY cmd.retry.accesskey "t">
+<!ENTITY cmd.cancel.label "Cancel">
+<!ENTITY cmd.cancel.accesskey "C">
+<!ENTITY cmd.remove.label "Remove From List">
+<!ENTITY cmd.remove.accesskey "e">
+<!ENTITY cmd.open.label "Open">
+<!ENTITY cmd.open.accesskey "O">
+<!ENTITY cmd.show.label "Open Containing Folder">
+<!ENTITY cmd.show.accesskey "F">
+<!ENTITY cmd.goToDownloadPage.label "Go to Download Page">
+<!ENTITY cmd.goToDownloadPage.accesskey "G">
+<!ENTITY cmd.copyDownloadLink.label "Copy Download Link">
+<!ENTITY cmd.copyDownloadLink.accesskey "L">
+<!ENTITY cmd.properties.label "Properties…">
+<!ENTITY cmd.properties.accesskey "s">
+<!-- LOCALIZATION NOTE (cmd.unblock2.label):
+ This command is shown in the context menu when downloads are blocked.
+ -->
+<!ENTITY cmd.unblock2.label "Allow Download">
+<!ENTITY cmd.unblock2.accesskey "l">
diff --git a/comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.properties b/comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.properties
new file mode 100644
index 0000000000..a62677e13d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/downloads/downloadmanager.properties
@@ -0,0 +1,146 @@
+# 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/.
+
+# LOCALIZATION NOTE (stateUnknown):
+# Indicates that the download stat is unknown.
+# You should never see this in the ui.
+stateUnknown=Unknown
+# LOCALIZATION NOTE (stateDownloading):
+# Indicates that the download is in progress.
+stateDownloading=Downloading
+# LOCALIZATION NOTE (stateUploading):
+# Indicates that the upload is in progress.
+stateUploading=Uploading
+# LOCALIZATION NOTE (stateStarting):
+# Indicates that the download is starting.
+# You won't probably ever see this in the ui.
+stateStarting=Starting…
+# LOCALIZATION NOTE (stateNotStarted):
+# Indicates that the download has not started yet.
+# You won't probably ever see this in the ui.
+stateNotStarted=Not Started
+# LOCALIZATION NOTE (stateScanning):
+# Indicates that an external program is scanning the download for viruses.
+stateScanning=Scanning for viruses…
+# LOCALIZATION NOTE (stateFailed):
+# Indicates that the download failed because of an error.
+stateFailed=Failed
+# LOCALIZATION NOTE (statePaused):
+# Indicates that the download was paused by the user.
+statePaused=Paused
+# LOCALIZATION NOTE (stateCanceled):
+# Indicates that the download was canceled by the user.
+stateCanceled=Canceled
+# LOCALIZATION NOTE (stateCompleted):
+# Indicates that the download was completed.
+stateCompleted=Finished
+# LOCALIZATION NOTE (stateBlockedParentalControls):
+# Indicates that the download was blocked by the Parental Controls feature of
+# Windows. "Parental Controls" should be consistently named and capitalized
+# with the display of this feature in Windows. The following article can
+# provide a reference for the translation of "Parental Controls" in various
+# languages:
+# http://windows.microsoft.com/en-US/windows-vista/Set-up-Parental-Controls
+stateBlockedParentalControls=Blocked by Parental Controls
+# LOCALIZATION NOTE (stateBlockedPolicy):
+# Indicates that the download was blocked on Windows because of the "Launching
+# applications and unsafe files" setting of the "security zone" associated with
+# the target site. "Security zone" should be consistently named and capitalized
+# with the display of this feature in Windows. The following article can
+# provide a reference for the translation of "security zone" in various
+# languages:
+# http://support.microsoft.com/kb/174360
+stateBlockedPolicy=Blocked by your security zone policy
+# LOCALIZATION NOTE (stateDirty):
+# Indicates that the download was blocked after scanning.
+stateDirty=Blocked: May contain a virus or spyware
+
+# LOCALIZATION NOTE (blockedMalware, blockedPotentiallyUnwanted,
+# blockedUncommon2):
+# These strings are shown in the panel for some types of blocked downloads, and
+# are immediately followed by the "Learn More" link, thus they must end with a
+# period. You may need to adjust "downloadDetails.width" in "downloads.dtd" if
+# this turns out to be longer than the other existing status strings.
+# Note: These strings don't exist in the UI yet. See bug 1053890.
+blockedMalware=This file contains a virus or malware.
+blockedPotentiallyUnwanted=This file may harm your computer.
+blockedUncommon2=This file is not commonly downloaded.
+
+# LOCALIZATION NOTE (unblockHeaderUnblock, unblockHeaderOpen,
+# unblockTypeMalware, unblockTypePotentiallyUnwanted2,
+# unblockTypeUncommon2, unblockTip2, unblockButtonOpen,
+# unblockButtonUnblock, unblockButtonConfirmBlock):
+# These strings are displayed in the dialog shown when the user asks a blocked
+# download to be unblocked. The severity of the threat is expressed in
+# descending order by the unblockType strings, it is higher for files detected
+# as malware and lower for uncommon downloads.
+unblockHeaderUnblock=Are you sure you want to allow this download?
+unblockHeaderOpen=Are you sure you want to open this file?
+unblockTypeMalware=This file contains a virus or other malware that will harm your computer.
+unblockTypePotentiallyUnwanted2=This file is disguised as a helpful download, but it can make unexpected changes to your programs and settings.
+unblockTypeUncommon2=This file is not commonly downloaded and may not be safe to open. It may contain a virus or make unexpected changes to your programs and settings.
+unblockTip2=You can search for an alternate download source or try again later.
+unblockButtonOpen=Open
+unblockButtonUnblock=Allow download
+unblockButtonConfirmBlock=Remove file
+
+fileExecutableSecurityWarning=“%S†is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch “%S�
+fileExecutableSecurityWarningTitle=Open Executable File?
+fileExecutableSecurityWarningDontAsk=Don’t ask me this again
+
+# LOCALIZATION NOTE (otherDownloads3):
+# This is displayed in an item at the bottom of the Downloads Panel when
+# there are more downloads than can fit in the list in the panel. Use a
+# semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/Localization_and_Plurals
+otherDownloads3=%1$S file downloading;%1$S files downloading
+
+# LOCALIZATION NOTE (downloadsTitleFiles, downloadsTitlePercent): Semi-colon list of
+# plural forms. See: http://developer.mozilla.org/en/Localization_and_Plurals
+# %1$S number of files; %2$S overall download percent (only for downloadsTitlePercent)
+# %% will appear as a single % sign, so %2$S%% is the percent number plus the % sign
+# examples: 2% of 1 file - Download Manager; 22% of 11 files - Download Manager
+downloadsTitleFiles=%1$S file - Download Manager;%1$S files - Download Manager
+downloadsTitlePercent=%2$S%% of %1$S file - Download Manager;%2$S%% of %1$S files - Download Manager
+
+# LOCALIZATION NOTE (progressTitle):
+# %1$S is the file name, %2$S is the download state
+# examples: coolvideo.ogg - Finished; seamonkey-nightly.zip - Paused
+progressTitle=%1$S - %2$S
+# LOCALIZATION NOTE (progressTitlePercent):
+# %1$S is download percent, %2$S is the file name, %3$S is the download state
+# %% will appear as a single % sign, so %1$S%% is the percent number plus the % sign
+# examples: 42% of coolvideo.ogg - Paused; 98% of seamonkey-nightly.zip - Downloading
+progressTitlePercent=%1$S%% of %2$S - %3$S
+
+# LOCALIZATION NOTE (percentFormat): %1$S is download percent
+# %% will appear as a single % sign, so %1$S%% is the percent number plus the % sign
+percentFormat=%1$S%%
+
+# LOCALIZATION NOTE (speedFormat):
+# %1$S rate number; %2$S rate unit
+# units are taken from toolkit's downloads.properties
+# example: 2.2 MB/sec
+speedFormat=%1$S %2$S/sec
+
+# LOCALIZATION NOTE (timeSingle): %1$S time number; %2$S time unit
+# example: 1 minute; 11 hours
+timeSingle=%1$S %2$S
+# LOCALIZATION NOTE (timeDouble):
+# %1$S time number; %2$S time unit; %3$S time sub number; %4$S time sub unit
+# example: 11 hours, 2 minutes; 1 day, 22 hours
+timeDouble=%1$S %2$S, %3$S %4$S
+
+# LOCALIZATION NOTE (sizeSpeed):
+# %1$S is transfer progress; %2$S download speed
+# example: 1.1 of 11.1 GB (2.2 MB/sec)
+sizeSpeed=%1$S (%2$S)
+
+# LOCALIZATION NOTE (statusActive): — is the "em dash" (long dash)
+# %1$S download status; %2$S time remaining
+# example: Paused — 11 hours, 2 minutes remaining
+statusActive=%1$S — %2$S
+
+fromSource=From %S
+toTarget=To %S
diff --git a/comm/suite/locales/en-US/chrome/common/downloads/progressDialog.dtd b/comm/suite/locales/en-US/chrome/common/downloads/progressDialog.dtd
new file mode 100644
index 0000000000..733faf7360
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/downloads/progressDialog.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!ENTITY progress.title "Download in Progress…">
+<!ENTITY closeWindow.key "w">
+<!ENTITY cmd.pause.tooltip "Pause">
+<!ENTITY cmd.resume.tooltip "Resume">
+<!ENTITY cmd.retry.tooltip "Retry">
+<!ENTITY cmd.cancel.tooltip "Cancel">
+<!ENTITY cmd.open.label "Open">
+<!ENTITY cmd.open.accesskey "O">
+<!ENTITY cmd.show.label "Open Containing Folder">
+<!ENTITY cmd.show.accesskey "F">
+<!ENTITY cmd.goToDownloadPage.label "Go to Download Page">
+<!ENTITY cmd.goToDownloadPage.accesskey "G">
+<!ENTITY cmd.copyDownloadLink.label "Copy Download Link">
+<!ENTITY cmd.copyDownloadLink.accesskey "L">
+<!ENTITY closeWhenDone.label "Close this window when the download is complete.">
+<!ENTITY closeWhenDone.accesskey "w">
diff --git a/comm/suite/locales/en-US/chrome/common/feeds/subscribe.dtd b/comm/suite/locales/en-US/chrome/common/feeds/subscribe.dtd
new file mode 100644
index 0000000000..06e59f3bd5
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/feeds/subscribe.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY feedPage.title "Viewing Feed">
+<!ENTITY feedSubscribeNow "Subscribe Now">
+<!ENTITY feedMessenger "News &amp; Blogs">
+<!ENTITY feedLiveBookmarks "Live Bookmarks">
diff --git a/comm/suite/locales/en-US/chrome/common/feeds/subscribe.properties b/comm/suite/locales/en-US/chrome/common/feeds/subscribe.properties
new file mode 100644
index 0000000000..e63890b493
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/feeds/subscribe.properties
@@ -0,0 +1,52 @@
+# 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/.
+
+linkTitleTextFormat=Go to %S
+addHandler=Add "%S" (%S) as a Feed Reader?
+addHandlerAddButton=Add Feed Reader
+addHandlerAddButtonAccesskey=A
+handlerRegistered="%S" is already registered as a Feed Reader
+subscribeNow=Subscribe Now
+chooseApplicationMenuItem=Choose Application…
+chooseApplicationDialogTitle=Choose Application
+alwaysUse=Always use %S to subscribe to feeds
+mediaLabel=Media files
+
+# LOCALIZATION NOTE: The next string is for the size of the enclosed media.
+# e.g. enclosureSizeText : "50.23 MB"
+# %1$S = size (in bytes or megabytes, ...)
+# %2$S = unit of measure (bytes, KB, MB, ...)
+enclosureSizeText=%1$S %2$S
+
+bytes=bytes
+kilobytes=KB
+megabytes=MB
+gigabytes=GB
+
+# LOCALIZATION NOTE: The next three strings explains to the user what they're
+# doing.
+# e.g. alwaysUseForVideoPodcasts : "Always use Miro to subscribe to video podcasts."
+# %S = application to use (Miro, iTunes, ...)
+alwaysUseForFeeds=Always use %S to subscribe to feeds.
+alwaysUseForAudioPodcasts=Always use %S to subscribe to podcasts.
+alwaysUseForVideoPodcasts=Always use %S to subscribe to video podcasts.
+
+subscribeFeedUsing=Subscribe to this feed using
+subscribeAudioPodcastUsing=Subscribe to this podcast using
+subscribeVideoPodcastUsing=Subscribe to this video podcast using
+
+feedSubscriptionFeed1=This is a "feed" of frequently changing content on this website.
+feedSubscriptionAudioPodcast1=This is a "podcast" of frequently changing content on this website.
+feedSubscriptionVideoPodcast1=This is a "video podcast" of frequently changing content on this website.
+
+feedSubscriptionFeed2=You can subscribe to this feed to receive updates when this content changes.
+feedSubscriptionAudioPodcast2=You can subscribe to this podcast to receive updates when this content changes.
+feedSubscriptionVideoPodcast2=You can subscribe to this video podcast to receive updates when this content changes.
+
+# Protocol Handling
+# "Add %appName (%appDomain) as an application for %protocolType links?"
+addProtocolHandler=Add %S (%S) as an application for %S links?
+addProtocolHandlerAddButton=Add Application
+# "%appName has already been added as an application for %protocolType links."
+protocolHandlerRegistered=%S has already been added as an application for %S links.
diff --git a/comm/suite/locales/en-US/chrome/common/gopherAddon.dtd b/comm/suite/locales/en-US/chrome/common/gopherAddon.dtd
new file mode 100644
index 0000000000..516660876a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/gopherAddon.dtd
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY loadError.label "Page Load Error">
+<!ENTITY gopherAddon.title "Gopher Protocol">
+<!ENTITY gopherAddon.shortDesc "gopher is not a registered protocol.">
+<!ENTITY gopherAddon.longDesc "The address specifies the gopher protocol which is no longer supported, so the browser cannot connect to the site. You can get a compatible add-on to access this server from Mozilla Add-ons.">
+<!ENTITY goToAddOn.label "Go to Add-on Page">
diff --git a/comm/suite/locales/en-US/chrome/common/help/cert_dialog_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/cert_dialog_help.xhtml
new file mode 100644
index 0000000000..a472473f59
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/cert_dialog_help.xhtml
@@ -0,0 +1,491 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Certificate Information and Decisions</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<div class="boilerPlate">This document is provided for your information only.
+ It may help you take certain steps to protect the privacy and security of
+ your personal information on the Internet. This document does not, however,
+ address all online privacy and security issues, nor does it represent a
+ recommendation about what constitutes adequate privacy and security
+ protection on the Internet.</div>
+
+<h1 id="certificate_information_and_decisions">Certificate Information and
+ Decisions</h1>
+
+<p>This section describes how to use various windows displayed at different times by
+ Certificate Manager. The additional information given here appears when you click
+ the Help button in one of those windows.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#certificate_viewer">Certificate Viewer</a></li>
+ <li><a href="#choose_security_device">Choose Security Device</a></li>
+ <li><a href="#encryption_key_copy">Encryption Key Copy</a></li>
+ <li><a href="#certificate_backup">Certificate Backup</a></li>
+ <li><a href="#user_identification_request">User Identification Request</a></li>
+ <li><a href="#new_certificate_authority">New Certificate Authority</a></li>
+ <li><a href="#website_certificates">Website Certificates</a></li>
+ </ul>
+</div>
+
+<h2 id="certificate_viewer">Certificate Viewer</h2>
+
+<p>The Certificate Viewer displays information about a certificate you selected
+ in one of the Certificate Manager tabs. The General tab summarizes
+ information about who issued the certificate, its verification status, what
+ the certificate can be used for, and so on. The Details tab provides complete
+ details on the certificate&apos;s contents.</p>
+
+<p>If you are not currently viewing the Certificate Viewer, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Click Manage Certificates.</li>
+ <li>Click the tab for the type of certificate whose details you want to
+ view.</li>
+ <li>Select the certificate whose details you want to view.</li>
+ <li>Click View.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#general_tab">General Tab</a></li>
+ <li><a href="#details_tab">Details Tab</a></li>
+ </ul>
+</div>
+
+<h3 id="general_tab">General Tab</h3>
+
+<p>When you first open the Certificate Viewer, the General tab displays several
+ kinds of information about the selected certificate:</p>
+
+<ul>
+ <li><strong>This certificate has been verified for the following
+ uses</strong>: See
+ <a href="glossary.xhtml#certificate_verification">certificate verification</a>
+ for a discussion of how the Certificate Manager verifies certificates. Uses
+ can include any of the following:
+ <ul>
+ <li><strong>SSL Client Certificate</strong>: Certificate used to identify
+ you to websites.</li>
+ <li><strong>SSL Server Certificate</strong>: Certificate used to identify
+ a website server to browsers.</li>
+ <li><strong>Email Signer Certificate</strong>: Certificate used to
+ identify you for the purposes of digitally signing email messages.</li>
+ <li><strong>Email Recipient Certificate</strong>: Certificate used to
+ identify someone else, for example so you can send that person
+ encrypted email.</li>
+ <li><strong>Status Responder Certificate</strong>: Certificate used to
+ identify an online status responder that uses the Online Certificate
+ Status Protocol (OCSP) to check the validity of certificates. For more
+ information about OCSP, see
+ <a href="certs_prefs_help.xhtml">Certificates Settings</a>.</li>
+ <li><strong>SSL Certificate Authority</strong>: Certificate used to
+ identify a certificate authority&mdash;that is, a service that issues
+ certificates for use as identification over computer networks.</li>
+ </ul>
+ </li>
+ <li><strong>Issued To</strong>: Summarizes the following information about
+ the certificate:
+ <ul>
+ <li><strong>Common Name</strong>: The name of the person or other entity
+ that the certificate identifies.</li>
+ <li><strong>Organization</strong>: The name of the organization to which
+ the entity belongs (such as the name of a company).</li>
+ <li><strong>Organizational Unit</strong>: The name of the organizational
+ unit to which the entity belongs (such as Accounting Department).</li>
+ <li><strong>Serial Number</strong>: The certificate&apos;s serial
+ number.</li>
+ </ul>
+ </li>
+ <li><strong>Issued By</strong>: Summarizes information (similar to that
+ provided under <q>Issued To</q>; see above) about the certificate authority
+ (CA) that issued the certificate.</li>
+ <li><strong>Validity</strong>: Indicates the period during which the
+ certificate is valid.</li>
+ <li><strong>Fingerprints</strong>: Lists the certificate&apos;s fingerprints.
+ A fingerprint is a unique number produced by applying a mathematical
+ function to the certificate contents. A certificate&apos;s fingerprint can
+ be used to verify that the certificate has not been tampered with.</li>
+</ul>
+
+<h3 id="details_tab">Details Tab</h3>
+
+<p>Click the Details tab at the top of the Certificate Viewer to see more
+ detailed information about the selected certificate. To examine information
+ for any certificate in the Certificate Hierarchy area, select its name,
+ select the field under Certificate Fields that you want to examine, and
+ read the field&apos;s value under Field Value:</p>
+
+<ul>
+ <li><strong>Certificate Hierarchy</strong>: Displays the certificate chain,
+ with the certificate you originally selected at the bottom. A certificate
+ chain is a hierarchical series of certificates signed by successive
+ certificate authorities (CAs). A CA certificate identifies a
+ <a href="glossary.xhtml#certificate_authority">certificate authority</a>
+ and is used to sign certificates issued by that authority. A CA certificate
+ can in turn be signed by the CA certificate of a parent CA and so on up to
+ a <a href="glossary.xhtml#root_ca">root CA</a>.</li>
+ <li><strong>Certificate Fields</strong>: Displays the fields of the
+ certificate selected under Certificate Hierarchy.</li>
+ <li><strong>Field Value</strong>: Displays the value of the field selected
+ under Certificate Fields.</li>
+</ul>
+
+<p>The Certificate Viewer displays basic ANSI types in human-readable form
+ wherever possible. For fields whose contents the Certificate Manager cannot
+ interpret, it displays the actual values contained in the certificate.</p>
+
+<h2 id="choose_security_device">Choose Security Device</h2>
+
+<p>A security device (sometimes called a token) is a hardware or software
+ device that provides cryptographic services such as encryption and decryption
+ and stores certificates and keys. The Choose Security Device window appears
+ when Certificate Manager needs help deciding which security device to use
+ when importing a certificate or performing a cryptographic operation, such as
+ generating keys for a new certificate. This window allows you to select one
+ of two or more security devices that Certificate Manager has detected on your
+ machine.</p>
+
+<p>A smart card is one example of a security device. For example, if a smart
+ card reader connected to your computer has a smart card inserted in it, the
+ name of the smart card will show up in the drop-down menu. In this case, you
+ must choose the name of the smart card from the menu to let Certificate
+ Manager know that you want to use it.</p>
+
+<p>The Certificate Manager also supplies its own default, built-in security
+ device, which can always be used no matter what additional devices are or
+ aren&apos;t available.</p>
+
+<h2 id="encryption_key_copy">Encryption Key Copy</h2>
+
+<p><a href="glossary.xhtml#certificate_authority">Certificate authorities (CAs)</a>
+ that issue separate signing and encryption email certificates typically make
+ backup copies of your private
+ <a href="glossary.xhtml#encryption_key">encryption key</a> during the
+ certificate enrollment process.</p>
+
+<p>The Encryption Key Copy dialog box allows you to approve the creation of
+ such a backup or cancel the certificate request. A CA that has archived a
+ backup copy of your encryption key has the potential capability of
+ decrypting any messages you receive that were encrypted with your
+ corresponding public key.</p>
+
+<p>You can take these actions from the Encryption Key Copy dialog box:</p>
+
+<ul>
+ <li><strong>View Certificate</strong>: To view the certificate identifying
+ the CA that is requesting the backup copy, click View Certificate.</li>
+ <li><strong>OK</strong>: If you trust the CA identified by the CA certificate
+ to decrypt encrypted messages that you receive, click OK.
+
+ <p>If you are not sure whether to trust the CA that is requesting the
+ backup copy, talk to your system administrator.</p>
+ </li>
+ <li><strong>Cancel</strong>: If you don&apos;t trust the CA that is
+ requesting the backup copy, don&apos;t request a certificate from it. Click
+ Cancel to stop both the backup procedure and the request for a
+ certificate.</li>
+</ul>
+
+<p>After your CA makes a backup copy of the encryption key, you will be able to
+ use that key to access your encrypted mail even if you lose your password or
+ lose your own copy of the key. If no backup copy of your encryption key
+ exists and you lose your password or the key, you will have no way of reading
+ email messages that were encrypted with that key.</p>
+
+<h2 id="certificate_backup">Certificate Backup</h2>
+
+<p>When you receive a certificate, make a backup copy of the certificate and
+ its private key, then store the copy in a safe place. For example, you can
+ put the copy on a floppy disk and store it with other valuable items under
+ lock and key. That way, even if you have hard disk or file corruption
+ problems, you can easily restore the certificate.</p>
+
+<p>It can be inconvenient, at best, and in some situations catastrophic to lose
+ your certificate and its associated private key, depending on what you use it
+ for. For example:</p>
+
+<ul>
+ <li>If you lose a certificate that identifies you to important websites, you
+ will not be able to access those websites until you obtain a new
+ certificate. </li>
+ <li>If you lose a certificate used to encrypt email messages, you will not
+ be able to read any of your encrypted email&mdash;including both encrypted
+ messages that you have sent and encrypted messages that you have received.
+ In this case, if you cannot obtain a backup of the private encryption key
+ associated with the certificate, you will never be able to read any of the
+ messages encrypted with that key.</li>
+</ul>
+
+<p>Like any other valuable data, certificates should be backed up to avoid
+ future trouble and expense. Do it now so you don&apos;t forget.</p>
+
+<h2 id="user_identification_request">User Identification Request</h2>
+
+<p>Some websites require that you identify yourself with a certificate rather
+ than a name and password, because certificates provide a more reliable form
+ of identification. This method of identifying yourself over the Internet is
+ sometimes called
+ <a href="glossary.xhtml#client_authentication">client authentication</a>.</p>
+
+<p>However, Certificate Manager may have more than one certificate on file that
+ can be used for the purposes of identifying yourself to a website. In this
+ case, Certificate Manager presents the User Identification Request dialog
+ box, which displays two kinds of information:</p>
+
+<p><strong>This site has requested that you identify yourself with a
+ certificate</strong>: This section of the dialog box lists the following
+ information:</p>
+
+<ul>
+ <li><strong>Host name</strong>: The name of the server requesting
+ identification, used as part of its URL. For example, the host name for the
+ Netscape website is <tt>home.netscape.com</tt>.</li>
+ <li><strong>Organization</strong>: The name of the organization that runs the
+ website.</li>
+ <li><strong>Issued under</strong>: The name of the
+ <a href="glossary.xhtml#certificate_authority">certificate authority (CA)</a>
+ that issued the certificate.</li>
+</ul>
+
+<p><strong>Choose a certificate to present as identification</strong>: The
+ certificates you have available for the purposes of identifying yourself to a
+ website are listed in the drop-down list in this section of the dialog box.
+ Choose the certificate that seems most likely to be recognized by the website
+ you want to visit.</p>
+
+<p>To help you decide, the following details of the selected certificate are
+ displayed:</p>
+
+<ul>
+ <li><strong>Issued to</strong>: Lists information about the person identified
+ by the certificate (for example, your name and email address) and the
+ certificate&apos;s serial number and validity dates.</li>
+ <li><strong>Issued by</strong>: Summarizes information about the CA that
+ issued the certificate, such as its name, location, and state.</li>
+</ul>
+
+<h2 id="new_certificate_authority">New Certificate Authority</h2>
+
+<p>The certificates that the Certificate Manager has on file, whether stored on
+ your computer or on an external security device such as a smart card, include
+ certificates that identify
+ <a href="glossary.xhtml#certificate_authority">certificate authorities (CAs)</a>.
+ To be able to recognize any other certificates it has on file, Certificate
+ Manager must have certificates for the CAs that issued or authorized issuance
+ of those certificates.</p>
+
+<p>When you decide to trust a CA, Certificate Manager downloads that CA&apos;s
+ certificate and can then recognize the kinds of certificates you trust that
+ CA to issue.</p>
+
+<p>Before downloading a new CA certificate, Certificate Manager allows you to
+ specify the purposes for which you trust the certificate, if at all. You can
+ select any of the following options:</p>
+
+<ul>
+ <li><strong>Trust this CA to identify websites</strong>: Website certificates
+ for some websites, such as those that handle financial transactions, can be
+ extremely important, and inappropriate or false identification can have
+ negative consequences.</li>
+ <li><strong>Trust this CA to identify email users</strong>: If you intend to
+ send email users confidential information in encrypted form, or if accurate
+ identification of email users is important to you for any other reason, you
+ should consider carefully the CA&apos;s procedures for identifying
+ prospective certificate owners and whether they are appropriate for your
+ purposes before selecting this option.</li>
+ <li><strong>Trust this CA to identify software developers</strong>: Selecting
+ this option means that you trust the CA to issue certificates that identify
+ the origin of JavaScript scripts requesting special access to your computer,
+ such as the ability to change files. Since such access privileges can be
+ misused, for example to destroy data stored on your hard disk, be very
+ careful about selecting this option unless you are certain that you trust
+ the CA for this purpose.</li>
+</ul>
+
+<p>Before you decide to trust a new CA, make sure that you know who is
+ operating it. Make sure the CA&apos;s policies and procedures are
+ appropriate for the kinds of certificates it issues. For example, if the CA
+ issues certificates identifying websites you use for financial transactions,
+ make sure you are comfortable with the level of assurance the CA
+ provides.</p>
+
+<ul>
+ <li><strong>View</strong>: Click this button to view the CA certificate you
+ are about to download. If you decide you don&apos;t want to download this
+ certificate, click Cancel.</li>
+</ul>
+
+<h2 id="website_certificates">Website Certificates</h2>
+
+<p>When you attempt to go to a website that supports the use of
+ <a href="glossary.xhtml#ssl">SSL</a> for
+ <a href="glossary.xhtml#authentication">authentication</a> and
+ <a href="glossary.xhtml#encryption">encryption</a>, you may be faced with an
+ error page. There are two types, one called
+ <a href="#secure_connection_failed_page">Secure Connection Failed</a> and one
+ called <a href="#untrusted_connection_page">Untrusted Connection</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#secure_connection_failed_page">Secure Connection Failed
+ Page</a></li>
+ <li><a href="#untrusted_connection_page">Untrusted Connection Page</a></li>
+ <li><a href="#secure_connection_failed_dialog">Secure Connection Failed
+ Dialog</a></li>
+ <li><a href="#certificate_expired">Server Certificate Expired</a></li>
+ <li><a href="#certificate_not_yet_valid">Server Certificate Not Yet
+ Valid</a></li>
+ <li><a href="#domain_name_mismatch">Domain Name Mismatch</a></li>
+ </ul>
+</div>
+
+<h3 id="secure_connection_failed_page">Secure Connection Failed Page</h3>
+
+<p>In the case where you have disabled the SSL protocol (e.g. through
+ <a href="ssl_help.xhtml#ssltls_settings">SSL/TLS Settings</a>) or the website
+ that you are accessing is using an older, insecure version of the SSL protocol
+ then you will be presented with a page titled &quot;Secure Connection
+ Failed&quot;. That page contains some basic background information (including
+ the <strong>Error code</strong> that uniquely identifies the type of problem
+ &brandShortName; detected with the website) and a <strong>Try Again</strong>
+ button that triggers a page reload.</p>
+
+<h3 id="untrusted_connection_page">Untrusted Connection Page</h3>
+
+<p>If SSL itself is enabled then the error page that you will be presented with
+ will be titled &quot;This Connection is Untrusted&quot;. There are many
+ different reasons why a connection can appear untrusted. Here are some of the
+ most common ones:</p>
+
+<ul>
+ <li>the certificate of the website is <a href="#certificate_expired">no longer
+ valid (expired)</a></li>
+ <li>the certificate of the website is
+ <a href="#certificate_not_yet_valid">not yet valid</a></li>
+ <li>the certificate of the website is only valid for another website
+ (<a href="#domain_name_mismatch">domain name mismatch</a>)</li>
+ <li>the certificate of the website is self-signed (thus the identity of the
+ website cannot be verified).</li>
+ <li>the issuer certificate is not trusted (&brandShortName; cannot
+ verify the identity of the website because it doesn&apos;t
+ recognize the <a href="glossary.xhtml#certificate_authority">certificate
+ authority (CA)</a> that issued the website&apos;s certificate)</li>
+</ul>
+
+<p>The page displayed in the above cases is meant to help you understand why
+ &brandShortName; was unable to establish a secure connection to the website.
+ It starts by telling you that the website&apos;s identity could not be
+ verified, then offers you to leave the page by clicking the <strong>This
+ sounds bad, take me to my home page instead</strong> button. If you are unsure
+ what to do it is recommended that you follow this advice.</p>
+
+<p>If you want to know a little bit more about the actual problem at hand you
+ may expand the corresponding section by clicking the chevron in front of
+ <strong>Technical Details</strong>. That section also contains the
+ <strong>Error code</strong> that uniquely identifies the type of problem
+ &brandShortName; detected with the website.</p>
+
+<h4 id="add_security_exception">Adding a Security Exception</h4>
+
+<p>The <strong>I Understand the Risks</strong> section of the Untrusted
+ Connection page allows you to tell &brandShortName; to explicitly override the
+ security checks for this website by adding an exception. If you expand the
+ section by clicking the chevron in front of it you will see an <strong>Add
+ Exception</strong> button that will take you to a dialog allowing you to get
+ and view the website&apos;s certificate and optionally add a Security
+ Exception for it (either permanently or just for the current session). Those
+ exceptions can be administered through the Certificate Manager&apos;s
+ <a href="certs_help.xhtml#servers">Servers</a> tab.</p>
+
+<h3 id="secure_connection_failed_dialog">Secure Connection Failed Dialog</h3>
+
+<p>In cases where &brandShortName; cannot determine the actual cause of the
+ problem a dialog titled &quot;Secure Connection Failed&quot; is shown in
+ addition to the <a href="#untrusted_connection_page">Untrusted Connection
+ page</a>. That dialog includes a <strong>View Certificate</strong> button
+ that allows you to examine the website&apos;s certificate more closely.</p>
+
+<h3 id="certificate_expired">Certificate Expired</h3>
+
+<p>Like a credit card, a driver&apos;s license, and many other forms of
+ identification, a <a href="glossary.xhtml#certificate">certificate</a> is
+ valid for a specified period of time. When a certificate expires, the owner
+ of the certificate needs to get a new one.</p>
+
+<p>&brandShortName; <a href="#untrusted_connection_page">warns</a> you when you
+ attempt to visit a website whose server certificate has expired. The first
+ thing you should do is make sure the time and date displayed by your computer
+ is correct. If your computer&apos;s clock is set to a date that is after the
+ expiration date, &brandShortName; treats the website&apos;s certificate as
+ expired.</p>
+
+<p>If your computer&apos;s clock is set correctly, you need to make a decision
+ about whether to trust the website. This decision depends on what you intend
+ to do at the website and what else you know about it. Most commercial sites
+ will make sure that they replace their certificates before they expire. If you
+ choose to continue you need to <a href="#add_security_exception">add a
+ security exception</a>.</p>
+
+<h3 id="certificate_not_yet_valid">Certificate Not Yet Valid</h3>
+
+<p>Like a credit card, a driver&apos;s license, and many other forms of
+ identification, a <a href="glossary.xhtml#certificate">certificate</a> is
+ valid for a specified period of time.</p>
+
+<p>&brandShortName; <a href="#untrusted_connection_page">warns</a> you when you
+ attempt to visit a website whose server certificate&apos;s validity period has
+ not yet started. The first thing you should do is make sure the time and date
+ displayed by your own computer is correct. If your computer&apos;s clock is
+ set to the wrong date, &brandShortName; may treat the server certificate as
+ not yet valid even if this is not the case.</p>
+
+<p>If your computer&apos;s clock is set correctly, you need to make a decision
+ about whether to trust the website. This decision depends on what you intend
+ to do at the website and what else you know about it. Most commercial sites
+ will make sure that the validity period for their certificates has begun
+ before beginning to use them. If you choose to continue you need to
+ <a href="#add_security_exception">add a security exception</a>.</p>
+
+<h3 id="domain_name_mismatch">Domain Name Mismatch</h3>
+
+<p>A server <a href="glossary.xhtml#certificate">certificate</a> specifies the
+ name of the server in the form of the website&apos;s domain name. For example,
+ the domain name for the Mozilla website is <tt>www.mozilla.org</tt>. If the
+ domain name in a server&apos;s certificate doesn&apos;t match the actual
+ domain name of the website, it may be a sign that someone is attempting to
+ intercept your communication with the website.</p>
+
+<p>&brandShortName; <a href="#untrusted_connection_page">warns</a> you when you
+ attempt to visit a website whose server certificate&apos;s domain does not
+ match the domain of the website you are trying to visit. The decision whether
+ to trust the website anyway depends on what you intend to do at the site and
+ what else you know about it. Most commercial sites will make sure that the
+ host name for a website certificate matches the website&apos;s actual host
+ name. If you choose to continue you need to
+ <a href="#add_security_exception">add a security exception</a>.</p>
+
+<p>If you decide to accept the certificate anyway (either for this session or
+ permanently), you should be cautious about what you do on the website, and you
+ should treat any information you find there as potentially suspect.</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/certs_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/certs_help.xhtml
new file mode 100644
index 0000000000..b9f3427372
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/certs_help.xhtml
@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Certificate Manager</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<div class="boilerPlate">This document is provided for your information only.
+ It may help you take certain steps to protect the privacy and security of
+ your personal information on the Internet. This document does not, however,
+ address all online privacy and security issues, nor does it represent a
+ recommendation about what constitutes adequate privacy and security
+ protection on the Internet.</div>
+
+<h1 id="certificate_manager">Certificate Manager</h1>
+
+<p>This section describes how to use the Certificate Manager. For more
+ information on using certificates, see <a href="using_certs_help.xhtml">Using
+ Certificates</a>.</p>
+
+<p>If you are not currently viewing the Certificate Manager window, follow
+ these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Click Manage Certificates.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#your_certificates">Your Certificates</a></li>
+ <li><a href="#people">People</a></li>
+ <li><a href="#servers">Servers</a></li>
+ <li><a href="#authorities">Authorities</a></li>
+ <li><a href="#others">Others</a></li>
+ </ul>
+</div>
+
+<h2 id="your_certificates">Your Certificates</h2>
+
+<p>The Your Certificates tab in the <a href="#certificate_manager">Certificate
+ Manager</a> displays the certificates on file that identify you. Your
+ certificates are listed under the names of the organizations that issued
+ them. If you can&apos;t see certificate names under an organization&apos;s
+ name, double-click the name to expand it.</p>
+
+<p>Use the following buttons to view and manage your certificates (most actions
+ require one or more certificates to be selected):</p>
+
+<ul>
+ <li><strong>View</strong>: Display detailed information about the selected
+ certificates.</li>
+ <li><strong>Backup</strong>: Initiate the process of saving the selected
+ certificates. A window appears that allows you to choose a password to
+ protect the backup. You can then save the backup in a directory of your
+ choice.</li>
+ <li><strong>Backup All</strong>: Initiate the process of saving all the
+ certificates stored in the
+ <a href="glossary.xhtml#software_security_device">Software Security
+ Device</a>.
+
+ <p><strong>Note</strong>: Certificates on smart cards cannot be backed up.
+ Whether you select some of your certificates and click Backup, or click
+ Backup All, the resulting backup file will not include any certificates
+ stored on smart cards or other external security devices. You can only
+ back up certificates that are stored on the built-in Software Security
+ Device.</p>
+ </li>
+ <li><strong>Import</strong>: Import a file containing one or more
+ certificates that were previously backed up. When you click Import,
+ Certificate Manager first asks you to locate the file that contains the
+ backup. The names of certificate backup files typically end in
+ <tt>.p12</tt>; for example, <tt>MyCert.p12</tt>. After you select the file
+ to be imported, Certificate Manager asks you to enter the password that you
+ set when you backed up the certificate.</li>
+ <li><strong>Delete</strong>: Delete the selected certificates.</li>
+</ul>
+
+<h3 id="choose_a_certificate_backup_password">Choose a Certificate Backup
+ Password</h3>
+
+<p>A certificate backup password protects one or more certificates that you are
+ backing up from the <a href="#your_certificates">Your Certificates</a> tab in
+ the Certificate Manager.</p>
+
+<p>The Certificate Manager asks you to set this password when you back up
+ certificates, and requests it when you attempt to import certificates that
+ have previously been backed up.</p>
+
+<ul>
+ <li><strong>Certificate backup password</strong>: Type your backup password
+ into this field.</li>
+ <li><strong>Certificate backup password (again)</strong>: Type your backup
+ password again. If you don&apos;t type it the second time exactly as you
+ did the first time, the OK button remains inactive. If this happens, try
+ typing the new password again.</li>
+</ul>
+
+<p>If someone obtains the file containing a certificate that you have backed up
+ and successfully imports the certificate, that person can send messages or
+ access websites while pretending to be you. This can be a problem, for
+ example, if you digitally sign important email messages or manage your bank
+ or investment accounts over the Internet.</p>
+
+<p>Therefore, it&apos;s important to select a certificate backup password that
+ is difficult to guess. The <strong>password quality meter</strong> gives you
+ a rough idea of the quality of your password as you type it based on factors
+ such as length and the use of uppercase letters, lowercase letters, numbers,
+ and symbols. It does not guarantee that your password cannot be guessed,
+ however.</p>
+
+<p>For further guidelines, see
+ <a href="passwords_help.xhtml#choosing_a_good_password">Choosing a Good
+ Password</a>.</p>
+
+<p>It&apos;s also important to record the password in a safe place&mdash;and
+ not anywhere that&apos;s easily accessible to someone else. If you forget
+ this password, you can&apos;t import the backup of your certificate.</p>
+
+<h3 id="delete_your_certificates">Delete Your Certificates</h3>
+
+<p>Before deleting one of your own expired certificates from the
+ <a href="#your_certificates">Your Certificates</a> tab in the Certificate
+ Manager, make sure you won&apos;t need it again some day for reading old
+ email messages that you may have encrypted with the corresponding private
+ key.</p>
+
+<h2 id="people">People</h2>
+
+<p>The People tab in the <a href="#certificate_manager">Certificate Manager</a>
+ displays email certificates you have on file that identify other people.</p>
+
+<p>When people send you digitally signed email messages, Certificate Manager
+ imports their certificates automatically. You can use these certificates to
+ send encrypted messages to those people.</p>
+
+<p>Certificates that identify people are listed under the names of the
+ organizations that issued them. If you can&apos;t see certificate names under
+ an organization&apos;s name, double-click the name to expand it.</p>
+
+<p>Use the following buttons to view and manage your certificates (most actions
+ require one or more certificates to be selected):</p>
+
+<ul>
+ <li><strong>View</strong>: Display detailed information about the selected
+ certificates.</li>
+ <li><strong>Import</strong>: Import a file containing one or more
+ certificates. When you click Import, Certificate Manager first asks you
+ to locate the file that contains the certificate(s).</li>
+ <li><strong>Export</strong>: Export the selected certificates. You can
+ choose among various formats.</li>
+ <li><strong>Delete</strong>: Delete the selected certificates.</li>
+</ul>
+
+<h3 id="delete_email_certificates">Delete Email Certificates</h3>
+
+<p>Before deleting someone else&apos;s certificate from the
+ <a href="#people">People</a> tab in the Certificate Manager, make sure you
+ won&apos;t need it again some day to send encrypted email to that person or
+ to verify digital signatures on messages from that person.</p>
+
+<h2 id="servers">Servers</h2>
+
+<p>The Servers tab in the Certificate Manager displays certificates you have
+ on file that identify servers (websites, mail servers).</p>
+
+<p>Certificates that identify servers are grouped under the names of the
+ organizations that issued them. If you can&apos;t see certificate names under
+ an organization&apos;s name, double-click the name to expand it.</p>
+
+<p>Use the following buttons to view and manage your certificates (most actions
+ require one or more certificates to be selected):</p>
+
+<ul>
+ <li><strong>View</strong>: Display detailed information about the selected
+ certificates.</li>
+ <li><strong>Export</strong>: Export the selected certificates. You can
+ choose among various formats.</li>
+ <li><strong>Delete</strong>: Delete the selected certificates.</li>
+ <li><strong>Add Exception</strong>: Add a security exception for a server
+ (website, mail server) that identifies itself with invalid information.
+ This is an advanced feature, act with caution.</li>
+</ul>
+
+<h3 id="delete_website_certificates">Delete Website Certificates</h3>
+
+<p>Before deleting a server certificate from the
+ <a href="#servers">Servers</a> tab in the Certificate Manager, make sure that
+ you won&apos;t need it again for the purposes of identifying a website or
+ mail server and setting up an encrypted connection.</p>
+
+<h2 id="authorities">Authorities</h2>
+
+<p>The Authorities tab in the <a href="#certificate_manager">Certificate
+ Manager</a> displays the certificates you have on file that identify
+ <a href="glossary.xhtml#certificate_authority">certificate authorities
+ (CAs)</a>.</p>
+
+<p>CA certificates are grouped under the names of the organizations that issued
+ them. If you can&apos;t see certificate names under an organization&apos;s
+ name, double-click the name to expand it.</p>
+
+<p>Use the following buttons to view and manage your certificates (most actions
+ require one or more certificates to be selected):</p>
+
+<ul>
+ <li><strong>View</strong>: Display detailed information about the selected
+ certificates.</li>
+ <li><strong>Edit Trust</strong>: View or change the settings that Certificate
+ Manager associates with the selected certificates. You can use these
+ settings to designate what kinds of certificates, if any, you trust that
+ are issued by the corresponding CAs.</li>
+ <li><strong>Import</strong>: Import a file containing one or more
+ certificates. When you click Import, Certificate Manager first asks you
+ to locate the file that contains the certificate(s).</li>
+ <li><strong>Export</strong>: Export the selected certificates. You can
+ choose among various formats.</li>
+ <li><strong>Delete or Distrust</strong>: Delete the selected certificates.</li>
+</ul>
+
+<p>To ensure that an entire
+ <a href="glossary.xhtml#certificate_chain">certificate chain</a> of CAs are
+ all trusted, you need to edit the root CA certificate only.</p>
+
+<p>To import the chain, you click a link on a web page provided by the CA. You
+ can then use the authorities tab to locate the root certificate and edit its
+ trust settings.</p>
+
+<p>The root and intermediate CAs all appear under the same organization. The
+ root certificate is the one that lists itself as the issuer.</p>
+
+<p><strong>If you download an intermediate CA</strong>: If you download an
+ intermediate CA certificate that chains to a root certificate already marked
+ as trusted in your browser, you don&apos;t have to indicate what purposes you
+ trust it for. Intermediate certificates automatically inherit the trust
+ settings of their roots.</p>
+
+<h3 id="edit_ca_certificate_trust_settings">Edit CA Certificate Trust
+ Settings</h3>
+
+<p>When you select a CA certificate from the
+ <a href="#authorities">Authorities</a> tab in the Certificate Manager and
+ click Edit, you see a window entitled <q>Edit CA certificate trust
+ settings</q>. Here you specify the kinds of certificates you trust this CA
+ to certify. If you deselect all the checkboxes, Certificate Manager will not
+ trust any certificates issued by this CA.</p>
+
+<p>The settings have these effects:</p>
+
+<ul>
+ <li><strong>This certificate can identify websites</strong>: Certificate
+ Manager will trust certificates issued by this CA for the purpose of
+ identifying websites and encrypting website connections. If you deselect
+ this checkbox, Certificate Manager will not trust website certificates
+ issued by this CA.</li>
+ <li><strong>This certificate can identify mail users</strong>: Certificate
+ Manager will trust certificates issued by this CA for the purpose of
+ signing or encrypting email. If you deselect this checkbox, Certificate
+ Manager will not trust email certificates issued by this CA.</li>
+ <li><strong>This certificate can identify software makers</strong>:
+ Certificate Manager will trust certificates issued by this CA for the
+ purpose of identifying software makers. If you deselect this checkbox,
+ Certificate Manager will not trust such certificates issued by this
+ CA.</li>
+</ul>
+
+<p>Click OK to confirm the settings you have selected.</p>
+
+<h3 id="delete_ca_certificates">Delete CA Certificates</h3>
+
+<p>Before deleting a CA certificate from the
+ <a href="#authorities">Authorities</a> tab in the Certificate Manager,
+ make sure that you won&apos;t need it again to validate certificates issued
+ by that CA. If you delete the only valid certificate you have for a CA,
+ Certificate Manager will no longer trust any certificates issued by that
+ CA.</p>
+
+<h2 id="others">Others</h2>
+
+<p>The Others tab in the Certificate Manager displays certificates you have
+ on file that do not fit in any of the other categories, i.e. certificates
+ that neither belong to you, other people, servers or CAs.</p>
+
+<p>Other certificates are grouped under the names of the organizations that
+ issued them. If you can&apos;t see certificate names under an
+ organization&apos;s name, double-click the name to expand it.</p>
+
+<p>Use the following buttons to view and manage your certificates:</p>
+
+<ul>
+ <li><strong>View</strong>: Display detailed information about the selected
+ certificates.</li>
+ <li><strong>Export</strong>: Export the selected certificates. You can
+ choose among various formats.</li>
+ <li><strong>Delete</strong>: Delete the selected certificates.</li>
+</ul>
+
+<h2 id="device_manager">Device Manager</h2>
+
+<p>This section describes the options available in the Device Manager window.
+ For background information and step-by-step instructions on the use of the
+ Device Manager, see
+ <a href="using_certs_help.xhtml#managing_smart_cards_and_other_security_devices">Managing
+ Smart Cards and Other Security Devices</a>.</p>
+
+<p>If you are not currently viewing the Device Manager window, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Certificates panel, click Manage Security Devices.</li>
+</ol>
+
+<p>The Device Manager lists each available PKCS #11 module, and the security
+ devices managed by each module below the module&apos;s name.</p>
+
+<p>When you select a module or device, information about the selected item
+ appears in the middle of the window, and some of the buttons on the right
+ side of the window become available. In general, you perform an action on
+ a module or device by selecting its name and clicking the appropriate
+ button:</p>
+
+<ul>
+ <li><strong>Log In</strong>: Log into the selected security device.</li>
+ <li><strong>Log Out</strong>: Log out of the selected security device. After
+ you have logged out of the device, the device and the certificates it
+ contains will not be available until you log in again.</li>
+ <li><strong>Change Password</strong>: Change the master password for the
+ selected security device.</li>
+ <li><strong>Load</strong>: Displays a dialog box that allows you to specify
+ the name and location of a new PKCS #11 module. Before adding a new module,
+ you should first install the module software on your computer and if
+ necessary connect any associated hardware device. Follow the instructions
+ provided by the vendor.</li>
+ <li><strong>Unload</strong>: Unload the selected module. If you unload a
+ module, both the module and its security devices are no longer available
+ for use by the browser.</li>
+ <li><strong>Enable FIPS</strong>: Turns the FIPS mode on and off. For more
+ information, see
+ <a href="using_certs_help.xhtml#enable_fips_mode">Enable FIPS
+ Mode</a>.</li>
+</ul>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/certs_prefs_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/certs_prefs_help.xhtml
new file mode 100644
index 0000000000..a6aae1b376
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/certs_prefs_help.xhtml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Privacy &amp; Security Preferences - Certificates</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="certificate_settings">Certificate Settings</h1>
+
+<p>This section describes how to set your certificate preferences and how to
+ use the Certificate Manager, Device Manager, and other dialog boxes related
+ to certificates.</p>
+
+<p>For step-by-step descriptions of various tasks related to certificates, see
+ <a href="using_certs_help.xhtml">Using Certificates</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#privacy_and_security_preferences_certificates">Certificate
+ Preferences</a></li>
+ <li><a href="certs_help.xhtml">Certificate Manager</a></li>
+ <li><a href="certs_help.xhtml#device_manager">Device Manager</a></li>
+ <li><a href="cert_dialog_help.xhtml">Certificate Information and
+ Decisions</a></li>
+ </ul>
+</div>
+
+<h2 id="privacy_and_security_preferences_certificates">Privacy &amp; Security
+ Preferences - Certificates</h2>
+
+<p>This section describes use the Certificates preferences panel. To view
+ Certificates preferences, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<h3 id="client_certificate_selection">Client Certificate Selection</h3>
+
+<p>Some websites require you to identify yourself with a certificate. The
+ option you select here determines how the browser identifies the certificate
+ to present among those you may have on file:</p>
+
+<ul>
+ <li><strong>Select Automatically:</strong> Click this option if you want
+ the browser to select a certificate without asking you.</li>
+ <li><strong>Ask Every Time:</strong> Click this option if you want the browser
+ to ask you which certificate to use each time a website requests one.</li>
+</ul>
+
+<h3 id="manage_certificates">Manage Certificates</h3>
+
+<p>Certificates are the digital equivalent of ID cards&mdash;they help other
+ people identify you, and they help you identify other people, websites, and
+ organizations.</p>
+
+<p>To examine or configure the certificates you have on file, click Manage
+ Certificates. See <a href="using_certs_help.xhtml#managing_certificates">Managing
+ Certificates</a> for further information on this dialog.</p>
+
+<h3 id="manage_security_devices">Manage Security Devices</h3>
+
+<p>A security device is a hardware or software device that stores your
+ certificates and keys. For example, a smart card is a security device. Your
+ browser has its own built-in software security device, and you can use
+ additional security devices, such as smart cards, at the same time.</p>
+
+<p>To examine or configure your security devices, click Manage Security
+ Devices. See <a href="using_certs_help.xhtml#managing_smart_cards_and_other_security_devices">Managing
+ Smart Cards and Other Security Devices</a> for further information on
+ this dialog.</p>
+
+<h3 id="ocsp">OCSP</h3>
+
+<p>A certificate revocation list (CRL) is a list of revoked certificates that
+ is generated and signed by a
+ <a href="glossary.xhtml#certificate_authority">certificate authority
+ (CA)</a>. The Online Certificate Status Protocol (OCSP) makes it possible for
+ Certificate Manager to perform an online check of a certificate&apos;s
+ validity each time the certificate is viewed or used. This process involves
+ checking the certificate against a CRL maintained at a server specified by
+ the CA of that certificate. Your computer must be online for OCSP to work.</p>
+
+<p>The following settings in the OCSP section of the Certificates preferences
+ panel determine how Certificate Manager uses OCSP:</p>
+
+<ul>
+ <li><strong>Use the Online Certificate Status Protocol (OCSP) to confirm the
+ current validity of certificates</strong>: Select this if you want
+ Certificate Manager to perform an online status check each time it verifies
+ a certificate. Certificate Manager makes sure that the certificate is
+ listed as valid at the URL and checks validity period and trust settings.
+
+ <p><strong>Note</strong>: If this setting is not selected, Certificate
+ Manager will only confirm the certificate&apos;s validity period and that
+ it is correctly signed by a CA whose own CA certificate is both listed
+ under the CA Certificates tab (in the main Certificate Manager window)
+ and marked as trusted for issuing that kind of certificate.</p>
+ </li>
+ <li><strong>When an OCSP server connection fails, treat the certificate as
+ invalid</strong>: Select this if you want the validation to fail if a
+ connection to the OCSP server can&apos;t be established, thus enforcing
+ that a certificate always must be positively validated for each use.</li>
+</ul>
+
+<p>For more detailed information on certificate validation, see
+ <a href="using_certs_help.xhtml#controlling_validation">How Certificate
+ Validation Works</a>.</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/composer_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/composer_help.xhtml
new file mode 100644
index 0000000000..81879bb90d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/composer_help.xhtml
@@ -0,0 +1,2624 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Creating Web Pages with &brandShortName; Composer</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="creating_web_pages_with_mozilla_composer">Creating Web Pages with
+ &brandShortName; Composer</h1>
+
+<p>&brandShortName; Composer lets you create your own web pages and publish
+ them on the web. You don&apos;t have to know HTML to use Composer; it is as
+ easy to use as a word processor.</p>
+
+<p>Toolbar buttons let you add lists, tables, images, links to other pages,
+ colors, and font styles. You can see what your document will look like on
+ the Web as you create it, and you can easily share your document with
+ other users, no matter what type of browser or HTML-capable email program
+ they use.</p>
+
+<p>To start using &brandShortName; Composer:</p>
+
+<ul>
+ <li>Click the Composer icon in the lower-left corner of any
+ &brandShortName; window.</li>
+</ul>
+
+<div><img src="images/composer_icon.png" alt="" /></div>
+<div style="margin-inline-start: 50px;"><strong>Composer icon</strong></div>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#starting_a_new_page">Starting a New Page</a></li>
+ <li><a href="#formatting_your_web_pages">Formatting Your Web Pages</a></li>
+ <li><a href="#adding_tables_to_your_web_page">Adding Tables to Your Web
+ Page</a></li>
+ <li><a href="#adding_images_to_your_web_page">Adding Pictures (Images) to
+ Your Web Page</a></li>
+ <li><a href="#adding_mathematical_formulas_to_your_web_page">Adding
+ Mathematical Formulas to Your Web Page</a></li>
+ <li><a href="#setting_page_properties">Setting Page Properties</a></li>
+ <li><a href="#creating_links_in_composer">Creating Links in
+ Composer</a></li>
+ <li><a href="#publishing_your_pages_on_the_web">Publishing Your Pages on
+ the Web</a></li>
+ <li><a href="#composer_preferences">Composer Preferences</a></li>
+ </ul>
+</div>
+
+<h1 id="starting_a_new_page" style="margin-top: 50px;">Starting a New Page</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#creating_a_new_page">Creating a New Page</a></li>
+ <li><a href="#saving_and_browsing_your_new_page">Saving and Browsing Your
+ New Page</a></li>
+ </ul>
+</div>
+
+<h2 id="creating_a_new_page">Creating a New Page</h2>
+
+<p>&brandShortName; Composer is an HTML (Hypertext Markup Language) editor that
+ allows you to create and edit web pages. Composer is a <em>WYSIWYG</em> (What
+ You See Is What You Get) editor, so you can display how your page will look
+ to the reader as you&apos;re creating it. It is not necessary for you to know
+ HTML, since most of the basic HTML functions are available as commands from
+ the toolbars and menus.</p>
+
+<p>Composer also lets you edit the HTML source if you want. To view or edit the
+ HTML source code, open the View menu, and choose HTML Source, or click the
+ &lt;HTML&gt; Source tab in the Edit Mode toolbar at the bottom of the
+ Composer window.</p>
+
+<p>To create a web page, use one of the methods described below. Once
+ you&apos;ve started a page, you can add and edit text just as you would
+ in a word processor.</p>
+
+<p><strong>To create a new page from the browser</strong>:</p>
+
+<ul>
+ <li>Open the File menu, choose New, and then Composer Page. A Composer
+ window containing a blank page opens.</li>
+</ul>
+
+<p><strong>To edit a page you&apos;re currently viewing in the
+ browser</strong>:</p>
+
+<ul>
+ <li>In the browser window of the page you&apos;re viewing, open the File
+ menu and choose Edit Page. You see a Composer window that contains the
+ page you&apos;re viewing.</li>
+</ul>
+
+<p><strong>To create a new page in Composer</strong>:</p>
+
+<ul>
+ <li>Click the New button in Composer&apos;s toolbar.</li>
+</ul>
+
+<p><strong>To start from an HTML file stored on your local drive</strong>:</p>
+
+<ol>
+ <li>Open the Window menu and choose Composer. You see the Composer
+ window.</li>
+ <li>Open the File menu and choose Open File. You see the Open HTML File
+ dialog box.</li>
+ <li>On your local drive, locate the file that you want to edit.</li>
+ <li>Click Open to display the specified file in a Composer window.</li>
+</ol>
+
+<p><strong>To edit a web page</strong>:</p>
+
+<ol>
+ <li>Open the Window menu and choose Browser.</li>
+ <li>Go to a web page: type the URL of the page (for example,
+ <tt>http://www.mozilla.org</tt>) in the Location Bar and press
+ <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.</li>
+ <li>Open the File menu and choose Edit Page.</li>
+</ol>
+
+<p><strong>Tip</strong>: In the Composer window you can quickly open the most
+ recent file you&apos;ve been working on by opening the File menu, choosing
+ Recent Pages, and then selecting the file you want from the list.</p>
+
+<p>[<a href="#starting_a_new_page">Return to beginning of section</a>]</p>
+
+<h2 id="saving_and_browsing_your_new_page">Saving and Browsing Your New
+ Page</h2>
+
+<p>You can save Composer documents in HTML or text-only format. Saving a
+ document in HTML format preserves the document&apos;s formatting, such
+ as text styles (for example, bold or italic), tables, links, and images.
+ Saving a document in text-only format removes all the HTML tags but
+ preserves the document&apos;s text.</p>
+
+<p>To save a document as an HTML file:</p>
+
+<ul>
+ <li>Open the File menu and choose Save or click the Save button on the
+ Composition toolbar.
+
+ <p>If you haven&apos;t already given your page a title, Composer prompts
+ you to do so. Composer displays the page title in the browser
+ window&apos;s title bar when you view the page in the browser. The
+ document&apos;s page title also appears in your list of bookmarks
+ if you bookmark the page.</p>
+
+ <p>Composer then prompts you to enter a filename and specify the location
+ where you want to save the file. Make sure you preserve the .html
+ extension in the filename.</p>
+ </li>
+</ul>
+
+<p>To change the filename or location of an existing HTML file:</p>
+
+<ul>
+ <li>Choose Save As and select a different filename or location.</li>
+</ul>
+
+<p>When you save a page in Composer, all parts of the page (the HTML, images
+ and other files, such as sound files and style sheets), are saved locally
+ on your hard drive. If you only want to save the HTML part of the page, you
+ must change the Composer preference for saving pages. See
+ <a href="#composer">Composer Preferences - Composer</a> for more information
+ on changing Composer&apos;s setting for saving pages.</p>
+
+<p>If an image location is absolute (starts with <q>http://</q>) and you are
+ connected to the Internet, you will still see that image in the document in
+ Composer and the browser. However, if the image location is relative to the
+ page location (starts with <q>file:///</q>), then you won&apos;t see the
+ image in the local version of the document.</p>
+
+<p>To save a document as a text-only file:</p>
+
+<ol>
+ <li>Open the File menu and choose Export to Text.</li>
+ <li>Enter the filename and specify the location where you want to save the
+ file.</li>
+</ol>
+
+<p><strong>Note</strong>: Images do not appear in documents saved in the
+ text-only format.</p>
+
+<p><strong>Tip</strong>: You can choose Revert to Last Saved from the File
+ menu to retrieve the most recently saved copy of the document in which
+ you&apos;re working. Keep in mind that your current changes will be lost.</p>
+
+<p>To view your page in a browser window in order to test your links:</p>
+
+<ul>
+ <li>Open the File menu and choose Browse Page (or click Browse in the
+ Composition toolbar). If you have not yet saved your document, Composer
+ prompts you to enter a page title, filename, and location. The Composer
+ window remains open behind the new browser window.</li>
+</ul>
+
+<p>[<a href="#starting_a_new_page">Return to beginning of section</a>]</p>
+
+<h1 id="formatting_your_web_pages">Formatting Your Web Pages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#formatting_paragraphs_headings_and_lists">Formatting
+ Paragraphs, Headings, and Lists</a></li>
+ <li><a href="#working_with_lists">Working with Lists</a></li>
+ <li><a href="#changing_text_color_style_and_font">Changing Text Color,
+ Style, and Font</a></li>
+ <li><a href="#removing_or_discontinuing_text_styles">Removing or
+ Discontinuing Text Styles</a></li>
+ <li><a href="#finding_and_replacing_text">Finding and Replacing
+ Text</a></li>
+ <li><a href="#inserting_horizontal_lines">Inserting Horizontal
+ Lines</a></li>
+ <li><a href="#inserting_special_characters">Inserting Special
+ Characters</a></li>
+ <li><a href="#inserting_html_elements_and_attributes">Inserting HTML
+ Elements and Attributes</a></li>
+ <li><a href="#validating_the_html">Validating the HTML</a></li>
+ <li><a href="#choosing_the_right_editing_mode">Choosing the Right Editing
+ Mode</a></li>
+ </ul>
+</div>
+
+<h2 id="formatting_paragraphs_headings_and_lists">Formatting Paragraphs,
+ Headings, and Lists</h2>
+
+<p>To apply a format to a paragraph, begin from the Composer window:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the format to begin,
+ or select the text you want to format.</li>
+ <li>Choose a paragraph format using the drop-down list in the Format toolbar:
+ <ul>
+ <li><strong>Body Text</strong>: Applies the application default font and
+ style for regular text, without affecting the spacing before or after
+ the text.</li>
+ <li><strong>Paragraph</strong>: Inserts a paragraph tag (use this to
+ begin a new paragraph). The paragraph includes top and bottom
+ margins.</li>
+ <li><strong>Heading 1</strong> - <strong>Heading 6</strong>: Formats the
+ paragraph as a heading. Heading 1 is the highest-level heading, while
+ Heading 6 is the lowest-level heading.</li>
+ <li><strong>Address</strong>: Can be used for a web page <q>signature</q>
+ that indicates the author of the page and the person to contact for
+ more information, for example: <tt>user@example.com</tt>
+
+ <p>You might want to include the date and a copyright notice. This
+ format usually appears at the bottom of the web page under a
+ horizontal line. The browser displays the address format in
+ italics.</p>
+ </li>
+ <li><strong>Preformat</strong>: This is useful for elements such as code
+ examples, column data, and mail messages that you want displayed in a
+ fixed-width font. In normal text, most browsers remove extra spaces,
+ tabs, and paragraph returns. However, text that uses the Preformatted
+ style is displayed with the white space intact, preserving the layout
+ of the original text.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>To format text as a heading:</p>
+
+<ol>
+ <li>Click to place the insertion point anywhere within the text that you
+ want to format.</li>
+ <li>Using the drop-down list in the Format toolbar, choose the level of
+ heading you want, from 1 (largest) to 6 (smallest). Choose <q>Heading 1</q>
+ for your main heading, <q>Heading 2</q> for the next level, and so
+ forth.</li>
+</ol>
+
+<p>To apply a list item format:</p>
+
+<ol>
+ <li>Click to place the insertion point within the line of text that you want
+ to format.</li>
+ <li>Open the Format menu and choose List.</li>
+ <li>Choose the list style:
+ <ul>
+ <li><strong>Bulleted</strong>: Each item has a bullet (dot) next to it
+ (as in this list).</li>
+ <li><strong>Numbered</strong>: Items are numbered.</li>
+ <li><strong>Term</strong> and <strong>Definition</strong>: These two
+ styles work together, creating a glossary-style appearance. Use the
+ Term tag for the word being defined, and the Definition tag for the
+ definition. The Term text appears flush left, and the Definition
+ text appears indented.</li>
+ </ul>
+ </li>
+</ol>
+
+<p><strong>Tip</strong>: You can quickly apply a list style to a block of
+ text by selecting the text and clicking the Numbered List
+ <img src="images/numbers.gif" width="21" height="21" alt="" />
+ or Bulleted List
+ <img src="images/bullets.gif" width="20" height="20" alt="" />
+ buttons on the Format toolbar.</p>
+
+<p>To change the style of bullets or numbers:</p>
+
+<ol>
+ <li>Click to place the insertion point within the text of the list item you
+ want to change, or select one or more items in the list if you want to
+ apply a new style to the entire list.</li>
+ <li>Open the Format menu and choose List Properties.</li>
+ <li>Select a bullet or number style from the drop-down list. For numbered
+ lists, you can specify a starting number. For bulleted lists, you can
+ change the bullet style.</li>
+</ol>
+
+<p><strong>Tip</strong>: You can also double-click on a bullet or number in a
+ list to display the List Properties dialog box.</p>
+
+<p>To align a paragraph or text in your page, for example, centering or
+ aligning to the left or right:</p>
+
+<ol>
+ <li>Click to place the insertion point within the paragraph or line of text
+ you want to align.</li>
+ <li>Open the Format menu and choose Align; then choose an alignment
+ option.</li>
+</ol>
+
+<p><strong>Note</strong>: You can also use the Format toolbar to align
+ text.</p>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="working_with_lists">Working with Lists</h2>
+
+<p>To end a list and continue typing body text:</p>
+
+<ul>
+ <li>Click to place the insertion point at the end of the last list item and
+ press <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd> twice to
+ end the list.</li>
+</ul>
+
+<p>To change one or more list items to body text:</p>
+
+<ol>
+ <li>Click to place the insertion point within the list item, or select the
+ list items.</li>
+ <li>In a numbered list, click the numbered list button (or in a bulleted
+ list, click the bulleted list button) in the Format toolbar.</li>
+</ol>
+
+<p>To position indented text below a list item:</p>
+
+<ol>
+ <li>Click to place the insertion point within the list item.</li>
+ <li>Press <kbd>Shift</kbd>+<kbd class="mac">Return</kbd><kbd
+ class="noMac">Enter</kbd> to create the hanging indent.</li>
+ <li>Type the text you want to indent.</li>
+ <li>Press <kbd>Shift</kbd>+<kbd class="mac">Return</kbd><kbd
+ class="noMac">Enter</kbd> to create another indented paragraph, or press
+ <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd> to create the
+ next list item.</li>
+</ol>
+
+<p><strong>Tip</strong>: You can increase or decrease the indentation of list
+ items by clicking anywhere in a list item and then clicking the Indent or
+ Outdent button on the Format toolbar. Alternatively, click anywhere in a
+ list item and press <kbd>Tab</kbd> to indent one level. Press
+ <kbd>Shift</kbd>+<kbd>Tab</kbd> to outdent one level.</p>
+
+<p>To merge two adjacent lists:</p>
+
+<ol>
+ <li>Select the two lists that you want to merge. Be sure to select all of
+ the elements in both lists. Note that any text in between the two lists
+ will also become part of the merged list.</li>
+ <li>Click the bulleted or numbered list button in the Format toolbar to
+ merge the lists.</li>
+</ol>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="changing_text_color_style_and_font">Changing Text Color, Style, and
+ Font</h2>
+
+<p>To change the style, color, or font of selected text:</p>
+
+<ol>
+ <li>Select the text you want to format.</li>
+ <li>Open the Format menu and choose one of the following:
+ <ul>
+ <li><strong>Font</strong>: Use this to choose a font. If you prefer to
+ use fonts specified by the reader&apos;s browser, select Variable
+ Width or Fixed Width.
+
+ <p><strong>Note</strong>: The fonts Helvetica, Arial, Times, and
+ Courier generally look the same when viewed on different computers.
+ If you select a different font, it may not look the same when viewed
+ using a different computer.</p>
+ </li>
+ <li><strong>Size</strong>: Use this to choose a <em>relative</em> font
+ size or select an option to increase or decrease text size (relative
+ to the surrounding text).</li>
+ <li><strong>Text Style</strong>: Use this to select a style, such as
+ italic, bold, or underline, or to apply a structured style, for
+ example, Code.</li>
+ <li><strong>Text Color</strong>: Use this to choose a color from the
+ color picker. If you are familiar with HTML hexadecimal color codes,
+ you can type a specific code or you can just type a color name (for
+ example, <q>blue</q>). You&apos;ll find the official W3C list of CSS
+ supported color names
+ <a href="http://www.w3.org/TR/CSS21/syndata.html#color-units">here</a>,
+ and another list of commonly supported color names
+ <a href="http://www.w3schools.com/html/html_colornames.asp">here</a>.
+ </li>
+ </ul>
+ </li>
+</ol>
+
+<p>To change the background color of the page:</p>
+
+<ol>
+ <li>Click anywhere in the page.</li>
+ <li>Click the background color block in the Format toolbar.</li>
+ <li>Choose a background color from the Block Background Color dialog
+ box.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p><strong>Tip</strong>: To quickly change the color of text to the color
+ last used, select the text, then press Shift and click on the text color
+ block in the Format toolbar. This is useful when you want to use one
+ color for separate lines of text.</p>
+
+<p>You can also use an image as a background. See
+ <a href="#setting_page_colors_and_backgrounds">Setting Page Colors and
+ Backgrounds</a>.</p>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="removing_or_discontinuing_text_styles">Removing or Discontinuing Text
+ Styles</h2>
+
+<p>To remove all text styles (bold, italic, and so on) from selected text:</p>
+
+<ol>
+ <li>Select the text.</li>
+ <li>Open the Format menu and choose Remove All Text Styles.</li>
+ <li>Continue typing.</li>
+</ol>
+
+<p>To continue typing text with all text styles removed:</p>
+
+<ol>
+ <li>Place the insertion point where you want to discontinue the text
+ styles.</li>
+ <li>Open the Format menu and choose Discontinue Text Styles.</li>
+ <li>Continue typing.</li>
+</ol>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="finding_and_replacing_text">Finding and Replacing Text</h2>
+
+<p>To find text in the page you&apos;re currently working on:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want to begin your
+ search.</li>
+ <li>Open the Edit menu and choose Find and Replace. You see the Find and
+ Replace dialog box.</li>
+ <li>Type the text you want to locate in the <q>Find what</q> field. To narrow
+ the search, check one or more of the following options:
+ <ul>
+ <li><strong>Match exact case</strong>: Use this to specify whether
+ the search is for case-sensitive text.</li>
+ <li><strong>Wrap around</strong>: Use this to search to the end of the
+ page and then start again from the top or bottom, depending on whether
+ you are searching forward or backwards.</li>
+ <li><strong>Search backwards</strong>: Use this to search back from the
+ insertion point to the beginning of the page.</li>
+ </ul>
+ </li>
+ <li>Click Find Next to begin searching. When Composer locates the first
+ occurrence of the text, click Find Next to search for the next
+ occurrence.</li>
+ <li>Click Close when you are done.</li>
+</ol>
+
+<p>To find and replace text in the page you&apos;re currently working on:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want to begin your
+ search.</li>
+ <li>Open the Edit menu and choose Find and Replace. You see the Find and
+ Replace dialog box.</li>
+ <li>Type the text you want to find and then type the replacement text.</li>
+ <li>To narrow the search, check one or more of the following options:
+ <ul>
+ <li><strong>Match exact case</strong>: Use this to specify whether
+ the search is for case-sensitive text. If you don&apos;t select this
+ option, the search will find matching text in both upper and lower
+ case.</li>
+ <li><strong>Wrap around</strong>: Use this to search to the end of the
+ page and then start again from the top.</li>
+ <li><strong>Search backwards</strong>: Use this to search from the end
+ to the beginning of the page.</li>
+ </ul>
+ </li>
+ <li>Click Find Next to search for the next occurrence. Composer selects the
+ next occurrence of the text.</li>
+ <li>Click Replace to replace the selected text with the replacement text.
+ Click Replace and Find to replace the selected text and find the next
+ occurrence. Click Replace All to replace every occurrence in the document
+ with the replacement text.</li>
+ <li>Click Close when you are done.</li>
+</ol>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="inserting_horizontal_lines">Inserting Horizontal Lines</h2>
+
+<p>Horizontal lines are typically used to separate different sections of a
+ document visually. To insert a horizontal line (also called a <em>rule</em>)
+ in your page, begin from the Composer window:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the line to
+ appear.</li>
+ <li>Open the Insert menu and choose Horizontal Line.</li>
+</ol>
+
+<h3 id="setting_horizontal_line_properties">Setting Horizontal Line
+ Properties</h3>
+
+<p>You can customize a line&apos;s height, length, width, alignment, and
+ shading.</p>
+
+<ol>
+ <li>Double-click the line to display the Horizontal Line Properties dialog
+ box.</li>
+ <li>Edit any of these properties:
+ <ul>
+ <li><strong>Width</strong>: Enter the width and then choose <q>% of
+ window</q> or <q>pixels</q>. If you specify width as a percentage,
+ the line&apos;s width changes whenever the Composer window&apos;s
+ or browser window&apos;s width changes.</li>
+ <li><strong>Height</strong>: Type a number for the line&apos;s height
+ (in pixels).</li>
+ <li><strong>3-D Shading</strong>: Select this to add depth to the line
+ by adding a bevel shading.</li>
+ <li><strong>Alignment</strong>: Specify where you want to place the
+ line (left, center, or right).</li>
+ </ul>
+ </li>
+ <li>Click Use as Default to use these settings as the default the next time
+ you insert a horizontal line.</li>
+ <li>To edit the properties of a horizontal line manually, click Advanced
+ Edit. See the section,
+ <a href="#using_the_advanced_property_editor">Advanced Property Editor</a>,
+ for details.</li>
+</ol>
+
+<p><strong>Tip</strong>: You can select <q>HTML Tags</q> from the View menu to
+ show all the HTML elements in yellow boxes. Click any yellow box to select
+ everything within that HTML tag or element. Double-click any yellow box to
+ display the
+ <a href="#using_the_advanced_property_editor">Advanced Property Editor</a>
+ dialog box for that HTML tag or element.</p>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="inserting_special_characters">Inserting Special Characters</h2>
+
+<p>To insert special characters such as accent marks, copyrights, or currency
+ symbols:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the special character
+ to appear.</li>
+ <li>Open the Insert menu and choose Characters and Symbols. You see the
+ Insert Character dialog box.</li>
+ <li>Select a category of characters.
+ <ul>
+ <li>If you choose Accent Uppercase or Accent Lowercase, then open the
+ Letter drop-down list and select the letter you wish to apply an
+ accent to. (Note: not all letters have accented forms.) Select
+ Common Symbols to insert special characters such as copyright symbols
+ or fractions.</li>
+ </ul>
+ </li>
+ <li>From the Character drop-down list, select the character you want to
+ insert.</li>
+ <li>Click Insert.
+
+ <p>You can continue typing in your document (or in a mail compose window)
+ while you keep this dialog box open, in case you want to use it
+ again.</p>
+ </li>
+ <li>Click Close when you are done inserting special characters.</li>
+</ol>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="inserting_html_elements_and_attributes">Inserting HTML Elements and
+ Attributes</h2>
+
+<p>If you understand how to work with HTML source code, you can insert
+ additional tags, style attributes, and JavaScript into your page. If you are
+ not sure how to work with HTML source code, it&apos;s best not to change it.
+ To work with HTML code, use one of these methods:</p>
+
+<ul>
+ <li>Place the insertion point where you want to insert the HTML code, or
+ select the text you want to edit, and then open the Insert menu and choose
+ HTML. In the Insert HTML dialog box, enter HTML tags and text, and then
+ click Insert.</li>
+ <li>Select an element such as a table, named anchor, image, link, or
+ horizontal line. Double-click the element to open the associated properties
+ dialog box for that item. Click Advanced Edit to open the Advanced Property
+ Editor. You can use the Advanced Property Editor to add HTML attributes,
+ JavaScript, and CSS to objects.</li>
+ <li>Open the View menu, and choose HTML Source, or click the &lt;HTML&gt;
+ Source tab in the Edit Mode toolbar at the bottom of the Composer window.
+ (If you don&apos;t see the Edit Mode toolbar, open the View menu and choose
+ Show/Hide; then make sure the Edit Mode Toolbar is checked.)</li>
+</ul>
+
+<h3 id="using_the_advanced_property_editor">Using the Advanced Property
+ Editor</h3>
+
+<p>To add HTML attributes and JavaScript to objects such as tables, images,
+ and horizontal lines, you can use the Advanced Property Editor.</p>
+
+<p><strong>Note</strong>: Unless you clearly understand how to add, delete, or
+ modify HTML attributes and their associated values, it&apos;s best not to do
+ so.</p>
+
+<p>If you are not currently viewing the Advanced Property Editor dialog box,
+ follow these steps:</p>
+
+<ol>
+ <li>From the View menu (or the Edit Mode toolbar), choose HTML Tags.</li>
+ <li>Double-click the object that you want to modify to open its Properties
+ dialog box.</li>
+ <li>Click Advanced Edit to open the object&apos;s Advanced Property Editor.
+ The Advanced Property Editor has three tabs, each of which lists the
+ current properties for the selected object:
+ <ul>
+ <li><strong>HTML Attributes</strong>: Click this tab to view or enter
+ additional HTML attributes.</li>
+ <li><strong>Inline Style</strong>: Click this tab to view or enter
+ additional CSS (cascading style sheet) properties through the
+ &lt;style&gt; attribute. For more information on using CSS styles
+ in Composer, see
+ <a href="#composer">Composer Preferences -Composer</a>.</li>
+ <li><strong>JavaScript Events</strong>: Click this tab to view or enter
+ JavaScript events.</li>
+ </ul>
+ </li>
+ <li>To edit a property or attribute in any of the three lists, select the
+ attribute you want to edit. You can then edit the attribute&apos;s name
+ or value using the editable Attribute and Value fields at the bottom of
+ the dialog box. To add a new attribute, type it in the Attribute field
+ at the bottom of the dialog box. The new attribute is automatically added
+ when you click in the Value field. To remove an attribute, select it in
+ the list, and click Remove Attribute.
+
+ <p><strong>Note</strong>: Required attributes are highlighted in the
+ Attribute list.</p>
+ </li>
+ <li>Click OK to apply your changes to the Advanced Property Editor dialog
+ box.</li>
+ <li>Click OK again to exit the Properties dialog box.</li>
+</ol>
+
+<p>Composer automatically places quotation marks around any attribute text.</p>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="validating_the_html">Validating the HTML</h2>
+
+<p>Before you put your document on a web server so that others can see it, you
+ should first check the document&apos;s HTML formatting to make sure it
+ conforms to web standards. Documents containing validated HTML are less
+ likely to cause problems when viewed by different browsers. Just visually
+ checking your web pages in the browser doesn&apos;t ensure that your document
+ will appear correctly when viewed in other web browsers.</p>
+
+<p>Composer provides a convenient way for you to check that your document
+ conforms to W3C (World Wide Web Consortium) HTML standards. Composer uses
+ the W3C HTML Validation Service, which checks your document&apos;s HTML
+ syntax for compliance with HTML 4.01 standards. This service also provides
+ information on how to correct errors.</p>
+
+<p><strong>Note</strong>: You must be connected to the Internet to use this
+ feature.</p>
+
+<p>To validate your document&apos;s HTML syntax:</p>
+
+<ol>
+ <li>Open the Tools menu, and choose Validate HTML. If you have unsaved
+ changes, Composer asks you to save them before proceeding.</li>
+ <li>When the W3C HTML Validation Service page appears, click <q>Browse</q>
+ and locate the file on your hard disk that you want to validate.</li>
+ <li>Click <q>Check</q>.</li>
+</ol>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="choosing_the_right_editing_mode">Choosing the Right Editing Mode</h2>
+
+<p>Typically, you won&apos;t need to change the editing mode from the default
+ (Normal). However, if you want to work with the document&apos;s HTML source
+ code, you may want to change editing modes.</p>
+
+<p>Composer allows you to quickly switch between four editing modes or views.
+ Each editing mode allows you to continue working on your document, but
+ displays varying levels of HTML tags (and tag icons).</p>
+
+<p>Before you choose an editing mode:</p>
+
+<ul>
+ <li>Open the View menu, choose Show/Hide, and then make sure there is a
+ checkmark next to Edit Mode Toolbar.</li>
+</ul>
+
+<p>The Edit Mode toolbar has four tabs:</p>
+
+<ul>
+ <li><strong>Normal</strong>: Choose this editing mode to see how the document
+ will look online while you are creating it. Choose this mode to
+ show table borders and named anchor icons. All other HTML tag icons
+ are hidden.</li>
+ <li><strong>HTML Tags</strong>: Choose this mode to show all HTML tag
+ icons.</li>
+ <li><strong>&lt;HTML&gt; Source</strong>: Choose this mode to view and edit
+ the document as unformatted HTML source code. When you save the
+ document, the Normal mode reappears.</li>
+ <li><strong>Preview</strong>: Choose this mode to display and edit the
+ document exactly as it would appear in a browser window, except
+ that links and JavaScript functions will not be active.</li>
+</ul>
+
+<p><strong>Note</strong>: JavaScript functions, frames, links, embedded objects
+ and animated GIF files are not active in any of the editing modes. To display
+ these items in their active state, click the Browse button on the Composition
+ toolbar to load the page into a browser window.</p>
+
+<p>[<a href="#formatting_your_web_pages">Return to beginning of
+ section</a>]</p>
+
+<h1 id="adding_tables_to_your_web_page">Adding Tables to Your Web Page</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#inserting_a_table">Inserting a Table</a></li>
+ <li><a href="#changing_a_tables_properties">Changing a Table&apos;s
+ Properties</a></li>
+ <li><a href="#adding_and_deleting_rows_columns_and_cells">Adding and
+ Deleting Rows, Columns, and Cells</a></li>
+ <li><a href="#selecting_table_elements">Selecting Table Elements</a></li>
+ <li><a href="#moving_copying_and_deleting_tables">Moving, Copying, and
+ Deleting Tables</a></li>
+ <li><a href="#converting_text_into_a_table">Converting Text into a
+ Table</a></li>
+ </ul>
+</div>
+
+<h2 id="inserting_a_table">Inserting a Table</h2>
+
+<p>Tables are useful for organizing text, pictures, and data into formatted
+ rows and columns. To insert a table:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the table to appear.</li>
+ <li>Click the Table button
+ <img src="images/table.gif" width="25" height="26" alt="" /> on the
+ Composition toolbar. The Insert Table dialog box appears.</li>
+ <li>Type the number of rows and columns you want.
+ <ul>
+ <li>(Optional) Enter a size for the table width, and select either
+ percentage of the window or pixels.</li>
+ </ul>
+ </li>
+ <li>Enter a number for the border thickness (in pixels); enter zero for no
+ border.
+
+ <p><strong>Note</strong>: Composer uses a red dotted line to indicate
+ tables with a zero border; the dotted line disappears when the page
+ is viewed in a browser.</p>
+ </li>
+ <li>To apply additional table attributes or JavaScript, click Advanced Edit
+ to display the
+ <a href="#using_the_advanced_property_editor">Advanced Property Editor</a>.
+ </li>
+ <li>Click OK to confirm your settings and view your new table.</li>
+</ol>
+
+<p>To change additional properties for your new table, see
+ <a href="#changing_a_tables_properties">Changing a Table&apos;s
+ Properties</a>.
+</p>
+
+<p><strong>Tip</strong>: To insert a table within a table, open the
+ Insert menu and choose Table.</p>
+
+<p>[<a href="#adding_tables_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h2 id="changing_a_tables_properties">Changing a Table&apos;s Properties</h2>
+
+<p>This section describes how to modify properties that apply to an entire
+ table as well as the rows, columns, or individual cells within a table. If
+ you are not currently viewing the Table Properties dialog box, follow these
+ steps:</p>
+
+<ol>
+ <li>Select the table, or click anywhere inside it.</li>
+ <li>Click the Table button
+ <img src="images/table.gif" width="25" height="26" alt="" /> on the
+ toolbar, or open the Table menu and choose Table Properties. The Table
+ Properties dialog box contains two tabs: Table and Cells.</li>
+ <li>Click the Table tab to edit these properties:
+ <ul>
+ <li><strong>Size</strong>: Use this to specify the number of rows and
+ columns. Enter the width of the table and then choose <q>% of
+ window</q> or <q>pixels</q>. If you specify width as a percentage, the
+ table&apos;s width changes whenever the Composer window&apos;s or
+ browser window&apos;s width changes.</li>
+ <li><strong>Borders and Spacing</strong>: Use this to specify, in pixels,
+ the border line width, the space between cells, and the cell padding
+ (the space between the contents of the cell and its border).
+
+ <p><strong>Note</strong>: Composer uses a dotted outline to display
+ tables with a zero border; the dotted line disappears when the page
+ is viewed in a browser.</p>
+ </li>
+ <li><strong>Table Alignment</strong>: Use this to align the table within
+ the page. Choose an option from the drop-down list.</li>
+ <li><strong>Caption</strong>: Choose the caption placement from the
+ drop-down list.</li>
+ <li><strong>Background Color</strong>: Use this to choose a color for
+ the table background, or leave it as transparent.</li>
+ </ul>
+ </li>
+ <li>To apply additional attributes or JavaScript events, click Advanced Edit
+ to display the <a href="#using_the_advanced_property_editor">Advanced
+ Property Editor</a>.
+ </li>
+ <li>Click Apply to preview your changes without closing the dialog box, or
+ click OK to confirm them.</li>
+</ol>
+
+<p>To view, change, or add properties for one or more cells:</p>
+
+<ol>
+ <li>Select the row, column, or cell, then open the Table menu and choose
+ Table Properties. The Table Properties dialog box appears.</li>
+ <li>Click the Cells tab to edit the following properties:
+ <ul>
+ <li><strong>Selection</strong>: Choose Cell, Row, or Column from the
+ drop-down list. Click Previous or Next to move through rows, columns,
+ or cells.</li>
+ <li><strong>Size</strong>: Type a number for Height and Width, and then
+ choose <q>% of table</q> or <q>pixels</q>.</li>
+ <li><strong>Content Alignment</strong>: Select a vertical and horizontal
+ alignment type for the text or data inside each cell.</li>
+ <li><strong>Cell Style</strong>: Select Header from the drop-down list
+ for column or row headers (which centers and bolds the text in the
+ cell); otherwise choose Normal.</li>
+ <li><strong>Text Wrap</strong>: Select <q>Don&apos;t wrap</q> from the
+ drop-down list to keep text from wrapping to the next line unless you
+ insert a paragraph break. Otherwise, choose Wrap.</li>
+ <li><strong>Background Color</strong>: Select a color for the cell
+ background or leave it as transparent.
+
+ <p><strong>Note</strong>: To apply additional attributes or JavaScript
+ events, click Advanced Edit to display the
+ <a href="#using_the_advanced_property_editor">Advanced Property
+ Editor</a>
+ </p>
+ </li>
+ </ul>
+ </li>
+ <li>Click Apply to preview your changes without closing the dialog box, or
+ click OK to confirm them.</li>
+</ol>
+
+<p><strong>Tip</strong>: To change the text color or background color of one
+ or more selected cells or the entire table, select the cells or click
+ anywhere in the table and then click the text color or background color
+ icon in the Format toolbar.</p>
+
+<p><strong>Tip</strong>: To change the color of cells to the color last used,
+ select the cell, then press Shift and click on the background color picker.
+ This is useful when you want to use one color for individual cells.</p>
+
+<p>[<a href="#adding_tables_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h2 id="adding_and_deleting_rows_columns_and_cells">Adding and Deleting Rows,
+ Columns, and Cells</h2>
+
+<p>Composer allows you to quickly add or delete one or more cells, columns,
+ or rows in a table. In addition, you can set options that allow you to
+ maintain the original rectangular structure or layout of the table while
+ you perform editing tasks.</p>
+
+<p>To add a cell, row, or column to your table:</p>
+
+<ol>
+ <li>Click inside the table where you want to add a cell (or cells).</li>
+ <li>Open the Table menu and then choose Insert.</li>
+ <li>Choose one of the cell groupings. (You can also insert a new table
+ within a table cell.)</li>
+</ol>
+
+<p>To delete a cell, row, or column:</p>
+
+<ol>
+ <li>Click a row, column, or cell to place the insertion point. Or,
+ select neighboring cells to delete more than one row at a time. To
+ select neighboring cells, drag over the cells you want to select.
+ To select individual cells in a table, hold down the
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd> key and click
+ on the cells you want to select.</li>
+ <li>Open the Table menu and choose Delete.</li>
+ <li>Choose the item you want to delete.</li>
+</ol>
+
+<p>To join (or merge) a cell with the cell on its right:</p>
+
+<ul>
+ <li>Click inside the cell on the left, open the Table menu, and
+ choose Join with Cell to the Right.</li>
+</ul>
+
+<p>To join (or merge) adjacent cells:</p>
+
+<ul>
+ <li>Select adjacent cells by dragging over them.</li>
+ <li>Open the Table menu, and choose Join Selected Cells.</li>
+</ul>
+
+<p>To split a joined cell back into two or more cells:</p>
+
+<ul>
+ <li>Click inside the joined cell, open the Table menu, and then
+ choose Split Cell. Composer puts the entire contents of the joined
+ cell into the first of the two cells.</li>
+</ul>
+
+<p>Refer to <a href="#selecting_table_elements">Selecting Table Elements</a>
+ for information on how to select non-adjacent cells, rows, and
+ columns.</p>
+
+<h3 id="changing_the_default_table_editing_behavior">Changing the Default Table
+ Editing Behavior</h3>
+
+<p>By default, when you delete one or more cells, Composer preserves the
+ table&apos;s structure by adding cells at the end of a row, wherever
+ needed. This allows you to delete one or more cells but still maintain
+ the table&apos;s original rectangular layout, or structure. Otherwise,
+ deleting cells can result in a table with empty spaces, or whose outline
+ appears irregular due to an uneven number of cells.</p>
+
+<p>To change the default table editing behavior, begin from the Composer
+ window:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu, choose Preferences, and then choose
+ Composer.</li>
+ <li>Under Editing, set the following preference:
+ <ul>
+ <li>Make sure that <q>Maintain table layout when inserting or
+ deleting cells</q> is checked to ensure that you don&apos;t get an
+ irregularly shaped table.</li>
+ </ul>
+ </li>
+ <li class="win">Click OK.</li>
+</ol>
+
+<p>[<a href="#adding_tables_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h2 id="selecting_table_elements">Selecting Table Elements</h2>
+
+<p>You can use one of two ways to quickly select a table, cell, or group of
+ cells:</p>
+
+<ul>
+ <li>Click in the table, open the Table menu, choose Select, and then choose
+ an item from the submenu. For example, to select a table, click anywhere
+ inside the table, open the Table menu, choose Select, and then choose
+ Table.</li>
+ <li>Or, you can use the mouse as a selection tool:
+ <ul>
+ <li>To select a group of adjacent cells: click in a cell, and then
+ drag to select the cells you want. Drag the mouse left or right to
+ select a row; up or down to select a column.</li>
+ <li>To select non-adjacent cells: press <kbd class="mac">Cmd</kbd>
+ <kbd class="noMac">Ctrl</kbd> and then click inside a cell. Keep
+ pressing <kbd class="mac">Cmd</kbd> <kbd class="noMac">Ctrl</kbd>
+ as you click to select additional cells.</li>
+ <li>To extend a selection to include adjacent cells: click inside a
+ cell and then drag over additional cells to extend the selection.</li>
+ <li>To select one or more adjacent columns or rows: drag up or down
+ to select the first column or row, and then drag left or right to
+ select additional adjacent columns or rows. Press <kbd>Shift</kbd>
+ and drag to the right to select an entire row. Press <kbd>Shift</kbd>
+ and drag up or down to select an entire column.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#adding_tables_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h2 id="moving_copying_and_deleting_tables">Moving, Copying, and Deleting
+ Tables</h2>
+
+<p>To move a table:</p>
+
+<ol>
+ <li>Click inside the table.</li>
+ <li>Open the Table menu, choose Select, and then choose Table.</li>
+</ol>
+
+<ul>
+ <li>To copy or move the table: Use the Edit menu&apos;s cut, copy, and
+ paste options.</li>
+ <li>To delete the table: Open the Table menu again, choose Delete, and
+ then choose Table.</li>
+</ul>
+
+<p>[<a href="#adding_tables_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h2 id="converting_text_into_a_table">Converting Text into a Table</h2>
+
+<p>To convert text into a table:</p>
+
+<ol>
+ <li>Select the text that you want to convert into a table. Keep in mind that
+ Composer creates a new table row for each paragraph in the selection.</li>
+ <li>Open the Table menu and choose Create Table from Selection. You see the
+ Convert to Table dialog box.</li>
+ <li>Choose the character Composer uses to separate the selection into
+ columns, or specify a different character to use. If you choose Space as
+ the separator for columns, choose whether or not you want Composer to
+ ignore multiple space and treat them as one space.</li>
+ <li>Leave <q>Delete separator character</q> checked to have Composer remove
+ the separator character when it converts the text into a table. If you
+ don&apos;t want Composer to delete the separator character, uncheck this
+ option.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p><strong>Note</strong>: Text formatting is removed when the selected text
+ is converted to a table.</p>
+
+<p>[<a href="#adding_tables_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h1 id="adding_images_to_your_web_page">Adding Pictures (Images) to Your Web
+ Page</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#inserting_an_image_into_your_page">Inserting an Image into
+ Your Page</a></li>
+ <li><a href="#editing_image_properties">Editing Image Properties</a></li>
+ </ul>
+</div>
+
+<h2 id="inserting_an_image_into_your_page">Inserting an Image into Your
+ Page</h2>
+
+<p>You can insert GIF, JPEG, BMP, and PNG (Portable Network Graphics) images
+ into your web page. You can also use them to
+ <a href="#using_images_as_links">create links</a>. When you insert an image,
+ Composer saves a reference to the image in your page.</p>
+
+<p><strong>Note</strong>: If you plan to publish your pages to the web,
+ it&apos;s best not to use BMP images in your pages.</p>
+
+<p><strong>Tip</strong>: It&apos;s best to first save or publish your page
+ before you insert images into it. This allows Composer to automatically
+ use relative references to images once you insert them.</p>
+
+<p>To insert an image:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the image to
+ appear.</li>
+ <li>Click the Image button
+ <img src="images/image.gif" width="23" height="25" alt="" />
+ on the toolbar, or open the Insert menu and choose Image. You see the
+ Image Properties dialog box.</li>
+ <li>Type the location and filename of the image file, or click Choose File
+ to search for an image file on your hard drive or network.</li>
+ <li>Type a simple description of your image as the alternate text that will
+ appear in text-only browsers (as well as other browsers) when an image is
+ loading or when image loading is disabled.
+
+ <p>Alternatively, you can choose not to include alternate text.</p>
+ </li>
+ <li>If needed, click other tabs so you can adjust the settings (for
+ example, alignment) in the
+ <a href="#editing_image_properties">Image Properties</a> dialog box.</li>
+</ol>
+
+<p><strong>Tip</strong>: To quickly insert an image: Drag and drop it onto
+ your page.</p>
+
+<p><strong>Tip</strong>: To insert a line break after all images in a
+ paragraph, choose Break Below Images from the Insert menu.</p>
+
+<p>[<a href="#adding_images_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h2 id="editing_image_properties">Editing Image Properties</h2>
+
+<p>Once you&apos;ve inserted an image into your page, you can edit its
+ properties and customize the layout in your page, such as the height,
+ width, spacing, and text alignment. If you are not currently viewing
+ the Image Properties dialog box, follow these steps:</p>
+
+<ol>
+ <li>Double-click the image, or select it and click the Image button
+ <img src="images/image.gif" width="23" height="25" alt="" /> on the toolbar
+ to display the Image Properties dialog box.</li>
+ <li>Click the Location tab to edit these properties:
+ <ul>
+ <li><strong>Image Location</strong>: Type the filename and location of
+ the image file. Click Choose File to search for an image file on your
+ hard drive or network.</li>
+ <li><strong>URL is relative to page location</strong>: If checked,
+ Composer converts the URL to be relative to the page&apos;s location.
+ This is especially useful if you plan to publish your pages on a web
+ server so that others can view them. Using relative URLs allows you
+ to keep all your linked files in the same place relative to each other,
+ regardless of their location on your hard disk or a web server.
+
+ <p>Unchecking this box causes Composer to convert the URL to a full
+ (absolute) URL. You typically use absolute URLs when linking to
+ images on other web servers (not stored locally on your hard
+ disk).</p>
+
+ <p>If you have never saved or published the page, you must first save
+ the page in order to enable this checkbox. (This checkbox is not
+ available if you open the Image Properties dialog box in a message
+ compose window.)</p>
+ </li>
+ <li><strong>Attach this image to the message</strong>: If checked,
+ the image is attached to the message you are sending. If unchecked, a
+ link to the image location is inserted instead. (This checkbox is only
+ available if you open the Image Properties dialog box in a message
+ compose window.)</li>
+ <li><strong>Alternate Text</strong>: Enter text that will display in
+ place of the original image; for example, a caption or a brief
+ description of the image. It&apos;s a good practice to specify
+ alternate text for readers who use text-only web browsers or who have
+ image loading turned off.</li>
+ <li><strong>Don&apos;t use alternate text</strong>: Choose this option
+ if the image does not require alternate text or if you don&apos;t want
+ to include it.</li>
+ </ul>
+ </li>
+ <li>Click the Dimensions tab to edit these properties:
+ <ul>
+ <li><strong>Actual Size</strong>: Select this option to undo any changes
+ you&apos;ve made to the dimensions and return the image to its original
+ size.</li>
+ <li><strong>Custom Size</strong>: Select this option and specify the new
+ height and width, in pixels or as a percentage. This setting
+ doesn&apos;t affect the original image file, just the image inserted
+ in your page.</li>
+ <li><strong>Constrain</strong>: If you change the image size, it&apos;s
+ a good idea to select this in order to maintain the image&apos;s aspect
+ ratio (so that it doesn&apos;t appear distorted). If you choose this
+ option, then you only need to change the height or width, but not
+ both.</li>
+ </ul>
+ </li>
+ <li>Click the Appearance Tab to edit these properties:
+ <ul>
+ <li><strong>Spacing</strong>: Specify the amount of space surrounding
+ the image; between the image and adjoining text. You can also put a
+ solid black border around the image and specify its width in pixels.
+ Specify zero for no border.</li>
+ <li><strong>Align Text to Image</strong>: If you&apos;ve placed your
+ image next to any text, select an alignment icon to indicate how you
+ want text positioned relative to the image.</li>
+ <li><strong>Image Map</strong>: Click Remove to remove any image map
+ settings.</li>
+ </ul>
+ </li>
+ <li>Click the Link tab to edit these properties:
+ <ul>
+ <li><strong>Enter a web page location</strong>: If you want to define
+ a link for this image, enter the URL of a remote or local page, or
+ select a named anchor or heading from the drop-down list. Click Choose
+ File to search for a file on your hard drive or network.</li>
+ <li><strong>URL is relative to page location</strong>: If checked,
+ Composer converts the URL to be relative to the page&apos;s location.
+ This is especially useful if you plan to publish your pages to a web
+ server so that others can view them. Using relative URLs allows you to
+ keep all your linked files in the same place relative to each other,
+ regardless of their location on your hard disk or a web server.
+
+ <p>Unchecking this box causes Composer to convert the URL to a full
+ (absolute) URL. You typically use absolute URLs when linking to files
+ on other web servers (not stored locally on your hard disk).</p>
+
+ <p>If you have unsaved changes, you must first save the page in order
+ to enable this checkbox. (This checkbox is not available if you open
+ the Image Properties dialog box in a message compose window.)</p>
+ </li>
+ <li><strong>Show border around linked image</strong>: If checked,
+ displays the link highlight color around the image.</li>
+ </ul>
+ </li>
+ <li>To apply additional attributes or JavaScript events, click Advanced Edit
+ to display the
+ <a href="#using_the_advanced_property_editor">Advanced Property Editor</a>.
+ </li>
+ <li>Click OK to confirm your changes.</li>
+</ol>
+
+<p>[<a href="#adding_images_to_your_web_page">Return to beginning of
+ section</a>]</p>
+
+<h1 id="adding_mathematical_formulas_to_your_web_page">Adding Mathematical
+ Formulas to Your Web Page</h1>
+
+<h2 id="inserting_a_mathematical_formula_into_your_page">Inserting a
+ Mathematical Formula into Your Web Page</h2>
+
+<p>&brandShortName; Composer allows you to write mathematical formulas, encoded
+ in your web page as <a href="glossary.xhtml#mathml">MathML</a> and
+ generated from a convenient
+ <a href="glossary.xhtml#latex">LaTeX</a>-like syntax.</p>
+
+<p>To insert a formula:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the formula to
+ appear.</li>
+ <li>Open the Insert menu and choose Math. You see the Math Insert dialog
+ box.</li>
+ <li>Enter your LaTeX source code, for example <tt>\frac{\sqrt{\pi}}{3}</tt>
+ to write
+ <math display="inline" xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mfrac><msqrt><mi>Ï€</mi></msqrt><mn>3</mn></mfrac><annotation encoding="TeX">\frac{\sqrt{\pi}}{3}</annotation></semantics></math>.
+ In order to help you, a preview of the formula as well as a panel of
+ predefined constructions are available. For details, see
+ <a href="#editing_the_latex_source">Editing the LaTeX source</a>.
+ </li>
+ <li>Choose the style for your formula:
+ <ul>
+ <li><strong>Inline mode</strong>: If selected, the formula is inserted
+ inside the current paragraph of text and rendered in a way that
+ minimizes its height as much as possible. This mode is typically used
+ for small expressions. For example this is an expression in inline mode:
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mo>∫</mo><mi>D</mi></msub><mi>x</mi><mi>d</mi><mi>x</mi></mrow><annotation encoding="TeX">\int_D x dx</annotation></semantics></math>.</li>
+ <li><strong>Display mode</strong>: If selected, the formula is inserted
+ in its own block and uses the standard rendering. This mode is typically
+ used for large expressions. For example this is an expression in display
+ mode:
+ <math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mo>∫</mo><mi>a</mi><mi>b</mi></msubsup><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><mspace width="thinmathspace"/><mi>d</mi><mi>x</mi></mrow><annotation encoding="TeX">\int_a^b {f(x)}\, dx</annotation></semantics></math></li>
+ </ul>
+ </li>
+ <li>Choose the overall direction of your formula:
+ <ul>
+ <li><strong>Left-to-right direction</strong>: If selected, the formula
+ will be laid out from left to right, e.g. <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><msqrt><mi>x</mi></msqrt><annotation encoding="TeX">\sqrt{x}</annotation></semantics></math>.
+ This is the standard orientation in most countries.</li>
+ <li><strong>Right-to-left direction</strong>: If selected, the formula
+ will be laid out from right to left, e.g.
+ <math xmlns="http://www.w3.org/1998/Math/MathML" dir="rtl"><semantics><msqrt><mi>ج</mi></msqrt><annotation encoding="TeX">\sqrt{ج}</annotation></semantics></math>.
+ This is the standard orientation in some Arabic countries.</li>
+ </ul>
+ </li>
+ <li>Verify that the LaTeX source does not contain any syntax error and click
+ Insert to create your new formula.</li>
+</ol>
+
+<p>To modify a mathematical formula, move the insertion point inside it.
+ Then open the context menu and choose Advanced Properties to
+ <a href="#editing_the_latex_source">edit the LaTeX source</a> again.
+ Alternatively, you can open the Insert menu and choose Math.
+</p>
+
+<p><strong>Note</strong>: &brandShortName; Composer attaches the LaTeX source to
+ the generated MathML expressions so that they can be edited again. In
+ general it is currently not possible to edit an arbitrary MathML expression
+ unless it has a LaTeX expression associated that is supported by
+ &brandShortName; Composer.
+</p>
+
+<h2 id="editing_the_latex_source">Editing the LaTeX Source</h2>
+
+<p>The Insert Math box contains a text field where you can enter mathematical
+ expressions using a <a href="glossary.xhtml#latex">LaTeX</a>-like syntax.
+ If you are not familiar with LaTeX, here are the basics:
+</p>
+
+<ul>
+ <li>Use standard plain text notations for simple expressions:
+ <tt>x_1 + 2^y</tt> generates
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>1</mn></msub><mo>+</mo><msup><mn>2</mn><mi>y</mi></msup></mrow><annotation encoding="TeX">x_1 + 2^y</annotation></semantics></math>.
+ </li>
+ <li>Use braces to define groups: <tt>2^x+y</tt> generates
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mi>x</mi></msup><mo>+</mo><mi>y</mi></mrow><annotation encoding="TeX">2^x+y</annotation></semantics></math>
+ while <tt>2^{x+y}</tt> generates
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><msup><mn>2</mn><mrow><mi>x</mi><mo>+</mo><mi>y</mi></mrow></msup><annotation encoding="TeX">2^{x+y}</annotation></semantics></math>
+ </li>
+ <li>Use LaTeX commands of the form
+ <tt>\commandname[option1,option2,...] argument1 argument2 ...</tt> to define
+ complex expressions: <tt>\gamma</tt>, <tt>\frac a b</tt>, <tt>\sqrt x</tt>
+ and <tt>\sqrt[3]x</tt> generate
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mi>γ</mi><annotation encoding="TeX">\gamma</annotation></semantics></math>,
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mfrac><mi>a</mi><mi>b</mi></mfrac><annotation encoding="TeX">\frac a b</annotation></semantics></math>,
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><msqrt><mi>x</mi></msqrt><annotation encoding="TeX">\sqrt x</annotation></semantics></math> and
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mroot><mi>x</mi><mn>3</mn></mroot><annotation encoding="TeX">\sqrt[3] x</annotation></semantics></math> respectively.
+ </li>
+ <li>Use LaTeX environments
+ of the form <tt>\begin{environmentname} ... \end{environmentname}</tt>
+ to define blocks with a special syntax. This is typically used for arrays:
+ <tt>\begin{matrix} a &amp; b &amp; c \\ d &amp; e &amp; f \end{matrix}</tt>
+ generates
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mtable displaystyle="false" rowspacing="0.5ex"><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd><mtd><mi>c</mi></mtd></mtr><mtr><mtd><mi>d</mi></mtd><mtd><mi>e</mi></mtd><mtd><mi>f</mi></mtd></mtr></mtable><annotation encoding="TeX">\begin{matrix}a &amp; b &amp; c \\ d &amp; e &amp; f \end{matrix}</annotation></semantics></math>.
+ </li>
+ <li>Use special commands to escape characters:
+ <tt>\backslash \&amp; \{ \}</tt> generates
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>\</mo><mi>&amp;</mi><mo stretchy="false">{</mo><mo stretchy="false">}</mo></mrow><annotation encoding="TeX">\backslash \&amp; \{ \}</annotation></semantics></math>.
+ </li>
+</ul>
+
+<p>There exists a large collection of LaTeX commands and there is not any
+ finite and well-defined list of them. &brandShortName; Composer only supports
+ the <a href="https://github.com/fred-wang/TeXZilla/wiki/TeXZilla-Commands">TeXZilla commands</a>
+ which are themselves based on the <a href="https://golem.ph.utexas.edu/~distler/blog/itex2MMLcommands.html">itex2MML commands</a>
+ and should cover the most popular constructions. One of the nice features of
+ TeXZilla is that it accepts arbitrary characters as input so that
+ <tt>\left⌊α^2\right⌋</tt> is equivalent to the more verbose
+ <tt>\left\lfloor\alpha^2\right\rfloor</tt> and generates
+ <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>⌊</mo><msup><mi>α</mi><mn>2</mn></msup><mo>⌋</mo></mrow><annotation encoding="TeX">\left⌊α^2\right⌋</annotation></semantics></math>.</p>
+
+<p>You do not need to know all the LaTeX commands to edit formulas. Instead, a
+ panel of predefined expressions is available. Click any of the panel buttons
+ to insert the corresponding LaTeX expression. Arguments in LaTeX expressions
+ are represented by ellipses. You can just edit the LaTeX source to
+ replace them with the actual content you want.</p>
+
+<p><strong>Tip</strong>: If you select a subexpression in the LaTeX source field
+ and use the construction panel to insert a LaTeX expression with at least one
+ argument, that subexpression will be used as the first argument of the
+ new expression.</p>
+
+<p>[<a href="#adding_mathematical_formulas_to_your_web_page">Return to
+ beginning of section</a>]</p>
+
+<h1 id="setting_page_properties">Setting Page Properties</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#setting_page_properties_and_meta_tags">Setting Page
+ Properties and Meta Tags</a></li>
+ <li><a href="#setting_page_colors_and_backgrounds">Setting Page Colors and
+ Backgrounds</a></li>
+ </ul>
+</div>
+
+<h2 id="setting_page_properties_and_meta_tags">Setting Page Properties and Meta
+ Tags</h2>
+
+<p>Use the Page Properties dialog box to enter properties such as the title,
+ author, and description of the document you&apos;re currently working on.
+ This information is useful if you plan to use the page on a website, since
+ search engines use this type of information to index your page. You can view
+ this information from the browser window by opening the View menu and
+ choosing Page Info.</p>
+
+<ol>
+ <li>Open the Format menu and choose Page Title and Properties.</li>
+ <li>Edit any of the following properties:
+ <ul>
+ <li><strong>Title</strong>: Type the text you want to appear as the
+ window title when someone views the page through a browser. This
+ is how most web search tools locate web pages, so choose a title
+ that conveys what your page is about.</li>
+ <li><strong>Author</strong>: Type the name of the person who created the
+ document. This information is helpful to readers who locate the
+ document by using a web search tool to search on name.
+
+ <p><strong>Tip</strong>: If you enter the Author name in
+ Composer&apos;s <a href="#new_page_settings">preferences</a>, then
+ you won&apos;t have to enter it each time you create a new page.</p>
+ </li>
+ <li><strong>Description</strong>: Enter a brief description of the
+ document&apos;s contents.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>[<a href="#setting_page_properties">Return to beginning of section</a>]</p>
+
+<h2 id="setting_page_colors_and_backgrounds">Setting Page Colors and
+ Backgrounds</h2>
+
+<p>You can change the background color or specify a background
+ image for the page you&apos;re currently working on. These choices
+ affect the way text and links in your page appear to people viewing
+ the page through a browser.</p>
+
+<p>To set the colors and background for the current page, begin
+ from the Composer window:</p>
+
+<ol>
+ <li>Open the Format menu and choose Page Colors and Background.</li>
+ <li>Edit any of the following properties:
+ <ul>
+ <li><strong>Reader&apos;s default colors</strong>: Select this if you
+ want your page to use the color settings from the viewer&apos;s browser
+ for text and links.</li>
+ <li><strong>Use custom colors</strong>: Select this if you want to
+ specify the colors of text and links. For each element, select a color
+ from the Color selection dialog. Sample output for each type of link
+ appears in the pane on the right.</li>
+ <li><strong>Background image</strong>: Select this if you want the
+ background of your page to be an image. Type the name of the image
+ file or click Choose File to locate the image file on your hard
+ drive or network.
+
+ <p><strong>Note</strong>: Background images are tiled and override
+ background color selections.</p>
+ </li>
+ <li><strong>URL is relative to page location</strong>: If checked,
+ Composer converts the URL to be relative to the page&apos;s location.
+ This is especially useful if you plan to publish your pages on a web
+ server so that others can view them.
+
+ <p>Using relative URLs allows you to keep all your linked files in
+ the same place relative to each other, regardless of their location
+ on your hard disk or a web server.</p>
+
+ <p>Deselecting this option causes Composer to convert the URL to a
+ full (absolute) URL. You typically use absolute URLs when linking
+ to images on other web servers (not stored locally on your hard
+ disk).</p>
+
+ <p>If you have unsaved changes, you must first save the page in
+ order to enable this checkbox.</p>
+ </li>
+ </ul>
+ </li>
+</ol>
+
+<p><strong>Note</strong>: To apply additional attributes or JavaScript events,
+ click Advanced Edit to display the
+ <a href="#using_the_advanced_property_editor">Advanced Property
+ Editor</a>.</p>
+
+<p>You can also set the <a href="#new_page_settings">default page
+ background and colors</a> for every new page you create in Composer.</p>
+
+<p>[<a href="#setting_page_properties">Return to beginning of section</a>]</p>
+
+<h1 id="creating_links_in_composer">Creating Links in Composer</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#creating_links_within_the_same_page">Creating Links Within
+ the Same Page</a></li>
+ <li><a href="#creating_links_to_other_pages">Creating Links to Other
+ Pages</a></li>
+ <li><a href="#using_images_as_links">Using Images as Links</a></li>
+ <li><a href="#removing_or_discontinuing_links">Removing or Discontinuing
+ Links</a></li>
+ </ul>
+</div>
+
+<h2 id="creating_links_within_the_same_page">Creating Links Within the Same
+ Page</h2>
+
+<p>To create a link within the same page, for example a link that the reader
+ can use to jump from one section to another, you must create an
+ <em>anchor</em> (target location), and then create a link that points to the
+ anchor. Anchors are also called <em>named anchors</em>. To create an anchor,
+ follow these steps:</p>
+
+<ol>
+ <li>Click to place the insertion point at the beginning of a line where you
+ want to create an anchor, or select some text.</li>
+ <li>Open the Insert menu and choose Named Anchor. You see the Named Anchor
+ Properties dialog box.</li>
+ <li>Type a unique name for the anchor in the Anchor Name field (up to 30
+ characters). If you include spaces, they will be converted to underscores
+ ( _ ). If you selected some text in step 1, this box already contains a
+ name.</li>
+ <li>Click OK. An anchor icon appears in your document to mark the
+ anchor&apos;s location:
+ <img src="images/anchor-in-doc.gif" width="20" height="17" alt="" /></li>
+</ol>
+
+<p>To create the link on which readers can click to jump to the object:</p>
+
+<ol>
+ <li>Select the text or image that you want to link to the anchor.</li>
+ <li>Click the Link button or open the Insert menu and choose Link. You see
+ the Link Properties dialog box.
+ <ul>
+ <li>If you&apos;re creating a link to an HTML file on your computer,
+ click Choose File to locate it.</li>
+ <li>If you&apos;re creating a link to a named anchor (target), select
+ it from the list of the anchors currently available in the page.</li>
+ <li>If you&apos;re creating a link to a level heading (for example,
+ Heading 1 - Heading 6), select it from the list of headings currently
+ available in the page.</li>
+ </ul>
+ </li>
+ <li>Click OK.</li>
+</ol>
+
+<p><strong>Note</strong>: To test the link you just created, open the File
+ menu and choose Browse Page, then click the link.</p>
+
+<p><strong>Tip</strong>: If you did not first create named anchors, you can
+ use the Link dialog box to create links to headings that already occur in
+ the page.</p>
+
+<p>[<a href="#creating_links_in_composer">Return to beginning of
+ section</a>]</p>
+
+<h2 id="creating_links_to_other_pages">Creating Links to Other Pages</h2>
+
+<p>You can create links from your page to local pages on your own computer or
+ on your workplace&apos;s network, or to remote pages on the Internet.</p>
+
+<p><strong>Tip</strong>: It&apos;s best to first save or publish your page
+ before you create links to other pages. This allows Composer to automatically
+ use relative references for links once you create them.</p>
+
+<p>To create a link to another page:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want to create a link, or
+ select the text or image that you want to link to the anchor.</li>
+ <li>Click the Link button. You see the Link Properties dialog box.</li>
+ <li>Define your link:
+ <ul>
+ <li><strong>Link text</strong>: If you&apos;ve already selected an image
+ file or text before clicking the Link button, the selected text or
+ file will be entered here. Otherwise, you must enter the text that you
+ want to use as the link.</li>
+ <li><strong>Link Location</strong>: Type the local path and filename or
+ remote URL of the page you want to link to. If you&apos;re not sure of
+ the path and filename for a local file, click Choose File to look for
+ it on your hard disk or network. For remote URLs, you can copy the URL
+ from the browser&apos;s Location Bar. Alternatively, you can select a
+ named anchor or a heading in the current page that you want to link
+ to.</li>
+ <li><strong>URL is relative to page location</strong>: If checked,
+ Composer converts the URL to be relative to the page&apos;s location.
+ This is especially useful if you plan to publish your pages to a web
+ server so that others can view them. Using relative URLs allows you to
+ keep all your linked files in the same place relative to each other,
+ regardless of their location on your hard disk or a web server.
+
+ <p>Deselecting this option causes Composer to convert the URL to a full
+ (absolute) URL. You typically use absolute URLs when linking to pages
+ on other web servers (not stored locally on your hard disk).</p>
+
+ <p>If you have unsaved changes, you must first save the page in order
+ to enable this checkbox. (This checkbox is not available if you open
+ the Link Properties dialog box in a message compose window.)</p>
+ </li>
+ <li><strong>Attach the source of this link to the message</strong>: If
+ checked, the source of the specified link is added as an attachment to
+ the message you are sending. If unchecked, just a link to the location
+ is inserted instead. (This checkbox is only available if you open the
+ Link Properties dialog box in a message compose window.)</li>
+ </ul>
+ </li>
+ <li>To apply additional attributes or JavaScript events, click Advanced Edit
+ to display the
+ <a href="#using_the_advanced_property_editor">Advanced Property Editor</a>.
+ </li>
+ <li>Click OK.</li>
+ <li>To test the link you just created, click the Browse button and then click
+ the link to make sure it works as expected.</li>
+</ol>
+
+<p><strong>Tip</strong>: You can copy a link quickly by clicking and dragging
+ the link from another window and then dropping it onto your page. For
+ example, you can click and drag a link from a web page, bookmark, or Mail
+ window and drop it onto your page. You can also right-click<span class="mac">
+ or, if you have a one-button mouse, <kbd>Ctrl</kbd>-click</span> a link on a
+ web page and choose Copy Link Location from the menu. Then you can paste the
+ link location into the Link Location field in the Link Properties dialog
+ box.</p>
+
+<p>[<a href="#creating_links_in_composer">Return to beginning of
+ section</a>]</p>
+
+<h2 id="using_images_as_links">Using Images as Links</h2>
+
+<p>You can make images, such as JPEG, GIF, or PNG files, behave like links in
+ your pages. When the reader clicks a linked image, the browser window
+ displays the page that the image is linked to.</p>
+
+<p>To make an image behaving like a link:</p>
+
+<ol>
+ <li>Select an image on your page.</li>
+ <li>Click the Link button
+ <img src="images/link.gif" width="22" height="20" alt="" />
+ on the toolbar, or open the Insert menu and choose Link.</li>
+ <li>Use the Link Properties dialog box to link the image to a
+ <a href="#creating_links_within_the_same_page">named anchor or heading
+ within the page</a>, or to a
+ <a href="#creating_links_to_other_pages">separate local or remote page</a>.
+ </li>
+</ol>
+
+<p><strong>Tip</strong>: Drag and drop a linked image from the browser window
+ into a Composer window to copy both the image and the link.</p>
+
+<p><strong>Note</strong>: To remove the blue border that can appear around
+ images used as links:</p>
+
+<ol>
+ <li>Select the linked image.</li>
+ <li>Open the Format menu and choose Image and Link Properties.</li>
+ <li>In the dialog box, select the Link tab.</li>
+ <li>Uncheck <q>Show border around linked image</q>.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>[<a href="#creating_links_in_composer">Return to beginning of
+ section</a>]</p>
+
+<h2 id="removing_or_discontinuing_links">Removing or Discontinuing Links</h2>
+
+<p>To remove a link:</p>
+
+<ol>
+ <li>Select the linked text (normally blue and underlined) or image.</li>
+ <li>Open the Format menu and choose Remove Links.</li>
+</ol>
+
+<p>To discontinue a link, so that text you type after the link is not included
+ as part of the link:</p>
+
+<ol>
+ <li>Click to place the insertion point where you want the link to end.</li>
+ <li>Open the Format menu and choose Discontinue Link.</li>
+</ol>
+
+<p>[<a href="#creating_links_in_composer">Return to beginning of
+ section</a>]</p>
+
+<h1 id="publishing_your_pages_on_the_web">Publishing Your Pages on the Web</h1>
+
+<p>If your pages exist only on your local hard disk, you can browse your pages,
+ but no one else can. Composer lets you publish your pages to a remote
+ computer called a web server.</p>
+
+<p>When you publish your pages to a web server, Composer copies (uploads) your
+ pages to a computer that lets others browse your pages. Most ISPs provide
+ space on their web servers for web page publishing. To find a web server
+ where you can publish your pages, ask your ISP, help desk, or system
+ administrator.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#publishing_a_document">Publishing a Document</a></li>
+ <li><a href="#updating_a_published_document">Updating a Published
+ Document</a></li>
+ <li><a href="#changing_the_filename_or_publishing_location">Changing the
+ Filename or Publishing Location</a></li>
+ <li><a href="#creating_a_new_publishing_site">Creating a New Publishing
+ Site</a></li>
+ <li><a href="#choosing_the_default_publishing_site">Choosing the Default
+ Publishing Site</a></li>
+ <li><a href="#deleting_a_publishing_site">Deleting a Publishing
+ Site</a></li>
+ <li><a href="#solving_common_publishing_problems">Solving Common Publishing
+ Problems</a></li>
+ <li><a href="#publishing_settings">Publishing Settings</a></li>
+ </ul>
+</div>
+
+<h2 id="publishing_a_document">Publishing a Document</h2>
+
+<p><strong>Tip</strong>: It&apos;s best to first save or publish your page
+ before you insert links or images into it. This allows Composer to
+ automatically use relative references for links and images once you insert
+ them.</p>
+
+<p>To publish a document:</p>
+
+<ol>
+ <li>Open the HTML document that you want to publish, or create a new Composer
+ document.</li>
+ <li>When you&apos;re ready to publish the document remotely, click the
+ Publish button.
+
+ <p>If you have published this document before, Composer remembers the
+ document&apos;s publishing settings and starts publishing the document.
+ While publishing is in progress, Composer displays a publishing status
+ dialog box.</p>
+ <ul>
+ <li>If you have never published this document before, Composer displays
+ the Settings tab in the Publish Page dialog box so you can enter
+ information about the document&apos;s remote publishing location. See
+ <a href="#publish_page_settings">Publish Page - Settings</a> for more
+ information. When you&apos;re done entering information, click Publish.
+ </li>
+ <li>If you have never saved the document, Composer displays the Publish
+ tab in the Publish Page dialog box, so you can enter the
+ document&apos;s filename. See
+ <a href="#publish_page_publish">Publish Page - Publish</a> for more
+ information. After entering the filename, click Publish.</li>
+ </ul>
+ </li>
+ <li>To browse your published page, click the Browse button. Test the
+ page&apos;s links and make sure there are no missing images.</li>
+ <li>Continue editing the page as necessary. When you&apos;re ready to update
+ the remote page with your changes, click the Publish button.</li>
+</ol>
+
+<p>When you publish a document for the first time, Composer changes the
+ document&apos;s <tt>file:///</tt> URL to an <tt>http://</tt> URL to indicate
+ that you are now editing the published document. If you want to save the
+ document locally (on your computer&apos;s hard disk), click the Save button.
+ You&apos;ll be prompted to choose a filename and location on your hard disk
+ for the document.</p>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h3 id="tips_for_avoiding_broken_links_or_missing_images">Tips for Avoiding
+ Broken Links or Missing Images</h3>
+
+<ul>
+ <li>Make sure your Composer filenames end with the .html or .htm file
+ extension. Make sure your image filenames end with the .JPG, .GIF, or .PNG
+ file extension. Don&apos;t use spaces or other special symbols in your
+ filenames. Keep your filenames short and only use lowercase or uppercase
+ letters and numbers.</li>
+ <li>If your images appear as broken links when you browse a document on the
+ web server, you may have forgotten to include the images when you
+ published. Open the File menu, and choose Publish As to display the Publish
+ Page dialog box. In the Publish tab, make sure you check <q>Include images
+ and other files</q> and then click Publish.</li>
+</ul>
+
+<p>For more troubleshooting tips, see
+ <a href="#solving_common_publishing_problems">Solving Common Publishing
+ Problems</a>.
+</p>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="updating_a_published_document">Updating a Published Document</h2>
+
+<p>To update a published document:</p>
+
+<ol>
+ <li>In a Composer window, open the File menu, and choose Recent Pages, then
+ select the document from the list. <p>Alternatively, browse to the location
+ of the document you want to update by entering the document&apos;s HTTP
+ address (the document&apos;s web address) in the browser&apos;s
+ Location Bar.</p></li>
+ <li>Edit the document as necessary.</li>
+ <li>When you&apos;re ready to update the remote page with your changes, click
+ Publish in Composer&apos;s toolbar.</li>
+</ol>
+
+<p><strong>Tip</strong>: To delete a page or image you&apos;ve published on a
+ web server, you must use an <a href="glossary.xhtml#ftp">FTP (File Transfer
+ Protocol)</a> program. You also must use an FTP program if you want to create
+ subdirectories or to rename files on the web server. Ask your service
+ provider if they recommend a particular FTP program. You can usually find
+ information on FTP programs in the Help or Support sections of your service
+ provider&apos;s website. FTP programs are also available from shareware sites
+ such as ZDNet Downloads.</p>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="changing_the_filename_or_publishing_location">Changing the Filename or
+ Publishing Location</h2>
+
+<p>To change a document&apos;s filename or publishing location:</p>
+
+<ol>
+ <li>In a Composer window, open the File menu, and choose Recent Pages, then
+ select the document from the list.
+
+ <p>Alternatively, browse to the location of the document you want to update
+ by entering the document&apos;s HTTP address (the document&apos;s web
+ address) in the browser&apos;s Location Bar.</p>
+ </li>
+ <li>Edit the document as necessary.</li>
+ <li>Open Composer&apos;s File menu and choose Publish As. Composer displays
+ the Publish tab in the Publish Page dialog box.</li>
+ <li>Enter a different page title, if necessary.</li>
+ <li>Enter a different filename for the page, if necessary.</li>
+ <li>From the Site Name list, choose the publishing location you want to use.
+ To set up a new publishing location, click New Site. See
+ <a href="#publish_page_settings">Publish Page - Settings</a> for more
+ information.</li>
+ <li>Click Publish to save the document to the new location.</li>
+</ol>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="creating_a_new_publishing_site">Creating a New Publishing Site</h2>
+
+<p>If you plan to publish documents to more than one remote location, you can
+ set up Composer to save the publishing information for each remote site you
+ use, so that you don&apos;t have to enter it each time you want to
+ publish.</p>
+
+<p>To create a new publishing site, begin from a Composer window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Publishing Site Settings. Composer
+ displays the Publish Settings dialog box.</li>
+ <li>Click New Site.</li>
+ <li>For <q>Site Name</q>, enter the nickname by which you want to refer to
+ this publishing site.
+
+ <p>For example, if you will use the new site to publish documents
+ related to the <q>Meteor</q> project, you might want to use the site
+ name <q>Meteor</q>. Site names remind you about the types of documents
+ you publish at each site.</p>
+ </li>
+ <li>For <q>Publishing address</q>, enter the complete URL provided to
+ you by your ISP, system administrator, or web hosting service. This
+ URL must begin with either <tt>ftp://</tt> or <tt>http://</tt>.
+
+ <p>The publishing address specifies the location where documents are
+ published (uploaded) at this site. If you are not sure what to enter,
+ ask your ISP or system administrator.</p>
+ </li>
+ <li>For <q>HTTP address of your home page</q>, enter the complete URL
+ that you would enter in the browser to view pages at this
+ site. Do not include a filename or subdirectory as part of the URL.
+
+ <p>This URL must always begin with <tt>http://</tt>. In some cases,
+ this URL is the same as the publishing address. If you are not sure
+ what to enter, ask your ISP or system administrator, or else leave
+ it blank.</p>
+ </li>
+ <li>For <q>user name</q>, enter the user name you use to log in to your ISP
+ or web hosting service.</li>
+ <li>For <q>password</q>, enter the password for your user name.</li>
+ <li>Select <q>Save Password</q> to save your password securely using
+ Password Manager so that you don&apos;t have to enter it each time you
+ publish pages at this site.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="choosing_the_default_publishing_site">Choosing the Default Publishing
+ Site</h2>
+
+<p>If you have set up more than one publishing site, but you typically use
+ only one site for most of your publishing needs, you can designate the site
+ you use most often as the default publishing site. Composer will use the
+ default publishing site for all documents that you publish, unless you
+ specifically choose an alternate site.</p>
+
+<p>Regardless of how many sites you&apos;ve set up, you can always publish a
+ document to a different site by choosing Publish As from Composer&apos;s
+ File menu. See
+ <a href="#changing_the_filename_or_publishing_location">Changing the Filename
+ or Publishing Location</a> for more information.</p>
+
+<p>To choose the default publishing site, begin from a Composer
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu, and choose Publishing Site Settings.
+ Composer displays the Publish Settings dialog box.</li>
+ <li>Select a publishing site from the list.
+
+ <p>If you only have one publishing site set up, Composer uses that
+ one as the default site.</p>
+ </li>
+ <li>Click Set as Default.</li>
+ <li>Click OK to confirm your changes.</li>
+</ol>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="deleting_a_publishing_site">Deleting a Publishing Site</h2>
+
+<p>Deleting a publishing site removes the site&apos;s settings from Composer.
+ If you later wish to publish to the site, you must re-enter the site&apos;s
+ settings.</p>
+
+<p>To delete a publishing site&apos;s settings, begin from a Composer
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu, and choose Publishing Site Settings. Composer
+ displays the Publish Settings dialog box.</li>
+ <li>Select a publishing site from the list.</li>
+ <li>Click Remove Site.
+
+ <p>Composer only removes the site&apos;s settings; the remote site itself
+ is not affected.</p>
+ </li>
+ <li>Click OK to confirm your changes.</li>
+</ol>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="solving_common_publishing_problems">Solving Common Publishing
+ Problems</h2>
+
+<p>If one or more of your files fail to publish, the Publishing
+ Status dialog box displays an error message that can help you
+ determine what went wrong and how to fix it.</p>
+
+<p>If you are still unable to publish a file, save the file to your
+ hard disk by opening Composer&apos;s File menu, and choosing Save. You
+ can then open the file at a later time to try to publish it. To
+ quickly locate the file later, open Composer&apos;s File menu, and
+ choose Recent Pages.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#verifying_your_publishing_settings">Verifying Your Publishing
+ Settings</a></li>
+ <li><a href="#checking_your_filenames">Checking Your Filenames</a></li>
+ <li><a href="#fixing_publishing_errors">Fixing Publishing Errors</a></li>
+ </ul>
+</div>
+
+<h3 id="verifying_your_publishing_settings">Verifying Your Publishing
+ Settings</h3>
+
+<p>To verify your publishing settings:</p>
+
+<ol>
+ <li>Close the Publishing Status dialog box, if it is open.</li>
+ <li>Open the Edit menu and choose Publishing Site Settings.</li>
+ <li>In the Publish Settings dialog box, confirm that the site settings are
+ correct for the site you are trying to publish to. If you&apos;re not
+ sure, check with your ISP or web hosting service.
+ <ul>
+ <li><strong>Verify that you correctly entered the publishing
+ settings</strong>: You may have accidentally mis-typed one of the
+ settings.</li>
+ <li><strong>Verify that you entered the correct publishing
+ address</strong>: Web hosting services or ISPs may refer to the
+ publishing address as the <q>server name</q>, the <q>hostname</q>, or
+ the <q>server/host</q>. They often specify the publishing location as
+ <tt>ftp.myisp.com/username</tt>, where <tt>username</tt> is your
+ user name.
+
+ <p>For the publishing address to be correct, you must precede the
+ publishing location with either <tt>ftp://</tt> or <tt>http://</tt>.
+ For example, the correct publishing address for the above-mentioned
+ site would be <tt>ftp://ftp.myisp.com/username</tt>.</p>
+ </li>
+ </ul>
+ </li>
+</ol>
+
+<h3 id="checking_your_filenames">Checking Your Filenames</h3>
+
+<p>Examine the names of any files that failed to publish. Make sure that the
+ filenames:</p>
+
+<ul>
+ <li>Use only numbers or lowercase or uppercase letters. While it&apos;s
+ acceptable to create filenames that use uppercase letters, you can avoid
+ potential errors in later locating the published file if you only use
+ lowercase letters in your filenames.
+
+ <p>When you publish files to a web server, filenames become
+ case-sensitive on the web server. It may be harder for you to remember
+ files names that use only uppercase letters or that use a mix of
+ uppercase and lowercase letters.</p>
+
+ <p>For example, when you try to locate a published file by typing the
+ filename&apos;s web address into the browser&apos;s Location Bar, you
+ must enter the filename exactly as you created it, using the same
+ combination of uppercase and lowercase letters.</p>
+ </li>
+ <li>Don&apos;t use punctuation characters or spaces. Underscores ( _ )
+ or hyphens ( - ) are OK.</li>
+ <li>End with .html or .htm (for Composer filenames).</li>
+ <li>Use less than 32 characters.</li>
+</ul>
+
+<h3 id="fixing_publishing_errors">Fixing Publishing Errors</h3>
+
+<p>If one or more of your files fails to publish, look at the messages
+ Composer displays in the Publishing Status area of the Publishing dialog box.
+ You can use these error messages to help determine what went wrong and what
+ to do to fix the problem.</p>
+
+<div class="contentsBox" style="background-color: #ebebeb;">
+ <p>Error Messages:</p>
+ <p><a href="#file_not_found"><tt><var>Filename</var> not found.</tt></a></p>
+ <p><a href="#file_not_found"><tt><var>X</var> of <var>Y</var> files failed to
+ publish.</tt></a></p>
+ <p><a href="#subdir_not_found"><tt>The subdirectory <var>directory name</var>
+ doesn&apos;t exist on this site or the filename <var>filename</var> is
+ already in use by another subdirectory.</tt></a></p>
+ <p><a href="#subdir_not_found"><tt>The filename <var>filename</var> is
+ already in use by another subdirectory.</tt></a></p>
+ <p><a href="#server_not_found"><tt>The server is not available. Check your
+ connection and try again later.</tt></a></p>
+ <p><a href="#no_permission"><tt>You do not have permission to publish to this
+ location.</tt></a></p>
+ <p><a href="#offline_error"><tt>You are currently offline. Click the icon
+ near the lower-right corner of any window to go online.</tt></a></p>
+ <p><a href="#no_disk_space"><tt>There is not enough disk space available to
+ save the file <var>filename</var>.</tt></a></p>
+ <p><a href="#name_too_long"><tt>The filename or subdirectory name is too
+ long.</tt></a></p>
+</div>
+
+<div class="errorMessage">
+ <p id="file_not_found"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt><var>Filename</var> not found.</tt>
+ <p>or</p>
+ <tt><var>X</var> of <var>Y</var> files failed to publish.</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: One or more image files or CSS files
+ failed to publish because Composer could not find them. Some typical
+ reasons might be:</p>
+
+ <ul>
+ <li>The file location you typed is incorrect.</li>
+ <li>The file&apos;s location on the web is not accessible.</li>
+ <li>The file&apos;s location was changed or the file was deleted or
+ moved to another location.</li>
+ </ul>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Look for broken images in the page you are trying to publish. Broken
+ images will appear with this icon
+ <img src="images/broken.gif" width="20" height="20" alt="" /> in the
+ page. To correct the image&apos;s address, double-click the broken
+ image to display the Image Properties dialog box so you can enter the
+ correct address.</li>
+ <li>Remove the broken image from the page by selecting it (click once on
+ the image), and then pressing <kbd>Backspace</kbd> or <kbd>Delete</kbd>
+ on your keyboard.</li>
+ <li>If the image is unavailable because the server where the image resides
+ is inaccessible, try publishing the page at a later time.</li>
+ <li>If the missing file is a CSS file, you must first verify the correct
+ location of the CSS file. To fix the file&apos;s address in Composer,
+ click the HTML Source tab and edit the file&apos;s location in the HTML
+ source code. You should only edit the HTML source if you are familiar
+ with HTML tags.</li>
+ </ul>
+</div>
+
+<div class="errorMessage">
+ <p id="subdir_not_found"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt>The subdirectory <var>directory name</var> doesn&apos;t exist on this
+ site or the filename <var>filename</var> is already in use by another
+ subdirectory</tt>
+ <p>or</p>
+ <tt>The filename <var>filename</var> is already in use by another
+ subdirectory</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: You specified the name of a remote
+ subdirectory that does not exist at the publishing site. Composer can only
+ publish to a remote subdirectory that already exists at the publishing
+ location. Or, you specified a filename that is identical to the name of an
+ existing subdirectory at the publishing site.</p>
+
+ <p>For example, in the Publish Page dialog box, under the Publish tab:</p>
+
+ <ul>
+ <li>for <q>Site subdirectory for this page</q>, you may have typed the name
+ of a subdirectory that does not exist at the publishing location.</li>
+ <li>you checked <q>Include images and other files</q>, and then you typed
+ the name of a subdirectory that does not exist at the publishing
+ location.</li>
+ <li>one of the files you are attempting to publish has the same name as a
+ subdirectory at the publishing site.</li>
+ </ul>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Use a separate FTP program if you want to create, rename, or delete
+ subdirectories at the publishing site. Ask your service provider if they
+ recommend a particular FTP program. You can usually find information on
+ FTP programs in the Help or Support sections of your service
+ provider&apos;s website. FTP programs are also available from shareware
+ sites such as ZDNet Downloads.</li>
+ <li>Don&apos;t use subdirectory names that end with <q>.html</q> or
+ <q>.htm</q>. Only your Composer filenames should end with <q>.html</q>
+ or <q>.htm</q>.</li>
+ <li>Subdirectory names are case-sensitive, so be sure to enter a
+ subdirectory name exactly as it appears at the publishing location.</li>
+ </ul>
+</div>
+
+<div class="errorMessage">
+ <p id="server_not_found"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt>The server is not available. Check your connection and try again
+ later.</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: This error can have many causes. For
+ example:</p>
+
+ <ul>
+ <li>Your publishing site settings may not be correct.</li>
+ <li>Your Internet connection may have been lost.</li>
+ <li>Your modem or other equipment that you use to connect to the Internet
+ might not be functioning correctly.</li>
+ <li>The web server that you are trying to publish to might be unavailable
+ due to a technical problem or to an unknown circumstance.</li>
+ <li>Your ISP or web hosting service may be experiencing technical
+ problems.</li>
+ </ul>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Verify that your publishing settings are correct and that you entered
+ them correctly. See
+ <a href="#verifying_your_publishing_settings">Verifying Your Publishing
+ Settings</a> for more information.</li>
+ <li>Make sure your Internet connection is working by attempting to view a
+ web page using the browser. For example, confirm that you can
+ successfully view the page <tt>http://www.mozilla.org</tt>.</li>
+ <li>If your Internet connection is not working, verify that all hardware,
+ telephone connections, modems, and network connections are functioning
+ properly.</li>
+ <li>Use the browser to try to view a page at the website you are
+ attempting to publish to. If you can successfully view other websites but
+ cannot view a page at the publishing site, your ISP or web hosting
+ service may be experiencing technical problems.</li>
+ <li>Try publishing again later. Your ISP, web hosting service, or the web
+ server may be experiencing temporary technical difficulties.</li>
+ </ul>
+</div>
+
+<div class="errorMessage">
+ <p id="no_permission"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt>You do not have permission to publish to this location.</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: You are attempting to publish to a
+ location that you are not authorized to use. You can only publish to sites
+ where you have been granted access by your ISP or web hosting service.</p>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Verify that you entered the correct user name and password in the
+ Publishing Site Settings dialog box, or in the Publish tab of the
+ Publish dialog box.</li>
+ <li>Contact your ISP to find out where you can publish your pages at their
+ site.</li>
+ <li>Find a web hosting service that you can use to publish your pages. In
+ the browser, search for <q>web hosting</q>.</li>
+ </ul>
+</div>
+
+<div class="errorMessage">
+ <p id="offline_error"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt>You are currently offline. Click the icon near the lower-right corner
+ of any window to go online.</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: You are attempting to publish, but
+ your &brandShortName; Internet connection is currently in the
+ <q>offline</q> state. Your Internet connection must be in the <q>online</q>
+ state (connected to the Internet) in order to publish your pages.</p>
+
+ <p>Verify that your Internet connection is currently offline by looking at
+ the online/offline icon in the lower right corner of any &brandShortName;
+ window. If you are currently offline, the icon appears as
+ <img src="images/offline.png" width="32" height="21" alt="" />.</p>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Click the online/offline icon to go online. In the online state, the
+ icon should look like this:
+ <img src="images/online.png" width="32" height="20" alt="" />.</li>
+ <li>Make sure your Internet connection is working by attempting to view a
+ web page using the browser. For example, confirm that you can
+ successfully view the page <tt>http://www.mozilla.org</tt>.</li>
+ </ul>
+</div>
+
+<div class="errorMessage">
+ <p id="no_disk_space"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt>There is not enough disk space available to save the file
+ <var>filename</var>.</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: The remote web server&apos;s hard
+ disk is full, or you may have exceeded the amount of disk space allocated
+ to you by your ISP or web hosting service.</p>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Use a separate FTP program to delete unnecessary files at your
+ publishing site. Ask your service provider if they recommend a
+ particular FTP program. You can usually find information on FTP programs
+ in the Help or Support sections of your service provider&apos;s website.
+ FTP programs are also available from shareware sites such as ZDNet
+ Downloads.</li>
+ <li>Find out from your ISP or web hosting service about increasing your
+ disk space allocation, or switch to a different service that can satisfy
+ your needs.</li>
+ <li>If the web server is located at your company or school, contact the
+ network administrator to find out if you can publish to a different
+ location that has more disk space, or if you can request that
+ additional disk space be allocated to your current publishing
+ location.</li>
+ </ul>
+</div>
+
+<div class="errorMessage">
+ <p id="name_too_long"><strong>Error Message</strong>:</p>
+
+ <div class="contentsBox" style="background-color: #ebebeb;">
+ <tt>The filename or subdirectory name is too long.</tt>
+ </div>
+
+ <p><strong>Error Description</strong>: The number of characters in the
+ filename or the subdirectory name is not supported by the web server
+ computer that you are trying to publish to.</p>
+
+ <p><strong>Possible Solutions</strong>:</p>
+
+ <ul>
+ <li>Limit the length of your filenames and subdirectory names to less than
+ 32 characters. Some operating systems do not support names longer than 32
+ characters.</li>
+ </ul>
+</div>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h2 id="publishing_settings">Publishing Settings</h2>
+
+<p>This section describes Composer&apos;s publishing settings. For
+ information on Composer&apos;s general and new page settings, see
+ <a href="#composer_preferences">Composer Preferences</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#publish_page_publish">Publish Page - Publish</a></li>
+ <li><a href="#publish_page_settings">Publish Page - Settings</a></li>
+ <li><a href="#publish_settings">Publish Settings</a></li>
+ </ul>
+</div>
+
+<h3 id="publish_page_publish">Publish Page - Publish</h3>
+
+<p>The Publish Page - Publish tab lets you specify where you want
+ to publish a document. These settings apply to the current
+ document.</p>
+
+<p>If you are not already viewing the Publish Page - Publish tab,
+ follow these steps:</p>
+
+<ol>
+ <li>Open the File menu and choose Publish As. The Publish Page
+ dialog box appears.</li>
+ <li>Click the Publish tab.</li>
+</ol>
+
+<ul>
+ <li><strong>Site Name</strong>: Lists all the publishing sites you&apos;ve
+ created, so you can choose the site that you want to publish to. To
+ create a new site, click New Site.</li>
+ <li><strong>Page Title</strong>: Specifies the document&apos;s page title as
+ it appears in the browser window&apos;s title bar when you view the page in
+ the browser. The document&apos;s page title also appears in your list of
+ bookmarks if you bookmark the page.</li>
+ <li><strong>Filename</strong>: Specifies the document&apos;s filename. Make
+ sure you include the .html or .htm extension in the filename.
+
+ <p><strong>Warning</strong>: If a file on the remote site you&apos;re
+ publishing to has the same filename as one you&apos;re uploading, the
+ newly uploaded file will replace the existing one. You will not be
+ asked to confirm the action.</p>
+ </li>
+ <li><strong>Site subdirectory for this page</strong>: If you leave this
+ blank, Composer publishes the page to the main (root) publishing
+ directory at this site. If you want to publish the page to a remote
+ subdirectory that resides underneath the main publishing directory
+ at this site, enter the name of the subdirectory or choose it from
+ the list. Composer keeps track of the locations you type here, so
+ you can select from a list of remote locations you&apos;ve previously
+ used. Keep in mind that subdirectory names are case-sensitive.
+
+ <p><strong>Note</strong>: The site subdirectory you choose must
+ already exist at the remote server.</p>
+ </li>
+ <li><strong>Include images and other files</strong>: If checked,
+ Composer publishes any images and other files referenced by this
+ page. You can choose to publish these files to the same location as
+ the page, or else you can choose to publish these files into a
+ remote subdirectory that exists underneath the main publishing
+ directory.</li>
+</ul>
+
+<p><strong>Tip</strong>: To create remote subdirectories or delete
+ published pages or images, you must use an FTP (File Transfer
+ Protocol) program. Ask your service provider if they recommend a
+ particular FTP program. You can usually find information on FTP
+ programs in the Help or Support sections of your service provider&apos;s
+ website. FTP programs are also available from shareware sites such
+ as ZDNet Downloads.</p>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h3 id="publish_page_settings">Publish Page - Settings</h3>
+
+<p>The Publish Page - Settings tab lets you specify your login information for
+ the remote publishing site, as well as the publishing settings for the remote
+ site. These settings apply to the current document and any other files you
+ publish to this location.</p>
+
+<p>If you are not already viewing the Publish Page - Settings tab, follow
+ these steps:</p>
+
+<ol>
+ <li>Open the File menu and choose Publish As. The Publish Page dialog box
+ appears.</li>
+ <li>Click the Settings tab.</li>
+</ol>
+
+<ul>
+ <li><strong>Site Name</strong>: Specifies the nickname you want to use for
+ this publishing site. Enter a short name that will help you identify this
+ publishing site.</li>
+ <li><strong>Publishing address</strong>: Specifies the complete URL provided
+ to you by your ISP or system administrator. This URL should begin with
+ either <tt>ftp://</tt> or <tt>http://</tt>. This name is often referred to
+ as the <q>host name</q> or the <q>host server name</q>.
+
+ <p>The publishing address specifies the location where documents are
+ published (uploaded) at this site. If you are not sure what to enter,
+ ask your ISP or system administrator.</p>
+ </li>
+ <li><strong>HTTP address of your home page</strong>: Specifies the complete
+ address of your publishing home directory. This is the web address of the
+ home page at your website. Do not include a filename or subdirectory as
+ part of the URL.
+
+ <p>This URL must always begin with <tt>http://</tt>. In some cases, this
+ URL is the same as the publishing address. If you are not sure what to
+ enter, ask your ISP or system administrator, or else leave it blank.</p>
+ </li>
+ <li><strong>User name</strong>: Specifies the user name you use to log into
+ your ISP or network.</li>
+ <li><strong>Password</strong>: Specifies the password for your user
+ name.</li>
+ <li><strong>Save Password</strong>: Select this to encrypt and save your
+ password securely using Password Manager so that you don&apos;t have to
+ enter it each time you publish pages at this site.</li>
+</ul>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h3 id="publish_settings">Publish Settings</h3>
+
+<p>The Publish Settings dialog box lets you create, edit, and
+ delete publishing site settings, and also lets you set the default
+ publishing site.</p>
+
+<p>If you are not already viewing the Publish Settings dialog box,
+ follow these steps:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Publishing Site Settings.
+ Composer displays the Publish Settings dialog box.</li>
+</ol>
+
+<ul>
+ <li><strong>New Site</strong>: Lets you specify settings for a new publishing
+ site. Composer adds the name of the new publishing site to the list
+ of available publishing sites.</li>
+ <li><strong>Set as Default</strong>: Sets the selected publishing site as the
+ default publishing site. Typically, the default publishing site is
+ the remote location that you most often use for publishing
+ documents. All documents you create or edit will be published to
+ the default publishing site, unless you specifically choose an
+ alternate site in the Publish Page dialog box.
+
+ <p>To publish a document to a different remote location, open the
+ File menu and choose Publish As to choose a different publishing
+ destination.</p>
+ </li>
+ <li><strong>Remove Site</strong>: Removes the selected site and its settings
+ from Composer.</li>
+ <li><strong>Site Name</strong>: Specifies the name by which you want to refer
+ to this publishing site.</li>
+ <li><strong>Publishing address</strong>: Specifies the complete URL provided
+ to you by your ISP or system administrator. This URL should begin
+ with either <tt>ftp://</tt> or <tt>http://</tt>.
+
+ <p>The publishing address specifies the location where documents
+ are published (uploaded) at this site. If you are not sure what to
+ enter, ask your ISP or system administrator.</p>
+ </li>
+ <li><strong>HTTP address of your homepage</strong>: Specifies the HTTP
+ address of your publishing home directory. Do not include a
+ filename or subdirectory as part of the URL.
+
+ <p>This URL must always begin with <tt>http://</tt>. In some cases,
+ this URL is the same as the publishing address. If you are not sure
+ what to enter, ask your ISP or system administrator, or else leave
+ it blank.</p>
+ </li>
+ <li><strong>User name</strong>: Specifies the user name you use to log in to
+ your ISP or network.</li>
+ <li><strong>Password</strong>: Specifies the password for your user
+ name.</li>
+ <li><strong>Save Password</strong>: Select this to save your
+ password securely using Password Manager so you don&apos;t have to enter
+ it each time you publish pages at this site.</li>
+</ul>
+
+<p>[<a href="#publishing_your_pages_on_the_web">Return to beginning of
+ section</a>]</p>
+
+<h1 id="composer_preferences">Composer Preferences</h1>
+
+<p>This section describes the settings in the Composer preferences panel. If
+ you are not currently viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Double-click the Composer category to expand the list.</li>
+</ol>
+
+<p>For information on Composer&apos;s publishing settings, see
+ <a href="#publishing_settings">Publishing Settings</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#composer">Composer</a></li>
+ <li><a href="#new_page_settings">New Page Settings</a></li>
+ </ul>
+</div>
+
+<h2 id="composer">Composer Preferences - Composer</h2>
+
+<p>Composer preferences allow you to specify settings for saving files and for
+ table editing. These settings apply to every document you create.</p>
+
+<p>If you are not already viewing the Composer preferences, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Composer category.</li>
+</ol>
+
+<ul>
+ <li><strong>Maximum number of pages listed</strong>: Specify the maximum
+ number of pages that are listed under Recent Pages in the File menu.</li>
+ <li><strong>Preserve original source formatting</strong>: Select this if you
+ want to preserve the original white space (extra lines, tabs, etc.) in the
+ HTML source code. Deselect this if you prefer Composer to indent and add
+ linebreaks to the code in order to make it more readable. <em>This
+ preference does not affect how your pages appear in a browser
+ window.</em></li>
+ <li><strong>Save images and other associated files when saving
+ pages</strong>: If checked, all images, JavaScript (JS), Cascading Style
+ Sheet (CSS), and other associated files are saved in the same location as
+ the document when the document is saved for the first time or when the
+ document is saved to a new location. If unchecked, only the HTML file is
+ saved.
+
+ <p>For example, when editing a remote page, this setting ensures that all
+ related files associated with the remote page will be saved locally when
+ you save the page to your hard disk.</p>
+ </li>
+ <li><strong>Always show Publish dialog when publishing pages</strong>: If
+ checked, Composer always displays the Publish Page dialog box when you
+ click the Publish button or choose Publish from the File menu. If not
+ checked, Composer only displays the Publish Page dialog box if it needs
+ more information in order to publish the page.</li>
+ <li><strong>Maintain table layout when inserting or deleting cells</strong>:
+ Select this if you want Composer to always preserve the table&apos;s
+ layout (that is, keep it in a rectangular shape) by adding cells where
+ needed. If you deselect this option, when you delete one or more cells,
+ Composer removes the cell border as well, which can result in a table with
+ empty spaces, or an outline that appears irregular due to an uneven number
+ of cells.</li>
+ <li><strong>Use CSS styles instead of HTML elements and attributes</strong>:
+ Enables the use of Cascading Style Sheet (CSS) formatting in your Composer
+ documents. With this preference enabled, Composer generates HTML 4.01
+ formatting with CSS inline styles for elements.
+
+ <p>If this preference is not enabled, Composer generates HTML 4.01
+ formatting, but does not use CSS styles.</p>
+
+ <p>Compared to HTML, HTML with CSS formatting is more portable, more
+ maintainable, and more compatible when viewed with different browsers.
+ If you enable this preference and then edit a document created without
+ CSS, Composer replaces the edited elements with CSS styles.</p>
+
+ <p>If you enable CSS styles, you can choose a text highlight color for
+ selected text using the text highlight color button on the Format
+ toolbar. You can also choose a color background for any element on the
+ page. (These features are not available if this preference is not
+ enabled.)</p>
+ </li>
+ <li><strong><kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd> in a
+ paragraph always creates a new paragraph</strong>: If selected, a new
+ paragraph will be added everytime you press the <kbd class="mac">Return
+ </kbd><kbd class="noMac">Enter</kbd> key inside a paragraph. If
+ deselected, a linebreak will be added when you press the <kbd
+ class="mac">Return</kbd><kbd class="noMac">Enter</kbd> key.</li>
+</ul>
+
+<p>[<a href="#composer_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="new_page_settings">Composer Preferences - New Page Settings</h2>
+
+<p>New page preferences allow you to specify settings for colors and
+ background images that apply to every document you create.</p>
+
+<p>If you are not already viewing the New Page Settings, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Double-click the Composer category and click New Page Settings.</li>
+</ol>
+
+<ul>
+ <li><strong>Author</strong>: Enter your name. This will add your name to the
+ HTML source code for each new page you create.</li>
+ <li><strong>Reader&apos;s default colors</strong>: Select this if you always
+ want your pages to use the color settings from the viewer&apos;s browser
+ for text and link elements.</li>
+ <li><strong>Use custom colors</strong>: Select this if you always want to
+ specify the colors that are applied to text and link elements. Then for
+ each element, select a color by clicking the color button next to each
+ element.</li>
+ <li><strong>Background image</strong>: Type the location and name of an image
+ file, or click Choose File to locate the image file on your hard disk or
+ network.
+
+ <p><strong>Note</strong>: Background images are tiled and override
+ background color.</p>
+ </li>
+</ul>
+
+<p>To change the author name for an individual page: Open the Format menu and
+ choose Page Title and Properties.</p>
+
+<p>To change the page colors and background image for an individual page: Open
+ the Format menu and choose Page Colors and Background.</p>
+
+<p>[<a href="#composer_preferences">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_advanced.xhtml b/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_advanced.xhtml
new file mode 100644
index 0000000000..48be5334d7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_advanced.xhtml
@@ -0,0 +1,751 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Advanced Preferences Help</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="advanced_preferences">Advanced Preferences</h1>
+
+<p>This section describes how to use the Advanced preferences panel. If you are
+ not already viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Advanced category.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#advanced">Advanced</a></li>
+ <li><a href="#scripts_and_plugins">Scripts &amp; Plugins</a></li>
+ <li><a href="#keyboard_navigation">Keyboard Navigation</a></li>
+ <li><a href="#fayt">Find As You Type</a></li>
+ <li><a href="#cache">Cache</a></li>
+ <li><a href="#offline_apps">Offline Apps</a></li>
+ <li><a href="#proxies">Proxies</a></li>
+ <li><a href="#http_networking">HTTP Networking</a></li>
+ <li><a href="#software_installation">Software Installation</a></li>
+ <li><a href="#mouse_wheel">Mouse Wheel</a></li>
+ <li><a href="#debugging">Debugging</a></li>
+ </ul>
+</div>
+
+<h2 id="advanced">Advanced Preferences - Advanced</h2>
+
+<p>This section describes how to use the main Advanced preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Advanced category.</li>
+</ol>
+
+<p>The main Advanced preferences panel allows you to:</p>
+
+<ul>
+ <li><strong>Check default application settings on startup</strong>:
+ Select this to have &brandShortName; check whether it is the default
+ application for tasks like browsing and mail. If &brandShortName; detects
+ on startup that it is not the default application for any of these tasks,
+ a dialog will appear that allows you to choose which tasks &brandShortName;
+ should handle by default.</li>
+ <li><strong>Make &brandShortName; the default application for</strong>: Select
+ &brandShortName; as the default browser, mail, news or feeds application for
+ the operating system, such as Windows, and from within other applications,
+ such as Microsoft Word.
+
+ <p><strong>Note</strong>: Setting &brandShortName; as the default
+ browser, mail, news or feeds application may remove the connection that
+ other applications had with these tasks. Refer to the documentation of the
+ respective applications in order to find how to restore the defaults.</p>
+ </li>
+ <li><strong>Use &brandShortName; Mail &amp; News when opening browser links
+ for</strong>: By default, any links to email addresses or newsgroups opened
+ from browser pages or other messages are handled by &brandShortName; itself.
+ Uncheck the Mail and/or News boxes if you want an external application to
+ handle such links instead. In this case, a dialog will open to select the
+ application to be used.
+
+ <!-- remove the following warning once bug 198547 is fixed -->
+ <p><strong>Note</strong>: Don&apos;t uncheck either of these boxes and then
+ select &brandShortName; in the dialog unless it is also registered as the
+ system&apos;s respective default mail or news application. Doing so may
+ cause &brandShortName; to continuously prompt for the program to use when
+ opening a link.</p>
+ </li>
+ <li><strong>Use Native Print Dialog (where supported)</strong>: Select this
+ to enable &brandShortName; to use the native print dialog, where supported,
+ rather than its own.</li>
+ <li><strong>Use Global Print Settings</strong>: Select this to enable
+ &brandShortName; to use the same print settings for all windows. If disabled
+ each window users its own settings and changes in one window do not affect
+ the settings in another.</li>
+ <li><strong>Submit crash reports</strong>: Select this to enable the Mozilla
+ Crash Reporter. If &brandShortName; crashes, you will be asked in each case
+ whether to report the issue.</li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="scripts_and_plugins">Advanced Preferences - Scripts &amp;
+ Plugins</h2>
+
+<p>This section describes how to use the Scripts &amp; Plugins preferences
+ panel. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Scripts &amp; Plugins. (If no
+ subcategories are visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Scripts &amp; Plugins preferences panel allows you to control how
+ JavaScript and plugins are used:</p>
+
+<ul>
+ <li><strong>Enable JavaScript for</strong>:
+ <ul>
+ <li><strong>Browser</strong>: Select this to turn on JavaScript for web
+ pages opened in the browser.</li>
+ </ul>
+ </li>
+ <li><strong>Allow scripts to</strong>: Select these checkboxes to control
+ how JavaScript can be used:
+ <ul>
+ <li><strong>Move or resize existing windows</strong>: Allows open windows
+ to be resized or moved.</li>
+ <li><strong>Raise or lower windows</strong>: Allows windows to be placed
+ under or on top of other windows.</li>
+ <li><strong>Hide the status bar</strong>: Allows the status bar to be
+ hidden.</li>
+ <li><strong>Change status bar text</strong>: Allows status bar text to be
+ changed, such as in scrolling text in the status bar.</li>
+ <li><strong>Change images</strong>: Allows images to be changed or
+ animated, such as in image rollovers (images that change when the mouse
+ cursor is placed over them).</li>
+ <li><strong>Disable or replace context menus</strong>: Allows right-click
+ menus<span class="mac"> or, if you&apos;re using a one-button mouse,
+ <kbd>Ctrl</kbd>-click menus</span> to be replaced or disabled by
+ webpages.</li>
+ </ul>
+ </li>
+ <li><strong>Enable JavaScript dump() output</strong>: Select this to enable
+ dump() statements in JavaScript code to be displayed in the console.</li>
+ <li><strong>Show strict JavaScript warnings</strong>: Select this to show in
+ the console the strict warnings generated by the JavaScript Engine for the
+ code it is processing.</li>
+ <li><strong>Show chrome JavaScript errors and warnings</strong>: Select this
+ to show in the console errors and warnings being generated by the JavaScript
+ code embedded within &brandShortName; when it is run.</li>
+ <li><strong>Enable Plugins for</strong>: Use these checkboxes to control how
+ plugins are used:
+ <ul>
+ <li><strong>Suite</strong>: Globally enables or disables plugins.</li>
+ <li><strong>Mail &amp; Newsgroups</strong>: Allows plugins to be used in
+ Mail &amp; Newsgroups.</li>
+ </ul>
+ </li>
+ <li><strong>When a page requires plugins</strong>: Select these
+ checkboxes to control what &brandShortName; should do if a web page requires
+ plugins:
+ <ul>
+ <li><strong>Activate all plugins by default</strong>: If you select this,
+ &brandShortName; will load and run all plugins found on the web page.
+ Otherwise, if the page requires plugins, &brandShortName; will show a
+ notification (plugin icon in the location bar or notification bar), plus
+ a placeholder for every plugin instance found on the page (unless the
+ website is whitelisted, see below).
+ You can activate individual visible plugin instances with a left mouse
+ click or use the notification to activate all the plugins on the page.
+ From the notification you have the option to either temporarily activate
+ the plugins or remember the choice for the current website. Remembered
+ choices can be edited using the Data Manager (Permissions tab).</li>
+ <li><strong>Warn me if additional plugins need to be installed</strong>:
+ When a website requires a plugin which is not installed,
+ a notification bar will be displayed above the website content area.
+ From the bar you will be able to download and install the missing
+ plugin.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>For more information about plugins, see
+ <a href="nav_help.xhtml#plugins_and_downloads">Plugins and Downloads</a>.
+</p>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="keyboard_navigation">Advanced Preferences - Keyboard Navigation</h2>
+
+<p>This section describes how to use the Keyboard Navigation preferences
+ panel. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Keyboard Navigation. (If no
+ subcategories are visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Keyboard Navigation preferences panel allows you to control how you use
+ the keyboard to navigate in web pages:</p>
+
+<ul>
+ <li class="noMac"><strong>Tab Key Navigation</strong>: Select which elements
+ should be taken into account when using the Tab key on a page:
+ <ul>
+ <li><strong>Links</strong>: If checked, pressing <kbd>Tab</kbd> or
+ <kbd>Shift</kbd>+<kbd>Tab</kbd> moves between links.</li>
+ <li><strong>Buttons, radio buttons, checkboxes, and selection
+ lists</strong>: If checked, pressing <kbd>Tab</kbd> or
+ <kbd>Shift</kbd>+<kbd>Tab</kbd> moves between buttons, radio buttons,
+ check boxes, and selection lists.</li>
+ </ul>
+ </li>
+ <li><strong>Browse With Caret</strong>: Select if and how <a
+ href="glossary.xhtml#caret_browsing">caret browsing</a> should be used:
+ <ul>
+ <li><strong>Use caret browsing</strong>: If checked, caret browsing will
+ be enabled by default when you load a web page.</li>
+ <li><strong>Use the F7 shortcut to toggle caret browsing</strong>: If
+ checked, the F7 shortcut will toggle caret browsing on or off. Otherwise
+ &brandShortName; will not use the shortcut and the below option will
+ have no effect.</li>
+ <li><strong>Warn me before turning on caret browsing</strong>: If checked,
+ &brandShortName; will issue a warning dialog if the caret browsing mode
+ is about to be entered, allowing you to choose whether to proceed or
+ not. (There is no warning when leaving caret browsing mode.)</li>
+ </ul>
+ </li>
+ <li><strong>Modifiers</strong>: Select which keyboard modifiers should
+ be used, if at all:
+ <ul>
+ <li><strong>Accelerator key</strong>: The key value to be used for the
+ accelerator key.</li>
+ <li><strong>Menu access key</strong>: The key value to be used for the
+ menu access key.</li>
+ <li><strong>Note</strong>: Setting a value of zero for either of the
+ above modifiers disables that key.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="fayt">Advanced Preferences - Find As You Type</h2>
+
+<p>This section describes how to use the Find As You Type references
+ panel. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Find As You Type. (If no
+ subcategories are visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Find As You Type preferences panel allows you to control how you use
+ the keyboard to search for text in web pages:</p>
+
+<ul>
+ <li><strong>Find automatically when typing within a web page</strong>: If
+ checked, typing text in a web page automatically activates Find As You Type
+ and locates the text you typed (if it exists in the page). Choose whether
+ you want typing to find any text in the page or links only. If unchecked,
+ you must choose Find Links As You Type or Find Text As You Type from the
+ Edit menu before typing the text you want to find.</li>
+ <li><strong>Play a sound when typed text isn&apos;t found</strong>: If
+ checked, Find As You Type plays a sound when the typed text isn&apos;t
+ found in the web page.</li>
+ <li><strong>Clear the current search after a few seconds of
+ inactivity</strong>: If checked, the search will be cancelled after a few
+ seconds of keyboard inactivity.</li>
+ <li><strong>Show the find toolbar during find as you type</strong>: If
+ checked, the find toolbar will open when Find As You Type is activated and
+ what you type will be entered into the toolbar search field. If unchecked,
+ the find toolbar will not be opened and the search string will be displayed
+ in the status bar. Note that international text entry will not work in this
+ mode.</li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="cache">Advanced Preferences - Cache</h2>
+
+<p>This section describes how to use the Cache preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Cache. (If no subcategories are
+ visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Cache preferences panel allows you to adjust the &brandShortName; memory
+ and disk cache:</p>
+
+<ul>
+ <li><strong>Let &brandShortName; manage the size of my cache</strong>: Select
+ this to let &brandShortName; apply a heuristic based on the space available
+ on your hard disk in order to determine your cache size. (This is the
+ default.)</li>
+ <li><strong>Use up to [__] MB of disk space for the cache</strong>: Type in
+ the amount of disk cache you want to allocate for &brandShortName;. The disk
+ cache is saved to your hard disk (drive) and can be used again, even if you
+ have restarted your computer. (The default is 1024 MB. This preference is
+ ignored if the above checkbox is selected.)</li>
+ <li><strong>Clear Cache</strong>: Click this to clear the disk cache.</li>
+ <li><strong>Cache Folder Location</strong>: Shows the current location of the
+ disk cache folder
+ <ul>
+ <li><strong>Choose Folder</strong>: Click this to choose a folder
+ location for the disk cache.</li>
+ </ul>
+ </li>
+ <li><strong>Compare the page in the cache to the page on the
+ network</strong>:
+ <ul>
+ <li><strong>Every time I view the page</strong>: Select this if you want
+ &brandShortName; to compare a web page to the cache every time you view
+ it.</li>
+ <li><strong>When the page is out of date</strong>: Select this if you
+ want &brandShortName; to compare a web page to the cache when the page
+ is determined by the server to have expired.</li>
+ <li><strong>Once per session</strong>: Select this if you want
+ &brandShortName; to compare a web page to the cache once for each time
+ you start &brandShortName;.</li>
+ <li><strong>Never</strong>: Select this if you do not want
+ &brandShortName; to compare cached information to the network.</li>
+ </ul>
+ </li>
+ <li><strong>Prefetch web pages when idle, so that links in web pages
+ designed for prefetching can load more quickly</strong>: Select this to
+ decrease the time it takes to load web pages when you click a link in a web
+ page that uses prefetching. For more information about Link Prefetching, see
+ the online
+ <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ">Link
+ Prefetching FAQ</a>.</li>
+ <li><strong>Enable Disk Cache</strong>: Select this to enable the use of the
+ disk cache up to the limits specified above.</li>
+ <li><strong>Enable Memory Cache</strong>: Select this to enable the use of a
+ memory cache.</li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="offline_apps">Advanced Preferences - Offline Apps</h2>
+
+<p>This section describes how to use the Offline Web Applications preferences
+ panel. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Offline Apps. (If no subcategories are
+ visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Offline Web Applications panel allows you to control and monitor how
+ websites use &brandShortName;&apos;s offline storage. Websites can provide
+ complex functionality beyond just the presentation of content, implementing
+ <a href="glossary.xhtml#web_application">web applications</a>. In general,
+ such web applications require you to stay online in order to use them, thus
+ won&apos;t function when you are disconnected from the network (offline).
+ If permitted, websites can store their content and any data you entered
+ locally in &brandShortName;. In this way, web applications can be used even
+ without a network connection.</p>
+
+<p>The <strong>Offline Web Content and User Data</strong> section provides the
+ following functions:</p>
+
+<ul>
+ <li><strong>Your offline storage currently uses [__] of disk space</strong>:
+ This entry is informational and provides you with an estimate of the
+ offline data currently used by all web applications combined.</li>
+ <li><strong>Clear</strong>: Immediately clears data that has been saved
+ by all websites for use when you are offline.</li>
+ <li><strong>Allow all websites to store data for offline use</strong>: Select
+ this option if you want to permit websites by default to store web content
+ and user data locally on the disk of your computer.</li>
+ <li><strong>Only allow websites with explicit permissions</strong>: Select
+ this option if you want to control whether or not a website can store web
+ content for offline use locally. Unless explicit permission has been
+ granted, the website&apos;s request to access the offline storage is
+ blocked or a notification shown to give you the option to allow it.</li>
+ <li><strong>Notify me when websites want to store data for offline
+ use</strong>: Check this box if you want to be notified when a website
+ requests to store content or data in the offline storage but does not
+ currently have any permissions set whether or not it is allowed to do so.
+ An information bar will appear for this site offering the following options:
+ <ul>
+ <li><strong>Always Allow</strong>: Permanently allow this site the use
+ of the offline storage for this and future accesses.</li>
+ <li><strong>Never for This Site</strong>: Permanently deny this site
+ the use of the offline storage for this and future accesses.</li>
+ <li><strong>Not Now</strong>: Deny this site the use of the offline
+ storage for this request but ask again for future accesses.</li>
+ </ul>
+ <p><strong>Note</strong>: The offline storage is <em>not</em> available in
+ a <a href="using_priv_help.xhtml#browsing_in_a_private_window">private
+ window</a>. You will receive a notification if this option is checked,
+ but all requests will be blocked.</p>
+ </li>
+ <!-- NOTE: link "Data Manager" below once bug 599097 has been fixed -->
+ <li><strong>Manage Permissions</strong>: Opens the Data Manager in a new
+ tab or window where you can modify the Offline Web Applications permissions
+ for each domain:
+ <ul>
+ <li><strong>Use Default</strong>: Check this box to return to the default
+ behavior, thus presenting the information bar with the next request
+ from a site within each domain if the notification option is chosen
+ (effectively removes the entry from this listing).</li>
+ <li><strong>Allow</strong>: Select this option to allow this site
+ the use of the offline storage for all accesses.</li>
+ <li><strong>Block</strong>: Select this option to deny this site
+ the use of the offline storage for all accesses.</li>
+ </ul>
+ </li>
+ <li><strong>The following websites are using offline storage</strong>:
+ Lists the domains of all websites which have stored offline data, along
+ with the amount of data currently used by this domain.</li>
+ <li><strong>Clear Data</strong>: Select a website domain and click this
+ button to delete all offline data for the specified domain. Click <q>Remove
+ offline data</q> after being prompted for confirmation to do so.</li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="proxies">Advanced Preferences - Proxies</h2>
+
+<p>This section describes how to use the Proxies preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Proxies. (If no subcategories are
+ visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Proxies preferences panel allows you to set up &brandShortName; to use a
+ <a href="glossary.xhtml#proxy">proxy</a>:</p>
+
+<p><strong>Before you start</strong>: Ask your network administrator if you
+ have a proxy configuration file or for the names and port numbers of the
+ proxy.</p>
+
+<ul>
+ <li><strong>Direct connection to the Internet</strong>: Choose this if you
+ don&apos;t want to use a proxy.</li>
+ <li><strong>Automatically discover the proxy configuration</strong>: Choose
+ this if you want &brandShortName; to automatically detect and configure the
+ proxy settings, using the <a href="glossary.xhtml#wpad">WPAD protocol</a>.</li>
+ <li><strong>Automatic proxy configuration URL</strong>: Choose this if you
+ have a proxy auto-configuration (PAC) file or URL, then enter the
+ configuration URL.
+ <ul>
+ <li><strong>Reload</strong>: Click this to reload the configuration file.
+
+ <p>A PAC file has JavaScript code which chooses a proxy to use depending
+ on the address you open. The same file can use more than one proxy or
+ use proxies only for specific addresses. For more information about
+ the content of PAC files, see the online
+ <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file">MDN
+ documentation on Proxy Auto-Configuration files</a>.</p>
+ </li>
+ </ul>
+ </li>
+ <li><strong>Manual proxy configuration</strong>: Choose this if you
+ don&apos;t have a proxy location (URL), or the automatic proxy discovery
+ was unable to setup the proxy settings correctly.
+ <ul>
+ <li><strong>Proxy</strong>: Enter the name or numeric IP address of
+ the proxy server.</li>
+ <li><strong>Port</strong>: Enter the port number in the Port field. Click
+ on <q>Advanced</q> to set
+ <a href="#advanced_proxy_preferences">Advanced Proxy Preferences</a></li>
+ <li><strong>No Proxy for</strong>: Type the domains and/or IP addresses
+ that you do not want to use a proxy for. Separate each entry with a
+ comma. (Example: <kbd>.mozilla.org, .net.nz, 192.168.1.0/24</kbd>.)
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h3 id="advanced_proxy_preferences">Advanced Proxy Preferences</h3>
+
+<p>If you want to use different proxies for different protocols or need to use
+ a SOCKS proxy:</p>
+
+<ul>
+ <li><strong>HTTP Proxy</strong>, <strong>SSL Proxy</strong>,
+ <strong>FTP Proxy</strong>: Enter the name or numeric IP address
+ of the proxy server. Type the port in the Port fields.
+
+ <p><strong>Note</strong>: If you&apos;re using the same settings
+ for all types of proxies, click on <q>Use HTTP Proxy settings
+ for all protocols</q>.</p>
+
+ </li>
+ <li><strong>SOCKS Proxy</strong>: Enter the name or numeric IP address
+ of the proxy server. Enter the port number in the Port field.
+ <ul>
+ <li><strong>SOCKS v4, SOCKS v5</strong>: When entering a SOCKS Proxy,
+ select <q>SOCKS v4</q> or <q>SOCKS v5</q>, depending on what version
+ of <a href="glossary.xhtml#socks">SOCKS</a> is used for the proxy.</li>
+ <li><strong>Use for resolving hostnames</strong>: Select this to
+ use the SOCKS Proxy for resolving hostnames. This is
+ recommended for SOCKS v5 proxies.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="http_networking">Advanced Preferences - HTTP Networking</h2>
+
+<p>This section describes how to use the HTTP Networking preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click HTTP Networking. (If no subcategories
+ are visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The HTTP Networking preferences panel is used to configure HTTP-based
+ networking:</p>
+
+<ul>
+ <li><strong>Direct Connection Options, Proxy Connection Options</strong>:
+ Choose the HTTP version and options for direct and proxy connections.
+ <ul>
+ <li><strong>Use HTTP 1.0</strong>: Choose this to use the original
+ version of HTTP, standardized in 1996. This setting is mostly intended
+ for compatibility reasons with proxy or older web servers.</li>
+ <li><strong>Use HTTP 1.1</strong>: Choose this to use the current version
+ of HTTP, which offers performance enhancements, including more efficient
+ use of HTTP connections, better support for client-side caching, and
+ more refined control over cache expiration and replacement policies.</li>
+ </ul>
+ </li>
+ <li><strong>User Agent String</strong>:
+ The identifier sent by &brandShortName; to all websites is used for
+ statistics about website usage but also sometimes to expose certain features
+ only to known browsers (a practice known as "sniffing"). Consequently,
+ changing this option may result in websites not working properly.
+ &brandShortName; can:
+ <ul>
+ <li><strong>Identify as Firefox</strong>: Do not mention &brandShortName;,
+ send a Firefox identifier instead. This might be needed for websites
+ which refuse to work when &brandShortName; is mentioned in the
+ identifier.</li>
+ <li><strong>Identify as &brandShortName;</strong>: &brandShortName; will
+ identify itself as &brandShortName;, without mentioning Firefox.</li>
+ <li><strong>Identify as &brandShortName; and advertise Firefox
+ compatibility</strong>: &brandShortName; will identify itself as both
+ &brandShortName; and also compatible with Firefox. This allows websites
+ that check for certain browsers rather than certain functionality to
+ work with &brandShortName;, while still appearing as &brandShortName; in
+ website statistics.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="software_installation">Advanced Preferences - Software
+ Installation</h2>
+
+<p>This section describes how to use the Software Installation preferences
+ panel. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Software Installation. (If no
+ subcategories are visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Software Installation preferences panel is used to enable add-on
+ installation and updates. The Add-ons Manager allows you to view and manage
+ all your installed extensions and themes.</p>
+
+<ul>
+ <li><strong>Add-ons</strong>:
+ <ul>
+ <li><strong>Allow websites to install add-ons and updates</strong>:
+ Select this if you want to allow websites to install add-ons and
+ updates to be used with &brandShortName;. You will be prompted before
+ each installation.
+ <ul>
+ <li><strong>Allowed Websites</strong>: Click this link to open the
+ Data Manager tab, where you can view and edit the list of websites
+ that you want to allow to install software</li>
+ <li><strong>Automatically check for updates</strong>:Select this to
+ be notified when a new version of one of your installed add-ons is
+ available. Choose whether you want &brandShortName; to do a
+ <strong>daily</strong> or a <strong>weekly</strong> check for new
+ versions.</li>
+ <li><strong>Automatically download and install the update</strong>:
+ Select this if you want &brandShortName; to automatically download
+ and install updates for you when they become available. The download
+ will happen in the background, with low priority. After the download
+ has finished the update to the add-on will be installed
+ automatically the next time you start &brandShortName;.</li>
+ </ul>
+ </li>
+ <li><strong>Personalize Add-on recommendations</strong>: Select this to
+ be offered suggestions for add-ons in the &quot;Get Add-ons&quot; tab
+ of the manager:
+ <ul>
+ <li>Note that this function involves sending a list of your
+ currently installed add-ons to the add-on website.</li>
+ <li>Disabling this option will also suppress contacting the add-on
+ site periodically for any new information about installed add-ons,
+ but won't affect automated updates.</li>
+ </ul>
+ </li>
+ <li><strong>Manage Add-ons</strong>: Click this link to open the Add-ons
+ Manager in a new browser tab.</li>
+ </ul>
+ </li>
+ <li><strong>&brandShortName;</strong>:
+ <ul>
+ <li><strong>Automatically check for updates</strong>:Select this to be
+ notified when a new version of &brandShortName; is available. Choose
+ whether you want &brandShortName; to do a <strong>daily</strong> or
+ a <strong>weekly</strong> check for new versions.
+ <ul>
+ <li><strong>Automatically download and install the update</strong>:
+ Select this if you want &brandShortName; to automatically download
+ and install updates for you when they become available. The download
+ will happen in the background, with low priority. After the download
+ has finished the update to &brandShortName; will be installed
+ automatically the next time you start it.</li>
+ </ul>
+ <p><strong>Note</strong>: You can use Check for Updates from the
+ <span class="mac">&brandShortName;</span><span class="noMac">Help</span>
+ menu to manually initiate the search for a &brandShortName; update.
+ <span class="noMac">The label of the menu item will change when an
+ update is being downloaded or ready to be applied.</span></p>
+ </li>
+ <li><strong>Show Update History…</strong>: Click this to open the Update
+ History dialog box which shows a list of &brandShortName; updates that
+ have been installed, including the update type (e.g. Security Update),
+ time of installation and installation status. The Details link next to
+ each update takes you to a web page that contains further information
+ regarding the update.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="mouse_wheel">Advanced Preferences - Mouse Wheel</h2>
+
+<p>This section describes how to use the Mouse Wheel preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Mouse Wheel. (If no subcategories
+ are visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Mouse Wheel preferences panel allows you to control how the mouse wheel
+ on your mouse (in between your mouse buttons) is used in &brandShortName;.
+ Modern mice may have two wheels or a button that can be used to switch the
+ scroll direction of the wheel. The behaviour for the vertical wheel function
+ is set in the upper panel <strong>Vertical scrolling</strong> while the
+ horizontal mode is controlled by the lower panel <strong>Horizontal
+ scrolling</strong>.</p>
+
+<ul>
+ <li>Select <q>No modifier key</q> or select a key that you want to use
+ along with the mouse wheel. Use the checkboxes below to configure mouse
+ wheel behavior:
+
+ <ul>
+ <li><strong>Same as vertical scrolling</strong>: Choose this when your
+ mouse has a single control that provides both vertical and horizontal
+ scrolling. If your mouse has a separate horizontal scrolling control
+ then you can choose to assign a different function to it.</li>
+ <li><strong>Do nothing</strong>: Choose this to disable the scroll wheel
+ when used with the specified modifier key.</li>
+ <li><strong>Scroll the document</strong>: Choose this to scroll the
+ document by the system default number of lines or characters.</li>
+ <li><strong>Move back and forward in the browsing history</strong>:
+ Choose this to use the mouse wheel to navigate back or forward to
+ previous pages you&apos;ve visited.</li>
+ <li><strong>Zoom the page in or out</strong>: Choose this to use
+ the mouse wheel to increase or decrease the size of a web page.
+ This setting can help you better read a page, or make text fit on the
+ screen.</li>
+ </ul>
+
+ <p>Use the <strong>Mouse wheel speed</strong> option to adjust the
+ responsiveness of the wheel. To reverse the scroll direction, use negative
+ numbers or select <strong>Reverse direction</strong>.</p>
+
+ <p><strong>Note</strong>: Each modifier key can be assigned to a different
+ function.</p>
+ </li>
+ <li>If your mouse does not have a mode for horizontal scrolling, any setting
+ in the lower panel <strong>Horizontal scrolling</strong> will be ignored.
+ </li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="debugging">Advanced Preferences - Debugging</h2>
+
+<p>This section describes how to use the Debugging preferences panel. If you are
+ not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Debugging. (If no subcategories are
+ visible, double-click Advanced to expand the list.)</li>
+</ol>
+
+<p>The Debugging preferences panel allows you to control various preferences
+ relating to XUL rendering and debugging events including reflow ones. Some
+ preferences only take affect once &brandShortName; has been restarted:</p>
+
+<ul>
+ <li><strong>Debug XUL boxes</strong>: Select this to enable the debugging of
+ XUL boxes, it also requires &brandShortName; to be restarted for the change
+ to take affect.</li>
+ <li><strong>Disable XUL Cache</strong>: Select this to disable the use of a
+ cache for chrome-level XUL components (XUL, JavaScript, CSS). With the
+ cache disabled files are re-read each time they are used rather than reading
+ them from a cache, which will slow down &brandShortName; but is useful for
+ debugging problems in XUL components.</li>
+</ul>
+
+<p>[<a href="#advanced_preferences">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_appearance.xhtml b/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_appearance.xhtml
new file mode 100644
index 0000000000..8c7996c1fc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_appearance.xhtml
@@ -0,0 +1,369 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Appearance Preferences Help</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="appearance_preferences">Appearance Preferences</h1>
+
+<p>This section describes how to use the Appearance preference panel. If you
+ are not already viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Double-click Appearance to expand the list, then click the name for the
+ preferences you want to view.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#appearance">Appearance</a></li>
+ <li><a href="#content">Content</a></li>
+ <li><a href="#fonts">Fonts</a></li>
+ <li><a href="#colors">Colors</a></li>
+ <li><a href="#media">Media</a></li>
+ <li><a href="#spelling">Spelling</a></li>
+ </ul>
+</div>
+
+<h2 id="appearance">Appearance Preferences - Appearance</h2>
+
+<p>This section describes how to use the Appearance preferences panels. If
+ you&apos;re not already viewing one of these panels, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Appearance category.</li>
+</ol>
+
+<p>The Appearance preferences panel allows you to set &brandShortName; startup
+ options and customize the user interface:</p>
+
+<ul>
+ <li><strong>When &brandShortName; starts up, open</strong>: Select the
+ components you want to use when you start up &brandShortName;</li>
+ <li>Show toolbars as:
+ <ul>
+ <li><strong>Pictures and text</strong>: Select this to see text
+ underneath each of the toolbar buttons.</li>
+ <li><strong>Pictures only</strong>: Select this to show the toolbar
+ buttons only.</li>
+ <li><strong>Text only</strong>: Select this to show text buttons
+ only.</li>
+ </ul>
+ </li>
+ <li><strong>Show Tooltips</strong>: Select this if you want to have
+ <a href="glossary.xhtml#tooltip">tooltips</a> appear when the cursor
+ is placed over parts of the &brandShortName; user interface and some
+ websites.</li>
+ <li><strong>Hide Toolbar Grippies</strong>: <span class="mac">This option is
+ not available on macOS.</span><span class="noMac">Check this if you want to
+ hide the <a href="glossary.xhtml#grippy">menubar and toolbar grippies</a>
+ preventing accidental collapses of menubars and toolbars.
+ </span>
+ </li>
+ <li><strong>User Interface Language</strong>: This setting allows you to
+ change the language used in the user interface of &brandShortName;.
+ Additional languages can be installed from the &brandShortName; home page.
+ </li>
+ <li><strong>Date and Time Formatting</strong>: There are usually differences
+ how settings like date and time are formatted, depending on the region
+ (locale). Select how &brandShortName; displays such values:
+ <ul>
+ <li><strong>Application locale</strong>: Select this to show values in
+ the regional way of &brandShortName;&apos;s user interface language.
+ </li>
+ <li><strong>Regional Settings locale</strong>: Select this to show
+ values in the way as defined by your operating system&apos;s settings.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<p><strong>Note</strong>: You must restart &brandShortName; for a change in
+ language or formatting settings to take effect. In many cases, the choices
+ will show the same language unless you have additional language pack(s)
+ installed or use a localized version of &brandShortName; for a language
+ which is different from your operating-system&apos;s settings.</p>
+
+<p>[<a href="#appearance_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="content">Appearance Preferences - Content</h2>
+
+<p>This section describes how to use the Content preferences panel.
+ If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, click Content. (If no
+ subcategories are visible, double-click Appearance to expand the
+ list.)</li>
+</ol>
+
+<p>The Content preferences panel allows you to change settings that influence
+ how website and message content appears in &brandShortName;.</p>
+
+<ul>
+ <li><strong>Scrolling</strong>:
+ <ul>
+ <li><strong>Use autoscrolling</strong>: Select this to enable
+ autoscrolling. Clicking the middle mouse button (usually the scroll
+ wheel) lets you scroll the content by moving the mouse around.</li>
+ <li><strong>Use smooth scrolling</strong>: Select this to enable smooth
+ scrolling. Pressing the Page Down key when this is enabled
+ will&mdash;instead of an immediate jump&mdash;smoothly scroll the
+ content down to the next page.</li>
+ </ul>
+ </li>
+ <li><strong>Zoom options</strong>:
+ <ul>
+ <li><strong>Range from [__] to [__] %</strong>: Defines the range of zoom
+ levels observed when opening the Zoom menu from the View main menu or
+ when using the <a href="cs_nav_prefs_advanced.xhtml#mouse_wheel">mouse
+ wheel</a> or the
+ <a href="shortcuts_navigator.xhtml#page_viewing_shortcuts"> keyboard
+ shortcuts</a> to increase or decrease the zoom. <strong>Note</strong>:
+ after changing these values, they become effective only for
+ <em>new</em> windows, not in the ones which are already open.</li>
+ <li><strong>Zoom only text instead of full pages</strong>: Select this if
+ you want &brandShortName; to only resize text of websites when using
+ the &quot;Zoom&quot; function. If this is not selected, the whole page,
+ including images, will be zoomed.</li>
+ <li><strong>Remember zoom levels on a per-site basis</strong>: Select
+ this if you want the system to remember the specific zoom level for
+ each website you visit.</li>
+ <li><strong>Show zoom controls in the status bar</strong>: Select this
+ if you want &brandShortName; to display the zoom controls in the
+ status bar of your browser window.</li>
+ <li><strong>Resize large images to fit in the browser window</strong>:
+ Select this if you want &brandShortName; to automatically shrink large
+ stand-alone images so they will fit in the browser window. Clicking on
+ the resized image will make it appear at full size.</li>
+ </ul>
+ </li>
+ <li><strong>Use hardware acceleration when available</strong>: Select this to
+ let &brandShortName; use hardware acceleration (if available) to render web
+ sites. If you experience problems with the visual presentation of web
+ content, disabling hardware acceleration may solve the issue.</li>
+ <li><strong>Warn me when websites try to redirect or reload the
+ page</strong>: Select this to let &brandShortName; block automatic meta
+ redirection (HTTP-EQUIV=refresh) requests by default. When a redirect is
+ supposed to be executed, a notification bar is shown instead which allows
+ you to permit the redirect on a case-by-case basis.</li>
+</ul>
+
+<p><strong>Note</strong>: See the separate preferences panels for colors, fonts
+ and languages to further customize content appearance and the Privacy &amp;
+ Security section for privacy-related settings that also might influence how
+ content appears to you.</p>
+
+<p>[<a href="#appearance_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="fonts">Appearance Preferences - Fonts</h2>
+
+<p>This section describes how to use the Fonts preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, click Fonts. (If no subcategories are
+ visible, double-click Appearance to expand the list.)</li>
+</ol>
+
+<p>The Fonts preferences panel allows you to set page font type and size.</p>
+
+<p><strong>Note</strong>: Some font styles may not be selectable because the
+ selected language does not have fonts available for that style.</p>
+
+<ul>
+ <li><strong>Fonts for</strong>: Choose a language group/script. For instance,
+ to set default fonts for West European languages/script (Latin), choose
+ <q>Western.</q> For Unicode or a language/script not yet in the list, choose
+ <q>Unicode.</q> For more information, including <q>User Defined</q>,
+ see <a href="nav_help.xhtml#selecting_text_encodings_and_fonts">Selecting
+ Text Encodings and Fonts</a>.<br/>
+ All settings below, except for the checkbox, are stored per language group;
+ each can have its own set of font definitions.
+ <ul>
+ <li><strong>Proportional</strong>: Select whether proportional text
+ should be serif (like Times Roman) or sans-serif (like Arial). You can
+ also specify what font size you want for proportional text.
+ Proportional text is variable in width, so characters and letters vary
+ in width.</li>
+ <li><strong>Serif</strong>: Select a serif font you want to use for
+ web pages.</li>
+ <li><strong>Sans-serif</strong>: Select a sans-serif font you want to use
+ for web pages.</li>
+ <li><strong>Cursive</strong>: Select a cursive font you want to use for
+ web pages.</li>
+ <li><strong>Fantasy</strong>: Select a fantasy font you want to use for
+ web pages.</li>
+ <li><strong>Monospace</strong>: Select a monospace font (like Courier)
+ and size you want to use for web pages. Monospace text is fixed in
+ width, so each character or letter takes the same amount of space.</li>
+ <li><strong>Minimum font size</strong>: Select the smallest font size you
+ want to be shown on web pages.</li>
+ </ul>
+ </li>
+ <li><strong>Allow documents to use other fonts</strong>: Select
+ this checkbox to keep a web page&apos;s font and size settings instead of
+ your own preferences.</li>
+</ul>
+
+<p>[<a href="#appearance_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="colors">Appearance Preferences - Colors</h2>
+
+<p>This section describes how to use the Colors preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, click Colors. (If no subcategories are
+ visible, double-click Appearance to expand the list.)</li>
+</ol>
+
+<p>The Colors preferences panel allows you to set the background and text
+ colors on web pages, mail/news messages, and feeds:</p>
+
+<ul>
+ <li><strong>Text and Background</strong>: Click the colored blocks to select
+ a color for displaying text and backgrounds on web pages and messages.
+ <ul>
+ <li><strong>Use system colors</strong>: Select this to use the colors
+ defined by your system settings (usually the desktop theme) rather
+ than choosing the colors yourself.</li>
+ </ul>
+ </li>
+ <li><strong>Link Colors</strong>: Click the colored blocks to select a color
+ for displaying unvisited, active, and visited links on web pages and
+ messages.
+ <ul>
+ <li><strong>Underline links</strong>: Select this to display underlined
+ links on web pages and messages.</li>
+ </ul>
+ </li>
+ <li><strong>When a web page provides its own colors and
+ backgrounds</strong>:
+ <ul>
+ <li><strong>Always use the colors and background specified by the web
+ page</strong>: Allows web pages and messages to choose displayed
+ colors and backgrounds. Your own colors will be used if no colors
+ are defined by the content itself.</li>
+ <li><strong>Use my chosen colors, ignoring the colors and background
+ image specified</strong>: All content uses your own display colors,
+ ignoring the web page or message colors and any background image.</li>
+ <li><strong>Only ignore the page colors when using a High Contrast
+ theme</strong>: Allows web pages and messages to choose displayed
+ colors and backgrounds, <em>unless</em> an accessibility desktop theme
+ is used, in which case your own colors will be used.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#appearance_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="media">Appearance Preferences - Media</h2>
+
+<p>This section describes how to use the Media preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, click Media. (If no subcategories are
+ visible, double-click Appearance to expand the list.)</li>
+</ol>
+
+<p>The Media preferences panel allows you to determine how to handle audio and
+ video content as well as image animation:</p>
+
+<ul>
+ <li><strong>Enable Autoplay of HTML5 media content</strong>:
+ <ul>
+ <li>Check this option to automatically play audio or video media as soon
+ as such content is loaded.</li>
+ <li>Uncheck this option if you want such media to play only when you
+ explicitly request it.</li>
+ </ul>
+ </li>
+ <li><strong>Animated images should loop</strong>: These settings control how
+ many times animated images repeat their animation:
+ <ul>
+ <li><strong>As many times as the image specifies</strong>: Select this if
+ you want image animation to repeat as many times as specified within
+ each image. (This option is selected by default.)</li>
+ <li><strong>Once</strong>: Select this if you want image animation to
+ occur once, overriding the number of times specified within each
+ image.</li>
+ <li><strong>Never</strong>: Select this if you do not want image
+ animation.</li>
+ </ul>
+ </li>
+</ul>
+
+<p><strong>Note</strong>: This options apply to HTML5 media and animated images
+ (e.g., GIF or PNG) only, thus not to any media handled by a plugin.</p>
+
+<p>[<a href="#appearance_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="spelling">Appearance Preferences - Spelling</h2>
+
+<p>This section describes how to use the Spelling preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, click Spelling. (If no subcategories are
+ visible, double-click Appearance to expand the list.)</li>
+</ol>
+
+<p>The Spelling preferences panel allows you to determine how spelling is
+ handled throughout &brandShortName;:</p>
+
+<ul>
+ <li><strong>General</strong>:
+ <ul>
+ <li><strong>Language</strong>: Use the drop-down list to select the language
+ you want to use to check the spelling in your messages or to download more
+ dictionaries.</li>
+ <li><strong>When typing, check my spelling</strong>: Use the drop-down
+ list to select if and how your typing is spell checked.</li>
+ </ul>
+ </li>
+ <li><strong>Mail &amp; Newsgroups</strong>:
+ <ul>
+ <li><strong>Check spelling before sending</strong>: Select this option to
+ have Mail &amp; Newsgroups always check the spelling of your message before
+ you send it.</li>
+ <li><strong>Check spelling as you type</strong>: Select this option to have
+ Mail &amp; Newsgroups always check the spelling of your message as you type
+ it.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#appearance_preferences">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_navigator.xhtml b/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_navigator.xhtml
new file mode 100644
index 0000000000..33bd02a8e7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/cs_nav_prefs_navigator.xhtml
@@ -0,0 +1,716 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Browser Preferences Help</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="navigator_preferences">Browser Preferences</h1>
+
+<p>This section describes how to use the Browser preference panel. If
+ you&apos;re not already viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Choose Browser.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#navigator">Browser</a></li>
+ <li><a href="#history">History</a></li>
+ <li><a href="#languages">Languages</a></li>
+ <li><a href="#helper_applications">Helper Applications</a></li>
+ <li><a href="#location_bar">Location Bar</a></li>
+ <li><a href="#internet_search">Internet Search</a></li>
+ <li><a href="#tabbed_browsing">Tabbed Browsing</a></li>
+ <li><a href="#link_behavior">Link Behavior</a></li>
+ <li><a href="#downloads">Downloads</a></li>
+ </ul>
+</div>
+
+<h2 id="navigator">Browser Preferences - Browser</h2>
+
+<p>This section describes how to use the main browser preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Browser category.</li>
+</ol>
+
+<p>The Browser preferences panel allows you to customize certain aspects of
+ the browser.</p>
+
+<ul>
+ <li><strong>Display on</strong>: Use the drop-down list in combination with
+ the checkboxes to control what will be displayed at startup or when you
+ open a new window or tab:
+ <ol>
+ <li>
+ <ul>
+ <li>Select <strong>Browser Startup</strong> to set what the browser
+ will display at startup.</li>
+ <li>Select <strong>New Window</strong> to set what will be displayed
+ when opening a new browser window.</li>
+ <li>Select <strong>New Tab</strong> to set what will be displayed
+ when opening a new tab.</li>
+ </ul>
+ </li>
+ <li>Select one of the following checkboxes:
+ <ul>
+ <li><strong>Blank page</strong>: Causes the browser to display a blank
+ page.</li>
+ <li><strong>Home page</strong>: Causes the browser to load your home
+ page (specified below).</li>
+ <li><strong>Last page visited</strong>: Causes the browser to load the
+ page you were viewing right before you last exited
+ &brandShortName;.</li>
+ <li><strong>Restore Previous Session</strong>: Causes the browser to
+ restore the windows and tabs you were viewing right before you last
+ exited &brandShortName;, including form data and browsing
+ history. This option is only available in connection with
+ <strong>Display on Browser Startup</strong>.</li>
+ </ul>
+ </li>
+ </ol>
+ </li>
+ <li><strong>When restoring sessions and windows</strong>: Choose how many
+ (if any) browser tabs should be restored at a time upon startup.
+ <ul>
+ <li><strong>Restore all tabs immediately</strong>: All the tabs you had
+ open the last time will be restored and loaded at once. This can affect
+ startup performance negatively if a large amount of tabs needs to be
+ restored in parallel.</li>
+ <li><strong>Restore [__] tab(s) at a time</strong>: If you choose this,
+ only the specified amount of tabs will be restored at a time. If you
+ had more tabs open than specified here, any extra tabs will only be
+ loaded once the loading of one of the other tabs has finished.</li>
+ <li><strong>Only restore tabs when I need them</strong>: This disables
+ automatic restoring of tabs, i.e. tabs will only be loaded if you
+ switch to or reload them.</li>
+ </ul>
+ </li>
+ <li><strong>Website icons</strong>:
+ <ul>
+ <li><strong>Show website icons</strong>: Select this if you want see
+ site-specific icons, if available, in place of the bookmark icon
+ <img src="chrome://communicator/skin/places/bookmark-item.svg"
+ alt="Bookmark item icon"/>. Website icons are shown to the left of the
+ Location Bar and Browser tabs.</li>
+ <li><strong>Try to use the server&apos;s favicon when the page
+ doesn&apos;t define one</strong>: If the page itself does not define a
+ website icon, turning on this setting makes &brandShortName; look for a
+ &quot;favicon&quot; on the server and use that instead.</li>
+ </ul>
+ </li>
+ <li>Home button preferences: In the field, type the web page you want as
+ your home page or do one of the following:
+ <ul>
+ <li><strong>Choose File</strong>: Click this to locate a file on disk
+ that you want to load as your home page.</li>
+ <li><strong>Use Current Page</strong>: Click this to use the web page
+ currently displayed in the browser as your home page.</li>
+ <li><strong>Use Current Group</strong>: If you have two or more browser
+ tabs open, click this to set them as your Home Page Group (a group of
+ tabs that are opened as your home page). After clicking this button,
+ the message <q>Home Page Group is Set</q> appears in the location
+ field.
+
+ <p><strong>Caution</strong>: If you edit the field after clicking Use
+ Current Group, your Home Page Group will be lost.</p>
+
+ </li>
+ <li><strong>Restore Default</strong>: Click this to revert to the
+ default home page.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="history">Browser Preferences - History</h2>
+
+<p>This section describes how to use the History preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click History. (If no subcategories are
+ visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The History preferences panel allows you to configure the history settings
+ for the browser.</p>
+
+<ul>
+ <li><strong>Browsing History</strong>:
+ <ul>
+ <li><strong>Clear History</strong>: Click this to delete the list of
+ websites visited.</li>
+ <li><strong>Remember visited pages</strong>: Select this to make
+ &brandShortName; remember pages you visit within the browsing history.
+ For example, you need that to be able to search for pages you have
+ already visited from the location bar or history window.</li>
+ </ul>
+ </li>
+ <li><strong>Location Bar History</strong>:
+ <ul>
+ <li><strong>Enable Location Bar history</strong>: Check this to enable
+ &brandShortName; to keep the location bar history.</li>
+ <li><strong>Clear Location Bar</strong>: Click this to clear the list of
+ websites in the Location bar menu.</li>
+ </ul>
+ </li>
+ <li><strong>Form and Search History</strong>:
+ <ul>
+ <li><strong>Enable form and search history</strong>: Select this to let
+ &brandShortName; keep a history of the forms you fill in and the
+ searches you do.</li>
+ <li><strong>Remember form and search history for up to [__] days</strong>:
+ Type the maximum number of days you want &brandShortName; to keep track
+ of forms you fill in and searches you do. For example, if you set this
+ number to 180 days, forms and searches 180 days old or less will be
+ kept.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>For more information about history in &brandShortName;, see
+ <a href="nav_help.xhtml#retracing_your_steps">Retracing Your Steps</a>.</p>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="languages">Browser Preferences - Languages</h2>
+
+<p>This section describes how to use the Languages preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Languages. (If no subcategories are
+ visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Languages preferences panel allows you to choose the languages and
+ text encoding for displaying web pages:</p>
+
+<ul>
+ <li><strong>Languages for Web Pages</strong>:
+ <ul>
+ <li><strong>Move Up / Move Down</strong>: Click one of these buttons to
+ move a selected language up or down, which sets the order of preference
+ for the listed languages.</li>
+ <li><strong>Add</strong>: Click this to add additional languages for
+ displaying web pages. In the dialog, select a language from the list.
+ If you want to add a language that is not in the list, type a language
+ code (both two- and three-letter codes can be used) in the field below
+ the list. See the online document
+ <a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">Codes
+ for the Representation of Names of Languages</a> for a complete list of
+ language codes. Click OK to close the dialog and save your
+ changes.</li>
+ <li><strong>Remove</strong>: Click this to remove a selected
+ language.</li>
+ </ul>
+ </li>
+ <li><strong>Text Encoding for Legacy Content</strong>:
+ <ul>
+ <li><strong>Fallback Text Encoding</strong>: Web pages are supposed to
+ declare the text encoding in which they are to be displayed, but legacy
+ content may not do so. Use the drop-down list to select the default
+ text encoding to be used for such web pages. This setting does not
+ affect any web pages which properly declare their encoding.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="helper_applications">Browser Preferences - Helper Applications</h2>
+
+<p>This section describes how to use the Helper Applications preferences panel.
+ If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Helper Applications. (If no
+ subcategories are visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Helper Applications preferences panel lets you choose applications and
+ other handlers to handle different types of content (e.g. PDF documents).
+ It shows you a list of content types and lets you select a handler for each
+ type. To filter the list, use the search field. Text entered in there will
+ narrow the list to entries containing that text either in the type description
+ or the currently selected action.</p>
+<p>You can choose a local application to handle any type. For some types, you
+ can also choose a <a href="glossary.xhtml#web_application">web application</a>
+ to handle the type, choose a <a href="glossary.xhtml#plugin">plugin</a> in
+ &brandShortName; to handle the type, or save the type on your computer.</p>
+<p>To choose a handler for a type, select the type from the list. The current
+ handler for the type will turn into a menu. Open the menu and select the
+ handler you want to handle the type. Depending on the actual type, you can:</p>
+
+<ul>
+ <li>Choose an application by selecting it from the menu. If you want a local
+ application that is not in the menu to handle the type, select
+ <strong>Use other…</strong> from the menu and navigate to its location
+ <span class="unix">or use the native application chooser, if it is
+ available and enabled</span>.</li>
+ <li>Choose a <!-- feature or --> plugin by selecting it from the menu.</li>
+ <li>Save files of this type on your computer by selecting
+ <strong>Save File</strong> from the menu. If you have selected the
+ <strong>Automatically download files to specified download folder</strong>
+ preference in the <strong>Downloads</strong> panel, &brandShortName; will
+ save content of this type on your computer automatically. Otherwise, when
+ you encounter this type, &brandShortName; will prompt you for a location on
+ your computer to save it to.</li>
+ <li>Tell &brandShortName; to <strong>always ask</strong> what to do when
+ encountering this type. When you choose this option, a dialog will always be
+ shown when files of this type are accessed, and you can choose how to handle
+ that specific file from there.</li>
+</ul>
+<span class="unix">
+<p>Some systems have a native application chooser, which help you select
+ suitable applications to handle different types of content:</p>
+
+<ul>
+ <li><strong>Use native application chooser when available</strong>: Select
+ this to enable the native application chooser when it is available.</li>
+</ul>
+</span>
+
+<p><strong>Note:</strong> When a plugin is available to handle a type, and you
+ choose another handler for that type, &brandShortName; will only use your
+ chosen handler when you access the type directly. When the type is embedded
+ inside a web page, &brandShortName; will continue to use the plugin to handle
+ it. See also
+ <a href="nav_help.xhtml#plugins_and_downloads">Plugins and Downloads</a>.
+</p>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="location_bar">Browser Preferences - Location Bar</h2>
+
+<p>This section describes how to use the Location Bar preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Location Bar. (If no subcategories
+ are visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Location Bar Preferences panel lets you fine-tune the behavior of the
+ Location Bar.</p>
+
+<ul>
+ <li id="location_bar_autocomplete"><strong>Autocomplete</strong>:
+ <ul>
+ <li><strong>Autocomplete from your browsing history as you type</strong>:
+ Select this to let &brandShortName; automatically show suggestions from
+ your browsing history when you type in the Location Bar.
+ <ul>
+ <li><strong>Match only websites you&apos;ve typed previously</strong>:
+ Shows only websites that you&apos;ve typed in the Location Bar and
+ not websites that were opened in other ways, such as clicking a link
+ on a web page.</li>
+ <li><strong>Match</strong>:
+ <ul>
+ <li><strong>Anywhere in the location or title</strong>: The
+ autocomplete suggestions will include all websites where what
+ you typed matches any part of the website&apos;s location or
+ title.</li>
+ <li><strong>Anywhere but preferring word boundaries</strong>: The
+ autocomplete suggestions will include all websites where what
+ you typed matches any part of the website&apos;s location or
+ title but matches at word boundaries (see next point) are
+ preferred. This is the default setting.</li>
+ <li><strong>Only on word boundaries</strong>: The autocomplete
+ suggestions will include all websites where what you typed
+ matches the beginning of any word contained in the
+ website&apos;s location or title. Matches may also be found
+ inside a word if it contains medial capital letters (as in
+ CamelCase) since all non-lowercase characters are treated as
+ word boundaries.</li>
+ <li><strong>Only at the beginning of the location or
+ title</strong>: The autocomplete suggestions will include all
+ websites where what you typed matches the beginning of the
+ website&apos;s location or title.</li>
+ </ul></li>
+ <li><strong>Automatically prefill the best match</strong>: As you
+ type in the Location Bar, &brandShortName; will automatically
+ complete your web address using the visited website it most closely
+ matches. <span class="unix"><strong>Note</strong>: Having this
+ option on will prefill local addresses (like paths to files on your
+ hard drive) even if you have turned off <q>Autocomplete from your
+ browsing history as you type</q>.</span></li>
+ <li><strong>Show list of matching results</strong>: As you type in
+ the Location Bar, &brandShortName; will show a drop-down list of
+ matching visited web addresses.
+ <span class="unix"><strong>Note</strong>: If you have turned off
+ <q>Autocomplete from your browsing history as you type</q>
+ matching results from locations on your hard drive will still be
+ shown in the drop-down list.</span></li>
+ </ul></li>
+ <li><strong>Show default Internet search engine</strong>: Shows a drop-down
+ list item, allowing you to search with the default search engine for
+ words you enter.</li>
+ </ul>
+ </li>
+ <li><strong>Formatting</strong>:
+ <ul>
+ <li><strong>Highlight the effective domain for websites and FTP
+ servers</strong>: Grays out all of the URL except for the effective
+ domain, so that it is clear which site you are visiting.</li>
+ <li><strong>Highlight web pages with a high level of connection
+ security</strong>: Colorizes the location bar for the next page
+ loaded if all of its components are fully encrypted.</li>
+ </ul>
+ </li>
+ <li><strong>Unknown Locations</strong>
+ <ul>
+ <li><strong>Add <q>www.</q> and <q>.com</q> to the location if a web page
+ is not found</strong>: Select this if you want &brandShortName; to
+ automatically add <tt>www.</tt> to the beginning and <tt>.com</tt> to
+ the end of a web page location that can&apos;t be found. For more
+ detailed information about this feature, see the online document
+ <a href="http://www.mozilla.org/docs/end-user/domain-guessing.html">
+ Domain Guessing</a>.</li>
+ <li><strong>Perform a web search when entered text is not a web
+ location</strong>: Select this to let &brandShortName; automatically
+ search the web for text entered in the Location Bar. If the text
+ you&apos;ve typed is not a web location, &brandShortName; will do a web
+ search when you press <kbd class="noMac">Enter</kbd>
+ <kbd class="mac">Return</kbd> in the Location Bar.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="internet_search">Browser Preferences - Internet Search</h2>
+
+<p>This section describes how to use the Internet Search preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Internet Search. (If no subcategories
+ are visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Internet Search preferences panel allows you to configure how you search
+ using &brandShortName;:</p>
+
+<ul>
+ <li><strong>Default Search Engine</strong>:
+ <ul>
+ <li><strong>Search using</strong>: Use the drop-down list to select the
+ search engine you want use for web searching.</li>
+ <li><strong>Manage Search Engines</strong>: Click this to open a dialog
+ where you can manage the search engines list. In this dialog you can
+ also select <strong>Show search suggestions</strong> to have
+ &brandShortName; show suggestions from the search engine as you type a
+ search string in the Sidebar search tab or in the Search Bar.</li>
+ </ul>
+ </li>
+ <li><strong>Search Results</strong>:
+ <ul>
+ <li><strong>Open new tabs for sidebar search results</strong>: Select this
+ to have &brandShortName; show your search results in a new tab rather
+ than in the current one when you search using the sidebar.</li>
+ <li><strong>Open a tab instead of a window for a context menu web
+ search</strong>: Select this to have &brandShortName; show your search
+ results in a new tab rather than a new window when you search on
+ selected words in a web page.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="tabbed_browsing">Browser Preferences - Tabbed Browsing</h2>
+
+<p>This section describes how to use the Tabbed Browsing preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Tabbed Browsing. (If no subcategories
+ are visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Tabbed Browsing preferences panel allows you to set up Tabbed
+ Browsing:</p>
+
+<ul>
+ <li><strong>Tab Display</strong>:
+ <ul>
+ <li><strong>Hide the tab bar when only one tab is open</strong>: Select
+ this to display the Tabbed Browsing bar only when more then one
+ browser tab is open.</li>
+ <li><strong>Switch to new tabs opened from links</strong>: Select this to
+ make &brandShortName; switch to the new tab when using <q>Open in a
+ New Tab</q> to open a link.</li>
+ <li><strong>Switch to new tabs opened from diverted links</strong>:
+ Select this to make &brandShortName; switch to the new tab when opening
+ diverted links or links from other applications.</li>
+ <li><strong>Focus browser window for links opened in tabs from other
+ applications</strong>: Select this to make &brandShortName; bring to the
+ foreground the browser window being used to open links in tabs from
+ other applications. When unchecked, the browser window stays in the
+ background.</li>
+ <li><strong>Warn me when closing a window with multiple tabs</strong>:
+ Select this to make &brandShortName; warn you when you try to close a
+ browser window which has multiple tabs open in it.</li>
+ <li><strong>Open related tabs after current tab</strong>:
+ Select this to make new tabs open next to the tab from which they have
+ been opened. When unchecked, new tabs open after the last tab on the
+ tab bar.</li>
+ </ul>
+ </li>
+ <li><strong>Open tabs instead of windows for</strong>:
+ <ul>
+ <li><strong>Middle-click, <span class="mac"><kbd>Cmd</kbd>+click or
+ <kbd>Cmd</kbd>+<kbd>Return</kbd></span>
+ <span class="noMac"><kbd>Ctrl</kbd>+click or
+ <kbd>Ctrl</kbd>+<kbd>Enter</kbd></span> on links in a Web
+ page</strong>: Select this to open Web page links in a new tab
+ when clicking a link with the middle mouse button,
+ <span class="mac">holding down the <kbd>Command</kbd> key whilst
+ clicking on links or holding down the <kbd>Command</kbd> key and
+ pressing <kbd>Return</kbd> on links</span>
+ <span class="noMac"><kbd>Ctrl</kbd>-clicking on links, and pressing
+ <kbd>Ctrl</kbd>+<kbd>Enter</kbd> on links</span>.
+
+ <p><strong>Tip</strong>: Use Find Links as You Type to navigate to the
+ link you want to open with the keyboard commands above.</p>
+ </li>
+
+ <li><strong>
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>+<kbd
+ class="mac">Return</kbd><kbd class="noMac">Enter</kbd> in the Location
+ bar</strong>: Select this to open a Web page in a new tab when you type
+ the URL of the page in the Location Bar and press
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>+<kbd
+ class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.</li>
+ </ul>
+ </li>
+ <li><strong>Open in a new tab instead of a stand-alone window</strong>:
+ <ul>
+ <!-- link to Data Manager description once bug 599097 is fixed -->
+ <li><strong>Data Manager</strong>: Select this to open the Data Manager
+ in a browser tab. If unchecked, it will open in a separate dialog
+ window.</li>
+ <li><strong>Add-ons Manager</strong>: Select this to open the
+ <a href="customize_help.xhtml#using_the_add-ons_manager">Add-ons
+ Manager</a> in a browser tab. If unchecked, it will open in a separate
+ dialog window.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="link_behavior">Browser Preferences - Link Behavior</h2>
+
+<p>This section describes how to use the Link Behavior preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Link Behavior. (If no subcategories
+ are visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Link Behavior preferences panel allows you to configure how links are
+ opened in &brandShortName;:</p>
+
+<ul>
+ <li><strong>Link open behavior</strong>: If a webpage is designed so that
+ certain links open in a new window by default, you may want to override
+ this. You can define the behavior separately for links with a target
+ attribute and windows opened using JavaScript:
+ <ul>
+ <li><strong>Open links meant to open a new window in</strong>:
+ <ul>
+ <li><strong>The current tab/window</strong>: Open the linked page in
+ the current tab of the active window.</li>
+ <li><strong>A new tab in the current window</strong>: Open the linked
+ page in a new tab instead of a new window.</li>
+ <li><strong>A new window</strong>: Open the linked page in a new
+ window. (This does not override the webpage design and disables
+ the below options for pages opened from scripts.)</li>
+ </ul>
+ </li>
+ <li><strong>When scripts want to open a new window</strong>:
+ <ul>
+ <li><strong>Always divert windows into tabs</strong>: Open the page to
+ be loaded in a tab according to the above settings.</li>
+ <li><strong>Don&apos;t divert custom windows into tabs</strong>: Open
+ the page to be loaded in a popup window if the script explicitly
+ specifies features of the new window (such as size or position),
+ else open the page in a tab according to the above settings.</li>
+ <li><strong>Always open new windows</strong>: Open the page to be
+ loaded in a new window. (This does not override the webpage
+ design.)</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ <li><strong>Links from other applications</strong>: If &brandShortName; is
+ called from another application with a webpage address as an argument (like
+ a click on a link in an external email program), you can control where the
+ page will be loaded:
+ <ul>
+ <li><strong>The current tab/window</strong>: Open the linked page in the
+ current tab of the active window.</li>
+ <li><strong>A new tab in the current window</strong>: Open the linked
+ page in a new tab instead of a new window.</li>
+ <li><strong>A new window</strong>: Open the linked page in a new
+ window.</li>
+ </ul>
+ </li>
+</ul>
+
+<div class="win">
+ <p>To ensure that &brandShortName; opens a new window, select the Browser
+ option in <a href="cs_nav_prefs_appearance.xhtml#appearance">Appearance
+ Preferences - Appearance</a>.</p>
+
+ <p>If you want &brandShortName; to open a new tab instead of a new window when
+ you launch it and it is already running, ensure the following:</p>
+
+ <ul>
+ <li>If &brandShortName; is launched from the command line, no command line
+ parameters that open windows (like <kbd>-new-window</kbd>) are used.</li>
+ <li>No <q>When &brandShortName; starts up, open</q> option is set in
+ <a href="cs_nav_prefs_appearance.xhtml#appearance">Appearance Preferences
+ - Appearance</a>.</li>
+ <li>The <q>Links from other applications</q> preference is set to <q>A new
+ tab in the current window</q>.</li>
+ </ul>
+</div>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+<h2 id="downloads">Browser Preferences - Downloads</h2>
+
+<p>This section describes how to use the Downloads preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Browser category, click Downloads. (If no subcategories are
+ visible, double-click Browser to expand the list.)</li>
+</ol>
+
+<p>The Downloads preferences panel allows you to set up how &brandShortName;
+ handles files you download from web pages:</p>
+
+<ul>
+ <li><strong>When starting a download</strong>:
+ <ul>
+ <li><strong>Flash the download manager if it is already open</strong>:
+ Select this to change focus to the Download Manager if it is currently
+ open. It displays the status for current and previous downloads in a
+ single window. This option only takes effect if the Download Manager
+ is already open and overrides the other settings.</li>
+ <li><strong>Open the download manager</strong>: Select this to display
+ the Download Manager when starting a new download. It will be opened
+ if the window isn&apos;t currently shown.</li>
+ <li><strong>Open a progress dialog</strong>: Select this to display a
+ progress dialog box, which display the status for your current
+ download. The status of each download is kept in a separate
+ window.</li>
+ <li><strong>Don&apos;t open anything</strong>: Select this if you want to
+ download files invisibly. No status is given for all your
+ downloads.</li>
+ </ul>
+ </li>
+</ul>
+
+<ul>
+ <li><strong>When saving a file</strong>:
+ <ul>
+ <li><strong>Save files to</strong>: Select this if you want files to be
+ saved to the specified folder without &brandShortName; prompting you
+ for the download location.</li>
+ <li><strong>Always ask me where to save files</strong>: Select this if
+ you always want to be able to choose a folder for the file to be saved.
+ The default will be the folder you last downloaded a file to.</li>
+ </ul>
+ </li>
+</ul>
+
+<ul>
+ <li><strong>Download history</strong>:
+ <ul>
+ <li><strong>Remove download entries</strong>: This determines when
+ a completed entry is removed from the Download Manager listing
+ (the downloaded file itself will <em>not</em> be affected):
+ <ul>
+ <li><strong>When they have completed</strong>: Select this to
+ remove an entry immediately once the download is successfully
+ completed.</li>
+ <li><strong>When quitting &brandShortName;</strong>: Select this
+ to retain all entries until closing the program, the list will
+ be empty upon restarting &brandShortName;.</li>
+ <li><strong>Never</strong>: Select this to not remove <em>any</em>
+ entry automatically, even after &brandShortName; is restarted.
+ You can remove them manually in the Download Manager.</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<ul>
+ <li><strong>When a download completes</strong>:
+ <ul>
+ <li><strong>Play a sound</strong>: Select this if you want
+ &brandShortName; to play a custom sound (typically in WAV format) when
+ a download is completed. Use the Browse button to select the sound file
+ in the file locator. To listen to the sound you&apos;ve chosen, click
+ Play.</li>
+ <li><strong>Show an alert</strong>: Select this if you want
+ &brandShortName; to show an alert on the screen when a download is
+ completed.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>For more information about downloading files from web pages, see
+ <a href= "nav_help.xhtml#plugins_and_downloads">Plugins and
+ Downloads</a>.</p>
+
+<p>[<a href="#navigator_preferences">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/cs_priv_prefs_popup.xhtml b/comm/suite/locales/en-US/chrome/common/help/cs_priv_prefs_popup.xhtml
new file mode 100644
index 0000000000..42046f17a3
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/cs_priv_prefs_popup.xhtml
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Popup Blocking Help</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<div class="boilerPlate">This document is provided for your information only.
+ It may help you take certain steps to protect the privacy and security of
+ your personal information on the Internet. This document does not, however,
+ address all online privacy and security issues, nor does it represent a
+ recommendation about what constitutes adequate privacy and security
+ protection on the Internet.</div>
+
+<h1 id="controlling_popups">Controlling Popups</h1>
+
+<p><strong>What are Popups?</strong></p>
+
+<p>Pop-up windows, or popups, are windows that appear automatically and without
+ your permission. They vary in size, but usually don&apos;t cover the whole
+ screen. Some popups open on top of the current browser window, thus popping
+ up, while others appear underneath the browser (popunders).</p>
+
+<p>&brandShortName; allows you to control both popups and popunders through the
+ <a href="#privacy_and_security_preferences_popup_windows">Popup Windows
+ preferences panel</a>. Since popup blocking is turned off by default, you
+ must enable it to prevent popups from appearing in the browser.</p>
+
+<p>When blocking a popup, &brandShortName; can be set up to play a sound or
+ display an icon
+ <img src="chrome://navigator/skin/icons/popup-blocked.png" alt="Popup control
+ icon"/> in the status bar or a notification bar at the top of the website
+ content area, or any combination of the above. You can use the icon or the
+ bar to add a website you&apos;re viewing to an exceptions list so that the
+ website is allowed to again display popups.</p>
+
+<p><strong>Blocking popups may interfere with some websites</strong>: Some
+ websites, including some banking sites, use popups for important features.
+ Blocking all popups disables such features. To allow specific websites to
+ use popups, while blocking all others, you can add specific websites to the
+ list of allowed websites. For more information, see
+ <a href="#privacy_and_security_preferences_popup_windows">Privacy &amp;
+ Security Preferences - Popup Windows</a>.</p>
+
+<p><strong>Blocking popups doesn&apos;t always work</strong>: Although
+ &brandShortName; blocks most popups, some websites, even when blocked, may
+ use other methods to show popups.</p>
+
+<p><strong>Allowing popups from certain websites</strong>: After you&apos;ve
+ enabled popup blocking, you can still allow specific websites to display
+ popups. Browse to the website, and then from the Tools menu, choose Popup
+ Manager, and then choose Allow Popups From This Website.</p>
+
+<p>The next section describes how to control popups through preferences and
+ through the popup control icon.</p>
+
+<h2 id="privacy_and_security_preferences_popup_windows">Privacy &amp; Security
+ Preferences - Popup Windows</h2>
+
+<p>This section describes how to use the Popup Windows preferences panel. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the
+ <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security Preferences category, click Popup
+ Windows. (If no subcategories are visible, double-click Privacy &amp;
+ Security to expand the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>Block unrequested popup windows</strong>: Select this to prevent
+ popups from appearing in the browser.</li>
+ <li><strong>Allowed Websites</strong>: Click this to view and edit the list of
+ websites that you want to allow to display popups.
+ <ul>
+ <li><strong>Allowed Websites - Popups</strong>: The list of allowed
+ websites appears when you click <q>Allowed Sites</q>. You can add or
+ remove websites that should be allowed to show popups.</li>
+ <li><strong>Allow</strong>: Click this after typing in a website that you
+ want to add to the list.</li>
+ <li><strong>Remove Website</strong>: Click this to remove a selected
+ website.</li>
+ <li><strong>Remove All Websites</strong>: Click this to remove all of the
+ websites in the current list.</li>
+ </ul>
+ </li>
+</ul>
+
+<h3>When a popup window has been blocked</h3>
+
+<ul>
+ <li><strong>Play a sound</strong>: If you want a sound to play each time
+ the browser blocks a popup, select this option and choose System beep
+ or Custom sound file. Selecting Custom sound file enables the following two
+ buttons:
+ <ul>
+ <li><strong>Browse</strong>: Click this to choose a sound file.</li>
+ <li><strong>Play</strong>: Click this to listen to the chosen
+ sound.</li>
+ </ul>
+ </li>
+ <li><strong>Display an icon in the browser status bar</strong>: Select this
+ to display an icon
+ <img src="chrome://navigator/skin/icons/popup-blocked.png" alt="Popup control
+ icon"/> in the browser status bar to indicate that a popup is blocked.
+
+ <p><strong>Note</strong>: After the popup control icon
+ <img src="chrome://navigator/skin/icons/popup-blocked.png" alt="Popup
+ control icon"/> appears, it remains visible until you visit another
+ website.</p>
+ </li>
+ <li><strong>Display a notification bar at the top of the content area</strong>:
+ Select this to display a notification bar above the website content area
+ whenever a popup is blocked. In the bar, use the Preferences button to choose
+ how to handle popups from that website.
+ </li>
+</ul>
+
+<p><strong>Using the popup control icon or notification bar to add allowed
+ websites</strong>: You can use the popup control icon or notification bar to
+ quickly add a website to the list of allowed websites. Click the icon
+ <img src="chrome://navigator/skin/icons/popup-blocked.png" alt="Popup control
+ icon"/> to open the list of allowed websites. The current website is
+ already filled in. Click Allow and then click OK to confirm your addition.
+ Similarly you can use the notification bar to allow popups from the current
+ website or open the list of allowed websites through Manage Popups. Just
+ click the Preferences button at the right end of the notification bar.</p>
+
+<p><strong>Note</strong>: Blocking popups may not always work and may interfere
+ with some websites. For more information about blocking popups, see
+ <a href="#controlling_popups">Controlling Popups</a>.</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/customize_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/customize_help.xhtml
new file mode 100644
index 0000000000..149e6ef124
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/customize_help.xhtml
@@ -0,0 +1,1451 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Customizing &brandShortName;</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<div class="boilerPlate">This document is provided for your information
+ only. It may help you take certain steps to protect the privacy and security
+ of your personal information on the Internet. This document does not,
+ however, address all online privacy and security issues, nor does it
+ represent a recommendation about what constitutes adequate privacy and
+ security protection on the Internet.</div>
+
+<h1 id="customizing_mozilla">Customizing &brandShortName;</h1>
+
+<p>You can customize &brandShortName; to better suit your needs using features
+ like Sidebar, bookmarks, Tabbed Browsing, and Add-ons.</p>
+
+<p>This section describes the customizable aspects of &brandShortName;&apos;s
+ browser component.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#sidebar">Sidebar</a></li>
+ <li><a href="#tabbed_browsing">Tabbed Browsing</a></li>
+ <li><a href="#changing_fonts_colors_and_themes">Changing Fonts, Colors, and
+ Themes</a></li>
+ <li><a href="#toolbars">Toolbars</a></li>
+ <li><a href="#bookmarks">Bookmarks</a></li>
+ <li><a href="#add-ons">Add-ons</a></li>
+ <li><a href="#specifying_how_mozilla_starts_up">Specifying How
+ &brandShortName; Starts Up</a></li>
+ <li><a href="cs_nav_prefs_appearance.xhtml">Appearance
+ Preferences</a></li>
+ <li><a href="cs_nav_prefs_navigator.xhtml">Browser
+ Preferences</a></li>
+ <li><a href="cs_nav_prefs_advanced.xhtml">Advanced Preferences</a></li>
+ </ul>
+</div>
+
+<h2 id="sidebar">Sidebar</h2>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#what_is_sidebar">What is Sidebar?</a></li>
+ <li><a href="#opening_closing_and_resizing_sidebar">Opening, Closing and
+ Resizing Sidebar</a></li>
+ <li><a href="#viewing_sidebar_tabs">Viewing Sidebar Tabs</a></li>
+ <li><a href="#adding_sidebar_tabs">Adding Sidebar Tabs</a></li>
+ <li><a href="#customizing_individual_sidebar_tabs">Customizing Individual
+ Sidebar Tabs</a></li>
+ <li><a href="#reorganizing_sidebar_tabs">Reorganizing Sidebar Tabs</a></li>
+ <li><a href="#removing_sidebar_tabs">Removing Sidebar Tabs</a></li>
+ </ul>
+</div>
+
+<h3 id="what_is_sidebar">What is Sidebar?</h3>
+
+<p>Sidebar is a customizable area in your browser where you can keep items
+ that you need to use all the time, including your bookmarks, browser history,
+ address book, and other available options. Sidebar presents these items to
+ you in tabs that are continually updated.</p>
+
+<p>&brandShortName; comes with some Sidebar tabs already set up, but you can
+ customize Sidebar by adding, removing, and rearranging tabs.</p>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h3 id="opening_closing_and_resizing_sidebar">Opening, Closing and Resizing
+ Sidebar</h3>
+
+<p>To open Sidebar, <span class="noMac">press <kbd>F9</kbd> or</span> open the
+ View menu in the browser, choose Show/Hide, and then Sidebar from the
+ submenu.</p>
+
+<p>Once Sidebar is opened, you can use its handle to close, open and resize
+ Sidebar&apos;s frame. Move the mouse pointer up and down along the left edge
+ of the &brandShortName; window. The pointer changes to a hand when it touches
+ the <q>handle</q> for Sidebar, as shown in the picture.</p>
+
+<table summary="Image table">
+ <tr>
+ <td colspan="2"></td>
+ </tr>
+ <tr style="vertical-align: top;">
+ <td><img src="images/sidebar.png" alt="image of sidebar with
+ handle"/></td>
+ <td style="vertical-align: middle;">
+ <p><strong>Sidebar<br/>Handle</strong></p>
+ </td>
+ </tr>
+</table>
+
+<ul>
+ <li>Click the handle to close/open Sidebar&apos;s frame</li>
+ <li>Click and drag the handle to resize Sidebar&apos;s frame</li>
+</ul>
+
+<p>To close Sidebar with its handle, do one of the following:</p>
+
+<ul>
+ <li class="noMac">Press <kbd>F9</kbd></li>
+ <li>Click the X in the upper-right corner of Sidebar</li>
+ <li>Open the View menu, choose Show/Hide, and then Sidebar from the
+ submenu</li>
+</ul>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h3 id="viewing_sidebar_tabs">Viewing Sidebar Tabs</h3>
+
+<p>To view a tab:</p>
+
+<ul>
+ <li>Click a tab&apos;s title; for instance, click the word <q>Search</q>. The
+ Search tab opens, which allows you to search for web pages.</li>
+</ul>
+
+<p><strong>Tip</strong>: To reload a Sidebar tab, right-click on the tab title
+ and choose Reload from the pop-up menu.</p>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h3 id="adding_sidebar_tabs">Adding Sidebar Tabs</h3>
+
+<p>To add a new tab:</p>
+
+<ol>
+ <li>Click <q>Tabs</q> at the top of Sidebar, and select Customize
+ Sidebar from the menu</li>
+ <li>In the Customize Sidebar dialog box, select a tab from the list on the
+ left. Double-click the folders to open or close folders.</li>
+ <li>Click Add.</li>
+ <li>Continue adding as many tabs as you want.</li>
+ <li>Click OK to finish.</li>
+</ol>
+
+<p><strong>Note</strong>: If you add more than eight tabs to Sidebar,
+ &brandShortName; hides the remaining tabs to reduce clutter. To scroll
+ through the hidden tabs, click the down arrow button at the bottom of Sidebar
+ until you see the desired tab. Click the up arrow button to once again scroll
+ up.</p>
+
+<p><strong>Tips</strong>:</p>
+
+<ul>
+ <li>To preview a Sidebar tab before adding it, select a tab from the list
+ on the left side of the Customize Sidebar dialog box and click Preview.
+ After a few seconds, the tab displays in the Tab Preview pop-up
+ window.</li>
+ <li>To view an extensive and categorized list of tabs available for Sidebar,
+ click <q>Tabs</q> at the top of Sidebar, and select Sidebar
+ Directory.</li>
+</ul>
+
+<p>You can also turn Sidebar tabs on and off.</p>
+
+<ol>
+ <li>Click <q>Tabs</q> at the top of Sidebar. Current tabs are listed in
+ the lower part of the menu.</li>
+ <li>Select the tabs you want displayed in Sidebar. Remove the checkmark
+ (deselect) to turn a tab off (it will still be available from the
+ menu).</li>
+</ol>
+
+<p><strong>Tip</strong>: To quickly turn off a Sidebar tab, right-click on its
+ name and choose Hide Tab.</p>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h3 id="customizing_individual_sidebar_tabs">Customizing Individual Sidebar
+ Tabs</h3>
+
+<p><strong>Note</strong>: Not all tabs can be customized.</p>
+
+<ol>
+ <li>Click <q>Tabs</q> at the top of Sidebar and select Customize Sidebar
+ from the menu.</li>
+ <li>Select an available tab from the list on the right.</li>
+ <li>Click Customize Tab if it is enabled. A window appears with information
+ and options for customizing the tab.
+
+ <p>The instructions vary depending on the source of the tab&mdash;in
+ addition to &brandShortName;, tab providers can be any company,
+ organization, or individual who uses the Internet.</p>
+ </li>
+ <li>After you follow the tab provider&apos;s instructions, close the
+ customization window (or follow the provider&apos;s instructions to close
+ it).</li>
+ <li>Click OK to finish.</li>
+</ol>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h3 id="reorganizing_sidebar_tabs">Reorganizing Sidebar Tabs</h3>
+
+<ol>
+ <li>Click <q>Tabs</q> at the top of Sidebar and select Customize Sidebar from
+ the menu.</li>
+ <li>Select a tab from the list on the right.</li>
+ <li>Click Up and Down to change the tab&apos;s placement.</li>
+ <li>Repeat steps 1 and 2 to continue reorganizing as many tabs as you
+ like.</li>
+ <li>Click OK to finish.</li>
+</ol>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h3 id="removing_sidebar_tabs">Removing Sidebar Tabs</h3>
+
+<ol>
+ <li>Click <q>Tabs</q> at the top of Sidebar and select Customize Sidebar from
+ the menu.</li>
+ <li>Select a tab from the list on the right.</li>
+ <li>Click Remove.</li>
+ <li>Continue removing as many tabs as you like.</li>
+ <li>Click OK to finish.</li>
+</ol>
+
+<p>[<a href="#sidebar">Return to beginning of section</a>]</p>
+
+<h2 id="tabbed_browsing">Tabbed Browsing</h2>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#what_is_tabbed_browsing">What is Tabbed Browsing?</a></li>
+ <li><a href="#setting_up_tabbed_browsing">Setting up Tabbed Browsing</a></li>
+ <li><a href="#opening_tabs">Opening Tabs</a></li>
+ <li><a href="#moving_tabs">Moving Tabs</a></li>
+ <li><a href="#bookmarking_tabs">Bookmarking Tabs</a></li>
+ <li><a href="#closing_tabs">Closing Tabs</a></li>
+ </ul>
+</div>
+
+<h3 id="what_is_tabbed_browsing">What is Tabbed Browsing?</h3>
+
+<p>Tabbed Browsing lets you open more than one web page in a single window.
+ Each web page has its own tab across the top of a single browser window.
+ Each tab appears on the Tab Bar. For example, you can visit mozilla.org,
+ icq.com, and cnn.com within one window instead of three windows.</p>
+
+<table summary="Image table">
+ <tr>
+ <td>Click this to open a new tab.</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <img src="images/tabbed_browsing_bar.png" alt="tab bar"/>
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align: left;">Tab being viewed.</td>
+ <td style="text-align: right;">Click this to close the tab being
+ viewed.</td>
+ </tr>
+ <tr>
+ <td colspan="2" style="text-align: center;"><strong>Tab Bar</strong></td>
+ </tr>
+</table>
+
+<p>You don&apos;t need to have several windows open to visit several web pages;
+ thus, freeing up more space on your desktop. Instead, you can open, close,
+ and reload web pages conveniently in one place without having to switch to
+ another window.</p>
+
+<p>[<a href="#tabbed_browsing">Return to beginning of section</a>]</p>
+
+<h3 id="setting_up_tabbed_browsing">Setting up Tabbed Browsing</h3>
+
+<p>There are several ways to customize Tabbed Browsing. For example, you can
+ change your preferences to open new browser tabs from the Location Bar. You
+ can set up Tabbed Browsing in other ways too, such as loading new browser
+ tabs in the background so the first page is kept on top while the second page
+ is loading. To learn more about setting up Tabbed Browsing in
+ &brandShortName;, see
+ <a href="cs_nav_prefs_navigator.xhtml#tabbed_browsing">Browser Preferences
+ - Tabbed Browsing</a>.</p>
+
+<p>[<a href="#tabbed_browsing">Return to beginning of section</a>]</p>
+
+<h3 id="opening_tabs">Opening Tabs</h3>
+
+<p>You can open a browser tab in the following ways:</p>
+
+<p><strong>Opening a New Blank Browser Tab</strong>:</p>
+
+<ul>
+ <li><strong>From the File menu</strong>: Open the File menu, choose New, and
+ then Browser Tab.</li>
+ <li><strong>From the Tab Bar</strong>: If visible, click the <q>new
+ tab</q> icon
+ <img src="chrome://navigator/skin/icons/tab-new.gif" alt="new tab icon"/>
+ on the left side of the Tab Bar.</li>
+ <li><strong>From a pop-up menu</strong>: If the Tab Bar is visible,
+ right-click on it, and choose New Tab from the pop.</li>
+</ul>
+
+<p><strong>Opening a Web Page Link in a Browser Tab</strong>:</p>
+
+<ul>
+ <li><strong>From a pop-up menu</strong>: Right-click<span class="mac"> or, if
+ you have a one-button mouse, <kbd>Ctrl</kbd>-click</span> on a web page
+ link and choose Open Link in New Tab.</li>
+ <li><strong>From the Location Bar</strong>: Type a web page location in the
+ Location Bar and press <kbd class="mac">Cmd</kbd><kbd class="noMac">
+ Ctrl</kbd>+<kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.
+
+ <p><strong>Note</strong>: You must set your Tabbed Browsing preferences to
+ open a browser tab from the Location Bar. See
+ <a href="cs_nav_prefs_navigator.xhtml#tabbed_browsing">Browser
+ Preferences - Tabbed Browsing</a> for more information.</p>
+ </li>
+</ul>
+
+<p><strong>Tips</strong>:</p>
+
+<ul>
+ <li>To quickly open a new browser tab, press
+ <kbd class="mac">Cmd</kbd>
+ <kbd class="noMac">Ctrl</kbd>+<kbd>T</kbd>.</li>
+ <li>To reload one or all browser tabs, right-click<span class="mac"> or, if
+ you have a one-button mouse, <kbd>Ctrl</kbd>-click</span> anywhere on the
+ Tab Bar and select Reload Tab or Reload All Tabs, respectively.</li>
+</ul>
+
+<p>[<a href="#tabbed_browsing">Return to beginning of section</a>]</p>
+
+<h3 id="moving_tabs">Moving Tabs</h3>
+
+<p>Tabs are displayed in the order you open them, which may not always be what
+ you want. To move a tab to a different location within a &brandShortName;
+ window, simply drag it there using your mouse. While you are dragging the
+ tab, &brandShortName; displays an indicator to show where the tab will be
+ moved. Alternately, you can use
+ <a href="shortcuts_navigator.xhtml#tabbed_browsing_shortcuts">keyboard
+ shortcuts</a> to move tabs within a window if desired.</p>
+
+<p><strong>Note</strong>: The keyboard shortcuts don&apos;t work when a text
+ box has focus.</p>
+
+<p>[<a href="#tabbed_browsing">Return to beginning of section</a>]</p>
+
+<h3 id="bookmarking_tabs">Bookmarking Tabs</h3>
+
+<p>To bookmark the group of browser tabs in the current window:</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose <q>Bookmark This Group of
+ Tabs</q>.</li>
+ <li>Type a name for the bookmark group in the Name field.</li>
+ <li>Choose an existing folder in which to create the bookmark group, or click
+ New Folder to create a new folder.</li>
+ <li>Click <q>Add Bookmarks</q> to add the bookmarks.</li>
+</ol>
+
+<p>The bookmark group will be saved as a folder, with the name you entered in
+ the Name field, inside the folder you selected in the Folder drop-down
+ list.</p>
+
+<p><strong>Tip</strong>: To learn how to use a group of tabs as your home page,
+ see <a href="cs_nav_prefs_navigator.xhtml#navigator">Browser Preferences -
+ Browser</a>.</p>
+
+<p>[<a href="#tabbed_browsing">Return to beginning of section</a>]</p>
+
+<h3 id="closing_tabs">Closing Tabs</h3>
+
+<p>You can close browser tabs in several ways:</p>
+
+<p><strong>Closing the Browser Tab Being Viewed</strong>:</p>
+
+<ul>
+ <li>Open the File menu and choose Close Tab.</li>
+ <li>Click the <q>X</q> button on the right side of the Tab Bar.</li>
+</ul>
+
+<table summary="Image table">
+ <tr>
+ <td> Click this to open a new tab.</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <img src="images/tabbed_browsing_bar.png" alt="tab bar"/>
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align: left;">Tab being viewed.</td>
+ <td style="text-align: right;">Click this to close the tab being
+ viewed.</td>
+ </tr>
+ <tr>
+ <td colspan="2" style="text-align: center;"><strong>Tab Bar</strong></td>
+ </tr>
+</table>
+
+<p><strong>Tips</strong>:</p>
+
+<ul>
+ <li>To close any browser tab, even if hidden, right-click on the tab and
+ choose Close Tab from the pop-up menu.</li>
+ <li>To keep only one browser tab open, while closing all other tabs,
+ right-click on the browser tab and choose Close Other Tabs.</li>
+</ul>
+
+<p>[<a href="#tabbed_browsing">Return to beginning of section</a>]</p>
+
+<h2 id="changing_fonts_colors_and_themes">Changing Fonts, Colors, and
+ Themes</h2>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#changing_the_default_fonts">Changing the Default Fonts</a></li>
+ <li><a href="#changing_the_default_colors">Changing the Default
+ Colors</a></li>
+ <li><a href="#changing_the_theme">Changing the Theme</a></li>
+ </ul>
+</div>
+
+<h3 id="changing_the_default_fonts">Changing the Default Fonts</h3>
+
+<p>Normally, web pages are displayed in the default font set by your browser
+ or in a font chosen by the web pages&apos; authors.</p>
+
+<p>To change the default fonts:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, choose Fonts. (If no options are visible
+ in this category, double-click Appearance to expand the list.)</li>
+ <li>From the "Fonts for" drop-down list, choose a language group/script.
+ For instance, to set default fonts for West European languages/script
+ (Latin), choose <q>Western</q>.</li>
+ <li>Select whether proportional text should be serif (like Times Roman) or
+ sans-serif (like Arial). Then specify the font size you want for
+ proportional text.</li>
+ <li>If an appropriate font is available for your language/script, select
+ fonts for Serif, Sans-Serif, Cursive, Fantasy, and Monospace. You can also
+ specify what font size you want for monospace text.</li>
+ <li>Specify whether the default font should be serif or sans serif.</li>
+ <li>Select a fixed-width font and size. Certain types of text, such as
+ equations and formulas, are displayed in a fixed-width font.</li>
+</ol>
+
+<p>Many web page authors choose their own fonts and font sizes. To allow fonts
+ other than the ones specified in your preferences, check <q>Allow
+ documents to use other fonts</q>.</p>
+
+<p>[<a href="#changing_fonts_colors_and_themes">Return to beginning of
+ section</a>]</p>
+
+<h3 id="changing_the_default_colors">Changing the Default Colors</h3>
+
+<p>Normally, the background and text colors on web pages are determined by the
+ default colors set by your browser or by the pages&apos; authors.</p>
+
+<p>To change the default colors:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Appearance category, choose Colors. (If no options are visible
+ in this category, click to expand the list.)</li>
+ <li>Click the colored blocks next to Text, Background, Unvisited Links, and
+ Visited Links. Choose a color for each from the color chart. You can also
+ specify that links should be underlined.</li>
+</ol>
+
+<p>Most web page authors choose their own colors. You can override the
+ authors&apos; intentions by selecting <q>Use my chosen colors, ignoring
+ the colors specified</q>.</p>
+
+<p>When viewing the source of a web page, you can see the HTML syntax of the
+ source of a web page highlighted in specific colors by selecting <q>Enable
+ syntax highlighting</q>.</p>
+
+<p>[<a href="#changing_fonts_colors_and_themes">Return to beginning of
+ section</a>]</p>
+
+<h3 id="changing_the_theme">Changing the Theme</h3>
+
+<p>You can change the look and feel of &brandShortName; by using a different
+ theme. Changing the theme can be done either from the View menu or from the
+ Add-ons Manager.</p>
+
+<p>From the View menu:</p>
+
+<ol>
+ <li>Choose Apply Theme, and then select a theme from the menu.</li>
+ <li>Quit and restart &brandShortName;.</li>
+</ol>
+
+<p>You can get new themes by selecting Get More Themes. This will open a new
+ tab or window and load the Themes webpage. Here you can download the latest
+ themes and apply them. Another way to install new themes is through the
+ Add-ons Manager (see below). Lightweight themes are applied immediately when
+ you select "Add to &brandShortName;".</p>
+
+<p>From the Add-ons Manager:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Add-ons Manager.</li>
+ <li>Click the Appearance button in the toolbar.</li>
+ <li>Select a theme from the list, and then click the Enable button.</li>
+ <li>Restart &brandShortName; by clicking <q>Restart Now</q> in the
+ Theme&apos;s entry.</li>
+</ol>
+
+<p>[<a href="#changing_fonts_colors_and_themes">Return to beginning of
+ section</a>]</p>
+
+<h2 id="toolbars">Toolbars</h2>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#menu_bar">Menu Bar</a></li>
+ <li><a href="#navigation_toolbar">Navigation Toolbar</a></li>
+ <li><a href="#personal_toolbar">Bookmarks Toolbar</a></li>
+ <li><a href="#status_bar">Status Bar</a></li>
+ <li><a href="#component_bar">Component Bar</a></li>
+ <li><a href="#hiding_a_toolbar">Hiding a Toolbar</a></li>
+ </ul>
+</div>
+
+<h3 id="menu_bar">Menu Bar</h3>
+
+<p>The Menu Bar provides drop down menus for performing actions within
+ &brandShortName;.</p>
+
+<table summary="Image table">
+ <tr>
+ <td><img src="images/menubar.png" alt="Menu Bar"/></td>
+ </tr>
+ <tr>
+ <td style="text-align: center;"><strong>Menu Bar</strong></td>
+ </tr>
+</table>
+
+<p class="noMac">If the Menu Bar is hidden (not minimized; see
+ <a href="#hiding_a_toolbar">Hiding a Toolbar</a>), or if you are in Full
+ Screen mode, press the Alt key to display the Menu Bar.</p>
+
+<p>[<a href="#toolbars">Return to beginning of section</a>]</p>
+
+<h3 id="navigation_toolbar">Navigation Toolbar</h3>
+
+<p>The Navigation Toolbar, pictured here, helps you move around the Web.</p>
+
+<table summary="Image table">
+ <tr>
+ <td colspan="4">
+ <img src="images/reload.gif" alt="navigation toolbar"/>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>Back</strong></td>
+ <td><strong>Forward</strong></td>
+ <td><strong>Reload</strong></td>
+ <td><strong>Stop</strong></td>
+ </tr>
+</table>
+
+<p>[<a href="#toolbars">Return to beginning of section</a>]</p>
+
+<h3 id="personal_toolbar">Bookmarks Toolbar</h3>
+
+<p>The Bookmarks Toolbar is completely customizable&mdash;you decide what you
+ want to keep there.</p>
+
+<table summary="Image table">
+ <tr>
+ <td><img src="images/personalbar.png" alt="Bookmarks Toolbar"/></td>
+ </tr>
+ <tr>
+ <td style="text-align: center;"><strong>Bookmarks Toolbar</strong></td>
+ </tr>
+</table>
+
+<p>You can easily add, delete, and rearrange items in the Bookmarks Toolbar.</p>
+
+<h4 id="adding_personal_toolbar_bookmarks">Adding Bookmarks Toolbar
+ Bookmarks</h4>
+
+<p>You can add buttons for your favorite bookmarks, or folders containing
+ groups of bookmarks. To create a new bookmark to add to the Bookmarks
+ Toolbar:</p>
+
+<ol>
+ <li>Open a web page you want to bookmark.</li>
+ <li>Drag the bookmark icon
+ <img src="chrome://communicator/skin/places/bookmark-item.svg"
+ alt="image of bookmark icon"/>(located to the left of URL in the
+ Location Bar) to a desired place on the Bookmarks Toolbar. You can drag the
+ icon directly to the Bookmarks Toolbar, or to a folder on the Bookmarks
+ Toolbar. For more information, see
+ <a href="#adding_bookmark_folders_to_the_personal_toolbar">Adding Bookmark
+ Folders to the Bookmarks Toolbar</a>.</li>
+</ol>
+
+<p><strong>Note</strong>: The bookmark icon
+ <img src="chrome://communicator/skin/places/bookmark-item.svg" alt="image
+ of bookmark icon"/> may appear as another page-specific icon if you have
+ checked Show Website Icons in preferences. See
+ <a href="cs_nav_prefs_appearance.xhtml#appearance">Appearance Preferences -
+ Appearance</a> for more information on changing this preferences.</p>
+
+<p>Each item in the Bookmarks Toolbar folder appears as a toolbar button. You
+ may need to enlarge the browser window to see them all.</p>
+
+<p id="adding_bookmark_folders_to_the_personal_toolbar"><strong>Adding
+ Bookmark Folders to the Bookmarks Toolbar</strong></p>
+
+<p>You can add bookmark folders to the Bookmarks Toolbar to sort your favorite
+ bookmarks into categories. For example, you can have one folder on the
+ Bookmarks Toolbar for hobby-related bookmarks and another folder for
+ work-related bookmarks. To add a new bookmark to the Bookmarks Toolbar:</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>Select the <q>Bookmarks Toolbar</q> folder.</li>
+ <li>Choose New Folder from the File menu.</li>
+ <li>Type a name for your new bookmark folder. By default, the name is
+ <q>New Folder</q>.</li>
+ <li>Click Add to confirm your new bookmark folder name.</li>
+</ol>
+
+<p>The new bookmark folder will appear at the end of the Bookmarks Toolbar.</p>
+
+<h4>Removing Bookmarks from the Bookmarks Toolbar</h4>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>Click the Bookmarks Toolbar Folder.</li>
+ <li>Select the bookmark or folder you want to delete.</li>
+ <li>Press Delete on your keyboard.</li>
+ <li>Close the Library window.</li>
+</ol>
+
+<p><strong>Tip</strong>: To quickly remove a bookmark placed on the Bookmarks
+ Toolbar, right-click on the bookmark and select Delete.</p>
+
+<h4>Rearranging the Bookmarks Toolbar</h4>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>In your Bookmarks window, click the Bookmarks Toolbar Folder.</li>
+ <li>Select a bookmark or folder and drag it to a new location.</li>
+ <li>When you are finished rearranging items, close your Bookmarks
+ window.</li>
+</ol>
+
+<p><strong>Tip</strong>: To move a bookmark placed on the Bookmarks Toolbar
+ quickly, click and drag the bookmark to another location on the Bookmarks
+ Toolbar or to a folder.</p>
+
+<p>[<a href="#toolbars">Return to beginning of section</a>]</p>
+
+<h3 id="status_bar">Status Bar</h3>
+
+<p>The Status Bar is located at the bottom of any &brandShortName; window. It
+ includes the following:</p>
+
+<ul>
+ <li>Component Bar: Allows you to switch between components. For more
+ information, see <a href="#component_bar">Component Bar</a>.</li>
+ <li>Status information: Displays information like the web-page URL and load
+ status information.</li>
+ <li>Work Offline
+ <img src="chrome://communicator/skin/icons/offline.png"
+ alt="work offline icon"/> or Work Online
+ <img src="chrome://communicator/skin/icons/online.png"
+ alt="work online icon"/> icon: Click the icon to toggle working
+ offline or online. Working offline prevents &brandShortName; from
+ attempting to connect to the Internet, for example to load images on web
+ pages or automatically check email.</li>
+ <li>Lock icon (Example:
+ <img src="chrome://communicator/skin/icons/lock-insecure.png"
+ alt="lock icon"/>): Indicates whether the entire contents of the page
+ was encrypted while it was being received by your computer. For more
+ information, see
+ <a href="using_certs_help.xhtml#checking_security_for_a_web_page">Checking
+ Security for a Web Page</a>.</li>
+</ul>
+
+<p>[<a href="#toolbars">Return to beginning of section</a>]</p>
+
+<h3 id="component_bar">Component Bar</h3>
+
+<p>Use the Component Bar at the bottom left of any &brandShortName; window to
+ switch between tasks (such as browsing or mail).</p>
+
+<p><img src="images/taskbar.png" alt="component bar"/></p>
+
+<p>[<a href="#toolbars">Return to beginning of section</a>]</p>
+
+<h3 id="hiding_a_toolbar">Hiding a Toolbar</h3>
+
+<p>There are two ways to hide the toolbars.</p>
+
+<p>To minimize a toolbar:</p>
+
+<ul>
+ <li>Click the small triangle at the left of the toolbar. To show the toolbar,
+ click the triangle again. (Note: You cannot hide the Component Bar using
+ this method.)</li>
+</ul>
+
+<p>To completely hide a toolbar, including its triangle:</p>
+
+<ol>
+ <li>Open the View menu.</li>
+ <li>Choose Show/Hide and uncheck the toolbars you want to hide.</li>
+</ol>
+
+<p>To reverse this action, open the View menu, choose Show/Hide, and then
+ select the toolbars you want to show. You can also right-click on an empty
+ section on the <span class="noMac">Menu Bar,</span> Navigation Toolbar or
+ Bookmarks Toolbar to turn toolbars on and off.</p>
+
+<p>[<a href="#toolbars">Return to beginning of section</a>]</p>
+
+<h2 id="bookmarks">Bookmarks</h2>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#what_are_bookmarks">What Are Bookmarks?</a></li>
+ <li><a href="#using_bookmarks">Using Bookmarks</a></li>
+ <li><a href="#creating_new_bookmarks">Creating New Bookmarks</a></li>
+ <li><a href="#organizing_your_bookmarks">Organizing Your Bookmarks</a></li>
+ <li><a href="#changing_individual_bookmarks">Changing Individual
+ Bookmarks</a></li>
+ <li><a href="#searching_your_bookmarks">Searching Your Bookmarks</a></li>
+ <li><a href="#exporting_or_importing_a_bookmark_list">Exporting or
+ Importing a Bookmark List</a></li>
+ </ul>
+</div>
+
+<h3 id="what_are_bookmarks">What Are Bookmarks?</h3>
+
+<p>Bookmarks are shortcuts to your favorite and most-visited web pages. Rather
+ than typing in long URLs (web addresses), you can create bookmarks that take
+ you directly to the pages you want to see.</p>
+
+<p>You access your bookmarks through the Bookmarks menu, the Bookmarks tab on
+ Sidebar, and the Library window. You can control what&apos;s listed
+ in the Bookmarks menu by adding bookmarks for your favorite web pages and
+ organizing your list of bookmarks any way you want.</p>
+
+<h3 id="using_bookmarks">Using Bookmarks</h3>
+
+<p>&brandShortName; comes with some bookmarks already available. To use a
+ bookmark:</p>
+
+<ol>
+ <li>Open the Bookmarks menu.</li>
+ <li>Choose a bookmark from the list or from a folder in the list.</li>
+</ol>
+
+<p>[<a href="#bookmarks">Return to beginning of section</a>]</p>
+
+<h3 id="creating_new_bookmarks">Creating New Bookmarks</h3>
+
+<p>You can bookmark your favorite websites to make it easy to return to
+ them.</p>
+
+<p>To bookmark the current page, perform one of these steps:</p>
+
+<ul>
+ <li>To add a bookmark to the Bookmarks menu, open the Bookmarks menu and
+ choose Bookmark This Page.</li>
+ <li>To add a bookmark to a specific folder on the Bookmarks menu, or to
+ provide a specific name or URL for your bookmark:
+ <ol>
+ <li>Open the Bookmarks menu and choose File Bookmark. Choose from any of
+ these options:
+ <ul>
+ <li><strong>Name</strong>: Enter a name for the bookmark if you want
+ a different name.</li>
+ <li><strong>Folder</strong>: Choose a folder in which to create
+ your bookmark.</li>
+ <li><strong>New Folder</strong>: Click this to create a new folder in
+ which to create your bookmark.</li>
+ </ul>
+ </li>
+ <li>Click Done to apply your changes.</li>
+ </ol>
+
+ <p><strong>Tip</strong>: If you have multiple browser tabs open in a
+ window, you can select <q>Bookmark This Group of Tabs</q> to add a folder
+ with bookmarks for all of the open tabs in the current window.</p>
+ </li>
+ <li>To add a bookmark to the Bookmarks Toolbar, drag the bookmark icon
+ <img src="chrome://communicator/skin/places/bookmark-item.svg"
+ alt="image of bookmark icon"/> next to the Location Bar to a place on
+ the Bookmarks Toolbar. You can drag a bookmark to the following places:
+ <ul>
+ <li>In the Bookmarks folder on the Bookmarks Toolbar.</li>
+ <li>In a bookmarks folder you&apos;ve created on the Bookmarks
+ Toolbar.</li>
+ <li>To the Bookmarks Toolbar itself, on the right side of all bookmarks
+ folders.
+ <p>For more information about adding bookmarks to the Bookmarks Toolbar,
+ see <a href="#adding_personal_toolbar_bookmarks">Adding Bookmarks
+ Toolbar bookmarks</a>.</p>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<table summary="Image table">
+ <tr>
+ <td><img src="images/personalbar.png" alt="Bookmarks Toolbar"/></td>
+ </tr>
+ <tr>
+ <td><strong>Bookmarks Toolbar</strong></td>
+ </tr>
+</table>
+
+<ul>
+ <li>To add a bookmark to the Bookmarks tab in Sidebar, open Sidebar, select
+ the Bookmarks tab, and drag the bookmark icon
+ <img src="chrome://communicator/skin/places/bookmark-item.svg"
+ alt="image of bookmark icon"/> next to the Location Bar to a place on
+ the bookmark list in the Bookmarks tab.
+ </li>
+</ul>
+
+<p><strong>Notes</strong>:</p>
+<ul>
+ <li>The bookmark icon
+ <img src="chrome://communicator/skin/places/bookmark-item.svg"
+ alt="image of bookmark icon"/> may appear as another page-specific
+ icon if you have checked Show Website Icons in preferences. See
+ <a href="cs_nav_prefs_appearance.xhtml#appearance">Appearance Preferences -
+ Appearance</a> for more information on changing this preferences.</li>
+ <li>After adding a bookmark using any of the methods listed above, it can be
+ accessed using the Sidebar Bookmarks tab, the Library window or
+ the Bookmarks menu.</li>
+</ul>
+
+<p>[<a href="#bookmarks">Return to beginning of section</a>]</p>
+
+<h3 id="organizing_your_bookmarks">Organizing Your Bookmarks</h3>
+
+<p>To organize your bookmarks, open the Bookmarks menu and choose Manage
+ Bookmarks. Perform any of the following tasks in your Library window.</p>
+
+<p>To view bookmarks inside of folders:</p>
+
+<ul>
+ <li>Double-click a folder to view its contents.</li>
+</ul>
+
+<p>To move a bookmark or a folder to another location in the list:</p>
+
+<ul>
+ <li>Drag the bookmark or folder that you want to move to the new location. To
+ put a bookmark in a folder, drag it to the folder.</li>
+</ul>
+
+<p>To create a new folder or separator:</p>
+
+<ul>
+ <li>Choose New Folder or New Separator from the File menu. The
+ new folder or separator appears below the current selection.</li>
+</ul>
+
+<p>To remove a bookmark or a folder from the list:</p>
+
+<ol>
+ <li>Click to highlight the bookmark or folder that you want to remove.</li>
+ <li>Press the Delete key on your keyboard, or choose Delete from the Edit
+ menu.</li>
+</ol>
+
+<p>To sort your bookmarks in the Library window:</p>
+
+<ul>
+ <li>Select the folder you want to sort.</li>
+ <li>To sort bookmarks by Name, right-click on the folder and select Sort By
+ Name.</li>
+ <li>To sort bookmarks in other ways, open the Edit menu and select Sort
+ Folder. In the dialog, choose how you want the list sorted.
+
+ <p><strong>Tip</strong>: To add more columns, open the View menu, open
+ <q>Show columns</q>, and select a column header in the list.</p></li>
+</ul>
+
+<p> [<a href="#bookmarks">Return to beginning of section</a>]</p>
+
+<h3 id="changing_individual_bookmarks">Changing Individual Bookmarks</h3>
+
+<p>You can change the information for any individual bookmark.</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>In your Library window, click a bookmark.</li>
+ <li>Properties for the currently selected bookmark will appear in the bottom
+ part of the window.</li>
+ <li>Click the arrow next to <q>More</q> to show additional property
+ fields.</li>
+</ol>
+
+<p>You can rename the bookmark (the name appears in your bookmark list),
+ add descriptive information, or set a
+ <a href="nav_help.xhtml#custom_keywords">keyword</a>.</p>
+
+<p> [<a href="#bookmarks">Return to beginning of section</a>]</p>
+
+<h3 id="searching_your_bookmarks">Searching Your Bookmarks</h3>
+
+<p>To search the bookmarks list:</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>In your Library window, click the fill-in field in the top right corner
+ and type all or part of the name or URL (web address) for the bookmarks that
+ you want to find.</li>
+ <li>Double-click a bookmark in the list to go to that page.</li>
+</ol>
+
+<p><strong>Tip</strong>: If the list is hard to read, try expanding the
+ window.</p>
+
+<p>[<a href="#bookmarks">Return to beginning of section</a>]</p>
+
+<h3 id="exporting_or_importing_a_bookmark_list">Exporting or Importing a
+ Bookmark List</h3>
+
+<p>Your bookmarks can be exported to an HTML file in a folder of your
+ choosing. You can then edit it and treat it as you would any HTML file.</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>In your Library window, open the Tools menu, and choose <q>Export
+ Bookmarks to HTML</q>.</li>
+ <li>In the <q>Export Bookmarks File</q> dialog box, choose a folder and
+ filename.</li>
+ <li>Click Save.</li>
+</ol>
+
+<p>Your &brandShortName; bookmarks are not altered by this procedure.</p>
+
+<p>You can also import bookmarks from other sources. For example, you can
+ import bookmarks from earlier &brandShortName; versions, other browsers, or
+ from bookmarks files that your friends send you.</p>
+
+<p>Before you start, make sure that the bookmarks file you want to import is an
+ HTML file.</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>In your Library window, open the Tools menu and choose <q>Import Bookmarks
+ from HTML</q>.</li>
+ <li>In the <q>Import Bookmarks File</q> dialog box, navigate to and select the
+ bookmarks file you want to import, then click Open.</li>
+</ol>
+
+<p>The imported bookmarks are added to the bottom of the Bookmarks Menu
+ folder.</p>
+
+<p><strong>Note</strong>: Importing a bookmarks file imports the bookmarks and
+ folders from that file. It does not create two bookmarks files.</p>
+
+<p>[<a href="#bookmarks">Return to beginning of section</a>]</p>
+
+<h2 id="add-ons">Add-ons</h2>
+
+<p>One of the most exciting ways to customize &brandShortName; is through the
+ addition of Add-ons. In this section, you will learn what Add-ons are, how
+ to install them, and how to use the Add-ons Manager.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#about_add-ons">About Add-ons</a></li>
+ <li><a href="#installing_add-ons">Installing Add-ons</a></li>
+ <li><a href="#using_the_add-ons_manager">Using the Add-ons Manager</a></li>
+ <li><a href="#using_the_add-on_converter">Using the Add-on Converter</a></li>
+ </ul>
+</div>
+
+<h3 id="about_add-ons">About Add-ons</h3>
+
+<p>An Add-on is a piece of software that can be added to &brandShortName; to
+ change its appearance, behavior, or to add new features. It can also change
+ the language shown in the user interface. Add-ons can be classified in four
+ types:</p>
+
+<ul>
+ <li><strong>Extensions</strong>: This type of Add-on either changes the way
+ &brandShortName; behaves or adds new features to it. For instance, there
+ are extensions that provide you with weather forecasts, that add a
+ full-featured FTP client, or that block advertisements in web pages. Some
+ extensions are designed to work only with specific websites.</li>
+ <li><strong>Themes</strong>: Themes change the appearance and design of
+ &brandShortName;, but don&apos;t add or change features. They act as a
+ skin. &brandShortName; ships with two themes, Default Theme and Modern
+ Theme, but you can add more to fit your style.</li>
+ <li><strong>Plugins</strong>: Plugins act as connectors between
+ &brandShortName; and other programs. They enable you to see special
+ content directly inside the &brandShortName; browser window, instead of
+ opening the target program in a separate window.</li>
+ <li><strong>Languages</strong>: While you can download &brandShortName; in
+ a number of languages, you may add additional languages to it in the form
+ of language packs. This way, different users may use &brandShortName; in
+ their own language without having to install the program for everyone.
+ This is specially useful if you are going to install &brandShortName; on a
+ computer that is shared by different users in, for example, a computer lab.
+
+ <p><strong>Note</strong>: Language packs only change the user interface
+ language. Web pages, messages, newsgroup and news &amp; blog posts will
+ still be shown in the original language in which they were written.</p>
+ </li>
+</ul>
+
+<p>Add-ons always come as packages. Each Add-on is a single file with the XPI
+ extension, except in the case of plugins which usually are binaries specific
+ to the target operating system.</p>
+
+<p>[<a href="#add-ons">Return to beginning of section</a>]</p>
+
+<h3 id="installing_add-ons">Installing Add-ons</h3>
+
+<p>There are a number of ways to install Add-ons:</p>
+
+<ul>
+ <li>By clicking a link or a button for an Add-on on a web page. This will
+ trigger a &brandShortName; dialog asking you to confirm or cancel the
+ installation, with the Install button disabled for some seconds in
+ order to avoid accidentally clicking it just as the dialog shows up.</li>
+ <li>By opening a previously saved Add-on package, just like you open a
+ regular file that you previously saved on your hard drive: Select Open file
+ from the File menu, or press <span class="mac"><kbd>Cmd</kbd></span><span
+ class="noMac"><kbd>Ctrl</kbd></span>+<kbd>O</kbd>; you can also open the
+ Tools menu in the Add-ons Manager (next to the search box) and select
+ <q>Install Add-on from File</q>, or just drag-and-drop the installer file
+ into the Add-ons Manager tab or window.</li>
+ <li>By using the Search functionality or the Get Add-ons panel of the
+ Add-ons Manager, as described in the next section.</li>
+</ul>
+
+<p><strong>Notes</strong>:</p>
+<ul>
+ <li>For security, &brandShortName; only allows installing Add-ons from
+ a list of permitted websites in the
+ <a href="cs_nav_prefs_advanced.xhtml#software_installation">Software
+ Installation preferences panel</a>.</li>
+ <li>Also, if you unchecked <q>Allow websites to install add-ons and
+ updates</q> there, you will be prompted to enable that function before
+ anything else happens. This is <em>not</em> a temporary change, you would
+ have to uncheck that box again to disallow installations and updates in
+ the future.</li>
+ <li>Plugins are usually installed as a separate program while
+ &brandShortName; is closed so both the external program and the
+ corresponding plugin for &brandShortName; get installed correctly.</li>
+</ul>
+
+<p>[<a href="#add-ons">Return to beginning of section</a>]</p>
+
+<h3 id="using_the_add-ons_manager">Using the Add-ons Manager</h3>
+
+<p>The Add-ons Manager enables you to install and uninstall Add-ons, enable and
+ disable them and, in some cases, set preferences for them.</p>
+
+<p id="accessing_the_add-ons_manager">To access the Add-ons Manager, follow any
+ of these steps:</p>
+
+<ul>
+ <li>Open the Tools menu, then select Add-ons Manager.</li>
+ <li>Open the <span class="mac">&brandShortName;</span><span
+ class="noMac">Edit</span> menu and choose Preferences, expand the Advanced
+ category, click Software Installation, then Manage Add-ons.</li>
+</ul>
+
+<p>The Add-ons Manager opens either in its own dialog window or in a browser
+ tab. You can modify this behavior in the <a
+ href="cs_nav_prefs_navigator.xhtml#tabbed_browsing">Tabbed Browsing</a>
+ preference settings.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#searching_for_add-ons">Searching for Add-ons</a></li>
+ <li><a href="#the_get_add-ons_panel">The Get Add-ons panel</a></li>
+ <li><a href="#the_extensions_panel">The Extensions panel</a></li>
+ <li><a href="#the_appearance_panel">The Appearance panel</a></li>
+ <li><a href="#the_plugins_panel">The Plugins panel</a></li>
+ <li><a href="#the_languages_panel">The Languages panel</a></li>
+ <li><a href="#updating_add-ons">Updating Add-ons</a></li>
+ </ul>
+</div>
+
+<h4 id="searching_for_add-ons">Searching for Add-ons</h4>
+
+<p>After <a href="#accessing_the_add-ons_manager">accessing the Add-ons
+ Manager</a>, you can search all available Mozilla Add-ons directly from
+ the Add-ons Manager and install them as desired:</p>
+
+<ul>
+ <li>To search for Add-ons based on name, description or tags, type a
+ word or phrase into the Search box and press
+ <span class="mac"><kbd>Return</kbd></span><span
+ class="noMac"><kbd>Enter</kbd></span>. The search panel will open and
+ display a list of matching Add-ons that are compatible with your current
+ version of &brandShortName;. You can sort the results by name or date of
+ the most recent release. You can also search your installed Add-ons by
+ clicking <q>My Add-ons</q>.</li>
+ <li>To get additional information about a specific Add-on in the list,
+ click <q>More</q> for that entry.</li>
+ <li>To install an Add-on in the list, click its Install button.</li>
+</ul>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h4 id="using_the_add-on_converter">Using the Add-on Converter</h4>
+
+<p>The <a href="http://addonconverter.fotokraina.com/">Add-on Converter</a>
+ has been created to convert Firefox and Thunderbird extensions for use in
+ &brandShortName;. You can try to convert any extension by uploading it from
+ your computer or by pasting the download URL from
+ <a href="https://addons.thunderbird.net">addons.thunderbird.net</a> (AMO)
+ into the appropriate input line on the Add-on Converter start page. If you
+ are an expert user, you can make adjustments in the Advanced Options area.
+ </p>
+
+<p>Not all Firefox and Thunderbird extensions will work in &brandShortName;.
+ Most extensions will install successfully, but some may not work properly once
+ installed. The <a href="http://addonconverter.fotokraina.com/compatibility/">
+ compatibility list</a> will help you to anticipate how well a converted
+ extension will work.</p>
+
+<p>The easiest way to use the Add-on Converter is to install the
+ <a href="https://addons.thunderbird.net/seamonkey/addon/amo-browsing-for-seamonkey/">
+ AMO Browsing for SeaMonkey</a> extension, which will overlay additional
+ information and links onto the Add-on pages at addons.thunderbird.net. If
+ the Add-on is not already compatible with &brandShortName;, a link will be
+ added to the Add-on Converter, which will select appropriate conversion
+ parameters automatically.</p>
+
+<p>[<a href="#using_the_add-on_converter">Return to beginning of section</a>]</p>
+
+<h4 id="the_get_add-ons_panel">The Get Add-ons panel</h4>
+
+<p>The Get Add-ons panel in the Add-ons Manager uses web services provided by
+ the Mozilla Add-ons website to present a list of recommended Add-ons, as
+ well as giving you the option to browse the available Mozilla Add-ons. After
+ <a href="#accessing_the_add-ons_manager">accessing the Add-ons Manager</a>,
+ click the Get Add-ons button to show the Get Add-ons panel:</p>
+
+<ul>
+ <li>To get more information about one of the recommended Add-ons in the list
+ displayed by default, click that entry. The entry will expand to use the
+ full tab, showing screenshots, a description of the Add-on, and reviews
+ as available. You will also be able to directly install the Add-on by
+ clicking the Add to &brandShortName; button inside the expanded entry.
+ <p><strong>Note</strong>: If you disallowed websites to install add-ons
+ and updates in the <a
+ href="cs_nav_prefs_advanced.xhtml#software_installation">Software
+ Installation preferences panel</a>, you will be prompted to enable
+ that function before anything else happens. This is <em>not</em> a
+ temporary change, you would have to uncheck that box again to
+ disallow installations and updates in the future.</p>
+ </li>
+ <li>The Get Add-ons panel also allows you to open the Mozilla Add-ons website
+ in a new browser window (or a new browser tab, depending on your preference
+ settings) to browse extensions and themes.</li>
+</ul>
+
+<p><strong>Note</strong>: Personalized Add-on recommendations require a list of
+ your currently installed Add-ons to be sent to the Mozilla Add-ons website.
+ If you have privacy concerns, this function can be switched off in the
+ <a href="cs_nav_prefs_advanced.xhtml#software_installation">Software
+ Installation preferences panel</a>.</p>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h4 id="the_extensions_panel">The Extensions panel</h4>
+
+<p>The Extensions panel in the Add-ons Manager lists the installed extensions.
+ After <a href="#accessing_the_add-ons_manager">accessing the Add-ons
+ Manager</a>, click the Extensions button to show the Extensions panel. There,
+ you can perform the following actions:</p>
+
+<ul>
+ <li>To get more information about any of the installed extensions, click
+ <q>More</q> for the entry in the list. The entry will expand into the full
+ tab and show the description of the extension along with its update
+ options.
+
+ <p><strong>Note</strong>: Disabled extensions will appear greyed out.
+ Incompatible extensions will appear greyed out, since they are also
+ disabled, and with a forbidden sign over the extension icon (incompatible
+ extensions are those which define themselves as not compatible for the
+ version of &brandShortName; you are using).</p>
+ </li>
+ <li>To access options of an extension (if the extension features a
+ preferences panel), click the Options button for the entry corresponding
+ to the desired extension.
+
+ <p><strong>Note</strong>: If the extension does not feature a preferences
+ panel, the Options button will be hidden. You can&apos;t access
+ preferences of disabled or incompatible extensions.</p>
+ </li>
+ <li>To disable an extension, click the Disable button for the entry
+ corresponding to the desired extension. An info bar appears if
+ the changes will apply only once you restart &brandShortName;.
+ The info bar features a <q>Restart Now</q> option.</li>
+ <li>To enable an extension, click the Enable button for the entry
+ corresponding to the desired extension. An info bar appears if
+ the changes will apply only once you restart &brandShortName;.
+ The info bar features a <q>Restart Now</q> option.</li>
+ <li>To uninstall an extension, click the Remove button for the entry
+ corresponding to the desired extension. An info bar appears if
+ the changes will apply only once you restart &brandShortName;.
+ The info bar features a <q>Restart Now</q> option.</li>
+</ul>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h4 id="the_appearance_panel">The Appearance panel</h4>
+
+<p>The Appearance panel in the Add-ons Manager lists the installed themes. After
+ <a href="#accessing_the_add-ons_manager">accessing the Add-ons Manager</a>,
+ click the Appearance button to show the Appearance panel. There, you can
+ perform the following actions:</p>
+
+<ul>
+ <li>To get more information about any of the installed themes, click
+ <q>More</q> for the entry in the list. The entry will expand into the
+ full tab and show the description of the theme along with its version
+ information.</li>
+ <li>To use a theme, click the Enable button for the entry corresponding
+ to the desired theme. An info bar appears if the changes will apply only
+ once you restart &brandShortName;. The info bar features a <q>Restart
+ Now</q> option.
+
+ <p><strong>Note</strong>: Contrary to extensions, you can use only one
+ theme at a time.</p>
+ </li>
+ <li>To uninstall a theme, click the Remove button for the entry corresponding
+ to the desired theme. An info bar appears if the changes will apply only
+ once you restart &brandShortName;. The info bar features a <q>Restart
+ Now</q> option.</li>
+</ul>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h4 id="the_plugins_panel">The Plugins panel</h4>
+
+<p>The Plugins panel in the Add-ons Manager lists the plugins found. After
+ <a href="#accessing_the_add-ons_manager">accessing the Add-ons Manager</a>,
+ click the Plugins button to show the Plugins panel. There, you can perform
+ the following actions:</p>
+
+<ul>
+ <li>To get more information about any of the plugins found, click <q>More</q>
+ for the entry in the list. The entry will expand into the full tab and show
+ the description of the plugin along with its version information.</li>
+ <li>Click <q>Check to see if your plugins are up to date</q> to open a new
+ tab or window connecting to a Mozilla website to check for status and
+ update information on all of your enabled plugins.</li>
+ <li>To disable a plugin, click the Disable button for the entry corresponding
+ to the desired plugin.</li>
+ <li>To enable a plugin, click the Enable button for the entry corresponding
+ to the desired plugin.</li>
+</ul>
+
+<p><strong>Notes</strong>:</p>
+
+<ul>
+ <li>Contrary to other types of Add-ons, enabling and disabling plugins
+ doesn&apos;t require restarting &brandShortName;.</li>
+ <li>Info bars may appear for any plugins which are determined to be outdated,
+ incompatible, or blocked for security reasons.</li>
+</ul>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h4 id="the_languages_panel">The Languages panel</h4>
+
+<p>The Languages panel in the Add-ons Manager lists the installed language
+ packs. This panel only appears if you have installed a language pack.</p>
+
+<p>After <a href="#accessing_the_add-ons_manager">accessing the Add-ons
+ Manager</a>, click the Languages button to show the Languages panel. There,
+ you can perform the following actions:</p>
+
+<ul>
+ <li>To get more information about any of the language packs, click <q>More</q>
+ for the entry in the list. The entry will expand into the full tab and show
+ the description of the language pack along with its update options.</li>
+ <li>To disable a language pack, click the Disable button for the entry
+ corresponding to the desired language pack.</li>
+ <li>To enable a language pack, click the Enable button for the entry
+ corresponding to the desired language pack.</li>
+ <li>To uninstall a language pack, click the Remove button for the entry
+ corresponding to the desired language pack.</li>
+</ul>
+
+<p><strong>Note</strong>: Enabling a language pack in the Language panel
+ doesn&apos;t change the language shown in &brandShortName;&apos;s user
+ interface, it only makes the language available for selection in the
+ <a href="cs_nav_prefs_appearance.xhtml#appearance">Appearance Preferences
+ panel</a>.</p>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h4 id="updating_add-ons">Updating Add-ons</h4>
+
+<p>Updates are by default performed automatically for any Add-ons installed
+ through the Search or Get Add-ons panels if such updates are offered through
+ the Mozilla Add-ons website. The global settings can be found in the
+ <a href="cs_nav_prefs_advanced.xhtml#software_installation">Software
+ Installation preferences panel</a>. You can override those for individual
+ Add-ons by clicking <q>More</q> and then selecting the desired option.
+</p>
+
+<p>Update functions are also offered in the Add-ons Manager&apos;s Tools menu,
+ found next to the search box:</p>
+
+<ul>
+ <li>You can check for updates for all installed Add-ons manually and set or
+ reset automated updates for all Add-ons.</li>
+ <li>If new updates are found for one or more Add-ons, an Update Now button
+ appears for each Add-on. They are also listed in a new Available Updates
+ panel. Rather than updating each Add-on separately, the Include in Update
+ checkbox can be used to select the updates to be performed, then click the
+ Install Updates button.</li>
+ <li>View Recent Updates opens a Recent Updates panel which lists Add-ons of
+ all types for which updates have been performed, along with the date of
+ that update.</li>
+</ul>
+
+<p>[<a href="#using_the_add-ons_manager">Return to beginning of section</a>]</p>
+
+<h2 id="specifying_how_mozilla_starts_up">Specifying How &brandShortName;
+ Starts Up</h2>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#specifying_a_starting_page">Specifying a Starting Page</a></li>
+ <li><a href="#session_restore">Session Restore</a></li>
+ <li><a href="#changing_your_home_page">Changing Your Home Page</a></li>
+ <li><a href="#specifying_which_components_open_at_launch">Specifying Which
+ Components Open at Launch</a></li>
+ </ul>
+</div>
+
+<h3 id="specifying_a_starting_page">Specifying a Starting Page</h3>
+
+<p>You can specify the page that loads when the browser starts:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under "Display on Browser Startup" choose whether you want a
+ blank page, your home page, or the last web page visited to open
+ automatically when you launch your browser. Alternatively you can
+ tell &brandShortName; to restore the previous session, i.e. the
+ windows and tabs you had open when you last exited &brandShortName;.
+ <p><strong>Note</strong>: If you selected Home Page, type the URL in the
+ Location Bar.</p></li>
+</ol>
+
+<p>[<a href="#specifying_how_mozilla_starts_up">Return to beginning of
+ section</a>]</p>
+
+<h3 id="session_restore">Session Restore</h3>
+
+<p>&brandShortName; periodically saves your browsing session (open windows
+ and tabs, including form data) to disk. When you start &brandShortName; with
+ Session Restore enabled, the windows and tabs from your previous session
+ will be restored. This is especially useful if your previous browsing session
+ ended unexpectedly (e.g. your computer crashed or a website you visited forced
+ &brandShortName; to terminate). &brandShortName; will automatically restore
+ the previous session if "Restore Previous Session" under "Display on Browser
+ Startup" has been selected. If you chose to not be warned when you close a
+ browser window with multiple tabs open (see <a href="#tabbed_browsing">Tabbed
+ Browsing</a> preference panel), &brandShortName; will open a page from
+ where you can choose which windows/tabs from the previous session you want to
+ restore. The same will happen if &brandShortName; crashes repeatedly.</p>
+
+<p>[<a href="#specifying_how_mozilla_starts_up">Return to beginning of
+ section</a>]</p>
+
+<h3 id="changing_your_home_page">Changing Your Home Page</h3>
+
+<p>Your home page is the page that opens when you click the Home button in the
+ Bookmarks Toolbar. Depending on how your preferences are set, it may also be
+ the page that opens automatically when you launch &brandShortName;.</p>
+
+<p>To specify your home page:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Browser category.</li>
+ <li>In the Home page section, perform one of the following:
+ <ul>
+ <li>Type your home page&apos;s URL (web address) in the Location
+ field.</li>
+ <li>Click Use Current Page to make the page currently displayed in the
+ browser window your home page.</li>
+ <li>Click Choose File to select a file from your computer&apos;s hard
+ drive.</li>
+ </ul>
+ </li>
+</ol>
+
+<p><strong>Tip</strong>: To specify your home page quickly, drag the bookmark
+ icon <img src="chrome://communicator/skin/places/bookmark-item.svg"
+ alt="image of bookmark icon"/> from the Location Bar to the Home Page
+ button on the Bookmarks Toolbar.</p>
+
+<p>[<a href="#specifying_how_mozilla_starts_up">Return to beginning of
+ section</a>]</p>
+
+<h3 id="specifying_which_components_open_at_launch">Specifying Which Components
+ Open at Launch</h3>
+
+<p>You can choose components (such as Mail &amp; Newsgroups and Composer) to
+ launch when you start &brandShortName;:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Appearance category.</li>
+ <li>Select the components you want opened automatically each time you start
+ &brandShortName;.</li>
+</ol>
+
+<p>[<a href="#specifying_how_mozilla_starts_up">Return to beginning of
+ section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/developer_tools.xhtml b/comm/suite/locales/en-US/chrome/common/help/developer_tools.xhtml
new file mode 100644
index 0000000000..7263619d3e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/developer_tools.xhtml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Tools and Development</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="web_development_tools">Web Development Tools</h1>
+
+<p>For web developers, &brandShortName; provides several tools to aid in
+ developing and debugging <a href="glossary.xhtml#web_application">web
+ applications</a>. Some of these are optional installs. To access these
+ tools, open the Tools menu and choose Web Development.</p>
+
+<ul class="separate">
+ <li id="js_console">
+ <strong>Error Console</strong>: a console window that reports problems
+ with <a href="glossary.xhtml#javascript">JavaScript</a> and CSS code in
+ web applications and the &brandShortName; application itself. By default,
+ CSS parsing errors and JavaScript errors are displayed. The console can
+ also be used to display logged messages from <a href="glossary.xhtml#xul">
+ XUL</a> and JavaScript code.
+ </li>
+ <li id="devtools">
+ <strong>Developer Tools</strong>: &brandShortName; includes a set of
+ developer tools that can be docked to the bottom of a browser window or
+ used in a separate window. The <strong>Toggle Tools</strong> command in
+ the Web Development menu can be used to open and close the Developer Tools.
+
+ The Developer Tools are shared with Firefox. To learn more about the
+ Developer Tools, visit the <a href="https://developer.mozilla.org/docs/Tools">
+ Firefox Developer Tools site</a>.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/glossary.xhtml b/comm/suite/locales/en-US/chrome/common/help/glossary.xhtml
new file mode 100644
index 0000000000..db71e63651
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/glossary.xhtml
@@ -0,0 +1,912 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Glossary</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<div class="boilerPlate">This glossary is provided for your information only,
+ and is not meant to be relied upon as a complete or authoritative description
+ of the terms defined below or of the privacy and/or security ramifications of
+ the technologies described.</div>
+
+<h1 id="glossary">Glossary</h1>
+
+<dl>
+
+<dt id="add-on">add-on</dt><dd>A piece of software that can be added to
+ &brandShortName; to change its appearance, behavior, or to add new features.
+ It can also change the language shown in the user interface. See also
+ <a href="#extension">extension</a>, <a href="#language_pack">language
+ pack</a>, <a href="#plugin">plugin</a>, and <a href="#theme">theme</a>.</dd>
+
+<dt id="authentication">authentication</dt><dd>The use of a password,
+ certificate, personal identification number (PIN), or other information to
+ validate an identity over a computer network. See also
+ <a href="#password-based_authentication">password-based authentication</a>,
+ <a href="#certificate-based_authentication">certificate-based
+ authentication</a>, <a href="#client_authentication">client
+ authentication</a>, <a href="#server_authentication">server
+ authentication</a>, <a href="#secure_authentication">secure
+ authentication</a>.</dd>
+
+<dt id="bookmark">bookmark</dt><dd>A stored <a href="#web_page">web page</a>
+ address (<a href="#url">URL</a>) that you can go to easily by clicking a
+ bookmark icon in the <a href="#personal_toolbar">Bookmarks Toolbar</a> or
+ choosing the bookmark&apos;s name from the Bookmarks menu.</dd>
+
+<dt id="ca">CA</dt><dd>See <a href="#certificate_authority">certificate
+ authority (CA)</a></dd>
+
+<dt id="ca_certificate">CA certificate</dt><dd>A certificate that
+ identifies a certificate authority. See also
+ <a href="#certificate_authority">certificate authority (CA)</a>,
+ <a href="#subordinate_ca">subordinate CA</a>, <a href="#root_ca">root
+ CA</a>.</dd>
+
+<dt id="cache">cache</dt><dd>A collection of web page copies stored
+ on your computer&apos;s hard disk or in its random-access memory (RAM). The
+ browser accumulates these copies as you browse the Web. When you click a link
+ or type a <a href="#url">URL</a> to fetch a particular web page for which the
+ cache already contains a copy, the browser compares the cached copy to the
+ original. If there have been no changes, the browser uses the cached copy
+ rather than refetching the original, saving processing and download
+ time.</dd>
+
+<dt id="caret_browsing">caret browsing</dt><dd>A &brandShortName; feature that
+ allows you to navigate through text in Web pages and email messages (or this
+ Help window) with a caret. Using your keyboard, you can navigate and select
+ text like you do in a text editor. You can turn caret browsing mode on or off
+ by pressing the <kbd>F7</kbd> key. Caret browsing mode can also be enabled or
+ disabled in Advanced Preferences - Keyboard Navigation.</dd>
+
+<dt id="certificate">certificate</dt><dd>The digital equivalent of an ID card.
+ A certificate specifies the name of an individual, company, or other entity
+ and certifies that a public key, which is included in the certificate,
+ belongs to that entity. When you digitally sign a message or other data, the
+ digital signature for that message is created with the aid of the private key
+ that corresponds to the public key in your certificate. A certificate is
+ issued and digitally signed by a <a href="#certificate_authority">certificate
+ authority (CA)</a>. A certificate&apos;s validity can be verified by checking
+ the CA&apos;s <a href="#digital_signature">digital signature</a>. Also called
+ digital ID, digital passport, public-key certificate, X.509 certificate, and
+ security certificate. See also <a href="#public-key_cryptography">public-key
+ cryptography</a>.</dd>
+
+<dt id="certificate_authority">certificate authority (CA)</dt><dd>A service
+ that issues a certificate after verifying the identity of the person or
+ entity the certificate is intended to identify. A CA also renews and revokes
+ certificates and generates a list of revoked certificates at regular
+ intervals. CAs can be independent vendors or a person or organization using
+ certificate-issuing server software (such as &brandShortName; Certificate
+ Management System). See also <a href="#certificate">certificate</a>,
+ <a href="#crl">CRL (certificate revocation list)</a>.</dd>
+
+<dt id="certificate_backup_password">certificate backup password</dt><dd>A
+ password that protects a certificate that you are backing up or have
+ previously backed up. Certificate Manager asks you to set this password when
+ you back up a certificate, and requests it when you attempt to restore a
+ certificate that has previously been backed up.</dd>
+
+<dt id="certificate-based_authentication">certificate-based
+ authentication</dt><dd>Verification of identity based on
+ <a href="#certificate">certificates</a> and public-key cryptography.
+ See also <a href="#password-based_authentication">password-based
+ authentication</a>.</dd>
+
+<dt id="certificate_chain">certificate chain</dt><dd>A hierarchical series of
+ certificates signed by successive certificate authorities. A CA certificate
+ identifies a <a href="#certificate_authority">certificate authority (CA)</a>
+ and is used to sign certificates issued by that authority. A CA certificate
+ can in turn be signed by the CA certificate of a parent CA and so on up to a
+ <a href="#root_ca">root CA</a>.</dd>
+
+<dt id="certificate_fingerprint">certificate fingerprint</dt><dd>
+ A unique number associated with a certificate. The number is not part of
+ the certificate itself but is produced by applying a mathematical function to
+ the contents of the certificate. If the contents of the certificate change,
+ even by a single character, the function produces a different number.
+ Certificate fingerprints can therefore be used to verify that certificates
+ have not been tampered with.</dd>
+
+<dt id="certificate_manager">Certificate
+ Manager</dt><dd>The part of the browser that allows you to view and manage
+ certificates. To view the main Certificate Manager window: Open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu, choose Preferences, click Privacy and Security, and then click Manage
+ Certificates.</dd>
+
+<dt id="certificate_renewal">certificate renewal</dt><dd>The process of
+ renewing a <a href="#certificate">certificate</a> that is about to
+ expire.</dd>
+
+<dt id="certificate_verification">certificate verification</dt><dd>When
+ <a href="#certificate_manager">Certificate Manager</a> verifies a
+ certificate, it confirms that the digital signature was created by a CA whose
+ own CA certificate is both on file with Certificate Manager and marked as
+ trusted for issuing that kind of certificate. It also confirms that the
+ certificate being verified has not itself been marked as untrusted. Finally,
+ if the <a href="#ocsp">OCSP (Online Certificate Status Protocol)</a> has been
+ activated, Certificate Manager also performs an online check. It does so by
+ looking up the certificate in a list of valid certificates maintained at a
+ <a href="#url">URL</a> that is specified either in the certificate itself or
+ in the browser&apos;s Validation preferences. If any of these checks fail,
+ Certificate Manager marks the certificate as unverified and won&apos;t
+ recognize the identity it certifies.</dd>
+
+<dt id="cipher">cipher</dt><dd>See
+ <a href="#cryptographic_algorithm">cryptographic algorithm</a>.</dd>
+
+<dt id="client">client</dt><dd>Software (such as browser software) that sends
+ requests to and receives information from a <a href="#server">server</a>,
+ which is usually running on a different computer. A computer on which client
+ software runs is also described as a client.</dd>
+
+<dt id="client_authentication">client authentication</dt><dd>The process of
+ identifying a <a href="#client">client</a> to a <a href="#server">server</a>,
+ for example with a name and password or with a
+ <a href="#client_ssl_certificate">client SSL certificate</a> and some
+ digitally signed data. See also <a href="#ssl">SSL (Secure
+ Sockets Layer)</a>, <a href="#server_authentication">server
+ authentication</a>.</dd>
+
+<dt id="client_ssl_certificate">client SSL certificate</dt><dd>A certificate
+ that a <a href="#client">client</a> (such as browser software) presents to a
+ <a href="#server">server</a> to authenticate the identity of the client
+ (or the identity of the person using the client) using the
+ <a href="#ssl">SSL (Secure Sockets Layer)</a> protocol. See
+ also <a href="#client_authentication">client authentication</a>.</dd>
+
+<dt id="component_bar">Component Bar</dt><dd>The toolbar located at the bottom
+ left of any &brandShortName; window. The Component Bar allows you to switch
+ between &brandShortName; components by clicking icons for Browser,
+ Mail &amp; Newsgroups, Composer, and so on.</dd>
+
+<dt id="cookie">cookie</dt><dd>A small bit of information stored on your
+ computer by some <a href="#website">websites</a>. When you visit such a
+ website, the website asks your browser to place one or more cookies on your
+ hard disk. Later, when you return to the website, your browser sends the
+ website the cookies that belong to it. Cookies help websites keep track of
+ information about you, such as the contents of your shopping cart. You can set
+ your cookie preferences to control how cookies are used and how much
+ information you are willing to let websites store on them. See also
+ <a href="#third-party_cookie">third-party cookie</a>.</dd>
+
+<dt id="cookie_manager">Cookie Manager</dt><dd>The part of the browser
+ that you can use to control <a href="#cookie">cookies</a>.</dd>
+
+<dt id="cram_md5">CRAM-MD5</dt><dd>A
+ <a href="#cryptographic_algorithm">cryptographic algorithm</a> used for
+ <a href="#encrypted_password">password encryption</a> to achieve
+ <a href="#secure_authentication">secure authentication</a>.</dd>
+
+<dt id="crl">CRL (certificate revocation list)</dt><dd>A list of revoked
+ certificates that is generated and signed by a
+ <a href="#certificate_authority">certificate authority (CA)</a>. You can
+ download the latest CRL to your browser or to a server, then check against it
+ to make sure that certificates are still valid before permitting their use
+ for authentication.</dd>
+
+<dt id="cryptographic_algorithm">cryptographic algorithm</dt><dd>A set of
+ rules or directions used to perform cryptographic operations such as
+ <a href="#encryption">encryption</a> and
+ <a href="#decryption">decryption</a>. Sometimes called a
+ <em>cipher.</em></dd>
+
+<dt id="cryptography">cryptography</dt><dd>The art and practice of scrambling
+ (encrypting) and unscrambling (decrypting) information. For example,
+ cryptographic techniques are used to scramble an unscramble information
+ flowing between commercial websites and your browser. See also
+ <a href="#public-key_cryptography">public-key cryptography</a>.</dd>
+
+<dt id="decryption">decryption</dt><dd>The process of unscrambling data that
+ has been encrypted. See also <a href="#encryption">encryption</a>.</dd>
+
+<dt id="digital_id">digital ID</dt><dd>
+ See <a href="#certificate">certificate</a>.</dd>
+
+<dt id="digital_signature">digital signature</dt><dd>A code created from both
+ the data to be signed and the private key of the signer. This code is unique
+ for each new piece of data. Even a single comma added to a message changes
+ the digital signature for that message. Successful validation of your digital
+ signature by appropriate software not only provides evidence that you
+ approved the transaction or message, but also provides evidence that the data
+ has not changed since you digitally signed it. A digital signature has
+ nothing to do with a handwritten signature, although it can sometimes be used
+ for similar legal purposes. See also
+ <a href="#nonrepudiation">nonrepudiation</a>,
+ <a href="#tamper_detection">tamper detection</a>.</dd>
+
+<dt id="distinguished_name">distinguished name (DN)</dt><dd>A specially
+ formatted name that uniquely identifies the subject of a
+ <a href="#certificate">certificate</a>.</dd>
+
+<dt id="do_not_track">Do Not Track</dt><dd>A mechanism that allows users
+ to inform <a href="#website">websites</a> that they do not want to be
+ <a href="#user_tracking">tracked</a> by third-party websites and
+ <a href="#web_application">web applications</a>. A user&apos; tracking
+ preferences is included in the <a href="#http">HTTP</a> header and sent
+ to the website. &brandShortName; supports sending <q>Do Not Track</q>
+ requests, but websites are not obligated to honor those.</dd>
+
+<dt id="dual_key_pairs">dual key pairs</dt><dd>Two public-private key
+ pairs&mdash;four keys altogether&mdash;corresponding to two separate
+ certificates. The private key of one pair is used for signing operations, and
+ the public and private keys of the other pair are used for encryption and
+ decryption operations. Each pair corresponds to a separate
+ <a href="#certificate">certificate</a>. See also
+ <a href="#public-key_cryptography">public-key cryptography</a>.</dd>
+
+<dt id="eavesdropping">eavesdropping</dt><dd>Surreptitious interception of
+ information sent over a network by an entity for which the information is not
+ intended.</dd>
+
+<dt id="encrypted_password">encrypted password</dt><dd>Used for
+ <a href="#password-based_authentication">password-based authentication</a>
+ to achieve <a href="#secure_authentication">secure authentication</a>.
+ The user&apos;s password is encrypted before it is sent to the server
+ (e.g., by methods like <a href="#cram_md5">CRAM-MD5</a>) to prevent that
+ anyone eavesdropping on the connection from seeing it in clear text. This
+ mechanism is frequently used when no <a href="#secure_connection">secure
+ connection</a> method is available.</dd>
+
+<dt id="encryption">encryption</dt><dd>The process of scrambling information in
+ a way that disguises its meaning. For example, encrypted connections between
+ computers make it very difficult for third-parties to unscramble, or
+ <em>decrypt</em>, information flowing over the connection. Encrypted
+ information can be decrypted only by someone who possesses the appropriate
+ key. See also <a href="#public-key_cryptography">public-key
+ cryptography</a>.</dd>
+
+<dt id="encryption_certificate">encryption certificate</dt><dd>A
+ <a href="#certificate">certificate</a> whose public key is used for
+ encryption only. Encryption certificates are not used for signing operations.
+ See also <a href="#dual_key_pairs">dual key pairs</a>,
+ <a href="#signing_certificate">signing certificate</a>.</dd>
+
+<dt id="encryption_key">encryption key</dt><dd>A private key used for
+ encryption only. An encryption key and its equivalent private key, plus a
+ <a href="#signing_key">signing key</a> and its equivalent public key,
+ constitute a <a href="#dual_key_pairs">dual key pairs</a>.</dd>
+
+<dt id="extension">extension</dt><dd>A type of <a href="#add-on">add-on</a>
+ that changes the behavior of &brandShortName; or adds new features to
+ it.</dd>
+
+<dt id="feed">feed</dt><dd>A frequently updated source of references to web
+ pages, usually blog articles or news. Technically it is an XML document
+ available through a public well-known URL, comprising of several items inside,
+ each one containing some metadata (possibly including a summary) and an URL
+ to the full blog or news article. The XML document is regenerated at fixed
+ intervals, or whenever a new article is published to the website. Web
+ applications can subscribe to the URL serving the feed and present the new
+ articles as they are updated in the underlying XML document. There are
+ specific XML formats for feeds, most common of which are
+ <a href="#rss">RSS</a> and Atom.</dd>
+
+<dt id="fingerprint">fingerprint (certificate)</dt><dd>See
+ <a href="#certificate_fingerprint">certificate fingerprint</a>.</dd>
+
+<dt id="fingerprinting">fingerprinting (browser)</dt><dd>A method of
+ <a href="#user_tracking">user tracking</a> by which a user is identified
+ based on browser characteristics like browser and operating system versions,
+ stated language preferences, or <a href="#plugin">plugins</a> installed.</dd>
+
+<dt id="fips_pubs_140-1">FIPS PUBS 140-1</dt><dd>Federal Information Processing
+ Standards Publications (FIPS PUBS) 140-1 is a US government standard for
+ implementations of cryptographic modules&mdash;that is, hardware or software
+ that encrypts and decrypts data or performs other cryptographic operations
+ (such as creating or verifying digital signatures). Many products sold to the
+ US government must comply with one or more of the FIPS standards.</dd>
+
+<dt id="foreign_cookie">foreign cookie</dt><dd>See
+ <a href="#third-party_cookie">third-party cookie</a>.</dd>
+
+<dt id="frame">frame</dt><dd>Frames are <a href="#web_page">web pages</a>
+ contained inside of an all-encompassing <q>meta</q> page.</dd>
+
+<dt id="ftp">FTP (File Transfer Protocol)</dt><dd>A
+ standard that allows users to transfer files from one computer to another
+ over a network. You can use your browser to fetch files using FTP.</dd>
+
+<dt id="geolocation_service">geolocation service</dt><dd>A web service for
+ <a href="#location_aware_browsing">location aware browsing</a>.</dd>
+
+<dt id="grippy">grippy</dt><dd>A small box element to the left of menubars and
+ toolbars. The grippy allows the user to quickly collapse menubars and
+ toolbars. <span class="mac">Grippies are not available on macOS.</span>
+ </dd>
+
+<dt id="gssapi">GSSAPI (Generic Security Services Application Program
+ Interface)</dt><dd>See <a href="#kerberos">Kerberos</a>.</dd>
+
+<dt id="helper_application">helper application</dt><dd>Any application that is
+ used to open or view a file downloaded by the browser. A
+ <a href="#plugin">plugin</a> is a special kind of helper application that
+ installs itself into the Plugins directory of the main browser installation
+ directory and can typically be opened within the browser itself (internally).
+ Microsoft Word, Adobe Photoshop, and other external applications are
+ considered helper applications but not plugins, since they don&apos;t
+ install themselves into the browser directory, but can be opened from the
+ download dialog box.</dd>
+
+<dt id="home_page">home page</dt><dd>The page your browser is set to display
+ every time you launch it or when you click the Home button. Also used to
+ refer to the main page for a website, from which you can explore the rest of
+ the website.</dd>
+
+<dt id="html">HTML (HyperText Markup Language)</dt><dd>The document format used
+ for web pages. The HTML standard defines tags, or codes, used to define the
+ text layout, fonts, style, images, and other elements that make up a web
+ page.</dd>
+
+<dt id="http">HTTP (HyperText Transfer Protocol)</dt><dd>The protocol used to
+ transfer <a href="#web_page">web pages</a> (HyperText documents) between
+ browsers and <a href="#server">servers</a> over the
+ <a href="#world_wide_web">World Wide Web</a>.</dd>
+
+<dt id="https">HTTPS (HyperText Transfer Protocol Secure)</dt><dd>The secure
+ version of the HTTP protocol that uses <a href="#ssl">SSL</a> to ensure the
+ privacy of customer data (such as credit card information) while en route
+ over the <a href="#internet">Internet</a>.</dd>
+
+<dt id="imap">IMAP (Internet Message Access Protocol)</dt><dd>A standard mail
+ server protocol that allows you to store all your messages and any changes to
+ them on the server rather than on your computer&apos;s hard disk. Using IMAP
+ rather than <a href="#pop">POP</a> saves disk space and allows you to access
+ your entire mailbox, including sent mail, drafts, and custom folders, from
+ any location. Using an IMAP server over a modem is generally faster than
+ using a POP mail server, since you initially download message headers only.
+ Not all <a href="#isp">ISPs</a> support IMAP.</dd>
+
+<dt id="implicit_consent">implicit consent</dt><dd>Also known as implied or
+ <q>opt-out</q> consent. Used to describe privacy settings that may allow
+ websites to gather information about you (for example by means of
+ <a href="#cookie">cookies</a> and online forms) unless you explicitly choose
+ to withhold your consent by selecting an option on a page that the website
+ provides for that purpose. Your consent may not be requested when the
+ information is actually gathered. See also <a href="#user_tracking">user
+ tracking</a>.</dd>
+
+<dt id="internet">Internet</dt><dd>A worldwide network of millions of computers
+ that communicate with each other using standard protocols such as
+ <a href="#tcp_ip">TCP/IP</a>. Originally developed for the US military in
+ 1969, the Internet grew to include educational and research institutions and,
+ in the late 1990s, millions of businesses, organizations, and individuals.
+ Today the Internet is used for email, browsing the
+ <a href="#world_wide_web">World Wide Web (WWW)</a>, instant messaging,
+ usegroups, and many other purposes.</dd>
+
+<dt id="ip_address">IP address (Internet protocol address)</dt><dd>The address
+ of a computer on a <a href="#tcp_ip">TCP/IP</a> network. Every computer on
+ the <a href="#internet">Internet</a> has an IP address.
+ <a href="#client">Clients</a> have either a permanent IP address or one that
+ is dynamically assigned to them each time they connect with the network. IP
+ addresses are written as four sets of numbers, like this: 204.171.64.2.</dd>
+
+<dt id="irc">IRC (Internet Relay Chat)</dt><dd>A protocol used to chat with
+ other people in real-time using an IRC <a href="#client">client</a>.</dd>
+
+<dt id="isp">ISP (Internet Service Provider)</dt><dd>A company/institution
+ that provides <a href="#internet">Internet</a> connections.</dd>
+
+<dt id="java">Java</dt><dd>A programming language developed by Sun
+ Microsystems. A single Java program can run on many different kinds of
+ computers, thus avoiding the need for programmers to create a separate
+ version of each program for each kind of computer. Java programs or applets
+ are not directly supported in your browser.</dd>
+
+<dt id="javascript">JavaScript</dt><dd>A scripting language commonly used to
+ construct <a href="#web_page">web pages</a>. Programmers use JavaScript to
+ make web pages more interactive; for example, to display forms and buttons.
+ JavaScript must not be confused with <a href="#java">Java</a>. They are
+ separate languages. Java is not required for JavaScript to work
+ correctly.</dd>
+
+<dt id="key">key</dt><dd>A large number used by a
+ <a href="#cryptographic_algorithm">cryptographic algorithm</a> to encrypt or
+ decrypt data. A person&apos;s public key, for example, allows other people to
+ encrypt messages to that person. The encrypted messages must be decrypted
+ with the corresponding private key. See also
+ <a href="#public-key_cryptography">public-key cryptography</a>.</dd>
+
+<dt id="kerberos">Kerberos</dt><dd>A mechanism to use single-signon,
+ <a href="#smart_card">smart cards</a>, or other custom methods to
+ <a href="#authentication">authenticate</a> access without using
+ <a href="#password-based_authentication">passwords</a> for each individual
+ service. Used mostly in large enterprise/institutional networks where
+ authentication is provided by centralized services like
+ <a href="#ldap">LDAP</a>.</dd>
+
+<dt id="language_pack">language pack</dt><dd>A type of
+ <a href="#add-on">add-on</a> that adds a new language to the user interface
+ of &brandShortName;.</dd>
+
+<dt id="latex">LaTeX</dt><dd>A word processor and document markup language to
+ typeset documents, widely used in academia. In particular, it provides a
+ compact plain text syntax to write complex mathematical formulas.</dd>
+
+<dt id="ldap">LDAP (Lightweight Directory Access Protocol)</dt><dd>A standard
+ protocol for accessing directory services, such as corporate address books,
+ across multiple platforms. You can set up your browser to access LDAP
+ directories from the Address Book. You can also set up Mail &amp; Newsgroups
+ to use an LDAP directory for email address autocompletion.</dd>
+
+<dt id="location_aware_browsing">location aware browsing</dt><dd>A method to
+ determine a user&apos;s location to provide customized services for the
+ current position, or for the purpose of <a href="#user_tracking">user
+ tracking</a>. In addition to the <a href="#ip_address">IP address</a>,
+ provider-specific information like wireless access points are used to
+ determine longitude and latitude, altitude as well as speed and heading
+ (as available) for the requesting <a href="#website">website</a>.
+ Provided by a <a href="#geolocation_service">geolocation service</a>.</dd>
+
+<dt id="location_bar">Location Bar</dt><dd>The field (and associated buttons)
+ near the top of a browser window where you can type a
+ <a href="#url">URL</a> or search terms.</dd>
+
+<dt id="malware">Malware</dt><dd>Short for <q><u>Mal</u>icious
+ Soft<u>ware</u></q> and a general term for a variety of software designed
+ to disrupt computer operation, gather sensitive information, or gain access
+ to your computer. They can be distributed by infected <a href="#web_page">web
+ pages</a> or as attachments to email messages. Examples include viruses,
+ worms, trojans, spyware, or adware. Malware may redistribute itself by
+ sending out email messages to infect other computers.</dd>
+
+<dt id="master_key">master key</dt><dd>A symmetric key used by
+ <a href="#certificate_manager">Certificate Manager</a> to encrypt
+ information. For example, <a href="#password_manager">Password Manager</a>
+ uses Certificate Manager and your master key to encrypt email passwords,
+ website passwords, and other stored sensitive information. See also
+ <a href="#symmetric_encryption">symmetric encryption</a>.</dd>
+
+<dt id="master_password">master password</dt><dd>A password used by
+ Certificate Manager to protect the master key and/or private keys stored on a
+ <a href="#security_device">security device</a>. Certificate Manager needs to
+ access your private keys, for example, when you sign email messages or use
+ one of your own certificates to identify yourself to a website. It needs to
+ access your master key when Password Manager or Form Manager reads or adds to
+ your personal information. You can set or change your master password from
+ the Passwords preferences panel. Each security device requires a separate
+ master password. See also <a href="#private_key">private key</a>,
+ <a href="#master_key">master key</a>.</dd>
+
+<dt id="mathml">MathML (Mathematical Markup Language)</dt><dd>The markup
+ language used to write mathematical notations in
+ <a href="#web_page">web pages</a>.</dd>
+
+<dt id="menu_bar">Menu Bar</dt><dd>The toolbar <span class="mac">at the top
+ of the screen</span><span class="noMac">near the top of any &brandShortName;
+ window</span> that includes the File, Edit, and View menus.</dd>
+
+<dt id="misrepresentation">misrepresentation</dt><dd>Presentation of an entity
+ as a person or organization that it is not. For example, a website might
+ pretend to be a furniture store when it is really just a website that takes
+ credit card payments but never sends any goods. See also
+ <a href="#spoofing">spoofing</a>.</dd>
+
+<dt id="navigation_toolbar">Navigation Toolbar</dt><dd>The toolbar near the top
+ of the browser window that includes the Back and Forward buttons.</dd>
+
+<dt id="nonrepudiation">nonrepudiation</dt><dd>The inability, of the sender of
+ a message, to deny having sent the message. A regular hand-written signature
+ provides one form of nonrepudiation. A
+ <a href="#digital_signature">digital signature</a> provides another.</dd>
+
+<dt id="notification_bar">notification bar</dt><dd>A bar that appears at the
+ top of the content area to inform you about something that needs your
+ attention, e.g. when the Password Manager can save a password for you, a
+ popup has been blocked or an additional plugin is required.</dd>
+
+<dt id="ntlm">NTLM (NT LAN Manager)</dt><dd>A protocol for
+ <a href="#authentication">authentication</a> in local networks that is
+ proprietary to Microsoft Windows. Used mostly in enterprise/institutional
+ networks.</dd>
+
+<dt id="object_signing">object signing</dt><dd>A technology that allows
+ software developers to sign Java code, JavaScript scripts, or any kind of
+ file, and that allows users to identify the signers and control access by
+ signed code to local system resources.</dd>
+
+<dt id="object-signing_certificate">object-signing certificate</dt><dd>A
+ certificate whose corresponding private key is used to sign objects such as
+ code files. See also <a href="#object_signing">object signing</a>.</dd>
+
+<dt id="ocsp">OCSP (Online Certificate Status Protocol)</dt><dd>A set of rules
+ that <a href="#certificate_manager">Certificate Manager</a> follows to
+ perform an online check of a certificate&apos;s validity each time the
+ certificate is used. This process involves checking the certificate against a
+ list of valid certificates maintained at a specified website. Your computer
+ must be online for OCSP to work.</dd>
+
+<dt id="opml">OPML (Outline Processor Markup Language)</dt><dd>An XML format
+ used to list <a href="#feed">feed</a> collections. Although broader in its
+ specification, it is mainly used nowadays to export and import feed
+ collections between different feed aggregators or readers, like
+ &brandShortName;.</dd>
+
+<dt id="password-based_authentication">password-based
+ authentication</dt><dd>Confident identification by means of a name and
+ password. See also <a href="#authentication">authentication</a>.</dd>
+
+<dt id="password_manager">Password Manager</dt><dd>The part of the
+ browser that can help you remember some or all of your names and passwords by
+ storing them on your computer&apos;s hard disk, and entering them for you
+ automatically when you visit such websites.</dd>
+
+<dt id="personal_toolbar">Bookmarks Toolbar</dt><dd>The customizable toolbar
+ that appears just below the location bar by default in the browser. It
+ contains standard buttons such as Home, Bookmarks, and so on that you can add
+ or remove. You can also add buttons for your favorite bookmarks, or folders
+ containing groups of bookmarks.</dd>
+
+<dt id="phishing">Phishing</dt><dd>Phishing is a fraudulent business scheme in
+ which a party creates counterfeit websites, hijacking brand names of banks,
+ e-retailers and credit card companies, trying to collect victims&apos;
+ personal information.</dd>
+
+<dt id="pkcs_11">PKCS #11</dt><dd>The public-key cryptography standard that
+ governs security devices such as smart cards. See also
+ <a href="#security_device">security device</a>, <a href="#smart_card">smart
+ card</a>.</dd>
+
+<dt id="pkcs_11_module">PKCS #11 module</dt><dd>A program on your computer
+ that manages cryptographic services such as encryption and decryption using
+ the PKCS #11 standard. Also called <em>cryptographic modules</em>,
+ <em>cryptographic service providers</em>, or <em>security modules</em>,
+ PKCS #11 modules control either hardware or software devices. A PKCS #11
+ module always controls one or more slots, which may be implemented as some
+ form of physical reader (for example, for reading smart cards) or in
+ software. Each slot for a PKCS #11 module can in turn contain a
+ <a href="#security_device">security device</a> (also called <em>token</em>),
+ which is the hardware or software device that provides cryptographic services
+ and stores certificates and keys. <a href="#certificate_manager">Certificate
+ Manager</a> provides two built-in PKCS #11 modules. You may install
+ additional modules on your computer to control smart card readers or other
+ hardware devices.</dd>
+
+<dt id="pki">PKI (public-key infrastructure)</dt><dd>The standards and services
+ that facilitate the use of public-key cryptography and certificates in a
+ networked environment.</dd>
+
+<dt id="plugin">plugin</dt><dd>A type of
+ <a href="#helper_application">helper application</a> that adds new
+ capabilities to your browser, such as the ability to play audio or video
+ clips. Unlike other kinds of helper applications, a plugin application
+ installs itself into the Plugins directory within the main browser
+ installation directory and typically can be opened within the browser itself
+ (internally). For example, an audio plugin lets you listen to audio files on
+ a <a href="#web_page">web page</a> or in an email message.</dd>
+
+<dt id="pop">POP (Post Office Protocol)</dt><dd>A standard mail server protocol
+ that requires you to download new messages to your local
+ computer&mdash;although you can choose to leave copies on the server. With
+ POP, you can store all your messages, including sent mail, drafts, and custom
+ folders, on one computer only. By contrast,
+ <a href="#imap">IMAP</a> allows you to permanently store all your messages
+ and any changes to them on the server, where you can access them from any
+ computer. Most <a href="#isp">ISPs</a> currently support POP.</dd>
+
+<dt id="private_browsing">private browsing</dt><dd>Browsing in a session in
+ which no private data (like browsing history, <a href="#cookie">cookies</a>,
+ and <a href="#cache">cached</a> content) are retained beyond the duration of
+ the private session. Private browsing should not be confused with anonymous
+ browsing and does not prevent <a href="#user_tracking">user tracking</a> or
+ monitoring of web activity by an internet provider or employer.</dd>
+
+<dt id="private_key">private key</dt><dd>One of a pair of
+ <a href="#key">keys</a> used in public-key cryptography. The private key is
+ kept secret and is used to decrypt data that has been encrypted with the
+ corresponding public key.</dd>
+
+<dt id="proxy">proxy</dt><dd>An intermediary or <q>go-between</q> program that
+ acts as both a <a href="#server">server</a> and a
+ <a href="#client">client</a> for the purpose of making requests on behalf of
+ other clients.</dd>
+
+<dt id="public_key">public key</dt><dd>
+ One of a pair of <a href="#key">keys</a> used in public-key cryptography.
+ The public key is distributed freely and published as part of a
+ <a href="#certificate">certificate</a>. It is typically used to encrypt data
+ sent to the public key&apos;s owner, who then decrypts the data with the
+ corresponding private key.</dd>
+
+<dt id="public-key_cryptography">public-key cryptography</dt><dd>A set of
+ well-established techniques and standards that allow an entity (such as a
+ person, an organization, or hardware such as a router) to verify its identity
+ electronically or to sign and encrypt electronic data. Two keys are involved:
+ a <a href="#public_key">public key</a> and a <a href="#private_key">private
+ key</a>. The public key is published as part of a
+ <a href="#certificate">certificate</a>, which associates that key with a
+ particular identity. The corresponding private key is kept secret. Data
+ encrypted with the public key can be decrypted only with the private key.
+ </dd>
+
+<dt id="root_ca">root CA</dt><dd>The
+ <a href="#certificate_authority">certificate authority (CA)</a> with a
+ self-signed certificate at the top of a
+ <a href="#certificate_chain">certificate chain</a>. See also
+ <a href="#subordinate_ca">subordinate CA</a>.</dd>
+
+<dt id="rss">RSS (Really Simple Syndication)</dt><dd>An <a href="#xml">XML</a>
+ data format for web <a href="#feed">feeds</a>.</dd>
+
+<dt id="safe_browsing">safe browsing</dt><dd>Protection against common threats
+ from <a href="#malware">Malware</a> and <a href="#phishing">Phishing</a> by
+ checking each <a href="#web_page">web page</a> against a list of reported
+ websites. If the web page you are about to visit has been reported as
+ containing malicious content, &brandShortName; prevents it from loading
+ and shows a warning instead.</dd>
+
+<dt id="search_engine">search engine</dt><dd>A web-based program that allows
+ users to search for and retrieve specific information from the
+ <a href="#world_wide_web">World Wide Web (WWW)</a>. The search engine may
+ search the full text of web documents or a list of keywords, or use
+ librarians who review web documents and index them manually for retrieval.
+ Typically, the user types a word or phrase, also called a query, into a
+ search box, and the search engine displays links to relevant web pages.</dd>
+
+<dt id="secure_authentication">secure authentication</dt><dd>A type of
+ <a href="#authentication">authentication</a> which can be achieved by
+ <a href="#encrypted_password">encryption of the password</a> or by mechanisms
+ like <a href="#kerberos">Kerberos</a> and <a href="#ntlm">NTLM</a>. Not to
+ be confused with <a href="#secure_connection">secure connection</a>.</dd>
+
+<dt id="secure_connection">secure connection</dt><dd>A connection using
+ <a href="#ssl">SSL</a> or <a href="#tls">TLS</a>. All communication between
+ your computer and the server is <a href="#encryption">encrypted</a> so that
+ no third party eavesdropping on your connection can read it. Note that the
+ data is only encrypted during transmission between your client application
+ and the server, after that it is no longer encrypted. To prove its
+ authenticity to the client, the server needs to identify itself using a
+ <a href="#certificate">certificate</a>. A bad certificate can indicate
+ an attack on the server or the connection, thus it is important to heed
+ certificate warnings.</dd>
+
+<dt id="security_certificate">security certificate</dt><dd>See
+ <a href="#certificate">certificate</a>.</dd>
+
+<dt id="security_device">security device</dt><dd>Hardware or software that
+ provides cryptographic services such as encryption and decryption and can
+ store certificates and keys. A <a href="#smart_card">smart card</a> is one
+ example of a security device implemented in hardware.
+ <a href="#certificate_manager">Certificate Manager</a> contains its own
+ built-in security device, called the
+ <a href="#software_security_device">software security device</a>, that is
+ always available while the browser is running. Each security device is
+ protected by its own <a href="#master_password">master password</a>.</dd>
+
+<dt id="security_module">security module</dt><dd>See
+ <a href="#pkcs_11_module">PKCS #11 module</a>.</dd>
+
+<dt id="security_token">security token</dt><dd>See
+ <a href="#security_device">security device</a>.</dd>
+
+<dt id="server">server</dt><dd>Software (such as software that serves up web
+ pages) that receives requests from and sends information to a
+ <a href="#client">client</a>, which is usually running on a different
+ computer. A computer on which server software runs is also described as a
+ server.</dd>
+
+<dt id="server_authentication">server authentication</dt><dd>The process of
+ identifying a <a href="#server">server</a> to a <a href="#client">client</a>
+ by using a <a href="#server_ssl_certificate">server SSL certificate</a>. See
+ also <a href="#client_authentication">client authentication</a>,
+ <a href="#ssl">SSL (Secure Sockets Layer)</a>.</dd>
+
+<dt id="server_ssl_certificate">server SSL certificate</dt><dd>A
+ certificate that a <a href="#server">server</a> presents to a
+ <a href="#client">client</a> to authenticate the server&apos;s identity using
+ the <a href="#ssl">SSL (Secure Sockets Layer)</a> protocol.</dd>
+
+<dt id="signing_certificate">signing certificate</dt><dd>A certificate whose
+ corresponding <a href="#private_key">private key</a> is used to sign
+ transmitted data, so that the receiver can verify the identity of the sender.
+ Certificate authorities (CAs) often issue a signing certificate that will be
+ used to sign email messages at the same time as an
+ <a href="#encryption_certificate">encryption certificate</a> that will be
+ used to encrypt email messages. See also <a href="#dual_key_pairs">dual key
+ pairs</a>, <a href="#digital_signature">digital signature</a>.</dd>
+
+<dt id="signing_key">signing key</dt><dd>A private key used for signing only.
+ A signing key and its equivalent public key, together with an
+ <a href="#encryption_key">encryption key</a> and its equivalent private key,
+ constitute <a href="#dual_key_pairs">dual key pairs</a>.</dd>
+
+<dt id="slot">slot</dt><dd>A piece of hardware, or its equivalent in software,
+ that is controlled by a <a href="#pkcs_11_module">PKCS #11 module</a> and
+ designed to contain a <a href="#security_device">security device</a>.</dd>
+
+<dt id="smart_card">smart card</dt><dd>A small device, typically about the size
+ of a credit card, that contains a microprocessor and is capable of storing
+ cryptographic information (such as keys and certificates) and performing
+ cryptographic operations. Smart cards use the <a href="#pkcs_11">PKCS #11</a>
+ standard. A smart card is one kind of <a href="#security_device">security
+ device</a>.</dd>
+
+<dt id="smtp">SMTP (Simple Mail Transfer Protocol)</dt><dd>A protocol that
+ sends email messages across the <a href="#internet">Internet</a>.</dd>
+
+<dt id="socks">SOCKS</dt><dd>A protocol that a <a href="#proxy">proxy</a>
+ server can use to accept requests from client users in an internal network
+ so that it can forward them across the <a href="#internet">Internet</a>.</dd>
+
+<dt id="software_security_device">software security device</dt><dd>The default
+ <a href="#security_device">security device</a> used by
+ <a href="#certificate_manager">Certificate Manager</a> to store private keys
+ associated with your certificates. In addition to private keys, the software
+ security device stores the master key used by
+ <a href="#password_manager">Password Manager</a> to encrypt email passwords,
+ website passwords, and other sensitive information. See also
+ <a href="#private_key">private key</a> and <a href="#master_password">master
+ key</a>.</dd>
+
+<dt id="spoofing">spoofing</dt><dd>Pretending to be someone else. For example,
+ a person can pretend to have the email address <tt>jdoe@mozilla.com</tt>, or
+ a computer can identify itself as a website called <tt>www.mozilla.com</tt>
+ when it is not. Spoofing is one form of
+ <a href="#misrepresentation">misrepresentation</a>.</dd>
+
+<dt id="ssl">SSL (Secure Sockets Layer)</dt><dd>A protocol that allows mutual
+ authentication between a <a href="#client">client</a> and a
+ <a href="#server">server</a> for the purpose of establishing an authenticated
+ and encrypted connection. SSL runs above <a href="#tcp_ip">TCP/IP</a> and
+ below <a href="#http">HTTP</a>, <a href="#ldap">LDAP</a>,
+ <a href="#imap">IMAP</a>, NNTP, and other high-level network protocols.
+ The new Internet Engineering Task Force (IETF) standard called Transport
+ Layer Security (TLS) is based on SSL. See also
+ <a href="#authentication">authentication</a>,
+ <a href="#encryption">encryption</a>.</dd>
+
+<dt id="starttls">STARTTLS</dt><dd>An extension to common standard TCP
+ protocols (like SMTP, POP or IMAP) so the client can tell the server to
+ use <a href="#tls">TLS</a> on the same TCP port as for non-secure
+ connections.</dd>
+
+<dt id="status_bar">Status Bar</dt><dd>The toolbar that appears at the bottom
+ of any &brandShortName; window. It includes the
+ <a href="#component_bar">Component Bar</a> on the left and status icons on
+ the right.</dd>
+
+<dt id="subject">subject</dt><dd>The entity (such as a person, organization,
+ or router) identified by a <a href="#certificate">certificate</a>. In
+ particular, the subject field of a certificate contains the certified
+ entity&apos;s <a href="#subject_name">subject name</a> and other
+ characteristics.</dd>
+
+<dt id="subject_name">subject name</dt><dd>A
+ <a href="#distinguished_name">distinguished name (DN)</a> that uniquely
+ describes the <a href="#subject">subject</a> of a
+ <a href="#certificate">certificate</a>.</dd>
+
+<dt id="subordinate_ca">subordinate CA</dt><dd>A
+ <a href="#certificate_authority">certificate authority (CA)</a> whose
+ certificate is signed by another subordinate CA or by the root CA. See also
+ <a href="#certificate_chain">certificate chain</a>, <a href="#root_ca">root
+ CA</a>.</dd>
+
+<dt id="symmetric_encryption">symmetric encryption</dt><dd>An encryption method
+ that uses a single cryptographic key to both encrypt and decrypt a given
+ message.</dd>
+
+<dt id="tamper_detection">tamper detection</dt><dd>A mechanism ensuring that
+ data received in electronic form has not been tampered with; that is, that
+ the data received corresponds entirely with the original version of the same
+ data.</dd>
+
+<dt id="tcp">TCP</dt><dd>See <a href="#tcp_ip">TCP/IP</a>.</dd>
+
+<dt id="tcp_ip">TCP/IP (Transmission Control Protocol/Internet
+ Protocol)</dt><dd>A Unix protocol used to connect computers running a variety
+ of operating systems. TCP/IP is an essential Internet protocol and has become
+ a global standard.</dd>
+
+<dt id="theme">theme</dt><dd>A type of <a href="#add-on">add-on</a> that changes
+ the appearance of &brandShortName;.</dd>
+
+<dt id="third-party_cookie">third-party cookie</dt><dd>A
+ <a href="#cookie">cookie</a> from one <a href="#website">website</a> that
+ gets stored on your computer when you visit a different website. Sometimes a
+ website displays content that is hosted on another website. That content can
+ be anything from an image to text or an advertisement. The second website
+ that hosts such elements also has the ability to store a cookie in your
+ browser, even though you don&apos;t visit it directly. Also known as
+ <q>foreign cookie</q>.</dd>
+
+<dt id="tls">TLS</dt><dd>Transport Layer Security (TLS) is the new Internet
+ Engineering Task Force (IETF) standard based on SSL (Secure Sockets Layer).
+ See also <a href="#ssl">SSL</a> and
+ <a href="#encryption">encryption</a>.</dd>
+
+<dt id="token">token</dt><dd>See <a href="#security_device">security
+ device</a>.</dd>
+
+<dt id="tooltip">tooltip</dt><dd>A small box with text that appears when
+ you hover your mouse's cursor over certain items. It usually contains
+ information regarding the item being hovered over.</dd>
+
+<dt id="trust">trust</dt><dd>Confident reliance on a person or other entity. In
+ the context of <a href="#pki">PKI (public-key infrastructure)</a>, trust
+ usually refers to the relationship between the user of a certificate and the
+ <a href="#certificate_authority">certificate authority (CA)</a> that issued
+ the certificate. If you use Certificate Manager to specify that you trust a
+ CA, Certificate Manager trusts valid certificates issued by that CA unless
+ you specify otherwise in the settings for individual certificates. You use
+ the Authorities tab in Certificate Manager to specify the kinds of
+ certificates you do or don&apos;t trust specific CAs to issue.</dd>
+
+<dt id="url">URL (Uniform Resource Locator)</dt><dd>The standardized address
+ that tells your browser how to locate a file or other resource on the Web.
+ For example: <tt>http://www.mozilla.org.</tt> You can type URLs into the
+ browser&apos;s <a href="#location_bar">Location Bar</a> to access
+ <a href="#web_page">web pages</a>. URLs are also used in the links on web
+ pages that you can click to go to other web pages. Also known as an Internet
+ address or Web address.</dd>
+
+<dt id="user_tracking">user tracking</dt><dd>Methods that some
+ <a href="#website">websites</a>, including advertisers and analytics services,
+ employ to determine patterns in how you browse the web (e.g., what websites
+ you have visited, which preferences you have voiced using buttons embedded by
+ social networks, and your purchase history). This information is mostly used
+ to show you specifically targeted offers or advertisements for products or
+ services. Mechanisms for user tracking include <a href="#cookie">cookies</a>
+ and <a href="#fingerprinting">browser fingerprinting</a>. See also
+ <a href="#do_not_track">Do Not Track</a>.</dd>
+
+<dt id="web_application">web application</dt><dd>An application that is not
+ running on your computer but remotely on a <a href="#website">website</a>.
+ Examples include web-mail systems or web-based systems where you enter
+ information into a form and receive a response as a <a href="#web_page">web
+ page</a>. An <i>offline</i> web application can work without a current
+ connection to the <a href="#internet">Internet</a> by saving the relevant
+ pages locally before running the application.</dd>
+
+<dt id="web_page">web page</dt><dd>A single document on the World Wide Web that
+ is specified by a unique address or <a href="#url">URL</a> and that may
+ contain text, hyperlinks, and graphics.</dd>
+
+<dt id="website">website</dt><dd>A group of related web pages linked by
+ hyperlinks and managed by a single company, organization, or individual. A
+ website may include text, graphics, audio and video files, and links to
+ other websites.</dd>
+
+<dt id="world_wide_web">World Wide Web (WWW)</dt><dd>Also known as the Web. A
+ portion of the <a href="#internet">Internet</a> that is made up of web pages
+ stored by web <a href="#server">servers</a> and displayed by
+ <a href="#client">clients</a> called web browsers (such as
+ &brandShortName;).</dd>
+
+<dt id="wpad">WPAD (Web Proxy AutoDiscovery)</dt><dd>A proposed Internet
+ protocol that allows a Web browser to automatically locate and interface
+ with <a href="#proxy">proxy</a> services in a network.</dd>
+
+<dt id="xml">XML (Extensible Markup Language)</dt><dd>An open standard for
+ describing data. Unlike <a href="#html">HTML</a>, XML allows the developer of
+ a web page to define special tags. For more information, see the online W3C
+ document
+ <a href="http://www.w3.org/XML/">Extensible Markup Language (XML)</a>.</dd>
+
+<dt id="xslt">XSLT (Extensible Stylesheet Language Transformation)</dt><dd>A
+ language used to convert an XML document into another XML document or into
+ some other format.</dd>
+
+<dt id="xul">XUL (XML User Interface Language)</dt><dd>An XML markup language
+ for creating user interfaces in applications.</dd>
+
+</dl>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/help-glossary.rdf b/comm/suite/locales/en-US/chrome/common/help/help-glossary.rdf
new file mode 100755
index 0000000000..7809bc6f5a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/help-glossary.rdf
@@ -0,0 +1,161 @@
+<?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/. -->
+
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+
+<!-- HELP Glossary SECTION -->
+ <rdf:Description about="urn:root">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description nc:name="add-on" nc:link="glossary.xhtml#add-on"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="authentication" nc:link="glossary.xhtml#authentication"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="bookmark" nc:link="glossary.xhtml#bookmark"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="CA" nc:link="glossary.xhtml#ca"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="CA certificate" nc:link="glossary.xhtml#ca_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="cache" nc:link="glossary.xhtml#cache"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="caret browsing" nc:link="glossary.xhtml#caret_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate" nc:link="glossary.xhtml#certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate authority (CA)" nc:link="glossary.xhtml#certificate_authority"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate backup password" nc:link="glossary.xhtml#certificate_backup_password"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate-based authentication" nc:link="glossary.xhtml#certificate-based_authentication"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate chain" nc:link="glossary.xhtml#certificate_chain"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate fingerprint" nc:link="glossary.xhtml#certificate_fingerprint"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Certificate Manager" nc:link="glossary.xhtml#certificate_manager"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate renewal" nc:link="glossary.xhtml#certificate_renewal"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="certificate verification" nc:link="glossary.xhtml#certificate_verification"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="cipher" nc:link="glossary.xhtml#cipher"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="client" nc:link="glossary.xhtml#client"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="client authentication" nc:link="glossary.xhtml#client_authentication"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="client SSL certificate" nc:link="glossary.xhtml#client_ssl_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Component Bar" nc:link="glossary.xhtml#component_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="cookie" nc:link="glossary.xhtml#cookie"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Cookie Manager" nc:link="glossary.xhtml#cookie_manager"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="CRAM-MD5" nc:link="glossary.xhtml#cram_md5"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="CRL" nc:link="glossary.xhtml#crl"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="cryptographic algorithm" nc:link="glossary.xhtml#cryptographic_algorithm"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="cryptography" nc:link="glossary.xhtml#cryptography"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="decryption" nc:link="glossary.xhtml#decryption"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="digital ID" nc:link="glossary.xhtml#digital_id"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="digital signature" nc:link="glossary.xhtml#digital_signature"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="distinguished name (DN)" nc:link="glossary.xhtml#distinguished_name"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Do Not Track" nc:link="glossary.xhtml#do_not_track"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="dual key pairs" nc:link="glossary.xhtml#dual_key_pairs"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="eavesdropping" nc:link="glossary.xhtml#eavesdropping"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="encrypted password" nc:link="glossary.xhtml#encrypted_password"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="encryption" nc:link="glossary.xhtml#encryption"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="encryption certificate" nc:link="glossary.xhtml#encryption_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="encryption key" nc:link="glossary.xhtml#encryption_key"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="extension" nc:link="glossary.xhtml#extension"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="feed" nc:link="glossary.xhtml#feed"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="fingerprint (certificate)" nc:link="glossary.xhtml#fingerprint"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="fingerprinting (browser)" nc:link="glossary.xhtml#fingerprinting"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="FIPS PUBS 140-1" nc:link="glossary.xhtml#fips_pubs_140-1"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="foreign cookie" nc:link="glossary.xhtml#foreign_cookie"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="frame" nc:link="glossary.xhtml#frame"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="FTP" nc:link="glossary.xhtml#ftp"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="geolocation service" nc:link="glossary.xhtml#geolocation_service"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="grippy" nc:link="glossary.xhtml#grippy"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="GSSAPI" nc:link="glossary.xhtml#gssapi"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="helper application" nc:link="glossary.xhtml#helper_application"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="home page" nc:link="glossary.xhtml#home_page"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="HTML" nc:link="glossary.xhtml#html"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="HTTP" nc:link="glossary.xhtml#http"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="HTTPS" nc:link="glossary.xhtml#https"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="IMAP" nc:link="glossary.xhtml#imap"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="implicit consent" nc:link="glossary.xhtml#implicit_consent"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Internet" nc:link="glossary.xhtml#internet"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="IP address" nc:link="glossary.xhtml#ip_address"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="IRC" nc:link="glossary.xhtml#irc"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="ISP" nc:link="glossary.xhtml#isp"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Java" nc:link="glossary.xhtml#java"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="JavaScript" nc:link="glossary.xhtml#javascript"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Kerberos" nc:link="glossary.xhtml#kerberos"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="key" nc:link="glossary.xhtml#key"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="LaTeX" nc:link="glossary.xhtml#latex"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="LDAP" nc:link="glossary.xhtml#ldap"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="language pack" nc:link="glossary.xhtml#language_pack"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="location aware browsing" nc:link="glossary.xhtml#location_aware_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Location Bar" nc:link="glossary.xhtml#location_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Malware" nc:link="glossary.xhtml#malware"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="master key" nc:link="glossary.xhtml#master_key"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="master password" nc:link="glossary.xhtml#master_password"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="MathML" nc:link="glossary.xhtml#mathml"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="misrepresentation" nc:link="glossary.xhtml#misrepresentation"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Navigation Toolbar" nc:link="glossary.xhtml#navigation_toolbar"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="nonrepudiation" nc:link="glossary.xhtml#nonrepudiation"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="notification bar" nc:link="glossary.xhtml#notification_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="NTLM" nc:link="glossary.xhtml#ntlm"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="object signing" nc:link="glossary.xhtml#object_signing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="object-signing certificate" nc:link="glossary.xhtml#object-signing_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="OCSP" nc:link="glossary.xhtml#ocsp"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="OPML" nc:link="glossary.xhtml#opml"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="password-based authentication" nc:link="glossary.xhtml#password-based_authentication"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Password Manager" nc:link="glossary.xhtml#password_manager"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Bookmarks Toolbar" nc:link="glossary.xhtml#personal_toolbar"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="phishing" nc:link="glossary.xhtml#phishing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="PKCS #11" nc:link="glossary.xhtml#pkcs_11"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="PKCS #11 module" nc:link="glossary.xhtml#pkcs_11_module"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="PKI" nc:link="glossary.xhtml#pki"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="plugin" nc:link="glossary.xhtml#plugin"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="POP" nc:link="glossary.xhtml#pop"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="private browsing" nc:link="glossary.xhtml#private_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="private key" nc:link="glossary.xhtml#private_key"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="proxy" nc:link="glossary.xhtml#proxy"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="public key" nc:link="glossary.xhtml#public_key"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="public-key cryptography" nc:link="glossary.xhtml#public-key_cryptography"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="root CA" nc:link="glossary.xhtml#root_ca"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="RSS" nc:link="glossary.xhtml#rss"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="safe browsing" nc:link="glossary.xhtml#safe_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="search engine" nc:link="glossary.xhtml#search_engine"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="secure authentication" nc:link="glossary.xhtml#secure_authentication"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="secure connection" nc:link="glossary.xhtml#secure_connection"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="security certificate" nc:link="glossary.xhtml#security_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="security device" nc:link="glossary.xhtml#security_device"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="security module" nc:link="glossary.xhtml#security_module"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="security token" nc:link="glossary.xhtml#security_token"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="server" nc:link="glossary.xhtml#server"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="server authentication" nc:link="glossary.xhtml#server_authentication"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="server SSL certificate" nc:link="glossary.xhtml#server_ssl_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="signing certificate" nc:link="glossary.xhtml#signing_certificate"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="signing key" nc:link="glossary.xhtml#signing_key"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="slot" nc:link="glossary.xhtml#slot"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="smart card" nc:link="glossary.xhtml#smart_card"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="SMTP" nc:link="glossary.xhtml#smtp"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="SOCKS" nc:link="glossary.xhtml#socks"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="software security device" nc:link="glossary.xhtml#software_security_device"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="spoofing" nc:link="glossary.xhtml#spoofing"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="SSL" nc:link="glossary.xhtml#ssl"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="STARTTLS" nc:link="glossary.xhtml#starttls"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="Status Bar" nc:link="glossary.xhtml#status_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="subject" nc:link="glossary.xhtml#subject"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="subject name" nc:link="glossary.xhtml#subject_name"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="subordinate CA" nc:link="glossary.xhtml#subordinate_ca"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="symmetric encryption" nc:link="glossary.xhtml#symmetric_encryption"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="tamper detection" nc:link="glossary.xhtml#tamper_detection"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="TCP" nc:link="glossary.xhtml#tcp"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="theme" nc:link="glossary.xhtml#theme"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="TCP/IP" nc:link="glossary.xhtml#tcp_ip"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="third-party cookie" nc:link="glossary.xhtml#third-party_cookie"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="TLS" nc:link="glossary.xhtml#tls"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="token" nc:link="glossary.xhtml#token"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="tooltip" nc:link="glossary.xhtml#tooltip"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="trust" nc:link="glossary.xhtml#trust"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="URL" nc:link="glossary.xhtml#url"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="user tracking" nc:link="glossary.xhtml#user_tracking"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="web application" nc:link="glossary.xhtml#web_application"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="web page" nc:link="glossary.xhtml#web_page"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="website" nc:link="glossary.xhtml#website"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="World Wide Web (WWW)" nc:link="glossary.xhtml#world_wide_web"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="WPAD" nc:link="glossary.xhtml#wpad"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="XML" nc:link="glossary.xhtml#xml"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="XSLT" nc:link="glossary.xhtml#xslt"/> </rdf:li>
+ <rdf:li> <rdf:Description nc:name="XUL" nc:link="glossary.xhtml#xul"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+</rdf:RDF>
diff --git a/comm/suite/locales/en-US/chrome/common/help/help-index1.rdf b/comm/suite/locales/en-US/chrome/common/help/help-index1.rdf
new file mode 100644
index 0000000000..897b8ebb26
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/help-index1.rdf
@@ -0,0 +1,2247 @@
+<?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/. -->
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+
+<rdf:Description about="help-indexAZ.rdf#a">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Account_Settings"
+ nc:name="Account Settings"
+ nc:link="mailnews_getting_started.xhtml#changing_the_settings_for_an_account"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="adding"
+ nc:name="adding"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons"
+ nc:name="add-ons"
+ nc:link="customize_help.xhtml#add-ons"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books"
+ nc:name="address books"
+ nc:link="mailnews_addressbooks.xhtml#about_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="appearance_panel"
+ nc:name="appearance panel"
+ nc:link="customize_help.xhtml#the_appearance_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="attaching_files_to_messages"
+ nc:name="attaching files to messages"
+ nc:link="mailnews_using_mail.xhtml#attaching_a_file_or_web_page"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#adding">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="adding:images_to_web_pages"
+ nc:name="inserting images to web pages"
+ nc:link="composer_help.xhtml#inserting_an_image_into_your_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="adding:blog_account"
+ nc:name="creating a new blog account"
+ nc:link="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="adding:mail_account"
+ nc:name="creating a new mail account"
+ nc:link="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="adding:newsgroup_account"
+ nc:name="adding a new newsgroup account"
+ nc:link="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="adding:table_elements"
+ nc:name="inserting table elements"
+ nc:link="composer_help.xhtml#adding_and_deleting_rows_columns_and_cells"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#address_books">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="address_books:adding_entries"
+ nc:name="adding address book entries"
+ nc:link="mailnews_addressbooks.xhtml#adding_entries_to_your_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books:creating"
+ nc:name="creating a new address book"
+ nc:link="mailnews_addressbooks.xhtml#creating_a_new_address_book"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books:creating_mailing_lists"
+ nc:name="creating mailing lists"
+ nc:link="mailnews_addressbooks.xhtml#creating_a_mailing_list"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books:exporting"
+ nc:name="exporting address books"
+ nc:link="mailnews_addressbooks.xhtml#exporting_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books:importing"
+ nc:name="importing address books"
+ nc:link="mailnews_addressbooks.xhtml#importing_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books:LDAP_directories"
+ nc:name="LDAP directories"
+ nc:link="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="address_books:searching"
+ nc:name="searching your address book"
+ nc:link="mailnews_addressbooks.xhtml#searching_address_books_and_directories"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#add-ons">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="add-ons:about"
+ nc:name="about"
+ nc:link="customize_help.xhtml#about_add-ons"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:installing"
+ nc:name="installing"
+ nc:link="customize_help.xhtml#installing_add-ons"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:manager"
+ nc:name="Add-ons Manager"
+ nc:link="customize_help.xhtml#using_the_add-ons_manager"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:converter"
+ nc:name="Add-on Converter"
+ nc:link="customize_help.xhtml#using_the_add-on_converter"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#add-ons:manager">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="add-ons:searching_for_add-ons"
+ nc:name="Searching for Add-ons"
+ nc:link="customize_help.xhtml#searching_for_add-ons"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:get_add-ons"
+ nc:name="Get Add-ons Panel"
+ nc:link="customize_help.xhtml#the_get_add-ons_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:extensions_panel"
+ nc:name="Extensions Panel"
+ nc:link="customize_help.xhtml#the_extensions_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:appearance_panel"
+ nc:name="Appearance Panel"
+ nc:link="customize_help.xhtml#the_appearance_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:plugins_panel"
+ nc:name="Plugins Panel"
+ nc:link="customize_help.xhtml#the_plugins_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:languages_panel"
+ nc:name="Languages Panel"
+ nc:link="customize_help.xhtml#the_languages_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="add-ons:updating_add-ons"
+ nc:name="Updating Add-ons"
+ nc:link="customize_help.xhtml#updating_add-ons"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#b">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="backing_up_certificates"
+ nc:name="backing up certificates"
+ nc:link="certs_help.xhtml#your_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks"
+ nc:name="bookmarks"
+ nc:link="nav_help.xhtml#visiting_bookmarked_pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser"
+ nc:name="browser"
+ nc:link="nav_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browsing_anonymously"
+ nc:name="browsing anonymously"
+ nc:link="privacy_help.xhtml#how_can_i_make_sure_unauthorized_people_dont_use_information_about_me"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#bookmarks">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="bookmarks:changing_bookmarks"
+ nc:name="changing bookmarks"
+ nc:link="customize_help.xhtml#changing_individual_bookmarks"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:creating_bookmarks"
+ nc:name="creating bookmarks"
+ nc:link="customize_help.xhtml#creating_new_bookmarks"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:exporting_bookmarks"
+ nc:name="exporting bookmarks"
+ nc:link="customize_help.xhtml#exporting_or_importing_a_bookmark_list"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:organizing_bookmarks"
+ nc:name="organizing bookmarks"
+ nc:link="customize_help.xhtml#organizing_your_bookmarks"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:searching_bookmarks"
+ nc:name="searching bookmarks"
+ nc:link="customize_help.xhtml#searching_your_bookmarks"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:using_bookmarks"
+ nc:name="using bookmarks"
+ nc:link="customize_help.xhtml#using_bookmarks"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:visiting"
+ nc:name="visiting bookmarked pages"
+ nc:link="nav_help.xhtml#visiting_bookmarked_pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="bookmarks:what_are_bookmarks"
+ nc:name="what are bookmarks"
+ nc:link="customize_help.xhtml#what_are_bookmarks"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#browser">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="browser:changing_your_home_page"
+ nc:name="changing your home page"
+ nc:link="customize_help.xhtml#changing_your_home_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:controlling_popups"
+ nc:name="controlling popups"
+ nc:link="cs_priv_prefs_popup.xhtml#controlling_popups"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:Download_Manager"
+ nc:name="Download Manager"
+ nc:link="nav_help.xhtml#plugins_and_downloads"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:full_screen_mode"
+ nc:name="Full screen mode"
+ nc:link="nav_help.xhtml#full_screen_mode"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:helper_applications"
+ nc:name="helper applications"
+ nc:link="cs_nav_prefs_navigator.xhtml#helper_applications"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:home_page"
+ nc:name="home page"
+ nc:link="nav_help.xhtml#viewing_your_home_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:keyboard_shortcuts"
+ nc:name="keyboard shortcuts"
+ nc:link="shortcuts_navigator.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:opening_pages"
+ nc:name="opening pages"
+ nc:link="nav_help.xhtml#moving_to_another_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:preferences"
+ nc:name="Browser Preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#navigator_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:set_as_wallpaper"
+ nc:name="set as wallpaper"
+ nc:link="nav_help.xhtml#setting_image_as_wallpaper"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:start_page"
+ nc:name="start page"
+ nc:link="customize_help.xhtml#specifying_a_starting_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:session_restore"
+ nc:name="session restore"
+ nc:link="customize_help.xhtml#session_restore"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:crash_recovery"
+ nc:name="crash recovery"
+ nc:link="customize_help.xhtml#session_restore"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="browser:start_up_components"
+ nc:name="start up components"
+ nc:link="customize_help.xhtml#specifying_which_components_open_at_launch"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#c">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="cache"
+ nc:name="cache"
+ nc:link="nav_help.xhtml#changing_cache_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="caret_browsing"
+ nc:name="caret browsing"
+ nc:link="cs_nav_prefs_advanced.xhtml#keyboard_navigation"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates"
+ nc:name="Certificates"
+ nc:link="using_certs_help.xhtml#using_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer"
+ nc:name="Composer"
+ nc:link="composer_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Cookie_Manager"
+ nc:name="Cookie Manager"
+ nc:link="using_priv_help.xhtml#using_the_cookie_manager"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies"
+ nc:name="cookies"
+ nc:link="using_priv_help.xhtml#using_the_cookie_manager"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="copy web pages"
+ nc:name="copy"
+ nc:link="nav_help.xhtml#copying_saving_and_printing_pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Customizing"
+ nc:name="Customizing Fonts and Colors"
+ nc:link="customize_help.xhtml#changing_fonts_colors_and_themes"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#cache">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="cache:changing_settings"
+ nc:name="changing cache settings"
+ nc:link="nav_help.xhtml#changing_cache_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cache:preferences"
+ nc:name="cache preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#cache"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Certificates">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Certificates:certificate_authority"
+ nc:name="certificate authority"
+ nc:link="using_certs_help.xhtml#managing_certificates_that_identify_certificate_authorities"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:getting"
+ nc:name="getting certificates"
+ nc:link="using_certs_help.xhtml#getting_your_own_certificate"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:managing"
+ nc:name="Managing Certificates"
+ nc:link="using_certs_help.xhtml#managing_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:preferences"
+ nc:name="Certificate Preferences"
+ nc:link="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:selecting_for_client_authentication"
+ nc:name="Selecting for client authentication"
+ nc:link="certs_prefs_help.xhtml#client_certificate_selection"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:server"
+ nc:name="server identity"
+ nc:link="using_certs_help.xhtml#managing_certificates_that_identify_servers"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:using"
+ nc:name="Using Certificates"
+ nc:link="using_certs_help.xhtml#using_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:validation"
+ nc:name="certificate validation"
+ nc:link="using_certs_help.xhtml#controlling_validation"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:website"
+ nc:name="website identity"
+ nc:link="using_certs_help.xhtml#managing_certificates_that_identify_servers"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Certificates:your_own"
+ nc:name="Your own identity"
+ nc:link="using_certs_help.xhtml#managing_certificates_that_identify_you"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Composer">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Composer:adding_tables"
+ nc:name="adding tables"
+ nc:link="composer_help.xhtml#adding_tables_to_your_web_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:Advanced_Property_Editor"
+ nc:name="Advanced Property Editor"
+ nc:link="composer_help.xhtml#using_the_advanced_property_editor"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:changing_text_color"
+ nc:name="changing text color"
+ nc:link="composer_help.xhtml#changing_text_color_style_and_font"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:changing_text_font"
+ nc:name="changing text font"
+ nc:link="composer_help.xhtml#changing_text_color_style_and_font"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:checking_html"
+ nc:name="checking html"
+ nc:link="composer_help.xhtml#validating_the_html"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:creating_links"
+ nc:name="creating links"
+ nc:link="composer_help.xhtml#creating_links_in_composer"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:creating_new_pages"
+ nc:name="creating new pages"
+ nc:link="composer_help.xhtml#creating_a_new_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:editing_image_properties"
+ nc:name="editing image properties"
+ nc:link="composer_help.xhtml#editing_image_properties"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:editing_modes"
+ nc:name="editing modes"
+ nc:link="composer_help.xhtml#choosing_the_right_editing_mode"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:finding_replacing_text"
+ nc:name="finding replacing text"
+ nc:link="composer_help.xhtml#finding_and_replacing_text"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:formatting_lists"
+ nc:name="formatting lists"
+ nc:link="composer_help.xhtml#formatting_paragraphs_headings_and_lists"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:formatting_paragraphs"
+ nc:name="formatting paragraphs"
+ nc:link="composer_help.xhtml#formatting_paragraphs_headings_and_lists"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:general_preferences"
+ nc:name="general preferences"
+ nc:link="composer_help.xhtml#composer_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:inserting_horizontal_lines"
+ nc:name="inserting horizontal lines"
+ nc:link="composer_help.xhtml#inserting_horizontal_lines"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:inserting_images"
+ nc:name="inserting images"
+ nc:link="composer_help.xhtml#inserting_an_image_into_your_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:keyboard_shortcuts"
+ nc:name="keyboard shortcuts"
+ nc:link="shortcuts_composer.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:removing_text_styles"
+ nc:name="removing text styles"
+ nc:link="composer_help.xhtml#removing_or_discontinuing_text_styles"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:saving_pages"
+ nc:name="saving pages in composer"
+ nc:link="composer_help.xhtml#saving_and_browsing_your_new_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:setting_page_colors"
+ nc:name="setting page colors"
+ nc:link="composer_help.xhtml#setting_page_colors_and_backgrounds"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:setting_page_properties"
+ nc:name="setting page properties"
+ nc:link="composer_help.xhtml#setting_page_properties_and_meta_tags"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:special_characters"
+ nc:name="special characters"
+ nc:link="composer_help.xhtml#inserting_special_characters"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Composer:working_with_lists"
+ nc:name="working with lists"
+ nc:link="composer_help.xhtml#working_with_lists"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Cookie_Manager">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Cookie_Manager:cookies_stored_by"
+ nc:name="stored cookies"
+ nc:link="using_priv_help.xhtml#stored_cookies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Cookie_Manager:using"
+ nc:name="using cookies"
+ nc:link="using_priv_help.xhtml#using_the_cookie_manager"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#cookies">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="cookies:about"
+ nc:name="about cookies"
+ nc:link="using_priv_help.xhtml#using_the_cookie_manager"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies:email_and"
+ nc:name="e-mail and cookies"
+ nc:link="privacy_help.xhtml#how_can_i_control_web_pages_in_email_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies:managing_per_site"
+ nc:name="managing cookies per website"
+ nc:link="using_priv_help.xhtml#managing_cookies_site-by-site"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies:preferences"
+ nc:name="cookie preferences"
+ nc:link="using_priv_help.xhtml#cookies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies:removing"
+ nc:name="removing cookies"
+ nc:link="using_priv_help.xhtml#removing_cookies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies:third-party"
+ nc:name="third-party cookies"
+ nc:link="privacy_help.xhtml#what_are_third-party_cookies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="cookies:viewing"
+ nc:name="viewing cookies"
+ nc:link="using_priv_help.xhtml#viewing_cookies"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#d">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="default_account"
+ nc:name="default account"
+ nc:link="mailnews_getting_started.xhtml#set_as_default"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Developer_Tools"
+ nc:name="Developer Tools"
+ nc:link="developer_tools.xhtml#devtools"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="device_manager"
+ nc:name="Device Manager"
+ nc:link="using_certs_help.xhtml#about_security_devices_and_modules"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Download_Manager"
+ nc:name="Download Manager"
+ nc:link="nav_help.xhtml#plugins_and_downloads"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Download_Manager">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Download_Manager:preferences"
+ nc:name="download manager preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#downloads"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#e">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="email_addresses"
+ nc:name="e-mail addresses"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="encryption"
+ nc:name="encryption"
+ nc:link="mailnews_security.xhtml#signing_and_encrypting_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="JSConsole"
+ nc:name="error console"
+ nc:link="developer_tools.xhtml#js_console"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="extensions_panel"
+ nc:name="extensions panel"
+ nc:link="customize_help.xhtml#the_extensions_panel"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#email_addresses">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="email_addresses:address_books_and"
+ nc:name="adding e-mail addresses to address books"
+ nc:link="mailnews_addressbooks.xhtml#adding_entries_to_your_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="email_addresses:in_mail_messages"
+ nc:name="e-mail address in messages"
+ nc:link="mailnews_using_mail.xhtml#addressing_a_message"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#encryption">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="encryption:about"
+ nc:name="about encryption and signing"
+ nc:link="mailnews_security.xhtml#about_digital_signatures_and_encryption"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="encryption:of_email_messages"
+ nc:name="encrypted e-mail messages"
+ nc:link="mailnews_security.xhtml#signing_and_encrypting_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="encryption:of_stored_sensitive_information"
+ nc:name="storing sensitive information"
+ nc:link="using_priv_help.xhtml#encrypting_stored_sensitive_information"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="encryption:settings_for_email"
+ nc:name="encryption settings"
+ nc:link="mailnews_security.xhtml#configuring_security_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="encryption:status_of_web_page"
+ nc:name="security of a web page"
+ nc:link="using_certs_help.xhtml#checking_security_for_a_web_page"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#f">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="feeds"
+ nc:name="Feeds"
+ nc:link="glossary.xhtml#feed"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="file_types"
+ nc:name="file types"
+ nc:link="nav_help.xhtml#plugins_and_downloads"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Filing_messages"
+ nc:name="Filing messages"
+ nc:link="mailnews_organizing.xhtml#filing_messages_in_folders"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Find_Links_As_You_Type"
+ nc:name="Find Links As You Type"
+ nc:link="cs_nav_prefs_advanced.xhtml#fayt"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Find_Text_As_You_Type"
+ nc:name="Find Text As You Type"
+ nc:link="cs_nav_prefs_advanced.xhtml#fayt"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="FIPS_mode"
+ nc:name="FIPS mode"
+ nc:link="using_certs_help.xhtml#enable_fips_mode"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="font_changing_in_composer"
+ nc:name="Font changing in Composer"
+ nc:link="composer_help.xhtml#changing_text_color_style_and_font"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#feeds">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="feeds:subscribing"
+ nc:name="subscribing to a feed"
+ nc:link="mailnews_blogs_and_feeds.xhtml#subscribing_to_blogs_and_news_feeds"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="feeds:reading"
+ nc:name="Reading a feed"
+ nc:link="mailnews_blogs_and_feeds.xhtml#reading_blogs_and_news_feed_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="feeds:exporting_importing"
+ nc:name="exporting and importing feeds"
+ nc:link="mailnews_blogs_and_feeds.xhtml#exporting_and_importing_feeds"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#g">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="GIF"
+ nc:name="GIF, inserting"
+ nc:link="composer_help.xhtml#inserting_an_image_into_your_page"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#h">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="helper_applications"
+ nc:name="helper applications"
+ nc:link="nav_help.xhtml#plugins_and_downloads"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="home_page"
+ nc:name="home page"
+ nc:link="cs_nav_prefs_navigator.xhtml#navigator"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="HTML"
+ nc:name="HTML"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="HTML_editor"
+ nc:name="HTML editor"
+ nc:link="composer_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="HTTP_Networking"
+ nc:name="HTTP Networking"
+ nc:link="cs_nav_prefs_advanced.xhtml#http_networking"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#HTML">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="HTML:editing_in_mail_messages"
+ nc:name="editing HTML in mail messages"
+ nc:link="mailnews_using_mail.xhtml#editing_or_inserting_html_elements"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="HTML:using_in_Composer"
+ nc:name="inserting HTML in Composer"
+ nc:link="composer_help.xhtml#inserting_html_elements_and_attributes"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="HTML:using_in_mail_messages"
+ nc:name="using HTML in mail messages"
+ nc:link="mailnews_using_mail.xhtml#using_html_in_your_messages"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#i">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="images"
+ nc:name="images"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="IMAP"
+ nc:name="IMAP"
+ nc:link="mailnews_account_settings.xhtml#about_internet_message_access_protocol"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="import"
+ nc:name="import"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+
+<rdf:Description about="#images">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="images:email_and"
+ nc:name="e-mail and images"
+ nc:link="privacy_help.xhtml#how_can_i_control_web_pages_in_email_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="images:managing"
+ nc:name="managing images"
+ nc:link="using_priv_help.xhtml#managing_images"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="images:preferences"
+ nc:name="image preferences"
+ nc:link="using_priv_help.xhtml#images"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="images:set_as_wallpaper"
+ nc:name="set as wallpaper"
+ nc:link="nav_help.xhtml#setting_image_as_wallpaper"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#IMAP">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="IMAP:about"
+ nc:name="about IMAP"
+ nc:link="mailnews_account_settings.xhtml#about_internet_message_access_protocol"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="IMAP:advanced_mail_settings"
+ nc:name="advanced IMAP mail settings"
+ nc:link="mailnews_account_settings.xhtml#advanced_imap_server_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="IMAP:server_settings"
+ nc:name="IMAP server settings"
+ nc:link="mailnews_account_settings.xhtml#imap_server_settings"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#import">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="import:address_book"
+ nc:name="import address books"
+ nc:link="mailnews_addressbooks.xhtml#importing_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="import:bookmark_list"
+ nc:name="import bookmarks"
+ nc:link="customize_help.xhtml#exporting_or_importing_a_bookmark_list"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="import:certificates"
+ nc:name="import certificates"
+ nc:link="certs_help.xhtml#your_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="import:feeds"
+ nc:name="import feeds"
+ nc:link="mailnews_blogs_and_feeds.xhtml#exporting_and_importing_feeds"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="import:mail"
+ nc:name="import mail or settings from other programs"
+ nc:link="mailnews_getting_started.xhtml#importing_mail_from_other_programs"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#j">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="JavaScript"
+ nc:name="JavaScript"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#JavaScript">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="JavaScript:preferences"
+ nc:name="JavaScript Preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#scripts_and_plugins"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#k">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="keyboard_preferences"
+ nc:name="keyboard preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#keyboard_navigation"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="keyboard_shortcuts"
+ nc:name="keyboard shortcuts"
+ nc:link="shortcuts.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="keywords"
+ nc:name="keywords"
+ nc:link="nav_help.xhtml#custom_keywords"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#keyboard_shortcuts">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="keyboard_shortcuts:Composer"
+ nc:name="Composer keyboard shortcuts"
+ nc:link="shortcuts_composer.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="keyboard_shortcuts:general"
+ nc:name="general keyboard shortcuts"
+ nc:link="shortcuts.xhtml#general_mozilla_shortcuts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="keyboard_shortcuts:help"
+ nc:name="help keyboard shortcuts"
+ nc:link="shortcuts.xhtml#using_shortcuts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="keyboard_shortcuts:Mail_and_Newsgroups"
+ nc:name="Mail and Newsgroups keyboard shortcuts"
+ nc:link="shortcuts_mailnews.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="keyboard_shortcuts:Navigator"
+ nc:name="Browser keyboard shortcuts"
+ nc:link="shortcuts_navigator.xhtml"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#l">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="languages"
+ nc:name="languages"
+ nc:link="nav_help.xhtml#using_languages_and_international_content"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="languages_panel"
+ nc:name="languages panel"
+ nc:link="customize_help.xhtml#the_languages_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="LDAP"
+ nc:name="LDAP"
+ nc:link="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="location_aware_browsing"
+ nc:name="location aware browsing preferences"
+ nc:link="privsec_help.xhtml#location_aware_browsing"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="lock_icon"
+ nc:name="lock icon"
+ nc:link="using_certs_help.xhtml#checking_security_for_a_web_page"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#languages">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="languages:and_international_content"
+ nc:name="language and international content"
+ nc:link="nav_help.xhtml#using_languages_and_international_content"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="languages:Appearance_preferences"
+ nc:name="User interface languages"
+ nc:link="cs_nav_prefs_appearance.xhtml#appearance"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#LDAP">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="LDAP:adding_to_address_book"
+ nc:name="adding directories to address book"
+ nc:link="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="LDAP:directory_server_settings"
+ nc:name="directory server settings"
+ nc:link="mailnews_addressbooks.xhtml#directory_server_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="LDAP:global_settings"
+ nc:name="LDAP settings"
+ nc:link="mailnews_preferences.xhtml#addressing_preferences"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#m">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="mail"
+ nc:name="mail security settings"
+ nc:link="mailnews_account_settings.xhtml#security"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups"
+ nc:name="Mail and Newsgroups"
+ nc:link="mailnews_getting_started.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="master_password"
+ nc:name="Master Password"
+ nc:link="passwords_help.xhtml#change_master_password"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="mouse_wheel"
+ nc:name="mouse wheel"
+ nc:link="nav_help.xhtml#using_a_mouse_wheel"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Mail_and_Newsgroups">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:Account_Setup_Wizard"
+ nc:name="Account Setup Wizard"
+ nc:link="mailnews_getting_started.xhtml#using_the_mail_account_setup_wizard"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:adding_accounts"
+ nc:name="adding accounts"
+ nc:link="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:address_autocompletion"
+ nc:name="address autocompletion"
+ nc:link="mailnews_preferences.xhtml#address_autocompletion"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:addressing_messages"
+ nc:name="addressing messages"
+ nc:link="mailnews_using_mail.xhtml#addressing_a_message"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:addressing_preferences"
+ nc:name="addressing preferences"
+ nc:link="mailnews_preferences.xhtml#addressing_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:Advanced_IMAP_settings"
+ nc:name="Advanced IMAP settings"
+ nc:link="mailnews_account_settings.xhtml#advanced_imap_server_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:attaching_files"
+ nc:name="attaching files"
+ nc:link="mailnews_using_mail.xhtml#attaching_a_file_or_web_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:changing_account_settings"
+ nc:name="changing account settings"
+ nc:link="mailnews_getting_started.xhtml#changing_the_settings_for_an_account"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:Compose_window"
+ nc:name="Compose window"
+ nc:link="mailnews_using_mail.xhtml#using_the_message_composition_window"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:composing_messages"
+ nc:name="composing messages"
+ nc:link="mailnews_using_mail.xhtml#composing_mail_and_newsgroup_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:copies_and_folder_settings"
+ nc:name="copies and folder settings"
+ nc:link="mailnews_account_settings.xhtml#copies_and_folders"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:copying_folders"
+ nc:name="copying folders"
+ nc:link="mailnews_organizing.xhtml#moving_or_copying_a_folder"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:creating_filters"
+ nc:name="creating filters"
+ nc:link="mailnews_organizing.xhtml#creating_message_filters"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:creating_folders"
+ nc:name="creating folders"
+ nc:link="mailnews_organizing.xhtml#creating_a_folder"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:creating_HTML_mail_messages"
+ nc:name="creating HTML mail messages"
+ nc:link="mailnews_using_mail.xhtml#creating_html_mail_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:disk_space_and_storage_settings"
+ nc:name="disk space and storage settings"
+ nc:link="mailnews_account_settings.xhtml#synchronization_and_storage"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:filing_messages"
+ nc:name="filing messages"
+ nc:link="mailnews_organizing.xhtml#filing_messages_in_folders"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:forwarding_messages"
+ nc:name="forwarding messages"
+ nc:link="mailnews_using_mail.xhtml#forwarding_a_message"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:general_preferences"
+ nc:name="general mail preferences"
+ nc:link="mailnews_preferences.xhtml#mail_and_newsgroup_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:getting_new_messages"
+ nc:name="getting new messages"
+ nc:link="mailnews_using_mail.xhtml#getting_new_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:HTML_mail_recipients"
+ nc:name="HTML mail recipients"
+ nc:link="mailnews_using_mail.xhtml#specifying_recipients_for_html_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:HTML_mail_sending_options"
+ nc:name="HTML mail sending options"
+ nc:link="mailnews_using_mail.xhtml#choosing_html_mail_sending_options"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:HTML_Message_Source"
+ nc:name="HTML Message Source"
+ nc:link="mailnews_using_mail.xhtml#viewing_the_message_source_for_html_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:identity_settings"
+ nc:name="identity settings"
+ nc:link="mailnews_account_settings.xhtml#account_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:mail_window_layout"
+ nc:name="mail window layout"
+ nc:link="mailnews_using_mail.xhtml#choosing_how_you_view_the_mail_window"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:message_searching"
+ nc:name="message searching"
+ nc:link="mailnews_organizing.xhtml#searching_through_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:network_preferences"
+ nc:name="Mail and Newsgroups Network preferences"
+ nc:link="mailnews_preferences.xhtml#network_and_storage_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:offline_synchronization_settings"
+ nc:name="offline synchronization settings"
+ nc:link="mailnews_account_settings.xhtml#synchronization_and_storage"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:POP_settings"
+ nc:name="POP settings"
+ nc:link="mailnews_account_settings.xhtml#pop_server_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:renaming_folders"
+ nc:name="renaming folders"
+ nc:link="mailnews_organizing.xhtml#renaming_a_folder"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:replying_to_messages"
+ nc:name="replying to messages"
+ nc:link="mailnews_using_mail.xhtml#replying_to_a_message"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:saving_and_printing_messages"
+ nc:name="saving and printing messages"
+ nc:link="mailnews_using_mail.xhtml#saving_and_printing_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:saving_attachments"
+ nc:name="saving attachments"
+ nc:link="mailnews_using_mail.xhtml#saving_attachments"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:saving_draft_messages"
+ nc:name="saving draft messages"
+ nc:link="mailnews_using_mail.xhtml#saving_and_editing_a_draft_message"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:sending_messages"
+ nc:name="sending messages"
+ nc:link="mailnews_using_mail.xhtml#sending_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:sending_options"
+ nc:name="sending options"
+ nc:link="mailnews_using_mail.xhtml#selecting_message_sending_options"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:SMTP_settings"
+ nc:name="SMTP settings"
+ nc:link="mailnews_account_settings.xhtml#outgoing_server"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:Text_Encoding"
+ nc:name="Text Encoding"
+ nc:link="mailnews_preferences.xhtml#text_encoding"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:sorting_and_threading_messages"
+ nc:name="sorting and threading messages"
+ nc:link="mailnews_using_mail.xhtml#sorting_and_threading_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:using_address_books"
+ nc:name="using address books"
+ nc:link="mailnews_addressbooks.xhtml#using_address_books"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:using_attachments"
+ nc:name="using attachments"
+ nc:link="mailnews_using_mail.xhtml#using_attachments"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:using_HTML_in_messages"
+ nc:name="using HTML in messages"
+ nc:link="mailnews_using_mail.xhtml#using_html_in_your_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:viewing_attachments"
+ nc:name="viewing attachments"
+ nc:link="mailnews_using_mail.xhtml#viewing_and_opening_attachments"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Mail_and_Newsgroups:working_offline"
+ nc:name="working offline"
+ nc:link="mailnews_offline.xhtml#working_offline"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#master_password">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="master_password:changing"
+ nc:name="Changing master password"
+ nc:link="passwords_help.xhtml#change_master_password"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="master_password:forgetting"
+ nc:name="Forgetting your master password"
+ nc:link="using_priv_help.xhtml#what_to_do_if_you_forget_your_master_password"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#n">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="navigation"
+ nc:name="navigation"
+ nc:link="help_help.xhtml#finding_the_topic_you_want"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Newsgroups"
+ nc:name="Newsgroups"
+ nc:link="mailnews_newsgroups.xhtml#getting_started_with_newsgroups"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Newsgroups">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Newsgroups:getting_started"
+ nc:name="getting started with newsgroups"
+ nc:link="mailnews_newsgroups.xhtml#getting_started_with_newsgroups"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Newsgroups:server_settings"
+ nc:name="newsgroup server settings"
+ nc:link="mailnews_account_settings.xhtml#server_settings"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#o">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Offline"
+ nc:name="Offline"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="OPML"
+ nc:name="OPML"
+ nc:link="glossary.xhtml#opml"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Offline">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Offline:general_preferences"
+ nc:name="offline preferences"
+ nc:link="mailnews_preferences.xhtml#network_and_storage_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Offline:offline_apps"
+ nc:name="Offline Web Applications"
+ nc:link="cs_nav_prefs_advanced.xhtml#offline_apps"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Offline:Mail_and_Newsgroups"
+ nc:name="Offline Mail and News"
+ nc:link="mailnews_offline.xhtml#working_offline"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#p">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="page_info"
+ nc:name="page info"
+ nc:link="page_info_help.xhtml#viewing_page_info"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="password_manager"
+ nc:name="Password Manager"
+ nc:link="using_priv_help.xhtml#using_the_password_manager"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="phishing"
+ nc:name="phishing"
+ nc:link="mailnews_organizing.xhtml#phishing_detection"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="plugins"
+ nc:name="plugins"
+ nc:link="nav_help.xhtml#plugins_and_downloads"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="plugins_panel"
+ nc:name="plugins panel"
+ nc:link="customize_help.xhtml#the_plugins_panel"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="POP"
+ nc:name="POP"
+ nc:link="mailnews_account_settings.xhtml#about_post_office_protocol"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="passwords"
+ nc:name="Passwords"
+ nc:link="passwords_help.xhtml#passwords"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="popup_windows"
+ nc:name="popup windows"
+ nc:link="cs_priv_prefs_popup.xhtml#controlling_popups"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="popups"
+ nc:name="popups"
+ nc:link="cs_priv_prefs_popup.xhtml#controlling_popups"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences"
+ nc:name="preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="print"
+ nc:name="print"
+ nc:link="nav_help.xhtml#copying_saving_and_printing_pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="privacy"
+ nc:name="privacy"
+ nc:link="privacy_help.xhtml#privacy_on_the_internet"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="profiles"
+ nc:name="profiles"
+ nc:link="profiles_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="proxies"
+ nc:name="proxies"
+ nc:link="cs_nav_prefs_advanced.xhtml#proxies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="publishing_pages"
+ nc:name="publishing pages"
+ nc:link="composer_help.xhtml#publishing_your_pages_on_the_web"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+
+<rdf:Description about="#password_manager">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="password_manager:encrypting_stored_passwords"
+ nc:name="Encrypting Stored Passwords"
+ nc:link="using_priv_help.xhtml#encrypting_stored_sensitive_information"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="password_manager:master_password"
+ nc:name="Master Password"
+ nc:link="using_priv_help.xhtml#setting_a_master_password"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="password_manager:preferences"
+ nc:name="Password Preferences"
+ nc:link="passwords_help.xhtml#password_manager"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#passwords">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="passwords:choosing"
+ nc:name="Choosing a Good Password"
+ nc:link="passwords_help.xhtml#choosing_a_good_password"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="passwords:preferences"
+ nc:name="Password Preferences"
+ nc:link="passwords_help.xhtml#passwords"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="passwords:remembering_automatically"
+ nc:name="Remembering Automatically"
+ nc:link="using_priv_help.xhtml#using_the_password_manager"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#POP">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="POP:server_settings"
+ nc:name="POP server settings"
+ nc:link="mailnews_account_settings.xhtml#pop_server_settings"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#popup_windows">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="popup_windows:preferences"
+ nc:name="popup window preferences"
+ nc:link="cs_priv_prefs_popup.xhtml#privacy_and_security_preferences_popup_windows"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#preferences">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="preferences:advanced"
+ nc:name="advanced preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#advanced_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:appearance"
+ nc:name="appearance preferences"
+ nc:link="cs_nav_prefs_appearance.xhtml#appearance"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:cache"
+ nc:name="cache preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#cache"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:certificates"
+ nc:name="certificates preferences"
+ nc:link="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:colors"
+ nc:name="colors preferences"
+ nc:link="cs_nav_prefs_appearance.xhtml#colors"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:content"
+ nc:name="content preferences"
+ nc:link="cs_nav_prefs_appearance.xhtml#content"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:cookies"
+ nc:name="cookies preferences"
+ nc:link="using_priv_help.xhtml#cookies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:Downloads"
+ nc:name="Downloads preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#downloads"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:fonts"
+ nc:name="fonts preferences"
+ nc:link="cs_nav_prefs_appearance.xhtml#fonts"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:helper_applications"
+ nc:name="helper application preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#helper_applications"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:history"
+ nc:name="history preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#history"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:images"
+ nc:name="image preferences"
+ nc:link="using_priv_help.xhtml#images"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:internet_search"
+ nc:name="internet search preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#internet_search"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:JavaScript"
+ nc:name="JavaScript preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#scripts_and_plugins"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:keyboard"
+ nc:name="keyboard preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#keyboard_navigation"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:languages"
+ nc:name="languages preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#languages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:Link_Behavior"
+ nc:name="Link Behavior preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#link_behavior"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:Navigator"
+ nc:name="Browser preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#navigator_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:passwords"
+ nc:name="passwords preferences"
+ nc:link="passwords_help.xhtml#passwords"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:popup_windows"
+ nc:name="popup windows preferences"
+ nc:link="cs_priv_prefs_popup.xhtml#privacy_and_security_preferences_popup_windows"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:privacy"
+ nc:name="privacy preferences"
+ nc:link="privsec_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:proxies"
+ nc:name="proxies preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#proxies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:security"
+ nc:name="security preferences"
+ nc:link="privsec_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:SSLTLS"
+ nc:name="SSL/TLS preferences"
+ nc:link="ssl_help.xhtml#privacy_and_security_preferences_ssltls"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="preferences:Tabbed_Browsing"
+ nc:name="Tabbed Browsing preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#tabbed_browsing"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#privacy">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="privacy:about"
+ nc:name="about privacy"
+ nc:link="privacy_help.xhtml#privacy_on_the_internet"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="privacy:cookies_and"
+ nc:name="cookies and privacy"
+ nc:link="privacy_help.xhtml#what_are_cookies_and_how_do_they_work"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="privacy:IP_address"
+ nc:name="IP address"
+ nc:link="privacy_help.xhtml#internet_address"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="privacy:preferences"
+ nc:name="privacy preferences"
+ nc:link="privsec_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="privacy:protecting"
+ nc:name="protecting your privacy"
+ nc:link="privacy_help.xhtml#using_privacy_features"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="privacy:viewing_site_policy"
+ nc:name="viewing website policy"
+ nc:link="page_info_help.xhtml"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#profiles">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="profiles:creating"
+ nc:name="creating a profile"
+ nc:link="profiles_help.xhtml#creating_a_new_profile"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="profiles:deleting"
+ nc:name="deleting a profile"
+ nc:link="profiles_help.xhtml#deleting_or_renaming_a_profile"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="profiles:managing"
+ nc:name="managing profiles"
+ nc:link="profiles_help.xhtml#managing_profiles"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="profiles:renaming"
+ nc:name="renaming a profile"
+ nc:link="profiles_help.xhtml#deleting_or_renaming_a_profile"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#proxies">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="proxies:advanced"
+ nc:name="advanced proxy preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#advanced_proxy_preferences"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="proxies:preferences"
+ nc:name="proxy preferences"
+ nc:link="cs_nav_prefs_advanced.xhtml#proxies"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="proxies:setting"
+ nc:name="setting proxy values"
+ nc:link="nav_help.xhtml#proxies"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#publishing_pages">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="publishing_pages:settings"
+ nc:name="publish page settings"
+ nc:link="composer_help.xhtml#publishing_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="publishing_pages:tips"
+ nc:name="publish page tips"
+ nc:link="composer_help.xhtml#tips_for_avoiding_broken_links_or_missing_images"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#r">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="resetting_master_password"
+ nc:name="resetting master password"
+ nc:link="passwords_help.xhtml#reset_master_password"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="return_receipts"
+ nc:name="return receipts"
+ nc:link="mailnews_account_settings.xhtml#return_receipts"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#return_receipts">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="return_receipts:general_preferences"
+ nc:name="return receipts preferences"
+ nc:link="mailnews_preferences.xhtml#return_receipts_preferences"/>
+ </rdf:li><rdf:li>
+ <rdf:Description ID="return_receipts:using"
+ nc:name="using return receipts"
+ nc:link="mailnews_using_mail.xhtml#confirming_that_your_message_was_opened"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#s">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="safe_browsing"
+ nc:name="safe browsing preferences"
+ nc:link="privsec_help.xhtml#safe_browsing"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="save"
+ nc:name="save"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="security"
+ nc:name="security"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="security_devices"
+ nc:name="security devices"
+ nc:link="using_certs_help.xhtml#about_security_devices_and_modules"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="settings"
+ nc:name="settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="security_modules"
+ nc:name="security modules"
+ nc:link="using_certs_help.xhtml#about_security_devices_and_modules"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="search"
+ nc:name="search"
+ nc:link="nav_help.xhtml#searching_the_web"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="searching_for_add-ons"
+ nc:name="searching for add-ons"
+ nc:link="customize_help.xhtml#searching_for_add-ons"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar"
+ nc:name="Sidebar"
+ nc:link="customize_help.xhtml#what_is_sidebar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="signing_email_messages"
+ nc:name="signing e-mail messages"
+ nc:link="mailnews_security.xhtml#signing_and_encrypting_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="smart_cards"
+ nc:name="smart cards"
+ nc:link="using_certs_help.xhtml#managing_smart_cards_and_other_security_devices"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="SSLTLS"
+ nc:name="SSL/TLS"
+ nc:link="ssl_help.xhtml"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="SMTP"
+ nc:name="SMTP"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="software_installation"
+ nc:name="software installation"
+ nc:link="cs_nav_prefs_advanced.xhtml#software_installation"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+
+<rdf:Description about="#save">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="save:web_pages"
+ nc:name="saving and printing web pages"
+ nc:link="nav_help.xhtml#copying_saving_and_printing_pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="save:messages"
+ nc:name="saving and printing messages"
+ nc:link="mailnews_using_mail.xhtml#saving_and_printing_messages"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#search">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="search:bookmarks"
+ nc:name="search bookmarks"
+ nc:link="nav_help.xhtml#searching_the_bookmarks_or_history_list"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="search:setting_preferences"
+ nc:name="search preferences"
+ nc:link="nav_help.xhtml#setting_search_preferences"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#security">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="security:checking_before_sending_message"
+ nc:name="checking security before sending message"
+ nc:link="mailnews_security.xhtml#message_security_compose_window"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="security:checking_for_a_web_page"
+ nc:name="checking security for a web page"
+ nc:link="using_certs_help.xhtml#checking_security_for_a_web_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="security:checking_for_received_messages"
+ nc:name="checking security for received messages"
+ nc:link="mailnews_security.xhtml#reading_signed_and_encrypted_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="security:preferences"
+ nc:name="security preferences"
+ nc:link="privsec_help.xhtml"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#security_devices">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="security_devices:managing"
+ nc:name="Managing security devices"
+ nc:link="using_certs_help.xhtml#managing_smart_cards_and_other_security_devices"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#security_modules">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="security_modules:managing"
+ nc:name="Managing security modules"
+ nc:link="using_certs_help.xhtml#managing_smart_cards_and_other_security_devices"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#settings">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="settings:certificate"
+ nc:name="certificate settings"
+ nc:link="certs_prefs_help.xhtml#certificate_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="settings:signed_and_encrypted mail"
+ nc:name="signed and encrypted mail settings"
+ nc:link="mailnews_account_settings.xhtml#security"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="settings:SSLTLS"
+ nc:name="SSL/TLS settings"
+ nc:link="ssl_help.xhtml#ssltls_settings"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Sidebar">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Sidebar:adding_tabs"
+ nc:name="adding sidebar tabs"
+ nc:link="customize_help.xhtml#adding_sidebar_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:customizing_tabs"
+ nc:name="customizing sidebar tabs"
+ nc:link="customize_help.xhtml#customizing_individual_sidebar_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:description"
+ nc:name="what is Sidebar?"
+ nc:link="customize_help.xhtml#what_is_sidebar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:opening_closing_resizing"
+ nc:name="opening, closing, and resizing sidebar"
+ nc:link="customize_help.xhtml#opening_closing_and_resizing_sidebar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:removing_tabs"
+ nc:name="removing sidebar tabs"
+ nc:link="customize_help.xhtml#removing_sidebar_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:reorganizing_tabs"
+ nc:name="reorganizing sidebar tabs"
+ nc:link="customize_help.xhtml#reorganizing_sidebar_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:search_categories"
+ nc:name="search categories"
+ nc:link="nav_help.xhtml#customizing_search_categories"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:searching_from"
+ nc:name="searching from Sidebar"
+ nc:link="nav_help.xhtml#searching_from_sidebar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Sidebar:viewing_tabs"
+ nc:name="viewing sidebar tabs"
+ nc:link="customize_help.xhtml#viewing_sidebar_tabs"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#SMTP">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="SMTP:choosing_a_different"
+ nc:name="choosing a different SMTP server"
+ nc:link="mailnews_account_settings.xhtml#account_settings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="SMTP:server_settings"
+ nc:name="SMTP server settings"
+ nc:link="mailnews_account_settings.xhtml#outgoing_server"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#SSLTLS">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="SSLTLS:preferences"
+ nc:name="SSL/TLS preferences"
+ nc:link="ssl_help.xhtml#privacy_and_security_preferences_ssltls"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="SSLTLS:protocols"
+ nc:name="SSL/TLS protocols"
+ nc:link="ssl_help.xhtml#ssltls_protocol_versions"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="SSLTLS:warnings"
+ nc:name="SSL/TLS warnings"
+ nc:link="ssl_help.xhtml#ssltls_warnings"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="SSLTLS:mixed_content"
+ nc:name="mixed content"
+ nc:link="ssl_help.xhtml#mixed_content"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#t">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Tab_Key_Navigation"
+ nc:name="Tab Key Navigation"
+ nc:link="cs_nav_prefs_advanced.xhtml#keyboard_navigation"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing"
+ nc:name="Tabbed Browsing"
+ nc:link="customize_help.xhtml#tabbed_browsing"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tags"
+ nc:name="Tags"
+ nc:link="mailnews_organizing.xhtml#tagging_messages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="themes"
+ nc:name="themes"
+ nc:link="customize_help.xhtml#changing_the_theme"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="toolbar"
+ nc:name="toolbar"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Tabbed_Browsing">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:bookmarking_tabs"
+ nc:name="bookmarking tabs"
+ nc:link="customize_help.xhtml#bookmarking_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:closing_tabs"
+ nc:name="closing tabs"
+ nc:link="customize_help.xhtml#closing_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:introduction"
+ nc:name="using tabbed browsing"
+ nc:link="nav_help.xhtml#using_tabbed_browsing"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:moving_tabs"
+ nc:name="moving tabs"
+ nc:link="customize_help.xhtml#moving_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:opening_tabs"
+ nc:name="opening tabs"
+ nc:link="customize_help.xhtml#opening_tabs"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:setting_up"
+ nc:name="setting up tabbed browsing"
+ nc:link="customize_help.xhtml#setting_up_tabbed_browsing"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Tabbed_Browsing:what_is"
+ nc:name="what is tabbed browsing"
+ nc:link="customize_help.xhtml#what_is_tabbed_browsing"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#Tags">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="Tags:general_preferences"
+ nc:name="tag preferences"
+ nc:link="mailnews_preferences.xhtml#tags"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#toolbar">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="toolbar:hiding_a_toolbar"
+ nc:name="hiding a toolbar"
+ nc:link="customize_help.xhtml#hiding_a_toolbar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="toolbar:menu_bar"
+ nc:name="menu bar"
+ nc:link="customize_help.xhtml#menu_bar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="toolbar:navigation_toolbar"
+ nc:name="navigation toolbar"
+ nc:link="customize_help.xhtml#navigation_toolbar"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="toolbar:personal_toolbar"
+ nc:name="Bookmarks Toolbar"
+ nc:link="customize_help.xhtml#personal_toolbar"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#u">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="updating_add-ons"
+ nc:name="updating add-ons"
+ nc:link="customize_help.xhtml#updating_add-ons"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="uploading_pages"
+ nc:name="uploading pages"
+ nc:link="composer_help.xhtml#publishing_your_pages_on_the_web"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="user_tracking"
+ nc:name="user tracking preferences"
+ nc:link="privsec_help.xhtml#privacy_and_security"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#v">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="validation"
+ nc:name="validation"
+ nc:link="using_certs_help.xhtml#how_validation_works"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#validation">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="validation:about"
+ nc:name="about validation"
+ nc:link="using_certs_help.xhtml#how_validation_works"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="validation:OCSP"
+ nc:name="OCSP"
+ nc:link="certs_prefs_help.xhtml#ocsp"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="validation:settings"
+ nc:name="validation settings"
+ nc:link="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#w">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="web_pages"
+ nc:name="web pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="Working_Offline"
+ nc:name="Working Offline"
+ nc:link="mailnews_offline.xhtml#working_offline"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="#web_pages">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="web_pages:copying"
+ nc:name="copying web pages"
+ nc:link="nav_help.xhtml#copying_saving_and_printing_pages"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="web_pages:customizing_fonts"
+ nc:name="customizing fonts"
+ nc:link="customize_help.xhtml#changing_fonts_colors_and_themes"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="web_pages:finding_within"
+ nc:name="finding text within a web page"
+ nc:link="nav_help.xhtml#searching_within_a_page"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="web_pages:history"
+ nc:name="web page history"
+ nc:link="nav_help.xhtml#about_history_lists"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="web_pages:navigating"
+ nc:name="navigating web pages"
+ nc:link="nav_help.xhtml#browsing_the_web"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="web_pages:searching"
+ nc:name="searching web pages"
+ nc:link="nav_help.xhtml#searching_the_web"/>
+ </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="web_pages:stopping"
+ nc:name="stopping"
+ nc:link="nav_help.xhtml#stopping_and_reloading"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+</rdf:RDF>
diff --git a/comm/suite/locales/en-US/chrome/common/help/help-indexAZ.rdf b/comm/suite/locales/en-US/chrome/common/help/help-indexAZ.rdf
new file mode 100644
index 0000000000..22cae57917
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/help-indexAZ.rdf
@@ -0,0 +1,41 @@
+<?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/. -->
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+
+ <rdf:Description about="urn:root">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="a" nc:name="A"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="b" nc:name="B"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="c" nc:name="C"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="d" nc:name="D"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="e" nc:name="E"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="f" nc:name="F"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="g" nc:name="G"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="h" nc:name="H"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="i" nc:name="I"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="j" nc:name="J"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="k" nc:name="K"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="l" nc:name="L"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="m" nc:name="M"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="n" nc:name="N"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="o" nc:name="O"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="p" nc:name="P"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="q" nc:name="Q"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="r" nc:name="R"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="s" nc:name="S"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="t" nc:name="T"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="u" nc:name="U"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="v" nc:name="V"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="w" nc:name="W"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="x" nc:name="X"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="y" nc:name="Y"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="z" nc:name="Z"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+</rdf:RDF>
diff --git a/comm/suite/locales/en-US/chrome/common/help/help-win.rdf b/comm/suite/locales/en-US/chrome/common/help/help-win.rdf
new file mode 100644
index 0000000000..86c1af49bd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/help-win.rdf
@@ -0,0 +1,52 @@
+<?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 rdf:RDF SYSTEM "chrome://branding/locale/brand.dtd" >
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+
+<rdf:Description about="suite-toc.rdf#nav-doc-ses">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="nav-doc-default"
+ nc:name="Making &brandShortName; Your Default Browser"
+ nc:link="nav_help.xhtml#making_mozilla_your_default_browser"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-index1.rdf#browser">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="browser:default"
+ nc:name="default browser"
+ nc:link="nav_help.xhtml#making_mozilla_your_default_browser"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-indexAZ.rdf#d">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="default_browser_preferences"
+ nc:name="default browser preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#navigator"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<rdf:Description about="help-index1.rdf#preferences">
+ <nc:subheadings>
+ <rdf:Seq><rdf:li>
+ <rdf:Description ID="preferences:default_browser"
+ nc:name="default browser preferences"
+ nc:link="cs_nav_prefs_navigator.xhtml#navigator"/>
+ </rdf:li></rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+</rdf:RDF>
diff --git a/comm/suite/locales/en-US/chrome/common/help/helpFileLayout.css b/comm/suite/locales/en-US/chrome/common/help/helpFileLayout.css
new file mode 100644
index 0000000000..9460882988
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/helpFileLayout.css
@@ -0,0 +1,70 @@
+/* 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/. */
+@import url("chrome://help/content/platformClasses.css");
+
+body {
+ margin: 2ex;
+ font-family: sans-serif;
+ font-size: 0.75em;
+ max-width: 120ex;
+}
+
+dd { margin-inline-start: 0px; margin-bottom: 1em; }
+dt { font-size: 10pt; font-weight: bold; }
+tt { font-size: 10pt; }
+
+:link:hover,
+:visited:hover { color: -moz-activehyperlinktext; }
+
+h1 { font-size: 20pt; }
+h2 { border-top: 1px solid black; font-size: 16pt; padding-top: 0.2em; }
+h3 { color: #009; font-size: 10pt; margin-bottom: 0px; margin-top: 35px; }
+
+kbd { font-family: sans-serif; }
+
+.defaultTable { border-collapse: collapse; border: 1px solid grey;
+ width: 100%; }
+
+.defaultTable td { border: 1px solid grey; padding: 4px; }
+
+.defaultTable th { background-color: #99ccff;
+ border: 1px solid grey; font-size: 10pt; padding: 4px; text-align: left; }
+
+.tbody-default > tr:nth-child(2n) {
+ background-color: #eeeeee;
+}
+
+.boilerPlate { font-size: 7pt; }
+
+.commandColumn { width: 40%; }
+.osFirstColumn { width: 20%; }
+.osSecondColumn { width: 20%; }
+.osThirdColumn { width: 20%; }
+
+p:first-child { padding-top: 0; margin-top: 0; }
+
+.separate > li { margin-bottom: 0.5em; }
+
+.contentsBox {
+ margin-top: 12px;
+ background-color: #cccccc;
+ border: 1px solid black;
+ width: 300px;
+ padding: 1em;
+}
+
+.contentsBox > ul {
+ list-style-type: none;
+}
+
+a[href^="http://"]:after, a[href^="https://"]:after, a[href^="x-moz-url-link:"]:after {
+ content: url("images/web-links.png");
+}
+
+@media print {
+ h1,h2,h3,h4,h5,h6 {
+ page-break-after: avoid;
+ page-break-inside: avoid;
+ }
+}
diff --git a/comm/suite/locales/en-US/chrome/common/help/help_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/help_help.xhtml
new file mode 100644
index 0000000000..49da2ef034
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/help_help.xhtml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Using the Help Window</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="help_window_top">Using the Help Window</h1>
+
+<p>This section describes how to use the Help window (the entire window in
+ which this text and the sidebar on the left are displayed).</p>
+
+<p>For additional sources of support and information, click the links displayed
+ in the bottom portion of the <a href="welcome_help.xhtml">Help and Support
+ Center</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#finding_the_topic_you_want">Finding the Topic You Want</a></li>
+ <li><a href="#retracing_your_steps">Retracing Your Steps and Printing</a></li>
+ <li><a href="#using_help_buttons">Using Help Buttons</a></li>
+ <li><a href="#search_tips">Search Tips</a></li>
+ </ul>
+</div>
+
+<h2 id="finding_the_topic_you_want">Finding the Topic You Want</h2>
+
+<ul>
+ <li>Main topics are listed in the left sidebar. Click a topic to read about
+ it. To see its subtopics, double-click or click the plus-sign/triangle to
+ the left.</li>
+ <li>Type a word or phrase in the search field to display a list of related
+ topics.<br/>
+ If your search doesn&apos;t return anything, try typing fewer words or a
+ different combination of words.
+ </li>
+ <li>To search inside a page, use the <kbd class="mac">Cmd</kbd><kbd
+ class="noMac">Ctrl</kbd>+<kbd>F</kbd> shortcut. Enter the word or phrase
+ you would like to find into the text field of the bar that will open at the
+ top and press <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>,
+ or use the <q>Next</q> and <q>Previous</q> buttons to find the next match in
+ the chosen direction. Use <q>Highlight all</q> to highlight your search
+ phrase within the whole page. If you want to do a case-sensitive search,
+ check the <q>Match case</q> box.</li>
+</ul>
+
+<p>To see information related to the Help topic you are reading, click
+ links in the Help window just as you would in a regular web page.</p>
+
+<h2 id="retracing_your_steps">Retracing Your Steps and Printing</h2>
+
+<p>To retrace your steps in Help, click the buttons near the top left corner of
+ the Help window:</p>
+
+<p><img src="images/help_nav.png" alt="" width="124" height="38"/></p>
+
+<ul>
+ <li>Click the Back button to move back through pages you have previously
+ visited. It works just like the Back button in the main &brandShortName;
+ window.</li>
+ <li>Click the Forward button to move forward through pages you have
+ previously visited. It works just like the Forward button in the main
+ &brandShortName; window.</li>
+ <li>Click the Home button to see the Help and Support Center, which includes
+ links to support options and web-based resources.</li>
+</ul>
+
+<p>Alternatively, print the instructions you want to follow:</p>
+
+<ul>
+ <li>To print the whole page that&apos;s currently displayed in the Help
+ window, click the Print button near the top left corner of the Help
+ window:
+
+ <p><img src="images/help_print.gif" alt="" width="37" height="31"/></p>
+ </li>
+ <li>To print just a portion of the page, first click and drag to select the
+ area you want to print and then click the Print button.</li>
+</ul>
+
+<h2 id="using_help_buttons">Using Help Buttons</h2>
+
+<p>Many specialized &brandShortName; windows and dialog boxes include a help
+ button.</p>
+
+<p>Click any help button to see detailed information about the window in
+ which it appears.</p>
+
+<h2 id="search_tips">Search Tips</h2>
+
+<p>If you don&apos;t find what you want in your search, here are a few helpful
+ tips:</p>
+
+<ul>
+ <li>Be sure that you are searching for something that relates to
+ &brandShortName;; this is not a general Internet search.</li>
+ <li>Try to broaden your search&mdash;don&apos;t be too specific; terms
+ could be worded differently than your search.</li>
+ <li>At the same time, you should avoid being too broad with your
+ search terms: a word like <q>web</q> will probably return far too
+ many hits.</li>
+</ul>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/anchor-in-doc.gif b/comm/suite/locales/en-US/chrome/common/help/images/anchor-in-doc.gif
new file mode 100755
index 0000000000..7b401120e8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/anchor-in-doc.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/broken.gif b/comm/suite/locales/en-US/chrome/common/help/images/broken.gif
new file mode 100644
index 0000000000..e0c46300df
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/broken.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/bullets.gif b/comm/suite/locales/en-US/chrome/common/help/images/bullets.gif
new file mode 100644
index 0000000000..a8457a591b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/bullets.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/columns.png b/comm/suite/locales/en-US/chrome/common/help/images/columns.png
new file mode 100644
index 0000000000..1b3cb63189
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/columns.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/composer_icon.png b/comm/suite/locales/en-US/chrome/common/help/images/composer_icon.png
new file mode 100644
index 0000000000..2e8ac1f415
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/composer_icon.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/help_nav.png b/comm/suite/locales/en-US/chrome/common/help/images/help_nav.png
new file mode 100644
index 0000000000..b28ccf061f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/help_nav.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/help_print.gif b/comm/suite/locales/en-US/chrome/common/help/images/help_print.gif
new file mode 100644
index 0000000000..73b523d02d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/help_print.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/image.gif b/comm/suite/locales/en-US/chrome/common/help/images/image.gif
new file mode 100644
index 0000000000..d5236e7f45
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/image.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/link.gif b/comm/suite/locales/en-US/chrome/common/help/images/link.gif
new file mode 100755
index 0000000000..4a40004b2c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/link.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/locationbar.png b/comm/suite/locales/en-US/chrome/common/help/images/locationbar.png
new file mode 100644
index 0000000000..71eec980ef
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/locationbar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/locationbar_search.png b/comm/suite/locales/en-US/chrome/common/help/images/locationbar_search.png
new file mode 100644
index 0000000000..6d52cd0cb8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/locationbar_search.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_flag.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_flag.png
new file mode 100644
index 0000000000..4a2ebaacf1
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_flag.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_flag_column.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_flag_column.png
new file mode 100644
index 0000000000..7ecd32e10f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_flag_column.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_junk_column.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_junk_column.png
new file mode 100644
index 0000000000..34c64a5d8e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_junk_column.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_alert.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_alert.png
new file mode 100644
index 0000000000..5efc813a99
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_alert.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_balloon.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_balloon.png
new file mode 100644
index 0000000000..bdfc3ccabc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_balloon.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_trayicon.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_trayicon.png
new file mode 100644
index 0000000000..6a61fa7202
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_newmail_trayicon.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_quicksearch.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_quicksearch.png
new file mode 100644
index 0000000000..b8e2a49873
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_quicksearch.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_read.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_read.png
new file mode 100644
index 0000000000..48d43202d8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_read.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_read_column.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_read_column.png
new file mode 100644
index 0000000000..060c82adc9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_read_column.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/mail_unread.png b/comm/suite/locales/en-US/chrome/common/help/images/mail_unread.png
new file mode 100644
index 0000000000..37baf810ec
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/mail_unread.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/menubar.png b/comm/suite/locales/en-US/chrome/common/help/images/menubar.png
new file mode 100644
index 0000000000..0c0d0eaae0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/menubar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/numbers.gif b/comm/suite/locales/en-US/chrome/common/help/images/numbers.gif
new file mode 100644
index 0000000000..2721565883
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/numbers.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/offline.png b/comm/suite/locales/en-US/chrome/common/help/images/offline.png
new file mode 100644
index 0000000000..b32f903850
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/offline.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/online.png b/comm/suite/locales/en-US/chrome/common/help/images/online.png
new file mode 100644
index 0000000000..6756ead790
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/online.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/personalbar.png b/comm/suite/locales/en-US/chrome/common/help/images/personalbar.png
new file mode 100644
index 0000000000..dab7163e2a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/personalbar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/reload.gif b/comm/suite/locales/en-US/chrome/common/help/images/reload.gif
new file mode 100644
index 0000000000..f0c23aca63
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/reload.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/search_navigation_toolbar.png b/comm/suite/locales/en-US/chrome/common/help/images/search_navigation_toolbar.png
new file mode 100644
index 0000000000..0ff37c3a2d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/search_navigation_toolbar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/sidebar.png b/comm/suite/locales/en-US/chrome/common/help/images/sidebar.png
new file mode 100644
index 0000000000..e65f5754ec
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/sidebar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/tabbed_browsing_bar.png b/comm/suite/locales/en-US/chrome/common/help/images/tabbed_browsing_bar.png
new file mode 100644
index 0000000000..8949fc96ee
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/tabbed_browsing_bar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/table.gif b/comm/suite/locales/en-US/chrome/common/help/images/table.gif
new file mode 100644
index 0000000000..d0e0add2a8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/table.gif
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/task_mail.png b/comm/suite/locales/en-US/chrome/common/help/images/task_mail.png
new file mode 100644
index 0000000000..0654d8fd3c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/task_mail.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/task_newmail.png b/comm/suite/locales/en-US/chrome/common/help/images/task_newmail.png
new file mode 100644
index 0000000000..b09d6315fa
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/task_newmail.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/taskbar-ab.png b/comm/suite/locales/en-US/chrome/common/help/images/taskbar-ab.png
new file mode 100644
index 0000000000..185b0be800
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/taskbar-ab.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/taskbar.png b/comm/suite/locales/en-US/chrome/common/help/images/taskbar.png
new file mode 100644
index 0000000000..4e19b06ded
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/taskbar.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/threadbutton.png b/comm/suite/locales/en-US/chrome/common/help/images/threadbutton.png
new file mode 100644
index 0000000000..62bdcaa1bf
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/threadbutton.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/images/web-links.png b/comm/suite/locales/en-US/chrome/common/help/images/web-links.png
new file mode 100644
index 0000000000..fd7913734c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/images/web-links.png
Binary files differ
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_account_settings.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_account_settings.xhtml
new file mode 100644
index 0000000000..ee495dac89
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_account_settings.xhtml
@@ -0,0 +1,1225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Mail &amp; Newsgroups Account Settings</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="mail_and_newsgroups_account_settings">Mail &amp; Newsgroups Account
+ Settings</h1>
+
+<p>This section describes the settings in the Mail &amp; Newsgroups Account
+ Settings dialog box. Unlike the Preferences dialog box, which applies
+ settings to all accounts, the Mail &amp; Newsgroups Account Settings dialog
+ box lets you specify settings on a per-account basis.</p>
+
+<p>If you are not currently viewing the Mail &amp; Newsgroups Account Settings
+ dialog box, follow these steps:</p>
+
+<ol>
+ <li>Begin from the Mail window.</li>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account
+ Settings.</li>
+ <li>Select the name of the account whose settings you want to view or
+ change.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#account_settings">Account Settings</a></li>
+ <li><a href="#server_settings">Server Settings</a></li>
+ <li><a href="#copies_and_folders">Copies &amp; Folders</a></li>
+ <li><a href="#addressing">Composition &amp; Addressing</a></li>
+ <li><a href="#synchronization_and_storage">Synchronization &amp; Storage</a></li>
+ <li><a href="#junk_settings">Junk Settings</a></li>
+ <li><a href="#return_receipts">Return Receipts</a></li>
+ <li><a href="#security">Security</a></li>
+ <li><a href="#local_folders">Local Folders</a></li>
+ <li><a href="#outgoing_server">Outgoing Server (SMTP)</a></li>
+ </ul>
+</div>
+
+<h2 id="account_settings">Mail &amp; Newsgroups Account Settings – Account
+ Settings</h2>
+
+<p>This section describes how to view or change your Account Settings, such as
+ your user name, reply-to address, and signature. If you are not already
+ viewing the Account Settings, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the name of the account to display the Account Settings
+ panel.</li>
+</ol>
+
+<ul>
+ <li><strong>Account Name</strong>: The name for this account.</li>
+ <li>For any type of account but Blogs &amp; News Feeds:
+ <ul>
+ <li><strong>Identity</strong>: Stores your name, email address, reply-to
+ address (only if different from your email address), and organization
+ (optional).</li>
+ <li><strong>Signature text</strong>: If you want to attach a signature to
+ all outgoing messages, type its text into this box. Check <strong>Use
+ HTML</strong> to enable HTML code, e.g., &lt;b&gt;bold&lt;/b&gt;
+ (optional).</li>
+ <li><strong>Attach the signature from a file instead</strong>: Lets you
+ choose to attach the signature from a file (in text, HTML, or image
+ format) rather than entering its text. Checking this option overrides
+ any text entered into the signature box. Click Choose to locate the
+ signature file (optional).
+
+ <p>More signature options are located in
+ <a href="#addressing">Composition &amp; Addressing</a>.</p>
+ </li>
+ <li><strong>Attach my vCard to messages</strong>: Lets you choose if your
+ vCard should be attached to your outgoing messages. Click Edit Card to
+ edit the card information (optional).</li>
+ </ul>
+ </li>
+ <li>For Blogs &amp; News Feeds accounts:
+ <ul>
+ <li><strong>Check for new articles at startup</strong>: Select this
+ checkbox if you want to check this account automatically for new blogs
+ &amp; news messages whenever you start Mail &amp; Newsgroups.</li>
+ <li><strong>Check for new articles every [__] minutes</strong>: Select
+ this checkbox if you want to specify the number of minutes between feed
+ checks. You can also check for new blogs &amp; news messages at any time
+ by clicking Get Msgs in the Mail window.</li>
+ <li><strong>By default, show the article summary instead of loading the
+ web page</strong>: Select this checkbox if you want &brandShortName;
+ to display a brief summary of the article (bundled inside the feed)
+ instead of loading the full web page. Showing the article summary is
+ slightly faster than the full web page, but you may miss part of the
+ article content.</li>
+ <li><strong>Empty Trash on Exit</strong>: Empties the Trash folder
+ whenever you quit Mail &amp; Newsgroups.</li>
+ <li><strong>Manage Subscriptions...</strong>: Shows the Feed Subscriptions
+ dialog, that allows you to add, edit and remove feeds to this blogs
+ &amp; news feeds account.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="server_settings">Mail &amp; Newsgroups Account Settings – Server
+ Settings</h2>
+
+<p>&brandShortName; Mail &amp; Newsgroups can work with two types of mail
+ servers: IMAP and POP. If you are not sure which server type your Internet
+ service provider supports, ask your service provider. If your Internet
+ service provider supports both, the following descriptions may help you
+ choose which one to use.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#about_internet_message_access_protocol">About Internet
+ Message Access Protocol (IMAP)</a></li>
+ <li><a href="#about_post_office_protocol">About Internet Post Office
+ Protocol (POP)</a></li>
+ <li><a href="#imap_server_settings">IMAP Server Settings</a></li>
+ <li><a href="#advanced_imap_server_settings">Advanced IMAP Server
+ Settings</a></li>
+ <li><a href="#pop_server_settings">POP Server Settings</a></li>
+ <li><a href="#news_server_settings">News Server Settings</a></li>
+ </ul>
+</div>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="about_internet_message_access_protocol">About Internet Message Access
+ Protocol (IMAP)</h3>
+
+<p><strong>Advantages</strong>: Your messages and any changes to them stay on
+ your server, saving local disk space. Also, you always have access to an
+ updated mailbox, and you can get your mail from multiple locations.
+ Performance on a modem is faster, since you initially download message
+ headers only.</p>
+
+<p><strong>Disadvantages</strong>: Not all ISPs support IMAP.</p>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="about_post_office_protocol">About Post Office Protocol (POP)</h3>
+
+<p><strong>Advantages</strong>: Your messages are downloaded to your local
+ computer all at once, but you can also specify whether to keep copies of the
+ messages on the server and delete messages on the server when they are
+ deleted locally. Most ISPs currently support POP.</p>
+
+<p><strong>Disadvantages</strong>: If you use more than one computer, messages
+ might reside on one or the other, but not both. POP doesn&apos;t work as well
+ as IMAP over a slow link connection. Also, you can&apos;t access all mail
+ folders from multiple locations.</p>
+
+<p>Note that more recent POP servers have features that allow retrieving only
+ the headers instead of the full message, like IMAP allows. Using these
+ features allows performance with POP to be nearly as fast as with IMAP.</p>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="imap_server_settings">IMAP Server Settings</h3>
+
+<p>If you are not already viewing the IMAP server settings, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account name and click the Server Settings category. (If you
+ chose an IMAP server when you set up this account, you see your IMAP server
+ settings.)</li>
+</ol>
+
+<ul>
+ <li><strong>Server Type</strong>: The server type (IMAP Mail Server) that you
+ specified when you created this account. To change the server type
+ associated with this account, you must delete the account and then
+ re-create it.</li>
+ <li><strong>Server Name</strong>: The server name that you specified when you
+ created this account. If you are having problems getting mail from this
+ account, verify with your service provider or system administrator that the
+ server name you entered is correct.</li>
+ <li><strong>User Name</strong>: The user name that you specified when you
+ created this account.</li>
+ <li><strong>Port</strong>: Unless otherwise instructed to do so by your
+ service provider or system administrator, leave this setting
+ unchanged.</li>
+ <li><strong>Connection security</strong>: Choose one of the available options
+ to establish a <a href="glossary.xhtml#secure_connection">secure
+ connection</a> to your incoming IMAP server. You can choose one of these:
+ <ul>
+ <li><strong>None</strong>: &brandShortName; will use a plain connection,
+ without encryption at all. You should choose this <em>only</em> if your
+ incoming server allows password encryption or doesn&apos;t support any
+ type of security.</li>
+ <li><strong>STARTTLS</strong>: Require an encrypted connection, use the
+ <a href="glossary.xhtml#starttls">STARTTLS</a> method. This mechanism
+ will usually run on the standard IMAP port 143.</li>
+ <li><strong>SSL/TLS</strong>: Require an encrypted connection, use the
+ IMAP-over-SSL method. The default port for this is 993.
+ </li>
+ </ul>
+ </li>
+ <li><strong>Authentication method</strong>: Choose one of the available
+ options to use <a href="glossary.xhtml#secure_authentication">secure
+ authentication</a> with your incoming IMAP server. You can choose one of
+ these:
+ <ul>
+ <li><strong>Normal password</strong>: &brandShortName; will send your
+ password as clear text, without encryption at all. This option is
+ safe when SSL/TLS or STARTTLS is used.</li>
+ <li><strong>Password, transmitted insecurely</strong>: Same as
+ <q>Normal password</q> but only available when a connection security
+ of <q>None</q> is selected and hence is unsafe. Do <em>not</em> choose
+ this unless your incoming server doesn&apos;t support any type of
+ security at all.</li>
+ <li><strong>Encrypted password</strong>: Require the encryption of the
+ user&apos;s credentials as supported by the server, such as
+ <a href="glossary.xhtml#cram_md5">CRAM-MD5</a>. This option is safe
+ to use even if the connection security setting is <q>None</q>, but
+ only the password would be secured in this way, not any content.</li>
+ <li><strong>Kerberos / GSSAPI</strong>: Choose this option if your
+ computer is set up for secure authentication using
+ <a href="glossary.xhtml#kerberos">Kerberos</a>. You may need to
+ acquire a Kerberos ticket by using a separate program, or it may be
+ assigned to you when logging into your computer.</li>
+ <li><strong>NTLM</strong>: Choose this option if your computer is set up
+ for secure authentication using an <a href="glossary.xhtml#ntlm">NT
+ LAN Manager</a>. In general, Kerberos should be preferred over NTLM as
+ it provides for a higher level of security.</li>
+ <li><strong>TLS Certificate</strong>: Choose this option to use
+ <a href="glossary.xhtml#certificate-based_authentication">certificate-based
+ authentication</a> on a connection with SSL/TLS or STARTTLS enabled,
+ without the need to provide any password for authentication.</li>
+ </ul>
+ If you are unsure which options are supported by your server, contact your
+ service provider or system administrator.
+ </li>
+ <li><strong>Check for new messages at startup</strong>: Choose this setting
+ if you want Mail &amp; Newsgroups to automatically check this account for
+ new messages whenever you start Mail &amp; Newsgroups.</li>
+ <li><strong>Check for new messages every [__] minutes</strong>: Choose this
+ setting to automatically check for new messages, and then specify the
+ number of minutes between mail checks. If you do not select this setting,
+ you can check for new messages at any time by clicking Get Msgs in the Mail
+ window.</li>
+ <li><strong>Allow immediate server notifications when new messages
+ arrive</strong>: Choose this setting if the server supports IMAP&apos;s
+ <q>IDLE</q> capability to immediately notify &brandShortName; when new
+ mail arrives. This option does not have any effect for servers which
+ don&apos;t support this capability. It can be used in addition to, or
+ instead of, any of the other options to check for mail.</li>
+ <li id="when_i_delete_a_message"><strong>When I delete a message</strong>:
+ Choose the behavior you want for deleted messages. <q>Move it to this
+ folder</q>, where you can choose the specific folder to use, is recommended
+ unless you are instructed to use a different setting by your system
+ administrator or service provider. Messages marked as deleted are removed
+ only when you compact folders.</li>
+ <li><strong>Clean up (Expunge) Inbox on Exit</strong>: Removes deleted
+ messages from the Inbox when you exit Mail &amp; Newsgroups. Choose this
+ if you chose to mark messages as deleted.</li>
+ <li><strong>Empty Trash on Exit</strong>: Empties the Trash folder whenever
+ you quit Mail &amp; Newsgroups.</li>
+ <li><strong>Advanced</strong>: Lets you choose a different outgoing server
+ (SMTP) for outgoing messages from this account. You can also reach the
+ <a href="#advanced_imap_server_settings">Advanced IMAP Server Settings</a>
+ through this button.</li>
+ <li><strong>Local directory</strong>: The location on your hard disk where
+ mail for this account is stored.</li>
+</ul>
+
+<h3 id="advanced_imap_server_settings">Advanced IMAP Server Settings</h3>
+
+<p>In most cases, advanced IMAP server settings are automatically supplied by
+ the server. If you are unsure about the settings for this dialog box,
+ contact your ISP or system administrator.</p>
+
+<p>If you are not already viewing the advanced IMAP server settings, begin
+ from the Mail window.</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account name and click the Server Settings category.</li>
+ <li>If the mail server type is an IMAP server, you can click Advanced to set
+ additional IMAP options, such as:
+ <ul>
+ <li>the IMAP server directory path</li>
+ <li>showing only <q>subscribed folders</q></li>
+ <li>support for subfolders</li>
+ <li>the maximum number of concurrent connections kept open to the server
+ for this account</li>
+ <li>any personal and public (shared folder) namespaces for this
+ directory</li>
+ </ul>
+ </li>
+</ol>
+
+<p>For more information, see
+ <a href="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories">Adding
+ and Removing LDAP Directories</a>.</p>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="pop_server_settings">POP Server Settings</h3>
+
+<p>If you are not already viewing the POP server settings, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account name and click the Server Settings category name. (If
+ you chose a POP server when you set up this account, you see your POP
+ server settings.)</li>
+</ol>
+
+<ul>
+ <li><strong>Server Type</strong>: The server type (POP Mail Server) that you
+ specified when you created this account. To change the server type
+ associated with this account, you must delete the account and then
+ re-create it.</li>
+ <li><strong>Server Name</strong>: The server name that you specified when you
+ created this account. If you are having problems getting mail from this
+ account, verify with your service provider or system administrator that the
+ server name you entered is correct.</li>
+ <li><strong>User Name</strong>: The user name that you specified when you
+ created this account.</li>
+ <li><strong>Port</strong>: Unless otherwise instructed to do so by your
+ service provider or system administrator, leave this setting
+ unchanged.</li>
+ <li><strong>Connection security</strong>: Choose one of the available options
+ to establish a <a href="glossary.xhtml#secure_connection">secure
+ connection</a> to your incoming POP server. You can choose one of these:
+ <ul>
+ <li><strong>None</strong>: &brandShortName; will use a plain connection,
+ without encryption at all. You should choose this <em>only</em> if your
+ incoming server allows password encryption or doesn&apos;t support any
+ type of security.</li>
+ <li><strong>STARTTLS</strong>: Require an encrypted connection, use the
+ <a href="glossary.xhtml#starttls">STARTTLS</a> method. This mechanism
+ will usually run on the standard POP port 110.</li>
+ <li><strong>SSL/TLS</strong>: Require an encrypted connection, use the
+ POP-over-SSL method. The default port for this is 995.
+ </li>
+ </ul>
+ </li>
+ <li><strong>Authentication method</strong>: Choose one of the available
+ options to use <a href="glossary.xhtml#secure_authentication">secure
+ authentication</a> with your incoming POP server. You can choose one of
+ these:
+ <ul>
+ <li><strong>Normal password</strong>: &brandShortName; will send your
+ password as clear text, without encryption at all. This option is
+ safe when SSL/TLS or STARTTLS is used.</li>
+ <li><strong>Password, transmitted insecurely</strong>: Same as
+ <q>Normal password</q> but only available when a connection security
+ of <q>None</q> is selected and hence is unsafe. Do <em>not</em> choose
+ this unless your incoming server doesn&apos;t support any type of
+ security at all.</li>
+ <li><strong>Encrypted password</strong>: Require the encryption of the
+ user&apos;s credentials as supported by the server, such as
+ <a href="glossary.xhtml#cram_md5">CRAM-MD5</a> or APOP. This option is
+ safe to use even if the connection security setting is <q>None</q>, but
+ only the password would be secured in this way, not any content.</li>
+ <li><strong>Kerberos / GSSAPI</strong>: Choose this option if your
+ computer is set up for secure authentication using
+ <a href="glossary.xhtml#kerberos">Kerberos</a>. You may need to
+ acquire a Kerberos ticket by using a separate program, or it may be
+ assigned to you when logging into your computer.</li>
+ <li><strong>NTLM</strong>: Choose this option if your computer is set up
+ for secure authentication using an <a href="glossary.xhtml#ntlm">NT
+ LAN Manager</a>. In general, Kerberos should be preferred over NTLM as
+ it provides for a higher level of security.</li>
+ <li><strong>TLS Certificate</strong>: Choose this option to use
+ <a href="glossary.xhtml#certificate-based_authentication">certificate-based
+ authentication</a> on a connection with SSL/TLS or STARTTLS enabled,
+ without the need to provide any password for authentication.</li>
+ </ul>
+ If you are unsure which options are supported by your server, contact your
+ service provider or system administrator.
+ </li>
+ <li><strong>Check for new messages at startup</strong>: Choose this setting
+ if you want Mail &amp; Newsgroups to automatically check this account for
+ new messages whenever you start Mail &amp; Newsgroups. For POP accounts,
+ Mail &amp; Newsgroups doesn&apos;t download the new messages until you
+ click Get Msgs on the Mail toolbar.</li>
+ <li><strong>Check for new messages every [__] minutes</strong>: Choose this
+ setting to automatically check for new messages, and then specify the
+ number of minutes between mail checks. If you do not select this setting,
+ you can check for new messages at any time by clicking Get Msgs in the Mail
+ window.</li>
+ <li><strong>Automatically download any new messages</strong>: Choose this
+ setting if you want Mail &amp; Newsgroups to retrieve messages immediately
+ each time it checks the server.</li>
+ <li><strong>Fetch headers only</strong>: Choose this setting if you want to
+ only download the headers instead of entire messages when downloading new
+ mail. This option requires your POP server to support the <q>TOP</q>
+ command. Most recent POP servers support it, but if you are unsure about
+ your server, contact your service provider or system administrator.</li>
+ <li><strong>Leave messages on server</strong>: Choose this setting to store a
+ copy of messages on the mail server in addition to downloading them to your
+ computer.
+ <ul>
+ <li><strong>For at most [__] days</strong>: Choose this setting to remove
+ messages from the server automatically after the number of days you
+ enter here.</li>
+ <li><strong>Until I delete them</strong>: Choose this setting to remove
+ messages from the server once you delete them.</li>
+ </ul>
+ </li>
+ <li><strong>Empty Trash on Exit</strong>: Choose this setting to empty the
+ Trash folder whenever you quit Mail &amp; Newsgroups.</li>
+ <li><strong>Advanced</strong>: Lets you choose where new messages should be
+ put. You can also set the server to be queried when checking for new
+ messages.</li>
+ <li><strong>Local directory</strong>: The location on your hard disk where
+ mail for this account is stored.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="news_server_settings">News Server Settings</h3>
+
+<p>This section describes how to change news server settings. If you are not
+ already viewing news server settings, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account name and click the Server Settings category. (If you
+ chose a newsgroup server when you set up this account, you see your
+ newsgroup server settings.)</li>
+</ol>
+
+<ul>
+ <li><strong>Server Type</strong>: The server type (NNTP) that you specified
+ when you created this account.</li>
+ <li><strong>Server Name</strong>: The server name that you specified when you
+ created this account. If you are having problems receiving messages from
+ this account, verify with your service provider or system administrator
+ that the server name you entered is correct.</li>
+ <li><strong>Port</strong>: Unless otherwise instructed to do so by your
+ service provider or system administrator, leave this setting
+ unchanged.</li>
+ <li><strong>Connection security</strong>: Choose <q>SSL/TLS</q> if your
+ news server is configured to send and receive encrypted messages, or
+ <q>None</q> if it doesn&apos;t support it. If you are unsure, contact
+ your service provider or system administrator.</li>
+ <li><strong>Check for new messages at startup</strong>: Choose this setting
+ to automatically check for new messages when you first open the Mail &amp;
+ Newsgroup component of &brandShortName;.</li>
+ <li><strong>Check for new messages every [__] minutes</strong>: Choose this
+ setting to automatically check for new messages, and then specify the
+ number of minutes between mail checks. If you do not select this setting,
+ you can check for new messages at any time by clicking Get Msgs in the Mail
+ window.</li>
+ <li><strong>Ask me before downloading more than [__] messages</strong>:
+ Choose this setting to conserve disk space and download time, by setting a
+ limit for the number of messages you can retrieve at one time.</li>
+ <li><strong>Always request authentication when connecting to this
+ server</strong>: Some servers allow you to talk to them without logging in,
+ but will silently hide all the <em>private</em> groups/postings unless you
+ are logged in. Choose this setting to force &brandShortName; to
+ authenticate each time it connects to this server even when the server
+ doesn&apos;t ask (also called <q>Pushed Authentication</q>).</li>
+ <li><strong>newsrc file</strong>: The path to the newsrc file is mostly
+ displayed for your information. The newsrc file stores information about
+ the newsgroups to which you are subscribed and the messages you have read
+ in each newsgroup.</li>
+ <li><strong>Local directory</strong>: The location on your hard disk where
+ messages for this account are stored.</li>
+ <li><strong>Default Text Encoding</strong>: Click this drop-down list to
+ select the text encoding you want Mail &amp; Newsgroups to use as the
+ default for incoming newsgroup messages. This is recommended if it is
+ likely you might receive messages in which the text encoding (MIME charset
+ parameter) is not indicated, such as when reading messages in international
+ newsgroups.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="copies_and_folders">Mail &amp; Newsgroups Account Settings – Copies
+ &amp; Folders</h2>
+
+<p>This section describes the settings for sending automatic copies, for
+ storing copies of outgoing messages, for storing draft messages and message
+ templates, and where to move archived messages.</p>
+
+<p>By default, &brandShortName; Mail &amp; Newsgroups stores copies of your
+ outgoing messages in the Sent folder for the current account.
+ &brandShortName; Mail &amp; Newsgroups also stores draft messages in the
+ Drafts folder, message templates in the Templates folder, and moves archived
+ messages into the Archives folder for the current account.</p>
+
+<p>If you are not already viewing the settings for Copies &amp; Folders, begin
+ from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account, and click Copies &amp; Folders. You see the Copies
+ &amp; Folders panel.</li>
+</ol>
+
+<ul>
+ <li><strong>Place a copy in</strong>: Select this option to store copies of
+ your outgoing mail and newsgroup messages after they have been sent. By
+ default, the copies are placed in the Sent folder of this account.
+ <ul>
+ <li><strong>&quot;Sent&quot; Folder on</strong>: Select the Sent folder
+ of an account or the Local Folders to place the copy in.</li>
+ <li><strong>Other Folder</strong>: Select any folder of any account or
+ the Local Folders to place the copy in.</li>
+ <li><strong>Place replies in the folder of the message replied
+ to</strong>: Select this option for a different handling of replies
+ in mail accounts. If the message sent is a reply to another message,
+ the copy is put into the folder of the original message rather than
+ following the selections made above.</li>
+ </ul>
+ </li>
+ <li><strong>Cc these email addresses</strong>: Select whether you want to
+ always send a carbon copy (cc) to another addressee, and enter the address.
+ If you want to always send a carbon copy to yourself, just add your address
+ to this list. Separate addresses with commas (,).</li>
+ <li><strong>Bcc these email addresses</strong>: Select whether you want to
+ always send a blind carbon copy (bcc) to another addressee, and enter the
+ address. If you want to always send a blind carbon copy to yourself, just
+ add your address to this list. Separate addresses with commas (,).</li>
+ <li><strong>Keep draft messages in</strong>: Select where to store draft
+ messages. If you don&apos;t want to use the default Drafts folder for the
+ current account, select the Drafts folder of a different account or the
+ Local Folders, or click Other Folder and then choose any account and folder
+ for storing drafts.</li>
+ <li><strong>Keep message archives in</strong>: Select where to move archived
+ messages to. If you don&apos;t want to use the default Archives folder for
+ the current account, select the Archives folder of a different account or
+ the Local Folders, or click Other Folder and then choose any account and
+ folder for archiving messages.</li>
+ <li><strong>Keep message templates in</strong>: Select where to store
+ message templates. If you don&apos;t want to use the default Templates
+ folder for the current account, select the Templates folder of a different
+ account or the Local Folders, or click Other Folder and then choose any
+ account and folder for storing templates.</li>
+ <li><strong>Show confirmation dialog when messages are saved</strong>: Choose
+ this option if you want Mail &amp; Newsgroups to display a confirmation
+ dialog box when you save a draft message or a template. If checked, a
+ dialog box will appear when you save a draft or template to remind you
+ where &brandShortName; Mail &amp; Newsgroups is saving the draft or
+ template.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="addressing">Mail &amp; Newsgroups Account Settings – Composition &amp;
+ Addressing</h2>
+
+<p>You use Composition settings to choose how to format text, handle replies,
+ and how a signature you defined is included.</p>
+
+<p>If you are not already viewing the Composition settings, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account and click the Composition &amp; Addressing
+ category.</li>
+</ol>
+
+<ul>
+ <li><strong>Compose messages in HTML format</strong>: Use the HTML editor as
+ the default editor for writing mail and newsgroup messages. Leave this item
+ unchecked to use the plain-text editor by default. HTML messages can
+ include formatted text, links, images, and tables, just like a web page.
+ However, some recipients may not be able to receive HTML messages.
+
+ <p><strong>Tip</strong>: If you only want to use an editor occasionally,
+ you can hold down the Shift key while clicking the Compose or the Reply
+ button to switch to the non-default on an as-needed basis.</p>
+ </li>
+ <li><strong>Automatically quote the original message when replying</strong>:
+ Select this to include the original message text in your reply. Use the
+ drop-down list to select if the cursor should be positioned below or above
+ the quoted text. You can also choose the quoting to be automatically
+ selected.
+ <ul>
+ <li><strong>and place my signature</strong>: This drop-down list lets you
+ choose where you want your signature to be placed. It&apos;s only
+ applicable if you decided to <a href="#account_settings">attach a
+ signature</a> and to place the cursor above the quoted text.</li>
+ </ul>
+ </li>
+</ul>
+
+<ul>
+ <li><strong>Include signature for replies</strong>: If you have created a
+ signature, select this option to include it in your reply to a message.
+ The signature is added according to your settings for quote and signature
+ placement.</li>
+ <li><strong>Include signature for forwards</strong>: If you have created a
+ signature, select this option to include it when you forward a message.
+ The signature is placed according to your reply settings when
+ <a href="mailnews_preferences.xhtml#composition">forwarding inline</a>.</li>
+</ul>
+
+<p>You use Addressing settings to override the global LDAP server settings
+ specified for all <a
+ href="mailnews_preferences.xhtml#addressing_preferences">address books</a> in
+ the Preferences dialog box. LDAP server settings affect the behavior of
+ <a href="mailnews_preferences.xhtml#address_autocompletion">address
+ autocompletion</a>, and you can change these settings for each account if
+ necessary.</p>
+
+<p>Address autocompletion uses your address books to find matching entries when
+ you type email addresses in the addressing area of the Compose window.</p>
+
+<p>If you are not already viewing the Addressing settings, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the account and click the Composition &amp; Addressing
+ category.</li>
+</ol>
+
+<ul>
+ <li><strong>Automatically append my domain to addresses</strong>: Select
+ this if you want Mail &amp; Newsgroups to automatically complete
+ addresses you type with the domain from your account&apos;s address.</li>
+ <li><strong>Use my global LDAP server preferences for this account</strong>:
+ This is the default. Select this if you don&apos;t want to override the
+ global LDAP server preferences for this account.</li>
+ <li><strong>Use a different LDAP server</strong>: Select this option and then
+ choose another LDAP server from the list if you want to use a different
+ LDAP directory server for address autocompletion with this account. If
+ necessary, click Edit Directories to edit individual directory server
+ settings, add a directory server, or delete a directory server. For more
+ information, see <a
+ href="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories">Adding
+ and Removing LDAP Directories</a>.</li>
+</ul>
+
+<p>The directory you select will also be searched for matching certificates
+ when you attempt to send an encrypted message to one or more recipients for
+ whom you don&apos;t have certificates on file.</p>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="synchronization_and_storage">Mail &amp; Newsgroups Account Settings -
+ Synchronization &amp; Storage</h2>
+
+<p>Synchronization &amp; Storage settings let you conserve disk space or set
+ up an account so that you can use it while offline (disconnected from the
+ Internet). The settings available depend on the mail server type (IMAP, POP,
+ or News) associated with the account.</p>
+
+<div class="contentsBox">
+ <ul>
+ <li><a href="#synchronization_and_storage_settings_imap">Synchronization
+ &amp; Storage Settings (IMAP)</a></li>
+ <li><a href="#disk_space_settings_pop">Disk Space Settings (POP)</a></li>
+ <li><a href="#disk_space_settings_blogs">Disk Space Settings
+ (Blogs)</a></li>
+ <li><a href="#synchronization_and_storage_settings_nntp">Synchronization
+ &amp; Storage Settings (News)</a></li>
+ <li><a href="#retention_policy">Common Retention Policy Settings</a></li>
+ </ul>
+</div>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="synchronization_and_storage_settings_imap">Synchronization &amp;
+ Storage Settings (IMAP)</h3>
+
+<p>If you are not already viewing the synchronization and storage preferences
+ for an IMAP account, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Choose the Synchronization &amp; Storage category for an IMAP
+ account.</li>
+</ol>
+
+<ul>
+ <li><strong>Keep messages for this account on this computer</strong>: Select
+ this option so that messages in your folders will be available when you
+ are working offline. This setting also applies to any new folders
+ created or subscribed to.</li>
+ <li><strong>Advanced</strong>: Click to open a dialog to select the
+ folders that you want to make available for offline use. See <a
+ href="mailnews_offline.xhtml#selecting_items_for_offline_viewing">Selecting
+ Items for Offline Viewing</a> for more information.
+
+ <p><strong>Note</strong>: While the default setting can be overridden for
+ an individual folder, those per-folder settings are <em>removed</em>
+ whenever the <q>Keep messages for this account</q> box is toggled.</p>
+ </li>
+ <li><strong>Synchronize all messages locally regardless of age</strong>:
+ When synchronization is enabled for an account or a folder, <em>all</em>
+ messages are downloaded and local copies of them kept on disk, unless
+ a size limit is specified.</li>
+ <li><strong>Synchronize the most recent [__] [days]</strong>: Only copies of
+ messages younger than the specified number of days (weeks, months, years)
+ are kept locally for synchronization, after that they are removed from the
+ offline storage. This does <em>not</em> affect the originals on the server,
+ only the local copies are removed if the given age is reached.</li>
+ <li><strong>Don't download messages larger than [__] KB</strong>: Select this
+ option to conserve disk space by preventing large messages from being
+ downloaded. Enter the maximum size for downloaded messages. Changing
+ this option does <em>not</em> affect messages that have already been
+ downloaded.</li>
+ <li>The <a href="#retention_policy">retention settings</a> can be used to
+ free up space by deleting old messages. Note that these settings apply
+ to <em>both</em> local copies and their originals on the server.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="disk_space_settings_pop">Disk Space Settings (POP)</h3>
+
+<p>Messages from POP accounts are fully downloaded to your local machine unless
+ you have enabled the <q>Fetch headers only</q> setting. This section
+ describes how you can save disk space for a POP account. If your account has
+ the <q>Fetch headers only</q> setting enabled, then these Disk Space
+ preferences are ignored. If you are not already viewing the Disk Space
+ preferences for a POP account, follow these steps:</p>
+
+<p>Begin from the Mail window.</p>
+
+<ol>
+ <li>Open the Edit menu, choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click the Disk Space category for a POP account.</li>
+</ol>
+
+<ul>
+ <li><strong>Messages larger than [__] KB</strong>: Select this option to
+ conserve disk space by preventing large messages from being downloaded.
+ Enter the maximum size for downloaded messages.</li>
+ <li>The <a href="#retention_policy">retention settings</a> can be used to
+ free up space by deleting old messages. Note that these settings apply
+ to <em>both</em> local copies and their originals on the server.
+
+ <p><strong>Note</strong>: If your POP account is set up to use a Global
+ Inbox, the retention period settings of the target Inbox apply.</p>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="disk_space_settings_blogs">Disk Space Settings (Blogs)</h3>
+
+<p>Messages from blogs &amp; news feeds accounts are only stored in your local
+ machine in their short form, ie. the article summary, regardless of whether
+ your settings are to show the full articles by default. Still, there are
+ options to control how much disk space is used by your blogs &amp; news feeds
+ account. If you are not already viewing the Disk Space preferences for a
+ blogs &amp; news feeds account, follow these steps:</p>
+
+<p>Begin from the Mail window.</p>
+
+<ol>
+ <li>Open the Edit menu, choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click the Disk Space category for a blogs &amp; news feeds account.</li>
+</ol>
+
+<p>There, you can specify which messages should be deleted to recover disk
+ space:</p>
+
+<ul>
+ <li><strong>Don&apos;t delete any messages</strong>: Select this option to
+ keep all messages forever. Keep in mind that, if you are subscribed to very
+ high-traffic blogs, this will increase the occupied disk space steadily and
+ could eventually fill up your hard disk.</li>
+ <li><strong>Delete all but the most recent [____] messages</strong>: Select this
+ option to keep in each feed only a maximum number of messages. Enter the
+ maximum number of messages (being 1,000 by default).</li>
+ <li><strong>Delete messages more than [__] days old</strong>: Select this
+ option to keep in each feed only messages that are not older than the number
+ of days you enter here (being 30 days by default).</li>
+ <li><strong>Always keep flagged messages</strong>: Check this option to
+ save (not delete) flagged messages, regardless of its age.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="synchronization_and_storage_settings_nntp">Synchronization &amp;
+ Storage Settings (News)</h3>
+
+<p>If you are not already viewing the offline and disk space settings for a
+ News account, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu, and choose Mail &amp; Newsgroups Account Settings.
+ You see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Choose the Synchronization &amp; Storage category for a News account.</li>
+</ol>
+
+<ul>
+ <li><strong>Select newsgroups for offline use</strong>: Click to select the
+ newsgroups that you want to make available for offline use. See <a
+ href="mailnews_offline.xhtml#selecting_items_for_offline_viewing">Selecting
+ Items for Offline Viewing</a> for more information.</li>
+</ul>
+
+<p>The following settings help to save disk space and download time. Specify
+ which messages you don&apos;t want to download locally:</p>
+
+<ul>
+ <li><strong>Read messages</strong>: Select this option to only download
+ message bodies from messages you haven&apos;t already read.</li>
+ <li><strong>Messages larger than [__] KB</strong>: Select this option to
+ conserve disk space by preventing large messages from being downloaded.
+ Enter the maximum size for downloaded messages.</li>
+ <li><strong>Messages more than [__] days old</strong>: Select this option to
+ only download messages that are not older than the number of days you enter
+ here.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h3 id="retention_policy">Common Retention Policy Settings</h3>
+
+<p>&brandShortName; can automatically delete old messages for you. You
+ can configure this process with the options listed below
+ <strong>To recover disk space, old messages can be permanently
+ deleted</strong>:</p>
+
+<ul>
+ <li><strong>Don't delete any messages</strong>: Keep all messages. Never
+ delete messages automatically based on their age.</li>
+ <li><strong>Delete all but the most recent [__] messages</strong>: Enter the
+ number of messages to keep. With this setting only messages older than these
+ messages are deleted.</li>
+ <li><strong>Delete messages more than [__] days old</strong>:
+ Keep all messages that arrived within the given number of days.</li>
+</ul>
+
+<p>With the following settings you can further constrain the three options to
+ delete messages automatically. This is especially useful in combination with
+ the option to keep all messages.</p>
+
+<ul>
+ <li><strong>Always keep flagged messages</strong>: Use this option to deny
+ &brandShortName; to delete any messages you have flagged.</li>
+ <li><strong>Remove bodies from message more than [__] days old</strong>:
+ Select this option to retain all headers but to delete message bodies that
+ are older than the number of days you specify here (news accounts only).
+ Any option to delete the entire message based on age still applies.</li>
+</ul>
+
+<p>This policy can be overridden for an individual folder in the Folder
+ Properties, Retention Policy tab.</p>
+
+<p><strong>Note:</strong> If message synchronization is enabled (for IMAP), or
+ messages are left on the server (for POP accounts), the settings apply to
+ <em>both</em> local copies and their originals on the server.</p>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="junk_settings">Mail &amp; Newsgroups Account Settings – Junk
+ Settings</h2>
+
+<p>This section describes how to use the account junk settings. If
+ you are not currently viewing the Junk Settings, follow these steps:</p>
+
+<ol>
+ <li>Open the Edit menu, and choose Mail &amp; Newsgroups Account Settings.
+ You see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click the Junk Settings category for your mail account.</li>
+</ol>
+
+<p>You use the Junk Settings panel to define your account-specific settings
+ for the adaptive mail filter. Global junk settings are changed under
+ <a href="mailnews_preferences.xhtml#junk_and_suspect_preferences">Mail &amp;
+ Newsgroups Preferences – Junk &amp; Suspect Mail</a>.</p>
+
+<ul>
+ <li><strong>Enable adaptive junk mail controls for this account</strong>:
+ Toggle this option to activate or deactivate junk mail classification.</li>
+ <li><strong>Do not mark mail as junk if the sender is in [the address
+ book chosen from all your address books available in the drop down
+ box]</strong>:
+ Choose this option to prevent messages from people you know inadvertently
+ classified as junk mail.</li>
+ <li><strong>Trust junk mail headers set by [an external junk filter
+ like Spam Assassin or Spam Pal]</strong>: Choose this option if you want to
+ trust the junk classification of external filter programs.</li>
+ <li><strong>Move new junk messages to</strong>:
+ Check this option to automatically move messages flagged as Junk to a
+ special folder.
+ <ul>
+ <li><strong><q>Junk</q> folder on [account]</strong>: Select this to use
+ the default Junk folder.</li>
+ <li><strong>Other: [account]</strong>: Select this to choose your own
+ custom-named junk folder.</li>
+ <li><strong>Automatically delete junk messages older than [__] days from
+ this folder</strong>: If you are confident old messages classified as
+ junk are indeed junk mail, check this option to automatically delete
+ old junk messages after a grace period.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="return_receipts">Mail &amp; Newsgroups Account Settings – Return
+ Receipts</h2>
+
+<p>This section describes how to use the Return Receipts account settings. If
+ you are not currently viewing the Return Receipts settings, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the Edit menu, and choose Mail &amp; Newsgroups Account Settings.
+ You see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click the Return Receipts category for your mail account.</li>
+</ol>
+
+<p>You use the Return Receipts settings to define return receipt settings for
+ outgoing messages from this mail account. You also use the Return Receipt
+ settings to specify how to manage requests you receive for return receipts.
+ These settings override global return receipt preferences you specified using
+ <a href="mailnews_preferences.xhtml#return_receipts_preferences">Mail &amp;
+ Newsgroups Preferences – Return Receipts</a>.</p>
+
+<ul>
+ <li><strong>Use my global return receipt preferences for this
+ account</strong>: By default, this account uses the return receipt
+ preferences specified by <a
+ href="mailnews_preferences.xhtml#return_receipts_preferences">Mail &amp;
+ Newsgroups Preferences – Return Receipts</a>.</li>
+ <li><strong>Customize return receipts for this account</strong>: Lets you
+ change the return receipt preferences for this account.
+ <ul>
+ <li><strong>When sending messages, always request a return
+ receipt</strong>: Enables automatic return receipt requests for all
+ outgoing messages from this mail account.</li>
+ <li><strong>Leave it in my Inbox</strong>: Return receipt
+ confirmation messages are delivered to the Inbox for this account.
+
+ <p><strong>Tip</strong>: Choose this option if you want to use a
+ filter that automatically moves return receipt confirmation
+ messages to a folder you specify. For information on creating and
+ using filters, see <a
+ href="mailnews_organizing.xhtml#creating_message_filters">Creating
+ Message Filters</a>.</p>
+ </li>
+ <li><strong>Move it to my Sent Mail folder</strong>: Incoming return
+ receipt confirmation messages are moved to the Sent mail folder for
+ this account.</li>
+ <li><strong>Never send a return receipt</strong>: Choose this option if
+ you do not want to send a return receipt in response to requests for
+ return receipts from others.</li>
+ <li><strong>Allow return receipts for some messages</strong>: Choose how
+ you want to respond to requests you receive for return receipts.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="security">Mail &amp; Newsgroups Account Settings – Security</h2>
+
+<p>This section describes how to configure the Mail &amp; Newsgroups Account
+ Settings that control mail message security. Before you do so, however, you
+ must obtain one or more mail certificates. For details, see
+ <a href="mailnews_security.xhtml">Signing &amp; Encrypting
+ Messages</a>.</p>
+
+<p>If you are not already viewing the Security settings for your mail account,
+ begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account
+ Settings.</li>
+ <li>Click Security under the name of the mail account whose security settings
+ you want to configure.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#about_certificates">About Certificates</a></li>
+ <li><a href="#digital_signing">Digital Signing</a></li>
+ <li><a href="#encryption">Encryption</a></li>
+ </ul>
+</div>
+
+<h3 id="about_certificates">About Certificates</h3>
+
+<p>The main purpose of the Security panel in Mail &amp; Newsgroups Account
+ Settings is to select two certificates:</p>
+
+<ul>
+ <li>The email certificate you want to use for signing mail messages you send
+ to other people.</li>
+ <li>The email certificate you want other people to use when they encrypt
+ messages they send to you.</li>
+</ul>
+
+<p>Depending on the policies of the
+ <a href="glossary.xhtml#certificate_authority">certificate authority (CA)</a>
+ that issues your certificate(s), you can use one certificate for both
+ purposes or two different certificates. Even if you use just one, you must
+ specify it twice, once for digital signing and once for encryption.</p>
+
+<p>The certificates you select here are included with every signed message you
+ send. These certificates allow your recipients to verify your digital
+ signature and to encrypt messages that they send to you.</p>
+
+<h3 id="digital_signing">Digital Signing</h3>
+
+<p>You use the Digital Signing area in the <a href="#security">Security
+ panel</a> to specify how you want to sign your email messages:</p>
+
+<ul>
+ <li><strong>Use this certificate to digitally sign messages you
+ send</strong>: If this field is empty or if it displays the wrong
+ certificate, click Select to choose from the certificates you have on
+ file.</li>
+ <li><strong>Digitally sign messages</strong>: Select this checkbox if you
+ want to digitally sign all the messages you send. (A personal certificate
+ must be specified below before you can select this checkbox.)</li>
+</ul>
+
+<p>Regardless of whether the <q>Digitally sign messages</q> checkbox is
+ selected here, you can change your mind before you send an individual
+ message.</p>
+
+<p>To change the digital signature setting for a message you are writing in
+ the Compose window, click the arrow below the Security icon near the top of
+ the window and select or deselect <q>Digital Sign This Message</q>. For
+ details, see <a
+ href="mailnews_security.xhtml#signing_and_encrypting_a_new_message">Signing
+ &amp; Encrypting a New Message.</a></p>
+
+<h3 id="encryption">Encryption</h3>
+
+<p>You use the Encryption area in the <a href="#security">Security panel</a> to
+ specify how you routinely want to use encryption when sending your
+ messages:</p>
+
+<ul>
+ <li><strong>Use this certificate to encrypt &amp; decrypt messages sent to
+ you</strong>: If this field is empty or if it displays the wrong
+ certificate, click Select to choose from the certificates you have on
+ file.</li>
+ <li><strong>Never</strong>: Select this option if you never want to use
+ encryption, or only occasionally.</li>
+ <li><strong>Required</strong>: Select this option if you always want to use
+ encryption. If you don&apos;t have all the necessary certificates, the
+ message won&apos;t be sent unless you explicitly turn off encryption for
+ that message only.</li>
+</ul>
+
+<p>Regardless of which encryption option you select, you can change your mind
+ before you send an individual message.</p>
+
+<p>To change the encryption setting for a message you are writing in the
+ Compose window, click the arrow below the Security icon near the top of the
+ window and choose the encryption setting you want. For details, see <a
+ href="mailnews_security.xhtml#signing_and_encrypting_a_new_message">Signing
+ &amp; Encrypting a New Message.</a></p>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+
+<h2 id="local_folders">Mail &amp; Newsgroups Account Settings – Local
+ Folders</h2>
+
+<p>Local Folders is the account where &brandShortName; Mail &amp; Newsgroups
+ saves any messages that you send while working offline. Messages you send
+ while working offline are saved in the Unsent Messages folder under Local
+ Folders. Any folders you create under the Local Folders account reside on
+ your hard disk, so Local Folders is a good place to save messages that you
+ want to keep.</p>
+
+<p>If you are not already viewing the Local Folders settings, begin from the
+ Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the Local Folders category.</li>
+</ol>
+
+<ul>
+ <li><strong>Account Name</strong>: The name associated with the Local Folders
+ account.</li>
+ <li><strong>Local directory</strong>: The location on your hard disk where
+ mail for this account is stored.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning
+ of section</a>]</p>
+
+<h2 id="outgoing_server">Mail &amp; Newsgroups Account Settings – Outgoing
+ Server (SMTP)</h2>
+
+<p>The outgoing server will transport your outgoing mail to the intended
+ recipients.</p>
+
+<p>If you are not already viewing the Outgoing Server (SMTP) settings, begin
+ from the Mail window:</p>
+
+<ol>
+ <li>Click on any Mail window.</li>
+ <li>From the Edit menu, choose Mail &amp; Newsgroups Account Settings.</li>
+ <li>Select Outgoing Server (SMTP) and either edit an existing server or
+ add a new one. If you are not sure which option to choose, check with
+ your ISP or system administrator)<br/>
+ You can choose from these servers via the Outgoing Server dropdown in
+ the <a href="#account_settings">Identity Settings</a>.</li>
+</ol>
+
+<ul>
+ <li><strong>Description</strong>: A short freetext description of that server
+ configuration. This will show up as first part in the server list.</li>
+ <li><strong>Server name</strong>: The SMTP server that will deliver your
+ outgoing mail. To use a different SMTP server, change this field.</li>
+ <li><strong>Port</strong>: The port on which the SMTP server will be
+ connected. By default it holds the standard port for the specified
+ encryption. Change it if the mail server is listening for connections
+ on a non-standard port.</li>
+ <li><strong>Connection security</strong>: Choose one of the available options
+ to establish a <a href="glossary.xhtml#secure_connection">secure
+ connection</a> to your outgoing SMTP server. You can choose one of these:
+ <ul>
+ <li><strong>None</strong>: &brandShortName; will use a plain connection,
+ without encryption at all. You should choose this <em>only</em> if your
+ outgoing server allows password encryption, doesn&apos;t support any
+ type of security at all, or if no authentication is required to send
+ messages.</li>
+ <li><strong>STARTTLS</strong>: Require an encrypted connection, use the
+ <a href="glossary.xhtml#starttls">STARTTLS</a> method. This mechanism
+ will usually run on the standard SMTP-submission port 587 or the
+ generic port 25.</li>
+ <li><strong>SSL/TLS</strong>: Require an encrypted connection, use the
+ SMTP-over-SSL (also known as SMTPS) method. The default port for this
+ is 465.</li>
+ </ul>
+ If you make a choice for which your server is not configured, you will
+ get an error message when sending mail.
+ </li>
+ <li><strong>Authentication method</strong>: Choose one of the available
+ options to use <a href="glossary.xhtml#secure_authentication">secure
+ authentication</a> with your incoming SMTP server. You can choose one of
+ these:
+ <ul>
+ <li><strong>No authentication</strong>: Neither a user name nor a
+ password will be sent to the server. This option may be chosen if
+ the SMTP server is in a local network or if other means are provided
+ to authenticate the user, such as <q>POP before SMTP</q>.</li>
+ <li><strong>Normal password</strong>: &brandShortName; will send your
+ password as clear text, without encryption at all. This option is
+ safe when SSL/TLS or STARTTLS is used.</li>
+ <li><strong>Password, transmitted insecurely</strong>: Same as
+ <q>Normal password</q> but only available when a connection security
+ of <q>None</q> is selected and hence is unsafe. Do <em>not</em> choose
+ this unless your outgoing server doesn&apos;t support any type of
+ security at all.</li>
+ <li><strong>Encrypted password</strong>: Require the encryption of the
+ user&apos;s credentials as supported by the server, such as
+ <a href="glossary.xhtml#cram_md5">CRAM-MD5</a>. This option is safe
+ to use even if the connection security setting is <q>None</q>, but
+ only the password would be secured in this way, not any content.</li>
+ <li><strong>Kerberos / GSSAPI</strong>: Choose this option if your
+ computer is set up for secure authentication using
+ <a href="glossary.xhtml#kerberos">Kerberos</a>. You may need to
+ acquire a Kerberos ticket by using a separate program, or it may be
+ assigned to you when logging into your computer.</li>
+ <li><strong>NTLM</strong>: Choose this option if your computer is set up
+ for secure authentication using an <a href="glossary.xhtml#ntlm">NT
+ LAN Manager</a>. In general, Kerberos should be preferred over NTLM as
+ it provides for a higher level of security.</li>
+ </ul>
+ If you are unsure which options are supported by your server, contact your
+ service provider or system administrator.
+ </li>
+ <li><strong>User Name</strong>: The user name that you specified when you
+ created this account. Not available if <q>No authentication</q> is
+ selected.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroups_account_settings">Return to beginning of
+ section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_addressbooks.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_addressbooks.xhtml
new file mode 100644
index 0000000000..456aad4d9a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_addressbooks.xhtml
@@ -0,0 +1,572 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Using Address Books</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+<h1 id="using_address_books">Using Address Books</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#about_address_books">About Address Books</a></li>
+ <li><a href="#adding_entries_to_your_address_books">Adding Entries to Your
+ Address Books</a></li>
+ <li><a href="#creating_a_new_address_book">Creating a New Address
+ Book</a></li>
+ <li><a href="#creating_a_new_address_book_card">Creating a New Address Book
+ Card</a></li>
+ <li><a href="#creating_a_mailing_list">Creating a Mailing List</a></li>
+ <li><a href="#editing_a_mailing_list">Editing a Mailing List</a></li>
+ <li><a href="#searching_address_books_and_directories">Searching Address
+ Books and Directories</a></li>
+ <li><a href="#importing_address_books">Importing Address Books</a></li>
+ <li><a href="#exporting_address_books">Exporting Address Books</a></li>
+ <li><a href="#adding_and_removing_ldap_directories">Adding and Removing
+ LDAP Directories</a></li>
+ </ul>
+</div>
+
+<h2 id="about_address_books">About Address Books</h2>
+
+<p>Address books store email addresses and contact information for people you
+ typically send mail to, such as colleagues, friends, and family.
+ &brandShortName; Mail &amp; Newsgroups provides you with two address books:
+ the Personal Address Book and the Collected Addresses&mdash;and you can
+ create additional address books as well. You can also import address books
+ from other mail programs and previous versions of &brandShortName;. The
+ contents of these address books are stored locally on your hard disk.</p>
+
+<p>Your address book may also list email addresses from an LDAP directory,
+ which is located on an LDAP directory server. The directory server stores
+ email addresses of people that are not included in your locally-stored
+ address books. The Lightweight Directory Access Protocol (LDAP) is an
+ industry-standard method for accessing Internet or intranet directory
+ services such as corporate address books.</p>
+
+<h4>Personal Address Book</h4>
+
+<p>Use the Personal Address Book to add specific names of your choice. You can
+ create mailing lists and edit individual address entries.</p>
+
+<h4>Collected Addresses</h4>
+
+<p>By default, the Collected Addresses automatically collects the email
+ addresses contained in outgoing mail messages. Addresses from outgoing
+ messages are stored in the Collected Addresses as soon as you click Send.</p>
+
+<h4>LDAP Directory (if available)</h4>
+
+<p>An LDAP directory (also known as an address lookup service) stores email
+ addresses of recipients who are not in your locally-stored address books.
+ LDAP directories offer you access to large, centrally maintained databases
+ of email addresses, which is especially useful with
+ <a href="mailnews_preferences.xhtml#address_autocompletion">address
+ autocompletion</a>.</p>
+
+<p>Automatic address collection is enabled by default. To change automatic
+ address collection settings, begin in the Mail window:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Addressing. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand the
+ list.)</li>
+ <li>Under Email Address Collection, select <q>Add email addresses to my</q>
+ and choose whether you want:
+ <ul>
+ <li>Personal Address Book.</li>
+ <li>Collected Addresses.</li>
+ </ul>
+ </li>
+ <li>Click OK.</li>
+</ol>
+
+<h4>Opening the Address Book Window</h4>
+
+<p>To open the Address Book window:</p>
+
+<ul>
+ <li>Open the Window menu and choose Address Book, or click the Address Book
+ icon in the lower-left corner of any &brandShortName; window.
+
+ <table>
+ <tr>
+ <td colspan="2"><img src="images/taskbar-ab.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 80px;"></td>
+ <td><strong>Address Book icon</strong></td>
+ </tr>
+ </table>
+ </li>
+</ul>
+
+<h4>Changing the Address Book Window Display</h4>
+
+<p>To customize how the Address Book window and the cards are displayed:</p>
+
+<ol>
+ <li>Open the Window menu and choose Address Book. You see the Address Book
+ window.</li>
+ <li>In the Address Book window, open the View menu and choose from the
+ following display options:
+ <ul>
+ <li>Choose Show/Hide, and then select the item you wish to uncheck (hide)
+ or check (show).</li>
+ <li>Choose Show Name As, and then select how you want card names
+ displayed (first/last, last/first, or Display Name).</li>
+ <li>Choose Sort by, and then select a sort option.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="adding_entries_to_your_address_books">Adding Entries to Your Address
+ Books</h2>
+
+<p>You can use any of the following ways to add entries to your address
+ books:</p>
+
+<ul>
+ <li>Click a name in the From or recipient fields (for example, To or Cc) in a
+ message you&apos;ve received, and then select <q>Add to Address Book</q>
+ from the drop-down list.</li>
+ <li>In the Address Book window, click New Card to create a new address book
+ card.</li>
+ <li>Send a message, which automatically adds the recipient&apos;s address
+ to your address book (if enabled).</li>
+ <li>In the Address Book window, copy entries to another address book by
+ selecting the entries and dragging them over the name of the address book
+ you want to copy them to.</li>
+</ul>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="creating_a_new_address_book">Creating a New Address Book</h2>
+
+<p>&brandShortName; Mail &amp; Newsgroups provides a default personal address
+ book, but you can create additional address books.</p>
+
+<p>To create a new address book:</p>
+
+<ol>
+ <li>Click the Address Book icon in the lower-left corner of any
+ &brandShortName; window, or open the Window menu and choose Address Book.
+ You see the Address Book window.
+
+ <table>
+ <tr>
+ <td colspan="2"><img src="images/taskbar-ab.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 80px;"></td>
+ <td><strong>Address Book icon</strong></td>
+ </tr>
+ </table>
+ </li>
+ <li>In the Address Book window, open the File menu, choose New, and choose
+ Address Book. You see the New Address Book dialog box.</li>
+ <li>Type the name of the new address book, and click OK.</li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="creating_a_new_address_book_card">Creating a New Address Book Card</h2>
+
+<p>Address book cards can be used to store names, postal addresses, email
+ addresses, phone numbers, and information such as whether the addressee
+ prefers to receive plain-text or HTML-formatted messages.</p>
+
+<p>To create an address book card for an individual:</p>
+
+<ol>
+ <li>Click the Address Book icon on the status bar or open the Window menu and
+ choose Address Book.</li>
+ <li>Click New Card. (If you have multiple address books, select the one to
+ which you want to add a card.)</li>
+ <li>Each New Card dialog box has three tabs:
+ <ul>
+ <li><strong>Contact</strong>: Enter the following information:
+ <ul>
+ <li>First and Last (first and last name of person as you want it to
+ appear in the address book).</li>
+ <li>Display name (the name that appears in the <q>To</q> field of the
+ Compose window).</li>
+ <li>Nickname (a shortcut or alias for the real name).</li>
+ <li>Email address (primary and additional address).</li>
+ <li>Prefers to receive messages formatted as: If you know this
+ recipient can read HTML-formatted messages (such as messages that
+ include links, images, or tables), choose HTML. If this recipient
+ can only read messages sent as plain text (no formatting), then
+ choose Plain Text. If you don&apos;t know or are not sure, choose
+ Unknown. If you choose Unknown, &brandShortName; Mail &amp;
+ Newsgroups determines the sending format based on the Mail &amp;
+ Newsgroups Send Format settings in the Preferences dialog box. If
+ Mail &amp; Newsgroups still can&apos;t determine the correct
+ format, Mail &amp; Newsgroups will prompt you to choose a sending
+ format when you send the message.</li>
+ <li>Allow remote images in HTML mail: If you want to allow this
+ sender to have remote content they send you displayed in your
+ message window.</li>
+ <li>Chat name (first filled-in entry in the Chat tab is displayed).
+ </li>
+ <li>Phones (enter phone numbers for this person)</li>
+ </ul>
+ </li>
+ <li><strong>Address</strong>: Type additional information such as street
+ address, phone number, and URL.
+
+ <p><strong>Tip</strong>: If you enter address information,
+ &brandShortName; displays a Get Map button next to the address when
+ you view this entry&apos;s address book card in your address book.
+ Clicking the Get Map button displays a web page that contains a map
+ to the address.</p>
+ </li>
+ <li><strong>Other</strong>: Store any additional information you
+ want.</li>
+ </ul>
+ </li>
+</ol>
+
+<p><strong>Tip</strong>: To quickly add entries to your address book, click any
+ email address in messages you receive and select Add to Address Book from the
+ drop-down list. The New Card dialog box appears where you can complete the
+ information.</p>
+
+<h3 id="viewing_or_editing_card_properties">Viewing or Editing Card
+ Properties</h3>
+
+<p>To view or edit the properties for an individual card:</p>
+
+<ol>
+ <li>Select the card from the list of entries in the Address Book window.</li>
+ <li>Click Properties.</li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="creating_a_mailing_list">Creating a Mailing List</h2>
+
+<p>If you regularly send messages to a group of recipients, you can quickly
+ address a message by using a mailing list that contains the names you
+ want.</p>
+
+<p>To create a mailing list and add it to your address book:</p>
+
+<ol>
+ <li>In the Address Book window, click New List.</li>
+ <li>Enter the following information in the Mailing List dialog box:
+ <ul>
+ <li>Click the drop-down list at <q>Add to</q> to choose an address book
+ in which to store the list.</li>
+ <li>List name: When you enter the list name in the <q>To</q> field of a
+ message, everyone on the list receives your message.</li>
+ <li>List nickname: Alias (or shortcut) for the list name.</li>
+ <li>Description: Appears after the list name in the address line of
+ the Compose window.</li>
+ </ul>
+ </li>
+ <li>Type email addresses to add them to the mailing list.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>In the left side of the Address Book window, the mailing list appears
+ underneath the address book you added it to.</p>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="editing_a_mailing_list">Editing a Mailing List</h2>
+
+<p>Mailing lists are stored in the address book in which you created them.</p>
+
+<p>To remove a member from the list, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Window menu and choose Address Book.</li>
+ <li>Expand the address book containing your mailing list by clicking the
+ small triangle beside the address book title.</li>
+ <li>Highlight the mailing list by clicking its name. The list members appear
+ to the right of the mailing list name.</li>
+ <li>Click the entry you wish to delete.</li>
+ <li>Click the Delete button.</li>
+</ol>
+
+<p>To add members to a mailing list:</p>
+
+<ol>
+ <li>Open Window menu and choose Address Book.</li>
+ <li>Expand the address book containing your mailing list by clicking the
+ small triangle beside the address book title.</li>
+ <li>Highlight the mailing list by clicking its name.</li>
+ <li>Click Properties.</li>
+ <li>Add or remove entries as necessary.</li>
+ <li>Click OK when you are done.</li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="searching_address_books_and_directories">Searching Address Books and
+ Directories</h2>
+
+<p>&brandShortName; Mail &amp; Newsgroups lets you quickly search an address
+ book or directory by name or email address, or use a combination of criteria
+ to perform a more specific search through an address book or directory.</p>
+
+<p>To quickly search an address book or directory for a name or email address,
+ begin from the Address Book window:</p>
+
+<ol>
+ <li>In the Address Book window, in the list of address books, select the
+ address book or directory that you want to search.</li>
+ <li>In the <q>Name or Email contains</q> field, type the name or email
+ address that you want to find. You can type only part of the name or email
+ address, or you can type the exact text that you want to find.
+
+ <p>As soon as you stop typing, &brandShortName; Mail &amp; Newsgroups
+ displays only those entries where the name or email address contains the
+ search text you entered.</p>
+ </li>
+ <li>Click Clear to erase the search text and show all entries.</li>
+</ol>
+
+<h3 id="searching_for_specific_entries">Searching for Specific Entries</h3>
+
+<p>You can search address books or directories for specific entries. If you are
+ not already viewing the Advanced Address Book Search dialog box, begin from
+ the Address Book window:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Search Addresses. You see the Advanced
+ Address Book Search dialog box.</li>
+ <li>Next to <q>Search in</q>, choose the address book or directory through
+ which you want to search.</li>
+ <li>Select the matching option Mail &amp; Newsgroups uses to search for
+ entries either that match all or at least one of the conditions (criteria)
+ that you choose.</li>
+ <li>Click More to add criteria and Fewer to remove them.</li>
+ <li>Click Search to begin, or click Clear to reset your entries. The search
+ results appear in lower part of the dialog box.</li>
+ <li>To sort the entries in a different order, click the column that you want
+ to sort by.</li>
+ <li>To view the card for an entry, select the entry and click
+ Properties.</li>
+ <li>To compose a message to selected recipients, select one or more entries
+ and click Compose.</li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="importing_address_books">Importing Address Books</h2>
+
+<p>If you have a &brandShortName; address book from another user profile or
+ computer, or if you have an address book from another mail program, you can
+ import its entries into the Address Book window as a new address book. Keep
+ in mind that when you upgrade a user profile from an earlier version of
+ &brandShortName;, your address books are automatically included, so
+ there&apos;s no need to import them.</p>
+
+<p>You can import address books from Netscape 6, Netscape 7, Outlook, or text
+ files (LDIF, tab-delimited (.tab), comma-separated (.csv), or text (.txt)
+ formats). When you import an address book, Mail &amp; Newsgroups creates a
+ new address book with the imported entries.</p>
+
+<p>You can also
+ <a href="mailnews_getting_started.xhtml#importing_mail_from_other_programs">import
+ mail messages and settings</a> from Communicator, and Outlook.</p>
+
+<p>To import an address book, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Tools menu, and choose Import. You see the Mail Import
+ Wizard.</li>
+ <li>Follow the instructions to import address books.</li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="exporting_address_books">Exporting Address Books</h2>
+
+<p>You can export a &brandShortName; address book if you later want to import
+ it into another user profile, move it to another computer, or use it with
+ another program that can import address books. You can export an address
+ book to one of these file formats: &brandShortName; (.ldif), tab-delimited
+ (.tab), comma-separated (.csv), or text (.txt) formats.</p>
+
+<p>To export an address book, begin from the Address Book window:</p>
+
+<ol>
+ <li>Select the address book that you want to export.</li>
+ <li>Open the Tools menu, and choose Export.</li>
+ <li>In the Export Address Book dialog box, browse to the location where you
+ want to save the address book file.</li>
+ <li>Choose the file format for the exported address book (.ldif,
+ comma-separated, or tab-delimited).</li>
+ <li>Enter a name for the address book file. Be sure to include the
+ appropriate file extension (.ldif, .csv, .tab, or .txt).</li>
+ <li>Click Save.</li>
+</ol>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+
+<h2 id="adding_and_removing_ldap_directories">Adding and Removing LDAP
+ Directories</h2>
+
+<p>Adding an LDAP directory to your address book allows you to search the
+ directory for email addresses and other contact information. You can also use
+ the directory for address autocompletion when addressing mail messages.</p>
+
+<p>You typically add or remove LDAP directories using instructions provided by
+ your system administrator. Check with your system administrator for the
+ information you will need in order to add a new directory to your address
+ book.</p>
+
+<p>To add a new directory, begin from the Address Book window:</p>
+
+<ol>
+ <li>Open the File menu, and choose New, and then choose LDAP Directory. You
+ see the Directory Server Properties dialog box.</li>
+ <li>Type the following information in the Directory Server Properties dialog
+ box General tab:
+ <ul>
+ <li><strong>Name</strong>: Enter the name of the directory service (for
+ example, InfoSpace Directory).</li>
+ <li><strong>Host Name</strong>: Enter the name of the host name server,
+ such as ldap.infospace.com.</li>
+ <li><strong>Base DN</strong>: This setting is used to set the Base
+ distinguished name. Enter codes to restrict searching to a specific
+ country or organization. For example, c=JP restricts the search to
+ Japan only. Base DN also specifies the organization to search on
+ within the directory (for instance, o=Netscape Communications
+ Corporation, c=US).</li>
+ <li><strong>Port Number</strong>: Enter the port number for the LDAP
+ server. The default is 389.</li>
+ <li><strong>Bind DN</strong>: The distinguished name that is used to
+ authenticate (log in) to the LDAP server. If left blank, the LDAP
+ server binds anonymously.</li>
+ <li><strong>Use secure connection (SSL)</strong>: Choose this setting
+ if your LDAP server supports secure (encrypted) connections. If you are
+ unsure, contact your system administrator.</li>
+ </ul>
+ </li>
+ <li>Click the Advanced tab to configure LDAP directory server settings.</li>
+ <li>Type the following information:
+ <ul>
+ <li><strong>Don&apos;t return more than _ results</strong>: This setting
+ lets you limit the number of autocompletion matches returned by the
+ directory server. Enter the maximum number of email address matches
+ to display for autocompletion.</li>
+ <li><strong>Scope</strong>: Defines the limits of the search. Choose one
+ of the following:
+ <ul>
+ <li><strong>One Level</strong>: Retrieves matching entries by
+ searching the base DN and one level below the base DN.</li>
+ <li><strong>Subtree</strong>: Retrieves matching entries by searching
+ the base DN in addition to all levels below the base DN. This is
+ the least restrictive search.</li>
+ </ul>
+ </li>
+ <li><strong>Search filter</strong>: Enter the search filter to apply to
+ matching results that are within the specified scope of the
+ search.</li>
+ </ul>
+ </li>
+ <li>Click OK to close the Directory Server Properties dialog box.</li>
+</ol>
+
+<p>The directory you added appears in the list of address books in the Address
+ Book window.</p>
+
+<p>To delete a directory:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Under the Mail &amp; Newsgroups category, select Addressing. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+ <li>Under Address Autocompletion on the right side of the dialog box, click
+ Edit Directories.</li>
+ <li>In the LDAP Directory Servers dialog box, select the directory that you
+ want to delete and click Delete.</li>
+ <li>Click OK, then click OK again to close the Preferences dialog box.</li>
+</ol>
+
+<p>For information on downloading or synchronizing a directory for offline use
+ so that you can search it or use it for address book autocompletion while
+ working offline, see
+ <a href="mailnews_offline.xhtml#downloading_directory_entries_for_offline_use">Downloading
+ Directory Entries for Offline Use</a>.</p>
+
+<h3 id="directory_server_settings">Directory Server Settings</h3>
+
+<p>If you are not already viewing the Directory Server Settings dialog box,
+ begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Window menu, and choose Address Book.</li>
+ <li>In the list of address books, select a directory.</li>
+ <li>Click Properties.</li>
+</ol>
+
+<p><strong>General Tab</strong></p>
+
+<ul>
+ <li><strong>Name</strong>: The name of the directory service (for example,
+ InfoSpace Directory).</li>
+ <li><strong>Host Name</strong>: The name of the host name server, such as
+ ldap.infospace.com.</li>
+ <li><strong>Base DN</strong>: The Base Distinguished Name. Codes entered here
+ restrict searching to a specific country or organization. For example, c=JP
+ restricts the search to Japan only. Base DN also specifies the organization
+ to search on within the directory (for instance, o=Netscape Communications
+ Corporation, c=US).</li>
+ <li><strong>Port Number</strong>: Enter the port number for the LDAP server.
+ The default is 389.</li>
+ <li><strong>Bind DN</strong>: The distinguished name that is used to
+ authenticate (log in) to the LDAP server. If left blank, the LDAP server
+ binds anonymously.</li>
+ <li><strong>Use secure connection (SSL)</strong>: Choose this setting if your
+ LDAP server supports secure (encrypted) connections. If you are unsure,
+ contact your system administrator.</li>
+</ul>
+
+<p><strong>Advanced Tab</strong></p>
+
+<ul>
+ <li><strong>Don&apos;t return more than _ results</strong>: This setting lets
+ you limit the number of autocompletion matches returned by the directory
+ server. Specify the maximum number of email address matches to display for
+ autocompletion.</li>
+ <li><strong>Scope</strong>: Defines the limits of the search:
+ <ul>
+ <li><strong>One Level</strong>: Retrieves matching entries by searching
+ the base DN and one level below the base DN.</li>
+ <li><strong>Subtree</strong>: Retrieves matching entries by searching the
+ base DN in addition to all levels below the base DN. This is the least
+ restrictive search.</li>
+ </ul>
+ </li>
+ <li><strong>Search filter</strong>: Specifies the search filter to apply to
+ matching results that are within the specified scope of the search.</li>
+</ul>
+
+<p>[<a href="#using_address_books">Return to beginning of section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_blogs_and_feeds.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_blogs_and_feeds.xhtml
new file mode 100644
index 0000000000..d96199b4e4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_blogs_and_feeds.xhtml
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Getting started with Blogs &amp; News Feeds</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+<h1 id="getting_started_with_blogs_and_news_feeds">Getting
+ started with Blogs &amp; News Feeds</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#subscribing_to_blogs_and_news_feeds">Subscribing to blogs
+ &amp; news feeds</a></li>
+ <li><a href="#subscribing_to_blogs_and_news_feeds_from_browser">Subscribing
+ to blogs &amp; news feeds from a browser window</a></li>
+ <li><a href="#reading_blogs_and_news_feed_messages">Reading blogs &amp;
+ news feeds messages</a></li>
+ <li><a href="#posting_blog_messages">Posting blog messages</a></li>
+ <li><a href="#adding_comments_to_a_blog_post">Adding comments to a blog
+ post</a></li>
+ <li><a href="#exporting_and_importing_feeds">Exporting and importing
+ feeds</a></li>
+ <li><a href="#editing_a_feed">Editing a feed</a></li>
+ <li><a href="#removing_a_feed">Removing a feed</a></li>
+ <li><a href="#using_different_blogs_and_news_feeds_accounts">Using
+ different blogs &amp; news feeds accounts</a></li>
+ <li><a href="#organizing_your_feeds">Organizing your feeds</a></li>
+ </ul>
+</div>
+
+<h2 id="subscribing_to_blogs_and_news_feeds">Subscribing to blogs &amp; news
+ feeds</h2>
+
+<p>If you have set up a
+ <a href="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts">Blogs
+ &amp; News account</a>, you can subscribe to Blogs &amp; News feeds.</p>
+
+<p>To subscribe to a feed, begin from the Mail window:</p>
+
+<ol>
+ <li id="getting_into_the_feed_subscriptions_dialog">Get into the Feed
+ Subscriptions dialog. There are several ways to do this:
+ <ul>
+ <li>In the accounts pane, click your desired Blogs &amp; News Feeds
+ account to manage, then click in the Manage Subscriptions in the right
+ pane.</li>
+ <li>In the accounts pane, click your desired Blogs &amp; News Feeds
+ account to manage, or a feed inside it, then open the File menu and
+ choose Subscribe.</li>
+ <li>In the accounts pane, <strong>right click</strong> your desired Blogs
+ &amp; News Feeds account to manage and choose Subscribe...</li>
+ <li>Open the Edit menu and select the Mail &amp; Newsgroups Account
+ Settings option. In the Mail &amp; Newsgroups Account Settings dialog,
+ click on a Blogs &amp; News Feeds account main section, and then
+ click the Manage Subscriptions... button.</li>
+ </ul>
+ </li>
+ <li>Once in the Feed Subscriptions dialog, click the Add button. The Feed
+ properties dialog will appear.</li>
+ <li>Type (or copy and paste) the feed URL into the Feed URL field.</li>
+ <li>Click <q>Store articles in</q> dropdown list to choose the item list
+ where you want the articles to be stored. This allows you to merge
+ multiple feeds in one list item.</li>
+ <li>Set <q>Show the article summary instead of loading the web page</q> to
+ display a brief summary that blog feeds usually include for each article.
+
+ <p><strong>Tip:</strong> Showing the article summary reduces the bandwidth
+ traffic and is faster, since the summary is already downloaded when the
+ feed is checked for new items. However, if you usually are interested in
+ the full article, you will save time by unchecking this option.</p>
+ </li>
+ <li>Click OK to confirm the feed addition.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="subscribing_to_blogs_and_news_feeds_from_browser">Subscribing to blogs
+ &amp; news feeds from a browser window</h2>
+
+<p>While browsing the web using &brandShortName;, you may find the Feed
+ discovery icon (<img src="chrome://communicator/skin/icons/feedIcon.png"
+ style="width: 16px; height: 16px;" />) while visiting a web page. You
+ can click on it to see a list of available feeds and choose one to get it
+ added to your first Blogs &amp; News Feeds account.</p>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="reading_blogs_and_news_feed_messages">Reading blogs &amp; news feeds
+ messages</h2>
+
+<p>When you open your Blogs &amp; News account, you see the list of feeds to
+ which you subscribed. &brandShortName; checks and downloads every feed for
+ new messages.</p>
+
+<p>To read blogs &amp; news messages, begin from the Mail window:</p>
+
+<ol>
+ <li>Double-click a blogs &amp; news account to see its feeds. (If there are
+ no feeds, you may need to subscribe to one.)</li>
+ <li>Click a feed name to see its messages.</li>
+ <li>Click a message to read it. The header will show the original URL of the
+ article, which you can click to open a browser window with the
+ corresponding webpage.</li>
+</ol>
+
+<p>Depending on your settings for the Blogs &amp; News account and each
+ individual feed, the message will be shown in its summarized view or the
+ full view. You can change it by choosing the menu option View, and then Feed
+ Message Body As. You can then select one of these options:</p>
+
+<ul>
+ <li><strong>Web Page</strong>: Select this to show the full web page of this
+ message.</li>
+ <li><strong>Summary</strong>: Select this to show the summarized, short
+ version of this message.</li>
+ <li><strong>Default format</strong>: Select this to show the article in its
+ default format, as specified in the feed options or, otherwise, the Blogs
+ &amp; News account.</li>
+</ul>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="posting_blog_messages">Posting blog messages</h2>
+
+<p>To post a blog message, you need an account in the corresponding blog. Also,
+ there is no standardized way to post blog messages, so you won&apos;t
+ normally be able to post messages from &brandShortName; Mail component.
+ Instead, you will need to open a browser window, log in to your blog account
+ and use the web interface.</p>
+
+<p>Some blog systems, however, allow posting blog messages by sending an email
+ message to a specific address. You will need to find out if your blog service
+ implements this feature, and the correct email address to use.</p>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="adding_comments_to_a_blog_post">Adding comments to a blog post</h2>
+
+<p>Since there is no standardized way to add comments to a blog post, you will
+ usually need to open a browser window and use the web interface.</p>
+
+<p>Some blog systems, however, allow adding comments by sending an email
+ message to a specific address. You will need to find out if the blog service
+ implements this feature, and the correct email address to use.</p>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="exporting_and_importing_feeds">Exporting and importing feeds</h2>
+
+<p>If you have set up a
+ <a href="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts">Blogs
+ &amp; News account</a>, you can export or import Blogs &amp; News feed
+ collections using the OPML format (Outline Processor Markup Language).</p>
+
+<p>To export the feeds in your selected blogs &amp; news account, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>In the accounts pane, click your desired Blogs &amp; News account to
+ manage, or a feed inside it.</li>
+ <li>Open the File menu and choose Subscribe (or use any other of the
+ <a href="#getting_into_the_feed_subscriptions_dialog">available methods</a>.
+ to access to the Feed Subscriptions dialog box).</li>
+ <li>In the Feed Subscriptions dialog, click the Export button. The Export
+ feeds as an OPML file dialog will appear.</li>
+ <li>Select the directory and filename to save the OPML file, and click
+ Save.</li>
+</ol>
+
+<p>To import the feeds in your selected blogs &amp; news account, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>In the accounts pane, click your desired Blogs &amp; News account to
+ manage, or a feed inside it.</li>
+ <li>Open the File menu and choose Subscribe (or use any other of the
+ <a href="#getting_into_the_feed_subscriptions_dialog">available methods</a>.
+ to access to the Feed Subscriptions dialog box).</li>
+ <li>In the Feed Subscriptions dialog, click the Import button. The Select
+ OPML file to import dialog will appear.</li>
+ <li>Select the directory and filename to load the OPML file, and click
+ Save.</li>
+ <li>All the feeds defined in the OPML file will be added to your blogs &amp;
+ feeds account.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="editing_a_feed">Editing a feed</h2>
+
+<p>If you want to change the properties of one of the feeds in your blogs &amp;
+ news account, you can edit it.</p>
+
+<p>To edit a feed in your selected blogs &amp; news account, begin from the
+ Mail window:</p>
+
+<ol>
+ <li>In the accounts pane, click your desired Blogs &amp; News account to
+ manage, or a feed inside it.</li>
+ <li>Open the File menu and choose Subscribe (or use any other of the
+ <a href="#getting_into_the_feed_subscriptions_dialog">available methods</a>.
+ to access to the Feed Subscriptions dialog box).</li>
+ <li>In the Feed Subscriptions dialog, click on a feed in the feed list. You
+ may need to expand the folders in the feed list to see each individual
+ feed.</li>
+ <li>Click the Edit button. The Feed properties dialog will appear.</li>
+ <li>You can change where you want the articles to be stored clicking the
+ <q>Store articles in</q> dropdown list.</li>
+ <li>You may mark <q>Show the article summary instead of loading the web
+ page</q> to display a brief summary that blog feeds usually include for
+ each article.
+
+ <p><strong>Tip:</strong> Showing the article summary reduces the bandwidth
+ traffic and is faster, since the summary is already downloaded when the
+ feed is checked for new items. However, if you usually are interested in
+ the full article, you will save time by unchecking this option.</p>
+ </li>
+ <li>Click OK to confirm the changes.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="removing_a_feed">Removing a feed</h2>
+
+<p>If you no longer want to follow one of the feeds in your blogs &amp; news
+ account, you can remove it.</p>
+
+<p>To remove a feed in your selected blogs &amp; news account, begin from the
+ Mail window:</p>
+
+<ol>
+ <li>In the accounts pane, click your desired Blogs &amp; News account to
+ manage, or a feed inside it.</li>
+ <li>Open the File menu and choose Subscribe (or use any other of the
+ <a href="#getting_into_the_feed_subscriptions_dialog">available methods</a>.
+ to access to the Feed Subscriptions dialog box).</li>
+ <li>In the Feed Subscriptions dialog, click on a feed in the feed list. You
+ may need to expand the folders in the feed list to see each individual
+ feed.</li>
+ <li>Click the Remove button. You will be asked to confirm the deletion of the
+ feed.</li>
+</ol>
+
+<p><strong>Note:</strong> don&apos;t confuse a feed with a folder in a Blogs
+ &amp; News account. Removing a feed doesn&apos;t delete the folder in which
+ the feed articles are stored, and thus, such articles will stay in the folder
+ until you delete either the whole folder or the articles themselves. To get
+ a better understanding, see <a href="#organizing_your_feeds">Organizing your
+ feeds</a> later in this section.</p>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="using_different_blogs_and_news_feeds_accounts">Using different blogs
+ &amp; news feeds accounts</h2>
+
+<p>A single blogs &amp; news feeds account can contain any number of feeds in
+ it, so you don&apos;t strictly need more than one blogs &amp; news feeds
+ account. However, you may want to create several blogs &amp; news feeds
+ accounts. Some reasons to do that are:</p>
+
+<ul>
+ <li>You can use different accounts to categorize your feeds. For example, you
+ can create an account named <q>Mozilla News</q> to put in it all your
+ feeds related to Mozilla, and another one named <q>Today Headlines</q> to
+ put in it all your feeds with general news.</li>
+ <li>If you have several accounts, each one can have different settings. This
+ way, you can choose, for example, different time intervals for each account
+ (and, therefore, their feeds.)</li>
+</ul>
+
+<p>[<a href="#getting_started_with_blogs_and_news_feeds">Return to beginning of
+ section</a>]</p>
+
+<h2 id="organizing_your_feeds">Organizing your feeds</h2>
+
+<p>The default operation mode when adding a feed to a Blogs &amp; News Feeds
+ account in &brandShortName; is to create a folder and a feed inside it.
+ However, &brandShortName; allows you a great deal of flexibility. This section
+ helps you to better organize your feeds:</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#feeds_vs_folders">Feeds versus folders</a></li>
+ <li><a href="#organizing_folders_in_blogs_and_news_feeds_accounts">Organizing
+ folders in Blogs &amp; News Feeds accounts</a></li>
+ <li><a href="#downloading_multiple_feeds_in_a_single_folder">Downloading
+ multiple feeds in a single folder</a></li>
+ <li><a href="#moving_a_feed_to_another_folder">Moving a feed to another
+ folder</a></li>
+ </ul>
+</div>
+
+<h3 id="feeds_vs_folders">Feeds versus folders</h3>
+
+<p>Blogs &amp; News Feeds accounts are organized through two main concepts:
+ <strong>feeds</strong> and <strong>folders</strong>.</p>
+
+<ul>
+ <li><strong>Feeds</strong> are sources for articles/posts. They provide
+ the means to get new articles from blogs. You subscribe to feeds.</li>
+ <li><strong>Folders</strong> in Blogs &amp; News Feeds accounts work pretty
+ much like in any other account type. Folders store articles/posts you got
+ through the feeds.</li>
+</ul>
+
+<p>You use the Feed Subscriptions dialog to tell &brandShortName; which feed
+ messages are downloaded in which folders. As feeds provide new articles and
+ folders provide the store to put such articles, you will want to have them
+ connected, usually linking a feed to a folder. However, keep in mind that
+ removing a feed will not automatically delete the associated folder, nor
+ will remove the articles/posts from the removed feed, since they are
+ stored into the folder.</p>
+
+<p>[<a href="#organizing_your_feeds">Return to beginning of section</a>]</p>
+
+<h3 id="organizing_folders_in_blogs_and_news_feeds_accounts">Organizing folders
+ in Blogs &amp; News Feeds accounts</h3>
+
+<p>You can create, rename, move or copy folders in Blogs &amp; News Feeds
+ accounts just like with any other account type. See
+ <a href="mailnews_organizing.xhtml#creating_a_folder">Creating a folder</a>,
+ <a href="mailnews_organizing.xhtml#renaming_a_folder">Renaming a folder</a>
+ and <a href="mailnews_organizing.xhtml#moving_or_copying_a_folder">Moving or
+ copying a folder</a> for more details.</p>
+
+<p>[<a href="#organizing_your_feeds">Return to beginning of section</a>]</p>
+
+<h3 id="downloading_multiple_feeds_in_a_single_folder">Downloading multiple
+ feeds in a single folder</h3>
+
+<p>You may want to use a single folder to store articles/items coming from
+ more than one feed. To do this, you just need to add additional feeds in
+ that folder. Begin from the Mail window:</p>
+
+<ol>
+ <li>In the accounts pane, click your desired Blogs &amp; News Feeds account
+ to manage, or a feed inside it.</li>
+ <li>Open the File menu and choose Subscribe (or use any other of the
+ <a href="#getting_into_the_feed_subscriptions_dialog">available methods</a>.
+ to access to the Feed Subscriptions dialog box).</li>
+ <li>In the Feed Subscriptions dialog, click in the desired folder, then click
+ the Add button. The Feed properties dialog will appear.</li>
+ <li>Type (or copy and paste) the feed URL in the Feed URL field.</li>
+ <li>Click OK to confirm the feed addition.</li>
+</ol>
+
+<p>[<a href="#organizing_your_feeds">Return to beginning of section</a>]</p>
+
+<h3 id="moving_a_feed_to_another_folder">Moving a feed to another folder</h3>
+
+<p>You can move a feed from a folder to another one using any of these
+ methods:</p>
+
+<ul>
+ <li>Drag &amp; drop a feed while inside the Feed Subscriptions window.</li>
+ <li><a href="#editing_a_feed">Edit the feed</a> to change in what folder it
+ downloads articles/items.</li>
+</ul>
+
+<p><strong>Note</strong>: Remember that moving the feed doesn&apos;t move
+ existing articles from the folder in which they have been
+ downloaded.</p>
+
+<p>[<a href="#organizing_your_feeds">Return to beginning of section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_getting_started.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_getting_started.xhtml
new file mode 100644
index 0000000000..330ebcabf6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_getting_started.xhtml
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Using &brandShortName; Mail &amp; Newsgroups</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+
+<div class="boilerPlate">This document is provided for your information only.
+ It may help you take certain steps to protect the privacy and security of
+ your personal information on the Internet. This document does not, however,
+ address all online privacy and security issues, nor does it represent a
+ recommendation about what constitutes adequate privacy and security
+ protection on the Internet.</div>
+
+<h1 id="using_mozilla_mail_and_newsgroups">Using &brandShortName; Mail &amp;
+ Newsgroups</h1>
+
+<p>&brandShortName; Mail &amp; Newsgroups lets you conveniently manage all your
+ Internet communications from one place. You can set up and maintain multiple
+ business and personal mail accounts and Internet newsgroups, all from one
+ window &mdash; the Mail &amp; Newsgroups window.</p>
+
+<p>To start using &brandShortName; Mail &amp; Newsgroups:</p>
+
+<ul>
+ <li>Click the Mail &amp; Newsgroups icon in the lower-left corner of the
+ &brandShortName; browser window.</li>
+</ul>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/task_mail.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 20px;"></td>
+ <td><strong>Mail &amp; Newsgroups icon</strong></td>
+ </tr>
+</table>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#getting_started_with_mozilla_mail_and_newsgroups">Getting
+ Started with &brandShortName; Mail &amp; Newsgroups</a></li>
+ <li><a href="#importing_mail_from_other_programs">Importing Mail from Other
+ Programs</a></li>
+ <li><a href="mailnews_using_mail.xhtml#reading_messages">Reading
+ Messages</a></li>
+ <li><a href="mailnews_using_mail.xhtml#sending_messages">Sending
+ Messages</a></li>
+ <li><a href="mailnews_using_mail.xhtml#creating_html_mail_messages">Creating
+ HTML Mail Messages</a></li>
+ <li><a href="mailnews_using_mail.xhtml#using_attachments">Using
+ Attachments</a></li>
+ <li><a href="mailnews_using_mail.xhtml#deleting_messages">Deleting
+ Messages</a></li>
+ <li><a href="mailnews_addressbooks.xhtml">Using Address Books</a></li>
+ <li><a href="mailnews_organizing.xhtml">Organizing Your Messages</a></li>
+ <li><a href="mailnews_organizing.xhtml#controlling_junk_mail">Controlling
+ Junk Mail</a></li>
+ <li><a href="mailnews_newsgroups.xhtml">Getting Started with
+ Newsgroups</a></li>
+ <li><a href="mailnews_blogs_and_feeds.xhtml">Getting Started
+ with Blogs &amp; News Feeds</a></li>
+ <li><a href="mailnews_offline.xhtml">Working Offline</a></li>
+ <li><a href="mailnews_security.xhtml">Signing &amp; Encrypting
+ Messages</a></li>
+ <li><a href="mailnews_account_settings.xhtml">Mail &amp; Newsgroups
+ Account Settings</a></li>
+ <li><a href="mailnews_preferences.xhtml">Mail &amp; Newsgroups
+ Preferences</a></li>
+ </ul>
+</div>
+
+<h1 id="getting_started_with_mozilla_mail_and_newsgroups">Getting Started with
+ &brandShortName; Mail &amp; Newsgroups</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#using_the_mail_account_setup_wizard">Using the Mail Account
+ Setup Wizard</a></li>
+ <li><a href="#setting_up_additional_mail_and_news_accounts">Setting Up
+ Additional Mail and News Accounts</a></li>
+ <li><a href="#changing_the_settings_for_an_account">Changing the Settings
+ for an Account</a></li>
+ </ul>
+</div>
+
+<h2 id="using_the_mail_account_setup_wizard">Using the Mail Account Setup
+ Wizard</h2>
+
+<p>To set up a mail, newsgroup or blogs &amp; news feeds account, first open
+ the Window menu and choose Mail &amp; Newsgroups. If you haven&apos;t
+ already set up an account, the Account Wizard appears automatically, enabling
+ you to set up an account.</p>
+
+<p>The Account Wizard guides you through the process of creating a new account.
+ If you don&apos;t know a setting, click Cancel and ask your Internet service
+ provider (ISP) or help desk.</p>
+
+<p>If an account already exists, the Account Wizard doesn&apos;t appear
+ automatically when the Mail window opens. Instead, after opening the Mail
+ window, open the File menu and choose New, then Account. For more details,
+ see <a href="#setting_up_additional_mail_and_news_accounts">Setting Up
+ Additional Mail, News &amp; Blogs &amp; News Feeds Accounts</a>.</p>
+
+<h3 id="setting_up_mail_accounts_with_an_isp_or_email_provider">Setting Up Mail
+ Accounts with an ISP or Email Provider</h3>
+
+<p>Before you set up a mail account, your ISP or email provider should give you
+ the following information:</p>
+
+<ul>
+ <li>your user name</li>
+ <li>your email address</li>
+ <li>the incoming and outgoing mail server names</li>
+ <li>the incoming server type (<a href="glossary.xhtml#imap">IMAP</a> or
+ <a href="glossary.xhtml#pop">POP</a>)</li>
+</ul>
+
+<p>Before you set up a newsgroup account, your ISP or email provider should
+ give you the following information:</p>
+
+<ul>
+ <li>your email address</li>
+ <li>newsgroup server name</li>
+ <li>account name</li>
+</ul>
+
+<p>To set up a new mail, newsgroup or blogs &amp; news feeds account, begin
+ from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click Add Account to start the Account Wizard.
+
+ <p>The information requested by the Account Wizard depends on the type of
+ new account you specify in its first window. The boldface headings that
+ follow correspond to the windows you&apos;ll see when you&apos;re setting
+ up an ISP or email provider account.</p>
+ </li>
+ <li><strong>New Account Setup</strong>: Choose the type of account you want
+ to set up, then click the right arrow.</li>
+ <li><strong>Identity</strong>: Enter the name and email address appropriate
+ for this account, then click the right arrow. This window is not available
+ for the Blogs &amp; News Feeds account type.</li>
+ <li><strong>Server Information</strong>: This window is not available for
+ for the Blogs &amp; News Feeds account type.
+ <ul>
+ <li>Indicate whether you want a POP account or an IMAP account. Not all
+ service providers can support both options. For more information, see
+ <a href="mailnews_account_settings.xhtml#server_settings">Mail &amp;
+ Newsgroups Account Settings - Server Settings</a>.</li>
+ <li>Enter the name of your incoming mail server.</li>
+ <li>Check the <q>Leave messages on server</q> checkbox if you want to
+ leave messages on the server so that when you download messages,
+ &brandShortName; does not remove the messages from the server.</li>
+ <li>If you want this account to be a part of the Local Folders Global
+ Inbox account, check the <q>Use Global Inbox</q> box. Mail for
+ this account will then be stored in your Local Folders. Otherwise, if
+ the checkbox is unchecked, mail will be stored in its own
+ directory.</li>
+ <li>Enter the name of your outgoing mail server (SMTP).
+
+ <p><strong>Note</strong>: You need to specify only one outgoing mail
+ server (SMTP), even if you have several mail accounts. The name of
+ your <a href="glossary.xhtml#smtp">SMTP</a> host may not have been
+ explicitly listed in the account setup information provided to you.
+ For example, your SMTP host may be the same as your POP or IMAP host.
+ If in doubt, contact your ISP or system administrator.</p>
+ </li>
+ <li>Click the right arrow to continue.</li>
+ </ul>
+ </li>
+ <li><strong>User Names</strong>: Enter the incoming and outgoing user names
+ provided by your ISP or email provider, then click the right arrow. This
+ window is not available for the Blogs &amp; News Feeds account type.</li>
+ <li><strong>Account Name</strong>: Enter whatever name you want to use to
+ refer to this account, then click the right arrow.</li>
+ <li><strong>Congratulations!</strong> Verify that the information you entered
+ is correct. If necessary, verify the information you entered with your ISP
+ or system administrator. When you are sure that it&apos;s correct, click
+ Finish to set up your account.</li>
+ <li>You see your new account listed in the left side of the Mail &amp;
+ Newsgroups Account Settings dialog box. Click OK to start using your new
+ account.</li>
+</ol>
+
+<p>You are now ready to get messages from your account. &brandShortName; Mail
+ &amp; Newsgroups will prompt you for your password when you retrieve mail for
+ the first time every session. For detailed instructions on how to retrieve
+ mail, see <a href="mailnews_using_mail.xhtml#getting_new_messages">Getting
+ New Messages</a>.</p>
+
+<p>[<a href="#getting_started_with_mozilla_mail_and_newsgroups">Return to
+ beginning of section</a>]</p>
+
+<h2 id="setting_up_additional_mail_and_news_accounts">Setting Up Additional
+ Mail and News Accounts</h2>
+
+<p>You use the Account Settings dialog box to add a new account or to change
+ information for an existing account, including:</p>
+
+<ul>
+ <li>mail and newsgroup server settings (for example, message deletion and
+ download preferences)</li>
+ <li>storage settings for message copies and folders</li>
+ <li>your reply-to address, organization name, and signature</li>
+</ul>
+
+<p>To add a new account or change settings for an existing account, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box. You can perform
+ these tasks:
+ <ul>
+ <li><strong>Add Account</strong>: Click this button to set up a new mail,
+ news or blogs &amp; news feeds account. Be sure to type the account
+ information exactly as it is given to you. Move through the screens
+ with the arrows, or click Cancel to stop account creation.</li>
+ <li id="set_as_default"><strong>Set as Default</strong>: Select an
+ account, then click this button to make the selected account the
+ default one. The default account will appear at the top of your list
+ of accounts in the Mail window. The change takes effect the next time
+ you open Mail &amp; Newsgroups.
+
+ <p>The default account determines which address is filled into the
+ From: field when you compose a new mail with either no other mail or
+ news account active (i.e. Local Folders or a blogs &amp; news feeds
+ account is selected), through an external application request, or by
+ following a mailto: link.</p>
+
+ <p><strong>Note:</strong> You can&apos;t set a blogs &amp; news feeds
+ account as default.</p>
+ </li>
+ <li><strong>Remove Account</strong>: Select an account, then click this
+ button to remove it completely from your Mail window.</li>
+ <li><strong>Outgoing Server (SMTP)</strong>: Click this (at the bottom of
+ the list of accounts) to modify information about the outgoing mail
+ server. See
+ <a href="mailnews_account_settings.xhtml#outgoing_server">Mail &amp;
+ Newsgroups Account Settings - Outgoing Server (SMTP)</a> for more
+ information.</li>
+ </ul>
+ </li>
+ <li>Click headings under any account&apos;s name and modify the corresponding
+ settings in the panel on the right.</li>
+ <li>Click OK to save your changes.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_mozilla_mail_and_newsgroups">Return to
+ beginning of section</a>]</p>
+
+<h2 id="changing_the_settings_for_an_account">Changing the Settings for an
+ Account</h2>
+
+<p>To view or change information for an existing mail or newsgroup account,
+ begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click the account name in the left-hand side of the Account Settings
+ dialog box. You see information about the account, such as your email
+ address and signature, in the right side of the dialog box.</li>
+ <li>Click any of these items beneath the name of an account to see the
+ corresponding settings:
+ <ul>
+ <li><strong>Server Settings</strong>: The settings available depend on
+ the type of server (IMAP, POP, or newsgroup server). For more
+ information, see
+ <a href="mailnews_account_settings.xhtml#server_settings">Mail &amp;
+ Newsgroups Account Settings - Server Settings</a>.
+
+ <p><strong>Important</strong>: If you need to change the server type
+ (for example, from POP to IMAP) you must first remove the existing
+ account. Next, you must exit &brandShortName; and restart it. You can
+ then reopen the Mail &amp; Newsgroups Account Settings dialog box and
+ recreate an account with the new server type by clicking Add
+ Account.</p>
+ </li>
+ <li><strong>Copies &amp; Folders</strong>: These settings determine
+ whether to send automatic messages (blind carbon copies) and where you
+ want to store copies of outgoing messages, draft messages, and message
+ templates. For more information, see
+ <a href="mailnews_account_settings.xhtml#copies_and_folders">Mail
+ &amp; Newsgroups Account Settings - Copies &amp; Folders</a>.</li>
+ <li><strong>Composition &amp; Addressing</strong>: These settings allow
+ you to choose your default format and quoting behavior when composing a
+ message. You can also override the global directory server settings
+ specified for all address books in the Preferences dialog box. For more
+ information, see
+ <a href="mailnews_account_settings.xhtml#addressing">Mail &amp;
+ Newsgroups Account Settings - Composition &amp; Addressing</a>.</li>
+ <li><strong>Synchronization &amp; Storage (IMAP and News accounts
+ only)</strong>: These settings apply when you are working offline
+ (disconnected from the Internet) or need to save download time and
+ conserve disk space. For more information, see
+ <a href="mailnews_account_settings.xhtml#synchronization_and_storage_settings_imap">Synchronization
+ &amp; Storage Settings (IMAP)</a> or
+ <a href="mailnews_account_settings.xhtml#synchronization_and_storage_settings_nntp">Synchronization
+ &amp; Storage Settings (News)</a>.</li>
+ <li><strong>Disk Space (POP and blogs &amp; news feeds accounts
+ only)</strong>: This setting helps you manage the amount of disk
+ space that downloaded messages take up on your hard disk. For more
+ information, see
+ <a href="mailnews_account_settings.xhtml#disk_space_settings_pop">Disk
+ Space Settings (POP)</a> or
+ <a href="mailnews_account_settings.xhtml#disk_space_settings_blogs">Disk
+ Space Settings (Blogs)</a>.</li>
+ <li><strong>Security</strong>: These settings determine which
+ <a href="glossary.xhtml#certificate">certificates</a> are used to
+ digitally sign and encrypt mail messages that you send. Digital
+ signatures allow you to identify yourself reliably to others in
+ mail messages that you send. Encryption helps ensure that your
+ messages remain private while they are in transit over the
+ Internet. For more information, see
+ <a href="mailnews_account_settings.xhtml#security">Mail &amp;
+ Newsgroups Account Settings - Security</a>.</li>
+ </ul>
+ </li>
+ <li>Click OK to save your changes.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_mozilla_mail_and_newsgroups">Return to
+ beginning of section</a>]</p>
+
+<h1 id="importing_mail_from_other_programs">Importing Mail from Other
+ Programs</h1>
+
+<p>This section describes how to import mail messages and settings from
+ Netscape Communicator, and Outlook. To import address books from these
+ programs, see
+ <a href="mailnews_addressbooks.xhtml#importing_address_books">Importing
+ Address Books</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#importing_mail_messages">Importing Mail Messages</a></li>
+ <li><a href="#importing_mail_settings">Importing Mail Settings</a></li>
+ </ul>
+</div>
+
+<h2 id="importing_mail_messages">Importing Mail Messages</h2>
+
+<p>To import mail messages from Netscape Communicator, or Outlook, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>Open the Tools menu, and choose Import. You see the Import Wizard.</li>
+ <li>Follow the instructions to import mail messages.</li>
+</ol>
+
+<p>For Netscape Communicator, the wizard imports a copy of all Communicator
+ mail folders included under Local Folders. Imported mail is added as a new
+ folder under Local Folders in the Mail window. (The Communicator mail
+ folders still remain in their original location).</p>
+
+<p>[<a href="#importing_mail_from_other_programs">Return to beginning of
+ section</a>]</p>
+
+<h2 id="importing_mail_settings">Importing Mail Settings</h2>
+
+<p>To import mail settings from Outlook, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Tools menu, and choose Import. You see the Import Wizard.</li>
+ <li>Follow the instructions to import mail settings.</li>
+</ol>
+
+<p>[<a href="#importing_mail_from_other_programs">Return to beginning of
+ section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_newsgroups.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_newsgroups.xhtml
new file mode 100644
index 0000000000..71f8c9f2f6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_newsgroups.xhtml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Getting Started With Newsgroups</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+<h1 id="getting_started_with_newsgroups">Getting Started With Newsgroups</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#subscribing_to_newsgroups">Subscribing to Newsgroups</a></li>
+ <li><a href="#reading_newsgroup_messages">Reading Newsgroup
+ Messages</a></li>
+ <li><a href="#posting_newsgroup_messages">Posting Newsgroup
+ Messages</a></li>
+ <li><a href="#contributing_to_ongoing_discussions">Contributing to Ongoing
+ Discussions</a></li>
+ <li><a href="#monitoring_threads">Monitoring Threads</a></li>
+ <li><a href="#removing_a_newsgroup">Removing a Newsgroup</a></li>
+ <li><a href="#adding_a_newsgroup_server">Adding a Newsgroup Server</a></li>
+ </ul>
+</div>
+
+<h2 id="subscribing_to_newsgroups">Subscribing to Newsgroups</h2>
+
+<p>If you have set up an <a href="#adding_a_newsgroup_server">account</a> on a
+ newsgroup server, you can join (subscribe) to newsgroups (also called
+ discussion groups).</p>
+
+<p>To subscribe to a newsgroup, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the File menu and choose Subscribe. You see the Subscribe dialog
+ box.</li>
+ <li>If necessary, click the Account drop-down list to choose another
+ newsgroup account.</li>
+ <li>Select a newsgroup. To select more than one newsgroup,
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>-click additional
+ newsgroup.</li>
+ <li>Click Subscribe or click in the Subscribe column next to the newsgroup.
+ You see a checkmark next to each newsgroup to which you subscribe. Click
+ Unsubscribe to cancel a selection.</li>
+ <li>Click OK. The list of your subscribed newsgroups appears in the Mail
+ window.</li>
+</ol>
+
+<p>If you are an IMAP mail user, you can also subscribe to message folders
+ located on an IMAP server. (Your Inbox is a type of message folder.) Follow
+ the instructions above for subscribing, but select an IMAP account from the
+ Account drop-down list. For more information on sharing folders and
+ subscribing to folders, see
+ <a href="mailnews_organizing.xhtml#sharing_folders_with_other_users">Sharing
+ Folders With Other Users (IMAP Only)</a>.</p>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+
+<h2 id="reading_newsgroup_messages">Reading Newsgroup Messages</h2>
+
+<p>When you open your newsgroup server, you see the list of newsgroups to which
+ you subscribe. The server downloads the <em>headers</em> of new messages in
+ each newsgroup.</p>
+
+<p>To read newsgroup messages, begin from the Mail window:</p>
+
+<ol>
+ <li>Double-click a newsgroup server icon to see its newsgroups. (If there are
+ no newsgroups, you may need to subscribe to one.)</li>
+ <li>Click a newsgroup name to see its messages.</li>
+ <li>Click a message to read it. Click the thread button to display all the
+ responses below the original message. You can click any header to display
+ its message. You can <a href="#posting_newsgroup_messages">start a new
+ thread</a> or <a href="#contributing_to_ongoing_discussions">post a
+ message</a> in response.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+
+<h2 id="posting_newsgroup_messages">Posting Newsgroup Messages</h2>
+
+<p>To start new threads (discussions):</p>
+
+<ol>
+ <li>From the list of your subscribed newsgroups in the Mail window, select a
+ newsgroup.</li>
+ <li>Click Compose.</li>
+ <li><a href="mailnews_using_mail.xhtml#composing_mail_and_newsgroup_messages">Compose</a>
+ your message, and click Send to post it.</li>
+ <li>Click Get Msgs to see your posting on the newsgroup.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+
+<h2 id="contributing_to_ongoing_discussions">Contributing to Ongoing
+ Discussions</h2>
+
+<p>To post a response to the newsgroup:</p>
+
+<ol>
+ <li>In the message list, select a message to reply to.</li>
+ <li>Click Reply.</li>
+ <li><a href="mailnews_using_mail.xhtml#composing_mail_and_newsgroup_messages">Compose</a>
+ your message, and click Send to post it.</li>
+</ol>
+
+<p>To reply to an individual as well as post a response to the group:</p>
+
+<ol>
+ <li>In the message list, select a message to reply to.</li>
+ <li>Click Reply All.</li>
+ <li>Compose your message, and click Send to post it.</li>
+</ol>
+
+<p>To redirect a posting to another newsgroup:</p>
+
+<ul>
+ <li>Click Reply and choose <q>Followup-To</q> from the <q>Newsgroup</q>
+ drop-down list. Subsequent responses will be posted to the newsgroup you
+ enter.</li>
+</ul>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+
+<h2 id="monitoring_threads">Monitoring Threads</h2>
+
+<p>To monitor unread messages in threads that are of interest to you:</p>
+
+<ol>
+ <li>Select a message in a thread.</li>
+ <li>Open the Message menu, and choose Watch Thread.</li>
+ <li>If you want to monitor additional threads, repeat steps 1 and 2 for
+ messages in additional threads.</li>
+ <li>When you&apos;re ready to monitor messages in these threads, open the
+ View menu, choose Threads, and then choose Watched Threads with Unread.
+ &brandShortName; Mail &amp; Newsgroups only displays the watched threads
+ that contain unread messages.</li>
+ <li>Open the View menu, choose Threads, and then choose All to return to
+ viewing all messages in the newsgroup.</li>
+</ol>
+
+<p>To ignore a message thread:</p>
+
+<ol>
+ <li>Select a message in the thread.</li>
+ <li>Open the Message menu, and choose Ignore Thread. &brandShortName; Mail
+ &amp; Newsgroups marks all messages in the thread as read, and new replies
+ posted to the thread will appear as read.</li>
+ <li>To view ignored threads, open the View menu, choose Threads, and then
+ choose Ignored Threads.</li>
+</ol>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+
+<h2 id="removing_a_newsgroup">Removing a Newsgroup</h2>
+
+<p>To remove a newsgroup from your list:</p>
+
+<ul>
+ <li>Select the newsgroup icon and press Delete.</li>
+</ul>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+
+<h2 id="adding_a_newsgroup_server">Adding a Newsgroup Server</h2>
+
+<p>If the newsgroup you want to subscribe to is on a different server, you must
+ first set up access to that server.</p>
+
+<p>To set up an additional newsgroup server, open the File menu in the Mail
+ window and choose New, then Account.</p>
+
+<ul>
+ <li>Using the Account Wizard, indicate that the new account you want to set
+ up is a newsgroup account.</li>
+</ul>
+
+<p>Once you&apos;ve set up access to the new server, you can
+ <a href="#subscribing_to_newsgroups">subscribe</a> to newsgroups on that
+ server. In the Mail window, open the File menu, and choose Subscribe.</p>
+
+<p>[<a href="#getting_started_with_newsgroups">Return to beginning of
+ section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_offline.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_offline.xhtml
new file mode 100644
index 0000000000..2c958051af
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_offline.xhtml
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Working Offline</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="working_offline">Working Offline</h1>
+
+<p>&brandShortName; Mail &amp; Newsgroups includes advanced features to help
+ you manage your messaging needs when you are not connected to the Internet.
+ You can download mail and news messages before going offline for later
+ reading, and you can defer sending mail messages and newsgroup posts until
+ you get back online. All of these features are explained in this
+ document.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#setting_up_mozilla_mail_and_newsgroups_to_work_offline">Setting
+ Up &brandShortName; Mail &amp; Newsgroups to Work Offline</a></li>
+ <li><a href="#downloading_all_messages_for_offline_use">Downloading All
+ Messages for Offline Use</a></li>
+ <li><a href="#downloading_an_individual_folder_for_offline_use">Downloading
+ an Individual Folder for Offline Use</a></li>
+ <li><a href="#downloading_selected_or_flagged_messages_for_offline_use">Downloading
+ Selected or Flagged Messages for Offline Use</a></li>
+ <li><a href="#downloading_directory_entries_for_offline_use">Downloading
+ Directory Entries for Offline Use</a></li>
+ <li><a href="#setting_up_your_accounts_for_working_offline">Setting Up Your
+ Accounts for Working Offline</a></li>
+ <li><a href="#selecting_items_for_offline_viewing">Selecting Items for
+ Offline Viewing</a></li>
+ <li><a href="#downloading_and_synchronizing_your_messages">Downloading and
+ Synchronizing Your Messages</a></li>
+ <li><a href="#working_offline_and_reconnecting_later">Working Offline and
+ Reconnecting Later</a></li>
+ </ul>
+</div>
+
+<h2 id="setting_up_mozilla_mail_and_newsgroups_to_work_offline">Setting Up
+ &brandShortName; Mail &amp; Newsgroups to Work Offline</h2>
+
+<p>&brandShortName; Mail &amp; Newsgroups&apos; offline feature lets you
+ download your mail and read it offline (while disconnected from the
+ Internet). If you use a dial-up (modem) connection to access your mail and
+ you want to reduce the time you are connected, or, if you need to temporarily
+ disconnect from your company&apos;s network while traveling or switching
+ locations, you can download your mail so that you can read it offline. The
+ offline feature can automatically download incoming messages and then later
+ send all your outgoing messages when you reconnect.</p>
+
+<p>Note that for POP accounts your mail is already downloaded by default, so
+ most of these offline features aren&apos;t relevant for POP accounts.</p>
+
+<p>If you occasionally want to work offline, &brandShortName; Mail &amp;
+ Newsgroups lets you easily:</p>
+
+<ul>
+ <li>Download your Inbox for offline use.</li>
+ <li>Download an individual folder for offline use.</li>
+ <li>Download only selected or flagged messages for offline use.</li>
+ <li>Download directory entries in your address book for offline use.</li>
+</ul>
+
+<p>If you frequently work offline, &brandShortName; Mail &amp; Newsgroups also
+ lets you:</p>
+
+<ul>
+ <li>Set up one or more of your accounts for offline use.</li>
+ <li>Set offline and disk space preferences for each account.</li>
+ <li>Select the folders and newsgroups that you want to view offline.</li>
+</ul>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="downloading_all_messages_for_offline_use">Downloading All Messages for
+ Offline Use</h2>
+
+<p>You can tell &brandShortName; Mail &amp; Newsgroups to automatically
+ download your messages for offline use. Later, when you go back online,
+ &brandShortName; Mail &amp; Newsgroups automatically synchronizes your
+ messages with the server.</p>
+
+<p>Note that the Inbox for POP accounts is downloaded by default, so this
+ section does not apply for POP accounts.</p>
+
+<p>To automatically download your messages for offline use, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>In the left side of the dialog box, under the name of the account you
+ want to use offline, select Synchronization &amp; Storage. (This category
+ is not available for POP accounts.)</li>
+ <li>Check the box labeled <q>Keep messages for this account on this
+ computer</q>.</li>
+ <li>Click OK.</li>
+ <li>Click the Online/Offline indicator <img src="images/online.png" alt=""/>
+ in the lower right corner of the Mail window (to the left of the Cookie
+ icon) to go offline. You will be asked to download messages for them to be
+ available while offline. Click on <q>Download</q> to proceed.</li>
+</ol>
+
+<p><strong>Note</strong>: This setting also applies to any new folders
+ created. While the per-account setting can be overridden for an
+ <a href="#downloading_an_individual_folder_for_offline_use">individual
+ folder</a>, those per-folder settings are <em>removed</em> when the
+ <q>Keep messages</q> box is toggled.</p>
+
+<p>&brandShortName; Mail &amp; Newsgroups automatically downloads all messages
+ in your Inbox so you can read and respond to them while working offline.
+ After disconnecting, &brandShortName; Mail &amp; Newsgroups remains open so
+ you can continue to work with your messages.</p>
+
+<p>To reconnect to the Internet so you can work online:</p>
+
+<ul>
+ <li>Click the Online/Offline indicator <img src="images/offline.png"
+ alt=""/> in the lower right corner of the Mail window (to the left of the
+ Cookie icon) to go back online.</li>
+</ul>
+
+<p>When you go back online, &brandShortName; Mail &amp; Newsgroups
+ automatically synchronizes your Inbox messages with the server, by
+ replicating any changes you made while working offline.</p>
+
+<p><strong>Tip</strong>: &brandShortName; Mail &amp; Newsgroups saves any
+ messages that you send while working offline in the Unsent Messages folder
+ under Local Folders. To have &brandShortName; Mail &amp; Newsgroups
+ automatically send your unsent messages when you reconnect, use the
+ Preferences command on the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu to change the
+ <a href="mailnews_preferences.xhtml#network_and_storage_preferences">offline
+ preferences</a> for all your accounts.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="downloading_an_individual_folder_for_offline_use">Downloading an
+ Individual Folder for Offline Use</h2>
+
+<p>Note that POP accounts don&apos;t allow you to manage folders on the POP
+ server, so this section does not apply to POP accounts.</p>
+
+<p>To download a specific folder for offline use, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>In the left side of the Mail window, select the folder that you want to
+ download for offline use.</li>
+ <li>Open the Edit menu, and choose Folder Properties. You see the Properties
+ dialog box.</li>
+ <li>Click the Synchronization tab.</li>
+ <li>Check <q>Select this folder for offline use</q>.</li>
+ <li>Click Download Now if you want to immediately begin downloading the
+ folder&apos;s messages. Alternatively, you can continue working, and when
+ you are ready to go offline, proceed to the next step.</li>
+ <li>Click the Online/Offline indicator <img src="images/online.png" alt=""/>
+ in the lower right corner of the Mail window to go offline.</li>
+ <li>In the Work Offline dialog box, click Download.</li>
+</ol>
+
+<p>&brandShortName; Mail &amp; Newsgroups automatically downloads all messages
+ in the selected folder so you can read and respond to them while working
+ offline. After disconnecting, &brandShortName; Mail &amp; Newsgroups remains
+ open so you can continue to work with your messages.</p>
+
+<p><strong>Note</strong>: Message headers that have been downloaded for reading
+ offline display a darker gray envelope or newsgroup icon.</p>
+
+<p>To reconnect to the Internet so you can work online:</p>
+
+<ul>
+ <li>Click the Online/Offline indicator <img src="images/offline.png"
+ alt=""/> in the lower right corner of the Mail window (to the left of the
+ Cookie icon) to go back online.</li>
+</ul>
+
+<p>&brandShortName; Mail &amp; Newsgroups automatically synchronizes the
+ offline folders with the server, by replicating any changes you made while
+ working offline.</p>
+
+<p><strong>Tip</strong>: &brandShortName; Mail &amp; Newsgroups saves any
+ messages that you sent while working offline in the Unsent Messages folder
+ under Local Folders. When you reconnect, choose Send Unsent Messages from the
+ File menu to send all your saved messages at once. To have &brandShortName;
+ Mail &amp; Newsgroups automatically send your unsent messages when you
+ reconnect, use the Preferences command on the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu to change your <a
+ href="mailnews_preferences.xhtml#network_and_storage_preferences">offline
+ preferences</a>.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="downloading_selected_or_flagged_messages_for_offline_use">Downloading
+ Selected or Flagged Messages for Offline Use</h2>
+
+<p>Note that messages are downloaded by default for POP accounts. However, if
+ you have enabled the <q>Fetch headers only</q> setting in the POP account
+ settings, then only the headers will be downloaded, and you will need to use
+ the commands in this section to download the complete messages.</p>
+
+<p>To download selected messages for offline use, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Select a Mail or Newsgroup folder to display its messages.</li>
+ <li>Select the messages you want to download, as follows:
+ <ul>
+ <li>To select a group of adjacent messages, click the first message, and
+ then Shift-click to select the last message in the group.</li>
+ <li>To select messages anywhere in the message list, hold down the
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd> key and click
+ each message.</li>
+ </ul>
+ </li>
+ <li>Open the File menu, choose Offline, and then choose Get Selected Messages
+ from the submenu. &brandShortName; Mail &amp; Newsgroups downloads the
+ selected messages.</li>
+</ol>
+
+<p>To download flagged messages for offline use, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Select a Mail or Newsgroup folder to display its messages.</li>
+ <li>Click in the flag column of each message you want to download. A flag
+ appears where you clicked to indicate that the message has been marked. If
+ the flag column is not visible, click the Show/Hide Columns icon
+ <img src="images/columns.png" alt=""/> and select Flag from the list.</li>
+ <li>Open the File menu, choose Offline, and then choose Get Flagged Messages.
+ &brandShortName; Mail &amp; Newsgroups downloads the flagged messages.</li>
+</ol>
+
+<p>Once downloading is complete, click the Online/Offline indicator in the
+ lower right corner of the Mail window (to the left of the Cookie icon) to go
+ offline. After you disconnect, &brandShortName; Mail &amp; Newsgroups remains
+ open so you can continue to work with your messages.</p>
+
+<p>Note that the <q>Get Selected Messages</q> and <q>Get Flagged Messages</q>
+ menu items are also available in the pop-up thread context menu, for faster
+ access.</p>
+
+<p><strong>Note</strong>: Message headers that have been downloaded for reading
+ offline display a darker gray envelope or newsgroup icon.</p>
+
+<p>To reconnect to the Internet so you can work online:</p>
+
+<ul>
+ <li>Click the Online/Offline indicator <img src="images/offline.png"
+ alt=""/> in the lower right corner of the Mail window to go online.</li>
+</ul>
+
+<p><strong>Tip</strong>: &brandShortName; Mail &amp; Newsgroups saves any
+ messages that you sent while working offline in the Unsent Messages folder
+ under Local Folders. When you reconnect, choose Send Unsent Messages from the
+ File menu to send all your saved messages at once. To have &brandShortName;
+ Mail &amp; Newsgroups automatically send your unsent messages when you
+ reconnect, use the Preferences command on the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu to change your <a
+ href="mailnews_preferences.xhtml#network_and_storage_preferences">offline
+ preferences</a>.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="downloading_directory_entries_for_offline_use">Downloading
+ Directory Entries for Offline Use</h2>
+
+<p>You can download (replicate) the entries in a directory server to your
+ computer so that they are available when you work offline. Once you&apos;ve
+ downloaded directory entries, you can use the same procedure to update your
+ local copy of the entries with the latest entries on the directory
+ server.</p>
+
+<p>To download or update an address book LDAP directory for offline use:</p>
+
+<ol>
+ <li>Make sure you&apos;re online.</li>
+ <li>Open the Window menu, and choose Address Book.</li>
+ <li>In the Address Book window, select the directory that you want to
+ download (replicate).</li>
+ <li>Click Properties in the Address Book toolbar. The Directory Server
+ Properties dialog box appears.</li>
+ <li>Click the Offline tab.</li>
+ <li>Click Download Now to start copying the entries to your computer.</li>
+ <li>If prompted, enter your network user name and password, and click OK to
+ start the download.
+
+ <p>Depending on the number of directory entries, the download process may
+ take a while, so please be patient.</p>
+ </li>
+</ol>
+
+<p>After the download finishes, you can work offline and search the directory
+ or use it for address autocompletion when composing messages. After
+ you&apos;ve been using your local copy of the directory for a while, you may
+ wish to update it to get the latest entries from the directory server. To
+ update your local copy, use the procedure described above.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="setting_up_your_accounts_for_working_offline">Setting Up Your Accounts
+ for Working Offline</h2>
+
+<p>To set up one or more accounts for working offline, you use the Offline and
+ Disk Space preferences in the Mail &amp; Newsgroups Account Settings dialog
+ box. Once set, you don&apos;t need to change these preferences each time you
+ want to work offline. The offline and disk space preferences you can set for
+ an account depend on the type of account (IMAP, POP, or Newsgroup).</p>
+
+<p>Here&apos;s a summary of the steps you will follow to set up your accounts
+ for offline use:</p>
+
+<ol>
+ <li>For each account that you want to work with while offline, use the Mail
+ &amp; Newsgroups Account Settings dialog box to set the Synchronization
+ &amp; Storage preferences for that account. You must select the items
+ (folders and newsgroups) that you want to download for offline use. See
+ <a href="#selecting_items_for_offline_viewing">Selecting Items for Offline
+ Viewing</a> for more information.
+
+ <p>Once set, you don&apos;t need to change these settings. See the sections
+ below for information on setting offline and disk space preferences for
+ <a href="mailnews_account_settings.xhtml#synchronization_and_storage_settings_imap">IMAP</a>,
+ <a href="mailnews_account_settings.xhtml#disk_space_settings_pop">POP</a>,
+ <a href="mailnews_account_settings.xhtml#disk_space_settings_blogs">Blogs</a>, and
+ <a href="mailnews_account_settings.xhtml#synchronization_and_storage_settings_nntp">Newsgroup</a>
+ accounts.</p>
+
+ <p><strong>Tip</strong>: To set the Synchronization &amp; Storage
+ preferences for the current account, open the File menu, choose Offline,
+ and then choose Offline Settings.</p>
+ </li>
+ <li>Open the File menu, choose Offline, and then choose Download/Sync Now
+ from the submenu.</li>
+ <li>Select the type of messages (mail or newsgroup or both) that you want to
+ download.
+
+ <p><strong>Important</strong>: You must select at least one category (mail
+ messages or newsgroup messages) in order for the download to work.</p>
+ </li>
+ <li>Select <q>Work offline once download and/or sync is complete</q>.</li>
+ <li>Click OK to download the selected items and then go offline. See
+ <a href="#downloading_and_synchronizing_your_messages">Downloading and
+ Synchronizing Your Messages</a> for more information.</li>
+</ol>
+
+<p>For subsequent offline sessions, you can skip step 1.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="selecting_items_for_offline_viewing">Selecting Items for Offline
+ Viewing</h2>
+
+<p>Before you can read mail and newsgroup messages while offline, you must
+ first select them for downloading. You can set up an entire account for
+ offline use. You can also choose which folders and newsgroups that you
+ want to use offline.</p>
+
+<p><strong>Note</strong>: Keep in mind that selecting more items may increase
+ download time and disk space used.</p>
+
+<p>To select accounts, folders, and newsgroups for offline viewing, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu, choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Choose the Synchronization &amp; Storage category for the account you
+ want to change.</li>
+ <li>Click <q>Advanced</q> to see your IMAP folders, or <q>Select newsgroups
+ for offline use</q> for your subscribed newsgroups.
+
+ <p><strong>Note</strong>: You see only the newsgroups and folders that
+ you&apos;ve already <a
+ href="mailnews_newsgroups.xhtml#subscribing_to_newsgroups">subscribed</a>
+ to. POP accounts and local mail folders don&apos;t appear in the
+ list.</p>
+ </li>
+ <li>Select the items (folders, newsgroups) that you want to make available
+ for offline use.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>Once set, you don&apos;t need to change these settings each time you want to
+ go offline. However, if you do want to change them, you can easily do so
+ before going offline, since the same Select button is available when using
+ the <a href="#downloading_and_synchronizing_your_messages">Download and
+ Sync</a> command.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="downloading_and_synchronizing_your_messages">Downloading and
+ Synchronizing Your Messages</h2>
+
+<p>If you have already selected mail folders and newsgroups for offline use,
+ you are now ready to download and synchronize them. If you haven&apos;t yet
+ selected items to download, you can choose them before you go offline.</p>
+
+<p>If you are not already viewing the Download/Sync Now dialog box, follow
+ these steps:</p>
+
+<p>To download and synchronize your messages, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the File menu, choose Offline, and then choose Download/Sync
+ Now.</li>
+ <li>Select the categories (mail messages or newsgroup messages) that you want
+ to download.
+
+ <p><strong>Important</strong>: You must select at least one category (Mail
+ messages, Newsgroup messages) in order for the download to work. If the
+ checkboxes are disabled, it means that you haven&apos;t yet selected
+ items to download. Use the Select button to select items to download.</p>
+ </li>
+ <li>To send messages in your Unsent Messages folder before going offline,
+ check <q>Send Unsent Messages</q>.</li>
+ <li>To go offline immediately after &brandShortName; Mail &amp; Newsgroups
+ finishes downloading, select <q>Work offline once download and/or sync is
+ complete</q>.</li>
+ <li>To set or change the items to download, click Select. See
+ <a href="#selecting_items_for_offline_viewing">Selecting Items for Offline
+ Viewing</a> for more information. You can skip this step if you&apos;ve
+ already selected items for download.</li>
+ <li>Click OK. &brandShortName; Mail &amp; Newsgroups begins downloading the
+ selected items.</li>
+</ol>
+
+<p>If you chose to work offline once the download completes, then
+ &brandShortName; Mail &amp; Newsgroups immediately switches to offline mode.
+ Otherwise, when you are ready to go offline, click the Online/Offline
+ indicator <img src="images/online.png" alt=""/> in the lower right corner of
+ the Mail window to go offline.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+
+<h2 id="working_offline_and_reconnecting_later">Working Offline and
+ Reconnecting Later</h2>
+
+<p>To work offline and reconnect later, begin from the Mail window.</p>
+
+<p>When you are ready to work offline:</p>
+
+<ol>
+ <li>Click the online/offline indicator <img src="images/online.png" alt=""/>
+ in the lower-right corner of the Mail window. Mail &amp; Newsgroups prompts
+ you to download messages, if you want, before going offline.</li>
+ <li>Click Download to download messages before going offline. If you want to
+ work offline without downloading messages, click Don&apos;t Download.</li>
+</ol>
+
+<p><strong>Note</strong>: Message headers that have been downloaded for reading
+ offline display a darker gray envelope or newsgroup icon.</p>
+
+<p><strong>Tip</strong>: To set &brandShortName; Mail &amp; Newsgroups&apos;
+ download behavior when going offline, open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu, choose Preferences, and then under the Mail &amp; Newsgroups category,
+ select Network &amp; Storage (if no subcategories are visible, double-click
+ Mail &amp; Newsgroups to expand the list). You can choose to have
+ &brandShortName; Mail &amp; Newsgroups prompt you to download messages when
+ going offline, to automatically download messages, or to not download any
+ messages.</p>
+
+<p>To reconnect and synchronize your messages:</p>
+
+<ol>
+ <li>Click the online/offline indicator <img src="images/offline.png"
+ alt=""/> in the lower-right corner of any &brandShortName; window.</li>
+ <li>Open the File menu, choose Offline, and then choose Download/Sync
+ Now.</li>
+</ol>
+
+<p>&brandShortName; Mail &amp; Newsgroups synchronizes your messages with the
+ server by replicating any changes you made while working offline.</p>
+
+<p><strong>Tip</strong>: To set &brandShortName; Mail &amp; Newsgroups&apos;
+ behavior when going online, open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu, choose Preferences, and then choose the Synchronization &amp; Storage
+ category. You can choose to have &brandShortName; Mail &amp; Newsgroups
+ prompt you to send unsent messages, to automatically send unsent messages,
+ or to not send unsent messages.</p>
+
+<p>[<a href="#working_offline">Return to beginning of section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_organizing.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_organizing.xhtml
new file mode 100644
index 0000000000..6a1c08f32d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_organizing.xhtml
@@ -0,0 +1,848 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Organizing Your Messages and Controlling Junk</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+<h1 id="organizing_your_messages">Organizing Your Messages</h1>
+
+<div class="contentsBox">
+ <ul>
+ <li><a href="#creating_a_folder">Creating a Folder</a></li>
+ <li><a href="#renaming_a_folder">Renaming a Folder</a></li>
+ <li><a href="#moving_or_copying_a_folder">Moving or Copying a
+ Folder</a></li>
+ <li><a href="#filing_messages_in_folders">Filing Messages in
+ Folders</a></li>
+ <li><a href="#sharing_folders_with_other_users">Sharing Folders With Other
+ Users (IMAP Only)</a></li>
+ <li><a href="#tagging_messages">Tagging Messages</a></li>
+ <li><a href="#marking_or_flagging_messages">Marking or Flagging
+ Messages</a></li>
+ <li><a href="#using_message_views">Using Message Views</a></li>
+ <li><a href="#creating_message_filters">Creating Message Filters</a></li>
+ <li><a href="#searching_through_messages">Searching Through
+ Messages</a></li>
+ </ul>
+</div>
+
+<h2 id="creating_a_folder">Creating a Folder</h2>
+
+<p>To create a message folder, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the File menu, choose New, and then Folder. You see the New Folder
+ dialog box.</li>
+ <li>Type the name of the folder.</li>
+ <li>Click the drop-down list and choose a folder location and click OK. Your
+ new folder appears in your Mail Folders list.</li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="renaming_a_folder">Renaming a Folder</h2>
+
+<p>To rename an existing folder, begin from the Mail window:</p>
+
+<ol>
+ <li>Select the folder you want to rename.</li>
+ <li>Open the File menu and choose Rename Folder. You see the Rename Folder
+ dialog box.</li>
+ <li>Type the new name and click OK.</li>
+</ol>
+
+<p><strong>Note</strong>: If you rename a folder that you&apos;ve been using to
+ store <a href="#creating_message_filters">filtered messages</a>, the filter
+ will automatically update to use the renamed folder.</p>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="moving_or_copying_a_folder">Moving or Copying a Folder</h2>
+
+<p>You can copy a folder and its contents to another mail account, or move a
+ folder within the same mail account.</p>
+
+<p>To move or copy a folder, begin from the Mail window:</p>
+
+<ol>
+ <li>Select the folder you want to move or copy.</li>
+ <li>Do one of the following:
+ <ul>
+ <li>To move the folder under another folder within the same account, drag
+ the folder over the name of the other folder. The folder you moved
+ becomes a subfolder of the other folder.</li>
+ <li>To copy the folder to another account, drag the folder over the name
+ of another account.</li>
+ <li>To copy the folder under another folder in another account, drag the
+ folder over the name of another folder in another account. The folder
+ you copied becomes a subfolder of the other folder.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="filing_messages_in_folders">Filing Messages in Folders</h2>
+
+<p>You can move messages from one folder to another by using either of these
+ methods:</p>
+
+<ul>
+ <li>Select the message, click the File button on the toolbar, and choose the
+ destination folder.</li>
+ <li>Drag and drop messages into the desired folder.
+
+ <p><strong>Note</strong>: If you drag and drop a message from an IMAP or
+ POP mail server folder to a local folder on your hard drive, the message
+ is moved to the local folder and removed from the server folder.</p>
+ </li>
+</ul>
+
+<p>To copy a message from one folder to another:</p>
+
+<ol>
+ <li>Select the message and right-click to display the pop-up menu.</li>
+ <li>Select <q>Copy To</q> and then select the destination account and folder
+ from the drop-down list.</li>
+</ol>
+
+<p><strong>Tip</strong>: Alternatively, you can copy a message between folders
+ by holding down the Shift key while dragging the message from the message
+ list over another folder.</p>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="sharing_folders_with_other_users">Sharing Folders With Other Users
+ (IMAP Only)</h2>
+
+<p>Users with IMAP mail accounts can share mail folders with other users on the
+ same network. Sharing folders allows several users to see and work with the
+ same messages, similar to a newsgroup. To use shared folders, your IMAP mail
+ server must support Access Control List (ACL) management. Check with your
+ system administrator or help desk if you are not sure that shared folders
+ are supported by your IMAP mail server.</p>
+
+<p>To share a mail folder with other users on your network, or to view sharing
+ information for a folder, begin from the Mail window:</p>
+
+<ol>
+ <li>Within an IMAP account, select a folder that you want to share, or select
+ a folder whose sharing privileges you want to view.
+
+ <p>Folders listed under Local Folders, or folders listed under a POP mail
+ account cannot be shared.</p>
+ </li>
+ <li>Open the Edit menu, and choose Folder Properties.</li>
+ <li>Click the Sharing tab.</li>
+ <li>Click Privileges. You may be prompted to enter your network user name and
+ password.
+
+ <p>The Privileges button is only available if the IMAP mail server allows
+ you to set folder sharing privileges. If this button is not available,
+ you can view the folder sharing privileges for this folder but cannot
+ change them.</p>
+ </li>
+ <li>Follow the instructions on the screen to add users and to set their
+ folder access privileges.
+ <ul>
+ <li><strong>Read privileges</strong>: Users can read messages and copy
+ their contents, but they cannot modify or delete messages, or copy
+ messages into the folder. Users can flag messages as read or unread.
+ See <a href="#marking_or_flagging_messages">Marking or Flagging
+ Messages</a> for instructions on flagging messages.</li>
+ <li><strong>Read and Write privileges</strong>: In addition to Read
+ privileges, users can modify and delete messages. Users can also copy
+ or move messages into the folder.</li>
+ <li><strong>Manage privileges</strong>: In addition to Read and Write
+ privileges, users can add and remove users and change their folder
+ permissions.</li>
+ </ul>
+ </li>
+ <li>Click OK to confirm your changes.</li>
+ <li>Click OK to close the Folder Properties dialog box.</li>
+</ol>
+
+<p>In the list of folders for your mail account, a shared folder displays a
+ distinctive folder icon to indicate that it is shared.</p>
+
+<p>To send a message that tells others how they can subscribe to your shared
+ folder, begin from the Mail window:</p>
+
+<ol>
+ <li>Select the shared folder.</li>
+ <li>Right-click to display a pop-up menu, and choose Copy Folder
+ Location.</li>
+ <li>Click Compose to display a Mail compose window.</li>
+ <li>Click in the message body, open the Edit menu, and choose Paste.</li>
+ <li>Address the message, type a subject, and type the message text. Tell
+ message recipients that they can subscribe to the shared folder by clicking
+ the link you pasted into the message.
+
+ <p>Only message recipients who share the same network will be able to
+ subscribe to your shared folder.</p>
+ </li>
+ <li>Click Send.</li>
+</ol>
+
+<h3 id="subscribing_to_a_shared_folder">Subscribing to a Shared Folder</h3>
+
+<p>Subscribing to a shared folder is similar to subscribing to a newsgroup. To
+ subscribe to a shared folder, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the File menu and choose Subscribe. You see the Subscribe dialog
+ box.</li>
+ <li>If necessary, click the Account drop-down list to choose another IMAP
+ mail account.</li>
+ <li>Select the folder that you want to subscribe to.</li>
+ <li>Click Subscribe or click in the Subscribe column next to the folder. You
+ see a checkmark next to each folder to which you subscribe. Click
+ Unsubscribe to cancel a selection.</li>
+ <li>Click OK. The list of your subscribed folders appears in the Mail
+ window.</li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="tagging_messages">Tagging Messages</h2>
+
+<p>You can apply tags to messages to help you organize and prioritize them.
+ You can apply a standard color and tag text to messages, or you can create
+ your own color and tag text to suit your needs.</p>
+
+<p>One powerful way to use tags is to set up a message filter to
+ automatically tag incoming messages from a specific sender. For example,
+ you can set up a message filter so that incoming messages from your boss are
+ tagged <q>Important</q> and appear in red. See
+ <a href="#creating_message_filters">Creating Message Filters</a> for more
+ information.</p>
+
+<h3 id="applying_a_tag">Applying a Tag</h3>
+
+<p>To apply a tag to a message, begin from the Mail window:</p>
+
+<ol>
+ <li>Select the message you want to tag.</li>
+ <li>Open the Message menu, and choose Tag.</li>
+ <li>Choose the tag you want to apply from the list.</li>
+</ol>
+
+<p>The message summary row changes to the color of the tag with the topmost
+ priority. To see the tag text, you must display the Tags column in the Mail
+ window.</p>
+
+<p><strong>Tip</strong>: To quickly tag messages or remove a tag, select
+ one or more messages and press one of the number keys 1-9 on your keyboard.
+ Press 0 to remove all tags.</p>
+
+<p>To display the Tags column, begin from the Mail window:</p>
+
+<ul>
+ <li>Click the Show/Hide Columns icon <img src="images/columns.png" alt=""/>
+ and select Tags from the list.</li>
+</ul>
+
+<p><strong>Note</strong>: Message tags apply on a per-account basis. For
+ example, if you move or copy a tagged message to another mail account, the
+ tags are not preserved. Similarly, if you forward a tagged message to
+ another recipient, the tags are not preserved. For IMAP mail accounts, if
+ your IMAP server supports user-defined keywords, message tags will persist
+ when you log in to your mail account from a different location.</p>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h3 id="customizing_tags">Customizing Tags</h3>
+
+<p>You can customize tag colors and text and their order to suit your needs.</p>
+
+<p>To customize tags, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Tags. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand the
+ list.)</li>
+ <li>Edit the tag text, or replace it with your own tag text. The tag
+ can be up to 32 characters long.</li>
+ <li>To change the tag color, click the color block next to that tag and
+ select a new color.</li>
+ <li>Click the Move Up and Move Down buttons to reorder the tags. Tags at
+ the top will have higher priority when coloring messages.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>Your changes are immediately applied to all tagged messages in all your
+ mail accounts.</p>
+
+<p><strong>Tip</strong>: To undo all customizations and restore just the
+ default tags' text and colors, follow the steps above to display the tag
+ settings, and click Restore Defaults.</p>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h3 id="sorting_messages_by_tags">Sorting Messages by Tags</h3>
+
+<p>To sort messages by tags, begin from the Mail window:</p>
+
+<ol>
+ <li>To display the Tags column if it is hidden, click the Show/Hide Columns
+ icon <img src="images/columns.png" alt=""/> and select Tags from the
+ list.</li>
+ <li>Click the Tags column to sort messages by tags, and within each tag
+ type, to sort messages by date.</li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h3 id="removing_tags">Removing Tags</h3>
+
+<p>To remove a message tag, begin from the Mail window:</p>
+
+<ol>
+ <li>Select one or more tagged messages.</li>
+ <li>Open the Message menu, and choose Tag.</li>
+ <li>Choose the tag you want to remove or <q>None</q> to remove all tags
+ from this message.</li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="marking_or_flagging_messages">Marking or Flagging Messages</h2>
+
+<p>You might want to mark a message you&apos;ve read as unread if you later
+ want to re-read the message or respond to it.</p>
+
+<p>To mark a message as unread, begin from the Mail window:</p>
+
+<ol>
+ <li>Select a Mail or Newsgroup folder to display its messages.</li>
+ <li>Click in the Read column of each message you want to mark as unread.
+ Messages marked as unread display a <img src="images/mail_unread.png"
+ alt=""/> symbol in the Read column. Messages marked as read display a
+ <img src="images/mail_read.png" alt=""/> symbol in the Read column. If the
+ Read column is not visible, click the Show/Hide Columns icon
+ <img src="images/columns.png" alt=""/> and select Read from the list.</li>
+</ol>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/mail_read_column.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td><strong>Read column</strong></td>
+ </tr>
+</table>
+
+<p>You can flag messages that you later want to download for
+ <a href="mailnews_offline.xhtml#downloading_selected_or_flagged_messages_for_offline_use">offline
+ use</a>.</p>
+
+<p>To flag messages, begin from the Mail window:</p>
+
+<ol>
+ <li>Select a Mail or Newsgroup folder to display its messages.</li>
+ <li>Click in the Flag column of each message you want to download. A flag
+ <img src="images/mail_flag.png" alt=""/> appears where you clicked to
+ indicate that the message has been flagged. If the Flag column is not
+ visible, click the Show/Hide Columns icon <img src="images/columns.png"
+ alt=""/> and select Flag from the list.</li>
+</ol>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/mail_flag_column.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td><strong>Flag column</strong></td>
+ </tr>
+</table>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="using_message_views">Using Message Views</h2>
+
+<p>You can apply preset or custom message views to help you manage messages by
+ filtering displayed messages.</p>
+
+<p>To use a message view, open the View menu and choose Messages. Choose an
+ option from the submenu.</p>
+
+<ul>
+ <li><strong>All</strong>: Choose this option to view all messages.</li>
+ <li><strong>Unread</strong>: Choose this option to view only unread
+ messages.</li>
+ <li><strong>Tags</strong>: Choose a <a href="#tagging_messages">tag</a>
+ to view tagged messages.</li>
+ <li><strong>Custom Views</strong>: Choose a custom view. By default you have
+ five preset views: <q>People I Know</q>, <q>Recent Mail</q>, <q>Last 5
+ Days</q>, <q>Not Junk</q>, and <q>Has Attachments</q>.</li>
+ <li><strong>Customize</strong>: Choose this option to view or modify
+ settings for custom views or create your own custom view.</li>
+</ul>
+
+<p><strong>Tip</strong>: You can quickly change the message view from the View
+ box in the Search Bar. If you do not see the Search Bar, open the View menu,
+ choose Show/Hide, and then choose Search Bar.</p>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/mail_quicksearch.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 80px;"></td>
+ <td><strong>Quick mail search bar</strong></td>
+ </tr>
+</table>
+
+<h3 id="creating_a_custom_view">Creating a Custom View</h3>
+
+<p>You can create custom message views to only display messages matching
+ certain criteria.</p>
+
+<p>To change or create a custom message view:</p>
+
+<ol>
+ <li>Open the View menu, choose Messages, and then choose Customize.</li>
+ <li>To create a new view, click New. To modify a view, select a view and
+ click Edit.</li>
+ <li>Type a name for the message view.</li>
+ <li>Select the matching option you want Mail to use: <q><em>all</em> of the
+ following</q> conditions (criteria) you choose, or <q><em>any</em> of the
+ following</q>.</li>
+ <li>Use the drop-down lists to choose the search criteria (for example,
+ <q>Subject</q>, <q>Sender</q>, <q>contains</q>, <q>doesn&apos;t
+ contain</q>) and then type the text or phrase you want to match.
+
+ <p><strong>Tip</strong>: To search for messages that contain a header not
+ listed in the first drop-down menu (for example, if you want to search
+ for messages that include the header Resent-From), choose Customize and
+ type the header you want to search for. &brandShortName; Mail &amp;
+ Newsgroups adds your custom header to the drop-down list, so you can then
+ choose it to search for matching entries. Make sure you enter the custom
+ header correctly, since Mail will only find entries that exactly match
+ what you type.</p>
+ </li>
+ <li>Click More to add criteria and Fewer to remove them.</li>
+ <li>Click OK to confirm your settings.</li>
+ <li>Click OK in the Customize Message Views dialog box. The selected view
+ setting applies automatically.</li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="creating_message_filters">Creating Message Filters</h2>
+
+<p>Message filters allow you to manage and organize your messages. You can
+ create message filters that &brandShortName; Mail &amp; Newsgroups uses to
+ automatically perform certain actions on incoming messages based on criteria
+ you specify. For example, you can create a message filter that automatically
+ moves incoming messages to a particular folder. Message filters operate on a
+ per-account basis.</p>
+
+<p>If you are not already viewing the Message Filters dialog box, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Message Filters. You see the Message
+ Filters dialog box.</li>
+ <li>If you have multiple mail accounts, choose the one to which you want to
+ apply the filter.</li>
+ <li>Click New. You use the Filter Rules dialog box to specify the types of
+ messages to act on, and the actions you want the filter to perform.</li>
+ <li>Type a name for the filter.</li>
+ <li>Select when you want the filter to be applied. This setting enables you
+ to define some filters to be applied in an automatic way (when checking
+ mail), on demand (manually run), or both. <q>After classification</q> means
+ that junk and phishing controls will be run before applying the
+ filter.</li>
+ <li>Select the matching option you want Mail to use: <q><em>all</em> of the
+ following</q> conditions (criteria) you choose, <q><em>any</em> of the
+ following</q> conditions you choose, or <q><em>all messages</em></q>.</li>
+ <li>Use the drop-down lists to choose the search criteria (for example,
+ <q>Subject</q>, <q>Sender</q>, <q>contains</q>, <q>doesn&apos;t
+ contain</q>) and then type the text or phrase you want to match.
+
+ <p><strong>Tip</strong>: To search for messages that contain a header not
+ listed in the first drop-down menu (for example, if you want to search
+ for messages that include the header Resent-From), choose Customize and
+ type the header you want to search for. &brandShortName; Mail &amp;
+ Newsgroups adds your custom header to the drop-down list, so you can then
+ choose it to search for matching entries. Make sure you enter the custom
+ header correctly, since Mail will only find entries that exactly match
+ what you type.</p>
+ </li>
+ <li>Click <q>+</q> to add criteria and <q>-</q> to remove them.</li>
+ <li>Use the list to choose the action you want the filter to perform on the
+ messages (for example, Move Message To). Use <q>+</q> and <q>-</q> to add
+ or remove additional actions.
+
+ <p><strong>Tip</strong>: To automatically tag incoming messages, choose
+ <q>Tag Message</q> from the drop-down list.</p>
+
+ <p><strong>Tip</strong>: Message filters are applied one after another. It
+ could be that you don&apos;t want all filters to be run if one or more
+ messages match some conditions. For instance, you may want to tag all
+ messages from your boss&apos;s email address as <q>Important</q>, and
+ you may want all messages containing the word <q>Memorandum</q> in their
+ subject to be moved to a folder named <q>Pending Reads</q>, but you
+ don&apos;t want any message from your boss to be moved to another folder,
+ even if it contains <q>Memorandum</q> in the subject. So the first
+ message filter you define should match your boss&apos;s email address,
+ and would contain two actions: <q>Tag Message</q> as <q>Important</q> and
+ <q>Stop Filter Execution</q>.</p>
+ </li>
+ <li>If you have chosen <q>Move</q> or <q>Copy</q> message to a folder, then
+ select a destination folder in which to store the messages, or create a
+ new folder.</li>
+ <li>Click OK to confirm your settings.</li>
+ <li>To run filters on existing messages in a folder, select the folder
+ in the bottom dropdown list and click the <q>Run Now</q> button.</li>
+ <li>Click OK in the Message Filters dialog box. The filter begins filtering
+ incoming messages as soon as you click OK.</li>
+</ol>
+
+<p><strong>Note</strong>: You can also run message filters manually at any
+ time. In the Mail window, choose Tools, and then select Run Filters on Folder
+ to apply filters to the current folder, or Run Filters on Message to apply
+ filters to the selected message (if any).</p>
+
+<p>To manage your filters, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Message Filters. You see the Message
+ Filters dialog box.</li>
+ <li>If you have multiple mail accounts, choose the one to which you want to
+ apply the filter.</li>
+ <li>Choose from the following:
+ <ul>
+ <li><strong>To turn a filter on or off</strong>: Click the checkbox to
+ the right of the filter name to enable it, or click it again to turn it
+ off.</li>
+ <li><strong>To edit a filter</strong>: Select the filter name and click
+ Edit (or double-click the filter name). Use the Filter Rules dialog box
+ to make your changes.</li>
+ <li><strong>To delete a filter</strong>: Select the filter name and click
+ Delete.</li>
+ <li><strong>To change the order in which filters are applied</strong>: In
+ the filter list, click a filter&apos;s name, and click <q>Move Up</q>
+ or <q>Move Down</q> to move it.
+
+ <p><strong>Note</strong>: Filters are applied to each incoming message
+ in the order you choose, until a filter action results in the message
+ being deleted or moved from the Inbox folder.</p>
+ </li>
+ </ul>
+ </li>
+ <li>Click OK when you are done managing your filters. If you created a new
+ filter, it begins filtering incoming messages as soon as you click OK.</li>
+</ol>
+
+<p><strong>Note</strong>: If you delete a folder that you&apos;ve been using to
+ store filtered messages, the filter will no longer work. Incoming messages
+ that match the filter criteria will appear in your Inbox. If you rename or
+ move the folder, the filter will automatically update to use the renamed or
+ moved folder.</p>
+
+<p><strong>Tip</strong>: If you have existing messages that you want to move to
+ another folder, use the Run Filters on Messages option in the Tools menu.</p>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h3 id="filtering_messages_from_a_specific_sender">Filtering Messages From a
+ Specific Sender</h3>
+
+<p>You can quickly create a filter for messages from a particular sender. For
+ example, if you want to automatically move all incoming messages from your
+ child&apos;s teacher into a folder called <q>School</q>, you can quickly set
+ up a filter to do this.</p>
+
+<p>To create a filter for messages from a specific sender, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Select a message from a specific sender.</li>
+ <li>Open the Message menu and choose Create Filter From Message. Or, in the
+ message header pane, right click the sender name and choose Create Filter
+ From.</li>
+ <li>You see the Filter Rules dialog box. Using the sender&apos;s email
+ address, &brandShortName; prefills the filter matching criteria and the
+ filter action (Move Message to). You can change or add new rules to the
+ matching criteria.</li>
+ <li>Choose a destination folder in which to store the incoming messages from
+ the specified sender, or create a new folder. You can also choose other
+ actions for this filter, or change the default one.</li>
+ <li>Note that, if you leave the filter name empty, &brandShortName; will
+ provide a name for it based on the first criterion.</li>
+ <li>Click OK to confirm your settings. You see the
+ <a href="#creating_message_filters">Message Filters</a> dialog box, where
+ you can create, delete, or edit message filters.</li>
+ <li>Click OK. The filter begins filtering incoming messages from the
+ specified sender as soon as you click OK.</li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h2 id="searching_through_messages">Searching Through Messages</h2>
+
+<p>&brandShortName; Mail &amp; Newsgroups lets you quickly find text in a
+ single message, search messages by subject or sender, or use a combination of
+ criteria to perform a thorough search through all messages in a specific mail
+ folder, newsgroup, or account.</p>
+
+<p>To locate text in a single message, begin from the Mail window:</p>
+
+<ol>
+ <li>Select the message, open the Edit menu, and choose Find in This
+ Message.</li>
+ <li>Type the text that you want to locate in the dialog box.</li>
+ <li>Click Find to locate the first occurrence of the text.</li>
+ <li>Continue clicking Find to locate additional occurrences, or click Cancel
+ when you are done.</li>
+ <li>Choose Find Again from the Edit menu to continue searching for the text
+ throughout the rest of the message.</li>
+</ol>
+
+<p>To quickly search for messages in a selected folder by subject or sender,
+ begin from the Mail window:</p>
+
+<ol>
+ <li>To the right of <q>Subject or Sender contains:</q>, type the subject text
+ or sender name that you want to find. You can type only part of the subject
+ or sender, or you can type the exact word or name that you want to find.
+
+ <p>As soon as you stop typing, &brandShortName; Mail &amp; Newsgroups
+ displays only those messages in the selected folder where the subject or
+ sender contains the search text you entered.</p>
+ </li>
+ <li>Click Clear to erase the search text and show all messages in the
+ selected folder.</li>
+</ol>
+
+<h3 id="searching_for_specific_messages">Searching for Specific Messages</h3>
+
+<p>You can search mail folders or newsgroups for specific messages. If you are
+ not already viewing the Search Messages dialog box, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Search Messages. You see the Search
+ Messages dialog box.</li>
+ <li>Next to <q>Search for messages in</q>, choose the account, newsgroup, or
+ folder through which you want to search.</li>
+ <li>Select <q>Search subfolders</q> to include all subfolders in the
+ search.</li>
+ <li>Next to <q>Perform search operations on</q>, select an option where to
+ search for messages in newsgroups or IMAP accounts:
+ <ul>
+ <li>Choose <q>Local system</q> to use only the information stored locally
+ for the search without any network activity. This mode includes all
+ major message headers. You can&apos;t search the message body locally
+ unless the account and its folders have been set up for
+ <a href="mailnews_account_settings.xhtml#synchronization_and_storage">synchronization</a>.
+ </li>
+ <li>Choose <q>Remote server</q> to perform all searches on the server
+ where the messages are located. This will allow you to also search for
+ contents in message bodies which have not been synchronized.</li>
+ </ul>
+ <p><strong>Note</strong>: This menu will be disabled if it&apos;s not
+ possible to search remotely on the server (e.g., for POP accounts).</p>
+ </li>
+ <li>Select which matching option Mail &amp; Newsgroups will use to search for
+ messages that match all or at least one of the conditions (criteria) that
+ you choose.</li>
+ <li>Use the drop-down lists to indicate the search criteria (for example,
+ <q>Subject</q> and <q>contains</q>) and then type the text or phrase that
+ you want to match.
+
+ <p><strong>Tip</strong>: To search for messages that contain a header not
+ listed in the first drop-down menu (for example, if you want to search
+ for messages that include the header Resent-From), choose Customize and
+ type the header you want to search for. &brandShortName; Mail &amp;
+ Newsgroups adds your custom header to the drop-down list, so you can then
+ choose it to search for matching entries. Make sure you enter the custom
+ header correctly, since Mail will only find entries that exactly match
+ what you type.</p>
+ </li>
+ <li>Click More to add criteria and Fewer to remove them.</li>
+ <li>Click Search to begin, or click Clear to reset your entries. The search
+ results appear in lower part of the Search Messages dialog box.
+ <ul>
+ <li>To open a message so you can read it, select the message and click
+ Open, or double-click the message.</li>
+ <li>To sort the messages in a different order, click the column that you
+ want to sort by.</li>
+ <li>To move or copy a message in the Results area to another folder,
+ select the message and then choose the destination folder from the File
+ drop-down list. If the destination folder is within the same account,
+ the message is moved to that folder. If the destination folder is
+ within a different account, the message is copied to that folder.</li>
+ <li>To delete a message in the Results area, select the message and then
+ click Delete.</li>
+ <li>To open the folder where the message is stored, select the message
+ and click Open Message Folder.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>[<a href="#organizing_your_messages">Return to beginning of section</a>]</p>
+
+<h1 id="controlling_junk_mail">Controlling Junk Mail</h1>
+
+<p>This section describes how to use &brandShortName;&apos;s Junk Mail Controls
+to filter unwanted mail, and how phishing detection works.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#using_junk_mail_controls">Using Junk Mail Controls</a></li>
+ <li><a href="#junk_controls_options">Junk Mail Controls Options</a></li>
+ <li><a href="#junk_controls_and_filters">Junk Mail Controls and
+ Filters</a></li>
+ <li><a href="#phishing_detection">Phishing Detection</a></li>
+ </ul>
+</div>
+
+
+<h2 id="using_junk_mail_controls">Using Junk Mail Controls</h2>
+
+<p>&brandShortName;&apos;s Junk Mail Controls feature can evaluate your
+ incoming messages and identify possible junk (or unsolicited) messages. The
+ feature uses the Bayesian classification method. You first train
+ &brandShortName; by showing it a bunch of mail that is junk, and a bunch of
+ mail that is not. Then, you let it auto-classify new mail for you. If
+ &brandShortName; makes any mistakes, you can correct them.</p>
+
+<p>To use Junk Mail Controls:</p>
+
+<ol>
+ <li>First, train &brandShortName; to recognize Junk messages and Non-Junk
+ messages. There are three ways to toggle junk status of the selected
+ message(s):
+ <ul>
+ <li>Open the Message menu, select <q>Mark</q> and choose <q>As Junk</q>
+ or <q>As Not Junk</q>.</li>
+ <li>Click on the Junk toolbar button.</li>
+ <li><img src="images/mail_junk_column.png" style="float:right" alt=""/>
+
+ <p>Click to toggle the Junk Status column in the message list. (If you
+ do not see it, click the right-most button (
+ <img src="images/columns.png" alt=""/>) in the list header bar and
+ select Junk Status from the pop-up menu.)</p>
+ </li>
+ </ul>
+
+ <p>When you toggle junk status, a trash-can icon will appear or disappear
+ in the Junk status column to indicate the junk status of the selected
+ message.</p>
+ </li>
+
+ <li>Open the Edit menu, and choose Mail &amp; Newsgroups Account Settings.
+ You see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Click the Junk Settings category for your mail account.</li>
+ <li>Enable the feature and &brandShortName; will automatically classify
+ incoming messages. (See
+ <a href="#junk_controls_options">Junk Mail Controls Options</a>.
+ Details on the other settings there can be found in the
+ <a href="mailnews_account_settings.xhtml#junk_settings">Junk Settings</a>
+ preference panel description.)
+ </li>
+ <li>If you have trained it on virus mail, consider disabling the white
+ listing (many mail viruses send bulk messages to people in the address book
+ of the infected computer).</li>
+ <li>Make sure to correct the Junk Mail Controls when it incorrectly labels
+ messages either as junk or not junk.</li>
+ <li>To analyze existing messages, select messages, open the Tools menu
+ and choose <q>Run Junk Mail Controls</q>.</li>
+</ol>
+
+<p><strong>Note</strong>: &brandShortName; will only run Junk Mail Controls
+ when the training database has information on non-Junk messages. If Junk Mail
+ Controls do not work, select some messages and explicitly mark them as Not
+ Junk.</p>
+
+<p>[<a href="#controlling_junk_mail">Return to beginning of section</a>]</p>
+
+<h2 id="junk_controls_options">Junk Mail Controls Options</h2>
+
+<p>To fine-tune how Junk Mail Controls work, use the
+ <a href="mailnews_preferences.xhtml#junk_and_suspect_preferences">Junk &amp;
+ Suspect Mail preference panel</a> for account-independent settings and the
+ account manager's <a href="mailnews_account_settings.xhtml#junk_settings">Junk
+ Settings</a> for settings of a specific mail account.</p>
+
+<p>[<a href="#controlling_junk_mail">Return to beginning of section</a>]</p>
+
+<h2 id="junk_controls_and_filters">Junk Mail Controls and Filters</h2>
+
+<p>Junk Mail Controls run after mail filters (unless you set the filter to run
+ after classification, where <q>classification</q> includes junk and phishing
+ scanning) and apply only to the Inbox folder and its sub-folders. Use this to
+ your advantage, for example, you can filter mail you are sure not to be Junk
+ to a special folder outside of Inbox so that the messages will not be
+ classified as Junk (especially useful if you subscribe to newsletters or if
+ you are on a moderated mailing list).</p>
+
+<p>[<a href="#controlling_junk_mail">Return to beginning of section</a>]</p>
+
+<h2 id="phishing_detection">Phishing Detection</h2>
+
+<p>Phishing is a particularly common fraudulent business scheme in which
+ a party creates counterfeit websites designed to trick recipients into
+ divulging personal data such as credit card numbers, account usernames,
+ passwords and social security numbers. Hijacking brand names of banks,
+ e-retailers and credit card companies, phishers often convince
+ recipients to respond.</p>
+
+<p>In many cases, you&apos;ll receive a link to a phishing page via an email
+ which claims to come from an official-looking address. You can also end up
+ at these pages by following links that you find on the Web or in IM
+ messages.</p>
+
+<p><strong>Tip</strong>: Since a forged <a href="glossary.xhtml#url">URL</a>
+ can look very similar to a genuine one, it&apos;s safer to use a bookmark
+ you&apos;ve created or to type the URL into the location bar by hand instead
+ of following a link in an email message. Always consider the risk of a forged
+ URL if you&apos;re asked to log in or provide private information on a
+ website.</p>
+
+<p>&brandShortName; Mail phishing detector is enabled by default. When it
+ encounters a mail which seems to be scam, it will show a warning bar in the
+ message window.</p>
+
+<p>If you think that the email is a valid one, you can click on the <q>Not
+ Scam</q> button, and the warning bar will disappear.</p>
+
+<p>When a user clicks on a link in an email that appears to be a phishing URL,
+ &brandShortName; will prompt the user with a dialog box before the website
+ is opened.</p>
+
+<p>This prompt will appear if either of the following is true: the host name of
+ the actual URL is an <a href="glossary.xhtml#ip_address">IP address</a>, or
+ the link text is a URL whose host name does not match the host name of the
+ actual URL.</p>
+
+<p><strong>Note</strong>: Phishing detection has a higher precedence than Junk
+ Mail detection.</p>
+
+<p>For more technical details on this subject, see the online document
+ <a href="http://www.honeynet.org/papers/phishing/">Know your Enemy:
+ Phishing</a>.</p>
+
+<p>[<a href="#controlling_junk_mail">Return to beginning of section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_preferences.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_preferences.xhtml
new file mode 100644
index 0000000000..27e1d5f54e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_preferences.xhtml
@@ -0,0 +1,792 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Mail &amp; Newsgroups Preferences</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="mail_and_newsgroup_preferences">Mail &amp; Newsgroups Preferences</h1>
+
+<p>The sections listed below describe the Mail &amp; Newsgroups preferences
+ that apply to all your mail and newsgroup accounts. To see these
+ preferences:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Double-click Mail &amp; Newsgroups to expand the list.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#mail_and_newsgroups">Mail &amp; Newsgroups</a></li>
+ <li><a href="#message_display">Message Display</a></li>
+ <li><a href="#notifications">Notifications</a></li>
+ <li><a href="#composition">Composition</a></li>
+ <li><a href="#send_format">Send Format</a></li>
+ <li><a href="#addressing_preferences">Addressing</a></li>
+ <li><a href="#junk_and_suspect_preferences">Junk &amp; Suspect Mail</a></li>
+ <li><a href="#tags">Tags</a></li>
+ <li><a href="#return_receipts_preferences">Return Receipts</a></li>
+ <li><a href="#text_encoding">Text Encoding</a></li>
+ <li><a href="#network_and_storage_preferences">Network &amp; Storage
+ Preferences</a></li>
+ </ul>
+</div>
+
+<h2 id="mail_and_newsgroups">Mail &amp; Newsgroups Preferences - Mail &amp;
+ Newsgroups</h2>
+
+<p>This section describes the main Mail &amp; Newsgroups preferences. If you
+ are not already viewing the Mail &amp; Newsgroups main preferences, follow
+ these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Click the Mail &amp; Newsgroups category.</li>
+</ol>
+
+<ul>
+ <li><strong>Confirm when moving folders to the Trash</strong>: Choose to
+ allow Mail &amp; Newsgroups to prompt you before deleting folders.</li>
+ <li><strong>Remember the last selected message</strong>: Choose this option
+ if you want &brandShortName; to select the message you had selected last
+ before leaving a folder when you reenter a folder.</li>
+ <li><strong>Preserve threading when sorting messages</strong>: Select this
+ option if you want &brandShortName; to preserve the threaded message
+ grouping
+ <a href="mailnews_using_mail.xhtml#sorting_and_threading_messages">when
+ sorting messages</a>. If it is not selected, &brandShortName; automatically
+ displays the messages unthreaded when you sort them by clicking on the
+ column headers.</li>
+ <li><strong>Hide the mail tab bar when only one tab is open</strong>: Select
+ this to display the Tabbed Mail bar only when more then one mail tab is
+ open.</li>
+ <li><strong>Switch to new tabs when opened</strong>: Select this to make
+ &brandShortName; switch to the new tab when opened using <q>Open in New
+ Tab</q> or <q>Open Message in New Tab</q> context menu options.</li>
+ <li><strong>Only check for new mail after opening Mail &amp;
+ Newsgroups</strong>: By default, &brandShortName; checks for new messages
+ even if only a browser window is open. Choose this option if you want to
+ delay checking for new messages until after the Mail &amp; Newsgroups window
+ has been opened at least once (be it automatically <a
+ href="cs_nav_prefs_appearance.xhtml#appearance">on startup</a> or
+ manually).</li>
+ <li><strong>Open tabs instead of windows for</strong>:
+ <ul>
+ <li><strong>Middle-click or
+ <span class="mac"><kbd>Cmd</kbd>+<kbd>Return</kbd></span>
+ <span class="noMac"><kbd>Ctrl</kbd>+<kbd>Enter</kbd></span> on a message
+ or a folder</strong>: Select this to open messages or folders in a new
+ tab when clicking with the middle mouse button or holding down the
+ <kbd class="mac">Command</kbd><kbd class="noMac">Ctrl</kbd> key and
+ pressing <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.
+ </li>
+ <li><strong>Double-click on a message</strong>: Select this to open
+ messages in a new tab when double-clicking on them.</li>
+ </ul>
+ </li>
+ <li><strong>When Mail launches, show the Start Page in the message
+ area</strong>: Select this to enable the Start Page. The Start Page
+ appears in the message area when you first open &brandShortName; Mail &amp;
+ Newsgroups. This page is the default page, but you can enter a different
+ web page or URL of your choice. To disable the Start Page, deselect this
+ option. Click Restore Default to return to the original page provided by
+ &brandShortName;.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="message_display">Mail &amp; Newsgroups Preferences - Message
+ Display</h2>
+
+<p>Message Display preferences allow you to choose how messages are displayed
+ in all accounts. If you are not already viewing the Message Display settings,
+ follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Message Display. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>When opening messages, display them in</strong>: These options
+ control the behavior when opening messages in a separate window, e.g., by
+ double-clicking on it in the message list of a folder:
+ <ul>
+ <li><strong>A new message window</strong>: Choose this if you want to
+ open each message in a new window.</li>
+ <li><strong>An existing message window</strong>: Choose this if you want
+ to reuse an already opened message window for the next mail.</li>
+ <li><strong>Close message window when deleting a message</strong>: Check
+ this if you want the message window to be closed automatically when
+ the message you are viewing in it is deleted.</li>
+ </ul>
+ </li>
+ <li><strong>Block images and other content from remote sources</strong>:
+ Select this checkbox if you do not want to display remote images and other
+ content in received messages, except from senders in your address books
+ whom you have allowed. (This checkbox is selected by default.)</li>
+ <li><strong>Show only display name for people in my address book</strong>:
+ Check this if you want to save some space in the message headers where
+ email addresses are shown. If selected, known senders and recipients are
+ only listed with their display names, their email addresses are hidden.
+ You can see the full email addresses when hovering over such entries with
+ the mouse.</li>
+ <li><strong>Automatically mark messages as read</strong>: Check this option
+ to mark messages as read once they are opened. (This checkbox is selected
+ by default.)
+ <ul>
+ <li><strong>Only after displaying for [__] seconds</strong>: Check this
+ option if you do not want a message to be marked as read when you are
+ only taking a brief look at it. Enter the number of seconds you want a
+ message to be displayed before it gets marked as read automatically.
+ If this box is not checked, messages are marked as read as soon as
+ they are opened.</li>
+ </ul>
+ </li>
+ <li><strong>Plain Text Messages</strong>: These settings control how
+ plain-text messages are displayed. These options do not have any effect
+ when viewing HTML (rich-text) messages, or when writing new messages.
+ <ul>
+ <li><strong>Font</strong>: Select the font you prefer for viewing
+ plain-text messages: fixed width or variable width.</li>
+ <li><strong>Wrap text to fit window width</strong>: Select this so that
+ incoming messages are word-wrapped to fit the width of your Mail
+ window.</li>
+ <li><strong>Display emoticons as graphics</strong>: Select this so that
+ when you receive messages that contain emoticons (also called smiley
+ faces) Mail &amp; Newsgroups can convert them to graphics, for example:
+ <table border="1">
+ <tr align="center">
+ <td><strong>This</strong>:</td>
+ <td><strong>Converts to</strong>:</td>
+ </tr>
+ <tr align="center">
+ <td>:-)</td>
+ <td><span class="smiley moz-smiley-s1"/></td>
+ </tr>
+ <tr align="center">
+ <td>:)</td>
+ <td><span class="smiley moz-smiley-s1"/></td>
+ </tr>
+ <tr align="center">
+ <td>:-(</td>
+ <td><span class="smiley moz-smiley-s2"/></td>
+ </tr>
+ <tr align="center">
+ <td>:(</td>
+ <td><span class="smiley moz-smiley-s2"/></td>
+ </tr>
+ <tr align="center">
+ <td>;-)</td>
+ <td><span class="smiley moz-smiley-s3"/></td>
+ </tr>
+ <tr align="center">
+ <td>;-p</td>
+ <td><span class="smiley moz-smiley-s4"/></td>
+ </tr>
+ </table>
+ This option also controls some common plain-text formatting conventions:
+ <table border="1">
+ <tr align="center">
+ <td><strong>This</strong>:</td>
+ <td><strong>Displays as</strong>:</td>
+ </tr>
+ <tr align="center">
+ <td>mm^2</td>
+ <td>mm<sup>2</sup></td>
+ </tr>
+ <tr align="center">
+ <td>+/-1</td>
+ <td>±1</td>
+ </tr>
+ </table>
+ </li>
+ <li><strong>Settings for quoted messages</strong>: Choose a different
+ font style, size, and/or color for quoted plain-text messages to more
+ easily distinguish quoted text (usually the content of a message for
+ which the sender replied to you, or quoted parts of a message you
+ replied to).</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="notifications">Mail &amp; Newsgroups Preferences - Notifications</h2>
+
+<p>Notification preferences allow you to select different methods for informing
+ you on arrival of a new message. So you don&apos;t have to always look in the
+ folders.</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Notifications. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>Show an alert for [__] seconds</strong>: Select this if you want
+ &brandShortName; Mail &amp; Newsgroups to display an alert on your desktop
+ when new messages arrive. This alert is usually located above your system
+ tray in the lower right corner of your screen. The alert only appears once
+ when new messages arrive, stays for the specified amount of time, and
+ won&apos;t appear again until you have visited one of your folders with new
+ mail, read one of the new messages, or checked for new messages manually.
+ <table>
+ <tr>
+ <td colspan="2"><img src="images/mail_newmail_alert.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 20px;"></td>
+ <td><strong>New mail desktop alert</strong></td>
+ </tr>
+ </table>
+ The following options determine which message-specific items are shown in
+ the alert for each new message:
+ <ul>
+ <li><strong>Show a preview of the message text</strong>: Check this to
+ show the first few words of the message received in the alert.</li>
+ <li><strong>Show the subject</strong>: Check this to show the subject
+ line of the message in the alert.</li>
+ <li><strong>Show the sender</strong>: Check this to show the sender&apos;s
+ name or email address in the alert.</li>
+ </ul>
+ <ul class="unix">
+ <li><strong>Use the operating system&apos;s desktop
+ notifications</strong>: Select this to use the operating system&apos;s
+ notification system (e.g., libnotify), if available. Note that some
+ configuration options for the alert message may not be supported.</li>
+ <li><strong>Use &brandShortName;&apos;s own notification
+ windows</strong>: Select this to use the built-in
+ <a href="glossary.xhtml#xul">XUL</a>-based notification system for
+ new-mail alerts. All configuration options are supported.</li>
+ </ul>
+ <p>When the alert appears, clicking an entry for a message will take you
+ to the respective folder and opens that message. You can close the alert
+ using the <strong>x</strong> button.
+ </p>
+
+ <p>The new message alert will continue to work even after you close the
+ Mail window (as long as another &brandShortName; window is open).
+ </p>
+ </li>
+
+ <li class="win"><strong>Show a tray icon</strong>: Select this if you want
+ &brandShortName; Mail &amp; Newsgroups to display an icon in your system
+ tray (which is usually found in the lower right corner of your screen) when
+ new messages arrive. This icon will stay in the system tray until you have
+ visited one of your folders with new mail, read one of the new messages,
+ or checked for new messages manually.
+ <p style="text-indent: 20px"><img src="images/mail_newmail_trayicon.png"
+ alt=""/>&nbsp;&nbsp;<strong>New mail tray icon</strong></p>
+ <p>When the icon appears, double-clicking it will open the &brandShortName;
+ Mail &amp; Newsgroups main window.</p>
+ <p><strong>Note</strong>: On Windows 7 and above, the Notification Area
+ Icons settings for &brandShortName; must read <q>Show icon and
+ notifications</q> for the icon to stay visible. Otherwise, it may be
+ hidden after a short period of time.</p>
+ <ul>
+ <li><strong>Show a balloon alert</strong>: As an alternative to the
+ desktop alert described above, you can use the operating system&apos;s
+ balloon notification. When new mail arrives, this will show the icon
+ along with a balloon indicating the number of new messages available
+ for the account.
+ <p style="text-indent: 20px"><img src="images/mail_newmail_balloon.png"
+ alt=""/><strong>Balloon&nbsp;alert</strong></p>
+ <p>When the balloon appears, clicking into it will open the Main &amp;
+ Newsgroup main window. The duration of the balloon alert depends on
+ the respective setting in your operating system. You can close the
+ balloon using the <strong>x</strong> button.</p>
+
+ <p>The new message alert will continue to work even after you close the
+ Mail window (as long as another &brandShortName; window is open).</p>
+
+ <p><strong>Note</strong>: <q>Show an alert for [__] seconds</q> and
+ <q>Show a balloon alert</q> cannot be selected at the same time.
+ Checking one option will uncheck the other.</p>
+ </li>
+ </ul>
+ </li>
+
+ <li class="mac"><strong>Animate the Dock icon</strong>: Select this if you
+ want Mail &amp; Newsgroups to bounce the &brandShortName; Dock icon when
+ new messages arrive.</li>
+ <li><strong>Play a sound</strong>: Select this if you want &brandShortName;
+ Mail &amp; Newsgroups to play a sound when new messages arrive. You can
+ choose between the default system sound and a custom sound in WAV format.
+ If you choose the latter, use the Browse button to select the sound file in
+ the file locator. Click on the Play button to listen to the chosen sound
+ file.
+
+ <p>Once &brandShortName; Mail has been started, the new messages sound will
+ continue to work even after you close the Mail window (as long as another
+ &brandShortName; window is open).</p>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="composition">Mail &amp; Newsgroups Preferences - Composition</h2>
+
+<p>Composition preferences affect how you create messages (for example,
+ forwarding options and address autocompletion) in all accounts. If you are
+ not already viewing the Composition settings, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Composition. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>Forward Messages</strong>: Choose how you want forwarded
+ message text to appear: as an attachment or inline (in the body of your
+ message).</li>
+ <li><strong>Quote attachments viewed inline in replies</strong>: If this
+ option is checked, then attachments (such as images, text, or messages)
+ viewed inline are included in the quote when replying to an email.</li>
+ <li><strong>Automatically save the message every [__] minutes</strong>:
+ Choose this option if you want Mail &amp; Newsgroups to save the message
+ you are currently composing automatically at the given interval. After a
+ computer crash or program failure you can find the latest saved version of
+ the message in your Drafts folder.</li>
+ <li><strong>Confirm when using keyboard shortcut to send message</strong>:
+ Check this option if want to be asked if you&apos;re sure to be ready to
+ send the message when you&apos;re pressing Ctrl+Enter in message editor.
+ This may help you avoid accidentally sending the message if you enter the
+ keyboard shortcut by mistake when composing a message.</li>
+ <li><strong>Select reply header type</strong>:
+ Select the type of reply header you wish to use. There are four choices
+ available:
+ <ol>
+ <li>No Reply Header</li>
+ <li>[Author] wrote:<br/>
+ This setting is based on the mailnews.reply_header_authorwrotesingle
+ preference.</li>
+ <li>On [date], [Author] wrote:<br/>
+ This setting is based on the mailnews.reply_header_ondateauthorwrote
+ preference.</li>
+ <li>[Author] wrote on [date]:<br/>
+ This setting is based on the mailnews.reply_header_authorwroteondate
+ preference.</li>
+ </ol>
+ </li>
+ <li><strong>Wrap plain text messages at [__] characters</strong>: Enter a
+ number to set the right margin for text in the message area.</li>
+ <li><strong>Defaults for HTML Messages</strong>: Here you can define what the
+ defaults are for font, size, text and background color if you choose to
+ send mails in HTML format.</li>
+ <li><strong>Default composition format</strong>: Select the initial style
+ for composing messages. With <q>Body Text</q> format, the Enter key always
+ inserts just a new line, while with <q>Paragraph</q> format, the Enter key
+ opens a new paragraph with additional line spacing. Use Shift+Enter to
+ insert just a plain line break regardless of this setting.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="send_format">Mail &amp; Newsgroups Preferences - Send Format</h2>
+
+<p>Send Format preferences allow you to specify how you want to format your
+ outgoing messages. If you are not already viewing the Send Format settings,
+ follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Send Format. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+</ol>
+
+<p>Formatted messages (composed in HTML) can be sent as HTML, plain text, or
+ both (those composed in plain text will always be sent as plain text only).
+</p>
+
+<p>If any of the recipients isn&apos;t explicitly listed to receive HTML,
+ based on his or her address-book entry or on a domain-based setting as
+ explained below, the following options apply:</p>
+<ul>
+ <li><strong>Ask me what to do</strong>: This option requires Mail &amp;
+ Newsgroups to prompt you to choose a format before you send the
+ message.</li>
+ <li><strong>Convert the message to plain text</strong>: This option sends
+ the message as plain text only, thus it will cause your message to lose
+ formatting such as custom colors or bold text. It will also remove all
+ images which are within the message itself (but not attachments).</li>
+ <li><strong>Send the message as HTML only</strong>: If you select this
+ option, keep in mind that some mail programs may have trouble displaying
+ the message as no separate plain-text representation is sent.</li>
+ <li><strong>Send the message as both plain text and HTML</strong>: This
+ option is the best trade-off between compatibility and retaining applied
+ formatting and embedded images, but increases the message size.</li>
+</ul>
+
+<p>Settings for entire domains:</p>
+<ul>
+ <li><strong>HTML and Plain Text Domains</strong>: These options explicitly set
+ HTML or plain-text preference for all users in the given domains (i.e., the
+ part of an e-mail address after the <q>@</q>). Use the Add button to add
+ the domain names that you typically send mail to, if you know which domains
+ can display HTML-formatted mail messages, and which domains can only
+ display plain text.</li>
+</ul>
+
+<p>Settings depending on the message content:</p>
+<ul>
+ <li><strong>Automatically send the message as plain text if no significant
+ formatting is present</strong>: Use this option to convert messages which
+ were composed in HTML to plain text to reduce the size of the message if
+ no or non-significant formatting has been applied. <q>Significant</q> in
+ this case includes bold or italic fonts, underlining, custom colors, etc.
+ This test is performed <em>before</em> the address book or domain settings
+ are consulted to determine the send format.</li>
+</ul>
+
+<p>For example, if you typically send mail to multiple recipients that have the
+ same domain name (for example, your colleagues all have email addresses that
+ end in <q>netscape.net</q>), and you know that this domain name is capable of
+ displaying HTML messages, then you can add the netscape.net domain to the
+ list of HTML Domains so that Mail &amp; Newsgroups will automatically send
+ messages in HTML format to these recipients (note that no plain-text part is
+ sent in this case).</p>
+
+<p>Similarly, if you typically send mail to recipients at a domain that you
+ know can only receive Plain Text messages, you can add that domain name to
+ the list of Plain Text domains, so that Mail &amp; Newsgroups automatically
+ sends messages to that domain in plain-text format (no HTML part is sent).</p>
+
+<p>Whenever you add a person or address card to your address book, you can
+ specify whether that addressee can receive HTML-formatted messages. However,
+ when this information is unknown, you can set Send Format preferences for how
+ Mail &amp; Newsgroups formats these messages.</p>
+
+<p>You can always override these preferences for an individual message by
+ using the Options menu in the Mail Compose window.</p>
+
+<p><strong>Note</strong>: If you regularly compose HTML (formatted) mail
+ messages, keep in mind that sometimes not all recipients use mail programs
+ that can display HTML formatting properly. Send Format preferences allow you
+ to specify how you want to format messages that go to recipients who cannot
+ display HTML-formatted mail. You can convert messages to plain text, format
+ them only as HTML, or format them as both HTML and plain text. These
+ preferences apply to all your mail accounts, but only to mail messages and
+ not to newsgroup messages.</p>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="addressing_preferences">Mail &amp; Newsgroups Preferences -
+ Addressing</h2>
+
+<p>Addressing preferences allow you to control the settings for
+ &brandShortName; Mail &amp; Newsgroups address books (for example, email
+ address collection and address autocompletion). If you are not already
+ viewing the Addressing settings, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Under the Mail &amp; Newsgroups category, select Addressing. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>Email Address Collection</strong>: Select this if you want Mail
+ &amp; Newsgroups to automatically collect recipients&apos; email addresses.
+ Use the drop-down list to choose between having the addresses added to your
+ Collected Addresses or your Personal Address Book.</li>
+ <li id="address_autocompletion"><strong>Address Autocompletion</strong>:
+ Address autocompletion allows you to quickly address mail without having to
+ search for names or type names completely. Select from which location Mail
+ &amp; Newsgroups will search for matching addresses: <q>Local Address
+ Books</q> (Personal Address Book, Collected Addresses, or any other local
+ address book) or <q>Directory Server</q> (an available LDAP directory
+ server) or both. If you want Mail &amp; Newsgroups to highlight addresses
+ that do not autocomplete, then select that option.
+
+ <p><strong>Note</strong>: If while addressing mail, multiple email address
+ matches are found, Mail &amp; Newsgroups displays a list of all possible
+ choices.</p>
+
+ <p>If you select Directory Server, choose a directory server from the list.
+ A directory server lets you look up addresses that are not stored in one
+ of your local address books. The directory you select will also be
+ searched for matching certificates when you attempt to send an encrypted
+ message to one or more recipients for whom you don&apos;t have
+ certificates on file.</p>
+
+ <p>See <a
+ href="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories">Adding
+ and Removing LDAP Directories</a> for information on setting LDAP
+ directory server settings.</p>
+
+ <p><strong>Note</strong>: Directory server settings you enter from the
+ Preferences dialog box apply to all your mail accounts. You can override
+ these settings for individual accounts by specifying different LDAP
+ directory servers or server settings using the Addressing settings for an
+ account in the Mail &amp; Newsgroups Account Settings dialog box. To set
+ different addressing options for a specific account, open the Edit menu
+ and choose Mail &amp; Newsgroups Account Settings.</p>
+ </li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="junk_and_suspect_preferences">Mail &amp; Newsgroups Preferences -
+ Junk &amp; Suspect Mail</h2>
+
+<p>This section describes how to use the Junk &amp; Suspect Mail preferences
+ panel. If you are not currently viewing the Junk &amp; Suspect Mail panel,
+ follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, select Junk &amp; Suspect Mail.
+ (If no subcategories are visible, double-click Mail &amp; Newsgroups to
+ expand the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>When I mark messages as junk</strong>: Choose this to
+ set what you want &brandShortName; to do when you manually mark messages
+ as Junk.
+ <ul>
+ <li><strong>Move them to the account's <q>Junk</q> folder</strong>:
+ Choose this to move manually-marked Junk messages to the Junk folder.
+ </li>
+ <li><strong>Delete them</strong>: Choose this to move manually-marked
+ Junk messages to the trash folder.</li>
+ </ul>
+ </li>
+ <li><strong>Mark messages as read</strong>:
+ <ul>
+ <li><strong>When &brandShortName; determines that they are junk</strong>:
+ Select this option to mark junk messages as read, so they will not show
+ up as new.</li>
+ <li><strong>When I manually mark them as junk</strong>: Select this
+ option to mark messages as read when you manually mark them as junk.
+ </li>
+ </ul>
+ </li>
+ <li><strong>Enable junk filter logging</strong>: Select this option to allow
+ logging the history of Junk mail detections. Click the <strong>Show log
+ </strong> button to open a dialog showing this log.</li>
+ <li><strong>Reset training data</strong>: Click this button to clear the
+ training data of the adaptive junk filter. Since this will effectively
+ destroy your personal junk profile, you will be asked for confirmation.
+ </li>
+ <li><strong>Tell me if the message I'm reading is a suspected email
+ scam</strong>: Choose this to make &brandShortName; analyze messages for
+ suspected email scams by looking for common techniques used to deceive
+ people.</li>
+ <li><strong>Allow antivirus clients to scan incoming messages more
+ easily</strong>: Choose this to let &brandShortName; make it easier for
+ antivirus software to analyze incoming mail messages for viruses before
+ they are stored locally.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+
+<h2 id="tags">Mail &amp; Newsgroups Preferences - Tags</h2>
+
+<p>This section describes how to use the Tags preferences panel. You use the
+ Tags preferences to define the tag text, colors and order for message tags.
+ If you are not currently viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, select Tags. (If no options
+ are visible, double-click the Mail &amp; Newsgroups category to expand the
+ list.)</li>
+</ol>
+
+<ul>
+ <li><strong>Customize Tags</strong>: Specifies the tag text and the color
+ for each tag. You can edit or replace the default tag text with your
+ own text (up to 32 characters). To change the tag color, click the color
+ chip next to that tag and select a new color. Use the Move Up and Move Down
+ buttons to order your tags by descending importance. Messages with
+ multiple tags will be colored according to their most important tag.</li>
+ <li><strong>Restore Defaults</strong>: Removes all customized tags and
+ restores just the default tags&apos; text and colors.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="return_receipts_preferences">Mail &amp; Newsgroups Preferences - Return
+ Receipts</h2>
+
+<p>This section describes how to use the Return Receipts preferences panel. If
+ you are not currently viewing the Return Receipts panel, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, select Return Receipts. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand the
+ list.)</li>
+</ol>
+
+<p>You use the Return Receipts preferences to define return receipt settings
+ for outgoing messages from all your mail accounts. You also use the Return
+ Receipt preferences to specify how to manage requests you receive for return
+ receipts.</p>
+
+<ul>
+ <li><strong>When sending messages, always request a return receipt</strong>:
+ Enables automatic return receipt requests for all outgoing messages in all
+ your mail accounts.</li>
+ <li><strong>Leave it in my Inbox</strong>: Return receipt confirmation
+ messages are delivered to your Inbox.
+
+ <p><strong>Tip</strong>: Choose this option if you want to use a filter
+ that automatically moves return receipt confirmation messages to a folder
+ you specify. For information on creating and using filters, see
+ <a href="mailnews_organizing.xhtml#creating_message_filters">Creating
+ Message Filters</a>.</p>
+ </li>
+ <li><strong>Move it to my Sent Mail folder</strong>: Incoming return receipt
+ confirmation messages are moved to your Sent mail folder.</li>
+ <li><strong>Never send a return receipt</strong>: Choose this option if you
+ do not want to send a return receipt in response to requests for return
+ receipts from others.</li>
+ <li><strong>Allow return receipts for some messages</strong>: Choose how you
+ want to respond to requests you receive for return receipts.</li>
+</ul>
+
+<p>To override these global preferences for individual accounts, see
+ <a href="mailnews_account_settings.xhtml#return_receipts">Mail &amp;
+ Newsgroups Account Settings - Return Receipts</a>.</p>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="text_encoding">Mail &amp; Newsgroups Preferences - Text Encoding</h2>
+
+<p>Text (character) encoding preferences allow you to choose how messages are
+ encoded when being displayed or created in all accounts. If you are not
+ already viewing the Text Encoding settings, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences. You see the
+ Preferences dialog box.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Text Encoding. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list.)</li>
+</ol>
+
+<ul>
+ <li><strong>Fallback Text Encoding</strong>: Click this drop-down list to
+ select the text encoding you want Mail &amp; Newsgroups to use as the
+ fallback for incoming mail and newsgroup messages. Senders are supposed
+ to declare the text encoding in which they are to be displayed, but some
+ legacy content (e.g., from mailing lists or in international newsgroups)
+ may not do so. This encoding is used for such messages you&apos;ve received
+ in which the text encoding (MIME charset parameter) is not specified.
+
+ <p><strong>Note</strong>: Individual folders may override this setting in
+ the General Information tab of the Folder Properties. This dialog can be
+ accessed from the Edit menu of a Mail &amp; Newsgroup window when a
+ folder is selected.</p>
+
+ <p><strong>Tip</strong>: You can later view or change the text encoding
+ for a specific message. Select a folder, then the message to display.
+ Open the View menu, and choose Text Encoding.</p>
+ </li>
+ <li><strong>For messages that contain 8-bit characters, use &apos;quoted
+ printable&apos; MIME encoding</strong>: Choose to have Mail &amp;
+ Newsgroups use <q>quoted printable</q> MIME encoding when sending regular
+ messages that use an 8-bit text encoding (for example, Latin ISO-8859-3).
+ This is usually only necessary when communicating via a legacy server that
+ doesn&apos;t process 8-bit encoding correctly.</li>
+ <li><strong>Default Text Encoding</strong>: Select the text encoding you want
+ Mail &amp; Newsgroups to use as the default for outgoing mail and newsgroup
+ messages.</li>
+ <li><strong>When possible, use this default text encoding in
+ replies.</strong>: By default, the selected text encoding is
+ <strong>not</strong> used when replying to a message. Instead, the text
+ encoding of the message being replied to is used. Choose this option to use
+ the default text encoding for outgoing messages even when replying, as long
+ as the quoted characters can be represented in the selected encoding.</li>
+</ul>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+<h2 id="network_and_storage_preferences">Mail &amp; Newsgroups Preferences -
+ Network &amp; Storage</h2>
+
+<p>This section describes how to use the Network &amp; Storage preferences
+ panel. If you are not currently viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, select Network &amp; Storage.
+ (If no subcategories are visible, double-click Mail &amp; Newsgroups to
+ expand the list.)</li>
+</ol>
+
+<p>The Network &amp; Storage preferences allow you to set preferences for
+ working offline, going online, mail connections and disk space.</p>
+
+<ul>
+ <li><strong>Offline</strong>: Select how you want Mail &amp; Newsgroups to
+ handle messages when going online or offline.</li>
+ <li><strong>Mail Connections</strong>: Choose how long you want Mail &amp;
+ Newsgroups to keep trying to contact the server before timing out.</li>
+ <li><strong>Disk Space</strong>: Select this to conserve disk space by
+ automatically compacting message folders when it will save the amount
+ of disk space you enter.</li>
+</ul>
+
+<p>See <a href="mailnews_offline.xhtml#working_offline">Working Offline</a> for
+ information on working offline.</p>
+
+<p>[<a href="#mail_and_newsgroup_preferences">Return to beginning of
+ section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_security.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_security.xhtml
new file mode 100644
index 0000000000..86781bbb24
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_security.xhtml
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Signing &amp; Encrypting Messages</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="signing_and_encrypting_messages">Signing &amp; Encrypting Messages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#about_digital_signatures_and_encryption">About Digital
+ Signatures &amp; Encryption</a></li>
+ <li><a href="#getting_other_peoples_certificates">Getting Other
+ People&apos;s Certificates</a></li>
+ <li><a href="#configuring_security_settings">Configuring Security
+ Settings</a></li>
+ <li><a href="#signing_and_encrypting_a_new_message">Signing &amp;
+ Encrypting a New Message</a></li>
+ <li><a href="#reading_signed_and_encrypted_messages">Reading Signed &amp;
+ Encrypted Messages</a></li>
+ <li><a href="#message_security_compose_window">Message Security -
+ Compose Window</a></li>
+ <li><a href="#message_security_received_message">Message Security -
+ Received Message</a></li>
+ </ul>
+</div>
+
+<h2 id="about_digital_signatures_and_encryption">About Digital Signatures &amp;
+ Encryption </h2>
+
+<p>When you compose a mail message, you can choose to attach your digital
+ signature to it. A <a href="glossary.xhtml#digital_signature">digital
+ signature</a> allows recipients of the message to verify that the message
+ really comes from you and hasn&apos;t been tampered with since you sent
+ it.</p>
+
+<p>When you compose a mail message, you can also choose to encrypt it.
+ <a href="glossary.xhtml#encryption">Encryption</a> makes it very difficult
+ for anyone other than the intended recipient to read the message while it is
+ in transit over the Internet.</p>
+
+<p>Signing and encryption are not available for newsgroup messages.</p>
+
+<p>Before you can sign or encrypt a message, you must take these preliminary
+ steps:</p>
+
+<ol>
+ <li>Obtain one or more <a href="glossary.xhtml#certificate">certificates</a>
+ (the digital equivalents of ID cards). For details, see
+ <a href="using_certs_help.xhtml#getting_your_own_certificate">Getting Your
+ Own Certificate</a>.</li>
+ <li>Configure the security settings for your email account. For details, see
+ <a href="mailnews_account_settings.xhtml#security">Configuring Your
+ Security Settings</a>.
+ </li>
+</ol>
+
+<p>Once you have completed these steps, you can complete the instructions in
+ <a href="#signing_and_encrypting_a_new_message">Signing &amp; Encrypting a
+ New Message</a>.</p>
+
+<p>The sections that follow provide a brief overview of how digital signatures
+ and encryption work. For more technical details on this subject, see the
+ online document
+ <a href="http://developer.mozilla.org/en/Introduction_to_Public-Key_Cryptography">Introduction
+ to Public-Key Cryptography</a>.</p>
+
+<h3 id="how_digital_signatures_work">How Digital Signatures Work</h3>
+
+<p>A digital signature is a special code, unique to each message, created by
+ means of <a href="glossary.xhtml#public-key_cryptography">public-key
+ cryptography</a>.</p>
+
+<p>A digital signature is completely different from a handwritten signature,
+ although it can sometimes be used for similar legal purposes, such as signing
+ a contract.</p>
+
+<p>To create a digital signature for an email message that you are sending, you
+ need two things:</p>
+
+<ul>
+ <li>A <a href="glossary.xhtml#signing_certificate">signing certificate</a>
+ that identifies you for this purpose. Every time you sign a message, your
+ signing certificate is included with the message. The certificate includes
+ a <a href="glossary.xhtml#public_key">public key</a>. The presence of the
+ certificate in the message permits the recipient to verify your digital
+ signature.
+
+ <p>Your certificate is a bit like your name and phone number in the
+ phonebook&mdash;it is public information that helps other people
+ communicate with you.</p>
+ </li>
+ <li>A <a href="glossary.xhtml#private_key">private key</a>, which is created
+ and stored on your computer when you first obtain a certificate.
+
+ <p>Your private key for a signing certificate is protected by your
+ <a href="glossary.xhtml#master_password">Master Password</a>, and the
+ &brandShortName; program does not disclose it to anyone else. The Mail
+ &amp; Newsgroup software uses your private key to create a unique,
+ verifiable digital signature for every message you choose to sign.</p>
+ </li>
+</ul>
+
+<h3 id="how_encryption_works">How Encryption Works</h3>
+
+<p>To encrypt an email message, you must have an
+ <a href="glossary.xhtml#encryption_certificate">encryption certificate</a>
+ for each of the message&apos;s recipients. The public key in each certificate
+ is used to encrypt the message for that recipient.</p>
+
+<p>If you don&apos;t have a certificate for even a single recipient, the
+ message cannot be encrypted.</p>
+
+<p>The recipient&apos;s software uses the recipient&apos;s private key, which
+ remains on that person&apos;s computer, to decrypt the message.</p>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="getting_other_peoples_certificates">Getting Other People&apos;s
+ Certificates</h2>
+
+<p>Every time you send a digitally signed message, your encryption certificate
+ is automatically included with the message. Therefore, one of the easiest
+ ways to obtain someone else&apos;s certificate is for that person to send you
+ a digitally signed message.</p>
+
+<p>When you receive such a message, the person&apos;s certificate is
+ automatically stored by the <a href="certs_help.xhtml">Certificate
+ Manager</a>, which is the part of the browser that keeps track of
+ certificates. This is useful because you need to have a certificate for each
+ recipient of any email message that you want to send in encrypted form.</p>
+
+<p>Another way to obtain certificates is to look them up in a public directory,
+ such as the <q>phonebook</q> directories maintained by many companies.</p>
+
+<p>It&apos;s also possible to look up certificates automatically. This feature
+ is controlled by
+ <a href="mailnews_preferences.xhtml#addressing_preferences">Mail &amp;
+ Newsgroups Preferences - Addressing</a> or
+ <a href="mailnews_account_settings.xhtml#addressing">Mail &amp; Newsgroups
+ Account Settings - Addressing</a>, which can be configured to look up
+ recipients&apos; email addresses in a directory.</p>
+
+<p>When you are using any account that is configured to look up addresses in a
+ directory, the same directory will be searched for matching certificates when
+ you attempt to send an encrypted message to one or more recipients for whom
+ you don&apos;t have certificates on file.</p>
+
+<p>The directory will also be searched for missing certificates when you open
+ the drop-down menu below the Security icon in the Compose window and choose
+ View Security Info.</p>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="configuring_security_settings">Configuring Security Settings</h2>
+
+<p>Once you have obtained an email certificate (or certificates), you must
+ specify the certificates you want to use for signing and encrypting
+ messages.</p>
+
+<p>For information about obtaining email certificates, see
+ <a href="using_certs_help.xhtml#getting_your_own_certificate">Getting Your
+ Own Certificate</a>.</p>
+
+<p>To specify which signing and encryption certificates to use with a
+ particular account, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings.</li>
+ <li>Click Security under the name of the mail account whose security settings
+ you want to configure.</li>
+ <li>Under Digital Signing, click Select. (You may be asked to provide your
+ <a href="glossary.xhtml#master_password">Master Password</a> before you can
+ proceed further.)
+
+ <p>A dialog box appears that allows you to select from among your available
+ signing certificates.</p>
+ </li>
+ <li>Choose the signing certificate you want to use, then click OK.</li>
+ <li>Follow the same steps under Encryption: click the Select button, select
+ the encryption certificate you want to use, and click OK.</li>
+
+ <p>In some cases you may be able to specify the same certificate under
+ Encryption that you specified under Digital Signing; check with your system
+ administrator to find out for sure.</p>
+</ol>
+
+<p>Optionally, you can also indicate that you normally want to sign or encrypt
+ all messages sent from a particular account. These account-specific settings
+ are for convenience only; you can override the default settings for
+ individual messages.</p>
+
+<p>To configure your default signing and encryption settings, start from the
+ Security panel for the account (described above) and select your settings as
+ follows:</p>
+
+<ul>
+ <li>Under Digital Signing:
+ <ul>
+ <li><strong>Digitally sign messages</strong>: When this checkbox is
+ selected, all the messages you send from this account will be digitally
+ signed unless you indicate otherwise before you send the message. To
+ turn off this default setting, deselect the checkbox.</li>
+ </ul>
+ </li>
+ <li>Under Encryption (choose one):
+ <ul>
+ <li><strong>Never</strong>: When this option is selected, messages you
+ send from this account will be not be encrypted unless you indicate
+ otherwise before you send them.</li>
+ <li><strong>Required</strong>: When this option is selected, all the
+ messages you send from this account will be encrypted&mdash;but only if
+ you have valid certificates for each of the message&apos;s recipients.
+ If you don&apos;t have all the necessary certificates, the message
+ can&apos;t be sent unless you turn off encryption for that message.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>When you have finished configuring your mail security settings, click OK to
+ confirm them.</p>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="signing_and_encrypting_a_new_message">Signing &amp; Encrypting a New
+ Message</h2>
+
+<p>Before you can digitally sign or encrypt any message, you must obtain at
+ least one email certificate and configure your mail security settings
+ correctly. For background information on these tasks, see
+ <a href="#about_digital_signatures_and_encryption">About Digital Signatures
+ &amp; Encryption</a>.</p>
+
+<p>The settings specified in
+ <a href="mailnews_account_settings.xhtml#security">Mail &amp; Newsgroups
+ Account Settings - Security</a> determine the default settings for each new
+ Compose window you open when you set out to write an email.</p>
+
+<p>To open a Compose window, start from the Mail window and click Compose. You
+ can immediately identify the default security settings from the presence or
+ absence of these icons near the lower-right corner of the window:</p>
+
+<table>
+ <tr>
+ <td><img src="chrome://messenger/skin/smime/icons/hdrSignOk.gif"
+ alt="digital signature icon"/></td><td>The message will be digitally
+ signed (assuming you have a valid email certificate that
+ identifies you).</td>
+ </tr>
+ <tr>
+ <td><img src="chrome://messenger/skin/smime/icons/hdrCryptoOk.gif"
+ alt="encryption icon"/></td><td>The message will be encrypted
+ (assuming you have valid certificates for all recipients).</td>
+ </tr>
+</table>
+
+<p>To turn these settings off or on, click the arrow just below the Security
+ icon in the Mail toolbar near the top of the window. Then select the item you
+ want from the drop-down list:</p>
+
+<ul>
+ <li><strong>Do Not Encrypt This Message</strong>: Choose this to turn off
+ encryption for this message. The message will not be encrypted when it is
+ sent over the Internet.</li>
+ <li><strong>Encrypt This Message</strong>: Choose this to turn on encryption
+ for this message. The message will be sent in encrypted form. However, it
+ can&apos;t be sent unless you have valid certificates for all
+ recipients.</li>
+ <li><strong>Digitally Sign This Message</strong>: Choose this to turn digital
+ signing on or off for this message. A checkmark indicates the message will
+ be signed.</li>
+ <li><strong>View Security Info</strong>: Choose this to view detailed
+ information about the security status of this message&mdash;to help you
+ determine, for example, whether you need to obtain a certificate for one of
+ the recipients.</li>
+</ul>
+
+<p>To view detailed information about the message&apos;s security status, you
+ can also click the key or lock icon as described in
+ <a href="#message_security_compose_window">Message Security - Compose
+ Window</a>.</p>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="reading_signed_and_encrypted_messages">Reading Signed &amp; Encrypted
+ Messages</h2>
+
+<p>When you view a signed or encrypted message in the Mail window, these icons
+ near the upper-right corner of the message header indicate the security
+ status of the message:</p>
+
+<table>
+ <tr>
+ <td><img src="chrome://messenger/skin/smime/icons/hdrSignOk.gif"
+ alt="digital signature icon"/></td><td>The message is digitally
+ signed and has been validated. If there is a problem with the signature,
+ the pen is broken.</td>
+ </tr>
+ <tr>
+ <td><img src="chrome://messenger/skin/smime/icons/hdrSignUnknown.gif"
+ alt="unknown icon"/></td><td>The message is signed, but it has a
+ large attachment that has not yet been downloaded from the IMAP server.
+ As a result, the signature cannot be validated. Click the icon to
+ download the attachment and validate the signature.</td>
+ </tr>
+ <tr>
+ <td><img src="chrome://messenger/skin/smime/icons/hdrCryptoOk.gif"
+ alt="encryption icon"/></td><td>The message is encrypted. If there
+ is a problem with the encryption, the key is broken.</td>
+ </tr>
+</table>
+
+<p>For information about certificate validation, see
+ <a href="using_certs_help.xhtml#controlling_validation">Controlling
+ Validation</a>.</p>
+
+<p>To see more detailed information about the message&apos;s security, click
+ the key or lock icon, or follow the instructions in
+ <a href="#message_security_received_message">Message Security - Received
+ Message</a>.</p>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="message_security_compose_window">Message Security - Compose Window</h2>
+
+<p>This section describes the Message Security window that you can open for any
+ message you are composing. If you&apos;re not already viewing Message
+ Security, click the Security icon in the toolbar of the Compose window.</p>
+
+<p>The Message Security window describes how your message will be sent:</p>
+
+<ul>
+ <li><strong>Digitally Signed</strong>: This line describes whether your
+ message will be signed. There are three possibilities:
+ <ul>
+ <li><strong>Yes</strong>: Digital signing has been enabled for this
+ message, you have a valid certificate identifying you, and the message
+ can be signed.</li>
+ <li><strong>No</strong>: Digital signing has been disabled for this
+ message.</li>
+ <li><strong>Not possible</strong>: Digital signing has been enabled for
+ this message. However, a valid
+ <a href="glossary.xhtml#certificate">certificate</a> identifying you
+ for this purpose is not available, or there is some other problem that
+ makes signing impossible.</li>
+ </ul>
+ </li>
+ <li><strong>Encrypted</strong>: This line describes whether your message will
+ be encrypted. There are three possibilities:
+ <ul>
+ <li><strong>Yes</strong>: Encryption has been enabled for this message,
+ valid certificates for all listed recipients are available, and the
+ message can be encrypted.</li>
+ <li><strong>No</strong>: Encryption has been disabled or is not possible
+ for this message.</li>
+ <li><strong>Not possible</strong>: Encryption has been enabled for this
+ message. However, a valid certificate for at least one of the listed
+ recipients is not available, or no recipients are listed, or there is
+ some other problem that makes encryption impossible.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>When you compose a message and select a different account, the signing
+ and encryption preferences are updated to reflect the settings of
+ the newly selected account.</p>
+
+<p>The Message Security window also lists the certificates available for the
+ recipients of your message:</p>
+
+<ul>
+ <li><strong>View</strong>: To view the details for any certificate in the
+ list, select its name, then click View.</li>
+</ul>
+
+<p>For more information about obtaining certificates and configuring message
+ security settings, see <a href="#signing_and_encrypting_messages">Signing
+ &amp; Encrypting Messages</a>.</p>
+
+<p>To indicate your signing or encryption choices for an individual message,
+ click the arrow beside the Security button in the Compose window, then select
+ the options you want.</p>
+
+<p>To indicate your default signing and encryption preferences for all
+ messages, see <a href="mailnews_account_settings.xhtml#security">Mail &amp;
+ Newsgroups Account Settings - Security</a></p>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="message_security_received_message">Message Security - Received
+ Message</h2>
+
+<p>This section describes the Message Security window that you can open for any
+ message you have received. If you&apos;re not already viewing Message
+ Security for a received message, follow these steps:</p>
+
+<ol>
+ <li>In the Mail window, select the message for which you want to view
+ security information.</li>
+ <li>Open the View menu and choose Message Security Info.</li>
+</ol>
+
+<p>The Message Security window displays the following information:</p>
+
+<ul>
+ <li><strong>Digital Signature</strong>: The top section describes whether the
+ message is digitally signed and if so, whether the signature is valid.</li>
+
+ <p>If validation failed while OCSP was enabled, check the OCSP settings in
+ <a href="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates">Privacy
+ &amp; Security Preferences - Certificates</a>. If you are not familiar with
+ OCSP, confirm the settings with your system administrator. If your settings
+ are correct, there may be a problem with the OCSP service or the
+ certificate used to create the signature is no longer valid.</p>
+
+ <p>If the signature is invalid because of a problem with a certificate&apos;s
+ trust settings, you can use the <a href="certs_help.xhtml">Certificate
+ Manager</a> to view or edit those settings.</p>
+
+ <li><strong>View Signature Certificate</strong>: If the message is signed,
+ click this button to view the certificate that was used to sign it.</li>
+ <li><strong>Encryption</strong>: The bottom section reports whether the
+ message is encrypted and any decrypting problems.
+ <ul>
+ <li>If the message&apos;s contents have been altered during transit, you
+ should ask the sender to resend it. The changes may have been caused by
+ network problems.</li>
+ <li>If a copy of your own certificate (used by the sender to encrypt the
+ message) is not available on your computer, the private key required to
+ decrypt the message cannot be retrieved. The only solution is to import
+ a backup copy of your certificate and its private key (see
+ <a href="certs_help.xhtml#your_certificates">Your Certificates</a> for
+ details.) If you don&apos;t have access to a backup certificate, you
+ will not be able to decrypt the message.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>[<a href="#signing_and_encrypting_messages">Return to beginning of
+ section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/mailnews_using_mail.xhtml b/comm/suite/locales/en-US/chrome/common/help/mailnews_using_mail.xhtml
new file mode 100644
index 0000000000..8685b747d0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/mailnews_using_mail.xhtml
@@ -0,0 +1,1188 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Using &brandShortName; Mail</title>
+<link rel="stylesheet" href="helpFileLayout.css" type="text/css"/>
+<link rel="stylesheet" href="chrome://communicator/skin/smileys.css"
+ type="text/css"/>
+</head>
+<body>
+<h1 id="reading_messages">Reading Messages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#getting_new_messages">Getting New Messages</a></li>
+ <li><a href="#choosing_how_you_view_the_mail_window">Choosing How You View
+ the Mail Window</a></li>
+ <li><a href="#sorting_and_threading_messages">Sorting and Threading
+ Messages</a></li>
+ <li><a href="#saving_and_printing_messages">Saving and Printing
+ Messages</a></li>
+ <li><a href="#controlling_images_scripts_and_plugins">Controlling Images,
+ Scripts, and Plugins</a></li>
+ </ul>
+</div>
+
+<h2 id="getting_new_messages">Getting New Messages</h2>
+
+<p>For an IMAP account, you can retrieve new messages automatically and display
+ them in the Inbox by opening Mail &amp; Newsgroups and selecting the Inbox
+ for the IMAP account.</p>
+
+<p>For a POP account, you must select the Inbox and click Get Msgs to retrieve
+ your messages. By default, messages from your POP account are downloaded in
+ full and deleted from the POP server when you retrieve them. You can
+ <a href="mailnews_account_settings.xhtml#pop_server_settings">change your POP
+ server settings</a> to retrieve just the headers and/or store a copy of
+ messages on the server in addition to downloading them to your computer.</p>
+
+<p>For news accounts, expanding the account newsgroups list will automatically
+ check for new items, as it will by just selecting one of the newsgroups.
+ While reading a newsgroup, you can force checking for new items by clicking
+ Get Msgs.</p>
+
+<p>For blogs &amp; feeds accounts, the first time you expand the account, it
+ will be checked for new items. Besides that, you can force checking for new
+ items at any time by clicking Get Msgs.</p>
+
+<p>You can also set up Mail &amp; Newsgroups to get new messages at startup and
+ to check for new messages at timed intervals.</p>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/task_mail.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 20px;"></td>
+ <td><strong>Mail &amp; Newsgroups icon</strong></td>
+ </tr>
+</table>
+
+<p>The Mail &amp; Newsgroups icon on the status bar displays a green arrow to
+ notify you when new messages have arrived.</p>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/task_newmail.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 20px;"></td>
+ <td><strong>New mail notification</strong></td>
+ </tr>
+</table>
+
+<p>To set up a mail account to automatically check for new messages, begin from
+ the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>If you have multiple accounts, select an account and click the Server
+ Settings category for that account.</li>
+ <li>Select one or both of the following options in the Server Settings
+ section:
+ <ul>
+ <li><strong>Check for new mail at startup</strong>: Select this checkbox
+ if you want to check this account automatically for new messages
+ whenever you start Mail &amp; Newsgroups. For POP accounts, Mail &amp;
+ Newsgroups checks for new mail, but doesn&apos;t download new messages
+ until you click Get Msgs or unless you choose <q>Automatically download
+ any new messages</q>.</li>
+ <li><strong>Check for new messages every [__] minutes</strong>: Select
+ this checkbox if you want to specify the number of minutes between mail
+ checks. You can also check for new messages at any time by clicking Get
+ Msgs in the Mail window.</li>
+ </ul>
+ </li>
+ <li>Click OK. Your settings take effect the next time you start
+ &brandShortName; Mail &amp; Newsgroups.</li>
+</ol>
+
+<p>To set up &brandShortName; Mail &amp; Newsgroups to play a sound or display
+ an alert when new mail arrives, see <a
+ href="mailnews_preferences.xhtml#notifications">Mail &amp; Newsgroups
+ Preferences - Notifications</a>.</p>
+
+<p>You can always retrieve messages manually at any time. To get new messages
+ for the selected account or newsgroup, do one of the following:</p>
+
+<ul>
+ <li>Click Get Msgs on the Mail toolbar.</li>
+ <li>Open the File menu (in the Mail window) and choose Get New Messages.</li>
+</ul>
+
+<p>To get new messages for all your mail accounts, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Click the triangle on the Get Msgs button in the Mail toolbar.</li>
+ <li>Choose Get All New Messages. &brandShortName; Mail &amp; Newsgroups
+ retrieves new messages for all your mail accounts.
+
+ <p>If you are not currently logged into one of your mail accounts, Mail
+ &amp; Newsgroups first prompts you to enter your user name and password
+ before retrieving new messages for that account. (If you have already
+ stored your user name and password using the Password Manager, Mail &amp;
+ Newsgroups doesn&apos;t prompt you for this information.)</p>
+ </li>
+</ol>
+
+<p><strong>Note</strong>: You can also open the File menu (in the Mail window)
+ and choose <q>Get New Messages for</q>.</p>
+
+<p>To get new messages for a specific mail account, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Click the triangle on the Get Msgs button on the Mail toolbar.</li>
+ <li>Choose the account for which you want to retrieve mail.</li>
+</ol>
+
+<p><strong>Note</strong>: Mail &amp; Newsgroups prompts you for your password
+ the first time you retrieve messages for an account. You can choose to have
+ Mail &amp; Newsgroups store your password in the Password Manager at that
+ time.</p>
+
+<p>Password Manager can save all your user names and passwords on your own
+ computer and enter them for you automatically. For more information, see
+ <a href="using_priv_help.xhtml#using_the_password_manager">Using the Password
+ Manager</a>.</p>
+
+<p>[<a href="#reading_messages">Return to beginning of section</a>]</p>
+
+<h2 id="choosing_how_you_view_the_mail_window">Choosing How You View the Mail
+ Window</h2>
+
+<p>You can customize the layout of the Mail window (the window you see when you
+ choose Mail &amp; Newsgroups from the Window menu):</p>
+
+<ul>
+ <li>Open the View menu and choose Show/Hide to show or hide the Mail toolbar,
+ search bar, or the status bar.</li>
+ <li>Open the View menu and choose Layout to select the type of three-pane
+ window layout to use.</li>
+ <li>Expand and collapse any pane to switch between a three-pane or two-pane
+ view.</li>
+</ul>
+
+<p>[<a href="#reading_messages">Return to beginning of section</a>]</p>
+
+<h2 id="sorting_and_threading_messages">Sorting and Threading Messages</h2>
+
+<p>To sort messages by categories such as subject, sender, date, or priority,
+ begin from the Mail window:</p>
+
+<ul>
+ <li>Click the appropriate column heading in the message list window. Or, open
+ the View menu, choose Sort by, and then select the column you want to sort
+ by.</li>
+</ul>
+
+<p>To reorder column headings, begin from the Mail window:</p>
+
+<ul>
+ <li>Click and drag a column heading to the left or right to reposition the
+ column.</li>
+</ul>
+
+<p>To change which columns are displayed, begin from the Mail window:</p>
+
+<ul>
+ <li>Click the Show/Hide Columns icon <img src="images/columns.png" alt=""/>
+ and select the column to be added/removed from the list.</li>
+</ul>
+
+<p>To group messages by threading (subject), so each message is grouped with
+ all its responses:</p>
+
+<ul>
+ <li>Click the thread button to the left of the Subject, Sender, and Date
+ column headings. Or, open the View menu, choose Sort by, and then select
+ Threaded.</li>
+</ul>
+
+<table>
+ <tr>
+ <td colspan="2"><img src="images/threadbutton.png" alt=""/></td>
+ </tr>
+ <tr>
+ <td style="width: 20px;"></td>
+ <td><strong>Thread button</strong></td>
+ </tr>
+</table>
+
+<p><strong>Tip</strong>: The thread button automatically sorts the threads by
+ the age of their parent messages. If you want to use another sort criterion
+ for the threads, open the View menu and select the desired option from the
+ Sort by submenu.</p>
+
+<p><strong>Tip</strong>: Select <q>Preserve threading when sorting messages</q>
+ in the <a href="mailnews_preferences.xhtml#mail_and_newsgroups">Mail &amp;
+ Newsgroups Preferences</a> if you want &brandShortName; to preserve the
+ threaded message grouping when sorting messages with column header clicks. The
+ thread button just toggles between threaded and unthreaded message grouping in
+ this mode. If <q>Preserve threading when sorting messages</q> is not selected,
+ &brandShortName; automatically displays the messages unthreaded when you sort
+ them by clicking on a column header.</p>
+
+<p><strong>Tip</strong>: To help you identify unread messages in a collapsed
+ thread where you&apos;ve read the parent message, &brandShortName; Mail &amp;
+ Newsgroups underlines the parent message.</p>
+
+<p>[<a href="#reading_messages">Return to beginning of section</a>]</p>
+
+<h2 id="saving_and_printing_messages">Saving and Printing Messages</h2>
+
+<p>To save a mail message as a plain-text, HTML, or Outlook file:</p>
+
+<ol>
+ <li>In the Mail window, select the message.</li>
+ <li>Open the File menu and choose Save As, and then choose File.</li>
+ <li>For <q>Save as type</q>, choose a file type (HTML, Text, or Mail file).
+ Choose Mail file if you want to save the message so it can be opened by
+ Microsoft Outlook.</li>
+ <li>Change the filename&apos;s extension to end in .html, .txt, or .eml,
+ depending on the file type you chose in step 3.</li>
+ <li>Choose a destination for the file and click Save.</li>
+</ol>
+
+<p>To print a selected message:</p>
+
+<ul>
+ <li>Click Print.</li>
+</ul>
+
+<p>[<a href="#reading_messages">Return to beginning of section</a>]</p>
+
+<h2 id="controlling_images_scripts_and_plugins">Controlling Images, Scripts,
+ and Plugins</h2>
+
+<p>By default, images and other content, that is hosted remotely, will not
+ display in messages you receive, except from senders in your address books
+ whom you have allowed. To change these settings:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Message Display. (If
+ no subcategories are visible, double-click Mail &amp; Newsgroups to
+ expand the list.)</li>
+ <li>Uncheck <q>Block images and other content from remote sources</q>.</li>
+ <li>Click OK to have your change take effect.</li>
+</ol>
+
+<p><strong>Note</strong>: See <q>Allow remote images in HTML mail</q> in <a
+ href="mailnews_addressbooks.xhtml#creating_a_new_address_book_card">Creating
+ a New Address Book Card</a> for details of how to change which senders can
+ show remote content.</p>
+
+<p>By default, plugins are not enabled for mail messages you receive. To change
+ this setting:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Scripts &amp; Plugins. (If no
+ subcategories are visible, double-click Advanced to expand the
+ list.)
+ <ul>
+ <li>Under <q>Enable Plugins for</q>, check <q>Mail &amp; Newsgroups</q>
+ to enable plugins.</li>
+ </ul>
+ </li>
+ <li>Click OK to have your changes take effect.</li>
+</ol>
+
+<p>[<a href="#reading_messages">Return to beginning of section</a>]</p>
+
+<h1 id="sending_messages">Sending Messages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#composing_mail_and_newsgroup_messages">Composing Mail and
+ Newsgroup Messages</a></li>
+ <li><a href="#using_the_message_composition_window">Using the Message
+ Composition Window</a></li>
+ <li><a href="#addressing_a_message">Addressing a Message</a></li>
+ <li><a href="#selecting_message_sending_options">Selecting Message
+ Sending Options</a></li>
+ <li><a href="#replying_to_a_message">Replying to a Message</a></li>
+ <li><a href="#forwarding_a_message">Forwarding a Message</a></li>
+ <li><a href="#confirming_that_your_message_was_opened">Confirming That Your
+ Message Was Opened</a></li>
+ <li><a href="#saving_and_editing_a_draft_message">Saving and Editing a
+ Draft Message</a></li>
+ <li><a href="#creating_and_using_templates">Creating and Using
+ Templates</a></li>
+ </ul>
+</div>
+
+<h2 id="composing_mail_and_newsgroup_messages">Composing Mail and Newsgroup
+ Messages</h2>
+
+<p>You can address, compose, reply to, or send a new message by doing one of
+ the following:</p>
+
+<ul>
+ <li>In any &brandShortName; window, open the File menu and choose New, then
+ Message.</li>
+ <li>Click Compose on the Mail toolbar.</li>
+ <li>While displaying a message, click Reply, Forward, or Reply All on the
+ Mail toolbar.</li>
+ <li>From the Address Book window, select an address and click Compose on the
+ Address Book.</li>
+</ul>
+
+<p><strong>Tip</strong>: Use the Mail &amp; Newsgroups Account Settings -
+ <a href="mailnews_account_settings.xhtml#addressing">Composition &amp;
+ Addressing</a> dialog box to specify the HTML text editor to use for
+ composing messages sent from this account. (You can specify a different
+ editor for each of your accounts.) See
+ <a href="mailnews_getting_started.xhtml#changing_the_settings_for_an_account">Changing
+ the Settings for an Account</a> for more information.</p>
+
+<p><strong>Note</strong>: It is generally not possible to compose messages for
+ them to be published in blogs &amp; news feeds accounts. If you want to
+ publish posts in a blog (and you have the appropiate rights to do it), you
+ will need to use the mechanisms provided by the specific blog system. In some
+ cases, this can even include sending a mail message to a specific address.</p>
+
+<p>Composing messages in HTML format allows you to use different fonts, text
+ styles (such as bold or italic) and text colors, tables, numbered or bulleted
+ lists, and pictures in your messages. However, some recipients may only be
+ able to read messages composed in plain text format. If you want to use the
+ plain-text editor occasionally, you can hold down the Shift key while
+ clicking the Compose or the Reply button to use the plain-text editor on an
+ as-needed basis.</p>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="using_the_message_composition_window">Using the Message Composition
+ Window</h2>
+
+<p>Use the Compose window to address, compose, and send mail and newsgroup
+ messages. First specify whether you want to compose messages in plain text or
+ HTML by default in the <a
+ href="mailnews_account_settings.xhtml#addressing">Composition &amp;
+ Addressing</a> Preferences panel (open the Edit menu and choose Mail &amp;
+ Newsgroups Account Settings).</p>
+
+<p>To view the Compose window, click the Compose button on the Mail
+ toolbar.</p>
+
+<p>The Compose window contains the following:</p>
+
+<ul>
+ <li>Mail Toolbar
+
+ <p>You can click the following buttons:</p>
+ <ul>
+ <li><strong>Send</strong>: To send a completed message.</li>
+ <li><strong>Address</strong>: To search for names in your address
+ books.</li>
+ <li><strong>Attach</strong>: To attach a file to a message. See
+ <a href="#using_attachments">Using Attachments</a> for more
+ information.</li>
+ <li><strong>Spell</strong>: To check the spelling of your message
+ text.</li>
+ <li><strong>Security</strong>: To display information about whether
+ your message will be sent encrypted or digitally signed (or
+ both).</li>
+ <li><strong>Save</strong>: To save the message as a draft.</li>
+ </ul>
+ </li>
+ <li>Addressing area: Where you enter the email addresses of recipients.</li>
+ <li>Attachments area: When you attach files to a message (by clicking in this
+ area or by clicking the Attach button), the filenames will be listed in the
+ Attachments area to the right of the Addressing area.</li>
+ <li>Message body area: Where you type the contents of your message.</li>
+</ul>
+
+<p>If you&apos;ve chosen to compose messages using the HTML editor, you see an
+ additional toolbar with text formatting buttons similar to those in
+ &brandShortName; Composer.</p>
+
+<p>For help using the HTML editor, see
+ <a href="composer_help.xhtml#formatting_your_web_pages">Formatting Your Web
+ Pages</a>.</p>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="addressing_a_message">Addressing a Message</h2>
+
+<p>To address a mail message:</p>
+
+<ol>
+ <li>Type the name in the addressing area.
+
+ <p>If you have <a
+ href="mailnews_preferences.xhtml#address_autocompletion">address
+ autocompletion</a> enabled (it&apos;s enabled by default), type the first
+ few letters of the recipient&apos;s name and wait for Mail &amp;
+ Newsgroups to complete the address. (Or you can type part of the name and
+ immediately press <kbd class="mac">Return</kbd><kbd
+ class="noMac">Enter</kbd> to have Mail &amp; Newsgroups try to complete
+ the address.)</p>
+ </li>
+ <li>If multiple addresses are displayed, select an address and press
+ <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.
+
+ <p><strong>Note</strong>: Use a comma to separate multiple addresses on the
+ same line. Do not use a comma to separate first or last names. For
+ example, multiple entries might be:</p>
+
+ <p><tt>user1@netscape.net,user2@netscape.net</tt></p>
+ </li>
+ <li>If you want this message to be sent from a different account, click the
+ <q>From</q> field to select the account you want. See
+ <a href="#changing_the_account_from_which_a_message_is_sent">Changing the
+ Account From Which a Message is Sent</a> for more information.</li>
+ <li>If necessary, click <q>To</q> to choose a different recipient type:
+ <ul>
+ <li><strong>To</strong>: For primary recipients of your message.</li>
+ <li><strong>Cc</strong>: For secondary recipients (carbon copy).</li>
+ <li><strong>Bcc</strong>: For secondary recipients not identified to the
+ other recipients, including those in the cc list (blind carbon
+ copy).</li>
+ <li><strong>Reply-To</strong>: For recipients to reply to a different
+ email address other than the one the message is sent from.</li>
+ <li><strong>Newsgroup</strong>: For posting to a newsgroup.</li>
+ <li><strong>Followup-To</strong>: For redirecting a newsgroup posting, so
+ that subsequent replies go directly to the redirected newsgroup instead
+ of the original newsgroup.</li>
+ </ul>
+ </li>
+</ol>
+
+<p><strong>Tip</strong>: You can quickly address a message by clicking the
+ email address contained in a message you&apos;re reading, and then selecting
+ Compose Mail To from the pop-up menu.</p>
+
+<p id="changing_the_account_from_which_a_message_is_sent"><strong>Changing the
+ Account From Which a Message is Sent</strong></p>
+
+<p>If you have multiple mail accounts, the account listed in the From field is
+ based on the account (or server) you selected when you choose to create a new
+ message. However, &brandShortName; Mail &amp; Newsgroups also allows you to
+ change the account a message is sent from while you&apos;re composing a
+ message. Click the From field to view a list of your accounts and then select
+ the account you want. A copy of the message is saved in the Sent folder of
+ the account where you sent the message from.</p>
+
+<p><strong>About Address Autocompletion</strong></p>
+
+<p>Address autocompletion allows you to address mail easily from the Compose
+ window without having to search for names or type complete names. Mail &amp;
+ Newsgroups automatically checks your address books and an
+ <a href="glossary.xhtml#ldap">LDAP</a> directory server (if available) and
+ completes the name if it finds a unique match. It also prevents mistakes by
+ showing all possible choices with additional information if it finds multiple
+ matches. Address autocompletion is enabled by default.</p>
+
+<p>If you don&apos;t want to use an address that Mail &amp; Newsgroups
+ provides, press Backspace or Delete to remove characters and then enter an
+ alternate address.</p>
+
+<p>To disable address autocompletion:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click Addressing. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand
+ the list).</li>
+ <li>In the Address Autocompletion section, deselect <q>Local Address
+ Books</q> and <q>Directory Server</q>.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="selecting_message_sending_options">Selecting Message Sending
+ Options</h2>
+
+<p>While you&apos;re composing a message, you can select these additional
+ message sending options from the Options menu:</p>
+
+<ul>
+ <li><strong>Select Addresses</strong>: The Select Addresses option lets you
+ choose the recipient&apos;s email address from your Address Books or a
+ remote directory. To look up an address in an address book or directory,
+ enter the first few letters of the recipient&apos;s first or last name to
+ start the search. Select an address and then click To:, Cc:, or Bcc: to
+ address your message.</li>
+ <li><strong>Quote Message</strong>: Choose this option to have the selection
+ of the message text shown as quoted text.</li>
+ <li><strong>Return Receipt</strong>: Choose this option to request a
+ confirmation message when the recipient displays (opens) the message. Keep
+ in mind that the recipient may choose not to send you a return receipt.
+ This option lets you enable or disable return receipt requests on a
+ per-message basis. To automatically request return receipts for all
+ messages you send, use the return receipts preferences. See
+ <a href="mailnews_preferences.xhtml#return_receipts_preferences">Mail &amp;
+ Newsgroups Preferences - Return Receipts</a> for more information.</li>
+ <li><strong>Format</strong>: Send the message as plain text, or HTML
+ (formatted), or both. If you choose <q>Auto-Detect</q>, Mail &amp;
+ Newsgroups prompts you for the format to use if it&apos;s unknown whether
+ the recipient&apos;s mail program can display an HTML message. The format
+ you choose here overrides the send format you specified using the
+ Preferences command on the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu.</li>
+ <li><strong>Priority</strong>: Choose a priority to indicate whether the
+ message has lowest, low, normal, high, or highest priority.</li>
+ <li><strong>Text Encoding</strong>: Choose a text encoding used for this
+ message.</li>
+ <li><strong>Send a Copy To</strong>: Choose this if you want to file an
+ additional copy of the sent message in a different folder than your default
+ Sent folder. Then select the folder you want.</li>
+ <li><strong>Encrypt This Message</strong>: Choose this to have &brandShortName;
+ encrypt this message (without changing the default).</li>
+ <li><strong>Digitally Sign This Message</strong>: Choose this to have
+ &brandShortName; digitally sign this message (without changing the
+ default).</li>
+</ul>
+
+<p>Additionally, the following options are available from the Edit menu:</p>
+
+<ul>
+ <li><strong>Rewrap</strong>: If you are composing a message using the
+ plain-text editor, you can use the Rewrap command to rewrap long lines of
+ quoted text to fit the Compose window. This command rewraps selected quoted
+ text to the number of characters specified by the
+ <a href="mailnews_preferences.xhtml#composition">Composition</a>
+ preferences. This command is primarily useful when you are replying to a
+ message where the original message is quoted in your reply, and the original
+ message contains long lines.
+
+ <p>You use the Mail &amp; Newsgroups Account Settings command on the Edit
+ menu to specify that you want to use the plain-text editor for composing
+ messages. Select the Composition &amp; Addressing panel of the account
+ and uncheck <q>Compose messages in HTML format</q> to use the plain-text
+ editor for all messages. If you only want to use the plain-text editor
+ occasionally, you can hold down the Shift key while clicking the Compose
+ or the Reply button to use the plain-text editor on an as-needed
+ basis.</p>
+ </li>
+ <li><strong>Check Spelling</strong>: Checks the spelling of the message text
+ before you send it. You can also click Spell.</li>
+ <li><strong>Spellcheck As You Type</strong>: Choose this option to have the
+ spelling of the message text checked as you type.</li>
+</ul>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="replying_to_a_message">Replying to a Message</h2>
+
+<p>To reply to a mail message:</p>
+
+<ul>
+ <li>Select the message.</li>
+ <li>Click Reply to respond to the sender alone.</li>
+ <li>Click Reply All to respond to all addressees in the message.</li>
+</ul>
+
+<p>To include the original message each time you reply to any message, and to
+ specify how to place the original message in the reply:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings.
+ You see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>If you have multiple accounts, select an account and click the
+ <a href="mailnews_account_settings.xhtml#addressing">Composition &amp;
+ Addressing</a> category for that account.</li>
+ <li>Select <q>Automatically quote the original message when
+ replying</q>.</li>
+ <li>Specify where in the message to place your reply. <q>Start my reply below
+ the quote</q> is the default.</li>
+ <li>If you have decided to <a
+ href="mailnews_account_settings.xhtml#account_settings">attach a
+ signature</a> to every outgoing message and selected to start your reply
+ above the quote here, you can additionally configure where your signature
+ is placed:
+ <ul>
+ <li>Select <q>below the quote (recommended)</q> to place your signature
+ at the very end of the message below the quoted text.</li>
+ <li>Select <q>below my reply (above the quote)</q> to place your
+ signature between your reply and the quoted text.</li>
+ </ul>
+
+ <p><strong>Note</strong>: If you have created a signature, you can
+ optionally <a href="mailnews_account_settings.xhtml#addressing">omit
+ it</a> when replying to a message.</p>
+ </li>
+ <li>Click OK.</li>
+</ol>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="forwarding_a_message">Forwarding a Message</h2>
+
+<p>When you forward a message, you can specify how its contents are included
+ in the new message: <em>inline</em> (in the body of the message), or as an
+ <em>attachment</em>.</p>
+
+<p>To forward a message:</p>
+
+<ol>
+ <li>Select the message and click Forward.</li>
+ <li>Type the name or email address of the recipient.</li>
+ <li>Click Send.</li>
+</ol>
+
+<p>To set the default for forwarding messages:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click
+ <a href="mailnews_preferences.xhtml#composition">Composition</a>. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand the
+ list.)</li>
+ <li>For forwarding messages, choose Inline (in the message body) or As
+ Attachment.
+
+ <p><strong>Note</strong>: If you have created a signature and forward
+ inline, its placement depends on the respective reply setting. You can
+ optionally <a href="mailnews_account_settings.xhtml#addressing">omit the
+ signature</a> when forwarding a message.</p>
+ </li>
+ <li>Click OK.</li>
+</ol>
+
+<p><strong>Tip</strong>: To override the default for forwarding a message,
+ select the message, open the Message menu, and choose Forward As, then
+ choose Inline or Attachment.</p>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="confirming_that_your_message_was_opened">Confirming That Your Message
+ Was Opened</h2>
+
+<p>You can use return receipts to notify you when a recipient has displayed
+ (opened) your message. The recipient must be using a mail program that
+ supports the Message Disposition Notification (MDN) standard. Keep in mind
+ that the recipient may choose not to send you a return receipt, even if
+ you&apos;ve requested one. Messages you send to a newsgroup address will not
+ include a return receipt request, since news servers don&apos;t support this
+ feature.</p>
+
+<p>To request return receipts for all messages you send, you can use the global
+ <a href="mailnews_preferences.xhtml#return_receipts_preferences">Return
+ Receipt</a> preferences to specify how to manage requests you receive for
+ return receipts. You can override these global preferences for individual
+ accounts.</p>
+
+<p>To request a return receipt on a per-message basis:</p>
+
+<ul>
+ <li>From a Mail Compose window, open the Options menu, and choose Return
+ Receipt.</li>
+</ul>
+
+<p>To automatically request return receipts when sending messages from each of
+ your mail accounts:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click
+ <a href="mailnews_preferences.xhtml#return_receipts_preferences">Return
+ Receipts</a>. (If no subcategories are visible, double-click Mail &amp;
+ Newsgroups to expand the list.)</li>
+ <li>Select <q>When sending messages, always request a return receipt</q>.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>For more information on setting return receipt preferences, see
+ <a href="mailnews_preferences.xhtml#return_receipts_preferences">Mail &amp;
+ Newsgroups Preferences - Return Receipts</a>.</p>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="saving_and_editing_a_draft_message">Saving and Editing a Draft
+ Message</h2>
+
+<p>To save a mail message as a draft so you can complete it later:</p>
+
+<ul>
+ <li>In the Compose window, click Save, or open the File menu and choose Save
+ as Draft. By default, the message is saved in the Drafts folder for the
+ current account.
+
+ <p><strong>Note</strong>: Your mail message will stay open after you save
+ it as a draft.</p>
+ </li>
+</ul>
+
+<p>To edit or send a draft message, begin from the Mail window:</p>
+
+<ol>
+ <li>Click the Drafts folder for the account where you created the draft
+ message.</li>
+ <li>Click the message that you want to edit.</li>
+ <li>In the top-right corner of the message, click the Edit Draft
+ button.</li>
+ <li>Edit the message as necessary.</li>
+ <li>Click Send to send the message or click Save to save the message so you
+ can complete it later.
+
+ <p><strong>Note</strong>: Sending the message removes it from the Drafts
+ folder.</p>
+ </li>
+</ol>
+
+<p><strong>Tip</strong>: You can also double-click the message to open it for
+ editing. This is especially useful if the message pane is closed.</p>
+
+<p>To delete one or more unwanted draft messages, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Click the Drafts folder for the account where you created the draft
+ messages.</li>
+ <li>Select the draft messages that you want to delete.</li>
+ <li>Click Delete in the Mail toolbar.</li>
+</ol>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h2 id="creating_and_using_templates">Creating and Using Templates</h2>
+
+<p>Templates are useful for setting the default format for messages that you
+ send regularly, such as weekly status reports. You can save a message as a
+ template from any window in which it is displayed, including from within a
+ Mail compose window.</p>
+
+<p>To save a message to use as a template:</p>
+
+<ol>
+ <li>In the Mail window, click Compose to create a new message and then set
+ the default font, text size, text color, background color, and any other
+ default formatting you want.
+
+ <p>Alternatively, open an existing message that already has the formatting
+ you want.</p>
+ </li>
+ <li>While displaying the message, open the File menu, choose Save As, then
+ choose Template. The message is stored as a template in the Templates
+ folder for the current mail account.</li>
+</ol>
+
+<p>To compose a message using a template:</p>
+
+<ol>
+ <li>In the Mail window, select the Templates folder for the account where you
+ created the message template.</li>
+ <li>Double-click the message template to open it.</li>
+ <li>Edit the message, then save it (to put it in the Drafts folder) or send
+ it.
+
+ <p><strong>Note</strong>: Sending the message does not remove the template
+ from the Templates folder. The template is preserved for future use.</p>
+ </li>
+</ol>
+
+<p>To delete one or more unwanted message templates, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Click the Templates folder for the account where you created the message
+ templates.</li>
+ <li>Select the message templates that you want to delete.</li>
+ <li>Click Delete in the Mail toolbar.</li>
+</ol>
+
+<p>[<a href="#sending_messages">Return to beginning of section</a>]</p>
+
+<h1 id="creating_html_mail_messages">Creating HTML Mail Messages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#using_html_in_your_messages">Using HTML in Your
+ Messages</a></li>
+ <li><a href="#choosing_html_mail_sending_options">Choosing HTML Message
+ Sending Options</a></li>
+ <li><a href="#specifying_recipients_for_html_messages">Specifying
+ Recipients for HTML Messages</a></li>
+ <li><a href="#viewing_the_message_source_for_html_messages">Viewing the
+ Message Source for HTML Messages</a></li>
+ <li><a href="#using_the_html_mail_question_dialog_box">Using the HTML Mail
+ Question Dialog Box</a></li>
+ </ul>
+</div>
+
+<h2 id="using_html_in_your_messages">Using HTML in Your Messages</h2>
+
+<p>HTML messages can include formatted text, links, images, and
+ tables&mdash;just like a web page. However, some recipients may not be able
+ to receive HTML messages. &brandShortName; Mail &amp; Newsgroups allows you
+ to compose mail and newsgroup messages using either the HTML (rich-text)
+ formatting editor or the plain-text editor for each mail account you have.
+ In addition, you can choose whether your addressees should receive HTML or
+ plain-text messages by default, and how Mail &amp; Newsgroups should handle
+ messages when it&apos;s not known if an addressee can receive HTML-formatted
+ mail.</p>
+
+<p>To specify whether to use the HTML editor as the default for composing
+ messages, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Select the mail or newsgroup account you want to use.</li>
+ <li>Go to the Composition &amp; Addressing panel and select <q>Compose
+ messages in HTML format</q>. You see the Formatting toolbar in the Compose
+ window. Leave this box unchecked to use the plain-text editor for this
+ account.</li>
+</ol>
+
+<p>[<a href="#creating_html_mail_messages">Return to beginning of
+ section</a>]</p>
+
+<h3 id="editing_or_inserting_html_elements">Editing or Inserting HTML
+ Elements</h3>
+
+<p>If you understand how to work with HTML source code, you can edit or insert
+ additional HTML tags, style attributes, and JavaScript in your mail message.
+ If you are not sure how to work with HTML source code, it&apos;s best not to
+ change it. To work with HTML code, use one of these methods:</p>
+
+<ul>
+ <li>Place the insertion point where you want to insert the HTML code, then
+ open the Insert menu and choose HTML. In the Insert HTML dialog box, enter
+ HTML tags and text, and then click Insert to insert your changes.</li>
+ <li>Select the HTML source code that you want to edit, then open the Insert
+ menu and choose HTML. In the Insert HTML dialog box, edit HTML tags and
+ text, and then click Insert to insert your changes.</li>
+ <li>Select an element such as a table, named anchor, image, link, or
+ horizontal line. Double-click the element to open the associated properties
+ dialog box for that item. Click Advanced Edit to open the Advanced Property
+ Editor. You can use the Advanced Property Editor to add HTML attributes and
+ JavaScript to objects.</li>
+</ul>
+
+<p>For more information on editing HTML source code, see
+ <a href="composer_help.xhtml#using_the_advanced_property_editor">Using the
+ Advanced Property Editor</a>.</p>
+
+<p>[<a href="#creating_html_mail_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="choosing_html_mail_sending_options">Choosing HTML Mail Sending
+ Options</h2>
+
+<p>By default, Mail &amp; Newsgroups prompts you before sending HTML messages
+ when it&apos;s not known whether the recipient&apos;s mail program can
+ display HTML-formatted messages.</p>
+
+<p>To choose sending-format options for mail messages, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Mail &amp; Newsgroups category, click
+ <a href="mailnews_preferences.xhtml#send_format">Send Format</a>. (If no
+ subcategories are visible, double-click Mail &amp; Newsgroups to expand the
+ list).
+
+ <p><strong>Note</strong>: This preference applies only to mail messages,
+ not to newsgroup messages.</p>
+ </li>
+ <li>Select the option you want and then click OK.</li>
+</ol>
+
+<p>If while composing a message you realize that one or more recipients may not
+ be able to receive HTML-formatted mail, you can easily convert the message to
+ a different format when you click Send:</p>
+
+<ol>
+ <li>In the Compose window, open the Options menu and choose Format.</li>
+ <li>Select the format you want to use for sending the message from the
+ submenu:
+ <ul>
+ <li><strong>Auto Detect</strong>: Mail &amp; Newsgroups chooses the
+ appropriate format for the message text. If it can&apos;t determine the
+ format, it asks you to choose a format.</li>
+ <li><strong>Plain Text Only</strong>: The message may not display
+ formatting such as bold text, but all mail programs will be able to
+ display the message.</li>
+ <li><strong>Rich Text (HTML) Only</strong>: Some mail programs may have
+ trouble displaying an HTML-formatted message. Choose this option only
+ if you are sure the recipient&apos;s mail program can display
+ HTML-formatted mail.</li>
+ <li><strong>Plain and Rich (HTML) Text</strong>: This uses more disk
+ space, but may be the best choice if you are not sure whether the
+ recipient&apos;s mail program can display HTML-formatted mail.</li>
+ </ul>
+ </li>
+ <li>When you&apos;ve finished composing the message, click Send.</li>
+</ol>
+
+<p>[<a href="#creating_html_mail_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="specifying_recipients_for_html_messages">Specifying Recipients for HTML
+ Messages</h2>
+
+<p>You can save time by indicating whether individuals in your address books
+ prefer to receive either HTML messages or plain text messages.</p>
+
+<ol>
+ <li>Open the Window menu and choose Address Book.</li>
+ <li>Select the address book on the left and then select the individual&apos;s
+ card on the right.</li>
+ <li>Click Properties to display the <q>Card for</q> dialog box.</li>
+ <li>In the Contact tab, use the <q>Prefers to receive messages formatted
+ as</q> drop-down list to select HTML if you know this recipient can read
+ HTML-formatted messages (such as messages that include links, images, or
+ tables).
+
+ <p>If this recipient can only read messages sent as plain text (no
+ formatting), then choose Plain Text. If you don&apos;t know or are not
+ sure, choose Unknown.</p>
+
+ <p>If you choose Unknown, &brandShortName; Mail &amp; Newsgroups determines
+ the sending format based on the Send Format settings for Mail &amp;
+ Newsgroups in the Preferences dialog box. If Mail &amp; Newsgroups still
+ can&apos;t determine the correct format, it will prompt you to choose a
+ sending format when you send the message.</p>
+ </li>
+ <li>Click OK.</li>
+</ol>
+
+<p>[<a href="#creating_html_mail_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="viewing_the_message_source_for_html_messages">Viewing the Message
+ Source for HTML Messages</h2>
+
+<p>You can quickly view the HTML and other code that generates an HTML message
+ you&apos;ve received:</p>
+
+<ol>
+ <li>In the message list window, open the message.</li>
+ <li>Open the View menu and choose Message Source.</li>
+</ol>
+
+<p>[<a href="#creating_html_mail_messages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="using_the_html_mail_question_dialog_box">Using the HTML Mail Question
+ Dialog Box</h2>
+
+<p>The HTML Mail Question dialog box appears when you try to send a message to
+ someone whose mail program may not be able to display HTML messages or when
+ Mail &amp; Newsgroups cannot determine whether your recipient can display
+ HTML messages. If you are in doubt, send the message in both HTML and
+ plain-text formats.</p>
+
+<p>[<a href="#creating_html_mail_messages">Return to beginning of
+ section</a>]</p>
+
+<h1 id="using_attachments">Using Attachments</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#attaching_a_file_or_web_page">Attaching a File or Web
+ Page</a></li>
+ <li><a href="#viewing_and_opening_attachments">Viewing and Opening
+ Attachments</a></li>
+ <li><a href="#saving_attachments">Saving Attachments</a></li>
+ </ul>
+</div>
+
+<h2 id="attaching_a_file_or_web_page">Attaching a File or Web Page</h2>
+
+<p>To attach a file to an outgoing mail message:</p>
+
+<ol>
+ <li>In the Compose window, click Attach or open the File menu and choose
+ Attach File. You see the <q>Enter file to attach</q> dialog box.
+
+ <p><strong>Tip</strong>: You can also click inside the Attachments area to
+ attach a file.</p>
+ </li>
+ <li>Type the name of the file you want to attach, or select a file from your
+ hard drive that you want to attach.</li>
+ <li>Click Open. The filename appears in the Attachments area.</li>
+</ol>
+
+<p><strong>Tip</strong>: You can also drag and drop one or more files from your
+ desktop into the Attachments area in the Compose window.</p>
+
+<p>To attach a web page to an outgoing mail message:</p>
+
+<ol>
+ <li>In the Compose window, open the File menu and choose Attach Web Page.</li>
+ <li>In the dialog box, enter the URL of the page and then click OK. The web
+ page URL appears in the Attachments area.</li>
+</ol>
+
+<p><strong>Tip</strong>: When you are viewing a page in the browser,
+ you can send the page to someone by opening the File menu and choosing Send
+ Page.</p>
+
+<p>[<a href="#using_attachments">Return to beginning of section</a>]</p>
+
+<h2 id="viewing_and_opening_attachments">Viewing and Opening Attachments</h2>
+
+<p>If you receive a mail attachment that consists of a file type that
+ &brandShortName; can display (such as graphic files and HTML files), you see
+ the attachment displayed inline (in the body of the message). For other file
+ types, Mail &amp; Newsgroups lets you open the attachment using another
+ application, or you can save the attachment on your hard disk.</p>
+
+<p>To open the attachment, make sure you have a program on your computer that
+ can open files of the same type as the attachment&apos;s file format. For
+ example, if you want to open a .DOC file, make sure you have a program on
+ your computer that can open .DOC files.</p>
+
+<p>To open an attachment:</p>
+
+<ol>
+ <li>Double-click the attachment you want (if there is more than one).</li>
+ <li>In the Downloading dialog box, choose what you want &brandShortName; to
+ do with the attachment:
+ <ul>
+ <li>If &brandShortName; finds an application on your hard disk that can
+ open the attachment, you can open the attachment using that
+ application. Click <q>Choose</q> to use a different application to open
+ the attachment.</li>
+ <li>If &brandShortName; can&apos;t find an application on your hard disk
+ that can open the attachment, you can save the attachment. You
+ won&apos;t be able to open the attachment, but at least you can save
+ it on your hard disk until you can install an application that can open
+ it.</li>
+ <li>Click <q>Advanced</q> to add a new file type to the list of helper
+ applications. &brandShortName; uses helper applications to determine
+ how different file types are opened by other applications from within
+ &brandShortName;. For more information, see
+ <a href="nav_help.xhtml#plugins_and_downloads">Plugins and
+ Downloads</a>.</li>
+ </ul>
+ </li>
+ <li>Click OK.</li>
+</ol>
+
+<p><strong>Note</strong>: If you are viewing your mail using an IMAP mail
+ server, all attachments remain on the server.</p>
+
+<p>[<a href="#using_attachments">Return to beginning of section</a>]</p>
+
+<h2 id="saving_attachments">Saving Attachments</h2>
+
+<p>To save an attachment:</p>
+
+<ol>
+ <li>In the right side of the message envelope, under <q>Attachments</q>,
+ select the attachment that you want to save.</li>
+ <li>Right-click <span class="mac">or, if you have a one-button mouse,
+ <kbd>Ctrl</kbd>-click</span>the attachment and choose Save As from the
+ pop-up menu.</li>
+ <li>Choose a filename and location for the attachment on your hard disk and
+ then click OK. Mail &amp; Newsgroups downloads the attachment and saves it
+ to the specified location.</li>
+</ol>
+
+<p><strong>Tip</strong>: To save all attachments, right-click
+ <span class="mac">or, if you have a one-button mouse,
+ <kbd>Ctrl</kbd>-click</span>the first one in the attachment list, and choose
+ Save All. You can then specify the location where you want all the
+ attachments to be saved.</p>
+
+<p>[<a href="#using_attachments">Return to beginning of section</a>]</p>
+
+<h1 id="deleting_messages">Deleting Messages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#deleting_pop_or_imap_messages">Deleting POP or IMAP
+ Messages</a></li>
+ <li><a href="#moving_messages_to_and_from_the_trash">Moving Messages to and
+ from the Trash</a></li>
+ </ul>
+</div>
+
+<h2 id="deleting_pop_or_imap_messages">Deleting POP or IMAP Messages</h2>
+
+<p>How you delete messages depends on your mail server type: POP or IMAP.
+ Deleted POP messages are automatically moved to the Trash folder. IMAP users
+ can set different options for deleting messages.</p>
+
+<p>To delete messages from your Inbox or other folders, begin from the Mail
+ window:</p>
+
+<ol>
+ <li>In the message list, select the messages and click Delete. By default,
+ Mail &amp; Newsgroups moves the selected messages to the Trash folder.</li>
+ <li>To delete messages permanently, open the File menu and choose Empty
+ Trash.</li>
+</ol>
+
+<p>To delete messages without opening them, begin from the Mail window:</p>
+
+<ol>
+ <li>Open the View menu and choose Layout, and then uncheck Message Pane.
+
+ <p>Alternatively, click the Message Pane handle (the ridged area centered
+ at the bottom of the message list) to close the message pane.</p>
+ </li>
+ <li>In the message list, select the messages and click Delete.</li>
+</ol>
+
+<p>To set deletion preferences for IMAP messages:</p>
+
+<ol>
+ <li>Open the Edit menu and choose Mail &amp; Newsgroups Account Settings. You
+ see the Mail &amp; Newsgroups Account Settings dialog box.</li>
+ <li>Locate the IMAP account you want, and then click the Server Settings
+ category under the account name.</li>
+ <li>Select the <a
+ href="mailnews_account_settings.xhtml#when_i_delete_a_message">options</a>
+ you want for deleting messages and click OK.</li>
+</ol>
+
+<p>[<a href="#deleting_messages">Return to beginning of section</a>]</p>
+
+<h2 id="moving_messages_to_and_from_the_trash">Moving Messages To and From the
+ Trash</h2>
+
+<p>If you use a POP server to deliver your mail, or if you set up IMAP to use
+ the Trash folder, follow these steps to delete messages from your Inbox or
+ other folders:</p>
+
+<ol>
+ <li>In the message list, select the messages you want to delete.</li>
+ <li>Click Delete. Mail &amp; Newsgroups moves the messages to the Trash
+ folder.</li>
+</ol>
+
+<p>To recover messages from the Trash:</p>
+
+<ol>
+ <li>Click the Trash folder.</li>
+ <li>Select the messages you want to recover and drag them to another
+ folder.</li>
+</ol>
+
+<p>To delete messages permanently:</p>
+
+<ul>
+ <li>Open the File menu and choose Empty Trash.</li>
+</ul>
+
+<p>[<a href="#deleting_messages">Return to beginning of section</a>]</p>
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/nav_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/nav_help.xhtml
new file mode 100644
index 0000000000..11fcb71164
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/nav_help.xhtml
@@ -0,0 +1,1439 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Browsing the Web</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="browsing_the_web">Browsing the Web</h1>
+
+<p>Welcome to &brandShortName;! One of the most popular ways people use
+ &brandShortName; is to browse the Web. The &brandShortName; browser
+ component that lets you visit web pages, offers many ways to visit web pages
+ and search the Web.</p>
+
+<p>This section introduces you to the browser, and how you can use it to
+ navigate, search, and save web pages.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#navigating_web_pages">Navigating Web Pages</a></li>
+ <li><a href="#searching_the_web">Searching the Web</a></li>
+ <li><a href="#copying_saving_and_printing_pages">Copying, Saving, and
+ Printing Pages</a></li>
+ <li><a href="#using_languages_and_international_content">Using Languages
+ and International Content</a></li>
+ <li><a href="#plugins_and_downloads">Plugins and Downloads</a></li>
+ <li><a href="#improving_speed_and_efficiency">Improving Speed and
+ Efficiency</a></li>
+ <li><a href="#proxies">Proxies</a></li>
+ <li><a href="page_info_help.xhtml">Viewing Page Info</a></li>
+ </ul>
+</div>
+
+<h1 id="navigating_web_pages">Navigating Web Pages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#viewing_your_home_page">Viewing Your Home Page</a></li>
+ <li><a href="#moving_to_another_page">Moving to Another Page</a></li>
+ <li><a href="#clicking_a_link">Clicking a Link</a></li>
+ <li><a href="#retracing_your_steps">Retracing Your Steps</a></li>
+ <li><a href="#reopening_closed_tabs_windows">Reopening Closed Tabs or Windows</a></li>
+ <li><a href="#stopping_and_reloading">Stopping and Reloading</a></li>
+ <li><a href="#visiting_bookmarked_pages">Visiting Bookmarked Pages</a></li>
+ <li><a href="#using_tabbed_browsing">Using Tabbed Browsing</a></li>
+ <li><a href="#using_sidebar">Using Sidebar</a></li>
+ </ul>
+</div>
+
+<h2 id="viewing_your_home_page">Viewing Your Home Page</h2>
+
+<p>After the first launch, you will normally see your home page when you launch
+ &brandShortName;. Unless you choose a home page yourself, your home page is
+ chosen by your network or Internet service provider, or you see
+ &brandShortName;&apos;s home page.</p>
+
+<p>To choose your own home page, see
+ <a href="customize_help.xhtml#specifying_how_mozilla_starts_up">Specifying
+ How &brandShortName; Starts Up</a>.</p>
+
+<p><strong>Tips</strong>:</p>
+
+<ul>
+ <li class="noMac" id="full_screen_mode">To streamline the &brandShortName;
+ interface, you can use Full Screen mode to display web pages using almost
+ all of your screen. In the browser, open the View menu and choose Full
+ Screen. You can also press <kbd>F11</kbd>.</li>
+ <li>To go to your home page quickly, press <kbd class="mac">Cmd</kbd><kbd
+ class="noMac">Alt</kbd>+<kbd>Home</kbd>.</li>
+</ul>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="moving_to_another_page">Moving to Another Page</h2>
+
+<p>You move to a new page by typing its URL&mdash;its location (address) on the
+ Web. URLs normally begin with <q>http://</q>, followed by one or more
+ names that identify the address. For instance,
+ <q>http://www.mozilla.org</q>.</p>
+
+<ol>
+ <li>Click the Location Bar to select the URL that is already there.</li>
+ <li>Type the URL of the page you want to visit. The URL you type replaces any
+ text already in the Location Bar.</li>
+ <li>Press <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.</li>
+</ol>
+
+<p>Using the lock icon near the lower-right corner of the window, you can check
+ a web page&apos;s security status at any time. For details, see
+ <a href="using_certs_help.xhtml#checking_security_for_a_web_page">Checking
+ Security for a Web Page</a>.</p>
+
+<p><strong>Tip</strong>: To quickly select the URL in the Location Bar, press
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>+<kbd>L</kbd>.</p>
+
+<table summary="table for images">
+ <tr>
+ <td><img src="images/locationbar.png"
+ alt="Location Bar"/></td>
+ </tr>
+ <tr>
+ <td style="text-align: center;"><strong>Location Bar</strong></td>
+ </tr>
+</table>
+
+<p><strong>Don&apos;t know a URL?</strong> You can type part of a URL, such as
+ <q>cnn</q> (for www.cnn.com); or you can type a general word, such as
+ <q>gifts</q> or <q>flowers</q>. The browser guesses what page you
+ want to view, or displays a page with a choice of links related to the word
+ you typed.</p>
+
+<p>If you are new to the Internet, see the
+ <a href="http://www.internet-guide.co.uk/help.html">Internet Guide</a>.</p>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="clicking_a_link">Clicking a Link</h2>
+
+<p>Most web pages contain links you can click to move to other pages.</p>
+
+<ol>
+ <li>Move the pointer until it changes to a pointing finger. This happens
+ whenever the pointer is over a link. Most links are underlined text, but
+ buttons and images can also be links.</li>
+ <li>Click the link once. While the network locates the page that the link
+ points to, status messages appear at the bottom of the window.</li>
+</ol>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="retracing_your_steps">Retracing Your Steps</h2>
+
+<p>There are several ways to re-visit pages:</p>
+
+<ul>
+ <li>To go back or forward one page, click the Back or Forward arrow.</li>
+ <li>To go back or forward more than one page, click the small triangles on
+ the Back and Forward buttons. You&apos;ll see a list of pages you&apos;ve
+ visited; to return to a page, choose it from the list.</li>
+</ul>
+
+<table summary="table for images">
+ <tr>
+ <td colspan="4"><img src="images/reload.gif"
+ alt="Navigation Buttons"/></td>
+ </tr>
+ <tr>
+ <td style="width: 8px;"></td>
+ <td style="width: 34px;"><strong>Back</strong></td>
+ <td style="width: 45px;"><strong>Forward</strong></td>
+ <td></td>
+ </tr>
+</table>
+
+<ul>
+ <li>To see a list of any URLs you&apos;ve typed into the Location Bar, click
+ the arrow at the right end of the Location Bar. To view a page, choose it
+ from the list.</li>
+</ul>
+
+<table summary="table for images">
+ <tr>
+ <td><img src="images/locationbar.png" alt="Location Bar"/></td>
+ </tr>
+ <tr>
+ <td style="text-align: center;"><strong>Location Bar</strong> </td>
+ </tr>
+</table>
+
+<ul>
+ <li>To choose from pages you&apos;ve visited during the current session, open
+ the Go menu and use the list in the bottom section of the menu.</li>
+
+ <li>To choose from pages you&apos;ve visited during the past several
+ sessions, open the Go menu and choose History. You see the history list.
+ The history list displays a list of folders. Double clicking the folders
+ displays subfolders or bookmarks to web pages. You can double-click the URL
+ next to the Bookmark icon to view that page.</li>
+</ul>
+
+<p><strong>Tip</strong>: The Sidebar History tab also allows you to choose from
+ pages you&apos;ve visited during the past several sessions. For
+ information, see
+ <a href="customize_help.xhtml#adding_sidebar_tabs">Adding Sidebar Tabs</a>.
+</p>
+
+<h3 id="about_history_lists">About History Lists</h3>
+
+<p>The history list contains links to recently visited pages. The Location Bar
+ list contains links to pages you&apos;ve typed into the Location Bar and then
+ visited.</p>
+
+<p>To access the history list from the browser, open the Go menu and choose
+ History. To access the Location Bar list, click the arrow at the right end of
+ the Location Bar.</p>
+
+<p><strong>Tip</strong>: To quickly open the history list, press
+ <span class="mac"><kbd>Cmd</kbd>+<kbd>Shift</kbd></span><kbd
+ class="noMac">Ctrl</kbd>+<kbd>H</kbd>.</p>
+
+<p>If you don&apos;t want the Location Bar or history list to display the pages
+ you&apos;ve been visiting, you can clear the history list and Location Bar
+ history entirely or selectively.</p>
+
+<p>To delete all pages from the Location Bar or history list, begin from the
+ browser window:</p>
+
+<ol>
+ <li>Open the
+ <span class="mac">&brandShortName;</span><span class="noMac">Edit</span>
+ menu and choose Preferences.</li>
+ <li>Under the Browser category, click History. (If no subcategories are
+ visible, double-click Browser to expand the list.)</li>
+ <li>Click Clear History and Clear Location Bar to remove all previously
+ visited web pages from the lists.</li>
+</ol>
+
+<p>To selectively delete pages from the history list, do any of the
+ following:</p>
+
+<ul>
+ <li>To delete all pages from a domain, select a page within that domain
+ (folder) in the History list, open the Edit menu, and select <q>Delete
+ History for <em>*.[domain name]</em></q>. For example, use this command
+ if you want to delete all pages that end in <q>mozilla.org</q>.</li>
+ <li>To delete pages from a subdomain, select a page within that subdomain in
+ the History list, open the Edit menu, and select <q>Delete History for
+ <em>[subdomain]</em></q>. For example, use this command if you want to
+ delete all pages from <q>bugzilla.mozilla.org</q> but not
+ <q>mozilla.org</q>.</li>
+ <li>To delete a single page or folder, select it in the history list and
+ press Delete.</li>
+</ul>
+
+<p><strong>Tip</strong>: To sort the history list, click one of the categories
+ (Title, Location, or Last Visited). Click the title again to reverse the
+ order.</p>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="reopening_closed_tabs_windows">Reopening Closed Tabs or Windows</h2>
+
+<p>&brandShortName; keeps track of your most recently closed tabs and
+ windows and allows you to get back to them easily.</p>
+
+<p>To reopen closed tabs:</p>
+
+<ul>
+ <li>Open the Go menu and expand the Recently Closed Tabs menu.</li>
+ <li>Select any of the previously closed tabs from the list.
+ Once selected, it will be restored and removed from the list.</li>
+</ul>
+
+<p>Similarly, to reopen closed windows:</p>
+
+<ul>
+ <li>Open the Go Menu and expand the Recently Closed Windows menu.</li>
+ <li>Select any of the previously closed windows. Once selected,
+ it will be restored and removed from the list.</li>
+</ul>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="stopping_and_reloading">Stopping and Reloading</h2>
+
+<p>If a page is taking too long to appear, or you change your mind and
+ don&apos;t want to view it, click the Stop button.</p>
+
+<p>To refresh the current page, or get the most up-to-date version, click the
+ Reload button, or press
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>+<kbd>R</kbd>.</p>
+
+<table summary="table for images">
+ <tr>
+ <td colspan="3"><img src="images/reload.gif"
+ alt="Navigation Buttons"/></td>
+ </tr>
+ <tr>
+ <td style="width: 87px;"></td>
+ <td style="width: 45px;"><strong>Reload</strong></td>
+ <td style="width: 47px;"><strong>Stop</strong></td>
+ </tr>
+</table>
+
+<p>To refresh the current page and reset all changes made (if the page contains
+ a form), hold down the <kbd>Shift</kbd> key and click the Reload button, or
+ press <kbd class="mac">Cmd</kbd><kbd
+ class="noMac">Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>R</kbd>.</p>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="visiting_bookmarked_pages">Visiting Bookmarked Pages</h2>
+
+<p>The addresses, or URLs, of web pages can be quite long and difficult to
+ remember. Fortunately, it&apos;s not necessary to memorize URLs in order to
+ browse the Web. Your browser has a list of bookmarks, which are pointers to
+ interesting web pages.</p>
+
+<p>To go to a bookmarked page, begin from the Browser window:</p>
+
+<ol>
+ <li>Open the Bookmarks menu. The menu contains bookmarks represented by a
+ bookmark icon, and folders that contain more bookmarks.</li>
+ <li>To visit a bookmarked page, choose a bookmark from the menu, or open a
+ folder and choose a bookmark.</li>
+</ol>
+
+<p><strong>Tip</strong>: To retrace your steps, click the Back arrow.</p>
+
+<p>You can save your own bookmarks to point to pages you frequently visit, or
+ to other interesting places on the Web. See
+ <a href="customize_help.xhtml#creating_new_bookmarks">Creating New
+ Bookmarks</a> for more information.</p>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="using_tabbed_browsing">Using Tabbed Browsing</h2>
+
+<p>When you visit more then one web page at a time, you can use Tabbed Browsing
+ to navigate the Web faster and easier.</p>
+
+<p>Tabbed Browsing lets you open tabs, each displaying a web page, within a
+ single browser window. You don&apos;t have to have several windows open to
+ visit several different web pages. This frees up space on your desktop. You
+ can open, close, and reload web pages conveniently in one place without
+ having to switch to another window.</p>
+
+<p>You can manage your navigation tabs easily and control when tabs are opened
+ automatically. For more information about setting Tabbed Browsing
+ preferences, see
+ <a href="cs_nav_prefs_navigator.xhtml#tabbed_browsing">Browser Preferences
+ - Tabbed Browsing</a>.</p>
+
+<p>To learn more about using Tabbed Browsing, see
+ <a href="customize_help.xhtml#tabbed_browsing">Tabbed Browsing</a>.</p>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h2 id="using_sidebar">Using Sidebar</h2>
+
+<p>In addition to navigating the Web with the browser, you can let the Web come
+ to you by using Sidebar.</p>
+
+<p>Sidebar is a customizable frame in your browser where you can keep items
+ that you need to use all the time, including your bookmarks, browser history,
+ address book, and other available options. Sidebar presents these items to
+ you in tabs that it continually updates.</p>
+
+<p>&brandShortName; comes with some Sidebar tabs already set up, but you can
+ customize Sidebar by adding, removing, and rearranging tabs. For details,
+ see <a href="customize_help.xhtml#sidebar">Sidebar</a>.</p>
+
+<p>To view an item in Sidebar, click its tab.</p>
+
+<table summary="table for images">
+ <tr>
+ <td><img src="images/sidebar.png"
+ alt="Sidebar Handle"/></td>
+ <td style="vertical-align:
+ middle; -moz-padding-end: 20px;"><strong>Sidebar<br/>Handle</strong></td>
+ <td style="vertical-align: middle;">If it is not already open, open Sidebar
+ by clicking its handle. If the handle is missing, open the View menu in
+ the browser, choose Show/Hide, and then Sidebar from the submenu.</td>
+ </tr>
+</table>
+
+<p class="noMac"><strong>Tip</strong>: To quickly open or close the Sidebar,
+ press <kbd>F9</kbd>.</p>
+
+<p>[<a href="#navigating_web_pages">Return to beginning of section</a>]</p>
+
+<h1 id="searching_the_web">Searching the Web</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#fast_searches">Fast Searches</a></li>
+ <li><a href="#setting_search_preferences">Setting Search
+ Preferences</a></li>
+ <li><a href="#searching_within_a_page">Searching Within a Page</a></li>
+ <li><a href="#using_find_as_you_type">Using Find-as-you-type</a></li>
+ <li><a href="#searching_the_bookmarks_or_history_list">Searching the
+ Bookmarks or History List</a></li>
+ </ul>
+</div>
+
+<h2 id="fast_searches">Fast Searches</h2>
+
+<p>There are four ways to search quickly: from the Location Bar, from Sidebar,
+ from the Net Search page, and by selecting words in a web page.</p>
+
+<h3 id="searching_from_the_location_bar">Searching from the Location Bar</h3>
+
+<p>Searching for web pages on a particular topic is as easy as typing a
+ question, or just a word or two, into the browser&apos;s Location Bar, as
+ shown below.</p>
+
+<p><img src="images/locationbar_search.png" width="236" height="39" alt=
+ "Location Bar with Search Term"/></p>
+
+<p>For example, if you want to find information about baby dolls:</p>
+
+<ol>
+ <li><span class="noUnix">Click</span><span class="unix">Double-click</span>
+ in the Location Bar to select the current text.</li>
+ <li>Type the word <q>baby doll</q>. Your typing replaces the current
+ text.</li>
+ <li>Perform one of these steps:
+ <ul>
+ <li>Click the Search button <img src="images/search_navigation_toolbar.png"
+ width="73" height="21" alt="search button"/>.</li>
+ <li>Click <q>Search DuckDuckGo for <q>baby doll</q></q> at the bottom of
+ the drop-down list in the Location Bar. (Your default search engine may
+ be different.)
+
+ <p>The default search engine you choose in the
+ <a href="cs_nav_prefs_navigator.xhtml#internet_search">Internet
+ Search Preferences</a> is used. Search results for <q>baby doll</q>
+ appear in the browser window. Click the links to visit web pages
+ about baby dolls.</p>
+ </li>
+ </ul>
+ </li>
+</ol>
+
+<p><strong>Note</strong>: In the <q>Unknown Locations</q> section of the
+ <a href="cs_nav_prefs_navigator.xhtml#location_bar">Location Bar
+ Preferences</a>, you can set up the Location Bar so a search is automatically
+ performed if the text you have typed is not a web location. Typing a word in
+ the Location Bar and pressing <kbd class="noMac">Enter</kbd><kbd class="mac">
+ Return</kbd> will then perform a search.</p>
+
+<h3 id="searching_from_sidebar">Searching from Sidebar</h3>
+
+<p>The Sidebar Search tab lets you keep a search bar in your sidebar.
+ For example, if you want to find information about toy cars:</p>
+
+<ol>
+ <li>If it is not already open, open Sidebar by clicking its handle. If the
+ handle is missing, <span class="noMac">press <kbd>F9</kbd> or </span>open
+ the View menu in the browser, choose Show/Hide, and then Sidebar from the
+ submenu.</li>
+ <li>Click the Search tab in Sidebar.</li>
+ <li>Open the drop-down list, and choose a search engine.</li>
+ <li>Type <q>toy car</q> in the search field.</li>
+ <li>Click Search. Search results for &apos;toy car&apos; appear in the
+ browser window. Click the links to visit web pages about toy cars.</li>
+</ol>
+
+<p>See <a href="customize_help.xhtml#sidebar">Sidebar</a> for more information
+ on how to use Sidebar.</p>
+
+<h3 id="searching_from_the_net_search_page">Searching from your search engine's
+ home page</h3>
+
+<p>You can also perform a search from your search engine's home page.
+ To visit this page, perform one of these steps:</p>
+
+<ul>
+ <li>Click on the Search button <img src="images/search_navigation_toolbar.png"
+ width="73" height="21" alt="search button"/> on the Navigation Toolbar.</li>
+ <li>Open the Tools menu and choose Search the Web.</li>
+</ul>
+
+<h3 id="searching_on_selected_words_in_a_web_page">Searching on Selected Words
+ in a Web Page</h3>
+
+<p>&brandShortName; allows you to search for words you select within a web
+ page:</p>
+
+<ol>
+ <li>Select (highlight) any words in a web page.</li>
+ <li>Right-click<span class="mac"> or, if you have a one-button mouse,
+ <kbd>Ctrl</kbd>-click</span> and choose <q>Search Web for
+ [your selected words]</q> for the menu.</li>
+</ol>
+
+<p>&brandShortName; opens a new window or tab (depending on your preferences)
+ and uses your default search engine to search for your selected words. To
+ learn how to change the search engine used to search for your selected words
+ and the way your search results are displayed, see
+ <a href="cs_nav_prefs_navigator.xhtml#internet_search">Browser Preferences
+ - Internet Search</a>.</p>
+
+<p>[<a href="#searching_the_web">Return to beginning of section</a>]</p>
+
+<h2 id="setting_search_preferences">Setting Search Preferences</h2>
+
+<p>You can choose a different search engine as the default. You can also
+ specify how you want search results displayed.</p>
+
+<ol>
+ <li>Open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu and choose Preferences.</li>
+ <li>Under the Browser category, click Internet Search. (If no subcategories
+ are visible, double-click the Browser to expand the list.)</li>
+ <li>Under Default Search Engine, choose a search engine you want to use for
+ web searching.</li>
+ <li>Click Manage Search Engines to open the <q>Manage Search Engine List</q>
+ dialog, where you can select <q>Show search suggestions</q> if you want
+ &brandShortName; to show suggestions from the search engine as you type a
+ search string in the Sidebar search tab or in the Search Bar. Then click OK
+ to save any changes and close this dialog.</li>
+ <li>Under Search Results, select <q>Open new tabs for sidebar search
+ results</q> if you want to see the results from Sidebar searches to open in
+ a new tab instead of the current tab.</li>
+ <li>Under Search Results, select <q>Open a tab instead of a window for a
+ context menu web search</q> if you want search results to open in a new tab
+ instead of a new window when you search for selected words.</li>
+</ol>
+
+<p>[<a href="#searching_the_web">Return to beginning of section</a>]</p>
+
+<h2 id="searching_within_a_page">Searching Within a Page</h2>
+
+<p>To find text within the page you are currently viewing in the browser:</p>
+
+<ol>
+ <li>Open the Edit menu and choose <q>Find in This Page</q>. If the page
+ you are viewing contains frames, you may need to click within a frame
+ before you begin your search. You see the <q>Find bar</q>.</li>
+ <li>Type the text you want to find.</li>
+ <li>Click Next to begin the search from where the cursor is forwards
+ to the bottom of the page.</li>
+ <li>Click Previous to begin the search from the cursor backwards to
+ the top of the page.</li>
+ <li>With <strong>Highlight all</strong>, all words or phrases that
+ matched are highlighted on the page.</li>
+ <li>Use <strong>Match case</strong> to limit the search to words or
+ phrases that exactly match what you typed (taking case into
+ account).</li>
+</ol>
+
+<p>If the search hits the bottom (or top) of the page, it will continue
+ from the other end and indicate on the Find bar that it wrapped.</p>
+
+<p>To find the same word or phrase again, keep pressing Enter while the
+ focus is on the search field, or use the Find Again shortcuts (see
+ the <q>Using Find-as-you-type</q> section below) when the focus
+ is on the page. In both cases, a forward search will be invoked,
+ no matter whether the Previous button was used the last time.</p>
+
+<ul>
+ <li>Open the Edit menu and choose Find Again.</li>
+</ul>
+
+<p>[<a href="#searching_the_web">Return to beginning of section</a>]</p>
+
+<h2 id="using_find_as_you_type">Using Find-as-you-type</h2>
+
+<p>Other than searching text through the Find bar, you can also search by
+ typing directly into a Web page.</p>
+
+<ol>
+ <li>To search for a link, type several characters into the active browser
+ window to navigate to any link with that text in it.
+
+ <p>If you repeat the same character, it will start to cycle through all
+ the links that begin with that character. However, if it can find a
+ match with the exact string you&apos;ve typed, such as
+ <q><tt>oo</tt></q> in <q><tt>woods</tt></q>, it will go there
+ first.</p>
+ </li>
+ <li>To search for all text (normal text and linked text), type <kbd>/</kbd>
+ before your search string. For example, type <q><kbd>/hello</kbd></q> to
+ search any text containing the string <q>hello</q>.</li>
+ <li>The status bar (at the bottom of your browser window) displays your
+ search string and whether or not the search was successful. Please note
+ that this happens only if the <q>Show the find toolbar during find as you
+ type</q> checkbox on the <a href="cs_nav_prefs_advanced.xhtml#fayt">Find
+ As You Type preferences panel</a> is not selected. If the checkbox is
+ selected, then the <q>Find bar</q> is used instead of changing the status
+ bar.</li>
+ <li>Use the backspace key to undo the last character typed.</li>
+ <li>To cancel a find, change focus or scroll, press Escape, or wait for the
+ timeout.</li>
+ <li>Press
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>+<kbd>G</kbd> or
+ <kbd>F3</kbd> to find the same search text again. Press
+ <kbd class="mac">Cmd</kbd><kbd
+ class="noMac">Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>G</kbd> or
+ <kbd>Shift</kbd>+<kbd>F3</kbd> to find previous occurrence of the search
+ text.</li>
+</ol>
+
+<p>Type Ahead Find works with any window, such as this help file (try it!).</p>
+
+<p>This feature also works with international characters such as Chinese and
+ Japanese.</p>
+
+<p>[<a href="#searching_the_web">Return to beginning of section</a>]</p>
+
+<h2 id="searching_the_bookmarks_or_history_list">Searching the Bookmarks or
+ History List</h2>
+
+<p>To search the bookmarks list, begin from the browser window:</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks. You see your
+ Bookmarks window.</li>
+ <li>In the Bookmarks window, open the Tools menu and choose Search Bookmarks.
+ You see the Find Bookmarks dialog box.</li>
+ <li>Use the drop-down lists to select options to define your search. Choose
+ from the following search options:
+ <ul>
+ <li>Choose <q>name</q>, <q>location</q>, <q>description</q>, or
+ <q>keyword</q> to choose where you would like to search.</li>
+ <li>Choose <q>contains</q>, <q>starts with</q>, or <q>ends
+ with</q> if you know only part of the word or phrase for which
+ you&apos;re searching.</li>
+ <li>Choose <q>is</q> if you know exactly what you&apos;re searching
+ for.</li>
+ <li>Choose <q>is not</q> or <q>doesn&apos;t contain</q> to
+ exclude pages.</li>
+ <li>Click in the field and type all or part of search word or URL (web
+ address) for the bookmarks that you want to find or exclude.</li>
+ </ul>
+ </li>
+ <li>Click Find. Bookmarks that match your search criteria are displayed in
+ the Search Results-Bookmarks window.</li>
+</ol>
+
+<p><strong>Tip</strong>: To quickly open the Bookmark Manager, press
+ <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>+<kbd>B</kbd>.</p>
+
+<p>To search the History list:</p>
+
+<ol>
+ <li>Open the Go menu and choose History. You see the history list.</li>
+ <li>Above the actual list, you see a textbox titled <q>Search History</q>.</li>
+ <li>Click into this textbox and type parts of the URL or page title you are
+ searching for.</li>
+ <li>The History list is filtered for those search terms as you type them.</li>
+</ol>
+
+<p>To use the search results:</p>
+
+<ul>
+ <li>Double-click a bookmark in the Search Results window to go to that web
+ page.</li>
+</ul>
+
+<p><strong>Tips</strong>:</p>
+
+<ul>
+ <li>To open the History list quickly, press <kbd class="mac">Cmd</kbd>
+ <kbd class="noMac">Ctrl</kbd>+<kbd>H</kbd>.</li>
+</ul>
+
+<p>[<a href="#searching_the_web">Return to beginning of section</a>]</p>
+
+<h1 id="copying_saving_and_printing_pages">Copying, Saving, and Printing
+ Pages</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#copying_part_of_a_page">Copying Part of a Page</a></li>
+ <li><a href="#saving_all_or_part_of_a_page">Saving All or Part of a
+ Page</a></li>
+ <li><a href="#printing_a_page">Printing a Page</a></li>
+ </ul>
+</div>
+
+<h2 id="copying_part_of_a_page">Copying Part of a Page</h2>
+
+<p>To copy some text from a page, begin from the browser window:</p>
+
+<ol>
+ <li>Select the text.</li>
+ <li>Open the Edit menu and choose Copy.</li>
+</ol>
+
+<p>You can paste the text into other programs.</p>
+
+<p>To copy a link (URL) or an image link from a page:</p>
+
+<ol>
+ <li>Position the pointer over the link or image.</li>
+ <li>Right-click<span class="mac"> or, if you have a one-button mouse,
+ <kbd>Ctrl</kbd>-click,</span> the link or image to display a pop-up
+ menu.</li>
+ <li>Choose Copy Link Location or Copy Image Location. If an image is also a
+ link, you are offered both options.</li>
+</ol>
+
+<p>You can paste the link into other programs or into browser&apos;s Location
+ Bar.</p>
+
+<p>[<a href="#copying_saving_and_printing_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="saving_all_or_part_of_a_page">Saving All or Part of a Page</h2>
+
+<p>To save an entire page, begin from the browser window:</p>
+
+<ol>
+ <li>Open the File menu and choose Save Page As. You see the Save As dialog
+ box.</li>
+ <li>Choose a folder in which to save this file.</li>
+ <li>Choose a format for the page you want to save:
+ <ul>
+ <li><strong>Web Page, Complete</strong>: Save the whole web page, along
+ with images and other supporting files. This option allows you to view
+ it as originally displayed with images. &brandShortName; creates a new
+ directory (where the page is saved) to save images and other files
+ necessary to show the whole web page.</li>
+ <li><strong>Web Page, HTML Only</strong>: Save the original page as-is
+ without images.</li>
+ <li><strong>Text file</strong>: Save the original page as a text file.
+ This option will not preserve the original HTML link structure, but
+ will allow you to see a text version of the web page in any text
+ editor.</li>
+ </ul>
+ </li>
+ <li>Type a file name for the page and click Save.</li>
+</ol>
+
+<p>When you view a page containing frames and a frame is currently selected,
+ the Save Frame As option is offered in the drop-down list in addition to Save
+ Page As. This lets you save only the page within the selected frame.</p>
+
+<p>Saving a file onto your hard drive lets you view the page (or its HTML code)
+ when you&apos;re not connected to the Internet.</p>
+
+<p>To save an image from a page:</p>
+
+<ol>
+ <li>Position the mouse pointer over the image.</li>
+ <li>Right-click<span class="mac"> or, if you have a one-button mouse,
+ <kbd>Ctrl</kbd>-click</span> the image to display a pop-up menu.</li>
+ <li>Choose Save Image As. You see the Save File dialog box.</li>
+ <li>Choose a folder in which to save this image.</li>
+ <li>Type a file name for the image and click Save.</li>
+</ol>
+
+<p>To save a page without displaying it (which is useful for retrieving a
+ non-formatted page, like a data file, that&apos;s not intended for
+ viewing):</p>
+
+<ol>
+ <li>Position the mouse pointer over the page&apos;s link.</li>
+ <li>Right-click<span class="mac"> or, if you have a one-button mouse,
+ <kbd>Ctrl</kbd>-click</span> the hyperlink on the page to display a pop-up
+ menu.</li>
+ <li>Choose Save Link Target As. You see the Save File dialog box.</li>
+ <li>Choose a folder in which to save this file.</li>
+ <li>Type a file name for the page and click Save.</li>
+</ol>
+
+<p><strong>Important</strong>: Some links automatically download and save files
+ to your hard drive after you click them. The URLs for these links often begin
+ with <q>ftp</q> or end with a file-type extension such as <q>au</q>
+ or <q>mpeg</q>. These links might transmit software, sound, or movie
+ files, and can launch helper applications that support the files.</p>
+
+<p id="setting_image_as_wallpaper"><strong>Tip</strong>: To set an image as
+ your desktop wallpaper on Windows, right-click on an image and choose Set As
+ Wallpaper from the pop-up menu.</p>
+
+<p>[<a href="#copying_saving_and_printing_pages">Return to beginning of
+ section</a>]</p>
+
+<h2 id="printing_a_page">Printing a Page</h2>
+
+<p>To print the current page, begin from the browser window:</p>
+
+<ul>
+ <li>Open the File menu and choose Print.</li>
+</ul>
+
+<p>To print selected text, begin from the browser window:</p>
+
+<ul>
+ <li>Select the text in the current page.</li>
+ <li>Open the File menu and choose Print. The print dialog box appears.</li>
+ <li>Under Print Range, click Selection.</li>
+</ul>
+
+<p>The size of the printed page, not the size of the onscreen window,
+ determines placement of content on the printed page. Text is wrapped and
+ graphics are repositioned to accommodate paper size.</p>
+
+<div class="noMac">
+
+<h3 id="print_preview">Using Print Preview</h3>
+
+<p>To have an early look at how a page will look before it is
+ printed, you can use Print Preview. Begin from the browser window:</p>
+
+<ul>
+ <li>Open the File menu and choose Print Preview.</li>
+</ul>
+
+<p>In Print Preview, you have the following options:</p>
+
+<ul>
+ <li class="win"><strong>Print</strong>: Click this to print pages.</li>
+ <li class="win"><strong>Page Setup</strong>: Click to further customize pages
+ you want to print.</li>
+ <li><strong>Page [__] of X</strong>: Enter a page number (less than X) to
+ jump to its preview. Click the First
+ <img src="chrome://navigator/skin/btn1/first.gif" alt="first button"/>,
+ Previous <img src="chrome://navigator/skin/btn1/previous.gif"
+ alt="previous button"/>, Next
+ <img src="chrome://navigator/skin/btn1/next.gif" alt="next button"/>,
+ or Last
+ <img src="chrome://navigator/skin/btn1/last.gif" alt="last button"/> button
+ to move between pages.</li>
+ <li class="win"><strong>Scale</strong>: Changes the scale (size) of pages,
+ using the dropdown list. You can choose between a fixed percentage of the
+ original page and two special options:
+ <ul>
+ <li><strong>Shrink To Fit Page Width</strong>: Select this to
+ automatically resize the page to the width of the paper.</li>
+ <li><strong>Custom</strong>: Type in the percentage of the original size
+ and press Enter. For example, type <q>50</q> and press Enter to make
+ each page half the size of the original page.</li>
+ </ul>
+ </li>
+ <li class="win"><strong>Portrait</strong>: Click on this to position the page
+ normally, with the shorter side facing up.</li>
+ <li class="win"><strong>Landscape</strong>: Click on this to position the
+ page sideways, with the longer side facing up.</li>
+ <li><strong>Close</strong>: Click on this to close the Print Preview
+ dialog.</li>
+</ul>
+
+</div>
+
+<h3 id="using_page_setup">Using Page Setup</h3>
+
+<p><strong>Note</strong>: Some Page Setup functions are different or
+ unavailable on macOS, Linux or Unix.</p>
+
+<p>To customize how pages are printed in &brandShortName;, you can use Page
+ Setup. From the browser, open the File menu and choose Page Setup.</p>
+
+<p>In Page Setup, you can change the following settings for pages you want to
+ print:</p>
+
+<ul>
+ <li><strong>Format &amp; Options</strong>: Choose the orientation, scale, and
+ other options:
+ <ul>
+ <li><strong>Orientation</strong>:
+ <ul>
+ <li><strong>Portrait</strong>: Choose this
+ <img src="chrome://global/skin/icons/Portrait.png"
+ alt="portrait button"/> to position the page normally, with the
+ shorter side facing up.</li>
+ <li><strong>Landscape</strong>: Choose this
+ <img src="chrome://global/skin/icons/Landscape.png"
+ alt="landscape button"/> to position the page sideways, with the
+ longer side facing up.</li>
+ </ul>
+ </li>
+ <li><strong>Scale</strong>: Type in a percentage of the original size.
+ For example, type <kbd>50</kbd> and to make each page half the size of
+ the original page.
+ <ul>
+ <li><strong>Shrink To Fit Page Width</strong>: Select this to
+ automatically resize the page to the width of the paper.</li>
+ </ul>
+ </li>
+ <li><strong>Options</strong>:
+ <ul>
+ <li><strong>Print Background (colors and images)</strong>: Select
+ this to print background images and colors. If unselected, only
+ images and color in the foreground (in front) are printed.</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ <li><strong>Margins &amp; Header/Footer</strong>: Click this tab to set up
+ margins, headers, and footers:
+ <ul>
+ <li><strong>Margins</strong>:
+ <ul>
+ <li><strong>Top, Bottom, Left, Right</strong>: Type a margin in
+ inches for the top, bottom, left, and right margin.</li>
+ </ul>
+ </li>
+ <li><strong>Headers &amp; Footers</strong>: Each drop-down list
+ represents either a header or a footer area. The top row of drop-down
+ lists are for the left, center, and right header areas. The bottom row
+ are for the left, center, and right footer areas. In each drop-down
+ list, choose one of the following options:
+ <ul>
+ <li><strong>--blank--</strong>: Show nothing in this area.</li>
+ <li><strong>Title</strong>: Show the web page title.</li>
+ <li><strong>URL</strong>: Show the web page URL (URLs usually
+ start with <q>http://</q>).</li>
+ <li><strong>Date/Time</strong>: Show the date and time when the web
+ page is printed.</li>
+ <li><strong>Page #</strong>: Show the page number of each page.</li>
+ <li><strong>Page # of #</strong>: Show the page number along with the
+ total number of pages. For example, if you print a five page web
+ page, <q>3 of 5</q> would be shown on the third page.</li>
+ <li><strong>Custom</strong>: Type your own text. You can include any
+ of the following codes to print specific information:
+ <ul>
+ <li><strong>&amp;PT</strong>: Page Number with Total (Example:
+ <q>3 of 5</q>)</li>
+ <li><strong>&amp;P</strong>: Page Number</li>
+ <li><strong>&amp;D</strong>: Date</li>
+ <li><strong>&amp;U</strong>: URL</li>
+ <li><strong>&amp;T</strong>: Page Title</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<p class="noMac"><strong>Tip</strong>: To see a preview of changes made to Page
+ Setup, use <a href="#print_preview">Print Preview</a>.</p>
+
+<p>[<a href="#copying_saving_and_printing_pages">Return to beginning of
+ section</a>]</p>
+
+<h1 id="using_languages_and_international_content">Using Languages and
+ International Content</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#selecting_text_encodings_and_fonts">Selecting Text Encodings
+ and Fonts</a></li>
+ <li><a href="#setting_language_preferences">Setting Language
+ Preferences</a></li>
+ <li><a href="#finding_localized_version">Finding a &brandShortName; version
+ in your own language</a></li>
+ </ul>
+</div>
+
+<h2 id="selecting_text_encodings_and_fonts">Selecting Text Encodings and
+ Fonts</h2>
+
+<p>If you browse, compose, or send and receive email in more than one language,
+ you need to select the appropriate text encodings and fonts.</p>
+
+<p>A text encoding method is the way characters in a document or message are
+ converted to data to be used by your computer. All web documents and mail or
+ news messages use a text encoding method (also known as a character set,
+ character coding, or charset).</p>
+
+<p>The text encoding method for a document may depend on its language.
+ Some languages e.g. most West European languages, share the same encoding
+ method. Others such as Chinese, Japanese, and Russian use different methods.
+ In contrast, Unicode provides language-independent encoding methods. UTF-8,
+ for example, can be used for any language document.</p>
+
+<p>Your version of &brandShortName; is set to a default text encoding which is
+ appropriate for your region. However, if you use more than one language, you
+ may need to select appropriate text encoding methods and designate the fonts
+ you wish to use for your language.</p>
+
+<p>To select text encodings, begin from the browser window:</p>
+
+<ol>
+ <li>Open the View menu, choose Text Encoding, and then choose More
+ Encodings.</li>
+ <li>Choose a region from the top section of the submenu.</li>
+ <li>Choose a text encoding within the region submenu. Repeat steps 1-3 for
+ each text encoding method you want.</li>
+</ol>
+
+<p>The text encoding methods you select are added to the Text Encoding menu.
+ If you have more than one encoding method selected, the active one has a
+ bullet (dot) next to it.</p>
+
+<p>If the page, which you are viewing, shows wrong character glyphs with all
+ predefined text encodings, there is a chance that it requires special fonts.
+ Such websites should contain instructions on which fonts to download and/or
+ use in order to view the page correctly. When you have the necessary fonts
+ installed on your system, you can choose User Defined from the More Encodings
+ submenu. &brandShortName; will then use the fonts defined in the <a
+ href="cs_nav_prefs_appearance.xhtml#fonts">Fonts preferences</a> (Fonts for:
+ User Defined).</p>
+
+<p>&brandShortName; can detect which text encoding a document uses, and can
+ display it correctly on your screen. To take advantage of this capability,
+ begin from the browser window:</p>
+
+<ol>
+ <li>Open the View menu, choose Text Encoding, and then choose
+ Auto-Detect.</li>
+ <li>Choose one of the Auto-Detect options, or choose (Off) from the
+ submenu.</li>
+</ol>
+
+<p>To make changes to your list of active text encodings:</p>
+
+<ol>
+ <li>Open the View menu, choose Text Encoding, and then choose Customize
+ List. You can see the Customize Text Encoding dialog box.</li>
+ <li>Choose from the following procedures:
+ <ul>
+ <li>To add to the list of active text encodings, choose a text encoding
+ from the list on the left and click Add.</li>
+ <li>To remove a text encoding from the active list, choose a text
+ encoding from the list on the right and click Remove.</li>
+ <li>To change the order in which active encodings appear in the Text
+ Encoding menu, highlight text encodings in the list on the right,
+ and use the Move Up and Move Down buttons to move the text encodings
+ up or down in the list.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>To change the default fonts within a language group:</p>
+
+<ol>
+ <li>Open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu and choose Preferences.</li>
+ <li>Under the Appearance category, click Fonts. (If no subcategories are
+ visible, double-click Appearance to expand the list.)</li>
+ <li>From the <q>Fonts for</q> drop-down list, choose a language
+ group/script. For instance, to set default fonts for West European
+ languages/script, choose <q>Western</q>.</li>
+ <li>Select whether proportional text should be serif (like Times Roman) or
+ sans serif (like Arial). You can also specify what font size you want for
+ proportional text. Proportional text varies in width.</li>
+ <li>(If available) Select a font for Serif, Sans-Serif, Cursive, and
+ Fantasy.</li>
+ <li>Select the monospace font (like Courier) that you want to use for web
+ pages. Monospace text is fixed in width, so each character or letter takes
+ the same amount of space.</li>
+</ol>
+
+<p>Many web page authors choose their own fonts and font sizes. You can use the
+ author&apos;s font settings by selecting <q>Allow documents to use other
+ fonts</q>.</p>
+
+<p>[<a href="#using_languages_and_international_content">Return to beginning of
+ section</a>]</p>
+
+<h2 id="setting_language_preferences">Setting Language Preferences</h2>
+
+<p>The language you use for &brandShortName; affects the user
+ interface&mdash;text of buttons, dialog boxes, menus, tools, and other items.
+ You can download and install language packages from the
+ <a href="http://www.seamonkey-project.org/releases/">SeaMonkey Project
+ Releases</a> page and then use the
+ <a href="cs_nav_prefs_appearance.xhtml#appearance">Appearance Preferences
+ panel</a> to switch user interface language.</p>
+
+<p>A web page can sometimes be available in several languages. In the
+ <a href="cs_nav_prefs_navigator.xhtml#languages">Languages Preferences
+ panel</a>, you can configure &brandShortName; so the page is shown in the
+ language you prefer. You can have multiple languages and list them in order
+ of preference.</p>
+
+<p>[<a href="#using_languages_and_international_content">Return to beginning
+ of section</a>]</p>
+
+<h2 id="finding_localized_version">Finding a &brandShortName; version in your
+ own language</h2>
+
+<p>If you&apos;re looking for a version of &brandShortName; in a language other
+ than American English, you can download it from the
+ <a href="http://www.seamonkey-project.org/releases/">SeaMonkey
+ Project Releases</a> page.</p>
+
+<p>[<a href="#using_languages_and_international_content">Return to beginning of
+ section</a>]</p>
+
+<h1 id="plugins_and_downloads">Plugins and Downloads</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#plugins">Plugins</a></li>
+ <li><a href="#helper_applications">Helper Applications</a></li>
+ <li><a href="#download_manager">Download Manager</a></li>
+ </ul>
+</div>
+
+<p>The browser can handle many types of files. However, for some files, such as
+ movies or music, &brandShortName; needs plugins or <q>helper</q> applications
+ that can handle those files. If the browser doesn&apos;t have the needed
+ helper application or plugin, it can still save the file to your hard disk.
+ When saving files, you can keep track of them using Download Manager.</p>
+
+<h2 id="plugins">Plugins</h2>
+
+<p>Plugins are helper applications that extend the functionality within the
+ browser and run within &brandShortName;. Plugins allow &brandShortName; to
+ show multimedia files and run small applications, such as movies, animations,
+ and games.</p>
+
+<p>&brandShortName; comes with no additional plugins installed, so you have
+ to add them separately.</p>
+
+<p>To see what plugins you currently have installed, do any of the following:
+</p>
+
+<ul>
+ <li>Open the Tools menu, choose Add-ons Manager, and select the Plugins
+ panel.
+
+ <p><strong>Tip</strong>: To learn more about the Add-ons Manager and its
+ features, see the section
+ <a href="customize_help.xhtml#using_the_add-ons_manager">Using the Add-ons
+ Manager</a>.</p>
+ </li>
+ <li>Click in the Location Bar, type <kbd>about:plugins</kbd> and press
+ <kbd class="mac">Return</kbd><kbd class="noMac">Enter</kbd>.</li>
+</ul>
+
+<p>[<a href="#plugins_and_downloads">Return to beginning of
+ section</a>]</p>
+
+<h2 id="helper_applications">Helper Applications</h2>
+
+<p>When files can not be used within &brandShortName;, you have the option to
+ launch helper applications that open outside of &brandShortName;. For
+ example, to play MP3 files, programs like Winamp can be opened outside of
+ &brandShortName;.</p>
+
+<p>The settings for this are explained in detail in
+ <a href="cs_nav_prefs_navigator.xhtml#helper_applications">Preferences
+ - Helper Applications</a>.</p>
+
+<p>[<a href="#plugins_and_downloads">Return to beginning of
+ section</a>]</p>
+
+<h2 id="download_manager">Download Manager</h2>
+
+<p>You can use Download Manager to keep track of files you download. Download
+ Manager shows the following information:</p>
+
+<ul>
+ <li>filename</li>
+ <li>time remaining before download is complete</li>
+ <li>transfer speed</li>
+ <li>percent complete</li>
+ <li>time elapsed</li>
+ <li>web location (source)</li>
+</ul>
+
+<p>To open Download Manager, do the following:</p>
+
+<ul>
+ <li>Open the Tools menu and choose Download Manager.</li>
+</ul>
+
+<p>The following menu options are available in Download Manager:</p>
+
+<ul>
+ <li><strong>Properties</strong>: Select a file being downloaded and click
+ Properties to show the progress dialog box.</li>
+ <li><strong>Cancel</strong>: Select a file being downloaded and click Cancel
+ to stop the download.</li>
+ <li><strong>Remove from List</strong>: Select a file and click Remove from
+ List to remove a canceled or finished download. This will not delete the
+ file from your hard disk.</li>
+ <li><strong>Launch File</strong>: Click this to open a selected file.</li>
+ <li><strong>Show in<span class="win"> Explorer</span><span class="unix">
+ Browser</span><span class="mac"> Finder</span></strong>: Click this to show
+ the location of a selected file.</li>
+</ul>
+
+<p>[<a href="#plugins_and_downloads">Return to beginning of
+ section</a>]</p>
+
+<h1 id="improving_speed_and_efficiency">Improving Speed and Efficiency</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#automatic_loading">Automatic Loading</a></li>
+ <li><a href="#custom_keywords">Using Custom Bookmark Keywords</a></li>
+ <li><a href="#changing_cache_settings">Changing Cache Settings</a></li>
+ <li><a href="#getting_the_latest_software_automatically">Getting the Latest
+ Software Automatically</a></li>
+ <li><a href="#using_a_mouse_wheel">Using a Mouse Wheel</a></li>
+ <li class="win"><a href="#making_mozilla_your_default_browser">Making
+ &brandShortName; Your Default Browser</a></li>
+ </ul>
+</div>
+
+<h2 id="automatic_loading">Automatic Loading</h2>
+
+<p>When you bring a web page to your screen, &brandShortName; automatically
+ loads (starts up) several features that help interpret web pages. These
+ features, like JavaScript, can make web pages more lively, but they take
+ time to load.</p>
+
+<p>To learn how to turn off JavaScript, see
+ <a href="cs_nav_prefs_advanced.xhtml#scripts_and_plugins">Advanced
+ Preferences - Scripts &amp; Plugins</a>.</p>
+
+<p>[<a href="#improving_speed_and_efficiency">Return to beginning of
+ section</a>]</p>
+
+<h2 id="custom_keywords">Using Custom Bookmark Keywords</h2>
+
+<p>Bookmark keywords allow you to create shorthand aliases for bookmarks and
+ Web searches. For example, if you give the bookmark to http://www.mozilla.org
+ the keyword <q>m.o</q>, you can enter <kbd>m.o</kbd> in the Location Bar and
+ the browser will load http://www.mozilla.org.</p>
+
+<p>To set a keyword, you must first create a bookmark for the URL. Then,</p>
+
+<ol>
+ <li>Open the Bookmarks menu and choose Manage Bookmarks.</li>
+ <li>In the Bookmarks window, click on the bookmark that you created.</li>
+ <li>Click Properties.</li>
+ <li>In the bookmark Properties dialog box window, enter a short string into
+ the Keyword field and close that dialog.</li>
+</ol>
+
+<p>Now, you can enter the keyword in the Location Bar, and &brandShortName;
+ will load that URL.</p>
+
+<h3 id="search_with_keywords">Search with Keywords</h3>
+
+<p>Custom keywords can be used to create shortcuts for your favorite search
+ engines, too. For example, you can create a keyword so that entering
+ <kbd>g Lord of the Rings</kbd> will perform a Google I-Feel-Lucky search
+ on Lord of the Rings.</p>
+
+<p>To create a custom keyword for use with a Web search:</p>
+
+<ol>
+ <li>Go to your search form (e.g. <kbd>www.google.com</kbd>).</li>
+ <li>Enter a dummy search string (e.g. <kbd>ILoveMozilla</kbd>).</li>
+ <li>Submit the search query.</li>
+ <li>After the results have loaded, open the Bookmarks menu and choose File
+ Bookmark.</li>
+ <li>In the File Bookmark dialog, look the Location field. Replace the
+ dummy string (e.g. <tt>ILoveMozilla</tt>) with <kbd>%s</kbd>.
+ For example, the location might become
+ <kbd>http://www.google.com/search?q=<strong>%s</strong>&amp;btnI=I&apos;mFeelingLucky</kbd>.</li>
+ <li>Enter a keyword in the Keyword field.</li>
+ <li>Give the bookmark a name and choose the location for the bookmark.</li>
+ <li>Close the dialog.</li>
+</ol>
+
+<p>Now you can search without going to the search page first by entering
+ <kbd><var>keyword</var> <var>search_words</var></kbd> in the Location Bar.
+</p>
+
+<p>For more details and examples, see
+ <a href="http://kb.mozillazine.org/Using_keyword_searches">"Using keyword
+ searches" at the MozillaZine Knowledge Base</a>.</p>
+
+<p>[<a href="#improving_speed_and_efficiency">Return to beginning of
+ section</a>]</p>
+
+<h2 id="changing_cache_settings">Changing Cache Settings</h2>
+
+<p>Your computer stores copies of frequently accessed pages in the cache. This
+ way, the computer doesn&apos;t have to retrieve the page from the network
+ each time you view it.</p>
+
+<p>To set the size of the cache or to clear it:</p>
+
+<ol>
+ <li>Open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Cache. (If no subcategories are
+ visible, double-click Advanced to expand the list.)</li>
+ <li>Enter a number in the Size field to specify the size of the cache. 50 MB
+ is sufficient. To clear the cache immediately, click Clear Cache.</li>
+</ol>
+
+<p><strong>Important</strong>: A larger disk cache allows more pages to be
+ quickly retrieved, but more of your hard disk space is used.</p>
+
+<p>When you quit &brandShortName;, it performs cache maintenance. If
+ maintenance takes longer than you wish, try reducing the size of the disk
+ cache.</p>
+
+<p>To specify how often the browser checks the network for page revisions (so
+ that you don&apos;t keep <q>stale</q> pages in the cache too long):</p>
+
+<ol>
+ <li>Open the
+ <span class="mac">&brandShortName;</span> <span class="noMac">Edit</span>
+ menu and choose Preferences.</li>
+ <li>Under the Advanced category, click Cache. (If no subcategories are
+ visible, double-click Advanced to expand the list.)</li>
+ <li>Choose from the following options:
+ <ul>
+ <li><strong>Every time I view the page</strong>: Select this if you want
+ &brandShortName; to compare a web page to the cache every time you view
+ it.</li>
+ <li><strong>When the page is out of date</strong>: Select this if you
+ want &brandShortName; to compare a web page to the cache when the page
+ is determined by the server to have expired.</li>
+ <li><strong>Once per session</strong>: Select this if you want
+ &brandShortName; to compare a web page to the cache once for each time
+ you start &brandShortName;.</li>
+ <li><strong>Never</strong>: Select this if you do not want
+ &brandShortName; to compare cached information to the network.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>If pages that should be in the cache are taking longer to appear than they
+ should, make sure the preference is not set to <q>Every time I view the
+ page</q>, because the verification requires a network connection that takes
+ time.</p>
+
+<p>To refresh a page at any time:</p>
+
+<ul>
+ <li>Click the Reload button in the browser&apos;s Navigation Toolbar. The
+ computer checks the network to make sure you have the latest version of the
+ page.</li>
+</ul>
+
+<p>[<a href="#improving_speed_and_efficiency">Return to beginning of
+ section</a>]</p>
+
+<h2 id="getting_the_latest_software_automatically">Getting the Latest Software
+ Automatically</h2>
+
+<p>&brandShortName; can notify you when updates for your software are
+ available, and it can install the updates automatically. &brandShortName; can
+ also inform you when new versions of &brandShortName; and installed add-ons
+ are available.</p>
+
+<p>To learn about setting up automatic software installation, see
+ <a href="cs_nav_prefs_advanced.xhtml#software_installation">Advanced
+ Preferences - Software Installation</a>.</p>
+
+<p>[<a href="#improving_speed_and_efficiency">Return to beginning of
+ section</a>]</p>
+
+<h2 id="using_a_mouse_wheel">Using a Mouse Wheel</h2>
+
+<p>If your mouse has a mouse wheel, you can control how the mouse wheel
+ functions in &brandShortName;.</p>
+
+<p>To learn more about setting up a mouse wheel, see
+ <a href="cs_nav_prefs_advanced.xhtml#mouse_wheel">Advanced Preferences -
+ Mouse Wheel</a>.</p>
+
+<p>[<a href="#improving_speed_and_efficiency">Return to beginning of
+ section</a>]</p>
+
+
+<div class="win">
+
+ <h2 id="making_mozilla_your_default_browser">Making &brandShortName; Your
+ Default Browser</h2>
+
+ <p>&brandShortName; is best known for displaying web pages, both on the
+ Internet and on your computer. To easily open web pages, you can make
+ &brandShortName; your default browser.</p>
+
+ <h3 id="common_internet_files_and_protocols">Common Internet Files and
+ Protocols</h3>
+
+ <p>Making &brandShortName; your default browser allows it to automatically
+ open common file formats and protocols used on the Internet. Common
+ Internet file formats and protocols include the following:</p>
+
+ <ul>
+ <li><strong>Image Files</strong>:
+ <ul>
+ <li>JPEG, GIF, PNG, BMP and ICO</li>
+ </ul>
+ </li>
+ <li><strong>Internet Document and Language Files</strong>:
+ <ul>
+ <li><a href="glossary.xhtml#html">HTML</a>, XHTML,
+ <a href="glossary.xhtml#xml">XML</a>, and
+ <a href="glossary.xhtml#xul">XUL</a></li>
+ </ul>
+ </li>
+ <li><strong>Internet Protocols</strong>:
+ <ul>
+ <li><a href="glossary.xhtml#http">HTTP</a>,
+ <a href="glossary.xhtml#https">HTTPS</a>,
+ <a href="glossary.xhtml#ftp">FTP</a>, Chrome</li>
+ </ul>
+ </li>
+ </ul>
+
+ <h3 id="changing_default_browser_settings_automatically">Changing Default
+ Browser Settings Automatically</h3>
+
+ <p>After installation is finished, &brandShortName; checks to see if it is
+ the default browser for any of the common Internet file formats or
+ protocols. If it isn&apos;t, you are asked, <q>&brandShortName; is not
+ currently set as your default browser. Would you like to make it your
+ default browser?</q></p>
+
+ <p>Click Yes to make &brandShortName; the default browser. If you click No,
+ you will be prompted with this question each time &brandShortName;
+ starts, unless you deselect the checkbox <q>Check at startup next time,
+ too</q>.</p>
+
+ <p>If you deselect the checkbox, <q>Check at startup next time, too</q>,
+ you can still make &brandShortName; the default browser by changing your
+ settings in Preferences. To learn how to set &brandShortName; as the
+ default browser through &brandShortName; preferences, see
+ <a href="cs_nav_prefs_navigator.xhtml#navigator">Browser Preferences -
+ Browser</a>.</p>
+
+ <p>[<a href="#improving_speed_and_efficiency">Return to beginning of
+ section</a>]</p>
+</div>
+
+<h1 id="proxies">Proxies</h1>
+
+<p>Many organizations block access from the Internet to their networks. This
+ prevents outside parties from gaining access to sensitive information. The
+ protection is called a firewall.</p>
+
+<p>If your organization has a firewall, the browser may need to go through a
+ proxy server before connecting you to the Internet. The proxy server prevents
+ outsiders from breaking into your organization&apos;s private network.</p>
+
+<p>For information on setting your proxy preferences, see
+ <a href="cs_nav_prefs_advanced.xhtml#proxies">Advanced Preferences -
+ Proxies</a>.</p>
+
+<p>[<a href="#proxies">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/page_info_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/page_info_help.xhtml
new file mode 100644
index 0000000000..fc923263dc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/page_info_help.xhtml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Viewing Page Info</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="viewing_page_info">Viewing Page Info</h1>
+
+<p>The Page Info dialog box consists of several tabs that display different
+ kinds of information about the page you are viewing. This section provides a
+ brief overview of the information available in each tab.</p>
+
+<p>To view Page Info for the page currently displayed by the browser, open the
+ View menu and choose Page Info. You can then click the tab that corresponds
+ to the kind of information you want to view.</p>
+
+<p>Most of the information displayed by the General, Media, Forms and Links
+ tabs in Page Info is taken from the HTML source for the page. This
+ information is usually of interest only to web developers and other
+ specialists.</p>
+
+<p>For detailed information about HTML, including the tags displayed by Page
+ Info, see the <a href="http://www.w3.org/TR/REC-html40/">HTML 4.01
+ Specification</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#general_tab">General Tab</a></li>
+ <li><a href="#media_tab">Media Tab</a></li>
+ <li><a href="#forms_tab">Forms Tab</a></li>
+ <li><a href="#links_tab">Links Tab</a></li>
+ <li><a href="#security_tab">Security Tab</a></li>
+ </ul>
+</div>
+
+<h2 id="general_tab">General (Page Info Tab)</h2>
+
+<p>When you choose Page Info from the View menu, the General tab displays basic
+ information about the page that you are viewing in the browser.</p>
+
+<p>The top portion displays the following information:</p>
+
+<ul>
+ <li><strong>Title</strong>: The name of the page (if it has one).</li>
+ <li><strong>Address</strong>: The
+ <a href="glossary.xhtml#url">Uniform Resource Locator</a> for the
+ page&mdash;that is, the standardized address that appears in the Location
+ Bar near the top of the browser window.</li>
+ <li><strong>Type</strong>: The document type of the page (usually text/html
+ for web pages).</li>
+ <li><strong>Render Mode</strong>: Indicates whether the browser is using
+ <strong>quirks mode</strong> or <strong>standards compliance mode</strong>
+ to lay out the page. Quirks mode takes account of nonstandard behavior that
+ may be used by some older web pages designed for older versions of web
+ browsers that are not fully standards compliant. Standards compliance mode
+ adheres strictly to standards specifications. Your browser chooses the
+ render mode automatically according to information contained in the web
+ page itself.</li>
+ <li><strong>Text Encoding</strong>: The text encoding used for this HTML
+ document.</li>
+ <li><strong>Size</strong>: The size of the file, if available.</li>
+ <li><strong>Referring URL</strong>: The address from where the current page
+ has been reached, if available.</li>
+ <li><strong>Modified</strong>: The date the page was last modified, if
+ available.</li>
+</ul>
+
+<p>The <i>Meta</i> portion displays the metatags specified by the page. Metatags
+ provide information about the type of content displayed by a page, such as a
+ general description of the page, keywords for search engines, copyright
+ information, and so on.</p>
+
+<p>The <i>Security information for this page</i> portion contains security
+ information concerning ownership and encryption. More details are available in
+ the <a href="#security_tab">Security tab</a>.</p>
+
+<h2 id="media_tab">Media (Page Info Tab)</h2>
+
+<p>When you choose Page Info from the View menu and click the Media tab, you
+ see a list of all the images and other media elements displayed by the
+ page.</p>
+
+<p>The top portion lists basic information about each element, including its
+ address (<a href="glossary.xhtml#url">URL</a>) and type.</p>
+
+<p>When you select a media element listed in the top portion of the Media tab,
+ the bottom portion displays available information about that element, such as
+ description, size, or dimensions.</p>
+
+<p>You can also see the selected element at the bottom of the dialog box. To
+ see larger images, you need to click the lower-right corner of the Page Info
+ dialog box and drag.</p>
+
+<p>To save a media element as a separate file:</p>
+
+<ul>
+ <li><strong>Save As</strong>: Select the element you want to save, click Save
+ As, and navigate to the location where you want to save it.</li>
+</ul>
+
+<h2 id="forms_tab">Forms (Page Info Tab)</h2>
+
+<p>When you choose Page Info from the View menu and click the Forms tab, you
+ see information about all the forms displayed by the page you are currently
+ viewing in the browser.</p>
+
+<p>The top portion lists basic information about the way each form in the page
+ is specified in the HTML source:</p>
+
+<ul>
+ <li><strong>Name</strong>: The form&apos;s name, if any.</li>
+ <li><strong>Method</strong>: The HTML method used to send information
+ captured by the form back to the web server. <tt>GET</tt> appends your
+ filled-in values to the website address to which it submits the form.
+ <tt>POST</tt> sends the values to the website as parameters that can be
+ read by a program on the website.</li>
+ <li><strong>Form Action</strong>: The URL of the program to be invoked when
+ the form is submitted.</li>
+</ul>
+
+<p>When you select a form listed in the top portion of the Forms tab, the
+ bottom portion displays detailed information about the way each of that
+ form&apos;s elements is specified in the HTML source:</p>
+
+<ul>
+ <li><strong>Label</strong>: The element&apos;s label (if it is tagged as such
+ in the HTML).</li>
+ <li><strong>Field Name</strong>: The element&apos;s name.</li>
+ <li><strong>Type</strong>: The element&apos;s input type, such as
+ <tt>TEXT</tt> (for submitting text), <tt>RADIO</tt> (for a radio button),
+ or <tt>HIDDEN</tt> (for storing information that is submitted but not
+ displayed on the screen).</li>
+ <li><strong>Current Value</strong>: The current value of the element. For
+ example, the current value of a text element is the text it currently
+ contains.</li>
+</ul>
+
+<h2 id="links_tab">Links (Page Info Tab)</h2>
+
+<p>When you choose Page Info from the View menu and click the Links tab, you
+ see a list of all the links available on that page. The following information
+ is displayed for each link:</p>
+
+<ul>
+ <li><strong>Name</strong>: The text displayed in the browser as a link.</li>
+ <li><strong>Address</strong>: The <a href="glossary.xhtml#url">URL</a> for the
+ page to which the link points.</li>
+ <li><strong>Type</strong>: The type of link, such Anchor (for a link to a
+ specific place in an HTML document) or Form Submission.</li>
+</ul>
+
+<h2 id="security_tab">Security (Page Info Tab)</h2>
+
+<p>When you choose Page Info from the View menu and click the Security tab, you
+ see information about
+ <a href="glossary.xhtml#authentication">authentication</a> and
+ <a href="glossary.xhtml#encryption">encryption</a> for the web page you are
+ viewing. The top portion indicates whether the website&apos;s identity has
+ been verified, and the bottom portion describes whether the page was
+ encrypted when it was received by the browser.</p>
+
+<p>You can also open the Security tab directly by clicking the lock icon in the
+ lower-right corner of any browser window.</p>
+
+<p>The top portion of the Security tab can display any of these messages:</p>
+
+<ul>
+ <li><strong>Website Identity Not Verified</strong>: The website you are
+ viewing did not present a certificate to authenticate itself. Therefore,
+ Certificate Manager cannot verify its identity. It is possible, though
+ unlikely, that the website is not what it claims to be.</li>
+ <li><strong>Website Identity Verified</strong>: The certificate that
+ Certificate Manager has used to verify this website&apos;s identity was
+ issued by a certificate authority (CA) marked as one that you trust. You
+ can be reasonably confident that the website is what it claims to be.</li>
+</ul>
+
+<p>The bottom portion of the Security tab can display any of these
+ messages:</p>
+
+<ul>
+ <li><strong>Connection Not Encrypted</strong>: It is possible that other
+ people can view information sent from your computer to the website or
+ information sent by the website to your computer, but it is unlikely that
+ someone is actually doing so.</li>
+ <li><strong>Connection Encrypted</strong>: In general, the strength of an
+ encrypted connection depends on the length of the keys used for encryption,
+ measured in bits. The longer the key, the stronger the
+ encryption&mdash;that is, the harder it is to for an unauthorized person to
+ unscramble the encrypted information.</li>
+</ul>
+
+<p>Lack of any encryption or lack of strong encryption should be of concern
+ only if you are sending or viewing confidential information, such as your
+ credit card number.</p>
+
+<p>You can quickly check the encryption status of a web page by noting the
+ state of the lock icon at the bottom-right corner of the browser window. For
+ more details, see
+ <a href="using_certs_help.xhtml#checking_security_for_a_web_page">Checking
+ Security for a Web Page</a>.</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/passwords_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/passwords_help.xhtml
new file mode 100644
index 0000000000..4eff45a2a3
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/passwords_help.xhtml
@@ -0,0 +1,407 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Password Settings</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="password_settings">Password Settings</h1>
+
+<p>This section describes how to set your password preferences, set your Master
+ Password, and control other aspects of password handling.</p>
+
+<p>For step-by-step descriptions of various tasks related to passwords, see
+ <a href="using_priv_help.xhtml#using_the_password_manager">Using the Password
+ Manager</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#passwords">Privacy &amp; Security Preferences -
+ Passwords</a></li>
+ <li><a href="#password_manager">Password Manager</a></li>
+ <li><a href="#change_master_password">Change Master Passwords</a></li>
+ <li><a href="#reset_master_password">Reset Master Password</a></li>
+ <li><a href="#choosing_a_good_password">Choosing a Good Password</a></li>
+ </ul>
+</div>
+
+<h2 id="passwords">Privacy &amp; Security Preferences - Passwords</h2>
+
+<p>This section describes the Passwords preferences panel. If you&apos;re not
+ already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<h3>Password Manager</h3>
+
+<p>Password Manager preferences allow you to</p>
+
+<ul>
+ <li><strong>Remember passwords</strong>: Select this checkbox to turn
+ Password Manager on, so that it asks to store your user names and passwords
+ at appropriate times and enters them for you automatically when
+ they&apos;re requested. To turn off Password Manager, deselect the same
+ checkbox.</li>
+ <li><strong>Manage Stored Passwords</strong>: Click this button to manage
+ information about your stored passwords and the websites whose user names
+ and passwords you don&apos;t want to be stored.</li>
+</ul>
+
+<p>For detailed information about using Password Manager, including how to
+ override it for individual websites and how to view and manage stored
+ passwords,
+ see <a href="using_priv_help.xhtml#using_the_password_manager">Using the
+ Password Manager</a>.</p>
+
+<h3 id="master_passwords">Master Passwords</h3>
+
+<p>A master password protects a security device, which is a software or
+ hardware device that stores sensitive information associated with your
+ identity, such as keys or certificates.</p>
+
+<p>For example, the browser has a built-in Software Security Device, and you
+ can also use external security devices, such as smart cards, if your computer
+ is configured to use them.</p>
+
+<p>The master password for the browser&apos;s built-in Software Security Device
+ also protects stored sensitive information such as email passwords, website
+ passwords, and other data stored by the Password Manager.</p>
+
+<p>Each security device, whether it is software or hardware, has its own
+ separate Master Password.</p>
+
+<ul>
+ <li><strong>Change Password</strong>: Click this button to set or change any
+ of your master passwords. For information about using the Change Master
+ Password dialog box that appears when you click this button, see
+ <a href="#change_master_password">Change Master Password</a>.</li>
+ <li>You can control how often the browser requests your master password:
+ <ul>
+ <li><strong>The first time it is needed</strong>: This setting
+ (selected by default) causes the browser to request your master
+ password only the first time it needs access to the private key
+ database after launching. The browser will not request the master
+ password again until after you exit and relaunch it. This setting
+ provides the lowest level of protection.</li>
+ <li><strong>Every time it is needed</strong>: This setting ensures that
+ the browser will never access your saved personal information without
+ first requesting your master password. This setting provides the
+ highest level of protection.</li>
+ <li><strong>If it has not been used for [__] minutes or longer</strong>:
+ This setting causes the browser to request your master password if it
+ needs to access your personal information and the specified interval
+ has elapsed since the last time it did so.</li>
+ </ul>
+ </li>
+ <li><strong>Reset Master Password</strong>: Click this button to reset the
+ master password for the Software Security Device. For more information,
+ see <a href="#reset_master_password">Reset Master Password</a>.</li>
+</ul>
+
+<h3 id="encrypting_versus_obscuring">Encrypting Versus Obscuring</h3>
+
+<p>If you use Password Manager to save passwords and personal data, this
+ sensitive information is stored on your computer in a file that&apos;s
+ difficult, but not impossible, for an intruder to read. This way of storing
+ information is sometimes described as <q>obscuring</q>. This is the default
+ setting that applies to information stored by Password Manager.</p>
+
+<p>For improved protection, you may choose to protect the file with encryption.
+ Encryption makes it more difficult (but again, not impossible) for an
+ unauthorized person to view your stored sensitive information. To turn on
+ encryption you need to set a <a href="glossary.xhtml#master_password">master
+ password</a>.</p>
+
+<p>Using encryption versus obscuring for stored sensitive data is a tradeoff
+ between improved security and convenience:</p>
+
+<ul>
+ <li>If you use encryption, you will need to enter a master password
+ periodically, which can be inconvenient. (For further information see the
+ discussion of the Master Password at <a href="#master_passwords">Master
+ Passwords</a>.)</li>
+ <li>If you use obscuring, you may not have to set a master password at all
+ (unless you&apos;re using certificates for identification purposes), but it
+ may be easier for a stranger who has access to your computer to steal your
+ passwords.</li>
+</ul>
+
+<p>For more details, see
+ <a href="using_priv_help.xhtml#encrypting_stored_sensitive_information">Encrypting
+ Stored Sensitive Information</a>.</p>
+
+<h2 id="password_manager">Password Manager</h2>
+
+<p>This section describes how to use the Password Manager dialog box to control
+ your stored passwords. If you are not already viewing it, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Click Manage Stored Passwords.</li>
+</ol>
+
+<p>Alternatively, open the Tools menu, choose Password Manager, and then choose
+ Manage Stored Passwords from the submenu.</p>
+
+<p>The Password Manager has two tabs:</p>
+
+<ol>
+ <li><strong>Passwords Saved</strong>: Click this tab to view the list of
+ websites for which Password Manager has saved your user name and
+ password&mdash;that is, the websites for which you selected <q>Yes</q>
+ in response to Password Manager&apos;s request to store logon
+ information.
+
+ <p>The second column shows the user name for each website. If the password
+ is stored in encrypted form, <q>(encrypted)</q> appears after the user
+ name.</p>
+
+ <p>By default, stored passwords are not displayed.</p>
+
+ <ul>
+ <li>To see the list of stored passwords, click Show Passwords and confirm
+ your choice.</li>
+ <li>To hide the passwords, click Hide Passwords.</li>
+ </ul>
+
+ <p>If you remove an entry from the list, the stored user name and password
+ will be discarded, and you will need to log in manually the next time you
+ visit that website.</p>
+ </li>
+ <li><strong>Passwords Never Saved</strong>: Click this tab to view the list
+ of websites for which you selected <q>Never for this site</q> in response
+ to Password Manager&apos;s request to store logon information.
+
+ <p>If a website is included on this list, you will always have to type in
+ your user name and password manually when you log onto the website.</p>
+
+ <p>If you remove an entry from this list, Password Manager will again ask
+ you, the next time you log onto the website, whether to store your user
+ name and password.</p>
+ </li>
+</ol>
+
+<p>Regardless of which tab you are viewing, you can remove entries from the
+ list as follows:</p>
+
+<ul>
+ <li><strong>Remove</strong>: Select one or more entries that you want to
+ remove, then click Remove.</li>
+ <li><strong>Remove All</strong>: Click this button to remove all the entries
+ listed in the tab you are viewing.</li>
+</ul>
+
+<p>For more information about the Password Manager, see <a href=
+ "using_priv_help.xhtml#using_the_password_manager">Using the Password
+ Manager</a>.</p>
+
+<h2 id="change_master_password">Change Master Password</h2>
+
+<p>You must remember your old master password to change it with the Change
+ Password button.</p>
+
+<p>This section describes the Change Master Password dialog box. If you&apos;re
+ not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Click Change Password.</li>
+</ol>
+
+<p>A master password protects a security device, which is a software or
+ hardware device that stores sensitive information associated with your
+ identity, such as keys or certificates.</p>
+
+<p>For example, the browser has a built-in Software Security Device, and you
+ can also use external security devices, such as smart cards, if your computer
+ is configured to use them.</p>
+
+<p>The master password for the browser&apos;s built-in Software Security Device
+ also protects your master key. Your master key is used to encrypt sensitive
+ information such as email passwords, website passwords, and other data stored
+ by the Password Manager.</p>
+
+<p>You use the Change Master Password dialog box to provide the following
+ information:</p>
+
+<ul>
+ <li><strong>Security Device</strong>: Each security device requires a
+ separate master password. For example, if you are using one or more smart
+ cards to store some of your certificates, you should set a separate master
+ password for each one. If more than one security device is available, a
+ drop-down list at the top of the Set Master Password dialog box allows you
+ to choose the device whose password you want to change.</li>
+ <li><strong>Current password</strong>: If you are changing an existing master
+ password, you must first type the current password. If you don&apos;t type
+ the current password correctly, you will see the message <q>You did not
+ enter the current correct Master Password</q> after you click OK. If this
+ happens, you must retype your current password.</li>
+ <li><strong>New password</strong>: Type your new password into this
+ field.</li>
+ <li><strong>New password (again)</strong>: Type your new password again. If
+ you don&apos;t type it the second time exactly as you did the first time,
+ the OK button remains inactive. If this happens, try typing the new
+ password again.</li>
+</ul>
+
+<p>If someone uses your computer who knows or can guess your master password,
+ that person may be able to access websites while pretending to be you. This
+ can be dangerous&mdash;for example, if you manage your financial accounts
+ over the Internet.</p>
+
+<p>Therefore, it&apos;s important to select a master password that&apos;s
+ difficult to guess. The <strong>password quality meter</strong> gives you a
+ rough idea of the quality of your password as you type it based on factors
+ such as length and the use of uppercase letters, lowercase letters, numbers,
+ and symbols. It does not guarantee, however, that no one will be able to
+ guess your password.</p>
+
+<p>For further guidelines, see <a href="#choosing_a_good_password">Choosing
+ a Good Password</a>.</p>
+
+<p>It&apos;s also important to record your master password in a safe
+ place&mdash;and <strong>not</strong> anywhere that&apos;s easily accessible
+ to someone else. If you forget this password, you may not be able to access
+ important information, such as websites that require passwords or
+ certificates stored on your computer.</p>
+
+<h2 id="reset_master_password">Reset Master Password</h2>
+
+<p><strong>Warning</strong>: If you reset your master password, you will
+ permanently erase all the encrypted web and email passwords, saved on your
+ behalf by Password Manager. You will also lose all your personal certificates
+ associated with the <a href="glossary.xhtml#software_security_device">
+ Software Security Device</a>.</p>
+
+<p>To change your master password rather than resetting it, click the Change
+ Password button in the Passwords preferences panel.</p>
+
+<p>This section describes the Reset Master Password dialog box. If you&apos;re
+ not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Click Reset Password.</li>
+</ol>
+
+<p><strong>Warning</strong>: If you reset your master password, you will
+ permanently erase all encrypted web and email passwords, saved on your behalf
+ by Password Manager You will also lose all your personal certificates
+ associated with the
+ <a href="glossary.xhtml#software_security_device">software security
+ device</a>.</p>
+
+<p>If you remember your master password and decide to change it, you can do so
+ without danger of losing any personal information. If you are viewing the
+ Reset Master Password alert and you decide you want to change your password
+ rather than resetting it, click Cancel to return to the Passwords
+ preferences panel, then click Change Password. For details, see
+ <a href="#change_master_password">Change Master Password</a>.</p>
+
+<p>Resetting your master password is a last resort that you should use only if
+ you are absolutely sure you&apos;ve forgotten it. The seriousness of the
+ situation depends on how much personal data your forgotten master password
+ protects.</p>
+
+<p>Resetting your master password does not create a new password. Instead, it
+ removes all the data your old master password protects. You will be asked to
+ specify a new master password the next time the browser needs to store
+ personal information.</p>
+
+<p>After you reset your master password, you may also want to re-save personal
+ information that you want to have prefilled in the future. For example, as
+ you browse you may want Password Manager to save website and email passwords
+ again. In addition, any personal certificates associated with the software
+ security device will be permanently erased and you will need to apply for new
+ ones.</p>
+
+<p><strong>Note for smart card users</strong>: Each smart card has its own
+ master password. The master password for a smart card protects only the data
+ on that smart card (such as personal certificates). You can normally change
+ the master password for a smart card (assuming that you remember it), but you
+ cannot reset it.</p>
+
+<h2 id="choosing_a_good_password">Choosing a Good Password</h2>
+
+<p>Choosing a good password will help in keeping your personal information
+ safe and private. To improve the security of your password, follow some
+ or all of these suggestions:</p>
+
+<ul>
+ <li>Special and punctuation characters (*!$+) mixed with letters and
+ numbers.</li>
+ <li>Mixed upper and lower-case letters&mdash;putting capitals in random
+ locations throughout a password is effective.</li>
+ <li>Nonsense words that aren&apos;t found in dictionaries but are easy to
+ pronounce.</li>
+ <li>Eight or more characters.</li>
+</ul>
+
+<p>You should avoid personal information that could be guessed. So the
+ following common items should be avoided:</p>
+
+<ul>
+ <li>Personal or family names, your initials or birthdays.</li>
+ <li>Your social security number.</li>
+ <li>Names of pets or famous places.</li>
+ <li>Phone numbers or addresses.</li>
+ <li>Words from any kind of dictionary.</li>
+ <li>Your username, login name or computer&apos;s name.</li>
+ <li>Repetition of the same letter or symbol.</li>
+ <li>Sequences of keyboard keys, such as <q>12345</q> or <q>qwerty</q>.</li>
+ <li>Any minor modification of the above, such as appending a character to the
+ end of your name or spelling backwards.</li>
+</ul>
+
+<p>A good way to choose a secure but easily remembered password is to use the
+ first character of each word in a phrase. For instance, <q>StNh*nbsS</q>
+ stands for <q>Surfing the Net has never been so Suite</q>; the asterisk in
+ the middle is included for increased security. (Don&apos;t use this
+ password!)</p>
+
+<p>To further protect your personal data, you are advised to follow these
+ simple rules:</p>
+
+<ul>
+ <li>Never give the password out to anyone.</li>
+ <li>If someone has learnt your password, change it immediately.</li>
+ <li>Every few months, change your password.</li>
+ <li>Choose a password you can remember so you don&apos;t have to write it
+ down.</li>
+ <li>Avoid letting people observe you typing your password.</li>
+</ul>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/privacy_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/privacy_help.xhtml
new file mode 100644
index 0000000000..67ea412bfc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/privacy_help.xhtml
@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Privacy on the Internet</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="using_privacy_features">Using Privacy Features</h1>
+
+<p>Your browser includes features you can use to enhance the privacy and
+ security of your personal information. The sections that follow describe how
+ your browser can help you control cookies, passwords, and images while you
+ are surfing the Internet.</p>
+
+<p>For information about related &brandShortName; security features, see
+ <a href="mailnews_security.xhtml">Signing &amp; Encrypting Messages</a> and
+ <a href="using_certs_help.xhtml">Using Certificates</a>.</p>
+
+<div class="contentsBox">Privacy topics:
+ <ul>
+ <li><a href="#privacy_on_the_internet">Privacy on the Internet</a></li>
+ <li><a href="using_priv_help.xhtml#using_the_cookie_manager">Using the
+ Cookie Manager</a></li>
+ <li><a href="using_priv_help.xhtml#using_the_password_manager">Using the
+ Password Manager</a></li>
+ <li><a href="using_priv_help.xhtml#clearing_private_data">Clearing Private
+ Data</a></li>
+ <li><a href="using_priv_help.xhtml#browsing_in_a_private_window">Browsing
+ in a Private Window</a></li>
+ <li><a href="using_priv_help.xhtml#encrypting_stored_sensitive_information">Encrypting
+ Stored Sensitive Information</a></li>
+ <li><a href="using_priv_help.xhtml#managing_images">Managing Images</a></li>
+ <li><a href="cs_priv_prefs_popup.xhtml#controlling_popups">Controlling Popups</a></li>
+ <li><a href="privsec_help.xhtml">Privacy &amp; Security Preferences</a></li>
+ </ul>
+</div>
+
+<h1 id="privacy_on_the_internet">Privacy on the Internet</h1>
+
+<p>This section summarizes some background information about privacy on the
+ Internet. It also describes several things you can do to help safeguard your
+ own privacy. It is not intended to provide a complete description of Internet
+ privacy issues.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#what_information_does_my_browser_give_to_a_website">What
+ Information Does My Browser Give to a Website?</a></li>
+ <li><a href="#what_are_cookies_and_how_do_they_work">What Are Cookies, and
+ How Do They Work?</a></li>
+ <li><a href="#why_and_how_are_websites_tracking_me">Why and How Are
+ Websites Tracking Me?</a></li>
+ <li><a href="#how_can_i_control_web_pages_in_email_messages">How Can I
+ Control Web Pages in Email Messages?</a></li>
+ <li><a href="#how_can_i_make_sure_unauthorized_people_dont_use_information_about_me">How
+ Can I Make Sure Unauthorized People Don&apos;t Use Information About
+ Me?</a></li>
+ </ul>
+</div>
+
+<h2 id="what_information_does_my_browser_give_to_a_website">What Information
+ Does My Browser Give to a Website?</h2>
+
+<p>When your browser displays a web page&mdash;for example, each time you click
+ a link or type a URL, or when a web page is displayed in an email
+ message&mdash;it gives certain kinds of information to the website. This
+ information may include (but is not limited to) your operating environment,
+ your Internet address, and the page you&apos;re coming from.</p>
+
+<h3>Operating Environment</h3>
+
+<p>The website is told something about your operating environment, such as your
+ browser type and operating system. This helps the website present the page in
+ the best way for your screen. For example, the website might learn that you
+ use &brandShortName;&nbsp;2.19 on a Windows&nbsp;7 computer.</p>
+
+<h3 id="internet_address">Internet Address</h3>
+
+<p>Your browser must tell the website your Internet address (also known as the
+ Internet Protocol, or IP address) so the website knows where to send the page
+ you are requesting. The website can&apos;t present the page you want to see
+ unless it knows your IP address.</p>
+
+<p>Your IP address can be either temporary or fixed (static).</p>
+
+<p>If you connect to the Internet through a standard modem that&apos;s attached
+ to your phone line, then your Internet service provider (ISP) may assign you
+ a temporary IP address each time you log on. You use the temporary IP address
+ for the duration of your Internet session&mdash;for example, until you sign
+ off or hang up your dial-up connection, or otherwise end your computer&apos;s
+ live connection with the Internet. Each ISP has many IP addresses, and they
+ assign the addresses at random to users.</p>
+
+<p>If you have DSL, a cable modem, or a fiber-optic connection, you may have a
+ fixed IP address that you use every time you connect.</p>
+
+<p>Your IP address is not the same as your email address.</p>
+
+<h3>Referring Page</h3>
+
+<p>The website is also told which page you were reading when you clicked a link
+ to see one of the website&apos;s pages. This allows the website to know which
+ website referred you. Or, as you traverse the website, it allows the website
+ to know which of its pages you came from.</p>
+
+<p>[<a href="#privacy_on_the_internet">Return to beginning of section</a>]</p>
+
+<h2 id="what_are_cookies_and_how_do_they_work">What Are Cookies, and How Do
+ They Work?</h2>
+
+<p>A cookie is a small bit of information used by some websites. When you
+ visit a website that uses cookies, the website might ask your browser to place
+ one or more cookies on your hard disk.</p>
+
+<p>Later, when you return to the website, your browser sends back the cookies
+ that belong to the website.</p>
+
+<p>When you are using the default cookie settings, this activity is invisible
+ to you, and you won&apos;t know when a website is setting a cookie or when
+ your browser is sending a website&apos;s cookie back. However, you can set
+ your preferences so that you will be asked before a cookie is set. For
+ information on how to do this, see
+ <a href="using_priv_help.xhtml#cookies">Privacy &amp; Security Preferences -
+ Cookies</a>.</p>
+
+<h3>How Do Websites Use Cookie Information?</h3>
+
+<p>Cookies allow a website to know something about your previous visits. For
+ example, if you typically search for local weather or purchase books at a
+ website, the website may use cookies to remember what city you live in or what
+ authors you like, so it can make your next visit easier and more useful.</p>
+
+<p>Some websites publish privacy policies that describe how they use the
+ information they gather.</p>
+
+<h3 id="what_are_third-party_cookies">What Are Third-Party Cookies?</h3>
+
+<p>If your browser stores a website&apos;s cookie, it will return the cookie
+ only to that particular website. Your browser will not provide one website
+ with cookies set by another. Since a website can only receive its own cookies,
+ it can learn about your activities while you are at that website but not your
+ activities in general while surfing the Web.</p>
+
+<p>But sometimes a website displays content that is hosted on another website.
+ That content can be anything from an image to text or an advertisement.
+ The other website that hosts such elements also has the ability to store a
+ cookie in your browser, even though you don&apos;t visit the website directly.
+</p>
+
+<p>Cookies that are stored by a website other than the one you are visiting are
+ called <strong>third-party cookies</strong> or <strong>foreign
+ cookies</strong>. Websites sometimes use third-party cookies with
+ <strong>transparent GIFs</strong>, which are special images that help websites
+ count users, track email responses, learn more about how visitors use the
+ website, or customize your browsing experience. (Transparent GIFs are also
+ known as web beacons or web bugs.)</p>
+
+<p>If you want, you can <a href="using_priv_help.xhtml#cookies">adjust your
+ cookie preferences</a> so that websites can store ordinary cookies but not
+ third-party ones, or only for those sites that you have previously visited.
+</p>
+
+<p>[<a href="#privacy_on_the_internet">Return to beginning of section</a>]</p>
+
+<h2 id="why_and_how_are_websites_tracking_me">Why and How Are Websites
+ Tracking Me?</h2>
+
+<p>Websites are frequently interested in how they are used and by whom.
+ Thus, they may perform analytics on your browsing behavior, either by
+ themselves, or by utilizing a third-party service.</p>
+
+<p>Motivations for such user tracking may be a more personalized browsing
+ experience, e.g., by offering you services or products corresponding to your
+ previous browsing pattern, thus making it more likely that you are interested
+ in those. Third-party services are mostly interested in placing advertisements
+ that match your interests, thus making it more likely that you would click on
+ those ads, which in turn generates revenue.</p>
+
+<p>Of course, this list isn&apos;t all inclusive, other reasons may exist to
+ track the user&apos;s browsing behavior. To summarize, the trade-off is:</p>
+
+<ul>
+ <li><strong>Pros</strong>: Tracking of the user&apos;s browsing activity
+ allows the website to provide a more specific browsing experience, by
+ analyzing and matching your interests. You may even get offers you
+ wouldn&apos;t receive without prior knowledge of your usage pattern.</li>
+ <li><strong>Cons</strong>: In most cases, especially with third-party
+ tracking services, the information is gathered without the user&apos;s
+ consent, and its usage is not transparent. Comprehensive user profiling
+ across websites and over long periods of time may be considered intrusive
+ and a questionable practice violating the user&apos;s privacy.</li>
+</ul>
+
+<h3>What Are the Mechanisms of User Tracking?</h3>
+
+<p>There isn&apos;t any single tracking mechanism, rather several methods
+ exist. For example, a website may employ one or more of these methods:</p>
+
+<ul>
+ <li><a href="#what_are_cookies_and_how_do_they_work">Cookies</a>: This is
+ the most direct form of user tracking. When visiting a website for the
+ first time, a random but unique identifier is created by that site and
+ stored as a cookie in &brandShortName;. When returning to the website,
+ this cookie associates you with any previous visit to that site. This is
+ especially effective for third-party tracking given that such a service
+ can collect data from multiple websites they have contracts with based on
+ just a single tracking cookie.</li>
+ <li><a href="#internet_address">Internet Address</a>: The IP address
+ identifies your location on your provider&apos;s network. It may also
+ serve as a reference to your geographical location. Even without cookies,
+ multiple accesses to one or more websites from the same location can be
+ associated with each other in this way.</li>
+ <li><a href="privsec_help.xhtml#location_aware_browsing">Geolocation</a>:
+ Much more detailed information about the user&apos;s location can be
+ obtained with location aware browsing services. Based on additional
+ information gathered by the browser, such as wireless access points,
+ a user&apos;s position and (in certain situations) heading and speed can
+ be determined and used by the website to track the user.</li>
+ <li><a href="#what_information_does_my_browser_give_to_a_website">Browser
+ Fingerprinting</a>: This method attempts to identify specific users based
+ on the characteristics of the browser that they are using. Such
+ characteristics include browser type and version, platform used, language
+ preferences, and possibly installed plugins or add-ons.</li>
+</ul>
+
+<h3>What Can I Do to Prevent User Tracking?</h3>
+
+<p>In general, there is no complete protection against unwanted tracking of
+ one&apos;s browsing activities. However, there are a couple of ways to make
+ it harder to get tracked:</p>
+
+<ul>
+ <li><a href="privsec_help.xhtml#privacy_and_security">Do Not Track</a>:
+ This is a method that allows users to explicitly opt out of their
+ browsing behavior being tracked. &brandShortName; supports sending
+ <q>Do Not Track</q> requests to websites, but they are <em>not</em>
+ obligated to honor such requests.</li>
+ <li><a href="privsec_help.xhtml#privacy_and_security">Site blocking</a>:
+ &brandShortName; can block content that has been identified as serving
+ the purpose of user tracking based on a list downloaded periodically
+ from a server. In this way, connections to such <em>known</em> tracking
+ sites can be prevented upfront.</li>
+ <!-- NOTE: link "Data Manager" below once bug 599097 has been fixed -->
+ <li><a href="privsec_help.xhtml#location_aware_browsing">Location Aware
+ Browsing</a>: Disabling geolocation services prevents a website from
+ obtaining location information beyond what can be derived from the IP
+ address of the access alone. Note though that this is always an opt-in
+ service, thus you will get a notification for each request unless you
+ gave a website permanent permission to use geolocation services.</li>
+ <!-- NOTE: link "Data Manager" below once bug 599097 has been fixed -->
+ <li><a href="using_priv_help.xhtml#cookies">Cookies</a>: The power of
+ tracking cookies is their persistence across sessions. Thus, when you
+ restart &brandShortName;, the identifying cookie will still be sent to
+ the website performing the analytics. One measure against third-party
+ tracking is to prohibit third-party cookies altogether; another option is
+ the restriction of cookies to the current session only. Some websites may
+ no longer function correctly, but you can add exceptions to such sites in
+ the Data Manager or with the Cookie Manager options in the Tools menu.</li>
+ <li><a href="nav_help.xhtml#plugins">Plugins</a>: Obscure plugins make
+ browser fingerprinting easier. Thus, if you need such plugins for some
+ websites but not in general, disable them in the Add-ons Manager until
+ you need them, to avoid that they are disclosed to tracking sites.</li>
+ <li><a href="customize_help.xhtml#add-ons">Add-ons</a>: There is a variety
+ of extensions available for privacy &amp; security support. Some of them
+ will block unwanted content from advertisers or tracking services, others
+ provide more control on private data collected by plugins (e.g. cookies),
+ or when and how to run scripts or embedded content.</li>
+</ul>
+
+<p><strong>Note</strong>: The issues related to user tracking are complex.
+ This description is not intended to be complete but to provide you with some
+ basic information on this topic.</p>
+
+<p>[<a href="#privacy_on_the_internet">Return to beginning of section</a>]</p>
+
+<h2 id="how_can_i_control_web_pages_in_email_messages">How Can I Control Web
+ Pages in Email Messages?</h2>
+
+<p>You can disable cookies, images, and plugins completely (JavaScript is
+ always disabled) for web pages that are received as part of email
+ messages.</p>
+
+<p>While it may be convenient to enable some or all of these capabilities when
+ you&apos;re browsing the web, they may not be necessary in single web pages
+ sent as attachments to messages.</p>
+
+<p>For information on enabling or disabling cookies, images, and plugins in
+ email messages, see the following sections:</p>
+
+<ul>
+ <li><a href="using_priv_help.xhtml#cookies">Privacy &amp; Security
+ Preferences - Cookies</a></li>
+ <li><a href="mailnews_preferences.xhtml#message_display">Mail &amp; Newsgroups
+ Preferences - Message Display</a></li>
+ <li><a href="cs_nav_prefs_advanced.xhtml#scripts_and_plugins">Advanced
+ Preferences - Scripts &amp; Plugins</a></li>
+</ul>
+
+<p>[<a href="#privacy_on_the_internet">Return to beginning of section</a>]</p>
+
+<h2 id="how_can_i_make_sure_unauthorized_people_dont_use_information_about_me">How
+ Can I Make Sure Unauthorized People Don&apos;t Use Information About Me?</h2>
+
+<p>The best way to keep your information private is to read the privacy
+ policies for the websites you visit and the Internet services you use, and
+ to be cautious about giving out your personal information online.</p>
+
+<p>The Internet is a public network. When you send your name, phone number,
+ address, and other personal information over the network (via a web page,
+ email, or any other method), it is possible that someone else may be able to
+ intercept it.</p>
+
+<p>Here are some questions you might ask about a website&apos;s privacy
+ policy:</p>
+
+<ul>
+ <li>What kinds of personal information is this website gathering?</li>
+ <li>How will the website use the information?</li>
+ <li>Will the website share the information with others and do I have choices
+ regarding the use of any shared information?</li>
+ <li>Can I access some or all of the information a website gathers about me, in
+ order to inspect or update it?</li>
+ <li>How does the website protect the information?</li>
+ <li>How do I contact the website if I have questions or problems?</li>
+</ul>
+
+<p>[<a href="#privacy_on_the_internet">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/privsec_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/privsec_help.xhtml
new file mode 100644
index 0000000000..cd06ce1f44
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/privsec_help.xhtml
@@ -0,0 +1,252 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Privacy &amp; Security Preferences</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="privsec_preferences">Privacy &amp; Security Preferences</h1>
+
+<p>The sections listed below describe the Privacy &amp; Security preferences.
+ To see the preference panels, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Privacy &amp; Security category. If no subcategories are
+ visible, double-click Privacy &amp; Security to expand the list, then
+ click the name for the preferences you want to view or change.</li>
+</ol>
+
+<p>For help with a panel&apos;s settings, click the appropriate link below.</p>
+
+<div class="contentsBox">Related sections:
+ <ul>
+ <li><a href="#privacy_and_security">Privacy &amp; Security</a></li>
+ <li><a href="using_priv_help.xhtml#private_data_prefs">Private Data</a></li>
+ <li><a href="using_priv_help.xhtml#cookies">Cookies</a></li>
+ <li><a href="using_priv_help.xhtml#images">Images</a></li>
+ <li><a href="cs_priv_prefs_popup.xhtml#privacy_and_security_preferences_popup_windows">Popup Windows</a></li>
+ <li><a href="passwords_help.xhtml#passwords">Passwords</a></li>
+ <li><a href="passwords_help.xhtml#master_passwords">Master Passwords</a></li>
+ <li><a href="ssl_help.xhtml#privacy_and_security_preferences_ssltls">SSL/TLS</a></li>
+ <li><a href="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates">Certificates</a></li>
+ </ul>
+</div>
+
+<h2 id="privacy_and_security">Privacy &amp; Security Preferences -
+ Privacy &amp; Security</h2>
+
+<p>This section describes how to use the main Privacy &amp; Security
+ preferences panel. If you&apos;re not already viewing it, follow these
+ steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Click the Privacy &amp; Security category.</li>
+</ol>
+
+<h3>User Tracking</h3>
+
+<p>These settings allow you to communicate your tracking preferences to
+ websites and to actively block tracking requests from known tracking
+ sites. <a href="glossary.xhtml#user_tracking">User tracking</a> refers
+ to websites (including advertisers, analytics providers, and social sites)
+ collecting and analyzing comprehensive data on your web browsing patterns,
+ thus affecting your privacy. For more information on this topic, see
+ <a href="privacy_help.xhtml#why_and_how_are_websites_tracking_me">Why and
+ How Are Websites Tracking Me?</a> The following options are available in
+ this section:</p>
+
+<ul>
+ <li><strong>Tell websites that I do not want to be tracked</strong>: Check
+ this to instruct &brandShortName; to inform every website you explicitly
+ visit (along with any providers of third-party content embedded in its
+ pages) that you don&apos;t want your browsing behavior being tracked.
+ If checked, a <a href="glossary.xhtml#do_not_track">Do Not Track</a>
+ request is sent to each website visited.</li>
+ <li><strong>Prevent tracking activities by known sites</strong>: Check
+ this to instruct &brandShortName; to actively block connections to
+ websites which are known to provide tracking services. You may not see
+ any content coming from these sites in the web page visited.</li>
+ <li><strong>Warn me when known tracking activities were detected</strong>:
+ Check this to be presented with a notification bar when content was blocked
+ which can be used for tracking. This bar contains the following buttons:
+ <ul>
+ <li><strong>Keep Blocking</strong>: Dismiss the notification bar without
+ loading the content from an identified tracking site.</li>
+ <li><strong>Unblock</strong>: Allow such content to be loaded now and
+ automatically when this page is visited again in the future.</li>
+ <li><strong>Preferences</strong>: Open the Privacy &amp; Security
+ preference panel.</li>
+ </ul>
+ </li>
+</ul>
+
+<p><strong>Notes</strong>:</p>
+
+<ul>
+ <li>Honoring a Do Not Track request is voluntary, thus individual websites
+ are <em>not required</em> to obey it. Websites and services that follow
+ the policy should stop gathering and using information about your web
+ browsing pattern for all activity for which Do Not Track is selected.</li>
+ <li>Requesting Do Not Track from a website should not affect your ability
+ to use the website properly (e.g., when using a
+ <a href="glossary.xhtml#web_application">web application</a> or when
+ putting items into a cart while shopping).</li>
+ <li>Note that Do Not Track does not <em>prevent</em> advertisements from
+ being presented to you while browsing. Since the website is not allowed to
+ gather and utilize information about you, the content of the advertisements
+ will be less specific if you have the Do Not Track option selected.</li>
+ <li>Tracking prevention actively blocks content known to be used for tracking
+ in a web page. Thus, the page may appear incomplete. Such sites are
+ identified by periodically downloading a list of known tracking sites
+ which are to be blocked if enabled. Tracking sites <em>not</em> on that
+ list will still be connected to until they are listed.</li>
+ <li>The selection of <q>Unblock</q> in the notification bar for a specific
+ site can be revoked in the Permissions tab of the Data Manager. When in
+ a <a href="using_priv_help.xhtml#browsing_in_a_private_window">private
+ window</a>, these options aren&apos;t present in the notification bar.</li>
+</ul>
+
+<h3 id="location_aware_browsing">Location Aware Browsing</h3>
+
+<p>Websites may want to obtain more detailed information about the user&apos;s
+ location, e.g., to offer specific services close to the current position. For
+ that purpose, they can request the longitude and latitude (along with other
+ information like altitude and heading, as available) from a browser.
+ &brandShortName; will never provide that information without the user&apos;s
+ consent. However, a notification is displayed for a page that requests the
+ location at least the first time that this site is visited. The preferences
+ located in the center section of the <a href="#privacy_and_security">Privacy
+ &amp; Security panel</a> allow you to disable this feature entirely:</p>
+
+<ul>
+ <li><strong>Prompt me for permission if a request is made</strong>: Select
+ this if you want &brandShortName; to prompt you if the website you are
+ visiting is requesting geolocation information. The notification provides
+ the following options:
+ <ul>
+ <li><strong>Share location</strong>: Select this option if you want the
+ current location to be determined and forwarded to the requesting site,
+ for <em>this</em> request only.</li>
+ <li><strong>Not for this request</strong>: Select this option if you do
+ <em>not</em> want the current location to be determined and forwarded
+ to the requesting site, prompt again for the next request.</li>
+ <li><strong>Always for this site</strong>: Select this option if you
+ want the current location to be determined and forwarded to the
+ requesting site, also for all <em>future</em> requests.</li>
+ <li><strong>Never for this site</strong>: Select this option if you do
+ <em>not</em> want the current location to be determined and forwarded
+ to the requesting site, also for all <em>future</em> requests.</li>
+ </ul>
+ Closing or dismissing the notification will not give any response to the
+ requesting page at all.</li>
+ <li><strong>Disable this feature and deny all requests</strong>: Select this
+ option for not being prompted for geolocation requests. &brandShortName;
+ will appear to a website as if it doesn&apos;t support this feature.</li>
+</ul>
+
+<p><strong>Notes</strong>:</p>
+
+<ul>
+ <li>Geolocation is a third-party web-based service. &brandShortName; collects
+ certain information about your internet access (e.g., wireless access
+ points) and sends those to this service.</li>
+ <li>The accuracy of the response varies and may range from city level to
+ within a few meters of your actual location.</li>
+ <li>No information about the requesting site itself is provided to the
+ geolocation service. The information returned by that service is provided
+ to the website by &brandShortName; in response to its request.</li>
+ <li>The requesting site receives an identification token that allows it to
+ track your movement for a limited time using the geolocation service.</li>
+</ul>
+
+<h3 id="safe_browsing">Safe Browsing</h3>
+
+<p>Some websites contain potentially dangerous content and may have been
+ reported as malicious. The preferences located in the lower section of the
+ <a href="#privacy_and_security">Privacy &amp; Security panel</a> allow you
+ to select which types of reported websites will be blocked. If enabled,
+ &brandShortName; compares each website&apos;s address you are about to visit
+ against regularly updated lists. A warning is issued prior to actually
+ loading a reported website, thus giving you the opportunity to cancel the
+ loading process. The following options are available in this section:</p>
+
+<ul>
+ <li><strong>Block reported attack sites</strong>: Check this if you want
+ &brandShortName; to warn you if the website you are about to visit has
+ been reported as a site that is trying to infect your computer with
+ malicious software. Such <a href="glossary.xhtml#malware">malware</a> may
+ interfere with your computer&apos;s functions or steal your personal
+ information. It is also frequently used to send spam email or to spread
+ more malware.</li>
+ <li><strong>Block reported web forgeries</strong>: Check this if you want
+ &brandShortName; to warn you if the website you are about to visit has
+ been reported as a site that pretends to be a legitimate website of some
+ well-known organization or service. Websites like this are frequently set
+ up to mislead you in providing username and password or other sensitive
+ personal information (commonly known as
+ <a href="glossary.xhtml#phishing">phishing</a>).</li>
+</ul>
+
+<p>When you are trying to visit a website that has been reported as malicious
+ (and the feature is enabled), you will see one of the following warnings:</p>
+
+<ul>
+ <li><strong>Reported Attack Page!</strong> This website has been reported
+ as a website trying to infect your computer with malware.</li>
+ <li><strong>Reported Web Forgery!</strong> This website has been reported
+ as a website trying to steal your personal information.</li>
+</ul>
+
+<p>No harm has been done at this point. Select one of the following options:</p>
+
+<ul>
+ <li><strong>Get me out of here!</strong> Do <em>not</em> load this website.
+ Instead, go to &brandShortName;&apos;s default start page.</li>
+ <li><strong>Why was this page blocked?</strong> Opens a web page showing you
+ the information reported on this specific website (this is an external
+ page located with the provider of this service). If no such report data is
+ available, a page with general information is shown.</li>
+ <li><strong>Ignore this warning</strong>: Proceed with loading the page. Only
+ use this when you are <em>certain</em> that the selected page is authentic
+ and safe to visit. At the top of the page, a notification bar will appear,
+ giving you an option to report errors to the provider of this service:
+ <ul>
+ <li><strong>This isn&apos;t an attack site</strong> or <strong>This
+ isn&apos;t a web forgery</strong>: Click this button to open a new tab
+ allowing you to report false warnings (this is an external page).</li>
+ </ul>
+ </li>
+</ul>
+
+<p><strong>Important</strong>: The Safe Browsing feature is switched on by
+ default. It is <em>not</em> recommended to disable either function as this
+ may result in you visiting unsafe sites.</p>
+
+<p><strong>Note</strong>: If no warning is shown for a specific website, it
+ does not <em>guarantee</em> that this site is trustworthy; it just means
+ that it has not been reported. Attack and phishing websites may <em>look</em>
+ safe but are secretly trying to attack you or to steal personal information.
+ The website&apos;s owner or provider may not be aware that the site has been
+ compromised.</p>
+
+<p>[<a href="#privsec_preferences">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/profiles_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/profiles_help.xhtml
new file mode 100644
index 0000000000..d79f9df4d6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/profiles_help.xhtml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Managing Profiles</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="managing_profiles">Managing Profiles</h1>
+
+<p>If you use the Internet at home and at work, you may want to have access to
+ a different set of bookmarks, preferences, address books, email accounts,
+ Sidebar setup, and so on. Similarly, family members may want to share a copy
+ of the same browser software but keep their Internet identities separate.</p>
+
+<p>The Profile Manager lets you create different profiles, each with its own
+ bookmarks, preferences, email settings, and so on. You automatically create a
+ default profile when you first install your browser software. After you
+ create one or more additional profiles, you will be asked which you want to
+ use each time you launch the browser.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#creating_a_new_profile">Creating a New Profile</a></li>
+ <li><a href="#deleting_or_renaming_a_profile">Deleting or Renaming a
+ Profile</a></li>
+ </ul>
+</div>
+
+<h2 id="creating_a_new_profile">Creating a New Profile</h2>
+
+<p>To create a profile:</p>
+
+<ol>
+ <li>Open the Profile Manager:
+ <ul>
+ <li><strong>If &brandShortName; is running</strong>: Open the Tools menu
+ and select Switch Profile.</li>
+ <li><strong>If &brandShortName; is closed</strong>:<span class="win">
+ Open the Start menu and choose Programs, then &brandShortName;, then
+ Profile Manager.</span><span class="mac"> Hold down the
+ <kbd>Option</kbd> key while you&apos;re starting &brandShortName; from
+ the Finder or the Dock.</span><span class="unix"> Type the following at
+ the command line: <kbd>./mozilla -profilemanager</kbd></span></li>
+ </ul>
+ </li>
+ <li>Click Create Profile, read the Profile description, and then click
+ Next.</li>
+ <li>Enter a profile name. This can be anything you like, such as your real
+ name or a name that&apos;s related to what you use the profile for, such as
+ <q>School</q>.</li>
+ <li>Accept the default location for the new profile, or click the Choose
+ Folder button and navigate to the location you want.</li>
+ <li>If you have additional Language Packs installed, click
+ Select Language and choose the language you want.</li>
+ <li>Click Finish.</li>
+</ol>
+
+<p>[<a href="#managing_profiles">Return to beginning of section</a>]</p>
+
+<h2 id="deleting_or_renaming_a_profile">Deleting or Renaming a Profile</h2>
+
+<p>You may want to delete profiles that you don&apos;t normally use. To delete
+ or rename an existing profile:</p>
+
+<ol>
+ <li>Open the Profile Manager:
+ <ul>
+ <li><strong>If &brandShortName; is running</strong>: Open the Tools menu
+ and select Switch Profile. <strong>Note</strong>: You can&apos;t delete
+ the profile that is in use.</li>
+ <li><strong>If &brandShortName; is closed</strong>:
+ <span class="win"> Open the Start menu and choose Programs, then
+ &brandShortName;, then Profile Manager.</span><span class="mac"> Hold
+ down the <kbd>Option</kbd> key while you&apos;re starting
+ &brandShortName; from the Finder or the Dock.</span><span class="unix">
+ Type the following at the command line:
+ <kbd>./mozilla -profilemanager</kbd></span></li>
+ </ul>
+ </li>
+ <li>To delete a profile, select its name and click Delete Profile. In the
+ confirmation box, choose one of the following options:
+ <ul>
+ <li><strong>Delete files</strong>: Deletes the whole profile folder
+ with its stored data (bookmarks, preferences, email
+ accounts, and so on). <em>Make sure that you won&apos;t need anything
+ from the profile in the future before you choose this option.</em></li>
+ <li><strong>Don&apos;t delete files</strong>: Removes the profile from
+ the list of available profiles, but keeps the profile folder. By
+ choosing this option none of your profile&apos;s stored data will be
+ deleted.</li>
+ </ul>
+ </li>
+ <li>To rename a profile, select its name, click Rename Profile, and follow the
+ instructions.</li>
+</ol>
+
+<p>[<a href="#managing_profiles">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/shortcuts.xhtml b/comm/suite/locales/en-US/chrome/common/help/shortcuts.xhtml
new file mode 100644
index 0000000000..ab8e3b7ea0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/shortcuts.xhtml
@@ -0,0 +1,524 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Keyboard Shortcuts</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1>&brandShortName; Keyboard Shortcuts</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#using_shortcuts">Using Shortcuts</a></li>
+ <li><a href="#general_mozilla_shortcuts">General &brandShortName;
+ Shortcuts</a></li>
+ <li><a href="#text_field_shortcuts">Text Field Shortcuts</a></li>
+ <li><a href="#help_window_shortcuts">Help Window Shortcuts</a></li>
+ <li><a href="shortcuts_navigator.xhtml">Browser Shortcuts</a></li>
+ <li><a href="shortcuts_mailnews.xhtml">Mail &amp; Newsgroups
+ Shortcuts</a></li>
+ <li><a href="shortcuts_composer.xhtml">Composer Shortcuts</a></li>
+ </ul>
+</div>
+
+<h2 id="using_shortcuts">Using Shortcuts</h2>
+
+<p>This document uses the following format for listing shortcuts:</p>
+
+<p><strong>Example:</strong></p>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Copy</td>
+ <td>Ctrl+C</td>
+ <td>Cmd+C</td>
+ <td>Ctrl+C</td>
+ </tr>
+</tbody>
+</table>
+
+<p>To perform a command, press the buttons listed together at the same time.
+ For example, to copy in Windows, press Ctrl and C at the same time.</p>
+
+<p><strong>Tip</strong>: The plus sign (+) means that you must press two keys
+ surrounding the plus sign at the same time. If there is more than one plus
+ sign (such as Ctrl+Shift+C), it means that all three buttons surrounding the
+ plus sign must be pressed at the same time.</p>
+
+<p><strong>Abbreviations:</strong></p>
+
+<ul>
+ <li>Ctrl = Control key</li>
+ <li>Cmd = Command key on the macOS Keyboard</li>
+</ul>
+
+<p><strong>Note</strong>: Some keyboard shortcuts perform different functions
+ based on cursor location (focus). For example, if you press Home on Windows
+ while viewing a web page, &brandShortName; will move to the top of the web
+ page. However, if you press Home on Windows while the cursor is in a text
+ field, the cursor will go to the beginning of the text field.</p>
+
+<h2 id="general_mozilla_shortcuts">General &brandShortName; Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Copy</td>
+ <td>Ctrl+C</td>
+ <td>Cmd+C</td>
+ <td>Ctrl+C</td>
+ </tr>
+ <tr>
+ <td>Paste</td>
+ <td>Ctrl+V</td>
+ <td>Cmd+V</td>
+ <td>Ctrl+V</td>
+ </tr>
+ <tr>
+ <td>Cut</td>
+ <td>Ctrl+X</td>
+ <td>Cmd+X</td>
+ <td>Ctrl+X</td>
+ </tr>
+ <tr>
+ <td>Select All</td>
+ <td>Ctrl+A</td>
+ <td>Cmd+A</td>
+ <td>Alt+A</td>
+ </tr>
+ <tr>
+ <td>Close Window</td>
+ <td>Ctrl+W</td>
+ <td>Cmd+W</td>
+ <td>Ctrl+W</td>
+ </tr>
+ <tr>
+ <td>Delete Next Word</td>
+ <td>Ctrl+Del</td>
+ <td>Opt+Del</td>
+ <td>Ctrl+Del</td>
+ </tr>
+ <tr>
+ <td>Go Up One Page</td>
+ <td>Page Up</td>
+ <td>Page Up</td>
+ <td>Page Up</td>
+ </tr>
+ <tr>
+ <td>Go Down One Page </td>
+ <td>Page Down</td>
+ <td>Page Down</td>
+ <td>Page Down</td>
+ </tr>
+ <tr>
+ <td>Go Up One Line</td>
+ <td>Up Arrow</td>
+ <td>Up Arrow</td>
+ <td>Up Arrow</td>
+ </tr>
+ <tr>
+ <td>Go Down One Line</td>
+ <td>Down Arrow</td>
+ <td>Down Arrow</td>
+ <td>Down Arrow</td>
+ </tr>
+ <tr>
+ <td>Undo</td>
+ <td>Ctrl+Z</td>
+ <td>Cmd+Z</td>
+ <td>Ctrl+Z</td>
+ </tr>
+ <tr>
+ <td>Redo</td>
+ <td>Ctrl+Y or Ctrl+Shift+Z</td>
+ <td>Cmd+Shift+Z</td>
+ <td>Ctrl+Y or Ctrl+Shift+Z</td>
+ </tr>
+ <tr>
+ <td>Focus Search Field</td>
+ <td>Ctrl+F or Ctrl+K</td>
+ <td>Cmd+F or Cmd+K</td>
+ <td>Ctrl+F or Ctrl+K</td>
+ </tr>
+ <tr>
+ <td>Find</td>
+ <td>Ctrl+F</td>
+ <td>Cmd+F</td>
+ <td>Ctrl+F</td>
+ </tr>
+ <tr>
+ <td>Find Again</td>
+ <td>Ctrl+G or F3</td>
+ <td>Cmd+G</td>
+ <td>Ctrl+G</td>
+ </tr>
+ <tr>
+ <td>Find Links As You Type</td>
+ <td>&apos; (apostrophe)</td>
+ <td>&apos; (apostrophe)</td>
+ <td>&apos; (apostrophe)</td>
+ </tr>
+ <tr>
+ <td>Find Text As You Type</td>
+ <td>/</td>
+ <td>/</td>
+ <td>/</td>
+ </tr>
+ <tr>
+ <td>Open Context Menu</td>
+ <td>Shift+F10</td>
+ <td>Ctrl+Space</td>
+ <td>Shift+F10</td>
+ </tr>
+ <tr>
+ <td>
+ Open Main Menu <span class="noMac">(switches to the first drop-down menu
+ at the top of the window)</span>
+ </td>
+ <td>Alt or F10</td>
+ <td>(macOS: Controlled through keyboard preference in Control Panel)</td>
+ <td>F10</td>
+ </tr>
+ <tr>
+ <td>Exit &brandShortName;</td>
+ <td>Ctrl+Q</td>
+ <td>Cmd+Q</td>
+ <td>Ctrl+Q</td>
+ </tr>
+ <tr>
+ <td>Start Browser</td>
+ <td>Ctrl+1</td>
+ <td>Cmd+1</td>
+ <td>Ctrl+1</td>
+ </tr>
+ <tr>
+ <td>Start Mail &amp; Newsgroups</td>
+ <td>Ctrl+2</td>
+ <td>Cmd+2</td>
+ <td>Ctrl+2</td>
+ </tr>
+ <tr>
+ <td>Start Composer</td>
+ <td>Ctrl+4</td>
+ <td>Cmd+4</td>
+ <td>Ctrl+4</td>
+ </tr>
+ <tr>
+ <td>Start Address Book</td>
+ <td>Ctrl+5</td>
+ <td>Cmd+5</td>
+ <td>Ctrl+5</td>
+ </tr>
+ <tr>
+ <td>Start IRC Chat</td>
+ <td>Ctrl+6</td>
+ <td>Cmd+6</td>
+ <td>Ctrl+6</td>
+ </tr>
+ <tr>
+ <td>Open Add-ons Manager</td>
+ <td>Ctrl+Shift+A</td>
+ <td>Cmd+Shift+A</td>
+ <td>Ctrl+Shift+A</td>
+ </tr>
+ <tr>
+ <td>Open Download Manager</td>
+ <td>Ctrl+J</td>
+ <td>Cmd+J</td>
+ <td>Ctrl+J</td>
+ </tr>
+ <tr>
+ <td>Open Error Console</td>
+ <td>Ctrl+Shift+J</td>
+ <td>Cmd+Shift+J</td>
+ <td>Ctrl+Shift+J</td>
+ </tr>
+ <tr>
+ <td>Open Help Window</td>
+ <td>F1</td>
+ <td>Cmd+?</td>
+ <td>F1</td>
+ </tr>
+ <tr>
+ <td>Turn on/off <a href="glossary.xhtml#caret_browsing">caret browsing</a></td>
+ <td>F7</td>
+ <td>F7 (if F7 is not programmed for another command)</td>
+ <td>F7</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="text_field_shortcuts">Text Field Shortcuts</h2>
+
+<p>The following are keyboard commands for navigating and modifying text in a
+ text input field (e.g. the Location Bar). Except where indicated, these
+ commands also apply to Web pages and e-mail messages in caret browsing
+ mode.</p>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Move one line up</td>
+ <td>Up Arrow</td>
+ <td>Up Arrow</td>
+ <td>Up Arrow</td>
+ </tr>
+ <tr>
+ <td>Move one line down</td>
+ <td>Down Arrow</td>
+ <td>Down Arrow</td>
+ <td>Down Arrow</td>
+ </tr>
+ <tr>
+ <td>Move one character left</td>
+ <td>Left Arrow</td>
+ <td>Left Arrow</td>
+ <td>Left Arrow</td>
+ </tr>
+ <tr>
+ <td>Move one character right</td>
+ <td>Right Arrow</td>
+ <td>Right Arrow</td>
+ <td>Right Arrow</td>
+ </tr>
+ <tr>
+ <td>Move to next word</td>
+ <td>Ctrl+Right Arrow</td>
+ <td>Opt+Right Arrow</td>
+ <td>Ctrl+Right Arrow</td>
+ </tr>
+ <tr>
+ <td>Move to previous word</td>
+ <td>Ctrl+Left Arrow</td>
+ <td>Opt+Left Arrow</td>
+ <td>Ctrl+Left Arrow</td>
+ </tr>
+ <tr>
+ <td>Move to beginning of line</td>
+ <td>Home</td>
+ <td>Cmd+Left Arrow<span class="mac"> (in text fields only)</span></td>
+ <td>Home</td>
+ </tr>
+ <tr>
+ <td>Move to end of line</td>
+ <td>End</td>
+ <td>Cmd+Right Arrow<span class="mac"> (in text fields only)</span></td>
+ <td>End</td>
+ </tr>
+ <tr>
+ <td>Move to beginning of text</td>
+ <td>Ctrl+Home</td>
+ <td>Cmd+Up Arrow</td>
+ <td>Ctrl+Home</td>
+ </tr>
+ <tr>
+ <td>Move to end of text</td>
+ <td>Ctrl+End</td>
+ <td>Cmd+Down Arrow</td>
+ <td>Ctrl+End</td>
+ </tr>
+ <tr>
+ <td>Select next character</td>
+ <td>Shift+Right Arrow</td>
+ <td>Shift+Right Arrow</td>
+ <td>Shift+Right Arrow</td>
+ </tr>
+ <tr>
+ <td>Select previous character</td>
+ <td>Shift+Left Arrow</td>
+ <td>Shift+Left Arrow</td>
+ <td>Shift+Left Arrow</td>
+ </tr>
+ <tr>
+ <td>Select next word</td>
+ <td>Ctrl+Shift+Right Arrow</td>
+ <td>Opt+Shift+Right Arrow</td>
+ <td>Ctrl+Shift+Right Arrow</td>
+ </tr>
+ <tr>
+ <td>Select previous word</td>
+ <td>Ctrl+Shift+Left Arrow</td>
+ <td>Opt+Shift+Left Arrow</td>
+ <td>Ctrl+Shift+Left Arrow</td>
+ </tr>
+ <tr>
+ <td>Select all text</td>
+ <td>Ctrl+A</td>
+ <td>Cmd+A</td>
+ <td>Ctrl+A</td>
+ </tr>
+ <tr>
+ <td>Copy</td>
+ <td>Ctrl+C</td>
+ <td>Cmd+C</td>
+ <td>Ctrl+C</td>
+ </tr>
+ <tr>
+ <td>Paste (in text fields only)</td>
+ <td>Ctrl+V</td>
+ <td>Cmd+V</td>
+ <td>Ctrl+V</td>
+ </tr>
+ <tr>
+ <td>Cut (in text fields only)</td>
+ <td>Ctrl+X</td>
+ <td>Cmd+X</td>
+ <td>Ctrl+X</td>
+ </tr>
+ <tr>
+ <td>Delete next character (in text fields only)</td>
+ <td>Del</td>
+ <td>Del</td>
+ <td>Del</td>
+ </tr>
+ <tr>
+ <td>Delete previous character (in text fields only)</td>
+ <td>Backspace</td>
+ <td>Backspace</td>
+ <td>Backspace</td>
+ </tr>
+ <tr>
+ <td>Delete next word (in text fields only)</td>
+ <td>Ctrl+Del</td>
+ <td>Opt+Del</td>
+ <td>Ctrl+Del</td>
+ </tr>
+ <tr>
+ <td>Delete previous word (in text fields only)</td>
+ <td>Ctrl+Backspace</td>
+ <td>Opt+Backspace</td>
+ <td>Ctrl+Backspace</td>
+ </tr>
+ </tbody>
+ </table>
+
+<h2 id="help_window_shortcuts">Help Window Shortcuts</h2>
+
+<p>These shortcuts are available from Help windows.</p>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Navigate Links within Content Pane (right pane)</td>
+ <td>Tab</td>
+ <td>Tab</td>
+ <td>Tab</td>
+ </tr>
+ <tr>
+ <td>Switch between Content Pane and Search/Contents/Index/Glossary
+ (toggle)</td>
+ <td>F6</td>
+ <td>F6</td>
+ <td>F6</td>
+ </tr>
+ <tr>
+ <td>Navigate Index Terms (while Index Pane is selected)</td>
+ <td>Up/Down Arrow</td>
+ <td>Up/Down Arrow</td>
+ <td>Up/Down Arrow</td>
+ </tr>
+ <tr>
+ <td>Scroll Pane (Content, Table of Contents, or Index)</td>
+ <td>Up/Down Arrow</td>
+ <td>Up/Down Arrow</td>
+ <td>Up/Down Arrow</td>
+ </tr>
+ <tr>
+ <td>Expand/Collapse Table of Contents Tree Structure</td>
+ <td>Left/Right Arrow</td>
+ <td>Left/Right Arrow</td>
+ <td>Left/Right Arrow</td>
+ </tr>
+ <tr>
+ <td>Print Page</td>
+ <td>Ctrl+P</td>
+ <td>Cmd+P</td>
+ <td>Ctrl+P</td>
+ </tr>
+ <tr>
+ <td>Go Back to Previous Page</td>
+ <td>Alt+Left Arrow</td>
+ <td>Cmd+Left Arrow</td>
+ <td>Alt+Left Arrow</td>
+ </tr>
+ <tr>
+ <td>Go Forward One Page</td>
+ <td>Alt+Right Arrow</td>
+ <td>Cmd+Right Arrow</td>
+ <td>Alt+Right Arrow</td>
+ </tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/shortcuts_composer.xhtml b/comm/suite/locales/en-US/chrome/common/help/shortcuts_composer.xhtml
new file mode 100644
index 0000000000..5490958d49
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/shortcuts_composer.xhtml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Composer Shortcuts</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1>Composer Shortcuts</h1>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>New Composer Page</td>
+ <td>Ctrl+Shift+N</td>
+ <td>Cmd+Shift+N</td>
+ <td>Ctrl+Shift+N</td>
+ </tr>
+ <tr>
+ <td>Save Page</td>
+ <td>Ctrl+S</td>
+ <td>Cmd+S</td>
+ <td>Ctrl+S</td>
+ </tr>
+ <tr>
+ <td>Open File</td>
+ <td>Ctrl+O</td>
+ <td>Cmd+O</td>
+ <td>Ctrl+O</td>
+ </tr>
+ <tr>
+ <td>Close Page</td>
+ <td>Ctrl+W</td>
+ <td>Cmd+W</td>
+ <td>Ctrl+W</td>
+ </tr>
+ <tr>
+ <td>Publish</td>
+ <td>Ctrl+Shift+S</td>
+ <td>Cmd+Shift+S</td>
+ <td>Ctrl+Shift+S</td>
+ </tr>
+ <tr>
+ <td>Find and Replace</td>
+ <td>Ctrl+H</td>
+ <td>Cmd+Opt+F</td>
+ <td>Ctrl+H</td>
+ </tr>
+ <tr>
+ <td>Find Again</td>
+ <td>Ctrl+G</td>
+ <td>Cmd+G</td>
+ <td>Ctrl+G</td>
+ </tr>
+ <tr>
+ <td>Find Previous</td>
+ <td>Ctrl+Shift+G</td>
+ <td>Cmd+Shift+G</td>
+ <td>Ctrl+Shift+G</td>
+ </tr>
+ <tr>
+ <td>Check Spelling</td>
+ <td>Ctrl+Shift+P</td>
+ <td>Cmd+Shift+P</td>
+ <td>Ctrl+Shift+P</td>
+ </tr>
+ <tr>
+ <td>Insert/Edit Link</td>
+ <td>Ctrl+L</td>
+ <td>Cmd+L</td>
+ <td>Ctrl+L</td>
+ </tr>
+ <tr>
+ <td>Increase Indent</td>
+ <td>Ctrl+[</td>
+ <td>Cmd+[</td>
+ <td>Ctrl+[</td>
+ </tr>
+ <tr>
+ <td>Decrease Indent</td>
+ <td>Ctrl+]</td>
+ <td>Cmd+]</td>
+ <td>Ctrl+]</td>
+ </tr>
+ <tr>
+ <td>Discontinue Text Styles</td>
+ <td>Ctrl+Shift+Y</td>
+ <td>Cmd+Shift+Y</td>
+ <td>Ctrl+Shift+Y</td>
+ </tr>
+ <tr>
+ <td>Discontinue Link</td>
+ <td>Ctrl+Shift+K</td>
+ <td>Cmd+Shift+K</td>
+ <td>Ctrl+Shift+K</td>
+ </tr>
+ <tr>
+ <td>Remove Named Anchors</td>
+ <td>Ctrl+Shift+A</td>
+ <td>Cmd+Shift+A</td>
+ <td>Ctrl+Shift+A</td>
+ </tr>
+ <tr>
+ <td>Select Row/Column</td>
+ <td>Ctrl+Drag</td>
+ <td>Cmd+Drag</td>
+ <td>Ctrl+Drag</td>
+ </tr>
+ <tr>
+ <td>Select Cells(s)</td>
+ <td>Ctrl+Click (drag to select block of cells, or continue clicking
+ to select individual cells)</td>
+ <td>Cmd+Click (drag to select block of cells, or continue clicking
+ to select individual cells)</td>
+ <td>Ctrl+Click (drag to select block of cells, or continue clicking
+ to select individual cells)</td>
+ </tr>
+ <tr>
+ <td>Decrease Font Size</td>
+ <td>Ctrl+- (minus sign)</td>
+ <td>Cmd+- (minus sign)</td>
+ <td>Ctrl+- (minus sign)</td>
+ </tr>
+ <tr>
+ <td>Increase Font Size</td>
+ <td>Ctrl++ (plus sign)</td>
+ <td>Cmd++ (plus sign)</td>
+ <td>Ctrl++ (plus sign)</td>
+ </tr>
+ <tr>
+ <td>Text Style Bold</td>
+ <td>Ctrl+B</td>
+ <td>Cmd+B</td>
+ <td>Ctrl+B</td>
+ </tr>
+ <tr>
+ <td>Text Style Italic</td>
+ <td>Ctrl+I</td>
+ <td>Cmd+I</td>
+ <td>Ctrl+I</td>
+ </tr>
+ <tr>
+ <td>Text Style Underline</td>
+ <td>Ctrl+U</td>
+ <td>Cmd+U</td>
+ <td>Ctrl+U</td>
+ </tr>
+ <tr>
+ <td>Text Style Fixed Width</td>
+ <td>Ctrl+T</td>
+ <td>Cmd+T</td>
+ <td>Ctrl+T</td>
+ </tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/shortcuts_mailnews.xhtml b/comm/suite/locales/en-US/chrome/common/help/shortcuts_mailnews.xhtml
new file mode 100644
index 0000000000..d7f6b3285a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/shortcuts_mailnews.xhtml
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Mail &amp; Newsgroups Shortcuts</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1>Mail &amp; Newsgroups Shortcuts</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#general_mail_and_newsgroups_shortcuts">General Mail &amp;
+ Newsgroups Shortcuts</a></li>
+ <li><a href="#message_list_shortcuts">Message List Shortcuts</a></li>
+ <li><a href="#message_compose_shortcuts">Message Compose Shortcuts</a></li>
+ </ul>
+</div>
+
+<h2 id="general_mail_and_newsgroups_shortcuts">General Mail &amp; Newsgroups
+ Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Reload</td>
+ <td>F5</td>
+ <td/>
+ <td>F5</td>
+ </tr>
+ <tr>
+ <td>Move to Next Mail Pane (Folder, QuickSearch, Thread,
+ Message Panes)</td>
+ <td>F6</td>
+ <td>F6</td>
+ <td>F6</td>
+ </tr>
+ <tr>
+ <td>Toggle Message Pane Visibility</td>
+ <td>F8</td>
+ <td>F8</td>
+ <td>F8</td>
+ </tr>
+ <tr>
+ <td>Toggle Folder Pane Visibility</td>
+ <td>F9</td>
+ <td>Cmd+Opt+S</td>
+ <td>F9</td>
+ </tr>
+ <tr>
+ <td>New Message</td>
+ <td>Ctrl+M</td>
+ <td>Cmd+Shift+M</td>
+ <td>Ctrl+M</td>
+ </tr>
+ <tr>
+ <td>Get New Messages</td>
+ <td>Ctrl+D</td>
+ <td>Cmd+D</td>
+ <td>Ctrl+D</td>
+ </tr>
+ <tr>
+ <td>Get All New Messages</td>
+ <td>Ctrl+Shift+D</td>
+ <td>Cmd+Shift+D</td>
+ <td>Ctrl+Shift+D</td>
+ </tr>
+ <tr>
+ <td>Search Messages</td>
+ <td>Ctrl+Shift+S</td>
+ <td>Cmd+Shift+S</td>
+ <td>Ctrl+Shift+S</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="message_list_shortcuts">Message List Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Save Message as File</td>
+ <td>Ctrl+S</td>
+ <td>Cmd+S</td>
+ <td>Ctrl+S</td>
+ </tr>
+ <tr>
+ <td>Open Message (in a new window)</td>
+ <td>Ctrl+O</td>
+ <td>Cmd+O</td>
+ <td>Ctrl+O</td>
+ </tr>
+ <tr>
+ <td>Delete Message</td>
+ <td>Del</td>
+ <td>Del</td>
+ <td>Del</td>
+ </tr>
+ <tr>
+ <td>Delete Message Immediately (without placing it in the Trash
+ folder)</td>
+ <td>Shift+Del</td>
+ <td>Shift+Del</td>
+ <td>Shift+Del</td>
+ </tr>
+ <tr>
+ <td>Undo Delete Message</td>
+ <td>Ctrl+Z</td>
+ <td>Cmd+Z</td>
+ <td>Ctrl+Z</td>
+ </tr>
+ <tr>
+ <td>Select All Messages</td>
+ <td>Ctrl+A</td>
+ <td>Cmd+A</td>
+ <td>Alt+A</td>
+ </tr>
+ <tr>
+ <td>Select Thread</td>
+ <td>Alt+Shift+A</td>
+ <td>Alt+Shift+A</td>
+ <td>Alt+Shift+A</td>
+ </tr>
+ <tr>
+ <td>Collapse All Threads</td>
+ <td>\ (backslash key)</td>
+ <td>\ (backslash key)</td>
+ <td>\ (backslash key)</td>
+ </tr>
+ <tr>
+ <td>Expand All Threads</td>
+ <td>* (asterisk key)</td>
+ <td>* (asterisk key)</td>
+ <td>* (asterisk key)</td>
+ </tr>
+ <tr>
+ <td>Message Source</td>
+ <td>Ctrl+U</td>
+ <td>Cmd+U</td>
+ <td>Ctrl+U</td>
+ </tr>
+ <tr>
+ <td>Go to Next Message</td>
+ <td>F</td>
+ <td>F</td>
+ <td>F</td>
+ </tr>
+ <tr>
+ <td>Go to Next Unread Message</td>
+ <td>N</td>
+ <td>N</td>
+ <td>N</td>
+ </tr>
+ <tr>
+ <td>Go to Next Unread Thread</td>
+ <td>T</td>
+ <td>T</td>
+ <td>T</td>
+ </tr>
+ <tr>
+ <td>Go to Previous Message</td>
+ <td>B</td>
+ <td>B</td>
+ <td>B</td>
+ </tr>
+ <tr>
+ <td>Go to Previous Unread Message</td>
+ <td>P</td>
+ <td>P</td>
+ <td>P</td>
+ </tr>
+ <tr>
+ <td>Reply to Message (replies only to sender)</td>
+ <td>Ctrl+R</td>
+ <td>Cmd+R</td>
+ <td>Ctrl+R</td>
+ </tr>
+ <tr>
+ <td>Reply to All in Message (replies to sender and to other email addresses
+ in message)</td>
+ <td>Ctrl+Shift+R</td>
+ <td>Cmd+Shift+R</td>
+ <td>Ctrl+Shift+R</td>
+ </tr>
+ <tr>
+ <td>Forward Message</td>
+ <td>Ctrl+L</td>
+ <td>Cmd+L</td>
+ <td>Ctrl+L</td>
+ </tr>
+ <tr>
+ <td>Edit Message As New (compose new email using the body and attachments
+ of the selected message)</td>
+ <td>Ctrl+E</td>
+ <td>Cmd+E</td>
+ <td>Ctrl+E</td>
+ </tr>
+ <tr>
+ <td>Remove Message Label</td>
+ <td>0</td>
+ <td>0</td>
+ <td>0</td>
+ </tr>
+ <tr>
+ <td>Label Message (5 customizable labels)</td>
+ <td>1, 2, 3, 4 and 5</td>
+ <td>1, 2, 3, 4 and 5</td>
+ <td>1, 2, 3, 4 and 5</td>
+ </tr>
+ <tr>
+ <td>Mark Message As Read</td>
+ <td>M</td>
+ <td>M</td>
+ <td>M</td>
+ </tr>
+ <tr>
+ <td>Mark Thread As Read</td>
+ <td>R</td>
+ <td>R</td>
+ <td>R</td>
+ </tr>
+ <tr>
+ <td>Mark Thread As Read and Move to Next Unread Message</td>
+ <td>T</td>
+ <td>T</td>
+ <td>T</td>
+ </tr>
+ <tr>
+ <td>Mark Messages As Read by Date</td>
+ <td>C</td>
+ <td>C</td>
+ <td>C</td>
+ </tr>
+ <tr>
+ <td>Mark All Messages in Selected Folder As Read</td>
+ <td>Ctrl+Shift+C</td>
+ <td>Cmd+Shift+C</td>
+ <td>Ctrl+Shift+C</td>
+ </tr>
+ <tr>
+ <td>Flag Message</td>
+ <td>I</td>
+ <td>I</td>
+ <td>I</td>
+ </tr>
+ <tr>
+ <td>Mark Message As Junk</td>
+ <td>J</td>
+ <td>J</td>
+ <td>J</td>
+ </tr>
+ <tr>
+ <td>Mark Message As Not Junk</td>
+ <td>Shift+J</td>
+ <td>Shift+J</td>
+ <td>Shift+J</td>
+ </tr>
+ <tr>
+ <td>Mark Message As Not Scam</td>
+ <td>Shift+P</td>
+ <td>Shift+P</td>
+ <td>Shift+P</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="message_compose_shortcuts">Message Compose Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Paste As Quotation</td>
+ <td>Ctrl+Shift+O</td>
+ <td>Cmd+Shift+O</td>
+ <td>Ctrl+Shift+O</td>
+ </tr>
+ <tr>
+ <td>Paste Without Formatting</td>
+ <td>Ctrl+Shift+V</td>
+ <td>Cmd+Shift+V</td>
+ <td>Ctrl+Shift+V</td>
+ </tr>
+ <tr>
+ <td>Rewrap</td>
+ <td>Ctrl+R</td>
+ <td>Cmd+R</td>
+ <td>Ctrl+R</td>
+ </tr>
+ <tr>
+ <td>Find and Replace</td>
+ <td>Ctrl+H</td>
+ <td>Cmd+Opt+F</td>
+ <td>Ctrl+H</td>
+ </tr>
+ <tr>
+ <td>Send Message Now</td>
+ <td>Ctrl+Enter</td>
+ <td>Cmd+Return</td>
+ <td>Ctrl+Enter</td>
+ </tr>
+ <tr>
+ <td>Send Message Later</td>
+ <td>Ctrl+Shift+Enter</td>
+ <td>Cmd+Shift+Return</td>
+ <td>Ctrl+Shift+Enter</td>
+ </tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/shortcuts_navigator.xhtml b/comm/suite/locales/en-US/chrome/common/help/shortcuts_navigator.xhtml
new file mode 100644
index 0000000000..b2762e1f52
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/shortcuts_navigator.xhtml
@@ -0,0 +1,541 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Browser Shortcuts</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1>Browser Shortcuts</h1>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#bookmarks_shortcuts">Bookmarks Shortcuts</a></li>
+ <li><a href="#page_navigation_shortcuts">Page Navigation Shortcuts</a></li>
+ <li><a href="#page_viewing_shortcuts">Page Viewing Shortcuts</a></li>
+ <li><a href="#tabbed_browsing_shortcuts">Tabbed Browsing Shortcuts</a></li>
+ <li><a href="#sidebar_shortcuts">Sidebar Shortcuts</a></li>
+ <li><a href="#forms_shortcuts">Forms Shortcuts</a></li>
+ </ul>
+</div>
+
+<h2 id="bookmarks_shortcuts">Bookmarks Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Open Manage Bookmarks Window </td>
+ <td>Ctrl+B</td>
+ <td>Cmd+B</td>
+ <td>Ctrl+B</td>
+ </tr>
+ <tr>
+ <td>Add Page to Bookmarks</td>
+ <td>Ctrl+Shift+D</td>
+ <td>Cmd+Shift+D</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>File Bookmark (to customize and file a page you are bookmarking)</td>
+ <td>Ctrl+D</td>
+ <td>Cmd+D</td>
+ <td>Ctrl+D</td>
+ </tr>
+ <tr>
+ <td>Sort Bookmarks Folder (selected folder in Manage Bookmarks window)
+ </td>
+ <td>Ctrl+S</td>
+ <td>Cmd+S</td>
+ <td>Ctrl+S</td>
+ </tr>
+ <tr>
+ <td>Sort Bookmarks Folder by Name (selected folder in Manage Bookmarks
+ window)
+ </td>
+ <td>Ctrl+N</td>
+ <td>Cmd+N</td>
+ <td>Ctrl+N</td>
+ </tr>
+ <tr>
+ <td>Bookmark Properties (for selected bookmark in Manage Bookmarks window)
+ </td>
+ <td>Ctrl+I</td>
+ <td>Cmd+I</td>
+ <td>Ctrl+I</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="page_navigation_shortcuts">Page Navigation Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Open History Window</td>
+ <td>Ctrl+H</td>
+ <td>Cmd+Shift+H</td>
+ <td>Ctrl+H</td>
+ </tr>
+ <tr>
+ <td>Reload</td>
+ <td>Ctrl+R</td>
+ <td>Cmd+R</td>
+ <td>Ctrl+R</td>
+ </tr>
+ <tr>
+ <td>Force Reload (not from cache)</td>
+ <td>Ctrl+Shift+R</td>
+ <td>Cmd+Shift+R</td>
+ <td>Ctrl+Shift+R</td>
+ </tr>
+ <tr>
+ <td>Back</td>
+ <td>Alt+Left Arrow or Backspace</td>
+ <td>Cmd+Left Arrow, Cmd+[ or Delete (Backspace)</td>
+ <td>Alt+Left Arrow or Ctrl+[</td>
+ </tr>
+ <tr>
+ <td>Forward</td>
+ <td>Alt+Right Arrow</td>
+ <td>Cmd+Right Arrow, Cmd+] or Shift+Delete (Backspace)</td>
+ <td>Alt+Right Arrow or Ctrl+]</td>
+ </tr>
+ <tr>
+ <td>Up</td>
+ <td>Alt+Up Arrow</td>
+ <td>Opt+Up Arrow</td>
+ <td>Alt+Up Arrow</td>
+ </tr>
+ <tr>
+ <td>Stop</td>
+ <td>Esc</td>
+ <td>Cmd+. or Esc</td>
+ <td>Esc</td>
+ </tr>
+ <tr>
+ <td>Home</td>
+ <td>Alt+Home</td>
+ <td>Cmd+Home</td>
+ <td>Alt+Home</td>
+ </tr>
+ <tr>
+ <td>Go to Bottom of Page</td>
+ <td>End</td>
+ <td></td>
+ <td>End</td>
+ </tr>
+ <tr>
+ <td>Go to Top of Page</td>
+ <td>Home</td>
+ <td>Home</td>
+ <td>Home</td>
+ </tr>
+ <tr>
+ <td>Select All Text in Location Bar</td>
+ <td>Ctrl+L or Alt+D</td>
+ <td>Cmd+L</td>
+ <td>Ctrl+L or Alt+D</td>
+ </tr>
+ <tr>
+ <td>Open Web Page Location</td>
+ <td>Ctrl+Shift+L</td>
+ <td>Cmd+Shift+L</td>
+ <td>Ctrl+Shift+L</td>
+ </tr>
+ <tr>
+ <td>Open Selected Link in a Web Page</td>
+ <td>Enter</td>
+ <td>Return</td>
+ <td>Enter</td>
+ </tr>
+ <tr>
+ <td>Open search engine page</td>
+ <td>Ctrl+Shift+S</td>
+ <td>Cmd+Shift+S</td>
+ <td>Ctrl+Shift+S</td>
+ </tr>
+ <tr>
+ <td>Move to Next Frame (in web pages using frames)</td>
+ <td>F6</td>
+ <td>Ctrl+Tab or F6 (if F6 is not programmed for another command)</td>
+ <td>F6</td>
+ </tr>
+ <tr>
+ <td>Move to Previous Frame (in web pages using frames)</td>
+ <td>Shift+F6</td>
+ <td>Ctrl+Shift+Tab or Shift+F6 (if F6 is not programmed for
+ another command)</td>
+ <td>Shift+F6</td>
+ </tr>
+ <tr>
+ <td>New Browser Window</td>
+ <td>Ctrl+N</td>
+ <td>Cmd+N</td>
+ <td>Ctrl+N</td>
+ </tr>
+ <tr>
+ <td>New Private Window</td>
+ <td>Ctrl+Shift+B</td>
+ <td>Cmd+Shift+B</td>
+ <td>Ctrl+Shift+B</td>
+ </tr>
+ <tr>
+ <td>Move to Next/Previous Link or Form Element in a Web Page</td>
+ <td>Tab/Shift+Tab</td>
+ <td>Tab/Shift+Tab</td>
+ <td>Tab/Shift+Tab</td>
+ </tr>
+ <tr>
+ <td>Open File</td>
+ <td>Ctrl+O</td>
+ <td>Cmd+O</td>
+ <td>Ctrl+O</td>
+ </tr>
+ <tr>
+ <td>Close Window</td>
+ <td>Ctrl+W</td>
+ <td>Cmd+W</td>
+ <td>Ctrl+W</td>
+ </tr>
+ <tr>
+ <td>Close Window (with more than one tab)</td>
+ <td>Ctrl+Shift+W</td>
+ <td>Cmd+Shift+W</td>
+ <td>Ctrl+Shift+W</td>
+ </tr>
+ <tr>
+ <td>Save Page As</td>
+ <td>Ctrl+S</td>
+ <td>Cmd+S</td>
+ <td>Ctrl+S</td>
+ </tr>
+ <tr>
+ <td>Save Linked Page (when a link is selected)</td>
+ <td>Shift+Enter</td>
+ <td>Opt+Return</td>
+ <td>Shift+Enter</td>
+ </tr>
+ <tr>
+ <td>Edit Page</td>
+ <td>Ctrl+E</td>
+ <td>Cmd+E</td>
+ <td>Ctrl+E</td>
+ </tr>
+ <tr>
+ <td>Print Page</td>
+ <td>Ctrl+P</td>
+ <td>Cmd+P</td>
+ <td>Ctrl+P</td>
+ </tr>
+ <tr>
+ <td>Go Up One Page</td>
+ <td>Page Up or Shift+Space</td>
+ <td>Page Up or Shift+Space</td>
+ <td>Page Up, Shift+Space or Backspace</td>
+ </tr>
+ <tr>
+ <td>Go Down One Page</td>
+ <td>Page Down or Space</td>
+ <td>Page Down or Space</td>
+ <td>Page Down, Space or Shift+Backspace</td>
+ </tr>
+ <tr>
+ <td>Go Up One Line</td>
+ <td>Up Arrow</td>
+ <td>Up Arrow</td>
+ <td>Up Arrow</td>
+ </tr>
+ <tr>
+ <td>Go Down One Line</td>
+ <td>Down Arrow</td>
+ <td>Down Arrow</td>
+ <td>Down Arrow</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="page_viewing_shortcuts">Page Viewing Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Full Screen (toggle)</td>
+ <td>F11</td>
+ <td>Cmd+Shift+F</td>
+ <td>F11 (may depend on window manager)</td>
+ </tr>
+ <tr>
+ <td>Zoom Text Smaller</td>
+ <td>Ctrl+- (minus sign)</td>
+ <td>Cmd+- (minus sign)</td>
+ <td>Ctrl+- (minus sign)</td>
+ </tr>
+ <tr>
+ <td>Zoom Text Larger</td>
+ <td>Ctrl++ (plus sign)</td>
+ <td>Cmd++ (plus sign)</td>
+ <td>Ctrl++ (plus sign)</td>
+ </tr>
+ <tr>
+ <td>No zoom (100%)</td>
+ <td>Ctrl+0 (zero)</td>
+ <td>Cmd+0 (zero)</td>
+ <td>Ctrl+0 (zero)</td>
+ </tr>
+ <tr>
+ <td>View Page Information</td>
+ <td>Ctrl+I</td>
+ <td>Cmd+I</td>
+ <td>Ctrl+I</td>
+ </tr>
+ <tr>
+ <td>View Page Source</td>
+ <td>Ctrl+U</td>
+ <td>Cmd+U</td>
+ <td>Ctrl+U</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="tabbed_browsing_shortcuts">Tabbed Browsing Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>New Browser Tab</td>
+ <td>Ctrl+T</td>
+ <td>Cmd+T</td>
+ <td>Ctrl+T</td>
+ </tr>
+ <tr>
+ <td>Switch to Next Tab (when using Tabbed Browsing with more than one
+ tab)</td>
+ <td>Ctrl+Tab or Ctrl+Page Down</td>
+ <td>Cmd+Opt+Right Arrow or Ctrl+Page Down</td>
+ <td>Ctrl+Tab or Ctrl+Page Down</td>
+ </tr>
+ <tr>
+ <td>Switch to Previous Tab (when using Tabbed Browsing with more
+ than one tab)
+ </td>
+ <td>Ctrl+Shift+Tab or Ctrl+Page Up</td>
+ <td>Cmd+Opt+Left Arrow or Ctrl+Page Up</td>
+ <td>Ctrl+Page Up</td>
+ </tr>
+ <tr>
+ <td>Close Tab (Close window if one page open)</td>
+ <td>Ctrl+W</td>
+ <td>Cmd+W</td>
+ <td>Ctrl+W</td>
+ </tr>
+ <tr>
+ <td>Move Tab Left (when tab is focused)</td>
+ <td>Ctrl+Left Arrow or Ctrl+Up Arrow</td>
+ <td>Cmd+Left Arrow or Cmd+Up Arrow</td>
+ <td>Ctrl+Left Arrow or Ctrl+Up Arrow</td>
+ </tr>
+ <tr>
+ <td>Move Tab Right (when tab is focused)</td>
+ <td>Ctrl+Right Arrow or Ctrl+Down Arrow</td>
+ <td>Cmd+Right Arrow or Cmd+Down Arrow</td>
+ <td>Ctrl+Right Arrow or Ctrl+Down Arrow</td>
+ </tr>
+ <tr>
+ <td>Move Tab to Beginning (when tab is focused)</td>
+ <td>Ctrl+Home</td>
+ <td>Cmd+Home</td>
+ <td>Ctrl+Home</td>
+ </tr>
+ <tr>
+ <td>Move Tab to End (when tab is focused)</td>
+ <td>Ctrl+End</td>
+ <td>Cmd+End</td>
+ <td>Ctrl+End</td>
+ </tr>
+ <tr>
+ <td>Open Link in a New Foreground Tab (when link is focused)</td>
+ <td>Insert (or Alt+Insert) *</td>
+ <td>(Alt+Insert) *</td>
+ <td>Insert (or Alt+Insert) *</td>
+ </tr>
+ <tr>
+ <td>Open Link in a New Background Tab (when link is focused)</td>
+ <td>Alt+Insert (or Insert) *</td>
+ <td>Alt+Insert</td>
+ <td>Alt+Insert (or Insert) *</td>
+ </tr>
+</tbody>
+</table>
+
+<p>* Shortcuts in parentheses apply when the
+ <a href="cs_nav_prefs_navigator.xhtml#tabbed_browsing"> Switch to new tabs
+ opened from links</a> setting is disabled. Depending on the
+ <a href="cs_nav_prefs_navigator.xhtml#tabbed_browsing"> Open tabs instead
+ of windows</a> settings, more keys might be available.</p>
+
+<h2 id="sidebar_shortcuts">Sidebar Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Open/Close Sidebar (toggle)</td>
+ <td>F9</td>
+ <td>Cmd+Opt+S</td>
+ <td>F9</td>
+ </tr>
+ <tr>
+ <td>Switch to Next Sidebar Panel</td>
+ <td>Alt+Page Down</td>
+ <td>Opt+Page Down</td>
+ <td>Alt+Page Down</td>
+ </tr>
+ <tr>
+ <td>Switch to Previous Sidebar Panel</td>
+ <td>Alt+Page Up</td>
+ <td>Opt+Page Up</td>
+ <td>Alt+Page Up</td>
+ </tr>
+</tbody>
+</table>
+
+<h2 id="forms_shortcuts">Forms Shortcuts</h2>
+
+<table class="defaultTable">
+<colgroup>
+ <col class="commandColumn"></col>
+ <col class="osFirstColumn"></col>
+ <col class="osSecondColumn"></col>
+ <col class="osThirdColumn"></col>
+</colgroup>
+<thead>
+ <tr>
+ <th>Command</th>
+ <th>Windows</th>
+ <th>macOS</th>
+ <th>Linux or Unix</th>
+ </tr>
+</thead>
+<tbody class="tbody-default">
+ <tr>
+ <td>Move to Next/Previous Item in Form</td>
+ <td>Tab/Shift+Tab</td>
+ <td>Tab/Shift+Tab</td>
+ <td>Tab/Shift+Tab</td>
+ </tr>
+ <tr>
+ <td>Submit Form</td>
+ <td>Enter</td>
+ <td>Return</td>
+ <td>Enter</td>
+ </tr>
+ <tr>
+ <td>Press Selected Button / Select Radio Button</td>
+ <td>Space</td>
+ <td>Space</td>
+ <td>Space</td>
+ </tr>
+ <tr>
+ <td>Select an Item from a List</td>
+ <td>Up Arrow, Down Arrow or First Letter of Item Name</td>
+ <td>Up Arrow, Down Arrow or First Letter of Item Name</td>
+ <td>Up Arrow, Down Arrow or First Letter of Item Name</td>
+ </tr>
+ <tr>
+ <td>Check/Uncheck Checkbox (toggle)</td>
+ <td>Space</td>
+ <td>Space</td>
+ <td>Space</td>
+ </tr>
+ <tr>
+ <td>Open a Drop-Down Menu</td>
+ <td>Alt+Down Arrow</td>
+ <td>Opt+Down Arrow</td>
+ <td>Alt+Down Arrow</td>
+ </tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/ssl_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/ssl_help.xhtml
new file mode 100644
index 0000000000..9725c90a61
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/ssl_help.xhtml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>SSL/TLS Settings</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="ssltls_settings">SSL/TLS Settings</h1>
+
+<p>This section describes how to set your SSL/TLS preferences.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#privacy_and_security_preferences_ssltls">Privacy &amp; Security
+ Preferences - SSL/TLS</a></li>
+ </ul>
+</div>
+
+<h2 id="privacy_and_security_preferences_ssltls">Privacy &amp; Security
+ Preferences - SSL/TLS</h2>
+
+<p>This section describes how to use the SSL/TLS preferences panel. If you are
+ not already viewing the panel, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click SSL/TLS. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<h3 id="ssltls_protocol_versions">SSL/TLS Protocol Versions</h3>
+
+<p>The <a href="glossary.xhtml#ssl">Secure Sockets Layer (SSL)</a> protocol
+ and its successor, the <a href="glossary.xhtml#tls">Transport Layer Security
+ (TLS)</a> protocol, are standards which define rules governing mutual
+ authentication between a website and browser software and the encryption
+ of information that flows between them. They are also used for secure
+ communication in various other protocols, e.g., for protection of sensitive
+ information exchanged with email, calendar, or directory servers.</p>
+
+<p>The SSL 2.0 and SSL 3.0 protocols are insecure and thus deprecated. The
+ current TLS protocol is based on SSL but with its own version numbering.
+ TLS 1.0 can be thought of as SSL 3.1, TLS 1.1 is in turn an update to TLS
+ 1.0, etc. Newer protocols are preferred over older ones as they provide
+ better security and more features. Older protocols are supported to ensure
+ compatibility.</p>
+
+<p>By default, &brandShortName; will select the most secure version which is
+ widely supported to connect to the server. If that attempt doesn&apos;t
+ succeed, it will try to connect with the next older version, etc., to the
+ extent allowed by the settings in this panel. The connection will fail if no
+ protocol supported by both sides is found. You can exclude older versions
+ explicitly or allow newer versions which may not be widely supported yet
+ with the following options:</p>
+
+<ul>
+ <li><strong>Enable</strong>: Check the <strong>TLS 1.0</strong>,
+ <strong>TLS 1.1</strong>, <strong>TLS 1.2</strong>, and/or
+ <strong>TLS 1.3</strong> boxes to indicate which protocol versions can be
+ used for a secure connection to a server.</li>
+</ul>
+
+<p><strong>Notes</strong>:</p>
+
+<ul>
+ <li>At least one protocol version must be selected, thus it is not possible
+ to uncheck the last remaining box.</li>
+ <li>Also, the selection must be contiguous. It is not possible to select both
+ TLS 1.0 and TLS 1.2 but to exclude the intermediate TLS 1.1 version.</li>
+ <li>You can extend the range by multiple versions. For example, if only TLS
+ 1.0 is currently checked and you select TLS 1.2, the TLS 1.1 version is
+ automatically selected as well.</li>
+ <li>Checkboxes may appear checked but grayed out if you cannot uncheck them
+ without violating these rules. Uncheck the outermost boxes to regain
+ access to an enclosed intermediate version.</li>
+</ul>
+
+<h3 id="ssltls_warnings">SSL/TLS Warnings</h3>
+
+<p>It&apos;s easy to tell when the website you are viewing is using an encrypted
+ connection. If the connection is encrypted, the lock icon in the lower-right
+ corner of the browser window is locked
+ (<img src="chrome://communicator/skin/icons/lock-secure.png"/>). If the
+ connection is not encrypted, the lock icon is unlocked
+ (<img src="chrome://communicator/skin/icons/lock-insecure.png"/>). Encrypted
+ pages which contain some unencrypted items (mixed content) are shown with a
+ broken-lock icon
+ (<img src="chrome://communicator/skin/icons/lock-broken.png"/>).</p>
+
+<p>If you want additional warnings, you can select one or more of the warning
+ checkboxes in the SSL/TLS preferences panel. Unless stated otherwise, a
+ notification bar will be presented at the top of the page triggering the
+ alert, with an option to enter this panel to change the option if the alert
+ is considered annoying.</p>
+
+<p>To activate any of these warnings, select the corresponding checkbox:</p>
+
+<ul>
+ <li><strong>Loading a page that supports encryption</strong>: Select this
+ warning if you want to be reminded whenever you are loading a page that
+ supports encryption.</li>
+ <li><strong>Leaving a page that supports encryption</strong>: Select this
+ warning if you want to be reminded whenever you are leaving a page that
+ supports encryption for one that does not.</li>
+ <li><strong>Sending form data from an unencrypted page to an unencrypted
+ page</strong>: Select this warning if you want to be alerted whenever you
+ are submitting data over an unencrypted connection. When this option is
+ selected, a dialog box will be presented to the user <em>before</em> the
+ page is actually opened, which allows the loading of the page to be
+ canceled before any potentially sensitive information is sent over an
+ unencrypted connection that can easily be intercepted by others.
+
+ <p><strong>Note</strong>: Submitting a form from an encrypted to an
+ unencrypted page will always prompt a dialog prior to opening the page,
+ regardless of this setting.</p>
+ </li>
+</ul>
+
+<h3 id="mixed_content">Mixed Content</h3>
+
+<p>In general, there are two major issues related to transmitting sensitive
+ information over an unencrypted connection: One is the danger of someone
+ eavesdropping on the line, thus listening to the content transmitted; the
+ other of someone intercepting requests for the desired page and replacing
+ the legitimate content of that page with own (potentially malicious)
+ content. While so-called <q>Man In The Middle</q> attacks can usually be
+ detected in encrypted connections (e.g., by a certificate mismatch or an
+ invalid certificate presented by the interceptor), no such verification
+ exists for unencrypted connections.</p>
+
+<p>The term <q>Mixed Content</q> refers to a web page which itself is
+ encrypted, but which includes content on the same or a different server
+ which is <em>not</em> encrypted. Consequently, this part of the page is
+ still subject to the vulnerabilities of an unencrypted line. While there
+ are legitimate uses of that concept (such as including a company logo from
+ a different insecure website into an otherwise secure page), such designs
+ should be avoided.</p>
+
+<p>There are two general types of mixed content:</p>
+
+<ul>
+ <li><strong>Mixed Active Content</strong> (or Mixed Script Content): This
+ is content which has the potential to hide or modify parts of a web page,
+ or to actively leak content from the secure part of the page to its
+ insecure part. Examples include scripts (JavaScript), style sheets (CSS),
+ or the embedding of entire web pages into the main web page (iframes).</li>
+ <li><strong>Mixed Passive Content</strong> (or Mixed Display Content):
+ This type of content does <em>not</em> have the potential to alter or
+ monitor the web page as such. Examples include images and audio or video
+ streams. It is however possible that sensitive information is passed as
+ an encoding of the content&apos;s location (URL), as cookies, or returned
+ with the content itself (e.g., as text included in an image). Thus, passive
+ content isn&apos;t entirely harmless either.</li>
+</ul>
+
+<p>The following options allow you to be warned about and/or to block both
+ mixed active and mixed passive content:</p>
+
+<ul>
+ <li><strong>Warn me when encrypted pages contain insecure content</strong>:
+ Check this to instruct &brandShortName; to present a notification bar when
+ mixed <em>active</em> content was loaded or blocked. The notification bar
+ contains a button to open this preference panel.</li>
+ <li><strong>Don&apos;t load insecure content on encrypted pages</strong>:
+ Check this to prevent mixed active content from being loaded at all but
+ to be blocked. If also the <q>Warn me</q> option is checked, the
+ notification bar will contain two additional buttons:
+ <ul>
+ <li><strong>Keep Blocking</strong>: Dismiss the notification bar without
+ loading the potentially insecure content.</li>
+ <li><strong>Unblock</strong>:
+ Load the potentially insecure content <em>once</em> but not
+ automatically when this page is visited again in the future.</li>
+ </ul>
+ <strong>Note</strong>: The selection of <q>Unblock</q> for a specific site
+ can be revoked in the Permissions tab of the Data Manager. When in a
+ <a href="using_priv_help.xhtml#browsing_in_a_private_window">private
+ window</a>, these options aren&apos;t available in the notification bar.
+ </li>
+ <li><strong>Warn me when encrypted pages contain other types of mixed
+ content</strong>: Check this to instruct &brandShortName; to present a
+ notification bar when mixed <em>passive</em> content was loaded or blocked.
+ The notification bar contains a button to open this preference panel.</li>
+ <li><strong>Don&apos;t load other types of mixed content on encrypted
+ pages</strong>: Check this to prevent mixed passive content from being
+ loaded at all but to be blocked. If also the <q>Warn me</q> option is
+ checked, a notification is presented that such content was blocked.</li>
+</ul>
+
+<p>For short definitions, click
+ <a href="glossary.xhtml#authentication">authentication</a>,
+ <a href="glossary.xhtml#encryption">encryption</a>, or
+ <a href="glossary.xhtml#certificate">certificate</a>.</p>
+
+<p>For more information about ciphers and encryption, see the following online
+ documents:</p>
+
+<ul>
+ <li>
+ <a href="https://developer.mozilla.org/en-US/docs/Introduction_to_Public-Key_Cryptography">Introduction
+ to Public-Key Cryptography</a></li>
+ <li>
+ <a href="https://developer.mozilla.org/en-US/docs/Introduction_to_SSL">Introduction
+ to SSL</a></li>
+ <li>
+ <a href="https://developer.mozilla.org/en-US/docs/NSS">Technologies
+ Available in the Network Security Services (NSS)</a>.</li>
+</ul>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/suite-toc.rdf b/comm/suite/locales/en-US/chrome/common/help/suite-toc.rdf
new file mode 100644
index 0000000000..838f04eebc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/suite-toc.rdf
@@ -0,0 +1,1203 @@
+<?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 rdf:RDF SYSTEM "chrome://branding/locale/brand.dtd" >
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+
+ <rdf:Description about="urn:root">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="welcome" nc:name="Help and Support Center" nc:link="welcome_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="help-help" nc:name="Using the Help Window" nc:link="help_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav" nc:name="Browsing the Web" nc:link="nav_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail" nc:name="Using Mail &amp; Newsgroups" nc:link="mailnews_getting_started.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp" nc:name="Creating Web Pages" nc:link="composer_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust" nc:name="Customizing &brandShortName;" nc:link="customize_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="using-priv-help" nc:name="Using Privacy Features" nc:link="privacy_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="using-help-certs" nc:name="Using Certificates" nc:link="using_certs_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="profile-help" nc:name="Managing Profiles" nc:link="profiles_help.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="shortcuts" nc:name="&brandShortName; Keyboard Shortcuts" nc:link="shortcuts.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="tools" nc:name="Tools and Development" nc:link="developer_tools.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="glossary" nc:name="Glossary" nc:link="glossary.xhtml"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#help-help">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="help-help-window" nc:name="Finding the Topic You Want" nc:link="help_help.xhtml#finding_the_topic_you_want"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="help-help-retrace" nc:name="Retracing Your Steps and Printing" nc:link="help_help.xhtml#retracing_your_steps"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="help-help-buttons" nc:name="Using Help Buttons" nc:link="help_help.xhtml#using_help_buttons"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="help-help-tips" nc:name="Search Tips" nc:link="help_help.xhtml#search_tips"/></rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc" nc:name="Navigating Web Pages" nc:link="nav_help.xhtml#navigating_web_pages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-search" nc:name="Searching the Web" nc:link="nav_help.xhtml#searching_the_web"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-printsave" nc:name="Copying, Saving, and Printing Pages" nc:link="nav_help.xhtml#copying_saving_and_printing_pages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-language" nc:name="Using Languages and International Content" nc:link="nav_help.xhtml#using_languages_and_international_content"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-pluginsdownloads" nc:name="Plugins and Downloads" nc:link="nav_help.xhtml#plugins_and_downloads"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-ses" nc:name="Improving Speed and Efficiency" nc:link="nav_help.xhtml#improving_speed_and_efficiency"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-proxies" nc:name="Proxies" nc:link="nav_help.xhtml#proxies"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-page-info" nc:name="Viewing Page Info" nc:link="page_info_help.xhtml"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-doc">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc-view" nc:name="Viewing Your Home Page" nc:link="nav_help.xhtml#viewing_your_home_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-move" nc:name="Moving to Another Page" nc:link="nav_help.xhtml#moving_to_another_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-link" nc:name="Clicking a Link" nc:link="nav_help.xhtml#clicking_a_link"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-steps" nc:name="Retracing Your Steps" nc:link="nav_help.xhtml#retracing_your_steps"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-reopen" nc:name="Reopening Closed Tabs or Windows" nc:link="nav_help.xhtml#reopening_closed_tabs_windows"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-reload" nc:name="Stopping and Reloading" nc:link="nav_help.xhtml#stopping_and_reloading"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-bmark" nc:name="Visiting Bookmarked Pages" nc:link="nav_help.xhtml#visiting_bookmarked_pages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-tabbed" nc:name="Using Tabbed Browsing" nc:link="nav_help.xhtml#using_tabbed_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-sidebar" nc:name="Using Sidebar" nc:link="nav_help.xhtml#using_sidebar"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-doc-search">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc-searchweb" nc:name="Fast Searches" nc:link="nav_help.xhtml#fast_searches"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-searchprefs" nc:name="Setting Search Preferences" nc:link="nav_help.xhtml#setting_search_preferences"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-searchpage" nc:name="Searching Within a Page" nc:link="nav_help.xhtml#searching_within_a_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-find_as_you_type" nc:name="Using Find-as-you-type" nc:link="nav_help.xhtml#using_find_as_you_type"/></rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-searchbookmark" nc:name="Searching the Bookmarks or History List" nc:link="nav_help.xhtml#searching_the_bookmarks_or_history_list"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-doc-printsave">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc-copy" nc:name="Copying Part of a Page" nc:link="nav_help.xhtml#copying_part_of_a_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-save" nc:name="Saving All or Part of a Page" nc:link="nav_help.xhtml#saving_all_or_part_of_a_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-print" nc:name="Printing a Page" nc:link="nav_help.xhtml#printing_a_page"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-doc-language">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc-charencode" nc:name="Selecting Text Encodings and Fonts" nc:link="nav_help.xhtml#selecting_text_encodings_and_fonts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-languagepref" nc:name="Setting Language Preferences" nc:link="nav_help.xhtml#setting_language_preferences"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-webcontent" nc:name="Finding a &brandShortName; version in your own language" nc:link="nav_help.xhtml#finding_localized_version"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-doc-pluginsdownloads">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc-plugins" nc:name="Plugins" nc:link="nav_help.xhtml#plugins"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-helperapps" nc:name="Helper Applications" nc:link="nav_help.xhtml#helper_applications"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-dlmanager" nc:name="Download Manager" nc:link="nav_help.xhtml#download_manager"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-doc-ses">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="nav-doc-autoload" nc:name="Automatic Loading" nc:link="nav_help.xhtml#automatic_loading"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-keywords" nc:name="Using Custom Bookmark Keywords" nc:link="nav_help.xhtml#custom_keywords"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-cache" nc:name="Changing Cache Settings" nc:link="nav_help.xhtml#changing_cache_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-smartup" nc:name="Getting the Latest Software Automatically" nc:link="nav_help.xhtml#getting_the_latest_software_automatically"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="nav-doc-mousewheel" nc:name="Using a Mouse Wheel" nc:link="nav_help.xhtml#using_a_mouse_wheel"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#nav-page-info">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="pageinfo_general" nc:name="General Tab" nc:link="page_info_help.xhtml#general_tab"/> </rdf:li>
+ <rdf:li>
+ <rdf:Description ID="pageinfo_media"
+ nc:name="Media Tab"
+ nc:link="page_info_help.xhtml#media_tab"/>
+ </rdf:li>
+ <rdf:li> <rdf:Description ID="pageinfo_forms" nc:name="Forms Tab" nc:link="page_info_help.xhtml#forms_tab"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="pageinfo_links" nc:name="Links Tab" nc:link="page_info_help.xhtml#links_tab"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="pageinfo_security" nc:name="Security Tab" nc:link="page_info_help.xhtml#security_tab"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<!-- MAIL HELP SECTION -->
+
+<rdf:Description about="#mail">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc" nc:name="Getting Started with Mail &amp; Newsgroups" nc:link="mailnews_getting_started.xhtml#getting_started_with_mozilla_mail_and_newsgroups" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-importing-other" nc:name="Importing Mail from Other Programs" nc:link="mailnews_getting_started.xhtml#importing_mail_from_other_programs" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-read" nc:name="Reading Messages" nc:link="mailnews_using_mail.xhtml#reading_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send" nc:name="Sending Messages" nc:link="mailnews_using_mail.xhtml#sending_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-html" nc:name="Creating HTML Mail Messages" nc:link="mailnews_using_mail.xhtml#creating_html_mail_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-attach" nc:name="Using Attachments" nc:link="mailnews_using_mail.xhtml#using_attachments" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-delete" nc:name="Deleting Messages" nc:link="mailnews_using_mail.xhtml#deleting_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-address" nc:name="Using Address Books" nc:link="mailnews_addressbooks.xhtml#using_address_books" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-folders" nc:name="Organizing Your Messages" nc:link="mailnews_organizing.xhtml#organizing_your_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-junk" nc:name="Controlling Junk Mail" nc:link="mailnews_organizing.xhtml#controlling_junk_mail" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-news" nc:name="Getting Started With Newsgroups" nc:link="mailnews_newsgroups.xhtml#getting_started_with_newsgroups" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-blogs" nc:name="Getting Started With Blogs &amp; News Feeds" nc:link="mailnews_blogs_and_feeds.xhtml#getting_started_with_blogs_and_news_feeds" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-offline" nc:name="Working Offline" nc:link="mailnews_offline.xhtml#working_offline" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="sign-encrypt" nc:name="Signing &amp; Encrypting Messages" nc:link="mailnews_security.xhtml#signing_and_encrypting_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-account-settings" nc:name="Mail &amp; Newsgroups Account Settings" nc:link="mailnews_account_settings.xhtml#mail_and_newsgroups_account_settings" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-mailprefs" nc:name="Mail &amp; Newsgroups Preferences" nc:link="mailnews_preferences.xhtml#mail_and_newsgroup_preferences" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+ <rdf:Description about="#mail-doc">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-setup" nc:name="Using the Mail Account Setup Wizard" nc:link="mailnews_getting_started.xhtml#using_the_mail_account_setup_wizard"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add" nc:name="Setting Up Additional Accounts" nc:link="mailnews_getting_started.xhtml#setting_up_additional_mail_and_news_accounts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-change" nc:name="Changing the Settings for an Account" nc:link="mailnews_getting_started.xhtml#changing_the_settings_for_an_account"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-importing-other">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-importing-mail-messages" nc:name="Importing Mail Messages" nc:link="mailnews_getting_started.xhtml#importing_mail_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-importing-mail-settings" nc:name="Importing Mail Settings" nc:link="mailnews_getting_started.xhtml#importing_mail_settings" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-read">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-notify" nc:name="Getting New Messages" nc:link="mailnews_using_mail.xhtml#getting_new_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-view" nc:name="Choosing How You View the Mail Window" nc:link="mailnews_using_mail.xhtml#choosing_how_you_view_the_mail_window"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-thread" nc:name="Sorting and Threading Messages" nc:link="mailnews_using_mail.xhtml#sorting_and_threading_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-printsave" nc:name="Saving and Printing Messages" nc:link="mailnews_using_mail.xhtml#saving_and_printing_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-scripts" nc:name="Controlling Images, Scripts, and Plugins" nc:link="mailnews_using_mail.xhtml#controlling_images_scripts_and_plugins"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-send">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-send-compose" nc:name="Composing Messages" nc:link="mailnews_using_mail.xhtml#composing_mail_and_newsgroup_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-new" nc:name="Using the Compose Window" nc:link="mailnews_using_mail.xhtml#using_the_message_composition_window" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-address" nc:name="Addressing a Message" nc:link="mailnews_using_mail.xhtml#addressing_a_message" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-options" nc:name="Selecting Message Sending Options" nc:link="mailnews_using_mail.xhtml#selecting_message_sending_options" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-reply" nc:name="Replying to a Message" nc:link="mailnews_using_mail.xhtml#replying_to_a_message" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-forward" nc:name="Forwarding a Message" nc:link="mailnews_using_mail.xhtml#forwarding_a_message" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-receipt" nc:name="Confirming That Your Message Was Opened" nc:link="mailnews_using_mail.xhtml#confirming_that_your_message_was_opened" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-edit" nc:name="Saving and Editing a Draft Message" nc:link="mailnews_using_mail.xhtml#saving_and_editing_a_draft_message" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-send-template" nc:name="Creating and Using Templates" nc:link="mailnews_using_mail.xhtml#creating_and_using_templates" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-html">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-html-use" nc:name="Using HTML in Your Messages" nc:link="mailnews_using_mail.xhtml#using_html_in_your_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-html-options" nc:name="Choosing HTML Mail Sending Options" nc:link="mailnews_using_mail.xhtml#choosing_html_mail_sending_options" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-html-address" nc:name="Specifying Recipients for HTML Messages" nc:link="mailnews_using_mail.xhtml#specifying_recipients_for_html_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-html-source" nc:name="Viewing HTML Message Source" nc:link="mailnews_using_mail.xhtml#viewing_the_message_source_for_html_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-html-question" nc:name="Using the HTML Mail Question Dialog Box" nc:link="mailnews_using_mail.xhtml#using_the_html_mail_question_dialog_box" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-html-use">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-html-edit" nc:name="Editing or Inserting HTML" nc:link="mailnews_using_mail.xhtml#editing_or_inserting_html_elements" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-attach">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-attach-file" nc:name="Attaching a File or Web Page" nc:link="mailnews_using_mail.xhtml#attaching_a_file_or_web_page" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-attach-view" nc:name="Viewing and Opening Attachments" nc:link="mailnews_using_mail.xhtml#viewing_and_opening_attachments" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-attach-save" nc:name="Saving Attachments" nc:link="mailnews_using_mail.xhtml#saving_attachments" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-delete">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-delete-server" nc:name="Deleting POP or IMAP Messages" nc:link="mailnews_using_mail.xhtml#deleting_pop_or_imap_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-delete-trash" nc:name="Moving Messages to and from the Trash" nc:link="mailnews_using_mail.xhtml#moving_messages_to_and_from_the_trash" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-address">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-add-about" nc:name="About Mail Address Books" nc:link="mailnews_addressbooks.xhtml#about_address_books" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-names" nc:name="Adding Entries to Your Address Books" nc:link="mailnews_addressbooks.xhtml#adding_entries_to_your_address_books" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-book" nc:name="Creating a New Address Book" nc:link="mailnews_addressbooks.xhtml#creating_a_new_address_book" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-card" nc:name="Creating a New Address Book Card" nc:link="mailnews_addressbooks.xhtml#creating_a_new_address_book_card" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-list" nc:name="Creating a Mailing List" nc:link="mailnews_addressbooks.xhtml#creating_a_mailing_list" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-listedit" nc:name="Editing a Mailing List" nc:link="mailnews_addressbooks.xhtml#editing_a_mailing_list" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_ab_search" nc:name="Searching Address Books" nc:link="mailnews_addressbooks.xhtml#searching_address_books_and_directories" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-import" nc:name="Importing Address Books" nc:link="mailnews_addressbooks.xhtml#importing_address_books" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-export" nc:name="Exporting Address Books" nc:link="mailnews_addressbooks.xhtml#exporting_address_books" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-add-sync-LDAP-add" nc:name="Adding and Removing LDAP Directories" nc:link="mailnews_addressbooks.xhtml#adding_and_removing_ldap_directories" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<rdf:Description about="#mail-doc-add-card">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-card-properties" nc:name="Editing Card Properties" nc:link="mailnews_addressbooks.xhtml#viewing_or_editing_card_properties" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail_ab_search">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail_advanced_ab_search" nc:name="Searching for Specific Entries" nc:link="mailnews_addressbooks.xhtml#searching_for_specific_entries" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-add-sync-LDAP-add">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-ldap-properties" nc:name="Directory Server Settings" nc:link="mailnews_addressbooks.xhtml#directory_server_settings" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-folders">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="folder-creating" nc:name="Creating a Folder" nc:link="mailnews_organizing.xhtml#creating_a_folder"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="folder-renaming" nc:name="Renaming a Folder" nc:link="mailnews_organizing.xhtml#renaming_a_folder"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="folder-copying" nc:name="Moving or Copying a Folder" nc:link="mailnews_organizing.xhtml#moving_or_copying_a_folder"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="folder-opening" nc:name="Filing Messages in Folders" nc:link="mailnews_organizing.xhtml#filing_messages_in_folders"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="folder-sharing" nc:name="Sharing Folders" nc:link="mailnews_organizing.xhtml#sharing_folders_with_other_users"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="tagging-messages" nc:name="Tagging Messages" nc:link="mailnews_organizing.xhtml#tagging_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-flagging" nc:name="Marking or Flagging Messages" nc:link="mailnews_organizing.xhtml#marking_or_flagging_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="message-views-using" nc:name="Using Message Views" nc:link="mailnews_organizing.xhtml#using_message_views"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-filters" nc:name="Creating Message Filters" nc:link="mailnews_organizing.xhtml#creating_message_filters"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="search-mailnews" nc:name="Searching Through Messages" nc:link="mailnews_organizing.xhtml#searching_through_messages"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#folder-sharing">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="folder-subscribing" nc:name="Subscribing to a Shared Folder" nc:link="mailnews_organizing.xhtml#subscribing_to_a_shared_folder"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#tagging-messages">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="tag-apply" nc:name="Applying a Tag" nc:link="mailnews_organizing.xhtml#applying_a_tag"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="tag-customize" nc:name="Customizing Tags" nc:link="mailnews_organizing.xhtml#customizing_tags"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="tag-sort" nc:name="Sorting Messages by Tags" nc:link="mailnews_organizing.xhtml#sorting_messages_by_tags"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="tag-remove" nc:name="Removing Tags" nc:link="mailnews_organizing.xhtml#removing_tags"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#message-views-using">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="message-views-create-new" nc:name="Creating a Custom View" nc:link="mailnews_organizing.xhtml#creating_a_custom_view"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-filters">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="send-filter" nc:name="Filtering Messages from a Specific Sender" nc:link="mailnews_organizing.xhtml#filtering_messages_from_a_specific_sender"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#search-mailnews">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="search_messages" nc:name="Searching for Specific Messages" nc:link="mailnews_organizing.xhtml#searching_for_specific_messages"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-junk">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-junk-controls" nc:name="Using Junk Mail Controls" nc:link="mailnews_organizing.xhtml#using_junk_mail_controls" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-junk-options" nc:name="Junk Mail Controls Options" nc:link="mailnews_organizing.xhtml#junk_controls_options" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-junk-filters" nc:name="Junk Mail Controls and Filters" nc:link="mailnews_organizing.xhtml#junk_controls_and_filters" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-junk-phishing" nc:name="Phishing Detection" nc:link="mailnews_organizing.xhtml#phishing_detection" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#mail-doc-news">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-subscribe" nc:name="Subscribing to Newsgroups" nc:link="mailnews_newsgroups.xhtml#subscribing_to_newsgroups"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-reading-news" nc:name="Reading Newsgroup Messages" nc:link="mailnews_newsgroups.xhtml#reading_newsgroup_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-posting" nc:name="Posting Newsgroup Messages" nc:link="mailnews_newsgroups.xhtml#posting_newsgroup_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-replying-news" nc:name="Contributing to Ongoing Discussions" nc:link="mailnews_newsgroups.xhtml#contributing_to_ongoing_discussions"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-monitoring" nc:name="Monitoring Threads" nc:link="mailnews_newsgroups.xhtml#monitoring_threads"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-delete-news" nc:name="Removing a Newsgroup" nc:link="mailnews_newsgroups.xhtml#removing_a_newsgroup"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-add-newsserver" nc:name="Adding a Newsgroup Server" nc:link="mailnews_newsgroups.xhtml#adding_a_newsgroup_server"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-blogs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-blogs-subscribe" nc:name="Subscribing to blogs &amp; news feeds" nc:link="mailnews_blogs_and_feeds.xhtml#subscribing_to_blogs_and_news_feeds"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-subscribe-from-browser" nc:name="Subscribing to blogs &amp; news feeds from a browser window" nc:link="mailnews_blogs_and_feeds.xhtml#subscribing_to_blogs_and_news_feeds_from_browser"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-reading" nc:name="Reading blogs &amp; news feed messages" nc:link="mailnews_blogs_and_feeds.xhtml#reading_blogs_and_news_feed_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-posting" nc:name="Posting blog messages" nc:link="mailnews_blogs_and_feeds.xhtml#posting_blog_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-comments" nc:name="Adding comments to a blog post" nc:link="mailnews_blogs_and_feeds.xhtml#adding_comments_to_a_blog_post"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-exporting-importing" nc:name="Exporting and importing feeds" nc:link="mailnews_blogs_and_feeds.xhtml#exporting_and_importing_feeds"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-editing" nc:name="Editing a feed" nc:link="mailnews_blogs_and_feeds.xhtml#editing_a_feed"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-removing" nc:name="Removing a feed" nc:link="mailnews_blogs_and_feeds.xhtml#removing_a_feed"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-using-different-accounts" nc:name="Using different blogs &amp; news feeds accounts" nc:link="mailnews_blogs_and_feeds.xhtml#using_different_blogs_and_news_feeds_accounts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-organizing" nc:name="Organizing your feeds" nc:link="mailnews_blogs_and_feeds.xhtml#organizing_your_feeds"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-blogs-organizing">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-blogs-feeds-folders" nc:name="Feeds versus folders" nc:link="mailnews_blogs_and_feeds.xhtml#feeds_vs_folders"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-folders-in-blogs" nc:name="Organizing folders in Blogs &amp; News Feeds accounts" nc:link="mailnews_blogs_and_feeds.xhtml#organizing_folders_in_blogs_and_news_feeds_accounts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-multiple-feeds" nc:name="Downloading multiple feeds in a single folder" nc:link="mailnews_blogs_and_feeds.xhtml#downloading_multiple_feeds_in_a_single_folder"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-blogs-moving-feeds" nc:name="Moving a feed to another folder" nc:link="mailnews_blogs_and_feeds.xhtml#moving_a_feed_to_another_folder"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-offline">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-offline-setup" nc:name="Setting Up Mail &amp; Newsgroups to Work Offline" nc:link="mailnews_offline.xhtml#setting_up_mozilla_mail_and_newsgroups_to_work_offline"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-inbox" nc:name="Downloading All Messages for Offline Use" nc:link="mailnews_offline.xhtml#downloading_all_messages_for_offline_use"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-folder" nc:name="Downloading an Individual Folder" nc:link="mailnews_offline.xhtml#downloading_an_individual_folder_for_offline_use"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-flagged" nc:name="Downloading Selected or Flagged Messages" nc:link="mailnews_offline.xhtml#downloading_selected_or_flagged_messages_for_offline_use"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-directory" nc:name="Downloading Directory Entries" nc:link="mailnews_offline.xhtml#downloading_directory_entries_for_offline_use"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-accounts" nc:name="Setting Up Your Accounts for Working Offline" nc:link="mailnews_offline.xhtml#setting_up_your_accounts_for_working_offline"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-items" nc:name="Selecting Items for Offline Viewing" nc:link="mailnews_offline.xhtml#selecting_items_for_offline_viewing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-sync" nc:name="Downloading and Synchronizing Your Messages" nc:link="mailnews_offline.xhtml#downloading_and_synchronizing_your_messages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-offline-go" nc:name="Working Offline and Reconnecting Later" nc:link="mailnews_offline.xhtml#working_offline_and_reconnecting_later"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#sign-encrypt">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="sign-encrypt-about" nc:name="Digital Signatures &amp; Encryption" nc:link="mailnews_security.xhtml#about_digital_signatures_and_encryption" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="sign-encrypt-get" nc:name="Getting Other People's Certificates" nc:link="mailnews_security.xhtml#getting_other_peoples_certificates" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="sign-encrypt-config" nc:name="Configuring Security Settings" nc:link="mailnews_security.xhtml#configuring_security_settings" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="sign-encrypt-signing" nc:name="Signing &amp; Encrypting a New Message" nc:link="mailnews_security.xhtml#signing_and_encrypting_a_new_message" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="sign-encrypt-read" nc:name="Reading Signed &amp; Encrypted Messages" nc:link="mailnews_security.xhtml#reading_signed_and_encrypted_messages" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="compose_security" nc:name="Message Security - Compose Window" nc:link="mailnews_security.xhtml#message_security_compose_window" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="received_security" nc:name="Message Security - Received Message" nc:link="mailnews_security.xhtml#message_security_received_message" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#sign-encrypt-about">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="sign-encrypt-about-sig" nc:name="How Digital Signatures Work" nc:link="mailnews_security.xhtml#how_digital_signatures_work" /> </rdf:li>
+ <rdf:li> <rdf:Description ID="sign-encrypt-about-encrypt" nc:name="How Encryption Works" nc:link="mailnews_security.xhtml#how_encryption_works" /> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<rdf:Description about="#mail-doc-account-settings">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail_account_identity" nc:name="Account Settings" nc:link="mailnews_account_settings.xhtml#account_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-choose" nc:name="Server Settings" nc:link="mailnews_account_settings.xhtml#server_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_copies" nc:name="Copies &amp; Folders" nc:link="mailnews_account_settings.xhtml#copies_and_folders"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_addressing_settings" nc:name="Composition &amp; Addressing" nc:link="mailnews_account_settings.xhtml#addressing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-offline-space" nc:name="Synchronization &amp; Storage" nc:link="mailnews_account_settings.xhtml#synchronization_and_storage"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-account-receipts" nc:name="Return Receipts" nc:link="mailnews_account_settings.xhtml#return_receipts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-account-junk" nc:name="Junk Settings" nc:link="mailnews_account_settings.xhtml#junk_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_security_settings" nc:name="Security" nc:link="mailnews_account_settings.xhtml#security"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_local_folders_settings" nc:name="Local Folders" nc:link="mailnews_account_settings.xhtml#local_folders"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_smtp" nc:name="Outgoing Server (SMTP)" nc:link="mailnews_account_settings.xhtml#outgoing_server"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-choose">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail-doc-choose-IMAP" nc:name="About IMAP" nc:link="mailnews_account_settings.xhtml#about_internet_message_access_protocol"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-choose-POP" nc:name="About POP" nc:link="mailnews_account_settings.xhtml#about_post_office_protocol"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_server_imap" nc:name="IMAP Server Settings" nc:link="mailnews_account_settings.xhtml#imap_server_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-doc-imap-advanced" nc:name="Advanced IMAP Server Settings" nc:link="mailnews_account_settings.xhtml#advanced_imap_server_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_server_pop3" nc:name="POP Server Settings" nc:link="mailnews_account_settings.xhtml#pop_server_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_server_nntp" nc:name="News Server Settings" nc:link="mailnews_account_settings.xhtml#news_server_settings"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-offline-space">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail_offline_imap" nc:name="IMAP" nc:link="mailnews_account_settings.xhtml#synchronization_and_storage_settings_imap"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_offline_pop3" nc:name="POP" nc:link="mailnews_account_settings.xhtml#disk_space_settings_pop"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_offline_blogs" nc:name="Blogs" nc:link="mailnews_account_settings.xhtml#disk_space_settings_blogs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_offline_nntp" nc:name="News" nc:link="mailnews_account_settings.xhtml#synchronization_and_storage_settings_nntp"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail_security_settings">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail_security_settings_certs" nc:name="About Certificates" nc:link="mailnews_account_settings.xhtml#about_certificates"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_security_settings_sign" nc:name="Digital Signing" nc:link="mailnews_account_settings.xhtml#digital_signing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_security_settings_encrypt" nc:name="Encryption" nc:link="mailnews_account_settings.xhtml#encryption"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#mail-doc-mailprefs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="mail_prefs_general" nc:name="Mail &amp; Newsgroups" nc:link="mailnews_preferences.xhtml#mail_and_newsgroups"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_display" nc:name="Message Display" nc:link="mailnews_preferences.xhtml#message_display"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_notifications" nc:name="Notifications" nc:link="mailnews_preferences.xhtml#notifications"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_messages" nc:name="Composition" nc:link="mailnews_preferences.xhtml#composition"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_formatting" nc:name="Send Format" nc:link="mailnews_preferences.xhtml#send_format"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_addressing" nc:name="Addressing" nc:link="mailnews_preferences.xhtml#addressing_preferences"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-prefs-junk" nc:name="Junk &amp; Suspect Mail" nc:link="mailnews_preferences.xhtml#junk_and_suspect_preferences"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-prefs-tags" nc:name="Tags" nc:link="mailnews_preferences.xhtml#tags"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail-prefs-receipts" nc:name="Return Receipts" nc:link="mailnews_preferences.xhtml#return_receipts_preferences"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_text_encoding" nc:name="Text Encoding" nc:link="mailnews_preferences.xhtml#text_encoding"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="mail_prefs_offline" nc:name="Network &amp; Storage" nc:link="mailnews_preferences.xhtml#network_and_storage_preferences"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<!-- COMPOSER HELP SECTION -->
+
+<rdf:Description about="#comp">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc" nc:name="Starting a New Page" nc:link="composer_help.xhtml#starting_a_new_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="page_change" nc:name="Formatting Your Web Pages" nc:link="composer_help.xhtml#formatting_your_web_pages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-table" nc:name="Adding Tables to Your Web Page" nc:link="composer_help.xhtml#adding_tables_to_your_web_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-image" nc:name="Adding Images to Your Web Page" nc:link="composer_help.xhtml#adding_images_to_your_web_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-math" nc:name="Adding Mathematical Formulas to Your Web Page" nc:link="composer_help.xhtml#adding_mathematical_formulas_to_your_web_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-page" nc:name="Setting Page Properties" nc:link="composer_help.xhtml#setting_page_properties"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="link_properties" nc:name="Creating Links" nc:link="composer_help.xhtml#creating_links_in_composer"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish" nc:name="Publishing Your Pages" nc:link="composer_help.xhtml#publishing_your_pages_on_the_web"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-prefs" nc:name="Composer Preferences" nc:link="composer_help.xhtml#composer_preferences"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#comp-doc">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-create" nc:name="Creating a New Page" nc:link="composer_help.xhtml#creating_a_new_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-save" nc:name="Saving and Browsing Your Page" nc:link="composer_help.xhtml#saving_and_browsing_your_new_page"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#page_change">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="props-para" nc:name="Formatting Paragraphs, Headings, and Lists" nc:link="composer_help.xhtml#formatting_paragraphs_headings_and_lists"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="lists" nc:name="Working with Lists" nc:link="composer_help.xhtml#working_with_lists"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="char" nc:name="Changing Text Color, Style, and Font" nc:link="composer_help.xhtml#changing_text_color_style_and_font"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="style-remove" nc:name="Removing or Discontinuing Text Styles" nc:link="composer_help.xhtml#removing_or_discontinuing_text_styles"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="find-text" nc:name="Finding and Replacing Text" nc:link="composer_help.xhtml#finding_and_replacing_text"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="props-hrule" nc:name="Inserting Horizontal Lines" nc:link="composer_help.xhtml#inserting_horizontal_lines"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="special-chars" nc:name="Inserting Special Characters" nc:link="composer_help.xhtml#inserting_special_characters"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="html-tag" nc:name="Inserting HTML Elements and Attributes" nc:link="composer_help.xhtml#inserting_html_elements_and_attributes"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="validate-html" nc:name="Validating the HTML" nc:link="composer_help.xhtml#validating_the_html"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-choose" nc:name="Choosing the Right Editing Mode" nc:link="composer_help.xhtml#choosing_the_right_editing_mode"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#props-hrule">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="props-hrule-horiz" nc:name="Setting Horizontal Line Properties" nc:link="composer_help.xhtml#setting_horizontal_line_properties"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#html-tag">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="advanced_property_editor" nc:name="Using the Advanced Property Editor" nc:link="composer_help.xhtml#using_the_advanced_property_editor"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#comp-doc-table">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-table-insert" nc:name="Inserting a Table" nc:link="composer_help.xhtml#inserting_a_table"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="table_properties" nc:name="Changing a Table's Properties" nc:link="composer_help.xhtml#changing_a_tables_properties"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-table-add" nc:name="Adding/Deleting Rows, Columns, and Cells" nc:link="composer_help.xhtml#adding_and_deleting_rows_columns_and_cells"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-table-select" nc:name="Selecting Table Elements" nc:link="composer_help.xhtml#selecting_table_elements"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-table-copy" nc:name="Moving, Copying, and Deleting Tables" nc:link="composer_help.xhtml#moving_copying_and_deleting_tables"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-tableize" nc:name="Converting Text into a Table" nc:link="composer_help.xhtml#converting_text_into_a_table"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#comp-doc-table-add">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-table-change-default" nc:name="Changing the Default Table Editing Behavior" nc:link="composer_help.xhtml#changing_the_default_table_editing_behavior"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#comp-doc-image">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-image-insert" nc:name="Inserting an Image into Your Page" nc:link="composer_help.xhtml#inserting_an_image_into_your_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="image_properties" nc:name="Editing Image Properties" nc:link="composer_help.xhtml#editing_image_properties"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#comp-doc-math">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-math-insert" nc:name="Inserting a Mathematical Formula into Your Page" nc:link="composer_help.xhtml#inserting_a_mathematical_formula_into_your_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-math-latex-dialog" nc:name="Editing the LaTeX Source" nc:link="composer_help.xhtml#editing_the_latex_source"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#comp-doc-page">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-page-props" nc:name="Setting Page Properties and Meta Tags" nc:link="composer_help.xhtml#setting_page_properties_and_meta_tags"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-page-appear" nc:name="Setting Colors and Background" nc:link="composer_help.xhtml#setting_page_colors_and_backgrounds"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#link_properties">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-links-inpage" nc:name="Within the Same Page" nc:link="composer_help.xhtml#creating_links_within_the_same_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-links-others" nc:name="To Other Pages" nc:link="composer_help.xhtml#creating_links_to_other_pages"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-links-images" nc:name="Using Images as Links" nc:link="composer_help.xhtml#using_images_as_links"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-links-remove" nc:name="Removing or Discontinuing Links" nc:link="composer_help.xhtml#removing_or_discontinuing_links"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#comp-doc-publish">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-prepare" nc:name="Publishing a Document" nc:link="composer_help.xhtml#publishing_a_document"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-update" nc:name="Updating a Published Document" nc:link="composer_help.xhtml#updating_a_published_document"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-as" nc:name="Changing the File Name or Publishing Location" nc:link="composer_help.xhtml#changing_the_filename_or_publishing_location"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-newsite" nc:name="Creating a New Publishing Site" nc:link="composer_help.xhtml#creating_a_new_publishing_site"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-default" nc:name="Choosing the Default Publishing Site" nc:link="composer_help.xhtml#choosing_the_default_publishing_site"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-delete" nc:name="Deleting a Publishing Site" nc:link="composer_help.xhtml#deleting_a_publishing_site"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-troubleshooting" nc:name="Solving Common Publishing Problems" nc:link="composer_help.xhtml#solving_common_publishing_problems"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-settings" nc:name="Publishing Settings" nc:link="composer_help.xhtml#publishing_settings"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#comp-doc-publish-prepare">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-tips" nc:name="Publishing Tips" nc:link="composer_help.xhtml#tips_for_avoiding_broken_links_or_missing_images"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#comp-doc-publish-troubleshooting">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-troubleshooting-settings" nc:name="Verifying Your Publishing Settings" nc:link="composer_help.xhtml#verifying_your_publishing_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-troubleshooting-files" nc:name="Checking Your File Names" nc:link="composer_help.xhtml#checking_your_filenames"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-troubleshooting-errors" nc:name="Fixing Publishing Errors" nc:link="composer_help.xhtml#fixing_publishing_errors"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#comp-doc-publish-settings">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-publishtab" nc:name="Publish Page - Publish" nc:link="composer_help.xhtml#publish_page_publish"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-settingstab" nc:name="Publish Page - Settings" nc:link="composer_help.xhtml#publish_page_settings"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="comp-doc-publish-site-settings" nc:name="Publish Settings" nc:link="composer_help.xhtml#publish_settings"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#comp-doc-prefs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="composer_prefs_general" nc:name="Composer" nc:link="composer_help.xhtml#composer"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="composer_prefs_newpage" nc:name="New Page Settings" nc:link="composer_help.xhtml#new_page_settings"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<!-- CUSTOMIZATION HELP CONTENT -->
+
+<rdf:Description about="#cust">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-doc" nc:name="Sidebar" nc:link="customize_help.xhtml#sidebar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-tabbed" nc:name="Tabbed Browsing" nc:link="customize_help.xhtml#tabbed_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-font" nc:name="Changing Fonts, Colors, and Themes" nc:link="customize_help.xhtml#changing_fonts_colors_and_themes"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-toolbar" nc:name="Toolbars" nc:link="customize_help.xhtml#toolbars"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk" nc:name="Bookmarks" nc:link="customize_help.xhtml#bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-addons" nc:name="Add-ons" nc:link="customize_help.xhtml#add-ons"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-page" nc:name="Specifying How &brandShortName; Starts Up" nc:link="customize_help.xhtml#specifying_how_mozilla_starts_up"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="appearance_pref" nc:name="Appearance Preferences" nc:link="cs_nav_prefs_appearance.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="navigator_pref_navigator" nc:name="Browser Preferences" nc:link="cs_nav_prefs_navigator.xhtml"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="advanced_pref_advanced" nc:name="Advanced Preferences" nc:link="cs_nav_prefs_advanced.xhtml"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+ <rdf:Description about="#cust-doc">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-sidebar-define" nc:name="What is Sidebar?" nc:link="customize_help.xhtml#what_is_sidebar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-sidebar-openclose" nc:name="Opening, Closing, and Resizing Sidebar" nc:link="customize_help.xhtml#opening_closing_and_resizing_sidebar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-sidebar-using" nc:name="Viewing Sidebar Tabs" nc:link="customize_help.xhtml#viewing_sidebar_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-sidebar-adding" nc:name="Adding Sidebar Tabs" nc:link="customize_help.xhtml#adding_sidebar_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-sidebar-indiv" nc:name="Customizing Individual Sidebar Tabs" nc:link="customize_help.xhtml#customizing_individual_sidebar_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-sidebar-reorg" nc:name="Reorganizing Sidebar Tabs" nc:link="customize_help.xhtml#reorganizing_sidebar_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-sidebar-removing" nc:name="Removing Sidebar Tabs" nc:link="customize_help.xhtml#removing_sidebar_tabs"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cust-tabbed">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-tabbed-whatis" nc:name="What is Tabbed Browsing?" nc:link="customize_help.xhtml#what_is_tabbed_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-tabbed-tabbedsetting" nc:name="Setting up Tabbed Browsing" nc:link="customize_help.xhtml#setting_up_tabbed_browsing"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-tabbed-tabbedopening" nc:name="Opening Tabs" nc:link="customize_help.xhtml#opening_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-tabbed-tabbedmoving" nc:name="Moving Tabs" nc:link="customize_help.xhtml#moving_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-tabbed-tabbedbookmarking" nc:name="Bookmarking Tabs" nc:link="customize_help.xhtml#bookmarking_tabs"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-tabbed-tabbedclosing" nc:name="Closing Tabs" nc:link="customize_help.xhtml#closing_tabs"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cust-font">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-fonts" nc:name="Changing the Default Font" nc:link="customize_help.xhtml#changing_the_default_fonts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-colors" nc:name="Changing the Default Colors" nc:link="customize_help.xhtml#changing_the_default_colors"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-themes" nc:name="Changing the Theme" nc:link="customize_help.xhtml#changing_the_theme"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cust-toolbar">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-menu" nc:name="Menu Bar" nc:link="customize_help.xhtml#menu_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-main" nc:name="Navigation Toolbar" nc:link="customize_help.xhtml#navigation_toolbar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-personal" nc:name="Bookmarks Toolbar" nc:link="customize_help.xhtml#personal_toolbar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-status" nc:name="Status Bar" nc:link="customize_help.xhtml#status_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-taskbar" nc:name="Component Bar" nc:link="customize_help.xhtml#component_bar"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-hide" nc:name="Hiding a Toolbar" nc:link="customize_help.xhtml#hiding_a_toolbar"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cust-bkmk">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-bkmk-intro" nc:name="What Are Bookmarks?" nc:link="customize_help.xhtml#what_are_bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk-use" nc:name="Using Bookmarks" nc:link="customize_help.xhtml#using_bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk-create" nc:name="Creating New Bookmarks" nc:link="customize_help.xhtml#creating_new_bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk-organize" nc:name="Organizing Your Bookmarks" nc:link="customize_help.xhtml#organizing_your_bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk-change" nc:name="Changing Individual Bookmarks" nc:link="customize_help.xhtml#changing_individual_bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk-search" nc:name="Searching Your Bookmarks" nc:link="customize_help.xhtml#searching_your_bookmarks"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-bkmk-multiple" nc:name="Exporting or Importing a Bookmark List" nc:link="customize_help.xhtml#exporting_or_importing_a_bookmark_list"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cust-addons">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-addons-about" nc:name="About Add-ons" nc:link="customize_help.xhtml#about_add-ons"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-addons-installing" nc:name="Installing Add-ons" nc:link="customize_help.xhtml#installing_add-ons"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-addons-manager" nc:name="Using the Add-ons Manager" nc:link="customize_help.xhtml#using_the_add-ons_manager"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-addons-converter" nc:name="Using the Add-on Converter" nc:link="customize_help.xhtml#using_the_add-on_converter"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cust-page">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="cust-startpage" nc:name="Specifying a Starting Page" nc:link="customize_help.xhtml#specifying_a_starting_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-home" nc:name="Changing Your Home Page" nc:link="customize_help.xhtml#changing_your_home_page"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="cust-start" nc:name="Specifying Which Components Open at Launch" nc:link="customize_help.xhtml#specifying_which_components_open_at_launch"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#appearance_pref">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li> <rdf:Description ID="appearance_pref_content" nc:name="Content" nc:link="cs_nav_prefs_appearance.xhtml#content"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="appearance_pref_fonts" nc:name="Fonts" nc:link="cs_nav_prefs_appearance.xhtml#fonts"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="appearance_pref_colors" nc:name="Colors" nc:link="cs_nav_prefs_appearance.xhtml#colors"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="appearance_pref_media" nc:name="Media" nc:link="cs_nav_prefs_appearance.xhtml#media"/> </rdf:li>
+ <rdf:li> <rdf:Description ID="appearance_pref_spelling" nc:name="Spelling" nc:link="cs_nav_prefs_appearance.xhtml#spelling"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#navigator_pref_navigator">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="navigator_pref_history" nc:name="History" nc:link="cs_nav_prefs_navigator.xhtml#history"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_languages" nc:name="Languages" nc:link="cs_nav_prefs_navigator.xhtml#languages"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_helper_applications" nc:name="Helper Applications" nc:link="cs_nav_prefs_navigator.xhtml#helper_applications"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_location_bar" nc:name="Location Bar" nc:link="cs_nav_prefs_navigator.xhtml#location_bar"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_internet_searching" nc:name="Internet Search" nc:link="cs_nav_prefs_navigator.xhtml#internet_search"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_tabbed_browsing" nc:name="Tabbed Browsing" nc:link="cs_nav_prefs_navigator.xhtml#tabbed_browsing"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_link_behavior" nc:name="Link Behavior" nc:link="cs_nav_prefs_navigator.xhtml#link_behavior"/> </rdf:li>
+ <rdf:li><rdf:Description ID="navigator_pref_downloads" nc:name="Downloads" nc:link="cs_nav_prefs_navigator.xhtml#downloads"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#advanced_pref_advanced">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="advanced_pref_scripts" nc:name="Scripts &amp; Plugins" nc:link="cs_nav_prefs_advanced.xhtml#scripts_and_plugins"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_keyboard_nav" nc:name="Keyboard Navigation" nc:link="cs_nav_prefs_advanced.xhtml#keyboard_navigation"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_find_as_you_type" nc:name="Find As You Type" nc:link="cs_nav_prefs_advanced.xhtml#fayt"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_cache" nc:name="Cache" nc:link="cs_nav_prefs_advanced.xhtml#cache"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_offlineapps" nc:name="Offline Apps" nc:link="cs_nav_prefs_advanced.xhtml#offline_apps"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_proxies" nc:name="Proxies" nc:link="cs_nav_prefs_advanced.xhtml#proxies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_http_networking" nc:name="HTTP Networking" nc:link="cs_nav_prefs_advanced.xhtml#http_networking"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_installation" nc:name="Software Installation" nc:link="cs_nav_prefs_advanced.xhtml#software_installation"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_mouse_wheel" nc:name="Mouse Wheel" nc:link="cs_nav_prefs_advanced.xhtml#mouse_wheel"/> </rdf:li>
+ <rdf:li><rdf:Description ID="advanced_pref_debugging" nc:name="Debugging" nc:link="cs_nav_prefs_advanced.xhtml#debugging"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<!-- To load Advanced Proxy Preferences Help content from prefs -->
+<rdf:Description ID="nav-prefs-advanced-proxy-advanced" nc:link="cs_nav_prefs_advanced.xhtml#advanced_proxy_preferences"/>
+
+<!-- USING PRIVACY FEATURES CONTENT -->
+<rdf:Description about="#using-priv-help">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="privacy-doc" nc:name="Privacy on the Internet" nc:link="privacy_help.xhtml#privacy_on_the_internet"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-cookies" nc:name="Using the Cookie Manager" nc:link="using_priv_help.xhtml#using_the_cookie_manager"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-password" nc:name="Using the Password Manager" nc:link="using_priv_help.xhtml#using_the_password_manager"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-private-data" nc:name="Clearing Private Data" nc:link="using_priv_help.xhtml#clearing_private_data"/> </rdf:li>
+ <rdf:li><rdf:Description ID="private-browsing" nc:name="Browsing in a Private Window" nc:link="using_priv_help.xhtml#browsing_in_a_private_window"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-encrypt" nc:name="Encrypting Stored Sensitive Information" nc:link="using_priv_help.xhtml#encrypting_stored_sensitive_information"/> </rdf:li>
+ <rdf:li><rdf:Description ID="images-help-managing" nc:name="Managing Images" nc:link="using_priv_help.xhtml#managing_images"/> </rdf:li>
+ <rdf:li><rdf:Description ID="pop_up_blocking" nc:name="Controlling Popups" nc:link="cs_priv_prefs_popup.xhtml"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen" nc:name="Privacy &amp; Security Preferences" nc:link="privsec_help.xhtml"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#privacy-doc">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="privacy-doc-visit" nc:name="What Information Does My Browser Give to a Website?" nc:link="privacy_help.xhtml#what_information_does_my_browser_give_to_a_website"/> </rdf:li>
+ <rdf:li><rdf:Description ID="privacy-doc-cookies" nc:name="What Are Cookies, and How Do They Work?" nc:link="privacy_help.xhtml#what_are_cookies_and_how_do_they_work"/> </rdf:li>
+ <rdf:li><rdf:Description ID="privacy-doc-tracking" nc:name="Why and How Are Websites Tracking Me?" nc:link="privacy_help.xhtml#why_and_how_are_websites_tracking_me"/> </rdf:li>
+ <rdf:li><rdf:Description ID="privacy-doc-email" nc:name="How Can I Control Web Pages in Email Messages?" nc:link="privacy_help.xhtml#how_can_i_control_web_pages_in_email_messages"/> </rdf:li>
+ <rdf:li><rdf:Description ID="privacy-doc-unauth" nc:name="How Can I Make Sure Unauthorized People Don't Use Information About Me?" nc:link="privacy_help.xhtml#how_can_i_make_sure_unauthorized_people_dont_use_information_about_me"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-priv-help-cookies">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-priv-help-cookies-manage" nc:name="Enabling &amp; Disabling Cookies" nc:link="using_priv_help.xhtml#enabling_and_disabling_cookies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-cookies-site" nc:name="Managing Cookies Website-By-Website" nc:link="using_priv_help.xhtml#managing_cookies_site-by-site"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-cookies-view" nc:name="Viewing Cookies" nc:link="using_priv_help.xhtml#viewing_cookies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-cookies-remove" nc:name="Removing Cookies" nc:link="using_priv_help.xhtml#removing_cookies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-cookies-settings" nc:name="Cookie Manager Settings" nc:link="using_priv_help.xhtml#cookie_manager_settings"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-priv-help-cookies-settings">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="cookies_prefs" nc:name="Cookie Preferences" nc:link="using_priv_help.xhtml#cookies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="cookies_stored" nc:name="Stored Cookies" nc:link="using_priv_help.xhtml#stored_cookies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="cookie_sites" nc:name="Cookie Websites" nc:link="using_priv_help.xhtml#cookie_sites"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-priv-help-password">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-priv-help-password-manage" nc:name="Remembering User Names and Passwords" nc:link="using_priv_help.xhtml#using_password_manager_to_remember_user_names_and_passwords"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-password-auto" nc:name="Entering Names and Passwords Automatically" nc:link="using_priv_help.xhtml#entering_user_names_and_passwords_automatically"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-password-onoff" nc:name="Turning Password Manager On and Off" nc:link="using_priv_help.xhtml#turning_password_manager_on_and_off"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-password-view" nc:name="Managing Stored Passwords" nc:link="using_priv_help.xhtml#viewing_and_managing_stored_passwords"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-password-settings" nc:name="Password Settings" nc:link="passwords_help.xhtml#password_settings"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-priv-help-password-settings">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="passwords_prefs" nc:name="Password Preferences" nc:link="passwords_help.xhtml#passwords"/> </rdf:li>
+ <rdf:li><rdf:Description ID="password_mgr" nc:name="Password Manager" nc:link="passwords_help.xhtml#password_manager"/> </rdf:li>
+ <rdf:li><rdf:Description ID="master-prefs-change" nc:name="Change Master Password" nc:link="passwords_help.xhtml#change_master_password"/> </rdf:li>
+ <rdf:li><rdf:Description ID="master-prefs-reset" nc:name="Reset Master Password" nc:link="passwords_help.xhtml#reset_master_password"/> </rdf:li>
+ <rdf:li><rdf:Description ID="choosing-good-password" nc:name="Choosing a Good Password" nc:link="passwords_help.xhtml#choosing_a_good_password"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-priv-help-private-data">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="types_of_private_data" nc:name="Types of Private Data" nc:link="using_priv_help.xhtml#types_of_private_data"/> </rdf:li>
+ <rdf:li><rdf:Description ID="privatedata_prefs" nc:name="Private Data Preferences" nc:link="using_priv_help.xhtml#private_data_prefs"/> </rdf:li>
+ <rdf:li><rdf:Description ID="clear_private_data_now" nc:name="Clear Private Data Dialog" nc:link="using_priv_help.xhtml#clear_private_data_now"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#private-browsing">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="opening_a_private_window" nc:name="Opening a Private Window" nc:link="using_priv_help.xhtml#opening_a_private_window"/> </rdf:li>
+ <rdf:li><rdf:Description ID="behavior_of_a_private_window" nc:name="Behavior of a Private Window" nc:link="using_priv_help.xhtml#behavior_of_a_private_window"/> </rdf:li>
+ <rdf:li><rdf:Description ID="leaving_the_private_browsing_mode" nc:name="Leaving the Private Browsing Mode" nc:link="using_priv_help.xhtml#leaving_the_private_browsing_mode"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-priv-help-encrypt">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-priv-help-encrypt-master" nc:name="Setting a Master Password" nc:link="using_priv_help.xhtml#setting_a_master_password"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-encrypt-change" nc:name="Changing Your Master Password" nc:link="using_priv_help.xhtml#changing_your_master_password"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-encrypt-logout" nc:name="Logging Out of Your Master Password" nc:link="using_priv_help.xhtml#logging_out_of_your_master_password"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-priv-help-encrypt-forget" nc:name="What to Do If You Forget Your Master Password" nc:link="using_priv_help.xhtml#what_to_do_if_you_forget_your_master_password"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#pop_up_blocking">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="pop_up_blocking_prefs" nc:name="Popup Preferences" nc:link="cs_priv_prefs_popup.xhtml#privacy_and_security_preferences_popup_windows"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<rdf:Description about="#images-help-managing">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="images_prefs" nc:name="Image Preferences" nc:link="using_priv_help.xhtml#images"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#sec_gen">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="sec_gen_private_data" nc:name="Private Data" nc:link="using_priv_help.xhtml#private_data_prefs"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_cookies" nc:name="Cookies" nc:link="using_priv_help.xhtml#cookies"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_images" nc:name="Images" nc:link="using_priv_help.xhtml#images"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_popup_windows" nc:name="Popup Windows" nc:link="cs_priv_prefs_popup.xhtml#privacy_and_security_preferences_popup_windows"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_passwords" nc:name="Passwords" nc:link="passwords_help.xhtml#passwords"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_master_passwords" nc:name="Master Passwords" nc:link="passwords_help.xhtml#master_passwords"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_ssltls" nc:name="SSL/TLS" nc:link="ssl_help.xhtml#privacy_and_security_preferences_ssltls"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_gen_certificates" nc:name="Certificates" nc:link="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<!-- USING CERTIFICATES CONTENT -->
+<rdf:Description about="#using-help-certs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-help-certs-get" nc:name="Getting Your Own Certificate" nc:link="using_certs_help.xhtml#getting_your_own_certificate"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-info" nc:name="Checking Security For a Web Page" nc:link="using_certs_help.xhtml#checking_security_for_a_web_page"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-manage" nc:name="Managing Certificates" nc:link="using_certs_help.xhtml#managing_certificates"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-devices" nc:name="Managing Smart Cards and Other Security Devices" nc:link="using_certs_help.xhtml#managing_smart_cards_and_other_security_devices"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-ssltls" nc:name="Managing SSL/TLS Warnings and Settings" nc:link="using_certs_help.xhtml#managing_ssltls_warnings_and_settings"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-validation" nc:name="Controlling Validation" nc:link="using_certs_help.xhtml#controlling_validation"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-context-help" nc:name="Certificate Settings" nc:link="certs_prefs_help.xhtml#certificate_settings"/> </rdf:li>
+
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<rdf:Description about="#using-help-certs-manage">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-help-certs-manage-my" nc:name="Certificates That Identify You" nc:link="using_certs_help.xhtml#managing_certificates_that_identify_you"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-manage-others" nc:name="Certificates That Identify People" nc:link="using_certs_help.xhtml#managing_certificates_that_identify_people"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-manage-sites" nc:name="Certificates That Identify Servers" nc:link="using_certs_help.xhtml#managing_certificates_that_identify_servers"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-manage-cas" nc:name="Certificates That Identify CAs" nc:link="using_certs_help.xhtml#managing_certificates_that_identify_certificate_authorities"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-manage-orphans" nc:name="Certificates That Identify Others" nc:link="using_certs_help.xhtml#managing_certificates_that_identify_others"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-help-certs-devices">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-help-certs-devices-about" nc:name="About Security Devices and Modules" nc:link="using_certs_help.xhtml#about_security_devices_and_modules"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-devices-devices" nc:name="Using Security Devices" nc:link="using_certs_help.xhtml#using_security_devices"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-devices-modules" nc:name="Using Security Modules" nc:link="using_certs_help.xhtml#using_security_modules"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-devices-fips" nc:name="Enabling FIPS Mode" nc:link="using_certs_help.xhtml#enable_fips_mode"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-help-certs-ssltls">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="ssltls-settings" nc:name="SSL/TLS Settings" nc:link="ssl_help.xhtml"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#ssltls-settings">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="ssl_prefs" nc:name="SSL/TLS Preferences" nc:link="ssl_help.xhtml#privacy_and_security_preferences_ssltls"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-help-certs-validation">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="using-help-certs-validation-how" nc:name="How Validation Works" nc:link="using_certs_help.xhtml#how_validation_works"/> </rdf:li>
+ <rdf:li><rdf:Description ID="using-help-certs-validation-ocsp" nc:name="Configuring OCSP" nc:link="using_certs_help.xhtml#configuring_ocsp"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#using-help-certs-context-help">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="certs_prefs" nc:name="Certificate Preferences" nc:link="certs_prefs_help.xhtml#privacy_and_security_preferences_certificates"/> </rdf:li>
+ <rdf:li><rdf:Description ID="certs-help" nc:name="Certificate Manager" nc:link="certs_help.xhtml"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_devices" nc:name="Device Manager" nc:link="certs_help.xhtml#device_manager"/> </rdf:li>
+ <rdf:li><rdf:Description ID="cert-dialog-help" nc:name="Certificate Information and Decisions" nc:link="cert_dialog_help.xhtml"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<!-- YOUR CERTIFICATES CONTENT -->
+
+<rdf:Description about="#certs-help">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="my_certs" nc:name="Your Certificates" nc:link="certs_help.xhtml#your_certificates"/> </rdf:li>
+ <rdf:li><rdf:Description ID="others_certs" nc:name="People" nc:link="certs_help.xhtml#people"/> </rdf:li>
+ <rdf:li><rdf:Description ID="web_certs" nc:name="Servers" nc:link="certs_help.xhtml#servers"/> </rdf:li>
+ <rdf:li><rdf:Description ID="ca_certs" nc:name="Authorities" nc:link="certs_help.xhtml#authorities"/> </rdf:li>
+ <rdf:li><rdf:Description ID="orphan_certs" nc:name="Others" nc:link="certs_help.xhtml#others"/> </rdf:li>
+
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#my_certs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="cert_backup_pwd" nc:name="Choose a Certificate Backup Password" nc:link="certs_help.xhtml#choose_a_certificate_backup_password"/> </rdf:li>
+ <rdf:li><rdf:Description ID="delete_my_certs" nc:name="Delete Your Certificate" nc:link="certs_help.xhtml#delete_your_certificates"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#others_certs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="delete_email_certs" nc:name="Delete Email Certificates" nc:link="certs_help.xhtml#delete_email_certificates"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#web_certs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="edit_web_certs" nc:name="Edit Website Certificate Settings" nc:link="certs_help.xhtml#edit_website_certificate_trust_settings"/> </rdf:li>
+ <rdf:li><rdf:Description ID="delete_web_certs" nc:name="Delete Website Certificate" nc:link="certs_help.xhtml#delete_website_certificates"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#ca_certs">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="edit_ca_certs" nc:name="Edit CA Certificate Settings" nc:link="certs_help.xhtml#edit_ca_certificate_trust_settings"/> </rdf:li>
+ <rdf:li><rdf:Description ID="delete_ca_certs" nc:name="Delete CA Certificate" nc:link="certs_help.xhtml#delete_ca_certificates"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<rdf:Description about="#cert-dialog-help">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="cert_details" nc:name="Certificate Viewer" nc:link="cert_dialog_help.xhtml#certificate_viewer"/> </rdf:li>
+ <rdf:li><rdf:Description ID="which_token" nc:name="Choose Security Device" nc:link="cert_dialog_help.xhtml#choose_security_device"/> </rdf:li>
+ <rdf:li><rdf:Description ID="priv_key_copy" nc:name="Encryption Key Copy" nc:link="cert_dialog_help.xhtml#encryption_key_copy"/> </rdf:li>
+ <rdf:li><rdf:Description ID="backup_your_cert" nc:name="Certificate Backup" nc:link="cert_dialog_help.xhtml#certificate_backup"/> </rdf:li>
+ <rdf:li><rdf:Description ID="which_cert" nc:name="User Identification Request" nc:link="cert_dialog_help.xhtml#user_identification_request"/> </rdf:li>
+ <rdf:li><rdf:Description ID="new_ca" nc:name="New Certificate Authority" nc:link="cert_dialog_help.xhtml#new_certificate_authority"/> </rdf:li>
+ <rdf:li><rdf:Description ID="cert-dialog-help-website" nc:name="Website Certificates" nc:link="cert_dialog_help.xhtml#website_certificates"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cert_details">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="cert-dialog-help-details-general" nc:name="General Tab" nc:link="cert_dialog_help.xhtml#general_tab"/> </rdf:li>
+ <rdf:li><rdf:Description ID="cert-dialog-help-details-details" nc:name="Details Tab" nc:link="cert_dialog_help.xhtml#details_tab"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#cert-dialog-help-website">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="sec_con_failed_page" nc:name="Secure Connection Failed Page" nc:link="cert_dialog_help.xhtml#secure_connection_failed_page"/> </rdf:li>
+ <rdf:li><rdf:Description ID="untrusted_con_page" nc:name="Untrusted Connection Page" nc:link="cert_dialog_help.xhtml#untrusted_connection_page"/> </rdf:li>
+ <rdf:li><rdf:Description ID="sec_con_failed_dialog" nc:name="Secure Connection Failed Dialog" nc:link="cert_dialog_help.xhtml#secure_connection_failed_dialog"/> </rdf:li>
+ <rdf:li><rdf:Description ID="exp_web_cert" nc:name="Certificate Expired" nc:link="cert_dialog_help.xhtml#certificate_expired"/> </rdf:li>
+ <rdf:li><rdf:Description ID="not_yet_web_cert" nc:name="Certificate Not Yet Valid" nc:link="cert_dialog_help.xhtml#certificate_not_yet_valid"/> </rdf:li>
+ <rdf:li><rdf:Description ID="bad_name_web_cert" nc:name="Domain Name Mismatch" nc:link="cert_dialog_help.xhtml#domain_name_mismatch"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+
+<!-- PROFILE HELP CONTENT STARTS-->
+<rdf:Description about="#profile-help">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="profile-help-create" nc:name="Creating a New Profile" nc:link="profiles_help.xhtml#creating_a_new_profile"/> </rdf:li>
+ <rdf:li><rdf:Description ID="profile-help-delete" nc:name="Deleting or Renaming a Profile" nc:link="profiles_help.xhtml#deleting_or_renaming_a_profile"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<!-- TOOLS AND DEVELOPMENT CONTENT STARTS -->
+
+<rdf:Description about="#tools">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="tools-js_console" nc:name="Error Console" nc:link="developer_tools.xhtml#js_console"/> </rdf:li>
+ <rdf:li><rdf:Description ID="tools-devtools" nc:name="Developer Tools" nc:link="developer_tools.xhtml#devtools"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+</rdf:Description>
+
+<!-- KEYBOARD SHORTCUTS CONTENT STARTS -->
+<rdf:Description about="#shortcuts">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="shortcuts_using" nc:name="Using Shortcuts" nc:link="shortcuts.xhtml#using_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_general" nc:name="General &brandShortName; Shortcuts" nc:link="shortcuts.xhtml#general_mozilla_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts-text-field" nc:name="Text Field Shortcuts" nc:link="shortcuts.xhtml#text_field_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_navigator" nc:name="Browser Shortcuts" nc:link="shortcuts_navigator.xhtml"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_mail" nc:name="Mail &amp; Newsgroups Shortcuts" nc:link="shortcuts_mailnews.xhtml"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_composer" nc:name="Composer Shortcuts" nc:link="shortcuts_composer.xhtml"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_help" nc:name="Help Window Shortcuts" nc:link="shortcuts.xhtml#help_window_shortcuts"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#shortcuts_navigator">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="shortcuts_navigator_bookmarks" nc:name="Bookmark Shortcuts" nc:link="shortcuts_navigator.xhtml#bookmarks_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_navigator_page_navigation" nc:name="Page Navigation Shortcuts" nc:link="shortcuts_navigator.xhtml#page_navigation_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_navigator_page_viewing" nc:name="Page Viewing Shortcuts" nc:link="shortcuts_navigator.xhtml#page_viewing_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_navigator_tabbed_browsing" nc:name="Tabbed Browsing Shortcuts" nc:link="shortcuts_navigator.xhtml#tabbed_browsing_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_navigator_sidebar" nc:name="Sidebar Shortcuts" nc:link="shortcuts_navigator.xhtml#sidebar_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_navigator_forms" nc:name="Forms Shortcuts" nc:link="shortcuts_navigator.xhtml#forms_shortcuts"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+<rdf:Description about="#shortcuts_mail">
+ <nc:subheadings>
+ <rdf:Seq>
+ <rdf:li><rdf:Description ID="shortcuts_mail_general" nc:name="General Mail &amp; Newsgroups Shortcuts" nc:link="shortcuts_mailnews.xhtml#general_mail_and_newsgroups_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_mail_message_list" nc:name="Message List Shortcuts" nc:link="shortcuts_mailnews.xhtml#message_list_shortcuts"/> </rdf:li>
+ <rdf:li><rdf:Description ID="shortcuts_mail_message_compose" nc:name="Message Compose Shortcuts" nc:link="shortcuts_mailnews.xhtml#message_compose_shortcuts"/> </rdf:li>
+ </rdf:Seq>
+ </nc:subheadings>
+ </rdf:Description>
+
+</rdf:RDF>
diff --git a/comm/suite/locales/en-US/chrome/common/help/suitehelp.rdf b/comm/suite/locales/en-US/chrome/common/help/suitehelp.rdf
new file mode 100755
index 0000000000..74d077a614
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/suitehelp.rdf
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE window [
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+]>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+
+ <!-- SEAMONKEY MASTER HELP DOCUMENT -->
+ <Description rdf:about="urn:root"
+ nc:title="&brandFullName; Help"
+ nc:defaulttopic="welcome"
+ nc:base="chrome://communicator/locale/help/">
+ <nc:panellist>
+ <Seq>
+ <li>
+ <Description nc:panelid="toc"
+ nc:datasources="suite-toc.rdf"
+ nc:platform="win mac unix"/>
+ </li>
+ <li>
+ <Description nc:panelid="toc"
+ nc:datasources="help-win.rdf"
+ nc:platform="win"/>
+ </li>
+ <li>
+ <Description nc:panelid="search"
+ nc:datasources="rdf:null"
+ nc:platform="win mac unix"
+ nc:emptysearchtext="[No matching items found.]"
+ nc:emptysearchlink="help_help.xhtml#search_tips"/>
+ </li>
+ <li>
+ <Description nc:panelid="glossary"
+ nc:datasources="help-glossary.rdf"
+ nc:platform="win mac unix"/>
+ </li>
+ <li>
+ <Description nc:panelid="index"
+ nc:datasources="help-indexAZ.rdf help-index1.rdf"
+ nc:platform="win mac unix"/>
+ </li>
+ <li>
+ <Description nc:panelid="index"
+ nc:datasources="help-win.rdf"
+ nc:platform="win"/>
+ </li>
+ </Seq>
+ </nc:panellist>
+ </Description>
+</RDF>
diff --git a/comm/suite/locales/en-US/chrome/common/help/using_certs_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/using_certs_help.xhtml
new file mode 100644
index 0000000000..70fc5d0096
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/using_certs_help.xhtml
@@ -0,0 +1,598 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Using Certificates</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="using_certificates">Using Certificates</h1>
+
+<p>A certificate is the digital equivalent of an ID card. Just as you may have
+ several ID cards for different purposes, such as a driver&apos;s license, an
+ employee ID card, or a credit card, you can have several different
+ certificates that identify you for different purposes.</p>
+
+<p>This section describes how to perform operations related to
+ certificates.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#getting_your_own_certificate">Getting Your Own
+ Certificate</a></li>
+ <li><a href="#checking_security_for_a_web_page">Checking Security for a Web
+ Page</a></li>
+ <li><a href="#managing_certificates">Managing Certificates</a></li>
+ <li><a href="#managing_smart_cards_and_other_security_devices">Managing
+ Smart Cards and Other Security Devices</a></li>
+ <li><a href="#managing_ssltls_warnings_and_settings">Managing SSL/TLS
+ Warnings and Settings</a></li>
+ <li><a href="#controlling_validation">Controlling Validation</a></li>
+ </ul>
+</div>
+
+<h1 id="getting_your_own_certificate">Getting Your Own Certificate</h1>
+
+<p>Much like a credit card or a driver&apos;s license, a certificate is a form
+ of identification you can use to identify yourself over the Internet and
+ other networks. Like other commonly used personal IDs, a certificate is
+ typically issued by an organization with recognized authority to issue such
+ identification. An organization that issues certificates is called a
+ <strong>certificate authority (CA)</strong>.</p>
+
+<p>You can obtain certificates that identify you from public CAs, from system
+ administrators or special CAs within your organization, or from websites
+ offering specialized services that require a means of identification more
+ reliable that your name and password.</p>
+
+<p>Just as the requirements for a driver&apos;s license vary depending on the
+ type of vehicle you want to drive, the requirements for obtaining a
+ certificate vary depending on what you want to use it for. In some cases
+ getting a certificate may be as easy as going to a website, entering some
+ personal information, and automatically downloading the certificate into your
+ browser. In other cases you may have to go through more complicated
+ procedures.</p>
+
+<p>You can obtain a certificate today by visiting the URL for a certificate
+ authority and following the on-screen instructions. For a list of certificate
+ authorities issuing certificates recognized by &brandShortName;, see the
+ online document
+ <a href="http://www.mozilla.org/projects/security/certs/included/">Included
+ Certificate List</a>.</p>
+
+<p>Once you obtain a certificate, it is automatically stored in a
+ <a href="glossary.xhtml#security_device">security device</a>. Your browser
+ comes with its own built-in Software Security Device. A security device can
+ also be a piece of hardware, such as a smart card.</p>
+
+<p>Like a driver&apos;s license or a credit card, a certificate is a valuable
+ form of identification that can be abused if it falls into the wrong hands.
+ Once you&apos;ve obtained a certificate that identifies you, you should
+ protect it in two ways: by backing it up and by setting your
+ <a href="glossary.xhtml#master_password">master password</a>.</p>
+
+<p>When you first obtain a certificate, you may be prompted to back it up. If
+ you haven&apos;t yet created a master password, you will be asked to create
+ one.</p>
+
+<p>For detailed information about backing up a certificate and setting your
+ master password, see <a href="certs_help.xhtml#your_certificates">Your
+ Certificates</a>.</p>
+
+<p>[<a href="#using_certificates">Return to beginning of section</a>]</p>
+
+<h1 id="checking_security_for_a_web_page">Checking Security for a Web Page</h1>
+
+<p>When you&apos;re viewing any web page, the lock icon near the lower-right
+ corner of the window informs you whether the entire contents of the page was
+ protected by <a href="glossary.xhtml#encryption">encryption</a> while it was
+ being received by your computer:</p>
+
+<table summary="lock icons">
+ <tr>
+ <td><img alt="closed lock icon"
+ src="chrome://communicator/skin/icons/lock-secure.png"/></td>
+ <td>A closed lock means that the page was protected by encryption when it
+ was received.</td>
+ </tr>
+ <tr>
+ <td><img alt="open lock icon"
+ src="chrome://communicator/skin/icons/lock-insecure.png"/></td>
+ <td>An open lock means the page was not protected by encryption when it was
+ received.</td>
+ </tr>
+ <tr>
+ <td><img alt="broken lock icon"
+ src="chrome://communicator/skin/icons/lock-broken.png"/></td>
+ <td>A broken lock means that some or all of the elements within the page
+ were not protected by encryption when the page was received, even though
+ the outermost HTML page was encrypted.</td>
+ </tr>
+</table>
+
+<p>For more details about the encryption status of the page when it was
+ received, click the lock icon (or open the View menu, choose Page Info, and
+ click the Security tab).</p>
+
+<p>The Security tab for Page Info provides two kinds of information:</p>
+
+<ul>
+ <li>The top half describes whether the website displaying the page has been
+ verified. (For information on certificate verification, see
+ <a href="#controlling_validation">Controlling Validation</a>.)</li>
+ <li>The bottom half describes whether the contents of the page you are
+ viewing is protected by encryption while in transit over the network.</li>
+</ul>
+
+<p><strong>Important</strong>: The lock icon describes only the encryption
+ status of the page while it was being received by your computer. To be
+ notified when you send or receive information without encryption, or to
+ block potentially harmful mixed content, select the appropriate SSL/TLS
+ warning and mixed content options. See <a href="ssl_help.xhtml">Privacy &amp;
+ Security Preferences - SSL/TLS</a> for details.</p>
+
+<p>[<a href="#using_certificates">Return to beginning of section</a>]</p>
+
+<h1 id="managing_certificates">Managing Certificates</h1>
+
+<p>You can use the Certificate Manager to manage the certificates you have
+ available. Certificates may be stored on your computer&apos;s hard disk or on
+ <a href="glossary.xhtml#smart_card">smart cards</a> or other security devices
+ attached to your computer.</p>
+
+<p>To open the Certificate Manager:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Manage Certificates section, click Manage Certificates. You see
+ the Certificate Manager.</li>
+</ol>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#managing_certificates_that_identify_you">Managing
+ Certificates that Identify You</a></li>
+ <li><a href="#managing_certificates_that_identify_people">Managing
+ Certificates that Identify People</a></li>
+ <li><a href="#managing_certificates_that_identify_servers">Managing
+ Certificates that Identify Servers</a></li>
+ <li><a href="#managing_certificates_that_identify_certificate_authorities">Managing
+ Certificates that Identify Certificate Authorities</a></li>
+ <li><a href="#managing_certificates_that_identify_others">Managing
+ Certificates that Identify Others</a></li>
+ </ul>
+</div>
+
+<h2 id="managing_certificates_that_identify_you">Managing Certificates that
+ Identify You</h2>
+
+<p>When you first open the Certificate Manager, you&apos;ll notice that it has
+ several tabs across the top of its window. The first tab is called Your
+ Certificates, and it displays the certificates your browser or mail client
+ has available that identify you. Your certificates are listed under the names
+ of the organizations that issued them.</p>
+
+<p>To perform an action on one or more certificates, click the entry for the
+ certificate (or <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>-click
+ to select more than one), then click one of the buttons at the bottom of the
+ Certificate Manager window. Each of these buttons brings up another window
+ that allows you to perform the action. Click the Help button in any window to
+ obtain more information about using that window.</p>
+
+<p>For more details on how to view and manage these certificates, see
+ <a href="certs_help.xhtml#your_certificates">Your Certificates</a>.</p>
+
+<p>[<a href="#managing_certificates">Return to beginning of section</a>]</p>
+
+<h2 id="managing_certificates_that_identify_people">Managing Certificates that
+ Identify People</h2>
+
+<p>When you compose a mail message, you can choose to attach your digital
+ signature to it. A <a href="glossary.xhtml#digital_signature">digital
+ signature</a> allows recipients of the message to verify that the message
+ really comes from you and hasn&apos;t been tampered with since you sent
+ it.</p>
+
+<p>Every time you send a digitally signed message, your encryption certificate
+ is automatically included with the message. This certificate allows the
+ message recipients to send you encrypted messages.</p>
+
+<p>One of the easiest ways to obtain someone else&apos;s encryption certificate
+ is for that person to send you a digitally signed message. Certificate
+ Manager automatically stores other people&apos;s certificates whenever they
+ are received in this way.</p>
+
+<p>To view all the certificates identifying other people that are available to
+ the Certificate Manager, click the People tab at the top of the
+ Certificate Manager window. You can send encrypted messages to anyone for
+ whom a valid certificate is listed. Certificates are listed under the names
+ of the organizations that issued them.</p>
+
+<p>To perform an action on one or more certificates, click the entry for the
+ certificate (or <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>-click
+ to select more than one), then click one of the buttons at the bottom of the
+ Certificate Manager window. Each of these buttons brings up another window
+ that allows you to perform the action. Click the Help button in any window to
+ obtain more information about using that window.</p>
+
+<p>For more details on how to view and manage these certificates, see the
+ description of the Certificate Manager&apos;s
+ <a href="certs_help.xhtml#people">People</a> tab.</p>
+
+<p>[<a href="#managing_certificates">Return to beginning of section</a>]</p>
+
+<h2 id="managing_certificates_that_identify_servers">Managing Certificates
+ that Identify Servers</h2>
+
+<p>Some websites and mail servers use certificates to identify themselves.
+ Such identification is required before the server can encrypt information
+ transferred between it and your computer (or vice versa), so that no one
+ can read the data while in transit.</p>
+
+<p>If the URL for a website begins with <tt>https://</tt>, the website has a
+ certificate. If you visit such a website and its certificate was issued by a
+ CA that the Certificate Manager doesn&apos;t know about or doesn&apos;t
+ trust, you will be asked whether you want to accept the website&apos;s
+ certificate. When you accept a new website certificate, the Certificate
+ Manager adds it to its list of website certificates.</p>
+
+<p>To view all the website certificates available to your browser, click the
+ Servers tab at the top of the Certificate Manager window.</p>
+
+<p>To perform an action on one or more certificates, click the entry for the
+ certificate (or <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>-click
+ to select more than one), then click one of the buttons at the bottom of the
+ Certificate Manager window. Each of these buttons brings up another window
+ that allows you to perform the action. Click the Help button in any window to
+ obtain more information about using that window.</p>
+
+<p>For more details on how to view and manage these certificates, see the
+ description of the Certificate Manager&apos;s
+ <a href="certs_help.xhtml#servers">Servers</a> tab.</p>
+
+<p>[<a href="#managing_certificates">Return to beginning of section</a>]</p>
+
+<h2 id="managing_certificates_that_identify_certificate_authorities">Managing
+ Certificates that Identify Certificate Authorities</h2>
+
+<p>Like other commonly used forms of ID, a certificate is issued by an
+ organization with recognized authority to issue such identification. An
+ organization that issues certificates is called a
+ <a href="glossary.xhtml#certificate_authority">certificate authority
+ (CA)</a>. A certificate that identifies a CA is called a CA certificate.</p>
+
+<p>Certificate Manager typically has many CA certificates on file. These CA
+ certificates permit Certificate Manager to recognize and work with
+ certificates issued by the corresponding CAs. However, the presence of a CA
+ certificate in this list does <em>not</em> guarantee that the certificates it
+ issues can be trusted. You or your system administrator must make decisions
+ about what kinds of certificates to trust depending on your security
+ needs.</p>
+
+<p>To view all the CA certificates available to your browser, click the
+ Authorities tab at the top of the Certificate Manager window.</p>
+
+<p>To perform an action on one or more CA certificates, click the entry for the
+ certificate (or <kbd class="mac">Cmd</kbd><kbd class="noMac">Ctrl</kbd>-click
+ to select more than one), then click one of the buttons at the bottom of the
+ Certificate Manager window. Each of these buttons brings up another window
+ that allows you to perform the action. Click the Help button in any window to
+ obtain more information about using that window.</p>
+
+<p>For more details on how to view and manage these certificates, see the
+ description of the Certificate Manager&apos;s
+ <a href="certs_help.xhtml#authorities">Authorities</a> tab.</p>
+
+<p>[<a href="#managing_certificates">Return to beginning of section</a>]</p>
+
+<h2 id="managing_certificates_that_identify_others">Managing Certificates that
+ Identify Others</h2>
+
+<p>To see all certificates that do not fit into any of the other categories,
+ click the Others tab at the top of the Certificate Manager window.</p>
+
+<p>For more details on how to view and manage these certificates, see the
+ description of the Certificate Manager&apos;s
+ <a href="certs_help.xhtml#others">Others</a> tab.</p>
+
+<p>[<a href="#managing_certificates">Return to beginning of section</a>]</p>
+
+<h1 id="managing_smart_cards_and_other_security_devices">Managing Smart Cards
+ and Other Security Devices</h1>
+
+<p>A smart card is a small device, typically about the size of a credit card,
+ that contains a microprocessor and is capable of storing information about
+ your identity (such as your <a href="glossary.xhtml#private_key">private
+ keys</a> and <a href="glossary.xhtml#certificate">certificates</a>) and
+ performing cryptographic operations.</p>
+
+<p>To use a smart card, you typically need to have a smart card reader (a piece
+ of hardware) attached to your computer, as well as software on your computer
+ that controls the reader.</p>
+
+<p>A smart card is just one kind of security device. A security device
+ (sometimes called a token) is a hardware or software device that provides
+ cryptographic services and stores information about your identity. Use the
+ Device Manager to work with smart cards and other security devices.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#about_security_devices_and_modules">About Security Devices
+ and Modules</a></li>
+ <li><a href="#using_security_devices">Using Security Devices</a></li>
+ <li><a href="#using_security_modules">Using Security Modules</a></li>
+ <li><a href="#enable_fips_mode">Enable FIPS Mode</a></li>
+ </ul>
+</div>
+
+<h2 id="about_security_devices_and_modules">About Security Devices and
+ Modules</h2>
+
+<p>The Device Manager displays a window that lists the available security
+ devices. You can use the Device Manager to manage any security devices,
+ including smart cards, that support the Public Key Cryptography Standard
+ (PKCS) #11.</p>
+
+<p>A <a href="glossary.xhtml#pkcs_11_module">PKCS #11 module</a> (sometimes
+ called a security module) controls one or more security devices in much the
+ same way that a software driver controls an external device such as a printer
+ or modem. If you are installing a smart card, you must install the PKCS #11
+ module for the smart card on your computer as well as connecting the smart
+ card reader.</p>
+
+<p>By default, the Device Manager controls two internal PKCS #11 modules that
+ manage three security devices:</p>
+
+<ul>
+ <li><strong>&brandShortName; Internal PKCS #11 Module</strong>: Controls two
+ security devices:
+ <ul>
+ <li><strong>Generic Crypto Services</strong>: A special security device
+ that performs all cryptographic operations required by the
+ &brandShortName; Internal PKCS #11 Module.</li>
+ <li><strong>Software Security Device</strong>: Stores your certificates
+ and keys that aren&apos;t stored on external security devices,
+ including any CA certificates that you may have installed in addition
+ to those that come with the browser.</li>
+ </ul>
+ </li>
+ <li><strong>Builtin Roots Module</strong>: Controls a special security device
+ called the Builtin Object Token. This security device stores the default
+ <a href="glossary.xhtml#ca_certificate">CA certificates</a> that come with
+ the browser.</li>
+</ul>
+
+<p>[<a href="#managing_smart_cards_and_other_security_devices">Return to
+ beginning of section</a>]</p>
+
+<h2 id="using_security_devices">Using Security Devices</h2>
+
+<p>The Device Manager allows you to perform operations on security devices. To
+ open the Device Manager, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Certificates panel, click Manage Security Devices.</li>
+</ol>
+
+<p>The Device Manager lists each available PKCS #11 module in boldface, and the
+ security devices managed by each module below its name.</p>
+
+<p>When you select a security device, information about it appears in the
+ middle of the Device Manager window, and some of the buttons on the right
+ side of the window become available. For example, if you select the Software
+ Security Device, you can perform these actions:</p>
+
+<ul>
+ <li>Click Login or Logout to log in or out of the Software Security Device.
+ If you are logging in, you will be asked to supply the master password for
+ the device. You must be logged into a security device before your browser
+ software can use it to provide cryptographic services.</li>
+ <li>Click Change Password to change the master password for the device.</li>
+</ul>
+
+<p>You can perform these actions on most security devices. However, you cannot
+ perform them on the Builtin Object Token or Generic Crypto Services, which
+ are special devices that must normally be available at all times.</p>
+
+<p>For more details, see <a href="certs_help.xhtml#device_manager">Device
+ Manager</a>.</p>
+
+<p>[<a href="#managing_smart_cards_and_other_security_devices">Return to
+ beginning of section</a>]</p>
+
+<h2 id="using_security_modules">Using Security Modules</h2>
+
+<p>If you want to use a smart card or other external security device, you must
+ first install the module software on your computer and, if necessary, connect
+ any associated hardware. Follow the instructions that come with the
+ hardware.</p>
+
+<p>After a new module is installed on your computer, follow these steps to load
+ it:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Certificates panel, click Manage Security Devices.</li>
+ <li>Click Load.</li>
+ <li>In the Load PKCS #11 Module dialog box, click the Browse button, locate
+ the module file, and click Open.</li>
+ <li>Fill in the Module Name field with the name of the module and click
+ OK.</li>
+</ol>
+
+<p>The new module will then show up in the list of modules with the name you
+ assigned to it.</p>
+
+<p>To unload a PKCS #11 module, select its name and click Unload.</p>
+
+<p>[<a href="#managing_smart_cards_and_other_security_devices">Return to
+ beginning of section</a>]</p>
+
+<h2 id="enable_fips_mode">Enable FIPS Mode</h2>
+
+<p>Federal Information Processing Standards Publications (FIPS PUBS) 140-1 is a
+ US government standard for implementations of cryptographic
+ modules&mdash;that is, hardware or software that encrypts and decrypts data
+ or performs other cryptographic operations (such as creating or verifying
+ digital signatures). Many products sold to the US government must comply with
+ one or more of the FIPS standards.</p>
+
+<p>To enable FIPS mode for the browser, you use the Device Manager:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Certificates panel, click Manage Devices.</li>
+ <li>Click the Enable FIPS button. When FIPS is enabled, the name NSS Internal
+ PKCS #11 Module changes to NSS Internal FIPS PKCS #11 Module and the Enable
+ FIPS button changes to Disable FIPS.</li>
+</ol>
+
+<p>To disable FIPS-mode, click Disable FIPS.</p>
+
+<p>[<a href="#managing_smart_cards_and_other_security_devices">Return to
+ beginning of section</a>]</p>
+
+<h1 id="managing_ssltls_warnings_and_settings">Managing SSL/TLS Warnings and
+ Settings</h1>
+
+<p>The Secure Sockets Layer (SSL) protocol allows your computer to exchange
+ information with other computers on the Internet in encrypted form&mdash;that
+ is, the information is scrambled while in transit so that no one else can
+ make sense of it. SSL is also used to identify computers on the Internet by
+ means of <a href="glossary.xhtml#certificate">certificates</a>.</p>
+
+<p>The Transport Layer Security (TLS) protocol is a new standard based on SSL.
+ The old SSL versions have been deprecated for security reasons and TLS is the
+ only supported protocol. The default set of enabled TLS versions works for
+ most people with current servers. However, in some circumstances system
+ administrators or other knowledgeable persons may wish to adjust the SSL/TLS
+ settings to fine-tune them for special security needs or to account for
+ limited capabilities of some legacy servers.</p>
+
+<p>You shouldn&apos;t adjust the SSL/TLS settings for your browser unless you
+ know what you&apos;re doing or have the assistance of someone else who does.
+ If you do need to adjust them for some reason, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, select SSL/TLS. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<p>For more details, see <a href="ssl_help.xhtml">SSL/TLS Settings</a>.</p>
+
+<p>[<a href="#using_certificates">Return to beginning of section</a>]</p>
+
+<h1 id="controlling_validation">Controlling Validation</h1>
+
+<p>As discussed above under <a href="#getting_your_own_certificate">Get Your
+ Own Certificate</a>, a certificate is a form of identification, much like a
+ driver&apos;s license, that you can use to identify yourself over the
+ Internet and other networks. However, also like a driver&apos;s license, a
+ certificate may expire or become invalid for some other reason. Therefore,
+ your browser software needs to confirm the validity of any given certificate
+ in some way before trusting it for identification purposes.</p>
+
+<p>This section describes how Certificate Manager validates certificates and
+ how to control that process. To understand the process, you should have some
+ familiarity with <a href="glossary.xhtml#public-key_cryptography">public-key
+ cryptography</a>. If you are not familiar with the use of certificates, you
+ should check with your system administrator before attempting to change any
+ of your browser&apos;s certificate validation settings.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#how_validation_works">How Validation Works</a></li>
+ <li><a href="#configuring_ocsp">Configuring OCSP</a></li>
+ </ul>
+</div>
+
+<h2 id="how_validation_works">How Validation Works</h2>
+
+<p>Whenever you use or view a certificate stored by Certificate Manager, it
+ takes several steps to verify the certificate. At a minimum, it confirms that
+ the CA&apos;s digital signature on the certificate was created by a CA whose
+ own certificate is (1) present in the Certificate Manager&apos;s list of
+ available CA certificates and (2) marked as trusted for issuing the kind of
+ certificate being verified.</p>
+
+<p>If the CA certificate is not itself present, the
+ <a href="glossary.xhtml#certificate_chain">certificate chain</a> for the CA
+ certificate must include a higher-level CA certificate that is present and
+ correctly trusted. Certificate Manager also confirms that the certificate
+ being verified is currently marked as trusted in the certificate store. If
+ any one of these checks fails, Certificate Manager marks the certificate as
+ unverified and won&apos;t recognize the identity it certifies.</p>
+
+<p>A certificate can pass all these tests and still be compromised in some way;
+ for example, the certificate may be revoked because an unauthorized person
+ has gained access to the certificate&apos;s private key. A compromised
+ certificate can allow an unauthorized person (or website) to pretend to be
+ the certificate owner.</p>
+
+<p>One way to combat this threat would be for Certificate Manager to check a
+ previously downloaded certificate revocation list (CRL) as part of the
+ verification process. However, those lists may be large and need to be
+ updated frequently in order to remain current and thus useful.</p>
+
+<p>The preferred way to combat the threat of compromised certificates is to use
+ a special server that supports the Online Certificate Status Protocol (OCSP).
+ Such a server can answer client queries about individual certificates (see
+ <a href="#configuring_ocsp">Configuring OCSP</a>, below).</p>
+
+<p>The server, called an OCSP responder, receives an updated CRL periodically
+ from the CA that issues the certificates to be verified. You can configure
+ Certificate Manager to submit a status request for a certificate to the OCSP
+ responder, and the OCSP responder confirms whether the certificate is
+ valid.</p>
+
+<p>[<a href="#controlling_validation">Return to beginning of section</a>]</p>
+
+<h2 id="configuring_ocsp">Configuring OCSP</h2>
+
+<p>The settings that control OCSP are part of Certificates preferences. To view
+ Certificates preferences, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Certificates. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<p>For information about the OCSP options available, see
+ <a href="certs_prefs_help.xhtml#ocsp">Privacy &amp; Security Preferences -
+ Certificates, OCSP</a>.</p>
+
+<p>[<a href="#controlling_validation">Return to beginning of section</a>]</p>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/using_priv_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/using_priv_help.xhtml
new file mode 100644
index 0000000000..851b62565e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/using_priv_help.xhtml
@@ -0,0 +1,1134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Using Privacy Features</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1 id="using_the_cookie_manager">Using the Cookie Manager</h1>
+
+<p>A cookie is a small amount of information on your computer that is used by
+ some websites. For a brief overview, see
+ <a href="privacy_help.xhtml#what_are_cookies_and_how_do_they_work">What Are
+ Cookies and How Do They Work?</a></p>
+
+<p>Before loading a web page that uses cookies, your browser handles the
+ page&apos;s cookies by doing two things:</p>
+
+<ul>
+ <li>Accepts or rejects any requests by the website to <strong>set</strong>
+ (store) one or more cookies on your computer.</li>
+ <li>Accepts or rejects any requests by the website to <strong>read</strong>
+ cookies it previously stored on your computer. A website can&apos;t
+ actually read cookies or any other data on your computer&mdash;instead,
+ your browser gets the cookies and sends them back to the website.</li>
+</ul>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#enabling_and_disabling_cookies">Enabling &amp; Disabling
+ Cookies</a></li>
+ <li><a href="#managing_cookies_site-by-site">Managing Cookies
+ Website-By-Website</a></li>
+ <li><a href="#viewing_cookies">Viewing Cookies</a></li>
+ <li><a href="#removing_cookies">Removing Cookies</a></li>
+ <li><a href="#cookie_manager_settings">Cookie Manager Settings</a></li>
+ </ul>
+</div>
+
+<h2 id="enabling_and_disabling_cookies">Enabling &amp; Disabling Cookies</h2>
+
+<p>You can specify how cookies should be handled by setting your Cookies
+ preferences. To change your Cookies preferences:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Cookies. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Set your Cookies preferences.</li>
+</ol>
+
+<p>For more information about the effect of each setting, see
+ <a href="#cookies">Privacy &amp; Security Preferences - Cookies</a>.</p>
+
+<p>[<a href="#using_the_cookie_manager">Return to beginning of
+ section</a>]</p>
+
+<h2 id="managing_cookies_site-by-site">Managing Cookies Website-By-Website</h2>
+
+<p>To set cookie permissions for the current website:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Cookie Manager.</li>
+ <li>Choose one of the following items:
+ <ul>
+ <li><strong>Block Cookies from this Website</strong>: Block the
+ current website from setting cookies.</li>
+ <li><strong>Use Default Cookie Permissions</strong>: Reset
+ cookie permission for the current website and use the
+ <a href="#cookie_manager_settings">default settings</a>.</li>
+ <li><strong>Allow Session Cookies from this Website</strong>: Allow
+ the current website to set session cookies. Persistent cookies from
+ this website will be downgraded to session cookies.</li>
+ <li><strong>Allow Cookies from this Website</strong>: Allow the
+ current website to set cookies.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>To <a href="#add_cookie_sites">set cookie permission</a> for several websites
+ or a website you are not viewing, use the Cookie Manager.</p>
+
+<p>Other dialog options:</p>
+
+<ul>
+ <li><strong>Use my choice for all cookies from this website</strong>:
+ If you check this option, you will not be warned the next time
+ that website tries to set or modify a cookie, and your <q>allow</q> or
+ <q>deny</q> response will still be in effect.</li>
+ <li><strong>Show Details</strong>/<strong>Hide Details</strong>:
+ Click the button to show or hide <a href="#viewing_cookies">detailed
+ information</a> of the cookie.</li>
+</ul>
+
+<p>If you want to change a remembered response later, use the Cookie Manager
+ to edit <a href="#stored_cookies">stored cookies</a> and
+ <a href="#cookie_sites">add or remove cookie websites</a>.</p>
+
+<p>To stop automatically accepting or rejecting cookies from a website:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Cookie Manager.</li>
+ <li>Choose Manage Stored Cookies from the submenu. The Cookie Manager window
+ opens with a list of all the cookies stored on your computer.</li>
+ <li>Click the Cookie Websites tab. The sites for which you have allowed or
+ denied cookies are listed.</li>
+ <li>Click to select the website from which you no longer want to automatically
+ accept cookies, and then click Remove Cookie.</li>
+</ol>
+
+<p>[<a href="#using_the_cookie_manager">Return to beginning of section</a>]</p>
+
+<h2 id="viewing_cookies">Viewing Cookies</h2>
+
+<p>To view detailed information about cookies:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Cookie Manager.</li>
+ <li>Choose Manage Stored Cookies from the submenu. The Cookie Manager window
+ opens with a list of all the cookies stored on your computer.</li>
+ <li>Select a cookie to see its details.</li>
+</ol>
+
+<p>For more information about the information displayed, see
+ <a href="#stored_cookies">Stored Cookies</a>.</p>
+
+<p>[<a href="#using_the_cookie_manager">Return to beginning of section</a>]</p>
+
+<h2 id="removing_cookies">Removing Cookies</h2>
+
+<p><strong>Important</strong>: To remove cookies, follow the steps in this
+ section. Do not try to edit the cookies file on your computer.</p>
+
+<p>To remove one or more cookies from your computer:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Cookie Manager.</li>
+ <li>Choose Manage Stored Cookies from the submenu. The Cookie Manager window
+ opens with a list of all the cookies stored on your computer.</li>
+ <li>Select one or more cookies and click Remove Cookie, or click Remove All
+ Cookies.</li>
+</ol>
+
+<p>Even though you&apos;ve removed the cookies now, you will reacquire those
+ same cookies the next time you return to the website.</p>
+
+<p>To prevent that from happening, select the checkbox labeled <q>Don&apos;t
+ allow websites that set removed cookies to set future cookies</q>. When this
+ checkbox is selected, websites for the cookies that you are removing are
+ added to the list of websites whose cookies will automatically be rejected.</p>
+
+<p>You must click OK for your changes to take effect.</p>
+
+<p>[<a href="#using_the_cookie_manager">Return to beginning of section</a>]</p>
+
+<h1 id="cookie_manager_settings">Cookie Manager Settings</h1>
+
+<p>This section describes how to set your Cookies preferences and control other
+ aspects of cookie handling.</p>
+
+<p>For step-by-step descriptions of various tasks related to cookies, see
+ <a href="#using_the_cookie_manager">Using the Cookie Manager</a>.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#cookies">Privacy &amp; Security Preferences -
+ Cookies</a></li>
+ <li><a href="#stored_cookies">Stored Cookies</a></li>
+ <li><a href="#cookie_sites">Cookie Websites</a></li>
+ </ul>
+</div>
+
+<h2 id="cookies">Privacy &amp; Security Preferences - Cookies</h2>
+
+<p>This section describes how to use the Cookies preferences panel to change
+ which cookies &brandShortName; will accept from and return to websites. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Cookies. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<p>Cookies help websites keep track of information for you, such as the
+ contents of your on-line shopping cart or which cities&apos; weather you want
+ to know about. For a brief overview, see
+ <a href="privacy_help.xhtml#what_are_cookies_and_how_do_they_work">What Are
+ Cookies and How Do They Work?</a></p>
+
+<p>You can select one of these options:</p>
+
+<ul>
+ <li><strong>Block cookies</strong>: Select this option to refuse all
+ cookies from websites not explicitly allowed to set cookies.</li>
+ <li><strong>Allow cookies for the originating website only</strong>: Select
+ this option if you don&apos;t want to accept or return
+ <a href="privacy_help.xhtml#what_are_third-party_cookies">third-party
+ (foreign) cookies</a> for any websites other than the one you are actively
+ visiting.</li>
+ <li><strong>Allow third-party cookies for previously visited websites
+ only</strong>: Select this option if you want to accept or return
+ <a href="privacy_help.xhtml#what_are_third-party_cookies">third-party
+ cookies</a> only for websites that stored cookies when you explicitly
+ visited them previously.</li>
+ <li><strong>Allow all cookies</strong>: Select this option to permit all
+ websites not explicitly blocked to set cookies on your computer.</li>
+</ul>
+
+<p><strong>Note</strong>: Blocking cookies does not remove old cookies. By
+ blocking cookies you only block websites from setting new cookies, and old
+ cookies will still be sent to websites. To completely block a website from
+ receiving old cookies, you need to <a href="#removing_cookies">remove its
+ cookies</a>.
+</p>
+
+<p><strong>Note</strong>: <a href="#cookie_sites">Per-website cookie permission</a>
+ supersedes default cookie setting. For example, if you allow a website to set
+ cookies, the website can set cookies even if you choose <q>Block cookies</q>.
+</p>
+
+<p>If you allow cookies or do not change the default setting, you can also
+ select the following preferences:</p>
+
+<ul>
+ <li><strong>Accept cookies normally</strong>: Select this
+ if you want websites to set or modify cookies without restrictions.</li>
+ <li><strong>Accept for current session only</strong>: Select this to delete
+ the cookie the next time you exit your browser.</li>
+ <li><strong>Accept cookies for [__] days</strong>: Select this if you
+ want to limit the length of time any cookie can remain on your computer,
+ then type the number of days.</li>
+</ul>
+
+<p>You can also get more information about your stored cookies:</p>
+
+<ul>
+ <li><strong>Cookie Manager</strong>: Click this button to view
+ information about the cookies currently stored on your computer and which
+ websites are allowed to set them.</li>
+</ul>
+
+<h2 id="stored_cookies">Stored Cookies</h2>
+
+<p>This section describes how to use the Stored Cookies tab of the Cookie
+ Manager. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Cookie Manager.</li>
+ <li>Choose Manage Stored Cookies from the submenu. The Cookie Manager window
+ opens with a list of all the cookies stored on your computer.</li>
+</ol>
+
+<p>The Stored Cookies tab lists all the cookies stored on your computer, the
+ websites they belong to, and their current status.</p>
+
+<p>When you select a cookie in this list, the following information about that
+ cookie appears in the bottom portion of the tab:</p>
+
+<table class="defaultTable">
+ <thead>
+ <tr>
+ <th>Item</th>
+ <th>Explanation</th>
+ </tr>
+ </thead>
+ <tbody class="tbody-default">
+ <tr>
+ <td>Name</td>
+ <td>The name assigned to the cookie by its originator.</td>
+ </tr>
+ <tr>
+ <td>Information</td>
+ <td>A string of characters containing the information a website tracks
+ for you. It might contain a user key or name by which you are
+ identified to the website, information about your interests, and so
+ forth.</td>
+ </tr>
+ <tr>
+ <td>Host or domain</td>
+ <td>Provides the name of the cookie&apos;s host or domain.
+
+ <p>A <strong>host</strong> cookie is sent back, during subsequent
+ visits, only to the <a href="glossary.xhtml#server">server</a> that
+ set it.</p>
+
+ <p>A <strong>domain</strong> cookie is sent back to any website
+ that&apos;s in the same domain as the website that set it. A
+ website&apos;s domain is the part of its URL that contains the name of
+ an organization, business, or school&mdash;such as netscape.com or
+ washington.org.</p>
+ </td>
+ </tr>
+ <tr>
+ <td>Path</td>
+ <td>The file pathway. This is provided only if the cookie should be sent
+ back to all URLs that are on that path or lower. For example,
+ <tt>http://a.b/x/y/z.html</tt> means that the cookie can also be set
+ for path <tt>x/</tt>.</td>
+ </tr>
+ <tr>
+ <td>Send For</td>
+ <td>When this field is <q>For encrypted connections only</q> it means
+ that the browser checks the connection whenever the server asks for a
+ cookie and will not send it unless the connection is encrypted
+ (HTTPS).</td>
+ </tr>
+ <tr>
+ <td>Expires</td>
+ <td>The date and time at which the cookie will be deactivated. The
+ browser regularly removes expired cookies from your computer.</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>To remove cookies, click one of these buttons:</p>
+
+<ul>
+ <li><strong>Remove Cookie</strong>: Removes the selected cookie or cookies
+ from the list.</li>
+ <li><strong>Remove All Cookies</strong>: Removes all cookies from the
+ list.</li>
+</ul>
+
+<p>Select this checkbox to prevent the cookies you remove from being added back
+ into the list later:</p>
+
+<ul>
+ <li><strong>Don&apos;t allow websites that set removed cookies to set future
+ cookies</strong></li>
+</ul>
+
+<p>Even if you remove cookies now, you will reacquire those same cookies the
+ next time you return to the website. To prevent that from happening, select
+ this checkbox. When this checkbox is selected, websites for the cookies that
+ you are removing are added to the list of websites whose cookies will
+ automatically be rejected.</p>
+
+<p>You must click Close for your changes to take effect.</p>
+
+<h2 id="cookie_sites">Cookie Websites</h2>
+
+<p>This section describes how to use the Cookie Websites tab of the Cookie
+ Manager. If you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Cookie Manager.</li>
+ <li>Choose Manage Stored Cookies from the submenu. The Cookie Manager window
+ opens with a list of all the cookies stored on your computer.</li>
+ <li>Click the Cookie Websites tab.</li>
+</ol>
+
+<p>The Cookie Websites tab of the Cookie Manager lists the websites for which
+ your decisions have been remembered, and what your decisions were. It also
+ allows you to add and remove websites from the list.</p>
+
+<h3 id="add_cookie_sites">Adding Cookie Websites</h3>
+
+<p>To add cookies websites manually:</p>
+
+<ul>
+ <li>Enter the website address, e.g. <tt>www.mozilla.org</tt></li>
+ <li>Set the website cookie permission:
+ <ul>
+ <li><strong>Block</strong>: Click this button to add the website as a
+ website blocked from setting cookies.</li>
+ <li><strong>Session</strong>: Click this button to add the website as a
+ website that can set session cookies. Persistent cookies from this
+ website will be downgraded to session cookies.</li>
+ <li><strong>Allow</strong>: Click this button to add the website as a
+ website that can set cookies.</li>
+ </ul>
+ </li>
+ <li>Repeat the steps to add additional websites.</li>
+</ul>
+
+<h3 id="remove_cookie_sites">Removing Cookie Websites</h3>
+
+<p>To remove a cookie website:</p>
+
+<ul>
+ <li><strong>Remove Website</strong>: Removes the selected website or websites
+ from the list.</li>
+ <li><strong>Remove All Websites</strong>: Removes all websites from the list.</li>
+</ul>
+
+<p>Once you&apos;ve removed a website from this list, Cookie Manager remembers
+ nothing about it.</p>
+
+<h1 id="using_the_password_manager">Using the Password Manager</h1>
+
+<p>Many websites require you to type a user name and password before you can
+ enter the website. For instance, personalized pages and websites containing
+ your financial information usually require you to log in.</p>
+
+<p>The user name and password you use at a particular website can be read by the
+ site&apos;s administrator. Potentially, that person could then attempt to log
+ into other websites where you may have used the same user name and password.
+ If this concerns you, you may wish to use a different password at every
+ website with which you register.</p>
+
+<p>Password Manager can help you remember some or all of your names and
+ passwords by storing them on your computer&apos;s hard disk, and entering
+ them for you automatically when you visit such websites.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href=
+ "#using_password_manager_to_remember_user_names_and_passwords">Using
+ Password Manager to Remember User Names and Passwords</a></li>
+ <li><a href="#entering_user_names_and_passwords_automatically">Entering
+ User Names and Passwords Automatically</a></li>
+ <li><a href="#turning_password_manager_on_and_off">Turning Password Manager
+ On and Off</a></li>
+ <li><a href="#viewing_and_managing_stored_passwords">Viewing and Managing
+ Stored Passwords</a></li>
+ <li><a href="passwords_help.xhtml">Password Settings</a></li>
+ </ul>
+</div>
+
+<h2 id="using_password_manager_to_remember_user_names_and_passwords">Using
+ Password Manager to Remember User Names and Passwords</h2>
+
+<p>When Password Manager is active (as it is by default), it gives you an
+ opportunity to save user names and passwords on your hard drive that you
+ enter while using the Internet.</p>
+
+<p>For example, after you log onto a website from a page that requests a user
+ name and password, a dialog box appears asking, <q>Do you want Password
+ Manager to remember this logon?</q> When you see this dialog box, you can
+ click one of the following buttons:</p>
+
+<ul>
+ <li><strong>Yes</strong>: The next time you return to the website you&apos;ll
+ see that your user name and password are already filled in. All you have to
+ do is click the Login button (or equivalent) to send them to the
+ server.</li>
+ <li><strong>Never for this site</strong>: Password Manager will not ask in
+ the future if you want to save your user name and password for that
+ website.</li>
+ <li><strong>No</strong>: Password Manager won&apos;t remember the user name
+ and password, but will ask again the next time you visit the website.</li>
+</ul>
+
+<p>Similarly, when you log onto an email account or an FTP site, or perform any
+ other action that requires the browser itself to display a special dialog box
+ for your login information, you can select this option in the dialog box:</p>
+
+<ul>
+ <li><strong>Use Password Manager to remember these values</strong></li>
+</ul>
+
+<p>The next time you check your email or perform other tasks that require a
+ password only, the password will be submitted directly without any further
+ action on your part. For tasks that require you to enter both a user name and
+ password, you need to click a Login button or equivalent after Password
+ Manager fills in the information.</p>
+
+<p>Password Manager saves your user names and passwords on your own computer in
+ a file that&apos;s difficult, but not impossible, for an intruder to read.
+ See <a href="#encrypting_stored_sensitive_information">Encrypting Stored
+ Sensitive Information</a> for information on protecting your stored user
+ names and passwords with encryption technology.</p>
+
+<p>If the Password Manager dialog box described above does not appear when you
+ click Submit after typing your user name and password, Password Manager may
+ be turned off or the website may disallow its use.</p>
+
+<p>To check whether Password Manager is currently active, see
+ <a href="#turning_password_manager_on_and_off">Turning Password Manager On
+ and Off</a>.</p>
+
+<p>[<a href="#using_the_password_manager">Return to beginning of
+ section</a>]</p>
+
+<h2 id="entering_user_names_and_passwords_automatically">Entering User Names
+ and Passwords Automatically</h2>
+
+<p>There are two different ways that Password Manager can fill in user names
+ and passwords on your behalf:</p>
+
+<ul>
+ <li>You use Password Manager to remember your user name and password for a
+ website (using the three-button dialog box described in
+ <a href=
+ "#using_password_manager_to_remember_user_names_and_passwords">Using
+ Password Manager to Remember User Names and Passwords</a>).
+
+ <p>The next time you visit the website, Password Manager automatically fills
+ in your user name and password on the website&apos;s log in page. You can
+ then click the Login button, or equivalent, to send the information to
+ the server.</p>
+ </li>
+ <li>You use Password Manager to remember your user name and password for an
+ email account, an FTP site, or in any other situation where you type login
+ information in a dialog box that displays a checkbox labeled <q>Use
+ Password Manager to remember these values</q>.
+
+ <p>In most cases, the next time you attempt to access that server, Password
+ Manager automatically fills in your user name and password in the same
+ dialog box. You can then click OK to send the information to the
+ server.</p>
+
+ <p>In some cases, such as when you open your email account,
+ &brandShortName; needs to send only the password to the server, and does
+ so immediately without displaying the dialog box or requiring any further
+ action on your part.</p>
+ </li>
+</ul>
+
+<p>[<a href="#using_the_password_manager">Return to beginning of
+ section</a>]</p>
+
+<h2 id="turning_password_manager_on_and_off">Turning Password Manager On and
+ Off</h2>
+
+<p>Password Manager is on by default. To turn it off:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Password Manager section, deselect <q>Remember passwords</q>
+ to turn Password Manager off.</li>
+</ol>
+
+<p>To turn Password Manager on, follow steps 1 and 2 above, but select the
+ checkbox in step 3 rather than deselecting it.</p>
+
+<p>[<a href="#using_the_password_manager">Return to beginning of
+ section</a>]</p>
+
+<h2 id="viewing_and_managing_stored_passwords">Viewing and Managing Stored
+ Passwords</h2>
+
+<p>To see the user names and passwords you have stored and to display a list of
+ websites from which logon information never is saved:</p>
+
+<ul>
+ <li>Open the Tools menu, choose Password Manager, and then choose Manage
+ Stored Passwords from the submenu. You see the Password Manager window with
+ the Passwords Saved tab opened.
+ <ul>
+ <li>To see your saved passwords, click Show Passwords and confirm your
+ choice.</li>
+ <li>To hide your passwords, click Hide Passwords.</li>
+ <li>To remove an entry from the list, click it and then click Remove. The
+ next time you visit the website, you will need to enter your user name
+ and password again, since Password Manager will no longer have the
+ information.</li>
+ </ul>
+ Click the Passwords Never Saved tab to see a list of the websites for which
+ you instructed Password Manager never to store user names and passwords. To
+ remove a website from this list, click it and then click Remove. The next
+ time you log into the website, you can use the stored user name and
+ password (if available) or indicate that you want Password Manager to save
+ the information for that website.</li>
+</ul>
+
+<p>[<a href="#using_the_password_manager">Return to beginning of
+ section</a>]</p>
+
+<h1 id="clearing_private_data">Clearing Private Data</h1>
+
+<p>While browsing the web, various items of potentially private information
+ may be gathered and stored by &brandShortName;. This section describes the
+ types of such private data and options to remove them either manually by
+ request or automatically when shutting down &brandShortName;.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#types_of_private_data">Types of Private Data</a></li>
+ <li><a href="#private_data_prefs">Private Data Preferences</a></li>
+ <li><a href="#clear_private_data_now">Clear Private Data Dialog</a></li>
+ </ul>
+</div>
+
+<h2 id="types_of_private_data">Types of Private Data</h2>
+
+<p>Several types of information are gathered and kept by &brandShortName; while
+ you are browsing websites. Some of these data are necessary for those sites to
+ function properly or more efficiently, others are for your convenience.</p>
+
+<p>Privacy implications vary from type to type. For example, browsing history
+ and cache contents provide a snapshot of your recent browsing activity which
+ is local to your computer; cookies or offline web content may be used by a
+ website to track a user directly across visits (e.g., for statistical
+ purposes or for targeted advertisements).</p>
+
+<p>In <a href="#browsing_in_a_private_window">private browsing mode</a>, no
+ private data will be retained beyond the duration of the private session.</p>
+
+<p>The following private information may be stored locally by
+ &brandShortName;:</p>
+
+<table class="defaultTable">
+ <thead>
+ <tr>
+ <th>Type</th>
+ <th>Explanation</th>
+ </tr>
+ </thead>
+ <tbody class="tbody-default">
+ <tr>
+ <td>Browsing History</td>
+ <td>If enabled in the
+ <a href="cs_nav_prefs_navigator.xhtml#history">History preferences</a>,
+ a history of any website pages you have <em>visited</em> is kept and
+ may be suggested to complete website addresses in the location bar.</td>
+ </tr>
+ <tr>
+ <td>Location Bar History</td>
+ <td>This is a history of web addresses which were <em>entered</em>
+ manually or copy-pasted into the location bar of the browser. This
+ list is available in the location bar menu.</td>
+ </tr>
+ <tr>
+ <td>Download History</td>
+ <td>Depending on the options selected in the
+ <a href="cs_nav_prefs_navigator.xhtml#downloads">Downloads
+ preferences</a>, a history of <em>files downloaded</em> from
+ websites is maintained in the Download Manager.</td>
+ </tr>
+ <tr>
+ <td>Saved Form and Search History</td>
+ <td>If enabled in the
+ <a href="cs_nav_prefs_navigator.xhtml#history">History preferences</a>,
+ text entered into <em>elements of forms</em> (e.g., user names, but not
+ passwords) will be stored for the specified number of days; matches are
+ suggested in a list when you revisit that page. If you put the search
+ box onto your toolbar, the history of <em>search terms</em> will be
+ stored as well.</td>
+ </tr>
+ <tr>
+ <td>Cache</td>
+ <td>The cache is a <em>short-term store</em> for web pages and other
+ data (like e-mail attachments for IMAP accounts or remote images in
+ messages) to avoid having these items being requested again from the
+ server if they were just recently accessed. The cache on your disk
+ may contain data up to the limit specified in the
+ <a href="cs_nav_prefs_advanced.xhtml#cache">Cache preferences</a>.</td>
+ </tr>
+ <tr>
+ <td>Cookies</td>
+ <td>Cookies are <em>small pieces of information</em> that websites use to
+ keep track of users and sessions, or to store website preferences. Use
+ the <a href="using_priv_help.xhtml#cookies">Cookies preferences</a> to
+ specify to what extent cookies are permitted and for how long they are
+ kept.</td>
+ </tr>
+ <tr>
+ <td>Offline Website Data</td>
+ <td>Websites may be permitted to store their pages&apos; contents and
+ related data locally so that they are available for use <em>without
+ a network connection</em>. See the
+ <a href="cs_nav_prefs_advanced.xhtml#offline_apps">Offline Apps
+ preferences</a> for options to control this behavior and to inspect
+ the contents of stored offline website data.</td>
+ </tr>
+ <tr>
+ <td>Saved Passwords</td>
+ <td>If enabled in the
+ <a href="passwords_help.xhtml#passwords">Passwords preferences</a>,
+ &brandShortName; keeps <em>entered passwords</em> for later use, thus
+ you don&apos;t have to retype them every time you visit a website.</td>
+ </tr>
+ <tr>
+ <td>Authenticated Sessions</td>
+ <td>Websites may require <em>authentication</em> (username and password,
+ asked for with a pop-up dialog) and can keep track of such by
+ authenticated sessions. A site will ask you for your credentials
+ again when you proceed to the next page after this information is
+ cleared.</td>
+ </tr>
+ <tr>
+ <td>Site Preferences</td>
+ <td>Site preferences are locally stored preferences for the specific
+ website. These usually are the individual zoom level and the last
+ location of a downloaded file from this site on your local disk.</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>[<a href="#clearing_private_data">Return to beginning of section</a>]</p>
+
+<h2 id="private_data_prefs">Privacy &amp; Security Preferences
+ - Private Data</h2>
+
+<p>This section describes how to use the Private Data preferences panel to
+ determine when and which type of private data should be deleted. If
+ you&apos;re not already viewing it, follow these steps:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Private Data. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<p>The <strong>Clear Private Data</strong> section provides the following
+ options:</p>
+
+<ul>
+ <li><strong>Clear Now</strong>: Click this button to initiate clearing of
+ private data. The Clear Private Data dialog will open where you can revisit
+ your choices.
+ </li>
+ <li><strong>Always clear my private data when I close
+ &brandShortName;</strong>: Check this option to always initiate clearing
+ of the selected private data when the application is shut down.</li>
+ <li><strong>Clear these private data items</strong>: For each
+ <a href="#types_of_private_data">type of private data</a>, a separate
+ option is provided whether or not to clear any stored items of this
+ specific type. Check the respective box for each item to be deleted. The
+ choices are split between manually clearing via dialog and automatically
+ initiated when closing &brandShortName;.</li>
+</ul>
+
+<!-- link up "Data Manager" below once bug 599097 has been taken care of -->
+
+<p><strong>Note</strong>: Also consider more selective alternatives to delete
+ private data. For example, the individual preference panels for each type may
+ provide additional options, and the Data Manager allows to clear private data
+ by type and the specific domain of a website. Rather than clearing all cookies
+ when shutting down &brandShortName;, you could specify to allow cookies for
+ sessions only, thus giving you the opportunity to establish exceptions for
+ selected websites for which you want to retain cookies.</p>
+
+<p>[<a href="#clearing_private_data">Return to beginning of section</a>]</p>
+
+<h2 id="clear_private_data_now">Clear Private Data Dialog</h2>
+
+<p>Private data can be cleared at any time, either from the
+ <a href="#private_data_prefs">Private Data preferences</a> by clicking
+ the Clear Now button, or by selecting Clear Private Data from the Tools
+ menu of a browser window.</p>
+
+ <p>In the dialog window you can confirm and change the
+ <a href="#types_of_private_data">types of private data</a> to be cleared
+ as follows:
+ <ul>
+ <li>Select the time range to clear if you don't want to clear all the
+ data. Only the following items honor this setting:
+ <ul>
+ <li>Browsing History</li>
+ <li>Download History</li>
+ <li>Cookies</li>
+ <li>Saved Form and Search History</li>
+ <li>Site Preferences.</li>
+ </ul>
+ All other items will always be fully cleared!</li>
+ <li>The defaults for the individual types are determined by the
+ <a href="#private_data_prefs">Private Data preferences</a> in the
+ Clear Manually column.</li>
+ <li>Check or uncheck boxes as desired if you want to clear a different
+ set of private data.</li>
+ <li>Click <q>Clear Now</q> to clear the selected items,
+ or Cancel to quit the dialog.</li>
+ </ul>
+ </p>
+
+<p>[<a href="#clearing_private_data">Return to beginning of section</a>]</p>
+
+<h1 id="browsing_in_a_private_window">Browsing in a Private Window</h1>
+
+<p>There may be occasions where you don&apos;t want &brandShortName; to keep
+ track of your browsing activities. For example, when someone else quickly
+ wants to use your computer and you don&apos;t want your current browsing
+ context disturbed; or, for confidential tasks such as online banking.</p>
+
+<p>Opening a private window starts a
+ <a href="glossary.xhtml#private_browsing">private browsing</a> session
+ in which no <a href="#types_of_private_data">private data</a> on the sites
+ and pages you visit are made available beyond the scope and duration of that
+ session. Each subsequently opened private window becomes part of the same
+ private session. It ends when the last private window is closed.</p>
+
+<p>Private windows are not entirely isolated from non-private windows; private
+ browsing just implies that &brandShortName; will not keep any local record
+ of your activities in such a window. Some private data may be shared between
+ private but not non-private windows and vice versa, others accessed in a
+ read-only mode from a private window.</p>
+
+<p>Note that <a href="customize_help.xhtml#add-ons">Add-ons</a> like plugins
+ and extensions may not be subjected to these policies and may change their
+ settings or modify their locally kept data even in a private window.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#opening_a_private_window">Opening a Private Window</a></li>
+ <li><a href="#behavior_of_a_private_window">Behavior of a Private
+ Window</a></li>
+ <li><a href="#leaving_the_private_browsing_mode">Leaving the Private
+ Browsing Mode</a></li>
+ </ul>
+</div>
+
+<h2 id="opening_a_private_window">Opening a Private Window</h2>
+
+<p>A browser window can be either in regular (non-private) or in private
+ browsing mode. It is not possible to switch a non-private browser window
+ into private mode, but you can open a new private window in two ways:</p>
+
+<ul>
+ <li>From a browser window, open the File menu, choose New, then Private
+ Window. The new window opens with a page explaining that you are now in
+ private browsing mode.</li>
+ <li>Right-click on a link in a web page you are viewing, then select <q>Open
+ Link in Private Window</q> from the menu. The new private window will open,
+ showing the page referred to by the selected link.</li>
+</ul>
+
+<p>Any number of private and non-private windows can be open at the same time,
+ but be aware in this case for which windows history information is kept.</p>
+
+<p>[<a href="#browsing_in_a_private_window">Return to beginning of
+ section</a>]</p>
+
+<h2 id="behavior_of_a_private_window">Behavior of a Private Window</h2>
+
+<p>A private window behaves differently than a non-private window in the way
+ it handles private data (see <a href="#types_of_private_data">Types of
+ Private Data</a> for detailed information of the individual categories).
+ Specifically, after closing a private session when closing the last private
+ window, no information related to that session will be retained in
+ &brandShortName;. Private data gathered in one private session won&apos;t
+ be available to any future private session either.</p>
+
+<h3>Recognizing a Private Window</h3>
+
+<p>To determine whether or not a window is in private browsing mode, have a
+ look at its title bar. Private windows show
+ <q>-&nbsp;<span class="noMac">&brandShortName; </span>Private Browsing</q>
+ <span class="noMac">rather than just <q>-&nbsp;&brandShortName;</q></span>
+ at the end of the window&apos;s title.</p>
+
+<h3>Navigation and Bookmarks</h3>
+
+<ul>
+ <li>Navigating within a private window is identical to a non-private
+ window. You can use the Back and Forward buttons as well as the Go
+ menu as usual.</li>
+ <li>Links will not be marked as visited when browsing in a private
+ window.</li>
+ <li>Bookmarks can be added in a private window in the same way as for a
+ non-private window. They will not be cleared once the private session
+ has ended.</li>
+</ul>
+
+<h3>Browsing and other Histories</h3>
+
+<ul>
+ <li>Previously established Browsing History and Location Bar History will
+ be available to a private window, but new sites visited and locations
+ entered into the location bar will not be recorded and saved.</li>
+ <li>Files can be downloaded as usual, but no entries will be added to the
+ Download Manager. The downloaded files themselves will not be removed.</li>
+ <li>Saved Form and Search History will be available to a private window,
+ thus providing suggestions for form fields or searches. However, any
+ new items or search terms entered will not be recorded and saved.</li>
+</ul>
+
+<h3>Cookies</h3>
+
+<ul>
+ <li>A private window can collect cookies from the sites it visits, but a
+ private window does not share cookies with a non-private window and vice
+ versa. For example, if you log into a website in a regular window, then
+ open a private window to continue, you likely will be asked to enter your
+ credentials again.</li>
+ <li>Cookies in a private session are accepted according to the settings,
+ but only retained for the duration of that session regardless of the
+ lifetime allowed in a non-private window.</li>
+ <li>Existing exceptions to the cookie policy will be honored in private
+ windows, and any new exceptions you create will be retained after the
+ private session has ended.</li>
+</ul>
+
+<h3>Cached and Offline Content</h3>
+
+<ul>
+ <li>While a private window can use the memory cache for performance, it is
+ not permitted to access the disk cache regardless of the settings for a
+ non-private window. Thus, no cached content will be available after
+ restarting &brandShortName;.</li>
+ <li>A private window cannot use any stored offline content deposited by
+ websites. Thus, you will not be able to use web applications while being
+ offline, even if they have already stored offline content locally.</li>
+</ul>
+
+<h3>Passwords and Authenticated Sessions</h3>
+
+<ul>
+ <li>No new passwords will be stored in the Password Manager when entered
+ in a private window.</li>
+ <li>Authenticated sessions do not carry over from a non-private to a private
+ window. Thus, you cannot continue a session started in a non-private window
+ in a private window or vice versa.</li>
+</ul>
+
+<h3>Preferences and Permissions</h3>
+
+<ul>
+ <li>Any changes to settings and permissions made from a private window are
+ handled in the same way as from a non-private window and retained after
+ the private session has ended.</li>
+ <li>Per-site preferences such as zoom levels or download locations are
+ observed in a private window. However, any changes you make will not be
+ retained, e.g., zoom levels revert to the initial value when navigating
+ or switching tabs.</li>
+</ul>
+
+<p>[<a href="#browsing_in_a_private_window">Return to beginning of
+ section</a>]</p>
+
+<h2 id="leaving_the_private_browsing_mode">Leaving the Private Browsing
+ Mode</h2>
+
+<p>There is no special function or command to leave the private mode,
+ and it is not possible to continue working in non-private mode with
+ a private window.</p>
+
+<p>To end a private browsing session, just close all private windows.
+ There will be no record on &brandShortName;&apos;s side on any browsing
+ activities performed in any of the associated private windows.</p>
+
+<p>[<a href="#browsing_in_a_private_window">Return to beginning of
+ section</a>]</p>
+
+<h1 id="encrypting_stored_sensitive_information">Encrypting Stored Sensitive
+ Information</h1>
+
+<p>If you use Password Manager to save passwords, then this sensitive
+ information is stored on your computer in a file that&apos;s difficult, but
+ not impossible, for an intruder to read.</p>
+
+<p>For example, if your computer is in an area where unauthorized people have
+ access to it, it&apos;s possible for a determined person to read the file
+ containing your sensitive information.</p>
+
+<p>For a greater degree of security, you may want to protect the file with
+ encryption. Encryption makes it much harder for an unauthorized person to
+ view your stored sensitive information.</p>
+
+<p>Your decision about whether to use encryption for stored sensitive data is a
+ tradeoff between improved security and convenience.</p>
+
+<p>If you use encryption, you will need to enter a master password, which can
+ be inconvenient. If you don&apos;t, it may be easier for a stranger who has
+ access to your computer to steal your passwords.</p>
+
+<div class="contentsBox">In this section:
+ <ul>
+ <li><a href="#setting_a_master_password">Setting a Master Password</a></li>
+ <li><a href="#changing_your_master_password">Changing Your Master
+ Password</a></li>
+ <li><a href="#logging_out_of_your_master_password">Logging Out of Your
+ Master Password</a></li>
+ <li><a href="#what_to_do_if_you_forget_your_master_password">What to Do If
+ You Forget Your Master Password</a></li>
+ </ul>
+</div>
+
+<h2 id="setting_a_master_password">Setting a Master Password</h2>
+
+<p>To enable encryption of passwords you need to set a master password. If
+ your master password has not previously been set, you can set it at this
+ time:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Change Master Password section, click Change Password to open the
+ Change Master Password dialog box.</li>
+ <li>Enter your desired master password, and retype it to confirm the
+ spelling.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>Make sure your new password is difficult to guess. For some suggestions on
+ how to improve password security, see
+ <a href="passwords_help.xhtml#choosing_a_good_password">Choosing a Good
+ Password</a>.</p>
+
+<p>[<a href="#encrypting_stored_sensitive_information">Return to beginning of
+ section</a>]</p>
+
+<h2 id="changing_your_master_password">Changing Your Master Password</h2>
+
+<p>To change your master password:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>In the Change Master Password section, click Change Password to open the
+ Change Master Password dialog box.</li>
+ <li>Enter your current master password.</li>
+ <li>Enter your new master password, and retype it to confirm the
+ spelling.</li>
+ <li>Click OK.</li>
+</ol>
+
+<p>Make sure your new password is difficult to guess. For some guidelines, see
+ <a href="passwords_help.xhtml#choosing_a_good_password">Choosing a Good
+ Password</a>.</p>
+
+<p>[<a href="#encrypting_stored_sensitive_information">Return to beginning of
+ section</a>]</p>
+
+<h2 id="logging_out_of_your_master_password">Logging Out of Your Master
+ Password</h2>
+
+<p>Normally, you are asked for your master password once during each
+ &brandShortName; session during which you access any of your stored sensitive
+ information.</p>
+
+<p>You can log out of your master password so that it must be entered again
+ before any sensitive information can be stored or retrieved. This is useful
+ if you are going to leave your computer unattended for a period of time.</p>
+
+<p>To log out of your master password:</p>
+
+<ol>
+ <li>Open the Tools menu and choose Password Manager.</li>
+ <li>Select Log Out from the submenu.</li>
+</ol>
+
+<p>[<a href="#encrypting_stored_sensitive_information">Return to beginning of
+ section</a>]</p>
+
+<h2 id="what_to_do_if_you_forget_your_master_password">What to Do If You Forget
+ Your Master Password</h2>
+
+<p>If you forget your master password, you won&apos;t be able to access any of
+ the stored password it protects. Your master password is your most important
+ password. Make sure you remember it or record it in a safe place.</p>
+
+<p>As a last resort, it&apos;s possible to reset your master password if you
+ are sure you can&apos;t remember it. However, resetting your master password
+ permanently erases all the web and email passwords, saved on your behalf by
+ Password Manager. You will also lose all your personal certificates
+ associated with the
+ <a href="glossary.xhtml#software_security_device">Software Security
+ Device</a>.</p>
+
+<p>Before taking this drastic step, read
+ <a href="passwords_help.xhtml#reset_master_password">Reset Master
+ Password</a>.</p>
+
+<p>If you are sure you can&apos;t remember or retrieve your master password,
+ follow these instructions to reset it:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Passwords. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+ <li>Click Reset Master Password.</li>
+ <li>In the Reset Master Password dialog box, click Reset.</li>
+</ol>
+
+<p>[<a href="#encrypting_stored_sensitive_information">Return to beginning of
+ section</a>]</p>
+
+<h1 id="managing_images">Managing Images</h1>
+
+<p>If you wish, you can choose not to download any images when you browse the
+ web. This greatly restricts what you can view online, but may be helpful if
+ you have a slow connection and wish to shorten the time it takes web pages to
+ load.</p>
+
+<p>You can also control how frequently animated images repeat their animation,
+ or turn off animation completely.</p>
+
+<p>The next section describes how to control these image settings. The default
+ settings allow all images to be accepted and allow them to repeat their
+ animation.</p>
+
+<h2 id="images">Privacy &amp; Security Preferences - Images</h2>
+
+<p>This section describes how to set preferences for images. To view the
+ preference settings for images:</p>
+
+<ol>
+ <li>Open the <span class="mac">&brandShortName;</span>
+ <span class="noMac">Edit</span> menu and choose Preferences.</li>
+ <li>Under the Privacy &amp; Security category, click Images. (If no
+ subcategories are visible, double-click Privacy &amp; Security to expand
+ the list.)</li>
+</ol>
+
+<h3 id="image_acceptance_policy">Image Acceptance Policy</h3>
+
+<p>Image Acceptance preferences allow you to control whether or under what
+ conditions the &brandShortName; browser should display images:</p>
+
+<ul>
+ <li><strong>Do not load any images</strong>: Select this option if you do not
+ want the &brandShortName; browser to display images.</li>
+ <li><strong>Only load images that come from the originating
+ server</strong>: Select this option if you do not want to load images from
+ third-party websites.</li>
+ <li><strong>Load all images</strong>: Select this option if you want to
+ display all images. (This option is selected by default.)</li>
+</ul>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/help/welcome_help.xhtml b/comm/suite/locales/en-US/chrome/common/help/welcome_help.xhtml
new file mode 100644
index 0000000000..9689969fec
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/help/welcome_help.xhtml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+ %brandDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>&brandShortName; Help</title>
+<link rel="stylesheet" href="helpFileLayout.css"
+ type="text/css"/>
+</head>
+<body>
+
+<h1>Welcome to &brandShortName; Help</h1>
+
+<p>To display information about &brandShortName; in this window, <strong>click
+ topics in the left-hand sidebar</strong>.</p>
+
+<p>For more information on using &brandShortName; Help, see
+ <a href="help_help.xhtml">Using the Help Window</a>.</p>
+
+<table width="100%" border="0">
+ <tr valign="top">
+ <td><h3>Help on the Web</h3></td>
+ <td><h3>Mozilla Community</h3></td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <p><strong><a href="x-moz-url-link:app.releaseNotesURL">Release Notes</a></strong></p>
+
+ <p>Latest information about known problems or issues with
+ &brandShortName;.</p>
+ </td>
+ <td>
+ <p><strong><a href="http://www.mozillazine.org/">MozillaZine</a></strong></p>
+
+ <p>The oldest community website around, providing news flashes, polls, and
+ Web forums.</p>
+ </td>
+ </tr>
+
+ <tr valign="top">
+ <td>
+ <p><strong><a href="x-moz-url-link:app.troubleshootingURL">Troubleshooting</a></strong></p>
+
+ <p>Answers to some of the most frequently encountered problems.</p>
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+
+ <tr valign="top">
+ <td>
+ <p><strong><a href="http://www.mozilla.org/plugincheck/">Plugin Check</a></strong></p>
+
+ <p>Up-to-date information on &brandShortName; plugins.</p>
+ </td>
+ <td></td>
+ </tr>
+
+ <tr valign="top">
+ <td colspan="2"><h2>Peer Support</h2></td>
+ </tr>
+
+ <tr valign="top">
+ <td>
+ <p><strong><a href="http://forums.mozillazine.org/index.php">MozillaZine
+ Forums</a></strong></p>
+
+ <p>Web forums for Mozilla users like you.</p>
+ </td>
+ <td>
+ <p><strong><a href="http://www.seamonkey-project.org/community#groups">User
+ Newsgroups</a></strong></p>
+
+ <p>User newsgroups hosted by mozilla.org.</p>
+ </td>
+ </tr>
+
+ <tr valign="top">
+ <td>
+ <p><strong><a href="ircs://libera.chat/SeaMonkey">Realtime Chat</a></strong></p>
+ <p>Chat with &brandShortName; users via IRC.</p>
+ </td>
+ <td></td>
+ </tr>
+</table>
+
+</body>
+</html>
diff --git a/comm/suite/locales/en-US/chrome/common/helpviewer/help.dtd b/comm/suite/locales/en-US/chrome/common/helpviewer/help.dtd
new file mode 100644
index 0000000000..5761be5082
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/helpviewer/help.dtd
@@ -0,0 +1,56 @@
+<!-- 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/. -->
+
+<!ENTITY printCmd.commandkey "p">
+
+<!ENTITY findOnCmd.commandkey "F">
+<!ENTITY findAgainCmd.commandkey "G">
+<!ENTITY findAgainCmd.commandkey2 "VK_F3">
+
+<!ENTITY backButton.label "Back">
+<!ENTITY backButton.accesskey "B">
+<!ENTITY backButton.tooltip "Go back one page">
+<!ENTITY forwardButton.label "Forward">
+<!ENTITY forwardButton.accesskey "F">
+<!ENTITY forwardButton.tooltip "Go forward one page">
+<!ENTITY copyCmd.label "Copy">
+<!ENTITY copyCmd.accesskey "C">
+<!ENTITY selectAllCmd.label "Select All">
+<!ENTITY selectAllCmd.accesskey "A">
+<!ENTITY goBackCmd.commandkey "[">
+<!ENTITY goForwardCmd.commandkey "]">
+<!ENTITY homeButton.label "Home">
+<!ENTITY homeButton.tooltip "Go to the Help Start Page">
+<!ENTITY printButton.label "Print">
+<!ENTITY printButton.tooltip "Print this page">
+<!ENTITY closeWindow.commandkey "W">
+
+<!ENTITY search.emptytext "Search">
+<!ENTITY searchHeader.label "Search results">
+<!ENTITY toctab.label "Contents">
+<!ENTITY toctab.accesskey "C">
+
+<!-- LOCALIZATION NOTE :
+fullZoomEnlargeCmd.commandkey3, fullZoomReduceCmd.commandkey2 and
+fullZoomResetCmd.commandkey2 are alternative acceleration keys for zoom.
+If shift key is needed with your locale popular keyboard for them,
+you can use these alternative items. Otherwise, their values should be empty. -->
+
+<!ENTITY fullZoomReduceCmd.commandkey "-">
+<!ENTITY fullZoomReduceCmd.commandkey2 "">
+<!ENTITY fullZoomEnlargeCmd.commandkey "+">
+<!ENTITY fullZoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
+<!ENTITY fullZoomEnlargeCmd.commandkey3 "">
+<!ENTITY fullZoomResetCmd.commandkey "0">
+<!ENTITY fullZoomResetCmd.commandkey2 "">
+
+<!ENTITY helpSearch.commandkey "k">
+
+<!ENTITY zLevel.label "Always on Top">
+<!ENTITY zLevel.accesskey "T">
+
+<!ENTITY fullZoomReduceBtn.label "Zoom Out">
+<!ENTITY fullZoomReduceBtn.accesskey "O">
+<!ENTITY fullZoomEnlargeBtn.label "Zoom In">
+<!ENTITY fullZoomEnlargeBtn.accesskey "I">
diff --git a/comm/suite/locales/en-US/chrome/common/helpviewer/help.properties b/comm/suite/locales/en-US/chrome/common/helpviewer/help.properties
new file mode 100644
index 0000000000..037bce2370
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/helpviewer/help.properties
@@ -0,0 +1,5 @@
+# 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/.
+
+emptySearchText=No search items found.
diff --git a/comm/suite/locales/en-US/chrome/common/migration/migration.dtd b/comm/suite/locales/en-US/chrome/common/migration/migration.dtd
new file mode 100644
index 0000000000..28141f980a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/migration/migration.dtd
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!ENTITY migrationWizard.title "Import Wizard">
+
+<!ENTITY importAllFrom.label "Import All Items from:">
+<!ENTITY importBookmarksFrom.label "Import Bookmarks from:">
+
+<!ENTITY importFromThunderbird.label "Thunderbird">
+<!ENTITY importFromThunderbird.accesskey "T">
+<!ENTITY importFromFile.label "File">
+<!ENTITY importFromFile.accesskey "F">
+<!ENTITY importFromNothing.label "Don't import anything">
+<!ENTITY importFromNothing.accesskey "D">
+
+<!ENTITY importSource.title "Import Settings and Data">
+<!ENTITY importItems.title "Items to Import">
+<!ENTITY importItems.label "Select which items to import:">
+
+<!ENTITY selectProfile.title "Select Profile">
+<!ENTITY selectProfile.label "The following profiles are available to import from:">
+
+<!ENTITY migrating.title "Importing…">
+<!ENTITY migrating.label "The following items are currently being imported…">
+
+<!ENTITY done.title "Import Complete">
+<!ENTITY done.label "The following items were successfully imported:">
diff --git a/comm/suite/locales/en-US/chrome/common/migration/migration.properties b/comm/suite/locales/en-US/chrome/common/migration/migration.properties
new file mode 100644
index 0000000000..d96079ba57
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/migration/migration.properties
@@ -0,0 +1,52 @@
+# 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/.
+
+# the following section is used in the first-time migration wizard
+# new and scary, however if these strings are not present, we will simply
+# skip this wizard page
+homePageStartDefault=Default SeaMonkey Home Page
+homePageStartCurrent=Current SeaMonkey Home Page
+homePageImport=Import your home page from %S
+
+homePageMigrationPageTitle=Home Page Selection
+homePageMigrationDescription=Please select the home page you wish to use:
+# end safe-to-not-have section
+
+# note that the names of apps are in lower case to fit in with the
+# protocol specifications.
+sourceNamethunderbird=Thunderbird
+
+importedBookmarksFolder=From %S
+
+importedSeamonkeyBookmarksTitle=SeaMonkey 1.x, Netscape 6/7 or Mozilla 1.x
+
+# Import Sources
+# These are the string names for the values given in nsISuiteProfileMigrator.idl
+# _generic will apply to all import sources unless specifically overriden by another
+# item.
+# e.g. 1_ie=Internet Options will display "Internet Options" rather than "Preferences" when
+# importing from Internet Explorer.
+1_generic=Preferences
+
+2_generic=Cookies
+
+4_generic=Browsing History
+
+8_generic=Home Page
+
+16_generic=Saved Passwords
+
+32_generic=Bookmarks
+
+64_generic=Other Data
+
+128_generic=Account Settings
+
+256_generic=Address Books
+
+512_generic=Junk Mail Training
+
+1024_generic=Newsgroup Folders
+
+2048_generic=Mail Folders
diff --git a/comm/suite/locales/en-US/chrome/common/notification.dtd b/comm/suite/locales/en-US/chrome/common/notification.dtd
new file mode 100644
index 0000000000..5812ef4bc0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/notification.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+<!ENTITY closeNotification.tooltip "Close this message">
+
+<!-- LOCALIZATION NOTE (closeNotificationItem.label): This entity is used
+ to replace the Toolkit default closeNotificationItem.label ("Not Now")
+ which is ambiguous in some cases. Thus, make sure to select a phrase
+ that clearly relates to closing the current doorhanger. -->
+<!ENTITY closeNotificationItem.label "Dismiss Notification">
+
+<!ENTITY checkForUpdates "Check for updates…">
+
+<!ENTITY learnMore "Learn more…">
diff --git a/comm/suite/locales/en-US/chrome/common/notification.properties b/comm/suite/locales/en-US/chrome/common/notification.properties
new file mode 100644
index 0000000000..1bd57aacf5
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/notification.properties
@@ -0,0 +1,162 @@
+# 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/.
+
+crashedpluginsMessage.title=The %S plugin has crashed.
+crashedpluginsMessage.reloadButton.label=Reload page
+crashedpluginsMessage.reloadButton.accesskey=R
+crashedpluginsMessage.submitButton.label=Submit a crash report
+crashedpluginsMessage.submitButton.accesskey=S
+crashedpluginsMessage.learnMore=Learn More…
+
+pluginInfo.unknownPlugin=Unknown
+
+# LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is brandShortName and #2 is the number of popups blocked.
+popupWarning.message=#1 prevented this website from opening a popup window.;#1 prevented this website from opening #2 popup windows.
+popupWarningButton=Preferences
+popupWarningButton.accesskey=P
+
+xpinstallHostNotAvailable=unknown host
+xpinstallPromptWarning=%S prevented this website (%S) from asking you to install software on your computer.
+xpinstallPromptInstallButton=Install Software…
+xpinstallPromptInstallButton.accesskey=I
+xpinstallDisabledMessageLocked=Software installation has been disabled by your system administrator.
+xpinstallDisabledMessage=Software installation is currently disabled. Click Enable and try again.
+xpinstallDisabledButton=Enable
+xpinstallDisabledButton.accesskey=n
+
+# LOCALIZATION NOTE (addonDownloading, addonDownloadCancelled):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/Localization_and_Plurals
+# The number of add-ons is not itself substituted in the string.
+addonDownloading=Add-on downloading:;Add-ons downloading:
+addonDownloadCancelled=Add-on download cancelled.;Add-on downloads cancelled.
+addonDownloadCancelButton=Cancel
+addonDownloadCancelButton.accesskey=C
+addonDownloadRestartButton=Restart
+addonDownloadRestartButton.accesskey=R
+
+# LOCALIZATION NOTE (addonsInstalled, addonsInstalledNeedsRestart):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/Localization_and_Plurals
+# #1 first add-on's name, #2 number of add-ons, #3 application name
+addonsInstalled=#1 has been installed successfully.;#2 add-ons have been installed successfully.
+addonsInstalledNeedsRestart=#1 will be installed after you restart #3.;#2 add-ons will be installed after you restart #3.
+addonInstallRestartButton=Restart Now
+addonInstallRestartButton.accesskey=R
+addonInstallManageButton=Open Add-ons Manager
+addonInstallManageButton.accesskey=O
+
+# LOCALIZATION NOTE (addonError-1, addonError-2, addonError-3, addonError-4, addonErrorIncompatible, addonErrorBlocklisted):
+# #1 is the add-on name, #2 is the host name, #3 is the application name
+# #4 is the application version
+addonError-1=The add-on could not be downloaded because of a connection failure on #2.
+addonError-2=The add-on from #2 could not be installed because it does not match the add-on #3 expected.
+addonError-3=The add-on downloaded from #2 could not be installed because it appears to be corrupt.
+addonError-4=#1 could not be installed because #3 cannot modify the needed file.
+addonErrorBlocklisted=#1 could not be installed because it has a high risk of causing stability or security problems.
+addonErrorIncompatible=#1 could not be installed because it is not compatible with #3 #4.
+
+# Light Weight Themes
+# LOCALIZATION NOTE (lwthemeInstallRequest.message): %S will be replaced with
+# the host name of the site.
+lwthemeInstallRequest.message=This site (%S) attempted to install a theme. Click Allow to proceed.
+lwthemeInstallRequest.allowButton=Allow
+lwthemeInstallRequest.allowButton.accesskey=A
+
+lwthemeInstallNotification.message=A new theme has been installed.
+lwthemeInstallNotification.undoButton=Undo
+lwthemeInstallNotification.undoButton.accesskey=n
+lwthemeInstallNotification.manageButton=Manage Themes…
+lwthemeInstallNotification.manageButton.accesskey=M
+
+# LOCALIZATION NOTE (lwthemeNeedsRestart.message):
+# %S will be replaced with the new theme name.
+lwthemeNeedsRestart.message=%S will be installed after you restart.
+lwthemeNeedsRestart.restartButton=Restart Now
+lwthemeNeedsRestart.restartButton.accesskey=R
+
+# Geolocation UI
+geolocation.allowLocation=Allow Location Access
+geolocation.allowLocation.accesskey=A
+geolocation.dontAllowLocation=Don’t Allow
+geolocation.dontAllowLocation.accesskey=N
+geolocation.shareWithSite3=Will you allow %S to access your location?
+geolocation.shareWithFile3=Will you allow this local file to access your location?
+geolocation.remember=Remember for this website
+
+# Persistent storage UI
+persistentStorage.allow=Allow
+persistentStorage.allow.accesskey=A
+persistentStorage.dontAllow=Don’t Allow
+persistentStorage.dontAllow.accesskey=N
+persistentStorage.allowWithSite=Will you allow %S to store data in persistent storage?
+persistentStorage.remember=Remember this decision
+
+# Desktop Notifications
+webNotifications.allow=Allow Notifications
+webNotifications.allow.accesskey=A
+webNotifications.notNow=Not Now
+webNotifications.notNow.accesskey=o
+webNotifications.never=Never Allow
+webNotifications.never.accesskey=N
+webNotifications.receiveFromSite2=Will you allow %S to send notifications?
+
+# IndexedDB
+offlineApps.permissions=This website (%S) is asking to store data on your computer for offline use.
+offlineApps.private=You are in a private window. This website (%S) has not been allowed to store data on your computer for offline use.
+offlineApps.quota=This website (%1$S) is attempting to store more than %2$SMB of data on your computer for offline use.
+offlineApps.always=Always Allow
+offlineApps.always.accesskey=A
+offlineApps.later=Not Now
+offlineApps.later.accesskey=N
+offlineApps.never=Never for This Site
+offlineApps.never.accesskey=e
+
+# Block autorefresh
+refreshBlocked.goButton=Allow
+refreshBlocked.goButton.accesskey=A
+refreshBlocked.refreshLabel=%S prevented this page from automatically reloading.
+refreshBlocked.redirectLabel=%S prevented this page from automatically redirecting to another page.
+
+# LOCALIZATION NOTE (updatePrompt.text)
+# %S will be replaced with the application name.
+updatePrompt.text=Your copy of %S is old and probably has known security flaws, but you have disabled automated update checks. Please update to a newer version.
+updatePromptCheckButton.label=Check for Updates
+updatePromptCheckButton.accesskey=C
+
+SecurityTitle=Security Warning
+MixedContentMessage=You have requested an encrypted page that contains some unencrypted information. Information that you see or enter on this page could easily be read by a third party.
+MixedActiveContentMessage=You have requested an encrypted page that contains insecure information. Information that you see or enter on this page could easily be read by a third party.
+TrackingContentMessage=Parts of this page may be tracking your online activity.
+MixedDisplayContentMessage=You have requested a page that is only partially encrypted and does not prevent eavesdropping.
+BlockedActiveContentMessage=Insecure information on this page was blocked.
+BlockedTrackingContentMessage=Parts of the page that track your online activity were blocked.
+BlockedDisplayContentMessage=Unencrypted information on this page was blocked.
+EnterInsecureMessage=You have left an encrypted page. Information you send or receive from now on could easily be read by a third party.
+EnterSecureMessage=You have requested an encrypted page. The website has identified itself correctly, and information you see or enter on this page can't easily be read by a third party.
+SecurityKeepBlocking.label=Keep Blocking
+SecurityKeepBlocking.accesskey=K
+SecurityUnblock.label=Unblock
+SecurityUnblock.accesskey=U
+SecurityPreferences.label=Preferences
+SecurityPreferences.accesskey=P
+PostToInsecureFromInsecureMessage=The information you have entered is to be sent over an unencrypted connection and could easily be read by a third party.\nAre you sure you want to continue sending this information?
+PostToInsecureFromInsecureShowAgain=Alert me whenever I submit information that's not encrypted.
+PostToInsecureContinue=Continue
+
+# Phishing/Malware Notification Bar.
+# LOCALIZATION NOTE (notADeceptiveSite, notAnAttack)
+# The two button strings will never be shown at the same time, so
+# it's okay for them to have the same access key.
+safebrowsing.getMeOutOfHereButton.label=Get me out of here!
+safebrowsing.getMeOutOfHereButton.accessKey=G
+safebrowsing.deceptiveSite=Deceptive Site!
+safebrowsing.notADeceptiveSiteButton.label=This isn't a deceptive site…
+safebrowsing.notADeceptiveSiteButton.accessKey=d
+safebrowsing.reportedAttackSite=Reported Attack Site!
+safebrowsing.notAnAttackButton.label=This isn't an attack site…
+safebrowsing.notAnAttackButton.accessKey=a
+safebrowsing.reportedUnwantedSite=Reported Unwanted Software Site!
diff --git a/comm/suite/locales/en-US/chrome/common/openLocation.dtd b/comm/suite/locales/en-US/chrome/common/openLocation.dtd
new file mode 100644
index 0000000000..c9f329f186
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/openLocation.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!-- extracted from content/openLocation.xul -->
+
+<!ENTITY enter.label "Enter the web location (URL), or specify the local file you would like to open:">
+<!-- LOCALIZATION NOTE (enter.accesskey): should be present in both enter.label
+ as defined above and attachEnterLabel as defined in
+ openLocation.properties -->
+<!ENTITY enter.accesskey "E">
+<!ENTITY chooseFile.label "Choose File…">
+<!ENTITY chooseFile.accesskey "C">
+<!ENTITY newPrivate.label "New private window">
+<!ENTITY newWindow.label "New browser window">
+<!ENTITY newTab.label "New browser tab">
+<!ENTITY currentTab.label "Current browser tab">
+<!ENTITY editNewWindow.label "New Composer window">
+<!ENTITY open.label "Open">
+<!ENTITY caption.label "Open Web Location">
+<!ENTITY openWhere.label "Open in:">
+<!ENTITY openWhere.accesskey "O">
diff --git a/comm/suite/locales/en-US/chrome/common/openLocation.properties b/comm/suite/locales/en-US/chrome/common/openLocation.properties
new file mode 100644
index 0000000000..ab96c9df55
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/openLocation.properties
@@ -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/.
+
+attachTitle=Attach Web Page
+# LOCALIZATION NOTE (attachEnterLabel): should contain "enter.accesskey"
+# as defined in openLocation.dtd
+attachEnterLabel=Enter the web location (URL), or specify the local file you would like to attach:
+attachButtonLabel=Attach
+chooseFileDialogTitle=Choose File
+existingNavigatorWindow=Existing browser window
diff --git a/comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.dtd b/comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.dtd
new file mode 100644
index 0000000000..93bf2348c0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.dtd
@@ -0,0 +1,46 @@
+<!-- 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/. -->
+
+<!ENTITY tab.cookiesonsystem.label "Stored Cookies">
+<!ENTITY tab.bannedservers.label "Cookie Websites">
+<!ENTITY div.bannedservers.label "Manage websites that can and cannot store cookies on your computer.">
+<!ENTITY div.cookiesonsystem.label "View and remove cookies that are stored on your computer.">
+<!ENTITY treehead.cookiename.label "Cookie Name">
+<!ENTITY treehead.cookiedomain.label "Website">
+<!ENTITY treehead.cookieexpires.label "Expires">
+<!ENTITY treehead.infoselected.label "Information about the selected Cookie">
+<!ENTITY button.removecookie.label "Remove Cookie">
+<!ENTITY button.removecookie.accesskey "R">
+<!ENTITY button.removeallcookies.label "Remove All Cookies">
+<!ENTITY button.removeallcookies.accesskey "e">
+
+<!ENTITY search.placeholder "Search Cookies">
+<!ENTITY focusSearch.key "f">
+<!ENTITY selectAll.key "a">
+
+<!ENTITY props.name.label "Name:">
+<!ENTITY props.value.label "Content:">
+<!ENTITY props.domain.label "Host:">
+<!ENTITY props.path.label "Path:">
+<!ENTITY props.secure.label "Send For:">
+<!ENTITY props.expires.label "Expires:">
+
+<!ENTITY treehead.sitename.label "Website">
+<!ENTITY treehead.scheme.label "Scheme">
+<!ENTITY treehead.status.label "Status">
+<!ENTITY windowtitle.label "Cookie Manager">
+
+<!ENTITY blockSite.label "Block">
+<!ENTITY blockSite.accesskey "B">
+<!ENTITY allowSite.label "Allow">
+<!ENTITY allowSite.accesskey "A">
+<!ENTITY allowSiteSession.label "Session">
+<!ENTITY allowSiteSession.accesskey "S">
+<!ENTITY removepermission.label "Remove Website">
+<!ENTITY removepermission.accesskey "R">
+<!ENTITY removeallpermissions.label "Remove All Websites">
+<!ENTITY removeallpermissions.accesskey "e">
+
+<!ENTITY futureCookies.label "Don't allow websites that set removed cookies to set future cookies">
+<!ENTITY futureCookies.accesskey "D">
diff --git a/comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.properties b/comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.properties
new file mode 100644
index 0000000000..37db53c69e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/permissions/cookieViewer.properties
@@ -0,0 +1,30 @@
+# 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/.
+
+# note this section of the code may require some tinkering in other languages =(
+# format in dialog: site [can/cannot] set cookies
+can=website can set cookies
+cannot=website cannot set cookies
+canSession=website can set session cookies
+domain=Domain for which this cookie applies:
+host=Server which set the cookie:
+hostColon=Host:
+domainColon=Domain:
+forSecureOnly=Encrypted connections only
+forAnyConnection=Any type of connection
+expireAtEndOfSession=At end of session
+allowedURLSchemes=Check the entered url. Also you can only add permissions for http or https schemes
+errorAddPermission=Permission for site could not be added
+deleteAllCookies=Are you sure you want to delete all the cookies?
+deleteAllCookiesTitle=Remove All Cookies
+deleteAllCookiesYes=&Remove
+deleteAllCookiesSites=Are you sure you want to delete all of the cookie websites?
+deleteAllSitesTitle=Remove All Websites
+deleteAllSitesYes=&Remove
+deleteSelectedCookies=Are you sure you want to delete the selected cookies?
+deleteSelectedCookiesTitle=Remove Selected Cookies
+deleteSelectedCookiesYes=&Remove
+deleteSelectedCookiesSites=Are you sure you want to delete the selected cookie websites?
+deleteSelectedSitesTitle=Remove Selected Websites
+deleteSelectedSitesYes=&Remove
diff --git a/comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.dtd b/comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.dtd
new file mode 100644
index 0000000000..576cbd55ae
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.dtd
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<!ENTITY windowtitle.label "Exceptions">
+<!ENTITY treehead.sitename.label "Website">
+<!ENTITY treehead.status.label "Status">
+<!ENTITY remove.label "Remove Website">
+<!ENTITY remove.accesskey "R">
+<!ENTITY removeall.label "Remove All Websites">
+<!ENTITY removeall.accesskey "e">
+<!ENTITY address.label "Address of website:">
+<!ENTITY address.accesskey "d">
+<!ENTITY block.label "Block">
+<!ENTITY block.accesskey "B">
+<!ENTITY session.label "Allow for Session">
+<!ENTITY session.accesskey "S">
+<!ENTITY allow.label "Allow">
+<!ENTITY allow.accesskey "A">
diff --git a/comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.properties b/comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.properties
new file mode 100644
index 0000000000..8974f4d2b1
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/permissions/permissionsManager.properties
@@ -0,0 +1,23 @@
+# 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/.
+
+installpermissionstext=You can specify which websites are allowed to install extensions and updates. Type the exact address of the website you want to allow and then click Allow.
+installpermissionstitle=Allowed Websites - Software Installation
+installpermissionshelp=advanced_pref_installation
+popuppermissionstext=You can specify which websites are allowed to open popup windows. Type the exact address of the website you want to allow and then click Allow.
+popuppermissionstitle=Allowed Websites - Popups
+popuppermissionshelp=pop_up_blocking
+imagepermissionstext=You can specify which websites are allowed to load images. Type the exact address of the website you want to manage and then click Block or Allow.
+imagepermissionstitle=Exceptions - Images
+imagepermissionshelp=images-help-managing
+offline-apppermissionstext=You can specify which websites are allowed to store data for offline use. Type the exact address of the website you want to manage and then click Block or Allow.
+offline-apppermissionstitle=Offline Data
+offline-apppermissionshelp=offline_apps
+
+can=Allow
+canSession=Allow for Session
+cannot=Block
+
+alertInvalidTitle=Invalid Website Entered
+alertInvalid=The website %S is invalid.
diff --git a/comm/suite/locales/en-US/chrome/common/places/bookmarkProperties.properties b/comm/suite/locales/en-US/chrome/common/places/bookmarkProperties.properties
new file mode 100644
index 0000000000..9f818f45bd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/places/bookmarkProperties.properties
@@ -0,0 +1,19 @@
+# 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/.
+
+dialogAcceptLabelAddItem=Add
+dialogAcceptLabelSaveItem=Save
+dialogAcceptLabelAddLivemark=Subscribe
+dialogAcceptLabelAddMulti=Add Bookmarks
+dialogAcceptLabelEdit=Save
+dialogTitleAddBookmark=New Bookmark
+dialogTitleAddLivemark=Subscribe with Live Bookmark
+dialogTitleAddFolder=New Folder
+dialogTitleAddMulti=New Bookmarks
+dialogTitleEdit=Properties for “%Sâ€
+
+bookmarkAllTabsDefault=[Folder Name]
+newFolderDefault=New Folder
+newBookmarkDefault=New Bookmark
+newLivemarkDefault=New Live Bookmark
diff --git a/comm/suite/locales/en-US/chrome/common/places/editBookmarkOverlay.dtd b/comm/suite/locales/en-US/chrome/common/places/editBookmarkOverlay.dtd
new file mode 100644
index 0000000000..d78c355c24
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/places/editBookmarkOverlay.dtd
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!ENTITY editBookmarkOverlay.name.label "Name:">
+<!ENTITY editBookmarkOverlay.name.accesskey "N">
+<!ENTITY editBookmarkOverlay.location.label "Location:">
+<!ENTITY editBookmarkOverlay.location.accesskey "L">
+<!ENTITY editBookmarkOverlay.feedLocation.label "Feed Location:">
+<!ENTITY editBookmarkOverlay.feedLocation.accesskey "F">
+<!ENTITY editBookmarkOverlay.siteLocation.label "Site Location:">
+<!ENTITY editBookmarkOverlay.siteLocation.accesskey "S">
+<!ENTITY editBookmarkOverlay.folder.label "Folder:">
+<!ENTITY editBookmarkOverlay.foldersExpanderDown.tooltip "Show all the bookmarks folders">
+<!ENTITY editBookmarkOverlay.expanderUp.tooltip "Hide">
+<!ENTITY editBookmarkOverlay.tags.label "Tags:">
+<!ENTITY editBookmarkOverlay.tags.accesskey "T">
+<!ENTITY editBookmarkOverlay.tagsEmptyDesc.label "Separate tags with commas">
+<!ENTITY editBookmarkOverlay.description.label "Description:">
+<!ENTITY editBookmarkOverlay.description.accesskey "D">
+<!ENTITY editBookmarkOverlay.keyword.label "Keyword:">
+<!ENTITY editBookmarkOverlay.keyword.accesskey "K">
+<!ENTITY editBookmarkOverlay.tagsExpanderDown.tooltip "Show all tags">
+<!ENTITY editBookmarkOverlay.loadInSidebar.label "Load this bookmark in the sidebar">
+<!ENTITY editBookmarkOverlay.loadInSidebar.accesskey "h">
+<!ENTITY editBookmarkOverlay.choose.label "Choose…">
+<!ENTITY editBookmarkOverlay.newFolderButton.label "New Folder">
+<!ENTITY editBookmarkOverlay.newFolderButton.accesskey "o">
diff --git a/comm/suite/locales/en-US/chrome/common/places/places.dtd b/comm/suite/locales/en-US/chrome/common/places/places.dtd
new file mode 100644
index 0000000000..a7882d1a97
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/places/places.dtd
@@ -0,0 +1,106 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE (places.library.title): use "Library", "Archive" or "Repository" -->
+<!ENTITY places.library.title "Library">
+<!ENTITY places.library.width "700">
+<!ENTITY places.library.height "500">
+<!ENTITY organize.label "Organize">
+<!ENTITY organize.accesskey "O">
+<!ENTITY organize.tooltip "Organize your bookmarks">
+
+<!ENTITY file.close.label "Close">
+<!ENTITY file.close.accesskey "C">
+<!ENTITY cmd.close.key "w">
+<!ENTITY views.label "Views">
+<!ENTITY views.accesskey "V">
+<!ENTITY views.tooltip "Change your view">
+<!ENTITY view.columns.label "Show Columns">
+<!ENTITY view.columns.accesskey "C">
+<!ENTITY view.sort.label "Sort">
+<!ENTITY view.sort.accesskey "S">
+<!ENTITY view.unsorted.label "Unsorted">
+<!ENTITY view.unsorted.accesskey "U">
+<!ENTITY view.sortAscending.label "A > Z Sort Order">
+<!ENTITY view.sortAscending.accesskey "A">
+<!ENTITY view.sortDescending.label "Z > A Sort Order">
+<!ENTITY view.sortDescending.accesskey "Z">
+
+<!ENTITY importBookmarksFromHTML.label "Import Bookmarks from HTML…">
+<!ENTITY importBookmarksFromHTML.accesskey "I">
+<!ENTITY exportBookmarksToHTML.label "Export Bookmarks to HTML…">
+<!ENTITY exportBookmarksToHTML.accesskey "E">
+<!ENTITY importOtherBrowser.label "Import Data from Another Browser…">
+<!ENTITY importOtherBrowser.accesskey "A">
+
+<!ENTITY cmd.backup.label "Backup…">
+<!ENTITY cmd.backup.accesskey "B">
+<!ENTITY cmd.restore2.label "Restore">
+<!ENTITY cmd.restore2.accesskey "R">
+<!ENTITY cmd.restoreFromFile.label "Choose File…">
+<!ENTITY cmd.restoreFromFile.accesskey "C">
+
+<!ENTITY cmd.deleteDomainData.label "Forget About This Site">
+<!ENTITY cmd.deleteDomainData.accesskey "F">
+
+<!ENTITY cmd.open.label "Open">
+<!ENTITY cmd.open.accesskey "O">
+<!ENTITY cmd.open_window.label "Open in a New Window">
+<!ENTITY cmd.open_window.accesskey "N">
+<!ENTITY cmd.open_private_window.label "Open in a New Private Window">
+<!ENTITY cmd.open_private_window.accesskey "P">
+<!ENTITY cmd.open_tab.label "Open in a New Tab">
+<!ENTITY cmd.open_tab.accesskey "w">
+<!ENTITY cmd.open_all_in_tabs.label "Open All in Tabs">
+<!ENTITY cmd.open_all_in_tabs.accesskey "O">
+
+<!ENTITY cmd.properties.label "Properties">
+<!ENTITY cmd.properties.accesskey "i">
+
+<!ENTITY cmd.sortby_name.label "Sort By Name">
+<!ENTITY cmd.sortby_name.accesskey "S">
+<!ENTITY cmd.context_sortby_name.accesskey "r">
+
+<!ENTITY cmd.new_bookmark.label "New Bookmark…">
+<!ENTITY cmd.new_bookmark.accesskey "B">
+<!ENTITY cmd.new_folder.label "New Folder…">
+<!ENTITY cmd.new_folder.accesskey "o">
+<!ENTITY cmd.context_new_folder.accesskey "F">
+<!ENTITY cmd.new_separator.label "New Separator">
+<!ENTITY cmd.new_separator.accesskey "S">
+
+<!ENTITY cmd.reloadLivebookmark.label "Reload Live Bookmark">
+<!ENTITY cmd.reloadLivebookmark.accesskey "R">
+
+<!ENTITY col.name.label "Name">
+<!ENTITY col.tags.label "Tags">
+<!ENTITY col.url.label "Location">
+<!ENTITY col.mostrecentvisit.label "Most Recent Visit">
+<!ENTITY col.visitcount.label "Visit Count">
+<!ENTITY col.description.label "Description">
+<!ENTITY col.dateadded.label "Added">
+<!ENTITY col.lastmodified.label "Last Modified">
+
+<!ENTITY search.placeholder "Search">
+
+<!ENTITY cmd.find.key "f">
+
+<!ENTITY detailsPane.more.label "More">
+<!ENTITY detailsPane.more.accesskey "e">
+<!ENTITY detailsPane.less.label "Less">
+<!ENTITY detailsPane.less.accesskey "e">
+<!ENTITY detailsPane.selectAnItemText.description "Select an item to view and edit its properties">
+
+<!ENTITY view.label "View">
+<!ENTITY view.accesskey "w">
+<!ENTITY byDate.label "By Date">
+<!ENTITY byDate.accesskey "D">
+<!ENTITY bySite.label "By Site">
+<!ENTITY bySite.accesskey "S">
+<!ENTITY byMostVisited.label "By Most Visited">
+<!ENTITY byMostVisited.accesskey "V">
+<!ENTITY byLastVisited.label "By Last Visited">
+<!ENTITY byLastVisited.accesskey "L">
+<!ENTITY byDayAndSite.label "By Date and Site">
+<!ENTITY byDayAndSite.accesskey "t">
diff --git a/comm/suite/locales/en-US/chrome/common/places/places.properties b/comm/suite/locales/en-US/chrome/common/places/places.properties
new file mode 100644
index 0000000000..9e84f61999
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/places/places.properties
@@ -0,0 +1,100 @@
+# 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-js-data-url-error=For security reasons, javascript or data urls cannot be loaded from the history window or sidebar.
+noTitle=(no title)
+
+bookmarksMenuEmptyFolder=(Empty)
+
+bookmarksBackupTitle=Bookmarks backup filename
+
+bookmarksRestoreAlertTitle=Revert Bookmarks
+bookmarksRestoreAlert=This will replace all of your current bookmarks with the backup. Are you sure?
+bookmarksRestoreTitle=Select a bookmarks backup
+bookmarksRestoreFilterName=JSON
+
+bookmarksRestoreFormatError=Unsupported file type.
+bookmarksRestoreParseError=Unable to process the backup file.
+
+bookmarksLivemarkLoading=Live Bookmark loading…
+bookmarksLivemarkFailed=Live Bookmark feed failed to load.
+
+menuOpenLivemarkOrigin.label=Open “%Sâ€
+
+sortByName=Sort ‘%S’ by Name
+sortByNameGeneric=Sort by Name
+# LOCALIZATION NOTE (view.sortBy.1.name.label): sortBy properties are versioned.
+# When any of these changes, all of the properties must be bumped, and the
+# change must be annotated here. Both label and accesskey must be updated.
+# - version 1: changed view.sortBy.1.date.
+view.sortBy.1.name.label=Sort by Name
+view.sortBy.1.name.accesskey=N
+view.sortBy.1.url.label=Sort by Location
+view.sortBy.1.url.accesskey=L
+view.sortBy.1.date.label=Sort by Most Recent Visit
+view.sortBy.1.date.accesskey=V
+view.sortBy.1.visitCount.label=Sort by Visit Count
+view.sortBy.1.visitCount.accesskey=C
+view.sortBy.1.description.label=Sort by Description
+view.sortBy.1.description.accesskey=D
+view.sortBy.1.dateAdded.label=Sort by Added
+view.sortBy.1.dateAdded.accesskey=e
+view.sortBy.1.lastModified.label=Sort by Last Modified
+view.sortBy.1.lastModified.accesskey=M
+view.sortBy.1.tags.label=Sort by Tags
+view.sortBy.1.tags.accesskey=T
+
+searchBookmarks=Search Bookmarks
+searchHistory=Search History
+
+SelectImport=Import Bookmarks File
+EnterExport=Export Bookmarks File
+
+detailsPane.noItems=No items
+# LOCALIZATION NOTE (detailsPane.itemsCountLabel): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 number of items
+# example: 111 items
+detailsPane.itemsCountLabel=One item;#1 items
+
+mostVisitedTitle=Most Visited
+recentlyBookmarkedTitle=Recently Bookmarked
+recentTagsTitle=Recent Tags
+
+OrganizerQueryHistory=History
+OrganizerQueryAllBookmarks=All Bookmarks
+OrganizerQueryTags=Tags
+
+# LOCALIZATION NOTE (tagResultLabel, bookmarkResultLabel, switchtabResultLabel,
+# keywordResultLabel, searchengineResultLabel)
+# Noun used to describe the location bar autocomplete result type
+# to users with screen readers
+# See createResultLabel() in urlbarBindings.xml
+tagResultLabel=Tag
+bookmarkResultLabel=Bookmark
+switchtabResultLabel=Tab
+keywordResultLabel=Keyword
+searchengineResultLabel=Search
+
+# LOCALIZATION NOTE (lockPrompt.text)
+# %S will be replaced with the application name.
+lockPrompt.text=The bookmarks and history system will not be functional because one of %S’s files is in use by another application. Some security software can cause this problem.
+lockPromptInfoButton.label=Learn More
+lockPromptInfoButton.accesskey=L
+
+# LOCALIZATION NOTE (cmd.deleteSinglePage.accesskey,
+# cmd.deleteMultiplePages.accesskey): these accesskeys can use the same
+# character, since they're never displayed at the same time
+cmd.deleteSinglePage.label=Delete Page
+cmd.deleteSinglePage.accesskey=D
+cmd.deleteMultiplePages.label=Delete Pages
+cmd.deleteMultiplePages.accesskey=D
+
+# LOCALIZATION NOTE (cmd.bookmarkSinglePage.accesskey,
+# cmd.bookmarkMultiplePages.accesskey): these accesskeys can use the same
+# character, since they're never displayed at the same time
+cmd.bookmarkSinglePage.label=Bookmark Page
+cmd.bookmarkSinglePage.accesskey=B
+cmd.bookmarkMultiplePages.label=Bookmark Pages
+cmd.bookmarkMultiplePages.accesskey=B
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-advanced.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-advanced.dtd
new file mode 100644
index 0000000000..d412ff73b7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-advanced.dtd
@@ -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/. -->
+
+<!--LOCALIZATION NOTE : FILE 'Advanced' prefs settings -->
+<!ENTITY pref.advanced.title "Advanced">
+
+<!ENTITY prefCheckDefault.caption "System Integration">
+<!ENTITY prefCheckDefaultClient.label "Check default application settings on startup">
+<!ENTITY prefCheckDefaultClient.accesskey "C">
+<!ENTITY defaultClientFor.description "Make &brandShortName; the default application for:">
+<!ENTITY setDefaultBrowser.label "Browser">
+<!ENTITY setDefaultBrowser.accesskey "B">
+<!ENTITY setDefaultMail.label "Mail">
+<!ENTITY setDefaultMail.accesskey "M">
+<!ENTITY setDefaultNews.label "News">
+<!ENTITY setDefaultNews.accesskey "N">
+<!ENTITY setDefaultFeed.label "Feeds">
+<!ENTITY setDefaultFeed.accesskey "F">
+<!ENTITY useInternalSettings.description "Use &brandShortName; Mail &amp; News when opening browser links for:">
+<!ENTITY useInternalMail.label "Mail (mailto:)">
+<!ENTITY useInternalMail.accesskey "a">
+<!ENTITY useInternalNews.label "News (news:, snews:, nntp:)">
+<!ENTITY useInternalNews.accesskey "e">
+
+<!ENTITY printing.label "Printing">
+<!ENTITY useNativePrintDialog.label "Use Native Print Dialog (where supported)">
+<!ENTITY useNativePrintDialog.accesskey "U">
+<!ENTITY useGlobalPrintSettings.label "Use Global Print Settings">
+<!ENTITY useGlobalPrintSettings.accesskey "G">
+
+<!ENTITY crashReports.caption "Crash Reporter">
+<!ENTITY submitCrashes.label "Submit crash reports">
+<!ENTITY submitCrashes.accesskey "S">
+
+<!ENTITY devTools.caption "Developer Tools">
+<!ENTITY allowDebugger.label "Allow a debugger to connect to &brandShortName;">
+<!ENTITY allowDebugger.accesskey "d">
+<!ENTITY allowRemoteConnections.label "Allow connections from other computers">
+<!ENTITY allowRemoteConnections.accesskey "o">
+<!ENTITY connectionPrompt.label "Prompt for incoming connections">
+<!ENTITY connectionPrompt.accesskey "P">
+<!ENTITY remoteDebuggerPort.label "Port number for connection:">
+<!ENTITY remoteDebuggerPort.accesskey "r">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-appearance.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-appearance.dtd
new file mode 100644
index 0000000000..515f88f0cc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-appearance.dtd
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE The Appearance prefs dialog. -->
+
+<!ENTITY pref.appearance.title "Appearance">
+<!--LOCALIZATION NOTE (onStartLegend.label): Don't translate "&brandShortName;".
+ Place &brandShortName; in the phrase where the name of the application should appear
+-->
+<!ENTITY onStartLegend.label "When &brandShortName; starts up, open">
+<!ENTITY navCheck.label "Browser">
+<!ENTITY navCheck.accesskey "B">
+<!ENTITY showToolsLegend.label "Show toolbars as">
+<!ENTITY picsNtextRadio.label "Pictures and text">
+<!ENTITY picsNtextRadio.accesskey "P">
+<!ENTITY picsOnlyRadio.label "Pictures only">
+<!ENTITY picsOnlyRadio.accesskey "o">
+<!ENTITY textonlyRadio.label "Text only">
+<!ENTITY textonlyRadio.accesskey "x">
+<!ENTITY showHideTooltips.label "Show Tooltips">
+<!ENTITY showHideTooltips.accesskey "T">
+<!ENTITY showHideGrippies.label "Hide Toolbar Grippies">
+<!ENTITY showHideGrippies.accesskey "G">
+
+<!ENTITY pref.locales.title "User Interface Language">
+<!ENTITY selectLocale.label "Select the language for text that appears in dialog boxes, menus, toolbars and button labels:">
+
+<!--LOCALIZATION NOTE (dateTimeFormatting.label): labels/accesskeys for
+ radiobuttons are set dynamically from prefutilities.properties
+-->
+<!ENTITY dateTimeFormatting.label "Date and Time Formatting">
+
+<!--LOCALIZATION NOTE (restartOnLocaleChange.label): Don't translate "&brandShortName;".
+ Place &brandShortName; in the phrase where the name of the application should appear
+-->
+<!ENTITY restartOnLocaleChange.label "Language and formatting preferences will take effect when you restart &brandShortName;.">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.dtd
new file mode 100644
index 0000000000..86883d0e39
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY appManager.title "Application details">
+<!ENTITY appManager.style "width: 40ch; min-height: 20em;">
+<!ENTITY remove.label "Remove">
+<!ENTITY remove.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.properties b/comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.properties
new file mode 100644
index 0000000000..0d35bd49f9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-applicationManager.properties
@@ -0,0 +1,10 @@
+# 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/.
+
+descriptionHandleProtocol=The following applications can be used to handle %S links
+descriptionHandleWebFeeds=The following applications can be used to handle Web Feeds
+descriptionHandleFile=The following applications can be used to handle %S content
+
+descriptionWebApp=This web application is hosted at:
+descriptionLocalApp=This application is located at:
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-applications.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-applications.dtd
new file mode 100644
index 0000000000..c0259d769e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-applications.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE The Applications prefs dialog -->
+<!ENTITY pref.applications.title "Helper Applications">
+
+<!ENTITY typeColumn.label "Content Type">
+<!ENTITY typeColumn.accesskey "T">
+
+<!ENTITY actionColumn2.label "Action">
+<!ENTITY actionColumn2.accesskey "A">
+
+<!ENTITY search.placeholder "Search Types and Actions">
+
+<!ENTITY useAppChooser.label "Use native application chooser when available">
+<!ENTITY useAppChooser.accesskey "U">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-applications.properties b/comm/suite/locales/en-US/chrome/common/pref/pref-applications.properties
new file mode 100644
index 0000000000..48098c6bbe
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-applications.properties
@@ -0,0 +1,34 @@
+# 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/.
+
+#### Applications
+
+fileEnding=%S file
+saveFile=Save File
+
+# LOCALIZATION NOTE (useApp, useDefault): %S = Application name
+useApp=Use %S
+useDefault=Use %S (default)
+
+useOtherApp=Use other…
+fpTitleChooseApp=Select Helper Application
+manageApp=Application Details…
+webFeed=Web Feed
+videoPodcastFeed=Video Podcast
+audioPodcastFeed=Podcast
+alwaysAsk=Always ask
+
+# LOCALIZATION NOTE (usePluginIn):
+# %1$S = plugin name (for example "QuickTime Plugin-in 7.2")
+# %2$S = brandShortName from brand.properties (for example "Minefield")
+usePluginIn=Use %S (in %S)
+
+# LOCALIZATION NOTE (previewInApp, addNewsBlogsInApp): %S = brandShortName
+previewInApp=Preview in %S
+addNewsBlogsInApp=Subscribe in %S
+
+# LOCALIZATION NOTE (typeDescriptionWithType):
+# %1$S = type description (for example "Portable Document Format")
+# %2$S = type (for example "application/pdf")
+typeDescriptionWithType=%S (%S)
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-cache.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-cache.dtd
new file mode 100644
index 0000000000..673ab038a4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-cache.dtd
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-cache.xul -->
+
+<!--LOCALIZATION NOTE : FILE Cache prefs dialog -->
+<!ENTITY pref.cache.title "Cache">
+<!ENTITY pref.cache.caption "Set Cache Options">
+<!ENTITY cachePara "The cache keeps copies of frequently visited web pages on your hard disk. (Clicking Reload always shows you the latest version of a page.)">
+<!ENTITY cacheCheck.label "Let &brandShortName; manage the size of my cache">
+<!ENTITY cacheCheck.accesskey "L">
+<!ENTITY diskCacheUpTo.label "Use up to">
+<!ENTITY diskCacheUpTo.accesskey "U">
+<!ENTITY spaceMbytes "MB of disk space for the cache">
+<!ENTITY diskCacheFolder.label "Cache Folder Location:">
+<!ENTITY clearDiskCache.label "Clear Cache">
+<!ENTITY clearDiskCache.accesskey "C">
+<!ENTITY chooseDiskCacheFolder.label "Choose Folder…">
+<!ENTITY chooseDiskCacheFolder.accesskey "F">
+<!ENTITY diskCacheFolderExplanation "Cache files will be stored in a subfolder named &quot;Cache&quot; of the directory you specify. Restart &brandShortName; for changes to take effect.">
+<!ENTITY docCache.label "Compare the page in the cache to the page on the network:">
+<!ENTITY docCache.accesskey "m">
+<!ENTITY checkOncePerSession.label "Once per session">
+<!ENTITY checkEveryTime.label "Every time I view the page">
+<!ENTITY checkNever.label "Never">
+<!ENTITY checkAutomatically.label "When the page is out of date">
+
+<!ENTITY prefetchTitle.label "Link Prefetching">
+<!ENTITY enablePrefetch.label "Prefetch web pages when idle, so that links in web pages designed for prefetching can load more quickly">
+<!ENTITY enablePrefetch.accesskey "P">
+
+<!ENTITY debugCache.label "Debugging">
+<!ENTITY debugEnableMemCache.label "Enable Memory Cache">
+<!ENTITY debugEnableMemCache.accesskey "E">
+<!ENTITY debugEnableDiskCache.label "Enable Disk Cache">
+<!ENTITY debugEnableDiskCache.accesskey "D">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-certs.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-certs.dtd
new file mode 100755
index 0000000000..3e2fccd3b2
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-certs.dtd
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+<!ENTITY managecerts.caption "Manage Certificates">
+<!ENTITY managecerts.text "Use the Certificate Manager to manage your personal certificates, as well as those of other people and certificate authorities.">
+<!ENTITY managecerts.button "Manage Certificates…">
+<!ENTITY managecerts.accesskey "M">
+<!ENTITY managedevices.caption "Manage Security Devices">
+<!ENTITY managedevices.text "Use this button to manage your security devices, such as smart cards.">
+<!ENTITY managedevices.button "Manage Security Devices…">
+<!ENTITY managedevices.accesskey "S">
+
+<!ENTITY ssl.label "SSL">
+
+<!ENTITY pref.certs.title "Certificates">
+<!ENTITY certs.label "Certificates">
+
+<!ENTITY validation.ocsp.caption "OCSP">
+<!ENTITY enableOCSP.label "Use the Online Certificate Status Protocol (OCSP) to confirm the current validity of certificates">
+<!ENTITY enableOCSP.accesskey "U">
+<!ENTITY validation.requireOCSP.description "When an OCSP server connection fails, treat the certificate as invalid">
+<!ENTITY validation.requireOCSP.accesskey "W">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-colors.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-colors.dtd
new file mode 100644
index 0000000000..e498b1a330
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-colors.dtd
@@ -0,0 +1,35 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-colors.xul -->
+
+<!-- LOCALIZATION NOTE : FILE Color management prefs -->
+<!ENTITY pref.colors.title "Colors">
+<!ENTITY color "Text and Background">
+<!ENTITY textColor.label "Text:">
+<!ENTITY textColor.accesskey "T">
+<!ENTITY backgroundColor.label "Background:">
+<!ENTITY backgroundColor.accesskey "B">
+<!ENTITY useSystemColors.label "Use system colors">
+<!ENTITY useSystemColors.accesskey "s">
+
+<!ENTITY underlineLinks.label "Underline links">
+<!ENTITY underlineLinks.accesskey "U">
+<!ENTITY links "Link Colors">
+<!ENTITY linkColor.label "Unvisited Links:">
+<!ENTITY linkColor.accesskey "L">
+<!ENTITY activeLinkColor.label "Active Links:">
+<!ENTITY activeLinkColor.accesskey "c">
+<!ENTITY visitedLinkColor.label "Visited Links:">
+<!ENTITY visitedLinkColor.accesskey "V">
+<!-- LOCALIZATION NOTE (someProvColors): This is the start of a sentence and will be followed by the following radio buttons. -->
+<!ENTITY someProvColors "When a web page provides its own colors and background">
+<!-- LOCALIZATION NOTE (alwaysUseDocColors.label): This is option one, and it appends to 'someProvColors' above. -->
+<!ENTITY alwaysUseDocumentColors.label "Always use the colors and background specified by the web page">
+<!ENTITY alwaysUseDocumentColors.accesskey "A">
+<!-- LOCALIZATION NOTE (ignoreDocColors.label): This is option two, and it appends to 'someProvColors' above. -->
+<!ENTITY useMyColors.label "Use my chosen colors, ignoring the colors and background image specified">
+<!ENTITY useMyColors.accesskey "m">
+<!ENTITY automaticColors.label "Only ignore the page colors when using a High Contrast theme">
+<!ENTITY automaticColors.accesskey "O">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-content.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-content.dtd
new file mode 100644
index 0000000000..49ef0a6e7d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-content.dtd
@@ -0,0 +1,39 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE The Appearance > Content prefs dialog. -->
+
+<!ENTITY pref.content.title "Content">
+<!ENTITY pref.content.description "These settings influence how website and message content appears in &brandShortName;.">
+
+<!ENTITY scrolling.label "Scrolling">
+<!ENTITY useAutoScroll.label "Use autoscrolling">
+<!ENTITY useAutoScroll.accesskey "t">
+<!ENTITY useSmoothScroll.label "Use smooth scrolling">
+<!ENTITY useSmoothScroll.accesskey "U">
+
+<!ENTITY zoomPrefs.label "Zoom options">
+<!-- LOCALIZATION NOTE (minZoom.label, maxZoom.label, percent.label):
+ single hbox with minZoom.label preceding the minZoom control and
+ maxZoom.label preceding the maxZoom control; percent.label shows
+ the ASCII '%' character at the end of that string -->
+<!ENTITY minZoom.label "Range from">
+<!ENTITY minZoom.accesskey "f">
+<!ENTITY maxZoom.label "to">
+<!ENTITY maxZoom.accesskey "o">
+<!ENTITY percent.label "&#37;">
+<!ENTITY defaultZoom.label "Default zoom">
+<!ENTITY defaultZoom.accesskey "D">
+
+<!ENTITY textZoomOnly.label "Zoom only text instead of full pages">
+<!ENTITY textZoomOnly.accesskey "Z">
+<!ENTITY siteSpecific.label "Remember zoom levels on per-site basis">
+<!ENTITY siteSpecific.accesskey "m">
+<!ENTITY showZoomStatusPanel.label "Show zoom controls in the status bar">
+<!ENTITY showZoomStatusPanel.accesskey "S">
+<!ENTITY enableAutomaticImageResizing.label "Resize large images to fit in the browser window">
+<!ENTITY enableAutomaticImageResizing.accesskey "R">
+
+<!ENTITY allowHWAccel.label "Use hardware acceleration when available">
+<!ENTITY allowHWAccel.accesskey "e">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-cookies.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-cookies.dtd
new file mode 100644
index 0000000000..e6dad7b5a7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-cookies.dtd
@@ -0,0 +1,39 @@
+<!-- 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/. -->
+
+<!ENTITY pref.cookies.title "Cookies">
+
+<!-- cookies -->
+
+<!ENTITY cookiePolicy.label "Cookie Acceptance Policy">
+
+<!ENTITY disableCookies.label "Block cookies">
+<!ENTITY disableCookies.accesskey "B">
+
+<!ENTITY accNo3rdPartyCookies.label "Allow cookies for the originating website only (no third-party cookies)">
+<!ENTITY accNo3rdPartyCookies.accesskey "o">
+
+<!ENTITY acc3rdPartyVisited.label "Allow third-party cookies for previously visited websites only">
+<!ENTITY acc3rdPartyVisited.accesskey "v">
+
+<!ENTITY accAllCookies.label "Allow all cookies">
+<!ENTITY accAllCookies.accesskey "c">
+
+<!ENTITY cookieRetentionPolicy.label "Cookie Retention Policy">
+
+<!ENTITY acceptNormally.label "Accept cookies normally">
+<!ENTITY acceptNormally.accesskey "n">
+
+<!ENTITY acceptForSession.label "Accept for current session only">
+<!ENTITY acceptForSession.accesskey "s">
+
+<!ENTITY acceptforNDays.label "Accept cookies for">
+<!ENTITY acceptforNDays.accesskey "f">
+<!ENTITY days.label "days">
+
+<!ENTITY manageCookies.label "Manage Cookies and Sites">
+<!ENTITY manageCookiesDescription.label "Allows you to view and manage stored cookies and per-website settings for accepting and rejecting cookies. Per-website settings will override the settings above.">
+
+<!ENTITY viewCookies.label "Cookie Manager">
+<!ENTITY viewCookies.accesskey "M">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-debugging.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-debugging.dtd
new file mode 100644
index 0000000000..d70bab5e8c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-debugging.dtd
@@ -0,0 +1,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/. -->
+
+<!-- extracted from pref-eventdebugging.xul -->
+
+<!ENTITY pref.debugging.title "Debugging">
+
+<!-- Render Debugging -->
+<!ENTITY debugRendering.label "Rendering">
+<!ENTITY debugXULBox.label "Debug XUL boxes">
+<!ENTITY debugXULBox.accesskey "X">
+<!ENTITY debugDisableXULCache.label "Disable XUL Cache">
+<!ENTITY debugDisableXULCache.accesskey "L">
+
+<!-- Event Debugging -->
+<!ENTITY debugEvents.label "Events">
+<!ENTITY debugPaintFlashing.label "Paint Flashing (Caps-Lock toggles)">
+<!ENTITY debugPaintFlashing.accesskey "P">
+<!ENTITY debugPaintFlashingChrome.label "Paint Flashing Chrome">
+<!ENTITY debugPaintFlashingChrome.accesskey "F">
+<!ENTITY debugPaintDumping.label "Paint Dumping">
+<!ENTITY debugPaintDumping.accesskey "a">
+<!ENTITY debugInvalidateDumping.label "Invalidate Dumping">
+<!ENTITY debugInvalidateDumping.accesskey "n">
+<!ENTITY debugEventDumping.label "Event Dumping">
+<!ENTITY debugEventDumping.accesskey "E">
+<!ENTITY debugMotionEventDumping.label "Motion Event Dumping">
+<!ENTITY debugMotionEventDumping.accesskey "M">
+<!ENTITY debugCrossingEventDumping.label "Crossing Event Dumping">
+<!ENTITY debugCrossingEventDumping.accesskey "C">
+
+<!-- Reflow Event Debugging -->
+<!ENTITY debugReflowEvents.label "Reflow Events">
+<!ENTITY debugReflowShowFrameCounts.label "Show frame counts">
+<!ENTITY debugReflowShowFrameCounts.accesskey "S">
+<!ENTITY debugReflowDumpFrameCounts.label "Dump frame counts">
+<!ENTITY debugReflowDumpFrameCounts.accesskey "D">
+<!ENTITY debugReflowDumpFrameByFrameCounts.label "Dump frame by frame counts">
+<!ENTITY debugReflowDumpFrameByFrameCounts.accesskey "u">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-download.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-download.dtd
new file mode 100644
index 0000000000..2a9e545dfa
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-download.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!ENTITY pref.download.title "Downloads">
+
+<!ENTITY downloadBehavior.label "When starting a download">
+<!ENTITY doNothing.label "Don't open anything">
+<!ENTITY doNothing.accesskey "D">
+<!ENTITY openProgressDialog.label "Open a progress dialog">
+<!ENTITY openProgressDialog.accesskey "O">
+<!ENTITY openDM.label "Open the download manager">
+<!ENTITY openDM.accesskey "m">
+<!ENTITY flashWhenOpen.label "Just flash the download manager if it is already open">
+<!ENTITY flashWhenOpen.accesskey "f">
+
+<!ENTITY downloadLocation.label "When saving a file">
+<!ENTITY saveTo.label "Save files to">
+<!ENTITY saveTo.accesskey "v">
+<!ENTITY chooseDownloadFolder.label "Choose Folder…">
+<!ENTITY chooseDownloadFolder.accesskey "C">
+<!ENTITY alwaysAsk.label "Always ask me where to save files">
+<!ENTITY alwaysAsk.accesskey "A">
+
+<!ENTITY finishedBehavior.label "When a download completes">
+<!ENTITY playSound.label "Play a sound">
+<!ENTITY playSound.accesskey "P">
+<!ENTITY browse.label "Browse…">
+<!ENTITY browse.accesskey "B">
+<!ENTITY playButton.label "Play">
+<!ENTITY playButton.accesskey "l">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-findasyoutype.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-findasyoutype.dtd
new file mode 100644
index 0000000000..dc55b6f0bb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-findasyoutype.dtd
@@ -0,0 +1,21 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY pref.findAsYouType.title "Find As You Type">
+<!ENTITY findAsYouTypeBehavior.label "Find As You Type">
+<!ENTITY findAsYouTypeTip.label "Tip: To manually start Find As You Type, type / to find text or ' to find links, followed by the text you want to find.">
+<!ENTITY findAsYouTypeTimeout.label "Clear the current search after a few seconds of inactivity">
+<!ENTITY findAsYouTypeTimeout.accesskey "C">
+<!ENTITY findAsYouTypeSound.label "Play a sound when typed text isn't found">
+<!ENTITY findAsYouTypeSound.accesskey "P">
+<!ENTITY findAsYouTypeEnableAuto.label "Find automatically when typing within a web page:">
+<!ENTITY findAsYouTypeEnableAuto.accesskey "F">
+<!ENTITY findAsYouTypeAutoText.label "Any text in the page">
+<!ENTITY findAsYouTypeAutoText.accesskey "A">
+<!ENTITY findAsYouTypeAutoLinks.label "Links only">
+<!ENTITY findAsYouTypeAutoLinks.accesskey "L">
+
+<!ENTITY findAsYouTypeFindbarEnable.label "Show the find toolbar during find as you type">
+<!ENTITY findAsYouTypeFindbarEnable.accesskey "S">
+<!ENTITY findAsYouTypeFindbarEnableTip.label "Note: Find as you type without showing the findbar does not allow international text entry.">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-fonts.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-fonts.dtd
new file mode 100644
index 0000000000..54559aa41c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-fonts.dtd
@@ -0,0 +1,79 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-fonts.xul -->
+
+<!-- LOCALIZATION NOTE : FILE 'Fonts' prefs dialog -->
+<!ENTITY pref.fonts.title "Fonts">
+
+<!ENTITY language.label "Fonts for:">
+<!ENTITY language.accesskey "t">
+
+<!ENTITY typefaces.label "Typeface">
+<!ENTITY sizes.label "Size (pixels)">
+
+<!ENTITY proportional.label "Proportional:">
+<!ENTITY proportional.accesskey "P">
+
+<!ENTITY serif.label "Serif:">
+<!ENTITY serif.accesskey "S">
+<!ENTITY sans-serif.label "Sans-serif:">
+<!ENTITY sans-serif.accesskey "n">
+<!ENTITY monospace.label "Monospace:">
+<!ENTITY monospace.accesskey "M">
+<!-- LOCALIZATION NOTE (fantasy.label): 'Fantasy' means 'Ornate' -->
+<!ENTITY fantasy.label "Fantasy:">
+<!ENTITY fantasy.accesskey "F">
+<!ENTITY cursive.label "Cursive:">
+<!ENTITY cursive.accesskey "C">
+
+<!-- LOCALIZATION NOTE (font.langGroup.latin) :
+ Translate "Latin" as the name of Latin (Roman) script, not as the name of
+ the Latin language. -->
+<!ENTITY font.langGroup.latin "Latin">
+<!ENTITY font.langGroup.japanese "Japanese">
+<!ENTITY font.langGroup.trad-chinese "Traditional Chinese(Taiwan)">
+<!ENTITY font.langGroup.simpl-chinese "Simplified Chinese">
+<!ENTITY font.langGroup.trad-chinese-hk "Traditional Chinese(Hong Kong)">
+<!ENTITY font.langGroup.korean "Korean">
+<!ENTITY font.langGroup.cyrillic "Cyrillic">
+<!ENTITY font.langGroup.el "Greek">
+<!ENTITY font.langGroup.thai "Thai">
+<!ENTITY font.langGroup.hebrew "Hebrew">
+<!ENTITY font.langGroup.arabic "Arabic">
+<!ENTITY font.langGroup.devanagari "Devanagari">
+<!ENTITY font.langGroup.tamil "Tamil">
+<!ENTITY font.langGroup.armenian "Armenian">
+<!ENTITY font.langGroup.bengali "Bengali">
+<!ENTITY font.langGroup.canadian "Unified Canadian Syllabary">
+<!ENTITY font.langGroup.ethiopic "Ethiopic">
+<!ENTITY font.langGroup.georgian "Georgian">
+<!ENTITY font.langGroup.gujarati "Gujarati">
+<!ENTITY font.langGroup.gurmukhi "Gurmukhi">
+<!ENTITY font.langGroup.khmer "Khmer">
+<!ENTITY font.langGroup.malayalam "Malayalam">
+<!ENTITY font.langGroup.math "Mathematics">
+<!ENTITY font.langGroup.odia "Odia">
+<!ENTITY font.langGroup.telugu "Telugu">
+<!ENTITY font.langGroup.kannada "Kannada">
+<!ENTITY font.langGroup.sinhala "Sinhala">
+<!ENTITY font.langGroup.tibetan "Tibetan">
+<!ENTITY font.langGroup.other "Other Writing Systems">
+<!-- Minimum font size -->
+<!ENTITY minSize.label "Minimum font size:">
+<!ENTITY minSize.accesskey "z">
+<!ENTITY minSize.none "None">
+
+<!-- default font type -->
+<!ENTITY useDefaultFontSerif.label "Serif">
+<!ENTITY useDefaultFontSansSerif.label "Sans Serif">
+
+<!ENTITY useDocumentFonts.label "Allow documents to use other fonts">
+<!ENTITY useDocumentFonts.accesskey "o">
+
+<!-- leaving this stuff in for now -->
+
+<!ENTITY header2 "When a webpage chooses its own fonts">
+<!ENTITY useDefaultFont.label "Use my default fonts, ignoring the fonts the page has chosen">
+<!ENTITY useDefaultFont.accesskey "U">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-history.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-history.dtd
new file mode 100644
index 0000000000..7f0a64adbf
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-history.dtd
@@ -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/. -->
+
+<!ENTITY pref.history.title "History">
+
+<!ENTITY pref.history.caption "Browsing History">
+<!ENTITY historyPages.label "History is a list of previously visited pages.">
+<!ENTITY enableHistory.label "Remember visited pages">
+<!ENTITY enableHistory.accesskey "R">
+<!ENTITY clearHistory.label "Clear History">
+<!ENTITY clearHistory.accesskey "s">
+
+<!ENTITY locationBarHistory.caption "Location Bar History">
+<!ENTITY urlBarHistoryEnabled.caption "Enable Location Bar history">
+<!ENTITY urlBarHistoryEnabled.accesskey "E">
+<!ENTITY clearLocationBar.label "Clear the list of websites stored in the location bar menu.">
+<!ENTITY clearLocationBarButton.label "Clear Location Bar">
+<!ENTITY clearLocationBarButton.accesskey "L">
+<!ENTITY formfillHistory.caption "Form and Search History">
+<!ENTITY enableFormfill.label "Enable form and search history">
+<!ENTITY enableFormfill.accesskey "n">
+<!ENTITY formfillExpire.label "Remember form and search history for up to">
+<!ENTITY formfillExpire.accesskey "f">
+<!ENTITY formfillDays.label "days">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-http.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-http.dtd
new file mode 100644
index 0000000000..530c81f913
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-http.dtd
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<!ENTITY pref.http.title "HTTP Networking">
+
+<!-- Network-->
+<!ENTITY prefDirect.label "Direct Connection Options">
+<!ENTITY prefProxy.label "Proxy Connection Options">
+<!ENTITY prefEnableHTTP10.label "Use HTTP 1.0">
+<!ENTITY prefEnableHTTP10.accesskey "U">
+<!ENTITY prefEnableHTTP10Proxy.accesskey "S">
+<!ENTITY prefEnableHTTP11.label "Use HTTP 1.1">
+<!ENTITY prefEnableHTTP11.accesskey "E">
+<!ENTITY prefEnableHTTP11Proxy.accesskey "T">
+<!ENTITY prefPara "HTTP connections may be fine-tuned using these options to enhance either performance or compatibility. Some proxy servers, for example, are known to require HTTP/1.0 (see the release notes for details).">
+<!ENTITY prefUseragent.label "User Agent String">
+<!ENTITY prefFirefoxStrict.label "Identify as Firefox">
+<!ENTITY prefFirefoxStrict.accesskey "I">
+<!ENTITY prefFirefoxNone.label "Identify as SeaMonkey">
+<!ENTITY prefFirefoxNone.accesskey "M">
+<!ENTITY prefFirefoxCompat2.label "Identify as SeaMonkey and advertise Firefox compatibility">
+<!ENTITY prefFirefoxCompat2.accesskey "F">
+<!ENTITY prefCompatWarning2.desc "WARNING: changing this setting may result in websites or services not working properly.">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-images.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-images.dtd
new file mode 100644
index 0000000000..26b9b15331
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-images.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY pref.images.title "Images">
+
+<!ENTITY imageBlocking.label "Image Acceptance Policy">
+
+<!--LOCALIZATION NOTE (onStartLegend.label): Don't translate "&brandShortName;".
+ Place "&brandShortName;" in the phrase where the name of the application should
+ appear
+-->
+<!ENTITY imageDetails "Specify how &brandShortName; handles images.">
+
+<!ENTITY loadAllImagesRadio.label "Load all images">
+<!ENTITY loadAllImagesRadio.accesskey "L">
+<!ENTITY loadOrgImagesRadio.label "Only load images that come from the originating server">
+<!ENTITY loadOrgImagesRadio.accesskey "n">
+<!ENTITY loadNoImagesRadio.label "Do not load any images">
+<!ENTITY loadNoImagesRadio.accesskey "D">
+<!ENTITY viewPermissions.label "Manage Permissions">
+<!ENTITY viewPermissions.accesskey "P">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-keynav.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-keynav.dtd
new file mode 100644
index 0000000000..1989cdb69b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-keynav.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<!ENTITY pref.keyNav.title "Keyboard Navigation">
+<!ENTITY tabNavigationBehavior.label "Tab Key Navigation">
+<!ENTITY tabNavigationLinks.label "Links">
+<!ENTITY tabNavigationLinks.accesskey "L">
+<!ENTITY tabNavigationForms.label "Buttons, radio buttons, checkboxes, and selection lists">
+<!ENTITY tabNavigationForms.accesskey "B">
+<!ENTITY tabNavigationTextboxes.label "Note: text boxes and scrollable regions are always part of the tabbing order.">
+<!ENTITY tabNavigationDesc.label "When Tab or Shift+Tab is pressed, move between:">
+<!ENTITY accessibilityBrowseWithCaret.label "Browse With Caret">
+<!ENTITY browseWithCaretDesc.label "Caret browsing enables you to navigate and select within pages using the cursor keys to move a visible caret.">
+<!ENTITY browseWithCaretUse.label "Use caret browsing">
+<!ENTITY browseWithCaretUse.accesskey "U">
+<!ENTITY browseWithCaretShortCut.label "Use the F7 shortcut to toggle caret browsing">
+<!ENTITY browseWithCaretShortCut.accesskey "F">
+<!ENTITY browseWithCaretWarn.label "Warn me before turning on caret browsing">
+<!ENTITY browseWithCaretWarn.accesskey "W">
+
+<!ENTITY modifiers.label "Modifiers">
+<!ENTITY acceleratorKey.label "Accelerator key:">
+<!ENTITY acceleratorKey.accesskey "A">
+<!ENTITY menuAccessKey.label "Menu access key:">
+<!ENTITY menuAccessKey.accesskey "M">
+<!ENTITY modifiersDesc.label "Note: A value of zero disables a modifier key.">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-languages.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-languages.dtd
new file mode 100644
index 0000000000..f9e425c797
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-languages.dtd
@@ -0,0 +1,30 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE : FILE 'Languages' preferences dialog -->
+
+<!ENTITY languages.customize.prefLangDescript "Web pages are sometimes available in more than one language. Choose languages for displaying web pages, in order of preference.">
+<!ENTITY languages.customize.title "Languages">
+<!ENTITY langtitle.label "Languages for Web Pages">
+<!ENTITY languages.customize.prefAddLangDescript "Select the language(s) you would like to add.">
+<!ENTITY languages.customize.addButton.label "Add…">
+<!ENTITY languages.customize.addButton.accesskey "A">
+<!ENTITY languages.customize.deleteButton.label "Remove">
+<!ENTITY languages.customize.deleteButton.accesskey "R">
+<!ENTITY languages.customize.add.title.label "Add Languages">
+<!ENTITY languages.customize.available.label "Languages:">
+<!ENTITY languages.customize.active.label "Languages in order of preference:">
+<!ENTITY languages.customize.active.accesskey "L">
+<!ENTITY languages.customize.others.label "Others:">
+<!ENTITY languages.customize.others.accesskey "O">
+<!ENTITY languages.customize.Fallback2.grouplabel "Text Encoding for Legacy Content">
+<!ENTITY languages.customize.Fallback2.label "Fallback Text Encoding:">
+<!ENTITY languages.customize.Fallback2.accesskey "T">
+<!ENTITY languages.customize.Fallback2.desc "Used for legacy content that fails to declare its encoding.">
+<!-- LOCALIZATION NOTE Character Encoding Preferences Dialog: Do NOT localize the terms "en-bz, ar-jo" -->
+<!ENTITY languages.customize.others.examples "e.g.: en-bz, ar-jo">
+<!ENTITY languages.customize.moveUp.label "Move Up">
+<!ENTITY languages.customize.moveUp.accesskey "U">
+<!ENTITY languages.customize.moveDown.label "Move Down">
+<!ENTITY languages.customize.moveDown.accesskey "D">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-languages.properties b/comm/suite/locales/en-US/chrome/common/pref/pref-languages.properties
new file mode 100644
index 0000000000..a77e020f2a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-languages.properties
@@ -0,0 +1,13 @@
+# 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/.
+
+illegalOtherLanguage=The following entries were not valid language codes:
+illegalOtherLanguageTitle=Invalid language code(s)
+# LOCALIZATION NOTE: Next two strings are for language name representations with
+# and without the region.
+# e.g. languageRegionCodeFormat : "French/Canada [fr-ca]" languageCodeFormat : "French [fr]"
+# %1$S = language name, %2$S = region name, %3$S = language-region code
+languageRegionCodeFormat=%1$S/%2$S [%3$S]
+# %1$S = language name, %2$S = language-region code
+languageCodeFormat=%1$S [%2$S]
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-links.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-links.dtd
new file mode 100644
index 0000000000..97c3bc097d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-links.dtd
@@ -0,0 +1,29 @@
+<!-- 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/. -->
+
+<!ENTITY linksHeader.label "Link Behavior">
+<!ENTITY newWindow.label "Link open behavior">
+<!ENTITY newWindowDescription.label "Open links meant to open a new window in:">
+<!ENTITY newWindowRestriction.label "When scripts want to open a new window:">
+<!ENTITY external.label "Links from other applications">
+<!ENTITY externalDescription.label "Open links passed from other applications in:">
+
+<!ENTITY openCurrent.label "The current tab/window">
+<!ENTITY newWindowGroupCurrent.accesskey "c">
+<!ENTITY externalGroupCurrent.accesskey "u">
+
+<!ENTITY openTab.label "A new tab in the current window">
+<!ENTITY newWindowGroupTab.accesskey "t">
+<!ENTITY externalGroupTab.accesskey "a">
+
+<!ENTITY openWindow.label "A new window">
+<!ENTITY newWindowGroupWindow.accesskey "w">
+<!ENTITY externalGroupWindow.accesskey "o">
+
+<!ENTITY divertAll.label "Always divert windows into tabs">
+<!ENTITY divertAll.accesskey "d">
+<!ENTITY divertNoFeatures.label "Don't divert custom windows into tabs">
+<!ENTITY divertNoFeatures.accesskey "s">
+<!ENTITY dontDivert.label "Always open new windows">
+<!ENTITY dontDivert.accesskey "n">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-locationbar.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-locationbar.dtd
new file mode 100644
index 0000000000..6049d17a1f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-locationbar.dtd
@@ -0,0 +1,42 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE The Location Bar prefs panel -->
+<!ENTITY pref.locationBar.title "Location Bar">
+
+<!ENTITY autoComplete.label "Autocomplete">
+<!ENTITY autoCompleteMatchHistory.label "Automatically suggest websites from History">
+<!ENTITY autoCompleteMatchHistory.accesskey "A">
+<!ENTITY autoCompleteMatchBookmarks.label "Automatically suggest websites from Bookmarks">
+<!ENTITY autoCompleteMatchBookmarks.accesskey "B">
+<!ENTITY autoCompleteAutoFill.label "Automatically prefill the best match">
+<!ENTITY autoCompleteAutoFill.accesskey "u">
+<!ENTITY autoCompleteShowPopup.label "Show list of matching results">
+<!ENTITY autoCompleteShowPopup.accesskey "S">
+<!ENTITY autoCompleteMatchOnlyTyped.label "Match only websites you've typed previously">
+<!ENTITY autoCompleteMatchOnlyTyped.accesskey "o">
+<!--LOCALIZATION NOTE (autoCompleteMatch.label): This is the common leading
+ part of the menulist items listed below, mainly to make the control available
+ via the accesskey
+-->
+<!ENTITY autoCompleteMatch.label "Match">
+<!ENTITY autoCompleteMatch.accesskey "M">
+<!ENTITY autoCompleteMatchAnywhere "Anywhere in the location or title">
+<!ENTITY autoCompleteMatchWordsFirst "Anywhere but preferring word boundaries">
+<!ENTITY autoCompleteMatchWords "Only on word boundaries">
+<!ENTITY autoCompleteMatchStart "Only at the beginning of the location or title">
+<!ENTITY showInternetSearch.label "Show default Internet search engine">
+<!ENTITY showInternetSearch.accesskey "e">
+
+<!ENTITY formatting.label "Formatting">
+<!ENTITY domainFormatting.label "Highlight the effective domain for websites and FTP servers">
+<!ENTITY domainFormatting.accesskey "d">
+<!ENTITY highlightSecure.label "Highlight web pages with a high level of connection security">
+<!ENTITY highlightSecure.accesskey "c">
+
+<!ENTITY unknownLocations.label "Unknown Locations">
+<!ENTITY domainGuessing.label "Add &quot;www.&quot; and &quot;.com&quot; to the location if a web page is not found">
+<!ENTITY domainGuessing.accesskey "w">
+<!ENTITY keywords.label "Perform a web search when entered text is not a web location">
+<!ENTITY keywords.accesskey "P">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-masterpass.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-masterpass.dtd
new file mode 100755
index 0000000000..d2e30a3aca
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-masterpass.dtd
@@ -0,0 +1,13 @@
+<!-- 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/. -->
+
+<!ENTITY changepassword.caption "Change Master Password">
+<!ENTITY changepassword.text "Your master password protects sensitive information such as web passwords and certificates.">
+<!ENTITY changepassword.button "Change Password…">
+<!ENTITY changepassword.accesskey "C">
+
+<!ENTITY resetpassword.caption "Reset Master Password">
+<!ENTITY resetpassword.text "If you reset your master password, all of your stored web and e-mail passwords, form data, personal certificates, and private keys will be lost.">
+<!ENTITY resetpassword2.button "Reset Password">
+<!ENTITY resetpassword2.accesskey "P">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-media.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-media.dtd
new file mode 100644
index 0000000000..6099e951fe
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-media.dtd
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-media.xul -->
+
+<!-- LOCALIZATION NOTE : FILE Media management prefs -->
+<!ENTITY pref.media.title "Media">
+<!ENTITY mediaHTML5Preferences.label "Audio/Video">
+<!ENTITY allowMediaAutoplay.label "Enable Autoplay of HTML5 media content">
+<!ENTITY allowMediaAutoplay.accesskey "A">
+
+<!ENTITY enableDrmMedia.label "Enable Digital Rights Management for">
+<!ENTITY enableEmeForSuite.label "Third-party Content Decryption Modules">
+<!ENTITY enableEmeForSuite.accesskey "C">
+
+<!ENTITY animLoopingTitle.label "Animated images should loop">
+<!ENTITY animLoopAsSpecified.label "As many times as the image specifies">
+<!ENTITY animLoopAsSpecified.accesskey "m">
+<!ENTITY animLoopOnce.label "Once">
+<!ENTITY animLoopOnce.accesskey "O">
+<!ENTITY animLoopNever.label "Never">
+<!ENTITY animLoopNever.accesskey "v">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-mousewheel.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-mousewheel.dtd
new file mode 100644
index 0000000000..0117a178bd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-mousewheel.dtd
@@ -0,0 +1,36 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE : FILE Mouse-wheel management prefs, for those lucky OSes that support mouse wheels -->
+
+<!ENTITY pref.mouseWheel.title "Mouse Wheel">
+<!ENTITY mouseWheelPanel.label "Specify mouse wheel behavior when used with this modifier key: ">
+<!ENTITY usingJustTheWheel.label "No modifier key">
+<!-- Key labels, for mousewheel prefs -->
+<!ENTITY usingWheelAndAlt.label2 "Alt">
+<!ENTITY usingWheelAndOption.label "Option">
+<!ENTITY usingWheelAndCtrl.label2 "Ctrl">
+<!ENTITY usingWheelAndShft.label2 "Shift">
+<!ENTITY mouseWheelGroup.label "Vertical scrolling">
+<!ENTITY mouseWheelHorizGroup.label "Horizontal scrolling">
+<!ENTITY sameAsVertical.label "Same as vertical scrolling">
+<!ENTITY sameAsVertical.accesskey "v">
+<!ENTITY doNothing.label "Do nothing">
+<!ENTITY doNothing.accesskey "D">
+<!ENTITY doNothingHoriz.accesskey "n">
+<!ENTITY scrollDocument.label "Scroll the document">
+<!ENTITY scrollDocument.accesskey "S">
+<!ENTITY scrollDocumentHoriz.accesskey "c">
+<!ENTITY history.label "Move back and forward in the browsing history">
+<!ENTITY history.accesskey "M">
+<!ENTITY historyHoriz.accesskey "b">
+<!ENTITY zoom.label "Zoom the page in or out">
+<!ENTITY zoom.accesskey "Z">
+<!ENTITY zoomHoriz.accesskey "o">
+<!ENTITY wheelSpeed.label "Mouse wheel speed:">
+<!ENTITY wheelSpeed.accesskey "w">
+<!ENTITY wheelSpeedHoriz.accesskey "u">
+<!ENTITY reverseDirection.label "Reverse direction">
+<!ENTITY reverseDirection.accesskey "R">
+<!ENTITY reverseDirectionHoriz.accesskey "e">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-navigator.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-navigator.dtd
new file mode 100644
index 0000000000..3efd71cf55
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-navigator.dtd
@@ -0,0 +1,48 @@
+<!-- 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/. -->
+
+<!ENTITY pref.navigator.title "Browser">
+
+<!ENTITY navRadio.label "Display on">
+<!ENTITY navRadio.accesskey "n">
+<!ENTITY navStartPageMenu.label "Browser Startup">
+<!ENTITY newWinPageMenu.label "New Window">
+<!ENTITY newTabPageMenu.label "New Tab">
+
+<!ENTITY blankPageRadio.label "Blank page">
+<!ENTITY blankPageRadio.accesskey "B">
+<!ENTITY homePageRadio.label "Home page">
+<!ENTITY homePageRadio.accesskey "m">
+<!ENTITY lastPageRadio.label "Last page visited">
+<!ENTITY lastPageRadio.accesskey "L">
+<!ENTITY restoreSessionRadio.label "Restore Previous Session">
+<!ENTITY restoreSessionRadio.accesskey "P">
+
+<!ENTITY restoreSessionIntro.label "When restoring sessions and windows">
+<!ENTITY restoreImmediately.label "Restore all tabs immediately">
+<!ENTITY restoreImmediately.accesskey "e">
+<!-- LOCALIZATION NOTE (restoreTabs.label): This will concatenate to
+ "Restore [___] tab(s) at a time",
+ using (restoreTabs.label) and a number (restoreTabsAtATime.label). -->
+<!ENTITY restoreTabs.label "Restore">
+<!ENTITY restoreTabs.accesskey "s">
+<!ENTITY restoreTabsAtATime.label "tab(s) at a time">
+<!ENTITY restoreDeferred.label "Only restore tabs when I need them">
+<!ENTITY restoreDeferred.accesskey "O">
+
+<!ENTITY siteIcons.label "Website icons">
+<!ENTITY useSiteIcons.label "Show website icons">
+<!ENTITY useSiteIcons.accesskey "w">
+<!ENTITY useFavIcons.label "Try to use the server&apos;s favicon when the page doesn&apos;t define an icon">
+<!ENTITY useFavIcons.accesskey "T">
+
+<!ENTITY homePageIntro.label "Clicking the Home button takes you to this group of pages:">
+<!ENTITY useCurrent.label "Use Current Page">
+<!ENTITY useCurrent.accesskey "U">
+<!ENTITY useCurrentGroup.label "Use Current Group">
+<!ENTITY useCurrentGroup.accesskey "G">
+<!ENTITY browseFile.label "Choose File…">
+<!ENTITY browseFile.accesskey "C">
+<!ENTITY useDefault.label "Restore Default">
+<!ENTITY useDefault.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-offlineapps.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-offlineapps.dtd
new file mode 100644
index 0000000000..ca5b5e27e5
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-offlineapps.dtd
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+<!-- extracted from content/pref-offlineapps.xul -->
+
+<!--LOCALIZATION NOTE : FILE Offline Apps prefs dialog -->
+<!ENTITY pref.offlineapps.title "Offline Web Applications">
+<!ENTITY pref.offlineCache.caption "Offline Web Content and User Data">
+
+<!ENTITY clearOfflineAppCache.label "Clear">
+<!ENTITY clearOfflineAppCache.accesskey "C">
+
+<!ENTITY offlineAlwaysAllow.label "Allow all websites to store data for offline use">
+<!ENTITY offlineAlwaysAllow.accesskey "A">
+<!ENTITY offlineExplicit.label "Only allow websites with explicit permissions">
+<!ENTITY offlineExplicit.accesskey "O">
+<!ENTITY offlineNotifyAsk.label "Notify me when websites want to store data for offline use">
+<!ENTITY offlineNotifyAsk.accesskey "N">
+<!ENTITY offlineNotifyPermissions.label "Manage Permissions">
+<!ENTITY offlineNotifyPermissions.accesskey "P">
+
+<!ENTITY offlineAppsUsage.label "The following websites are using offline storage:">
+<!ENTITY offlineAppsListRemove.label "Clear Data…">
+<!ENTITY offlineAppsListRemove.accesskey "D">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-passwords.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-passwords.dtd
new file mode 100644
index 0000000000..f9b2641f13
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-passwords.dtd
@@ -0,0 +1,13 @@
+<!-- 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/. -->
+
+<!ENTITY pref.passwords.title "Passwords">
+
+<!ENTITY signonHeader.caption "Password Manager">
+<!ENTITY signonDescription.label "Password Manager stores your login information for password-protected websites, mail servers, and news servers, and enters the information automatically when needed.">
+
+<!ENTITY signonEnabled.label "Remember passwords">
+<!ENTITY signonEnabled.accesskey "R">
+<!ENTITY viewSignons.label "Manage Stored Passwords">
+<!ENTITY viewSignons.accesskey "M">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-popups.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-popups.dtd
new file mode 100644
index 0000000000..0119de22e6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-popups.dtd
@@ -0,0 +1,34 @@
+<!-- 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/. -->
+
+<!ENTITY pref.popups.title "Popup Windows">
+
+<!ENTITY pref.popups.caption "Popup Windows">
+
+<!ENTITY popupBlock.label "Block unrequested popup windows">
+<!ENTITY popupBlock.accesskey "B">
+
+<!ENTITY viewPermissions.label "Manage Permissions">
+<!ENTITY viewPermissions.accesskey "M">
+
+<!ENTITY whenBlock.description "When a popup window has been blocked:">
+<!ENTITY playSound.label "Play a sound:">
+<!ENTITY playSound.accesskey "P">
+<!ENTITY systemSound.label "System beep">
+<!ENTITY systemSound.accesskey "S">
+<!ENTITY customSound.label "Custom sound file">
+<!ENTITY customSound.accesskey "C">
+
+<!ENTITY selectSound.label "Browse…">
+<!ENTITY selectSound.accesskey "o">
+<!ENTITY playSoundButton.label "Play">
+<!ENTITY playSoundButton.accesskey "l">
+
+<!ENTITY displayIcon.label "Display an icon in the browser status bar">
+<!ENTITY displayIcon.accesskey "D">
+
+<!ENTITY displayNotification.label "Display a notification bar at the top of the content area">
+<!ENTITY displayNotification.accesskey "N">
+
+<!ENTITY popupNote.description "Note: Blocking all popups may prevent important features of some websites from working, such as login windows for banks and shopping websites. For details of how to allow specific websites to use popups while blocking all others, click Help. Even if blocked, websites may use other methods to show popups.">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-privatedata.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-privatedata.dtd
new file mode 100644
index 0000000000..326ba999ad
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-privatedata.dtd
@@ -0,0 +1,33 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE 'Private Data' prefs settings
+ These entities go on top of the sanitize.dtd definitions
+-->
+<!ENTITY pref.privatedata.title "Private Data">
+
+<!ENTITY clearPrivateData.label "Clear Private Data">
+
+<!ENTITY alwaysClear.label "Always clear my private data when I close &brandShortName;">
+<!ENTITY alwaysClear.accesskey "w">
+
+<!--LOCALIZATION NOTE : The askBeforeClear label is
+ no longer used but we might bring it back later
+-->
+<!ENTITY askBeforeClear.label "Ask me before clearing private data">
+
+<!ENTITY clearDataSettings.label "When I ask &brandShortName; to clear my private data, it should erase:">
+
+<!ENTITY clearData.label "Clear these private data items:">
+<!ENTITY clearData.cpd.label "Manually">
+<!ENTITY clearData.onShutdown.label "When closing">
+
+
+<!--LOCALIZATION NOTE (clearDataDialog.label, clearDataSilent.label, clearDataDialog.accesskey):
+ The only difference bettween the two labels is that one calls a dialog, the other doesn't.
+ The same accesskey is used for both labels.
+-->
+<!ENTITY clearDataDialog.label "Clear Now…">
+<!ENTITY clearDataSilent.label "Clear Now">
+<!ENTITY clearDataDialog.accesskey "N">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-proxies-advanced.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-proxies-advanced.dtd
new file mode 100644
index 0000000000..319aacb255
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-proxies-advanced.dtd
@@ -0,0 +1,32 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE The Advanced Proxy Preferences dialog -->
+<!ENTITY pref.proxies.advanced.title "Advanced Proxy Preferences">
+<!ENTITY protocols.caption "Protocol-Specific Proxies">
+<!ENTITY protocols.description "Normally the same proxy can handle all protocols listed here.">
+<!ENTITY http.label "HTTP Proxy:">
+<!ENTITY http.accesskey "x">
+<!ENTITY ssl.label "SSL Proxy:">
+<!ENTITY ssl.accesskey "L">
+<!ENTITY ftp.label "FTP Proxy:">
+<!ENTITY ftp.accesskey "F">
+<!ENTITY reuseProxy.label "Use HTTP Proxy settings for all protocols">
+<!ENTITY reuseProxy.accesskey "U">
+<!ENTITY port.label "Port:">
+<!ENTITY HTTPPort.accesskey "P">
+<!ENTITY SSLPort.accesskey "o">
+<!ENTITY FTPPort.accesskey "r">
+
+<!ENTITY socks.caption "Generic Proxy">
+<!ENTITY socks.description "A SOCKS proxy is a generic proxy sometimes used in corporate or similar environments.">
+<!ENTITY socks.label "SOCKS Proxy:">
+<!ENTITY socks.accesskey "S">
+<!ENTITY socks4.label "SOCKS v4">
+<!ENTITY socks4.accesskey "C">
+<!ENTITY socks5.label "SOCKS v5">
+<!ENTITY socks5.accesskey "K">
+<!ENTITY socksRemoteDNS.label "Use for resolving hostnames (recommended for SOCKS v5)">
+<!ENTITY socksRemoteDNS.accesskey "e">
+<!ENTITY SOCKSport.accesskey "t">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-proxies.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-proxies.dtd
new file mode 100644
index 0000000000..01baaefd77
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-proxies.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-proxies.xul -->
+
+<!--LOCALIZATION NOTE : FILE The Proxies preferences dialog -->
+<!ENTITY pref.proxies.title "Proxies">
+<!ENTITY pref.proxies.desc "A Proxy is a network service that can filter and speed up your Internet connection.">
+<!ENTITY proxyTitle.label "Configure Proxies to Access the Internet">
+<!ENTITY directTypeRadio.label "Direct connection to the Internet">
+<!ENTITY directTypeRadio.accesskey "D">
+<!ENTITY systemTypeRadio.label "Use system proxy settings">
+<!ENTITY systemTypeRadio.accesskey "U">
+<!ENTITY manualTypeRadio.label "Manual proxy configuration:">
+<!ENTITY manualTypeRadio.accesskey "M">
+<!ENTITY wpadTypeRadio.label "Automatically discover the proxy configuration">
+<!ENTITY wpadTypeRadio.accesskey "A">
+<!ENTITY autoTypeRadio.label "Automatic proxy configuration URL:">
+<!ENTITY autoTypeRadio.accesskey "c">
+<!ENTITY reload.label "Reload">
+<!ENTITY reload.accesskey "R">
+<!ENTITY http.label "Proxy:">
+<!ENTITY http.accesskey "P">
+<!ENTITY port.label "Port:">
+<!ENTITY HTTPPort.accesskey "o">
+<!ENTITY advanced.label "Advanced…">
+<!ENTITY advanced.accesskey "v">
+<!ENTITY noproxy.label "No Proxy for:">
+<!ENTITY noproxy.accesskey "N">
+<!ENTITY noproxyExplain.label "Example: .mozilla.org, .net.nz, 192.168.1.0/24">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-scripts.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-scripts.dtd
new file mode 100644
index 0000000000..b0a4bfd4db
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-scripts.dtd
@@ -0,0 +1,29 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-scripts.xul -->
+
+<!--LOCALIZATION NOTE : FILE The 'Scripts' preferences dialog -->
+<!ENTITY pref.scripts2.title "Scripts">
+
+<!ENTITY navigator.label "Browser">
+<!ENTITY navigator.accesskey "B">
+
+<!--LOCALIZATION NOTE (enableJavaScript.label): 'JavaScript' should never be translated -->
+<!ENTITY enableJavaScript.label "Enable JavaScript for">
+<!ENTITY allowScripts.label "Allow scripts to:">
+<!ENTITY allowScripts.accesskey "s">
+<!ENTITY allowWindowMoveResize.label "Move or resize existing windows">
+<!ENTITY allowWindowFlip.label "Raise or lower windows">
+<!ENTITY allowWindowStatusChange.label "Change status bar text">
+<!ENTITY allowContextmenuDisable.label "Disable or replace context menus">
+<!ENTITY allowHideStatusBar.label "Hide the status bar">
+
+<!ENTITY debugging.label "JavaScript Debugging">
+<!ENTITY debugEnableDump.label "Enable JavaScript dump() output">
+<!ENTITY debugEnableDump.accesskey "E">
+<!ENTITY debugStrictJavascript.label "Show strict JavaScript warnings">
+<!ENTITY debugStrictJavascript.accesskey "J">
+<!ENTITY debugConsoleJavascript.label "Show chrome JavaScript errors and warnings">
+<!ENTITY debugConsoleJavascript.accesskey "c">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-search.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-search.dtd
new file mode 100755
index 0000000000..5376bb7425
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-search.dtd
@@ -0,0 +1,21 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- extracted from content/pref-search.xul -->
+
+<!-- LOCALIZATION NOTE : FILE The Search prefs dialog -->
+
+<!ENTITY pref.search.title "Internet Search">
+<!ENTITY legendHeader "Default Search Engine">
+<!ENTITY defaultSearchEngine.label "Search using:">
+<!ENTITY defaultSearchEngine.accesskey "S">
+
+<!ENTITY engineManager.label "Manage Search Engines…">
+
+<!ENTITY searchResults.label "Search Results">
+
+<!ENTITY openInTab.label "Open new tabs for sidebar search results">
+<!ENTITY openInTab.accesskey "n">
+<!ENTITY openContextSearchTab.label "Open a tab instead of a window for a context menu web search">
+<!ENTITY openContextSearchTab.accesskey "t">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-security.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-security.dtd
new file mode 100644
index 0000000000..54afc7df6d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-security.dtd
@@ -0,0 +1,42 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE 'Privacy and Security' prefs settings -->
+<!ENTITY pref.security.title "Privacy &amp; Security">
+
+<!ENTITY tracking.label "User Tracking">
+<!ENTITY trackingIntro.label "Websites may track how you use them, thus affecting your privacy.">
+
+<!ENTITY doNotTrack.label "Tell websites that I do not want to be tracked">
+<!ENTITY doNotTrack.accesskey "T">
+<!ENTITY trackProtect.label "Prevent tracking activities by known sites">
+<!ENTITY trackProtect.accesskey "n">
+<!ENTITY warnTrackContent.label "Warn me when known tracking activities were detected">
+<!ENTITY warnTrackContent.accesskey "W">
+
+<!ENTITY geoLocation.label "Location Aware Browsing">
+<!ENTITY geoIntro.label "Websites may request more information about your current location.">
+
+<!ENTITY geoEnabled.label "Prompt me for permission if a request is made">
+<!ENTITY geoEnabled.accesskey "m">
+<!ENTITY geoDisabled.label "Disable this feature and deny all requests">
+<!ENTITY geoDisabled.accesskey "D">
+
+<!-- LOCALIZATION NOTE (safeBrowsing.label, blockWebForgeries.label, blockAttackSites.label):
+ The methods by which forged (phished) and attack sites will be detected by
+ phishing providers will vary from human review to machine-based heuristics
+ to a combination of both, so it's important that these strings convey the
+ meaning "reported" (and not something like "known").
+-->
+<!ENTITY safeBrowsing.label "Safe Browsing">
+<!ENTITY safeBrowsingIntro.label "&brandShortName; can block websites reported to contain malicious content.">
+
+<!ENTITY blockAttackSites.label "Block reported attack sites (malware, viruses)">
+<!ENTITY blockAttackSites.accesskey "B">
+
+<!ENTITY blockWebForgeries.label "Block reported web forgeries (Phishing)">
+<!ENTITY blockWebForgeries.accesskey "P">
+
+<!ENTITY blockAutoRefresh.label "Warn me when websites try to redirect or reload the page">
+<!ENTITY blockAutoRefresh.accesskey "a">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-smartupdate.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-smartupdate.dtd
new file mode 100644
index 0000000000..22845003cb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-smartupdate.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE : FILE UI for Software Updates prefs -->
+<!ENTITY pref.smartUpdate.title "Software Installation">
+<!ENTITY addOnsTitle.label "Add-ons">
+<!ENTITY addOnsAllow.label "Allow websites to install add-ons and updates">
+<!ENTITY addOnsAllow.accesskey "b">
+<!ENTITY allowedSitesLink.label "Allowed Websites">
+<!ENTITY autoAddOnsUpdates.label "Automatically check for updates">
+<!ENTITY autoAddOnsUpdates.accesskey "o">
+<!ENTITY daily.label "daily">
+<!ENTITY addOnsDaily.accesskey "d">
+<!ENTITY weekly.label "weekly">
+<!ENTITY addOnsWeekly.accesskey "k">
+<!ENTITY addOnsModeAutomatic.label "Automatically download and install the updates">
+<!ENTITY addOnsModeAutomatic.accesskey "m">
+<!ENTITY enablePersonalized.label "Personalize add-on recommendations">
+<!ENTITY enablePersonalized.accesskey "P">
+<!ENTITY addonManagerLink.label "Manage Add-ons">
+
+<!ENTITY appUpdates.caption "&brandShortName;">
+<!ENTITY autoAppUpdates.label "Automatically check for updates">
+<!ENTITY autoAppUpdates.accesskey "t">
+<!ENTITY appDaily.accesskey "a">
+<!ENTITY appWeekly.accesskey "e">
+<!ENTITY appModeAutomatic.label "Automatically download and install the update">
+<!ENTITY appModeAutomatic.accesskey "u">
+<!ENTITY updateHistoryButton.label "Show Update History…">
+<!ENTITY updateHistoryButton.accesskey "S">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-spelling.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-spelling.dtd
new file mode 100644
index 0000000000..2b704cc025
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-spelling.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!ENTITY prefSpelling.title "Spelling">
+<!ENTITY generalSpelling.label "General">
+<!ENTITY checkSpellingWhenTyping.label "When typing, check my spelling:">
+<!ENTITY checkSpellingWhenTyping.accesskey "W">
+<!ENTITY dontCheckSpelling.label "Never">
+<!ENTITY multilineCheckSpelling.label "In multiline boxes">
+<!ENTITY alwaysCheckSpelling.label "All boxes">
+<!ENTITY spellForMailAndNews.label "Mail &amp; Newsgroups">
+<!ENTITY checkSpellingBeforeSend.label "Check spelling before sending">
+<!ENTITY checkSpellingBeforeSend.accesskey "C">
+<!ENTITY spellCheckInline.label "Check spelling as you type">
+<!ENTITY spellCheckInline.accesskey "e">
+<!ENTITY languagePopup.label "Language:">
+<!ENTITY languagePopup.accessKey "L">
+<!ENTITY moreDictionaries.label "Download more dictionaries&#x2026;">
+<!ENTITY noSpellCheckAvailable.label "No dictionaries available.">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-ssl.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-ssl.dtd
new file mode 100755
index 0000000000..86bcee2044
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-ssl.dtd
@@ -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/. -->
+
+<!ENTITY SSLTLSProtocolVersions.caption "SSL/TLS Protocol Versions">
+<!ENTITY SSLTLSWarnings.caption "SSL/TLS Warnings">
+<!ENTITY SSLMixedContent.caption "Mixed Content">
+<!ENTITY SSLClientAuthMethod.caption "Client Certificate Selection">
+
+<!ENTITY pref.ssltls.title "Transport Layer Security (SSL/TLS)">
+<!ENTITY limit.description "You can restrict which encryption protocols to use for secure connections. Choose a single version or a contiguous range of versions.">
+<!ENTITY limit.enable.label "Enable:">
+<!ENTITY limit.tls10.label "TLS 1.0">
+<!ENTITY limit.tls10.accesskey "T">
+<!ENTITY limit.tls11.label "TLS 1.1">
+<!ENTITY limit.tls11.accesskey "1">
+<!ENTITY limit.tls12.label "TLS 1.2">
+<!ENTITY limit.tls12.accesskey "2">
+<!ENTITY limit.tls13.label "TLS 1.3">
+<!ENTITY limit.tls13.accesskey "3">
+
+<!ENTITY warn.description2 "&brandShortName; can alert you to the security status of the web page you are viewing. Set &brandShortName; to show a warning when:">
+<!ENTITY warn.enteringsecure "Loading a page that supports encryption">
+<!ENTITY warn.enteringsecure.accesskey "L">
+<!ENTITY warn.insecurepost "Sending form data from an unencrypted page to an unencrypted page">
+<!ENTITY warn.insecurepost.accesskey "S">
+<!ENTITY warn.leavingsecure "Leaving a page that supports encryption">
+<!ENTITY warn.leavingsecure.accesskey "a">
+
+<!ENTITY mixed.description "Encrypted pages may contain unencrypted content that is vulnerable to eavesdropping or forgery. &brandShortName; can detect and block it:">
+<!ENTITY warn.mixedactivecontent "Warn me when encrypted pages contain insecure content">
+<!ENTITY warn.mixedactivecontent.accesskey "W">
+<!ENTITY block.activecontent "Don't load insecure content on encrypted pages">
+<!ENTITY block.activecontent.accesskey "D">
+<!ENTITY warn.mixeddisplaycontent "Warn me when encrypted pages contain other types of mixed content">
+<!ENTITY warn.mixeddisplaycontent.accesskey "c">
+<!ENTITY block.displaycontent "Don't load other types of mixed content on encrypted pages">
+<!ENTITY block.displaycontent.accesskey "m">
+
+<!ENTITY certselect.description "Decide how &brandShortName; selects a security certificate to present to websites that require one:">
+<!ENTITY certselect.auto "Select Automatically">
+<!ENTITY certselect.auto.accesskey "A">
+<!ENTITY certselect.ask "Ask Every Time">
+<!ENTITY certselect.ask.accesskey "E">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-sync.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-sync.dtd
new file mode 100644
index 0000000000..10605f1894
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-sync.dtd
@@ -0,0 +1,56 @@
+<!-- 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/. -->
+
+<!-- The page shown when not logged in... -->
+<!ENTITY setupButton.label "Set Up &syncBrand.fullName.label;">
+<!ENTITY setupButton.accesskey "S">
+<!ENTITY weaveDesc.label "&syncBrand.fullName.label; lets you access your history, bookmarks, passwords and open tabs across all your devices.">
+
+<!-- The page shown when logged in... -->
+<!ENTITY accountGroupboxCaption.label "&syncBrand.fullName.label; Account">
+<!ENTITY accountName.label "Account Name:">
+
+<!-- Login error feedback -->
+<!ENTITY updatePass.label "Update">
+<!ENTITY updatePass.accesskey "U">
+<!ENTITY resetPass.label "Reset">
+<!ENTITY resetPass.accesskey "R">
+
+<!-- Manage Account -->
+<!ENTITY manageAccount.label "Manage Account">
+<!ENTITY manageAccount.accesskey "M">
+<!ENTITY viewQuota.label "View Quota">
+<!ENTITY viewQuota.accesskey "V">
+<!ENTITY changePassword.label "Change Password">
+<!ENTITY changePassword.accesskey "C">
+<!ENTITY myRecoveryKey.label "My Recovery Key">
+<!ENTITY myRecoveryKey.accesskey "M">
+<!ENTITY resetSync.label "Reset Sync">
+<!ENTITY resetSync.accesskey "R">
+<!ENTITY unlinkDevice.label "Unlink This Device">
+<!ENTITY unlinkDevice.accesskey "D">
+<!ENTITY addDevice.label "Add a Device">
+<!ENTITY addDevice.accesskey "A">
+
+<!-- Sync Settings -->
+<!ENTITY syncComputerName.label "Computer Name:">
+<!ENTITY syncComputerName.accesskey "N">
+
+<!ENTITY syncMy2.label "Sync My:">
+<!ENTITY engine.addons.label "Add-ons">
+<!ENTITY engine.addons.accesskey "A">
+<!ENTITY engine.bookmarks.label "Bookmarks">
+<!ENTITY engine.bookmarks.accesskey "B">
+<!ENTITY engine.tabs.label "Tabs">
+<!ENTITY engine.tabs.accesskey "T">
+<!ENTITY engine.history.label "History">
+<!ENTITY engine.history.accesskey "r">
+<!ENTITY engine.passwords.label "Passwords">
+<!ENTITY engine.passwords.accesskey "w">
+<!ENTITY engine.prefs.label "Preferences">
+<!ENTITY engine.prefs.accesskey "P">
+
+<!-- Footer stuff -->
+<!ENTITY prefs.tosLink.label "Terms of Service">
+<!ENTITY prefs.ppLink.label "Privacy Policy">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/pref-tabs.dtd b/comm/suite/locales/en-US/chrome/common/pref/pref-tabs.dtd
new file mode 100644
index 0000000000..081b9fdd52
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/pref-tabs.dtd
@@ -0,0 +1,35 @@
+<!-- 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/. -->
+
+<!ENTITY tabHeader.label "Tabbed Browsing">
+
+<!ENTITY tabDisplay.label "Tab Display">
+<!ENTITY autoHide.label "Hide the tab bar when only one tab is open">
+<!ENTITY autoHide.accesskey2 "e">
+<!ENTITY background.label "Switch to new tabs opened from links">
+<!ENTITY background.accesskey "S">
+<!ENTITY diverted.label "Switch to new tabs opened from diverted links">
+<!ENTITY diverted.accesskey "a">
+<!ENTITY browserFocus.label "Focus browser window for links opened in tabs from other applications">
+<!ENTITY browserFocus.accesskey "F">
+<!ENTITY warnOnClose.label "Warn me when closing a window with multiple tabs">
+<!ENTITY warnOnClose.accesskey "W">
+<!ENTITY relatedAfterCurrent.label "Open related tabs after current tab">
+<!ENTITY relatedAfterCurrent.accesskey "O">
+
+<!ENTITY openTabs.label "Open tabs instead of windows for">
+<!ENTITY middleClick.label "Middle-click, Ctrl+click or Ctrl+Enter on links in a Web page">
+<!ENTITY middleClick.accesskey "M">
+<!ENTITY middleClickMac.label "Middle-click, &#8984;+click or &#8984;+Return on links in a Web page">
+<!ENTITY middleClickMac.accesskey "M">
+<!ENTITY urlbar.label "Ctrl+Enter in the Location bar">
+<!ENTITY urlbar.accesskey "L">
+<!ENTITY urlbarMac.label "&#8984;+Return in the Location bar">
+<!ENTITY urlbarMac.accesskey "L">
+
+<!ENTITY openManagers.label "Open in a new tab instead of a stand-alone window">
+<!ENTITY openDataManager.label "Data Manager">
+<!ENTITY openDataManager.accesskey "D">
+<!ENTITY openAddOnsManager.label "Add-ons Manager">
+<!ENTITY openAddOnsManager.accesskey "n">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/preferences.dtd b/comm/suite/locales/en-US/chrome/common/pref/preferences.dtd
new file mode 100644
index 0000000000..a97c8a1b9f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/preferences.dtd
@@ -0,0 +1,58 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY windowClose.key "w">
+<!ENTITY preferencesDefaultTitleMac.title "Preferences">
+<!ENTITY preferencesDefaultTitleWin.title "Options">
+<!ENTITY preferencesCloseButton.label "Close">
+<!ENTITY preferencesCloseButton.accesskey "C">
+
+<!--LOCALIZATION NOTE (.label): Preferences categories that appear on the left of the preferences dialog -->
+<!ENTITY prefWindow.title "Preferences">
+<!ENTITY prefWindow.size "width: 102ch; height: 44em;">
+<!ENTITY prefWindowMac2.size "width: 70em; height: 41em;">
+<!ENTITY prefWindowWin2.size "width: 125ch; height: 44em;">
+<!ENTITY categoryHeader "Category">
+
+<!ENTITY appear.label "Appearance">
+<!ENTITY content.label "Content">
+<!ENTITY fonts.label "Fonts">
+<!ENTITY colors.label "Colors">
+<!ENTITY media.label "Media">
+<!ENTITY spellingPane.label "Spelling">
+
+<!ENTITY navigator.label "Browser">
+<!ENTITY history.label "History">
+<!ENTITY languages.label "Languages">
+<!ENTITY applications.label "Helper Applications">
+<!ENTITY locationBar.label "Location Bar">
+<!ENTITY search.label "Internet Search">
+<!ENTITY tabWindows.label "Tabbed Browsing">
+<!ENTITY links.label "Link Behavior">
+<!ENTITY download.label "Downloads">
+
+<!ENTITY security.label "Privacy &amp; Security">
+<!ENTITY privatedata.label "Private Data">
+<!ENTITY cookies.label "Cookies">
+<!ENTITY images.label "Images">
+<!ENTITY popups.label "Popup Windows">
+<!ENTITY passwords.label "Passwords">
+<!ENTITY ssltls.label "SSL/TLS">
+<!ENTITY certs.label "Certificates">
+
+<!ENTITY sync.label "Sync">
+
+<!ENTITY advance.label "Advanced">
+<!ENTITY scriptsAndWindows2.label "Scripts">
+<!ENTITY keynav.label "Keyboard Navigation">
+<!ENTITY findAsYouType.label "Find As You Type">
+<!ENTITY cache.label "Cache">
+<!ENTITY offlineApps.label "Offline Apps">
+<!ENTITY proxies.label "Proxies">
+<!ENTITY httpnetworking.label "HTTP Networking">
+<!ENTITY smart.label "Software Installation">
+<!ENTITY mousewheel.label "Mouse Wheel">
+<!ENTITY debugging.label "Debugging">
+
+<!ENTITY focusSearch.key "f">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/prefutilities.dtd b/comm/suite/locales/en-US/chrome/common/pref/prefutilities.dtd
new file mode 100644
index 0000000000..132bc21296
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/prefutilities.dtd
@@ -0,0 +1,43 @@
+<!-- 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/. -->
+
+<!ENTITY FallbackCharset.auto "Default for Current Locale">
+<!-- LOCALIZATION NOTE (FallbackCharset.arabic):
+ Translate "Arabic" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.arabic "Arabic">
+<!ENTITY FallbackCharset.baltic "Baltic">
+<!ENTITY FallbackCharset.ceiso "Central European, ISO">
+<!ENTITY FallbackCharset.cewindows "Central European, Microsoft">
+<!-- LOCALIZATION NOTE (FallbackCharset.simplified):
+ Translate "Chinese" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.simplified "Chinese, Simplified">
+<!-- LOCALIZATION NOTE (FallbackCharset.traditional):
+ Translate "Chinese" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.traditional "Chinese, Traditional">
+<!ENTITY FallbackCharset.cyrillic "Cyrillic">
+<!-- LOCALIZATION NOTE (FallbackCharset.greek):
+ Translate "Greek" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.greek "Greek">
+<!-- LOCALIZATION NOTE (FallbackCharset.hebrew):
+ Translate "Hebrew" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.hebrew "Hebrew">
+<!-- LOCALIZATION NOTE (FallbackCharset.japanese):
+ Translate "Japanese" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.japanese "Japanese">
+<!-- LOCALIZATION NOTE (FallbackCharset.korean):
+ Translate "Korean" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.korean "Korean">
+<!-- LOCALIZATION NOTE (FallbackCharset.thai):
+ Translate "Thai" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.thai "Thai">
+<!-- LOCALIZATION NOTE (FallbackCharset.turkish):
+ Translate "Turkish" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.turkish "Turkish">
+<!-- LOCALIZATION NOTE (FallbackCharset.unicode):
+ Only used for mail and news default text encoding and not for browser locale fallback. -->
+<!ENTITY FallbackCharset.unicode "Unicode (UTF-8)">
+<!-- LOCALIZATION NOTE (FallbackCharset.vietnamese):
+ Translate "Vietnamese" as an adjective for an encoding, not as the name of the language. -->
+<!ENTITY FallbackCharset.vietnamese "Vietnamese">
+<!ENTITY FallbackCharset.other "Other (including Western European)">
diff --git a/comm/suite/locales/en-US/chrome/common/pref/prefutilities.properties b/comm/suite/locales/en-US/chrome/common/pref/prefutilities.properties
new file mode 100644
index 0000000000..33db13337b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/pref/prefutilities.properties
@@ -0,0 +1,45 @@
+# 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/.
+
+cachefolder=Choose Cache Folder
+#LOCALIZATION NOTE (%1$S) is the size and (%2$S) is the unit of disk space.
+cacheSizeInfo=Your cache is currently using %1$S %2$S of disk space.
+
+# Offline apps
+offlineAppSizeInfo=Your offline storage currently uses %1$S %2$S of disk space.
+offlineAppRemoveTitle=Remove offline website data
+offlineAppRemovePrompt=After removing this data, %S will not be available offline. Are you sure you want to remove this offline website?
+offlineAppRemoveConfirm=Remove offline data
+
+# LOCALIZATION NOTE: The next string is for the disk usage of the
+# offline application
+# e.g. offlineAppUsage : "50.23 MB"
+# %1$S = size (in bytes or megabytes, ...)
+# %2$S = unit of measure (bytes, KB, MB, ...)
+offlineAppUsage=%1$S %2$S
+
+choosehomepage=Choose Home Page
+downloadfolder=Choose a Download Folder
+desktopFolderName=Desktop
+downloadsFolderName=Downloads
+choosesound=Choose a sound
+
+SoundFiles=Sounds
+
+# LOCALIZATION NOTE (labelDefaultFont2): %S = font name
+labelDefaultFont2=Default (%S)
+labelDefaultFontUnnamed=Default
+
+# LOCALIZATION NOTE (appLocale.label): %S = Name of the application locale,
+# e.g. English (United States)
+appLocale.label=Application locale: %S
+appLocale.accesskey=n
+# LOCALIZATION NOTE (rsLocale.label): %S = Name of the locale chosen in regional settings,
+# e.g. German (Germany)
+rsLocale.label=Regional settings locale: %S
+rsLocale.accesskey=R
+
+syncUnlink.title=Do you want to unlink your device?
+syncUnlink.label=This device will no longer be associated with your Sync account. All of your personal data, both on this device and in your Sync account, will remain intact.
+syncUnlinkConfirm.label=Unlink
diff --git a/comm/suite/locales/en-US/chrome/common/printPreview.dtd b/comm/suite/locales/en-US/chrome/common/printPreview.dtd
new file mode 100644
index 0000000000..ea64d4147a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/printPreview.dtd
@@ -0,0 +1,39 @@
+<!-- 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/. -->
+
+<!ENTITY print.label "Print…">
+<!ENTITY print.accesskey "P">
+<!ENTITY pageSetup.label "Page Setup…">
+<!ENTITY pageSetup.accesskey "u">
+<!ENTITY page.label "Page:">
+<!ENTITY page.accesskey "a">
+<!ENTITY of.label "of">
+<!ENTITY scale.label "Scale:">
+<!ENTITY scale.accesskey "S">
+<!ENTITY portrait.label "Portrait">
+<!ENTITY portrait.accesskey "o">
+<!ENTITY landscape.label "Landscape">
+<!ENTITY landscape.accesskey "L">
+<!ENTITY close.label "Close">
+<!ENTITY close.accesskey "C">
+<!ENTITY p30.label "30&#037;">
+<!ENTITY p40.label "40&#037;">
+<!ENTITY p50.label "50&#037;">
+<!ENTITY p60.label "60&#037;">
+<!ENTITY p70.label "70&#037;">
+<!ENTITY p80.label "80&#037;">
+<!ENTITY p90.label "90&#037;">
+<!ENTITY p100.label "100&#037;">
+<!ENTITY p125.label "125&#037;">
+<!ENTITY p150.label "150&#037;">
+<!ENTITY p175.label "175&#037;">
+<!ENTITY p200.label "200&#037;">
+<!ENTITY Custom.label "Custom…">
+<!ENTITY ShrinkToFit.label "Shrink To Fit">
+<!ENTITY customPrompt.title "Custom Scale…">
+
+<!ENTITY homearrow.tooltip "First page">
+<!ENTITY endarrow.tooltip "Last page">
+<!ENTITY rightarrow.tooltip "Next page">
+<!ENTITY leftarrow.tooltip "Previous page">
diff --git a/comm/suite/locales/en-US/chrome/common/profile/profileSelection.dtd b/comm/suite/locales/en-US/chrome/common/profile/profileSelection.dtd
new file mode 100644
index 0000000000..147d8266a7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/profile/profileSelection.dtd
@@ -0,0 +1,35 @@
+<!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
+<!--
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY windowTitle.label "Select User Profile">
+<!ENTITY profileManager.title "&brandShortName; Profile Manager">
+
+<!ENTITY manage.label "Manage Profiles…">
+<!ENTITY manage.accesskey "M">
+<!ENTITY select.label "Use Profile">
+
+<!ENTITY availableProfiles.label "Available Profiles">
+
+<!ENTITY introStart.label "To access your personal profile, which contains your stored messages, settings and other personalized information, please choose your profile from the list, and click &start.label; to begin your session.">
+<!ENTITY introSwitch.label "To switch to another profile, which contains stored messages, settings and other personalized information, please choose that profile from the list, and click &select.label; to begin using that profile.">
+<!ENTITY profileManagerText.label "&brandShortName; stores information about your settings, preferences, bookmarks, stored messages and other user items in your user profile.">
+
+<!ENTITY autoSelect.label "Default to this profile">
+<!ENTITY autoSelect.accesskey "S">
+
+<!ENTITY start.label "Start &brandShortName;">
+<!ENTITY exit.label "Exit">
+
+<!ENTITY newButton.label "Create Profile…">
+<!ENTITY newButton.accesskey "C">
+<!ENTITY renameButton.label "Rename Profile…">
+<!ENTITY renameButton.accesskey "R">
+<!ENTITY deleteButton.label "Delete Profile…">
+<!ENTITY deleteButton.accesskey "D">
+
+<!ENTITY offlineState.label "Work offline">
+<!ENTITY offlineState.accesskey "o">
diff --git a/comm/suite/locales/en-US/chrome/common/profile/profileSelection.properties b/comm/suite/locales/en-US/chrome/common/profile/profileSelection.properties
new file mode 100644
index 0000000000..5b5ad1a8a0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/profile/profileSelection.properties
@@ -0,0 +1,22 @@
+# 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/.
+
+deleteLocked=%S cannot delete the profile "%S" because it is in use.
+deleteProfile=Deleting a profile will remove the profile from the list of available profiles and cannot be undone.\n\nYou may also choose to delete the profile data files, including your saved mail, settings, and certificates. This option will delete the folder "%S" and cannot be undone.\n\nWould you like to delete the profile data files?\n\n
+
+manageTitle=Manage User Profiles
+selectTitle=Select User Profile
+
+dirLocked=%S cannot use the profile "%S". It may be in use, unavailable or damaged.\n\nPlease choose another profile or create a new one.
+
+renameProfileTitle=Rename Profile
+renameProfilePrompt=Rename the profile "%S" to:
+profileNameInvalidTitle=Invalid profile name
+profileNameEmpty=An empty profile name is not allowed.
+invalidChar=The character "%S" is not allowed in profile names. Please choose a different name.
+deleteTitle=Delete Profile
+deleteFiles=Delete Files
+dontDeleteFiles=Don't Delete Files
+profileExists=A profile with this name already exists. Please choose another name.
+profileExistsTitle=Profile Exists
diff --git a/comm/suite/locales/en-US/chrome/common/quitDialog.properties b/comm/suite/locales/en-US/chrome/common/quitDialog.properties
new file mode 100644
index 0000000000..e91bb709cf
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/quitDialog.properties
@@ -0,0 +1,29 @@
+# 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/.
+
+# Strings for the Quit, Restart, and Close-browser dialogs
+# used by _onQuitRequest() in nsSuiteGlue.js
+
+# LOCALIZATION NOTE:
+# %S (also in the messages further down)
+# is the application's short name (e.g. SeaMonkey)
+# from the brand.properties file
+quitDialogTitle=Quit %S
+lastwindowDialogTitle=Close %S Browser
+restartDialogTitle=Restart %S
+
+restartNowTitle=&Restart Now
+restartLaterTitle=Restart &Later
+quitTitle=&Quit
+lastwindowTitle=Close &Browser
+cancelTitle=&Cancel
+# LOCALIZATION NOTE:
+# The following two strings are labels for the same button, depending on
+# whether we are quitting the whole Suite, or only the Browser
+saveTitle=&Save and Quit
+savelastwindowTitle=&Save and Close
+neverAsk=&Do not ask next time
+message=Do you want %S to save your tabs and windows for the next time it starts?
+messageNoWindows=Do you want %S to save your tabs for the next time it starts?
+messageRestart=%S will try to restore your tabs and windows when it restarts.
diff --git a/comm/suite/locales/en-US/chrome/common/safeBrowsing.dtd b/comm/suite/locales/en-US/chrome/common/safeBrowsing.dtd
new file mode 100644
index 0000000000..7e2b37a27f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/safeBrowsing.dtd
@@ -0,0 +1,33 @@
+<!-- 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/. -->
+
+<!ENTITY safeb.palm.accept.label "Get me out of here!">
+<!ENTITY safeb.palm.decline.label "Ignore this warning">
+<!ENTITY safeb.palm.reportPage.label "Why was this page blocked?">
+
+<!ENTITY safeb.blocked.malwarePage.title "Reported Attack Page!">
+<!-- Localization note (safeb.blocked.malwarePage.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
+<!ENTITY safeb.blocked.malwarePage.shortDesc "This web page at <span id='malware_sitename'/> has been reported as an attack page and has been blocked based on your security preferences.">
+<!ENTITY safeb.blocked.malwarePage.longDesc "<p>Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>">
+
+<!ENTITY safeb.blocked.unwantedPage.title "Reported Unwanted Software Page!">
+<!-- Localization note (safeb.blocked.unwantedPage.shortDesc) - Please don't translate the contents of the <span id="unwanted_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
+<!ENTITY safeb.blocked.unwantedPage.shortDesc "This web page at <span id='unwanted_sitename'/> has been reported to contain unwanted software and has been blocked based on your security preferences.">
+<!ENTITY safeb.blocked.unwantedPage.longDesc "<p>Unwanted software pages try to install software that can be deceptive and affect your system in unexpected ways.</p>">
+
+<!ENTITY safeb.blocked.phishingPage.title2 "Deceptive Site!">
+<!-- Localization note (safeb.blocked.phishingPage.shortDesc2) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
+<!ENTITY safeb.blocked.phishingPage.shortDesc2 "This web page at <span id='phishing_sitename'/> has been reported as a deceptive site and has been blocked based on your security preferences.">
+<!ENTITY safeb.blocked.phishingPage.longDesc2 "<p>Deceptive sites are designed to trick you into doing something dangerous, like installing software, or revealing your personal information, like passwords, phone numbers or credit cards.</p><p>Entering any information on this web page may result in identity theft or other fraud.</p>">
+
+<!ENTITY safeb.blocked.harmfulPage.title "The site ahead may contain malware">
+<!-- Localization note (safeb.blocked.harmfulPage.shortDesc) - Please don't translate the contents of the <span id="harmful_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
+<!ENTITY safeb.blocked.harmfulPage.shortDesc "&brandShortName; blocked the web page at <span id='harmful_sitename'/> because it might try to install dangerous apps that steal or delete your information (for example, photos, passwords, messages and credit cards).">
+<!ENTITY safeb.blocked.harmfulPage.longDesc "">
+
+<!-- Localization note (reportDeceptiveSite, notADeceptiveSite) - The two button strings will never be shown at the same time, so it's okay for them to have the same access key. -->
+<!ENTITY reportDeceptiveSite.label "Report deceptive site…">
+<!ENTITY reportDeceptiveSite.accesskey "d">
+<!ENTITY notADeceptiveSite.label "This isn't a deceptive site…">
+<!ENTITY notADeceptiveSite.accesskey "d">
diff --git a/comm/suite/locales/en-US/chrome/common/safeMode.dtd b/comm/suite/locales/en-US/chrome/common/safeMode.dtd
new file mode 100644
index 0000000000..3b881665be
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/safeMode.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<!ENTITY safeModeDialog.title "&brandShortName; Safe Mode">
+<!ENTITY window.width "37em">
+
+<!ENTITY safeModeDescription.label "&brandShortName; is now running in Safe Mode, which temporarily disables your custom settings, themes, and extensions.">
+<!ENTITY safeModeDescription2.label "You can make some or all of these changes permanent:">
+
+<!ENTITY disableAddons.label "Disable all add-ons">
+<!ENTITY disableAddons.accesskey "D">
+
+<!ENTITY resetToolbars.label "Reset toolbars and window sizes">
+<!ENTITY resetToolbars.accesskey "R">
+
+<!ENTITY deleteBookmarks.label "Delete all bookmarks except for backups">
+<!ENTITY deleteBookmarks.accesskey "b">
+
+<!ENTITY resetUserPrefs.label "Reset all user preferences to &brandShortName; defaults">
+<!ENTITY resetUserPrefs.accesskey "p">
+
+<!ENTITY restoreSearch.label "Restore default search engines">
+<!ENTITY restoreSearch.accesskey "s">
+
+<!ENTITY changeAndRestartButton.label "Make Changes and Restart">
+<!ENTITY continueButton.label "Continue in Safe Mode">
diff --git a/comm/suite/locales/en-US/chrome/common/sanitize.dtd b/comm/suite/locales/en-US/chrome/common/sanitize.dtd
new file mode 100644
index 0000000000..7d3ed1cbae
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sanitize.dtd
@@ -0,0 +1,66 @@
+<!-- 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/. -->
+
+<!ENTITY sanitizeDialog.title "Clear Private Data">
+
+<!-- LOCALIZATION NOTE (sanitizeDialog.width): width of the Clear Recent
+ History dialog -->
+<!ENTITY sanitizeDialog.width "34em">
+
+<!-- LOCALIZATION NOTE (clearTimeDuration.*): "Time range to clear"
+ dropdown. -->
+<!ENTITY clearTimeDuration.label "Time range to clear: ">
+<!ENTITY clearTimeDuration.accesskey "T">
+<!ENTITY clearTimeDuration.lastHour "Last Hour">
+<!ENTITY clearTimeDuration.last2Hours "Last Two Hours">
+<!ENTITY clearTimeDuration.last4Hours "Last Four Hours">
+<!ENTITY clearTimeDuration.today "Today">
+<!ENTITY clearTimeDuration.everything "Everything">
+<!-- Localization note (clearTimeDuration.suffix) - trailing entity for
+ languages that require it. -->
+<!ENTITY clearTimeDuration.suffix "">
+
+<!-- LOCALIZATION NOTE (sanitizeSelectedWarning): Warning that appears
+ when not all items are selected in the in Clear Private data dialog. -->
+<!ENTITY sanitizeSelectedWarning "All selected items will be cleared.">
+
+<!-- LOCALIZATION NOTE (sanitizeUndoWarning): Second warning
+ paragraph that always appears. -->
+<!ENTITY sanitizeUndoWarning "This action cannot be undone.">
+
+<!ENTITY sanitizeItems.label "Clear the following items now:">
+
+<!-- LOCALIZATION NOTE : Try to make sure to not have overlapped accesskeys
+ with pref-privatedata.dtd. These entities are also used in the private
+ data preferences dialog. -->
+<!ENTITY itemHistory.label "Browsing History">
+<!ENTITY itemHistory.accesskey "B">
+<!ENTITY itemHistoryS.accesskey "r">
+<!ENTITY itemUrlBar.label "Location Bar History">
+<!ENTITY itemUrlBar.accesskey "L">
+<!ENTITY itemUrlBarS.accesskey "t">
+<!ENTITY itemDownloads.label "Download History">
+<!ENTITY itemDownloads.accesskey "D">
+<!ENTITY itemDownloadsS.accesskey "y">
+<!ENTITY itemFormSearchHistory.label "Saved Form and Search History">
+<!ENTITY itemFormSearchHistory.accesskey "F">
+<!ENTITY itemFormSearchHistoryS.accesskey "m">
+<!ENTITY itemCache.label "Cache">
+<!ENTITY itemCache.accesskey "a">
+<!ENTITY itemCacheS.accesskey "e">
+<!ENTITY itemCookies.label "Cookies">
+<!ENTITY itemCookies.accesskey "C">
+<!ENTITY itemCookiesS.accesskey "k">
+<!ENTITY itemOfflineApps.label "Offline Website Data">
+<!ENTITY itemOfflineApps.accesskey "O">
+<!ENTITY itemOfflineAppsS.accesskey "i">
+<!ENTITY itemPasswords.label "Saved Passwords">
+<!ENTITY itemPasswords.accesskey "P">
+<!ENTITY itemPasswordsS.accesskey "v">
+<!ENTITY itemSessions.label "Authenticated Sessions">
+<!ENTITY itemSessions.accesskey "S">
+<!ENTITY itemSessionsS.accesskey "u">
+<!ENTITY itemSitePreferences.label "Site Preferences">
+<!ENTITY itemSitePreferences.accesskey "P">
+<!ENTITY itemSitePreferencesS.accesskey "t">
diff --git a/comm/suite/locales/en-US/chrome/common/sanitize.properties b/comm/suite/locales/en-US/chrome/common/sanitize.properties
new file mode 100644
index 0000000000..7d555175ac
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sanitize.properties
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+sanitizeButtonOK=Clear Now
+# LOCALIZATION NOTE (sanitizeButtonClearing): The label for the default
+# button between the user clicking it and the window closing. Indicates the
+# items are being cleared.
+sanitizeButtonClearing=Clearing
diff --git a/comm/suite/locales/en-US/chrome/common/search/engineManager.dtd b/comm/suite/locales/en-US/chrome/common/search/engineManager.dtd
new file mode 100644
index 0000000000..8ad9772057
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/search/engineManager.dtd
@@ -0,0 +1,29 @@
+<!-- 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/. -->
+
+<!ENTITY engineManager.title "Manage Search Engine List">
+<!ENTITY engineManager.style "min-width: 35em;">
+<!ENTITY engineManager.intro "You have the following search engines installed:">
+
+<!ENTITY columnLabel.name "Name">
+<!ENTITY columnLabel.keyword "Keyword">
+
+<!-- Buttons -->
+<!ENTITY up.label "Move Up">
+<!ENTITY up.accesskey "U">
+<!ENTITY dn.label "Move Down">
+<!ENTITY dn.accesskey "D">
+<!ENTITY remove.label "Remove">
+<!ENTITY remove.accesskey "R">
+<!ENTITY edit.label "Edit Keyword…">
+<!ENTITY edit.accesskey "t">
+
+<!ENTITY addEngine.label "Get more search engines…">
+<!ENTITY addEngine.accesskey "A">
+
+<!ENTITY enableSuggest.label "Show search suggestions">
+<!ENTITY enableSuggest.accesskey "S">
+
+<!ENTITY restoreDefaults.label "Restore Defaults">
+<!ENTITY restoreDefaults.accesskey "e">
diff --git a/comm/suite/locales/en-US/chrome/common/search/engineManager.properties b/comm/suite/locales/en-US/chrome/common/search/engineManager.properties
new file mode 100644
index 0000000000..040a4caf3c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/search/engineManager.properties
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+editTitle=Edit Keyword
+editMsg=Enter a new keyword for "%S":
+duplicateTitle=Duplicate Keyword
+duplicateEngineMsg=You have chosen a keyword that is currently in use by "%S". Please select another.
+duplicateBookmarkMsg=You have chosen a keyword that is currently in use by a bookmark. Please select another.
diff --git a/comm/suite/locales/en-US/chrome/common/search/search-panel.dtd b/comm/suite/locales/en-US/chrome/common/search/search-panel.dtd
new file mode 100755
index 0000000000..5c58c824d0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/search/search-panel.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY search.button.label "Search">
+<!ENTITY search.placeholder "Enter search text">
+
+<!ENTITY search.engineManager.label "Manage Search Engines…">
diff --git a/comm/suite/locales/en-US/chrome/common/search/search.properties b/comm/suite/locales/en-US/chrome/common/search/search.properties
new file mode 100644
index 0000000000..c36c025f69
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/search/search.properties
@@ -0,0 +1,6 @@
+# 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/.
+
+searchtip=Search using %S
+cmd_addFoundEngine=Add "%S"
diff --git a/comm/suite/locales/en-US/chrome/common/search/searchbar.dtd b/comm/suite/locales/en-US/chrome/common/search/searchbar.dtd
new file mode 100644
index 0000000000..e5d18e4e8b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/search/searchbar.dtd
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+
+<!ENTITY cmd_engineManager.label "Manage Search Engines…">
+<!ENTITY searchEndCap.label "Search">
diff --git a/comm/suite/locales/en-US/chrome/common/setDesktopBackground.dtd b/comm/suite/locales/en-US/chrome/common/setDesktopBackground.dtd
new file mode 100644
index 0000000000..59d952fee0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/setDesktopBackground.dtd
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<!ENTITY setDesktopBackground.title "Set Desktop Background">
+
+<!ENTITY position.label "Position:">
+<!ENTITY position.accesskey "P">
+<!ENTITY position.tile.label "Tile">
+<!ENTITY position.stretch.label "Stretch">
+<!ENTITY position.center.label "Center">
+<!ENTITY position.fill.label "Fill">
+<!ENTITY position.fit.label "Fit">
+<!ENTITY picker.label "Color:">
+<!ENTITY picker.accesskey "C">
+<!ENTITY preview.caption "Preview">
+<!ENTITY apply.label "Apply">
+<!ENTITY apply.accesskey "A">
+<!ENTITY close.label "Close">
diff --git a/comm/suite/locales/en-US/chrome/common/shellservice.properties b/comm/suite/locales/en-US/chrome/common/shellservice.properties
new file mode 100644
index 0000000000..885ddf9ae8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/shellservice.properties
@@ -0,0 +1,7 @@
+# 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/.
+
+preferencesLabel=%S &Preferences
+safeModeLabel=%S &Safe Mode
+desktopBackgroundLeafNameWin=Desktop Background.bmp
diff --git a/comm/suite/locales/en-US/chrome/common/sidebar/customize.dtd b/comm/suite/locales/en-US/chrome/common/sidebar/customize.dtd
new file mode 100644
index 0000000000..491fe98986
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sidebar/customize.dtd
@@ -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/. -->
+
+<!-- extracted from ./customize.xul -->
+
+<!ENTITY sidebar.customize.title "Customize Sidebar">
+<!ENTITY sidebar.customize.current2.label "Tabs in Sidebar:">
+<!ENTITY sidebar.customize.current2.accesskey "T">
+<!ENTITY sidebar.customize.customize.label "Customize Tab…">
+<!ENTITY sidebar.customize.customize.accesskey "C">
+<!ENTITY sidebar.customize.remove.label "Remove">
+<!ENTITY sidebar.customize.remove.accesskey "R">
+<!ENTITY sidebar.customize.additional.label "Available Tabs:">
+<!ENTITY sidebar.customize.additional.accesskey "v">
+<!ENTITY sidebar.customize.add.label "Add">
+<!ENTITY sidebar.customize.add.accesskey "A">
+<!ENTITY sidebar.customize.preview.label "Preview…">
+<!ENTITY sidebar.customize.preview.accesskey "P">
+<!ENTITY sidebar.customize.up.label "Move Up">
+<!ENTITY sidebar.customize.up.accesskey "U">
+<!ENTITY sidebar.customize.down.label "Move Down">
+<!ENTITY sidebar.customize.down.accesskey "D">
+<!ENTITY sidebar.more.label "Find More Tabs…">
+<!ENTITY sidebar.more.accesskey "F">
diff --git a/comm/suite/locales/en-US/chrome/common/sidebar/preview.dtd b/comm/suite/locales/en-US/chrome/common/sidebar/preview.dtd
new file mode 100644
index 0000000000..5947fa9400
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sidebar/preview.dtd
@@ -0,0 +1,5 @@
+<!-- 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/. -->
+
+<!ENTITY sidebar.preview.title.label "Tab Preview">
diff --git a/comm/suite/locales/en-US/chrome/common/sidebar/sidebar.properties b/comm/suite/locales/en-US/chrome/common/sidebar/sidebar.properties
new file mode 100644
index 0000000000..c389e66e7d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sidebar/sidebar.properties
@@ -0,0 +1,10 @@
+# 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/.
+
+addPanelConfirmTitle=Add Tab to Sidebar
+addPanelConfirmMessage2=Add the tab '%title%' to the sidebar?##Source: %url%
+persistentPanelWarning2=The sidebar tab you are adding can transfer data across the Internet and run JavaScript even while the sidebar is closed.
+
+dupePanelAlertTitle=Sidebar
+dupePanelAlertMessage2=%url% already exists in the sidebar.
diff --git a/comm/suite/locales/en-US/chrome/common/sidebar/sidebarOverlay.dtd b/comm/suite/locales/en-US/chrome/common/sidebar/sidebarOverlay.dtd
new file mode 100644
index 0000000000..59271a84da
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sidebar/sidebarOverlay.dtd
@@ -0,0 +1,38 @@
+<!-- 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/. -->
+
+<!ENTITY sidebar.panels.label "Sidebar">
+<!ENTITY sidebar.reload.label "Reload">
+<!ENTITY sidebar.reload.accesskey "R">
+<!ENTITY sidebar.picker.label "Tabs">
+<!ENTITY sidebar.customize.label "Customize Sidebar…">
+<!ENTITY sidebar.customize.accesskey "u">
+<!ENTITY sidebar.hide.label "Hide Tab">
+<!ENTITY sidebar.hide.accesskey "H">
+<!ENTITY sidebar.switch.label "Switch to Tab">
+<!ENTITY sidebar.switch.accesskey "T">
+<!ENTITY sidebarCmd.label "Sidebar">
+<!ENTITY sidebarCmd.accesskey "b">
+<!ENTITY sidebar.loading.label "Loading…">
+<!ENTITY sidebar.loadstopped.label "Load stopped">
+<!ENTITY sidebar.loading.stop.label "Stop">
+<!ENTITY sidebar.loading.stop.accesskey "S">
+
+<!-- LOCALIZATION NOTE (showHideSidebarCmd.key): This is only used on the
+ mac platform, other platforms use VK_F9. -->
+<!ENTITY showHideSidebarCmd.key "S">
+
+<!ENTITY sidebar.no-panels.state "The sidebar is currently empty.">
+<!ENTITY sidebar.no-panels.add 'You may add tabs by clicking on the "Tabs" button above.'>
+<!ENTITY sidebar.no-panels.hide 'If you would like to completely hide the Sidebar, click on the "View" menu above, and select "Sidebar" from the "Show/Hide" sub-menu.'>
+<!ENTITY sidebar.sbDirectory.label "Sidebar Directory…">
+
+<!ENTITY sidebar.pagenotfound.label "This tab is not available right now.">
+<!ENTITY sidebar.close.tooltip "Close Sidebar">
+<!ENTITY sidebar.open.tooltip "Open Sidebar">
+
+<!ENTITY sidebar.search.label "Search">
+<!ENTITY sidebar.client-bookmarks.label "Bookmarks">
+<!ENTITY sidebar.client-history.label "History">
+<!ENTITY sidebar.client-addressbook.label "Address Book">
diff --git a/comm/suite/locales/en-US/chrome/common/sitePermissions.properties b/comm/suite/locales/en-US/chrome/common/sitePermissions.properties
new file mode 100644
index 0000000000..1d5b40975a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sitePermissions.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+# LOCALIZATION NOTE (state.current.allowed,
+# state.current.allowedForSession,
+# state.current.allowedTemporarily,
+# state.current.blockedTemporarily,
+# state.current.blocked,
+# state.current.hide):
+# This label is used to display active permission states in the site
+# identity popup (which does not have a lot of screen space).
+state.current.allowed = Allowed
+state.current.allowedForSession = Allowed for Session
+state.current.allowedTemporarily = Allowed Temporarily
+state.current.blockedTemporarily = Blocked Temporarily
+state.current.blocked = Blocked
+state.current.prompt = Always Ask
+
+# LOCALIZATION NOTE (state.multichoice.alwaysAsk,
+# state.multichoice.allow,
+# state.multichoice.allowForSameDomain,
+# state.multichoice.allowForSession,
+# state.multichoice.block):
+# Used to label permission state checkboxes in the page info dialog.
+state.multichoice.alwaysAsk = Always Ask
+state.multichoice.allow = Allow
+state.multichoice.allowForSameDomain = Allow for Same Domain
+state.multichoice.allowForSession = Allow for Session
+state.multichoice.block = Block
+
+permission.cookie.label = Set Cookies
+permission.desktop-notification2.label = Receive Notifications
+permission.image.label = Load Images
+permission.camera.label = Use the Camera
+permission.microphone.label = Use the Microphone
+permission.screen.label = Share the Screen
+permission.install.label = Install Add-ons
+permission.popup.label = Open Pop-up Windows
+permission.geo.label = Access Your Location
+permission.indexedDB.label = Maintain Offline Storage
+permission.focus-tab-by-prompt.label = Switch to this Tab
+permission.persistent-storage.label = Store Data in Persistent Storage
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncBrand.dtd b/comm/suite/locales/en-US/chrome/common/sync/syncBrand.dtd
new file mode 100644
index 0000000000..71955e69ae
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncBrand.dtd
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+
+<!ENTITY syncBrand.shortName.label "Sync">
+<!ENTITY syncBrand.fullName.label "SeaMonkey Sync">
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncGenericChange.properties b/comm/suite/locales/en-US/chrome/common/sync/syncGenericChange.properties
new file mode 100644
index 0000000000..ed91abae36
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncGenericChange.properties
@@ -0,0 +1,37 @@
+# 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/.
+
+# LOCALIZATION NOTE (whole file) "Sync" should match &syncBrand.shortName.label; from syncBrand.dtd
+# LOCALIZATION NOTE (change.password.title): This (and associated change.password/passphrase) are used when the user elects to change their password.
+change.password.title = Change your Password
+change.password.acceptButton = Change Password
+change.password.status.active = Changing your password…
+change.password.status.success = Your password has been changed.
+change.password.status.error = There was an error changing your password.
+
+change.password3.introText = Your password must be at least 8 characters long. It cannot be the same as either your user name or your Recovery Key.
+change.password.warningText = Note: All of your other devices will be unable to connect to your account once you change this password.
+
+change.recoverykey.title = Change your Recovery Key
+change.recoverykey.acceptButton = Change Recovery Key
+change.recoverykey.label = Changing Recovery Key and uploading local data, please wait…
+change.recoverykey.error = There was an error while changing your Recovery Key!
+change.recoverykey.success = Your Recovery Key was successfully changed!
+change.recoverykey.introText2 = To ensure your total privacy, all of your data is encrypted prior to being uploaded. The key to decrypt your data is not uploaded.
+change.recoverykey.warningText = Note: Changing this will erase all data stored on the Sync server and upload new data secured by this Recovery Key. Your other devices will not sync until the new Recovery Key is entered for that device.
+
+new.recoverykey.label = Your Recovery Key
+
+# LOCALIZATION NOTE (new.password.title): This (and associated new.password/passphrase) are used on a second computer when it detects that your password or passphrase has been changed on a different device.
+new.password.title = Update Password
+new.password.introText = Your password was rejected by the server, please update your password.
+new.password.label = Enter your new password
+new.password.confirm = Confirm your new password
+new.password.acceptButton = Update Password
+new.password.status.incorrect = Password incorrect, please try again.
+
+new.recoverykey.title = Update Recovery Key
+new.recoverykey.introText = You changed your Recovery Key from another device, please enter your updated Recovery Key.
+new.recoverykey.acceptButton = Update Recovery Key
+new.recoverykey.status.incorrect = Recovery Key incorrect, please try again.
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncKey.dtd b/comm/suite/locales/en-US/chrome/common/sync/syncKey.dtd
new file mode 100644
index 0000000000..d3a56c74de
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncKey.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!ENTITY syncKey.page.title "Your &syncBrand.fullName.label; Key">
+<!ENTITY syncKey.page.description "This key is used to decode the data in your &syncBrand.fullName.label; account. You will need to enter the key each time you configure &syncBrand.fullName.label; on a new computer or device.">
+<!ENTITY syncKey.keepItSecret.heading "Keep it secret">
+<!ENTITY syncKey.keepItSecret.description "Your &syncBrand.fullName.label; account is encrypted to protect your privacy. Without this key, it would take years for anyone to decode your personal information. You are the only person who holds this key. This means you're the only one who can access your &syncBrand.fullName.label; data.">
+<!ENTITY syncKey.keepItSafe.heading "Keep it safe">
+<!ENTITY syncKey.keepItSafe1.description "Do not lose this key.">
+<!ENTITY syncKey.keepItSafe2.description " We don't keep a copy of your key (that wouldn't be keeping it secret!) so ">
+<!ENTITY syncKey.keepItSafe3.description "we can't help you recover it">
+<!ENTITY syncKey.keepItSafe4.description " if it's lost. You'll need to use this key any time you connect a new computer or device to &syncBrand.fullName.label;.">
+<!ENTITY syncKey.findOutMore1.label "Find out more about &syncBrand.fullName.label; and your privacy at ">
+<!ENTITY syncKey.findOutMore2.label ".">
+<!ENTITY syncKey.footer1.label "&syncBrand.fullName.label; Terms of Service are available at ">
+<!ENTITY syncKey.footer2.label ". The Privacy Policy is available at ">
+<!ENTITY syncKey.footer3.label ".">
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncQuota.dtd b/comm/suite/locales/en-US/chrome/common/sync/syncQuota.dtd
new file mode 100644
index 0000000000..71174f087e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncQuota.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY quota.dialogTitle.label "Server Quota">
+<!ENTITY quota.retrievingInfo.label "Retrieving quota information…">
+<!ENTITY quota.typeColumn.label "Type">
+<!ENTITY quota.sizeColumn.label "Size">
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncQuota.properties b/comm/suite/locales/en-US/chrome/common/sync/syncQuota.properties
new file mode 100644
index 0000000000..8bed9ca69e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncQuota.properties
@@ -0,0 +1,42 @@
+# 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/.
+
+collection.addons.label = Add-ons
+collection.bookmarks.label = Bookmarks
+collection.history.label = History
+collection.passwords.label = Passwords
+collection.prefs.label = Preferences
+collection.tabs.label = Tabs
+
+# LOCALIZATION NOTE (quota.usageNoQuota.label): %1$S and %2$S are numeric value
+# and unit (as defined in the download manager) of the amount of space occupied
+# on the server
+quota.usageNoQuota.label = You are currently using %1$S %2$S.
+# LOCALIZATION NOTE (quota.usagePercentage.label):
+# %1$S is the percentage of space used,
+# %2$S and %3$S numeric value and unit (as defined in the download manager)
+# of the amount of space used,
+# %3$S and %4$S numeric value and unit (as defined in the download manager)
+# of the total space available.
+quota.usagePercentage.label = You are using %1$S%% (%2$S %3$S) of your allowed %4$S %5$S.
+quota.usageError.label = Could not retrieve quota information.
+quota.retrieving.label = Retrieving…
+# LOCALIZATION NOTE (quota.sizeValueUnit.label): %1$S is the amount of space
+# occupied by the engine, %2$K the corresponding unit (e.g. kB) as defined in
+# the download manager.
+quota.sizeValueUnit.label = %1$S %2$S
+quota.remove.label = Remove
+quota.treeCaption.label = Uncheck items to stop syncing them and free up space on the server.
+# LOCALIZATION NOTE (quota.removal.label): %S is a list of engines that will be
+# disabled and whose data will be removed once the user confirms.
+quota.removal.label = SeaMonkey Sync will remove the following data: %S.
+# LOCALIZATION NOTE (quota.list.separator): This is the separator string used
+# for the list of engines (incl. spaces where appropriate)
+quota.list.separator = ,\u0020
+# LOCALIZATION NOTE (quota.freeup.label): %1$S and %2$S are numeric value
+# and unit (as defined in the download manager) of the amount of space freed
+# up by disabling the unchecked engines. If displayed this string is
+# concatenated directly to quota.removal.label and may need to start off with
+# whitespace.
+quota.freeup.label = \u0020This will free up %1$S %2$S.
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncSetup.dtd b/comm/suite/locales/en-US/chrome/common/sync/syncSetup.dtd
new file mode 100644
index 0000000000..e9ed5a48b5
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncSetup.dtd
@@ -0,0 +1,116 @@
+<!-- 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/. -->
+
+<!ENTITY accountSetupTitle.label "&syncBrand.fullName.label; Setup">
+
+<!-- First page of the wizard -->
+
+<!ENTITY setup.pickSetupType.description "Welcome, if you've never used &syncBrand.fullName.label; before, you will need to create a new account.">
+<!ENTITY button.createNewAccount.label "Create a New Account">
+<!ENTITY setup.haveAccount.label "I already have a &syncBrand.fullName.label; account.">
+<!ENTITY button.connect.label "Connect">
+
+<!ENTITY setup.choicePage.title.label "Have you used &syncBrand.fullName.label; before?">
+<!ENTITY setup.choicePage.new.label "I've never used &syncBrand.shortName.label; before">
+<!ENTITY setup.choicePage.existing.label "I'm already using &syncBrand.shortName.label; on another computer">
+
+<!-- New Account AND Existing Account -->
+<!ENTITY server.label "Server">
+<!ENTITY server.accesskey "S">
+<!ENTITY serverType.main.label "&syncBrand.fullName.label; Server">
+<!ENTITY serverType.custom2.label "Use a custom server…">
+<!ENTITY signIn.account2.label "Account">
+<!ENTITY signIn.account2.accesskey "A">
+<!ENTITY signIn.password.label "Password">
+<!ENTITY signIn.password.accesskey "P">
+<!ENTITY signIn.recoveryKey.label "Recovery Key">
+<!ENTITY signIn.recoveryKey.accesskey "K">
+
+<!-- New Account Page 1: Basic Account Info -->
+<!ENTITY setup.newAccountDetailsPage.title.label "Account Details">
+<!ENTITY setup.confirmPassword.label "Confirm Password">
+<!ENTITY setup.confirmPassword.accesskey "C">
+<!ENTITY setup.emailAddress.label "Email Address">
+<!ENTITY setup.emailAddress.accesskey "E">
+<!-- LOCALIZATION NOTE: tosAgree1, tosLink, tosAgree2, ppLink, tosAgree3 are
+ joined with implicit white space, so spaces in the strings aren't necessary -->
+<!ENTITY setup.tosAgree1.label "I agree to the">
+<!ENTITY setup.tosAgree1.accesskey "a">
+<!ENTITY setup.tosLink.label "Terms of Service">
+<!ENTITY setup.tosAgree2.label "and the">
+<!ENTITY setup.ppLink.label "Privacy Policy">
+<!ENTITY setup.tosAgree3.label "">
+<!ENTITY setup.tosAgree2.accesskey "">
+
+<!-- New Account Page 2: Recovery Key -->
+<!ENTITY setup.newRecoveryKeyPage.title.label "&brandShortName; Cares About Your Privacy">
+<!ENTITY setup.newRecoveryKeyPage.description.label "To ensure your total privacy, all of your data is encrypted prior to being uploaded. The Recovery Key which is necessary to decrypt your data is not uploaded.">
+<!ENTITY recoveryKeyEntry.label "Your Recovery Key">
+<!ENTITY recoveryKeyEntry.accesskey "K">
+<!ENTITY recoveryGenerateNewKey.label "Generate a new key">
+<!ENTITY recoveryKeyBackup.description "Your Recovery Key is required to access &syncBrand.fullName.label; on other machines. Please create a backup copy. We cannot help you recover your Recovery Key.">
+
+<!ENTITY button.syncKeyBackup.print.label "Print…">
+<!ENTITY button.syncKeyBackup.print.accesskey "P">
+<!ENTITY button.syncKeyBackup.save.label "Save…">
+<!ENTITY button.syncKeyBackup.save.accesskey "S">
+
+<!-- New Account Page 3: Captcha -->
+<!ENTITY setup.captchaPage2.title.label "Please Confirm You're Not a Robot">
+
+<!-- Existing Account Page 1: Add Device (incl. Add a Device dialog strings) -->
+<!ENTITY addDevice.title.label "Add a Device">
+<!ENTITY addDevice.showMeHow.label "Show me how.">
+<!ENTITY addDevice.dontHaveDevice.label "I don't have the device with me">
+<!ENTITY addDevice.setup.description.label "To activate, go to &syncBrand.shortName.label; Preferences/Options on your other device and select &#x0022;Add a Device&#x0022;.">
+<!ENTITY addDevice.setup.enterCode.label "Then, enter this code:">
+<!ENTITY addDevice.dialog.description.label "To activate your new device, go to &syncBrand.shortName.label; Preferences/Options on the device and select &#x0022;Connect.&#x0022;">
+<!ENTITY addDevice.dialog.enterCode.label "Enter the code that the device provides:">
+<!ENTITY addDevice.dialog.tryAgain.label "Please try again.">
+<!ENTITY addDevice.dialog.successful.label "The device has been successfully added. The initial synchronization can take several minutes and will finish in the background.">
+<!ENTITY addDevice.dialog.recoveryKey.label "To activate your device you will need to enter your Recovery Key. Please print or save this key and take it with you.">
+<!ENTITY addDevice.dialog.connected.label "Device Connected">
+
+<!-- Existing Account Page 2: Manual Login -->
+<!ENTITY setup.signInPage.title.label "Sign In">
+<!ENTITY existingRecoveryKey.description "You can get a copy of your Recovery Key by going to &syncBrand.shortName.label; Preferences/Options on your other device, and selecting &#x0022;My Recovery Key&#x0022; under &#x0022;Manage Account&#x0022;.">
+<!ENTITY verifying.label "Verifying…">
+<!ENTITY resetPassword.label "Reset Password">
+<!ENTITY resetSyncKey.label "I have lost my other device.">
+
+<!-- Sync Options -->
+<!ENTITY setup.optionsPage.title "Sync Options">
+<!ENTITY syncComputerName.label "Computer Name:">
+<!ENTITY syncComputerName.accesskey "C">
+
+<!ENTITY syncMy.label "Sync My">
+<!ENTITY engine.addons.label "Add-ons">
+<!ENTITY engine.addons.accesskey "A">
+<!ENTITY engine.bookmarks.label "Bookmarks">
+<!ENTITY engine.bookmarks.accesskey "B">
+<!ENTITY engine.tabs.label "Tabs">
+<!ENTITY engine.tabs.accesskey "T">
+<!ENTITY engine.history.label "History">
+<!ENTITY engine.history.accesskey "r">
+<!ENTITY engine.passwords.label "Passwords">
+<!ENTITY engine.passwords.accesskey "P">
+<!ENTITY engine.prefs.label "Preferences">
+<!ENTITY engine.prefs.accesskey "e">
+
+<!ENTITY choice2.merge.main.label "Merge this computer's data with my &syncBrand.shortName.label; data">
+<!ENTITY choice2.merge.recommended.label "(recommended)">
+<!ENTITY choice2.client.main.label "Replace all data on this computer with my &syncBrand.shortName.label; data">
+<!ENTITY choice2.server.main.label "Replace all other devices with this computer's data">
+
+<!-- Confirm Merge Options -->
+<!ENTITY setup.optionsConfirmPage.title "Confirm">
+<!ENTITY confirm.merge.label "&syncBrand.fullName.label; will now merge all this computer's browser data into your Sync account.">
+<!ENTITY confirm.client2.label "Warning: The following &brandShortName; data on this computer will be deleted:">
+<!ENTITY confirm.client.moreinfo.label "&brandShortName; will then copy your &syncBrand.fullName.label; data to this computer.">
+<!ENTITY confirm.server2.label "Warning: The following devices will be overwritten with your local data:">
+
+<!-- New & Existing Account: Setup Complete -->
+<!ENTITY setup.successPage.title "Setup Complete">
+<!ENTITY changeOptions.label "You can change this preference by selecting Sync Options below.">
+<!ENTITY continueUsing.label "You may now continue using &brandShortName;.">
diff --git a/comm/suite/locales/en-US/chrome/common/sync/syncSetup.properties b/comm/suite/locales/en-US/chrome/common/sync/syncSetup.properties
new file mode 100644
index 0000000000..f92cd37d9a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/sync/syncSetup.properties
@@ -0,0 +1,50 @@
+# 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/.
+
+button.syncOptions.label = Sync Options
+button.syncOptionsDone.label = Done
+button.syncOptionsCancel.label = Cancel
+
+invalidEmail.label = Invalid email address
+serverInvalid.label = Please enter a valid server URL
+usernameNotAvailable.label = Already in use
+
+verifying.label = Verifying…
+
+# LOCALIZATION NOTE (additionalClientCount.label):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of additional clients (was %S for a short while, use #1 instead, even if both work)
+additionalClientCount.label = and #1 additional device;and #1 additional devices
+# LOCALIZATION NOTE (bookmarksCount.label):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of bookmarks (was %S for a short while, use #1 instead, even if both work)
+bookmarksCount.label = #1 bookmark;#1 bookmarks
+# LOCALIZATION NOTE (historyDaysCount.label):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of days (was %S for a short while, use #1 instead, even if both work)
+historyDaysCount.label = #1 day of history;#1 days of history
+# LOCALIZATION NOTE (passwordsCount.label):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of passwords (was %S for a short while, use #1 instead, even if both work)
+passwordsCount.label = #1 password;#1 passwords
+# LOCALIZATION NOTE (addonsCount.label):
+# #1 is the number of add-ons, see the link above for forms
+addonsCount.label = #1 add-on;#1 add-ons
+
+save.recoverykey.title = Save Recovery Key
+save.recoverykey.defaultfilename = SeaMonkey Recovery Key.xhtml
+
+newAccount.action.label = SeaMonkey Sync is now set up to automatically sync all of your browser data.
+newAccount.change.label = You can choose exactly what to sync by selecting Sync Options below.
+resetClient.change.label = SeaMonkey Sync will now merge all this computer's browser data into your Sync account.
+wipeClient.change.label = SeaMonkey Sync will now replace all of the browser data on this computer with the data in your Sync account.
+wipeRemote.change.label = SeaMonkey Sync will now replace all of the browser data in your Sync account with the data on this computer.
+existingAccount.change.label = You can change this preference by selecting Sync Options below.
+
+# Several other strings are used (via Weave.Status.login), but they come from
+# /services/sync
diff --git a/comm/suite/locales/en-US/chrome/common/tasksOverlay.dtd b/comm/suite/locales/en-US/chrome/common/tasksOverlay.dtd
new file mode 100644
index 0000000000..18f0b5999d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/tasksOverlay.dtd
@@ -0,0 +1,64 @@
+<!-- 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/. -->
+
+<!-- extracted from tasksOverlay.xul -->
+<!ENTITY minimizeWindowCmd.label "Minimize">
+<!ENTITY minimizeWindowCmd.key "M">
+<!ENTITY zoomWindowCmd.label "Zoom">
+<!ENTITY navigatorCmd.label "Browser">
+<!ENTITY navigatorCmd.accesskey "B">
+<!ENTITY navigatorCmd.commandkey "1">
+
+<!-- LOCALIZATION NOTE (editorCmd.label): DONT_TRANSLATE -->
+<!ENTITY editorCmd.label "Composer">
+<!ENTITY editorCmd.accesskey "C">
+<!ENTITY editorCmd.commandkey "4">
+
+<!ENTITY errorConsoleCmd.label "Error Console">
+<!ENTITY errorConsoleCmd.accesskey "C">
+<!ENTITY errorConsoleCmd.commandkey2 "j">
+
+<!ENTITY taskNavigator.tooltip "Browser">
+<!ENTITY taskComposer.tooltip "Composer">
+
+<!ENTITY webDevelopment.label "Web Development">
+<!ENTITY webDevelopment.accesskey "W">
+
+<!ENTITY windowMenu.label "Window">
+<!ENTITY windowMenu.accesskey "W">
+
+<!ENTITY tasksMenu.label "Tools">
+<!ENTITY tasksMenu.accesskey "T">
+
+<!ENTITY datamanCmd.label "Data Manager">
+<!ENTITY datamanCmd.accesskey "D">
+
+<!ENTITY passwordManagerCmd.label "Password Manager">
+<!ENTITY passwordManagerCmd.accesskey "P">
+
+<!ENTITY passwordDisplayCmd.label "Manage Stored Passwords">
+<!ENTITY passwordDisplayCmd.accesskey "M">
+
+<!ENTITY passwordExpireCmd.label "Log Out">
+<!ENTITY passwordExpireCmd.accesskey "l">
+
+<!ENTITY downloadManagerCmd.label "Download Manager">
+<!ENTITY downloadManagerCmd.accesskey "n">
+<!ENTITY downloadManagerCmd.commandkey "j">
+
+<!ENTITY addOnsManagerCmd.label "Add-ons Manager">
+<!ENTITY addOnsManagerCmd.accesskey "A">
+<!ENTITY addOnsManagerCmd.commandkey "a">
+
+<!ENTITY switchProfileCmd.label "Switch Profile…">
+<!ENTITY switchProfileCmd.accesskey "h">
+
+<!ENTITY syncBrand.shortName.label "Sync">
+<!-- LOCALIZATION NOTE (syncSetup.accesskey, syncSyncNowItem.accesskey):
+ Only one of these will show at a time (based on setup state),
+ so reusing accesskey is ok. -->
+<!ENTITY syncSetup.label "Set Up Sync…">
+<!ENTITY syncSetup.accesskey "S">
+<!ENTITY syncSyncNowItem.label "Sync Now">
+<!ENTITY syncSyncNowItem.accesskey "S">
diff --git a/comm/suite/locales/en-US/chrome/common/typeaheadfind.properties b/comm/suite/locales/en-US/chrome/common/typeaheadfind.properties
new file mode 100644
index 0000000000..93c61e476f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/typeaheadfind.properties
@@ -0,0 +1,17 @@
+# 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/.
+
+openparen = (
+closeparen = )
+textfound = Text found: "
+textnotfound = Text not found: "
+linkfound = Link found: "
+linknotfound = Link not found: "
+closequote = "
+stopfind = Find stopped.
+starttextfind = Starting -- find text as you type
+startlinkfind = Starting -- find links as you type
+repeated = repeated
+nextmatch = - next match
+prevmatch = - previous match
diff --git a/comm/suite/locales/en-US/chrome/common/utilityOverlay.dtd b/comm/suite/locales/en-US/chrome/common/utilityOverlay.dtd
new file mode 100644
index 0000000000..49a192de96
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/utilityOverlay.dtd
@@ -0,0 +1,206 @@
+<!-- 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/. -->
+
+<!-- these things need to move into utilityOverlay.xul -->
+<!ENTITY offlineGoOfflineCmd.label "Work Offline">
+<!ENTITY offlineGoOfflineCmd.accesskey "k">
+
+<!-- LOCALIZATION NOTE : FILE This file contains the global menu items -->
+
+<!ENTITY fileMenu.label "File">
+<!ENTITY fileMenu.accesskey "F">
+<!ENTITY newMenu.label "New">
+<!ENTITY newMenu.accesskey "N">
+<!ENTITY newBlankPageCmd.label "Composer Page">
+<!ENTITY newBlankPageCmd.accesskey "P">
+<!ENTITY newBlankPageCmd.key "n">
+<!ENTITY newPageFromTemplateCmd.label "Page Using Template">
+<!ENTITY newPageFromTemplateCmd.accesskey "t">
+<!ENTITY newPageFromDraftCmd.label "Page Using Draft">
+<!ENTITY newPageFromDraftCmd.accesskey "d">
+<!ENTITY newNavigatorCmd.label "Browser Window">
+<!ENTITY newNavigatorCmd.key "N">
+<!ENTITY newNavigatorCmd.accesskey "B">
+<!ENTITY newPrivateWindowCmd.label "Private Window">
+<!ENTITY newPrivateWindowCmd.key "B">
+<!ENTITY newPrivateWindowCmd.accesskey "W">
+<!ENTITY closeCmd.label "Close">
+<!ENTITY closeCmd.key "W">
+<!ENTITY closeCmd.accesskey "C">
+<!ENTITY printSetupCmd.label "Page Setup…">
+<!ENTITY printSetupCmd.accesskey "u">
+<!ENTITY printPreviewCmd.label "Print Preview">
+<!ENTITY printPreviewCmd.accesskey "v">
+<!ENTITY printCmd.label "Print…">
+<!ENTITY printCmd.accesskey "P">
+<!ENTITY printCmd.key "P">
+
+<!-- LOCALIZATION NOTE (.modifiers): The following entites are for the
+ application menu. Never change the modifiers unless you are 100% sure that
+ they are different on your locale (should be very rare). -->
+<!ENTITY preferencesCmdMac.label "Preferences…">
+<!ENTITY preferencesCmdMac.key ",">
+<!ENTITY preferencesCmdMac.modifiers "accel">
+<!ENTITY servicesMenu.label "Services">
+<!ENTITY hideThisAppCmd.label "Hide &brandShortName;">
+<!ENTITY hideThisAppCmd.key "H">
+<!ENTITY hideThisAppCmd.modifiers "accel">
+<!ENTITY hideOtherAppsCmd.label "Hide Others">
+<!ENTITY hideOtherAppsCmd.key "H">
+<!ENTITY hideOtherAppsCmd.modifiers "accel,alt">
+<!ENTITY showAllAppsCmd.label "Show All">
+<!ENTITY openHelpCmdMac.label "&brandShortName; Help">
+<!ENTITY openHelpCmdMac.accesskey "H">
+<!ENTITY openHelpCmdMac.key "?">
+<!ENTITY openHelpCmdMac.modifiers "accel">
+
+<!ENTITY quitApplicationCmd.label "Exit">
+<!ENTITY quitApplicationCmd.key "Q">
+<!ENTITY quitApplicationCmd.accesskey "x">
+<!ENTITY quitApplicationCmdMac.label "Quit &brandShortName;">
+<!ENTITY quitApplicationCmdMac.accesskey "Q">
+<!ENTITY quitApplicationCmdUnix.label "Quit">
+<!ENTITY quitApplicationCmdUnix.accesskey "Q">
+
+<!ENTITY editMenu.label "Edit">
+<!ENTITY editMenu.accesskey "E">
+<!ENTITY undoCmd.label "Undo">
+<!ENTITY undoCmd.key "Z">
+<!ENTITY undoCmd.accesskey "U">
+<!ENTITY redoCmd.label "Redo">
+<!ENTITY redoCmd.key "Y">
+<!ENTITY redoCmdMac.key "Z">
+<!ENTITY redoCmd.accesskey "R">
+<!ENTITY cutCmd.label "Cut">
+<!ENTITY cutCmd.key "X">
+<!ENTITY cutCmd.accesskey "t">
+<!ENTITY copyCmd.label "Copy">
+<!ENTITY copyCmd.key "C">
+<!ENTITY copyCmd.accesskey "C">
+<!ENTITY pasteCmd.label "Paste">
+<!ENTITY pasteCmd.key "V">
+<!ENTITY pasteCmd.accesskey "P">
+<!ENTITY pasteGoCmd.label "Paste &amp; Go">
+<!ENTITY pasteGoCmd.accesskey "G">
+
+<!-- LOCALIZATION NOTE (pasteSearchCmd): "Search" is a verb, this is the
+ search bar equivalent to the url bar's "Paste & Go" -->
+<!ENTITY pasteSearchCmd.label "Paste &amp; Search">
+<!ENTITY pasteSearchCmd.accesskey "e">
+<!ENTITY deleteCmd.label "Delete">
+<!ENTITY deleteCmd.accesskey "D">
+<!ENTITY selectAllCmd.label "Select All">
+<!ENTITY selectAllCmd.key "A">
+<!ENTITY selectAllCmd.accesskey "A">
+<!ENTITY clearHistoryCmd.label "Clear Search History">
+<!ENTITY clearHistoryCmd.accesskey "H">
+<!ENTITY showSuggestionsCmd.label "Show Suggestions">
+<!ENTITY showSuggestionsCmd.accesskey "S">
+<!ENTITY preferencesCmd.label "Preferences…">
+<!ENTITY preferencesCmd.key "E">
+<!ENTITY preferencesCmd.accesskey "e">
+<!ENTITY findBarCmd.key "F">
+<!-- LOCALIZATION NOTE (findBarCmd.accesskey): This accesskey should be within
+ findBarCmd.label found in editorOverlay.dtd, findCmd.label in messenger.dtd
+ and messengercompose.dtd and findOnCmd.label found in navigatorOverlay.dtd -->
+<!ENTITY findBarCmd.accesskey "F">
+<!ENTITY findCmd.key2 "VK_F19">
+<!ENTITY findReplaceCmd.key "H">
+<!ENTITY findReplaceCmdMac.key "F">
+<!ENTITY findReplaceCmd.accesskey "l">
+<!ENTITY findAgainCmd.label "Find Again">
+<!ENTITY findAgainCmd.key "G">
+<!ENTITY findAgainCmd.accesskey "g">
+<!ENTITY findAgainCmd.key2 "VK_F3">
+<!ENTITY findPrevCmd.label "Find Previous">
+<!ENTITY findPrevCmd.key "G">
+<!ENTITY findPrevCmd.key2 "VK_F3">
+<!ENTITY findPrevCmd.accesskey "v">
+<!ENTITY findTypeTextCmd.label "Find Text As You Type">
+<!ENTITY findTypeTextCmd.key "/">
+<!ENTITY findTypeTextCmd.accesskey "x">
+<!ENTITY findTypeLinksCmd.label "Find Links As You Type">
+<!ENTITY findTypeLinksCmd.key "'">
+<!ENTITY findTypeLinksCmd.accesskey "k">
+
+<!ENTITY viewMenu.label "View">
+<!ENTITY viewMenu.accesskey "V">
+<!ENTITY viewToolbarsMenu.label "Show/Hide">
+<!ENTITY viewToolbarsMenu.accesskey "w">
+<!ENTITY showTaskbarCmd.label "Status Bar">
+<!ENTITY showTaskbarCmd.accesskey "S">
+
+<!ENTITY helpMenu.label "Help">
+<!ENTITY helpMenu.accesskey "H">
+<!-- LOCALIZATION NOTE some localizations of Windows use "?"
+ for the help button in the menubar. -->
+<!ENTITY helpMenuWin.label "Help">
+<!ENTITY helpMenuWin.accesskey "H">
+<!ENTITY openHelpCmd.label "Help Contents">
+<!ENTITY openHelpCmd.accesskey "H">
+<!ENTITY openHelpCmd.key "VK_F1">
+
+<!ENTITY helpTroubleshootingInfo.label "Troubleshooting Information">
+<!ENTITY helpTroubleshootingInfo.accesskey "T">
+<!ENTITY releaseCmd.label "Release Notes">
+<!ENTITY releaseCmd.accesskey "N">
+<!ENTITY helpSafeMode.label "Restart with Add-ons Disabled">
+<!ENTITY helpSafeMode.accesskey "R">
+<!ENTITY updateCmd.label "Check for Updates…">
+<!ENTITY updateCmd.accesskey "C">
+<!ENTITY aboutCmd.label "About &brandShortName;">
+<!ENTITY aboutCmd.accesskey "A">
+<!ENTITY aboutBuildConfigCmd.label "Show Build Configuration">
+<!ENTITY aboutBuildConfigCmd.accesskey "B">
+
+<!ENTITY direct.label "Online (Proxy: None)">
+<!ENTITY direct.accesskey "N">
+<!ENTITY manual.label "Online (Proxy: Manual)">
+<!ENTITY manual.accesskey "M">
+<!ENTITY pac.label "Online (Proxy: Auto URL)">
+<!ENTITY pac.accesskey "A">
+<!ENTITY wpad.label "Online (Proxy: Auto Discover)">
+<!ENTITY wpad.accesskey "D">
+<!ENTITY system.label "Online (Proxy: System Proxy)">
+<!ENTITY system.accesskey "S">
+
+<!ENTITY proxy.label "Proxy Configuration…">
+<!ENTITY proxy.accesskey "C">
+
+<!ENTITY bidiSwitchTextDirectionItem.label "Switch Text Direction">
+<!ENTITY bidiSwitchTextDirectionItem.accesskey "w">
+<!ENTITY bidiSwitchTextDirectionItem.commandkey "X">
+
+<!ENTITY menubarCmd.label "Menu Bar">
+<!ENTITY menubarCmd.accesskey "e">
+
+<!ENTITY customizeToolbarContext.label "Customize…">
+<!ENTITY customizeToolbarContext.accesskey "C">
+
+<!ENTITY customizeToolbar.toolbarmode.label "Settings for this toolbar">
+<!ENTITY customizeToolbar.toolbarmode.accesskey "e">
+<!ENTITY customizeToolbar.iconsAndText.label "Icons and Text">
+<!ENTITY customizeToolbar.iconsAndText.accesskey "a">
+<!ENTITY customizeToolbar.icons.label "Icons">
+<!ENTITY customizeToolbar.icons.accesskey "o">
+<!ENTITY customizeToolbar.text.label "Text">
+<!ENTITY customizeToolbar.text.accesskey "T">
+<!ENTITY customizeToolbar.useSmallIcons.label "Use small icons">
+<!ENTITY customizeToolbar.useSmallIcons.accesskey "s">
+<!ENTITY customizeToolbar.labelAlignEnd.label "Show text beside icon">
+<!ENTITY customizeToolbar.labelAlignEnd.accesskey "b">
+<!ENTITY customizeToolbar.useDefault.label "Use default settings">
+<!ENTITY customizeToolbar.useDefault.accesskey "U">
+
+<!-- Popup Blocked notification menu -->
+<!ENTITY allowPopups.accesskey "p">
+<!ENTITY showPopupManager.label "Manage Popups">
+<!ENTITY showPopupManager.accesskey "M">
+<!ENTITY dontShowMessage.label "Don't show this message when popups are blocked">
+<!ENTITY dontShowMessage.accesskey "D">
+
+<!ENTITY throbber.title "Throbber">
+<!ENTITY throbber.tooltip2 "Go to the &brandShortName; home page">
+
+<!ENTITY syncToolbarButton.label "Sync">
diff --git a/comm/suite/locales/en-US/chrome/common/utilityOverlay.properties b/comm/suite/locales/en-US/chrome/common/utilityOverlay.properties
new file mode 100644
index 0000000000..c0e1386bf7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/utilityOverlay.properties
@@ -0,0 +1,35 @@
+# 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/.
+
+# Online/offline tooltips
+onlineTooltip0=You are online (proxy: none). Click the icon to go offline.
+onlineTooltip1=You are online (proxy: manual). Click the icon to go offline.
+onlineTooltip2=You are online (proxy: auto URL). Click the icon to go offline.
+onlineTooltip4=You are online (proxy: auto discover). Click the icon to go offline.
+onlineTooltip5=You are online (proxy: use system proxy). Click the icon to go offline.
+offlineTooltip=You are offline. Click the icon to go online.
+
+# Popup menus
+popupMenuShow=Show %S
+popupAllow=Allow popups from %S
+
+# Check for Updates
+updatesItem_default=Check for Updates…
+updatesItem_defaultFallback=Check for Updates…
+updatesItem_defaultAccessKey=C
+updatesItem_downloading=Downloading %S…
+updatesItem_downloadingFallback=Downloading Update…
+updatesItem_downloadingAccessKey=D
+updatesItem_resume=Resume Downloading %S…
+updatesItem_resumeFallback=Resume Downloading Update…
+updatesItem_resumeAccessKey=D
+updatesItem_pending=Apply Downloaded Update Now…
+updatesItem_pendingFallback=Apply Downloaded Update Now…
+updatesItem_pendingAccessKey=U
+
+# safeModeRestart
+safeModeRestartPromptTitle=Restart with Add-ons Disabled
+safeModeRestartPromptMessage=Are you sure you want to disable all add-ons and restart?
+safeModeRestartButton=Restart
+safeModeRestartCheckbox=Restart with Add-ons Disabled
diff --git a/comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.dtd b/comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.dtd
new file mode 100644
index 0000000000..47958fb417
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.dtd
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+
+<!ENTITY applyTheme.label "Apply Theme">
+<!ENTITY applyTheme.accesskey "A">
+<!ENTITY getMoreThemesCmd.label "Get More Themes">
+<!ENTITY getMoreThemesCmd.accesskey "G">
+<!ENTITY getBackgroundsCmd.label "Get Backgrounds">
+<!ENTITY getBackgroundsCmd.accesskey "B">
diff --git a/comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.properties b/comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.properties
new file mode 100644
index 0000000000..dbd657b07c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/viewApplyThemeOverlay.properties
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+switchskins=Theme changes will take effect when you restart %S.
+switchskinstitle=Apply Theme
+switchskinsnow=Restart Now
+switchskinslater=Restart Later
diff --git a/comm/suite/locales/en-US/chrome/common/viewZoomOverlay.dtd b/comm/suite/locales/en-US/chrome/common/viewZoomOverlay.dtd
new file mode 100644
index 0000000000..c111107943
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/viewZoomOverlay.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE: do not use digits "0"-"9" as accesskeys -->
+<!ENTITY zoomEnlargeCmd.label "Larger">
+<!ENTITY zoomEnlargeCmd.accesskey "L">
+<!ENTITY zoomEnlargeCmd.commandkey "+">
+<!ENTITY zoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
+
+<!ENTITY zoomReduceCmd.label "Smaller">
+<!ENTITY zoomReduceCmd.accesskey "S">
+<!ENTITY zoomReduceCmd.commandkey "-">
+
+<!ENTITY zoomResetCmd.commandkey "0">
diff --git a/comm/suite/locales/en-US/chrome/common/viewZoomOverlay.properties b/comm/suite/locales/en-US/chrome/common/viewZoomOverlay.properties
new file mode 100644
index 0000000000..6090937cd4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/common/viewZoomOverlay.properties
@@ -0,0 +1,32 @@
+# 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/.
+
+# zoom submenu
+#
+# LOCALIZATION NOTE: don't translate %zoom% in any property
+# don't use digits "0"-"9" for accesskeys
+
+# LOCALIZATION NOTE (fullZoom,textZoom): are never available at the same time
+fullZoom.label=Zoom (%zoom% %)
+fullZoom.accesskey=Z
+textZoom.label=Text Zoom (%zoom% %)
+textZoom.accesskey=Z
+
+# labels and accesskeys to emphasize the 100 % and 200 % entries
+zoom.100.label=100 % (Original Size)
+zoom.100.accesskey=z
+zoom.200.label=200 % (Double Size)
+zoom.200.accesskey=D
+
+# labels and accesskeys to emphasize the minimum and maximum boundaries
+zoom.min.label=%zoom% % (Minimum)
+zoom.min.accesskey=n
+zoom.max.label=%zoom% % (Maximum)
+zoom.max.accesskey=x
+
+# label pattern for remaining values, accesskeys are assigned dynamically
+zoom.value.label=%zoom% %
+
+zoom.other.label=Other (%zoom% %) …
+zoom.other.accesskey=O
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EdAdvancedEdit.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EdAdvancedEdit.dtd
new file mode 100644
index 0000000000..83fcbd7416
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EdAdvancedEdit.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!ENTITY WindowTitle.label "Advanced Property Editor">
+<!ENTITY AttName.label "Attribute: ">
+<!ENTITY AttValue.label "Value: ">
+<!ENTITY PropertyName.label "Property: ">
+<!ENTITY currentattributesfor.label "Current attributes for: ">
+<!ENTITY tree.attributeHeader.label "Attribute">
+<!ENTITY tree.propertyHeader.label "Property">
+<!ENTITY tree.valueHeader.label "Value">
+<!ENTITY tabHTML.label "HTML Attributes">
+<!ENTITY tabCSS.label "Inline Style">
+<!ENTITY tabJSE.label "JavaScript Events">
+
+<!ENTITY editAttribute.label "Click on an item above to edit its value">
+<!ENTITY removeAttribute.label "Remove">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EdColorPicker.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EdColorPicker.dtd
new file mode 100644
index 0000000000..c00d24f298
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EdColorPicker.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Color">
+<!ENTITY lastPickedColor.label "Last-picked color">
+<!ENTITY lastPickedColor.accessKey "L">
+<!ENTITY chooseColor1.label "Choose a color:">
+<!ENTITY chooseColor2.label "Enter an HTML color string">
+<!ENTITY chooseColor2.accessKey "H">
+<!ENTITY setColorExample.label "(e.g.: &quot;#0000ff&quot; or &quot;blue&quot;):">
+<!ENTITY default.label "Default">
+<!ENTITY default.accessKey "D">
+<!ENTITY palette.label "Palette:">
+<!ENTITY standardPalette.label "Standard">
+<!ENTITY webPalette.label "All web colors">
+<!ENTITY background.label "Background for:">
+<!ENTITY background.accessKey "B">
+<!ENTITY table.label "Table">
+<!ENTITY table.accessKey "T">
+<!ENTITY cell.label "Cell(s)">
+<!ENTITY cell.accessKey "C">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EdConvertToTable.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EdConvertToTable.dtd
new file mode 100644
index 0000000000..044f60e496
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EdConvertToTable.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Convert To Table">
+<!ENTITY instructions1.label "Composer creates a new table row for each paragraph in the selection.">
+<!ENTITY instructions2.label "Choose the character used to separate the selection into columns:">
+<!ENTITY commaRadio.label "Comma">
+<!ENTITY spaceRadio.label "Space">
+<!ENTITY otherRadio.label "Other Character:">
+<!ENTITY deleteCharCheck.label "Delete separator character">
+<!ENTITY collapseSpaces.label "Ignore extra spaces">
+<!ENTITY collapseSpaces.tooltip "Convert adjacent spaces to one separator">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EdDialogOverlay.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EdDialogOverlay.dtd
new file mode 100644
index 0000000000..527e723a5a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EdDialogOverlay.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!ENTITY AdvancedEditButton.label "Advanced Edit…">
+<!ENTITY AdvancedEditButton.accessKey "E">
+<!ENTITY AdvancedEditButton.tooltip "Add or modify HTML attributes, style attributes, and JavaScript">
+<!ENTITY chooseFileButton.label "Choose File...">
+<!ENTITY chooseFileButton.accessKey "F">
+<!ENTITY chooseFileLinkButton.label "Choose File...">
+<!ENTITY chooseFileLinkButton.accessKey "o">
+<!ENTITY makeUrlRelative.label "URL is relative to page location">
+<!ENTITY makeUrlRelative.accessKey "r">
+<!ENTITY makeUrlRelative.tooltip "Change between relative and absolute URL. You must first save the page to change this.">
+
+<!-- Shared by Link and Image dialogs -->
+<!ENTITY LinkURLEditField2.label "Enter a web page location, a local file, or select a Named Anchor or Heading from the field's context menu:">
+<!ENTITY LinkURLEditField2.accessKey "w">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EdNamedAnchorProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EdNamedAnchorProperties.dtd
new file mode 100644
index 0000000000..d418ed14b6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EdNamedAnchorProperties.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY windowTitle.label "Named Anchor Properties">
+<!ENTITY anchorNameEditField.label "Anchor Name:">
+<!ENTITY anchorNameEditField.accessKey "N">
+<!ENTITY nameInput.tooltip "Enter a unique name for this named anchor (target)">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditConflict.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditConflict.dtd
new file mode 100644
index 0000000000..d874ff06b0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditConflict.dtd
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Select Edit Changes">
+<!ENTITY conflictWarning.label "This page has been modified by another program, but you also have unsaved changes in Composer.">
+<!ENTITY conflictResolve.label "Select which version to keep:">
+<!ENTITY keepCurrentPageButton.label "Keep current page changes">
+<!ENTITY useOtherPageButton.label "Replace current page with other changes">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorButtonProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorButtonProperties.dtd
new file mode 100644
index 0000000000..efc6b400dc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorButtonProperties.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Button Properties">
+
+<!ENTITY Settings.label "Settings">
+
+<!ENTITY ButtonType.label "Type">
+<!ENTITY ButtonType.accesskey "T">
+<!ENTITY submit.value "Submit">
+<!ENTITY reset.value "Reset">
+<!ENTITY button.value "Button">
+
+<!ENTITY ButtonName.label "Name:">
+<!ENTITY ButtonName.accesskey "N">
+<!ENTITY ButtonValue.label "Value:">
+<!ENTITY ButtonValue.accesskey "V">
+<!ENTITY tabIndex.label "Tab Index:">
+<!ENTITY tabIndex.accesskey "I">
+<!ENTITY ButtonDisabled.label "Disabled">
+<!ENTITY ButtonDisabled.accesskey "D">
+<!ENTITY AccessKey.label "Access Key:">
+<!ENTITY AccessKey.accesskey "K">
+
+<!ENTITY RemoveButton.label "Remove Button">
+<!ENTITY RemoveButton.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorColorProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorColorProperties.dtd
new file mode 100644
index 0000000000..2e3bf76925
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorColorProperties.dtd
@@ -0,0 +1,29 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Page Colors and Background">
+<!ENTITY pageColors.label "Page Colors">
+<!ENTITY defaultColorsRadio.label "Reader's default colors (Don't set colors in page)">
+<!ENTITY defaultColorsRadio.accessKey "D">
+<!ENTITY defaultColorsRadio.tooltip "Use the color settings from the viewer (reader's) browser only">
+<!ENTITY customColorsRadio.label "Use custom colors:">
+<!ENTITY customColorsRadio.accessKey "C">
+<!ENTITY customColorsRadio.tooltip "These color settings override the viewer's browser settings">
+
+<!ENTITY normalText.label "Normal text">
+<!ENTITY normalText.accessKey "N">
+<!ENTITY linkText.label "Link text">
+<!ENTITY linkText.accessKey "L">
+<!ENTITY activeLinkText.label "Active link text">
+<!ENTITY activeLinkText.accessKey "A">
+<!ENTITY visitedLinkText.label "Visited link text">
+<!ENTITY visitedLinkText.accessKey "V">
+<!ENTITY background.label "Background:">
+<!ENTITY background.accessKey "B">
+<!ENTITY colon.character ":">
+<!ENTITY backgroundImage.label "Background Image:">
+<!ENTITY backgroundImage.accessKey "m">
+<!ENTITY backgroundImage.tooltip "Use an image file as the background for your page">
+<!ENTITY backgroundImage.shortenedDataURI "Shortened data URI (copy will place the full URI onto the clipboard)">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorFieldSetProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorFieldSetProperties.dtd
new file mode 100644
index 0000000000..a2db9d4e69
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorFieldSetProperties.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Field Set Properties">
+
+<!ENTITY Legend.label "Legend">
+<!ENTITY Legend.accesskey "L">
+
+<!ENTITY EditLegendText.label "Edit Legend:">
+<!ENTITY EditLegendText.accesskey "T">
+<!ENTITY LegendAlign.label "Align Legend:">
+<!ENTITY LegendAlign.accesskey "A">
+<!ENTITY AlignDefault.label "Default">
+<!ENTITY AlignLeft.label "Left">
+<!ENTITY AlignCenter.label "Center">
+<!ENTITY AlignRight.label "Right">
+
+<!ENTITY RemoveFieldSet.label "Remove Field Set">
+<!ENTITY RemoveFieldSet.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorFormProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorFormProperties.dtd
new file mode 100644
index 0000000000..2389adff22
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorFormProperties.dtd
@@ -0,0 +1,21 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY windowTitle.label "Form Properties">
+
+<!ENTITY Settings.label "Settings">
+
+<!ENTITY FormName.label "Form Name:">
+<!ENTITY FormName.accesskey "N">
+<!ENTITY FormAction.label "Action URL:">
+<!ENTITY FormAction.accesskey "A">
+<!ENTITY FormMethod.label "Method:">
+<!ENTITY FormMethod.accesskey "M">
+<!ENTITY FormEncType.label "Encoding:">
+<!ENTITY FormEncType.accesskey "c">
+<!ENTITY FormTarget.label "Target Frame:">
+<!ENTITY FormTarget.accesskey "T">
+
+<!ENTITY RemoveForm.label "Remove Form">
+<!ENTITY RemoveForm.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorHLineProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorHLineProperties.dtd
new file mode 100644
index 0000000000..9ad023dee0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorHLineProperties.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Horizontal Line Properties">
+
+<!ENTITY dimensionsBox.label "Dimensions">
+<!ENTITY heightEditField.label "Height:">
+<!ENTITY heightEditField.accessKey "G">
+<!ENTITY widthEditField.label "Width:">
+<!ENTITY widthEditField.accessKey "W">
+<!ENTITY pixelsPopup.value "pixels">
+<!ENTITY alignmentBox.label "Alignment">
+<!ENTITY leftRadio.label "Left">
+<!ENTITY leftRadio.accessKey "L">
+<!ENTITY centerRadio.label "Center">
+<!ENTITY centerRadio.accessKey "C">
+<!ENTITY rightRadio.label "Right">
+<!ENTITY rightRadio.accessKey "R">
+
+<!ENTITY threeDShading.label "3-D Shading">
+<!ENTITY threeDShading.accessKey "S">
+<!ENTITY saveSettings.label "Use as Default">
+<!ENTITY saveSettings.accessKey "D">
+<!ENTITY saveSettings.tooltip "Save these settings to use when inserting new horizontal lines">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorImageProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorImageProperties.dtd
new file mode 100644
index 0000000000..280af6df2a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorImageProperties.dtd
@@ -0,0 +1,79 @@
+<!-- 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/. -->
+
+<!-- These strings are for use specifically in the editor's image and form image dialogs. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Image Properties">
+
+<!ENTITY pixelsPopup.value "pixels">
+
+<!-- These are in the Location tab panel -->
+<!ENTITY locationEditField.label "Image Location:">
+<!ENTITY locationEditField.accessKey "L">
+<!ENTITY locationEditField.tooltip "Type the image's filename or location">
+<!ENTITY locationEditField.shortenedDataURI "Shortened data URI (copy will place the full URI onto the clipboard)">
+<!ENTITY title.label "Tooltip:">
+<!ENTITY title.accessKey "T">
+<!ENTITY title.tooltip "The html 'title' attribute that displays as a tooltip">
+<!ENTITY altText.label "Alternate text:">
+<!ENTITY altText.accessKey "A">
+<!ENTITY altTextEditField.tooltip "Type text to display in place of the image">
+<!ENTITY noAltText.label "Don't use alternate text">
+<!ENTITY noAltText.accessKey "D">
+
+<!ENTITY previewBox.label "Image Preview">
+
+<!-- These controls are in the Dimensions tab panel -->
+<!-- actualSize.label should be same as actualSizeRadio.label + ":" -->
+<!ENTITY actualSize.label "Actual Size:">
+<!ENTITY actualSizeRadio.label "Actual Size">
+<!ENTITY actualSizeRadio.accessKey "A">
+<!ENTITY actualSizeRadio.tooltip "Revert to the image's actual size">
+<!ENTITY customSizeRadio.label "Custom Size">
+<!ENTITY customSizeRadio.accessKey "S">
+<!ENTITY customSizeRadio.tooltip "Change the image's size as displayed in the page">
+<!ENTITY heightEditField.label "Height:">
+<!ENTITY heightEditField.accessKey "G">
+<!ENTITY widthEditField.label "Width:">
+<!ENTITY widthEditField.accessKey "W">
+<!ENTITY constrainCheckbox.label "Constrain">
+<!ENTITY constrainCheckbox.accessKey "C">
+<!ENTITY constrainCheckbox.tooltip "Maintain the image's aspect ratio">
+
+<!-- These controls are in the Image Map box of the expanded area -->
+<!ENTITY imagemapBox.label "Image Map">
+<!ENTITY removeImageMapButton.label "Remove">
+<!ENTITY removeImageMapButton.accessKey "R">
+
+<!-- These are the options for image alignment -->
+<!ENTITY alignment.label "Align Text to Image">
+<!ENTITY bottomPopup.value "At the bottom">
+<!ENTITY topPopup.value "At the top">
+<!ENTITY centerPopup.value "In the center">
+<!ENTITY wrapRightPopup.value "Wrap to the right">
+<!ENTITY wrapLeftPopup.value "Wrap to the left">
+
+<!-- These controls are in the Spacing Box -->
+<!ENTITY spacingBox.label "Spacing">
+<!ENTITY leftRightEditField.label "Left and Right:">
+<!ENTITY leftRightEditField.accessKey "L">
+<!ENTITY topBottomEditField.label "Top and Bottom:">
+<!ENTITY topBottomEditField.accessKey "T">
+<!ENTITY borderEditField.label "Solid Border:">
+<!ENTITY borderEditField.accessKey "B">
+
+<!-- These controls are in the Link Box -->
+<!ENTITY showImageLinkBorder.label "Show border around linked image">
+<!ENTITY showImageLinkBorder.accessKey "B">
+<!ENTITY LinkAdvancedEditButton.label "Link Advanced Edit…">
+<!ENTITY LinkAdvancedEditButton.accessKey "L">
+<!ENTITY LinkAdvancedEditButton.tooltip "Add or modify HTML attributes, style attributes, and JavaScript">
+
+<!-- These tabs are currently used in the image input dialog -->
+<!ENTITY imageInputTab.label "Form">
+<!ENTITY imageLocationTab.label "Location">
+<!ENTITY imageDimensionsTab.label "Dimensions">
+<!ENTITY imageAppearanceTab.label "Appearance">
+<!ENTITY imageLinkTab.label "Link">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInputProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInputProperties.dtd
new file mode 100644
index 0000000000..ea7d09c75e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInputProperties.dtd
@@ -0,0 +1,50 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Form Field Properties">
+<!ENTITY windowTitleImage.label "Form Image Properties">
+
+<!ENTITY InputType.label "Field Type">
+<!ENTITY InputType.accesskey "T">
+<!ENTITY text.value "Text">
+<!ENTITY password.value "Password">
+<!ENTITY checkbox.value "Check Box">
+<!ENTITY radio.value "Radio Button">
+<!ENTITY submit.value "Submit Button">
+<!ENTITY reset.value "Reset Button">
+<!ENTITY file.value "File">
+<!ENTITY hidden.value "Hidden">
+<!ENTITY image.value "Image">
+<!ENTITY button.value "Button">
+
+<!ENTITY InputSettings.label "Field Settings">
+<!ENTITY InputName.label "Field Name:">
+<!ENTITY InputName.accesskey "N">
+<!ENTITY GroupName.label "Group Name:">
+<!ENTITY GroupName.accesskey "N">
+<!ENTITY InputValue.label "Field Value:">
+<!ENTITY InputValue.accesskey "V">
+<!ENTITY InitialValue.label "Initial Value:">
+<!ENTITY InitialValue.accesskey "V">
+<!ENTITY InputChecked.label "Initially Checked">
+<!ENTITY InputChecked.accesskey "C">
+<!ENTITY InputSelected.label "Initially Selected">
+<!ENTITY InputSelected.accesskey "S">
+<!ENTITY InputReadOnly.label "Read Only">
+<!ENTITY InputReadOnly.accesskey "R">
+<!ENTITY InputDisabled.label "Disabled">
+<!ENTITY InputDisabled.accesskey "D">
+<!ENTITY tabIndex.label "Tab Index:">
+<!ENTITY tabIndex.accesskey "I">
+<!ENTITY TextSize.label "Field Size:">
+<!ENTITY TextSize.accesskey "F">
+<!ENTITY TextLength.label "Maximum Length:">
+<!ENTITY TextLength.accesskey "L">
+<!ENTITY AccessKey.label "Access Key:">
+<!ENTITY AccessKey.accesskey "K">
+<!ENTITY Accept.label "Accept Types:">
+<!ENTITY Accept.accesskey "A">
+
+<!ENTITY ImageProperties.label "Image Properties…">
+<!ENTITY ImageProperties.accesskey "E">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertChars.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertChars.dtd
new file mode 100644
index 0000000000..1755e499ca
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertChars.dtd
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Insert Character">
+<!ENTITY category.label "Category">
+<!ENTITY letter.label "Letter:">
+<!ENTITY letter.accessKey "L">
+<!ENTITY character.label "Character:">
+<!ENTITY character.accessKey "C">
+<!ENTITY accentUpper.label "Accent Uppercase">
+<!ENTITY accentLower.label "Accent Lowercase">
+<!ENTITY otherUpper.label "Other Uppercase">
+<!ENTITY otherLower.label "Other Lowercase">
+<!ENTITY commonSymbols.label "Common Symbols">
+<!ENTITY insertButton.label "Insert">
+<!ENTITY closeButton.label "Close">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertMath.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertMath.dtd
new file mode 100644
index 0000000000..357ed0b20d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertMath.dtd
@@ -0,0 +1,21 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Insert Math">
+
+<!ENTITY sourceEditField.label "Enter LaTeX source code:">
+
+<!ENTITY options.label "Options">
+<!ENTITY optionInline.label "Inline mode">
+<!ENTITY optionInline.accesskey "N">
+<!ENTITY optionDisplay.label "Display mode">
+<!ENTITY optionDisplay.accesskey "D">
+<!ENTITY optionLTR.label "Left-to-right direction">
+<!ENTITY optionLTR.accesskey "L">
+<!ENTITY optionRTL.label "Right-to-left direction">
+<!ENTITY optionRTL.accesskey "R">
+
+<!ENTITY insertButton.label "Insert">
+<!ENTITY insertButton.accesskey "I">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertSource.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertSource.dtd
new file mode 100644
index 0000000000..0b51a86c8c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertSource.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Insert HTML">
+<!ENTITY sourceEditField.label "Enter HTML tags and text:">
+<!ENTITY example.label "Example: ">
+<!-- LOCALIZATION NOTE (exampleOpenTag.label): DONT_TRANSLATE: they are text for HTML tagnames: "<i>" and "</i>" -->
+<!ENTITY exampleOpenTag.label "&lt;i&gt;">
+<!-- LOCALIZATION NOTE (exampleCloseTag.label): DONT_TRANSLATE: they are text for HTML tagnames: "<i>" and "</i>" -->
+<!ENTITY exampleCloseTag.label "&lt;/i&gt;">
+<!ENTITY exampleText.label "Hello World!">
+<!ENTITY insertButton.label "Insert">
+<!ENTITY insertButton.accesskey "I">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTOC.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTOC.dtd
new file mode 100644
index 0000000000..f3285a357a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTOC.dtd
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+
+<!ENTITY Window.title "Table of Contents">
+<!ENTITY buildToc.label "Build table of contents from:">
+<!ENTITY tag.label "Tag:">
+<!ENTITY class.label "Class:">
+<!ENTITY header1.label "Level 1">
+<!ENTITY header2.label "Level 2">
+<!ENTITY header3.label "Level 3">
+<!ENTITY header4.label "Level 4">
+<!ENTITY header5.label "Level 5">
+<!ENTITY header6.label "Level 6">
+<!ENTITY makeReadOnly.label "Make the table of contents read-only">
+<!ENTITY orderedList.label "Number all entries in the table of contents">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTable.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTable.dtd
new file mode 100644
index 0000000000..00b5d2d131
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorInsertTable.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Insert Table">
+
+<!ENTITY size.label "Size">
+<!ENTITY numRowsEditField.label "Rows:">
+<!ENTITY numRowsEditField.accessKey "R">
+<!ENTITY numColumnsEditField.label "Columns:">
+<!ENTITY numColumnsEditField.accessKey "C">
+<!ENTITY widthEditField.label "Width:">
+<!ENTITY widthEditField.accessKey "W">
+<!ENTITY borderEditField.label "Border:">
+<!ENTITY borderEditField.accessKey "B">
+<!ENTITY borderEditField.tooltip "Type a number for the table's border, or type zero (0) for no border">
+<!ENTITY pixels.label "pixels">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorLabelProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorLabelProperties.dtd
new file mode 100644
index 0000000000..5ed05aa63d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorLabelProperties.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Label Properties">
+
+<!ENTITY Settings.label "Settings">
+<!ENTITY Settings.accesskey "S">
+
+<!ENTITY EditLabelText.label "Edit Text:">
+<!ENTITY EditLabelText.accesskey "T">
+<!ENTITY LabelFor.label "For Control:">
+<!ENTITY LabelFor.accesskey "F">
+<!ENTITY AccessKey.label "Access Key:">
+<!ENTITY AccessKey.accesskey "K">
+
+<!ENTITY RemoveLabel.label "Remove Label">
+<!ENTITY RemoveLabel.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorLinkProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorLinkProperties.dtd
new file mode 100644
index 0000000000..09b3b01550
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorLinkProperties.dtd
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Link Properties">
+<!ENTITY LinkURLBox.label "Link Location">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorListProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorListProperties.dtd
new file mode 100644
index 0000000000..2494330000
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorListProperties.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "List Properties">
+
+<!ENTITY ListType.label "List Type">
+<!ENTITY bulletStyle.label "Bullet Style:">
+<!ENTITY startingNumber.label "Start at:">
+<!ENTITY startingNumber.accessKey "S">
+<!ENTITY none.value "None">
+<!ENTITY bulletList.value "Bullet (Unnumbered) List">
+<!ENTITY numberList.value "Numbered List">
+<!ENTITY definitionList.value "Definition List">
+<!ENTITY changeEntireListRadio.label "Change entire list">
+<!ENTITY changeEntireListRadio.accessKey "C">
+<!ENTITY changeSelectedRadio.label "Change just selected items">
+<!ENTITY changeSelectedRadio.accessKey "I">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPageProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPageProperties.dtd
new file mode 100644
index 0000000000..4f36bad9a9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPageProperties.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Page Properties">
+<!ENTITY location.label "Location:">
+<!ENTITY lastModified.label "Last Modified:">
+<!ENTITY titleInput.label "Title:">
+<!ENTITY titleInput.accessKey "T">
+<!ENTITY authorInput.label "Author:">
+<!ENTITY authorInput.accessKey "A">
+<!ENTITY descriptionInput.label "Description:">
+<!ENTITY descriptionInput.accessKey "D">
+<!ENTITY locationNewPage.label "[New page, not saved yet]">
+<!ENTITY EditHEADSource1.label "Advanced users:">
+<!ENTITY EditHEADSource2.label "To edit other contents of the &lt;head&gt; region, use &quot;HTML Source&quot; in the View Menu or Edit Mode Toolbar.">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPersonalDictionary.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPersonalDictionary.dtd
new file mode 100644
index 0000000000..2a8cf9b729
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPersonalDictionary.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Personal Dictionary">
+
+<!ENTITY wordEditField.label "New word:">
+<!ENTITY wordEditField.accessKey "N">
+<!ENTITY AddButton.label "Add">
+<!ENTITY AddButton.accessKey "A">
+<!ENTITY DictionaryList.label "Words in dictionary:">
+<!ENTITY DictionaryList.accessKey "W">
+<!ENTITY ReplaceButton.label "Replace">
+<!ENTITY ReplaceButton.accessKey "R">
+<!ENTITY RemoveButton.label "Remove">
+<!ENTITY RemoveButton.accessKey "e">
+
+<!ENTITY CloseButton.label "Close">
+<!ENTITY CloseButton.accessKey "C">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublish.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublish.dtd
new file mode 100644
index 0000000000..60437b4318
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublish.dtd
@@ -0,0 +1,65 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Publish Page">
+<!ENTITY windowTitleSettings.label "Publish Settings">
+<!ENTITY publishTab.label "Publish">
+<!ENTITY settingsTab.label "Settings">
+<!ENTITY publishButton.label "Publish">
+
+<!-- Publish Tab Panel -->
+<!ENTITY siteList.label "Site Name:">
+<!ENTITY siteList.accesskey "e">
+<!ENTITY siteList.tooltip "Choose the site that you want to publish to">
+<!ENTITY newSiteButton.label "New Site">
+<!ENTITY newSiteButton.accesskey "N">
+<!ENTITY docDirList.label "Site subdirectory for this page:">
+<!ENTITY docDirList.accesskey "S">
+<!ENTITY docDirList.tooltip "Choose or enter the name of the remote subdirectory for this page">
+<!ENTITY publishImgCheckbox.label "Include images and other files">
+<!ENTITY publishImgCheckbox.accesskey "o">
+<!ENTITY publishImgCheckbox.tooltip "Publish images and other files referenced by this page">
+<!ENTITY sameLocationRadio.label "Use same location as page">
+<!ENTITY sameLocationRadio.accesskey "U">
+<!ENTITY sameLocationRadio.tooltip "Publish files to the same location as the page">
+<!ENTITY useSubdirRadio.label "Use this site subdirectory:">
+<!ENTITY useSubdirRadio.accesskey "d">
+<!ENTITY useSubdirRadio.tooltip "Publish files to the selected remote subdirectory">
+<!ENTITY otherDirList.tooltip "Choose or enter name of remote subdirectory where files will be published">
+<!ENTITY pageTitle.label "Page Title:">
+<!ENTITY pageTitle.accesskey "T">
+<!ENTITY pageTitle.tooltip "Enter a title to identify the page in the window and in bookmarks">
+<!ENTITY pageTitleExample.label "e.g.: &quot;My Web Page&quot;">
+<!ENTITY filename.label "Filename:">
+<!ENTITY filename.accesskey "F">
+<!ENTITY filename.tooltip "Enter a name for this file, including '.html' for a web page">
+<!ENTITY filenameExample.label "e.g,: &quot;mypage.html&quot;">
+<!ENTITY setDefaultButton.label "Set as Default">
+<!ENTITY setDefaultButton.accesskey "D">
+<!ENTITY removeButton.label "Remove Site">
+<!ENTITY removeButton.accesskey "R">
+
+<!-- Settings Tab Panel -->
+<!ENTITY publishSites.label "Publishing Sites">
+<!ENTITY serverInfo.label "Server Information">
+<!ENTITY loginInfo.label "Login Information">
+<!ENTITY siteName.label "Site Name:">
+<!ENTITY siteName.accesskey "e">
+<!ENTITY siteName.tooltip "A nickname that identifies this publishing site (e.g.: 'MySite')">
+<!ENTITY siteUrl.label "Publishing address (e.g.: 'ftp://ftp.myisp.com/myusername'):">
+<!ENTITY siteUrl.accesskey "a">
+<!ENTITY siteUrl.tooltip "The FTP:// or HTTP:// address provided by your ISP or web hosting service">
+<!ENTITY browseUrl.label "HTTP address of your homepage (e.g.: 'http://www.myisp.com/myusername'):">
+<!ENTITY browseUrl.accesskey "T">
+<!ENTITY browseUrl.tooltip "The HTTP:// address of your home directory (don't include filename)">
+<!ENTITY username.label "User name:">
+<!ENTITY username.accesskey "U">
+<!ENTITY username.tooltip "The user name you use to log in to your ISP or web hosting service">
+<!ENTITY password.label "Password:">
+<!ENTITY password.accesskey "w">
+<!ENTITY password.tooltip "The password associated with your user name">
+<!ENTITY savePassword.label "Save Password">
+<!ENTITY savePassword.accesskey "S">
+<!ENTITY savePassword.tooltip "Select this to save your password securely using Password Manager">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublishProgress.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublishProgress.dtd
new file mode 100644
index 0000000000..ef4927c680
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorPublishProgress.dtd
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY siteUrl.label "Site URL:">
+<!ENTITY docSubdir.label "Page subdirectory:">
+<!ENTITY otherSubdir.label "Image subdirectory:">
+
+<!ENTITY status.label "Publishing…">
+<!ENTITY fileList.label "Publishing Status">
+<!ENTITY succeeded.label "Succeeded">
+<!ENTITY failed.label "Failed">
+
+<!ENTITY keepOpen "Keep this window open after publishing is complete.">
+<!ENTITY closeButton.label "Close">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorReplace.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorReplace.dtd
new file mode 100644
index 0000000000..74d907a012
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorReplace.dtd
@@ -0,0 +1,27 @@
+<!-- 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/. -->
+
+<!-- extracted from EdReplace.xhtml -->
+
+<!ENTITY replaceDialog.title "Find and Replace">
+<!ENTITY findField.label "Find text:">
+<!ENTITY findField.accesskey "n">
+<!ENTITY replaceField.label "Replace with:">
+<!ENTITY replaceField.accesskey "e">
+<!ENTITY caseSensitiveCheckbox.label "Match exact case">
+<!ENTITY caseSensitiveCheckbox.accesskey "M">
+<!ENTITY wrapCheckbox.label "Wrap around">
+<!ENTITY wrapCheckbox.accesskey "W">
+<!ENTITY backwardsCheckbox.label "Search backwards">
+<!ENTITY backwardsCheckbox.accesskey "b">
+<!ENTITY findNextButton.label "Find Next">
+<!ENTITY findNextButton.accesskey "F">
+<!ENTITY replaceButton.label "Replace">
+<!ENTITY replaceButton.accesskey "R">
+<!ENTITY replaceAndFindButton.label "Replace and Find">
+<!ENTITY replaceAndFindButton.accesskey "d">
+<!ENTITY replaceAllButton.label "Replace All">
+<!ENTITY replaceAllButton.accesskey "A">
+<!ENTITY closeButton.label "Close">
+<!ENTITY closeButton.accesskey "C">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSaveAsCharset.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSaveAsCharset.dtd
new file mode 100644
index 0000000000..0fe33491c7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSaveAsCharset.dtd
@@ -0,0 +1,14 @@
+<!-- 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/. -->
+
+<!-- These strings are generic to all or most of the editor's dialogs. -->
+
+<!-- This button is for the progressive disclosure of additional editing functionality -->
+
+<!-- These strings are for use specifically in the editor's link dialog. -->
+<!ENTITY windowTitle2.label "Save And Change Text Encoding">
+<!ENTITY documentTitleTitle.label "Page Title">
+<!ENTITY documentCharsetTitle2.label "Text Encoding">
+<!ENTITY documentCharsetDesc2.label "Select the text encoding you want to save a document in:">
+<!ENTITY documentExportToText.label "Export to Text">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSelectProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSelectProperties.dtd
new file mode 100644
index 0000000000..e2eb20e698
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSelectProperties.dtd
@@ -0,0 +1,48 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Selection List Properties">
+
+<!ENTITY Select.label "Selection List">
+<!ENTITY SelectName.label "List Name:">
+<!ENTITY SelectName.accesskey "N">
+<!ENTITY SelectSize.label "Height:">
+<!ENTITY SelectSize.accesskey "H">
+<!ENTITY SelectMultiple.label "Multiple Selection">
+<!ENTITY SelectMultiple.accesskey "M">
+<!ENTITY SelectDisabled.label "Disabled">
+<!ENTITY SelectDisabled.accesskey "D">
+<!ENTITY SelectTabIndex.label "Tab Index:">
+<!ENTITY SelectTabIndex.accesskey "I">
+
+<!ENTITY OptGroup.label "Option Group">
+<!ENTITY OptGroupLabel.label "Label:">
+<!ENTITY OptGroupLabel.accesskey "L">
+<!ENTITY OptGroupDisabled.label "Disabled">
+<!ENTITY OptGroupDisabled.accesskey "D">
+
+<!ENTITY Option.label "Option">
+<!ENTITY OptionText.label "Text:">
+<!ENTITY OptionText.accesskey "T">
+<!ENTITY OptionValue.label "Value:">
+<!ENTITY OptionValue.accesskey "V">
+<!ENTITY OptionSelected.label "Initially Selected">
+<!ENTITY OptionSelected.accesskey "S">
+<!ENTITY OptionDisabled.label "Disabled">
+<!ENTITY OptionDisabled.accesskey "D">
+
+<!ENTITY TextHeader.label "Text">
+<!ENTITY ValueHeader.label "Value">
+<!ENTITY SelectedHeader.label "Selected">
+
+<!ENTITY AddOption.label "Add Option">
+<!ENTITY AddOption.accesskey "O">
+<!ENTITY AddOptGroup.label "Add Group">
+<!ENTITY AddOptGroup.accesskey "G">
+<!ENTITY RemoveElement.label "Remove">
+<!ENTITY RemoveElement.accesskey "R">
+<!ENTITY MoveElementUp.label "Move Up">
+<!ENTITY MoveElementUp.accesskey "U">
+<!ENTITY MoveElementDown.label "Move Down">
+<!ENTITY MoveElementDown.accesskey "D">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSnapToGrid.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSnapToGrid.dtd
new file mode 100644
index 0000000000..cb37918eb5
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSnapToGrid.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Snap to Grid">
+
+<!ENTITY enableSnapToGrid.label "enable Snap to Grid">
+<!ENTITY enableSnapToGrid.accessKey "e">
+
+<!ENTITY sizeEditField.label "Size:">
+<!ENTITY sizeEditField.accessKey "S">
+
+<!ENTITY pixelsLabel.value "pixels">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSpellCheck.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSpellCheck.dtd
new file mode 100644
index 0000000000..7d29154831
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorSpellCheck.dtd
@@ -0,0 +1,38 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!ENTITY windowTitle.label "Check Spelling">
+
+<!ENTITY misspelledWord.label "Misspelled word:">
+<!ENTITY wordEditField.label "Replace with:">
+<!ENTITY wordEditField.accessKey "w">
+<!ENTITY checkwordButton.label "Check Word">
+<!ENTITY checkwordButton.accessKey "k">
+<!ENTITY suggestions.label "Suggestions:">
+<!ENTITY suggestions.accessKey "u">
+<!ENTITY ignoreButton.label "Ignore">
+<!ENTITY ignoreButton.accessKey "I">
+<!ENTITY ignoreAllButton.label "Ignore All">
+<!ENTITY ignoreAllButton.accessKey "n">
+<!ENTITY replaceButton.label "Replace">
+<!ENTITY replaceButton.accessKey "R">
+<!ENTITY replaceAllButton.label "Replace All">
+<!ENTITY replaceAllButton.accessKey "A">
+<!ENTITY stopButton.label "Stop">
+<!ENTITY stopButton.accessKey "o">
+<!ENTITY userDictionary.label "Personal Dictionary:">
+<!ENTITY moreDictionaries.label "Download more dictionaries…">
+<!ENTITY addToUserDictionaryButton.label "Add Word">
+<!ENTITY addToUserDictionaryButton.accessKey "d">
+<!ENTITY editUserDictionaryButton.label "Edit…">
+<!ENTITY editUserDictionaryButton.accessKey "E">
+<!ENTITY recheckButton2.label "Recheck Text">
+<!ENTITY recheckButton2.accessKey "T">
+<!ENTITY closeButton.label "Close">
+<!ENTITY closeButton.accessKey "C">
+<!ENTITY sendButton.label "Send">
+<!ENTITY sendButton.accessKey "S">
+<!ENTITY languagePopup.label "Language:">
+<!ENTITY languagePopup.accessKey "L">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorTableProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorTableProperties.dtd
new file mode 100644
index 0000000000..512734d7a0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorTableProperties.dtd
@@ -0,0 +1,75 @@
+<!-- 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/. -->
+
+<!ENTITY tableWindow.title "Table Properties">
+<!ENTITY applyButton.label "Apply">
+<!ENTITY applyButton.accesskey "A">
+<!ENTITY closeButton.label "Close">
+<!ENTITY tableTab.label "Table">
+<!ENTITY cellTab.label "Cells">
+<!ENTITY tableRows.label "Rows:">
+<!ENTITY tableRows.accessKey "R">
+<!ENTITY tableColumns.label "Columns:">
+<!ENTITY tableColumns.accessKey "C">
+<!ENTITY tableHeight.label "Height:">
+<!ENTITY tableHeight.accessKey "G">
+<!ENTITY tableWidth.label "Width:">
+<!ENTITY tableWidth.accessKey "W">
+<!ENTITY tableBorderSpacing.label "Borders and Spacing">
+<!ENTITY tableBorderWidth.label "Border:">
+<!ENTITY tableBorderWidth.accessKey "B">
+<!ENTITY tableSpacing.label "Spacing:">
+<!ENTITY tableSpacing.accessKey "S">
+<!ENTITY tablePadding.label "Padding:">
+<!ENTITY tablePadding.accessKey "P">
+<!ENTITY tablePxBetwCells.label "pixels between cells">
+<!ENTITY tablePxBetwBrdrCellContent.label "pixels between cell border and content">
+<!ENTITY tableAlignment.label "Table Alignment:">
+<!ENTITY tableAlignment.accessKey "T">
+<!ENTITY tableCaption.label "Caption:">
+<!ENTITY tableCaption.accessKey "N">
+<!ENTITY tableCaptionAbove.label "Above Table">
+<!ENTITY tableCaptionBelow.label "Below Table">
+<!ENTITY tableCaptionLeft.label "Left of Table">
+<!ENTITY tableCaptionRight.label "Right of table">
+<!ENTITY tableCaptionNone.label "None">
+<!ENTITY tableInheritColor.label "(Let page color show through)">
+
+<!ENTITY cellSelection.label "Selection">
+<!ENTITY cellSelectCell.label "Cell">
+<!ENTITY cellSelectRow.label "Row">
+<!ENTITY cellSelectColumn.label "Column">
+<!ENTITY cellSelectNext.label "Next">
+<!ENTITY cellSelectNext.accessKey "N">
+<!ENTITY cellSelectPrevious.label "Previous">
+<!ENTITY cellSelectPrevious.accessKey "P">
+<!ENTITY applyBeforeChange.label "Current changes will be applied before changing the selection.">
+<!ENTITY cellContentAlignment.label "Content Alignment">
+<!ENTITY cellHorizontal.label "Horizontal:">
+<!ENTITY cellHorizontal.accessKey "Z">
+<!ENTITY cellVertical.label "Vertical:">
+<!ENTITY cellVertical.accessKey "V">
+<!ENTITY cellStyle.label "Cell Style:">
+<!ENTITY cellStyle.accessKey "C">
+<!ENTITY cellNormal.label "Normal">
+<!ENTITY cellHeader.label "Header">
+<!ENTITY cellTextWrap.label "Text Wrap:">
+<!ENTITY cellTextWrap.accessKey "T">
+<!ENTITY cellWrap.label "Wrap">
+<!ENTITY cellNoWrap.label "Don't wrap">
+<!ENTITY cellAlignTop.label "Top">
+<!ENTITY cellAlignMiddle.label "Middle">
+<!ENTITY cellAlignBottom.label "Bottom">
+<!ENTITY cellAlignJustify.label "Justify">
+<!ENTITY cellInheritColor.label "(Let table color show through)">
+<!ENTITY cellUseCheckboxHelp.label "Use checkboxes to determine which properties are applied to all selected cells">
+
+<!-- Used in both Table and Cell panels -->
+<!ENTITY size.label "Size">
+<!ENTITY pixels.label "pixels">
+<!ENTITY backgroundColor.label "Background Color:">
+<!ENTITY backgroundColor.accessKey "B">
+<!ENTITY AlignLeft.label "Left">
+<!ENTITY AlignCenter.label "Center">
+<!ENTITY AlignRight.label "Right">
diff --git a/comm/suite/locales/en-US/chrome/editor/dialogs/EditorTextAreaProperties.dtd b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorTextAreaProperties.dtd
new file mode 100644
index 0000000000..a1e8bbd035
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/dialogs/EditorTextAreaProperties.dtd
@@ -0,0 +1,33 @@
+<!-- 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/. -->
+
+<!ENTITY windowTitle.label "Text Area Properties">
+
+<!ENTITY Settings.label "Settings">
+
+<!ENTITY TextAreaName.label "Field Name:">
+<!ENTITY TextAreaName.accessKey "N">
+<!ENTITY TextAreaRows.label "Rows:">
+<!ENTITY TextAreaRows.accessKey "R">
+<!ENTITY TextAreaCols.label "Columns:">
+<!ENTITY TextAreaCols.accessKey "C">
+<!ENTITY TextAreaReadOnly.label "Read Only">
+<!ENTITY TextAreaReadOnly.accessKey "O">
+<!ENTITY TextAreaDisabled.label "Disabled">
+<!ENTITY TextAreaDisabled.accessKey "D">
+<!ENTITY TextAreaTabIndex.label "Tab Index:">
+<!ENTITY TextAreaTabIndex.accessKey "I">
+<!ENTITY TextAreaAccessKey.label "Access Key:">
+<!ENTITY TextAreaAccessKey.accessKey "K">
+<!ENTITY InitialText.label "Initial Text:">
+<!ENTITY InitialText.accessKey "T">
+
+<!ENTITY TextAreaWrap.label "Wrap Mode:">
+<!ENTITY TextAreaWrap.accessKey "W">
+<!ENTITY WrapDefault.value "Default">
+<!ENTITY WrapOff.value "Off">
+<!ENTITY WrapHard.value "Hard">
+<!ENTITY WrapSoft.value "Soft">
+<!ENTITY WrapPhysical.value "Physical">
+<!ENTITY WrapVirtual.value "Virtual">
diff --git a/comm/suite/locales/en-US/chrome/editor/editingOverlay.dtd b/comm/suite/locales/en-US/chrome/editor/editingOverlay.dtd
new file mode 100644
index 0000000000..141d8c5cce
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/editingOverlay.dtd
@@ -0,0 +1,62 @@
+<!-- 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/. -->
+
+<!-- File menu items -->
+<!ENTITY openFileCmd.label "Open File…">
+<!ENTITY openFileCmd.accesskey "O">
+<!ENTITY openFileCmd.key "O">
+<!ENTITY openRemoteCmd.label "Open Web Location…">
+<!ENTITY openRemoteCmd.accesskey "L">
+<!ENTITY openRemoteCmd.key "L">
+<!ENTITY fileRecentMenu.label "Recent Pages">
+<!ENTITY fileRecentMenu.accesskey "R">
+<!ENTITY saveCmd.label "Save">
+<!ENTITY saveCmd.accesskey "S">
+<!ENTITY saveAsCmd.label "Save As…">
+<!ENTITY saveAsCmd.accesskey "A">
+<!ENTITY saveAsChangeEncodingCmd2.label "Save And Change Text Encoding">
+<!ENTITY saveAsChangeEncodingCmd2.accesskey "E">
+<!ENTITY publishCmd.label "Publish">
+<!ENTITY publishCmd.accesskey "b">
+<!ENTITY publishCmd.key "S">
+<!ENTITY publishAsCmd.label "Publish As…">
+<!ENTITY publishAsCmd.accesskey "h">
+<!ENTITY fileRevert.label "Revert">
+<!ENTITY fileRevert.accesskey "t">
+
+<!-- Edit menu items -->
+<!ENTITY publishSettings.label "Publishing Site Settings…">
+<!ENTITY publishSettings.accesskey "b">
+
+<!-- Insert menu items -->
+<!ENTITY insertFormMenu.label "Form">
+<!ENTITY insertFormMenu.accesskey "F">
+<!ENTITY insertFormCmd.label "Define Form…">
+<!ENTITY insertFormCmd.accesskey "D">
+<!ENTITY insertInputTagCmd.label "Form Field…">
+<!ENTITY insertInputTagCmd.accesskey "e">
+<!ENTITY insertInputImageCmd.label "Form Image…">
+<!ENTITY insertInputImageCmd.accesskey "I">
+<!ENTITY insertTextAreaCmd.label "Text Area…">
+<!ENTITY insertTextAreaCmd.accesskey "T">
+<!ENTITY insertSelectCmd.label "Selection List…">
+<!ENTITY insertSelectCmd.accesskey "S">
+<!ENTITY insertButtonCmd.label "Define Button…">
+<!ENTITY insertButtonCmd.accesskey "B">
+<!ENTITY insertLabelCmd.label "Define Label">
+<!ENTITY insertLabelCmd.accesskey "L">
+<!ENTITY insertFieldSetCmd.label "Define Field Set…">
+<!ENTITY insertFieldSetCmd.accesskey "F">
+
+<!-- Toolbar buttons/items -->
+<!ENTITY newToolbarCmd.label "New">
+<!ENTITY newToolbarCmd.tooltip "Create a new Composer page">
+<!ENTITY openToolbarCmd.label "Open">
+<!ENTITY openToolbarCmd.tooltip "Open a local file">
+<!ENTITY saveToolbarCmd.tooltip "Save file to a local location">
+<!ENTITY publishToolbarCmd.tooltip "Upload file to a remote location">
+<!ENTITY printToolbarCmd.label "Print">
+<!ENTITY printToolbarCmd.tooltip "Print this page">
+<!ENTITY formToolbarCmd.label "Form">
+<!ENTITY formToolbarCmd.tooltip "Insert new form or edit selected form's properties">
diff --git a/comm/suite/locales/en-US/chrome/editor/editor.dtd b/comm/suite/locales/en-US/chrome/editor/editor.dtd
new file mode 100644
index 0000000000..c73dc919f2
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/editor.dtd
@@ -0,0 +1,67 @@
+<!-- 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/. -->
+
+<!-- Window title -->
+<!-- LOCALIZATION NOTE (editorWindow.titlemodifier): DONT_TRANSLATE -->
+<!ENTITY editorWindow.titlemodifier "Composer">
+<!ENTITY editorWindow.titlemodifiermenuseparator " - ">
+
+<!-- Menu items: the . means that the menu item isn't implemented yet -->
+
+<!-- Toolbar-only items -->
+<!ENTITY compositionToolbar.tooltip "Composition Toolbar">
+<!ENTITY previewToolbarCmd.label "Browse">
+<!ENTITY previewToolbarCmd.tooltip "Load this page in the browser">
+
+<!-- File menu items -->
+<!ENTITY exportToTextCmd.label "Export to Text…">
+<!ENTITY exportToTextCmd.accesskey "x">
+<!ENTITY previewCmd.label "Browse Page">
+<!ENTITY previewCmd.accesskey "w">
+<!ENTITY sendPageCmd.label "Send Page…">
+<!ENTITY sendPageCmd.accesskey "d">
+
+<!-- View menu items -->
+<!ENTITY compositionToolbarCmd.label "Composition Toolbar">
+<!ENTITY compositionToolbarCmd.accesskey "C">
+<!ENTITY formattingToolbarCmd.label "Format Toolbar">
+<!ENTITY formattingToolbarCmd.accesskey "F">
+<!ENTITY editmodeToolbarCmd.label "Edit Mode Toolbar">
+<!ENTITY editmodeToolbarCmd.accesskey "E">
+
+<!-- Format menu items -->
+<!ENTITY formatMenu.label "Format">
+<!ENTITY formatMenu.accesskey "o">
+<!ENTITY grid.label "Positioning grid">
+<!ENTITY grid.accesskey "t">
+<!ENTITY pageProperties.label "Page Title and Properties…">
+<!ENTITY pageProperties.accesskey "g">
+
+<!-- Tools menu items -->
+<!ENTITY validateCmd.label "Validate HTML">
+<!ENTITY validateCmd.accesskey "V">
+
+<!-- Display Mode toolbar and View menu items -->
+<!ENTITY NormalModeTab.label "Normal">
+<!ENTITY NormalMode.label "Normal Edit Mode">
+<!ENTITY NormalMode.accesskey "N">
+<!ENTITY NormalMode.tooltip "Show table borders and named anchors">
+<!ENTITY AllTagsModeTab.label "HTML Tags">
+<!ENTITY AllTagsMode.label "HTML Tags">
+<!ENTITY AllTagsMode.accesskey "a">
+<!ENTITY AllTagsMode.tooltip "Display icons for all HTML tags">
+<!-- LOCALIZATION NOTE: (HTMLSourceModeTab.dir, HTMLSourceModeTab.label)
+ Do NOT translate text for 'HTMLSourceModeTab.dir', use latin "ltr" if
+ you want the <html> image to left of the 'HTMLSourceModeTab.label' text,
+ or use latin "rtl" if you want this image to the right of text. You do
+ not need to include HTML in the label 'HTMLSourceModeTab.label' -->
+<!ENTITY HTMLSourceModeTab.dir "ltr">
+<!ENTITY HTMLSourceModeTab.label "Source">
+<!ENTITY HTMLSourceMode.label "HTML Source">
+<!ENTITY HTMLSourceMode.accesskey "H">
+<!ENTITY HTMLSourceMode.tooltip "Edit HTML source">
+<!ENTITY PreviewModeTab.label "Preview">
+<!ENTITY PreviewMode.label "Preview">
+<!ENTITY PreviewMode.accesskey "P">
+<!ENTITY PreviewMode.tooltip "Display as WYSIWYG (as in the browser)">
diff --git a/comm/suite/locales/en-US/chrome/editor/editor.properties b/comm/suite/locales/en-US/chrome/editor/editor.properties
new file mode 100644
index 0000000000..de9f688ed8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/editor.properties
@@ -0,0 +1,208 @@
+# 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/.
+
+# LOCALIZATION NOTE FILE: embedded "\n" represent HTML breaks (<br>)
+# Don't translate embedded "\n".
+# Don't translate strings like this: %variable%
+# as they will be replaced using JavaScript
+#
+No=No
+Save=Save
+More=More
+Less=Less
+MoreProperties=More Properties
+FewerProperties=Fewer Properties
+PropertiesAccessKey=P
+None=None
+none=none
+OpenHTMLFile=Open HTML File
+OpenTextFile=Open Text File
+SelectImageFile=Select Image File
+SaveDocument=Save Page
+SaveDocumentAs=Save Page As
+SaveTextAs=Save Text As
+EditMode=Edit Mode
+Preview=Preview
+Publish=Publish
+PublishPage=Publish Page
+DontPublish=Don't Publish
+SavePassword=Use Password Manager to save this password
+CorrectSpelling=(correct spelling)
+NoSuggestedWords=(no suggested words)
+NoMisspelledWord=No misspelled words
+CheckSpellingDone=Completed spell checking.
+CheckSpelling=Check Spelling
+InputError=Error
+Alert=Alert
+CantEditFramesetMsg=Composer cannot edit HTML framesets, or pages with inline frames. For framesets, try editing the page for each frame separately. For pages with iframes, save a copy of the page and remove the <iframe> tag.
+CantEditMimeTypeMsg=This type of page can't be edited.
+CantEditDocumentMsg=This page can't be edited for an unknown reason.
+BeforeClosing=before closing
+BeforePreview=before viewing in the browser
+BeforeValidate=before validating the document
+# LOCALIZATION NOTE (SaveFilePrompt, PublishPrompt): Don't translate %title% and %reason% (this is the reason for asking user to close, such as "before closing")
+SaveFilePrompt=Save changes to "%title%" %reason%?
+PublishPrompt=Save changes to "%title%" %reason%?
+SaveFileFailed=Saving file failed!
+
+# Publishing error strings:
+# LOCALIZATION NOTE Don't translate %dir% or %file% in the Publishing error strings:
+FileNotFound=%file% not found.
+SubdirDoesNotExist=The subdirectory "%dir%" doesn't exist on this site or the filename "%file%" is already in use by another subdirectory.
+FilenameIsSubdir=The filename "%file%" is already in use by another subdirectory.
+ServerNotAvailable=The server is not available. Check your connection and try again later.
+Offline=You are currently offline. Click the icon near the lower-right corner of any window to go online.
+DiskFull=There is not enough disk space available to save the file "%file%."
+NameTooLong=The filename or subdirectory name is too long.
+AccessDenied=You do not have permission to publish to this location.
+UnknownPublishError=Unknown publishing error occurred.
+PublishFailed=Publishing failed.
+PublishCompleted=Publishing completed.
+AllFilesPublished=All files published
+# LOCALIZATION NOTE Don't translate %x% or %total%
+FailedFileMsg=%x% of %total% files failed to publish.
+# End-Publishing error strings
+Prompt=Prompt
+# LOCALIZATION NOTE (PromptFTPUsernamePassword): Don't translate %host%
+PromptFTPUsernamePassword=Enter username and password for FTP server at %host%
+RevertCaption=Revert To Last Saved
+Revert=Revert
+SendPageReason=before sending this page
+Send=Send
+## LOCALIZATION NOTE (PublishProgressCaption, PublishToSite, AbandonChanges): Don't translate %title%
+PublishProgressCaption=Publishing: %title%
+PublishToSite=Publishing to Site: %title%
+AbandonChanges=Abandon unsaved changes to "%title%" and reload page?
+DocumentTitle=Page Title
+NeedDocTitle=Please enter a title for the current page.
+DocTitleHelp=This identifies the page in the window title and bookmarks.
+CancelPublishTitle=Cancel publishing?
+## LOCALIZATION NOTE: "Continue" in this sentence must match the text for
+## the CancelPublishContinue key below
+CancelPublishMessage=Cancelling while publishing is in progress may result in your file(s) being incompletely transferred. Would you like to Continue or Cancel?
+CancelPublishContinue=Continue
+MissingImageError=Please enter or choose an image of type gif, jpg, or png.
+EmptyHREFError=Please choose a location to create a new link.
+LinkText=Link Text
+LinkImage=Link Image
+MixedSelection=[Mixed selection]
+Mixed=(mixed)
+# LOCALIZATION NOTE (NotInstalled): %S is the name of the font
+NotInstalled=%S (not installed)
+EnterLinkText=Enter text to display for the link:
+EnterLinkTextAccessKey=T
+EmptyLinkTextError=Please enter some text for this link.
+EditTextWarning=This will replace existing content.
+#LOCALIZATION NOTE (ValidateNumber):Don't translate: %n% %min% %max%
+ValidateRangeMsg=The number you entered (%n%) is outside of the allowed range.
+ValidateNumberMsg=Please enter a number between %min% and %max%.
+MissingAnchorNameError=Please enter a name for this anchor.
+#LOCALIZATION NOTE (DuplicateAnchorNameError): Don't translate %name%
+DuplicateAnchorNameError="%name%" already exists in this page. Please enter a different name.
+BulletStyle=Bullet Style
+SolidCircle=Solid circle
+OpenCircle=Open circle
+SolidSquare=Solid square
+NumberStyle=Number Style
+Automatic=Automatic
+Style_1=1, 2, 3…
+Style_I=I, II, III…
+Style_i=i, ii, iii…
+Style_A=A, B, C…
+Style_a=a, b, c…
+Pixels=pixels
+Percent=percent
+PercentOfCell=% of cell
+PercentOfWindow=% of window
+PercentOfTable=% of table
+#LOCALIZATION NOTE (untitledTitle): %S is the window #. No plural handling needed.
+untitledTitle=untitled-%S
+untitledDefaultFilename=untitled
+ShowToolbar=Show Toolbar
+HideToolbar=Hide Toolbar
+ImapError=Unable to load image
+ImapCheck=\nPlease select a new location (URL) and try again.
+SaveToUseRelativeUrl=Relative URLs can only be used on pages which have been saved
+NoNamedAnchorsOrHeadings=(No named anchors or headings in this page)
+TextColor=Text Color
+HighlightColor=Highlight Color
+PageColor=Page Background Color
+BlockColor=Block Background Color
+TableColor=Table Background Color
+CellColor=Cell Background Color
+TableOrCellColor=Table or Cell Color
+LinkColor=Link Text Color
+ActiveLinkColor=Active Link Color
+VisitedLinkColor=Visited Link Color
+NoColorError=Click on a color or enter a valid HTML color string
+Table=Table
+TableCell=Table Cell
+NestedTable=Nested Table
+HLine=Horizontal Line
+Link=Link
+Image=Image
+ImageAndLink=Image and Link
+NamedAnchor=Named Anchor
+List=List
+ListItem=List Item
+Form=Form
+InputTag=Form Field
+InputImage=Form Image
+TextArea=Text Area
+Select=Selection List
+Button=Button
+Label=Label
+FieldSet=Field Set
+Tag=Tag
+MissingSiteNameError=Please enter a name for this publishing site.
+MissingPublishUrlError=Please enter a location for publishing this page.
+MissingPublishFilename=Please enter a filename for the current page.
+#LOCALIZATION NOTE (DuplicateSiteNameError): Don't translate %name%
+DuplicateSiteNameError="%name%" already exists. Please enter a different site name.
+AdvancedProperties=Advanced Properties…
+AdvancedEditForCellMsg=Advanced Edit is unavailable when multiple cells are selected
+# LOCALIZATION NOTE (ObjectProperties):Don't translate "%obj%" it will be replaced with one of above object nouns
+ObjectProperties=%obj% Properties…
+# LOCALIZATION NOTE This character must be in the above string and not conflict with other accesskeys in Format menu
+ObjectPropertiesAccessKey=o
+# LOCALIZATION NOTE (JoinSelectedCells): This variable should contain the "tableJoinCells.accesskey"
+# letter as defined in editorOverlay.dtd
+JoinSelectedCells=Join Selected Cells
+# LOCALIZATION NOTE (JoinCellToRight): This variable should contain the "tableJoinCells.accesskey"
+# letter as defined in editorOverlay.dtd
+JoinCellToRight=Join with Cell to the Right
+JoinCellAccesskey=j
+# LOCALIZATION NOTE (TableSelectKey): Ctrl key on a keyboard
+TableSelectKey=Ctrl+
+# LOCALIZATION NOTE (XulKeyMac): Command key on a Mac keyboard
+XulKeyMac=Cmd+
+# LOCALIZATION NOTE (Del): Del key on a keyboard
+Del=Del
+Delete=Delete
+DeleteCells=Delete Cells
+DeleteTableTitle=Delete Rows or Columns
+DeleteTableMsg=Reducing the number of rows or columns will delete table cells and their contents. Do you really want to do this?
+Clear=Clear
+#Mouse actions
+Click=Click
+Drag=Drag
+Unknown=Unknown
+#
+# LOCALIZATION NOTE "RemoveTextStylesAccesskey" is used for both
+# menu items: "RemoveTextStyles" and "StopTextStyles"
+RemoveTextStylesAccesskey=x
+RemoveTextStyles=Remove All Text Styles
+StopTextStyles=Discontinue Text Styles
+#
+# LOCALIZATION NOTE "RemoveLinksAccesskey" is used for both
+# menu items: "RemoveLinks" and "StopLinks"
+RemoveLinksAccesskey=n
+RemoveLinks=Remove Links
+StopLinks=Discontinue Link
+#
+NoFormAction=It is recommended that you enter an action for this form. Self-posting forms are an advanced technique that may not work consistently in all browsers.
+NoAltText=If the image is relevant to the content of the document, you must supply alternate text that will appear in text-only browsers, and that will appear in other browsers when an image is loading or when image loading is disabled.
+#
+Malformed=The source could not be converted back into the document because it is not valid XHTML.
+NoLinksToCheck=There are no elements with links to check
diff --git a/comm/suite/locales/en-US/chrome/editor/editorOverlay.dtd b/comm/suite/locales/en-US/chrome/editor/editorOverlay.dtd
new file mode 100644
index 0000000000..41b1768602
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/editorOverlay.dtd
@@ -0,0 +1,368 @@
+<!-- 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/. -->
+
+<!-- Attn: Localization - some of the menus in this dialog directly affect mail also. -->
+
+<!-- File menu items -->
+<!ENTITY saveCmd.key "S">
+
+<!-- Edit menu items -->
+<!ENTITY pasteNoFormatting.label "Paste Without Formatting">
+<!ENTITY pasteNoFormatting.accesskey "n">
+<!ENTITY pasteNoFormatting.key "V">
+<!ENTITY pasteAsQuotationCmd.label "Paste As Quotation">
+<!ENTITY pasteAsQuotationCmd.accesskey "Q">
+<!ENTITY pasteAsQuotationCmd.key "o">
+<!ENTITY editRewrapCmd.label "Rewrap">
+<!ENTITY editRewrapCmd.key "R">
+<!ENTITY editRewrapCmd.accesskey "w">
+<!ENTITY findBarCmd.label "Find…">
+<!ENTITY findReplaceCmd.label "Find and Replace…">
+<!ENTITY enableInlineSpellChecker.label "Spellcheck As You Type">
+<!ENTITY enableInlineSpellChecker.accesskey "S">
+<!ENTITY checkSpellingCmd2.label "Check Spelling…">
+<!ENTITY checkSpellingCmd2.accesskey "h">
+<!ENTITY checkSpellingCmd2.key "p">
+
+<!-- Insert menu items -->
+<!ENTITY insertMenu.label "Insert">
+<!ENTITY insertMenu.accesskey "I">
+<!ENTITY insertLinkCmd2.label "Link…">
+<!ENTITY insertLinkCmd2.accesskey "L">
+<!ENTITY insertLinkCmd2.key "K">
+<!ENTITY insertAnchorCmd.label "Named Anchor…">
+<!ENTITY insertAnchorCmd.accesskey "A">
+<!ENTITY insertImageCmd.label "Image…">
+<!ENTITY insertImageCmd.accesskey "I">
+<!ENTITY insertHLineCmd.label "Horizontal Line">
+<!ENTITY insertHLineCmd.accesskey "o">
+<!ENTITY insertTableCmd.label "Table…">
+<!ENTITY insertTableCmd.accesskey "T">
+<!ENTITY insertHTMLCmd.label "HTML…">
+<!ENTITY insertHTMLCmd.accesskey "H">
+<!ENTITY insertMathCmd.label "Math…">
+<!ENTITY insertMathCmd.accesskey "M">
+<!ENTITY insertCharsCmd.label "Characters and Symbols…">
+<!ENTITY insertCharsCmd.accesskey "C">
+<!ENTITY insertBreakAllCmd.label "Break Below Image(s)">
+<!ENTITY insertBreakAllCmd.accesskey "k">
+
+<!-- Used just in context popup. -->
+<!ENTITY createLinkCmd.label "Create Link…">
+<!ENTITY createLinkCmd.accesskey "k">
+<!ENTITY editLinkCmd.label "Edit Link in New Composer">
+<!ENTITY editLinkCmd.accesskey "i">
+
+<!-- Font Face SubMenu -->
+<!ENTITY FontFaceSelect.title "Font">
+<!ENTITY FontFaceSelect.tooltip "Choose a font">
+<!ENTITY fontfaceMenu.label "Font">
+<!ENTITY fontfaceMenu.accesskey "F">
+<!ENTITY fontVarWidth.label "Variable Width">
+<!ENTITY fontVarWidth.accesskey "V">
+<!ENTITY fontFixedWidth.label "Fixed Width">
+<!ENTITY fontFixedWidth.accesskey "x">
+<!ENTITY fontFixedWidth.key "T">
+<!ENTITY fontHelvetica.label "Helvetica, Arial">
+<!ENTITY fontHelvetica.accesskey "l">
+<!ENTITY fontTimes.label "Times">
+<!ENTITY fontTimes.accesskey "T">
+<!ENTITY fontCourier.label "Courier">
+<!ENTITY fontCourier.accesskey "C">
+
+<!-- Font Size SubMenu -->
+<!ENTITY FontSizeSelect.title "Font Size">
+<!ENTITY FontSizeSelect.tooltip "Choose a font size">
+<!ENTITY decreaseFontSize.label "Smaller">
+<!ENTITY decreaseFontSize.accesskey "r">
+<!ENTITY decrementFontSize.key "&lt;">
+<!ENTITY decrementFontSize.key2 ","> <!-- < is above this key on many keyboards -->
+<!ENTITY increaseFontSize.label "Larger">
+<!ENTITY increaseFontSize.accesskey "g">
+<!ENTITY incrementFontSize.key "&gt;">
+<!ENTITY incrementFontSize.key2 "."> <!-- > is above this key on many keyboards -->
+
+<!ENTITY fontSizeMenu.label "Size">
+<!ENTITY fontSizeMenu.accesskey "z">
+<!ENTITY size-tinyCmd.label "Tiny">
+<!ENTITY size-tinyCmd.accesskey "T">
+<!ENTITY size-smallCmd.label "Small">
+<!ENTITY size-smallCmd.accesskey "S">
+<!ENTITY size-mediumCmd.label "Medium">
+<!ENTITY size-mediumCmd.accesskey "M">
+<!ENTITY size-largeCmd.label "Large">
+<!ENTITY size-largeCmd.accesskey "L">
+<!ENTITY size-extraLargeCmd.label "Extra Large">
+<!ENTITY size-extraLargeCmd.accesskey "x">
+<!ENTITY size-hugeCmd.label "Huge">
+<!ENTITY size-hugeCmd.accesskey "H">
+
+<!-- Font Style SubMenu -->
+<!ENTITY fontStyleMenu.label "Text Style">
+<!ENTITY fontStyleMenu.accesskey "S">
+<!ENTITY styleBoldCmd.label "Bold">
+<!ENTITY styleBoldCmd.accesskey "B">
+<!ENTITY styleBoldCmd.key "B">
+<!ENTITY styleItalicCmd.label "Italic">
+<!ENTITY styleItalicCmd.accesskey "I">
+<!ENTITY styleItalicCmd.key "I">
+<!ENTITY styleUnderlineCmd.label "Underline">
+<!ENTITY styleUnderlineCmd.accesskey "U">
+<!ENTITY styleUnderlineCmd.key "U">
+<!ENTITY styleStrikeThruCmd.label "Strikethrough">
+<!ENTITY styleStrikeThruCmd.accesskey "k">
+<!ENTITY styleSuperscriptCmd.label "Superscript">
+<!ENTITY styleSuperscriptCmd.accesskey "p">
+<!ENTITY styleSubscriptCmd.label "Subscript">
+<!ENTITY styleSubscriptCmd.accesskey "S">
+<!ENTITY styleNonbreakingCmd.label "Nonbreaking">
+<!ENTITY styleNonbreakingCmd.accesskey "N">
+<!ENTITY styleEm.label "Emphasis">
+<!ENTITY styleEm.accesskey "E">
+<!ENTITY styleStrong.label "Stronger Emphasis">
+<!ENTITY styleStrong.accesskey "t">
+<!ENTITY styleCite.label "Citation">
+<!ENTITY styleCite.accesskey "C">
+<!ENTITY styleAbbr.label "Abbreviation">
+<!ENTITY styleAbbr.accesskey "A">
+<!ENTITY styleAcronym.label "Acronym">
+<!ENTITY styleAcronym.accesskey "r">
+<!ENTITY styleCode.label "Code">
+<!ENTITY styleCode.accesskey "o">
+<!ENTITY styleSamp.label "Sample Output">
+<!ENTITY styleSamp.accesskey "m">
+<!ENTITY styleVar.label "Variable">
+<!ENTITY styleVar.accesskey "V">
+
+<!ENTITY formatFontColor.label "Text Color…">
+<!ENTITY formatFontColor.accesskey "C">
+<!ENTITY tableOrCellColor.label "Table or Cell Background Color…">
+<!ENTITY tableOrCellColor.accesskey "B">
+
+<!ENTITY formatRemoveStyles.key "Y">
+<!ENTITY formatRemoveLinks.key "K">
+<!ENTITY formatRemoveNamedAnchors.label "Remove Named Anchors">
+<!ENTITY formatRemoveNamedAnchors.accesskey "R">
+<!ENTITY formatRemoveNamedAnchors2.key "R">
+
+<!ENTITY paragraphMenu.label "Paragraph">
+<!ENTITY paragraphMenu.accesskey "P">
+<!ENTITY paragraphParagraphCmd.label "Paragraph">
+<!ENTITY paragraphParagraphCmd.accesskey "P">
+<!ENTITY heading1Cmd.label "Heading 1">
+<!ENTITY heading1Cmd.accesskey "1">
+<!ENTITY heading2Cmd.label "Heading 2">
+<!ENTITY heading2Cmd.accesskey "2">
+<!ENTITY heading3Cmd.label "Heading 3">
+<!ENTITY heading3Cmd.accesskey "3">
+<!ENTITY heading4Cmd.label "Heading 4">
+<!ENTITY heading4Cmd.accesskey "4">
+<!ENTITY heading5Cmd.label "Heading 5">
+<!ENTITY heading5Cmd.accesskey "5">
+<!ENTITY heading6Cmd.label "Heading 6">
+<!ENTITY heading6Cmd.accesskey "6">
+<!ENTITY paragraphAddressCmd.label "Address">
+<!ENTITY paragraphAddressCmd.accesskey "A">
+<!ENTITY paragraphPreformatCmd.label "Preformat">
+<!ENTITY paragraphPreformatCmd.accesskey "f">
+
+<!-- List menu items -->
+<!ENTITY formatlistMenu.label "List">
+<!ENTITY formatlistMenu.accesskey "L">
+<!ENTITY noneCmd.label "None">
+<!ENTITY noneCmd.accesskey "N">
+<!ENTITY listBulletCmd.label "Bulleted">
+<!ENTITY listBulletCmd.accesskey "B">
+<!ENTITY listNumberedCmd.label "Numbered">
+<!ENTITY listNumberedCmd.accesskey "m">
+<!ENTITY listTermCmd.label "Term">
+<!ENTITY listTermCmd.accesskey "T">
+<!ENTITY listDefinitionCmd.label "Definition">
+<!ENTITY listDefinitionCmd.accesskey "D">
+<!ENTITY listPropsCmd.label "List Properties…">
+<!ENTITY listPropsCmd.accesskey "L">
+
+<!ENTITY ParagraphSelect.title "Paragraph">
+<!ENTITY ParagraphSelect.tooltip "Choose a paragraph format">
+<!-- Shared in Paragraph, and Toolbar menulist -->
+<!ENTITY bodyTextCmd.label "Body Text">
+<!ENTITY bodyTextCmd.accesskey "T">
+<!-- isn't used in menu now, but may be added in future -->
+<!ENTITY advancedPropertiesCmd.label "Advanced Properties">
+<!ENTITY advancedPropertiesCmd.accesskey "v">
+
+<!-- Align menu items -->
+<!ENTITY alignMenu.label "Align">
+<!ENTITY alignMenu.accesskey "A">
+<!ENTITY alignLeft.label "Left">
+<!ENTITY alignLeft.accesskey "L">
+<!ENTITY alignLeft.tooltip "Align Left">
+<!ENTITY alignCenter.label "Center">
+<!ENTITY alignCenter.accesskey "C">
+<!ENTITY alignCenter.tooltip "Align Center">
+<!ENTITY alignRight.label "Right">
+<!ENTITY alignRight.accesskey "R">
+<!ENTITY alignRight.tooltip "Align Right">
+<!ENTITY alignJustify.label "Justify">
+<!ENTITY alignJustify.accesskey "J">
+<!ENTITY alignJustify.tooltip "Align Justified">
+
+<!-- Layer toolbar items -->
+<!ENTITY absolutePosition.label "Positioning">
+<!ENTITY layer.tooltip "Layer">
+<!ENTITY decreaseZIndex.label "Send to Back">
+<!ENTITY layerSendToBack.tooltip "Send to Back">
+<!ENTITY increaseZIndex.label "Bring to Front">
+<!ENTITY layerBringToFront.tooltip "Bring to Front">
+
+<!ENTITY increaseIndent.label "Increase Indent">
+<!ENTITY increaseIndent.accesskey "I">
+<!ENTITY increaseIndent.key "]">
+<!ENTITY decreaseIndent.label "Decrease Indent">
+<!ENTITY decreaseIndent.accesskey "D">
+<!ENTITY decreaseIndent.key "[">
+
+<!ENTITY colorsAndBackground.label "Page Colors and Background…">
+<!ENTITY colorsAndBackground.accesskey "u">
+
+<!-- Table Menu -->
+<!ENTITY tableMenu.label "Table">
+<!ENTITY tableMenu.accesskey "b">
+
+<!-- Select Submenu -->
+<!ENTITY tableSelectMenu.label "Select">
+<!ENTITY tableSelectMenu.accesskey "S">
+
+<!ENTITY tableSelectMenu2.label "Table Select">
+<!ENTITY tableSelectMenu2.accesskey "S">
+<!ENTITY tableInsertMenu2.label "Table Insert">
+<!ENTITY tableInsertMenu2.accesskey "I">
+<!ENTITY tableDeleteMenu2.label "Table Delete">
+<!ENTITY tableDeleteMenu2.accesskey "D">
+
+<!-- Insert SubMenu -->
+<!ENTITY tableInsertMenu.label "Insert">
+<!ENTITY tableInsertMenu.accesskey "I">
+<!ENTITY tableTable.label "Table">
+<!ENTITY tableTable.accesskey "T">
+<!ENTITY tableRow.label "Row">
+<!ENTITY tableRows.label "Row(s)">
+<!ENTITY tableRow.accesskey "R">
+<!ENTITY tableRowAbove.label "Row Above">
+<!ENTITY tableRowAbove.accesskey "R">
+<!ENTITY tableRowBelow.label "Row Below">
+<!ENTITY tableRowBelow.accesskey "B">
+<!ENTITY tableColumn.label "Column">
+<!ENTITY tableColumns.label "Column(s)">
+<!ENTITY tableColumn.accesskey "o">
+<!ENTITY tableColumnBefore.label "Column Before">
+<!ENTITY tableColumnBefore.accesskey "o">
+<!ENTITY tableColumnAfter.label "Column After">
+<!ENTITY tableColumnAfter.accesskey "A">
+<!ENTITY tableCell.label "Cell">
+<!ENTITY tableCells.label "Cell(s)">
+<!ENTITY tableCell.accesskey "C">
+<!ENTITY tableCellContents.label "Cell Contents">
+<!ENTITY tableCellContents.accesskey "n">
+<!ENTITY tableAllCells.label "All Cells">
+<!ENTITY tableAllCells.accesskey "A">
+<!ENTITY tableCellBefore.label "Cell Before">
+<!ENTITY tableCellBefore.accesskey "C">
+<!ENTITY tableCellAfter.label "Cell After">
+<!ENTITY tableCellAfter.accesskey "f">
+<!-- Delete SubMenu -->
+<!ENTITY tableDeleteMenu.label "Delete">
+<!ENTITY tableDeleteMenu.accesskey "D">
+
+<!-- text for "Join Cells" is in editor.properties
+ ("JoinSelectedCells" and "JoinCellToRight")
+ the access key must exist in both of those strings
+ But value must be set here for accesskey to draw properly
+-->
+<!ENTITY tableJoinCells.label "j">
+<!ENTITY tableJoinCells.accesskey "j">
+<!ENTITY tableSplitCell.label "Split Cell">
+<!ENTITY tableSplitCell.accesskey "C">
+<!ENTITY convertToTable.label "Create Table from Selection">
+<!ENTITY convertToTable.accesskey "r">
+<!ENTITY tableProperties.label "Table Properties…">
+<!ENTITY tableProperties.accesskey "o">
+
+<!-- Toolbar-only items -->
+<!ENTITY menuBar.tooltip "Menu Bar">
+<!ENTITY formatToolbar.tooltip "Formatting Toolbar">
+<!ENTITY cutToolbarCmd.tooltip "Cut">
+<!ENTITY copyToolbarCmd.tooltip "Copy">
+<!ENTITY pasteToolbarCmd.tooltip "Paste">
+<!ENTITY findToolbarCmd.label "Find">
+<!ENTITY findToolbarCmd.tooltip "Find text in page">
+<!ENTITY spellToolbarCmd.label "Spell">
+<!ENTITY spellToolbarCmd.tooltip "Check spelling of selection or entire page">
+<!ENTITY imageToolbarCmd.label "Image">
+<!ENTITY imageToolbarCmd.tooltip "Insert new image or edit selected image's properties">
+<!ENTITY hruleToolbarCmd.label "H.Line">
+<!ENTITY hruleToolbarCmd.tooltip "Insert horizontal line or edit selected line's properties">
+<!ENTITY tableToolbarCmd.label "Table">
+<!ENTITY tableToolbarCmd.tooltip "Insert new table or edit selected table's properties">
+<!ENTITY linkToolbarCmd.label "Link">
+<!ENTITY linkToolbarCmd.tooltip "Insert new link or edit selected link's properties">
+<!ENTITY anchorToolbarCmd.label "Anchor">
+<!ENTITY anchorToolbarCmd.tooltip "Insert new named anchor or edit selected anchor's properties">
+<!ENTITY colorButtons.title "Colors">
+<!ENTITY TextColorButton.tooltip "Choose color for text">
+<!ENTITY BackgroundColorButton.tooltip "Choose color for background">
+<!ENTITY HighlightColorButton.label "Highlighter">
+<!ENTITY HighlightColorButton.tooltip "Choose highlight color for text">
+
+<!-- Editor toolbar -->
+<!ENTITY absoluteFontSize.label "Font Size">
+<!ENTITY absoluteFontSizeToolbarCmd.tooltip "Set font size">
+<!ENTITY smaller.label "Smaller">
+<!ENTITY decreaseFontSizeToolbarCmd.tooltip "Smaller font size">
+<!ENTITY larger.label "Larger">
+<!ENTITY increaseFontSizeToolbarCmd.tooltip "Larger font size">
+<!ENTITY bold.label "Bold">
+<!ENTITY boldToolbarCmd.tooltip "Bold">
+<!ENTITY italic.label "Italic">
+<!ENTITY italicToolbarCmd.tooltip "Italic">
+<!ENTITY underline.label "Underline">
+<!ENTITY underlineToolbarCmd.tooltip "Underline">
+<!ENTITY bullets.label "Bullets">
+<!ENTITY bulletListToolbarCmd.tooltip "Apply or remove bulleted list">
+<!ENTITY numbers.label "Numbers">
+<!ENTITY numberListToolbarCmd.tooltip "Apply or remove numbered list">
+<!ENTITY outdent.label "Outdent">
+<!ENTITY outdentToolbarCmd.tooltip "Outdent text (move left)">
+<!ENTITY indent.label "Indent">
+<!ENTITY indentToolbarCmd.tooltip "Indent text (move right)">
+<!ENTITY AlignPopupButton.label "Alignment">
+<!ENTITY AlignPopupButton.tooltip "Choose text alignment">
+<!ENTITY InsertPopupButton.label "Insert">
+<!ENTITY InsertPopupButton.tooltip "Insert a Link, Anchor, Image, Horizontal Line, or Table">
+<!ENTITY alignLeftButton.label "Align Left">
+<!ENTITY alignLeftButton.tooltip "Align text along left margin">
+<!ENTITY alignCenterButton.label "Align Center">
+<!ENTITY alignCenterButton.tooltip "Align text centered">
+<!ENTITY alignRightButton.label "Align Right">
+<!ENTITY alignRightButton.tooltip "Align text along right margin">
+<!ENTITY alignJustifyButton.label "Align Justify">
+<!ENTITY alignJustifyButton.tooltip "Align text along left and right margins">
+
+<!-- Structure Toolbar Context Menu items -->
+<!ENTITY structSelect.label "Select">
+<!ENTITY structSelect.accesskey "s">
+<!ENTITY structRemoveTag.label "Remove tag">
+<!ENTITY structRemoveTag.accesskey "r">
+<!ENTITY structChangeTag.label "Change tag">
+<!ENTITY structChangeTag.accesskey "c">
+
+<!-- TOC manipulation -->
+<!ENTITY insertTOC.label "Insert">
+<!ENTITY insertTOC.accesskey "i">
+<!ENTITY updateTOC.label "Update">
+<!ENTITY updateTOC.accesskey "u">
+<!ENTITY removeTOC.label "Remove">
+<!ENTITY removeTOC.accesskey "r">
+<!ENTITY tocMenu.label "Table of Contents…">
+<!ENTITY tocMenu.accesskey "b">
diff --git a/comm/suite/locales/en-US/chrome/editor/editorSmileyOverlay.dtd b/comm/suite/locales/en-US/chrome/editor/editorSmileyOverlay.dtd
new file mode 100644
index 0000000000..a0f0b5847e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/editorSmileyOverlay.dtd
@@ -0,0 +1,58 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- Smiley Menu items -->
+<!ENTITY insertSmiley.label "Smiley">
+<!ENTITY insertSmiley.accesskey "S">
+
+<!ENTITY smiley1Cmd.label "Smile">
+<!ENTITY smiley1Cmd.accesskey "S">
+<!ENTITY smiley1Cmd.tooltip "Insert a smiley face">
+<!ENTITY smiley2Cmd.label "Frown">
+<!ENTITY smiley2Cmd.accesskey "F">
+<!ENTITY smiley2Cmd.tooltip "Insert a frown face">
+<!ENTITY smiley3Cmd.label "Wink">
+<!ENTITY smiley3Cmd.accesskey "W">
+<!ENTITY smiley3Cmd.tooltip "Insert a wink face">
+<!ENTITY smiley4Cmd.label "Tongue-out">
+<!ENTITY smiley4Cmd.accesskey "T">
+<!ENTITY smiley4Cmd.tooltip "Insert a tongue-out face">
+<!ENTITY smiley5Cmd.label "Laughing">
+<!ENTITY smiley5Cmd.accesskey "L">
+<!ENTITY smiley5Cmd.tooltip "Insert a laughing face">
+<!ENTITY smiley6Cmd.label "Embarrassed">
+<!ENTITY smiley6Cmd.accesskey "E">
+<!ENTITY smiley6Cmd.tooltip "Insert an embarrassed face">
+<!ENTITY smiley7Cmd.label "Undecided">
+<!ENTITY smiley7Cmd.accesskey "U">
+<!ENTITY smiley7Cmd.tooltip "Insert an undecided face">
+<!ENTITY smiley8Cmd.label "Surprise">
+<!ENTITY smiley8Cmd.accesskey "p">
+<!ENTITY smiley8Cmd.tooltip "Insert a surprised face">
+<!ENTITY smiley9Cmd.label "Kiss">
+<!ENTITY smiley9Cmd.accesskey "K">
+<!ENTITY smiley9Cmd.tooltip "Insert a kiss face">
+<!ENTITY smiley10Cmd.label "Yell">
+<!ENTITY smiley10Cmd.accesskey "Y">
+<!ENTITY smiley10Cmd.tooltip "Insert a yelling face">
+<!ENTITY smiley11Cmd.label "Cool">
+<!ENTITY smiley11Cmd.accesskey "C">
+<!ENTITY smiley11Cmd.tooltip "Insert a cool face">
+<!ENTITY smiley12Cmd.label "Money-Mouth">
+<!ENTITY smiley12Cmd.accesskey "M">
+<!ENTITY smiley12Cmd.tooltip "Insert a money-mouth face">
+<!ENTITY smiley13Cmd.label "Foot-in-Mouth">
+<!ENTITY smiley13Cmd.accesskey "o">
+<!ENTITY smiley13Cmd.tooltip "Insert a foot-in-mouth face">
+<!ENTITY smiley14Cmd.label "Innocent">
+<!ENTITY smiley14Cmd.accesskey "I">
+<!ENTITY smiley14Cmd.tooltip "Insert an innocent face">
+<!ENTITY smiley15Cmd.label "Cry">
+<!ENTITY smiley15Cmd.accesskey "r">
+<!ENTITY smiley15Cmd.tooltip "Insert a crying face">
+<!ENTITY smiley16Cmd.label "Lips-are-Sealed">
+<!ENTITY smiley16Cmd.accesskey "a">
+<!ENTITY smiley16Cmd.tooltip "Insert a lips-are-sealed face">
+<!ENTITY SmileButton.label "Insert Smiley">
+<!ENTITY SmileButton.tooltip "Insert a smiley face">
diff --git a/comm/suite/locales/en-US/chrome/editor/prefs/editorPrefsOverlay.dtd b/comm/suite/locales/en-US/chrome/editor/prefs/editorPrefsOverlay.dtd
new file mode 100644
index 0000000000..1e9775cee8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/prefs/editorPrefsOverlay.dtd
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+
+
+<!--LOCALIZATION NOTE (editorCheck.label): DONT_TRANSLATE -->
+<!ENTITY editorCheck.label "Composer">
+<!ENTITY editorCheck.accesskey "C">
+
+<!ENTITY compose.label "Composer">
+<!ENTITY editing.label "New Page Settings">
+<!ENTITY publish.label "Publishing">
diff --git a/comm/suite/locales/en-US/chrome/editor/prefs/pref-composer.dtd b/comm/suite/locales/en-US/chrome/editor/prefs/pref-composer.dtd
new file mode 100644
index 0000000000..d60456dca4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/prefs/pref-composer.dtd
@@ -0,0 +1,29 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-composer.xhtml -->
+
+<!--LOCALIZATION NOTE : FILE 'Composer' prefs dialog. Similar to Communcator 4.x Document Properties/Colors and Background -->
+
+<!--LOCALIZATION NOTE (pref.composer.title): DONT_TRANSLATE -->
+<!ENTITY pref.composer.title "Composer">
+<!ENTITY recentFiles.title "Recent Pages Menu">
+<!ENTITY documentsInMenu.label "Maximum number of pages listed:">
+<!ENTITY documentsInMenu.accesskey "n">
+<!ENTITY savingFiles.title "When Saving or Publishing Pages">
+<!ENTITY preserveExisting.label "Preserve original source formatting">
+<!ENTITY preserveExisting.accesskey "P">
+<!ENTITY preserveExisting.tooltip "Preserves line breaks and page's original formatting">
+<!ENTITY saveAssociatedFiles.label "Save images and other associated files when saving pages">
+<!ENTITY saveAssociatedFiles.accesskey "S">
+<!ENTITY showPublishDialog.label "Always show Publish dialog when publishing pages">
+<!ENTITY showPublishDialog.accesskey "A">
+<!ENTITY composerEditing.label "Editing">
+<!ENTITY maintainStructure.label "Maintain table layout when inserting or deleting cells">
+<!ENTITY maintainStructure.tooltip "Preserves table's rectangular shape by automatically adding cells after inserting or deleting cells">
+<!ENTITY maintainStructure.accesskey "M">
+<!ENTITY useCSS.label "Use CSS styles instead of HTML elements and attributes">
+<!ENTITY useCSS.accesskey "U">
+<!ENTITY crInPCreatesNewP.label "Return in a paragraph always creates a new paragraph">
+<!ENTITY crInPCreatesNewP.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/editor/prefs/pref-editing.dtd b/comm/suite/locales/en-US/chrome/editor/prefs/pref-editing.dtd
new file mode 100644
index 0000000000..5ff0bcb927
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/editor/prefs/pref-editing.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!ENTITY pref.editing.title "New Page Settings">
+
+<!ENTITY authorName.label "Author:">
+<!ENTITY authorName.accesskey "u">
+<!ENTITY pageColorHeader "Default Page Appearance">
+
+<!ENTITY defaultColors.label "Reader's default colors (Don't set colors in page)">
+<!ENTITY defaultColors.accesskey "d">
+<!ENTITY customColors.label "Use custom colors:">
+<!ENTITY customColors.accesskey "c">
+
+<!ENTITY normalText.label "Normal text">
+<!ENTITY normalText.accesskey "N">
+<!ENTITY linkText.label "Link text">
+<!ENTITY linkText.accesskey "L">
+<!ENTITY activeLinkText.label "Active link text">
+<!ENTITY activeLinkText.accesskey "A">
+<!ENTITY visitedLinkText.label "Visited link text">
+<!ENTITY visitedLinkText.accesskey "V">
+<!ENTITY background.label "Background:">
+<!ENTITY background.accesskey "B">
+<!ENTITY colon.character ":">
+
+<!ENTITY backgroundImage.label "Background image:">
+<!ENTITY backgroundImage.accesskey "m">
+<!ENTITY chooseFile.label "Choose File…">
+<!ENTITY chooseFile.accesskey "o">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/CustomHeaders.dtd b/comm/suite/locales/en-US/chrome/mailnews/CustomHeaders.dtd
new file mode 100644
index 0000000000..0f0858c0ae
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/CustomHeaders.dtd
@@ -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/. -->
+
+<!ENTITY window.title "Customize Headers">
+<!ENTITY addButton.label "Add">
+<!ENTITY addButton.accesskey "A">
+<!ENTITY removeButton.label "Remove">
+<!ENTITY removeButton.accesskey "R">
+<!ENTITY newMsgHeader.label "New message header:">
+<!ENTITY newMsgHeader.accesskey "N">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/FilterEditor.dtd b/comm/suite/locales/en-US/chrome/mailnews/FilterEditor.dtd
new file mode 100644
index 0000000000..fa672f0180
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/FilterEditor.dtd
@@ -0,0 +1,67 @@
+<!-- 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/. -->
+
+<!ENTITY window.title "Filter Rules">
+<!ENTITY filterName.label "Filter name:">
+<!ENTITY filterName.accesskey "i">
+
+<!ENTITY recentFolders.label "Recent">
+
+<!ENTITY junk.label "Junk">
+<!ENTITY notJunk.label "Not Junk">
+
+<!ENTITY lowestPriorityCmd.label "Lowest">
+<!ENTITY lowPriorityCmd.label "Low">
+<!ENTITY normalPriorityCmd.label "Normal">
+<!ENTITY highPriorityCmd.label "High">
+<!ENTITY highestPriorityCmd.label "Highest">
+
+<!ENTITY contextDesc.label "Apply filter when:">
+<!ENTITY contextIncomingMail.label "Getting New Mail:">
+<!ENTITY contextIncomingMail.accesskey "G">
+<!ENTITY contextManual.label "Manually Run">
+<!ENTITY contextManual.accesskey "R">
+<!ENTITY contextBeforeCls.label "Filter before Junk Classification">
+<!ENTITY contextAfterCls.label "Filter after Junk Classification">
+<!ENTITY contextOutgoing.label "After Sending">
+<!ENTITY contextOutgoing.accesskey "S">
+<!ENTITY contextArchive.label "Archiving">
+<!ENTITY contextArchive.accesskey "A">
+<!ENTITY contextPeriodic.accesskey "e">
+
+<!ENTITY filterActionDesc.label "Perform these actions:">
+<!ENTITY filterActionDesc.accesskey "P">
+
+<!ENTITY filterActionOrderWarning.label "Note: Filter actions will be run in a different order.">
+<!ENTITY filterActionOrder.label "See execution order">
+
+<!-- New Style Filter Rule Actions -->
+<!ENTITY moveMessage.label "Move Message to">
+<!ENTITY copyMessage.label "Copy Message to">
+<!ENTITY forwardTo.label "Forward Message to">
+<!ENTITY replyWithTemplate.label "Reply with Template">
+<!ENTITY markMessageRead.label "Mark As Read">
+<!ENTITY markMessageUnread.label "Mark As Unread">
+<!ENTITY markMessageStarred.label "Mark As Flagged">
+<!ENTITY setPriority.label "Set Priority to">
+<!ENTITY addTag.label "Tag Message">
+<!ENTITY setJunkScore.label "Set Junk Status to">
+<!ENTITY deleteMessage.label "Delete Message">
+<!ENTITY deleteFromPOP.label "Delete From POP Server">
+<!ENTITY fetchFromPOP.label "Fetch From POP Server">
+<!ENTITY ignoreThread.label "Ignore Thread">
+<!ENTITY ignoreSubthread.label "Ignore Subthread">
+<!ENTITY watchThread.label "Watch Thread">
+<!ENTITY stopExecution.label "Stop Filter Execution">
+
+<!ENTITY addAction.tooltip "Add a new action">
+<!ENTITY removeAction.tooltip "Remove this action">
+<!-- LOCALIZATION NOTE
+ The values below are used to control the widths of the filter action widgets.
+ Change the values only when the localized strings in the popup menus
+ are truncated in the widgets.
+ -->
+<!-- Flex Attribute: https://developer.mozilla.org/docs/XUL/Attribute/flex -->
+<!ENTITY filterActionTypeFlexValue "1">
+<!ENTITY filterActionTargetFlexValue "4">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/FilterListDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/FilterListDialog.dtd
new file mode 100644
index 0000000000..d6b884de15
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/FilterListDialog.dtd
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+
+<!ENTITY filterListDialog.title "Message Filters">
+<!ENTITY nameColumn.label "Filter Name">
+<!ENTITY activeColumn.label "Enabled">
+<!ENTITY newButton.label "New…">
+<!ENTITY newButton.accesskey "N">
+<!ENTITY copyButton.label "Copy…">
+<!ENTITY copyButton.accesskey "p">
+<!ENTITY editButton.label "Edit…">
+<!ENTITY editButton.accesskey "E">
+<!ENTITY deleteButton.label "Delete">
+<!ENTITY deleteButton.accesskey "t">
+<!ENTITY reorderTopButton "Move to Top">
+<!ENTITY reorderTopButton.accessKey "o">
+<!ENTITY reorderTopButton.toolTip "Rearrange filter so it executes before all others">
+<!ENTITY reorderUpButton.label "Move Up">
+<!ENTITY reorderUpButton.accesskey "U">
+<!ENTITY reorderDownButton.label "Move Down">
+<!ENTITY reorderDownButton.accesskey "D">
+<!ENTITY reorderBottomButton "Move to Bottom">
+<!ENTITY reorderBottomButton.accessKey "B">
+<!ENTITY reorderBottomButton.toolTip "Rearrange filter so it executes after all others">
+<!ENTITY filterHeader.label "Enabled filters are run automatically in the order shown below.">
+<!ENTITY filtersForPrefix.label "Filters for:">
+<!ENTITY filtersForPrefix.accesskey "F">
+<!ENTITY viewLogButton.label "Filter Log">
+<!ENTITY viewLogButton.accesskey "L">
+<!ENTITY runFilters.label "Run Now">
+<!ENTITY runFilters.accesskey "R">
+<!ENTITY stopFilters.label "Stop">
+<!ENTITY stopFilters.accesskey "S">
+<!ENTITY folderPickerPrefix.label "Run selected filter(s) on:">
+<!ENTITY folderPickerPrefix.accesskey "c">
+<!ENTITY searchBox.emptyText "Search filters by name…">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/SearchDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/SearchDialog.dtd
new file mode 100644
index 0000000000..23033924dd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/SearchDialog.dtd
@@ -0,0 +1,38 @@
+<!-- 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 SearchDialog.xul -->
+<!ENTITY searchHeading.label "Search for messages in:">
+<!ENTITY searchHeading.accesskey "e">
+<!ENTITY searchSubfolders.label "Search subfolders">
+<!ENTITY searchSubfolders.accesskey "u">
+<!ENTITY searchOnHeading.label "Perform search operations on:">
+<!ENTITY searchOnHeading.accesskey "P">
+<!ENTITY searchOnRemote.label "Remote server">
+<!ENTITY searchOnLocal.label "Local system">
+<!ENTITY resetButton.label "Clear">
+<!ENTITY resetButton.accesskey "C">
+<!ENTITY openButton.label "Open">
+<!ENTITY openButton.accesskey "O">
+<!ENTITY deleteButton.label "Delete">
+<!ENTITY deleteButton.accesskey "D">
+<!ENTITY searchDialogTitle.label "Search Messages">
+<!ENTITY results.label "Results">
+<!ENTITY moveHereMenu.label "Move Here">
+<!ENTITY moveHereMenu.accesskey "H">
+<!ENTITY moveButton.label "Move To">
+<!ENTITY moveButton.accesskey "T">
+<!ENTITY goToFolderButton.label "Open Message Folder">
+<!ENTITY goToFolderButton.accesskey "n">
+<!ENTITY saveAsVFButton.label "Save as Search Folder">
+<!ENTITY saveAsVFButton.accesskey "v">
+
+<!-- for ABSearchDialog.xul -->
+<!ENTITY abSearchHeading.label "Search in:">
+<!ENTITY abSearchHeading.accesskey "e">
+<!ENTITY propertiesButton.label "Properties">
+<!ENTITY propertiesButton.accesskey "P">
+<!ENTITY composeButton.label "Compose">
+<!ENTITY composeButton.accesskey "o">
+<!ENTITY abSearchDialogTitle.label "Advanced Address Book Search">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abAddressBookNameDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abAddressBookNameDialog.dtd
new file mode 100644
index 0000000000..f73630a8f6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abAddressBookNameDialog.dtd
@@ -0,0 +1,5 @@
+<!-- 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/. -->
+<!ENTITY name.label "Address Book Name:">
+<!ENTITY name.accesskey "A">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd
new file mode 100644
index 0000000000..3e08050af6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd
@@ -0,0 +1,152 @@
+<!-- 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/. -->
+
+<!ENTITY Contact.tab "Contact">
+<!ENTITY Contact.accesskey "C">
+<!ENTITY Name.box "Name">
+
+<!-- LOCALIZATION NOTE:
+ NameField1, NameField2, PhoneticField1, PhoneticField2
+ those fields are either LN or FN depends on the target country.
+ "FirstName" and "LastName" can be swapped for id to change the order
+ but they should not be translated (same applied to phonetic id).
+ Make sure the translation of label corresponds to the order of id.
+-->
+
+<!-- LOCALIZATION NOTE (NameField1.id) : DONT_TRANSLATE -->
+<!ENTITY NameField1.id "FirstName">
+<!-- LOCALIZATION NOTE (NameField2.id) : DONT_TRANSLATE -->
+<!ENTITY NameField2.id "LastName">
+<!-- LOCALIZATION NOTE (PhoneticField1.id) : DONT_TRANSLATE -->
+<!ENTITY PhoneticField1.id "PhoneticFirstName">
+<!-- LOCALIZATION NOTE (PhoneticField2.id) : DONT_TRANSLATE -->
+<!ENTITY PhoneticField2.id "PhoneticLastName">
+
+<!ENTITY NameField1.label "First:">
+<!ENTITY NameField1.accesskey "F">
+<!ENTITY NameField2.label "Last:">
+<!ENTITY NameField2.accesskey "L">
+<!ENTITY PhoneticField1.label "Phonetic:">
+<!ENTITY PhoneticField2.label "Phonetic:">
+<!ENTITY DisplayName.label "Display:">
+<!ENTITY DisplayName.accesskey "D">
+<!ENTITY preferDisplayName.label "Always prefer display name over message header">
+<!ENTITY preferDisplayName2.accesskey "y">
+<!ENTITY NickName.label "Nickname:">
+<!ENTITY NickName.accesskey "N">
+
+<!ENTITY PrimaryEmail.label "Email:">
+<!ENTITY PrimaryEmail.accesskey "E">
+<!ENTITY SecondEmail.label "Additional Email:">
+<!ENTITY SecondEmail.accesskey "i">
+<!ENTITY PreferMailFormat.label "Prefers to receive messages formatted as:">
+<!ENTITY PreferMailFormat.accesskey "v">
+<!ENTITY PlainText.label "Plain Text">
+<!ENTITY HTML.label "HTML">
+<!ENTITY Unknown.label "Unknown">
+<!ENTITY chatName.label "Chat Name:">
+
+<!ENTITY WorkPhone.label "Work:">
+<!ENTITY WorkPhone.accesskey "k">
+<!ENTITY HomePhone.label "Home:">
+<!ENTITY HomePhone.accesskey "m">
+<!ENTITY FaxNumber.label "Fax:">
+<!ENTITY FaxNumber.accesskey "x">
+<!ENTITY PagerNumber.label "Pager:">
+<!ENTITY PagerNumber.accesskey "g">
+<!ENTITY CellularNumber.label "Mobile:">
+<!ENTITY CellularNumber.accesskey "b">
+
+<!ENTITY Home.tab "Private">
+<!ENTITY Home.accesskey "P">
+<!ENTITY HomeAddress.label "Address:">
+<!ENTITY HomeAddress.accesskey "d">
+<!ENTITY HomeAddress2.label "">
+<!ENTITY HomeAddress2.accesskey "">
+<!ENTITY HomeCity.label "City:">
+<!ENTITY HomeCity.accesskey "y">
+<!ENTITY HomeState.label "State/Province:">
+<!ENTITY HomeState.accesskey "S">
+<!ENTITY HomeZipCode.label "ZIP/Postal Code:">
+<!ENTITY HomeZipCode.accesskey "Z">
+<!ENTITY HomeCountry.label "Country:">
+<!ENTITY HomeCountry.accesskey "u">
+<!ENTITY HomeWebPage.label "Web Page:">
+<!ENTITY HomeWebPage.accesskey "e">
+<!ENTITY Birthday.label "Birthday:">
+<!ENTITY Birthday.accesskey "B">
+<!ENTITY In.label "">
+<!ENTITY Year.placeholder "Year">
+<!ENTITY Or.value "or">
+<!ENTITY Age.placeholder "Age">
+<!ENTITY YearsOld.label "">
+
+<!ENTITY Work.tab "Work">
+<!ENTITY Work.accesskey "W">
+<!ENTITY JobTitle.label "Title:">
+<!ENTITY JobTitle.accesskey "i">
+<!ENTITY Department.label "Department:">
+<!ENTITY Department.accesskey "m">
+<!ENTITY Company.label "Organization:">
+<!ENTITY Company.accesskey "n">
+<!ENTITY WorkAddress.label "Address:">
+<!ENTITY WorkAddress.accesskey "d">
+<!ENTITY WorkAddress2.label "">
+<!ENTITY WorkAddress2.accesskey "">
+<!ENTITY WorkCity.label "City:">
+<!ENTITY WorkCity.accesskey "y">
+<!ENTITY WorkState.label "State/Province:">
+<!ENTITY WorkState.accesskey "S">
+<!ENTITY WorkZipCode.label "ZIP/Postal Code:">
+<!ENTITY WorkZipCode.accesskey "Z">
+<!ENTITY WorkCountry.label "Country:">
+<!ENTITY WorkCountry.accesskey "u">
+<!ENTITY WorkWebPage.label "Web Page:">
+<!ENTITY WorkWebPage.accesskey "e">
+
+<!ENTITY Other.tab "Other">
+<!ENTITY Other.accesskey "h">
+<!ENTITY Custom1.label "Custom 1:">
+<!ENTITY Custom1.accesskey "1">
+<!ENTITY Custom2.label "Custom 2:">
+<!ENTITY Custom2.accesskey "2">
+<!ENTITY Custom3.label "Custom 3:">
+<!ENTITY Custom3.accesskey "3">
+<!ENTITY Custom4.label "Custom 4:">
+<!ENTITY Custom4.accesskey "4">
+<!ENTITY Notes.label "Notes:">
+<!ENTITY Notes.accesskey "N">
+
+<!ENTITY Chat.tab "Chat">
+<!ENTITY Chat.accesskey "a">
+<!ENTITY Yahoo.label "Yahoo!:">
+<!ENTITY Yahoo.accesskey "Y">
+<!ENTITY Skype.label "Skype:">
+<!ENTITY Skype.accesskey "S">
+<!ENTITY QQ.label "QQ:">
+<!ENTITY QQ.accesskey "Q">
+<!ENTITY MSN.label "MSN:">
+<!ENTITY MSN.accesskey "N">
+<!ENTITY ICQ.label "ICQ:">
+<!ENTITY ICQ.accesskey "I">
+<!ENTITY XMPP.label "Jabber ID:">
+<!ENTITY XMPP.accesskey "J">
+<!ENTITY IRC.label "IRC Nick:">
+<!ENTITY IRC.accesskey "R">
+
+<!ENTITY Photo.tab "Photo">
+<!ENTITY Photo.accesskey "o">
+<!ENTITY GenericPhoto.label "Generic Photo">
+<!ENTITY GenericPhoto.accesskey "G">
+<!ENTITY DefaultPhoto.label "Default">
+<!ENTITY PhotoFile.label "On this Computer">
+<!ENTITY PhotoFile.accesskey "n">
+<!ENTITY BrowsePhoto.label "Browse">
+<!ENTITY BrowsePhoto.accesskey "r">
+<!ENTITY PhotoURL.label "On the Web">
+<!ENTITY PhotoURL.accesskey "b">
+<!ENTITY PhotoURL.placeholder "Paste or type the web address of a photo">
+<!ENTITY UpdatePhoto.label "Update">
+<!ENTITY UpdatePhoto.accesskey "u">
+<!ENTITY PhotoDropTarget.label "Drag new photo here">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abMailListDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abMailListDialog.dtd
new file mode 100644
index 0000000000..2d0401e6ae
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abMailListDialog.dtd
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<!-- Title -->
+<!ENTITY mailListWindow.title "Mailing List">
+
+<!-- Labels and Access Keys -->
+<!ENTITY addToAddressBook.label "Add to: ">
+<!ENTITY addToAddressBook.accesskey "A">
+<!ENTITY ListName.label "List Name: ">
+<!ENTITY ListName.accesskey "L">
+<!ENTITY ListNickName.label "List Nickname: ">
+<!ENTITY ListNickName.accesskey "N">
+<!ENTITY ListDescription.label "Description: ">
+<!ENTITY ListDescription.accesskey "D">
+<!-- See bug 58485, when we implement drag and drop, add 'or drag addresses' back in -->
+<!ENTITY AddressTitle.label "Type email addresses to add them to the mailing list:">
+<!ENTITY AddressTitle.accesskey "m">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abMainWindow.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abMainWindow.dtd
new file mode 100644
index 0000000000..c7910e6fd5
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abMainWindow.dtd
@@ -0,0 +1,118 @@
+<!-- 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/. -->
+
+<!-- Title -->
+<!ENTITY addressbookWindow.title "Address Book">
+<!ENTITY blankResultsPaneMessage.label "This address book shows contacts only after a search">
+<!ENTITY localResultsOnlyMessage.label "Contacts from remote address books are not shown until you search">
+<!-- File Menu -->
+<!ENTITY newContact.label "Contact…">
+<!ENTITY newContact.accesskey "C">
+<!ENTITY newContact.key "N">
+<!ENTITY newListCmd.label "Mailing List…">
+<!ENTITY newListCmd.accesskey "L">
+<!ENTITY newAddressBookCmd.label "Address Book…">
+<!ENTITY newAddressBookCmd.accesskey "o">
+<!ENTITY newLDAPDirectoryCmd.label "LDAP Directory…">
+<!ENTITY newLDAPDirectoryCmd.accesskey "D">
+<!ENTITY printContactViewCmd.label "Print Contact…">
+<!ENTITY printContactViewCmd.accesskey "P">
+<!ENTITY printContactViewCmd.key "P">
+<!ENTITY printPreviewContactViewCmd.label "Print Preview Contact">
+<!ENTITY printPreviewContactViewCmd.accesskey "v">
+<!ENTITY printAddressBook.label "Print Address Book…">
+<!ENTITY printAddressBook.accesskey "A">
+<!ENTITY printPreviewAddressBook.label "Print Preview Address Book">
+<!ENTITY printPreviewAddressBook.accesskey "B">
+
+<!-- Edit Menu -->
+<!ENTITY deleteAbCmd.label "Delete Address Book">
+<!ENTITY deleteContactCmd.label "Delete Contact">
+<!ENTITY deleteContactsCmd.label "Delete Selected Contacts">
+<!ENTITY deleteListCmd.label "Delete List">
+<!ENTITY deleteListsCmd.label "Delete Selected Lists">
+<!ENTITY deleteItemsCmd.label "Delete Selected Items">
+<!ENTITY swapFirstNameLastNameCmd.label "Swap First/Last Name">
+<!ENTITY swapFirstNameLastNameCmd.accesskey "w">
+<!ENTITY propertiesCmd.label "Properties…">
+<!ENTITY propertiesCmd.accesskey "i">
+<!ENTITY propertiesCmd.key "i">
+
+<!-- View Menu -->
+<!ENTITY showAbToolbarCmd.label "Address Book Toolbar">
+<!ENTITY showAbToolbarCmd.accesskey "o">
+<!ENTITY layoutMenu.label "Layout">
+<!ENTITY layoutMenu.accesskey "L">
+<!ENTITY showDirectoryPane.label "Directory Pane">
+<!ENTITY showDirectoryPane.accesskey "D">
+<!ENTITY showContactPane2.label "Contact Pane">
+<!ENTITY showContactPane2.accesskey "C">
+<!ENTITY menu_ShowNameAs.label "Show Name As">
+<!ENTITY menu_ShowNameAs.accesskey "n">
+<!ENTITY firstLastCmd.label "First Last">
+<!ENTITY firstLastCmd.accesskey "F">
+<!ENTITY lastFirstCmd.label "Last, First">
+<!ENTITY lastFirstCmd.accesskey "L">
+<!ENTITY displayNameCmd.label "Display Name">
+<!ENTITY displayNameCmd.accesskey "D">
+<!-- LOCALIZATION NOTE (toggleDirectoryPaneCmd.key): This is only used on the
+ mac platform, other platforms use VK_F9. -->
+<!ENTITY toggleDirectoryPaneCmd.key "S">
+
+<!-- Tasks Menu -->
+<!ENTITY importCmd.label "Import…">
+<!ENTITY importCmd.accesskey "I">
+<!ENTITY exportCmd.label "Export…">
+<!ENTITY exportCmd.accesskey "E">
+
+<!-- Toolbar and Popup items -->
+<!ENTITY newContactButton.label "New Contact">
+<!ENTITY newContactButton.accesskey "C">
+<!ENTITY newlistButton.label "New List">
+<!ENTITY newlistButton.accesskey "L">
+<!ENTITY editItemButton.label "Properties">
+<!ENTITY editItemButton.accesskey "P">
+<!ENTITY newmsgButton.label "Compose">
+<!ENTITY newmsgButton.accesskey "C">
+<!ENTITY deleteItemButton.label "Delete">
+<!ENTITY deleteItemButton.accesskey "D">
+<!ENTITY printButton.label "Print">
+<!ENTITY printButton.accesskey "r">
+<!ENTITY searchNameAndEmail.placeholder "Search Name and Email">
+<!ENTITY searchBox.title "Search">
+
+<!-- Tooltips -->
+<!ENTITY addressbookToolbar.tooltip "Address Book Toolbar">
+<!ENTITY newContactButton.tooltip "Create a new address book contact">
+<!ENTITY newlistButton.tooltip "Create a new list">
+<!ENTITY editItemButton.tooltip "Edit the selected item">
+<!ENTITY newmsgButton.tooltip "Send a mail message">
+<!ENTITY printButton.tooltip "Print selected item">
+<!ENTITY deleteItemButton.tooltip "Delete selected item">
+<!ENTITY advancedButton.tooltip "Advanced address search">
+
+<!-- Dir Tree header -->
+<!ENTITY dirTreeHeader.label "Address Books">
+
+<!-- Card Summary Pane -->
+<!-- Box Headings -->
+<!ENTITY contact.heading "Contact">
+<!ENTITY home.heading "Home">
+<!ENTITY other.heading "Other">
+<!ENTITY chat.heading "Chat">
+<!ENTITY phone.heading "Phone">
+<!ENTITY work.heading "Work">
+<!-- Special Box Headings, for mailing lists -->
+<!ENTITY description.heading "Description">
+<!ENTITY addresses.heading "Addresses">
+<!-- For Map It! -->
+<!ENTITY mapItButton.label "Get Map">
+<!ENTITY mapIt.tooltip "Display a map of this address from the Web">
+
+<!-- Status Bar -->
+<!ENTITY statusText.label "">
+
+<!-- LOCALIZATION NOTE (hideSwapFnLnUI) : DONT_TRANSLATE -->
+<!-- Swap FN/LN UI Set to "false" to show swap fn/ln UI -->
+<!ENTITY hideSwapFnLnUI "true">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abNewCardDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abNewCardDialog.dtd
new file mode 100644
index 0000000000..709f2b6bd0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abNewCardDialog.dtd
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+
+<!-- Labels -->
+<!ENTITY chooseAddressBook.label "Add to: ">
+<!ENTITY chooseAddressBook.accesskey "t">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abResultsPaneOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abResultsPaneOverlay.dtd
new file mode 100644
index 0000000000..03659a690e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abResultsPaneOverlay.dtd
@@ -0,0 +1,52 @@
+<!-- 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/. -->
+
+<!ENTITY Addrbook.label "Address Book">
+<!ENTITY Addrbook.accesskey "B">
+<!ENTITY GeneratedName.label "Name">
+<!ENTITY GeneratedName.accesskey "N">
+<!ENTITY PrimaryEmail.label "Email">
+<!ENTITY PrimaryEmail.accesskey "E">
+<!ENTITY Company.label "Organization">
+<!ENTITY Company.accesskey "z">
+<!ENTITY _PhoneticName.label "Phonetic Name">
+<!ENTITY _PhoneticName.accesskey "o">
+<!ENTITY NickName.label "Nickname">
+<!ENTITY NickName.accesskey "i">
+<!ENTITY SecondEmail.label "Additional Email">
+<!ENTITY SecondEmail.accesskey "l">
+<!ENTITY Department.label "Department">
+<!ENTITY Department.accesskey "r">
+<!ENTITY JobTitle.label "Title">
+<!ENTITY JobTitle.accesskey "T">
+<!ENTITY CellularNumber.label "Mobile">
+<!ENTITY CellularNumber.accesskey "M">
+<!ENTITY PagerNumber.label "Pager">
+<!ENTITY PagerNumber.accesskey "P">
+<!ENTITY FaxNumber.label "Fax">
+<!ENTITY FaxNumber.accesskey "F">
+<!ENTITY HomePhone.label "Home Phone">
+<!ENTITY HomePhone.accesskey "H">
+<!ENTITY WorkPhone.label "Work Phone">
+<!ENTITY WorkPhone.accesskey "W">
+<!ENTITY ChatName.label "Chat Name">
+<!ENTITY ChatName.accesskey "C">
+<!ENTITY sortAscending.label "Ascending">
+<!ENTITY sortAscending.accesskey "A">
+<!ENTITY sortDescending.label "Descending">
+<!ENTITY sortDescending.accesskey "D">
+
+<!-- context menu -->
+<!ENTITY composeEmail.label "Compose Email To">
+<!ENTITY composeEmail.accesskey "C">
+<!ENTITY copyAddress.label "Copy Email Address">
+<!ENTITY copyAddress.accesskey "A">
+<!ENTITY deleteAddrBookCard.label "Delete">
+<!ENTITY deleteAddrBookCard.accesskey "D">
+<!ENTITY newAddrBookCard.label "New Card…">
+<!ENTITY newAddrBookCard.accesskey "N">
+<!ENTITY newAddrBookMailingList.label "New Mailing List…">
+<!ENTITY newAddrBookMailingList.accesskey "M">
+<!ENTITY addrBookCardProperties.label "Properties">
+<!ENTITY addrBookCardProperties.accesskey "P">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/abSelectAddressesDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abSelectAddressesDialog.dtd
new file mode 100644
index 0000000000..7922186689
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/abSelectAddressesDialog.dtd
@@ -0,0 +1,30 @@
+<!-- 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/. -->
+
+<!-- Title -->
+<!ENTITY selectAddressWindow.title "Select Addresses">
+
+<!-- Buttons -->
+<!ENTITY toButton.label "To->">
+<!ENTITY toButton.accesskey "T">
+<!ENTITY ccButton.label "Cc->">
+<!ENTITY ccButton.accesskey "C">
+<!ENTITY bccButton.label "Bcc->">
+<!ENTITY bccButton.accesskey "B">
+<!ENTITY newButton.label "New…">
+<!ENTITY newButton.accesskey "N">
+<!ENTITY editButton.label "Edit…">
+<!ENTITY editButton.accesskey "E">
+<!ENTITY removeButton.label "Remove">
+<!ENTITY removeButton.accesskey "R">
+<!ENTITY lookIn.label "Look in:">
+<!ENTITY lookIn.accesskey "L">
+<!ENTITY for.label "for:">
+<!ENTITY for.accesskey "f">
+<!ENTITY for.placeholder "Name or Email">
+<!ENTITY addressMessageTo.label "Address message to:">
+
+<!-- Tooltips items -->
+<!ENTITY addressPickerNewButton.tooltip "Create a new address book card">
+<!ENTITY addressPickerEditButton.tooltip "Edit the selected card">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties b/comm/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
new file mode 100644
index 0000000000..3dcab827aa
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
@@ -0,0 +1,264 @@
+# 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/.
+
+# The following are used by the Mailing list dialog.
+# LOCALIZATION NOTE (mailingListTitleEdit): %S will be replaced by the Mailing List's display name.
+mailingListTitleEdit=Edit %S
+emptyListName=You must enter a list name.
+lastFirstFormat=%S, %S
+firstLastFormat=%S %S
+
+allAddressBooks=All Address Books
+
+newContactTitle=New Contact
+# %S will be the card's display name
+newContactTitleWithDisplayName=New Contact for %S
+editContactTitle=Edit Contact
+# %S will be the card's display name
+editContactTitleWithDisplayName=Edit Contact for %S
+# don't translate vCard
+editVCardTitle=Edit vCard
+# %S will be the card's display name, don't translate vCard
+editVCardTitleWithDisplayName=Edit vCard for %S
+
+## LOCALIZATION NOTE (cardRequiredDataMissingMessage): do not localize \n
+cardRequiredDataMissingMessage=You must enter at least one of the following items:\nEmail Address, First Name, Last Name, Display Name, Organization.
+cardRequiredDataMissingTitle=Required Information Missing
+incorrectEmailAddressFormatMessage=The primary e-mail address must be of the form user@host.
+incorrectEmailAddressFormatTitle=Incorrect Email Address Format
+
+viewListTitle=Mailing List: %S
+mailListNameExistsTitle=Mailing List Already Exists
+mailListNameExistsMessage=A Mailing List with that name already exists. Please choose a different name.
+
+confirmDeleteThisContactTitle=Delete Contact
+# LOCALIZATION NOTE (confirmDeleteThisContact):
+# #1 The name of the selected contact
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete this contact?
+# • John Doe
+confirmDeleteThisContact=Are you sure you want to delete this contact?\n• #1
+
+confirmDelete2orMoreContactsTitle=Delete Multiple Contacts
+# LOCALIZATION NOTE (confirmDelete2orMoreContacts):
+# Semicolon list of plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected contacts, always more than 1.
+# Example: Are you sure you want to delete these 3 contacts?
+confirmDelete2orMoreContacts=Are you sure you want to delete this #1 contact?;Are you sure you want to delete these #1 contacts?
+
+confirmRemoveThisContactTitle=Remove Contact
+# LOCALIZATION NOTE (confirmRemoveThisContact):
+# #1 The name of the selected contact
+# #2 The name of the containing mailing list
+# This title is about a contact in a mailing list, so it will not be deleted,
+# but only removed from the list.
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to remove this contact from the mailing list 'Customers List'?
+# • John Doe
+confirmRemoveThisContact=Are you sure you want to remove this contact from the mailing list '#2'?\n• #1
+
+confirmRemove2orMoreContactsTitle=Remove Multiple Contacts
+# LOCALIZATION NOTE (confirmRemove2orMoreContacts):
+# Semicolon list of singular and plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected contacts, always more than 1.
+# #2 The name of the containing mailing list
+# Example: Are you sure you want to remove these 3 contacts from the mailing list 'Customers List'?
+confirmRemove2orMoreContacts=Are you sure you want to remove this #1 contact from the mailing list '#2'?;Are you sure you want to remove these #1 contacts from the mailing list '#2'?
+
+confirmDeleteThisMailingListTitle=Delete Mailing List
+# LOCALIZATION NOTE (confirmDeleteThisMailingList):
+# #1 The name of the selected mailing list
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete this mailing list?
+# • Customers List
+confirmDeleteThisMailingList=Are you sure you want to delete this mailing list?\n• #1
+
+confirmDelete2orMoreMailingListsTitle=Delete Multiple Mailing Lists
+# LOCALIZATION NOTE (confirmDelete2orMoreMailingLists):
+# Semicolon list of plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected mailing lists, always more than 1
+# Example: Are you sure you want to delete these 3 mailing lists?
+confirmDelete2orMoreMailingLists=Are you sure you want to delete this #1 mailing list?;Are you sure you want to delete these #1 mailing lists?
+
+confirmDelete2orMoreContactsAndListsTitle=Delete Contacts and Mailing Lists
+# LOCALIZATION NOTE (confirmDelete2orMoreContactsAndLists):
+# Semicolon list of and plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected contacts and mailing lists, always more than 1
+# Example: Are you sure you want to delete these 3 contacts and mailing lists?
+confirmDelete2orMoreContactsAndLists=Are you sure you want to delete these #1 contacts and mailing lists?;Are you sure you want to delete these #1 contacts and mailing lists?
+
+confirmDeleteThisAddressbookTitle=Delete Address Book
+# LOCALIZATION NOTE (confirmDeleteThisAddressbookTitle):
+# #1 The name of the selected address book
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete this address book and all of its contacts?
+# • Friends and Family Address Book
+confirmDeleteThisAddressbook=Are you sure you want to delete this address book and all of its contacts?\n• #1
+
+confirmDeleteThisLDAPDirTitle=Delete Local LDAP Directory
+# LOCALIZATION NOTE (confirmDeleteThisLDAPDir):
+# #1 The name of the selected LDAP directory
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete the local copy of this LDAP directory and all of its offline contacts?
+# • Mozilla LDAP Directory
+confirmDeleteThisLDAPDir=Are you sure you want to delete the local copy of this LDAP directory and all of its offline contacts?\n• #1
+
+confirmDeleteThisCollectionAddressbookTitle=Delete Collection Address Book
+# LOCALIZATION NOTE (confirmDeleteThisCollectionAddressbook):
+# #1 The name of the selected collection address book
+# #2 The name of the application (Thunderbird)
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: If this address book is deleted, Thunderbird will no longer collect addresses.
+# Are you sure you want to delete this address book and all of its contacts?
+# • My Collecting Addressbook
+confirmDeleteThisCollectionAddressbook=If this address book is deleted, #2 will no longer collect addresses.\nAre you sure you want to delete this address book and all of its contacts?\n• #1
+
+propertyPrimaryEmail=Email
+propertyListName=List Name
+propertySecondaryEmail=Additional Email
+propertyNickname=Nickname
+propertyDisplayName=Display Name
+propertyWork=Work
+propertyHome=Home
+propertyFax=Fax
+propertyCellular=Mobile
+propertyPager=Pager
+propertyBirthday=Birthday
+propertyCustom1=Custom 1
+propertyCustom2=Custom 2
+propertyCustom3=Custom 3
+propertyCustom4=Custom 4
+
+# Google Talk and AIM need to stay for now because of shared mailnews code.
+propertyGtalk=Google Talk
+propertyAIM=AIM
+propertyYahoo=Yahoo!
+propertySkype=Skype
+propertyQQ=QQ
+propertyMSN=MSN
+propertyICQ=ICQ
+propertyXMPP=Jabber ID
+propertyIRC=IRC Nick
+
+## LOCALIZATION NOTE (cityAndStateAndZip):
+## %1$S is city, %2$S is state, %3$S is zip
+cityAndStateAndZip=%1$S, %2$S %3$S
+## LOCALIZATION NOTE (cityAndStateNoZip):
+## %1$S is city, %2$S is state
+cityAndStateNoZip=%1$S, %2$S
+## LOCALIZATION NOTE (cityOrStateAndZip):
+## %1$S is city or state, %2$S is zip
+cityOrStateAndZip=%1$S %2$S
+
+stateZipSeparator=
+
+prefixTo=To
+prefixCc=Cc
+prefixBcc=Bcc
+emptyEmailAddCard=You cannot add a card that has no primary email address
+emptyEmailAddCardTitle=Cannot Add Card
+addressBook=Address Book
+
+# Contact photo management
+browsePhoto=Contact Photo
+stateImageSave=Saving the image…
+errorInvalidUri=Error: Invalid source image.
+errorNotAvailable=Error: The file is not accessible.
+errorInvalidImage=Error: Only JPG, PNG and GIF image types are supported.
+errorSaveOperation=Error: Could not save the image.
+
+# mailnews.js
+ldap_2.servers.pab.description=Personal Address Book
+ldap_2.servers.history.description=Collected Addresses
+## LOCALIZATION NOTE (ldap_2.servers.oe.description is only used on Windows)
+ldap_2.servers.oe.description=OE Contacts
+## LOCALIZATION NOTE (ldap_2.servers.osx.description is only used on macOS)
+ldap_2.servers.osx.description=macOS Address Book
+
+# status bar stuff
+## LOCALIZATION NOTE (totalContactStatus):
+## %1$S is address book name, %2$S is card count
+totalContactStatus=Total contacts in %1$S: %2$S
+noMatchFound=No matches found
+## LOCALIZATION NOTE (matchesFound1):
+## Semicolon-separated list of singular and plural forms.
+## See: https://developer.mozilla.org/docs/Mozilla/Localization/Localization_and_Plurals
+## #1 is the number of matching contacts found
+matchesFound1=#1 match found;#1 matches found
+
+## LOCALIZATION NOTE (contactsCopied): Semi-colon list of plural forms
+## %1$S is the number of contacts that were copied. This should be used multiple
+## times wherever you need it. Do not replace by %S.
+contactsCopied=%1$S contact copied;%1$S contacts copied
+
+## LOCALIZATION NOTE (contactsMoved): Semi-colon list of plural forms
+## %1$S is the number of contacts that were moved. This should be used multiple
+## times wherever you need it. Do not replace by %S.
+contactsMoved=%1$S contact moved;%1$S contacts moved
+
+# LDAP directory stuff
+invalidName=Please enter a valid Name.
+invalidHostname=Please enter a valid Hostname.
+invalidPortNumber=Please enter a valid Port Number.
+invalidResults=Please enter a valid number in the results field.
+abReplicationOfflineWarning=You must be online to perform LDAP replication.
+abReplicationSaveSettings=Settings must be saved before a directory may be downloaded.
+
+# For importing / exporting
+## LOCALIZATION NOTE (ExportAddressBookNameTitle): %S is the name of exported addressbook
+ExportAddressBookNameTitle=Export Address Book - %S
+LDIFFiles=LDIF
+CSVFiles=Comma Separated
+CSVFilesSysCharset=Comma Separated (System Charset)
+CSVFilesUTF8=Comma Separated (UTF-8)
+TABFiles=Tab Delimited
+TABFilesSysCharset=Tab Delimited (System Charset)
+TABFilesUTF8=Tab Delimited (UTF-8)
+VCFFiles=vCard
+failedToExportTitle=Export Failed
+failedToExportMessageNoDeviceSpace=Failed to export addressbook, no space left on device.
+failedToExportMessageFileAccessDenied=Failed to export addressbook, file access denied.
+
+# For getting authDN for replication using dlg box
+AuthDlgTitle=Address Book LDAP Replication
+AuthDlgDesc=To access the directory server, enter your user name and password.
+
+# LOCALIZATION NOTE(joinMeInThisChat)
+# use + for spaces
+joinMeInThisChat=Join+me+in+this+Chat.
+
+# For printing
+headingHome=Home
+headingWork=Work
+headingOther=Other
+headingChat=Chat
+headingPhone=Phone
+headingDescription=Description
+headingAddresses=Addresses
+
+## For address books
+addressBookTitleNew=New Address Book
+# LOCALIZATION NOTE (addressBookTitleEdit):
+# %S is the current name of the address book.
+# Example: My Custom AB Properties
+addressBookTitleEdit=%S Properties
+duplicateNameTitle=Duplicate Address Book Name
+# LOCALIZATION NOTE (duplicateNameText):
+# Don't localize "\n• %S" unless your local layout comes out wrong.
+# %S is the name of the existing address book.
+# Example: An address book with this name already exists:
+# • My Custom AB
+duplicateNameText=An address book with this name already exists:\n• %S
+
+# For corrupt .mab files
+corruptMabFileTitle=Corrupt Address Book File
+corruptMabFileAlert=One of your address book files (%1$S file) could not be read. A new %2$S file will be created and a backup of the old file, called %3$S, will be created in the same directory.
+
+# For locked .mab files
+lockedMabFileTitle=Unable to Load Address Book File
+lockedMabFileAlert=Unable to load address book file %S. It may be read-only, or locked by another application. Please try again later.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/addressbook/ldapAutoCompErrs.properties b/comm/suite/locales/en-US/chrome/mailnews/addressbook/ldapAutoCompErrs.properties
new file mode 100644
index 0000000000..d70b0da538
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/addressbook/ldapAutoCompErrs.properties
@@ -0,0 +1,104 @@
+# 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/.
+
+# These are error strings for problems that happen while in the
+# various states declared in nsILDAPAutoCompFormatter.idl. Note that
+# the number that indexes each error state is the same as the number
+# corresponding to that state in nsILDAPAutoCompFormatter.idl.
+
+## @name ERR_STATE_UNBOUND
+## @loc none
+0=LDAP initialization problem
+
+## @name ERR_STATE_INITIALIZING
+## @loc none
+1=LDAP server connection failed
+
+## @name ERR_STATE_BINDING
+## @loc none
+2=LDAP server connection failed
+
+## @name ERR_STATE_BOUND
+## @loc none
+3=LDAP server communications problem
+
+## @name ERR_STATE_SEARCHING
+## @loc none
+4=LDAP server search problem
+
+
+# The format of the alert dialog itself
+#
+## @name ALERT_FORMAT
+## @loc None of %1$S, %2$S and %3$S should be localized.
+## %1$S is the error code itself, %2$S is an LDAP SDK error message from
+## chrome://mozldap/locale/ldap.properties, and %3$S is a hint relating
+## to that specific error, found in this file.
+errorAlertFormat=Error code %1$S: %2$S\n\n %3$S
+
+## The following errors are for error codes other than LDAP-specific ones.
+## Someday mozilla will actually have a system for mapping nsresults to
+## error strings that's actually widely used, unlike nsIErrorService. But
+## until it does, these strings live here…
+
+## @name HOST_NOT_FOUND
+## @loc none
+5000=Host not found
+
+## @name GENERIC_ERROR
+## @loc none
+9999=Unknown error
+
+
+# Hints to for the user, associated with specific error codes (ie error code
+# + 10000)
+
+
+## @name TIMELIMIT_EXCEEDED_HINT
+## @loc none
+10003=Please try again later, or else contact your System Administrator.
+
+## @name STRONGAUTH_REQUIRED_HINT
+## @loc none
+10008=Strong authentication is not currently supported.
+
+## @name INVALID_SYNTAX_HINT
+## @loc none
+10021=Verify that the search filter is correct, and then try again, or else contact your System Administrator. To verify that the search filter is correct, from the Edit menu, choose Preferences, then choose Mail & Newsgroups, and then choose Addressing. Click Edit Directories, and select the LDAP server being used. Click Edit, and then click Advanced to display the Search Filter.
+
+## @name NO_SUCH_OBJECT_HINT
+## @loc none
+10032=Verify that the Base DN is correct, and then try again, or else contact your System Administrator. To verify that the Base DN is correct, from the Edit menu, choose Preferences, then choose Mail & Newsgroups, and then choose Addressing. Click Edit Directories, and select the LDAP server being used. Click Edit to display the Base DN.
+
+## @name BUSY_HINT
+## @loc none
+10051=Please try again later.
+
+## @name SERVER_DOWN_HINT
+## @loc none
+10081=Verify that the Hostname and Port Number are correct, and then try again, or else contact your System Administrator. To verify that the Hostname and Port Number are correct, from the Edit menu, choose Preferences, then choose Mail & Newsgroups, and then choose Addressing. Click Edit Directories, and select the LDAP server being used. Click Edit to display the Hostname. Click Advanced to display the Port Number.
+
+## @name TIMEOUT_HINT
+## @loc none
+10085=Please try again later.
+
+## @name FILTER_ERROR_HINT
+## @loc none
+10087=Verify that the search filter is correct, and then try again, or else contact your System Administrator. To verify that the search filter is correct, from the Edit menu, choose Preferences, then choose Mail & Newsgroups, and then choose Addressing. Click Edit Directories, and select the LDAP server being used. Click Edit, and then click Advanced to display the Search Filter.
+
+## @name NO_MEMORY_HINT
+## @loc none
+10090=Please close some other windows and/or applications and try again.
+
+## @name CONNECT_ERROR_HINT
+## @loc none
+10091=Verify that the Hostname and Port Number are correct, and then try again, or else contact your System Administrator. To verify that the Hostname and Port Number are correct, from the Edit menu, choose Preferences, then choose Mail & Newsgroups, and then choose Addressing. Click Edit Directories, and select the LDAP server being used. Click Edit to display the Hostname. Click Advanced to display the Port Number.
+
+## @name HOST_NOT_FOUND_HINT
+## @loc none
+15000=Verify that the Hostname is correct, and then try again, or else contact your System Administrator. To verify that the Hostname is correct, from the Edit menu, choose Preferences, then choose Mail & Newsgroups, and then choose Addressing. Click Edit Directories, and select the LDAP server being used. Click Edit to display the Hostname.
+
+## @name GENERIC_HINT
+## @loc none
+19999=Please contact your System Administrator.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/appleMailImportMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/appleMailImportMsgs.properties
new file mode 100644
index 0000000000..cbf8c02d69
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/appleMailImportMsgs.properties
@@ -0,0 +1,20 @@
+# 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/.
+
+# Short name of import module
+ApplemailImportName=Apple Mail
+
+# Description of import module
+ApplemailImportDescription=Import Local Mail from Apple Mail
+
+# Success Message
+# LOCALIZATION NOTE(ApplemailImportMailboxSuccess): Do not translate the word "%S" below.
+ApplemailImportMailboxSuccess=Local messages were successfully imported from %S
+
+# Error Message
+ApplemailImportMailboxBadparam=An internal error occurred. Importing failed. Try importing again.
+
+# Error message
+# LOCALIZATION NOTE(ApplemailImportMailboxConverterror): Do not translate the word "%S" below.
+ApplemailImportMailboxConverterror=An error occurred while importing messages from %S. Messages were not imported.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/beckyImportMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/beckyImportMsgs.properties
new file mode 100644
index 0000000000..97aa769651
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/beckyImportMsgs.properties
@@ -0,0 +1,19 @@
+# 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/.
+#
+# The following are used by the becky import code to display status/error
+# and informational messages
+
+# Short name of import module
+BeckyImportName=Becky! Internet Mail
+
+# Description of import module
+BeckyImportDescription=Import Local Mail from Becky! Internet Mail
+
+# Success Message
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+# The variable %S will contain the name of the Mailbox
+BeckyImportMailboxSuccess=Local messages were successfully imported from %S.
+
+BeckyImportAddressSuccess=Address book imported
diff --git a/comm/suite/locales/en-US/chrome/mailnews/charsetTitles.properties b/comm/suite/locales/en-US/chrome/mailnews/charsetTitles.properties
new file mode 100644
index 0000000000..91fb080bfa
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/charsetTitles.properties
@@ -0,0 +1,80 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+## Rule of this file:
+## 1. key should always be in lower case ascii so we can do case insensitive
+## comparison in the code faster.
+
+## Format of this file:
+## charset_name.title = a_title - specifies the human readable title for
+## this charset
+
+iso-8859-1.title = Western (ISO-8859-1)
+iso-8859-2.title = Central European (ISO-8859-2)
+iso-8859-3.title = South European (ISO-8859-3)
+iso-8859-4.title = Baltic (ISO-8859-4)
+iso-8859-10.title = Nordic (ISO-8859-10)
+iso-8859-13.title = Baltic (ISO-8859-13)
+iso-8859-14.title = Celtic (ISO-8859-14)
+iso-8859-15.title = Western (ISO-8859-15)
+iso-8859-16.title = Romanian (ISO-8859-16)
+windows-1250.title = Central European (Windows-1250)
+windows-1252.title = Western (Windows-1252)
+windows-1254.title = Turkish (Windows-1254)
+windows-1257.title = Baltic (Windows-1257)
+macintosh.title = Western (MacRoman)
+x-mac-ce.title = Central European (MacCE)
+x-mac-turkish.title = Turkish (MacTurkish)
+x-mac-croatian.title = Croatian (MacCroatian)
+x-mac-romanian.title = Romanian (MacRomanian)
+x-mac-icelandic.title = Icelandic (MacIcelandic)
+iso-2022-jp.title = Japanese (ISO-2022-JP)
+shift_jis.title = Japanese (Shift_JIS)
+euc-jp.title = Japanese (EUC-JP)
+big5.title = Chinese Traditional (Big5)
+big5-hkscs.title = Chinese Traditional (Big5-HKSCS)
+gb2312.title = Chinese Simplified (GB2312)
+gbk.title = Chinese Simplified (GBK)
+euc-kr.title = Korean (EUC-KR)
+utf-7.title = Unicode (UTF-7)
+utf-8.title = Unicode (UTF-8)
+utf-16.title = Unicode (UTF-16)
+utf-16le.title = Unicode (UTF-16LE)
+utf-16be.title = Unicode (UTF-16BE)
+iso-8859-5.title = Cyrillic (ISO-8859-5)
+windows-1251.title = Cyrillic (Windows-1251)
+x-mac-cyrillic.title = Cyrillic (MacCyrillic)
+x-mac-ukrainian.title = Cyrillic/Ukrainian (MacUkrainian)
+koi8-r.title = Cyrillic (KOI8-R)
+koi8-u.title = Cyrillic/Ukrainian (KOI8-U)
+iso-8859-7.title = Greek (ISO-8859-7)
+windows-1253.title = Greek (Windows-1253)
+x-mac-greek.title = Greek (MacGreek)
+windows-1258.title = Vietnamese (Windows-1258)
+windows-874.title = Thai (Windows-874)
+iso-8859-6.title = Arabic (ISO-8859-6)
+iso-8859-8.title = Hebrew Visual (ISO-8859-8)
+iso-8859-8-i.title = Hebrew (ISO-8859-8-I)
+windows-1255.title = Hebrew (Windows-1255)
+windows-1256.title = Arabic (Windows-1256)
+x-user-defined.title = User Defined
+ibm866.title = Cyrillic/Russian (CP-866)
+gb18030.title = Chinese Simplified (GB18030)
+x-mac-arabic.title = Arabic (MacArabic)
+x-mac-farsi.title = Farsi (MacFarsi)
+x-mac-hebrew.title = Hebrew (MacHebrew)
+x-mac-devanagari.title = Hindi (MacDevanagari)
+x-mac-gujarati.title = Gujarati (MacGujarati)
+x-mac-gurmukhi.title = Gurmukhi (MacGurmukhi)
+
+chardet.off.title = (Off)
+chardet.universal_charset_detector.title = Universal
+chardet.ja_parallel_state_machine.title = Japanese
+chardet.ko_parallel_state_machine.title = Korean
+chardet.zhtw_parallel_state_machine.title = Traditional Chinese
+chardet.zhcn_parallel_state_machine.title = Simplified Chinese
+chardet.zh_parallel_state_machine.title = Chinese
+chardet.cjk_parallel_state_machine.title = East Asian
+chardet.ruprob.title = Russian
+chardet.ukprob.title = Ukrainian
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/addressingWidgetOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/compose/addressingWidgetOverlay.dtd
new file mode 100644
index 0000000000..cbad90663e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/addressingWidgetOverlay.dtd
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+
+<!-- address labels -->
+<!--LOCALIZATION NOTE addressingWidgetOverlay.dtd The basic mail/news composition headers as they are seen in UI -->
+<!ENTITY toAddr.label "To:">
+<!ENTITY ccAddr.label "Cc:">
+<!ENTITY bccAddr.label "Bcc:">
+<!ENTITY replyAddr.label "Reply-To:">
+<!ENTITY newsgroupsAddr.label "Newsgroup:">
+<!ENTITY followupAddr.label "Followup-To:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.dtd b/comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.dtd
new file mode 100644
index 0000000000..11e06a452b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE askSendFormat.dtd UI for dialog that asks the user, which format to use for sending a message -->
+<!ENTITY windowTitle.label "HTML Mail Question">
+
+<!ENTITY recipient.label "Some of the recipients are not listed as being able to receive HTML mail.">
+
+<!ENTITY question.label "Would you like to convert the message to plain text or send it in HTML anyway?">
+
+<!ENTITY plainTextAndHtml.label "Send in Plain Text and HTML">
+<!ENTITY plainTextAndHtml.accesskey "a">
+<!ENTITY plainTextOnly.label "Send in Plain Text Only">
+<!ENTITY plainTextOnly.accesskey "P">
+<!ENTITY htmlOnly.label "Send in HTML Only">
+<!ENTITY htmlOnly.accesskey "H">
+
+<!ENTITY send.label "Send">
+<!ENTITY send.accesskey "S">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.properties b/comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.properties
new file mode 100644
index 0000000000..b594a444fd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/askSendFormat.properties
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+convertibleYes=Your message can be converted to plain text without losing information.
+convertibleAltering=Your message can be converted to plain text without losing important information. However, the plain text version might look different from what you saw in the composer.
+convertibleNo=However, you used formatting (e.g. colors) that will not be converted to plain text.
+recommended=(recommended)
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties
new file mode 100644
index 0000000000..61358d9ae3
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/composeMsgs.properties
@@ -0,0 +1,318 @@
+# 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/.
+
+#
+# The following are used by the compose back end
+#
+## LOCALIZATION NOTE (unableToOpenFile, unableToOpenTmpFile):
+## %S will be replaced with the name of file that could not be opened
+unableToOpenFile=Unable to open the file %S.
+unableToOpenTmpFile=Unable to open the temporary file %S. Check your 'Temporary Directory' setting.
+unableToSaveTemplate=Unable to save your message as a template.
+unableToSaveDraft=Unable to save your message as a draft.
+couldntOpenFccFolder=Couldn't open the Sent mail folder. Please verify that your Mail preferences are correct.
+noSender=No sender was specified. Please add your email address in the Mail & Newsgroups account settings.
+noRecipients=No recipients were specified. Please enter a recipient or newsgroup in the addressing area.
+errorWritingFile=Error writing temporary file.
+
+## LOCALIZATION NOTE (errorSendingFromCommand): argument %s is the Outgoing server (SMTP) response
+errorSendingFromCommand=An error occurred while sending mail. The mail server responded: %s. Please verify that your email address is correct in your Mail preferences and try again.
+
+## LOCALIZATION NOTE (errorSendingDataCommand): argument %s is the Outgoing server (SMTP) response
+errorSendingDataCommand=An Outgoing server (SMTP) error occurred while sending mail. The server responded: %s.
+
+## LOCALIZATION NOTE (errorSendingMessage): argument %s is the Outgoing server (SMTP) response
+errorSendingMessage=An error occurred while sending mail. The mail server responded: %s. Please check the message and try again.
+postFailed=The message could not be posted because connecting to the news server failed. The server may be unavailable or is refusing connections. Please verify that your news server settings are correct and try again.
+errorQueuedDeliveryFailed=An error occurred while delivering unsent messages.
+sendFailed=Sending of the message failed.
+
+## LOCALIZATION NOTE (sendFailedUnexpected): argument %X is a hex error code value
+sendFailedUnexpected=Failed due to unexpected error %X. No description is available.
+
+## LOCALIZATION NOTE (smtpSecurityIssue): argument %S is the Outgoing smtp server name
+smtpSecurityIssue=The configuration related to %S must be corrected.
+
+## LOCALIZATION NOTE (smtpServerError): argument %s is the Outgoing server (SMTP) response
+smtpServerError=An error occurred while sending mail: Outgoing server (SMTP) error. The server responded: %s.
+unableToSendLater=Sorry, we were unable to save your message for sending later.
+
+## LOCALIZATION NOTE (communicationsError): argument %d is the error code
+communicationsError=A communications error occurred: %d. Please try again.
+dontShowAlert=THIS IS JUST A PLACEHOLDER. YOU SHOULD NEVER SEE THIS STRING.
+
+couldNotGetUsersMailAddress2=An error occurred while sending mail: the sender's address (From:) was invalid. Please verify that this email address is correct and try again.
+couldNotGetSendersIdentity=An error occurred while sending mail: the sender identity was invalid. Please verify the configuration of your identity and try again.
+mimeMpartAttachmentError=Attachment error.
+failedCopyOperation=The message was sent successfully, but could not be copied to your Sent folder.
+nntpNoCrossPosting=You can only send a message to one news server at a time.
+msgCancelling=Cancelling…
+sendFailedButNntpOk=Your message has been posted to the newsgroup but has not been sent to the other recipient.
+errorReadingFile=Error reading file.
+followupToSenderMessage=The author of this message has requested that responses be sent only to the author. If you also want to reply to the newsgroup, add a new row to the addressing area, choose Newsgroup from the recipients list, and enter the name of the newsgroup.
+
+## LOCALIZATION NOTE (errorAttachingFile): argument %S is the file name/URI of object to be attached
+errorAttachingFile=There was an error attaching %S. Please check that you have access to the file.
+
+## LOCALIZATION NOTE (incorrectSmtpGreeting): argument %s is the Outgoing server (SMTP) greeting
+incorrectSmtpGreeting=An error occurred while sending mail: The mail server sent an incorrect greeting: %s.
+
+## LOCALIZATION NOTE (errorSendingRcptCommand): argument %1$S is the Outgoing server (SMTP) response, argument %2$S is the intended message recipient.
+errorSendingRcptCommand=An error occurred while sending mail. The mail server responded:\n%1$S.\nPlease check the message recipient "%2$S" and try again.
+
+## LOCALIZATION NOTE (startTlsFailed): argument %S is the Outgoing server (SMTP)
+startTlsFailed=An error occurred while sending mail: Unable to establish a secure link with Outgoing server (SMTP) %S using STARTTLS since it doesn't advertise that feature. Switch off STARTTLS for this server or contact your service provider.
+
+## LOCALIZATION NOTE (smtpPasswordUndefined): argument %S is the Outgoing server (SMTP) account
+smtpPasswordUndefined=An error occurred while sending mail: Could not get password for %S. The message was not sent.
+
+## LOCALIZATION NOTE (smtpSendNotAllowed): argument %s is the Outgoing server (SMTP) response
+smtpSendNotAllowed=An error occurred while sending mail. The mail server responded:\n%s.\nPlease ensure that you are using the correct identity to send and that the used authentication method is correct. Verify that you are allowed to send via this SMTP server with your current credentials from your current network.
+
+## LOCALIZATION NOTE (smtpTempSizeExceeded): argument %s is the Outgoing server (SMTP) response
+smtpTempSizeExceeded=The size of the message you are trying to send exceeds a temporary size limit of the server. The message was not sent; try to reduce the message size or wait some time and try again. The server responded: %s.
+
+## LOCALIZATION NOTE (smtpClientid): argument %s is the Outgoing server (SMTP) response
+smtpClientid=The outgoing server (SMTP) detected an error in the CLIENTID command. The message was not sent. The server responded: %s
+
+## LOCALIZATION NOTE (smtpClientidPermission): argument %s is the Outgoing server (SMTP) response
+smtpClientidPermission=The outgoing server (SMTP) response to the CLIENTID command indicates that your device is not permitted to send mail. The server responded: %s
+
+## LOCALIZATION NOTE (smtpPermSizeExceeded1): argument %d is the Outgoing server (SMTP) size limit
+smtpPermSizeExceeded1=The size of the message you are trying to send exceeds the global size limit (%d bytes) of the server. The message was not sent; reduce the message size and try again.
+
+## LOCALIZATION NOTE (smtpPermSizeExceeded2): argument %s is the server's response
+smtpPermSizeExceeded2=The size of the message you are trying to send exceeds the global size limit of the server. The message was not sent; reduce the message size and try again. The server responded: %s.
+
+## LOCALIZATION NOTE (smtpSendFailedUnknownServer): argument %S is the Outgoing server (SMTP)
+smtpSendFailedUnknownServer=An error occurred while sending mail: Outgoing server (SMTP) %S is unknown. The server may be incorrectly configured. Please verify that your Outgoing server (SMTP) settings are correct and try again.
+
+## LOCALIZATION NOTE (smtpSendRequestRefused): argument %S is the Outgoing server (SMTP)
+smtpSendRequestRefused=The message could not be sent because connecting to Outgoing server (SMTP) %S failed. The server may be unavailable or is refusing SMTP connections. Please verify that your Outgoing server (SMTP) settings are correct and try again.
+
+## LOCALIZATION NOTE (smtpSendInterrupted): argument %S is the Outgoing server (SMTP)
+smtpSendInterrupted=The message could not be sent because the connection to Outgoing server (SMTP) %S was lost in the middle of the transaction. Try again.
+
+## LOCALIZATION NOTE (smtpSendTimeout): argument %S is the Outgoing server (SMTP)
+smtpSendTimeout=The message could not be sent because the connection to Outgoing server (SMTP) %S timed out. Try again.
+
+## LOCALIZATION NOTE (smtpSendFailedUnknownReason): argument %S is the Outgoing server (SMTP)
+smtpSendFailedUnknownReason=The message could not be sent using Outgoing server (SMTP) %S for an unknown reason. Please verify that your Outgoing server (SMTP) settings are correct and try again.
+
+# LOCALIZATION NOTE (smtpHintAuthEncryptToPlainNoSsl): %S is the server hostname
+smtpHintAuthEncryptToPlainNoSsl=The Outgoing server (SMTP) %S does not seem to support encrypted passwords. If you just set up the account, try changing the 'Authentication method' in 'Account Settings | Outgoing server (SMTP)' to 'Password, transmitted insecurely'. If it used to work but now doesn't, you may be susceptible to getting your password stolen.
+
+# LOCALIZATION NOTE (smtpHintAuthEncryptToPlainSsl): %S is the server hostname
+smtpHintAuthEncryptToPlainSsl=The Outgoing server (SMTP) %S does not seem to support encrypted passwords. If you just set up the account, try changing the 'Authentication method' in 'Account settings | Outgoing server (SMTP)' to 'Normal password'.
+
+# LOCALIZATION NOTE (smtpHintAuthPlainToEncrypt): %S is the server hostname
+smtpHintAuthPlainToEncrypt=The Outgoing server (SMTP) %S does not allow plaintext passwords. Please try changing the 'Authentication method' in 'Account Settings | Outgoing server (SMTP)' to 'Encrypted password'.
+
+# LOCALIZATION NOTE (smtpAuthFailure): %S is the server hostname
+smtpAuthFailure=Unable to authenticate to Outgoing server (SMTP) %S. Please check the password, and verify the 'Authentication method' in 'Account Settings | Outgoing server (SMTP)'.
+
+# LOCALIZATION NOTE (smtpAuthGssapi): %S is the server hostname
+smtpAuthGssapi=The Kerberos/GSSAPI ticket was not accepted by the Outgoing server (SMTP) %S. Please check that you are logged in to the Kerberos/GSSAPI realm.
+
+# LOCALIZATION NOTE (smtpAuthMechNotSupported): %S is the server hostname
+smtpAuthMechNotSupported=The Outgoing server (SMTP) %S does not support the selected authentication method. Please change the 'Authentication method' in 'Account Settings | Outgoing server (SMTP)'.
+
+# LOCALIZATION NOTE (errorIllegalLocalPart2): %s is an email address with an illegal localpart
+errorIllegalLocalPart2=There are non-ASCII characters in the local part of the recipient address %s and your server does not support SMTPUTF8. Please change this address and try again.
+
+## Strings used for the save message dialog shown when the user closes a message compose window
+saveDlogTitle=Save Message
+
+## LOCALIZATION NOTE (saveDlogMessages3): Do not translate the words %1$S and \n.
+## %1$S is replaced by the folder name configured for saving drafts (typically the "Drafts" folder).
+## Translate "Compose" to match the translation of item "windowTitlePrefix" below.
+saveDlogMessages3=Save this message to your drafts folder (%1$S) and close the Compose window?
+discardButtonLabel=&Discard changes
+
+## generics string
+defaultSubject=(no subject)
+chooseFileToAttach=Attach File(s)
+
+##
+windowTitlePrefix=Compose:
+
+## Strings used by the empty subject dialog.
+subjectEmptyTitle=Subject Reminder
+subjectEmptyMessage=Your message doesn't have a subject.
+sendWithEmptySubjectButton=&Send Without Subject
+cancelSendingButton=&Cancel Sending
+
+## Strings used by the dialog that informs about lack of newsgroup support.
+noNewsgroupSupportTitle=Newsgroups Not Supported
+recipientDlogMessage=This account only supports email recipients. Continuing will ignore newsgroups.
+
+## Strings used by the alert that tells the user an e-mail address is invalid.
+addressInvalidTitle=Invalid Recipient Address
+## LOCALIZATION NOTE (addressInvalid): %1$S is the email address
+addressInvalid=%1$S is not a valid e-mail address because it is not of the form user@host. You must correct it before sending the e-mail.
+
+genericFailureExplanation=Please verify that your Mail & Newsgroups account settings are correct and try again.
+
+## LOCALIZATION NOTE (undisclosedRecipients): this string must use only US_ASCII characters
+undisclosedRecipients=undisclosed-recipients
+
+## String used for attachment pretty name, when the attachment is a message
+messageAttachmentSafeName=Attached Message
+## String used for attachment pretty name, when the attachment is a message part
+partAttachmentSafeName=Attached Message Part
+
+## String used by the Initialization Error dialog
+initErrorDlogTitle=Message Compose
+initErrorDlgMessage=An error occurred while creating a message compose window. Please try again.
+
+## String used if the file to attach does not exist when passed as
+## a command line argument
+errorFileAttachTitle=File Attach
+
+## LOCALIZATION NOTE (errorFileAttachMessage): %1$S will be replaced by the non-existent file name.
+errorFileAttachMessage=The file %1$S does not exist so could not be attached to the message.
+
+## String used if a file to serve as message body does not exist or cannot be
+## loaded when passed as a command line argument
+errorFileMessageTitle=Message File
+
+## LOCALIZATION NOTE (errorFileMessageMessage): %1$S will be replaced by the non-existent file name.
+errorFileMessageMessage=The file %1$S does not exist and could not be used as message body.
+
+## LOCALIZATION NOTE (errorLoadFileMessageMessage): %1$S will be replaced by the name of the file that can't be loaded.
+errorLoadFileMessageMessage=The file %1$S could not be loaded as message body.
+
+## Strings used by the Save as Draft/Template dialog
+SaveDialogTitle=Save Message
+
+## LOCALIZATION NOTE (SaveDialogMsg): %1$S is the folder name, %2$S is the host name
+SaveDialogMsg=Your message has been saved to the folder %1$S under %2$S.
+CheckMsg=Do not show me this dialog box again.
+
+## Strings used by the prompt when Quitting while in progress
+quitComposeWindowTitle=Sending Message
+
+## LOCALIZATION NOTE (quitComposeWindowMessage): don't translate \n
+quitComposeWindowMessage2=%1$S is currently in the process of sending a message.\nWould you like to wait until the message has been sent before quitting or quit now?
+quitComposeWindowQuitButtonLabel2=&Quit
+quitComposeWindowWaitButtonLabel2=&Wait
+
+## Strings used by the prompt for Ctrl-Enter check before sending message
+sendMessageCheckWindowTitle=Send Message
+sendMessageCheckLabel=Are you sure you are ready to send this message?
+sendMessageCheckSendButtonLabel=Send
+assemblingMessageDone=Assembling message…Done
+assemblingMessage=Assembling message…
+smtpDeliveringMail=Delivering mail…
+smtpMailSent=Mail sent successfully
+assemblingMailInformation=Assembling mail information…
+
+## LOCALIZATION NOTE (gatheringAttachment): argument %S is the file name/URI of attachment
+gatheringAttachment=Attaching %S…
+creatingMailMessage=Creating mail message…
+
+## LOCALIZATION NOTE (copyMessageStart): argument %S is the folder name
+copyMessageStart=Copying message to %S folder…
+copyMessageComplete=Copy complete.
+copyMessageFailed=Copy failed.
+filterMessageComplete=Filter complete.
+filterMessageFailed=Filter failed.
+
+## LOCALIZATION NOTE (largeMessageSendWarning):
+## %S is the message size in user-friendly notation. Do not translate.
+largeMessageSendWarning=Warning! You are about to send a message of size %S which may exceed the allowed limit on the mail server. Are you sure that you want to do this?
+sendingMessage=Sending message…
+sendMessageErrorTitle=Send Message Error
+postingMessage=Posting message…
+sendLaterErrorTitle=Send Later Error
+saveDraftErrorTitle=Save Draft Error
+saveTemplateErrorTitle=Save Template Error
+
+## LOCALIZATION NOTE (failureOnObjectEmbeddingWhileSaving): argument %.200S is the file name/URI of object to be embedded
+failureOnObjectEmbeddingWhileSaving=There was a problem including the file %.200S in the message. Would you like to continue saving the message without this file?
+
+## LOCALIZATION NOTE (failureOnObjectEmbeddingWhileSending): argument %.200S is the file name/URI of object to be embedded
+failureOnObjectEmbeddingWhileSending=There was a problem including the file %.200S in the message. Would you like to continue sending the message without this file?
+returnToComposeWindowQuestion=Would you like to return to the compose window?
+
+## reply header in composeMsg
+## LOCALIZATION NOTE (mailnews.reply_header_authorwrotesingle): #1 is the author (name of person replying to)
+mailnews.reply_header_authorwrotesingle=#1 wrote:
+
+## LOCALIZATION NOTE (mailnews.reply_header_ondateauthorwrote): #1 is the author, #2 is the date, #3 is the time
+mailnews.reply_header_ondateauthorwrote=On #2 #3, #1 wrote:
+
+## LOCALIZATION NOTE (mailnews.reply_header_authorwroteondate): #1 is the author, #2 is the date, #3 is the time
+mailnews.reply_header_authorwroteondate=#1 wrote on #2 #3:
+
+## reply header in composeMsg
+## user specified
+mailnews.reply_header_originalmessage=-------- Original Message --------
+
+## forwarded header in composeMsg
+## user specified
+mailnews.forward_header_originalmessage=-------- Forwarded Message --------
+
+## Strings used by the rename attachment dialog
+renameAttachmentTitle=Rename Attachment
+renameAttachmentMessage=New attachment name:
+
+## LOCALIZATION NOTE(smtpEnterPasswordPrompt): Do not translate the
+## word %S. Place the word %S where the host name should appear.
+smtpEnterPasswordPrompt=Enter your password for %S:
+
+## LOCALIZATION NOTE(smtpEnterPasswordPromptWithUsername): Do not translate the
+## words %1$S and %2$S. Place the word %1$S where the host name should appear,
+## and %2$S where the user name should appear.
+smtpEnterPasswordPromptWithUsername=Enter your password for %2$S on %1$S:
+## LOCALIZATION NOTE(smtpEnterPasswordPromptTitleWithHostname): Do not translate the
+## word %1$S. Place the word %1$S where the server host name should appear.
+smtpEnterPasswordPromptTitleWithHostname=Password Required for Outgoing (SMTP) Server %1$S
+
+## LOCALIZATION NOTE(promptToSaveSentLocally2): Do not translate the strings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved sent messages (typically the "Sent" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveSentLocally2=Your message was sent but a copy was not placed in your sent folder (%1$S) due to network or file access errors.\nYou can retry or save the message locally to %3$S/%1$S-%2$S.
+errorFilteringMsg=Your message has been sent and saved, but there was an error while running message filters on it.
+
+## LOCALIZATION NOTE(promptToSaveDraftLocally2): Do not translate the strings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved draft messages (typically the "Drafts" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveDraftLocally2=Your draft message was not copied to your drafts folder (%1$S) due to network or file access errors.\nYou can retry or save the draft locally to %3$S/%1$S-%2$S.
+buttonLabelRetry2=&Retry
+
+## LOCALIZATION NOTE(promptToSaveTemplateLocally2): Do not translate the strings %1$S, %2$S, %3$S and \n.
+## %2$S will be replaced with the account name. $1$S will be replaced by the folder name
+## configured to contain saved templates (typically the "Templates" folder).
+## %3$S will be replaced with the local folders account name (typically "Local Folders").
+promptToSaveTemplateLocally2=Your template was not copied to your templates folder (%1$S) due to network or file access errors.\nYou can retry or save the template locally to %3$S/%1$S-%2$S.
+
+## LOCALIZATION NOTE(saveToLocalFoldersFailed): Message appears after normal
+## save fails (e.g., to Sent) and save to Local Folders also fails. This could
+## occur if network is down and filesystem problems are present such as disk
+## full, permission issues or hardware failure.
+saveToLocalFoldersFailed=Unable to save your message to local folders. Possibly out of file storage space.
+
+## LOCALIZATION NOTE(blockedAllowResource): %S is the URL to load.
+blockedAllowResource=Unblock %S
+## LOCALIZATION NOTE (blockedContentMessage): Semi-colon list of plural forms.
+## See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+## %S will be replaced by brandShortName.
+## Files must be unblocked individually, therefore the plural form reads:
+## Unblocking a file (one of several) will include it (that one file) in your sent message.
+## In other words:
+## Unblocking one/several file(s) will include it/them in your message.
+blockedContentMessage=%S has blocked a file from loading into this message. Unblocking the file will include it in your sent message.;%S has blocked some files from loading into this message. Unblocking a file will include it in your sent message.
+
+## LOCALIZATION NOTE (blockedContentPrefLabel, blockedContentPrefAccesskey):
+## Same content as (blockedContentPrefLabel, blockedContentPrefAccesskey)
+## in mail directory. SeaMonkey does only use Options and not Preferences.
+blockedContentPrefLabel=Options
+blockedContentPrefAccesskey=O
+
+## Identity matching warning notification bar.
+## LOCALIZATION NOTE(identityWarning): %S will be replaced with the identity name.
+identityWarning=A unique identity matching the From address was not found. The message will be sent using the current From field and settings from identity %S.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/mailComposeEditorOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/compose/mailComposeEditorOverlay.dtd
new file mode 100755
index 0000000000..e367a329b0
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/mailComposeEditorOverlay.dtd
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY attachImageSource.label "Attach this image to the message">
+<!ENTITY attachImageSource.accesskey "s">
+
+<!ENTITY attachLinkSource.label "Attach the source of this link to the message">
+<!ENTITY attachLinkSource.accesskey "s">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/messengercompose.dtd b/comm/suite/locales/en-US/chrome/mailnews/compose/messengercompose.dtd
new file mode 100644
index 0000000000..88fa068d04
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/messengercompose.dtd
@@ -0,0 +1,137 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE messengercompose.dtd Main UI for message composition -->
+<!ENTITY msgComposeWindow.title "Compose: (no subject)">
+
+<!ENTITY fromAddr.label "From:">
+<!ENTITY fromAddr.accesskey "r">
+<!ENTITY toAddr.label "To:">
+<!ENTITY ccAddr.label "Cc:">
+<!ENTITY bccAddr.label "Bcc:">
+<!ENTITY replyAddr.label "Reply-To:">
+<!ENTITY newsgroupsAddr.label "Newsgroup:">
+<!ENTITY followupAddr.label "Followup-To:">
+<!ENTITY subject.label "Subject:">
+<!ENTITY subject.accesskey "S">
+<!ENTITY attachments.label "Attachments:">
+<!ENTITY attachments.accesskey "c">
+
+<!-- menu items: the . means that the menu item isn't implemented yet -->
+
+<!-- File menu items -->
+<!ENTITY saveCmd.label "Save">
+<!ENTITY saveCmd.accesskey "s">
+<!ENTITY saveAsCmd.label "Save As">
+<!ENTITY saveAsCmd.accesskey "A">
+<!ENTITY saveAsFileCmd.label "File…">
+<!ENTITY saveAsFileCmd.accesskey "F">
+<!ENTITY saveAsDraftCmd.label "Draft">
+<!ENTITY saveAsDraftCmd.accesskey "D">
+<!ENTITY saveAsTemplateCmd.label "Template">
+<!ENTITY saveAsTemplateCmd.accesskey "T">
+<!ENTITY attachMenu.label "Attach">
+<!ENTITY attachMenu.accesskey "t">
+<!ENTITY attachFileCmd.label "File(s)…">
+<!ENTITY attachFileCmd.accesskey "F">
+<!ENTITY attachPageCmd.label "Web Page…">
+<!ENTITY attachPageCmd.accesskey "W">
+<!--LOCALIZATION NOTE attachVCardCmd.label Don't translate the term 'vCard' -->
+<!ENTITY attachVCardCmd.label "Personal Card (vCard)">
+<!ENTITY attachVCardCmd.accesskey "P">
+<!ENTITY sendNowCmd.label "Send Now">
+<!ENTITY sendCmd.keycode "VK_RETURN">
+<!ENTITY sendNowCmd.accesskey "d">
+<!ENTITY sendLaterCmd.label "Send Later">
+<!ENTITY sendLaterCmd.keycode "VK_RETURN">
+<!ENTITY sendLaterCmd.accesskey "L">
+
+<!-- Edit menu items -->
+<!ENTITY accountManagerCmd.label "Mail &amp; Newsgroups Account Settings…">
+<!ENTITY accountManagerCmd.accesskey "m">
+
+<!-- View menu items -->
+<!ENTITY showComposeToolbarCmd.label "Mail Toolbar">
+<!ENTITY showComposeToolbarCmd.accesskey "o">
+<!ENTITY showFormatToolbarCmd.label "Formatting Toolbar">
+<!ENTITY showFormatToolbarCmd.accesskey "F">
+
+<!-- Format menu items -->
+<!ENTITY formatMenu.label "Format">
+<!ENTITY formatMenu.accesskey "o">
+
+<!-- Options menu items -->
+<!ENTITY optionsMenu.label "Options">
+<!ENTITY optionsMenu.accesskey "p">
+
+<!ENTITY quoteCmd.label "Quote Message">
+<!ENTITY quoteCmd.accesskey "Q">
+<!ENTITY selectAddressCmd.label "Select Addresses…">
+<!ENTITY selectAddressCmd.key "">
+<!ENTITY selectAddressCmd.accesskey "A">
+
+<!ENTITY priorityMenu.label "Priority">
+<!ENTITY priorityMenu.accesskey "p">
+<!ENTITY lowestPriorityCmd.label "Lowest">
+<!ENTITY lowestPriorityCmd.accesskey "l">
+<!ENTITY lowPriorityCmd.label "Low">
+<!ENTITY lowPriorityCmd.accesskey "o">
+<!ENTITY normalPriorityCmd.label "Normal">
+<!ENTITY normalPriorityCmd.accesskey "n">
+<!ENTITY highPriorityCmd.label "High">
+<!ENTITY highPriorityCmd.accesskey "i">
+<!ENTITY highestPriorityCmd.label "Highest">
+<!ENTITY highestPriorityCmd.accesskey "H">
+
+<!ENTITY returnReceiptMenu.label "Return Receipt">
+<!ENTITY returnReceiptMenu.accesskey "t">
+<!ENTITY dsnMenu.label "Delivery Status Notification">
+<!ENTITY dsnMenu.accesskey "N">
+
+<!ENTITY outputFormatMenu.label "Format">
+<!ENTITY outputFormatMenu.accesskey "f">
+<!ENTITY autoFormatCmd.label "Auto-Detect">
+<!ENTITY autoFormatCmd.accesskey "a">
+<!ENTITY plainTextFormatCmd.label "Plain Text Only">
+<!ENTITY plainTextFormatCmd.accesskey "p">
+<!ENTITY htmlFormatCmd.label "Rich Text (HTML) Only">
+<!ENTITY htmlFormatCmd.accesskey "r">
+<!ENTITY bothFormatCmd.label "Plain and Rich (HTML) Text">
+<!ENTITY bothFormatCmd.accesskey "l">
+
+<!ENTITY fileCarbonCopyCmd.label "Send a Copy To">
+<!ENTITY fileCarbonCopyCmd.accesskey "d">
+<!ENTITY fileHereMenu.label "File Here">
+
+<!--LOCALIZATION NOTE Toolbar items Don't change any "chrome://" URLs -->
+<!-- Toolbar items -->
+<!ENTITY sendButton.label "Send">
+<!ENTITY addressButton.label "Address">
+<!ENTITY attachButton.label "Attach">
+<!ENTITY spellingButton.label "Spelling">
+<!ENTITY saveButton.label "Save">
+<!ENTITY stopButton.label "Stop">
+
+<!--tooltips-->
+<!-- We already inherit &menuBar.tooltip and &mailToolbar.tooltip from messenger.dtd -->
+<!ENTITY addressBar.tooltip "Address Bar">
+<!ENTITY formatToolbar.tooltip "Formatting Toolbar">
+<!ENTITY sendButton.tooltip "Send this message now">
+<!ENTITY sendlaterButton.tooltip "Send this message later">
+<!ENTITY addressButton.tooltip "Select a recipient from an Address Book">
+<!ENTITY attachButton.tooltip "Include an attachment">
+<!ENTITY saveButton.tooltip "Save this message">
+<!ENTITY stopButton.tooltip "Stop the current transfer">
+
+<!-- context menu items -->
+<!ENTITY openAttachment.label "Open">
+<!ENTITY openAttachment.accesskey "O">
+<!ENTITY renameAttachment.label "Rename…">
+<!ENTITY renameAttachment.accesskey "R">
+<!ENTITY deleteAttachment.accesskey "D">
+<!ENTITY selectAllAttachments.accesskey "A">
+<!ENTITY attachFile.label "Attach File(s)…">
+<!ENTITY attachFile.accesskey "F">
+<!ENTITY attachPage.label "Attach Web Page…">
+<!ENTITY attachPage.accesskey "W">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.dtd b/comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.dtd
new file mode 100644
index 0000000000..d6e3939055
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!--LOCALIZATION NOTE sendprogress.dtd Main UI for Send Message Progress Dialog -->
+<!ENTITY sendDialog.title "Processing Message">
+<!ENTITY status.label "Status:">
+<!ENTITY progress.label "Progress:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.properties b/comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.properties
new file mode 100644
index 0000000000..61799efb37
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/compose/sendProgress.properties
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# LOCALIZATION NOTE (titleSendMsgSubject):
+# %S will be replaced by the message subject.
+titleSendMsgSubject=Sending Message - %S
+titleSendMsg=Sending Message
+# LOCALIZATION NOTE (titleSaveMsgSubject):
+# %S will be replaced by the message subject.
+titleSaveMsgSubject=Saving Message - %S
+titleSaveMsg=Saving Message
+
+# LOCALIZATION NOTE (percentMsg):
+# This string is used to format the text to the right of the progress meter.
+# %S will be replaced by the percentage of the file that has been saved.
+# %% will be replaced a single % sign.
+percentMsg=%S%%
+
+messageSent=Your message has been sent.
+messageSaved=Your message has been saved.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/custom.properties b/comm/suite/locales/en-US/chrome/mailnews/custom.properties
new file mode 100644
index 0000000000..f51faa3a68
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/custom.properties
@@ -0,0 +1,5 @@
+# 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/.
+
+colonInHeaderName=The header you entered contains an invalid character, such as ':', a non-printable character, a non-ascii character, or an eight bit ascii character. Please remove the invalid character and try again.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/downloadheaders.dtd b/comm/suite/locales/en-US/chrome/mailnews/downloadheaders.dtd
new file mode 100644
index 0000000000..405c2950bb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/downloadheaders.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!ENTITY all.label "Download all headers">
+<!ENTITY all.accesskey "D">
+<!--LOCALIZATION NOTE (download.label):
+ consider the download.label and headers.label as a single sentence
+ with the number of headers to be downloaded inserted between them:
+ EXAMPLE: "Download" <some number> "headers"
+ Either label could be set to null ("") if required grammatically.
+-->
+
+<!ENTITY download.label "Download">
+<!ENTITY download.accesskey "o">
+<!--LOCALIZATION NOTE (headers.label): see note for download.label -->
+<!ENTITY headers.label "headers">
+<!ENTITY headers.accesskey "h">
+<!ENTITY mark.label "Mark remaining headers as read">
+<!ENTITY mark.accesskey "M">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/fieldMapImport.dtd b/comm/suite/locales/en-US/chrome/mailnews/fieldMapImport.dtd
new file mode 100644
index 0000000000..561d7bb0cc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/fieldMapImport.dtd
@@ -0,0 +1,19 @@
+<!-- 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/. -->
+
+<!ENTITY fieldMapImport.title "Import Address Book">
+<!ENTITY fieldMapImport.recordNumber "Imported data for Record: ">
+<!ENTITY fieldMapImport.next.label "Next">
+<!ENTITY fieldMapImport.next.accesskey "N">
+<!ENTITY fieldMapImport.previous.label "Previous">
+<!ENTITY fieldMapImport.previous.accesskey "P">
+<!ENTITY fieldMapImport.text "Use Move Up and Move Down to match the address book fields on the left to the correct data for import on the right. Uncheck items you do not want to import.">
+<!ENTITY fieldMapImport.up.label "Move Up">
+<!ENTITY fieldMapImport.up.accesskey "U">
+<!ENTITY fieldMapImport.down.label "Move Down">
+<!ENTITY fieldMapImport.down.accesskey "D">
+<!ENTITY fieldMapImport.fieldListTitle "Address Book fields">
+<!ENTITY fieldMapImport.dataTitle "Record data to import">
+<!ENTITY fieldMapImport.skipFirstRecord.label "First record contains field names">
+<!ENTITY fieldMapImport.skipFirstRecord.accessKey "F">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/filter.properties b/comm/suite/locales/en-US/chrome/mailnews/filter.properties
new file mode 100644
index 0000000000..b1dd387d92
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/filter.properties
@@ -0,0 +1,111 @@
+# 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/.
+
+mustSelectFolder=You must select a target folder.
+enterValidEmailAddress=Enter a valid e-mail address to forward to.
+pickTemplateToReplyWith=Choose a template to reply with.
+mustEnterName=You must give this filter a name.
+cannotHaveDuplicateFilterTitle=Duplicate Filter Name
+cannotHaveDuplicateFilterMessage=The filter name you entered already exists. Please enter a different filter name.
+mustHaveFilterTypeTitle=No filter event selected
+mustHaveFilterTypeMessage=You must select at least one event when this filter is applied. If you temporarily do not wish the filter to run at any event, uncheck its enabled state from the Message Filters dialog.
+deleteFilterConfirmation=Are you sure you want to delete the selected filter(s)?
+untitledFilterName=Untitled Filter
+matchAllFilterName=Match All Messages
+filterListBackUpMsg=Your filters do not work because the msgFilterRules.dat file, which contains your filters, could not be read. A new msgFilterRules.dat file will be created and a backup of the old file, called rulesbackup.dat, will be created in the same directory.
+customHeaderOverflow=You've exceeded the limit of 50 custom headers. Please remove one or more custom headers and try again.
+filterCustomHeaderOverflow=Your filters have exceeded the limit of 50 custom headers. Please edit the msgFilterRules.dat file, which contains your filters, to use fewer custom headers.
+invalidCustomHeader=One of your filters uses a custom header that contains an invalid character, such as ':', a non-printable character, a non-ascii character, or an eight-bit ascii character. Please edit the msgFilterRules.dat file, which contains your filters, to remove invalid characters from your custom headers.
+continueFilterExecution=Applying filter %S failed. Would you like to continue applying filters?
+promptTitle=Running Filters
+promptMsg=You are currently in the process of filtering messages.\nWould you like to continue applying filters?
+stopButtonLabel=Stop
+continueButtonLabel=Continue
+# LOCALIZATION NOTE(cannotEnableIncompatFilter)
+# %S=the name of the application
+cannotEnableIncompatFilter=This filter was probably created by a newer or incompatible version of %S. You cannot enable this filter because we don't know how to apply it.
+dontWarnAboutDeleteCheckbox=&Don't ask me again
+
+# LOCALIZATION NOTE(copyToNewFilterName)
+# %S=the name of the filter that is being copied
+copyToNewFilterName=Copy of %S
+
+# LOCALIZATION NOTE(contextPeriodic.label): Semi-colon list of plural forms.
+# #1=the number of minutes
+contextPeriodic.label=Periodically, every minute;Periodically, every #1 minutes
+# LOCALIZATION NOTE(filterFailureWarningPrefix)
+# %1$S=filter error action
+# %2$S=error code as hexadecimal string.
+filterFailureWarningPrefix=Filter action failed: "%1$S" with error code=%2$S while attempting:
+filterFailureSendingReplyError=Error sending reply
+filterFailureSendingReplyAborted=Sending reply aborted
+filterFailureMoveFailed=Move failed
+filterFailureCopyFailed=Copy failed
+filterFailureAction=Failed applying the filter action
+
+searchTermsInvalidTitle=Search Terms Invalid
+# LOCALIZATION NOTE(searchTermsInvalidRule)
+# %1$S=search attribute name from the invalid rule
+# %2$S=search operator from the bad rule
+searchTermsInvalidRule=This filter cannot be saved because the search term "%1$S %2$S" is invalid in the current context.
+# LOCALIZATION NOTE(filterActionOrderExplanation)
+# Keep the \n\n that mean 2 linebreaks.
+filterActionOrderExplanation=When a message matches this filter the actions will be run in this order:\n\n
+filterActionOrderTitle=Real action order
+## LOCALIZATION NOTE(filterActionItem):
+# %1$S=sequence number of the action, %2$S=action text, %3$S=action argument
+filterActionItem=%1$S. %2$S %3$S\n
+
+## LOCALIZATION NOTE(filterCountVisibleOfTotal):
+# %1$S=number of matching filters, %2$S=total number of filters
+filterCountVisibleOfTotal=%1$S of %2$S
+## LOCALIZATION NOTE(filterCountItems):
+## Semicolon-separated list of singular and plural forms.
+## See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+## #1 is the count of items in the list.
+filterCountItems=#1 item; #1 items
+
+# for junk mail logging / mail filter logging
+# LOCALIZATION NOTE(junkLogDetectStr)
+# %1$S=author, %2$S=subject, %3$S=date
+junkLogDetectStr=Detected junk message from %1$S - %2$S at %3$S
+# LOCALIZATION NOTE(logMoveStr)
+# %1$S=message id, %2$S=folder URI
+logMoveStr=moved message id = %1$S to %2$S
+# LOCALIZATION NOTE(logCopyStr)
+# %1$S=message id, %2$S=folder URI
+logCopyStr=copied message id = %1$S to %2$S
+# LOCALIZATION NOTE(filterLogLine):
+# %1$S=timestamp %2$S=log message
+filterLogLine=[%1$S] %2$S
+# LOCALIZATION NOTE(filterMessage):
+# %1$S=filter name, %1$S=log message
+filterMessage=Message from filter "%1$S": %2$S
+# LOCALIZATION NOTE(filterLogDetectStr)
+# %1$S=filter name %2$S=author, %3$S=subject, %4$S=date
+filterLogDetectStr=Applied filter "%1$S" to message from %2$S - %3$S at %4$S
+filterMissingCustomAction=Missing Custom Action
+filterAction2=priority changed
+filterAction3=deleted
+filterAction4=marked as read
+filterAction5=thread killed
+filterAction6=thread watched
+filterAction7=flagged
+filterAction8=tagged
+filterAction9=replied
+filterAction10=forwarded
+filterAction11=execution stopped
+filterAction12=deleted from POP3 server
+filterAction13=left on POP3 server
+filterAction14=junk score
+filterAction15=body fetched from POP3 server
+filterAction16=copied to folder
+filterAction17=tagged
+filterAction18=ignored subthread
+filterAction19=marked as unread
+# LOCALIZATION NOTE(filterAutoNameStr)
+# %1$S=Header or item to match, e.g. "From", "Tag", "Age in days", etc.
+# %2$S=Operator, e.g. "Contains", "is", "is greater than", etc.
+# %3$S=Value, e.g. "Steve Jobs", "Important", "42", etc.
+filterAutoNameStr=%1$S %2$S: %3$S
diff --git a/comm/suite/locales/en-US/chrome/mailnews/folderProps.dtd b/comm/suite/locales/en-US/chrome/mailnews/folderProps.dtd
new file mode 100644
index 0000000000..e7df4d0c5c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/folderProps.dtd
@@ -0,0 +1,70 @@
+<!-- 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/. -->
+
+<!ENTITY folderProps.windowtitle.label "Properties">
+
+<!ENTITY generalInfo.label "General Information">
+<!ENTITY folderCharsetFallback2.label "Fallback Text Encoding:">
+<!ENTITY folderCharsetFallback2.accesskey "E">
+<!ENTITY folderCharsetEnforce2.label "Apply encoding to all messages in the folder (individual message text encoding settings and auto-detection will be ignored)">
+<!ENTITY folderCharsetEnforce2.accesskey "A">
+<!ENTITY folderRebuildSummaryFileTip2.label "Rebuild Summary File Index">
+<!ENTITY folderRebuildSummaryFile2.label "Repair Folder">
+<!ENTITY folderRebuildSummaryFile2.accesskey "R">
+<!ENTITY folderRebuildSummaryFile.explanation "Sometimes the folder index (.msf) file becomes damaged and messages may appear missing or deleted messages continue showing; repairing the folder may fix these issues.">
+<!ENTITY folderIncludeInGlobalSearch.label "Include messages in this folder in Global Search results">
+<!ENTITY folderIncludeInGlobalSearch.accesskey "G">
+
+<!ENTITY retention.label "Retention Policy">
+<!ENTITY retentionUseAccount.label "Use my account settings">
+<!ENTITY retentionUseAccount.accesskey "U">
+<!ENTITY daysOld.label "days old">
+<!ENTITY message.label "messages">
+<!ENTITY retentionCleanup.label "Keep messages:">
+<!ENTITY retentionCleanupImap.label "Keep messages, both the local copies and their originals on the server:">
+<!ENTITY retentionCleanupPop.label "Keep messages, including their originals on the server:">
+<!ENTITY retentionDeleteMsg.label "Delete messages more than">
+<!ENTITY retentionDeleteMsg.accesskey "m">
+<!ENTITY retentionKeepAll.label "All messages">
+<!ENTITY retentionKeepAll.accesskey "A">
+<!ENTITY retentionKeepRecent.label "The newest">
+<!ENTITY retentionKeepRecent.accesskey "n">
+<!ENTITY retentionApplyToFlagged.label "Always keep flagged messages">
+<!ENTITY retentionApplyToFlagged.accesskey "e">
+
+<!ENTITY folderSynchronizationTab.label "Synchronization">
+<!ENTITY folderCheckForNewMessages2.label "When getting new messages for this account, always check this folder">
+<!ENTITY folderCheckForNewMessages2.accesskey "c">
+
+<!ENTITY offlineFolder.check.label "Select this folder for offline use">
+<!ENTITY offlineFolder.check.accesskey "S">
+<!ENTITY offlineFolder.button.label "Download Now">
+<!ENTITY offlineFolder.button.accesskey "D">
+
+<!ENTITY selectofflineNewsgroup.check.label "Select this newsgroup for offline use">
+<!ENTITY selectofflineNewsgroup.check.accesskey "o">
+<!ENTITY offlineNewsgroup.button.label "Download Now">
+<!ENTITY offlineNewsgroup.button.accesskey "D">
+
+<!ENTITY folderProps.name.label "Name:">
+<!ENTITY folderProps.name.accesskey "N">
+<!ENTITY folderProps.location.label "Location:">
+<!ENTITY folderProps.location.accesskey "L">
+
+<!ENTITY folderSharingTab.label "Sharing">
+<!ENTITY privileges.button.label "Privileges…">
+<!ENTITY privileges.button.accesskey "P">
+<!ENTITY permissionsDesc.label "You have the following permissions:">
+<!ENTITY folderType.label "Folder Type:">
+
+<!ENTITY folderQuotaTab.label "Quota">
+<!ENTITY folderQuotaUsage.label "Usage:">
+<!ENTITY folderQuotaStatus.label "Status:">
+
+<!ENTITY numberOfMessages.label "Number of messages:">
+<!-- LOCALIZATION NOTE: When the number of messages can't be determined, this string is displayed as the number -->
+<!ENTITY numberUnknown.label "unknown">
+<!ENTITY sizeOnDisk.label "Size on disk:">
+<!-- LOCALIZATION NOTE: When the size can't be determined, this string is displayed as the size -->
+<!ENTITY sizeUnknown.label "unknown">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/folderWidgets.properties b/comm/suite/locales/en-US/chrome/mailnews/folderWidgets.properties
new file mode 100644
index 0000000000..82465951dd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/folderWidgets.properties
@@ -0,0 +1,12 @@
+# 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/.
+
+# LOCALIZATION NOTE(globalInbox)
+# %S=name of the Local folders account
+globalInbox=Global Inbox (%S)
+# LOCALIZATION NOTE(verboseFolderFormat): %1$S is folder name, %2$S is server name
+verboseFolderFormat=%1$S on %2$S
+chooseFolder=Choose Folder…
+chooseAccount=Choose Account…
+noFolders=No available folders
diff --git a/comm/suite/locales/en-US/chrome/mailnews/folderpane.dtd b/comm/suite/locales/en-US/chrome/mailnews/folderpane.dtd
new file mode 100644
index 0000000000..20d6bb2527
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/folderpane.dtd
@@ -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/. -->
+<!ENTITY nameColumn.label "Name">
+<!ENTITY unreadColumn.label "Unread">
+<!ENTITY totalColumn.label "Total">
+<!ENTITY folderSizeColumn.label "Size">
+<!ENTITY folderLocationToolbarItem.title "Folder Location">
+<!ENTITY mailViewsToolbarItem.title "Mail Views">
+<!ENTITY searchToolbarItem.title "Search">
+<!ENTITY searchSubjectOrAddress.placeholder "Search Subject or Address">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/gloda.properties b/comm/suite/locales/en-US/chrome/mailnews/gloda.properties
new file mode 100644
index 0000000000..f5ff7a34bb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/gloda.properties
@@ -0,0 +1,104 @@
+# 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/.
+
+# LOCALIZATION NOTE (*.facetLabel): These are the labels used to label the facet
+# displays in the global search facet display mechanism.
+
+# LOCALIZATION NOTE (*.includeLabel): The label to use for the included group
+# in the facet display. If not provided, we will fall back to
+# "glodaFacetView.facets.included.fallbackLabel".
+
+# LOCALIZATION NOTE (*.excludeLabel): The label to use for the excluded group
+# in the facet display. If not provided, we will fall back to
+# "glodaFacetView.facets.excluded.fallbackLabel".
+
+# LOCALIZATION NOTE (*.remainderLabel): The label to use for the remaining items
+# that are neither part of the included group or the excluded group in the
+# facet display. If not provided, we will fall back to
+# "glodaFacetView.facets.remainder.fallbackLabel".
+
+# LOCALIZATION NOTE (gloda.message.attr.folder.*): Stores the message folder in
+# which the message is stored.
+gloda.message.attr.folder.facetLabel=Mail Folder
+
+# LOCALIZATION NOTE (gloda.message.attr.fromMe.*): Stores everyone involved
+# with the message. This means from/to/cc/bcc.
+gloda.message.attr.fromMe.facetLabel=From Me
+
+# LOCALIZATION NOTE (gloda.message.attr.toMe.*): Stores everyone involved
+# with the message. This means from/to/cc/bcc.
+gloda.message.attr.toMe.facetLabel=To Me
+
+# LOCALIZATION NOTE (gloda.message.attr.involves.*): Stores everyone involved
+# with the message. This means from/to/cc/bcc.
+gloda.message.attr.involves.facetLabel=People
+gloda.message.attr.involves.includeLabel=involving any of:
+gloda.message.attr.involves.excludeLabel=not involving:
+gloda.message.attr.involves.remainderLabel=other participants:
+
+# LOCALIZATION NOTE (gloda.message.attr.date.*): Stores the date of the message.
+# SeaMonkey normally stores the date the message claims it was composed
+# according to the "Date" header. This is not the same as when the message
+# was sent or when it was eventually received by the user. In the future we
+# may change this to be one of the other dates, but not anytime soon.
+gloda.message.attr.date.facetLabel=Date
+
+# LOCALIZATION NOTE (gloda.message.attr.attachmentTypes.*): Stores the list of
+# MIME types (ex: image/png, text/plain) of real attachments (not just part of
+# the message content but explicitly named attachments) on the message.
+# Although we hope to be able to provide localized human-readable explanations
+# of the MIME type (ex: "PowerPoint document"), I don't know if that is going
+# to happen.
+gloda.message.attr.attachmentTypes.facetLabel=Attachments
+
+# LOCALIZATION NOTE (gloda.message.attr.mailing-list.*): Stores the mailing
+# lists detected in the message. This will normally be the e-mail address of
+# the mailing list and only be detected in messages received from the mailing
+# list. Extensions may contribute additional detected mailing-list-like
+# things.
+gloda.message.attr.mailing-list.facetLabel=Mail List Involved
+
+# LOCALIZATION NOTE (gloda.message.attr.tag.*): Stores the tags applied to the
+# message. Notably, gmail's labels are not currently exposed via IMAP and we
+# do not do anything clever with gmail, so this is indepdendent of gmail
+# labels. This may change in the future, but it's a safe bet it's not
+# happening on SeaMonkey's side prior to 2.0.
+gloda.message.attr.tag.facetLabel=Tags
+
+# LOCALIZATION NOTE (gloda.message.attr.star.*): Stores whether the message is
+# flagged or not, as indicated by a pretty flag icon.
+# Thunderbird uses a star.
+gloda.message.attr.star.facetLabel=Flagged
+
+# LOCALIZATION NOTE (gloda.message.attr.read.*): Stores whether the user has
+# read the message or not.
+gloda.message.attr.read.facetLabel=Read
+
+# LOCALIZATION NOTE (gloda.message.attr.repliedTo.*): Stores whether we believe
+# the user has ever replied to the message. We normally show a little icon in
+# the thread pane when this is the case.
+gloda.message.attr.repliedTo.facetLabel=Replied
+
+# LOCALIZATION NOTE (gloda.message.attr.forwarded.*): Stores whether we believe
+# the user has ever forwarded the message. We normally show a little icon in
+# the thread pane when this is the case.
+gloda.message.attr.forwarded.facetLabel=Forwarded
+
+# LOCALIZATION NOTE (gloda.mimetype.category.*.label): Map categories of MIME
+# types defined in mimeTypeCategories.js to labels.
+# LOCALIZATION NOTE (gloda.mimetype.category.archives.label): Archive is
+# referring to things like zip files, tar files, tar.gz files, etc.
+gloda.mimetype.category.archives.label=Archives
+gloda.mimetype.category.documents.label=Documents
+gloda.mimetype.category.images.label=Images
+# LOCALIZATION NOTE (gloda.mimetype.category.media.label): Media is meant to
+# encompass both audio and video. This is because video and audio streams are
+# frequently stored in the same type of container and we cannot rely on the
+# sending e-mail client to have been clever enough to figure out what was
+# really in the file. So we group them together.
+gloda.mimetype.category.media.label=Media (Audio, Video)
+gloda.mimetype.category.pdf.label=PDF Files
+# LOCALIZATION NOTE (gloda.mimetype.category.other.label): Other is the category
+# for MIME types that we don't really know what it is.
+gloda.mimetype.category.other.label=Other
diff --git a/comm/suite/locales/en-US/chrome/mailnews/imapMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/imapMsgs.properties
new file mode 100644
index 0000000000..ec365e6567
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/imapMsgs.properties
@@ -0,0 +1,271 @@
+# 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/.
+
+#
+# The following are used by the imap code to display progress/status/error messages
+#
+
+#LOCALIZATION NOTE(imapAlertDialogTile): Do not translate the word "%S"
+# below. Place the word %S where the account name should appear.
+imapAlertDialogTitle=Alert for account %S
+
+# Status - opening folder
+imapStatusSelectingMailbox=Opening folder %S…
+
+# Status - create folder
+imapStatusCreatingMailbox=Creating folder…
+
+# Status - deleting a folder
+# LOCALIZATION NOTE (imapStatusDeletingMailbox): The "%S" below should not be translated.
+# Instead, insert "%S" in your translation where you wish to display the name
+# of the folder being deleted.
+imapStatusDeletingMailbox=Deleting folder %S…
+
+# Status - renaming mailbox
+# LOCALIZATION NOTE (imapStatusRenamingMailbox): The "%S" below should not be translated.
+# Instead, insert "%S" in your translation where you wish to display the name
+# of the folder being renamed.
+imapStatusRenamingMailbox=Renaming folder %S…
+
+# Status - looking for mailboxes
+imapStatusLookingForMailbox=Looking for folders…
+
+# Status - subscribing to mailbox
+# LOCALIZATION NOTE (imapStatusSubscribeToMailbox): The "%S" below should not be translated.
+# Instead, insert "%S" in your translation where you wish to display the name
+# of the folder being subscribed to.
+imapStatusSubscribeToMailbox=Subscribing to folder %S…
+
+# Status - unsubscribing from mailbox
+# LOCALIZATION NOTE (imapStatusUnsubscribeMailbox): The "%S" below should not be translated.
+# Instead, insert "%S" in your translation where you wish to display the name
+# of the folder being unsubscribed from.
+imapStatusUnsubscribeMailbox=Unsubscribing from folder %S…
+
+# Status - searching imap folder
+imapStatusSearchMailbox=Searching folder…
+
+# Status - closing a folder
+imapStatusCloseMailbox=Closing folder…
+
+# Status - compacting a folder
+imapStatusExpungingMailbox=Compacting folder…
+
+# Status - logging out
+imapStatusLoggingOut=Logging out…
+
+# Status - checking server capabilities
+imapStatusCheckCompat=Checking mail server capabilities…
+
+# Status - logging on
+imapStatusSendingLogin=Sending login information…
+
+# Status - auth logon
+imapStatusSendingAuthLogin=Sending login information…
+
+# Status - downloading message
+imapDownloadingMessage=Downloading message…
+
+# Status - getting acl for folder
+# LOCALIZATION NOTE (imapGettingACLForFolder): Do not translate the word "ACL" below.
+imapGettingACLForFolder=Getting folder ACL…
+
+# Status - getting server info
+imapGettingServerInfo=Getting Server Configuration Info…
+
+# Status - getting mailbox info
+imapGettingMailboxInfo=Getting Mailbox Configuration Info…
+
+# Status - empty mime part
+imapEmptyMimePart=This body part will be downloaded on demand.
+
+# LOCALIZATION NOTE (imapReceivingMessageHeaders3): Do not translate the words "%1$S", "%2$S", and "%3$S" below.
+# Place the word %1$S in your translation where the number of the header being downloaded should appear.
+# Place the word %2$S in your translation where the total number of headers to be downloaded should appear.
+# Place the word %3$S in your translation where the name of the folder being processed should appear.
+# Note: The account name and separators (e.g. colon, space) are automatically added to the status message.
+# Example: "Joe's Account: Downloading message header 100 of 1000 in Drafts…"
+imapReceivingMessageHeaders3=Downloading message header %1$S of %2$S in %3$S…
+
+# LOCALIZATION NOTE (imapReceivingMessageFlags3): Do not translate the words "%1$S", "%2$S", and "%3$S" below.
+# Place the word %1$S in your translation where the number of the flag being downloaded should appear.
+# Place the word %2$S in your translation where the total number of flags to be downloaded should appear.
+# Place the word %3$S in your translation where the name of the folder being processed should appear.
+# Note: The account name and separators (e.g. colon, space) are automatically added to the status message.
+# Example: "Jim's Account: Downloading message flag 100 of 1000 in INBOX…"
+imapReceivingMessageFlags3=Downloading message flag %1$S of %2$S in %3$S…
+
+imapDeletingMessages=Deleting messages…
+
+imapDeletingMessage=Deleting message…
+
+# LOCALIZATION NOTE (imapMovingMessages): Do not translate the word "%S" below.
+# Place the word %S in your translation where the name of the folder should appear.
+imapMovingMessages=Moving messages to %S…
+
+# LOCALIZATION NOTE (imapMovingMessage): Do not translate the word "%S" below.
+# Place the word %S in your translation where the name of the folder should appear.
+imapMovingMessage=Moving message to %S…
+
+# LOCALIZATION NOTE (imapCopyingMessages): Do not translate the word "%S" below.
+# Place the word %S in your translation where the name of the folder should appear.
+imapCopyingMessages=Copying messages to %S…
+
+# LOCALIZATION NOTE (imapCopyingMessage): Do not translate the word "%S" below.
+# Place the word %S in your translation where the name of the folder should appear.
+imapCopyingMessage=Copying message to %S…
+
+# LOCALIZATION NOTE (imapFolderReceivingMessageOf3): Do not translate the words "%1$S", "%2$S", and "%3$S" below.
+# Place the word %1$S in your translation where the number of the message being downloaded should appear.
+# Place the word %2$S in your translation where the total number of messages to be downloaded should appear.
+# Place the word %3$S in your translation where the name of the folder being processed should appear.
+# Note: The account name and separators (e.g. colon, space) are automatically added to the status message.
+# Example: "Juan's Account: Downloading message 100 of 1000 in Sent…"
+imapFolderReceivingMessageOf3=Downloading message %1$S of %2$S in %3$S…
+
+# LOCALIZATION NOTE (imapDiscoveringMailbox): Do not translate the word "%S" below.
+# Place the word %S in your translation where the name of the folder should appear.
+imapDiscoveringMailbox=Found folder: %S
+
+# LOCALIZATION NOTE (imapEnterServerPasswordPrompt): Do not translate the words %1$S and %2$S below.
+# Place the word %1$S in your translation where the username should appear.
+# Place the word %2$S in your translation where the servername should appear.
+imapEnterServerPasswordPrompt=Enter your password for %1$S on %2$S:
+
+# LOCALIZATION NOTE (imapServerNotImap4): Do not translate the word "IMAP4" below.
+imapServerNotImap4=Mail server %S is not an IMAP4 mail server.
+
+# This is intentionally left blank.
+imapDone=
+
+# LOCALIZATION NOTE (imapEnterPasswordPromptTitleWithUsername): Do not translate the
+# word %1$S. Place the word %1$S where the user name should appear.
+imapEnterPasswordPromptTitleWithUsername=Enter your password for %1$S
+
+imapUnknownHostError=Failed to connect to server %S.
+imapOAuth2Error=Authentication failure while connecting to server %S.
+
+imapConnectionRefusedError=Could not connect to mail server %S; the connection was refused.
+
+imapNetTimeoutError=Connection to server %S timed out.
+
+# Status - no messages to download
+imapNoNewMessages=There are no new messages on the server.
+
+imapDefaultAccountName=Mail for %S
+
+imapSpecialChar2=The %S character is reserved on this imap server. Please choose another name.
+
+imapPersonalSharedFolderTypeName=Personal Folder
+
+imapPublicFolderTypeName=Public Folder
+
+imapOtherUsersFolderTypeName=Other User's Folder
+
+imapPersonalFolderTypeDescription=This is a personal mail folder. It is not shared.
+
+imapPersonalSharedFolderTypeDescription=This is a personal mail folder. It has been shared.
+
+imapPublicFolderTypeDescription=This is a public folder.
+
+imapOtherUsersFolderTypeDescription=This is a mail folder shared by the user '%S'.
+
+imapAclFullRights=Full Control
+
+imapAclLookupRight=Lookup
+
+imapAclReadRight=Read
+
+imapAclSeenRight=Set Read/Unread State
+
+imapAclWriteRight=Write
+
+imapAclInsertRight=Insert (Copy Into)
+
+imapAclPostRight=Post
+
+imapAclCreateRight=Create Subfolder
+
+imapAclDeleteRight=Delete Messages
+
+imapAclAdministerRight=Administer Folder
+
+imapServerDoesntSupportAcl=This server does not support shared folders.
+
+imapAclExpungeRight=Expunge
+
+imapServerDisconnected= Server %S has disconnected. The server may have gone down or there may be a network problem.
+
+# LOCALIZATION NOTE (autoSubscribeText): %1$S is the imap folder.
+imapSubscribePrompt=Would you like to subscribe to %1$S?
+
+imapServerDroppedConnection=Unable to connect to your IMAP server. You may have exceeded the maximum number \
+of connections to this server. If so, use the Advanced IMAP Server Settings dialog to \
+reduce the number of cached connections.
+
+# This will occur when a folder that has never been imap selected or opened
+# (left-clicked) is first right-clicked to access quota properties.
+imapQuotaStatusFolderNotOpen=Quota information is not available because the folder is not open.
+
+# The imap capability response reports that QUOTA is not supported.
+imapQuotaStatusNotSupported=This server does not support quotas.
+
+# The getqutaroot command succeeded but reported no quota information.
+imapQuotaStatusNoQuota2=This folder reports no quota information.
+
+# Folder properties were requested by the user (right-click) before the getquotaroot
+# command was sent.
+imapQuotaStatusInProgress=Quota information not yet available.
+
+# Out of memory
+imapOutOfMemory=Application is out of memory.
+
+# LOCALIZATION NOTE (imapCopyingMessageOf2): Do not translate the word "%S" below.
+# Place the word %3$S in your translation where the name of the destination folder should appear.
+# Place the word %1$S where the currently copying message should appear.
+# Place the word %2$S where the total number of messages should appear.
+imapCopyingMessageOf2=Copying message %1$S of %2$S to %3$S…
+
+# LOCALIZATION NOTE (imapMoveFolderToTrash): Do not translate the word %S below.
+# "%S" is the name of the folder.
+imapMoveFolderToTrash=Are you sure you want to delete the folder '%S'?
+
+# LOCALIZATION NOTE (imapDeleteNoTrash): Do not translate the word %S below.
+# "%S" is the name of the folder.
+imapDeleteNoTrash=Deleting this folder is not undoable and will delete all of the messages it contains, and its sub-folders. Are you sure you still want to delete the folder '%S'?
+
+imapDeleteFolderDialogTitle=Delete Folder
+
+imapDeleteFolderButtonLabel=&Delete Folder
+
+# LOCALIZATION NOTE (imapAuthChangeEncryptToPlainSSL): %S is the server hostname
+imapAuthChangeEncryptToPlainSSL=The IMAP server %S does not seem to support encrypted passwords. If you just set up this account, please try changing to 'Normal password' as the 'Authentication method' in the 'Account Settings | Server settings'. If it used to work and now suddenly fails, please contact your email administrator or provider.
+
+# LOCALIZATION NOTE (imapAuthChangePlainToEncrypt): %S is the server hostname
+imapAuthChangePlainToEncrypt=The IMAP server %S does not allow plaintext passwords. Please try changing to 'Encrypted password' as the 'Authentication method' in the 'Account Settings | Server settings'.
+
+# LOCALIZATION NOTE (imapAuthChangeEncryptToPlainNoSSL): %S is the server hostname
+imapAuthChangeEncryptToPlainNoSSL=The IMAP server %S does not seem to support encrypted passwords. If you just set up the account, please try changing to 'Password, transmitted insecurely' as the 'Authentication method' in the 'Account Settings | Server settings'. If it used to work and now suddenly fails, this is a common scenario how someone could steal your password.
+
+# LOCALIZATION NOTE (imapAuthMechNotSupported): %S is the server hostname
+imapAuthMechNotSupported=The IMAP server %S does not support the selected authentication method. Please change the 'Authentication method' in the 'Account Settings | Server settings'.
+
+# LOCALIZATION NOTE (imapAuthGssapiFailed): %S is the server hostname
+imapAuthGssapiFailed=The Kerberos/GSSAPI ticket was not accepted by the IMAP server %S. Please check that you are logged in to the Kerberos/GSSAPI realm.
+
+# LOCALIZATION NOTE (imapServerCommandFailed):
+# Place the word %1$S in your translation where the name of the account name should appear.
+# Place the word %2$S in your translation where the server response should appear.
+imapServerCommandFailed=The current command did not succeed. The mail server for account %1$S responded: %2$S
+
+# LOCALIZATION NOTE (imapFolderCommandFailed): Do not translate the word %S below.
+# Place the word %1$S in your translation where the name of the account should appear.
+# Place the word %2$S in your translation where the name of the folder should appear.
+# Place the word %3$S in your translation where the server response should appear.
+imapFolderCommandFailed=The current operation on '%2$S' did not succeed. The mail server for account %1$S responded: %3$S
+
+# LOCALIZATION NOTE (imapServerAlert):
+# Place the word %1$S in your translation where the name of the account should appear.
+# Place the word %2$S in your translation where the alert from the server should appear.
+imapServerAlert=Alert from account %1$S: %2$S
diff --git a/comm/suite/locales/en-US/chrome/mailnews/importDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/importDialog.dtd
new file mode 100644
index 0000000000..baeb7885a9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/importDialog.dtd
@@ -0,0 +1,43 @@
+<!-- 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/. -->
+
+<!ENTITY importDialog.windowTitle "Import">
+<!ENTITY importAll.label "Import Everything">
+<!ENTITY importAll.accesskey "E">
+<!ENTITY importMail.label "Mail">
+<!ENTITY importMail.accesskey "M">
+<!ENTITY importFeeds.label "Feed Subscriptions">
+<!ENTITY importFeeds.accesskey "d">
+<!ENTITY importAddressbook.label "Address Books">
+<!ENTITY importAddressbook.accesskey "A">
+<!ENTITY importSettings.label "Settings">
+<!ENTITY importSettings.accesskey "S">
+<!ENTITY importFilters.label "Filters">
+<!ENTITY importFilters.accesskey "F">
+
+<!ENTITY window.width "40em">
+<!ENTITY window.macWidth "45em">
+
+<!ENTITY importTitle.label "&brandShortName; Import Wizard">
+<!ENTITY importShortDesc.label "Import Mail, Address Books, Settings, and Filters from other programs">
+
+<!ENTITY importDescription1.label "This wizard will import mail messages, address book entries, feed subscriptions, preferences, and/or filters from other mail programs and common address book formats into &brandShortName;.">
+<!ENTITY importDescription2.label "Once they have been imported, you will be able to access them from within &brandShortName;.">
+
+<!ENTITY selectDescription.label "Please select the type of file that you would like to import:">
+<!ENTITY selectDescriptionB.label "Please select an existing account or create a new account:">
+<!ENTITY selectDescription.accesskey "P">
+<!ENTITY acctName.label "Name:">
+<!ENTITY acctName.accesskey "N">
+<!ENTITY noModulesFound.label "No application or file to import data from found.">
+
+<!ENTITY back.label "&lt; Back">
+<!ENTITY forward.label "Next &gt;">
+<!ENTITY finish.label "Finish">
+<!ENTITY cancel.label "Cancel">
+
+<!ENTITY select.label "or select the type of material to import:">
+
+<!ENTITY title.label "Title">
+<!ENTITY processing.label "Importing…">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/importMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/importMsgs.properties
new file mode 100644
index 0000000000..e90cefb086
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/importMsgs.properties
@@ -0,0 +1,306 @@
+# 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/.
+
+#
+# The following are used by the import code to display status/error
+# and informational messages
+#
+
+# Success message when no address books are found to import
+## @name IMPORT_NO_ADDRBOOKS
+## @loc None
+2000=No address books were found to import.
+
+# Error: Address book import not intialized
+## @name IMPORT_ERROR_AB_NOTINITIALIZED
+## @loc None
+2001=Unable to import address books: initialization error.
+
+# Error: Unable to create the import thread
+## @name IMPORT_ERROR_AB_NOTHREAD
+## @loc None
+2002=Unable to import address books: cannot create import thread.
+
+# Error: Unable to create the import thread
+## @name IMPORT_ERROR_GETABOOK
+## @loc None
+# LOCALIZATION NOTE (Error 2003): Do not translate the word "%S" below.
+2003=Error importing %S: unable to create address book.
+
+# Success message when no mailboxes are found to import
+## @name IMPORT_NO_MAILBOXES
+## @loc None
+2004=No mailboxes were found to import
+
+# Error: Mailbox import not intialized
+## @name IMPORT_ERROR_MB_NOTINITIALIZED
+## @loc None
+2005=Unable to import mailboxes, initialization error
+
+# Error: Unable to create the import thread
+## @name IMPORT_ERROR_MB_NOTHREAD
+## @loc None
+2006=Unable to import mailboxes, cannot create import thread
+
+# Error: Unable to create the proxy object for importing mailboxes
+## @name IMPORT_ERROR_MB_NOPROXY
+## @loc None
+2007=Unable to import mailboxes, cannot create proxy object for destination mailboxes
+
+# Error: Error creating destination mailboxes
+## @name IMPORT_ERROR_MB_FINDCHILD
+## @loc None
+# LOCALIZATION NOTE (Error 2008): Do not translate the word "%S" below.
+# Place %S in your translation where the name of the mailbox should appear.
+2008=Error creating destination mailboxes, cannot find mailbox %S
+
+# Error: Error creating destination mailboxes
+## @name IMPORT_ERROR_MB_CREATE
+## @loc None
+# LOCALIZATION NOTE (Error 2009): Do not translate the word "%S" below.
+# Place %S in your translation where the name of the mailbox should appear.
+2009=Error importing mailbox %S, unable to create destination mailbox
+
+# Error: No destination folder to import mailboxes
+## @name IMPORT_ERROR_MB_NODESTFOLDER
+## @loc None
+2010=Unable to create folder to import mail into
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC_START
+## @loc None
+2100=First Name
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2101=Last Name
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2102=Display Name
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2103=Nickname
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2104=Primary Email
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2105=Secondary Email
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2106=Work Phone
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2107=Home Phone
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2108=Fax Number
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2109=Pager Number
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2110=Mobile Number
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2111=Home Address
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2112=Home Address 2
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2113=Home City
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2114=Home State
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2115=Home ZipCode
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2116=Home Country
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2117=Work Address
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2118=Work Address 2
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2119=Work City
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2120=Work State
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2121=Work ZipCode
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2122=Work Country
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2123=Job Title
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2124=Department
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2125=Organization
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2126=Web Page 1
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2127=Web Page 2
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2128=Birth Year
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2129=Birth Month
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2130=Birth Day
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2131=Custom 1
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2132=Custom 2
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2133=Custom 3
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2134=Custom 4
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC
+## @loc None
+2135=Notes
+
+# Description: Address book field name
+## @name IMPORT_FIELD_DESC_END
+## @loc None
+2136=Screen Name
+
+#Error strings
+ImportAlreadyInProgress=An import operation is currently in progress. Try again when the current import has finished.
+
+#Error strings for settings import
+ImportSettingsBadModule=Unable to load settings module
+ImportSettingsNotFound=Unable to find settings. Check to make sure the application is installed on this machine.
+ImportSettingsFailed=An error occurred while importing settings. Some, or all, of the settings may not have been imported.
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+ImportSettingsSuccess=Settings were imported from %S
+
+#Error string for mail import
+ImportMailBadModule=Unable to load mail import module
+ImportMailNotFound=Unable to find mail to import. Check to make sure the mail application is correctly installed on this machine.
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+ImportMailFailed=An error occurred importing mail from %S
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+ImportMailSuccess=Mail was successfully imported from %S
+
+# Error string for address import
+ImportAddressBadModule=Unable to load address book import module.
+ImportAddressNotFound=Unable to find any address books to import. Check to make sure the selected application or format is correctly installed on this machine.
+ImportEmptyAddressBook=Can't import empty address book %S.
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+ImportAddressFailed=An error occurred importing addresses from %S.
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+ImportAddressSuccess=Addresses successfully imported from %S.
+
+# Error string for filters import
+ImportFiltersBadModule=Unable to load filters import module.
+# LOCALIZATION NOTE : The %S will get replaced by the name of the import module.
+ImportFiltersFailed=An error occurred importing filters from %S.
+# LOCALIZATION NOTE : The %S will get replaced by the name of the import module.
+ImportFiltersSuccess=Filters successfully imported from %S.
+# LOCALIZATION NOTE : The %S will get replaced by the name of the import module.
+ImportFiltersPartial=Filters partially imported from %S. Warnings below:
+
+#Progress strings
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+MailProgressMeterText=Converting mailboxes from %S
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+AddrProgressMeterText=Converting address books from %S
+
+#Import file dialog strings
+ImportSelectSettings=Select Settings File
+ImportSelectMailDir=Select Mail Directory
+ImportSelectAddrDir=Select Address Book Directory
+ImportSelectAddrFile=Select Address Book File
+
+# Folder Names for imported Mail
+DefaultFolderName=Imported Mail
+# LOCALIZATION NOTE: Do not translate the word "%S" below.
+ImportModuleFolderName=%S Import
diff --git a/comm/suite/locales/en-US/chrome/mailnews/junkLog.dtd b/comm/suite/locales/en-US/chrome/mailnews/junkLog.dtd
new file mode 100644
index 0000000000..dba7e67dbc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/junkLog.dtd
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+
+<!ENTITY adaptiveJunkLog.title "Adaptive Junk Mail Log">
+<!ENTITY adaptiveJunkLogInfo.label "Log of adaptive junk mail control activity.">
+<!ENTITY clearLog.label "Clear Log">
+<!ENTITY clearLog.accesskey "C">
+<!ENTITY closeLog.label "Close">
+<!ENTITY closeLog.accesskey "o">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/junkMailInfo.dtd b/comm/suite/locales/en-US/chrome/mailnews/junkMailInfo.dtd
new file mode 100644
index 0000000000..900d9d66d1
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/junkMailInfo.dtd
@@ -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/. -->
+
+<!ENTITY window.title "About Junk Mail">
+<!ENTITY window.width "450">
+<!ENTITY info1a.label "Mail automatically detects incoming messages that appear to be junk mail (also known as spam). Messages that Mail thinks are junk will display a junk icon">
+<!ENTITY info1b.label ".">
+<!ENTITY info2.label "At first, you must train Mail to identify junk mail by using the Junk toolbar button to mark messages as junk or not junk.">
+<!ENTITY info3.label "Once Mail is correctly identifying junk mail, you can use the Junk Mail Controls to automatically move incoming junk mail to the Junk folder.">
+<!ENTITY info4.label "For more information, click Help.">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/localMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/localMsgs.properties
new file mode 100644
index 0000000000..33b93dc2e9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/localMsgs.properties
@@ -0,0 +1,136 @@
+# 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/.
+
+#
+# The following are used by the local mail code to display progress/status/error messages
+#
+
+# LOCALIZATION NOTE(pop3ErrorDialogTitle): Do not translate the word "%S"
+# below. Place the word %S where the account name should appear.
+pop3ErrorDialogTitle=Error with account %S
+
+# LOCALIZATION NOTE (pop3EnterPasswordPromptTitleWithUsername): Do not translate the
+# word %1$S. Place the word %1$S where the user name should appear.
+pop3EnterPasswordPromptTitleWithUsername=Enter your password for %1$S
+
+# LOCALIZATION NOTE(pop3EnterPasswordPrompt): Do not translate the words "%1$S"
+# and "%2$S" below. Place the word %1$S where the user name should appear, and
+# %2$S where the host name should appear.
+pop3EnterPasswordPrompt=Enter your password for %1$S on %2$S:
+
+# LOCALIZATION NOTE(pop3PreviouslyEnteredPasswordIsInvalidPrompt): Do not
+# translate the words "%1$S" and "%2$S" below. Place the word %1$S where the
+# user name should appear, and %2$S where the host name should appear.
+pop3PreviouslyEnteredPasswordIsInvalidPrompt=Please enter a new password for user %1$S on %2$S:
+
+# Status - Downloading message n of m
+# LOCALIZATION NOTE (receivingMessages): Do not translate %1$S or %2$S in the following lines.
+# Place the word %1$S where the number of messages downloaded so far should appear.
+# Place the word %2$S where the total number of messages to receive should appear;
+receivingMessages=Downloading message %1$S of %2$S…
+
+# Status - connecting to host
+hostContact=Host contacted, sending login information…
+
+# Status - no messages to download
+noNewMessages=There are no new messages.
+
+# Status - messages received after the download
+#LOCALIZATION NOTE : Do not translate %1$S or %2$S in the following line.
+# %1$S will receive the number of messages received
+# %2$S will receive the total number of messages
+receivedMsgs=Received %1$S of %2$S messages
+
+# Status - parsing folder
+#LOCALIZATION NOTE (buildingSummary): Do not translate %S in the following line.
+# Place the word %S where the name of the mailbox should appear
+buildingSummary=Building summary file for %S…
+
+# Status - parsing folder
+localStatusDocumentDone=Done
+
+# Status - pop3 server error
+#LOCALIZATION NOTE (pop3ServerError): Do not translate POP3 in the following line.
+pop3ServerError=An error occurred with the POP3 mail server.
+
+# Status - pop3 user name failed
+pop3UsernameFailure=Sending of username did not succeed.
+
+# Status - password failed
+#LOCALIZATION NOTE (pop3PasswordFailed): Do not translate "%1$S" below.
+# Place the word %1$S where the user name should appear.
+pop3PasswordFailed=Sending of password for user %1$S did not succeed.
+
+# Status - write error occurred
+pop3MessageWriteError=Unable to write the email to the mailbox. Make sure the file system allows you write privileges, and you have enough disk space to copy the mailbox.
+
+# Status - pop3 server or folder busy
+# LOCALIZATION NOTE (pop3ServerBusy): Do not translate the word "%S" below.
+# Place %S where the account name should appear.
+pop3ServerBusy=The account %S is being processed. Please wait until processing is complete to get messages.
+
+# Status - retr failure from the server
+pop3RetrFailure=The RETR command did not succeed. Error retrieving a message.
+
+# Status - password undefined
+pop3PasswordUndefined=Error getting mail password.
+
+# Status - username undefined
+pop3UsernameUndefined=You have not supplied a username for this server. Please provide one in the account setup menu and try again.
+
+# Status - list failure
+pop3ListFailure=The LIST command did not succeed. Error getting the ID and size of a message.
+
+# Status - delete error
+pop3DeleFailure=The DELE command did not succeed. Error marking a message as deleted.
+
+# Status - stat failed
+pop3StatFail=The STAT command did not succeed. Error getting message number and sizes.
+
+#LOCALIZATION NOTE (pop3ServerSaid): Do not remove the leading space during translation.
+pop3ServerSaid= Mail server %S responded:
+
+copyingMessagesStatus=Copying %S of %S messages to %S
+
+movingMessagesStatus=Moving %S of %S messages to %S
+
+pop3TmpDownloadError=There was an error downloading the following message: \nFrom: %S\n Subject: %S\n This message may contain a virus or there is not enough disk space. Skip this message?
+
+# Status - the server doesn't support UIDL…
+# LOCALIZATION NOTE(pop3ServerDoesNotSupportUidlEtc): The following sentence should be translated in this way:
+# Do not translate "POP3"
+# Do not translate "%S". Place %S in your translation where the name of the server should appear.
+# Do not translate "UIDL"
+pop3ServerDoesNotSupportUidlEtc=The POP3 mail server (%S) does not support UIDL or XTND XLST, which are required to implement the ``Leave on Server'', ``Maximum Message Size'' or ``Fetch Headers Only'' options. To download your mail, turn off these options in the Server Settings for your mail server in the Account Settings window.
+
+# Status - the server doesn't support the top command
+# LOCALIZATION NOTE(pop3ServerDoesNotSupportTopCommand): The following sentence should be translated in this way:
+# Do not translate "POP3"
+# Do not translate "%S". Place %S in your translation where the name of the server should appear.
+# Do not translate "TOP"
+pop3ServerDoesNotSupportTopCommand=The POP3 mail server (%S) does not support the TOP command. Without server support for this, we cannot implement the ``Maximum Message Size'' or ``Fetch Headers Only'' preference. This option has been disabled, and messages will be downloaded regardless of their size.
+
+nsErrorCouldNotConnectViaTls=Unable to establish TLS connection to POP3 server. The server may be down or may be incorrectly configured. Please verify the correct configuration in the Server Settings for your mail server in the Account Settings window and try again.
+
+# LOCALIZATION NOTE (pop3MoveFolderToTrash): Do not translate the word %S below.
+# "%S" is the name of the folder.
+pop3MoveFolderToTrash=Are you sure you want to delete the folder '%S'?
+
+pop3DeleteFolderDialogTitle=Delete Folder
+
+pop3DeleteFolderButtonLabel=&Delete Folder
+
+pop3AuthInternalError=Internal state error during POP3 server authentication. This is an internal, unexpected error in the application, please report it as bug.
+
+pop3AuthChangeEncryptToPlainNoSSL=This POP3 server does not seem to support encrypted passwords. If you just set up the account, please try changing to 'Password, transmitted insecurely' as the 'Authentication method' in the 'Account Settings | Server settings'. If it used to work and now suddenly fails, this is a common scenario how someone could steal your password.
+
+pop3AuthChangeEncryptToPlainSSL=This POP3 server does not seem to support encrypted passwords. If you just set up this account, please try changing to 'Normal password' as the 'Authentication method' in the 'Account Settings | Server settings'. If it used to work and now suddenly fails, please contact your email administrator or provider.
+
+pop3AuthChangePlainToEncrypt=This POP3 server does not allow plaintext passwords. Please try changing to 'Encrypted password' as the 'Authentication method' in the 'Account Settings | Server settings'.
+
+# Authentication server caps and pref don't match
+pop3AuthMechNotSupported=The server does not support the selected authentication method. Please change the 'Authentication method' in the 'Account Settings | Server settings'.
+
+# Status - Could not log in to GSSAPI, and it was the only method
+pop3GssapiFailure=The Kerberos/GSSAPI ticket was not accepted by the POP server. Please check that you are logged in to the Kerberos/GSSAPI realm.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailEditorOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/mailEditorOverlay.dtd
new file mode 100644
index 0000000000..be8fca049d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailEditorOverlay.dtd
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+
+
+<!ENTITY sendPage.label "Send Page…">
+<!ENTITY sendPage.accesskey "g">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailKeysOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/mailKeysOverlay.dtd
new file mode 100644
index 0000000000..6df2b60417
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailKeysOverlay.dtd
@@ -0,0 +1,30 @@
+<!-- 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/. -->
+
+<!ENTITY markAsReadCmd.label "As Read">
+<!ENTITY markAsReadCmd.accesskey "R">
+<!ENTITY markAsReadCmd.key "m">
+
+<!ENTITY markAsUnreadCmd.label "As Unread">
+<!ENTITY markAsUnreadCmd.accesskey "U">
+<!ENTITY markAsUnreadCmd2.key "u">
+
+<!ENTITY markFlaggedCmd.label "Flag">
+<!ENTITY markFlaggedCmd.accesskey "F">
+<!ENTITY markFlaggedCmd.key "i">
+
+<!ENTITY openMessageWindowCmd.label "Open Message">
+<!ENTITY openMessageWindowCmd.accesskey "O">
+<!ENTITY openMessageWindowCmd.key "o">
+
+<!ENTITY tagCmd0.key "0">
+<!ENTITY tagCmd1.key "1">
+<!ENTITY tagCmd2.key "2">
+<!ENTITY tagCmd3.key "3">
+<!ENTITY tagCmd4.key "4">
+<!ENTITY tagCmd5.key "5">
+<!ENTITY tagCmd6.key "6">
+<!ENTITY tagCmd7.key "7">
+<!ENTITY tagCmd8.key "8">
+<!ENTITY tagCmd9.key "9">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/mailOverlay.dtd
new file mode 100644
index 0000000000..adf5b57ecd
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailOverlay.dtd
@@ -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/. -->
+
+
+<!ENTITY newMessageCmd.key "M">
+<!ENTITY newMessageCmd.label "Message">
+<!ENTITY newMessageCmd.accesskey "m">
+
+<!ENTITY newContactCmd.label "Contact…">
+<!ENTITY newContactCmd.accesskey "C">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailTasksOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/mailTasksOverlay.dtd
new file mode 100644
index 0000000000..03002d5d9d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailTasksOverlay.dtd
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+
+<!-- LOCALIZATION NOTE (messengerCmd.label): DONT_TRANSLATE -->
+<!ENTITY messengerCmd.label "Mail &amp; Newsgroups">
+<!ENTITY messengerCmd.accesskey "m">
+<!ENTITY messengerCmd.commandkey "2">
+<!ENTITY addressBookCmd.label "Address Book">
+<!ENTITY addressBookCmd.accesskey "a">
+<!ENTITY addressBookCmd.commandkey "5">
+
+<!ENTITY taskMessenger.tooltip "Mail &amp; Newsgroups">
+<!ENTITY taskAddressBook.tooltip "Address Book">
+
+<!-- searchAddressesCmd is also used by addressbook -->
+<!ENTITY searchAddressesCmd.label "Search Addresses…">
+<!ENTITY searchAddressesCmd.accesskey "A">
+
+<!ENTITY searchMailCmd.label "Search Messages…">
+<!ENTITY searchMailCmd.accesskey "M">
+<!ENTITY searchMailCmd.key "f">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailViewList.dtd b/comm/suite/locales/en-US/chrome/mailnews/mailViewList.dtd
new file mode 100644
index 0000000000..f83b7e6658
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailViewList.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!--LOCALIZATION NOTE msgViewPickerOverlay.dtd UI for showing various views on a folder -->
+
+<!ENTITY mailViewListTitle.label "Customize Message Views">
+<!ENTITY viewName.label "View Name">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailViewSetup.dtd b/comm/suite/locales/en-US/chrome/mailnews/mailViewSetup.dtd
new file mode 100644
index 0000000000..3ebe7246b6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailViewSetup.dtd
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE msgViewPickerOverlay.dtd UI for showing various views on a folder -->
+
+<!ENTITY mailViewSetupTitle.label "Message View Setup">
+<!ENTITY mailViewHeading.label "Message view name:">
+<!ENTITY mailViewHeading.accesskey "e">
+<!ENTITY searchTermCaption.label "When this view is selected, display only messages that:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mailviews.properties b/comm/suite/locales/en-US/chrome/mailnews/mailviews.properties
new file mode 100644
index 0000000000..0257ac6d5d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mailviews.properties
@@ -0,0 +1,13 @@
+# 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/.
+
+#
+# Mail Views
+#
+
+mailViewPeopleIKnow=People I Know
+mailViewRecentMail=Recent Mail
+mailViewLastFiveDays=Last 5 Days
+mailViewNotJunk=Not Junk
+mailViewHasAttachments=Has Attachments
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mapi/mapi.properties b/comm/suite/locales/en-US/chrome/mailnews/mapi/mapi.properties
new file mode 100644
index 0000000000..c687dda694
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mapi/mapi.properties
@@ -0,0 +1,13 @@
+# 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/.
+
+# MAPI Messages
+loginText=Please enter your password for %S:
+loginTextwithName=Please enter your username and password
+loginTitle=%S Mail
+PasswordTitle=%S Mail
+
+# MAPI Security Messages
+mapiBlindSendWarning=Another application is attempting to send mail using your user profile. Are you sure you want to send mail?
+mapiBlindSendDontShowAgain=Warn me whenever other applications try to send mail from me
diff --git a/comm/suite/locales/en-US/chrome/mailnews/markByDate.dtd b/comm/suite/locales/en-US/chrome/mailnews/markByDate.dtd
new file mode 100644
index 0000000000..c34c23d9d9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/markByDate.dtd
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY messageMarkByDate.label "Mark Messages as Read by Date">
+<!ENTITY markByDateLower.label "Mark messages as read from:">
+<!ENTITY markByDateLower.accesskey "F">
+<!ENTITY markByDateUpper.accesskey "T">
+<!ENTITY markByDateUpper.label "To:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/messenger.dtd b/comm/suite/locales/en-US/chrome/mailnews/messenger.dtd
new file mode 100644
index 0000000000..d709078bc9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/messenger.dtd
@@ -0,0 +1,564 @@
+<!-- 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/. -->
+
+<!ENTITY messengerWindow.title "Mail &amp; Newsgroups">
+<!ENTITY titleModifier.label "&brandShortName;">
+<!ENTITY titleSeparator.label " - ">
+
+<!-- tabmail -->
+<!ENTITY tabmailClose.label "Close Tab">
+<!ENTITY tabmailClose.tooltip "Close tab">
+<!ENTITY tabmailNewButton.tooltip "Duplicate current tab">
+<!ENTITY tabmailCloseButton.tooltip "Close current tab">
+<!ENTITY tabmailAllTabs.tooltip "List all tabs">
+
+<!-- menu items: the . means that the menu item isn't implemented yet -->
+
+<!-- File menu items -->
+<!ENTITY newMessage.label "New Message">
+<!ENTITY newMessage.accesskey "N">
+<!ENTITY newFolderCmd.label "Folder…">
+<!ENTITY newFolderCmd.accesskey "F">
+<!ENTITY newVirtualFolderCmd.label "Saved Search…">
+<!ENTITY newVirtualFolderCmd.accesskey "S">
+<!ENTITY newTabCmd.label "Duplicate Tab">
+<!ENTITY newTabCmd.accesskey "T">
+<!ENTITY newTabCmd.key "t">
+<!ENTITY closeTabCmd.label "Close Tab">
+<!ENTITY closeTabCmd.accesskey "e">
+<!ENTITY newAccountCmd.label "Account…">
+<!ENTITY newAccountCmd.accesskey "A">
+<!ENTITY openMessageFileCmd.label "Open File…">
+<!ENTITY openMessageFileCmd.accesskey "O">
+<!ENTITY openAttachmentCmd.label "Attachments">
+<!ENTITY openAttachmentCmd.accesskey "h">
+<!ENTITY saveAsMenu.label "Save As">
+<!ENTITY saveAsMenu.accesskey "S">
+<!ENTITY saveAsFileCmd.label "File">
+<!ENTITY saveAsFileCmd.accesskey "F">
+<!ENTITY saveAsFileCmd.key "s">
+<!ENTITY saveAsTemplateCmd.label "Template">
+<!ENTITY saveAsTemplateCmd.accesskey "T">
+<!ENTITY getNewMsgCmd.label "Get New Messages">
+<!ENTITY getNewMsgCmd.accesskey "G">
+<!ENTITY getNewMsgCmd2.key "d">
+<!ENTITY getNewMsgForCmd.label "Get New Messages for">
+<!ENTITY getNewMsgForCmd.accesskey "w">
+<!ENTITY getAllNewMsgCmdPopupMenu.label "All Accounts">
+<!ENTITY getAllNewMsgCmdPopupMenu.accesskey "A">
+<!ENTITY getAllNewMsgCmd2.key "d">
+<!ENTITY getNextNMsgCmd.label "Get Next 500 News Messages">
+<!ENTITY getNextNMsgCmd.accesskey "t">
+<!ENTITY sendUnsentCmd.label "Send Unsent Messages">
+<!ENTITY sendUnsentCmd.accesskey "d">
+<!ENTITY subscribeCmd.label "Subscribe…">
+<!ENTITY subscribeCmd.accesskey "b">
+<!ENTITY renameFolder.label "Rename Folder…">
+<!ENTITY renameFolder.accesskey "R">
+<!ENTITY compactFolders.label "Compact Folders">
+<!ENTITY compactFolders.accesskey "F">
+<!ENTITY emptyTrashCmd.label "Empty Trash">
+<!ENTITY emptyTrashCmd.accesskey "y">
+<!ENTITY offlineMenu.label "Offline">
+<!ENTITY offlineMenu.accesskey "l">
+<!ENTITY synchronizeOfflineCmd.label "Download/Sync Now…">
+<!ENTITY synchronizeOfflineCmd.accesskey "S">
+<!ENTITY settingsOfflineCmd.label "Offline Settings…">
+<!ENTITY settingsOfflineCmd.accesskey "e">
+<!ENTITY downloadSelectedCmd.label "Get Selected Messages">
+<!ENTITY downloadSelectedCmd.accesskey "t">
+<!ENTITY downloadFlaggedCmd.label "Get Flagged Messages">
+<!ENTITY downloadFlaggedCmd.accesskey "G">
+
+<!-- Edit Menu -->
+<!ENTITY deleteMsgCmd.label "Delete Message">
+<!ENTITY deleteMsgCmd.accesskey "D">
+<!ENTITY undeleteMsgCmd.label "Undelete Message">
+<!ENTITY undeleteMsgCmd.accesskey "d">
+<!ENTITY cancelNewsMsgCmd.label "Cancel Message">
+<!ENTITY cancelNewsMsgCmd.accesskey "n">
+<!ENTITY deleteMsgsCmd.label "Delete Selected Messages">
+<!ENTITY deleteMsgsCmd.accesskey "D">
+<!ENTITY undeleteMsgsCmd.label "Undelete Selected Messages">
+<!ENTITY undeleteMsgsCmd.accesskey "d">
+<!ENTITY deleteFolderCmd.label "Delete Folder">
+<!ENTITY deleteFolderCmd.accesskey "D">
+<!ENTITY unsubscribeNewsgroupCmd.label "Unsubscribe">
+<!ENTITY unsubscribeNewsgroupCmd.accesskey "n">
+<!ENTITY selectMenu.label "Select">
+<!ENTITY selectMenu.accesskey "S">
+<!ENTITY selectThreadCmd.label "Thread">
+<!ENTITY selectThreadCmd.accesskey "T">
+<!ENTITY selectThreadCmd.key "a">
+<!ENTITY selectFlaggedCmd.label "Flagged Messages">
+<!ENTITY selectFlaggedCmd.accesskey "F">
+<!ENTITY menuFavoriteFolder.label "Favorite Folder">
+<!ENTITY menuFavoriteFolder.accesskey "v">
+<!ENTITY folderPropsCmd.label "Properties…">
+<!ENTITY folderPropsFolderCmd.label "Folder Properties…">
+<!ENTITY folderPropsNewsgroupCmd.label "Newsgroup Properties…">
+<!ENTITY folderPropsCmd.accesskey "o">
+<!ENTITY accountManagerCmd.label "Mail &amp; Newsgroups Account Settings…">
+<!ENTITY accountManagerCmd.accesskey "M">
+<!ENTITY undoDeleteMsgCmd.label "Undo Delete Message">
+<!ENTITY redoDeleteMsgCmd.label "Redo Delete Message">
+<!ENTITY undoMoveMsgCmd.label "Undo Move Message">
+<!ENTITY redoMoveMsgCmd.label "Redo Move Message">
+<!ENTITY undoCopyMsgCmd.label "Undo Copy Message">
+<!ENTITY redoCopyMsgCmd.label "Redo Copy Message">
+<!ENTITY undoMarkAllCmd.label "Undo Mark All Read">
+<!ENTITY redoMarkAllCmd.label "Redo Mark All Read">
+<!ENTITY undoDefaultCmd.label "Undo">
+<!ENTITY redoDefaultCmd.label "Redo">
+
+<!-- View Menu -->
+<!ENTITY showMessengerToolbarCmd.label "Mail Toolbar">
+<!ENTITY showMessengerToolbarCmd.accesskey "M">
+<!ENTITY showSearchToolbarCmd.label "Search Bar">
+<!ENTITY showSearchToolbarCmd.accesskey "a">
+<!ENTITY showTabsToolbarCmd.label "Tabs Toolbar">
+<!ENTITY showTabsToolbarCmd.accesskey "T">
+
+<!ENTITY messagePaneLayoutStyle.label "Layout">
+<!ENTITY messagePaneLayoutStyle.accesskey "L">
+<!ENTITY messagePaneClassic.label "Classic View">
+<!ENTITY messagePaneClassic.accesskey "C">
+<!ENTITY messagePaneWide.label "Wide View">
+<!ENTITY messagePaneWide.accesskey "W">
+<!ENTITY messagePaneVertical.label "Vertical View">
+<!ENTITY messagePaneVertical.accesskey "V">
+<!ENTITY showMessagePaneCmd.label "Message Pane">
+<!ENTITY showMessagePaneCmd.accesskey "M">
+<!ENTITY showThreadPaneCmd.label "Thread Pane">
+<!ENTITY showThreadPaneCmd.accesskey "T">
+<!ENTITY showFolderPaneCmd.label "Folder Pane">
+<!ENTITY showFolderPaneCmd.accesskey "F">
+<!-- LOCALIZATION NOTE (toggleFolderPaneCmd.key): This is only used on the
+ mac platform, other platforms use VK_F9. -->
+<!ENTITY toggleFolderPaneCmd.key "S">
+
+<!-- sortMenu is also used by addressbook -->
+<!ENTITY sortMenu.label "Sort by">
+<!ENTITY sortMenu.accesskey "t">
+
+<!ENTITY sortByDateCmd.label "Date">
+<!ENTITY sortByDateCmd.accesskey "e">
+<!ENTITY sortByReceivedCmd.label "Received">
+<!ENTITY sortByReceivedCmd.accesskey "v">
+<!ENTITY sortByFlagCmd.label "Flag">
+<!ENTITY sortByFlagCmd.accesskey "l">
+<!ENTITY sortByPriorityCmd.label "Priority">
+<!ENTITY sortByPriorityCmd.accesskey "P">
+<!ENTITY sortBySizeCmd.label "Size">
+<!ENTITY sortBySizeCmd.accesskey "z">
+<!ENTITY sortByStatusCmd.label "Status">
+<!ENTITY sortByStatusCmd.accesskey "u">
+<!ENTITY sortByTagsCmd.label "Tags">
+<!ENTITY sortByTagsCmd.accesskey "g">
+<!ENTITY sortByJunkStatusCmd.label "Junk Status">
+<!ENTITY sortByJunkStatusCmd.accesskey "J">
+<!ENTITY sortBySubjectCmd.label "Subject">
+<!ENTITY sortBySubjectCmd.accesskey "S">
+<!ENTITY sortByFromCmd.label "From">
+<!ENTITY sortByFromCmd.accesskey "F">
+<!ENTITY sortByRecipientCmd.label "Recipient">
+<!ENTITY sortByRecipientCmd.accesskey "c">
+<!ENTITY sortByUnreadCmd.label "Read">
+<!ENTITY sortByUnreadCmd.accesskey "R">
+<!ENTITY sortByOrderReceivedCmd.label "Order Received">
+<!ENTITY sortByOrderReceivedCmd.accesskey "O">
+<!ENTITY sortByAttachmentsCmd.label "Attachments">
+<!ENTITY sortByAttachmentsCmd.accesskey "m">
+<!ENTITY sortAscending.label "Ascending">
+<!ENTITY sortAscending.accesskey "A">
+<!ENTITY sortDescending.label "Descending">
+<!ENTITY sortDescending.accesskey "D">
+<!ENTITY sortThreaded.label "Threaded">
+<!ENTITY sortThreaded.accesskey "T">
+<!ENTITY sortUnthreaded.label "Unthreaded">
+<!ENTITY sortUnthreaded.accesskey "h">
+<!ENTITY groupBySort.label "Grouped By Sort">
+<!ENTITY groupBySort.accesskey "G">
+<!ENTITY msgsMenu.label "Messages">
+<!ENTITY msgsMenu.accesskey "M">
+<!ENTITY threads.label "Threads">
+<!ENTITY threads.accesskey "e">
+<!ENTITY allMsgsCmd.label "All">
+<!ENTITY allMsgsCmd.accesskey "A">
+<!ENTITY expandAllThreadsCmd.label "Expand All Threads">
+<!ENTITY expandAllThreadsCmd.accesskey "E">
+<!ENTITY expandAllThreadsCmd.key "*">
+<!ENTITY collapseAllThreadsCmd.label "Collapse All Threads">
+<!ENTITY collapseAllThreadsCmd.accesskey "C">
+<!ENTITY collapseAllThreadsCmd.key "\">
+<!ENTITY unreadMsgsCmd.label "Unread">
+<!ENTITY unreadMsgsCmd.accesskey "U">
+<!ENTITY threadsWithUnreadCmd.label "Threads with Unread">
+<!ENTITY threadsWithUnreadCmd.accesskey "T">
+<!ENTITY watchedThreadsWithUnreadCmd.label "Watched Threads with Unread">
+<!ENTITY watchedThreadsWithUnreadCmd.accesskey "W">
+<!ENTITY ignoredThreadsCmd.label "Ignored Threads">
+<!ENTITY ignoredThreadsCmd.accesskey "i">
+
+<!ENTITY headersMenu.label "Headers">
+<!ENTITY headersMenu.accesskey "H">
+<!ENTITY headersAllCmd.label "All">
+<!ENTITY headersAllCmd.accesskey "A">
+<!ENTITY headersNormalCmd.label "Normal">
+<!ENTITY headersNormalCmd.accesskey "N">
+<!ENTITY bodyMenu.label "Message Body As">
+<!ENTITY bodyMenu.accesskey "B">
+<!ENTITY bodyAllowHTML.label "Original HTML">
+<!ENTITY bodyAllowHTML.accesskey "H">
+<!ENTITY bodySanitized.label "Simple HTML">
+<!ENTITY bodySanitized.accesskey "S">
+<!ENTITY bodyAsPlaintext.label "Plain Text">
+<!ENTITY bodyAsPlaintext.accesskey "P">
+<!ENTITY bodyAllParts.label "All Body Parts">
+<!ENTITY bodyAllParts.accesskey "A">
+
+<!ENTITY bodyMenuFeed.label "Feed Message Body As">
+<!ENTITY bodyMenuFeed.accesskey "B">
+<!ENTITY viewFeedWebPage.label "Web Page">
+<!ENTITY viewFeedWebPage.accesskey "W">
+<!ENTITY viewFeedSummary.label "Summary">
+<!ENTITY viewFeedSummary.accesskey "m">
+<!ENTITY viewFeedSummaryFeedPropsPref.label "Default Format">
+<!ENTITY viewFeedSummaryFeedPropsPref.accesskey "D">
+
+<!ENTITY viewAttachmentsInlineCmd.label "Display Attachments Inline">
+<!ENTITY viewAttachmentsInlineCmd.accesskey "D">
+<!ENTITY reloadCmd.label "Reload">
+<!ENTITY reloadCmd.accesskey "R">
+<!ENTITY stopCmd.label "Stop">
+<!ENTITY stopCmd.accesskey "S">
+<!ENTITY pageSourceCmd.label "Message Source">
+<!ENTITY pageSourceCmd.accesskey "o">
+<!ENTITY pageSourceCmd.key "u">
+
+<!ENTITY findCmd.label "Find in This Message…">
+
+<!-- LOCALIZATION NOTE (quickFilterBar.show.key2):
+ This is the key used to show the Lightning quick filter bar. -->
+<!ENTITY quickFilterBar.show.key2 "K">
+
+<!-- Go Menu -->
+
+<!ENTITY goMenu.label "Go">
+<!ENTITY goMenu.accesskey "G">
+<!ENTITY nextMenu.label "Next">
+<!ENTITY nextMenu.accesskey "N">
+<!ENTITY nextMsgCmd.label "Message">
+<!ENTITY nextMsgCmd.accesskey "M">
+<!ENTITY nextMsgCmd.key "f">
+<!ENTITY nextUnreadMsgCmd.label "Unread Message">
+<!ENTITY nextUnreadMsgCmd.accesskey "U">
+<!ENTITY nextUnreadMsgCmd.key "n">
+<!ENTITY nextFlaggedMsgCmd.label "Flagged Message">
+<!ENTITY nextFlaggedMsgCmd.accesskey "F">
+<!ENTITY nextUnreadThread.label "Unread Thread">
+<!ENTITY nextUnreadThread.accesskey "T">
+<!ENTITY nextUnreadThread.key "t">
+<!ENTITY prevMenu.label "Previous">
+<!ENTITY prevMenu.accesskey "P">
+<!ENTITY prevMsgCmd.label "Message">
+<!ENTITY prevMsgCmd.accesskey "M">
+<!ENTITY prevMsgCmd.key "b">
+<!ENTITY prevUnreadMsgCmd.label "Unread Message">
+<!ENTITY prevUnreadMsgCmd.accesskey "U">
+<!ENTITY prevUnreadMsgCmd.key "p">
+<!ENTITY goBackCmd.label "Back">
+<!ENTITY goBackCmd.accesskey "B">
+<!ENTITY goBackCmd.commandKey "[">
+<!ENTITY goForwardCmd.label "Forward">
+<!ENTITY goForwardCmd.accesskey "F">
+<!ENTITY goForwardCmd.commandKey "]">
+<!ENTITY prevFlaggedMsgCmd.label "Flagged Message">
+<!ENTITY prevFlaggedMsgCmd.accesskey "F">
+<!ENTITY folderMenu.label "Folder">
+<!ENTITY folderMenu.accesskey "o">
+<!ENTITY startPageCmd.label "Mail Start Page">
+<!ENTITY startPageCmd.accesskey "S">
+
+<!-- Message Menu -->
+<!ENTITY msgMenu.label "Message">
+<!ENTITY msgMenu.accesskey "M">
+<!ENTITY newMsgCmd.label "New Message">
+<!ENTITY newMsgCmd.accesskey "N">
+<!ENTITY newNewMsgCmd.label "Message">
+<!ENTITY newNewMsgCmd.accesskey "M">
+<!ENTITY replyMsgCmd.label "Reply">
+<!ENTITY replyMsgCmd.accesskey "R">
+<!ENTITY replyMsgCmd.key "r">
+<!ENTITY replyListCmd.label "Reply to List">
+<!ENTITY replyListCmd.accesskey "y">
+<!ENTITY replyNewsgroupCmd.label "Reply to Newsgroup">
+<!ENTITY replyNewsgroupCmd.accesskey "y">
+<!ENTITY replySenderCmd.label "Reply to Sender Only">
+<!ENTITY replySenderCmd.accesskey "R">
+<!ENTITY replyToAllMsgCmd.label "Reply to All">
+<!ENTITY replyToAllMsgCmd.accesskey "p">
+<!ENTITY replyToAllMsgCmd.key "r">
+<!ENTITY replyToSenderAndNewsgroupCmd.label "Reply to Sender and Newsgroup">
+<!ENTITY replyToSenderAndNewsgroupCmd.accesskey "p">
+<!ENTITY replyToAllRecipientsCmd.label "Reply to All Recipients">
+<!ENTITY replyToAllRecipientsCmd.accesskey "a">
+<!ENTITY forwardMsgCmd.label "Forward">
+<!ENTITY forwardMsgCmd.accesskey "F">
+<!ENTITY forwardMsgCmd.key "l">
+<!ENTITY forwardAsMenu.label "Forward As">
+<!ENTITY forwardAsMenu.accesskey "o">
+<!ENTITY forwardAsInline.label "Inline">
+<!ENTITY forwardAsInline.accesskey "I">
+<!ENTITY forwardAsAttachmentCmd.label "Attachment">
+<!ENTITY forwardAsAttachmentCmd.accesskey "A">
+<!ENTITY editAsNewMsgCmd.label "Edit As New Message">
+<!ENTITY editAsNewMsgCmd.accesskey "E">
+<!ENTITY editAsNewMsgCmd.key "e">
+<!ENTITY editDraftMsgCmd.label "Edit Draft Message">
+<!ENTITY editDraftMsgCmd.accesskey "D">
+<!ENTITY editTemplateMsgCmd.label "Edit Template">
+<!ENTITY editTemplateMsgCmd.accesskey "T">
+<!ENTITY newMsgFromTemplateCmd.label "New Message from Template">
+<!-- LOCALIZATION NOTE (newMsgFromTemplateCmd.keycode):
+ Do not localize VK_RETURN. -->
+<!ENTITY newMsgFromTemplateCmd.keycode "VK_RETURN">
+<!ENTITY createFilter.label "Create Filter From Message…">
+<!ENTITY createFilter.accesskey "a">
+<!ENTITY archiveMsgCmd.label "Archive">
+<!ENTITY archiveMsgCmd.accesskey "A">
+<!ENTITY archiveMsgCmd.key "a">
+<!ENTITY moveMsgToMenu.label "Move To">
+<!ENTITY moveMsgToMenu.accesskey "M">
+<!ENTITY copyMessageLocation.label "Copy Message Location">
+<!ENTITY copyMessageLocation.accesskey "M">
+<!ENTITY copyMsgToMenu.label "Copy To">
+<!ENTITY copyMsgToMenu.accesskey "C">
+<!ENTITY moveCopyMsgRecentMenu.label "Recent">
+<!ENTITY moveCopyMsgRecentMenu.accesskey "R">
+<!ENTITY killThreadMenu.label "Ignore Thread">
+<!ENTITY killThreadMenu.accesskey "I">
+<!ENTITY killThreadMenu.key "k">
+<!ENTITY killSubthreadMenu.label "Ignore Subthread">
+<!ENTITY killSubthreadMenu.accesskey "S">
+<!ENTITY killSubthreadMenu.key "k">
+<!ENTITY watchThreadMenu.label "Watch Thread">
+<!ENTITY watchThreadMenu.accesskey "W">
+<!ENTITY watchThreadMenu.key "w">
+<!ENTITY fileHereMenu.label "File Here">
+<!ENTITY fileHereMenu.accesskey "F">
+<!ENTITY copyHereMenu.label "Copy Here">
+<!ENTITY copyHereMenu.accesskey "C">
+<!ENTITY tagMenu.label "Tag">
+<!ENTITY tagMenu.accesskey "g">
+<!ENTITY tagCustomize.label "Customize…">
+<!ENTITY tagCustomize.accesskey "C">
+<!ENTITY markMenu.label "Mark">
+<!ENTITY markMenu.accesskey "k">
+<!ENTITY markThreadAsReadCmd.label "Thread As Read">
+<!ENTITY markThreadAsReadCmd.accesskey "T">
+<!ENTITY markThreadAsReadCmd.key "r">
+<!ENTITY markReadByDateCmd.label "As Read by Date…">
+<!ENTITY markReadByDateCmd.accesskey "D">
+<!ENTITY markReadByDateCmd.key "c">
+<!ENTITY markAllReadCmd.label "All Read">
+<!ENTITY markAllReadCmd.accesskey "A">
+<!ENTITY markAllReadCmd.key "c">
+<!ENTITY markAsJunkCmd.label "As Junk">
+<!ENTITY markAsJunkCmd.accesskey "J">
+<!ENTITY markAsJunkCmd.key "j">
+<!ENTITY markAsNotJunkCmd.label "As Not Junk">
+<!ENTITY markAsNotJunkCmd.accesskey "N">
+<!ENTITY markAsNotJunkCmd.key "j">
+<!ENTITY recalculateJunkScoreCmd.label "Run Junk Mail Controls">
+<!ENTITY recalculateJunkScoreCmd.accesskey "C">
+<!ENTITY markAsShowRemoteCmd.label "Show Remote Content">
+<!ENTITY markAsShowRemoteCmd.accesskey "e">
+<!ENTITY markAsShowRemoteCmd.key "r">
+<!ENTITY markAsNotPhishCmd.label "As Not Scam">
+<!ENTITY markAsNotPhishCmd.accesskey "S">
+<!ENTITY markAsNotPhishCmd.key "p">
+<!ENTITY openFeedMessage.label "Open Feed Message">
+<!ENTITY openFeedMessage.accesskey "O">
+<!ENTITY openFeedWebPageInWindow.label "Web Page in New Window">
+<!ENTITY openFeedWebPageInWindow.accesskey "W">
+<!ENTITY openFeedSummaryInWindow.label "Summary in New Window">
+<!ENTITY openFeedSummaryInWindow.accesskey "S">
+<!ENTITY openFeedWebPageInMP.label "Toggle Web Page and Summary in Message Pane">
+<!ENTITY openFeedWebPageInMP.accesskey "T">
+
+<!-- Tools Menu -->
+<!ENTITY searchMailCmd.label "Search Messages…">
+<!ENTITY searchMailCmd.accesskey "M">
+<!ENTITY searchMailCmd.key "s">
+<!ENTITY searchAddressesCmd.label "Search Addresses…">
+<!ENTITY searchAddressesCmd.accesskey "c">
+<!ENTITY filtersCmd.label "Message Filters…">
+<!ENTITY filtersCmd.accesskey "F">
+<!ENTITY filtersApply.label "Run Filters on Folder">
+<!ENTITY filtersApply.accesskey "R">
+<!ENTITY filtersApplyToSelection.label "Run Filters on Selected Messages">
+<!ENTITY filtersApplyToSelection.accesskey "g">
+<!ENTITY filtersApplyToMessage.label "Run Filters on Message">
+<!ENTITY filtersApplyToMessage.accesskey "g">
+<!ENTITY runJunkControls.label "Run Junk Mail Controls on Folder">
+<!ENTITY runJunkControls.accesskey "u">
+<!ENTITY deleteJunk.label "Delete Mail Marked as Junk in Folder">
+<!ENTITY deleteJunk.accesskey "e">
+<!ENTITY importCmd.label "Import…">
+<!ENTITY importCmd.accesskey "I">
+
+<!-- Folder Pane -->
+<!ENTITY nameColumn.label "Name">
+<!ENTITY unreadColumn.label "Unread">
+<!ENTITY totalColumn.label "Total">
+
+<!-- Toolbar items -->
+<!ENTITY getMsgButton.label "Get Msgs">
+<!ENTITY getAllNewMsgCmd.label "Get All New Messages">
+<!ENTITY getAllNewMsgCmd.accesskey "G">
+<!ENTITY newMsgButton.label "Compose">
+<!ENTITY newHTMLMessageCmd.label "Compose in HTML">
+<!ENTITY newHTMLMessageCmd.accesskey "H">
+<!ENTITY newPlainTextMessageCmd.label "Compose in Plain Text">
+<!ENTITY newPlainTextMessageCmd.accesskey "P">
+<!ENTITY replyButton.label "Reply">
+<!ENTITY replyAllButton.label "Reply All">
+<!ENTITY forwardButton.label "Forward">
+<!ENTITY fileButton.label "File">
+<!ENTITY nextButton.label "Next">
+<!ENTITY goBackButton.label "Go Back">
+<!ENTITY goForwardButton.label "Go Forward">
+<!ENTITY deleteButton.label "Delete">
+<!ENTITY undeleteButton.label "Undelete">
+<!ENTITY markButton.label "Mark">
+<!ENTITY printButton.label "Print">
+<!ENTITY stopButton.label "Stop">
+<!ENTITY junkButton.label "Junk">
+<!ENTITY notJunkButton.label "Not Junk">
+<!ENTITY searchButton.title "Advanced Search">
+
+<!-- Tooltips -->
+<!ENTITY menuBar.tooltip "Menu Bar">
+<!ENTITY mailToolbar.tooltip "Mail Toolbar">
+<!ENTITY searchToolbar.tooltip "Search Bar">
+<!ENTITY advancedButton.tooltip "Advanced message search">
+<!ENTITY getMsgButton.tooltip "Get new messages">
+<!ENTITY newMsgButton.tooltip "Create a new message">
+<!ENTITY replyButton.tooltip "Reply to the message">
+<!ENTITY replyAllButton.tooltip "Reply to sender and all recipients">
+<!ENTITY replyAllButtonNews.tooltip "Reply to sender and newsgroup">
+<!ENTITY forwardButton.tooltip "Forward selected message">
+<!ENTITY fileButton.tooltip "File selected message">
+<!ENTITY nextButton.tooltip "Move to the next unread message">
+<!ENTITY goBackButton.tooltip "Go back one message">
+<!ENTITY goForwardButton.tooltip "Go forward one message">
+<!ENTITY deleteButton.tooltip "Delete selected message or folder">
+<!ENTITY undeleteButton.tooltip "Undelete selected message">
+<!ENTITY markButton.tooltip "Mark messages">
+<!ENTITY printButton.tooltip "Print this message">
+<!ENTITY stopButton.tooltip "Stop the current transfer">
+<!ENTITY junkButton.tooltip "Mark the selected messages as junk">
+<!ENTITY notJunkButton.tooltip "Mark the selected messages as not junk">
+
+<!-- Remote Content Button Popup -->
+<!ENTITY remoteContentOptionsAllowForMsg.label "Show remote content in this message">
+<!ENTITY remoteContentOptionsAllowForMsg.accesskey "S">
+<!ENTITY editRemoteContentSettings.label "Edit permissions for remote content…">
+<!ENTITY editRemoteContentSettings.accesskey "E">
+
+<!-- Statusbar -->
+<!ENTITY statusText.label "Done">
+
+<!-- Thread Pane Context Menu -->
+<!ENTITY contextOpenNewWindow.label "Open Message in New Window">
+<!ENTITY contextOpenNewWindow.accesskey "W">
+<!ENTITY contextOpenNewTab.label "Open Message in New Tab">
+<!ENTITY contextOpenNewTab.accesskey "T">
+<!ENTITY contextEditMsgAsNew.label "Edit As New Message">
+<!ENTITY contextEditMsgAsNew.accesskey "E">
+<!ENTITY contextEditDraftMsg.label "Edit Draft Message">
+<!ENTITY contextEditTemplate.label "Edit Template">
+<!ENTITY contextEditTemplate.accesskey "T">
+<!ENTITY contextNewMsgFromTemplate.label "New Message from Template">
+<!ENTITY contextReplySender.label "Reply to Sender Only">
+<!ENTITY contextReplySender.accesskey "O">
+<!ENTITY contextReplyList.label "Reply to List">
+<!ENTITY contextReplyList.accesskey "y">
+<!ENTITY contextReplyNewsgroup.label "Reply to Newsgroup">
+<!ENTITY contextReplyNewsgroup.accesskey "y">
+<!ENTITY contextReplyAll.label "Reply to All">
+<!ENTITY contextReplyAll.accesskey "R">
+<!ENTITY contextReplySenderAndNewsgroup.label "Reply to Sender and Newsgroup">
+<!ENTITY contextReplySenderAndNewsgroup.accesskey "p">
+<!ENTITY contextForward.label "Forward">
+<!ENTITY contextForward.accesskey "F">
+<!ENTITY contextForwardAsAttachment.label "Forward as Attachments">
+<!ENTITY contextForwardAsAttachment.accesskey "o">
+<!ENTITY contextArchive.label "Archive">
+<!ENTITY contextArchive.accesskey "A">
+<!ENTITY contextMoveMsgMenu.label "Move To">
+<!ENTITY contextMoveMsgMenu.accesskey "M">
+<!ENTITY contextCopyMsgMenu.label "Copy To">
+<!ENTITY contextCopyMsgMenu.accesskey "C">
+<!ENTITY contextMoveCopyMsgRecentMenu.label "Recent">
+<!ENTITY contextMoveCopyMsgRecentMenu.accesskey "R">
+<!ENTITY contextMoveCopyMsgFavoritesMenu.label "Favorites">
+<!ENTITY contextMoveCopyMsgFavoritesMenu.accesskey "F">
+<!ENTITY contextSaveAs.label "Save As…">
+<!ENTITY contextSaveAs.accesskey "S">
+<!ENTITY contextPrint.label "Print…">
+<!ENTITY contextPrint.accesskey "P">
+<!ENTITY contextPrintPreview.label "Print Preview">
+<!ENTITY contextPrintPreview.accesskey "v">
+
+<!-- Folder Pane Context Menu -->
+<!ENTITY folderContextGetMessages.label "Get Messages for Account">
+<!ENTITY folderContextGetMessages.accesskey "G">
+<!ENTITY folderContextMarkAllFoldersRead.label "Mark All Folders Read">
+<!ENTITY folderContextMarkAllFoldersRead.accesskey "M">
+<!ENTITY folderContextOpenNewWindow.label "Open in New Mail Window">
+<!ENTITY folderContextOpenNewWindow.accesskey "W">
+<!ENTITY folderContextOpenNewTab.label "Open in New Tab">
+<!ENTITY folderContextOpenNewTab.accesskey "T">
+<!ENTITY folderContextRename.label "Rename">
+<!ENTITY folderContextRename.accesskey "R">
+<!ENTITY folderContextRemove.label "Delete">
+<!ENTITY folderContextRemove.accesskey "D">
+<!ENTITY folderContextCompact.label "Compact This Folder">
+<!ENTITY folderContextCompact.accesskey "F">
+<!ENTITY folderContextEmptyTrash.label "Empty Trash">
+<!ENTITY folderContextEmptyTrash.accesskey "y">
+<!ENTITY folderContextEmptyJunk.label "Empty Junk">
+<!ENTITY folderContextEmptyJunk.accesskey "J">
+<!ENTITY folderContextSendUnsentMessages.label "Send Unsent Messages">
+<!ENTITY folderContextSendUnsentMessages.accesskey "d">
+<!ENTITY folderContextUnsubscribe.label "Unsubscribe">
+<!ENTITY folderContextUnsubscribe.accesskey "U">
+<!ENTITY folderContextMarkNewsgroupRead.label "Mark Newsgroup Read">
+<!ENTITY folderContextMarkNewsgroupRead.accesskey "k">
+<!ENTITY folderContextMarkMailFolderRead.label "Mark Folder Read">
+<!ENTITY folderContextMarkMailFolderRead.accesskey "k">
+<!ENTITY folderContextNew.label "New Subfolder…">
+<!ENTITY folderContextNew.accesskey "N">
+<!ENTITY folderContextSubscribe.label "Subscribe…">
+<!ENTITY folderContextSubscribe.accesskey "b">
+<!ENTITY folderContextSearchMessages.label "Search Messages…">
+<!ENTITY folderContextSearchMessages.accesskey "S">
+<!ENTITY folderContextProperties.label "Properties…">
+<!ENTITY folderContextProperties.accesskey "P">
+<!ENTITY folderContextFavoriteFolder.label "Favorite Folder">
+<!ENTITY folderContextFavoriteFolder.accesskey "a">
+<!ENTITY folderContextSettings.label "Settings…">
+<!ENTITY folderContextSettings.accesskey "e">
+
+<!-- focusSearchInput.key also used by addressbook -->
+<!ENTITY focusSearchInput.key "k">
+<!ENTITY advancedButton.label "Advanced…">
+<!ENTITY advancedButton.accesskey "A">
+<!ENTITY searchButton.label "Search Messages…">
+<!ENTITY searchButton.accesskey "S">
+
+<!ENTITY all.label "All">
+<!ENTITY all.accesskey "A">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/messenger.properties b/comm/suite/locales/en-US/chrome/mailnews/messenger.properties
new file mode 100644
index 0000000000..2553178013
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/messenger.properties
@@ -0,0 +1,521 @@
+# 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/.
+
+# The following are used by the messenger application
+
+# LOCALIZATION NOTE(statusMessage):
+# Do not translate the words %1$S and %2$S below. Place the word %1$S where the
+# account name should appear and %2$S where the status message should appear.
+# EXAMPLE: Jim's Account: Downloading messages...
+statusMessage=%1$S: %2$S
+
+renameFolder=Rename Folder…
+compactFolders=Compact This Folder;Compact These Folders
+removeAccount=Delete Account…
+removeFolder=Delete Folder
+newFolderMenuItem=Folder…
+newSubfolderMenuItem=Subfolder…
+newFolder=New Folder…
+newSubfolder=New Subfolder…
+markFolderRead=Mark Folder Read;Mark Folders Read
+markNewsgroupRead=Mark Newsgroup Read;Mark Newsgroups Read
+folderProperties=Folder Properties
+getMessages=Get Messages
+getMessagesFor=Get Messages For Account
+# LOCALIZATION NOTE (getNextNewsMessages): Semi-colon list of plural forms.
+# #1 is the number of news messages to get.
+getNextNewsMessages=Get Next #1 News Message;Get Next #1 News Messages
+advanceNextPrompt=Advance to next unread message in %S?
+titleNewsPreHost=on
+titleMailPreHost=for
+replyToSender=Reply to Sender
+reply=Reply
+EMLFiles=Mail Files
+OpenEMLFiles=Open Message
+# LOCALIZATION NOTE(defaultSaveMessageAsFileName): Do not translate ".eml"
+# in the line below. Also, the complete file name should be 8.3.
+defaultSaveMessageAsFileName=message.eml
+SaveMailAs=Save Message As
+SaveAttachment=Save Attachment
+SaveAllAttachments=Save All Attachments
+DetachAttachment=Detach Attachment
+DetachAllAttachments=Detach All Attachments
+ChooseFolder=Choose Folder
+LoadingMessageToPrint=Loading message to print…
+MessageLoaded=Message loaded…
+PrintingMessage=Printing message…
+PrintPreviewMessage=Print Previewing message…
+PrintingContact=Printing contact…
+PrintPreviewContact=Print Previewing contact…
+PrintingAddrBook=Printing AddressBook…
+PrintPreviewAddrBook=Print Previewing AddressBook…
+PrintingComplete=Done.
+PreviewTitle=%S - %S
+LoadingMailMsgForPrint=(Loading content for Printing)
+LoadingMailMsgForPrintPreview=(Loading content for Print Preview)
+saveAttachmentFailed=Unable to save the attachment. Please check your file name and try again later.
+saveMessageFailed=Unable to save the message. Please check your file name and try again later.
+fileExists=%S already exists. Do you want to replace it?
+# LOCALIZATION NOTE(failedToReadFile): %1$S is replaced by the file name, %2$S is replaced by the reason the file load failed.
+failedToReadFile=Failed to read file: %1$S reason: %2$S
+
+downloadingNewsgroups=Downloading Newsgroups for Offline Use
+downloadingMail=Downloading Mail for Offline Use
+sendingUnsent=Sending Unsent Messages
+
+folderExists=A folder with that name already exists. Please enter a different name.
+# LOCALIZATION NOTE(confirmDuplicateFolderRename): %1$S is name of folder being moved, %2$S is parent folder name, %3$S is proposed new folder name
+confirmDuplicateFolderRename=A subfolder with the name '%1$S' already exists in the folder '%2$S'. Would you like to move this folder using the new name '%3$S'?
+folderCreationFailed=The folder could not be created because the folder name you specified contains an unrecognized character. Please enter a different name and try again.
+
+compactingFolder=Compacting folder %S…
+# LOCALIZATION NOTE(compactingDone): %1$S is the compaction gain.
+compactingDone=Done compacting (approx. %1$S saved).
+autoCompactAllFoldersTitle=Compact Folders
+# LOCALIZATION NOTE(autoCompactAllFoldersMsg): %1$S will be replaced by size gain of the compaction (including the unit), %2$S will be replaced by application name
+autoCompactAllFoldersMsg=The messages you have deleted can be purged from disk. This operation will save about %1$S of disk space. Select the option below to let %2$S do this automatically without asking you.
+autoCompactNeverAskCheckbox=Remove message deletions automatically and do not ask me.
+proceedButton=&Proceed
+
+confirmFolderDeletionForFilter=Deleting the folder '%S' will disable its associated filter(s). Are you sure you want to delete the folder?
+alertFilterChanged=Filters associated with this folder will be updated.
+filterDisabled=The folder '%S' could not be found, so filter(s) associated with this folder will be disabled. Verify that the folder exists, and that filters point to a valid destination folder.
+filterFolderDeniedLocked=The messages could not be filtered to folder '%S' because another operation is in progress.
+parsingFolderFailed=Unable to open the folder %S because it is in use by some other operation. Please wait for that operation to finish and then select the folder again.
+deletingMsgsFailed=Unable to delete messages in folder %S because it is in use by some other operation. Please wait for that operation to finish and then try again.
+alertFilterCheckbox=Do not warn me again.
+compactFolderDeniedLock=The folder '%S' cannot be compacted because another operation is in progress. Please try again later.
+compactFolderWriteFailed=The folder '%S' could not be compacted because writing to folder failed. Verify that you have enough disk space, and that you have write privileges to the file system, then try again.
+compactFolderInsufficientSpace=Some folders (e.g. '%S') cannot be compacted because there is not enough free disk space. Please delete some files and try again.
+filterFolderHdrAddFailed=The messages could not be filtered to folder '%S' because adding a message to it failed. Verify that the folder is displaying properly or try to repair it from the folder properties.
+filterFolderWriteFailed=The messages could not be filtered to folder '%S' because writing to folder failed. Verify that you have enough disk space, and that you have write privileges to the file system, then try again.
+copyMsgWriteFailed=The messages could not be moved or copied to folder '%S' because writing to folder failed. To gain disk space, from the File menu, first choose Empty Trash, and then choose Compact Folders, and then try again.
+cantMoveMsgWOBodyOffline=While working offline, you cannot move or copy messages that were not downloaded for offline use. From the Mail window, open the File menu, choose Offline, then uncheck Work Offline, and then try again.
+operationFailedFolderBusy=The operation failed because another operation is using the folder. Please wait for that operation to finish and then try again.
+folderRenameFailed=The folder could not be renamed. Perhaps the folder is being reparsed, or the new name is not a valid folder name.
+# LOCALIZATION NOTE(verboseFolderFormat): %1$S is folder name, %2$S is server name
+verboseFolderFormat=%1$S on %2$S
+# LOCALIZATION NOTE(filterFolderTruncateFailed): %1$S is replaced by the folder name, %2$S is replaced by the brandShortName
+filterFolderTruncateFailed=There was an error truncating the Inbox after filtering a message to folder '%1$S'. You may need to shutdown %2$S and delete INBOX.msf.
+
+mailboxTooLarge=The folder %S is full, and can't hold any more messages. To make room for more messages, delete any old or unwanted mail and compact the folder.
+outOfDiskSpace=There is not enough disk space to download new messages. Try deleting old mail, emptying the Trash folder, and compacting your mail folders, and then try again.
+errorGettingDB=Unable to open the summary file for '%S'. Perhaps there was an error on disk, or the full path is too long.
+
+defaultServerTag=(Default)
+
+# Used in message database list view to provide a text value for graphic based cells.
+messageUnread=Unread
+messageHasFlag=Flagged
+messageHasAttachment=Has Attachment
+messageJunk=Junk
+messageExpanded=Expanded
+messageCollapsed=Collapsed
+
+# Used in the SMTP Account Settings panel when a server value has no properties
+smtpServerList-NotSpecified=<not specified>
+smtpServer-ConnectionSecurityType-0=None
+smtpServer-ConnectionSecurityType-1=STARTTLS, if available
+smtpServer-ConnectionSecurityType-2=STARTTLS
+smtpServer-ConnectionSecurityType-3=SSL/TLS
+smtpServers-confirmServerDeletionTitle=Delete Server
+smtpServers-confirmServerDeletion=Are you sure you want to delete the server: \n %S?
+
+# Account Settings - Both Incoming and SMTP server
+authNo=No authentication
+authOld=Password, original method (insecure)
+authPasswordCleartextInsecurely=Password, transmitted insecurely
+authPasswordCleartextViaSSL=Normal password
+authPasswordEncrypted=Encrypted password
+authKerberos=Kerberos / GSSAPI
+authExternal=TLS Certificate
+authNTLM=NTLM
+authOAuth2=OAuth2
+authAnySecure=Any secure method (deprecated)
+authAny=Any method (insecure)
+
+# OAuth2 window title
+# LOCALIZATION NOTE(oauth2WindowTitle):
+# %1$S is the username (or full email address) used for authentication.
+# %2$S is the hostname of the account being authenticated.
+oauth2WindowTitle=Enter credentials for %1$S on %2$S
+
+# LOCALIZATION NOTE(serverType-nntp): Do not translate "NNTP" in the line below
+serverType-nntp=News Server (NNTP)
+# LOCALIZATION NOTE(serverType-pop3): Do not translate "POP" in the line below
+serverType-pop3=POP Mail Server
+# LOCALIZATION NOTE(serverType-imap): Do not translate "IMAP" in the line below
+serverType-imap=IMAP Mail Server
+serverType-none=Local Mail Store
+
+sizeColumnTooltip2=Sort by size
+sizeColumnHeader=Size
+linesColumnTooltip2=Sort by lines
+linesColumnHeader=Lines
+
+# status feedback stuff
+documentDone=
+documentLoading=Loading Message…
+
+unreadMsgStatus=Unread: %S
+selectedMsgStatus=Selected: %S
+totalMsgStatus=Total: %S
+
+# localized folder names
+
+localFolders=Local Folders
+
+# LOCALIZATION NOTE (inboxFolderName): OK to translate all foldernames, bugzilla #57440 & bugzilla #23625 fixed
+inboxFolderName=Inbox
+trashFolderName=Trash
+sentFolderName=Sent
+draftsFolderName=Drafts
+templatesFolderName=Templates
+outboxFolderName=Outbox
+junkFolderName=Junk
+archivesFolderName=Archives
+
+# "Normal" priority is often blank,
+# depending on the consumers of these strings
+priorityLowest=Lowest
+priorityLow=Low
+priorityNormal=Normal
+priorityHigh=High
+priorityHighest=Highest
+
+#Group by date thread pane titles
+today=Today
+yesterday=Yesterday
+lastWeek=Last Week
+last7Days=Last 7 Days
+twoWeeksAgo=Two Weeks Ago
+last14Days=Last 14 Days
+older=Old Mail
+futureDate=Future
+
+#Grouped By Tags
+untaggedMessages=Untagged Messages
+
+# Grouped by status
+messagesWithNoStatus=No Status
+
+#Grouped by priority
+noPriority=No Priority
+
+#Grouped by has attachments
+noAttachments=No Attachments
+attachments=Attachments
+
+#Grouped by flagged
+notFlagged=Not Flagged
+groupFlagged=Flagged
+
+# defaults descriptions for tag prefs listed in mailnews.js
+# (we keep the .labels. names for backwards compatibility)
+mailnews.tags.remove=Remove All Tags
+mailnews.labels.description.1=Important
+mailnews.labels.description.2=Work
+mailnews.labels.description.3=Personal
+mailnews.labels.description.4=To Do
+mailnews.labels.description.5=Later
+
+# Format definition tag menu texts.
+# This is necessary in order to get the accesskeys to be the on the first
+# character of the menu text instead of after the menu text.
+# If a key definition exists for the tag at index n, that key's key will be
+# taken as the accesskey, eg.
+# <key id="key_tag3" key="&tagCmd3.key;" oncommand="ToggleMessageTagKey(3);"/>
+# makes the third tag have the accesskey &tagCmd3.key;.
+# In the menuitem's label, this accesskey appears at %1$S below; %2$S will be
+# replaced by the tag label.
+mailnews.tags.format=%1$S %2$S
+
+replied=Replied
+forwarded=Forwarded
+new=New
+read=Read
+flagged=Flagged
+
+# for junk status picker in search and mail views
+junk=Junk
+
+# for junk score origin picker in search and mail views
+junkScoreOriginPlugin=Plugin
+junkScoreOriginFilter=Filter
+junkScoreOriginWhitelist=Whitelist
+junkScoreOriginUser=User
+junkScoreOriginImapFlag=IMAP Flag
+
+# for the has attachment picker in search and mail views
+hasAttachments=Has Attachments
+
+# for the Tag picker in search and mail views.
+tag=Tag
+
+# LOCALIZATION NOTE(andOthers):
+# for multiple authors, add this abbreviation to the first author to indicate
+# there are more; for the From column in the threadpane message list.
+andOthers=et al.
+
+# mailnews.js
+mailnews.send_default_charset=UTF-8
+mailnews.view_default_charset=ISO-8859-1
+
+# whether to generate display names in last first order
+# LOCALIZATION NOTE(mail.addr_book.displayName.lastnamefirst):
+# the only valid values are: true OR false (choose from the untranslated English words)
+mail.addr_book.displayName.lastnamefirst=false
+
+# whether to also show phonetic fields in the addressbook
+# LOCALIZATION NOTE(mail.addr_book.show_phonetic_fields):
+# the only valid values are: true OR false (choose from the untranslated English words)
+mail.addr_book.show_phonetic_fields=false
+
+# valid format options are:
+# 1: yyyy/mm/dd
+# 2: yyyy/dd/mm
+# 3: mm/dd/yyyy
+# 4: mm/yyyy/dd
+# 5: dd/mm/yyyy
+# 6: dd/yyyy/mm
+#
+# 0: auto-detect the current locale format
+# a separator has to be either '/', '-', '.' and the year in Chistian year
+# otherwise mm/dd/yyyy (option 3) is used
+#
+mailnews.search_date_format=0
+# separator for search date (e.g. "/", "-"), or empty when search_date_format is zero
+mailnews.search_date_separator=
+# leading zeros for day and month values, not used if mailnews.search_date_format is not zero
+mailnews.search_date_leading_zeros=true
+
+# offline msg
+nocachedbodybody2=The body of this message has not been downloaded from \
+the server for reading offline. To read this message, \
+you must reconnect to the network, choose Offline from \
+the File menu and then uncheck Work Offline. \
+In the future, you can select which messages or folders to read offline. To do \
+this, choose Offline from the file menu and then select Download/Sync Now. \
+You can adjust the Disk Space preference to prevent the downloading of large \
+messages.
+
+# accountCentral
+mailnews.account_central_page.url=chrome://messenger/content/msgAccountCentral.xul
+# LOCALIZATION NOTE(acctCentralTitleFormat): %1$S is brand, %2$S is account type, %3$S is account name
+acctCentralTitleFormat=%1$S %2$S - %3$S
+mailAcctType=Mail
+newsAcctType=News
+feedsAcctType=Feeds
+
+# LOCALIZATION NOTE(nocachedbodytitle): Do not translate "<TITLE>" or "</TITLE>" in the line below
+nocachedbodytitle=<TITLE>Go Online to View This Message</TITLE>\n
+
+# mailWindowOverlay.js
+confirmUnsubscribeTitle=Confirm Unsubscribe
+confirmUnsubscribeText=Are you sure you want to unsubscribe from %S?
+confirmUnsubscribeManyText=Are you sure you want to unsubscribe from these newsgroups?
+
+# msgHdrViewOverlay.js
+deleteAttachments=The following attachments will be permanently deleted from this message:\n%S\nThis action cannot be undone. Do you wish to continue?
+detachAttachments=The following attachments have been successfully saved and will now be permanently deleted from this message:\n%S\nThis action cannot be undone. Do you wish to continue?
+deleteAttachmentFailure=Failed to delete the selected attachments.
+
+# This is the format for prepending accesskeys to the
+# each of the attachments in the file|attachments menu:
+# ie: 1 file.txt
+# 2 another file.txt
+attachmentDisplayNameFormat=%S %S
+
+# This is the heading for the attachment summary when printing an email
+attachmentsPrintHeader=Attachments:
+
+# Connection Error Messages
+# LOCALIZATION NOTE(unknownHostError): %S is the server name
+unknownHostError=Failed to connect to server %S.
+# LOCALIZATION NOTE(connectionRefusedError): %S is the server name
+connectionRefusedError=Could not connect to server %S; the connection was refused.
+# LOCALIZATION NOTE(netTimeoutError): %S is the server name
+netTimeoutError=Connection to server %S timed out.
+# LOCALIZATION NOTE(netResetError): %S is the server name
+netResetError=Connection to server %S was reset.
+# LOCALIZATION NOTE(netInterruptError): %S is the server name
+netInterruptError=Connection to server %S was interrupted.
+
+# LOCALIZATION NOTE(biffNotification): %1$S is the number of new messages
+biffNotification_message=has %1$S new message
+biffNotification_messages=has %1$S new messages
+
+# LOCALIZATION NOTE(macBiffNotification is Mac only):
+# %1$S is the number of new messages
+# %2$S is a list of names and/or email addresses separated by biffNotification_separator
+# %3$S is the number of new messages not displayed in the biff alert
+macBiffNotification_message=%1$S new message from %2$S.
+macBiffNotification_messages=%1$S new messages from %2$S.
+macBiffNotification_messages_extra=%1$S new messages from %2$S and %3$S more.
+# Used to separate names/email addresses in a list. Note the trailing space ', '
+macBiffNotification_separator=,\u0020
+
+# LOCALIZATION NOTE(newMailNotification_message): %1$S is the name of the account %2$S is the number of new messages
+newMailNotification_message=%1$S received %2$S new message
+
+# LOCALIZATION NOTE(newMailNotification_messages): %1$S is the name of the account %2$S is the number of new messages
+newMailNotification_messages=%1$S received %2$S new messages
+
+# LOCALIZATION NOTE(newMailAlert_message):
+# Semi-colon list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the name of the account, #2 is the number of new messages
+newMailAlert_message=#1 received #2 new message;#1 received #2 new messages
+
+# For the Quota tab in the mail folder properties dialog
+quotaPercentUsed=%S%% full
+
+# for message views
+confirmViewDeleteTitle=Confirm
+confirmViewDeleteMessage=Are you sure you want to delete this view?
+
+# for virtual folders
+confirmSavedSearchDeleteTitle=Delete Saved Search
+confirmSavedSearchDeleteMessage=Are you sure you want to delete this saved search?
+confirmSavedSearchDeleteButton=&Delete Saved Search
+
+## @name ENTER_PASSWORD_PROMPT
+## @loc None
+# LOCALIZATION NOTE (passwordPrompt): Do not translate the word %S below.
+# Place the word "%S" in your translation where the email address
+# or the username should appear
+passwordPrompt=Enter your password for %1$S on %2$S:
+
+## @name ENTER_PASSWORD_PROMPT_TITLE
+## @loc None
+passwordTitle=Mail Server Password Required
+
+# for checking if the user really wants to open lots of messages in separate windows.
+openWindowWarningTitle=Confirm
+# LOCALIZATION NOTE (openWindowWarningConfirmation): Semi-colon list of plural forms.
+# #1 is the number of messages the user is attempting to open.
+openWindowWarningConfirmation=Opening #1 message may be slow. Continue?;Opening #1 messages may be slow. Continue?
+
+# for warning the user that a tag he's trying to create already exists
+tagExists=A tag with that name already exists!
+
+# for checking if the user really wants to delete the adaptive filter training set
+confirmResetJunkTrainingTitle=Confirm
+confirmResetJunkTrainingText=Are you sure you want to reset the adaptive filter training data?
+
+# for the virtual folder list dialog title
+# %S is the name of the saved search folder
+editVirtualFolderPropertiesTitle=Edit Saved Search Properties for %S
+# LOCALIZATION NOTE (foldersChosen): #1 number of chosen folders
+virtualFolderSourcesChosen=#1 folder chosen;#1 folders chosen
+
+#alert to inform the user to choose one or more folders to search for a saved search folder
+alertNoSearchFoldersSelected=You must choose at least one folder to search for the saved search folder.
+
+# These are displayed in the message and folder pane windows
+# LOCALIZATION NOTE %.*f is the abbreviated size in the appropriate units
+byteAbbreviation2=%.*f bytes
+kiloByteAbbreviation2=%.*f KB
+megaByteAbbreviation2=%.*f MB
+gigaByteAbbreviation2=%.*f GB
+teraByteAbbreviation2=%.*f TB
+petaByteAbbreviation2=%.*f PB
+
+## LOCALIZATION NOTE(folderWithAccount):
+## This is used to show folder name together with an account name.
+## %1$S = folder name
+## %2$S = account name
+folderWithAccount=%1$S - %2$S
+## LOCALIZATION NOTE(folderWithUnreadMsgs):
+## This is a concatenation of two strings to compose a folder label with unread messages.
+## %1$S = folder name
+## %2$S = count of unread messages
+folderWithUnreadMsgs=%1$S (%2$S)
+## LOCALIZATION NOTE(summarizedValue):
+## This string shows an indication that the value shown is actually a summary
+## accumulated from all subfolders.
+## %S = summarized value from all subfolders
+folderSummarizedSymbolValue=â–¾%S
+## LOCALIZATION NOTE(subfoldersExplanation):
+## This is a tooltip message shown on the values in the numeric folder pane columns.
+## %1$S = is the count of messages having the respective property, found in the folder under mouse cursor
+## %2$S = is the count of messages having the respective property, found in subfolders of the folder under mouse cursor
+subfoldersExplanation=%1$S in this folder, %2$S in subfolders
+
+# Error message if message for a message id wasn't found
+errorOpenMessageForMessageIdTitle=Error opening message-id
+errorOpenMessageForMessageIdMessage=Message for message-id %S not found
+
+# Warnings to alert users about phishing urls
+confirmPhishingTitle=Email Scam Alert
+#LOCALIZATION NOTE %1$S is the brand name, %2$S is the host name of the url being visited
+confirmPhishingUrl1=%1$S thinks this website is suspicious! It may be trying to impersonate the web page you want to visit. Most legitimate websites use names instead of numbers. Are you sure you want to visit %2$S?
+confirmPhishingUrl2=%1$S thinks this website is suspicious! It may be trying to impersonate the web page you want to visit. Are you sure you want to visit %2$S?
+
+#LOCALIZATION NOTE(mdnBarMessageNormal) %1$S is the name of the sender
+mdnBarMessageNormal=%1$S has asked to be notified when you read this message.
+#LOCALIZATION NOTE(mdnBarMessageAddressDiffers) %1$S is the name of the sender, %2$S is the address(es) to send return receipt to
+mdnBarMessageAddressDiffers=%1$S has asked to be notified at %2$S when you read this message.
+
+# mailCommands.js
+emptyJunkTitle=Confirm
+emptyJunkMessage=Are you sure you want to permanently delete all messages and subfolders in the Junk folder?
+emptyJunkDontAsk=Don't ask me again.
+emptyTrashTitle=Confirm
+emptyTrashMessage=Are you sure you want to permanently delete all messages and subfolders in the Trash folder?
+emptyTrashDontAsk=Don't ask me again.
+
+# junkCommands.js
+junkAnalysisPercentComplete=Junk analysis %S complete
+processingJunkMessages=Processing Junk Messages
+
+# Messenger bootstrapping messages
+fileNotFoundTitle = File Not Found
+#LOCALIZATION NOTE(fileNotFoundMsg): %S is the filename
+fileNotFoundMsg = The file %S does not exist.
+
+confirmMsgDelete.title=Confirm Deletion
+confirmMsgDelete.collapsed.desc=This will delete messages in collapsed threads. Are you sure you want to continue?
+confirmMsgDelete.deleteNoTrash.desc=This will delete messages immediately, without saving a copy to Trash. Are you sure you want to continue?
+confirmMsgDelete.deleteFromTrash.desc=This will permanently delete messages from Trash. Are you sure you want to continue?
+confirmMsgDelete.dontAsk.label=Don't ask me again.
+confirmMsgDelete.delete.label=Delete
+
+mailServerLoginFailedTitle=Login Failed
+# LOCALIZATION NOTE (mailServerLoginFailedTitleWithAccount):
+# "%S" is the account name.
+mailServerLoginFailedTitleWithAccount=Login to account "%S" failed
+# LOCALIZATION NOTE (mailServerLoginFailed2):
+# %1$S is the host name of the server, %2$S is the user name.
+mailServerLoginFailed2=Login to server %1$S with username %2$S failed.
+mailServerLoginFailedRetryButton=&Retry
+mailServerLoginFailedEnterNewPasswordButton=&Enter New Password
+
+confirmMarkAllFoldersReadTitle=Mark All Folders Read
+confirmMarkAllFoldersReadMessage=Are you sure you want to mark all messages in all folders of this account as read?
+
+# LOCALIZATION NOTE (junkBarMessage): %S is the brandname
+junkBarMessage=%S regards this message as junk.
+junkBarButton=Not Junk
+junkBarButtonKey=N
+junkBarInfoButton=?
+junkBarInfoButtonKey=?
+# LOCALIZATION NOTE (remoteContentBarMessage): %S is the brandname
+remoteContentBarMessage=To protect your privacy, %S has blocked remote content in this message.
+remoteContentPrefLabel=Options
+remoteContentPrefAccesskey=O
+# LOCALIZATION NOTE(remoteContentAllow): %S is host name
+remoteContentAllow=Allow remote content for %S
+
+# LOCALIZATION NOTE (phishingBarMessage): %S is the brandname
+phishingBarMessage=%S regards this message as an e-mail scam.
+phishingBarIgnoreButton=Ignore Warning
+phishingBarIgnoreButtonKey=I
+mdnBarMessage=The sender of this message has asked to be notified when you read this message. Do you wish to notify the sender?
+mdnBarIgnoreButton=Ignore Request
+mdnBarIgnoreButtonKey=I
+mdnBarSendReqButton=Send Receipt
+mdnBarSendReqButtonKey=S
+# LOCALIZATION NOTE (saveAsType): replace %S with the extension of the file to be saved.
+saveAsType=%S file
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mime.properties b/comm/suite/locales/en-US/chrome/mailnews/mime.properties
new file mode 100644
index 0000000000..152ec7fdde
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mime.properties
@@ -0,0 +1,156 @@
+# 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/.
+
+#
+# The following are used by libmime to emit header display in HTML
+#
+
+# Mail subject
+## @name MIME_MHTML_SUBJECT
+## @loc None
+1000=Subject
+
+# Resent-Comments
+## @name MIME_MHTML_RESENT_COMMENTS
+## @loc
+1001=Resent-Comments
+
+# Resent-Date
+## @name MIME_MHTML_RESENT_DATE
+## @loc
+1002=Resent-Date
+
+# Resent-Sender
+## @name MIME_MHTML_RESENT_SENDER
+## @loc
+1003=Resent-Sender
+
+# Resent-From
+## @name MIME_MHTML_RESENT_FROM
+## @loc
+1004=Resent-From
+
+# Resent-To
+## @name MIME_MHTML_RESENT_TO
+## @loc
+1005=Resent-To
+
+# Resent-CC
+## @name MIME_MHTML_RESENT_CC
+## @loc
+# LOCALIZATION NOTE (1006): Do not translate "CC" below.
+1006=Resent-CC
+
+# Date
+## @name MIME_MHTML_DATE
+## @loc
+1007=Date
+
+# Sender
+## @name MIME_MHTML_SENDER
+## @loc
+1008=Sender
+
+# From
+## @name MIME_MHTML_FROM
+## @loc
+1009=From
+
+# Reply-To
+## @name MIME_MHTML_REPLY_TO
+## @loc
+1010=Reply-To
+
+# Organization
+## @name MIME_MHTML_ORGANIZATION
+## @loc
+1011=Organization
+
+# To
+## @name MIME_MHTML_TO
+## @loc
+1012=To
+
+# CC
+## @name MIME_MHTML_CC
+## @loc
+# LOCALIZATION NOTE (1013): Do not translate "CC" below.
+1013=CC
+
+# Newsgroups
+## @name MIME_MHTML_NEWSGROUPS
+## @loc
+1014=Newsgroups
+
+# Followup-To
+## @name MIME_MHTML_FOLLOWUP_TO
+## @loc
+1015=Followup-To
+
+# References
+## @name MIME_MHTML_REFERENCES
+## @loc
+1016=References
+
+# Message ID
+## @name MIME_MHTML_MESSAGE_ID
+## @loc
+1021=Message-ID
+
+# BCC
+## @name MIME_MHTML_BCC
+## @loc
+1023=BCC
+
+# Link to doc
+## @name MIME_MSG_LINK_TO_DOCUMENT
+## @loc
+1026=Link to Document
+
+# Get Doc info
+## @name MIME_MSG_DOCUMENT_INFO
+## @loc
+1027=<B>Document Info:</B>
+
+# Msg Attachment
+## @name MIME_MSG_ATTACHMENT
+## @loc
+1028=Attachment
+
+# default attachment name
+## @name MIME_MSG_DEFAULT_ATTACHMENT_NAME
+## @loc
+# LOCALIZATION NOTE (1040): Do not translate "%s" below.
+# Place the %s where you wish the part number of the attachment to appear
+1040=Part %s
+
+# default forwarded message prefix
+## @name MIME_FORWARDED_MESSAGE_HTML_USER_WROTE
+## @loc
+1041=-------- Original Message --------
+
+# Partial Message Truncated
+## @name MIME_MSG_PARTIAL_TRUNCATED
+## @loc
+MIME_MSG_PARTIAL_TRUNCATED=Truncated!
+
+# Partial Message Truncated Explanation
+## @name MIME_MSG_PARTIAL_TRUNCATED_EXPLANATION
+## @loc
+MIME_MSG_PARTIAL_TRUNCATED_EXPLANATION=This message exceeded the Maximum Message Size set in Account Settings, so we have only downloaded the first few lines from the mail server.
+
+# Partial Message Not Downloaded
+## @name MIME_MSG_PARTIAL_NOT_DOWNLOADED
+## @loc
+MIME_MSG_PARTIAL_NOT_DOWNLOADED=Not Downloaded
+
+# Partial Message Not Downloaded Explanation
+## @name MIME_MSG_PARTIAL_NOT_DOWNLOADED_EXPLANATION
+## @loc
+MIME_MSG_PARTIAL_NOT_DOWNLOADED_EXPLANATION=Only the headers for this message were downloaded from the mail server.
+
+# MIME_MSG_PARTIAL_CLICK_FOR_REST
+## @name MIME_MSG_PARTIAL_CLICK_FOR_REST
+## @loc
+MIME_MSG_PARTIAL_CLICK_FOR_REST=Download the rest of the message.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/mimeheader.properties b/comm/suite/locales/en-US/chrome/mailnews/mimeheader.properties
new file mode 100644
index 0000000000..8a5c0c04d7
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/mimeheader.properties
@@ -0,0 +1,39 @@
+# 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/.
+
+#
+# The following are used by libmime for header display in XML & HTML
+#
+TO=To
+# LOCALIZATION NOTE (BCC); DONT_TRANSLATE
+BCC=BCC
+# LOCALIZATION NOTE (CC); DONT_TRANSLATE
+CC=CC
+DATE=Date
+DISTRIBUTION=Distribution
+# LOCALIZATION NOTE (FCC); DONT_TRANSLATE
+FCC=FCC
+FOLLOWUP-TO=Followup-To
+FROM=From
+STATUS=Status
+LINES=Lines
+MESSAGE-ID=Message-ID
+MIME-VERSION=MIME-Version
+NEWSGROUPS=Newsgroups
+ORGANIZATION=Organization
+REFERENCES=References
+REPLY-TO=Reply-To
+RESENT-COMMENTS=Resent-Comments
+RESENT-DATE=Resent-Date
+RESENT-FROM=Resent-From
+RESENT-MESSAGE-ID=Resent-Message-ID
+RESENT-SENDER=Resent-Sender
+RESENT-TO=Resent-To
+# LOCALIZATION NOTE (RESENT-CC); Do not translate "CC"
+RESENT-CC=Resent-CC
+SENDER=Sender
+SUBJECT=Subject
+APPROVED-BY=Approved-By
+USER-AGENT=User-Agent
+FILENAME=Filename
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgAccountCentral.dtd b/comm/suite/locales/en-US/chrome/mailnews/msgAccountCentral.dtd
new file mode 100644
index 0000000000..e7c99cce13
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgAccountCentral.dtd
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<!ENTITY emailSectionHdr.label "Email">
+<!ENTITY readMsgsLink.label "Read messages">
+<!ENTITY composeMsgLink.label "Compose a new message">
+
+<!ENTITY newsSectionHdr.label "Newsgroups">
+<!ENTITY subscribeNewsLink.label "Manage newsgroup subscriptions">
+
+<!ENTITY accountsSectionHdr.label "Accounts">
+<!ENTITY subscribeImapFolders.label "Manage folder subscriptions">
+<!ENTITY settingsLink.label "View settings for this account">
+<!ENTITY newAcctLink.label "Create a new account">
+
+<!ENTITY advFeaturesSectionHdr.label "Advanced Features">
+<!ENTITY searchMsgsLink.label "Search messages">
+<!ENTITY filtersLink.label "Manage message filters">
+<!ENTITY junkSettings.label "Junk mail settings">
+<!ENTITY offlineLink.label "Offline settings">
+
+<!ENTITY feedsSectionHdr.label "Feeds">
+<!ENTITY subscribeFeeds.label "Manage subscriptions">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgHdrViewOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/msgHdrViewOverlay.dtd
new file mode 100644
index 0000000000..071ad1de19
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgHdrViewOverlay.dtd
@@ -0,0 +1,48 @@
+<!-- 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/. -->
+
+<!ENTITY toField.label "To: ">
+<!ENTITY fromField.label "From: ">
+<!ENTITY senderField.label "Sender: ">
+<!ENTITY organizationField.label "Organization: ">
+<!ENTITY replyToField.label "Reply-To: ">
+
+<!ENTITY subjectField.label "Subject: ">
+<!--# LOCALIZATION NOTE (ccField.label): DONT_TRANSLATE -->
+<!ENTITY ccField.label "Cc: ">
+<!ENTITY bccField.label "Bcc: ">
+<!ENTITY newsgroupsField.label "Newsgroups: ">
+<!ENTITY followupToField.label "Followup-To: ">
+
+<!ENTITY tagsHdr.label "Tags: ">
+<!ENTITY dateField.label "Date: ">
+<!ENTITY userAgentField.label "User-Agent: ">
+<!ENTITY referencesField.label "References: ">
+<!ENTITY messageIdField.label "Message-ID: ">
+<!ENTITY inReplyToField.label "In-Reply-To: ">
+<!ENTITY originalWebsite.label "Website: ">
+
+<!ENTITY editDraft.label "Edit Draft…">
+<!ENTITY editDraft.accesskey "D">
+<!ENTITY editTemplate.label "Edit Template…">
+<!ENTITY editTemplate.accesskey "d">
+
+<!ENTITY attachmentsTree.label "Attachments:">
+<!ENTITY attachmentsTree.accesskey "c">
+<!ENTITY openAttachmentCmd.label "Open">
+<!ENTITY openAttachmentCmd.accesskey "O">
+<!ENTITY viewAttachmentCmd.label "View Source">
+<!ENTITY viewAttachmentCmd.accesskey "V">
+<!ENTITY saveAsAttachmentCmd.label "Save As…">
+<!ENTITY saveAsAttachmentCmd.accesskey "A">
+<!ENTITY detachAttachmentCmd.label "Detach…">
+<!ENTITY detachAttachmentCmd.accesskey "h">
+<!ENTITY deleteAttachmentCmd.label "Delete">
+<!ENTITY deleteAttachmentCmd.accesskey "t">
+<!ENTITY saveAllAttachmentsCmd.label "Save All…">
+<!ENTITY saveAllAttachmentsCmd.accesskey "S">
+<!ENTITY detachAllAttachmentsCmd.label "Detach All…">
+<!ENTITY detachAllAttachmentsCmd.accesskey "D">
+<!ENTITY deleteAllAttachmentsCmd.label "Delete All…">
+<!ENTITY deleteAllAttachmentsCmd.accesskey "e">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgHdrViewPopup.dtd b/comm/suite/locales/en-US/chrome/mailnews/msgHdrViewPopup.dtd
new file mode 100644
index 0000000000..97076ea871
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgHdrViewPopup.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!-- Message Header View Popup -->
+<!ENTITY AddToAddressBook.label "Add To Address Book…">
+<!ENTITY AddToAddressBook.accesskey "B">
+<!ENTITY EditContact.label "Edit Contact…">
+<!ENTITY EditContact.accesskey "E">
+<!ENTITY ViewContact.label "View Contact">
+<!ENTITY ViewContact.accesskey "V">
+<!ENTITY SendMailTo.label "Compose Mail To…">
+<!ENTITY SendMailTo.accesskey "s">
+<!ENTITY CopyEmailAddress.label "Copy Email Address">
+<!ENTITY CopyEmailAddress.accesskey "C">
+<!ENTITY CopyNameAndEmailAddress.label "Copy Name and Email Address">
+<!ENTITY CopyNameAndEmailAddress.accesskey "N">
+<!ENTITY CreateFilterFrom.label "Create Filter From…">
+<!ENTITY CreateFilterFrom.accesskey "F">
+<!ENTITY openInBrowser.label "Open in Browser">
+<!ENTITY openInBrowser.accesskey "O">
+<!ENTITY bookmarkLinkCmd.label "Bookmark This Link…">
+<!ENTITY bookmarkLinkCmd.accesskey "B">
+<!ENTITY copyLinkCmd.label "Copy Link Location">
+<!ENTITY copyLinkCmd.accesskey "C">
+<!ENTITY CopyMessageId.label "Copy Message-ID">
+<!ENTITY CopyMessageId.accesskey "C">
+<!ENTITY OpenMessageForMsgId.label "Open Message For ID">
+<!ENTITY OpenMessageForMsgId.accesskey "O">
+<!ENTITY OpenBrowserWithMsgId.label "Open Browser With Message-ID">
+<!ENTITY OpenBrowserWithMsgId.accesskey "B">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgPrintEngine.dtd b/comm/suite/locales/en-US/chrome/mailnews/msgPrintEngine.dtd
new file mode 100644
index 0000000000..08139ffaf8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgPrintEngine.dtd
@@ -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/. -->
+
+<!-- LOCALIZATION NOTE (printCmd.key, closeCmd.key):
+ As defined in msgPrintEngine.xul, Ctrl plus the command keys defined here
+ will be the keyboard shortcuts effective in print preview, e.g. Ctrl+P
+ -->
+
+<!ENTITY printCmd.key "P">
+<!ENTITY closeCmd.key "W">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgSynchronize.dtd b/comm/suite/locales/en-US/chrome/mailnews/msgSynchronize.dtd
new file mode 100644
index 0000000000..84bcfc05be
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgSynchronize.dtd
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+<!-- extracted from MsgSynchronize.xul and msgSelectOfflineFolders.xul-->
+
+<!ENTITY MsgSynchronize.label "Download and Sync Messages">
+<!ENTITY MsgSelect.label "Items for Offline Use">
+<!ENTITY MsgSyncDesc.label "If you have already selected mail folders or newsgroups for offline use, you can download and/or sync them now. Otherwise, use the &quot;Select&quot; button to choose mail folders and newsgroups for offline use.">
+<!ENTITY MsgSyncDirections.label "Download and/or sync the following:">
+<!ENTITY syncTypeMail.label "Mail messages">
+<!ENTITY syncTypeMail.accesskey "M">
+<!ENTITY syncTypeNews.label "Newsgroup messages">
+<!ENTITY syncTypeNews.accesskey "N">
+<!ENTITY sendMessage.label "Send Unsent messages">
+<!ENTITY sendMessage.accesskey "S">
+<!ENTITY workOffline.label "Work offline once download and/or sync is complete">
+<!ENTITY workOffline.accesskey "W">
+<!ENTITY selectButton.label "Select…">
+<!ENTITY selectButton.accesskey "E">
+<!ENTITY MsgSelectDesc.label "Choose mail folders and newsgroups for offline use.">
+<!ENTITY MsgSelectInd.label "Download">
+<!ENTITY MsgSelectItems.label "Folders and Newsgroups">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgViewPickerOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/msgViewPickerOverlay.dtd
new file mode 100644
index 0000000000..95937c7e33
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgViewPickerOverlay.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE msgViewPickerOverlay.dtd UI for showing various views on a folder -->
+
+<!ENTITY viewPicker.label "View:">
+<!ENTITY viewPicker.accesskey "i">
+<!ENTITY viewAll.label "All">
+<!ENTITY viewAll.accesskey "A">
+<!ENTITY viewUnread.label "Unread">
+<!ENTITY viewUnread.accesskey "U">
+<!ENTITY viewNotDeleted.label "Not Deleted">
+<!ENTITY viewNotDeleted.accesskey "D">
+<!ENTITY viewTags.label "Tags">
+<!ENTITY viewTags.accesskey "T">
+<!ENTITY viewCustomViews.label "Custom Views">
+<!ENTITY viewCustomViews.accesskey "V">
+<!ENTITY viewVirtualFolder.label "Save View as a Folder…">
+<!ENTITY viewVirtualFolder.accesskey "S">
+<!ENTITY viewCustomizeView.label "Customize…">
+<!ENTITY viewCustomizeView.accesskey "C">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/msgmdn.properties b/comm/suite/locales/en-US/chrome/mailnews/msgmdn.properties
new file mode 100644
index 0000000000..6a9ccf58f6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/msgmdn.properties
@@ -0,0 +1,18 @@
+# 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/.
+## Msg Mdn Report strings
+MsgMdnDisplayed=Note: This Return Receipt only acknowledges that the message was displayed on the recipient's computer. There is no guarantee that the recipient has read or understood the message contents.
+MsgMdnDispatched=The message was either printed, faxed, or forwarded without being displayed to the recipient. There is no guarantee that the recipient will read the message at a later time.
+MsgMdnProcessed=The message was processed by the recipient's mail client without being displayed. There is no guarantee that the message will be read at a later time.
+MsgMdnDeleted=The message has been deleted. The person you sent it to may or may not have seen it. They might undelete it at a later time and read it.
+MsgMdnDenied=The recipient of the message does not wish to send a return receipt back to you.
+MsgMdnFailed=A failure occurred. A proper return receipt could not be generated or sent to you.
+# LOCALIZATION NOTE : Do not translate the word "%S" below.
+MsgMdnMsgSentTo=This is a Return Receipt for the mail that you sent to %S.
+MdnDisplayedReceipt=Return Receipt (displayed)
+MdnDispatchedReceipt=Return Receipt (dispatched)
+MdnProcessedReceipt=Return Receipt (processed)
+MdnDeletedReceipt=Return Receipt (deleted)
+MdnDeniedReceipt=Return Receipt (denied)
+MdnFailedReceipt=Return Receipt (failed)
diff --git a/comm/suite/locales/en-US/chrome/mailnews/newFolderDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/newFolderDialog.dtd
new file mode 100644
index 0000000000..c34028d2d4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/newFolderDialog.dtd
@@ -0,0 +1,16 @@
+<!-- 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/. -->
+
+<!-- Labels -->
+<!ENTITY newFolderDialog.title "New Folder">
+<!ENTITY name.label "Name:">
+<!ENTITY name.accesskey "n">
+<!ENTITY description.label "Create as a subfolder of:">
+<!ENTITY description.accesskey "c">
+<!ENTITY folderRestriction1.label "This server restricts folders to two special kinds.">
+<!ENTITY folderRestriction2.label "Allow your new folder to contain:">
+<!ENTITY foldersOnly.label "Folders Only">
+<!ENTITY messagesOnly.label "Messages Only">
+<!ENTITY accept.label "Create Folder">
+<!ENTITY accept.accesskey "r">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/news.properties b/comm/suite/locales/en-US/chrome/mailnews/news.properties
new file mode 100644
index 0000000000..323a373c73
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/news.properties
@@ -0,0 +1,56 @@
+# 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/.
+downloadHeadersTitlePrefix=Download Headers
+downloadHeadersInfoText=There are %S new message headers to download for this newsgroup.
+cancelDisallowed=This message does not appear to be from you. You may only cancel your own posts, not those made by others.
+cancelConfirm=Are you sure you want to cancel this message?
+messageCancelled=Message cancelled.
+enterUserPassTitle=News Server Username and Password Required
+# LOCALIZATION NOTE (enterUserPassServer): %S is the server being accessed
+enterUserPassServer=Please enter a username and password for %S:
+# LOCALIZATION NOTE (enterUserPassGroup): %1$S is a specific newsgroup to set
+# the password for; %2$S is the server from which the newsgroup is accessed
+enterUserPassGroup=Please enter a username and password for %1$S on %2$S:
+okButtonText=Download
+
+noNewMessages=There are no new messages on the server.
+# LOCALIZATION NOTE (newNewsgroupHeaders): %1$S is the number of the current
+# header being downloaded, %2$S is the number of headers to be downloaded, and
+# %3$S is the newsgroup whose headers are being downloaded.
+newNewsgroupHeaders=Downloading %1$S of %2$S headers on %3$S
+# LOCALIZATION NOTE (newNewsgroupFilteringHeaders): %1$S is the name of the MIME
+# header being filtered on, %2$S is the number of the current header being
+# downloaded, %3$S is the number of headers to be downloaded, and %4$S is the
+# newsgroup whose headers are being downloaded.
+newNewsgroupFilteringHeaders=Getting headers for filters: %1$S (%2$S/%3$S) on %4$S
+downloadingArticles=Downloading articles %S-%S
+bytesReceived=Downloading newsgroups: %S received (%SKB read at %SKB/sec)
+downloadingArticlesForOffline=Downloading articles %S-%S in %S
+
+# LOCALIZATION NOTE (autoUnsubscribeText): %1$S is the newsgroup and %2$S is the newsgroup-server it is being removed from.
+autoUnsubscribeText=The newsgroup %1$S does not appear to exist on the host %2$S. Would you like to unsubscribe from it?
+
+# LOCALIZATION NOTE (autoSubscribeText): %1$S is the newsgroup.
+autoSubscribeText=Would you like to subscribe to %1$S?
+
+# LOCALIZATION NOTE (Error -304): In the following item, don't translate "NNTP"
+# Error - server error
+## @name NNTP_ERROR_MESSAGE
+## @loc None
+-304=A News (NNTP) error occurred:
+
+# Error - newsgroup scan error
+## @name NNTP_NEWSGROUP_SCAN_ERROR
+## @loc None
+-305=A News error occurred. The scan of all newsgroups is incomplete. Try to View All Newsgroups again
+
+# Error - NNTP authinfo failure
+## @name NNTP_AUTH_FAILED
+## @loc None
+-260=An authorization error occurred. Please try entering your name and/or password again.
+
+# Error - TCP error
+## @name TCP_ERROR
+## @loc None
+-206=A communications error occurred. Try connecting again. TCP Error:
diff --git a/comm/suite/locales/en-US/chrome/mailnews/newsError.dtd b/comm/suite/locales/en-US/chrome/mailnews/newsError.dtd
new file mode 100644
index 0000000000..be88a12507
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/newsError.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE (newsError.title): The title of the news error page.
+ Not generally visible. -->
+<!ENTITY newsError.title "Problem Loading Article">
+
+<!-- LOCALIZATION NOTE (articleNotFound.title): The main heading for the news
+ error page. -->
+<!ENTITY articleNotFound.title "Article not found">
+
+<!-- LOCALIZATION NOTE (articleNotFound.desc): A longer description for the news
+ error page. -->
+<!ENTITY articleNotFound.desc "The newsgroup server reports that it can't find the article.">
+
+<!-- LOCALIZATION NOTE (serverResponded.title): A string preceding the text
+ response from the newsgroup server describing the error. -->
+<!ENTITY serverResponded.title "Newsgroup server responded:">
+
+<!-- LOCALIZATION NOTE (articleExpired.title): A string explaining that the
+ article may have expired. -->
+<!ENTITY articleExpired.title "Perhaps the article has expired?">
+
+<!-- LOCALIZATION NOTE (trySearching.title): A string preceding the message's
+ ID. -->
+<!ENTITY trySearching.title "Try searching for article:">
+
+<!-- LOCALIZATION NOTE (removeExpiredArticles.title): The label for the button
+ to remove all expired articles from the newsgroup. -->
+<!ENTITY removeExpiredArticles.title "Remove All Expired Articles">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/newsblog/am-newsblog.dtd b/comm/suite/locales/en-US/chrome/mailnews/newsblog/am-newsblog.dtd
new file mode 100644
index 0000000000..5a87c01b57
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/newsblog/am-newsblog.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!ENTITY biffAll.label "Enable updates for all feeds">
+<!ENTITY biffAll.accesskey "E">
+
+<!ENTITY newFeedSettings.label "Default Settings for New Feeds">
+
+<!ENTITY manageSubscriptions.label "Manage Subscriptions…">
+<!ENTITY manageSubscriptions.accesskey "M">
+
+<!-- entities from rss.rdf -->
+<!ENTITY feeds.accountName "Blogs &amp; News Feeds">
+<!ENTITY feeds.wizardShortName "Feeds">
+<!ENTITY feeds.wizardLongName "Blogs &amp; News Feeds">
+<!ENTITY feeds.wizardLongName.accesskey "F">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd b/comm/suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd
new file mode 100644
index 0000000000..0df7f953aa
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd
@@ -0,0 +1,55 @@
+<!-- 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/. -->
+
+<!-- Subscription Dialog -->
+<!ENTITY feedSubscriptions.label "Feed Subscriptions">
+<!ENTITY learnMore.label "Learn more about Feeds">
+
+<!ENTITY feedTitle.label "Title:">
+<!ENTITY feedTitle.accesskey "T">
+
+<!ENTITY feedLocation.label "Feed URL:">
+<!ENTITY feedLocation.accesskey "F">
+<!ENTITY feedLocation2.placeholder "Enter a valid feed url">
+<!ENTITY locationValidate.label "Validate">
+<!ENTITY validateText.label "Check validation and retrieve a valid url.">
+
+<!ENTITY feedFolder.label "Store Articles in:">
+<!ENTITY feedFolder.accesskey "S">
+
+<!-- Account Settings and Subscription Dialog -->
+<!ENTITY biffStart.label "Check for new articles every ">
+<!ENTITY biffStart.accesskey "k">
+<!ENTITY biffMinutes.label "minutes">
+<!ENTITY biffMinutes.accesskey "n">
+<!ENTITY biffDays.label "days">
+<!ENTITY biffDays.accesskey "d">
+<!ENTITY recommendedUnits.label "Publisher recommends:">
+
+<!ENTITY quickMode.label "Show the article summary instead of loading the web page">
+<!ENTITY quickMode.accesskey "h">
+
+<!ENTITY autotagEnable.label "Automatically create tags from feed &lt;category&gt; names">
+<!ENTITY autotagEnable.accesskey "o">
+<!ENTITY autotagUsePrefix.label "Prefix tags with:">
+<!ENTITY autotagUsePrefix.accesskey "P">
+<!ENTITY autoTagPrefix.placeholder "Enter a tag prefix">
+
+<!-- Subscription Dialog -->
+<!ENTITY button.addFeed.label "Add">
+<!ENTITY button.addFeed.accesskey "A">
+<!ENTITY button.verifyFeed.label "Verify">
+<!ENTITY button.verifyFeed.accesskey "V">
+<!ENTITY button.updateFeed.label "Update">
+<!ENTITY button.updateFeed.accesskey "U">
+<!ENTITY button.removeFeed.label "Remove">
+<!ENTITY button.removeFeed.accesskey "R">
+<!ENTITY button.importOPML.label "Import">
+<!ENTITY button.importOPML.accesskey "I">
+<!ENTITY button.exportOPML.label "Export">
+<!ENTITY button.exportOPML.accesskey "X">
+<!ENTITY button.exportOPML.tooltip "Export Feeds with folder structure; ctrl click or ctrl enter to export Feeds as a list">
+
+<!ENTITY cmd.close.commandKey "w">
+<!ENTITY button.close.label "Close">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties b/comm/suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties
new file mode 100644
index 0000000000..972d2af4f4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties
@@ -0,0 +1,93 @@
+# 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/.
+
+subscribe-validating-feed=Verifying the feed…
+subscribe-cancelSubscription=Are you sure you wish to cancel subscribing to the current feed?
+subscribe-cancelSubscriptionTitle=Subscribing to a Feed…
+subscribe-feedAlreadySubscribed=You already have a subscription for this feed.
+subscribe-errorOpeningFile=Could not open the file.
+subscribe-feedAdded=Feed added.
+subscribe-feedUpdated=Feed updated.
+subscribe-feedMoved=Feed subscription moved.
+subscribe-feedCopied=Feed subscription copied.
+subscribe-feedRemoved=Feed unsubscribed.
+subscribe-feedNotValid=The Feed URL is not a valid feed.
+subscribe-feedVerified=The Feed URL has been verified.
+subscribe-networkError=The Feed URL could not be found. Please check the name and try again.
+subscribe-noAuthError=The Feed URL is not authorized.
+subscribe-loading=Loading, please wait…
+
+subscribe-OPMLImportTitle=Select OPML file to import
+## LOCALIZATION NOTE(subscribe-OPMLExportTitleList):
+## %S is the name of the feed account folder name.
+subscribe-OPMLExportTitleList=Export %S as an OPML file - Feeds list
+## LOCALIZATION NOTE(subscribe-OPMLExportTitleStruct):
+## %S is the name of the feed account folder name.
+subscribe-OPMLExportTitleStruct=Export %S as an OPML file - Feeds with folder structure
+## LOCALIZATION NOTE(subscribe-OPMLExportFileDialogTitle):
+## %1$S is the brandShortName, %2$S is the name of the feed account folder name.
+subscribe-OPMLExportFileDialogTitle=%1$S OPML Export - %2$S
+## LOCALIZATION NOTE(subscribe-OPMLExportDefaultFileName):
+## %1$S is the brandShortName (Thunderbird for example), %2$S is the account name.
+## The default extension (.opml) is added here as it is not automatically appended in the file picker on MacOS.
+subscribe-OPMLExportDefaultFileName=My%1$SFeeds-%2$S.opml
+## LOCALIZATION NOTE(subscribe-OPMLImportInvalidFile): %S is the name of the OPML file the user tried to import.
+subscribe-OPMLImportInvalidFile=The file %S does not seem to be a valid OPML file.
+## LOCALIZATION NOTE(subscribe-OPMLImportFeedCount): Semi-colon list of plural forms.
+## See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+## #1 is the count of new imported entries.
+subscribe-OPMLImportFeedCount=Imported #1 new feed.;Imported #1 new feeds.
+## LOCALIZATION NOTE(subscribe-OPMLImportUniqueFeeds): Semi-colon list of plural forms.
+## #1 is the count of new imported entries
+subscribe-OPMLImportUniqueFeeds=Imported #1 new feed to which you aren't already subscribed;Imported #1 new feeds to which you aren't already subscribed
+## LOCALIZATION NOTE(subscribe-OPMLImportFoundFeeds):
+## #1 is total number of elements found in the file
+subscribe-OPMLImportFoundFeeds=(out of #1 entry found);(out of #1 total entries found)
+## LOCALIZATION NOTE(subscribe-OPMLImportStatus):
+## This is the concatenation of the two strings defined above to compose 1 sentence.
+## %1$S = subscribe-OPMLImportUniqueFeeds
+## %2$S = subscribe-OPMLImportFoundFeeds
+subscribe-OPMLImportStatus=%1$S %2$S.
+
+subscribe-OPMLExportOPMLFilesFilterText=OPML Files
+## LOCALIZATION NOTE(subscribe-OPMLExportDone): %S is the export file name.
+subscribe-OPMLExportDone=Feeds in this account have been exported to %S.
+
+subscribe-confirmFeedDeletionTitle=Remove Feed
+## LOCALIZATION NOTE(subscribe-confirmFeedDeletion): %S is the name of the feed the user wants to unsubscribe from.
+subscribe-confirmFeedDeletion=Are you sure you want to unsubscribe from the feed: \n %S?
+
+## LOCALIZATION NOTE(subscribe-gettingFeedItems):
+## - The first %S is the number of articles processed so far;
+## - The second %S is the total number of items
+subscribe-gettingFeedItems=Downloading feed articles (%S of %S)…
+
+newsblog-noNewArticlesForFeed=There are no new articles for this feed.
+## LOCALIZATION NOTE(newsblog-networkError): %S is the feed URL
+newsblog-networkError=%S could not be found. Please check the name and try again.
+## LOCALIZATION NOTE(newsblog-feedNotValid): %S is the feed URL
+newsblog-feedNotValid=%S is not a valid feed.
+## LOCALIZATION NOTE(newsblog-badCertError): %S is the feed URL host
+newsblog-badCertError=%S uses an invalid security certificate.
+## LOCALIZATION NOTE(newsblog-noAuthError): %S is the feed URL
+newsblog-noAuthError=%S is not authorized.
+newsblog-getNewMsgsCheck=Checking feeds for new items…
+
+## LOCALIZATION NOTE(feeds-accountname): This string should be the same as feeds.accountName in am-newsblog.dtd
+feeds-accountname=Blogs & News Feeds
+
+## LOCALIZATION NOTE(externalAttachmentMsg): Content in the MIME part for external link attachments.
+externalAttachmentMsg=This MIME attachment is stored separately from the message.
+
+## Import wizard.
+ImportFeedsCreateNewListItem=* New Account *
+ImportFeedsNewAccount=Create and import into a new Feeds account
+ImportFeedsExistingAccount=Import into an existing Feeds account
+## LOCALIZATION NOTE(ImportFeedsDone):
+## - The first %S is the import file name;
+## - The second %S is the value of either ImportFeedsNew or ImportFeedsExisting;
+## - The third %S is the feed account name.
+ImportFeedsNew=new
+ImportFeedsExisting=existing
+ImportFeedsDone=The feed subscriptions import from file %1$S into %2$S account '%3$S' has finished.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/offline.properties b/comm/suite/locales/en-US/chrome/mailnews/offline.properties
new file mode 100644
index 0000000000..d78de71c1b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/offline.properties
@@ -0,0 +1,51 @@
+# 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/.
+
+# LOCALIZATION NOTE :
+# do not localize "\n". use "\n" to make the text fit nicely in the dialog.
+
+#
+# Download Messages Prompt
+#
+downloadMessagesWindowTitle=Work Offline
+
+# LOCALIZATION NOTE :
+# do not localize "\n". use "\n" to make the text fit nicely in the dialog.
+downloadMessagesLabel=Do you want to download messages \nfor offline use before you go offline?\n\n
+
+downloadMessagesCheckboxLabel=Always ask me when I go offline
+downloadMessagesDownloadButtonLabel=Download
+downloadMessagesNoDownloadButtonLabel=Don't Download
+downloadMessagesCancelButtonLabel=Cancel
+
+#
+# Send Messages Prompt
+#
+sendMessagesWindowTitle=Work Online
+
+sendMessagesLabel2=Would you like to send your unsent messages now?
+sendMessagesCheckboxLabel=Always ask me when I go online
+sendMessagesSendButtonLabel=Send
+sendMessagesNoSendButtonLabel=Don't Send
+sendMessagesCancelButtonLabel=Cancel
+
+#
+# GetMessages Offline Prompt
+#
+getMessagesOfflineWindowTitle=Get Messages
+
+# LOCALIZATION NOTE :
+# do not localize "\n". use "\n" to make the text fit nicely in the dialog.
+getMessagesOfflineLabel=You are currently offline. Would you like \nto go online to get your new messages?\n\n
+getMessagesOfflineGoButtonLabel=Go online
+
+#
+# Send Messages Offline Prompt
+#
+sendMessagesOfflineWindowTitle=Send Messages
+
+# LOCALIZATION NOTE :
+# do not localize "\n". use "\n" to make the text fit nicely in the dialog.
+sendMessagesOfflineLabel=You are currently offline. Would you like \nto go online and send your unsent messages?\n\n
+sendMessagesOfflineGoButtonLabel=Go online
diff --git a/comm/suite/locales/en-US/chrome/mailnews/offlineStartup.properties b/comm/suite/locales/en-US/chrome/mailnews/offlineStartup.properties
new file mode 100644
index 0000000000..bd2024e761
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/offlineStartup.properties
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+title=Work Online
+desc=Would you like to go online now?\n\n(If you choose to work offline, you can go online later - choose `Offline' from the `File' menu, then uncheck `Work Offline'.)
+workOnline=Work Online
+workOffline=Work Offline
diff --git a/comm/suite/locales/en-US/chrome/mailnews/outlookImportMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/outlookImportMsgs.properties
new file mode 100644
index 0000000000..ce2bc1fa3f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/outlookImportMsgs.properties
@@ -0,0 +1,72 @@
+# 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/.
+
+#
+# The following are used by the Outlook import code to display status/error
+# and informational messages
+#
+
+# Short name of import module
+## @name OUTLOOKIMPORT_NAME
+## @loc None
+## LOCALIZATION NOTE (2000): DONT_TRANSLATE
+2000=Outlook
+
+# Description of import module
+## @name OUTLOOKIMPORT_DESCRIPTION
+## @loc None
+## LOCALIZATION NOTE (2010): In this item, don't translate "Outlook"
+2010=Outlook mail, address books, and settings
+
+# Success message
+## @name OUTLOOKIMPORT_MAILBOX_SUCCESS
+## @loc None
+## LOCALIZATION NOTE (2002): In this item, don't translate "%S" or "%d"
+## The variable %S will receive the name of the mailbox
+## The variable %d will receive the number of messages
+2002=Mailbox %S, imported %d messages
+
+# Error message
+## @name OUTLOOKIMPORT_MAILBOX_BADPARAM
+## @loc None
+2003=Bad parameter passed to import mailbox.
+
+# Error message
+## @name OUTLOOKIMPORT_MAILBOX_CONVERTERROR
+## @loc None
+## LOCALIZATION NOTE (2004): In this item, don't translate "%S"
+## The variable %S will receive the name of the mailbox
+2004=Error importing mailbox %S, all messages may not be imported from this mailbox.
+
+# Address book name
+## @name OUTLOOKIMPORT_ADDRNAME
+## @loc None
+## LOCALIZATION NOTE (2005): In this item, don't translate "Outlook"
+2005=Outlook address books
+
+# Description
+## @name OUTLOOKIMPORT_ADDRESS_SUCCESS
+## @loc None
+## LOCALIZATION NOTE (2006): In this item, don't translate "%S"
+## The variable %S will receive the name of the address book
+2006=Imported address book %S
+
+# Error message
+## @name OUTLOOKIMPORT_ADDRESS_BADPARAM
+## @loc None
+2007=Bad parameter passed to import address book.
+
+# Error message
+## @name OUTLOOKIMPORT_ADDRESS_BADSOURCEFILE
+## @loc None
+## LOCALIZATION NOTE (2008): In this item, don't translate "%S"
+## The variable %S will receive the name of the address book
+2008=Error accessing file for address book %S.
+
+# Error message
+## @name OUTLOOKIMPORT_ADDRESS_CONVERTERROR
+## @loc None
+## LOCALIZATION NOTE (2009): In this item, don't translate "%S"
+## The variable %S will receive the name of the address book
+2009=Error importing address book %S, all addresses may not have been imported.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pgpmime.properties b/comm/suite/locales/en-US/chrome/mailnews/pgpmime.properties
new file mode 100644
index 0000000000..17beb4f886
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pgpmime.properties
@@ -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/.
+
+#
+# The following are used by the pgpmime content type handler
+#
+
+# LOCALIZATION NOTE(pgpMimeNeedsAddon): The text can contain HTML tags.
+# %S is the url to Enigmail on AMO supplied from preferences.
+pgpMimeNeedsAddon=This is an encrypted OpenPGP message.<br>In order to decrypt this mail, you need to install an <a href="%S">OpenPGP add-on</a>.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/AccountManager.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/AccountManager.dtd
new file mode 100644
index 0000000000..475b2e0507
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/AccountManager.dtd
@@ -0,0 +1,26 @@
+<!-- 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/. -->
+
+<!-- extracted from AccountManager.xul -->
+
+<!ENTITY accountManagerTitle.label "Mail &amp; Newsgroups Account Settings">
+
+<!ENTITY addAccountButton.label "Add Account…">
+<!ENTITY addAccountButton.accesskey "A">
+<!ENTITY setDefaultButton.label "Set as Default">
+<!ENTITY setDefaultButton.accesskey "f">
+<!ENTITY removeButton.label "Remove Account">
+<!ENTITY removeButton.accesskey "R">
+
+<!-- AccountManager.xul -->
+<!-- LOCALIZATION NOTE : this is part of an inline-style attribute on the account
+ setting dialog, which specifies the width and height in em units of the dialog.
+ Localizers ONLY can increase these widths if they are having difficulty getting
+ panel content to fit.
+ 1ch = The width of the "0" (ZERO, U+0030) glyph for the current font.
+ 1em = The height of the font.
+ XUL/FE DEVELOPERS: DO NOT MODIFY THIS VALUE. It represents the correct size of
+ this window for en-US. -->
+<!ENTITY accountManager.size "width: 105ch; height: 55em;">
+<!ENTITY accountTree.width "width: 32ch;">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/AccountWizard.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/AccountWizard.dtd
new file mode 100644
index 0000000000..55f99e3799
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/AccountWizard.dtd
@@ -0,0 +1,124 @@
+<!-- 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/. -->
+
+<!-- Entities for AccountWizard -->
+
+<!ENTITY windowTitle.label "Account Wizard">
+<!ENTITY accountWizard.size "width: 40em; height: 38em;">
+
+<!-- Entities for Account Type page -->
+
+<!ENTITY accountSetupInfo2.label "In order to receive messages, you first need to set up an account.">
+<!ENTITY accountTypeTitle.label "New Account Setup">
+<!ENTITY accountTypeDesc2.label "This Wizard will collect the information necessary to set up an account. If you do not know the information requested, please contact your System Administrator or Internet Service Provider.">
+<!ENTITY accountTypeDirections.label "Select the type of account you would like to set up:">
+<!ENTITY accountTypeMail.label "Email account">
+<!ENTITY accountTypeMail.accesskey "m">
+<!ENTITY accountTypeNews.label "Newsgroup account">
+<!ENTITY accountTypeNews.accesskey "w">
+
+<!-- Entities for Identity page -->
+
+<!ENTITY identityTitle.label "Identity">
+<!ENTITY identityDesc.label "Each account has an identity, which is the information that identifies you to others when they receive your messages.">
+
+<!-- LOCALIZATION NOTE (fullnameDesc.label) : do not translate two of "&quot;" in below line -->
+<!ENTITY fullnameDesc.label "Enter the name you would like to appear in the &quot;From&quot; field of your outgoing messages">
+<!-- LOCALIZATION NOTE (fullnameExample.label) : use following directions for below line
+ 1, do not translate two of "&quot;"
+ 2, Use localized full name instead of "John Smith"
+-->
+<!ENTITY fullnameExample.label "(for example, &quot;John Smith&quot;).">
+<!ENTITY fullnameLabel.label "Your Name:">
+<!ENTITY fullnameLabel.accesskey "Y">
+
+<!ENTITY emailLabel.label "Email Address:">
+<!ENTITY emailLabel.accesskey "E">
+
+<!-- Entities for Incoming Server page -->
+
+<!ENTITY incomingTitle.label "Incoming Server Information">
+<!ENTITY incomingServerTypeDesc.label "Select the type of incoming server you are using.">
+<!-- LOCALIZATION NOTE (imapType.label) : Do not translate "IMAP" in below line -->
+<!ENTITY imapType.label "IMAP">
+<!ENTITY imapType.accesskey "I">
+<!-- LOCALIZATION NOTE (popType.label) : Do not translate "POP" in below line -->
+<!ENTITY popType.label "POP">
+<!ENTITY popType.accesskey "P">
+<!ENTITY portNum.label "Port:">
+<!ENTITY portNum.accesskey "o">
+<!ENTITY defaultPortLabel.label "Default:">
+<!ENTITY defaultPortValue.label "">
+<!-- LOCALIZATION NOTE (incomingServerNameDesc.label) : Do not translate "&quot;pop.example.net&quot;" in below line -->
+<!ENTITY incomingServer.description "Enter the name of your incoming server (for example, &quot;mail.example.net&quot;).">
+<!ENTITY incomingServer.label "Incoming Server:">
+<!ENTITY incomingServer.accesskey "S">
+<!ENTITY leaveMsgsOnSrvr.label "Leave messages on server">
+<!ENTITY leaveMsgsOnSrvr.accesskey "L">
+<!-- LOCALIZATION NOTE (incomingUsername.description) : do not translate "&quot;jsmith&quot;" in below line -->
+<!ENTITY incomingUsername.description "Enter the incoming user name given to you by your email provider (for example, &quot;jsmith&quot;).">
+<!ENTITY incomingUsername.label "User Name:">
+<!ENTITY incomingUsername.accesskey "U">
+<!-- LOCALIZATION NOTE (newsServerNameDesc.label) : Do not translate "NNTP" or the "&quot;" entities in below line -->
+<!ENTITY newsServerNameDesc.label "Enter the name of your news server (NNTP) (for example, &quot;news.example.net&quot;).">
+<!ENTITY newsServerLabel.label "Newsgroup Server:">
+<!ENTITY newsServerLabel.accesskey "S">
+
+<!-- Entities for Outgoing Server page -->
+
+<!ENTITY outgoingTitle.label "Outgoing Server Information">
+<!-- LOCALIZATION NOTE (outgoingServer.description) : Do not translate "SMTP" and "&quot;smtp.example.net&quot;" in below line -->
+<!ENTITY outgoingServer.description "Enter the name of your outgoing server (SMTP) (for example, &quot;smtp.example.net&quot;).">
+<!ENTITY outgoingServer.label "Outgoing Server:">
+<!ENTITY outgoingServer.accesskey "S">
+<!ENTITY outgoingUsername.description "Enter the outgoing user name given to you by your email provider (this is typically the same as your incoming user name).">
+<!ENTITY outgoingUsername.label "Outgoing User Name:">
+<!ENTITY outgoingUsername.accesskey "U">
+
+<!-- LOCALIZATION NOTE (modifyOutgoing.suffix) : This string will be appended after each of
+ haveSmtp1.suffix3, haveSmtp2.suffix3, haveSmtp3.suffix3 .
+-->
+<!ENTITY modifyOutgoing.suffix "You can modify outgoing server settings from within Mail &amp; Newsgroups Account Settings.">
+<!-- LOCALIZATION NOTE (haveSmtp1.prefix and haveSmtp1.suffix3) : Do not translate "SMTP" and "&quot;" in
+ these variables. Also, translate haveSmtp1.prefix and haveSmtp1.suffix3 as a single sentence, inserting
+ text after the "&quot;" entity in haveSmtp1.suffix3, if required grammatically.
+-->
+<!ENTITY haveSmtp1.prefix "Your existing outgoing server (SMTP), &quot;">
+<!ENTITY haveSmtp1.suffix3 "&quot;, will be used.">
+<!-- LOCALIZATION NOTE (haveSmtp2.prefix and haveSmtp2.suffix3) : Do not translate "SMTP" and "&quot;" in
+ these variables. Also, translate haveSmtp2.prefix and haveSmtp2.suffix3 as a single sentence, inserting
+ text after the "&quot;" entity in haveSmtp2.suffix3, if required grammatically.
+-->
+<!ENTITY haveSmtp2.prefix "Your existing outgoing (SMTP) username, &quot;">
+<!ENTITY haveSmtp2.suffix3 "&quot;, will be used.">
+<!-- LOCALIZATION NOTE (haveSmtp3.prefix and haveSmtp3.suffix3) : Do not translate "SMTP" and "&quot;" in
+ these variables. Also, translate haveSmtp3.prefix and haveSmtp3.suffix3 as a single sentence, inserting
+ text after the "&quot;" entity in haveSmtp3.suffix3, if required grammatically.
+-->
+<!ENTITY haveSmtp3.prefix "Your outgoing (SMTP) server, &quot;">
+<!ENTITY haveSmtp3.suffix3 "&quot;, is identical to your incoming server, your incoming user name will be used to access it.">
+
+<!-- Entities for Account name page -->
+
+<!ENTITY accnameTitle.label "Account Name">
+<!-- LOCALIZATION NOTE (accnameDesc.label) : do not translate any "&quot;" in below line -->
+<!ENTITY accnameDesc.label "Enter the name by which you would like to refer to this account (for example, &quot;Work Account&quot;, &quot;Home Account&quot; or &quot;News Account&quot;).">
+<!ENTITY accnameLabel.label "Account Name:">
+<!ENTITY accnameLabel.accesskey "A">
+
+<!-- Entities for Done (Congratulations) page -->
+
+<!ENTITY completionTitle.label "Congratulations!">
+<!ENTITY completionText.label "Please verify that the information below is correct.">
+<!ENTITY serverTypePrefix.label "Incoming Server Type:">
+<!ENTITY serverNamePrefix.label "Incoming Server Name:">
+<!ENTITY smtpServerNamePrefix.label "Outgoing Server Name (SMTP):">
+<!ENTITY newsServerNamePrefix.label "News Server Name (NNTP):">
+<!ENTITY downloadOnLogin.label "Download messages now">
+<!ENTITY downloadOnLogin.accesskey "D">
+<!ENTITY deferStorageDesc.label "Check this checkbox to store mail for this account in the Local Folders Global Inbox. Otherwise the account appears as a top-level account and its mail is stored in its own directory.">
+<!ENTITY deferStorage.label "Use Global Inbox (store mail in Local Folders)">
+<!ENTITY deferStorage.accesskey "G">
+<!ENTITY clickFinish.label "Click Finish to save these settings and exit the Account Wizard.">
+<!ENTITY clickFinish.labelMac "Click Done to save these settings and exit the Account Wizard.">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-addressing.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-addressing.dtd
new file mode 100644
index 0000000000..9ba251c92e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-addressing.dtd
@@ -0,0 +1,49 @@
+<!-- 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/. -->
+
+<!-- extracted from am-addressing.xul -->
+
+<!ENTITY addressing.label "Composition &amp; Addressing">
+<!ENTITY addressingGroupTitle.label "Addressing">
+<!ENTITY autocompleteToMyDomain.label "Automatically append my domain to addresses">
+<!ENTITY autocompleteToMyDomain.accesskey "o">
+<!ENTITY addressingText.label "When looking up addresses:">
+<!-- LOCALIZATION NOTE (override.label) : do not translate "LDAP" in below line -->
+<!ENTITY useGlobal.label "Use my global LDAP server preferences for this account">
+<!ENTITY useGlobal.accesskey "U">
+<!ENTITY editDirectories.label "Edit Directories…">
+<!ENTITY editDirectories.accesskey "E">
+<!-- LOCALIZATION NOTE (directories.label) : do not translate "LDAP" in below line -->
+<!ENTITY directories.label "Use a different LDAP server:">
+<!ENTITY directories.accesskey "d">
+<!ENTITY directoriesNone.label "None">
+
+<!-- am-addressing.xul -->
+
+<!ENTITY compositionGroupTitle.label "Composition">
+<!-- LOCALIZATION NOTE (useHtml.label) : do not translate "html" in below line -->
+<!ENTITY useHtml.label "Compose messages in HTML format">
+<!ENTITY useHtml.accesskey "C">
+<!ENTITY autoQuote.label "Automatically quote the original message when replying">
+<!ENTITY autoQuote.accesskey "m">
+<!-- LOCALIZATION NOTE (quoting.label): This will concatenate with the 4 strings that follow. -->
+<!ENTITY quoting.label "When quoting,">
+<!ENTITY quoting.accesskey "q">
+<!ENTITY aboveQuote.label "start my reply above the quote">
+<!ENTITY belowQuote.label "start my reply below the quote">
+<!ENTITY selectAndQuote.label "select the quote">
+<!ENTITY place.label "and place my signature">
+<!ENTITY place.accesskey "s">
+<!ENTITY belowText.label "below the quote (recommended)">
+<!ENTITY aboveText.label "below my reply (above the quote)">
+<!ENTITY includeSigOnReply.label "Include signature for replies">
+<!ENTITY includeSigOnReply.accesskey "I">
+<!ENTITY includeSigOnForward.label "Include signature for forwards">
+<!ENTITY includeSigOnForward.accesskey "w">
+
+<!ENTITY globalComposingPrefs.label "Global Composing Preferences…">
+<!ENTITY globalComposingPrefs.accesskey "G">
+
+<!ENTITY globalAddressingPrefs.label "Global Addressing Preferences…">
+<!ENTITY globalAddressingPrefs.accesskey "P">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-advanced.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-advanced.dtd
new file mode 100644
index 0000000000..3496c1c52e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-advanced.dtd
@@ -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/. -->
+
+<!-- LOCALIZATION NOTE (smtpServer.label): do not translate "SMTP" in below line -->
+<!ENTITY smtpServer.label "Outgoing Server (SMTP) Settings">
+<!-- LOCALIZATION NOTE (smtpDescription.label): do not translate "SMTP" in below line -->
+<!ENTITY smtpDescription.label "When managing your identities you can use a server from this list by selecting it as the Outgoing Server (SMTP), or you can use the default server from this list by selecting &quot;Use Default Server&quot;.">
+
+<!ENTITY smtpListAdd.label "Add…">
+<!ENTITY smtpListAdd.accesskey "d">
+<!ENTITY smtpListEdit.label "Edit…">
+<!ENTITY smtpListEdit.accesskey "E">
+<!ENTITY smtpListDelete.label "Remove">
+<!ENTITY smtpListDelete.accesskey "m">
+<!ENTITY smtpListSetDefault.label "Set Default">
+<!ENTITY smtpListSetDefault.accesskey "t">
+
+<!ENTITY serverDetails.label "Details of selected server:">
+<!ENTITY serverDescription.label "Description: ">
+<!ENTITY serverName.label "Server Name: ">
+<!ENTITY serverPort.label "Port: ">
+<!ENTITY userName.label "User Name: ">
+<!ENTITY connectionSecurity.label "Connection Security: ">
+<!ENTITY authMethod.label "Authentication method: ">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-archiveoptions.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-archiveoptions.dtd
new file mode 100644
index 0000000000..3e5702d7a4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-archiveoptions.dtd
@@ -0,0 +1,23 @@
+<!-- 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/. -->
+
+<!-- extracted from am-archiveoptions.xul -->
+
+<!ENTITY dialogTitle.label "Archive Options">
+<!ENTITY archiveGranularityPrefix.label "When archiving messages, place them in:">
+<!ENTITY archiveFlat.label "A single folder">
+<!ENTITY archiveFlat.accesskey "s">
+<!ENTITY archiveYearly.label "Yearly archived folders">
+<!ENTITY archiveYearly.accesskey "Y">
+<!ENTITY archiveMonthly.label "Monthly archived folders">
+<!ENTITY archiveMonthly.accesskey "M">
+<!ENTITY keepFolderStructure.label "Keep existing folder structure of archived messages">
+<!ENTITY keepFolderStructure.accesskey "K">
+<!ENTITY archiveExample.label "Example">
+<!-- LOCALIZATION NOTE (archiveFolderName.label): this should match the default
+ name for the "Archives" folder -->
+<!ENTITY archiveFolderName.label "Archives">
+<!-- LOCALIZATION NOTE (inboxFolderName.label): this should match the default
+ name for the "Inbox" folder -->
+<!ENTITY inboxFolderName.label "Inbox">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-copies.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-copies.dtd
new file mode 100644
index 0000000000..8507b8678b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-copies.dtd
@@ -0,0 +1,50 @@
+<!-- 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/. -->
+
+<!-- extracted from am-copies.xul -->
+
+<!ENTITY copyAndFolderTitle.label "Copies &amp; Folders">
+<!ENTITY sendingPrefix.label "When sending messages:">
+<!ENTITY fccMailFolder.label "Automatically place a copy in:">
+<!ENTITY fccMailFolder.accesskey "u">
+<!ENTITY fccReplyFollowsParent.label "Place replies in the folder of the message being replied to">
+<!ENTITY fccReplyFollowsParent.accesskey "P">
+<!-- LOCALIZATION NOTE (ccAddress.label): do not translate "Cc" in below line -->
+<!ENTITY ccAddress.label "Cc these email addresses:">
+<!ENTITY ccAddress.accesskey "C">
+<!ENTITY ccAddressList.placeholder "Separate addresses with commas">
+<!-- LOCALIZATION NOTE (bccAddress.label): do not translate "Bcc" in below line -->
+<!ENTITY bccAddress.label "Bcc these email addresses:">
+<!ENTITY bccAddress.accesskey "B">
+<!ENTITY bccAddressList.placeholder "Separate addresses with commas">
+<!ENTITY saveMessageDlg.label "Show confirmation dialog when messages are saved">
+<!ENTITY saveMessageDlg.accesskey "w">
+<!-- LOCALIZATION NOTE (sentFolderOn.label): OK to translate this, bug #57440 -->
+<!ENTITY sentFolderOn.label "&quot;Sent&quot; Folder on:">
+<!ENTITY sentFolderOn.accesskey "S">
+<!ENTITY sentInOtherFolder.label "Other Folder:">
+<!ENTITY sentInOtherFolder.accesskey "O">
+<!-- LOCALIZATION NOTE (archivesFolderOn.label): OK to translate this, bug #57440 -->
+<!ENTITY archivesTitle.label "Message Archives">
+<!ENTITY keepArchives.label "Keep message archives in:">
+<!ENTITY keepArchives.accesskey "K">
+<!ENTITY archiveHierarchyButton.label "Archive options…">
+<!ENTITY archiveHierarchyButton.accesskey "n">
+<!ENTITY archivesFolderOn.label "&quot;Archives&quot; Folder on:">
+<!ENTITY archivesFolderOn.accesskey "v">
+<!ENTITY archiveInOtherFolder.label "Other Folder:">
+<!ENTITY archiveInOtherFolder.accesskey "l">
+<!ENTITY specialFolders.label "Drafts and Templates">
+<!ENTITY keepDrafts2.label "Keep draft messages in:">
+<!-- LOCALIZATION NOTE (draftsFolderOn.label): OK to translate this, bug #57440 -->
+<!ENTITY draftsFolderOn.label "&quot;Drafts&quot; Folder on:">
+<!ENTITY draftsFolderOn.accesskey "D">
+<!ENTITY draftInOtherFolder.label "Other Folder:">
+<!ENTITY draftInOtherFolder.accesskey "t">
+<!ENTITY keepTemplates.label "Keep message templates in:">
+<!-- LOCALIZATION NOTE (templatesFolderOn.label): OK to translate this, bug #57440 -->
+<!ENTITY templatesFolderOn.label "&quot;Templates&quot; Folder on:">
+<!ENTITY templatesFolderOn.accesskey "m">
+<!ENTITY templateInOtherFolder.label "Other Folder:">
+<!ENTITY templateInOtherFolder.accesskey "e">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-identities-list.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-identities-list.dtd
new file mode 100644
index 0000000000..1935a37ba1
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-identities-list.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+<!ENTITY identitiesListManageDesc.label "Manage the identities for this account. The first identity is used by default.">
+<!ENTITY identitiesListAdd.label "Add…">
+<!ENTITY identitiesListAdd.accesskey "A">
+<!ENTITY identitiesListEdit.label "Edit…">
+<!ENTITY identitiesListEdit.accesskey "E">
+<!ENTITY identitiesListDefault.label "Set Default">
+<!ENTITY identitiesListDefault.accesskey "S">
+<!ENTITY identitiesListDelete.label "Delete">
+<!ENTITY identitiesListDelete.accesskey "D">
+<!ENTITY identitiesListClose.label "Close">
+<!ENTITY identitiesListClose.accesskey "C">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-identity-edit.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-identity-edit.dtd
new file mode 100644
index 0000000000..acb612dad8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-identity-edit.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE (identityDialog.style): This value should be roughly
+ equal to the value of accountManager.size entity minus the value
+ of accountTree.width entity. -->
+<!ENTITY identityDialog.style "min-width: 75ch;">
+<!ENTITY identityListDesc.label "Configure the settings for this identity:">
+
+<!ENTITY settingsTab.label "Settings">
+<!ENTITY copiesFoldersTab.label "Copies &amp; Folders">
+<!ENTITY addressingTab.label "Composition &amp; Addressing">
+
+<!ENTITY publicData.label "Public Data">
+<!ENTITY privateData.label "Private Data">
+<!ENTITY identityAlias.label "Identity Label:">
+<!ENTITY identityAlias.accesskey "b">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-junk.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-junk.dtd
new file mode 100644
index 0000000000..0f9c55d881
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-junk.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!ENTITY junkSettings.label "Junk Settings">
+<!ENTITY trainingDescription.label "If enabled, you must first train &brandShortName; to identify junk mail by using the Junk toolbar button to mark messages as junk or not. You need to identify both junk and non junk messages. After that &brandShortName; will be able to mark junk automatically.">
+<!ENTITY level.label "Enable adaptive junk mail controls for this account">
+<!ENTITY level.accesskey "E">
+
+<!ENTITY move.label "Move new junk messages to:">
+<!ENTITY move.accesskey "M">
+<!ENTITY junkFolderOn.label "&quot;Junk&quot; folder on:">
+<!ENTITY junkFolderOn.accesskey "J">
+<!ENTITY otherFolder.label "Other:">
+<!ENTITY otherFolder.accesskey "O">
+<!ENTITY purge1.label "Automatically delete junk mail older than">
+<!ENTITY purge1.accesskey "u">
+<!ENTITY purge2.label "days">
+
+<!ENTITY whitelistHeader.label "Do not automatically mark mail as junk if the sender is in: ">
+<!ENTITY whitelistHeader.accesskey "D">
+
+<!ENTITY ispHeadersWarning.label "If enabled, &brandShortName; will automatically consider messages marked by this external classifier as junk.">
+<!ENTITY ispHeaders.label "Trust junk mail headers set by: ">
+<!ENTITY ispHeaders.accesskey "T">
+
+<!ENTITY junkClassification.label "Selection">
+<!ENTITY junkActions.label "Destination and Retention">
+
+<!ENTITY globalJunkPrefs.label "Global Junk Preferences…">
+<!ENTITY globalJunkPrefs.accesskey "G">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-main.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-main.dtd
new file mode 100644
index 0000000000..00edd05e0d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-main.dtd
@@ -0,0 +1,46 @@
+<!-- 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/. -->
+
+<!-- extracted from am-main.xul -->
+
+<!ENTITY accountTitle.label "Account Settings">
+<!ENTITY accountName.label "Account Name:">
+<!ENTITY accountName.accesskey "N">
+<!ENTITY identityTitle.label "Default Identity">
+<!ENTITY identityDesc.label "Each account has an identity, which is the information that other people see when they read your messages.">
+<!ENTITY name.label "Your Name:">
+<!ENTITY name.accesskey "Y">
+<!ENTITY email.label "Email Address:">
+<!ENTITY email.accesskey "E">
+<!ENTITY catchAll.label "Reply from this identity when delivery headers match:">
+<!ENTITY catchAll.accesskey "d">
+<!ENTITY replyTo.label "Reply-to Address:">
+<!ENTITY replyTo.accesskey "s">
+<!ENTITY replyTo.placeholder "Recipients will reply to this other address">
+<!ENTITY organization.label "Organization:">
+<!ENTITY organization.accesskey "O">
+<!ENTITY signatureText.label "Signature text:">
+<!ENTITY signatureText.accesskey "x">
+<!ENTITY signatureHtml.label "Use HTML">
+<!ENTITY signatureHtml.accesskey "L">
+<!ENTITY signatureFile.label "Attach the signature from a file instead:">
+<!ENTITY signatureFile.accesskey "t">
+<!ENTITY choose.label "Choose…">
+<!ENTITY choose.accesskey "C">
+<!ENTITY editVCard.label "Edit Card…">
+<!ENTITY editVCard.accesskey "d">
+<!-- LOCALIZATION NOTE (attachVCard.label) : do not translate "vCard" in below line -->
+<!ENTITY attachVCard.label "Attach my vCard to messages">
+<!ENTITY attachVCard.accesskey "v">
+
+<!ENTITY manageIdentities.label "Manage Identities…">
+<!ENTITY manageIdentities.accesskey "M">
+
+<!-- LOCALIZATION NOTE (smtpName.label) : do not translate "SMTP" in below line -->
+<!ENTITY smtpName.label "Outgoing Server (SMTP):">
+<!ENTITY smtpName.accesskey "u">
+<!ENTITY smtpDefaultServer.label "Use Default Server">
+
+<!ENTITY smtpServerEdit.label "Edit SMTP server…">
+<!ENTITY smtpServerEdit.accesskey "P">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.dtd
new file mode 100644
index 0000000000..292508ec86
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.dtd
@@ -0,0 +1,33 @@
+<!-- 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/. -->
+
+<!ENTITY pane.title "Return Receipts">
+<!ENTITY useGlobalPrefs.label "Use my global return receipt preferences for this account">
+<!ENTITY useGlobalPrefs.accesskey "U">
+<!ENTITY globalReceipts.label "Global Preferences…">
+<!ENTITY globalReceipts.accesskey "G">
+<!ENTITY useCustomPrefs.label "Customize return receipts for this account">
+<!ENTITY useCustomPrefs.accesskey "C">
+<!ENTITY requestReceipt.label "When sending messages, always request a return receipt">
+<!ENTITY requestReceipt.accesskey "W">
+<!ENTITY receiptArrive.label "When a receipt arrives:">
+<!ENTITY leaveIt.label "Leave it in my Inbox">
+<!ENTITY leaveIt.accesskey "I">
+<!-- LOCALIZATION NOTE moveToSent.label Translate: 'Sent' according to Netscape glossary -->
+<!ENTITY moveToSent.label "Move it to my &quot;Sent&quot; folder">
+<!ENTITY moveToSent.accesskey "M">
+<!ENTITY requestMDN.label "When I receive a request for a return receipt:">
+<!ENTITY returnSome.label "Allow return receipts for some messages">
+<!ENTITY returnSome.accesskey "e">
+<!ENTITY never.label "Never send a return receipt">
+<!ENTITY never.accesskey "N">
+<!ENTITY notInToCc.label "If I'm not in the To or Cc of the message:">
+<!ENTITY notInToCc.accesskey "T">
+<!ENTITY outsideDomain.label "If the sender is outside my domain:">
+<!ENTITY outsideDomain.accesskey "s">
+<!ENTITY otherCases.label "In all other cases:">
+<!ENTITY otherCases.accesskey "o">
+<!ENTITY askMe.label "Ask me">
+<!ENTITY alwaysSend.label "Always send">
+<!ENTITY neverSend.label "Never send">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.properties b/comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.properties
new file mode 100644
index 0000000000..90dd7bcc4d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-mdn.properties
@@ -0,0 +1,6 @@
+# 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/.
+
+## Strings used in prefs.
+prefPanel-mdn=Return Receipts
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-offline.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-offline.dtd
new file mode 100644
index 0000000000..24390ff54b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-offline.dtd
@@ -0,0 +1,57 @@
+<!-- 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/. -->
+
+<!ENTITY doNotDownloadPop3Movemail.label "To save disk space, do not download:">
+<!ENTITY doNotDownloadNntp.label "To save disk space, do not download for offline use:">
+<!ENTITY doNotDownloadImap.label "To save disk space, downloading messages from the server and keeping local copies for offline use can be restricted by age or size.">
+<!ENTITY allFoldersOffline2.label "Keep messages in all folders for this account on this computer">
+<!ENTITY allFoldersOffline2.accesskey "o">
+<!ENTITY allFoldersOfflineNote.label "Note: Changing this affects all folders in this account. To set individual folders, use the Advanced… button.">
+<!ENTITY offlineNotDownload.label "Messages larger than">
+<!ENTITY offlineNotDownload.accesskey "M">
+<!ENTITY autosyncNotDownload.label "Don't download messages larger than">
+<!ENTITY autosyncNotDownload.accesskey "m">
+<!ENTITY kb.label "KB">
+<!ENTITY daysOld.label "days old">
+<!ENTITY message.label "messages">
+<!ENTITY nntpNotDownloadRead.label "Read messages">
+<!ENTITY nntpNotDownloadRead.accesskey "d">
+<!ENTITY nntpDownloadMsg.label "Messages more than">
+<!ENTITY nntpDownloadMsg.accesskey "e">
+<!ENTITY retentionCleanup.label "To recover disk space, old messages can be permanently deleted.">
+<!ENTITY retentionCleanupImap.label "To recover disk space, old messages can be permanently deleted, both the local copies and their originals on the server.">
+<!ENTITY retentionCleanupPop.label "To recover disk space, old messages can be permanently deleted, including their originals on the server.">
+<!ENTITY retentionKeepMsg.label "Delete messages more than">
+<!ENTITY retentionKeepMsg.accesskey "t">
+<!ENTITY retentionKeepAll.label "Don't delete any messages">
+<!ENTITY retentionKeepAll.accesskey "n">
+<!ENTITY retentionKeepRecent.label "Delete all but the most recent">
+<!ENTITY retentionKeepRecent.accesskey "b">
+<!ENTITY retentionApplyToFlagged.label "Always keep flagged messages">
+<!ENTITY retentionApplyToFlagged.accesskey "k">
+<!ENTITY nntpRemoveMsgBody.label "Remove bodies from messages more than">
+<!ENTITY nntpRemoveMsgBody.accesskey "o">
+<!ENTITY offlineSelectNntp.label "Select newsgroups for offline use…">
+<!ENTITY offlineSelectNntp.accesskey "S">
+<!ENTITY offlineImapAdvancedOffline.label "Advanced…">
+<!ENTITY offlineImapAdvancedOffline.accesskey "v">
+<!ENTITY syncGroupTitle.label "Message Synchronizing">
+<!ENTITY diskspaceGroupTitle.label "Disk Space">
+
+<!-- LOCALIZATION NOTE: (ageAutosyncBefore.label, ageAutosyncMiddle.label, ageAutosyncAfter.label):
+ The entities ageAutosyncBefore.label, ageAutosyncMiddle.label, and ageAutosyncAfter.label appear
+ on a single line within the scope of useAutosync.ByAge as follows:
+
+ &ageAutosyncBefore.label [textbox for autosync value] &ageAutosyncMiddle.label; [dropdown for autosync interval] &ageAutosyncAfter.label;
+-->
+<!ENTITY allAutosync.label "Synchronize all messages locally regardless of age">
+<!ENTITY allAutosync.accesskey "c">
+<!ENTITY ageAutosyncBefore.label "Synchronize the most recent">
+<!ENTITY ageAutosync.accesskey "S">
+<!ENTITY ageAutosyncMiddle.label "">
+<!ENTITY dayAgeInterval.label "Days">
+<!ENTITY weekAgeInterval.label "Weeks">
+<!ENTITY monthAgeInterval.label "Months">
+<!ENTITY yearAgeInterval.label "Years">
+<!ENTITY ageAutosyncAfter.label "">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-server-advanced.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-server-advanced.dtd
new file mode 100644
index 0000000000..b06449d931
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-server-advanced.dtd
@@ -0,0 +1,31 @@
+<!-- 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/. -->
+
+<!ENTITY serverAdvanced.label "Advanced Account Settings">
+<!-- LOCALIZATION NOTE (serverDirectory.label): DONT_TRANSLATE "IMAP" -->
+<!ENTITY serverDirectory.label "IMAP server directory:">
+<!ENTITY serverDirectory.accesskey "d">
+<!ENTITY usingSubscription.label "Show only subscribed folders">
+<!ENTITY usingSubscription.accesskey "w">
+<!ENTITY dualUseFolders.label "Server supports folders that contain sub-folders and messages">
+<!ENTITY dualUseFolders.accesskey "f">
+<!ENTITY maximumConnectionsNumber.label "Maximum number of server connections to cache">
+<!ENTITY maximumConnectionsNumber.accesskey "M">
+<!-- LOCALIZATION NOTE (namespaceDesc.label): DONT_TRANSLATE "IMAP" -->
+<!ENTITY namespaceDesc.label "These preferences specify the namespaces on your IMAP server">
+<!ENTITY personalNamespace.label "Personal namespace:">
+<!ENTITY personalNamespace.accesskey "P">
+<!ENTITY publicNamespace.label "Public (shared):">
+<!ENTITY publicNamespace.accesskey "u">
+<!ENTITY otherUsersNamespace.label "Other Users:">
+<!ENTITY otherUsersNamespace.accesskey "O">
+<!ENTITY overrideNamespaces.label "Allow server to override these namespaces">
+<!ENTITY overrideNamespaces.accesskey "A">
+<!ENTITY pop3DeferringDesc.label "When downloading mail from this account's server, use the following folder to store new messages:">
+<!ENTITY accountInbox.label "Inbox for this account">
+<!ENTITY accountInbox.accesskey "s">
+<!ENTITY deferToServer.label "Inbox for different account">
+<!ENTITY deferToServer.accesskey "d">
+<!ENTITY deferGetNewMail.label "Include this server when getting new mail">
+<!ENTITY deferGetNewMail.accesskey "I">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-server-top.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-server-top.dtd
new file mode 100644
index 0000000000..f34e822f2d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-server-top.dtd
@@ -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/. -->
+
+<!ENTITY messageStorage.label "Message Storage">
+<!ENTITY securitySettings.label "Security Settings">
+<!ENTITY serverSettings.label "Server Settings">
+<!ENTITY serverType.label "Server Type:">
+<!ENTITY serverName.label "Server Name:">
+<!ENTITY serverName.accesskey "S">
+<!ENTITY userName.label "User Name:">
+<!ENTITY userName.accesskey "N">
+<!ENTITY port.label "Port:">
+<!ENTITY port.accesskey "P">
+<!ENTITY serverPortDefault.label "Default:">
+<!-- LOCALIZATION NOTE (biffStart.label) : translate below 2 line with grammer dependency
+ For example, in Japanese cases:
+ biffStart.label "every"
+ biffEnd.label "minutes for new messages Check"
+-->
+<!ENTITY biffStart.label "Check for new messages every ">
+<!ENTITY biffStart.accesskey "y">
+<!ENTITY biffEnd.label "minutes">
+<!ENTITY useIdleNotifications.label "Allow immediate server notifications when new messages arrive">
+<!ENTITY useIdleNotifications.accesskey "w">
+<!ENTITY connectionSecurity.label "Connection security:">
+<!ENTITY connectionSecurity.accesskey "u">
+<!ENTITY connectionSecurityType-0.label "None">
+<!ENTITY connectionSecurityType-1.label "STARTTLS, if available">
+<!ENTITY connectionSecurityType-2.label "STARTTLS">
+<!ENTITY connectionSecurityType-3.label "SSL/TLS">
+<!ENTITY authMethod.label "Authentication method:">
+<!ENTITY authMethod.accesskey "i">
+<!ENTITY leaveOnServer.label "Leave messages on server">
+<!ENTITY leaveOnServer.accesskey "g">
+<!ENTITY headersOnly.label "Fetch headers only">
+<!ENTITY headersOnly.accesskey "e">
+<!ENTITY deleteByAgeFromServer.label "For at most">
+<!ENTITY deleteByAgeFromServer.accesskey "o">
+<!ENTITY daysEnd.label "days">
+<!ENTITY deleteOnServer2.label "Until I delete them">
+<!ENTITY deleteOnServer2.accesskey "d">
+<!ENTITY downloadOnBiff.label "Automatically download new messages">
+<!ENTITY downloadOnBiff.accesskey "m">
+<!ENTITY deleteMessagePrefix.label "When I delete a message:">
+<!ENTITY modelMoveToTrash.label "Move it to this folder:">
+<!ENTITY modelMoveToTrash.accesskey "o">
+<!ENTITY modelMarkDeleted.label "Just mark it as deleted">
+<!ENTITY modelMarkDeleted.accesskey "k">
+<!ENTITY modelDeleteImmediately.label "Remove it immediately">
+<!ENTITY modelDeleteImmediately.accesskey "d">
+<!-- LOCALIZATION NOTE (expungeOnExit.label) : do not translate two of "&quot;" in below line -->
+<!ENTITY expungeOnExit.label "Clean up (&quot;Expunge&quot;) Inbox on Exit">
+<!ENTITY expungeOnExit.accesskey "E">
+<!ENTITY emptyTrashOnExit.label "Empty Trash on Exit">
+<!ENTITY emptyTrashOnExit.accesskey "x">
+<!ENTITY loginAtStartup.label "Check for new messages at startup">
+<!ENTITY loginAtStartup.accesskey "C">
+<!-- LOCALIZATION NOTE (maxMessagesStart.label) : translate below 2 lines with grammar dependency
+ maxMessengerStart.label will be followed by maxMessagesEnd.label with the number
+ of messages between them
+-->
+<!ENTITY maxMessagesStart.label "Ask me before downloading more than">
+<!ENTITY maxMessagesStart.accesskey "m">
+<!-- LOCALIZATION NOTE (maxMessagesEnd.label) : see note for maxMessagesStart.label -->
+<!ENTITY maxMessagesEnd.label "messages">
+<!ENTITY alwaysAuthenticate.label "Always request authentication when connecting to this server">
+<!ENTITY alwaysAuthenticate.accesskey "w">
+<!ENTITY newsrcFilePath.label "newsrc file:">
+<!ENTITY newsrcPicker.label "Select newsrc File">
+<!ENTITY abbreviate.label "Show newsgroup names in the Mail Folder pane as:">
+<!ENTITY abbreviateOn.label "Full names (For example, 'netscape.public.mozilla.mail-news')">
+<!ENTITY abbreviateOff.label "Abbreviate names (For example, 'n.p.m.mail-news')">
+<!ENTITY advancedButton.label "Advanced…">
+<!ENTITY serverDefaultCharset2.label "Default Text Encoding:">
+<!ENTITY advancedButton.accesskey "v">
+<!ENTITY localPath.label "Local directory:">
+<!ENTITY localFolderPicker.label "Select Local Directory">
+<!ENTITY browseFolder.label "Browse…">
+<!ENTITY browseFolder.accesskey "B">
+<!ENTITY browseNewsrc.label "Browse…">
+<!ENTITY browseNewsrc.accesskey "e">
+
+<!ENTITY accountTitle.label "Account Settings">
+<!ENTITY accountSettingsDesc.label "The following is a special account. There are no identities associated with it.">
+<!ENTITY storeType.label "Message Store Type:">
+<!ENTITY storeType.accesskey "T">
+<!ENTITY mboxStore2.label "File per folder (mbox)">
+<!ENTITY maildirStore.label "File per message (maildir)">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/am-serverwithnoidentities.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/am-serverwithnoidentities.dtd
new file mode 100644
index 0000000000..f568613a16
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/am-serverwithnoidentities.dtd
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+
+<!ENTITY accountName.label "Account Name:">
+<!ENTITY accountName.accesskey "N">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/mailPrefsOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/mailPrefsOverlay.dtd
new file mode 100644
index 0000000000..27c390c0f9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/mailPrefsOverlay.dtd
@@ -0,0 +1,21 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- These are added to the Window menu -->
+<!ENTITY mail.label "Mail &amp; Newsgroups">
+<!ENTITY mail.accesskey "M">
+<!ENTITY addressbook.label "Address Book">
+<!ENTITY addressbook.accesskey "A">
+
+<!-- These are added to Preferences dialog -->
+<!ENTITY viewingMessages.label "Message Display">
+<!ENTITY notifications.label "Notifications">
+<!ENTITY composingMessages.label "Composition">
+<!ENTITY format.label "Send Format">
+<!ENTITY address.label "Addressing">
+<!ENTITY junk.label "Junk &amp; Suspect Mail">
+<!ENTITY tags.label "Tags">
+<!ENTITY return.label "Return Receipts">
+<!ENTITY characterEncoding2.label "Text Encoding">
+<!ENTITY networkStorage.label "Network &amp; Storage">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-addressing.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-addressing.dtd
new file mode 100644
index 0000000000..664f09603e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-addressing.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY pref.addressing.title "Addressing">
+<!ENTITY emailCollectiontitle.label "Email Address Collection">
+<!ENTITY emailCollectionPicker.label "Add email addresses to my:">
+<!ENTITY emailCollectionPicker.accesskey "m">
+<!ENTITY emailCollectiontext.label "Email addresses from outgoing messages can be automatically added to a local address book.">
+
+<!-- Autocompletion -->
+<!ENTITY addressingTitle.label "Address Autocompletion">
+<!ENTITY highlightNonMatches.label "Highlight addresses that do not autocomplete">
+<!ENTITY highlightNonMatches.accesskey "a">
+<!ENTITY addressingEnable.label "Local Address Books">
+<!ENTITY addressingEnable.accesskey "L">
+<!ENTITY autocompleteText.label "When addressing messages, look for matching entries in:">
+<!ENTITY directories.label "Directory Server:">
+<!ENTITY directories.accesskey "D">
+<!ENTITY directoriesNone.label "None">
+<!ENTITY editDirectories.label "Edit Directories…">
+<!ENTITY editDirectories.accesskey "E">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-character_encoding.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-character_encoding.dtd
new file mode 100755
index 0000000000..1445347157
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-character_encoding.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!ENTITY pref.character.encoding2.title "Text Encoding">
+<!ENTITY messageDisplay.caption "Message Display">
+<!ENTITY viewFallbackCharset2.label "Fallback Text Encoding:">
+<!ENTITY viewFallbackCharset2.accesskey "E">
+<!ENTITY viewFallbackCharset.desc "(Used for legacy content that does not declare its encoding.)">
+
+<!ENTITY composingMessages.caption "Composing Messages">
+<!ENTITY useMIME.label "For messages that contain 8-bit characters, use 'quoted printable' MIME encoding. Leave unchecked to send the messages as is.">
+<!ENTITY useMIME.accesskey "F">
+<!ENTITY sendDefaultCharset2.label "Default Text Encoding:">
+<!ENTITY sendDefaultCharset2.accesskey "T">
+<!ENTITY replyInDefaultCharset3.label "When possible, use this default text encoding in replies. (When unchecked, only new messages use this default.)">
+<!ENTITY replyInDefaultCharset3.accesskey "W">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-composing_messages.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-composing_messages.dtd
new file mode 100644
index 0000000000..3a9fd28e5b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-composing_messages.dtd
@@ -0,0 +1,56 @@
+<!-- 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/. -->
+
+<!ENTITY pref.composing.messages.title "Composition">
+<!ENTITY generalComposing.label "General">
+<!ENTITY forwardMsg.label "Forward messages:">
+<!ENTITY inline.label "Inline">
+<!ENTITY inline.accesskey "n">
+<!ENTITY asAttachment.label "As Attachment">
+<!ENTITY asAttachment.accesskey "A">
+
+<!ENTITY replyQuoteInline.label "Quote attachments viewed inline in replies">
+<!ENTITY replyQuoteInline.accesskey "Q">
+<!ENTITY warnOnSendAccelKey.label "Confirm when using keyboard shortcut to send message">
+<!ENTITY warnOnSendAccelKey.accesskey "k">
+<!-- LOCALIZATION NOTE (autoSave.label): This will concatenate with
+ "xxx minutes", using a number and (autoSaveEnd.label). -->
+<!ENTITY autoSave.label "Automatically save the message every">
+<!ENTITY autoSave.accesskey "u">
+<!ENTITY autoSaveEnd.label "minutes">
+<!-- LOCALIZATION NOTE (wrapOutMsg.label): This will concatenate with "xxx characters", using a number and (char.label). -->
+<!ENTITY wrapOutMsg.label "Wrap plain text messages at">
+<!ENTITY wrapOutMsg.accesskey "W">
+<!ENTITY char.label "characters">
+
+<!ENTITY defaultMessagesHeader.label "Defaults for HTML Messages">
+<!ENTITY font.label "Font:">
+<!ENTITY font.accesskey "F">
+<!ENTITY size.label "Size:">
+<!ENTITY size.accesskey "S">
+<!ENTITY fontColor.label "Text:">
+<!ENTITY fontColor.accesskey "T">
+<!ENTITY bgColor.label "Background:">
+<!ENTITY bgColor.accesskey "B">
+<!ENTITY defaultCompose.label "Default composition format:">
+<!ENTITY defaultBodyText.label "Body Text (Enter key creates a new line)">
+<!ENTITY defaultBodyText.accesskey "o">
+<!ENTITY defaultParagraph.label "Paragraph (Enter key creates a new paragraph)">
+<!ENTITY defaultParagraph.accesskey "P">
+
+<!ENTITY selectHeaderType.label "Select reply header type:">
+<!ENTITY selectHeaderType.accesskey "e">
+<!ENTITY noReplyOption.label "No Reply Header">
+<!-- LOCALIZATION NOTE (authorWroteOption.label): this is tied to the
+ mailnews.reply_header_authorwrotesingle preference. [Author] needs to be
+ translated. -->
+<!ENTITY authorWroteOption.label "[Author] wrote:">
+<!-- LOCALIZATION NOTE (onDateAuthorWroteOption.label): this is tied to the
+ mailnews.reply_header_ondateauthorwrote preference. [Author] and [date]
+ need to be translated. -->
+<!ENTITY onDateAuthorWroteOption.label "On [date], [Author] wrote:">
+<!-- LOCALIZATION NOTE (onDateAuthorWroteOption.label): this is tied to the
+ mailnews.reply_header_authorwroteondate preference. [Author] and [date]
+ need to be translated. -->
+<!ENTITY authorWroteOnDateOption.label "[Author] wrote on [date]:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd
new file mode 100644
index 0000000000..2e721ce1e4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory-add.dtd
@@ -0,0 +1,45 @@
+<!-- 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/. -->
+
+<!ENTITY directoryName.label "Name: ">
+<!ENTITY directoryName.accesskey "n">
+<!ENTITY directoryHostname.label "Hostname: ">
+<!ENTITY directoryHostname.accesskey "o">
+<!ENTITY directoryBaseDN.label "Base DN: ">
+<!ENTITY directoryBaseDN.accesskey "b">
+<!ENTITY findButton.label "Find">
+<!ENTITY findButton.accesskey "f">
+<!ENTITY directorySecure.label "Use secure connection (SSL)">
+<!ENTITY directorySecure.accesskey "U">
+<!ENTITY directoryLogin.label "Bind DN: ">
+<!ENTITY directoryLogin.accesskey "i">
+<!ENTITY General.tab "General">
+<!ENTITY Offline.tab "Offline">
+<!ENTITY Advanced.tab "Advanced">
+<!ENTITY portNumber.label "Port number: ">
+<!ENTITY portNumber.accesskey "p">
+<!ENTITY searchFilter.label "Search filter: ">
+<!ENTITY searchFilter.accesskey "f">
+<!ENTITY scope.label "Scope: ">
+<!ENTITY scope.accesskey "c">
+<!ENTITY scopeOneLevel.label "One Level">
+<!ENTITY scopeOneLevel.accesskey "L">
+<!ENTITY scopeSubtree.label "Subtree">
+<!ENTITY scopeSubtree.accesskey "S">
+<!ENTITY return.label "Don't return more than">
+<!ENTITY return.accesskey "r">
+<!ENTITY results.label "results">
+<!ENTITY offlineText.label "You can download a local copy of this directory so that it is available for use when you are working offline.">
+<!ENTITY saslMechanism.label "Login method: ">
+<!ENTITY saslMechanism.accesskey "m">
+<!ENTITY saslOff.label "Simple">
+<!ENTITY saslOff.accesskey "l">
+<!ENTITY saslGSSAPI.label "Kerberos (GSSAPI)">
+<!ENTITY saslGSSAPI.accesskey "K">
+
+<!-- Localization note: this is here because the width of the dialog
+ is determined by the width of the base DN box; and that is likely
+ to vary somewhat with the language.
+-->
+<!ENTITY newDirectoryWidth "36em">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory.dtd
new file mode 100644
index 0000000000..874cfe4749
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-directory.dtd
@@ -0,0 +1,17 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE (window.title) : do not translate "LDAP" in below line -->
+<!ENTITY pref.ldap.window.title "LDAP Directory Servers">
+<!-- LOCALIZATION NOTE (directories.label) : do not translate "LDAP" in below line -->
+<!ENTITY directories.label "LDAP Directory Server:">
+<!-- LOCALIZATION NOTE (directoriesText.label) : do not translate "LDAP" in below line -->
+<!ENTITY directoriesText.label "Select an LDAP Directory Server:">
+<!ENTITY directoriesText.accesskey "S">
+<!ENTITY addDirectory.label "Add">
+<!ENTITY addDirectory.accesskey "a">
+<!ENTITY editDirectory.label "Edit">
+<!ENTITY editDirectory.accesskey "e">
+<!ENTITY deleteDirectory.label "Delete">
+<!ENTITY deleteDirectory.accesskey "d">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-formatting.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-formatting.dtd
new file mode 100644
index 0000000000..d4d699f0d4
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-formatting.dtd
@@ -0,0 +1,42 @@
+<!-- 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/. -->
+
+<!ENTITY pref.formatting.title "Send Format">
+<!ENTITY sendMaildesc.label "When sending formatted (HTML) messages and one or more recipients are not listed as being able to receive HTML:">
+<!ENTITY askMe.label "Ask me what to do (Mail prompts you to choose a format)">
+<!ENTITY askMe.accesskey "k">
+<!ENTITY convertPlain2.label "Convert the message to plain text (formatting may be lost)">
+<!ENTITY convertPlain2.accesskey "C">
+<!ENTITY sendHTML2.label "Send the message as HTML only (may cause display problems)">
+<!ENTITY sendHTML2.accesskey "S">
+<!ENTITY sendBoth2.label "Send the message as both plain text and HTML (larger size)">
+<!ENTITY sendBoth2.accesskey "n">
+
+<!-- Html and Plain Text Domains -->
+<!ENTITY domain.title "HTML and Plain Text Domains">
+<!ENTITY domaindesc.label "When you send a message to an address with one of the domain names listed below, Mail automatically sends the message in the preferred format (formatted text or plain text).">
+<!ENTITY HTMLdomaintitle.label "HTML Domains">
+<!ENTITY HTMLdomaintitle.accesskey "M">
+<!ENTITY PlainTexttitle.label "Plain Text Domains">
+<!ENTITY PlainTexttitle.accesskey "P">
+<!ENTITY AddButton.label "Add…">
+<!ENTITY AddHtmlDomain.accesskey "A">
+<!ENTITY AddPlainText.accesskey "d">
+<!ENTITY DeleteButton.label "Delete">
+<!ENTITY DeleteHtmlDomain.accesskey "e">
+<!ENTITY DeletePlainText.accesskey "t">
+
+<!-- Add Domain Name -->
+<!ENTITY add.htmltitle "Add HTML Domain Name">
+<!ENTITY add.htmldomain "HTML Domain Name:">
+<!ENTITY add.plaintexttitle "Add Plain Text Domain Name">
+<!ENTITY add.plaintextdomain "Plain Text Domain Name:">
+
+<!ENTITY domainnameError.title "Domain Name Error">
+<!-- LOCALIZATION NOTE: do not translate @string@ -->
+<!ENTITY invalidEntryError.label "The domain name @string@ is invalid and will be ignored. Valid domain names must include at least one '.' and characters on either side of it.">
+
+<!-- Global auto-detect switch -->
+<!ENTITY autoDowngrade.label "Automatically send the message as plain text if no significant formatting is present (overrides other options)">
+<!ENTITY autoDowngrade.accesskey "o">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-junk.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-junk.dtd
new file mode 100644
index 0000000000..c11959db39
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-junk.dtd
@@ -0,0 +1,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/. -->
+
+<!ENTITY pref.junk.title "Junk &amp; Suspect Mail">
+<!ENTITY junkSettings.caption "Global Junk Mail Settings">
+<!ENTITY junkMail.intro "Goto Account Settings to configure account specific junk mail settings.">
+
+<!-- Junk Mail Controls -->
+<!ENTITY manualMark.label "When I mark messages as junk:">
+<!ENTITY manualMark.accesskey "W">
+<!ENTITY manualMarkModeMove.label "Move them to the account's &quot;Junk&quot; folder">
+<!ENTITY manualMarkModeMove.accesskey "M">
+<!ENTITY manualMarkModeDelete.label "Delete them">
+<!ENTITY manualMarkModeDelete.accesskey "D">
+
+<!ENTITY markAsRead.intro "Mark messages as read:">
+<!ENTITY autoMarkAsRead.label "When &brandShortName; determines that they are junk">
+<!ENTITY autoMarkAsRead.accesskey "u">
+<!ENTITY manualMarkAsRead.label "When I manually mark them as junk">
+<!ENTITY manualMarkAsRead.accesskey "k">
+
+<!ENTITY enableJunkLogging.label "Enable junk filter logging">
+<!ENTITY enableJunkLogging.accesskey "E">
+<!ENTITY openJunkLog.label "Show log">
+<!ENTITY openJunkLog.accesskey "S">
+<!ENTITY resetTrainingData.label "Reset training data">
+<!ENTITY resetTrainingData.accesskey "R">
+
+<!ENTITY pref.suspectMail.caption "Suspect Mail">
+
+<!-- Phishing Detector -->
+<!ENTITY pref.phishing.caption "E-mail Scams">
+<!ENTITY enablePhishingDetector.label "Tell me if the message I'm reading is a suspected email scam">
+<!ENTITY enablePhishingDetector.accesskey "T">
+
+<!-- Anti Virus -->
+<!ENTITY pref.antivirus.caption "Antivirus">
+<!ENTITY antiVirus.label "Allow antivirus clients to scan incoming messages more easily">
+<!ENTITY antiVirus.accesskey "A">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-mailnews.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-mailnews.dtd
new file mode 100644
index 0000000000..c2c8ef7ead
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-mailnews.dtd
@@ -0,0 +1,33 @@
+<!-- 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/. -->
+
+<!ENTITY pref.mailnews.title "Mail &amp; Newsgroups">
+<!ENTITY generalSettings.caption "General Settings">
+<!ENTITY confirmMove.label "Confirm when moving folders to the Trash">
+<!ENTITY confirmMove.accesskey "C">
+<!ENTITY preserveThreading.label "Preserve threading when sorting messages">
+<!ENTITY preserveThreading.accesskey "v">
+<!ENTITY mailAutoHide.label "Hide the mail tab bar when only one tab is open">
+<!ENTITY mailAutoHide.accesskey "b">
+<!ENTITY loadInBackground.label "Switch to new tabs when opened">
+<!ENTITY loadInBackground.accesskey "t">
+<!ENTITY mailOpenTabsFor.label "Open tabs instead of windows for">
+<!ENTITY mailMiddleClick.label "Middle-click or Ctrl+Enter on a message or a folder">
+<!ENTITY mailMiddleClick.accesskey "M">
+<!ENTITY mailMiddleClickMac.label "Middle-click or &#8984;+Return on a message or a folder">
+<!ENTITY mailMiddleClickMac.accesskey "M">
+<!ENTITY mailDoubleClick.label "Double-click on a message">
+<!ENTITY mailDoubleClick.accesskey "k">
+<!ENTITY mailBiffOnNewWindow.label "Only check for new mail after opening Mail &amp; Newsgroups">
+<!ENTITY mailBiffOnNewWindow.accesskey "O">
+
+<!ENTITY messengerStartPage.caption "Mail Start Page">
+<!ENTITY enableStartPage.label "When Mail launches, show the Start Page in the message area">
+<!ENTITY enableStartPage.accesskey "W">
+<!ENTITY location.label "Location:">
+<!ENTITY location.accesskey "L">
+<!ENTITY useDefault.label "Restore Default">
+<!ENTITY useDefault.accesskey "D">
+<!ENTITY rememberLastMsg.label "Remember the last selected message">
+<!ENTITY rememberLastMsg.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-notifications.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-notifications.dtd
new file mode 100644
index 0000000000..8d4a5e1929
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-notifications.dtd
@@ -0,0 +1,46 @@
+<!-- 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/. -->
+
+<!ENTITY pref.notifications.title "Notifications">
+<!ENTITY notifications.caption "Notifications">
+<!ENTITY newMessagesArrive.label "When new messages arrive:">
+
+<!-- LOCALIZATION NOTE: (showAlertFor.label, showAlertTimeEnd.label)
+ showAlertFor.label is associated with the checkbox to show the alert,
+ leading into a numerical field to enter a time in seconds, followed by
+ showAlertTimeEnd.label which in en-US is the unit of the value -->
+
+<!ENTITY showAlertFor.label "Show an alert for">
+<!ENTITY showAlertFor.accesskey "S">
+<!ENTITY showAlertTimeEnd.label "seconds">
+
+<!ENTITY showAlertPreviewText.label "Show a preview of the message text">
+<!ENTITY showAlertPreviewText.accesskey "v">
+<!ENTITY showAlertSubject.label "Show the subject">
+<!ENTITY showAlertSubject.accesskey "u">
+<!ENTITY showAlertSender.label "Show the sender">
+<!ENTITY showAlertSender.accesskey "n">
+<!ENTITY useSystemAlert.label "Use the operating system&apos;s desktop notifications">
+<!ENTITY useSystemAlert.accesskey "d">
+<!ENTITY useBuiltInAlert.label "Use &brandShortName;&apos;s own notification windows">
+<!ENTITY useBuiltInAlert.accesskey "w">
+
+<!ENTITY showTrayIcon.label "Show a tray icon">
+<!ENTITY showTrayIcon.accesskey "t">
+<!ENTITY showBalloon.label "Show a balloon alert">
+<!ENTITY showBalloon.accesskey "o">
+<!ENTITY bounceSystemDockIcon.label "Animate the Dock icon">
+<!ENTITY bounceSystemDockIcon.accesskey "A">
+
+<!ENTITY playSound.label "Play a sound">
+<!ENTITY playSound.accesskey "P">
+<!ENTITY playButton.label "Play">
+<!ENTITY playButton.accesskey "l">
+<!ENTITY systemsound.label "System New Mail Sound">
+<!ENTITY systemsound.accesskey "M">
+<!ENTITY customsound.label "Custom sound file">
+<!ENTITY customsound.accesskey "C">
+<!ENTITY browse.label "Browse…">
+<!ENTITY browse.accesskey "B">
+<!ENTITY browse.title "Choose file">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-offline.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-offline.dtd
new file mode 100644
index 0000000000..988395a433
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-offline.dtd
@@ -0,0 +1,38 @@
+<!-- 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/. -->
+
+<!-- extracted from content/pref-offline.xul -->
+
+<!--LOCALIZATION NOTE : FILE The 'Offline' preferences dialog -->
+<!ENTITY pref.network.title "Network &amp; Storage">
+<!ENTITY pref.offline.caption "Offline">
+<!ENTITY textStartUp "When starting up:">
+<!ENTITY textStartUp.accesskey "W">
+<!ENTITY menuitemRememberPrevState "Remember previous online state">
+<!ENTITY menuitemAskMe "Ask me for online state at startup">
+<!ENTITY menuitemAlwaysOnline "Always start up online">
+<!ENTITY menuitemAlwaysOffline "Always start up offline">
+<!ENTITY menuitemAutomatic "Detect automatically (if available)">
+<!ENTITY textGoingOnline "Send unsent messages when going online?">
+<!ENTITY radioAutoSend "Yes">
+<!ENTITY radioAutoSend.accesskey "Y">
+<!ENTITY radioNotSend "No">
+<!ENTITY radioNotSend.accesskey "N">
+<!ENTITY radioAskUnsent "Ask me">
+<!ENTITY radioAskUnsent.accesskey "s">
+<!ENTITY textGoingOffline "Download messages for offline use when going offline?">
+<!ENTITY radioAutoDownload "Yes">
+<!ENTITY radioAutoDownload.accesskey "e">
+<!ENTITY radioNotDownload "No">
+<!ENTITY radioNotDownload.accesskey "o">
+<!ENTITY radioAskDownload "Ask me">
+<!ENTITY radioAskDownload.accesskey "k">
+<!ENTITY mailConnections.caption "Mail Connections">
+<!ENTITY mailnewsTimeout.label "Connection timeout:">
+<!ENTITY mailnewsTimeout.accesskey "m">
+<!ENTITY mailnewsTimeoutSeconds.label "seconds">
+<!ENTITY Diskspace "Disk Space">
+<!ENTITY offlineCompactFolders.label "Compact all folders when it will save over">
+<!ENTITY offlineCompactFolders.accesskey "C">
+<!ENTITY offlineCompactFoldersMB.label "MB in total">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-receipts.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-receipts.dtd
new file mode 100644
index 0000000000..b36d1ba706
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-receipts.dtd
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE pref-receipts.dtd: UI for Mail/News Return Receipts prefs -->
+<!ENTITY pref.receipts.title "Return Receipts">
+<!ENTITY prefReceipts.caption "Return Receipts">
+<!ENTITY requestReceipt.label "When sending messages, always request a return receipt">
+<!ENTITY requestReceipt.accesskey "W">
+<!ENTITY receiptArrive.label "When a receipt arrives:">
+<!ENTITY leaveIt.label "Leave it in my Inbox">
+<!ENTITY leaveIt.accesskey "L">
+<!ENTITY moveToSent.label "Move it to my &quot;Sent&quot; folder">
+<!ENTITY moveToSent.accesskey "M">
+<!ENTITY requestMDN.label "When I receive a request for a return receipt:">
+<!ENTITY returnSome.label "Allow return receipts for some messages">
+<!ENTITY returnSome.accesskey "A">
+<!ENTITY never.label "Never send a return receipt">
+<!ENTITY never.accesskey "N">
+<!ENTITY notInToCc.label "If I'm not in the To or Cc of the message:">
+<!ENTITY notInToCc.accesskey "T">
+<!ENTITY outsideDomain.label "If the sender is outside my domain:">
+<!ENTITY outsideDomain.accesskey "s">
+<!ENTITY otherCases.label "In all other cases:">
+<!ENTITY otherCases.accesskey "o">
+<!ENTITY askMe.label "Ask me">
+<!ENTITY alwaysSend.label "Always send">
+<!ENTITY neverSend.label "Never send">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-tags.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-tags.dtd
new file mode 100644
index 0000000000..e96c0daa00
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-tags.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!ENTITY pref.tags.title "Tags">
+<!ENTITY pref.tags.caption "Customize Tags">
+<!ENTITY pref.tags.description "Tags can be used to categorize and prioritize your messages. Modify the appearance and importance of tags using the settings below. Tags near the top are more important than those further down.">
+<!ENTITY tagColumn.label "Tag">
+<!ENTITY colorColumn.label "Color">
+<!ENTITY defaultTagName.label "Untitled Tag">
+<!ENTITY addTagButton.label "Add">
+<!ENTITY addTagButton.accesskey "A">
+<!ENTITY deleteTagButton.label "Delete">
+<!ENTITY deleteTagButton.accesskey "D">
+<!ENTITY raiseTagButton.label "Raise Importance">
+<!ENTITY raiseTagButton.accesskey "R">
+<!ENTITY lowerTagButton.label "Lower Importance">
+<!ENTITY lowerTagButton.accesskey "L">
+<!ENTITY restoreButton.label "Restore Defaults">
+<!ENTITY restoreButton.accesskey "s">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/pref-viewing_messages.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-viewing_messages.dtd
new file mode 100644
index 0000000000..ec0a856d74
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/pref-viewing_messages.dtd
@@ -0,0 +1,47 @@
+<!-- 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/. -->
+
+<!ENTITY pref.viewing.messages.title "Message Display">
+<!ENTITY style.label "Style:">
+<!ENTITY style.accesskey "S">
+<!ENTITY regular.label "Regular">
+<!ENTITY bold.label "Bold">
+<!ENTITY italic.label "Italic">
+<!ENTITY boldItalic.label "Bold Italic">
+<!ENTITY size.label "Size:">
+<!ENTITY size.accesskey "z">
+<!ENTITY bigger.label "Bigger">
+<!ENTITY smaller.label "Smaller">
+<!ENTITY color.label "Color:">
+<!ENTITY color.accesskey "C">
+<!ENTITY displayPlainText.caption "Plain text messages">
+<!ENTITY fontPlainText.label "Font:">
+<!ENTITY fontPlainText.accesskey "F">
+<!ENTITY displayQuoted.label "Settings for quoted messages:">
+<!ENTITY wrapInMsg.label "Wrap text to fit window width">
+<!ENTITY wrapInMsg.accesskey "W">
+<!-- LOCALIZATION NOTE : (convertEmoticons.label) 'Emoticons' are also known as 'Smileys', e.g. :-) -->
+<!ENTITY convertEmoticons.label "Display emoticons as graphics">
+<!ENTITY convertEmoticons.accesskey "D">
+<!ENTITY generalMessageDisplay.caption "General">
+<!ENTITY autoMarkAsRead.label "Automatically mark messages as read">
+<!ENTITY autoMarkAsRead.accesskey "A">
+<!-- LOCALIZATION NOTE (markAsReadAfter.label): This will concatenate to
+ "Only after displaying for [___] seconds",
+ using (markAsReadAfter.label) and a number (secondsLabel.label). -->
+<!ENTITY markAsReadAfter.label "Only after displaying for">
+<!ENTITY markAsReadAfter.accesskey "t">
+<!ENTITY secondsLabel.label "seconds">
+
+<!ENTITY openingMessages.label "When opening messages, display them in:">
+<!ENTITY newWindowRadio.label "A new message window">
+<!ENTITY newWindowRadio.accesskey "n">
+<!ENTITY existingWindowRadio.label "An existing message window">
+<!ENTITY existingWindowRadio.accesskey "e">
+<!ENTITY disableContent.label "Block images and other content from remote sources">
+<!ENTITY disableContent.accesskey "B">
+<!ENTITY showCondensedAddresses.label "Show only display name for people in my address book">
+<!ENTITY showCondensedAddresses.accesskey "o">
+<!ENTITY closeMsgWindowOnDelete.label "Close message window when deleting the message">
+<!ENTITY closeMsgWindowOnDelete.accesskey "l">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/prefs.properties b/comm/suite/locales/en-US/chrome/mailnews/pref/prefs.properties
new file mode 100644
index 0000000000..bda2064c91
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/prefs.properties
@@ -0,0 +1,90 @@
+# 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/.
+
+#
+# The following are used by the Account Wizard
+#
+enterValidEmail=Please enter a valid email address.
+accountNameExists=An account with this name already exists. Please enter a different account name.
+accountNameEmpty=The account name can not be empty.
+modifiedAccountExists=An account with that user name and server name already exists. Please enter a different user name and/or server name.
+userNameChanged=Your User Name has been updated. You may also need to update your Email Address and/or User Name associated with this account.
+serverNameChanged=The server name setting has changed. Please verify that any folders used by filters exist on the new server.
+# LOCALIZATION NOTE (junkSettingsBroken): %1$S is the account name
+junkSettingsBroken=The Junk settings on account "%1$S" have a possible problem. Would you like to review them before saving Account Settings?
+# LOCALIZATION NOTE (localDirectoryChanged): %1$S is program name (&brandShortName;)
+localDirectoryChanged=%1$S needs to restart now to apply the change to the Local directory setting.
+localDirectoryRestart=Restart
+userNameEmpty=The user name can not be empty.
+# LOCALIZATION NOTE (localDirectoryInvalid): %1$S is path to folder
+localDirectoryInvalid=The Local Directory path "%1$S" is invalid. Please pick a different directory.
+# LOCALIZATION NOTE (localDirectoryNotAllowed): %1$S is path to folder
+localDirectoryNotAllowed=The Local Directory path "%1$S" is not suitable for message storage. Please choose another directory.
+# if the user chooses to cancel the wizard when no accounts are there throw a message
+# LOCALIZATION NOTE (cancelWizard)
+# do not localize "\n\n"
+cancelWizard=Are you sure you want to exit the Account Wizard?\n\nIf you exit, any information you have entered will be lost and the account will not be created.
+accountWizard=Account Wizard
+WizardExit=Exit
+WizardContinue=Cancel
+# when the wizard already has a domain (Should we say something different?)
+enterValidServerName=Please enter a valid server name.
+failedRemoveAccount=Failed to remove this account.
+#LOCALIZATION NOTE: accountName: %1$S is server name, %2$S is user name
+accountName=%1$S - %2$S
+
+# LOCALIZATION NOTE: confirmDeferAccountWarning: do not localize "\n\n", it means a new empty line in the string.
+confirmDeferAccountWarning=If you store this account's new mail in a different account's Inbox, you will no longer be able to access already downloaded e-mail for this account. If you have mail in this account, please copy it to another account first.\n\nIf you have filters that filter mail into this account, you should disable them or change the destination folder. If any accounts have special folders in this account (Sent, Drafts, Templates, Archives, Junk), you should change them to be in another account.\n\nDo you still want to store this account's e-mail in a different account?
+confirmDeferAccountTitle=Defer Account?
+
+directoryAlreadyUsedByOtherAccount=The directory specified in the Local Directory setting is already used by the "%S" account. Please pick a different directory.
+directoryParentUsedByOtherAccount=A parent directory of the directory specified in the Local Directory setting is already used by the "%S" account. Please pick a different directory.
+directoryChildUsedByOtherAccount=A subdirectory of the directory specified in the Local Directory setting is already used by the "%S" account. Please pick a different directory.
+#Provide default example values for sample email address
+exampleEmailUserName=user
+exampleEmailDomain=example.net
+emailFieldText=Email Address:
+#LOCALIZATION NOTE: defaultEmailText: %1$S is user name, %2$S is domain
+defaultEmailText=Enter your email address. This is the address others will use to send email to you (for example, "%1$S@%2$S").
+#LOCALIZATION NOTE: customizedEmailText: %1$S is provider, %2$S is email username, %3$S is sample email, %4$S is sample username
+customizedEmailText=Enter your %1$S %2$S (for example, if your %1$S email address is "%3$S", your %2$S is "%4$S").
+
+# account manager stuff
+prefPanel-server=Server Settings
+prefPanel-copies=Copies & Folders
+prefPanel-synchronization=Synchronization & Storage
+prefPanel-diskspace=Disk Space
+prefPanel-addressing=Composition & Addressing
+prefPanel-junk=Junk Settings
+## LOCALIZATION NOTE (prefPanel-smtp): Don't translate "SMTP"
+prefPanel-smtp=Outgoing Server (SMTP)
+
+# account manager multiple identity support
+#LOCALIZATION NOTE: accountName: %1$S
+identity-list-title=Identities for %1$S
+
+identityDialogTitleAdd=New Identity
+## LOCALIZATION NOTE (identityDialogTitleEdit): %S is the identity name
+identityDialogTitleEdit=Edit %S
+
+identity-edit-req=You must specify a valid email address for this identity.
+identity-edit-req-title=Error Creating Identity
+
+## LOCALIZATION NOTE (identity-delete-confirm): %S is the identity name
+# and should be put on a new line. The new line is produced with the "\n" string.
+identity-delete-confirm=Are you sure you want to delete the identity\n%S?
+## LOCALIZATION NOTE (identity-delete-confirm-title): %S is the account name
+identity-delete-confirm-title=Deleting identity for %S
+identity-delete-confirm-button=Delete
+
+choosefile=Choose a file
+
+forAccount=For account "%S"
+
+removeFromServerTitle=Confirm permanent, automatic deletion of messages
+removeFromServer=This setting will permanently delete old messages from the remote server AND your local storage. Are you sure you want to proceed?
+
+confirmSyncChangesTitle=Confirm synchronization changes
+confirmSyncChanges=The Message Synchronization settings were changed.\n\nDo you want to save them?
+confirmSyncChangesDiscard=Discard
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.dtd
new file mode 100644
index 0000000000..fbc22bcec3
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY dialogTitle "Remove Account and Data">
+<!ENTITY removeButton.label "Remove">
+<!ENTITY removeButton.accesskey "R">
+<!ENTITY removeAccount.label "Remove account information">
+<!ENTITY removeAccount.accesskey "a">
+<!ENTITY removeAccount.desc "Removes only &brandShortName;'s knowledge of this account. Does not affect the account itself on the server.">
+<!ENTITY removeData.label "Remove message data">
+<!ENTITY removeData.accesskey "d">
+<!ENTITY removeDataChat.label "Remove conversation data">
+<!ENTITY removeDataChat.accesskey "d">
+<!ENTITY removeDataLocalAccount.desc "Removes all messages, folders and filters associated with this account from your local disk. This does not affect some messages which may still be kept on the server. Do not choose this if you plan to archive the local data or re-use it in &brandShortName; later.">
+<!ENTITY removeDataServerAccount.desc "Removes all messages, folders and filters associated with this account from your local disk. Your messages and folders are still kept on the server.">
+<!ENTITY removeDataChatAccount.desc "Removes all logs of conversations stored for this account on your local disk.">
+<!ENTITY showData.label "Show data location">
+<!ENTITY showData.accesskey "S">
+<!ENTITY progressPending "Removing selected data…">
+<!ENTITY progressSuccess "Removal succeeded.">
+<!ENTITY progressFailure "Removal failed.">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.properties b/comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.properties
new file mode 100644
index 0000000000..535fd1ea9c
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/removeAccount.properties
@@ -0,0 +1,5 @@
+# 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/.
+
+removeQuestion=Are you sure you want to remove the account "%S"?
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/replicationProgress.properties b/comm/suite/locales/en-US/chrome/mailnews/pref/replicationProgress.properties
new file mode 100644
index 0000000000..9acf606463
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/replicationProgress.properties
@@ -0,0 +1,20 @@
+# 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/.
+replicationStarted=Replication started…
+changesStarted=Started finding changes to replicate…
+replicationSucceeded=Replication succeeded
+replicationFailed=Replication failed
+replicationCancelled=Replication cancelled
+# LOCALIZATION NOTE
+# do not localize %S. %S is the current entry number (an integer)
+currentCount=Replicating directory entry: %S
+
+downloadButton=Download Now
+downloadButton.accesskey=D
+cancelDownloadButton=Cancel Download
+cancelDownloadButton.accesskey=C
+
+directoryTitleNew=New LDAP Directory
+## LOCALIZATION NOTE (directoryTitleEdit): %S will be replaced by the LDAP directory's display name
+directoryTitleEdit=%S Properties
diff --git a/comm/suite/locales/en-US/chrome/mailnews/pref/smtpEditOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/pref/smtpEditOverlay.dtd
new file mode 100644
index 0000000000..593393a21b
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/pref/smtpEditOverlay.dtd
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+
+<!ENTITY settings.caption "Settings">
+<!ENTITY security.caption "Security and Authentication">
+<!ENTITY serverName.label "Server Name:">
+<!ENTITY serverName.accesskey "S">
+<!ENTITY serverDescription.label "Description:">
+<!ENTITY serverDescription.accesskey "D">
+<!ENTITY serverPort.label "Port:">
+<!ENTITY serverPort.accesskey "P">
+<!ENTITY userName.label "User Name:">
+<!ENTITY userName.accesskey "m">
+<!ENTITY connectionSecurity.label "Connection security:">
+<!ENTITY connectionSecurity.accesskey "n">
+<!ENTITY connectionSecurityType-0.label "None">
+<!ENTITY connectionSecurityType-1.label "STARTTLS, if available">
+<!ENTITY connectionSecurityType-2.label "STARTTLS">
+<!ENTITY connectionSecurityType-3.label "SSL/TLS">
+<!ENTITY smtpEditTitle.label "SMTP Server">
+<!ENTITY serverPortDefault.label "Default:">
+<!ENTITY authMethod.label "Authentication method:">
+<!ENTITY authMethod.accesskey "i">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/region.properties b/comm/suite/locales/en-US/chrome/mailnews/region.properties
new file mode 100644
index 0000000000..39987fdaa1
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/region.properties
@@ -0,0 +1,31 @@
+# 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/.
+
+#
+# messenger.properties
+# mailnews.js
+mailnews.start_page.url=chrome://messenger/content/start.xhtml
+# To make mapit buttons to disappear in the addressbook, specify empty string. For example:
+# mail.addr_book.mapit_url.format=
+# The mail.addr_book.mapit_url.format should start with the URL of the mapping
+# service and then the query part with placeholders to be subsituted from values
+# from the addressbook contact's address.
+# Available placeholders are:
+# @A1 == address, part 1
+# @A2 == address, part 2
+# @CI == city
+# @ST == state
+# @ZI == zip code
+# @CO == country
+# Default map service:
+mail.addr_book.mapit_url.format=https://maps.google.com/maps?q=@A1%20@A2%20@CI%20@ST%20@ZI%20@CO
+# List of available map services (up to 5 can be defined here):
+mail.addr_book.mapit_url.1.name=Google Maps
+mail.addr_book.mapit_url.1.format=https://maps.google.com/maps?q=@A1%20@A2%20@CI%20@ST%20@ZI%20@CO
+mail.addr_book.mapit_url.2.name=OpenStreetMap
+mail.addr_book.mapit_url.2.format=https://nominatim.openstreetmap.org/search.php?polygon=1&q=@A1%2C@A2%2C@CI%2C@ST%2C@ZI%2C@CO
+mailnews.messageid_browser.url=http://groups.google.com/search?as_umsgid=%mid
+# Recognize non-standard versions of "Re:" in subjects from localized versions of MS Outlook et al.
+# Specify a comma-separated list without spaces. For example: mailnews.localizedRe=AW,SV
+mailnews.localizedRe=
diff --git a/comm/suite/locales/en-US/chrome/mailnews/renameFolderDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/renameFolderDialog.dtd
new file mode 100644
index 0000000000..62dd784f45
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/renameFolderDialog.dtd
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY renameFolderDialog.title "Rename Folder">
+<!ENTITY rename.label "Enter the new name for your folder:">
+<!ENTITY rename.accesskey "E">
+<!ENTITY accept.label "Rename">
+<!ENTITY accept.accesskey "R">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/search-attributes.properties b/comm/suite/locales/en-US/chrome/mailnews/search-attributes.properties
new file mode 100644
index 0000000000..010b8cd1ef
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/search-attributes.properties
@@ -0,0 +1,45 @@
+# 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/.
+
+#these need to match nsMsgSearchAttrib interface in nsMsgSearchCore.idl
+#and nsMsgSearchAttribMap in nsMsgSearchAdapter.cpp
+Subject=Subject
+From=From
+Body=Body
+Date=Date
+Priority=Priority
+Status=Status
+To=To
+Cc=Cc
+ToOrCc=To or Cc
+AgeInDays=Age In Days
+SizeKB=Size (KB)
+Tags=Tags
+# for AB and LDAP
+AnyName=Any Name
+DisplayName=Display Name
+Nickname=Nickname
+ScreenName=Screen Name
+Email=Email
+AdditionalEmail=Additional Email
+AnyNumber=Any Number
+WorkPhone=Work Phone
+HomePhone=Home Phone
+Fax=Fax
+Pager=Pager
+Mobile=Mobile
+City=City
+Street=Street
+Title=Title
+Organization=Organization
+Department=Department
+# more mailnews
+FromToCcOrBcc=From, To, Cc or Bcc
+JunkScoreOrigin=Junk Score Origin
+JunkPercent=Junk Percent
+AttachmentStatus=Attachment Status
+JunkStatus=Junk Status
+Label=Label
+Customize=Customize…
+MissingCustomTerm=Missing Custom Term
diff --git a/comm/suite/locales/en-US/chrome/mailnews/search-operators.properties b/comm/suite/locales/en-US/chrome/mailnews/search-operators.properties
new file mode 100644
index 0000000000..e2f58e5b69
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/search-operators.properties
@@ -0,0 +1,31 @@
+# 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/.
+
+0=contains
+1=doesn't contain
+2=is
+3=isn't
+4=is empty
+
+5=is before
+6=is after
+
+7=is higher than
+8=is lower than
+
+9=begins with
+10=ends with
+
+11=sounds like
+12=LdapDwim
+
+13=is greater than
+14=is less than
+
+15=NameCompletion
+16=is in my address book
+17=isn't in my address book
+18=isn't empty
+19=matches
+20=doesn't match
diff --git a/comm/suite/locales/en-US/chrome/mailnews/search.properties b/comm/suite/locales/en-US/chrome/mailnews/search.properties
new file mode 100644
index 0000000000..43a8c35e97
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/search.properties
@@ -0,0 +1,28 @@
+# 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/.
+
+# search and filter strings
+#
+# these are the fields that get inserted in the search line
+# for "and" searches, this looks like:
+#
+# searchAnd0 <attribute> searchAnd1 <operator> searchAnd2 <value> searchAnd4
+#
+# for example, in english this looks like:
+# and the [Sender ] [doesn't contain] [John]
+#
+# TODO: need to special-case the first line (filterindex==0)
+
+# filter stuff
+searchingMessage=Searching…
+# LOCALIZATION NOTE (matchesFound): #1 number of matches found
+matchesFound=#1 match found;#1 matches found
+noMatchesFound=No matches found
+labelForStopButton=Stop
+labelForSearchButton=Search
+labelForStopButton.accesskey=S
+labelForSearchButton.accesskey=S
+
+moreButtonTooltipText=Add a new rule
+lessButtonTooltipText=Remove this rule
diff --git a/comm/suite/locales/en-US/chrome/mailnews/searchTermOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/searchTermOverlay.dtd
new file mode 100644
index 0000000000..0482afa59d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/searchTermOverlay.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!ENTITY matchAll.label "Match all of the following">
+<!ENTITY matchAll.accesskey "M">
+<!ENTITY matchAny.label "Match any of the following">
+<!ENTITY matchAny.accesskey "a">
+<!ENTITY matchAllMsgs.label "Match all messages">
+<!ENTITY matchAllMsgs.accesskey "t">
+<!-- LOCALIZATION NOTE
+ The values below are used to control the widths of the search widgets.
+ Change the values only when the localized strings in the popup menus
+ are truncated in the widgets.
+ -->
+<!ENTITY searchTermListAttributesFlexValue "1">
+<!ENTITY searchTermListOperatorsFlexValue "1">
+<!ENTITY searchTermListValueFlexValue "3">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/shutdownWindow.properties b/comm/suite/locales/en-US/chrome/mailnews/shutdownWindow.properties
new file mode 100644
index 0000000000..95cbad1768
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/shutdownWindow.properties
@@ -0,0 +1,10 @@
+# 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/.
+
+
+# These strings are loaded and represented by the XUL dialog.
+shutdownDialogTitle=Shutdown Progress Window
+taskProgress=Processing %1$S of %2$S Tasks
+
+# These strings are loaded by the individual shutdown tasks.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime.properties b/comm/suite/locales/en-US/chrome/mailnews/smime.properties
new file mode 100644
index 0000000000..7bfffdb56a
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime.properties
@@ -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/.
+
+#
+# The following are used by the smime content type handler
+#
+
+## @name NS_MSG_UNABLE_TO_OPEN_FILE
+## LOCALIZATION NOTE: the text can contain HTML tags.
+1000=This is an <B>ENCRYPTED</B> or <B>SIGNED</B> message.<br> This Mail application does not support encrypted or signed mail.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.dtd b/comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.dtd
new file mode 100644
index 0000000000..5c4e2063a3
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.dtd
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+
+<!ENTITY securityTitle.label "Security">
+<!ENTITY securityTab.label "Security">
+<!ENTITY securityHeading.label "To send and receive signed or encrypted messages, you should specify both a digital signing certificate and an encryption certificate.">
+<!ENTITY encryptionGroupTitle.label "Encryption">
+<!ENTITY encryptionChoiceLabel.label "Default encryption setting when sending messages:">
+<!ENTITY neverEncrypt.label "Never (do not use encryption)">
+<!ENTITY neverEncrypt.accesskey "N">
+<!ENTITY alwaysEncryptMessage.label "Required (can't send message unless all recipients have certificates)">
+<!ENTITY alwaysEncryptMessage.accesskey "u">
+<!ENTITY encryptionCert.message "Use this certificate to encrypt &amp; decrypt messages sent to you:">
+<!ENTITY digitalSign.certificate.button "Select…">
+<!ENTITY digitalSign.certificate.accesskey "S">
+<!ENTITY digitalSign.certificate_clear.button "Clear">
+<!ENTITY digitalSign.certificate_clear.accesskey "C">
+<!ENTITY encryption.certificate.button "Select…">
+<!ENTITY encryption.certificate.accesskey "t">
+<!ENTITY encryption.certificate_clear.button "Clear">
+<!ENTITY encryption.certificate_clear.accesskey "e">
+<!ENTITY signingGroupTitle.label "Digital Signing">
+<!ENTITY signMessage.label "Digitally sign messages (by default)">
+<!ENTITY signMessage.accesskey "D">
+<!ENTITY signingCert.message "Use this certificate to digitally sign messages you send:">
+
+<!ENTITY certificates.label "Certificates">
+<!ENTITY manageCerts2.label "Manage Certificates…">
+<!ENTITY manageCerts2.accesskey "M">
+<!ENTITY manageDevices.label "Manage Security Devices…">
+<!ENTITY manageDevices.accesskey "g">
+
+<!-- Strings for the cert picker dialog -->
+<!ENTITY certPicker.title "Select Certificate">
+<!ENTITY certPicker.info "Certificate:">
+<!ENTITY certPicker.detailsLabel "Details of selected certificate:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.properties b/comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.properties
new file mode 100644
index 0000000000..7e1742efbb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/am-smime.properties
@@ -0,0 +1,41 @@
+# 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/.
+
+## S/MIME error strings.
+## Note to localization: %S is a placeholder
+NoSenderSigningCert=You specified that this message should be digitally signed, but the application either failed to find the signing certificate specified in your Mail & Newsgroup Account Settings, or the certificate has expired.
+NoSenderEncryptionCert=You specified encryption for this message, but the application either failed to find the encryption certificate specified in your Mail & Newsgroup Account Settings, or the certificate has expired.
+MissingRecipientEncryptionCert=You specified encryption for this message, but the application failed to find an encryption certificate for %S.
+ErrorEncryptMail=Unable to encrypt message. Please check that you have a valid email certificate for each recipient. Please check that the certificates specified in Mail & Newsgroups Account Settings for this mail account are valid and trusted for mail.
+ErrorCanNotSignMail=Unable to sign message. Please check that the certificates specified in Mail & Newsgroups Account Settings for this mail account are valid and trusted for mail.
+
+## Strings used for in the prefs.
+prefPanel-smime=Security
+NoSigningCert=Certificate Manager can't locate a valid certificate that can be used to digitally sign your messages.
+NoSigningCertForThisAddress=Certificate Manager can't locate a valid certificate that can be used to digitally sign your messages with an address of <%S>.
+NoEncryptionCert=Certificate Manager can't locate a valid certificate that other people can use to send you encrypted email messages.
+NoEncryptionCertForThisAddress=Certificate Manager can't locate a valid certificate that other people can use to send you encrypted email messages to the address <%S>.
+
+encryption_needCertWantSame=You should also specify a certificate for other people to use when they send you encrypted messages. Do you want to use the same certificate to encrypt & decrypt messages sent to you?
+encryption_wantSame=Do you want to use the same certificate to encrypt & decrypt messages sent to you?
+encryption_needCertWantToSelect=You should also specify a certificate for other people to use when they send you encrypted messages. Do you want to configure an encryption certificate now?
+signing_needCertWantSame=You should also specify a certificate to use for digitally signing your messages. Do you want to use the same certificate to digitally sign your messages?
+signing_wantSame=Do you want to use the same certificate to digitally sign your messages?
+signing_needCertWantToSelect=You should also specify a certificate to use for digitally signing your messages. Do you want to configure a certificate for digitally signing messages now?
+
+## Strings used by nsMsgComposeSecure
+mime_smimeEncryptedContentDesc=S/MIME Encrypted Message
+mime_smimeSignatureContentDesc=S/MIME Cryptographic Signature
+
+## Strings used by the cert picker.
+CertInfoIssuedFor=Issued to:
+CertInfoIssuedBy=Issued by:
+CertInfoValid=Valid
+CertInfoFrom=from
+CertInfoTo=to
+CertInfoPurposes=Purposes
+CertInfoEmail=Email
+CertInfoStoredIn=Stored in:
+NicknameExpired=(expired)
+NicknameNotYetValid=(not yet valid)
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/certFetchingStatus.dtd b/comm/suite/locales/en-US/chrome/mailnews/smime/certFetchingStatus.dtd
new file mode 100644
index 0000000000..8673b9ed9d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/certFetchingStatus.dtd
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!--LOCALIZATION NOTE shown while obtaining certificates from a directory -->
+
+<!ENTITY title.label "Downloading Certificates">
+<!ENTITY info.message "Searching the directory for recipients' certificates. This may take a few minutes.">
+<!ENTITY stop.label "Stop Searching">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.dtd
new file mode 100644
index 0000000000..f9ae79ef72
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.dtd
@@ -0,0 +1,20 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE msgCompSMIMEOverlay.dtd UI for s/mime hooks in message composition -->
+
+<!ENTITY menu_securityEncryptRequire.label "Encrypt This Message">
+<!ENTITY menu_securityEncryptRequire.accesskey "E">
+
+<!ENTITY menu_securitySign.label "Digitally Sign This Message">
+<!ENTITY menu_securitySign.accesskey "M">
+
+<!ENTITY menu_securityStatus.label "View Security Info">
+<!ENTITY menu_securityStatus.accesskey "I">
+
+<!ENTITY securityButton.label "Security">
+<!ENTITY securityButton.tooltip "View or change security settings">
+
+<!ENTITY menu_viewSecurityStatus.label "Message Security Info">
+<!ENTITY menu_viewSecurityStatus.accesskey "I">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.properties b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.properties
new file mode 100644
index 0000000000..285d3cbea6
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSMIMEOverlay.properties
@@ -0,0 +1,6 @@
+# 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/.
+
+## S/MIME mail compose window error strings.
+NeedSetup=You need to set up one or more personal certificates before you can use this security feature. Would you like to learn how to do this now?
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.dtd b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.dtd
new file mode 100644
index 0000000000..d8dbe6df27
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.dtd
@@ -0,0 +1,18 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE msgCompSecurityInfo.dtd UI for viewing security status when composing a message -->
+
+<!ENTITY title.label "Message Security">
+<!ENTITY subject.plaintextWarning "Please note: Subject lines of email messages are never encrypted.">
+<!ENTITY status.heading "The contents of your message will be sent as follows:">
+<!ENTITY status.signed "Digitally signed:">
+<!ENTITY status.encrypted "Encrypted:">
+<!ENTITY status.certificates "Certificates:">
+<!ENTITY view.label "View">
+<!ENTITY view.accesskey "V">
+<!ENTITY tree.recipient "Recipient">
+<!ENTITY tree.status "Status">
+<!ENTITY tree.issuedDate "Issued">
+<!ENTITY tree.expiresDate "Expires">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.properties b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.properties
new file mode 100644
index 0000000000..bf8ca06592
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgCompSecurityInfo.properties
@@ -0,0 +1,13 @@
+# 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/.
+
+StatusNotFound=Not Found
+StatusValid=Valid
+StatusExpired=Expired
+StatusUntrusted=Not Trusted
+StatusRevoked=Revoked
+StatusInvalid=Invalid
+StatusYes=Yes
+StatusNo=No
+StatusNotPossible=Not possible
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.dtd b/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.dtd
new file mode 100644
index 0000000000..f8dffb7df8
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!--LOCALIZATION NOTE msgReadSMIMEOverlay.dtd UI for s/mime hooks in message reading -->
+
+<!ENTITY menu_securityStatus.label "Message Security Info">
+<!ENTITY menu_securityStatus.accesskey "I">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.properties b/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.properties
new file mode 100644
index 0000000000..c7a1862843
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSMIMEOverlay.properties
@@ -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/.
+
+ImapOnDemand=The displayed message has been digitally signed, but not all its attachments have been downloaded yet. Therefore, the signature cannot be validated. Click OK to download the complete message and validate the signature.
+#
+#NOTE To translater, anything between %..% and <..> should not be translated.
+# the former will be replaced by java script, and the latter is HTML formatting.
+#
+CantDecryptTitle=%brand% cannot decrypt this message
+CantDecryptBody=The sender encrypted this message to you using one of your digital certificates, however %brand% was not able to find this certificate and corresponding private key. <br> Possible solutions: <br><ul><li>If you have a smartcard, please insert it now. <li>If you are using a new machine, or if you are using a new %brand% profile, you will need to restore your certificate and private key from a backup. Certificate backups usually end in ".p12".</ul>
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSecurityInfo.dtd b/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSecurityInfo.dtd
new file mode 100644
index 0000000000..06f1788987
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgReadSecurityInfo.dtd
@@ -0,0 +1,14 @@
+<!-- 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/. -->
+
+<!--LOCALIZATION NOTE msgReadSecurityInfo.dtd UI for viewing security status when reading a received message -->
+
+<!ENTITY status.label "Message Security">
+<!ENTITY signatureCert.label "View Signature Certificate">
+<!ENTITY encryptionCert.label "View Encryption Certificate">
+
+<!ENTITY signer.name "Signed by:">
+<!ENTITY recipient.name "Encrypted for:">
+<!ENTITY email.address "Email address:">
+<!ENTITY issuer.name "Certificate issued by:">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/smime/msgSecurityInfo.properties b/comm/suite/locales/en-US/chrome/mailnews/smime/msgSecurityInfo.properties
new file mode 100644
index 0000000000..7453b1aa78
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/smime/msgSecurityInfo.properties
@@ -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/.
+
+## Signature Information strings
+SINoneLabel=Message Has No Digital Signature
+SINone=This message does not include the sender's digital signature. The absence of a digital signature means that the message could have been sent by someone pretending to have this email address. It is also possible that the message has been altered while in transit over the network. However, it is unlikely that either event has occurred.
+
+SIValidLabel=Message Is Signed
+SIValid=This message includes a valid digital signature. The message has not been altered since it was sent.
+
+SIInvalidLabel=Digital Signature Is Not Valid
+SIInvalidHeader=This message includes a digital signature, but the signature is invalid.
+
+SIContentAltered=The signature does not match the message content correctly. The message appears to have been altered after the sender signed it. You should not trust the validity of this message until you verify its contents with the sender.
+SIExpired=The certificate used to sign the message appears to have expired. Make sure your computer's clock is set correctly.
+SIRevoked=The certificate used to sign the message has been revoked. You should not trust the validity of this message until you verify its contents with the sender.
+SINotYetValid=The certificate used to sign the message appears not to be valid yet. Make sure your computer's clock is set correctly.
+SIUnknownCA=The certificate used to sign the message was issued by an unknown certificate authority.
+SIUntrustedCA=The certificate used to sign the message was issued by a certificate authority that you do not trust for issuing this kind of certificate.
+SIExpiredCA=The certificate used to sign the message was issued by a certificate authority whose own certificate has expired. Make sure your computer's clock is set correctly.
+SIRevokedCA=The certificate used to sign the message was issued by a certificate authority whose own certificate has been revoked. You should not trust the validity of this message until you verify its contents with the sender.
+SINotYetValidCA=The certificate used to sign the message was issued by a certificate authority whose own certificate is not yet valid. Make sure your computer's clock is set correctly.
+SIInvalidCipher=The message was signed using an encryption strength that this version of your software does not support.
+SIClueless=There are unknown problems with this digital signature. You should not trust the validity of this message until you verify its contents with the sender.
+
+SIPartiallyValidLabel=Message is signed
+SIPartiallyValidHeader=Although the digital signature is valid, it is unknown whether sender and signer are the same person.
+
+SIHeaderMismatch=The email address listed in the signer's certificate is different from the email address that was used to send this message. Please look at the details of the signature certificate to learn who signed the message.
+SICertWithoutAddress=The certificate used to sign the message does not contain an email address. Please look at the details of the signature certificate to learn who signed the message.
+
+## Encryption Information strings
+EINoneLabel2=Message Is Not Encrypted
+EINone=This message was not encrypted before it was sent. Information sent over the Internet without encryption can be seen by other people while in transit.
+
+EIValidLabel=Message Is Encrypted
+EIValid=This message was encrypted before it was sent to you. Encryption makes it very difficult for other people to view information while it is traveling over the network.
+
+EIInvalidLabel=Message Cannot Be Decrypted
+EIInvalidHeader=This message was encrypted before it was sent to you, but it cannot be decrypted.
+
+EIContentAltered=The message contents appear to have been altered during transmission.
+EIClueless=There are unknown problems with this encrypted message.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/start.dtd b/comm/suite/locales/en-US/chrome/mailnews/start.dtd
new file mode 100644
index 0000000000..49abae10ef
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/start.dtd
@@ -0,0 +1,34 @@
+<!-- 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/. -->
+
+<!ENTITY startpage.title "Welcome to &brandShortName; Mail &amp; Newsgroups">
+<!--# LOCALIZATION NOTE (headline.label): the URL for <a id="vendorURL"> is fetched from brand.properties -->
+<!ENTITY headline.label
+'Welcome to <a id="vendorURL" href="">&brandShortName;</a> Mail &amp; Newsgroups!'>
+<!ENTITY description.label
+"&brandShortName; Mail &amp; Newsgroups is a powerful open-source mail and news
+client, supporting advanced junk mail detection and other useful features.">
+<!ENTITY features.title "Features">
+<!ENTITY feat_multiacc.label "Support for multiple accounts">
+<!ENTITY feat_junk.label "Junk Mail detection">
+<!ENTITY feat_feeds.label "Feed Reader">
+<!ENTITY feat_filters.label "Message Filters">
+<!ENTITY feat_htmlmsg.label "HTML message support">
+<!ENTITY feat_abook.label "Address Book">
+<!ENTITY feat_tags.label "Customizable Tags and Mail Views">
+<!ENTITY feat_integration.label
+"Tight integration with the &brandShortName; application suite">
+<!ENTITY dict.title "Dictionaries">
+<!ENTITY dict_intro.label "Dictionaries are used to check the spelling of your messages.">
+<!ENTITY dict_info.label2
+'Many additional languages can be installed from the
+<a id="dictURL" href="">dictionaries</a> section on
+<a href="https://addons.thunderbird.net/seamonkey/">SeaMonkey and Thunderbird Add-ons</a>.'>
+<!ENTITY info.title "More Information">
+<!--# LOCALIZATION NOTE (info_bugs.label2): the URL for <a id="releaseNotesURL"> is fetched from brand.properties -->
+<!ENTITY info_bugs.label2
+'<a href="https://www.seamonkey-project.org/dev/get-involved">We</a> welcome
+bug reports and feature requests, but please read the
+<a id="releaseNotesURL" href="">release notes</a> and query
+<a href="https://bugzilla.mozilla.org/query.cgi">Bugzilla</a> first.'>
diff --git a/comm/suite/locales/en-US/chrome/mailnews/subscribe.dtd b/comm/suite/locales/en-US/chrome/mailnews/subscribe.dtd
new file mode 100644
index 0000000000..9ce66e0559
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/subscribe.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY subscribeDialog.title "Subscribe">
+<!ENTITY subscribeButton.label "Subscribe">
+<!ENTITY subscribeButton.accesskey "S">
+<!ENTITY unsubscribeButton.label "Unsubscribe">
+<!ENTITY unsubscribeButton.accesskey "U">
+<!ENTITY newGroupsTab.label "New Groups">
+<!ENTITY newGroupsTab.accesskey "N">
+<!ENTITY refreshButton.label "Refresh">
+<!ENTITY refreshButton.accesskey "R">
+<!ENTITY stopButton.label "Stop">
+<!ENTITY stopButton.accesskey "T">
+<!ENTITY server.label "Account:">
+<!ENTITY server.accesskey "A">
+<!ENTITY subscribedHeader.label "Subscribe">
+<!-- commenting out until bug 38906 is fixed
+<!ENTITY messagesHeader.label "Messages"> -->
+<!ENTITY namefield.label "Show items that contain:">
+<!ENTITY namefield.accesskey "O">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/subscribe.properties b/comm/suite/locales/en-US/chrome/mailnews/subscribe.properties
new file mode 100644
index 0000000000..9fc327f5e9
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/subscribe.properties
@@ -0,0 +1,14 @@
+# 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/.
+subscribeLabel-nntp=Select the newsgroups to subscribe to:
+subscribeLabel-imap=Select the folders to subscribe to:
+columnHeader-nntp=Newsgroup name
+columnHeader-imap=Folder name
+currentListTab-nntp.label=Current Group List
+currentListTab-nntp.accesskey=L
+currentListTab-imap.label=Folder List
+currentListTab-imap.accesskey=L
+pleaseWaitString=Please wait…
+offlineState=You are offline. Items could not be retrieved from the server.
+errorPopulating=Error retrieving items from the server.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/tabmail.properties b/comm/suite/locales/en-US/chrome/mailnews/tabmail.properties
new file mode 100644
index 0000000000..4d3788b4fc
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/tabmail.properties
@@ -0,0 +1,13 @@
+# 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/.
+
+tabs.closeWarningTitleAll=Confirm close
+# LOCALIZATION NOTE (tabs.closeWarningAll):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The singular form is not considered since this string is used only for
+# multiple tabs.
+tabs.closeWarningAll=;This messenger window has #1 tabs open. Do you want to close it and all its tabs?
+tabs.closeButtonAll=Close all tabs
+tabs.closeWarningPromptMeAll=Warn me when closing multiple tabs
diff --git a/comm/suite/locales/en-US/chrome/mailnews/textImportMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/textImportMsgs.properties
new file mode 100644
index 0000000000..37df51c03f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/textImportMsgs.properties
@@ -0,0 +1,43 @@
+# 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/.
+
+#
+# The following are used by the text import code to display status/error
+# and informational messages
+#
+
+# Short name of import module
+## @name TEXTIMPORT_NAME
+## @loc None
+2000=Text file (LDIF, .tab, .csv, .txt)
+
+# Description of import module
+## @name TEXTIMPORT_DESCRIPTION
+## @loc None
+2001=Import an address book from a text file, including: LDIF (.ldif, .ldi), tab-delimited (.tab, .txt) or comma-separated (.csv) formats.
+
+# Description of import module
+## @name TEXTIMPORT_ADDRESS_NAME
+## @loc None
+2002=Text Address Book
+
+# Description
+## @name TEXTIMPORT_ADDRESS_SUCCESS
+## @loc None
+2003=Imported address book %S
+
+# Error message
+## @name TEXTIMPORT_ADDRESS_BADPARAM
+## @loc None
+2004=Bad parameter passed to import address book.
+
+# Error message
+## @name TEXTIMPORT_ADDRESS_BADSOURCEFILE
+## @loc None
+2005=Error accessing file for address book %S.
+
+# Error message
+## @name TEXTIMPORT_ADDRESS_CONVERTERROR
+## @loc None
+2006=Error importing address book %S, all addresses may not have been imported.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/threadpane.dtd b/comm/suite/locales/en-US/chrome/mailnews/threadpane.dtd
new file mode 100644
index 0000000000..0b24aa1fbb
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/threadpane.dtd
@@ -0,0 +1,45 @@
+<!-- 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/. -->
+
+<!ENTITY threadColumn.label "Thread">
+<!ENTITY fromColumn.label "From">
+<!ENTITY recipientColumn.label "Recipient">
+<!ENTITY attachmentColumn.label "Attachments">
+<!ENTITY subjectColumn.label "Subject">
+<!ENTITY dateColumn.label "Date">
+<!ENTITY priorityColumn.label "Priority">
+<!ENTITY tagsColumn.label "Tags">
+<!ENTITY accountColumn.label "Account">
+<!ENTITY statusColumn.label "Status">
+<!ENTITY sizeColumn.label "Size">
+<!ENTITY junkStatusColumn.label "Junk Status">
+<!ENTITY unreadColumn.label "Unread in Thread">
+<!ENTITY totalColumn.label "Total in Thread">
+<!ENTITY readColumn.label "Read">
+<!ENTITY receivedColumn.label "Received">
+<!ENTITY flagColumn.label "Flag">
+<!ENTITY locationColumn.label "Location">
+<!ENTITY idColumn.label "Order Received">
+
+<!--Tooltips-->
+<!ENTITY columnChooser2.tooltip "Select columns to display">
+<!ENTITY threadColumn2.tooltip "Display message threads">
+<!ENTITY fromColumn2.tooltip "Sort by from">
+<!ENTITY recipientColumn2.tooltip "Sort by recipient">
+<!ENTITY attachmentColumn2.tooltip "Sort by attachments">
+<!ENTITY subjectColumn2.tooltip "Sort by subject">
+<!ENTITY dateColumn2.tooltip "Sort by date">
+<!ENTITY priorityColumn2.tooltip "Sort by priority">
+<!ENTITY tagsColumn2.tooltip "Sort by tags">
+<!ENTITY accountColumn2.tooltip "Sort by account">
+<!ENTITY statusColumn2.tooltip "Sort by status">
+<!ENTITY sizeColumn2.tooltip "Sort by size">
+<!ENTITY junkStatusColumn2.tooltip "Sort by junk status">
+<!ENTITY unreadColumn2.tooltip "Number of unread messages in thread">
+<!ENTITY totalColumn2.tooltip "Total number of messages in thread">
+<!ENTITY readColumn2.tooltip "Sort by read">
+<!ENTITY receivedColumn2.tooltip "Sort by date received">
+<!ENTITY flagColumn2.tooltip "Sort by flag">
+<!ENTITY locationColumn2.tooltip "Sort by location">
+<!ENTITY idColumn2.tooltip "Sort by order received">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/vCardImportMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/vCardImportMsgs.properties
new file mode 100644
index 0000000000..0a24e9cf2d
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/vCardImportMsgs.properties
@@ -0,0 +1,26 @@
+# 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/.
+
+#
+# The following are used by the vCard import code to display status, error, and
+# informational messages
+#
+
+vCardImportName=vCard file (.vcf)
+
+vCardImportDescription=Import an address book from vCard format
+
+vCardImportAddressName=vCard Address Book
+
+# LOCALIZATION NOTE (vCardImportAddressSuccess): %S is replaced by the
+# name of the address book being imported.
+vCardImportAddressSuccess=Imported address book %S
+
+# LOCALIZATION NOTE (vCardImportAddressSuccess): %S is replaced by the
+# name of the address book being imported.
+vCardImportAddressBadSourceFile=Error accessing file for address book %S.
+
+# LOCALIZATION NOTE (vCardImportAddressSuccess): %S is replaced by the
+# name of the address book being imported.
+vCardImportAddressConvertError=Error importing address book %S, all addresses may not have been imported.
diff --git a/comm/suite/locales/en-US/chrome/mailnews/viewLog.dtd b/comm/suite/locales/en-US/chrome/mailnews/viewLog.dtd
new file mode 100644
index 0000000000..5bca64539e
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/viewLog.dtd
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+
+<!ENTITY viewLog.title "Filter Log">
+<!ENTITY viewLogInfo.text "The Filter Log documents the filters that have been run for this account. Use the check box below to enable logging.">
+<!ENTITY clearLog.label "Clear Log">
+<!ENTITY clearLog.accesskey "C">
+<!ENTITY enableLog.label "Enable the Filter Log">
+<!ENTITY enableLog.accesskey "E">
+<!ENTITY closeLog.label "Close">
+<!ENTITY closeLog.accesskey "o">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/virtualFolderListDialog.dtd b/comm/suite/locales/en-US/chrome/mailnews/virtualFolderListDialog.dtd
new file mode 100644
index 0000000000..d3199adb48
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/virtualFolderListDialog.dtd
@@ -0,0 +1,8 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!ENTITY virtualFolderListTitle.title "Select Folder(s)">
+<!ENTITY virtualFolderDesc.label "Select the folders to search:">
+<!ENTITY folderName.label "Folder name">
+<!ENTITY folderSearch.label "Search">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/virtualFolderProperties.dtd b/comm/suite/locales/en-US/chrome/mailnews/virtualFolderProperties.dtd
new file mode 100644
index 0000000000..ef4fc4128f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/virtualFolderProperties.dtd
@@ -0,0 +1,22 @@
+<!-- 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/. -->
+
+<!ENTITY virtualFolderProperties.title "New Saved Search Folder">
+<!ENTITY name.label "Name:">
+<!ENTITY name.accesskey "N">
+<!ENTITY description.label "Create as a subfolder of:">
+<!ENTITY description.accesskey "C">
+
+<!ENTITY searchTermCaption.label "Configure the search criteria used for this saved search folder: ">
+
+<!ENTITY folderSelectionCaption.label "Select the folders to search: ">
+<!ENTITY chooseFoldersButton.label "Choose…">
+<!ENTITY chooseFoldersButton.accesskey "h">
+
+<!ENTITY searchOnline.label "Search Online (Gives up-to-date results for IMAP and News folders but increases time to open the folder)">
+<!ENTITY searchOnline.accesskey "S">
+<!ENTITY newFolderButton.label "Create">
+<!ENTITY newFolderButton.accesskey "r">
+<!ENTITY editFolderButton.label "Update">
+<!ENTITY editFolderButton.accesskey "U">
diff --git a/comm/suite/locales/en-US/chrome/mailnews/wmImportMsgs.properties b/comm/suite/locales/en-US/chrome/mailnews/wmImportMsgs.properties
new file mode 100644
index 0000000000..42786af7c1
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mailnews/wmImportMsgs.properties
@@ -0,0 +1,76 @@
+# 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/.
+
+#
+# The following are used by the windows live mail import code to display status/error
+# and informational messages
+#
+
+# Short name of import module
+## @name WMIMPORT_NAME
+## @loc None
+## LOCALIZATION NOTE (2000): DONT_TRANSLATE
+2000=Windows Live Mail
+
+# Description of import module
+## @name WMIMPORT_DESCRIPTION
+## @loc None
+## LOCALIZATION NOTE (2001): In this item, don't translate "Windows Live Mail"
+2001=Windows Live Mail settings
+
+# Success message
+## @name WMIMPORT_MAILBOX_SUCCESS
+## @loc None
+## LOCALIZATION NOTE (2002): In this item, don't translate "%1$S" or "%2$d"
+## The variable %1$S will contain the name of the Mailbox
+## The variable %2$d will contain the number of messages
+2002=Mailbox %1$S, imported %2$d messages
+
+# Error message
+## @name WMIMPORT_MAILBOX_BADPARAM
+## @loc None
+2003=Bad parameter passed to import mailbox.
+
+# Error message
+## @name WMIMPORT_MAILBOX_BADSOURCEFILE
+## @loc None
+## LOCALIZATION NOTE (2004): In this item, don't translate "%S"
+## The variable %S will contain the name of the Mailbox
+2004=Error accessing file for mailbox %S.
+
+# Error message
+## @name WMIMPORT_MAILBOX_CONVERTERROR
+## @loc None
+## LOCALIZATION NOTE (2005): In this item, don't translate "%S"
+## The variable %S will contain the name of the Mailbox
+2005=Error importing mailbox %S, all messages may not be imported from this mailbox.
+
+# Default name of imported addressbook
+## @name WMIMPORT_DEFAULT_NAME
+## @loc None
+2006=Windows Live Mail Address Book
+
+# Autofind description
+## @name WMIMPORT_AUTOFIND
+## @loc None
+2007=Windows Live Mail address book (windows address book)
+
+# Description
+## @name WMIMPORT_ADDRESS_SUCCESS
+## @loc None
+## LOCALIZATION NOTE (2006): In this item, don't translate "%S"
+## The variable %S will receive the name of the address book
+2008=Imported address book %S
+
+# Error message
+## @name WMIMPORT_ADDRESS_CONVERTERROR
+## @loc None
+## LOCALIZATION NOTE (2009): In this item, don't translate "%S"
+## The variable %S will receive the name of the address book
+2009=Error importing address book %S, all addresses may not have been imported.
+
+# Error message
+## @name WMIMPORT_ADDRESS_BADPARAM
+## @loc None
+2010=Bad parameter passed to import addressbook.
diff --git a/comm/suite/locales/en-US/chrome/mozldap/ldap.properties b/comm/suite/locales/en-US/chrome/mozldap/ldap.properties
new file mode 100644
index 0000000000..2d9dbd3e8f
--- /dev/null
+++ b/comm/suite/locales/en-US/chrome/mozldap/ldap.properties
@@ -0,0 +1,261 @@
+#
+# 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/.
+
+# The following two strings are used when prompting the user for authentication
+# information:
+
+## @name AUTH_PROMPT_TITLE
+## @loc none
+authPromptTitle=LDAP Server Password Required
+
+## @name AUTH_PROMPT_TEXT
+## @loc %1$S should not be localized. It is the hostname of the LDAP server.
+authPromptText=Please enter your password for %1$S.
+
+# These are string versions of all the errors defined in
+# nsILDAPErrors.idl, as well as the nsresult codes based on those
+# errors. See that file for the genesis of these codes, as well as
+# for info about how to get documentation about their precise
+# meanings.
+
+## @name OPERATIONS_ERROR
+## @loc none
+1=Operations error
+
+## @name PROTOCOL_ERROR
+## @loc none
+2=Protocol error
+
+## @name TIMELIMIT_EXCEEDED
+## @loc none
+3=Timelimit exceeded
+
+## @name SIZELIMIT_EXCEEDED
+## @loc none
+4=Sizelimit exceeded
+
+## @name COMPARE_FALSE
+## @loc none
+5=Compare false
+
+## @name COMPARE_TRUE
+## @loc none
+6=Compare true
+
+## @name STRONG_AUTH_NOT_SUPPORTED
+## @loc none
+7=Authentication method not supported
+
+## @name STRONG_AUTH_REQUIRED
+## @loc none
+8=Strong authentication required
+
+## @name PARTIAL_RESULTS
+## @loc none
+9=Partial results and referral received
+
+## @name REFERRAL
+## @loc none
+10=Referral received
+
+## @name ADMINLIMIT_EXCEEDED
+## @loc none
+11=Administrative limit exceeded
+
+## @name UNAVAILABLE_CRITICAL_EXTENSION
+## @loc none
+12=Unavailable critical extension
+
+## @name CONFIDENTIALITY_REQUIRED
+## @loc none
+13=Confidentiality required
+
+## @name SASL_BIND_IN_PROGRESS
+## @loc none
+14=SASL bind in progress
+
+## @name NO_SUCH_ATTRIBUTE
+## @loc none
+16=No such attribute
+
+## @name UNDEFINED_TYPE
+## @loc none
+17=Undefined attribute type
+
+## @name INAPPROPRIATE MATCHIN
+## @loc none
+18=Inappropriate matching
+
+## @name CONSTRAINT_VIOLATION
+## @loc none
+19=Constraint violation
+
+## @name TYPE_OR_VALUE_EXISTS
+## @loc none
+20=Type or value exists
+
+## @name INVALID_SYNTAX
+## @loc none
+21=Invalid syntax
+
+## @name NO_SUCH_OBJECT
+## @loc none
+32=No such object
+
+## @name ALIAS_PROBLEM
+## @loc none
+33=Alias problem
+
+## @name INVALID_DN_ SYNTAX
+## @loc none
+34=Invalid DN syntax
+
+## @name IS_LEAF
+## @loc none
+35=Object is a leaf
+
+## @name ALIAS_DEREF_PROBLEM
+## @loc none
+36=Alias dereferencing problem
+
+## @name INAPPROPRIATE_AUTH
+## @loc none
+48=Inappropriate authentication
+
+## @name INVALID_CREDENTIALS
+## @loc none
+49=Invalid credentials
+
+## @name INSUFFICIENT_ACCESS
+## @loc none
+50=Insufficient access
+
+## @name BUSY
+## @loc none
+51=The LDAP server is busy
+
+## @name UNAVAILABLE
+## @loc none
+52=LDAP server is unavailable
+
+## @name UNWILLING_TO_PERFORM
+## @loc none
+53=LDAP server is unwilling to perform
+
+## @name LOOP_DETECT
+## @loc none
+54=Loop detected
+
+## @name SORT_CONTROL_MISSING
+## @loc none
+60=Sort Control is missing
+
+## @name INDEX_RANGE_ERROR
+## @loc none
+61=Search results exceed the range specified by the offsets
+
+## @name NAMING_VIOLATION
+## @loc none
+64=Naming violation
+
+## @name OBJECT_CLASS_VIOLATION
+## @loc none
+65=Object class violation
+
+## @name NOT_ALLOWED_ON_NONLEAF
+## @loc none
+66=Operation not allowed on nonleaf
+
+## @name NOT_ALLOWED_ON_RDN
+## @loc none
+67=Operation not allowed on RDN
+
+## @name ALREADY_EXISTS
+## @loc none
+68=Already exists
+
+## @name NO_OBJECT_CLASS_MODS
+## @loc none
+69=Cannot modify object class
+
+## @name RESULTS_TOO_LARGE
+## @loc none
+70=Results too large
+
+## @name AFFECTS_MULTIPLE_DSAS
+## @loc none
+71=Affects multiple servers
+
+## @name OTHER
+## @loc none
+80=Unknown error
+
+## @name SERVER_DOWN
+## @loc none
+81=Can't contact the LDAP server
+
+## @name LOCAL_ERROR
+## @loc none
+82=Local error
+
+## @name ENCODING_ERROR
+## @loc none
+83=Encoding error
+
+## @name DECODING_ERROR
+## @loc none
+84=Decoding error
+
+## @name TIMEOUT
+## @loc none
+85=The LDAP server timed out
+
+## @name AUTH_UNKNOWN
+## @loc none
+86=Unknown authentication method
+
+## @name FILTER_ERROR
+## @loc none
+87=Invalid search filter
+
+## @name USER_CANCELLED
+## @loc none
+88=User cancelled operation
+
+## @name PARAM_ERROR
+## @loc none
+89=Bad parameter to an LDAP routine
+
+## @name NO_MEMORY
+## @loc none
+90=Out of memory
+
+## @name CONNECT_ERROR
+## @loc none
+91=Can't connect to the LDAP server
+
+## @name NOT_SUPPORTED
+## @loc none
+92=Not supported by this version of the LDAP protocol
+
+## @name CONTROL_NOT_FOUND
+## @loc none
+93=Requested LDAP control not found
+
+## @name NO_RESULTS_RETURNED
+## @loc none
+94=No results returned
+
+## @name MORE_RESULTS_TO_RETURN
+## @loc none
+95=More results to return
+
+## @name CLIENT_LOOP
+## @loc none
+96=Client detected loop
+
+## @name REFERRAL_LIMIT_EXCEEDED
+## @loc none
+97=Referral hop limit exceeded
diff --git a/comm/suite/locales/en-US/crashreporter/crashreporter-override.ini b/comm/suite/locales/en-US/crashreporter/crashreporter-override.ini
new file mode 100644
index 0000000000..78506ddac0
--- /dev/null
+++ b/comm/suite/locales/en-US/crashreporter/crashreporter-override.ini
@@ -0,0 +1,9 @@
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This file is in the UTF-8 encoding
+[Strings]
+# LOCALIZATION NOTE (CrashReporterProductErrorText2): The %s is replaced with a string containing detailed information.
+CrashReporterProductErrorText2=SeaMonkey had a problem and crashed. We'll try to restore your tabs and windows when it restarts.\n\nUnfortunately the crash reporter is unable to submit a crash report.\n\nDetails: %s
+CrashReporterDescriptionText2=SeaMonkey had a problem and crashed. We'll try to restore your tabs and windows when it restarts.\n\nTo help us diagnose and fix the problem, you can send us a crash report.
diff --git a/comm/suite/locales/en-US/defines.inc b/comm/suite/locales/en-US/defines.inc
new file mode 100644
index 0000000000..24f45813a2
--- /dev/null
+++ b/comm/suite/locales/en-US/defines.inc
@@ -0,0 +1,9 @@
+#filter emptyLines
+
+#define MOZ_LANGPACK_CREATOR mozilla.org
+
+# If non-English locales wish to credit multiple contributors, uncomment this
+# variable definition and use the format specified.
+# #define MOZ_LANGPACK_CONTRIBUTORS <em:contributor>Joe Solon</em:contributor> <em:contributor>Suzy Solon</em:contributor>
+
+#unfilter emptyLines
diff --git a/comm/suite/locales/en-US/installer/windows/custom.properties b/comm/suite/locales/en-US/installer/windows/custom.properties
new file mode 100644
index 0000000000..8cba6fd1c2
--- /dev/null
+++ b/comm/suite/locales/en-US/installer/windows/custom.properties
@@ -0,0 +1,86 @@
+# 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/.
+
+# LOCALIZATION NOTE:
+
+# This file must be saved as UTF8
+
+# Accesskeys are defined by prefixing the letter that is to be used for the
+# accesskey with an ampersand (e.g. &).
+
+# Do not replace $BrandShortName, $BrandFullName, or $BrandFullNameDA with a
+# custom string and always use the same one as used by the en-US files.
+# $BrandFullNameDA allows the string to contain an ampersand (e.g. DA stands
+# for double ampersand) and prevents the letter following the ampersand from
+# being used as an accesskey.
+
+# You can use \n to create a newline in the string but only when the string
+# from en-US contains a \n.
+
+REG_APP_DESC=$BrandShortName delivers safe, easy web browsing. A familiar user interface, enhanced security features including protection from online identity theft, and integrated search let you get the most out of the web.
+OPTIONAL_COMPONENTS_TITLE=Choose Optional Components
+OPTIONAL_COMPONENTS_SUBTITLE=Choose which features of $BrandFullNameDA you want to install.
+OPTIONAL_COMPONENTS_LABEL=Optional Components:
+DEBUGQA_TITLE=Debug and QA UI
+DEBUGQA_TEXT=Provides additional debug and QA UI for $BrandShortName development.
+CONTEXT_OPTIONS=$BrandShortName &Options
+CONTEXT_SAFE_MODE=$BrandShortName &Safe Mode
+SAFE_MODE=Safe Mode
+# MAILNEWS_TEXT appears in Windows (All) Programs menu as "SeaMonkey $(MAILNEWS_TEXT)"
+MAILNEWS_TEXT=Mail
+PROFILE_TEXT=Profile Manager
+OPTIONS_PAGE_TITLE=Setup Type
+OPTIONS_PAGE_SUBTITLE=Choose setup options
+SHORTCUTS_PAGE_TITLE=Set Up Shortcuts
+SHORTCUTS_PAGE_SUBTITLE=Create Program Icons
+SUMMARY_PAGE_TITLE=Summary
+SUMMARY_PAGE_SUBTITLE=Ready to start installing $BrandShortName
+SUMMARY_INSTALLED_TO=$BrandShortName will be installed to the following location:
+SUMMARY_CLICK=Click Install to continue.
+SUMMARY_REBOOT_REQUIRED_INSTALL=A restart of your computer may be required to complete the installation.
+SUMMARY_REBOOT_REQUIRED_UNINSTALL=A restart of your computer may be required to complete the uninstall.
+SURVEY_TEXT=&Tell us what you thought of $BrandShortName
+LAUNCH_TEXT=&Launch $BrandFullNameDA now
+CREATE_ICONS_DESC=Create icons for $BrandShortName:
+ICONS_DESKTOP=On my &Desktop
+ICONS_STARTMENU=In my &Start Menu Programs folder
+ICONS_QUICKLAUNCH=In my &Quick Launch bar
+WARN_WRITE_ACCESS=You don't have access to write to the installation directory.\n\nClick OK to select a different directory.
+WARN_DISK_SPACE=You don't have sufficient disk space to install to this location.\n\nClick OK to select a different location.
+WARN_MIN_SUPPORTED_CPU_MSG=Sorry, $BrandShortName can't be installed. This version of $BrandShortName requires a processor with ${MinSupportedCPU} support. Please click the OK button for additional information.
+WARN_MIN_SUPPORTED_OSVER_MSG=Sorry, $BrandShortName can't be installed. This version of $BrandShortName requires ${MinSupportedVer} or newer. Please click the OK button for additional information.
+WARN_MIN_SUPPORTED_OSVER_CPU_MSG=Sorry, $BrandShortName can't be installed. This version of $BrandShortName requires ${MinSupportedVer} or newer and a processor with ${MinSupportedCPU} support. Please click the OK button for additional information.
+WARN_MANUALLY_CLOSE_APP_INSTALL=$BrandShortName must be closed to proceed with the installation.\n\nPlease close $BrandShortName to continue.
+WARN_MANUALLY_CLOSE_APP_LAUNCH=$BrandShortName is already running.\n\nPlease close $BrandShortName prior to launching the version you have just installed.
+WARN_MANUALLY_CLOSE_APP_UNINSTALL=$BrandShortName must be closed to proceed with the uninstall.\n\nPlease close $BrandShortName to continue.
+WARN_RESTART_REQUIRED_UNINSTALL=Your computer must be restarted to complete a previous uninstall of $BrandShortName. Do you want to reboot now?
+WARN_RESTART_REQUIRED_UPGRADE=Your computer must be restarted to complete a previous upgrade of $BrandShortName. Do you want to reboot now?
+ERROR_CREATE_DIRECTORY_PREFIX=Error creating directory:
+ERROR_CREATE_DIRECTORY_SUFFIX=Click Cancel to stop the installation or\nRetry to try again.
+
+UN_CONFIRM_CLICK=Click Uninstall to continue.
+UN_CONFIRM_PAGE_TITLE=Uninstall $BrandFullName
+UN_CONFIRM_PAGE_SUBTITLE=Remove $BrandFullName from your computer.
+UN_CONFIRM_UNINSTALLED_FROM=$BrandShortName will be uninstalled from the following location:
+
+STATUS_INSTALL_APP=Installing $BrandShortName…
+STATUS_INSTALL_LANG=Installing Language Files (${AB_CD})…
+STATUS_INSTALL_OPTIONAL=Installing Optional Components…
+STATUS_UNINSTALL_MAIN=Uninstalling $BrandShortName…
+STATUS_CLEANUP=A Little Housekeeping…
+
+BANNER_CHECK_EXISTING=Checking existing installation…
+
+# _DESC strings support approximately 65 characters per line.
+# One line
+OPTIONS_SUMMARY=Choose the type of setup you prefer, then click Next.
+# One line
+OPTION_STANDARD_DESC=$BrandShortName will be installed with the most common options.
+OPTION_STANDARD_RADIO=&Standard
+# One line
+OPTION_COMPLETE_DESC=$BrandShortName will be installed with all available options.
+OPTION_COMPLETE_RADIO=C&omplete
+# Two lines
+OPTION_CUSTOM_DESC=You may choose individual options to be installed. Recommended for experienced users.
+OPTION_CUSTOM_RADIO=&Custom
diff --git a/comm/suite/locales/en-US/installer/windows/mui.properties b/comm/suite/locales/en-US/installer/windows/mui.properties
new file mode 100644
index 0000000000..68841d5e39
--- /dev/null
+++ b/comm/suite/locales/en-US/installer/windows/mui.properties
@@ -0,0 +1,64 @@
+# 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/.
+
+# To make the l10n tinderboxen see changes to this file you can change a value
+# name by adding - to the end of the name followed by chars (e.g. Branding-2).
+
+# LOCALIZATION NOTE:
+
+# This file must be saved as UTF8
+
+# Accesskeys are defined by prefixing the letter that is to be used for the
+# accesskey with an ampersand (e.g. &).
+
+# Do not replace $BrandShortName, $BrandFullName, or $BrandFullNameDA with a
+# custom string and always use the same one as used by the en-US files.
+# $BrandFullNameDA allows the string to contain an ampersand (e.g. DA stands
+# for double ampersand) and prevents the letter following the ampersand from
+# being used as an accesskey.
+
+# You can use \n to create a newline in the string but only when the string
+# from en-US contains a \n.
+MUI_TEXT_WELCOME_INFO_TITLE=Welcome to the $BrandFullNameDA Setup Wizard
+MUI_TEXT_WELCOME_INFO_TEXT=This wizard will guide you through the installation of $BrandFullNameDA.\n\nIt is recommended that you close all other applications before starting Setup. This will make it possible to update relevant system files without having to reboot your computer.\n\n$_CLICK
+MUI_TEXT_LICENSE_TITLE=License Agreement
+MUI_TEXT_LICENSE_SUBTITLE=Please review the license terms before installing $BrandFullNameDA.
+MUI_INNERTEXT_LICENSE_TOP=Press Page Down to see the rest of the agreement.
+MUI_INNERTEXT_LICENSE_BOTTOM_CHECKBOX=If you accept the terms of the agreement, click the check box below. You must accept the agreement to install $BrandFullNameDA. $_CLICK
+MUI_TEXT_COMPONENTS_TITLE=Choose Components
+MUI_TEXT_COMPONENTS_SUBTITLE=Choose which features of $BrandFullNameDA you want to install.
+MUI_INNERTEXT_COMPONENTS_DESCRIPTION_TITLE=Description
+MUI_INNERTEXT_COMPONENTS_DESCRIPTION_INFO=Position your mouse over a component to see its description.
+MUI_TEXT_DIRECTORY_TITLE=Choose Install Location
+MUI_TEXT_DIRECTORY_SUBTITLE=Choose the folder in which to install $BrandFullNameDA.
+MUI_TEXT_INSTALLING_TITLE=Installing
+MUI_TEXT_INSTALLING_SUBTITLE=Please wait while $BrandFullNameDA is being installed.
+MUI_TEXT_FINISH_TITLE=Installation Complete
+MUI_TEXT_FINISH_SUBTITLE=Setup was completed successfully.
+MUI_TEXT_ABORT_TITLE=Installation Aborted
+MUI_TEXT_ABORT_SUBTITLE=Setup was not completed successfully.
+MUI_BUTTONTEXT_FINISH=&Finish
+MUI_TEXT_FINISH_INFO_TITLE=Completing the $BrandFullNameDA Setup Wizard
+MUI_TEXT_FINISH_INFO_TEXT=$BrandFullNameDA has been installed on your computer.\n\nClick Finish to close this wizard.
+MUI_TEXT_FINISH_INFO_REBOOT=Your computer must be restarted in order to complete the installation of $BrandFullNameDA. Do you want to reboot now?
+MUI_TEXT_FINISH_REBOOTNOW=Reboot now
+MUI_TEXT_FINISH_REBOOTLATER=I want to manually reboot later
+MUI_TEXT_STARTMENU_TITLE=Choose Start Menu Folder
+MUI_TEXT_STARTMENU_SUBTITLE=Choose a Start Menu folder for the $BrandFullNameDA shortcuts.
+MUI_INNERTEXT_STARTMENU_TOP=Select the Start Menu folder in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.
+MUI_TEXT_ABORTWARNING=Are you sure you want to quit $BrandFullName Setup?
+MUI_UNTEXT_WELCOME_INFO_TITLE=Welcome to the $BrandFullNameDA Uninstall Wizard
+MUI_UNTEXT_WELCOME_INFO_TEXT=This wizard will guide you through the uninstallation of $BrandFullNameDA.\n\nBefore starting the uninstallation, make sure $BrandFullNameDA is not running.\n\n$_CLICK
+MUI_UNTEXT_CONFIRM_TITLE=Uninstall $BrandFullNameDA
+MUI_UNTEXT_CONFIRM_SUBTITLE=Remove $BrandFullNameDA from your computer.
+MUI_UNTEXT_UNINSTALLING_TITLE=Uninstalling
+MUI_UNTEXT_UNINSTALLING_SUBTITLE=Please wait while $BrandFullNameDA is being uninstalled.
+MUI_UNTEXT_FINISH_TITLE=Uninstallation Complete
+MUI_UNTEXT_FINISH_SUBTITLE=Uninstall was completed successfully.
+MUI_UNTEXT_ABORT_TITLE=Uninstallation Aborted
+MUI_UNTEXT_ABORT_SUBTITLE=Uninstall was not completed successfully.
+MUI_UNTEXT_FINISH_INFO_TITLE=Completing the $BrandFullNameDA Uninstall Wizard
+MUI_UNTEXT_FINISH_INFO_TEXT=$BrandFullNameDA has been uninstalled from your computer.\n\nClick Finish to close this wizard.
+MUI_UNTEXT_FINISH_INFO_REBOOT=Your computer must be restarted in order to complete the uninstallation of $BrandFullNameDA. Do you want to reboot now?
+MUI_UNTEXT_ABORTWARNING=Are you sure you want to quit $BrandFullName Uninstall?
diff --git a/comm/suite/locales/en-US/installer/windows/override.properties b/comm/suite/locales/en-US/installer/windows/override.properties
new file mode 100644
index 0000000000..288f674805
--- /dev/null
+++ b/comm/suite/locales/en-US/installer/windows/override.properties
@@ -0,0 +1,86 @@
+# 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/.
+
+# LOCALIZATION NOTE:
+
+# This file must be saved as UTF8
+
+# Accesskeys are defined by prefixing the letter that is to be used for the
+# accesskey with an ampersand (e.g. &).
+
+# Do not replace $BrandShortName, $BrandFullName, or $BrandFullNameDA with a
+# custom string and always use the same one as used by the en-US files.
+# $BrandFullNameDA allows the string to contain an ampersand (e.g. DA stands
+# for double ampersand) and prevents the letter following the ampersand from
+# being used as an accesskey.
+
+# You can use \n to create a newline in the string but only when the string
+# from en-US contains a \n.
+
+# Strings that require a space at the end should be enclosed with double
+# quotes and the double quotes will be removed. To add quotes to the beginning
+# and end of a strong enclose the add and additional double quote to the
+# beginning and end of the string (e.g. ""This will include quotes"").
+
+SetupCaption=$BrandFullName Setup
+UninstallCaption=$BrandFullName Uninstall
+BackBtn=< &Back
+NextBtn=&Next >
+AcceptBtn=I &accept the terms in the License Agreement
+DontAcceptBtn=I &do not accept the terms in the License Agreement
+InstallBtn=&Install
+UninstallBtn=&Uninstall
+CancelBtn=Cancel
+CloseBtn=&Close
+BrowseBtn=B&rowse…
+ShowDetailsBtn=Show &details
+ClickNext=Click Next to continue.
+ClickInstall=Click Install to start the installation.
+ClickUninstall=Click Uninstall to start the uninstallation.
+Completed=Completed
+LicenseTextRB=Please review the license agreement before installing $BrandFullNameDA. If you accept all terms of the agreement, select the first option below. $_CLICK
+ComponentsText=Check the components you want to install and uncheck the components you don't want to install. $_CLICK
+ComponentsSubText2_NoInstTypes=Select components to install:
+DirText=Setup will install $BrandFullNameDA in the following folder. To install in a different folder, click Browse and select another folder. $_CLICK
+DirSubText=Destination Folder
+DirBrowseText=Select the folder to install $BrandFullNameDA in:
+SpaceAvailable="Space available: "
+SpaceRequired="Space required: "
+UninstallingText=$BrandFullNameDA will be uninstalled from the following folder. $_CLICK
+UninstallingSubText=Uninstalling from:
+FileError=Error opening file for writing: \r\n\r\n$0\r\n\r\nClick Abort to stop the installation,\r\nRetry to try again, or\r\nIgnore to skip this file.
+FileError_NoIgnore=Error opening file for writing: \r\n\r\n$0\r\n\r\nClick Retry to try again, or\r\nCancel to stop the installation.
+CantWrite="Can't write: "
+CopyFailed=Copy failed
+CopyTo="Copy to "
+Registering="Registering: "
+Unregistering="Unregistering: "
+SymbolNotFound="Could not find symbol: "
+CouldNotLoad="Could not load: "
+CreateFolder="Create folder: "
+CreateShortcut="Create shortcut: "
+CreatedUninstaller="Created uninstaller: "
+Delete="Delete file: "
+DeleteOnReboot="Delete on reboot: "
+ErrorCreatingShortcut="Error creating shortcut: "
+ErrorCreating="Error creating: "
+ErrorDecompressing=Error decompressing data! Corrupted installer?
+ErrorRegistering=Error registering DLL
+ExecShell="ExecShell: "
+Exec="Execute: "
+Extract="Extract: "
+ErrorWriting="Extract: error writing to file "
+InvalidOpcode=Installer corrupted: invalid opcode
+NoOLE="No OLE for: "
+OutputFolder="Output folder: "
+RemoveFolder="Remove folder: "
+RenameOnReboot="Rename on reboot: "
+Rename="Rename: "
+Skipped="Skipped: "
+CopyDetails=Copy Details To Clipboard
+LogInstall=Log install process
+Byte=B
+Kilo=K
+Mega=M
+Giga=G
diff --git a/comm/suite/locales/en-US/profile/bookmarks.extra b/comm/suite/locales/en-US/profile/bookmarks.extra
new file mode 100644
index 0000000000..54e8abc3c1
--- /dev/null
+++ b/comm/suite/locales/en-US/profile/bookmarks.extra
@@ -0,0 +1,18 @@
+#filter emptyLines
+
+# LOCALIZATION NOTE: You can add additional bookmark entries here for inclusion
+# in your language's default profiles.
+# Please do not add many entries here, only things that many users in your
+# country will actually need.
+# For most localizations, it's enough to localize the descriptions and domain
+# names of the Google bookmarks below.
+
+ <DT><H3>Search the Web</H3>
+ <DL><p>
+ <DT><A HREF="https://duckduckgo.com/">DuckDuckGo</A>
+ <DT><A HREF="https://www.google.com/">Google</A>
+ <DT><A HREF="https://groups.google.com/">Google Groups</A>
+ <DT><A HREF="https://news.google.com/">Google News</A>
+ </DL><p>
+
+#unfilter emptyLines
diff --git a/comm/suite/locales/en-US/profile/bookmarks.inc b/comm/suite/locales/en-US/profile/bookmarks.inc
new file mode 100644
index 0000000000..f8807c426e
--- /dev/null
+++ b/comm/suite/locales/en-US/profile/bookmarks.inc
@@ -0,0 +1,69 @@
+#filter emptyLines
+
+# LOCALIZATION NOTE: The 'en-US' strings in some URLs will be replaced with
+# your locale code, and link to your translated pages as soon as they're live.
+
+#define bookmarks_title Bookmarks
+#define bookmarks_heading Bookmarks
+
+#define personal_toolbarfolder Personal Toolbar Folder
+
+#define seamonkey_and_mozilla SeaMonkey and Mozilla
+
+# LOCALIZATION NOTE (seamonkey):
+# link title for https://www.seamonkey-project.org/ (in the personal toolbar)
+#define seamonkey SeaMonkey
+
+# LOCALIZATION NOTE (seamonkey_long):
+# link title for https://www.seamonkey-project.org/ (in normal bookmarks)
+#define seamonkey_long The SeaMonkey Project
+
+# LOCALIZATION NOTE (mozilla_org_long):
+# link title for https://www.mozilla.org/ (in normal bookmarks)
+#define mozilla_org_long The Mozilla Organization
+
+#define extend_seamonkey Extending SeaMonkey
+
+# LOCALIZATION NOTE (seamonkey_addons):
+# link title for https://addons.thunderbird.net/en-US/seamonkey/
+#define seamonkey_addons SeaMonkey Add-ons
+
+# LOCALIZATION NOTE (seamonkey_themes):
+# link title for https://addons.thunderbird.net/en-US/seamonkey/themes
+#define seamonkey_themes SeaMonkey Themes
+
+# LOCALIZATION NOTE (seamonkey_dictionaries):
+# link title for https://addons.thunderbird.net/en-US/seamonkey/dictionaries
+#define seamonkey_dictionaries Spell Checking Dictionaries
+
+# LOCALIZATION NOTE (seamonkey_plugins):
+# link title for https://addons.thunderbird.net/en-US/seamonkey/plugins
+#define seamonkey_plugins Plugins for SeaMonkey
+
+#define community_support Community &amp; Support
+
+# LOCALIZATION NOTE (seamonkey_community):
+# link title for https://www.seamonkey-project.org/community
+#define seamonkey_community SeaMonkey Community
+
+# LOCALIZATION NOTE (mozillazine):
+# link title for http://www.mozillazine.org/
+#define mozillazine mozillaZine
+
+# LOCALIZATION NOTE (seamonkey_support):
+# link title for the mozillaZine SeaMonkey Support forum
+#define seamonkey_support SeaMonkey Support Forum (mozillaZine)
+
+# LOCALIZATION NOTE (seamonkey_l10n):
+# insert full bookmark line for localized SeaMonkey page (personal toolbar)
+# e.g. #define seamonkey_l10n <DT><A HREF="https://www.seamonkey.tlh/">SeaMonkey tlhIngan</a>
+# Do not remove the trailing spaces here or MERGE_FILE in l0n builds will fail!
+#define seamonkey_l10n
+
+# LOCALIZATION NOTE (seamonkey_l10n_long):
+# insert full bookmark line for localized SeaMonkey page (normal bookmark)
+# e.g. #define seamonkey_l10n_long <DT><A HREF="https://www.seamonkey.tld/">tlhIngan Hol SeaMonkey</a>
+# Do not remove the trailing spaces here or or MERGE_FILE in l0n builds will fail!
+#define seamonkey_l10n_long
+
+#unfilter emptyLines
diff --git a/comm/suite/locales/en-US/profile/chrome/userChrome-example.css b/comm/suite/locales/en-US/profile/chrome/userChrome-example.css
new file mode 100644
index 0000000000..420ec3fd6d
--- /dev/null
+++ b/comm/suite/locales/en-US/profile/chrome/userChrome-example.css
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Edit this file and copy it as userChrome.css into your
+ * profile-directory/chrome/
+ */
+
+/*
+ * This file can be used to customize the look of Mozilla's user interface
+ * You should consider using !important on rules which you want to
+ * override default settings.
+ */
+
+/*
+ * Do not remove the @namespace line -- it's required for correct functioning
+ */
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
+
+
+/*
+ * Some possible accessibility enhancements:
+ */
+/*
+ * Make all the default font sizes 20 pt:
+ *
+ * * {
+ * font-size: 20pt !important
+ * }
+ */
+/*
+ * Make menu items in particular 15 pt instead of the default size:
+ *
+ * menupopup > * {
+ * font-size: 15pt !important
+ * }
+ */
+/*
+ * Give the Location (URL) Bar a fixed-width font
+ *
+ * #urlbar {
+ * font-family: monospace !important;
+ * }
+ */
+
+/*
+ * Eliminate the throbber and its annoying movement:
+ *
+ * #throbber-box {
+ * display: none !important;
+ * }
+ */
+
+/*
+ * For more examples see http://www-archive.mozilla.org/unix/customizing.html
+ */
+
diff --git a/comm/suite/locales/en-US/profile/chrome/userContent-example.css b/comm/suite/locales/en-US/profile/chrome/userContent-example.css
new file mode 100644
index 0000000000..4528c98713
--- /dev/null
+++ b/comm/suite/locales/en-US/profile/chrome/userContent-example.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+/*
+ * Edit this file and copy it as userContent.css into your
+ * profile-directory/chrome/
+ */
+
+/*
+ * This file can be used to apply a style to all web pages you view
+ * Rules without !important are overruled by author rules if the
+ * author sets any. Rules with !important overrule author rules.
+ */
+
+/*
+ * example: turn off "blink" element blinking
+ *
+ * blink { text-decoration: none ! important; }
+ *
+ */
+
+/*
+ * example: give all tables a 2px border
+ *
+ * table { border: 2px solid; }
+ */
+
+/*
+ * example: turn off "marquee" element
+ *
+ * marquee { -moz-binding: none; }
+ *
+ */
+
+/*
+ * example: make search fields on www.mozilla.org black-on-white
+ *
+ * @-moz-document url-prefix(http://www.mozilla.org/) {
+ * #q { background: white ! important; color: black ! important; }
+ * }
+ */
+
+/*
+ * For more examples see http://www-archive.mozilla.org/unix/customizing.html
+ */
+
diff --git a/comm/suite/locales/en-US/profile/panels.extra b/comm/suite/locales/en-US/profile/panels.extra
new file mode 100644
index 0000000000..f28b96e455
--- /dev/null
+++ b/comm/suite/locales/en-US/profile/panels.extra
@@ -0,0 +1,7 @@
+#filter emptyLines
+
+# LOCALIZATION NOTE: You can place sidebar panel entries here that will be
+# in default profiles. Only do this if you know what you're doing!
+# For normal localizations, it's best to leave this file unchanged.
+
+#unfilter emptyLines
diff --git a/comm/suite/locales/en-US/suite-l10n.js b/comm/suite/locales/en-US/suite-l10n.js
new file mode 100644
index 0000000000..afb322fa68
--- /dev/null
+++ b/comm/suite/locales/en-US/suite-l10n.js
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#filter substitution
+
+pref("intl.locale.requested", "@AB_CD@");
+pref("spellchecker.dictionary", "@AB_CD@");
diff --git a/comm/suite/locales/en-US/updater/updater.ini b/comm/suite/locales/en-US/updater/updater.ini
new file mode 100644
index 0000000000..40cec45ef6
--- /dev/null
+++ b/comm/suite/locales/en-US/updater/updater.ini
@@ -0,0 +1,8 @@
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+; This file is in the UTF-8 encoding
+[Strings]
+TitleText=%MOZ_APP_DISPLAYNAME% Update
+InfoText=%MOZ_APP_DISPLAYNAME% is installing your updates and will start in a few moments…
diff --git a/comm/suite/locales/filter.py b/comm/suite/locales/filter.py
new file mode 100644
index 0000000000..32db06e7da
--- /dev/null
+++ b/comm/suite/locales/filter.py
@@ -0,0 +1,72 @@
+# 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/.
+
+
+def test(mod, path, entity=None):
+ import re
+
+ # ignore anything but SeaMonkey
+ if mod not in (
+ "netwerk",
+ "dom",
+ "toolkit",
+ "security/manager",
+ "devtools/client",
+ "devtools/shared",
+ "devtools/startup",
+ "suite",
+ "extensions/spellcheck",
+ "suite/branding/seamonkey",
+ "services/sync",
+ ):
+ return "ignore"
+ # ignore temporary files, hiden files and files from rejects
+ if (
+ re.match(r".*?\/[.#].+", path)
+ or re.match(r".*~$", path)
+ or re.match(r".+\.(orig|rej)", path)
+ ):
+ return "ignore"
+ if mod not in ("suite"):
+ # we only have exceptions for suite
+ return "error"
+ if entity is None:
+ # missing and obsolete files
+ return (
+ "ignore"
+ if (
+ re.match(r"searchplugins\/.+\.xml", path)
+ or path == "profile/bookmarks.extra"
+ or path == "profile/panels.extra"
+ or path == "defines.inc"
+ or re.match(r"chrome\/common\/help\/images\/[A-Za-z-_]+\.[a-z]+", path)
+ )
+ else "error"
+ )
+ if path == "defines.inc":
+ return "ignore" if (entity == "MOZ_LANGPACK_CONTRIBUTORS") else "error"
+
+ if path == "chrome/common/region.properties":
+ return (
+ "ignore"
+ if (re.match(r"browser\.search\.order\.[1-9]", entity))
+ else "error"
+ )
+
+ if path == "chrome/mailnews/region.properties":
+ return (
+ "ignore"
+ if (re.match(r"mail\.addr_book\.mapit_url\.[1-5]", entity))
+ else "error"
+ )
+
+ if path != "chrome/browser/region.properties":
+ # only region.properties exceptions remain, compare all others
+ return "error"
+
+ return (
+ "ignore"
+ if (re.match(r"browser\.contentHandlers\.types\.[0-5]", entity))
+ else "error"
+ )
diff --git a/comm/suite/locales/generic/install.rdf b/comm/suite/locales/generic/install.rdf
new file mode 100644
index 0000000000..78f2f9c55b
--- /dev/null
+++ b/comm/suite/locales/generic/install.rdf
@@ -0,0 +1,30 @@
+<?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/.
+
+#filter substitution
+-->
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest"
+ em:id="@MOZ_LANGPACK_EID@"
+ em:name="@MOZ_LANG_TITLE@ Language Pack"
+ em:version="@MOZ_APP_VERSION@"
+ em:type="8"
+ em:creator="@MOZ_LANGPACK_CREATOR@">
+#ifdef MOZ_LANGPACK_CONTRIBUTORS
+ @MOZ_LANGPACK_CONTRIBUTORS@
+#endif
+
+ <em:targetApplication>
+ <Description>
+ <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id>
+ <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
+ <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
diff --git a/comm/suite/locales/generic/profile/bookmarks.html.in b/comm/suite/locales/generic/profile/bookmarks.html.in
new file mode 100644
index 0000000000..a322151a7d
--- /dev/null
+++ b/comm/suite/locales/generic/profile/bookmarks.html.in
@@ -0,0 +1,43 @@
+#filter substitution
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+#include bookmarks.inc
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+It will be read and overwritten.
+Do Not Edit! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>@bookmarks_title@</TITLE>
+<H1>@bookmarks_heading@</H1>
+
+<DL><p>
+ <DT><H3 PERSONAL_TOOLBAR_FOLDER="true" ID="NC:PersonalToolbarFolder">@personal_toolbarfolder@</H3>
+ <DL><p>
+ <DT><A HREF="https://www.seamonkey-project.org/">@seamonkey@</a>
+ @seamonkey_l10n@
+ <DT><A HREF="http://www.mozillazine.org/">@mozillazine@</A>
+ </DL><p>
+ <DT><H3>@seamonkey_and_mozilla@</H3>
+ <DL><p>
+ <DT><A HREF="https://www.seamonkey-project.org/">@seamonkey_long@</a>
+ @seamonkey_l10n_long@
+ <DT><A HREF="https://www.mozilla.org/">@mozilla_org_long@</A>
+ <DT><H3>@extend_seamonkey@</H3>
+ <DL><p>
+ <DT><A HREF="https://addons.thunderbird.net/@AB_CD@/seamonkey/">@seamonkey_addons@</a>
+ <DT><A HREF="https://addons.thunderbird.net/@AB_CD@/seamonkey/themes">@seamonkey_themes@</a>
+ <DT><A HREF="https://addons.thunderbird.net/@AB_CD@/seamonkey/dictionaries">@seamonkey_dictionaries@</a>
+ <DT><A HREF="https://addons.thunderbird.net/@AB_CD@/seamonkey/plugins">@seamonkey_plugins@</a>
+ </DL><p>
+ <DT><H3>@community_support@</H3>
+ <DL><p>
+ <DT><A HREF="https://www.seamonkey-project.org/community">@seamonkey_community@</a>
+ <DT><A HREF="http://www.mozillazine.org/">@mozillazine@</A>
+ <DT><A HREF="http://forums.mozillazine.org/viewforum.php?f=40">@seamonkey_support@</A>
+ </DL><p>
+ </DL><p>
+#include bookmarks.extra
+ <HR>
+</DL><p>
diff --git a/comm/suite/locales/generic/profile/mimeTypes.rdf b/comm/suite/locales/generic/profile/mimeTypes.rdf
new file mode 100644
index 0000000000..443dffcf2c
--- /dev/null
+++ b/comm/suite/locales/generic/profile/mimeTypes.rdf
@@ -0,0 +1,86 @@
+<?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/. -->
+
+
+<!--
+ This file is used as a persistent data store for helper application
+ information about both MIME type and protocol scheme helpers.
+
+ The root of the data are the two containers
+ <RDF:Seq about="urn:mimetypes:root"/> and <RDF:Seq about="urn:schemes:root"/>.
+
+ These contain one <RDF:li/> entry per MIME type/protocol. Each <RDF:li/> entry
+ corresponds to a "urn:<class>:<type>" resource, where <class> is either
+ "mimetype" or "scheme" and <type> is either a MIME type in "major/minor" format
+ or a scheme. For example, for HTML we would have "urn:mimetype:text/html",
+ while for mailto: we would have "urn:scheme:mailto".
+
+ Typically, this resource will be in the <RDF:Description/> node which has the
+ corresponding "about" attribute.
+
+ Each "urn:<class>:<type>" resource can have the following properties:
+
+ NC:Value - the MIME type or scheme string
+ NC:editable - a "true" or "false" depending on whether this entry is
+ editable
+ NC:description - a description of the type ("HTML Document" for text/html)
+ NC:fileExtensions - for MIME types, there will be one of these properties
+ per extension that corresponds to this MIME type,
+ each one having a single extension as its value.
+ NC:handlerProp - the way the type should be handled. This corresponds to a
+ "urn:<class>:handler:<type>" resource. Eg, the way HTML is
+ handled would be stored in the
+ "urn:mimetype:handler:text/html" resource.
+
+ Each "urn:<class>:handler:<type>" resource can have the following properties:
+
+ NC:useSystemDefault - "true" if we should handle per default OS setting,
+ "false" or not set otherwise
+ NC:saveToDisk - "true" if the data should be saved to disk, "false" or not
+ set otherwise.
+ (Note - if both of these are false, that means "open in helper app")
+ NC:alwaysAsk - "true" if the user should always be prompted before handling
+ data of this type, false otherwise.
+ NC:externalApplication - the preferred helper application to use for this
+ type. This corresponds to a
+ "urn:<class>:externalApplication:<type>" resource.
+ NC:possibleApplication - a helper application that can be used for this type.
+ Since there can be multiple possible applications,
+ there can be multiple assertions in the graph with
+ this property for a given handler resource.
+
+ Each "urn:<class>:externalApplication:<type>" resource, and each resource
+ that represents a possible application, can have the following property:
+
+ NC:prettyName - the "pretty name" of the application ("Acrobat Reader" for
+ /usr/bin/acroread, eg).
+
+ If the resource represents a local application, then it can have the following
+ property:
+
+ NC:path - the path to the application on the local filesystem, for example
+ /usr/bin/test or C:\windows\system32\cmd.exe.
+
+ If the resource represents a web application, then it can have the following
+ property:
+
+ NC:uriTemplate - a URI pointing to the web application to which the type
+ should be handed off, with %s in the template representing
+ the place where the content should be inserted. For example,
+ here is a URI template for a service that lets you email
+ an address in a mailto: link:
+ http://www.example.com/sendmail?link=%s
+-->
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:NC="http://home.netscape.com/NC-rdf#">
+
+ <RDF:Description about="urn:mimetypes">
+ <NC:MIME-types>
+ <RDF:Seq about="urn:mimetypes:root">
+ </RDF:Seq>
+ </NC:MIME-types>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/comm/suite/locales/generic/profile/panels.rdf.in b/comm/suite/locales/generic/profile/panels.rdf.in
new file mode 100644
index 0000000000..5254688d42
--- /dev/null
+++ b/comm/suite/locales/generic/profile/panels.rdf.in
@@ -0,0 +1,19 @@
+<?xml version="1.0"?> <!-- -*- Mode: SGML -*- -->
+<!-- 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/. -->
+
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:NC="http://home.netscape.com/NC-rdf#">
+
+ <RDF:Description about="urn:sidebar:current-panel-list">
+ <NC:panel-list>
+ <RDF:Seq>
+#include panels-urn.inc
+ </RDF:Seq>
+ </NC:panel-list>
+ <NC:version>0.1</NC:version>
+ </RDF:Description>
+#include panels.extra
+</RDF:RDF>
diff --git a/comm/suite/locales/jar.mn b/comm/suite/locales/jar.mn
new file mode 100644
index 0000000000..6bcc340582
--- /dev/null
+++ b/comm/suite/locales/jar.mn
@@ -0,0 +1,396 @@
+#filter substitution
+# 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/.
+
+
+@AB_CD@.jar:
+% locale branding @AB_CD@ %locale/@AB_CD@/branding/
+% locale communicator @AB_CD@ %locale/@AB_CD@/communicator/
+% locale editor @AB_CD@ %locale/@AB_CD@/editor/
+% locale help @AB_CD@ %locale/@AB_CD@/helpviewer/
+% locale messenger @AB_CD@ %locale/@AB_CD@/messenger/
+% locale messenger-mapi @AB_CD@ %locale/@AB_CD@/messenger-mapi/
+% locale messenger-region @AB_CD@ %locale/@AB_CD@/messenger-region/
+% locale messenger-smime @AB_CD@ %locale/@AB_CD@/messenger-smime/
+% locale messenger-newsblog @AB_CD@ %locale/@AB_CD@/messenger-newsblog/
+% locale mozldap @AB_CD@ %locale/@AB_CD@/mozldap/
+% locale navigator @AB_CD@ %locale/@AB_CD@/navigator/
+% locale navigator-region @AB_CD@ %locale/@AB_CD@/navigator-region/
+ locale/@AB_CD@/branding/aboutRights.dtd (%chrome/branding/aboutRights.dtd)
+ locale/@AB_CD@/branding/aboutRights.properties (%chrome/branding/aboutRights.properties)
+ locale/@AB_CD@/communicator/askViewZoom.dtd (%chrome/common/askViewZoom.dtd)
+ locale/@AB_CD@/communicator/certError.dtd (%chrome/common/certError.dtd)
+ locale/@AB_CD@/communicator/contentAreaCommands.dtd (%chrome/common/contentAreaCommands.dtd)
+ locale/@AB_CD@/communicator/contentAreaCommands.properties (%chrome/common/contentAreaCommands.properties)
+ locale/@AB_CD@/communicator/customizeToolbar.dtd (%chrome/common/customizeToolbar.dtd)
+ locale/@AB_CD@/communicator/customizeToolbar.properties (%chrome/common/customizeToolbar.properties)
+ locale/@AB_CD@/communicator/datetimepicker.dtd (%chrome/common/datetimepicker.dtd)
+ locale/@AB_CD@/communicator/defaultClientDialog.dtd (%chrome/common/defaultClientDialog.dtd)
+ locale/@AB_CD@/communicator/feeds/subscribe.dtd (%chrome/common/feeds/subscribe.dtd)
+ locale/@AB_CD@/communicator/feeds/subscribe.properties (%chrome/common/feeds/subscribe.properties)
+ locale/@AB_CD@/communicator/gopherAddon.dtd (%chrome/common/gopherAddon.dtd)
+ locale/@AB_CD@/communicator/notification.dtd (%chrome/common/notification.dtd)
+ locale/@AB_CD@/communicator/notification.properties (%chrome/common/notification.properties)
+ locale/@AB_CD@/communicator/openLocation.dtd (%chrome/common/openLocation.dtd)
+ locale/@AB_CD@/communicator/openLocation.properties (%chrome/common/openLocation.properties)
+ locale/@AB_CD@/communicator/printPreview.dtd (%chrome/common/printPreview.dtd)
+ locale/@AB_CD@/communicator/safeBrowsing.dtd (%chrome/common/safeBrowsing.dtd)
+ locale/@AB_CD@/communicator/quitDialog.properties (%chrome/common/quitDialog.properties)
+ locale/@AB_CD@/communicator/shellservice.properties (%chrome/common/shellservice.properties)
+ locale/@AB_CD@/communicator/sanitize.dtd (%chrome/common/sanitize.dtd)
+ locale/@AB_CD@/communicator/sanitize.properties (%chrome/common/sanitize.properties)
+ locale/@AB_CD@/communicator/setDesktopBackground.dtd (%chrome/common/setDesktopBackground.dtd)
+ locale/@AB_CD@/communicator/sitePermissions.properties (%chrome/common/sitePermissions.properties)
+ locale/@AB_CD@/communicator/tasksOverlay.dtd (%chrome/common/tasksOverlay.dtd)
+ locale/@AB_CD@/communicator/typeaheadfind.properties (%chrome/common/typeaheadfind.properties)
+ locale/@AB_CD@/communicator/utilityOverlay.dtd (%chrome/common/utilityOverlay.dtd)
+ locale/@AB_CD@/communicator/utilityOverlay.properties (%chrome/common/utilityOverlay.properties)
+ locale/@AB_CD@/communicator/viewApplyThemeOverlay.dtd (%chrome/common/viewApplyThemeOverlay.dtd)
+ locale/@AB_CD@/communicator/viewApplyThemeOverlay.properties (%chrome/common/viewApplyThemeOverlay.properties)
+ locale/@AB_CD@/communicator/viewZoomOverlay.dtd (%chrome/common/viewZoomOverlay.dtd)
+ locale/@AB_CD@/communicator/viewZoomOverlay.properties (%chrome/common/viewZoomOverlay.properties)
+ locale/@AB_CD@/communicator/about.dtd (%chrome/common/about.dtd)
+ locale/@AB_CD@/communicator/aboutPrivateBrowsing.dtd (%chrome/common/aboutPrivateBrowsing.dtd)
+ locale/@AB_CD@/communicator/aboutSessionRestore.dtd (%chrome/common/aboutSessionRestore.dtd)
+ locale/@AB_CD@/communicator/console/console.dtd (%chrome/common/console/console.dtd)
+ locale/@AB_CD@/communicator/console/console.properties (%chrome/common/console/console.properties)
+ locale/@AB_CD@/communicator/dataman/dataman.dtd (%chrome/common/dataman/dataman.dtd)
+ locale/@AB_CD@/communicator/dataman/dataman.properties (%chrome/common/dataman/dataman.properties)
+ locale/@AB_CD@/communicator/downloads/downloadmanager.dtd (%chrome/common/downloads/downloadmanager.dtd)
+ locale/@AB_CD@/communicator/downloads/downloadmanager.properties (%chrome/common/downloads/downloadmanager.properties)
+ locale/@AB_CD@/communicator/downloads/progressDialog.dtd (%chrome/common/downloads/progressDialog.dtd)
+ locale/@AB_CD@/communicator/help/cert_dialog_help.xhtml (%chrome/common/help/cert_dialog_help.xhtml)
+ locale/@AB_CD@/communicator/help/certs_help.xhtml (%chrome/common/help/certs_help.xhtml)
+ locale/@AB_CD@/communicator/help/certs_prefs_help.xhtml (%chrome/common/help/certs_prefs_help.xhtml)
+ locale/@AB_CD@/communicator/help/composer_help.xhtml (%chrome/common/help/composer_help.xhtml)
+ locale/@AB_CD@/communicator/help/cs_nav_prefs_advanced.xhtml (%chrome/common/help/cs_nav_prefs_advanced.xhtml)
+ locale/@AB_CD@/communicator/help/cs_nav_prefs_appearance.xhtml (%chrome/common/help/cs_nav_prefs_appearance.xhtml)
+ locale/@AB_CD@/communicator/help/cs_nav_prefs_navigator.xhtml (%chrome/common/help/cs_nav_prefs_navigator.xhtml)
+ locale/@AB_CD@/communicator/help/cs_priv_prefs_popup.xhtml (%chrome/common/help/cs_priv_prefs_popup.xhtml)
+ locale/@AB_CD@/communicator/help/customize_help.xhtml (%chrome/common/help/customize_help.xhtml)
+ locale/@AB_CD@/communicator/help/developer_tools.xhtml (%chrome/common/help/developer_tools.xhtml)
+ locale/@AB_CD@/communicator/help/glossary.xhtml (%chrome/common/help/glossary.xhtml)
+ locale/@AB_CD@/communicator/help/suitehelp.rdf (%chrome/common/help/suitehelp.rdf)
+ locale/@AB_CD@/communicator/help/helpFileLayout.css (%chrome/common/help/helpFileLayout.css)
+ locale/@AB_CD@/communicator/help/help-glossary.rdf (%chrome/common/help/help-glossary.rdf)
+ locale/@AB_CD@/communicator/help/help_help.xhtml (%chrome/common/help/help_help.xhtml)
+ locale/@AB_CD@/communicator/help/help-index1.rdf (%chrome/common/help/help-index1.rdf)
+ locale/@AB_CD@/communicator/help/help-indexAZ.rdf (%chrome/common/help/help-indexAZ.rdf)
+ locale/@AB_CD@/communicator/help/help-win.rdf (%chrome/common/help/help-win.rdf)
+ locale/@AB_CD@/communicator/help/mailnews_account_settings.xhtml (%chrome/common/help/mailnews_account_settings.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_addressbooks.xhtml (%chrome/common/help/mailnews_addressbooks.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_blogs_and_feeds.xhtml (%chrome/common/help/mailnews_blogs_and_feeds.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_getting_started.xhtml (%chrome/common/help/mailnews_getting_started.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_newsgroups.xhtml (%chrome/common/help/mailnews_newsgroups.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_offline.xhtml (%chrome/common/help/mailnews_offline.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_organizing.xhtml (%chrome/common/help/mailnews_organizing.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_preferences.xhtml (%chrome/common/help/mailnews_preferences.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_security.xhtml (%chrome/common/help/mailnews_security.xhtml)
+ locale/@AB_CD@/communicator/help/mailnews_using_mail.xhtml (%chrome/common/help/mailnews_using_mail.xhtml)
+ locale/@AB_CD@/communicator/help/nav_help.xhtml (%chrome/common/help/nav_help.xhtml)
+ locale/@AB_CD@/communicator/help/page_info_help.xhtml (%chrome/common/help/page_info_help.xhtml)
+ locale/@AB_CD@/communicator/help/passwords_help.xhtml (%chrome/common/help/passwords_help.xhtml)
+ locale/@AB_CD@/communicator/help/privacy_help.xhtml (%chrome/common/help/privacy_help.xhtml)
+ locale/@AB_CD@/communicator/help/privsec_help.xhtml (%chrome/common/help/privsec_help.xhtml)
+ locale/@AB_CD@/communicator/help/profiles_help.xhtml (%chrome/common/help/profiles_help.xhtml)
+ locale/@AB_CD@/communicator/help/shortcuts.xhtml (%chrome/common/help/shortcuts.xhtml)
+ locale/@AB_CD@/communicator/help/shortcuts_composer.xhtml (%chrome/common/help/shortcuts_composer.xhtml)
+ locale/@AB_CD@/communicator/help/shortcuts_mailnews.xhtml (%chrome/common/help/shortcuts_mailnews.xhtml)
+ locale/@AB_CD@/communicator/help/shortcuts_navigator.xhtml (%chrome/common/help/shortcuts_navigator.xhtml)
+ locale/@AB_CD@/communicator/help/ssl_help.xhtml (%chrome/common/help/ssl_help.xhtml)
+ locale/@AB_CD@/communicator/help/suite-toc.rdf (%chrome/common/help/suite-toc.rdf)
+ locale/@AB_CD@/communicator/help/using_certs_help.xhtml (%chrome/common/help/using_certs_help.xhtml)
+ locale/@AB_CD@/communicator/help/using_priv_help.xhtml (%chrome/common/help/using_priv_help.xhtml)
+ locale/@AB_CD@/communicator/help/welcome_help.xhtml (%chrome/common/help/welcome_help.xhtml)
+ locale/@AB_CD@/communicator/help/images/anchor-in-doc.gif (%chrome/common/help/images/anchor-in-doc.gif)
+ locale/@AB_CD@/communicator/help/images/broken.gif (%chrome/common/help/images/broken.gif)
+ locale/@AB_CD@/communicator/help/images/bullets.gif (%chrome/common/help/images/bullets.gif)
+ locale/@AB_CD@/communicator/help/images/columns.png (%chrome/common/help/images/columns.png)
+ locale/@AB_CD@/communicator/help/images/composer_icon.png (%chrome/common/help/images/composer_icon.png)
+ locale/@AB_CD@/communicator/help/images/help_nav.png (%chrome/common/help/images/help_nav.png)
+ locale/@AB_CD@/communicator/help/images/help_print.gif (%chrome/common/help/images/help_print.gif)
+ locale/@AB_CD@/communicator/help/images/image.gif (%chrome/common/help/images/image.gif)
+ locale/@AB_CD@/communicator/help/images/link.gif (%chrome/common/help/images/link.gif)
+ locale/@AB_CD@/communicator/help/images/locationbar.png (%chrome/common/help/images/locationbar.png)
+ locale/@AB_CD@/communicator/help/images/locationbar_search.png (%chrome/common/help/images/locationbar_search.png)
+ locale/@AB_CD@/communicator/help/images/mail_flag.png (%chrome/common/help/images/mail_flag.png)
+ locale/@AB_CD@/communicator/help/images/mail_flag_column.png (%chrome/common/help/images/mail_flag_column.png)
+ locale/@AB_CD@/communicator/help/images/mail_junk_column.png (%chrome/common/help/images/mail_junk_column.png)
+ locale/@AB_CD@/communicator/help/images/mail_newmail_alert.png (%chrome/common/help/images/mail_newmail_alert.png)
+ locale/@AB_CD@/communicator/help/images/mail_newmail_balloon.png (%chrome/common/help/images/mail_newmail_balloon.png)
+ locale/@AB_CD@/communicator/help/images/mail_newmail_trayicon.png (%chrome/common/help/images/mail_newmail_trayicon.png)
+ locale/@AB_CD@/communicator/help/images/mail_quicksearch.png (%chrome/common/help/images/mail_quicksearch.png)
+ locale/@AB_CD@/communicator/help/images/mail_read.png (%chrome/common/help/images/mail_read.png)
+ locale/@AB_CD@/communicator/help/images/mail_read_column.png (%chrome/common/help/images/mail_read_column.png)
+ locale/@AB_CD@/communicator/help/images/mail_unread.png (%chrome/common/help/images/mail_unread.png)
+ locale/@AB_CD@/communicator/help/images/menubar.png (%chrome/common/help/images/menubar.png)
+ locale/@AB_CD@/communicator/help/images/numbers.gif (%chrome/common/help/images/numbers.gif)
+ locale/@AB_CD@/communicator/help/images/offline.png (%chrome/common/help/images/offline.png)
+ locale/@AB_CD@/communicator/help/images/online.png (%chrome/common/help/images/online.png)
+ locale/@AB_CD@/communicator/help/images/personalbar.png (%chrome/common/help/images/personalbar.png)
+ locale/@AB_CD@/communicator/help/images/reload.gif (%chrome/common/help/images/reload.gif)
+ locale/@AB_CD@/communicator/help/images/search_navigation_toolbar.png (%chrome/common/help/images/search_navigation_toolbar.png)
+ locale/@AB_CD@/communicator/help/images/sidebar.png (%chrome/common/help/images/sidebar.png)
+ locale/@AB_CD@/communicator/help/images/tabbed_browsing_bar.png (%chrome/common/help/images/tabbed_browsing_bar.png)
+ locale/@AB_CD@/communicator/help/images/table.gif (%chrome/common/help/images/table.gif)
+ locale/@AB_CD@/communicator/help/images/taskbar.png (%chrome/common/help/images/taskbar.png)
+ locale/@AB_CD@/communicator/help/images/taskbar-ab.png (%chrome/common/help/images/taskbar-ab.png)
+ locale/@AB_CD@/communicator/help/images/task_mail.png (%chrome/common/help/images/task_mail.png)
+ locale/@AB_CD@/communicator/help/images/task_newmail.png (%chrome/common/help/images/task_newmail.png)
+ locale/@AB_CD@/communicator/help/images/threadbutton.png (%chrome/common/help/images/threadbutton.png)
+ locale/@AB_CD@/communicator/help/images/web-links.png (%chrome/common/help/images/web-links.png)
+ locale/@AB_CD@/helpviewer/help.properties (%chrome/common/helpviewer/help.properties)
+ locale/@AB_CD@/helpviewer/help.dtd (%chrome/common/helpviewer/help.dtd)
+ locale/@AB_CD@/communicator/migration/migration.dtd (%chrome/common/migration/migration.dtd)
+ locale/@AB_CD@/communicator/migration/migration.properties (%chrome/common/migration/migration.properties)
+ locale/@AB_CD@/communicator/permissions/cookieViewer.properties (%chrome/common/permissions/cookieViewer.properties)
+ locale/@AB_CD@/communicator/permissions/cookieViewer.dtd (%chrome/common/permissions/cookieViewer.dtd)
+ locale/@AB_CD@/communicator/permissions/permissionsManager.dtd (%chrome/common/permissions/permissionsManager.dtd)
+ locale/@AB_CD@/communicator/permissions/permissionsManager.properties (%chrome/common/permissions/permissionsManager.properties)
+ locale/@AB_CD@/communicator/places/bookmarkProperties.properties (%chrome/common/places/bookmarkProperties.properties)
+ locale/@AB_CD@/communicator/places/editBookmarkOverlay.dtd (%chrome/common/places/editBookmarkOverlay.dtd)
+ locale/@AB_CD@/communicator/places/places.dtd (%chrome/common/places/places.dtd)
+ locale/@AB_CD@/communicator/places/places.properties (%chrome/common/places/places.properties)
+ locale/@AB_CD@/communicator/pref/prefutilities.dtd (%chrome/common/pref/prefutilities.dtd)
+ locale/@AB_CD@/communicator/pref/prefutilities.properties (%chrome/common/pref/prefutilities.properties)
+ locale/@AB_CD@/communicator/pref/preferences.dtd (%chrome/common/pref/preferences.dtd)
+ locale/@AB_CD@/communicator/pref/pref-advanced.dtd (%chrome/common/pref/pref-advanced.dtd)
+ locale/@AB_CD@/communicator/pref/pref-appearance.dtd (%chrome/common/pref/pref-appearance.dtd)
+ locale/@AB_CD@/communicator/pref/pref-applications.dtd (%chrome/common/pref/pref-applications.dtd)
+ locale/@AB_CD@/communicator/pref/pref-applications.properties (%chrome/common/pref/pref-applications.properties)
+ locale/@AB_CD@/communicator/pref/pref-applicationManager.dtd (%chrome/common/pref/pref-applicationManager.dtd)
+ locale/@AB_CD@/communicator/pref/pref-applicationManager.properties (%chrome/common/pref/pref-applicationManager.properties)
+ locale/@AB_CD@/communicator/pref/pref-cache.dtd (%chrome/common/pref/pref-cache.dtd)
+ locale/@AB_CD@/communicator/pref/pref-colors.dtd (%chrome/common/pref/pref-colors.dtd)
+ locale/@AB_CD@/communicator/pref/pref-content.dtd (%chrome/common/pref/pref-content.dtd)
+ locale/@AB_CD@/communicator/pref/pref-cookies.dtd (%chrome/common/pref/pref-cookies.dtd)
+ locale/@AB_CD@/communicator/pref/pref-debugging.dtd (%chrome/common/pref/pref-debugging.dtd)
+ locale/@AB_CD@/communicator/pref/pref-download.dtd (%chrome/common/pref/pref-download.dtd)
+ locale/@AB_CD@/communicator/pref/pref-findasyoutype.dtd (%chrome/common/pref/pref-findasyoutype.dtd)
+ locale/@AB_CD@/communicator/pref/pref-fonts.dtd (%chrome/common/pref/pref-fonts.dtd)
+ locale/@AB_CD@/communicator/pref/pref-history.dtd (%chrome/common/pref/pref-history.dtd)
+ locale/@AB_CD@/communicator/pref/pref-http.dtd (%chrome/common/pref/pref-http.dtd)
+ locale/@AB_CD@/communicator/pref/pref-images.dtd (%chrome/common/pref/pref-images.dtd)
+ locale/@AB_CD@/communicator/pref/pref-keynav.dtd (%chrome/common/pref/pref-keynav.dtd)
+ locale/@AB_CD@/communicator/pref/pref-languages.dtd (%chrome/common/pref/pref-languages.dtd)
+ locale/@AB_CD@/communicator/pref/pref-languages.properties (%chrome/common/pref/pref-languages.properties)
+ locale/@AB_CD@/communicator/pref/pref-links.dtd (%chrome/common/pref/pref-links.dtd)
+ locale/@AB_CD@/communicator/pref/pref-locationbar.dtd (%chrome/common/pref/pref-locationbar.dtd)
+ locale/@AB_CD@/communicator/pref/pref-media.dtd (%chrome/common/pref/pref-media.dtd)
+ locale/@AB_CD@/communicator/pref/pref-mousewheel.dtd (%chrome/common/pref/pref-mousewheel.dtd)
+ locale/@AB_CD@/communicator/pref/pref-navigator.dtd (%chrome/common/pref/pref-navigator.dtd)
+ locale/@AB_CD@/communicator/pref/pref-offlineapps.dtd (%chrome/common/pref/pref-offlineapps.dtd)
+ locale/@AB_CD@/communicator/pref/pref-popups.dtd (%chrome/common/pref/pref-popups.dtd)
+ locale/@AB_CD@/communicator/pref/pref-privatedata.dtd (%chrome/common/pref/pref-privatedata.dtd)
+ locale/@AB_CD@/communicator/pref/pref-proxies.dtd (%chrome/common/pref/pref-proxies.dtd)
+ locale/@AB_CD@/communicator/pref/pref-proxies-advanced.dtd (%chrome/common/pref/pref-proxies-advanced.dtd)
+ locale/@AB_CD@/communicator/pref/pref-scripts.dtd (%chrome/common/pref/pref-scripts.dtd)
+ locale/@AB_CD@/communicator/pref/pref-search.dtd (%chrome/common/pref/pref-search.dtd)
+ locale/@AB_CD@/communicator/pref/pref-security.dtd (%chrome/common/pref/pref-security.dtd)
+ locale/@AB_CD@/communicator/pref/pref-smartupdate.dtd (%chrome/common/pref/pref-smartupdate.dtd)
+ locale/@AB_CD@/communicator/pref/pref-spelling.dtd (%chrome/common/pref/pref-spelling.dtd)
+ locale/@AB_CD@/communicator/pref/pref-tabs.dtd (%chrome/common/pref/pref-tabs.dtd)
+ locale/@AB_CD@/communicator/profile/profileSelection.dtd (%chrome/common/profile/profileSelection.dtd)
+ locale/@AB_CD@/communicator/profile/profileSelection.properties (%chrome/common/profile/profileSelection.properties)
+ locale/@AB_CD@/communicator/safeMode.dtd (%chrome/common/safeMode.dtd)
+ locale/@AB_CD@/communicator/search/engineManager.dtd (%chrome/common/search/engineManager.dtd)
+ locale/@AB_CD@/communicator/search/engineManager.properties (%chrome/common/search/engineManager.properties)
+ locale/@AB_CD@/communicator/search/search.properties (%chrome/common/search/search.properties)
+ locale/@AB_CD@/communicator/search/searchbar.dtd (%chrome/common/search/searchbar.dtd)
+ locale/@AB_CD@/communicator/search/search-panel.dtd (%chrome/common/search/search-panel.dtd)
+ locale/@AB_CD@/communicator/sidebar/customize.dtd (%chrome/common/sidebar/customize.dtd)
+ locale/@AB_CD@/communicator/sidebar/preview.dtd (%chrome/common/sidebar/preview.dtd)
+ locale/@AB_CD@/communicator/sidebar/sidebar.properties (%chrome/common/sidebar/sidebar.properties)
+ locale/@AB_CD@/communicator/sidebar/sidebarOverlay.dtd (%chrome/common/sidebar/sidebarOverlay.dtd)
+ locale/@AB_CD@/editor/editingOverlay.dtd (%chrome/editor/editingOverlay.dtd)
+ locale/@AB_CD@/editor/editor.dtd (%chrome/editor/editor.dtd)
+ locale/@AB_CD@/editor/editor.properties (%chrome/editor/editor.properties)
+ locale/@AB_CD@/editor/editorOverlay.dtd (%chrome/editor/editorOverlay.dtd)
+ locale/@AB_CD@/editor/editorSmileyOverlay.dtd (%chrome/editor/editorSmileyOverlay.dtd)
+ locale/@AB_CD@/editor/EdAdvancedEdit.dtd (%chrome/editor/dialogs/EdAdvancedEdit.dtd)
+ locale/@AB_CD@/editor/EdColorPicker.dtd (%chrome/editor/dialogs/EdColorPicker.dtd)
+ locale/@AB_CD@/editor/EdConvertToTable.dtd (%chrome/editor/dialogs/EdConvertToTable.dtd)
+ locale/@AB_CD@/editor/EdDialogOverlay.dtd (%chrome/editor/dialogs/EdDialogOverlay.dtd)
+ locale/@AB_CD@/editor/EditorButtonProperties.dtd (%chrome/editor/dialogs/EditorButtonProperties.dtd)
+ locale/@AB_CD@/editor/EditorColorProperties.dtd (%chrome/editor/dialogs/EditorColorProperties.dtd)
+ locale/@AB_CD@/editor/EditorFieldSetProperties.dtd (%chrome/editor/dialogs/EditorFieldSetProperties.dtd)
+ locale/@AB_CD@/editor/EditorFormProperties.dtd (%chrome/editor/dialogs/EditorFormProperties.dtd)
+ locale/@AB_CD@/editor/EditorHLineProperties.dtd (%chrome/editor/dialogs/EditorHLineProperties.dtd)
+ locale/@AB_CD@/editor/EditorImageProperties.dtd (%chrome/editor/dialogs/EditorImageProperties.dtd)
+ locale/@AB_CD@/editor/EditorInputProperties.dtd (%chrome/editor/dialogs/EditorInputProperties.dtd)
+ locale/@AB_CD@/editor/EditorInsertChars.dtd (%chrome/editor/dialogs/EditorInsertChars.dtd)
+ locale/@AB_CD@/editor/EditorInsertMath.dtd (%chrome/editor/dialogs/EditorInsertMath.dtd)
+ locale/@AB_CD@/editor/EditorInsertSource.dtd (%chrome/editor/dialogs/EditorInsertSource.dtd)
+ locale/@AB_CD@/editor/EditorInsertTable.dtd (%chrome/editor/dialogs/EditorInsertTable.dtd)
+ locale/@AB_CD@/editor/EditorInsertTOC.dtd (%chrome/editor/dialogs/EditorInsertTOC.dtd)
+ locale/@AB_CD@/editor/EditorLabelProperties.dtd (%chrome/editor/dialogs/EditorLabelProperties.dtd)
+ locale/@AB_CD@/editor/EditorLinkProperties.dtd (%chrome/editor/dialogs/EditorLinkProperties.dtd)
+ locale/@AB_CD@/editor/EditorListProperties.dtd (%chrome/editor/dialogs/EditorListProperties.dtd)
+ locale/@AB_CD@/editor/EditorPageProperties.dtd (%chrome/editor/dialogs/EditorPageProperties.dtd)
+ locale/@AB_CD@/editor/EditorPersonalDictionary.dtd (%chrome/editor/dialogs/EditorPersonalDictionary.dtd)
+ locale/@AB_CD@/editor/EditorPublish.dtd (%chrome/editor/dialogs/EditorPublish.dtd)
+ locale/@AB_CD@/editor/EditorPublishProgress.dtd (%chrome/editor/dialogs/EditorPublishProgress.dtd)
+ locale/@AB_CD@/editor/EditorReplace.dtd (%chrome/editor/dialogs/EditorReplace.dtd)
+ locale/@AB_CD@/editor/EditorSaveAsCharset.dtd (%chrome/editor/dialogs/EditorSaveAsCharset.dtd)
+ locale/@AB_CD@/editor/EditorSelectProperties.dtd (%chrome/editor/dialogs/EditorSelectProperties.dtd)
+ locale/@AB_CD@/editor/EditorSnapToGrid.dtd (%chrome/editor/dialogs/EditorSnapToGrid.dtd)
+ locale/@AB_CD@/editor/EditorSpellCheck.dtd (%chrome/editor/dialogs/EditorSpellCheck.dtd)
+ locale/@AB_CD@/editor/EditorTableProperties.dtd (%chrome/editor/dialogs/EditorTableProperties.dtd)
+ locale/@AB_CD@/editor/EditorTextAreaProperties.dtd (%chrome/editor/dialogs/EditorTextAreaProperties.dtd)
+ locale/@AB_CD@/editor/EditConflict.dtd (%chrome/editor/dialogs/EditConflict.dtd)
+ locale/@AB_CD@/editor/EdNamedAnchorProperties.dtd (%chrome/editor/dialogs/EdNamedAnchorProperties.dtd)
+ locale/@AB_CD@/editor/editorPrefsOverlay.dtd (%chrome/editor/prefs/editorPrefsOverlay.dtd)
+ locale/@AB_CD@/editor/pref-composer.dtd (%chrome/editor/prefs/pref-composer.dtd)
+ locale/@AB_CD@/editor/pref-editing.dtd (%chrome/editor/prefs/pref-editing.dtd)
+ locale/@AB_CD@/messenger/AccountManager.dtd (%chrome/mailnews/pref/AccountManager.dtd)
+ locale/@AB_CD@/messenger/AccountWizard.dtd (%chrome/mailnews/pref/AccountWizard.dtd)
+ locale/@AB_CD@/messenger/addressbook/abAddressBookNameDialog.dtd (%chrome/mailnews/addressbook/abAddressBookNameDialog.dtd)
+ locale/@AB_CD@/messenger/addressbook/abCardOverlay.dtd (%chrome/mailnews/addressbook/abCardOverlay.dtd)
+ locale/@AB_CD@/messenger/addressbook/abMailListDialog.dtd (%chrome/mailnews/addressbook/abMailListDialog.dtd)
+ locale/@AB_CD@/messenger/addressbook/abMainWindow.dtd (%chrome/mailnews/addressbook/abMainWindow.dtd)
+ locale/@AB_CD@/messenger/addressbook/abNewCardDialog.dtd (%chrome/mailnews/addressbook/abNewCardDialog.dtd)
+ locale/@AB_CD@/messenger/addressbook/abResultsPaneOverlay.dtd (%chrome/mailnews/addressbook/abResultsPaneOverlay.dtd)
+ locale/@AB_CD@/messenger/addressbook/abSelectAddressesDialog.dtd (%chrome/mailnews/addressbook/abSelectAddressesDialog.dtd)
+ locale/@AB_CD@/messenger/addressbook/addressBook.properties (%chrome/mailnews/addressbook/addressBook.properties)
+ locale/@AB_CD@/messenger/addressbook/ldapAutoCompErrs.properties (%chrome/mailnews/addressbook/ldapAutoCompErrs.properties)
+ locale/@AB_CD@/messenger/addressbook/pref-addressing.dtd (%chrome/mailnews/pref/pref-addressing.dtd)
+ locale/@AB_CD@/messenger/addressbook/pref-directory.dtd (%chrome/mailnews/pref/pref-directory.dtd)
+ locale/@AB_CD@/messenger/addressbook/pref-directory-add.dtd (%chrome/mailnews/pref/pref-directory-add.dtd)
+ locale/@AB_CD@/messenger/addressbook/replicationProgress.properties (%chrome/mailnews/pref/replicationProgress.properties)
+ locale/@AB_CD@/messenger/am-addressing.dtd (%chrome/mailnews/pref/am-addressing.dtd)
+ locale/@AB_CD@/messenger/am-advanced.dtd (%chrome/mailnews/pref/am-advanced.dtd)
+ locale/@AB_CD@/messenger/am-copies.dtd (%chrome/mailnews/pref/am-copies.dtd)
+ locale/@AB_CD@/messenger/am-identities-list.dtd (%chrome/mailnews/pref/am-identities-list.dtd)
+ locale/@AB_CD@/messenger/am-identity-edit.dtd (%chrome/mailnews/pref/am-identity-edit.dtd)
+ locale/@AB_CD@/messenger/am-junk.dtd (%chrome/mailnews/pref/am-junk.dtd)
+ locale/@AB_CD@/messenger/am-main.dtd (%chrome/mailnews/pref/am-main.dtd)
+ locale/@AB_CD@/messenger/am-mdn.dtd (%chrome/mailnews/pref/am-mdn.dtd)
+ locale/@AB_CD@/messenger/am-mdn.properties (%chrome/mailnews/pref/am-mdn.properties)
+ locale/@AB_CD@/messenger/am-offline.dtd (%chrome/mailnews/pref/am-offline.dtd)
+ locale/@AB_CD@/messenger/am-serverwithnoidentities.dtd (%chrome/mailnews/pref/am-serverwithnoidentities.dtd)
+ locale/@AB_CD@/messenger/am-server-advanced.dtd (%chrome/mailnews/pref/am-server-advanced.dtd)
+ locale/@AB_CD@/messenger/am-server-top.dtd (%chrome/mailnews/pref/am-server-top.dtd)
+ locale/@AB_CD@/messenger/am-archiveoptions.dtd (%chrome/mailnews/pref/am-archiveoptions.dtd)
+ locale/@AB_CD@/messenger/removeAccount.dtd (%chrome/mailnews/pref/removeAccount.dtd)
+ locale/@AB_CD@/messenger/removeAccount.properties (%chrome/mailnews/pref/removeAccount.properties)
+ locale/@AB_CD@/messenger/appleMailImportMsgs.properties (%chrome/mailnews/appleMailImportMsgs.properties)
+ locale/@AB_CD@/messenger/beckyImportMsgs.properties (%chrome/mailnews/beckyImportMsgs.properties)
+ locale/@AB_CD@/messenger/charsetTitles.properties (%chrome/mailnews/charsetTitles.properties)
+ locale/@AB_CD@/messenger/custom.properties (%chrome/mailnews/custom.properties)
+ locale/@AB_CD@/messenger/CustomHeaders.dtd (%chrome/mailnews/CustomHeaders.dtd)
+ locale/@AB_CD@/messenger/downloadheaders.dtd (%chrome/mailnews/downloadheaders.dtd)
+ locale/@AB_CD@/messenger/fieldMapImport.dtd (%chrome/mailnews/fieldMapImport.dtd)
+ locale/@AB_CD@/messenger/filter.properties (%chrome/mailnews/filter.properties)
+ locale/@AB_CD@/messenger/FilterEditor.dtd (%chrome/mailnews/FilterEditor.dtd)
+ locale/@AB_CD@/messenger/FilterListDialog.dtd (%chrome/mailnews/FilterListDialog.dtd)
+ locale/@AB_CD@/messenger/folderpane.dtd (%chrome/mailnews/folderpane.dtd)
+ locale/@AB_CD@/messenger/folderProps.dtd (%chrome/mailnews/folderProps.dtd)
+ locale/@AB_CD@/messenger/folderWidgets.properties (%chrome/mailnews/folderWidgets.properties)
+ locale/@AB_CD@/messenger/gloda.properties (%chrome/mailnews/gloda.properties)
+ locale/@AB_CD@/messenger/imapMsgs.properties (%chrome/mailnews/imapMsgs.properties)
+ locale/@AB_CD@/messenger/importDialog.dtd (%chrome/mailnews/importDialog.dtd)
+ locale/@AB_CD@/messenger/importMsgs.properties (%chrome/mailnews/importMsgs.properties)
+ locale/@AB_CD@/messenger/localMsgs.properties (%chrome/mailnews/localMsgs.properties)
+ locale/@AB_CD@/messenger/junkLog.dtd (%chrome/mailnews/junkLog.dtd)
+ locale/@AB_CD@/messenger/junkMailInfo.dtd (%chrome/mailnews/junkMailInfo.dtd)
+ locale/@AB_CD@/messenger/mailEditorOverlay.dtd (%chrome/mailnews/mailEditorOverlay.dtd)
+ locale/@AB_CD@/messenger/mailKeysOverlay.dtd (%chrome/mailnews/mailKeysOverlay.dtd)
+ locale/@AB_CD@/messenger/mailOverlay.dtd (%chrome/mailnews/mailOverlay.dtd)
+ locale/@AB_CD@/messenger/mailPrefsOverlay.dtd (%chrome/mailnews/pref/mailPrefsOverlay.dtd)
+ locale/@AB_CD@/messenger/mailTasksOverlay.dtd (%chrome/mailnews/mailTasksOverlay.dtd)
+ locale/@AB_CD@/messenger/mailViewList.dtd (%chrome/mailnews/mailViewList.dtd)
+ locale/@AB_CD@/messenger/mailViewSetup.dtd (%chrome/mailnews/mailViewSetup.dtd)
+ locale/@AB_CD@/messenger/mailviews.properties (%chrome/mailnews/mailviews.properties)
+ locale/@AB_CD@/messenger/markByDate.dtd (%chrome/mailnews/markByDate.dtd)
+ locale/@AB_CD@/messenger/messenger.dtd (%chrome/mailnews/messenger.dtd)
+ locale/@AB_CD@/messenger/messenger.properties (%chrome/mailnews/messenger.properties)
+ locale/@AB_CD@/messenger/messengercompose/askSendFormat.dtd (%chrome/mailnews/compose/askSendFormat.dtd)
+ locale/@AB_CD@/messenger/messengercompose/askSendFormat.properties (%chrome/mailnews/compose/askSendFormat.properties)
+ locale/@AB_CD@/messenger/messengercompose/composeMsgs.properties (%chrome/mailnews/compose/composeMsgs.properties)
+ locale/@AB_CD@/messenger/messengercompose/mailComposeEditorOverlay.dtd (%chrome/mailnews/compose/mailComposeEditorOverlay.dtd)
+ locale/@AB_CD@/messenger/messengercompose/messengercompose.dtd (%chrome/mailnews/compose/messengercompose.dtd)
+ locale/@AB_CD@/messenger/messengercompose/pref-composing_messages.dtd (%chrome/mailnews/pref/pref-composing_messages.dtd)
+ locale/@AB_CD@/messenger/messengercompose/pref-formatting.dtd (%chrome/mailnews/pref/pref-formatting.dtd)
+ locale/@AB_CD@/messenger/messengercompose/sendProgress.dtd (%chrome/mailnews/compose/sendProgress.dtd)
+ locale/@AB_CD@/messenger/messengercompose/sendProgress.properties (%chrome/mailnews/compose/sendProgress.properties)
+ locale/@AB_CD@/messenger/mime.properties (%chrome/mailnews/mime.properties)
+ locale/@AB_CD@/messenger/mimeheader.properties (%chrome/mailnews/mimeheader.properties)
+ locale/@AB_CD@/messenger/msgAccountCentral.dtd (%chrome/mailnews/msgAccountCentral.dtd)
+ locale/@AB_CD@/messenger/msgHdrViewOverlay.dtd (%chrome/mailnews/msgHdrViewOverlay.dtd)
+ locale/@AB_CD@/messenger/msgHdrViewPopup.dtd (%chrome/mailnews/msgHdrViewPopup.dtd)
+ locale/@AB_CD@/messenger/msgmdn.properties (%chrome/mailnews/msgmdn.properties)
+ locale/@AB_CD@/messenger/msgPrintEngine.dtd (%chrome/mailnews/msgPrintEngine.dtd)
+ locale/@AB_CD@/messenger/msgSynchronize.dtd (%chrome/mailnews/msgSynchronize.dtd)
+ locale/@AB_CD@/messenger/msgViewPickerOverlay.dtd (%chrome/mailnews/msgViewPickerOverlay.dtd)
+ locale/@AB_CD@/messenger/newFolderDialog.dtd (%chrome/mailnews/newFolderDialog.dtd)
+ locale/@AB_CD@/messenger/news.properties (%chrome/mailnews/news.properties)
+ locale/@AB_CD@/messenger/newsError.dtd (%chrome/mailnews/newsError.dtd)
+ locale/@AB_CD@/messenger/offline.properties (%chrome/mailnews/offline.properties)
+ locale/@AB_CD@/messenger/offlineStartup.properties (%chrome/mailnews/offlineStartup.properties)
+ locale/@AB_CD@/messenger/outlookImportMsgs.properties (%chrome/mailnews/outlookImportMsgs.properties)
+ locale/@AB_CD@/messenger/prefs.properties (%chrome/mailnews/pref/prefs.properties)
+ locale/@AB_CD@/messenger/pref-character_encoding.dtd (%chrome/mailnews/pref/pref-character_encoding.dtd)
+ locale/@AB_CD@/messenger/pref-junk.dtd (%chrome/mailnews/pref/pref-junk.dtd)
+ locale/@AB_CD@/messenger/pref-tags.dtd (%chrome/mailnews/pref/pref-tags.dtd)
+ locale/@AB_CD@/messenger/pref-mailnews.dtd (%chrome/mailnews/pref/pref-mailnews.dtd)
+ locale/@AB_CD@/messenger/pref-notifications.dtd (%chrome/mailnews/pref/pref-notifications.dtd)
+ locale/@AB_CD@/messenger/pref-offline.dtd (%chrome/mailnews/pref/pref-offline.dtd)
+ locale/@AB_CD@/messenger/pref-receipts.dtd (%chrome/mailnews/pref/pref-receipts.dtd)
+ locale/@AB_CD@/messenger/pref-viewing_messages.dtd (%chrome/mailnews/pref/pref-viewing_messages.dtd)
+ locale/@AB_CD@/messenger/renameFolderDialog.dtd (%chrome/mailnews/renameFolderDialog.dtd)
+ locale/@AB_CD@/messenger/search.properties (%chrome/mailnews/search.properties)
+ locale/@AB_CD@/messenger/SearchDialog.dtd (%chrome/mailnews/SearchDialog.dtd)
+ locale/@AB_CD@/messenger/searchTermOverlay.dtd (%chrome/mailnews/searchTermOverlay.dtd)
+ locale/@AB_CD@/messenger/search-attributes.properties (%chrome/mailnews/search-attributes.properties)
+ locale/@AB_CD@/messenger/search-operators.properties (%chrome/mailnews/search-operators.properties)
+ locale/@AB_CD@/messenger/shutdownWindow.properties (%chrome/mailnews/shutdownWindow.properties)
+ locale/@AB_CD@/messenger/smime.properties (%chrome/mailnews/smime.properties)
+ locale/@AB_CD@/messenger/pgpmime.properties (%chrome/mailnews/pgpmime.properties)
+ locale/@AB_CD@/messenger/smtpEditOverlay.dtd (%chrome/mailnews/pref/smtpEditOverlay.dtd)
+ locale/@AB_CD@/messenger/start.dtd (%chrome/mailnews/start.dtd)
+ locale/@AB_CD@/messenger/subscribe.dtd (%chrome/mailnews/subscribe.dtd)
+ locale/@AB_CD@/messenger/subscribe.properties (%chrome/mailnews/subscribe.properties)
+ locale/@AB_CD@/messenger/tabmail.properties (%chrome/mailnews/tabmail.properties)
+ locale/@AB_CD@/messenger/textImportMsgs.properties (%chrome/mailnews/textImportMsgs.properties)
+ locale/@AB_CD@/messenger/threadpane.dtd (%chrome/mailnews/threadpane.dtd)
+ locale/@AB_CD@/messenger/vCardImportMsgs.properties (%chrome/mailnews/vCardImportMsgs.properties)
+ locale/@AB_CD@/messenger/viewLog.dtd (%chrome/mailnews/viewLog.dtd)
+ locale/@AB_CD@/messenger/virtualFolderProperties.dtd (%chrome/mailnews/virtualFolderProperties.dtd)
+ locale/@AB_CD@/messenger/virtualFolderListDialog.dtd (%chrome/mailnews/virtualFolderListDialog.dtd)
+ locale/@AB_CD@/messenger/wmImportMsgs.properties (%chrome/mailnews/wmImportMsgs.properties)
+ locale/@AB_CD@/messenger-mapi/mapi.properties (%chrome/mailnews/mapi/mapi.properties)
+ locale/@AB_CD@/messenger-region/region.properties (%chrome/mailnews/region.properties)
+ locale/@AB_CD@/messenger/am-smime.dtd (%chrome/mailnews/smime/am-smime.dtd)
+ locale/@AB_CD@/messenger/am-smime.properties (%chrome/mailnews/smime/am-smime.properties)
+ locale/@AB_CD@/messenger-smime/certFetchingStatus.dtd (%chrome/mailnews/smime/certFetchingStatus.dtd)
+ locale/@AB_CD@/messenger-smime/msgCompSecurityInfo.dtd (%chrome/mailnews/smime/msgCompSecurityInfo.dtd)
+ locale/@AB_CD@/messenger-smime/msgCompSecurityInfo.properties (%chrome/mailnews/smime/msgCompSecurityInfo.properties)
+ locale/@AB_CD@/messenger-smime/msgCompSMIMEOverlay.dtd (%chrome/mailnews/smime/msgCompSMIMEOverlay.dtd)
+ locale/@AB_CD@/messenger-smime/msgCompSMIMEOverlay.properties (%chrome/mailnews/smime/msgCompSMIMEOverlay.properties)
+ locale/@AB_CD@/messenger-smime/msgReadSecurityInfo.dtd (%chrome/mailnews/smime/msgReadSecurityInfo.dtd)
+ locale/@AB_CD@/messenger-smime/msgReadSMIMEOverlay.dtd (%chrome/mailnews/smime/msgReadSMIMEOverlay.dtd)
+ locale/@AB_CD@/messenger-smime/msgReadSMIMEOverlay.properties (%chrome/mailnews/smime/msgReadSMIMEOverlay.properties)
+ locale/@AB_CD@/messenger-smime/msgSecurityInfo.properties (%chrome/mailnews/smime/msgSecurityInfo.properties)
+ locale/@AB_CD@/messenger-newsblog/newsblog.properties (%chrome/mailnews/newsblog/newsblog.properties)
+ locale/@AB_CD@/messenger-newsblog/feed-subscriptions.dtd (%chrome/mailnews/newsblog/feed-subscriptions.dtd)
+ locale/@AB_CD@/messenger-newsblog/am-newsblog.dtd (%chrome/mailnews/newsblog/am-newsblog.dtd)
+ locale/@AB_CD@/mozldap/ldap.properties (%chrome/mozldap/ldap.properties)
+ locale/@AB_CD@/navigator/linkToolbar.dtd (%chrome/browser/linkToolbar.dtd)
+ locale/@AB_CD@/navigator/mailNavigatorOverlay.dtd (%chrome/browser/mailNavigatorOverlay.dtd)
+ locale/@AB_CD@/navigator/metadata.dtd (%chrome/browser/metadata.dtd)
+ locale/@AB_CD@/navigator/metadata.properties (%chrome/browser/metadata.properties)
+ locale/@AB_CD@/navigator/navigator.dtd (%chrome/browser/navigator.dtd)
+ locale/@AB_CD@/navigator/navigator.properties (%chrome/browser/navigator.properties)
+ locale/@AB_CD@/navigator/navigatorOverlay.dtd (%chrome/browser/navigatorOverlay.dtd)
+ locale/@AB_CD@/navigator/pageInfo.dtd (%chrome/browser/pageInfo.dtd)
+ locale/@AB_CD@/navigator/pageInfo.properties (%chrome/browser/pageInfo.properties)
+ locale/@AB_CD@/navigator/tabbrowser.dtd (%chrome/browser/tabbrowser.dtd)
+ locale/@AB_CD@/navigator/tabbrowser.properties (%chrome/browser/tabbrowser.properties)
+ locale/@AB_CD@/navigator/taskbar.properties (%chrome/browser/taskbar.properties)
+ locale/@AB_CD@/navigator/webDeveloper.dtd (%chrome/browser/webDeveloper.dtd)
+ locale/@AB_CD@/navigator-region/region.properties (%chrome/browser/region.properties)
+ locale/@AB_CD@/pippki/pref-certs.dtd (%chrome/common/pref/pref-certs.dtd)
+ locale/@AB_CD@/pippki/pref-masterpass.dtd (%chrome/common/pref/pref-masterpass.dtd)
+ locale/@AB_CD@/pippki/pref-passwords.dtd (%chrome/common/pref/pref-passwords.dtd)
+ locale/@AB_CD@/pippki/pref-ssl.dtd (%chrome/common/pref/pref-ssl.dtd)
diff --git a/comm/suite/locales/l10n-beta.ini b/comm/suite/locales/l10n-beta.ini
new file mode 100644
index 0000000000..c56ef0e1ce
--- /dev/null
+++ b/comm/suite/locales/l10n-beta.ini
@@ -0,0 +1,36 @@
+; 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/.
+
+[general]
+depth = ../..
+all = suite/locales/all-locales
+
+[compare]
+dirs = suite
+ suite/branding/seamonkey
+
+[includes]
+# non-central apps might want to use %(topsrcdir)s here, or other vars
+# RFE: that needs to be supported by compare-locales, too, though
+toolkit = mozilla/toolkit/locales/l10n.ini
+services_sync = mozilla/services/sync/locales/l10n.ini
+devtools_client = mozilla/devtools/client/locales/l10n.ini
+
+[include_toolkit]
+type = hg
+mozilla = releases/mozilla-beta
+repo = https://hg.mozilla.org/
+l10n.ini = toolkit/locales/l10n.ini
+
+[include_services_sync]
+type = hg
+mozilla = releases/mozilla-beta
+repo = https://hg.mozilla.org/
+l10n.ini = services/sync/locales/l10n.ini
+
+[include_devtools_client]
+type = hg
+mozilla = releases/mozilla-beta
+repo = https://hg.mozilla.org/
+l10n.ini = devtools/client/locales/l10n.ini
diff --git a/comm/suite/locales/l10n-central.ini b/comm/suite/locales/l10n-central.ini
new file mode 100644
index 0000000000..911801903e
--- /dev/null
+++ b/comm/suite/locales/l10n-central.ini
@@ -0,0 +1,36 @@
+; 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/.
+
+[general]
+depth = ../..
+all = suite/locales/all-locales
+
+[compare]
+dirs = suite
+ suite/branding/seamonkey
+
+[includes]
+# non-central apps might want to use %(topsrcdir)s here, or other vars
+# RFE: that needs to be supported by compare-locales, too, though
+toolkit = mozilla/toolkit/locales/l10n.ini
+services_sync = mozilla/services/sync/locales/l10n.ini
+devtools_client = mozilla/devtools/client/locales/l10n.ini
+
+[include_toolkit]
+type = hg
+mozilla = mozilla-central
+repo = https://hg.mozilla.org/
+l10n.ini = toolkit/locales/l10n.ini
+
+[include_services_sync]
+type = hg
+mozilla = mozilla-central
+repo = https://hg.mozilla.org/
+l10n.ini = services/sync/locales/l10n.ini
+
+[include_devtools_client]
+type = hg
+mozilla = mozilla-central
+repo = https://hg.mozilla.org/
+l10n.ini = devtools/client/locales/l10n.ini
diff --git a/comm/suite/locales/l10n.ini b/comm/suite/locales/l10n.ini
new file mode 100644
index 0000000000..d0251231ee
--- /dev/null
+++ b/comm/suite/locales/l10n.ini
@@ -0,0 +1,20 @@
+; 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/.
+
+[general]
+depth = ../..
+all = suite/locales/all-locales
+
+[compare]
+dirs = suite
+ suite/branding/seamonkey
+
+[includes]
+# include toolkit and services/sync from mozilla.
+# Don't specify which, use l10n-central.ini and friends if you're
+# not working on a local check-out
+toolkit = mozilla/toolkit/locales/l10n.ini
+services_sync = mozilla/services/sync/locales/l10n.ini
+devtools_client = mozilla/devtools/client/locales/l10n.ini
+chatzilla = mozilla/comm/suite/chatzilla/locales/l10n.ini
diff --git a/comm/suite/locales/l10n.toml b/comm/suite/locales/l10n.toml
new file mode 100644
index 0000000000..be8e8c6ac9
--- /dev/null
+++ b/comm/suite/locales/l10n.toml
@@ -0,0 +1,126 @@
+# 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/.
+
+basepath = "../.."
+
+locales = [
+ "ca",
+ "cs",
+ "da",
+ "de",
+ "el",
+ "en-GB",
+ "es-AR",
+ "es-ES",
+ "fi",
+ "fr",
+ "hu",
+ "it",
+ "ja",
+ "ja-JP-mac",
+ "ka",
+ "lt",
+ "nb-NO",
+ "nl",
+ "pl",
+ "pt-BR",
+ "pt-PT",
+ "ro",
+ "ru",
+ "si",
+ "sk",
+ "sv-SE",
+ "tr",
+ "zh-CN",
+ "zh-TW",
+]
+
+[env]
+ l = "{l10n_base}/{locale}/"
+ mozilla = ".."
+
+[[paths]]
+ reference = "suite/locales/en-US/**"
+ l10n = "{l}suite/**"
+
+[[paths]]
+ reference = "suite/branding/seamonkey/locales/en-US/**"
+ l10n = "{l}suite/branding/seamonkey/**"
+
+[[paths]]
+ reference = "{mozilla}/services/sync/locales/en-US/**"
+ l10n = "{l}services/sync/**"
+
+[[includes]]
+ path = "{mozilla}/toolkit/locales/l10n.toml"
+
+[[includes]]
+ path = "calendar/locales/l10n.toml"
+
+[[includes]]
+ path = "{mozilla}/devtools/client/locales/l10n.toml"
+
+[[includes]]
+ path = "suite/chatzilla/locales/l10n.toml"
+
+[[paths]]
+ reference = "{mozilla}/devtools/startup/locales/en-US/**"
+ l10n = "{l}devtools/startup/**"
+
+# for Gecko 60 and older and cross-channel
+[[paths]]
+ reference = "{mozilla}/devtools/shim/locales/en-US/**"
+ l10n = "{l}devtools/shim/**"
+
+# ignore MOZ_LANGPACK_CONTRIBUTORS
+[[filters]]
+ path = "{l}suite/defines.inc"
+ key = "MOZ_LANGPACK_CONTRIBUTORS"
+ action = "ignore"
+
+# defines.inc and suite-l10n.js can be missing completely
+[[filters]]
+ path = [
+ "{l}suite/defines.inc",
+ "{l}suite/suite-l10n.js",
+ ]
+ action = "ignore"
+
+# ignore MOZ_LANGPACK_CONTRIBUTORS
+[[filters]]
+ path = "{l}suite/chatzilla/defines.inc"
+ key = "MOZ_LANGPACK_CONTRIBUTORS"
+ action = "ignore"
+
+# search prefs don't have the same number for all locales
+[[filters]]
+ path = "{l}suite/chrome/common/region.properties"
+ key = "re:browser\\.search\\.order\\.[1-9].*"
+ action = "ignore"
+
+# content handler prefs don't have the same number for all locales
+[[filters]]
+ path = "{l}suite/chrome/browser/region.properties"
+ key = "re:browser\\.contentHandlers\\.types\\.[0-5].*"
+ action = "ignore"
+
+# map service prefs don't have the same number for all locales
+[[filters]]
+ path = "{l}suite/chrome/mailnews/region.properties"
+ key = "re:mail\\.addr_book\\.mapit_url\\.[1-5].*"
+ action = "ignore"
+
+# search plugins are different per locale, ignore file difference
+[[filters]]
+ path = "{l}suite/searchplugins/*.xml"
+ action = "ignore"
+
+# extra data and help images are optional
+[[filters]]
+ path = [
+ "{l}suite/profile/bookmarks.extra",
+ "{l}suite/profile/panels.extra",
+ "{l}suite/chrome/common/help/images/*.*",
+ ]
+ action = "ignore"
diff --git a/comm/suite/locales/moz.build b/comm/suite/locales/moz.build
new file mode 100644
index 0000000000..0418ecbfc6
--- /dev/null
+++ b/comm/suite/locales/moz.build
@@ -0,0 +1,18 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+# If DIST_SUBDIR ever gets unset in browser this path might be wrong due to PREF_DIR changing.
+LOCALIZED_PP_FILES.defaults.pref += ["en-US/suite-l10n.js"]
+
+if CONFIG["MOZ_CRASHREPORTER"]:
+ LOCALIZED_FILES += ["en-US/crashreporter/crashreporter-override.ini"]
+
+DEFINES["MOZ_APP_MAXVERSION"] = CONFIG["MOZ_APP_MAXVERSION"]
+
+FINAL_TARGET_FILES.defaults.profile += [
+ "generic/profile/mimeTypes.rdf",
+]
diff --git a/comm/suite/locales/shipped-locales b/comm/suite/locales/shipped-locales
new file mode 100644
index 0000000000..2a837ad123
--- /dev/null
+++ b/comm/suite/locales/shipped-locales
@@ -0,0 +1,25 @@
+cs
+de
+el
+en-GB
+en-US
+es-AR
+es-ES
+fi
+fr
+hu
+it
+ja linux win32
+ja-JP-mac osx
+ka
+lt
+nb-NO
+nl
+pl
+pt-BR
+pt-PT
+ru
+sk
+sv-SE
+zh-CN
+zh-TW
diff --git a/comm/suite/mailnews/components/addrbook/content/abCardOverlay.js b/comm/suite/mailnews/components/addrbook/content/abCardOverlay.js
new file mode 100644
index 0000000000..06167ca240
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abCardOverlay.js
@@ -0,0 +1,1397 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const kNonVcardFields =
+ ["NickNameContainer", "SecondaryEmailContainer", "ScreenNameContainer",
+ "customFields", "preferDisplayName"];
+
+const kPhoneticFields =
+ ["PhoneticLastName", "PhoneticLabel1", "PhoneticSpacer1",
+ "PhoneticFirstName", "PhoneticLabel2", "PhoneticSpacer2"];
+
+// Item is |[dialogField, cardProperty]|.
+const kVcardFields =
+ [ // Contact > Name
+ ["FirstName", "FirstName"],
+ ["LastName", "LastName"],
+ ["DisplayName", "DisplayName"],
+ ["NickName", "NickName"],
+ // Contact > Internet
+ ["PrimaryEmail", "PrimaryEmail"],
+ ["SecondEmail", "SecondEmail"],
+ // Contact > Phones
+ ["WorkPhone", "WorkPhone"],
+ ["HomePhone", "HomePhone"],
+ ["FaxNumber", "FaxNumber"],
+ ["PagerNumber", "PagerNumber"],
+ ["CellularNumber", "CellularNumber"],
+ // Address > Home
+ ["HomeAddress", "HomeAddress"],
+ ["HomeAddress2", "HomeAddress2"],
+ ["HomeCity", "HomeCity"],
+ ["HomeState", "HomeState"],
+ ["HomeZipCode", "HomeZipCode"],
+ ["HomeCountry", "HomeCountry"],
+ ["WebPage2", "WebPage2"],
+ // Address > Work
+ ["JobTitle", "JobTitle"],
+ ["Department", "Department"],
+ ["Company", "Company"],
+ ["WorkAddress", "WorkAddress"],
+ ["WorkAddress2", "WorkAddress2"],
+ ["WorkCity", "WorkCity"],
+ ["WorkState", "WorkState"],
+ ["WorkZipCode", "WorkZipCode"],
+ ["WorkCountry", "WorkCountry"],
+ ["WebPage1", "WebPage1"],
+ // Other > (custom)
+ ["Custom1", "Custom1"],
+ ["Custom2", "Custom2"],
+ ["Custom3", "Custom3"],
+ ["Custom4", "Custom4"],
+ // Other > Notes
+ ["Notes", "Notes"],
+ // Chat
+ ["Yahoo", "_Yahoo"],
+ ["Skype", "_Skype"],
+ ["QQ", "_QQ"],
+ ["MSN", "_MSN"],
+ ["ICQ", "_ICQ"],
+ ["XMPP", "_JabberId"],
+ ["IRC", "_IRC"]
+ ];
+
+var gEditCard;
+var gOnSaveListeners = [];
+var gOnLoadListeners = [];
+var gOkCallback = null;
+var gHideABPicker = false;
+var gPhotoHandlers = {};
+// If any new photos were added to the card, this stores the name of the original
+// and any temporary new filenames used to store photos of the card.
+// 'null' is a valid value when there was no photo (e.g. the generic photo).
+var gOldPhotos = [];
+// If a new photo was added, the name is stored here.
+var gNewPhoto = null;
+
+function OnLoadNewCard()
+{
+ InitEditCard();
+
+ gEditCard.card =
+ (("arguments" in window) && (window.arguments.length > 0) &&
+ (window.arguments[0] instanceof Ci.nsIAbCard))
+ ? window.arguments[0]
+ : Cc["@mozilla.org/addressbook/cardproperty;1"]
+ .createInstance(Ci.nsIAbCard);
+ gEditCard.titleProperty = "newContactTitle";
+ gEditCard.selectedAB = "";
+
+ if ("arguments" in window && window.arguments[0])
+ {
+ gEditCard.selectedAB = kPersonalAddressbookURI;
+
+ if ("selectedAB" in window.arguments[0] &&
+ (window.arguments[0].selectedAB != kAllDirectoryRoot + "?")) {
+ // check if selected ab is a mailing list
+ var abURI = window.arguments[0].selectedAB;
+
+ var directory = GetDirectoryFromURI(abURI);
+ if (directory.isMailList) {
+ var parentURI = GetParentDirectoryFromMailingListURI(abURI);
+ if (parentURI)
+ gEditCard.selectedAB = parentURI;
+ }
+ else if (!directory.readOnly)
+ gEditCard.selectedAB = window.arguments[0].selectedAB;
+ }
+
+ // we may have been given properties to pre-initialize the window with....
+ // we'll fill these in here...
+ if ("primaryEmail" in window.arguments[0])
+ gEditCard.card.primaryEmail = window.arguments[0].primaryEmail;
+ if ("displayName" in window.arguments[0]) {
+ gEditCard.card.displayName = window.arguments[0].displayName;
+ // if we've got a display name, don't generate
+ // a display name (and stomp on the existing display name)
+ // when the user types a first or last name
+ if (gEditCard.card.displayName)
+ gEditCard.generateDisplayName = false;
+ }
+ if ("okCallback" in window.arguments[0])
+ gOkCallback = window.arguments[0].okCallback;
+
+ if ("escapedVCardStr" in window.arguments[0]) {
+ // hide non vcard values
+ HideNonVcardFields();
+ gEditCard.card = Cc["@mozilla.org/addressbook/msgvcardservice;1"]
+ .getService(Ci.nsIMsgVCardService)
+ .escapedVCardToAbCard(window.arguments[0].escapedVCardStr);
+ }
+
+ if ("titleProperty" in window.arguments[0])
+ gEditCard.titleProperty = window.arguments[0].titleProperty;
+
+ if ("hideABPicker" in window.arguments[0])
+ gHideABPicker = window.arguments[0].hideABPicker;
+ }
+
+ // set popup with address book names
+ var abPopup = document.getElementById('abPopup');
+ abPopup.value = gEditCard.selectedAB || kPersonalAddressbookURI;
+
+ if (gHideABPicker && abPopup) {
+ abPopup.hidden = true;
+ document.getElementById("abPopupLabel").hidden = true;
+ }
+
+ SetCardDialogTitle(gEditCard.card.displayName);
+
+ GetCardValues(gEditCard.card, document);
+
+ // FIX ME - looks like we need to focus on both the text field and the tab widget
+ // probably need to do the same in the addressing widget
+
+ // focus on first or last name based on the pref
+ var focus = document.getElementById(gEditCard.displayLastNameFirst
+ ? "LastName" : "FirstName");
+ if (focus) {
+ // XXX Using the setTimeout hack until bug 103197 is fixed
+ setTimeout( function(firstTextBox) { firstTextBox.focus(); }, 0, focus );
+ }
+}
+
+/**
+ * Get the source directory containing the card we are editing.
+ */
+function getContainingDirectory() {
+ let directory = GetDirectoryFromURI(gEditCard.abURI);
+ // If the source directory is "All Address Books", find the parent
+ // address book of the card being edited and reflect the changes in it.
+ if (directory.URI == kAllDirectoryRoot + "?") {
+ let dirId =
+ gEditCard.card.directoryId
+ .substring(0, gEditCard.card.directoryId.indexOf("&"));
+ directory = MailServices.ab.getDirectoryFromId(dirId);
+ }
+ return directory;
+}
+
+function EditCardOKButton()
+{
+ if (!CheckCardRequiredDataPresence(document))
+ return false; // don't close window
+
+ // See if this card is in any mailing list
+ // if so then we need to update the addresslists of those mailing lists
+ let directory = getContainingDirectory();
+
+ // if the directory is a mailing list we need to search all the mailing lists
+ // in the parent directory if the card exists.
+ if (directory.isMailList) {
+ var parentURI = GetParentDirectoryFromMailingListURI(gEditCard.abURI);
+ directory = GetDirectoryFromURI(parentURI);
+ }
+
+ var listDirectoriesCount = directory.addressLists.length;
+ var foundDirectories = [];
+
+ // create a list of mailing lists and the index where the card is at.
+ for (let i = 0; i < listDirectoriesCount; i++)
+ {
+ var subdirectory = directory.addressLists.queryElementAt(i, Ci.nsIAbDirectory);
+ if (subdirectory.isMailList)
+ {
+ // See if any card in this list is the one we edited.
+ // Must compare card contents using .equals() instead of .indexOf()
+ // because gEditCard is not really a member of the .addressLists array.
+ let listCardsCount = subdirectory.addressLists.length;
+ for (let index = 0; index < listCardsCount; index++)
+ {
+ let card = subdirectory.addressLists.queryElementAt(index, Ci.nsIAbCard);
+ if (card.equals(gEditCard.card))
+ foundDirectories.push({directory:subdirectory, cardIndex:index});
+ }
+ }
+ }
+
+ CheckAndSetCardValues(gEditCard.card, document, false);
+
+ directory.modifyCard(gEditCard.card);
+
+ while (foundDirectories.length)
+ {
+ // Update the addressLists item for this card
+ let foundItem = foundDirectories.pop();
+ foundItem.directory.addressLists.replaceElementAt(gEditCard.card, foundItem.cardIndex);
+ }
+
+ NotifySaveListeners(directory);
+
+ // callback to allow caller to update
+ if (gOkCallback)
+ gOkCallback();
+
+ return true; // close the window
+}
+
+function EditCardCancelButton()
+{
+ // If a new photo was created, remove it now as it won't be used.
+ purgeOldPhotos(false);
+}
+
+function OnLoadEditCard()
+{
+ InitEditCard();
+
+ gEditCard.titleProperty = "editContactTitle";
+
+ if (window.arguments && window.arguments[0])
+ {
+ if ( window.arguments[0].card )
+ gEditCard.card = window.arguments[0].card;
+ if ( window.arguments[0].okCallback )
+ gOkCallback = window.arguments[0].okCallback;
+ if ( window.arguments[0].abURI )
+ gEditCard.abURI = window.arguments[0].abURI;
+ }
+
+ // set global state variables
+ // if first or last name entered, disable generateDisplayName
+ if (gEditCard.generateDisplayName &&
+ (gEditCard.card.firstName.length +
+ gEditCard.card.lastName.length +
+ gEditCard.card.displayName.length > 0))
+ {
+ gEditCard.generateDisplayName = false;
+ }
+
+ GetCardValues(gEditCard.card, document);
+
+ SetCardDialogTitle(gEditCard.card.displayName);
+
+ // check if selectedAB is a writeable
+ // if not disable all the fields
+ if ("arguments" in window && window.arguments[0])
+ {
+ if ("abURI" in window.arguments[0]) {
+ var abURI = window.arguments[0].abURI;
+ var directory = GetDirectoryFromURI(abURI);
+
+ if (directory.readOnly)
+ {
+ // Set all the editable vcard fields to read only
+ for (var i = kVcardFields.length; i-- > 0; )
+ document.getElementById(kVcardFields[i][0]).readOnly = true;
+
+ // the birthday fields
+ document.getElementById("Birthday").readOnly = true;
+ document.getElementById("BirthYear").readOnly = true;
+ document.getElementById("Age").readOnly = true;
+
+ // the photo field and buttons
+ document.getElementById("PhotoType").disabled = true;
+ document.getElementById("GenericPhotoList").disabled = true;
+ document.getElementById("PhotoURI").disabled = true;
+ document.getElementById("PhotoURI").placeholder = "";
+ document.getElementById("BrowsePhoto").disabled = true;
+ document.getElementById("UpdatePhoto").disabled = true;
+
+ // And the phonetic fields
+ document.getElementById(kPhoneticFields[0]).readOnly = true;
+ document.getElementById(kPhoneticFields[3]).readOnly = true;
+
+ // Also disable the mail format popup.
+ document.getElementById("PreferMailFormatPopup").disabled = true;
+
+ // And the "prefer display name" checkbox.
+ document.getElementById("preferDisplayName").disabled = true;
+
+ document.documentElement.buttons = "accept";
+ document.documentElement.removeAttribute("ondialogaccept");
+ }
+ }
+ }
+}
+
+/* Registers functions that are called when loading the card
+ * values into the contact editor dialog. This is useful if
+ * extensions have added extra fields to the nsIAbCard, and
+ * need to display them in the contact editor.
+ */
+function RegisterLoadListener(aFunc)
+{
+ gOnLoadListeners.push(aFunc);
+}
+
+function UnregisterLoadListener(aFunc)
+{
+ var fIndex = gOnLoadListeners.indexOf(aFunc);
+ if (fIndex != -1)
+ gOnLoadListeners.splice(fIndex, 1);
+}
+
+// Notifies load listeners that an nsIAbCard is being loaded.
+function NotifyLoadListeners(aCard, aDoc)
+{
+ if (!gOnLoadListeners.length)
+ return;
+
+ for (let listener of gOnLoadListeners)
+ listener(aCard, aDoc);
+}
+
+/* Registers functions that are called when saving the card
+ * values. This is useful if extensions have added extra
+ * fields to the user interface, and need to set those values
+ * in their nsIAbCard.
+ */
+function RegisterSaveListener(aFunc)
+{
+ gOnSaveListeners.push(aFunc);
+}
+
+function UnregisterSaveListener(aFunc)
+{
+ var fIndex = gOnSaveListeners.indexOf(aFunc);
+ if (fIndex != -1)
+ gOnSaveListeners.splice(fIndex, 1);
+}
+
+// Notifies save listeners that an nsIAbCard is being saved.
+function NotifySaveListeners(directory)
+{
+ if (!gOnSaveListeners.length)
+ return;
+
+ for (let listener of gOnSaveListeners)
+ listener(gEditCard.card, document);
+
+ // the save listeners might have tweaked the card
+ // in which case we need to commit it.
+ directory.modifyCard(gEditCard.card);
+}
+
+function InitPhoneticFields()
+{
+ var showPhoneticFields =
+ Services.prefs.getComplexValue("mail.addr_book.show_phonetic_fields",
+ Ci.nsIPrefLocalizedString).data;
+
+ // show phonetic fields if indicated by the pref
+ if (showPhoneticFields == "true")
+ {
+ for (var i = kPhoneticFields.length; i-- > 0; )
+ document.getElementById(kPhoneticFields[i]).hidden = false;
+ }
+}
+
+function InitEditCard()
+{
+ InitPhoneticFields();
+
+ InitCommonJS();
+ // Create gEditCard object that contains global variables for the current js
+ // file.
+ gEditCard = new Object();
+
+ // get specific prefs that gEditCard will need
+ try {
+ var displayLastNameFirst =
+ Services.prefs.getComplexValue("mail.addr_book.displayName.lastnamefirst",
+ Ci.nsIPrefLocalizedString).data;
+ gEditCard.displayLastNameFirst = (displayLastNameFirst == "true");
+ gEditCard.generateDisplayName =
+ Services.prefs.getBoolPref("mail.addr_book.displayName.autoGeneration");
+ }
+ catch (ex) {
+ dump("ex: failed to get pref" + ex + "\n");
+ }
+}
+
+function NewCardOKButton()
+{
+ if (gOkCallback)
+ {
+ if (!CheckAndSetCardValues(gEditCard.card, document, true))
+ return false; // don't close window
+
+ gOkCallback(gEditCard.card.translateTo("vcard"));
+ return true; // close the window
+ }
+
+ var popup = document.getElementById('abPopup');
+ if ( popup )
+ {
+ var uri = popup.value;
+
+ // FIX ME - hack to avoid crashing if no ab selected because of blank option bug from template
+ // should be able to just remove this if we are not seeing blank lines in the ab popup
+ if ( !uri )
+ return false; // don't close window
+ // -----
+
+ if (gEditCard.card)
+ {
+ if (!CheckAndSetCardValues(gEditCard.card, document, true))
+ return false; // don't close window
+
+ // replace gEditCard.card with the card we added
+ // so that save listeners can get / set attributes on
+ // the card that got created.
+ var directory = GetDirectoryFromURI(uri);
+ gEditCard.card = directory.addCard(gEditCard.card);
+ NotifySaveListeners(directory);
+ }
+ }
+
+ return true; // close the window
+}
+
+function NewCardCancelButton()
+{
+ // If a new photo was created, remove it now as it won't be used.
+ purgeOldPhotos(false);
+}
+
+// Move the data from the cardproperty to the dialog
+function GetCardValues(cardproperty, doc)
+{
+ if (!cardproperty)
+ return;
+
+ // Pass the nsIAbCard and the Document through the listeners
+ // to give extensions a chance to populate custom fields.
+ NotifyLoadListeners(cardproperty, doc);
+
+ for (var i = kVcardFields.length; i-- > 0; ) {
+ doc.getElementById(kVcardFields[i][0]).value =
+ cardproperty.getProperty(kVcardFields[i][1], "");
+ }
+
+ var birthday = doc.getElementById("Birthday");
+ modifyDatepicker(birthday);
+
+ // Get the year first, so that the following month/day
+ // calculations can take leap years into account.
+ var year = cardproperty.getProperty("BirthYear", null);
+ var birthYear = doc.getElementById("BirthYear");
+ // set the year in the datepicker to the stored year
+ // if the year isn't present, default to 2000 (a leap year)
+ birthday.year = saneBirthYear(year);
+ birthYear.value = year;
+
+ // get the month of the year (1 - 12)
+ var month = cardproperty.getProperty("BirthMonth", null);
+ if (month > 0 && month < 13)
+ birthday.month = month - 1;
+ else
+ birthday.monthField.value = null;
+
+ // get the date of the month (1 - 31)
+ var date = cardproperty.getProperty("BirthDay", null);
+ if (date > 0 && date < 32)
+ birthday.date = date;
+ else
+ birthday.dateField.value = null;
+
+ // get the current age
+ calculateAge(null, birthYear);
+ // when the birth year changes, update the datepicker's year to the new value
+ // or to kDefaultYear if the value is null
+ birthYear.onchange = calculateAge;
+ birthday.onchange = calculateAge;
+ var age = doc.getElementById("Age");
+ age.onchange = calculateYear;
+
+ var popup = document.getElementById("PreferMailFormatPopup");
+ if (popup)
+ popup.value = cardproperty.getProperty("PreferMailFormat", "");
+
+ var preferDisplayNameEl = document.getElementById("preferDisplayName");
+ if (preferDisplayNameEl)
+ // getProperty may return a "1" or "0" string, we want a boolean
+ preferDisplayNameEl.checked = cardproperty.getProperty("PreferDisplayName", true) != false;
+
+ // get phonetic fields if exist
+ try {
+ doc.getElementById("PhoneticFirstName").value = cardproperty.getProperty("PhoneticFirstName", "");
+ doc.getElementById("PhoneticLastName").value = cardproperty.getProperty("PhoneticLastName", "");
+ }
+ catch (ex) {}
+
+ // Select the type if there is a valid value stored for that type, otherwise
+ // select the generic photo
+ loadPhoto(cardproperty);
+
+ updateChatName();
+}
+
+// when the ab card dialog is being loaded to show a vCard,
+// hide the fields which aren't supported
+// by vCard so the user does not try to edit them.
+function HideNonVcardFields()
+{
+ document.getElementById("homeTabButton").hidden = true;
+ document.getElementById("photoTabButton").hidden = true;
+ var i;
+ for (i = kNonVcardFields.length; i-- > 0; )
+ document.getElementById(kNonVcardFields[i]).collapsed = true;
+ for (i = kPhoneticFields.length; i-- > 0; )
+ document.getElementById(kPhoneticFields[i]).collapsed = true;
+}
+
+// Move the data from the dialog to the cardproperty to be stored in the database
+// @Returns false - Some required data are missing (card values were not set);
+// true - Card values were set, or there is no card to set values on.
+function CheckAndSetCardValues(cardproperty, doc, check)
+{
+ // If requested, check the required data presence.
+ if (check && !CheckCardRequiredDataPresence(document))
+ return false;
+
+ if (!cardproperty)
+ return true;
+
+ for (var i = kVcardFields.length; i-- > 0; )
+ cardproperty.setProperty(kVcardFields[i][1],
+ doc.getElementById(kVcardFields[i][0]).value);
+
+ // get the birthday information from the dialog
+ var birthdayElem = doc.getElementById("Birthday");
+ var birthMonth = birthdayElem.monthField.value;
+ var birthDay = birthdayElem.dateField.value;
+ var birthYear = doc.getElementById("BirthYear").value;
+
+ // set the birth day, month, and year properties
+ cardproperty.setProperty("BirthDay", birthDay);
+ cardproperty.setProperty("BirthMonth", birthMonth);
+ cardproperty.setProperty("BirthYear", birthYear);
+
+ var popup = document.getElementById("PreferMailFormatPopup");
+ if (popup)
+ cardproperty.setProperty("PreferMailFormat", popup.value);
+
+ var preferDisplayNameEl = document.getElementById("preferDisplayName");
+ if (preferDisplayNameEl)
+ cardproperty.setProperty("PreferDisplayName", preferDisplayNameEl.checked);
+
+ // set phonetic fields if exist
+ try {
+ cardproperty.setProperty("PhoneticFirstName", doc.getElementById("PhoneticFirstName").value);
+ cardproperty.setProperty("PhoneticLastName", doc.getElementById("PhoneticLastName").value);
+ }
+ catch (ex) {}
+
+ let photoType = doc.getElementById("PhotoType").value;
+ if (gPhotoHandlers[photoType]) {
+ if (!gPhotoHandlers[photoType].onSave(cardproperty, doc)) {
+ photoType = "generic";
+ onSwitchPhotoType("generic");
+ gPhotoHandlers[photoType].onSave(cardproperty, doc);
+ }
+ }
+ cardproperty.setProperty("PhotoType", photoType);
+ purgeOldPhotos(true);
+
+ // Remove obsolete chat names.
+ try {
+ cardproperty.setProperty("_GoogleTalk", "");
+ }
+ catch (ex) {}
+ try {
+ cardproperty.setProperty("_AimScreenName", "");
+ }
+ catch (ex) {}
+
+ return true;
+}
+
+function CleanUpWebPage(webPage)
+{
+ // no :// yet so we should add something
+ if ( webPage.length && webPage.search("://") == -1 )
+ {
+ // check for missing / on http://
+ if ( webPage.substr(0, 6) == "http:/" )
+ return( "http://" + webPage.substr(6) );
+ else
+ return( "http://" + webPage );
+ }
+ else
+ return(webPage);
+}
+
+// @Returns false - Some required data are missing;
+// true - All required data are present.
+function CheckCardRequiredDataPresence(doc)
+{
+ // Bug 314995 - We require at least one of the following fields to be
+ // filled in: email address, first name, last name, display name,
+ // organization (company name).
+ var primaryEmail = doc.getElementById("PrimaryEmail");
+ if (primaryEmail.textLength == 0 &&
+ doc.getElementById("FirstName").textLength == 0 &&
+ doc.getElementById("LastName").textLength == 0 &&
+ doc.getElementById("DisplayName").textLength == 0 &&
+ doc.getElementById("Company").textLength == 0)
+ {
+ Services.prompt.alert(window,
+ gAddressBookBundle.getString("cardRequiredDataMissingTitle"),
+ gAddressBookBundle.getString("cardRequiredDataMissingMessage"));
+
+ return false;
+ }
+
+ // Simple checks that the primary email should be of the form |user@host|.
+ // Note: if the length of the primary email is 0 then we skip the check
+ // as some other field must have something as per the check above.
+ if (primaryEmail.textLength != 0 && !/.@./.test(primaryEmail.value))
+ {
+ Services.prompt.alert(window,
+ gAddressBookBundle.getString("incorrectEmailAddressFormatTitle"),
+ gAddressBookBundle.getString("incorrectEmailAddressFormatMessage"));
+
+ // Focus the dialog field, to help the user.
+ document.getElementById("abTabs").selectedIndex = 0;
+ primaryEmail.focus();
+
+ return false;
+ }
+
+ return true;
+}
+
+function GenerateDisplayName()
+{
+ if (!gEditCard.generateDisplayName)
+ return;
+
+ var displayName;
+
+ var firstNameValue = document.getElementById("FirstName").value;
+ var lastNameValue = document.getElementById("LastName").value;
+ if (lastNameValue && firstNameValue) {
+ displayName = (gEditCard.displayLastNameFirst)
+ ? gAddressBookBundle.getFormattedString("lastFirstFormat", [lastNameValue, firstNameValue])
+ : gAddressBookBundle.getFormattedString("firstLastFormat", [firstNameValue, lastNameValue]);
+ }
+ else {
+ // one (or both) of these is empty, so this works.
+ displayName = firstNameValue + lastNameValue;
+ }
+
+ document.getElementById("DisplayName").value = displayName;
+
+ SetCardDialogTitle(displayName);
+}
+
+function DisplayNameChanged()
+{
+ // turn off generateDisplayName if the user changes the display name
+ gEditCard.generateDisplayName = false;
+
+ SetCardDialogTitle(document.getElementById("DisplayName").value);
+}
+
+function SetCardDialogTitle(displayName)
+{
+ document.title = displayName
+ ? gAddressBookBundle.getFormattedString(gEditCard.titleProperty + "WithDisplayName", [displayName])
+ : gAddressBookBundle.getString(gEditCard.titleProperty);
+}
+
+/**
+ * Calculates the duration of time between an event and now and updates the year
+ * of whichever element did not call this function.
+ * @param aEvent The event calling this method.
+ * @param aElement Optional, but required if this function is not called from an
+ * element's event listener. The element that would call this.
+ */
+function calculateAge(aEvent, aElement) {
+ var datepicker, yearElem, ageElem;
+ if (aEvent)
+ aElement = this;
+ if (aElement.id == "BirthYear" || aElement.id == "Birthday") {
+ datepicker = document.getElementById("Birthday");
+ yearElem = document.getElementById("BirthYear");
+ ageElem = document.getElementById("Age");
+ }
+ if (!datepicker || !yearElem || !ageElem)
+ return;
+
+ // if the datepicker was updated, update the year element
+ if (aElement == datepicker && !(datepicker.year == kDefaultYear && !yearElem.value))
+ yearElem.value = datepicker.year;
+ var year = yearElem.value;
+ // if the year element's value is invalid set the year and age elements to null
+ if (isNaN(year) || year < kMinYear || year > kMaxYear) {
+ yearElem.value = null;
+ ageElem.value = null;
+ datepicker.year = kDefaultYear;
+ return;
+ }
+ else if (aElement == yearElem)
+ datepicker.year = year;
+ // calculate the length of time between the event and now
+ try {
+ var event = new Date(datepicker.year, datepicker.month, datepicker.date);
+ // if the year is only 2 digits, then the year won't be set correctly
+ // using setFullYear fixes this issue
+ event.setFullYear(datepicker.year);
+ // get the difference between today and the event
+ var age = new Date(new Date() - event);
+ // get the number of years of the difference and subtract 1970 (epoch)
+ ageElem.value = age.getFullYear() - 1970;
+ }
+ catch(e) {
+ datepicker.year = kDefaultYear;
+ // if there was an error (like invalid year) set the year and age to null
+ yearElem.value = null;
+ ageElem.value = null;
+ }
+}
+
+/**
+ * Calculates the year an event ocurred based on the number of years, months,
+ * and days since the event and updates the relevant element.
+ * @param aEvent The event calling this method.
+ * @param aElement Optional, but required if this function is not called from an
+ * element's event listener. The element that would call this.
+ */
+function calculateYear(aEvent, aElement) {
+ var yearElem, datepicker;
+ if (aEvent)
+ aElement = this;
+ if (aElement.id == "Age") {
+ datepicker = document.getElementById("Birthday");
+ yearElem = document.getElementById("BirthYear");
+ }
+ if (!datepicker || !yearElem)
+ return;
+
+ // if the age is null, remove the year from the year element, and set the
+ // datepicker to the default year
+ if (!aElement.value) {
+ datepicker.year = kDefaultYear;
+ yearElem.value = null;
+ return;
+ }
+ var today = new Date();
+ try {
+ var date = new Date(aElement.value, datepicker.month, datepicker.date);
+ date.setFullYear(aElement.value);
+ // get the difference between today and the age (the year is offset by 1970)
+ var difference = new Date(today - date);
+ datepicker.year = yearElem.value = difference.getFullYear() - 1970;
+ }
+ // the above code may throw an invalid year exception. If that happens, set
+ // the year to kDefaultYear and set the year element's value to 0
+ catch (e) {
+ datepicker.year = kDefaultYear;
+ // if there was an error (like invalid year) set the year and age to null
+ yearElem.value = null;
+ let ageElem = document.getElementById("Age");
+ if (ageElem)
+ ageElem.value = null;
+ }
+}
+
+/**
+ * Modifies a datepicker in the following ways:
+ * - Removes the scroll arrows
+ * - Hides the year
+ * - Allows the day and month to be blank
+ * NOTE:
+ * The datepicker's date, month, year, and dateValue properties are not always
+ * what appear physically to the user in the datepicker fields.
+ * If any field is blank, the corresponding property is either the previous
+ * value if there was one since the card was opened or the relevant portion of
+ * the current date.
+ *
+ * To get the displayed values, get the value of the individual field, such as
+ * datepicker.yyyyField.value where yyyy is "year", "month", or "date" for the
+ * year, month, and day, respectively.
+ * If the value is null, then the field is blank and vice versa.
+ * @param aDatepicker The datepicker to modify.
+ */
+function modifyDatepicker(aDatepicker) {
+ // collapse the year field and separator
+ aDatepicker.yearField.parentNode.collapsed = true;
+ if (aDatepicker.yearField == aDatepicker._fieldThree ||
+ aDatepicker.yearField == aDatepicker._fieldTwo)
+ aDatepicker._separatorSecond.collapsed = true;
+ else
+ aDatepicker._separatorFirst.collapsed = true;
+ // collapse the spinner element
+ document.getAnonymousElementByAttribute(aDatepicker, "anonid", "buttons")
+ .collapsed = true;
+ // this modified constrain value function ignores values less than the minimum
+ // to let the value be blank (null)
+ // from: mozilla/toolkit/content/widgets/datetimepicker.xml#759
+ aDatepicker._constrainValue = function newConstrainValue(aField, aValue, aNoWrap) {
+ // if the value is less than one, make the field's value null
+ if (aValue < 1) {
+ aField.value = null;
+ return null;
+ }
+ if (aNoWrap && aField == this.monthField)
+ aValue--;
+ // make sure the date is valid for the given month
+ if (aField == this.dateField) {
+ var currentMonth = this.month;
+ var dt = new Date(this.year, currentMonth, aValue);
+ return dt.getMonth() != currentMonth ? 1 : aValue;
+ }
+ var min = (aField == this.monthField) ? 0 : 1;
+ var max = (aField == this.monthField) ? 11 : kMaxYear;
+ // make sure the value isn't too high
+ if (aValue > max)
+ return aNoWrap ? max : min;
+ return aValue;
+ }
+ // sets the specified field to the given value, but allows blank fields
+ // from: mozilla/toolkit/content/widgets/datetimepicker.xml#698
+ aDatepicker._setFieldValue = function setValue(aField, aValue) {
+ if (aField == this.yearField && aValue >= kMinYear && aValue <= kMaxYear) {
+ var oldDate = this._dateValue;
+ this._dateValue.setFullYear(aValue);
+ if (oldDate != this._dateValue) {
+ this._dateValue.setDate(0);
+ this._updateUI(this.dateField, this.date);
+ }
+ }
+ // update the month if the value isn't null
+ else if (aField == this.monthField && aValue != null) {
+ var oldDate = this.date;
+ this._dateValue.setMonth(aValue);
+ if (oldDate != this.date)
+ this._dateValue.setDate(0);
+ this._updateUI(this.dateField, this.date);
+ var date = this._dateValue.getDate();
+ this.dateField.value = date < 10 && this.dateLeadingZero ? "0" + date : date;
+ var month = this._dateValue.getMonth() + 1;
+ this.monthField.value = month < 10 && this.monthLeadingZero ? "0" + month : month;
+ }
+ // update the date if the value isn't null
+ else if (aField == this.dateField && aValue != null) {
+ this._dateValue.setDate(aValue);
+ this._updateUI(this.dateField, this.date);
+ var date = this._dateValue.getDate();
+ this.dateField.value = date < 10 && this.dateLeadingZero ? "0" + date : date;
+ var month = this._dateValue.getMonth() + 1;
+ this.monthField.value = month < 10 && this.monthLeadingZero ? "0" + month : month;
+ }
+ this.setAttribute("value", this.value);
+
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ // if the aField's value is null or 0, set both field's values to null
+ if (!aField.value && aField != this.yearField) {
+ this.dateField.value = null;
+ this.monthField.value = null;
+ }
+ // make the field's value null if aValue is null and the field's value isn't
+ if (aValue == null && aField.value != null)
+ aField.value = null;
+ }
+}
+
+var chatNameFieldIds =
+ ["Yahoo", "Skype", "QQ", "MSN", "ICQ", "XMPP", "IRC"];
+
+/**
+ * Show the 'Chat' tab and focus the first field that has a value, or
+ * the first field if none of them has a value.
+ */
+function showChat()
+{
+ document.getElementById('abTabPanels').parentNode.selectedTab =
+ document.getElementById('chatTabButton');
+ for (let id of chatNameFieldIds) {
+ let elt = document.getElementById(id);
+ if (elt.value) {
+ elt.focus();
+ return;
+ }
+ }
+ document.getElementById(chatNameFieldIds[0]).focus();
+}
+
+/**
+ * Fill in the value of the ChatName readonly field with the first
+ * value of the fields in the Chat tab.
+ */
+function updateChatName()
+{
+ let value = "";
+ for (let id of chatNameFieldIds) {
+ let val = document.getElementById(id).value;
+ if (val) {
+ value = val;
+ break;
+ }
+ }
+ document.getElementById("ChatName").value = value;
+}
+
+/**
+ * Extract the photo information from an nsIAbCard, and populate
+ * the appropriate input fields in the contact editor. If the
+ * nsIAbCard returns an unrecognized PhotoType, the generic
+ * display photo is switched to.
+ *
+ * @param aCard The nsIAbCard to extract the information from.
+ *
+ */
+function loadPhoto(aCard) {
+ var type = aCard.getProperty("PhotoType", "");
+
+ if (!gPhotoHandlers[type] || !gPhotoHandlers[type].onLoad(aCard, document)) {
+ type = "generic";
+ gPhotoHandlers[type].onLoad(aCard, document);
+ }
+
+ document.getElementById("PhotoType").value = type;
+ gPhotoHandlers[type].onShow(aCard, document, "photo");
+}
+
+/**
+ * Event handler for when the user switches the type of
+ * photo for the nsIAbCard being edited. Tries to initiate a
+ * photo download.
+ *
+ * @param aPhotoType {string} The type to switch to
+ * @param aEvent {Event} The event object if used as an event handler
+ */
+function onSwitchPhotoType(aPhotoType, aEvent) {
+ if (!gEditCard)
+ return;
+
+ // Stop event propagation to the radiogroup command event in case that the
+ // child button is pressed. Otherwise, the download is started twice in a row.
+ if (aEvent) {
+ aEvent.stopPropagation();
+ }
+
+ if (aPhotoType) {
+ if (aPhotoType != document.getElementById("PhotoType").value) {
+ document.getElementById("PhotoType").value = aPhotoType;
+ }
+ } else {
+ aPhotoType = document.getElementById("PhotoType").value;
+ }
+
+ if (gPhotoHandlers[aPhotoType]) {
+ if (!gPhotoHandlers[aPhotoType].onRead(gEditCard.card, document)) {
+ onSwitchPhotoType("generic");
+ }
+ }
+}
+
+/**
+ * Removes the photo file at the given path, if present.
+ *
+ * @param aName The name of the photo to remove from the Photos directory.
+ * 'null' value is allowed and means to remove no file.
+ *
+ * @return {boolean} True if the file was deleted, false otherwise.
+ */
+function removePhoto(aName) {
+ if (!aName)
+ return false;
+ // Get the directory with all the photos
+ var file = getPhotosDir();
+ // Get the photo (throws an exception for invalid names)
+ try {
+ file.append(aName);
+ file.remove(false);
+ return true;
+ }
+ catch (e) {}
+ return false;
+}
+
+/**
+ * Remove previous and temporary photo files from the Photos directory.
+ *
+ * @param aSaved {boolean} Whether the new card is going to be saved/committed.
+ */
+function purgeOldPhotos(aSaved = true) {
+ // If photo was changed, the array contains at least one member, the original photo.
+ while (gOldPhotos.length > 0) {
+ let photoName = gOldPhotos.pop();
+ if (!aSaved && (gOldPhotos.length == 0)) {
+ // If the saving was cancelled, we want to keep the original photo of the card.
+ break;
+ }
+ removePhoto(photoName);
+ }
+
+ if (aSaved) {
+ // The new photo should stay so we clear the reference to it.
+ gNewPhoto = null;
+ } else {
+ // Changes to card not saved, we don't need the new photo.
+ // It may be null when there was no change of it.
+ removePhoto(gNewPhoto);
+ }
+}
+
+/**
+ * Opens a file picker with image filters to look for a contact photo.
+ * If the user selects a file and clicks OK then the PhotoURI textbox is set
+ * with a file URI pointing to that file and updatePhoto is called.
+ *
+ * @param aEvent {Event} The event object if used as an event handler.
+ */
+function browsePhoto(aEvent) {
+ // Stop event propagation to the radiogroup command event in case that the
+ // child button is pressed. Otherwise, the download is started twice in a row.
+ if (aEvent)
+ aEvent.stopPropagation();
+
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+ fp.init(window, gAddressBookBundle.getString("browsePhoto"),
+ Ci.nsIFilePicker.modeOpen);
+
+ // Open the directory of the currently chosen photo (if any)
+ let currentPhotoFile = document.getElementById("PhotoFile").file
+ if (currentPhotoFile) {
+ fp.displayDirectory = currentPhotoFile.parent;
+ }
+
+ // Add All Files & Image Files filters and select the latter
+ fp.appendFilters(Ci.nsIFilePicker.filterImages);
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+
+ fp.open(rv => {
+ if (rv != Ci.nsIFilePicker.returnOK) {
+ return;
+ }
+ document.getElementById("PhotoFile").file = fp.file;
+ onSwitchPhotoType("file");
+ });
+}
+
+/**
+ * Handlers to add drag and drop support.
+ */
+function checkDropPhoto(aEvent) {
+ // Just allow anything to be dropped. Different types of data are handled
+ // in doDropPhoto() below.
+ aEvent.preventDefault();
+}
+
+function doDropPhoto(aEvent) {
+ aEvent.preventDefault();
+
+ let photoType = "";
+
+ // Check if a file has been dropped.
+ let file = aEvent.dataTransfer.mozGetDataAt("application/x-moz-file", 0);
+ if (file instanceof Ci.nsIFile) {
+ photoType = "file";
+ document.getElementById("PhotoFile").file = file;
+ } else {
+ // Check if a URL has been dropped.
+ let link = aEvent.dataTransfer.getData("URL");
+ if (link) {
+ photoType = "web";
+ document.getElementById("PhotoURI").value = link;
+ } else {
+ // Check if dropped text is a URL.
+ link = aEvent.dataTransfer.getData("text/plain");
+ if (/^(ftps?|https?):\/\//i.test(link)) {
+ photoType = "web";
+ document.getElementById("PhotoURI").value = link;
+ }
+ }
+ }
+
+ onSwitchPhotoType(photoType);
+}
+
+/**
+ * Self-contained object to manage the user interface used for downloading
+ * and storing contact photo images.
+ */
+var gPhotoDownloadUI = (function() {
+ // UI DOM elements
+ let elProgressbar;
+ let elProgressLabel;
+ let elPhotoType;
+ let elProgressContainer;
+
+ window.addEventListener("load", function load(event) {
+ if (!elProgressbar)
+ elProgressbar = document.getElementById("PhotoDownloadProgress");
+ if (!elProgressLabel)
+ elProgressLabel = document.getElementById("PhotoStatus");
+ if (!elPhotoType)
+ elPhotoType = document.getElementById("PhotoType");
+ if (!elProgressContainer)
+ elProgressContainer = document.getElementById("ProgressContainer");
+ }, false);
+
+ function onStart() {
+ elProgressContainer.setAttribute("class", "expanded");
+ elProgressLabel.value = "";
+ elProgressbar.hidden = false;
+ elProgressbar.value = 3; // Start with a tiny visible progress
+ }
+
+ function onSuccess() {
+ elProgressLabel.value = "";
+ elProgressContainer.setAttribute("class", "");
+ }
+
+ function onError(state) {
+ let msg;
+ switch (state) {
+ case gImageDownloader.ERROR_INVALID_URI:
+ msg = gAddressBookBundle.getString("errorInvalidUri");
+ break;
+ case gImageDownloader.ERROR_UNAVAILABLE:
+ msg = gAddressBookBundle.getString("errorNotAvailable");
+ break;
+ case gImageDownloader.ERROR_INVALID_IMG:
+ msg = gAddressBookBundle.getString("errorInvalidImage");
+ break;
+ case gImageDownloader.ERROR_SAVE:
+ msg = gAddressBookBundle.getString("errorSaveOperation");
+ break;
+ }
+ if (msg) {
+ elProgressLabel.value = msg;
+ elProgressbar.hidden = true;
+ onSwitchPhotoType("generic");
+ }
+ }
+
+ function onProgress(state, percent) {
+ elProgressbar.value = percent;
+ elProgressLabel.value = gAddressBookBundle.getString("stateImageSave");
+ }
+
+ return {
+ onStart: onStart,
+ onSuccess: onSuccess,
+ onError: onError,
+ onProgress: onProgress
+ }
+})();
+
+/* A photo handler defines the behaviour of the contact editor
+ * for a particular photo type. Each photo handler must implement
+ * the following interface:
+ *
+ * onLoad: function(aCard, aDocument):
+ * Called when the editor wants to populate the contact editor
+ * input fields with information about aCard's photo. Note that
+ * this does NOT make aCard's photo appear in the contact editor -
+ * this is left to the onShow function. Returns true on success.
+ * If the function returns false, the generic photo handler onLoad
+ * function will be called.
+ *
+ * onShow: function(aCard, aDocument, aTargetID):
+ * Called when the editor wants to show this photo type.
+ * The onShow method should take the input fields in the document,
+ * and render the requested photo in the IMG tag with id
+ * aTargetID. Note that onShow does NOT save the photo for aCard -
+ * this job is left to the onSave function. Returns true on success.
+ * If the function returns false, the generic photo handler onShow
+ * function will be called.
+ *
+ * onRead: function(aCard, aDocument)
+ * Called when the editor wants to read the user supplied new photo.
+ * The onRead method is responsible for analyzing the photo of this
+ * type requested by the user, and storing it, as well as the
+ * other fields required by onLoad/onShow to retrieve and display
+ * the photo again. Returns true on success. If the function
+ * returns false, the generic photo handler onRead function will
+ * be called.
+ *
+ * onSave: function(aCard, aDocument)
+ * Called when the editor wants to save this photo type to the card.
+ * Returns true on success.
+ */
+
+var gGenericPhotoHandler = {
+ onLoad: function(aCard, aDocument) {
+ return true;
+ },
+
+ onShow: function(aCard, aDocument, aTargetID) {
+ // XXX TODO: this ignores any other value from the generic photos
+ // menulist than "default".
+ aDocument.getElementById(aTargetID)
+ .setAttribute("src", defaultPhotoURI);
+ return true;
+ },
+
+ onRead: function(aCard, aDocument) {
+ gPhotoDownloadUI.onSuccess();
+
+ newPhotoAdded("", aCard);
+
+ gGenericPhotoHandler.onShow(aCard, aDocument, "photo");
+ return true;
+ },
+
+ onSave: function(aCard, aDocument) {
+ // XXX TODO: this ignores any other value from the generic photos
+ // menulist than "default".
+
+ // Update contact
+ aCard.setProperty("PhotoName", "");
+ aCard.setProperty("PhotoURI", "");
+ return true;
+ }
+};
+
+var gFilePhotoHandler = {
+
+ onLoad: function(aCard, aDocument) {
+ let photoURI = aCard.getProperty("PhotoURI", "");
+ let file;
+ try {
+ // The original file may not exist anymore, but we still display it.
+ file = Services.io.newURI(photoURI)
+ .QueryInterface(Ci.nsIFileURL)
+ .file;
+ } catch (e) {}
+
+ if (!file)
+ return false;
+
+ aDocument.getElementById("PhotoFile").file = file;
+ return true;
+ },
+
+ onShow: function(aCard, aDocument, aTargetID) {
+ let photoName = gNewPhoto || aCard.getProperty("PhotoName", null);
+ let photoURI = getPhotoURI(photoName);
+ aDocument.getElementById(aTargetID).setAttribute("src", photoURI);
+ return true;
+ },
+
+ onRead: function(aCard, aDocument) {
+ let file = aDocument.getElementById("PhotoFile").file;
+ if (!file)
+ return false;
+
+ // If the local file has been removed/renamed, keep the current photo as is.
+ if (!file.exists() || !file.isFile())
+ return false;
+
+ let photoURI = Services.io.newFileURI(file).spec;
+
+ gPhotoDownloadUI.onStart();
+
+ let cbSuccess = function(newPhotoName) {
+ gPhotoDownloadUI.onSuccess();
+
+ newPhotoAdded(newPhotoName, aCard);
+ aDocument.getElementById("PhotoFile").setAttribute("PhotoURI", photoURI);
+
+ gFilePhotoHandler.onShow(aCard, aDocument, "photo");
+ };
+
+ gImageDownloader.savePhoto(photoURI, cbSuccess,
+ gPhotoDownloadUI.onError,
+ gPhotoDownloadUI.onProgress);
+ return true;
+ },
+
+ onSave: function(aCard, aDocument) {
+ // Update contact
+ if (gNewPhoto) {
+ // The file may not be valid unless the photo has changed.
+ let photoURI = aDocument.getElementById("PhotoFile").getAttribute("PhotoURI");
+ aCard.setProperty("PhotoName", gNewPhoto);
+ aCard.setProperty("PhotoURI", photoURI);
+ }
+ return true;
+ }
+};
+
+var gWebPhotoHandler = {
+ onLoad: function(aCard, aDocument) {
+ let photoURI = aCard.getProperty("PhotoURI", null);
+
+ if (!photoURI)
+ return false;
+
+ aDocument.getElementById("PhotoURI").value = photoURI;
+ return true;
+ },
+
+ onShow: function(aCard, aDocument, aTargetID) {
+ let photoName = gNewPhoto || aCard.getProperty("PhotoName", null);
+ if (!photoName)
+ return false;
+
+ let photoURI = getPhotoURI(photoName);
+
+ aDocument.getElementById(aTargetID).setAttribute("src", photoURI);
+ return true;
+ },
+
+ onRead: function(aCard, aDocument) {
+ let photoURI = aDocument.getElementById("PhotoURI").value;
+ if (!photoURI)
+ return false;
+
+ gPhotoDownloadUI.onStart();
+
+ let cbSuccess = function(newPhotoName) {
+ gPhotoDownloadUI.onSuccess();
+
+ newPhotoAdded(newPhotoName, aCard);
+
+ gWebPhotoHandler.onShow(aCard, aDocument, "photo");
+
+ }
+
+ gImageDownloader.savePhoto(photoURI, cbSuccess,
+ gPhotoDownloadUI.onError,
+ gPhotoDownloadUI.onProgress);
+ return true;
+ },
+
+ onSave: function(aCard, aDocument) {
+ // Update contact
+ if (gNewPhoto) {
+ let photoURI = aDocument.getElementById("PhotoURI").value;
+ aCard.setProperty("PhotoName", gNewPhoto);
+ aCard.setProperty("PhotoURI", photoURI);
+ }
+ return true;
+ }
+};
+
+function newPhotoAdded(aPhotoName, aCard) {
+ // If we had the photo saved locally, shedule it for removal if card is saved.
+ gOldPhotos.push(gNewPhoto !== null ? gNewPhoto : aCard.getProperty("PhotoName", null));
+ gNewPhoto = aPhotoName;
+}
+
+/* In order for other photo handlers to be recognized for
+ * a particular type, they must be registered through this
+ * function.
+ * @param aType the type of photo to handle
+ * @param aPhotoHandler the photo handler to register
+ */
+function registerPhotoHandler(aType, aPhotoHandler)
+{
+ gPhotoHandlers[aType] = aPhotoHandler;
+}
+
+registerPhotoHandler("generic", gGenericPhotoHandler);
+registerPhotoHandler("web", gWebPhotoHandler);
+registerPhotoHandler("file", gFilePhotoHandler);
diff --git a/comm/suite/mailnews/components/addrbook/content/abCardOverlay.xul b/comm/suite/mailnews/components/addrbook/content/abCardOverlay.xul
new file mode 100644
index 0000000000..40287e8411
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abCardOverlay.xul
@@ -0,0 +1,515 @@
+<?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://messenger/skin/addressbook/cardDialog.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/abCardOverlay.dtd">
+
+<overlay id="editcardOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+</stringbundleset>
+
+<script src="chrome://messenger/content/addressbook/abCommon.js"/>
+<script src="chrome://messenger/content/addressbook/abCardOverlay.js"/>
+
+<vbox id="editcard">
+ <tabbox>
+ <tabs id="abTabs">
+ <tab id="contactTabButton" label="&Contact.tab;"
+ accesskey="&Contact.accesskey;"/>
+ <tab id="homeTabButton" label="&Home.tab;" accesskey="&Home.accesskey;"/>
+ <tab id="workTabButton" label="&Work.tab;" accesskey="&Work.accesskey;"/>
+ <tab id="otherTabButton" label="&Other.tab;" accesskey="&Other.accesskey;"/>
+ <tab id="chatTabButton" label="&Chat.tab;" accesskey="&Chat.accesskey;"/>
+ <tab id="photoTabButton" label="&Photo.tab;" accesskey="&Photo.accesskey;"/>
+ </tabs>
+
+ <tabpanels id="abTabPanels" flex="1">
+ <!-- ** Name Tab ** -->
+ <!-- The following vbox contains two hboxes
+ top: Name/Email/Phonenumber bottom: Email prefs. -->
+ <vbox id="abNameTab" >
+ <!-- This hbox contains two vboxes
+ left: Name/Email, right: Phonenumbers -->
+ <hbox>
+ <vbox id="namesAndEmailAddresses">
+ <!-- This box contains the Names and Emailnames -->
+
+ <!-- LOCALIZATION NOTE:
+ NameField1, NameField2, PhoneticField1, PhoneticField2
+ those fields are either LN or FN depends on the target country.
+ They are configurable in the .dtd file.
+ -->
+
+ <hbox id="NameField1Container" align="center">
+ <spacer flex="1"/>
+ <label control="&NameField1.id;" value="&NameField1.label;"
+ accesskey="&NameField1.accesskey;"/>
+ <hbox class="CardEditWidth" align="center">
+ <textbox id="&NameField1.id;" flex="1"
+ oninput="GenerateDisplayName()"/>
+
+ <!-- LOCALIZATION NOTE:
+ Fields for phonetic are disabled as default and can be
+ enabled by^editing "mail.addr_book.show_phonetic_fields"
+ -->
+
+ <spacer id="PhoneticSpacer1" flex="1" hidden="true"/>
+ <label id="PhoneticLabel1" control="&PhoneticField1.id;"
+ value="&PhoneticField1.label;" hidden="true"/>
+ <textbox id="&PhoneticField1.id;" flex="1" hidden="true"/>
+ </hbox>
+ </hbox>
+ <hbox id="NameField2Container" align="center">
+ <spacer flex="1"/>
+ <label control="&NameField2.id;" value="&NameField2.label;"
+ accesskey="&NameField2.accesskey;"/>
+ <hbox class="CardEditWidth" align="center">
+ <textbox id="&NameField2.id;" flex="1"
+ oninput="GenerateDisplayName()"/>
+
+ <!-- LOCALIZATION NOTE:
+ Fields for phonetic are disabled as default and can be
+ enabled by editing "mail.addr_book.show_phonetic_fields"
+ -->
+
+ <spacer id="PhoneticSpacer2" flex="1" hidden="true"/>
+ <label id="PhoneticLabel2" control="&PhoneticField2.id;"
+ value="&PhoneticField2.label;" hidden="true"/>
+ <textbox id="&PhoneticField2.id;" flex="1" hidden="true"/>
+ </hbox>
+ </hbox>
+ <hbox id="DisplayNameContainer" align="center">
+ <spacer flex="1"/>
+ <label control="DisplayName" value="&DisplayName.label;"
+ accesskey="&DisplayName.accesskey;" />
+ <hbox class="CardEditWidth">
+ <textbox id="DisplayName" flex="1"
+ oninput="DisplayNameChanged()"/>
+ </hbox>
+ </hbox>
+ <hbox id="PreferDisplayNameContainer" align="center">
+ <spacer flex="1"/>
+ <hbox class="CardEditWidth">
+ <checkbox id="preferDisplayName"
+ label="&preferDisplayName.label;"
+ accesskey="&preferDisplayName2.accesskey;"/>
+ </hbox>
+ </hbox>
+
+ <hbox id="NickNameContainer" align="center">
+ <spacer flex="1"/>
+ <label control="NickName" value="&NickName.label;"
+ accesskey="&NickName.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="NickName" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="PrimaryEmailContainer" align="center">
+ <spacer flex="1"/>
+ <label control="PrimaryEmail" value="&PrimaryEmail.label;"
+ accesskey="&PrimaryEmail.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="PrimaryEmail" flex="1" class="uri-element"/>
+ </hbox>
+ </hbox>
+ <hbox id="SecondaryEmailContainer" align="center">
+ <spacer flex="1"/>
+ <label control="SecondEmail" value="&SecondEmail.label;"
+ accesskey="&SecondEmail.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="SecondEmail" flex="1" class="uri-element"/>
+ </hbox>
+ </hbox>
+ <hbox id="ScreenNameContainer" align="center">
+ <spacer flex="1"/>
+ <label class="text-link" value="&chatName.label;"
+ onclick="showChat();"/>
+ <hbox class="CardEditWidth">
+ <textbox id="ChatName" readonly="true" flex="1"
+ onclick="showChat();"/>
+ </hbox>
+ </hbox>
+ </vbox> <!-- End of Name/Email -->
+ <!-- Phone Number section -->
+ <vbox id="PhoneNumbers">
+ <hbox id="WorkPhoneContainer" align="center">
+ <spacer flex="1"/>
+ <label control="WorkPhone" value="&WorkPhone.label;"
+ accesskey="&WorkPhone.accesskey;" />
+ <textbox id="WorkPhone" class="PhoneEditWidth"/>
+ </hbox>
+ <hbox id="HomePhoneContainer" align="center">
+ <spacer flex="1"/>
+ <label control="HomePhone" value="&HomePhone.label;"
+ accesskey="&HomePhone.accesskey;"/>
+ <textbox id="HomePhone" class="PhoneEditWidth"/>
+ </hbox>
+ <hbox id="FaxNumberContainer" align="center">
+ <spacer flex="1"/>
+ <label control="FaxNumber" value="&FaxNumber.label;"
+ accesskey="&FaxNumber.accesskey;"/>
+ <textbox id="FaxNumber" class="PhoneEditWidth"/>
+ </hbox>
+ <hbox id="PagerNumberContainer" align="center">
+ <spacer flex="1"/>
+ <label control="PagerNumber" value="&PagerNumber.label;"
+ accesskey="&PagerNumber.accesskey;"/>
+ <textbox id="PagerNumber" class="PhoneEditWidth"/>
+ </hbox>
+ <hbox id="CellularNumberContainer" align="center">
+ <spacer flex="1"/>
+ <label control="CellularNumber" value="&CellularNumber.label;"
+ accesskey="&CellularNumber.accesskey;"/>
+ <textbox id="CellularNumber" class="PhoneEditWidth"/>
+ </hbox>
+ </vbox> <!-- End of Phonenumbers -->
+ </hbox> <!-- End of Name/Email/Phonenumbers -->
+ <!-- Email Preferences -->
+ <hbox>
+ <vbox valign="middle">
+ <label control="PreferMailFormatPopup"
+ value="&PreferMailFormat.label;"
+ accesskey="&PreferMailFormat.accesskey;"/>
+ </vbox>
+ <menulist id="PreferMailFormatPopup">
+ <menupopup>
+ <!-- 0,1,2 come from nsIAbPreferMailFormat in nsIAbCard.idl -->
+ <menuitem value="0" label="&Unknown.label;"/>
+ <menuitem value="1" label="&PlainText.label;"/>
+ <menuitem value="2" label="&HTML.label;"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </vbox> <!-- End of Name Tab -->
+
+ <!-- ** Home Address Tab ** -->
+ <vbox id="abHomeTab" >
+ <hbox align="center">
+ <spacer flex="1"/>
+ <label control="HomeAddress" value="&HomeAddress.label;"
+ accesskey="&HomeAddress.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="HomeAddress" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox align="center">
+ <spacer flex="1"/>
+ <label control="HomeAddress2" value="&HomeAddress2.label;"
+ accesskey="&HomeAddress2.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="HomeAddress2" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="HomeCityContainer" align="center">
+ <spacer flex="1"/>
+ <label control="HomeCity" value="&HomeCity.label;"
+ accesskey="&HomeCity.accesskey;"/>
+ <hbox id="HomeCityFieldContainer"
+ class="AddressCardEditWidth"
+ align="center">
+ <textbox id="HomeCity" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox align="center">
+ <spacer flex="1"/>
+ <label control="HomeState" value="&HomeState.label;"
+ accesskey="&HomeState.accesskey;"/>
+ <hbox align="center" class="AddressCardEditWidth">
+ <textbox id="HomeState" flex="1"/>
+ <spacer class="stateZipSpacer"/>
+ <label control="HomeZipCode" value="&HomeZipCode.label;"
+ accesskey="&HomeZipCode.accesskey;"/>
+ <textbox id="HomeZipCode" class="ZipWidth"/>
+ </hbox>
+ </hbox>
+ <hbox align="center">
+ <spacer flex="1"/>
+ <label control="HomeCountry" value="&HomeCountry.label;"
+ accesskey="&HomeCountry.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="HomeCountry" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="WebPage2Container" align="center">
+ <spacer flex="1"/>
+ <label control="WebPage2" value="&HomeWebPage.label;"
+ accesskey="&HomeWebPage.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="WebPage2" flex="1" class="uri-element"/>
+ </hbox>
+ </hbox>
+ <hbox id="birthdayField" align="center">
+ <spacer flex="1"/>
+ <label control="Birthday" value="&Birthday.label;"
+ accesskey="&Birthday.accesskey;"/>
+ <hbox class="AddressCardEditWidth" align="center">
+ <!-- NOTE: This datepicker is modified.
+ See abCardOverlay.js for details-->
+ <datepicker id="Birthday" type="popup"/>
+ <label value="&In.label;"/>
+ <textbox id="BirthYear" maxlength="4"
+ placeholder="&Year.placeholder;" class="YearWidth" />
+ <label control="Age" value="&Or.value;"/>
+ <textbox id="Age" maxlength="4"
+ placeholder="&Age.placeholder;" class="YearWidth" />
+ <label value="&YearsOld.label;"/>
+ <spacer flex="1"/>
+ </hbox>
+ </hbox>
+ </vbox>
+
+ <!-- ** Business Address Tab ** -->
+ <vbox id="abBusinessTab" >
+ <hbox id="JobTitleDepartmentContainer" align="center">
+ <spacer flex="1"/>
+ <label control="JobTitle" value="&JobTitle.label;"
+ accesskey="&JobTitle.accesskey;"/>
+ <hbox class="AddressCardEditWidth" align="center">
+ <textbox id="JobTitle" flex="1"/>
+ <label control="Department" value="&Department.label;"
+ accesskey="&Department.accesskey;"/>
+ <textbox id="Department" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="CompanyContainer" align="center">
+ <spacer flex="1"/>
+ <label control="Company" value="&Company.label;"
+ accesskey="&Company.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="Company" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="WorkAddressContainer" align="center">
+ <spacer flex="1"/>
+ <label control="WorkAddress" value="&WorkAddress.label;"
+ accesskey="&WorkAddress.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="WorkAddress" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="WorkAddress2Container" align="center">
+ <spacer flex="1"/>
+ <label control="WorkAddress2" value="&WorkAddress2.label;"
+ accesskey="&WorkAddress2.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="WorkAddress2" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="WorkCityContainer" align="center">
+ <spacer flex="1"/>
+ <label control="WorkCity" value="&WorkCity.label;"
+ accesskey="&WorkCity.accesskey;"/>
+ <hbox id="WorkCityFieldContainer"
+ class="AddressCardEditWidth"
+ align="center">
+ <textbox id="WorkCity" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="WorkStateZipContainer" align="center">
+ <spacer flex="1"/>
+ <label control="WorkState" value="&WorkState.label;"
+ accesskey="&WorkState.accesskey;"/>
+ <hbox class="AddressCardEditWidth" align="center">
+ <textbox id="WorkState" flex="1"/>
+ <spacer class="stateZipSpacer"/>
+ <label control="WorkZipCode" value="&WorkZipCode.label;"
+ accesskey="&WorkZipCode.accesskey;"/>
+ <textbox id="WorkZipCode" class="ZipWidth"/>
+ </hbox>
+ </hbox>
+ <hbox id="WorkCountryContainer" align="center">
+ <spacer flex="1"/>
+ <label control="WorkCountry" value="&WorkCountry.label;"
+ accesskey="&WorkCountry.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="WorkCountry" flex="1"/>
+ </hbox>
+ </hbox>
+ <hbox id="WebPage1Container" align="center">
+ <spacer flex="1"/>
+ <label control="WebPage1" value="&WorkWebPage.label;"
+ accesskey="&WorkWebPage.accesskey;"/>
+ <hbox class="AddressCardEditWidth">
+ <textbox id="WebPage1" flex="1" class="uri-element"/>
+ </hbox>
+ </hbox>
+ </vbox>
+
+ <!-- ** Other Tab ** -->
+ <vbox id="abOtherTab" >
+ <vbox id="customFields">
+ <hbox flex="1" align="center">
+ <label control="Custom1" value="&Custom1.label;"
+ accesskey="&Custom1.accesskey;"/>
+ <textbox id="Custom1" flex="1"/>
+ </hbox>
+ <hbox flex="1" align="center">
+ <label control="Custom2" value="&Custom2.label;"
+ accesskey="&Custom2.accesskey;"/>
+ <textbox id="Custom2" flex="1"/>
+ </hbox>
+ <hbox flex="1" align="center">
+ <label control="Custom3" value="&Custom3.label;"
+ accesskey="&Custom3.accesskey;"/>
+ <textbox id="Custom3" flex="1"/>
+ </hbox>
+ <hbox flex="1" align="center">
+ <label control="Custom4" value="&Custom4.label;"
+ accesskey="&Custom4.accesskey;"/>
+ <textbox id="Custom4" flex="1"/>
+ </hbox>
+ </vbox>
+ <label control="Notes" value="&Notes.label;"
+ accesskey="&Notes.accesskey;"/>
+ <textbox id="Notes" multiline="true" wrap="virtual" flex="1"/>
+ </vbox>
+
+ <!-- ** Chat Tab ** -->
+ <hbox id="abChatTab">
+ <vbox>
+ <hbox id="YahooContainer" align="center">
+ <spacer flex="1"/>
+ <label control="Yahoo" value="&Yahoo.label;"
+ accesskey="&Yahoo.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="Yahoo" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ <hbox id="SkypeContainer" align="center">
+ <spacer flex="1"/>
+ <label control="Skype" value="&Skype.label;"
+ accesskey="&Skype.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="Skype" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ <hbox id="QQContainer" align="center">
+ <spacer flex="1"/>
+ <label control="QQ" value="&QQ.label;"
+ accesskey="&QQ.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="QQ" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ <hbox id="MSNContainer" align="center">
+ <spacer flex="1"/>
+ <label control="MSN" value="&MSN.label;"
+ accesskey="&MSN.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="MSN" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ <hbox id="ICQContainer" align="center">
+ <spacer flex="1"/>
+ <label control="ICQ" value="&ICQ.label;"
+ accesskey="&ICQ.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="ICQ" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ <hbox id="XMPPContainer" align="center">
+ <spacer flex="1"/>
+ <label control="XMPP" value="&XMPP.label;"
+ accesskey="&XMPP.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="XMPP" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ <hbox id="IRCContainer" align="center">
+ <spacer flex="1"/>
+ <label control="IRC" value="&IRC.label;"
+ accesskey="&IRC.accesskey;"/>
+ <hbox class="CardEditWidth">
+ <textbox id="IRC" flex="1" onchange="updateChatName();"/>
+ </hbox>
+ </hbox>
+ </vbox>
+ </hbox>
+
+ <!-- ** Photo Tab ** -->
+ <hbox id="abPhotoTab">
+ <vbox align="center" id="PhotoContainer"
+ style="height: 25ch; width: 25ch;"
+ ondrop="doDropPhoto(event);"
+ ondragenter="checkDropPhoto(event);"
+ ondragover="checkDropPhoto(event);">
+ <image id="photo" style="max-height: 25ch; max-width: 25ch;"/>
+ <hbox id="PhotoDropTarget" flex="1" pack="center" align="center">
+ <description>&PhotoDropTarget.label;</description>
+ </hbox>
+ </vbox>
+
+ <vbox flex="1">
+ <radiogroup id="PhotoType" oncommand="onSwitchPhotoType();">
+ <vbox id="GenericPhotoContainer">
+ <radio id="GenericPhotoType"
+ value="generic"
+ label="&GenericPhoto.label;"
+ accesskey="&GenericPhoto.accesskey;"/>
+ <menulist id="GenericPhotoList"
+ class="indent"
+ flex="1"
+ oncommand="onSwitchPhotoType('generic', event);">
+ <menupopup>
+ <menuitem label="&DefaultPhoto.label;"
+ selected="true"
+ value="default"/>
+ </menupopup>
+ </menulist>
+ </vbox>
+
+ <vbox id="FilePhotoContainer">
+ <radio id="FilePhotoType"
+ value="file"
+ label="&PhotoFile.label;"
+ accesskey="&PhotoFile.accesskey;"/>
+ <hbox class="indent">
+ <filefield id="PhotoFile"
+ readonly="true"
+ maxlength="255"
+ flex="1"/>
+ <button id="BrowsePhoto"
+ label="&BrowsePhoto.label;"
+ accesskey="&BrowsePhoto.accesskey;"
+ oncommand="browsePhoto(event);"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="WebPhotoContainer">
+ <radio id="WebPhotoType"
+ value="web"
+ label="&PhotoURL.label;"
+ accesskey="&PhotoURL.accesskey;"/>
+ <hbox class="indent">
+ <textbox id="PhotoURI"
+ maxlength="255"
+ flex="1"
+ placeholder="&PhotoURL.placeholder;"/>
+ <button id="UpdatePhoto"
+ label="&UpdatePhoto.label;"
+ accesskey="&UpdatePhoto.accesskey;"
+ oncommand="onSwitchPhotoType('web', event);"/>
+ </hbox>
+ </vbox>
+ </radiogroup>
+ <hbox id="ProgressContainer" align="begin">
+ <label id="PhotoStatus"/>
+ <spacer flex="2"/>
+ <progressmeter id="PhotoDownloadProgress"
+ value="0"
+ mode="determined"
+ hidden="true"
+ flex="1"/>
+ </hbox>
+ </vbox>
+ </hbox>
+ </tabpanels>
+ </tabbox>
+</vbox>
+</overlay>
diff --git a/comm/suite/mailnews/components/addrbook/content/abCardViewOverlay.js b/comm/suite/mailnews/components/addrbook/content/abCardViewOverlay.js
new file mode 100644
index 0000000000..e9edfbfbe4
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abCardViewOverlay.js
@@ -0,0 +1,527 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//NOTE: gAddressBookBundle must be defined and set or this Overlay won't work
+
+var gMapItURLFormat;
+
+var gPhotoDisplayHandlers = {};
+
+var zListName;
+var zPrimaryEmail;
+var zSecondaryEmail;
+var zNickname;
+var zDisplayName;
+var zWork;
+var zHome;
+var zFax;
+var zCellular;
+var zPager;
+var zBirthday;
+var zCustom1;
+var zCustom2;
+var zCustom3;
+var zCustom4;
+var zYahoo;
+var zSkype;
+var zQQ;
+var zMSN;
+var zICQ;
+var zXMPP;
+var zIRC;
+
+var cvData;
+
+function OnLoadCardView()
+{
+ gMapItURLFormat = GetLocalizedStringPref("mail.addr_book.mapit_url.format");
+
+ zPrimaryEmail = gAddressBookBundle.getString("propertyPrimaryEmail");
+ zSecondaryEmail = gAddressBookBundle.getString("propertySecondaryEmail");
+ zNickname = gAddressBookBundle.getString("propertyNickname");
+ zDisplayName = gAddressBookBundle.getString("propertyDisplayName");
+ zListName = gAddressBookBundle.getString("propertyListName");
+ zWork = gAddressBookBundle.getString("propertyWork");
+ zHome = gAddressBookBundle.getString("propertyHome");
+ zFax = gAddressBookBundle.getString("propertyFax");
+ zCellular = gAddressBookBundle.getString("propertyCellular");
+ zPager = gAddressBookBundle.getString("propertyPager");
+ zBirthday = gAddressBookBundle.getString("propertyBirthday");
+ zCustom1 = gAddressBookBundle.getString("propertyCustom1");
+ zCustom2 = gAddressBookBundle.getString("propertyCustom2");
+ zCustom3 = gAddressBookBundle.getString("propertyCustom3");
+ zCustom4 = gAddressBookBundle.getString("propertyCustom4");
+ zYahoo = gAddressBookBundle.getString("propertyYahoo");
+ zSkype = gAddressBookBundle.getString("propertySkype");
+ zQQ = gAddressBookBundle.getString("propertyQQ");
+ zMSN = gAddressBookBundle.getString("propertyMSN");
+ zICQ = gAddressBookBundle.getString("propertyICQ");
+ zXMPP = gAddressBookBundle.getString("propertyXMPP");
+ zIRC = gAddressBookBundle.getString("propertyIRC");
+
+ var doc = document;
+
+ /* data for address book, prefixes: "cvb" = card view box
+ "cvh" = crad view header
+ "cv" = card view (normal fields) */
+ cvData = new Object;
+
+ // Card View Box
+ cvData.CardViewBox = doc.getElementById("CardViewInnerBox");
+ // Title
+ cvData.CardTitle = doc.getElementById("CardTitle");
+ // Name section
+ cvData.cvbContact = doc.getElementById("cvbContact");
+ cvData.cvhContact = doc.getElementById("cvhContact");
+ cvData.cvNickname = doc.getElementById("cvNickname");
+ cvData.cvDisplayName = doc.getElementById("cvDisplayName");
+ cvData.cvEmail1Box = doc.getElementById("cvEmail1Box");
+ cvData.cvEmail1 = doc.getElementById("cvEmail1");
+ cvData.cvBuddyIcon = doc.getElementById("cvBuddyIcon");
+ cvData.cvListNameBox = doc.getElementById("cvListNameBox");
+ cvData.cvListName = doc.getElementById("cvListName");
+ cvData.cvEmail2Box = doc.getElementById("cvEmail2Box");
+ cvData.cvEmail2 = doc.getElementById("cvEmail2");
+ // Home section
+ cvData.cvbHome = doc.getElementById("cvbHome");
+ cvData.cvhHome = doc.getElementById("cvhHome");
+ cvData.cvHomeAddress = doc.getElementById("cvHomeAddress");
+ cvData.cvHomeAddress2 = doc.getElementById("cvHomeAddress2");
+ cvData.cvHomeCityStZip = doc.getElementById("cvHomeCityStZip");
+ cvData.cvHomeCountry = doc.getElementById("cvHomeCountry");
+ cvData.cvbHomeMapItBox = doc.getElementById("cvbHomeMapItBox");
+ cvData.cvHomeMapIt = doc.getElementById("cvHomeMapIt");
+ cvData.cvHomeWebPageBox = doc.getElementById("cvHomeWebPageBox");
+ cvData.cvHomeWebPage = doc.getElementById("cvHomeWebPage");
+ // Other section
+ cvData.cvbOther = doc.getElementById("cvbOther");
+ cvData.cvBirthday = doc.getElementById("cvBirthday");
+ cvData.cvhOther = doc.getElementById("cvhOther");
+ cvData.cvCustom1 = doc.getElementById("cvCustom1");
+ cvData.cvCustom2 = doc.getElementById("cvCustom2");
+ cvData.cvCustom3 = doc.getElementById("cvCustom3");
+ cvData.cvCustom4 = doc.getElementById("cvCustom4");
+ cvData.cvNotes = doc.getElementById("cvNotes");
+ // Description section (mailing lists only)
+ cvData.cvbDescription = doc.getElementById("cvbDescription");
+ cvData.cvhDescription = doc.getElementById("cvhDescription");
+ cvData.cvDescription = doc.getElementById("cvDescription");
+ // Addresses section (mailing lists only)
+ cvData.cvbAddresses = doc.getElementById("cvbAddresses");
+ cvData.cvhAddresses = doc.getElementById("cvhAddresses");
+ cvData.cvAddresses = doc.getElementById("cvAddresses");
+ // Phone section
+ cvData.cvbPhone = doc.getElementById("cvbPhone");
+ cvData.cvhPhone = doc.getElementById("cvhPhone");
+ cvData.cvPhWork = doc.getElementById("cvPhWork");
+ cvData.cvPhHome = doc.getElementById("cvPhHome");
+ cvData.cvPhFax = doc.getElementById("cvPhFax");
+ cvData.cvPhCellular = doc.getElementById("cvPhCellular");
+ cvData.cvPhPager = doc.getElementById("cvPhPager");
+ // Work section
+ cvData.cvbWork = doc.getElementById("cvbWork");
+ cvData.cvhWork = doc.getElementById("cvhWork");
+ cvData.cvJobTitle = doc.getElementById("cvJobTitle");
+ cvData.cvDepartment = doc.getElementById("cvDepartment");
+ cvData.cvCompany = doc.getElementById("cvCompany");
+ cvData.cvWorkAddress = doc.getElementById("cvWorkAddress");
+ cvData.cvWorkAddress2 = doc.getElementById("cvWorkAddress2");
+ cvData.cvWorkCityStZip = doc.getElementById("cvWorkCityStZip");
+ cvData.cvWorkCountry = doc.getElementById("cvWorkCountry");
+ cvData.cvbWorkMapItBox = doc.getElementById("cvbWorkMapItBox");
+ cvData.cvWorkMapIt = doc.getElementById("cvWorkMapIt");
+ cvData.cvWorkWebPageBox = doc.getElementById("cvWorkWebPageBox");
+ cvData.cvWorkWebPage = doc.getElementById("cvWorkWebPage");
+ cvData.cvbPhoto = doc.getElementById("cvbPhoto");
+ cvData.cvPhoto = doc.getElementById("cvPhoto");
+ // Chat section
+ cvData.cvbChat = doc.getElementById("cvbChat");
+ cvData.cvhChat = doc.getElementById("cvhChat");
+ cvData.cvYahoo = doc.getElementById("cvYahoo");
+ cvData.cvSkype = doc.getElementById("cvSkype");
+ cvData.cvQQ = doc.getElementById("cvQQ");
+ cvData.cvMSN = doc.getElementById("cvMSN");
+ cvData.cvICQ = doc.getElementById("cvICQ");
+ cvData.cvXMPP = doc.getElementById("cvXMPP");
+ cvData.cvIRC = doc.getElementById("cvIRC");
+}
+
+// XXX todo
+// some similar code (in spirit) already exists, see OnLoadEditList()
+// perhaps we could combine and put in abCommon.js?
+function GetAddressesFromURI(uri)
+{
+ var addresses = "";
+
+ var editList = GetDirectoryFromURI(uri);
+ var addressList = editList.addressLists;
+ if (addressList) {
+ var total = addressList.length;
+ if (total > 0)
+ addresses = addressList.queryElementAt(0, Ci.nsIAbCard).primaryEmail;
+ for (var i = 1; i < total; i++ ) {
+ addresses += ", " + addressList.queryElementAt(i, Ci.nsIAbCard).primaryEmail;
+ }
+ }
+ return addresses;
+}
+
+function DisplayCardViewPane(realCard)
+{
+ var generatedName = realCard.generateName(Services.prefs.getIntPref("mail.addr_book.lastnamefirst"));
+
+ let data = top.cvData;
+ let visible = false;
+
+ let card = { getProperty : function (prop) {
+ return realCard.getProperty(prop, "");
+ },
+ primaryEmail : realCard.primaryEmail,
+ displayName : realCard.displayName,
+ isMailList : realCard.isMailList,
+ mailListURI : realCard.mailListURI
+ };
+
+ // Contact photo
+ displayPhoto(card, cvData.cvPhoto);
+
+ let titleString;
+ if (generatedName == "")
+ titleString = card.primaryEmail; // if no generatedName, use email
+ else
+ titleString = generatedName;
+
+ // set fields in card view pane
+ if (card.isMailList)
+ cvSetNode(data.CardTitle, gAddressBookBundle.getFormattedString("viewListTitle", [generatedName]));
+ else
+ cvSetNode(data.CardTitle, titleString);
+
+ // Contact section
+ cvSetNodeWithLabel(data.cvNickname, zNickname, card.getProperty("NickName"));
+
+ if (card.isMailList) {
+ // email1 and display name always hidden when a mailing list.
+ cvSetVisible(data.cvDisplayName, false);
+ cvSetVisible(data.cvEmail1Box, false);
+
+ visible = HandleLink(data.cvListName, zListName, card.displayName, data.cvListNameBox, "mailto:" + encodeURIComponent(GenerateAddressFromCard(card)));
+ }
+ else {
+ // listname always hidden if not a mailing list
+ cvSetVisible(data.cvListNameBox, false);
+
+ cvSetNodeWithLabel(data.cvDisplayName, zDisplayName, card.displayName);
+
+ visible = HandleLink(data.cvEmail1, zPrimaryEmail, card.primaryEmail, data.cvEmail1Box, "mailto:" + card.primaryEmail);
+ }
+
+ visible = HandleLink(data.cvEmail2, zSecondaryEmail, card.getProperty("SecondEmail"), data.cvEmail2Box, "mailto:" + card.getProperty("SecondEmail")) || visible;
+
+ // Home section
+ visible = cvSetNode(data.cvHomeAddress, card.getProperty("HomeAddress"));
+ visible = cvSetNode(data.cvHomeAddress2, card.getProperty("HomeAddress2")) || visible;
+ visible = cvSetCityStateZip(data.cvHomeCityStZip, card.getProperty("HomeCity"), card.getProperty("HomeState"), card.getProperty("HomeZipCode")) || visible;
+ visible = cvSetNode(data.cvHomeCountry, card.getProperty("HomeCountry")) || visible;
+
+ let mapURLList = data.cvHomeMapIt.firstChild;
+ if (visible)
+ mapURLList.initMapAddressFromCard(card, "Home");
+
+ cvSetVisible(data.cvbHomeMapItBox, visible && !!mapURLList.mapURL);
+
+ visible = HandleLink(data.cvHomeWebPage, "", card.getProperty("WebPage2"), data.cvHomeWebPageBox, card.getProperty("WebPage2")) || visible;
+
+ cvSetVisible(data.cvhHome, visible);
+ cvSetVisible(data.cvbHome, visible);
+ if (card.isMailList) {
+ // Description section
+ visible = cvSetNode(data.cvDescription, card.getProperty("Notes"))
+ cvSetVisible(data.cvbDescription, visible);
+
+ // Addresses section
+ visible = cvAddAddressNodes(data.cvAddresses, card.mailListURI);
+ cvSetVisible(data.cvbAddresses, visible);
+
+ // Other and Chat sections, not shown for mailing lists.
+ cvSetVisible(data.cvbOther, false);
+ cvSetVisible(data.cvbChat, false);
+ }
+ else {
+ // Other section
+ /// setup the birthday information
+ let day = card.getProperty("BirthDay", null);
+ let month = card.getProperty("BirthMonth", null);
+ let year = card.getProperty("BirthYear", null);
+ let dateStr;
+ if (day > 0 && day < 32 && month > 0 && month < 13) {
+ let date;
+ let formatter;
+ if (year) {
+ // use UTC-based calculations to avoid off-by-one day
+ // due to time zone/dst discontinuity
+ date = new Date(Date.UTC(year, month - 1, day));
+ date.setUTCFullYear(year); // to handle two-digit years properly
+ formatter = new Services.intl.DateTimeFormat(undefined,
+ { dateStyle: "long", timeZone: "UTC" });
+ }
+ // if the year doesn't exist, display Month DD (ex. January 1)
+ else {
+ date = new Date(Date.UTC(saneBirthYear(year), month - 1, day));
+ formatter = new Services.intl.DateTimeFormat(undefined,
+ { month: "long", day: "numeric", timeZone: "UTC" });
+ }
+ dateStr = formatter.format(date);
+ }
+ else if (year) {
+ dateStr = year;
+ }
+
+ visible = cvSetNodeWithLabel(data.cvBirthday, zBirthday, dateStr);
+
+ visible = cvSetNodeWithLabel(data.cvCustom1, zCustom1, card.getProperty("Custom1")) || visible;
+ visible = cvSetNodeWithLabel(data.cvCustom2, zCustom2, card.getProperty("Custom2")) || visible;
+ visible = cvSetNodeWithLabel(data.cvCustom3, zCustom3, card.getProperty("Custom3")) || visible;
+ visible = cvSetNodeWithLabel(data.cvCustom4, zCustom4, card.getProperty("Custom4")) || visible;
+ visible = cvSetNode(data.cvNotes, card.getProperty("Notes")) || visible;
+
+ cvSetVisible(data.cvhOther, visible);
+ cvSetVisible(data.cvbOther, visible);
+
+ // Chat section
+ visible = cvSetNodeWithLabel(data.cvYahoo, zYahoo,
+ card.getProperty("_Yahoo"));
+ visible = cvSetNodeWithLabel(data.cvSkype, zSkype,
+ card.getProperty("_Skype")) || visible;
+ visible = cvSetNodeWithLabel(data.cvQQ, zQQ,
+ card.getProperty("_QQ")) || visible;
+ visible = cvSetNodeWithLabel(data.cvMSN, zMSN,
+ card.getProperty("_MSN")) || visible;
+ visible = cvSetNodeWithLabel(data.cvICQ, zICQ,
+ card.getProperty("_ICQ")) || visible;
+ visible = cvSetNodeWithLabel(data.cvXMPP, zXMPP,
+ card.getProperty("_JabberId")) || visible;
+ visible = cvSetNodeWithLabel(data.cvIRC, zIRC,
+ card.getProperty("_IRC")) || visible;
+ cvSetVisible(data.cvhChat, visible);
+ cvSetVisible(data.cvbChat, visible);
+
+ // hide description section, not show for non-mailing lists
+ cvSetVisible(data.cvbDescription, false);
+
+ // hide addresses section, not show for non-mailing lists
+ cvSetVisible(data.cvbAddresses, false);
+ }
+
+ // Phone section
+ visible = cvSetNodeWithLabel(data.cvPhWork, zWork, card.getProperty("WorkPhone"));
+ visible = cvSetNodeWithLabel(data.cvPhHome, zHome, card.getProperty("HomePhone")) || visible;
+ visible = cvSetNodeWithLabel(data.cvPhFax, zFax, card.getProperty("FaxNumber")) || visible;
+ visible = cvSetNodeWithLabel(data.cvPhCellular, zCellular, card.getProperty("CellularNumber")) || visible;
+ visible = cvSetNodeWithLabel(data.cvPhPager, zPager, card.getProperty("PagerNumber")) || visible;
+ cvSetVisible(data.cvhPhone, visible);
+ cvSetVisible(data.cvbPhone, visible);
+
+ // Work section
+ visible = cvSetNode(data.cvJobTitle, card.getProperty("JobTitle"));
+ visible = cvSetNode(data.cvDepartment, card.getProperty("Department")) || visible;
+ visible = cvSetNode(data.cvCompany, card.getProperty("Company")) || visible;
+
+ let addressVisible = cvSetNode(data.cvWorkAddress, card.getProperty("WorkAddress"));
+ addressVisible = cvSetNode(data.cvWorkAddress2, card.getProperty("WorkAddress2")) || addressVisible;
+ addressVisible = cvSetCityStateZip(data.cvWorkCityStZip, card.getProperty("WorkCity"), card.getProperty("WorkState"), card.getProperty("WorkZipCode")) || addressVisible;
+ addressVisible = cvSetNode(data.cvWorkCountry, card.getProperty("WorkCountry")) || addressVisible;
+
+ mapURLList = data.cvWorkMapIt.firstChild;
+ if (addressVisible)
+ mapURLList.initMapAddressFromCard(card, "Work");
+
+ cvSetVisible(data.cvbWorkMapItBox, addressVisible && !!mapURLList.mapURL);
+
+ visible = HandleLink(data.cvWorkWebPage, "", card.getProperty("WebPage1"), data.cvWorkWebPageBox, card.getProperty("WebPage1")) || addressVisible || visible;
+
+ cvSetVisible(data.cvhWork, visible);
+ cvSetVisible(data.cvbWork, visible);
+
+ // make the card view box visible
+ cvSetVisible(top.cvData.CardViewBox, true);
+}
+
+function ClearCardViewPane()
+{
+ cvSetVisible(top.cvData.CardViewBox, false);
+}
+
+function cvSetNodeWithLabel(node, label, text)
+{
+ if (text) {
+ if (label)
+ return cvSetNode(node, label + ": " + text);
+ else
+ return cvSetNode(node, text);
+ }
+ else
+ return cvSetNode(node, "");
+}
+
+function cvSetCityStateZip(node, city, state, zip)
+{
+ let text = "";
+
+ if (city && state && zip)
+ text = gAddressBookBundle.getFormattedString("cityAndStateAndZip",
+ [city, state, zip]);
+ else if (city && state && !zip)
+ text = gAddressBookBundle.getFormattedString("cityAndStateNoZip",
+ [city, state]);
+ else if (zip && ((!city && state) || (city && !state)))
+ text = gAddressBookBundle.getFormattedString("cityOrStateAndZip",
+ [city + state, zip]);
+ else {
+ // Only one of the strings is non-empty so contatenating them produces that string.
+ text = city + state + zip;
+ }
+
+ return cvSetNode(node, text);
+}
+
+function cvSetNode(node, text)
+{
+ if (!node)
+ return false;
+
+ node.textContent = text;
+ let visible = !!text;
+ cvSetVisible(node, visible);
+
+ return visible;
+}
+
+function cvAddAddressNodes(node, uri)
+{
+ var visible = false;
+
+ if (node) {
+ var editList = GetDirectoryFromURI(uri);
+ var addressList = editList.addressLists;
+
+ if (addressList) {
+ var total = addressList.length;
+ if (total > 0) {
+ while (node.hasChildNodes()) {
+ node.lastChild.remove();
+ }
+ for (i = 0; i < total; i++ ) {
+ var descNode = document.createElement("description");
+ var card = addressList.queryElementAt(i, Ci.nsIAbCard);
+
+ descNode.setAttribute("class", "CardViewLink");
+ node.appendChild(descNode);
+
+ var linkNode = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
+ linkNode.setAttribute("id", "addr#" + i);
+ linkNode.setAttribute("href", "mailto:" + card.primaryEmail);
+ descNode.appendChild(linkNode);
+
+ var textNode = document.createTextNode(card.displayName + " <" + card.primaryEmail + ">");
+ linkNode.appendChild(textNode);
+ }
+ visible = true;
+ }
+ }
+ cvSetVisible(node, visible);
+ }
+ return visible;
+}
+
+function cvSetVisible(node, visible)
+{
+ if ( visible )
+ node.removeAttribute("collapsed");
+ else
+ node.setAttribute("collapsed", "true");
+}
+
+function HandleLink(node, label, value, box, link)
+{
+ var visible = cvSetNodeWithLabel(node, label, value);
+ if (visible)
+ node.setAttribute('href', link);
+ cvSetVisible(box, visible);
+
+ return visible;
+}
+
+function openLink(aEvent)
+{
+ openAsExternal(aEvent.target.getAttribute("href"));
+ // return false, so we don't load the href in the addressbook window
+ return false;
+}
+
+function openLinkWithUrl(aUrl)
+{
+ if (aUrl)
+ openAsExternal(aUrl);
+ // return false, so we don't load the href in the addressbook window
+ return false;
+}
+
+/* Display the contact photo from the nsIAbCard in the IMG element.
+ * If the photo cannot be displayed, show the generic contact
+ * photo.
+ */
+function displayPhoto(aCard, aImg)
+{
+ var type = aCard.getProperty("PhotoType", "");
+ if (!gPhotoDisplayHandlers[type] ||
+ !gPhotoDisplayHandlers[type](aCard, aImg))
+ gPhotoDisplayHandlers["generic"](aCard, aImg);
+}
+
+/* In order to display the contact photos in the card view, there
+ * must be a registered photo display handler for the card photo
+ * type. The generic, file, and web photo types are handled
+ * by default.
+ *
+ * A photo display handler is a function that behaves as follows:
+ *
+ * function(aCard, aImg):
+ * The function is responsible for determining how to retrieve
+ * the photo from nsIAbCard aCard, and for displaying it in img
+ * img element aImg. Returns true if successful. If it returns
+ * false, the generic photo display handler will be called.
+ *
+ * The following display handlers are for the generic, file and
+ * web photo types.
+ */
+
+var gGenericPhotoDisplayHandler = function(aCard, aImg)
+{
+ aImg.setAttribute("src", defaultPhotoURI);
+ return true;
+};
+
+var gPhotoNameDisplayHandler = function(aCard, aImg)
+{
+ var photoSrc = getPhotoURI(aCard.getProperty("PhotoName"));
+ aImg.setAttribute("src", photoSrc);
+ return true;
+};
+
+/* In order for a photo display handler to be registered for
+ * a particular photo type, it must be registered here.
+ */
+function registerPhotoDisplayHandler(aType, aPhotoDisplayHandler)
+{
+ if (!gPhotoDisplayHandlers[aType])
+ gPhotoDisplayHandlers[aType] = aPhotoDisplayHandler;
+}
+
+registerPhotoDisplayHandler("generic", gGenericPhotoDisplayHandler);
+// File and Web are treated the same, and therefore use the
+// same handler.
+registerPhotoDisplayHandler("file", gPhotoNameDisplayHandler);
+registerPhotoDisplayHandler("web", gPhotoNameDisplayHandler);
diff --git a/comm/suite/mailnews/components/addrbook/content/abCommon.js b/comm/suite/mailnews/components/addrbook/content/abCommon.js
new file mode 100644
index 0000000000..a8e9d37c21
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abCommon.js
@@ -0,0 +1,1185 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { IOUtils } = ChromeUtils.import("resource:///modules/IOUtils.js");
+const { MailServices } =
+ ChromeUtils.import("resource:///modules/MailServices.jsm");
+const { FileUtils } =
+ ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+const { PrivateBrowsingUtils } =
+ ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+
+var gDirTree = null;
+var abList = null;
+var gAbResultsTree = null;
+var gAbView = null;
+var gAddressBookBundle;
+// A boolean variable determining whether AB column should be shown in AB
+// sidebar in compose window.
+var gShowAbColumnInComposeSidebar = false;
+
+const kDefaultSortColumn = "GeneratedName";
+const kDefaultAscending = "ascending";
+const kDefaultDescending = "descending";
+// kDefaultYear will be used in birthday calculations when no year is given;
+// this is a leap year so that Feb 29th works.
+const kDefaultYear = nearestLeap(new Date().getFullYear());
+const kMaxYear = 9999;
+const kMinYear = 1;
+const kAllDirectoryRoot = "moz-abdirectory://";
+const kLdapUrlPrefix = "moz-abldapdirectory://";
+const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
+const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
+// The default, generic contact image is displayed via CSS when the photoURI is
+// blank.
+var defaultPhotoURI = "";
+
+// Controller object for Dir Pane
+var DirPaneController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command) {
+ case "cmd_selectAll":
+ case "cmd_delete":
+ case "button_delete":
+ case "cmd_properties":
+ case "cmd_printcard":
+ case "cmd_printcardpreview":
+ case "cmd_print":
+ case "cmd_printpreview":
+ case "cmd_newlist":
+ case "cmd_newCard":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ switch (command) {
+ case "cmd_selectAll":
+ // The gDirTree pane only handles single selection, but normally we
+ // enable cmd_selectAll as it will get forwarded to the results pane.
+ // But if there is no gAbView, disable as we can't forward to anywhere.
+ return (gAbView != null);
+ case "cmd_delete":
+ case "button_delete": {
+ let selectedDir = getSelectedDirectory();
+ if (!selectedDir)
+ return false;
+ let selectedDirURI = selectedDir.URI;
+
+ // Context-sensitive labels for Edit > Delete menuitem.
+ if (command == "cmd_delete") {
+ goSetMenuValue(command, selectedDir.isMailList ?
+ "valueList" : "valueAddressBook");
+ }
+
+ // If it's one of these special ABs, return false to disable deletion.
+ if (selectedDirURI == kPersonalAddressbookURI ||
+ selectedDirURI == kCollectedAddressbookURI ||
+ selectedDirURI == (kAllDirectoryRoot + "?"))
+ return false;
+
+ // If the directory is a mailing list, and it is read-only,
+ // return false to disable deletion.
+ if (selectedDir.isMailList && selectedDir.readOnly)
+ return false;
+
+ // If the selected directory is an ldap directory,
+ // and if the prefs for this directory are locked,
+ // return false to disable deletion.
+ if (selectedDirURI.startsWith(kLdapUrlPrefix)) {
+ let disable = false;
+ try {
+ let prefName = selectedDirURI.substr(kLdapUrlPrefix.length);
+ disable = Services.prefs.getBoolPref(prefName + ".disable_delete");
+ }
+ catch(ex) {
+ // If this preference is not set, that's ok.
+ }
+ if (disable)
+ return false;
+ }
+
+ // Else return true to enable deletion (default).
+ return true;
+ }
+ case "cmd_printcard":
+ case "cmd_printcardpreview":
+ return (GetSelectedCardIndex() != -1);
+ case "cmd_print":
+ case "cmd_printpreview":
+ document.querySelectorAll("[command=cmd_print]").forEach(e => {
+ e.disabled = false;
+ });
+ return true;
+ case "cmd_properties":
+ return (getSelectedDirectoryURI() != null);
+ case "cmd_newlist":
+ case "cmd_newCard":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(command)
+ {
+ switch (command) {
+ case "cmd_printcard":
+ case "cmd_printcardpreview":
+ case "cmd_selectAll":
+ SendCommandToResultsPane(command);
+ break;
+ case "cmd_print":
+ AbPrintAddressBook();
+ break;
+ case "cmd_printpreview":
+ AbPrintPreviewAddressBook();
+ break;
+ case "cmd_delete":
+ case "button_delete":
+ if (gDirTree)
+ AbDeleteSelectedDirectory();
+ break;
+ case "cmd_properties":
+ AbEditSelectedDirectory();
+ break;
+ case "cmd_newlist":
+ AbNewList();
+ break;
+ case "cmd_newCard":
+ AbNewCard();
+ break;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ // on blur events set the menu item texts back to the normal values
+ if (event == "blur")
+ goSetMenuValue("cmd_delete", "valueDefault");
+ }
+};
+
+function SendCommandToResultsPane(command)
+{
+ ResultsPaneController.doCommand(command);
+
+ // if we are sending the command so the results pane
+ // we should focus the results pane
+ gAbResultsTree.focus();
+}
+
+function AbNewLDAPDirectory()
+{
+ window.openDialog("chrome://messenger/content/addressbook/pref-directory-add.xul",
+ "",
+ "chrome,modal,resizable=no,centerscreen",
+ null);
+}
+
+function AbNewAddressBook()
+{
+ window.openDialog("chrome://messenger/content/addressbook/abAddressBookNameDialog.xul",
+ "",
+ "chrome,modal,resizable=no,centerscreen",
+ null);
+}
+
+function AbEditSelectedDirectory()
+{
+ let selectedDir = getSelectedDirectory();
+ if (!selectedDir)
+ return;
+
+ if (selectedDir.isMailList) {
+ goEditListDialog(null, selectedDir.URI);
+ } else {
+ window.openDialog(selectedDir.propertiesChromeURI,
+ "",
+ "chrome,modal,resizable=no,centerscreen",
+ {selectedDirectory: selectedDir});
+ }
+}
+
+function AbDeleteSelectedDirectory()
+{
+ let selectedDirURI = getSelectedDirectoryURI();
+ if (!selectedDirURI)
+ return;
+
+ AbDeleteDirectory(selectedDirURI);
+}
+
+function AbDeleteDirectory(aURI)
+{
+ // Determine strings for smart and context-sensitive user prompts
+ // for confirming deletion.
+ let directory = GetDirectoryFromURI(aURI);
+ let confirmDeleteTitleID;
+ let confirmDeleteTitle;
+ let confirmDeleteMessageID;
+ let confirmDeleteMessage;
+ let brandShortName;
+ let clearCollectionPrefs = false;
+
+ if (directory.isMailList) {
+ // It's a mailing list.
+ confirmDeleteMessageID = "confirmDeleteThisMailingList";
+ confirmDeleteTitleID = "confirmDeleteThisMailingListTitle";
+ } else {
+ // It's an address book: check which type.
+ if (Services.prefs.getCharPref("mail.collect_addressbook") == aURI &&
+ (Services.prefs.getBoolPref("mail.collect_email_address_outgoing") ||
+ Services.prefs.getBoolPref("mail.collect_email_address_incoming") ||
+ Services.prefs.getBoolPref("mail.collect_email_address_newsgroup"))) {
+ // It's a collection address book: let's be clear about the consequences.
+ brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
+ confirmDeleteMessageID = "confirmDeleteThisCollectionAddressbook";
+ confirmDeleteTitleID = "confirmDeleteThisCollectionAddressbookTitle";
+ clearCollectionPrefs = true;
+ } else if (directory.URI.startsWith(kLdapUrlPrefix)) {
+ // It's an LDAP directory, so we only delete our offline copy.
+ confirmDeleteMessageID = "confirmDeleteThisLDAPDir";
+ confirmDeleteTitleID = "confirmDeleteThisLDAPDirTitle";
+ } else {
+ // It's a normal personal address book: we'll delete its contacts, too.
+ confirmDeleteMessageID = "confirmDeleteThisAddressbook";
+ confirmDeleteTitleID = "confirmDeleteThisAddressbookTitle";
+ }
+ }
+
+ // Get the raw strings with placeholders.
+ confirmDeleteTitle = gAddressBookBundle.getString(confirmDeleteTitleID);
+ confirmDeleteMessage = gAddressBookBundle.getString(confirmDeleteMessageID);
+
+ // Substitute placeholders as required.
+ // Replace #1 with the name of the selected address book or mailing list.
+ confirmDeleteMessage = confirmDeleteMessage.replace("#1", directory.dirName);
+ if (brandShortName) {
+ // For a collection address book, replace #2 with the brandShortName.
+ confirmDeleteMessage = confirmDeleteMessage.replace("#2", brandShortName);
+ }
+
+ // Ask for confirmation before deleting
+ if (!Services.prompt.confirm(window, confirmDeleteTitle,
+ confirmDeleteMessage)) {
+ // Deletion cancelled by user.
+ return;
+ }
+
+ // If we're about to delete the collection AB, update the respective prefs.
+ if (clearCollectionPrefs) {
+ Services.prefs.setBoolPref("mail.collect_email_address_outgoing", false);
+ Services.prefs.setBoolPref("mail.collect_email_address_incoming", false);
+ Services.prefs.setBoolPref("mail.collect_email_address_newsgroup", false);
+
+ // Change the collection AB pref to "Personal Address Book" so that we
+ // don't get a blank item in prefs dialog when collection is re-enabled.
+ Services.prefs.setCharPref("mail.collect_addressbook",
+ kPersonalAddressbookURI);
+ }
+
+ MailServices.ab.deleteAddressBook(aURI);
+}
+
+function InitCommonJS()
+{
+ gDirTree = document.getElementById("dirTree");
+ abList = document.getElementById("addressbookList");
+ gAddressBookBundle = document.getElementById("bundle_addressBook");
+
+ // Make an entry for "All Address Books".
+ if (abList) {
+ abList.insertItemAt(0, gAddressBookBundle.getString("allAddressBooks"),
+ kAllDirectoryRoot + "?");
+ }
+}
+
+function UpgradeAddressBookResultsPaneUI(prefName)
+{
+ // placeholder in case any new columns get added to the address book
+ // var resultsPaneUIVersion = Services.prefs.getIntPref(prefName);
+}
+
+function AbDelete()
+{
+ let types = GetSelectedCardTypes();
+ if (types == kNothingSelected)
+ return;
+
+ // Determine strings for smart and context-sensitive user prompts
+ // for confirming deletion.
+ let confirmDeleteTitleID;
+ let confirmDeleteTitle;
+ let confirmDeleteMessageID;
+ let confirmDeleteMessage;
+ let itemName;
+ let containingListName;
+ let selectedDir = getSelectedDirectory();
+ let numSelectedItems = gAbView.selection.count;
+
+ switch(types) {
+ case kListsAndCards:
+ confirmDeleteMessageID = "confirmDelete2orMoreContactsAndLists";
+ confirmDeleteTitleID = "confirmDelete2orMoreContactsAndListsTitle";
+ break;
+ case kSingleListOnly:
+ // Set item name for single mailing list.
+ let theCard = GetSelectedAbCards()[0];
+ itemName = theCard.displayName;
+ confirmDeleteMessageID = "confirmDeleteThisMailingList";
+ confirmDeleteTitleID = "confirmDeleteThisMailingListTitle";
+ break;
+ case kMultipleListsOnly:
+ confirmDeleteMessageID = "confirmDelete2orMoreMailingLists";
+ confirmDeleteTitleID = "confirmDelete2orMoreMailingListsTitle";
+ break;
+ case kCardsOnly:
+ if (selectedDir.isMailList) {
+ // Contact(s) in mailing lists will be removed from the list, not deleted.
+ if (numSelectedItems == 1) {
+ confirmDeleteMessageID = "confirmRemoveThisContact";
+ confirmDeleteTitleID = "confirmRemoveThisContactTitle";
+ } else {
+ confirmDeleteMessageID = "confirmRemove2orMoreContacts";
+ confirmDeleteTitleID = "confirmRemove2orMoreContactsTitle";
+ }
+ // For removing contacts from mailing list, set placeholder value
+ containingListName = selectedDir.dirName;
+ } else {
+ // Contact(s) in address books will be deleted.
+ if (numSelectedItems == 1) {
+ confirmDeleteMessageID = "confirmDeleteThisContact";
+ confirmDeleteTitleID = "confirmDeleteThisContactTitle";
+ } else {
+ confirmDeleteMessageID = "confirmDelete2orMoreContacts";
+ confirmDeleteTitleID = "confirmDelete2orMoreContactsTitle";
+ }
+ }
+ if (numSelectedItems == 1) {
+ // Set item name for single contact.
+ let theCard = GetSelectedAbCards()[0];
+ let nameFormatFromPref = Services.prefs.getIntPref("mail.addr_book.lastnamefirst");
+ itemName = theCard.generateName(nameFormatFromPref);
+ }
+ break;
+ }
+
+ // Get the raw model strings.
+ // For numSelectedItems == 1, it's simple strings.
+ // For messages with numSelectedItems > 1, it's multi-pluralform string sets.
+ // confirmDeleteMessage has placeholders for some forms.
+ confirmDeleteTitle = gAddressBookBundle.getString(confirmDeleteTitleID);
+ confirmDeleteMessage = gAddressBookBundle.getString(confirmDeleteMessageID);
+
+ // Get plural form where applicable; substitute placeholders as required.
+ if (numSelectedItems == 1) {
+ // If single selected item, substitute itemName.
+ confirmDeleteMessage = confirmDeleteMessage.replace("#1", itemName);
+ } else {
+ // If multiple selected items, get the right plural string from the
+ // localized set, then substitute numSelectedItems.
+ confirmDeleteMessage = PluralForm.get(numSelectedItems, confirmDeleteMessage);
+ confirmDeleteMessage = confirmDeleteMessage.replace("#1", numSelectedItems);
+ }
+ // If contact(s) in a mailing list, substitute containingListName.
+ if (containingListName)
+ confirmDeleteMessage = confirmDeleteMessage.replace("#2", containingListName);
+
+ // Finally, show our smart confirmation message, and act upon it!
+ if (!Services.prompt.confirm(window, confirmDeleteTitle,
+ confirmDeleteMessage)) {
+ // Deletion cancelled by user.
+ return;
+ }
+
+ if (selectedDir.URI == (kAllDirectoryRoot + "?")) {
+ // Delete cards from "All Address Books" view.
+ let cards = GetSelectedAbCards();
+ for (let i = 0; i < cards.length; i++) {
+ let dirId = cards[i].directoryId
+ .substring(0, cards[i].directoryId.indexOf("&"));
+ let directory = MailServices.ab.getDirectoryFromId(dirId);
+
+ let cardArray =
+ Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ cardArray.appendElement(cards[i], false);
+ if (directory)
+ directory.deleteCards(cardArray);
+ }
+ SetAbView(kAllDirectoryRoot + "?");
+ } else {
+ // Delete cards from address books or mailing lists.
+ gAbView.deleteSelectedCards();
+ }
+}
+
+function AbNewCard()
+{
+ goNewCardDialog(getSelectedDirectoryURI());
+}
+
+function AbEditCard(card)
+{
+ // Need a card,
+ if (!card)
+ return;
+
+ if (card.isMailList) {
+ goEditListDialog(card, card.mailListURI);
+ } else {
+ goEditCardDialog(getSelectedDirectoryURI(), card);
+ }
+}
+
+function AbNewMessage()
+{
+ let params = Cc["@mozilla.org/messengercompose/composeparams;1"].createInstance(Ci.nsIMsgComposeParams);
+ if (params) {
+ let composeFields = Cc["@mozilla.org/messengercompose/composefields;1"].createInstance(Ci.nsIMsgCompFields);
+ if (composeFields) {
+ params.type = Ci.nsIMsgCompType.New;
+ params.format = Ci.nsIMsgCompFormat.Default;
+ if (DirPaneHasFocus()) {
+ let selectedDir = getSelectedDirectory();
+ let hidesRecipients = false;
+ try {
+ // This is a bit of hackery so that extensions can have mailing lists
+ // where recipients are sent messages via BCC.
+ hidesRecipients = selectedDir.getBoolValue("HidesRecipients", false);
+ } catch(e) {
+ // Standard Thunderbird mailing lists do not have preferences
+ // associated with them, so we'll silently eat the error.
+ }
+
+ if (selectedDir && selectedDir.isMailList && hidesRecipients)
+ // Bug 669301 (https://bugzilla.mozilla.org/show_bug.cgi?id=669301)
+ // We're using BCC right now to hide recipients from one another.
+ // We should probably use group syntax, but that's broken
+ // right now, so this will have to do.
+ composeFields.bcc = GetSelectedAddressesFromDirTree();
+ else
+ composeFields.to = GetSelectedAddressesFromDirTree();
+ } else {
+ composeFields.to = GetSelectedAddresses();
+ }
+ params.composeFields = composeFields;
+ MailServices.compose.OpenComposeWindowWithParams(null, params);
+ }
+ }
+}
+
+function AbCopyAddress()
+{
+ var cards = GetSelectedAbCards();
+ if (!cards)
+ return;
+
+ var count = cards.length;
+ if (!count)
+ return;
+
+ var addresses = cards[0].primaryEmail;
+ for (var i = 1; i < count; i++)
+ addresses += "," + cards[i].primaryEmail;
+
+ Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(addresses);
+}
+
+/**
+ * Set up items in the View > Layout menupopup. This function is responsible
+ * for updating the menu items' state to reflect reality.
+ *
+ * @param aEvent the event that caused the View > Layout menupopup to be shown
+ */
+function InitViewLayoutMenuPopup(aEvent)
+{
+ let dirTreeVisible = document.getElementById("dirTree-splitter")
+ .getAttribute("state") != "collapsed";
+ document.getElementById("menu_showDirectoryPane")
+ .setAttribute("checked", dirTreeVisible);
+
+ let cardPaneVisible = document.getElementById("results-splitter")
+ .getAttribute("state") != "collapsed";
+ document.getElementById("menu_showCardPane")
+ .setAttribute("checked", cardPaneVisible);
+}
+
+// Generate a list of cards from the selected mailing list
+// and get a comma separated list of card addresses. If the
+// item selected in the directory pane is not a mailing list,
+// an empty string is returned.
+function GetSelectedAddressesFromDirTree()
+{
+ let selectedDir = getSelectedDirectory();
+
+ if (!selectedDir || !selectedDir.isMailList)
+ return "";
+
+ let listCardsCount = selectedDir.addressLists.length;
+ let cards = new Array(listCardsCount);
+ for (let i = 0; i < listCardsCount; ++i)
+ cards[i] = selectedDir.addressLists
+ .queryElementAt(i, Ci.nsIAbCard);
+ return GetAddressesForCards(cards);
+}
+
+// Generate a comma separated list of addresses from a given
+// set of cards.
+function GetAddressesForCards(cards)
+{
+ var addresses = "";
+
+ if (!cards)
+ return addresses;
+
+ var count = cards.length;
+ for (var i = 0; i < count; ++i) {
+ var generatedAddress = GenerateAddressFromCard(cards[i]);
+ if (generatedAddress) {
+ // If it's not the first address in the list, add a comma separator.
+ if (addresses)
+ addresses += ",";
+ addresses += generatedAddress;
+ }
+ }
+
+ return addresses;
+}
+
+
+function SelectFirstAddressBook()
+{
+ if (gDirectoryTreeView.selection.currentIndex != 0) {
+ gDirectoryTreeView.selection.select(0);
+ ChangeDirectoryByURI(getSelectedDirectoryURI());
+ }
+ gAbResultsTree.focus();
+}
+
+function DirPaneClick(event)
+{
+ // we only care about left button events
+ if (event.button != 0)
+ return;
+
+ // if the user clicks on the header / trecol, do nothing
+ if (event.originalTarget.localName == "treecol") {
+ event.stopPropagation();
+ return;
+ }
+}
+
+function DirPaneDoubleClick(event)
+{
+ // We only care about left button events.
+ if (event.button != 0)
+ return;
+
+ // Ignore double clicking on invalid rows.
+ let row = gDirTree.treeBoxObject.getRowAt(event.clientX, event.clientY);
+ if (row == -1 || row >= gDirectoryTreeView.rowCount)
+ return;
+
+ // Default action for double click is expand/collapse which ships with the tree.
+ // For convenience, allow double-click to edit the properties of mailing
+ // lists in directory tree.
+ if (gDirTree && gDirTree.view.selection &&
+ gDirTree.view.selection.count == 1 &&
+ getSelectedDirectory().isMailList) {
+ AbEditSelectedDirectory();
+ }
+}
+
+function DirPaneSelectionChange()
+{
+ let uri = getSelectedDirectoryURI();
+ // clear out the search box when changing folders...
+ onAbClearSearch(false);
+ if (gDirectoryTreeView.selection &&
+ gDirectoryTreeView.selection.count == 1) {
+ ChangeDirectoryByURI(uri);
+ document.getElementById("localResultsOnlyMessage")
+ .setAttribute("hidden",
+ !gDirectoryTreeView.hasRemoteAB ||
+ uri != kAllDirectoryRoot + "?");
+ }
+}
+
+function ChangeDirectoryByURI(uri = kPersonalAddressbookURI)
+{
+ SetAbView(uri);
+
+ // Actively de-selecting if there are any pre-existing selections
+ // in the results list.
+ if (gAbView && gAbView.getCardFromRow(0))
+ gAbView.selection.clearSelection();
+ else
+ // the selection changes if we were switching directories.
+ ResultsPaneSelectionChanged()
+}
+
+function AbNewList()
+{
+ goNewListDialog(getSelectedDirectoryURI());
+}
+
+function goNewListDialog(selectedAB)
+{
+ window.openDialog("chrome://messenger/content/addressbook/abMailListDialog.xul",
+ "",
+ "chrome,modal,resizable,centerscreen",
+ {selectedAB:selectedAB});
+}
+
+function goEditListDialog(abCard, listURI)
+{
+ let params = {
+ abCard: abCard,
+ listURI: listURI,
+ refresh: false, // This is an out param, true if OK in dialog is clicked.
+ };
+
+ window.openDialog("chrome://messenger/content/addressbook/abEditListDialog.xul",
+ "",
+ "chrome,modal,resizable,centerscreen",
+ params);
+
+ if (params.refresh) {
+ ChangeDirectoryByURI(listURI); // force refresh
+ }
+}
+
+function goNewCardDialog(selectedAB)
+{
+ window.openDialog("chrome://messenger/content/addressbook/abNewCardDialog.xul",
+ "",
+ "chrome,modal,resizable=no,centerscreen",
+ {selectedAB:selectedAB});
+}
+
+function goEditCardDialog(abURI, card)
+{
+ window.openDialog("chrome://messenger/content/addressbook/abEditCardDialog.xul",
+ "",
+ "chrome,modal,resizable=no,centerscreen",
+ {abURI:abURI, card:card});
+}
+
+function setSortByMenuItemCheckState(id, value)
+{
+ var menuitem = document.getElementById(id);
+ if (menuitem) {
+ menuitem.setAttribute("checked", value);
+ }
+}
+
+function InitViewSortByMenu()
+{
+ var sortColumn = kDefaultSortColumn;
+ var sortDirection = kDefaultAscending;
+
+ if (gAbView) {
+ sortColumn = gAbView.sortColumn;
+ sortDirection = gAbView.sortDirection;
+ }
+
+ // this approach is necessary to support generic columns that get overlayed.
+ let elements = document.querySelectorAll('[name="sortas"]');
+ for (let i = 0; i < elements.length; i++) {
+ let cmd = elements[i].id;
+ let columnForCmd = cmd.substr(10); // everything right of cmd_SortBy
+ setSortByMenuItemCheckState(cmd, (sortColumn == columnForCmd));
+ }
+
+ setSortByMenuItemCheckState("sortAscending", (sortDirection == kDefaultAscending));
+ setSortByMenuItemCheckState("sortDescending", (sortDirection == kDefaultDescending));
+}
+
+function GenerateAddressFromCard(card)
+{
+ if (!card)
+ return "";
+
+ var email;
+
+ if (card.isMailList)
+ {
+ var directory = GetDirectoryFromURI(card.mailListURI);
+ email = directory.description || card.displayName;
+ }
+ else
+ email = card.primaryEmail;
+
+ return MailServices.headerParser.makeMimeAddress(card.displayName, email);
+}
+
+function GetDirectoryFromURI(uri)
+{
+ return MailServices.ab.getDirectory(uri);
+}
+
+// returns null if abURI is not a mailing list URI
+function GetParentDirectoryFromMailingListURI(abURI)
+{
+ var abURIArr = abURI.split("/");
+ /*
+ turn turn "moz-abmdbdirectory://abook.mab/MailList6"
+ into ["moz-abmdbdirectory:","","abook.mab","MailList6"]
+ then, turn ["moz-abmdbdirectory:","","abook.mab","MailList6"]
+ into "moz-abmdbdirectory://abook.mab"
+ */
+ if (abURIArr.length == 4 && abURIArr[0] == "moz-abmdbdirectory:" && abURIArr[3] != "") {
+ return abURIArr[0] + "/" + abURIArr[1] + "/" + abURIArr[2];
+ }
+
+ return null;
+}
+
+/**
+ * Return true if the directory pane has focus, otherwise false.
+ */
+function DirPaneHasFocus()
+{
+ return (top.document.commandDispatcher.focusedElement == gDirTree);
+}
+
+/**
+ * Get the selected directory object.
+ *
+ * @return The object of the currently selected directory
+ */
+function getSelectedDirectory()
+{
+ // Select Addresses Dialog
+ if (abList)
+ return MailServices.ab.getDirectory(abList.value);
+
+ // Main Address Book
+ if (gDirTree.currentIndex < 0)
+ return null;
+ return gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex);
+}
+
+/**
+ * Get the URI of the selected directory.
+ *
+ * @return The URI of the currently selected directory
+ */
+function getSelectedDirectoryURI()
+{
+ // Select Addresses Dialog
+ if (abList)
+ return abList.value;
+
+ // Main Address Book
+ if (gDirTree.currentIndex < 0)
+ return null;
+ return gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex).URI;
+}
+
+/**
+ * DEPRECATED legacy function wrapper for addon compatibility;
+ * use getSelectedDirectoryURI() instead!
+ * Return the URI of the selected directory.
+ */
+function GetSelectedDirectory()
+{
+ return getSelectedDirectoryURI();
+}
+
+/**
+ * Clears the contents of the search input field,
+ * possibly causing refresh of results.
+ *
+ * @param aRefresh Set to false if the refresh isn't needed,
+ * e.g. window/AB is going away so user will not see anything.
+ */
+function onAbClearSearch(aRefresh = true)
+{
+ let searchInput = document.getElementById("searchInput");
+ if (!searchInput || !searchInput.value)
+ return;
+
+ searchInput.value = "";
+ if (aRefresh)
+ onEnterInSearchBar();
+}
+
+/**
+ * Returns an nsIFile of the directory in which contact photos are stored.
+ * This will create the directory if it does not yet exist.
+ */
+function getPhotosDir() {
+ var file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ // Get the Photos directory
+ file.append("Photos");
+ if (!file.exists() || !file.isDirectory())
+ file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8));
+ return file;
+}
+
+/**
+ * Returns a URI specifying the location of a photo based on its name.
+ * If the name is blank, or if the photo with that name is not in the Photos
+ * directory then the default photo URI is returned.
+ *
+ * @param aPhotoName The name of the photo from the Photos folder, if any.
+ *
+ * @return A URI pointing to a photo.
+ */
+function getPhotoURI(aPhotoName) {
+ if (!aPhotoName)
+ return defaultPhotoURI;
+ var file = getPhotosDir();
+ try {
+ file.append(aPhotoName);
+ }
+ catch (e) {
+ return defaultPhotoURI;
+ }
+ if (!file.exists())
+ return defaultPhotoURI;
+ return Services.io.newFileURI(file).spec;
+}
+
+/**
+ * Generates a unique filename to be used for a local copy of a contact's photo.
+ *
+ * @param aPath The path to the folder in which the photo will be saved.
+ * @param aExtension The file extension of the photo.
+ *
+ * @return A unique filename in the given path.
+ */
+function makePhotoFile(aDir, aExtension) {
+ var filename, newFile;
+ // Find a random filename for the photo that doesn't exist yet
+ do {
+ filename = new String(Math.random()).replace("0.", "") + "." + aExtension;
+ newFile = aDir.clone();
+ newFile.append(filename);
+ } while (newFile.exists());
+ return newFile;
+}
+
+/**
+ * Public self-contained object for image transfers.
+ * Responsible for file transfer, validating the image and downscaling.
+ * Attention: It is the responsibility of the caller to remove the old photo
+ * and update the card!
+ */
+var gImageDownloader = (function() {
+ let downloadInProgress = false;
+
+ // Current instance of nsIWebBrowserPersist. It is used two times, during
+ // the actual download and for saving canvas data.
+ let downloader;
+
+ // Temporary nsIFile used for download.
+ let tempFile;
+
+ // Images are downsized to this size while keeping the aspect ratio.
+ const maxSize = 300;
+
+ // Callback function for various states
+ let callbackSuccess;
+ let callbackError;
+ let callbackProgress;
+
+ // Start at 4% to show a slight progress initially.
+ const initProgress = 4;
+
+ // Constants indicating error and file transfer status
+ const STATE_TRANSFERRING = 0;
+ const STATE_RESIZING = 1;
+ const STATE_OK = 2;
+ // The URI does not have a valid format.
+ const ERROR_INVALID_URI = 0;
+ // In case of HTTP transfers: server did not answer with a 200 status code.
+ const ERROR_UNAVAILABLE = 1;
+ // The file type is not supported. Only jpeg, png and gif are.
+ const ERROR_INVALID_IMG = 2;
+ // An error occurred while saving the image to the hard drive.
+ const ERROR_SAVE = 4;
+
+
+ /**
+ * Saves a target photo in the profile's photo directory. Only one concurrent
+ * file transfer is supported. Starting a new transfer while another is still
+ * in progress will cancel the former file transfer.
+ *
+ * @param aURI {string} URI pointing to the photo.
+ * @param cbSuccess(photoName) {function} A callback function which is called
+ * on success.
+ * The photo file name is passed in.
+ * @param cbError(state) {function} A callback function which is called
+ * in case of an error. The error
+ * state is passed in.
+ * @param cbProgress(errcode, percent) {function} A callback function which
+ * provides progress report.
+ * An error code (see above)
+ * and the progress percentage
+ * (0-100) is passed in.
+ * State transitions: STATE_TRANSFERRING -> STATE_RESIZING -> STATE_OK (100%)
+ */
+ function savePhoto(aURI, aCBSuccess, aCBError, aCBProgress) {
+ callbackSuccess = typeof aCBSuccess == "function" ? aCBSuccess : null;
+ callbackError = typeof aCBError == "function" ? aCBError : null;
+ callbackProgress = typeof aCBProgress == "function" ? aCBProgress : null;
+
+ // Make sure that there is no running download.
+ cancelSave();
+ downloadInProgress = true;
+
+ if (callbackProgress) {
+ callbackProgress(STATE_TRANSFERRING, initProgress);
+ }
+
+ downloader = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist);
+ downloader.persistFlags =
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE |
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_CLEANUP_ON_FAILURE;
+ downloader.progressListener = {
+ onProgressChange(aWebProgress, aRequest, aCurSelfProgress,
+ aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
+ if (aMaxTotalProgress > -1 && callbackProgress) {
+ // Download progress is 0-90%, 90-100% is verifying and scaling the
+ // image.
+ let percent =
+ Math.round(initProgress +
+ (aCurTotalProgress / aMaxTotalProgress) *
+ (90 - initProgress));
+ callbackProgress(STATE_TRANSFERRING, percent);
+ }
+ },
+ onStateChange(aWebProgress, aRequest, aStateFlag, aStatus) {
+ // Check if the download successfully finished.
+ if ((aStateFlag & Ci.nsIWebProgressListener.STATE_STOP) &&
+ !(aStateFlag & Ci.nsIWebProgressListener.STATE_IS_REQUEST)) {
+ try {
+ // Check the response code in case of an HTTP request to catch 4xx
+ // errors.
+ let http = aRequest.QueryInterface(Ci.nsIHttpChannel);
+ if (http.responseStatus == 200) {
+ verifyImage();
+ } else if (callbackError) {
+ callbackError(ERROR_UNAVAILABLE);
+ }
+ } catch (err) {
+ // The nsIHttpChannel interface is not available - just proceed.
+ verifyImage();
+ }
+ }
+ },
+ };
+
+ let source;
+ try {
+ source = Services.io.newURI(aURI);
+ } catch (err) {
+ if (callbackError) {
+ callbackError(ERROR_INVALID_URI);
+ }
+ return;
+ }
+
+ // Start the transfer to a temporary file.
+ tempFile = FileUtils.getFile("TmpD",
+ ["tb-photo-" + new Date().getTime() + ".tmp"]);
+ tempFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ try {
+ // Obtain the privacy context of the browser window that the URL
+ // we are downloading comes from. If, and only if, the URL is not
+ // related to a window, null should be used instead.
+ let privacy = PrivateBrowsingUtils.privacyContextFromWindow(window);
+ downloader.saveURI(source, null, null, null, null, null, tempFile,
+ privacy);
+ } catch (err) {
+ cleanup();
+ if (callbackError) {
+ callbackError(ERROR_SAVE);
+ }
+ }
+ }
+
+ /**
+ * Verifies the downloaded file to be an image.
+ * Scales the image and starts the saving operation.
+ */
+ function verifyImage() {
+ let img = new Image();
+ img.onerror = function() {
+ cleanup();
+ if (callbackError) {
+ callbackError(ERROR_INVALID_IMG);
+ }
+ };
+ img.onload = function() {
+ if (callbackProgress) {
+ callbackProgress(STATE_RESIZING, 95);
+ }
+
+ // Images are scaled down in two steps to improve quality. Resizing
+ // ratios larger than 2 use a different interpolation algorithm than
+ // small ratios. Resize three times (instead of just two steps) to
+ // improve the final quality.
+ let canvas = downscale(img, 3.8 * maxSize);
+ canvas = downscale(canvas, 1.9 * maxSize);
+ canvas = downscale(canvas, maxSize);
+
+ saveCanvas(canvas);
+
+ if (callbackProgress) {
+ callbackProgress(STATE_OK, 100);
+ }
+
+ // Remove the temporary file.
+ cleanup();
+ };
+
+ if (callbackProgress) {
+ callbackProgress(STATE_RESIZING, 92);
+ }
+
+ img.src = Services.io.newFileURI(tempFile).spec;
+ }
+
+ /**
+ * Scale a graphics object down to a specified maximum dimension while
+ * preserving the aspect ratio. Does not upscale an image.
+ *
+ * @param aGraphicsObject {image | canvas} Image or canvas object
+ * @param aMaxDimension {integer} The maximal allowed width or
+ * height
+ *
+ * @return A canvas object.
+ */
+ function downscale(aGraphicsObject, aMaxDimension) {
+ let w = aGraphicsObject.width;
+ let h = aGraphicsObject.height;
+
+ if (w > h && w > aMaxDimension) {
+ h = Math.round(aMaxDimension * h / w);
+ w = aMaxDimension;
+ } else if (h > aMaxDimension) {
+ w = Math.round(aMaxDimension * w / h);
+ h = aMaxDimension;
+ }
+
+ let canvas = document.createElementNS("http://www.w3.org/1999/xhtml",
+ "canvas");
+ canvas.width = w;
+ canvas.height = h;
+
+ let ctx = canvas.getContext("2d");
+ ctx.drawImage(aGraphicsObject, 0, 0, w, h);
+ return canvas;
+ }
+
+ /**
+ * Cancel a running download (if any).
+ */
+ function cancelSave() {
+ if (!downloadInProgress) {
+ return;
+ }
+
+ // Cancel the nsIWebBrowserPersist file transfer.
+ if (downloader) {
+ downloader.cancelSave();
+ }
+ cleanup();
+ }
+
+ /**
+ * Remove the temporary file and reset internal status.
+ */
+ function cleanup() {
+ if (tempFile) {
+ try {
+ if (tempFile.exists()) {
+ tempFile.remove(false);
+ }
+ } catch (err) {}
+ tempFile = null;
+ }
+
+ downloadInProgress = false;
+ }
+
+ /**
+ * Save the contents of a canvas to the photos directory of the profile.
+ */
+ function saveCanvas(aCanvas) {
+ // Get the photos directory and check that it exists.
+ let file = getPhotosDir();
+ file = makePhotoFile(file, "png");
+
+ // Create a data url from the canvas and then create URIs of the source and
+ // targets.
+ let source = Services.io.newURI(aCanvas.toDataURL("image/png", ""), "UTF8");
+ let target = Services.io.newFileURI(file);
+
+ downloader = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist);
+ downloader.persistFlags =
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE |
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ Ci.nsIWebBrowserPersist.PERSIST_FLAGS_CLEANUP_ON_FAILURE;
+ downloader.progressListener = {
+ onStateChange(aWebProgress, aRequest, aFlag, aStatus) {
+ if ((aFlag & Ci.nsIWebProgressListener.STATE_STOP) &&
+ !(aFlag & Ci.nsIWebProgressListener.STATE_IS_REQUEST)) {
+ if (callbackSuccess) {
+ callbackSuccess(file.leafName);
+ }
+ }
+ },
+ };
+
+ // Obtain the privacy context of the browser window that the URL
+ // we are downloading comes from. If, and only if, the URL is not
+ // related to a window, null should be used instead.
+ let privacy = PrivateBrowsingUtils.privacyContextFromWindow(window);
+ downloader.saveURI(source, null, null, null, null, null, target, privacy);
+ }
+
+ // Publicly accessible methods.
+ return { cancelSave, savePhoto, STATE_TRANSFERRING, STATE_RESIZING, STATE_OK,
+ ERROR_UNAVAILABLE, ERROR_INVALID_URI, ERROR_INVALID_IMG,
+ ERROR_SAVE, };
+})();
+
+/**
+ * Validates the given year and returns it, if it looks sane.
+ * Returns kDefaultYear (a leap year), if no valid date is given.
+ * This ensures that month/day calculations still work.
+ */
+function saneBirthYear(aYear) {
+ return aYear && (aYear <= kMaxYear) && (aYear >= kMinYear) ? aYear : kDefaultYear;
+}
+
+/**
+ * Returns the nearest leap year before aYear.
+ */
+function nearestLeap(aYear) {
+ for (let year = aYear; year > 0; year--) {
+ if (new Date(year, 1, 29).getMonth() == 1)
+ return year;
+ }
+ return 2000;
+}
diff --git a/comm/suite/mailnews/components/addrbook/content/abEditCardDialog.xul b/comm/suite/mailnews/components/addrbook/content/abEditCardDialog.xul
new file mode 100644
index 0000000000..752934becb
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abEditCardDialog.xul
@@ -0,0 +1,18 @@
+<?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" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abCardOverlay.xul"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="abcardWindow"
+ onload="OnLoadEditCard()"
+ ondialogaccept="return EditCardOKButton();"
+ ondialogcancel="return EditCardCancelButton();">
+
+ <stringbundleset id="stringbundleset"/>
+ <vbox id="editcard"/>
+</dialog>
diff --git a/comm/suite/mailnews/components/addrbook/content/abEditListDialog.xul b/comm/suite/mailnews/components/addrbook/content/abEditListDialog.xul
new file mode 100644
index 0000000000..3162664e99
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abEditListDialog.xul
@@ -0,0 +1,22 @@
+<?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://messenger/skin/" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abListOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abMailListDialog.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ id="ablistWindow"
+ onload="OnLoadEditList()"
+ ondialogaccept="return EditListOKButton();"
+ ondragover="DragOverAddressListTree(event);"
+ ondrop="DropOnAddressListTree(event);">
+
+ <stringbundleset id="stringbundleset"/>
+ <vbox id="editlist" flex="1"/>
+</dialog>
diff --git a/comm/suite/mailnews/components/addrbook/content/abListOverlay.xul b/comm/suite/mailnews/components/addrbook/content/abListOverlay.xul
new file mode 100644
index 0000000000..9f8c07f010
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abListOverlay.xul
@@ -0,0 +1,86 @@
+<?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://messenger/skin/addressingWidget.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/cardDialog.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/abMailListDialog.dtd">
+
+<overlay id="editListOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://messenger/content/messengercompose/addressingWidgetOverlay.js"/>
+ <script src="chrome://messenger/content/addressbook/abCommon.js"/>
+ <script src="chrome://messenger/content/addressbook/abMailListDialog.js"/>
+
+<vbox id="editlist">
+ <grid>
+ <columns>
+ <column/>
+ <column class="CardEditWidth" flex="1"/>
+ </columns>
+
+ <rows>
+ <row id="ListNameContainer" align="center">
+ <label control="ListName"
+ class="CardEditLabel"
+ value="&ListName.label;"
+ accesskey="&ListName.accesskey;"/>
+ <textbox id="ListName"/>
+ </row>
+
+ <row id="ListNickNameContainer" align="center">
+ <label control="ListNickName"
+ class="CardEditLabel"
+ value="&ListNickName.label;"
+ accesskey="&ListNickName.accesskey;"/>
+ <textbox id="ListNickName"/>
+ </row>
+
+ <row id="ListDescriptionContainer" align="center">
+ <label control="ListDescription"
+ class="CardEditLabel"
+ value="&ListDescription.label;"
+ accesskey="&ListDescription.accesskey;"/>
+ <textbox id="ListDescription"/>
+ </row>
+ </rows>
+ </grid>
+
+ <spacer style="height:1em"/>
+ <label control="addressCol1#1"
+ value="&AddressTitle.label;"
+ accesskey="&AddressTitle.accesskey;"/>
+
+ <spacer style="height:0.1em"/>
+
+ <listbox id="addressingWidget" style="height: 15em;"
+ onclick="awClickEmptySpace(event.target, true)">
+ <listitem class="addressingWidgetItem" allowevents="true">
+ <listcell class="addressingWidgetCell">
+ <textbox id="addressCol1#1"
+ class="plain textbox-addressingWidget uri-element"
+ type="autocomplete"
+ flex="1"
+ autocompletesearch="addrbook ldap"
+ autocompletesearchparam="{}" timeout="300" maxrows="4"
+ completedefaultindex="true" forcecomplete="true"
+ minresultsforpopup="3"
+ ontextentered="awRecipientTextCommand(eventParam, this)"
+ onkeydown="awRecipientKeyDown(event, this);"
+ onclick="awNotAnEmptyArea(event);">
+ <image onclick="awNotAnEmptyArea(event)" class="person-icon"/>
+ </textbox>
+ </listcell>
+ </listitem>
+ </listbox>
+</vbox>
+
+</overlay>
+
diff --git a/comm/suite/mailnews/components/addrbook/content/abMailListDialog.xul b/comm/suite/mailnews/components/addrbook/content/abMailListDialog.xul
new file mode 100644
index 0000000000..f335bfdce3
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abMailListDialog.xul
@@ -0,0 +1,34 @@
+<?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://messenger/skin/" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abListOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abMailListDialog.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="ablistWindow"
+ title="&mailListWindow.title;"
+ ondialogaccept="return MailListOKButton();"
+ onload="OnLoadNewMailList()"
+ ondragover="DragOverAddressListTree(event);"
+ ondrop="DropOnAddressListTree(event);">
+
+ <stringbundleset id="stringbundleset"/>
+
+ <hbox align="center" valign="center">
+ <label control="abPopup" value="&addToAddressBook.label;" accesskey="&addToAddressBook.accesskey;"/>
+ <menulist id="abPopup">
+ <menupopup id="abPopup-menupopup" class="addrbooksPopup" writable="true"
+ supportsmaillists="true"/>
+ </menulist>
+ </hbox>
+
+ <spacer style="height:1em"/>
+
+ <vbox id="editlist"/>
+
+</dialog>
diff --git a/comm/suite/mailnews/components/addrbook/content/abNewCardDialog.xul b/comm/suite/mailnews/components/addrbook/content/abNewCardDialog.xul
new file mode 100644
index 0000000000..80f4e578e3
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abNewCardDialog.xul
@@ -0,0 +1,35 @@
+<?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" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abCardOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abNewCardDialog.dtd">
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="abcardWindow"
+ windowtype="mailnews:newcarddialog"
+ onload="OnLoadNewCard()"
+ ondialogaccept="return NewCardOKButton();"
+ ondialogcancel="return NewCardCancelButton();">
+
+ <stringbundleset id="stringbundleset"/>
+
+ <hbox align="center">
+
+ <label id="abPopupLabel" control="abPopup" value="&chooseAddressBook.label;" accesskey="&chooseAddressBook.accesskey;"/>
+
+ <menulist id="abPopup">
+ <menupopup id="abPopup-menupopup" class="addrbooksPopup" writable="true"/>
+ </menulist>
+
+ </hbox>
+
+ <spacer style="height:1em"/>
+
+ <vbox id="editcard"/>
+
+</dialog>
diff --git a/comm/suite/mailnews/components/addrbook/content/abResultsPaneOverlay.xul b/comm/suite/mailnews/components/addrbook/content/abResultsPaneOverlay.xul
new file mode 100644
index 0000000000..08af2de3fc
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abResultsPaneOverlay.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/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/abResultsPane.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/abResultsPaneOverlay.dtd">
+
+<overlay
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://messenger/content/addressbook/abResultsPane.js"/>
+<script src="chrome://global/content/nsDragAndDrop.js"/>
+<script src="chrome://messenger/content/addressbook/abDragDrop.js"/>
+
+<tree id="abResultsTree" flex="1" enableColumnDrag="true" class="plain focusring"
+ onclick="AbResultsPaneOnClick(event);"
+ onselect="this.view.selectionChanged(); document.commandDispatcher.updateCommands('addrbook-select');"
+ sortCol="GeneratedName"
+ persist="sortCol height">
+
+ <treecols id="abResultsTreeCols">
+ <!-- these column ids must match up to the mork column names, except for GeneratedName and ChatName, see nsIAddrDatabase.idl -->
+ <treecol id="GeneratedName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&GeneratedName.label;" primary="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="PrimaryEmail"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&PrimaryEmail.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ChatName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&ChatName.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Company"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&Company.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="NickName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&NickName.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="SecondEmail"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&SecondEmail.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Department"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&Department.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="JobTitle"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&JobTitle.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="CellularNumber"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&CellularNumber.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="PagerNumber"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&PagerNumber.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="FaxNumber"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&FaxNumber.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="HomePhone"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&HomePhone.label;" hidden="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="WorkPhone"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&WorkPhone.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="addrbook"
+ persist="hidden ordinal width sortDirection" flex="1"
+ hidden="true"
+ label="&Addrbook.label;"/>
+ <!-- LOCALIZATION NOTE: _PhoneticName may be enabled for Japanese builds. -->
+ <!--
+ <splitter class="tree-splitter"/>
+ <treecol id="_PhoneticName"
+ persist="hidden ordinal width sortDirection" flex="1"
+ label="&_PhoneticName.label;" hidden="true"/>
+ -->
+
+ </treecols>
+ <treechildren ondragstart="nsDragAndDrop.startDrag(event, abResultsPaneObserver);"/>
+</tree>
+
+</overlay>
+
diff --git a/comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.js b/comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.js
new file mode 100644
index 0000000000..7e23a0b89d
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.js
@@ -0,0 +1,399 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {encodeABTermValue, getModelQuery} = ChromeUtils.import("resource:///modules/ABQueryUtils.jsm");
+
+var addressbook = 0;
+var composeWindow = 0;
+var msgCompFields = 0;
+var editCardCallback = 0;
+
+var gSearchInput;
+var gSearchTimer = null;
+var gQueryURIFormat = null;
+
+// localization strings
+var prefixTo;
+var prefixCc;
+var prefixBcc;
+
+var gToButton;
+var gCcButton;
+var gBccButton;
+
+var gActivatedButton;
+
+var gDragService = Cc["@mozilla.org/widget/dragservice;1"]
+ .getService(Ci.nsIDragService);
+
+var gSelectAddressesAbViewListener = {
+ onSelectionChanged: function() {
+ ResultsPaneSelectionChanged();
+ },
+ onCountChanged: function(total) {
+ // do nothing
+ }
+};
+
+function GetAbViewListener()
+{
+ return gSelectAddressesAbViewListener;
+}
+
+function OnLoadSelectAddress()
+{
+ InitCommonJS();
+
+ prefixTo = gAddressBookBundle.getString("prefixTo") + ": ";
+ prefixCc = gAddressBookBundle.getString("prefixCc") + ": ";
+ prefixBcc = gAddressBookBundle.getString("prefixBcc") + ": ";
+
+ UpgradeAddressBookResultsPaneUI("mailnews.ui.select_addresses_results.version");
+
+ var toAddress="", ccAddress="", bccAddress="";
+
+ // look in arguments[0] for parameters
+ if (window.arguments && window.arguments[0])
+ {
+ // keep parameters in global for later
+ if ( window.arguments[0].composeWindow )
+ top.composeWindow = window.arguments[0].composeWindow;
+ if ( window.arguments[0].msgCompFields )
+ top.msgCompFields = window.arguments[0].msgCompFields;
+ if ( window.arguments[0].toAddress )
+ toAddress = window.arguments[0].toAddress;
+ if ( window.arguments[0].ccAddress )
+ ccAddress = window.arguments[0].ccAddress;
+ if ( window.arguments[0].bccAddress )
+ bccAddress = window.arguments[0].bccAddress;
+
+ // put the addresses into the bucket
+ AddAddressFromComposeWindow(toAddress, prefixTo);
+ AddAddressFromComposeWindow(ccAddress, prefixCc);
+ AddAddressFromComposeWindow(bccAddress, prefixBcc);
+ }
+
+ gSearchInput = document.getElementById("searchInput");
+
+ // Reselect the persisted address book if possible, if not just select the
+ // first in the list.
+ var temp = abList.value;
+ abList.selectedItem = null;
+ abList.value = temp;
+ if (!abList.selectedItem)
+ abList.selectedIndex = 0;
+
+ ChangeDirectoryByURI(abList.value);
+
+ DialogBucketPaneSelectionChanged();
+
+ var workPhoneCol = document.getElementById("WorkPhone");
+ workPhoneCol.setAttribute("hidden", "true");
+
+ var companyCol = document.getElementById("Company");
+ companyCol.setAttribute("hidden", "true");
+
+ gToButton = document.getElementById("toButton");
+ gCcButton = document.getElementById("ccButton");
+ gBccButton = document.getElementById("bccButton");
+
+ gAbResultsTree.focus();
+
+ gActivatedButton = gToButton;
+
+ document.documentElement.addEventListener("keypress", OnReturnHit, true);
+}
+
+function OnUnloadSelectAddress()
+{
+ CloseAbView();
+}
+
+function AddAddressFromComposeWindow(addresses, prefix)
+{
+ if ( addresses )
+ {
+ var emails = {};
+ var names = {};
+ var fullNames = {};
+ var numAddresses = MailServices.headerParser.parseHeadersWithArray(addresses, emails, names, fullNames);
+
+ for ( var index = 0; index < numAddresses; index++ )
+ {
+ AddAddressIntoBucket(prefix, fullNames.value[index], emails.value[index]);
+ }
+ }
+}
+
+function SelectAddressOKButton()
+{
+ // Empty email checks are now done in AddAddressIntoBucket below.
+ var body = document.getElementById('bucketBody');
+ var item, row, cell, prefix, address, email;
+ var toAddress="", ccAddress="", bccAddress="", emptyEmail="";
+
+ for ( var index = 0; index < body.childNodes.length; index++ )
+ {
+ item = body.childNodes[index];
+ if ( item.childNodes && item.childNodes.length )
+ {
+ row = item.childNodes[0];
+ if ( row.childNodes && row.childNodes.length )
+ {
+ cell = row.childNodes[0];
+ prefix = cell.getAttribute('prefix');
+ address = cell.getAttribute('address');
+ email = cell.getAttribute('email');
+ if ( prefix )
+ {
+ switch ( prefix )
+ {
+ case prefixTo:
+ if ( toAddress )
+ toAddress += ", ";
+ toAddress += address;
+ break;
+ case prefixCc:
+ if ( ccAddress )
+ ccAddress += ", ";
+ ccAddress += address;
+ break;
+ case prefixBcc:
+ if ( bccAddress )
+ bccAddress += ", ";
+ bccAddress += address;
+ break;
+ }
+ }
+ }
+ }
+ }
+ // reset the UI in compose window
+ msgCompFields.to = toAddress;
+ msgCompFields.cc = ccAddress;
+ msgCompFields.bcc = bccAddress;
+ top.composeWindow.CompFields2Recipients(top.msgCompFields);
+
+ return true;
+}
+
+function SelectAddressToButton()
+{
+ AddSelectedAddressesIntoBucket(prefixTo);
+ gActivatedButton = gToButton;
+}
+
+function SelectAddressCcButton()
+{
+ AddSelectedAddressesIntoBucket(prefixCc);
+ gActivatedButton = gCcButton;
+}
+
+function SelectAddressBccButton()
+{
+ AddSelectedAddressesIntoBucket(prefixBcc);
+ gActivatedButton = gBccButton;
+}
+
+function AddSelectedAddressesIntoBucket(prefix)
+{
+ var cards = GetSelectedAbCards();
+ var count = cards.length;
+
+ for (var i = 0; i < count; i++) {
+ AddCardIntoBucket(prefix, cards[i]);
+ }
+}
+
+function AddCardIntoBucket(prefix, card)
+{
+ var address = GenerateAddressFromCard(card);
+ if (card.isMailList) {
+ AddAddressIntoBucket(prefix, address, card.displayName);
+ }
+ else {
+ AddAddressIntoBucket(prefix, address, card.primaryEmail);
+ }
+}
+
+function AddAddressIntoBucket(prefix, address, email)
+{
+ if (!email)
+ {
+ Services.prompt.alert(window,
+ gAddressBookBundle.getString("emptyEmailAddCardTitle"),
+ gAddressBookBundle.getString("emptyEmailAddCard"));
+ }
+ else
+ {
+ var body = document.getElementById("bucketBody");
+
+ var item = document.createElement('treeitem');
+ var row = document.createElement('treerow');
+ var cell = document.createElement('treecell');
+ cell.setAttribute('label', prefix + address);
+ cell.setAttribute('prefix', prefix);
+ cell.setAttribute('address', address);
+ cell.setAttribute('email', email);
+
+ row.appendChild(cell);
+ item.appendChild(row);
+ body.appendChild(item);
+ }
+}
+
+function RemoveSelectedFromBucket()
+{
+ var bucketTree = document.getElementById("addressBucket");
+ if ( bucketTree )
+ {
+ var body = document.getElementById("bucketBody");
+ var selection = bucketTree.view.selection;
+ var rangeCount = selection.getRangeCount();
+
+ for (var i = rangeCount-1; i >= 0; --i)
+ {
+ var start = {}, end = {};
+ selection.getRangeAt(i,start,end);
+ for (var j = end.value; j >= start.value; --j)
+ {
+ bucketTree.contentView.getItemAtIndex(j).remove();
+ }
+ }
+ }
+}
+
+/* Function: ResultsPaneSelectionChanged()
+ * Callers : OnLoadSelectAddress(), abCommon.js:ResultsPaneSelectionChanged()
+ * -------------------------------------------------------------------------
+ * This function is used to grab the selection state of the results tree to maintain
+ * the appropriate enabled/disabled states of the "Edit", "To:", "CC:", and "Bcc:" buttons.
+ * If an entry is selected in the results Tree, then the "disabled" attribute is removed.
+ * Otherwise, if nothing is selected, "disabled" is set to true.
+ */
+
+function ResultsPaneSelectionChanged()
+{;
+ var editButton = document.getElementById("edit");
+ var toButton = document.getElementById("toButton");
+ var ccButton = document.getElementById("ccButton");
+ var bccButton = document.getElementById("bccButton");
+
+ var numSelected = GetNumSelectedCards();
+ if (numSelected > 0)
+ {
+ if (numSelected == 1)
+ editButton.removeAttribute("disabled");
+ else
+ editButton.setAttribute("disabled", "true");
+
+ toButton.removeAttribute("disabled");
+ ccButton.removeAttribute("disabled");
+ bccButton.removeAttribute("disabled");
+ }
+ else
+ {
+ editButton.setAttribute("disabled", "true");
+ toButton.setAttribute("disabled", "true");
+ ccButton.setAttribute("disabled", "true");
+ bccButton.setAttribute("disabled", "true");
+ }
+}
+
+/* Function: DialogBucketPaneSelectionChanged()
+ * Callers : OnLoadSelectAddress(), abSelectAddressesDialog.xul:id="addressBucket"
+ * -------------------------------------------------------------------------------
+ * This function is used to grab the selection state of the bucket tree to maintain
+ * the appropriate enabled/disabled states of the "Remove" button.
+ * If an entry is selected in the bucket Tree, then the "disabled" attribute is removed.
+ * Otherwise, if nothing is selected, "disabled" is set to true.
+ */
+
+function DialogBucketPaneSelectionChanged()
+{
+ var bucketTree = document.getElementById("addressBucket");
+ var removeButton = document.getElementById("remove");
+
+ removeButton.disabled = bucketTree.view.selection.count == 0;
+}
+
+function AbResultsPaneDoubleClick(card)
+{
+ AddCardIntoBucket(prefixTo, card);
+}
+
+function OnClickedCard(card)
+{
+ // in the select address dialog, do nothing on click
+}
+
+function UpdateCardView()
+{
+ // in the select address dialog, do nothing
+}
+
+function DropRecipient(address)
+{
+ AddAddressFromComposeWindow(address, prefixTo);
+}
+
+function OnReturnHit(event)
+{
+ if (event.keyCode == 13) {
+ var focusedElement = document.commandDispatcher.focusedElement;
+ if (focusedElement && (focusedElement.id == "addressBucket"))
+ return;
+ event.stopPropagation();
+ if (focusedElement && (focusedElement.id == "abResultsTree"))
+ gActivatedButton.doCommand();
+ }
+}
+
+
+function onEnterInSearchBar()
+{
+ var selectedNode = abList.selectedItem;
+
+ if (!selectedNode)
+ return;
+
+ if (!gQueryURIFormat) {
+ // Get model query from pref. We don't want the query starting with "?"
+ // as we have to prefix "?and" to this format.
+ gQueryURIFormat = getModelQuery("mail.addr_book.quicksearchquery.format");
+ }
+
+ var searchURI = selectedNode.value;
+
+ // Use helper method to split up search query to multi-word search
+ // query against multiple fields.
+ let searchWords = getSearchTokens(gSearchInput.value);
+ searchURI += generateQueryURI(gQueryURIFormat, searchWords);
+
+ SetAbView(searchURI);
+
+ SelectFirstCard();
+}
+
+function DirPaneSelectionChangeMenulist()
+{
+ if (abList && abList.selectedItem) {
+ if (gSearchInput.value && (gSearchInput.value != ""))
+ onEnterInSearchBar();
+ else
+ ChangeDirectoryByURI(abList.value);
+ }
+
+ // Hide the addressbook column if the selected addressbook isn't
+ // "All address books". Since the column is redundant in all other cases.
+ let addrbookColumn = document.getElementById("addrbook");
+ if (abList.value.startsWith(kAllDirectoryRoot + "?")) {
+ addrbookColumn.hidden = !gShowAbColumnInComposeSidebar;
+ addrbookColumn.removeAttribute("ignoreincolumnpicker");
+ } else {
+ addrbookColumn.hidden = true;
+ addrbookColumn.setAttribute("ignoreincolumnpicker", "true");
+ }
+}
diff --git a/comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.xul b/comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.xul
new file mode 100644
index 0000000000..067139e7ec
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abSelectAddressesDialog.xul
@@ -0,0 +1,92 @@
+<?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://messenger/skin/addressbook/selectAddressesDialog.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abResultsPaneOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % abSelectAddressesDialogDTD SYSTEM "chrome://messenger/locale/addressbook/abSelectAddressesDialog.dtd" >
+%abSelectAddressesDialogDTD;
+<!ENTITY % abResultsPaneOverlayDTD SYSTEM "chrome://messenger/locale/addressbook/abResultsPaneOverlay.dtd" >
+%abResultsPaneOverlayDTD;
+]>
+
+<dialog id="selectAddressesWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&selectAddressWindow.title;"
+ style="width: 50em; height: 35em;"
+ persist="width height screenX screenY"
+ buttons="accept,cancel"
+ ondialogaccept="return SelectAddressOKButton();"
+ onload="OnLoadSelectAddress();"
+ onunload="OnUnloadSelectAddress();">
+
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ <stringbundle id="bundle_composeMsgs" src="chrome://messenger/locale/messengercompose/composeMsgs.properties"/>
+
+ <script src="chrome://messenger/content/addressbook/abCommon.js"/>
+ <script src="chrome://messenger/content/addressbook/abSelectAddressesDialog.js"/>
+ <script src="chrome://messenger/content/addressbook/abDragDrop.js"/>
+ <script src="chrome://messenger/content/messengercompose/MsgComposeCommands.js"/>
+ <script src="chrome://messenger/content/messengercompose/addressingWidgetOverlay.js"/>
+ <script src="chrome://global/content/globalOverlay.js"/>
+
+ <vbox flex="1">
+
+ <hbox id="topBox" align="center">
+ <label value="&lookIn.label;" accesskey="&lookIn.accesskey;" control="addressbookList"/>
+ <menulist id="addressbookList"
+ oncommand="DirPaneSelectionChangeMenulist(); document.commandDispatcher.updateCommands('addrbook-select');">
+ <menupopup id="addressbookList-menupopup" class="addrbooksPopup"/>
+ </menulist>
+ <label value="&for.label;" accesskey="&for.accesskey;" control="searchInput"/>
+ <textbox id="searchInput" flex="1" type="search"
+ aria-controls="abResultsTree"
+ placeholder="&for.placeholder;"
+ oncommand="onEnterInSearchBar();" clickSelectsAll="true"/>
+ </hbox>
+
+ <hbox flex="1">
+
+ <vbox id="resultsBox" flex="4">
+ <tree id="abResultsTree" flex="1" persist="height"/>
+ </vbox>
+
+ <vbox id="addToBucketButtonBox">
+ <spacer flex="1"/>
+ <button id="toButton" label="&toButton.label;" accesskey="&toButton.accesskey;" oncommand="SelectAddressToButton()"/>
+ <spacer class="middle-button-spacer"/>
+ <button id="ccButton" label="&ccButton.label;" accesskey="&ccButton.accesskey;" oncommand="SelectAddressCcButton()"/>
+ <spacer class="middle-button-spacer"/>
+ <button id="bccButton" label="&bccButton.label;" accesskey="&bccButton.accesskey;" oncommand="SelectAddressBccButton()"/>
+ <spacer class="above-remove-spacer"/>
+ <button id="remove" label="&removeButton.label;" accesskey="&removeButton.accesskey;" class="dialog" oncommand="RemoveSelectedFromBucket()"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ <vbox id="bucketBox" flex="1">
+ <label value="&addressMessageTo.label;" control="addressBucket"/>
+ <tree id="addressBucket" flex="1" hidecolumnpicker="true"
+ ondragover="DragAddressOverTargetControl(event);"
+ ondrop="DropOnAddressingTarget(event, false);"
+ onselect="DialogBucketPaneSelectionChanged();">
+ <treecols>
+ <treecol id="addressCol" flex="1" hideheader="true"/>
+ </treecols>
+ <treechildren id="bucketBody" flex="1"/>
+ </tree>
+ </vbox>
+
+ </hbox>
+
+ <hbox id="newEditButtonBox">
+ <button id="new" label="&newButton.label;" accesskey="&newButton.accesskey;" tooltiptext="&addressPickerNewButton.tooltip;" oncommand="AbNewCard();"/>
+ <button id="edit" label="&editButton.label;" accesskey="&editButton.accesskey;" tooltiptext="&addressPickerEditButton.tooltip;" oncommand="AbEditSelectedCard()"/>
+ </hbox>
+ </vbox>
+
+</dialog>
diff --git a/comm/suite/mailnews/components/addrbook/content/abTrees.js b/comm/suite/mailnews/components/addrbook/content/abTrees.js
new file mode 100644
index 0000000000..ac52d3464d
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/abTrees.js
@@ -0,0 +1,332 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file contains our implementation for various addressbook trees. It
+ * depends on jsTreeView.js being loaded before this script is loaded.
+ */
+
+const {IOUtils} = ChromeUtils.import("resource:///modules/IOUtils.js");
+
+// Tree Sort helper methods.
+var AB_ORDER = ["aab", "pab", "mork", "ldap", "mapi+other", "anyab", "cab"];
+
+function getDirectoryValue(aDir, aKey) {
+ if (aKey == "ab_type") {
+ if (aDir._directory.URI == kAllDirectoryRoot + "?")
+ return "aab";
+ if (aDir._directory.URI == kPersonalAddressbookURI)
+ return "pab";
+ if (aDir._directory.URI == kCollectedAddressbookURI)
+ return "cab";
+ if (aDir._directory instanceof Ci.nsIAbMDBDirectory)
+ return "mork";
+ if (aDir._directory instanceof Ci.nsIAbLDAPDirectory)
+ return "ldap";
+
+ // If there is any other AB type.
+ return "mapi+other";
+ } else if (aKey == "ab_name") {
+ return aDir._directory.dirName;
+ }
+
+ // This should never happen.
+ return null;
+}
+
+function abNameCompare(a, b) {
+ return a.localeCompare(b);
+}
+
+function abTypeCompare(a, b) {
+ return (AB_ORDER.indexOf(a) - AB_ORDER.indexOf(b));
+}
+
+var SORT_PRIORITY = ["ab_type", "ab_name"];
+var SORT_FUNCS = [abTypeCompare, abNameCompare];
+
+function abSort(a, b) {
+ for (let i = 0; i < SORT_FUNCS.length; i++) {
+ let sortBy = SORT_PRIORITY[i];
+ let aValue = getDirectoryValue(a, sortBy);
+ let bValue = getDirectoryValue(b, sortBy);
+
+ if (!aValue && !bValue)
+ return 0;
+ if (!aValue)
+ return -1;
+ if (!bValue)
+ return 1;
+ if (aValue != bValue) {
+ let result = SORT_FUNCS[i](aValue, bValue);
+
+ if (result != 0)
+ return result;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Each abDirTreeItem corresponds to one row in the tree view.
+ */
+function abDirTreeItem(aDirectory)
+{
+ this._directory = aDirectory;
+}
+
+abDirTreeItem.prototype =
+{
+ getText: function atv_getText()
+ {
+ return this._directory.dirName;
+ },
+
+ get id()
+ {
+ return this._directory.URI;
+ },
+
+ _open: false,
+ get open()
+ {
+ return this._open;
+ },
+
+ _level: 0,
+ get level()
+ {
+ return this._level;
+ },
+
+ _children: null,
+ get children()
+ {
+ if (!this._children)
+ {
+ this._children = [];
+ let myEnum;
+ if (this._directory.URI == (kAllDirectoryRoot + "?"))
+ myEnum = MailServices.ab.directories;
+ else
+ myEnum = this._directory.childNodes;
+
+ while (myEnum.hasMoreElements())
+ {
+ var abItem = new abDirTreeItem(
+ myEnum.getNext().QueryInterface(Ci.nsIAbDirectory));
+ if (gDirectoryTreeView&&
+ this.id == kAllDirectoryRoot + "?" &&
+ getDirectoryValue(abItem, "ab_type") == "ldap")
+ gDirectoryTreeView.hasRemoteAB = true;
+
+ abItem._level = this._level + 1;
+ abItem._parent = this;
+ this._children.push(abItem);
+ }
+
+ this._children.sort(abSort);
+ }
+ return this._children;
+ },
+
+ getProperties: function atv_getProps()
+ {
+ var properties = []
+ if (this._directory.isMailList)
+ properties.push("IsMailList-true");
+ if (this._directory.isRemote)
+ properties.push("IsRemote-true");
+ if (this._directory.isSecure)
+ properties.push("IsSecure-true");
+ return properties.join(" ");
+ }
+};
+
+/**
+ * Our actual implementation of nsITreeView.
+ */
+function directoryTreeView() {}
+directoryTreeView.prototype =
+{
+ __proto__: new PROTO_TREE_VIEW(),
+
+ hasRemoteAB: false,
+
+ init: function dtv_init(aTree, aJSONFile)
+ {
+ if (aJSONFile) {
+ // Parse our persistent-open-state json file
+ let data = IOUtils.loadFileToString(aJSONFile);
+ if (data) {
+ this._persistOpenMap = JSON.parse(data);
+ }
+ }
+
+ this._rebuild();
+ aTree.view = this;
+ },
+
+ shutdown: function dtv_shutdown(aJSONFile)
+ {
+ // Write out the persistOpenMap to our JSON file.
+ if (aJSONFile)
+ {
+ // Write out our json file...
+ let data = JSON.stringify(this._persistOpenMap);
+ IOUtils.saveStringToFile(aJSONFile, data);
+ }
+ },
+
+ // Override the dnd methods for those functions in abDragDrop.js
+ canDrop: function dtv_canDrop(aIndex, aOrientation, dataTransfer)
+ {
+ return abDirTreeObserver.canDrop(aIndex, aOrientation, dataTransfer);
+ },
+
+ drop: function dtv_drop(aRow, aOrientation, dataTransfer)
+ {
+ abDirTreeObserver.onDrop(aRow, aOrientation, dataTransfer);
+ },
+
+ getDirectoryAtIndex: function dtv_getDirForIndex(aIndex)
+ {
+ return this._rowMap[aIndex]._directory;
+ },
+
+ getIndexOfDirectory: function dtv_getIndexOfDir(aItem)
+ {
+ for (var i = 0; i < this._rowMap.length; i++)
+ if (this._rowMap[i]._directory == aItem)
+ return i;
+
+ return -1;
+ },
+
+ // Override jsTreeView's isContainer, since we want to be able
+ // to react to drag-drop events for all items in the directory
+ // tree.
+ isContainer: function dtv_isContainer(aIndex)
+ {
+ return true;
+ },
+
+ /**
+ * NOTE: This function will result in indeterminate rows being selected.
+ * Callers should take care to re-select a desired row after calling
+ * this function.
+ */
+ _rebuild: function dtv__rebuild() {
+ this._rowMap = [];
+
+ // Make an entry for All Address Books.
+ let rootAB = MailServices.ab.getDirectory(kAllDirectoryRoot + "?");
+ rootAB.dirName = gAddressBookBundle.getString("allAddressBooks");
+ this._rowMap.push(new abDirTreeItem(rootAB));
+
+ // Sort our addressbooks now.
+ this._rowMap.sort(abSort);
+
+ this._restoreOpenStates();
+ },
+
+ getIndexForId: function(aId) {
+ for (let i = 0; i < this._rowMap.length; i++) {
+ if (this._rowMap[i].id == aId)
+ return i;
+ }
+
+ return -1;
+ },
+
+ // nsIAbListener interfaces
+ onItemAdded: function dtv_onItemAdded(aParent, aItem)
+ {
+ if (!(aItem instanceof Ci.nsIAbDirectory))
+ return;
+
+ var oldCount = this._rowMap.length;
+ var tree = this._tree;
+ this._tree = null;
+ this._rebuild();
+ if (!tree)
+ return;
+
+ this._tree = tree;
+ var itemIndex = this.getIndexOfDirectory(aItem);
+ tree.rowCountChanged(itemIndex, this._rowMap.length - oldCount);
+ var parentIndex = this.getIndexOfDirectory(aParent);
+ if (parentIndex > -1)
+ tree.invalidateRow(parentIndex);
+ },
+
+ onItemRemoved: function dtv_onItemRemoved(aParent, aItem)
+ {
+ if (!(aItem instanceof Ci.nsIAbDirectory))
+ return;
+
+ var itemIndex = this.getIndexOfDirectory(aItem);
+ var oldCount = this._rowMap.length;
+ var tree = this._tree;
+ this._tree = null;
+ this._rebuild();
+ if (!tree)
+ return;
+
+ this._tree = tree;
+ tree.rowCountChanged(itemIndex, this._rowMap.length - oldCount);
+
+ // This does not currently work, see Bug 1323563.
+ // If we're deleting a top-level address-book, just select the first book.
+ // if (aParent.URI == kAllDirectoryRoot ||
+ // aParent.URI == kAllDirectoryRoot + "?") {
+ // this.selection.select(0);
+ // return;
+ // }
+
+ var parentIndex = this.getIndexOfDirectory(aParent);
+ if (parentIndex > -1)
+ tree.invalidateRow(parentIndex);
+
+ if (!this.selection.count)
+ {
+ // The previously selected item was a member of the deleted subtree.
+ // Select the parent of the subtree.
+ // If there is no parent, select the next item.
+ // If there is no next item, select the first item.
+ var newIndex = parentIndex;
+ if (newIndex < 0)
+ newIndex = itemIndex;
+ if (newIndex >= this._rowMap.length)
+ newIndex = 0;
+
+ this.selection.select(newIndex);
+ }
+ },
+
+ onItemPropertyChanged: function dtv_onItemProp(aItem, aProp, aOld, aNew)
+ {
+ if (!(aItem instanceof Ci.nsIAbDirectory))
+ return;
+
+ var index = this.getIndexOfDirectory(aItem);
+ var current = this.getDirectoryAtIndex(this.selection.currentIndex);
+ var tree = this._tree;
+ this._tree = null;
+ this._rebuild();
+ this._tree = tree;
+ this.selection.select(this.getIndexOfDirectory(current));
+
+ if (index > -1) {
+ var newIndex = this.getIndexOfDirectory(aItem);
+ if (newIndex >= index)
+ this._tree.invalidateRange(index, newIndex);
+ else
+ this._tree.invalidateRange(newIndex, index);
+ }
+ }
+};
+
+var gDirectoryTreeView = new directoryTreeView();
diff --git a/comm/suite/mailnews/components/addrbook/content/addressbook-panel.js b/comm/suite/mailnews/components/addrbook/content/addressbook-panel.js
new file mode 100644
index 0000000000..9fa0a125e0
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/addressbook-panel.js
@@ -0,0 +1,119 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gIsMsgCompose = false;
+
+function GetAbViewListener()
+{
+ // the ab panel doesn't care if the total changes, or if the selection changes
+ return null;
+}
+
+var mutationObs = null;
+
+function AbPanelLoad()
+{
+ InitCommonJS();
+
+ UpgradeAddressBookResultsPaneUI("mailnews.ui.addressbook_panel_results.version");
+
+ var abPopup = document.getElementById('addressbookList');
+
+ // Reselect the persisted address book if possible, if not just select the
+ // first in the list.
+ var temp = abPopup.value;
+ abPopup.selectedItem = null;
+ abPopup.value = temp;
+ if (!abPopup.selectedItem)
+ abPopup.selectedIndex = 0;
+
+ ChangeDirectoryByURI(abPopup.value);
+
+ mutationObs = new MutationObserver(function(aMutations) {
+ aMutations.forEach(function(mutation) {
+ if (getSelectedDirectoryURI() == (kAllDirectoryRoot + "?") &&
+ mutation.type == "attributes" &&
+ mutation.attributeName == "hidden") {
+ let curState = document.getElementById("addrbook").hidden;
+ gShowAbColumnInComposeSidebar = !curState;
+ }
+ });
+ });
+
+ document.getElementById("addrbook").hidden = !gShowAbColumnInComposeSidebar;
+
+ mutationObs.observe(document.getElementById("addrbook"),
+ { attributes: true, childList: true });
+
+ gSearchInput = document.getElementById("searchInput");
+
+ // for the compose window we want to show To, Cc, Bcc and a separator
+ // for all other windows we want to show Compose Mail To
+ var popup = document.getElementById("composeMail");
+ gIsMsgCompose = parent.document
+ .documentElement
+ .getAttribute("windowtype") == "msgcompose";
+ for (var i = 0; i < 4; i++)
+ popup.childNodes[i].hidden = !gIsMsgCompose;
+ popup.childNodes[4].hidden = gIsMsgCompose;
+}
+
+function AbPanelUnload()
+{
+ mutationObs.disconnect();
+
+ CloseAbView();
+}
+
+function AbPanelAdd(addrtype)
+{
+ var cards = GetSelectedAbCards();
+ var count = cards.length;
+
+ for (var i = 0; i < count; i++) {
+ // turn each card into a properly formatted address
+ var address = GenerateAddressFromCard(cards[i]);
+ if (address)
+ top.awAddRecipient(addrtype, address);
+ else
+ Services.prompt.alert(window,
+ gAddressBookBundle.getString("emptyEmailAddCardTitle"),
+ gAddressBookBundle.getString("emptyEmailAddCard"));
+ }
+}
+
+function AbPanelNewCard()
+{
+ goNewCardDialog(abList.value);
+}
+
+function AbPanelNewList()
+{
+ goNewListDialog(abList.value);
+}
+
+function ResultsPaneSelectionChanged()
+{
+ // do nothing for ab panel
+}
+
+function OnClickedCard()
+{
+ // do nothing for ab panel
+}
+
+function AbResultsPaneDoubleClick(card)
+{
+ // double click for ab panel means "send mail to this person / list"
+ if (gIsMsgCompose)
+ AbPanelAdd('addr_to');
+ else
+ AbNewMessage();
+}
+
+function UpdateCardView()
+{
+ // do nothing for ab panel
+}
diff --git a/comm/suite/mailnews/components/addrbook/content/addressbook-panel.xul b/comm/suite/mailnews/components/addrbook/content/addressbook-panel.xul
new file mode 100644
index 0000000000..72f650ffdb
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/addressbook-panel.xul
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<!-- -*- Mode: xml; indent-tabs-mode: nil; -*-
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/sidebarPanel.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/addressPanes.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/abResultsPane.css" type="text/css"?>
+
+<!DOCTYPE page [
+<!ENTITY % abSelectAddressesDialogDTD SYSTEM "chrome://messenger/locale/addressbook/abSelectAddressesDialog.dtd" >
+%abSelectAddressesDialogDTD;
+<!ENTITY % abResultsPaneOverlayDTD SYSTEM "chrome://messenger/locale/addressbook/abResultsPaneOverlay.dtd" >
+%abResultsPaneOverlayDTD;
+]>
+
+<page id="addressbook-panel"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="AbPanelLoad();"
+ onunload="AbPanelUnload();"
+ title="&selectAddressWindow.title;"
+ selectedaddresses="true">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://global/content/nsDragAndDrop.js"/>
+ <script src="chrome://messenger/content/addressbook/addressbook.js"/>
+ <script src="chrome://messenger/content/addressbook/abCommon.js"/>
+ <script src="chrome://messenger/content/addressbook/abDragDrop.js"/>
+ <script src="chrome://messenger/content/addressbook/abResultsPane.js"/>
+ <script src="chrome://messenger/content/addressbook/abSelectAddressesDialog.js"/>
+ <script src="chrome://messenger/content/addressbook/addressbook-panel.js"/>
+ <script src="chrome://communicator/content/utilityOverlay.js"/>
+
+ <commandset id="addressbook-panel-commandset">
+ <command id="cmd_newlist" oncommand="AbNewList();"/>
+ <command id="cmd_properties" oncommand="goDoCommand('cmd_properties');"/>
+ </commandset>
+
+ <menupopup id="composeMail" onpopupshowing="CommandUpdate_AddressBook();">
+ <menuitem label="&toButton.label;" accesskey="&toButton.accesskey;" oncommand="AbPanelAdd('addr_to');" default="true"/>
+ <menuitem label="&ccButton.label;" accesskey="&ccButton.accesskey;" oncommand="AbPanelAdd('addr_cc');"/>
+ <menuitem label="&bccButton.label;" accesskey="&bccButton.accesskey;" oncommand="AbPanelAdd('addr_bcc');"/>
+ <menuseparator/>
+ <menuitem label="&composeEmail.label;" accesskey="&composeEmail.accesskey;" oncommand="AbNewMessage();" default="true"/>
+ <menuitem label="&copyAddress.label;" accesskey="&copyAddress.accesskey;" oncommand="AbCopyAddress();"/>
+ <menuitem label="&deleteAddrBookCard.label;" accesskey="&deleteAddrBookCard.accesskey;" oncommand="AbDelete();"/>
+ <menuseparator/>
+ <menuitem label="&newAddrBookCard.label;" accesskey="&newAddrBookCard.accesskey;" oncommand="AbPanelNewCard();"/>
+ <menuitem label="&newAddrBookMailingList.label;" accesskey="&newAddrBookMailingList.accesskey;" command="cmd_newlist"/>
+ <menuseparator/>
+ <menuitem label="&addrBookCardProperties.label;" accesskey="&addrBookCardProperties.accesskey;" command="cmd_properties"/>
+ </menupopup>
+ <vbox id="results_box" flex="1">
+ <hbox id="panel-bar" class="toolbar" align="center">
+ <label value="&lookIn.label;" control="addressbookList" id="lookInLabel"/>
+ <menulist id="addressbookList"
+ oncommand="DirPaneSelectionChangeMenulist();" flex="1"
+ persist="value">
+ <menupopup id="addressbookList-menupopup" class="addrbooksPopup"/>
+ </menulist>
+ </hbox>
+ <hbox align="center">
+ <label value="&for.label;" id="forLabel" control="searchInput"/>
+ <textbox id="searchInput" flex="1" type="search"
+ aria-labelledby="lookInLabel addressbookList forLabel"
+ aria-controls="abResultsTree"
+ placeholder="&for.placeholder;"
+ oncommand="onEnterInSearchBar();" clickSelectsAll="true"/>
+ </hbox>
+
+ <tree id="abResultsTree" flex="1" context="composeMail" onclick="AbResultsPaneOnClick(event);" class="plain"
+ sortCol="GeneratedName" persist="sortCol">
+ <treecols>
+ <!-- these column ids must match up to the mork column names, see nsIAddrDatabase.idl -->
+ <treecol id="GeneratedName"
+ persist="hidden ordinal width sortDirection" flex="1" label="&GeneratedName.label;" primary="true"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="addrbook"
+ persist="hidden ordinal width sortDirection" hidden="true"
+ flex="1" label="&Addrbook.label;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="PrimaryEmail"
+ persist="hidden ordinal width sortDirection"
+ hiddenbydefault="true"
+ flex="1" label="&PrimaryEmail.label;"/>
+ </treecols>
+ <treechildren ondragstart="nsDragAndDrop.startDrag(event, abResultsPaneObserver);"/>
+</tree>
+
+ </vbox>
+</page>
diff --git a/comm/suite/mailnews/components/addrbook/content/addressbook.js b/comm/suite/mailnews/components/addrbook/content/addressbook.js
new file mode 100644
index 0000000000..e1bce6a868
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/addressbook.js
@@ -0,0 +1,581 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {encodeABTermValue, getModelQuery} = ChromeUtils.import("resource:///modules/ABQueryUtils.jsm");
+const {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const nsIAbListener = Ci.nsIAbListener;
+const kPrefMailAddrBookLastNameFirst = "mail.addr_book.lastnamefirst";
+const kPersistCollapseMapStorage = "directoryTree.json";
+
+var gSearchTimer = null;
+var gStatusText = null;
+var gQueryURIFormat = null;
+var gSearchInput;
+var gSearchBox;
+var gCardViewBox;
+var gCardViewBoxEmail1;
+
+var msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"]
+ .createInstance(Ci.nsIMsgWindow);
+
+// Constants that correspond to choices
+// in Address Book->View -->Show Name as
+const kDisplayName = 0;
+const kLastNameFirst = 1;
+const kFirstNameFirst = 2;
+const kLDAPDirectory = 0; // defined in nsDirPrefs.h
+const kPABDirectory = 2; // defined in nsDirPrefs.h
+
+function OnUnloadAddressBook()
+{
+ MailServices.ab.removeAddressBookListener(gDirectoryTreeView);
+
+ // Shutdown the tree view - this will also save the open/collapsed
+ // state of the tree view to a JSON file.
+ gDirectoryTreeView.shutdown(kPersistCollapseMapStorage);
+
+ MailServices.mailSession.RemoveMsgWindow(msgWindow);
+
+ CloseAbView();
+}
+
+var gAddressBookAbViewListener = {
+ onSelectionChanged: function() {
+ ResultsPaneSelectionChanged();
+ },
+ onCountChanged: function(total) {
+ SetStatusText(total);
+ }
+};
+
+function GetAbViewListener()
+{
+ return gAddressBookAbViewListener;
+}
+
+function OnLoadAddressBook()
+{
+ gSearchInput = document.getElementById("searchInput");
+
+ verifyAccounts(null, false); // this will do migration, if we need to.
+
+ InitCommonJS();
+
+ UpgradeAddressBookResultsPaneUI("mailnews.ui.addressbook_results.version");
+
+ GetCurrentPrefs();
+
+ // FIX ME - later we will be able to use onload from the overlay
+ OnLoadCardView();
+
+ // Before and after callbacks for the customizeToolbar code
+ var abToolbox = getAbToolbox();
+ abToolbox.customizeInit = AbToolboxCustomizeInit;
+ abToolbox.customizeDone = AbToolboxCustomizeDone;
+ abToolbox.customizeChange = AbToolboxCustomizeChange;
+
+ // Initialize the Address Book tree view
+ gDirectoryTreeView.init(gDirTree, kPersistCollapseMapStorage);
+
+ SelectFirstAddressBook();
+
+ // if the pref is locked disable the menuitem New->LDAP directory
+ if (Services.prefs.prefIsLocked("ldap_2.disable_button_add"))
+ document.getElementById("addLDAP").setAttribute("disabled", "true");
+
+ // Add a listener, so we can switch directories if the current directory is
+ // deleted. This listener cares when a directory (= address book), or a
+ // directory item is/are removed. In the case of directory items, we are
+ // only really interested in mailing list changes and not cards but we have
+ // to have both.
+ MailServices.ab.addAddressBookListener(gDirectoryTreeView, nsIAbListener.all);
+
+ gDirTree.controllers.appendController(DirPaneController);
+
+ // Ensure we don't load xul error pages into the main window
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .useErrorPages = false;
+
+ MailServices.mailSession.AddMsgWindow(msgWindow);
+}
+
+function GetCurrentPrefs()
+{
+ // check "Show Name As" menu item based on pref
+ var menuitemID;
+ switch (Services.prefs.getIntPref(kPrefMailAddrBookLastNameFirst))
+ {
+ case kFirstNameFirst:
+ menuitemID = 'firstLastCmd';
+ break;
+ case kLastNameFirst:
+ menuitemID = 'lastFirstCmd';
+ break;
+ case kDisplayName:
+ default:
+ menuitemID = 'displayNameCmd';
+ break;
+ }
+
+ var menuitem = top.document.getElementById(menuitemID);
+ if ( menuitem )
+ menuitem.setAttribute('checked', 'true');
+
+ // show phonetic fields if indicated by the pref
+ if (GetLocalizedStringPref("mail.addr_book.show_phonetic_fields") == "true")
+ document.getElementById("cmd_SortBy_PhoneticName")
+ .setAttribute("hidden", "false");
+}
+
+function SetNameColumn(cmd)
+{
+ var prefValue;
+
+ switch (cmd)
+ {
+ case 'firstLastCmd':
+ prefValue = kFirstNameFirst;
+ break;
+ case 'lastFirstCmd':
+ prefValue = kLastNameFirst;
+ break;
+ case 'displayNameCmd':
+ prefValue = kDisplayName;
+ break;
+ }
+
+ Services.prefs.setIntPref(kPrefMailAddrBookLastNameFirst, prefValue);
+}
+
+function CommandUpdate_AddressBook()
+{
+ goUpdateCommand('cmd_delete');
+ goUpdateCommand('button_delete');
+ goUpdateCommand('cmd_printcardpreview');
+ goUpdateCommand('cmd_printcard');
+ goUpdateCommand('cmd_print');
+ goUpdateCommand('cmd_properties');
+ goUpdateCommand('cmd_newlist');
+}
+
+function ResultsPaneSelectionChanged()
+{
+ UpdateCardView();
+}
+
+function UpdateCardView()
+{
+ var cards = GetSelectedAbCards();
+
+ // display the selected card, if exactly one card is selected.
+ // either no cards, or more than one card is selected, clear the pane.
+ if (cards.length == 1)
+ OnClickedCard(cards[0])
+ else
+ ClearCardViewPane();
+}
+
+function OnClickedCard(card)
+{
+ if (card)
+ DisplayCardViewPane(card);
+ else
+ ClearCardViewPane();
+}
+
+function AbClose()
+{
+ top.close();
+}
+
+function AbPrintCardInternal(doPrintPreview, msgType)
+{
+ var selectedItems = GetSelectedAbCards();
+ var numSelected = selectedItems.length;
+
+ if (!numSelected)
+ return;
+
+ let statusFeedback;
+ statusFeedback = Cc["@mozilla.org/messenger/statusfeedback;1"].createInstance();
+ statusFeedback = statusFeedback.QueryInterface(Ci.nsIMsgStatusFeedback);
+
+ let selectionArray = [];
+
+ for (let i = 0; i < numSelected; i++) {
+ let card = selectedItems[i];
+ let printCardUrl = CreatePrintCardUrl(card);
+ if (printCardUrl) {
+ selectionArray.push(printCardUrl);
+ }
+ }
+
+ printEngineWindow = window.openDialog("chrome://messenger/content/msgPrintEngine.xul",
+ "",
+ "chrome,dialog=no,all",
+ selectionArray.length, selectionArray,
+ statusFeedback, doPrintPreview, msgType);
+
+ return;
+}
+
+function AbPrintCard()
+{
+ AbPrintCardInternal(false, Ci.nsIMsgPrintEngine.MNAB_PRINT_AB_CARD);
+}
+
+function AbPrintPreviewCard()
+{
+ AbPrintCardInternal(true, Ci.nsIMsgPrintEngine.MNAB_PRINTPREVIEW_AB_CARD);
+}
+
+function CreatePrintCardUrl(card)
+{
+ return "data:application/xml;base64," + card.translateTo("base64xml");
+}
+
+function AbPrintAddressBookInternal(doPrintPreview, msgType)
+{
+ let uri = getSelectedDirectoryURI();
+ if (!uri)
+ return;
+
+ var statusFeedback;
+ statusFeedback = Cc["@mozilla.org/messenger/statusfeedback;1"].createInstance();
+ statusFeedback = statusFeedback.QueryInterface(Ci.nsIMsgStatusFeedback);
+
+ /*
+ turn "moz-abmdbdirectory://abook.mab" into
+ "addbook://moz-abmdbdirectory/abook.mab?action=print"
+ */
+
+ var abURIArr = uri.split("://");
+ var printUrl = "addbook://" + abURIArr[0] + "/" + abURIArr[1] + "?action=print"
+
+ printEngineWindow = window.openDialog("chrome://messenger/content/msgPrintEngine.xul",
+ "",
+ "chrome,dialog=no,all",
+ 1, [printUrl],
+ statusFeedback, doPrintPreview, msgType);
+
+ return;
+}
+
+function AbPrintAddressBook()
+{
+ AbPrintAddressBookInternal(false, Ci.nsIMsgPrintEngine.MNAB_PRINT_ADDRBOOK);
+}
+
+function AbPrintPreviewAddressBook()
+{
+ AbPrintAddressBookInternal(true, Ci.nsIMsgPrintEngine.MNAB_PRINTPREVIEW_ADDRBOOK);
+}
+
+/**
+ * Export the currently selected addressbook.
+ */
+function AbExportSelection() {
+ let selectedDirURI = getSelectedDirectoryURI();
+ if (!selectedDirURI)
+ return;
+
+ if (selectedDirURI == (kAllDirectoryRoot + "?"))
+ AbExportAll();
+ else
+ AbExport(selectedDirURI);
+}
+
+/**
+ * Export all found addressbooks, each in a separate file.
+ */
+function AbExportAll()
+{
+ let directories = MailServices.ab.directories;
+
+ while (directories.hasMoreElements()) {
+ let directory = directories.getNext();
+ // Do not export LDAP ABs.
+ if (directory.URI.startsWith(kLdapUrlPrefix))
+ continue;
+
+ AbExport(directory.URI);
+ }
+}
+
+/**
+ * Export the specified addressbook to a file.
+ *
+ * @param aSelectedDirURI The URI of the addressbook to export.
+ */
+function AbExport(aSelectedDirURI)
+{
+ if (!aSelectedDirURI)
+ return;
+
+ try {
+ let directory = GetDirectoryFromURI(aSelectedDirURI);
+ MailServices.ab.exportAddressBook(window, directory);
+ }
+ catch (ex) {
+ var message;
+ switch (ex.result) {
+ case Cr.NS_ERROR_FILE_ACCESS_DENIED:
+ message = gAddressBookBundle.getString("failedToExportMessageFileAccessDenied");
+ break;
+ case Cr.NS_ERROR_FILE_NO_DEVICE_SPACE:
+ message = gAddressBookBundle.getString("failedToExportMessageNoDeviceSpace");
+ break;
+ default:
+ message = ex.message;
+ break;
+ }
+
+ Services.prompt.alert(window,
+ gAddressBookBundle.getString("failedToExportTitle"),
+ message);
+ }
+}
+
+function SetStatusText(total)
+{
+ if (!gStatusText)
+ gStatusText = document.getElementById('statusText');
+
+ try {
+ let statusText;
+
+ if (gSearchInput.value) {
+ if (total == 0) {
+ statusText = gAddressBookBundle.getString("noMatchFound");
+ } else {
+ statusText = PluralForm
+ .get(total, gAddressBookBundle.getString("matchesFound1"))
+ .replace("#1", total);
+ }
+ }
+ else
+ statusText =
+ gAddressBookBundle.getFormattedString(
+ "totalContactStatus",
+ [getSelectedDirectory().dirName, total]);
+
+ gStatusText.setAttribute("label", statusText);
+ }
+ catch(ex) {
+ dump("failed to set status text: " + ex + "\n");
+ }
+}
+
+function AbResultsPaneDoubleClick(card)
+{
+ AbEditCard(card);
+}
+
+function onAdvancedAbSearch()
+{
+ let selectedDirURI = getSelectedDirectoryURI();
+ if (!selectedDirURI)
+ return;
+
+ var existingSearchWindow = Services.wm.getMostRecentWindow("mailnews:absearch");
+ if (existingSearchWindow)
+ existingSearchWindow.focus();
+ else
+ window.openDialog("chrome://messenger/content/ABSearchDialog.xul", "",
+ "chrome,resizable,status,centerscreen,dialog=no",
+ {directory: selectedDirURI});
+}
+
+function onEnterInSearchBar()
+{
+ ClearCardViewPane();
+ if (!gQueryURIFormat) {
+ // Get model query from pref. We don't want the query starting with "?"
+ // as we have to prefix "?and" to this format.
+ gQueryURIFormat = getModelQuery("mail.addr_book.quicksearchquery.format");
+ }
+
+ let searchURI = getSelectedDirectoryURI();
+ if (!searchURI) return;
+
+ /*
+ XXX todo, handle the case where the LDAP url
+ already has a query, like
+ moz-abldapdirectory://nsdirectory.netscape.com:389/ou=People,dc=netscape,dc=com?(or(Department,=,Applications))
+ */
+ // Use helper method to split up search query to multi-word search
+ // query against multiple fields.
+ let searchWords = getSearchTokens(gSearchInput.value);
+ searchURI += generateQueryURI(gQueryURIFormat, searchWords);
+
+ if (searchURI == kAllDirectoryRoot)
+ searchURI += "?";
+
+ document.getElementById("localResultsOnlyMessage")
+ .setAttribute("hidden",
+ !gDirectoryTreeView.hasRemoteAB ||
+ searchURI != kAllDirectoryRoot + "?");
+
+ SetAbView(searchURI);
+
+ // XXX todo
+ // this works for synchronous searches of local addressbooks,
+ // but not for LDAP searches
+ SelectFirstCard();
+}
+
+function SwitchPaneFocus(event)
+{
+ var focusedElement = WhichPaneHasFocus();
+ var cardViewBox = GetCardViewBox();
+ var cardViewBoxEmail1 = GetCardViewBoxEmail1();
+ var searchBox = GetSearchBox();
+ var dirTree = GetDirTree();
+
+ if (event && event.shiftKey)
+ {
+ if (focusedElement == gAbResultsTree && searchBox.getAttribute('hidden') != 'true')
+ searchInput.focus();
+ else if ((focusedElement == gAbResultsTree || focusedElement == searchBox) && !IsDirPaneCollapsed())
+ dirTree.focus();
+ else if (focusedElement != cardViewBox && !IsCardViewAndAbResultsPaneSplitterCollapsed())
+ {
+ if(cardViewBoxEmail1)
+ cardViewBoxEmail1.focus();
+ else
+ cardViewBox.focus();
+ }
+ else
+ gAbResultsTree.focus();
+ }
+ else
+ {
+ if (focusedElement == searchBox)
+ gAbResultsTree.focus();
+ else if (focusedElement == gAbResultsTree && !IsCardViewAndAbResultsPaneSplitterCollapsed())
+ {
+ if(cardViewBoxEmail1)
+ cardViewBoxEmail1.focus();
+ else
+ cardViewBox.focus();
+ }
+ else if (focusedElement != dirTree && !IsDirPaneCollapsed())
+ dirTree.focus();
+ else if (searchBox.getAttribute('hidden') != 'true')
+ gSearchInput.focus();
+ else
+ gAbResultsTree.focus();
+ }
+}
+
+function WhichPaneHasFocus()
+{
+ var cardViewBox = GetCardViewBox();
+ var searchBox = GetSearchBox();
+ var dirTree = GetDirTree();
+
+ var currentNode = top.document.commandDispatcher.focusedElement;
+ while (currentNode)
+ {
+ var nodeId = currentNode.getAttribute('id');
+
+ if(currentNode == gAbResultsTree ||
+ currentNode == cardViewBox ||
+ currentNode == searchBox ||
+ currentNode == dirTree)
+ return currentNode;
+
+ currentNode = currentNode.parentNode;
+ }
+
+ return null;
+}
+
+function GetDirTree()
+{
+ if (!gDirTree)
+ gDirTree = document.getElementById('dirTree');
+ return gDirTree;
+}
+
+function GetSearchBox()
+{
+ if (!gSearchBox)
+ gSearchBox = document.getElementById('searchBox');
+ return gSearchBox;
+}
+
+function GetCardViewBox()
+{
+ if (!gCardViewBox)
+ gCardViewBox = document.getElementById('CardViewBox');
+ return gCardViewBox;
+}
+
+function GetCardViewBoxEmail1()
+{
+ if (!gCardViewBoxEmail1)
+ {
+ try {
+ gCardViewBoxEmail1 = document.getElementById('cvEmail1');
+ }
+ catch (ex) {
+ gCardViewBoxEmail1 = null;
+ }
+ }
+ return gCardViewBoxEmail1;
+}
+
+function IsDirPaneCollapsed()
+{
+ var dirPaneBox = GetDirTree().parentNode;
+ return dirPaneBox.getAttribute("collapsed") == "true" ||
+ dirPaneBox.getAttribute("hidden") == "true";
+}
+
+function IsCardViewAndAbResultsPaneSplitterCollapsed()
+{
+ var cardViewBox = document.getElementById('CardViewOuterBox');
+ try {
+ return (cardViewBox.getAttribute("collapsed") == "true");
+ }
+ catch (ex) {
+ return false;
+ }
+}
+
+function LaunchUrl(url)
+{
+ // Doesn't matter if this bit fails, window.location contains its own prompts
+ try {
+ window.location = url;
+ }
+ catch (ex) {}
+}
+
+function getAbToolbox()
+{
+ return document.getElementById("ab-toolbox");
+}
+
+function AbToolboxCustomizeInit()
+{
+ toolboxCustomizeInit("ab-menubar");
+}
+
+function AbToolboxCustomizeDone(aToolboxChanged)
+{
+ toolboxCustomizeDone("ab-menubar", getAbToolbox(), aToolboxChanged);
+}
+
+function AbToolboxCustomizeChange(event)
+{
+ toolboxCustomizeChange(getAbToolbox(), event);
+}
diff --git a/comm/suite/mailnews/components/addrbook/content/addressbook.xul b/comm/suite/mailnews/components/addrbook/content/addressbook.xul
new file mode 100644
index 0000000000..698fbe62c5
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/addressbook.xul
@@ -0,0 +1,722 @@
+<?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://messenger/skin/addressbook/addressbook.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/addressbook/addressPanes.css"
+ type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/contentAreaContextOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/addressbook/abResultsPaneOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % abMainWindowDTD SYSTEM "chrome://messenger/locale/addressbook/abMainWindow.dtd" >
+%abMainWindowDTD;
+<!ENTITY % abResultsPaneOverlayDTD SYSTEM "chrome://messenger/locale/addressbook/abResultsPaneOverlay.dtd" >
+%abResultsPaneOverlayDTD;
+<!ENTITY % mailOverlayDTD SYSTEM "chrome://messenger/locale/mailOverlay.dtd">
+%mailOverlayDTD;
+<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
+%messengerDTD;
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+]>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ id="addressbookWindow"
+ height="450"
+ width="750"
+ title="&addressbookWindow.title;"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ windowtype="mail:addressbook"
+ macanimationtype="document"
+ drawtitle="true"
+ persist="width height screenX screenY sizemode"
+ toggletoolbar="true"
+ onload="OnLoadAddressBook()"
+ onunload="OnUnloadAddressBook()">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ </stringbundleset>
+
+<script src="chrome://messenger/content/jsTreeView.js"/>
+<script src="chrome://messenger/content/addressbook/abTrees.js"/>
+<script src="chrome://messenger/content/accountUtils.js"/>
+<script src="chrome://messenger/content/addressbook/addressbook.js"/>
+<script src="chrome://messenger/content/addressbook/abCommon.js"/>
+<script src="chrome://communicator/content/contentAreaClick.js"/>
+<script src="chrome://global/content/printUtils.js"/>
+<script src="chrome://messenger/content/msgPrintEngine.js"/>
+<script src="chrome://messenger/content/addressbook/abCardViewOverlay.js"/>
+
+ <commandset id="addressBook">
+ <commandset id="CommandUpdate_AddressBook"
+ commandupdater="true"
+ events="focus,addrbook-select"
+ oncommandupdate="CommandUpdate_AddressBook()"/>
+ <commandset id="selectEditMenuItems"/>
+ <commandset id="undoEditMenuItems"/>
+ <commandset id="globalEditMenuItems"/>
+ <command id="cmd_newNavigator"/>
+ <command id="cmd_newPrivateWindow"/>
+ <command id="cmd_newEditor"/>
+ <command id="cmd_newlist" oncommand="AbNewList();"/>
+ <command id="cmd_newCard" oncommand="goDoCommand('cmd_newCard');"/>
+ <command id="cmd_newMessage" oncommand="AbNewMessage();"/>
+ <command id="cmd_printSetup" oncommand="PrintUtils.showPageSetup()"/>
+ <command id="cmd_print" oncommand="AbPrintCard();"/>
+ <command id="cmd_printpreview" oncommand="AbPrintPreviewCard();"/>
+ <command id="cmd_printcard" oncommand="AbPrintCard()"/>
+ <command id="cmd_printcardpreview" oncommand="AbPrintPreviewCard()"/>
+ <command id="cmd_printAddressBook" oncommand="AbPrintAddressBook()"/>
+ <command id="cmd_printPreviewAddressBook" oncommand="AbPrintPreviewAddressBook()"/>
+ <command id="cmd_close" oncommand="AbClose()"/>
+ <command id="cmd_properties" oncommand="goDoCommand('cmd_properties');"/>
+ <command id="cmd_undo"/>
+ <command id="cmd_redo"/>
+ <command id="cmd_copy"/>
+ <command id="cmd_paste"/>
+ <command id="cmd_cut"/>
+ <command id="cmd_delete"
+ valueAddressBook="&deleteAbCmd.label;"
+ valueCard="&deleteContactCmd.label;"
+ valueCards="&deleteContactsCmd.label;"
+ valueList="&deleteListCmd.label;"
+ valueLists="&deleteListsCmd.label;"
+ valueItems="&deleteItemsCmd.label;"/>
+ <command id="cmd_selectAll"/>
+ <command id="button_delete" oncommand="goDoCommand('button_delete');"/>
+ <command id="cmd_swapFirstNameLastName" oncommand="AbSwapFirstNameLastName()"/>
+ <commandset id="tasksCommands"/>
+ </commandset>
+
+<broadcasterset id="abBroadcasters">
+ <broadcaster id="Communicator:WorkMode"/>
+</broadcasterset>
+
+<broadcasterset id="mainBroadcasterSet"/>
+
+<keyset id="tasksKeys">
+ <!-- File Menu -->
+ <key id="key_newNavigator"/>
+ <key id="key_newPrivateWindow"/>
+ <key id="key_newBlankPage"/>
+#ifdef XP_MACOSX
+ <key id="key_newMessage" key="&newMessageCmd.key;"
+ modifiers="accel,shift" command="cmd_newMessage"/>
+#else
+ <key id="key_newMessage" key="&newMessageCmd.key;"
+ modifiers="accel" command="cmd_newMessage"/>
+#endif
+ <key id="key_newCard" key="&newContact.key;" modifiers="accel"
+ command="cmd_newCard"/>
+ <key id="key_printCard" key="&printContactViewCmd.key;"
+ command="cmd_printcard" modifiers="accel"/>
+ <key id="key_close"/>
+ <!-- Edit Menu -->
+ <key id="key_delete"/>
+ <key id="key_delete2"/> <!-- secondary delete key -->
+ <key id="key_undo"/>
+ <key id="key_redo"/>
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"/>
+ <key id="key_selectAll"/>
+ <key id="key_properties" key="&propertiesCmd.key;" command="cmd_properties" modifiers="accel"/>
+
+ <!-- View Menu -->
+#ifndef XP_MACOSX
+ <key id="key_toggleDirectoryPane"
+ keycode="VK_F9"
+ oncommand="togglePaneSplitter('dirTree-splitter');"/>
+#else
+ <key id="key_toggleDirectoryPane"
+ key="&toggleDirectoryPaneCmd.key;"
+ modifiers="accel,alt"
+ oncommand="togglePaneSplitter('dirTree-splitter');"/>
+#endif
+
+ <!-- Tab/F6 Keys -->
+ <key keycode="VK_TAB" oncommand="SwitchPaneFocus(event);" modifiers="control,shift"/>
+ <key keycode="VK_TAB" oncommand="SwitchPaneFocus(event);" modifiers="control"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="control,shift"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="control"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="shift"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);"/>
+
+ <!-- Search field -->
+ <key key="&focusSearchInput.key;"
+ modifiers="accel"
+ oncommand="focusElement(document.getElementById('searchInput'));"/>
+
+</keyset>
+
+<menupopup id="dirTreeContext">
+ <menuitem id="dirTreeContext-properties"
+ label="&editItemButton.label;"
+ accesskey="&editItemButton.accesskey;"
+ command="cmd_properties"/>
+ <menuseparator/>
+ <menuitem id="dirTreeContext-newcard"
+ label="&newContactButton.label;"
+ accesskey="&newContactButton.accesskey;"
+ command="cmd_newCard"/>
+ <menuitem id="dirTreeContext-newlist"
+ label="&newlistButton.label;"
+ accesskey="&newlistButton.accesskey;"
+ command="cmd_newlist"/>
+ <menuseparator/>
+ <menuitem id="dirTreeContext-print"
+ label="&printButton.label;"
+ accesskey="&printButton.accesskey;"
+ command="cmd_printAddressBook"/>
+ <menuitem id="dirTreeContext-delete"
+ label="&deleteItemButton.label;"
+ accesskey="&deleteItemButton.accesskey;"
+ command="button_delete"/>
+</menupopup>
+
+<menupopup id="abResultsTreeContext">
+ <menuitem id="abResultsTreeContext-properties"
+ label="&editItemButton.label;"
+ accesskey="&editItemButton.accesskey;"
+ command="cmd_properties"/>
+ <menuseparator/>
+ <menuitem id="abResultsTreeContext-newmessage"
+ label="&newmsgButton.label;"
+ accesskey="&newmsgButton.accesskey;"
+ command="cmd_newMessage"/>
+ <menuseparator/>
+ <menuitem id="abResultsTreeContext-print"
+ label="&printButton.label;"
+ accesskey="&printButton.accesskey;"
+ command="cmd_printcard"/>
+ <menuitem id="abResultsTreeContext-delete"
+ label="&deleteItemButton.label;"
+ accesskey="&deleteItemButton.accesskey;"
+ command="button_delete"/>
+</menupopup>
+
+<menupopup id="toolbar-context-menu"/>
+
+<vbox id="titlebar"/>
+
+<toolbox id="ab-toolbox"
+ mode="full"
+ defaultmode="full"
+ class="toolbox-top">
+ <toolbar type="menubar"
+ id="addrbook-toolbar-menubar2"
+ class="chromeclass-menubar"
+ persist="collapsed"
+ grippytooltiptext="&menuBar.tooltip;"
+ customizable="true"
+ defaultset="menubar-items"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu">
+ <toolbaritem id="menubar-items"
+ class="menubar-items"
+ align="center">
+ <menubar id="ab-menubar">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup">
+ <menu id="menu_New">
+ <menupopup id="menu_NewPopup">
+ <menuitem id="menu_newContact"
+ label="&newContact.label;"
+ accesskey="&newContact.accesskey;"
+ key="key_newCard"
+ command="cmd_newCard"/>
+ <menuitem id="menu_newList"
+ label="&newListCmd.label;"
+ accesskey="&newListCmd.accesskey;"
+ command="cmd_newlist"/>
+ <menuitem id="menu_newAddrbook"
+ label="&newAddressBookCmd.label;"
+ accesskey="&newAddressBookCmd.accesskey;"
+ oncommand="AbNewAddressBook();"/>
+ <menuitem id="addLDAP"
+ label="&newLDAPDirectoryCmd.label;"
+ accesskey="&newLDAPDirectoryCmd.accesskey;"
+ oncommand="AbNewLDAPDirectory();"/>
+ <menuseparator/>
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ <menuitem id="menu_newMessage"
+ label="&newMessageCmd.label;"
+ accesskey="&newMessageCmd.accesskey;"
+ key="key_newMessage"
+ command="cmd_newMessage"/>
+ <menuitem id="menu_newEditor"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_close"/>
+ <menuseparator/>
+#ifndef XP_MACOSX
+ <menuitem id="printPreviewMenuItem"
+ label="&printPreviewContactViewCmd.label;"
+ accesskey="&printPreviewContactViewCmd.accesskey;"
+ command="cmd_printcardpreview"/>
+#endif
+ <menuitem id="printMenuItem" label="&printContactViewCmd.label;"
+ accesskey="&printContactViewCmd.accesskey;"
+ key="key_printCard"
+ command="cmd_printcard"/>
+ <menuseparator id="menu_PageSetupSeparator"/>
+ <menuitem id="menu_printSetup"/>
+ <menuseparator id="menu_PrintAddrbookSeparator"/>
+#ifndef XP_MACOSX
+ <menuitem id="printPreviewAddressBook"
+ label="&printPreviewAddressBook.label;"
+ accesskey="&printPreviewAddressBook.accesskey;"
+ command="cmd_printPreviewAddressBook"/>
+#endif
+ <menuitem id="printAddressBook"
+ label="&printAddressBook.label;"
+ accesskey="&printAddressBook.accesskey;"
+ command="cmd_printAddressBook"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_Edit">
+ <menupopup id="menu_EditPopup">
+ <menuitem id="menu_undo"/>
+ <menuitem id="menu_redo"/>
+ <menuseparator/>
+ <menuitem id="menu_cut"/>
+ <menuitem id="menu_copy"/>
+ <menuitem id="menu_paste"/>
+ <menuitem id="menu_delete"/>
+ <menuseparator/>
+ <menuitem id="menu_selectAll"/>
+ <menuseparator/>
+ <!-- LOCALIZATION NOTE: set "hideSwapFnLnUI" to false in .dtd to enable the UI -->
+ <menuitem label="&swapFirstNameLastNameCmd.label;"
+ accesskey="&swapFirstNameLastNameCmd.accesskey;"
+ hidden="&hideSwapFnLnUI;"
+ command="cmd_swapFirstNameLastName"/>
+ <menuitem label="&propertiesCmd.label;"
+ accesskey="&propertiesCmd.accesskey;"
+ key="key_properties"
+ command="cmd_properties"/>
+ <menuitem id="menu_preferences"
+ oncommand="goPreferences('addressing_pane')"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_View">
+ <menupopup id="menu_View_Popup">
+ <menu id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup"
+ onpopupshowing="onViewToolbarsPopupShowing(event);"
+ oncommand="onViewToolbarCommand(event);">
+ <menuitem id="menu_showTaskbar"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_Layout"
+ label="&layoutMenu.label;"
+ accesskey="&layoutMenu.accesskey;">
+ <menupopup id="view_layout_popup"
+ onpopupshowing="InitViewLayoutMenuPopup(event);">
+ <menuitem id="menu_showDirectoryPane"
+ label="&showDirectoryPane.label;"
+ key="key_toggleDirectoryPane"
+ accesskey="&showDirectoryPane.accesskey;"
+ oncommand="togglePaneSplitter('dirTree-splitter');"
+ checked="true"
+ type="checkbox"/>
+ <menuitem id="menu_showCardPane"
+ label="&showContactPane2.label;"
+ accesskey="&showContactPane2.accesskey;"
+ oncommand="togglePaneSplitter('results-splitter');"
+ checked="true"
+ type="checkbox"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="viewMenuAfterLayoutSeparator"/>
+ <menu id="menu_ShowNameAs" label="&menu_ShowNameAs.label;" accesskey="&menu_ShowNameAs.accesskey;">
+ <menupopup id="menuShowNameAsPopup">
+ <menuitem type="radio" name="shownameas"
+ id="firstLastCmd"
+ label="&firstLastCmd.label;"
+ accesskey="&firstLastCmd.accesskey;"
+ oncommand="SetNameColumn('firstLastCmd')"/>
+ <menuitem type="radio" name="shownameas"
+ id="lastFirstCmd"
+ label="&lastFirstCmd.label;"
+ accesskey="&lastFirstCmd.accesskey;"
+ oncommand="SetNameColumn('lastFirstCmd')"/>
+ <menuitem type="radio" name="shownameas"
+ id="displayNameCmd"
+ label="&displayNameCmd.label;"
+ accesskey="&displayNameCmd.accesskey;"
+ oncommand="SetNameColumn('displayNameCmd')"/>
+ </menupopup>
+ </menu>
+ <menu id="sortMenu" label="&sortMenu.label;" accesskey="&sortMenu.accesskey;">
+ <menupopup id="sortMenuPopup" onpopupshowing="InitViewSortByMenu()">
+ <menuitem label="&GeneratedName.label;"
+ id="cmd_SortByGeneratedName"
+ accesskey="&GeneratedName.accesskey;"
+ oncommand="SortResultPane('GeneratedName');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&PrimaryEmail.label;"
+ id="cmd_SortByPrimaryEmail"
+ accesskey="&PrimaryEmail.accesskey;"
+ oncommand="SortResultPane('PrimaryEmail');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&ChatName.label;"
+ id="cmd_SortByChatName"
+ accesskey="&ChatName.accesskey;"
+ oncommand="SortResultPane('ChatName');"
+ name="sortas"
+ type="radio"
+ checked="true"/>
+ <menuitem label="&Company.label;"
+ id="cmd_SortByCompany"
+ accesskey="&Company.accesskey;"
+ oncommand="SortResultPane('Company');" name="sortas" type="radio" checked="true"/>
+ <!-- LOCALIZATION NOTE:
+ Fields for phonetic are disabled as default and can be enabled by
+ editing "mail.addr_book.show_phonetic_fields"
+ -->
+ <menuitem label="&_PhoneticName.label;"
+ id="cmd_SortBy_PhoneticName"
+ hidden="true"
+ accesskey="&_PhoneticName.accesskey;"
+ oncommand="SortResultPane('_PhoneticName');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&NickName.label;"
+ id="cmd_SortByNickName"
+ accesskey="&NickName.accesskey;"
+ oncommand="SortResultPane('NickName');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&SecondEmail.label;"
+ id="cmd_SortBySecondEmail"
+ accesskey="&SecondEmail.accesskey;"
+ oncommand="SortResultPane('SecondEmail');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&Department.label;"
+ id="cmd_SortByDepartment"
+ accesskey="&Department.accesskey;"
+ oncommand="SortResultPane('Department');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&JobTitle.label;"
+ id="cmd_SortByJobTitle"
+ accesskey="&JobTitle.accesskey;"
+ oncommand="SortResultPane('JobTitle');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&CellularNumber.label;"
+ id="cmd_SortByCellularNumber"
+ accesskey="&CellularNumber.accesskey;"
+ oncommand="SortResultPane('CellularNumber');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&PagerNumber.label;"
+ id="cmd_SortByPagerNumber"
+ accesskey="&PagerNumber.accesskey;"
+ oncommand="SortResultPane('PagerNumber');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&FaxNumber.label;"
+ id="cmd_SortByFaxNumber"
+ accesskey="&FaxNumber.accesskey;"
+ oncommand="SortResultPane('FaxNumber');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&HomePhone.label;"
+ id="cmd_SortByHomePhone"
+ accesskey="&HomePhone.accesskey;"
+ oncommand="SortResultPane('HomePhone');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&WorkPhone.label;"
+ id="cmd_SortByWorkPhone"
+ accesskey="&WorkPhone.accesskey;"
+ oncommand="SortResultPane('WorkPhone');" name="sortas" type="radio" checked="true"/>
+ <menuitem label="&Addrbook.label;"
+ id="cmd_SortByaddrbook"
+ accesskey="&Addrbook.accesskey;"
+ oncommand="SortResultPane('addrbook');"
+ name="sortas"
+ type="radio"
+ checked="true"/>
+ <menuseparator/>
+ <menuitem id="sortAscending" type="radio" name="sortdirection" label="&sortAscending.label;" accesskey="&sortAscending.accesskey;" oncommand="AbSortAscending()"/>
+ <menuitem id="sortDescending" type="radio" name="sortdirection" label="&sortDescending.label;" accesskey="&sortDescending.accesskey;" oncommand="AbSortDescending()"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ <menu id="tasksMenu">
+ <menupopup id="taskPopup">
+ <menuitem label="&searchAddressesCmd.label;"
+ accesskey="&searchAddressesCmd.accesskey;"
+ id="menu_search_addresses"
+ oncommand="onAdvancedAbSearch();"/>
+ <menuitem label="&importCmd.label;" accesskey="&importCmd.accesskey;" oncommand="toImport()"/>
+ <menuitem label="&exportCmd.label;"
+ accesskey="&exportCmd.accesskey;"
+ oncommand="AbExportSelection();"/>
+ <menuseparator/>
+ </menupopup>
+ </menu>
+
+ <menu id="windowMenu"/>
+ <menu id="menu_Help"/>
+ <spacer flex="1"/>
+ </menubar>
+ </toolbaritem>
+ </toolbar>
+ <toolbar class="chromeclass-toolbar toolbar-primary"
+ id="ab-bar2"
+ persist="collapsed"
+ customizable="true"
+ grippytooltiptext="&addressbookToolbar.tooltip;"
+ toolbarname="&showAbToolbarCmd.label;"
+ accesskey="&showAbToolbarCmd.accesskey;"
+ defaultset="button-newcard,button-newlist,separator,button-editcard,button-newmessage,button-abdelete,spring,searchBox,throbber-box"
+ context="toolbar-context-menu">
+ <toolbarbutton id="button-newcard"
+ class="toolbarbutton-1"
+ label="&newContactButton.label;"
+ tooltiptext="&newContactButton.tooltip;"
+ removable="true"
+ command="cmd_newCard"/>
+ <toolbarbutton id="button-newlist"
+ class="toolbarbutton-1"
+ label="&newlistButton.label;"
+ tooltiptext="&newlistButton.tooltip;"
+ removable="true"
+ command="cmd_newlist"/>
+ <toolbarbutton id="button-editcard"
+ class="toolbarbutton-1"
+ label="&editItemButton.label;"
+ tooltiptext="&editItemButton.tooltip;"
+ removable="true"
+ command="cmd_properties"/>
+ <toolbarbutton id="button-newmessage"
+ class="toolbarbutton-1"
+ label="&newmsgButton.label;"
+ tooltiptext="&newmsgButton.tooltip;"
+ removable="true"
+ oncommand="AbNewMessage();"/>
+ <toolbarbutton id="button-abdelete"
+ class="toolbarbutton-1"
+ label="&deleteItemButton.label;"
+ tooltiptext="&deleteItemButton.tooltip;"
+ removable="true"
+ oncommand="goDoCommand('button_delete');"/>
+ <toolbarbutton id="print-button"
+ label="&printButton.label;"
+ tooltiptext="&printButton.tooltip;"
+ removable="true"/>
+ <toolbaritem id="searchBox"
+ flex="1"
+ title="&searchBox.title;"
+ removable="true"
+ align="center"
+ class="toolbaritem-noline chromeclass-toolbar-additional">
+ <textbox id="searchInput"
+ flex="1"
+ type="search"
+ aria-controls="abResultsTree"
+ placeholder="&searchNameAndEmail.placeholder;"
+ clickSelectsAll="true"
+ oncommand="onEnterInSearchBar();"
+ onkeypress="if (event.keyCode == KeyEvent.DOM_VK_RETURN) this.select();"/>
+ </toolbaritem>
+
+ <toolbaritem id="throbber-box"/>
+ </toolbar>
+ <toolbarpalette id="AbToolbarPalette"/>
+ <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+</toolbox>
+
+ <!-- The main address book three pane -->
+ <hbox flex="1">
+ <vbox id="dirTreeBox" persist="width collapsed">
+ <tree id="dirTree"
+ class="abDirectory plain"
+ seltype="single"
+ style="min-width: 150px;"
+ flex="1"
+ persist="width"
+ hidecolumnpicker="true"
+ context="dirTreeContext"
+ onselect="DirPaneSelectionChange(); document.commandDispatcher.updateCommands('addrbook-select');"
+ ondblclick="DirPaneDoubleClick(event);"
+ onclick="DirPaneClick(event);"
+ onblur="goOnEvent(this, 'blur');">
+ <treecols>
+ <treecol id="DirCol"
+ flex="1"
+ primary="true"
+ label="&dirTreeHeader.label;"
+ crop="center"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ </vbox>
+
+ <splitter id="dirTree-splitter" collapse="before" persist="state">
+ <grippy/>
+ </splitter>
+
+ <vbox flex="1" style="min-width:100px">
+ <description id="localResultsOnlyMessage"
+ value="&localResultsOnlyMessage.label;"/>
+ <vbox id="blankResultsPaneMessageBox"
+ flex="1"
+ pack="center"
+ align="center">
+ <description id="blankResultsPaneMessage"
+ value="&blankResultsPaneMessage.label;"/>
+ </vbox>
+ <!-- results pane -->
+ <tree id="abResultsTree" context="abResultsTreeContext" flex="1" />
+
+ <splitter id="results-splitter" collapse="after" persist="state">
+ <grippy/>
+ </splitter>
+
+ <!-- card view -->
+ <hbox id="CardViewOuterBox" flex="1" persist="height">
+ <vbox id="CardViewBox"
+ flex="1"
+ style="height:170px; min-height:1px; min-width:1px">
+ <vbox id="CardViewInnerBox" collapsed="true" flex="1">
+ <description id="CardTitle"/>
+ <hbox style="width:100%" flex="1">
+ <vbox id="cvbPhoto"
+ class="cardViewGroup"
+ align="center"
+ style="min-width: 10ch; max-width: 10ch;">
+ <image id="cvPhoto" style="max-width: 10ch; max-height: 10ch;"/>
+ </vbox>
+ <hbox flex="1" equalsize="always">
+ <vbox flex="1" class="cardViewColumn">
+ <vbox id="cvbContact" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhContact">&contact.heading;</description>
+ <description class="CardViewLink" id="cvListNameBox">
+ <html:p><html:a href="" id="cvListName"/></html:p>
+ </description>
+ <description class="CardViewText" id="cvDisplayName"/>
+ <description class="CardViewText" id="cvNickname"/>
+ <description class="CardViewLink" id="cvEmail1Box">
+ <html:a href="" id="cvEmail1"/>
+ </description>
+ <description class="CardViewLink" id="cvEmail2Box">
+ <html:a href="" id="cvEmail2"/>
+ </description>
+ </vbox>
+ <vbox id="cvbHome" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhHome">&home.heading;</description>
+ <hbox>
+ <vbox flex="1">
+ <description class="CardViewText" id="cvHomeAddress"/>
+ <description class="CardViewText" id="cvHomeAddress2"/>
+ <description class="CardViewText" id="cvHomeCityStZip"/>
+ <description class="CardViewText" id="cvHomeCountry"/>
+ </vbox>
+ <vbox id="cvbHomeMapItBox" pack="end">
+ <button id="cvHomeMapIt"
+ label="&mapItButton.label;"
+ type="menu-button"
+ oncommand="openLinkWithUrl(this.firstChild.mapURL);"
+ tooltiptext="&mapIt.tooltip;">
+ <menupopup class="map-list"/>
+ </button>
+ </vbox>
+ </hbox>
+ <description class="CardViewLink" id="cvHomeWebPageBox">
+ <html:a onclick="return openLink(event);"
+ href=""
+ id="cvHomeWebPage"/>
+ </description>
+ </vbox>
+ <vbox id="cvbOther" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhOther">&other.heading;</description>
+ <description class="CardViewText" id="cvBirthday"/>
+ <description class="CardViewText" id="cvCustom1"/>
+ <description class="CardViewText" id="cvCustom2"/>
+ <description class="CardViewText" id="cvCustom3"/>
+ <description class="CardViewText" id="cvCustom4"/>
+ <description class="CardViewText" id="cvNotes"
+ style="white-space: pre-wrap;"/>
+ <hbox>
+ <image id="cvBuddyIcon"/>
+ </hbox>
+ </vbox>
+ <vbox id="cvbChat" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhChat">&chat.heading;</description>
+ <description class="CardViewText" id="cvYahoo"/>
+ <description class="CardViewText" id="cvSkype"/>
+ <description class="CardViewText" id="cvQQ"/>
+ <description class="CardViewText" id="cvMSN"/>
+ <description class="CardViewText" id="cvICQ"/>
+ <description class="CardViewText" id="cvXMPP"/>
+ <description class="CardViewText" id="cvIRC"/>
+ </vbox>
+ <!-- the description and addresses groups are only for
+ mailing lists -->
+ <vbox id="cvbDescription" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhDescription">&description.heading;</description>
+ <description class="CardViewText" id="cvDescription"/>
+ </vbox>
+ <vbox id="cvbAddresses" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhAddresses">&addresses.heading;</description>
+ <vbox id="cvAddresses"/>
+ </vbox>
+ </vbox>
+
+ <vbox flex="1" class="cardViewColumn">
+ <vbox id="cvbPhone" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhPhone">&phone.heading;</description>
+ <description class="CardViewText" id="cvPhWork"/>
+ <description class="CardViewText" id="cvPhHome"/>
+ <description class="CardViewText" id="cvPhFax"/>
+ <description class="CardViewText" id="cvPhCellular"/>
+ <description class="CardViewText" id="cvPhPager"/>
+ </vbox>
+ <vbox id="cvbWork" class="cardViewGroup">
+ <description class="CardViewHeading" id="cvhWork">&work.heading;</description>
+ <description class="CardViewText" id="cvJobTitle"/>
+ <description class="CardViewText" id="cvDepartment"/>
+ <description class="CardViewText" id="cvCompany"/>
+ <hbox>
+ <vbox flex="1">
+ <description class="CardViewText" id="cvWorkAddress"/>
+ <description class="CardViewText" id="cvWorkAddress2"/>
+ <description class="CardViewText" id="cvWorkCityStZip"/>
+ <description class="CardViewText" id="cvWorkCountry"/>
+ </vbox>
+ <vbox id="cvbWorkMapItBox" pack="end">
+ <button id="cvWorkMapIt"
+ label="&mapItButton.label;"
+ type="menu-button"
+ oncommand="openLinkWithUrl(this.firstChild.mapURL);"
+ tooltiptext="&mapIt.tooltip;">
+ <menupopup class="map-list"/>
+ </button>
+ </vbox>
+ </hbox>
+ <description class="CardViewLink" id="cvWorkWebPageBox">
+ <html:a onclick="return openLink(event);"
+ href=""
+ id="cvWorkWebPage"/>
+ </description>
+ </vbox>
+ </vbox>
+ </hbox>
+ </hbox>
+ </vbox>
+ </vbox>
+ </hbox>
+ </vbox>
+ </hbox>
+
+ <panel id="customizeToolbarSheetPopup"/>
+ <statusbar id="status-bar" class="chromeclass-status">
+ <statusbarpanel id="component-bar"/>
+ <statusbarpanel id="statusText" flex="1" value="&statusText.label;"/>
+ <statusbarpanel id="offline-status" class="statusbarpanel-iconic"/>
+ </statusbar>
+</window>
diff --git a/comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.js b/comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.js
new file mode 100644
index 0000000000..bfc741c97f
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.js
@@ -0,0 +1,24 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function Startup()
+{
+ enableAutocomplete();
+}
+
+function onEditDirectories()
+{
+ window.openDialog("chrome://messenger/content/addressbook/pref-editdirectories.xul",
+ "editDirectories", "chrome,modal=yes,resizable=no", null);
+}
+
+function enableAutocomplete()
+{
+ var acLDAPValue = document.getElementById("ldap_2.autoComplete.useDirectory")
+ .value;
+
+ EnableElementById("directoriesList", acLDAPValue, false);
+ EnableElementById("editButton", acLDAPValue, false);
+}
diff --git a/comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.xul b/comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.xul
new file mode 100644
index 0000000000..af4bed750b
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/content/prefs/pref-addressing.xul
@@ -0,0 +1,92 @@
+<?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://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/messenger.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/pref-addressing.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="addressing_pane" label="&pref.addressing.title;"
+ script="chrome://messenger/content/addressbook/pref-addressing.js">
+
+ <stringbundle id="bundle_addressBook"
+ src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+
+ <preferences id="addressing_preferences">
+ <preference id="mail.collect_email_address_outgoing"
+ name="mail.collect_email_address_outgoing"
+ type="bool"/>
+ <preference id="mail.collect_addressbook"
+ name="mail.collect_addressbook"
+ type="string"/>
+ <preference id="mail.autoComplete.highlightNonMatches"
+ name="mail.autoComplete.highlightNonMatches"
+ type="bool"/>
+ <preference id="mail.enable_autocomplete"
+ name="mail.enable_autocomplete"
+ type="bool"/>
+ <preference id="ldap_2.autoComplete.useDirectory"
+ name="ldap_2.autoComplete.useDirectory"
+ onchange="enableAutocomplete();" type="bool"/>
+ <preference id="ldap_2.autoComplete.directoryServer"
+ name="ldap_2.autoComplete.directoryServer"
+ type="string"/>
+ <preference id="pref.ldap.disable_button.edit_directories"
+ name="pref.ldap.disable_button.edit_directories"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&emailCollectiontitle.label;"/>
+ <description>&emailCollectiontext.label;</description>
+ <hbox align="center">
+ <checkbox id="emailCollectionOutgoing"
+ label="&emailCollectionPicker.label;"
+ accesskey="&emailCollectionPicker.accesskey;"
+ preference="mail.collect_email_address_outgoing"/>
+ <menulist id="localDirectoriesList" flex="1"
+ aria-labelledby="emailCollectionOutgoing"
+ preference="mail.collect_addressbook">
+ <menupopup id="localDirectoriesPopup" class="addrbooksPopup"
+ localonly="true" writable="true"/>
+ </menulist>
+ </hbox>
+ </groupbox>
+ <groupbox id="addressAutocompletion">
+ <caption label="&addressingTitle.label;"/>
+ <hbox align="center">
+ <checkbox id="highlightNonMatches" label="&highlightNonMatches.label;"
+ preference="mail.autoComplete.highlightNonMatches"
+ accesskey="&highlightNonMatches.accesskey;"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <description>&autocompleteText.label;</description>
+ <hbox align="center">
+ <checkbox id="addressingAutocomplete" label="&addressingEnable.label;"
+ preference="mail.enable_autocomplete"
+ accesskey="&addressingEnable.accesskey;"/>
+ </hbox>
+ <hbox align="center">
+ <checkbox id="autocompleteLDAP" label="&directories.label;"
+ preference="ldap_2.autoComplete.useDirectory"
+ accesskey="&directories.accesskey;"/>
+ <menulist id="directoriesList" flex="1"
+ aria-labelledby="autocompleteLDAP"
+ preference="ldap_2.autoComplete.directoryServer">
+ <menupopup id="directoriesListPopup" class="addrbooksPopup"
+ none="&directoriesNone.label;"
+ remoteonly="true" value="dirPrefId"/>
+ </menulist>
+ <button id="editButton" label="&editDirectories.label;"
+ oncommand="onEditDirectories();"
+ accesskey="&editDirectories.accesskey;"
+ preference="pref.ldap.disable_button.edit_directories"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/addrbook/jar.mn b/comm/suite/mailnews/components/addrbook/jar.mn
new file mode 100644
index 0000000000..cc2cec4e13
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/jar.mn
@@ -0,0 +1,24 @@
+# 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/.
+
+messenger.jar:
+ content/messenger/addressbook/addressbook.js (content/addressbook.js)
+* content/messenger/addressbook/addressbook.xul (content/addressbook.xul)
+ content/messenger/addressbook/abCommon.js (content/abCommon.js)
+ content/messenger/addressbook/abCardOverlay.js (content/abCardOverlay.js)
+ content/messenger/addressbook/abCardOverlay.xul (content/abCardOverlay.xul)
+ content/messenger/addressbook/abCardViewOverlay.js (content/abCardViewOverlay.js)
+ content/messenger/addressbook/abEditCardDialog.xul (content/abEditCardDialog.xul)
+ content/messenger/addressbook/abNewCardDialog.xul (content/abNewCardDialog.xul)
+ content/messenger/addressbook/abResultsPaneOverlay.xul (content/abResultsPaneOverlay.xul)
+ content/messenger/addressbook/abMailListDialog.xul (content/abMailListDialog.xul)
+ content/messenger/addressbook/abEditListDialog.xul (content/abEditListDialog.xul)
+ content/messenger/addressbook/abListOverlay.xul (content/abListOverlay.xul)
+ content/messenger/addressbook/abSelectAddressesDialog.js (content/abSelectAddressesDialog.js)
+ content/messenger/addressbook/abSelectAddressesDialog.xul (content/abSelectAddressesDialog.xul)
+ content/messenger/addressbook/abTrees.js (content/abTrees.js)
+ content/messenger/addressbook/addressbook-panel.xul (content/addressbook-panel.xul)
+ content/messenger/addressbook/addressbook-panel.js (content/addressbook-panel.js)
+ content/messenger/addressbook/pref-addressing.js (content/prefs/pref-addressing.js)
+ content/messenger/addressbook/pref-addressing.xul (content/prefs/pref-addressing.xul)
diff --git a/comm/suite/mailnews/components/addrbook/moz.build b/comm/suite/mailnews/components/addrbook/moz.build
new file mode 100644
index 0000000000..77788c7f3e
--- /dev/null
+++ b/comm/suite/mailnews/components/addrbook/moz.build
@@ -0,0 +1,8 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+DEFINES["TOOLKIT_DIR"] = "%s/toolkit" % (CONFIG["topsrcdir"],)
diff --git a/comm/suite/mailnews/components/calendar/content/suite-overlay-addons.xhtml b/comm/suite/mailnews/components/calendar/content/suite-overlay-addons.xhtml
new file mode 100644
index 0000000000..c1ccec4917
--- /dev/null
+++ b/comm/suite/mailnews/components/calendar/content/suite-overlay-addons.xhtml
@@ -0,0 +1,39 @@
+<?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/. -->
+
+<overlay id="suiteAddonsOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <script><![CDATA[
+ var lightningPrefs = {
+ guid: "{e2fda1a4-762b-4020-b5ad-a41df1933103}",
+ handleEvent: function(aEvent) {
+ var item = gListView.getListItemForID(this.guid);
+ if (!item)
+ return;
+
+ item.showPreferences = this.showPreferences;
+ },
+ showPreferences: function() {
+ var win = Services.wm.getMostRecentWindow("mozilla:preferences");
+ if (win) {
+ win.focus();
+ var doc = win.document;
+ var pane = doc.getElementById("paneLightning");
+ doc.querySelector("dialog").syncTreeWithPane(pane, true);
+ } else {
+ openDialog("chrome://communicator/content/pref/preferences.xhtml",
+ "PrefWindow",
+ "non-private,chrome,titlebar,dialog=no,resizable",
+ "paneLightning");
+ }
+ },
+ };
+
+ window.addEventListener("ViewChanged", lightningPrefs, false);
+ ]]></script>
+
+</overlay>
diff --git a/comm/suite/mailnews/components/calendar/content/suite-overlay-preferences.xhtml b/comm/suite/mailnews/components/calendar/content/suite-overlay-preferences.xhtml
new file mode 100644
index 0000000000..0e7aad33df
--- /dev/null
+++ b/comm/suite/mailnews/components/calendar/content/suite-overlay-preferences.xhtml
@@ -0,0 +1,66 @@
+<?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://lightning/skin/lightning.css"?>
+
+<?xul-overlay href="chrome://calendar/content/preferences/general.xhtml"?>
+<?xul-overlay href="chrome://calendar/content/preferences/alarms.xhtml"?>
+<?xul-overlay href="chrome://calendar/content/preferences/categories.xhtml"?>
+<?xul-overlay href="chrome://calendar/content/preferences/views.xhtml"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % lightningDTD SYSTEM "chrome://lightning/locale/lightning.dtd">
+ %lightningDTD;
+ <!ENTITY % preferencesDTD SYSTEM "chrome://calendar/locale/preferences/preferences.dtd">
+ %preferencesDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <treechildren id="prefsPanelChildren">
+ <treeitem container="true"
+ id="lightningItem"
+ insertafter="mailnewsItem,navigatorItem"
+ label="&lightning.preferencesLabel;"
+ prefpane="paneLightning">
+ <treechildren id="lightningChildren">
+ <treeitem id="lightningAlarms"
+ label="&paneAlarms.title;"
+ prefpane="paneLightningAlarms"/>
+ <treeitem id="lightningCategories"
+ label="&paneCategories.title;"
+ prefpane="paneLightningCategories"/>
+ <treeitem id="lightningViews"
+ label="&paneViews.title;"
+ prefpane="paneLightningViews"/>
+ </treechildren>
+ </treeitem>
+ </treechildren>
+
+ <prefwindow id="prefDialog">
+ <prefpane id="paneLightning"
+ label="&lightning.preferencesLabel;"
+ onpaneload="gCalendarGeneralPane.init();">
+ <vbox id="calPreferencesBoxGeneral"/>
+ </prefpane>
+ <prefpane id="paneLightningAlarms"
+ label="&paneAlarms.title;"
+ onpaneload="gAlarmsPane.init();">
+ <vbox id="calPreferencesBoxAlarms"/>
+ </prefpane>
+ <prefpane id="paneLightningCategories"
+ label="&paneCategories.title;"
+ onpaneload="gCategoriesPane.init();">
+ <vbox id="calPreferencesBoxCategories"/>
+ </prefpane>
+ <prefpane id="paneLightningViews"
+ label="&paneViews.title;"
+ onpaneload="gViewsPane.init();">
+ <vbox id="calPreferencesBoxViews"/>
+ </prefpane>
+ </prefwindow>
+
+</overlay>
diff --git a/comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.js b/comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.js
new file mode 100644
index 0000000000..6c27c9c385
--- /dev/null
+++ b/comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../../suite/base/content/utilityOverlay.js */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var ltnSuiteUtils = {
+ addStartupObserver: function() {
+ Services.obs.addObserver(this.startupObserver, "lightning-startup-done");
+ Services.obs.addObserver(this.startupObserver, "calendar-taskview-startup-done");
+ },
+
+ startupObserver: {
+ observe: function(subject, topic, state) {
+ if (topic != "lightning-startup-done" && topic != "calendar-taskview-startup-done") {
+ return;
+ }
+
+ const ids = [
+ ["CustomizeTaskActionsToolbar", "task-actions-toolbox"],
+ ["CustomizeCalendarToolbar", "calendar-toolbox"],
+ ["CustomizeTaskToolbar", "task-toolbox"],
+ ];
+
+ ids.forEach(([itemID, toolboxID]) => {
+ let item = document.getElementById(itemID);
+ let toolbox = document.getElementById(toolboxID);
+ toolbox.customizeInit = function() {
+ item.setAttribute("disabled", "true");
+ toolboxCustomizeInit("mail-menubar");
+ };
+ toolbox.customizeDone = function(aToolboxChanged) {
+ item.removeAttribute("disabled");
+ toolboxCustomizeDone("mail-menubar", toolbox, aToolboxChanged);
+ };
+ toolbox.customizeChange = function(aEvent) {
+ toolboxCustomizeChange(toolbox, aEvent);
+ };
+ });
+ },
+ },
+};
+
+ltnSuiteUtils.addStartupObserver();
diff --git a/comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.xhtml b/comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.xhtml
new file mode 100644
index 0000000000..79ae5d662a
--- /dev/null
+++ b/comm/suite/mailnews/components/calendar/content/suite-overlay-sidebar.xhtml
@@ -0,0 +1,39 @@
+<?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/. -->
+
+<overlay id="suiteSidebarOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
+
+ <script src="chrome://lightning/content/suite-overlay-sidebar.js"/>
+
+ <key id="openLightningKey" removeelement="true"/>
+ <key id="openTasksKey" removeelement="true"/>
+ <key id="calendar-new-event-key" removeelement="true"/>
+ <key id="calendar-new-todo-key" removeelement="true"/>
+
+ <menuitem id="CustomizeTaskActionsToolbar"
+ oncommand="goCustomizeToolbar(document.getElementById('task-actions-toolbox'))"/>
+
+ <toolbox id="calendar-toolbox"
+ defaultlabelalign="end"
+ xpfe="false"/>
+ <toolbox id="task-toolbox"
+ defaultlabelalign="end"
+ xpfe="false"/>
+ <toolbox id="task-actions-toolbox"
+ defaultlabelalign="end"
+ xpfe="false"/>
+
+ <toolbar id="calendar-toolbar2"
+ defaultlabelalign="end"
+ context="toolbar-context-menu"/>
+ <toolbar id="task-toolbar2"
+ defaultlabelalign="end"
+ context="toolbar-context-menu"/>
+ <toolbar id="task-actions-toolbar"
+ context="toolbar-context-menu"/>
+
+</overlay>
diff --git a/comm/suite/mailnews/components/compose/content/MsgComposeCommands.js b/comm/suite/mailnews/components/compose/content/MsgComposeCommands.js
new file mode 100644
index 0000000000..48f7f31b1a
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/MsgComposeCommands.js
@@ -0,0 +1,3936 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm");
+const {FolderUtils} = ChromeUtils.import("resource:///modules/FolderUtils.jsm");
+const {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+const { MimeParser } = ChromeUtils.import("resource:///modules/mimeParser.jsm");
+
+ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+
+/**
+ * interfaces
+ */
+var nsIMsgCompDeliverMode = Ci.nsIMsgCompDeliverMode;
+var nsIMsgCompSendFormat = Ci.nsIMsgCompSendFormat;
+var nsIMsgCompConvertible = Ci.nsIMsgCompConvertible;
+var nsIMsgCompType = Ci.nsIMsgCompType;
+var nsIMsgCompFormat = Ci.nsIMsgCompFormat;
+var nsIAbPreferMailFormat = Ci.nsIAbPreferMailFormat;
+var mozISpellCheckingEngine = Ci.mozISpellCheckingEngine;
+
+/**
+ * In order to distinguish clearly globals that are initialized once when js load (static globals) and those that need to be
+ * initialize every time a compose window open (globals), I (ducarroz) have decided to prefix by s... the static one and
+ * by g... the other one. Please try to continue and repect this rule in the future. Thanks.
+ */
+/**
+ * static globals, need to be initialized only once
+ */
+var sComposeMsgsBundle;
+var sBrandBundle;
+
+var sRDF = null;
+var sNameProperty = null;
+var sDictCount = 0;
+
+/**
+ * Global message window object. This is used by mail-offline.js and therefore
+ * should not be renamed. We need to avoid doing this kind of cross file global
+ * stuff in the future and instead pass this object as parameter when needed by
+ * functions in the other js file.
+ */
+var msgWindow;
+
+var gMessenger;
+
+/**
+ * Global variables, need to be re-initialized every time mostly because
+ * we need to release them when the window closes.
+ */
+var gHideMenus;
+var gMsgCompose;
+var gOriginalMsgURI;
+var gWindowLocked;
+var gSendLocked;
+var gContentChanged;
+var gAutoSaving;
+var gCurrentIdentity;
+var defaultSaveOperation;
+var gSendOrSaveOperationInProgress;
+var gCloseWindowAfterSave;
+var gSavedSendNowKey;
+var gSendFormat;
+var gLogComposePerformance;
+
+var gMsgIdentityElement;
+var gMsgAddressingWidgetElement;
+var gMsgSubjectElement;
+var gMsgAttachmentElement;
+var gMsgHeadersToolbarElement;
+var gComposeType;
+var gFormatToolbarHidden = false;
+var gBodyFromArgs;
+
+// i18n globals
+var gCharsetConvertManager;
+
+var gLastWindowToHaveFocus;
+var gReceiptOptionChanged;
+var gDSNOptionChanged;
+var gAttachVCardOptionChanged;
+
+var gAutoSaveInterval;
+var gAutoSaveTimeout;
+var gAutoSaveKickedIn;
+var gEditingDraft;
+
+var kComposeAttachDirPrefName = "mail.compose.attach.dir";
+
+function InitializeGlobalVariables()
+{
+ gMessenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger);
+
+ gMsgCompose = null;
+ gOriginalMsgURI = null;
+ gWindowLocked = false;
+ gContentChanged = false;
+ gCurrentIdentity = null;
+ defaultSaveOperation = "draft";
+ gSendOrSaveOperationInProgress = false;
+ gAutoSaving = false;
+ gCloseWindowAfterSave = false;
+ gSavedSendNowKey = null;
+ gSendFormat = nsIMsgCompSendFormat.AskUser;
+ gCharsetConvertManager = Cc['@mozilla.org/charset-converter-manager;1'].getService(Ci.nsICharsetConverterManager);
+ gHideMenus = false;
+ // We are storing the value of the bool logComposePerformance inorder to
+ // avoid logging unnecessarily.
+ gLogComposePerformance = MailServices.compose.logComposePerformance;
+
+ gLastWindowToHaveFocus = null;
+ gReceiptOptionChanged = false;
+ gDSNOptionChanged = false;
+ gAttachVCardOptionChanged = false;
+ msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"]
+ .createInstance(Ci.nsIMsgWindow);
+ MailServices.mailSession.AddMsgWindow(msgWindow);
+}
+InitializeGlobalVariables();
+
+function ReleaseGlobalVariables()
+{
+ gCurrentIdentity = null;
+ gCharsetConvertManager = null;
+ gMsgCompose = null;
+ gOriginalMsgURI = null;
+ gMessenger = null;
+ sComposeMsgsBundle = null;
+ sBrandBundle = null;
+ MailServices.mailSession.RemoveMsgWindow(msgWindow);
+ msgWindow = null;
+}
+
+function disableEditableFields()
+{
+ gMsgCompose.editor.flags |= Ci.nsIEditor.eEditorReadonlyMask;
+ var disableElements = document.getElementsByAttribute("disableonsend", "true");
+ for (let i = 0; i < disableElements.length; i++)
+ disableElements[i].setAttribute('disabled', 'true');
+
+}
+
+function enableEditableFields()
+{
+ gMsgCompose.editor.flags &= ~Ci.nsIEditor.eEditorReadonlyMask;
+ var enableElements = document.getElementsByAttribute("disableonsend", "true");
+ for (let i = 0; i < enableElements.length; i++)
+ enableElements[i].removeAttribute('disabled');
+
+}
+
+/**
+ * Small helper function to check whether the node passed in is a signature.
+ * Note that a text node is not a DOM element, hence .localName can't be used.
+ */
+function isSignature(aNode) {
+ return ["DIV","PRE"].includes(aNode.nodeName) &&
+ aNode.classList.contains("moz-signature");
+}
+
+var stateListener = {
+ NotifyComposeFieldsReady: function() {
+ ComposeFieldsReady();
+ updateSendCommands(true);
+ },
+
+ NotifyComposeBodyReady: function() {
+ this.useParagraph = gMsgCompose.composeHTML &&
+ Services.prefs.getBoolPref("mail.compose.default_to_paragraph");
+ this.editor = GetCurrentEditor();
+ this.paragraphState = document.getElementById("cmd_paragraphState");
+
+ // Look at the compose types which require action (nsIMsgComposeParams.idl):
+ switch (gComposeType) {
+
+ case Ci.nsIMsgCompType.MailToUrl:
+ gBodyFromArgs = true;
+ case Ci.nsIMsgCompType.New:
+ case Ci.nsIMsgCompType.NewsPost:
+ case Ci.nsIMsgCompType.ForwardAsAttachment:
+ this.NotifyComposeBodyReadyNew();
+ break;
+
+ case Ci.nsIMsgCompType.Reply:
+ case Ci.nsIMsgCompType.ReplyAll:
+ case Ci.nsIMsgCompType.ReplyToSender:
+ case Ci.nsIMsgCompType.ReplyToGroup:
+ case Ci.nsIMsgCompType.ReplyToSenderAndGroup:
+ case Ci.nsIMsgCompType.ReplyWithTemplate:
+ case Ci.nsIMsgCompType.ReplyToList:
+ this.NotifyComposeBodyReadyReply();
+ break;
+
+ case Ci.nsIMsgCompType.ForwardInline:
+ this.NotifyComposeBodyReadyForwardInline();
+ break;
+
+ case Ci.nsIMsgCompType.EditTemplate:
+ defaultSaveOperation = "template";
+ case Ci.nsIMsgCompType.Draft:
+ case Ci.nsIMsgCompType.Template:
+ case Ci.nsIMsgCompType.Redirect:
+ case Ci.nsIMsgCompType.EditAsNew:
+ break;
+
+ default:
+ dump("Unexpected nsIMsgCompType in NotifyComposeBodyReady (" +
+ gComposeType + ")\n");
+ }
+
+ // Set the selected item in the identity list as needed, which will cause
+ // an identity/signature switch. This can only be done once the message
+ // body has already been assembled with the signature we need to switch.
+ if (gMsgCompose.identity != gCurrentIdentity) {
+ // Since switching the signature loses the caret position, we record it
+ // and restore it later.
+ let selection = this.editor.selection;
+ let range = selection.getRangeAt(0);
+ let start = range.startOffset;
+ let startNode = range.startContainer;
+
+ this.editor.enableUndo(false);
+ let identityList = GetMsgIdentityElement();
+ identityList.selectedItem = identityList.getElementsByAttribute(
+ "identitykey", gMsgCompose.identity.key)[0];
+ LoadIdentity(false);
+
+ this.editor.enableUndo(true);
+ this.editor.resetModificationCount();
+ selection.collapse(startNode, start);
+ }
+
+ if (gMsgCompose.composeHTML)
+ loadHTMLMsgPrefs();
+ AdjustFocus();
+ },
+
+ NotifyComposeBodyReadyNew: function() {
+ let insertParagraph = this.useParagraph;
+
+ let mailDoc = document.getElementById("content-frame").contentDocument;
+ let mailBody = mailDoc.querySelector("body");
+ if (insertParagraph && gBodyFromArgs) {
+ // Check for "empty" body before allowing paragraph to be inserted.
+ // Non-empty bodies in a new message can occur when clicking on a
+ // mailto link or when using the command line option -compose.
+ // An "empty" body can be one of these two cases:
+ // 1) <br> and nothing follows (no next sibling)
+ // 2) <div/pre class="moz-signature">
+ // Note that <br><div/pre class="moz-signature"> doesn't happen in
+ // paragraph mode.
+ let firstChild = mailBody.firstChild;
+ if ((firstChild.nodeName != "BR" || firstChild.nextSibling) &&
+ !isSignature(firstChild))
+ insertParagraph = false;
+ }
+
+ // Control insertion of line breaks.
+ if (insertParagraph) {
+ this.editor.enableUndo(false);
+
+ this.editor.selection.collapse(mailBody, 0);
+ let pElement = this.editor.createElementWithDefaults("p");
+ let brElement = this.editor.createElementWithDefaults("br");
+ pElement.appendChild(brElement);
+ this.editor.insertElementAtSelection(pElement, false);
+
+ this.paragraphState.setAttribute("state", "p");
+
+ this.editor.beginningOfDocument();
+ this.editor.enableUndo(true);
+ this.editor.resetModificationCount();
+ } else {
+ this.paragraphState.setAttribute("state", "");
+ }
+ },
+
+ NotifyComposeBodyReadyReply: function() {
+ // Control insertion of line breaks.
+ if (this.useParagraph) {
+ let mailDoc = document.getElementById("content-frame").contentDocument;
+ let mailBody = mailDoc.querySelector("body");
+ let selection = this.editor.selection;
+
+ // Make sure the selection isn't inside the signature.
+ if (isSignature(mailBody.firstChild))
+ selection.collapse(mailBody, 0);
+
+ let range = selection.getRangeAt(0);
+ let start = range.startOffset;
+
+ if (start != range.endOffset) {
+ // The selection is not collapsed, most likely due to the
+ // "select the quote" option. In this case we do nothing.
+ return;
+ }
+
+ if (range.startContainer != mailBody) {
+ dump("Unexpected selection in NotifyComposeBodyReadyReply\n");
+ return;
+ }
+
+ this.editor.enableUndo(false);
+
+ let pElement = this.editor.createElementWithDefaults("p");
+ let brElement = this.editor.createElementWithDefaults("br");
+ pElement.appendChild(brElement);
+ this.editor.insertElementAtSelection(pElement, false);
+
+ // Position into the paragraph.
+ selection.collapse(pElement, 0);
+
+ this.paragraphState.setAttribute("state", "p");
+
+ this.editor.enableUndo(true);
+ this.editor.resetModificationCount();
+ } else {
+ this.paragraphState.setAttribute("state", "");
+ }
+ },
+
+ NotifyComposeBodyReadyForwardInline: function() {
+ let mailDoc = document.getElementById("content-frame").contentDocument;
+ let mailBody = mailDoc.querySelector("body");
+ let selection = this.editor.selection;
+
+ this.editor.enableUndo(false);
+
+ // Control insertion of line breaks.
+ selection.collapse(mailBody, 0);
+ if (this.useParagraph) {
+ let pElement = this.editor.createElementWithDefaults("p");
+ let brElement = this.editor.createElementWithDefaults("br");
+ pElement.appendChild(brElement);
+ this.editor.insertElementAtSelection(pElement, false);
+ this.paragraphState.setAttribute("state", "p");
+ } else {
+ // insertLineBreak() has been observed to insert two <br> elements
+ // instead of one before a <div>, so we'll do it ourselves here.
+ let brElement = this.editor.createElementWithDefaults("br");
+ this.editor.insertElementAtSelection(brElement, false);
+ this.paragraphState.setAttribute("state", "");
+ }
+
+ this.editor.beginningOfDocument();
+ this.editor.enableUndo(true);
+ this.editor.resetModificationCount();
+ },
+
+ ComposeProcessDone: function(aResult) {
+ gWindowLocked = false;
+ enableEditableFields();
+ updateComposeItems();
+
+ if (aResult== Cr.NS_OK)
+ {
+ if (!gAutoSaving)
+ SetContentAndBodyAsUnmodified();
+
+ if (gCloseWindowAfterSave)
+ {
+ // Notify the SendListener that Send has been aborted and Stopped
+ if (gMsgCompose)
+ gMsgCompose.onSendNotPerformed(null, Cr.NS_ERROR_ABORT);
+
+ MsgComposeCloseWindow();
+ }
+ }
+ // else if we failed to save, and we're autosaving, need to re-mark the editor
+ // as changed, so that we won't lose the changes.
+ else if (gAutoSaving)
+ {
+ gMsgCompose.bodyModified = true;
+ gContentChanged = true;
+ }
+
+ gAutoSaving = false;
+ gCloseWindowAfterSave = false;
+ },
+
+ SaveInFolderDone: function(folderURI) {
+ DisplaySaveFolderDlg(folderURI);
+ }
+};
+
+// all progress notifications are done through the nsIWebProgressListener implementation...
+var progressListener = {
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus)
+ {
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_START)
+ {
+ document.getElementById('navigator-throbber').setAttribute("busy", "true");
+ document.getElementById('compose-progressmeter').setAttribute( "mode", "undetermined" );
+ document.getElementById("statusbar-progresspanel").collapsed = false;
+ }
+
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
+ {
+ gSendOrSaveOperationInProgress = false;
+ document.getElementById('navigator-throbber').removeAttribute("busy");
+ document.getElementById('compose-progressmeter').setAttribute( "mode", "normal" );
+ document.getElementById('compose-progressmeter').setAttribute( "value", 0 );
+ document.getElementById("statusbar-progresspanel").collapsed = true;
+ document.getElementById('statusText').setAttribute('label', '');
+ }
+ },
+
+ onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
+ {
+ // Calculate percentage.
+ var percent;
+ if ( aMaxTotalProgress > 0 )
+ {
+ percent = Math.round( (aCurTotalProgress*100)/aMaxTotalProgress );
+ if ( percent > 100 )
+ percent = 100;
+
+ document.getElementById('compose-progressmeter').removeAttribute("mode");
+
+ // Advance progress meter.
+ document.getElementById('compose-progressmeter').setAttribute( "value", percent );
+ }
+ else
+ {
+ // Progress meter should be barber-pole in this case.
+ document.getElementById('compose-progressmeter').setAttribute( "mode", "undetermined" );
+ }
+ },
+
+ onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags)
+ {
+ // we can ignore this notification
+ },
+
+ onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage)
+ {
+ // Looks like it's possible that we get call while the document has been already delete!
+ // therefore we need to protect ourself by using try/catch
+ try {
+ let statusText = document.getElementById("statusText");
+ if (statusText)
+ statusText.setAttribute("label", aMessage);
+ } catch (ex) {}
+ },
+
+ onSecurityChange: function(aWebProgress, aRequest, state)
+ {
+ // we can ignore this notification
+ },
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIWebProgressListener) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_NOINTERFACE;
+ }
+};
+
+var defaultController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command)
+ {
+ //File Menu
+ case "cmd_attachFile":
+ case "cmd_attachPage":
+ case "cmd_close":
+ case "cmd_save":
+ case "cmd_saveAsFile":
+ case "cmd_saveAsDraft":
+ case "cmd_saveAsTemplate":
+ case "cmd_sendButton":
+ case "cmd_sendNow":
+ case "cmd_sendWithCheck":
+ case "cmd_sendLater":
+ case "cmd_printSetup":
+ case "cmd_printpreview":
+ case "cmd_print":
+
+ //Edit Menu
+ case "cmd_account":
+ case "cmd_preferences":
+
+ //Options Menu
+ case "cmd_selectAddress":
+ case "cmd_outputFormat":
+ case "cmd_quoteMessage":
+ return true;
+
+ default:
+ return false;
+ }
+ },
+ isCommandEnabled: function(command)
+ {
+ var composeHTML = gMsgCompose && gMsgCompose.composeHTML;
+
+ switch (command)
+ {
+ //File Menu
+ case "cmd_attachFile":
+ case "cmd_attachPage":
+ case "cmd_close":
+ case "cmd_save":
+ case "cmd_saveAsFile":
+ case "cmd_saveAsDraft":
+ case "cmd_saveAsTemplate":
+ case "cmd_printSetup":
+ case "cmd_printpreview":
+ case "cmd_print":
+ return !gWindowLocked;
+ case "cmd_sendButton":
+ case "cmd_sendLater":
+ case "cmd_sendWithCheck":
+ case "cmd_sendButton":
+ return !gWindowLocked && !gSendLocked;
+ case "cmd_sendNow":
+ return !gWindowLocked && !Services.io.offline && !gSendLocked;
+
+ //Edit Menu
+ case "cmd_account":
+ case "cmd_preferences":
+ return true;
+
+ //Options Menu
+ case "cmd_selectAddress":
+ return !gWindowLocked;
+ case "cmd_outputFormat":
+ return composeHTML;
+ case "cmd_quoteMessage":
+ var selectedURIs = GetSelectedMessages();
+ if (selectedURIs && selectedURIs.length > 0)
+ return true;
+ return false;
+
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(command)
+ {
+ switch (command)
+ {
+ //File Menu
+ case "cmd_attachFile" : if (defaultController.isCommandEnabled(command)) AttachFile(); break;
+ case "cmd_attachPage" : AttachPage(); break;
+ case "cmd_close" : DoCommandClose(); break;
+ case "cmd_save" : Save(); break;
+ case "cmd_saveAsFile" : SaveAsFile(true); break;
+ case "cmd_saveAsDraft" : SaveAsDraft(); break;
+ case "cmd_saveAsTemplate" : SaveAsTemplate(); break;
+ case "cmd_sendButton" :
+ if (defaultController.isCommandEnabled(command))
+ {
+ if (Services.io.offline)
+ SendMessageLater();
+ else
+ SendMessage();
+ }
+ break;
+ case "cmd_sendNow" : if (defaultController.isCommandEnabled(command)) SendMessage(); break;
+ case "cmd_sendWithCheck" : if (defaultController.isCommandEnabled(command)) SendMessageWithCheck(); break;
+ case "cmd_sendLater" : if (defaultController.isCommandEnabled(command)) SendMessageLater(); break;
+ case "cmd_printSetup" : PrintUtils.showPageSetup(); break;
+ case "cmd_printpreview" : PrintUtils.printPreview(PrintPreviewListener); break;
+ case "cmd_print" :
+ let browser = GetCurrentEditorElement();
+ PrintUtils.printWindow(browser.outerWindowID, browser);
+ break;
+
+ //Edit Menu
+ case "cmd_account" :
+ let currentAccountKey = getCurrentAccountKey();
+ let account = MailServices.accounts.getAccount(currentAccountKey);
+ MsgAccountManager(null, account.incomingServer);
+ break;
+ case "cmd_preferences" : DoCommandPreferences(); break;
+
+ //Options Menu
+ case "cmd_selectAddress" : if (defaultController.isCommandEnabled(command)) SelectAddress(); break;
+ case "cmd_quoteMessage" : if (defaultController.isCommandEnabled(command)) QuoteSelectedMessage(); break;
+ default:
+ return;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ }
+};
+
+var gAttachmentBucketController =
+{
+ supportsCommand: function(aCommand)
+ {
+ switch (aCommand)
+ {
+ case "cmd_delete":
+ case "cmd_renameAttachment":
+ case "cmd_selectAll":
+ case "cmd_openAttachment":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(aCommand)
+ {
+ switch (aCommand)
+ {
+ case "cmd_delete":
+ return MessageGetNumSelectedAttachments() > 0;
+ case "cmd_renameAttachment":
+ return MessageGetNumSelectedAttachments() == 1;
+ case "cmd_selectAll":
+ return MessageHasAttachments();
+ case "cmd_openAttachment":
+ return MessageGetNumSelectedAttachments() == 1;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(aCommand)
+ {
+ switch (aCommand)
+ {
+ case "cmd_delete":
+ if (MessageGetNumSelectedAttachments() > 0)
+ RemoveSelectedAttachment();
+ break;
+ case "cmd_renameAttachment":
+ if (MessageGetNumSelectedAttachments() == 1)
+ RenameSelectedAttachment();
+ break;
+ case "cmd_selectAll":
+ if (MessageHasAttachments())
+ SelectAllAttachments();
+ break;
+ case "cmd_openAttachment":
+ if (MessageGetNumSelectedAttachments() == 1)
+ OpenSelectedAttachment();
+ break;
+ default:
+ return;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ }
+};
+
+function QuoteSelectedMessage()
+{
+ var selectedURIs = GetSelectedMessages();
+ if (selectedURIs)
+ for (let i = 0; i < selectedURIs.length; i++)
+ gMsgCompose.quoteMessage(selectedURIs[i]);
+}
+
+function GetSelectedMessages()
+{
+ var mailWindow = gMsgCompose && Services.wm.getMostRecentWindow("mail:3pane");
+ return mailWindow && mailWindow.gFolderDisplay.selectedMessageUris;
+}
+
+function SetupCommandUpdateHandlers()
+{
+ top.controllers.appendController(defaultController);
+
+ let attachmentBucket = document.getElementById("attachmentBucket");
+ attachmentBucket.controllers.appendController(gAttachmentBucketController);
+
+ document.getElementById("optionsMenuPopup")
+ .addEventListener("popupshowing", updateOptionItems, true);
+}
+
+function UnloadCommandUpdateHandlers()
+{
+ document.getElementById("optionsMenuPopup")
+ .removeEventListener("popupshowing", updateOptionItems, true);
+
+ top.controllers.removeController(defaultController);
+
+ let attachmentBucket = document.getElementById("attachmentBucket");
+ attachmentBucket.controllers.removeController(gAttachmentBucketController);
+}
+
+function CommandUpdate_MsgCompose()
+{
+ var focusedWindow = top.document.commandDispatcher.focusedWindow;
+
+ // we're just setting focus to where it was before
+ if (focusedWindow == gLastWindowToHaveFocus) {
+ return;
+ }
+
+ gLastWindowToHaveFocus = focusedWindow;
+
+ updateComposeItems();
+}
+
+function updateComposeItems()
+{
+ try {
+ // Edit Menu
+ goUpdateCommand("cmd_rewrap");
+
+ // Insert Menu
+ if (gMsgCompose && gMsgCompose.composeHTML)
+ {
+ goUpdateCommand("cmd_renderedHTMLEnabler");
+ goUpdateCommand("cmd_decreaseFontStep");
+ goUpdateCommand("cmd_increaseFontStep");
+ goUpdateCommand("cmd_bold");
+ goUpdateCommand("cmd_italic");
+ goUpdateCommand("cmd_underline");
+ goUpdateCommand("cmd_ul");
+ goUpdateCommand("cmd_ol");
+ goUpdateCommand("cmd_indent");
+ goUpdateCommand("cmd_outdent");
+ goUpdateCommand("cmd_align");
+ goUpdateCommand("cmd_smiley");
+ }
+
+ // Options Menu
+ goUpdateCommand("cmd_spelling");
+ } catch(e) {}
+}
+
+function openEditorContextMenu(popup)
+{
+ gContextMenu = new nsContextMenu(popup);
+ if (gContextMenu.shouldDisplay)
+ {
+ // If message body context menu then focused element should be content.
+ var showPasteExtra =
+ top.document.commandDispatcher.focusedWindow == content;
+ gContextMenu.showItem("context-pasteNoFormatting", showPasteExtra);
+ gContextMenu.showItem("context-pasteQuote", showPasteExtra);
+ if (showPasteExtra)
+ {
+ goUpdateCommand("cmd_pasteNoFormatting");
+ goUpdateCommand("cmd_pasteQuote");
+ }
+ return true;
+ }
+ return false;
+}
+
+function updateEditItems()
+{
+ goUpdateCommand("cmd_pasteNoFormatting");
+ goUpdateCommand("cmd_pasteQuote");
+ goUpdateCommand("cmd_delete");
+ goUpdateCommand("cmd_renameAttachment");
+ goUpdateCommand("cmd_selectAll");
+ goUpdateCommand("cmd_openAttachment");
+ goUpdateCommand("cmd_findReplace");
+ goUpdateCommand("cmd_find");
+ goUpdateCommand("cmd_findNext");
+ goUpdateCommand("cmd_findPrev");
+}
+
+function updateOptionItems()
+{
+ goUpdateCommand("cmd_quoteMessage");
+}
+
+/**
+ * Update all the commands for sending a message to reflect their current state.
+ */
+function updateSendCommands(aHaveController) {
+ updateSendLock();
+ if (aHaveController) {
+ goUpdateCommand("cmd_sendButton");
+ goUpdateCommand("cmd_sendNow");
+ goUpdateCommand("cmd_sendLater");
+ goUpdateCommand("cmd_sendWithCheck");
+ } else {
+ goSetCommandEnabled("cmd_sendButton",
+ defaultController.isCommandEnabled("cmd_sendButton"));
+ goSetCommandEnabled("cmd_sendNow",
+ defaultController.isCommandEnabled("cmd_sendNow"));
+ goSetCommandEnabled("cmd_sendLater",
+ defaultController.isCommandEnabled("cmd_sendLater"));
+ goSetCommandEnabled("cmd_sendWithCheck",
+ defaultController.isCommandEnabled("cmd_sendWithCheck"));
+ }
+}
+
+var messageComposeOfflineQuitObserver = {
+ observe: function(aSubject, aTopic, aState) {
+ // sanity checks
+ if (aTopic == "network:offline-status-changed")
+ {
+ MessageComposeOfflineStateChanged(aState == "offline");
+ }
+ // check whether to veto the quit request (unless another observer already
+ // did)
+ else if (aTopic == "quit-application-requested" &&
+ aSubject instanceof Ci.nsISupportsPRBool &&
+ !aSubject.data)
+ aSubject.data = !ComposeCanClose();
+ }
+}
+
+function AddMessageComposeOfflineQuitObserver()
+{
+ Services.obs.addObserver(messageComposeOfflineQuitObserver,
+ "network:offline-status-changed");
+ Services.obs.addObserver(messageComposeOfflineQuitObserver,
+ "quit-application-requested");
+
+ // set the initial state of the send button
+ MessageComposeOfflineStateChanged(Services.io.offline);
+}
+
+function RemoveMessageComposeOfflineQuitObserver()
+{
+ Services.obs.removeObserver(messageComposeOfflineQuitObserver,
+ "network:offline-status-changed");
+ Services.obs.removeObserver(messageComposeOfflineQuitObserver,
+ "quit-application-requested");
+}
+
+function MessageComposeOfflineStateChanged(goingOffline)
+{
+ try {
+ var sendButton = document.getElementById("button-send");
+ var sendNowMenuItem = document.getElementById("menu_sendNow");
+
+ if (!gSavedSendNowKey) {
+ gSavedSendNowKey = sendNowMenuItem.getAttribute('key');
+ }
+
+ // don't use goUpdateCommand here ... the defaultController might not be installed yet
+ updateSendCommands(false);
+
+ if (goingOffline)
+ {
+ sendButton.label = sendButton.getAttribute('later_label');
+ sendButton.setAttribute('tooltiptext', sendButton.getAttribute('later_tooltiptext'));
+ sendNowMenuItem.removeAttribute('key');
+ }
+ else
+ {
+ sendButton.label = sendButton.getAttribute('now_label');
+ sendButton.setAttribute('tooltiptext', sendButton.getAttribute('now_tooltiptext'));
+ if (gSavedSendNowKey) {
+ sendNowMenuItem.setAttribute('key', gSavedSendNowKey);
+ }
+ }
+
+ } catch(e) {}
+}
+
+function DoCommandClose()
+{
+ if (ComposeCanClose()) {
+ // Notify the SendListener that Send has been aborted and Stopped
+ if (gMsgCompose)
+ gMsgCompose.onSendNotPerformed(null, Cr.NS_ERROR_ABORT);
+
+ // note: if we're not caching this window, this destroys it for us
+ MsgComposeCloseWindow();
+ }
+
+ return false;
+}
+
+function DoCommandPreferences()
+{
+ goPreferences('composing_messages_pane');
+}
+
+function toggleAffectedChrome(aHide)
+{
+ // chrome to toggle includes:
+ // (*) menubar
+ // (*) toolbox
+ // (*) sidebar
+ // (*) statusbar
+
+ if (!gChromeState)
+ gChromeState = {};
+
+ var statusbar = document.getElementById("status-bar");
+
+ // sidebar states map as follows:
+ // hidden => hide/show nothing
+ // collapsed => hide/show only the splitter
+ // shown => hide/show the splitter and the box
+ if (aHide)
+ {
+ // going into print preview mode
+ gChromeState.sidebar = SidebarGetState();
+ SidebarSetState("hidden");
+
+ // deal with the Status Bar
+ gChromeState.statusbarWasHidden = statusbar.hidden;
+ statusbar.hidden = true;
+ }
+ else
+ {
+ // restoring normal mode (i.e., leaving print preview mode)
+ SidebarSetState(gChromeState.sidebar);
+
+ // restore the Status Bar
+ statusbar.hidden = gChromeState.statusbarWasHidden;
+ }
+
+ // if we are unhiding and sidebar used to be there rebuild it
+ if (!aHide && gChromeState.sidebar == "visible")
+ SidebarRebuild();
+
+ getMailToolbox().hidden = aHide;
+ document.getElementById("appcontent").collapsed = aHide;
+}
+
+var PrintPreviewListener = {
+ getPrintPreviewBrowser()
+ {
+ var browser = document.getElementById("ppBrowser");
+ if (!browser)
+ {
+ browser = document.createElement("browser");
+ browser.setAttribute("id", "ppBrowser");
+ browser.setAttribute("flex", "1");
+ browser.setAttribute("disablehistory", "true");
+ browser.setAttribute("disablesecurity", "true");
+ browser.setAttribute("type", "content");
+ document.getElementById("sidebar-parent")
+ .insertBefore(browser, document.getElementById("appcontent"));
+ }
+ return browser;
+ },
+ getSourceBrowser()
+ {
+ return GetCurrentEditorElement();
+ },
+ getNavToolbox()
+ {
+ return getMailToolbox();
+ },
+ onEnter()
+ {
+ toggleAffectedChrome(true);
+ },
+ onExit()
+ {
+ document.getElementById("ppBrowser").collapsed = true;
+ toggleAffectedChrome(false);
+ }
+}
+
+function ToggleWindowLock()
+{
+ gWindowLocked = !gWindowLocked;
+ updateComposeItems();
+}
+
+/* This function will go away soon as now arguments are passed to the window using a object of type nsMsgComposeParams instead of a string */
+function GetArgs(originalData)
+{
+ var args = new Object();
+
+ if (originalData == "")
+ return null;
+
+ var data = "";
+ var separator = String.fromCharCode(1);
+
+ var quoteChar = "";
+ var prevChar = "";
+ var nextChar = "";
+ for (let i = 0; i < originalData.length; i++, prevChar = aChar)
+ {
+ var aChar = originalData.charAt(i)
+ var aCharCode = originalData.charCodeAt(i)
+ if ( i < originalData.length - 1)
+ nextChar = originalData.charAt(i + 1);
+ else
+ nextChar = "";
+
+ if (aChar == quoteChar && (nextChar == "," || nextChar == ""))
+ {
+ quoteChar = "";
+ data += aChar;
+ }
+ else if ((aCharCode == 39 || aCharCode == 34) && prevChar == "=") //quote or double quote
+ {
+ if (quoteChar == "")
+ quoteChar = aChar;
+ data += aChar;
+ }
+ else if (aChar == ",")
+ {
+ if (quoteChar == "")
+ data += separator;
+ else
+ data += aChar
+ }
+ else
+ data += aChar
+ }
+
+ var pairs = data.split(separator);
+
+ for (let i = pairs.length - 1; i >= 0; i--)
+ {
+ var pos = pairs[i].indexOf('=');
+ if (pos == -1)
+ continue;
+ var argname = pairs[i].substring(0, pos);
+ var argvalue = pairs[i].substring(pos + 1);
+ if (argvalue.charAt(0) == "'" && argvalue.charAt(argvalue.length - 1) == "'")
+ args[argname] = argvalue.substring(1, argvalue.length - 1);
+ else
+ try {
+ args[argname] = decodeURIComponent(argvalue);
+ } catch (e) {args[argname] = argvalue;}
+ // dump("[" + argname + "=" + args[argname] + "]\n");
+ }
+ return args;
+}
+
+function ComposeFieldsReady()
+{
+ //If we are in plain text, we need to set the wrap column
+ if (! gMsgCompose.composeHTML) {
+ try {
+ gMsgCompose.editor.wrapWidth = gMsgCompose.wrapLength;
+ }
+ catch (e) {
+ dump("### textEditor.wrapWidth exception text: " + e + " - failed\n");
+ }
+ }
+ CompFields2Recipients(gMsgCompose.compFields);
+ SetComposeWindowTitle();
+ enableEditableFields();
+}
+
+// checks if the passed in string is a mailto url, if it is, generates nsIMsgComposeParams
+// for the url and returns them.
+function handleMailtoArgs(mailtoUrl)
+{
+ // see if the string is a mailto url....do this by checking the first 7 characters of the string
+ if (/^mailto:/i.test(mailtoUrl))
+ {
+ // if it is a mailto url, turn the mailto url into a MsgComposeParams object....
+ var uri = Services.io.newURI(mailtoUrl);
+
+ if (uri)
+ return MailServices.compose.getParamsForMailto(uri);
+ }
+
+ return null;
+}
+/**
+ * Handle ESC keypress from composition window for
+ * notifications with close button in the
+ * attachmentNotificationBox.
+ */
+function handleEsc()
+{
+ let activeElement = document.activeElement;
+
+ // If findbar is visible and the focus is in the message body,
+ // hide it. (Focus on the findbar is handled by findbar itself).
+ let findbar = document.getElementById("FindToolbar");
+ if (findbar && !findbar.hidden && activeElement.id == "content-frame") {
+ findbar.close();
+ return;
+ }
+
+ // If there is a notification in the attachmentNotificationBox
+ // AND focus is in message body, subject field or on the notification,
+ // hide it.
+ let notification = document.getElementById("attachmentNotificationBox")
+ .currentNotification;
+ if (notification && (activeElement.id == "content-frame" ||
+ activeElement.parentNode.parentNode.id == "msgSubject" ||
+ notification.contains(activeElement) ||
+ activeElement.classList.contains("messageCloseButton"))) {
+ notification.close();
+ }
+}
+
+/**
+ * On paste or drop, we may want to modify the content before inserting it into
+ * the editor, replacing file URLs with data URLs when appropriate.
+ */
+function onPasteOrDrop(e) {
+ // For paste use e.clipboardData, for drop use e.dataTransfer.
+ let dataTransfer = ("clipboardData" in e) ? e.clipboardData : e.dataTransfer;
+
+ if (!dataTransfer.types.includes("text/html")) {
+ return;
+ }
+
+ if (!gMsgCompose.composeHTML) {
+ // We're in the plain text editor. Nothing to do here.
+ return;
+ }
+
+ let html = dataTransfer.getData("text/html");
+ let doc = (new DOMParser()).parseFromString(html, "text/html");
+ let tmpD = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ let pendingConversions = 0;
+ let needToPreventDefault = true;
+ for (let img of doc.images) {
+ if (!/^file:/i.test(img.src)) {
+ // Doesn't start with file:. Nothing to do here.
+ continue;
+ }
+
+ // This may throw if the URL is invalid for the OS.
+ let nsFile;
+ try {
+ nsFile = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler)
+ .getFileFromURLSpec(img.src);
+ } catch (ex) {
+ continue;
+ }
+
+ if (!nsFile.exists()) {
+ continue;
+ }
+
+ if (!tmpD.contains(nsFile)) {
+ // Not anywhere under the temp dir.
+ continue;
+ }
+
+ let contentType = Cc["@mozilla.org/mime;1"]
+ .getService(Ci.nsIMIMEService)
+ .getTypeFromFile(nsFile);
+ if (!contentType.startsWith("image/")) {
+ continue;
+ }
+
+ // If we ever get here, we need to prevent the default paste or drop since
+ // the code below will do its own insertion.
+ if (needToPreventDefault) {
+ e.preventDefault();
+ needToPreventDefault = false;
+ }
+
+ File.createFromNsIFile(nsFile).then(function(file) {
+ if (file.lastModified < (Date.now() - 60000)) {
+ // Not put in temp in the last minute. May be something other than
+ // a copy-paste. Let's not allow that.
+ return;
+ }
+
+ let doTheInsert = function() {
+ // Now run it through sanitation to make sure there wasn't any
+ // unwanted things in the content.
+ let ParserUtils = Cc["@mozilla.org/parserutils;1"]
+ .getService(Ci.nsIParserUtils);
+ let html2 = ParserUtils.sanitize(doc.documentElement.innerHTML,
+ ParserUtils.SanitizerAllowStyle);
+ getBrowser().contentDocument.execCommand("insertHTML", false, html2);
+ }
+
+ // Everything checks out. Convert file to data URL.
+ let reader = new FileReader();
+ reader.addEventListener("load", function() {
+ let dataURL = reader.result;
+ pendingConversions--;
+ img.src = dataURL;
+ if (pendingConversions == 0) {
+ doTheInsert();
+ }
+ });
+
+ reader.addEventListener("error", function() {
+ pendingConversions--;
+ if (pendingConversions == 0) {
+ doTheInsert();
+ }
+ });
+
+ pendingConversions++;
+ reader.readAsDataURL(file);
+ });
+ }
+}
+
+function ComposeStartup(aParams)
+{
+ var params = null; // New way to pass parameters to the compose window as a nsIMsgComposeParameters object
+ var args = null; // old way, parameters are passed as a string
+ gBodyFromArgs = false;
+
+ if (aParams)
+ params = aParams;
+ else if (window.arguments && window.arguments[0]) {
+ try {
+ if (window.arguments[0] instanceof Ci.nsIMsgComposeParams)
+ params = window.arguments[0];
+ else
+ params = handleMailtoArgs(window.arguments[0]);
+ }
+ catch(ex) { dump("ERROR with parameters: " + ex + "\n"); }
+
+ // if still no dice, try and see if the params is an old fashioned list of string attributes
+ // XXX can we get rid of this yet?
+ if (!params)
+ {
+ args = GetArgs(window.arguments[0]);
+ }
+ }
+
+ // Set the document language to the preference as early as possible.
+ document.documentElement
+ .setAttribute("lang", Services.prefs.getCharPref("spellchecker.dictionary"));
+
+ var identityList = GetMsgIdentityElement();
+
+ document.addEventListener("paste", onPasteOrDrop);
+ document.addEventListener("drop", onPasteOrDrop);
+
+ if (identityList)
+ FillIdentityList(identityList);
+
+ if (!params) {
+ // This code will go away soon as now arguments are passed to the window
+ // using a object of type nsMsgComposeParams instead of a string.
+ params = Cc["@mozilla.org/messengercompose/composeparams;1"]
+ .createInstance(Ci.nsIMsgComposeParams);
+ params.composeFields = Cc["@mozilla.org/messengercompose/composefields;1"]
+ .createInstance(Ci.nsIMsgCompFields);
+
+ if (args) { //Convert old fashion arguments into params
+ var composeFields = params.composeFields;
+ if (args.bodyislink && args.bodyislink == "true")
+ params.bodyIsLink = true;
+ if (args.type)
+ params.type = args.type;
+ if (args.format) {
+ // Only use valid values.
+ if (args.format == Ci.nsIMsgCompFormat.PlainText ||
+ args.format == Ci.nsIMsgCompFormat.HTML ||
+ args.format == Ci.nsIMsgCompFormat.OppositeOfDefault)
+ params.format = args.format;
+ else if (args.format.toLowerCase().trim() == "html")
+ params.format = Ci.nsIMsgCompFormat.HTML;
+ else if (args.format.toLowerCase().trim() == "text")
+ params.format = Ci.nsIMsgCompFormat.PlainText;
+ }
+ if (args.originalMsgURI)
+ params.originalMsgURI = args.originalMsgURI;
+ if (args.preselectid)
+ params.identity = getIdentityForKey(args.preselectid);
+ if (args.from)
+ composeFields.from = args.from;
+ if (args.to)
+ composeFields.to = args.to;
+ if (args.cc)
+ composeFields.cc = args.cc;
+ if (args.bcc)
+ composeFields.bcc = args.bcc;
+ if (args.newsgroups)
+ composeFields.newsgroups = args.newsgroups;
+ if (args.subject)
+ composeFields.subject = args.subject;
+ if (args.attachment)
+ {
+ var attachmentList = args.attachment.split(",");
+ var commandLine = Cc["@mozilla.org/toolkit/command-line;1"]
+ .createInstance();
+ for (let i = 0; i < attachmentList.length; i++)
+ {
+ let attachmentStr = attachmentList[i];
+ let uri = commandLine.resolveURI(attachmentStr);
+ let attachment = Cc["@mozilla.org/messengercompose/attachment;1"]
+ .createInstance(Ci.nsIMsgAttachment);
+
+ if (uri instanceof Ci.nsIFileURL)
+ {
+ if (uri.file.exists())
+ attachment.size = uri.file.fileSize;
+ else
+ attachment = null;
+ }
+
+ // Only want to attach if a file that exists or it is not a file.
+ if (attachment)
+ {
+ attachment.url = uri.spec;
+ composeFields.addAttachment(attachment);
+ }
+ else
+ {
+ let title = sComposeMsgsBundle.getString("errorFileAttachTitle");
+ let msg = sComposeMsgsBundle.getFormattedString("errorFileAttachMessage",
+ [attachmentStr]);
+ Services.prompt.alert(null, title, msg);
+ }
+ }
+ }
+ if (args.newshost)
+ composeFields.newshost = args.newshost;
+ if (args.message) {
+ let msgFile = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsIFile);
+ if (OS.Path.dirname(args.message) == ".") {
+ let workingDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ args.message = OS.Path.join(workingDir.path, OS.Path.basename(args.message));
+ }
+ msgFile.initWithPath(args.message);
+
+ if (!msgFile.exists()) {
+ let title = sComposeMsgsBundle.getString("errorFileMessageTitle");
+ let msg = sComposeMsgsBundle.getFormattedString("errorFileMessageMessage",
+ [args.message]);
+ Services.prompt.alert(null, title, msg);
+ } else {
+ let data = "";
+ let fstream = null;
+ let cstream = null;
+
+ try {
+ fstream = Cc["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Ci.nsIFileInputStream);
+ cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
+ .createInstance(Ci.nsIConverterInputStream);
+ fstream.init(msgFile, -1, 0, 0); // Open file in default/read-only mode.
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ let str = {};
+ let read = 0;
+
+ do {
+ // Read as much as we can and put it in str.value.
+ read = cstream.readString(0xffffffff, str);
+ data += str.value;
+ } while (read != 0);
+ } catch (e) {
+ let title = sComposeMsgsBundle.getString("errorFileMessageTitle");
+ let msg = sComposeMsgsBundle.getFormattedString("errorLoadFileMessageMessage",
+ [args.message]);
+ Services.prompt.alert(null, title, msg);
+
+ } finally {
+ if (cstream)
+ cstream.close();
+ if (fstream)
+ fstream.close();
+ }
+
+ if (data) {
+ let pos = data.search(/\S/); // Find first non-whitespace character.
+
+ if (params.format != Ci.nsIMsgCompFormat.PlainText &&
+ (args.message.endsWith(".htm") ||
+ args.message.endsWith(".html") ||
+ data.substr(pos, 14).toLowerCase() == "<!doctype html" ||
+ data.substr(pos, 5).toLowerCase() == "<html")) {
+ // We replace line breaks because otherwise they'll be converted
+ // to <br> in nsMsgCompose::BuildBodyMessageAndSignature().
+ // Don't do the conversion if the user asked explicitly for plain
+ // text.
+ data = data.replace(/\r?\n/g, " ");
+ }
+ gBodyFromArgs = true;
+ composeFields.body = data;
+ }
+ }
+ } else if (args.body) {
+ gBodyFromArgs = true;
+ composeFields.body = args.body;
+ }
+ }
+ }
+
+ gComposeType = params.type;
+
+ // Detect correct identity when missing or mismatched.
+ // An identity with no email is likely not valid.
+ // When editing a draft, 'params.identity' is pre-populated with the identity
+ // that created the draft or the identity owning the draft folder for a
+ // "foreign", draft, see ComposeMessage() in mailCommands.js. We don't want
+ // the latter, so use the creator identity which could be null.
+ if (gComposeType == Ci.nsIMsgCompType.Draft) {
+ let creatorKey = params.composeFields.creatorIdentityKey;
+ params.identity = creatorKey ? getIdentityForKey(creatorKey) : null;
+ }
+ let from = [];
+ if (params.composeFields.from)
+ from = MailServices.headerParser
+ .parseEncodedHeader(params.composeFields.from, null);
+ from = (from.length && from[0] && from[0].email) ?
+ from[0].email.toLowerCase().trim() : null;
+ if (!params.identity || !params.identity.email ||
+ (from && !emailSimilar(from, params.identity.email))) {
+ let identities = MailServices.accounts.allIdentities;
+ let suitableCount = 0;
+
+ // Search for a matching identity.
+ if (from) {
+ for (let ident of identities) {
+ if (ident.email && from == ident.email.toLowerCase()) {
+ if (suitableCount == 0)
+ params.identity = ident;
+ suitableCount++;
+ if (suitableCount > 1)
+ break; // No need to find more, it's already not unique.
+ }
+ }
+ }
+
+ if (!params.identity || !params.identity.email) {
+ let identity = null;
+ // No preset identity and no match, so use the default account.
+ let defaultAccount = MailServices.accounts.defaultAccount;
+ if (defaultAccount) {
+ identity = defaultAccount.defaultIdentity;
+ }
+ if (!identity) {
+ // Get the first identity we have in the list.
+ let identitykey = identityList.getItemAtIndex(0).getAttribute("identitykey");
+ identity = MailServices.accounts.getIdentity(identitykey);
+ }
+ params.identity = identity;
+ }
+
+ // Warn if no or more than one match was found.
+ // But don't warn for +suffix additions (a+b@c.com).
+ if (from && (suitableCount > 1 ||
+ (suitableCount == 0 && !emailSimilar(from, params.identity.email))))
+ gComposeNotificationBar.setIdentityWarning(params.identity.identityName);
+ }
+
+ identityList.selectedItem =
+ identityList.getElementsByAttribute("identitykey", params.identity.key)[0];
+ if (params.composeFields.from)
+ identityList.value = MailServices.headerParser.parseDecodedHeader(params.composeFields.from)[0].toString();
+ LoadIdentity(true);
+
+ // Get the <editor> element to startup an editor
+ var editorElement = GetCurrentEditorElement();
+
+ // Remember the original message URI. When editing a draft which is a reply
+ // or forwarded message, this gets overwritten by the ancestor's message URI
+ // so the disposition flags ("replied" or "forwarded") can be set on the
+ // ancestor.
+ // For our purposes we need the URI of the message being processed, not its
+ // original ancestor.
+ gOriginalMsgURI = params.originalMsgURI;
+ gMsgCompose = MailServices.compose.initCompose(params, window,
+ editorElement.docShell);
+
+ document.getElementById("returnReceiptMenu")
+ .setAttribute("checked", gMsgCompose.compFields.returnReceipt);
+ document.getElementById("dsnMenu")
+ .setAttribute('checked', gMsgCompose.compFields.DSN);
+ document.getElementById("cmd_attachVCard")
+ .setAttribute("checked", gMsgCompose.compFields.attachVCard);
+ document.getElementById("menu_inlineSpellCheck")
+ .setAttribute("checked",
+ Services.prefs.getBoolPref("mail.spellcheck.inline"));
+
+ let editortype = gMsgCompose.composeHTML ? "htmlmail" : "textmail";
+ editorElement.makeEditable(editortype, true);
+
+ // setEditorType MUST be call before setContentWindow
+ if (gMsgCompose.composeHTML) {
+ initLocalFontFaceMenu(document.getElementById("FontFacePopup"));
+ } else {
+ //Remove HTML toolbar, format and insert menus as we are editing in plain
+ //text mode.
+ let toolbar = document.getElementById("FormatToolbar");
+ toolbar.hidden = true;
+ toolbar.setAttribute("hideinmenu", "true");
+ document.getElementById("outputFormatMenu").setAttribute("hidden", true);
+ document.getElementById("formatMenu").setAttribute("hidden", true);
+ document.getElementById("insertMenu").setAttribute("hidden", true);
+ }
+
+ // Do setup common to Message Composer and Web Composer.
+ EditorSharedStartup();
+
+ if (params.bodyIsLink) {
+ let body = gMsgCompose.compFields.body;
+ if (gMsgCompose.composeHTML) {
+ let cleanBody;
+ try {
+ cleanBody = decodeURI(body);
+ } catch(e) {
+ cleanBody = body;
+ }
+
+ body = body.replace(/&/g, "&amp;");
+ gMsgCompose.compFields.body =
+ "<br /><a href=\"" + body + "\">" + cleanBody + "</a><br />";
+ } else {
+ gMsgCompose.compFields.body = "\n<" + body + ">\n";
+ }
+ }
+
+ GetMsgSubjectElement().value = gMsgCompose.compFields.subject;
+
+ var attachments = gMsgCompose.compFields.attachments;
+ while (attachments.hasMoreElements()) {
+ AddAttachment(attachments.getNext().QueryInterface(Ci.nsIMsgAttachment));
+ }
+
+ var event = document.createEvent('Events');
+ event.initEvent('compose-window-init', false, true);
+ document.getElementById("msgcomposeWindow").dispatchEvent(event);
+
+ gMsgCompose.RegisterStateListener(stateListener);
+
+ // Add an observer to be called when document is done loading,
+ // which creates the editor.
+ try {
+ GetCurrentCommandManager().addCommandObserver(gMsgEditorCreationObserver,
+ "obs_documentCreated");
+
+ // Load empty page to create the editor
+ editorElement.webNavigation.loadURI("about:blank",
+ Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
+ null, // referrer
+ null, // post-data stream
+ null, // HTTP headers
+ Services.scriptSecurityManager.getSystemPrincipal());
+ } catch (e) {
+ dump(" Failed to startup editor: "+e+"\n");
+ }
+
+ // create URI of the folder from draftId
+ var draftId = gMsgCompose.compFields.draftId;
+ var folderURI = draftId.substring(0, draftId.indexOf("#")).replace("-message", "");
+
+ try {
+ var folder = sRDF.GetResource(folderURI);
+
+ gEditingDraft = (folder instanceof Ci.nsIMsgFolder) &&
+ (folder.flags & Ci.nsMsgFolderFlags.Drafts);
+ }
+ catch (ex) {
+ gEditingDraft = false;
+ }
+
+ gAutoSaveKickedIn = false;
+
+ gAutoSaveInterval = Services.prefs.getBoolPref("mail.compose.autosave")
+ ? Services.prefs.getIntPref("mail.compose.autosaveinterval") * 60000
+ : 0;
+
+ if (gAutoSaveInterval)
+ gAutoSaveTimeout = setTimeout(AutoSave, gAutoSaveInterval);
+}
+
+function splitEmailAddress(aEmail) {
+ let at = aEmail.lastIndexOf("@");
+ return (at != -1) ? [aEmail.slice(0, at), aEmail.slice(at + 1)]
+ : [aEmail, ""];
+}
+
+// Emails are equal ignoring +suffixes (email+suffix@example.com).
+function emailSimilar(a, b) {
+ if (!a || !b)
+ return a == b;
+ a = splitEmailAddress(a.toLowerCase());
+ b = splitEmailAddress(b.toLowerCase());
+ return a[1] == b[1] && a[0].split("+", 1)[0] == b[0].split("+", 1)[0];
+}
+
+// The new, nice, simple way of getting notified when a new editor has been created
+var gMsgEditorCreationObserver =
+{
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic == "obs_documentCreated")
+ {
+ var editor = GetCurrentEditor();
+ var commandManager = GetCurrentCommandManager();
+ if (editor && commandManager == aSubject) {
+ let editorStyle = editor.QueryInterface(Ci.nsIEditorStyleSheets);
+ // We use addOverrideStyleSheet rather than addStyleSheet so that we get
+ // a synchronous load, rather than having a late-finishing async load
+ // mark our editor as modified when the user hasn't typed anything yet,
+ // but that means the sheet must not @import slow things, especially
+ // not over the network.
+ editorStyle.addOverrideStyleSheet("chrome://messenger/skin/messageQuotes.css");
+ InitEditor(editor);
+ }
+ // Now that we know this document is an editor, update commands now if
+ // the document has focus, or next time it receives focus via
+ // CommandUpdate_MsgCompose()
+ if (gLastWindowToHaveFocus == document.commandDispatcher.focusedWindow)
+ updateComposeItems();
+ else
+ gLastWindowToHaveFocus = null;
+ }
+ }
+}
+
+function WizCallback(state)
+{
+ if (state){
+ ComposeStartup(null);
+ }
+ else
+ {
+ // The account wizard is still closing so we can't close just yet
+ setTimeout(MsgComposeCloseWindow, 0);
+ }
+}
+
+function ComposeLoad()
+{
+ sComposeMsgsBundle = document.getElementById("bundle_composeMsgs");
+ sBrandBundle = document.getElementById("brandBundle");
+
+ var otherHeaders = Services.prefs.getCharPref("mail.compose.other.header");
+
+ sRDF = Cc['@mozilla.org/rdf/rdf-service;1']
+ .getService(Ci.nsIRDFService);
+ sNameProperty = sRDF.GetResource("http://home.netscape.com/NC-rdf#Name?sort=true");
+
+ AddMessageComposeOfflineQuitObserver();
+
+ if (gLogComposePerformance)
+ MailServices.compose.TimeStamp("Start initializing the compose window (ComposeLoad)", false);
+
+ msgWindow.notificationCallbacks = new nsMsgBadCertHandler();
+
+ try {
+ SetupCommandUpdateHandlers();
+ // This will do migration, or create a new account if we need to.
+ // We also want to open the account wizard if no identities are found
+ var state = verifyAccounts(WizCallback, true);
+
+ if (otherHeaders) {
+ var selectNode = document.getElementById('addressCol1#1');
+ var otherHeaders_Array = otherHeaders.split(",");
+ for (let i = 0; i < otherHeaders_Array.length; i++)
+ selectNode.appendItem(otherHeaders_Array[i] + ":", "addr_other");
+ }
+ if (state)
+ ComposeStartup(null);
+ }
+ catch (ex) {
+ Cu.reportError(ex);
+ var errorTitle = sComposeMsgsBundle.getString("initErrorDlogTitle");
+ var errorMsg = sComposeMsgsBundle.getString("initErrorDlgMessage");
+ Services.prompt.alert(window, errorTitle, errorMsg);
+
+ MsgComposeCloseWindow();
+ return;
+ }
+ if (gLogComposePerformance)
+ MailServices.compose.TimeStamp("Done with the initialization (ComposeLoad). Waiting on editor to load about:blank", false);
+
+ // Before and after callbacks for the customizeToolbar code
+ var mailToolbox = getMailToolbox();
+ mailToolbox.customizeInit = MailToolboxCustomizeInit;
+ mailToolbox.customizeDone = MailToolboxCustomizeDone;
+ mailToolbox.customizeChange = MailToolboxCustomizeChange;
+}
+
+function ComposeUnload()
+{
+ // Send notification that the window is going away completely.
+ document.getElementById("msgcomposeWindow").dispatchEvent(
+ new Event("compose-window-unload", { bubbles: false, cancelable: false }));
+
+ GetCurrentCommandManager().removeCommandObserver(gMsgEditorCreationObserver,
+ "obs_documentCreated");
+ UnloadCommandUpdateHandlers();
+
+ // Stop InlineSpellCheckerUI so personal dictionary is saved
+ EnableInlineSpellCheck(false);
+
+ EditorCleanup();
+
+ RemoveMessageComposeOfflineQuitObserver();
+
+ if (gMsgCompose)
+ gMsgCompose.UnregisterStateListener(stateListener);
+ if (gAutoSaveTimeout)
+ clearTimeout(gAutoSaveTimeout);
+ if (msgWindow) {
+ msgWindow.closeWindow();
+ msgWindow.notificationCallbacks = null;
+ }
+
+ ReleaseGlobalVariables();
+}
+
+function ComposeSetCharacterSet(aEvent)
+{
+ if (gMsgCompose)
+ SetDocumentCharacterSet(aEvent.target.getAttribute("charset"));
+ else
+ dump("Compose has not been created!\n");
+}
+
+function SetDocumentCharacterSet(aCharset)
+{
+ // Replace generic Japanese with ISO-2022-JP.
+ if (aCharset == "Japanese") {
+ aCharset = "ISO-2022-JP";
+ }
+ gMsgCompose.SetDocumentCharset(aCharset);
+ SetComposeWindowTitle();
+}
+
+function GetCharsetUIString()
+{
+ // The charset here is already the canonical charset (not an alias).
+ let charset = gMsgCompose.compFields.characterSet;
+ if (!charset)
+ return "";
+
+ if (charset.toLowerCase() != gMsgCompose.compFields.defaultCharacterSet.toLowerCase()) {
+ try {
+ return " - " + gCharsetConvertManager.getCharsetTitle(charset);
+ }
+ catch(e) { // Not a canonical charset after all...
+ Cu.reportError("Not charset title for charset=" + charset);
+ return " - " + charset;
+ }
+ }
+ return "";
+}
+
+// Add-ons can override this to customize the behavior.
+function DoSpellCheckBeforeSend()
+{
+ return Services.prefs.getBoolPref("mail.SpellCheckBeforeSend");
+}
+
+/**
+ * Handles message sending operations.
+ * @param msgType nsIMsgCompDeliverMode of the operation.
+ */
+function GenericSendMessage(msgType) {
+ var msgCompFields = gMsgCompose.compFields;
+
+ Recipients2CompFields(msgCompFields);
+ var address = GetMsgIdentityElement().value;
+ address = MailServices.headerParser.makeFromDisplayAddress(address);
+ msgCompFields.from = MailServices.headerParser.makeMimeHeader([address[0]]);
+ var subject = GetMsgSubjectElement().value;
+ msgCompFields.subject = subject;
+ Attachments2CompFields(msgCompFields);
+
+ if (msgType == Ci.nsIMsgCompDeliverMode.Now ||
+ msgType == Ci.nsIMsgCompDeliverMode.Later ||
+ msgType == Ci.nsIMsgCompDeliverMode.Background) {
+ //Do we need to check the spelling?
+ if (DoSpellCheckBeforeSend()) {
+ // We disable spellcheck for the following -subject line, attachment
+ // pane, identity and addressing widget therefore we need to explicitly
+ // focus on the mail body when we have to do a spellcheck.
+ SetMsgBodyFrameFocus();
+ window.cancelSendMessage = false;
+ window.openDialog("chrome://editor/content/EdSpellCheck.xul", "_blank",
+ "dialog,close,titlebar,modal,resizable",
+ true, true, false);
+ if (window.cancelSendMessage)
+ return;
+ }
+
+ // Strip trailing spaces and long consecutive WSP sequences from the
+ // subject line to prevent getting only WSP chars on a folded line.
+ var fixedSubject = subject.replace(/\s{74,}/g, " ")
+ .replace(/\s*$/, "");
+ if (fixedSubject != subject) {
+ subject = fixedSubject;
+ msgCompFields.subject = fixedSubject;
+ GetMsgSubjectElement().value = fixedSubject;
+ }
+
+ // Remind the person if there isn't a subject.
+ if (subject == "") {
+ if (Services.prompt.confirmEx(
+ window,
+ sComposeMsgsBundle.getString("subjectEmptyTitle"),
+ sComposeMsgsBundle.getString("subjectEmptyMessage"),
+ (Services.prompt.BUTTON_TITLE_IS_STRING *
+ Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_IS_STRING *
+ Services.prompt.BUTTON_POS_1),
+ sComposeMsgsBundle.getString("sendWithEmptySubjectButton"),
+ sComposeMsgsBundle.getString("cancelSendingButton"),
+ null, null, {value:0}) == 1) {
+ GetMsgSubjectElement().focus();
+ return;
+ }
+ }
+
+ // Check if the user tries to send a message to a newsgroup through a mail
+ // account.
+ var currentAccountKey = getCurrentAccountKey();
+ var account = MailServices.accounts.getAccount(currentAccountKey);
+ if (!account) {
+ throw "UNEXPECTED: currentAccountKey '" + currentAccountKey +
+ "' has no matching account!";
+ }
+
+ if (account.incomingServer.type != "nntp" &&
+ msgCompFields.newsgroups != "") {
+ const kDontAskAgainPref = "mail.compose.dontWarnMail2Newsgroup";
+ // Default to ask user if the pref is not set.
+ var dontAskAgain = Services.prefs.getBoolPref(kDontAskAgainPref);
+ if (!dontAskAgain) {
+ var checkbox = {value:false};
+ var okToProceed = Services.prompt.confirmCheck(
+ window,
+ sComposeMsgsBundle.getString("noNewsgroupSupportTitle"),
+ sComposeMsgsBundle.getString("recipientDlogMessage"),
+ sComposeMsgsBundle.getString("CheckMsg"),
+ checkbox);
+
+ if (!okToProceed)
+ return;
+ }
+ if (checkbox.value)
+ Services.prefs.setBoolPref(kDontAskAgainPref, true);
+
+ // Remove newsgroups to prevent news_p to be set
+ // in nsMsgComposeAndSend::DeliverMessage()
+ msgCompFields.newsgroups = "";
+ }
+
+ // Before sending the message, check what to do with HTML message,
+ // eventually abort.
+ var convert = DetermineConvertibility();
+ var action = DetermineHTMLAction(convert);
+ // Check if e-mail addresses are complete, in case user has turned off
+ // autocomplete to local domain.
+ if (!CheckValidEmailAddress(msgCompFields.to, msgCompFields.cc, msgCompFields.bcc))
+ return;
+
+ if (action == Ci.nsIMsgCompSendFormat.AskUser) {
+ var recommAction = (convert == Ci.nsIMsgCompConvertible.No)
+ ? Ci.nsIMsgCompSendFormat.AskUser
+ : Ci.nsIMsgCompSendFormat.PlainText;
+ var result2 = {action:recommAction, convertible:convert, abort:false};
+ window.openDialog("chrome://messenger/content/messengercompose/askSendFormat.xul",
+ "askSendFormatDialog", "chrome,modal,titlebar,centerscreen",
+ result2);
+ if (result2.abort)
+ return;
+ action = result2.action;
+ }
+
+ // We will remember the users "send format" decision in the address
+ // collector code (see nsAbAddressCollector::CollectAddress())
+ // by using msgCompFields.forcePlainText and
+ // msgCompFields.useMultipartAlternative to determine the
+ // nsIAbPreferMailFormat (unknown, plaintext, or html).
+ // If the user sends both, we remember html.
+ switch (action) {
+ case Ci.nsIMsgCompSendFormat.PlainText:
+ msgCompFields.forcePlainText = true;
+ msgCompFields.useMultipartAlternative = false;
+ break;
+ case Ci.nsIMsgCompSendFormat.HTML:
+ msgCompFields.forcePlainText = false;
+ msgCompFields.useMultipartAlternative = false;
+ break;
+ case Ci.nsIMsgCompSendFormat.Both:
+ msgCompFields.forcePlainText = false;
+ msgCompFields.useMultipartAlternative = true;
+ break;
+ default:
+ throw new Error("Invalid nsIMsgCompSendFormat action; action=" + action);
+ }
+ }
+
+ // Hook for extra compose pre-processing.
+ Services.obs.notifyObservers(window, "mail:composeOnSend");
+
+ var originalCharset = gMsgCompose.compFields.characterSet;
+ // Check if the headers of composing mail can be converted to a mail charset.
+ if (msgType == Ci.nsIMsgCompDeliverMode.Now ||
+ msgType == Ci.nsIMsgCompDeliverMode.Later ||
+ msgType == Ci.nsIMsgCompDeliverMode.Background ||
+ msgType == Ci.nsIMsgCompDeliverMode.Save ||
+ msgType == Ci.nsIMsgCompDeliverMode.SaveAsDraft ||
+ msgType == Ci.nsIMsgCompDeliverMode.AutoSaveAsDraft ||
+ msgType == Ci.nsIMsgCompDeliverMode.SaveAsTemplate) {
+ var fallbackCharset = new Object;
+ // Check encoding, switch to UTF-8 if the default encoding doesn't fit
+ // and disable_fallback_to_utf8 isn't set for this encoding.
+ if (!gMsgCompose.checkCharsetConversion(getCurrentIdentity(),
+ fallbackCharset)) {
+ let disableFallback = Services.prefs
+ .getBoolPref("mailnews.disable_fallback_to_utf8." + originalCharset, false);
+ if (disableFallback)
+ msgCompFields.needToCheckCharset = false;
+ else
+ fallbackCharset.value = "UTF-8";
+ }
+
+ if (fallbackCharset &&
+ fallbackCharset.value && fallbackCharset.value != "")
+ gMsgCompose.SetDocumentCharset(fallbackCharset.value);
+ }
+ try {
+ // Just before we try to send the message, fire off the
+ // compose-send-message event for listeners such as smime so they can do
+ // any pre-security work such as fetching certificates before sending.
+ var event = document.createEvent('UIEvents');
+ event.initEvent('compose-send-message', false, true);
+ var msgcomposeWindow = document.getElementById("msgcomposeWindow");
+ msgcomposeWindow.setAttribute("msgtype", msgType);
+ msgcomposeWindow.dispatchEvent(event);
+ if (event.defaultPrevented)
+ throw Cr.NS_ERROR_ABORT;
+
+ gAutoSaving = (msgType == Ci.nsIMsgCompDeliverMode.AutoSaveAsDraft);
+ if (!gAutoSaving) {
+ // Disable the ui if we're not auto-saving.
+ gWindowLocked = true;
+ disableEditableFields();
+ updateComposeItems();
+ } else {
+ // If we're auto saving, mark the body as not changed here, and not
+ // when the save is done, because the user might change it between now
+ // and when the save is done.
+ SetContentAndBodyAsUnmodified();
+ }
+
+ var progress = Cc["@mozilla.org/messenger/progress;1"]
+ .createInstance(Ci.nsIMsgProgress);
+ if (progress) {
+ progress.registerListener(progressListener);
+ gSendOrSaveOperationInProgress = true;
+ }
+ msgWindow.domWindow = window;
+ msgWindow.rootDocShell.allowAuth = true;
+ gMsgCompose.SendMsg(msgType, getCurrentIdentity(), getCurrentAccountKey(),
+ msgWindow, progress);
+ }
+ catch (ex) {
+ Cu.reportError("GenericSendMessage FAILED: " + ex);
+ gWindowLocked = false;
+ enableEditableFields();
+ updateComposeItems();
+ }
+ if (gMsgCompose && originalCharset != gMsgCompose.compFields.characterSet)
+ SetDocumentCharacterSet(gMsgCompose.compFields.characterSet);
+}
+
+/**
+ * Check if the given address is valid (contains a @).
+ *
+ * @param aAddress The address string to check.
+ */
+function isValidAddress(aAddress) {
+ return (aAddress.includes("@", 1) && !aAddress.endsWith("@"));
+}
+
+/**
+ * Keep the Send buttons disabled until any recipient is entered.
+ */
+function updateSendLock() {
+ gSendLocked = true;
+ if (!gMsgCompose)
+ return;
+
+ // Helper function to check for a valid list name.
+ function isValidListName(aInput) {
+ let listNames = MimeParser.parseHeaderField(aInput,
+ MimeParser.HEADER_ADDRESS);
+ return listNames.length > 0 &&
+ MailServices.ab.mailListNameExists(listNames[0].name);
+ }
+
+ const mailTypes = [ "addr_to", "addr_cc", "addr_bcc" ];
+
+ // Enable the send buttons if anything usable was entered into at least one
+ // recipient field.
+ for (let row = 1; row <= top.MAX_RECIPIENTS; row ++) {
+ let popupValue = awGetPopupElement(row).value;
+ let inputValue = awGetInputElement(row).value.trim();
+ // Check for a valid looking email address or a valid mailing list name
+ // from one of our addressbooks.
+ if ((mailTypes.includes(popupValue) &&
+ (isValidAddress(inputValue) || isValidListName(inputValue))) ||
+ ((popupValue == "addr_newsgroups") && (inputValue != ""))) {
+ gSendLocked = false;
+ break;
+ }
+ }
+}
+
+function CheckValidEmailAddress(aTo, aCC, aBCC)
+{
+ var invalidStr = null;
+ // crude check that the to, cc, and bcc fields contain at least one '@'.
+ // We could parse each address, but that might be overkill.
+ if (aTo.length > 0 && (aTo.indexOf("@") <= 0 && aTo.toLowerCase() != "postmaster" || aTo.indexOf("@") == aTo.length - 1))
+ invalidStr = aTo;
+ else if (aCC.length > 0 && (aCC.indexOf("@") <= 0 && aCC.toLowerCase() != "postmaster" || aCC.indexOf("@") == aCC.length - 1))
+ invalidStr = aCC;
+ else if (aBCC.length > 0 && (aBCC.indexOf("@") <= 0 && aBCC.toLowerCase() != "postmaster" || aBCC.indexOf("@") == aBCC.length - 1))
+ invalidStr = aBCC;
+ if (invalidStr)
+ {
+ var errorTitle = sComposeMsgsBundle.getString("addressInvalidTitle");
+ var errorMsg = sComposeMsgsBundle.getFormattedString("addressInvalid", [invalidStr], 1);
+ Services.prompt.alert(window, errorTitle, errorMsg);
+ return false;
+ }
+ return true;
+}
+
+function SendMessage()
+{
+ let sendInBackground = Services.prefs.getBoolPref("mailnews.sendInBackground");
+ if (sendInBackground && AppConstants.platform != "macosx") {
+ let enumerator = Services.wm.getEnumerator(null);
+ let count = 0;
+ while (enumerator.hasMoreElements() && count < 2)
+ {
+ enumerator.getNext();
+ count++;
+ }
+ if (count == 1)
+ sendInBackground = false;
+ }
+ GenericSendMessage(sendInBackground ? nsIMsgCompDeliverMode.Background
+ : nsIMsgCompDeliverMode.Now);
+}
+
+function SendMessageWithCheck()
+{
+ var warn = Services.prefs.getBoolPref("mail.warn_on_send_accel_key");
+
+ if (warn) {
+ var checkValue = {value:false};
+ var buttonPressed = Services.prompt.confirmEx(window,
+ sComposeMsgsBundle.getString('sendMessageCheckWindowTitle'),
+ sComposeMsgsBundle.getString('sendMessageCheckLabel'),
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
+ sComposeMsgsBundle.getString('sendMessageCheckSendButtonLabel'),
+ null, null,
+ sComposeMsgsBundle.getString('CheckMsg'),
+ checkValue);
+ if (buttonPressed != 0) {
+ return;
+ }
+ if (checkValue.value) {
+ Services.prefs.setBoolPref("mail.warn_on_send_accel_key", false);
+ }
+ }
+
+ if (Services.io.offline)
+ SendMessageLater();
+ else
+ SendMessage();
+}
+
+function SendMessageLater()
+{
+ GenericSendMessage(nsIMsgCompDeliverMode.Later);
+}
+
+function Save()
+{
+ switch (defaultSaveOperation)
+ {
+ case "file" : SaveAsFile(false); break;
+ case "template" : SaveAsTemplate(false); break;
+ default : SaveAsDraft(false); break;
+ }
+}
+
+function SaveAsFile(saveAs)
+{
+ var subject = GetMsgSubjectElement().value;
+ GetCurrentEditorElement().contentDocument.title = subject;
+
+ if (gMsgCompose.bodyConvertible() == nsIMsgCompConvertible.Plain)
+ SaveDocument(saveAs, false, "text/plain");
+ else
+ SaveDocument(saveAs, false, "text/html");
+ defaultSaveOperation = "file";
+}
+
+function SaveAsDraft()
+{
+ GenericSendMessage(nsIMsgCompDeliverMode.SaveAsDraft);
+ defaultSaveOperation = "draft";
+
+ gAutoSaveKickedIn = false;
+ gEditingDraft = true;
+}
+
+function SaveAsTemplate()
+{
+ let savedReferences = null;
+ if (gMsgCompose && gMsgCompose.compFields) {
+ // Clear References header. When we use the template, we don't want that
+ // header, yet, "edit as new message" maintains it. So we need to clear
+ // it when saving the template.
+ // Note: The In-Reply-To header is the last entry in the references header,
+ // so it will get cleared as well.
+ savedReferences = gMsgCompose.compFields.references;
+ gMsgCompose.compFields.references = null;
+ }
+
+ GenericSendMessage(nsIMsgCompDeliverMode.SaveAsTemplate);
+ defaultSaveOperation = "template";
+
+ if (savedReferences)
+ gMsgCompose.compFields.references = savedReferences;
+
+ gAutoSaveKickedIn = false;
+ gEditingDraft = false;
+}
+
+// Sets the additional FCC, in addition to the default FCC.
+function MessageFcc(aFolder) {
+ if (!gMsgCompose)
+ return;
+
+ var msgCompFields = gMsgCompose.compFields;
+ if (!msgCompFields)
+ return;
+
+ // Get the uri for the folder to FCC into.
+ var fccURI = aFolder.URI;
+ msgCompFields.fcc2 = (msgCompFields.fcc2 == fccURI) ? "nocopy://" : fccURI;
+}
+
+function updatePriorityMenu(priorityMenu)
+{
+ var priority = (gMsgCompose && gMsgCompose.compFields && gMsgCompose.compFields.priority) || "Normal";
+ priorityMenu.getElementsByAttribute("value", priority)[0].setAttribute("checked", "true");
+}
+
+function PriorityMenuSelect(target)
+{
+ if (gMsgCompose)
+ {
+ var msgCompFields = gMsgCompose.compFields;
+ if (msgCompFields)
+ msgCompFields.priority = target.getAttribute("value");
+ }
+}
+
+function OutputFormatMenuSelect(target)
+{
+ if (gMsgCompose)
+ {
+ var msgCompFields = gMsgCompose.compFields;
+ var toolbar = document.getElementById("FormatToolbar");
+ var format_menubar = document.getElementById("formatMenu");
+ var insert_menubar = document.getElementById("insertMenu");
+
+ if (msgCompFields)
+ switch (target.getAttribute('id'))
+ {
+ case "format_auto": gSendFormat = nsIMsgCompSendFormat.AskUser; break;
+ case "format_plain": gSendFormat = nsIMsgCompSendFormat.PlainText; break;
+ case "format_html": gSendFormat = nsIMsgCompSendFormat.HTML; break;
+ case "format_both": gSendFormat = nsIMsgCompSendFormat.Both; break;
+ }
+ gHideMenus = (gSendFormat == nsIMsgCompSendFormat.PlainText);
+ format_menubar.hidden = gHideMenus;
+ insert_menubar.hidden = gHideMenus;
+ if (gHideMenus) {
+ gFormatToolbarHidden = toolbar.hidden;
+ toolbar.hidden = true;
+ toolbar.setAttribute("hideinmenu", "true");
+ } else {
+ toolbar.hidden = gFormatToolbarHidden;
+ toolbar.removeAttribute("hideinmenu");
+ }
+ }
+}
+
+function SelectAddress()
+{
+ var msgCompFields = gMsgCompose.compFields;
+
+ Recipients2CompFields(msgCompFields);
+
+ var toAddress = msgCompFields.to;
+ var ccAddress = msgCompFields.cc;
+ var bccAddress = msgCompFields.bcc;
+
+ dump("toAddress: " + toAddress + "\n");
+ window.openDialog("chrome://messenger/content/addressbook/abSelectAddressesDialog.xul",
+ "",
+ "chrome,resizable,titlebar,modal",
+ {composeWindow:top.window,
+ msgCompFields:msgCompFields,
+ toAddress:toAddress,
+ ccAddress:ccAddress,
+ bccAddress:bccAddress});
+ // We have to set focus to the addressingwidget because we seem to loose focus often
+ // after opening the SelectAddresses Dialog- bug # 89950
+ AdjustFocus();
+}
+
+// walk through the recipients list and add them to the inline spell checker ignore list
+function addRecipientsToIgnoreList(aAddressesToAdd)
+{
+ if (InlineSpellCheckerUI.enabled)
+ {
+ // break the list of potentially many recipients back into individual names
+ var emailAddresses = {};
+ var names = {};
+ var fullNames = {};
+ var numAddresses =
+ MailServices.headerParser.parseHeadersWithArray(aAddressesToAdd,
+ emailAddresses, names,
+ fullNames);
+ var tokenizedNames = [];
+
+ // each name could consist of multiple words delimited by commas and/or spaces.
+ // i.e. Green Lantern or Lantern,Green.
+ for (let i = 0; i < names.value.length; i++)
+ {
+ if (!names.value[i])
+ continue;
+ var splitNames = names.value[i].match(/[^\s,]+/g);
+ if (splitNames)
+ tokenizedNames = tokenizedNames.concat(splitNames);
+ }
+
+ if (InlineSpellCheckerUI.mInlineSpellChecker.spellCheckPending)
+ {
+ // spellchecker is enabled, but we must wait for its init to complete
+ Services.obs.addObserver(function observe(subject, topic, data) {
+ if (subject == gMsgCompose.editor)
+ {
+ Services.obs.removeObserver(observe, topic);
+ InlineSpellCheckerUI.mInlineSpellChecker.ignoreWords(tokenizedNames);
+ }
+ }, "inlineSpellChecker-spellCheck-ended");
+ }
+ else
+ {
+ InlineSpellCheckerUI.mInlineSpellChecker.ignoreWords(tokenizedNames);
+ }
+ }
+}
+
+function onAddressColCommand(aWidgetId) {
+ gContentChanged = true;
+ awSetAutoComplete(aWidgetId.slice(aWidgetId.lastIndexOf('#') + 1));
+ updateSendCommands(true);
+}
+
+/**
+ * Called if the list of recipients changed in any way.
+ *
+ * @param aAutomatic Set to true if the change of recipients was invoked
+ * programatically and should not be considered a change
+ * of message content.
+ */
+function onRecipientsChanged(aAutomatic) {
+ if (!aAutomatic) {
+ gContentChanged = true;
+ setupAutocomplete();
+ }
+ updateSendCommands(true);
+}
+
+function InitLanguageMenu()
+{
+ var languageMenuList = document.getElementById("languageMenuList");
+ if (!languageMenuList)
+ return;
+
+ var spellChecker = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(mozISpellCheckingEngine);
+ // Get the list of dictionaries from the spellchecker.
+ var dictList = spellChecker.getDictionaryList();
+ var count = dictList.length;
+
+ // If dictionary count hasn't changed then no need to update the menu.
+ if (sDictCount == count)
+ return;
+
+ // Store current dictionary count.
+ sDictCount = count;
+
+ // Load the language string bundle that will help us map
+ // RFC 1766 strings to UI strings.
+ var languageBundle = document.getElementById("languageBundle");
+ var isoStrArray;
+ var langId;
+ var langLabel;
+
+ for (let i = 0; i < count; i++)
+ {
+ try
+ {
+ langId = dictList[i];
+ isoStrArray = dictList[i].split(/[-_]/);
+
+ if (languageBundle && isoStrArray[0])
+ langLabel = languageBundle.getString(isoStrArray[0].toLowerCase());
+
+ // the user needs to be able to distinguish between the UK English dictionary
+ // and say the United States English Dictionary. If we have a isoStr value then
+ // wrap it in parentheses and append it to the menu item string. i.e.
+ // English (US) and English (UK)
+ if (!langLabel)
+ langLabel = langId;
+ // if we have a language ID like US or UK, append it to the menu item, and any sub-variety
+ else if (isoStrArray.length > 1 && isoStrArray[1]) {
+ langLabel += ' (' + isoStrArray[1];
+ if (isoStrArray.length > 2 && isoStrArray[2])
+ langLabel += '-' + isoStrArray[2];
+ langLabel += ')';
+ }
+ }
+ catch (ex)
+ {
+ // getString throws an exception when a key is not found in the
+ // bundle. In that case, just use the original dictList string.
+ langLabel = langId;
+ }
+ dictList[i] = [langLabel, langId];
+ }
+
+ // sort by locale-aware collation
+ dictList.sort(
+ function compareFn(a, b)
+ {
+ return a[0].localeCompare(b[0]);
+ }
+ );
+
+ // Remove any languages from the list.
+ while (languageMenuList.hasChildNodes())
+ languageMenuList.lastChild.remove();
+
+ for (let i = 0; i < count; i++)
+ {
+ var item = document.createElement("menuitem");
+ item.setAttribute("label", dictList[i][0]);
+ item.setAttribute("value", dictList[i][1]);
+ item.setAttribute("type", "radio");
+ languageMenuList.appendChild(item);
+ }
+}
+
+function OnShowDictionaryMenu(aTarget)
+{
+ InitLanguageMenu();
+ var spellChecker = InlineSpellCheckerUI.mInlineSpellChecker.spellChecker;
+ var curLang = spellChecker.GetCurrentDictionary();
+ var languages = aTarget.getElementsByAttribute("value", curLang);
+ if (languages.length > 0)
+ languages[0].setAttribute("checked", true);
+}
+
+function ChangeLanguage(event)
+{
+ // We need to change the dictionary language and if we are using inline spell check,
+ // recheck the message
+ var spellChecker = InlineSpellCheckerUI.mInlineSpellChecker.spellChecker;
+ if (spellChecker.GetCurrentDictionary() != event.target.value)
+ {
+ spellChecker.SetCurrentDictionary(event.target.value);
+
+ ComposeChangeLanguage(event.target.value)
+ }
+ event.stopPropagation();
+}
+
+function ComposeChangeLanguage(aLang)
+{
+ if (document.documentElement.getAttribute("lang") != aLang) {
+
+ // Update the document language as well.
+ // This is needed to synchronize the subject.
+ document.documentElement.setAttribute("lang", aLang);
+
+ // Update spellchecker pref
+ Services.prefs.setCharPref("spellchecker.dictionary", aLang);
+
+ // Now check the document and the subject over again with the new
+ // dictionary.
+ if (InlineSpellCheckerUI.enabled) {
+ InlineSpellCheckerUI.mInlineSpellChecker.spellCheckRange(null);
+
+ // Also force a recheck of the subject. The spell checker for the subject
+ // isn't always ready yet. Usually throws unless the subject was selected
+ // at least once. So don't auto-create it, hence pass 'false'.
+ let inlineSpellChecker =
+ GetMsgSubjectElement().editor.getInlineSpellChecker(false);
+ if (inlineSpellChecker) {
+ inlineSpellChecker.spellCheckRange(null);
+ }
+ }
+ }
+}
+
+function ToggleReturnReceipt(target)
+{
+ var msgCompFields = gMsgCompose.compFields;
+ if (msgCompFields)
+ {
+ msgCompFields.returnReceipt = ! msgCompFields.returnReceipt;
+ target.setAttribute('checked', msgCompFields.returnReceipt);
+ gReceiptOptionChanged = true;
+ }
+}
+
+function ToggleDSN(target)
+{
+ var msgCompFields = gMsgCompose.compFields;
+
+ if (msgCompFields)
+ {
+ msgCompFields.DSN = !msgCompFields.DSN;
+ target.setAttribute('checked', msgCompFields.DSN);
+ gDSNOptionChanged = true;
+ }
+}
+
+function ToggleAttachVCard(target)
+{
+ var msgCompFields = gMsgCompose.compFields;
+ if (msgCompFields)
+ {
+ msgCompFields.attachVCard = ! msgCompFields.attachVCard;
+ target.setAttribute('checked', msgCompFields.attachVCard);
+ gAttachVCardOptionChanged = true;
+ }
+}
+
+function FillIdentityList(menulist)
+{
+ var accounts = FolderUtils.allAccountsSorted(true);
+
+ for (let acc = 0; acc < accounts.length; acc++)
+ {
+ let account = accounts[acc];
+ let identities = account.identities;
+
+ if (identities.length == 0)
+ continue;
+
+ for (let i = 0; i < identities.length; i++)
+ {
+ let identity = identities[i];
+ let item = menulist.appendItem(identity.identityName,
+ identity.fullAddress,
+ account.incomingServer.prettyName);
+ item.setAttribute("identitykey", identity.key);
+ item.setAttribute("accountkey", account.key);
+ if (i == 0)
+ {
+ // Mark the first identity as default.
+ item.setAttribute("default", "true");
+ }
+ }
+ }
+}
+
+function getCurrentAccountKey()
+{
+ // get the accounts key
+ var identityList = GetMsgIdentityElement();
+ return identityList.selectedItem.getAttribute("accountkey");
+}
+
+function getCurrentIdentityKey()
+{
+ // get the identity key
+ var identityList = GetMsgIdentityElement();
+ return identityList.selectedItem.getAttribute("identitykey");
+}
+
+function getIdentityForKey(key)
+{
+ return MailServices.accounts.getIdentity(key);
+}
+
+function getCurrentIdentity()
+{
+ return getIdentityForKey(getCurrentIdentityKey());
+}
+
+function AdjustFocus()
+{
+ let element = awGetInputElement(awGetNumberOfRecipients());
+ if (element.value == "") {
+ awSetFocusTo(element);
+ }
+ else
+ {
+ element = GetMsgSubjectElement();
+ if (element.value == "") {
+ element.focus();
+ }
+ else {
+ SetMsgBodyFrameFocus();
+ }
+ }
+}
+
+function SetComposeWindowTitle()
+{
+ var newTitle = GetMsgSubjectElement().value;
+
+ if (newTitle == "" )
+ newTitle = sComposeMsgsBundle.getString("defaultSubject");
+
+ newTitle += GetCharsetUIString();
+ document.title = sComposeMsgsBundle.getString("windowTitlePrefix") + " " + newTitle;
+}
+
+// Check for changes to document and allow saving before closing
+// This is hooked up to the OS's window close widget (e.g., "X" for Windows)
+function ComposeCanClose()
+{
+ if (gSendOrSaveOperationInProgress)
+ {
+ var brandShortName = sBrandBundle.getString("brandShortName");
+
+ var promptTitle = sComposeMsgsBundle.getString("quitComposeWindowTitle");
+ var promptMsg = sComposeMsgsBundle.getFormattedString("quitComposeWindowMessage2",
+ [brandShortName], 1);
+ var quitButtonLabel = sComposeMsgsBundle.getString("quitComposeWindowQuitButtonLabel2");
+ var waitButtonLabel = sComposeMsgsBundle.getString("quitComposeWindowWaitButtonLabel2");
+
+ if (Services.prompt.confirmEx(window, promptTitle, promptMsg,
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1),
+ waitButtonLabel, quitButtonLabel, null, null, {value:0}) == 1)
+ {
+ gMsgCompose.abort();
+ return true;
+ }
+ return false;
+ }
+
+ // Returns FALSE only if user cancels save action
+ if (gContentChanged || gMsgCompose.bodyModified || (gAutoSaveKickedIn && !gEditingDraft))
+ {
+ // call window.focus, since we need to pop up a dialog
+ // and therefore need to be visible (to prevent user confusion)
+ window.focus();
+ let draftFolderURI = gCurrentIdentity.draftFolder;
+ let draftFolderName = MailUtils.getFolderForURI(draftFolderURI).prettyName;
+ switch (Services.prompt.confirmEx(window,
+ sComposeMsgsBundle.getString("saveDlogTitle"),
+ sComposeMsgsBundle.getFormattedString("saveDlogMessages3", [draftFolderName]),
+ (Services.prompt.BUTTON_TITLE_SAVE * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2),
+ null,
+ null,
+ sComposeMsgsBundle.getString("discardButtonLabel"),
+ null, {value:0}))
+ {
+ case 0: //Save
+ // we can close immediately if we already autosaved the draft
+ if (!gContentChanged && !gMsgCompose.bodyModified)
+ break;
+ gCloseWindowAfterSave = true;
+ GenericSendMessage(nsIMsgCompDeliverMode.AutoSaveAsDraft);
+ return false;
+ case 1: //Cancel
+ return false;
+ case 2: //Don't Save
+ // only delete the draft if we didn't start off editing a draft
+ if (!gEditingDraft && gAutoSaveKickedIn)
+ RemoveDraft();
+ break;
+ }
+ }
+
+ return true;
+}
+
+function RemoveDraft()
+{
+ try
+ {
+ var draftId = gMsgCompose.compFields.draftId;
+ var msgKey = draftId.substr(draftId.indexOf('#') + 1);
+ var folder = sRDF.GetResource(gMsgCompose.savedFolderURI);
+ try {
+ if (folder instanceof Ci.nsIMsgFolder)
+ {
+ let msg = folder.GetMessageHeader(msgKey);
+ folder.deleteMessages([msg], null, true, false, null, false);
+ }
+ }
+ catch (ex) // couldn't find header - perhaps an imap folder.
+ {
+ if (folder instanceof Ci.nsIMsgImapMailFolder)
+ {
+ const kImapMsgDeletedFlag = 0x0008;
+ folder.storeImapFlags(kImapMsgDeletedFlag, true, [msgKey], null);
+ }
+ }
+ } catch (ex) {}
+}
+
+function SetContentAndBodyAsUnmodified()
+{
+ gMsgCompose.bodyModified = false;
+ gContentChanged = false;
+}
+
+function MsgComposeCloseWindow()
+{
+ if (gMsgCompose)
+ gMsgCompose.CloseWindow();
+ else
+ window.close();
+}
+
+// attachedLocalFile must be a nsIFile
+function SetLastAttachDirectory(attachedLocalFile)
+{
+ try {
+ var file = attachedLocalFile.QueryInterface(Ci.nsIFile);
+ var parent = file.parent.QueryInterface(Ci.nsIFile);
+
+ Services.prefs.setComplexValue(kComposeAttachDirPrefName,
+ Ci.nsIFile, parent);
+ }
+ catch (ex) {
+ dump("error: SetLastAttachDirectory failed: " + ex + "\n");
+ }
+}
+
+function AttachFile()
+{
+ //Get file using nsIFilePicker and convert to URL
+ const nsIFilePicker = Ci.nsIFilePicker;
+ let fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(nsIFilePicker);
+ fp.init(window, sComposeMsgsBundle.getString("chooseFileToAttach"),
+ nsIFilePicker.modeOpenMultiple);
+ let lastDirectory = GetLocalFilePref(kComposeAttachDirPrefName);
+ if (lastDirectory)
+ fp.displayDirectory = lastDirectory;
+
+ fp.appendFilters(nsIFilePicker.filterAll);
+ fp.open(rv => {
+ if (rv != nsIFilePicker.returnOK || !fp.files) {
+ return;
+ }
+ try {
+ let firstAttachedFile = AttachFiles(fp.files);
+ if (firstAttachedFile) {
+ SetLastAttachDirectory(firstAttachedFile);
+ }
+ }
+ catch (ex) {
+ dump("failed to get attachments: " + ex + "\n");
+ }
+ });
+}
+
+function AttachFiles(attachments)
+{
+ if (!attachments || !attachments.hasMoreElements())
+ return null;
+
+ var firstAttachedFile = null;
+
+ while (attachments.hasMoreElements()) {
+ var currentFile = attachments.getNext().QueryInterface(Ci.nsIFile);
+
+ if (!firstAttachedFile) {
+ firstAttachedFile = currentFile;
+ }
+
+ var fileHandler = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
+ var currentAttachment = fileHandler.getURLSpecFromFile(currentFile);
+
+ if (!DuplicateFileCheck(currentAttachment)) {
+ var attachment = Cc["@mozilla.org/messengercompose/attachment;1"].createInstance(Ci.nsIMsgAttachment);
+ attachment.url = currentAttachment;
+ attachment.size = currentFile.fileSize;
+ AddAttachment(attachment);
+ gContentChanged = true;
+ }
+ }
+ return firstAttachedFile;
+}
+
+function AddAttachment(attachment)
+{
+ if (attachment && attachment.url)
+ {
+ var bucket = GetMsgAttachmentElement();
+ var item = document.createElement("listitem");
+
+ if (!attachment.name)
+ attachment.name = gMsgCompose.AttachmentPrettyName(attachment.url, attachment.urlCharset);
+
+ // for security reasons, don't allow *-message:// uris to leak out
+ // we don't want to reveal the .slt path (for mailbox://), or the username or hostname
+ var messagePrefix = /^mailbox-message:|^imap-message:|^news-message:/i;
+ if (messagePrefix.test(attachment.name))
+ attachment.name = sComposeMsgsBundle.getString("messageAttachmentSafeName");
+ else {
+ // for security reasons, don't allow mail protocol uris to leak out
+ // we don't want to reveal the .slt path (for mailbox://), or the username or hostname
+ var mailProtocol = /^file:|^mailbox:|^imap:|^s?news:/i;
+ if (mailProtocol.test(attachment.name))
+ attachment.name = sComposeMsgsBundle.getString("partAttachmentSafeName");
+ }
+
+ var nameAndSize = attachment.name;
+ if (attachment.size != -1)
+ nameAndSize += " (" + gMessenger.formatFileSize(attachment.size) + ")";
+ item.setAttribute("label", nameAndSize); //use for display only
+ item.attachment = attachment; //full attachment object stored here
+ try {
+ item.setAttribute("tooltiptext", decodeURI(attachment.url));
+ } catch(e) {
+ item.setAttribute("tooltiptext", attachment.url);
+ }
+ item.setAttribute("class", "listitem-iconic");
+ item.setAttribute("image", "moz-icon:" + attachment.url);
+ item.setAttribute("crop", "center");
+ bucket.appendChild(item);
+ }
+}
+
+function SelectAllAttachments()
+{
+ var bucketList = GetMsgAttachmentElement();
+ if (bucketList)
+ bucketList.selectAll();
+}
+
+function MessageHasAttachments()
+{
+ var bucketList = GetMsgAttachmentElement();
+ if (bucketList) {
+ return (bucketList && bucketList.hasChildNodes() && (bucketList == top.document.commandDispatcher.focusedElement));
+ }
+ return false;
+}
+
+function MessageGetNumSelectedAttachments()
+{
+ var bucketList = GetMsgAttachmentElement();
+ return (bucketList) ? bucketList.selectedItems.length : 0;
+}
+
+function AttachPage()
+{
+ var params = { action: "5", url: null };
+ window.openDialog("chrome://communicator/content/openLocation.xul",
+ "_blank", "chrome,close,titlebar,modal", params);
+ if (params.url)
+ {
+ var attachment =
+ Cc["@mozilla.org/messengercompose/attachment;1"]
+ .createInstance(Ci.nsIMsgAttachment);
+ attachment.url = params.url;
+ AddAttachment(attachment);
+ }
+}
+
+function DuplicateFileCheck(FileUrl)
+{
+ var bucket = GetMsgAttachmentElement();
+ for (let i = 0; i < bucket.childNodes.length; i++)
+ {
+ let attachment = bucket.childNodes[i].attachment;
+ if (attachment)
+ {
+ if (FileUrl == attachment.url)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function Attachments2CompFields(compFields)
+{
+ var bucket = GetMsgAttachmentElement();
+
+ //First, we need to clear all attachment in the compose fields
+ compFields.removeAttachments();
+
+ for (let i = 0; i < bucket.childNodes.length; i++)
+ {
+ let attachment = bucket.childNodes[i].attachment;
+ if (attachment)
+ compFields.addAttachment(attachment);
+ }
+}
+
+function RemoveAllAttachments()
+{
+ var child;
+ var bucket = GetMsgAttachmentElement();
+ while (bucket.hasChildNodes())
+ {
+ child = bucket.removeChild(bucket.lastChild);
+ // Let's release the attachment object hold by the node else it won't go away until the window is destroyed
+ child.attachment = null;
+ }
+}
+
+function RemoveSelectedAttachment()
+{
+ var child;
+ var bucket = GetMsgAttachmentElement();
+ if (bucket.selectedItems.length > 0) {
+ for (let i = bucket.selectedItems.length - 1; i >= 0; i--)
+ {
+ child = bucket.removeChild(bucket.selectedItems[i]);
+ // Let's release the attachment object hold by the node else it won't go away until the window is destroyed
+ child.attachment = null;
+ }
+ gContentChanged = true;
+ }
+}
+
+function RenameSelectedAttachment()
+{
+ var bucket = GetMsgAttachmentElement();
+ if (bucket.selectedItems.length != 1)
+ return; // not one attachment selected
+
+ var item = bucket.getSelectedItem(0);
+ var attachmentName = {value: item.attachment.name};
+ if (Services.prompt.prompt(
+ window,
+ sComposeMsgsBundle.getString("renameAttachmentTitle"),
+ sComposeMsgsBundle.getString("renameAttachmentMessage"),
+ attachmentName,
+ null,
+ {value: 0}))
+ {
+ var modifiedAttachmentName = attachmentName.value;
+ if (modifiedAttachmentName == "")
+ return; // name was not filled, bail out
+
+ var nameAndSize = modifiedAttachmentName;
+ if (item.attachment.size != -1)
+ nameAndSize += " (" + gMessenger.formatFileSize(item.attachment.size) + ")";
+ item.label = nameAndSize;
+ item.attachment.name = modifiedAttachmentName;
+ gContentChanged = true;
+ }
+}
+
+function FocusOnFirstAttachment()
+{
+ var bucketList = GetMsgAttachmentElement();
+
+ if (bucketList && bucketList.hasChildNodes())
+ bucketList.selectItem(bucketList.firstChild);
+}
+
+function AttachmentElementHasItems()
+{
+ var element = GetMsgAttachmentElement();
+ return element ? element.childNodes.length : 0;
+}
+
+function OpenSelectedAttachment()
+{
+ let bucket = document.getElementById("attachmentBucket");
+ if (bucket.selectedItems.length == 1) {
+ let attachmentUrl = bucket.getSelectedItem(0).attachment.url;
+
+ let messagePrefix = /^mailbox-message:|^imap-message:|^news-message:/i;
+ if (messagePrefix.test(attachmentUrl)) {
+ // We must be dealing with a forwarded attachment, treat this special.
+ let msgHdr = gMessenger.msgHdrFromURI(attachmentUrl);
+ if (msgHdr) {
+ MailUtils.openMessageInNewWindow(msgHdr);
+ }
+ } else {
+ // Turn the URL into a nsIURI object then open it.
+ let uri = Services.io.newURI(attachmentUrl);
+ if (uri) {
+ let channel = Services.io.newChannelFromURI(uri,
+ null,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null,
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ if (channel) {
+ let uriLoader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);
+ uriLoader.openURI(channel, true, new nsAttachmentOpener());
+ }
+ }
+ }
+ } // if one attachment selected
+}
+
+function nsAttachmentOpener()
+{
+}
+
+nsAttachmentOpener.prototype =
+{
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIURIContentListener) ||
+ iid.equals(Ci.nsIInterfaceRequestor) ||
+ iid.equals(Ci.nsISupports)) {
+ return this;
+ }
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ doContent: function(contentType, isContentPreferred, request, contentHandler)
+ {
+ return false;
+ },
+
+ isPreferred: function(contentType, desiredContentType)
+ {
+ return false;
+ },
+
+ canHandleContent: function(contentType, isContentPreferred, desiredContentType)
+ {
+ return false;
+ },
+
+ getInterface: function(iid)
+ {
+ if (iid.equals(Ci.nsIDOMWindow)) {
+ return window;
+ }
+
+ if (iid.equals(Ci.nsIDocShell)) {
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ }
+
+ return this.QueryInterface(iid);
+ },
+
+ loadCookie: null,
+ parentContentListener: null
+}
+
+function DetermineHTMLAction(convertible)
+{
+ try {
+ gMsgCompose.expandMailingLists();
+ } catch(ex) {
+ dump("gMsgCompose.expandMailingLists failed: " + ex + "\n");
+ }
+
+ if (!gMsgCompose.composeHTML)
+ {
+ return nsIMsgCompSendFormat.PlainText;
+ }
+
+ if (gSendFormat == nsIMsgCompSendFormat.AskUser)
+ {
+ return gMsgCompose.determineHTMLAction(convertible);
+ }
+
+ return gSendFormat;
+}
+
+function DetermineConvertibility()
+{
+ if (!gMsgCompose.composeHTML)
+ return nsIMsgCompConvertible.Plain;
+
+ try {
+ return gMsgCompose.bodyConvertible();
+ } catch(ex) {}
+ return nsIMsgCompConvertible.No;
+}
+
+function LoadIdentity(startup)
+{
+ var identityElement = GetMsgIdentityElement();
+ var prevIdentity = gCurrentIdentity;
+
+ if (identityElement) {
+ identityElement.value = identityElement.selectedItem.value;
+
+ var idKey = identityElement.selectedItem.getAttribute("identitykey");
+ gCurrentIdentity = MailServices.accounts.getIdentity(idKey);
+
+ let accountKey = null;
+ if (identityElement.selectedItem)
+ accountKey = identityElement.selectedItem.getAttribute("accountkey");
+
+ let maxRecipients = awGetMaxRecipients();
+ for (let i = 1; i <= maxRecipients; i++)
+ {
+ let params = JSON.parse(awGetInputElement(i).searchParam);
+ params.idKey = idKey;
+ params.accountKey = accountKey;
+ awGetInputElement(i).searchParam = JSON.stringify(params);
+ }
+
+ if (!startup && prevIdentity && idKey != prevIdentity.key)
+ {
+ var prevReplyTo = prevIdentity.replyTo;
+ var prevCc = "";
+ var prevBcc = "";
+ var prevReceipt = prevIdentity.requestReturnReceipt;
+ var prevDSN = prevIdentity.requestDSN;
+ var prevAttachVCard = prevIdentity.attachVCard;
+
+ if (prevIdentity.doCc)
+ prevCc += prevIdentity.doCcList;
+
+ if (prevIdentity.doBcc)
+ prevBcc += prevIdentity.doBccList;
+
+ var newReplyTo = gCurrentIdentity.replyTo;
+ var newCc = "";
+ var newBcc = "";
+ var newReceipt = gCurrentIdentity.requestReturnReceipt;
+ var newDSN = gCurrentIdentity.requestDSN;
+ var newAttachVCard = gCurrentIdentity.attachVCard;
+
+ if (gCurrentIdentity.doCc)
+ newCc += gCurrentIdentity.doCcList;
+
+ if (gCurrentIdentity.doBcc)
+ newBcc += gCurrentIdentity.doBccList;
+
+ var needToCleanUp = false;
+ var msgCompFields = gMsgCompose.compFields;
+
+ if (!gReceiptOptionChanged &&
+ prevReceipt == msgCompFields.returnReceipt &&
+ prevReceipt != newReceipt)
+ {
+ msgCompFields.returnReceipt = newReceipt;
+ document.getElementById("returnReceiptMenu").setAttribute('checked',msgCompFields.returnReceipt);
+ }
+
+ if (!gDSNOptionChanged &&
+ prevDSN == msgCompFields.DSN &&
+ prevDSN != newDSN)
+ {
+ msgCompFields.DSN = newDSN;
+ document.getElementById("dsnMenu").setAttribute('checked',msgCompFields.DSN);
+ }
+
+ if (!gAttachVCardOptionChanged &&
+ prevAttachVCard == msgCompFields.attachVCard &&
+ prevAttachVCard != newAttachVCard)
+ {
+ msgCompFields.attachVCard = newAttachVCard;
+ document.getElementById("cmd_attachVCard").setAttribute('checked',msgCompFields.attachVCard);
+ }
+
+ if (newReplyTo != prevReplyTo)
+ {
+ needToCleanUp = true;
+ if (prevReplyTo != "")
+ awRemoveRecipients(msgCompFields, "addr_reply", prevReplyTo);
+ if (newReplyTo != "")
+ awAddRecipients(msgCompFields, "addr_reply", newReplyTo);
+ }
+
+ let toAddrs = new Set(msgCompFields.splitRecipients(msgCompFields.to, true));
+ let ccAddrs = new Set(msgCompFields.splitRecipients(msgCompFields.cc, true));
+
+ if (newCc != prevCc)
+ {
+ needToCleanUp = true;
+ if (prevCc)
+ awRemoveRecipients(msgCompFields, "addr_cc", prevCc);
+ if (newCc) {
+ // Ensure none of the Ccs are already in To.
+ let cc2 = msgCompFields.splitRecipients(newCc, true);
+ newCc = cc2.filter(x => !toAddrs.has(x)).join(", ");
+ awAddRecipients(msgCompFields, "addr_cc", newCc);
+ }
+ }
+
+ if (newBcc != prevBcc)
+ {
+ needToCleanUp = true;
+ if (prevBcc)
+ awRemoveRecipients(msgCompFields, "addr_bcc", prevBcc);
+ if (newBcc) {
+ // Ensure none of the Bccs are already in To or Cc.
+ let bcc2 = msgCompFields.splitRecipients(newBcc, true);
+ let toCcAddrs = new Set([...toAddrs, ...ccAddrs]);
+ newBcc = bcc2.filter(x => !toCcAddrs.has(x)).join(", ");
+ awAddRecipients(msgCompFields, "addr_bcc", newBcc);
+ }
+ }
+
+ if (needToCleanUp)
+ awCleanupRows();
+
+ try {
+ gMsgCompose.identity = gCurrentIdentity;
+ } catch (ex) { dump("### Cannot change the identity: " + ex + "\n");}
+
+ var event = document.createEvent('Events');
+ event.initEvent('compose-from-changed', false, true);
+ document.getElementById("msgcomposeWindow").dispatchEvent(event);
+
+ gComposeNotificationBar.clearIdentityWarning();
+ }
+
+ if (!startup) {
+ if (Services.prefs.getBoolPref("mail.autoComplete.highlightNonMatches"))
+ document.getElementById('addressCol2#1').highlightNonMatches = true;
+
+ // Only do this if we aren't starting up...
+ // It gets done as part of startup already.
+ addRecipientsToIgnoreList(gCurrentIdentity.fullAddress);
+ }
+ }
+}
+
+function setupAutocomplete()
+{
+ var autoCompleteWidget = document.getElementById("addressCol2#1");
+
+ // if the pref is set to turn on the comment column, honor it here.
+ // this element then gets cloned for subsequent rows, so they should
+ // honor it as well
+ //
+ if (Services.prefs.getBoolPref("mail.autoComplete.highlightNonMatches"))
+ autoCompleteWidget.highlightNonMatches = true;
+
+ if (Services.prefs.getIntPref("mail.autoComplete.commentColumn", 0) != 0)
+ autoCompleteWidget.showCommentColumn = true;
+}
+
+function subjectKeyPress(event)
+{
+ switch(event.keyCode) {
+ case KeyEvent.DOM_VK_TAB:
+ if (!event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) {
+ SetMsgBodyFrameFocus();
+ event.preventDefault();
+ }
+ break;
+ case KeyEvent.DOM_VK_RETURN:
+ SetMsgBodyFrameFocus();
+ break;
+ }
+}
+
+function AttachmentBucketClicked(event)
+{
+ if (event.button != 0)
+ return;
+
+ if (event.originalTarget.localName == "listboxbody")
+ goDoCommand('cmd_attachFile');
+ else if (event.originalTarget.localName == "listitem" && event.detail == 2)
+ OpenSelectedAttachment();
+}
+
+// Content types supported in the attachmentBucketObserver.
+let flavours = [ "text/x-moz-message", "application/x-moz-file",
+ "text/x-moz-url", ];
+
+var attachmentBucketObserver = {
+ onDrop(aEvent) {
+ let dt = aEvent.dataTransfer;
+ let dataList = [];
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ let types = Array.from(dt.mozTypesAt(i));
+ for (let flavour of flavours) {
+ if (types.includes(flavour)) {
+ let data = dt.mozGetDataAt(flavour, i);
+ if (data) {
+ dataList.push({ data, flavour });
+ }
+ break;
+ }
+ }
+ }
+
+ for (let { data, flavour } of dataList) {
+ let isValidAttachment = false;
+ let prettyName;
+ let size;
+
+ // We could be dropping an attachment of various flavours;
+ // check and do the right thing.
+ switch (flavour) {
+ case "application/x-moz-file": {
+ if (data instanceof Ci.nsIFile) {
+ size = data.fileSize;
+ }
+
+ try {
+ data = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler)
+ .getURLSpecFromFile(data);
+ isValidAttachment = true;
+ } catch (e) {
+ Cu.reportError("Couldn't process the dragged file " +
+ data.leafName + ":" + e);
+ }
+ break;
+ }
+
+ case "text/x-moz-message": {
+ isValidAttachment = true;
+ let msgHdr = gMessenger.messageServiceFromURI(data)
+ .messageURIToMsgHdr(data);
+ prettyName = msgHdr.mime2DecodedSubject + ".eml";
+ size = msgHdr.messageSize;
+ break;
+ }
+
+ case "text/x-moz-url": {
+ let pieces = data.split("\n");
+ data = pieces[0];
+ if (pieces.length > 1) {
+ prettyName = pieces[1];
+ }
+ if (pieces.length > 2) {
+ size = parseInt(pieces[2]);
+ }
+
+ // If this is a URL (or selected text), check if it's a valid URL
+ // by checking if we can extract a scheme using Services.io.
+ // Don't attach invalid or mailto: URLs.
+ try {
+ let scheme = Services.io.extractScheme(data);
+ if (scheme != "mailto") {
+ isValidAttachment = true;
+ }
+ } catch (ex) {}
+ break;
+ }
+ }
+
+ if (isValidAttachment && !DuplicateFileCheck(data)) {
+ let attachment = Cc["@mozilla.org/messengercompose/attachment;1"]
+ .createInstance(Ci.nsIMsgAttachment);
+ attachment.url = data;
+ attachment.name = prettyName;
+
+ if (size !== undefined) {
+ attachment.size = size;
+ }
+
+ AddAttachment(attachment);
+ }
+ }
+
+ aEvent.stopPropagation();
+ },
+
+ onDragOver(aEvent) {
+ let dragSession = Cc["@mozilla.org/widget/dragservice;1"]
+ .getService(Ci.nsIDragService).getCurrentSession();
+ for (let flavour of flavours) {
+ if (dragSession.isDataFlavorSupported(flavour)) {
+ let attachmentBucket = GetMsgAttachmentElement();
+ attachmentBucket.setAttribute("dragover", "true");
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ break;
+ }
+ }
+ },
+
+ onDragExit(aEvent) {
+ let attachmentBucket = GetMsgAttachmentElement();
+ attachmentBucket.removeAttribute("dragover");
+ },
+};
+
+function DisplaySaveFolderDlg(folderURI)
+{
+ try
+ {
+ var showDialog = gCurrentIdentity.showSaveMsgDlg;
+ }
+ catch (e)
+ {
+ return;
+ }
+
+ if (showDialog){
+ let msgfolder = MailUtils.getFolderForURI(folderURI, true);
+ if (!msgfolder)
+ return;
+ var checkbox = {value:0};
+ var SaveDlgTitle = sComposeMsgsBundle.getString("SaveDialogTitle");
+ var dlgMsg = sComposeMsgsBundle.getFormattedString("SaveDialogMsg",
+ [msgfolder.name,
+ msgfolder.server.prettyName]);
+
+ var CheckMsg = sComposeMsgsBundle.getString("CheckMsg");
+ Services.prompt.alertCheck(window, SaveDlgTitle, dlgMsg, CheckMsg, checkbox);
+ try {
+ gCurrentIdentity.showSaveMsgDlg = !checkbox.value;
+ }//try
+ catch (e) {
+ return;
+ }//catch
+
+ }//if
+ return;
+}
+
+function SetMsgAddressingWidgetElementFocus()
+{
+ awSetFocusTo(awGetInputElement(awGetNumberOfRecipients()));
+}
+
+function SetMsgIdentityElementFocus()
+{
+ GetMsgIdentityElement().focus();
+}
+
+function SetMsgSubjectElementFocus()
+{
+ GetMsgSubjectElement().focus();
+}
+
+function SetMsgAttachmentElementFocus()
+{
+ GetMsgAttachmentElement().focus();
+ FocusOnFirstAttachment();
+}
+
+function SetMsgBodyFrameFocus()
+{
+ //window.content.focus(); fails to blur the currently focused element
+ document.commandDispatcher
+ .advanceFocusIntoSubtree(document.getElementById("appcontent"));
+}
+
+function GetMsgAddressingWidgetElement()
+{
+ if (!gMsgAddressingWidgetElement)
+ gMsgAddressingWidgetElement = document.getElementById("addressingWidget");
+
+ return gMsgAddressingWidgetElement;
+}
+
+function GetMsgIdentityElement()
+{
+ if (!gMsgIdentityElement)
+ gMsgIdentityElement = document.getElementById("msgIdentity");
+
+ return gMsgIdentityElement;
+}
+
+function GetMsgSubjectElement()
+{
+ if (!gMsgSubjectElement)
+ gMsgSubjectElement = document.getElementById("msgSubject");
+
+ return gMsgSubjectElement;
+}
+
+function GetMsgAttachmentElement()
+{
+ if (!gMsgAttachmentElement)
+ gMsgAttachmentElement = document.getElementById("attachmentBucket");
+
+ return gMsgAttachmentElement;
+}
+
+function GetMsgHeadersToolbarElement()
+{
+ if (!gMsgHeadersToolbarElement)
+ gMsgHeadersToolbarElement = document.getElementById("MsgHeadersToolbar");
+
+ return gMsgHeadersToolbarElement;
+}
+
+function IsMsgHeadersToolbarCollapsed()
+{
+ var element = GetMsgHeadersToolbarElement();
+ return element && element.collapsed;
+}
+
+function WhichElementHasFocus()
+{
+ var msgIdentityElement = GetMsgIdentityElement();
+ var msgAddressingWidgetElement = GetMsgAddressingWidgetElement();
+ var msgSubjectElement = GetMsgSubjectElement();
+ var msgAttachmentElement = GetMsgAttachmentElement();
+
+ if (top.document.commandDispatcher.focusedWindow == content)
+ return content;
+
+ var currentNode = top.document.commandDispatcher.focusedElement;
+ while (currentNode)
+ {
+ if (currentNode == msgIdentityElement ||
+ currentNode == msgAddressingWidgetElement ||
+ currentNode == msgSubjectElement ||
+ currentNode == msgAttachmentElement)
+ return currentNode;
+
+ currentNode = currentNode.parentNode;
+ }
+
+ return null;
+}
+
+// Function that performs the logic of switching focus from
+// one element to another in the mail compose window.
+// The default element to switch to when going in either
+// direction (shift or no shift key pressed), is the
+// AddressingWidgetElement.
+//
+// The only exception is when the MsgHeadersToolbar is
+// collapsed, then the focus will always be on the body of
+// the message.
+function SwitchElementFocus(event)
+{
+ var focusedElement = WhichElementHasFocus();
+
+ if (event && event.shiftKey)
+ {
+ if (IsMsgHeadersToolbarCollapsed())
+ SetMsgBodyFrameFocus();
+ else if (focusedElement == gMsgAddressingWidgetElement)
+ SetMsgIdentityElementFocus();
+ else if (focusedElement == gMsgIdentityElement)
+ SetMsgBodyFrameFocus();
+ else if (focusedElement == content)
+ {
+ // only set focus to the attachment element if there
+ // are any attachments.
+ if (AttachmentElementHasItems())
+ SetMsgAttachmentElementFocus();
+ else
+ SetMsgSubjectElementFocus();
+ }
+ else if (focusedElement == gMsgAttachmentElement)
+ SetMsgSubjectElementFocus();
+ else
+ SetMsgAddressingWidgetElementFocus();
+ }
+ else
+ {
+ if (IsMsgHeadersToolbarCollapsed())
+ SetMsgBodyFrameFocus();
+ else if (focusedElement == gMsgAddressingWidgetElement)
+ SetMsgSubjectElementFocus();
+ else if (focusedElement == gMsgSubjectElement)
+ {
+ // only set focus to the attachment element if there
+ // are any attachments.
+ if (AttachmentElementHasItems())
+ SetMsgAttachmentElementFocus();
+ else
+ SetMsgBodyFrameFocus();
+ }
+ else if (focusedElement == gMsgAttachmentElement)
+ SetMsgBodyFrameFocus();
+ else if (focusedElement == content)
+ SetMsgIdentityElementFocus();
+ else
+ SetMsgAddressingWidgetElementFocus();
+ }
+}
+
+function loadHTMLMsgPrefs()
+{
+ var fontFace = Services.prefs.getStringPref("msgcompose.font_face", "");
+ doStatefulCommand("cmd_fontFace", fontFace);
+
+ var fontSize = Services.prefs.getCharPref("msgcompose.font_size", "");
+ if (fontSize)
+ EditorSetFontSize(fontSize);
+
+ var bodyElement = GetBodyElement();
+
+ var textColor = Services.prefs.getCharPref("msgcompose.text_color", "");
+ if (!bodyElement.hasAttribute("text") && textColor)
+ {
+ bodyElement.setAttribute("text", textColor);
+ gDefaultTextColor = textColor;
+ document.getElementById("cmd_fontColor").setAttribute("state", textColor);
+ onFontColorChange();
+ }
+
+ var bgColor = Services.prefs.getCharPref("msgcompose.background_color", "");
+ if (!bodyElement.hasAttribute("bgcolor") && bgColor)
+ {
+ bodyElement.setAttribute("bgcolor", bgColor);
+ gDefaultBackgroundColor = bgColor;
+ document.getElementById("cmd_backgroundColor").setAttribute("state", bgColor);
+ onBackgroundColorChange();
+ }
+}
+
+function AutoSave()
+{
+ if (gMsgCompose.editor && (gContentChanged || gMsgCompose.bodyModified) &&
+ !gSendOrSaveOperationInProgress)
+ {
+ GenericSendMessage(nsIMsgCompDeliverMode.AutoSaveAsDraft);
+ gAutoSaveKickedIn = true;
+ }
+ gAutoSaveTimeout = setTimeout(AutoSave, gAutoSaveInterval);
+}
+
+/**
+ * Helper function to remove a query part from a URL, so for example:
+ * ...?remove=xx&other=yy becomes ...?other=yy.
+ *
+ * @param aURL the URL from which to remove the query part
+ * @param aQuery the query part to remove
+ * @return the URL with the query part removed
+ */
+function removeQueryPart(aURL, aQuery)
+{
+ // Quick pre-check.
+ if (!aURL.includes(aQuery))
+ return aURL;
+
+ let indexQM = aURL.indexOf("?");
+ if (indexQM < 0)
+ return aURL;
+
+ let queryParts = aURL.substr(indexQM + 1).split("&");
+ let indexPart = queryParts.indexOf(aQuery);
+ if (indexPart < 0)
+ return aURL;
+ queryParts.splice(indexPart, 1);
+ return aURL.substr(0, indexQM + 1) + queryParts.join("&");
+}
+
+function InitEditor(editor)
+{
+ // Set the eEditorMailMask flag to avoid using content prefs for the spell
+ // checker, otherwise the dictionary setting in preferences is ignored and
+ // the dictionary is inconsistent between the subject and message body.
+ var eEditorMailMask = Ci.nsIEditor.eEditorMailMask;
+ editor.flags |= eEditorMailMask;
+ GetMsgSubjectElement().editor.flags |= eEditorMailMask;
+
+ // Control insertion of line breaks.
+ editor.returnInParagraphCreatesNewParagraph =
+ Services.prefs.getBoolPref("mail.compose.default_to_paragraph") ||
+ Services.prefs.getBoolPref("editor.CR_creates_new_p");
+ editor.document.execCommand("defaultparagraphseparator", false,
+ gMsgCompose.composeHTML &&
+ Services.prefs.getBoolPref("mail.compose.default_to_paragraph") ?
+ "p" : "br");
+
+ gMsgCompose.initEditor(editor, window.content);
+ InlineSpellCheckerUI.init(editor);
+ EnableInlineSpellCheck(Services.prefs.getBoolPref("mail.spellcheck.inline"));
+ document.getElementById("menu_inlineSpellCheck").setAttribute("disabled", !InlineSpellCheckerUI.canSpellCheck);
+
+ // Listen for spellchecker changes, set the document language to the
+ // dictionary picked by the user via the right-click menu in the editor.
+ document.addEventListener("spellcheck-changed", updateDocumentLanguage);
+
+ // XXX: the error event fires twice for each load. Why??
+ editor.document.body.addEventListener("error", function(event) {
+ if (event.target.localName != "img") {
+ return;
+ }
+
+ if (event.target.getAttribute("moz-do-not-send") == "true") {
+ return;
+ }
+
+ let src = event.target.src;
+
+ if (!src) {
+ return;
+ }
+
+ if (!/^file:/i.test(src)) {
+ // Check if this is a protocol that can fetch parts.
+ let protocol = src.substr(0, src.indexOf(":")).toLowerCase();
+ if (!(Services.io.getProtocolHandler(protocol) instanceof
+ Ci.nsIMsgMessageFetchPartService)) {
+ // Can't fetch parts, don't try to load.
+ return;
+ }
+ }
+
+ if (event.target.classList.contains("loading-internal")) {
+ // We're already loading this, or tried so unsuccesfully.
+ return;
+ }
+
+ if (gOriginalMsgURI) {
+ let msgSvc = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger)
+ .messageServiceFromURI(gOriginalMsgURI);
+ let originalMsgNeckoURI = msgSvc.getUrlForUri(gOriginalMsgURI);
+
+ if (src.startsWith(removeQueryPart(originalMsgNeckoURI.spec,
+ "type=application/x-message-display"))) {
+ // Reply/Forward/Edit Draft/Edit as New can contain references to
+ // images in the original message. Load those and make them data: URLs
+ // now.
+ event.target.classList.add("loading-internal");
+ try {
+ loadBlockedImage(src);
+ } catch (e) {
+ // Couldn't load the referenced image.
+ Cu.reportError(e);
+ }
+ }
+ else {
+ // Appears to reference a random message. Notify and keep blocking.
+ gComposeNotificationBar.setBlockedContent(src);
+ }
+ }
+ else {
+ // For file:, and references to parts of random messages, show the
+ // blocked content notification.
+ gComposeNotificationBar.setBlockedContent(src);
+ }
+ }, true);
+
+ // Convert mailnews URL back to data: URL.
+ let background = editor.document.body.background;
+ if (background && gOriginalMsgURI) {
+ // Check that background has the same URL as the message itself.
+ let msgSvc = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger)
+ .messageServiceFromURI(gOriginalMsgURI);
+ let originalMsgNeckoURI = msgSvc.getUrlForUri(gOriginalMsgURI);
+
+ if (background.startsWith(
+ removeQueryPart(originalMsgNeckoURI.spec,
+ "type=application/x-message-display"))) {
+ try {
+ editor.document.body.background = loadBlockedImage(background, true);
+ } catch (e) {
+ // Couldn't load the referenced image.
+ Cu.reportError(e);
+ }
+ }
+ }
+}
+
+/**
+ * The event listener for the "spellcheck-changed" event updates
+ * the document language.
+ */
+function updateDocumentLanguage(event)
+{
+ document.documentElement.setAttribute("lang", event.detail.dictionary);
+}
+
+function EnableInlineSpellCheck(aEnableInlineSpellCheck)
+{
+ InlineSpellCheckerUI.enabled = aEnableInlineSpellCheck;
+ GetMsgSubjectElement().setAttribute("spellcheck", aEnableInlineSpellCheck);
+}
+
+function getMailToolbox()
+{
+ return document.getElementById("compose-toolbox");
+}
+
+function MailToolboxCustomizeInit()
+{
+ if (document.commandDispatcher.focusedWindow == content)
+ window.focus();
+ disableEditableFields();
+ GetMsgHeadersToolbarElement().setAttribute("moz-collapsed", true);
+ document.getElementById("compose-toolbar-sizer").setAttribute("moz-collapsed", true);
+ document.getElementById("content-frame").setAttribute("moz-collapsed", true);
+ toolboxCustomizeInit("mail-menubar");
+}
+
+function MailToolboxCustomizeDone(aToolboxChanged)
+{
+ toolboxCustomizeDone("mail-menubar", getMailToolbox(), aToolboxChanged);
+ GetMsgHeadersToolbarElement().removeAttribute("moz-collapsed");
+ document.getElementById("compose-toolbar-sizer").removeAttribute("moz-collapsed");
+ document.getElementById("content-frame").removeAttribute("moz-collapsed");
+ enableEditableFields();
+ SetMsgBodyFrameFocus();
+}
+
+function MailToolboxCustomizeChange(aEvent)
+{
+ toolboxCustomizeChange(getMailToolbox(), aEvent);
+}
+
+/**
+ * Object to handle message related notifications that are showing in a
+ * notificationbox below the composed message content.
+ */
+var gComposeNotificationBar = {
+
+ get notificationBar() {
+ delete this.notificationBar;
+ return this.notificationBar = document.getElementById("attachmentNotificationBox");
+ },
+
+ setBlockedContent: function(aBlockedURI) {
+ let brandName = sBrandBundle.getString("brandShortName");
+ let buttonLabel = sComposeMsgsBundle.getString("blockedContentPrefLabel");
+ let buttonAccesskey = sComposeMsgsBundle.getString("blockedContentPrefAccesskey");
+
+ let buttons = [{
+ label: buttonLabel,
+ accessKey: buttonAccesskey,
+ popup: "blockedContentOptions",
+ callback: function(aNotification, aButton) {
+ return true; // keep notification open
+ }
+ }];
+
+ // The popup value is a space separated list of all the blocked urls.
+ let popup = document.getElementById("blockedContentOptions");
+ let urls = popup.value ? popup.value.split(" ") : [];
+ if (!urls.includes(aBlockedURI)) {
+ urls.push(aBlockedURI);
+ }
+ popup.value = urls.join(" ");
+
+ let msg = sComposeMsgsBundle.getFormattedString("blockedContentMessage",
+ [brandName, brandName]);
+ msg = PluralForm.get(urls.length, msg);
+
+ if (!this.isShowingBlockedContentNotification()) {
+ this.notificationBar
+ .appendNotification(msg, "blockedContent", null,
+ this.notificationBar.PRIORITY_WARNING_MEDIUM,
+ buttons);
+ }
+ else {
+ this.notificationBar.getNotificationWithValue("blockedContent")
+ .setAttribute("label", msg);
+ }
+ },
+
+ isShowingBlockedContentNotification: function() {
+ return !!this.notificationBar.getNotificationWithValue("blockedContent");
+ },
+
+ clearBlockedContentNotification: function() {
+ this.notificationBar.removeNotification(
+ this.notificationBar.getNotificationWithValue("blockedContent"));
+ },
+
+ clearNotifications: function(aValue) {
+ this.notificationBar.removeAllNotifications(true);
+ },
+
+ setIdentityWarning: function(aIdentityName) {
+ if (!this.notificationBar.getNotificationWithValue("identityWarning")) {
+ let text = sComposeMsgsBundle.getString("identityWarning").split("%S");
+ let label = new DocumentFragment();
+ label.appendChild(document.createTextNode(text[0]));
+ label.appendChild(document.createElement("b"));
+ label.lastChild.appendChild(document.createTextNode(aIdentityName));
+ label.appendChild(document.createTextNode(text[1]));
+ this.notificationBar.appendNotification(label, "identityWarning", null,
+ this.notificationBar.PRIORITY_WARNING_HIGH, null);
+ }
+ },
+
+ clearIdentityWarning: function() {
+ let idWarning = this.notificationBar.getNotificationWithValue("identityWarning");
+ if (idWarning)
+ this.notificationBar.removeNotification(idWarning);
+ }
+};
+
+/**
+ * Populate the menuitems of what blocked content to unblock.
+ */
+function onBlockedContentOptionsShowing(aEvent) {
+ let urls = aEvent.target.value ? aEvent.target.value.split(" ") : [];
+
+ // Out with the old...
+ let childNodes = aEvent.target.childNodes;
+ for (let i = childNodes.length - 1; i >= 0; i--) {
+ childNodes[i].remove();
+ }
+
+ // ... and in with the new.
+ for (let url of urls) {
+ let menuitem = document.createElement("menuitem");
+ let fString = sComposeMsgsBundle.getFormattedString("blockedAllowResource",
+ [url]);
+ menuitem.setAttribute("label", fString);
+ menuitem.setAttribute("crop", "center");
+ menuitem.setAttribute("value", url);
+ menuitem.setAttribute("oncommand",
+ "onUnblockResource(this.value, this.parentNode);");
+ aEvent.target.appendChild(menuitem);
+ }
+}
+
+/**
+ * Handle clicking the "Load <url>" in the blocked content notification bar.
+ * @param {String} aURL - the URL that was unblocked
+ * @param {Node} aNode - the node holding as value the URLs of the blocked
+ * resources in the message (space separated).
+ */
+function onUnblockResource(aURL, aNode) {
+ try {
+ loadBlockedImage(aURL);
+ } catch (e) {
+ // Couldn't load the referenced image.
+ Cu.reportError(e);
+ } finally {
+ // Remove it from the list on success and failure.
+ let urls = aNode.value.split(" ");
+ for (let i = 0; i < urls.length; i++) {
+ if (urls[i] == aURL) {
+ urls.splice(i, 1);
+ aNode.value = urls.join(" ");
+ if (urls.length == 0) {
+ gComposeNotificationBar.clearBlockedContentNotification();
+ }
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Convert the blocked content to a data URL and swap the src to that for the
+ * elements that were using it.
+ *
+ * @param {String} aURL - (necko) URL to unblock
+ * @param {Bool} aReturnDataURL - return data: URL instead of processing image
+ * @return {String} the image as data: URL.
+ * @throw Error() if reading the data failed
+ */
+function loadBlockedImage(aURL, aReturnDataURL = false) {
+ let filename;
+ if (/^(file|chrome):/i.test(aURL)) {
+ filename = aURL.substr(aURL.lastIndexOf("/") + 1);
+ }
+ else {
+ let fnMatch = /[?&;]filename=([^?&]+)/.exec(aURL);
+ filename = (fnMatch && fnMatch[1]) || "";
+ }
+
+ filename = decodeURIComponent(filename);
+ let uri = Services.io.newURI(aURL);
+ let contentType;
+ if (filename) {
+ try {
+ contentType = Cc["@mozilla.org/mime;1"]
+ .getService(Ci.nsIMIMEService)
+ .getTypeFromURI(uri);
+ } catch (ex) {
+ contentType = "image/png";
+ }
+
+ if (!contentType.startsWith("image/")) {
+ // Unsafe to unblock this. It would just be garbage either way.
+ throw new Error("Won't unblock; URL=" + aURL +
+ ", contentType=" + contentType);
+ }
+ }
+ else {
+ // Assuming image/png is the best we can do.
+ contentType = "image/png";
+ }
+
+ let channel =
+ Services.io.newChannelFromURI(uri,
+ null,
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null,
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+
+ let inputStream = channel.open();
+ let stream = Cc["@mozilla.org/binaryinputstream;1"]
+ .createInstance(Ci.nsIBinaryInputStream);
+ stream.setInputStream(inputStream);
+ let streamData = "";
+ try {
+ while (stream.available() > 0) {
+ streamData += stream.readBytes(stream.available());
+ }
+ } catch(e) {
+ stream.close();
+ throw new Error("Couln't read all data from URL=" + aURL + " (" + e +")");
+ }
+ stream.close();
+
+ let encoded = btoa(streamData);
+ let dataURL = "data:" + contentType +
+ (filename ? ";filename=" + encodeURIComponent(filename) : "") +
+ ";base64," + encoded;
+
+ if (aReturnDataURL) {
+ return dataURL;
+ }
+
+ let editor = GetCurrentEditor();
+ for (let img of editor.document.images) {
+ if (img.src == aURL) {
+ img.src = dataURL; // Swap to data URL.
+ img.classList.remove("loading-internal");
+ }
+ }
+}
diff --git a/comm/suite/mailnews/components/compose/content/addressingWidgetOverlay.js b/comm/suite/mailnews/components/compose/content/addressingWidgetOverlay.js
new file mode 100644
index 0000000000..38cd1f5ecc
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/addressingWidgetOverlay.js
@@ -0,0 +1,1167 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+
+top.MAX_RECIPIENTS = 1; /* for the initial listitem created in the XUL */
+
+var inputElementType = "";
+var selectElementType = "";
+var selectElementIndexTable = null;
+
+var gNumberOfCols = 0;
+
+var gDragService = Cc["@mozilla.org/widget/dragservice;1"]
+ .getService(Ci.nsIDragService);
+
+/**
+ * global variable inherited from MsgComposeCommands.js
+ *
+ var gMsgCompose;
+ */
+
+function awGetMaxRecipients()
+{
+ return top.MAX_RECIPIENTS;
+}
+
+function awGetNumberOfCols()
+{
+ if (gNumberOfCols == 0)
+ {
+ var listbox = document.getElementById('addressingWidget');
+ var listCols = listbox.getElementsByTagName('listcol');
+ gNumberOfCols = listCols.length;
+ if (!gNumberOfCols)
+ gNumberOfCols = 1; /* if no cols defined, that means we have only one! */
+ }
+
+ return gNumberOfCols;
+}
+
+function awInputElementName()
+{
+ if (inputElementType == "")
+ inputElementType = document.getElementById("addressCol2#1").localName;
+ return inputElementType;
+}
+
+function awSelectElementName()
+{
+ if (selectElementType == "")
+ selectElementType = document.getElementById("addressCol1#1").localName;
+ return selectElementType;
+}
+
+// TODO: replace awGetSelectItemIndex with recipient type index constants
+
+function awGetSelectItemIndex(itemData)
+{
+ if (selectElementIndexTable == null)
+ {
+ selectElementIndexTable = new Object();
+ var selectElem = document.getElementById("addressCol1#1");
+ for (var i = 0; i < selectElem.childNodes[0].childNodes.length; i ++)
+ {
+ var aData = selectElem.childNodes[0].childNodes[i].getAttribute("value");
+ selectElementIndexTable[aData] = i;
+ }
+ }
+ return selectElementIndexTable[itemData];
+}
+
+function Recipients2CompFields(msgCompFields)
+{
+ if (!msgCompFields) {
+ throw new Error("Message Compose Error: msgCompFields is null (ExtractRecipients)");
+ return;
+ }
+
+ var i = 1;
+ var addrTo = "";
+ var addrCc = "";
+ var addrBcc = "";
+ var addrReply = "";
+ var addrNg = "";
+ var addrFollow = "";
+ var to_Sep = "";
+ var cc_Sep = "";
+ var bcc_Sep = "";
+ var reply_Sep = "";
+ var ng_Sep = "";
+ var follow_Sep = "";
+
+ var recipientType;
+ var inputField;
+ var fieldValue;
+ var recipient;
+ while ((inputField = awGetInputElement(i)))
+ {
+ fieldValue = inputField.value;
+
+ if (fieldValue != "")
+ {
+ recipientType = awGetPopupElement(i).value;
+ recipient = null;
+
+ switch (recipientType)
+ {
+ case "addr_to" :
+ case "addr_cc" :
+ case "addr_bcc" :
+ case "addr_reply" :
+ try {
+ let headerParser = MailServices.headerParser;
+ recipient =
+ headerParser.makeFromDisplayAddress(fieldValue)
+ .map(fullValue => headerParser.makeMimeAddress(
+ fullValue.name,
+ fullValue.email))
+ .join(", ");
+ } catch (ex) {
+ recipient = fieldValue;
+ }
+ break;
+ }
+
+ switch (recipientType)
+ {
+ case "addr_to" : addrTo += to_Sep + recipient; to_Sep = ","; break;
+ case "addr_cc" : addrCc += cc_Sep + recipient; cc_Sep = ","; break;
+ case "addr_bcc" : addrBcc += bcc_Sep + recipient; bcc_Sep = ","; break;
+ case "addr_reply" : addrReply += reply_Sep + recipient; reply_Sep = ","; break;
+ case "addr_newsgroups" : addrNg += ng_Sep + fieldValue; ng_Sep = ","; break;
+ case "addr_followup" : addrFollow += follow_Sep + fieldValue; follow_Sep = ","; break;
+ case "addr_other":
+ let headerName = awGetPopupElement(i).label;
+ headerName = headerName.substring(0, headerName.indexOf(':'));
+ msgCompFields.setRawHeader(headerName, fieldValue, null);
+ break;
+ }
+ }
+ i ++;
+ }
+
+ msgCompFields.to = addrTo;
+ msgCompFields.cc = addrCc;
+ msgCompFields.bcc = addrBcc;
+ msgCompFields.replyTo = addrReply;
+ msgCompFields.newsgroups = addrNg;
+ msgCompFields.followupTo = addrFollow;
+}
+
+function CompFields2Recipients(msgCompFields)
+{
+ if (msgCompFields) {
+ var listbox = document.getElementById('addressingWidget');
+ var newListBoxNode = listbox.cloneNode(false);
+ var listBoxColsClone = listbox.firstChild.cloneNode(true);
+ newListBoxNode.appendChild(listBoxColsClone);
+ let templateNode = listbox.querySelector("listitem");
+ // dump("replacing child in comp fields 2 recips \n");
+ listbox.parentNode.replaceChild(newListBoxNode, listbox);
+
+ top.MAX_RECIPIENTS = 0;
+ var msgReplyTo = msgCompFields.replyTo;
+ var msgTo = msgCompFields.to;
+ var msgCC = msgCompFields.cc;
+ var msgBCC = msgCompFields.bcc;
+ var msgNewsgroups = msgCompFields.newsgroups;
+ var msgFollowupTo = msgCompFields.followupTo;
+ var havePrimaryRecipient = false;
+ if (msgReplyTo)
+ awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgReplyTo, false),
+ "addr_reply", newListBoxNode, templateNode);
+ if (msgTo) {
+ var rcp = msgCompFields.splitRecipients(msgTo, false);
+ if (rcp.length)
+ {
+ awSetInputAndPopupFromArray(rcp, "addr_to", newListBoxNode, templateNode);
+ havePrimaryRecipient = true;
+ }
+ }
+ if (msgCC)
+ awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgCC, false),
+ "addr_cc", newListBoxNode, templateNode);
+ if (msgBCC)
+ awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgBCC, false),
+ "addr_bcc", newListBoxNode, templateNode);
+ if (msgNewsgroups) {
+ awSetInputAndPopup(msgNewsgroups, "addr_newsgroups", newListBoxNode, templateNode);
+ havePrimaryRecipient = true;
+ }
+ if(msgFollowupTo)
+ awSetInputAndPopup(msgFollowupTo, "addr_followup", newListBoxNode, templateNode);
+
+ // If it's a new message, we need to add an extra empty recipient.
+ if (!havePrimaryRecipient)
+ _awSetInputAndPopup("", "addr_to", newListBoxNode, templateNode);
+ awFitDummyRows(2);
+
+ // CompFields2Recipients is called whenever a user replies or edits an existing message.
+ // We want to add all of the recipients for this message to the ignore list for spell check
+ let currentAddress = gCurrentIdentity ? gCurrentIdentity.fullAddress : "";
+ addRecipientsToIgnoreList([currentAddress,msgTo,msgCC,msgBCC].filter(adr => adr).join(", "));
+ }
+}
+
+function awSetInputAndPopupId(inputElem, popupElem, rowNumber)
+{
+ popupElem.id = "addressCol1#" + rowNumber;
+ inputElem.id = "addressCol2#" + rowNumber;
+ inputElem.setAttribute("aria-labelledby", popupElem.id);
+}
+
+function awSetInputAndPopupValue(inputElem, inputValue, popupElem, popupValue, rowNumber)
+{
+ inputElem.value = inputValue.trimLeft();
+
+ popupElem.selectedItem = popupElem.childNodes[0].childNodes[awGetSelectItemIndex(popupValue)];
+
+ if (rowNumber >= 0)
+ awSetInputAndPopupId(inputElem, popupElem, rowNumber);
+
+ _awSetAutoComplete(popupElem, inputElem);
+
+ onRecipientsChanged(true);
+}
+
+function _awSetInputAndPopup(inputValue, popupValue, parentNode, templateNode)
+{
+ top.MAX_RECIPIENTS++;
+
+ var newNode = templateNode.cloneNode(true);
+ parentNode.appendChild(newNode); // we need to insert the new node before we set the value of the select element!
+
+ var input = newNode.getElementsByTagName(awInputElementName());
+ var select = newNode.getElementsByTagName(awSelectElementName());
+
+ if (input && input.length == 1 && select && select.length == 1)
+ awSetInputAndPopupValue(input[0], inputValue, select[0], popupValue, top.MAX_RECIPIENTS)
+}
+
+function awSetInputAndPopup(inputValue, popupValue, parentNode, templateNode)
+{
+ if ( inputValue && popupValue )
+ {
+ var addressArray = inputValue.split(",");
+
+ for ( var index = 0; index < addressArray.length; index++ )
+ _awSetInputAndPopup(addressArray[index], popupValue, parentNode, templateNode);
+ }
+}
+
+function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templateNode)
+{
+ if (popupValue)
+ {
+ for (let recipient of inputArray)
+ _awSetInputAndPopup(recipient, popupValue, parentNode, templateNode);
+ }
+}
+
+function awRemoveRecipients(msgCompFields, recipientType, recipientsList)
+{
+ if (!msgCompFields)
+ return;
+
+ var recipientArray = msgCompFields.splitRecipients(recipientsList, false);
+
+ for (var index = 0; index < recipientArray.length; index++)
+ for (var row = 1; row <= top.MAX_RECIPIENTS; row ++)
+ {
+ var popup = awGetPopupElement(row);
+ if (popup.value == recipientType) {
+ var input = awGetInputElement(row);
+ if (input.value == recipientArray[index])
+ {
+ awSetInputAndPopupValue(input, "", popup, "addr_to", -1);
+ break;
+ }
+ }
+ }
+}
+
+function awAddRecipients(msgCompFields, recipientType, recipientsList)
+{
+ if (!msgCompFields)
+ return;
+
+ var recipientArray = msgCompFields.splitRecipients(recipientsList, false);
+
+ for (var index = 0; index < recipientArray.length; index++)
+ awAddRecipient(recipientType, recipientArray[index]);
+}
+
+// this was broken out of awAddRecipients so it can be re-used...adds a new row matching recipientType and
+// drops in the single address.
+function awAddRecipient(recipientType, address)
+{
+ for (var row = 1; row <= top.MAX_RECIPIENTS; row ++)
+ {
+ if (awGetInputElement(row).value == "")
+ break;
+ }
+
+ if (row > top.MAX_RECIPIENTS)
+ awAppendNewRow(false);
+
+ awSetInputAndPopupValue(awGetInputElement(row), address, awGetPopupElement(row), recipientType, row);
+
+ /* be sure we still have an empty row left at the end */
+ if (row == top.MAX_RECIPIENTS)
+ {
+ awAppendNewRow(true);
+ awSetInputAndPopupValue(awGetInputElement(top.MAX_RECIPIENTS), "", awGetPopupElement(top.MAX_RECIPIENTS), recipientType, top.MAX_RECIPIENTS);
+ }
+
+ // add the recipient to our spell check ignore list
+ addRecipientsToIgnoreList(address);
+}
+
+function awTestRowSequence()
+{
+ /*
+ This function is for debug and testing purpose only, normal users should not run it!
+
+ Everytime we insert or delete a row, we must be sure we didn't break the ID sequence of
+ the addressing widget rows. This function will run a quick test to see if the sequence still ok
+
+ You need to define the pref mail.debug.test_addresses_sequence to true in order to activate it
+ */
+
+ var test_sequence;
+ if (Services.prefs.getPrefType("mail.debug.test_addresses_sequence") == Ci.nsIPrefBranch.PREF_BOOL)
+ test_sequence = Services.prefs.getBoolPref("mail.debug.test_addresses_sequence");
+ if (!test_sequence)
+ return true;
+
+ /* debug code to verify the sequence still good */
+
+ var listbox = document.getElementById('addressingWidget');
+ var listitems = listbox.getElementsByTagName('listitem');
+ if (listitems.length >= top.MAX_RECIPIENTS )
+ {
+ for (var i = 1; i <= listitems.length; i ++)
+ {
+ var item = listitems [i - 1];
+ let inputID = item.querySelector(awInputElementName()).id.split("#")[1];
+ let popupID = item.querySelector(awSelectElementName()).id.split("#")[1];
+ if (inputID != i || popupID != i)
+ {
+ dump("#ERROR: sequence broken at row " + i + ", inputID=" + inputID + ", popupID=" + popupID + "\n");
+ return false;
+ }
+ dump("---SEQUENCE OK---\n");
+ return true;
+ }
+ }
+ else
+ dump("#ERROR: listitems.length(" + listitems.length + ") < top.MAX_RECIPIENTS(" + top.MAX_RECIPIENTS + ")\n");
+
+ return false;
+}
+
+function awCleanupRows()
+{
+ var maxRecipients = top.MAX_RECIPIENTS;
+ var rowID = 1;
+
+ for (var row = 1; row <= maxRecipients; row ++)
+ {
+ var inputElem = awGetInputElement(row);
+ if (inputElem.value == "" && row < maxRecipients)
+ awRemoveRow(awGetRowByInputElement(inputElem));
+ else
+ {
+ awSetInputAndPopupId(inputElem, awGetPopupElement(row), rowID);
+ rowID ++;
+ }
+ }
+
+ awTestRowSequence();
+}
+
+function awDeleteRow(rowToDelete)
+{
+ /* When we delete a row, we must reset the id of others row in order to not break the sequence */
+ var maxRecipients = top.MAX_RECIPIENTS;
+ awRemoveRow(rowToDelete);
+
+ // assume 2 column update (input and popup)
+ for (var row = rowToDelete + 1; row <= maxRecipients; row ++)
+ awSetInputAndPopupId(awGetInputElement(row), awGetPopupElement(row), (row-1));
+
+ awTestRowSequence();
+}
+
+function awClickEmptySpace(target, setFocus)
+{
+ if (target == null ||
+ (target.localName != "listboxbody" &&
+ target.localName != "listcell" &&
+ target.localName != "listitem"))
+ return;
+
+ let lastInput = awGetInputElement(top.MAX_RECIPIENTS);
+
+ if ( lastInput && lastInput.value )
+ awAppendNewRow(setFocus);
+ else if (setFocus)
+ awSetFocusTo(lastInput);
+}
+
+function awReturnHit(inputElement)
+{
+ let row = awGetRowByInputElement(inputElement);
+ let nextInput = awGetInputElement(row+1);
+
+ if ( !nextInput )
+ {
+ if ( inputElement.value )
+ awAppendNewRow(true);
+ else // No address entered, switch to Subject field
+ {
+ var subjectField = document.getElementById( 'msgSubject' );
+ subjectField.select();
+ subjectField.focus();
+ }
+ }
+ else
+ {
+ nextInput.select();
+ awSetFocusTo(nextInput);
+ }
+
+ // be sure to add the recipient to our ignore list
+ // when the user hits enter in an autocomplete widget...
+ addRecipientsToIgnoreList(inputElement.value);
+}
+
+function awDeleteHit(inputElement)
+{
+ let row = awGetRowByInputElement(inputElement);
+
+ /* 1. don't delete the row if it's the last one remaining, just reset it! */
+ if (top.MAX_RECIPIENTS <= 1)
+ {
+ inputElement.value = "";
+ return;
+ }
+
+ /* 2. Set the focus to the previous field if possible */
+ // Note: awSetFocusTo() is asynchronous, i.e. we'll focus after row removal.
+ if (row > 1)
+ awSetFocusTo(awGetInputElement(row - 1))
+ else
+ awSetFocusTo(awGetInputElement(2))
+
+ /* 3. Delete the row */
+ awDeleteRow(row);
+}
+
+function awAppendNewRow(setFocus)
+{
+ var listbox = document.getElementById('addressingWidget');
+ var listitem1 = awGetListItem(1);
+
+ if ( listbox && listitem1 )
+ {
+ var lastRecipientType = awGetPopupElement(top.MAX_RECIPIENTS).value;
+
+ var nextDummy = awGetNextDummyRow();
+ var newNode = listitem1.cloneNode(true);
+ if (nextDummy)
+ listbox.replaceChild(newNode, nextDummy);
+ else
+ listbox.appendChild(newNode);
+
+ top.MAX_RECIPIENTS++;
+
+ var input = newNode.getElementsByTagName(awInputElementName());
+ if ( input && input.length == 1 )
+ {
+ input[0].value = "";
+
+ // We always clone the first row. The problem is that the first row
+ // could be focused. When we clone that row, we end up with a cloned
+ // XUL textbox that has a focused attribute set. Therefore we think
+ // we're focused and don't properly refocus. The best solution to this
+ // would be to clone a template row that didn't really have any presentation,
+ // rather than using the real visible first row of the listbox.
+ //
+ // For now we'll just put in a hack that ensures the focused attribute
+ // is never copied when the node is cloned.
+ if (input[0].getAttribute('focused') != '')
+ input[0].removeAttribute('focused');
+ }
+ var select = newNode.getElementsByTagName(awSelectElementName());
+ if ( select && select.length == 1 )
+ {
+ // It only makes sense to clone some field types; others
+ // should not be cloned, since it just makes the user have
+ // to go to the trouble of selecting something else. In such
+ // cases let's default to 'To' (a reasonable default since
+ // we already default to 'To' on the first dummy field of
+ // a new message).
+ switch (lastRecipientType)
+ {
+ case "addr_reply":
+ case "addr_other":
+ select[0].selectedIndex = awGetSelectItemIndex("addr_to");
+ break;
+ case "addr_followup":
+ select[0].selectedIndex = awGetSelectItemIndex("addr_newsgroups");
+ break;
+ default:
+ // e.g. "addr_to","addr_cc","addr_bcc","addr_newsgroups":
+ select[0].selectedIndex = awGetSelectItemIndex(lastRecipientType);
+ }
+
+ awSetInputAndPopupId(input[0], select[0], top.MAX_RECIPIENTS);
+
+ if (input)
+ _awSetAutoComplete(select[0], input[0]);
+ }
+
+ // Focus the new input widget.
+ if (setFocus && input[0] )
+ awSetFocusTo(input[0]);
+ }
+}
+
+// functions for accessing the elements in the addressing widget
+
+/**
+ * Returns the recipient type popup for a row.
+ *
+ * @param row Index of the recipient row to return. Starts at 1.
+ * @return This returns the menulist (not its child menupopup), despite the
+ * function name.
+ */
+function awGetPopupElement(row)
+{
+ return document.getElementById("addressCol1#" + row);
+}
+
+/**
+ * Returns the recipient inputbox for a row.
+ *
+ * @param row Index of the recipient row to return. Starts at 1.
+ * @return This returns the textbox element.
+ */
+function awGetInputElement(row)
+{
+ return document.getElementById("addressCol2#" + row);
+}
+
+function awGetElementByCol(row, col)
+{
+ var colID = "addressCol" + col + "#" + row;
+ return document.getElementById(colID);
+}
+
+function awGetListItem(row)
+{
+ var listbox = document.getElementById('addressingWidget');
+
+ if ( listbox && row > 0)
+ {
+ var listitems = listbox.getElementsByTagName('listitem');
+ if ( listitems && listitems.length >= row )
+ return listitems[row-1];
+ }
+ return 0;
+}
+
+function awGetRowByInputElement(inputElement)
+{
+ var row = 0;
+ if (inputElement) {
+ var listitem = inputElement.parentNode.parentNode;
+ while (listitem) {
+ if (listitem.localName == "listitem")
+ ++row;
+ listitem = listitem.previousSibling;
+ }
+ }
+ return row;
+}
+
+
+// Copy Node - copy this node and insert ahead of the (before) node. Append to end if before=0
+function awCopyNode(node, parentNode, beforeNode)
+{
+ var newNode = node.cloneNode(true);
+
+ if ( beforeNode )
+ parentNode.insertBefore(newNode, beforeNode);
+ else
+ parentNode.appendChild(newNode);
+
+ return newNode;
+}
+
+// remove row
+
+function awRemoveRow(row)
+{
+ awGetListItem(row).remove();
+ awFitDummyRows();
+
+ top.MAX_RECIPIENTS --;
+}
+
+/**
+ * Set focus to the specified element, typically a recipient input element.
+ * We do this asynchronusly to allow other processes like adding or removing rows
+ * to complete before shifting focus.
+ *
+ * @param element the element to receive focus asynchronously
+ */
+function awSetFocusTo(element) {
+ // Remember the (input) element to focus for asynchronous focusing, so that we
+ // play safe if this gets called again and the original element gets removed
+ // before we can focus it.
+ top.awInputToFocus = element;
+ setTimeout(_awSetFocusTo, 0);
+}
+
+function _awSetFocusTo() {
+ top.awInputToFocus.focus();
+}
+
+// Deprecated - use awSetFocusTo() instead.
+// ### TODO: This function should be removed if we're sure addons aren't using it.
+function awSetFocus(row, inputElement) {
+ awSetFocusTo(inputElement);
+}
+
+function awTabFromRecipient(element, event) {
+ var row = awGetRowByInputElement(element);
+ if (!event.shiftKey && row < top.MAX_RECIPIENTS) {
+ var listBoxRow = row - 1; // listbox row indices are 0-based, ours are 1-based.
+ var listBox = document.getElementById("addressingWidget");
+ listBox.listBoxObject.ensureIndexIsVisible(listBoxRow + 1);
+ }
+
+ // be sure to add the recipient to our ignore list
+ // when the user tabs out of an autocomplete line...
+ addRecipientsToIgnoreList(element.value);
+}
+
+function awTabFromMenulist(element, event)
+{
+ var row = awGetRowByInputElement(element);
+ if (event.shiftKey && row > 1) {
+ var listBoxRow = row - 1; // listbox row indices are 0-based, ours are 1-based.
+ var listBox = document.getElementById("addressingWidget");
+ listBox.listBoxObject.ensureIndexIsVisible(listBoxRow - 1);
+ }
+}
+
+function awGetNumberOfRecipients()
+{
+ return top.MAX_RECIPIENTS;
+}
+
+function DropOnAddressingTarget(event, onWidget) {
+ let dragSession = gDragService.getCurrentSession();
+
+ let trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ trans.init(getLoadContext());
+ trans.addDataFlavor("text/x-moz-address");
+
+ let added = false;
+ for (let i = 0; i < dragSession.numDropItems; ++i) {
+ dragSession.getData(trans, i);
+ let dataObj = {};
+ let bestFlavor = {};
+ let len = {};
+
+ // Ensure we catch any empty data that may have slipped through.
+ try {
+ trans.getAnyTransferData(bestFlavor, dataObj, len);
+ } catch(ex) {
+ continue;
+ }
+ if (dataObj) {
+ dataObj = dataObj.value.QueryInterface(Ci.nsISupportsString);
+ }
+ if (!dataObj) {
+ continue;
+ }
+
+ // Pull the address out of the data object.
+ let address = dataObj.data.substring(0, len.value);
+ if (!address) {
+ continue;
+ }
+
+ if (onWidget) {
+ // Break down and add each address.
+ parseAndAddAddresses(address,
+ awGetPopupElement(top.MAX_RECIPIENTS).value);
+ } else {
+ // Add address into the bucket.
+ DropRecipient(address);
+ }
+ added = true;
+ }
+
+ // We added at least one address during the drop.
+ // Disable the default handler and stop propagating the event
+ // to avoid data being dropped twice.
+ if (added) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+}
+
+function _awSetAutoComplete(selectElem, inputElem)
+{
+ let params = JSON.parse(inputElem.getAttribute('autocompletesearchparam'));
+ params.type = selectElem.value;
+ inputElem.setAttribute('autocompletesearchparam', JSON.stringify(params));
+}
+
+function awSetAutoComplete(rowNumber)
+{
+ var inputElem = awGetInputElement(rowNumber);
+ var selectElem = awGetPopupElement(rowNumber);
+ _awSetAutoComplete(selectElem, inputElem)
+}
+
+function awRecipientTextCommand(userAction, element)
+{
+ if (userAction == "typing" || userAction == "scrolling")
+ awReturnHit(element);
+}
+
+// Called when an autocomplete session item is selected and the status of
+// the session it was selected from is nsIAutoCompleteStatus::failureItems.
+//
+// As of this writing, the only way that can happen is when an LDAP
+// autocomplete session returns an error to be displayed to the user.
+//
+// There are hardcoded messages in here, but these are just fallbacks for
+// when string bundles have already failed us.
+//
+function awRecipientErrorCommand(errItem, element)
+{
+ // remove the angle brackets from the general error message to construct
+ // the title for the alert. someday we'll pass this info using a real
+ // exception object, and then this code can go away.
+ //
+ var generalErrString;
+ if (errItem.value != "") {
+ generalErrString = errItem.value.slice(1, errItem.value.length-1);
+ } else {
+ generalErrString = "Unknown LDAP server problem encountered";
+ }
+
+ // try and get the string of the specific error to contruct the complete
+ // err msg, otherwise fall back to something generic. This message is
+ // handed to us as an nsISupportsString in the param slot of the
+ // autocomplete error item, by agreement documented in
+ // nsILDAPAutoCompFormatter.idl
+ //
+ var specificErrString = "";
+ try {
+ var specificError = errItem.param.QueryInterface(Ci.nsISupportsString);
+ specificErrString = specificError.data;
+ } catch (ex) {
+ }
+ if (specificErrString == "") {
+ specificErrString = "Internal error";
+ }
+
+ Services.prompt.alert(window, generalErrString, specificErrString);
+}
+
+function awRecipientKeyPress(event, element)
+{
+ switch(event.key) {
+ case "ArrowUp":
+ awArrowHit(element, -1);
+ break;
+ case "ArrowDown":
+ awArrowHit(element, 1);
+ break;
+ case "Enter":
+ case "Tab":
+ // if the user text contains a comma or a line return, ignore
+ if (element.value.includes(',')) {
+ var addresses = element.value;
+ element.value = ""; // clear out the current line so we don't try to autocomplete it..
+ parseAndAddAddresses(addresses, awGetPopupElement(awGetRowByInputElement(element)).value);
+ }
+ else if (event.key == "Tab")
+ awTabFromRecipient(element, event);
+
+ break;
+ }
+}
+
+function awArrowHit(inputElement, direction)
+{
+ var row = awGetRowByInputElement(inputElement) + direction;
+ if (row) {
+ var nextInput = awGetInputElement(row);
+
+ if (nextInput)
+ awSetFocusTo(nextInput);
+ else if (inputElement.value)
+ awAppendNewRow(true);
+ }
+}
+
+function awRecipientKeyDown(event, element)
+{
+ switch(event.key) {
+ case "Delete":
+ case "Backspace":
+ /* do not query directly the value of the text field else the autocomplete widget could potentially
+ alter it value while doing some internal cleanup, instead, query the value through the first child
+ */
+ if (!element.value)
+ awDeleteHit(element);
+
+ //We need to stop the event else the listbox will receive it and the function
+ //awKeyDown will be executed!
+ event.stopPropagation();
+ break;
+ }
+}
+
+function awKeyDown(event, listboxElement)
+{
+ switch(event.key) {
+ case "Delete":
+ case "Backspace":
+ /* Warning, the listboxElement.selectedItems will change everytime we delete a row */
+ var length = listboxElement.selectedCount;
+ for (var i = 1; i <= length; i++) {
+ var inputs = listboxElement.selectedItem.getElementsByTagName(awInputElementName());
+ if (inputs && inputs.length == 1)
+ awDeleteHit(inputs[0]);
+ }
+ break;
+ }
+}
+
+function awMenulistKeyPress(event, element)
+{
+ switch(event.key) {
+ case "Tab":
+ awTabFromMenulist(element, event);
+ break;
+ }
+}
+
+/* ::::::::::: addressing widget dummy rows ::::::::::::::::: */
+
+var gAWContentHeight = 0;
+var gAWRowHeight = 0;
+
+function awFitDummyRows()
+{
+ awCalcContentHeight();
+ awCreateOrRemoveDummyRows();
+}
+
+function awCreateOrRemoveDummyRows()
+{
+ var listbox = document.getElementById("addressingWidget");
+ var listboxHeight = listbox.boxObject.height;
+
+ // remove rows to remove scrollbar
+ let kids = listbox.querySelectorAll('[_isDummyRow]');
+ for (let i = kids.length - 1; gAWContentHeight > listboxHeight && i >= 0; --i) {
+ gAWContentHeight -= gAWRowHeight;
+ kids[i].remove();
+ }
+
+ // add rows to fill space
+ if (gAWRowHeight) {
+ while (gAWContentHeight + gAWRowHeight < listboxHeight) {
+ awCreateDummyItem(listbox);
+ gAWContentHeight += gAWRowHeight;
+ }
+ }
+}
+
+function awCalcContentHeight()
+{
+ var listbox = document.getElementById("addressingWidget");
+ var items = listbox.getElementsByTagName("listitem");
+
+ gAWContentHeight = 0;
+ if (items.length > 0) {
+ // all rows are forced to a uniform height in xul listboxes, so
+ // find the first listitem with a boxObject and use it as precedent
+ var i = 0;
+ do {
+ gAWRowHeight = items[i].boxObject.height;
+ ++i;
+ } while (i < items.length && !gAWRowHeight);
+ gAWContentHeight = gAWRowHeight*items.length;
+ }
+}
+
+function awCreateDummyItem(aParent)
+{
+ var titem = document.createElement("listitem");
+ titem.setAttribute("_isDummyRow", "true");
+ titem.setAttribute("class", "dummy-row");
+
+ for (var i = awGetNumberOfCols(); i > 0; i--)
+ awCreateDummyCell(titem);
+
+ if (aParent)
+ aParent.appendChild(titem);
+
+ return titem;
+}
+
+function awCreateDummyCell(aParent)
+{
+ var cell = document.createElement("listcell");
+ cell.setAttribute("class", "addressingWidgetCell dummy-row-cell");
+ if (aParent)
+ aParent.appendChild(cell);
+
+ return cell;
+}
+
+function awGetNextDummyRow()
+{
+ // gets the next row from the top down
+ return document.querySelector('#addressingWidget > [_isDummyRow]');
+}
+
+function awSizerListen()
+{
+ // when splitter is clicked, fill in necessary dummy rows each time the mouse is moved
+ awCalcContentHeight(); // precalculate
+ document.addEventListener("mousemove", awSizerMouseMove, true);
+ document.addEventListener("mouseup", awSizerMouseUp);
+}
+
+function awSizerMouseMove()
+{
+ awCreateOrRemoveDummyRows(2);
+}
+
+function awSizerMouseUp()
+{
+ document.removeEventListener("mousemove", awSizerMouseMove);
+ document.removeEventListener("mouseup", awSizerMouseUp);
+}
+
+function awSizerResized(aSplitter)
+{
+ // set the height on the listbox rather than on the toolbox
+ var listbox = document.getElementById("addressingWidget");
+ listbox.height = listbox.boxObject.height;
+ // remove all the heights set on the splitter's previous siblings
+ for (let sib = aSplitter.previousSibling; sib; sib = sib.previousSibling)
+ sib.removeAttribute("height");
+}
+
+function awDocumentKeyPress(event)
+{
+ try {
+ var id = event.target.id;
+ if (id.startsWith('addressCol1'))
+ awRecipientKeyPress(event, event.target);
+ } catch (e) { }
+}
+
+function awRecipientInputCommand(event, inputElement)
+{
+ gContentChanged=true;
+ setupAutocomplete();
+}
+
+// Given an arbitrary block of text like a comma delimited list of names or a names separated by spaces,
+// we will try to autocomplete each of the names and then take the FIRST match for each name, adding it the
+// addressing widget on the compose window.
+
+var gAutomatedAutoCompleteListener = null;
+
+function parseAndAddAddresses(addressText, recipientType)
+{
+ // strip any leading >> characters inserted by the autocomplete widget
+ var strippedAddresses = addressText.replace(/.* >> /, "");
+
+ var addresses = MailServices.headerParser
+ .makeFromDisplayAddress(strippedAddresses);
+
+ if (addresses.length)
+ {
+ // we need to set up our own autocomplete session and search for results
+
+ setupAutocomplete(); // be safe, make sure we are setup
+ if (!gAutomatedAutoCompleteListener)
+ gAutomatedAutoCompleteListener = new AutomatedAutoCompleteHandler();
+
+ gAutomatedAutoCompleteListener.init(addresses.map(addr => addr.toString()),
+ addresses.length, recipientType);
+ }
+}
+
+function AutomatedAutoCompleteHandler()
+{
+}
+
+// state driven self contained object which will autocomplete a block of addresses without any UI.
+// force picks the first match and adds it to the addressing widget, then goes on to the next
+// name to complete.
+
+AutomatedAutoCompleteHandler.prototype =
+{
+ param: this,
+ sessionName: null,
+ namesToComplete: {},
+ numNamesToComplete: 0,
+ indexIntoNames: 0,
+
+ numSessionsToSearch: 0,
+ numSessionsSearched: 0,
+ recipientType: null,
+ searchResults: null,
+
+ init:function(namesToComplete, numNamesToComplete, recipientType)
+ {
+ this.indexIntoNames = 0;
+ this.numNamesToComplete = numNamesToComplete;
+ this.namesToComplete = namesToComplete;
+
+ this.recipientType = recipientType;
+
+ // set up the auto complete sessions to use
+ setupAutocomplete();
+ this.autoCompleteNextAddress();
+ },
+
+ autoCompleteNextAddress:function()
+ {
+ this.numSessionsToSearch = 0;
+ this.numSessionsSearched = 0;
+ this.searchResults = new Array;
+
+ if (this.indexIntoNames < this.numNamesToComplete && this.namesToComplete[this.indexIntoNames])
+ {
+ /* XXX This is used to work, until switching to the new toolkit broke it
+ We should fix it see bug 456550.
+ if (!this.namesToComplete[this.indexIntoNames].includes('@')) // don't autocomplete if address has an @ sign in it
+ {
+ // make sure total session count is updated before we kick off ANY actual searches
+ if (gAutocompleteSession)
+ this.numSessionsToSearch++;
+
+ if (gLDAPSession && gCurrentAutocompleteDirectory)
+ this.numSessionsToSearch++;
+
+ if (gAutocompleteSession)
+ {
+ gAutocompleteSession.onAutoComplete(this.namesToComplete[this.indexIntoNames], null, this);
+ // AB searches are actually synchronous. So by the time we get here we have already looked up results.
+
+ // if we WERE going to also do an LDAP lookup, then check to see if we have a valid match in the AB, if we do
+ // don't bother with the LDAP search too just return
+
+ if (gLDAPSession && gCurrentAutocompleteDirectory && this.searchResults[0] && this.searchResults[0].defaultItemIndex != -1)
+ {
+ this.processAllResults();
+ return;
+ }
+ }
+
+ if (gLDAPSession && gCurrentAutocompleteDirectory)
+ gLDAPSession.onStartLookup(this.namesToComplete[this.indexIntoNames], null, this);
+ }
+ */
+
+ if (!this.numSessionsToSearch)
+ this.processAllResults(); // ldap and ab are turned off, so leave text alone
+ }
+ },
+
+ onStatus:function(aStatus)
+ {
+ return;
+ },
+
+ onAutoComplete: function(aResults, aStatus)
+ {
+ // store the results until all sessions are done and have reported in
+ if (aResults)
+ this.searchResults[this.numSessionsSearched] = aResults;
+
+ this.numSessionsSearched++; // bump our counter
+
+ if (this.numSessionsToSearch <= this.numSessionsSearched)
+ setTimeout('gAutomatedAutoCompleteListener.processAllResults()', 0); // we are all done
+ },
+
+ processAllResults: function()
+ {
+ // Take the first result and add it to the compose window
+ var addressToAdd;
+
+ // loop through the results looking for the non default case (default case is the address book with only one match, the default domain)
+ var sessionIndex;
+
+ var searchResultsForSession;
+
+ for (sessionIndex in this.searchResults)
+ {
+ searchResultsForSession = this.searchResults[sessionIndex];
+ if (searchResultsForSession && searchResultsForSession.defaultItemIndex > -1)
+ {
+ addressToAdd = searchResultsForSession.items
+ .queryElementAt(searchResultsForSession.defaultItemIndex,
+ Ci.nsIAutoCompleteItem).value;
+ break;
+ }
+ }
+
+ // still no match? loop through looking for the -1 default index
+ if (!addressToAdd)
+ {
+ for (sessionIndex in this.searchResults)
+ {
+ searchResultsForSession = this.searchResults[sessionIndex];
+ if (searchResultsForSession && searchResultsForSession.defaultItemIndex == -1)
+ {
+ addressToAdd = searchResultsForSession.items
+ .queryElementAt(0, Ci.nsIAutoCompleteItem).value;
+ break;
+ }
+ }
+ }
+
+ // no matches anywhere...just use what we were given
+ if (!addressToAdd)
+ addressToAdd = this.namesToComplete[this.indexIntoNames];
+
+ // that will automatically set the focus on a new available row, and make sure it is visible
+ awAddRecipient(this.recipientType ? this.recipientType : "addr_to", addressToAdd);
+
+ this.indexIntoNames++;
+ this.autoCompleteNextAddress();
+ },
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIAutoCompleteListener) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ }
+}
diff --git a/comm/suite/mailnews/components/compose/content/mailComposeOverlay.xul b/comm/suite/mailnews/components/compose/content/mailComposeOverlay.xul
new file mode 100644
index 0000000000..efd1c6007a
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/mailComposeOverlay.xul
@@ -0,0 +1,16 @@
+<?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/. -->
+
+<overlay id="mailComposeOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <menupopup id="menu_EditPopup" onpopupshowing="updateEditItems();">
+ <menuitem id="menu_inlineSpellCheck"
+ oncommand="EnableInlineSpellCheck(!InlineSpellCheckerUI.enabled);"/>
+ <menuitem id="menu_accountmgr"
+ insertafter="sep_preferences"
+ command="cmd_account"/>
+ </menupopup>
+</overlay>
diff --git a/comm/suite/mailnews/components/compose/content/messengercompose.xul b/comm/suite/mailnews/components/compose/content/messengercompose.xul
new file mode 100644
index 0000000000..89126d814d
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/messengercompose.xul
@@ -0,0 +1,720 @@
+<?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://messenger/skin/messengercompose/messengercompose.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
+<?xml-stylesheet href="chrome://editor/skin/editorFormatToolbar.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/addressingWidget.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/smime/msgCompSMIMEOverlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/content/bindings.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/charsetOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/sidebar/sidebarOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/contentAreaContextOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/messengercompose/msgComposeContextOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://editor/content/editorOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/messengercompose/mailComposeOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/mailOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % messengercomposeDTD SYSTEM "chrome://messenger/locale/messengercompose/messengercompose.dtd" >
+%messengercomposeDTD;
+<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
+%messengerDTD;
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % utilityDTD SYSTEM "chrome://communicator/locale/utilityOverlay.dtd">
+%utilityDTD;
+<!ENTITY % msgCompSMIMEDTD SYSTEM "chrome://messenger-smime/locale/msgCompSMIMEOverlay.dtd">
+%msgCompSMIMEDTD;
+]>
+
+<window id="msgcomposeWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ onunload="ComposeUnload()"
+ onload="ComposeLoad()"
+ onclose="return DoCommandClose()"
+ onfocus="EditorOnFocus()"
+ title="&msgComposeWindow.title;"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ windowtype="msgcompose"
+ macanimationtype="document"
+ drawtitle="true"
+ width="640" height="480"
+ persist="screenX screenY width height sizemode">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_composeMsgs" src="chrome://messenger/locale/messengercompose/composeMsgs.properties"/>
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_offlinePrompts" src="chrome://messenger/locale/offline.properties"/>
+ <stringbundle id="languageBundle" src="chrome://global/locale/languageNames.properties"/>
+ <stringbundle id="brandBundle" src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="bundle_comp_smime" src="chrome://messenger-smime/locale/msgCompSMIMEOverlay.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://communicator/content/contentAreaClick.js"/>
+ <script src="chrome://global/content/printUtils.js"/>
+ <script src="chrome://messenger/content/accountUtils.js"/>
+ <script src="chrome://messenger/content/mail-offline.js"/>
+ <script src="chrome://editor/content/editor.js"/>
+ <script src="chrome://messenger/content/messengercompose/MsgComposeCommands.js"/>
+ <script src="chrome://messenger/content/messengercompose/addressingWidgetOverlay.js"/>
+ <script src="chrome://messenger/content/addressbook/abDragDrop.js"/>
+ <script src="chrome://messenger-smime/content/msgCompSMIMEOverlay.js"/>
+
+ <commandset id="composeCommands">
+ <commandset id="msgComposeCommandUpdate"
+ commandupdater="true"
+ events="focus"
+ oncommandupdate="CommandUpdate_MsgCompose()"/>
+
+ <commandset id="editorCommands"/>
+ <commandset id="commonEditorMenuItems"/>
+ <commandset id="composerMenuItems"/>
+ <commandset id="composerEditMenuItems"/>
+ <commandset id="composerStyleMenuItems"/>
+ <commandset id="composerTableMenuItems"/>
+ <commandset id="composerListMenuItems"/>
+ <commandset id="tasksCommands"/>
+ <!-- File Menu -->
+ <command id="cmd_attachFile" oncommand="goDoCommand('cmd_attachFile')"/>
+ <command id="cmd_attachPage" oncommand="goDoCommand('cmd_attachPage')"/>
+ <command id="cmd_attachVCard" checked="false" oncommand="ToggleAttachVCard(event.target)"/>
+ <command id="cmd_save" oncommand="goDoCommand('cmd_save')"/>
+ <command id="cmd_saveAsFile" oncommand="goDoCommand('cmd_saveAsFile')"/>
+ <command id="cmd_saveAsDraft" oncommand="goDoCommand('cmd_saveAsDraft')"/>
+ <command id="cmd_saveAsTemplate" oncommand="goDoCommand('cmd_saveAsTemplate')"/>
+ <command id="cmd_sendButton" oncommand="goDoCommand('cmd_sendButton')"/>
+ <command id="cmd_sendNow" oncommand="goDoCommand('cmd_sendNow')"/>
+ <command id="cmd_sendWithCheck" oncommand="goDoCommand('cmd_sendWithCheck')"/>
+ <command id="cmd_sendLater" oncommand="goDoCommand('cmd_sendLater')"/>
+
+ <!-- Edit Menu -->
+ <!--command id="cmd_pasteQuote"/ DO NOT INCLUDE THOSE COMMANDS ELSE THE EDIT MENU WILL BE BROKEN! -->
+ <!--command id="cmd_find"/-->
+ <!--command id="cmd_findNext"/-->
+ <!--command id="cmd_findReplace"/-->
+ <command id="cmd_renameAttachment" oncommand="goDoCommand('cmd_renameAttachment')" disabled="true"/>
+ <command id="cmd_openAttachment" oncommand="goDoCommand('cmd_openAttachment')"/>
+ <command id="cmd_account"
+ label="&accountManagerCmd.label;"
+ accesskey="&accountManagerCmd.accesskey;"
+ oncommand="goDoCommand('cmd_account');"/>
+
+ <!-- Options Menu -->
+ <command id="cmd_selectAddress" oncommand="goDoCommand('cmd_selectAddress')"/>
+ <command id="cmd_outputFormat" oncommand="OutputFormatMenuSelect(event.target)"/>
+ <command id="cmd_quoteMessage" oncommand="goDoCommand('cmd_quoteMessage')"/>
+ <command id="cmd_viewSecurityStatus" oncommand="showMessageComposeSecurityStatus();"/>
+ </commandset>
+
+ <broadcasterset id="composeBroadcasters">
+ <broadcaster id="Communicator:WorkMode"/>
+ <broadcaster id="securityStatus" crypto="" signing=""/>
+ </broadcasterset>
+
+ <observes element="securityStatus" attribute="crypto"/>
+ <observes element="securityStatus" attribute="signing"/>
+
+ <broadcasterset id="mainBroadcasterSet"/>
+
+ <keyset id="tasksKeys">
+ <!-- File Menu -->
+ <key id="key_send" keycode="&sendCmd.keycode;" observes="cmd_sendWithCheck" modifiers="accel"/>
+ <key id="key_sendLater" keycode="&sendLaterCmd.keycode;" observes="cmd_sendLater" modifiers="accel, shift"/>
+
+ <!-- Options Menu -->
+ <!-- key id="key_selectAddresses" key="&selectAddressCmd.key;" command="cmd_selectAddress"/ -->
+
+ <key id="showHideSidebar"/>
+ <!-- Tab/F6 Keys -->
+ <key keycode="VK_TAB" oncommand="SwitchElementFocus(event);" modifiers="control"/>
+ <key keycode="VK_TAB" oncommand="SwitchElementFocus(event);" modifiers="control,shift"/>
+ <key keycode="VK_F6" oncommand="SwitchElementFocus(event);" modifiers="control"/>
+ <key keycode="VK_F6" oncommand="SwitchElementFocus(event);" modifiers="control,shift"/>
+ <key keycode="VK_F6" oncommand="SwitchElementFocus(event);" modifiers="shift"/>
+ <key keycode="VK_F6" oncommand="SwitchElementFocus(event);"/>
+ <key keycode="VK_ESCAPE" oncommand="handleEsc();"/>
+ </keyset>
+ <keyset id="editorKeys"/>
+ <keyset id="composeKeys">
+#ifndef XP_MACOSX
+ <key id="key_renameAttachment" keycode="VK_F2"
+ oncommand="goDoCommand('cmd_renameAttachment');"/>
+#endif
+ </keyset>
+
+ <popupset id="contentAreaContextSet"/>
+
+ <popupset id="editorPopupSet">
+ <menupopup id="sidebarPopup"/>
+
+ <menupopup id="msgComposeAttachmentContext"
+ onpopupshowing="updateEditItems();">
+ <menuitem label="&openAttachment.label;"
+ accesskey="&openAttachment.accesskey;"
+ command="cmd_openAttachment"/>
+ <menuitem accesskey="&deleteAttachment.accesskey;"
+ command="cmd_delete"/>
+ <menuitem label="&renameAttachment.label;"
+ accesskey="&renameAttachment.accesskey;"
+ command="cmd_renameAttachment"/>
+ <menuitem label="&selectAllCmd.label;"
+ accesskey="&selectAllAttachments.accesskey;"
+ command="cmd_selectAll"/>
+ <menuseparator/>
+ <menuitem label="&attachFile.label;"
+ accesskey="&attachFile.accesskey;"
+ command="cmd_attachFile"/>
+ <menuitem label="&attachPage.label;"
+ accesskey="&attachPage.accesskey;"
+ command="cmd_attachPage"/>
+ </menupopup>
+ </popupset>
+
+ <menupopup id="blockedContentOptions" value=""
+ onpopupshowing="onBlockedContentOptionsShowing(event);">
+ </menupopup>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="compose-toolbox"
+ class="toolbox-top"
+ mode="full"
+ defaultmode="full">
+ <toolbar id="compose-toolbar-menubar2"
+ type="menubar"
+ class="chromeclass-menubar"
+ persist="collapsed"
+ grippytooltiptext="&menuBar.tooltip;"
+ customizable="true"
+ defaultset="menubar-items"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu">
+ <toolbaritem id="menubar-items"
+ class="menubar-items"
+ align="center">
+ <menubar id="mail-menubar">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup">
+ <menu id="menu_New">
+ <menupopup id="menu_NewPopup">
+ <menuitem id="menu_newMessage"/>
+ <menuseparator id="menuNewPopupSeparator"/>
+ <menuitem id="menu_newCard"/>
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ <menuitem id="menu_newEditor"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_Attach"
+ label="&attachMenu.label;"
+ accesskey="&attachMenu.accesskey;">
+ <menupopup id="menu_AttachPopup">
+ <menuitem id="menu_AttachFile"
+ label="&attachFileCmd.label;"
+ accesskey="&attachFileCmd.accesskey;"
+ command="cmd_attachFile"/>
+ <menuitem id="menu_AttachPage"
+ label="&attachPageCmd.label;"
+ accesskey="&attachPageCmd.accesskey;"
+ command="cmd_attachPage"/>
+ <menuseparator id="menuAttachPageSeparator"/>
+ <menuitem id="menu_AttachPageVCard"
+ type="checkbox"
+ label="&attachVCardCmd.label;"
+ accesskey="&attachVCardCmd.accesskey;"
+ command="cmd_attachVCard"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_close"/>
+ <menuseparator id="menuFileAfterCloseSeparator"/>
+ <menuitem id="menu_SaveCmd"
+ label="&saveCmd.label;"
+ accesskey="&saveCmd.accesskey;"
+ key="key_save"
+ command="cmd_save"/>
+ <menu id="menu_SaveAsCmd"
+ label="&saveAsCmd.label;"
+ accesskey="&saveAsCmd.accesskey;">
+ <menupopup id="menu_SaveAsCmdPopup">
+ <menuitem id="menu_SaveAsFileCmd"
+ label="&saveAsFileCmd.label;"
+ accesskey="&saveAsFileCmd.accesskey;"
+ command="cmd_saveAsFile"/>
+ <menuseparator id="menuSaveAfterSaveAsSeparator"/>
+ <menuitem id="menu_SaveAsDraftCmd"
+ label="&saveAsDraftCmd.label;"
+ accesskey="&saveAsDraftCmd.accesskey;"
+ command="cmd_saveAsDraft"/>
+ <menuitem id="menu_SaveAsTemplateCmd"
+ label="&saveAsTemplateCmd.label;"
+ accesskey="&saveAsTemplateCmd.accesskey;"
+ command="cmd_saveAsTemplate"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="menuFileAfterSaveAsSeparator"/>
+ <menuitem id="menu_sendNow"
+ label="&sendNowCmd.label;"
+ accesskey="&sendNowCmd.accesskey;"
+ key="key_send" command="cmd_sendNow"/>
+ <menuitem id="menu_sendLater"
+ label="&sendLaterCmd.label;"
+ accesskey="&sendLaterCmd.accesskey;"
+ key="key_sendLater"
+ command="cmd_sendLater"/>
+ <menuseparator id="menuFileAfterSendLaterSeparator"/>
+ <menuitem id="menu_printSetup"/>
+ <menuitem id="menu_printPreview"/>
+ <menuitem id="menu_print"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_Edit"/>
+ <menu id="menu_View">
+ <menupopup id="menu_View_Popup">
+ <menu id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup"
+ onpopupshowing="onViewToolbarsPopupShowing(event)"
+ oncommand="onViewToolbarCommand(event);">
+ <menuitem id="menu_showTaskbar"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="viewMenuBeforeSecurityStatusSeparator"/>
+ <menuitem id="menu_viewSecurityStatus"
+ label="&menu_viewSecurityStatus.label;"
+ accesskey="&menu_viewSecurityStatus.accesskey;"
+ command="cmd_viewSecurityStatus"/>
+ </menupopup>
+ </menu>
+
+ <menu id="insertMenu"
+ command="cmd_renderedHTMLEnabler"/>
+
+ <menu id="formatMenu"
+ label="&formatMenu.label;"
+ accesskey="&formatMenu.accesskey;"
+ command="cmd_renderedHTMLEnabler">
+ <menupopup id="formatMenuPopup">
+ <menu id="tableMenu"/>
+ <menuseparator id="menuFormatAfterTableSeparator"/>
+ <menuitem id="objectProperties"/>
+ <menuitem id="colorsAndBackground"/>
+ </menupopup>
+ </menu>
+
+ <menu id="optionsMenu"
+ label="&optionsMenu.label;"
+ accesskey="&optionsMenu.accesskey;">
+ <menupopup id="optionsMenuPopup"
+ onpopupshowing="setSecuritySettings(1);">
+ <menuitem id="menu_selectAddress"
+ label="&selectAddressCmd.label;"
+ accesskey="&selectAddressCmd.accesskey;"
+ command="cmd_selectAddress"/>
+ <menuitem id="menu_quoteMessage"
+ label="&quoteCmd.label;"
+ accesskey="&quoteCmd.accesskey;"
+ command="cmd_quoteMessage"/>
+ <menuseparator id="menuOptionsAfterQuoteSeparator"/>
+ <menuitem id="returnReceiptMenu"
+ type="checkbox"
+ label="&returnReceiptMenu.label;"
+ accesskey="&returnReceiptMenu.accesskey;"
+ checked="false"
+ oncommand="ToggleReturnReceipt(event.target)"/>
+ <menuitem id="dsnMenu"
+ type="checkbox"
+ label="&dsnMenu.label;"
+ accesskey="&dsnMenu.accesskey;"
+ checked="false"
+ oncommand="ToggleDSN(event.target);"/>
+ <menu id="outputFormatMenu"
+ label="&outputFormatMenu.label;"
+ accesskey="&outputFormatMenu.accesskey;"
+ command="cmd_outputFormat">
+ <menupopup id="outputFormatMenuPopup">
+ <menuitem id="format_auto" type="radio" name="output_format" label="&autoFormatCmd.label;" accesskey="&autoFormatCmd.accesskey;" checked="true"/>
+ <menuitem id="format_plain" type="radio" name="output_format" label="&plainTextFormatCmd.label;" accesskey="&plainTextFormatCmd.accesskey;"/>
+ <menuitem id="format_html" type="radio" name="output_format" label="&htmlFormatCmd.label;" accesskey="&htmlFormatCmd.accesskey;"/>
+ <menuitem id="format_both" type="radio" name="output_format" label="&bothFormatCmd.label;" accesskey="&bothFormatCmd.accesskey;"/>
+ </menupopup>
+ </menu>
+ <menu id="priorityMenu"
+ label="&priorityMenu.label;"
+ accesskey="&priorityMenu.accesskey;"
+ oncommand="PriorityMenuSelect(event.target);">
+ <menupopup id="priorityMenuPopup"
+ onpopupshowing="updatePriorityMenu(this);">
+ <menuitem id="priority_highest" type="radio" name="priority" label="&highestPriorityCmd.label;" accesskey="&highestPriorityCmd.accesskey;" value="Highest"/>
+ <menuitem id="priority_high" type="radio" name="priority" label="&highPriorityCmd.label;" accesskey="&highPriorityCmd.accesskey;" value="High"/>
+ <menuitem id="priority_normal" type="radio" name="priority" label="&normalPriorityCmd.label;" accesskey="&normalPriorityCmd.accesskey;" value="Normal"/>
+ <menuitem id="priority_low" type="radio" name="priority" label="&lowPriorityCmd.label;" accesskey="&lowPriorityCmd.accesskey;" value="Low"/>
+ <menuitem id="priority_lowest" type="radio" name="priority" label="&lowestPriorityCmd.label;" accesskey="&lowestPriorityCmd.accesskey;" value="Lowest"/>
+ </menupopup>
+ </menu>
+ <menu id="charsetMenu"
+ onpopupshowing="UpdateCharsetMenu(gMsgCompose.compFields.characterSet, this);"
+ oncommand="ComposeSetCharacterSet(event);">
+ <menupopup id="charsetPopup" detectors="false"/>
+ </menu>
+ <menu id="fccMenu"
+ label="&fileCarbonCopyCmd.label;"
+ accesskey="&fileCarbonCopyCmd.accesskey;"
+ oncommand="MessageFcc(event.target._folder);">
+ <menupopup id="fccMenuPopup"
+ type="folder"
+ mode="filing"
+ showFileHereLabel="true"
+ fileHereLabel="&fileHereMenu.label;"/>
+ </menu>
+ <menuseparator id="smimeOptionsSeparator"/>
+ <menuitem id="menu_securityEncryptRequire1"
+ type="checkbox"
+ label="&menu_securityEncryptRequire.label;"
+ accesskey="&menu_securityEncryptRequire.accesskey;"
+ oncommand="toggleEncryptMessage();"/>
+ <menuitem id="menu_securitySign1"
+ type="checkbox"
+ label="&menu_securitySign.label;"
+ accesskey="&menu_securitySign.accesskey;"
+ oncommand="toggleSignMessage();"/>
+ </menupopup>
+ </menu>
+ <menu id="tasksMenu"/>
+ <menu id="windowMenu"/>
+ <menu id="menu_Help"/>
+ </menubar>
+ </toolbaritem>
+ </toolbar>
+
+ <toolbar id="composeToolbar"
+ class="toolbar-primary chromeclass-toolbar"
+ persist="collapsed"
+ grippytooltiptext="&mailToolbar.tooltip;"
+ toolbarname="&showComposeToolbarCmd.label;"
+ accesskey="&showComposeToolbarCmd.accesskey;"
+ customizable="true"
+ defaultset="button-send,separator,button-address,button-attach,spellingButton,button-security,separator,button-save,spring,throbber-box"
+ context="toolbar-context-menu">
+ <toolbarbutton id="button-send"
+ class="toolbarbutton-1"
+ label="&sendButton.label;"
+ tooltiptext="&sendButton.tooltip;"
+ now_label="&sendButton.label;"
+ now_tooltiptext="&sendButton.tooltip;"
+ later_label="&sendLaterCmd.label;"
+ later_tooltiptext="&sendlaterButton.tooltip;"
+ removable="true"
+ command="cmd_sendButton">
+ <observes element="Communicator:WorkMode"
+ attribute="offline"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="button-address"
+ class="toolbarbutton-1"
+ label="&addressButton.label;"
+ tooltiptext="&addressButton.tooltip;"
+ removable="true"
+ command="cmd_selectAddress"/>
+
+ <toolbarbutton id="button-attach"
+ type="menu-button"
+ class="toolbarbutton-1"
+ label="&attachButton.label;"
+ tooltiptext="&attachButton.tooltip;"
+ removable="true"
+ command="cmd_attachFile">
+ <menupopup id="button-attachPopup">
+ <menuitem id="button-attachFile"
+ label="&attachFileCmd.label;"
+ accesskey="&attachFileCmd.accesskey;"
+ command="cmd_attachFile"/>
+ <menuitem id="button-attachPage"
+ label="&attachPageCmd.label;"
+ accesskey="&attachPageCmd.accesskey;"
+ command="cmd_attachPage"/>
+ <menuseparator id="buttonAttachAfterPageSeparator"/>
+ <menuitem id="button-attachVCard"
+ type="checkbox"
+ label="&attachVCardCmd.label;"
+ accesskey="&attachVCardCmd.accesskey;"
+ command="cmd_attachVCard"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbarbutton id="spellingButton"
+ type="menu-button"
+ class="toolbarbutton-1"
+ label="&spellingButton.label;"
+ removable="true"
+ command="cmd_spelling">
+ <!-- this popup gets dynamically generated -->
+ <menupopup id="languageMenuList"
+ oncommand="ChangeLanguage(event);"
+ onpopupshowing="OnShowDictionaryMenu(event.target);"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="button-save"
+ type="menu-button"
+ class="toolbarbutton-1"
+ label="&saveButton.label;"
+ tooltiptext="&saveButton.tooltip;"
+ removable="true"
+ command="cmd_save">
+ <menupopup id="button-savePopup">
+ <menuitem id="button-saveAsFile"
+ label="&saveAsFileCmd.label;"
+ accesskey="&saveAsFileCmd.accesskey;"
+ command="cmd_saveAsFile"/>
+ <menuseparator id="buttonSaveAfterFileSeparator"/>
+ <menuitem id="button-saveAsDraft"
+ label="&saveAsDraftCmd.label;"
+ accesskey="&saveAsDraftCmd.accesskey;"
+ command="cmd_saveAsDraft"/>
+ <menuitem id="button-saveAsTemplate"
+ label="&saveAsTemplateCmd.label;"
+ accesskey="&saveAsTemplateCmd.accesskey;"
+ command="cmd_saveAsTemplate"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbaritem id="throbber-box"/>
+ </toolbar>
+
+ <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+
+ <toolbar id="MsgHeadersToolbar"
+ persist="collapsed"
+ flex="1"
+ grippytooltiptext="&addressBar.tooltip;"
+ nowindowdrag="true">
+ <hbox id="msgheaderstoolbar-box" flex="1">
+ <vbox id="addresses-box" flex="1">
+ <hbox align="center">
+ <label value="&fromAddr.label;" accesskey="&fromAddr.accesskey;" control="msgIdentity"/>
+ <menulist id="msgIdentity"
+ editable="true"
+ disableautoselect="true"
+ flex="1"
+ oncommand="LoadIdentity(false);">
+ <menupopup id="msgIdentityPopup"/>
+ </menulist>
+ </hbox>
+ <!-- Addressing Widget -->
+ <listbox id="addressingWidget" flex="1"
+ seltype="multiple" rows="4"
+ onkeydown="awKeyDown(event, this);"
+ onclick="awClickEmptySpace(event.originalTarget, true);"
+ ondragover="DragAddressOverTargetControl(event);"
+ ondrop="DropOnAddressingTarget(event, true);">
+
+ <listcols>
+ <listcol id="typecol-addressingWidget"/>
+ <listcol id="textcol-addressingWidget" flex="1"/>
+ </listcols>
+
+ <listitem class="addressingWidgetItem" allowevents="true">
+ <listcell class="addressingWidgetCell" align="stretch">
+ <menulist id="addressCol1#1" disableonsend="true"
+ class="aw-menulist menulist-compact" flex="1"
+ onkeypress="awMenulistKeyPress(event, this);"
+ oncommand="onAddressColCommand(this.id);">
+ <menupopup>
+ <menuitem value="addr_to" label="&toAddr.label;"/>
+ <menuitem value="addr_cc" label="&ccAddr.label;"/>
+ <menuitem value="addr_bcc" label="&bccAddr.label;"/>
+ <menuitem value="addr_reply" label="&replyAddr.label;"/>
+ <menuitem value="addr_newsgroups"
+ label="&newsgroupsAddr.label;"/>
+ <menuitem value="addr_followup"
+ label="&followupAddr.label;"/>
+ </menupopup>
+ </menulist>
+ </listcell>
+
+ <listcell class="addressingWidgetCell">
+ <textbox id="addressCol2#1"
+ class="plain textbox-addressingWidget uri-element"
+ aria-labelledby="addressCol1#1"
+ type="autocomplete" flex="1" maxrows="4"
+ newlines="replacewithcommas"
+ autocompletesearch="mydomain addrbook ldap news"
+ timeout="300" autocompletesearchparam="{}"
+ completedefaultindex="true" forcecomplete="true"
+ minresultsforpopup="2" ignoreblurwhilesearching="true"
+ ontextentered="awRecipientTextCommand(eventParam, this);"
+ onerrorcommand="awRecipientErrorCommand(eventParam, this);"
+ onchange="onRecipientsChanged();"
+ oninput="onRecipientsChanged();"
+ onkeypress="awRecipientKeyPress(event, this);"
+ onkeydown="awRecipientKeyDown(event, this);"
+ disableonsend="true">
+ <image class="person-icon"
+ onclick="this.parentNode.select();"/>
+ </textbox>
+ </listcell>
+ </listitem>
+ </listbox>
+ <hbox align="center">
+ <label value="&subject.label;" accesskey="&subject.accesskey;" control="msgSubject"/>
+ <textbox id="msgSubject" flex="1" class="toolbar" disableonsend="true" spellcheck="true"
+ oninput="gContentChanged=true;SetComposeWindowTitle();"
+ onkeypress="subjectKeyPress(event);" />
+ </hbox>
+ </vbox>
+ <splitter id="attachmentbucket-sizer" collapse="after"/>
+ <vbox id="attachments-box">
+ <label id="attachmentBucketText" value="&attachments.label;" crop="right"
+ accesskey="&attachments.accesskey;" control="attachmentBucket"/>
+ <listbox id="attachmentBucket"
+ seltype="multiple"
+ flex="1"
+ rows="4"
+ tabindex="-1"
+ context="msgComposeAttachmentContext"
+ disableoncustomize="true"
+ onkeypress="if (event.keyCode == 8 || event.keyCode == 46) RemoveSelectedAttachment();"
+ onclick="AttachmentBucketClicked(event);"
+ ondragover="attachmentBucketObserver.onDragOver(event);"
+ ondrop="attachmentBucketObserver.onDrop(event);"
+ ondragexit="attachmentBucketObserver.onDragExit(event);"/>
+ </vbox>
+ </hbox>
+ </toolbar>
+
+ <!-- These toolbar items get filled out from the editorOverlay -->
+ <toolbar id="FormatToolbar"
+ class="chromeclass-toolbar"
+ persist="collapsed"
+ grippytooltiptext="&formatToolbar.tooltip;"
+ toolbarname="&showFormatToolbarCmd.label;"
+ accesskey="&showFormatToolbarCmd.accesskey;"
+ customizable="true"
+ defaultset="paragraph-select-container,font-face-select-container,color-buttons-container,DecreaseFontSizeButton,IncreaseFontSizeButton,separator,boldButton,italicButton,underlineButton,separator,ulButton,olButton,outdentButton,indentButton,separator,AlignPopupButton,InsertPopupButton,smileButtonMenu"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu"
+ nowindowdrag="true">
+ <toolbaritem id="paragraph-select-container"/>
+ <toolbaritem id="font-face-select-container"/>
+ <toolbaritem id="color-buttons-container"
+ disableoncustomize="true"/>
+ <toolbarbutton id="DecreaseFontSizeButton"/>
+ <toolbarbutton id="IncreaseFontSizeButton"/>
+ <toolbarbutton id="boldButton"/>
+ <toolbarbutton id="italicButton"/>
+ <toolbarbutton id="underlineButton"/>
+ <toolbarbutton id="ulButton"/>
+ <toolbarbutton id="olButton"/>
+ <toolbarbutton id="outdentButton"/>
+ <toolbarbutton id="indentButton"/>
+ <toolbarbutton id="AlignPopupButton"/>
+ <toolbarbutton id="InsertPopupButton"/>
+ <toolbarbutton id="smileButtonMenu"/>
+ </toolbar>
+
+ <toolbarpalette id="MsgComposeToolbarPalette">
+ <toolbarbutton id="print-button"
+ label="&printButton.label;"
+ tooltiptext="&printButton.tooltip;"/>
+ <toolbarbutton id="button-security"
+ type="menu-button"
+ class="toolbarbutton-1"
+ label="&securityButton.label;"
+ tooltiptext="&securityButton.tooltip;"
+ oncommand="doSecurityButton();">
+ <menupopup onpopupshowing="setSecuritySettings(2);">
+ <menuitem id="menu_securityEncryptRequire2"
+ type="checkbox"
+ label="&menu_securityEncryptRequire.label;"
+ accesskey="&menu_securityEncryptRequire.accesskey;"
+ oncommand="setNextCommand('encryptMessage');"/>
+ <menuitem id="menu_securitySign2"
+ type="checkbox"
+ label="&menu_securitySign.label;"
+ accesskey="&menu_securitySign.accesskey;"
+ oncommand="setNextCommand('signMessage');"/>
+ <menuseparator id="smimeToolbarButtonSeparator"/>
+ <menuitem id="menu_securityStatus2"
+ label="&menu_securityStatus.label;"
+ accesskey="&menu_securityStatus.accesskey;"
+ oncommand="setNextCommand('show');"/>
+ </menupopup>
+ </toolbarbutton>
+ </toolbarpalette>
+
+ </toolbox>
+
+ <splitter id="compose-toolbar-sizer"
+ resizeafter="grow"
+ onmousedown="awSizerListen();"
+ oncommand="awSizerResized(this);">
+ <observes element="MsgHeadersToolbar" attribute="collapsed"/>
+ </splitter>
+
+ <!-- sidebar/toolbar/content/status -->
+ <hbox id="sidebar-parent" flex="1">
+ <!-- From sidebarOverlay.xul -->
+ <vbox id="sidebar-box" class="chromeclass-extrachrome" hidden="true"/>
+ <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
+
+ <!-- The mail message body frame -->
+ <vbox id="appcontent" flex="1">
+ <findbar id="FindToolbar" browserid="content-frame"/>
+ <editor id="content-frame"
+ type="content"
+ primary="true"
+ src="about:blank"
+ name="browser.message.body"
+ minheight="100"
+ flex="1"
+ ondblclick="EditorDblClick(event);"
+ context="contentAreaContextMenu"/>
+ </vbox>
+ </hbox>
+
+ <hbox>
+ <notificationbox id="attachmentNotificationBox"
+ flex="1"
+ notificationside="bottom"/>
+ </hbox>
+
+ <statusbar id="status-bar"
+ class="chromeclass-status">
+ <statusbarpanel id="component-bar"/>
+ <statusbarpanel id="statusText"
+ flex="1"/>
+ <statusbarpanel id="statusbar-progresspanel"
+ class="statusbarpanel-progress"
+ collapsed="true">
+ <progressmeter id="compose-progressmeter"
+ class="progressmeter-statusbar"
+ mode="normal"
+ value="0"/>
+ </statusbarpanel>
+ <statusbarpanel id="signing-status"
+ class="statusbarpanel-iconic"
+ collapsed="true"
+ oncommand="showMessageComposeSecurityStatus();"/>
+ <statusbarpanel id="encryption-status"
+ class="statusbarpanel-iconic"
+ collapsed="true"
+ oncommand="showMessageComposeSecurityStatus();"/>
+ <statusbarpanel id="offline-status"
+ class="statusbarpanel-iconic"
+ checkfunc="MailCheckBeforeOfflineChange();"/>
+ </statusbar>
+
+</window>
diff --git a/comm/suite/mailnews/components/compose/content/msgComposeContextOverlay.xul b/comm/suite/mailnews/components/compose/content/msgComposeContextOverlay.xul
new file mode 100644
index 0000000000..f3558873d2
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/msgComposeContextOverlay.xul
@@ -0,0 +1,23 @@
+<?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/. -->
+
+<overlay id="msgComposeContextOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <menupopup id="contentAreaContextMenu"
+ onpopupshowing="return event.target != this ||
+ openEditorContextMenu(this);">
+ <!-- Hide the menuitems by default so they do not to show up
+ in the sidebar context menu. -->
+ <menuitem id="context-pasteNoFormatting"
+ insertafter="context-paste"
+ hidden="true"
+ command="cmd_pasteNoFormatting"/>
+ <menuitem id="context-pasteQuote"
+ insertafter="context-pasteNoFormatting"
+ hidden="true"
+ command="cmd_pasteQuote"/>
+ </menupopup>
+</overlay>
diff --git a/comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.js b/comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.js
new file mode 100644
index 0000000000..f67d919f63
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.js
@@ -0,0 +1,30 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function Startup() {
+ let value = document.getElementById("mail.compose.autosave").value;
+ EnableElementById("autoSaveInterval", value, false);
+}
+
+function EnableMailComposeAutosaveInterval(aValue) {
+ let focus = (document.getElementById("autoSave") == document.commandDispatcher.focusedElement);
+ EnableElementById("autoSaveInterval", aValue, focus);
+}
+
+function PopulateFonts() {
+ var fontsList = document.getElementById("fontSelect");
+ try {
+ var enumerator = Cc["@mozilla.org/gfx/fontenumerator;1"]
+ .getService(Ci.nsIFontEnumerator);
+ var localFonts = enumerator.EnumerateAllFonts();
+ for (let font of localFonts)
+ if (font != "serif" && font != "sans-serif" && font != "monospace")
+ fontsList.appendItem(font, font);
+ } catch (ex) { }
+
+ // Select the item after the list is completely generated.
+ document.getElementById(fontsList.getAttribute("preference"))
+ .setElementValue(fontsList);
+}
diff --git a/comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.xul b/comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.xul
new file mode 100644
index 0000000000..c6f3b4fac8
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/prefs/pref-composing_messages.xul
@@ -0,0 +1,212 @@
+<?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 % pref-composing_messagesDTD SYSTEM "chrome://messenger/locale/messengercompose/pref-composing_messages.dtd">
+%pref-composing_messagesDTD;
+<!ENTITY % editorOverlayDTD SYSTEM "chrome://editor/locale/editorOverlay.dtd">
+%editorOverlayDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="composing_messages_pane"
+ label="&pref.composing.messages.title;"
+ script="chrome://messenger/content/messengercompose/pref-composing_messages.js"
+ onpaneload="this.PopulateFonts();">
+
+ <preferences id="composing_messages_preferences">
+ <preference id="mail.forward_message_mode"
+ name="mail.forward_message_mode"
+ type="int"/>
+ <preference id="mail.reply_quote_inline"
+ name="mail.reply_quote_inline"
+ type="bool"/>
+ <preference id="mail.compose.autosave"
+ name="mail.compose.autosave"
+ type="bool"
+ onchange="EnableMailComposeAutosaveInterval(this.value);"/>
+ <preference id="mail.compose.autosaveinterval"
+ name="mail.compose.autosaveinterval"
+ type="int"/>
+ <preference id="mail.warn_on_send_accel_key"
+ name="mail.warn_on_send_accel_key"
+ type="bool"/>
+ <preference id="mailnews.wraplength"
+ name="mailnews.wraplength"
+ type="int"/>
+ <preference id="msgcompose.font_face"
+ name="msgcompose.font_face"
+ type="string"/>
+ <preference id="msgcompose.font_size"
+ name="msgcompose.font_size"
+ type="string"/>
+ <preference id="msgcompose.text_color"
+ name="msgcompose.text_color"
+ type="string"/>
+ <preference id="msgcompose.background_color"
+ name="msgcompose.background_color"
+ type="string"/>
+ <preference id="mailnews.reply_header_type"
+ name="mailnews.reply_header_type"
+ type="int"/>
+ <preference id="mail.compose.default_to_paragraph"
+ name="mail.compose.default_to_paragraph"
+ type="bool"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&generalComposing.label;"/>
+
+ <radiogroup id="forwardMessageMode"
+ orient="horizontal"
+ align="center"
+ preference="mail.forward_message_mode">
+ <label value="&forwardMsg.label;" control="forwardMessageMode"/>
+ <radio value="2"
+ label="&inline.label;"
+ accesskey="&inline.accesskey;"/>
+ <radio value="0"
+ label="&asAttachment.label;"
+ accesskey="&asAttachment.accesskey;"/>
+ </radiogroup>
+
+ <checkbox id="replyQuoteInline" label="&replyQuoteInline.label;"
+ preference="mail.reply_quote_inline"
+ accesskey="&replyQuoteInline.accesskey;"/>
+
+ <hbox align="center">
+ <checkbox id="autoSave" label="&autoSave.label;"
+ preference="mail.compose.autosave"
+ accesskey="&autoSave.accesskey;"
+ aria-labelledby="autoSave autoSaveInterval autoSaveEnd"/>
+ <textbox id="autoSaveInterval"
+ type="number"
+ min="1"
+ max="99"
+ size="2"
+ preference="mail.compose.autosaveinterval"
+ aria-labelledby="autoSave autoSaveInterval autoSaveEnd"/>
+ <label id="autoSaveEnd" value="&autoSaveEnd.label;"/>
+ </hbox>
+
+ <checkbox id="mailWarnOnSendAccelKey"
+ label="&warnOnSendAccelKey.label;"
+ accesskey="&warnOnSendAccelKey.accesskey;"
+ preference="mail.warn_on_send_accel_key"/>
+
+ <hbox align="center">
+ <label id="wrapOutLabel"
+ value="&wrapOutMsg.label;"
+ accesskey="&wrapOutMsg.accesskey;"
+ control="wrapLength"/>
+ <textbox id="wrapLength"
+ type="number"
+ min="0"
+ max="999"
+ size="3"
+ preference="mailnews.wraplength"
+ aria-labelledby="wrapOutLabel wrapLength wrapOutEnd"/>
+ <label id="wrapOutEnd" value="&char.label;"/>
+ </hbox>
+ <hbox align="center">
+ <label id="selectHeaderType"
+ value="&selectHeaderType.label;"
+ accesskey="&selectHeaderType.accesskey;"
+ control="mailNewsReplyList"/>
+ <menulist id="mailNewsReplyList"
+ preference="mailnews.reply_header_type">
+ <menupopup>
+ <menuitem value="0"
+ label="&noReplyOption.label;"/>
+ <menuitem value="1"
+ label="&authorWroteOption.label;"/>
+ <menuitem value="2"
+ label="&onDateAuthorWroteOption.label;"/>
+ <menuitem value="3"
+ label="&authorWroteOnDateOption.label;"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ </groupbox>
+
+ <!-- Composing Mail -->
+
+ <groupbox align="start">
+ <caption label="&defaultMessagesHeader.label;"/>
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+
+ <rows>
+ <row align="center">
+ <label value="&font.label;"
+ accesskey="&font.accesskey;"
+ control="fontSelect"/>
+ <menulist id="fontSelect" preference="msgcompose.font_face">
+ <menupopup>
+ <menuitem value=""
+ label="&fontVarWidth.label;"/>
+ <menuitem value="tt"
+ label="&fontFixedWidth.label;"/>
+ <menuseparator/>
+ <menuitem value="Helvetica, Arial, sans-serif"
+ label="&fontHelvetica.label;"/>
+ <menuitem value="Times New Roman, Times, serif"
+ label="&fontTimes.label;"/>
+ <menuitem value="Courier New, Courier, monospace"
+ label="&fontCourier.label;"/>
+ <menuseparator/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <label value="&size.label;"
+ accesskey="&size.accesskey;"
+ control="fontSizeSelect"/>
+ <hbox align="center">
+ <menulist id="fontSizeSelect" preference="msgcompose.font_size">
+ <menupopup>
+ <menuitem value="x-small" label="&size-tinyCmd.label;"/>
+ <menuitem value="small" label="&size-smallCmd.label;"/>
+ <menuitem value="medium" label="&size-mediumCmd.label;"/>
+ <menuitem value="large" label="&size-largeCmd.label;"/>
+ <menuitem value="x-large" label="&size-extraLargeCmd.label;"/>
+ <menuitem value="xx-large" label="&size-hugeCmd.label;"/>
+ </menupopup>
+ </menulist>
+ <label value="&fontColor.label;"
+ accesskey="&fontColor.accesskey;"
+ control="msgComposeTextColor"/>
+ <colorpicker id="msgComposeTextColor"
+ type="button"
+ preference="msgcompose.text_color"/>
+ <label value="&bgColor.label;"
+ accesskey="&bgColor.accesskey;"
+ control="msgComposeBackgroundColor"/>
+ <colorpicker id="msgComposeBackgroundColor"
+ type="button"
+ preference="msgcompose.background_color"/>
+ </hbox>
+ </row>
+ </rows>
+ </grid>
+ <separator class="thin"/>
+ <description>&defaultCompose.label;</description>
+ <radiogroup id="defaultCompose"
+ class="indent"
+ preference="mail.compose.default_to_paragraph">
+ <radio value="false"
+ label="&defaultBodyText.label;"
+ accesskey="&defaultBodyText.accesskey;"/>
+ <radio value="true"
+ label="&defaultParagraph.label;"
+ accesskey="&defaultParagraph.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/compose/content/prefs/pref-formatting.js b/comm/suite/mailnews/components/compose/content/prefs/pref-formatting.js
new file mode 100644
index 0000000000..b5c31d424d
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/prefs/pref-formatting.js
@@ -0,0 +1,151 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gListbox;
+var gPref;
+var gError;
+
+function Startup()
+{
+ // Store some useful elements in globals.
+ gListbox =
+ {
+ html: document.getElementById("html_domains"),
+ plaintext: document.getElementById("plaintext_domains")
+ };
+ gPref =
+ {
+ html_domains: document.getElementById("mailnews.html_domains"),
+ plaintext_domains: document.getElementById("mailnews.plaintext_domains")
+ };
+ gError = document.getElementById("formatting_error_msg");
+
+ // Make it easier to access the pref pane from onsync.
+ gListbox.html.pane = this;
+ gListbox.plaintext.pane = this;
+}
+
+function AddDomain(aType)
+{
+ var domains = null;
+ var result = {value: null};
+ if (Services.prompt.prompt(window, gListbox[aType].getAttribute("title"),
+ gListbox[aType].getAttribute("msg"), result,
+ null, {value: 0}))
+ domains = result.value.replace(/ /g, "").split(",");
+
+ if (domains)
+ {
+ var added = false;
+ var removed = false;
+ var listbox = gListbox[aType];
+ var other = aType == "html" ? gListbox.plaintext : gListbox.html;
+ for (var i = 0; i < domains.length; i++)
+ {
+ var domainName = TidyDomainName(domains[i], true);
+ if (domainName)
+ {
+ if (!DomainFirstMatch(listbox, domainName))
+ {
+ var match = DomainFirstMatch(other, domainName);
+ if (match)
+ {
+ match.remove();
+ removed = true;
+ }
+ listbox.appendItem(domainName);
+ added = true;
+ }
+ }
+ }
+ if (added)
+ listbox.doCommand();
+ if (removed)
+ other.doCommand();
+ }
+}
+
+function TidyDomainName(aDomain, aWarn)
+{
+ // See if it is an email address and if so take just the domain part.
+ aDomain = aDomain.replace(/.*@/, "");
+
+ // See if it is a valid domain otherwise return null.
+ if (!/.\../.test(aDomain))
+ {
+ if (aWarn)
+ {
+ var errorMsg = gError.getAttribute("inverr").replace(/@string@/, aDomain);
+ Services.prompt.alert(window, gError.getAttribute("title"), errorMsg);
+ }
+ return null;
+ }
+
+ // Finally make sure the domain is in lowercase.
+ return aDomain.toLowerCase();
+}
+
+function DomainFirstMatch(aListbox, aDomain)
+{
+ return aListbox.getElementsByAttribute("label", aDomain).item(0);
+}
+
+function RemoveDomains(aType, aEvent)
+{
+ if (aEvent && aEvent.keyCode != KeyEvent.DOM_VK_DELETE &&
+ aEvent.keyCode != KeyEvent.DOM_VK_BACK_SPACE)
+ return;
+
+ var nextNode = null;
+ var listbox = gListbox[aType];
+
+ while (listbox.selectedItem)
+ {
+ var selectedNode = listbox.selectedItem;
+ nextNode = selectedNode.nextSibling || selectedNode.previousSibling;
+ selectedNode.remove();
+ }
+
+ if (nextNode)
+ listbox.selectItem(nextNode);
+
+ listbox.doCommand();
+}
+
+function ReadDomains(aListbox)
+{
+ var arrayOfPrefs = gPref[aListbox.id].value.replace(/ /g, "").split(",");
+ if (arrayOfPrefs)
+ {
+ var i;
+ // Check all the existing items, remove any that are not needed and
+ // make sure we do not duplicate any by removing from pref array.
+ var domains = aListbox.getElementsByAttribute("label", "*");
+ if (domains)
+ {
+ for (i = domains.length; --i >= 0; )
+ {
+ var domain = domains[i];
+ var index = arrayOfPrefs.indexOf(domain.label);
+ if (index > -1)
+ arrayOfPrefs.splice(index, 1);
+ else
+ domain.remove();
+ }
+ }
+ for (i = 0; i < arrayOfPrefs.length; i++)
+ {
+ var str = TidyDomainName(arrayOfPrefs[i], false);
+ if (str)
+ aListbox.appendItem(str);
+ }
+ }
+}
+
+function WriteDomains(aListbox)
+{
+ var domains = aListbox.getElementsByAttribute("label", "*");
+ return Array.from(domains, e => e.label).join(",");
+}
diff --git a/comm/suite/mailnews/components/compose/content/prefs/pref-formatting.xul b/comm/suite/mailnews/components/compose/content/prefs/pref-formatting.xul
new file mode 100644
index 0000000000..0167a0990f
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/content/prefs/pref-formatting.xul
@@ -0,0 +1,120 @@
+<?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 SYSTEM "chrome://messenger/locale/messengercompose/pref-formatting.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="formatting_pane"
+ label="&pref.formatting.title;"
+ script="chrome://messenger/content/messengercompose/pref-formatting.js">
+ <preferences id="formatting_preferences">
+ <preference id="mail.default_html_action"
+ name="mail.default_html_action"
+ type="int"/>
+ <preference id="mailnews.html_domains"
+ name="mailnews.html_domains"
+ type="string"/>
+ <preference id="mailnews.plaintext_domains"
+ name="mailnews.plaintext_domains"
+ type="string"/>
+ <preference id="mailnews.sendformat.auto_downgrade"
+ name="mailnews.sendformat.auto_downgrade"
+ type="bool"/>
+ </preferences>
+
+ <data id="formatting_error_msg"
+ title="&domainnameError.title;"
+ inverr="&invalidEntryError.label;"/>
+
+ <description>&sendMaildesc.label;</description>
+
+ <radiogroup id="mailDefaultHTMLAction"
+ preference="mail.default_html_action">
+ <radio value="0"
+ label="&askMe.label;"
+ accesskey="&askMe.accesskey;"/>
+ <radio value="1"
+ label="&convertPlain2.label;"
+ accesskey="&convertPlain2.accesskey;"/>
+ <radio value="2"
+ label="&sendHTML2.label;"
+ accesskey="&sendHTML2.accesskey;"/>
+ <radio value="3"
+ label="&sendBoth2.label;"
+ accesskey="&sendBoth2.accesskey;"/>
+ </radiogroup>
+
+ <groupbox flex="1">
+ <caption label="&domain.title;"/>
+
+ <description>&domaindesc.label;</description>
+
+ <hbox flex="1">
+ <vbox flex="1">
+ <label value="&HTMLdomaintitle.label;"
+ accesskey="&HTMLdomaintitle.accesskey;"
+ control="html_domains"/>
+ <hbox flex="1">
+ <listbox id="html_domains"
+ title="&add.htmltitle;"
+ msg="&add.htmldomain;"
+ flex="1"
+ seltype="multiple"
+ preference="mailnews.html_domains"
+ onsyncfrompreference="return this.pane.ReadDomains(this);"
+ onsynctopreference="return this.pane.WriteDomains(this);"
+ onkeypress="RemoveDomains('html', event);"/>
+ <vbox>
+ <button label="&AddButton.label;"
+ accesskey="&AddHtmlDomain.accesskey;"
+ oncommand="AddDomain('html');">
+ <observes element="html_domains" attribute="disabled"/>
+ </button>
+ <button label="&DeleteButton.label;"
+ accesskey="&DeleteHtmlDomain.accesskey;"
+ oncommand="RemoveDomains('html', null);">
+ <observes element="html_domains" attribute="disabled"/>
+ </button>
+ </vbox>
+ </hbox>
+ </vbox>
+ <vbox flex="1">
+ <label value="&PlainTexttitle.label;"
+ accesskey="&PlainTexttitle.accesskey;"
+ control="plaintext_domains"/>
+ <hbox flex="1">
+ <listbox id="plaintext_domains"
+ title="&add.plaintexttitle;"
+ msg="&add.plaintextdomain;"
+ flex="1"
+ seltype="multiple"
+ preference="mailnews.plaintext_domains"
+ onsyncfrompreference="return this.pane.ReadDomains(this);"
+ onsynctopreference="return this.pane.WriteDomains(this);"
+ onkeypress="RemoveDomains('plaintext', event);"/>
+ <vbox>
+ <button label="&AddButton.label;"
+ accesskey="&AddPlainText.accesskey;"
+ oncommand="AddDomain('plaintext');">
+ <observes element="plaintext_domains" attribute="disabled"/>
+ </button>
+ <button label="&DeleteButton.label;"
+ accesskey="&DeletePlainText.accesskey;"
+ oncommand="RemoveDomains('plaintext', null);">
+ <observes element="plaintext_domains" attribute="disabled"/>
+ </button>
+ </vbox>
+ </hbox>
+ </vbox>
+ </hbox>
+ </groupbox>
+
+ <checkbox id="autoDowngrade"
+ label="&autoDowngrade.label;"
+ accesskey="&autoDowngrade.accesskey;"
+ preference="mailnews.sendformat.auto_downgrade"/>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/compose/jar.mn b/comm/suite/mailnews/components/compose/jar.mn
new file mode 100644
index 0000000000..c9465fa8d7
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/jar.mn
@@ -0,0 +1,14 @@
+# 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/.
+
+messenger.jar:
+ content/messenger/messengercompose/pref-composing_messages.xul (content/prefs/pref-composing_messages.xul)
+ content/messenger/messengercompose/pref-composing_messages.js (content/prefs/pref-composing_messages.js)
+ content/messenger/messengercompose/pref-formatting.xul (content/prefs/pref-formatting.xul)
+ content/messenger/messengercompose/pref-formatting.js (content/prefs/pref-formatting.js)
+* content/messenger/messengercompose/messengercompose.xul (content/messengercompose.xul)
+ content/messenger/messengercompose/mailComposeOverlay.xul (content/mailComposeOverlay.xul)
+ content/messenger/messengercompose/msgComposeContextOverlay.xul (content/msgComposeContextOverlay.xul)
+ content/messenger/messengercompose/MsgComposeCommands.js (content/MsgComposeCommands.js)
+ content/messenger/messengercompose/addressingWidgetOverlay.js (content/addressingWidgetOverlay.js)
diff --git a/comm/suite/mailnews/components/compose/moz.build b/comm/suite/mailnews/components/compose/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/mailnews/components/compose/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/mailnews/components/moz.build b/comm/suite/mailnews/components/moz.build
new file mode 100644
index 0000000000..9d5b9f36ad
--- /dev/null
+++ b/comm/suite/mailnews/components/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ "compose",
+ "prefs",
+ "addrbook",
+ "smime",
+]
diff --git a/comm/suite/mailnews/components/prefs/content/mailPrefsOverlay.xul b/comm/suite/mailnews/components/prefs/content/mailPrefsOverlay.xul
new file mode 100644
index 0000000000..2a4acef93b
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/mailPrefsOverlay.xul
@@ -0,0 +1,102 @@
+<?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 % mailPrefsOverlayDTD SYSTEM "chrome://messenger/locale/mailPrefsOverlay.dtd">
+%mailPrefsOverlayDTD;
+]>
+
+<overlay id="mailPrefsOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <preferences id="appearance_preferences">
+ <preference id="general.startup.mail"
+ name="general.startup.mail"
+ type="bool"/>
+ <preference id="general.startup.addressbook"
+ name="general.startup.addressbook"
+ type="bool"/>
+ </preferences>
+
+ <!-- mail startup toggle -->
+ <groupbox id="generalStartupPreferences">
+ <checkbox id="generalStartupMail"
+ insertafter="generalStartupBrowser"
+ label="&mail.label;"
+ accesskey="&mail.accesskey;"
+ preference="general.startup.mail"/>
+ <checkbox id="generalStartupAddressBook"
+ insertafter="generalStartupEditor,generalStartupMail"
+ label="&addressbook.label;"
+ accesskey="&addressbook.accesskey;"
+ preference="general.startup.addressbook"/>
+ </groupbox>
+
+ <!-- category tree entries for mail/news -->
+ <treechildren id="prefsPanelChildren">
+ <treeitem container="true"
+ id="mailnewsItem"
+ insertafter="navigatorItem"
+ label="&mail.label;"
+ prefpane="mailnews_pane"
+ url="chrome://messenger/content/pref-mailnews.xul"
+ helpTopic="mail_prefs_general">
+ <treechildren id="messengerChildren">
+ <treeitem id="viewingMessagesItem"
+ label="&viewingMessages.label;"
+ prefpane="viewing_messages_pane"
+ url="chrome://messenger/content/pref-viewing_messages.xul"
+ helpTopic="mail_prefs_display"/>
+ <treeitem id="notificationsItem"
+ label="&notifications.label;"
+ prefpane="notifications_pane"
+ url="chrome://messenger/content/pref-notifications.xul"
+ helpTopic="mail_prefs_notifications"/>
+ <treeitem id="composingItem"
+ label="&composingMessages.label;"
+ prefpane="composing_messages_pane"
+ url="chrome://messenger/content/messengercompose/pref-composing_messages.xul"
+ helpTopic="mail_prefs_messages"/>
+ <treeitem id="formattingItem"
+ label="&format.label;"
+ prefpane="formatting_pane"
+ url="chrome://messenger/content/messengercompose/pref-formatting.xul"
+ helpTopic="mail_prefs_formatting"/>
+ <treeitem id="addressItem"
+ label="&address.label;"
+ prefpane="addressing_pane"
+ url="chrome://messenger/content/addressbook/pref-addressing.xul"
+ helpTopic="mail_prefs_addressing"/>
+ <treeitem id="junkItem"
+ label="&junk.label;"
+ prefpane="junk_pane"
+ url="chrome://messenger/content/pref-junk.xul"
+ helpTopic="mail-prefs-junk"/>
+ <treeitem id="tagsItem"
+ label="&tags.label;"
+ prefpane="tags_pane"
+ url="chrome://messenger/content/pref-tags.xul"
+ helpTopic="mail-prefs-tags"/>
+ <treeitem id="receiptsItem"
+ label="&return.label;"
+ prefpane="receipts_pane"
+ url="chrome://messenger/content/pref-receipts.xul"
+ helpTopic="mail-prefs-receipts"/>
+ <treeitem id="characterEncodingItem"
+ label="&characterEncoding2.label;"
+ prefpane="character_encoding_pane"
+ url="chrome://messenger/content/pref-character_encoding.xul"
+ helpTopic="mail_prefs_text_encoding"/>
+ <treeitem id="offlineItem"
+ label="&networkStorage.label;"
+ prefpane="offline_pane"
+ url="chrome://messenger/content/pref-offline.xul"
+ helpTopic="mail_prefs_offline"/>
+ </treechildren>
+ </treeitem>
+ </treechildren>
+
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-character_encoding.js b/comm/suite/mailnews/components/prefs/content/pref-character_encoding.js
new file mode 100644
index 0000000000..0ae30e9b1f
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-character_encoding.js
@@ -0,0 +1,41 @@
+/* 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/. */
+
+// The contents of this file will be loaded into the scope of the object
+// <prefpane id="character_encoding_pane">!
+
+var updatingPref = false;
+
+function Startup ()
+{
+ PrefChanged(document.getElementById('mailnews.view_default_charset'));
+ PrefChanged(document.getElementById('mailnews.send_default_charset'));
+}
+
+function PrefChanged(aPref)
+{
+ if (updatingPref)
+ return;
+
+ var id = aPref.id.substr(9, 4) + "DefaultCharsetList";
+ var menulist = document.getElementById(id);
+ if (!aPref.hasUserValue)
+ menulist.selectedIndex = 0;
+ else {
+ var bundle = document.getElementById("charsetBundle");
+ menulist.value = bundle.getString(aPref.value.toLowerCase());
+ }
+}
+
+function UpdatePref(aMenulist)
+{
+ updatingPref = true;
+ var id = "mailnews." + aMenulist.id.substr(0, 4) + "_default_charset";
+ var pref = document.getElementById(id);
+ if (aMenulist.selectedIndex)
+ pref.value = aMenulist.value;
+ else
+ pref.value = undefined; // reset to default
+ updatingPref = false;
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-character_encoding.xul b/comm/suite/mailnews/components/prefs/content/pref-character_encoding.xul
new file mode 100755
index 0000000000..009f5f49de
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-character_encoding.xul
@@ -0,0 +1,111 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % prefCharacterEncodingDTD SYSTEM "chrome://messenger/locale/pref-character_encoding.dtd"> %prefCharacterEncodingDTD;
+ <!ENTITY % prefUtilitiesDTD SYSTEM "chrome://communicator/locale/pref/prefutilities.dtd"> %prefUtilitiesDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="character_encoding_pane"
+ label="&pref.character.encoding2.title;"
+ script="chrome://messenger/content/pref-character_encoding.js">
+ <preferences id="character_encoding_preferences">
+ <preference id="mailnews.view_default_charset"
+ name="mailnews.view_default_charset"
+ type="wstring"
+ onchange="PrefChanged(this);"/>
+ <preference id="mail.strictly_mime"
+ name="mail.strictly_mime"
+ type="bool"/>
+ <preference id="mailnews.send_default_charset"
+ name="mailnews.send_default_charset"
+ type="wstring"
+ onchange="PrefChanged(this);"/>
+ <preference id="mailnews.reply_in_default_charset"
+ name="mailnews.reply_in_default_charset"
+ type="bool"/>
+ </preferences>
+
+ <groupbox align="start">
+ <caption label="&messageDisplay.caption;"/>
+ <hbox align="center">
+ <label control="viewDefaultCharsetList"
+ value="&viewFallbackCharset2.label;"
+ accesskey="&viewFallbackCharset2.accesskey;"/>
+ <menulist id="viewDefaultCharsetList"
+ oncommand="UpdatePref(this);">
+ <menupopup>
+ <menuitem label="&FallbackCharset.auto;" value=""/>
+ <menuitem label="&FallbackCharset.unicode;" value="UTF-8"/>
+ <menuitem label="&FallbackCharset.other;" value="windows-1252"/>
+ <menuseparator/>
+ <menuitem label="&FallbackCharset.arabic;" value="windows-1256"/>
+ <menuitem label="&FallbackCharset.baltic;" value="windows-1257"/>
+ <menuitem label="&FallbackCharset.ceiso;" value="ISO-8859-2"/>
+ <menuitem label="&FallbackCharset.cewindows;" value="windows-1250"/>
+ <menuitem label="&FallbackCharset.simplified;" value="gbk"/>
+ <menuitem label="&FallbackCharset.traditional;" value="Big5"/>
+ <menuitem label="&FallbackCharset.cyrillic;" value="windows-1251"/>
+ <menuitem label="&FallbackCharset.greek;" value="ISO-8859-7"/>
+ <menuitem label="&FallbackCharset.hebrew;" value="windows-1255"/>
+ <menuitem label="&FallbackCharset.japanese;" value="Shift_JIS"/>
+ <menuitem label="&FallbackCharset.korean;" value="EUC-KR"/>
+ <menuitem label="&FallbackCharset.thai;" value="windows-874"/>
+ <menuitem label="&FallbackCharset.turkish;" value="windows-1254"/>
+ <menuitem label="&FallbackCharset.vietnamese;" value="windows-1258"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <description>&viewFallbackCharset.desc;</description>
+ </groupbox>
+
+ <!-- Composing Mail -->
+ <groupbox align="start">
+ <caption label="&composingMessages.caption;"/>
+
+ <checkbox id="strictlyMime"
+ label="&useMIME.label;"
+ accesskey="&useMIME.accesskey;"
+ preference="mail.strictly_mime"/>
+
+ <hbox align="center">
+ <label value="&sendDefaultCharset2.label;"
+ accesskey="&sendDefaultCharset2.accesskey;"
+ control="sendDefaultCharsetList"/>
+ <menulist id="sendDefaultCharsetList"
+ oncommand="UpdatePref(this);">
+ <menupopup>
+ <menuitem label="&FallbackCharset.auto;" value=""/>
+ <menuitem label="&FallbackCharset.unicode;" value="UTF-8"/>
+ <menuitem label="&FallbackCharset.other;" value="windows-1252"/>
+ <menuseparator/>
+ <menuitem label="&FallbackCharset.arabic;" value="windows-1256"/>
+ <menuitem label="&FallbackCharset.baltic;" value="windows-1257"/>
+ <menuitem label="&FallbackCharset.ceiso;" value="ISO-8859-2"/>
+ <menuitem label="&FallbackCharset.cewindows;" value="windows-1250"/>
+ <menuitem label="&FallbackCharset.simplified;" value="gbk"/>
+ <menuitem label="&FallbackCharset.traditional;" value="Big5"/>
+ <menuitem label="&FallbackCharset.cyrillic;" value="windows-1251"/>
+ <menuitem label="&FallbackCharset.greek;" value="ISO-8859-7"/>
+ <menuitem label="&FallbackCharset.hebrew;" value="windows-1255"/>
+ <menuitem label="&FallbackCharset.japanese;" value="Shift_JIS"/>
+ <menuitem label="&FallbackCharset.korean;" value="EUC-KR"/>
+ <menuitem label="&FallbackCharset.thai;" value="windows-874"/>
+ <menuitem label="&FallbackCharset.turkish;" value="windows-1254"/>
+ <menuitem label="&FallbackCharset.vietnamese;" value="windows-1258"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+ <checkbox id="replyInDefaultCharset"
+ label="&replyInDefaultCharset3.label;"
+ accesskey="&replyInDefaultCharset3.accesskey;"
+ preference="mailnews.reply_in_default_charset"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-junk.js b/comm/suite/mailnews/components/prefs/content/pref-junk.js
new file mode 100644
index 0000000000..9f31050c46
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-junk.js
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+function Startup()
+{
+ UpdateDependentElement("manualMark", "manualMarkMode");
+ UpdateDependentElement("enableJunkLogging", "openJunkLog");
+}
+
+function UpdateDependentElement(aBaseId, aDependentId)
+{
+ var pref = document.getElementById(aBaseId).getAttribute("preference");
+ EnableElementById(aDependentId, document.getElementById(pref).value, false);
+}
+
+function OpenJunkLog()
+{
+ window.openDialog("chrome://messenger/content/junkLog.xul",
+ "junkLog",
+ "chrome,modal,titlebar,resizable,centerscreen");
+}
+
+function ResetTrainingData()
+{
+ // make sure the user really wants to do this
+ var bundle = document.getElementById("bundleJunkPreferences");
+ var title = bundle.getString("confirmResetJunkTrainingTitle");
+ var text = bundle.getString("confirmResetJunkTrainingText");
+
+ // if the user says no, then just fall out
+ if (Services.prompt.confirmEx(window, title, text,
+ Services.prompt.STD_YES_NO_BUTTONS |
+ Services.prompt.BUTTON_POS_1_DEFAULT,
+ "", "", "", null, {}))
+ return;
+
+ // otherwise go ahead and remove the training data
+ MailServices.junk.resetTrainingData();
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-junk.xul b/comm/suite/mailnews/components/prefs/content/pref-junk.xul
new file mode 100644
index 0000000000..b6d1f4507d
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-junk.xul
@@ -0,0 +1,134 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % prefJunkDTD SYSTEM "chrome://messenger/locale/pref-junk.dtd">
+%prefJunkDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="junk_pane"
+ label="&pref.junk.title;"
+ script="chrome://messenger/content/pref-junk.js">
+ <preferences id="junk_preferences">
+ <preference id="mail.spam.manualMark"
+ name="mail.spam.manualMark"
+ type="bool"
+ onchange="EnableElementById('manualMarkMode', this.value, false);"/>
+ <preference id="mail.spam.manualMarkMode"
+ name="mail.spam.manualMarkMode"
+ type="int"/>
+ <preference id="mail.spam.markAsReadOnSpam"
+ name="mail.spam.markAsReadOnSpam"
+ type="bool"/>
+ <preference id="mailnews.ui.junk.manualMarkAsJunkMarksRead"
+ name="mailnews.ui.junk.manualMarkAsJunkMarksRead"
+ type="bool"/>
+ <preference id="mail.spam.logging.enabled"
+ name="mail.spam.logging.enabled"
+ type="bool"
+ onchange="EnableElementById('openJunkLog', this.value, false);"/>
+ <preference id="pref.junk.disable_button.openJunkLog"
+ name="pref.junk.disable_button.openJunkLog"
+ type="string"/>
+ <preference id="pref.junk.disable_button.resetTrainingData"
+ name="pref.junk.disable_button.resetTrainingData"
+ type="string"/>
+ <preference id="mail.phishing.detection.enabled"
+ name="mail.phishing.detection.enabled"
+ type="bool"/>
+ <preference id="mailnews.downloadToTempFile"
+ name="mailnews.downloadToTempFile"
+ type="bool"/>
+ </preferences>
+
+ <stringbundleset id="junkBundleset">
+ <stringbundle id="bundleJunkPreferences"
+ src="chrome://messenger/locale/messenger.properties"/>
+ </stringbundleset>
+
+ <groupbox>
+ <caption label="&junkSettings.caption;"/>
+ <description>&junkMail.intro;</description>
+ <class separator="thin"/>
+
+ <checkbox id="manualMark"
+ label="&manualMark.label;"
+ accesskey="&manualMark.accesskey;"
+ preference="mail.spam.manualMark"/>
+ <radiogroup id="manualMarkMode"
+ class="indent"
+ aria-labelledby="manualMark"
+ preference="mail.spam.manualMarkMode">
+ <radio id="manualMarkMode0"
+ label="&manualMarkModeMove.label;"
+ accesskey="&manualMarkModeMove.accesskey;"
+ value="0"/>
+ <radio id="manualMarkMode1"
+ label="&manualMarkModeDelete.label;"
+ accesskey="&manualMarkModeDelete.accesskey;"
+ value="1"/>
+ </radiogroup>
+
+ <separator class="thin"/>
+
+ <description>&markAsRead.intro;</description>
+ <vbox class="indent">
+ <checkbox id="autoMarkAsRead"
+ label="&autoMarkAsRead.label;"
+ accesskey="&autoMarkAsRead.accesskey;"
+ preference="mail.spam.markAsReadOnSpam"/>
+ <checkbox id="manualMarkAsRead"
+ label="&manualMarkAsRead.label;"
+ accesskey="&manualMarkAsRead.accesskey;"
+ preference="mailnews.ui.junk.manualMarkAsJunkMarksRead"/>
+ </vbox>
+
+ <separator class="thin"/>
+
+ <hbox align="start">
+ <checkbox id="enableJunkLogging"
+ label="&enableJunkLogging.label;"
+ accesskey="&enableJunkLogging.accesskey;"
+ preference="mail.spam.logging.enabled"/>
+ <spacer flex="1"/>
+ <button id="openJunkLog"
+ label="&openJunkLog.label;"
+ accesskey="&openJunkLog.accesskey;"
+ preference="pref.junk.disable_button.openJunkLog"
+ oncommand="OpenJunkLog();"/>
+ </hbox>
+ <hbox align="start">
+ <spacer flex="1"/>
+ <button id="resetTrainingData"
+ label="&resetTrainingData.label;"
+ accesskey="&resetTrainingData.accesskey;"
+ preference="pref.junk.disable_button.resetTrainingData"
+ oncommand="ResetTrainingData();"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&pref.suspectMail.caption;"/>
+
+ <checkbox id="enablePhishingDetector"
+ label="&enablePhishingDetector.label;"
+ accesskey="&enablePhishingDetector.accesskey;"
+ preference="mail.phishing.detection.enabled"/>
+
+ <separator class="thin"/>
+
+ <checkbox id="enableAntiVirusQuarantine"
+ label="&antiVirus.label;"
+ accesskey="&antiVirus.accesskey;"
+ preference="mailnews.downloadToTempFile"/>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-mailnews.js b/comm/suite/mailnews/components/prefs/content/pref-mailnews.js
new file mode 100644
index 0000000000..f057fb46ae
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-mailnews.js
@@ -0,0 +1,25 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function Startup()
+{
+ startPageCheck();
+}
+
+function startPageCheck()
+{
+ var checked = document.getElementById("mailnews.start_page.enabled").value;
+ var urlElement = document.getElementById("mailnewsStartPageUrl");
+ var prefLocked = document.getElementById("mailnews.start_page.url").locked;
+
+ urlElement.disabled = !checked || prefLocked;
+}
+
+function setHomePageToDefaultPage()
+{
+ var startPagePref = document.getElementById("mailnews.start_page.url");
+
+ startPagePref.value = startPagePref.defaultValue;
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-mailnews.xul b/comm/suite/mailnews/components/prefs/content/pref-mailnews.xul
new file mode 100644
index 0000000000..e3fbeb12e8
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-mailnews.xul
@@ -0,0 +1,141 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % prefMailnewsDTD SYSTEM "chrome://messenger/locale/pref-mailnews.dtd">
+%prefMailnewsDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="mailnews_pane"
+ label="&pref.mailnews.title;"
+ script="chrome://messenger/content/pref-mailnews.js">
+
+ <preferences id="mailnews_preferences">
+ <preference id="mailnews.confirm.moveFoldersToTrash"
+ name="mailnews.confirm.moveFoldersToTrash" type="bool"/>
+ <preference id="mailnews.remember_selected_message"
+ name="mailnews.remember_selected_message" type="bool"/>
+ <preference id="mailnews.thread_pane_column_unthreads"
+ name="mailnews.thread_pane_column_unthreads"
+ inverted="true" type="bool"/>
+ <preference id="mail.tabs.autoHide"
+ name="mail.tabs.autoHide"
+ type="bool"/>
+ <preference id="mail.tabs.loadInBackground"
+ name="mail.tabs.loadInBackground"
+ inverted="true" type="bool"/>
+ <preference id="mail.biff.on_new_window"
+ name="mail.biff.on_new_window"
+ type="bool"
+ inverted="true"/>
+ <preference id="mail.tabs.opentabfor.middleclick"
+ name="mail.tabs.opentabfor.middleclick"
+ type="bool"/>
+ <preference id="mail.tabs.opentabfor.doubleclick"
+ name="mail.tabs.opentabfor.doubleclick"
+ type="bool"/>
+ <preference id="mailnews.start_page.enabled"
+ onchange="this.parentNode.parentNode.startPageCheck();"
+ name="mailnews.start_page.enabled" type="bool"/>
+ <preference id="mailnews.start_page.url"
+ name="mailnews.start_page.url" type="wstring"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&generalSettings.caption;"/>
+
+ <hbox align="center">
+ <checkbox id="mailnewsConfirmMoveFoldersToTrash" label="&confirmMove.label;"
+ preference="mailnews.confirm.moveFoldersToTrash"
+ accesskey="&confirmMove.accesskey;"/>
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="mailRememberLastMsg" label="&rememberLastMsg.label;"
+ preference="mailnews.remember_selected_message"
+ accesskey="&rememberLastMsg.accesskey;" />
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="mailPreserveThreading"
+ label="&preserveThreading.label;"
+ accesskey="&preserveThreading.accesskey;"
+ preference="mailnews.thread_pane_column_unthreads"/>
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="mailAutoHide"
+ label="&mailAutoHide.label;"
+ accesskey="&mailAutoHide.accesskey;"
+ preference="mail.tabs.autoHide"/>
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="loadInBackground"
+ label="&loadInBackground.label;"
+ accesskey="&loadInBackground.accesskey;"
+ preference="mail.tabs.loadInBackground"/>
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="mailBiffOnNewWindow"
+ label="&mailBiffOnNewWindow.label;"
+ accesskey="&mailBiffOnNewWindow.accesskey;"
+ preference="mail.biff.on_new_window"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox id="mailOpenTabFor" align="start">
+ <caption label="&mailOpenTabsFor.label;"/>
+ <hbox align="center">
+ <checkbox id="mailMiddleClick"
+#ifndef XP_MACOSX
+ label="&mailMiddleClick.label;"
+ accesskey="&mailMiddleClick.accesskey;"
+#else
+ label="&mailMiddleClickMac.label;"
+ accesskey="&mailMiddleClickMac.accesskey;"
+#endif
+ preference="mail.tabs.opentabfor.middleclick"/>
+ </hbox>
+
+ <hbox align="center">
+ <checkbox id="mailDoubleClick"
+ label="&mailDoubleClick.label;"
+ accesskey="&mailDoubleClick.accesskey;"
+ preference="mail.tabs.opentabfor.doubleclick"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&messengerStartPage.caption;"/>
+ <hbox align="center">
+ <checkbox id="mailnewsStartPageEnabled" label="&enableStartPage.label;"
+ preference="mailnews.start_page.enabled"
+ accesskey="&enableStartPage.accesskey;"/>
+ </hbox>
+
+ <hbox align="center">
+ <label value="&location.label;" accesskey="&location.accesskey;"
+ control="mailnewsStartPageUrl"/>
+ <textbox id="mailnewsStartPageUrl" flex="1" type="autocomplete"
+ preference="mailnews.start_page.url" timeout="50"
+ autocompletesearch="history" maxrows="6" class="uri-element"/>
+ </hbox>
+ <hbox pack="end">
+ <button label="&useDefault.label;" accesskey="&useDefault.accesskey;"
+ oncommand="setHomePageToDefaultPage();">
+ <observes element="mailnewsStartPageUrl" attribute="disabled"/>
+ </button>
+ </hbox>
+
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-notifications.js b/comm/suite/mailnews/components/prefs/content/pref-notifications.js
new file mode 100644
index 0000000000..89191e7cd6
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-notifications.js
@@ -0,0 +1,91 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// The contents of this file will be loaded into the scope of the object
+// <prefpane id="notifications_pane">!
+
+var {AppConstants} = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+var gSoundUrlPref = null;
+
+function Startup()
+{
+ // if we don't have the alert service, hide the pref UI for using alerts to notify on new mail
+ // see bug #158711
+ var newMailNotificationAlertUI = document.getElementById("newMailNotificationAlertBox");
+ newMailNotificationAlertUI.hidden = !("@mozilla.org/alerts-service;1" in Cc);
+
+ // as long as the old notification code is still around, the new options
+ // won't apply if mail.biff.show_new_alert is false and should be hidden
+ document.getElementById("showAlertPreviewText").hidden =
+ document.getElementById("showAlertSubject").hidden =
+ document.getElementById("showAlertSender").hidden =
+ !Services.prefs.getBoolPref("mail.biff.show_new_alert");
+
+ // animate dock icon option currently available for macOS only
+ var newMailNotificationBouncePref = document.getElementById("newMailNotificationBounceBox");
+ newMailNotificationBouncePref.hidden = AppConstants.platform != "macosx";
+
+ // show tray icon option currently available for Windows only
+ var newMailNotificationTrayIconPref = document.getElementById("newMailNotificationTrayIconBox");
+ newMailNotificationTrayIconPref.hidden = AppConstants.platform != "win";
+
+ // use system alert option currently available for Linux only
+ var useSystemAlertPref = document.getElementById("useSystemAlertBox");
+ useSystemAlertPref.hidden = AppConstants.platform != "linux";
+
+ EnableAlert(document.getElementById("mail.biff.show_alert").value, false);
+ EnableTrayIcon(document.getElementById("mail.biff.show_tray_icon").value);
+
+ gSoundUrlPref = document.getElementById("mail.biff.play_sound.url");
+
+ PlaySoundCheck(document.getElementById("mail.biff.play_sound").value);
+}
+
+function EnableAlert(aEnable, aFocus)
+{
+ // switch off the balloon on Windows if the user wants regular alerts
+ if (aEnable && AppConstants.platform == "win") {
+ let balloonAlert = document.getElementById("mail.biff.show_balloon");
+ if (!balloonAlert.locked)
+ balloonAlert.value = false;
+ }
+
+ EnableElementById("showAlertTime", aEnable, aFocus);
+ EnableElementById("showAlertPreviewText", aEnable, false);
+ EnableElementById("showAlertSubject", aEnable, false);
+ EnableElementById("showAlertSender", aEnable, false);
+ EnableElementById("useSystemAlertRadio", aEnable, false);
+}
+
+function EnableTrayIcon(aEnable)
+{
+ EnableElementById("newMailNotificationBalloon", aEnable, false);
+}
+
+function ClearAlert(aEnable)
+{
+ // switch off the regular alerts if the user wants the balloon
+ if (aEnable && AppConstants.platform == "win") {
+ let showAlert = document.getElementById("mail.biff.show_alert");
+ if (!showAlert.locked)
+ showAlert.value = false;
+ }
+}
+
+function PlaySoundCheck(aPlaySound)
+{
+ let playSoundType = document.getElementById("mail.biff.play_sound.type").value;
+
+ EnableElementById("newMailNotificationType", aPlaySound, false);
+ EnableSoundURL(aPlaySound && (playSoundType == 1));
+}
+
+function EnableSoundURL(aEnable)
+{
+ EnableElementById("mailnewsSoundFileUrl", aEnable, false);
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-notifications.xul b/comm/suite/mailnews/components/prefs/content/pref-notifications.xul
new file mode 100644
index 0000000000..20ac974050
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-notifications.xul
@@ -0,0 +1,187 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+%brandDTD;
+<!ENTITY % prefNotificationsDTD SYSTEM "chrome://messenger/locale/pref-notifications.dtd">
+%prefNotificationsDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="notifications_pane"
+ label="&pref.notifications.title;"
+ script="chrome://messenger/content/pref-notifications.js">
+
+ <preferences id="notifications_preferences">
+ <preference id="mail.biff.show_alert"
+ name="mail.biff.show_alert"
+ type="bool"
+ onchange="EnableAlert(this.value, this.value);"/>
+ <preference id="alerts.totalOpenTime"
+ name="alerts.totalOpenTime"
+ type="int"/>
+ <preference id="mail.biff.alert.show_preview"
+ name="mail.biff.alert.show_preview"
+ type="bool"/>
+ <preference id="mail.biff.alert.show_subject"
+ name="mail.biff.alert.show_subject"
+ type="bool"/>
+ <preference id="mail.biff.alert.show_sender"
+ name="mail.biff.alert.show_sender"
+ type="bool"/>
+ <preference id="mail.biff.use_system_alert"
+ name="mail.biff.use_system_alert"
+ type="bool"/>
+ <preference id="mail.biff.show_tray_icon"
+ name="mail.biff.show_tray_icon"
+ type="bool"
+ onchange="EnableTrayIcon(this.value);"/>
+ <preference id="mail.biff.show_balloon"
+ name="mail.biff.show_balloon"
+ type="bool"
+ onchange="ClearAlert(this.value);"/>
+ <preference id="mail.biff.animate_dock_icon"
+ name="mail.biff.animate_dock_icon"
+ type="bool"/>
+ <preference id="mail.biff.play_sound"
+ name="mail.biff.play_sound"
+ type="bool"
+ onchange="PlaySoundCheck(this.value);"/>
+ <preference id="mail.biff.play_sound.type"
+ name="mail.biff.play_sound.type"
+ type="int"
+ onchange="EnableSoundURL(this.value == 1);"/>
+ <preference id="mail.biff.play_sound.url"
+ name="mail.biff.play_sound.url"
+ type="string"/>
+ </preferences>
+
+ <groupbox id="newMessagesArrivePrefs">
+ <caption label="&notifications.caption;"/>
+
+ <label value="&newMessagesArrive.label;"/>
+ <vbox id="newMailNotificationAlertBox">
+ <hbox align="center">
+ <checkbox id="newMailNotificationAlert"
+ label="&showAlertFor.label;"
+ accesskey="&showAlertFor.accesskey;"
+ preference="mail.biff.show_alert"/>
+ <textbox id="showAlertTime"
+ type="number"
+ size="3"
+ min="1"
+ max="3600"
+ preference="alerts.totalOpenTime"
+ onsyncfrompreference="return document.getElementById(this.getAttribute('preference')).value / 1000;"
+ onsynctopreference="return this.value * 1000;"
+ aria-labelledby="newMailNotificationAlert showAlertTime showAlertTimeEnd"/>
+ <label id="showAlertTimeEnd"
+ value="&showAlertTimeEnd.label;">
+ <observes element="newMailNotificationAlert"
+ attribute="disabled"/>
+ </label>
+ </hbox>
+ <vbox id="showAlertOptionsBox"
+ class="indent">
+ <checkbox id="showAlertPreviewText"
+ label="&showAlertPreviewText.label;"
+ accesskey="&showAlertPreviewText.accesskey;"
+ preference="mail.biff.alert.show_preview"/>
+ <checkbox id="showAlertSubject"
+ label="&showAlertSubject.label;"
+ accesskey="&showAlertSubject.accesskey;"
+ preference="mail.biff.alert.show_subject"/>
+ <checkbox id="showAlertSender"
+ label="&showAlertSender.label;"
+ accesskey="&showAlertSender.accesskey;"
+ preference="mail.biff.alert.show_sender"/>
+ <separator id="newMailNotificationAlertSeparator"
+ class="thin"/>
+ <vbox id="useSystemAlertBox">
+ <radiogroup id="useSystemAlertRadio"
+ preference="mail.biff.use_system_alert">
+ <radio id="useSystemAlert"
+ value="true"
+ label="&useSystemAlert.label;"
+ accesskey="&useSystemAlert.accesskey;"/>
+ <radio id="useBuiltInAlert"
+ value="false"
+ label="&useBuiltInAlert.label;"
+ accesskey="&useBuiltInAlert.accesskey;"/>
+ </radiogroup>
+ <separator id="useSystemAlertSeparator"
+ class="thin"/>
+ </vbox>
+ </vbox>
+ </vbox>
+ <vbox id="newMailNotificationTrayIconBox">
+ <checkbox id="newMailNotificationTrayIcon"
+ label="&showTrayIcon.label;"
+ accesskey="&showTrayIcon.accesskey;"
+ preference="mail.biff.show_tray_icon"/>
+ <checkbox id="newMailNotificationBalloon"
+ class="indent"
+ label="&showBalloon.label;"
+ accesskey="&showBalloon.accesskey;"
+ preference="mail.biff.show_balloon"/>
+ <separator id="newMailNotificationTrayIconSeparator"
+ class="thin"/>
+ </vbox>
+ <vbox id="newMailNotificationBounceBox">
+ <checkbox id="newMailNotificationBounce"
+ label="&bounceSystemDockIcon.label;"
+ accesskey="&bounceSystemDockIcon.accesskey;"
+ preference="mail.biff.animate_dock_icon"/>
+ <separator id="newMailNotificationBounceSeparator"
+ class="thin"/>
+ </vbox>
+ <checkbox id="newMailNotification"
+ label="&playSound.label;"
+ accesskey="&playSound.accesskey;"
+ preference="mail.biff.play_sound"/>
+ <radiogroup id="newMailNotificationType"
+ preference="mail.biff.play_sound.type"
+ class="indent"
+ aria-labelledby="newMailNotification">
+ <radio id="system"
+ value="0"
+ label="&systemsound.label;"
+ accesskey="&systemsound.accesskey;"/>
+ <radio id="custom"
+ value="1"
+ label="&customsound.label;"
+ accesskey="&customsound.accesskey;"/>
+ </radiogroup>
+
+ <hbox align="center" class="indent">
+ <filefield id="mailnewsSoundFileUrl"
+ flex="1"
+ preference="mail.biff.play_sound.url"
+ preference-editable="true"
+ onsyncfrompreference="return WriteSoundField(this, document.getElementById('notifications_pane').gSoundUrlPref.value);"
+ aria-labelledby="custom"/>
+ <hbox align="center">
+ <button id="browse"
+ label="&browse.label;"
+ filepickertitle="&browse.title;"
+ accesskey="&browse.accesskey;"
+ oncommand="SelectSound(gSoundUrlPref);">
+ <observes element="mailnewsSoundFileUrl" attribute="disabled"/>
+ </button>
+ <button id="playButton"
+ label="&playButton.label;"
+ accesskey="&playButton.accesskey;"
+ oncommand="PlaySound(gSoundUrlPref.value, true);">
+ <observes element="mailnewsSoundFileUrl" attribute="disabled"/>
+ </button>
+ </hbox>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-offline.js b/comm/suite/mailnews/components/prefs/content/pref-offline.js
new file mode 100644
index 0000000000..d453c8f97a
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-offline.js
@@ -0,0 +1,19 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// The contents of this file will be loaded into the scope of the object
+// <prefpane id="offline_pane">!
+
+function Startup()
+{
+ var value = document.getElementById("mail.prompt_purge_threshhold").value;
+ EnableElementById("offlineCompactFolderMin", value, false);
+}
+
+function EnableMailPurgeThreshold(aValue)
+{
+ var focus = (document.getElementById("offlineCompactFolder") == document.commandDispatcher.focusedElement);
+ EnableElementById("offlineCompactFolderMin", aValue, focus);
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-offline.xul b/comm/suite/mailnews/components/prefs/content/pref-offline.xul
new file mode 100644
index 0000000000..49e4288ab0
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-offline.xul
@@ -0,0 +1,121 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/pref-offline.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="offline_pane"
+ label="&pref.network.title;"
+ script="chrome://messenger/content/pref-offline.js">
+
+ <preferences id="offline_preferences">
+ <preference id="offline.startup_state"
+ name="offline.startup_state"
+ type="int"/>
+ <preference id="offline.send.unsent_messages"
+ name="offline.send.unsent_messages"
+ type="int"/>
+ <preference id="offline.download.download_messages"
+ name="offline.download.download_messages"
+ type="int"/>
+ <preference id="mailnews.tcptimeout"
+ name="mailnews.tcptimeout"
+ type="int"/>
+ <preference id="mail.prompt_purge_threshhold"
+ name="mail.prompt_purge_threshhold"
+ type="bool"
+ onchange="EnableMailPurgeThreshold(this.value);"/>
+ <preference id="mail.purge_threshhold_mb"
+ name="mail.purge_threshhold_mb"
+ type="int"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&pref.offline.caption;"/>
+
+ <hbox align="center">
+ <label value="&textStartUp;" control="whenStartingUp"
+ accesskey="&textStartUp.accesskey;"/>
+ <menulist id="whenStartingUp" preference="offline.startup_state">
+ <menupopup>
+ <menuitem value="0" label="&menuitemRememberPrevState;"/>
+ <menuitem value="1" label="&menuitemAskMe;"/>
+ <menuitem value="2" label="&menuitemAlwaysOnline;"/>
+ <menuitem value="3" label="&menuitemAlwaysOffline;"/>
+ <menuitem value="4" label="&menuitemAutomatic;"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+
+ <separator/>
+
+ <label value="&textGoingOnline;" control="whengoingOnlinestate"/>
+ <radiogroup id="whengoingOnlinestate"
+ orient="horizontal" class="indent"
+ preference="offline.send.unsent_messages">
+ <radio value="1" label="&radioAutoSend;"
+ accesskey="&radioAutoSend.accesskey;"/>
+ <radio value="2" label="&radioNotSend;"
+ accesskey="&radioNotSend.accesskey;"/>
+ <radio value="0" label="&radioAskUnsent;"
+ accesskey="&radioAskUnsent.accesskey;"/>
+ </radiogroup>
+
+ <separator/>
+
+ <label value="&textGoingOffline;" control="whengoingOfflinestate"/>
+ <radiogroup id="whengoingOfflinestate"
+ orient="horizontal" class="indent"
+ preference="offline.download.download_messages">
+ <radio value="1" label="&radioAutoDownload;"
+ accesskey="&radioAutoDownload.accesskey;"/>
+ <radio value="2" label="&radioNotDownload;"
+ accesskey="&radioNotDownload.accesskey;"/>
+ <radio value="0" label="&radioAskDownload;"
+ accesskey="&radioAskDownload.accesskey;"/>
+ </radiogroup>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&mailConnections.caption;"/>
+ <hbox align="center">
+ <label id="timeoutLabel"
+ value="&mailnewsTimeout.label;"
+ accesskey="&mailnewsTimeout.accesskey;"
+ control="mailnewsTimeoutSeconds"/>
+ <textbox id="mailnewsTimeoutSeconds"
+ type="number"
+ size="4"
+ preference="mailnews.tcptimeout"
+ aria-labelledby="timeoutLabel mailnewsTimeoutSeconds timeoutSeconds"/>
+ <label id="timeoutSeconds" value="&mailnewsTimeoutSeconds.label;"/>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&Diskspace;"/>
+ <hbox align="center">
+ <checkbox id="offlineCompactFolder"
+ label="&offlineCompactFolders.label;"
+ accesskey="&offlineCompactFolders.accesskey;"
+ preference="mail.prompt_purge_threshhold"
+ aria-labelledby="offlineCompactFolder offlineCompactFolderMin offlineCompactFolderMB"/>
+ <textbox id="offlineCompactFolderMin"
+ type="number"
+ size="4"
+ min="1"
+ max="2048"
+ increment="10"
+ value="20"
+ preference="mail.purge_threshhold_mb"
+ aria-labelledby="offlineCompactFolder offlineCompactFolderMin offlineCompactFolderMB"/>
+ <label id="offlineCompactFolderMB" value="&offlineCompactFoldersMB.label;"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-receipts.js b/comm/suite/mailnews/components/prefs/content/pref-receipts.js
new file mode 100644
index 0000000000..7a85ac1a1f
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-receipts.js
@@ -0,0 +1,28 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gNotInToCcLabel;
+var gOutsideDomainLabel;
+var gOtherCasesLabel;
+
+function Startup()
+{
+ gNotInToCcLabel = document.getElementById("notInToCcLabel");
+ gOutsideDomainLabel = document.getElementById("outsideDomainLabel");
+ gOtherCasesLabel = document.getElementById("otherCasesLabel");
+
+ var value = document.getElementById("mail.mdn.report.enabled").value;
+ EnableDisableAllowedReceipts(value);
+}
+
+function EnableDisableAllowedReceipts(aEnable)
+{
+ EnableElementById("notInToCcPref", aEnable, false);
+ EnableElementById("outsideDomainPref", aEnable, false);
+ EnableElementById("otherCasesPref", aEnable, false);
+ gNotInToCcLabel.disabled = !aEnable;
+ gOutsideDomainLabel.disabled = !aEnable;
+ gOtherCasesLabel.disabled = !aEnable;
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-receipts.xul b/comm/suite/mailnews/components/prefs/content/pref-receipts.xul
new file mode 100644
index 0000000000..0ca17a02ed
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-receipts.xul
@@ -0,0 +1,146 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/pref-receipts.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="receipts_pane"
+ label="&pref.receipts.title;"
+ script="chrome://messenger/content/pref-receipts.js">
+ <preferences id="receipts_preferences">
+ <preference id="mail.receipt.request_return_receipt_on"
+ name="mail.receipt.request_return_receipt_on"
+ type="bool"/>
+ <preference id="mail.incorporate.return_receipt"
+ name="mail.incorporate.return_receipt"
+ type="int"/>
+ <preference id="mail.mdn.report.enabled"
+ name="mail.mdn.report.enabled"
+ type="bool"
+ onchange="EnableDisableAllowedReceipts(this.value);"/>
+ <preference id="mail.mdn.report.not_in_to_cc"
+ name="mail.mdn.report.not_in_to_cc"
+ type="int"/>
+ <preference id="mail.mdn.report.outside_domain"
+ name="mail.mdn.report.outside_domain"
+ type="int"/>
+ <preference id="mail.mdn.report.other"
+ name="mail.mdn.report.other"
+ type="int"/>
+ </preferences>
+
+ <groupbox>
+ <caption label="&prefReceipts.caption;"/>
+
+ <vbox id="returnReceiptSettings" align="start">
+ <checkbox id="alwaysRequest"
+ label="&requestReceipt.label;"
+ accesskey="&requestReceipt.accesskey;"
+ preference="mail.receipt.request_return_receipt_on"/>
+
+ <separator/>
+
+ <vbox id="receiptArrive">
+ <label control="receiptFolder">&receiptArrive.label;</label>
+ <radiogroup id="receiptFolder"
+ class="indent"
+ preference="mail.incorporate.return_receipt">
+ <radio value="0"
+ label="&leaveIt.label;"
+ accesskey="&leaveIt.accesskey;"/>
+ <radio value="1"
+ label="&moveToSent.label;"
+ accesskey="&moveToSent.accesskey;"/>
+ </radiogroup>
+ </vbox>
+
+ <separator/>
+
+ <vbox id="receiptRequest">
+ <label control="receiptSend">&requestMDN.label;</label>
+ <radiogroup id="receiptSend"
+ class="indent"
+ preference="mail.mdn.report.enabled">
+ <radio value="false"
+ label="&never.label;"
+ accesskey="&never.accesskey;"/>
+ <radio value="true"
+ label="&returnSome.label;"
+ accesskey="&returnSome.accesskey;"/>
+
+ <hbox id="receiptSendIf" class="indent">
+ <grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row align="center">
+ <label id="notInToCcLabel"
+ accesskey="&notInToCc.accesskey;"
+ control="notInToCcPref"
+ value="&notInToCc.label;"/>
+ <menulist id="notInToCcPref"
+ preference="mail.mdn.report.not_in_to_cc">
+ <menupopup>
+ <menuitem value="0"
+ label="&neverSend.label;"/>
+ <menuitem value="1"
+ label="&alwaysSend.label;"/>
+ <menuitem value="2"
+ label="&askMe.label;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <label id="outsideDomainLabel"
+ accesskey="&outsideDomain.accesskey;"
+ control="outsideDomainPref"
+ value="&outsideDomain.label;"/>
+ <menulist id="outsideDomainPref"
+ preference="mail.mdn.report.outside_domain">
+ <menupopup>
+ <menuitem value="0"
+ label="&neverSend.label;"/>
+ <menuitem value="1"
+ label="&alwaysSend.label;"/>
+ <menuitem value="2"
+ label="&askMe.label;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ <row align="center">
+ <label id="otherCasesLabel"
+ accesskey="&otherCases.accesskey;"
+ control="otherCasesPref"
+ value="&otherCases.label;"/>
+ <menulist id="otherCasesPref"
+ preference="mail.mdn.report.other">
+ <menupopup>
+ <menuitem value="0"
+ label="&neverSend.label;"/>
+ <menuitem value="1"
+ label="&alwaysSend.label;"/>
+ <menuitem value="2"
+ label="&askMe.label;"/>
+ </menupopup>
+ </menulist>
+ </row>
+ </rows>
+ </grid>
+ </hbox>
+ </radiogroup>
+
+ </vbox>
+
+ </vbox>
+
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-tags.js b/comm/suite/mailnews/components/prefs/content/pref-tags.js
new file mode 100644
index 0000000000..8182fe7237
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-tags.js
@@ -0,0 +1,478 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+// Each tag entry in our list looks like this:
+// <listitem>
+// <listcell>
+// <textbox/>
+// </listcell>
+// <listcell>
+// <colorpicker type='button'/>
+// </listcell>
+// </listitem>
+// For ease of handling, all tag data is stored in <listitem>.tagInfo also.
+
+const kOrdinalCharLow = "a";
+const kOrdinalCharHigh = "z";
+const kOrdinalPadding = String.fromCharCode(kOrdinalCharLow.charCodeAt(0) - 1);
+
+var gInstantApply = document.documentElement.instantApply; // read only once
+var gTagList = null; // tagList root element
+var gAddButton = null;
+var gDeleteButton = null;
+var gRaiseButton = null;
+var gLowerButton = null;
+
+var gDeletedTags = {}; // tags marked for deletion in non-instant apply mode
+
+
+function Startup()
+{
+ gTagList = document.getElementById('tagList');
+ gAddButton = document.getElementById('addTagButton');
+ gDeleteButton = document.getElementById('deleteTagButton');
+ gRaiseButton = document.getElementById('raiseTagButton');
+ gLowerButton = document.getElementById('lowerTagButton');
+ InitTagList();
+ if (!gInstantApply)
+ window.addEventListener("dialogaccept", this.OnOK, true);
+ UpdateButtonStates();
+}
+
+function InitTagList()
+{
+ // Read the tags from preferences via the tag service.
+ var tagArray = MailServices.tags.getAllTags();
+ for (var i = 0; i < tagArray.length; ++i)
+ {
+ var t = tagArray[i];
+ var tagInfo = {tag: t.tag,
+ key: t.key,
+ color: t.color,
+ ordinal: t.ordinal,
+ new: false, // not added in this run
+ changed: false}; // not changed (yet)
+ AppendTagEntry(tagInfo, null);
+ }
+}
+
+// read text and color from the listitem
+function UpdateTagInfo(aTagInfo, aEntry)
+{
+ var tag = aEntry.firstChild.firstChild.value;
+ var color = aEntry.lastChild.lastChild.color;
+ if (tag != aTagInfo.tag || color != aTagInfo.color)
+ {
+ aTagInfo.changed = true; // never unset changed flag here!
+ aTagInfo.tag = tag;
+ aTagInfo.color = color;
+ }
+}
+
+// set text and color of the listitem
+function UpdateTagEntry(aTagInfo, aEntry)
+{
+ aEntry.firstChild.firstChild.value = aTagInfo.tag;
+ aEntry.lastChild.lastChild.color = aTagInfo.color || 'inherit';
+}
+
+function AppendTagEntry(aTagInfo, aRefChild)
+{
+ // Creating a colorpicker dynamically in an onload handler is really sucky.
+ // You MUST first set its type attribute (to select the correct binding), then
+ // add the element to the DOM (to bind the binding) and finally set the color
+ // property(!) afterwards. Try in any other order and fail... :-(
+ var tagCell = document.createElement('listcell');
+ var textbox = document.createElement('textbox');
+ textbox.setAttribute('flex', 1);
+ textbox.setAttribute('value', aTagInfo.tag);
+ tagCell.appendChild(textbox);
+
+ var colorCell = document.createElement('listcell');
+ var colorpicker = document.createElement('colorpicker');
+ colorpicker.setAttribute('type', 'button');
+ colorpicker.setAttribute('color', aTagInfo.color || 'inherit')
+ colorCell.appendChild(colorpicker);
+
+ var entry = document.createElement('listitem');
+ entry.addEventListener('focus', OnFocus, true);
+ entry.addEventListener('change', OnChange);
+ entry.setAttribute('allowevents', 'true'); // activate textbox and colorpicker
+ entry.tagInfo = aTagInfo;
+ entry.appendChild(tagCell);
+ entry.appendChild(colorCell);
+
+ gTagList.insertBefore(entry, aRefChild);
+ return entry;
+}
+
+function OnFocus(aEvent)
+{
+ gTagList.selectedItem = this;
+ UpdateButtonStates();
+}
+
+function FocusTagEntry(aEntry)
+{
+ // focus the entry's textbox
+ gTagList.ensureElementIsVisible(aEntry);
+ aEntry.firstChild.firstChild.focus();
+}
+
+function GetTagOrdinal(aTagInfo)
+{
+ if (aTagInfo.ordinal)
+ return aTagInfo.ordinal;
+ return aTagInfo.key;
+}
+
+function SetTagOrdinal(aTagInfo, aOrdinal)
+{
+ var ordinal = aTagInfo.ordinal;
+ aTagInfo.ordinal = (aTagInfo.key != aOrdinal) ? aOrdinal : '';
+ if (aTagInfo.ordinal != ordinal)
+ aTagInfo.changed = true;
+}
+
+function BisectString(aPrev, aNext)
+{
+ // find a string which is lexically greater than aPrev and lesser than aNext:
+ // - copy leading parts common to aPrev and aNext into the result
+ // - find the first position where aPrev and aNext differ:
+ // - if we can squeeze a character in between there: fine, done!
+ // - if not:
+ // - if the rest of aNext is longer than one character, we can squeeze
+ // in just the first aNext rest-character and be done!
+ // - else we try to "increment" aPrev a bit to fit in
+ if ((aPrev >= aNext) || (aPrev + kOrdinalCharLow >= aNext))
+ return ''; // no such string exists
+
+ // pad the shorter string
+ var lenPrev = aPrev.length;
+ var lenNext = aNext.length;
+ var lenMax = Math.max(lenPrev, lenNext);
+
+ // loop over both strings at once, padding if necessary
+ var constructing = false;
+ var result = '';
+ for (var i = 0; i < lenMax; ++i)
+ {
+ var prevChar = (i < lenPrev) ? aPrev[i] : kOrdinalPadding;
+ var nextChar = constructing ? kOrdinalCharHigh
+ : (i < lenNext) ? aNext[i]
+ : kOrdinalPadding;
+ var prevCode = prevChar.charCodeAt(0);
+ var nextCode = nextChar.charCodeAt(0);
+ if (prevCode == nextCode)
+ {
+ // copy common characters
+ result += prevChar;
+ }
+ else if (prevCode + 1 < nextCode)
+ {
+ // found a real bisecting string
+ result += String.fromCharCode((prevCode + nextCode) / 2);
+ return result;
+ }
+ else
+ {
+ // nextCode is greater than prevCode, but there's no place in between.
+ // But if aNext[i+1] exists, then nextChar will suffice and we're done!
+ // ("x" < "xsomething")
+ if (i + 1 < lenNext)
+ {
+ // found a real bisecting string
+ return result + nextChar;
+ }
+ // just copy over prevChar and enter construction mode
+ result += prevChar;
+ constructing = true;
+ }
+ }
+ return ''; // nothing found
+}
+
+function RecalculateOrdinal(aEntry)
+{
+ // Calculate a new ordinal for the given entry, assuming that both its
+ // predecessor's and successor's are correct, i.e. ord(p) < ord(s)!
+ var tagInfo = aEntry.tagInfo;
+ var ordinal = tagInfo.key;
+ // get neighbouring ordinals
+ var prevOrdinal = '', nextOrdinal = '';
+ var prev = aEntry.previousSibling;
+ if (prev && prev.nodeName == 'listitem') // first.prev == listhead
+ prevOrdinal = GetTagOrdinal(prev.tagInfo);
+ var next = aEntry.nextSibling;
+ if (next)
+ {
+ nextOrdinal = GetTagOrdinal(next.tagInfo);
+ }
+ else
+ {
+ // ensure key < nextOrdinal if entry is the last/only entry
+ nextOrdinal = prevOrdinal || ordinal;
+ nextOrdinal = String.fromCharCode(nextOrdinal.charCodeAt(0) + 2);
+ }
+
+ if (prevOrdinal < ordinal && ordinal < nextOrdinal)
+ {
+ // no ordinal needed, just clear it
+ SetTagOrdinal(tagInfo, '')
+ return;
+ }
+
+ // so we need a new ordinal, because key <= prevOrdinal or key >= nextOrdinal
+ ordinal = BisectString(prevOrdinal, nextOrdinal);
+ if (ordinal)
+ {
+ // found a new ordinal
+ SetTagOrdinal(tagInfo, ordinal)
+ return;
+ }
+
+ // couldn't find an ordinal before the nextOrdinal, so take that instead
+ // and recalculate a new one for the next entry
+ SetTagOrdinal(tagInfo, nextOrdinal);
+ if (next)
+ ApplyChange(next);
+}
+
+function OnChange(aEvent)
+{
+ ApplyChange(aEvent.currentTarget);
+}
+
+function ApplyChange(aEntry)
+{
+ if (!aEntry)
+ {
+ dump('ApplyChange: aEntry is null! (called by ' + ApplyChange.caller.name + ')\n');
+ return;
+ }
+
+ // the tag data got changed, so write it back to the system
+ var tagInfo = aEntry.tagInfo;
+ UpdateTagInfo(tagInfo, aEntry);
+ // ensure unique tag name
+ var dupeList = ReadTagListFromUI(aEntry);
+ var uniqueTag = DisambiguateTag(tagInfo.tag, dupeList);
+ if (tagInfo.tag != uniqueTag)
+ {
+ tagInfo.tag = uniqueTag;
+ tagInfo.changed = true;
+ UpdateTagEntry(tagInfo, aEntry);
+ }
+
+ if (gInstantApply)
+ {
+ // If the item was newly added, we still can rename the key,
+ // so that it's in sync with the actual tag.
+ if (tagInfo.new && tagInfo.key)
+ {
+ // Do not clear the "new" flag!
+ // The key will only stick after closing the dialog.
+ MailServices.tags.deleteKey(tagInfo.key);
+ tagInfo.key = '';
+ }
+ if (!tagInfo.key)
+ {
+ // create a new key, based upon the new tag
+ MailServices.tags.addTag(tagInfo.tag, '', '');
+ tagInfo.key = MailServices.tags.getKeyForTag(tagInfo.tag);
+ }
+
+ // Recalculate the sort ordinal, if necessary.
+ // We assume that the neighbour's ordinals are correct,
+ // i.e. that ordinal(pos - 1) < ordinal(pos + 1)!
+ RecalculateOrdinal(aEntry);
+ WriteTag(tagInfo);
+ }
+}
+
+function WriteTag(aTagInfo)
+{
+//dump('********** WriteTag: ' + aTagInfo.toSource() + '\n');
+ try
+ {
+ MailServices.tags.addTagForKey(aTagInfo.key, aTagInfo.tag, aTagInfo.color,
+ aTagInfo.ordinal);
+ aTagInfo.changed = false;
+ }
+ catch (e)
+ {
+ dump('WriteTag: update exception:\n' + e);
+ }
+}
+
+function UpdateButtonStates()
+{
+ var entry = gTagList.selectedItem;
+ // disable Delete if no selection
+ gDeleteButton.disabled = !entry;
+ // disable Raise if no selection or first entry
+ gRaiseButton.disabled = !entry || !gTagList.getPreviousItem(entry, 1);
+ // disable Lower if no selection or last entry
+ gLowerButton.disabled = !entry || !gTagList.getNextItem(entry, 1);
+}
+
+function ReadTagListFromUI(aIgnoreEntry)
+{
+ // reads distinct tag names from the UI
+ var dupeList = {}; // indexed by tag
+ for (var entry = gTagList.firstChild; entry; entry = entry.nextSibling)
+ if ((entry != aIgnoreEntry) && (entry.localName == 'listitem'))
+ dupeList[entry.firstChild.firstChild.value] = true;
+ return dupeList;
+}
+
+function DisambiguateTag(aTag, aTagList)
+{
+ if (aTag in aTagList)
+ {
+ var suffix = 2;
+ while (aTag + ' ' + suffix in aTagList)
+ ++suffix;
+ aTag += ' ' + suffix;
+ }
+ return aTag;
+}
+
+function AddTag()
+{
+ // Add a new tag to the UI here.
+ // It will be be written to the preference system
+ // (a) directly on each change for instant apply, or
+ // (b) only if the dialogaccept handler is executed.
+
+ // create new unique tag name
+ var dupeList = ReadTagListFromUI();
+ var tag = DisambiguateTag(gAddButton.getAttribute('defaulttagname'), dupeList);
+
+ // create new tag list entry
+ var tagInfo = {tag: tag,
+ key: '',
+ color: 'inherit',
+ ordinal: '',
+ new: true,
+ changed: true};
+ var refChild = gTagList.getNextItem(gTagList.selectedItem, 1);
+ var newEntry = AppendTagEntry(tagInfo, refChild);
+ ApplyChange(newEntry);
+ FocusTagEntry(newEntry);
+}
+
+function DeleteTag()
+{
+ // Delete the selected tag from the UI here. If it was added during this
+ // preference dialog session, we can drop it at once; if it was read from
+ // the preferences system, we may need to remember killing it in OnOK.
+ var entry = gTagList.selectedItem;
+ var key = entry.tagInfo.key;
+ if (key)
+ {
+ if (gInstantApply)
+ MailServices.tags.deleteKey(key);
+ else
+ gDeletedTags[key] = true; // dummy value
+ }
+ // after removing, move focus to next entry, if it exist, else try previous
+ var newFocusItem = gTagList.getNextItem(entry, 1) ||
+ gTagList.getPreviousItem(entry, 1);
+ gTagList.removeItemAt(gTagList.getIndexOfItem(entry));
+ if (newFocusItem)
+ FocusTagEntry(newFocusItem);
+ else
+ UpdateButtonStates();
+}
+
+function MoveTag(aMoveUp)
+{
+ // Move the selected tag one position up or down in the tagList's child order.
+ // This reordering may require changing ordinal strings.
+ var entry = gTagList.selectedItem;
+ var tagInfo = entry.tagInfo;
+ UpdateTagInfo(tagInfo, entry); // remember changed values
+ var successor = aMoveUp ? gTagList.getPreviousItem(entry, 1)
+ : gTagList.getNextItem(entry, 2);
+ entry.parentNode.insertBefore(entry, successor);
+ FocusTagEntry(entry);
+ tagInfo.changed = true;
+ UpdateTagEntry(tagInfo, entry); // needs to be visible
+ ApplyChange(entry);
+}
+
+function Restore()
+{
+ // clear pref panel tag list
+ // Remember any known keys for deletion in the OKHandler.
+ while (gTagList.getRowCount())
+ {
+ var key = gTagList.removeItemAt(0).tagInfo.key;
+ if (key)
+ {
+ if (gInstantApply)
+ MailServices.tags.deleteKey(key);
+ else
+ gDeletedTags[key] = true; // dummy value
+ }
+ }
+ // add default items (no ordinal strings for those)
+ for (var i = 1; i <= 5; ++i)
+ {
+ // create default tags from the former label defaults
+ var key = "$label" + i;
+ var tag = GetLocalizedStringPref("mailnews.labels.description." + i);
+ var color = Services.prefs.getDefaultBranch("mailnews.labels.color.").getCharPref(i);
+ var tagInfo = {tag: tag,
+ key: key,
+ color: color,
+ ordinal: '',
+ new: false,
+ changed: true};
+ var newEntry = AppendTagEntry(tagInfo, null);
+ ApplyChange(newEntry);
+ }
+ FocusTagEntry(gTagList.getItemAtIndex(0));
+}
+
+function OnOK()
+{
+ // remove all deleted tags from the preferences system
+ for (var key in gDeletedTags)
+ MailServices.tags.deleteKey(key);
+
+ // Write tags to the preferences system, creating keys and ordinal strings.
+ for (var entry = gTagList.firstChild; entry; entry = entry.nextSibling)
+ {
+ if (entry.localName == 'listitem')
+ {
+ // only write listitems which have changed (this includes new ones)
+ var tagInfo = entry.tagInfo;
+ if (tagInfo.changed)
+ {
+ if (!tagInfo.key)
+ {
+ // newly added tag, need to create a key and read it
+ MailServices.tags.addTag(tagInfo.tag, '', '');
+ tagInfo.key = MailServices.tags.getKeyForTag(tagInfo.tag);
+ }
+ if (tagInfo.key)
+ {
+ // Recalculate the sort ordinal, if necessary.
+ // We assume that the neighbour's ordinals are correct,
+ // i.e. that ordinal(pos - 1) < ordinal(pos + 1)!
+ RecalculateOrdinal(entry);
+ // update the tag definition
+ WriteTag(tagInfo);
+ }
+ }
+ }
+ }
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-tags.xul b/comm/suite/mailnews/components/prefs/content/pref-tags.xul
new file mode 100644
index 0000000000..cd99824a25
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-tags.xul
@@ -0,0 +1,83 @@
+<?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://messenger/skin/prefPanels.css" type="text/css"?>
+
+<!DOCTYPE page SYSTEM "chrome://messenger/locale/pref-tags.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="tags_pane"
+ label="&pref.tags.title;"
+ script="chrome://messenger/content/pref-tags.js">
+
+ <preferences id="tags_preferences">
+ <preference id="pref.tags.disable_button.add"
+ name="pref.tags.disable_button.add"
+ type="bool"/>
+ <preference id="pref.tags.disable_button.delete"
+ name="pref.tags.disable_button.delete"
+ type="bool"/>
+ <preference id="pref.tags.disable_button.lower"
+ name="pref.tags.disable_button.lower"
+ type="bool"/>
+ <preference id="pref.tags.disable_button.raise"
+ name="pref.tags.disable_button.raise"
+ type="bool"/>
+ <preference id="pref.tags.disable_button.restore"
+ name="pref.tags.disable_button.restore"
+ type="bool"/>
+ </preferences>
+
+ <groupbox flex="1">
+ <caption label="&pref.tags.caption;"/>
+ <label control="tagList">&pref.tags.description;</label>
+ <hbox flex="1">
+ <listbox id="tagList" flex="1" onselect="UpdateButtonStates();">
+ <listcols>
+ <listcol flex="1"/>
+ <listcol/>
+ </listcols>
+ <listhead>
+ <listheader label="&tagColumn.label;"/>
+ <listheader label="&colorColumn.label;"/>
+ </listhead>
+ </listbox>
+
+ <vbox>
+ <button id="addTagButton"
+ label="&addTagButton.label;"
+ accesskey="&addTagButton.accesskey;"
+ defaulttagname="&defaultTagName.label;"
+ preference="pref.tags.disable_button.add"
+ oncommand="AddTag();"/>
+ <button id="deleteTagButton"
+ label="&deleteTagButton.label;"
+ accesskey="&deleteTagButton.accesskey;"
+ preference="pref.tags.disable_button.delete"
+ oncommand="DeleteTag();"/>
+ <spacer flex="1"/>
+ <button id="raiseTagButton"
+ label="&raiseTagButton.label;"
+ accesskey="&raiseTagButton.accesskey;"
+ preference="pref.tags.disable_button.raise"
+ oncommand="MoveTag(true);"/>
+ <button id="lowerTagButton"
+ label="&lowerTagButton.label;"
+ accesskey="&lowerTagButton.accesskey;"
+ preference="pref.tags.disable_button.lower"
+ oncommand="MoveTag(false);"/>
+ <spacer flex="1"/>
+ <button id="restoreButton"
+ label="&restoreButton.label;"
+ accesskey="&restoreButton.accesskey;"
+ preference="pref.tags.disable_button.restore"
+ oncommand="Restore();"/>
+ </vbox>
+ </hbox>
+ </groupbox>
+
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/content/pref-viewing_messages.js b/comm/suite/mailnews/components/prefs/content/pref-viewing_messages.js
new file mode 100644
index 0000000000..75b5da1b3d
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-viewing_messages.js
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// The contents of this file will be loaded into the scope of the object
+// <prefpane id="viewing_messages_pane">!
+
+function Startup()
+{
+ var autoPref = document.getElementById("mailnews.mark_message_read.auto");
+ UpdateMarkAsReadOptions(autoPref.value);
+}
+
+function UpdateMarkAsReadOptions(aEnableReadDelay)
+{
+ EnableElementById("markAsReadAfterPreferences", aEnableReadDelay, false);
+ // ... and the extras!
+ var delayPref = document.getElementById("mailnews.mark_message_read.delay");
+ UpdateMarkAsReadTextbox(aEnableReadDelay && delayPref.value, false);
+}
+
+function UpdateMarkAsReadTextbox(aEnable, aFocus)
+{
+ EnableElementById("markAsReadDelay", aEnable, aFocus);
+}
diff --git a/comm/suite/mailnews/components/prefs/content/pref-viewing_messages.xul b/comm/suite/mailnews/components/prefs/content/pref-viewing_messages.xul
new file mode 100644
index 0000000000..117761c86b
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/content/pref-viewing_messages.xul
@@ -0,0 +1,174 @@
+<?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 % pref-viewing_messagesDTD SYSTEM "chrome://messenger/locale/pref-viewing_messages.dtd">
+%pref-viewing_messagesDTD;
+<!ENTITY % editorOverlayDTD SYSTEM "chrome://editor/locale/editorOverlay.dtd">
+%editorOverlayDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <prefpane id="viewing_messages_pane"
+ label="&pref.viewing.messages.title;"
+ script="chrome://messenger/content/pref-viewing_messages.js">
+
+ <preferences id="viewing_messages_preferences">
+ <preference id="mailnews.reuse_message_window"
+ name="mailnews.reuse_message_window"
+ type="bool"/>
+ <preference id="mail.close_message_window.on_delete"
+ name="mail.close_message_window.on_delete"
+ type="bool"/>
+ <preference id="mailnews.message_display.disable_remote_image"
+ name="mailnews.message_display.disable_remote_image"
+ type="bool"/>
+ <preference id="mailnews.mark_message_read.auto"
+ name="mailnews.mark_message_read.auto"
+ type="bool"
+ onchange="UpdateMarkAsReadOptions(this.value);"/>
+ <preference id="mailnews.mark_message_read.delay"
+ name="mailnews.mark_message_read.delay"
+ type="bool"
+ onchange="UpdateMarkAsReadTextbox(this.value, this.value);"/>
+ <preference id="mailnews.mark_message_read.delay.interval"
+ name="mailnews.mark_message_read.delay.interval"
+ type="int"/>
+ <preference id="mail.fixed_width_messages"
+ name="mail.fixed_width_messages"
+ type="bool"/>
+ <preference id="mail.wrap_long_lines"
+ name="mail.wrap_long_lines"
+ type="bool"/>
+ <preference id="mail.display_glyph"
+ name="mail.display_glyph"
+ type="bool"/>
+ <preference id="mail.quoted_style"
+ name="mail.quoted_style"
+ type="int"/>
+ <preference id="mail.quoted_size"
+ name="mail.quoted_size"
+ type="int"/>
+ <preference id="mail.citation_color"
+ name="mail.citation_color"
+ type="string"/>
+ <preference id="mail.showCondensedAddresses"
+ name="mail.showCondensedAddresses"
+ type="bool"/>
+ </preferences>
+
+ <groupbox align="start">
+ <caption label="&generalMessageDisplay.caption;"/>
+ <label value="&openingMessages.label;" control="reuseMessageWindow"/>
+ <vbox class="indent">
+ <radiogroup id="reuseMessageWindow"
+ orient="horizontal"
+ preference="mailnews.reuse_message_window">
+ <radio id="new"
+ label="&newWindowRadio.label;"
+ accesskey="&newWindowRadio.accesskey;"
+ value="false"/>
+ <radio id="existing"
+ label="&existingWindowRadio.label;"
+ accesskey="&existingWindowRadio.accesskey;"
+ value="true"/>
+ </radiogroup>
+ <checkbox id="closeMsgWindowOnDelete"
+ label="&closeMsgWindowOnDelete.label;"
+ accesskey="&closeMsgWindowOnDelete.accesskey;"
+ preference="mail.close_message_window.on_delete"/>
+ </vbox>
+
+ <checkbox id="disableContent" label="&disableContent.label;"
+ accesskey="&disableContent.accesskey;"
+ preference="mailnews.message_display.disable_remote_image"/>
+ <checkbox id="showCondensedAddresses"
+ label="&showCondensedAddresses.label;"
+ accesskey="&showCondensedAddresses.accesskey;"
+ preference="mail.showCondensedAddresses"/>
+
+ <separator class="thin"/>
+
+ <checkbox id="automaticallyMarkAsRead"
+ preference="mailnews.mark_message_read.auto"
+ label="&autoMarkAsRead.label;"
+ accesskey="&autoMarkAsRead.accesskey;"
+ oncommand="UpdateMarkAsReadOptions(this.checked);"/>
+
+ <hbox align="center" class="indent">
+ <checkbox id="markAsReadAfterPreferences"
+ label="&markAsReadAfter.label;"
+ accesskey="&markAsReadAfter.accesskey;"
+ preference="mailnews.mark_message_read.delay"/>
+ <textbox id="markAsReadDelay"
+ type="number"
+ size="2"
+ maximum="99"
+ preference="mailnews.mark_message_read.delay.interval"
+ aria-labelledby="markAsReadAfterPreferences markAsReadDelay secondsLabel"/>
+ <label id="secondsLabel"
+ value="&secondsLabel.label;">
+ <observes element="markAsReadAfterPreferences"
+ attribute="disabled"/>
+ </label>
+ </hbox>
+ </groupbox>
+
+ <groupbox>
+ <caption label="&displayPlainText.caption;"/>
+ <hbox align="center">
+ <label value="&fontPlainText.label;"
+ accesskey="&fontPlainText.accesskey;"
+ control="mailFixedWidthMessages"/>
+ <radiogroup id="mailFixedWidthMessages"
+ orient="horizontal"
+ preference="mail.fixed_width_messages">
+ <radio label="&fontFixedWidth.label;"
+ accesskey="&fontFixedWidth.accesskey;"
+ value="true"/>
+ <radio label="&fontVarWidth.label;"
+ accesskey="&fontVarWidth.accesskey;"
+ value="false"/>
+ </radiogroup>
+ </hbox>
+
+ <checkbox id="wrapLongLines" label="&wrapInMsg.label;"
+ accesskey="&wrapInMsg.accesskey;"
+ preference="mail.wrap_long_lines"/>
+ <checkbox id="displayGlyph" label="&convertEmoticons.label;"
+ accesskey="&convertEmoticons.accesskey;"
+ preference="mail.display_glyph"/>
+
+ <separator class="thin"/>
+
+ <description>&displayQuoted.label;</description>
+ <hbox class="indent" align="center">
+ <label value="&style.label;" accesskey="&style.accesskey;" control="mailQuotedStyle"/>
+ <menulist id="mailQuotedStyle" preference="mail.quoted_style">
+ <menupopup>
+ <menuitem value="0" label="&regular.label;"/>
+ <menuitem value="1" label="&bold.label;"/>
+ <menuitem value="2" label="&italic.label;"/>
+ <menuitem value="3" label="&boldItalic.label;"/>
+ </menupopup>
+ </menulist>
+
+ <label value="&size.label;" accesskey="&size.accesskey;" control="mailQuotedSize"/>
+ <menulist id="mailQuotedSize" preference="mail.quoted_size">
+ <menupopup>
+ <menuitem value="0" label="&regular.label;"/>
+ <menuitem value="1" label="&bigger.label;"/>
+ <menuitem value="2" label="&smaller.label;"/>
+ </menupopup>
+ </menulist>
+
+ <label value="&color.label;" accesskey="&color.accesskey;" control="mailCitationColor"/>
+ <colorpicker type="button" id="mailCitationColor" palettename="standard"
+ preference="mail.citation_color"/>
+ </hbox>
+ </groupbox>
+ </prefpane>
+</overlay>
diff --git a/comm/suite/mailnews/components/prefs/jar.mn b/comm/suite/mailnews/components/prefs/jar.mn
new file mode 100644
index 0000000000..9baa230079
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/jar.mn
@@ -0,0 +1,23 @@
+# 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/.
+
+messenger.jar:
+ content/messenger/mailPrefsOverlay.xul (content/mailPrefsOverlay.xul)
+* content/messenger/pref-mailnews.xul (content/pref-mailnews.xul)
+ content/messenger/pref-mailnews.js (content/pref-mailnews.js)
+ content/messenger/pref-notifications.xul (content/pref-notifications.xul)
+ content/messenger/pref-notifications.js (content/pref-notifications.js)
+ content/messenger/pref-junk.xul (content/pref-junk.xul)
+ content/messenger/pref-junk.js (content/pref-junk.js)
+ content/messenger/pref-tags.xul (content/pref-tags.xul)
+ content/messenger/pref-tags.js (content/pref-tags.js)
+ content/messenger/pref-viewing_messages.xul (content/pref-viewing_messages.xul)
+ content/messenger/pref-viewing_messages.js (content/pref-viewing_messages.js)
+ content/messenger/pref-receipts.xul (content/pref-receipts.xul)
+ content/messenger/pref-receipts.js (content/pref-receipts.js)
+ content/messenger/pref-character_encoding.xul (content/pref-character_encoding.xul)
+ content/messenger/pref-character_encoding.js (content/pref-character_encoding.js)
+ content/messenger/pref-offline.xul (content/pref-offline.xul)
+ content/messenger/pref-offline.js (content/pref-offline.js)
+
diff --git a/comm/suite/mailnews/components/prefs/moz.build b/comm/suite/mailnews/components/prefs/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/mailnews/components/prefs/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/mailnews/components/smime/content/msgCompSMIMEOverlay.js b/comm/suite/mailnews/components/smime/content/msgCompSMIMEOverlay.js
new file mode 100644
index 0000000000..e802130008
--- /dev/null
+++ b/comm/suite/mailnews/components/smime/content/msgCompSMIMEOverlay.js
@@ -0,0 +1,354 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+// Account encryption policy values:
+// const kEncryptionPolicy_Never = 0;
+// 'IfPossible' was used by ns4.
+// const kEncryptionPolicy_IfPossible = 1;
+var kEncryptionPolicy_Always = 2;
+
+var gEncryptedURIService =
+ Cc["@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"]
+ .getService(Ci.nsIEncryptedSMIMEURIsService);
+
+var gNextSecurityButtonCommand = "";
+var gSMFields = null;
+var gEncryptOptionChanged;
+var gSignOptionChanged;
+
+function onComposerLoad()
+{
+ // Are we already set up ? Or are the required fields missing ?
+ if (gSMFields || !gMsgCompose || !gMsgCompose.compFields)
+ return;
+
+ gMsgCompose.compFields.composeSecure = null;
+
+ gSMFields = Cc["@mozilla.org/messengercompose/composesecure;1"]
+ .createInstance(Ci.nsIMsgComposeSecure);
+ if (!gSMFields)
+ return;
+
+ gMsgCompose.compFields.composeSecure = gSMFields;
+
+ // Set up the initial security state.
+ gSMFields.requireEncryptMessage =
+ gCurrentIdentity.getIntAttribute("encryptionpolicy") == kEncryptionPolicy_Always;
+ if (!gSMFields.requireEncryptMessage &&
+ gEncryptedURIService &&
+ gEncryptedURIService.isEncrypted(gMsgCompose.originalMsgURI))
+ {
+ // Override encryption setting if original is known as encrypted.
+ gSMFields.requireEncryptMessage = true;
+ }
+ if (gSMFields.requireEncryptMessage)
+ setEncryptionUI();
+ else
+ setNoEncryptionUI();
+
+ gSMFields.signMessage = gCurrentIdentity.getBoolAttribute("sign_mail");
+ if (gSMFields.signMessage)
+ setSignatureUI();
+ else
+ setNoSignatureUI();
+}
+
+addEventListener("load", smimeComposeOnLoad, {capture: false, once: true});
+
+// this function gets called multiple times
+function smimeComposeOnLoad()
+{
+ onComposerLoad();
+
+ top.controllers.appendController(SecurityController);
+
+ addEventListener("compose-from-changed", onComposerFromChanged, true);
+ addEventListener("compose-send-message", onComposerSendMessage, true);
+
+ addEventListener("unload", smimeComposeOnUnload, {capture: false, once: true});
+}
+
+function smimeComposeOnUnload()
+{
+ removeEventListener("compose-from-changed", onComposerFromChanged, true);
+ removeEventListener("compose-send-message", onComposerSendMessage, true);
+
+ top.controllers.removeController(SecurityController);
+}
+
+function showNeedSetupInfo()
+{
+ let compSmimeBundle = document.getElementById("bundle_comp_smime");
+ let brandBundle = document.getElementById("brandBundle");
+ if (!compSmimeBundle || !brandBundle)
+ return;
+
+ let buttonPressed = Services.prompt.confirmEx(window,
+ brandBundle.getString("brandShortName"),
+ compSmimeBundle.getString("NeedSetup"),
+ Services.prompt.STD_YES_NO_BUTTONS, 0, 0, 0, null, {});
+ if (buttonPressed == 0)
+ openHelp("sign-encrypt", "chrome://communicator/locale/help/suitehelp.rdf");
+}
+
+function toggleEncryptMessage()
+{
+ if (!gSMFields)
+ return;
+
+ gSMFields.requireEncryptMessage = !gSMFields.requireEncryptMessage;
+
+ if (gSMFields.requireEncryptMessage)
+ {
+ // Make sure we have a cert.
+ if (!gCurrentIdentity.getUnicharAttribute("encryption_cert_name"))
+ {
+ gSMFields.requireEncryptMessage = false;
+ showNeedSetupInfo();
+ return;
+ }
+
+ setEncryptionUI();
+ }
+ else
+ {
+ setNoEncryptionUI();
+ }
+
+ gEncryptOptionChanged = true;
+}
+
+function toggleSignMessage()
+{
+ if (!gSMFields)
+ return;
+
+ gSMFields.signMessage = !gSMFields.signMessage;
+
+ if (gSMFields.signMessage) // make sure we have a cert name...
+ {
+ if (!gCurrentIdentity.getUnicharAttribute("signing_cert_name"))
+ {
+ gSMFields.signMessage = false;
+ showNeedSetupInfo();
+ return;
+ }
+
+ setSignatureUI();
+ }
+ else
+ {
+ setNoSignatureUI();
+ }
+
+ gSignOptionChanged = true;
+}
+
+function setSecuritySettings(menu_id)
+{
+ if (!gSMFields)
+ return;
+
+ document.getElementById("menu_securityEncryptRequire" + menu_id)
+ .setAttribute("checked", gSMFields.requireEncryptMessage);
+ document.getElementById("menu_securitySign" + menu_id)
+ .setAttribute("checked", gSMFields.signMessage);
+}
+
+function setNextCommand(what)
+{
+ gNextSecurityButtonCommand = what;
+}
+
+function doSecurityButton()
+{
+ var what = gNextSecurityButtonCommand;
+ gNextSecurityButtonCommand = "";
+
+ switch (what)
+ {
+ case "encryptMessage":
+ toggleEncryptMessage();
+ break;
+
+ case "signMessage":
+ toggleSignMessage();
+ break;
+
+ case "show":
+ default:
+ showMessageComposeSecurityStatus();
+ }
+}
+
+function setNoSignatureUI()
+{
+ top.document.getElementById("securityStatus").removeAttribute("signing");
+ top.document.getElementById("signing-status").collapsed = true;
+}
+
+function setSignatureUI()
+{
+ top.document.getElementById("securityStatus").setAttribute("signing", "ok");
+ top.document.getElementById("signing-status").collapsed = false;
+}
+
+function setNoEncryptionUI()
+{
+ top.document.getElementById("securityStatus").removeAttribute("crypto");
+ top.document.getElementById("encryption-status").collapsed = true;
+}
+
+function setEncryptionUI()
+{
+ top.document.getElementById("securityStatus").setAttribute("crypto", "ok");
+ top.document.getElementById("encryption-status").collapsed = false;
+}
+
+function showMessageComposeSecurityStatus()
+{
+ Recipients2CompFields(gMsgCompose.compFields);
+
+ window.openDialog(
+ "chrome://messenger-smime/content/msgCompSecurityInfo.xul",
+ "",
+ "chrome,modal,resizable,centerscreen",
+ {
+ compFields : gMsgCompose.compFields,
+ subject : GetMsgSubjectElement().value,
+ smFields : gSMFields,
+ isSigningCertAvailable :
+ gCurrentIdentity.getUnicharAttribute("signing_cert_name") != "",
+ isEncryptionCertAvailable :
+ gCurrentIdentity.getUnicharAttribute("encryption_cert_name") != "",
+ currentIdentity : gCurrentIdentity
+ }
+ );
+}
+
+var SecurityController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_viewSecurityStatus":
+ return true;
+
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_viewSecurityStatus":
+ return true;
+
+ default:
+ return false;
+ }
+ }
+};
+
+function onComposerSendMessage()
+{
+ let emailAddresses = [];
+
+ try
+ {
+ if (!gMsgCompose.compFields.composeSecure.requireEncryptMessage)
+ return;
+
+ emailAddresses = Cc["@mozilla.org/messenger-smime/smimejshelper;1"]
+ .createInstance(Ci.nsISMimeJSHelper)
+ .getNoCertAddresses(gMsgCompose.compFields);
+ }
+ catch (e)
+ {
+ return;
+ }
+
+ if (emailAddresses.length > 0)
+ {
+ // The rules here: If the current identity has a directoryServer set, then
+ // use that, otherwise, try the global preference instead.
+
+ let autocompleteDirectory;
+
+ // Does the current identity override the global preference?
+ if (gCurrentIdentity.overrideGlobalPref)
+ {
+ autocompleteDirectory = gCurrentIdentity.directoryServer;
+ }
+ else
+ {
+ // Try the global one
+ if (Services.prefs.getBoolPref("ldap_2.autoComplete.useDirectory"))
+ autocompleteDirectory =
+ Services.prefs.getCharPref("ldap_2.autoComplete.directoryServer");
+ }
+
+ if (autocompleteDirectory)
+ window.openDialog("chrome://messenger-smime/content/certFetchingStatus.xul",
+ "",
+ "chrome,modal,resizable,centerscreen",
+ autocompleteDirectory,
+ emailAddresses);
+ }
+}
+
+function onComposerFromChanged()
+{
+ if (!gSMFields)
+ return;
+
+ var encryptionPolicy = gCurrentIdentity.getIntAttribute("encryptionpolicy");
+ var useEncryption = false;
+
+ if (!gEncryptOptionChanged)
+ {
+ // Encryption wasn't manually checked.
+ // Set up the encryption policy from the setting of the new identity.
+
+ // 0 == never, 1 == if possible (ns4), 2 == always encrypt.
+ useEncryption = (encryptionPolicy == kEncryptionPolicy_Always);
+ }
+ else
+ {
+ useEncryption = !!gCurrentIdentity.getUnicharAttribute("encryption_cert_name");
+ }
+
+ gSMFields.requireEncryptMessage = useEncryption;
+ if (useEncryption)
+ setEncryptionUI();
+ else
+ setNoEncryptionUI();
+
+ // - If signing is disabled, we will not turn it on automatically.
+ // - If signing is enabled, but the new account defaults to not sign, we will turn signing off.
+ var signMessage = gCurrentIdentity.getBoolAttribute("sign_mail");
+ var useSigning = false;
+
+ if (!gSignOptionChanged)
+ {
+ // Signing wasn't manually checked.
+ // Set up the signing policy from the setting of the new identity.
+ useSigning = signMessage;
+ }
+ else
+ {
+ useSigning = !!gCurrentIdentity.getUnicharAttribute("signing_cert_name");
+ }
+ gSMFields.signMessage = useSigning;
+ if (useSigning)
+ setSignatureUI();
+ else
+ setNoSignatureUI();
+}
diff --git a/comm/suite/mailnews/components/smime/content/msgHdrViewSMIMEOverlay.js b/comm/suite/mailnews/components/smime/content/msgHdrViewSMIMEOverlay.js
new file mode 100644
index 0000000000..09f665b4d0
--- /dev/null
+++ b/comm/suite/mailnews/components/smime/content/msgHdrViewSMIMEOverlay.js
@@ -0,0 +1,258 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gSignedUINode = null;
+var gEncryptedUINode = null;
+var gSMIMEContainer = null;
+var gStatusBar = null;
+var gSignedStatusPanel = null;
+var gEncryptedStatusPanel = null;
+
+var gEncryptedURIService = null;
+var gMyLastEncryptedURI = null;
+
+var gSMIMEBundle = null;
+// var gBrandBundle; -- defined in mailWindow.js
+
+// manipulates some globals from msgReadSMIMEOverlay.js
+
+var nsICMSMessageErrors = Ci.nsICMSMessageErrors;
+
+/// Get the necko URL for the message URI.
+function neckoURLForMessageURI(aMessageURI)
+{
+ let msgSvc = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger)
+ .messageServiceFromURI(aMessageURI);
+ let neckoURI = msgSvc.getUrlForUri(aMessageURI);
+ return neckoURI.spec;
+}
+
+var smimeHeaderSink =
+{
+ maxWantedNesting: function()
+ {
+ return 1;
+ },
+
+ signedStatus: function(aNestingLevel, aSignatureStatus, aSignerCert)
+ {
+ if (aNestingLevel > 1) {
+ // we are not interested
+ return;
+ }
+
+ gSignatureStatus = aSignatureStatus;
+ gSignerCert = aSignerCert;
+
+ gSMIMEContainer.collapsed = false;
+ gSignedUINode.collapsed = false;
+ gSignedStatusPanel.collapsed = false;
+
+ switch (aSignatureStatus) {
+ case nsICMSMessageErrors.SUCCESS:
+ gSignedUINode.setAttribute("signed", "ok");
+ gStatusBar.setAttribute("signed", "ok");
+ break;
+
+ case nsICMSMessageErrors.VERIFY_NOT_YET_ATTEMPTED:
+ gSignedUINode.setAttribute("signed", "unknown");
+ gStatusBar.setAttribute("signed", "unknown");
+ break;
+
+ case nsICMSMessageErrors.VERIFY_CERT_WITHOUT_ADDRESS:
+ case nsICMSMessageErrors.VERIFY_HEADER_MISMATCH:
+ gSignedUINode.setAttribute("signed", "mismatch");
+ gStatusBar.setAttribute("signed", "mismatch");
+ break;
+
+ default:
+ gSignedUINode.setAttribute("signed", "notok");
+ gStatusBar.setAttribute("signed", "notok");
+ break;
+ }
+ },
+
+ encryptionStatus: function(aNestingLevel, aEncryptionStatus, aRecipientCert)
+ {
+ if (aNestingLevel > 1) {
+ // we are not interested
+ return;
+ }
+
+ gEncryptionStatus = aEncryptionStatus;
+ gEncryptionCert = aRecipientCert;
+
+ gSMIMEContainer.collapsed = false;
+ gEncryptedUINode.collapsed = false;
+ gEncryptedStatusPanel.collapsed = false;
+
+ if (nsICMSMessageErrors.SUCCESS == aEncryptionStatus)
+ {
+ gEncryptedUINode.setAttribute("encrypted", "ok");
+ gStatusBar.setAttribute("encrypted", "ok");
+ }
+ else
+ {
+ gEncryptedUINode.setAttribute("encrypted", "notok");
+ gStatusBar.setAttribute("encrypted", "notok");
+ }
+
+ if (gEncryptedURIService)
+ {
+ // Remember the message URI and the corresponding necko URI.
+ gMyLastEncryptedURI = GetLoadedMessage();
+ gEncryptedURIService.rememberEncrypted(gMyLastEncryptedURI);
+ gEncryptedURIService.rememberEncrypted(
+ neckoURLForMessageURI(gMyLastEncryptedURI));
+ }
+
+ switch (aEncryptionStatus)
+ {
+ case nsICMSMessageErrors.SUCCESS:
+ case nsICMSMessageErrors.ENCRYPT_INCOMPLETE:
+ break;
+ default:
+ var brand = gBrandBundle.getString("brandShortName");
+ var title = gSMIMEBundle.getString("CantDecryptTitle").replace(/%brand%/g, brand);
+ var body = gSMIMEBundle.getString("CantDecryptBody").replace(/%brand%/g, brand);
+
+ // insert our message
+ msgWindow.displayHTMLInMessagePane(title,
+ "<html>\n" +
+ "<body bgcolor=\"#fafaee\">\n" +
+ "<center><br><br><br>\n" +
+ "<table>\n" +
+ "<tr><td>\n" +
+ "<center><strong><font size=\"+3\">\n" +
+ title+"</font></center><br>\n" +
+ body+"\n" +
+ "</td></tr></table></center></body></html>", false);
+ }
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIMsgSMIMEHeaderSink"]),
+};
+
+function forgetEncryptedURI()
+{
+ if (gMyLastEncryptedURI && gEncryptedURIService)
+ {
+ gEncryptedURIService.forgetEncrypted(gMyLastEncryptedURI);
+ gEncryptedURIService.forgetEncrypted(
+ neckoURLForMessageURI(gMyLastEncryptedURI));
+ gMyLastEncryptedURI = null;
+ }
+}
+
+function onSMIMEStartHeaders()
+{
+ gEncryptionStatus = -1;
+ gSignatureStatus = -1;
+
+ gSignerCert = null;
+ gEncryptionCert = null;
+
+ gSMIMEContainer.collapsed = true;
+
+ gSignedUINode.collapsed = true;
+ gSignedUINode.removeAttribute("signed");
+ gSignedStatusPanel.collapsed = true;
+ gStatusBar.removeAttribute("signed");
+
+ gEncryptedUINode.collapsed = true;
+ gEncryptedUINode.removeAttribute("encrypted");
+ gEncryptedStatusPanel.collapsed = true;
+ gStatusBar.removeAttribute("encrypted");
+
+ forgetEncryptedURI();
+}
+
+function onSMIMEEndHeaders()
+{}
+
+function onSmartCardChange()
+{
+ // only reload encrypted windows
+ if (gMyLastEncryptedURI && gEncryptionStatus != -1)
+ ReloadMessage();
+}
+
+function msgHdrViewSMIMEOnLoad(event)
+{
+ window.crypto.enableSmartCardEvents = true;
+ document.addEventListener("smartcard-insert", onSmartCardChange);
+ document.addEventListener("smartcard-remove", onSmartCardChange);
+ if (!gSMIMEBundle)
+ gSMIMEBundle = document.getElementById("bundle_read_smime");
+
+ // we want to register our security header sink as an opaque nsISupports
+ // on the msgHdrSink used by mail.....
+ msgWindow.msgHeaderSink.securityInfo = smimeHeaderSink;
+
+ gSignedUINode = document.getElementById('signedHdrIcon');
+ gEncryptedUINode = document.getElementById('encryptedHdrIcon');
+ gSMIMEContainer = document.getElementById('smimeBox');
+ gStatusBar = document.getElementById('status-bar');
+ gSignedStatusPanel = document.getElementById('signed-status');
+ gEncryptedStatusPanel = document.getElementById('encrypted-status');
+
+ // add ourself to the list of message display listeners so we get notified when we are about to display a
+ // message.
+ var listener = {};
+ listener.onStartHeaders = onSMIMEStartHeaders;
+ listener.onEndHeaders = onSMIMEEndHeaders;
+ gMessageListeners.push(listener);
+
+ gEncryptedURIService =
+ Cc["@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"]
+ .getService(Ci.nsIEncryptedSMIMEURIsService);
+}
+
+function msgHdrViewSMIMEOnUnload(event)
+{
+ window.crypto.enableSmartCardEvents = false;
+ document.removeEventListener("smartcard-insert", onSmartCardChange);
+ document.removeEventListener("smartcard-remove", onSmartCardChange);
+ forgetEncryptedURI();
+ removeEventListener("messagepane-loaded", msgHdrViewSMIMEOnLoad, true);
+ removeEventListener("messagepane-unloaded", msgHdrViewSMIMEOnUnload, true);
+ removeEventListener("messagepane-hide", msgHdrViewSMIMEOnMessagePaneHide, true);
+ removeEventListener("messagepane-unhide", msgHdrViewSMIMEOnMessagePaneUnhide, true);
+}
+
+function msgHdrViewSMIMEOnMessagePaneHide()
+{
+ gSMIMEContainer.collapsed = true;
+ gSignedUINode.collapsed = true;
+ gSignedStatusPanel.collapsed = true;
+ gEncryptedUINode.collapsed = true;
+ gEncryptedStatusPanel.collapsed = true;
+}
+
+function msgHdrViewSMIMEOnMessagePaneUnhide()
+{
+ if (gEncryptionStatus != -1 || gSignatureStatus != -1)
+ {
+ gSMIMEContainer.collapsed = false;
+
+ if (gSignatureStatus != -1)
+ {
+ gSignedUINode.collapsed = false;
+ gSignedStatusPanel.collapsed = false;
+ }
+
+ if (gEncryptionStatus != -1)
+ {
+ gEncryptedUINode.collapsed = false;
+ gEncryptedStatusPanel.collapsed = false;
+ }
+ }
+}
+
+addEventListener('messagepane-loaded', msgHdrViewSMIMEOnLoad, true);
+addEventListener('messagepane-unloaded', msgHdrViewSMIMEOnUnload, true);
+addEventListener('messagepane-hide', msgHdrViewSMIMEOnMessagePaneHide, true);
+addEventListener('messagepane-unhide', msgHdrViewSMIMEOnMessagePaneUnhide, true);
diff --git a/comm/suite/mailnews/components/smime/jar.mn b/comm/suite/mailnews/components/smime/jar.mn
new file mode 100644
index 0000000000..2f1f63cf32
--- /dev/null
+++ b/comm/suite/mailnews/components/smime/jar.mn
@@ -0,0 +1,15 @@
+# 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/.
+
+messenger.jar:
+% content messenger-smime %content/messenger-smime/
+ content/messenger/certpicker.js (../../../../mailnews/extensions/smime/certpicker.js)
+ content/messenger/certpicker.xhtml (../../../../mailnews/extensions/smime/certpicker.xhtml)
+ content/messenger-smime/msgHdrViewSMIMEOverlay.js (content/msgHdrViewSMIMEOverlay.js)
+ content/messenger-smime/msgCompSMIMEOverlay.js (content/msgCompSMIMEOverlay.js)
+ content/messenger-smime/msgReadSMIMEOverlay.js (../../../../mailnews/extensions/smime/msgReadSMIMEOverlay.js)
+ content/messenger-smime/msgCompSecurityInfo.js (../../../../mailnews/extensions/smime/msgCompSecurityInfo.js)
+ content/messenger-smime/msgCompSecurityInfo.xhtml (../../../../mailnews/extensions/smime/msgCompSecurityInfo.xhtml)
+ content/messenger-smime/certFetchingStatus.js (../../../../mailnews/extensions/smime/certFetchingStatus.js)
+ content/messenger-smime/certFetchingStatus.xhtml (../../../../mailnews/extensions/smime/certFetchingStatus.xhtml)
diff --git a/comm/suite/mailnews/components/smime/moz.build b/comm/suite/mailnews/components/smime/moz.build
new file mode 100644
index 0000000000..de5cd1bf81
--- /dev/null
+++ b/comm/suite/mailnews/components/smime/moz.build
@@ -0,0 +1,6 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/comm/suite/mailnews/content/ABSearchDialog.js b/comm/suite/mailnews/content/ABSearchDialog.js
new file mode 100644
index 0000000000..15d85b6234
--- /dev/null
+++ b/comm/suite/mailnews/content/ABSearchDialog.js
@@ -0,0 +1,327 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {encodeABTermValue} = ChromeUtils.import("resource:///modules/ABQueryUtils.jsm");
+const {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+
+var searchSessionContractID = "@mozilla.org/messenger/searchSession;1";
+var gSearchSession;
+
+var nsMsgSearchScope = Ci.nsMsgSearchScope;
+var nsMsgSearchOp = Ci.nsMsgSearchOp;
+var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+var nsIAbDirectory = Ci.nsIAbDirectory;
+
+var gStatusText;
+var gSearchBundle;
+var gAddressBookBundle;
+
+var gSearchStopButton;
+var gPropertiesButton;
+var gComposeButton;
+var gSearchPhoneticName = "false";
+
+var gSearchAbViewListener = {
+ onSelectionChanged: function() {
+ UpdateCardView();
+ },
+ onCountChanged: function(aTotal) {
+ let statusText;
+ if (aTotal == 0) {
+ statusText = gAddressBookBundle.getString("noMatchFound");
+ } else {
+ statusText = PluralForm
+ .get(aTotal, gAddressBookBundle.getString("matchesFound1"))
+ .replace("#1", aTotal);
+ }
+
+ gStatusText.setAttribute("label", statusText);
+ }
+};
+
+function searchOnLoad()
+{
+ setHelpFileURI("chrome://communicator/locale/help/suitehelp.rdf");
+ UpgradeAddressBookResultsPaneUI("mailnews.ui.advanced_directory_search_results.version");
+
+ initializeSearchWidgets();
+ initializeSearchWindowWidgets();
+
+ gSearchBundle = document.getElementById("bundle_search");
+ gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForSearchButton"));
+ gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForSearchButton.accesskey"));
+ gAddressBookBundle = document.getElementById("bundle_addressBook");
+ gSearchSession = Cc[searchSessionContractID].createInstance(Ci.nsIMsgSearchSession);
+
+ // initialize a flag for phonetic name search
+ gSearchPhoneticName =
+ GetLocalizedStringPref("mail.addr_book.show_phonetic_fields");
+
+ if (window.arguments && window.arguments[0])
+ SelectDirectory(window.arguments[0].directory);
+ else
+ SelectDirectory(document.getElementById("abPopup-menupopup")
+ .firstChild.value);
+
+ // initialize globals, see abCommon.js, InitCommonJS()
+ abList = document.getElementById("abPopup");
+
+ onMore(null);
+}
+
+function searchOnUnload()
+{
+ CloseAbView();
+}
+
+function initializeSearchWindowWidgets()
+{
+ gSearchStopButton = document.getElementById("search-button");
+ gPropertiesButton = document.getElementById("propertiesButton");
+ gComposeButton = document.getElementById("composeButton");
+ gStatusText = document.getElementById('statusText');
+ // matchAll doesn't make sense for address book search
+ hideMatchAllItem();
+}
+
+function onSearchStop()
+{
+}
+
+function onAbSearchReset(event)
+{
+ gPropertiesButton.setAttribute("disabled","true");
+ gComposeButton.setAttribute("disabled","true");
+
+ CloseAbView();
+
+ onReset(event);
+ gStatusText.setAttribute("label", "");
+}
+
+function SelectDirectory(aURI)
+{
+ var selectedAB = aURI;
+
+ if (!selectedAB)
+ selectedAB = kPersonalAddressbookURI;
+
+ // set popup with address book names
+ var abPopup = document.getElementById('abPopup');
+ if ( abPopup )
+ abPopup.value = selectedAB;
+
+ setSearchScope(GetScopeForDirectoryURI(selectedAB));
+}
+
+function GetScopeForDirectoryURI(aURI)
+{
+ var directory = MailServices.ab.getDirectory(aURI);
+ var booleanAnd = gSearchBooleanRadiogroup.selectedItem.value == "and";
+
+ if (directory.isRemote) {
+ if (booleanAnd)
+ return nsMsgSearchScope.LDAPAnd;
+ else
+ return nsMsgSearchScope.LDAP;
+ }
+ else {
+ if (booleanAnd)
+ return nsMsgSearchScope.LocalABAnd;
+ else
+ return nsMsgSearchScope.LocalAB;
+ }
+}
+
+function onEnterInSearchTerm()
+{
+ // on enter
+ // if not searching, start the search
+ // if searching, stop and then start again
+ if (gSearchStopButton.getAttribute("label") == gSearchBundle.getString("labelForSearchButton")) {
+ onSearch();
+ }
+ else {
+ onSearchStop();
+ onSearch();
+ }
+}
+
+function onSearch()
+{
+ gStatusText.setAttribute("label", "");
+ gPropertiesButton.setAttribute("disabled","true");
+ gComposeButton.setAttribute("disabled","true");
+
+ gSearchSession.clearScopes();
+
+ var currentAbURI = document.getElementById('abPopup').getAttribute('value');
+
+ gSearchSession.addDirectoryScopeTerm(GetScopeForDirectoryURI(currentAbURI));
+ gSearchSession.searchTerms = saveSearchTerms(gSearchSession.searchTerms, gSearchSession);
+
+ var searchUri = currentAbURI + "?(";
+
+ for (let i = 0; i < gSearchSession.searchTerms.length; i++) {
+ let searchTerm = gSearchSession.searchTerms[i];
+
+ // get the "and" / "or" value from the first term
+ if (i == 0) {
+ if (searchTerm.booleanAnd)
+ searchUri += "and";
+ else
+ searchUri += "or";
+ }
+
+ var attrs;
+
+ switch (searchTerm.attrib) {
+ case nsMsgSearchAttrib.Name:
+ if (gSearchPhoneticName != "true")
+ attrs = ["DisplayName","FirstName","LastName","NickName"];
+ else
+ attrs = ["DisplayName","FirstName","LastName","NickName","PhoneticFirstName","PhoneticLastName"];
+ break;
+ case nsMsgSearchAttrib.DisplayName:
+ attrs = ["DisplayName"];
+ break;
+ case nsMsgSearchAttrib.Email:
+ attrs = ["PrimaryEmail"];
+ break;
+ case nsMsgSearchAttrib.PhoneNumber:
+ attrs = ["HomePhone","WorkPhone","FaxNumber","PagerNumber","CellularNumber"];
+ break;
+ case nsMsgSearchAttrib.Organization:
+ attrs = ["Company"];
+ break;
+ case nsMsgSearchAttrib.Department:
+ attrs = ["Department"];
+ break;
+ case nsMsgSearchAttrib.City:
+ attrs = ["WorkCity"];
+ break;
+ case nsMsgSearchAttrib.Street:
+ attrs = ["WorkAddress"];
+ break;
+ case nsMsgSearchAttrib.Nickname:
+ attrs = ["NickName"];
+ break;
+ case nsMsgSearchAttrib.WorkPhone:
+ attrs = ["WorkPhone"];
+ break;
+ case nsMsgSearchAttrib.HomePhone:
+ attrs = ["HomePhone"];
+ break;
+ case nsMsgSearchAttrib.Fax:
+ attrs = ["FaxNumber"];
+ break;
+ case nsMsgSearchAttrib.Pager:
+ attrs = ["PagerNumber"];
+ break;
+ case nsMsgSearchAttrib.Mobile:
+ attrs = ["CellularNumber"];
+ break;
+ case nsMsgSearchAttrib.Title:
+ attrs = ["JobTitle"];
+ break;
+ case nsMsgSearchAttrib.AdditionalEmail:
+ attrs = ["SecondEmail"];
+ break;
+ default:
+ dump("XXX " + searchTerm.attrib + " not a supported search attr!\n");
+ attrs = ["DisplayName"];
+ break;
+ }
+
+ var opStr;
+
+ switch (searchTerm.op) {
+ case nsMsgSearchOp.Contains:
+ opStr = "c";
+ break;
+ case nsMsgSearchOp.DoesntContain:
+ opStr = "!c";
+ break;
+ case nsMsgSearchOp.Is:
+ opStr = "=";
+ break;
+ case nsMsgSearchOp.Isnt:
+ opStr = "!=";
+ break;
+ case nsMsgSearchOp.BeginsWith:
+ opStr = "bw";
+ break;
+ case nsMsgSearchOp.EndsWith:
+ opStr = "ew";
+ break;
+ case nsMsgSearchOp.SoundsLike:
+ opStr = "~=";
+ break;
+ default:
+ opStr = "c";
+ break;
+ }
+
+ // currently, we can't do "and" and "or" searches at the same time
+ // (it's either all "and"s or all "or"s)
+ var max_attrs = attrs.length;
+
+ for (var j=0;j<max_attrs;j++) {
+ // append the term(s) to the searchUri
+ searchUri += "(" + attrs[j] + "," + opStr + "," + encodeABTermValue(searchTerm.value.str) + ")";
+ }
+ }
+
+ searchUri += ")";
+ SetAbView(searchUri);
+}
+
+// used to toggle functionality for Search/Stop button.
+function onSearchButton(event)
+{
+ if (event.target.label == gSearchBundle.getString("labelForSearchButton"))
+ onSearch();
+ else
+ onSearchStop();
+}
+
+function GetAbViewListener()
+{
+ return gSearchAbViewListener;
+}
+
+function onProperties()
+{
+ AbEditSelectedCard();
+}
+
+function onCompose()
+{
+ AbNewMessage();
+}
+
+function AbResultsPaneDoubleClick(card)
+{
+ AbEditCard(card);
+}
+
+function UpdateCardView()
+{
+ var numSelected = GetNumSelectedCards();
+
+ if (!numSelected) {
+ gPropertiesButton.setAttribute("disabled","true");
+ gComposeButton.setAttribute("disabled","true");
+ return;
+ }
+
+ gComposeButton.removeAttribute("disabled");
+
+ if (numSelected == 1)
+ gPropertiesButton.removeAttribute("disabled");
+ else
+ gPropertiesButton.setAttribute("disabled","true");
+}
diff --git a/comm/suite/mailnews/content/ABSearchDialog.xul b/comm/suite/mailnews/content/ABSearchDialog.xul
new file mode 100644
index 0000000000..a32c7dbb8b
--- /dev/null
+++ b/comm/suite/mailnews/content/ABSearchDialog.xul
@@ -0,0 +1,99 @@
+<?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://messenger/skin/searchDialog.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/addressbook/abResultsPaneOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/searchTermOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % abResultsPaneOverlayDTD SYSTEM "chrome://messenger/locale/addressbook
+/abResultsPaneOverlay.dtd">
+%abResultsPaneOverlayDTD;
+<!ENTITY % SearchDialogDTD SYSTEM "chrome://messenger/locale/SearchDialog.dtd">
+%SearchDialogDTD;
+]>
+
+<dialog id="searchAddressBookWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ windowtype="mailnews:absearch"
+ title="&abSearchDialogTitle.label;"
+ style="width: 52em; height: 34em;"
+ persist="screenX screenY width height sizemode"
+ buttons="help"
+ ondialoghelp="return openHelp('mail_advanced_ab_search');"
+ onload="searchOnLoad();"
+ onunload="onSearchStop(); searchOnUnload();">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
+ <stringbundle id="bundle_search" src="chrome://messenger/locale/search.properties"/>
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://messenger/content/mailWindow.js"/>
+ <script src="chrome://messenger/content/msgMail3PaneWindow.js"/>
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://messenger/content/commandglue.js"/>
+ <script src="chrome://messenger/content/ABSearchDialog.js"/>
+ <script src="chrome://messenger/content/addressbook/abCommon.js"/>
+
+ <broadcaster id="Communicator:WorkMode"/>
+
+ <dummy class="usesMailWidgets"/>
+
+ <vbox id="searchTerms" flex="3" persist="height">
+ <vbox>
+ <hbox align="center">
+ <label value="&abSearchHeading.label;" accesskey="&abSearchHeading.accesskey;" control="abPopup"/>
+ <menulist id="abPopup" oncommand="SelectDirectory(this.value);">
+ <menupopup id="abPopup-menupopup" class="addrbooksPopup"/>
+ </menulist>
+ <spacer flex="10"/>
+ <button id="search-button" oncommand="onSearchButton(event);" default="true"/>
+ </hbox>
+ <hbox align="center">
+ <spacer flex="1"/>
+ <button label="&resetButton.label;" oncommand="onAbSearchReset(event);" accesskey="&resetButton.accesskey;"/>
+ </hbox>
+ </vbox>
+
+ <hbox flex="1">
+ <vbox id="searchTermListBox" flex="1"/>
+ </hbox>
+ </vbox>
+
+ <splitter id="gray_horizontal_splitter" collapse="after" persist="state">
+ <grippy/>
+ </splitter>
+
+ <vbox id="searchResults" flex="4" persist="height">
+ <vbox id="searchResultListBox" flex="1">
+ <tree id="abResultsTree" flex="1" context="threadPaneContext"/>
+ </vbox>
+ <hbox align="center">
+ <button id="propertiesButton"
+ label="&propertiesButton.label;"
+ accesskey="&propertiesButton.accesskey;"
+ disabled="true"
+ oncommand="onProperties();"/>
+ <button id="composeButton"
+ label="&composeButton.label;"
+ accesskey="&composeButton.accesskey;"
+ disabled="true"
+ oncommand="onCompose();"/>
+ <spacer flex="1"/>
+ <button dlgtype="help" class="dialog-button"/>
+ </hbox>
+ </vbox>
+
+ <statusbar class="chromeclass-status" id="status-bar">
+ <statusbarpanel id="statusText" crop="right" flex="1"/>
+ <statusbarpanel id="offline-status" class="statusbarpanel-iconic"/>
+ </statusbar>
+
+</dialog>
diff --git a/comm/suite/mailnews/content/FilterListDialog.js b/comm/suite/mailnews/content/FilterListDialog.js
new file mode 100644
index 0000000000..555796fc5e
--- /dev/null
+++ b/comm/suite/mailnews/content/FilterListDialog.js
@@ -0,0 +1,1037 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { PluralForm } = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+
+var gEditButton;
+var gDeleteButton;
+var gNewButton;
+var gCopyToNewButton;
+var gTopButton;
+var gUpButton;
+var gDownButton;
+var gBottomButton;
+var gSearchBox;
+var gRunFiltersFolderPrefix;
+var gRunFiltersFolder;
+var gRunFiltersButton;
+var gFilterBundle;
+var gFilterListMsgWindow = null;
+var gFilterListbox;
+var gCurrentFilterList;
+var gStatusBar;
+var gStatusText;
+var gServerMenu;
+
+var msgMoveMotion = {
+ Up : 0,
+ Down : 1,
+ Top : 2,
+ Bottom : 3,
+}
+
+var gStatusFeedback = {
+ showStatusString: function(status)
+ {
+ gStatusText.setAttribute("value", status);
+ },
+ startMeteors: function()
+ {
+ // change run button to be a stop button
+ gRunFiltersButton.setAttribute("label", gRunFiltersButton.getAttribute("stoplabel"));
+ gRunFiltersButton.setAttribute("accesskey", gRunFiltersButton.getAttribute("stopaccesskey"));
+ gStatusBar.setAttribute("mode", "undetermined");
+ },
+ stopMeteors: function()
+ {
+ try {
+ // change run button to be a stop button
+ gRunFiltersButton.setAttribute("label", gRunFiltersButton.getAttribute("runlabel"));
+ gRunFiltersButton.setAttribute("accesskey", gRunFiltersButton.getAttribute("runaccesskey"));
+ gStatusBar.setAttribute("mode", "normal");
+ }
+ catch (ex) {
+ // can get here if closing window when running filters
+ }
+ },
+ showProgress: function(percentage)
+ {
+ },
+ closeWindow: function()
+ {
+ }
+};
+
+function onLoad()
+{
+ setHelpFileURI("chrome://communicator/locale/help/suitehelp.rdf");
+ gFilterListMsgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance(Ci.nsIMsgWindow);
+ gFilterListMsgWindow.domWindow = window;
+ gFilterListMsgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL;
+ gFilterListMsgWindow.statusFeedback = gStatusFeedback;
+
+ gFilterBundle = document.getElementById("bundle_filter");
+
+ gServerMenu = document.getElementById("serverMenu");
+ gFilterListbox = document.getElementById("filterList");
+
+ gEditButton = document.getElementById("editButton");
+ gDeleteButton = document.getElementById("deleteButton");
+ gNewButton = document.getElementById("newButton");
+ gCopyToNewButton = document.getElementById("copyToNewButton");
+ gTopButton = document.getElementById("reorderTopButton");
+ gUpButton = document.getElementById("reorderUpButton");
+ gDownButton = document.getElementById("reorderDownButton");
+ gBottomButton = document.getElementById("reorderBottomButton");
+ gSearchBox = document.getElementById("searchBox");
+ gRunFiltersFolderPrefix = document.getElementById("folderPickerPrefix");
+ gRunFiltersFolder = document.getElementById("runFiltersFolder");
+ gRunFiltersButton = document.getElementById("runFiltersButton");
+ gStatusBar = document.getElementById("statusbar-icon");
+ gStatusText = document.getElementById("statusText");
+
+ updateButtons();
+
+ processWindowArguments(window.arguments[0]);
+
+ Services.obs.addObserver(onFilterClose,
+ "quit-application-requested");
+
+ top.controllers.insertControllerAt(0, gFilterController);
+}
+
+/**
+ * Processes arguments sent to this dialog when opened or refreshed.
+ *
+ * @param aArguments An object having members representing the arguments.
+ * { arg1: value1, arg2: value2, ... }
+ */
+function processWindowArguments(aArguments) {
+ let wantedFolder;
+ if ("folder" in aArguments)
+ wantedFolder = aArguments.folder;
+
+ // If a specific folder was requested, try to select it
+ // if we don't already show its server.
+ if (!gServerMenu._folder ||
+ (wantedFolder && (wantedFolder != gServerMenu._folder) &&
+ (wantedFolder.rootFolder != gServerMenu._folder))) {
+
+ // Get the folder where filters should be defined, if that server
+ // can accept filters.
+ let firstItem = getFilterFolderForSelection(wantedFolder);
+
+ // if the selected server cannot have filters, get the default server
+ // if the default server cannot have filters, check all accounts
+ // and get a server that can have filters.
+ if (!firstItem) {
+ var server = getServerThatCanHaveFilters();
+ if (server)
+ firstItem = server.rootFolder;
+ }
+
+ if (firstItem)
+ setFilterFolder(firstItem);
+
+ if (wantedFolder)
+ setRunFolder(wantedFolder);
+ } else {
+ // If we didn't change folder still redraw the list
+ // to show potential new filters if we were called for refresh.
+ rebuildFilterList();
+ }
+
+ // If a specific filter was requested, try to select it.
+ if ("filter" in aArguments)
+ selectFilter(aArguments.filter);
+}
+
+/**
+ * This is called from OpenOrFocusWindow() if the dialog is already open.
+ * New filters could have been created by operations outside the dialog.
+ *
+ * @param aArguments An object of arguments having the same format
+ * as window.arguments[0].
+ */
+function refresh(aArguments) {
+ // As we really don't know what has changed, clear the search box
+ // unconditionally so that the changed/added filters are surely visible.
+ resetSearchBox();
+
+ processWindowArguments(aArguments);
+}
+
+function CanRunFiltersAfterTheFact(aServer)
+{
+ // filter after the fact is implement using search
+ // so if you can't search, you can't filter after the fact
+ return aServer.canSearchMessages;
+}
+
+/**
+ * Change the root server for which we are managing filters.
+ *
+ * @param msgFolder The nsIMsgFolder server containing filters
+ * (or a folder for NNTP server).
+ */
+function setFilterFolder(msgFolder) {
+ if (!msgFolder || msgFolder == gServerMenu._folder)
+ return;
+
+ // Save the current filters to disk before switching because
+ // the dialog may be closed and we'll lose current filters.
+ if (gCurrentFilterList)
+ gCurrentFilterList.saveToDefaultFile();
+
+ // Setting this attribute should go away in bug 473009.
+ gServerMenu._folder = msgFolder;
+ // Calling this should go away in bug 802609.
+ gServerMenu.menupopup.selectFolder(msgFolder);
+
+ // Calling getEditableFilterList will detect any errors in
+ // msgFilterRules.dat, backup the file, and alert the user.
+ gCurrentFilterList = msgFolder.getEditableFilterList(gFilterListMsgWindow);
+ rebuildFilterList();
+
+ // Select the first item in the list, if there is one.
+ if (gFilterListbox.itemCount > 0)
+ gFilterListbox.selectItem(gFilterListbox.getItemAtIndex(0));
+
+ // This will get the deferred to account root folder, if server is deferred.
+ // We intentionally do this after setting the current server, as we want
+ // that to refer to the rootFolder for the actual server, not the
+ // deferred-to server, as current server is really a proxy for the
+ // server whose filters we are editing. But below here we are managing
+ // where the filters will get applied, which is on the deferred-to server.
+ msgFolder = msgFolder.server.rootMsgFolder;
+
+ // root the folder picker to this server
+ let runMenu = gRunFiltersFolder.menupopup;
+ runMenu._teardown();
+ runMenu._parentFolder = msgFolder;
+ runMenu._ensureInitialized();
+
+ var canFilterAfterTheFact = CanRunFiltersAfterTheFact(msgFolder.server);
+ gRunFiltersButton.hidden = !canFilterAfterTheFact;
+ gRunFiltersFolder.hidden = !canFilterAfterTheFact;
+ gRunFiltersFolderPrefix.hidden = !canFilterAfterTheFact;
+
+ if (canFilterAfterTheFact) {
+ let wantedFolder = null;
+ // For a given server folder, get the default run target folder or show
+ // "Choose Folder".
+ if (!msgFolder.isServer) {
+ wantedFolder = msgFolder;
+ } else {
+ try {
+ switch (msgFolder.server.type) {
+ case "nntp":
+ // For NNTP select the subscribed newsgroup.
+ wantedFolder = gServerMenu._folder;
+ break;
+ case "rss":
+ // Show "Choose Folder" for feeds.
+ wantedFolder = null;
+ break;
+ case "imap":
+ case "pop3":
+ case "none":
+ // Find Inbox for IMAP and POP or Local Folders,
+ // show "Choose Folder" if not found.
+ wantedFolder = msgFolder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Inbox);
+ break;
+ default:
+ // For other account types we don't know what's good to select,
+ // so show "Choose Folder".
+ wantedFolder = null;
+ }
+ } catch (e) {
+ Cu.reportError("Failed to select a suitable folder to run filters on: " + e);
+ wantedFolder = null;
+ }
+ }
+ // Select a useful first folder for the server.
+ setRunFolder(wantedFolder);
+ }
+}
+
+/**
+ * Select a folder on which filters are to be run.
+ *
+ * @param aFolder nsIMsgFolder folder to select.
+ */
+function setRunFolder(aFolder) {
+ // Setting this attribute should go away in bug 473009.
+ gRunFiltersFolder._folder = aFolder;
+ // Calling this should go away in bug 802609.
+ gRunFiltersFolder.menupopup.selectFolder(gRunFiltersFolder._folder);
+ updateButtons();
+}
+
+/**
+ * Toggle enabled state of a filter, in both the filter properties and the UI.
+ *
+ * @param aFilterItem an item (row) of the filter list to be toggled
+ */
+function toggleFilter(aFilterItem)
+{
+ let filter = aFilterItem._filter;
+ if (filter.unparseable && !filter.enabled)
+ {
+ Services.prompt.alert(window, null,
+ gFilterBundle.getFormattedString("cannotEnableIncompatFilter",
+ [document.getElementById("bundle_brand").getString("brandShortName")]));
+ return;
+ }
+ filter.enabled = !filter.enabled;
+
+ // Now update the checkbox
+ aFilterItem.childNodes[1].setAttribute("enabled", filter.enabled);
+ // For accessibility set the checked state on listitem
+ aFilterItem.setAttribute("aria-checked", filter.enabled);
+}
+
+/**
+ * Selects a specific filter in the filter list.
+ * The listbox view is scrolled to the corresponding item.
+ *
+ * @param aFilter The nsIMsgFilter to select.
+ *
+ * @return true/false indicating whether the filter was found and selected.
+ */
+function selectFilter(aFilter) {
+ if (currentFilter() == aFilter)
+ return true;
+
+ resetSearchBox(aFilter);
+
+ let filterCount = gCurrentFilterList.filterCount;
+ for (let i = 0; i < filterCount; i++) {
+ if (gCurrentFilterList.getFilterAt(i) == aFilter) {
+ gFilterListbox.ensureIndexIsVisible(i);
+ gFilterListbox.selectedIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns the currently selected filter. If multiple filters are selected,
+ * returns the first one. If none are selected, returns null.
+ */
+function currentFilter()
+{
+ let currentItem = gFilterListbox.selectedItem;
+ return currentItem ? currentItem._filter : null;
+}
+
+function onEditFilter()
+{
+ if (gEditButton.disabled)
+ return;
+
+ var selectedFilter = currentFilter();
+ if (!selectedFilter)
+ return;
+
+ let args = {filter: selectedFilter, filterList: gCurrentFilterList};
+
+ window.openDialog("chrome://messenger/content/FilterEditor.xul", "FilterEditor", "chrome,modal,titlebar,resizable,centerscreen", args);
+
+ if ("refresh" in args && args.refresh) {
+ // Reset search if edit was okay (name change might lead to hidden entry).
+ resetSearchBox(selectedFilter);
+ rebuildFilterList();
+ }
+}
+
+/**
+ * Handler function for the 'New...' buttons.
+ * Opens the filter dialog for creating a new filter.
+ */
+function onNewFilter() {
+ calculatePositionAndShowCreateFilterDialog({});
+}
+
+/**
+ * Handler function for the 'Copy...' button.
+ * Opens the filter dialog for copying the selected filter.
+ */
+function onCopyToNewFilter() {
+ if (gCopyToNewButton.disabled)
+ return;
+
+ let selectedFilter = currentFilter();
+ if (!selectedFilter)
+ return;
+
+ calculatePositionAndShowCreateFilterDialog({copiedFilter: selectedFilter});
+}
+
+/**
+ * Calculates the position for inserting the new filter,
+ * and then displays the create dialog.
+ *
+ * @param args The object containing the arguments for the dialog,
+ * passed to the filterEditorOnLoad() function.
+ * It will be augmented with the insertion position
+ * and global filters list properties by this function.
+ */
+function calculatePositionAndShowCreateFilterDialog(args) {
+ let selectedFilter = currentFilter();
+ // If no filter is selected use the first position.
+ let position = 0;
+ if (selectedFilter) {
+ // Get the position in the unfiltered list.
+ // - this is where the new filter should be inserted!
+ let filterCount = gCurrentFilterList.filterCount;
+ for (let i = 0; i < filterCount; i++) {
+ if (gCurrentFilterList.getFilterAt(i) == selectedFilter) {
+ position = i;
+ break;
+ }
+ }
+ }
+ args.filterPosition = position;
+ args.filterList = gCurrentFilterList;
+ args.refresh = false;
+
+ window.openDialog("chrome://messenger/content/FilterEditor.xul",
+ "FilterEditor",
+ "chrome,modal,titlebar,resizable,centerscreen", args);
+
+ if (args.refresh)
+ {
+ // On success: reset the search box if necessary!
+ resetSearchBox(args.newFilter);
+ rebuildFilterList();
+
+ // Select the new filter, it is at the position of previous selection.
+ gFilterListbox.selectItem(gFilterListbox.getItemAtIndex(position));
+ if (currentFilter() != args.newFilter)
+ Cu.reportError("Filter created at an unexpected position!");
+ }
+}
+
+function onDeleteFilter()
+{
+ if (gDeleteButton.disabled)
+ return;
+
+ let items = gFilterListbox.selectedItems;
+ if (!items.length)
+ return;
+
+ let checkValue = {value: false};
+ if (Services.prefs.getBoolPref("mailnews.filters.confirm_delete") &&
+ Services.prompt.confirmEx(window, null,
+ gFilterBundle.getString("deleteFilterConfirmation"),
+ Services.prompt.STD_YES_NO_BUTTONS,
+ '', '', '',
+ gFilterBundle.getString('dontWarnAboutDeleteCheckbox'),
+ checkValue))
+ return;
+
+ if (checkValue.value)
+ Services.prefs.setBoolPref("mailnews.filters.confirm_delete", false);
+
+ // Save filter position before the first selected one.
+ let newSelectionIndex = gFilterListbox.selectedIndex - 1;
+
+ // Must reverse the loop, as the items list shrinks when we delete.
+ for (let index = items.length - 1; index >= 0; --index) {
+ let item = items[index];
+ gCurrentFilterList.removeFilter(item._filter);
+ item.remove();
+ }
+ updateCountBox();
+
+ // Select filter above previously selected if one existed,
+ // otherwise the first one.
+ if (newSelectionIndex == -1 && gFilterListbox.itemCount > 0)
+ newSelectionIndex = 0;
+ if (newSelectionIndex > -1) {
+ gFilterListbox.selectedIndex = newSelectionIndex;
+ updateViewPosition(-1);
+ }
+}
+
+/**
+ * Move filter one step up in visible list.
+ */
+function onUp(event) {
+ moveFilter(msgMoveMotion.Up);
+}
+
+/**
+ * Move filter one step down in visible list.
+ */
+function onDown(event) {
+ moveFilter(msgMoveMotion.Down);
+}
+
+/**
+ * Move filter to bottom for long filter lists.
+ */
+function onTop(event) {
+ moveFilter(msgMoveMotion.Top);
+}
+
+/**
+ * Move filter to top for long filter lists.
+ */
+function onBottom(event) {
+ moveFilter(msgMoveMotion.Bottom);
+}
+
+/**
+ * Moves a singular selected filter up or down either 1 increment or to the
+ * top/bottom.
+ *
+ * @param motion
+ * msgMoveMotion.Up, msgMoveMotion.Down, msgMoveMotion.Top, msgMoveMotion.Bottom
+ */
+function moveFilter(motion) {
+ // At the moment, do not allow moving groups of filters.
+ let selectedFilter = currentFilter();
+ if (!selectedFilter)
+ return;
+
+ let relativeStep = 0;
+ let moveFilterNative;
+
+ switch (motion) {
+ case msgMoveMotion.Top:
+ if (selectedFilter) {
+ gCurrentFilterList.removeFilter(selectedFilter);
+ gCurrentFilterList.insertFilterAt(0, selectedFilter);
+ rebuildFilterList();
+ }
+ return;
+ case msgMoveMotion.Bottom:
+ if (selectedFilter) {
+ gCurrentFilterList.removeFilter(selectedFilter);
+ gCurrentFilterList.insertFilterAt(gCurrentFilterList.filterCount,
+ selectedFilter);
+ rebuildFilterList();
+ }
+ return;
+ case msgMoveMotion.Up:
+ relativeStep = -1;
+ moveFilterNative = Ci.nsMsgFilterMotion.up;
+ break;
+ case msgMoveMotion.Down:
+ relativeStep = +1;
+ moveFilterNative = Ci.nsMsgFilterMotion.down;
+ break;
+ }
+
+ if (!gSearchBox.value) {
+ // Use legacy move filter code: up, down; only if searchBox is empty.
+ moveCurrentFilter(moveFilterNative);
+ return;
+ }
+
+ let nextIndex = gFilterListbox.selectedIndex + relativeStep;
+ let nextFilter = gFilterListbox.getItemAtIndex(nextIndex)._filter;
+
+ gCurrentFilterList.removeFilter(selectedFilter);
+
+ // Find the index of the filter we want to insert at.
+ let newIndex = -1;
+ let filterCount = gCurrentFilterList.filterCount;
+ for (let i = 0; i < filterCount; i++) {
+ if (gCurrentFilterList.getFilterAt(i) == nextFilter) {
+ newIndex = i;
+ break;
+ }
+ }
+
+ if (motion == msgMoveMotion.Down)
+ newIndex += relativeStep;
+
+ gCurrentFilterList.insertFilterAt(newIndex, selectedFilter);
+
+ rebuildFilterList();
+}
+
+function viewLog()
+{
+ let args = {filterList: gCurrentFilterList};
+
+ window.openDialog("chrome://messenger/content/viewLog.xul", "FilterLog", "chrome,modal,titlebar,resizable,centerscreen", args);
+}
+
+function onFilterUnload()
+{
+ // make sure to save the filter to disk
+ if (gCurrentFilterList)
+ gCurrentFilterList.saveToDefaultFile();
+
+ Services.obs.removeObserver(onFilterClose, "quit-application-requested");
+ top.controllers.removeController(gFilterController);
+}
+
+function onFilterClose(aCancelQuit, aTopic, aData)
+{
+ if (aTopic == "quit-application-requested" &&
+ aCancelQuit instanceof Ci.nsISupportsPRBool &&
+ aCancelQuit.data)
+ return false;
+
+ if (gRunFiltersButton.getAttribute("label") == gRunFiltersButton.getAttribute("stoplabel")) {
+ var promptTitle = gFilterBundle.getString("promptTitle");
+ var promptMsg = gFilterBundle.getString("promptMsg");;
+ var stopButtonLabel = gFilterBundle.getString("stopButtonLabel");
+ var continueButtonLabel = gFilterBundle.getString("continueButtonLabel");
+
+ if (Services.prompt.confirmEx(window, promptTitle, promptMsg,
+ (Services.prompt.BUTTON_TITLE_IS_STRING *
+ Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1),
+ continueButtonLabel, stopButtonLabel, null, null, {value:0}) == 0) {
+ if (aTopic == "quit-application-requested")
+ aCancelQuit.data = true;
+ return false;
+ }
+ gFilterListMsgWindow.StopUrls();
+ }
+
+ return true;
+}
+
+function runSelectedFilters()
+{
+ // if run button has "stop" label, do stop.
+ if (gRunFiltersButton.getAttribute("label") == gRunFiltersButton.getAttribute("stoplabel")) {
+ gFilterListMsgWindow.StopUrls();
+ return;
+ }
+
+ let folder = gRunFiltersFolder._folder ||
+ gRunFiltersFolder.selectedItem._folder;
+ if (!folder)
+ return;
+
+ let filterList = MailServices.filters.getTempFilterList(folder);
+
+ // make sure the tmp filter list uses the real filter list log stream
+ filterList.logStream = gCurrentFilterList.logStream;
+ filterList.loggingEnabled = gCurrentFilterList.loggingEnabled;
+
+ let index = 0;
+ for (let item of gFilterListbox.selectedItems) {
+ filterList.insertFilterAt(index++, item._filter);
+ }
+
+ MailServices.filters.applyFiltersToFolders(filterList, [folder], gFilterListMsgWindow);
+}
+
+function moveCurrentFilter(motion)
+{
+ let filter = currentFilter();
+ if (!filter)
+ return;
+
+ gCurrentFilterList.moveFilter(filter, motion);
+ rebuildFilterList();
+}
+
+/**
+ * Redraws the list of filters. Takes the search box value into account.
+ *
+ * This function should perform very fast even in case of high number of filters.
+ * Therefore there are some optimizations (e.g. listelement.children[] instead of
+ * list.getItemAtIndex()), that favour speed vs. semantical perfection.
+ */
+function rebuildFilterList()
+{
+ // Get filters that match the search box.
+ let aTempFilterList = onFindFilter();
+
+ let searchBoxFocus = false;
+ let activeElement = document.activeElement;
+
+ // Find if the currently focused element is a child inside the search box
+ // (probably html:input). Traverse up the parents until the first element
+ // with an ID is found. If it is not searchBox, return false.
+ while (activeElement != null) {
+ if (activeElement == gSearchBox) {
+ searchBoxFocus = true;
+ break;
+ }
+ else if (activeElement.id) {
+ searchBoxFocus = false;
+ break;
+ }
+ activeElement = activeElement.parentNode;
+ }
+
+ // Make a note of which filters were previously selected
+ let selectedNames = [];
+ for (let i = 0; i < gFilterListbox.selectedItems.length; i++)
+ selectedNames.push(gFilterListbox.selectedItems[i]._filter.filterName);
+
+ // Save scroll position so we can try to restore it later.
+ // Doesn't work when the list is rebuilt after search box condition changed.
+ let firstVisibleRowIndex = gFilterListbox.getIndexOfFirstVisibleRow();
+
+ // listbox.xml seems to cache the value of the first selected item in a
+ // range at _selectionStart. The old value though is now obsolete,
+ // since we will recreate all of the elements. We need to clear this,
+ // and one way to do this is with a call to clearSelection. This might be
+ // ugly from an accessibility perspective, since it fires an onSelect event.
+ gFilterListbox.clearSelection();
+
+ let listitem, nameCell, enabledCell, filter;
+ let filterCount = gCurrentFilterList.filterCount;
+ let listitemCount = gFilterListbox.itemCount;
+ let listitemIndex = 0;
+ let tempFilterListLength = aTempFilterList ? aTempFilterList.length - 1 : 0;
+ for (let i = 0; i < filterCount; i++) {
+ if (aTempFilterList && listitemIndex > tempFilterListLength)
+ break;
+
+ filter = gCurrentFilterList.getFilterAt(i);
+ if (aTempFilterList && aTempFilterList[listitemIndex] != i)
+ continue;
+
+ if (listitemCount > listitemIndex) {
+ // If there is a free existing listitem, reuse it.
+ // Use .children[] instead of .getItemAtIndex() as it is much faster.
+ listitem = gFilterListbox.children[listitemIndex + 1];
+ nameCell = listitem.childNodes[0];
+ enabledCell = listitem.childNodes[1];
+ }
+ else
+ {
+ // If there are not enough listitems in the list, create a new one.
+ listitem = document.createElement("listitem");
+ listitem.setAttribute("role", "checkbox");
+ nameCell = document.createElement("listcell");
+ enabledCell = document.createElement("listcell");
+ enabledCell.setAttribute("class", "listcell-iconic");
+ listitem.appendChild(nameCell);
+ listitem.appendChild(enabledCell);
+ gFilterListbox.appendChild(listitem);
+ let size = (enabledCell.clientWidth - 28) / 2;
+ enabledCell.style.paddingLeft = size.toString() + "px";
+ // We have to attach this listener to the listitem, even though we only
+ // care about clicks on the enabledCell. However, attaching to that item
+ // doesn't result in any events actually getting received.
+ listitem.addEventListener("click", onFilterClick, true);
+ listitem.addEventListener("dblclick", onFilterDoubleClick, true);
+ }
+ // For accessibility set the label on listitem.
+ listitem.setAttribute("label", filter.filterName);
+ // Set the listitem values to represent the current filter.
+ nameCell.setAttribute("label", filter.filterName);
+ enabledCell.setAttribute("enabled", filter.enabled);
+ listitem.setAttribute("aria-checked", filter.enabled);
+ listitem._filter = filter;
+
+ if (selectedNames.includes(filter.filterName))
+ gFilterListbox.addItemToSelection(listitem);
+
+ listitemIndex++;
+ }
+ // Remove any superfluous listitems, if the number of filters shrunk.
+ for (let i = listitemCount - 1; i >= listitemIndex; i--) {
+ gFilterListbox.lastChild.remove();
+ }
+
+ updateViewPosition(firstVisibleRowIndex);
+ updateCountBox();
+
+ // If before rebuilding the list the searchbox was focused, focus it again.
+ // In any other case, focus the list.
+ if (searchBoxFocus)
+ gSearchBox.focus();
+ else
+ gFilterListbox.focus();
+}
+
+function updateViewPosition(firstVisibleRowIndex)
+{
+ if (firstVisibleRowIndex == -1)
+ firstVisibleRowIndex = gFilterListbox.getIndexOfFirstVisibleRow();
+
+ // Restore to the extent possible the scroll position.
+ if (firstVisibleRowIndex && gFilterListbox.itemCount)
+ gFilterListbox.scrollToIndex(Math.min(firstVisibleRowIndex,
+ gFilterListbox.itemCount - 1));
+
+ if (gFilterListbox.selectedCount) {
+ // Make sure that at least the first selected item is visible.
+ gFilterListbox.ensureElementIsVisible(gFilterListbox.selectedItems[0]);
+
+ // The current item should be the first selected item, so that keyboard
+ // selection extension can work.
+ gFilterListbox.currentItem = gFilterListbox.selectedItems[0];
+ }
+
+ updateButtons();
+}
+
+/**
+ * Try to only enable buttons that make sense
+ * - moving filters is currently only enabled for single selection
+ * also movement is restricted by searchBox and current selection position
+ * - edit only for single filters
+ * - delete / run only for one or more selected filters
+ */
+function updateButtons()
+{
+ var numFiltersSelected = gFilterListbox.selectedItems.length;
+ var oneFilterSelected = (numFiltersSelected == 1);
+
+ // "edit" only enabled when one filter selected
+ // or if we couldn't parse the filter.
+ let disabled = !oneFilterSelected || currentFilter().unparseable;
+ gEditButton.disabled = disabled;
+
+ // "copy" is the same as "edit".
+ gCopyToNewButton.disabled = disabled;
+
+ // "delete" only disabled when no filters are selected
+ gDeleteButton.disabled = !numFiltersSelected;
+
+ // we can run multiple filters on a folder
+ // so only disable this UI if no filters are selected
+ gRunFiltersFolderPrefix.disabled = !numFiltersSelected;
+ gRunFiltersFolder.disabled = !numFiltersSelected;
+ gRunFiltersButton.disabled = !numFiltersSelected ||
+ !gRunFiltersFolder._folder;
+
+ // "up" and "top" enabled only if one filter is selected,
+ // and it's not the first.
+ // Don't use gFilterListbox.currentIndex here, it's buggy when we've just
+ // changed the children in the list (via rebuildFilterList)
+ disabled = !(oneFilterSelected &&
+ gFilterListbox.getSelectedItem(0) != gFilterListbox.getItemAtIndex(0));
+ gUpButton.disabled = disabled;
+ gTopButton.disabled = disabled;
+
+ // "down" and "bottom" enabled only if one filter selected,
+ // and it's not the last.
+ disabled = !(oneFilterSelected &&
+ gFilterListbox.selectedIndex < gFilterListbox.itemCount - 1);
+ gDownButton.disabled = disabled;
+ gBottomButton.disabled = disabled;
+}
+
+/**
+ * Given a selected folder, returns the folder where filters should
+ * be defined (the root folder except for news) if the server can
+ * accept filters.
+ *
+ * @param nsIMsgFolder aFolder - selected folder, from window args
+ * @returns an nsIMsgFolder where the filter is defined
+ */
+function getFilterFolderForSelection(aFolder) {
+ if (!aFolder || !aFolder.server)
+ return null;
+
+ let rootFolder = aFolder.server.rootFolder;
+ if (rootFolder && rootFolder.isServer && rootFolder.server.canHaveFilters)
+ return (aFolder.server.type == "nntp") ? aFolder : rootFolder;
+
+ return null;
+}
+
+/**
+ * If the selected server cannot have filters, get the default server.
+ * If the default server cannot have filters, check all accounts
+ * and get a server that can have filters.
+ *
+ * @returns an nsIMsgIncomingServer
+ */
+function getServerThatCanHaveFilters()
+{
+ let defaultAccount = MailServices.accounts.defaultAccount;
+ if (defaultAccount) {
+ let defaultIncomingServer = defaultAccount.incomingServer;
+ // Check to see if default server can have filters.
+ if (defaultIncomingServer.canHaveFilters)
+ return defaultIncomingServer;
+ }
+
+ // if it cannot, check all accounts to find a server
+ // that can have filters
+ for (let currentServer of MailServices.accounts.allServers)
+ {
+ if (currentServer.canHaveFilters)
+ return currentServer;
+ }
+
+ return null;
+}
+
+function onFilterClick(event)
+{
+ // We only care about button 0 (left click) events.
+ if (event.button != 0)
+ return;
+
+ // Remember, we had to attach the click-listener to the whole listitem, so
+ // now we need to see if the clicked the enable-column
+ let toggle = event.target.childNodes[1];
+ if ((event.clientX < toggle.boxObject.x + toggle.boxObject.width) &&
+ (event.clientX > toggle.boxObject.x)) {
+ toggleFilter(event.target);
+ event.stopPropagation();
+ }
+}
+
+function onFilterDoubleClick(event)
+{
+ // We only care about button 0 (left click) events.
+ if (event.button != 0)
+ return;
+
+ onEditFilter();
+}
+
+function onFilterListKeyPress(aEvent) {
+ if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey || aEvent.shiftKey)
+ return;
+
+ if (aEvent.keyCode) {
+ switch (aEvent.keyCode) {
+ case KeyEvent.DOM_VK_INSERT:
+ if (!gNewButton.disabled)
+ onNewFilter();
+ break;
+ case KeyEvent.DOM_VK_DELETE:
+ if (!gDeleteButton.disabled)
+ onDeleteFilter();
+ break;
+ case KeyEvent.DOM_VK_RETURN:
+ if (!gEditButton.disabled)
+ onEditFilter();
+ break;
+ }
+ return;
+ }
+
+ switch (aEvent.charCode) {
+ case KeyEvent.DOM_VK_SPACE:
+ for (let item of gFilterListbox.selectedItems) {
+ toggleFilter(item);
+ }
+ break;
+ default:
+ gSearchBox.focus();
+ gSearchBox.value = String.fromCharCode(aEvent.charCode);
+ }
+}
+
+/**
+ * Decides if the given filter matches the given keyword.
+ *
+ * @param aFilter nsIMsgFilter to check
+ * @param aKeyword the string to find in the filter name
+ *
+ * @return True if the filter name contains the searched keyword.
+ Otherwise false. In the future this may be extended to match
+ other filter attributes.
+ */
+function filterSearchMatch(aFilter, aKeyword) {
+ return (aFilter.filterName.toLocaleLowerCase().includes(aKeyword))
+}
+
+/**
+ * Called from rebuildFilterList when the list needs to be redrawn.
+ * @return Uses the search term in search box, to produce an array of
+ * row (filter) numbers (indexes) that match the search term.
+ */
+function onFindFilter() {
+ let keyWord = gSearchBox.value.toLocaleLowerCase();
+
+ // If searchbox is empty, just return and let rebuildFilterList
+ // create an unfiltered list.
+ if (!keyWord)
+ return null;
+
+ // Rematch everything in the list, remove what doesn't match the search box.
+ let rows = gCurrentFilterList.filterCount;
+ let matchingFilterList = [];
+ // Use the full gCurrentFilterList, not the filterList listbox,
+ // which may already be filtered.
+ for (let i = 0; i < rows; i++) {
+ if (filterSearchMatch(gCurrentFilterList.getFilterAt(i), keyWord))
+ matchingFilterList.push(i);
+ }
+
+ return matchingFilterList;
+}
+
+/**
+ * Clear the search term in the search box if needed.
+ *
+ * @param aFilter If this nsIMsgFilter matches the search term,
+ * do not reset the box. If this is null,
+ * reset unconditionally.
+ */
+function resetSearchBox(aFilter) {
+ let keyword = gSearchBox.value.toLocaleLowerCase();
+ if (keyword && (!aFilter || !filterSearchMatch(aFilter, keyword)))
+ gSearchBox.reset();
+}
+
+/**
+ * Display "1 item", "11 items" or "4 of 10" if list is filtered via search box.
+ */
+function updateCountBox() {
+ let countBox = document.getElementById("countBox");
+ let sum = gCurrentFilterList.filterCount;
+ let len = gFilterListbox.itemCount;
+
+ if (len == sum) {
+ // "N items"
+ countBox.value = PluralForm.get(len, gFilterBundle.getString("filterCountItems"))
+ .replace("#1", len);
+ countBox.removeAttribute("filterActive");
+ } else {
+ // "N of M"
+ countBox.value = gFilterBundle.getFormattedString("filterCountVisibleOfTotal",
+ [len, sum]);
+ if (len == 0 && sum > 0)
+ countBox.setAttribute("filterActive", "nomatches");
+ else
+ countBox.setAttribute("filterActive", "matches");
+ }
+}
+
+function doHelpButton()
+{
+ openHelp("mail-filters");
+}
+
+var gFilterController =
+{
+ supportsCommand: function(aCommand)
+ {
+ return aCommand == "cmd_selectAll";
+ },
+
+ isCommandEnabled: function(aCommand)
+ {
+ return aCommand == "cmd_selectAll";
+ },
+
+ doCommand: function(aCommand)
+ {
+ if (aCommand == "cmd_selectAll")
+ gFilterListbox.selectAll();
+ },
+
+ onEvent: function(aEvent)
+ {
+ }
+};
diff --git a/comm/suite/mailnews/content/FilterListDialog.xul b/comm/suite/mailnews/content/FilterListDialog.xul
new file mode 100644
index 0000000000..95f6d473ae
--- /dev/null
+++ b/comm/suite/mailnews/content/FilterListDialog.xul
@@ -0,0 +1,197 @@
+<?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://messenger/skin/filterDialog.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/FilterListDialog.dtd">
+
+<dialog id="filterListDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="mailnews:filterlist"
+ title="&filterListDialog.title;"
+ style="width: 45em; height: 31em;"
+ persist="width height screenX screenY"
+ buttons="help"
+ ondialoghelp="return openHelp('mail-filters');"
+ onload="onLoad();"
+ onunload="onFilterUnload();"
+ onclose="return onFilterClose();">
+
+ <script src="chrome://messenger/content/FilterListDialog.js"/>
+
+ <stringbundle id="bundle_filter"
+ src="chrome://messenger/locale/filter.properties"/>
+ <stringbundle id="bundle_brand"
+ src="chrome://branding/locale/brand.properties"/>
+
+ <keyset id="filterKeys">
+ <key id="key_selectAll"/>
+ </keyset>
+
+ <hbox align="center">
+ <label value="&filtersForPrefix.label;"
+ accesskey="&filtersForPrefix.accesskey;"
+ control="serverMenu"/>
+
+ <menulist id="serverMenu"
+ class="folderMenuItem"
+ IsServer="true"
+ IsSecure="false"
+ ServerType="none">
+ <menupopup id="serverMenuPopup"
+ class="menulist-menupopup"
+ type="folder"
+ mode="filters"
+ expandFolders="nntp"
+ showFileHereLabel="true"
+ showAccountsFileHere="true"
+ oncommand="setFilterFolder(event.target._folder)"/>
+ </menulist>
+ <textbox id="searchBox"
+ class="searchBox"
+ flex="1"
+ type="search"
+ oncommand="rebuildFilterList();"
+ emptytext="&searchBox.emptyText;"
+ isempty="true"/>
+ </hbox>
+
+ <grid flex="1">
+ <columns>
+ <column flex="1"/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <separator class="thin"/>
+ </row>
+
+ <row>
+ <hbox>
+ <label id="filterHeader"
+ flex="1"
+ control="filterTree">&filterHeader.label;</label>
+ <label id="countBox"/>
+ </hbox>
+ </row>
+
+ <row flex="1">
+ <vbox>
+ <listbox id="filterList"
+ flex="1"
+ seltype="multiple"
+ onselect="updateButtons();"
+ onkeypress="onFilterListKeyPress(event);">
+ <listhead>
+ <listheader id="nameColumn"
+ label="&nameColumn.label;"
+ flex="1"/>
+ <listheader id="activeColumn"
+ label="&activeColumn.label;"
+ minwidth="40px"/>
+ </listhead>
+ </listbox>
+ </vbox>
+
+ <vbox>
+ <button id="newButton"
+ label="&newButton.label;"
+ accesskey="&newButton.accesskey;"
+ oncommand="onNewFilter();"/>
+ <button id="copyToNewButton"
+ label="&copyButton.label;"
+ accesskey="&copyButton.accesskey;"
+ oncommand="onCopyToNewFilter();"/>
+ <button id="editButton"
+ label="&editButton.label;"
+ accesskey="&editButton.accesskey;"
+ oncommand="onEditFilter();"/>
+ <button id="deleteButton"
+ label="&deleteButton.label;"
+ accesskey="&deleteButton.accesskey;"
+ oncommand="onDeleteFilter();"/>
+ <spacer flex="1"/>
+ <button id="reorderTopButton"
+ label="&reorderTopButton;"
+ accesskey="&reorderTopButton.accessKey;"
+ tooltiptext="&reorderTopButton.toolTip;"
+ oncommand="onTop(event);"/>
+ <button id="reorderUpButton"
+ label="&reorderUpButton.label;"
+ accesskey="&reorderUpButton.accesskey;"
+ class="up"
+ oncommand="onUp(event);"/>
+ <button id="reorderDownButton"
+ label="&reorderDownButton.label;"
+ accesskey="&reorderDownButton.accesskey;"
+ class="down"
+ oncommand="onDown(event);"/>
+ <button id="reorderBottomButton"
+ label="&reorderBottomButton;"
+ accesskey="&reorderBottomButton.accessKey;"
+ tooltiptext="&reorderBottomButton.toolTip;"
+ oncommand="onBottom(event);"/>
+ <spacer flex="1"/>
+ <button dlgtype="help" class="dialog-button"/>
+ </vbox>
+ </row>
+
+ <row>
+ <separator class="thin"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center">
+ <label id="folderPickerPrefix"
+ value="&folderPickerPrefix.label;"
+ accesskey="&folderPickerPrefix.accesskey;"
+ disabled="true"
+ control="runFiltersFolder"/>
+
+ <menulist id="runFiltersFolder"
+ flex="1"
+ disabled="true"
+ class="folderMenuItem"
+ displayformat="verbose">
+ <menupopup id="runFiltersPopup"
+ class="menulist-menupopup"
+ type="folder"
+ showFileHereLabel="true"
+ showAccountsFileHere="false"
+ oncommand="setRunFolder(event.target._folder);"/>
+ </menulist>
+ <spacer flex="1"/>
+ <button id="runFiltersButton"
+ label="&runFilters.label;"
+ accesskey="&runFilters.accesskey;"
+ runlabel="&runFilters.label;"
+ runaccesskey="&runFilters.accesskey;"
+ stoplabel="&stopFilters.label;"
+ stopaccesskey="&stopFilters.accesskey;"
+ disabled="true"
+ oncommand="runSelectedFilters();"/>
+ </hbox>
+ <vbox>
+ <button label="&viewLogButton.label;"
+ accesskey="&viewLogButton.accesskey;"
+ oncommand="viewLog();"/>
+ </vbox>
+ </row>
+ </rows>
+ </grid>
+
+ <statusbar class="chromeclass-status" id="status-bar">
+ <statusbarpanel class="statusbarpanel-progress">
+ <progressmeter id="statusbar-icon"
+ class="progressmeter-statusbar"
+ mode="normal"
+ value="0"/>
+ </statusbarpanel>
+ <statusbarpanel id="statusText" crop="right" flex="1"/>
+ </statusbar>
+</dialog>
diff --git a/comm/suite/mailnews/content/SearchDialog.js b/comm/suite/mailnews/content/SearchDialog.js
new file mode 100644
index 0000000000..665b12e16b
--- /dev/null
+++ b/comm/suite/mailnews/content/SearchDialog.js
@@ -0,0 +1,729 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+
+var searchSessionContractID = "@mozilla.org/messenger/searchSession;1";
+var gDBView;
+var gSearchSession;
+var gMsgFolderSelected;
+
+var nsIMsgFolder = Ci.nsIMsgFolder;
+var nsIMsgWindow = Ci.nsIMsgWindow;
+var nsMsgSearchScope = Ci.nsMsgSearchScope;
+
+var gFolderPicker;
+var gStatusBar = null;
+var gStatusFeedback = new nsMsgStatusFeedback();
+var gMessengerBundle = null;
+var RDF;
+var gSearchBundle;
+var gNextMessageViewIndexAfterDelete = -2;
+
+// Datasource search listener -- made global as it has to be registered
+// and unregistered in different functions.
+var gDataSourceSearchListener;
+var gViewSearchListener;
+
+var gSearchStopButton;
+
+// Controller object for search results thread pane
+var nsSearchResultsController =
+{
+ supportsCommand: function(command)
+ {
+ switch(command) {
+ case "cmd_openMessage":
+ case "cmd_delete":
+ case "cmd_shiftDelete":
+ case "button_delete":
+ case "file_message_button":
+ case "goto_folder_button":
+ case "saveas_vf_button":
+ case "cmd_selectAll":
+ case "cmd_markAsRead":
+ case "cmd_markAsUnread":
+ case "cmd_markAsFlagged":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ // this controller only handles commands
+ // that rely on items being selected in
+ // the search results pane.
+ isCommandEnabled: function(command)
+ {
+ var enabled = true;
+
+ switch (command) {
+ case "goto_folder_button":
+ if (GetNumSelectedMessages() != 1)
+ enabled = false;
+ break;
+ case "cmd_delete":
+ case "cmd_shiftDelete":
+ case "button_delete":
+ // this assumes that advanced searches don't cross accounts
+ if (GetNumSelectedMessages() <= 0)
+ enabled = false;
+ break;
+ case "saveas_vf_button":
+ // need someway to see if there are any search criteria...
+ return true;
+ case "cmd_selectAll":
+ return GetDBView() != null;
+ default:
+ if (GetNumSelectedMessages() <= 0)
+ enabled = false;
+ break;
+ }
+
+ return enabled;
+ },
+
+ doCommand: function(command)
+ {
+ switch(command) {
+ case "cmd_openMessage":
+ MsgOpenSelectedMessages();
+ return true;
+
+ case "cmd_delete":
+ case "button_delete":
+ MsgDeleteSelectedMessages(nsMsgViewCommandType.deleteMsg);
+ return true;
+ case "cmd_shiftDelete":
+ MsgDeleteSelectedMessages(nsMsgViewCommandType.deleteNoTrash);
+ return true;
+
+ case "goto_folder_button":
+ GoToFolder();
+ return true;
+
+ case "saveas_vf_button":
+ saveAsVirtualFolder();
+ return true;
+
+ case "cmd_selectAll":
+ // move the focus to the search results pane
+ GetThreadTree().focus();
+ GetDBView().doCommand(nsMsgViewCommandType.selectAll)
+ return true;
+
+ case "cmd_markAsRead":
+ MsgMarkMsgAsRead(true);
+ return true;
+
+ case "cmd_markAsUnread":
+ MsgMarkMsgAsRead(false);
+ return true;
+
+ case "cmd_markAsFlagged":
+ MsgMarkAsFlagged();
+ return true;
+
+ default:
+ return false;
+ }
+
+ },
+
+ onEvent: function(event)
+ {
+ }
+}
+
+function UpdateMailSearch(caller)
+{
+ //dump("XXX update mail-search " + caller + "\n");
+ document.commandDispatcher.updateCommands('mail-search');
+}
+
+function SetAdvancedSearchStatusText(aNumHits)
+{
+ var statusMsg;
+ // if there are no hits, it means no matches were found in the search.
+ if (aNumHits == 0)
+ {
+ statusMsg = gSearchBundle.getString("noMatchesFound");
+ }
+ else
+ {
+ statusMsg = PluralForm.get(aNumHits,
+ gSearchBundle.getString("matchesFound"));
+ statusMsg = statusMsg.replace("#1", aNumHits);
+ }
+ gStatusFeedback.showStatusString(statusMsg);
+}
+
+// nsIMsgSearchNotify object
+var gSearchNotificationListener =
+{
+ onSearchHit: function(header, folder)
+ {
+ // XXX TODO
+ // update status text?
+ },
+
+ onSearchDone: function(status)
+ {
+ gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForSearchButton"));
+ gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForSearchButton.accesskey"));
+ gStatusFeedback._stopMeteors();
+ SetAdvancedSearchStatusText(gDBView.QueryInterface(Ci.nsITreeView).rowCount);
+ },
+
+ onNewSearch: function()
+ {
+ gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForStopButton"));
+ gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForStopButton.accesskey"));
+ UpdateMailSearch("new-search");
+ gStatusFeedback._startMeteors();
+ gStatusFeedback.showStatusString(gSearchBundle.getString("searchingMessage"));
+ }
+}
+
+// the folderListener object
+var gFolderListener = {
+ onFolderAdded: function(parentFolder, child) {},
+ onMessageAdded: function(parentFolder, msg) {},
+ onFolderRemoved: function(parentFolder, child) {},
+ onMessageRemoved: function(parentFolder, msg) {},
+
+ onFolderPropertyChanged: function(item, property, oldValue, newValue) {},
+
+ onFolderIntPropertyChanged: function(item, property, oldValue, newValue) {},
+
+ onFolderBoolPropertyChanged: function(item, property, oldValue, newValue) {},
+
+ onFolderUnicharPropertyChanged: function(item, property, oldValue, newValue){},
+ onFolderPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
+
+ onFolderEvent: function(folder, event) {
+ if (event == "DeleteOrMoveMsgCompleted") {
+ HandleDeleteOrMoveMessageCompleted(folder);
+ }
+ else if (event == "DeleteOrMoveMsgFailed") {
+ HandleDeleteOrMoveMessageFailed(folder);
+ }
+ }
+}
+
+function HideSearchColumn(id)
+{
+ var col = document.getElementById(id);
+ if (col) {
+ col.setAttribute("hidden","true");
+ col.setAttribute("ignoreincolumnpicker","true");
+ }
+}
+
+function ShowSearchColumn(id)
+{
+ var col = document.getElementById(id);
+ if (col) {
+ col.removeAttribute("hidden");
+ col.removeAttribute("ignoreincolumnpicker");
+ }
+}
+
+function searchOnLoad()
+{
+ setHelpFileURI("chrome://communicator/locale/help/suitehelp.rdf");
+ initializeSearchWidgets();
+ initializeSearchWindowWidgets();
+ messenger = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger);
+
+ gSearchBundle = document.getElementById("bundle_search");
+ gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForSearchButton"));
+ gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForSearchButton.accesskey"));
+ gMessengerBundle = document.getElementById("bundle_messenger");
+ setupDatasource();
+ setupSearchListener();
+
+ if (window.arguments && window.arguments[0])
+ selectFolder(window.arguments[0].folder);
+
+ onMore(null);
+ UpdateMailSearch("onload");
+
+ // hide and remove these columns from the column picker. you can't thread search results
+ HideSearchColumn("threadCol"); // since you can't thread search results
+ HideSearchColumn("totalCol"); // since you can't thread search results
+ HideSearchColumn("unreadCol"); // since you can't thread search results
+ HideSearchColumn("unreadButtonColHeader");
+ HideSearchColumn("idCol");
+ HideSearchColumn("junkStatusCol");
+ HideSearchColumn("accountCol");
+
+ // we want to show the location column for search
+ ShowSearchColumn("locationCol");
+}
+
+function searchOnUnload()
+{
+ // unregister listeners
+ gSearchSession.unregisterListener(gViewSearchListener);
+ gSearchSession.unregisterListener(gSearchNotificationListener);
+
+ MailServices.mailSession.RemoveFolderListener(gFolderListener);
+
+ if (gDBView)
+ {
+ gDBView.close();
+ gDBView = null;
+ }
+
+ top.controllers.removeController(nsSearchResultsController);
+
+ // release this early because msgWindow holds a weak reference
+ msgWindow.rootDocShell = null;
+}
+
+function initializeSearchWindowWidgets()
+{
+ gFolderPicker = document.getElementById("searchableFolders");
+ gSearchStopButton = document.getElementById("search-button");
+ gStatusBar = document.getElementById('statusbar-icon');
+ hideMatchAllItem();
+
+ msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"]
+ .createInstance(nsIMsgWindow);
+ msgWindow.domWindow = window;
+ msgWindow.rootDocShell.allowAuth = true;
+ msgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL;
+ msgWindow.statusFeedback = gStatusFeedback;
+
+ // functionality to enable/disable buttons using nsSearchResultsController
+ // depending of whether items are selected in the search results thread pane.
+ top.controllers.insertControllerAt(0, nsSearchResultsController);
+}
+
+
+function onSearchStop() {
+ gSearchSession.interruptSearch();
+}
+
+function onResetSearch(event) {
+ onReset(event);
+
+ var tree = GetThreadTree();
+ tree.treeBoxObject.view = null;
+ gStatusFeedback.showStatusString("");
+}
+
+function selectFolder(folder)
+{
+ var folderURI;
+
+ // if we can't search messages on this folder, just select the first one
+ if (!folder || !folder.server.canSearchMessages ||
+ (folder.flags & Ci.nsMsgFolderFlags.Virtual)) {
+ // find first item in our folder picker menu list
+ folderURI = gFolderPicker.firstChild.tree.builderView.getResourceAtIndex(0).Value;
+ } else {
+ folderURI = folder.URI;
+ }
+ updateSearchFolderPicker(folderURI);
+}
+
+function updateSearchFolderPicker(folderURI)
+{
+ SetFolderPicker(folderURI, gFolderPicker.id);
+
+ // use the URI to get the real folder
+ gMsgFolderSelected = MailUtils.getFolderForURI(folderURI);
+
+ var searchSubFolders = document.getElementById("checkSearchSubFolders");
+ if (searchSubFolders)
+ searchSubFolders.disabled = !gMsgFolderSelected.hasSubFolders;
+ var searchLocalSystem = document.getElementById("menuSearchLocalSystem");
+ if (searchLocalSystem)
+ searchLocalSystem.disabled = gMsgFolderSelected.server.searchScope == nsMsgSearchScope.offlineMail;
+ setSearchScope(GetScopeForFolder(gMsgFolderSelected));
+}
+
+function updateSearchLocalSystem()
+{
+ setSearchScope(GetScopeForFolder(gMsgFolderSelected));
+}
+
+function UpdateAfterCustomHeaderChange()
+{
+ updateSearchAttributes();
+}
+
+function onChooseFolder(event) {
+ var folderURI = event.id;
+ if (folderURI) {
+ updateSearchFolderPicker(folderURI);
+ }
+}
+
+function onEnterInSearchTerm()
+{
+ // on enter
+ // if not searching, start the search
+ // if searching, stop and then start again
+ if (gSearchStopButton.getAttribute("label") == gSearchBundle.getString("labelForSearchButton")) {
+ onSearch();
+ }
+ else {
+ onSearchStop();
+ onSearch();
+ }
+}
+
+function onSearch()
+{
+ // set the view. do this on every search, to
+ // allow the tree to reset itself
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ if (treeView)
+ {
+ var tree = GetThreadTree();
+ tree.treeBoxObject.view = treeView;
+ }
+
+ gSearchSession.clearScopes();
+ // tell the search session what the new scope is
+ if (!gMsgFolderSelected.isServer && !gMsgFolderSelected.noSelect)
+ gSearchSession.addScopeTerm(GetScopeForFolder(gMsgFolderSelected),
+ gMsgFolderSelected);
+
+ var searchSubfolders = document.getElementById("checkSearchSubFolders").checked;
+ if (gMsgFolderSelected && (searchSubfolders || gMsgFolderSelected.isServer || gMsgFolderSelected.noSelect))
+ {
+ AddSubFolders(gMsgFolderSelected);
+ }
+ // reflect the search widgets back into the search session
+ gSearchSession.searchTerms = saveSearchTerms(gSearchSession.searchTerms, gSearchSession);
+
+ try
+ {
+ gSearchSession.search(msgWindow);
+ }
+ catch(ex)
+ {
+ dump("Search Exception\n");
+ }
+ // refresh the tree after the search starts, because initiating the
+ // search will cause the datasource to clear itself
+}
+
+function AddSubFolders(folder) {
+ for (let nextFolder of folder.subFolders) {
+ if (!(nextFolder.flags & Ci.nsMsgFolderFlags.Virtual))
+ {
+ if (!nextFolder.noSelect)
+ gSearchSession.addScopeTerm(GetScopeForFolder(nextFolder), nextFolder);
+
+ AddSubFolders(nextFolder);
+ }
+ }
+}
+
+function AddSubFoldersToURI(folder)
+{
+ var returnString = "";
+
+ for (let nextFolder of folder.subFolders) {
+ {
+ if (!(nextFolder.flags & Ci.nsMsgFolderFlags.Virtual))
+ {
+ if (!nextFolder.noSelect && !nextFolder.isServer)
+ {
+ if (returnString.length > 0)
+ returnString += '|';
+ returnString += nextFolder.URI;
+ }
+ var subFoldersString = AddSubFoldersToURI(nextFolder);
+ if (subFoldersString.length > 0)
+ {
+ if (returnString.length > 0)
+ returnString += '|';
+ returnString += subFoldersString;
+ }
+ }
+ }
+ return returnString;
+}
+
+
+function GetScopeForFolder(folder)
+{
+ var searchLocalSystem = document.getElementById("menuSearchLocalSystem");
+ return searchLocalSystem && searchLocalSystem.value == "local" ?
+ nsMsgSearchScope.offlineMail :
+ folder.server.searchScope;
+}
+
+var nsMsgViewSortType = Ci.nsMsgViewSortType;
+var nsMsgViewSortOrder = Ci.nsMsgViewSortOrder;
+var nsMsgViewFlagsType = Ci.nsMsgViewFlagsType;
+var nsMsgViewCommandType = Ci.nsMsgViewCommandType;
+
+function goUpdateSearchItems(commandset)
+{
+ for (var i = 0; i < commandset.childNodes.length; i++)
+ {
+ var commandID = commandset.childNodes[i].getAttribute("id");
+ if (commandID)
+ {
+ goUpdateCommand(commandID);
+ }
+ }
+}
+
+function nsMsgSearchCommandUpdater()
+{}
+
+nsMsgSearchCommandUpdater.prototype =
+{
+ updateCommandStatus : function()
+ {
+ // the back end is smart and is only telling us to update command status
+ // when the # of items in the selection has actually changed.
+ document.commandDispatcher.updateCommands('mail-search');
+ },
+ displayMessageChanged : function(aFolder, aSubject, aKeywords)
+ {
+ },
+
+ updateNextMessageAfterDelete : function()
+ {
+ SetNextMessageAfterDelete();
+ },
+
+ summarizeSelection: function() {return false},
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgDBViewCommandUpdater) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_NOINTERFACE;
+ }
+}
+
+function setupDatasource() {
+ gDBView = Cc["@mozilla.org/messenger/msgdbview;1?type=search"]
+ .createInstance(Ci.nsIMsgDBView);
+ var count = new Object;
+ var cmdupdator = new nsMsgSearchCommandUpdater();
+
+ gDBView.init(messenger, msgWindow, cmdupdator);
+ gDBView.open(null, nsMsgViewSortType.byId, nsMsgViewSortOrder.ascending, nsMsgViewFlagsType.kNone, count);
+
+ // the thread pane needs to use the search datasource (to get the
+ // actual list of messages) and the message datasource (to get any
+ // attributes about each message)
+ gSearchSession = Cc[searchSessionContractID].createInstance(Ci.nsIMsgSearchSession);
+
+ var nsIFolderListener = Ci.nsIFolderListener;
+ var notifyFlags = nsIFolderListener.event;
+ MailServices.mailSession.AddFolderListener(gFolderListener, notifyFlags);
+
+ // the datasource is a listener on the search results
+ gViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify);
+ gSearchSession.registerListener(gViewSearchListener);
+}
+
+
+function setupSearchListener()
+{
+ // Setup the javascript object as a listener on the search results
+ gSearchSession.registerListener(gSearchNotificationListener);
+}
+
+// used to toggle functionality for Search/Stop button.
+function onSearchButton(event)
+{
+ if (event.target.label == gSearchBundle.getString("labelForSearchButton"))
+ onSearch();
+ else
+ onSearchStop();
+}
+
+// Stuff after this is implemented to make the thread pane work.
+function GetNumSelectedMessages()
+{
+ try {
+ return gDBView.numSelected;
+ }
+ catch (ex) {
+ return 0;
+ }
+}
+
+function GetDBView()
+{
+ return gDBView;
+}
+
+function MsgDeleteSelectedMessages(aCommandType)
+{
+ SetNextMessageAfterDelete();
+ gDBView.doCommand(aCommandType);
+}
+
+function SetNextMessageAfterDelete()
+{
+ gNextMessageViewIndexAfterDelete = gDBView.msgToSelectAfterDelete;
+}
+
+function HandleDeleteOrMoveMessageFailed(folder)
+{
+ gDBView.onDeleteCompleted(false);
+ gNextMessageViewIndexAfterDelete = -2;
+}
+
+function HandleDeleteOrMoveMessageCompleted(folder)
+{
+ gDBView.onDeleteCompleted(true);
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ var treeSelection = treeView.selection;
+ var viewSize = treeView.rowCount;
+
+ if (gNextMessageViewIndexAfterDelete == -2) {
+ // a move or delete can cause our selection can change underneath us.
+ // this can happen when the user
+ // deletes message from the stand alone msg window
+ // or the three pane
+ if (!treeSelection) {
+ // this can happen if you open the search window
+ // and before you do any searches
+ // and you do delete from another mail window
+ return;
+ }
+ else if (treeSelection.count == 0) {
+ // this can happen if you double clicked a message
+ // in the thread pane, and deleted it from the stand alone msg window
+ // see bug #185147
+ treeSelection.clearSelection();
+
+ UpdateMailSearch("delete from another view, 0 rows now selected");
+ }
+ else if (treeSelection.count == 1) {
+ // this can happen if you had two messages selected
+ // in the search results pane, and you deleted one of them from another view
+ // (like the view in the stand alone msg window or the three pane)
+ // since one item is selected, we should load it.
+ var startIndex = {};
+ var endIndex = {};
+ treeSelection.getRangeAt(0, startIndex, endIndex);
+
+ // select the selected item, so we'll load it
+ treeSelection.select(startIndex.value);
+ treeView.selectionChanged();
+
+ EnsureRowInThreadTreeIsVisible(startIndex.value);
+ UpdateMailSearch("delete from another view, 1 row now selected");
+ }
+ else {
+ // this can happen if you have more than 2 messages selected
+ // in the search results pane, and you deleted one of them from another view
+ // (like the view in the stand alone msg window or the three pane)
+ // since multiple messages are still selected, do nothing.
+ }
+ }
+ else {
+ if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None && gNextMessageViewIndexAfterDelete >= viewSize)
+ {
+ if (viewSize > 0)
+ gNextMessageViewIndexAfterDelete = viewSize - 1;
+ else
+ {
+ gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None;
+
+ // there is nothing to select since viewSize is 0
+ treeSelection.clearSelection();
+
+ UpdateMailSearch("delete from current view, 0 rows left");
+ }
+ }
+
+ // if we are about to set the selection with a new element then DON'T clear
+ // the selection then add the next message to select. This just generates
+ // an extra round of command updating notifications that we are trying to
+ // optimize away.
+ if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None)
+ {
+ treeSelection.select(gNextMessageViewIndexAfterDelete);
+ // since gNextMessageViewIndexAfterDelete probably has the same value
+ // as the last index we had selected, the tree isn't generating a new
+ // selectionChanged notification for the tree view. So we aren't loading the
+ // next message. to fix this, force the selection changed update.
+ if (treeView)
+ treeView.selectionChanged();
+
+ EnsureRowInThreadTreeIsVisible(gNextMessageViewIndexAfterDelete);
+
+ // XXX TODO
+ // I think there is a bug in the suppression code above.
+ // what if I have two rows selected, and I hit delete,
+ // and so we load the next row.
+ // what if I have commands that only enable where
+ // exactly one row is selected?
+ UpdateMailSearch("delete from current view, at least one row selected");
+ }
+ }
+
+ // default value after delete/move/copy is over
+ gNextMessageViewIndexAfterDelete = -2;
+
+ // something might have been deleted, so update the status text
+ SetAdvancedSearchStatusText(viewSize);
+}
+
+function MoveMessageInSearch(destFolder)
+{
+ if (destFolder._folder)
+ {
+ try {
+ SetNextMessageAfterDelete();
+ gDBView.doCommandWithFolder(nsMsgViewCommandType.moveMessages,
+ destFolder._folder);
+ }
+ catch (ex) {
+ dump("MoveMessageInSearch failed: " + ex + "\n");
+ }
+ }
+}
+
+function GoToFolder()
+{
+ var hdr = gDBView.hdrForFirstSelectedMessage;
+ MsgOpenNewWindowForFolder(hdr.folder.URI, hdr.messageKey);
+}
+
+function saveAsVirtualFolder()
+{
+ let searchFolderURIs = window.arguments[0].folder.URI;
+
+ var searchSubfolders = document.getElementById("checkSearchSubFolders").checked;
+ if (gMsgFolderSelected && (searchSubfolders || gMsgFolderSelected.isServer || gMsgFolderSelected.noSelect))
+ {
+ var subFolderURIs = AddSubFoldersToURI(gMsgFolderSelected);
+ if (subFolderURIs.length > 0)
+ searchFolderURIs += '|' + subFolderURIs;
+ }
+
+ var dialog = window.openDialog("chrome://messenger/content/virtualFolderProperties.xul", "",
+ "chrome,titlebar,modal,centerscreen",
+ {folder:window.arguments[0].folder,
+ searchTerms:gSearchSession.searchTerms,
+ searchFolderURIs: searchFolderURIs});
+}
+
+function OnTagsChange()
+{
+ // Dummy, called by RemoveAllMessageTags and ToggleMessageTag
+}
diff --git a/comm/suite/mailnews/content/SearchDialog.xul b/comm/suite/mailnews/content/SearchDialog.xul
new file mode 100644
index 0000000000..65fabb731b
--- /dev/null
+++ b/comm/suite/mailnews/content/SearchDialog.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/. -->
+<?xml-stylesheet href="chrome://messenger/skin/searchDialog.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/folderPane.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/threadPane.xul"?>
+<?xul-overlay href="chrome://messenger/content/searchTermOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/mailKeysOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/SearchDialog.dtd">
+
+<dialog id="searchMailWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="mailnews:search"
+ title="&searchDialogTitle.label;"
+ style="width: 52em; height: 34em;"
+ persist="screenX screenY width height sizemode"
+ buttons="help"
+ ondialoghelp="return openHelp('search_messages');"
+ ondialogaccept="return false; /* allow Search on Enter */"
+ onload="searchOnLoad();"
+ onunload="onSearchStop(); searchOnUnload();">
+
+ <stringbundle id="bundle_search" src="chrome://messenger/locale/search.properties"/>
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+
+ <script src="chrome://messenger/content/mailWindow.js"/>
+ <script src="chrome://messenger/content/msgMail3PaneWindow.js"/>
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://messenger/content/mailCommands.js"/>
+ <script src="chrome://messenger/content/mailWindowOverlay.js"/>
+ <script src="chrome://messenger/content/commandglue.js"/>
+ <script src="chrome://messenger/content/SearchDialog.js"/>
+ <script src="chrome://messenger/content/msgFolderPickerOverlay.js"/>
+ <script src="chrome://messenger/content/tabmail.js"/>
+ <script src="chrome://messenger/content/folderDisplay.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+ <script src="chrome://messenger-newsblog/content/newsblogOverlay.js"/>
+
+ <commands id="commands">
+ <commandset id="mailSearchItems"
+ commandupdater="true"
+ events="mail-search"
+ oncommandupdate="goUpdateSearchItems(this)">
+ <command id="cmd_openMessage" oncommand="goDoCommand('cmd_openMessage');" disabled="true"/>
+ <command id="button_delete" oncommand="goDoCommand('button_delete')" disabled="true"/>
+ <command id="goto_folder_button" oncommand="goDoCommand('goto_folder_button')" disabled="true"/>
+ <command id="saveas_vf_button" oncommand="goDoCommand('saveas_vf_button')" disabled="false"/>
+ <command id="file_message_button"/>
+ <command id="cmd_delete"/>
+ <command id="cmd_shiftDelete" oncommand="goDoCommand('cmd_shiftDelete');"/>
+ </commandset>
+ </commands>
+
+ <keyset id="mailKeys"/>
+
+ <broadcasterset id="mailBroadcasters">
+ <broadcaster id="Communicator:WorkMode"/>
+ </broadcasterset>
+
+ <dummy class="usesMailWidgets"/>
+
+ <vbox id="searchTerms" flex="3" persist="height">
+ <vbox>
+ <hbox align="center">
+ <label value="&searchHeading.label;" accesskey="&searchHeading.accesskey;"
+ control="searchableFolders"/>
+ <menulist id="searchableFolders" flex="2"
+ class="folderMenuItem"
+ displayformat="verbose">
+ <menupopup class="menulist-menupopup"
+ type="folder"
+ mode="search"
+ showAccountsFileHere="true"
+ showFileHereLabel="true"
+ oncommand="updateSearchFolderPicker(event.target.id);"/>
+ </menulist>
+ <checkbox id="checkSearchSubFolders"
+ label="&searchSubfolders.label;"
+ checked="true"
+ accesskey="&searchSubfolders.accesskey;"/>
+ <spacer flex="3"/>
+ <button id="search-button" oncommand="onSearchButton(event);" default="true"/>
+ </hbox>
+ <hbox align="center">
+ <label id="searchOnHeading"
+ value="&searchOnHeading.label;"
+ accesskey="&searchOnHeading.accesskey;"
+ control="menuSearchLocalSystem">
+ <observes element="menuSearchLocalSystem"
+ attribute="disabled"/>
+ </label>
+ <menulist id="menuSearchLocalSystem"
+ persist="value"
+ oncommand="updateSearchLocalSystem();">
+ <menupopup>
+ <menuitem id="menuOnRemote"
+ value="remote"
+ label="&searchOnRemote.label;"/>
+ <menuitem id="menuOnLocal"
+ value="local"
+ label="&searchOnLocal.label;"/>
+ </menupopup>
+ </menulist>
+ <spacer flex="1"/>
+ <button label="&resetButton.label;" oncommand="onResetSearch(event);" accesskey="&resetButton.accesskey;"/>
+ </hbox>
+ </vbox>
+
+ <hbox flex="1">
+ <vbox id="searchTermListBox" flex="1"/>
+ </hbox>
+ </vbox>
+
+ <splitter id="gray_horizontal_splitter" persist="state">
+ <grippy/>
+ </splitter>
+
+ <vbox id="searchResults" flex="4" persist="height">
+ <vbox id="searchResultListBox" flex="1">
+ <tree id="threadTree"/>
+ </vbox>
+ <hbox align="center">
+
+ <button id="openButton"
+ label="&openButton.label;"
+ command="cmd_openMessage"
+ accesskey="&openButton.accesskey;"/>
+ <button id="fileMessageButton"
+ type="menu"
+ label="&moveButton.label;"
+ accesskey="&moveButton.accesskey;"
+ observes="file_message_button"
+ oncommand="MoveMessageInSearch(event.target);">
+ <menupopup type="folder"
+ showFileHereLabel="true"
+ mode="filing"
+ fileHereLabel="&moveHereMenu.label;"
+ fileHereAccessKey="&moveHereMenu.accesskey;"/>
+ </button>
+
+ <button id="deleteButton"
+ label="&deleteButton.label;"
+ accesskey="&deleteButton.accesskey;"
+ command="button_delete"/>
+ <button id="goToFolderButton"
+ label="&goToFolderButton.label;"
+ accesskey="&goToFolderButton.accesskey;"
+ command="goto_folder_button"/>
+ <button id="saveAsVFButton"
+ label="&saveAsVFButton.label;"
+ accesskey="&saveAsVFButton.accesskey;"
+ command="saveas_vf_button"/>
+ <spacer flex="1"/>
+ <button dlgtype="help" class="dialog-button"/>
+ </hbox>
+ </vbox>
+
+ <statusbar id="status-bar" class="chromeclass-status">
+ <statusbarpanel id="statusbar-progresspanel"
+ class="statusbarpanel-progress"
+ collapsed="true">
+ <progressmeter id="statusbar-icon"
+ class="progressmeter-statusbar"
+ mode="normal"
+ value="0"/>
+ </statusbarpanel>
+ <statusbarpanel id="statusText" crop="right" flex="1"/>
+ <statusbarpanel id="offline-status" class="statusbarpanel-iconic"/>
+ </statusbar>
+
+</dialog>
diff --git a/comm/suite/mailnews/content/browserRequest.js b/comm/suite/mailnews/content/browserRequest.js
new file mode 100644
index 0000000000..56ff0f8b9e
--- /dev/null
+++ b/comm/suite/mailnews/content/browserRequest.js
@@ -0,0 +1,107 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const wpl = Ci.nsIWebProgressListener;
+
+var reporterListener = {
+ _isBusy: false,
+ get securityButton() {
+ delete this.securityButton;
+ return this.securityButton = document.getElementById("security-button");
+ },
+
+ QueryInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIWebProgressListener) ||
+ aIID.equals(Ci.nsISupportsWeakReference) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ onStateChange: function(/*in nsIWebProgress*/ aWebProgress,
+ /*in nsIRequest*/ aRequest,
+ /*in unsigned long*/ aStateFlags,
+ /*in nsresult*/ aStatus) {
+ },
+
+ onProgressChange: function(/*in nsIWebProgress*/ aWebProgress,
+ /*in nsIRequest*/ aRequest,
+ /*in long*/ aCurSelfProgress,
+ /*in long */aMaxSelfProgress,
+ /*in long */aCurTotalProgress,
+ /*in long */aMaxTotalProgress) {
+ },
+
+ onLocationChange: function(/*in nsIWebProgress*/ aWebProgress,
+ /*in nsIRequest*/ aRequest,
+ /*in nsIURI*/ aLocation) {
+ document.getElementById("headerMessage").textContent = aLocation.spec;
+ },
+
+ onStatusChange: function(/*in nsIWebProgress*/ aWebProgress,
+ /*in nsIRequest*/ aRequest,
+ /*in nsresult*/ aStatus,
+ /*in wstring*/ aMessage) {
+ },
+
+ onSecurityChange: function(/*in nsIWebProgress*/ aWebProgress,
+ /*in nsIRequest*/ aRequest,
+ /*in unsigned long*/ aState) {
+ const wpl_security_bits = wpl.STATE_IS_SECURE |
+ wpl.STATE_IS_BROKEN |
+ wpl.STATE_IS_INSECURE;
+ var browser = document.getElementById("requestFrame");
+ var level;
+
+ switch (aState & wpl_security_bits) {
+ case wpl.STATE_IS_SECURE:
+ level = "high";
+ break;
+ case wpl.STATE_IS_BROKEN:
+ level = "broken";
+ break;
+ }
+ if (level) {
+ this.securityButton.setAttribute("level", level);
+ this.securityButton.hidden = false;
+ } else {
+ this.securityButton.hidden = true;
+ this.securityButton.removeAttribute("level");
+ }
+ this.securityButton.setAttribute("tooltiptext",
+ browser.securityUI.tooltipText);
+ }
+}
+
+function cancelRequest()
+{
+ reportUserClosed();
+ window.close();
+}
+
+function reportUserClosed()
+{
+ let request = window.arguments[0].wrappedJSObject;
+ request.cancelled();
+}
+
+function loadRequestedUrl()
+{
+ let request = window.arguments[0].wrappedJSObject;
+ document.getElementById("headerMessage").textContent = request.promptText;
+ let account = request.account;
+ if (request.iconURI != "")
+ document.getElementById("headerImage").src = request.iconURI;
+
+ var browser = document.getElementById("requestFrame");
+ browser.addProgressListener(reporterListener,
+ Ci.nsIWebProgress.NOTIFY_ALL);
+ var url = request.url;
+ if (url != "") {
+ browser.setAttribute("src", url);
+ document.getElementById("headerMessage").textContent = url;
+ }
+ request.loaded(window, browser.webProgress);
+}
diff --git a/comm/suite/mailnews/content/browserRequest.xul b/comm/suite/mailnews/content/browserRequest.xul
new file mode 100644
index 0000000000..9911601f86
--- /dev/null
+++ b/comm/suite/mailnews/content/browserRequest.xul
@@ -0,0 +1,34 @@
+<?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://messenger/skin/browserRequest.css" type="text/css"?>
+
+<!DOCTYPE window>
+<window id="browserRequest"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons=","
+ onload="loadRequestedUrl()"
+ onclose="reportUserClosed()"
+ title=""
+ width="800"
+ height="500"
+ orient="vertical">
+
+ <script src="chrome://messenger/content/browserRequest.js"/>
+
+ <keyset id="mainKeyset">
+ <key id="key_close" key="w" modifiers="accel" oncommand="cancelRequest()"/>
+ <key id="key_close2" keycode="VK_ESCAPE" oncommand="cancelRequest()"/>
+ </keyset>
+ <hbox id="header">
+ <hbox id="addressbox" flex="1" disabled="true">
+ <image id="security-button"/>
+ <description id="headerMessage"/>
+ </hbox>
+ </hbox>
+ <browser type="content" src="about:blank" id="requestFrame" flex="1"/>
+</window>
diff --git a/comm/suite/mailnews/content/commandglue.js b/comm/suite/mailnews/content/commandglue.js
new file mode 100644
index 0000000000..a9a9332c64
--- /dev/null
+++ b/comm/suite/mailnews/content/commandglue.js
@@ -0,0 +1,989 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Command-specific code. This stuff should be called by the widgets
+ */
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+//NOTE: gMessengerBundle and gBrandBundle must be defined and set
+// for this Overlay to work properly
+
+var gFolderJustSwitched = false;
+var gBeforeFolderLoadTime;
+var gVirtualFolderTerms;
+var gXFVirtualFolderTerms;
+var gCurrentVirtualFolderUri;
+var gPrevFolderFlags;
+var gPrevSelectedFolder;
+var gMsgFolderSelected;
+
+function setTitleFromFolder(msgfolder, subject)
+{
+ var title = subject || "";
+
+ if (msgfolder)
+ {
+ if (title)
+ title += " - ";
+
+ title += msgfolder.prettyName;
+
+ if (!msgfolder.isServer)
+ {
+ var server = msgfolder.server;
+ var middle;
+ var end;
+ if (server.type == "nntp") {
+ // <folder> on <hostname>
+ middle = gMessengerBundle.getString("titleNewsPreHost");
+ end = server.hostName;
+ } else {
+ // <folder> for <accountname>
+ middle = gMessengerBundle.getString("titleMailPreHost");
+ end = server.prettyName;
+ }
+ if (middle) title += " " + middle;
+ if (end) title += " " + end;
+ }
+ }
+
+ if (AppConstants.platform != "macosx") {
+ title += " - " + gBrandBundle.getString("brandShortName");
+ }
+
+ document.title = title;
+
+ // Notify the current tab, it might want to update also.
+ var tabmail = GetTabMail();
+ if (tabmail)
+ {
+ tabmail.saveCurrentTabState(); // gDBView may have changed!
+ tabmail.setTabTitle();
+ }
+}
+
+function UpdateMailToolbar(caller)
+{
+ //dump("XXX update mail-toolbar " + caller + "\n");
+ document.commandDispatcher.updateCommands('mail-toolbar');
+
+ // hook for extra toolbar items
+ Services.obs.notifyObservers(window, "mail:updateToolbarItems");
+}
+
+/**
+ * @param folder - If viewFolder is a single folder saved
+ - search, this folder is the scope of the
+ - saved search, the real, underlying folder.
+ - Otherwise, it's the same as the viewFolder.
+ * @param viewFolder - nsIMsgFolder selected in the folder pane.
+ - Will be the same as folder, except if
+ - it's a single folder saved search.
+ * @param viewType - nsMsgViewType (see nsIMsgDBView.idl)
+ * @param viewFlags - nsMsgViewFlagsType (see nsIMsgDBView.idl)
+ * @param sortType - nsMsgViewSortType (see nsIMsgDBView.idl)
+ * @param sortOrder - nsMsgViewSortOrder (see nsIMsgDBView.idl)
+ **/
+function ChangeFolder(folder, viewFolder, viewType, viewFlags, sortType, sortOrder)
+{
+ if (folder.URI == gCurrentLoadingFolderURI)
+ return;
+
+ SetUpToolbarButtons(folder.URI);
+
+ // hook for extra toolbar items
+ Services.obs.notifyObservers(window, "mail:setupToolbarItems", folder.URI);
+
+ try {
+ setTitleFromFolder(viewFolder, null);
+ } catch (ex) {
+ dump("error setting title: " + ex + "\n");
+ }
+
+ //if it's a server, clear the threadpane and don't bother trying to load.
+ if (folder.isServer) {
+ msgWindow.openFolder = null;
+
+ ClearThreadPane();
+
+ // Load AccountCentral page here.
+ ShowAccountCentral(folder);
+
+ return;
+ }
+ else
+ {
+ if (folder.server.displayStartupPage)
+ {
+ gDisplayStartupPage = true;
+ folder.server.displayStartupPage = false;
+ }
+ }
+
+ // If the user clicks on folder, time to display thread pane and message pane.
+ ShowThreadPane();
+
+ gCurrentLoadingFolderURI = folder.URI;
+ gNextMessageAfterDelete = null; // forget what message to select, if any
+
+ gCurrentFolderToReroot = folder.URI;
+ gCurrentLoadingFolderViewFlags = viewFlags;
+ gCurrentLoadingFolderViewType = viewType;
+ gCurrentLoadingFolderSortType = sortType;
+ gCurrentLoadingFolderSortOrder = sortOrder;
+
+ var showMessagesAfterLoading;
+ try {
+ let server = folder.server;
+ if (Services.prefs.getBoolPref("mail.password_protect_local_cache"))
+ {
+ showMessagesAfterLoading = server.passwordPromptRequired;
+ // servers w/o passwords (like local mail) will always be non-authenticated.
+ // So we need to use the account manager for that case.
+ }
+ else
+ showMessagesAfterLoading = false;
+ }
+ catch (ex) {
+ showMessagesAfterLoading = false;
+ }
+
+ if (viewType != nsMsgViewType.eShowVirtualFolderResults &&
+ (folder.manyHeadersToDownload || showMessagesAfterLoading))
+ {
+ gRerootOnFolderLoad = true;
+ try
+ {
+ ClearThreadPane();
+ SetBusyCursor(window, true);
+ folder.startFolderLoading();
+ folder.updateFolder(msgWindow);
+ }
+ catch(ex)
+ {
+ SetBusyCursor(window, false);
+ dump("Error loading with many headers to download: " + ex + "\n");
+ }
+ }
+ else
+ {
+ if (viewType != nsMsgViewType.eShowVirtualFolderResults)
+ SetBusyCursor(window, true);
+ RerootFolder(folder.URI, folder, viewType, viewFlags, sortType, sortOrder);
+ gRerootOnFolderLoad = false;
+ folder.startFolderLoading();
+
+ //Need to do this after rerooting folder. Otherwise possibility of receiving folder loaded
+ //notification before folder has actually changed.
+ if (viewType != nsMsgViewType.eShowVirtualFolderResults)
+ folder.updateFolder(msgWindow);
+ }
+}
+
+function isNewsURI(uri)
+{
+ return ((/^news-message:/.test(uri)) || (/^news:/.test(uri)));
+}
+
+function RerootFolder(uri, newFolder, viewType, viewFlags, sortType, sortOrder)
+{
+ viewDebug("In reroot folder, sortType = " + sortType + "viewType = " + viewType + "\n");
+ if (sortType == 0)
+ {
+ try
+ {
+ var dbFolderInfo = newFolder.msgDatabase.dBFolderInfo;
+ sortType = dbFolderInfo.sortType;
+ sortOrder = dbFolderInfo.sortOrder;
+ viewFlags = dbFolderInfo.viewFlags;
+ viewType = dbFolderInfo.viewType;
+ dbFolderInfo = null;
+ }
+ catch(ex)
+ {
+ dump("invalid db in RerootFolder: " + ex + "\n");
+ }
+ }
+
+ // workaround for #39655
+ gFolderJustSwitched = true;
+
+ ClearThreadPaneSelection();
+
+ //Clear the new messages of the old folder
+ var oldFolder = gPrevSelectedFolder;
+ if (oldFolder) {
+ oldFolder.clearNewMessages();
+ oldFolder.hasNewMessages = false;
+ }
+
+ //Set the window's new open folder.
+ msgWindow.openFolder = newFolder;
+
+ //the new folder being selected should have its biff state get cleared.
+ if(newFolder)
+ {
+ newFolder.biffState =
+ Ci.nsIMsgFolder.nsMsgBiffState_NoMail;
+ }
+
+ //Clear out the thread pane so that we can sort it with the new sort id without taking any time.
+ // folder.setAttribute('ref', "");
+
+ // null this out, so we don't try sort.
+ if (gDBView) {
+ gDBView.close();
+ gDBView = null;
+ }
+
+ // cancel the pending mark as read timer
+ ClearPendingReadTimer();
+
+ // If this is the sent, drafts, templates, or send later folder,
+ // we show "Recipient" instead of "Author".
+ let outgoingFlags = Ci.nsMsgFolderFlags.SentMail |
+ Ci.nsMsgFolderFlags.Drafts |
+ Ci.nsMsgFolderFlags.Templates |
+ Ci.nsMsgFolderFlags.Queue;
+ SetSentFolderColumns(newFolder.isSpecialFolder(outgoingFlags, true));
+ ShowLocationColumn(viewType == nsMsgViewType.eShowVirtualFolderResults);
+ // Only show 'Received' column for e-mails. For newsgroup messages, the 'Date' header is as reliable as an e-mail's
+ // 'Received' header, as it is replaced with the news server's (more reliable) date.
+ UpdateReceivedColumn(newFolder);
+
+ // now create the db view, which will sort it.
+ CreateDBView(newFolder, viewType, viewFlags, sortType, sortOrder);
+ if (oldFolder)
+ {
+ /*disable quick search clear button if we were in the search view on folder switching*/
+ disableQuickSearchClearButton();
+
+ /*we don't null out the db reference for inbox because inbox is like the "main" folder
+ and performance outweighs footprint */
+ if (!oldFolder.isSpecialFolder(Ci.nsMsgFolderFlags.Inbox, false))
+ if (oldFolder.URI != newFolder.URI)
+ oldFolder.msgDatabase = null;
+ }
+ // that should have initialized gDBView, now re-root the thread pane
+ RerootThreadPane();
+
+ UpdateStatusMessageCounts(gMsgFolderSelected);
+
+ UpdateMailToolbar("reroot folder in 3 pane");
+ // hook for extra toolbar items
+ Services.obs.notifyObservers(window, "mail:updateToolbarItems");
+ // this is to kick off cross-folder searches for virtual folders.
+ if (gSearchSession && !gVirtualFolderTerms) // another var might be better...
+ {
+ viewDebug("doing a xf folder search in rerootFolder\n");
+ gCurrentLoadingFolderURI = "";
+ ViewChangeByFolder(newFolder);
+ gPreQuickSearchView = null; // don't remember the cross folder search
+ ScrollToMessageAfterFolderLoad(newFolder);
+ }
+}
+
+function SwitchView(command)
+{
+ // when switching thread views, we might be coming out of quick search
+ // or a message view.
+ // first set view picker to all
+ ViewChangeByValue(kViewItemAll);
+
+ // clear the QS text, if we need to
+ ClearQSIfNecessary();
+
+ // now switch views
+ var oldSortType = gDBView ? gDBView.sortType : nsMsgViewSortType.byThread;
+ var oldSortOrder = gDBView ? gDBView.sortOrder : nsMsgViewSortOrder.ascending;
+ var viewFlags = gDBView ? gDBView.viewFlags : gCurViewFlags;
+
+ // close existing view.
+ if (gDBView) {
+ gDBView.close();
+ gDBView = null;
+ }
+
+ switch(command)
+ {
+ // "All" threads and "Unread" threads don't change threading state
+ case "cmd_viewAllMsgs":
+ viewFlags = viewFlags & ~nsMsgViewFlagsType.kUnreadOnly;
+ CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowAllThreads, viewFlags,
+ oldSortType, oldSortOrder);
+ break;
+ case "cmd_viewUnreadMsgs":
+ viewFlags = viewFlags | nsMsgViewFlagsType.kUnreadOnly;
+ CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowAllThreads, viewFlags,
+ oldSortType, oldSortOrder );
+ break;
+ // "Threads with Unread" and "Watched Threads with Unread" force threading
+ case "cmd_viewThreadsWithUnread":
+ CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowThreadsWithUnread, nsMsgViewFlagsType.kThreadedDisplay,
+ oldSortType, oldSortOrder);
+ break;
+ case "cmd_viewWatchedThreadsWithUnread":
+ CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowWatchedThreadsWithUnread, nsMsgViewFlagsType.kThreadedDisplay,
+ oldSortType, oldSortOrder);
+ break;
+ // "Ignored Threads" toggles 'ignored' inclusion --
+ // but it also resets 'With Unread' views to 'All'
+ case "cmd_viewIgnoredThreads":
+ if (viewFlags & nsMsgViewFlagsType.kShowIgnored)
+ viewFlags = viewFlags & ~nsMsgViewFlagsType.kShowIgnored;
+ else
+ viewFlags = viewFlags | nsMsgViewFlagsType.kShowIgnored;
+ CreateDBView(msgWindow.openFolder, nsMsgViewType.eShowAllThreads, viewFlags,
+ oldSortType, oldSortOrder);
+ break;
+ }
+
+ RerootThreadPane();
+
+ // this is to kick off cross-folder searches for virtual folders.
+ if (gSearchSession && !gVirtualFolderTerms) // another var might be better...
+ {
+ gDBView.searchSession = gSearchSession;
+ gSearchSession.search(msgWindow);
+ }
+}
+
+function SetSentFolderColumns(isSentFolder)
+{
+ var tree = GetThreadTree();
+ var searchBox = document.getElementById("searchInput");
+
+ var lastFolderSent = tree.getAttribute("lastfoldersent") == "true";
+ if (isSentFolder != lastFolderSent)
+ {
+ var senderColumn = document.getElementById("senderCol");
+ var recipientColumn = document.getElementById("recipientCol");
+
+ var saveHidden = senderColumn.getAttribute("hidden");
+ senderColumn.setAttribute("hidden", senderColumn.getAttribute("swappedhidden"));
+ senderColumn.setAttribute("swappedhidden", saveHidden);
+
+ saveHidden = recipientColumn.getAttribute("hidden");
+ recipientColumn.setAttribute("hidden", recipientColumn.getAttribute("swappedhidden"));
+ recipientColumn.setAttribute("swappedhidden", saveHidden);
+ }
+
+ tree.setAttribute("lastfoldersent", isSentFolder ? "true" : "false");
+}
+
+function ShowLocationColumn(show)
+{
+ var col = document.getElementById("locationCol");
+ if (col) {
+ if (show) {
+ col.removeAttribute("hidden");
+ col.removeAttribute("ignoreincolumnpicker");
+ }
+ else {
+ col.setAttribute("hidden","true");
+ col.setAttribute("ignoreincolumnpicker","true");
+ }
+ }
+}
+
+function UpdateReceivedColumn(newFolder)
+{
+ // Only show 'Received' column for e-mails. For newsgroup messages, the 'Date' header is as reliable as an e-mail's
+ // 'Received' header, as it is replaced with the news server's (more reliable) date.
+ var receivedColumn = document.getElementById("receivedCol");
+
+ var newFolderShowsRcvd = (newFolder.flags & Ci.nsMsgFolderFlags.Mail) &&
+ !(newFolder.flags & (Ci.nsMsgFolderFlags.Queue |
+ Ci.nsMsgFolderFlags.Templates |
+ Ci.nsMsgFolderFlags.Drafts |
+ Ci.nsMsgFolderFlags.SentMail));
+
+ var tempHidden = receivedColumn.getAttribute("temphidden") == "true";
+ var isHidden = receivedColumn.getAttribute("hidden") == "true";
+
+ if (!newFolderShowsRcvd && !isHidden)
+ {
+ // Record state & hide
+ receivedColumn.setAttribute("temphidden", "true");
+ receivedColumn.setAttribute("hidden", "true");
+ }
+ else if (newFolderShowsRcvd && tempHidden && isHidden)
+ {
+ receivedColumn.setAttribute("hidden", "false");
+ }
+
+ if (newFolderShowsRcvd)
+ {
+ receivedColumn.removeAttribute("ignoreincolumnpicker");
+ receivedColumn.removeAttribute("temphidden");
+ }
+ else
+ receivedColumn.setAttribute("ignoreincolumnpicker", "true");
+}
+
+
+function SetNewsFolderColumns()
+{
+ var sizeColumn = document.getElementById("sizeCol");
+
+ if (gDBView.usingLines) {
+ sizeColumn.setAttribute("tooltiptext",gMessengerBundle.getString("linesColumnTooltip2"));
+ sizeColumn.setAttribute("label",gMessengerBundle.getString("linesColumnHeader"));
+ }
+ else {
+ sizeColumn.setAttribute("tooltiptext", gMessengerBundle.getString("sizeColumnTooltip2"));
+ sizeColumn.setAttribute("label", gMessengerBundle.getString("sizeColumnHeader"));
+ }
+}
+
+function UpdateStatusMessageCounts(folder)
+{
+ var unreadElement = GetUnreadCountElement();
+ var totalElement = GetTotalCountElement();
+ if(folder && unreadElement && totalElement)
+ {
+ var numSelected = GetNumSelectedMessages();
+
+ var numUnread = (numSelected > 1) ?
+ gMessengerBundle.getFormattedString("selectedMsgStatus",
+ [numSelected]) :
+ gMessengerBundle.getFormattedString("unreadMsgStatus",
+ [ folder.getNumUnread(false)]);
+ var numTotal =
+ gMessengerBundle.getFormattedString("totalMsgStatus",
+ [folder.getTotalMessages(false)]);
+
+ unreadElement.setAttribute("label", numUnread);
+ totalElement.setAttribute("label", numTotal);
+ unreadElement.hidden = false;
+ totalElement.hidden = false;
+
+ }
+
+}
+
+function ConvertSortTypeToColumnID(sortKey)
+{
+ var columnID;
+
+ // Hack to turn this into an integer, if it was a string.
+ // It would be a string if it came from xulstore.json
+ sortKey = sortKey - 0;
+
+ switch (sortKey) {
+ // In the case of None, we default to the date column
+ // This appears to be the case in such instances as
+ // Global search, so don't complain about it.
+ case nsMsgViewSortType.byNone:
+ case nsMsgViewSortType.byDate:
+ columnID = "dateCol";
+ break;
+ case nsMsgViewSortType.byReceived:
+ columnID = "receivedCol";
+ break;
+ case nsMsgViewSortType.byAuthor:
+ columnID = "senderCol";
+ break;
+ case nsMsgViewSortType.byRecipient:
+ columnID = "recipientCol";
+ break;
+ case nsMsgViewSortType.bySubject:
+ columnID = "subjectCol";
+ break;
+ case nsMsgViewSortType.byLocation:
+ columnID = "locationCol";
+ break;
+ case nsMsgViewSortType.byAccount:
+ columnID = "accountCol";
+ break;
+ case nsMsgViewSortType.byUnread:
+ columnID = "unreadButtonColHeader";
+ break;
+ case nsMsgViewSortType.byStatus:
+ columnID = "statusCol";
+ break;
+ case nsMsgViewSortType.byTags:
+ columnID = "tagsCol";
+ break;
+ case nsMsgViewSortType.bySize:
+ columnID = "sizeCol";
+ break;
+ case nsMsgViewSortType.byPriority:
+ columnID = "priorityCol";
+ break;
+ case nsMsgViewSortType.byFlagged:
+ columnID = "flaggedCol";
+ break;
+ case nsMsgViewSortType.byThread:
+ columnID = "threadCol";
+ break;
+ case nsMsgViewSortType.byId:
+ columnID = "idCol";
+ break;
+ case nsMsgViewSortType.byJunkStatus:
+ columnID = "junkStatusCol";
+ break;
+ case nsMsgViewSortType.byAttachments:
+ columnID = "attachmentCol";
+ break;
+ case nsMsgViewSortType.byCustom:
+ columnID = gDBView.db.dBFolderInfo.getProperty("customSortCol");
+ if (!columnID) {
+ dump("ConvertSortTypeToColumnID: custom sort key but columnID not found\n");
+ columnID = "dateCol";
+ }
+ break;
+ default:
+ dump("unsupported sort key: " + sortKey + "\n");
+ columnID = null;
+ break;
+ }
+ return columnID;
+}
+
+var nsMsgViewSortType = Ci.nsMsgViewSortType;
+var nsMsgViewSortOrder = Ci.nsMsgViewSortOrder;
+var nsMsgViewFlagsType = Ci.nsMsgViewFlagsType;
+var nsMsgViewCommandType = Ci.nsMsgViewCommandType;
+var nsMsgViewType = Ci.nsMsgViewType;
+var nsMsgNavigationType = Ci.nsMsgNavigationType;
+
+var gDBView = null;
+var gCurViewFlags;
+var gCurSortType;
+
+// CreateDBView is called when we have a thread pane. CreateBareDBView is called when there is no
+// tree associated with the view. CreateDBView will call into CreateBareDBView...
+
+function CreateBareDBView(originalView, msgFolder, viewType, viewFlags, sortType, sortOrder)
+{
+ var dbviewContractId = "@mozilla.org/messenger/msgdbview;1?type=";
+ // hack to turn this into an integer, if it was a string
+ // it would be a string if it came from xulstore.json
+ viewType = viewType - 0;
+
+ switch (viewType) {
+ case nsMsgViewType.eShowQuickSearchResults:
+ dbviewContractId += "quicksearch";
+ break;
+ case nsMsgViewType.eShowSearch:
+ dbviewContractId += "search";
+ break;
+ case nsMsgViewType.eShowThreadsWithUnread:
+ dbviewContractId += "threadswithunread";
+ break;
+ case nsMsgViewType.eShowWatchedThreadsWithUnread:
+ dbviewContractId += "watchedthreadswithunread";
+ break;
+ case nsMsgViewType.eShowVirtualFolderResults:
+ dbviewContractId += "xfvf";
+ break;
+ case nsMsgViewType.eShowAllThreads:
+ default:
+ if (viewFlags & nsMsgViewFlagsType.kGroupBySort)
+ dbviewContractId += "group";
+ else
+ dbviewContractId += "threaded";
+ break;
+ }
+
+// dump ("contract id = " + dbviewContractId + "original view = " + originalView + "\n");
+ if (!originalView)
+ gDBView = Cc[dbviewContractId].createInstance(Ci.nsIMsgDBView);
+
+ gCurViewFlags = viewFlags;
+ var count = new Object;
+ if (!gThreadPaneCommandUpdater)
+ gThreadPaneCommandUpdater = new nsMsgDBViewCommandUpdater();
+
+ gCurSortType = sortType;
+
+ if (!originalView) {
+ gDBView.init(messenger, msgWindow, gThreadPaneCommandUpdater);
+ gDBView.open(msgFolder, gCurSortType, sortOrder, viewFlags, count);
+ if (viewType == nsMsgViewType.eShowVirtualFolderResults)
+ {
+ // the view is a listener on the search results
+ gViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify);
+ gSearchSession.registerListener(gViewSearchListener);
+ }
+ }
+ else {
+ gDBView = originalView.cloneDBView(messenger, msgWindow, gThreadPaneCommandUpdater);
+ }
+}
+
+function CreateDBView(msgFolder, viewType, viewFlags, sortType, sortOrder)
+{
+ // call the inner create method
+ CreateBareDBView(null, msgFolder, viewType, viewFlags, sortType, sortOrder);
+
+ // now do tree specific work
+
+ // based on the collapsed state of the thread pane/message pane splitter,
+ // suppress message display if appropriate.
+ gDBView.suppressMsgDisplay = IsMessagePaneCollapsed();
+
+ UpdateSortIndicators(gCurSortType, sortOrder);
+ Services.obs.notifyObservers(msgFolder, "MsgCreateDBView", viewType + ":" + viewFlags);
+}
+
+function FolderPaneSelectionChange()
+{
+ let folders = GetSelectedMsgFolders();
+ if (folders.length) {
+ let locationItem = document.getElementById("locationFolders");
+ if (locationItem &&
+ locationItem.parentNode.parentNode.localName != "toolbarpalette") {
+ let msgFolder = folders[0];
+ locationItem.setAttribute("label", msgFolder.prettyName);
+ document.getElementById("folderLocationPopup")
+ ._setCssSelectors(msgFolder, locationItem);
+ }
+ }
+
+ let folderSelection = gFolderTreeView.selection;
+
+ // This prevents a folder from being loaded in the case that the user
+ // has right-clicked on a folder different from the one that was
+ // originally highlighted. On a right-click, the highlight (selection)
+ // of a row will be different from the value of currentIndex, thus if
+ // the currentIndex is not selected, it means the user right-clicked
+ // and we don't want to load the contents of the folder.
+ if (!folderSelection.isSelected(folderSelection.currentIndex))
+ return;
+
+ gVirtualFolderTerms = null;
+ gXFVirtualFolderTerms = null;
+
+ if (folders.length == 1)
+ {
+ let msgFolder = folders[0];
+ let uriToLoad = msgFolder.URI;
+
+ if (msgFolder == gMsgFolderSelected)
+ return;
+ // If msgFolder turns out to be a single folder saved search, not a virtual folder,
+ // realFolder will get set to the underlying folder the saved search is based on.
+ let realFolder = msgFolder;
+ gPrevSelectedFolder = gMsgFolderSelected;
+ gMsgFolderSelected = msgFolder;
+ var folderFlags = msgFolder.flags;
+ const kVirtual = Ci.nsMsgFolderFlags.Virtual;
+ // if this is same folder, and we're not showing a virtual folder
+ // then do nothing.
+ if (msgFolder == msgWindow.openFolder &&
+ !(folderFlags & kVirtual) && !(gPrevFolderFlags & kVirtual))
+ return;
+
+ OnLeavingFolder(gPrevSelectedFolder); // mark all read in last folder
+ var sortType = 0;
+ var sortOrder = 0;
+ var viewFlags = 0;
+ var viewType = 0;
+ gDefaultSearchViewTerms = null;
+ gVirtualFolderTerms = null;
+ gXFVirtualFolderTerms = null;
+ gPrevFolderFlags = folderFlags;
+ gCurrentVirtualFolderUri = null;
+ // don't get the db if this folder is a server
+ // we're going to be display account central
+ if (!(msgFolder.isServer))
+ {
+ try
+ {
+ var msgDatabase = msgFolder.msgDatabase;
+ if (msgDatabase)
+ {
+ gSearchSession = null;
+ var dbFolderInfo = msgDatabase.dBFolderInfo;
+ sortType = dbFolderInfo.sortType;
+ sortOrder = dbFolderInfo.sortOrder;
+ viewType = dbFolderInfo.viewType;
+ viewFlags = dbFolderInfo.viewFlags;
+ if (folderFlags & kVirtual)
+ {
+ viewType = nsMsgViewType.eShowQuickSearchResults;
+ var searchTermString = dbFolderInfo.getCharProperty("searchStr");
+ // trick the view code into updating the real folder...
+ gCurrentVirtualFolderUri = uriToLoad;
+ var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri");
+ var srchFolderUriArray = srchFolderUri.split('|');
+ var searchOnline = dbFolderInfo.getBooleanProperty("searchOnline", false);
+ // cross folder search
+ var filterList = MailServices.filters.getTempFilterList(msgFolder);
+ var tempFilter = filterList.createFilter("temp");
+ filterList.parseCondition(tempFilter, searchTermString);
+ if (srchFolderUriArray.length > 1)
+ {
+ viewType = nsMsgViewType.eShowVirtualFolderResults;
+ gXFVirtualFolderTerms = CreateGroupedSearchTerms(tempFilter.searchTerms);
+ setupXFVirtualFolderSearch(srchFolderUriArray, gXFVirtualFolderTerms, searchOnline);
+ // need to set things up so that reroot folder issues the search
+ }
+ else
+ {
+ uriToLoad = srchFolderUri;
+ // we need to load the db for the actual folder so that many hdrs to download
+ // will return false...
+ realFolder = MailUtils.getFolderForURI(uriToLoad);
+ msgDatabase = realFolder.msgDatabase;
+// dump("search term string = " + searchTermString + "\n");
+
+ gVirtualFolderTerms = CreateGroupedSearchTerms(tempFilter.searchTerms);
+ }
+ }
+ msgDatabase = null;
+ dbFolderInfo = null;
+ }
+ }
+ catch (ex)
+ {
+ dump("failed to get view & sort values. ex = " + ex +"\n");
+ }
+ }
+ // clear cached view if we have no db or a pending quick search
+ if (!gDBView || gDBView.viewType == nsMsgViewType.eShowQuickSearchResults)
+ {
+ if (gPreQuickSearchView) //close cached view before quick search
+ {
+ gPreQuickSearchView.close();
+ gPreQuickSearchView = null;
+ }
+ var searchInput = document.getElementById("searchInput"); //reset the search input on folder switch
+ if (searchInput)
+ searchInput.value = "";
+ }
+ ClearMessagePane();
+
+ if (gXFVirtualFolderTerms)
+ viewType = nsMsgViewType.eShowVirtualFolderResults;
+ else if (gSearchEmailAddress || gVirtualFolderTerms)
+ viewType = nsMsgViewType.eShowQuickSearchResults;
+ else if (viewType == nsMsgViewType.eShowQuickSearchResults)
+ viewType = nsMsgViewType.eShowAllThreads; //override viewType - we don't want to start w/ quick search
+ ChangeFolder(realFolder, msgFolder, viewType, viewFlags, sortType, sortOrder);
+ if (gVirtualFolderTerms)
+ gDBView.viewFolder = msgFolder;
+
+ let tabmail = GetTabMail();
+ if (tabmail)
+ {
+ tabmail.saveCurrentTabState(); // gDBView may have changed!
+ tabmail.setTabTitle();
+ }
+ }
+ else
+ {
+ msgWindow.openFolder = null;
+ ClearThreadPane();
+ }
+
+ if (gAccountCentralLoaded)
+ UpdateMailToolbar("gAccountCentralLoaded");
+
+ if (gDisplayStartupPage)
+ {
+ loadStartPage();
+ gDisplayStartupPage = false;
+ UpdateMailToolbar("gDisplayStartupPage");
+ }
+}
+
+function ClearThreadPane()
+{
+ if (gDBView) {
+ gDBView.close();
+ gDBView = null;
+ }
+}
+
+var mailOfflineObserver = {
+ observe: function(subject, topic, state) {
+ // sanity checks
+ if (topic != "network:offline-status-changed") return;
+ MailOfflineStateChanged(state == "offline");
+ }
+}
+
+function AddMailOfflineObserver()
+{
+ Services.obs.addObserver(mailOfflineObserver, "network:offline-status-changed");
+}
+
+function RemoveMailOfflineObserver()
+{
+ Services.obs.removeObserver(mailOfflineObserver, "network:offline-status-changed");
+}
+
+function getSearchTermString(searchTerms)
+{
+ var searchIndex;
+ var condition = "";
+ var count = searchTerms.length;
+ for (searchIndex = 0; searchIndex < count; )
+ {
+ var term = searchTerms[searchIndex++];
+
+ if (condition.length > 1)
+ condition += ' ';
+
+ if (term.matchAll)
+ {
+ condition = "ALL";
+ break;
+ }
+ condition += (term.booleanAnd) ? "AND (" : "OR (";
+ condition += term.termAsString + ')';
+ }
+ return condition;
+}
+
+function CreateVirtualFolder(newName, parentFolder, searchFolderURIs, searchTerms, searchOnline)
+{
+ // ### need to make sure view/folder doesn't exist.
+ if (searchFolderURIs && (searchFolderURIs != "") && newName && (newName != ""))
+ {
+ var newFolder;
+ try
+ {
+ if (parentFolder instanceof(Ci.nsIMsgLocalMailFolder))
+ newFolder = parentFolder.createLocalSubfolder(newName);
+ else
+ newFolder = parentFolder.addSubfolder(newName);
+ newFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
+ var vfdb = newFolder.msgDatabase;
+ var searchTermString = getSearchTermString(searchTerms);
+ var dbFolderInfo = vfdb.dBFolderInfo;
+ // set the view string as a property of the db folder info
+ // set the original folder name as well.
+ dbFolderInfo.setCharProperty("searchStr", searchTermString);
+ dbFolderInfo.setCharProperty("searchFolderUri", searchFolderURIs);
+ dbFolderInfo.setBooleanProperty("searchOnline", searchOnline);
+ vfdb.summaryValid = true;
+ vfdb.Close(true);
+ parentFolder.notifyFolderAdded(newFolder);
+ MailServices.accounts.saveVirtualFolders();
+ }
+ catch(e)
+ {
+ throw(e); // so that the dialog does not automatically close
+ dump ("Exception : creating virtual folder \n");
+ }
+ }
+ else
+ {
+ dump("no name or nothing selected\n");
+ }
+}
+
+var searchSessionContractID = "@mozilla.org/messenger/searchSession;1";
+var gSearchSession;
+
+var nsMsgSearchScope = Ci.nsMsgSearchScope;
+
+var gMessengerBundle = null;
+
+var gViewSearchListener;
+
+function GetScopeForFolder(folder)
+{
+ return folder.server.searchScope;
+}
+
+function setupXFVirtualFolderSearch(folderUrisToSearch, searchTerms, searchOnline)
+{
+ var count = new Object;
+ var i;
+
+ gSearchSession = Cc[searchSessionContractID]
+ .createInstance(Ci.nsIMsgSearchSession);
+
+ for (i in folderUrisToSearch)
+ {
+ let realFolder = MailUtils.getFolderForURI(folderUrisToSearch[i]);
+ if (!realFolder.isServer)
+ gSearchSession.addScopeTerm(!searchOnline ? nsMsgSearchScope.offlineMail : GetScopeForFolder(realFolder), realFolder);
+ }
+
+ for (let term of searchTerms) {
+ gSearchSession.appendTerm(term);
+ }
+}
+
+/**
+ * Uses an array of search terms to produce a new list usable from quick search.
+ *
+ * @param searchTermsArray A nsIArray of terms to copy.
+ *
+ * @return nsIMutableArray of search terms
+ */
+function CreateGroupedSearchTerms(searchTermsArray)
+{
+
+ var searchSession = gSearchSession ||
+ Cc[searchSessionContractID].createInstance(Ci.nsIMsgSearchSession);
+
+ // Create a temporary nsIMutableArray to store our search terms
+ // since we will be modifying the terms so they work with quick search.
+ var searchTermsArrayForQS = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+
+ var numEntries = searchTermsArray.length;
+ for (let i = 0; i < numEntries; i++) {
+ let searchTerm = searchTermsArray[i];
+
+ // clone the term, since we might be modifying it
+ var searchTermForQS = searchSession.createTerm();
+ searchTermForQS.value = searchTerm.value;
+ searchTermForQS.attrib = searchTerm.attrib;
+ searchTermForQS.arbitraryHeader = searchTerm.arbitraryHeader
+ searchTermForQS.hdrProperty = searchTerm.hdrProperty;
+ searchTermForQS.customId = searchTerm.customId
+ searchTermForQS.op = searchTerm.op;
+
+ // mark the first node as a group
+ if (i == 0)
+ searchTermForQS.beginsGrouping = true;
+ else if (i == numEntries - 1)
+ searchTermForQS.endsGrouping = true;
+
+ // turn the first term to true to work with quick search...
+ searchTermForQS.booleanAnd = i ? searchTerm.booleanAnd : true;
+
+ searchTermsArrayForQS.appendElement(searchTermForQS);
+ }
+ return searchTermsArrayForQS;
+}
+
+function OnLeavingFolder(aFolder)
+{
+ try
+ {
+ // Mark all messages of aFolder as read:
+ // We can't use the command controller, because it is already tuned in to the
+ // new folder, so we just mimic its behaviour wrt goDoCommand('cmd_markAllRead').
+ if (gDBView && Services.prefs.getBoolPref("mailnews.mark_message_read." + aFolder.server.type))
+ {
+ gDBView.doCommand(nsMsgViewCommandType.markAllRead);
+ }
+ }
+ catch(e){/* ignore */}
+}
+
+var gViewDebug = false;
+
+function viewDebug(str)
+{
+ if (gViewDebug)
+ dump(str);
+}
+
diff --git a/comm/suite/mailnews/content/folderDisplay.js b/comm/suite/mailnews/content/folderDisplay.js
new file mode 100644
index 0000000000..0318fb2e66
--- /dev/null
+++ b/comm/suite/mailnews/content/folderDisplay.js
@@ -0,0 +1,142 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gFolderDisplay =
+{
+ get selectedCount()
+ {
+ return gDBView ? gDBView.numSelected : 0;
+ },
+
+ get selectedMessage()
+ {
+ if (!this.selectedIndices.length)
+ return null;
+ return gDBView.hdrForFirstSelectedMessage;
+ },
+
+ get selectedMessageUri()
+ {
+ if (!this.selectedIndices.length)
+ return null;
+ return gDBView.URIForFirstSelectedMessage;
+ },
+
+ get selectedMessageIsFeed()
+ {
+ return FeedMessageHandler.isFeedMessage(this.selectedMessage);
+ },
+
+ get selectedMessageIsImap()
+ {
+ var message = this.selectedMessage;
+ return message && message.folder &&
+ (message.folder.flags & Ci.nsMsgFolderFlags.ImapBox) != 0;
+ },
+
+ get selectedMessageIsNews()
+ {
+ var message = this.selectedMessage;
+ return message && message.folder &&
+ (message.folder.flags & Ci.nsMsgFolderFlags.Newsgroup) != 0;
+ },
+
+ get selectedMessageIsExternal()
+ {
+ var message = this.selectedMessage;
+ return message && !message.folder;
+ },
+
+ get selectedIndices()
+ {
+ return gDBView ? gDBView.getIndicesForSelection() : [];
+ },
+
+ get selectedMessages()
+ {
+ return gDBView ? gDBView.getSelectedMsgHdrs() : [];
+ },
+
+ get selectedMessageUris()
+ {
+ if (!gDBView)
+ return null;
+ var messageArray = gDBView.getURIsForSelection();
+ return messageArray.length ? messageArray : null;
+ },
+
+ get canArchiveSelectedMessages()
+ {
+ if (!gDBView)
+ return false;
+ var selectedMessages = this.selectedMessages;
+ if (selectedMessages.length == 0)
+ return false;
+ return selectedMessages.every(function(aMsg) {
+ let identity = GetIdentityForHeader(aMsg);
+ return identity && identity.archiveEnabled;
+ });
+ },
+
+ get displayedFolder()
+ {
+ return gMsgFolderSelected;
+ },
+
+ /**
+ * Determine which pane currently has focus (one of the folder pane, thread
+ * pane, or message pane). When changing focus to the message pane, be sure
+ * to focus the appropriate content window in addition to the messagepanebox
+ * (doing both is required in order to blur the previously-focused chrome
+ * element).
+ *
+ * @return the focused pane
+ */
+ get focusedPane() {
+ let panes = ["threadTree", "folderTree", "messagepanebox"].map(id =>
+ document.getElementById(id));
+
+ let currentNode = top.document.activeElement;
+
+ while (currentNode) {
+ if (panes.includes(currentNode)) {
+ return currentNode;
+ }
+
+ currentNode = currentNode.parentNode;
+ }
+ return null;
+ },
+
+}
+
+var gMessageDisplay =
+{
+ get displayedMessage()
+ {
+ if (!gDBView)
+ return null;
+ var viewIndex = gDBView.currentlyDisplayedMessage;
+ return viewIndex == nsMsgViewIndex_None ? null :
+ gDBView.getMsgHdrAt(viewIndex);
+ },
+
+ get isDummy()
+ {
+ return gDBView && gDBView.keyForFirstSelectedMessage == nsMsgKey_None;
+ },
+
+ get visible()
+ {
+ return !GetMessagePane().collapsed;
+ },
+
+ set visible(aVisible)
+ {
+ return aVisible; // Fake setter for the time being.
+ }
+}
+
+gFolderDisplay.messageDisplay = gMessageDisplay;
diff --git a/comm/suite/mailnews/content/folderPane.js b/comm/suite/mailnews/content/folderPane.js
new file mode 100644
index 0000000000..35cdc8849a
--- /dev/null
+++ b/comm/suite/mailnews/content/folderPane.js
@@ -0,0 +1,2221 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Implements a tree of folders. It shows icons depending on folder type
+// and other fancy styling.
+// This is used in the main folder pane, but also some dialogs that need
+// to show a nice list of folders.
+
+var { FeedUtils } =
+ ChromeUtils.import("resource:///modules/FeedUtils.jsm");
+var { FolderUtils } =
+ ChromeUtils.import("resource:///modules/FolderUtils.jsm");
+var { IOUtils } =
+ ChromeUtils.import("resource:///modules/IOUtils.js");
+var { IteratorUtils } =
+ ChromeUtils.import("resource:///modules/iteratorUtils.jsm");
+var { mailServices } =
+ ChromeUtils.import("resource:///modules/mailServices.js");
+var { MailUtils } =
+ ChromeUtils.import("resource:///modules/MailUtils.js");
+var { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+var { Services } =
+ ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+if (typeof FeedMessageHandler != "object") {
+ Services.scriptloader.loadSubScript("chrome://messenger-newsblog/content/newsblogOverlay.js");
+}
+
+const kDefaultMode = "all";
+
+/**
+ * This file contains the controls and functions for the folder pane.
+ * The following definitions will be useful to know:
+ *
+ * gFolderTreeView - the controller for the folder tree.
+ * ftvItem - folder tree view item, representing a row in the tree
+ * mode - folder view type, e.g., all folders, favorite folders, MRU...
+ */
+
+ /**
+ * An interface that needs to be implemented in order to add a new view to the
+ * folder tree. For default behavior, it is recommended that implementers
+ * subclass this interface instead of relying on duck typing.
+ *
+ * For implementation examples, see |gFolderTreeView._modes|. For how to
+ * register this mode with |gFolderTreeView|, see
+ * |gFolderTreeView.registerFolderTreeMode|.
+ */
+let IFolderTreeMode = {
+ /**
+ * Generates the folder map for this mode.
+ *
+ * @param aFolderTreeView The gFolderTreeView for which this mode is being
+ * activated.
+ *
+ * @returns An array containing ftvItem instances representing the top-level
+ * folders in this view.
+ */
+ generateMap: function IFolderTreeMode_generateMap(aFolderTreeView) {
+ return null;
+ },
+
+ /**
+ * Given an nsIMsgFolder, returns its parent in the map. The default behaviour
+ * is to return the folder's actual parent (aFolder.parent). Folder tree modes
+ * may decide to override it.
+ *
+ * If the parent isn't easily computable given just the folder, you may
+ * consider generating the entire ftvItem tree at once and using a map from
+ * folders to ftvItems.
+ *
+ * @returns an nsIMsgFolder representing the parent of the folder in the view,
+ * or null if the folder is a top-level folder in the map. It is expected
+ * that the returned parent will have the given folder as one of its
+ * children.
+ * @note This function need not guarantee that either the folder or its parent
+ * is actually in the view.
+ */
+ getParentOfFolder: function IFolderTreeMode_getParentOfFolder(aFolder) {
+ return aFolder.parent;
+ },
+
+ /**
+ * Given an nsIMsgDBHdr, returns the folder it is considered to be contained
+ * in, in this mode. This is usually just the physical folder it is contained
+ * in (aMsgHdr.folder), but some modes may decide to override this. For
+ * example, combined views like Smart Folders return the smart inbox for any
+ * messages in any inbox.
+ *
+ * The folder returned doesn't need to be in the view.
+ *
+ * @returns The folder the message header is considered to be contained in, in
+ * this mode. The returned folder may or may not actually be in the view
+ * -- however, given a valid nsIMsgDBHdr, it is expected that a) a
+ * non-null folder is returned, and that b) the folder that is returned
+ * actually does contain the message header.
+ */
+ getFolderForMsgHdr: function IFolderTreeMode_getFolderForMsgHdr(aMsgHdr) {
+ return aMsgHdr.folder;
+ },
+
+ /**
+ * Notified when a folder is added. The default behavior is to add it as a
+ * child of the parent item, but some views may decide to override this. For
+ * example, combined views like Smart Folders add any new inbox as a child of
+ * the smart inbox.
+ *
+ * @param aParent The parent of the folder that was added.
+ * @param aFolder The folder that was added.
+ */
+ onFolderAdded: function IFolderTreeMode_onFolderAdded(aParent, aFolder) {
+ gFolderTreeView.addFolder(aParent, aFolder);
+ },
+
+ /**
+ * Notified when a folder int property is changed.
+ *
+ * Returns true if the event was processed inside the function and no further
+ * default handling should be done in the caller. Otherwise false.
+ *
+ * @param aItem The folder with a change.
+ * @param aProperty The changed property string.
+ * @param aOld The old value of the property.
+ * @param aNew The new value of the property.
+ */
+ handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+ return false;
+ }
+};
+
+/**
+ * This is our controller for the folder-tree. It includes our nsITreeView
+ * implementation, as well as other control functions.
+ */
+let gFolderTreeView = {
+ messengerBundle: null,
+
+ /**
+ * Called when the window is initially loaded. This function initializes the
+ * folder-pane to the view last shown before the application was closed.
+ */
+ load: function ftv_load(aTree, aJSONFile) {
+ this._treeElement = aTree;
+ this.messengerBundle = document.getElementById("bundle_messenger");
+
+ // The folder pane can be used for other trees which may not have these
+ // elements.
+ if (document.getElementById("folderpane-splitter"))
+ document.getElementById("folderpane-splitter").collapsed = false;
+ if (document.getElementById("folderPaneBox"))
+ document.getElementById("folderPaneBox").collapsed = false;
+
+ if (aJSONFile) {
+ // Parse our persistent-open-state json file.
+ let data = IOUtils.loadFileToString(aJSONFile);
+ if (data) {
+ try {
+ this._persistOpenMap = JSON.parse(data);
+ } catch (x) {
+ Cu.reportError(gFolderTreeView.messengerBundle.getFormattedString("failedToReadFile", [aJSONFile, x]));
+ }
+ }
+ }
+
+ // Load our data.
+ this._rebuild();
+ // And actually draw the tree.
+ aTree.view = this;
+
+ gFolderStatsHelpers.init();
+
+ // Add this listener so that we can update the tree when things change.
+ MailServices.mailSession.AddFolderListener(this, Ci.nsIFolderListener.all);
+ },
+
+ /**
+ * Called when the window is being torn down. Here we undo everything we did
+ * onload. That means removing our listener and serializing our JSON.
+ */
+ unload: function ftv_unload(aJSONFile) {
+ // Remove our listener.
+ MailServices.mailSession.RemoveFolderListener(this);
+
+ if (aJSONFile) {
+ // Write out our json file...
+ let data = JSON.stringify(this._persistOpenMap);
+ IOUtils.saveStringToFile(aJSONFile, data);
+ }
+ },
+
+ /**
+ * Extensions can use this function to add a new mode to the folder pane.
+ *
+ * @param aCommonName an internal name to identify this mode. Must be unique
+ * @param aMode An implementation of |IFolderTreeMode| for this mode.
+ * @param aDisplayName a localized name for this mode
+ */
+ registerFolderTreeMode: function ftv_registerFolderTreeMode(aCommonName,
+ aMode,
+ aDisplayName) {
+ this._modeNames.push(aCommonName);
+ this._modes[aCommonName] = aMode;
+ this._modeDisplayNames[aCommonName] = aDisplayName;
+ },
+
+ /**
+ * Unregisters a previously registered mode. Since common-names must be unique
+ * this is all that need be provided to unregister.
+ * @param aCommonName the common-name with which the mode was previously
+ * registered
+ */
+ unregisterFolderTreeMode: function ftv_unregisterFolderTreeMode(aCommonName) {
+ this._modeNames.splice(this._modeNames.indexOf(aCommonName), 1);
+ delete this._modes[aCommonName];
+ delete this._modeDisplayNames[aCommonName];
+ if (this._mode == aCommonName)
+ this.mode = kDefaultMode;
+ },
+
+ /**
+ * Retrieves a specific mode object
+ * @param aCommonName the common-name with which the mode was previously
+ * registered
+ */
+ getFolderTreeMode: function ftv_getFolderTreeMode(aCommonName) {
+ return this._modes[aCommonName];
+ },
+
+ /**
+ * Called to move to the next/prev folder-mode in the list
+ *
+ * @param aForward whether or not we should move forward in the list
+ */
+ cycleMode: function ftv_cycleMode(aForward) {
+ let index = this._modeNames.indexOf(this.mode);
+ let offset = aForward ? 1 : this._modeNames.length - 1;
+ index = (index + offset) % this._modeNames.length;
+
+ this.mode = this._modeNames[index];
+ },
+
+ /**
+ * If the hidden pref is set, then double-clicking on a folder should open it
+ *
+ * @param event the double-click event
+ */
+ onDoubleClick: function ftv_onDoubleClick(aEvent) {
+ if (aEvent.button != 0 || aEvent.originalTarget.localName == "twisty" ||
+ aEvent.originalTarget.localName == "slider" ||
+ aEvent.originalTarget.localName == "scrollbarbutton")
+ return;
+
+ let row = gFolderTreeView._treeElement.treeBoxObject.getRowAt(aEvent.clientX,
+ aEvent.clientY);
+ let folderItem = gFolderTreeView._rowMap[row];
+ if (folderItem)
+ folderItem.command();
+
+ // Don't let the double-click toggle the open state of the folder here.
+ aEvent.stopPropagation();
+ },
+
+ onKeyPress(event) {
+ if (event.keyCode == KeyEvent.DOM_VK_RETURN) {
+ if ((AppConstants.platform == "macosx" ? event.metaKey : event.ctrlKey) &&
+ AllowOpenTabOnMiddleClick()) {
+ FolderPaneContextMenuNewTab(event);
+ let folderTree = document.getElementById("folderTree");
+ RestoreSelectionWithoutContentLoad(folderTree);
+ }
+ }
+ },
+
+ getFolderAtCoords: function ftv_getFolderAtCoords(aX, aY) {
+ let row = gFolderTreeView._treeElement.treeBoxObject.getRowAt(aX, aY);
+ if (row in gFolderTreeView._rowMap)
+ return gFolderTreeView._rowMap[row]._folder;
+ return null;
+ },
+
+ /**
+ * A string representation for the current display-mode. Each value here must
+ * correspond to an entry in _modes
+ */
+ _mode: null,
+ get mode() {
+ if (!this._mode) {
+ this._mode = this._treeElement.getAttribute("mode");
+ // This can happen when an extension is removed.
+ if (!(this._mode in this._modes))
+ this._mode = kDefaultMode;
+ }
+ return this._mode;
+ },
+
+ /**
+ * @param aMode The final name of the mode to switch to.
+ */
+ set mode(aMode) {
+ // Ignore unknown modes.
+ if (!(aMode in this._modes))
+ return;
+
+ this._mode = aMode;
+
+ // Store current mode and actually build the folder pane.
+ this._treeElement.setAttribute("mode", this._mode);
+ this._rebuild();
+ },
+
+ /**
+ * Selects a given nsIMsgFolder in the tree. This function will also ensure
+ * that the folder is actually being displayed (that is, that none of its
+ * ancestors are collapsed.
+ *
+ * @param aFolder the nsIMsgFolder to select
+ * @param [aForceSelect] Whether we should switch to the default mode to
+ * select the folder in case we didn't find the folder in the current
+ * view. Defaults to false.
+ * @returns true if the folder selection was successful, false if it failed
+ * (probably because the folder isn't in the view at all)
+ */
+ selectFolder: function ftv_selectFolder(aFolder, aForceSelect = false) {
+ // "this" inside the nested function refers to the function...
+ // Also note that openIfNot is recursive.
+ let tree = this;
+ let folderTreeMode = this._modes[this._mode];
+ function openIfNot(aFolderToOpen) {
+ let index = tree.getIndexOfFolder(aFolderToOpen);
+ if (index != null) {
+ if (!tree._rowMap[index].open)
+ tree._toggleRow(index, false);
+ return true;
+ }
+
+ // Not found, so open the parent.
+ let parent = folderTreeMode.getParentOfFolder(aFolderToOpen);
+ if (parent && openIfNot(parent)) {
+ // Now our parent is open, so we can open ourselves.
+ index = tree.getIndexOfFolder(aFolderToOpen);
+ if (index != null) {
+ tree._toggleRow(index, false);
+ return true;
+ }
+ }
+
+ // No way we can find the folder now.
+ return false;
+ }
+ let parent = folderTreeMode.getParentOfFolder(aFolder);
+ if (parent)
+ openIfNot(parent);
+
+ let folderIndex = tree.getIndexOfFolder(aFolder);
+ if (folderIndex == null) {
+ if (aForceSelect) {
+ // Switch to the default mode. The assumption here is that the default
+ // mode can display every folder.
+ this.mode = kDefaultMode;
+ // We don't want to get stuck in an infinite recursion,
+ // so pass in false.
+ return this.selectFolder(aFolder, false);
+ }
+
+ return false;
+ }
+
+ this.selection.select(folderIndex);
+ this._treeElement.treeBoxObject.ensureRowIsVisible(folderIndex);
+ return true;
+ },
+
+ /**
+ * Returns the index of a folder in the current display.
+ *
+ * @param aFolder the folder whose index should be returned.
+ * @returns The index of the folder in the view (a number).
+ * @note If the folder is not in the display (perhaps because one of its
+ * anscetors is collapsed), this function returns null.
+ */
+ getIndexOfFolder: function ftv_getIndexOfFolder(aFolder) {
+ for (let [iRow, row] of this._rowMap.entries()) {
+ if (row.id == aFolder.URI)
+ return iRow;
+ }
+ return null;
+ },
+
+ /**
+ * Returns the folder for an index in the current display.
+ *
+ * @param aIndex the index for which the folder should be returned.
+ * @note If the index is out of bounds, this function returns null.
+ */
+ getFolderForIndex: function ftv_getFolderForIndex(aIndex) {
+ if (aIndex < 0 || aIndex >= this._rowMap.length)
+ return null;
+ return this._rowMap[aIndex]._folder;
+ },
+
+ /**
+ * Returns the parent of a folder in the current view. This may be, but is not
+ * necessarily, the actual parent of the folder (aFolder.parent). In
+ * particular, in the smart view, special folders are usually children of the
+ * smart folder of that kind.
+ *
+ * @param aFolder The folder to get the parent of.
+ * @returns The parent of the folder, or null if the parent wasn't found.
+ * @note This function does not guarantee that either the folder or its parent
+ * is actually in the view.
+ */
+ getParentOfFolder: function ftv_getParentOfFolder(aFolder) {
+ return this._modes[this._mode].getParentOfFolder(aFolder);
+ },
+
+ /**
+ * Given an nsIMsgDBHdr, returns the folder it is considered to be contained
+ * in, in the current mode. This is usually, but not necessarily, the actual
+ * folder the message is in (aMsgHdr.folder). For more details, see
+ * |IFolderTreeMode.getFolderForMsgHdr|.
+ */
+ getFolderForMsgHdr: function ftv_getFolderForMsgHdr(aMsgHdr) {
+ return this._modes[this._mode].getFolderForMsgHdr(aMsgHdr);
+ },
+
+ /**
+ * Returns the |ftvItem| for an index in the current display. Intended for use
+ * by folder tree mode implementers.
+ *
+ * @param aIndex The index for which the ftvItem should be returned.
+ * @note If the index is out of bounds, this function returns null.
+ */
+ getFTVItemForIndex: function ftv_getFTVItemForIndex(aIndex) {
+ return this._rowMap[aIndex];
+ },
+
+ /**
+ * Returns an array of nsIMsgFolders corresponding to the current selection
+ * in the tree
+ */
+ getSelectedFolders: function ftv_getSelectedFolders() {
+ let selection = this.selection;
+ if (!selection)
+ return [];
+
+ let folderArray = [];
+ let rangeCount = selection.getRangeCount();
+ for (let i = 0; i < rangeCount; i++) {
+ let startIndex = {};
+ let endIndex = {};
+ selection.getRangeAt(i, startIndex, endIndex);
+ for (let j = startIndex.value; j <= endIndex.value; j++) {
+ if (j < this._rowMap.length)
+ folderArray.push(this._rowMap[j]._folder);
+ }
+ }
+ return folderArray;
+ },
+
+ /**
+ * Adds a new child |ftvItem| to the given parent |ftvItem|. Intended for use
+ * by folder tree mode implementers.
+ *
+ * @param aParentItem The parent ftvItem. It is assumed that this is visible
+ * in the view.
+ * @param aParentIndex The index of the parent ftvItem in the view.
+ * @param aItem The item to add.
+ */
+ addChildItem: function ftv_addChildItem(aParentItem, aParentIndex, aItem) {
+ this._addChildToView(aParentItem, aParentIndex, aItem);
+ },
+
+ // ****************** Start of nsITreeView implementation **************** //
+
+ get rowCount() {
+ return this._rowMap.length;
+ },
+
+ /**
+ * drag drop interfaces
+ */
+ canDrop: function ftv_canDrop(aRow, aOrientation) {
+ let targetFolder = gFolderTreeView._rowMap[aRow]._folder;
+ if (!targetFolder)
+ return false;
+ let dt = this._currentTransfer;
+ let types = Array.from(dt.mozTypesAt(0));
+ if (types.includes("text/x-moz-message")) {
+ if (aOrientation != Ci.nsITreeView.DROP_ON)
+ return false;
+ // Don't allow drop onto server itself.
+ if (targetFolder.isServer)
+ return false;
+ // Don't allow drop into a folder that cannot take messages.
+ if (!targetFolder.canFileMessages)
+ return false;
+ let messenger = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger);
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ let msgHdr = messenger.msgHdrFromURI(dt.mozGetDataAt("text/x-moz-message", i));
+ // Don't allow drop onto original folder.
+ if (msgHdr.folder == targetFolder)
+ return false;
+ }
+ return true;
+ }
+ else if (types.includes("text/x-moz-folder")) {
+ if (aOrientation != Ci.nsITreeView.DROP_ON)
+ return false;
+ // If cannot create subfolders then don't allow drop here.
+ if (!targetFolder.canCreateSubfolders)
+ return false;
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ let folder = dt.mozGetDataAt("text/x-moz-folder", i)
+ .QueryInterface(Ci.nsIMsgFolder);
+ // Don't allow to drop on itself.
+ if (targetFolder == folder)
+ return false;
+ // Don't copy within same server.
+ if ((folder.server == targetFolder.server) &&
+ (dt.dropEffect == 'copy'))
+ return false;
+ // Don't allow immediate child to be dropped onto its parent.
+ if (targetFolder == folder.parent)
+ return false;
+ // Don't allow dragging of virtual folders across accounts.
+ if ((folder.flags & Ci.nsMsgFolderFlags.Virtual) &&
+ folder.server != targetFolder.server)
+ return false;
+ // Don't allow parent to be dropped on its ancestors.
+ if (folder.isAncestorOf(targetFolder))
+ return false;
+ // If there is a folder that can't be renamed, don't allow it to be
+ // dropped if it is not to "Local Folders" or is to the same account.
+ if (!folder.canRename && (targetFolder.server.type != "none" ||
+ folder.server == targetFolder.server))
+ return false;
+ }
+ return true;
+ }
+ else if (types.includes("text/x-moz-newsfolder")) {
+ // Don't allow dragging onto element.
+ if (aOrientation == Ci.nsITreeView.DROP_ON)
+ return false;
+ // Don't allow drop onto server itself.
+ if (targetFolder.isServer)
+ return false;
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ let folder = dt.mozGetDataAt("text/x-moz-newsfolder", i)
+ .QueryInterface(Ci.nsIMsgFolder);
+ // Don't allow dragging newsgroup to other account.
+ if (targetFolder.rootFolder != folder.rootFolder)
+ return false;
+ // Don't allow dragging newsgroup to before/after itself.
+ if (targetFolder == folder)
+ return false;
+ // Don't allow dragging newsgroup to before item after or
+ // after item before.
+ let row = aRow + aOrientation;
+ if (row in gFolderTreeView._rowMap &&
+ (gFolderTreeView._rowMap[row]._folder == folder))
+ return false;
+ }
+ return true;
+ }
+ // Allow subscribing to feeds by dragging an url to a feed account.
+ else if (targetFolder.server.type == "rss" && dt.mozItemCount == 1)
+ return FeedUtils.getFeedUriFromDataTransfer(dt) ? true : false;
+ else if (types.includes("application/x-moz-file")) {
+ if (aOrientation != Ci.nsITreeView.DROP_ON)
+ return false;
+ // Don't allow drop onto server itself.
+ if (targetFolder.isServer)
+ return false;
+ // Don't allow drop into a folder that cannot take messages.
+ if (!targetFolder.canFileMessages)
+ return false;
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ let extFile = dt.mozGetDataAt("application/x-moz-file", i);
+ if (!extFile) {
+ continue;
+ }
+ return extFile.QueryInterface(Ci.nsIFile).isFile();
+ }
+ }
+ return false;
+ },
+ drop: function ftv_drop(aRow, aOrientation) {
+ let targetFolder = gFolderTreeView._rowMap[aRow]._folder;
+
+ let dt = this._currentTransfer;
+ let count = dt.mozItemCount;
+ let cs = MailServices.copy;
+
+ // This is a potential rss feed. A link image as well as link text url
+ // should be handled; try to extract a url from non moz apps as well.
+ let feedUri = targetFolder.server.type == "rss" && count == 1 ?
+ FeedUtils.getFeedUriFromDataTransfer(dt) : null;
+
+ // We only support drag of a single flavor at a time.
+ let types = Array.from(dt.mozTypesAt(0));
+ if (types.includes("text/x-moz-folder")) {
+ for (let i = 0; i < count; i++) {
+ let folder = dt.mozGetDataAt("text/x-moz-folder", i)
+ .QueryInterface(Ci.nsIMsgFolder);
+ cs.copyFolders(folder, targetFolder,
+ (folder.server == targetFolder.server), null,
+ msgWindow);
+ }
+ }
+ else if (types.includes("text/x-moz-newsfolder")) {
+ // Start by getting folders into order.
+ let folders = new Array;
+ for (let i = 0; i < count; i++) {
+ let folder = dt.mozGetDataAt("text/x-moz-newsfolder", i)
+ .QueryInterface(Ci.nsIMsgFolder);
+ folders[this.getIndexOfFolder(folder)] = folder;
+ }
+ let newsFolder = targetFolder.rootFolder
+ .QueryInterface(Ci.nsIMsgNewsFolder);
+ // When moving down, want to insert first one last.
+ // When moving up, want to insert first one first.
+ let i = (aOrientation == 1) ? folders.length - 1 : 0;
+ while (i >= 0 && i < folders.length) {
+ let folder = folders[i];
+ if (folder) {
+ newsFolder.moveFolder(folder, targetFolder, aOrientation);
+ this.selection.toggleSelect(this.getIndexOfFolder(folder));
+ }
+ i -= aOrientation;
+ }
+ }
+ else if (types.includes("text/x-moz-message")) {
+ let array = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ let sourceFolder;
+ let messenger = Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger);
+ for (let i = 0; i < count; i++) {
+ let msgHdr = messenger.msgHdrFromURI(dt.mozGetDataAt("text/x-moz-message", i));
+ if (!i)
+ sourceFolder = msgHdr.folder;
+ array.appendElement(msgHdr);
+ }
+ let isMove = Cc["@mozilla.org/widget/dragservice;1"]
+ .getService(Ci.nsIDragService).getCurrentSession()
+ .dragAction == Ci.nsIDragService.DRAGDROP_ACTION_MOVE;
+ let isNews = sourceFolder.flags & Ci.nsMsgFolderFlags.Newsgroup;
+ if (!sourceFolder.canDeleteMessages || isNews)
+ isMove = false;
+
+ Services.prefs.setCharPref("mail.last_msg_movecopy_target_uri",
+ targetFolder.URI);
+ Services.prefs.setBoolPref("mail.last_msg_movecopy_was_move", isMove);
+ // ### ugh, so this won't work with cross-folder views. We would
+ // really need to partition the messages by folder.
+ cs.copyMessages(sourceFolder, array, targetFolder, isMove, null,
+ msgWindow, true);
+ }
+ else if (feedUri) {
+ Cc["@mozilla.org/newsblog-feed-downloader;1"]
+ .getService(Ci.nsINewsBlogFeedDownloader)
+ .subscribeToFeed(feedUri.spec, targetFolder, msgWindow);
+ }
+ else if (types.includes("application/x-moz-file")) {
+ for (let i = 0; i < count; i++) {
+ let extFile = dt.mozGetDataAt("application/x-moz-file", i)
+ .QueryInterface(Ci.nsIFile);
+ if (extFile.isFile()) {
+ let len = extFile.leafName.length;
+ if (len > 4 && extFile.leafName.toLowerCase().endsWith(".eml"))
+ cs.copyFileMessage(extFile, targetFolder, null, false, 1, "", null,
+ msgWindow);
+ }
+ }
+ }
+ },
+
+ _onDragStart: function ftv_dragStart(aEvent) {
+ // Ugh, this is ugly but necessary.
+ let view = gFolderTreeView;
+
+ if (aEvent.originalTarget.localName != "treechildren")
+ return;
+
+ let folders = view.getSelectedFolders();
+ folders = folders.filter(function(f) { return !f.isServer; });
+ for (let i in folders) {
+ let flavor = folders[i].server.type == "nntp" ? "text/x-moz-newsfolder" :
+ "text/x-moz-folder";
+ aEvent.dataTransfer.mozSetDataAt(flavor, folders[i], i);
+ }
+ aEvent.dataTransfer.effectAllowed = "copyMove";
+ aEvent.dataTransfer.addElement(aEvent.originalTarget);
+ return;
+ },
+
+ _onDragOver: function ftv_onDragOver(aEvent) {
+ this._currentTransfer = aEvent.dataTransfer;
+ },
+
+ _onDragDrop: function ftv_onDragDrop(aEvent) {
+ this._currentTransfer = aEvent.dataTransfer;
+ },
+
+ /**
+ * CSS files will cue off of these. Note that we reach into the rowMap's
+ * items so that custom data-displays can define their own properties
+ */
+ getCellProperties: function ftv_getCellProperties(aRow, aCol) {
+ return this._rowMap[aRow].getProperties(aCol);
+ },
+
+ /**
+ * The actual text to display in the tree
+ */
+ getCellText: function ftv_getCellText(aRow, aCol) {
+ if ((aCol.id == "folderNameCol") ||
+ (aCol.id == "folderUnreadCol") ||
+ (aCol.id == "folderTotalCol") ||
+ (aCol.id == "folderSizeCol"))
+ return this._rowMap[aRow].getText(aCol.id);
+ return "";
+ },
+
+ /**
+ * The ftvItems take care of assigning this when created.
+ */
+ getLevel: function ftv_getLevel(aIndex) {
+ return this._rowMap[aIndex].level;
+ },
+
+ /**
+ * The ftvItems take care of assigning this when building children lists
+ */
+ getServerNameAdded: function ftv_getServerNameAdded(aIndex) {
+ return this._rowMap[aIndex].addServerName;
+ },
+
+ /**
+ * This is easy since the ftv items assigned the _parent property when making
+ * the child lists
+ */
+ getParentIndex: function ftv_getParentIndex(aIndex) {
+ return this._rowMap.indexOf(this._rowMap[aIndex]._parent);
+ },
+
+ /**
+ * This is duplicative for our normal ftv views, but custom data-displays may
+ * want to do something special here
+ */
+ getRowProperties: function ftv_getRowProperties(aRow) {
+ return this._rowMap[aRow].getProperties();
+ },
+
+ /**
+ * Check whether there are any more rows with our level before the next row
+ * at our parent's level
+ */
+ hasNextSibling: function ftv_hasNextSibling(aIndex, aNextIndex) {
+ var currentLevel = this._rowMap[aIndex].level;
+ for (var i = aNextIndex + 1; i < this._rowMap.length; i++) {
+ if (this._rowMap[i].level == currentLevel)
+ return true;
+ if (this._rowMap[i].level < currentLevel)
+ return false;
+ }
+ return false;
+ },
+
+ /**
+ * All folders are containers, so we can drag drop messages to them.
+ */
+ isContainer: function ftv_isContainer(aIndex) {
+ return true;
+ },
+
+ isContainerEmpty: function ftv_isContainerEmpty(aIndex) {
+ // If the folder has no children, the container is empty.
+ return !this._rowMap[aIndex].children.length;
+ },
+
+ /**
+ * Just look at the ftvItem here
+ */
+ isContainerOpen: function ftv_isContainerOpen(aIndex) {
+ return this._rowMap[aIndex].open;
+ },
+ getSummarizedCounts: function(aIndex, aColName) {
+ return this._rowMap[aIndex]._summarizedCounts.get(aColName);
+ },
+ isEditable: function ftv_isEditable(aRow, aCol) {
+ // We don't support editing rows in the tree yet. We may want to later as
+ // an easier way to rename folders.
+ return false;
+ },
+ isSeparator: function ftv_isSeparator(aIndex) {
+ // There are no separators in our trees.
+ return false;
+ },
+ isSorted: function ftv_isSorted() {
+ // We do our own customized sorting.
+ return false;
+ },
+ setTree: function ftv_setTree(aTree) {
+ this._tree = aTree;
+ },
+
+ /**
+ * Opens or closes a folder with children. The logic here is a bit hairy, so
+ * be very careful about changing anything.
+ */
+ toggleOpenState: function ftv_toggleOpenState(aIndex) {
+ this._toggleRow(aIndex, true);
+ },
+
+ recursivelyAddToMap: function ftv_recursivelyAddToMap(aChild, aNewIndex) {
+ // When we add sub-children, we're going to need to increase our index
+ // for the next add item at our own level.
+ let count = 0;
+ if (aChild.children.length && aChild.open) {
+ for (let [i, child] of Array.from(this._rowMap[aNewIndex].children).entries()) {
+ count++;
+ let index = Number(aNewIndex) + Number(i) + 1;
+ this._rowMap.splice(index, 0, child);
+
+ let kidsAdded = this.recursivelyAddToMap(child, index);
+ count += kidsAdded;
+ // Somehow the aNewIndex turns into a string without this.
+ aNewIndex = Number(aNewIndex) + kidsAdded;
+ }
+ }
+ return count;
+ },
+
+ _toggleRow: function toggleRow(aIndex, aExpandServer)
+ {
+ // Ok, this is a bit tricky.
+ this._rowMap[aIndex].open = !this._rowMap[aIndex].open;
+ if (!this._rowMap[aIndex].open) {
+ // We're closing the current container. Remove the children.
+
+ // Note that we can't simply splice out children.length, because some of
+ // them might have children too. Find out how many items we're actually
+ // going to splice.
+ let count = 0;
+ let i = aIndex + 1;
+ let row = this._rowMap[i];
+ while (row && row.level > this._rowMap[aIndex].level) {
+ count++;
+ row = this._rowMap[++i];
+ }
+ this._rowMap.splice(aIndex + 1, count);
+
+ // Remove us from the persist map.
+ this._persistItemClosed(this._rowMap[aIndex].id);
+
+ // Notify the tree of changes.
+ if (this._tree) {
+ this._tree.rowCountChanged(aIndex + 1, (-1) * count);
+ this._tree.invalidateRow(aIndex);
+ }
+ } else {
+ // We're opening the container. Add the children to our map.
+
+ // Note that these children may have been open when we were last closed,
+ // and if they are, we also have to add those grandchildren to the map.
+ let oldCount = this._rowMap.length;
+ this.recursivelyAddToMap(this._rowMap[aIndex], aIndex);
+
+ // Add this folder to the persist map.
+ this._persistItemOpen(this._rowMap[aIndex].id);
+
+ // Notify the tree of changes.
+ if (this._tree) {
+ this._tree.rowCountChanged(aIndex + 1, this._rowMap.length - oldCount);
+ this._tree.invalidateRow(aIndex);
+ }
+
+ if (this._treeElement.getAttribute("simplelist") == "true")
+ return;
+
+ // If this was a server that was expanded, let it update its counts.
+ let folder = this._rowMap[aIndex]._folder;
+ if (aExpandServer) {
+ if (folder.isServer)
+ folder.server.performExpand(msgWindow);
+ else if (folder instanceof Ci.nsIMsgImapMailFolder)
+ folder.performExpand(msgWindow);
+ }
+ }
+ },
+
+ // We don't implement any of these at the moment.
+ performAction: function ftv_performAction(aAction) {},
+ performActionOnCell: function ftv_performActionOnCell(aAction, aRow, aCol) {},
+ performActionOnRow: function ftv_performActionOnRow(aAction, aRow) {},
+ selectionChanged: function ftv_selectionChanged() {},
+ setCellText: function ftv_setCellText(aRow, aCol, aValue) {},
+ setCellValue: function ftv_setCellValue(aRow, aCol, aValue) {},
+ getCellValue: function ftv_getCellValue(aRow, aCol) {},
+ getColumnProperties: function ftv_getColumnProperties(aCol) { return ""; },
+ getImageSrc: function ftv_getImageSrc(aRow, aCol) {},
+ getProgressMode: function ftv_getProgressMode(aRow, aCol) {},
+ cycleCell: function ftv_cycleCell(aRow, aCol) {},
+ cycleHeader: function ftv_cycleHeader(aCol) {},
+
+ // ****************** End of nsITreeView implementation **************** //
+
+ //
+ // WARNING: Everything below this point is considered private. Touch at your
+ // own risk.
+
+ /**
+ * This is an array of all possible modes for the folder tree. You should not
+ * modify this directly, but rather use registerFolderTreeMode.
+ *
+ * Internally each mode is defined separately. But in the UI we currently
+ * expose only the "base" name (see baseMode()) of the mode plus a
+ * "Compact view" option. The internal name of the mode to use is then
+ * constructed from the base name and "_compact" suffix if compact view is
+ * selected. See bug 978592.
+ */
+ _modeNames: ["all", "unread", "unread_compact", "favorite", "favorite_compact", "recent_compact"],
+ _modeDisplayNames: {},
+
+ /**
+ * This is a javascript map of which folders we had open, so that we can
+ * persist their state over-time. It is designed to be used as a JSON object.
+ */
+ _persistOpenMap: {},
+ _notPersistedModes: ["unread", "unread_compact", "favorite", "favorite_compact", "recent_compact"],
+
+ /**
+ * Iterate over the persistent list and open the items (folders) stored in it.
+ */
+ _restoreOpenStates: function ftv__persistOpenStates() {
+ let mode = this.mode;
+ // Remove any saved state of modes where open state should not be persisted.
+ // This is mostly for migration from older profiles that may have the info
+ // stored.
+ if (this._notPersistedModes.includes(mode)) {
+ delete this._persistOpenMap[mode];
+ }
+
+ let curLevel = 0;
+ let tree = this;
+ let map = tree._persistOpenMap[mode]; // may be undefined
+ function openLevel() {
+ let goOn = false;
+ // We can't use a js iterator because we're changing the array as we go.
+ // So fallback on old trick of going backwards from the end, which
+ // doesn't care when you add things at the end.
+ for (let i = tree._rowMap.length - 1; i >= 0; i--) {
+ let row = tree._rowMap[i];
+ if (row.level != curLevel)
+ continue;
+
+ // The initial state of all rows is closed,
+ // so toggle those we want open.
+ if (!map || map.includes(row.id)) {
+ tree._toggleRow(i, false);
+ goOn = true;
+ }
+ }
+
+ // If we opened up any new kids, we need to check their level as well.
+ curLevel++;
+ if (goOn)
+ openLevel();
+ }
+ openLevel();
+ },
+
+ /**
+ * Remove the item from the persistent list, meaning the item should
+ * be persisted as closed in the tree.
+ *
+ * @param aItemId The URI of the folder item.
+ */
+ _persistItemClosed: function ftv_unpersistItem(aItemId) {
+ let mode = this.mode;
+ if (this._notPersistedModes.includes(mode))
+ return;
+
+ // If the whole mode is not in the map yet,
+ // we can silently ignore the folder removal.
+ if (!this._persistOpenMap[mode])
+ return;
+
+ let persistMapIndex = this._persistOpenMap[mode].indexOf(aItemId);
+ if (persistMapIndex != -1)
+ this._persistOpenMap[mode].splice(persistMapIndex, 1);
+ },
+
+ /**
+ * Add the item from the persistent list, meaning the item should
+ * be persisted as open (expanded) in the tree.
+ *
+ * @param aItemId The URI of the folder item.
+ */
+ _persistItemOpen: function ftv_persistItem(aItemId) {
+ let mode = this.mode;
+ if (this._notPersistedModes.includes(mode))
+ return;
+
+ if (!this._persistOpenMap[mode])
+ this._persistOpenMap[mode] = [];
+
+ if (!this._persistOpenMap[mode].includes(aItemId))
+ this._persistOpenMap[mode].push(aItemId);
+ },
+
+ _tree: null,
+ selection: null,
+ /**
+ * An array of ftvItems, where each item corresponds to a row in the tree
+ */
+ _rowMap: null,
+
+ /**
+ * Completely discards the current tree and rebuilds it based on current
+ * settings
+ */
+ _rebuild: function ftv__rebuild() {
+ let newRowMap;
+ try {
+ newRowMap = this._modes[this.mode].generateMap(this);
+ } catch(ex) {
+ Services.console.logStringMessage("generator " + this.mode +
+ " failed with exception: " + ex);
+ this.mode = kDefaultMode;
+ newRowMap = this._modes[this.mode].generateMap(this);
+ }
+ let selectedFolders = this.getSelectedFolders();
+ if (this.selection)
+ this.selection.clearSelection();
+ // There's a chance the call to the map generator altered this._rowMap, so
+ // evaluate oldCount after calling it rather than before.
+ let oldCount = this._rowMap ? this._rowMap.length : null;
+ this._rowMap = newRowMap;
+
+ this._treeElement.dispatchEvent(new Event("mapRebuild",
+ { bubbles: true, cancelable: false }));
+
+ if (this._tree) {
+ if (oldCount !== null)
+ this._tree.rowCountChanged(0, this._rowMap.length - oldCount);
+ this._tree.invalidate();
+ }
+
+ this._restoreOpenStates();
+ // Restore selection.
+ for (let folder of selectedFolders) {
+ if (folder) {
+ let index = this.getIndexOfFolder(folder);
+ if (index != null)
+ this.selection.toggleSelect(index);
+ }
+ }
+ },
+
+ _sortedAccounts: function ftv_getSortedAccounts() {
+ let accounts = FolderUtils.allAccountsSorted(true);
+
+ // Don't show deferred pop accounts.
+ accounts = accounts.filter(function isNotDeferred(a) {
+ let server = a.incomingServer;
+ return !(server instanceof Ci.nsIPop3IncomingServer &&
+ server.deferredToAccount);
+ });
+
+ return accounts;
+ },
+ /**
+ * Contains the set of modes registered with the folder tree, initially those
+ * included by default. This is a map from names of modes to their
+ * implementations of |IFolderTreeMode|.
+ */
+ _modes: {
+ /**
+ * The all mode returns all folders, arranged in a hierarchy
+ */
+ all: {
+ __proto__: IFolderTreeMode,
+
+ generateMap: function(ftv) {
+ let accounts = gFolderTreeView._sortedAccounts();
+ // Force each root folder to do its local subfolder discovery.
+ MailUtils.discoverFolders();
+
+ return accounts.map(acct => new ftvItem(acct.incomingServer.rootFolder));
+ }
+ },
+
+ /**
+ * The unread mode returns all folders that are not root-folders and that
+ * have unread items. Also always keep the currently selected folder
+ * so it doesn't disappear under the user.
+ * It also includes parent folders of the Unread folders so the hierarchy
+ * shown.
+ */
+ unread: {
+ __proto__: IFolderTreeMode,
+
+ generateMap: function(ftv) {
+ let filterUnread = function filterUnread(aFolder) {
+ let currentFolder = gFolderTreeView.getSelectedFolders()[0];
+ return ((aFolder.getNumUnread(true) > 0) ||
+ (aFolder == currentFolder));
+ }
+
+ let accounts = gFolderTreeView._sortedAccounts();
+ // Force each root folder to do its local subfolder discovery.
+ MailUtils.discoverFolders();
+
+ let unreadRootFolders = [];
+ for (let acct of accounts) {
+ let rootFolder = acct.incomingServer.rootFolder;
+ // Add rootFolders of accounts that contain at least one Favorite
+ // folder.
+ if (rootFolder.getNumUnread(true) > 0)
+ unreadRootFolders.push(new ftvItem(rootFolder, filterUnread));
+ }
+
+ return unreadRootFolders;
+ },
+
+ handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+ // We want to rebuild only if we have a newly unread folder
+ // and we didn't already have the folder.
+ if (aProperty == "TotalUnreadMessages" && aOld == 0 && aNew > 0 &&
+ gFolderTreeView.getIndexOfFolder(aItem) == null) {
+ gFolderTreeView._rebuild();
+ return true;
+ }
+ return false;
+ }
+ },
+
+ /**
+ * A variant of the 'unread' mode above. This does not include the parent
+ * folders and the unread folders are shown in a flat list with no
+ * hierarchy.
+ */
+ unread_compact: {
+ __proto__: IFolderTreeMode,
+
+ generateMap: function(ftv) {
+ let map = [];
+ let currentFolder = gFolderTreeView.getSelectedFolders()[0];
+ for (let folder of ftv._enumerateFolders) {
+ if ((!folder.isServer && folder.getNumUnread(false) > 0) ||
+ (folder == currentFolder))
+ map.push(new ftvItem(folder));
+ }
+
+ // There are no children in this view!
+ for (let folder of map) {
+ folder.__defineGetter__("children", () => []);
+ folder.addServerName = true;
+ }
+ sortFolderItems(map);
+ return map;
+ },
+
+ getParentOfFolder: function(aFolder) {
+ // This is a flat view, so no folders have parents.
+ return null;
+ },
+
+ handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+ // We want to rebuild only if we have a newly unread folder
+ // and we didn't already have the folder.
+ if (aProperty == "TotalUnreadMessages" && aOld == 0 && aNew > 0 &&
+ gFolderTreeView.getIndexOfFolder(aItem) == null) {
+ gFolderTreeView._rebuild();
+ return true;
+ }
+ return false;
+ }
+ },
+
+ /**
+ * The favorites mode returns all folders whose flags are set to include
+ * the favorite flag.
+ * It also includes parent folders of the Unread folders so the hierarchy
+ * shown.
+ */
+ favorite: {
+ __proto__: IFolderTreeMode,
+
+ generateMap: function(ftv) {
+ let accounts = gFolderTreeView._sortedAccounts();
+ // Force each root folder to do its local subfolder discovery.
+ MailUtils.discoverFolders();
+
+ let favRootFolders = [];
+ let filterFavorite = function filterFavorite(aFolder) {
+ return aFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Favorite) != null;
+ }
+ for (let acct of accounts) {
+ let rootFolder = acct.incomingServer.rootFolder;
+ // Add rootFolders of accounts that contain at least one Favorite folder.
+ if (filterFavorite(rootFolder))
+ favRootFolders.push(new ftvItem(rootFolder, filterFavorite));
+ }
+
+ return favRootFolders;
+ },
+
+ handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+ // We want to rebuild if the favorite status of a folder changed.
+ if (aProperty == "FolderFlag" &&
+ ((aOld & Ci.nsMsgFolderFlags.Favorite) !=
+ (aNew & Ci.nsMsgFolderFlags.Favorite))) {
+ gFolderTreeView._rebuild();
+ return true;
+ }
+ return false;
+ }
+ },
+
+ /**
+ * A variant of the 'favorite' mode above. This does not include the parent
+ * folders and the unread folders are shown in a compact list with no
+ * hierarchy.
+ */
+ favorite_compact: {
+ __proto__: IFolderTreeMode,
+
+ generateMap: function(ftv) {
+ let faves = [];
+ for (let folder of ftv._enumerateFolders) {
+ if (folder.flags & Ci.nsMsgFolderFlags.Favorite)
+ faves.push(new ftvItem(folder));
+ }
+
+ // We want to display the account name alongside folders that have
+ // duplicated folder names.
+ let uniqueNames = new Set(); // set of folder names seen at least once
+ let dupeNames = new Set(); // set of folders seen at least twice
+ for (let item of faves) {
+ let name = item._folder.abbreviatedName.toLocaleLowerCase();
+ if (uniqueNames.has(name)) {
+ if (!dupeNames.has(name))
+ dupeNames.add(name);
+ } else {
+ uniqueNames.add(name);
+ }
+ }
+
+ // There are no children in this view!
+ for (let item of faves) {
+ let name = item._folder.abbreviatedName.toLocaleLowerCase();
+ item.__defineGetter__("children", () => []);
+ item.addServerName = dupeNames.has(name);
+ }
+ sortFolderItems(faves);
+ return faves;
+ },
+
+ getParentOfFolder: function(aFolder) {
+ // This is a flat view, so no folders have parents.
+ return null;
+ },
+
+ handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+ // We want to rebuild if the favorite status of a folder changed.
+ if (aProperty == "FolderFlag" &&
+ ((aOld & Ci.nsMsgFolderFlags.Favorite) !=
+ (aNew & Ci.nsMsgFolderFlags.Favorite))) {
+ gFolderTreeView._rebuild();
+ return true;
+ }
+ return false;
+ }
+ },
+
+ /**
+ * The recent mode is a flat view of the 15 most recently used folders
+ */
+ recent_compact: {
+ __proto__: IFolderTreeMode,
+
+ generateMap: function(ftv) {
+ const MAXRECENT = 15;
+
+ // Get 15 (MAXRECENT) most recently accessed folders.
+ let recentFolders = FolderUtils.getMostRecentFolders(
+ ftv._enumerateFolders,
+ MAXRECENT,
+ "MRUTime",
+ null
+ );
+
+ // Sort the folder names alphabetically.
+ recentFolders.sort(function rf_sort(a, b){
+ let aLabel = a.prettyName;
+ let bLabel = b.prettyName;
+ if (aLabel == bLabel) {
+ aLabel = a.server.prettyName;
+ bLabel = b.server.prettyName;
+ }
+ return FolderUtils.folderNameCompare(aLabel, bLabel);
+ });
+
+ let items = recentFolders.map(f => new ftvItem(f));
+
+ // There are no children in this view!
+ // And we want to display the account name to distinguish folders w/
+ // the same name.
+ for (let folder of items) {
+ folder.__defineGetter__("children", () => []);
+ folder.addServerName = true;
+ }
+
+ return items;
+ },
+
+ getParentOfFolder: function(aFolder) {
+ // This is a flat view, so no folders have parents.
+ return null;
+ }
+ }
+ },
+
+ /**
+ * This is a helper attribute that simply returns a flat list of all folders
+ */
+ get _enumerateFolders() {
+ let folders = [];
+
+ for (let server of fixIterator(MailServices.accounts.allServers, Ci.nsIMsgIncomingServer)) {
+ // Skip deferred accounts.
+ if (server instanceof Ci.nsIPop3IncomingServer &&
+ server.deferredToAccount)
+ continue;
+
+ let rootFolder = server.rootFolder;
+ folders.push(rootFolder);
+ this.addSubFolders(rootFolder, folders);
+ }
+ return folders;
+ },
+
+ /**
+ * This is a recursive function to add all subfolders to the array. It
+ * assumes that the passed in folder itself has already been added.
+ *
+ * @param aFolder the folder whose subfolders should be added
+ * @param folders the array to add the folders to.
+ */
+ addSubFolders : function ftv_addSubFolders (folder, folders) {
+ for (let f of fixIterator(folder.subFolders, Ci.nsIMsgFolder)) {
+ folders.push(f);
+ this.addSubFolders(f, folders);
+ }
+ },
+
+ /**
+ * This updates the rowmap and invalidates the right row(s) in the tree
+ */
+ _addChildToView: function ftl_addChildToView(aParent, aParentIndex, aNewChild) {
+ if (aParent.open) {
+ let newChildIndex;
+ let newChildNum = aParent._children.indexOf(aNewChild);
+ // Only child - go right after our parent.
+ if (newChildNum == 0) {
+ newChildIndex = Number(aParentIndex) + 1
+ }
+ // If we're not the last child, insert ourselves before the next child.
+ else if (newChildNum < aParent._children.length - 1) {
+ newChildIndex = this.getIndexOfFolder(aParent._children[Number(newChildNum) + 1]._folder);
+ }
+ // Otherwise, go after the last child.
+ else {
+ let lastChild = aParent._children[newChildNum - 1];
+ let lastChildIndex = this.getIndexOfFolder(lastChild._folder);
+ newChildIndex = Number(lastChildIndex) + 1;
+ while (newChildIndex < this.rowCount &&
+ this._rowMap[newChildIndex].level > this._rowMap[lastChildIndex].level)
+ newChildIndex++;
+ }
+ this._rowMap.splice(newChildIndex, 0, aNewChild);
+ this._tree.rowCountChanged(newChildIndex, 1);
+ } else {
+ this._tree.invalidateRow(aParentIndex);
+ }
+ },
+
+ /**
+ * This is our implementation of nsIMsgFolderListener to watch for changes
+ */
+ onFolderAdded: function ftl_add(aParentItem, aItem) {
+ // Ignore this item if it's not a folder, or we knew about it.
+ if (this.getIndexOfFolder(aItem) != null)
+ return;
+
+ // If no parent, this is an account, so let's rebuild.
+ if (!aParentItem) {
+ if (!aItem.server.hidden) // Ignore hidden server items.
+ this._rebuild();
+ return;
+ }
+ this._modes[this._mode].onFolderAdded(
+ aParentItem.QueryInterface(Ci.nsIMsgFolder), aItem);
+ },
+ onMessageAdded: function(parentFolder, msg) {},
+
+ addFolder: function ftl_add_folder(aParentItem, aItem) {
+ // This intentionally adds any new folder even if it would not pass the
+ // _filterFunction. The idea is that the user can add new folders even
+ // in modes like "unread" or "favorite" and could wonder why they
+ // are not appearing (forgetting they do not meet the criteria of the view).
+ // The folders will be hidden properly next time the view is rebuilt.
+ let parentIndex = this.getIndexOfFolder(aParentItem);
+ let parent = this._rowMap[parentIndex];
+ if (!parent)
+ return;
+
+ // Getting these children might have triggered our parent to build its
+ // array just now, in which case the added item will already exist.
+ let children = parent.children;
+ var newChild;
+ for (let child of children) {
+ if (child._folder == aItem) {
+ newChild = child;
+ break;
+ }
+ }
+ if (!newChild) {
+ newChild = new ftvItem(aItem);
+ parent.children.push(newChild);
+ newChild._level = parent._level + 1;
+ newChild._parent = parent;
+ sortFolderItems(parent._children);
+ }
+ // If the parent is open, add the new child into the folder pane.
+ // Otherwise, just invalidate the parent row. Note that this code doesn't
+ // get called for the smart folder case.
+ if (!parent.open) {
+ // Special case adding a special folder when the parent is collapsed.
+ // Expand the parent so the user can see the special child.
+ // Expanding the parent is sufficient to add the folder to the view,
+ // because either we knew about it, or we will have added a child item
+ // for it above.
+ if (newChild._folder.flags & Ci.nsMsgFolderFlags.SpecialUse) {
+ this._toggleRow(parentIndex, false);
+ return;
+ }
+ }
+ this._addChildToView(parent, parentIndex, newChild);
+ },
+
+ onFolderRemoved: function ftl_remove(aRDFParentItem, aItem) {
+ this._persistItemClosed(aItem.URI);
+
+ let index = this.getIndexOfFolder(aItem);
+ if (index == null)
+ return;
+ // Forget our parent's children; they'll get rebuilt.
+ if (aRDFParentItem && this._rowMap[index]._parent)
+ this._rowMap[index]._parent._children = null;
+ let kidCount = 1;
+ let walker = Number(index) + 1;
+ while (walker < this.rowCount &&
+ this._rowMap[walker].level > this._rowMap[index].level) {
+ walker++;
+ kidCount++;
+ }
+ this._rowMap.splice(index, kidCount);
+ this._tree.rowCountChanged(index, -1 * kidCount);
+ this._tree.invalidateRow(index);
+ },
+
+ onMessageRemoved: function(parentFolder, msg) {},
+
+ onFolderPropertyChanged: function(aItem, aProperty, aOld, aNew) {},
+ onFolderIntPropertyChanged: function(aItem, aProperty, aOld, aNew) {
+ // First try mode specific handling of the changed property.
+ if (this._modes[this.mode].handleChangedIntProperty(aItem, aProperty, aOld,
+ aNew))
+ return;
+
+ if (aItem instanceof Ci.nsIMsgFolder) {
+ let index = this.getIndexOfFolder(aItem);
+ let folder = aItem;
+ let folderTreeMode = this._modes[this._mode];
+ // Look for first visible ancestor.
+ while (index == null) {
+ folder = folderTreeMode.getParentOfFolder(folder);
+ if (!folder)
+ break;
+ index = this.getIndexOfFolder(folder);
+ }
+ if (index != null)
+ this._tree.invalidateRow(index);
+ }
+ },
+
+ onFolderBoolPropertyChanged: function(aItem, aProperty, aOld, aNew) {
+ let index = this.getIndexOfFolder(aItem);
+ if (index != null)
+ this._tree.invalidateRow(index);
+ },
+ onFolderUnicharPropertyChanged: function(aItem, aProperty, aOld, aNew) {},
+ onFolderPropertyFlagChanged: function(aItem, aProperty, aOld, aNew) {},
+ onFolderEvent: function(aFolder, aEvent) {
+ let index = this.getIndexOfFolder(aFolder);
+ if (index != null)
+ this._tree.invalidateRow(index);
+ }
+};
+
+/**
+ * The ftvItem object represents a single row in the tree view. Because I'm lazy
+ * I'm just going to define the expected interface here. You are free to return
+ * an alternative object, provided that it matches this interface:
+ *
+ * id (attribute) - a unique string for this object. Must persist over sessions
+ * text (attribute) - the text to display in the tree
+ * level (attribute) - the level in the tree to display the item at
+ * open (rw, attribute) - whether or not this container is open
+ * children (attribute) - an array of child items also conforming to this spec
+ * getProperties (function) - a call from getRowProperties or getCellProperties
+ * for this item will be passed into this function
+ * command (function) - this function will be called when the item is double-
+ * clicked
+ */
+
+/**
+ * The ftvItem constructor takes these arguments:
+ *
+ * @param aFolder The folder attached to this row in the tree.
+ * @param aFolderFilter When showing children folders of this one,
+ * only show those that pass this filter function.
+ * If unset, show all subfolders.
+ */
+function ftvItem(aFolder, aFolderFilter) {
+ this._folder = aFolder;
+ this._level = 0;
+ this._parent = null;
+ this._folderFilter = aFolderFilter;
+ // The map contains message counts for each folder column.
+ // Each key is a column name (ID) from the folder tree.
+ // Value is an array of the format
+ // "[value_for_folder, value_for_all_its_subfolders]".
+ this._summarizedCounts = new Map();
+}
+
+ftvItem.prototype = {
+ open: false,
+ addServerName: false,
+ useServerNameOnly: false,
+
+ get id() {
+ return this._folder.URI;
+ },
+ get text() {
+ return this.getText("folderNameCol");
+ },
+
+ getText(aColName) {
+ // Only show counts / total size of subtree if the pref is set,
+ // we are in "All folders" mode and this folder row is not expanded.
+ gFolderStatsHelpers.sumSubfolders =
+ gFolderStatsHelpers.sumSubfoldersPref &&
+ (gFolderTreeView.mode == kDefaultMode) &&
+ this._folder.hasSubFolders && !this.open;
+
+ this._summarizedCounts.delete(aColName);
+ switch (aColName) {
+ case "folderNameCol":
+ let text;
+ if (this.useServerNameOnly)
+ text = this._folder.server.prettyName;
+ else {
+ text = this._folder.abbreviatedName;
+ if (this.addServerName) {
+ text = gFolderTreeView.messengerBundle.getFormattedString(
+ "folderWithAccount", [text, this._folder.server.prettyName]);
+ }
+ }
+
+ // In a simple list tree we don't care for attributes other than folder
+ // name.
+ if (gFolderTreeView._treeElement.getAttribute("simplelist") == "true")
+ return text;
+
+ // If the unread column is shown, we don't need to add the count
+ // to the name.
+ if (!document.getElementById("folderUnreadCol").hidden)
+ return text;
+
+ let unread = this._folder.getNumUnread(false);
+ let totalUnread = gFolderStatsHelpers.sumSubfolders ?
+ this._folder.getNumUnread(true) : unread;
+ this._summarizedCounts.set(aColName, [unread, totalUnread - unread]);
+ if (totalUnread > 0) {
+ text = gFolderTreeView.messengerBundle.getFormattedString(
+ "folderWithUnreadMsgs",
+ [text,
+ gFolderStatsHelpers.addSummarizedPrefix(totalUnread,
+ unread != totalUnread)]);
+ }
+ return text;
+
+ case "folderUnreadCol":
+ let folderUnread = this._folder.getNumUnread(false);
+ let subfoldersUnread = gFolderStatsHelpers.sumSubfolders ?
+ this._folder.getNumUnread(true) : folderUnread;
+ this._summarizedCounts.set(aColName, [folderUnread,
+ subfoldersUnread - folderUnread]);
+ return gFolderStatsHelpers
+ .fixNum(subfoldersUnread, folderUnread != subfoldersUnread);
+
+ case "folderTotalCol":
+ let folderTotal = this._folder.getTotalMessages(false);
+ let subfoldersTotal = gFolderStatsHelpers.sumSubfolders ?
+ this._folder.getTotalMessages(true) : folderTotal;
+ this._summarizedCounts.set(aColName, [folderTotal,
+ subfoldersTotal - folderTotal]);
+ return gFolderStatsHelpers
+ .fixNum(subfoldersTotal, folderTotal != subfoldersTotal);
+
+ case "folderSizeCol":
+ let thisFolderSize = gFolderStatsHelpers.getFolderSize(this._folder);
+ let subfoldersSize = gFolderStatsHelpers.sumSubfolders ?
+ gFolderStatsHelpers.getSubfoldersSize(this._folder) : 0;
+
+ if (subfoldersSize == gFolderStatsHelpers.kUnknownSize ||
+ thisFolderSize == gFolderStatsHelpers.kUnknownSize)
+ return gFolderStatsHelpers.kUnknownSize;
+
+ let totalSize = thisFolderSize + subfoldersSize;
+ if (totalSize == 0)
+ return "";
+
+ let [totalText, folderUnit] = gFolderStatsHelpers.formatFolderSize(totalSize);
+ let folderText = (subfoldersSize == 0) ? totalText :
+ gFolderStatsHelpers.formatFolderSize(thisFolderSize, folderUnit)[0];
+ let subfoldersText = (subfoldersSize == 0) ? "" :
+ gFolderStatsHelpers.formatFolderSize(subfoldersSize, folderUnit)[0];
+ this._summarizedCounts.set(aColName, [folderText, subfoldersText]);
+ return gFolderStatsHelpers
+ .addSummarizedPrefix(totalText, totalSize != thisFolderSize);
+
+ default:
+ return "";
+ }
+ },
+
+ get level() {
+ return this._level;
+ },
+
+ getProperties: function (aColumn) {
+ if (aColumn && aColumn.id != "folderNameCol")
+ return "";
+
+ let properties = FolderUtils.getFolderProperties(this._folder, this.open);
+
+ return properties;
+ },
+
+ command: function fti_command() {
+ if (!Services.prefs.getBoolPref("mailnews.reuse_thread_window2")) {
+ MsgOpenNewWindowForFolder(this._folder.URI, -1 /* key */);
+ }
+ },
+
+ _children: null,
+ get children() {
+ // We're caching our child list to save perf.
+ if (!this._children) {
+ let iter;
+ try {
+ iter = fixIterator(this._folder.subFolders, Ci.nsIMsgFolder);
+ } catch (ex) {
+ Services.console.logStringMessage("Discovering children for " +
+ this._folder.URI + " failed with " +
+ "exception: " + ex);
+ iter = [];
+ }
+ this._children = [];
+ // Out of all children, only keep those that match the _folderFilter
+ // and those that contain such children.
+ for (let folder of iter) {
+ if (!this._folderFilter || this._folderFilter(folder)) {
+ this._children.push(new ftvItem(folder, this._folderFilter));
+ }
+ }
+ sortFolderItems(this._children);
+ // Each child is a level one below us.
+ for (let child of this._children) {
+ child._level = this._level + 1;
+ child._parent = this;
+ }
+ }
+ return this._children;
+ }
+};
+
+/**
+ * This handles the invocation of most commands dealing with folders, based off
+ * of the current selection, or a passed in folder.
+ */
+var gFolderTreeController = {
+ /**
+ * Opens the dialog to create a new sub-folder, and creates it if the user
+ * accepts
+ *
+ * @param aParent (optional) the parent for the new subfolder
+ */
+ newFolder(aParent) {
+ let folder = aParent || GetSelectedMsgFolders()[0];
+
+ // Make sure we actually can create subfolders.
+ if (!folder.canCreateSubfolders) {
+ // Check if we can create them at the root.
+ let rootMsgFolder = folder.server.rootMsgFolder;
+ if (rootMsgFolder.canCreateSubfolders)
+ folder = rootMsgFolder;
+ else // just use the default account
+ folder = GetDefaultAccountRootFolder();
+ }
+
+ let dualUseFolders = true;
+ if (folder.server instanceof Ci.nsIImapIncomingServer)
+ dualUseFolders = folder.server.dualUseFolders;
+
+ function newFolderCallback(aName, aFolder) {
+ // createSubfolder can throw an exception, causing the newFolder dialog
+ // to not close and wait for another input.
+ // TODO: Rewrite this logic and move the opening of alert dialogs from
+ // nsMsgLocalMailFolder::CreateSubfolderInternal to here (bug 831190#c16).
+ if (aName)
+ aFolder.createSubfolder(aName, msgWindow);
+ }
+
+ window.openDialog("chrome://messenger/content/newFolderDialog.xul",
+ "",
+ "chrome,modal,centerscreen",
+ {folder: folder,
+ dualUseFolders: dualUseFolders,
+ okCallback: newFolderCallback});
+ },
+
+ /**
+ * Opens the dialog to edit the properties for a folder
+ *
+ * @param aTabID (optional) the tab to show in the dialog
+ * @param aFolder (optional) the folder to edit, if not the selected one
+ */
+ editFolder(aTabID, aFolder) {
+ let folder = aFolder || GetSelectedMsgFolders()[0];
+
+ // If a server is selected, view settings for that account.
+ if (folder.isServer) {
+ MsgAccountManager(null, folder.server);
+ return;
+ }
+
+ if (folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
+ // virtual folders get their own property dialog that contains all of the
+ // search information related to the virtual folder.
+ this.editVirtualFolder(folder);
+ return;
+ }
+
+ let title = gFolderTreeView.messengerBundle.getString("folderProperties");
+
+ function editFolderCallback(aNewName, aOldName, aUri) {
+ if (aNewName != aOldName)
+ folder.rename(aNewName, msgWindow);
+ }
+
+ function rebuildSummary(msgFolder) {
+ if (msgFolder.locked) {
+ msgFolder.throwAlertMsg("operationFailedFolderBusy", msgWindow);
+ return;
+ }
+ if (msgFolder.supportsOffline) {
+ // Remove the offline store, if any.
+ let offlineStore = msgFolder.filePath;
+ // XXX todo: figure out how to delete a maildir directory async. This
+ // delete causes main thread lockup for large maildir folders.
+ if (offlineStore.exists())
+ offlineStore.remove(true);
+ }
+
+ // Send a notification that we are triggering a database rebuild.
+ MailServices.mfn.notifyItemEvent(folder, "FolderReindexTriggered", null,
+ null);
+
+ msgFolder.msgDatabase.summaryValid = false;
+
+ try {
+ msgFolder.closeAndBackupFolderDB("");
+ }
+ catch(e) {
+ // In a failure, proceed anyway since we're dealing with problems
+ msgFolder.ForceDBClosed();
+ }
+ // these two lines will cause the thread pane to get reloaded
+ // when the download/reparse is finished. Only do this
+ // if the selected folder is loaded (i.e., not thru the
+ // context menu on a non-loaded folder).
+ if (msgFolder == GetLoadedMsgFolder()) {
+ gRerootOnFolderLoad = true;
+ gCurrentFolderToReroot = msgFolder.URI;
+ }
+ msgFolder.updateFolder(msgWindow);
+ }
+
+ window.openDialog("chrome://messenger/content/folderProps.xul",
+ "", "chrome,modal,centerscreen",
+ {folder: folder, serverType: folder.server.type,
+ msgWindow: msgWindow, title: title,
+ okCallback: editFolderCallback, tabID: aTabID,
+ name: folder.prettyName,
+ rebuildSummaryCallback: rebuildSummary});
+ },
+
+ /**
+ * Opens the dialog to rename a particular folder, and does the renaming if
+ * the user clicks OK in that dialog
+ *
+ * @param aFolder (optional) the folder to rename, if different than the
+ * currently selected one
+ */
+ renameFolder(aFolder) {
+ let folder = aFolder || GetSelectedMsgFolders()[0];
+
+ let controller = this;
+ function renameCallback(aName, aUri) {
+ if (aUri != folder.URI)
+ Cu.reportError("got back a different folder to rename!");
+
+ controller._resetThreadPane();
+ let folderTree = document.getElementById("folderTree");
+ folderTree.view.selection.clearSelection();
+
+ folder.rename(aName, msgWindow);
+ }
+
+ window.openDialog("chrome://messenger/content/renameFolderDialog.xul",
+ "", "chrome,modal,centerscreen",
+ {preselectedURI: folder.URI,
+ okCallback: renameCallback, name: folder.prettyName});
+ },
+
+ /**
+ * Deletes a folder from its parent. Also handles unsubscribe from newsgroups
+ * if the selected folder/s happen to be nntp.
+ *
+ * @param aFolder (optional) the folder to delete, if not the selected one
+ */
+ deleteFolder(aFolder) {
+ let folders = aFolder ? [aFolder] : GetSelectedMsgFolders();
+ let prompt = Services.prompt;
+ for (let folder of folders) {
+ // For newsgroups, "delete" means "unsubscribe".
+ if (folder.server.type == "nntp" &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
+ MsgUnsubscribe([folder]);
+ continue;
+ }
+
+ let canDelete = folder.isSpecialFolder(Ci.nsMsgFolderFlags.Junk, false) ?
+ CanRenameDeleteJunkMail(folder.URI) : folder.deletable;
+ if (!canDelete)
+ continue;
+
+ if (folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
+ let confirmation = gMessengerBundle.getString("confirmSavedSearchDeleteMessage");
+ let title = gMessengerBundle.getString("confirmSavedSearchDeleteTitle");
+ let buttonTitle = gMessengerBundle.getString("confirmSavedSearchDeleteButton");
+ let buttonFlags = prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0 +
+ prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1;
+ if (prompt.confirmEx(window, title, confirmation, buttonFlags, buttonTitle,
+ "", "", "", {}) != 0) /* the yes button is in position 0 */
+ continue;
+ if (gCurrentVirtualFolderUri == folder.URI)
+ gCurrentVirtualFolderUri = null;
+ }
+
+ // We can delete this folder.
+ try {
+ folder.deleteSelf(msgWindow);
+ }
+ // Ignore known errors from canceled warning dialogs.
+ catch (ex) {
+ const NS_MSG_ERROR_COPY_FOLDER_ABORTED = 0x8055001a;
+ if (ex.result != NS_MSG_ERROR_COPY_FOLDER_ABORTED) {
+ throw ex;
+ }
+ }
+ }
+ },
+
+ /**
+ * Prompts the user to confirm and empties the trash for the selected folder.
+ * The folder and its children are only emptied if it has the proper Trash
+ * flag.
+ *
+ * @param aFolder (optional) The trash folder to empty. If unspecified or not
+ * a trash folder, the currently selected server's
+ * trash folder is used.
+ */
+ emptyTrash(aFolder) {
+ let folder = aFolder || GetSelectedMsgFolders()[0];
+ if (!folder.getFlag(Ci.nsMsgFolderFlags.Trash))
+ folder = folder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Trash);
+ if (!folder)
+ return;
+
+ if (this._checkConfirmationPrompt("emptyTrash"))
+ folder.emptyTrash(null);
+ },
+
+ /**
+ * Deletes everything (folders and messages) in the selected folder.
+ * The folder is only emptied if it has the proper Junk flag.
+ *
+ * @param aFolder (optional) The folder to empty. If unspecified, the
+ * currently selected folder is used, if it
+ * is junk.
+ */
+ emptyJunk(aFolder) {
+ let folder = aFolder || GetSelectedMsgFolders()[0];
+
+ if (!folder || !folder.getFlag(Ci.nsMsgFolderFlags.Junk))
+ return;
+
+ if (!this._checkConfirmationPrompt("emptyJunk"))
+ return;
+
+ // Delete any sub-folders this folder might have.
+ for (let f of folder.subFolders) {
+ folder.propagateDelete(f, true);
+ }
+
+ // Now delete the messages.
+ folder.deleteMessages([...folder.messages], msgWindow, true, false, null, false);
+ },
+
+ /**
+ * Compacts either particular folder/s, or selected folders.
+ *
+ * @param aFolders (optional) the folders to compact, if different than the
+ * currently selected ones
+ */
+ compactFolders(aFolders) {
+ let folders = aFolders || GetSelectedMsgFolders();
+ for (let folder of folders) {
+ let isImapFolder = folder.server.type == "imap";
+ // Can't compact folders that have just been compacted
+ if (!isImapFolder && !folder.expungedBytes)
+ return;
+
+ // Reset thread pane for non-imap folders.
+ if (!isImapFolder && gDBView && gDBView.msgFolder == folder) {
+ this._resetThreadPane();
+ }
+
+ folder.compact(null, msgWindow);
+ }
+ },
+ /**
+ * Compacts all folders for accounts that the given folders belong
+ * to, or all folders for accounts of the currently selected folders.
+ *
+ * @param aFolders (optional) the folders for whose accounts we should compact
+ * all folders, if different than the currently
+ * selected ones
+ */
+ compactAllFoldersForAccount(aFolders) {
+ let folders = aFolders || GetSelectedMsgFolders();
+ for (let folder of folders) {
+ folder.compactAll(null, msgWindow);
+ // Reset thread pane for non-imap folders.
+ if (gDBView && folder.server.type != "imap")
+ this._resetThreadPane();
+ }
+ },
+
+ /**
+ * Opens the dialog to create a new virtual folder
+ *
+ * @param aName - the default name for the new folder
+ * @param aSearchTerms - the search terms associated with the folder
+ * @param aParent - the folder to run the search terms on
+ */
+ newVirtualFolder(aName, aSearchTerms, aParent) {
+ let folder = aParent || GetSelectedMsgFolders()[0];
+ if (!folder)
+ folder = GetDefaultAccountRootFolder();
+
+ let name = folder.prettyName;
+ if (aName)
+ name += "-" + aName;
+
+ window.openDialog("chrome://messenger/content/virtualFolderProperties.xul",
+ "", "chrome,modal,centerscreen",
+ {folder: folder, searchTerms: aSearchTerms,
+ newFolderName: name});
+ },
+
+ /**
+ * Opens the dialog to edit the properties for a virtual folder
+ *
+ * @param aFolder (optional) the folder to edit, if not the selected one
+ */
+ editVirtualFolder(aFolder) {
+ let folder = aFolder || GetSelectedMsgFolders()[0];
+
+ function editVirtualCallback(aURI) {
+ // we need to reload the folder if it is the currently loaded folder...
+ if (gMsgFolderSelected && aURI == gMsgFolderSelected.URI) {
+ // force the folder pane to reload the virtual folder
+ gMsgFolderSelected = null;
+ FolderPaneSelectionChange();
+ }
+ }
+ window.openDialog("chrome://messenger/content/virtualFolderProperties.xul",
+ "", "chrome,modal,centerscreen",
+ {folder: folder, editExistingFolder: true,
+ onOKCallback: editVirtualCallback,
+ msgWindow:msgWindow});
+ },
+
+ /**
+ * Opens a search window with the given folder, or the selected one if none
+ * is given.
+ *
+ * @param [aFolder] the folder to open the search window for, if different
+ * from the selected one
+ */
+ searchMessages(aFolder) {
+ MsgSearchMessages(aFolder || GetSelectedMsgFolders()[0]);
+ },
+
+ /**
+ * For certain folder commands, the thread pane needs to be invalidated, this
+ * takes care of doing so.
+ */
+ _resetThreadPane() {
+ if (gDBView)
+ gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
+
+ ClearThreadPaneSelection();
+ ClearThreadPane();
+ ClearMessagePane();
+ },
+
+ /**
+ * Prompts for confirmation, if the user hasn't already chosen the "don't ask
+ * again" option.
+ *
+ * @param aCommand - the command to prompt for
+ */
+ _checkConfirmationPrompt(aCommand) {
+ const kDontAskAgainPref = "mailnews." + aCommand + ".dontAskAgain";
+ // default to ask user if the pref is not set
+ if (!Services.prefs.getBoolPref(kDontAskAgainPref, false)) {
+ let checkbox = {value: false};
+ let choice = Services.prompt.confirmEx(
+ window,
+ gMessengerBundle.getString(aCommand + "Title"),
+ gMessengerBundle.getString(aCommand + "Message"),
+ Services.prompt.STD_YES_NO_BUTTONS,
+ null, null, null,
+ gMessengerBundle.getString(aCommand + "DontAsk"),
+ checkbox);
+ if (checkbox.value)
+ Services.prefs.setBoolPref(kDontAskAgainPref, true);
+
+ if (choice != 0)
+ return false;
+ }
+ return true;
+ },
+}
+
+/**
+ * Sorts the passed in array of folder items using the folder sort key
+ *
+ * @param aFolders - the array of ftvItems to sort.
+ */
+function sortFolderItems (aFtvItems) {
+ function sorter(a, b) {
+ return a._folder.compareSortKeys(b._folder);
+ }
+ aFtvItems.sort(sorter);
+}
+
+var gFolderStatsHelpers = {
+ kUnknownSize: "-",
+ sumSubfoldersPref: false,
+ sumSubfolders: false,
+ sizeUnits: "",
+ kiloUnit: "KB",
+ megaUnit: "MB",
+
+ init: function() {
+ // We cache these values because the cells in the folder pane columns
+ // using these helpers can be redrawn often.
+ this.sumSubfoldersPref = Services.prefs.getBoolPref("mail.folderpane.sumSubfolders");
+ this.sizeUnits = Services.prefs.getCharPref("mail.folderpane.sizeUnits");
+ this.kiloUnit = gFolderTreeView.messengerBundle.getString("kiloByteAbbreviation2");
+ this.megaUnit = gFolderTreeView.messengerBundle.getString("megaByteAbbreviation2");
+ },
+
+ /**
+ * Add a prefix to denote the value is actually a sum of all the subfolders.
+ * The prefix is useful as this sum may not always be the exact sum of
+ * individual folders when they are shown expanded (due to rounding to a
+ * unit).
+ * E.g. folder1 600bytes -> 1KB, folder2 700bytes -> 1KB
+ * summarized at parent folder: 1300bytes -> 1KB
+ *
+ * @param aValue The value to be displayed.
+ * @param aSubfoldersContributed Boolean indicating whether subfolders
+ * contributed to the accumulated total value.
+ */
+ addSummarizedPrefix: function(aValue, aSubfoldersContributed) {
+ if (!this.sumSubfolders)
+ return aValue;
+
+ if (!aSubfoldersContributed)
+ return aValue;
+
+ return gFolderTreeView.messengerBundle.getFormattedString("folderSummarizedSymbolValue", [aValue]);
+ },
+
+ /**
+ * nsIMsgFolder uses -1 as a magic number to mean "I don't know". In those
+ * cases we indicate it to the user. The user has to open the folder
+ * so that the property is initialized from the DB.
+ *
+ * @param aNumber The number to translate for the user.
+ * @param aSubfoldersContributed Boolean indicating whether subfolders
+ * contributed to the accumulated total value.
+ */
+ fixNum: function(aNumber, aSubfoldersContributed) {
+ if (aNumber < 0)
+ return this.kUnknownSize;
+
+ return (aNumber == 0 ? ""
+ : this.addSummarizedPrefix(aNumber,
+ aSubfoldersContributed));
+ },
+
+ /**
+ * Get the size of the specified folder.
+ *
+ * @param aFolder The nsIMsgFolder to analyze.
+ */
+ getFolderSize: function(aFolder) {
+ let folderSize = 0;
+ try {
+ folderSize = aFolder.sizeOnDisk;
+ if (folderSize < 0)
+ return this.kUnknownSize;
+ } catch(ex) {
+ return this.kUnknownSize;
+ }
+ return folderSize;
+ },
+
+ /**
+ * Get the total size of all subfolders of the specified folder.
+ *
+ * @param aFolder The nsIMsgFolder to analyze.
+ */
+ getSubfoldersSize: function(aFolder) {
+ let folderSize = 0;
+ if (aFolder.hasSubFolders) {
+ let subFolders = aFolder.subFolders;
+ while (subFolders.hasMoreElements()) {
+ let subFolder = subFolders.getNext().QueryInterface(Ci.nsIMsgFolder);
+ let subSize = this.getFolderSize(subFolder);
+ let subSubSize = this.getSubfoldersSize(subFolder);
+ if (subSize == this.kUnknownSize || subSubSize == this.kUnknownSize)
+ return subSize;
+
+ folderSize += subSize + subSubSize;
+ }
+ }
+ return folderSize;
+ },
+
+ /**
+ * Format the given folder size into a string with an appropriate unit.
+ *
+ * @param aSize The size in bytes to format.
+ * @param aUnit Optional unit to use for the format.
+ * Possible values are "KB" or "MB".
+ * @return An array with 2 values.
+ * First is the resulting formatted strings.
+ * The second one is the final unit used to format the string.
+ */
+ formatFolderSize: function(aSize, aUnit = gFolderStatsHelpers.sizeUnits) {
+ let size = Math.round(aSize / 1024);
+ let unit = gFolderStatsHelpers.kiloUnit;
+ // If size is non-zero try to show it in a unit that fits in 3 digits,
+ // but if user specified a fixed unit, use that.
+ if (aUnit != "KB" && (size > 999 || aUnit == "MB")) {
+ size = Math.round(size / 1024);
+ unit = gFolderStatsHelpers.megaUnit;
+ aUnit = "MB";
+ }
+ // This needs to be updated if the "%.*f" placeholder string
+ // in "*ByteAbbreviation2" in messenger.properties changes.
+ return [unit.replace("%.*f", size).replace(" ",""), aUnit];
+ }
+};
diff --git a/comm/suite/mailnews/content/folderPane.xul b/comm/suite/mailnews/content/folderPane.xul
new file mode 100644
index 0000000000..c56c799071
--- /dev/null
+++ b/comm/suite/mailnews/content/folderPane.xul
@@ -0,0 +1,169 @@
+<?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://messenger/skin/folderPane.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/folderPaneExtras.css" type="text/css"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % folderpaneDTD SYSTEM "chrome://messenger/locale/folderpane.dtd">
+ %folderpaneDTD;
+ <!ENTITY % msgViewPickerDTD SYSTEM "chrome://messenger/locale/msgViewPickerOverlay.dtd">
+ %msgViewPickerDTD;
+]>
+
+<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <tree id="folderTree"
+ class="plain focusring window-focusborder"
+ flex="1"
+ treelines="true"
+ persist="mode"
+ mode="all"
+ keepcurrentinview="true"
+ context="folderPaneContext"
+ focusring="false"
+ disableKeyNavigation="true"
+ ondragstart="gFolderTreeView._onDragStart(event);"
+ ondragover="gFolderTreeView._onDragOver(event);"
+ ondrop="gFolderTreeView._onDragDrop(event);"
+ ondblclick="gFolderTreeView.onDoubleClick(event);"
+ onkeypress="gFolderTreeView.onKeyPress(event);"
+ onselect="FolderPaneSelectionChange();">
+ <treecols id="folderPaneCols">
+ <treecol id="folderNameCol"
+ flex="5"
+ label="&nameColumn.label;"
+ crop="center"
+ persist="width"
+ ignoreincolumnpicker="true"
+ primary="true"
+ sortActive="true"
+ sortDirection="ascending"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="folderUnreadCol"
+ hidden="true"
+ persist="hidden width"
+ flex="1"
+ label="&unreadColumn.label;"
+ selectable="false"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="folderTotalCol"
+ hidden="true"
+ persist="hidden width"
+ flex="1"
+ label="&totalColumn.label;"
+ selectable="false"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="folderSizeCol"
+ hidden="true"
+ persist="hidden width"
+ flex="1"
+ label="&folderSizeColumn.label;"
+ selectable="false"/>
+ </treecols>
+ </tree>
+
+ <toolbarpalette id="MailToolbarPalette">
+ <toolbaritem id="folder-location-container"
+ title="&folderLocationToolbarItem.title;"
+ align="center"
+ context="folderPaneContext"
+ class="toolbaritem-noline chromeclass-toolbar-additional">
+ <image id="locationIcon" class="folderMenuItem"/>
+ <menulist id="locationFolders"
+ class="folderMenuItem"
+ label=" "
+ crop="center">
+ <menupopup id="folderLocationPopup"
+ class="menulist-menupopup"
+ type="folder"
+ flex="1"
+ mode="notDeferred"
+ showFileHereLabel="true"
+ oncommand="gFolderTreeView.selectFolder(event.target._folder, true);"/>
+ </menulist>
+ </toolbaritem>
+ <toolbaritem id="mailviews-container"
+ title="&mailViewsToolbarItem.title;"
+ observes="mailDisableViewsSearch"
+ align="center"
+ class="toolbaritem-noline chromeclass-toolbar-additional">
+ <label id="viewPickerLabel"
+ value="&viewPicker.label;"
+ accesskey="&viewPicker.accesskey;"
+ control="viewPicker">
+ <observes element="mailviews-container" attribute="disabled"/>
+ </label>
+ <menulist id="viewPicker"
+ oncommand="ViewChangeByMenuitem(event.target);">
+ <menupopup id="viewPickerPopup"
+ onpopupshowing="RefreshViewPopup(this);">
+ <menuitem id="viewPickerAll"
+ class="menuitem-iconic"
+ label="&viewAll.label;"
+ type="radio"
+ name="viewmessages"
+ value="0"/>
+ <menuitem id="viewPickerUnread"
+ class="menuitem-iconic"
+ label="&viewUnread.label;"
+ type="radio"
+ name="viewmessages"
+ value="1"/>
+ <menuitem id="viewPickerNotDeleted"
+ class="menuitem-iconic"
+ label="&viewNotDeleted.label;"
+ type="radio"
+ name="viewmessages"
+ value="3"/>
+ <menuseparator id="afterViewPickerUnreadSeparator"/>
+ <menu id="viewPickerTags"
+ class="menu-iconic"
+ label="&viewTags.label;">
+ <menupopup id="viewPickerTagsPopup"
+ class="menulist-menupopup"
+ onpopupshowing="RefreshTagsPopup(this);"/>
+ </menu>
+ <menu id="viewPickerCustomViews"
+ class="menu-iconic"
+ label="&viewCustomViews.label;">
+ <menupopup id="viewPickerCustomViewsPopup"
+ class="menulist-menupopup"
+ onpopupshowing="RefreshCustomViewsPopup(this);"/>
+ </menu>
+ <menuseparator id="afterViewPickerCustomViewsSeparator"/>
+ <menuitem id="viewPickerVirtualFolder"
+ class="menuitem-iconic"
+ label="&viewVirtualFolder.label;"
+ value="7"/>
+ <menuitem id="viewPickerCustomize"
+ class="menuitem-iconic"
+ label="&viewCustomizeView.label;"
+ value="8"/>
+ </menupopup>
+ <observes element="mailviews-container" attribute="disabled"/>
+ </menulist>
+ </toolbaritem>
+ <toolbaritem id="search-container"
+ title="&searchToolbarItem.title;"
+ observes="mailDisableViewsSearch"
+ align="center"
+ flex="1"
+ class="toolbaritem-noline chromeclass-toolbar-additional">
+ <textbox id="searchInput"
+ flex="1"
+ type="search"
+ aria-controls="threadTree"
+ placeholder="&searchSubjectOrAddress.placeholder;"
+ clickSelectsAll="true"
+ onkeypress="if (event.keyCode == KeyEvent.DOM_VK_RETURN) this.select();"
+ oncommand="onEnterInSearchBar();">
+ <observes element="search-container" attribute="disabled"/>
+ </textbox>
+ </toolbaritem>
+ </toolbarpalette>
+</overlay>
diff --git a/comm/suite/mailnews/content/mail-offline.js b/comm/suite/mailnews/content/mail-offline.js
new file mode 100644
index 0000000000..506c198c9a
--- /dev/null
+++ b/comm/suite/mailnews/content/mail-offline.js
@@ -0,0 +1,164 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gOfflinePromptsBundle;
+var gOfflineManager;
+
+function MailOfflineStateChanged(goingOffline)
+{
+ // tweak any mail UI here that needs to change when we go offline or come back online
+ gFolderJustSwitched = true;
+}
+
+function MsgSettingsOffline()
+{
+ window.parent.MsgAccountManager('am-offline.xul');
+}
+
+// Check for unsent messages
+function CheckForUnsentMessages()
+{
+ return Cc["@mozilla.org/messengercompose/sendlater;1"]
+ .getService(Ci.nsIMsgSendLater)
+ .hasUnsentMessages();
+}
+
+// Init strings.
+function InitPrompts()
+{
+ if (!gOfflinePromptsBundle)
+ gOfflinePromptsBundle = document.getElementById("bundle_offlinePrompts");
+}
+
+// prompt for sending messages while going online, and go online.
+function PromptSendMessages()
+{
+ InitPrompts();
+ InitServices();
+
+ var checkValue = {value:true};
+ var buttonPressed = Services.prompt.confirmEx(
+ window,
+ gOfflinePromptsBundle.getString('sendMessagesWindowTitle'),
+ gOfflinePromptsBundle.getString('sendMessagesLabel2'),
+ Services.prompt.BUTTON_TITLE_IS_STRING * (Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_POS_1 + Services.prompt.BUTTON_POS_2),
+ gOfflinePromptsBundle.getString('sendMessagesSendButtonLabel'),
+ gOfflinePromptsBundle.getString('sendMessagesCancelButtonLabel'),
+ gOfflinePromptsBundle.getString('sendMessagesNoSendButtonLabel'),
+ gOfflinePromptsBundle.getString('sendMessagesCheckboxLabel'),
+ checkValue);
+ switch (buttonPressed) {
+ case 0:
+ Services.prefs.setIntPref("offline.send.unsent_messages", !checkValue.value);
+ gOfflineManager.goOnline(true, true, msgWindow);
+ return true;
+
+ case 2:
+ Services.prefs.setIntPref("offline.send.unsent_messages", 2*!checkValue.value);
+ gOfflineManager.goOnline(false, true, msgWindow);
+ return true;
+ }
+ return false;
+}
+
+// prompt for downlading messages while going offline, and synchronise
+function PromptDownloadMessages()
+{
+ InitPrompts();
+ InitServices();
+
+ var checkValue = {value:true};
+ var buttonPressed = Services.prompt.confirmEx(
+ window,
+ gOfflinePromptsBundle.getString('downloadMessagesWindowTitle'),
+ gOfflinePromptsBundle.getString('downloadMessagesLabel'),
+ Services.prompt.BUTTON_TITLE_IS_STRING * (Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_POS_1 + Services.prompt.BUTTON_POS_2),
+ gOfflinePromptsBundle.getString('downloadMessagesDownloadButtonLabel'),
+ gOfflinePromptsBundle.getString('downloadMessagesCancelButtonLabel'),
+ gOfflinePromptsBundle.getString('downloadMessagesNoDownloadButtonLabel'),
+ gOfflinePromptsBundle.getString('downloadMessagesCheckboxLabel'),
+ checkValue);
+ switch (buttonPressed) {
+ case 0:
+ Services.prefs.setIntPref("offline.download.download_messages", !checkValue.value);
+ gOfflineManager.synchronizeForOffline(true, true, false, true, msgWindow);
+ return true;
+
+ case 2:
+ Services.prefs.setIntPref("offline.download.download_messages", 2*!checkValue.value);
+ gOfflineManager.synchronizeForOffline(false, false, false, true, msgWindow);
+ return true;
+ }
+ return false;
+}
+
+// Init Pref Service & Offline Manager
+function InitServices()
+{
+ if (!gOfflineManager)
+ GetOfflineMgrService();
+}
+
+// Init Offline Manager
+function GetOfflineMgrService()
+{
+ if (!gOfflineManager) {
+ gOfflineManager = Cc["@mozilla.org/messenger/offline-manager;1"]
+ .getService(Ci.nsIMsgOfflineManager);
+ }
+}
+
+// This function must always return false to prevent toggling of offline state because
+// we change the offline state ourselves
+function MailCheckBeforeOfflineChange()
+{
+ InitServices();
+
+
+ if (Services.io.offline) {
+ switch(Services.prefs.getIntPref("offline.send.unsent_messages")) {
+ case 0:
+ if(CheckForUnsentMessages()) {
+ if(! PromptSendMessages())
+ return false;
+ }
+ else
+ gOfflineManager.goOnline(false /* sendUnsentMessages */,
+ true /* playbackOfflineImapOperations */,
+ msgWindow);
+ break;
+ case 1:
+ gOfflineManager.goOnline(CheckForUnsentMessages() /* sendUnsentMessages */,
+ true /* playbackOfflineImapOperations */,
+ msgWindow);
+ break;
+ case 2:
+ gOfflineManager.goOnline(false /* sendUnsentMessages */,
+ true /* playbackOfflineImapOperations */,
+ msgWindow);
+ break;
+ }
+ }
+ else {
+ // going offline
+ switch(Services.prefs.getIntPref("offline.download.download_messages")) {
+ case 0:
+ if(! PromptDownloadMessages()) return false;
+ break;
+ case 1:
+ // download news, download mail, send unsent messages, go offline when done, msg window
+ gOfflineManager.synchronizeForOffline(true, true, false, true, msgWindow);
+ break;
+ case 2:
+ // download news, download mail, send unsent messages, go offline when done, msg window
+ gOfflineManager.synchronizeForOffline(false, false, false, true, msgWindow);
+ break;
+ }
+ }
+ return false;
+}
+
diff --git a/comm/suite/mailnews/content/mail3PaneWindowCommands.js b/comm/suite/mailnews/content/mail3PaneWindowCommands.js
new file mode 100644
index 0000000000..6bd9f762d4
--- /dev/null
+++ b/comm/suite/mailnews/content/mail3PaneWindowCommands.js
@@ -0,0 +1,1057 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Functionality for the main application window (aka the 3pane) usually
+ * consisting of folder pane, thread pane and message pane.
+ */
+
+const { MailServices } =
+ ChromeUtils.import("resource:///modules/MailServices.jsm");
+
+// Controller object for folder pane
+var FolderPaneController =
+{
+ supportsCommand: function(command)
+ {
+ switch ( command )
+ {
+ case "cmd_delete":
+ case "cmd_shiftDelete":
+ case "button_delete":
+ case "button_shiftDelete":
+ // Even if the folder pane has focus, don't do a folder delete if
+ // we have a selected message, but do a message delete instead.
+ // Return false here supportsCommand and let the command fall back
+ // to the DefaultController.
+ if (Services.prefs.getBoolPref("mailnews.ui.deleteAlwaysSelectedMessages") && (gFolderDisplay.selectedCount != 0))
+ return false;
+ // else fall through
+ //case "cmd_selectAll": the folder pane currently only handles single selection
+ case "cmd_cut":
+ case "cmd_copy":
+ case "cmd_paste":
+ return true;
+
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ switch ( command )
+ {
+ case "cmd_cut":
+ case "cmd_copy":
+ case "cmd_paste":
+ return false;
+ case "cmd_delete":
+ case "cmd_shiftDelete":
+ case "button_delete":
+ case "button_shiftDelete":
+ {
+ // Make sure the button doesn't show "Undelete" for folders.
+ UpdateDeleteToolbarButton(true);
+ let folders = GetSelectedMsgFolders();
+ if (folders.length) {
+ let folder = folders[0];
+ // XXX Figure out some better way/place to update the folder labels.
+ UpdateDeleteLabelsFromFolderCommand(folder, command);
+ return CanDeleteFolder(folder) && folder.isCommandEnabled(command);
+ }
+ return false;
+ }
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(command)
+ {
+ // if the user invoked a key short cut then it is possible that we got here for a command which is
+ // really disabled. kick out if the command should be disabled.
+ if (!this.isCommandEnabled(command)) return;
+
+ switch ( command )
+ {
+ case "cmd_delete":
+ case "cmd_shiftDelete":
+ case "button_delete":
+ case "button_shiftDelete":
+ gFolderTreeController.deleteFolder();
+ break;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ }
+};
+
+function UpdateDeleteLabelsFromFolderCommand(folder, command) {
+ if (command != "cmd_delete")
+ return;
+
+ if (folder.server.type == "nntp" &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
+ goSetMenuValue(command, "valueNewsgroup");
+ goSetAccessKey(command, "valueNewsgroupAccessKey");
+ }
+ else {
+ goSetMenuValue(command, "valueFolder");
+ }
+}
+
+// DefaultController object (handles commands when one of the trees does not have focus)
+var DefaultController =
+{
+ supportsCommand: function(command)
+ {
+
+ switch ( command )
+ {
+ case "cmd_createFilterFromPopup":
+ case "cmd_archive":
+ case "cmd_reply":
+ case "button_reply":
+ case "cmd_replyList":
+ case "cmd_replyGroup":
+ case "cmd_replySender":
+ case "cmd_replyall":
+ case "button_replyall":
+ case "cmd_replySenderAndGroup":
+ case "cmd_replyAllRecipients":
+ case "cmd_forward":
+ case "button_forward":
+ case "cmd_forwardInline":
+ case "cmd_forwardAttachment":
+ case "cmd_editAsNew":
+ case "cmd_editDraftMsg":
+ case "cmd_newMsgFromTemplate":
+ case "cmd_editTemplateMsg":
+ case "cmd_createFilterFromMenu":
+ case "cmd_delete":
+ case "cmd_shiftDelete":
+ case "button_delete":
+ case "button_shiftDelete":
+ case "button_junk":
+ case "cmd_nextMsg":
+ case "button_next":
+ case "cmd_nextUnreadMsg":
+ case "cmd_nextFlaggedMsg":
+ case "cmd_nextUnreadThread":
+ case "cmd_previousMsg":
+ case "cmd_previousUnreadMsg":
+ case "cmd_previousFlaggedMsg":
+ case "button_goBack":
+ case "cmd_goBack":
+ case "button_goForward":
+ case "cmd_goForward":
+ case "cmd_goStartPage":
+ case "cmd_viewAllMsgs":
+ case "cmd_viewUnreadMsgs":
+ case "cmd_viewThreadsWithUnread":
+ case "cmd_viewWatchedThreadsWithUnread":
+ case "cmd_viewIgnoredThreads":
+ case "cmd_stop":
+ case "cmd_undo":
+ case "cmd_redo":
+ case "cmd_expandAllThreads":
+ case "cmd_collapseAllThreads":
+ case "cmd_renameFolder":
+ case "cmd_sendUnsentMsgs":
+ case "cmd_subscribe":
+ case "cmd_openMessage":
+ case "button_print":
+ case "cmd_print":
+ case "cmd_printpreview":
+ case "cmd_printSetup":
+ case "cmd_saveAsFile":
+ case "cmd_saveAsTemplate":
+ case "cmd_properties":
+ case "cmd_viewPageSource":
+ case "cmd_setFolderCharset":
+ case "cmd_reload":
+ case "button_getNewMessages":
+ case "cmd_getNewMessages":
+ case "cmd_getMsgsForAuthAccounts":
+ case "cmd_getNextNMessages":
+ case "cmd_find":
+ case "cmd_findNext":
+ case "cmd_findPrev":
+ case "button_search":
+ case "cmd_search":
+ case "button_mark":
+ case "cmd_markAsRead":
+ case "cmd_markAsUnread":
+ case "cmd_markAllRead":
+ case "cmd_markThreadAsRead":
+ case "cmd_markReadByDate":
+ case "cmd_markAsFlagged":
+ case "cmd_markAsJunk":
+ case "cmd_markAsNotJunk":
+ case "cmd_recalculateJunkScore":
+ case "cmd_markAsShowRemote":
+ case "cmd_markAsNotPhish":
+ case "cmd_displayMsgFilters":
+ case "cmd_applyFiltersToSelection":
+ case "cmd_applyFilters":
+ case "cmd_runJunkControls":
+ case "cmd_deleteJunk":
+ case "button_file":
+ case "cmd_emptyTrash":
+ case "cmd_compactFolder":
+ case "cmd_settingsOffline":
+ case "cmd_selectAll":
+ case "cmd_selectThread":
+ case "cmd_selectFlagged":
+ case "cmd_viewAllHeader":
+ case "cmd_viewNormalHeader":
+ return true;
+ case "cmd_downloadFlagged":
+ case "cmd_downloadSelected":
+ case "cmd_synchronizeOffline":
+ return !Services.io.offline;
+ case "cmd_watchThread":
+ case "cmd_killThread":
+ case "cmd_killSubthread":
+ case "cmd_cancel":
+ return gFolderDisplay.selectedMessageIsNews;
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ var enabled = new Object();
+ enabled.value = false;
+ var checkStatus = new Object();
+
+ switch ( command )
+ {
+ case "cmd_delete":
+ UpdateDeleteCommand();
+ // fall through
+ case "button_delete":
+ if (command == "button_delete")
+ UpdateDeleteToolbarButton(false);
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.deleteMsg, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_shiftDelete":
+ case "button_shiftDelete":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.deleteNoTrash, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_cancel":
+ return GetNumSelectedMessages() == 1 &&
+ gFolderDisplay.selectedMessageIsNews;
+ case "button_junk":
+ UpdateJunkToolbarButton();
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.junk, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_killThread":
+ case "cmd_killSubthread":
+ return GetNumSelectedMessages() > 0;
+ case "cmd_watchThread":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.toggleThreadWatched, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_createFilterFromPopup":
+ case "cmd_createFilterFromMenu":
+ var loadedFolder = GetLoadedMsgFolder();
+ if (!(loadedFolder && loadedFolder.server.canHaveFilters))
+ return false; // else fall thru
+ case "cmd_saveAsFile":
+ return GetNumSelectedMessages() > 0;
+ case "cmd_saveAsTemplate":
+ var msgFolder = GetSelectedMsgFolders();
+ var target = msgFolder[0].server.localStoreType;
+ if (GetNumSelectedMessages() == 0 || target == "news")
+ return false; // else fall thru
+ case "cmd_reply":
+ case "button_reply":
+ case "cmd_replyList":
+ case "cmd_replyGroup":
+ case "cmd_replySender":
+ case "cmd_replyall":
+ case "button_replyall":
+ case "cmd_replySenderAndGroup":
+ case "cmd_replyAllRecipients":
+ case "cmd_forward":
+ case "button_forward":
+ case "cmd_forwardInline":
+ case "cmd_forwardAttachment":
+ case "cmd_editAsNew":
+ case "cmd_editDraftMsg":
+ case "cmd_newMsgFromTemplate":
+ case "cmd_editTemplateMsg":
+ case "cmd_openMessage":
+ case "button_print":
+ case "cmd_print":
+ case "cmd_viewPageSource":
+ case "cmd_reload":
+ case "cmd_applyFiltersToSelection":
+ if (command == "cmd_applyFiltersToSelection")
+ {
+ var whichText = "valueMessage";
+ if (GetNumSelectedMessages() > 1)
+ whichText = "valueSelection";
+ goSetMenuValue(command, whichText);
+ goSetAccessKey(command, whichText + "AccessKey");
+ }
+ if (GetNumSelectedMessages() > 0)
+ {
+ if (gDBView)
+ {
+ gDBView.getCommandStatus(nsMsgViewCommandType.cmdRequiringMsgBody, enabled, checkStatus);
+ return enabled.value;
+ }
+ }
+ return false;
+ case "cmd_printpreview":
+ if ( GetNumSelectedMessages() == 1 && gDBView)
+ {
+ gDBView.getCommandStatus(nsMsgViewCommandType.cmdRequiringMsgBody, enabled, checkStatus);
+ return enabled.value;
+ }
+ return false;
+ case "cmd_printSetup":
+ case "cmd_viewAllHeader":
+ case "cmd_viewNormalHeader":
+ return true;
+ case "cmd_markAsFlagged":
+ case "button_file":
+ return GetNumSelectedMessages() > 0;
+ case "cmd_archive":
+ return gFolderDisplay.canArchiveSelectedMessages;
+ case "cmd_markAsJunk":
+ case "cmd_markAsNotJunk":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.junk, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_recalculateJunkScore":
+ // We're going to take a conservative position here, because we really
+ // don't want people running junk controls on folders that are not
+ // enabled for junk. The junk type picks up possible dummy message headers,
+ // while the runJunkControls will prevent running on XF virtual folders.
+ if (gDBView)
+ {
+ gDBView.getCommandStatus(nsMsgViewCommandType.runJunkControls, enabled, checkStatus);
+ if (enabled.value)
+ gDBView.getCommandStatus(nsMsgViewCommandType.junk, enabled, checkStatus);
+ }
+ return enabled.value;
+ case "cmd_markAsShowRemote":
+ return (GetNumSelectedMessages() > 0 && checkMsgHdrPropertyIsNot("remoteContentPolicy", kAllowRemoteContent));
+ case "cmd_markAsNotPhish":
+ return (GetNumSelectedMessages() > 0 && checkMsgHdrPropertyIsNot("notAPhishMessage", kNotAPhishMessage));
+ case "cmd_displayMsgFilters":
+ return MailServices.accounts.accounts.length > 0;
+ case "cmd_applyFilters":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.applyFilters, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_runJunkControls":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.runJunkControls, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_deleteJunk":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.deleteJunk, enabled, checkStatus);
+ return enabled.value;
+ case "button_mark":
+ case "cmd_markThreadAsRead":
+ return GetNumSelectedMessages() > 0;
+ case "cmd_markAsRead":
+ return CanMarkMsgAsRead(true);
+ case "cmd_markAsUnread":
+ return CanMarkMsgAsRead(false);
+ case "button_next":
+ return IsViewNavigationItemEnabled();
+ case "cmd_nextMsg":
+ case "cmd_nextUnreadMsg":
+ case "cmd_nextUnreadThread":
+ case "cmd_previousMsg":
+ case "cmd_previousUnreadMsg":
+ return IsViewNavigationItemEnabled();
+ case "button_goBack":
+ case "cmd_goBack":
+ return gDBView && gDBView.navigateStatus(nsMsgNavigationType.back);
+ case "button_goForward":
+ case "cmd_goForward":
+ return gDBView && gDBView.navigateStatus(nsMsgNavigationType.forward);
+ case "cmd_goStartPage":
+ return Services.prefs.getBoolPref("mailnews.start_page.enabled") && !IsMessagePaneCollapsed();
+ case "cmd_markAllRead":
+ return IsFolderSelected() && gDBView && gDBView.msgFolder.getNumUnread(false) > 0;
+ case "cmd_markReadByDate":
+ return IsFolderSelected();
+ case "cmd_find":
+ case "cmd_findNext":
+ case "cmd_findPrev":
+ return IsMessageDisplayedInMessagePane();
+ break;
+ case "button_search":
+ case "cmd_search":
+ return MailServices.accounts.accounts.length > 0;
+ case "cmd_selectAll":
+ case "cmd_selectFlagged":
+ return !!gDBView;
+ // these are enabled on when we are in threaded mode
+ case "cmd_selectThread":
+ if (GetNumSelectedMessages() <= 0) return false;
+ case "cmd_expandAllThreads":
+ case "cmd_collapseAllThreads":
+ return gDBView && (gDBView.viewFlags & nsMsgViewFlagsType.kThreadedDisplay);
+ break;
+ case "cmd_nextFlaggedMsg":
+ case "cmd_previousFlaggedMsg":
+ return IsViewNavigationItemEnabled();
+ case "cmd_viewAllMsgs":
+ case "cmd_viewUnreadMsgs":
+ case "cmd_viewIgnoredThreads":
+ return gDBView;
+ case "cmd_viewThreadsWithUnread":
+ case "cmd_viewWatchedThreadsWithUnread":
+ return gDBView && !(GetSelectedMsgFolders()[0].flags &
+ Ci.nsMsgFolderFlags.Virtual);
+ case "cmd_stop":
+ return true;
+ case "cmd_undo":
+ case "cmd_redo":
+ return SetupUndoRedoCommand(command);
+ case "cmd_renameFolder":
+ {
+ let folders = GetSelectedMsgFolders();
+ return folders.length == 1 && folders[0].canRename &&
+ folders[0].isCommandEnabled("cmd_renameFolder");
+ }
+ case "cmd_sendUnsentMsgs":
+ return IsSendUnsentMsgsEnabled(null);
+ case "cmd_subscribe":
+ return IsSubscribeEnabled();
+ case "cmd_properties":
+ return IsPropertiesEnabled(command);
+ case "button_getNewMessages":
+ case "cmd_getNewMessages":
+ case "cmd_getMsgsForAuthAccounts":
+ return IsGetNewMessagesEnabled();
+ case "cmd_getNextNMessages":
+ return IsGetNextNMessagesEnabled();
+ case "cmd_emptyTrash":
+ {
+ let folder = GetSelectedMsgFolders()[0];
+ return folder && folder.server.canEmptyTrashOnExit ?
+ IsMailFolderSelected() : false;
+ }
+ case "cmd_compactFolder":
+ {
+ let folders = GetSelectedMsgFolders();
+ let canCompactAll = function canCompactAll(folder) {
+ return folder.server.canCompactFoldersOnServer &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Virtual) &&
+ folder.isCommandEnabled("cmd_compactFolder");
+ }
+ return folders && folders.every(canCompactAll);
+ }
+ case "cmd_setFolderCharset":
+ return IsFolderCharsetEnabled();
+ case "cmd_downloadFlagged":
+ return !Services.io.offline;
+ case "cmd_downloadSelected":
+ return IsFolderSelected() && !Services.io.offline &&
+ GetNumSelectedMessages() > 0;
+ case "cmd_synchronizeOffline":
+ return !Services.io.offline;
+ case "cmd_settingsOffline":
+ return IsAccountOfflineEnabled();
+ default:
+ return false;
+ }
+ return false;
+ },
+
+ doCommand: function(command)
+ {
+ // if the user invoked a key short cut then it is possible that we got here for a command which is
+ // really disabled. kick out if the command should be disabled.
+ if (!this.isCommandEnabled(command))
+ return;
+
+ switch (command)
+ {
+ case "button_getNewMessages":
+ case "cmd_getNewMessages":
+ MsgGetMessage();
+ break;
+ case "cmd_getMsgsForAuthAccounts":
+ MsgGetMessagesForAllAuthenticatedAccounts();
+ break;
+ case "cmd_getNextNMessages":
+ MsgGetNextNMessages();
+ break;
+ case "cmd_archive":
+ MsgArchiveSelectedMessages(null);
+ break;
+ case "cmd_reply":
+ MsgReplyMessage(null);
+ break;
+ case "cmd_replyList":
+ MsgReplyList(null);
+ break;
+ case "cmd_replyGroup":
+ MsgReplyGroup(null);
+ break;
+ case "cmd_replySender":
+ MsgReplySender(null);
+ break;
+ case "cmd_replyall":
+ MsgReplyToAllMessage(null);
+ break;
+ case "cmd_replySenderAndGroup":
+ MsgReplyToSenderAndGroup(null);
+ break;
+ case "cmd_replyAllRecipients":
+ MsgReplyToAllRecipients(null);
+ break;
+ case "cmd_forward":
+ MsgForwardMessage(null);
+ break;
+ case "cmd_forwardInline":
+ MsgForwardAsInline(null);
+ break;
+ case "cmd_forwardAttachment":
+ MsgForwardAsAttachment(null);
+ break;
+ case "cmd_editAsNew":
+ MsgEditMessageAsNew(null);
+ break;
+ case "cmd_editDraftMsg":
+ MsgEditDraftMessage(null);
+ break;
+ case "cmd_newMsgFromTemplate":
+ MsgNewMessageFromTemplate(null);
+ break;
+ case "cmd_editTemplateMsg":
+ MsgEditTemplateMessage(null);
+ break;
+ case "cmd_createFilterFromMenu":
+ MsgCreateFilter();
+ break;
+ case "cmd_createFilterFromPopup":
+ CreateFilter(document.popupNode);
+ break;
+ case "cmd_delete":
+ case "button_delete":
+ MsgDeleteMessage(false);
+ UpdateDeleteToolbarButton(false);
+ break;
+ case "cmd_shiftDelete":
+ case "button_shiftDelete":
+ MsgDeleteMessage(true);
+ UpdateDeleteToolbarButton(false);
+ break;
+ case "cmd_cancel":
+ let message = gFolderDisplay.selectedMessage;
+ message.folder.QueryInterface(Ci.nsIMsgNewsFolder)
+ .cancelMessage(message, msgWindow);
+ break;
+ case "cmd_killThread":
+ /* kill thread kills the thread and then does a next unread */
+ GoNextMessage(nsMsgNavigationType.toggleThreadKilled, true);
+ break;
+ case "cmd_killSubthread":
+ GoNextMessage(nsMsgNavigationType.toggleSubthreadKilled, true);
+ break;
+ case "cmd_watchThread":
+ gDBView.doCommand(nsMsgViewCommandType.toggleThreadWatched);
+ break;
+ case "button_next":
+ case "cmd_nextUnreadMsg":
+ GoNextMessage(nsMsgNavigationType.nextUnreadMessage, true);
+ break;
+ case "cmd_nextUnreadThread":
+ GoNextMessage(nsMsgNavigationType.nextUnreadThread, true);
+ break;
+ case "cmd_nextMsg":
+ GoNextMessage(nsMsgNavigationType.nextMessage, false);
+ break;
+ case "cmd_nextFlaggedMsg":
+ GoNextMessage(nsMsgNavigationType.nextFlagged, true);
+ break;
+ case "cmd_previousMsg":
+ GoNextMessage(nsMsgNavigationType.previousMessage, false);
+ break;
+ case "cmd_previousUnreadMsg":
+ GoNextMessage(nsMsgNavigationType.previousUnreadMessage, true);
+ break;
+ case "cmd_previousFlaggedMsg":
+ GoNextMessage(nsMsgNavigationType.previousFlagged, true);
+ break;
+ case "button_goForward":
+ case "cmd_goForward":
+ GoNextMessage(nsMsgNavigationType.forward, true);
+ break;
+ case "button_goBack":
+ case "cmd_goBack":
+ GoNextMessage(nsMsgNavigationType.back, true);
+ break;
+ case "cmd_goStartPage":
+ HideMessageHeaderPane();
+ loadStartPage();
+ break;
+ case "cmd_viewAllMsgs":
+ case "cmd_viewThreadsWithUnread":
+ case "cmd_viewWatchedThreadsWithUnread":
+ case "cmd_viewUnreadMsgs":
+ case "cmd_viewIgnoredThreads":
+ SwitchView(command);
+ break;
+ case "cmd_undo":
+ messenger.undo(msgWindow);
+ break;
+ case "cmd_redo":
+ messenger.redo(msgWindow);
+ break;
+ case "cmd_expandAllThreads":
+ gDBView.doCommand(nsMsgViewCommandType.expandAll);
+ break;
+ case "cmd_collapseAllThreads":
+ gDBView.doCommand(nsMsgViewCommandType.collapseAll);
+ break;
+ case "cmd_renameFolder":
+ gFolderTreeController.renameFolder();
+ return;
+ case "cmd_sendUnsentMsgs":
+ MsgSendUnsentMsgs();
+ return;
+ case "cmd_subscribe":
+ MsgSubscribe();
+ return;
+ case "cmd_openMessage":
+ MsgOpenSelectedMessages();
+ return;
+ case "cmd_printSetup":
+ PrintUtils.showPageSetup();
+ return;
+ case "cmd_print":
+ PrintEnginePrint();
+ return;
+ case "cmd_printpreview":
+ PrintEnginePrintPreview();
+ return;
+ case "cmd_saveAsFile":
+ MsgSaveAsFile();
+ return;
+ case "cmd_saveAsTemplate":
+ MsgSaveAsTemplate();
+ return;
+ case "cmd_viewPageSource":
+ MsgViewPageSource();
+ return;
+ case "cmd_setFolderCharset":
+ gFolderTreeController.editFolder();
+ return;
+ case "cmd_reload":
+ ReloadMessage();
+ return;
+ case "cmd_find":
+ MsgFind();
+ return;
+ case "cmd_findNext":
+ MsgFindAgain(false);
+ return;
+ case "cmd_findPrev":
+ MsgFindAgain(true);
+ return;
+ case "cmd_properties":
+ gFolderTreeController.editFolder();
+ return;
+ case "button_search":
+ case "cmd_search":
+ MsgSearchMessages();
+ return;
+ case "button_mark":
+ MsgMarkMsgAsRead();
+ return;
+ case "cmd_markAsRead":
+ MsgMarkMsgAsRead(true);
+ return;
+ case "cmd_markAsUnread":
+ MsgMarkMsgAsRead(false);
+ return;
+ case "cmd_markThreadAsRead":
+ MsgMarkThreadAsRead();
+ return;
+ case "cmd_markAllRead":
+ gDBView.doCommand(nsMsgViewCommandType.markAllRead);
+ return;
+ case "cmd_markReadByDate":
+ MsgMarkReadByDate();
+ return;
+ case "button_junk":
+ MsgJunk();
+ return;
+ case "cmd_stop":
+ msgWindow.StopUrls();
+ return;
+ case "cmd_markAsFlagged":
+ MsgMarkAsFlagged();
+ return;
+ case "cmd_viewAllHeader":
+ MsgViewAllHeaders();
+ return;
+ case "cmd_viewNormalHeader":
+ MsgViewNormalHeaders();
+ return;
+ case "cmd_markAsJunk":
+ JunkSelectedMessages(true);
+ return;
+ case "cmd_markAsNotJunk":
+ JunkSelectedMessages(false);
+ return;
+ case "cmd_recalculateJunkScore":
+ analyzeMessagesForJunk();
+ return;
+ case "cmd_markAsShowRemote":
+ LoadMsgWithRemoteContent();
+ return;
+ case "cmd_markAsNotPhish":
+ MsgIsNotAScam();
+ return;
+ case "cmd_displayMsgFilters":
+ MsgFilters(null, null);
+ return;
+ case "cmd_applyFiltersToSelection":
+ MsgApplyFiltersToSelection();
+ return;
+ case "cmd_applyFilters":
+ MsgApplyFilters(null);
+ return;
+ case "cmd_runJunkControls":
+ filterFolderForJunk();
+ return;
+ case "cmd_deleteJunk":
+ deleteJunkInFolder();
+ return;
+ case "cmd_emptyTrash":
+ gFolderTreeController.emptyTrash();
+ return;
+ case "cmd_compactFolder":
+ gFolderTreeController.compactAllFoldersForAccount();
+ return;
+ case "cmd_downloadFlagged":
+ MsgDownloadFlagged();
+ break;
+ case "cmd_downloadSelected":
+ MsgDownloadSelected();
+ break;
+ case "cmd_synchronizeOffline":
+ MsgSynchronizeOffline();
+ break;
+ case "cmd_settingsOffline":
+ MsgSettingsOffline();
+ break;
+ case "cmd_selectAll":
+ // move the focus so the user can delete the newly selected messages, not the folder
+ SetFocusThreadPane();
+ // if in threaded mode, the view will expand all before selecting all
+ gDBView.doCommand(nsMsgViewCommandType.selectAll)
+ if (gDBView.numSelected != 1) {
+ setTitleFromFolder(gDBView.msgFolder,null);
+ ClearMessagePane();
+ }
+ break;
+ case "cmd_selectThread":
+ gDBView.doCommand(nsMsgViewCommandType.selectThread);
+ break;
+ case "cmd_selectFlagged":
+ gDBView.doCommand(nsMsgViewCommandType.selectFlagged);
+ break;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ // on blur events set the menu item texts back to the normal values
+ if ( event == 'blur' )
+ {
+ goSetMenuValue('cmd_undo', 'valueDefault');
+ goSetMenuValue('cmd_redo', 'valueDefault');
+ }
+ }
+};
+
+function MsgCloseTabOrWindow()
+{
+ var tabmail = GetTabMail();
+ if (tabmail.tabInfo.length > 1)
+ tabmail.removeCurrentTab();
+ else
+ window.close();
+}
+
+function GetNumSelectedMessages()
+{
+ return gDBView ? gDBView.numSelected : 0;
+}
+
+var gLastFocusedElement=null;
+
+function FocusRingUpdate_Mail()
+{
+ // If the focusedElement is null, we're here on a blur.
+ // nsFocusController::Blur() calls nsFocusController::SetFocusedElement(null),
+ // which will update any commands listening for "focus".
+ // we really only care about nsFocusController::Focus() happens,
+ // which calls nsFocusController::SetFocusedElement(element)
+ var currentFocusedElement = gFolderDisplay.focusedPane;
+
+ if (currentFocusedElement != gLastFocusedElement) {
+ if (currentFocusedElement)
+ currentFocusedElement.setAttribute("focusring", "true");
+
+ if (gLastFocusedElement)
+ gLastFocusedElement.removeAttribute("focusring");
+
+ gLastFocusedElement = currentFocusedElement;
+
+ // since we just changed the pane with focus we need to update the toolbar to reflect this
+ // XXX TODO
+ // can we optimize
+ // and just update cmd_delete and button_delete?
+ UpdateMailToolbar("focus");
+ }
+}
+
+function SetupCommandUpdateHandlers()
+{
+ // folder pane
+ var widget = document.getElementById("folderTree");
+ if (widget)
+ widget.controllers.appendController(FolderPaneController);
+}
+
+// Called from <msgMail3PaneWindow.js>.
+function UnloadCommandUpdateHandlers()
+{
+ var widget = document.getElementById("folderTree");
+ if (widget)
+ widget.controllers.removeController(FolderPaneController);
+}
+
+function IsSendUnsentMsgsEnabled(folderResource)
+{
+ var msgSendLater =
+ Cc["@mozilla.org/messengercompose/sendlater;1"]
+ .getService(Ci.nsIMsgSendLater);
+
+ // If we're currently sending unsent msgs, disable this cmd.
+ if (msgSendLater.sendingMessages)
+ return false;
+
+ if (folderResource &&
+ folderResource instanceof Ci.nsIMsgFolder) {
+ // If unsentMsgsFolder is non-null, it is the "Outbox" folder.
+ // We're here because we've done a right click on the "Outbox"
+ // folder (context menu), so we can use the folder and return true/false
+ // straight away.
+ return folderResource.getTotalMessages(false) > 0;
+ }
+
+ // Otherwise, we don't know where we are, so use the current identity and
+ // find out if we have messages or not via that.
+ let identity = null;
+ let folders = GetSelectedMsgFolders();
+ if (folders.length > 0)
+ identity = getIdentityForServer(folders[0].server);
+
+ if (!identity) {
+ let defaultAccount = MailServices.accounts.defaultAccount;
+ if (defaultAccount)
+ identity = defaultAccount.defaultIdentity;
+
+ if (!identity)
+ return false;
+ }
+
+ return msgSendLater.hasUnsentMessages(identity);
+}
+
+/**
+ * Determine whether there exists any server for which to show the Subscribe dialog.
+ */
+function IsSubscribeEnabled()
+{
+ // If there are any IMAP or News servers, we can show the dialog any time and
+ // it will properly show those.
+ for (let server of accountManager.allServers) {
+ if (server.type == "imap" || server.type == "nntp")
+ return true;
+ }
+
+ // RSS accounts use a separate Subscribe dialog that we can only show when
+ // such an account is selected.
+ let preselectedFolder = GetFirstSelectedMsgFolder();
+ if (preselectedFolder && preselectedFolder.server.type == "rss")
+ return true;
+
+ return false;
+}
+
+function IsFolderCharsetEnabled()
+{
+ return IsFolderSelected();
+}
+
+function IsPropertiesEnabled(command)
+{
+ let folders = GetSelectedMsgFolders();
+ if (!folders.length)
+ return false;
+
+ let folder = folders[0];
+ // When servers are selected, it should be "Edit | Properties...".
+ if (folder.isServer) {
+ goSetMenuValue(command, "valueGeneric");
+ } else if (folder.server.type == "nntp" &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
+ goSetMenuValue(command, "valueNewsgroup");
+ } else {
+ goSetMenuValue(command, "valueFolder");
+ }
+
+ return folders.length == 1;
+}
+
+function IsViewNavigationItemEnabled()
+{
+ return IsFolderSelected();
+}
+
+function IsFolderSelected()
+{
+ let folders = GetSelectedMsgFolders();
+ return folders.length == 1 && !folders[0].isServer;
+}
+
+function IsMessageDisplayedInMessagePane()
+{
+ return (!IsMessagePaneCollapsed() && (GetNumSelectedMessages() > 0));
+}
+
+function SetFocusThreadPaneIfNotOnMessagePane()
+{
+ var focusedElement = gFolderDisplay.focusedPane;
+
+ if((focusedElement != GetThreadTree()) &&
+ (focusedElement != GetMessagePane()))
+ SetFocusThreadPane();
+}
+
+function SwitchPaneFocus(event)
+{
+ var folderTree = document.getElementById("folderTree");
+ var threadTree = GetThreadTree();
+ var messagePane = GetMessagePane();
+
+ var folderPaneCollapsed = document.getElementById("folderPaneBox").collapsed;
+
+ // Although internally this is actually a four-pane window, it is presented as
+ // a three-pane -- the search pane is more of a toolbar. So, shift among the
+ // three main panes.
+
+ var focusedElement = gFolderDisplay.focusedPane;
+ if (focusedElement == null) // focus not on one of the main three panes?
+ focusedElement = threadTree; // treat as if on thread tree
+
+ if (event && event.shiftKey)
+ {
+ // Reverse traversal: Message -> Thread -> Folder -> Message
+ if (focusedElement == threadTree && !folderPaneCollapsed)
+ folderTree.focus();
+ else if (focusedElement != messagePane && !IsMessagePaneCollapsed())
+ SetFocusMessagePane();
+ else
+ threadTree.focus();
+ }
+ else
+ {
+ // Forward traversal: Folder -> Thread -> Message -> Folder
+ if (focusedElement == threadTree && !IsMessagePaneCollapsed())
+ SetFocusMessagePane();
+ else if (focusedElement != folderTree && !folderPaneCollapsed)
+ folderTree.focus();
+ else
+ threadTree.focus();
+ }
+}
+
+function SetFocusThreadPane()
+{
+ var threadTree = GetThreadTree();
+ threadTree.focus();
+}
+
+function SetFocusMessagePane()
+{
+ // XXX hack: to clear the focus on the previous element first focus
+ // on the message pane element then focus on the main content window
+ GetMessagePane().focus();
+ GetMessagePaneFrame().focus();
+}
+
+//
+// This function checks if the configured junk mail can be renamed or deleted.
+//
+function CanRenameDeleteJunkMail(aFolderUri)
+{
+ if (!aFolderUri)
+ return false;
+
+ // Go through junk mail settings for all servers and see if the folder is set/used by anyone.
+ try
+ {
+ var allServers = accountManager.allServers;
+
+ for (var i = 0; i < allServers.length; i++)
+ {
+ var currentServer =
+ allServers.queryElementAt(i, Ci.nsIMsgIncomingServer);
+ var settings = currentServer.spamSettings;
+ // If junk mail control or move junk mail to folder option is disabled then
+ // allow the folder to be removed/renamed since the folder is not used in this case.
+ if (!settings.level || !settings.moveOnSpam)
+ continue;
+ if (settings.spamFolderURI == aFolderUri)
+ return false;
+ }
+ }
+ catch(ex)
+ {
+ dump("Can't get all servers\n");
+ }
+ return true;
+}
+
+/** Check if this is a folder the user is allowed to delete. */
+function CanDeleteFolder(folder) {
+ if (folder.isServer)
+ return false;
+
+ var specialFolder = FolderUtils.getSpecialFolderString(folder);
+
+ if (specialFolder == "Inbox" || specialFolder == "Trash" ||
+ specialFolder == "Drafts" || specialFolder == "Sent" ||
+ specialFolder == "Templates" || specialFolder == "Outbox" ||
+ (specialFolder == "Junk" && !CanRenameDeleteJunkMail(folder.URI)))
+ return false;
+
+ return true;
+}
diff --git a/comm/suite/mailnews/content/mailCommands.js b/comm/suite/mailnews/content/mailCommands.js
new file mode 100644
index 0000000000..ae7e91a6cb
--- /dev/null
+++ b/comm/suite/mailnews/content/mailCommands.js
@@ -0,0 +1,415 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+
+/**
+ * Get the identity that most likely is the best one to use, given the hint.
+ * @param {Array<nsIMsgIdentity> identities The candidates to pick from.
+ * @param {String} optionalHint String containing comma separated mailboxes
+ */
+function getBestIdentity(identities, optionalHint)
+{
+ let identityCount = identities.length;
+ if (identityCount < 1)
+ return null;
+
+ // If we have more than one identity and a hint to help us pick one.
+ if (identityCount > 1 && optionalHint) {
+ // Normalize case on the optional hint to improve our chances of
+ // finding a match.
+ let hints = optionalHint.toLowerCase().split(",");
+
+ for (let i = 0 ; i < hints.length; i++) {
+ for (let identity of identities) {
+ if (!identity.email)
+ continue;
+ if (hints[i].trim() == identity.email.toLowerCase() ||
+ hints[i].includes("<" + identity.email.toLowerCase() + ">"))
+ return identity;
+ }
+ }
+ }
+ // Return only found identity or pick the first one from list if no matches found.
+ return identities[0];
+}
+
+function getIdentityForServer(server, optionalHint)
+{
+ let identities = accountManager.getIdentitiesForServer(server);
+ return getBestIdentity(identities, optionalHint);
+}
+
+/**
+ * Get the identity for the given header.
+ * @param hdr nsIMsgHdr message header
+ * @param type nsIMsgCompType compose type the identity ise used for.
+ */
+
+function GetIdentityForHeader(aMsgHdr, aType)
+{
+ function findDeliveredToIdentityEmail() {
+ // Get the delivered-to headers.
+ let key = "delivered-to";
+ let deliveredTos = new Array();
+ let index = 0;
+ let header = "";
+ while (currentHeaderData[key]) {
+ deliveredTos.push(currentHeaderData[key].headerValue.toLowerCase().trim());
+ key = "delivered-to" + index++;
+ }
+
+ // Reverse the array so that the last delivered-to header will show at front.
+ deliveredTos.reverse();
+ for (let i = 0; i < deliveredTos.length; i++) {
+ for (let identity of accountManager.allIdentities) {
+ if (!identity.email)
+ continue;
+ // If the deliver-to header contains the defined identity, that's it.
+ if (deliveredTos[i] == identity.email.toLowerCase() ||
+ deliveredTos[i].includes("<" + identity.email.toLowerCase() + ">"))
+ return identity.email;
+ }
+ }
+ return "";
+ }
+
+ let hintForIdentity = "";
+ if (aType == Ci.nsIMsgCompType.ReplyToList)
+ hintForIdentity = findDeliveredToIdentityEmail();
+ else if (aType == Ci.nsIMsgCompType.Template ||
+ aType == Ci.nsIMsgCompType.EditTemplate ||
+ aType == Ci.nsIMsgCompType.EditAsNew)
+ hintForIdentity = aMsgHdr.author;
+ else
+ hintForIdentity = aMsgHdr.recipients + "," + aMsgHdr.ccList + "," +
+ findDeliveredToIdentityEmail();
+
+ let server = null;
+ let identity = null;
+ let folder = aMsgHdr.folder;
+ if (folder)
+ {
+ server = folder.server;
+ identity = folder.customIdentity;
+ }
+
+ if (!identity)
+ {
+ let accountKey = aMsgHdr.accountKey;
+ if (accountKey.length > 0)
+ {
+ let account = accountManager.getAccount(accountKey);
+ if (account)
+ server = account.incomingServer;
+ }
+
+ if (server)
+ identity = getIdentityForServer(server, hintForIdentity);
+
+ if (!identity)
+ identity = getBestIdentity(accountManager.allIdentities, hintForIdentity);
+ }
+ return identity;
+}
+
+function GetNextNMessages(folder)
+{
+ if (folder) {
+ var newsFolder = folder.QueryInterface(Ci.nsIMsgNewsFolder);
+ if (newsFolder) {
+ newsFolder.getNextNMessages(msgWindow);
+ }
+ }
+}
+
+// type is a nsIMsgCompType and format is a nsIMsgCompFormat
+function ComposeMessage(type, format, folder, messageArray)
+{
+ var msgComposeType = Ci.nsIMsgCompType;
+ var identity = null;
+ var newsgroup = null;
+ var hdr;
+
+ // dump("ComposeMessage folder=" + folder + "\n");
+ try
+ {
+ if (folder)
+ {
+ // Get the incoming server associated with this uri.
+ var server = folder.server;
+
+ // If they hit new or reply and they are reading a newsgroup,
+ // turn this into a new post or a reply to group.
+ if (!folder.isServer && server.type == "nntp" && type == msgComposeType.New)
+ {
+ type = msgComposeType.NewsPost;
+ newsgroup = folder.folderURL;
+ }
+
+ identity = getIdentityForServer(server);
+ // dump("identity = " + identity + "\n");
+ }
+ }
+ catch (ex)
+ {
+ dump("failed to get an identity to pre-select: " + ex + "\n");
+ }
+
+ // dump("\nComposeMessage from XUL: " + identity + "\n");
+
+ if (!msgComposeService)
+ {
+ dump("### msgComposeService is invalid\n");
+ return;
+ }
+
+ switch (type)
+ {
+ case msgComposeType.New: //new message
+ // dump("OpenComposeWindow with " + identity + "\n");
+ // If the addressbook sidebar panel is open and has focus, get
+ // the selected addresses from it.
+ if (document.commandDispatcher.focusedWindow &&
+ document.commandDispatcher.focusedWindow
+ .document.documentElement.hasAttribute("selectedaddresses"))
+ NewMessageToSelectedAddresses(type, format, identity);
+ else
+ msgComposeService.OpenComposeWindow(null, null, null, type,
+ format, identity, null, msgWindow);
+ return;
+ case msgComposeType.NewsPost:
+ // dump("OpenComposeWindow with " + identity + " and " + newsgroup + "\n");
+ msgComposeService.OpenComposeWindow(null, null, newsgroup, type,
+ format, identity, null, msgWindow);
+ return;
+ case msgComposeType.ForwardAsAttachment:
+ if (messageArray && messageArray.length)
+ {
+ // If we have more than one ForwardAsAttachment then pass null instead
+ // of the header to tell the compose service to work out the attachment
+ // subjects from the URIs.
+ hdr = messageArray.length > 1 ? null : messenger.msgHdrFromURI(messageArray[0]);
+ msgComposeService.OpenComposeWindow(null, hdr, messageArray.join(','),
+ type, format, identity, null, msgWindow);
+ return;
+ }
+ default:
+ if (!messageArray)
+ return;
+
+ // Limit the number of new compose windows to 8. Why 8 ?
+ // I like that number :-)
+ if (messageArray.length > 8)
+ messageArray.length = 8;
+
+ for (var i = 0; i < messageArray.length; ++i)
+ {
+ var messageUri = messageArray[i];
+ hdr = messenger.msgHdrFromURI(messageUri);
+ identity = GetIdentityForHeader(hdr, type);
+ if (FeedMessageHandler.isFeedMessage(hdr))
+ openComposeWindowForRSSArticle(null, hdr, messageUri, type,
+ format, identity, msgWindow);
+ else
+ msgComposeService.OpenComposeWindow(null, hdr, messageUri, type,
+ format, identity, null, msgWindow);
+ }
+ }
+}
+
+function NewMessageToSelectedAddresses(type, format, identity) {
+ var abSidebarPanel = document.commandDispatcher.focusedWindow;
+ var abResultsTree = abSidebarPanel.document.getElementById("abResultsTree");
+ var abResultsBoxObject = abResultsTree.treeBoxObject;
+ var abView = abResultsBoxObject.view;
+ abView = abView.QueryInterface(Ci.nsIAbView);
+ var addresses = abView.selectedAddresses;
+ var params = Cc["@mozilla.org/messengercompose/composeparams;1"].createInstance(Ci.nsIMsgComposeParams);
+ if (params) {
+ params.type = type;
+ params.format = format;
+ params.identity = identity;
+ var composeFields = Cc["@mozilla.org/messengercompose/composefields;1"].createInstance(Ci.nsIMsgCompFields);
+ if (composeFields) {
+ let addressList = [];
+ const nsISupportsString = Ci.nsISupportsString;
+ for (let i = 0; i < addresses.length; i++) {
+ addressList.push(addresses.queryElementAt(i, nsISupportsString).data);
+ }
+ composeFields.to = addressList.join(",");
+ params.composeFields = composeFields;
+ msgComposeService.OpenComposeWindowWithParams(null, params);
+ }
+ }
+}
+
+function Subscribe(preselectedMsgFolder)
+{
+ window.openDialog("chrome://messenger/content/subscribe.xul",
+ "subscribe", "chrome,modal,titlebar,resizable=yes",
+ {folder:preselectedMsgFolder,
+ okCallback:SubscribeOKCallback});
+}
+
+function SubscribeOKCallback(changeTable)
+{
+ for (var serverURI in changeTable) {
+ var folder = MailUtils.getFolderForURI(serverURI, true);
+ var server = folder.server;
+ var subscribableServer =
+ server.QueryInterface(Ci.nsISubscribableServer);
+
+ for (var name in changeTable[serverURI]) {
+ if (changeTable[serverURI][name] == true) {
+ try {
+ subscribableServer.subscribe(name);
+ }
+ catch (ex) {
+ dump("failed to subscribe to " + name + ": " + ex + "\n");
+ }
+ }
+ else if (changeTable[serverURI][name] == false) {
+ try {
+ subscribableServer.unsubscribe(name);
+ }
+ catch (ex) {
+ dump("failed to unsubscribe to " + name + ": " + ex + "\n");
+ }
+ }
+ else {
+ // no change
+ }
+ }
+
+ try {
+ subscribableServer.commitSubscribeChanges();
+ }
+ catch (ex) {
+ dump("failed to commit the changes: " + ex + "\n");
+ }
+ }
+}
+
+function SaveAsFile(aUris)
+{
+ if (/type=application\/x-message-display/.test(aUris[0]))
+ {
+ saveURL(aUris[0], null, "", true, false, null, document);
+ return;
+ }
+
+ var num = aUris.length;
+ var fileNames = [];
+ for (let i = 0; i < num; i++)
+ {
+ let subject = messenger.messageServiceFromURI(aUris[i])
+ .messageURIToMsgHdr(aUris[i])
+ .mime2DecodedSubject;
+ fileNames[i] = suggestUniqueFileName(subject.substr(0, 120), ".eml",
+ fileNames);
+ }
+ if (num == 1)
+ messenger.saveAs(aUris[0], true, null, fileNames[0]);
+ else
+ messenger.saveMessages(fileNames, aUris);
+}
+
+function saveAsUrlListener(aUri, aIdentity)
+{
+ this.uri = aUri;
+ this.identity = aIdentity;
+}
+
+saveAsUrlListener.prototype = {
+ OnStartRunningUrl: function(aUrl)
+ {
+ },
+ OnStopRunningUrl: function(aUrl, aExitCode)
+ {
+ messenger.saveAs(this.uri, false, this.identity, null);
+ }
+};
+
+function SaveAsTemplate(aUris)
+{
+ // For backwards compatibility check if the argument is a string and,
+ // if so, convert to an array.
+ if (typeof aUris == "string")
+ aUris = [aUris];
+
+ var num = aUris.length;
+ if (!num)
+ return;
+
+ for (let i = 0; i < num; i++)
+ {
+ let uri = aUris[i];
+ var hdr = messenger.msgHdrFromURI(uri);
+ var identity = GetIdentityForHeader(hdr, Ci.nsIMsgCompType.Template);
+ var templates = MailUtils.getFolderForURI(identity.stationeryFolder, false);
+ if (!templates.parent)
+ {
+ templates.setFlag(Ci.nsMsgFolderFlags.Templates);
+ let isAsync = templates.server.protocolInfo.foldersCreatedAsync;
+ templates.createStorageIfMissing(new saveAsUrlListener(uri, identity));
+ if (isAsync)
+ continue;
+ }
+ messenger.saveAs(uri, false, identity, null);
+ }
+}
+
+function MarkSelectedMessagesRead(markRead)
+{
+ ClearPendingReadTimer();
+ gDBView.doCommand(markRead ? nsMsgViewCommandType.markMessagesRead : nsMsgViewCommandType.markMessagesUnread);
+}
+
+function MarkSelectedMessagesFlagged(markFlagged)
+{
+ gDBView.doCommand(markFlagged ? nsMsgViewCommandType.flagMessages : nsMsgViewCommandType.unflagMessages);
+}
+
+function ViewPageSource(messages)
+{
+ var numMessages = messages.length;
+
+ if (numMessages == 0)
+ {
+ dump("MsgViewPageSource(): No messages selected.\n");
+ return false;
+ }
+
+ var browser = getBrowser();
+
+ try {
+ // First, get the mail session.
+ for (let i = 0; i < numMessages; i++) {
+ // Now, we need to get a URL from a URI.
+ var url = MailServices.mailSession.ConvertMsgURIToMsgURL(messages[i],
+ msgWindow);
+
+ // Strip out the message-display parameter to ensure that attached
+ // emails display the message source, not the processed HTML.
+ url = url.replace(/(\?|&)type=application\/x-message-display(&|$)/, "$1")
+ .replace(/\?$/, "");
+ window.openDialog("chrome://global/content/viewSource.xul", "_blank",
+ "all,dialog=no",
+ {URL: url, browser: browser,
+ outerWindowID: browser.outerWindowID});
+ }
+ return true;
+ } catch (e) {
+ // Couldn't get mail session.
+ return false;
+ }
+}
+
+function doHelpButton()
+{
+ openHelp("mail-offline-items");
+}
diff --git a/comm/suite/mailnews/content/mailContextMenus.js b/comm/suite/mailnews/content/mailContextMenus.js
new file mode 100644
index 0000000000..d70d7a31fc
--- /dev/null
+++ b/comm/suite/mailnews/content/mailContextMenus.js
@@ -0,0 +1,828 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+
+//NOTE: gMessengerBundle must be defined and set or this Overlay won't work
+
+/**
+ * Function to change the highlighted row back to the row that is currently
+ * outline/dotted without loading the contents of either rows. This is
+ * triggered when the context menu for a given row is hidden/closed
+ * (onpopuphiding).
+ * @param tree the tree element to restore selection for
+ */
+function RestoreSelectionWithoutContentLoad(tree)
+{
+ // If a delete or move command had been issued, then we should
+ // reset gRightMouseButtonDown and gThreadPaneDeleteOrMoveOccurred
+ // and return (see bug 142065).
+ if(gThreadPaneDeleteOrMoveOccurred)
+ {
+ gRightMouseButtonDown = false;
+ gThreadPaneDeleteOrMoveOccurred = false;
+ return;
+ }
+
+ var treeSelection = tree.view.selection;
+
+ // make sure that currentIndex is valid so that we don't try to restore
+ // a selection of an invalid row.
+ if((!treeSelection.isSelected(treeSelection.currentIndex)) &&
+ (treeSelection.currentIndex >= 0))
+ {
+ treeSelection.selectEventsSuppressed = true;
+ treeSelection.select(treeSelection.currentIndex);
+ treeSelection.selectEventsSuppressed = false;
+
+ // Keep track of which row in the thread pane is currently selected.
+ // This is currently only needed when deleting messages. See
+ // declaration of var in msgMail3PaneWindow.js.
+ if(tree.id == "threadTree")
+ gThreadPaneCurrentSelectedIndex = treeSelection.currentIndex;
+ }
+ else if(treeSelection.currentIndex < 0)
+ // Clear the selection in the case of when a folder has just been
+ // loaded where the message pane does not have a message loaded yet.
+ // When right-clicking a message in this case and dismissing the
+ // popup menu (by either executing a menu command or clicking
+ // somewhere else), the selection needs to be cleared.
+ // However, if the 'Delete Message' or 'Move To' menu item has been
+ // selected, DO NOT clear the selection, else it will prevent the
+ // tree view from refreshing.
+ treeSelection.clearSelection();
+
+ // Need to reset gRightMouseButtonDown to false here because
+ // TreeOnMouseDown() is only called on a mousedown, not on a key down.
+ // So resetting it here allows the loading of messages in the messagepane
+ // when navigating via the keyboard or the toolbar buttons *after*
+ // the context menu has been dismissed.
+ gRightMouseButtonDown = false;
+}
+
+/**
+ * Function to clear out the global nsContextMenu, and in the case when we
+ * are a threadpane context menu, restore the selection so that a right-click
+ * on a non-selected row doesn't move the selection.
+ * @param aTarget the target of the popup event
+ */
+function MailContextOnPopupHiding(aTarget, aEvent) {
+ // Don't do anything if it's a submenu's onpopuphiding that's just bubbling
+ // up to the top.
+ if (aEvent.target != aTarget)
+ return;
+
+ gContextMenu.hiding();
+ gContextMenu = null;
+ if (InThreadPane(aTarget))
+ RestoreSelectionWithoutContentLoad(GetThreadTree());
+}
+
+/**
+ * Determines whether the context menu was triggered by a node that's a child
+ * of the threadpane by looking for an ancestor node with id="threadTree".
+ * @param aTarget the target of the popup event
+ * @return true if the popupNode is a child of the threadpane, otherwise false
+ */
+function InThreadPane(aTarget)
+{
+ var node = aTarget.triggerNode;
+ while (node)
+ {
+ if (node.id == "threadTree")
+ return true;
+ node = node.parentNode;
+ }
+ return false;
+}
+
+/**
+ * Function to set up the global nsContextMenu, and the mailnews overlay.
+ * @param aTarget the target of the popup event
+ * @return true always
+ */
+function FillMailContextMenu(aTarget, aEvent) {
+ // If the popupshowing was for a submenu, we don't need to do anything.
+ if (aEvent.target != aTarget)
+ return true;
+
+ var inThreadPane = InThreadPane(aTarget);
+ gContextMenu = new nsContextMenu(aTarget);
+
+ // Initialize gContextMenuContentData.
+ if (aEvent)
+ gContextMenu.initContentData(aEvent);
+
+ // Need to call nsContextMenu's initItems to hide what is not used.
+ gContextMenu.initItems();
+
+ var numSelected = GetNumSelectedMessages();
+ var oneOrMore = (numSelected > 0);
+ var single = (numSelected == 1);
+
+ var isNewsgroup = gFolderDisplay.selectedMessageIsNews;
+
+ // Clear the global var used to keep track if a 'Delete Message' or 'Move
+ // To' command has been triggered via the thread pane context menu.
+ gThreadPaneDeleteOrMoveOccurred = false;
+
+ // Don't show mail items for links/images, just show related items.
+ var showMailItems = inThreadPane ||
+ (!gContextMenu.onImage && !gContextMenu.onLink);
+
+ // Select-all and copy are only available in the message-pane
+ ShowMenuItem("context-selectall", single && !inThreadPane);
+ ShowMenuItem("context-copy", !inThreadPane);
+
+ ShowMenuItem("mailContext-openNewWindow", inThreadPane && single);
+ ShowMenuItem("mailContext-openNewTab", inThreadPane && single);
+ ShowMenuItem("mailContext-downloadflagged",
+ inThreadPane || (numSelected > 1));
+ ShowMenuItem("mailContext-downloadselected",
+ inThreadPane || (numSelected > 1));
+
+ ShowMenuItem("mailContext-editAsNew", showMailItems && oneOrMore);
+ // Show "Edit Draft Message" menus only in a drafts folder;
+ // otherwise hide them.
+ let showEditDraft = showCommandInSpecialFolder("cmd_editDraftMsg",
+ Ci.nsMsgFolderFlags.Drafts);
+ ShowMenuItem("mailContext-editDraftMsg",
+ showMailItems && oneOrMore && showEditDraft);
+ // Show "New Message from Template" and "Edit Template" menus only in a
+ // templates folder; otherwise hide them.
+ let showTemplates = showCommandInSpecialFolder("cmd_newMsgFromTemplate",
+ Ci.nsMsgFolderFlags.Templates);
+ ShowMenuItem("mailContext-newMsgFromTemplate",
+ showMailItems && oneOrMore && showTemplates);
+ showTemplates = showCommandInSpecialFolder("cmd_editTemplateMsg",
+ Ci.nsMsgFolderFlags.Templates);
+ ShowMenuItem("mailContext-editTemplateMsg",
+ showMailItems && oneOrMore && showTemplates);
+
+ ShowMenuItem("mailContext-replySender", showMailItems && single);
+ ShowMenuItem("mailContext-replyList",
+ showMailItems && single && !isNewsgroup && IsListPost());
+ ShowMenuItem("mailContext-replyNewsgroup",
+ showMailItems && single && isNewsgroup);
+ ShowMenuItem("mailContext-replySenderAndNewsgroup",
+ showMailItems && single && isNewsgroup);
+ ShowMenuItem("mailContext-replyAll", showMailItems && single);
+ ShowMenuItem("mailContext-forward", showMailItems && single);
+ ShowMenuItem("mailContext-forwardAsAttachment",
+ showMailItems && (numSelected > 1));
+ ShowMenuItem("mailContext-copyMessageUrl",
+ showMailItems && single && isNewsgroup);
+ ShowMenuItem("mailContext-archive", showMailItems && oneOrMore &&
+ gFolderDisplay.canArchiveSelectedMessages);
+
+ // Set up the move menu. We can't move from newsgroups.
+ // Disable move if we can't delete message(s) from this folder.
+ var msgFolder = GetLoadedMsgFolder();
+ ShowMenuItem("mailContext-moveMenu",
+ showMailItems && oneOrMore && !isNewsgroup);
+ EnableMenuItem("mailContext-moveMenu",
+ oneOrMore && msgFolder && msgFolder.canDeleteMessages);
+
+ // Copy is available as long as something is selected.
+ var canCopy = showMailItems && oneOrMore && (!gMessageDisplay.isDummy ||
+ window.arguments[0].scheme == "file");
+ ShowMenuItem("mailContext-copyMenu", canCopy);
+ ShowMenuItem("mailContext-tags", showMailItems && oneOrMore);
+ ShowMenuItem("mailContext-mark", showMailItems && oneOrMore);
+ ShowMenuItem("mailContext-saveAs", showMailItems && oneOrMore);
+ ShowMenuItem("mailContext-printpreview", showMailItems && single);
+
+ ShowMenuItem("mailContext-print", showMailItems);
+ EnableMenuItem("mailContext-print", oneOrMore);
+ ShowMenuItem("mailContext-delete", showMailItems);
+ EnableMenuItem("mailContext-delete", oneOrMore);
+ // This function is needed for the case where a folder is just loaded
+ // (while there isn't a message loaded in the message pane), a right-click
+ // is done in the thread pane. This function will disable enable the
+ // 'Delete Message' menu item.
+ goUpdateCommand('cmd_delete');
+
+ ShowMenuItem("context-addemail", gContextMenu.onMailtoLink);
+ ShowMenuItem("context-composeemailto", gContextMenu.onMailtoLink);
+ ShowMenuItem("context-createfilterfrom", gContextMenu.onMailtoLink);
+
+ // Figure out separators.
+ initSeparators();
+
+ return true;
+}
+
+/**
+ * Hide separators with no active menu items.
+ *
+ */
+function initSeparators() {
+ const mailContextSeparators = [
+ "mailContext-sep-link", "mailContext-sep-open",
+ "mailContext-sep-tags", "mailContext-sep-mark",
+ "mailContext-sep-move", "mailContext-sep-print",
+ "mailContext-sep-edit", "mailContext-sep-image",
+ "mailContext-sep-blockimage", "mailContext-sep-copy",
+ ];
+
+ mailContextSeparators.forEach(hideIfAppropriate);
+}
+
+/**
+ * Hide a separator based on whether there are any non-hidden items between
+ * it and the previous separator.
+ *
+ * @param aID The id of the separator element.
+ */
+function hideIfAppropriate(aID) {
+ let separator = document.getElementById(aID);
+
+ function hasAVisibleNextSibling(aNode) {
+ let sibling = aNode.nextSibling;
+ while (sibling) {
+ if (sibling.getAttribute("hidden") != "true" &&
+ sibling.localName != "menuseparator")
+ return true;
+ sibling = sibling.nextSibling;
+ }
+ return false;
+ }
+
+ let sibling = separator.previousSibling;
+ while (sibling) {
+ if (sibling.getAttribute("hidden") != "true") {
+ ShowMenuItem(aID, sibling.localName != "menuseparator" &&
+ hasAVisibleNextSibling(separator));
+ return;
+ }
+ sibling = sibling.previousSibling;
+ }
+ ShowMenuItem(aID, false);
+}
+
+function FolderPaneOnPopupHiding()
+{
+ RestoreSelectionWithoutContentLoad(document.getElementById("folderTree"));
+}
+
+function FillFolderPaneContextMenu()
+{
+ // Do not show menu if rows are selected.
+ let folders = gFolderTreeView.getSelectedFolders();
+ let numSelected = folders.length;
+ if (!numSelected)
+ return false;
+
+ function checkIsVirtualFolder(folder) {
+ return folder.getFlag(Ci.nsMsgFolderFlags.Virtual);
+ }
+ let haveAnyVirtualFolders = folders.some(checkIsVirtualFolder);
+
+ function checkIsServer(folder) {
+ return folder.isServer;
+ }
+ let selectedServers = folders.filter(checkIsServer);
+
+ let folder = folders[0];
+ let isServer = folder.isServer;
+ let serverType = folder.server.type;
+ let specialFolder = haveAnyVirtualFolders ? "Virtual" :
+ FolderUtils.getSpecialFolderString(folder);
+
+ function checkCanSubscribeToFolder(folder) {
+ if (checkIsVirtualFolder(folder))
+ return false;
+
+ // All feed account folders, besides Trash, are subscribable.
+ if (folder.server.type == "rss" &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Trash))
+ return true;
+
+ // We only want the subscribe item on the account nodes.
+ if (!folder.isServer)
+ return false;
+
+ return folder.server.type == "nntp" ||
+ folder.server.type == "imap";
+ }
+ let haveOnlySubscribableFolders = folders.every(checkCanSubscribeToFolder);
+
+ function checkIsNewsgroup(folder) {
+ return !folder.isServer && folder.server.type == "nntp" &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Virtual);
+ }
+ let haveOnlyNewsgroups = folders.every(checkIsNewsgroup);
+
+ function checkIsMailFolder(folder) {
+ return !folder.isServer && folder.server.type != "nntp";
+ }
+ let haveOnlyMailFolders = folders.every(checkIsMailFolder);
+
+ function checkCanGetMessages(folder) {
+ return (folder.isServer && (folder.server.type != "none")) ||
+ checkIsNewsgroup(folder) ||
+ ((folder.server.type == "rss") &&
+ !folder.isSpecialFolder(Ci.nsMsgFolderFlags.Trash, true) &&
+ !checkIsVirtualFolder(folder));
+ }
+ let selectedFoldersThatCanGetMessages = folders.filter(checkCanGetMessages);
+
+ // --- Set up folder properties / account settings menu item.
+ if (numSelected != 1) {
+ ShowMenuItem("folderPaneContext-settings", false);
+ ShowMenuItem("folderPaneContext-properties", false);
+ }
+ else if (selectedServers.length != 1) {
+ ShowMenuItem("folderPaneContext-settings", false);
+ ShowMenuItem("folderPaneContext-properties", true);
+ }
+ else {
+ ShowMenuItem("folderPaneContext-properties", false);
+ ShowMenuItem("folderPaneContext-settings", true);
+ }
+
+ // --- Set up the get messages menu item.
+ // Show if only servers, or it's only newsgroups/feeds. We could mix,
+ // but it gets messy for situations where both server and a folder
+ // on the server are selected.
+ let showGet = selectedFoldersThatCanGetMessages.length == numSelected;
+ ShowMenuItem("folderPaneContext-getMessages", showGet);
+ if (showGet) {
+ if (selectedServers.length > 0 &&
+ selectedServers.length == selectedFoldersThatCanGetMessages.length) {
+ SetMenuItemLabel("folderPaneContext-getMessages",
+ gMessengerBundle.getString("getMessagesFor"));
+ }
+ else {
+ SetMenuItemLabel("folderPaneContext-getMessages",
+ gMessengerBundle.getString("getMessages"));
+ }
+ }
+
+ // --- Setup the Mark All Folders Read menu item.
+ // Show only in case the server item is selected.
+ ShowMenuItem("folderPaneContext-markAllFoldersRead",
+ selectedServers.length > 0);
+
+ // --- Set up new sub/folder menu item.
+ let isInbox = specialFolder == "Inbox";
+ let showNew =
+ (numSelected == 1) &&
+ ((serverType != "nntp" && folder.canCreateSubfolders) || isInbox);
+ ShowMenuItem("folderPaneContext-new", showNew);
+ if (showNew) {
+ EnableMenuItem("folderPaneContext-new",
+ serverType != "imap" || !Services.io.offline);
+ let label = (isServer || isInbox) ? "newFolder" : "newSubfolder";
+ SetMenuItemLabel("folderPaneContext-new",
+ gMessengerBundle.getString(label));
+ }
+
+ // --- Set up rename menu item.
+ let canRename = (numSelected == 1) && !isServer && folder.canRename &&
+ (specialFolder == "none" || specialFolder == "Virtual" ||
+ (specialFolder == "Junk" &&
+ CanRenameDeleteJunkMail(folder.URI)));
+ ShowMenuItem("folderPaneContext-rename", canRename);
+ if (canRename) {
+ EnableMenuItem("folderPaneContext-rename",
+ !isServer && folder.isCommandEnabled("cmd_renameFolder"));
+ SetMenuItemLabel("folderPaneContext-rename",
+ gMessengerBundle.getString("renameFolder"));
+ }
+
+ // --- Set up the delete folder menu item.
+ function checkCanDeleteFolder(folder) {
+ if (folder.isSpecialFolder(Ci.nsMsgFolderFlags.Junk, false))
+ return CanRenameDeleteJunkMail(folder.URI);
+ return folder.deletable;
+ }
+ let haveOnlyDeletableFolders = folders.every(checkCanDeleteFolder);
+ ShowMenuItem("folderPaneContext-remove",
+ haveOnlyDeletableFolders && numSelected == 1);
+ if (haveOnlyDeletableFolders && numSelected == 1)
+ SetMenuItemLabel("folderPaneContext-remove",
+ gMessengerBundle.getString("removeFolder"));
+
+ function checkIsDeleteEnabled(folder) {
+ return folder.isCommandEnabled("cmd_delete");
+ }
+ let haveOnlyDeleteEnabledFolders = folders.every(checkIsDeleteEnabled);
+ EnableMenuItem("folderPaneContext-remove", haveOnlyDeleteEnabledFolders);
+
+ // --- Set up the compact folder menu item.
+ function checkCanCompactFolder(folder) {
+ return folder.canCompact &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Virtual) &&
+ folder.isCommandEnabled("cmd_compactFolder");
+ }
+ let haveOnlyCompactableFolders = folders.every(checkCanCompactFolder);
+ ShowMenuItem("folderPaneContext-compact", haveOnlyCompactableFolders);
+ if (haveOnlyCompactableFolders)
+ SetMenuItemLabel("folderPaneContext-compact",
+ PluralForm.get(numSelected, gMessengerBundle.getString("compactFolders")));
+
+ function checkIsCompactEnabled(folder) {
+ return folder.isCommandEnabled("cmd_compactFolder");
+ }
+ let haveOnlyCompactEnabledFolders = folders.every(checkIsCompactEnabled);
+ EnableMenuItem("folderPaneContext-compact", haveOnlyCompactEnabledFolders);
+
+ // --- Set up favorite folder menu item.
+ let showFavorite = (numSelected == 1) && !isServer;
+ ShowMenuItem("folderPaneContext-favoriteFolder", showFavorite);
+ if (showFavorite) {
+ // Adjust the checked state on the menu item.
+ document.getElementById("folderPaneContext-favoriteFolder")
+ .setAttribute("checked",
+ folder.getFlag(Ci.nsMsgFolderFlags.Favorite));
+ }
+
+ // --- Set up the empty trash menu item.
+ ShowMenuItem("folderPaneContext-emptyTrash",
+ numSelected == 1 && specialFolder == "Trash");
+
+ // --- Set up the empty junk menu item.
+ ShowMenuItem("folderPaneContext-emptyJunk",
+ numSelected == 1 && specialFolder == "Junk");
+
+ // --- Set up the send unsent messages menu item.
+ let showSendUnsentMessages = numSelected == 1 && specialFolder == "Outbox";
+ ShowMenuItem("folderPaneContext-sendUnsentMessages", showSendUnsentMessages);
+ if (showSendUnsentMessages)
+ EnableMenuItem("folderPaneContext-sendUnsentMessages",
+ IsSendUnsentMsgsEnabled(folder));
+
+ // --- Set up the subscribe menu item.
+ ShowMenuItem("folderPaneContext-subscribe",
+ numSelected == 1 && haveOnlySubscribableFolders);
+
+ // --- Set up the unsubscribe menu item.
+ ShowMenuItem("folderPaneContext-newsUnsubscribe", haveOnlyNewsgroups);
+
+ // --- Set up the mark newsgroup/s read menu item.
+ ShowMenuItem("folderPaneContext-markNewsgroupAllRead", haveOnlyNewsgroups);
+ SetMenuItemLabel("folderPaneContext-markNewsgroupAllRead",
+ PluralForm.get(numSelected, gMessengerBundle.getString("markNewsgroupRead")));
+
+ // --- Set up the mark folder/s read menu item.
+ ShowMenuItem("folderPaneContext-markMailFolderAllRead",
+ haveOnlyMailFolders && !haveAnyVirtualFolders);
+ SetMenuItemLabel("folderPaneContext-markMailFolderAllRead",
+ PluralForm.get(numSelected, gMessengerBundle.getString("markFolderRead")));
+
+ // Set up the search menu item.
+ ShowMenuItem("folderPaneContext-searchMessages",
+ numSelected == 1 && !haveAnyVirtualFolders);
+ goUpdateCommand('cmd_search');
+
+ ShowMenuItem("folderPaneContext-openNewWindow", numSelected == 1);
+ ShowMenuItem("folderPaneContext-openNewTab", numSelected == 1);
+
+ // Hide / Show our menu separators based on the menu items we are showing.
+ hideIfAppropriate("folderPaneContext-sep1");
+ hideIfAppropriate("folderPaneContext-sep-edit");
+ hideIfAppropriate("folderPaneContext-sep4");
+
+ return true;
+}
+
+function ShowMenuItem(id, showItem)
+{
+ var item = document.getElementById(id);
+ if(item && item.hidden != "true")
+ item.hidden = !showItem;
+}
+
+function EnableMenuItem(id, enableItem)
+{
+ var item = document.getElementById(id);
+ if(item)
+ {
+ var enabled = (item.getAttribute('disabled') !='true');
+ if(enableItem != enabled)
+ {
+ item.setAttribute('disabled', enableItem ? '' : 'true');
+ }
+ }
+}
+
+function SetMenuItemLabel(id, label)
+{
+ var item = document.getElementById(id);
+ if(item)
+ item.setAttribute('label', label);
+}
+
+function SetMenuItemAccessKey(id, accessKey)
+{
+ var item = document.getElementById(id);
+ if(item)
+ item.setAttribute('accesskey', accessKey);
+}
+
+// message pane context menu helper methods
+function AddContact(aEmailAddressNode)
+{
+ if (aEmailAddressNode)
+ AddEmailToAddressBook(aEmailAddressNode.getAttribute("emailAddress"),
+ aEmailAddressNode.getAttribute("displayName"));
+}
+
+function AddEmailToAddressBook(primaryEmail, displayName)
+{
+ window.openDialog("chrome://messenger/content/addressbook/abNewCardDialog.xul",
+ "", "chrome,resizable=no,titlebar,modal,centerscreen",
+ {primaryEmail:primaryEmail, displayName:displayName});
+}
+
+function EditContact(aEmailAddressNode)
+{
+ if (aEmailAddressNode.cardDetails.card)
+ {
+ window.openDialog("chrome://messenger/content/addressbook/abEditCardDialog.xul",
+ "", "chrome,resizable=no,modal,titlebar,centerscreen",
+ { abURI: aEmailAddressNode.cardDetails.book.URI,
+ card: aEmailAddressNode.cardDetails.card });
+ }
+}
+
+/**
+ * SendMailToNode takes the email address title button, extracts the email address
+ * we stored in there and opens a compose window with that address.
+ *
+ * @param addressNode a node which has a "fullAddress" attribute
+ * @param aEvent the event object when user triggers the menuitem
+ */
+function SendMailToNode(emailAddressNode, aEvent)
+{
+ if (emailAddressNode)
+ SendMailTo(emailAddressNode.getAttribute("fullAddress"), aEvent);
+}
+
+function SendMailTo(fullAddress, aEvent)
+{
+ var fields = Cc["@mozilla.org/messengercompose/composefields;1"]
+ .createInstance(Ci.nsIMsgCompFields);
+ var params = Cc["@mozilla.org/messengercompose/composeparams;1"]
+ .createInstance(Ci.nsIMsgComposeParams);
+
+ var headerParser = MailServices.headerParser;
+ var addresses = headerParser.makeFromDisplayAddress(fullAddress);
+ fields.to = headerParser.makeMimeHeader([addresses[0]]);
+ params.type = Ci.nsIMsgCompType.New;
+
+ // If aEvent is passed, check if Shift key was pressed for composition in
+ // non-default format (HTML vs. plaintext).
+ params.format = (aEvent && aEvent.shiftKey) ?
+ Ci.nsIMsgCompFormat.OppositeOfDefault :
+ Ci.nsIMsgCompFormat.Default;
+
+ params.identity = accountManager.getFirstIdentityForServer(GetLoadedMsgFolder().server);
+ params.composeFields = fields;
+ MailServices.compose.OpenComposeWindowWithParams(null, params);
+}
+
+/**
+ * Takes the email address, extracts the address/name
+ * we stored in there and copies it to the clipboard.
+ *
+ * @param addressNode a node which has an "emailAddress"
+ * attribute
+ * @param aIncludeName when true, also copy the name onto the clipboard,
+ * otherwise only the email address
+ */
+function CopyEmailAddress(emailAddressNode, aIncludeName = false)
+{
+ if (emailAddressNode) {
+ let address = emailAddressNode.getAttribute(aIncludeName ? "fullAddress"
+ : "emailAddress");
+ CopyString(address);
+ }
+}
+
+// show the message id in the context menu
+function FillMessageIdContextMenu(messageIdNode)
+{
+ var msgId = messageIdNode.getAttribute("messageid");
+ document.getElementById("messageIdContext-messageIdTarget")
+ .setAttribute("label", msgId);
+
+ // We don't want to show "Open Message For ID" for the same message
+ // we're viewing.
+ var currentMsgId = "<" + gFolderDisplay.selectedMessage.messageId + ">";
+ document.getElementById("messageIdContext-openMessageForMsgId")
+ .hidden = (currentMsgId == msgId);
+
+ // We don't want to show "Open Browser With Message-ID" for non-nntp messages.
+ document.getElementById("messageIdContext-openBrowserWithMsgId")
+ .hidden = !gFolderDisplay.selectedMessageIsNews;
+}
+
+function GetMessageIdFromNode(messageIdNode, cleanMessageId)
+{
+ var messageId = messageIdNode.getAttribute("messageid");
+
+ // remove < and >
+ if (cleanMessageId)
+ messageId = messageId.substring(1, messageId.length - 1);
+
+ return messageId;
+}
+
+// take the message id from the messageIdNode and use the
+// url defined in the hidden pref "mailnews.messageid_browser.url"
+// to open it in a browser window (%mid is replaced by the message id)
+function OpenBrowserWithMessageId(messageId)
+{
+ var browserURL = GetLocalizedStringPref("mailnews.messageid_browser.url");
+ if (browserURL)
+ openAsExternal(browserURL.replace(/%mid/, messageId));
+}
+
+// take the message id from the messageIdNode, search for the
+// corresponding message in all folders starting with the current
+// selected folder, then the current account followed by the other
+// accounts and open corresponding message if found
+function OpenMessageForMessageId(messageId)
+{
+ var startServer = gDBView.msgFolder.server;
+ var messageHeader;
+
+ window.setCursor("wait");
+
+ // first search in current folder for message id
+ var messageHeader = CheckForMessageIdInFolder(gDBView.msgFolder, messageId);
+
+ // if message id not found in current folder search in all folders
+ if (!messageHeader)
+ {
+ messageHeader = SearchForMessageIdInSubFolder(startServer.rootFolder, messageId);
+
+ for (let currentServer of MailServices.accounts.allServers)
+ {
+ if (currentServer && startServer != currentServer &&
+ currentServer.canSearchMessages && !currentServer.isDeferredTo)
+ {
+ messageHeader = SearchForMessageIdInSubFolder(currentServer.rootFolder, messageId);
+ }
+ }
+ }
+ window.setCursor("auto");
+
+ // if message id was found open corresponding message
+ // else show error message
+ if (messageHeader)
+ OpenMessageByHeader(messageHeader, Services.prefs.getBoolPref("mailnews.messageid.openInNewWindow"));
+ else
+ {
+ var messageIdStr = "<" + messageId + ">";
+ var errorTitle = gMessengerBundle.getString("errorOpenMessageForMessageIdTitle");
+ var errorMessage = gMessengerBundle.getFormattedString("errorOpenMessageForMessageIdMessage",
+ [messageIdStr]);
+ Services.prompt.alert(window, errorTitle, errorMessage);
+ }
+}
+
+function OpenMessageByHeader(messageHeader, openInNewWindow)
+{
+ var folder = messageHeader.folder;
+ var folderURI = folder.URI;
+
+ if (openInNewWindow)
+ {
+ var messageURI = folder.getUriForMsg(messageHeader);
+
+ window.openDialog("chrome://messenger/content/messageWindow.xul",
+ "_blank", "all,chrome,dialog=no,status,toolbar",
+ messageURI, folderURI, null);
+ }
+ else
+ {
+ if (msgWindow.openFolder != folderURI)
+ gFolderTreeView.selectFolder(folder)
+
+ var tree = null;
+ var wintype = document.documentElement.getAttribute('windowtype');
+ if (wintype != "mail:messageWindow")
+ {
+ tree = GetThreadTree();
+ tree.view.selection.clearSelection();
+ }
+
+ try
+ {
+ gDBView.selectMsgByKey(messageHeader.messageKey);
+ }
+ catch(e)
+ { // message not in the thread pane
+ try
+ {
+ goDoCommand("cmd_viewAllMsgs");
+ gDBView.selectMsgByKey(messageHeader.messageKey);
+ }
+ catch(e)
+ {
+ dump("select messagekey " + messageHeader.messageKey +
+ " failed in folder " + folder.URI);
+ }
+ }
+
+ if (tree && tree.currentIndex != -1)
+ tree.treeBoxObject.ensureRowIsVisible(tree.currentIndex);
+ }
+}
+
+// search for message by message id in given folder and its subfolders
+// return message header if message was found
+function SearchForMessageIdInSubFolder(folder, messageId)
+{
+ var messageHeader;
+
+ // search in folder
+ if (!folder.isServer)
+ messageHeader = CheckForMessageIdInFolder(folder, messageId);
+
+ // search subfolders recursively
+ for (let currentFolder of folder.subFolders) {
+ // search in current folder
+ messageHeader = CheckForMessageIdInFolder(currentFolder, messageId);
+
+ // search in its subfolder
+ if (!messageHeader && currentFolder.hasSubFolders)
+ messageHeader = SearchForMessageIdInSubFolder(currentFolder, messageId);
+ }
+
+ return messageHeader;
+}
+
+// check folder for corresponding message to given message id
+// return message header if message was found
+function CheckForMessageIdInFolder(folder, messageId)
+{
+ var messageDatabase = folder.msgDatabase;
+ var messageHeader;
+
+ try
+ {
+ messageHeader = messageDatabase.getMsgHdrForMessageID(messageId);
+ }
+ catch (ex)
+ {
+ dump("Failed to find message-id in folder!");
+ }
+
+ if (!MailServices.mailSession.IsFolderOpenInWindow(folder) &&
+ !folder.getFlag(Ci.nsMsgFolderFlags.Trash | Ci.nsMsgFolderFlags.Inbox))
+ {
+ folder.msgDatabase = null;
+ }
+
+ return messageHeader;
+}
+
+// CreateFilter opens the Message Filters and Filter Rules dialogs.
+//The Filter Rules dialog has focus. The window is prefilled with filtername <email address>
+//Sender condition is selected and the value is prefilled <email address>
+function CreateFilter(emailAddressNode)
+{
+ if (emailAddressNode)
+ CreateFilterFromMail(emailAddressNode.getAttribute("emailAddress"));
+}
+
+function CreateFilterFromMail(emailAddress)
+{
+ if (emailAddress)
+ top.MsgFilters(emailAddress, GetFirstSelectedMsgFolder());
+}
+
+function CopyMessageUrl()
+{
+ try
+ {
+ var hdr = gDBView.hdrForFirstSelectedMessage;
+ var server = hdr.folder.server;
+
+ // TODO let backend construct URL and return as attribute
+ var url = (server.socketType == Ci.nsMsgSocketType.SSL) ?
+ "snews://" : "news://";
+ url += server.hostName + ":" + server.port + "/" + hdr.messageId;
+ CopyString(url);
+ }
+ catch (ex)
+ {
+ dump("ex="+ex+"\n");
+ }
+}
+
+function CopyString(aString)
+{
+ Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(aString);
+}
diff --git a/comm/suite/mailnews/content/mailEditorOverlay.xul b/comm/suite/mailnews/content/mailEditorOverlay.xul
new file mode 100644
index 0000000000..7eb6d651ad
--- /dev/null
+++ b/comm/suite/mailnews/content/mailEditorOverlay.xul
@@ -0,0 +1,61 @@
+<?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/. -->
+
+
+<!-- retrieve generic commands -->
+<?xul-overlay href="chrome://messenger/content/mailOverlay.xul"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/mailEditorOverlay.dtd" >
+
+<overlay id="mailEditorOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script>
+ <![CDATA[
+
+ function openComposeWindow(pageUrl, pageTitle)
+ {
+ var params = Cc["@mozilla.org/messengercompose/composeparams;1"].createInstance(Ci.nsIMsgComposeParams);
+ if (params)
+ {
+ params.composeFields = Cc['@mozilla.org/messengercompose/composefields;1'].createInstance(Ci.nsIMsgCompFields);
+ if (params.composeFields)
+ {
+ params.composeFields.body = pageUrl;
+ params.composeFields.subject = pageTitle;
+ var attachmentData = Cc["@mozilla.org/messengercompose/attachment;1"].createInstance(Ci.nsIMsgAttachment);
+ if (attachmentData)
+ {
+ attachmentData.url = pageUrl;
+ params.composeFields.addAttachment(attachmentData);
+ }
+ params.bodyIsLink = true;
+
+ var composeService = Cc["@mozilla.org/messengercompose;1"].getService(Ci.nsIMsgComposeService);
+ if (composeService)
+ composeService.OpenComposeWindowWithParams(null, params);
+ }
+ }
+ }
+
+ ]]>
+ </script>
+
+ <!-- editor specific UI items -->
+ <menupopup id="menu_NewPopup">
+ <!-- Command nodes and implemention are in mailOverlay.xul -->
+ <menuitem id="menu_newMessage" insertafter="menu_newPrivateWindow"/>
+ <menuitem id="menu_newCard" insertafter="menu_newPrivateWindow"/>
+ </menupopup>
+
+ <menupopup id="menu_FilePopup">
+ <!-- The command node cmd_editSendPage is in editor.xul.
+ Implementation is in ComposerCommands.js
+ -->
+ <menuitem id="menu_sendPage" label="&sendPage.label;" accesskey="&sendPage.accesskey;" observes="cmd_editSendPage" insertafter="previewInBrowser"/>
+ </menupopup>
+
+</overlay>
+
diff --git a/comm/suite/mailnews/content/mailKeysOverlay.xul b/comm/suite/mailnews/content/mailKeysOverlay.xul
new file mode 100644
index 0000000000..e89cee3968
--- /dev/null
+++ b/comm/suite/mailnews/content/mailKeysOverlay.xul
@@ -0,0 +1,64 @@
+<?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 SYSTEM "chrome://messenger/locale/mailKeysOverlay.dtd">
+
+<overlay id="mailKeysOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <keyset id="mailKeys">
+ <key id="key_delete"/>
+ <key id="key_delete2"/> <!-- secondary delete key -->
+#ifdef XP_MACOSX
+ <!-- not all Mac keyboards have a VK_DELETE key, so we use VK_BACK as
+ the primary and provide VK_DELETE as a secondary key definition -->
+ <key id="key_shiftDelete" keycode="VK_BACK"
+ modifiers="shift" command="cmd_shiftDelete"/>
+ <key id="key_shiftDelete2" keycode="VK_DELETE"
+ modifiers="shift" command="cmd_shiftDelete"/>
+#else
+ <key id="key_shiftDelete" keycode="VK_DELETE"
+ modifiers="shift" command="cmd_shiftDelete"/>
+#endif
+ <key id="key_selectAll"/>
+
+ <key id="key_markAsRead"
+ key="&markAsReadCmd.key;"
+ oncommand="goDoCommand('cmd_markAsRead');"/>
+ <key id="key_markAsUnread"
+ key="&markAsUnreadCmd2.key;"
+ oncommand="goDoCommand('cmd_markAsUnread');"/>
+ <key id="key_toggleFlagged" key="&markFlaggedCmd.key;"
+ oncommand="goDoCommand('cmd_markAsFlagged');"/>
+ <key id="key_openMessage" key="&openMessageWindowCmd.key;"
+ modifiers="accel" oncommand="goDoCommand('cmd_openMessage');"/>
+
+ <!-- Tag Keys -->
+ <!-- Includes both shifted and not, for Azerty and other layouts where the
+ numeric keys are shifted. -->
+ <key id="key_tag0" key="&tagCmd0.key;" modifiers="shift any"
+ oncommand="RemoveAllMessageTags();"/>
+ <key id="key_tag1" key="&tagCmd1.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(1);"/>
+ <key id="key_tag2" key="&tagCmd2.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(2);"/>
+ <key id="key_tag3" key="&tagCmd3.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(3);"/>
+ <key id="key_tag4" key="&tagCmd4.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(4);"/>
+ <key id="key_tag5" key="&tagCmd5.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(5);"/>
+ <key id="key_tag6" key="&tagCmd6.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(6);"/>
+ <key id="key_tag7" key="&tagCmd7.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(7);"/>
+ <key id="key_tag8" key="&tagCmd8.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(8);"/>
+ <key id="key_tag9" key="&tagCmd9.key;" modifiers="shift any"
+ oncommand="ToggleMessageTagKey(9);"/>
+ </keyset>
+
+</overlay>
+
diff --git a/comm/suite/mailnews/content/mailOverlay.js b/comm/suite/mailnews/content/mailOverlay.js
new file mode 100644
index 0000000000..fd812eaa54
--- /dev/null
+++ b/comm/suite/mailnews/content/mailOverlay.js
@@ -0,0 +1,30 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function openNewCardDialog()
+{
+ window.openDialog("chrome://messenger/content/addressbook/abNewCardDialog.xul",
+ "", "chrome,modal,resizable=no,centerscreen");
+}
+
+function goOpenNewMessage()
+{
+ // if there is a MsgNewMessage function in scope
+ // and we should use it, so that we choose the proper
+ // identity, based on the selected message or folder
+ // if not, bring up the compose window to the default identity
+ if ("MsgNewMessage" in window)
+ {
+ MsgNewMessage(null);
+ return;
+ }
+
+ Cc["@mozilla.org/messengercompose;1"]
+ .getService(Ci.nsIMsgComposeService)
+ .OpenComposeWindow(null, null, null,
+ Ci.nsIMsgCompType.New,
+ Ci.nsIMsgCompFormat.Default,
+ null, null, null);
+}
diff --git a/comm/suite/mailnews/content/mailOverlay.xul b/comm/suite/mailnews/content/mailOverlay.xul
new file mode 100644
index 0000000000..b1379f6824
--- /dev/null
+++ b/comm/suite/mailnews/content/mailOverlay.xul
@@ -0,0 +1,29 @@
+<?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 SYSTEM "chrome://messenger/locale/mailOverlay.dtd">
+<overlay id="mailOverlay.xul"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://messenger/content/mailOverlay.js"/>
+
+ <!-- generic commands -->
+ <commandset id="tasksCommands">
+ <command id="cmd_newMessage" oncommand="goOpenNewMessage();"/>
+ <command id="cmd_newCard" oncommand="openNewCardDialog()"/>
+ </commandset>
+ <menuitem id="menu_newCard" label="&newContactCmd.label;"
+ accesskey="&newContactCmd.accesskey;" command="cmd_newCard"/>
+ <menuitem id="menu_newMessage" label="&newMessageCmd.label;" accesskey="&newMessageCmd.accesskey;" key="key_newMessage" command="cmd_newMessage"/>
+ <keyset id="tasksKeys">
+#ifdef XP_MACOSX
+ <key id="key_newMessage" key="&newMessageCmd.key;"
+ modifiers="accel,shift" command="cmd_newMessage"/>
+#else
+ <key id="key_newMessage" key="&newMessageCmd.key;"
+ modifiers="accel" command="cmd_newMessage"/>
+#endif
+ </keyset>
+</overlay>
diff --git a/comm/suite/mailnews/content/mailTasksOverlay.js b/comm/suite/mailnews/content/mailTasksOverlay.js
new file mode 100644
index 0000000000..2033eb0651
--- /dev/null
+++ b/comm/suite/mailnews/content/mailTasksOverlay.js
@@ -0,0 +1,250 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+// biff observer topic
+const BIFF_TOPIC = "mail:biff-state-changed";
+
+// biff state constants used by themes
+const BIFF_STATE_MESSAGES = "NewMail";
+const BIFF_STATE_NOMESSAGES = "NoMail";
+const BIFF_STATE_UNKNOWN = "UnknownMail";
+
+
+// uses "toOpenWindowByType" function provided by tasksOverlay.js
+// which is included by most clients.
+function toMessengerWindow()
+{
+ toOpenWindowByType("mail:3pane", "chrome://messenger/content/");
+}
+
+function toAddressBook()
+{
+ toOpenWindowByType("mail:addressbook",
+ "chrome://messenger/content/addressbook/addressbook.xul");
+}
+
+function toNewsgroups()
+{
+ dump("Sorry, command not implemented.\n");
+}
+
+function toImport()
+{
+ window.openDialog("chrome://messenger/content/importDialog.xul",
+ "importDialog",
+ "chrome, modal, titlebar, centerscreen");
+}
+
+function CoalesceGetMsgsForPop3ServersByDestFolder(aCurrentServer,
+ aPOP3DownloadServersArray,
+ aLocalFoldersToDownloadTo)
+{
+ // coalesce the servers that download into the same folder...
+ var inbox = aCurrentServer.rootMsgFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Inbox);
+ var index = aLocalFoldersToDownloadTo.indexOf(inbox);
+ if (index == -1)
+ {
+ inbox.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NoMail;
+ inbox.clearNewMessages();
+ aLocalFoldersToDownloadTo.push(inbox);
+ index = aPOP3DownloadServersArray.length;
+ aPOP3DownloadServersArray.push([]);
+ }
+ aPOP3DownloadServersArray[index].push(aCurrentServer);
+}
+
+function MailTasksGetMessagesForAllServers(aBiff, aMsgWindow, aDefaultServer)
+{
+ // now log into any server
+ try
+ {
+ // array of array of servers for a particular folder
+ var pop3DownloadServersArray = [];
+ // parallel array of folders to download to...
+ var localFoldersToDownloadTo = [];
+ var pop3Server = null;
+ for (let currentServer of MailServices.accounts.allServers)
+ {
+ if (currentServer)
+ {
+ if (aBiff)
+ {
+ if (currentServer.protocolInfo.canLoginAtStartUp &&
+ currentServer.loginAtStartUp)
+ {
+ if (aDefaultServer &&
+ aDefaultServer.equals(currentServer) &&
+ !aDefaultServer.isDeferredTo &&
+ aDefaultServer.rootFolder == aDefaultServer.rootMsgFolder)
+ {
+ dump(currentServer.serverURI + " ... skipping, already opened\n");
+ }
+ else if (currentServer.type == "pop3" && currentServer.downloadOnBiff)
+ {
+ CoalesceGetMsgsForPop3ServersByDestFolder(currentServer,
+ pop3DownloadServersArray,
+ localFoldersToDownloadTo);
+ pop3Server = currentServer;
+ }
+ else
+ {
+ // check to see if there are new messages on the server
+ currentServer.performBiff(aMsgWindow);
+ }
+ }
+ }
+ else
+ {
+ if (currentServer.protocolInfo.canGetMessages &&
+ !currentServer.passwordPromptRequired)
+ {
+ if (currentServer.type == "pop3")
+ {
+ CoalesceGetMsgsForPop3ServersByDestFolder(currentServer,
+ pop3DownloadServersArray,
+ localFoldersToDownloadTo);
+ pop3Server = currentServer;
+ }
+ else
+ {
+ // get new messages on the server for IMAP or RSS
+ GetMessagesForInboxOnServer(currentServer);
+ }
+ }
+ }
+ }
+ }
+
+ if (pop3Server instanceof Ci.nsIPop3IncomingServer)
+ {
+ for (let i = 0; i < pop3DownloadServersArray.length; ++i)
+ {
+ // any ol' pop3Server will do -
+ // the serversArray specifies which servers to download from
+ pop3Server.downloadMailFromServers(pop3DownloadServersArray[i],
+ aMsgWindow,
+ localFoldersToDownloadTo[i],
+ null);
+ }
+ }
+ }
+ catch (e)
+ {
+ Cu.reportError(e);
+ }
+}
+
+var biffObserver =
+{
+ observe: function observe(subject, topic, state)
+ {
+ // sanity check
+ if (topic == BIFF_TOPIC)
+ {
+ var biffManager = Cc["@mozilla.org/messenger/statusBarBiffManager;1"]
+ .getService(Ci.nsIStatusBarBiffManager);
+ document.getElementById("mini-mail")
+ .setAttribute("BiffState",
+ [BIFF_STATE_MESSAGES,
+ BIFF_STATE_NOMESSAGES,
+ BIFF_STATE_UNKNOWN][biffManager.biffState]);
+ }
+ }
+};
+
+function MailTasksOnLoad(aEvent)
+{
+ // Without the mini-mail icon to show the biff state, there's no need to
+ // initialize this here. We won't start with the hidden window alone,
+ // so this early return doesn't break anything.
+ var miniMail = document.getElementById("mini-mail");
+ if (!miniMail)
+ return;
+
+ // initialize biff state
+ Services.obs.addObserver(biffObserver, BIFF_TOPIC);
+ biffObserver.observe(null, BIFF_TOPIC, null); // init mini-mail icon
+ addEventListener("unload", MailTasksOnUnload, false);
+
+ // don't try to biff if offline, but do so silently
+ if (Services.io.offline)
+ return;
+
+ // Performing biff here will mean performing it for all new windows opened!
+ // This might make non-users of mailnews unhappy...
+ if (!Services.prefs.getBoolPref("mail.biff.on_new_window"))
+ return;
+
+ // The MailNews main window will perform biff later in its onload handler,
+ // so we don't need to do this here.
+ if (Services.wm.getMostRecentWindow("mail:3pane"))
+ return;
+
+ // If we already have a defined biff-state set on the mini-mail icon,
+ // we know that biff is already running.
+ const kBiffState = Cc["@mozilla.org/messenger/statusBarBiffManager;1"]
+ .getService(Ci.nsIStatusBarBiffManager)
+ .biffState;
+ if (kBiffState != Ci.nsIMsgFolder.nsMsgBiffState_Unknown)
+ return;
+
+ // still no excuse to refuse to use this ruse
+ MailTasksGetMessagesForAllServers(true, null, null);
+}
+
+function MailTasksOnUnload(aEvent)
+{
+ Services.obs.removeObserver(biffObserver, BIFF_TOPIC);
+}
+
+/**
+ * This class implements nsIBadCertListener2. Its job is to prevent "bad cert"
+ * security dialogs from being shown to the user. Currently it puts up the
+ * cert override dialog, though we'd like to give the user more detailed
+ * information in the future.
+ */
+function nsMsgBadCertHandler() {
+}
+
+nsMsgBadCertHandler.prototype = {
+ // Suppress any certificate errors
+ notifyCertProblem: function(socketInfo, status, targetSite) {
+ if (!status)
+ return true;
+
+ setTimeout(InformUserOfCertError, 0, status, targetSite);
+ return true;
+ },
+
+ // nsIInterfaceRequestor
+ getInterface: function(iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsISupports
+ QueryInterface: function(iid) {
+ if (!iid.equals(Ci.nsIBadCertListener2) &&
+ !iid.equals(Ci.nsIInterfaceRequestor) &&
+ !iid.equals(Ci.nsISupports))
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ return this;
+ }
+};
+
+function InformUserOfCertError(status, targetSite)
+{
+ var params = { exceptionAdded : false,
+ sslStatus : status,
+ prefetchCert : true,
+ location : targetSite };
+ window.openDialog('chrome://pippki/content/exceptionDialog.xul',
+ '','chrome,centerscreen,modal', params);
+}
+
+addEventListener("load", MailTasksOnLoad, false);
diff --git a/comm/suite/mailnews/content/mailTasksOverlay.xul b/comm/suite/mailnews/content/mailTasksOverlay.xul
new file mode 100644
index 0000000000..9a208bb750
--- /dev/null
+++ b/comm/suite/mailnews/content/mailTasksOverlay.xul
@@ -0,0 +1,64 @@
+<?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 SYSTEM "chrome://messenger/locale/mailTasksOverlay.dtd">
+
+<overlay id="mailTasksOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://messenger/content/mailTasksOverlay.js"/>
+
+ <keyset id="tasksKeys">
+ <key id="key_mail"
+ command="Tasks:Mail"
+ key="&messengerCmd.commandkey;"
+ modifiers="accel"/>
+ <key id="key_addressbook"
+ command="Tasks:AddressBook"
+ key="&addressBookCmd.commandkey;"
+ modifiers="accel"/>
+ </keyset>
+
+ <commandset id="tasksCommands">
+ <command id="Tasks:Mail" oncommand="toMessengerWindow();"/>
+ <command id="Tasks:AddressBook" oncommand="toAddressBook();"/>
+ </commandset>
+
+ <statusbarpanel id="component-bar">
+ <toolbarbutton id="mini-mail"
+ class="taskbutton"
+ oncommand="toMessengerWindow()"
+ position="2"
+ tooltiptext="&taskMessenger.tooltip;"/>
+ <toolbarbutton id="mini-comp"
+ insertafter="mini-mail"/>
+ <toolbarbutton id="mini-addr"
+ class="taskbutton"
+ oncommand="toAddressBook();"
+ insertafter="mini-comp"
+ tooltiptext="&taskAddressBook.tooltip;"/>
+ </statusbarpanel>
+
+ <menupopup id="windowPopup">
+ <menuitem id="tasksMenuMail"
+ class="menuitem-iconic icon-mail16 menu-iconic"
+ label="&messengerCmd.label;"
+ accesskey="&messengerCmd.accesskey;"
+ key="key_mail"
+ command="Tasks:Mail"
+ insertafter="tasksMenuNavigator"/>
+ <menuitem id="tasksMenuEditor"
+ insertafter="tasksMenuMail"/>
+ <menuitem id="tasksMenuAddressBook"
+ class="menuitem-iconic icon-addressbook16 menu-iconic"
+ label="&addressBookCmd.label;"
+ accesskey="&addressBookCmd.accesskey;"
+ key="key_addressbook"
+ command="Tasks:AddressBook"
+ insertafter="tasksMenuEditor"/>
+ </menupopup>
+
+</overlay>
diff --git a/comm/suite/mailnews/content/mailViewList.js b/comm/suite/mailnews/content/mailViewList.js
new file mode 100644
index 0000000000..ef9589ba74
--- /dev/null
+++ b/comm/suite/mailnews/content/mailViewList.js
@@ -0,0 +1,161 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gMailListView;
+var gListBox;
+var gEditButton;
+var gDeleteButton;
+var gMailViewListController =
+{
+ supportsCommand: function(aCommand)
+ {
+ switch (aCommand)
+ {
+ case "cmd_new":
+ case "cmd_edit":
+ case "cmd_delete":
+ return true;
+ }
+ return false;
+ },
+
+ isCommandEnabled: function(aCommand)
+ {
+ switch (aCommand)
+ {
+ case "cmd_new":
+ return true;
+ case "cmd_edit":
+ case "cmd_delete":
+ return gListBox.selectedIndex >= 0;
+ }
+ return false;
+ },
+
+ doCommand: function(aCommand)
+ {
+ switch (aCommand)
+ {
+ case "cmd_new":
+ OnNewMailView();
+ break;
+ case "cmd_edit":
+ OnEditMailView();
+ break;
+ case "cmd_delete":
+ OnDeleteMailView();
+ break;
+ }
+ },
+
+ onEvent: function(aEvent) {},
+
+ onCommandUpdate: function()
+ {
+ for (let command of ["cmd_new", "cmd_edit", "cmd_delete"])
+ goUpdateCommand(command);
+ }
+};
+
+function MailViewListOnLoad()
+{
+ gMailListView = Cc["@mozilla.org/messenger/mailviewlist;1"]
+ .getService(Ci.nsIMsgMailViewList);
+ gListBox = document.getElementById('mailViewList');
+
+ window.controllers.insertControllerAt(0, gMailViewListController);
+
+ // Construct list view based on current mail view list data
+ RefreshListView(null);
+ gEditButton = document.getElementById('editButton');
+ gDeleteButton = document.getElementById('deleteButton');
+}
+
+function MailViewListOnUnload()
+{
+ window.controllers.removeController(gMailViewListController);
+}
+
+function RefreshListView(aSelectedMailView)
+{
+ // remove any existing items in the view...
+ for (let index = gListBox.getRowCount(); index > 0; index--)
+ gListBox.getItemAtIndex(index - 1).remove();
+
+ var numItems = gMailListView.mailViewCount;
+ for (let index = 0; index < numItems; index++)
+ {
+ let mailView = gMailListView.getMailViewAt(index);
+ gListBox.appendItem(mailView.prettyName, index);
+ if (aSelectedMailView && (mailView.prettyName == aSelectedMailView.prettyName))
+ gListBox.selectedIndex = index;
+ }
+}
+
+function OnNewMailView()
+{
+ window.openDialog('chrome://messenger/content/mailViewSetup.xul',
+ '',
+ 'centerscreen,resizable,modal,titlebar,chrome',
+ {onOkCallback: RefreshListView});
+}
+
+function OnDeleteMailView()
+{
+ let bundle = Services.strings.createBundle("chrome://messenger/locale/messenger.properties");
+
+ let ps = Services.prompt;
+ if (!ps.confirm(window, bundle.GetStringFromName("confirmViewDeleteTitle"),
+ bundle.GetStringFromName("confirmViewDeleteMessage")))
+ return;
+
+ // get the selected index
+ var selectedIndex = gListBox.selectedIndex;
+ if (selectedIndex >= 0)
+ {
+ var mailView = gMailListView.getMailViewAt(selectedIndex);
+ if (mailView)
+ {
+ gMailListView.removeMailView(mailView);
+ // now remove it from the view...
+ gListBox.selectedItem.remove();
+
+ // select the next item in the list..
+ if (selectedIndex < gListBox.getRowCount())
+ gListBox.selectedIndex = selectedIndex;
+ else
+ gListBox.selectedIndex = gListBox.getRowCount() - 1;
+
+ gMailListView.save();
+ }
+ }
+}
+
+function OnEditMailView()
+{
+ // get the selected index
+ var selectedIndex = gListBox.selectedIndex;
+ if (selectedIndex >= 0)
+ {
+ let selMailView = gMailListView.getMailViewAt(selectedIndex);
+ // open up the mail view setup dialog passing in the mail view as an argument
+ let args = {mailView: selMailView, onOkCallback: RefreshListView};
+ window.openDialog('chrome://messenger/content/mailViewSetup.xul',
+ '',
+ 'centerscreen,modal,resizable,titlebar,chrome',
+ args);
+ }
+}
+
+function OnMailViewSelect(aEvent)
+{
+ gMailViewListController.onCommandUpdate();
+}
+
+function OnMailViewDoubleClick(aEvent)
+{
+ if (aEvent.button == 0 && aEvent.target.selected)
+ OnEditMailView();
+}
diff --git a/comm/suite/mailnews/content/mailViewList.xul b/comm/suite/mailnews/content/mailViewList.xul
new file mode 100644
index 0000000000..c055bfd02a
--- /dev/null
+++ b/comm/suite/mailnews/content/mailViewList.xul
@@ -0,0 +1,79 @@
+<?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://messenger/skin/" type="text/css"?>
+<!-- Mac needs dialog.css to correctly style the moved Help button -->
+<?xml-stylesheet href="chrome://global/skin/dialog.css" type="text/css"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % mailViewLisDTD SYSTEM "chrome://messenger/locale/mailViewList.dtd">
+%mailViewLisDTD;
+<!ENTITY % FilterListDialogDTD SYSTEM "chrome://messenger/locale/FilterListDialog.dtd">
+%FilterListDialogDTD;
+]>
+
+<dialog id="mailViewListDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="MailViewListOnLoad();"
+ onunload="MailViewListOnUnload();"
+ ondialogaccept="return false;"
+ windowtype="mailnews:mailviewlist"
+ title="&mailViewListTitle.label;"
+ width="400" height="340"
+ buttons=","
+ persist="screenX screenY width height">
+
+ <script src="chrome://messenger/content/mailViewList.js"/>
+ <script src="chrome://global/content/globalOverlay.js"/>
+
+ <commandset id="mailViewCommands">
+ <command id="cmd_new" oncommand="goDoCommand('cmd_new');"/>
+ <command id="cmd_edit" oncommand="goDoCommand('cmd_edit');" disabled="true"/>
+ <command id="cmd_delete" oncommand="goDoCommand('cmd_delete');" disabled="true"/>
+ </commandset>
+
+ <keyset id="mailViewListKeys">
+ <key id="key_delete"/>
+ <key id="key_delete2"/>
+ <key id="key_open" keycode="VK_RETURN" command="cmd_edit"/>
+ </keyset>
+
+ <vbox flex="1">
+ <hbox flex="1">
+ <listbox id="mailViewList"
+ flex="1"
+ onselect="OnMailViewSelect(event);"
+ ondblclick="OnMailViewDoubleClick(event);">
+ <listcols>
+ <listcol flex="1" width="0"/>
+ </listcols>
+ <listhead>
+ <listheader label="&viewName.label;"/>
+ </listhead>
+ </listbox>
+
+ <vbox id="buttonCol">
+ <button id="newButton"
+ label="&newButton.label;"
+ accesskey="&newButton.accesskey;"
+ command="cmd_new"/>
+ <button id="editButton"
+ label="&editButton.label;"
+ accesskey="&editButton.accesskey;"
+ command="cmd_edit"/>
+ <button id="deleteButton"
+ label="&deleteButton.label;"
+ accesskey="&deleteButton.accesskey;"
+ command="cmd_delete"/>
+ <spacer flex="1"/>
+ <button id="helpButton"
+ dlgtype="help"
+ class="dialog-button"/>
+ </vbox>
+ </hbox>
+ </vbox>
+</dialog>
diff --git a/comm/suite/mailnews/content/mailViewSetup.js b/comm/suite/mailnews/content/mailViewSetup.js
new file mode 100644
index 0000000000..4c9b47f070
--- /dev/null
+++ b/comm/suite/mailnews/content/mailViewSetup.js
@@ -0,0 +1,119 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var nsMsgSearchScope = Ci.nsMsgSearchScope;
+var gMailView = null;
+
+var dialog;
+
+function mailViewOnLoad()
+{
+ initializeSearchWidgets();
+ initializeMailViewOverrides();
+ dialog = {};
+
+ if ("arguments" in window && window.arguments[0])
+ {
+ var args = window.arguments[0];
+ if ("mailView" in args)
+ gMailView = window.arguments[0].mailView;
+ if ("onOkCallback" in args)
+ dialog.okCallback = window.arguments[0].onOkCallback;
+ }
+
+ dialog.OKButton = document.documentElement.getButton("accept");
+ dialog.nameField = document.getElementById("name");
+ dialog.nameField.focus();
+
+ setSearchScope(nsMsgSearchScope.offlineMail);
+
+ if (gMailView)
+ {
+ dialog.nameField.value = gMailView.prettyName;
+ initializeSearchRows(nsMsgSearchScope.offlineMail, gMailView.searchTerms);
+ }
+ else
+ onMore(null);
+
+ doEnabling();
+}
+
+function mailViewOnUnLoad()
+{
+
+}
+
+function onOK()
+{
+ var mailViewList = Cc["@mozilla.org/messenger/mailviewlist;1"].getService(Ci.nsIMsgMailViewList);
+
+ // reflect the search widgets back into the search session
+ var newMailView = null;
+ if (gMailView)
+ {
+ gMailView.searchTerms = saveSearchTerms(gMailView.searchTerms, gMailView);
+ // if the name of the view has been changed...
+ if (gMailView.prettyName != dialog.nameField.value)
+ gMailView.mailViewName = dialog.nameField.value;
+ }
+ else
+ {
+ // otherwise, create a new mail view
+ newMailView = mailViewList.createMailView();
+
+ newMailView.searchTerms = saveSearchTerms(newMailView.searchTerms, newMailView);
+ newMailView.mailViewName = dialog.nameField.value;
+ // now add the mail view to our mail view list
+ mailViewList.addMailView(newMailView);
+ }
+
+ mailViewList.save();
+
+ if (dialog.okCallback)
+ dialog.okCallback(gMailView ? gMailView : newMailView);
+
+ return true;
+}
+
+function initializeMailViewOverrides()
+{
+ // replace some text with something we want. Need to add some ids to searchOverlay.js
+ //var orButton = document.getElementById('or');
+ //orButton.setAttribute('label', 'Any of the following');
+ //var andButton = document.getElementById('and');
+ //andButton.setAttribute('label', 'All of the following');
+ // matchAll doesn't make sense for views, since views are a single folder
+ hideMatchAllItem();
+
+}
+
+function UpdateAfterCustomHeaderChange()
+{
+ updateSearchAttributes();
+}
+
+function doEnabling()
+{
+ if (dialog.nameField.value)
+ {
+ if (dialog.OKButton.disabled)
+ dialog.OKButton.disabled = false;
+ } else
+ {
+ if (!dialog.OKButton.disabled)
+ dialog.OKButton.disabled = true;
+ }
+}
+
+function onEnterInSearchTerm()
+{
+ // no-op for us...
+}
+
+function doHelpButton()
+{
+ openHelp("message-views-create-new");
+}
+
diff --git a/comm/suite/mailnews/content/mailViewSetup.xul b/comm/suite/mailnews/content/mailViewSetup.xul
new file mode 100644
index 0000000000..203a8b8991
--- /dev/null
+++ b/comm/suite/mailnews/content/mailViewSetup.xul
@@ -0,0 +1,51 @@
+<?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://messenger/skin/searchDialog.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/searchTermOverlay.xul"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/mailViewSetup.dtd" >
+
+<dialog id="mailViewSetupDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="mailViewOnLoad();"
+ onunload="mailViewOnUnLoad();"
+ ondialogaccept="return onOK();"
+ buttons="accept,cancel"
+ buttonalign="right"
+ windowtype="mailnews:mailview"
+ title="&mailViewSetupTitle.label;"
+ style="width: 52em; height: 22em;"
+ persist="screenX screenY width height">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_search" src="chrome://messenger/locale/search.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://global/content/globalOverlay.js"/>
+ <script src="chrome://messenger/content/mailViewSetup.js"/>
+
+ <dummy class="usesMailWidgets"/>
+
+ <vbox flex="1">
+ <separator class="thin"/>
+ <vbox>
+ <hbox align="center">
+ <label value="&mailViewHeading.label;" accesskey="&mailViewHeading.accesskey;" control="name"/>
+ <textbox tabindex="0" id="name" oninput="doEnabling();"/>
+ </hbox>
+ </vbox>
+
+ <separator/>
+ <label value="&searchTermCaption.label;"/>
+ <hbox flex="1">
+ <vbox id="searchTermListBox" flex="1"/>
+ </hbox>
+ </vbox>
+
+</dialog>
diff --git a/comm/suite/mailnews/content/mailWidgets.xml b/comm/suite/mailnews/content/mailWidgets.xml
new file mode 100644
index 0000000000..29288b3b70
--- /dev/null
+++ b/comm/suite/mailnews/content/mailWidgets.xml
@@ -0,0 +1,1946 @@
+<?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/. -->
+
+
+<bindings id="mailBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <!-- dummy widget to force this file to load -->
+ <binding id="dummy" extends="xul:box"/>
+
+ <!-- temporary holding place for horizontal list -->
+
+ <binding id="extdescription" extends="chrome://global/content/bindings/listbox.xml#listbox-base">
+ <implementation>
+ <constructor><![CDATA[
+ this.children.filter(aChild => aChild.getAttribute("selected") == "true")
+ .forEach(this.selectedItems.append, this.selectedItems);
+ ]]></constructor>
+
+ <!-- ///////////////// public members ///////////////// -->
+
+ <property name="itemCount" readonly="true"
+ onget="return this.children.length;"/>
+
+ <method name="getIndexOfItem">
+ <parameter name="item"/>
+ <body><![CDATA[
+ return this.children.indexOf(item);
+ ]]></body>
+ </method>
+ <method name="getItemAtIndex">
+ <parameter name="index"/>
+ <body><![CDATA[
+ return this.children[index] || null;
+ ]]></body>
+ </method>
+ <method name="getRowCount">
+ <body><![CDATA[
+ return this.children.length;
+ ]]></body>
+ </method>
+ <method name="getNumberOfVisibleRows">
+ <body><![CDATA[
+ var firstItem = this.children[0] || null;
+ if (!firstItem)
+ return 0; // nothing to be visible
+ var itemsPerRow = Math.floor(this.boxObject.width / firstItem.boxObject.width);
+ var itemsPerCol = Math.floor(this.boxObject.height / firstItem.boxObject.height);
+ return Math.max(itemsPerRow, 1) * Math.max(itemsPerCol, 1);
+ ]]></body>
+ </method>
+ <method name="getIndexOfFirstVisibleRow">
+ <body><![CDATA[
+ //XXXzeniko unimplementable without a way to scroll
+ ]]></body>
+ </method>
+
+ <method name="ensureIndexIsVisible">
+ <parameter name="index"/>
+ <body><![CDATA[
+ this.ensureElementIsVisible(this.getItemAtIndex(index));
+ ]]></body>
+ </method>
+ <method name="ensureElementIsVisible">
+ <parameter name="item"/>
+ <body><![CDATA[
+ //XXXzeniko unimplementable without a way to scroll
+ ]]></body>
+ </method>
+ <method name="scrollToIndex">
+ <parameter name="index"/>
+ <body><![CDATA[
+ //XXXzeniko unimplementable without a way to scroll
+ ]]></body>
+ </method>
+
+ <method name="appendItem">
+ <parameter name="label"/>
+ <parameter name="value"/>
+ <body><![CDATA[
+ // -1 appends due to the way getItemAtIndex is implemented
+ return this.insertItemAt(-1, label, value);
+ ]]></body>
+ </method>
+ <method name="insertItemAt">
+ <parameter name="index"/>
+ <parameter name="label"/>
+ <parameter name="value"/>
+ <body><![CDATA[
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var item = document.createElementNS(XULNS, "descriptionitem");
+ item.setAttribute("label", label);
+ this.insertBefore(item, this.getItemAtIndex(index));
+ return item;
+ ]]></body>
+ </method>
+
+ <method name="scrollOnePage">
+ <parameter name="direction"/>
+ <body><![CDATA[
+ return direction * this.getNumberOfVisibleRows();
+ ]]></body>
+ </method>
+
+ <!-- ///////////////// private members ///////////////// -->
+
+ <property name="children" readonly="true"
+ onget="return Array.from(this.getElementsByTagName('descriptionitem'));"/>
+
+ <method name="_fireOnSelect">
+ <body><![CDATA[
+ if (!this._suppressOnSelect && !this.suppressOnSelect) {
+ this.dispatchEvent(new Event("select",
+ { bubbles: false, cancelable: true }));
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress" keycode="VK_LEFT" modifiers="control shift any"
+ action="this.moveByOffset(-1, !event.ctrlKey, event.shiftKey);"
+ phase="target" preventdefault="true"/>
+ <handler event="keypress" keycode="VK_RIGHT" modifiers="control shift any"
+ action="this.moveByOffset(1, !event.ctrlKey, event.shiftKey);"
+ phase="target" preventdefault="true"/>
+ <handler event="click" button="0" phase="target"><![CDATA[
+ if (this.selType != "multiple" || (!event.ctrlKey && !event.shiftKey && !event.metaKey))
+ this.clearSelection();
+ ]]></handler>
+ <!-- make sure we keep the focus... -->
+ <handler event="mousedown" button="0"
+ action="if (document.commandDispatcher.focusedElement != this) this.focus();"/>
+ </handlers>
+ </binding>
+
+ <binding id="descriptionitem" extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <content>
+ <xul:hbox class="attachmentBox" xbl:inherits="orient" align="start">
+ <xul:label class="descriptioncell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled,context" flex="1" dir="ltr" crop="center"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="descriptionitem-iconic" extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <content>
+ <xul:hbox class="attachmentBox" xbl:inherits="orient" align="center">
+ <xul:image class="descriptioncell-icon" xbl:inherits="src=image"/>
+ <xul:label class="descriptioncell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled,context" flex="1" dir="ltr" crop="center"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <!-- Message Pane Widgets -->
+
+ <!-- mail-toggle-headerfield: Non-email addrs headers which have a toggle
+ associated with them (i.e. the subject).
+ Use label to set the header name.
+ Use headerValue to set the header value. -->
+ <binding id="mail-toggle-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:image class="expandHeaderViewButton" xbl:inherits="onclick=ontwistyclick"/>
+ <xul:spacer flex="1"/>
+ <xul:label class="headerName" xbl:inherits="value=label" control="headerValue"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:textbox class="headerValue plain" anonid="headerValue" flex="1" readonly="true"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="headerValue" onset="return document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue').value = val;"/>
+ </implementation>
+ </binding>
+
+ <!-- mail-headerfield: presents standard text header name & value pairs. Don't use this for email addresses.
+ use label to set the header name.
+ use headerValue to set the header value. -->
+ <binding id="mail-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" control="headerValue" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:textbox class="headerValue plain" anonid="headerValue" flex="1" readonly="true"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="headerValue" onset="return document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue').value = val;"/>
+ </implementation>
+ </binding>
+
+ <binding id="mail-urlfield" extends="chrome://messenger/content/mailWidgets.xml#mail-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:label onclick="if (event.button != 2) openAsExternal(event.target.value);"
+ ondragstart="this.parentNode.setDataTransfer(event);"
+ class="headerValue plain text-link headerValueUrl"
+ anonid="headerValue" flex="1" readonly="true" context="copyUrlPopup"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="setDataTransfer">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var dt = aEvent.dataTransfer;
+ var val = aEvent.target.value;
+ dt.setData('text/x-moz-url', val + "\n" + val);
+ dt.setData('text/uri-list', val);
+ dt.setData('text/plain', val);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-emailheaderfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:mail-emailaddress class="headerValue" anonid="emailAddressNode"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="emailAddressNode" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'emailAddressNode');"
+ readonly="true"/>
+ </implementation>
+ </binding>
+
+ <!-- multi-emailHeaderField: presents multiple emailheaderfields with a toggle -->
+ <binding id="mail-multi-emailHeaderField">
+ <content>
+ <xul:hbox class="headerNameBox" align="start" pack="end">
+ <xul:image class="addresstwisty" anonid="toggleIcon"
+ collapsed="true" onclick="toggleWrap();"/>
+ <xul:label class="headerName" xbl:inherits="value=label"/>
+ </xul:hbox>
+
+ <xul:hbox class="headerValueBox" anonid="longEmailAddresses" flex="1" align="start"
+ onoverflow="if (event.detail != 1) this.parentNode.toggleIcon.collapsed = false;"
+ onunderflow="if (event.detail != 1) this.parentNode.toggleIcon.collapsed = true;">
+ <xul:description class="headerValue" anonid="emailAddresses" flex="1"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.mAddresses = new Array;
+ ]]>
+ </constructor>
+
+ <field name="mAddresses"/>
+ <!-- as a perf optimization we are going to keep a cache of email address nodes which we've
+ created around for the lifetime of the widget. mSizeOfAddressCache controls how many of these
+ elements we keep around -->
+ <field name="mSizeOfAddressCache">3</field>
+
+ <!-- addAddressView: a public method used to add an address to this widget.
+ aAddresses is an object with 3 properties: displayName, emailAddress and fullAddress
+ -->
+ <method name="addAddressView">
+ <parameter name="aAddress"/>
+ <body>
+ <![CDATA[
+ this.mAddresses.push(aAddress);
+ ]]>
+ </body>
+ </method>
+
+ <!-- updateEmailAddressNode: private method used to set properties on an address node -->
+ <method name="updateEmailAddressNode">
+ <parameter name="aEmailNode"/>
+ <parameter name="aAddress"/>
+ <body>
+ <![CDATA[
+ if (aEmailNode.parentNode.useShortView && aAddress.displayName)
+ {
+ aEmailNode.setAttribute("label", aAddress.displayName);
+ aEmailNode.setAttribute("tooltiptext", aAddress.fullAddress);
+ }
+ else
+ {
+ aEmailNode.setAttribute("label", aAddress.fullAddress || aAddress.displayName);
+ aEmailNode.removeAttribute("tooltiptext");
+ }
+ aEmailNode.setAttribute("emailAddress", aAddress.emailAddress);
+ aEmailNode.setAttribute("fullAddress", aAddress.fullAddress);
+ aEmailNode.setAttribute("displayName", aAddress.displayName);
+
+ // Add aria-label with header field type and header field content
+ // for better accessibility.
+ // Note: No extra colon and space needed, since it is
+ // already provided by this object's label attribute.
+ var ariaLabel = this.getAttribute("label") +
+ aEmailNode.getAttribute("label");
+ aEmailNode.setAttribute("aria-label", ariaLabel);
+
+ try
+ {
+ if ("UpdateEmailNodeDetails" in top)
+ UpdateEmailNodeDetails(aAddress.emailAddress, aEmailNode);
+ }
+ catch(ex)
+ {
+ dump("UpdateEmailNodeDetails failed: " + ex + "\n");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- fillCachedAddresses: private method used to fill up any cached pre-existing
+ emailAddress fields without creating new email address fields. Returns a remainder
+ for the # of addresses which require new addresses being created.
+ Invariants: 1) aNumAddressesToShow >= 0 && it is <= mAddresses.length -->
+ <method name="fillCachedAddresses">
+ <parameter name="aAddressesNode"/>
+ <parameter name="aNumAddressesToShow"/>
+ <body>
+ <![CDATA[
+ var numExistingCachedAddresses = aAddressesNode.childNodes.length;
+ if (!numExistingCachedAddresses)
+ return this.mAddresses.length; // we couldn't pre fill anything
+ else if (numExistingCachedAddresses > 1)
+ numExistingCachedAddresses = (numExistingCachedAddresses + 1)/ 2;
+
+ var index = 0;
+ var numAddressesAdded = 0;
+ var emailAddressNode;
+ var commaNode;
+ while (numAddressesAdded < numExistingCachedAddresses && numAddressesAdded < aNumAddressesToShow)
+ {
+ if (index && numExistingCachedAddresses > 1)
+ {
+ commaNode = aAddressesNode.childNodes[index++];
+ if (commaNode)
+ commaNode.hidden = false;
+ }
+
+ // get the node pointed to by index
+ emailAddressNode = aAddressesNode.childNodes[index++];
+ this.updateEmailAddressNode(emailAddressNode, this.mAddresses[numAddressesAdded]);
+ emailAddressNode.hidden = false;
+ numAddressesAdded++;
+ }
+
+ // if we have added all of our elements but we still have more cached items in this address node
+ // then make sure the extra cached copies are hidden...
+ numExistingCachedAddresses = aAddressesNode.childNodes.length; // reset
+ while (index < numExistingCachedAddresses)
+ {
+ aAddressesNode.childNodes[index++].hidden = true;
+ }
+
+ return this.mAddresses.length - numAddressesAdded;
+ ]]>
+ </body>
+ </method>
+
+ <!-- fillAddressesNode: private method used to create email address nodes for either our short
+ or long view. aAddressesNode: the div we want to add addresses too.
+ aNumAddressesToShow: number of addresses to put into the list -->
+ <method name="fillAddressesNode">
+ <parameter name="aAddressesNode"/>
+ <parameter name="aNumAddressesToShow"/>
+ <body>
+ <![CDATA[
+ var numAddresses = this.mAddresses.length;
+ if (aNumAddressesToShow <= 0 || aNumAddressesToShow > numAddresses) // then show all
+ aNumAddressesToShow = numAddresses;
+
+ // before we try to create email address nodes, try to leverage any cached nodes...
+ var remainder = this.fillCachedAddresses(aAddressesNode, aNumAddressesToShow);
+ var index = numAddresses - remainder;
+ while (index < numAddresses && index < aNumAddressesToShow)
+ {
+ var newAddressNode = document.createElement("mail-emailaddress");
+
+ // Stash the headerName somewhere that UpdateEmailNodeDetails
+ // will be able to find it.
+ newAddressNode.setAttribute("headerName", this.headerName);
+
+ if (index)
+ {
+ var textNode = document.createElement("text");
+ textNode.setAttribute("value", ", ");
+ textNode.setAttribute("class", "emailSeparator");
+ aAddressesNode.appendChild(textNode);
+ }
+
+ var itemInDocument = aAddressesNode.appendChild(newAddressNode);
+ this.updateEmailAddressNode(itemInDocument, this.mAddresses[index]);
+ index++;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="emailAddresses" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'emailAddresses');"
+ readonly="true"/>
+ <property name="longEmailAddresses" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'longEmailAddresses');"
+ readonly="true"/>
+ <property name="toggleIcon" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'toggleIcon');"
+ readonly="true"/>
+
+ <!-- buildView: public method used by callers when they are done adding all the email addresses to the widget
+ aNumAddressesToShow: total # of addresses to show in the short view -->
+ <method name="buildViews">
+ <body>
+ <![CDATA[
+ this.fillAddressesNode(this.emailAddresses, -1);
+ ]]>
+ </body>
+ </method>
+
+ <!-- Updates the nodes of this field with a call to
+ UpdateExtraAddressProcessing. The parameters are optional fields
+ that can contain extra information to be passed to
+ UpdateExtraAddressProcessing, the implementation of that function
+ should be checked to determine what it requires -->
+ <method name="updateExtraAddressProcessing">
+ <parameter name="aParam1"/>
+ <parameter name="aParam2"/>
+ <parameter name="aParam3"/>
+ <body>
+ <![CDATA[
+ if (UpdateExtraAddressProcessing) {
+ var childNodes = this.emailAddresses.childNodes;
+ for (let i = 0; i < this.mAddresses.length; i++) {
+ UpdateExtraAddressProcessing(this.mAddresses[i],
+ childNodes[i * 2],
+ aParam1, aParam2, aParam3);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="toggleWrap">
+ <body>
+ <![CDATA[
+ if (this.toggleIcon.hasAttribute("open")) {
+ this.toggleIcon.removeAttribute("open");
+ this.longEmailAddresses.setAttribute("singleline", "true");
+ } else {
+ this.toggleIcon.setAttribute("open", "true");
+ this.longEmailAddresses.removeAttribute("singleline");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- internal method used to clear both our divs -->
+ <method name="clearChildNodes">
+ <parameter name="aParentNode"/>
+ <body>
+ <![CDATA[
+ // we want to keep around the first mSizeOfAddressCache email address nodes
+ // don't forget that we have comma text nodes in there too so really we want to keep
+ // around cache size * 2 - 1.
+ var numItemsToPreserve = this.mSizeOfAddressCache * 2 - 1;
+ var numItemsInNode = aParentNode.childNodes.length;
+
+ while (numItemsInNode && (numItemsInNode > numItemsToPreserve))
+ {
+ aParentNode.childNodes[numItemsInNode - 1].remove();
+ numItemsInNode = numItemsInNode - 1;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="clearHeaderValues">
+ <body>
+ <![CDATA[
+ // clear out our local state
+ this.mAddresses = new Array;
+ if (this.toggleIcon.hasAttribute("open"))
+ // no automatic overflow tracking in this case
+ this.toggleIcon.collapsed = true;
+ this.toggleIcon.removeAttribute("open");
+ this.longEmailAddresses.setAttribute("singleline", "true");
+ // remove anything inside of each of our labels....
+ this.clearChildNodes(this.emailAddresses);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-emailaddress">
+ <content popup="emailAddressPopup" context="emailAddressPopup">
+ <xul:description anonid="emailValue" class="emailDisplayButton plain"
+ xbl:inherits="xbl:text=label,crop,aria-label" flex="1"/>
+ <xul:image class="emailDisplayImage" anonid="emailImage"
+ xbl:inherits="src=image"/>
+ </content>
+
+ <implementation>
+ <property name="label" onset="this.getPart('emailValue').setAttribute('label',val); return val;"
+ onget="return this.getPart('emailValue').getAttribute('label');"/>
+ <property name="crop" onset="this.getPart('emailValue').setAttribute('crop',val); return val;"
+ onget="return this.getPart('emailValue').getAttribute('crop');"/>
+ <property name="disabled" onset="this.getPart('emailValue').setAttribute('disabled',val); return val;"
+ onget="return this.getPart('emailValue').getAttribute('disabled');"/>
+ <property name="src" onset="this.getPart('emailImage').setAttribute('src',val); return val;"
+ onget="return this.getPart('emailImage').getAttribute('src');"/>
+ <property name="imgalign" onset="this.getPart('emailImage').setAttribute('imgalign',val); return val;"
+ onget="return this.getPart('emailImage').getAttribute('imgalign');"/>
+
+ <method name="getPart">
+ <parameter name="aPartId"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this, "anonid", aPartId);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-messageids-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start" pack="end">
+ <xul:image class="addresstwisty" anonid="toggleIcon"
+ onclick="toggleWrap();"/>
+ <xul:label class="headerName" xbl:inherits="value=label"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:label class="headerValue" anonid="headerValue" flex="1"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.mMessageIds = [];
+ this.showFullMessageIds = false;
+ ]]>
+ </constructor>
+
+ <property name="headerValue" readonly="true"
+ onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue');"/>
+ <property name="toggleIcon" readonly="true"
+ onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'toggleIcon');"/>
+
+ <field name="mMessageIds"/>
+
+ <!-- addMessageIdView: a public method used to add a message-id to this widget. -->
+ <method name="addMessageIdView">
+ <parameter name="aMessageId"/>
+ <body>
+ <![CDATA[
+ this.mMessageIds.push(aMessageId);
+ ]]>
+ </body>
+ </method>
+
+ <!-- updateMessageIdNode: private method used to set properties on an MessageId node -->
+ <method name="updateMessageIdNode">
+ <parameter name="aMessageIdNode"/>
+ <parameter name="aIndex"/>
+ <parameter name="aMessageId"/>
+ <parameter name="aLastId"/>
+ <body>
+ <![CDATA[
+ var showFullMessageIds = this.showFullMessageIds;
+
+ if (showFullMessageIds || aIndex == aLastId)
+ {
+ aMessageIdNode.setAttribute("label", aMessageId);
+ aMessageIdNode.removeAttribute("tooltiptext");
+ }
+ else
+ {
+ aMessageIdNode.setAttribute("label", aIndex);
+ aMessageIdNode.setAttribute("tooltiptext", aMessageId);
+ }
+
+ aMessageIdNode.setAttribute("index", aIndex);
+ aMessageIdNode.setAttribute("messageid", aMessageId);
+ ]]>
+ </body>
+ </method>
+
+ <method name="fillMessageIdNodes">
+ <body>
+ <![CDATA[
+ var headerValue = this.headerValue;
+ var messageIdNodes = headerValue.childNodes;
+ var numMessageIds = this.mMessageIds.length;
+ var index = 0;
+
+ while (messageIdNodes.length > numMessageIds * 2 - 1)
+ headerValue.lastChild.remove();
+
+ this.toggleIcon.hidden = numMessageIds <= 1;
+
+ for (var index = 0; index < numMessageIds; index++)
+ {
+ if (index * 2 <= messageIdNodes.length - 1)
+ {
+ this.updateMessageIdNode(messageIdNodes[index * 2], index + 1,
+ this.mMessageIds[index], numMessageIds);
+ }
+ else
+ {
+ var newMessageIdNode = document.createElement("mail-messageid");
+
+ if (index)
+ {
+ var textNode = document.createElement("text");
+ textNode.setAttribute("value", ", ");
+ textNode.setAttribute("class", "messageIdSeparator");
+ headerValue.appendChild(textNode);
+ }
+ var itemInDocument = headerValue.appendChild(newMessageIdNode);
+ this.updateMessageIdNode(itemInDocument, index + 1,
+ this.mMessageIds[index], numMessageIds);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="toggleWrap">
+ <body>
+ <![CDATA[
+ var headerValue = this.headerValue;
+ var messageIdNodes = headerValue.childNodes;
+ var showFullMessageIds = !this.showFullMessageIds;
+ var messageIds = this.mMessageIds
+
+ for (var i = 0; i < messageIdNodes.length; i += 2)
+ {
+ if (showFullMessageIds)
+ {
+ this.toggleIcon.setAttribute("open", "true");
+ messageIdNodes[i].setAttribute("label", messageIds[i / 2]);
+ messageIdNodes[i].removeAttribute("tooltiptext");
+ headerValue.removeAttribute("singleline");
+ } else
+ {
+ this.toggleIcon.removeAttribute("open");
+ messageIdNodes[i].setAttribute("label", i / 2 + 1);
+ messageIdNodes[i].setAttribute("tooltiptext", messageIds[i / 2]);
+ }
+ }
+
+ this.showFullMessageIds = showFullMessageIds;
+ ]]>
+ </body>
+ </method>
+
+ <method name="clearHeaderValues">
+ <body>
+ <![CDATA[
+ // clear out our local state
+ this.mMessageIds = new Array;
+ if (this.showFullMessageIds)
+ {
+ this.showFullMessageIds = false;
+ this.toggleIcon.removeAttribute("open");
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-messageid">
+ <content context="messageIdContext" onclick="MessageIdClick(this, event);">
+ <xul:label anonid="messageIdValue" class="messageIdDisplayButton plain"
+ xbl:inherits="value=label"/>
+ <xul:image class="messageIdDisplayImage" anonid="messageIdImage"/>
+ </content>
+
+ <implementation>
+ <property name="label" onset="this.getPart().setAttribute('label',val); return val;"
+ onget="return this.getPart('messageIdValue').getAttribute('label');"/>
+
+ <method name="getPart">
+ <parameter name="aPartId"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this, "anonid", 'messageIdValue');
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <!-- Header field for showing the tags associated with a message -->
+ <binding id="mail-headerfield-tags">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:label class="headerValue plain" anonid="headerValue" flex="1"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="headerValue" onset="return this.buildTags(val);"/>
+ <method name="buildTags">
+ <parameter name="aTags"/>
+ <body>
+ <![CDATA[
+ // aTags contains a list of actual tag names (not the keys), delimited by spaces
+ // each tag name is encoded.
+
+ // remove any existing tag items we've appended to the list
+ var headerValueNode = document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue');
+ for (var i = headerValueNode.childNodes.length - 1; i >= 0; --i)
+ headerValueNode.childNodes[i].remove();
+
+ // tokenize the keywords based on ' '
+ var tagsArray = aTags.split(' ');
+ for (var index = 0; index < tagsArray.length; index++)
+ {
+ // for each tag, create a label, give it the font color that corresponds to the
+ // color of the tag and append it.
+ var tagName;
+ try {
+ // if we got a bad tag name, getTagForKey will throw an exception, skip it
+ // and go to the next one.
+ tagName = MailServices.tags.getTagForKey(tagsArray[index]);
+ } catch (ex) { continue; }
+
+ var color = MailServices.tags.getColorForKey(tagsArray[index]);
+
+ // now create a label for the tag name, and set the color
+ var label = document.createElement("label");
+ label.setAttribute('value', tagName);
+ label.style.color = color;
+ label.className = "tagvalue blc-" + color.substr(1);
+ headerValueNode.appendChild(label);
+ }
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="search-menulist-abstract" name="searchMenulistAbstract" extends="xul:box">
+ <content>
+ <xul:menulist class="search-menulist" xbl:inherits="flex,disabled" oncommand="this.parentNode.onSelect(event)">
+ <xul:menupopup class="search-menulist-popup"/>
+ </xul:menulist>
+ </content>
+
+ <implementation>
+ <field name="internalScope">null</field>
+ <field name="internalValue">-1</field>
+ <field readonly="true" name="validityManager">
+ <![CDATA[
+ Cc['@mozilla.org/mail/search/validityManager;1'].getService(Ci.nsIMsgSearchValidityManager);
+ ]]>
+ </field>
+ <property name="searchScope" onget="return this.internalScope;">
+ <!-- scope ID - retrieve the table -->
+ <setter>
+ <![CDATA[
+ // if scope isn't changing this is a noop
+ if (this.internalScope == val) return val;
+
+ this.internalScope = val;
+ this.refreshList();
+ var targets = this.targets;
+ if (targets) {
+ for (var i=0; i< targets.length; i++) {
+ targets[i].searchScope = val;
+ }
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="validityTable" readonly="true" onget="return this.validityManager.getTable(this.searchScope)"/>
+
+ <property name="targets" readonly="true">
+ <getter>
+ <![CDATA[
+ var forAttrs = this.getAttribute("for");
+ if (!forAttrs) return null;
+ var targetIds = forAttrs.split(",");
+ if (targetIds.length == 0) return null;
+
+ var targets = new Array;
+ for (let j = 0, i = 0; i < targetIds.length; i++) {
+ var target = document.getElementById(targetIds[i]);
+ if (target) targets[j++] = target;
+ }
+ return targets;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="optargets" readonly="true">
+ <getter>
+ <![CDATA[
+ var forAttrs = this.getAttribute("opfor");
+ if (!forAttrs) return null;
+ var optargetIds = forAttrs.split(",");
+ if (optargetIds.length == 0) return null;
+
+ var optargets = new Array;
+ var j=0;
+ for (var i=0; i<optargetIds.length;i++) {
+ var optarget = document.getElementById(optargetIds[i]);
+ if (optarget) optargets[j++] = optarget;
+ }
+ return optargets;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="value" onget="return this.internalValue;">
+ <setter>
+ <![CDATA[
+ if (this.internalValue == val)
+ return val;
+ this.internalValue = val;
+ var menulist = document.getAnonymousNodes(this)[0];
+ menulist.selectedItem = this.validMenuitem;
+
+ // now notify targets of new parent's value
+ var targets = this.targets;
+ if (targets) {
+ for (var i=0; i < targets.length; i++) {
+ targets[i].parentValue = val;
+ }
+ }
+
+ // now notify optargets of new op parent's value
+ var optargets = this.optargets;
+ if (optargets) {
+ for (i=0; i < optargets.length; i++) {
+ optargets[i].opParentValue = val;
+ }
+ }
+
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <!-- label forwards to the internal menulist's "label" attribute -->
+ <property name="label" onget="return document.getAnonymousNodes(this)[0].selectedItem.getAttribute('label');">
+ </property>
+ <property name="validMenuitem" readonly="true">
+ <!-- Prepare menulist selection, adding a missing hidden menuitem if needed, and
+ updating the disabled state of the menulist label. -->
+ <getter>
+ <![CDATA[
+ if (this.value == -1) // -1 means not initialized
+ return null;
+
+ let menulist = document.getAnonymousNodes(this)[0];
+ let isCustom = isNaN(this.value);
+ let typedValue = isCustom ? this.value : parseInt(this.value);
+
+ // custom attribute to style the unavailable menulist item
+ menulist.setAttribute("unavailable",
+ !this.valueIds.includes(typedValue));
+
+ // add a hidden menulist item if value is missing
+ let menuitem = menulist.getElementsByAttribute("value", this.value).item(0);
+ if (!menuitem)
+ { // need to add a hidden menuitem
+ menuitem = menulist.appendItem(this.valueLabel, this.value);
+ menuitem.hidden = true;
+ }
+ return menuitem;
+ ]]>
+ </getter>
+ </property>
+ <method name="refreshList">
+ <parameter name="dontRestore"/> <!-- should we not restore old selection? -->
+ <body>
+ <![CDATA[
+ var menuItemIds = this.valueIds;
+ var menuItemStrings = this.valueStrings;
+
+ var menulist = document.getAnonymousNodes(this)[0];
+ var popup = menulist.firstChild;
+
+ // save our old "value" so we can restore it later
+ var oldData;
+ if (!dontRestore)
+ oldData = menulist.value;
+
+ // remove the old popup children
+ while (popup.hasChildNodes())
+ popup.lastChild.remove();
+
+ var newSelection;
+ var customizePos=-1;
+ for (var i = 0; i < menuItemIds.length; ++i)
+ {
+ // create the menuitem
+ if (Ci.nsMsgSearchAttrib.OtherHeader == menuItemIds[i].toString())
+ customizePos = i;
+ else
+ {
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", menuItemStrings[i]);
+ menuitem.setAttribute("value", menuItemIds[i]);
+ popup.appendChild(menuitem);
+ // try to restore the selection
+ if (!newSelection || oldData == menuItemIds[i].toString())
+ newSelection = menuitem;
+ }
+ }
+ if (customizePos != -1)
+ {
+ var separator = document.createElement("menuseparator");
+ popup.appendChild(separator);
+ menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", menuItemStrings[customizePos]);
+ menuitem.setAttribute("value", menuItemIds[customizePos]);
+ popup.appendChild(menuitem);
+ }
+ //
+ // If we are either uninitialized, or if we are called because
+ // of a change in our parent, update the value to the
+ // default stored in newSelection.
+ //
+ if ((this.value == -1 || dontRestore) && newSelection)
+ this.value = newSelection.getAttribute("value");
+ menulist.selectedItem = this.validMenuitem;
+ ]]>
+ </body>
+ </method>
+ <method name="onSelect">
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ var menulist = document.getAnonymousNodes(this)[0];
+ if (menulist.value == Ci.nsMsgSearchAttrib.OtherHeader) {
+ // Customize menuitem selected.
+ let args = {};
+ window.openDialog("chrome://messenger/content/CustomHeaders.xul",
+ "",
+ "modal,centerscreen,resizable,titlebar,chrome",
+ args);
+ // User may have removed the custom header currently selected in
+ // the menulist so temporarily set the selection to a safe value.
+ this.value = Ci.nsMsgSearchAttrib.OtherHeader;
+ // rebuild the menulist
+ UpdateAfterCustomHeaderChange();
+ // Find the created or chosen custom header and select it.
+ if (args.selectedVal) {
+ let menuitem = menulist.querySelector('[label="' +
+ args.selectedVal + '"]');
+ this.value = menuitem.value;
+ } else {
+ // Nothing was picked in the custom headers editor so just pick
+ // something instead of the current "Customize" menuitem.
+ this.value = menulist.getItemAtIndex(0).value;
+ }
+ } else {
+ this.value = menulist.value;
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <!-- searchattribute - Subject, Sender, To, CC, etc. -->
+ <binding id="searchattribute" name="searchAttribute"
+ extends="chrome://messenger/content/mailWidgets.xml#search-menulist-abstract">
+ <implementation>
+ <field name="stringBundle">
+ <![CDATA[
+ this.Services.strings.createBundle(
+ "chrome://messenger/locale/search-attributes.properties");
+ ]]>
+ </field>
+ <property name="valueLabel" readonly="true">
+ <getter>
+ <![CDATA[
+ if (isNaN(this.value)) // is this a custom term?
+ {
+ let customTerm = MailServices.filters.getCustomTerm(this.value);
+ if (customTerm)
+ return customTerm.name;
+ // The custom term may be missing after the extension that added
+ // it was disabled or removed. We need to notify the user.
+ let scriptError = Cc["@mozilla.org/scripterror;1"]
+ .createInstance(Ci.nsIScriptError);
+ scriptError.init("Missing custom search term " + this.value,
+ null, null, 0, 0, Ci.nsIScriptError.errorFlag,
+ "component javascript");
+ this.Services.console.logMessage(scriptError);
+ return this.stringBundle.GetStringFromName("MissingCustomTerm");
+ }
+ return this.stringBundle.GetStringFromName(
+ this.validityManager.getAttributeProperty(parseInt(this.value)));
+ ]]>
+ </getter>
+ </property>
+ <property name="valueIds" readonly="true">
+ <getter>
+ <![CDATA[
+ let result = this.validityTable.getAvailableAttributes();
+ // add any available custom search terms
+ for (let customTerm of MailServices.filters.getCustomTerms()) {
+ // for custom terms, the array element is a string with the custom id
+ // instead of the integer attribute
+ if (customTerm.getAvailable(this.searchScope, null))
+ result.push(customTerm.id);
+ }
+ return result;
+ ]]>
+ </getter>
+ </property>
+ <property name="valueStrings" readonly="true">
+ <getter>
+ <![CDATA[
+ let strings = new Array;
+ let ids = this.valueIds;
+ let hdrsArray = null;
+ try
+ {
+ let hdrs =
+ this.Services.prefs.getCharPref("mailnews.customHeaders");
+ hdrs = hdrs.replace(/\s+/g, ""); //remove white spaces before splitting
+ hdrsArray = hdrs.match(/[^:]+/g);
+ }
+ catch(ex)
+ {
+ }
+
+ let j = 0;
+ for (let i = 0; i < ids.length; i++)
+ {
+ if (isNaN(ids[i])) // Is this a custom search term?
+ {
+ let customTerm = MailServices.filters.getCustomTerm(ids[i]);
+ if (customTerm)
+ strings[i] = customTerm.name;
+ else
+ strings[i] = "";
+ }
+ else if(ids[i] > Ci.nsMsgSearchAttrib.OtherHeader && hdrsArray)
+ strings[i] = hdrsArray[j++];
+ else
+ strings[i] = this.stringBundle.GetStringFromName(
+ this.validityManager.getAttributeProperty(ids[i]));
+ }
+ return strings;
+ ]]>
+ </getter>
+ </property>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ initializeTermFromId(this.id);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <!-- searchoperator - Contains, Is Less than, etc -->
+ <binding id="searchoperator" name="searchOperator"
+ extends="chrome://messenger/content/mailWidgets.xml#search-menulist-abstract">
+ <implementation>
+ <field name="searchAttribute">Ci.nsMsgSearchAttrib.Default</field>
+ <field name="stringBundle">
+ <![CDATA[
+ this.Services.strings.createBundle("chrome://messenger/locale/search-operators.properties")
+ ]]>
+ </field>
+ <property name="valueLabel" readonly="true">
+ <getter>
+ <![CDATA[
+ return this.stringBundle.GetStringFromName(this.value);
+ ]]>
+ </getter>
+ </property>
+ <property name="valueIds" readonly="true">
+ <getter>
+ <![CDATA[
+ let isCustom = isNaN(this.searchAttribute);
+ if (isCustom)
+ {
+ let customTerm = MailServices.filters.getCustomTerm(this.searchAttribute);
+ if (customTerm)
+ return customTerm.getAvailableOperators(this.searchScope);
+ return [Ci.nsMsgSearchOp.Contains];
+ }
+ return this.validityTable.getAvailableOperators(this.searchAttribute);
+ ]]>
+ </getter>
+ </property>
+ <property name="valueStrings" readonly="true">
+ <getter>
+ <![CDATA[
+ let strings = new Array;
+ let ids = this.valueIds;
+ for (let i = 0; i < ids.length; i++)
+ strings[i] = this.stringBundle.GetStringFromID(ids[i]);
+ return strings;
+ ]]>
+ </getter>
+ </property>
+ <property name="parentValue">
+ <setter>
+ <![CDATA[
+ if (this.searchAttribute == val && val != Ci.nsMsgSearchAttrib.OtherHeader) return val;
+ this.searchAttribute = val;
+ this.refreshList(true); // don't restore the selection, since searchvalue nulls it
+ if (val == Ci.nsMsgSearchAttrib.AgeInDays) {
+ // Bug 187741 We want "Age in Days" to default to "is less than".
+ this.value = Ci.nsMsgSearchOp.IsLessThan;
+ }
+ return val;
+ ]]>
+ </setter>
+ <getter>
+ <![CDATA[
+ return this.searchAttribute;
+ ]]>
+ </getter>
+ </property>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <!-- searchvalue - a widget which dynamically changes its user interface
+ depending on what type of data it's supposed to be showing
+ currently handles arbitrary text entry, and menulists for
+ priority, status, junk status, tags, hasAttachment status,
+ and addressbook
+ -->
+ <binding id="searchvalue" name="searchValue">
+ <content>
+ <xul:textbox flex="1" class="search-value-textbox" xbl:inherits="disabled"/>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="6" stringTag="priorityHighest" class="search-value-menuitem"/>
+ <xul:menuitem value="5" stringTag="priorityHigh" class="search-value-menuitem"/>
+ <xul:menuitem value="4" stringTag="priorityNormal" class="search-value-menuitem"/>
+ <xul:menuitem value="3" stringTag="priorityLow" class="search-value-menuitem"/>
+ <xul:menuitem value="2" stringTag="priorityLowest" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="2" stringTag="replied" class="search-value-menuitem"/>
+ <xul:menuitem value="1" stringTag="read" class="search-value-menuitem"/>
+ <xul:menuitem value="65536" stringTag="new" class="search-value-menuitem"/>
+ <xul:menuitem value="4096" stringTag="forwarded" class="search-value-menuitem"/>
+ <xul:menuitem value="4" stringTag="flagged" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:textbox flex="1" class="search-value-textbox" xbl:inherits="disabled"/>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup addrbooksPopup" localonly="true"/>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="2" stringTag="junk" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="0" stringTag="hasAttachments" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="plugin" stringTag="junkScoreOriginPlugin"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="user" stringTag="junkScoreOriginUser"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="filter" stringTag="junkScoreOriginFilter"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="whitelist" stringTag="junkScoreOriginWhitelist"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="imapflag" stringTag="junkScoreOriginImapFlag"
+ class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:hbox flex="1" class="search-value-custom" xbl:inherits="disabled"/>
+ </content>
+ <implementation>
+ <field name="internalOperator">null</field>
+ <field name="internalAttribute">null</field>
+ <field name="internalValue">null</field>
+ <property name="opParentValue" onget="return this.internalOperator;">
+ <setter>
+ <![CDATA[
+ // noop if we're not changing it
+ if (this.internalOperator == val) return val;
+
+ // Keywords has the null field IsEmpty
+ if (this.searchAttribute == Ci.nsMsgSearchAttrib.Keywords) {
+ if (val == Ci.nsMsgSearchOp.IsEmpty ||
+ val == Ci.nsMsgSearchOp.IsntEmpty)
+ this.setAttribute("selectedIndex", "-1");
+ else
+ this.setAttribute("selectedIndex", "5");
+ }
+
+ // JunkStatus has the null field IsEmpty
+ if (this.searchAttribute == Ci.nsMsgSearchAttrib.JunkStatus) {
+ if (val == Ci.nsMsgSearchOp.IsEmpty ||
+ val == Ci.nsMsgSearchOp.IsntEmpty)
+ this.setAttribute("selectedIndex", "-1");
+ else
+ this.setAttribute("selectedIndex", "6");
+ }
+
+ // if it's not sender, to, cc, alladdresses, or toorcc, we don't care
+ if (this.searchAttribute != Ci.nsMsgSearchAttrib.Sender &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.To &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.ToOrCC &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.AllAddresses &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.CC ) {
+ this.internalOperator = val;
+ return val;
+ }
+
+ var children = document.getAnonymousNodes(this);
+ if (val == Ci.nsMsgSearchOp.IsntInAB ||
+ val == Ci.nsMsgSearchOp.IsInAB) {
+ // if the old internalOperator was
+ // IsntInAB or IsInAB, and the new internalOperator is
+ // IsntInAB or IsInAB, noop because the search value
+ // was an ab type, and it still is.
+ // otherwise, switch to the ab picker and select the PAB
+ if (this.internalOperator != Ci.nsMsgSearchOp.IsntInAB &&
+ this.internalOperator != Ci.nsMsgSearchOp.IsInAB) {
+ var abs = children[4].getElementsByAttribute("value", "moz-abmdbdirectory://abook.mab");
+ if (abs.item(0))
+ children[4].selectedItem = abs[0];
+ this.setAttribute("selectedIndex", "4");
+ }
+ }
+ else {
+ // if the old internalOperator wasn't
+ // IsntInAB or IsInAB, and the new internalOperator isn't
+ // IsntInAB or IsInAB, noop because the search value
+ // wasn't an ab type, and it still isn't.
+ // otherwise, switch to the textbox and clear it
+ if (this.internalOperator == Ci.nsMsgSearchOp.IsntInAB ||
+ this.internalOperator == Ci.nsMsgSearchOp.IsInAB) {
+ children[0].value = "";
+ this.setAttribute("selectedIndex", "0");
+ }
+ }
+
+ this.internalOperator = val;
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <!-- parentValue forwards to the attribute -->
+ <property name="parentValue" onset="return this.searchAttribute=val;"
+ onget="return this.searchAttribute;"/>
+ <property name="searchAttribute" onget="return this.internalAttribute;">
+ <setter>
+ <![CDATA[
+ // noop if we're not changing it
+ if (this.internalAttribute == val) return val;
+ this.internalAttribute = val;
+
+ // if the searchAttribute changing, null out the internalOperator
+ this.internalOperator = null;
+
+ // we inherit from a deck, so just use it's index attribute
+ // to hide/show widgets
+ if (isNaN(val)) // Is this a custom attribute?
+ {
+ this.setAttribute("selectedIndex", "9");
+ let customHbox = document.getAnonymousNodes(this)[9];
+ if (this.internalValue)
+ customHbox.setAttribute("value", this.internalValue.str);
+ // the searchAttribute attribute is intended as a selector in
+ // CSS for custom search terms to bind a custom value
+ customHbox.setAttribute("searchAttribute", val);
+ }
+ else if (val == Ci.nsMsgSearchAttrib.Priority)
+ this.setAttribute("selectedIndex", "1");
+ else if (val == Ci.nsMsgSearchAttrib.MsgStatus)
+ this.setAttribute("selectedIndex", "2");
+ else if (val == Ci.nsMsgSearchAttrib.Date)
+ this.setAttribute("selectedIndex", "3");
+ else if (val == Ci.nsMsgSearchAttrib.Sender) {
+ // since the internalOperator is null
+ // this is the same as the initial state
+ // the initial state for Sender isn't an ab type search
+ // it's a text search, so show the textbox
+ this.setAttribute("selectedIndex", "0");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.Keywords) {
+ this.setAttribute("selectedIndex", "5");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.JunkStatus) {
+ this.setAttribute("selectedIndex", "6");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.HasAttachmentStatus) {
+ this.setAttribute("selectedIndex", "7");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.JunkScoreOrigin) {
+ this.setAttribute("selectedIndex", "8");
+ }
+ else {
+ // a normal text field
+ this.setAttribute("selectedIndex", "0");
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="value" onget="return this.internalValue;">
+ <setter>
+ <![CDATA[
+ // val is a nsIMsgSearchValue object
+ this.internalValue = val;
+ var attrib = this.internalAttribute;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var children = document.getAnonymousNodes(this);
+ this.searchAttribute = attrib;
+ if (isNaN(attrib)) // a custom term
+ {
+ let customHbox = document.getAnonymousNodes(this)[9];
+ customHbox.setAttribute("value", val.str);
+ return val;
+ }
+ if (attrib == nsMsgSearchAttrib.Priority) {
+ var matchingPriority =
+ children[1].getElementsByAttribute("value", val.priority);
+ if (matchingPriority.item(0))
+ children[1].selectedItem = matchingPriority[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.MsgStatus) {
+ var matchingStatus =
+ children[2].getElementsByAttribute("value", val.status);
+ if (matchingStatus.item(0))
+ children[2].selectedItem = matchingStatus[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.AgeInDays)
+ children[0].value = val.age;
+ else if (attrib == nsMsgSearchAttrib.Date)
+ children[3].value = convertPRTimeToString(val.date);
+ else if (attrib == nsMsgSearchAttrib.Sender ||
+ attrib == nsMsgSearchAttrib.To ||
+ attrib == nsMsgSearchAttrib.CC ||
+ attrib == nsMsgSearchAttrib.AllAddresses ||
+ attrib == nsMsgSearchAttrib.ToOrCC)
+ {
+ if (this.internalOperator == Ci.nsMsgSearchOp.IsntInAB ||
+ this.internalOperator == Ci.nsMsgSearchOp.IsInAB) {
+ var abs = children[4].getElementsByAttribute("value", val.str);
+ if (abs.item(0))
+ children[4].selectedItem = abs[0];
+ }
+ else
+ children[0].value = val.str;
+ }
+ else if (attrib == nsMsgSearchAttrib.Keywords)
+ {
+ var keywordVal = children[5].getElementsByAttribute("value", val.str);
+ if (keywordVal.item(0))
+ {
+ children[5].value = val.str;
+ children[5].selectedItem = keywordVal[0];
+ }
+ }
+ else if (attrib == nsMsgSearchAttrib.JunkStatus) {
+ var junkStatus =
+ children[6].getElementsByAttribute("value", val.junkStatus);
+ if (junkStatus.item(0))
+ children[6].selectedItem = junkStatus[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.HasAttachmentStatus) {
+ var hasAttachmentStatus =
+ children[7].getElementsByAttribute("value", val.hasAttachmentStatus);
+ if (hasAttachmentStatus.item(0))
+ children[7].selectedItem = hasAttachmentStatus[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.JunkScoreOrigin) {
+ var junkScoreOrigin =
+ children[8].getElementsByAttribute("value", val.str);
+ if (junkScoreOrigin.item(0))
+ children[8].selectedItem = junkScoreOrigin[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.JunkPercent) {
+ children[0].value = val.junkPercent;
+ }
+ else if (attrib == nsMsgSearchAttrib.Size) {
+ children[0].value = val.size;
+ }
+ else
+ children[0].value = val.str;
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <method name="save">
+ <body>
+ <![CDATA[
+ var searchValue = this.value;
+ var searchAttribute = this.searchAttribute;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var children = document.getAnonymousNodes(this);
+
+ searchValue.attrib = searchAttribute;
+ if (searchAttribute == nsMsgSearchAttrib.Priority) {
+ searchValue.priority = children[1].selectedItem.value;
+ }
+ else if (searchAttribute == nsMsgSearchAttrib.MsgStatus)
+ searchValue.status = children[2].value;
+ else if (searchAttribute == nsMsgSearchAttrib.AgeInDays)
+ searchValue.age = children[0].value;
+ else if (searchAttribute == nsMsgSearchAttrib.Date)
+ searchValue.date = convertStringToPRTime(children[3].value);
+ else if (searchAttribute == nsMsgSearchAttrib.Sender ||
+ searchAttribute == nsMsgSearchAttrib.To ||
+ searchAttribute == nsMsgSearchAttrib.CC ||
+ searchAttribute == nsMsgSearchAttrib.AllAddresses ||
+ searchAttribute == nsMsgSearchAttrib.ToOrCC)
+ {
+ if (this.internalOperator == Ci.nsMsgSearchOp.IsntInAB ||
+ this.internalOperator == Ci.nsMsgSearchOp.IsInAB)
+ searchValue.str = children[4].selectedItem.value;
+ else
+ searchValue.str = children[0].value;
+ }
+ else if (searchAttribute == nsMsgSearchAttrib.Keywords)
+ {
+ searchValue.str = children[5].value;
+ }
+ else if (searchAttribute == nsMsgSearchAttrib.JunkStatus)
+ searchValue.junkStatus = children[6].value;
+ else if (searchAttribute == nsMsgSearchAttrib.JunkPercent)
+ searchValue.junkPercent = children[0].value;
+ else if (searchAttribute == nsMsgSearchAttrib.Size)
+ searchValue.size = children[0].value;
+ else if (searchAttribute == nsMsgSearchAttrib.HasAttachmentStatus)
+ searchValue.status = 0x10000000; // 0x10000000 is MSG_FLAG_ATTACHMENT;
+ else if (searchAttribute == nsMsgSearchAttrib.JunkScoreOrigin)
+ searchValue.str = children[8].value;
+ else if (isNaN(searchAttribute)) // a custom term
+ {
+ searchValue.attrib = nsMsgSearchAttrib.Custom;
+ searchValue.str = children[9].getAttribute("value");
+ }
+ else
+ searchValue.str = children[0].value;
+ ]]>
+ </body>
+ </method>
+ <method name="saveTo">
+ <parameter name="searchValue"/>
+ <body>
+ <![CDATA[
+ this.internalValue = searchValue;
+ this.save();
+ ]]>
+ </body>
+ </method>
+ <method name="fillInTags">
+ <body>
+ <![CDATA[
+ var children = document.getAnonymousNodes(this);
+ var popupMenu = children[5].firstChild;
+ var tagArray = MailServices.tags.getAllTags();
+ for (var i = 0; i < tagArray.length; ++i)
+ {
+ var taginfo = tagArray[i];
+ var newMenuItem = document.createElement('menuitem');
+ newMenuItem.setAttribute('label', taginfo.tag);
+ newMenuItem.setAttribute('value', taginfo.key);
+ popupMenu.appendChild(newMenuItem);
+ if (!i)
+ children[5].selectedItem = newMenuItem;
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="fillStringsForChildren">
+ <parameter name="parentNode"/>
+ <parameter name="bundle"/>
+ <body>
+ <![CDATA[
+ var children = parentNode.childNodes;
+ var len=children.length;
+ for (var i=0; i<len; i++) {
+ var node = children[i];
+ var stringTag = node.getAttribute("stringTag");
+ if (stringTag) {
+ var attr = (node.tagName == "label") ? "value" : "label";
+ node.setAttribute(attr, bundle.GetStringFromName(stringTag));
+ }
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="initialize">
+ <parameter name="menulist"/>
+ <parameter name="bundle"/>
+ <body>
+ <![CDATA[
+ this.fillStringsForChildren(menulist.firstChild, bundle);
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+
+ // initialize strings
+ let bundle = Services.strings.createBundle("chrome://messenger/locale/messenger.properties");
+
+ // intialize the priority picker
+ this.initialize(document.getAnonymousNodes(this)[1], bundle);
+
+ // initialize the status picker
+ this.initialize(document.getAnonymousNodes(this)[2], bundle);
+
+ // initialize the date picker
+ var datePicker = document.getAnonymousNodes(this)[3];
+ var searchAttribute = this.searchAttribute;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var time;
+ if (searchAttribute == nsMsgSearchAttrib.Date)
+ time = datePicker.value;
+ else
+ time = new Date();
+ // do .value instead of .setAttribute("value", xxx);
+ // to work around for bug #179412
+ // (caused by bug #157210)
+ //
+ // the searchvalue widget has two textboxes
+ // one for text, one as a placeholder for a date / calendar widget
+ datePicker.value = convertDateToString(time);
+
+ // initialize the address book picker
+ this.initialize(document.getAnonymousNodes(this)[4], bundle);
+
+ // initialize the junk status picker
+ this.initialize(document.getAnonymousNodes(this)[6], bundle);
+
+ // initialize the has attachment status picker
+ this.initialize(document.getAnonymousNodes(this)[7], bundle);
+
+ // initialize the junk score origin picker
+ this.initialize(document.getAnonymousNodes(this)[8], bundle);
+
+ // initialize the tag list
+ fillInTags();
+ ]]>
+ </constructor>
+ </implementation>
+ <handlers>
+ <handler event="keypress" keycode="VK_RETURN" modifiers="accel any"
+ action="onEnterInSearchTerm(event);" preventdefault="true"/>
+ </handlers>
+ </binding>
+
+ <binding id="folderSummary-popup" extends="chrome://global/content/bindings/popup.xml#tooltip">
+ <content>
+ <children>
+ <xul:folderSummary/>
+ </children>
+ </content>
+ <handlers>
+ <handler event="popupshowing">
+ <![CDATA[
+ let msgFolder = gFolderTreeView.getFolderAtCoords(event.clientX,
+ event.clientY);
+ if (!msgFolder)
+ return false;
+
+ let tooltipnode = document.getAnonymousNodes(this)[0];
+ let asyncResults = {};
+ if (tooltipnode.parseFolder(msgFolder, null, asyncResults))
+ return true;
+
+ let row = {}, col = {};
+ gFolderTreeView._tree.getCellAt(event.clientX, event.clientY, row,
+ col, {});
+ if (col.value.id == "folderNameCol") {
+ let cropped = gFolderTreeView._tree.isCellCropped(row.value,
+ col.value);
+ if (tooltipnode.addLocationInfo(msgFolder, cropped))
+ return true;
+ }
+
+ let counts = gFolderTreeView.getSummarizedCounts(row.value,
+ col.value.id);
+ if (counts) {
+ if (tooltipnode.addSummarizeExplain(counts))
+ return true;
+ }
+
+ return false;
+ ]]>
+ </handler>
+
+ <handler event="popuphiding">
+ document.getAnonymousNodes(this)[0].clear();
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="folderSummary">
+ <content>
+ <xul:vbox/>
+ </content>
+
+ <implementation>
+ <field name="mMaxMsgHdrsInPopup">8</field>
+ <property name="hasMessages" readonly="true" onget="return document.getAnonymousNodes(this)[0].hasChildNodes();"/>
+ <method name="parseFolder">
+ <parameter name="aFolder"/>
+ <parameter name="aUrlListener"/>
+ <parameter name="aOutAsync"/>
+ <body>
+ <![CDATA[
+ // Skip servers, Trash and Junk folders, and newgroups.
+ if (!aFolder || aFolder.isServer || !aFolder.hasNewMessages ||
+ aFolder.getFlag(Ci.nsMsgFolderFlags.Junk) ||
+ aFolder.getFlag(Ci.nsMsgFolderFlags.Trash) ||
+ (aFolder.server instanceof Ci.nsINntpIncomingServer))
+ return false;
+ let showPreviewText = this.Services.prefs.getBoolPref("mail.biff.alert.show_preview");
+ let folderArray = [];
+ let msgDatabase;
+ try {
+ msgDatabase = aFolder.msgDatabase;
+ } catch(e) {
+ // The database for this folder may be missing
+ // (e.g. outdated/missing .msf), so just skip this folder.
+ return false;
+ }
+
+ if (aFolder.flags & Ci.nsMsgFolderFlags.Virtual)
+ {
+ let dbFolderInfo = msgDatabase.dBFolderInfo;
+ var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri");
+ var srchFolderUriArray = srchFolderUri.split('|');
+ var foldersAdded = 0;
+ var RDF = Cc['@mozilla.org/rdf/rdf-service;1']
+ .getService(Ci.nsIRDFService);
+ for (var i in srchFolderUriArray)
+ {
+ var realFolder = RDF.GetResource(srchFolderUriArray[i])
+ .QueryInterface(Ci.nsIMsgFolder);
+ if (!realFolder.isServer)
+ folderArray[foldersAdded++] = realFolder;
+ }
+ }
+ else {
+ folderArray[0] = aFolder;
+ }
+
+ var foundNewMsg = false;
+ for (var folderIndex = 0; folderIndex < folderArray.length; folderIndex++)
+ {
+ aFolder = folderArray[folderIndex];
+ // now get the database
+ try {
+ msgDatabase = aFolder.msgDatabase;
+ } catch(e) {
+ // The database for this folder may be missing
+ // (e.g. outdated/missing .msf), then just skip this folder.
+ continue;
+ }
+
+ aFolder.msgDatabase = null;
+ let msgKeys = msgDatabase.getNewList();
+
+ if (!msgKeys.length)
+ continue;
+
+ if (showPreviewText)
+ {
+ // fetchMsgPreviewText forces the previewText property to get generated
+ // for each of the message keys.
+ try {
+ aOutAsync.value = aFolder.fetchMsgPreviewText(msgKeys, aUrlListener);
+ aFolder.msgDatabase = null;
+ }
+ catch (ex)
+ {
+ // fetchMsgPreviewText throws an error when we call it on a news folder, we should just not show
+ // the tooltip if this method returns an error.
+ aFolder.msgDatabase = null;
+ continue;
+ }
+ }
+ // if fetching the preview text is going to be an asynch operation and the caller
+ // is set up to handle that fact, then don't bother filling in any of the fields since
+ // we'll have to do this all over again when the fetch for the preview text completes.
+ // We don't expect to get called with a urlListener if we're doing a virtual folder.
+ if (aOutAsync.value && aUrlListener)
+ return false;
+ var unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ unicodeConverter.charset = "UTF-8";
+ foundNewMsg = true;
+
+ var index = 0;
+ while (document.getAnonymousNodes(this)[0].childNodes.length < this.mMaxMsgHdrsInPopup && index < msgKeys.length)
+ {
+ var msgPopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummaryMessage");
+ var msgHdr = msgDatabase.GetMsgHdrForKey(msgKeys[index++]);
+
+ var msgSubject = msgHdr.mime2DecodedSubject;
+ const kMsgFlagHasRe = 0x0010; // MSG_FLAG_HAS_RE
+ if(msgHdr.flags & kMsgFlagHasRe)
+ msgSubject = (msgSubject) ? "Re: " + msgSubject : "Re: ";
+
+ msgPopup.setAttribute('subject', msgSubject);
+
+ var previewText = msgHdr.getStringProperty('preview');
+ // convert the preview text from utf-8 to unicode
+ if (previewText)
+ {
+ try
+ {
+ var text = unicodeConverter.ConvertToUnicode(previewText);
+ if (text)
+ msgPopup.setAttribute('previewText', text);
+ }
+ catch (ex) { }
+ }
+
+ var names = {};
+ var emails = {};
+ var numAddresses = MailServices.headerParser.parseHeadersWithArray(msgHdr.mime2DecodedAuthor, emails, names, {});
+ msgPopup.setAttribute('sender', names.value[0] ? names.value[0] : emails.value[0]);
+ msgPopup.messageUri = aFolder.getUriForMsg(msgHdr);
+ msgPopup.folderUri = aFolder.URI;
+ msgPopup.msgKey = msgHdr.messageKey;
+ document.getAnonymousNodes(this)[0].appendChild(msgPopup);
+ }
+ if (document.getAnonymousNodes(this)[0].childNodes.length >= this.mMaxMsgHdrsInPopup)
+ return true;
+ }
+ return foundNewMsg;
+ ]]>
+ </body>
+ </method>
+
+ <method name="addLocationInfo">
+ <parameter name="aFolder"/>
+ <parameter name="aCropped"/>
+ <body>
+ <![CDATA[
+ let popupValue = null;
+ // Display also server name for items that are on level 0 and are
+ // not server names by themselves and do not have server name
+ // already appended in their label.
+ let folderIndex = gFolderTreeView.getIndexOfFolder(aFolder);
+ if (!aFolder.isServer &&
+ gFolderTreeView.getLevel(folderIndex) == 0 &&
+ !gFolderTreeView.getServerNameAdded(folderIndex)) {
+ let midPath = "";
+ let midFolder = aFolder.parent;
+ while (aFolder.server.rootFolder != midFolder) {
+ midPath = midFolder.name + " - " + midPath;
+ midFolder = midFolder.parent;
+ }
+ popupValue = aFolder.server.prettyName + " - " + midPath +
+ aFolder.name;
+ }
+ // If folder name is cropped or is a newsgroup and abbreviated per
+ // pref, use the full name as a tooltip.
+ else if (aCropped ||
+ ((aFolder.server instanceof Ci.nsINntpIncomingServer) &&
+ !(aFolder.flags & Ci.nsMsgFolderFlags.Virtual) &&
+ aFolder.server.abbreviate) && !aFolder.isServer) {
+ popupValue = aFolder.name;
+ }
+
+ if (popupValue) {
+ let loc = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummaryLocation");
+ loc.setAttribute("location", popupValue);
+ document.getAnonymousNodes(this)[0].appendChild(loc);
+ return true;
+ }
+
+ return false;
+ ]]>
+ </body>
+ </method>
+
+ <method name="addSummarizeExplain">
+ <parameter name="aCounts"/>
+ <body>
+ <![CDATA[
+ if (!aCounts || !aCounts[1])
+ return false;
+ let expl = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummarySubfoldersSummary");
+ let sumString = document.getElementById("bundle_messenger")
+ .getFormattedString("subfoldersExplanation", [aCounts[0], aCounts[1]], 2);
+ expl.setAttribute("subfolders", sumString);
+ document.getAnonymousNodes(this)[0].appendChild(expl);
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="clear">
+ <body>
+ <![CDATA[
+ var containingBox = document.getAnonymousNodes(this)[0];
+ while (containingBox.hasChildNodes())
+ containingBox.lastChild.remove();
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="folderSummary-location">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="location" xbl:inherits="value=location"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="folderSummary-subfoldersSummary">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="subfolders" xbl:inherits="value=subfolders"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="folderSummary-message">
+ <content>
+ <xul:vbox class="folderSummaryMessage">
+ <xul:hbox class="folderSummary-message-row">
+ <xul:label anonid="subject" flex="1" class="folderSummary-subject" xbl:inherits="value=subject" crop="right"/>
+ <xul:label anonid="sender" class="folderSummary-sender" xbl:inherits="value=sender" crop="right"/>
+ <xul:spring anonid="spring" flex="100%"/>
+ </xul:hbox>
+ <xul:description anonid="preview" class="folderSummary-message-row folderSummary-previewText" xbl:inherits="value=previewText" crop="right"></xul:description>
+ </xul:vbox>
+ </content>
+ <implementation>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+
+ if (!this.Services.prefs.getBoolPref("mail.biff.alert.show_preview"))
+ document.getAnonymousElementByAttribute(this, "anonid", "preview").hidden = true;
+ var hideSubject = !this.Services.prefs.getBoolPref("mail.biff.alert.show_subject");
+ var hideSender = !this.Services.prefs.getBoolPref("mail.biff.alert.show_sender");
+ if (hideSubject)
+ document.getAnonymousElementByAttribute(this, "anonid", "subject").hidden = true;
+ if (hideSender)
+ document.getAnonymousElementByAttribute(this, "anonid", "sender").hidden = true;
+ if (hideSubject && hideSender)
+ document.getAnonymousElementByAttribute(this, "anonid", "spring").hidden = true;
+ ]]>
+ </constructor>
+ </implementation>
+ <handlers>
+ <handler event="click" button="0">
+ <![CDATA[
+ var topmostMsgWindow;
+ try {
+ topmostMsgWindow = MailServices.mailSession.topmostMsgWindow;
+ } catch (ex) {}
+
+ if (topmostMsgWindow)
+ {
+ // Bring window to the front
+ topmostMsgWindow.domWindow.focus();
+
+ try {
+ // SelectFolder throws an exception if the folder is not in the current folder view
+ MailServices.mailSession.topmostMsgWindow.windowCommands.selectFolder(this.folderUri);
+ MailServices.mailSession.topmostMsgWindow.windowCommands.selectMessage(this.messageUri);
+ } catch (ex) {}
+ }
+ else
+ {
+ // open a new window
+ var mailWindowService = Cc["@mozilla.org/messenger/windowservice;1"].
+ getService(Ci.nsIMessengerWindowService);
+ mailWindowService.openMessengerWindowWithUri("mail:3pane", this.folderUri, this.msgKey);
+ }
+
+ if (gAlertListener)
+ gAlertListener.observe(null, "alertclicksimplecallback", "");
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+</bindings>
diff --git a/comm/suite/mailnews/content/mailWindow.js b/comm/suite/mailnews/content/mailWindow.js
new file mode 100644
index 0000000000..388ebe2cb1
--- /dev/null
+++ b/comm/suite/mailnews/content/mailWindow.js
@@ -0,0 +1,593 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ //This file stores variables common to mail windows
+
+var messenger;
+var statusFeedback;
+var msgWindow;
+
+var msgComposeService;
+var accountManager;
+var RDF;
+var msgComposeType;
+var msgComposeFormat;
+
+var gMessengerBundle;
+var gBrandBundle;
+
+var accountCentralBox = null;
+var gDisableViewsSearch = null;
+var gAccountCentralLoaded = true;
+//End progress and Status variables
+
+var gOfflineManager;
+
+function OnMailWindowUnload()
+{
+ RemoveMailOfflineObserver();
+ ClearPendingReadTimer();
+
+ var searchSession = GetSearchSession();
+ if (searchSession)
+ {
+ removeGlobalListeners();
+ if (gPreQuickSearchView) //close the cached pre quick search view
+ gPreQuickSearchView.close();
+ }
+
+ var dbview = GetDBView();
+ if (dbview) {
+ dbview.close();
+ }
+
+ MailServices.mailSession.RemoveFolderListener(folderListener);
+
+ MailServices.mailSession.RemoveMsgWindow(msgWindow);
+ messenger.setWindow(null, null);
+
+ msgWindow.closeWindow();
+
+ msgWindow.msgHeaderSink = null;
+ msgWindow.notificationCallbacks = null;
+ gDBView = null;
+}
+
+/**
+ * When copying/dragging, convert imap/mailbox URLs of images into data URLs so
+ * that the images can be accessed in a paste elsewhere.
+ */
+function onCopyOrDragStart(e) {
+ let browser = getBrowser();
+ if (!browser) {
+ return;
+ }
+ let sourceDoc = browser.contentDocument;
+ if (e.target.ownerDocument != sourceDoc) {
+ // We're only interested if this is in the message content.
+ return;
+ }
+
+ let imgMap = new Map(); // Mapping img.src -> dataURL.
+
+ // For copy, the data of what is to be copied is not accessible at this point.
+ // Figure out what images are a) part of the selection and b) visible in
+ // the current document. If their source isn't http or data already, convert
+ // them to data URLs.
+ let selection = sourceDoc.getSelection();
+ let draggedImg = selection.isCollapsed ? e.target : null;
+ for (let img of sourceDoc.images) {
+ if (/^(https?|data):/.test(img.src)) {
+ continue;
+ }
+
+ if (img.naturalWidth == 0) {
+ // Broken/inaccessible image then...
+ continue;
+ }
+
+ if (!draggedImg && !selection.containsNode(img, true)) {
+ continue;
+ }
+
+ let style = window.getComputedStyle(img);
+ if (style.display == "none" || style.visibility == "hidden") {
+ continue;
+ }
+
+ // Do not convert if the image is specifically flagged to not snarf.
+ if (img.getAttribute("moz-do-not-send") == "true") {
+ continue;
+ }
+
+ // We don't need to wait for the image to load. If it isn't already loaded
+ // in the source document, we wouldn't want it anyway.
+ let canvas = sourceDoc.createElement("canvas");
+ canvas.width = img.width;
+ canvas.height = img.height;
+ canvas.getContext("2d").drawImage(img, 0, 0, img.width, img.height);
+
+ let type = /\.jpe?g$/i.test(img.src) ? "image/jpg" : "image/png";
+ imgMap.set(img.src, canvas.toDataURL(type));
+ }
+
+ if (imgMap.size == 0) {
+ // Nothing that needs converting!
+ return;
+ }
+
+ let clonedSelection = draggedImg ? draggedImg.cloneNode(false) :
+ selection.getRangeAt(0).cloneContents();
+ let div = sourceDoc.createElement("div");
+ div.appendChild(clonedSelection);
+
+ let images = div.querySelectorAll("img");
+ for (let img of images) {
+ if (!imgMap.has(img.src)) {
+ continue;
+ }
+ img.src = imgMap.get(img.src);
+ }
+
+ let html = div.innerHTML;
+ let parserUtils = Cc["@mozilla.org/parserutils;1"]
+ .getService(Ci.nsIParserUtils);
+ let plain =
+ parserUtils.convertToPlainText(html,
+ Ci.nsIDocumentEncoder.OutputForPlainTextClipboardCopy,
+ 0);
+
+ // Copy operation.
+ if ("clipboardData" in e) {
+ e.clipboardData.setData("text/html", html);
+ e.clipboardData.setData("text/plain", plain);
+ e.preventDefault();
+ }
+ // Drag operation.
+ else if ("dataTransfer" in e) {
+ e.dataTransfer.setData("text/html", html);
+ e.dataTransfer.setData("text/plain", plain);
+ }
+}
+
+function CreateMailWindowGlobals()
+{
+ // Get the messenger instance.
+ messenger = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger);
+
+ // Create windows status feedback
+ // set the JS implementation of status feedback before creating the c++ one..
+ window.MsgStatusFeedback = new nsMsgStatusFeedback();
+ // Double register the status feedback object as the xul browser window
+ // implementation.
+ window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow)
+ .XULBrowserWindow = window.MsgStatusFeedback;
+
+ statusFeedback = Cc["@mozilla.org/messenger/statusfeedback;1"]
+ .createInstance(Ci.nsIMsgStatusFeedback);
+ statusFeedback.setWrappedStatusFeedback(window.MsgStatusFeedback);
+
+ window.MsgWindowCommands = new nsMsgWindowCommands();
+
+ //Create message window object
+ msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"]
+ .createInstance(Ci.nsIMsgWindow);
+
+ msgComposeService = Cc['@mozilla.org/messengercompose;1']
+ .getService(Ci.nsIMsgComposeService);
+
+ accountManager = MailServices.accounts;
+
+ RDF = Cc['@mozilla.org/rdf/rdf-service;1']
+ .getService(Ci.nsIRDFService);
+
+ msgComposeType = Ci.nsIMsgCompType;
+ msgComposeFormat = Ci.nsIMsgCompFormat;
+
+ gMessengerBundle = document.getElementById("bundle_messenger");
+ gBrandBundle = document.getElementById("bundle_brand");
+
+ msgWindow.notificationCallbacks = new nsMsgBadCertHandler();
+}
+
+function InitMsgWindow()
+{
+ msgWindow.windowCommands = new nsMsgWindowCommands();
+ // set the domWindow before setting the status feedback and header sink objects
+ msgWindow.domWindow = window;
+ msgWindow.statusFeedback = statusFeedback;
+ msgWindow.msgHeaderSink = messageHeaderSink;
+ MailServices.mailSession.AddMsgWindow(msgWindow);
+
+ var messagepane = getMessageBrowser();
+ messagepane.docShell.allowAuth = false;
+ messagepane.docShell.allowDNSPrefetch = false;
+ msgWindow.rootDocShell.allowAuth = true;
+ msgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL;
+ // Ensure we don't load xul error pages into the main window
+ msgWindow.rootDocShell.useErrorPages = false;
+
+ document.addEventListener("copy", onCopyOrDragStart, true);
+ document.addEventListener("dragstart", onCopyOrDragStart, true);
+}
+
+function messagePaneOnResize(event)
+{
+ // scale any overflowing images
+ var messagepane = getMessageBrowser();
+ var doc = messagepane.contentDocument;
+ var imgs = doc.images;
+ for (var img of imgs)
+ {
+ if (img.className == "moz-attached-image")
+ {
+ if (img.naturalWidth <= doc.body.clientWidth)
+ {
+ img.removeAttribute("isshrunk");
+ img.removeAttribute("overflowing");
+ }
+ else if (img.hasAttribute("shrinktofit"))
+ {
+ img.setAttribute("isshrunk", "true");
+ img.removeAttribute("overflowing");
+ }
+ else
+ {
+ img.setAttribute("overflowing", "true");
+ img.removeAttribute("isshrunk");
+ }
+ }
+ }
+
+}
+
+function messagePaneOnClick(event)
+{
+ // if this is stand alone mail (no browser)
+ // or this isn't a simple left click, do nothing, and let the normal code execute
+ if (event.button != 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
+ return contentAreaClick(event);
+
+ // try to determine the href for what you are clicking on.
+ // for example, it might be "" if you aren't left clicking on a link
+ var ceParams = hrefAndLinkNodeForClickEvent(event);
+ if (!ceParams && !event.button)
+ {
+ var target = event.target;
+ // is this an image that we might want to scale?
+ if (target instanceof Ci.nsIImageLoadingContent)
+ {
+ // make sure it loaded successfully
+ var req = target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (!req || req.imageStatus & Ci.imgIRequest.STATUS_ERROR)
+ return true;
+ // is it an inline attachment?
+ if (/^moz-attached-image/.test(target.className))
+ {
+ if (target.hasAttribute("isshrunk"))
+ {
+ // currently shrunk to fit, so unshrink it
+ target.removeAttribute("isshrunk");
+ target.removeAttribute("shrinktofit");
+ target.setAttribute("overflowing", "true");
+ }
+ else if (target.hasAttribute("overflowing"))
+ {
+ // user wants to shrink now
+ target.setAttribute("isshrunk", "true");
+ target.setAttribute("shrinktofit", "true");
+ target.removeAttribute("overflowing");
+ }
+ }
+ }
+ return true;
+ }
+ var href = ceParams.href;
+
+ // we know that http://, https://, ftp://, file://, chrome://,
+ // resource://, and about, should load in a browser. but if
+ // we don't have one of those (examples are mailto, imap, news, mailbox, snews,
+ // nntp, ldap, and externally handled schemes like aim) we may or may not
+ // want a browser window, in which case we return here and let the normal code
+ // handle it
+ var needABrowser = /(^http(s)?:|^ftp:|^file:|^chrome:|^resource:|^about:)/i;
+ if (href.search(needABrowser) == -1)
+ return true;
+
+ // however, if the protocol should not be loaded internally, then we should
+ // not put up a new browser window. we should just let the usual processing
+ // take place.
+ try {
+ var extProtService = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService);
+ var scheme = href.substring(0, href.indexOf(":"));
+ if (!extProtService.isExposedProtocol(scheme))
+ return true;
+ }
+ catch (ex) {} // ignore errors, and just assume that we can proceed.
+
+ // if you get here, the user did a simple left click on a link
+ // that we know should be in a browser window.
+ // since we are in the message pane, send it to the top most browser window
+ // (or open one) right away, instead of waiting for us to get some data and
+ // determine the content type, and then open a browser window
+ // we want to preventDefault, so that in
+ // nsGenericHTMLElement::HandleDOMEventForAnchors(), we don't try to handle the click again
+ event.preventDefault();
+ if (isPhishingURL(ceParams.linkNode, false, href))
+ return false;
+
+ openAsExternal(href);
+ return true;
+}
+
+// We're going to implement our status feedback for the mail window in JS now.
+// the following contains the implementation of our status feedback object
+
+function nsMsgStatusFeedback()
+{
+}
+
+nsMsgStatusFeedback.prototype =
+{
+ // global variables for status / feedback information....
+ statusTextFld : null,
+ statusBar : null,
+ statusPanel : null,
+ throbber : null,
+ stopCmd : null,
+ startTimeoutID : null,
+ stopTimeoutID : null,
+ pendingStartRequests : 0,
+ meteorsSpinning : false,
+ myDefaultStatus : "",
+
+ ensureStatusFields : function()
+ {
+ if (!this.statusTextFld ) this.statusTextFld = document.getElementById("statusText");
+ if (!this.statusBar) this.statusBar = document.getElementById("statusbar-icon");
+ if (!this.statusPanel) this.statusPanel = document.getElementById("statusbar-progresspanel");
+ if (!this.throbber) this.throbber = document.getElementById("navigator-throbber");
+ if (!this.stopCmd) this.stopCmd = document.getElementById("cmd_stop");
+ },
+
+ // nsIXULBrowserWindow implementation
+ setJSStatus : function(status)
+ {
+ if (status.length > 0)
+ this.showStatusString(status);
+ },
+ setOverLink : function(link, context)
+ {
+ this.ensureStatusFields();
+ this.statusTextFld.label = link;
+ },
+
+ // Called before links are navigated to to allow us to retarget them if needed.
+ onBeforeLinkTraversal: function(aOriginalTarget, aLinkURI, aLinkNode, aIsAppTab)
+ {
+ return aOriginalTarget;
+ },
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgStatusFeedback) ||
+ iid.equals(Ci.nsIXULBrowserWindow) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ // nsIMsgStatusFeedback implementation.
+ showStatusString : function(statusText)
+ {
+ this.ensureStatusFields();
+ if ( !statusText.length )
+ statusText = this.myDefaultStatus;
+ else
+ this.myDefaultStatus = "";
+ this.statusTextFld.label = statusText;
+ },
+ setStatusString : function(status)
+ {
+ if (status.length > 0)
+ {
+ this.myDefaultStatus = status;
+ this.statusTextFld.label = status;
+ }
+ },
+ _startMeteors : function()
+ {
+ this.ensureStatusFields();
+
+ this.meteorsSpinning = true;
+ this.startTimeoutID = null;
+
+ // Show progress meter
+ this.statusPanel.collapsed = false;
+
+ // Turn progress meter on.
+ this.statusBar.setAttribute("mode","undetermined");
+
+ // start the throbber
+ if (this.throbber)
+ this.throbber.setAttribute("busy", true);
+
+ //turn on stop button and menu
+ if (this.stopCmd)
+ this.stopCmd.removeAttribute("disabled");
+ },
+ startMeteors : function()
+ {
+ this.pendingStartRequests++;
+ // if we don't already have a start meteor timeout pending
+ // and the meteors aren't spinning, then kick off a start
+ if (!this.startTimeoutID && !this.meteorsSpinning && window.MsgStatusFeedback)
+ this.startTimeoutID = setTimeout('window.MsgStatusFeedback._startMeteors();', 500);
+
+ // since we are going to start up the throbber no sense in processing
+ // a stop timeout...
+ if (this.stopTimeoutID)
+ {
+ clearTimeout(this.stopTimeoutID);
+ this.stopTimeoutID = null;
+ }
+ },
+ _stopMeteors : function()
+ {
+ this.ensureStatusFields();
+ this.showStatusString(this.myDefaultStatus);
+
+ // stop the throbber
+ if (this.throbber)
+ this.throbber.setAttribute("busy", false);
+
+ // Turn progress meter off.
+ this.statusPanel.collapsed = true;
+ this.statusBar.setAttribute("mode","normal");
+ this.statusBar.value = 0; // be sure to clear the progress bar
+ this.statusBar.label = "";
+ if (this.stopCmd)
+ this.stopCmd.setAttribute("disabled", "true");
+
+ this.meteorsSpinning = false;
+ this.stopTimeoutID = null;
+ },
+ stopMeteors : function()
+ {
+ if (this.pendingStartRequests > 0)
+ this.pendingStartRequests--;
+
+ // if we are going to be starting the meteors, cancel the start
+ if (this.pendingStartRequests == 0 && this.startTimeoutID)
+ {
+ clearTimeout(this.startTimeoutID);
+ this.startTimeoutID = null;
+ }
+
+ // if we have no more pending starts and we don't have a stop timeout already in progress
+ // AND the meteors are currently running then fire a stop timeout to shut them down.
+ if (this.pendingStartRequests == 0 && !this.stopTimeoutID)
+ {
+ if (this.meteorsSpinning && window.MsgStatusFeedback)
+ this.stopTimeoutID = setTimeout('window.MsgStatusFeedback._stopMeteors();', 500);
+ }
+ },
+ showProgress : function(percentage)
+ {
+ this.ensureStatusFields();
+ if (percentage >= 0)
+ {
+ this.statusBar.setAttribute("mode", "normal");
+ this.statusBar.value = percentage;
+ this.statusBar.label = Math.round(percentage) + "%";
+ }
+ }
+}
+
+
+function nsMsgWindowCommands()
+{
+}
+
+nsMsgWindowCommands.prototype =
+{
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgWindowCommands) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_NOINTERFACE;
+ },
+
+ selectFolder: function(folderUri)
+ {
+ gFolderTreeView.selectFolder(MailUtils.getFolderForURI(folderUri));
+ },
+
+ selectMessage: function(messageUri)
+ {
+ SelectMessage(messageUri);
+ },
+
+ clearMsgPane: function()
+ {
+ if (gDBView)
+ setTitleFromFolder(gDBView.msgFolder,null);
+ else
+ setTitleFromFolder(null,null);
+ ClearMessagePane();
+ }
+}
+
+function StopUrls()
+{
+ msgWindow.StopUrls();
+}
+
+function loadStartPage()
+{
+ try
+ {
+ gMessageNotificationBar.clearMsgNotifications();
+
+ var startpageenabled = Services.prefs.getBoolPref("mailnews.start_page.enabled");
+ if (startpageenabled)
+ {
+ var startpage = GetLocalizedStringPref("mailnews.start_page.url");
+ if (startpage)
+ {
+ GetMessagePaneFrame().location.href = startpage;
+ //dump("start message pane with: " + startpage + "\n");
+ ClearMessageSelection();
+ }
+ }
+ }
+ catch (ex)
+ {
+ dump("Error loading start page.\n");
+ return;
+ }
+}
+
+// Given the server, open the twisty and the set the selection
+// on inbox of that server.
+// prompt if offline.
+function OpenInboxForServer(server)
+{
+ ShowThreadPane();
+ gFolderTreeView.selectFolder(GetInboxFolder(server));
+
+ if (!Services.io.offline) {
+ if (server.type != "imap")
+ GetMessagesForInboxOnServer(server);
+ }
+ else if (DoGetNewMailWhenOffline()) {
+ GetMessagesForInboxOnServer(server);
+ }
+}
+
+function GetSearchSession()
+{
+ if (("gSearchSession" in top) && gSearchSession)
+ return gSearchSession;
+ else
+ return null;
+}
+
+function MailSetCharacterSet(aEvent)
+{
+ if (aEvent.target.hasAttribute("charset")) {
+ msgWindow.mailCharacterSet = aEvent.target.getAttribute("charset");
+ msgWindow.charsetOverride = true;
+ }
+ messenger.setDocumentCharset(msgWindow.mailCharacterSet);
+}
diff --git a/comm/suite/mailnews/content/mailWindowOverlay.js b/comm/suite/mailnews/content/mailWindowOverlay.js
new file mode 100644
index 0000000000..223587e914
--- /dev/null
+++ b/comm/suite/mailnews/content/mailWindowOverlay.js
@@ -0,0 +1,2695 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+var {FeedUtils} = ChromeUtils.import("resource:///modules/FeedUtils.jsm");
+var { FolderUtils } = ChromeUtils.import("resource:///modules/FolderUtils.jsm");
+var {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
+var { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+
+var kClassicMailLayout = 0;
+var kWideMailLayout = 1;
+var kVerticalMailLayout = 2;
+
+var kMouseButtonLeft = 0;
+var kMouseButtonMiddle = 1;
+var kMouseButtonRight = 2;
+
+// Per message header flags to keep track of whether the user is allowing remote
+// content for a particular message.
+// if you change or add more values to these constants, be sure to modify
+// the corresponding definitions in nsMsgContentPolicy.cpp
+var kNoRemoteContentPolicy = 0;
+var kBlockRemoteContent = 1;
+var kAllowRemoteContent = 2;
+
+var kIsAPhishMessage = 0;
+var kNotAPhishMessage = 1;
+
+var kMsgForwardAsAttachment = 0;
+
+var gMessengerBundle;
+var gOfflineManager;
+// Timer to mark read, if the user has configured the app to mark a message as
+// read if it is viewed for more than n seconds.
+var gMarkViewedMessageAsReadTimer = null;
+
+// The user preference, if HTML is not allowed. Assume, that the user could have
+// set this to a value > 1 in their prefs.js or user.js, but that the value will
+// not change during runtime other than through the MsgBody*() functions below.
+var gDisallow_classes_no_html = 1;
+
+// Disable the File | New | Account... menu item if the account preference is
+// locked. Two other affected areas are the account central and the account
+// manager dialogs.
+function menu_new_init() {
+ let folders = GetSelectedMsgFolders();
+ if (folders.length != 1)
+ return;
+
+ let folder = folders[0];
+
+ if (!gMessengerBundle)
+ gMessengerBundle = document.getElementById("bundle_messenger");
+
+ if (Services.prefs.prefIsLocked("mail.disable_new_account_addition"))
+ document.getElementById("newAccountMenuItem")
+ .setAttribute("disabled", "true");
+
+ let isInbox = folder.isSpecialFolder(Ci.nsMsgFolderFlags.Inbox, false);
+ let showNew = folder.canCreateSubfolders ||
+ (isInbox && !(folder.flags & Ci.nsMsgFolderFlags.Virtual));
+ ShowMenuItem("menu_newFolder", showNew);
+ ShowMenuItem("menu_newVirtualFolder", showNew);
+ EnableMenuItem("menu_newFolder", folder.server.type != "imap" ||
+ !Services.io.offline);
+ if (showNew) {
+ // Change "New Folder..." menu according to the context.
+ let label = (folder.isServer || isInbox) ? "newFolderMenuItem" :
+ "newSubfolderMenuItem";
+ SetMenuItemLabel("menu_newFolder", gMessengerBundle.getString(label));
+ }
+}
+
+function goUpdateMailMenuItems(commandset) {
+ for (var i = 0; i < commandset.childNodes.length; i++) {
+ var commandID = commandset.childNodes[i].getAttribute("id");
+ if (commandID)
+ goUpdateCommand(commandID);
+ }
+}
+
+function file_init() {
+ document.commandDispatcher.updateCommands("create-menu-file");
+}
+
+function InitEditMessagesMenu() {
+ goSetMenuValue("cmd_delete", "valueDefault");
+ goSetAccessKey("cmd_delete", "valueDefaultAccessKey");
+ document.commandDispatcher.updateCommands("create-menu-edit");
+
+ // initialize the favorite Folder checkbox in the edit menu
+ let favoriteFolderMenu = document.getElementById("menu_favoriteFolder");
+ if (!favoriteFolderMenu.hasAttribute("disabled")) {
+ let folders = GetSelectedMsgFolders();
+ if (folders.length == 1 && !folders[0].isServer) {
+ let checked = folders[0].getFlag(Ci.nsMsgFolderFlags.Favorite);
+ // Adjust the checked state on the menu item.
+ favoriteFolderMenu.setAttribute("checked", checked);
+ favoriteFolderMenu.hidden = false;
+ } else {
+ favoriteFolderMenu.hidden = true;
+ }
+ }
+}
+
+function InitGoMessagesMenu() {
+ // deactivate the folders in the go menu if we don't have a folderpane
+ document.getElementById("goFolderMenu")
+ .setAttribute("disabled", IsFolderPaneCollapsed());
+ document.commandDispatcher.updateCommands("create-menu-go");
+}
+
+function view_init() {
+ if (!gMessengerBundle)
+ gMessengerBundle = document.getElementById("bundle_messenger");
+
+ var message_menuitem = document.getElementById("menu_showMessagePane");
+ if (message_menuitem && !message_menuitem.hidden) {
+ message_menuitem.setAttribute("checked", !IsMessagePaneCollapsed());
+ message_menuitem.setAttribute("disabled", gAccountCentralLoaded);
+ }
+
+ var threadpane_menuitem = document.getElementById("menu_showThreadPane");
+ if (threadpane_menuitem && !threadpane_menuitem.hidden) {
+ threadpane_menuitem.setAttribute("checked", !IsDisplayDeckCollapsed());
+ threadpane_menuitem.setAttribute("disabled", gAccountCentralLoaded);
+ }
+
+ var folderPane_menuitem = document.getElementById("menu_showFolderPane");
+ if (folderPane_menuitem && !folderPane_menuitem.hidden)
+ folderPane_menuitem.setAttribute("checked", !IsFolderPaneCollapsed());
+
+ document.getElementById("viewSortMenu").disabled = gAccountCentralLoaded;
+ document.getElementById("viewMessageViewMenu").disabled = gAccountCentralLoaded;
+ document.getElementById("viewMessagesMenu").disabled = gAccountCentralLoaded;
+ document.getElementById("charsetMenu").disabled = !gMessageDisplay.displayedMessage;
+
+ // Initialize the Message Body menuitem
+ let isFeed = gFolderDisplay &&
+ ((gFolderDisplay.displayedFolder &&
+ gFolderDisplay.displayedFolder.server.type == "rss") ||
+ gFolderDisplay.selectedMessageIsFeed);
+ document.getElementById("viewBodyMenu").hidden = isFeed;
+
+ // Initialize the Show Feed Summary menu
+ let viewFeedSummary = document.getElementById("viewFeedSummary");
+ viewFeedSummary.hidden = !isFeed ||
+ document.documentElement.getAttribute("windowtype") != "mail:3pane";
+
+ let viewRssMenuItemIds = ["bodyFeedGlobalWebPage",
+ "bodyFeedGlobalSummary",
+ "bodyFeedPerFolderPref"];
+ let checked = FeedMessageHandler.onSelectPref;
+ for (let [index, id] of viewRssMenuItemIds.entries()) {
+ document.getElementById(id)
+ .setAttribute("checked", index == checked);
+ }
+
+ // Initialize the Display Attachments Inline menu.
+ var viewAttachmentInline = Services.prefs.getBoolPref("mail.inline_attachments");
+ document.getElementById("viewAttachmentsInlineMenuitem")
+ .setAttribute("checked", viewAttachmentInline);
+
+ document.commandDispatcher.updateCommands("create-menu-view");
+}
+
+function InitViewLayoutStyleMenu(event) {
+ var paneConfig = Services.prefs.getIntPref("mail.pane_config.dynamic");
+ var layoutStyleMenuitem = event.target.childNodes[paneConfig];
+ if (layoutStyleMenuitem)
+ layoutStyleMenuitem.setAttribute("checked", "true");
+}
+
+function setSortByMenuItemCheckState(id, value) {
+ var menuitem = document.getElementById(id);
+ if (menuitem) {
+ menuitem.setAttribute("checked", value);
+ }
+}
+
+function InitViewSortByMenu() {
+ var sortType = gDBView.sortType;
+
+ setSortByMenuItemCheckState("sortByDateMenuitem",
+ sortType == Ci.nsMsgViewSortType.byDate);
+ setSortByMenuItemCheckState("sortByReceivedMenuitem",
+ sortType == Ci.nsMsgViewSortType.byReceived);
+ setSortByMenuItemCheckState("sortByFlagMenuitem",
+ sortType == Ci.nsMsgViewSortType.byFlagged);
+ setSortByMenuItemCheckState("sortByOrderReceivedMenuitem",
+ sortType == Ci.nsMsgViewSortType.byId);
+ setSortByMenuItemCheckState("sortByPriorityMenuitem",
+ sortType == Ci.nsMsgViewSortType.byPriority);
+ setSortByMenuItemCheckState("sortBySizeMenuitem",
+ sortType == Ci.nsMsgViewSortType.bySize);
+ setSortByMenuItemCheckState("sortByStatusMenuitem",
+ sortType == Ci.nsMsgViewSortType.byStatus);
+ setSortByMenuItemCheckState("sortBySubjectMenuitem",
+ sortType == Ci.nsMsgViewSortType.bySubject);
+ setSortByMenuItemCheckState("sortByUnreadMenuitem",
+ sortType == Ci.nsMsgViewSortType.byUnread);
+ setSortByMenuItemCheckState("sortByTagsMenuitem",
+ sortType == Ci.nsMsgViewSortType.byTags);
+ setSortByMenuItemCheckState("sortByJunkStatusMenuitem",
+ sortType == Ci.nsMsgViewSortType.byJunkStatus);
+ setSortByMenuItemCheckState("sortByFromMenuitem",
+ sortType == Ci.nsMsgViewSortType.byAuthor);
+ setSortByMenuItemCheckState("sortByRecipientMenuitem",
+ sortType == Ci.nsMsgViewSortType.byRecipient);
+ setSortByMenuItemCheckState("sortByAttachmentsMenuitem",
+ sortType == Ci.nsMsgViewSortType.byAttachments);
+
+ var sortOrder = gDBView.sortOrder;
+ var sortTypeSupportsGrouping = (sortType == Ci.nsMsgViewSortType.byAuthor ||
+ sortType == Ci.nsMsgViewSortType.byDate ||
+ sortType == Ci.nsMsgViewSortType.byReceived ||
+ sortType == Ci.nsMsgViewSortType.byPriority ||
+ sortType == Ci.nsMsgViewSortType.bySubject ||
+ sortType == Ci.nsMsgViewSortType.byTags ||
+ sortType == Ci.nsMsgViewSortType.byRecipient ||
+ sortType == Ci.nsMsgViewSortType.byFlagged ||
+ sortType == Ci.nsMsgViewSortType.byAttachments);
+
+ setSortByMenuItemCheckState("sortAscending",
+ sortOrder == Ci.nsMsgViewSortOrder.ascending);
+ setSortByMenuItemCheckState("sortDescending",
+ sortOrder == Ci.nsMsgViewSortOrder.descending);
+
+ var grouped = ((gDBView.viewFlags & Ci.nsMsgViewFlagsType.kGroupBySort) != 0);
+ var threaded = ((gDBView.viewFlags & Ci.nsMsgViewFlagsType.kThreadedDisplay) != 0 && !grouped);
+ var sortThreadedMenuItem = document.getElementById("sortThreaded");
+ var sortUnthreadedMenuItem = document.getElementById("sortUnthreaded");
+
+ sortThreadedMenuItem.setAttribute("checked", threaded);
+ sortUnthreadedMenuItem.setAttribute("checked", !threaded && !grouped);
+
+ var groupBySortOrderMenuItem = document.getElementById("groupBySort");
+
+ groupBySortOrderMenuItem.setAttribute("disabled", !sortTypeSupportsGrouping);
+ groupBySortOrderMenuItem.setAttribute("checked", grouped);
+}
+
+function InitViewMessagesMenu() {
+ var viewFlags = gDBView ? gDBView.viewFlags : 0;
+ var viewType = gDBView ? gDBView.viewType : 0;
+
+ document.getElementById("viewAllMessagesMenuItem").setAttribute("checked",
+ (viewFlags & Ci.nsMsgViewFlagsType.kUnreadOnly) == 0 &&
+ (viewType == Ci.nsMsgViewType.eShowAllThreads));
+
+ document.getElementById("viewUnreadMessagesMenuItem").setAttribute("checked",
+ (viewFlags & Ci.nsMsgViewFlagsType.kUnreadOnly) != 0);
+
+ document.getElementById("viewThreadsWithUnreadMenuItem").setAttribute("checked",
+ viewType == Ci.nsMsgViewType.eShowThreadsWithUnread);
+
+ document.getElementById("viewWatchedThreadsWithUnreadMenuItem").setAttribute("checked",
+ viewType == Ci.nsMsgViewType.eShowWatchedThreadsWithUnread);
+
+ document.getElementById("viewIgnoredThreadsMenuItem").setAttribute("checked",
+ (viewFlags & Ci.nsMsgViewFlagsType.kShowIgnored) != 0);
+}
+
+function InitMessageMenu() {
+ var selectedMsg = gFolderDisplay.selectedMessage;
+ var isNews = gFolderDisplay.selectedMessageIsNews;
+ var isFeed = gFolderDisplay.selectedMessageIsFeed;
+
+ // We show Reply to Newsgroups only for news messages.
+ document.getElementById("replyNewsgroupMainMenu").hidden = !isNews;
+
+ // We show Reply to List only for list posts.
+ document.getElementById("replyListMainMenu").hidden = isNews || !IsListPost();
+
+ // For mail messages we say reply. For news we say ReplyToSender.
+ document.getElementById("replyMainMenu").hidden = isNews;
+ document.getElementById("replySenderMainMenu").hidden = !isNews;
+
+ // We show Reply to Sender and Newsgroup only for news messages.
+ document.getElementById("replySenderAndNewsgroupMainMenu").hidden = !isNews;
+
+ // For mail messages we say reply all. For news we say ReplyToAllRecipients.
+ document.getElementById("replyallMainMenu").hidden = isNews;
+ document.getElementById("replyAllRecipientsMainMenu").hidden = !isNews;
+
+ // We only show Ignore Thread and Watch Thread menu items for news.
+ document.getElementById("threadItemsSeparator").hidden = !isNews;
+ document.getElementById("killThread").hidden = !isNews;
+ document.getElementById("killSubthread").hidden = !isNews;
+ document.getElementById("watchThread").hidden = !isNews;
+ document.getElementById("menu_cancel").hidden = !isNews;
+
+ // Disable the Move and Copy menus if there are no messages selected.
+ // Disable the Move menu if we can't delete messages from the folder.
+ var msgFolder = GetLoadedMsgFolder();
+ var enableMenuItem = !isNews && selectedMsg &&
+ msgFolder && msgFolder.canDeleteMessages;
+ document.getElementById("moveMenu").disabled = !enableMenuItem;
+
+ // Also disable copy when no folder is loaded (like for .eml files).
+ var canCopy = selectedMsg && (!gMessageDisplay.isDummy ||
+ window.arguments[0].scheme == "file");
+ document.getElementById("copyMenu").disabled = !canCopy;
+
+ // Disable the Forward as/Tag menu items if no message is selected.
+ document.getElementById("forwardAsMenu").disabled = !selectedMsg;
+ document.getElementById("tagMenu").disabled = !selectedMsg;
+
+ // Show "Edit Draft Message" menus only in a drafts folder;
+ // otherwise hide them.
+ showCommandInSpecialFolder("cmd_editDraftMsg", Ci.nsMsgFolderFlags.Drafts);
+ // Show "New Message from Template" and "Edit Template" menus only in a
+ // templates folder; otherwise hide them.
+ showCommandInSpecialFolder("cmd_newMsgFromTemplate",
+ Ci.nsMsgFolderFlags.Templates);
+ showCommandInSpecialFolder("cmd_editTemplateMsg",
+ Ci.nsMsgFolderFlags.Templates);
+
+ // Initialize the Open Message menuitem
+ var winType = document.documentElement.getAttribute("windowtype");
+ if (winType == "mail:3pane")
+ document.getElementById("openMessageWindowMenuitem").hidden = isFeed;
+
+ // Initialize the Open Feed Message handler menu
+ let index = FeedMessageHandler.onOpenPref;
+ document.getElementById("menu_openFeedMessage")
+ .childNodes[index].setAttribute("checked", true);
+
+ let openRssMenu = document.getElementById("openFeedMessage");
+ openRssMenu.hidden = !isFeed;
+ if (winType != "mail:3pane")
+ openRssMenu.hidden = true;
+
+ // Disable the Mark menu when we're not in a folder.
+ document.getElementById("markMenu").disabled = !msgFolder;
+
+ document.commandDispatcher.updateCommands("create-menu-message");
+}
+
+/**
+ * Show folder-specific menu items only for messages in special folders, e.g.
+ * show 'cmd_editDraftMsg' in Drafts folder.
+ * show 'cmd_newMsgFromTemplate' in Templates folder.
+ *
+ * aCommandId the ID of a command to be shown in folders having aFolderFlag
+ * aFolderFlag the nsMsgFolderFlag that the folder must have to show the
+ * command
+ */
+function showCommandInSpecialFolder(aCommandId, aFolderFlag) {
+ let msg = gFolderDisplay.selectedMessage;
+ let folder = gFolderDisplay.displayedFolder;
+ // Check msg.folder exists as messages opened from a file have none.
+ let inSpecialFolder = (msg &&
+ msg.folder &&
+ msg.folder.isSpecialFolder(aFolderFlag, true)) ||
+ (folder && folder.getFlag(aFolderFlag));
+ document.getElementById(aCommandId).setAttribute("hidden", !inSpecialFolder);
+ return inSpecialFolder;
+}
+
+function InitViewHeadersMenu() {
+ var headerchoice =
+ Services.prefs.getIntPref("mail.show_headers",
+ Ci.nsMimeHeaderDisplayTypes.NormalHeaders);
+ document
+ .getElementById("cmd_viewAllHeader")
+ .setAttribute("checked",
+ headerchoice == Ci.nsMimeHeaderDisplayTypes.AllHeaders);
+ document
+ .getElementById("cmd_viewNormalHeader")
+ .setAttribute("checked",
+ headerchoice == Ci.nsMimeHeaderDisplayTypes.NormalHeaders);
+ document.commandDispatcher.updateCommands("create-menu-mark");
+}
+
+function InitViewBodyMenu() {
+ // Separate render prefs not implemented for feeds, bug 458606. Show the
+ // checked item for feeds as for the regular pref.
+ // let html_as = Services.prefs.getIntPref("rss.display.html_as");
+ // let prefer_plaintext = Services.prefs.getBoolPref("rss.display.prefer_plaintext");
+ // let disallow_classes = Services.prefs.getIntPref("rss.display.disallow_mime_handlers");
+
+ let html_as = Services.prefs.getIntPref("mailnews.display.html_as");
+ let prefer_plaintext = Services.prefs.getBoolPref("mailnews.display.prefer_plaintext");
+ let disallow_classes = Services.prefs.getIntPref("mailnews.display.disallow_mime_handlers");
+ let isFeed = gFolderDisplay.selectedMessageIsFeed;
+ const defaultIDs = ["bodyAllowHTML",
+ "bodySanitized",
+ "bodyAsPlaintext",
+ "bodyAllParts"];
+ const rssIDs = ["bodyFeedSummaryAllowHTML",
+ "bodyFeedSummarySanitized",
+ "bodyFeedSummaryAsPlaintext"];
+ let menuIDs = isFeed ? rssIDs : defaultIDs;
+
+ if (disallow_classes > 0)
+ gDisallow_classes_no_html = disallow_classes;
+ // else gDisallow_classes_no_html keeps its inital value (see top)
+
+ let AllowHTML_menuitem = document.getElementById(menuIDs[0]);
+ let Sanitized_menuitem = document.getElementById(menuIDs[1]);
+ let AsPlaintext_menuitem = document.getElementById(menuIDs[2]);
+ let AllBodyParts_menuitem;
+ if (!isFeed) {
+ AllBodyParts_menuitem = document.getElementById(menuIDs[3]);
+ AllBodyParts_menuitem.hidden =
+ !Services.prefs.getBoolPref("mailnews.display.show_all_body_parts_menu");
+ }
+
+ if (!prefer_plaintext && !html_as && !disallow_classes &&
+ AllowHTML_menuitem)
+ AllowHTML_menuitem.setAttribute("checked", true);
+ else if (!prefer_plaintext && html_as == 3 && disallow_classes > 0 &&
+ Sanitized_menuitem)
+ Sanitized_menuitem.setAttribute("checked", true);
+ else if (prefer_plaintext && html_as == 1 && disallow_classes > 0 &&
+ AsPlaintext_menuitem)
+ AsPlaintext_menuitem.setAttribute("checked", true);
+ else if (!prefer_plaintext && html_as == 4 && !disallow_classes &&
+ AllBodyParts_menuitem)
+ AllBodyParts_menuitem.setAttribute("checked", true);
+ // else (the user edited prefs/user.js) check none of the radio menu items
+
+ if (isFeed) {
+ AllowHTML_menuitem.hidden = !FeedMessageHandler.gShowSummary;
+ Sanitized_menuitem.hidden = !FeedMessageHandler.gShowSummary;
+ AsPlaintext_menuitem.hidden = !FeedMessageHandler.gShowSummary;
+ document.getElementById("viewFeedSummarySeparator").hidden = !FeedMessageHandler.gShowSummary;
+ }
+}
+
+function SetMenuItemLabel(menuItemId, customLabel) {
+ var menuItem = document.getElementById(menuItemId);
+ if (menuItem)
+ menuItem.setAttribute("label", customLabel);
+}
+
+function RemoveAllMessageTags() {
+ var selectedMessages = gFolderDisplay.selectedMessages;
+ if (!selectedMessages.length)
+ return;
+
+ var messages = [];
+ var tagArray = MailServices.tags.getAllTags();
+
+ var allKeys = "";
+ for (let j = 0; j < tagArray.length; ++j) {
+ if (j)
+ allKeys += " ";
+ allKeys += tagArray[j].key;
+ }
+
+ var prevHdrFolder = null;
+ // this crudely handles cross-folder virtual folders with selected messages
+ // that spans folders, by coalescing consecutive messages in the selection
+ // that happen to be in the same folder. nsMsgSearchDBView does this better,
+ // but nsIMsgDBView doesn't handle commands with arguments, and untag takes a
+ // key argument. Furthermore, we only delete legacy labels and known tags,
+ // keeping other keywords like (non)junk intact.
+
+ for (let i = 0; i < selectedMessages.length; ++i) {
+ var msgHdr = selectedMessages[i];
+ msgHdr.label = 0; // remove legacy label
+ if (prevHdrFolder != msgHdr.folder) {
+ if (prevHdrFolder)
+ prevHdrFolder.removeKeywordsFromMessages(messages, allKeys);
+ messages = [];
+ prevHdrFolder = msgHdr.folder;
+ }
+ messages.push(msgHdr);
+ }
+ if (prevHdrFolder)
+ prevHdrFolder.removeKeywordsFromMessages(messages, allKeys);
+ OnTagsChange();
+}
+
+function InitNewMsgMenu(aPopup) {
+ var identity = null;
+ var folder = GetFirstSelectedMsgFolder();
+ if (folder)
+ identity = getIdentityForServer(folder.server);
+ if (!identity) {
+ let defaultAccount = MailServices.accounts.defaultAccount;
+ if (defaultAccount)
+ identity = defaultAccount.defaultIdentity;
+ }
+
+ // If the identity is not found, use the mail.html_compose pref to
+ // determine the message compose type (HTML or PlainText).
+ var composeHTML = identity ? identity.composeHtml
+ : Services.prefs.getBoolPref("mail.html_compose");
+ const kIDs = {true: "button-newMsgHTML", false: "button-newMsgPlain"};
+ document.getElementById(kIDs[composeHTML]).setAttribute("default", "true");
+ document.getElementById(kIDs[!composeHTML]).removeAttribute("default");
+}
+
+function InitMessageReply(aPopup) {
+ var isNews = gFolderDisplay.selectedMessageIsNews;
+ // For mail messages we say reply. For news we say ReplyToSender.
+ // We show Reply to Newsgroups only for news messages.
+ aPopup.childNodes[0].hidden = isNews; // Reply
+ aPopup.childNodes[1].hidden = isNews || !IsListPost(); // Reply to List
+ aPopup.childNodes[2].hidden = !isNews; // Reply to Newsgroup
+ aPopup.childNodes[3].hidden = !isNews; // Reply to Sender Only
+}
+
+function InitMessageForward(aPopup) {
+ var forwardType = Services.prefs.getIntPref("mail.forward_message_mode");
+
+ if (forwardType != kMsgForwardAsAttachment) {
+ // forward inline is the first menuitem
+ aPopup.firstChild.setAttribute("default", "true");
+ aPopup.lastChild.removeAttribute("default");
+ } else {
+ // attachment is the last menuitem
+ aPopup.lastChild.setAttribute("default", "true");
+ aPopup.firstChild.removeAttribute("default");
+ }
+}
+
+function ToggleMessageTagKey(index) {
+ // toggle the tag state based upon that of the first selected message
+ var msgHdr = gFolderDisplay.selectedMessage;
+ if (!msgHdr)
+ return;
+
+ var tagArray = MailServices.tags.getAllTags();
+ for (var i = 0; i < tagArray.length; ++i) {
+ var key = tagArray[i].key;
+ if (!--index) {
+ // found the key, now toggle its state
+ var curKeys = msgHdr.getStringProperty("keywords");
+ if (msgHdr.label)
+ curKeys += " $label" + msgHdr.label;
+ var addKey = !(" " + curKeys + " ").includes(" " + key + " ");
+ ToggleMessageTag(key, addKey);
+ return;
+ }
+ }
+}
+
+function ToggleMessageTagMenu(target) {
+ var key = target.getAttribute("value");
+ var addKey = target.getAttribute("checked") == "true";
+ ToggleMessageTag(key, addKey);
+}
+
+function ToggleMessageTag(key, addKey) {
+ var messages = [];
+ var selectedMessages = gFolderDisplay.selectedMessages;
+ var toggler = addKey ? "addKeywordsToMessages" : "removeKeywordsFromMessages";
+ var prevHdrFolder = null;
+ // this crudely handles cross-folder virtual folders with selected messages
+ // that spans folders, by coalescing consecutive msgs in the selection
+ // that happen to be in the same folder. nsMsgSearchDBView does this
+ // better, but nsIMsgDBView doesn't handle commands with arguments,
+ // and (un)tag takes a key argument.
+ for (let i = 0; i < selectedMessages.length; ++i) {
+ var msgHdr = selectedMessages[i];
+ if (msgHdr.label) {
+ // Since we touch all these messages anyway, migrate the label now.
+ // If we don't, the thread tree won't always show the correct tag state,
+ // because resetting a label doesn't update the tree anymore...
+ msgHdr.folder.addKeywordsToMessages([msgHdr], "$label" + msgHdr.label);
+ msgHdr.label = 0; // remove legacy label
+ }
+ if (prevHdrFolder != msgHdr.folder) {
+ if (prevHdrFolder)
+ prevHdrFolder[toggler](messages, key);
+ messages = [];
+ prevHdrFolder = msgHdr.folder;
+ }
+ messages.push(msgHdr);
+ }
+ if (prevHdrFolder)
+ prevHdrFolder[toggler](messages, key);
+ OnTagsChange();
+}
+
+function SetMessageTagLabel(menuitem, index, name) {
+ // if a <key> is defined for this tag, use its key as the accesskey
+ // (the key for the tag at index n needs to have the id key_tag<n>)
+ var shortcutkey = document.getElementById("key_tag" + index);
+ var accesskey = shortcutkey ? shortcutkey.getAttribute("key") : "";
+ if (accesskey)
+ menuitem.setAttribute("accesskey", accesskey);
+ var label = gMessengerBundle.getFormattedString("mailnews.tags.format",
+ [accesskey, name]);
+ menuitem.setAttribute("label", label);
+}
+
+function InitMessageTags(menuPopup) {
+ var tagArray = MailServices.tags.getAllTags();
+ var tagCount = tagArray.length;
+
+ // remove any existing non-static entries...
+ var menuseparator = menuPopup.lastChild.previousSibling;
+ for (var i = menuPopup.childNodes.length; i > 4; --i)
+ menuseparator.previousSibling.remove();
+
+ // hide double menuseparator
+ menuseparator.previousSibling.hidden = !tagCount;
+
+ // create label and accesskey for the static remove item
+ var tagRemoveLabel = gMessengerBundle.getString("mailnews.tags.remove");
+ SetMessageTagLabel(menuPopup.firstChild, 0, tagRemoveLabel);
+
+ // now rebuild the list
+ var msgHdr = gFolderDisplay.selectedMessage;
+ var curKeys = msgHdr.getStringProperty("keywords");
+ if (msgHdr.label)
+ curKeys += " $label" + msgHdr.label;
+ for (var i = 0; i < tagCount; ++i) {
+ var taginfo = tagArray[i];
+ var removeKey = (" " + curKeys + " ").includes(" " + taginfo.key + " ");
+ if (taginfo.ordinal.includes("~AUTOTAG") && !removeKey)
+ continue;
+
+ // TODO we want to either remove or "check" the tags that already exist
+ var newMenuItem = document.createElement("menuitem");
+ SetMessageTagLabel(newMenuItem, i + 1, taginfo.tag);
+ newMenuItem.setAttribute("value", taginfo.key);
+ newMenuItem.setAttribute("type", "checkbox");
+ newMenuItem.setAttribute("checked", removeKey);
+ newMenuItem.setAttribute("oncommand", "ToggleMessageTagMenu(event.target);");
+ var color = taginfo.color;
+ if (color)
+ newMenuItem.setAttribute("class", "lc-" + color.substr(1));
+ menuPopup.insertBefore(newMenuItem, menuseparator);
+ }
+}
+
+function InitBackToolbarMenu(menuPopup) {
+ PopulateHistoryMenu(menuPopup, -1);
+}
+
+function InitForwardToolbarMenu(menuPopup) {
+ PopulateHistoryMenu(menuPopup, 1);
+}
+
+function PopulateHistoryMenu(menuPopup, navOffset) {
+ // remove existing entries
+ while (menuPopup.hasChildNodes())
+ menuPopup.lastChild.remove();
+
+ let startPos = messenger.navigatePos;
+ let historyArray = messenger.getNavigateHistory();
+ let maxPos = historyArray.length / 2; // Array consists of pairs.
+ if (GetLoadedMessage())
+ startPos += navOffset;
+
+ // starting from the current entry, march through history until we reach
+ // the array border or our menuitem limit
+ for (var i = startPos, itemCount = 0;
+ (i >= 0) && (i < maxPos) && (itemCount < 25);
+ i += navOffset, ++itemCount) {
+ var menuText = "";
+ let folder = MailUtils.getFolderForURI(historyArray[i * 2 + 1]);
+ if (!IsCurrentLoadedFolder(folder))
+ menuText += folder.prettyName + ": ";
+
+ var msgHdr = messenger.msgHdrFromURI(historyArray[i * 2]);
+ var subject = "";
+ if (msgHdr.flags & Ci.nsMsgMessageFlags.HasRe)
+ subject = "Re: ";
+ if (msgHdr.mime2DecodedSubject)
+ subject += msgHdr.mime2DecodedSubject;
+ if (subject)
+ menuText += subject + " - ";
+ menuText += msgHdr.mime2DecodedAuthor;
+
+ var newMenuItem = document.createElement("menuitem");
+ newMenuItem.setAttribute("label", menuText);
+ newMenuItem.setAttribute("value", i - startPos);
+ newMenuItem.folder = folder;
+ menuPopup.appendChild(newMenuItem);
+ }
+}
+
+function NavigateToUri(target) {
+ var historyIndex = target.getAttribute("value");
+ var msgUri = messenger.getMsgUriAtNavigatePos(historyIndex);
+ let msgHdrKey = messenger.msgHdrFromURI(msgUri).messageKey;
+ messenger.navigatePos += Number(historyIndex);
+ if (target.folder.URI == GetThreadPaneFolder().URI) {
+ gDBView.selectMsgByKey(msgHdrKey);
+ } else {
+ gStartMsgKey = msgHdrKey;
+ SelectMsgFolder(target.folder);
+ }
+}
+
+function InitMessageMark() {
+ document.getElementById("cmd_markAsFlagged")
+ .setAttribute("checked", SelectedMessagesAreFlagged());
+
+ document.commandDispatcher.updateCommands("create-menu-mark");
+}
+
+function UpdateJunkToolbarButton() {
+ var junkButtonDeck = document.getElementById("junk-deck");
+ // Wallpaper over Bug 491676 by using the attribute instead of the property.
+ junkButtonDeck.setAttribute("selectedIndex", SelectedMessagesAreJunk() ? 1 : 0);
+}
+
+function UpdateDeleteToolbarButton(aFolderPaneHasFocus) {
+ var deleteButtonDeck = document.getElementById("delete-deck");
+ var selectedIndex = 0;
+
+ // Never show "Undelete" in the 3-pane for folders, when delete would
+ // apply to the selected folder.
+ if (!aFolderPaneHasFocus && SelectedMessagesAreDeleted())
+ selectedIndex = 1;
+
+ // Wallpaper over Bug 491676 by using the attribute instead of the property.
+ deleteButtonDeck.setAttribute("selectedIndex", selectedIndex);
+}
+
+function UpdateDeleteCommand() {
+ var value = "value";
+ if (SelectedMessagesAreDeleted())
+ value += "IMAPDeleted";
+ if (GetNumSelectedMessages() < 2)
+ value += "Message";
+ else
+ value += "Messages";
+ goSetMenuValue("cmd_delete", value);
+ goSetAccessKey("cmd_delete", value + "AccessKey");
+}
+
+function SelectedMessagesAreDeleted() {
+ var firstSelectedMessage = gFolderDisplay.selectedMessage;
+ return firstSelectedMessage &&
+ (firstSelectedMessage.flags &
+ Ci.nsMsgMessageFlags.IMAPDeleted);
+}
+
+function SelectedMessagesAreJunk() {
+ var firstSelectedMessage = gFolderDisplay.selectedMessage;
+ if (!firstSelectedMessage)
+ return false;
+
+ var junkScore = firstSelectedMessage.getStringProperty("junkscore");
+ return (junkScore != "") && (junkScore != "0");
+}
+
+function SelectedMessagesAreRead() {
+ let messages = gFolderDisplay.selectedMessages;
+ if (messages.length == 0)
+ return undefined;
+ if (messages.every(function(msg) { return msg.isRead; }))
+ return true;
+ if (messages.every(function(msg) { return !msg.isRead; }))
+ return false;
+ return undefined;
+}
+
+function SelectedMessagesAreFlagged() {
+ var firstSelectedMessage = gFolderDisplay.selectedMessage;
+ return firstSelectedMessage && firstSelectedMessage.isFlagged;
+}
+
+function getMsgToolbarMenu_init() {
+ document.commandDispatcher.updateCommands("create-menu-getMsgToolbar");
+}
+
+function GetFirstSelectedMsgFolder() {
+ var selectedFolders = GetSelectedMsgFolders();
+ return (selectedFolders.length > 0) ? selectedFolders[0] : null;
+}
+
+function GetInboxFolder(server) {
+ try {
+ var rootMsgFolder = server.rootMsgFolder;
+
+ // Now find Inbox.
+ return rootMsgFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Inbox);
+ } catch (ex) {
+ dump(ex + "\n");
+ }
+ return null;
+}
+
+function GetMessagesForInboxOnServer(server) {
+ var inboxFolder = GetInboxFolder(server);
+
+ // If the server doesn't support an inbox it could be an RSS server or
+ // some other server type, just use the root folder and the server
+ // implementation can figure out what to do.
+ if (!inboxFolder)
+ inboxFolder = server.rootFolder;
+
+ GetNewMsgs(server, inboxFolder);
+}
+
+function MsgGetMessage() {
+ // if offline, prompt for getting messages
+ if (DoGetNewMailWhenOffline())
+ GetFolderMessages();
+}
+
+function MsgGetMessagesForAllServers(defaultServer) {
+ MailTasksGetMessagesForAllServers(true, msgWindow, defaultServer);
+}
+
+/**
+ * Get messages for all those accounts which have the capability
+ * of getting messages and have session password available i.e.,
+ * curretnly logged in accounts.
+ * if offline, prompt for getting messages.
+ */
+function MsgGetMessagesForAllAuthenticatedAccounts() {
+ if (DoGetNewMailWhenOffline())
+ MailTasksGetMessagesForAllServers(false, msgWindow, null);
+}
+
+/**
+ * Get messages for the account selected from Menu dropdowns.
+ * if offline, prompt for getting messages.
+ *
+ * @param aFolder (optional) a folder in the account for which messages should
+ * be retrieved. If null, all accounts will be used.
+ */
+function MsgGetMessagesForAccount(aFolder) {
+ if (!aFolder) {
+ goDoCommand("cmd_getNewMessages");
+ return;
+ }
+
+ if (DoGetNewMailWhenOffline())
+ GetMessagesForInboxOnServer(aFolder.server);
+}
+
+// if offline, prompt for getNextNMessages
+function MsgGetNextNMessages() {
+ if (DoGetNewMailWhenOffline()) {
+ var folder = GetFirstSelectedMsgFolder();
+ if (folder)
+ GetNextNMessages(folder);
+ }
+}
+
+function MsgDeleteMessage(aReallyDelete) {
+ // If the user deletes a message before its mark as read timer goes off,
+ // we should mark it as read (unless the user changed the pref). This
+ // ensures that we clear the biff indicator from the system tray when
+ // the user deletes the new message.
+ if (Services.prefs.getBoolPref("mailnews.ui.deleteMarksRead"))
+ MarkSelectedMessagesRead(true);
+ SetNextMessageAfterDelete();
+
+ // determine if we're using the IMAP delete model
+ var server = GetFirstSelectedMsgFolder().server;
+ const kIMAPDelete = Ci.nsMsgImapDeleteModels.IMAPDelete;
+ var imapDeleteModelUsed = server instanceof Ci.nsIImapIncomingServer &&
+ server.deleteModel == kIMAPDelete;
+
+ // execute deleteNoTrash only if IMAP delete model is not used
+ if (aReallyDelete && !imapDeleteModelUsed)
+ gDBView.doCommand(nsMsgViewCommandType.deleteNoTrash);
+ else
+ gDBView.doCommand(nsMsgViewCommandType.deleteMsg);
+}
+
+/**
+ * Copies the selected messages to the destination folder
+ * @param aDestFolder the destination folder
+ */
+function MsgCopyMessage(aDestFolder) {
+ if (gMessageDisplay.isDummy) {
+ let file = window.arguments[0].QueryInterface(Ci.nsIFileURL).file;
+ MailServices.copy.copyFileMessage(file, aDestFolder, null, false,
+ Ci.nsMsgMessageFlags.Read,
+ "", null, msgWindow);
+ } else {
+ gDBView.doCommandWithFolder(nsMsgViewCommandType.copyMessages, aDestFolder);
+ }
+}
+
+/**
+ * Moves the selected messages to the destination folder
+ * @param aDestFolder the destination folder
+ */
+function MsgMoveMessage(aDestFolder) {
+ SetNextMessageAfterDelete();
+ gDBView.doCommandWithFolder(nsMsgViewCommandType.moveMessages, aDestFolder);
+}
+
+/**
+ * Calls the ComposeMessage function with the desired type and proper default
+ * based on the event that fired it.
+ *
+ * @param aCompType The nsIMsgCompType to pass to the function.
+ * @param aEvent (optional) The event that triggered the call.
+ * @param aFormat (optional) Override the message format.
+ */
+function ComposeMsgByType(aCompType, aEvent, aFormat) {
+ var format = aFormat || ((aEvent && aEvent.shiftKey) ? msgComposeFormat.OppositeOfDefault : msgComposeFormat.Default);
+
+ ComposeMessage(aCompType,
+ format,
+ GetFirstSelectedMsgFolder(),
+ gFolderDisplay ? gFolderDisplay.selectedMessageUris : null);
+}
+
+function MsgNewMessage(aEvent) {
+ var mode = aEvent && aEvent.target.getAttribute("mode");
+ ComposeMsgByType(msgComposeType.New, aEvent, mode && msgComposeFormat[mode]);
+}
+
+function MsgReplyMessage(aEvent) {
+ if (gFolderDisplay.selectedMessageIsNews)
+ MsgReplyGroup(aEvent);
+ else if (!gFolderDisplay.selectedMessageIsFeed)
+ MsgReplySender(aEvent);
+}
+
+function MsgReplyList(aEvent) {
+ ComposeMsgByType(msgComposeType.ReplyToList, aEvent);
+}
+
+function MsgReplyGroup(aEvent) {
+ ComposeMsgByType(msgComposeType.ReplyToGroup, aEvent);
+}
+
+function MsgReplySender(aEvent) {
+ ComposeMsgByType(msgComposeType.ReplyToSender, aEvent);
+}
+
+function MsgReplyToAllMessage(aEvent) {
+ var loadedFolder = GetLoadedMsgFolder();
+ var server = loadedFolder.server;
+
+ if (server && server.type == "nntp")
+ MsgReplyToSenderAndGroup(aEvent);
+ else
+ MsgReplyToAllRecipients(aEvent);
+}
+
+function MsgReplyToAllRecipients(aEvent) {
+ ComposeMsgByType(msgComposeType.ReplyAll, aEvent);
+}
+
+function MsgReplyToSenderAndGroup(aEvent) {
+ ComposeMsgByType(msgComposeType.ReplyToSenderAndGroup, aEvent);
+}
+
+
+// Message Archive function
+
+function BatchMessageMover() {
+ this._batches = {};
+ this._currentKey = null;
+ this._dstFolderParent = null;
+ this._dstFolderName = null;
+}
+
+BatchMessageMover.prototype =
+{
+ archiveMessages(aMsgHdrs) {
+ if (!aMsgHdrs.length)
+ return;
+
+ // We need to get the index of the message to select after archiving
+ // completes but reset the global variable to prevent the DBview from
+ // updating the selection; we'll do it manually at the end of
+ // processNextBatch.
+ SetNextMessageAfterDelete();
+ this.messageToSelectAfterWereDone = gNextMessageViewIndexAfterDelete;
+ gNextMessageViewIndexAfterDelete = -2;
+
+ for (let i = 0; i < aMsgHdrs.length; ++i) {
+ let msgHdr = aMsgHdrs[i];
+ let server = msgHdr.folder.server;
+ let msgDate = new Date(msgHdr.date / 1000); // convert date to JS date object
+ let msgYear = msgDate.getFullYear().toString();
+ let monthFolderName = msgYear + "-" + (msgDate.getMonth() + 1).toString().padStart(2, "0");
+
+ let archiveFolderUri;
+ let archiveGranularity;
+ let archiveKeepFolderStructure;
+ if (server.type == "rss") {
+ // RSS servers don't have an identity so we special case the archives URI.
+ archiveFolderUri = server.serverURI + "/Archives";
+ archiveGranularity =
+ Services.prefs.getIntPref("mail.identity.default.archive_granularity");
+ archiveKeepFolderStructure =
+ Services.prefs.getBoolPref("mail.identity.default.archive_keep_folder_structure");
+ } else {
+ let identity = GetIdentityForHeader(msgHdr,
+ Ci.nsIMsgCompType.ReplyAll);
+ archiveFolderUri = identity.archiveFolder;
+ archiveGranularity = identity.archiveGranularity;
+ archiveKeepFolderStructure = identity.archiveKeepFolderStructure;
+ }
+ let archiveFolder = MailUtils.getFolderForURI(archiveFolderUri, false);
+
+ let copyBatchKey = msgHdr.folder.URI + "\0" + monthFolderName;
+ if (!(copyBatchKey in this._batches))
+ this._batches[copyBatchKey] = [msgHdr.folder,
+ archiveFolderUri,
+ archiveGranularity,
+ archiveKeepFolderStructure,
+ msgYear,
+ monthFolderName];
+ this._batches[copyBatchKey].push(msgHdr);
+ }
+
+ MailServices.mfn.addListener(this, MailServices.mfn.folderAdded);
+
+ // Now we launch the code iterating over all message copies, one in turn.
+ this.processNextBatch();
+ },
+
+ processNextBatch() {
+ for (let key in this._batches) {
+ this._currentBatch = this._batches[key];
+ delete this._batches[key];
+ return this.filterBatch();
+ }
+
+ // all done
+ MailServices.mfn.removeListener(this);
+
+ // We're just going to select the message now.
+ let treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ treeView.selection.select(this.messageToSelectAfterWereDone);
+ treeView.selectionChanged();
+
+ },
+
+ filterBatch() {
+ let batch = this._currentBatch;
+ // Apply filters to this batch.
+ let msgs = batch.slice(6);
+ let srcFolder = batch[0];
+ MailServices.filters.applyFilters(
+ Ci.nsMsgFilterType.Archive,
+ msgs, srcFolder, msgWindow, this);
+ // continues with onStopOperation
+ },
+
+ onStopOperation(aResult) {
+ if (!Components.isSuccessCode(aResult)) {
+ Cu.reportError("Archive filter failed: " + aResult);
+ // We don't want to effectively disable archiving because a filter
+ // failed, so we'll continue after reporting the error.
+ }
+ // Now do the default archive processing
+ this.continueBatch();
+ },
+
+ // continue processing of default archive operations
+ continueBatch() {
+ let batch = this._currentBatch;
+ let [srcFolder, archiveFolderUri, granularity, keepFolderStructure, msgYear, msgMonth] = batch;
+ let msgs = batch.slice(6);
+
+ let moveArray = [];
+ // Don't move any items that the filter moves or deleted
+ for (let item of msgs) {
+ if (srcFolder.msgDatabase.ContainsKey(item.messageKey) &&
+ !(srcFolder.getProcessingFlags(item.messageKey) &
+ Ci.nsMsgProcessingFlags.FilterToMove)) {
+ moveArray.push(item);
+ }
+ }
+
+ if (moveArray.length == 0)
+ return this.processNextBatch(); // continue processing
+
+ let archiveFolder = MailUtils.getFolderForURI(archiveFolderUri, false);
+ let dstFolder = archiveFolder;
+ // For folders on some servers (e.g. IMAP), we need to create the
+ // sub-folders asynchronously, so we chain the urls using the listener
+ // called back from createStorageIfMissing. For local,
+ // createStorageIfMissing is synchronous.
+ let isAsync = archiveFolder.server.protocolInfo.foldersCreatedAsync;
+ if (!archiveFolder.parent) {
+ archiveFolder.setFlag(Ci.nsMsgFolderFlags.Archive);
+ archiveFolder.createStorageIfMissing(this);
+ if (isAsync)
+ return; // continues with OnStopRunningUrl
+ }
+ if (!archiveFolder.canCreateSubfolders)
+ granularity = Ci.nsIMsgIdentity.singleArchiveFolder;
+ if (granularity >= Ci.nsIMsgIdentity.perYearArchiveFolders) {
+ archiveFolderUri += "/" + msgYear;
+ dstFolder = MailUtils.getFolderForURI(archiveFolderUri, false);
+ if (!dstFolder.parent) {
+ dstFolder.createStorageIfMissing(this);
+ if (isAsync)
+ return; // continues with OnStopRunningUrl
+ }
+ }
+ if (granularity >= Ci.nsIMsgIdentity.perMonthArchiveFolders) {
+ archiveFolderUri += "/" + msgMonth;
+ dstFolder = MailUtils.getFolderForURI(archiveFolderUri, false);
+ if (!dstFolder.parent) {
+ dstFolder.createStorageIfMissing(this);
+ if (isAsync)
+ return; // continues with OnStopRunningUrl
+ }
+ }
+
+ // Create the folder structure in Archives.
+ // For imap folders, we need to create the sub-folders asynchronously,
+ // so we chain the actions using the listener called back from
+ // createSubfolder. For local, createSubfolder is synchronous.
+ if (archiveFolder.canCreateSubfolders && keepFolderStructure) {
+ // Collect in-order list of folders of source folder structure,
+ // excluding top-level INBOX folder
+ let folderNames = [];
+ let rootFolder = srcFolder.server.rootFolder;
+ let inboxFolder = GetInboxFolder(srcFolder.server);
+ let folder = srcFolder;
+ while (folder != rootFolder && folder != inboxFolder) {
+ folderNames.unshift(folder.name);
+ folder = folder.parent;
+ }
+ // Determine Archive folder structure.
+ for (let i = 0; i < folderNames.length; ++i) {
+ let folderName = folderNames[i];
+ if (!dstFolder.containsChildNamed(folderName)) {
+ // Create Archive sub-folder (IMAP: async).
+ if (isAsync) {
+ this._dstFolderParent = dstFolder;
+ this._dstFolderName = folderName;
+ }
+ dstFolder.createSubfolder(folderName, msgWindow);
+ if (isAsync)
+ return; // continues with folderAdded
+ }
+ dstFolder = dstFolder.getChildNamed(folderName);
+ }
+ }
+
+ if (dstFolder != srcFolder) {
+ // Make sure the target folder is visible in the folder tree.
+ EnsureFolderIndex(gFolderTreeView, dstFolder);
+
+ let isNews = srcFolder.flags & Ci.nsMsgFolderFlags.Newsgroup;
+
+ // If the source folder doesn't support deleting messages, we
+ // make archive a copy, not a move.
+ MailServices.copy.copyMessages(srcFolder, moveArray, dstFolder,
+ srcFolder.canDeleteMessages && !isNews,
+ this, msgWindow, true);
+ return; // continues with OnStopCopy
+ }
+ return this.processNextBatch();
+ },
+
+
+ // This also implements nsIUrlListener, but we only care about the
+ // OnStopRunningUrl (createStorageIfMissing callback).
+ OnStartRunningUrl(aUrl) {
+ },
+ OnStopRunningUrl(aUrl, aExitCode) {
+ // This will always be a create folder url, afaik.
+ if (Components.isSuccessCode(aExitCode))
+ this.continueBatch();
+ else {
+ Cu.reportError("Archive failed to create folder: " + aExitCode);
+ this._batches = null;
+ this.processNextBatch(); // for cleanup and exit
+ }
+ },
+
+ // This also implements nsIMsgCopyServiceListener, but we only care
+ // about the OnStopCopy (copyMessages callback).
+ OnStartCopy() {
+ },
+ OnProgress(aProgress, aProgressMax) {
+ },
+ SetMessageKey(aKey) {
+ },
+ GetMessageId() {
+ },
+ OnStopCopy(aStatus) {
+ if (Components.isSuccessCode(aStatus)) {
+ return this.processNextBatch();
+ }
+
+ Cu.reportError("Archive failed to copy: " + aStatus);
+ this._batches = null;
+ this.processNextBatch(); // for cleanup and exit
+
+ },
+
+ // This also implements nsIMsgFolderListener, but we only care about the
+ // folderAdded (createSubfolder callback).
+ folderAdded(aFolder) {
+ // Check that this is the folder we're interested in.
+ if (aFolder.parent == this._dstFolderParent &&
+ aFolder.name == this._dstFolderName) {
+ this._dstFolderParent = null;
+ this._dstFolderName = null;
+ this.continueBatch();
+ }
+ },
+
+ QueryInterface(aIID) {
+ if (aIID.equals(Ci.nsIUrlListener) ||
+ aIID.equals(Ci.nsIMsgCopyServiceListener) ||
+ aIID.equals(Ci.nsIMsgFolderListener) ||
+ aIID.equals(Ci.nsIMsgOperationListener) ||
+ aIID.equals(Ci.nsISupports))
+ return this;
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+function MsgArchiveSelectedMessages(aEvent) {
+ let batchMover = new BatchMessageMover();
+ batchMover.archiveMessages(gFolderDisplay.selectedMessages);
+}
+
+
+function MsgForwardMessage(event) {
+ var forwardType = Services.prefs.getIntPref("mail.forward_message_mode");
+
+ // mail.forward_message_mode could be 1, if the user migrated from 4.x
+ // 1 (forward as quoted) is obsolete, so we treat is as forward inline
+ // since that is more like forward as quoted then forward as attachment
+ if (forwardType == kMsgForwardAsAttachment)
+ MsgForwardAsAttachment(event);
+ else
+ MsgForwardAsInline(event);
+}
+
+function MsgForwardAsAttachment(event) {
+ ComposeMsgByType(msgComposeType.ForwardAsAttachment, event);
+}
+
+function MsgForwardAsInline(event) {
+ ComposeMsgByType(msgComposeType.ForwardInline, event);
+}
+
+function MsgEditMessageAsNew(aEvent) {
+ ComposeMsgByType(msgComposeType.EditAsNew, aEvent);
+}
+
+function MsgEditDraftMessage(aEvent) {
+ ComposeMsgByType(msgComposeType.Draft, aEvent);
+}
+
+function MsgNewMessageFromTemplate(aEvent) {
+ ComposeMsgByType(msgComposeType.Template, aEvent);
+}
+
+function MsgEditTemplateMessage(aEvent) {
+ ComposeMsgByType(msgComposeType.EditTemplate, aEvent);
+}
+
+function MsgComposeDraftMessage() {
+ ComposeMsgByType(msgComposeType.Draft, null, msgComposeFormat.Default);
+}
+
+function MsgCreateFilter() {
+ // retrieve Sender direct from selected message's headers
+ var msgHdr = gFolderDisplay.selectedMessage;
+ var emailAddress =
+ MailServices.headerParser.extractHeaderAddressMailboxes(msgHdr.author);
+ var accountKey = msgHdr.accountKey;
+ var folder;
+ if (accountKey.length > 0) {
+ var account = accountManager.getAccount(accountKey);
+ if (account) {
+ server = account.incomingServer;
+ if (server)
+ folder = server.rootFolder;
+ }
+ }
+ if (!folder)
+ folder = GetFirstSelectedMsgFolder();
+
+ if (emailAddress)
+ top.MsgFilters(emailAddress, folder);
+}
+
+function MsgSubscribe(folder) {
+ var preselectedFolder = folder || GetFirstSelectedMsgFolder();
+
+ if (preselectedFolder && preselectedFolder.server.type == "rss")
+ openSubscriptionsDialog(preselectedFolder); // open feed subscription dialog
+ else
+ Subscribe(preselectedFolder); // open imap/nntp subscription dialog
+}
+
+/**
+ * Show a confirmation dialog - check if the user really want to unsubscribe
+ * from the given newsgroup/s.
+ * @folders an array of newsgroup folders to unsubscribe from
+ * @return true if the user said it's ok to unsubscribe
+ */
+function ConfirmUnsubscribe(folders) {
+ if (!gMessengerBundle)
+ gMessengerBundle = document.getElementById("bundle_messenger");
+
+ let titleMsg = gMessengerBundle.getString("confirmUnsubscribeTitle");
+ let dialogMsg = (folders.length == 1) ?
+ gMessengerBundle.getFormattedString("confirmUnsubscribeText",
+ [folders[0].name], 1) :
+ gMessengerBundle.getString("confirmUnsubscribeManyText");
+
+ return Services.prompt.confirm(window, titleMsg, dialogMsg);
+}
+
+/**
+ * Unsubscribe from selected or passed in newsgroup/s.
+ * @param newsgroups (optional param) the newsgroup folders to unsubscribe from
+ */
+function MsgUnsubscribe(newsgroups) {
+ let folders = newsgroups || GetSelectedMsgFolders();
+ if (!ConfirmUnsubscribe(folders))
+ return;
+
+ for (let folder of folders) {
+ let subscribableServer =
+ folder.server.QueryInterface(Ci.nsISubscribableServer);
+ subscribableServer.unsubscribe(folder.name);
+ subscribableServer.commitSubscribeChanges();
+ }
+}
+
+function ToggleFavoriteFolderFlag() {
+ var folder = GetFirstSelectedMsgFolder();
+ folder.toggleFlag(Ci.nsMsgFolderFlags.Favorite);
+}
+
+function MsgSaveAsFile() {
+ SaveAsFile(gFolderDisplay.selectedMessageUris);
+}
+
+function MsgSaveAsTemplate() {
+ SaveAsTemplate(gFolderDisplay.selectedMessageUris);
+}
+
+function MsgOpenFromFile() {
+ var fp = Cc["@mozilla.org/filepicker;1"]
+ .createInstance(Ci.nsIFilePicker);
+
+ var filterLabel = gMessengerBundle.getString("EMLFiles");
+ var windowTitle = gMessengerBundle.getString("OpenEMLFiles");
+
+ fp.init(window, windowTitle, Ci.nsIFilePicker.modeOpen);
+ fp.appendFilter(filterLabel, "*.eml; *.msg");
+
+ // Default or last filter is "All Files".
+ fp.appendFilters(Ci.nsIFilePicker.filterAll);
+
+ fp.open(rv => {
+ if (rv != Ci.nsIFilePicker.returnOK || !fp.file) {
+ return;
+ }
+ let uri = fp.fileURL.QueryInterface(Ci.nsIURL);
+ uri.query = "type=application/x-message-display";
+
+ window.openDialog("chrome://messenger/content/messageWindow.xul", "_blank",
+ "all,chrome,dialog=no,status,toolbar", uri);
+ });
+}
+
+function MsgOpenNewWindowForFolder(folderURI, msgKeyToSelect) {
+ let mailWindowService = Cc["@mozilla.org/messenger/windowservice;1"]
+ .getService(Ci.nsIMessengerWindowService);
+ if (!mailWindowService)
+ return;
+
+ if (folderURI) {
+ mailWindowService.openMessengerWindowWithUri("mail:3pane", folderURI,
+ msgKeyToSelect);
+ return;
+ }
+
+ // If there is a right-click happening, GetSelectedMsgFolders()
+ // will tell us about it (while the selection's currentIndex would reflect
+ // the node that was selected/displayed before the right-click.)
+ for (let folder of GetSelectedMsgFolders()) {
+ mailWindowService.openMessengerWindowWithUri("mail:3pane", folder.URI,
+ msgKeyToSelect);
+ }
+}
+
+function MsgOpenSelectedMessages() {
+ // Toggle message body (feed summary) and content-base url in message pane or
+ // load in browser, per pref, otherwise open summary or web page in new window
+ // or tab, per that pref.
+ if (gFolderDisplay.selectedMessageIsFeed) {
+ let msgHdr = gFolderDisplay.selectedMessage;
+ if (document.documentElement.getAttribute("windowtype") == "mail:3pane" &&
+ FeedMessageHandler.onOpenPref == FeedMessageHandler.kOpenToggleInMessagePane) {
+ let showSummary = FeedMessageHandler.shouldShowSummary(msgHdr, true);
+ FeedMessageHandler.setContent(msgHdr, showSummary);
+ FeedMessageHandler.onSelectPref =
+ showSummary ? FeedMessageHandler.kSelectOverrideSummary :
+ FeedMessageHandler.kSelectOverrideWebPage;
+ return;
+ }
+ if (FeedMessageHandler.onOpenPref == FeedMessageHandler.kOpenLoadInBrowser) {
+ setTimeout(FeedMessageHandler.loadWebPage, 20, msgHdr, {browser: true});
+ return;
+ }
+ }
+
+ var dbView = GetDBView();
+ var indices = GetSelectedIndices(dbView);
+ var numMessages = indices.length;
+
+ // This is a radio type button pref, currently with only 2 buttons.
+ // We need to keep the pref type as 'bool' for backwards compatibility
+ // with 4.x migrated prefs. For future radio button(s), please use another
+ // pref (either 'bool' or 'int' type) to describe it.
+ //
+ // mailnews.reuse_message_window values:
+ // false: open new standalone message window for each message
+ // true : reuse existing standalone message window for each message
+ if (Services.prefs.getBoolPref("mailnews.reuse_message_window") &&
+ numMessages == 1 &&
+ MsgOpenSelectedMessageInExistingWindow())
+ return;
+
+ var openWindowWarning = Services.prefs.getIntPref("mailnews.open_window_warning");
+ if ((openWindowWarning > 1) && (numMessages >= openWindowWarning)) {
+ InitPrompts();
+ if (!gMessengerBundle)
+ gMessengerBundle = document.getElementById("bundle_messenger");
+ var title = gMessengerBundle.getString("openWindowWarningTitle");
+ var text = PluralForm.get(numMessages,
+ gMessengerBundle.getString("openWindowWarningConfirmation"))
+ .replace("#1", numMessages);
+ if (!Services.prompt.confirm(window, title, text))
+ return;
+ }
+
+ for (var i = 0; i < numMessages; i++) {
+ MsgOpenNewWindowForMessage(dbView.getURIForViewIndex(indices[i]), dbView.getFolderForViewIndex(indices[i]).URI);
+ }
+}
+
+function MsgOpenSelectedMessageInExistingWindow() {
+ var windowID = Services.wm.getMostRecentWindow("mail:messageWindow");
+ if (!windowID)
+ return false;
+
+ try {
+ var messageURI = gDBView.URIForFirstSelectedMessage;
+ var msgHdr = gDBView.hdrForFirstSelectedMessage;
+
+ // Reset the window's message uri and folder uri vars, and
+ // update the command handlers to what's going to be used.
+ // This has to be done before the call to CreateView().
+ windowID.gCurrentMessageUri = messageURI;
+ windowID.gCurrentFolderUri = msgHdr.folder.URI;
+ windowID.UpdateMailToolbar("MsgOpenExistingWindowForMessage");
+
+ // even if the folder uri's match, we can't use the existing view
+ // (msgHdr.folder.URI == windowID.gCurrentFolderUri)
+ // the reason is quick search and mail views.
+ // see bug #187673
+ //
+ // for the sake of simplicity,
+ // let's always call CreateView(gDBView)
+ // which will clone gDBView
+ windowID.CreateView(gDBView);
+ windowID.OnLoadMessageWindowDelayed(false);
+
+ // bring existing window to front
+ windowID.focus();
+ return true;
+ } catch (ex) {
+ dump("reusing existing standalone message window failed: " + ex + "\n");
+ }
+ return false;
+}
+
+function MsgOpenSearch(aSearchStr, aEvent) {
+ // If you change /suite/navigator/navigator.js->BrowserSearch::loadSearch()
+ // make sure you make corresponding changes here.
+ var submission = Services.search.defaultEngine.getSubmission(aSearchStr);
+ if (!submission)
+ return;
+
+ var newTabPref = Services.prefs.getBoolPref("browser.search.opentabforcontextsearch");
+ var where = newTabPref ? aEvent && aEvent.shiftKey ? "tabshifted" : "tab" : "window";
+ openUILinkIn(submission.uri.spec, where, null, submission.postData);
+}
+
+function MsgOpenNewWindowForMessage(messageUri, folderUri) {
+ if (!messageUri)
+ messageUri = gFolderDisplay.selectedMessageUri;
+
+ if (!folderUri)
+ // Use GetSelectedMsgFolders() to find out which message to open
+ // instead of gDBView.getURIForViewIndex(currentIndex). This is
+ // required because on a right-click, the currentIndex value will be
+ // different from the actual row that is highlighted.
+ // GetSelectedMsgFolders() will return the message that is
+ // highlighted.
+ folderUri = GetSelectedMsgFolders()[0].URI;
+
+ // be sure to pass in the current view....
+ if (messageUri && folderUri) {
+ window.openDialog( "chrome://messenger/content/messageWindow.xul", "_blank", "all,chrome,dialog=no,status,toolbar", messageUri, folderUri, gDBView );
+ }
+}
+
+function CloseMailWindow() {
+ window.close();
+}
+
+function MsgJunk() {
+ MsgJunkMailInfo(true);
+ JunkSelectedMessages(!SelectedMessagesAreJunk());
+}
+
+/**
+ * Checks if the selected messages can be marked as read or unread
+ *
+ * @param read true if trying to mark messages as read, false otherwise
+ * @return true if the chosen operation can be performed
+ */
+function CanMarkMsgAsRead(read) {
+ return SelectedMessagesAreRead() != read;
+}
+
+/**
+ * Marks the selected messages as read or unread
+ *
+ * @param read true if trying to mark messages as read, false if marking unread,
+ * undefined if toggling the read status
+ */
+function MsgMarkMsgAsRead(read) {
+ if (read == undefined)
+ read = !SelectedMessagesAreRead();
+ MarkSelectedMessagesRead(read);
+}
+
+function MsgMarkAsFlagged() {
+ MarkSelectedMessagesFlagged(!SelectedMessagesAreFlagged());
+}
+
+function MsgMarkReadByDate() {
+ window.openDialog("chrome://messenger/content/markByDate.xul", "",
+ "chrome,modal,titlebar,centerscreen",
+ GetLoadedMsgFolder());
+}
+
+function MsgMarkAllRead() {
+ let folders = GetSelectedMsgFolders();
+ for (let folder of folders)
+ folder.markAllMessagesRead(msgWindow);
+}
+
+function MsgDownloadFlagged() {
+ gDBView.doCommand(nsMsgViewCommandType.downloadFlaggedForOffline);
+}
+
+function MsgDownloadSelected() {
+ gDBView.doCommand(nsMsgViewCommandType.downloadSelectedForOffline);
+}
+
+function MsgMarkThreadAsRead() {
+ ClearPendingReadTimer();
+ gDBView.doCommand(nsMsgViewCommandType.markThreadRead);
+}
+
+function MsgViewPageSource() {
+ ViewPageSource(gFolderDisplay.selectedMessageUris);
+}
+
+var gFindInstData;
+function getFindInstData() {
+ if (!gFindInstData) {
+ gFindInstData = new nsFindInstData();
+ gFindInstData.browser = getMessageBrowser();
+ gFindInstData.rootSearchWindow = window.top.content;
+ gFindInstData.currentSearchWindow = window.top.content;
+ }
+ return gFindInstData;
+}
+
+function MsgFind() {
+ findInPage(getFindInstData());
+}
+
+function MsgFindAgain(reverse) {
+ findAgainInPage(getFindInstData(), reverse);
+}
+
+function MsgCanFindAgain() {
+ return canFindAgainInPage();
+}
+
+/**
+ * Go through each selected server and mark all its folders read.
+ */
+function MsgMarkAllFoldersRead() {
+ if (!Services.prompt.confirm(window,
+ gMessengerBundle.getString("confirmMarkAllFoldersReadTitle"),
+ gMessengerBundle.getString("confirmMarkAllFoldersReadMessage"))) {
+ return;
+ }
+
+ const selectedFolders = GetSelectedMsgFolders();
+ if (selectedFolders) {
+ const selectedServers = selectedFolders.filter(folder => folder.isServer);
+
+ selectedServers.forEach(function(server) {
+ for (let folder of server.rootFolder.descendants) {
+ folder.markAllMessagesRead(msgWindow);
+ }
+ });
+ }
+}
+
+function MsgFilters(emailAddress, folder) {
+ if (!folder)
+ folder = GetFirstSelectedMsgFolder();
+ var args;
+ if (emailAddress) {
+ // Prefill the filterEditor with the emailAddress.
+ args = {filterList: folder.getEditableFilterList(msgWindow), filterName: emailAddress};
+ window.openDialog("chrome://messenger/content/FilterEditor.xul", "",
+ "chrome, modal, resizable,centerscreen,dialog", args);
+
+ // If the user hits ok in the filterEditor dialog we set args.refresh=true
+ // there and we check this here in args to show filterList dialog.
+ // We also received the filter created via args.newFilter.
+ if ("refresh" in args && args.refresh) {
+ args = { refresh: true, folder, filter: args.newFilter };
+ MsgFilterList(args);
+ }
+ } else // just launch filterList dialog
+ {
+ args = { refresh: false, folder };
+ MsgFilterList(args);
+ }
+}
+
+function MsgApplyFilters() {
+ var preselectedFolder = GetFirstSelectedMsgFolder();
+
+ var curFilterList = preselectedFolder.getFilterList(msgWindow);
+ // create a new filter list and copy over the enabled filters to it.
+ // We do this instead of having the filter after the fact code ignore
+ // disabled filters because the Filter Dialog filter after the fact
+ // code would have to clone filters to allow disabled filters to run,
+ // and we don't support cloning filters currently.
+ var tempFilterList =
+ MailServices.filters.getTempFilterList(preselectedFolder);
+ var numFilters = curFilterList.filterCount;
+ // make sure the temp filter list uses the same log stream
+ tempFilterList.loggingEnabled = curFilterList.loggingEnabled;
+ tempFilterList.logStream = curFilterList.logStream;
+ var newFilterIndex = 0;
+ for (var i = 0; i < numFilters; i++) {
+ var curFilter = curFilterList.getFilterAt(i);
+ // only add enabled, UI visibile filters that are in the manual context
+ if (curFilter.enabled && !curFilter.temporary &&
+ (curFilter.filterType & Ci.nsMsgFilterType.Manual)) {
+ tempFilterList.insertFilterAt(newFilterIndex, curFilter);
+ newFilterIndex++;
+ }
+ }
+ MailServices.filters.applyFiltersToFolders(tempFilterList,
+ [preselectedFolder],
+ msgWindow);
+}
+
+function MsgApplyFiltersToSelection() {
+ var folder = gDBView.msgFolder;
+ var indices = GetSelectedIndices(gDBView);
+ if (indices && indices.length) {
+ var selectedMsgs = [];
+ for (var i = 0; i < indices.length; i++) {
+ try {
+ // Getting the URI will tell us if the item is real or a dummy header
+ var uri = gDBView.getURIForViewIndex(indices[i]);
+ if (uri) {
+ var msgHdr = folder.GetMessageHeader(gDBView.getKeyAt(indices[i]));
+ if (msgHdr)
+ selectedMsgs.push(msgHdr);
+ }
+ } catch (ex) {}
+ }
+
+ MailServices.filters.applyFilters(Ci.nsMsgFilterType.Manual, selectedMsgs,
+ folder, msgWindow);
+ }
+}
+
+function ChangeMailLayout(newLayout) {
+ Services.prefs.setIntPref("mail.pane_config.dynamic", newLayout);
+}
+
+function MsgViewAllHeaders() {
+ Services.prefs.setIntPref("mail.show_headers",
+ Ci.nsMimeHeaderDisplayTypes.AllHeaders);
+}
+
+function MsgViewNormalHeaders() {
+ Services.prefs.setIntPref("mail.show_headers",
+ Ci.nsMimeHeaderDisplayTypes.NormalHeaders);
+}
+
+function MsgBodyAllowHTML() {
+ ChangeMsgBodyDisplay(false, 0, 0);
+}
+
+function MsgBodySanitized() {
+ ChangeMsgBodyDisplay(false, 3, gDisallow_classes_no_html);
+}
+
+function MsgBodyAsPlaintext() {
+ ChangeMsgBodyDisplay(true, 1, gDisallow_classes_no_html);
+}
+
+function MsgBodyAllParts() {
+ ChangeMsgBodyDisplay(false, 4, 0);
+}
+
+function ChangeMsgBodyDisplay(plaintext, html, mime) {
+ Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", plaintext);
+ Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", mime);
+ Services.prefs.setIntPref("mailnews.display.html_as", html);
+}
+
+function MsgFeedBodyRenderPrefs(plaintext, html, mime) {
+ // Separate render prefs not implemented for feeds, bug 458606.
+ // Services.prefs.setBoolPref("rss.display.prefer_plaintext", plaintext);
+ // Services.prefs.setIntPref("rss.display.disallow_mime_handlers", mime);
+ // Services.prefs.setIntPref("rss.display.html_as", html)
+
+ Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", plaintext);
+ Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", mime);
+ Services.prefs.setIntPref("mailnews.display.html_as", html);
+}
+
+function ToggleInlineAttachment(target) {
+ var viewInline = !Services.prefs.getBoolPref("mail.inline_attachments");
+ Services.prefs.setBoolPref("mail.inline_attachments", viewInline);
+ target.setAttribute("checked", viewInline ? "true" : "false");
+}
+
+function MsgStop() {
+ StopUrls();
+}
+
+function MsgSendUnsentMsgs() {
+ // if offline, prompt for sendUnsentMessages
+ if (!Services.io.offline) {
+ SendUnsentMessages();
+ } else {
+ var option = PromptMessagesOffline("send");
+ if (option == 0) {
+ if (!gOfflineManager)
+ GetOfflineMgrService();
+ gOfflineManager.goOnline(false /* sendUnsentMessages */,
+ false /* playbackOfflineImapOperations */,
+ msgWindow);
+ SendUnsentMessages();
+ }
+ }
+}
+
+function PrintEnginePrintInternal(aDoPrintPreview, aMsgType) {
+ var messageList = gFolderDisplay.selectedMessageUris;
+ if (!messageList) {
+ dump("PrintEnginePrint(): No messages selected.\n");
+ return false;
+ }
+
+ window.openDialog("chrome://messenger/content/msgPrintEngine.xul", "",
+ "chrome,dialog=no,all,centerscreen",
+ messageList.length, messageList, statusFeedback,
+ aDoPrintPreview, aMsgType);
+ return true;
+
+}
+
+function PrintEnginePrint() {
+ return PrintEnginePrintInternal(false, Ci.nsIMsgPrintEngine.MNAB_PRINT_MSG);
+}
+
+function PrintEnginePrintPreview() {
+ return PrintEnginePrintInternal(true, Ci.nsIMsgPrintEngine.MNAB_PRINTPREVIEW_MSG);
+}
+
+// Kept for add-on compatibility.
+function SelectFolder(folderUri) {
+ SelectMsgFolder(MailUtils.getFolderForURI(folderUri));
+}
+
+function IsMailFolderSelected() {
+ var selectedFolders = GetSelectedMsgFolders();
+ var folder = selectedFolders.length ? selectedFolders[0] : null;
+ return folder && folder.server.type != "nntp";
+}
+
+function IsGetNewMessagesEnabled() {
+ // users don't like it when the "Get Msgs" button is disabled
+ // so let's never do that.
+ // we'll just handle it as best we can in GetFolderMessages()
+ // when they click "Get Msgs" and
+ // Local Folders or a news server is selected
+ // see bugs #89404 and #111102
+ return true;
+}
+
+function IsGetNextNMessagesEnabled() {
+ var selectedFolders = GetSelectedMsgFolders();
+ var folder = selectedFolders.length ? selectedFolders[0] : null;
+
+ var menuItem = document.getElementById("menu_getnextnmsg");
+ if (folder && !folder.isServer &&
+ folder.server instanceof Ci.nsINntpIncomingServer) {
+ var menuLabel = PluralForm.get(folder.server.maxArticles,
+ gMessengerBundle.getString("getNextNewsMessages"))
+ .replace("#1", folder.server.maxArticles);
+ menuItem.setAttribute("label", menuLabel);
+ menuItem.removeAttribute("hidden");
+ return true;
+ }
+
+ menuItem.setAttribute("hidden", "true");
+ return false;
+}
+
+function SetUpToolbarButtons(uri) {
+ let deleteButton = document.getElementById("button-delete");
+ let replyAllButton = document.getElementById("button-replyall");
+
+ // Eventually, we might want to set up the toolbar differently for imap,
+ // pop, and news. For now, just tweak it based on if it is news or not.
+ let forNews = isNewsURI(uri);
+
+ deleteButton.hidden = forNews;
+ if (forNews) {
+ replyAllButton.setAttribute("type", "menu-button");
+ replyAllButton.setAttribute("tooltiptext",
+ replyAllButton.getAttribute("tooltiptextnews"));
+ } else {
+ replyAllButton.removeAttribute("type");
+ replyAllButton.setAttribute("tooltiptext",
+ replyAllButton.getAttribute("tooltiptextmail"));
+ }
+}
+
+function getMessageBrowser() {
+ return document.getElementById("messagepane");
+}
+
+// The zoom manager, view source and possibly some other functions still rely
+// on the getBrowser function.
+function getBrowser() {
+ return GetTabMail() ? GetTabMail().getBrowserForSelectedTab() :
+ getMessageBrowser();
+}
+
+function MsgSynchronizeOffline() {
+ window.openDialog("chrome://messenger/content/msgSynchronize.xul", "",
+ "centerscreen,chrome,modal,titlebar,resizable",
+ {msgWindow});
+}
+
+function MsgOpenAttachment() {}
+function MsgUpdateMsgCount() {}
+function MsgImport() {}
+function MsgSynchronize() {}
+function MsgGetSelectedMsg() {}
+function MsgGetFlaggedMsg() {}
+function MsgSelectThread() {}
+function MsgShowFolders() {}
+function MsgShowLocationbar() {}
+function MsgViewAttachInline() {}
+function MsgWrapLongLines() {}
+function MsgIncreaseFont() {}
+function MsgDecreaseFont() {}
+function MsgShowImages() {}
+function MsgRefresh() {}
+function MsgViewPageInfo() {}
+function MsgFirstUnreadMessage() {}
+function MsgFirstFlaggedMessage() {}
+function MsgAddSenderToAddressBook() {}
+function MsgAddAllToAddressBook() {}
+
+function SpaceHit(event) {
+ var contentWindow = document.commandDispatcher.focusedWindow;
+ if (contentWindow.top == window)
+ contentWindow = content;
+ else if (document.commandDispatcher.focusedElement &&
+ !hrefAndLinkNodeForClickEvent(event))
+ return;
+ var rssiframe = content.document.getElementById("_mailrssiframe");
+
+ // If we are displaying an RSS article, we really want to scroll
+ // the nested iframe.
+ if (contentWindow == content && rssiframe)
+ contentWindow = rssiframe.contentWindow;
+
+ if (event && event.shiftKey) {
+ // if at the start of the message, go to the previous one
+ if (contentWindow.scrollY > 0)
+ contentWindow.scrollByPages(-1);
+ else if (Services.prefs.getBoolPref("mail.advance_on_spacebar"))
+ goDoCommand("cmd_previousUnreadMsg");
+ } else {
+ // if at the end of the message, go to the next one
+ if (contentWindow.scrollY < contentWindow.scrollMaxY)
+ contentWindow.scrollByPages(1);
+ else if (Services.prefs.getBoolPref("mail.advance_on_spacebar"))
+ goDoCommand("cmd_nextUnreadMsg");
+ }
+}
+
+function IsAccountOfflineEnabled() {
+ var selectedFolders = GetSelectedMsgFolders();
+
+ if (selectedFolders && (selectedFolders.length == 1))
+ return selectedFolders[0].supportsOffline;
+
+ return false;
+}
+
+function DoGetNewMailWhenOffline() {
+ if (!Services.io.offline)
+ return true;
+
+ if (PromptMessagesOffline("get") == 0) {
+ var sendUnsent = false;
+ if (this.CheckForUnsentMessages != undefined && CheckForUnsentMessages()) {
+ sendUnsent =
+ Services.prefs.getIntPref("offline.send.unsent_messages") == 1 ||
+ Services.prompt.confirmEx(
+ window,
+ gOfflinePromptsBundle.getString("sendMessagesOfflineWindowTitle"),
+ gOfflinePromptsBundle.getString("sendMessagesLabel2"),
+ Services.prompt.BUTTON_TITLE_IS_STRING *
+ (Services.prompt.BUTTON_POS_0 + Services.prompt.BUTTON_POS_1),
+ gOfflinePromptsBundle.getString("sendMessagesSendButtonLabel"),
+ gOfflinePromptsBundle.getString("sendMessagesNoSendButtonLabel"),
+ null, null, {value: false}) == 0;
+ }
+ if (!gOfflineManager)
+ GetOfflineMgrService();
+ gOfflineManager.goOnline(sendUnsent /* sendUnsentMessages */,
+ false /* playbackOfflineImapOperations */,
+ msgWindow);
+ return true;
+ }
+ return false;
+}
+
+// prompt for getting/sending messages when offline
+function PromptMessagesOffline(aPrefix) {
+ InitPrompts();
+ var checkValue = {value: false};
+ return Services.prompt.confirmEx(
+ window,
+ gOfflinePromptsBundle.getString(aPrefix + "MessagesOfflineWindowTitle"),
+ gOfflinePromptsBundle.getString(aPrefix + "MessagesOfflineLabel"),
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
+ gOfflinePromptsBundle.getString(aPrefix + "MessagesOfflineGoButtonLabel"),
+ null, null, null, checkValue);
+}
+
+function GetDefaultAccountRootFolder() {
+ var account = accountManager.defaultAccount;
+ if (account) {
+ return account.incomingServer.rootMsgFolder;
+ }
+ return null;
+}
+
+/**
+ * Check for new messages for all selected folders, or for the default account
+ * in case no folders are selected.
+ */
+function GetFolderMessages() {
+ var selectedFolders = GetSelectedMsgFolders();
+ var defaultAccountRootFolder = GetDefaultAccountRootFolder();
+
+ var folders = (selectedFolders.length) ? selectedFolders
+ : [defaultAccountRootFolder];
+
+ if (!folders[0]) {
+ return;
+ }
+
+ for (let folder of folders) {
+ var serverType = folder.server.type;
+ if (folder.isServer && (serverType == "nntp")) {
+ // If we're doing "get msgs" on a news server,
+ // update unread counts on this server.
+ folder.server.performExpand(msgWindow);
+ } else if (serverType == "none") {
+ // If "Local Folders" is selected and the user does "Get Msgs" and
+ // LocalFolders is not deferred to, get new mail for the default account
+ //
+ // XXX TODO
+ // Should shift click get mail for all (authenticated) accounts?
+ // see bug #125885.
+ if (!folder.server.isDeferredTo) {
+ if (!defaultAccountRootFolder) {
+ continue;
+ }
+ GetNewMsgs(defaultAccountRootFolder.server, defaultAccountRootFolder);
+ } else {
+ GetNewMsgs(folder.server, folder);
+ }
+ } else {
+ GetNewMsgs(folder.server, folder);
+ }
+ }
+}
+
+/**
+ * Gets new messages for the given server, for the given folder.
+ * @param server which nsIMsgIncomingServer to check for new messages
+ * @param folder which nsIMsgFolder folder to check for new messages
+ */
+function GetNewMsgs(server, folder) {
+ // Note that for Global Inbox folder.server != server when we want to get
+ // messages for a specific account.
+
+ // Whenever we do get new messages, clear the old new messages.
+ folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NoMail;
+ folder.clearNewMessages();
+ server.getNewMessages(folder, msgWindow, null);
+}
+
+function SendUnsentMessages() {
+ let msgSendlater = Cc["@mozilla.org/messengercompose/sendlater;1"]
+ .getService(Ci.nsIMsgSendLater);
+
+ let allIdentities = MailServices.accounts.allIdentities;
+ for (let currentIdentity of allIdentities) {
+ let msgFolder = msgSendlater.getUnsentMessagesFolder(currentIdentity);
+ if (msgFolder) {
+ let numMessages = msgFolder.getTotalMessages(false /* include subfolders */);
+ if (numMessages > 0) {
+ msgSendlater.statusFeedback = statusFeedback;
+ msgSendlater.sendUnsentMessages(currentIdentity);
+ // Right now, all identities point to the same unsent messages
+ // folder, so to avoid sending multiple copies of the
+ // unsent messages, we only call messenger.SendUnsentMessages() once
+ // see bug #89150 for details
+ break;
+ }
+ }
+ }
+}
+
+function CommandUpdate_UndoRedo() {
+ EnableMenuItem("menu_undo", SetupUndoRedoCommand("cmd_undo"));
+ EnableMenuItem("menu_redo", SetupUndoRedoCommand("cmd_redo"));
+}
+
+function SetupUndoRedoCommand(command) {
+ // If we have selected a server, and are viewing account central
+ // there is no loaded folder.
+ var loadedFolder = GetLoadedMsgFolder();
+ if (!loadedFolder || !loadedFolder.server.canUndoDeleteOnServer)
+ return false;
+
+ var canUndoOrRedo = false;
+ var txnType = 0;
+
+ if (command == "cmd_undo") {
+ canUndoOrRedo = messenger.canUndo();
+ txnType = messenger.getUndoTransactionType();
+ } else {
+ canUndoOrRedo = messenger.canRedo();
+ txnType = messenger.getRedoTransactionType();
+ }
+
+ if (canUndoOrRedo) {
+ switch (txnType) {
+ default:
+ case Ci.nsIMessenger.eUnknown:
+ goSetMenuValue(command, "valueDefault");
+ break;
+ case Ci.nsIMessenger.eDeleteMsg:
+ goSetMenuValue(command, "valueDeleteMsg");
+ break;
+ case Ci.nsIMessenger.eMoveMsg:
+ goSetMenuValue(command, "valueMoveMsg");
+ break;
+ case Ci.nsIMessenger.eCopyMsg:
+ goSetMenuValue(command, "valueCopyMsg");
+ break;
+ case Ci.nsIMessenger.eMarkAllMsg:
+ goSetMenuValue(command, "valueUnmarkAllMsgs");
+ break;
+ }
+ } else {
+ goSetMenuValue(command, "valueDefault");
+ }
+ return canUndoOrRedo;
+}
+
+function HandleJunkStatusChanged(folder) {
+ // This might be the stand alone window, open to a message that was
+ // and attachment (or on disk), in which case, we want to ignore it.
+ var loadedMessage = GetLoadedMessage();
+ if (!loadedMessage ||
+ /type=application\/x-message-display/.test(loadedMessage) ||
+ !IsCurrentLoadedFolder(folder))
+ return;
+
+ // If multiple message are selected and we change the junk status
+ // we don't want to show the junk bar (since the message pane is blank).
+ var msgHdr = null;
+ if (GetNumSelectedMessages() == 1)
+ msgHdr = messenger.msgHdrFromURI(loadedMessage);
+
+ var junkBarWasDisplayed = gMessageNotificationBar.isShowingJunkNotification();
+ gMessageNotificationBar.setJunkMsg(msgHdr);
+
+ // Only reload message if junk bar display state has changed.
+ if (msgHdr && junkBarWasDisplayed != gMessageNotificationBar.isShowingJunkNotification()) {
+ // We may be forcing junk mail to be rendered with sanitized html.
+ // In that scenario, we want to reload the message if the status has just
+ // changed to not junk.
+ var sanitizeJunkMail = Services.prefs.getBoolPref("mail.spam.display.sanitize");
+
+ // Only bother doing this if we are modifying the html for junk mail...
+ if (sanitizeJunkMail) {
+ let junkScore = msgHdr.getStringProperty("junkscore");
+ let isJunk = (junkScore == Ci.nsIJunkMailPlugin.IS_SPAM_SCORE);
+
+ // If the current row isn't going to change, reload to show sanitized or
+ // unsanitized. Otherwise we wouldn't see the reloaded version anyway.
+
+ // XXX: need to special handle last message in view, for imap mark as deleted
+
+ // 1) When marking as non-junk, the msg would move back to the inbox.
+ // 2) When marking as junk, the msg will move or delete, if manualMark is set.
+ // 3) Marking as junk in the junk folder just changes the junk status.
+ if ((!isJunk && folder.isSpecialFolder(Ci.nsMsgFolderFlags.Inbox)) ||
+ (isJunk && !folder.server.spamSettings.manualMark) ||
+ (isJunk && folder.isSpecialFolder(Ci.nsMsgFolderFlags.Junk)))
+ ReloadMessage();
+ }
+ }
+}
+
+var gMessageNotificationBar =
+{
+ get mStringBundle() {
+ delete this.mStringBundle;
+
+ return this.mStringBundle = document.getElementById("bundle_messenger");
+ },
+
+ get mBrandBundle() {
+ delete this.mBrandBundle;
+
+ return this.mBrandBundle = document.getElementById("bundle_brand");
+ },
+
+ get mMsgNotificationBar() {
+ delete this.mMsgNotificationBar;
+
+ return this.mMsgNotificationBar = document.getElementById("messagepanebox");
+ },
+
+ setJunkMsg(aMsgHdr) {
+ let isJunk = false;
+ if (aMsgHdr) {
+ let junkScore = aMsgHdr.getStringProperty("junkscore");
+ isJunk = ((junkScore != "") && (junkScore != "0"));
+ }
+
+ goUpdateCommand("button_junk");
+
+ if (isJunk) {
+ if (!this.isShowingJunkNotification()) {
+ let brandName = this.mBrandBundle.getString("brandShortName");
+ let junkBarMsg = this.mStringBundle.getFormattedString("junkBarMessage",
+ [brandName]);
+
+ let buttons = [{
+ label: this.mStringBundle.getString("junkBarInfoButton"),
+ accessKey: this.mStringBundle.getString("junkBarInfoButtonKey"),
+ popup: null,
+ callback() {
+ MsgJunkMailInfo(false);
+ return true;
+ }
+ },
+ {
+ label: this.mStringBundle.getString("junkBarButton"),
+ accessKey: this.mStringBundle.getString("junkBarButtonKey"),
+ popup: null,
+ callback() {
+ JunkSelectedMessages(false);
+ return true;
+ }
+ }];
+ this.mMsgNotificationBar.appendNotification(junkBarMsg, "junkContent",
+ null, this.mMsgNotificationBar.PRIORITY_WARNING_HIGH, buttons);
+ this.mMsgNotificationBar.collapsed = false;
+ }
+ }
+ },
+
+ remoteOrigins: null,
+
+ isShowingJunkNotification() {
+ return !!this.mMsgNotificationBar.getNotificationWithValue("junkContent");
+ },
+
+ setRemoteContentMsg(aMsgHdr, aContentURI, aCanOverride) {
+ // remoteOrigins is a Set of all blockable Origins.
+ if (!this.remoteOrigins)
+ this.remoteOrigins = new Set();
+
+ var origin = aContentURI.spec;
+ try {
+ origin = aContentURI.scheme + "://" + aContentURI.hostPort;
+ }
+ // No hostport so likely a special url. Try to use the whole url and see
+ // what comes of it.
+ catch (e) { }
+
+ this.remoteOrigins.add(origin);
+
+ if (this.mMsgNotificationBar.getNotificationWithValue("remoteContent"))
+ return;
+
+ var headerParser = MailServices.headerParser;
+ // update the allow remote content for sender string
+ var mailbox = headerParser.extractHeaderAddressMailboxes(aMsgHdr.author);
+ var emailAddress = mailbox || aMsgHdr.author;
+ var displayName = headerParser.extractFirstName(aMsgHdr.mime2DecodedAuthor);
+ var brandName = this.mBrandBundle.getString("brandShortName");
+ var remoteContentMsg = this.mStringBundle
+ .getFormattedString("remoteContentBarMessage",
+ [brandName]);
+ var buttons = [{
+ label: this.mStringBundle.getString("remoteContentPrefLabel"),
+ accessKey: this.mStringBundle.getString("remoteContentPrefAccesskey"),
+ popup: "remoteContentOptions"
+ }];
+
+ this.mMsgNotificationBar
+ .appendNotification(remoteContentMsg,
+ "remoteContent",
+ null,
+ this.mMsgNotificationBar.PRIORITY_WARNING_MEDIUM,
+ (aCanOverride ? buttons : []));
+ },
+
+ // aUrl is the nsIURI for the message currently loaded in the message pane
+ setPhishingMsg(aUrl) {
+ // if we've explicitly marked this message as not being an email scam, then don't
+ // bother checking it with the phishing detector.
+ var phishingMsg = false;
+
+ if (!checkMsgHdrPropertyIsNot("notAPhishMessage", kIsAPhishMessage))
+ phishingMsg = isMsgEmailScam(aUrl);
+
+ var oldNotif = this.mMsgNotificationBar.getNotificationWithValue("phishingContent");
+ if (phishingMsg) {
+ if (!oldNotif) {
+ let brandName = this.mBrandBundle.getString("brandShortName");
+ let phishingMsgNote = this.mStringBundle.getFormattedString("phishingBarMessage",
+ [brandName]);
+
+ let buttons = [{
+ label: this.mStringBundle.getString("phishingBarIgnoreButton"),
+ accessKey: this.mStringBundle.getString("phishingBarIgnoreButtonKey"),
+ popup: null,
+ callback() {
+ MsgIsNotAScam();
+ }
+ }];
+
+ this.mMsgNotificationBar.appendNotification(phishingMsgNote, "phishingContent",
+ null, this.mMsgNotificationBar.PRIORITY_CRITICAL_MEDIUM, buttons);
+ }
+ }
+ },
+
+ setMDNMsg(aMdnGenerator, aMsgHeader, aMimeHdr) {
+ this.mdnGenerator = aMdnGenerator;
+ // Return receipts can be RFC 3798 "Disposition-Notification-To",
+ // or non-standard "Return-Receipt-To".
+ var mdnHdr = aMimeHdr.extractHeader("Disposition-Notification-To", false) ||
+ aMimeHdr.extractHeader("Return-Receipt-To", false); // not
+ var fromHdr = aMimeHdr.extractHeader("From", false);
+
+ var mdnAddr = MailServices.headerParser
+ .extractHeaderAddressMailboxes(mdnHdr);
+ var fromAddr = MailServices.headerParser
+ .extractHeaderAddressMailboxes(fromHdr);
+
+ var authorName = MailServices.headerParser
+ .extractFirstName(aMsgHeader.mime2DecodedAuthor)
+ || aMsgHeader.author;
+
+ var barMsg;
+ // If the return receipt doesn't go to the sender address, note that in the
+ // notification.
+ if (mdnAddr != fromAddr)
+ barMsg = this.mStringBundle.getFormattedString("mdnBarMessageAddressDiffers",
+ [authorName, mdnAddr]);
+ else
+ barMsg = this.mStringBundle.getFormattedString("mdnBarMessageNormal", [authorName]);
+
+ var oldNotif = this.mMsgNotificationBar.getNotificationWithValue("mdnContent");
+ if (!oldNotif) {
+ let buttons = [{
+ label: this.mStringBundle.getString("mdnBarSendReqButton"),
+ accessKey: this.mStringBundle.getString("mdnBarSendReqButtonKey"),
+ popup: null,
+ callback: SendMDNResponse
+ },
+ {
+ label: this.mStringBundle.getString("mdnBarIgnoreButton"),
+ accessKey: this.mStringBundle.getString("mdnBarIgnoreButtonKey"),
+ popup: null,
+ callback: IgnoreMDNResponse
+ }];
+
+ this.mMsgNotificationBar.appendNotification(barMsg, "mdnContent",
+ null, this.mMsgNotificationBar.PRIORITY_INFO_MEDIUM, buttons);
+ }
+ },
+
+ clearMsgNotifications() {
+ }
+};
+
+/**
+ * LoadMsgWithRemoteContent
+ * Reload the current message, allowing remote content
+ */
+function LoadMsgWithRemoteContent() {
+ // we want to get the msg hdr for the currently selected message
+ // change the "remoteContentBar" property on it
+ // then reload the message
+
+ setMsgHdrPropertyAndReload("remoteContentPolicy", kAllowRemoteContent);
+ window.content.focus();
+}
+
+/**
+ * Populate the remote content options for the current message.
+ */
+function onRemoteContentOptionsShowing(aEvent) {
+ var origins = [...gMessageNotificationBar.remoteOrigins];
+
+ var addresses = {};
+ MailServices.headerParser.parseHeadersWithArray(
+ gMessageDisplay.displayedMessage.author, addresses, {}, {});
+ var authorEmailAddress = addresses.value[0];
+
+ var emailURI = Services.io.newURI(
+ "chrome://messenger/content/email=" + authorEmailAddress);
+ var principal = Services.scriptSecurityManager
+ .createCodebasePrincipal(emailURI, {});
+ // Put author email first in the menu.
+ origins.unshift(principal.origin);
+
+ // Out with the old...
+ let childNodes = aEvent.target.querySelectorAll(".allow-remote-uri");
+ for (let child of childNodes)
+ child.remove();
+
+ var messengerBundle = gMessageNotificationBar.mStringBundle;
+ var separator = document.getElementById("remoteContentSettingsMenuSeparator")
+
+ // ... and in with the new.
+ for (let origin of origins) {
+ let menuitem = document.createElement("menuitem");
+ let host = origin.replace("chrome://messenger/content/email=", "");
+ let hostString = messengerBundle.getFormattedString("remoteContentAllow", [host]);
+ menuitem.setAttribute("label", hostString);
+ menuitem.setAttribute("value", origin);
+ menuitem.setAttribute("class", "allow-remote-uri");
+ aEvent.target.insertBefore(menuitem, separator);
+ }
+}
+
+/**
+ * Add privileges to display remote content for the given uri.
+ * @param aItem |Node| Item that was selected. The origin
+ * is extracted and converted to a uri and used to add
+ * permissions for the site.
+ */
+function allowRemoteContentForURI(aItem) {
+
+ var origin = aItem.getAttribute("value");
+
+ if (!origin)
+ return;
+
+ let uri = Services.io.newURI(origin);
+ Services.perms.add(uri, "image", Services.perms.ALLOW_ACTION);
+
+ ReloadMessage();
+}
+
+/**
+ * Displays fine-grained, per-site permissions for remote content.
+ */
+function editRemoteContentSettings() {
+ toDataManager("|permissions");
+ if (!Services.prefs.getBoolPref("browser.preferences.instantApply"))
+ ReloadMessage();
+}
+
+/**
+ * msgHdrForCurrentMessage
+ * Returns the msg hdr associated with the current loaded message.
+ */
+function msgHdrForCurrentMessage() {
+ var msgURI = GetLoadedMessage();
+ return (msgURI && !(/type=application\/x-message-display/.test(msgURI))) ? messenger.msgHdrFromURI(msgURI) : null;
+}
+
+function MsgIsNotAScam() {
+ // we want to get the msg hdr for the currently selected message
+ // change the "isPhishingMsg" property on it
+ // then reload the message
+
+ setMsgHdrPropertyAndReload("notAPhishMessage", kNotAPhishMessage);
+}
+
+function setMsgHdrPropertyAndReload(aProperty, aValue) {
+ // we want to get the msg hdr for the currently selected message
+ // change the appropiate property on it then reload the message
+
+ var msgHdr = msgHdrForCurrentMessage();
+ if (msgHdr) {
+ msgHdr.setUint32Property(aProperty, aValue);
+ ReloadMessage();
+ }
+}
+
+function checkMsgHdrPropertyIsNot(aProperty, aValue) {
+ // we want to get the msg hdr for the currently selected message,
+ // get the appropiate property on it and then test against value.
+
+ var msgHdr = msgHdrForCurrentMessage();
+ return (msgHdr && msgHdr.getUint32Property(aProperty) != aValue);
+}
+
+/**
+ * Mark a specified message as read.
+ * @param msgHdr header (nsIMsgDBHdr) of the message to mark as read
+ */
+function MarkMessageAsRead(msgHdr) {
+ ClearPendingReadTimer();
+ msgHdr.folder.markMessagesRead([msgHdr], true);
+}
+
+function ClearPendingReadTimer() {
+ if (gMarkViewedMessageAsReadTimer) {
+ clearTimeout(gMarkViewedMessageAsReadTimer);
+ gMarkViewedMessageAsReadTimer = null;
+ }
+}
+
+function OnMsgParsed(aUrl) {
+ gMessageNotificationBar.setPhishingMsg(aUrl);
+
+ // notify anyone (e.g., extensions) who's interested in when a message is loaded.
+ var msgURI = GetLoadedMessage();
+ Services.obs.notifyObservers(msgWindow.msgHeaderSink,
+ "MsgMsgDisplayed", msgURI);
+
+ // scale any overflowing images
+ var doc = getMessageBrowser().contentDocument;
+ var imgs = doc.getElementsByTagName("img");
+ for (var img of imgs) {
+ if (img.className == "moz-attached-image" &&
+ img.naturalWidth > doc.body.clientWidth) {
+ if (img.hasAttribute("shrinktofit"))
+ img.setAttribute("isshrunk", "true");
+ else
+ img.setAttribute("overflowing", "true");
+ }
+ }
+}
+
+function OnMsgLoaded(aUrl) {
+ if (!aUrl)
+ return;
+
+ // nsIMsgMailNewsUrl.folder throws an error when opening .eml files.
+ var folder;
+ try {
+ folder = aUrl.folder;
+ } catch (ex) {}
+
+ var msgURI = GetLoadedMessage();
+
+ if (!folder || !msgURI)
+ return;
+
+ // If we are in the middle of a delete or move operation, make sure that
+ // if the user clicks on another message then that message stays selected
+ // and the selection does not "snap back" to the message chosen by
+ // SetNextMessageAfterDelete() when the operation completes (bug 243532).
+ var wintype = document.documentElement.getAttribute("windowtype");
+ gNextMessageViewIndexAfterDelete = -2;
+
+ var msgHdr = msgHdrForCurrentMessage();
+ gMessageNotificationBar.setJunkMsg(msgHdr);
+ // Reset the blocked origins so we can populate it again for this message.
+ // Reset to null so it's only a Set if there's something in the Set.
+ gMessageNotificationBar.remoteOrigins = null;
+
+ var markReadAutoMode = Services.prefs.getBoolPref("mailnews.mark_message_read.auto");
+
+ // We just finished loading a message. If messages are to be marked as read
+ // automatically, set a timer to mark the message is read after n seconds
+ // where n can be configured by the user.
+ if (msgHdr && !msgHdr.isRead && markReadAutoMode) {
+ let markReadOnADelay = Services.prefs.getBoolPref("mailnews.mark_message_read.delay");
+ // Only use the timer if viewing using the 3-pane preview pane and the
+ // user has set the pref.
+ if (markReadOnADelay && wintype == "mail:3pane") // 3-pane window
+ {
+ ClearPendingReadTimer();
+ let markReadDelayTime = Services.prefs.getIntPref("mailnews.mark_message_read.delay.interval");
+ if (markReadDelayTime == 0)
+ MarkMessageAsRead(msgHdr);
+ else
+ gMarkViewedMessageAsReadTimer = setTimeout(MarkMessageAsRead,
+ markReadDelayTime * 1000,
+ msgHdr);
+ } else // standalone msg window
+ {
+ MarkMessageAsRead(msgHdr);
+ }
+ }
+
+ // See if MDN was requested but has not been sent.
+ HandleMDNResponse(aUrl);
+}
+
+/*
+ * This function handles all mdn response generation (ie, imap and pop).
+ * For pop the msg uid can be 0 (ie, 1st msg in a local folder) so no
+ * need to check uid here. No one seems to set mimeHeaders to null so
+ * no need to check it either.
+ */
+function HandleMDNResponse(aUrl) {
+ if (!aUrl)
+ return;
+
+ var msgFolder = aUrl.folder;
+ var msgHdr = gFolderDisplay.selectedMessage;
+ if (!msgFolder || !msgHdr || gFolderDisplay.selectedMessageIsNews)
+ return;
+
+ // if the message is marked as junk, do NOT attempt to process a return receipt
+ // in order to better protect the user
+ if (SelectedMessagesAreJunk())
+ return;
+
+ var mimeHdr;
+
+ try {
+ mimeHdr = aUrl.mimeHeaders;
+ } catch (ex) {
+ return;
+ }
+
+ // If we didn't get the message id when we downloaded the message header,
+ // we cons up an md5: message id. If we've done that, we'll try to extract
+ // the message id out of the mime headers for the whole message.
+ var msgId = msgHdr.messageId;
+ if (msgId.split(":")[0] == "md5") {
+ var mimeMsgId = mimeHdr.extractHeader("Message-Id", false);
+ if (mimeMsgId)
+ msgHdr.messageId = mimeMsgId;
+ }
+
+ // After a msg is downloaded it's already marked READ at this point so we must check if
+ // the msg has a "Disposition-Notification-To" header and no MDN report has been sent yet.
+ if (msgHdr.flags & Ci.nsMsgMessageFlags.MDNReportSent)
+ return;
+
+ var DNTHeader = mimeHdr.extractHeader("Disposition-Notification-To", false);
+ var oldDNTHeader = mimeHdr.extractHeader("Return-Receipt-To", false);
+ if (!DNTHeader && !oldDNTHeader)
+ return;
+
+ // Everything looks good so far, let's generate the MDN response.
+ var mdnGenerator = Cc["@mozilla.org/messenger-mdn/generator;1"]
+ .createInstance(Ci.nsIMsgMdnGenerator);
+ var askUser = mdnGenerator.process(Ci.nsIMsgMdnGenerator.eDisplayed,
+ msgWindow,
+ msgFolder,
+ msgHdr.messageKey,
+ mimeHdr,
+ false);
+ if (askUser)
+ gMessageNotificationBar.setMDNMsg(mdnGenerator, msgHdr, mimeHdr);
+}
+
+function SendMDNResponse() {
+ gMessageNotificationBar.mdnGenerator.userAgreed();
+}
+
+function IgnoreMDNResponse() {
+ gMessageNotificationBar.mdnGenerator.userDeclined();
+}
+
+/**
+ * Opens a search window with the given folder, or the displayed one if none is
+ * chosen.
+ *
+ * @param [aFolder] the folder to open the search window for, if different from
+ * the displayed one
+ */
+function MsgSearchMessages(aFolder) {
+ let folder = aFolder || gFolderDisplay.displayedFolder;
+ OpenOrFocusWindow({ folder }, "mailnews:search",
+ "chrome://messenger/content/SearchDialog.xul");
+}
+
+function MsgJunkMailInfo(aCheckFirstUse) {
+ if (aCheckFirstUse) {
+ if (!Services.prefs.getBoolPref("mailnews.ui.junk.firstuse"))
+ return;
+ Services.prefs.setBoolPref("mailnews.ui.junk.firstuse", false);
+
+ // Check to see if this is an existing profile where the user has started
+ // using the junk mail feature already.
+ if (MailServices.junk.userHasClassified)
+ return;
+ }
+
+ var desiredWindow = Services.wm.getMostRecentWindow("mailnews:junkmailinfo");
+
+ if (desiredWindow)
+ desiredWindow.focus();
+ else
+ window.openDialog("chrome://messenger/content/junkMailInfo.xul", "mailnews:junkmailinfo", "centerscreen,resizeable=no,titlebar,chrome,modal", null);
+}
+
+function MsgSearchAddresses() {
+ var args = { directory: null };
+ OpenOrFocusWindow(args, "mailnews:absearch", "chrome://messenger/content/ABSearchDialog.xul");
+}
+
+function MsgFilterList(args) {
+ OpenOrFocusWindow(args, "mailnews:filterlist", "chrome://messenger/content/FilterListDialog.xul");
+}
+
+function OpenOrFocusWindow(args, windowType, chromeURL) {
+ var desiredWindow = Services.wm.getMostRecentWindow(windowType);
+
+ if (desiredWindow) {
+ desiredWindow.focus();
+ if ("refresh" in args && args.refresh)
+ desiredWindow.refresh(args);
+ } else
+ window.openDialog(chromeURL, "", "chrome,resizable,status,centerscreen,dialog=no", args);
+}
+
+function getMailToolbox() {
+ return document.getElementById("mail-toolbox");
+}
+
+function MailToolboxCustomizeInit() {
+ toolboxCustomizeInit("mail-menubar");
+}
+
+function MailToolboxCustomizeDone(aToolboxChanged) {
+ toolboxCustomizeDone("mail-menubar", getMailToolbox(), aToolboxChanged);
+
+ // Make sure the folder location picker is initialized.
+ let folderContainer = document.getElementById("folder-location-container");
+ if (folderContainer &&
+ folderContainer.parentNode.localName != "toolbarpalette") {
+ FolderPaneSelectionChange();
+ }
+}
+
+function MailToolboxCustomizeChange(event) {
+ toolboxCustomizeChange(getMailToolbox(), event);
+}
diff --git a/comm/suite/mailnews/content/mailWindowOverlay.xul b/comm/suite/mailnews/content/mailWindowOverlay.xul
new file mode 100644
index 0000000000..61a7b4ff65
--- /dev/null
+++ b/comm/suite/mailnews/content/mailWindowOverlay.xul
@@ -0,0 +1,1929 @@
+<?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://messenger/skin/folderMenus.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/smime/msgReadSMIMEOverlay.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/content/bindings.css" type="text/css"?>
+
+
+<?xul-overlay href="chrome://communicator/content/charsetOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/contentAreaContextOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/viewZoomOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/viewApplyThemeOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/msgHdrViewOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/mailOverlay.xul"?>
+<?xul-overlay href="chrome://messenger/content/mailKeysOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+
+<!DOCTYPE overlay [
+ <!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd">
+ %messengerDTD;
+ <!ENTITY % mailKeysDTD SYSTEM "chrome://messenger/locale/mailKeysOverlay.dtd">
+ %mailKeysDTD;
+ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+ <!ENTITY % msgViewPickerDTD SYSTEM "chrome://messenger/locale/msgViewPickerOverlay.dtd">
+ %msgViewPickerDTD;
+ <!ENTITY % msgHdrViewPopupDTD SYSTEM "chrome://messenger/locale/msgHdrViewPopup.dtd">
+ %msgHdrViewPopupDTD;
+ <!ENTITY % contentAreaCommandsDTD SYSTEM "chrome://communicator/locale/contentAreaCommands.dtd">
+ %contentAreaCommandsDTD;
+ <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+ %brandDTD;
+ <!ENTITY % msgReadSMIMEDTD SYSTEM "chrome://messenger-smime/locale/msgReadSMIMEOverlay.dtd">
+ %msgReadSMIMEDTD;
+]>
+
+<overlay
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://messenger/content/mailCommands.js"/>
+<script src="chrome://messenger/content/junkCommands.js"/>
+<script src="chrome://messenger/content/mailWindowOverlay.js"/>
+<script src="chrome://messenger/content/msgViewPickerOverlay.js"/>
+<script src="chrome://messenger-newsblog/content/newsblogOverlay.js"/>
+<script src="chrome://messenger/content/mail-offline.js"/>
+<script src="chrome://communicator/content/findUtils.js"/>
+<script src="chrome://global/content/printUtils.js"/>
+<script src="chrome://messenger/content/folderDisplay.js"/>
+<script src="chrome://messenger-smime/content/msgReadSMIMEOverlay.js"/>
+<script>
+<![CDATA[
+ ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
+ ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
+]]></script>
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_offlinePrompts" src="chrome://messenger/locale/offline.properties"/>
+ <stringbundle id="bundle_read_smime"
+ src="chrome://messenger-smime/locale/msgReadSMIMEOverlay.properties"/>
+ <stringbundle id="bundle_viewZoom"/>
+ <stringbundle id="bundle_viewApplyTheme"/>
+ <stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
+</stringbundleset>
+
+<!-- Performance optimization...we include utilityOverlay.xul which defines some command sets
+ which are updated based on events like focus and select. We have our own custom events
+ which we use to optmize when we do command updating. To avoid unnecessary command updating,
+ we are going to override the events the global edit menu items and select edit menu items
+ are updated on with events of our own controlling.
+ -->
+
+<commandset id="globalEditMenuItems"
+ commandupdater="true"
+ events="create-menu-edit"
+ oncommandupdate="goUpdateGlobalEditMenuItems()"/>
+<commandset id="selectEditMenuItems"
+ commandupdater="true"
+ events="create-menu-edit"
+ oncommandupdate="goUpdateSelectEditMenuItems()"/>
+
+<!-- End command set merging -->
+
+<commandset id="mailDownloadCommands">
+ <command id="cmd_downloadFlagged" oncommand="goDoCommand('cmd_downloadFlagged')"/>
+ <command id="cmd_downloadSelected" oncommand="goDoCommand('cmd_downloadSelected')"/>
+</commandset>
+
+<commandset id="mailFileMenuItems"
+ commandupdater="true"
+ events="create-menu-file, message-header-pane"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+
+ <command id="cmd_getNewMessages" oncommand="goDoCommand('cmd_getNewMessages')" disabled="true"/>
+ <command id="cmd_open" oncommand="goDoCommand('cmd_open')"/>
+
+ <command id="cmd_emptyTrash" oncommand="goDoCommand('cmd_emptyTrash')" disabled="true"/>
+ <command id="cmd_compactFolder" oncommand="goDoCommand('cmd_compactFolder')" disabled="true"/>
+
+ <command id="cmd_printSetup" oncommand="goDoCommand('cmd_printSetup')" disabled="true"/>
+ <command id="cmd_print" oncommand="goDoCommand('cmd_print')" disabled="true"/>
+ <command id="cmd_printpreview" oncommand="goDoCommand('cmd_printpreview')" disabled="true"/>
+ <command id="cmd_saveAsFile" oncommand="goDoCommand('cmd_saveAsFile')" disabled="true"/>
+ <command id="cmd_saveAsTemplate" oncommand="goDoCommand('cmd_saveAsTemplate')" disabled="true"/>
+ <command id="cmd_getNextNMessages" oncommand="goDoCommand('cmd_getNextNMessages')" disabled="true"/>
+ <command id="cmd_renameFolder" oncommand="goDoCommand('cmd_renameFolder')" />
+ <command id="cmd_sendUnsentMsgs" oncommand="goDoCommand('cmd_sendUnsentMsgs')" />
+ <command id="cmd_subscribe" oncommand="goDoCommand('cmd_subscribe')" disabled="true"/>
+ <command id="cmd_synchronizeOffline" oncommand="goDoCommand('cmd_synchronizeOffline');" disabled="true"/>
+ <command id="cmd_settingsOffline" oncommand="goDoCommand('cmd_settingsOffline');" disabled="true"/>
+</commandset>
+
+<commandset id="mailCommands">
+ <command id="cmd_newNavigator"/>
+ <command id="cmd_newPrivateWindow"/>
+ <command id="cmd_newEditor"/>
+ <command id="cmd_createFilterFromPopup" oncommand="goDoCommand('cmd_createFilterFromPopup')"/>
+ <command id="cmd_pageSetup"/>
+</commandset>
+
+<commandset id="mailViewMenuItems"
+ commandupdater="true"
+ events="create-menu-view"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+
+ <command id="cmd_viewPageSource" oncommand="goDoCommand('cmd_viewPageSource')" disabled="true"/>
+ <command id="cmd_setFolderCharset" oncommand="goDoCommand('cmd_setFolderCharset')" />
+ <command id="cmd_reload" oncommand="goDoCommand('cmd_reload')" disabled="true"/>
+
+ <command id="cmd_expandAllThreads" oncommand="goDoCommand('cmd_expandAllThreads')" disabled="true"/>
+ <command id="cmd_collapseAllThreads" oncommand="goDoCommand('cmd_collapseAllThreads')" disabled="true"/>
+ <command id="cmd_viewAllMsgs" oncommand="goDoCommand('cmd_viewAllMsgs')" disabled="true"/>
+ <command id="cmd_viewUnreadMsgs" oncommand="goDoCommand('cmd_viewUnreadMsgs')" disabled="true"/>
+ <command id="cmd_viewThreadsWithUnread" oncommand="goDoCommand('cmd_viewThreadsWithUnread')" disabled="true"/>
+ <command id="cmd_viewWatchedThreadsWithUnread" oncommand="goDoCommand('cmd_viewWatchedThreadsWithUnread')" disabled="true"/>
+ <command id="cmd_viewIgnoredThreads" oncommand="goDoCommand('cmd_viewIgnoredThreads')" disabled="true"/>
+ <!-- Needed to support the Lightning Task filter See Bug 316916 -->
+ <command id="cmd_showQuickFilterBar" oncommand="goDoCommand('cmd_showQuickFilterBar');"/>
+ <commandset id="viewZoomCommands"/>
+ <command id="cmd_viewSecurityStatus" oncommand="showMessageReadSecurityInfo();" disabled="true"/>
+</commandset>
+
+<commandset id="mailEditMenuItems"
+ commandupdater="true"
+ events="create-menu-edit, message-header-pane"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+
+ <command id="cmd_undo"
+ valueDeleteMsg="&undoDeleteMsgCmd.label;"
+ valueMoveMsg="&undoMoveMsgCmd.label;"
+ valueCopyMsg="&undoCopyMsgCmd.label;"
+ valueUnmarkAllMsgs="&undoMarkAllCmd.label;"
+ valueDefault="&undoDefaultCmd.label;"/>
+ <command id="cmd_redo"
+ valueDeleteMsg="&redoDeleteMsgCmd.label;"
+ valueMoveMsg="&redoMoveMsgCmd.label;"
+ valueCopyMsg="&redoCopyMsgCmd.label;"
+ valueUnmarkAllMsgs="&redoMarkAllCmd.label;"
+ valueDefault="&redoDefaultCmd.label;"/>
+ <command id="cmd_cut"/>
+ <command id="cmd_copy"/>
+ <command id="cmd_paste"/>
+ <command id="cmd_delete"
+ valueFolder="&deleteFolderCmd.label;"
+ valueFolderAccessKey="&deleteFolderCmd.accesskey;"
+ valueNewsgroup="&unsubscribeNewsgroupCmd.label;"
+ valueNewsgroupAccessKey="&unsubscribeNewsgroupCmd.accesskey;"
+ valueMessage="&deleteMsgCmd.label;"
+ valueMessageAccessKey="&deleteMsgCmd.accesskey;"
+ valueIMAPDeletedMessage="&undeleteMsgCmd.label;"
+ valueIMAPDeletedMessageAccessKey="&undeleteMsgCmd.accesskey;"
+ valueMessages="&deleteMsgsCmd.label;"
+ valueMessagesAccessKey="&deleteMsgsCmd.accesskey;"
+ valueIMAPDeletedMessages="&undeleteMsgsCmd.label;"
+ valueIMAPDeletedMessagesAccessKey="&undeleteMsgsCmd.accesskey;"/>
+ <command id="cmd_selectAll"/>
+ <command id="cmd_selectThread" oncommand="goDoCommand('cmd_selectThread')"/>
+ <command id="cmd_selectFlagged" oncommand="goDoCommand('cmd_selectFlagged')"/>
+ <command id="cmd_properties" oncommand="goDoCommand('cmd_properties')"
+ valueNewsgroup="&folderPropsNewsgroupCmd.label;"
+ valueFolder="&folderPropsFolderCmd.label;"
+ valueGeneric="&folderPropsCmd.label;"/>
+ <command id="cmd_find" oncommand="goDoCommand('cmd_find')" disabled="true"/>
+ <command id="cmd_findNext"
+ oncommand="goDoCommand('cmd_findNext');"
+ disabled="true"/>
+ <command id="cmd_findPrev"
+ oncommand="goDoCommand('cmd_findPrev');"
+ disabled="true"/>
+ <command id="cmd_findTypeText"/>
+ <command id="cmd_findTypeLinks"/>
+ <command id="cmd_search" oncommand="goDoCommand('cmd_search');"/>
+ <command id="cmd_stop" oncommand="MsgStop();"/>
+</commandset>
+
+<commandset id="mailEditContextMenuItems">
+ <command id="cmd_copyLink"/>
+ <command id="cmd_copyImage"/>
+</commandset>
+
+<commandset id="mailGoMenuItems"
+ commandupdater="true"
+ events="create-menu-go"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+
+ <command id="cmd_nextMsg" oncommand="goDoCommand('cmd_nextMsg')" disabled="true"/>
+ <command id="cmd_nextUnreadMsg" oncommand="goDoCommand('cmd_nextUnreadMsg')" disabled="true"/>
+ <command id="cmd_nextFlaggedMsg" oncommand="goDoCommand('cmd_nextFlaggedMsg')" disabled="true"/>
+ <command id="cmd_nextUnreadThread" oncommand="goDoCommand('cmd_nextUnreadThread')" disabled="true"/>
+ <command id="cmd_previousMsg" oncommand="goDoCommand('cmd_previousMsg')" disabled="true"/>
+ <command id="cmd_previousUnreadMsg" oncommand="goDoCommand('cmd_previousUnreadMsg')" disabled="true"/>
+ <command id="cmd_previousFlaggedMsg" oncommand="goDoCommand('cmd_previousFlaggedMsg')" disabled="true"/>
+ <command id="cmd_goStartPage" oncommand="goDoCommand('cmd_goStartPage');"/>
+ <command id="cmd_goBack" oncommand="goDoCommand('cmd_goBack')" disabled="true"/>
+ <command id="cmd_goForward" oncommand="goDoCommand('cmd_goForward');" disabled="true"/>
+</commandset>
+
+<commandset id="mailMessageMenuItems"
+ commandupdater="true"
+ events="create-menu-message"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+ <command id="cmd_archive" oncommand="goDoCommand('cmd_archive')"/>
+ <command id="cmd_reply" oncommand="goDoCommand('cmd_reply')"/>
+ <command id="cmd_replyList" oncommand="goDoCommand('cmd_replyList')"/>
+ <command id="cmd_replyGroup" oncommand="goDoCommand('cmd_replyGroup')"/>
+ <command id="cmd_replySender" oncommand="goDoCommand('cmd_replySender')"/>
+ <command id="cmd_replyall" oncommand="goDoCommand('cmd_replyall')"/>
+ <command id="cmd_replySenderAndGroup" oncommand="goDoCommand('cmd_replySenderAndGroup')"/>
+ <command id="cmd_replyAllRecipients" oncommand="goDoCommand('cmd_replyAllRecipients')"/>
+ <command id="cmd_forward" oncommand="goDoCommand('cmd_forward')"/>
+ <command id="cmd_forwardInline" oncommand="goDoCommand('cmd_forwardInline')"/>
+ <command id="cmd_forwardAttachment" oncommand="goDoCommand('cmd_forwardAttachment')"/>
+ <command id="cmd_editAsNew" oncommand="MsgEditMessageAsNew(event);"/>
+ <command id="cmd_editDraftMsg" oncommand="MsgEditDraftMessage(event);"/>
+ <command id="cmd_newMsgFromTemplate"
+ oncommand="MsgNewMessageFromTemplate(event);"/>
+ <command id="cmd_editTemplateMsg" oncommand="MsgEditTemplateMessage(event);"/>
+ <command id="cmd_openMessage" oncommand="goDoCommand('cmd_openMessage')"/>
+ <command id="cmd_createFilterFromMenu" oncommand="goDoCommand('cmd_createFilterFromMenu')"/>
+ <command id="cmd_cancel" oncommand="goDoCommand('cmd_cancel')"/>
+ <command id="cmd_killThread" oncommand="goDoCommand('cmd_killThread')"/>
+ <command id="cmd_killSubthread" oncommand="goDoCommand('cmd_killSubthread')"/>
+ <command id="cmd_watchThread" oncommand="goDoCommand('cmd_watchThread')"/>
+</commandset>
+
+<commandset id="mailToolbarItems"
+ commandupdater="true"
+ events="mail-toolbar"
+ oncommandupdate="goUpdateMailMenuItems(this);
+ /* update cmd_delete manually to avoid a doubled id */
+ goUpdateCommand('cmd_delete');">
+ <command id="button_reply"/>
+ <command id="button_replyall"/>
+ <command id="button_forward"/>
+ <command id="button_delete"/>
+ <command id="button_mark"/>
+ <command id="button_getNewMessages"/>
+ <command id="button_print"/>
+ <command id="button_next"/>
+ <command id="button_goBack"/>
+ <command id="button_goForward"/>
+ <command id="button_file"/>
+ <command id="cmd_shiftDelete" oncommand="goDoCommand('cmd_shiftDelete');"/>
+ <command id="button_junk"/>
+ <command id="button_search"/>
+</commandset>
+
+
+<commandset id="mailGetMsgMenuItems"
+ commandupdater="true"
+ events="create-menu-getMsgToolbar,create-menu-file"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+
+ <command id="cmd_getMsgsForAuthAccounts"
+ oncommand="goDoCommand('cmd_getMsgsForAuthAccounts'); event.stopPropagation()"
+ disabled="true"/>
+</commandset>
+
+<commandset id="mailMarkMenuItems"
+ commandupdater="true"
+ events="create-menu-mark"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+
+ <command id="cmd_markAsRead" oncommand="goDoCommand('cmd_markAsRead'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_markAsUnread" oncommand="goDoCommand('cmd_markAsUnread'); event.stopPropagation();" disabled="true"/>
+ <command id="cmd_markAllRead" oncommand="goDoCommand('cmd_markAllRead'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_markThreadAsRead" oncommand="goDoCommand('cmd_markThreadAsRead'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_markReadByDate" oncommand="goDoCommand('cmd_markReadByDate');" disabled="true"/>
+ <command id="cmd_markAsFlagged" oncommand="goDoCommand('cmd_markAsFlagged'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_markAsJunk" oncommand="goDoCommand('cmd_markAsJunk'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_markAsNotJunk" oncommand="goDoCommand('cmd_markAsNotJunk'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_recalculateJunkScore" oncommand="goDoCommand('cmd_recalculateJunkScore');" disabled="true"/>
+ <command id="cmd_markAsShowRemote" oncommand="goDoCommand('cmd_markAsShowRemote'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_markAsNotPhish" oncommand="goDoCommand('cmd_markAsNotPhish'); event.stopPropagation()" disabled="true"/>
+ <command id="cmd_viewAllHeader"
+ oncommand="goDoCommand('cmd_viewAllHeader');"
+ disabled="true"/>
+ <command id="cmd_viewNormalHeader"
+ oncommand="goDoCommand('cmd_viewNormalHeader');"
+ disabled="true"/>
+</commandset>
+
+<commandset id="mailToolsMenuItems"
+ commandupdater="true"
+ events="create-menu-tasks"
+ oncommandupdate="goUpdateMailMenuItems(this)">
+ <command id="cmd_displayMsgFilters"
+ disabled="true"
+ oncommand="goDoCommand('cmd_displayMsgFilters');"/>
+ <command id="cmd_applyFilters" oncommand="goDoCommand('cmd_applyFilters');" disabled="true"/>
+ <command id="cmd_applyFiltersToSelection"
+ oncommand="goDoCommand('cmd_applyFiltersToSelection');"
+ disabled="true"
+ valueSelection="&filtersApplyToSelection.label;"
+ valueSelectionAccessKey="&filtersApplyToSelection.accesskey;"
+ valueMessage="&filtersApplyToMessage.label;"
+ valueMessageAccessKey="&filtersApplyToMessage.accesskey;"/>
+ <command id="cmd_runJunkControls" oncommand="goDoCommand('cmd_runJunkControls');" disabled="true"/>
+ <command id="cmd_deleteJunk" oncommand="goDoCommand('cmd_deleteJunk');" disabled="true"/>
+</commandset>
+
+<keyset id="mailKeys">
+ <key id="space" key=" " modifiers="shift any" oncommand="SpaceHit(event);"/>
+
+ <!-- File Menu -->
+ <key id="key_newTab"
+ key="&newTabCmd.key;"
+ modifiers="accel"
+ oncommand="MsgOpenNewTab();"/>
+ <key id="key_newNavigator"/>
+ <key id="key_newPrivateWindow"/>
+ <key id="key_newBlankPage"/>
+ <key id="key_close"/>
+ <!-- Edit Menu -->
+ <key id="key_undo"/>
+ <key id="key_redo"/>
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"/>
+ <key id="key_selectThread" key="&selectThreadCmd.key;" oncommand="goDoCommand('cmd_selectThread');" modifiers="alt, shift"/>
+
+ <key id="key_markJunk" key="&markAsJunkCmd.key;" oncommand="goDoCommand('cmd_markAsJunk');"/>
+ <key id="key_markNotJunk" key="&markAsNotJunkCmd.key;" oncommand="goDoCommand('cmd_markAsNotJunk');"
+ modifiers="shift"/>
+ <key id="key_markShowRemote" key="&markAsShowRemoteCmd.key;" oncommand="goDoCommand('cmd_markAsShowRemote');"
+ modifiers="shift"/>
+ <key id="key_markNotPhish" key="&markAsNotPhishCmd.key;" oncommand="goDoCommand('cmd_markAsNotPhish');"
+ modifiers="shift"/>
+ <key id="key_markAllRead" key="&markAllReadCmd.key;" oncommand="goDoCommand('cmd_markAllRead');" modifiers="accel, shift"/>
+ <key id="key_markThreadAsRead" key="&markThreadAsReadCmd.key;" oncommand="goDoCommand('cmd_markThreadAsRead')"/>
+ <key id="key_markReadByDate" key="&markReadByDateCmd.key;" oncommand="goDoCommand('cmd_markReadByDate')"/>
+ <key id="key_nextMsg" key="&nextMsgCmd.key;" oncommand="goDoCommand('cmd_nextMsg')"/>
+ <key id="key_nextUnreadMsg" key="&nextUnreadMsgCmd.key;" oncommand="goDoCommand('cmd_nextUnreadMsg')"/>
+ <key id="key_expandAllThreads" key="&expandAllThreadsCmd.key;" oncommand="goDoCommand('cmd_expandAllThreads')"/>
+ <key key="&expandAllThreadsCmd.key;" modifiers="shift" oncommand="goDoCommand('cmd_expandAllThreads')"/>
+ <key id="key_collapseAllThreads" key="&collapseAllThreadsCmd.key;" oncommand="goDoCommand('cmd_collapseAllThreads')"/>
+ <key key="&collapseAllThreadsCmd.key;" modifiers="shift" oncommand="goDoCommand('cmd_collapseAllThreads')"/>
+ <key id="key_nextUnreadThread" key="&nextUnreadThread.key;" oncommand="goDoCommand('cmd_nextUnreadThread')"/>
+ <key id="key_previousMsg" key="&prevMsgCmd.key;" oncommand="goDoCommand('cmd_previousMsg')"/>
+ <key id="key_previousUnreadMsg" key="&prevUnreadMsgCmd.key;" oncommand="goDoCommand('cmd_previousUnreadMsg')"/>
+ <key id="key_archive" key="&archiveMsgCmd.key;" oncommand="goDoCommand('cmd_archive')" modifiers="shift"/>
+ <key id="key_goBack" key="&goBackCmd.commandKey;" oncommand="goDoCommand('cmd_goBack')"/>
+ <key id="key_goForward" key="&goForwardCmd.commandKey;" oncommand="goDoCommand('cmd_goForward');"/>
+ <key id="key_reply" key="&replyMsgCmd.key;" oncommand="goDoCommand('cmd_reply')" modifiers="accel"/>
+ <key id="key_replyall" key="&replyToAllMsgCmd.key;" oncommand="goDoCommand('cmd_replyall')" modifiers="accel, shift"/>
+ <key id="key_forward" key="&forwardMsgCmd.key;" oncommand="goDoCommand('cmd_forward')" modifiers="accel"/>
+ <key id="key_editAsNew"
+ key="&editAsNewMsgCmd.key;"
+ modifiers="accel"
+ oncommand="goDoCommand('cmd_editAsNew');"/>
+ <!-- for display on menus only -->
+ <key id="key_newMsgFromTemplate"
+ keycode="&newMsgFromTemplateCmd.keycode;"/>
+ <key id="key_watchThread" key="&watchThreadMenu.key;" oncommand="goDoCommand('cmd_watchThread')" />
+ <key id="key_killThread" key="&killThreadMenu.key;" oncommand="goDoCommand('cmd_killThread')" />
+ <key id="key_killSubthread" key="&killSubthreadMenu.key;" oncommand="goDoCommand('cmd_killSubthread')" modifiers="shift" />
+ <key id="key_print"/>
+ <key id="key_saveAsFile" key="&saveAsFileCmd.key;" oncommand="goDoCommand('cmd_saveAsFile')" modifiers="accel"/>
+ <key id="key_viewPageSource" key="&pageSourceCmd.key;" oncommand="goDoCommand('cmd_viewPageSource')" modifiers="accel"/>
+ <key id="key_getNewMessages" key="&getNewMsgCmd2.key;" oncommand="goDoCommand('cmd_getNewMessages')" modifiers="accel"/>
+ <key id="key_getAllNewMessages"
+ key="&getAllNewMsgCmd2.key;"
+ oncommand="goDoCommand('cmd_getMsgsForAuthAccounts');"
+ modifiers="accel, shift"/>
+ <keyset id="findKeys"/>
+ <key id="key_stop" keycode="VK_ESCAPE" command="cmd_stop"/>
+ <keyset id="viewZoomKeys"/>
+#ifndef XP_MACOSX
+ <key id="key_reload" keycode="VK_F5" oncommand="ReloadMessage();"/>
+#endif
+
+ <!-- View Toggle Keys -->
+#ifndef XP_MACOSX
+ <key id="key_toggleFolderPane"
+ keycode="VK_F9"
+ oncommand="MsgToggleFolderPane(true);"
+ observes="mailDisableKeys"/>
+#else
+ <key id="key_toggleFolderPane"
+ key="&toggleFolderPaneCmd.key;"
+ modifiers="accel,alt"
+ oncommand="MsgToggleFolderPane(true);"
+ observes="mailDisableKeys"/>
+#endif
+ <key id="key_toggleThreadPane"
+ keycode="VK_F8"
+ modifiers="shift"
+ oncommand="MsgToggleThreadPane();"
+ disabled="true"/>
+ <key id="key_toggleMessagePane"
+ keycode="VK_F8"
+ oncommand="MsgToggleMessagePane(true);"
+ disabled="true"/>
+
+ <key id="key_searchMail" key="&searchMailCmd.key;" oncommand="goDoCommand('cmd_search')" modifiers="accel, shift"/>
+
+ <key key="&focusSearchInput.key;"
+ modifiers="accel"
+ oncommand="focusElement(document.getElementById('searchInput'));"/>
+
+ <!-- Needed to support the Lightning Task filter See Bug 316916 -->
+ <key id="key_qfb_show"
+ key="&quickFilterBar.show.key2;"
+ modifiers="accel,shift"
+ command="cmd_showQuickFilterBar"/>
+</keyset>
+
+ <menupopup id="folderPaneContext"
+ onpopupshowing="return FillFolderPaneContextMenu();"
+ onpopuphiding="if (event.target == this) FolderPaneOnPopupHiding();">
+ <menuitem id="folderPaneContext-getMessages"
+ label="&folderContextGetMessages.label;"
+ accesskey="&folderContextGetMessages.accesskey;"
+ oncommand="MsgGetMessage();"/>
+ <menuitem id="folderPaneContext-openNewTab"
+ label="&folderContextOpenNewTab.label;"
+ accesskey="&folderContextOpenNewTab.accesskey;"
+ oncommand="FolderPaneContextMenuNewTab(event);"/>
+ <menuitem id="folderPaneContext-openNewWindow"
+ label="&folderContextOpenNewWindow.label;"
+ accesskey="&folderContextOpenNewWindow.accesskey;"
+ oncommand="MsgOpenNewWindowForFolder(null,-1);"/>
+ <menuitem id="folderPaneContext-searchMessages"
+ label="&folderContextSearchMessages.label;"
+ accesskey="&folderContextSearchMessages.accesskey;"
+ oncommand="gFolderTreeController.searchMessages();"/>
+ <menuitem id="folderPaneContext-subscribe"
+ label="&folderContextSubscribe.label;"
+ accesskey="&folderContextSubscribe.accesskey;"
+ oncommand="MsgSubscribe();"/>
+ <menuitem id="folderPaneContext-newsUnsubscribe"
+ label="&folderContextUnsubscribe.label;"
+ accesskey="&folderContextUnsubscribe.accesskey;"
+ oncommand="MsgUnsubscribe();"/>
+
+ <menuseparator id="folderPaneContext-sep1"/>
+
+ <menuitem id="folderPaneContext-new"
+ label="&folderContextNew.label;"
+ accesskey="&folderContextNew.accesskey;"
+ oncommand="gFolderTreeController.newFolder();"/>
+ <menuitem id="folderPaneContext-remove"
+ label="&folderContextRemove.label;"
+ accesskey="&folderContextRemove.accesskey;"
+ oncommand="gFolderTreeController.deleteFolder();"/>
+ <menuitem id="folderPaneContext-rename"
+ label="&folderContextRename.label;"
+ accesskey="&folderContextRename.accesskey;"
+ oncommand="gFolderTreeController.renameFolder();"/>
+
+ <menuitem id="folderPaneContext-compact"
+ label="&folderContextCompact.label;"
+ accesskey="&folderContextCompact.accesskey;"
+ oncommand="gFolderTreeController.compactFolders();"/>
+ <menuitem id="folderPaneContext-markMailFolderAllRead"
+ label="&folderContextMarkMailFolderRead.label;"
+ accesskey="&folderContextMarkMailFolderRead.accesskey;"
+ oncommand="MsgMarkAllRead();"/>
+ <menuitem id="folderPaneContext-markNewsgroupAllRead"
+ label="&folderContextMarkNewsgroupRead.label;"
+ accesskey="&folderContextMarkNewsgroupRead.accesskey;"
+ oncommand="MsgMarkAllRead();"/>
+ <menuitem id="folderPaneContext-emptyTrash"
+ label="&folderContextEmptyTrash.label;"
+ accesskey="&folderContextEmptyTrash.accesskey;"
+ oncommand="gFolderTreeController.emptyTrash();"/>
+ <menuitem id="folderPaneContext-emptyJunk"
+ label="&folderContextEmptyJunk.label;"
+ accesskey="&folderContextEmptyJunk.accesskey;"
+ oncommand="gFolderTreeController.emptyJunk();"/>
+ <menuitem id="folderPaneContext-sendUnsentMessages"
+ label="&folderContextSendUnsentMessages.label;"
+ accesskey="&folderContextSendUnsentMessages.accesskey;"
+ oncommand="goDoCommand('cmd_sendUnsentMsgs')"/>
+
+ <menuseparator id="folderPaneContext-sep-edit"/>
+
+ <menuitem id="folderPaneContext-favoriteFolder"
+ type="checkbox"
+ label="&folderContextFavoriteFolder.label;"
+ accesskey="&folderContextFavoriteFolder.accesskey;"
+ checked="false"
+ oncommand="ToggleFavoriteFolderFlag();"/>
+ <menuitem id="folderPaneContext-properties"
+ label="&folderContextProperties.label;"
+ accesskey="&folderContextProperties.accesskey;"
+ oncommand="gFolderTreeController.editFolder();"/>
+ <menuitem id="folderPaneContext-markAllFoldersRead"
+ label="&folderContextMarkAllFoldersRead.label;"
+ accesskey="&folderContextMarkAllFoldersRead.accesskey;"
+ oncommand="MsgMarkAllFoldersRead();"/>
+ <menuseparator id="folderPaneContext-sep4"/>
+ <menuitem id="folderPaneContext-settings"
+ label="&folderContextSettings.label;"
+ accesskey="&folderContextSettings.accesskey;"
+ oncommand="gFolderTreeController.editFolder();"/>
+ </menupopup>
+
+ <menupopup id="mailContext"
+ onpopupshowing="return FillMailContextMenu(this, event);"
+ onpopuphiding="MailContextOnPopupHiding(this, event);">
+ <menuitem id="context-openlinkintab"
+ label="&openLinkCmdInTab.label;"
+ accesskey="&openLinkCmdInTab.accesskey;"
+ usercontextid="0"
+ oncommand="gContextMenu.openLinkInTab(event);"/>
+ <menuitem id="context-openlink"
+ label="&openLinkCmd.label;"
+ accesskey="&openLinkCmd.accesskey;"
+ oncommand="gContextMenu.openLinkInWindow();"/>
+ <menuitem id="context-openlinkinprivatewindow"
+ label="&openLinkCmdInPrivateWindow.label;"
+ accesskey="&openLinkCmdInPrivateWindow.accesskey;"
+ oncommand="gContextMenu.openLinkInPrivateWindow();"/>
+ <menuseparator id="mailContext-sep-link"/>
+ <menuitem id="context-selectall"/>
+ <menuitem id="context-copy"/>
+ <menuitem id="context-searchselect"
+ oncommand="MsgOpenSearch(gContextMenu.searchSelected(), event);"/>
+ <menuitem id="mailContext-openNewTab"
+ label="&contextOpenNewTab.label;"
+ accesskey="&contextOpenNewTab.accesskey;"
+ oncommand="OpenMessageInNewTab(event);"/>
+ <menuitem id="mailContext-openNewWindow"
+ label="&contextOpenNewWindow.label;"
+ accesskey="&contextOpenNewWindow.accesskey;"
+ oncommand="MsgOpenNewWindowForMessage();"/>
+ <menuseparator id="mailContext-sep-open"/>
+ <menuitem id="mailContext-replySender"
+ label="&contextReplySender.label;"
+ accesskey="&contextReplySender.accesskey;"
+ oncommand="MsgReplySender(event);"/>
+ <menuitem id="mailContext-replyList"
+ label="&contextReplyList.label;"
+ accesskey="&contextReplyList.accesskey;"
+ oncommand="MsgReplyList(event);"/>
+ <menuitem id="mailContext-replyNewsgroup"
+ label="&contextReplyNewsgroup.label;"
+ accesskey="&contextReplyNewsgroup.accesskey;"
+ oncommand="MsgReplyGroup(event);"/>
+ <menuitem id="mailContext-replySenderAndNewsgroup"
+ label="&contextReplySenderAndNewsgroup.label;"
+ accesskey="&contextReplySenderAndNewsgroup.accesskey;"
+ oncommand="MsgReplyToSenderAndGroup(event);"/>
+ <menuitem id="mailContext-replyAll"
+ label="&contextReplyAll.label;"
+ accesskey="&contextReplyAll.accesskey;"
+ oncommand="MsgReplyToAllRecipients(event);"/>
+ <menuitem id="mailContext-forward"
+ label="&contextForward.label;"
+ accesskey="&contextForward.accesskey;"
+ oncommand="MsgForwardMessage(event);"/>
+ <menuitem id="mailContext-forwardAsAttachment"
+ label="&contextForwardAsAttachment.label;"
+ accesskey="&contextForwardAsAttachment.accesskey;"
+ oncommand="MsgForwardAsAttachment(event);"/>
+ <menuitem id="mailContext-editAsNew"
+ label="&contextEditMsgAsNew.label;"
+ accesskey="&contextEditMsgAsNew.accesskey;"
+ oncommand="MsgEditMessageAsNew(event);"/>
+ <menuitem id="mailContext-editDraftMsg"
+ label="&contextEditDraftMsg.label;"
+ default="true"
+ oncommand="MsgEditDraftMessage(event);"/>
+ <menuitem id="mailContext-newMsgFromTemplate"
+ label="&contextNewMsgFromTemplate.label;"
+ default="true"
+ oncommand="MsgNewMessageFromTemplate(event);"/>
+ <menuitem id="mailContext-editTemplateMsg"
+ label="&contextEditTemplate.label;"
+ accesskey="&contextEditTemplate.accesskey;"
+ oncommand="MsgEditTemplateMessage(event);"/>
+ <menuseparator id="mailContext-sep-tags"/>
+ <menu id="mailContext-tags"
+ label="&tagMenu.label;"
+ accesskey="&tagMenu.accesskey;">
+ <menupopup id="mailContext-tagpopup"
+ onpopupshowing="InitMessageTags(this)">
+ <menuitem id="mailContext-tagRemoveAll"
+ oncommand="RemoveAllMessageTags();"/>
+ <menuseparator id="mailContext-sep-afterTagRemoveAll"/>
+ <menuseparator id="mailContext-sep-beforeAddNewTag"/>
+ <menuitem id="mailContext-tagCustomize"
+ label="&tagCustomize.label;"
+ accesskey="&tagCustomize.accesskey;"
+ oncommand="goPreferences('tags_pane');"/>
+ </menupopup>
+ </menu>
+ <menu id="mailContext-mark"
+ label="&markMenu.label;"
+ accesskey="&markMenu.accesskey;">
+ <menupopup id="mailContext-markPopup"
+ onpopupshowing="InitMessageMark()">
+ <menuitem id="mailContext-markRead"
+ label="&markAsReadCmd.label;"
+ accesskey="&markAsReadCmd.accesskey;"
+ command="cmd_markAsRead"/>
+ <menuitem id="mailContext-markUnread"
+ label="&markAsUnreadCmd.label;"
+ accesskey="&markAsUnreadCmd.accesskey;"
+ command="cmd_markAsUnread"/>
+ <menuitem id="mailContext-markThreadAsRead"
+ label="&markThreadAsReadCmd.label;"
+ accesskey="&markThreadAsReadCmd.accesskey;"
+ command="cmd_markThreadAsRead"/>
+ <menuitem id="mailContext-markReadByDate"
+ label="&markReadByDateCmd.label;"
+ accesskey="&markReadByDateCmd.accesskey;"
+ command="cmd_markReadByDate"/>
+ <menuitem id="mailContext-markAllRead"
+ label="&markAllReadCmd.label;"
+ accesskey="&markAllReadCmd.accesskey;"
+ command="cmd_markAllRead"/>
+ <menuseparator id="mailContext-sep-afterMarkAllRead"/>
+ <menuitem id="mailContext-markFlagged"
+ type="checkbox"
+ label="&markFlaggedCmd.label;"
+ accesskey="&markFlaggedCmd.accesskey;"
+ command="cmd_markAsFlagged"/>
+ <menuseparator id="mailContext-sep-afterMarkFlagged"/>
+ <menuitem id="mailContext-markAsJunk"
+ label="&markAsJunkCmd.label;"
+ accesskey="&markAsJunkCmd.accesskey;"
+ command="cmd_markAsJunk"/>
+ <menuitem id="mailContext-markAsNotJunk"
+ label="&markAsNotJunkCmd.label;"
+ accesskey="&markAsNotJunkCmd.accesskey;"
+ command="cmd_markAsNotJunk"/>
+ <menuitem id="mailContext-recalculateJunkScore"
+ label="&recalculateJunkScoreCmd.label;"
+ accesskey="&recalculateJunkScoreCmd.accesskey;"
+ command="cmd_recalculateJunkScore"/>
+ <menuitem id="mailContext-markAsShowRemote"
+ label="&markAsShowRemoteCmd.label;"
+ accesskey="&markAsShowRemoteCmd.accesskey;"
+ command="cmd_markAsShowRemote"/>
+ <menuitem id="mailContext-markAsNotPhish"
+ label="&markAsNotPhishCmd.label;"
+ accesskey="&markAsNotPhishCmd.accesskey;"
+ command="cmd_markAsNotPhish"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="mailContext-sep-mark"/>
+ <menuitem id="mailContext-downloadflagged"
+ label="&downloadFlaggedCmd.label;"
+ accesskey="&downloadFlaggedCmd.accesskey;"
+ command="cmd_downloadFlagged"/>
+ <menuitem id="mailContext-downloadselected"
+ label="&downloadSelectedCmd.label;"
+ accesskey="&downloadSelectedCmd.accesskey;"
+ command="cmd_downloadSelected"/>
+ <menuseparator id="mailContext-sep-move"/>
+ <menuitem id="mailContext-copyMessageUrl"
+ label="&copyMessageLocation.label;"
+ accesskey="&copyMessageLocation.accesskey;"
+ oncommand="CopyMessageUrl()"/>
+ <menuitem id="mailContext-archive"
+ label="&contextArchive.label;"
+ accesskey="&contextArchive.accesskey;"
+ oncommand="MsgArchiveSelectedMessages(event);"/>
+ <menu id="mailContext-moveMenu"
+ label="&contextMoveMsgMenu.label;"
+ accesskey="&contextMoveMsgMenu.accesskey;"
+ oncommand="MsgMoveMessage(event.target._folder);">
+ <menupopup id="mailContext-fileHereMenu"
+ type="folder"
+ mode="filing"
+ showFileHereLabel="true"
+ showRecent="true"
+ recentLabel="&contextMoveCopyMsgRecentMenu.label;"
+ recentAccessKey="&contextMoveCopyMsgRecentMenu.accesskey;"
+ showFavorites="true"
+ favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
+ favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
+ </menu>
+ <menu id="mailContext-copyMenu"
+ label="&contextCopyMsgMenu.label;"
+ accesskey="&contextCopyMsgMenu.accesskey;"
+ oncommand="MsgCopyMessage(event.target._folder);">
+ <menupopup id="mailContext-copyHereMenu"
+ type="folder"
+ mode="filing"
+ showFileHereLabel="true"
+ showRecent="true"
+ recentLabel="&contextMoveCopyMsgRecentMenu.label;"
+ recentAccessKey="&contextMoveCopyMsgRecentMenu.accesskey;"
+ showFavorites="true"
+ favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
+ favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
+ </menu>
+ <menuitem id="mailContext-saveAs"
+ label="&contextSaveAs.label;"
+ accesskey="&contextSaveAs.accesskey;"
+ oncommand="MsgSaveAsFile();"/>
+ <menuitem id="mailContext-delete"
+ command="cmd_delete"/>
+ <menuseparator id="mailContext-sep-print"/>
+#ifndef XP_MACOSX
+ <menuitem id="mailContext-printpreview"
+ label="&contextPrintPreview.label;"
+ accesskey="&contextPrintPreview.accesskey;"
+ oncommand="PrintEnginePrintPreview();"/>
+#endif
+ <menuitem id="mailContext-print"
+ label="&contextPrint.label;"
+ accesskey="&contextPrint.accesskey;"
+ oncommand="PrintEnginePrint();"/>
+ <menuseparator id="mailContext-sep-edit"/>
+ <menuitem id="context-copylink"
+ label="&copyLinkCmd.label;"
+ accesskey="&copyLinkCmd.accesskey;"
+ command="cmd_copyLink"/>
+ <menuitem id="context-copyimage"
+ label="&copyImageCmd.label;"
+ accesskey="&copyImageCmd.accesskey;"
+ command="cmd_copyImage"/>
+ <menuitem id="context-viewimage"
+ label="&viewImageCmd.label;"
+ accesskey="&viewImageCmd.accesskey;"
+ oncommand="gContextMenu.viewMedia();"/>
+ <menuitem id="context-addemail"
+ label="&AddToAddressBook.label;"
+ accesskey="&AddToAddressBook.accesskey;"
+ oncommand="AddEmailToAddressBook(gContextMenu.getEmail(), gContextMenu.linkText());"/>
+ <menuseparator id="mailContext-sep-image"/>
+ <menuitem id="context-blockimage"
+ oncommand="gContextMenu.toggleImageBlocking(true);"/>
+ <menuitem id="context-unblockimage"
+ oncommand="gContextMenu.toggleImageBlocking(false);"/>
+ <menuseparator id="mailContext-sep-blockimage"/>
+ <menuitem id="context-composeemailto"
+ label="&SendMailTo.label;"
+ accesskey="&SendMailTo.accesskey;"
+ oncommand="SendMailTo(gContextMenu.getEmail(), event);"/>
+ <menuitem id="context-createfilterfrom"
+ label="&CreateFilterFrom.label;"
+ accesskey="&CreateFilterFrom.accesskey;"
+ oncommand="CreateFilterFromMail(gContextMenu.getEmail());"/>
+ <menuitem id="context-copyemail"
+ label="&copyEmailCmd.label;"
+ accesskey="&copyEmailCmd.accesskey;"
+ oncommand="gContextMenu.copyEmail();"/>
+ <menuseparator id="mailContext-sep-copy"/>
+ <menuitem id="context-savelink"
+ label="&saveLinkCmd.label;"
+ accesskey="&saveLinkCmd.accesskey;"
+ oncommand="gContextMenu.saveLink();"/>
+ <menuitem id="context-saveimage"
+ label="&saveImageCmd.label;"
+ accesskey="&saveImageCmd.accesskey;"
+ oncommand="gContextMenu.saveImage();"/>
+ <menuitem id="context-bookmarklink"
+ label="&bookmarkLinkCmd.label;"
+ accesskey="&bookmarkLinkCmd.accesskey;"
+ oncommand="PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(gContextMenu.linkURL),
+ gContextMenu.linkText());"/>
+ </menupopup>
+
+ <menupopup id="remoteContentOptions"
+ onpopupshowing="onRemoteContentOptionsShowing(event);"
+ oncommand="allowRemoteContentForURI(event.target);">
+ <menuitem id="remoteContentOptionAllowForMsg"
+ label="&remoteContentOptionsAllowForMsg.label;"
+ accesskey="&remoteContentOptionsAllowForMsg.accesskey;"
+ oncommand="LoadMsgWithRemoteContent();"/>
+ <menuseparator id="remoteContentSettingsMenuSeparator"/>
+ <menuitem id="editRemoteContentSettings"
+ label="&editRemoteContentSettings.label;"
+ accesskey="&editRemoteContentSettings.accesskey;"
+ oncommand="editRemoteContentSettings();"/>
+ </menupopup>
+
+ <toolbar type="menubar"
+ id="mail-toolbar-menubar2"
+ class="chromeclass-menubar"
+ persist="collapsed"
+ grippytooltiptext="&menuBar.tooltip;"
+ customizable="true"
+ defaultset="menubar-items"
+ mode="icons"
+ iconsize="small"
+ defaultmode="icons"
+ defaulticonsize="small"
+ context="toolbar-context-menu">
+ <toolbaritem id="menubar-items"
+ class="menubar-items"
+ align="center">
+ </toolbaritem>
+ </toolbar>
+
+<menubar id="mail-menubar">
+ <menu id="menu_File" >
+ <menupopup id="menu_FilePopup" onpopupshowing="file_init();">
+ <menu id="menu_New">
+ <menupopup id="menu_NewPopup" onpopupshowing="menu_new_init();">
+ <menuitem id="newNewMsgCmd"
+ label="&newNewMsgCmd.label;"
+ accesskey="&newNewMsgCmd.accesskey;"
+ key="key_newMessage"
+ oncommand="MsgNewMessage(null);"/>
+ <menuitem id="menu_newFolder"
+ label="&newFolderCmd.label;"
+ accesskey="&newFolderCmd.accesskey;"
+ oncommand="gFolderTreeController.newFolder();"/>
+ <menuitem id="menu_newVirtualFolder" label="&newVirtualFolderCmd.label;"
+ oncommand="gFolderTreeController.newVirtualFolder();"
+ accesskey="&newVirtualFolderCmd.accesskey;"/>
+ <menuitem id="newAccountMenuItem"
+ label="&newAccountCmd.label;"
+ accesskey="&newAccountCmd.accesskey;"
+ oncommand="MsgAccountWizard();"/>
+ <menuseparator id="newPopupMenuSeparator"/>
+ <menuitem id="menu_newCard"/>
+ <menuitem id="menu_newTab"
+ label="&newTabCmd.label;"
+ accesskey="&newTabCmd.accesskey;"
+ key="key_newTab"
+ oncommand="MsgOpenNewTab();"/>
+ <menuitem id="menu_newNavigator"/>
+ <menuitem id="menu_newPrivateWindow"/>
+ <menuitem id="menu_newEditor"/>
+ </menupopup>
+ </menu>
+ <menuitem id="openMessageFileMenuitem" label="&openMessageFileCmd.label;"
+ key="key_openFileMessage"
+ accesskey="&openMessageFileCmd.accesskey;"
+ oncommand="MsgOpenFromFile();"/>
+ <menuitem id="menu_close"/>
+ <menuseparator id="fileMenuAfterCloseSeparator"/>
+ <menu id="menu_saveAs" label="&saveAsMenu.label;" accesskey="&saveAsMenu.accesskey;">
+ <menupopup id="menu_SavePopup">
+ <menuitem id="menu_saveAsFile"
+ label="&saveAsFileCmd.label;"
+ accesskey="&saveAsFileCmd.accesskey;"
+ key="key_saveAsFile"
+ command="cmd_saveAsFile"/>
+ <menuitem id="menu_saveAsTemplate"
+ label="&saveAsTemplateCmd.label;"
+ accesskey="&saveAsTemplateCmd.accesskey;"
+ command="cmd_saveAsTemplate"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="fileMenuAfterSaveSeparator"/>
+ <menuitem id="menu_getNewMsg"
+ label="&getNewMsgCmd.label;"
+ accesskey="&getNewMsgCmd.accesskey;"
+ key="key_getNewMessages"
+ command="cmd_getNewMessages"/>
+ <menu id="menu_getAllNewMsg"
+ label="&getNewMsgForCmd.label;"
+ accesskey="&getNewMsgForCmd.accesskey;"
+ oncommand="MsgGetMessagesForAccount();">
+ <menupopup id="menu_getAllNewMsgPopup"
+ type="folder"
+ mode="getMail"
+ expandFolders="false"
+ oncommand="MsgGetMessagesForAccount(event.target._folder); event.stopPropagation();">
+ <menuitem id="menu_getAllNewMsgPopupMenu"
+ label="&getAllNewMsgCmdPopupMenu.label;"
+ accesskey="&getAllNewMsgCmdPopupMenu.accesskey;"
+ key="key_getAllNewMessages"
+ command="cmd_getMsgsForAuthAccounts"/>
+ <menuseparator id="fileMenuAfterGetNewMsgSeparator"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_getnextnmsg" label="&getNextNMsgCmd.label;"
+ accesskey="&getNextNMsgCmd.accesskey;"
+ command="cmd_getNextNMessages"/>
+ <menuitem id="menu_sendunsentmsgs" label="&sendUnsentCmd.label;"
+ accesskey="&sendUnsentCmd.accesskey;"
+ command="cmd_sendUnsentMsgs"/>
+ <menuitem label="&subscribeCmd.label;"
+ accesskey="&subscribeCmd.accesskey;"
+ command="cmd_subscribe"/>
+ <menuseparator id="fileMenuAfterSubscribeSeparator"/>
+ <menuitem id="menu_renameFolder" label="&renameFolder.label;"
+ accesskey="&renameFolder.accesskey;"
+ command="cmd_renameFolder"
+ observes="mailHideMenus"/>
+ <menuitem id="menu_compactFolder" label="&compactFolders.label;"
+ accesskey="&compactFolders.accesskey;"
+ command="cmd_compactFolder"
+ observes="mailHideMenus"/>
+ <menuitem id="menu_emptyTrash" label="&emptyTrashCmd.label;"
+ accesskey="&emptyTrashCmd.accesskey;"
+ command="cmd_emptyTrash"
+ observes="mailHideMenus"/>
+ <menuseparator id="trashMenuSeparator" observes="mailHideMenus"/>
+ <menu id="menu_Offline"
+ label="&offlineMenu.label;"
+ accesskey="&offlineMenu.accesskey;">
+ <menupopup id="menu_OfflinePopup">
+ <menuitem id="offlineGoOfflineCmd"/>
+ <menuseparator id="offlineMenuAfterGoSeparator"/>
+ <menuitem id="menu_synchronizeOffline"
+ label="&synchronizeOfflineCmd.label;"
+ accesskey="&synchronizeOfflineCmd.accesskey;"
+ command="cmd_synchronizeOffline"/>
+ <menuitem id="menu_settingsOffline"
+ label="&settingsOfflineCmd.label;"
+ accesskey="&settingsOfflineCmd.accesskey;"
+ command="cmd_settingsOffline"/>
+ <menuseparator id="offlineMenuAfterSettingsSeparator"/>
+ <menuitem id="menu_downloadFlagged"
+ label="&downloadFlaggedCmd.label;"
+ accesskey="&downloadFlaggedCmd.accesskey;"
+ command="cmd_downloadFlagged"/>
+ <menuitem id="menu_downloadSelected"
+ label="&downloadSelectedCmd.label;"
+ accesskey="&downloadSelectedCmd.accesskey;"
+ command="cmd_downloadSelected"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="fileMenuAfterOfflineSeparator"/>
+ <menuitem id="menu_printSetup"/>
+ <menuitem id="menu_printPreview"/>
+ <menuitem id="menu_print"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_Edit" oncommand="CommandUpdate_UndoRedo();">
+ <menupopup id="menu_EditPopup" onpopupshowing="InitEditMessagesMenu()">
+ <menuitem id="menu_undo"/>
+ <menuitem id="menu_redo"/>
+ <menuseparator id="editMenuAfterRedoSeparator"/>
+ <menuitem id="menu_cut"/>
+ <menuitem id="menu_copy"/>
+ <menuitem id="menu_paste"/>
+ <menuitem id="menu_delete" command="cmd_delete"/>
+ <menuseparator id="editMenuAfterDeleteSeparator"/>
+ <menu id="menu_select" label="&selectMenu.label;"
+ accesskey="&selectMenu.accesskey;">
+ <menupopup id="menu_SelectPopup">
+ <menuitem id="menu_mailSelectAll"
+ label="&all.label;"
+ accesskey="&all.accesskey;" key="key_selectAll"
+ command="cmd_selectAll"/>
+ <menuseparator id="selectMenuSeparator"/>
+ <menuitem id="menu_selectThread"
+ label="&selectThreadCmd.label;"
+ accesskey="&selectThreadCmd.accesskey;" key="key_selectThread"
+ command="cmd_selectThread"/>
+ <menuitem id="menu_selectFlagged"
+ label="&selectFlaggedCmd.label;"
+ accesskey="&selectFlaggedCmd.accesskey;"
+ command="cmd_selectFlagged"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="editMenuAfterSelectSeparator"/>
+ <menuitem id="menu_find" label="&findCmd.label;"/>
+ <menuitem id="menu_findNext"/>
+ <menuitem id="menu_findPrev"/>
+ <menuseparator id="editMenuAfterFindSeparator"/>
+ <menuitem id="menu_findTypeLinks"/>
+ <menuitem id="menu_findTypeText"/>
+ <menuseparator id="editPropertiesSeparator"/>
+ <menuitem id="menu_favoriteFolder"
+ type="checkbox"
+ label="&menuFavoriteFolder.label;"
+ accesskey="&menuFavoriteFolder.accesskey;"
+ checked="false"
+ oncommand="ToggleFavoriteFolderFlag();"
+ observes="mailHideMenus"/>
+ <menuitem id="menu_properties" label="&folderPropsCmd.label;"
+ accesskey="&folderPropsCmd.accesskey;"
+ command="cmd_properties"
+ observes="mailHideMenus"/>
+ <menuitem id="menu_accountmgr"
+ label="&accountManagerCmd.label;"
+ accesskey="&accountManagerCmd.accesskey;"
+ oncommand="MsgAccountManager(null);"/>
+ <menuitem id="menu_preferences" oncommand="goPreferences('mailnews_pane')"/>
+ </menupopup>
+ </menu>
+
+ <menu id="menu_View">
+ <menupopup id="menu_View_Popup" onpopupshowing="view_init()">
+ <menu id="menu_Toolbars">
+ <menupopup id="view_toolbars_popup"
+ onpopupshowing="onViewToolbarsPopupShowing(event)"
+ oncommand="onViewToolbarCommand(event);">
+ <menuitem id="menu_showTaskbar"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_MessagePaneLayout" label="&messagePaneLayoutStyle.label;"
+ accesskey="&messagePaneLayoutStyle.accesskey;" observes="mailHideMenus">
+ <menupopup id="view_layout_popup" onpopupshowing="InitViewLayoutStyleMenu(event)">
+ <menuitem id="messagePaneClassic" type="radio" label="&messagePaneClassic.label;" name="viewlayoutgroup"
+ accesskey="&messagePaneClassic.accesskey;" oncommand="ChangeMailLayout(kClassicMailLayout);"/>
+ <menuitem id="messagePaneWide" type="radio" label="&messagePaneWide.label;" name="viewlayoutgroup"
+ accesskey="&messagePaneWide.accesskey;" oncommand="ChangeMailLayout(kWideMailLayout);"/>
+ <menuitem id="messagePaneVertical" type="radio" label="&messagePaneVertical.label;" name="viewlayoutgroup"
+ accesskey="&messagePaneVertical.accesskey;" oncommand="ChangeMailLayout(kVerticalMailLayout);"/>
+ <menuseparator id="viewMenuAfterPaneVerticalSeparator"/>
+ <menuitem id="menu_showMessagePane"
+ type="checkbox"
+ label="&showMessagePaneCmd.label;"
+ accesskey="&showMessagePaneCmd.accesskey;"
+ key="key_toggleMessagePane"
+ oncommand="MsgToggleMessagePane(true);"
+ observes="mailHideMenus"/>
+ <menuitem id="menu_showThreadPane"
+ type="checkbox"
+ label="&showThreadPaneCmd.label;"
+ accesskey="&showThreadPaneCmd.accesskey;"
+ key="key_toggleThreadPane"
+ oncommand="MsgToggleThreadPane();"
+ observes="mailHideMenus"/>
+ <menuitem id="menu_showFolderPane"
+ type="checkbox"
+ label="&showFolderPaneCmd.label;"
+ accesskey="&showFolderPaneCmd.accesskey;"
+ key="key_toggleFolderPane"
+ oncommand="MsgToggleFolderPane(true);"
+ observes="mailHideMenus"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="viewMessagesMenuSeparator" observes="mailHideMenus"/>
+ <menu id="viewSortMenu" label="&sortMenu.label;"
+ accesskey="&sortMenu.accesskey;" observes="mailHideMenus">
+ <menupopup id="menu_viewSortPopup" onpopupshowing="InitViewSortByMenu()">
+ <menuitem id="sortByDateMenuitem" type="radio" name="sortby" label="&sortByDateCmd.label;" accesskey="&sortByDateCmd.accesskey;" oncommand="MsgSortThreadPane('byDate')"/>
+ <menuitem id="sortByReceivedMenuitem" type="radio" name="sortby" label="&sortByReceivedCmd.label;" accesskey="&sortByReceivedCmd.accesskey;" oncommand="MsgSortThreadPane('byReceived')"/>
+ <menuitem id="sortByFlagMenuitem" type="radio" name="sortby" label="&sortByFlagCmd.label;" accesskey="&sortByFlagCmd.accesskey;" oncommand="MsgSortThreadPane('byFlagged')"/>
+ <menuitem id="sortByOrderReceivedMenuitem" type="radio" name="sortby" label="&sortByOrderReceivedCmd.label;" accesskey="&sortByOrderReceivedCmd.accesskey;" oncommand="MsgSortThreadPane('byId')"/>
+ <menuitem id="sortByPriorityMenuitem" type="radio" name="sortby" label="&sortByPriorityCmd.label;" accesskey="&sortByPriorityCmd.accesskey;" oncommand="MsgSortThreadPane('byPriority')"/>
+ <menuitem id="sortByFromMenuitem" type="radio" name="sortby" label="&sortByFromCmd.label;" accesskey="&sortByFromCmd.accesskey;" oncommand="MsgSortThreadPane('byAuthor')"/>
+ <menuitem id="sortByRecipientMenuitem" type="radio" name="sortby" label="&sortByRecipientCmd.label;" accesskey="&sortByRecipientCmd.accesskey;" oncommand="MsgSortThreadPane('byRecipient')"/>
+ <menuitem id="sortBySizeMenuitem" type="radio" name="sortby" label="&sortBySizeCmd.label;" accesskey="&sortBySizeCmd.accesskey;" oncommand="MsgSortThreadPane('bySize')"/>
+ <menuitem id="sortByStatusMenuitem" type="radio" name="sortby" label="&sortByStatusCmd.label;" accesskey="&sortByStatusCmd.accesskey;" oncommand="MsgSortThreadPane('byStatus')"/>
+ <menuitem id="sortBySubjectMenuitem" type="radio" name="sortby" label="&sortBySubjectCmd.label;" accesskey="&sortBySubjectCmd.accesskey;" oncommand="MsgSortThreadPane('bySubject')"/>
+ <menuitem id="sortByUnreadMenuitem" type="radio" name="sortby" label="&sortByUnreadCmd.label;" accesskey="&sortByUnreadCmd.accesskey;" oncommand="MsgSortThreadPane('byUnread')"/>
+ <menuitem id="sortByTagsMenuitem" type="radio" name="sortby" label="&sortByTagsCmd.label;" accesskey="&sortByTagsCmd.accesskey;" oncommand="MsgSortThreadPane('byTags')"/>
+ <menuitem id="sortByJunkStatusMenuitem" type="radio" name="sortby" label="&sortByJunkStatusCmd.label;" accesskey="&sortByJunkStatusCmd.accesskey;" oncommand="MsgSortThreadPane('byJunkStatus')"/>
+ <menuitem id="sortByAttachmentsMenuitem" type="radio" name="sortby" label="&sortByAttachmentsCmd.label;" accesskey="&sortByAttachmentsCmd.accesskey;" oncommand="MsgSortThreadPane('byAttachments')"/>
+ <menuseparator id="sortAfterAttachmentSeparator"/>
+ <menuitem id="sortAscending" type="radio" name="sortdirection" label="&sortAscending.label;" accesskey="&sortAscending.accesskey;" oncommand="MsgSortAscending()"/>
+ <menuitem id="sortDescending" type="radio" name="sortdirection" label="&sortDescending.label;" accesskey="&sortDescending.accesskey;" oncommand="MsgSortDescending()"/>
+ <menuseparator id="sortAfterDescendingSeparator"/>
+ <menuitem id="sortThreaded" type="radio" name="threaded" label="&sortThreaded.label;" accesskey="&sortThreaded.accesskey;" oncommand="MsgSortThreaded();"/>
+ <menuitem id="sortUnthreaded" type="radio" name="threaded" label="&sortUnthreaded.label;" accesskey="&sortUnthreaded.accesskey;" oncommand="MsgSortUnthreaded();"/>
+ <menuitem id="groupBySort" type="radio" name="group" label="&groupBySort.label;" accesskey="&groupBySort.accesskey;" oncommand="MsgGroupBySort();"/>
+ </menupopup>
+ </menu>
+ <menu id="viewMessageViewMenu" label="&msgsMenu.label;" accesskey="&msgsMenu.accesskey;"
+ observes="mailHideMenus" oncommand="ViewChangeByMenuitem(event.target);">
+ <menupopup id="viewMessagePopup"
+ onpopupshowing="RefreshViewPopup(this);">
+ <menuitem id="viewMessageAll"
+ label="&viewAll.label;"
+ accesskey="&viewAll.accesskey;"
+ type="radio"
+ name="viewmessages"
+ value="0"/>
+ <menuitem id="viewMessageUnread"
+ label="&viewUnread.label;"
+ accesskey="&viewUnread.accesskey;"
+ type="radio"
+ name="viewmessages"
+ value="1"/>
+ <menuitem id="viewMessageNotDeleted"
+ label="&viewNotDeleted.label;"
+ accesskey="&viewNotDeleted.accesskey;"
+ type="radio"
+ name="viewmessages"
+ value="3"/>
+ <menuseparator id="messageViewAfterUnreadSeparator"/>
+ <menu id="viewMessageTags" label="&viewTags.label;" accesskey="&viewTags.accesskey;">
+ <menupopup id="viewMessageTagsPopup"
+ onpopupshowing="RefreshTagsPopup(this);"/>
+ </menu>
+ <menu id="viewMessageCustomViews" label="&viewCustomViews.label;" accesskey="&viewCustomViews.accesskey;">
+ <menupopup id="viewMessageCustomViewsPopup"
+ onpopupshowing="RefreshCustomViewsPopup(this);"/>
+ </menu>
+ <menuseparator id="messageViewAfterCustomSeparator"/>
+ <menuitem id="viewMessageVirtualFolder" value="7" label="&viewVirtualFolder.label;" accesskey="&viewVirtualFolder.accesskey;"/>
+ <menuitem id="viewMessageCustomize" value="8" label="&viewCustomizeView.label;" accesskey="&viewCustomizeView.accesskey;"/>
+ </menupopup>
+ </menu>
+ <menu id="viewMessagesMenu" label="&threads.label;"
+ accesskey="&threads.accesskey;" observes="mailHideMenus">
+ <menupopup id="menu_ThreadsPopup" onpopupshowing="InitViewMessagesMenu()">
+ <menuitem id="viewAllMessagesMenuItem" type="radio" name="viewmessages" label="&allMsgsCmd.label;" accesskey="&allMsgsCmd.accesskey;" disabled="true" command="cmd_viewAllMsgs"/>
+ <menuitem id="viewUnreadMessagesMenuItem" type="radio" name="viewmessages" label="&unreadMsgsCmd.label;" accesskey="&unreadMsgsCmd.accesskey;" disabled="true" command="cmd_viewUnreadMsgs"/>
+ <menuitem id="viewThreadsWithUnreadMenuItem" type="radio" name="viewmessages" label="&threadsWithUnreadCmd.label;" accesskey="&threadsWithUnreadCmd.accesskey;" disabled="true" command="cmd_viewThreadsWithUnread"/>
+ <menuitem id="viewWatchedThreadsWithUnreadMenuItem" type="radio" name="viewmessages" label="&watchedThreadsWithUnreadCmd.label;" accesskey="&watchedThreadsWithUnreadCmd.accesskey;" disabled="true" command="cmd_viewWatchedThreadsWithUnread"/>
+ <menuseparator id="threadsAfterWatchedSeparator"/>
+ <menuitem id="viewIgnoredThreadsMenuItem" type="checkbox" label="&ignoredThreadsCmd.label;" disabled="true" command="cmd_viewIgnoredThreads" accesskey="&ignoredThreadsCmd.accesskey;"/>
+ <menuseparator id="threadsAfterIgnoredSeparator"/>
+ <menuitem label="&expandAllThreadsCmd.label;" accesskey="&expandAllThreadsCmd.accesskey;" key="key_expandAllThreads" disabled="true" command="cmd_expandAllThreads"/>
+ <menuitem label="&collapseAllThreadsCmd.label;" accesskey="&collapseAllThreadsCmd.accesskey;" key="key_collapseAllThreads" disabled="true" command="cmd_collapseAllThreads"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="viewAfterThreadsSeparator"/>
+ <menu id="viewheadersmenu" label="&headersMenu.label;" accesskey="&headersMenu.accesskey;">
+ <menupopup id="menu_HeadersPopup" onpopupshowing="InitViewHeadersMenu();">
+ <menuitem id="viewallheaders"
+ type="radio"
+ name="viewheadergroup"
+ label="&headersAllCmd.label;"
+ accesskey="&headersAllCmd.accesskey;"
+ command="cmd_viewAllHeader"/>
+ <menuitem id="viewnormalheaders"
+ type="radio"
+ name="viewheadergroup"
+ label="&headersNormalCmd.label;"
+ accesskey="&headersNormalCmd.accesskey;"
+ command="cmd_viewNormalHeader"/>
+ </menupopup>
+ </menu>
+ <menu id="viewBodyMenu" accesskey="&bodyMenu.accesskey;" label="&bodyMenu.label;">
+ <menupopup id="viewBodyPopMenu" onpopupshowing="InitViewBodyMenu()">
+ <menuitem id="bodyAllowHTML"
+ type="radio"
+ name="bodyPlaintextVsHTMLPref"
+ label="&bodyAllowHTML.label;"
+ accesskey="&bodyAllowHTML.accesskey;"
+ oncommand="MsgBodyAllowHTML()"/>
+ <menuitem id="bodySanitized"
+ type="radio"
+ name="bodyPlaintextVsHTMLPref"
+ label="&bodySanitized.label;"
+ accesskey="&bodySanitized.accesskey;"
+ oncommand="MsgBodySanitized()"/>
+ <menuitem id="bodyAsPlaintext"
+ type="radio"
+ name="bodyPlaintextVsHTMLPref"
+ label="&bodyAsPlaintext.label;"
+ accesskey="&bodyAsPlaintext.accesskey;"
+ oncommand="MsgBodyAsPlaintext()"/>
+ <menuitem id="bodyAllParts"
+ type="radio"
+ name="bodyPlaintextVsHTMLPref"
+ label="&bodyAllParts.label;"
+ accesskey="&bodyAllParts.accesskey;"
+ oncommand="MsgBodyAllParts();"/>
+ </menupopup>
+ </menu>
+ <menu id="viewFeedSummary"
+ label="&bodyMenuFeed.label;"
+ accesskey="&bodyMenuFeed.accesskey;">
+ <menupopup id="viewFeedSummaryPopupMenu"
+ onpopupshowing="InitViewBodyMenu()">
+ <menuitem id="bodyFeedSummaryAllowHTML"
+ type="radio"
+ name="viewFeedBodyHTMLGroup"
+ label="&bodyAllowHTML.label;"
+ accesskey="&bodyAllowHTML.accesskey;"
+ oncommand="MsgFeedBodyRenderPrefs(false, 0, 0)"/>
+ <menuitem id="bodyFeedSummarySanitized"
+ type="radio"
+ name="viewFeedBodyHTMLGroup"
+ label="&bodySanitized.label;"
+ accesskey="&bodySanitized.accesskey;"
+ oncommand="MsgFeedBodyRenderPrefs(false, 3, gDisallow_classes_no_html)"/>
+ <menuitem id="bodyFeedSummaryAsPlaintext"
+ type="radio"
+ name="viewFeedBodyHTMLGroup"
+ label="&bodyAsPlaintext.label;"
+ accesskey="&bodyAsPlaintext.accesskey;"
+ oncommand="MsgFeedBodyRenderPrefs(true, 1, gDisallow_classes_no_html)"/>
+ <menuseparator id="viewFeedSummarySeparator"/>
+ <menuitem id="bodyFeedGlobalWebPage"
+ type="radio"
+ name="viewFeedSummaryGroup"
+ label="&viewFeedWebPage.label;"
+ accesskey="&viewFeedWebPage.accesskey;"
+ oncommand="FeedMessageHandler.onSelectPref = 0"/>
+ <menuitem id="bodyFeedGlobalSummary"
+ type="radio"
+ name="viewFeedSummaryGroup"
+ label="&viewFeedSummary.label;"
+ accesskey="&viewFeedSummary.accesskey;"
+ oncommand="FeedMessageHandler.onSelectPref = 1"/>
+ <menuitem id="bodyFeedPerFolderPref"
+ type="radio"
+ name="viewFeedSummaryGroup"
+ label="&viewFeedSummaryFeedPropsPref.label;"
+ accesskey="&viewFeedSummaryFeedPropsPref.accesskey;"
+ oncommand="FeedMessageHandler.onSelectPref = 2"/>
+ </menupopup>
+ </menu>
+ <menuitem id="viewAttachmentsInlineMenuitem"
+ type="checkbox"
+ checked="true"
+ label="&viewAttachmentsInlineCmd.label;"
+ accesskey="&viewAttachmentsInlineCmd.accesskey;"
+ oncommand="ToggleInlineAttachment(event.target)"/>
+ <menuseparator id="viewAfterAttachmentsSeparator"/>
+ <menuitem id="stopMenuitem"
+ label="&stopCmd.label;"
+ accesskey="&stopCmd.accesskey;"
+ key="key_stop"
+ disabled="true"
+ command="cmd_stop"/>
+ <menuitem id="menu_Stop"
+ label="&reloadCmd.label;"
+ key="key_reload"
+ accesskey="&reloadCmd.accesskey;"
+ command="cmd_reload"/>
+ <menuseparator id="viewAfterStopSeparator"/>
+ <!-- overlayed from viewZoomOverlay.xul -->
+ <menu id="menu_zoom"/>
+ <menu id="charsetMenu"
+ onpopupshowing="UpdateCharsetMenu(msgWindow.mailCharacterSet, this);"
+ oncommand="MailSetCharacterSet(event);"/>
+ <menuseparator id="viewAfterCharsetSeparator"/>
+ <menuitem id="pageSourceMenuItem" label="&pageSourceCmd.label;" key="key_viewPageSource" accesskey="&pageSourceCmd.accesskey;" command="cmd_viewPageSource"/>
+ <menuitem id="appmenu_securityStatus"
+ label="&menu_securityStatus.label;"
+ accesskey="&menu_securityStatus.accesskey;"
+ command="cmd_viewSecurityStatus"/>
+ <menuseparator observes="mailHideMenus"/>
+ <!-- overlayed from viewApplyThemeOverlay.xul -->
+ <menu id="menu_viewApplyTheme" observes="mailHideMenus"/>
+ </menupopup>
+ </menu>
+
+ <menu id="goMenu" label="&goMenu.label;" accesskey="&goMenu.accesskey;">
+ <menupopup id="menu_GoPopup" onpopupshowing="InitGoMessagesMenu();">
+ <menu id="goNextMenu" label="&nextMenu.label;" accesskey="&nextMenu.accesskey;">
+ <menupopup id="menu_GoNextPopup">
+ <menuitem id="nextMsgMenuItem"
+ label="&nextMsgCmd.label;"
+ accesskey="&nextMsgCmd.accesskey;"
+ key="key_nextMsg"
+ command="cmd_nextMsg"/>
+ <menuitem id="nextUnreadMsgMenuItem"
+ label="&nextUnreadMsgCmd.label;"
+ accesskey="&nextUnreadMsgCmd.accesskey;"
+ key="key_nextUnreadMsg"
+ command="cmd_nextUnreadMsg"/>
+ <menuitem id="nextFlaggedMenuItem"
+ label="&nextFlaggedMsgCmd.label;"
+ accesskey="&nextFlaggedMsgCmd.accesskey;"
+ command="cmd_nextFlaggedMsg"/>
+ <menuseparator id="goNextAfterFlaggedSeparator"/>
+ <menuitem id="nextUnreadThreadMenuItem"
+ label="&nextUnreadThread.label;"
+ accesskey="&nextUnreadThread.accesskey;"
+ key="key_nextUnreadThread"
+ command="cmd_nextUnreadThread"/>
+ </menupopup>
+ </menu>
+ <menu id="goPreviousMenu" label="&prevMenu.label;" accesskey="&prevMenu.accesskey;">
+ <menupopup id="menu_GoPreviousPopup">
+ <menuitem id="prevMsgMenuItem"
+ label="&prevMsgCmd.label;"
+ accesskey="&prevMsgCmd.accesskey;"
+ key="key_previousMsg"
+ command="cmd_previousMsg"/>
+ <menuitem id="prevUnreadMsgMenuItem"
+ label="&prevUnreadMsgCmd.label;"
+ accesskey="&prevUnreadMsgCmd.accesskey;"
+ key="key_previousUnreadMsg"
+ command="cmd_previousUnreadMsg"/>
+ <menuitem id="prevFlaggedMenuItem"
+ label="&prevFlaggedMsgCmd.label;"
+ accesskey="&prevFlaggedMsgCmd.accesskey;"
+ command="cmd_previousFlaggedMsg"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_goBack"
+ label="&goBackCmd.label;"
+ accesskey="&goBackCmd.accesskey;"
+ key="key_goBack"
+ command="cmd_goBack"/>
+ <menuitem id="menu_goForward"
+ label="&goForwardCmd.label;"
+ accesskey="&goForwardCmd.accesskey;"
+ key="key_goForward"
+ command="cmd_goForward"/>
+ <menuseparator id="goNextAfterForwardSeparator" observes="mailHideMenus"/>
+ <menu id="goFolderMenu"
+ label="&folderMenu.label;"
+ accesskey="&folderMenu.accesskey;"
+ oncommand="SelectMsgFolder(event.target._folder);"
+ observes="mailHideMenus">
+ <menupopup id="menu_GoFolderPopup"
+ type="folder"
+ showFileHereLabel="true"
+ showRecent="true"
+ recentLabel="&contextMoveCopyMsgRecentMenu.label;"
+ recentAccessKey="&contextMoveCopyMsgRecentMenu.accesskey;"
+ showFavorites="true"
+ favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
+ favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
+ </menu>
+ <menuseparator id="goFolderSeparator"/>
+ <menuitem id="goStartPage" label="&startPageCmd.label;"
+ accesskey="&startPageCmd.accesskey;" command="cmd_goStartPage"
+ observes="mailHideMenus"/>
+ <menuseparator id="goNextAfterStartPageSeparator" observes="mailHideMenus"/>
+ </menupopup>
+ </menu>
+
+ <menu id="messageMenu" label="&msgMenu.label;" accesskey="&msgMenu.accesskey;">
+ <menupopup id="messageMenuPopup" onpopupshowing="InitMessageMenu();">
+ <menuitem id="newMsgCmd"
+ label="&newMsgCmd.label;"
+ accesskey="&newMsgCmd.accesskey;"
+ key="key_newMessage"
+ oncommand="MsgNewMessage(null);"/>
+ <menuitem id="replyMainMenu"
+ label="&replyMsgCmd.label;"
+ accesskey="&replyMsgCmd.accesskey;"
+ key="key_reply"
+ command="cmd_reply"/>
+ <menuitem id="replyListMainMenu"
+ label="&replyListCmd.label;"
+ accesskey="&replyListCmd.accesskey;"
+ command="cmd_replyList"/>
+ <menuitem id="replyNewsgroupMainMenu"
+ label="&replyNewsgroupCmd.label;"
+ accesskey="&replyNewsgroupCmd.accesskey;"
+ key="key_reply"
+ command="cmd_replyGroup"/>
+ <menuitem id="replySenderMainMenu"
+ label="&replySenderCmd.label;"
+ accesskey="&replySenderCmd.accesskey;"
+ command="cmd_replySender"/>
+ <menuitem id="replyallMainMenu"
+ label="&replyToAllMsgCmd.label;"
+ accesskey="&replyToAllMsgCmd.accesskey;"
+ key="key_replyall"
+ command="cmd_replyall"/>
+ <menuitem id="replySenderAndNewsgroupMainMenu"
+ label="&replyToSenderAndNewsgroupCmd.label;"
+ accesskey="&replyToSenderAndNewsgroupCmd.accesskey;"
+ key="key_replyall" command="cmd_replySenderAndGroup"/>
+ <menuitem id="replyAllRecipientsMainMenu"
+ label="&replyToAllRecipientsCmd.label;"
+ accesskey="&replyToAllRecipientsCmd.accesskey;"
+ command="cmd_replyAllRecipients"/>
+ <menuitem id="menu_forwardMsg"
+ label="&forwardMsgCmd.label;"
+ accesskey="&forwardMsgCmd.accesskey;"
+ key="key_forward"
+ command="cmd_forward"/>
+ <menu id="forwardAsMenu" label="&forwardAsMenu.label;" accesskey="&forwardAsMenu.accesskey;">
+ <menupopup id="menu_forwardAsPopup">
+ <menuitem id="menu_forwardAsInline"
+ label="&forwardAsInline.label;"
+ accesskey="&forwardAsInline.accesskey;"
+ command="cmd_forwardInline"/>
+ <menuitem id="menu_forwardAsAttachment"
+ label="&forwardAsAttachmentCmd.label;"
+ accesskey="&forwardAsAttachmentCmd.accesskey;"
+ command="cmd_forwardAttachment"/>
+ </menupopup>
+ </menu>
+ <menuitem id="menu_editMsgAsNew"
+ label="&editAsNewMsgCmd.label;"
+ accesskey="&editAsNewMsgCmd.accesskey;"
+ key="key_editAsNew"
+ command="cmd_editAsNew"/>
+ <menuitem id="menu_editDraftMsg"
+ label="&editDraftMsgCmd.label;"
+ accesskey="&editDraftMsgCmd.accesskey;"
+ command="cmd_editDraftMsg"/>
+ <menuitem id="menu_newMsgFromTemplate"
+ label="&newMsgFromTemplateCmd.label;"
+ key="key_newMsgFromTemplate"
+ command="cmd_newMsgFromTemplate"/>
+ <menuitem id="menu_editTemplate"
+ label="&editTemplateMsgCmd.label;"
+ accesskey="&editTemplateMsgCmd.accesskey;"
+ command="cmd_editTemplateMsg"/>
+ <menuitem id="openMessageWindowMenuitem"
+ label="&openMessageWindowCmd.label;"
+ command="cmd_openMessage"
+ accesskey="&openMessageWindowCmd.accesskey;"
+ key="key_openMessage" observes="mailHideMenus"/>
+ <menu id="openFeedMessage"
+ label="&openFeedMessage.label;"
+ accesskey="&openFeedMessage.accesskey;">
+ <menupopup id="menu_openFeedMessage">
+ <menuitem id="menu_openFeedWebPageInWindow"
+ type="radio"
+ name="openFeedGroup"
+ label="&openFeedWebPageInWindow.label;"
+ accesskey="&openFeedWebPageInWindow.accesskey;"
+ oncommand="FeedMessageHandler.onOpenPref = 0"/>
+ <menuitem id="menu_openFeedSummaryInWindow"
+ type="radio"
+ name="openFeedGroup"
+ label="&openFeedSummaryInWindow.label;"
+ accesskey="&openFeedSummaryInWindow.accesskey;"
+ oncommand="FeedMessageHandler.onOpenPref = 1"/>
+ <menuitem id="menu_openFeedWebPageInMessagePane"
+ type="radio"
+ name="openFeedGroup"
+ label="&openFeedWebPageInMP.label;"
+ accesskey="&openFeedWebPageInMP.accesskey;"
+ oncommand="FeedMessageHandler.onOpenPref = 2"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="messageAfterOpenMsgSeparator"/>
+ <menu id="msgAttachmentMenu" label="&openAttachmentCmd.label;"
+ accesskey="&openAttachmentCmd.accesskey;" disabled="true">
+ <menupopup id="attachmentMenuList" onpopupshowing="FillAttachmentListPopup(this);"/>
+ </menu>
+ <menuseparator id="messageAfterAttachmentMenuSeparator"/>
+ <menuitem id="archiveMainMenu"
+ label="&archiveMsgCmd.label;"
+ accesskey="&archiveMsgCmd.accesskey;"
+ key="key_archive"
+ command="cmd_archive"/>
+ <menu id="moveMenu"
+ label="&moveMsgToMenu.label;"
+ accesskey="&moveMsgToMenu.accesskey;"
+ oncommand="MsgMoveMessage(event.target._folder);">
+ <menupopup id="menu_MovePopup"
+ type="folder"
+ mode="filing"
+ showFileHereLabel="true"
+ showRecent="true"
+ recentLabel="&moveCopyMsgRecentMenu.label;"
+ recentAccessKey="&moveCopyMsgRecentMenu.accesskey;"
+ showFavorites="true"
+ favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
+ favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
+ </menu>
+ <menu id="copyMenu"
+ label="&copyMsgToMenu.label;"
+ accesskey="&copyMsgToMenu.accesskey;"
+ oncommand="MsgCopyMessage(event.target._folder);">
+ <menupopup id="menu_copyPopup"
+ type="folder"
+ mode="filing"
+ showFileHereLabel="true"
+ showRecent="true"
+ recentLabel="&moveCopyMsgRecentMenu.label;"
+ recentAccessKey="&moveCopyMsgRecentMenu.accesskey;"
+ showFavorites="true"
+ favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
+ favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
+ </menu>
+ <menu id="tagMenu" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
+ <menupopup id="tagMenu-tagpopup" onpopupshowing="InitMessageTags(this)">
+ <menuitem id="tagMenu-tagRemoveAll" oncommand="RemoveAllMessageTags();"/>
+ <menuseparator id="tagMenuAfterRemoveSeparator"/>
+ <menuseparator id="tagMenuBeforeCustomizeSeparator"/>
+ <menuitem id="tagMenu-tagCustomize"
+ label="&tagCustomize.label;"
+ accesskey="&tagCustomize.accesskey;"
+ oncommand="goPreferences('tags_pane');"/>
+ </menupopup>
+ </menu>
+ <menu id="markMenu" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
+ <menupopup id="menu_MarkPopup" onpopupshowing="InitMessageMark()">
+ <menuitem id="markReadMenuItem"
+ label="&markAsReadCmd.label;"
+ accesskey="&markAsReadCmd.accesskey;"
+ key="key_markAsRead"
+ command="cmd_markAsRead"/>
+ <menuitem id="markUnreadMenuItem"
+ label="&markAsUnreadCmd.label;"
+ accesskey="&markAsUnreadCmd.accesskey;"
+ key="key_markAsUnread"
+ command="cmd_markAsUnread"/>
+ <menuitem id="markThreadReadMenuItem"
+ label="&markThreadAsReadCmd.label;"
+ accesskey="&markThreadAsReadCmd.accesskey;"
+ key="key_markThreadAsRead"
+ command="cmd_markThreadAsRead"/>
+ <menuitem id="markReadByDateMenuItem"
+ label="&markReadByDateCmd.label;"
+ accesskey="&markReadByDateCmd.accesskey;"
+ key="key_markReadByDate"
+ command="cmd_markReadByDate"/>
+ <menuitem id="markAllReadMenuItem"
+ label="&markAllReadCmd.label;"
+ accesskey="&markAllReadCmd.accesskey;"
+ key="key_markAllRead"
+ command="cmd_markAllRead"/>
+ <menuseparator id="markMenuAfterAllReadSeparator"/>
+ <menuitem id="markFlaggedMenuItem"
+ type="checkbox"
+ label="&markFlaggedCmd.label;"
+ accesskey="&markFlaggedCmd.accesskey;"
+ key="key_toggleFlagged"
+ command="cmd_markAsFlagged"/>
+ <menuseparator id="markMenuAfterFlaggedSeparator"/>
+ <menuitem id="markAsJunkMenuItem"
+ label="&markAsJunkCmd.label;"
+ accesskey="&markAsJunkCmd.accesskey;"
+ key="key_markJunk"
+ command="cmd_markAsJunk"/>
+ <menuitem id="markAsNotJunkMenuItem"
+ label="&markAsNotJunkCmd.label;"
+ accesskey="&markAsNotJunkCmd.accesskey;"
+ key="key_markNotJunk"
+ command="cmd_markAsNotJunk"/>
+ <menuitem id="recalculateJunkScoreMenuItem"
+ label="&recalculateJunkScoreCmd.label;"
+ accesskey="&recalculateJunkScoreCmd.accesskey;"
+ command="cmd_recalculateJunkScore"/>
+ <menuitem id="markAsShowRemoteMenuitem"
+ label="&markAsShowRemoteCmd.label;"
+ accesskey="&markAsShowRemoteCmd.accesskey;"
+ key="key_markShowRemote"
+ command="cmd_markAsShowRemote"/>
+ <menuitem id="markAsNotPhishMenuItem"
+ label="&markAsNotPhishCmd.label;"
+ accesskey="&markAsNotPhishCmd.accesskey;"
+ key="key_markNotPhish"
+ command="cmd_markAsNotPhish"/>
+ </menupopup>
+ </menu>
+ <menuseparator id="messageMenuAfterMarkSeparator"/>
+ <menuitem id="createFilter"
+ label="&createFilter.label;"
+ accesskey="&createFilter.accesskey;"
+ command="cmd_createFilterFromMenu"/>
+ <menuseparator id="threadItemsSeparator"/>
+ <menuitem id="menu_cancel"
+ label="&cancelNewsMsgCmd.label;"
+ accesskey="&cancelNewsMsgCmd.accesskey;"
+ command="cmd_cancel"/>
+ <menuitem id="killThread"
+ label="&killThreadMenu.label;"
+ accesskey="&killThreadMenu.accesskey;"
+ key="key_killThread" command="cmd_killThread"/>
+ <menuitem id="killSubthread"
+ label="&killSubthreadMenu.label;"
+ accesskey="&killSubthreadMenu.accesskey;"
+ key="key_killSubthread" command="cmd_killSubthread"/>
+ <menuitem id="watchThread"
+ label="&watchThreadMenu.label;"
+ accesskey="&watchThreadMenu.accesskey;"
+ key="key_watchThread" command="cmd_watchThread"/>
+ </menupopup>
+</menu>
+
+<menu id="tasksMenu">
+ <menupopup id="taskPopup" onpopupshowing="document.commandDispatcher.updateCommands('create-menu-tasks')">
+ <menuitem id="menu_SearchMail"
+ label="&searchMailCmd.label;"
+ key="key_searchMail"
+ accesskey="&searchMailCmd.accesskey;"
+ command="cmd_search"/>
+ <menuitem id="menu_SearchAddresses"
+ label="&searchAddressesCmd.label;"
+ accesskey="&searchAddressesCmd.accesskey;"
+ oncommand="MsgSearchAddresses()"/>
+ <menuseparator id="tasksMenuAfterAddressesSeparator"/>
+ <menuitem id="menu_Filters"
+ label="&filtersCmd.label;"
+ accesskey="&filtersCmd.accesskey;"
+ command="cmd_displayMsgFilters"/>
+ <menuitem id="applyFilters"
+ label="&filtersApply.label;"
+ accesskey="&filtersApply.accesskey;"
+ command="cmd_applyFilters"/>
+ <menuitem id="applyFiltersToSelection"
+ label="&filtersApplyToMessage.label;"
+ accesskey="&filtersApplyToMessage.accesskey;"
+ command="cmd_applyFiltersToSelection"/>
+ <menuseparator id="tasksMenuAfterApplySeparator"/>
+ <menuitem id="runJunkControls"
+ label="&runJunkControls.label;"
+ accesskey="&runJunkControls.accesskey;"
+ command="cmd_runJunkControls"/>
+ <menuitem id="deleteJunk"
+ label="&deleteJunk.label;"
+ accesskey="&deleteJunk.accesskey;"
+ command="cmd_deleteJunk"/>
+ <menuseparator id="tasksMenuAfterDeleteSeparator"/>
+ <menuitem id="menu_import"
+ label="&importCmd.label;"
+ accesskey="&importCmd.accesskey;"
+ oncommand="toImport();"/>
+ <menuseparator/>
+ </menupopup>
+</menu>
+<menu id="windowMenu"/>
+<menu id="menu_Help"/>
+<spacer flex="100%"/>
+</menubar>
+
+<toolbox id="mail-toolbox"
+ mode="full"
+ defaultmode="full">
+ <toolbar class="toolbar-primary chromeclass-toolbar"
+ id="msgToolbar"
+ persist="collapsed"
+ grippytooltiptext="&mailToolbar.tooltip;"
+ toolbarname="&showMessengerToolbarCmd.label;"
+ accesskey="&showMessengerToolbarCmd.accesskey;"
+ customizable="true"
+ defaultset="button-getmsg,button-newmsg,separator,button-reply,button-replyall,button-forward,separator,button-goback,button-goforward,button-next,button-junk,button-delete,button-mark,spring,throbber-box"
+ context="toolbar-context-menu">
+ </toolbar>
+ <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+
+ <toolbarpalette id="MailToolbarPalette">
+ <toolbarbutton id="button-getmsg"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&getMsgButton.label;"
+ tooltiptext="&getMsgButton.tooltip;"
+ observes="button_getNewMessages"
+ oncommand="MsgGetMessagesForAccount();">
+ <menupopup id="button-getMsgPopup"
+ type="folder"
+ mode="getMail"
+ expandFolders="false"
+ onpopupshowing="getMsgToolbarMenu_init();"
+ oncommand="MsgGetMessagesForAccount(event.target._folder); event.stopPropagation();">
+ <menuitem id="button-getAllNewMsg"
+ label="&getAllNewMsgCmd.label;"
+ accesskey="&getAllNewMsgCmd.accesskey;"
+ command="cmd_getMsgsForAuthAccounts"/>
+ <menuseparator id="button-getAllNewMsgSeparator"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="button-newmsg"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&newMsgButton.label;"
+ tooltiptext="&newMsgButton.tooltip;"
+ oncommand="MsgNewMessage(event)">
+ <menupopup id="button-newMsgPopup"
+ onpopupshowing="InitNewMsgMenu(this);">
+ <menuitem id="button-newMsgHTML"
+ label="&newHTMLMessageCmd.label;"
+ accesskey="&newHTMLMessageCmd.accesskey;"
+ mode="HTML"/>
+ <menuitem id="button-newMsgPlain"
+ label="&newPlainTextMessageCmd.label;"
+ accesskey="&newPlainTextMessageCmd.accesskey;"
+ mode="PlainText"/>
+ <menuitem id="newMsgButton-mail-menuitem" hidden="true"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="button-reply"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&replyButton.label;"
+ tooltiptext="&replyButton.tooltip;"
+ observes="button_reply"
+ oncommand="MsgReplyMessage(event)">
+ <menupopup id="button-replyPopup"
+ onpopupshowing="InitMessageReply(this);">
+ <menuitem label="&replyMsgCmd.label;"
+ accesskey="&replyMsgCmd.accesskey;"
+ command="cmd_reply"
+ default="true"/>
+ <menuitem label="&replyListCmd.label;"
+ accesskey="&replyListCmd.accesskey;"
+ command="cmd_replyList"/>
+ <menuitem label="&replyNewsgroupCmd.label;"
+ accesskey="&replyNewsgroupCmd.accesskey;"
+ command="cmd_replyGroup"
+ default="true"/>
+ <menuitem label="&replySenderCmd.label;"
+ accesskey="&replySenderCmd.accesskey;"
+ command="cmd_replySender"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="button-replyall"
+ class="toolbarbutton-1"
+ label="&replyAllButton.label;"
+ tooltiptext="&replyAllButton.tooltip;"
+ tooltiptextmail="&replyAllButton.tooltip;"
+ tooltiptextnews="&replyAllButtonNews.tooltip;"
+ observes="button_replyall"
+ oncommand="MsgReplyToAllMessage(event)">
+ <menupopup id="button-replyallPopup">
+ <menuitem label="&replyToSenderAndNewsgroupCmd.label;"
+ accesskey="&replyToSenderAndNewsgroupCmd.accesskey;"
+ command="cmd_replySenderAndGroup"
+ default="true"/>
+ <menuitem label="&replyToAllRecipientsCmd.label;"
+ accesskey="&replyToAllRecipientsCmd.accesskey;"
+ command="cmd_replyAllRecipients"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="button-forward"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&forwardButton.label;"
+ tooltiptext="&forwardButton.tooltip;"
+ observes="button_forward"
+ oncommand="MsgForwardMessage(event)">
+ <menupopup id="button-forwardPopup"
+ onpopupshowing="InitMessageForward(this);">
+ <menuitem label="&forwardAsInline.label;"
+ accesskey="&forwardAsInline.accesskey;"
+ command="cmd_forwardInline"/>
+ <menuitem label="&forwardAsAttachmentCmd.label;"
+ accesskey="&forwardAsAttachmentCmd.accesskey;"
+ command="cmd_forwardAttachment"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="button-file"
+ type="menu"
+ class="toolbarbutton-1"
+ label="&fileButton.label;"
+ observes="button_file"
+ tooltiptext="&fileButton.tooltip;"
+ oncommand="MsgMoveMessage(event.target._folder);">
+ <menupopup id="button-filePopup"
+ type="folder"
+ mode="filing"
+ showRecent="true"
+ showFileHereLabel="true"
+ recentLabel="&moveCopyMsgRecentMenu.label;"
+ recentAccessKey="&moveCopyMsgRecentMenu.accesskey;"
+ showFavorites="true"
+ favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
+ favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="button-goback"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&goBackButton.label;"
+ tooltiptext="&goBackButton.tooltip;"
+ observes="button_goBack"
+ oncommand="goDoCommand('cmd_goBack')">
+ <menupopup id="button-goBackPopup"
+ onpopupshowing="InitBackToolbarMenu(this)"
+ oncommand="NavigateToUri(event.target);"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="button-goforward"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&goForwardButton.label;"
+ tooltiptext="&goForwardButton.tooltip;"
+ observes="button_goForward"
+ oncommand="goDoCommand('cmd_goForward')">
+ <menupopup id="button-goForwardPopup"
+ onpopupshowing="InitForwardToolbarMenu(this)"
+ oncommand="NavigateToUri(event.target);"/>
+ </toolbarbutton>
+
+ <toolbarbutton id="button-next"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&nextButton.label;"
+ tooltiptext="&nextButton.tooltip;"
+ observes="button_next"
+ oncommand="goDoCommand('button_next')">
+ <menupopup id="button-nextPopup"
+ onpopupshowing="InitGoMessagesMenu();">
+ <menuitem label="&nextMsgCmd.label;"
+ accesskey="&nextMsgCmd.accesskey;"
+ command="cmd_nextMsg"/>
+ <menuitem label="&nextUnreadMsgCmd.label;"
+ accesskey="&nextUnreadMsgCmd.accesskey;"
+ command="cmd_nextUnreadMsg" default="true"/>
+ <menuitem label="&nextFlaggedMsgCmd.label;"
+ accesskey="&nextFlaggedMsgCmd.accesskey;"
+ command="cmd_nextFlaggedMsg"/>
+ <menuseparator/>
+ <menuitem label="&nextUnreadThread.label;"
+ accesskey="&nextUnreadThread.accesskey;"
+ command="cmd_nextUnreadThread"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <toolbaritem id="button-junk"
+ title="&junkButton.label;"
+ observes="button_junk">
+ <deck id="junk-deck"
+ oncommand="goDoCommand('button_junk')">
+ <toolbarbutton id="button-isJunk"
+ class="toolbarbutton-1"
+ label="&junkButton.label;"
+ tooltiptext="&junkButton.tooltip;"
+ observes="button-junk"/>
+ <toolbarbutton id="button-notJunk"
+ class="toolbarbutton-1"
+ label="&notJunkButton.label;"
+ tooltiptext="&notJunkButton.tooltip;"
+ observes="button-junk"/>
+ </deck>
+ </toolbaritem>
+
+ <toolbaritem id="button-delete"
+ title="&deleteButton.label;"
+ observes="button_delete">
+ <deck id="delete-deck">
+ <toolbarbutton id="button-mark-deleted"
+ class="toolbarbutton-1"
+ label="&deleteButton.label;"
+ tooltiptext="&deleteButton.tooltip;"
+ observes="button-delete"
+ oncommand="goDoCommand(event.shiftKey ? 'button_shiftDelete' : 'button_delete')"/>
+ <toolbarbutton id="button-mark-undelete"
+ class="toolbarbutton-1"
+ label="&undeleteButton.label;"
+ tooltiptext="&undeleteButton.tooltip;"
+ observes="button-delete"
+ oncommand="goDoCommand('button_delete')"/>
+ </deck>
+ </toolbaritem>
+
+ <toolbarbutton id="button-mark"
+ class="toolbarbutton-1"
+ type="menu-button"
+ label="&markButton.label;"
+ oncommand="goDoCommand('button_mark')"
+ observes="button_mark" tooltiptext="&markButton.tooltip;">
+ <menupopup id="button-markPopup"
+ onpopupshowing="InitMessageMark()">
+ <menuitem id="markReadToolbarItem"
+ label="&markAsReadCmd.label;"
+ accesskey="&markAsReadCmd.accesskey;"
+ command="cmd_markAsRead"/>
+ <menuitem id="markUnreadToolbarItem"
+ label="&markAsUnreadCmd.label;"
+ accesskey="&markAsUnreadCmd.accesskey;"
+ command="cmd_markAsUnread"/>
+ <menuitem id="button-markThreadAsRead"
+ label="&markThreadAsReadCmd.label;"
+ accesskey="&markThreadAsReadCmd.accesskey;"
+ command="cmd_markThreadAsRead"/>
+ <menuitem id="button-markReadByDate"
+ label="&markReadByDateCmd.label;"
+ accesskey="&markReadByDateCmd.accesskey;"
+ command="cmd_markReadByDate"/>
+ <menuitem id="button-markAllRead"
+ label="&markAllReadCmd.label;"
+ accesskey="&markAllReadCmd.accesskey;"
+ command="cmd_markAllRead"/>
+ <menuseparator id="button-markAllReadSeparator"/>
+ <menuitem id="markFlaggedToolbarItem"
+ type="checkbox"
+ label="&markFlaggedCmd.label;"
+ accesskey="&markFlaggedCmd.accesskey;"
+ command="cmd_markAsFlagged"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="print-button"
+ label="&printButton.label;"
+ tooltiptext="&printButton.tooltip;"
+ observes="button_print"/>
+ <toolbarbutton id="button-stop"
+ class="toolbarbutton-1"
+ label="&stopButton.label;"
+ tooltiptext="&stopButton.tooltip;"
+ command="cmd_stop"/>
+ <toolbaritem id="button-search-container"
+ title="&searchButton.title;"
+ align="center"
+ class="toolbaritem-noline chromeclass-toolbar-additional">
+ <button id="button-search"
+ label="&searchButton.label;"
+ accesskey="&searchButton.accesskey;"
+ tooltiptext="&advancedButton.tooltip;"
+ observes="button_search"
+ oncommand="goDoCommand('button_search')"/>
+ <button id="button-advanced"
+ label="&advancedButton.label;"
+ accesskey="&advancedButton.accesskey;"
+ tooltiptext="&advancedButton.tooltip;"
+ observes="button_search"
+ oncommand="goDoCommand('button_search')"/>
+ </toolbaritem>
+ <toolbaritem id="throbber-box"/>
+ <!-- see utilityOverlay.xul
+ <toolbarbutton id="sync-button"/> -->
+ </toolbarpalette>
+
+</toolbox>
+
+<statusbar id="status-bar"
+ class="chromeclass-status" >
+ <statusbarpanel id="component-bar"/>
+ <statusbarpanel id="statusText"
+ label="&statusText.label;"
+ crop="right"
+ flex="1"/>
+ <statusbarpanel id="statusbar-progresspanel"
+ class="statusbarpanel-progress"
+ collapsed="true">
+ <progressmeter id="statusbar-icon"
+ class="progressmeter-statusbar"
+ mode="normal"
+ value="0"/>
+ </statusbarpanel>
+ <statusbarpanel id="unreadMessageCount"
+ hidden="true"/>
+ <statusbarpanel id="totalMessageCount"
+ hidden="true"/>
+ <statusbarpanel id="signed-status"
+ class="statusbarpanel-iconic"
+ collapsed="true"
+ oncommand="showMessageReadSecurityInfo();"/>
+ <statusbarpanel id="encrypted-status"
+ class="statusbarpanel-iconic"
+ collapsed="true"
+ oncommand="showMessageReadSecurityInfo();"/>
+ <statusbarpanel id="offline-status"
+ class="statusbarpanel-iconic"
+ checkfunc="MailCheckBeforeOfflineChange();" />
+</statusbar>
+
+</overlay>
diff --git a/comm/suite/mailnews/content/messageWindow.js b/comm/suite/mailnews/content/messageWindow.js
new file mode 100644
index 0000000000..fd61c6737f
--- /dev/null
+++ b/comm/suite/mailnews/content/messageWindow.js
@@ -0,0 +1,1044 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+
+/* This is where functions related to the standalone message window are kept */
+
+// from MailNewsTypes.h
+const nsMsgKey_None = 0xFFFFFFFF;
+const nsMsgViewIndex_None = 0xFFFFFFFF;
+
+/* globals for a particular window */
+
+var gCurrentMessageUri;
+var gCurrentFolderUri;
+var gThreadPaneCommandUpdater = null;
+var gCurrentMessageIsDeleted = false;
+var gNextMessageViewIndexAfterDelete = -2;
+var gCurrentFolderToRerootForStandAlone;
+var gRerootOnFolderLoadForStandAlone = false;
+var gNextMessageAfterLoad = null;
+
+// the folderListener object
+var folderListener = {
+ onFolderAdded: function(parentFolder, child) {},
+ onMessageAdded: function(parentFolder, msg) {},
+ onFolderRemoved: function(parentFolder, child) {},
+ onMessageRemoved: function(parentFolder, msg)
+ {
+ if (parentFolder.URI != gCurrentFolderUri)
+ return;
+ if (extractMsgKeyFromURI() == msg.messageKey)
+ gCurrentMessageIsDeleted = true;
+ },
+
+ onFolderPropertyChanged: function(item, property, oldValue, newValue) {},
+ onFolderIntPropertyChanged: function(item, property, oldValue, newValue) {
+ if (item.URI == gCurrentFolderUri) {
+ if (property == "TotalMessages" || property == "TotalUnreadMessages") {
+ UpdateStandAloneMessageCounts();
+ }
+ }
+ },
+ onFolderBoolPropertyChanged: function(item, property, oldValue, newValue) {},
+ onFolderUnicharPropertyChanged: function(item, property, oldValue, newValue){},
+ onFolderPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
+
+ onFolderEvent: function(folder, event) {
+ if (event == "DeleteOrMoveMsgCompleted")
+ HandleDeleteOrMoveMsgCompleted(folder);
+ else if (event == "DeleteOrMoveMsgFailed")
+ HandleDeleteOrMoveMsgFailed(folder);
+ else if (event == "FolderLoaded") {
+ if (folder) {
+ var uri = folder.URI;
+ if (uri == gCurrentFolderToRerootForStandAlone) {
+ gCurrentFolderToRerootForStandAlone = null;
+ folder.endFolderLoading();
+ if (gRerootOnFolderLoadForStandAlone) {
+ RerootFolderForStandAlone(uri);
+ }
+ }
+ }
+ }
+ else if (event == "JunkStatusChanged") {
+ HandleJunkStatusChanged(folder);
+ }
+ }
+}
+
+var messagepaneObserver = {
+ onDrop(aEvent) {
+ let dragSession = Cc["@mozilla.org/widget/dragservice;1"]
+ .getService(Ci.nsIDragService)
+ .getCurrentSession();
+ if (!this.canDrop(aEvent, dragSession)) {
+ return;
+ }
+ let sourceUri = aEvent.dataTransfer.getData("text/x-moz-message");
+ if (sourceUri != gCurrentMessageUri)
+ {
+ var msgHdr = GetMsgHdrFromUri(sourceUri);
+
+ // Reset the window's message uri and folder uri vars, and
+ // update the command handlers to what's going to be used.
+ // This has to be done before the call to CreateView().
+ gCurrentMessageUri = sourceUri;
+ gCurrentFolderUri = msgHdr.folder.URI;
+ UpdateMailToolbar('onDrop');
+
+ // even if the folder uri's match, we can't use the existing view
+ // (msgHdr.folder.URI == windowID.gCurrentFolderUri)
+ // the reason is quick search and mail views.
+ // see bug #187673
+ CreateView(dragSession.sourceNode.ownerDocument.defaultView.gDBView);
+ LoadMessageByMsgKey(msgHdr.messageKey);
+ }
+ aEvent.stopPropagation();
+ },
+
+ onDragOver(aEvent) {
+ var messagepanebox = document.getElementById("messagepanebox");
+ messagepanebox.setAttribute("dragover", "true");
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ },
+
+ onDragExit(aEvent) {
+ var messagepanebox = document.getElementById("messagepanebox");
+ messagepanebox.removeAttribute("dragover");
+ },
+
+ canDrop(aEvent, aDragSession) {
+ // Allow drop from mail:3pane window only - 4xp.
+ var doc = aDragSession.sourceNode.ownerDocument;
+ var elem = doc.getElementById("messengerWindow");
+ return (elem && (elem.getAttribute("windowtype") == "mail:3pane"));
+ },
+};
+
+function nsMsgDBViewCommandUpdater()
+{}
+
+function UpdateStandAloneMessageCounts()
+{
+ // hook for extra toolbar items
+ Services.obs.notifyObservers(window,
+ "mail:updateStandAloneMessageCounts");
+}
+
+nsMsgDBViewCommandUpdater.prototype =
+{
+ updateCommandStatus : function()
+ {
+ // the back end is smart and is only telling us to update command status
+ // when the # of items in the selection has actually changed.
+ UpdateMailToolbar("dbview, std alone window");
+ },
+
+ displayMessageChanged : function(aFolder, aSubject, aKeywords)
+ {
+ setTitleFromFolder(aFolder, aSubject);
+ ClearPendingReadTimer(); // we are loading / selecting a new message so kill the mark as read timer for the currently viewed message
+ gCurrentMessageUri = gDBView.URIForFirstSelectedMessage;
+ UpdateStandAloneMessageCounts();
+ goUpdateCommand("button_delete");
+ goUpdateCommand("button_junk");
+ goUpdateCommand("button_goBack");
+ goUpdateCommand("button_goForward");
+ },
+
+ updateNextMessageAfterDelete : function()
+ {
+ SetNextMessageAfterDelete();
+ },
+
+ summarizeSelection: function() {return false},
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgDBViewCommandUpdater) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_NOINTERFACE;
+ }
+}
+
+function HandleDeleteOrMoveMsgCompleted(folder)
+{
+ if ((folder.URI == gCurrentFolderUri) && gCurrentMessageIsDeleted)
+ {
+ gDBView.onDeleteCompleted(true);
+ gCurrentMessageIsDeleted = false;
+ if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None)
+ {
+ var nextMstKey = gDBView.getKeyAt(gNextMessageViewIndexAfterDelete);
+ if (nextMstKey != nsMsgKey_None &&
+ !Services.prefs.getBoolPref("mail.close_message_window.on_delete"))
+ LoadMessageByViewIndex(gNextMessageViewIndexAfterDelete);
+ else
+ window.close();
+ }
+ else
+ {
+ // close the stand alone window because there are no more messages in the folder
+ window.close();
+ }
+ }
+}
+
+function HandleDeleteOrMoveMsgFailed(folder)
+{
+ gDBView.onDeleteCompleted(false);
+ if ((folder.URI == gCurrentFolderUri) && gCurrentMessageIsDeleted)
+ gCurrentMessageIsDeleted = false;
+}
+
+function IsCurrentLoadedFolder(folder)
+{
+ return (folder.URI == gCurrentFolderUri);
+}
+
+function OnLoadMessageWindow()
+{
+ AddMailOfflineObserver();
+ CreateMailWindowGlobals();
+ verifyAccounts(null);
+
+ InitMsgWindow();
+
+ messenger.setWindow(window, msgWindow);
+ // FIX ME - later we will be able to use onload from the overlay
+ OnLoadMsgHeaderPane();
+
+ var nsIFolderListener = Ci.nsIFolderListener;
+ var notifyFlags = nsIFolderListener.removed | nsIFolderListener.event |
+ nsIFolderListener.intPropertyChanged;
+ MailServices.mailSession.AddFolderListener(folderListener, notifyFlags);
+
+ var originalView = null;
+ var folder = null;
+ var messageUri;
+ var loadCustomMessage = false; //set to true when either loading a message/rfc822 attachment or a .eml file
+ if (window.arguments)
+ {
+ if (window.arguments[0])
+ {
+ try
+ {
+ messageUri = window.arguments[0];
+ if (messageUri instanceof Ci.nsIURI)
+ {
+ loadCustomMessage = /type=application\/x-message-display/.test(messageUri.spec);
+ gCurrentMessageUri = messageUri.spec;
+ if (messageUri instanceof Ci.nsIMsgMailNewsUrl)
+ folder = messageUri.folder;
+ }
+ }
+ catch(ex)
+ {
+ folder = null;
+ dump("## ex=" + ex + "\n");
+ }
+
+ if (!gCurrentMessageUri)
+ gCurrentMessageUri = window.arguments[0];
+ }
+ else
+ gCurrentMessageUri = null;
+
+ if (window.arguments[1])
+ gCurrentFolderUri = window.arguments[1];
+ else
+ gCurrentFolderUri = folder ? folder.URI : null;
+
+ if (window.arguments[2])
+ originalView = window.arguments[2];
+
+ }
+
+ CreateView(originalView);
+
+ // Before and after callbacks for the customizeToolbar code
+ var mailToolbox = getMailToolbox();
+ mailToolbox.customizeInit = MailToolboxCustomizeInit;
+ mailToolbox.customizeDone = MailToolboxCustomizeDone;
+ mailToolbox.customizeChange = MailToolboxCustomizeChange;
+
+ setTimeout(OnLoadMessageWindowDelayed, 0, loadCustomMessage);
+
+ SetupCommandUpdateHandlers();
+
+ window.addEventListener("AppCommand", HandleAppCommandEvent, true);
+}
+
+function HandleAppCommandEvent(evt)
+{
+ evt.stopPropagation();
+ switch (evt.command)
+ {
+ case "Back":
+ goDoCommand('cmd_goBack');
+ break;
+ case "Forward":
+ goDoCommand('cmd_goForward');
+ break;
+ case "Stop":
+ goDoCommand('cmd_stop');
+ break;
+ case "Search":
+ goDoCommand('cmd_search');
+ break;
+ case "Bookmarks":
+ toAddressBook();
+ break;
+ case "Reload":
+ goDoCommand('cmd_reload');
+ break;
+ case "Home":
+ default:
+ break;
+ }
+}
+
+function OnLoadMessageWindowDelayed(loadCustomMessage)
+{
+ gDBView.suppressMsgDisplay = false;
+ if (loadCustomMessage)
+ gDBView.loadMessageByUrl(gCurrentMessageUri);
+ else
+ {
+ var msgKey = extractMsgKeyFromURI(gCurrentMessageUri);
+ var viewIndex = gDBView.findIndexFromKey(msgKey, true);
+ // the message may not appear in the view if loaded from a search dialog
+ if (viewIndex != nsMsgViewIndex_None)
+ LoadMessageByViewIndex(viewIndex);
+ else
+ messenger.openURL(gCurrentMessageUri);
+ }
+ gNextMessageViewIndexAfterDelete = gDBView.msgToSelectAfterDelete;
+ UpdateStandAloneMessageCounts();
+
+ // set focus to the message pane
+ window.content.focus();
+
+ // since we just changed the pane with focus we need to update the toolbar to reflect this
+ // XXX TODO
+ // can we optimize
+ // and just update cmd_delete and button_delete?
+ UpdateMailToolbar("focus");
+}
+
+function CreateView(originalView)
+{
+ var msgFolder = GetLoadedMsgFolder();
+
+ // extract the sort type, the sort order,
+ var sortType;
+ var sortOrder;
+ var viewFlags;
+ var viewType;
+
+ if (originalView)
+ {
+ viewType = originalView.viewType;
+ viewFlags = originalView.viewFlags;
+ sortType = originalView.sortType;
+ sortOrder = originalView.sortOrder;
+ }
+ else if (msgFolder)
+ {
+ var msgDatabase = msgFolder.msgDatabase;
+ if (msgDatabase)
+ {
+ var dbFolderInfo = msgDatabase.dBFolderInfo;
+ sortType = dbFolderInfo.sortType;
+ sortOrder = dbFolderInfo.sortOrder;
+ viewFlags = dbFolderInfo.viewFlags;
+ viewType = dbFolderInfo.viewType;
+ msgDatabase = null;
+ dbFolderInfo = null;
+ }
+ }
+ else
+ {
+ viewType = nsMsgViewType.eShowSearch;
+ }
+
+ // create a db view
+ CreateBareDBView(originalView, msgFolder, viewType, viewFlags, sortType, sortOrder);
+
+ var uri;
+ if (gCurrentMessageUri)
+ uri = gCurrentMessageUri;
+ else if (gCurrentFolderUri)
+ uri = gCurrentFolderUri;
+ else
+ uri = null;
+
+ SetUpToolbarButtons(uri);
+
+ // hook for extra toolbar items
+ Services.obs.notifyObservers(window, "mail:setupToolbarItems", uri);
+}
+
+function extractMsgKeyFromURI()
+{
+ var msgKey = -1;
+ var msgHdr = messenger.msgHdrFromURI(gCurrentMessageUri);
+ if (msgHdr)
+ msgKey = msgHdr.messageKey;
+ return msgKey;
+}
+
+function OnUnloadMessageWindow()
+{
+ window.removeEventListener("AppCommand", HandleAppCommandEvent, true);
+
+ UnloadCommandUpdateHandlers();
+
+ // FIX ME - later we will be able to use onunload from the overlay
+ OnUnloadMsgHeaderPane();
+
+ OnMailWindowUnload();
+}
+
+function GetSelectedMsgFolders()
+{
+ var msgFolder = GetLoadedMsgFolder();
+ return msgFolder ? [msgFolder] : [];
+}
+
+function GetNumSelectedMessages()
+{
+ return (gCurrentMessageUri) ? 1 : 0;
+}
+
+function GetSelectedIndices(dbView)
+{
+ try {
+ return dbView.getIndicesForSelection();
+ }
+ catch (ex) {
+ dump("ex = " + ex + "\n");
+ return null;
+ }
+}
+
+function GetLoadedMsgFolder()
+{
+ return gCurrentFolderUri ? MailUtils.getFolderForURI(gCurrentFolderUri)
+ : null;
+}
+
+function GetLoadedMessage()
+{
+ return gCurrentMessageUri;
+}
+
+//Clear everything related to the current message. called after load start page.
+function ClearMessageSelection()
+{
+ gCurrentMessageUri = null;
+ gCurrentFolderUri = null;
+ UpdateMailToolbar("clear msg, std alone window");
+}
+
+function SetNextMessageAfterDelete()
+{
+ gNextMessageViewIndexAfterDelete = gDBView.msgToSelectAfterDelete;
+}
+
+function SelectMsgFolder(msgfolder) {
+ if (!msgfolder || msgfolder.isServer)
+ return;
+
+ let folderUri = msgfolder.URI;
+ if (folderUri == gCurrentFolderUri)
+ return;
+
+ // close old folder view
+ var dbview = GetDBView();
+ if (dbview)
+ dbview.close();
+
+ gCurrentFolderToRerootForStandAlone = folderUri;
+
+ if (msgfolder.manyHeadersToDownload)
+ {
+ gRerootOnFolderLoadForStandAlone = true;
+ try
+ {
+ msgfolder.startFolderLoading();
+ msgfolder.updateFolder(msgWindow);
+ }
+ catch(ex)
+ {
+ dump("Error loading with many headers to download: " + ex + "\n");
+ }
+ }
+ else
+ {
+ RerootFolderForStandAlone(folderUri);
+ gRerootOnFolderLoadForStandAlone = false;
+ msgfolder.startFolderLoading();
+
+ //Need to do this after rerooting folder. Otherwise possibility of receiving folder loaded
+ //notification before folder has actually changed.
+ msgfolder.updateFolder(msgWindow);
+ }
+}
+
+function RerootFolderForStandAlone(uri)
+{
+ gCurrentFolderUri = uri;
+
+ // create new folder view
+ CreateView(null);
+
+ // now do the work to load the appropriate message
+ if (gNextMessageAfterLoad) {
+ var type = gNextMessageAfterLoad;
+ gNextMessageAfterLoad = null;
+ LoadMessageByNavigationType(type);
+ }
+
+ SetUpToolbarButtons(gCurrentFolderUri);
+
+ UpdateMailToolbar("reroot folder in stand alone window");
+
+ // hook for extra toolbar items
+ Services.obs.notifyObservers(window, "mail:setupToolbarItems", uri);
+}
+
+function GetMsgHdrFromUri(messageUri)
+{
+ return messenger.msgHdrFromURI(messageUri);
+}
+
+function SelectMessage(messageUri)
+{
+ var msgHdr = GetMsgHdrFromUri(messageUri);
+ LoadMessageByMsgKey(msgHdr.messageKey);
+}
+
+function ReloadMessage()
+{
+ gDBView.reloadMessage();
+}
+
+// MessageWindowController object (handles commands when one of the trees does not have focus)
+var MessageWindowController =
+{
+ supportsCommand: function(command)
+ {
+ switch (command)
+ {
+ case "cmd_delete":
+ case "cmd_stop":
+ case "cmd_undo":
+ case "cmd_redo":
+ case "cmd_killThread":
+ case "cmd_killSubthread":
+ case "cmd_watchThread":
+ case "button_delete":
+ case "button_shiftDelete":
+ case "button_junk":
+ case "cmd_shiftDelete":
+ case "cmd_saveAsTemplate":
+ case "cmd_getMsgsForAuthAccounts":
+ case "button_mark":
+ case "cmd_markAsRead":
+ case "cmd_markAsUnread":
+ case "cmd_markAllRead":
+ case "cmd_markThreadAsRead":
+ case "cmd_markReadByDate":
+ case "cmd_markAsFlagged":
+ case "button_file":
+ case "cmd_markAsJunk":
+ case "cmd_markAsNotJunk":
+ case "cmd_recalculateJunkScore":
+ case "cmd_markAsShowRemote":
+ case "cmd_markAsNotPhish":
+ case "cmd_applyFiltersToSelection":
+ case "cmd_applyFilters":
+ case "cmd_runJunkControls":
+ case "cmd_deleteJunk":
+ case "cmd_nextMsg":
+ case "button_next":
+ case "cmd_nextUnreadMsg":
+ case "cmd_nextFlaggedMsg":
+ case "cmd_nextUnreadThread":
+ case "cmd_previousMsg":
+ case "cmd_previousUnreadMsg":
+ case "cmd_previousFlaggedMsg":
+ case "cmd_goBack":
+ case "button_goBack":
+ case "cmd_goForward":
+ case "button_goForward":
+ return (gDBView.keyForFirstSelectedMessage != nsMsgKey_None);
+ case "cmd_viewPageSource":
+ return GetNumSelectedMessages() > 0;
+ case "cmd_reply":
+ case "button_reply":
+ case "cmd_replyList":
+ case "cmd_replyGroup":
+ case "cmd_replySender":
+ case "cmd_replyall":
+ case "cmd_replySenderAndGroup":
+ case "cmd_replyAllRecipients":
+ case "button_replyall":
+ case "cmd_forward":
+ case "button_forward":
+ case "cmd_forwardInline":
+ case "cmd_forwardAttachment":
+ case "cmd_editAsNew":
+ case "cmd_editDraftMsg":
+ case "cmd_newMsgFromTemplate":
+ case "cmd_editTemplateMsg":
+ case "cmd_getNextNMessages":
+ case "cmd_find":
+ case "cmd_findNext":
+ case "cmd_findPrev":
+ case "button_search":
+ case "cmd_search":
+ case "cmd_reload":
+ case "cmd_saveAsFile":
+ case "cmd_getNewMessages":
+ case "button_getNewMessages":
+ case "button_print":
+ case "cmd_print":
+ case "cmd_printpreview":
+ case "cmd_printSetup":
+ case "cmd_settingsOffline":
+ case "cmd_createFilterFromPopup":
+ case "cmd_createFilterFromMenu":
+ case "cmd_viewAllHeader":
+ case "cmd_viewNormalHeader":
+ return true;
+ case "cmd_synchronizeOffline":
+ case "cmd_downloadFlagged":
+ case "cmd_downloadSelected":
+ return !Services.io.offline;
+ default:
+ return false;
+ }
+ },
+
+ isCommandEnabled: function(command)
+ {
+ var loadedFolder;
+ var enabled = new Object();
+ enabled.value = false;
+ var checkStatus = new Object();
+
+ switch (command)
+ {
+ case "cmd_createFilterFromPopup":
+ case "cmd_createFilterFromMenu":
+ loadedFolder = GetLoadedMsgFolder();
+ return (loadedFolder && loadedFolder.server.canHaveFilters);
+ case "cmd_delete":
+ UpdateDeleteCommand();
+ // fall through
+ case "button_delete":
+ if (command == "button_delete")
+ UpdateDeleteToolbarButton(false);
+ // fall through
+ case "cmd_shiftDelete":
+ case "button_shiftDelete":
+ loadedFolder = GetLoadedMsgFolder();
+ return gCurrentMessageUri && loadedFolder && loadedFolder.canDeleteMessages;
+ case "button_junk":
+ UpdateJunkToolbarButton();
+ // fall through
+ case "cmd_markAsJunk":
+ case "cmd_markAsNotJunk":
+ if (gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.junk, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_recalculateJunkScore":
+ if (GetNumSelectedMessages() > 0 && gDBView)
+ gDBView.getCommandStatus(nsMsgViewCommandType.runJunkControls, enabled, checkStatus);
+ return enabled.value;
+ case "cmd_reply":
+ case "button_reply":
+ case "cmd_replyList":
+ case "cmd_replyGroup":
+ case "cmd_replySender":
+ case "cmd_replyall":
+ case "button_replyall":
+ case "cmd_replySenderAndGroup":
+ case "cmd_replyAllRecipients":
+ case "cmd_forward":
+ case "button_forward":
+ case "cmd_forwardInline":
+ case "cmd_forwardAttachment":
+ case "cmd_editAsNew":
+ case "cmd_editDraftMsg":
+ case "cmd_newMsgFromTemplate":
+ case "cmd_editTemplateMsg":
+ case "cmd_print":
+ case "cmd_printpreview":
+ case "button_print":
+ case "cmd_saveAsFile":
+ return true;
+ case "cmd_saveAsTemplate":
+ var target = getMessageBrowser().contentPrincipal.URI.scheme;
+ return target != "news";
+ case "cmd_viewPageSource":
+ case "cmd_reload":
+ case "cmd_find":
+ case "button_mark":
+ case "cmd_markAllRead":
+ case "cmd_markThreadAsRead":
+ case "cmd_markReadByDate":
+ case "cmd_viewAllHeader":
+ case "cmd_viewNormalHeader":
+ return true;
+ case "cmd_markAsRead":
+ return CanMarkMsgAsRead(true);
+ case "cmd_markAsUnread":
+ return CanMarkMsgAsRead(false);
+ case "cmd_markAsFlagged":
+ case "button_file":
+ return (gCurrentMessageUri != null);
+ case "cmd_markAsShowRemote":
+ return (GetNumSelectedMessages() > 0 && checkMsgHdrPropertyIsNot("remoteContentPolicy", kAllowRemoteContent));
+ case "cmd_markAsNotPhish":
+ return (GetNumSelectedMessages() > 0 && checkMsgHdrPropertyIsNot("notAPhishMessage", kNotAPhishMessage));
+ case "cmd_printSetup":
+ return true;
+ case "cmd_getNewMessages":
+ case "button_getNewMessages":
+ case "cmd_getMsgsForAuthAccounts":
+ return IsGetNewMessagesEnabled();
+ case "cmd_getNextNMessages":
+ return IsGetNextNMessagesEnabled();
+ case "cmd_downloadFlagged":
+ case "cmd_downloadSelected":
+ case "cmd_synchronizeOffline":
+ return !Services.io.offline;
+ case "cmd_settingsOffline":
+ return IsAccountOfflineEnabled();
+ case "cmd_nextMsg":
+ case "button_next":
+ case "cmd_nextUnreadMsg":
+ case "cmd_nextFlaggedMsg":
+ case "cmd_nextUnreadThread":
+ case "cmd_previousMsg":
+ case "cmd_previousUnreadMsg":
+ case "cmd_previousFlaggedMsg":
+ case "cmd_applyFiltersToSelection":
+ return true;
+ case "cmd_findNext":
+ case "cmd_findPrev":
+ return MsgCanFindAgain();
+ case "cmd_goBack":
+ case "button_goBack":
+ return gDBView && gDBView.navigateStatus(nsMsgNavigationType.back);
+ case "cmd_goForward":
+ case "button_goForward":
+ return gDBView && gDBView.navigateStatus(nsMsgNavigationType.forward);
+ case "button_search":
+ case "cmd_search":
+ loadedFolder = GetLoadedMsgFolder();
+ return (loadedFolder && loadedFolder.server.canSearchMessages);
+ case "cmd_stop":
+ return true;
+ case "cmd_undo":
+ case "cmd_redo":
+ return SetupUndoRedoCommand(command);
+ case "cmd_applyFilters":
+ case "cmd_runJunkControls":
+ case "cmd_deleteJunk":
+ return false;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(command)
+ {
+ // if the user invoked a key short cut then it is possible that we got here for a command which is
+ // really disabled. kick out if the command should be disabled.
+ if (!this.isCommandEnabled(command)) return;
+
+ var navigationType = nsMsgNavigationType.nextUnreadMessage;
+
+ switch ( command )
+ {
+ case "cmd_getNewMessages":
+ MsgGetMessage();
+ break;
+ case "cmd_undo":
+ messenger.undo(msgWindow);
+ break;
+ case "cmd_redo":
+ messenger.redo(msgWindow);
+ break;
+ case "cmd_getMsgsForAuthAccounts":
+ MsgGetMessagesForAllAuthenticatedAccounts();
+ break;
+ case "cmd_getNextNMessages":
+ MsgGetNextNMessages();
+ break;
+ case "cmd_reply":
+ MsgReplyMessage(null);
+ break;
+ case "cmd_replyList":
+ MsgReplyList(null);
+ break;
+ case "cmd_replyGroup":
+ MsgReplyGroup(null);
+ break;
+ case "cmd_replySender":
+ MsgReplySender(null);
+ break;
+ case "cmd_replyall":
+ MsgReplyToAllMessage(null);
+ break;
+ case "cmd_replySenderAndGroup":
+ MsgReplyToSenderAndGroup(null);
+ break;
+ case "cmd_replyAllRecipients":
+ MsgReplyToAllRecipients(null);
+ break;
+ case "cmd_forward":
+ MsgForwardMessage(null);
+ break;
+ case "cmd_forwardInline":
+ MsgForwardAsInline(null);
+ break;
+ case "cmd_forwardAttachment":
+ MsgForwardAsAttachment(null);
+ break;
+ case "cmd_editAsNew":
+ MsgEditMessageAsNew(null);
+ break;
+ case "cmd_editDraftMsg":
+ MsgEditDraftMessage(null);
+ break;
+ case "cmd_newMsgFromTemplate":
+ MsgNewMessageFromTemplate(null);
+ break;
+ case "cmd_editTemplateMsg":
+ MsgEditTemplateMessage(null);
+ break;
+ case "cmd_createFilterFromPopup":
+ CreateFilter(document.popupNode);
+ break;
+ case "cmd_createFilterFromMenu":
+ MsgCreateFilter();
+ break;
+ case "cmd_delete":
+ case "button_delete":
+ MsgDeleteMessage(false);
+ UpdateDeleteToolbarButton(false);
+ break;
+ case "cmd_shiftDelete":
+ case "button_shiftDelete":
+ MsgDeleteMessage(true);
+ break;
+ case "button_junk":
+ MsgJunk();
+ break;
+ case "cmd_stop":
+ MsgStop();
+ break;
+ case "cmd_printSetup":
+ PrintUtils.showPageSetup();
+ break;
+ case "cmd_print":
+ PrintEnginePrint();
+ break;
+ case "cmd_printpreview":
+ PrintEnginePrintPreview();
+ break;
+ case "cmd_saveAsFile":
+ MsgSaveAsFile();
+ break;
+ case "cmd_saveAsTemplate":
+ MsgSaveAsTemplate();
+ break;
+ case "cmd_viewPageSource":
+ MsgViewPageSource();
+ break;
+ case "cmd_reload":
+ ReloadMessage();
+ break;
+ case "cmd_find":
+ MsgFind();
+ break;
+ case "cmd_findNext":
+ MsgFindAgain(false);
+ break;
+ case "cmd_findPrev":
+ MsgFindAgain(true);
+ break;
+ case "button_search":
+ case "cmd_search":
+ MsgSearchMessages();
+ break;
+ case "button_mark":
+ MsgMarkMsgAsRead();
+ return;
+ case "cmd_markAsRead":
+ MsgMarkMsgAsRead(true);
+ return;
+ case "cmd_markAsUnread":
+ MsgMarkMsgAsRead(false);
+ return;
+ case "cmd_markThreadAsRead":
+ MsgMarkThreadAsRead();
+ return;
+ case "cmd_markAllRead":
+ MsgMarkAllRead();
+ return;
+ case "cmd_markReadByDate":
+ MsgMarkReadByDate();
+ return;
+ case "cmd_viewAllHeader":
+ MsgViewAllHeaders();
+ return;
+ case "cmd_viewNormalHeader":
+ MsgViewNormalHeaders();
+ return;
+ case "cmd_markAsFlagged":
+ MsgMarkAsFlagged();
+ return;
+ case "cmd_markAsJunk":
+ JunkSelectedMessages(true);
+ return;
+ case "cmd_markAsNotJunk":
+ JunkSelectedMessages(false);
+ return;
+ case "cmd_recalculateJunkScore":
+ analyzeMessagesForJunk();
+ return;
+ case "cmd_markAsShowRemote":
+ LoadMsgWithRemoteContent();
+ return;
+ case "cmd_markAsNotPhish":
+ MsgIsNotAScam();
+ return;
+ case "cmd_downloadFlagged":
+ MsgDownloadFlagged();
+ return;
+ case "cmd_downloadSelected":
+ MsgDownloadSelected();
+ return;
+ case "cmd_synchronizeOffline":
+ MsgSynchronizeOffline();
+ return;
+ case "cmd_settingsOffline":
+ MsgSettingsOffline();
+ return;
+ case "cmd_nextUnreadMsg":
+ case "button_next":
+ performNavigation(nsMsgNavigationType.nextUnreadMessage);
+ break;
+ case "cmd_nextUnreadThread":
+ performNavigation(nsMsgNavigationType.nextUnreadThread);
+ break;
+ case "cmd_nextMsg":
+ performNavigation(nsMsgNavigationType.nextMessage);
+ break;
+ case "cmd_nextFlaggedMsg":
+ performNavigation(nsMsgNavigationType.nextFlagged);
+ break;
+ case "cmd_previousMsg":
+ performNavigation(nsMsgNavigationType.previousMessage);
+ break;
+ case "cmd_previousUnreadMsg":
+ performNavigation(nsMsgNavigationType.previousUnreadMessage);
+ break;
+ case "cmd_previousFlaggedMsg":
+ performNavigation(nsMsgNavigationType.previousFlagged);
+ break;
+ case "cmd_goBack":
+ performNavigation(nsMsgNavigationType.back);
+ break;
+ case "cmd_goForward":
+ performNavigation(nsMsgNavigationType.forward);
+ break;
+ case "cmd_applyFiltersToSelection":
+ MsgApplyFiltersToSelection();
+ break;
+ }
+ },
+
+ onEvent: function(event)
+ {
+ }
+};
+
+function LoadMessageByNavigationType(type)
+{
+ var resultId = new Object;
+ var resultIndex = new Object;
+ var threadIndex = new Object;
+
+ gDBView.viewNavigate(type, resultId, resultIndex, threadIndex, true /* wrap */);
+
+ // if we found something....display it.
+ if ((resultId.value != nsMsgKey_None) && (resultIndex.value != nsMsgKey_None))
+ {
+ // load the message key
+ LoadMessageByMsgKey(resultId.value);
+ // if we changed folders, the message counts changed.
+ UpdateStandAloneMessageCounts();
+
+ // new message has been loaded
+ return true;
+ }
+
+ // no message found to load
+ return false;
+}
+
+function performNavigation(type)
+{
+ // Try to load a message by navigation type if we can find
+ // the message in the same folder.
+ if (LoadMessageByNavigationType(type))
+ return;
+
+ CrossFolderNavigation(type);
+}
+
+function SetupCommandUpdateHandlers()
+{
+ top.controllers.insertControllerAt(0, MessageWindowController);
+}
+
+function UnloadCommandUpdateHandlers()
+{
+ top.controllers.removeController(MessageWindowController);
+}
+
+function GetDBView()
+{
+ return gDBView;
+}
+
+function LoadMessageByMsgKey(messageKey)
+{
+ var viewIndex = gDBView.findIndexFromKey(messageKey, true);
+ gDBView.loadMessageByViewIndex(viewIndex);
+ // we only want to update the toolbar if there was no previous selected message.
+ if (nsMsgKey_None == gDBView.keyForFirstSelectedMessage)
+ UpdateMailToolbar("update toolbar for message Window");
+}
+
+function LoadMessageByViewIndex(viewIndex)
+{
+ gDBView.loadMessageByViewIndex(viewIndex);
+ // we only want to update the toolbar if there was no previous selected message.
+ if (nsMsgKey_None == gDBView.keyForFirstSelectedMessage)
+ UpdateMailToolbar("update toolbar for message Window");
+}
diff --git a/comm/suite/mailnews/content/messageWindow.xul b/comm/suite/mailnews/content/messageWindow.xul
new file mode 100644
index 0000000000..9ec502086c
--- /dev/null
+++ b/comm/suite/mailnews/content/messageWindow.xul
@@ -0,0 +1,141 @@
+<?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://messenger/skin/messageWindow.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/mailWindowOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
+%messengerDTD;
+]>
+
+<window id="messengerWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ title="&messengerWindow.title;"
+ titlemodifier="&titleModifier.label;"
+ titlemenuseparator="&titleSeparator.label;"
+ onload="OnLoadMessageWindow()"
+ onunload="OnUnloadMessageWindow()"
+ width="750"
+ height="500"
+ persist="width height screenX screenY sizemode"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ macanimationtype="document"
+ drawtitle="true"
+ windowtype="mail:messageWindow">
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_offlinePrompts" src="chrome://messenger/locale/offline.properties"/>
+ </stringbundleset>
+
+ <script src="chrome://messenger/content/commandglue.js"/>
+ <script src="chrome://messenger/content/mailWindow.js"/>
+ <script src="chrome://messenger/content/messageWindow.js"/>
+ <script src="chrome://messenger/content/accountUtils.js"/>
+ <script src="chrome://messenger/content/mailContextMenus.js"/>
+ <script src="chrome://messenger/content/phishingDetector.js"/>
+ <script src="chrome://communicator/content/contentAreaClick.js"/>
+ <script src="chrome://global/content/nsDragAndDrop.js"/>
+ <script src="chrome://messenger/content/msgViewNavigation.js"/>
+ <script src="chrome://messenger/content/tabmail.js"/>
+
+ <commandset id="mailCommands">
+ <commandset id="mailFileMenuItems"/>
+ <commandset id="mailDownloadCommands"/>
+ <commandset id="mailViewMenuItems"/>
+ <commandset id="mailEditMenuItems"/>
+ <commandset id="mailSearchMenuItems"/>
+ <commandset id="mailGoMenuItems"/>
+ <commandset id="mailMessageMenuItems"/>
+ <commandset id="mailToolbarItems"/>
+ <commandset id="mailGetMsgMenuItems"/>
+ <commandset id="mailMarkMenuItems"/>
+ <commandset id="mailToolsMenuItems"/>
+ <commandset id="mailEditContextMenuItems"/>
+ <commandset id="tasksCommands"/>
+ <commandset id="commandKeys"/>
+ <command id="cmd_close" oncommand="window.close();"/>
+ </commandset>
+
+ <broadcasterset id="mailBroadcasters">
+ <broadcaster id="mailHideMenus" hidden="true"/>
+ <broadcaster id="mailDisableKeys" disabled="true"/>
+ <!-- File Menu -->
+ <broadcaster id="Communicator:WorkMode"/>
+ </broadcasterset>
+
+ <broadcasterset id="mainBroadcasterSet"/>
+
+ <keyset id="mailKeys">
+ <keyset id="tasksKeys"/>
+ <key keycode="VK_ESCAPE" oncommand="window.close();"/>
+ </keyset>
+
+ <popupset id="messagePopupSet">
+ <menupopup id="mailContext"/>
+ <menupopup id="attachmentListContext"/>
+ <menupopup id="copyUrlPopup"/>
+ <menupopup id="messageIdContext"/>
+ <menupopup id="emailAddressPopup"/>
+ <menupopup id="toolbar-context-menu"/>
+ <menupopup id="remoteContentOptions"/>
+ <tooltip id="aHTMLTooltip"
+ onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
+ <panel id="customizeToolbarSheetPopup"/>
+ </popupset>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="mail-toolbox">
+ <toolbar id="mail-toolbar-menubar2">
+ <toolbaritem id="menubar-items">
+ <menubar id="mail-menubar"/>
+ </toolbaritem>
+ </toolbar>
+ <toolbar id="msgToolbar"/>
+ <toolbarset id="customToolbars"/>
+ </toolbox>
+
+ <!-- msg header view -->
+<vbox id="messagesBox" flex="1">
+ <notificationbox id="messagepanebox"
+ class="browser-notificationbox"
+ flex="3"
+ persist="collapsed"
+ ondragover="messagepaneObserver.onDragOver(event);"
+ ondrop="messagepaneObserver.onDrop(event);"
+ ondragexit="messagepaneObserver.onDragExit(event);">
+
+ <hbox id="msgHeaderView"/>
+
+ <!-- message view -->
+ <browser id="messagepane"
+ name="messagepane"
+ height="0"
+ flex="1"
+ minwidth="1"
+ minheight="1"
+ context="mailContext"
+ tooltip="aHTMLTooltip"
+ disablesecurity="true"
+ disablehistory="true"
+ autofind="false"
+ type="content"
+ primary="true"
+ onresize="return messagePaneOnResize(event);"
+ onclick="return messagePaneOnClick(event);"/>
+ </notificationbox>
+</vbox>
+
+ <statusbar class="chromeclass-status" id="status-bar"/>
+
+</window>
diff --git a/comm/suite/mailnews/content/messenger.css b/comm/suite/mailnews/content/messenger.css
new file mode 100644
index 0000000000..785c1178e8
--- /dev/null
+++ b/comm/suite/mailnews/content/messenger.css
@@ -0,0 +1,236 @@
+/* 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/. */
+
+/* ===== messenger.css ==================================================
+ == Content specific styles for Messenger.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: mail xbl bindings ::::: */
+
+description[selectable="true"] {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#extdescription");
+}
+
+descriptionitem {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#descriptionitem");
+}
+
+.descriptionitem-iconic {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#descriptionitem-iconic");
+}
+
+mail-messageid {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-messageid");
+}
+
+mail-messageids-headerfield {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-messageids-headerfield");
+}
+
+mail-emailaddress {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-emailaddress");
+ -moz-user-focus: normal;
+}
+
+mail-emailheaderfield {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-emailheaderfield");
+}
+
+mail-toggle-headerfield {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-toggle-headerfield");
+}
+
+mail-multi-emailHeaderField {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-multi-emailHeaderField");
+}
+
+mail-headerfield {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-headerfield");
+}
+
+mail-urlfield {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-urlfield");
+}
+
+mail-tagfield {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#mail-headerfield-tags");
+}
+
+menupopup[type="folder"] {
+ -moz-binding: url("chrome://messenger/content/folderWidgets.xml#folder-menupopup");
+}
+
+.addrbooksPopup {
+ -moz-binding: url("chrome://messenger/content/addressbook/addrbookWidgets.xml#addrbooks-menupopup");
+}
+
+.map-list {
+ -moz-binding: url("chrome://messenger/content/addressbook/addrbookWidgets.xml#map-list");
+}
+
+#searchTermList > listitem {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#listitem");
+}
+
+searchattribute {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#searchattribute");
+}
+
+searchoperator {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#searchoperator");
+}
+
+searchvalue {
+ display: -moz-deck;
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#searchvalue");
+}
+
+searchterm {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#searchterm");
+}
+
+.ruleaction {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleaction");
+}
+
+.ruleactiontype {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontype-menulist");
+}
+
+.ruleactiontarget[type] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base");
+}
+
+.ruleactiontarget[type="movemessage"], .ruleactiontarget[type="copymessage"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
+}
+
+.ruleactiontarget[type="addtagtomessage"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-tag");
+}
+
+.ruleactiontarget[type="setpriorityto"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-priority");
+}
+
+.ruleactiontarget[type="setjunkscore"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-junkscore");
+}
+
+.ruleactiontarget[type="forwardmessage"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-forwardto");
+}
+
+.ruleactiontarget[type="replytomessage"] {
+ -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-replyto");
+}
+
+.folderSummaryPopup
+{
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary-popup");
+}
+
+folderSummary
+{
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary");
+}
+
+folderSummaryMessage
+{
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary-message");
+}
+
+folderSummaryLocation
+{
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary-location");
+}
+
+folderSummarySubfoldersSummary
+{
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary-subfoldersSummary");
+}
+
+dummy.usesMailWidgets {
+ -moz-binding: url("chrome://messenger/content/mailWidgets.xml#dummy");
+}
+
+/* tabmail */
+
+#tabmail
+{
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail");
+}
+
+.tabmail-tabs {
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail-tabs");
+}
+
+.tabmail-arrowscrollbox {
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail-arrowscrollbox");
+}
+
+.tabmail-tab {
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail-tab");
+}
+
+.tabs-newbutton {
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail-new-tab-button");
+}
+
+.tab-close-button,
+.tabs-closebutton {
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail-close-tab-button");
+}
+
+.tab-close-button {
+ display: none;
+}
+
+.tabmail-tabs:not([closebuttons="noclose"]):not([closebuttons="closeatend"]) > .tabmail-tab[selected="true"] > .tab-close-button {
+ display: -moz-box;
+}
+
+.tabmail-tabs[closebuttons="alltabs"] .tab-close-button {
+ display: -moz-box;
+}
+
+.tabs-alltabs-popup {
+ /* override toolkit's .menulist-menupopup binding */
+ -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail-alltabs-popup") ! important;
+}
+
+/* Used for selecting appropriate button for when next to search box */
+
+#button-search {
+ display: -moz-box;
+}
+
+#search-container + #button-search-container > #button-search,
+#wrapper-search-container + toolbarpaletteitem[place="toolbar"] > #button-search-container > #button-search {
+ display: none;
+}
+
+#button-advanced {
+ display: none;
+}
+
+#search-container + #button-search-container > #button-advanced,
+#wrapper-search-container + toolbarpaletteitem[place="toolbar"] > #button-search-container > #button-advanced {
+ display: -moz-box;
+}
+
+/* Wallpaper patch for Bug 517924 */
+
+#expandedHeaderView {
+ overflow-y: auto;
+ overflow-x: hidden;
+ max-height: 14em;
+}
+
+/* Lightning toobar menu button */
+.button-appmenu {
+display: none;
+}
diff --git a/comm/suite/mailnews/content/messenger.xul b/comm/suite/mailnews/content/messenger.xul
new file mode 100644
index 0000000000..42c73a96ec
--- /dev/null
+++ b/comm/suite/mailnews/content/messenger.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://messenger/skin/mailWindow1.css" type="text/css"?>
+
+<?xul-overlay href="chrome://messenger/content/threadPane.xul"?>
+<?xul-overlay href="chrome://messenger/content/folderPane.xul"?>
+<?xul-overlay href="chrome://messenger/content/mailWindowOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
+%messengerDTD;
+]>
+
+<window id="messengerWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ title="&messengerWindow.title;"
+ titlemodifier="&titleModifier.label;"
+ titlemenuseparator="&titleSeparator.label;"
+ onload="OnLoadMessenger()"
+ onunload="OnUnloadMessenger()"
+ onclose="return MailWindowIsClosing();"
+ screenX="10" screenY="10"
+ persist="width height screenX screenY sizemode"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ macanimationtype="document"
+ drawtitle="true"
+ windowtype="mail:3pane">
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+ <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+ <stringbundle id="bundle_search" src="chrome://messenger/locale/search.properties"/>
+</stringbundleset>
+
+<script src="chrome://messenger/content/commandglue.js"/>
+<script src="chrome://messenger/content/msgViewNavigation.js"/>
+<script src="chrome://messenger/content/mailWindow.js"/>
+<script src="chrome://messenger/content/msgMail3PaneWindow.js"/>
+<script src="chrome://messenger/content/mail3PaneWindowCommands.js"/>
+<script src="chrome://messenger/content/mailContextMenus.js"/>
+<script src="chrome://messenger/content/accountUtils.js"/>
+<script src="chrome://messenger/content/folderPane.js"/>
+<script src="chrome://messenger/content/phishingDetector.js"/>
+<script src="chrome://communicator/content/contentAreaClick.js"/>
+<script src="chrome://global/content/nsDragAndDrop.js"/>
+<script src="chrome://messenger/content/searchBar.js"/>
+<script src="chrome://messenger/content/tabmail.js"/>
+
+<commandset id="mailCommands">
+ <commandset id="mailFileMenuItems"/>
+ <commandset id="mailDownloadCommands"/>
+ <commandset id="mailViewMenuItems"/>
+ <commandset id="mailEditMenuItems"/>
+ <commandset id="mailEditContextMenuItems"/>
+ <commandset id="mailSearchMenuItems"/>
+ <commandset id="mailGoMenuItems"/>
+ <commandset id="mailMessageMenuItems"/>
+ <commandset id="mailToolbarItems"/>
+ <commandset id="mailGetMsgMenuItems"/>
+ <commandset id="mailMarkMenuItems"/>
+ <commandset id="mailToolsMenuItems"/>
+ <commandset id="globalEditMenuItems"/>
+ <commandset id="selectEditMenuItems"/>
+ <commandset id="clipboardEditMenuItems"/>
+ <commandset id="FocusRingUpdate_Mail"
+ commandupdater="true"
+ events="focus"
+ oncommandupdate="FocusRingUpdate_Mail()"/>
+ <commandset id="tasksCommands"/>
+ <command id="cmd_close" oncommand="MsgCloseTabOrWindow();"/>
+</commandset>
+
+<broadcasterset id="mailBroadcasters">
+ <broadcaster id="mailHideMenus"/>
+ <broadcaster id="mailDisableKeys"/>
+ <broadcaster id="mailDisableViewsSearch" disabled="true"/>
+ <!-- File Menu -->
+ <broadcaster id="Communicator:WorkMode"/>
+</broadcasterset>
+
+<broadcasterset id="mainBroadcasterSet"/>
+
+<keyset id="mailKeys">
+ <!-- Tab/F6 Keys -->
+ <key keycode="VK_TAB" oncommand="SwitchPaneFocus(event);" modifiers="control,shift"/>
+ <key keycode="VK_TAB" oncommand="SwitchPaneFocus(event);" modifiers="control"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="control,shift"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="control"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="shift"/>
+ <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);"/>
+
+ <keyset id="tasksKeys"/>
+</keyset>
+
+ <popupset id="mainPopupSet">
+ <menupopup id="mailContext"/>
+ <menupopup id="folderPaneContext"/>
+ <menupopup id="attachmentListContext"/>
+ <tooltip id="attachmentListTooltip"/>
+ <menupopup id="copyUrlPopup"/>
+ <menupopup id="messageIdContext"/>
+ <menupopup id="emailAddressPopup"/>
+ <menupopup id="toolbar-context-menu"/>
+ <tooltip id="folderpopup" class="folderSummaryPopup"/>
+ <tooltip id="aHTMLTooltip"
+ onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
+ <panel id="customizeToolbarSheetPopup"/>
+ <menupopup id="networkProperties"/>
+ <menupopup id="remoteContentOptions"/>
+ </popupset>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="mail-toolbox" class="toolbox-top">
+ <toolbar id="mail-toolbar-menubar2"
+ type="menubar">
+ <toolbaritem id="menubar-items">
+ <menubar id="mail-menubar"/>
+ </toolbaritem>
+ </toolbar>
+ <toolbar id="msgToolbar"/>
+ <toolbarset id="customToolbars"/>
+ <toolbar id="searchToolbar"
+ class="chromeclass-toolbar"
+ persist="collapsed"
+ grippytooltiptext="&searchToolbar.tooltip;"
+ toolbarname="&showSearchToolbarCmd.label;"
+ accesskey="&showSearchToolbarCmd.accesskey;"
+ customizable="true"
+ nowindowdrag="true"
+ mode="full"
+ iconsize="small"
+ labelalign="end"
+ defaultmode="full"
+ defaulticonsize="small"
+ defaultlabelalign="end"
+ defaultset="mailviews-container,spring,search-container,button-search-container"
+ context="toolbar-context-menu"/>
+ </toolbox>
+
+ <!-- XXX This extension point (tabmail-container) is only temporary!
+ (See bug 460252 for details.)
+ We will readd a mechanism for sidebar panes in bug 178003.
+ -->
+ <hbox id="tabmail-container" flex="1">
+ <tabmail id="tabmail" flex="1" panelcontainer="tabpanelcontainer">
+ <box id="tabmail-buttons" orientation="horizontal"/>
+ <toolbar id="tabbar-toolbar"
+ xpfe="false"
+ toolboxid="mail-toolbox"
+ toolbarname="&showTabsToolbarCmd.label;"
+ accesskey="&showTabsToolbarCmd.accesskey;"
+ customizable="true"
+ nowindowdrag="true"
+ mode="icons"
+ iconsize="small"
+ labelalign="end"
+ defaultmode="icons"
+ defaulticonsize="small"
+ defaultlabelalign="end"
+ context="toolbar-context-menu"/>
+ <tabpanels id="tabpanelcontainer" flex="1" class="plain" selectedIndex="0">
+ <!-- The main mail three pane frame -->
+ <box id="mailContent" orient="vertical" flex="1">
+ <box id="messengerBox"
+ orient="horizontal"
+ flex="1"
+ minheight="100"
+ height="100"
+ persist="height">
+ <vbox id="folderPaneBox"
+ minwidth="100"
+ width="200"
+ persist="collapsed width hidden">
+ <tree id="folderTree">
+ <treechildren tooltip="folderpopup"/>
+ </tree>
+ </vbox>
+
+ <splitter id="folderpane-splitter"
+ collapse="before"
+ resizeafter="grow"
+ persist="state collapsed"
+ oncommand="MsgToggleFolderPane(false);">
+ <grippy/>
+ </splitter>
+
+ <box id="messagesBox"
+ orient="vertical"
+ flex="1"
+ minwidth="100"
+ width="100"
+ persist="width">
+ <deck id="displayDeck"
+ flex="1"
+ selectedIndex="0"
+ minheight="100"
+ height="100"
+ persist="height"
+ onselect="ObserveDisplayDeckChange(event);">
+ <!-- first panel in displayDeck is Account Central -->
+ <vbox id="accountCentralBox">
+ <iframe name="accountCentralPane"
+ width="150"
+ flex="1"
+ src="about:blank"/>
+ </vbox>
+ <!-- second panel is the threadPane -->
+ <vbox id="threadPaneBox">
+ <tree id="threadTree"
+ treelines="true"
+ keepcurrentinview="true"
+ flex="1"
+ context="mailContext"
+ class="window-focusborder"
+ focusring="false"/>
+ </vbox>
+ <!-- extensions may overlay in additional panels; don't assume that there are only 2! -->
+ </deck>
+
+ <!-- if you change this id, please change GetThreadAndMessagePaneSplitter() and MsgToggleMessagePane() -->
+ <splitter id="threadpane-splitter"
+ collapse="after"
+ persist="state collapsed hidden"
+ collapsed="true"
+ oncommand="MsgToggleMessagePane(false);">
+ <grippy/>
+ </splitter>
+
+ <notificationbox id="messagepanebox"
+ flex="2"
+ minheight="100"
+ height="200"
+ minwidth="100"
+ width="200"
+ persist="height width"
+ class="browser-notificationbox window-focusborder"
+ focusring="false">
+ <hbox id="msgHeaderView"/>
+ <!-- The messagepanewrapper hbox exists to allow extensions
+ to add sidebars to the message pane. -->
+ <hbox id="messagepanewrapper" flex="1">
+ <browser id="messagepane"
+ name="messagepane"
+ height="0"
+ flex="1"
+ minwidth="1"
+ minheight="1"
+ tooltip="aHTMLTooltip"
+ context="mailContext"
+ disablesecurity="true"
+ disablehistory="true"
+ autofind="false"
+ type="content"
+ primary="true"
+ onresize="return messagePaneOnResize(event);"
+ onclick="return messagePaneOnClick(event);"/>
+ </hbox>
+ </notificationbox>
+ </box>
+ </box>
+ </box>
+ </tabpanels>
+ </tabmail>
+ </hbox>
+
+ <statusbar id="status-bar" class="chromeclass-status mailwindow-statusbar"/>
+</window>
diff --git a/comm/suite/mailnews/content/msgFolderPickerOverlay.js b/comm/suite/mailnews/content/msgFolderPickerOverlay.js
new file mode 100644
index 0000000000..b097cd553e
--- /dev/null
+++ b/comm/suite/mailnews/content/msgFolderPickerOverlay.js
@@ -0,0 +1,100 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.jsm");
+
+var gMessengerBundle;
+
+// call this from dialog onload() to set the menu item to the correct value
+function MsgFolderPickerOnLoad(pickerID) {
+ var uri = null;
+ try {
+ uri = window.arguments[0].preselectedURI;
+ } catch (ex) {
+ uri = null;
+ }
+
+ if (uri) {
+ // dump("on loading, set titled button to " + uri + "\n");
+
+ // verify that the value we are attempting to
+ // pre-flight the menu with is valid for this
+ // picker type
+ var msgfolder = MailUtils.getExistingFolder(uri);
+ if (!msgfolder) {
+ return;
+ }
+
+ var verifyFunction = null;
+
+ switch (pickerID) {
+ case "msgNewFolderPicker":
+ verifyFunction = msgfolder.canCreateSubfolders;
+ break;
+ case "msgRenameFolderPicker":
+ verifyFunction = msgfolder.canRename;
+ break;
+ default:
+ verifyFunction = msgfolder.canFileMessages;
+ break;
+ }
+
+ if (verifyFunction) {
+ SetFolderPicker(uri, pickerID);
+ }
+ }
+}
+
+function PickedMsgFolder(selection, pickerID) {
+ var selectedUri = selection.getAttribute("id");
+ SetFolderPicker(selectedUri, pickerID);
+}
+
+function SetFolderPickerElement(uri, picker) {
+ var msgfolder = MailUtils.getExistingFolder(uri);
+
+ if (!msgfolder) {
+ return;
+ }
+
+ var selectedValue = null;
+ var serverName;
+
+ if (msgfolder.isServer) {
+ selectedValue = msgfolder.name;
+ } else {
+ if (msgfolder.server) {
+ serverName = msgfolder.server.prettyName;
+ } else {
+ dump("Can't find server for " + uri + "\n");
+ serverName = "???";
+ }
+
+ switch (picker.id) {
+ case "runFiltersFolder":
+ selectedValue = msgfolder.name;
+ break;
+ case "msgTrashFolderPicker":
+ selectedValue = msgfolder.name;
+ break;
+ default:
+ if (!gMessengerBundle) {
+ gMessengerBundle = document.getElementById("bundle_messenger");
+ }
+ selectedValue = gMessengerBundle.getFormattedString(
+ "verboseFolderFormat",
+ [msgfolder.name, serverName]
+ );
+ break;
+ }
+ }
+
+ picker.setAttribute("label", selectedValue);
+ picker.setAttribute("uri", uri);
+}
+
+function SetFolderPicker(uri, pickerID) {
+ SetFolderPickerElement(uri, document.getElementById(pickerID));
+}
diff --git a/comm/suite/mailnews/content/msgHdrViewOverlay.js b/comm/suite/mailnews/content/msgHdrViewOverlay.js
new file mode 100644
index 0000000000..c1f12388e7
--- /dev/null
+++ b/comm/suite/mailnews/content/msgHdrViewOverlay.js
@@ -0,0 +1,1971 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {GlodaUtils} = ChromeUtils.import("resource:///modules/gloda/utils.js");
+
+
+/* This is where functions related to displaying the headers for a selected message in the
+ message pane live. */
+
+////////////////////////////////////////////////////////////////////////////////////
+// Warning: if you go to modify any of these JS routines please get a code review from
+// scott@scott-macgregor.org. It's critical that the code in here for displaying
+// the message headers for a selected message remain as fast as possible. In particular,
+// right now, we only introduce one reflow per message. i.e. if you click on a message in the thread
+// pane, we batch up all the changes for displaying the header pane (to, cc, attachements button, etc.)
+// and we make a single pass to display them. It's critical that we maintain this one reflow per message
+// view in the message header pane.
+////////////////////////////////////////////////////////////////////////////////////
+
+var gViewAllHeaders = false;
+var gCollectIncoming = false;
+var gCollectOutgoing = false;
+var gCollectNewsgroup = false;
+var gCollapsedHeaderViewMode = false;
+var gCollectAddressTimer = null;
+var gBuildAttachmentsForCurrentMsg = false;
+var gBuildAttachmentPopupForCurrentMsg = true;
+var gBuiltExpandedView = false;
+var gBuiltCollapsedView = false;
+var gMessengerBundle;
+
+// Show the friendly display names for people I know, instead of the name + email address.
+var gShowCondensedEmailAddresses;
+
+var abAddressCollector = null;
+
+// other components may listen to on start header & on end header notifications for each message we display
+// to do that you need to add yourself to our gMessageListeners array with an object that supports the three properties:
+// onStartHeaders, onEndHeaders and onEndAttachments.
+var gMessageListeners = new Array();
+
+// For every possible "view" in the message pane, you need to define the header names you want to
+// see in that view. In addition, include information describing how you want that header field to be
+// presented. i.e. if it's an email address field, if you want a toggle inserted on the node in case
+// of multiple email addresses, etc. We'll then use this static table to dynamically generate header view entries
+// which manipulate the UI.
+// When you add a header to one of these view lists you can specify the following properties:
+// name: the name of the header. i.e. "to", "subject". This must be in lower case and the name of the
+// header is used to help dynamically generate ids for objects in the document. (REQUIRED)
+// useToggle: true if the values for this header are multiple email addresses and you want a
+// a toggle icon to show a short vs. long list (DEFAULT: false)
+// useShortView: (only works on some fields like From). If the field has a long presentation and a
+// short presentation we'll use the short one. i.e. if you are showing the From field and you
+// set this to true, we can show just "John Doe" instead of "John Doe <jdoe@netscape.net>".
+// (DEFAULT: false)
+//
+// outputFunction: this is a method which takes a headerEntry (see the definition below) and a header value
+// This allows you to provide your own methods for actually determining how the header value
+// is displayed. (DEFAULT: updateHeaderValue which just sets the header value on the text node)
+
+// Our first view is the collapsed view. This is very light weight view of the data. We only show a couple
+// fields.
+var gCollapsedHeaderList = [ {name:"subject", outputFunction:updateHeaderValueInTextNode},
+ {name:"from", useToggle:true, useShortView:true, outputFunction:OutputEmailAddresses},
+ {name:"date", outputFunction:updateHeaderValueInTextNode}];
+
+// We also have an expanded header view. This shows many of your more common (and useful) headers.
+var gExpandedHeaderList = [ {name:"subject"},
+ {name:"from", useToggle:true, outputFunction:OutputEmailAddresses},
+ {name:"sender", outputFunction:OutputEmailAddresses},
+ {name:"reply-to", useToggle:true, outputFunction:OutputEmailAddresses},
+ {name:"date"},
+ {name:"to", useToggle:true, outputFunction:OutputEmailAddresses},
+ {name:"cc", useToggle:true, outputFunction:OutputEmailAddresses},
+ {name:"bcc", useToggle:true, outputFunction:OutputEmailAddresses},
+ {name:"newsgroups", outputFunction:OutputNewsgroups},
+ {name:"references", outputFunction:OutputMessageIds},
+ {name:"followup-to", outputFunction:OutputNewsgroups},
+ {name:"content-base"},
+ {name:"tags"} ];
+
+// These are all the items that use a mail-multi-emailHeaderField widget and
+// therefore may require updating if the address book changes.
+const gEmailAddressHeaderNames = ["from", "reply-to", "to", "cc", "bcc"];
+
+// Now, for each view the message pane can generate, we need a global table of headerEntries. These
+// header entry objects are generated dynamically based on the static data in the header lists (see above)
+// and elements we find in the DOM based on properties in the header lists.
+var gCollapsedHeaderView = {};
+var gExpandedHeaderView = {};
+
+// currentHeaderData --> this is an array of header name and value pairs for the currently displayed message.
+// it's purely a data object and has no view information. View information is contained in the view objects.
+// for a given entry in this array you can ask for:
+// .headerName ---> name of the header (i.e. 'to'). Always stored in lower case
+// .headerValue --> value of the header "johndoe@netscape.net"
+var currentHeaderData = {};
+
+// For the currently displayed message, we store all the attachment data. When displaying a particular
+// view, it's up to the view layer to extract this attachment data and turn it into something useful.
+// For a given entry in the attachments list, you can ask for the following properties:
+// .contentType --> the content type of the attachment
+// url --> an imap, or mailbox url which can be used to fetch the message
+// uri --> an RDF URI which refers to the message containig the attachment
+// isExternalAttachment --> boolean flag stating whether the attachment is external or not.
+var currentAttachments = new Array();
+
+const nsIAbDirectory = Ci.nsIAbDirectory;
+const nsIAbListener = Ci.nsIAbListener;
+const nsIAbCard = Ci.nsIAbCard;
+
+// createHeaderEntry --> our constructor method which creates a header Entry
+// based on an entry in one of the header lists. A header entry is different from a header list.
+// a header list just describes how you want a particular header to be presented. The header entry
+// actually has knowledge about the DOM and the actual DOM elements associated with the header.
+// prefix --> the name of the view (i.e. "collapsed", "expanded")
+// headerListInfo --> entry from a header list.
+function createHeaderEntry(prefix, headerListInfo)
+{
+ var useShortView = false;
+ var partialIDName = prefix + headerListInfo.name;
+ this.enclosingBox = document.getElementById(partialIDName + 'Box');
+ this.textNode = document.getElementById(partialIDName + 'Value');
+ this.isNewHeader = false;
+ this.isValid = false;
+
+ if ("useShortView" in headerListInfo)
+ {
+ useShortView = headerListInfo.useShortView;
+ if (useShortView)
+ this.enclosingBox = this.textNode;
+ else
+ this.enclosingBox.emailAddressNode = this.textNode;
+ }
+
+ if ("useToggle" in headerListInfo)
+ {
+ this.useToggle = headerListInfo.useToggle;
+ if (this.useToggle) // find the toggle icon in the document
+ {
+ this.toggleIcon = this.enclosingBox.toggleIcon;
+ this.longTextNode = this.enclosingBox.longEmailAddresses;
+ this.textNode = this.enclosingBox.emailAddresses;
+ }
+ }
+ else
+ this.useToggle = false;
+
+ if (this.textNode)
+ this.textNode.useShortView = useShortView;
+
+ if ("outputFunction" in headerListInfo)
+ this.outputFunction = headerListInfo.outputFunction;
+ else
+ this.outputFunction = updateHeaderValue;
+
+ // Stash this so that the <mail-multi-emailheaderfield/> binding can
+ // later attach it to any <mail-emailaddress> tags it creates for later
+ // extraction and use by UpdateEmailNodeDetails.
+ this.enclosingBox.headerName = headerListInfo.name;
+
+}
+
+function initializeHeaderViewTables()
+{
+ // iterate over each header in our header list arrays and create header entries
+ // for each one. These header entries are then stored in the appropriate header table
+ for (let index = 0; index < gCollapsedHeaderList.length; index++)
+ {
+ gCollapsedHeaderView[gCollapsedHeaderList[index].name] =
+ new createHeaderEntry('collapsed', gCollapsedHeaderList[index]);
+ }
+
+ for (let index = 0; index < gExpandedHeaderList.length; index++)
+ {
+ var headerName = gExpandedHeaderList[index].name;
+ gExpandedHeaderView[headerName] = new createHeaderEntry('expanded', gExpandedHeaderList[index]);
+ }
+
+ var extraHeaders = Services.prefs.getCharPref("mailnews.headers.extraExpandedHeaders").match(/[^ ]+/g);
+ if (extraHeaders) {
+ for (let index = 0; index < extraHeaders.length; index++)
+ {
+ let extraHeader = extraHeaders[index];
+ gExpandedHeaderView[extraHeader.toLowerCase()] = new createNewHeaderView(extraHeader, extraHeader + ':');
+ }
+ }
+
+ if (Services.prefs.getBoolPref("mailnews.headers.showOrganization"))
+ {
+ let organizationEntry = {name:"organization", outputFunction:updateHeaderValue};
+ gExpandedHeaderView[organizationEntry.name] = new createHeaderEntry('expanded', organizationEntry);
+ }
+
+ if (Services.prefs.getBoolPref("mailnews.headers.showUserAgent"))
+ {
+ let userAgentEntry = {name:"user-agent", outputFunction:updateHeaderValue};
+ gExpandedHeaderView[userAgentEntry.name] = new createHeaderEntry('expanded', userAgentEntry);
+ }
+
+ if (Services.prefs.getBoolPref("mailnews.headers.showMessageId"))
+ {
+ let messageIdEntry = {name:"message-id", outputFunction:OutputMessageIds};
+ gExpandedHeaderView[messageIdEntry.name] = new createHeaderEntry('expanded', messageIdEntry);
+ }
+}
+
+function OnLoadMsgHeaderPane()
+{
+ // load any preferences that at are global with regards to
+ // displaying a message...
+ gCollectIncoming = Services.prefs.getBoolPref("mail.collect_email_address_incoming");
+ gCollectNewsgroup = Services.prefs.getBoolPref("mail.collect_email_address_newsgroup");
+ gCollectOutgoing = Services.prefs.getBoolPref("mail.collect_email_address_outgoing");
+ gShowCondensedEmailAddresses = Services.prefs.getBoolPref("mail.showCondensedAddresses");
+
+ Services.prefs.addObserver("mail.showCondensedAddresses", MsgHdrViewObserver);
+ Services.prefs.addObserver("mail.show_headers", MsgHdrViewObserver);
+ Services.prefs.addObserver("mailnews.display.html_as", MsgHdrViewObserver);
+ Services.prefs.addObserver("mail.inline_attachments", MsgHdrViewObserver);
+
+ initializeHeaderViewTables();
+
+ // Add an address book listener so we can update the header view when things
+ // change.
+ MailServices.ab.addAddressBookListener(AddressBookListener,
+ Ci.nsIAbListener.all);
+
+ var toggleHeaderView = GetHeaderPane();
+ var initialCollapsedSetting = toggleHeaderView.getAttribute("state");
+ if (initialCollapsedSetting == "true")
+ gCollapsedHeaderViewMode = true;
+
+ // dispatch an event letting any listeners know that we have loaded the message pane
+ toggleHeaderView.dispatchEvent(new Event('messagepane-loaded',
+ { bubbles: false, cancelable: true }));
+}
+
+function OnUnloadMsgHeaderPane()
+{
+ Services.prefs.removeObserver("mail.showCondensedAddresses", MsgHdrViewObserver);
+ Services.prefs.removeObserver("mail.show_headers", MsgHdrViewObserver);
+ Services.prefs.removeObserver("mailnews.display.html_as", MsgHdrViewObserver);
+ Services.prefs.removeObserver("mail.inline_attachments", MsgHdrViewObserver);
+
+ MailServices.ab.removeAddressBookListener(AddressBookListener);
+
+ // dispatch an event letting any listeners know that we have unloaded the message pane
+ GetHeaderPane().dispatchEvent(new Event('messagepane-unloaded',
+ { bubbles: false, cancelable: true }));
+}
+
+var MsgHdrViewObserver = {
+ observe: function(subject, topic, prefName) {
+ // Verify that we're changing mail pane config prefs.
+ if (topic == "nsPref:changed") {
+ if (prefName == "mail.showCondensedAddresses") {
+ gShowCondensedEmailAddresses =
+ Services.prefs.getBoolPref("mail.showCondensedAddresses");
+ ReloadMessage();
+ } else if (prefName == "mail.show_headers" ||
+ prefName == "mailnews.display.html_as" ||
+ prefName == "mail.inline_attachments") {
+ ReloadMessage();
+ }
+ }
+ }
+};
+
+var AddressBookListener =
+{
+ onItemAdded: function(aParentDir, aItem) {
+ OnAddressBookDataChanged(nsIAbListener.itemAdded,
+ aParentDir, aItem);
+ },
+ onItemRemoved: function(aParentDir, aItem) {
+ OnAddressBookDataChanged(aItem instanceof nsIAbCard ?
+ nsIAbListener.directoryItemRemoved :
+ nsIAbListener.directoryRemoved,
+ aParentDir, aItem);
+ },
+ onItemPropertyChanged: function(aItem, aProperty, aOldValue, aNewValue) {
+ // We only need updates for card changes, address book and mailing list
+ // ones don't affect us here.
+ if (aItem instanceof nsIAbCard)
+ OnAddressBookDataChanged(nsIAbListener.itemChanged, null, aItem);
+ }
+};
+
+function OnAddressBookDataChanged(aAction, aParentDir, aItem)
+{
+ gEmailAddressHeaderNames.forEach(function (aHeaderName)
+ {
+ var headerEntry = null;
+
+ // Ensure both collapsed and expanded are updated in case we toggle
+ // between the two.
+ if (aHeaderName in gCollapsedHeaderView)
+ {
+ headerEntry = gCollapsedHeaderView[aHeaderName];
+ if (headerEntry)
+ headerEntry.enclosingBox.updateExtraAddressProcessing(aAction,
+ aParentDir,
+ aItem);
+ }
+ if (aHeaderName in gExpandedHeaderView)
+ {
+ headerEntry = gExpandedHeaderView[aHeaderName];
+ if (headerEntry)
+ headerEntry.enclosingBox.updateExtraAddressProcessing(aAction,
+ aParentDir,
+ aItem);
+ }
+ });
+}
+
+// The messageHeaderSink is the class that gets notified of a message's headers as we display the message
+// through our mime converter.
+
+var messageHeaderSink = {
+ QueryInterface: XPCOMUtils.generateQI(
+ [Ci.nsIMsgHeaderSink]),
+ onStartHeaders: function()
+ {
+ this.mSaveHdr = null;
+ // clear out any pending collected address timers...
+ if (gCollectAddressTimer)
+ {
+ clearTimeout(gCollectAddressTimer);
+ gCollectAddressTimer = null;
+ }
+
+ // every time we start to redisplay a message, check the view all headers pref....
+ var showAllHeadersPref = Services.prefs.getIntPref("mail.show_headers");
+ if (showAllHeadersPref == 2)
+ {
+ gViewAllHeaders = true;
+ }
+ else
+ {
+ if (gViewAllHeaders) // if we currently are in view all header mode, rebuild our header view so we remove most of the header data
+ {
+ hideHeaderView(gExpandedHeaderView);
+ RemoveNewHeaderViews(gExpandedHeaderView);
+ gExpandedHeaderView = {};
+ initializeHeaderViewTables();
+ }
+
+ gViewAllHeaders = false;
+ }
+
+ ClearCurrentHeaders();
+ gBuiltExpandedView = false;
+ gBuiltCollapsedView = false;
+ gBuildAttachmentsForCurrentMsg = false;
+ gBuildAttachmentPopupForCurrentMsg = true;
+ ClearAttachmentList();
+ ClearEditMessageBox("editDraftBox");
+ ClearEditMessageBox("editTemplateBox");
+ gMessageNotificationBar.clearMsgNotifications();
+
+ for (let index in gMessageListeners)
+ gMessageListeners[index].onStartHeaders();
+ },
+
+ onEndHeaders: function()
+ {
+ ClearHeaderView(gCollapsedHeaderView);
+ ClearHeaderView(gExpandedHeaderView);
+
+ EnsureSubjectValue(); // make sure there is a subject even if it's empty so we'll show the subject and the twisty
+
+ // Load feed web page if so configured. This entry point works for
+ // messagepane loads in 3pane folder tab, 3pane message tab, and the
+ // standalone message window.
+ if (!FeedMessageHandler.shouldShowSummary(gMessageDisplay.displayedMessage, false))
+ FeedMessageHandler.setContent(gMessageDisplay.displayedMessage, false);
+
+ ShowMessageHeaderPane();
+ UpdateMessageHeaders();
+ ShowEditMessageBox("editDraftBox", Ci.nsMsgFolderFlags.Drafts);
+ ShowEditMessageBox("editTemplateBox", Ci.nsMsgFolderFlags.Templates);
+
+ for (let index in gMessageListeners)
+ gMessageListeners[index].onEndHeaders();
+ },
+
+ processHeaders: function(headerNameEnumerator, headerValueEnumerator, dontCollectAddress)
+ {
+ this.onStartHeaders();
+
+ const kMailboxSeparator = ", ";
+ var index = 0;
+ while (headerNameEnumerator.hasMore())
+ {
+ var header = new Object;
+ header.headerValue = headerValueEnumerator.getNext();
+ header.headerName = headerNameEnumerator.getNext();
+
+ // For consistency's sake, let us force all header names to be lower
+ // case so we don't have to worry about looking for: Cc and CC, etc.
+ var lowerCaseHeaderName = header.headerName.toLowerCase();
+
+ // If we have an x-mailer, x-mimeole, or x-newsreader string,
+ // put it in the user-agent slot which we know how to handle already.
+ if (/^x-(mailer|mimeole|newsreader)$/.test(lowerCaseHeaderName))
+ lowerCaseHeaderName = "user-agent";
+
+ if (this.mDummyMsgHeader)
+ {
+ if (lowerCaseHeaderName == "from")
+ this.mDummyMsgHeader.author = header.headerValue;
+ else if (lowerCaseHeaderName == "to")
+ this.mDummyMsgHeader.recipients = header.headerValue;
+ else if (lowerCaseHeaderName == "cc")
+ this.mDummyMsgHeader.ccList = header.headerValue;
+ else if (lowerCaseHeaderName == "subject")
+ this.mDummyMsgHeader.subject = header.headerValue;
+ else if (lowerCaseHeaderName == "reply-to")
+ this.mDummyMsgHeader.replyTo = header.headerValue;
+ else if (lowerCaseHeaderName == "message-id")
+ this.mDummyMsgHeader.messageId = header.headerValue;
+ else if (lowerCaseHeaderName == "list-post")
+ this.mDummyMsgHeader.listPost = header.headerValue;
+ else if (lowerCaseHeaderName == "date")
+ this.mDummyMsgHeader.date = Date.parse(header.headerValue) * 1000;
+ }
+
+ // We emit both the original, raw date header and a localized version.
+ // Pretend that the localized version is the real version.
+ if (lowerCaseHeaderName == "date")
+ continue;
+ if (lowerCaseHeaderName == "x-mozilla-localizeddate")
+ {
+ lowerCaseHeaderName = "date";
+ header.headerName = "Date";
+ }
+
+ // according to RFC 2822, certain headers
+ // can occur "unlimited" times
+ if (lowerCaseHeaderName in currentHeaderData)
+ {
+ // sometimes, you can have multiple To or Cc lines....
+ // in this case, we want to append these headers into one.
+ if (lowerCaseHeaderName == 'to' || lowerCaseHeaderName == 'cc')
+ currentHeaderData[lowerCaseHeaderName].headerValue = currentHeaderData[lowerCaseHeaderName].headerValue + ',' + header.headerValue;
+ else
+ {
+ // use the index to create a unique header name like:
+ // received5, received6, etc
+ currentHeaderData[lowerCaseHeaderName + index++] = header;
+ }
+ }
+ else
+ currentHeaderData[lowerCaseHeaderName] = header;
+
+ if (lowerCaseHeaderName == "from")
+ {
+ if (header.headerValue)
+ {
+ try
+ {
+ var createCard = (gCollectIncoming && !dontCollectAddress) || (gCollectNewsgroup && dontCollectAddress);
+ if (createCard || gCollectOutgoing)
+ {
+ // collect, add card if doesn't exist and gCollectOutgoing is set,
+ // otherwise only update existing cards, unknown preferred send format
+ gCollectAddressTimer = setTimeout(collectAddresses,
+ 2000,
+ header.headerValue,
+ createCard);
+ }
+ }
+ catch(ex) {}
+ }
+ } // if lowerCaseHeaderName == "from"
+ } // while we have more headers to parse
+
+ // process message tags as if they were headers in the message
+ SetTagHeader();
+
+ if (("from" in currentHeaderData) && ("sender" in currentHeaderData))
+ {
+ var senderMailbox = kMailboxSeparator +
+ MailServices.headerParser.extractHeaderAddressMailboxes(
+ currentHeaderData.sender.headerValue) + kMailboxSeparator;
+ var fromMailboxes = kMailboxSeparator +
+ MailServices.headerParser.extractHeaderAddressMailboxes(
+ currentHeaderData.from.headerValue) + kMailboxSeparator;
+ if (fromMailboxes.includes(senderMailbox))
+ delete currentHeaderData.sender;
+ }
+
+ this.onEndHeaders();
+ },
+
+ handleAttachment: function(contentType, url, displayName, uri,
+ isExternalAttachment)
+ {
+ this.skipAttachment = true;
+
+ // Don't show vcards as external attachments in the UI. libmime already
+ // renders them inline.
+ try
+ {
+ if (!this.mSaveHdr)
+ this.mSaveHdr = messenger.msgHdrFromURI(uri);
+ }
+ catch (ex) {}
+ if (contentType == "text/x-vcard")
+ {
+ var inlineAttachments = Services.prefs.getBoolPref("mail.inline_attachments");
+ var displayHtmlAs = Services.prefs.getIntPref("mailnews.display.html_as");
+ if (inlineAttachments && !displayHtmlAs)
+ return;
+ }
+
+ var size = null;
+ if (isExternalAttachment)
+ {
+ var file = GetFileFromString(url);
+ if (file && file.exists())
+ size = file.fileSize;
+ else
+ dump("Couldn't open external attachment!");
+ }
+
+ currentAttachments.push(new createNewAttachmentInfo(contentType,
+ url,
+ displayName,
+ uri,
+ isExternalAttachment,
+ size));
+ this.skipAttachment = false;
+
+ // If we have an attachment, set the nsMsgMessageFlags.Attachment flag
+ // on the hdr to cause the "message with attachment" icon to show up
+ // in the thread pane.
+ // We only need to do this on the first attachment.
+ var numAttachments = currentAttachments.length;
+ if (numAttachments == 1) {
+ // we also have to enable the Message/Attachments menuitem
+ var node = document.getElementById("msgAttachmentMenu");
+ if (node)
+ node.removeAttribute("disabled");
+
+ try {
+ // convert the uri into a hdr
+ this.mSaveHdr.markHasAttachments(true);
+ }
+ catch (ex) {
+ dump("ex = " + ex + "\n");
+ }
+ }
+ },
+
+ addAttachmentField: function(aField, aValue)
+ {
+ if (this.skipAttachment)
+ return;
+
+ let last = currentAttachments[currentAttachments.length - 1];
+ if (aField == "X-Mozilla-PartSize" && !last.isExternalAttachment &&
+ last.contentType != "text/x-moz-deleted")
+ {
+ let size = parseInt(aValue);
+ // libmime returns -1 if it never managed to figure out the size.
+ if (size != -1)
+ last.size = size;
+ }
+ else if (aField == "X-Mozilla-PartDownloaded" && aValue == "0")
+ {
+ // We haven't downloaded the attachment, so any size we get from
+ // libmime is almost certainly inaccurate. Just get rid of it. (Note:
+ // this relies on the fact that PartDownloaded comes after PartSize from
+ // the MIME emitter.)
+ last.size = null;
+ }
+ },
+
+ onEndAllAttachments: function()
+ {
+ // AddSaveAllAttachmentsMenu();
+ if (gCollapsedHeaderViewMode)
+ displayAttachmentsForCollapsedView();
+ else
+ displayAttachmentsForExpandedView();
+
+ for (let index in gMessageListeners) {
+ if ("onEndAttachments" in gMessageListeners[index])
+ gMessageListeners[index].onEndAttachments();
+ }
+ },
+
+ onEndMsgDownload: function(url)
+ {
+ // if we don't have any attachments, turn off the attachments flag
+ if (!this.mSaveHdr)
+ {
+ var messageUrl = url.QueryInterface(Ci.nsIMsgMessageUrl);
+ try
+ {
+ this.mSaveHdr = messenger.msgHdrFromURI(messageUrl.uri);
+ }
+ catch (ex) {}
+
+ }
+ if (!currentAttachments.length && this.mSaveHdr)
+ this.mSaveHdr.markHasAttachments(false);
+
+ let browser = getBrowser();
+ if (currentAttachments.length &&
+ Services.prefs.getBoolPref("mail.inline_attachments") &&
+ this.mSaveHdr && gFolderDisplay.selectedMessageIsFeed &&
+ browser && browser.contentDocument && browser.contentDocument.body) {
+ for (let img of browser.contentDocument.body.getElementsByClassName("moz-attached-image")) {
+ for (let attachment of currentAttachments) {
+ let partID = img.src.split("&part=")[1];
+ partID = partID ? partID.split("&")[0] : null;
+ if (attachment.partID && partID == attachment.partID) {
+ img.src = attachment.url;
+ break;
+ }
+ }
+ }
+ }
+
+ OnMsgParsed(url);
+ },
+
+ onEndMsgHeaders: function(url)
+ {
+ OnMsgLoaded(url);
+ },
+
+ onMsgHasRemoteContent: function(aMsgHdr, aContentURI, aCanOverride)
+ {
+ gMessageNotificationBar.setRemoteContentMsg(aMsgHdr, aContentURI, aCanOverride);
+ },
+
+ mSecurityInfo : null,
+ mSaveHdr: null,
+ get securityInfo()
+ {
+ return this.mSecurityInfo;
+ },
+ set securityInfo(aSecurityInfo)
+ {
+ this.mSecurityInfo = aSecurityInfo;
+ },
+
+ mDummyMsgHeader: null,
+
+ get dummyMsgHeader()
+ {
+ if (!this.mDummyMsgHeader)
+ this.mDummyMsgHeader = new nsDummyMsgHeader();
+ return this.mDummyMsgHeader;
+ },
+ mProperties: null,
+ get properties()
+ {
+ if (!this.mProperties)
+ this.mProperties = Cc["@mozilla.org/hash-property-bag;1"].
+ createInstance(Ci.nsIWritablePropertyBag2);
+ return this.mProperties;
+ },
+
+ resetProperties: function()
+ {
+ this.mProperties = null;
+ }
+};
+
+// Private method which generates a space delimited list of tag keys for the
+// current message. This list is then stored in currentHeaderData["tags"].
+function SetTagHeader()
+{
+ // it would be nice if we passed in the msgHdr from the back end
+ var msgHdr;
+ try
+ {
+ msgHdr = gDBView.hdrForFirstSelectedMessage;
+ }
+ catch (ex)
+ {
+ return; // no msgHdr to add our tags to
+ }
+
+ // get the list of known tags
+ var tagArray = MailServices.tags.getAllTags();
+ var tagKeys = {};
+ for (var tagInfo of tagArray)
+ if (tagInfo.tag)
+ tagKeys[tagInfo.key] = true;
+
+ // extract the tag keys from the msgHdr
+ var msgKeyArray = msgHdr.getStringProperty("keywords").split(" ");
+
+ // attach legacy label to the front if not already there
+ var label = msgHdr.label;
+ if (label)
+ {
+ var labelKey = "$label" + label;
+ if (!msgKeyArray.includes(labelKey))
+ msgKeyArray.unshift(labelKey);
+ }
+
+ // Rebuild the keywords string with just the keys that are actual tags or
+ // legacy labels and not other keywords like Junk and NonJunk.
+ // Retain their order, though, with the label as oldest element.
+ for (let i = msgKeyArray.length - 1; i >= 0; --i)
+ if (!(msgKeyArray[i] in tagKeys))
+ msgKeyArray.splice(i, 1); // remove non-tag key
+ var msgKeys = msgKeyArray.join(" ");
+
+ if (msgKeys)
+ currentHeaderData.tags = {headerName: "tags", headerValue: msgKeys};
+ else // no more tags, so clear out the header field
+ delete currentHeaderData.tags;
+}
+
+function EnsureSubjectValue()
+{
+ if (!('subject' in currentHeaderData))
+ {
+ var foo = new Object;
+ foo.headerValue = "";
+ foo.headerName = 'subject';
+ currentHeaderData[foo.headerName] = foo;
+ }
+}
+
+// Private method used by messageHeaderSink::processHeaders.
+function collectAddresses(aAddresses, aCreateCard)
+{
+ if (!abAddressCollector)
+ abAddressCollector = Cc["@mozilla.org/addressbook/services/addressCollector;1"]
+ .getService(Ci.nsIAbAddressCollector);
+ var sendFormat = Ci.nsIAbPreferMailFormat.unknown;
+ abAddressCollector.collectAddress(aAddresses, aCreateCard, sendFormat);
+}
+
+// Public method called by the tag front end code when the tags for the selected
+// message has changed.
+function OnTagsChange()
+{
+ // rebuild the tag headers
+ SetTagHeader();
+
+ // now update the expanded header view to rebuild the tags,
+ // and then show or hide the tag header box.
+ if (gBuiltExpandedView)
+ {
+ var headerEntry = gExpandedHeaderView.tags;
+ if (headerEntry)
+ {
+ headerEntry.valid = ("tags" in currentHeaderData);
+ if (headerEntry.valid)
+ headerEntry.outputFunction(headerEntry, currentHeaderData.tags.headerValue);
+
+ // if we are showing the expanded header view then we may need to collapse or
+ // show the tag header box...
+ if (!gCollapsedHeaderViewMode)
+ headerEntry.enclosingBox.collapsed = !headerEntry.valid;
+ }
+ }
+}
+
+// flush out any local state being held by a header entry for a given
+// table
+function ClearHeaderView(headerTable)
+{
+ for (let index in headerTable)
+ {
+ let headerEntry = headerTable[index];
+ if (headerEntry.useToggle)
+ {
+ headerEntry.enclosingBox.clearHeaderValues();
+ }
+
+ headerEntry.valid = false;
+ }
+}
+
+// make sure that any valid header entry in the table is collapsed
+function hideHeaderView(headerTable)
+{
+ for (let index in headerTable)
+ {
+ headerTable[index].enclosingBox.collapsed = true;
+ }
+}
+
+// make sure that any valid header entry in the table specified is
+// visible
+function showHeaderView(headerTable)
+{
+ for (let index in headerTable)
+ {
+ let headerEntry = headerTable[index];
+ if (headerEntry.valid)
+ {
+ headerEntry.enclosingBox.collapsed = false;
+ }
+ else // if the entry is invalid, always make sure it's collapsed
+ headerEntry.enclosingBox.collapsed = true;
+ }
+}
+
+// make sure the appropriate fields within the currently displayed view header mode
+// are collapsed or visible...
+function updateHeaderViews()
+{
+ if (gCollapsedHeaderViewMode)
+ {
+ showHeaderView(gCollapsedHeaderView);
+ displayAttachmentsForCollapsedView();
+ }
+ else
+ {
+ showHeaderView(gExpandedHeaderView);
+ displayAttachmentsForExpandedView();
+ }
+}
+
+function ToggleHeaderView()
+{
+ var expandedNode = document.getElementById("expandedHeaderView");
+ var collapsedNode = document.getElementById("collapsedHeaderView");
+
+ if (gCollapsedHeaderViewMode)
+ {
+ gCollapsedHeaderViewMode = false;
+ // hide the current view
+ hideHeaderView(gCollapsedHeaderView);
+ // update the current view
+ UpdateMessageHeaders();
+
+ // now uncollapse / collapse the right views
+ expandedNode.collapsed = false;
+ collapsedNode.collapsed = true;
+ }
+ else
+ {
+ gCollapsedHeaderViewMode = true;
+ // hide the current view
+ hideHeaderView(gExpandedHeaderView);
+ // update the current view
+ UpdateMessageHeaders();
+
+ // now uncollapse / collapse the right views
+ collapsedNode.collapsed = false;
+ expandedNode.collapsed = true;
+ }
+
+ var toggleHeaderView = GetHeaderPane();
+ if (gCollapsedHeaderViewMode)
+ toggleHeaderView.setAttribute("state", "true");
+ else
+ toggleHeaderView.setAttribute("state", "false");
+}
+
+// default method for updating a header value into a header entry
+function updateHeaderValue(headerEntry, headerValue)
+{
+ headerEntry.enclosingBox.headerValue = headerValue;
+}
+
+function updateHeaderValueInTextNode(headerEntry, headerValue)
+{
+ headerEntry.textNode.value = headerValue;
+}
+
+function createNewHeaderView(headerName, label)
+{
+ var idName = 'expanded' + headerName + 'Box';
+ var newHeader = document.createElement("mail-headerfield");
+
+ newHeader.setAttribute('id', idName);
+ newHeader.setAttribute('label', label);
+ // all mail-headerfield elements are keyword related
+ newHeader.setAttribute('keywordrelated','true');
+ newHeader.collapsed = true;
+
+ // this new element needs to be inserted into the view...
+ var topViewNode = document.getElementById('expandedHeaders');
+
+ topViewNode.appendChild(newHeader);
+
+ this.enclosingBox = newHeader;
+ this.isNewHeader = true;
+ this.isValid = false;
+ this.useToggle = false;
+ this.outputFunction = updateHeaderValue;
+}
+
+/**
+ * Removes all non-predefined header nodes from the view.
+ *
+ * @param aHeaderTable Table of header entries.
+ */
+function RemoveNewHeaderViews(aHeaderTable)
+{
+ for (let index in aHeaderTable)
+ {
+ let headerEntry = aHeaderTable[index];
+ if (headerEntry.isNewHeader)
+ headerEntry.enclosingBox.remove();
+ }
+}
+
+// UpdateMessageHeaders: Iterate through all the current header data we received from mime for this message
+// for each header entry table, see if we have a corresponding entry for that header. i.e. does the particular
+// view care about this header value. if it does then call updateHeaderEntry
+function UpdateMessageHeaders()
+{
+ // iterate over each header we received and see if we have a matching entry in each
+ // header view table...
+
+ for (let headerName in currentHeaderData)
+ {
+ let headerField = currentHeaderData[headerName];
+ let headerEntry = null;
+
+ if (headerName == "subject")
+ {
+ try {
+ if (gDBView.keyForFirstSelectedMessage == nsMsgKey_None)
+ {
+ let folder = null;
+ if (gCurrentFolderUri)
+ folder = MailUtils.getFolderForURI(gCurrentFolderUri);
+ setTitleFromFolder(folder, headerField.headerValue);
+ }
+ } catch (ex) {}
+ }
+
+ if (gCollapsedHeaderViewMode && !gBuiltCollapsedView)
+ {
+ if (headerName in gCollapsedHeaderView)
+ headerEntry = gCollapsedHeaderView[headerName];
+ }
+ else if (!gCollapsedHeaderViewMode && !gBuiltExpandedView)
+ {
+ if (headerName in gExpandedHeaderView)
+ headerEntry = gExpandedHeaderView[headerName];
+
+ if (!headerEntry && gViewAllHeaders)
+ {
+ // for view all headers, if we don't have a header field for this value....cheat and create one....then
+ // fill in a headerEntry
+ if (headerName == "message-id" || headerName == "in-reply-to")
+ {
+ let messageIdEntry = {name:headerName, outputFunction:OutputMessageIds};
+ gExpandedHeaderView[headerName] = new createHeaderEntry('expanded', messageIdEntry);
+ }
+ else
+ {
+ gExpandedHeaderView[headerName] = new createNewHeaderView(headerName,
+ currentHeaderData[headerName].headerName + ':');
+ }
+
+ headerEntry = gExpandedHeaderView[headerName];
+ }
+ } // if we are in expanded view....
+
+ if (headerEntry)
+ {
+ let show = Services.prefs.getBoolPref("mailnews.headers.showReferences");
+ if (headerName == "references" &&
+ !(gViewAllHeaders || show ||
+ (gDBView.msgFolder && gDBView.msgFolder.server.type == "nntp")))
+ {
+ // hide references header if view all headers mode isn't selected, the pref show references is
+ // deactivated and the currently displayed message isn't a newsgroup posting
+ headerEntry.valid = false;
+ }
+ else
+ {
+ headerEntry.outputFunction(headerEntry, headerField.headerValue);
+ headerEntry.valid = true;
+ }
+ }
+ }
+
+ if (gCollapsedHeaderViewMode)
+ gBuiltCollapsedView = true;
+ else
+ gBuiltExpandedView = true;
+
+ // now update the view to make sure the right elements are visible
+ updateHeaderViews();
+}
+
+function ClearCurrentHeaders()
+{
+ currentHeaderData = {};
+ currentAttachments = new Array();
+}
+
+function IsListPost()
+{
+ if ("list-post" in currentHeaderData)
+ return /<mailto:.+@.+>/.test(currentHeaderData["list-post"].headerValue);
+
+ return false;
+}
+
+function ShowMessageHeaderPane()
+{
+ var node;
+ if (gCollapsedHeaderViewMode)
+ {
+ node = document.getElementById("collapsedHeaderView");
+ if (node)
+ node.collapsed = false;
+ }
+ else
+ {
+ node = document.getElementById("expandedHeaderView");
+ if (node)
+ node.collapsed = false;
+ }
+
+ /* workaround for 39655 */
+ if (gFolderJustSwitched)
+ {
+ let el = GetHeaderPane();
+ el.setAttribute("style", el.getAttribute("style"));
+ gFolderJustSwitched = false;
+ }
+
+ document.commandDispatcher.updateCommands("message-header-pane");
+}
+
+function HideMessageHeaderPane()
+{
+ var node = document.getElementById("collapsedHeaderView");
+ if (node)
+ node.collapsed = true;
+
+ node = document.getElementById("expandedHeaderView");
+ if (node)
+ node.collapsed = true;
+
+ // we also have to disable the Message/Attachments menuitem
+ node = document.getElementById("msgAttachmentMenu");
+ if (node)
+ node.setAttribute("disabled", "true");
+
+ document.commandDispatcher.updateCommands("message-header-pane");
+}
+
+function OutputNewsgroups(headerEntry, headerValue)
+{
+ headerValue = headerValue.replace(/,/g,", ");
+ updateHeaderValue(headerEntry, headerValue);
+}
+
+// take string of message-ids separated by whitespace, split it
+// into message-ids and send them together with the index number
+// to the corresponding mail-messageids-headerfield element
+function OutputMessageIds(headerEntry, headerValue)
+{
+ var messageIdArray = headerValue.split(/\s+/);
+
+ headerEntry.enclosingBox.clearHeaderValues();
+ for (let i = 0; i < messageIdArray.length; i++)
+ headerEntry.enclosingBox.addMessageIdView(messageIdArray[i]);
+
+ headerEntry.enclosingBox.fillMessageIdNodes();
+}
+
+// OutputEmailAddresses --> knows how to take a comma separated list of email addresses,
+// extracts them one by one, linkifying each email address into a mailto url.
+// Then we add the link-ified email address to the parentDiv passed in.
+//
+// emailAddresses --> comma separated list of the addresses for this header field
+
+function OutputEmailAddresses(headerEntry, emailAddresses)
+{
+ if (!emailAddresses)
+ return;
+
+ // The email addresses are still RFC2047 encoded but libmime has already
+ // converted from "raw UTF-8" to "wide" (UTF-16) characters.
+ var addresses =
+ MailServices.headerParser.parseEncodedHeaderW(emailAddresses);
+
+ for (let addr of addresses) {
+ // If we want to include short/long toggle views and we have a long view,
+ // always add it. If we aren't including a short/long view OR if we are and
+ // we haven't parsed enough addresses to reach the cutoff valve yet then
+ // add it to the default (short) div.
+ let address = {};
+ address.emailAddress = addr.email || "";
+ address.fullAddress = addr.toString() || "";
+ address.displayName = addr.name || "";
+ if (headerEntry.useToggle)
+ headerEntry.enclosingBox.addAddressView(address);
+ else
+ updateEmailAddressNode(headerEntry.enclosingBox.emailAddressNode,
+ address);
+ }
+
+ if (headerEntry.useToggle)
+ headerEntry.enclosingBox.buildViews();
+}
+
+function updateEmailAddressNode(emailAddressNode, address)
+{
+ emailAddressNode.setAttribute("emailAddress", address.emailAddress);
+ emailAddressNode.setAttribute("fullAddress", address.fullAddress);
+ emailAddressNode.setAttribute("displayName", address.displayName);
+
+ UpdateEmailNodeDetails(address.emailAddress, emailAddressNode);
+}
+
+function UpdateEmailNodeDetails(aEmailAddress, aDocumentNode, aCardDetails)
+{
+ // If we haven't been given specific details, search for a card.
+ var cardDetails = aCardDetails || GetCardForEmail(aEmailAddress);
+ aDocumentNode.cardDetails = cardDetails;
+
+ var condense = gShowCondensedEmailAddresses;
+ // Get the id of the mail-multi-emailHeaderField binding parent.
+ var parentElementId = aDocumentNode.parentNode.parentNode.parentNode.id;
+ // Don't condense the address for the from and reply-to fields.
+ // Ids: "collapsedfromValue", "expandedfromBox", "expandedreply-toBox".
+ if (/^(collapsedfromValue|expanded(from|reply-to)Box)$/.test(parentElementId))
+ condense = false;
+
+ var displayName = "";
+ if (condense && cardDetails.card)
+ {
+ if (cardDetails.card.getProperty("PreferDisplayName", true) != true)
+ displayName = aDocumentNode.getAttribute("displayName");
+ if (!displayName)
+ displayName = cardDetails.card.displayName;
+ }
+
+ if (displayName)
+ {
+ aDocumentNode.setAttribute("tooltiptext", aEmailAddress);
+ }
+ else
+ {
+ aDocumentNode.removeAttribute("tooltiptext");
+ displayName = aDocumentNode.getAttribute("fullAddress") ||
+ aDocumentNode.getAttribute("displayName");
+ }
+
+ aDocumentNode.setAttribute("label", displayName);
+}
+
+function UpdateExtraAddressProcessing(aAddressData, aDocumentNode, aAction,
+ aParentDir, aItem)
+{
+ switch (aAction)
+ {
+ case nsIAbListener.itemChanged:
+ if (aAddressData &&
+ aDocumentNode.cardDetails.card &&
+ aItem.hasEmailAddress(aAddressData.emailAddress)) {
+ aDocumentNode.cardDetails.card = aItem;
+ UpdateEmailNodeDetails(aAddressData.emailAddress, aDocumentNode,
+ aDocumentNode.cardDetails);
+ }
+ break;
+ case nsIAbListener.itemAdded:
+ // Is it a new address book?
+ if (aItem instanceof nsIAbDirectory)
+ {
+ // If we don't have a match, search again for updates (e.g. a interface
+ // to an existing book may just have been added).
+ if (!aDocumentNode.cardDetails.card)
+ UpdateEmailNodeDetails(aAddressData.emailAddress, aDocumentNode);
+ }
+ else if (aItem instanceof nsIAbCard)
+ {
+ // If we don't have a card, does this new one match?
+ if (!aDocumentNode.cardDetails.card &&
+ aItem.hasEmailAddress(aAddressData.emailAddress))
+ {
+ // Just in case we have a bogus parent directory.
+ if (aParentDir instanceof nsIAbDirectory)
+ {
+ let cardDetails = { book: aParentDir, card: aItem };
+ UpdateEmailNodeDetails(aAddressData.emailAddress, aDocumentNode,
+ cardDetails);
+ }
+ else
+ {
+ UpdateEmailNodeDetails(aAddressData.emailAddress, aDocumentNode);
+ }
+ }
+ }
+ break;
+ case nsIAbListener.directoryItemRemoved:
+ // Unfortunately we don't necessarily get the same card object back.
+ if (aAddressData &&
+ aDocumentNode.cardDetails.card &&
+ aDocumentNode.cardDetails.book == aParentDir &&
+ aItem.hasEmailAddress(aAddressData.emailAddress))
+ {
+ UpdateEmailNodeDetails(aAddressData.emailAddress, aDocumentNode);
+ }
+ break;
+ case nsIAbListener.directoryRemoved:
+ if (aDocumentNode.cardDetails.book == aItem)
+ UpdateEmailNodeDetails(aAddressData.emailAddress, aDocumentNode);
+ break;
+ }
+}
+
+function SetupEmailAddressPopup(aAddressNode)
+{
+ document.getElementById("emailAddressPlaceHolder")
+ .setAttribute("label", aAddressNode.getAttribute("emailAddress"));
+
+ var addItem = document.getElementById("addToAddressBookItem");
+ var editItem = document.getElementById("editContactItem");
+ var viewItem = document.getElementById("viewContactItem");
+
+ if (aAddressNode.cardDetails.card)
+ {
+ addItem.setAttribute("hidden", true);
+ if (!aAddressNode.cardDetails.book.readOnly)
+ {
+ editItem.removeAttribute("hidden");
+ viewItem.setAttribute("hidden", true);
+ }
+ else
+ {
+ editItem.setAttribute("hidden", true);
+ viewItem.removeAttribute("hidden");
+ }
+ }
+ else
+ {
+ addItem.removeAttribute("hidden");
+ editItem.setAttribute("hidden", true);
+ viewItem.setAttribute("hidden", true);
+ }
+}
+
+/**
+ * Returns an object with two properties, book and card. If the email address
+ * is found in the address books, then book will contain an nsIAbDirectory,
+ * and card will contain an nsIAbCard. If the email address is not found, both
+ * properties will be null.
+ *
+ * @param emailAddress The email address to find.
+ * @return An object with two properties, book and card.
+ * @see nsIAbDirectory.cardForEmailAddress()
+ */
+function GetCardForEmail(aEmailAddress)
+{
+ var books = MailServices.ab.directories;
+
+ var result = { book: null, card: null};
+
+ while (!result.card && books.hasMoreElements())
+ {
+ var ab = books.getNext();
+ if (ab instanceof nsIAbDirectory)
+ {
+ try
+ {
+ var card = ab.cardForEmailAddress(aEmailAddress);
+ if (card)
+ {
+ result.book = ab;
+ result.card = card;
+ }
+ }
+ catch (ex)
+ {
+ // Unsearchable address books throw |NS_ERROR_NOT_IMPLEMENTED|.
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Create a new attachment object which goes into the data attachment array.
+ * This method checks whether the passed attachment is empty or not.
+ *
+ * @param contentType The attachment's mimetype
+ * @param url The URL for the attachment
+ * @param displayName The name to be displayed for this attachment (usually the
+ filename)
+ * @param uri The URI for the message containing the attachment
+ * @param isExternalAttachment True if the attachment has been detached
+ * @param size The size in bytes of the attachment
+ */
+function createNewAttachmentInfo(contentType, url, displayName, uri,
+ isExternalAttachment, size)
+{
+ this.contentType = contentType;
+ this.displayName = displayName;
+ this.uri = uri;
+ this.isExternalAttachment = isExternalAttachment;
+ this.attachment = this;
+ this.size = size;
+ let match;
+
+ // Remote urls, unlike non external mail part urls, may also contain query
+ // strings starting with ?; PART_RE does not handle this.
+ if (url.startsWith("http") || url.startsWith("file")) {
+ match = url.match(/[?&]part=[^&]+$/);
+ match = match && match[0];
+ this.partID = match && match.split("part=")[1];
+ url = url.replace(match, "");
+ }
+ else {
+ match = GlodaUtils.PART_RE.exec(url);
+ this.partID = match && match[1];
+ }
+
+ // Make sure to communicate it if it's an external http attachment and not a
+ // local attachment. For feeds attachments (enclosures) are always remote,
+ // so there is nothing to communicate.
+ if (isExternalAttachment && url.startsWith("http") &&
+ !gFolderDisplay.selectedMessageIsFeed) {
+ if (this.displayName) {
+ this.displayName = url + " - " + this.displayName;
+ }
+ else {
+ this.displayName = url;
+ }
+ }
+
+ this.url = url;
+
+}
+
+createNewAttachmentInfo.prototype.saveAttachment = function saveAttachment()
+{
+ if (this.isExternalAttachment)
+ // TODO: This displays "Save As" instead of "Save Attachment" in the title
+ internalSave(this.url, null,
+ this.displayName, null,
+ this.contentType, false,
+ null, null, null, document);
+ else
+ messenger.saveAttachment(this.contentType,
+ this.url,
+ encodeURIComponent(this.displayName),
+ this.uri,
+ false);
+}
+
+createNewAttachmentInfo.prototype.viewAttachment = function viewAttachment()
+{
+ var url = this.url;
+ if (!this.isExternalAttachment)
+ url += "&filename=" + encodeURIComponent(this.displayName);
+ openDialog("chrome://global/content/viewSource.xul",
+ "_blank", "all,dialog=no", {URL: url});
+}
+
+createNewAttachmentInfo.prototype.openAttachment = function openAttachment()
+{
+ switch (this.contentType)
+ {
+ // As of bug 599119, isTypeSupported returns true for messages, but
+ // attached messages don't open reliably in the browser, so pretend
+ // they're not supported and open a message window for them instead.
+ case "message/rfc822":
+ var url = this.url + "&type=application/x-message-display";
+ window.openDialog("chrome://messenger/content/messageWindow.xul",
+ "_blank", "all,dialog=no",
+ Services.io.newURI(url));
+ return;
+ case "text/x-moz-deleted":
+ return;
+ }
+
+ var webNavigationInfo =
+ Cc["@mozilla.org/webnavigation-info;1"]
+ .getService(Ci.nsIWebNavigationInfo);
+
+ if (webNavigationInfo.isTypeSupported(this.contentType, null))
+ openAsExternal(this.url);
+ else
+ messenger.openAttachment(this.contentType,
+ this.url,
+ encodeURIComponent(this.displayName),
+ this.uri,
+ this.isExternalAttachment);
+}
+
+createNewAttachmentInfo.prototype.printAttachment = function printAttachment()
+{
+ /* we haven't implemented the ability to print attachments yet...
+ messenger.printAttachment(this.contentType,
+ this.url,
+ encodeURIComponent(this.displayName),
+ this.uri);
+ */
+}
+
+createNewAttachmentInfo.prototype.deleteAttachment = function deleteAttachment()
+{
+ messenger.detachAttachment(this.contentType,
+ this.url,
+ encodeURIComponent(this.displayName),
+ this.uri,
+ false);
+}
+
+createNewAttachmentInfo.prototype.detachAttachment = function detachAttachment()
+{
+ messenger.detachAttachment(this.contentType,
+ this.url,
+ encodeURIComponent(this.displayName),
+ this.uri,
+ true);
+}
+
+function CanDetachAttachments()
+{
+ var canDetach = !gFolderDisplay.selectedMessageIsNews &&
+ (!gFolderDisplay.selectedMessageIsImap ||
+ !Services.io.offline);
+ if (canDetach && ("content-type" in currentHeaderData))
+ canDetach = !ContentTypeIsSMIME(currentHeaderData["content-type"].headerValue);
+ return canDetach;
+}
+
+/** Return true if the content type is an S/MIME one. */
+function ContentTypeIsSMIME(contentType)
+{
+ // S/MIME is application/pkcs7-mime and application/pkcs7-signature
+ // - also match application/x-pkcs7-mime and application/x-pkcs7-signature.
+ return /application\/(x-)?pkcs7-(mime|signature)/.test(contentType);
+}
+
+function onShowAttachmentContextMenu()
+{
+ // if no attachments are selected, disable the Open and Save...
+ var attachmentList = document.getElementById('attachmentList');
+ var selectedAttachments = [...attachmentList.selectedItems];
+ var openMenu = document.getElementById('context-openAttachment');
+ var viewMenu = document.getElementById('context-viewAttachment');
+ var saveMenu = document.getElementById('context-saveAttachment');
+ var detachMenu = document.getElementById('context-detachAttachment');
+ var deleteMenu = document.getElementById('context-deleteAttachment');
+ var saveAllMenu = document.getElementById('context-saveAllAttachments');
+ var detachAllMenu = document.getElementById('context-detachAllAttachments');
+ var deleteAllMenu = document.getElementById('context-deleteAllAttachments');
+
+ var canDetach = CanDetachAttachments();
+ var deletedAmongSelected = false;
+ var detachedAmongSelected = false;
+ var anyDeleted = false; // at least one deleted attachment in the list
+ var anyDetached = false; // at least one detached attachment in the list
+
+ // Check if one or more of the selected attachments are deleted.
+ for (let i = 0; i < selectedAttachments.length && !deletedAmongSelected; i++)
+ deletedAmongSelected =
+ (selectedAttachments[i].attachment.contentType == 'text/x-moz-deleted');
+
+ // Check if one or more of the selected attachments are detached.
+ for (let i = 0; i < selectedAttachments.length && !detachedAmongSelected; i++)
+ detachedAmongSelected = selectedAttachments[i].attachment.isExternalAttachment;
+
+ // Check if any attachments are deleted.
+ for (let i = 0; i < currentAttachments.length && !anyDeleted; i++)
+ anyDeleted = (currentAttachments[i].contentType == 'text/x-moz-deleted');
+
+ // Check if any attachments are detached.
+ for (let i = 0; i < currentAttachments.length && !anyDetached; i++)
+ anyDetached = currentAttachments[i].isExternalAttachment;
+
+ if (!deletedAmongSelected && selectedAttachments.length == 1)
+ {
+ openMenu.removeAttribute('disabled');
+ viewMenu.removeAttribute('disabled');
+ }
+ else
+ {
+ openMenu.setAttribute('disabled', true);
+ viewMenu.setAttribute('disabled', true);
+ }
+
+ saveMenu.setAttribute('disabled', deletedAmongSelected);
+ detachMenu.setAttribute('disabled', !canDetach || deletedAmongSelected
+ || detachedAmongSelected);
+ deleteMenu.setAttribute('disabled', !canDetach || deletedAmongSelected
+ || detachedAmongSelected);
+ saveAllMenu.setAttribute('disabled', anyDeleted);
+ detachAllMenu.setAttribute('disabled', !canDetach || anyDeleted || anyDetached);
+ deleteAllMenu.setAttribute('disabled', !canDetach || anyDeleted || anyDetached);
+}
+
+function MessageIdClick(node, event)
+{
+ if (event.button == 0)
+ {
+ var messageId = GetMessageIdFromNode(node, true);
+ OpenMessageForMessageId(messageId);
+ }
+}
+
+// this is our onclick handler for the attachment list.
+// A double click in a listitem simulates "opening" the attachment....
+function attachmentListClick(event)
+{
+ // we only care about button 0 (left click) events
+ if (event.button != 0)
+ return;
+
+ if (event.detail == 2) // double click
+ {
+ var target = event.target;
+ if (target.localName == "listitem")
+ target.attachment.openAttachment();
+ }
+}
+
+// on command handlers for the attachment list context menu...
+// commandPrefix matches one of our existing functions
+// (openAttachment, saveAttachment, etc.)
+function handleAttachmentSelection(commandPrefix)
+{
+ var attachmentList = document.getElementById('attachmentList');
+ var selectedAttachments = [...attachmentList.selectedItems];
+ if (selectedAttachments.length > 1)
+ HandleMultipleAttachments(commandPrefix, selectedAttachments);
+ else
+ selectedAttachments[0].attachment[commandPrefix]();
+}
+
+function createAttachmentDisplayName(aAttachment)
+{
+ // Strip any white space at the end of the display name to avoid
+ // attachment name spoofing (especially Windows will drop trailing dots
+ // and whitespace from filename extensions). Leading and internal
+ // whitespace will be taken care of by the crop="center" attribute.
+ // We must not change the actual filename, though.
+ return aAttachment.displayName.trimRight();
+}
+
+function displayAttachmentsForExpandedView()
+{
+ var numAttachments = currentAttachments.length;
+ if (numAttachments > 0 && !gBuildAttachmentsForCurrentMsg)
+ {
+ let attachmentList = document.getElementById('attachmentList');
+
+ for (let index in currentAttachments)
+ {
+ let attachment = currentAttachments[index];
+
+ // create a listitem for the attachment listbox
+ let displayName = createAttachmentDisplayName(attachment);
+ let nameAndSize = displayName;
+ if (attachment.size != null)
+ nameAndSize += " (" + messenger.formatFileSize(attachment.size) + ")";
+ let item = attachmentList.appendItem(nameAndSize, "");
+ item.setAttribute("crop", "center");
+ item.setAttribute("class", "listitem-iconic attachment-item");
+ item.setAttribute("tooltiptext", attachment.displayName);
+ item.attachment = attachment;
+ item.setAttribute("attachmentUrl", attachment.url);
+ item.setAttribute("attachmentContentType", attachment.contentType);
+ item.setAttribute("attachmentUri", attachment.uri);
+ item.setAttribute("attachmentSize", attachment.size);
+ if (attachment.contentType == "text/x-moz-deleted")
+ item.setAttribute('disabled', 'true');
+ else
+ setApplicationIconForAttachment(attachment, item);
+ } // for each attachment
+
+ gBuildAttachmentsForCurrentMsg = true;
+ }
+
+ var expandedAttachmentBox = document.getElementById('expandedAttachmentBox');
+ expandedAttachmentBox.collapsed = numAttachments <= 0;
+}
+
+// attachment --> the attachment struct containing all the information on the attachment
+// listitem --> the listitem currently showing the attachment.
+function setApplicationIconForAttachment(attachment, listitem)
+{
+ // generate a moz-icon url for the attachment so we'll show a nice icon next to it.
+ listitem.setAttribute('image', "moz-icon:" + "//" + attachment.displayName + "?size=16&contentType=" + attachment.contentType);
+}
+
+function displayAttachmentsForCollapsedView()
+{
+ var numAttachments = currentAttachments.length;
+ var attachmentNode = document.getElementById('collapsedAttachmentBox');
+ attachmentNode.collapsed = numAttachments <= 0; // make sure the attachment button is visible
+}
+
+// Public method called when we create the attachments file menu
+function FillAttachmentListPopup(popup)
+{
+ // the FE sometimes call this routine TWICE...I haven't been able to figure out why yet...
+ // protect against it...
+ if (!gBuildAttachmentPopupForCurrentMsg)
+ return;
+
+ var attachmentIndex = 0;
+
+ // otherwise we need to build the attachment view...
+ // First clear out the old view...
+ ClearAttachmentMenu(popup);
+
+ var canDetachOrDeleteAll = CanDetachAttachments();
+
+ for (let index in currentAttachments)
+ {
+ ++attachmentIndex;
+ addAttachmentToPopup(popup, currentAttachments[index], attachmentIndex);
+ if (canDetachOrDeleteAll &&
+ (currentAttachments[index].isExternalAttachment ||
+ currentAttachments[index].contentType == 'text/x-moz-deleted'))
+ canDetachOrDeleteAll = false;
+ }
+
+ gBuildAttachmentPopupForCurrentMsg = false;
+
+ var detachAllMenu = document.getElementById('file-detachAllAttachments');
+ var deleteAllMenu = document.getElementById('file-deleteAllAttachments');
+
+ detachAllMenu.setAttribute('disabled', !canDetachOrDeleteAll);
+ deleteAllMenu.setAttribute('disabled', !canDetachOrDeleteAll);
+}
+
+// Public method used to clear the file attachment menu
+function ClearAttachmentMenu(popup)
+{
+ if ( popup )
+ {
+ while (popup.firstChild.localName == 'menu')
+ popup.firstChild.remove();
+ }
+}
+
+// Public method used to determine the number of attachments for the currently displayed message...
+function GetNumberOfAttachmentsForDisplayedMessage()
+{
+ return currentAttachments.length;
+}
+
+// private method used to build up a menu list of attachments
+function addAttachmentToPopup(popup, attachment, attachmentIndex)
+{
+ if (popup)
+ {
+ var item = document.createElement('menu');
+ if ( item )
+ {
+ if (!gMessengerBundle)
+ gMessengerBundle = document.getElementById("bundle_messenger");
+
+ // insert the item just before the separator
+ item = popup.insertBefore(item, popup.childNodes[attachmentIndex - 1]);
+ item.setAttribute('class', 'menu-iconic attachment-item');
+
+ var displayName = createAttachmentDisplayName(attachment);
+ var formattedDisplayNameString = gMessengerBundle.getFormattedString("attachmentDisplayNameFormat",
+ [attachmentIndex, displayName]);
+
+ item.setAttribute("crop", "center");
+ item.setAttribute('label', formattedDisplayNameString);
+ item.setAttribute('accesskey', attachmentIndex);
+
+ var openpopup = document.createElement('menupopup');
+ openpopup = item.appendChild(openpopup);
+ if (attachment.contentType == "text/x-moz-deleted") {
+ item.setAttribute('disabled', 'true');
+ return;
+ }
+ openpopup.attachment = attachment;
+ openpopup.addEventListener('popupshowing', FillAttachmentItemPopup);
+ setApplicationIconForAttachment(attachment, item);
+ }
+ }
+}
+
+function FillAttachmentItemPopup(event)
+{
+ var openpopup = event.target;
+ var canDetach = CanDetachAttachments() && !openpopup.attachment.isExternalAttachment;
+ openpopup.removeEventListener('popupshowing', FillAttachmentItemPopup);
+
+ var menuitementry = document.getElementById("context-openAttachment").cloneNode(false);
+ menuitementry.setAttribute('oncommand', 'this.parentNode.attachment.openAttachment();');
+ menuitementry = openpopup.appendChild(menuitementry);
+
+ menuitementry = document.getElementById("context-viewAttachment").cloneNode(false);
+ menuitementry.setAttribute('oncommand', 'this.parentNode.attachment.viewAttachment();');
+ menuitementry = openpopup.appendChild(menuitementry);
+
+ menuitementry = document.getElementById("context-saveAttachment").cloneNode(false);
+ menuitementry.setAttribute('oncommand', 'this.parentNode.attachment.saveAttachment()');
+ menuitementry = openpopup.appendChild(menuitementry);
+
+ openpopup.appendChild(document.createElement("menuseparator"));
+
+ menuitementry = document.getElementById("context-detachAttachment").cloneNode(false);
+ menuitementry.setAttribute('oncommand', 'this.parentNode.attachment.detachAttachment()');
+ if (!canDetach)
+ menuitementry.setAttribute('disabled', 'true');
+ menuitementry = openpopup.appendChild(menuitementry);
+
+ menuitementry = document.getElementById("context-deleteAttachment").cloneNode(false);
+ menuitementry.setAttribute('oncommand', 'this.attachment.deleteAttachment()');
+ if (!canDetach)
+ menuitementry.setAttribute('disabled', 'true');
+ menuitementry = openpopup.appendChild(menuitementry);
+}
+
+function HandleMultipleAttachments(commandPrefix, selectedAttachments)
+{
+ try
+ {
+ // convert our attachment data into some c++ friendly structs
+ var attachmentContentTypeArray = new Array();
+ var attachmentUrlArray = new Array();
+ var attachmentDisplayNameArray = new Array();
+ var attachmentMessageUriArray = new Array();
+
+ // populate these arrays..
+ for (let index in selectedAttachments)
+ {
+ let attachment = selectedAttachments[index].attachment;
+ attachmentContentTypeArray[index] = attachment.contentType;
+ attachmentUrlArray[index] = attachment.url;
+ attachmentDisplayNameArray[index] = encodeURI(attachment.displayName);
+ attachmentMessageUriArray[index] = attachment.uri;
+ }
+
+ // okay the list has been built... now call our action code...
+ switch (commandPrefix)
+ {
+ case "saveAttachment":
+ messenger.saveAllAttachments(attachmentContentTypeArray,
+ attachmentUrlArray,
+ attachmentDisplayNameArray,
+ attachmentMessageUriArray);
+ break;
+ case "detachAttachment":
+ messenger.detachAllAttachments(attachmentContentTypeArray,
+ attachmentUrlArray,
+ attachmentDisplayNameArray,
+ attachmentMessageUriArray,
+ true /* save */);
+ break;
+ case "deleteAttachment":
+ messenger.detachAllAttachments(attachmentContentTypeArray,
+ attachmentUrlArray,
+ attachmentDisplayNameArray,
+ attachmentMessageUriArray,
+ false /* don't save */);
+ break;
+ default:
+ dump (commandPrefix + "** unknown handle all attachments action **\n");
+ }
+ }
+ catch (ex)
+ {
+ dump ("** failed to handle all attachments **\n");
+ }
+}
+
+function ClearAttachmentList()
+{
+ // we also have to disable the Message/Attachments menuitem
+ var node = document.getElementById("msgAttachmentMenu");
+ if (node)
+ node.setAttribute("disabled", "true");
+
+ // clear selection
+ var list = document.getElementById('attachmentList');
+ list.clearSelection();
+
+ while (list.hasChildNodes())
+ list.lastChild.remove();
+}
+
+function ShowEditMessageBox(aMessageBox, aFlag) {
+ try {
+ // it would be nice if we passed in the msgHdr from the back end
+ var msgHdr = gDBView.hdrForFirstSelectedMessage;
+ if (!msgHdr || !msgHdr.folder)
+ return;
+ if (msgHdr.folder.isSpecialFolder(aFlag, true))
+ document.getElementById(aMessageBox).collapsed = false;
+ }
+ catch (ex) {}
+}
+
+function ClearEditMessageBox(aMessageBox) {
+ var editBox = document.getElementById(aMessageBox);
+ if (editBox)
+ editBox.collapsed = true;
+}
+
+// CopyWebsiteAddress takes the website address title button, extracts
+// the website address we stored in there and copies it to the clipboard
+function CopyWebsiteAddress(websiteAddressNode)
+{
+ if (websiteAddressNode)
+ {
+ var websiteAddress = websiteAddressNode.getAttribute("value");
+
+ var contractid = "@mozilla.org/widget/clipboardhelper;1";
+ var iid = Ci.nsIClipboardHelper;
+ var clipboard = Cc[contractid].getService(iid);
+ clipboard.copyString(websiteAddress);
+ }
+}
+
+function BookmarkWebsite(aWebsiteAddressNode)
+{
+ if (aWebsiteAddressNode)
+ {
+ let websiteAddress = aWebsiteAddressNode.getAttribute("value");
+
+ if (currentHeaderData && "content-base" in currentHeaderData)
+ {
+ let url = currentHeaderData["content-base"].headerValue;
+ if (url != websiteAddress)
+ return;
+
+ let title = currentHeaderData["subject"].headerValue;
+ PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(url), title);
+ }
+ }
+}
+
+var attachmentAreaDNDObserver = {
+ onDragStart(aEvent) {
+ var target = aEvent.target;
+ if (target.localName == "listitem") {
+ let index = 0;
+ let selection = target.parentNode.selectedItems;
+ for (let item of selection) {
+ let attachment = item.attachment;
+ if (attachment.contentType == "text/x-moz-deleted") {
+ continue;
+ }
+
+ let name = attachment.name || attachment.displayName;
+ if (!attachment.url || !name) {
+ continue;
+ }
+
+ let info = attachment.url;
+ // Only add type/filename info for non-file URLs that don't already
+ // have it.
+ if (!/(^file:|&filename=)/.test(info)) {
+ info += "&type=" + attachment.contentType + "&filename=" +
+ encodeURIComponent(name);
+ }
+ let dt = aEvent.dataTransfer;
+ dt.mozSetDataAt("text/x-moz-url",
+ info + "\n" + name + "\n" + attachment.size,
+ index);
+ dt.mozSetDataAt("text/x-moz-url-data", attachment.url, index);
+ dt.mozSetDataAt("text/x-moz-url-desc", name, index);
+ dt.mozSetDataAt("application/x-moz-file-promise-url", attachment.url,
+ index);
+ dt.mozSetDataAt("application/x-moz-file-promise",
+ new nsFlavorDataProvider(), index);
+ index++;
+ }
+ }
+ aEvent.stopPropagation();
+ }
+};
+
+function nsFlavorDataProvider()
+{
+}
+
+nsFlavorDataProvider.prototype =
+{
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFlavorDataProvider]),
+
+ getFlavorData : function(aTransferable, aFlavor, aData, aDataLen)
+ {
+ // get the url for the attachment
+ if (aFlavor == "application/x-moz-file-promise")
+ {
+ var urlPrimitive = { };
+ var dataSize = { };
+ aTransferable.getTransferData("application/x-moz-file-promise-url", urlPrimitive, dataSize);
+
+ var srcUrlPrimitive = urlPrimitive.value.QueryInterface(Ci.nsISupportsString);
+
+ // now get the destination file location from kFilePromiseDirectoryMime
+ var dirPrimitive = {};
+ aTransferable.getTransferData("application/x-moz-file-promise-dir", dirPrimitive, dataSize);
+ var destDirectory = dirPrimitive.value.QueryInterface(Ci.nsIFile);
+
+ // now save the attachment to the specified location
+ // XXX: we need more information than just the attachment url to save it, fortunately, we have an array
+ // of all the current attachments so we can cheat and scan through them
+
+ var attachment = null;
+ for (let index in currentAttachments)
+ {
+ attachment = currentAttachments[index];
+ if (attachment.url == srcUrlPrimitive)
+ break;
+ }
+
+ // call our code for saving attachments
+ if (attachment)
+ {
+ var destFilePath = messenger.saveAttachmentToFolder(attachment.contentType, attachment.url, encodeURIComponent(attachment.displayName), attachment.uri, destDirectory);
+ aData.value = destFilePath.QueryInterface(Ci.nsISupports);
+ aDataLen.value = 4;
+ }
+ }
+ }
+}
+
+function nsDummyMsgHeader()
+{
+}
+
+nsDummyMsgHeader.prototype =
+{
+ mProperties : new Array,
+ getStringProperty : function(aProperty)
+ {
+ return this.mProperties[aProperty];
+ },
+ setStringProperty : function(aProperty, aVal)
+ {
+ this.mProperties[aProperty] = aVal;
+ },
+ getUint32Property : function(aProperty)
+ {
+ if (aProperty in this.mProperties)
+ return parseInt(this.mProperties[aProperty]);
+ return 0;
+ },
+ setUint32Property : function(aProperty, aVal)
+ {
+ this.mProperties[aProperty] = aVal.toString();
+ },
+ markHasAttachments : function(hasAttachments) {},
+ messageSize : 0,
+ recipients : null,
+ from : null,
+ subject : "",
+ get mime2DecodedSubject() { return this.subject; },
+ ccList : null,
+ messageId : null,
+ listPost : null,
+ date : 0,
+ accountKey : "",
+ flags : 0,
+ folder : null
+};
diff --git a/comm/suite/mailnews/content/msgHdrViewOverlay.xul b/comm/suite/mailnews/content/msgHdrViewOverlay.xul
new file mode 100644
index 0000000000..ea2c12e091
--- /dev/null
+++ b/comm/suite/mailnews/content/msgHdrViewOverlay.xul
@@ -0,0 +1,273 @@
+<?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 % msgHdrViewPopupDTD SYSTEM "chrome://messenger/locale/msgHdrViewPopup.dtd" >
+%msgHdrViewPopupDTD;
+<!ENTITY % msgHdrViewOverlayDTD SYSTEM "chrome://messenger/locale/msgHdrViewOverlay.dtd" >
+%msgHdrViewOverlayDTD;
+]>
+
+<?xml-stylesheet href="chrome://messenger/skin/messageHeader.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/messageKeywords.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/smime/msgHdrViewSMIMEOverlay.css" type="text/css"?>
+
+<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://messenger/content/msgHdrViewOverlay.js"/>
+
+<script src="chrome://messenger-smime/content/msgHdrViewSMIMEOverlay.js"/>
+
+<stringbundleset id="stringbundleset">
+ <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+</stringbundleset>
+
+<menupopup id="messageIdContext" popupanchor="bottomleft"
+ onpopupshowing="FillMessageIdContextMenu(document.popupNode);">
+ <menuitem id="messageIdContext-messageIdTarget"
+ disabled="true"/>
+ <menuseparator/>
+ <menuitem id="messageIdContext-openMessageForMsgId"
+ label="&OpenMessageForMsgId.label;"
+ accesskey="&OpenMessageForMsgId.accesskey;"
+ oncommand="var messageId = GetMessageIdFromNode(document.popupNode, true);
+ OpenMessageForMessageId(messageId)"/>
+ <menuitem id="messageIdContext-openBrowserWithMsgId"
+ label="&OpenBrowserWithMsgId.label;"
+ accesskey="&OpenBrowserWithMsgId.accesskey;"
+ oncommand="var messageId = GetMessageIdFromNode(document.popupNode, true);
+ OpenBrowserWithMessageId(messageId)"/>
+ <menuitem id="messageIdContext-copyMessageId"
+ label="&CopyMessageId.label;"
+ accesskey="&CopyMessageId.accesskey;"
+ oncommand="var messageId = GetMessageIdFromNode(document.popupNode, false);
+ CopyString(messageId);"/>
+</menupopup>
+
+<menupopup id="emailAddressPopup" popupanchor="bottomleft"
+ onpopupshowing="SetupEmailAddressPopup(document.popupNode);
+ goUpdateCommand('cmd_createFilterFromPopup');">
+ <menuitem id="emailAddressPlaceHolder" label="" disabled="true"/>
+ <menuseparator/>
+ <menuitem id="sendMailToItem"
+ label="&SendMailTo.label;"
+ accesskey="&SendMailTo.accesskey;"
+ oncommand="SendMailToNode(document.popupNode, event)"/>
+ <menuitem id="createFilterFromItem"
+ label="&CreateFilterFrom.label;"
+ accesskey="&CreateFilterFrom.accesskey;"
+ command="cmd_createFilterFromPopup"/>
+ <menuitem id="addToAddressBookItem"
+ label="&AddToAddressBook.label;"
+ accesskey="&AddToAddressBook.accesskey;"
+ oncommand="AddContact(document.popupNode);"/>
+ <menuitem id="editContactItem"
+ label="&EditContact.label;"
+ accesskey="&EditContact.accesskey;"
+ hidden="true"
+ oncommand="EditContact(document.popupNode);"/>
+ <menuitem id="viewContactItem"
+ label="&ViewContact.label;"
+ accesskey="&ViewContact.accesskey;"
+ hidden="true"
+ oncommand="EditContact(document.popupNode);"/>
+ <menuitem id="copyEmailAddressItem"
+ label="&CopyEmailAddress.label;"
+ accesskey="&CopyEmailAddress.accesskey;"
+ oncommand="CopyEmailAddress(document.popupNode);"/>
+ <menuitem id="copyNameAndEmailAddressItem"
+ label="&CopyNameAndEmailAddress.label;"
+ accesskey="&CopyNameAndEmailAddress.accesskey;"
+ oncommand="CopyEmailAddress(document.popupNode, true);"/>
+</menupopup>
+
+<menupopup id="attachmentListContext" onpopupshowing="return onShowAttachmentContextMenu();">
+ <menuitem id="context-openAttachment" label="&openAttachmentCmd.label;" accesskey="&openAttachmentCmd.accesskey;"
+ oncommand="handleAttachmentSelection('openAttachment');"/>
+ <menuitem id="context-viewAttachment" label="&viewAttachmentCmd.label;" accesskey="&viewAttachmentCmd.accesskey;"
+ oncommand="handleAttachmentSelection('viewAttachment');"/>
+ <menuitem id="context-saveAttachment" label="&saveAsAttachmentCmd.label;" accesskey="&saveAsAttachmentCmd.accesskey;"
+ oncommand="handleAttachmentSelection('saveAttachment');"/>
+ <menuseparator/>
+ <menuitem id="context-detachAttachment" label="&detachAttachmentCmd.label;" accesskey="&detachAttachmentCmd.accesskey;"
+ oncommand="handleAttachmentSelection('detachAttachment');"/>
+ <menuitem id="context-deleteAttachment" label="&deleteAttachmentCmd.label;" accesskey="&deleteAttachmentCmd.accesskey;"
+ oncommand="handleAttachmentSelection('deleteAttachment');"/>
+ <menuseparator/>
+ <menuitem id="context-saveAllAttachments" oncommand="HandleMultipleAttachments('saveAttachment', currentAttachments);"
+ label="&saveAllAttachmentsCmd.label;" accesskey="&saveAllAttachmentsCmd.accesskey;"/>
+ <menuitem id="context-detachAllAttachments" oncommand="HandleMultipleAttachments('detachAttachment', currentAttachments);"
+ label="&detachAllAttachmentsCmd.label;" accesskey="&detachAllAttachmentsCmd.accesskey;"/>
+ <menuitem id="context-deleteAllAttachments" oncommand="HandleMultipleAttachments('deleteAttachment', currentAttachments);"
+ label="&deleteAllAttachmentsCmd.label;" accesskey="&deleteAllAttachmentsCmd.accesskey;"/>
+</menupopup>
+
+<menupopup id="attachmentMenuList">
+ <menuseparator/>
+ <menuitem id="file-saveAllAttachments" label="&saveAllAttachmentsCmd.label;"
+ accesskey="&saveAllAttachmentsCmd.accesskey;" oncommand="HandleMultipleAttachments('saveAttachment', currentAttachments);"/>
+ <menuitem id="file-detachAllAttachments" label="&detachAllAttachmentsCmd.label;"
+ accesskey="&detachAllAttachmentsCmd.accesskey;" oncommand="HandleMultipleAttachments('detachAttachment', currentAttachments);" />
+ <menuitem id="file-deleteAllAttachments" label="&deleteAllAttachmentsCmd.label;"
+ accesskey="&deleteAllAttachmentsCmd.accesskey;" oncommand="HandleMultipleAttachments('deleteAttachment', currentAttachments);" />
+</menupopup>
+
+<menupopup id="copyUrlPopup">
+ <menuitem label="&openInBrowser.label;"
+ accesskey="&openInBrowser.accesskey;"
+ oncommand="openAsExternal(document.popupNode.getAttribute('value'));"/>
+ <menuitem label="&bookmarkLinkCmd.label;"
+ accesskey="&bookmarkLinkCmd.accesskey;"
+ oncommand="BookmarkWebsite(document.popupNode);"/>
+ <menuitem label="&copyLinkCmd.label;"
+ accesskey="&copyLinkCmd.accesskey;"
+ oncommand="CopyWebsiteAddress(document.popupNode);"/>
+</menupopup>
+
+<hbox id="msgHeaderView" persist="state">
+
+<grid id="collapsedHeaderView" class="header-part1" flex="1" collapsed="true">
+ <rows>
+ <row flex="1"/>
+ </rows>
+ <columns>
+ <column class="collapsedToggleHdrBox">
+ <hbox align="start">
+ <image id="toggleHeaderView" class="collapsedHeaderViewButton"
+ onclick="ToggleHeaderView();"/>
+ </hbox>
+ </column>
+
+ <column id="collapsedsubjectBox" collapsed="true" flex="1">
+ <hbox>
+ <label class="collapsedHeaderDisplayName" value="&subjectField.label;" control="collapsedsubjectValue"/>
+ <textbox id="collapsedsubjectValue"
+ class="collapsedHeaderValue plain"
+ readonly="true" crop="right" flex="1"/>
+ </hbox>
+ </column>
+
+ <column id="collapsedfromBox" flex="1">
+ <hbox align="start">
+ <mail-multi-emailHeaderField id="collapsedfromValue" class="collapsedHeaderDisplayName" label="&fromField.label;" collapsed="true" flex="1"/>
+ </hbox>
+ </column>
+
+ <column id = "collapseddateBox" collapsed="true">
+ <hbox align="start">
+ <textbox id="collapseddateValue"
+ class="collapsedHeaderValue plain"
+ readonly="true"/>
+ </hbox>
+ </column>
+
+ <column id="collapsedKeywordBox">
+ <hbox align="start">
+ <image id="collapsedKeywordImage"/>
+ </hbox>
+ </column>
+
+ <column id="collapsedAttachmentBox" collapsed="true">
+ <hbox align="start">
+ <image id="collapsedAttachment" class="collapsedAttachmentButton" onclick="ToggleHeaderView();" />
+ </hbox>
+ </column>
+ </columns>
+</grid>
+
+<hbox id="expandedHeaderView" class="header-part1" flex="1" collapsed="true">
+
+ <vbox id="expandedHeaders" flex="1">
+ <mail-toggle-headerfield id="expandedsubjectBox"
+ class="subjectvalue"
+ label="&subjectField.label;"
+ ontwistyclick="ToggleHeaderView();"
+ collapsed="true"/>
+
+ <mail-multi-emailHeaderField id="expandedfromBox" label="&fromField.label;" collapsed="true"/>
+ <mail-emailheaderfield id="expandedsenderBox" label="&senderField.label;" collapsed="true"/>
+ <mail-headerfield id="expandedorganizationBox" label="&organizationField.label;" collapsed="true"/>
+ <mail-multi-emailHeaderField id="expandedreply-toBox" label="&replyToField.label;" collapsed="true"/>
+
+ <mail-headerfield id="expandeddateBox"
+ label="&dateField.label;"
+ collapsed="true"/>
+
+ <mail-multi-emailHeaderField id="expandedtoBox" label="&toField.label;" collapsed="true"/>
+ <mail-multi-emailHeaderField id="expandedccBox" label="&ccField.label;" collapsed="true"/>
+ <mail-multi-emailHeaderField id="expandedbccBox" label="&bccField.label;" collapsed="true"/>
+
+ <mail-headerfield id="expandednewsgroupsBox"
+ label="&newsgroupsField.label;"
+ collapsed="true"/>
+ <mail-headerfield id="expandedfollowup-toBox"
+ label="&followupToField.label;"
+ collapsed="true"/>
+ <mail-messageids-headerfield id="expandedmessage-idBox" label="&messageIdField.label;" collapsed="true"/>
+ <mail-messageids-headerfield id="expandedin-reply-toBox" label="&inReplyToField.label;" collapsed="true"/>
+ <mail-messageids-headerfield id="expandedreferencesBox" label="&referencesField.label;" collapsed="true"/>
+ <mail-tagfield id="expandedtagsBox" label="&tagsHdr.label;" collapsed="true"/>
+ <mail-urlfield id="expandedcontent-baseBox" label="&originalWebsite.label;" collapsed="true"/>
+ <mail-headerfield id="expandeduser-agentBox"
+ label="&userAgentField.label;"
+ collapsed="true"/>
+ </vbox>
+
+ <vbox id="smimeBox" collapsed="true">
+ <spacer flex="1"/>
+ <image id="signedHdrIcon"
+ onclick="showMessageReadSecurityInfo();"
+ collapsed="true"/>
+ <image id="encryptedHdrIcon"
+ onclick="showMessageReadSecurityInfo();"
+ collapsed="true"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ <vbox id="expandedKeywordBox">
+ <spacer flex="1"/>
+ <image id="expandedKeywordImage"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ <vbox id="editDraftBox" class="header-part1" collapsed="true">
+ <spacer flex="1"/>
+ <button id="editDraftButton"
+ label="&editDraft.label;"
+ accesskey="&editDraft.accesskey;"
+ oncommand="MsgComposeDraftMessage(null);"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ <vbox id="editTemplateBox" class="header-part1" collapsed="true">
+ <spacer flex="1"/>
+ <button id="editTemplateButton"
+ label="&editTemplate.label;"
+ accesskey="&editTemplate.accesskey;"
+ oncommand="MsgEditTemplateMessage(null);"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ <vbox>
+ <spacer flex="1"/>
+ <image style="padding: 5px" id="fromBuddyIcon"/>
+ <spacer flex="1"/>
+ </vbox>
+
+ <vbox id="expandedAttachmentBox" class="header-part1" collapsed="true">
+ <label id="attachmentText"
+ value="&attachmentsTree.label;"
+ accesskey="&attachmentsTree.accesskey;"
+ crop="right"
+ control="attachmentList"/>
+ <listbox id="attachmentList" rows="3" seltype="multiple"
+ onclick="attachmentListClick(event);"
+ ondragstart="attachmentAreaDNDObserver.onDragStart(event);"
+ context="attachmentListContext"/>
+ </vbox>
+</hbox>
+</hbox>
+</overlay>
diff --git a/comm/suite/mailnews/content/msgMail3PaneWindow.js b/comm/suite/mailnews/content/msgMail3PaneWindow.js
new file mode 100644
index 0000000000..5cd3aa0693
--- /dev/null
+++ b/comm/suite/mailnews/content/msgMail3PaneWindow.js
@@ -0,0 +1,1265 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This is where functions related to the 3 pane window are kept */
+const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.js");
+const {msgDBCacheManager} = ChromeUtils.import("resource:///modules/msgDBCacheManager.js");
+const {PeriodicFilterManager} = ChromeUtils.import("resource:///modules/PeriodicFilterManager.jsm");
+
+// from MailNewsTypes.h
+const nsMsgKey_None = 0xFFFFFFFF;
+const nsMsgViewIndex_None = 0xFFFFFFFF;
+const kMailCheckOncePrefName = "mail.startup.enabledMailCheckOnce";
+
+var gSearchInput;
+
+var gUnreadCount = null;
+var gTotalCount = null;
+
+var gCurrentLoadingFolderURI;
+var gCurrentFolderToReroot;
+var gCurrentLoadingFolderSortType = 0;
+var gCurrentLoadingFolderSortOrder = 0;
+var gCurrentLoadingFolderViewType = 0;
+var gCurrentLoadingFolderViewFlags = 0;
+var gRerootOnFolderLoad = false;
+var gCurrentDisplayedMessage = null;
+var gNextMessageAfterDelete = null;
+var gNextMessageAfterLoad = null;
+var gNextMessageViewIndexAfterDelete = -2;
+var gCurrentlyDisplayedMessage=nsMsgViewIndex_None;
+var gStartMsgKey = nsMsgKey_None;
+var gSearchEmailAddress = null;
+var gRightMouseButtonDown = false;
+// Global var to keep track of which row in the thread pane has been selected
+// This is used to make sure that the row with the currentIndex has the selection
+// after a Delete or Move of a message that has a row index less than currentIndex.
+var gThreadPaneCurrentSelectedIndex = -1;
+// Account Wizard can exceptionally override this feature.
+var gLoadStartFolder = true;
+
+// Global var to keep track of if the 'Delete Message' or 'Move To' thread pane
+// context menu item was triggered. This helps prevent the tree view from
+// not updating on one of those menu item commands.
+var gThreadPaneDeleteOrMoveOccurred = false;
+
+//If we've loaded a message, set to true. Helps us keep the start page around.
+var gHaveLoadedMessage;
+
+var gDisplayStartupPage = false;
+
+function SelectAndScrollToKey(aMsgKey)
+{
+ // select the desired message
+ // if the key isn't found, we won't select anything
+ if (!gDBView)
+ return false;
+ gDBView.selectMsgByKey(aMsgKey);
+
+ // is there a selection?
+ // if not, bail out.
+ var indicies = GetSelectedIndices(gDBView);
+ if (!indicies || !indicies.length)
+ return false;
+
+ // now scroll to it
+ EnsureRowInThreadTreeIsVisible(indicies[0]);
+ return true;
+}
+
+// A helper routine called after a folder is loaded to make sure
+// we select and scroll to the correct message (could be the first new message,
+// could be the last displayed message, etc.)
+function ScrollToMessageAfterFolderLoad(folder)
+{
+ var scrolled = Services.prefs.getBoolPref("mailnews.scroll_to_new_message") &&
+ ScrollToMessage(nsMsgNavigationType.firstNew, true, false /* selectMessage */);
+ if (!scrolled && folder && Services.prefs.getBoolPref("mailnews.remember_selected_message"))
+ {
+ // If we failed to scroll to a new message,
+ // reselect the last selected message
+ var lastMessageLoaded = folder.lastMessageLoaded;
+ if (lastMessageLoaded != nsMsgKey_None)
+ scrolled = SelectAndScrollToKey(lastMessageLoaded);
+ }
+
+ if (!scrolled)
+ {
+ // if we still haven't scrolled,
+ // scroll to the newest, which might be the top or the bottom
+ // depending on our sort order and sort type
+ if (gDBView && gDBView.sortOrder == nsMsgViewSortOrder.ascending)
+ {
+ switch (gDBView.sortType)
+ {
+ case nsMsgViewSortType.byDate:
+ case nsMsgViewSortType.byReceived:
+ case nsMsgViewSortType.byId:
+ case nsMsgViewSortType.byThread:
+ scrolled = ScrollToMessage(nsMsgNavigationType.lastMessage, true, false /* selectMessage */);
+ break;
+ }
+ }
+
+ // if still we haven't scrolled,
+ // scroll to the top.
+ if (!scrolled)
+ EnsureRowInThreadTreeIsVisible(0);
+ }
+}
+
+// the folderListener object
+var folderListener =
+{
+ onFolderAdded: function(parentFolder, child) {},
+ onMessageAdded: function(parentFolder, msg) {},
+ onFolderRemoved: function(parentFolder, child) {},
+ onMessageRemoved: function(parentFolder, msg) {},
+
+ onFolderPropertyChanged: function(item, property, oldValue, newValue) {},
+ onFolderBoolPropertyChanged: function(item, property, oldValue, newValue) {},
+ onFolderUnicharPropertyChanged: function(item, property, oldValue, newValue) {},
+ onFolderPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
+
+ onFolderIntPropertyChanged: function(item, property, oldValue, newValue)
+ {
+ // handle the currently visible folder
+ if (item == gMsgFolderSelected)
+ {
+ if (property == "TotalMessages" || property == "TotalUnreadMessages")
+ {
+ UpdateStatusMessageCounts(gMsgFolderSelected);
+ }
+ }
+
+ // check folders shown in tabs
+ if (item instanceof Ci.nsIMsgFolder)
+ {
+ // find corresponding tabinfos
+ // we may have the folder openened in more than one tab
+ let tabmail = GetTabMail();
+ for (let i = 0; i < tabmail.tabInfo.length; ++i)
+ {
+ // if we never switched away from the tab, we only have just one
+ let tabFolder = tabmail.tabInfo[i].msgSelectedFolder || gMsgFolderSelected;
+ if (tabFolder == item)
+ {
+ // update tab title incl. any icon styles
+ tabmail.setTabTitle(tabmail.tabInfo[i]);
+ }
+ }
+ }
+ },
+
+ onFolderEvent: function(folder, event) {
+ if (event == "FolderLoaded") {
+ if (folder) {
+ var scrolled = false;
+ var msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
+ var uri = folder.URI;
+ var rerootingFolder = (uri == gCurrentFolderToReroot);
+ if (rerootingFolder) {
+ viewDebug("uri = gCurrentFolderToReroot, setting gQSViewIsDirty\n");
+ gQSViewIsDirty = true;
+ gCurrentFolderToReroot = null;
+ if (msgFolder) {
+ msgFolder.endFolderLoading();
+ // Suppress command updating when rerooting the folder.
+ // When rerooting, we'll be clearing the selection
+ // which will cause us to update commands.
+ if (gDBView) {
+ gDBView.suppressCommandUpdating = true;
+ // If the db's view isn't set, something went wrong and we
+ // should reroot the folder, which will re-open the view.
+ if (!gDBView.db)
+ gRerootOnFolderLoad = true;
+ }
+ if (gRerootOnFolderLoad)
+ RerootFolder(uri, msgFolder, gCurrentLoadingFolderViewType, gCurrentLoadingFolderViewFlags, gCurrentLoadingFolderSortType, gCurrentLoadingFolderSortOrder);
+
+ if (gDBView)
+ gDBView.suppressCommandUpdating = false;
+
+ gCurrentLoadingFolderSortType = 0;
+ gCurrentLoadingFolderSortOrder = 0;
+ gCurrentLoadingFolderViewType = 0;
+ gCurrentLoadingFolderViewFlags = 0;
+
+ // Used for rename folder msg loading after folder is loaded.
+ scrolled = LoadCurrentlyDisplayedMessage();
+
+ if (gStartMsgKey != nsMsgKey_None) {
+ scrolled = SelectAndScrollToKey(gStartMsgKey);
+ gStartMsgKey = nsMsgKey_None;
+ }
+
+ if (gNextMessageAfterLoad) {
+ var type = gNextMessageAfterLoad;
+ gNextMessageAfterLoad = null;
+
+ // Scroll to and select the proper message.
+ scrolled = ScrollToMessage(type, true, true /* selectMessage */);
+ }
+ }
+ }
+ if (uri == gCurrentLoadingFolderURI) {
+ viewDebug("uri == current loading folder uri\n");
+ gCurrentLoadingFolderURI = "";
+ // Scroll to message for virtual folders is done in
+ // gSearchNotificationListener.OnSearchDone (see searchBar.js).
+ if (!scrolled && gMsgFolderSelected &&
+ !(gMsgFolderSelected.flags & Ci.nsMsgFolderFlags.Virtual))
+ ScrollToMessageAfterFolderLoad(msgFolder);
+ SetBusyCursor(window, false);
+ }
+ // Folder loading is over,
+ // now issue quick search if there is an email address.
+ if (gVirtualFolderTerms)
+ viewDebug("in folder loaded gVirtualFolderTerms = " +
+ gVirtualFolderTerms + "\n");
+ if (gMsgFolderSelected)
+ viewDebug("in folder loaded gMsgFolderSelected = " +
+ gMsgFolderSelected.URI + "\n");
+ if (rerootingFolder)
+ {
+ if (gSearchEmailAddress)
+ {
+ Search(gSearchEmailAddress);
+ gSearchEmailAddress = null;
+ }
+ else if (gVirtualFolderTerms)
+ {
+ gDefaultSearchViewTerms = null;
+ viewDebug("searching gVirtualFolderTerms\n");
+ gDBView.viewFolder = gMsgFolderSelected;
+ ViewChangeByFolder(gMsgFolderSelected);
+ }
+ else if (gMsgFolderSelected &&
+ gMsgFolderSelected.flags & Ci.nsMsgFolderFlags.Virtual)
+ {
+ viewDebug("selected folder is virtual\n");
+ gDefaultSearchViewTerms = null;
+ }
+ else
+ {
+ // Get the view value from the folder.
+ if (msgFolder)
+ {
+ // If our new view is the same as the old view and we already
+ // have the list of search terms built up for the old view,
+ // just re-use it.
+ var result = GetMailViewForFolder(msgFolder);
+ if (GetSearchInput() && gCurrentViewValue == result && gDefaultSearchViewTerms)
+ {
+ viewDebug("searching gDefaultSearchViewTerms and rerootingFolder\n");
+ Search("");
+ }
+ else
+ {
+ viewDebug("changing view by value\n");
+ ViewChangeByValue(result);
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (event == "ImapHdrDownloaded") {
+ if (folder) {
+ var imapFolder = folder.QueryInterface(Ci.nsIMsgImapMailFolder);
+ if (imapFolder) {
+ var hdrParser = imapFolder.hdrParser;
+ if (hdrParser) {
+ var msgHdr = hdrParser.GetNewMsgHdr();
+ if (msgHdr)
+ {
+ var hdrs = hdrParser.headers;
+ if (hdrs && hdrs.includes("X-attachment-size:")) {
+ msgHdr.OrFlags(Ci.nsMsgMessageFlags
+ .Attachment);
+ }
+ if (hdrs && hdrs.includes("X-image-size:")) {
+ msgHdr.setStringProperty("imageSize", "1");
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (event == "DeleteOrMoveMsgCompleted") {
+ HandleDeleteOrMoveMsgCompleted(folder);
+ }
+ else if (event == "DeleteOrMoveMsgFailed") {
+ HandleDeleteOrMoveMsgFailed(folder);
+ }
+ else if (event == "AboutToCompact") {
+ if (gDBView)
+ gCurrentlyDisplayedMessage = gDBView.currentlyDisplayedMessage;
+ }
+ else if (event == "CompactCompleted") {
+ HandleCompactCompleted(folder);
+ }
+ else if (event == "RenameCompleted") {
+ // Clear this so we don't try to clear its new messages.
+ gMsgFolderSelected = null;
+ gFolderTreeView.selectFolder(folder);
+ }
+ else if (event == "JunkStatusChanged") {
+ HandleJunkStatusChanged(folder);
+ }
+ }
+}
+
+function HandleDeleteOrMoveMsgFailed(folder)
+{
+ gDBView.onDeleteCompleted(false);
+ if(IsCurrentLoadedFolder(folder)) {
+ if(gNextMessageAfterDelete) {
+ gNextMessageAfterDelete = null;
+ gNextMessageViewIndexAfterDelete = -2;
+ }
+ }
+
+ // fix me???
+ // ThreadPaneSelectionChange(true);
+}
+
+// WARNING
+// this is a fragile and complicated function.
+// be careful when hacking on it.
+// Don't forget about things like different imap
+// delete models, multiple views (from multiple thread panes,
+// search windows, stand alone message windows)
+function HandleDeleteOrMoveMsgCompleted(folder)
+{
+ // you might not have a db view. this can happen if
+ // biff fires when the 3 pane is set to account central.
+ if (!gDBView)
+ return;
+
+ gDBView.onDeleteCompleted(true);
+
+ if (!IsCurrentLoadedFolder(folder)) {
+ // default value after delete/move/copy is over
+ gNextMessageViewIndexAfterDelete = -2;
+ return;
+ }
+
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ var treeSelection = treeView.selection;
+
+ if (gNextMessageViewIndexAfterDelete == -2) {
+ // a move or delete can cause our selection can change underneath us.
+ // this can happen when the user
+ // deletes message from the stand alone msg window
+ // or the search view, or another 3 pane
+ if (treeSelection.count == 0) {
+ // this can happen if you double clicked a message
+ // in the thread pane, and deleted it from the stand alone msg window
+ // see bug #172392
+ treeSelection.clearSelection();
+ setTitleFromFolder(folder, null);
+ ClearMessagePane();
+ UpdateMailToolbar("delete from another view, 0 rows now selected");
+ }
+ else if (treeSelection.count == 1) {
+ // this can happen if you had two messages selected
+ // in the thread pane, and you deleted one of them from another view
+ // (like the view in the stand alone msg window)
+ // since one item is selected, we should load it.
+ var startIndex = {};
+ var endIndex = {};
+ treeSelection.getRangeAt(0, startIndex, endIndex);
+
+ // select the selected item, so we'll load it
+ treeSelection.select(startIndex.value);
+ treeView.selectionChanged();
+
+ EnsureRowInThreadTreeIsVisible(startIndex.value);
+
+ UpdateMailToolbar("delete from another view, 1 row now selected");
+ }
+ else {
+ // this can happen if you have more than 2 messages selected
+ // in the thread pane, and you deleted one of them from another view
+ // (like the view in the stand alone msg window)
+ // since multiple messages are still selected, do nothing.
+ }
+ }
+ else {
+ if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None)
+ {
+ var viewSize = treeView.rowCount;
+ if (gNextMessageViewIndexAfterDelete >= viewSize)
+ {
+ if (viewSize > 0)
+ gNextMessageViewIndexAfterDelete = viewSize - 1;
+ else
+ {
+ gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None;
+
+ // there is nothing to select since viewSize is 0
+ treeSelection.clearSelection();
+ setTitleFromFolder(folder, null);
+ ClearMessagePane();
+ UpdateMailToolbar("delete from current view, 0 rows left");
+ }
+ }
+ }
+
+ // if we are about to set the selection with a new element then DON'T clear
+ // the selection then add the next message to select. This just generates
+ // an extra round of command updating notifications that we are trying to
+ // optimize away.
+ if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None)
+ {
+ // When deleting a message we don't update the commands
+ // when the selection goes to 0
+ // (we have a hack in nsMsgDBView which prevents that update)
+ // so there is no need to
+ // update commands when we select the next message after the delete;
+ // the commands already
+ // have the right update state...
+ gDBView.suppressCommandUpdating = true;
+
+ // This check makes sure that the tree does not perform a
+ // selection on a non selected row (row < 0), else assertions will
+ // be thrown.
+ if (gNextMessageViewIndexAfterDelete >= 0)
+ treeSelection.select(gNextMessageViewIndexAfterDelete);
+
+ // If gNextMessageViewIndexAfterDelete has the same value
+ // as the last index we had selected, the tree won't generate a
+ // selectionChanged notification for the tree view. So force a manual
+ // selection changed call.
+ // (don't worry it's cheap if we end up calling it twice).
+ if (treeView)
+ treeView.selectionChanged();
+
+ EnsureRowInThreadTreeIsVisible(gNextMessageViewIndexAfterDelete);
+ gDBView.suppressCommandUpdating = false;
+
+ // hook for extra toolbar items
+ // XXX TODO
+ // I think there is a bug in the suppression code above.
+ // What if I have two rows selected, and I hit delete,
+ // and so we load the next row.
+ // What if I have commands that only enable where
+ // exactly one row is selected?
+ UpdateMailToolbar("delete from current view, at least one row selected");
+ }
+ }
+
+ // default value after delete/move/copy is over
+ gNextMessageViewIndexAfterDelete = -2;
+}
+
+function HandleCompactCompleted(folder)
+{
+ if (folder && folder.server.type != "imap")
+ {
+ let msgFolder = msgWindow.openFolder;
+ if (msgFolder && folder.URI == msgFolder.URI)
+ {
+ // pretend the selection changed, to reselect the current folder+view.
+ gMsgFolderSelected = null;
+ msgWindow.openFolder = null;
+ FolderPaneSelectionChange();
+ LoadCurrentlyDisplayedMessage();
+ }
+ }
+}
+
+function LoadCurrentlyDisplayedMessage()
+{
+ var scrolled = (gCurrentlyDisplayedMessage != nsMsgViewIndex_None);
+ if (scrolled)
+ {
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ var treeSelection = treeView.selection;
+ treeSelection.select(gCurrentlyDisplayedMessage);
+ if (treeView)
+ treeView.selectionChanged();
+ EnsureRowInThreadTreeIsVisible(gCurrentlyDisplayedMessage);
+ SetFocusThreadPane();
+ gCurrentlyDisplayedMessage = nsMsgViewIndex_None; //reset
+ }
+ return scrolled;
+}
+
+function IsCurrentLoadedFolder(aFolder)
+{
+ let msgFolderUri = aFolder.QueryInterface(Ci.nsIMsgFolder)
+ .URI;
+ let currentLoadedFolder = GetThreadPaneFolder();
+
+ // If the currently loaded folder is virtual,
+ // check if aFolder is one of its searched folders.
+ if (currentLoadedFolder.flags & Ci.nsMsgFolderFlags.Virtual)
+ {
+ return currentLoadedFolder.msgDatabase.dBFolderInfo
+ .getCharProperty("searchFolderUri").split("|")
+ .includes(msgFolderUri);
+ }
+
+ // Is aFolder the currently loaded folder?
+ return currentLoadedFolder.URI == msgFolderUri;
+}
+
+function ServerContainsFolder(server, folder)
+{
+ if (!folder || !server)
+ return false;
+
+ return server.equals(folder.server);
+}
+
+function SelectServer(server)
+{
+ gFolderTreeView.selectFolder(server.rootFolder);
+}
+
+// we have this incoming server listener in case we need to
+// alter the folder pane selection when a server is removed
+// or changed (currently, when the real username or real hostname change)
+var gThreePaneIncomingServerListener = {
+ onServerLoaded: function(server) {},
+ onServerUnloaded: function(server) {
+ var selectedFolders = GetSelectedMsgFolders();
+ for (var i = 0; i < selectedFolders.length; i++) {
+ if (ServerContainsFolder(server, selectedFolders[i])) {
+ if (accountManager.defaultAccount)
+ SelectServer(accountManager.defaultAccount.incomingServer);
+ // we've made a new selection, we're done
+ return;
+ }
+ }
+
+ // if nothing is selected at this point, better go select the default
+ // this could happen if nothing was selected when the server was removed
+ selectedFolders = GetSelectedMsgFolders();
+ if (selectedFolders.length == 0) {
+ if (accountManager.defaultAccount)
+ SelectServer(accountManager.defaultAccount.incomingServer);
+ }
+ },
+ onServerChanged: function(server) {
+ // if the current selected folder is on the server that changed
+ // and that server is an imap or news server,
+ // we need to update the selection.
+ // on those server types, we'll be reconnecting to the server
+ // and our currently selected folder will need to be reloaded
+ // or worse, be invalid.
+ if (server.type != "imap" && server.type !="nntp")
+ return;
+
+ var selectedFolders = GetSelectedMsgFolders();
+ for (var i = 0; i < selectedFolders.length; i++) {
+ // if the selected item is a server, we don't have to update
+ // the selection
+ if (!(selectedFolders[i].isServer) && ServerContainsFolder(server, selectedFolders[i])) {
+ SelectServer(server);
+ // we've made a new selection, we're done
+ return;
+ }
+ }
+ }
+}
+
+function UpdateMailPaneConfig() {
+ const dynamicIds = ["messagesBox", "mailContent", "messengerBox"];
+ var desiredId = dynamicIds[Services.prefs.getIntPref("mail.pane_config.dynamic")];
+ var messagePane = GetMessagePane();
+ if (messagePane.parentNode.id != desiredId) {
+ ClearAttachmentList();
+ var messagePaneSplitter = GetThreadAndMessagePaneSplitter();
+ var desiredParent = document.getElementById(desiredId);
+ // See Bug 381992. The ctor for the browser element will fire again when we
+ // re-insert the messagePaneBox back into the document.
+ // But the dtor doesn't fire when the element is removed from the document.
+ // Manually call destroy here to avoid a nasty leak.
+ getMessageBrowser().destroy();
+ desiredParent.appendChild(messagePaneSplitter);
+ desiredParent.appendChild(messagePane);
+ messagePaneSplitter.orient = desiredParent.orient;
+ // Reroot message display
+ InvalidateTabDBs();
+ let tabmail = GetTabMail();
+ tabmail.currentTabInfo = null;
+ tabmail.updateCurrentTab();
+ }
+}
+
+var MailPrefObserver = {
+ observe: function observe(subject, topic, prefName) {
+ if (topic == "nsPref:changed") {
+ if (prefName == "mail.pane_config.dynamic") {
+ UpdateMailPaneConfig();
+ } else if (prefName == "mail.showCondensedAddresses") {
+ let currentDisplayNameVersion =
+ Services.prefs.getIntPref("mail.displayname.version");
+ Services.prefs.setIntPref("mail.displayname.version",
+ ++currentDisplayNameVersion);
+
+ // Refresh the thread pane.
+ GetThreadTree().treeBoxObject.invalid();
+ }
+ }
+ }
+};
+
+/* Functions related to startup */
+function OnLoadMessenger()
+{
+ AddMailOfflineObserver();
+ CreateMailWindowGlobals();
+ Services.prefs.addObserver("mail.pane_config.dynamic", MailPrefObserver);
+ Services.prefs.addObserver("mail.showCondensedAddresses", MailPrefObserver);
+ UpdateMailPaneConfig();
+ Create3PaneGlobals();
+ verifyAccounts(null, false);
+ msgDBCacheManager.init();
+
+ // set the messenger default window size relative to the screen size
+ // initial default dimensions are 2/3 and 1/2 of the screen dimensions
+ if (!document.documentElement.hasAttribute("width")) {
+ let screenHeight = window.screen.availHeight;
+ let screenWidth = window.screen.availWidth;
+ let defaultHeight = Math.floor(screenHeight * 2 / 3);
+ let defaultWidth = Math.floor(screenWidth / 2);
+
+ // minimum dimensions are 1024x768 less padding unless restrained by screen
+ const minHeight = 768;
+ const minWidth = 1024;
+
+ if (defaultHeight < minHeight)
+ defaultHeight = Math.min(minHeight, screenHeight);
+ if (defaultWidth < minWidth)
+ defaultWidth = Math.min(minWidth, screenWidth);
+
+ // keep some distance to the borders, accounting for window decoration
+ document.documentElement.setAttribute("height", defaultHeight - 48);
+ document.documentElement.setAttribute("width", defaultWidth - 24);
+ }
+
+ // initialize tabmail system - see tabmail.js and tabmail.xml for details
+ let tabmail = GetTabMail();
+ tabmail.registerTabType(gMailNewsTabsType);
+ tabmail.openFirstTab();
+ Services.obs.addObserver(MailWindowIsClosing,
+ "quit-application-requested");
+
+ InitMsgWindow();
+ messenger.setWindow(window, msgWindow);
+
+ InitPanes();
+
+ MigrateJunkMailSettings();
+
+ accountManager.setSpecialFolders();
+ accountManager.loadVirtualFolders();
+ accountManager.addIncomingServerListener(gThreePaneIncomingServerListener);
+
+ AddToSession();
+
+ var startFolderUri = null;
+ //need to add to session before trying to load start folder otherwise listeners aren't
+ //set up correctly.
+ // argument[0] --> folder uri
+ // argument[1] --> optional message key
+ // argument[2] --> optional email address; // Will come from aim; needs to show msgs from buddy's email address.
+ if ("arguments" in window)
+ {
+ var args = window.arguments;
+ // filter our any feed urls that came in as arguments to the new window...
+ if (args.length && /^feed:/i.test(args[0]))
+ {
+ var feedHandler =
+ Cc["@mozilla.org/newsblog-feed-downloader;1"]
+ .getService(Ci.nsINewsBlogFeedDownloader);
+ if (feedHandler)
+ feedHandler.subscribeToFeed(args[0], null, msgWindow);
+ }
+ else
+ {
+ startFolderUri = (args.length > 0) ? args[0] : null;
+ }
+ gStartMsgKey = (args.length > 1) ? args[1] : nsMsgKey_None;
+ gSearchEmailAddress = (args.length > 2) ? args[2] : null;
+ }
+
+ window.setTimeout(loadStartFolder, 0, startFolderUri);
+
+ Services.obs.notifyObservers(window, "mail-startup-done");
+
+ // FIX ME - later we will be able to use onload from the overlay
+ OnLoadMsgHeaderPane();
+
+ gHaveLoadedMessage = false;
+
+ //Set focus to the Thread Pane the first time the window is opened.
+ SetFocusThreadPane();
+
+ // Before and after callbacks for the customizeToolbar code
+ var mailToolbox = getMailToolbox();
+ mailToolbox.customizeInit = MailToolboxCustomizeInit;
+ mailToolbox.customizeDone = MailToolboxCustomizeDone;
+ mailToolbox.customizeChange = MailToolboxCustomizeChange;
+
+ // initialize the sync UI
+ // gSyncUI.init();
+
+ window.addEventListener("AppCommand", HandleAppCommandEvent, true);
+
+ // Load the periodic filter timer.
+ PeriodicFilterManager.setupFiltering();
+}
+
+function HandleAppCommandEvent(evt)
+{
+ evt.stopPropagation();
+ switch (evt.command)
+ {
+ case "Back":
+ goDoCommand('cmd_goBack');
+ break;
+ case "Forward":
+ goDoCommand('cmd_goForward');
+ break;
+ case "Stop":
+ goDoCommand('cmd_stop');
+ break;
+ case "Search":
+ goDoCommand('cmd_search');
+ break;
+ case "Bookmarks":
+ toAddressBook();
+ break;
+ case "Reload":
+ goDoCommand('cmd_reload');
+ break;
+ case "Home":
+ goDoCommand('cmd_goStartPage');
+ break;
+ default:
+ break;
+ }
+}
+
+function OnUnloadMessenger()
+{
+ Services.prefs.removeObserver("mail.pane_config.dynamic", MailPrefObserver, false);
+ Services.prefs.removeObserver("mail.showCondensedAddresses", MailPrefObserver, false);
+ window.removeEventListener("AppCommand", HandleAppCommandEvent, true);
+ Services.obs.removeObserver(MailWindowIsClosing,
+ "quit-application-requested");
+
+ OnLeavingFolder(gMsgFolderSelected); // mark all read in current folder
+ accountManager.removeIncomingServerListener(gThreePaneIncomingServerListener);
+ GetTabMail().closeTabs();
+
+ // FIX ME - later we will be able to use onload from the overlay
+ OnUnloadMsgHeaderPane();
+ UnloadPanes();
+ OnMailWindowUnload();
+}
+
+// we probably want to warn if more than one tab is closed
+function MailWindowIsClosing(aCancelQuit, aTopic, aData)
+{
+ if (aTopic == "quit-application-requested" &&
+ aCancelQuit instanceof Ci.nsISupportsPRBool &&
+ aCancelQuit.data)
+ return false;
+
+ let tabmail = GetTabMail();
+ let reallyClose = tabmail.warnAboutClosingTabs(tabmail.closingTabsEnum.ALL);
+
+ if (!reallyClose && aTopic == "quit-application-requested")
+ aCancelQuit.data = true;
+
+ return reallyClose;
+}
+
+function Create3PaneGlobals()
+{
+ // Update <mailWindow.js> global variables.
+ accountCentralBox = document.getElementById("accountCentralBox");
+ gDisableViewsSearch = document.getElementById("mailDisableViewsSearch");
+
+ GetMessagePane().collapsed = true;
+}
+
+function loadStartFolder(initialUri)
+{
+ var defaultServer = null;
+ var startFolder;
+ var isLoginAtStartUpEnabled = false;
+
+ //First get default account
+ if (initialUri) {
+ startFolder = MailUtils.getFolderForURI(initialUri);
+ } else {
+ var defaultAccount = accountManager.defaultAccount;
+ if (defaultAccount) {
+ defaultServer = defaultAccount.incomingServer;
+ var rootMsgFolder = defaultServer.rootMsgFolder;
+
+ startFolder = rootMsgFolder;
+ // Enable check new mail once by turning checkmail pref 'on' to bring
+ // all users to one plane. This allows all users to go to Inbox. User can
+ // always go to server settings panel and turn off "Check for new mail at startup"
+ if (!Services.prefs.getBoolPref(kMailCheckOncePrefName))
+ {
+ Services.prefs.setBoolPref(kMailCheckOncePrefName, true);
+ defaultServer.loginAtStartUp = true;
+ }
+
+ // Get the user pref to see if the login at startup is enabled for default account
+ isLoginAtStartUpEnabled = defaultServer.loginAtStartUp;
+
+ // Get Inbox only if login at startup is enabled.
+ if (isLoginAtStartUpEnabled)
+ {
+ //now find Inbox
+ const kInboxFlag = Ci.nsMsgFolderFlags.Inbox;
+ var inboxFolder = rootMsgFolder.getFolderWithFlags(kInboxFlag);
+ if (inboxFolder)
+ startFolder = inboxFolder;
+ }
+ } else {
+ // If no default account then show account central page.
+ ShowAccountCentral();
+ }
+
+ }
+
+ if (startFolder) {
+ try {
+ gFolderTreeView.selectFolder(startFolder);
+ } catch(ex) {
+ // This means we tried to select a folder that isn't in the current
+ // view. Just select the first one in the view then.
+ if (gFolderTreeView._rowMap.length)
+ gFolderTreeView.selectFolder(gFolderTreeView._rowMap[0]._folder);
+ }
+
+ // Perform biff on the server to check for new mail, if:
+ // the login at startup is enabled, and
+ // this feature is not exceptionally overridden, and
+ // the account is not deferred-to or deferred.
+ if (isLoginAtStartUpEnabled &&
+ gLoadStartFolder &&
+ !defaultServer.isDeferredTo &&
+ defaultServer.rootFolder == defaultServer.rootMsgFolder)
+ defaultServer.performBiff(msgWindow);
+ }
+
+ MsgGetMessagesForAllServers(defaultServer);
+
+ if (CheckForUnsentMessages() && !Services.io.offline)
+ {
+ InitPrompts();
+ InitServices();
+
+ var sendUnsentWhenGoingOnlinePref = Services.prefs.getIntPref("offline.send.unsent_messages");
+ if (sendUnsentWhenGoingOnlinePref == 0) // pref is "ask"
+ {
+ var buttonPressed = Services.prompt.confirmEx(window,
+ gOfflinePromptsBundle.getString('sendMessagesOfflineWindowTitle'),
+ gOfflinePromptsBundle.getString('sendMessagesLabel2'),
+ Services.prompt.BUTTON_TITLE_IS_STRING * (Services.prompt.BUTTON_POS_0 +
+ Services.prompt.BUTTON_POS_1),
+ gOfflinePromptsBundle.getString('sendMessagesSendButtonLabel'),
+ gOfflinePromptsBundle.getString('sendMessagesNoSendButtonLabel'),
+ null, null, {value:0});
+ if (buttonPressed == 0)
+ SendUnsentMessages();
+ }
+ else if(sendUnsentWhenGoingOnlinePref == 1) // pref is "yes"
+ SendUnsentMessages();
+ }
+}
+
+function AddToSession()
+{
+ var nsIFolderListener = Ci.nsIFolderListener;
+ var notifyFlags = nsIFolderListener.intPropertyChanged |
+ nsIFolderListener.event;
+ MailServices.mailSession.AddFolderListener(folderListener, notifyFlags);
+}
+
+function InitPanes()
+{
+ gFolderTreeView.load(document.getElementById("folderTree"),
+ "folderTree.json");
+ var folderTree = document.getElementById("folderTree");
+ folderTree.addEventListener("click", FolderPaneOnClick, true);
+ folderTree.addEventListener("mousedown", TreeOnMouseDown, true);
+
+ OnLoadThreadPane();
+ SetupCommandUpdateHandlers();
+}
+
+function UnloadPanes()
+{
+ var folderTree = document.getElementById("folderTree");
+ folderTree.removeEventListener("click", FolderPaneOnClick, true);
+ folderTree.removeEventListener("mousedown", TreeOnMouseDown, true);
+ gFolderTreeView.unload("folderTree.json");
+ UnloadCommandUpdateHandlers();
+}
+
+function AddMutationObserver(callback)
+{
+ new MutationObserver(callback).observe(callback(), {attributes: true, attributeFilter: ["hidden"]});
+}
+
+function OnLoadThreadPane()
+{
+ AddMutationObserver(UpdateAttachmentCol);
+}
+
+function UpdateAttachmentCol()
+{
+ var attachmentCol = document.getElementById("attachmentCol");
+ var threadTree = GetThreadTree();
+ threadTree.setAttribute("noattachcol", attachmentCol.getAttribute("hidden"));
+ threadTree.treeBoxObject.clearStyleAndImageCaches();
+ return attachmentCol;
+}
+
+function GetSearchInput()
+{
+ if (!gSearchInput)
+ gSearchInput = document.getElementById("searchInput");
+ return gSearchInput;
+}
+
+function GetMessagePaneFrame()
+{
+ return window.content;
+}
+
+function FindInSidebar(currentWindow, id)
+{
+ var item = currentWindow.document.getElementById(id);
+ if (item)
+ return item;
+
+ for (var i = 0; i < currentWindow.frames.length; ++i)
+ {
+ var frameItem = FindInSidebar(currentWindow.frames[i], id);
+ if (frameItem)
+ return frameItem;
+ }
+
+ return null;
+}
+
+function GetUnreadCountElement()
+{
+ if (!gUnreadCount)
+ gUnreadCount = document.getElementById('unreadMessageCount');
+ return gUnreadCount;
+}
+
+function GetTotalCountElement()
+{
+ if (!gTotalCount)
+ gTotalCount = document.getElementById('totalMessageCount');
+ return gTotalCount;
+}
+
+function ClearThreadPaneSelection()
+{
+ try {
+ if (gDBView) {
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ var treeSelection = treeView.selection;
+ if (treeSelection)
+ treeSelection.clearSelection();
+ }
+ }
+ catch (ex) {
+ dump("ClearThreadPaneSelection: ex = " + ex + "\n");
+ }
+}
+
+function ClearMessagePane()
+{
+ if (gHaveLoadedMessage)
+ {
+ gHaveLoadedMessage = false;
+ gCurrentDisplayedMessage = null;
+ if (GetMessagePaneFrame().location.href != "about:blank")
+ GetMessagePaneFrame().location.href = "about:blank";
+
+ // hide the message header view AND the message pane...
+ HideMessageHeaderPane();
+ gMessageNotificationBar.clearMsgNotifications();
+ ClearPendingReadTimer();
+ }
+}
+
+// Function to change the highlighted row to where the mouse was clicked
+// without loading the contents of the selected row.
+// It will also keep the outline/dotted line in the original row.
+function ChangeSelectionWithoutContentLoad(event, tree)
+{
+ // usually, we're only interested in tree content clicks, not scrollbars etc.
+ if (event.originalTarget.localName != "treechildren")
+ return;
+
+ var treeBoxObj = tree.treeBoxObject;
+ var treeSelection = tree.view.selection;
+
+ var row = treeBoxObj.getRowAt(event.clientX, event.clientY);
+ // make sure that row.value is valid so that it doesn't mess up
+ // the call to ensureRowIsVisible().
+ if ((row >= 0) && !treeSelection.isSelected(row))
+ {
+ var saveCurrentIndex = treeSelection.currentIndex;
+ treeSelection.selectEventsSuppressed = true;
+ treeSelection.select(row);
+ treeSelection.currentIndex = saveCurrentIndex;
+ treeBoxObj.ensureRowIsVisible(row);
+ treeSelection.selectEventsSuppressed = false;
+
+ // Keep track of which row in the thread pane is currently selected.
+ if (tree.id == "threadTree")
+ gThreadPaneCurrentSelectedIndex = row;
+ }
+ event.stopPropagation();
+}
+
+function TreeOnMouseDown(event)
+{
+ // Detect right mouse click and change the highlight to the row
+ // where the click happened without loading the message headers in
+ // the Folder or Thread Pane.
+ // Same for middle click, which will open the folder/message in a tab.
+ gRightMouseButtonDown = event.button == kMouseButtonRight;
+ if (!gRightMouseButtonDown)
+ gRightMouseButtonDown = AllowOpenTabOnMiddleClick() &&
+ event.button == kMouseButtonMiddle;
+ if (gRightMouseButtonDown)
+ ChangeSelectionWithoutContentLoad(event, event.target.parentNode);
+}
+
+function FolderPaneContextMenuNewTab(event) {
+ var bgLoad = Services.prefs.getBoolPref("mail.tabs.loadInBackground");
+ if (event.shiftKey)
+ bgLoad = !bgLoad;
+ MsgOpenNewTabForFolder(bgLoad);
+}
+
+function FolderPaneOnClick(event)
+{
+ // usually, we're only interested in tree content clicks, not scrollbars etc.
+ if (event.originalTarget.localName != "treechildren")
+ return;
+
+ var folderTree = document.getElementById("folderTree");
+
+ // we may want to open the folder in a new tab on middle click
+ if (event.button == kMouseButtonMiddle)
+ {
+ if (AllowOpenTabOnMiddleClick())
+ {
+ FolderPaneContextMenuNewTab(event);
+ RestoreSelectionWithoutContentLoad(folderTree);
+ return;
+ }
+ }
+
+ // otherwise, we only care about left click events
+ if (event.button != kMouseButtonLeft)
+ return;
+
+ var cell = folderTree.treeBoxObject.getCellAt(event.clientX, event.clientY);
+ if (cell.row == -1)
+ {
+ if (event.originalTarget.localName == "treecol")
+ {
+ // clicking on the name column in the folder pane should not sort
+ event.stopPropagation();
+ }
+ }
+}
+
+function OpenMessageInNewTab(event) {
+ var bgLoad = Services.prefs.getBoolPref("mail.tabs.loadInBackground");
+ if (event.shiftKey)
+ bgLoad = !bgLoad;
+
+ MsgOpenNewTabForMessage(bgLoad);
+}
+
+function GetSelectedMsgFolders()
+{
+ return gFolderTreeView.getSelectedFolders();
+}
+
+function GetSelectedIndices(dbView)
+{
+ try {
+ return dbView.getIndicesForSelection();
+ }
+ catch (ex) {
+ dump("ex = " + ex + "\n");
+ return null;
+ }
+}
+
+function GetLoadedMsgFolder()
+{
+ if (!gDBView) return null;
+ return gDBView.msgFolder;
+}
+
+function GetLoadedMessage()
+{
+ try {
+ return gDBView.URIForFirstSelectedMessage;
+ }
+ catch (ex) {
+ return null;
+ }
+}
+
+//Clear everything related to the current message. called after load start page.
+function ClearMessageSelection()
+{
+ ClearThreadPaneSelection();
+}
+
+// Figures out how many messages are selected (hilighted - does not necessarily
+// have the dotted outline) above a given index row value in the thread pane.
+function NumberOfSelectedMessagesAboveCurrentIndex(index)
+{
+ var numberOfMessages = 0;
+ var indicies = GetSelectedIndices(gDBView);
+
+ if (indicies && indicies.length)
+ {
+ for (var i = 0; i < indicies.length; i++)
+ {
+ if (indicies[i] < index)
+ ++numberOfMessages;
+ else
+ break;
+ }
+ }
+ return numberOfMessages;
+}
+
+function SetNextMessageAfterDelete()
+{
+ var treeSelection = GetThreadTree().view.selection;
+
+ if (treeSelection.isSelected(treeSelection.currentIndex))
+ gNextMessageViewIndexAfterDelete = gDBView.msgToSelectAfterDelete;
+ else if(gDBView.removeRowOnMoveOrDelete)
+ {
+ // Only set gThreadPaneDeleteOrMoveOccurred to true if the message was
+ // truly moved to the trash or deleted, as opposed to an IMAP delete
+ // (where it is only "marked as deleted". This will prevent bug 142065.
+ //
+ // If it's an IMAP delete, then just set gNextMessageViewIndexAfterDelete
+ // to treeSelection.currentIndex (where the outline is at) because nothing
+ // was moved or deleted from the folder.
+ gThreadPaneDeleteOrMoveOccurred = true;
+ gNextMessageViewIndexAfterDelete = treeSelection.currentIndex - NumberOfSelectedMessagesAboveCurrentIndex(treeSelection.currentIndex);
+ }
+ else
+ gNextMessageViewIndexAfterDelete = treeSelection.currentIndex;
+}
+
+function EnsureFolderIndex(treeView, msgFolder) {
+ // Try to get the index of the folder in the tree.
+ let index = treeView.getIndexOfFolder(msgFolder);
+ if (!index) {
+ // If we couldn't find the folder, open the parents.
+ let folder = msgFolder;
+ while (!index && folder) {
+ folder = folder.parent;
+ index = EnsureFolderIndex(treeView, folder);
+ }
+ if (index) {
+ treeView.toggleOpenState(index);
+ index = treeView.getIndexOfFolder(msgFolder);
+ }
+ }
+ return index;
+}
+
+function SelectMsgFolder(msgFolder) {
+ gFolderTreeView.selectFolder(msgFolder);
+}
+
+function SelectMessage(messageUri)
+{
+ var msgHdr = messenger.msgHdrFromURI(messageUri);
+ if (msgHdr)
+ gDBView.selectMsgByKey(msgHdr.messageKey);
+}
+
+function ReloadMessage()
+{
+ gDBView.reloadMessage();
+}
+
+function SetBusyCursor(window, enable)
+{
+ // setCursor() is only available for chrome windows.
+ // However one of our frames is the start page which
+ // is a non-chrome window, so check if this window has a
+ // setCursor method
+ if ("setCursor" in window) {
+ if (enable)
+ window.setCursor("progress");
+ else
+ window.setCursor("auto");
+ }
+
+ var numFrames = window.frames.length;
+ for(var i = 0; i < numFrames; i++)
+ SetBusyCursor(window.frames[i], enable);
+}
+
+function GetDBView()
+{
+ return gDBView;
+}
+
+// Some of the per account junk mail settings have been
+// converted to global prefs. Let's try to migrate some
+// of those settings from the default account.
+function MigrateJunkMailSettings()
+{
+ var junkMailSettingsVersion = Services.prefs.getIntPref("mail.spam.version");
+ if (!junkMailSettingsVersion)
+ {
+ // Get the default account, check to see if we have values for our
+ // globally migrated prefs.
+ var defaultAccount = accountManager.defaultAccount;
+ if (defaultAccount)
+ {
+ // we only care about
+ var prefix = "mail.server." + defaultAccount.incomingServer.key + ".";
+ if (Services.prefs.prefHasUserValue(prefix + "manualMark"))
+ Services.prefs.setBoolPref("mail.spam.manualMark", Services.prefs.getBoolPref(prefix + "manualMark"));
+ if (Services.prefs.prefHasUserValue(prefix + "manualMarkMode"))
+ Services.prefs.setIntPref("mail.spam.manualMarkMode", Services.prefs.getIntPref(prefix + "manualMarkMode"));
+ if (Services.prefs.prefHasUserValue(prefix + "spamLoggingEnabled"))
+ Services.prefs.setBoolPref("mail.spam.logging.enabled", Services.prefs.getBoolPref(prefix + "spamLoggingEnabled"));
+ if (Services.prefs.prefHasUserValue(prefix + "markAsReadOnSpam"))
+ Services.prefs.setBoolPref("mail.spam.markAsReadOnSpam", Services.prefs.getBoolPref(prefix + "markAsReadOnSpam"));
+ }
+ // bump the version so we don't bother doing this again.
+ Services.prefs.setIntPref("mail.spam.version", 1);
+ }
+}
diff --git a/comm/suite/mailnews/content/msgViewNavigation.js b/comm/suite/mailnews/content/msgViewNavigation.js
new file mode 100644
index 0000000000..a7d0496210
--- /dev/null
+++ b/comm/suite/mailnews/content/msgViewNavigation.js
@@ -0,0 +1,243 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This file contains the js functions necessary to implement view navigation within the 3 pane. */
+
+const {FolderUtils} = ChromeUtils.import("resource:///modules/FolderUtils.jsm");
+
+//NOTE: gMessengerBundle must be defined and set or this Overlay won't work
+
+function GetSubFoldersInFolderPaneOrder(folder)
+{
+ var msgFolders = folder.subFolders;
+
+ function compareFolderSortKey(folder1, folder2) {
+ return folder1.compareSortKeys(folder2);
+ }
+
+ // sort the subfolders
+ msgFolders.sort(compareFolderSortKey);
+ return msgFolders;
+}
+
+function FindNextChildFolder(aParent, aAfter)
+{
+ // Search the child folders of aParent for unread messages
+ // but in the case that we are working up from the current folder
+ // we need to skip up to and including the current folder
+ // we skip the current folder in case a mail view is hiding unread messages
+ if (aParent.getNumUnread(true) > 0) {
+ var subFolders = GetSubFoldersInFolderPaneOrder(aParent);
+ var i = 0;
+ var folder = null;
+
+ // Skip folders until after the specified child
+ while (folder != aAfter)
+ folder = subFolders[i++];
+
+ let ignoreFlags = Ci.nsMsgFolderFlags.Trash | Ci.nsMsgFolderFlags.SentMail |
+ Ci.nsMsgFolderFlags.Drafts | Ci.nsMsgFolderFlags.Queue |
+ Ci.nsMsgFolderFlags.Templates | Ci.nsMsgFolderFlags.Junk;
+ while (i < subFolders.length) {
+ folder = subFolders[i++];
+ // if there is unread mail in the trash, sent, drafts, unsent messages
+ // templates or junk special folder,
+ // we ignore it when doing cross folder "next" navigation
+ if (!folder.isSpecialFolder(ignoreFlags, true)) {
+ if (folder.getNumUnread(false) > 0)
+ return folder;
+
+ folder = FindNextChildFolder(folder, null);
+ if (folder)
+ return folder;
+ }
+ }
+ }
+
+ return null;
+}
+
+function FindNextFolder()
+{
+ // look for the next folder, this will only look on the current account
+ // and below us, in the folder pane
+ // note use of gDBView restricts this function to message folders
+ // otherwise you could go next unread from a server
+ var folder = FindNextChildFolder(gDBView.msgFolder, null);
+ if (folder)
+ return folder;
+
+ // didn't find folder in children
+ // go up to the parent, and start at the folder after the current one
+ // unless we are at a server, in which case bail out.
+ for (folder = gDBView.msgFolder; !folder.isServer; ) {
+
+ var parent = folder.parent;
+ folder = FindNextChildFolder(parent, folder);
+ if (folder)
+ return folder;
+
+ // none at this level after the current folder. go up.
+ folder = parent;
+ }
+
+ // nothing in the current account, start with the next account (below)
+ // and try until we hit the bottom of the folder pane
+
+ // start at the account after the current account
+ var rootFolders = GetRootFoldersInFolderPaneOrder();
+ for (var i = 0; i < rootFolders.length; i++) {
+ if (rootFolders[i].URI == gDBView.msgFolder.server.serverURI)
+ break;
+ }
+
+ for (var j = i + 1; j < rootFolders.length; j++) {
+ folder = FindNextChildFolder(rootFolders[j], null);
+ if (folder)
+ return folder;
+ }
+
+ // if nothing from the current account down to the bottom
+ // (of the folder pane), start again at the top.
+ for (j = 0; j <= i; j++) {
+ folder = FindNextChildFolder(rootFolders[j], null);
+ if (folder)
+ return folder;
+ }
+ return null;
+}
+
+function GetRootFoldersInFolderPaneOrder()
+{
+ var accounts = FolderUtils.allAccountsSorted(false);
+
+ var serversMsgFolders = [];
+ for (var account of accounts)
+ serversMsgFolders.push(account.incomingServer.rootMsgFolder);
+
+ return serversMsgFolders;
+}
+
+function CrossFolderNavigation(type)
+{
+ // do cross folder navigation for next unread message/thread and message history
+ if (type != nsMsgNavigationType.nextUnreadMessage &&
+ type != nsMsgNavigationType.nextUnreadThread &&
+ type != nsMsgNavigationType.forward &&
+ type != nsMsgNavigationType.back)
+ return;
+
+ if (type == nsMsgNavigationType.nextUnreadMessage ||
+ type == nsMsgNavigationType.nextUnreadThread)
+ {
+
+ var nextMode = Services.prefs.getIntPref("mailnews.nav_crosses_folders");
+ // 0: "next" goes to the next folder, without prompting
+ // 1: "next" goes to the next folder, and prompts (the default)
+ // 2: "next" does nothing when there are no unread messages
+
+ // not crossing folders, don't find next
+ if (nextMode == 2)
+ return;
+
+ var folder = FindNextFolder();
+ if (folder && (gDBView.msgFolder.URI != folder.URI))
+ {
+ switch (nextMode)
+ {
+ case 0:
+ // do this unconditionally
+ gNextMessageAfterLoad = type;
+ SelectMsgFolder(folder);
+ break;
+ case 1:
+ default:
+ var promptText = gMessengerBundle.getFormattedString("advanceNextPrompt", [ folder.name ], 1);
+ if (Services.prompt.confirmEx(window, null, promptText,
+ Services.prompt.STD_YES_NO_BUTTONS,
+ null, null, null, null, {}) == 0)
+ {
+ gNextMessageAfterLoad = type;
+ SelectMsgFolder(folder);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ // if no message is loaded, relPos should be 0, to
+ // go back to the previously loaded message
+ var relPos = (type == nsMsgNavigationType.forward)
+ ? 1 : ((GetLoadedMessage()) ? -1 : 0);
+ var folderUri = messenger.getFolderUriAtNavigatePos(relPos);
+ var msgHdr = messenger.msgHdrFromURI(messenger.getMsgUriAtNavigatePos(relPos));
+ gStartMsgKey = msgHdr.messageKey;
+ var curPos = messenger.navigatePos;
+ curPos += relPos;
+ messenger.navigatePos = curPos;
+ SelectMsgFolder(MailUtils.getFolderForURI(folderUri));
+ }
+}
+
+
+function ScrollToMessage(type, wrap, selectMessage)
+{
+ try {
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ var treeSelection = treeView.selection;
+ var currentIndex = treeSelection.currentIndex;
+
+ var resultId = new Object;
+ var resultIndex = new Object;
+ var threadIndex = new Object;
+
+ let elidedFlag = Ci.nsMsgMessageFlags.Elided;
+ let summarizeSelection =
+ Services.prefs.getBoolPref("mail.operate_on_msgs_in_collapsed_threads");
+
+ // if we're doing next unread, and a collapsed thread is selected, and
+ // the top level message is unread, just set the result manually to
+ // the top level message, without using gDBView.viewNavigate.
+ if (summarizeSelection && type == nsMsgNavigationType.nextUnreadMessage &&
+ currentIndex != -1 &&
+ gDBView.getFlagsAt(currentIndex) & elidedFlag &&
+ gDBView.isContainer(currentIndex) &&
+ ! (gDBView.getFlagsAt(currentIndex) &
+ Ci.nsMsgMessageFlags.Read)) {
+ resultIndex.value = currentIndex;
+ resultId.value = gDBView.getKeyAt(currentIndex);
+ } else {
+ gDBView.viewNavigate(type, resultId, resultIndex, threadIndex, true /* wrap */);
+ }
+
+ // only scroll and select if we found something
+ if ((resultId.value != nsMsgViewIndex_None) && (resultIndex.value != nsMsgViewIndex_None)) {
+ if (gDBView.getFlagsAt(resultIndex.value) & elidedFlag &&
+ summarizeSelection)
+ gDBView.toggleOpenState(resultIndex.value);
+
+ if (selectMessage){
+ treeSelection.select(resultIndex.value);
+ }
+ EnsureRowInThreadTreeIsVisible(resultIndex.value);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ catch (ex) {
+ return false;
+ }
+}
+
+function GoNextMessage(type, startFromBeginning)
+{
+ if (!ScrollToMessage(type, startFromBeginning, true))
+ CrossFolderNavigation(type);
+
+ SetFocusThreadPaneIfNotOnMessagePane();
+}
diff --git a/comm/suite/mailnews/content/msgViewPickerOverlay.js b/comm/suite/mailnews/content/msgViewPickerOverlay.js
new file mode 100644
index 0000000000..39b3286b5d
--- /dev/null
+++ b/comm/suite/mailnews/content/msgViewPickerOverlay.js
@@ -0,0 +1,413 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// menuitem value constants
+// tag views have kViewTagMarker + their key as value
+const kViewItemAll = 0;
+const kViewItemUnread = 1;
+const kViewItemTags = 2; // former labels used values 2-6
+const kViewItemNotDeleted = 3;
+const kViewItemVirtual = 7;
+const kViewItemCustomize = 8;
+const kViewItemFirstCustom = 9;
+
+const kViewCurrent = "current-view";
+const kViewCurrentTag = "current-view-tag";
+const kViewTagMarker = ":";
+
+var gMailViewList = null;
+var gCurrentViewValue = kViewItemAll;
+var gCurrentViewLabel = "";
+var gSaveDefaultSVTerms;
+
+var nsMsgSearchScope = Ci.nsMsgSearchScope;
+var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+var nsMsgSearchOp = Ci.nsMsgSearchOp;
+
+
+// perform the view/action requested by the aValue string
+// and set the view picker label to the aLabel string
+function ViewChange(aValue, aLabel)
+{
+ if (aValue == kViewItemCustomize || aValue == kViewItemVirtual)
+ {
+ // restore to the previous view value, in case they cancel
+ UpdateViewPicker(gCurrentViewValue, gCurrentViewLabel);
+ if (aValue == kViewItemCustomize)
+ LaunchCustomizeDialog();
+ else
+ {
+ gFolderTreeController.newVirtualFolder(gCurrentViewLabel,
+ gSaveDefaultSVTerms);
+ }
+ return;
+ }
+
+ // persist the view
+ gCurrentViewValue = aValue;
+ gCurrentViewLabel = aLabel;
+ SetMailViewForFolder(GetFirstSelectedMsgFolder(), gCurrentViewValue)
+ UpdateViewPicker(gCurrentViewValue, gCurrentViewLabel);
+
+ // tag menuitem values are of the form :<keyword>
+ if (isNaN(aValue))
+ {
+ // split off the tag key
+ var tagkey = aValue.substr(kViewTagMarker.length);
+ ViewTagKeyword(tagkey);
+ }
+ else
+ {
+ var numval = Number(aValue);
+ switch (numval)
+ {
+ case kViewItemAll: // View All
+ gDefaultSearchViewTerms = null;
+ break;
+ case kViewItemUnread: // Unread
+ ViewNewMail();
+ break;
+ case kViewItemNotDeleted: // Not deleted
+ ViewNotDeletedMail();
+ break;
+ default:
+ // for legacy reasons, custom views start at index 9
+ LoadCustomMailView(numval - kViewItemFirstCustom);
+ break;
+ }
+ }
+ gSaveDefaultSVTerms = gDefaultSearchViewTerms;
+ onEnterInSearchBar();
+ gQSViewIsDirty = true;
+}
+
+
+function ViewChangeByMenuitem(aMenuitem)
+{
+ // Mac View menu menuitems don't have XBL bindings
+ ViewChange(aMenuitem.getAttribute("value"), aMenuitem.getAttribute("label"));
+}
+
+
+function ViewChangeByValue(aValue)
+{
+ ViewChange(aValue, GetLabelForValue(aValue));
+}
+
+function ViewChangeByFolder(aFolder)
+{
+ var result = GetMailViewForFolder(aFolder);
+ ViewChangeByValue(result);
+}
+
+function GetLabelForValue(aValue)
+{
+ var label = "";
+ var viewPickerPopup = document.getElementById("viewPickerPopup");
+ if (viewPickerPopup)
+ {
+ // grab the label for the menulist from one of its menuitems
+ var selectedItems = viewPickerPopup.getElementsByAttribute("value", aValue);
+ if (!selectedItems || !selectedItems.length)
+ {
+ // we may have a new item
+ RefreshAllViewPopups(viewPickerPopup);
+ selectedItems = viewPickerPopup.getElementsByAttribute("value", aValue);
+ }
+ label = selectedItems && selectedItems.length && selectedItems.item(0).getAttribute("label");
+ }
+ return label;
+}
+
+function UpdateViewPickerByValue(aValue)
+{
+ UpdateViewPicker(aValue, GetLabelForValue(aValue));
+}
+
+function UpdateViewPicker(aValue, aLabel)
+{
+ var viewPicker = document.getElementById("viewPicker");
+ if (viewPicker)
+ {
+ viewPicker.value = aValue;
+ viewPicker.setAttribute("label", aLabel);
+ }
+}
+
+function GetFolderInfo(aFolder)
+{
+ try
+ {
+ var db = aFolder.msgDatabase;
+ if (db)
+ return db.dBFolderInfo;
+ }
+ catch (ex) {}
+ return null;
+}
+
+
+function GetMailViewForFolder(aFolder)
+{
+ var val = "";
+ var folderInfo = GetFolderInfo(aFolder);
+ if (folderInfo)
+ {
+ val = folderInfo.getCharProperty(kViewCurrentTag);
+ if (!val)
+ {
+ // no new view value, thus using the old
+ var numval = folderInfo.getUint32Property(kViewCurrent, kViewItemAll);
+ // and migrate it, if it's a former label view (label views used values 2-6)
+ if ((kViewItemTags <= numval) && (numval < kViewItemVirtual))
+ val = kViewTagMarker + "$label" + (val - 1);
+ else
+ val = numval;
+ }
+ }
+ return val;
+}
+
+
+function SetMailViewForFolder(aFolder, aValue)
+{
+ var folderInfo = GetFolderInfo(aFolder);
+ if (folderInfo)
+ {
+ // we can't map tags back to labels in general,
+ // so set view to all for backwards compatibility in this case
+ folderInfo.setUint32Property (kViewCurrent, isNaN(aValue) ? kViewItemAll : aValue);
+ folderInfo.setCharProperty(kViewCurrentTag, aValue);
+ }
+}
+
+
+function LaunchCustomizeDialog()
+{
+ OpenOrFocusWindow({}, "mailnews:mailviewlist", "chrome://messenger/content/mailViewList.xul");
+}
+
+
+function LoadCustomMailView(index)
+{
+ PrepareForViewChange();
+ var searchTermsArrayForQS = CreateGroupedSearchTerms(gMailViewList.getMailViewAt(index).searchTerms);
+ createSearchTermsWithList(searchTermsArrayForQS);
+ AddVirtualFolderTerms(searchTermsArrayForQS);
+ gDefaultSearchViewTerms = searchTermsArrayForQS;
+}
+
+
+function ViewTagKeyword(keyword)
+{
+ PrepareForViewChange();
+
+ // create an i supports array to store our search terms
+ var searchTermsArray = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ var term = gSearchSession.createTerm();
+ var value = term.value;
+
+ value.str = keyword;
+ value.attrib = nsMsgSearchAttrib.Keywords;
+ term.value = value;
+ term.attrib = nsMsgSearchAttrib.Keywords;
+ term.op = nsMsgSearchOp.Contains;
+ term.booleanAnd = true;
+
+ searchTermsArray.appendElement(term);
+ AddVirtualFolderTerms(searchTermsArray);
+ createSearchTermsWithList(searchTermsArray);
+ gDefaultSearchViewTerms = searchTermsArray;
+}
+
+
+function ViewNewMail()
+{
+ PrepareForViewChange();
+
+ // create an i supports array to store our search terms
+ var searchTermsArray = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ var term = gSearchSession.createTerm();
+ var value = term.value;
+
+ value.status = 1;
+ value.attrib = nsMsgSearchAttrib.MsgStatus;
+ term.value = value;
+ term.attrib = nsMsgSearchAttrib.MsgStatus;
+ term.op = nsMsgSearchOp.Isnt;
+ term.booleanAnd = true;
+ searchTermsArray.appendElement(term);
+
+ AddVirtualFolderTerms(searchTermsArray);
+
+ createSearchTermsWithList(searchTermsArray);
+ // not quite right - these want to be just the view terms...but it might not matter.
+ gDefaultSearchViewTerms = searchTermsArray;
+}
+
+
+function ViewNotDeletedMail()
+{
+ PrepareForViewChange();
+
+ // create an i supports array to store our search terms
+ var searchTermsArray = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ var term = gSearchSession.createTerm();
+ var value = term.value;
+
+ value.status = 0x00200000;
+ value.attrib = nsMsgSearchAttrib.MsgStatus;
+ term.value = value;
+ term.attrib = nsMsgSearchAttrib.MsgStatus;
+ term.op = nsMsgSearchOp.Isnt;
+ term.booleanAnd = true;
+ searchTermsArray.appendElement(term);
+
+ AddVirtualFolderTerms(searchTermsArray);
+
+ createSearchTermsWithList(searchTermsArray);
+ // not quite right - these want to be just the view terms...but it might not matter.
+ gDefaultSearchViewTerms = searchTermsArray;
+}
+
+
+function AddVirtualFolderTerms(searchTermsArray)
+{
+ // add in any virtual folder terms
+ var virtualFolderSearchTerms = (gVirtualFolderTerms || gXFVirtualFolderTerms);
+ if (virtualFolderSearchTerms)
+ {
+ for (let virtualFolderSearchTerm of virtualFolderSearchTerms)
+ {
+ searchTermsArray.appendElement(virtualFolderSearchTerm);
+ }
+ }
+}
+
+
+function PrepareForViewChange()
+{
+ // this is a problem - it saves the current view in gPreQuickSearchView
+ // then we eventually call onEnterInSearchBar, and we think we need to restore the pre search view!
+ initializeSearchBar();
+ ClearThreadPaneSelection();
+ ClearMessagePane();
+}
+
+
+// refresh view popup and its subpopups
+function RefreshAllViewPopups(aViewPopup)
+{
+ var menupopups = aViewPopup.getElementsByTagName("menupopup");
+ if (menupopups.length > 1)
+ {
+ // when we have menupopups, we assume both tags and custom views are there
+ RefreshTagsPopup(menupopups[0]);
+ RefreshCustomViewsPopup(menupopups[1]);
+ }
+}
+
+
+function RefreshViewPopup(aViewPopup)
+{
+ // mark default views if selected
+ let viewAll = aViewPopup.getElementsByAttribute("value", kViewItemAll)[0];
+ viewAll.setAttribute("checked", gCurrentViewValue == kViewItemAll);
+ let viewUnread =
+ aViewPopup.getElementsByAttribute("value", kViewItemUnread)[0];
+ viewUnread.setAttribute("checked", gCurrentViewValue == kViewItemUnread);
+
+ let viewNotDeleted =
+ aViewPopup.getElementsByAttribute("value", kViewItemNotDeleted)[0];
+ var folderArray = GetSelectedMsgFolders();
+ if (folderArray.length == 0)
+ return;
+
+ // Only show the "Not Deleted" item for IMAP servers
+ // that are using the IMAP delete model.
+ viewNotDeleted.setAttribute("hidden", true);
+ var msgFolder = folderArray[0];
+ var server = msgFolder.server;
+ if (server.type == "imap")
+ {
+ let imapServer =
+ server.QueryInterface(Ci.nsIImapIncomingServer);
+ if (imapServer.deleteModel == Ci.nsMsgImapDeleteModels.IMAPDelete)
+ {
+ viewNotDeleted.setAttribute("hidden", false);
+ viewNotDeleted.setAttribute("checked",
+ gCurrentViewValue == kViewItemNotDeleted);
+ }
+ }
+}
+
+
+function RefreshCustomViewsPopup(aMenupopup)
+{
+ // for each mail view in the msg view list, add an entry in our combo box
+ if (!gMailViewList)
+ gMailViewList = Cc["@mozilla.org/messenger/mailviewlist;1"]
+ .getService(Ci.nsIMsgMailViewList);
+ // remove all menuitems
+ while (aMenupopup.hasChildNodes())
+ aMenupopup.lastChild.remove();
+
+ // now rebuild the list
+ var currentView = isNaN(gCurrentViewValue) ? kViewItemAll : Number(gCurrentViewValue);
+ var numItems = gMailViewList.mailViewCount;
+ for (var i = 0; i < numItems; ++i)
+ {
+ var viewInfo = gMailViewList.getMailViewAt(i);
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", viewInfo.prettyName);
+ menuitem.setAttribute("value", kViewItemFirstCustom + i);
+ menuitem.setAttribute("name", "viewmessages");
+ menuitem.setAttribute("type", "radio");
+ if (kViewItemFirstCustom + i == currentView)
+ menuitem.setAttribute("checked", true);
+ aMenupopup.appendChild(menuitem);
+ }
+}
+
+
+function RefreshTagsPopup(aMenupopup)
+{
+ // remove all menuitems
+ while (aMenupopup.hasChildNodes())
+ aMenupopup.lastChild.remove();
+
+ // create tag menuitems
+ var currentTagKey = isNaN(gCurrentViewValue) ? gCurrentViewValue.substr(kViewTagMarker.length) : "";
+ var tagArray = MailServices.tags.getAllTags();
+ for (var i = 0; i < tagArray.length; ++i)
+ {
+ var tagInfo = tagArray[i];
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", tagInfo.tag);
+ menuitem.setAttribute("value", kViewTagMarker + tagInfo.key);
+ menuitem.setAttribute("name", "viewmessages");
+ menuitem.setAttribute("type", "radio");
+ if (tagInfo.key == currentTagKey)
+ menuitem.setAttribute("checked", true);
+ var color = tagInfo.color;
+ if (color)
+ menuitem.setAttribute("class", "lc-" + color.substr(1));
+ aMenupopup.appendChild(menuitem);
+ }
+}
+
+
+function ViewPickerOnLoad()
+{
+ var viewPickerPopup = document.getElementById("viewPickerPopup");
+ if (viewPickerPopup)
+ RefreshAllViewPopups(viewPickerPopup);
+}
+
+
+window.addEventListener("load", ViewPickerOnLoad);
diff --git a/comm/suite/mailnews/content/nsDragAndDrop.js b/comm/suite/mailnews/content/nsDragAndDrop.js
new file mode 100644
index 0000000000..8808e5ecd0
--- /dev/null
+++ b/comm/suite/mailnews/content/nsDragAndDrop.js
@@ -0,0 +1,595 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+////////////////////////////////////////////////////////////////////////
+//
+// USE OF THIS API FOR DRAG AND DROP IS DEPRECATED!
+// Do not use this file for new code.
+//
+// For documentation about what to use instead, see:
+// http://developer.mozilla.org/En/DragDrop/Drag_and_Drop
+//
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * nsTransferable - a wrapper for nsITransferable that simplifies
+ * javascript clipboard and drag&drop. for use in
+ * these situations you should use the nsClipboard
+ * and nsDragAndDrop wrappers for more convenience
+ **/
+
+var nsTransferable = {
+ /**
+ * nsITransferable set (TransferData aTransferData) ;
+ *
+ * Creates a transferable with data for a list of supported types ("flavours")
+ *
+ * @param TransferData aTransferData
+ * a javascript object in the format described above
+ **/
+ set: function (aTransferDataSet)
+ {
+ var trans = this.createTransferable();
+ for (var i = 0; i < aTransferDataSet.dataList.length; ++i)
+ {
+ var currData = aTransferDataSet.dataList[i];
+ var currFlavour = currData.flavour.contentType;
+ trans.addDataFlavor(currFlavour);
+ var supports = null; // nsISupports data
+ var length = 0;
+ if (currData.flavour.dataIIDKey == "nsISupportsString")
+ {
+ supports = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+
+ supports.data = currData.supports;
+ length = supports.data.length;
+ }
+ else
+ {
+ // non-string data.
+ supports = currData.supports;
+ length = 0; // kFlavorHasDataProvider
+ }
+ trans.setTransferData(currFlavour, supports, length * 2);
+ }
+ return trans;
+ },
+
+ /**
+ * TransferData/TransferDataSet get (FlavourSet aFlavourSet,
+ * Function aRetrievalFunc, Boolean aAnyFlag) ;
+ *
+ * Retrieves data from the transferable provided in aRetrievalFunc, formatted
+ * for more convenient access.
+ *
+ * @param FlavourSet aFlavourSet
+ * a FlavourSet object that contains a list of supported flavours.
+ * @param Function aRetrievalFunc
+ * a reference to a function that returns a nsIArray of nsITransferables
+ * for each item from the specified source (clipboard/drag&drop etc)
+ * @param Boolean aAnyFlag
+ * a flag specifying whether or not a specific flavour is requested. If false,
+ * data of the type of the first flavour in the flavourlist parameter is returned,
+ * otherwise the best flavour supported will be returned.
+ **/
+ get: function (aFlavourSet, aRetrievalFunc, aAnyFlag)
+ {
+ if (!aRetrievalFunc)
+ throw "No data retrieval handler provided!";
+
+ var array = aRetrievalFunc(aFlavourSet);
+ var dataArray = [];
+
+ // Iterate over the number of items returned from aRetrievalFunc. For
+ // clipboard operations, this is 1, for drag and drop (where multiple
+ // items may have been dragged) this could be >1.
+ for (let i = 0; i < array.length; i++)
+ {
+ let trans = array.queryElementAt(i, Ci.nsITransferable);
+ if (!trans)
+ continue;
+
+ var data = { };
+ var length = { };
+
+ var currData = null;
+ if (aAnyFlag)
+ {
+ var flavour = { };
+ trans.getAnyTransferData(flavour, data, length);
+ if (data && flavour)
+ {
+ var selectedFlavour = aFlavourSet.flavourTable[flavour.value];
+ if (selectedFlavour)
+ dataArray[i] = FlavourToXfer(data.value, length.value, selectedFlavour);
+ }
+ }
+ else
+ {
+ var firstFlavour = aFlavourSet.flavours[0];
+ trans.getTransferData(firstFlavour, data, length);
+ if (data && firstFlavour)
+ dataArray[i] = FlavourToXfer(data.value, length.value, firstFlavour);
+ }
+ }
+ return new TransferDataSet(dataArray);
+ },
+
+ /**
+ * nsITransferable createTransferable (void) ;
+ *
+ * Creates and returns a transferable object.
+ **/
+ createTransferable: function ()
+ {
+ const kXferableContractID = "@mozilla.org/widget/transferable;1";
+ const kXferableIID = Ci.nsITransferable;
+ var trans = Cc[kXferableContractID].createInstance(kXferableIID);
+ trans.init(null);
+ return trans;
+ }
+};
+
+/**
+ * A FlavourSet is a simple type that represents a collection of Flavour objects.
+ * FlavourSet is constructed from an array of Flavours, and stores this list as
+ * an array and a hashtable. The rationale for the dual storage is as follows:
+ *
+ * Array: Ordering is important when adding data flavours to a transferable.
+ * Flavours added first are deemed to be 'preferred' by the client.
+ * Hash: Convenient lookup of flavour data using the content type (MIME type)
+ * of data as a key.
+ */
+function FlavourSet(aFlavourList)
+{
+ this.flavours = aFlavourList || [];
+ this.flavourTable = { };
+
+ this._XferID = "FlavourSet";
+
+ for (var i = 0; i < this.flavours.length; ++i)
+ this.flavourTable[this.flavours[i].contentType] = this.flavours[i];
+}
+
+FlavourSet.prototype = {
+ appendFlavour: function (aFlavour, aFlavourIIDKey)
+ {
+ var flavour = new Flavour (aFlavour, aFlavourIIDKey);
+ this.flavours.push(flavour);
+ this.flavourTable[flavour.contentType] = flavour;
+ }
+};
+
+/**
+ * A Flavour is a simple type that represents a data type that can be handled.
+ * It takes a content type (MIME type) which is used when storing data on the
+ * system clipboard/drag and drop, and an IIDKey (string interface name
+ * which is used to QI data to an appropriate form. The default interface is
+ * assumed to be wide-string.
+ */
+function Flavour(aContentType, aDataIIDKey)
+{
+ this.contentType = aContentType;
+ this.dataIIDKey = aDataIIDKey || "nsISupportsString";
+
+ this._XferID = "Flavour";
+}
+
+function TransferDataBase() {}
+TransferDataBase.prototype = {
+ push: function (aItems)
+ {
+ this.dataList.push(aItems);
+ },
+
+ get first ()
+ {
+ return "dataList" in this && this.dataList.length ? this.dataList[0] : null;
+ }
+};
+
+/**
+ * TransferDataSet is a list (array) of TransferData objects, which represents
+ * data dragged from one or more elements.
+ */
+function TransferDataSet(aTransferDataList)
+{
+ this.dataList = aTransferDataList || [];
+
+ this._XferID = "TransferDataSet";
+}
+TransferDataSet.prototype = TransferDataBase.prototype;
+
+/**
+ * TransferData is a list (array) of FlavourData for all the applicable content
+ * types associated with a drag from a single item.
+ */
+function TransferData(aFlavourDataList)
+{
+ this.dataList = aFlavourDataList || [];
+
+ this._XferID = "TransferData";
+}
+TransferData.prototype = {
+ __proto__: TransferDataBase.prototype,
+
+ addDataForFlavour: function (aFlavourString, aData, aLength, aDataIIDKey)
+ {
+ this.dataList.push(new FlavourData(aData, aLength,
+ new Flavour(aFlavourString, aDataIIDKey)));
+ }
+};
+
+/**
+ * FlavourData is a type that represents data retrieved from the system
+ * clipboard or drag and drop. It is constructed internally by the Transferable
+ * using the raw (nsISupports) data from the clipboard, the length of the data,
+ * and an object of type Flavour representing the type. Clients implementing
+ * IDragDropObserver receive an object of this type in their implementation of
+ * onDrop. They access the 'data' property to retrieve data, which is either data
+ * QI'ed to a usable form, or unicode string.
+ */
+function FlavourData(aData, aLength, aFlavour)
+{
+ this.supports = aData;
+ this.contentLength = aLength;
+ this.flavour = aFlavour || null;
+
+ this._XferID = "FlavourData";
+}
+
+FlavourData.prototype = {
+ get data ()
+ {
+ if (this.flavour &&
+ this.flavour.dataIIDKey != "nsISupportsString")
+ return this.supports.QueryInterface(Ci[this.flavour.dataIIDKey]);
+
+ var supports = this.supports;
+ if (supports instanceof Ci.nsISupportsString)
+ return supports.data.substring(0, this.contentLength/2);
+
+ return supports;
+ }
+}
+
+/**
+ * Create a TransferData object with a single FlavourData entry. Used when
+ * unwrapping data of a specific flavour from the drag service.
+ */
+function FlavourToXfer(aData, aLength, aFlavour)
+{
+ return new TransferData([new FlavourData(aData, aLength, aFlavour)]);
+}
+
+var transferUtils = {
+
+ retrieveURLFromData: function (aData, flavour)
+ {
+ switch (flavour) {
+ case "text/unicode":
+ case "text/plain":
+ case "text/x-moz-text-internal":
+ return aData.replace(/^\s+|\s+$/g, "");
+ case "text/x-moz-url":
+ return ((aData instanceof Ci.nsISupportsString) ? aData.toString() : aData).split("\n")[0];
+ case "application/x-moz-file":
+ var fileHandler = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ return fileHandler.getURLSpecFromFile(aData);
+ }
+ return null;
+ }
+
+}
+
+/**
+ * nsDragAndDrop - a convenience wrapper for nsTransferable, nsITransferable
+ * and nsIDragService/nsIDragSession.
+ *
+ * Use: map the handler functions to the 'ondraggesture', 'ondragover' and
+ * 'ondragdrop' event handlers on your XML element, e.g.
+ * <xmlelement ondraggesture="nsDragAndDrop.startDrag(event, observer);"
+ * ondragover="nsDragAndDrop.dragOver(event, observer);"
+ * ondragdrop="nsDragAndDrop.drop(event, observer);"/>
+ *
+ * You need to create an observer js object with the following member
+ * functions:
+ * Object onDragStart (event) // called when drag initiated,
+ * // returns flavour list with data
+ * // to stuff into transferable
+ * void onDragOver (Object flavour) // called when element is dragged
+ * // over, so that it can perform
+ * // any drag-over feedback for provided
+ * // flavour
+ * void onDrop (Object data) // formatted data object dropped.
+ * Object getSupportedFlavours () // returns a flavour list so that
+ * // nsTransferable can determine
+ * // whether or not to accept drop.
+ **/
+
+var nsDragAndDrop = {
+
+ _mDS: null,
+ get mDragService()
+ {
+ if (!this._mDS)
+ {
+ const kDSContractID = "@mozilla.org/widget/dragservice;1";
+ const kDSIID = Ci.nsIDragService;
+ this._mDS = Cc[kDSContractID].getService(kDSIID);
+ }
+ return this._mDS;
+ },
+
+ /**
+ * void startDrag (DOMEvent aEvent, Object aDragDropObserver) ;
+ *
+ * called when a drag on an element is started.
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by the drag init
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ **/
+ startDrag: function (aEvent, aDragDropObserver)
+ {
+ if (!("onDragStart" in aDragDropObserver))
+ return;
+
+ const kDSIID = Ci.nsIDragService;
+ var dragAction = { action: kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_MOVE + kDSIID.DRAGDROP_ACTION_LINK };
+
+ var transferData = { data: null };
+ try
+ {
+ aDragDropObserver.onDragStart(aEvent, transferData, dragAction);
+ }
+ catch (e)
+ {
+ return; // not a draggable item, bail!
+ }
+
+ if (!transferData.data) return;
+ transferData = transferData.data;
+
+ var dt = aEvent.dataTransfer;
+ var count = 0;
+ do {
+ var tds = transferData._XferID == "TransferData"
+ ? transferData
+ : transferData.dataList[count]
+ for (var i = 0; i < tds.dataList.length; ++i)
+ {
+ var currData = tds.dataList[i];
+ var currFlavour = currData.flavour.contentType;
+ var value = currData.supports;
+ if (value instanceof Ci.nsISupportsString)
+ value = value.toString();
+ dt.mozSetDataAt(currFlavour, value, count);
+ }
+
+ count++;
+ }
+ while (transferData._XferID == "TransferDataSet" &&
+ count < transferData.dataList.length);
+
+ dt.effectAllowed = "all";
+ // a drag targeted at a tree should instead use the treechildren so that
+ // the current selection is used as the drag feedback
+ dt.addElement(aEvent.originalTarget.localName == "treechildren" ?
+ aEvent.originalTarget : aEvent.target);
+ aEvent.stopPropagation();
+ },
+
+ /**
+ * void dragOver (DOMEvent aEvent, Object aDragDropObserver) ;
+ *
+ * called when a drag passes over this element
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by passing over the element
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ **/
+ dragOver: function (aEvent, aDragDropObserver)
+ {
+ if (!("onDragOver" in aDragDropObserver))
+ return;
+ if (!this.checkCanDrop(aEvent, aDragDropObserver))
+ return;
+ var flavourSet = aDragDropObserver.getSupportedFlavours();
+ for (var flavour in flavourSet.flavourTable)
+ {
+ if (this.mDragSession.isDataFlavorSupported(flavour))
+ {
+ aDragDropObserver.onDragOver(aEvent,
+ flavourSet.flavourTable[flavour],
+ this.mDragSession);
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ break;
+ }
+ }
+ },
+
+ mDragSession: null,
+
+ /**
+ * void drop (DOMEvent aEvent, Object aDragDropObserver) ;
+ *
+ * called when the user drops on the element
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by the drop
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ **/
+ drop: function (aEvent, aDragDropObserver)
+ {
+ if (!("onDrop" in aDragDropObserver))
+ return;
+ if (!this.checkCanDrop(aEvent, aDragDropObserver))
+ return;
+
+ var flavourSet = aDragDropObserver.getSupportedFlavours();
+
+ var dt = aEvent.dataTransfer;
+ var dataArray = [];
+ var count = dt.mozItemCount;
+ for (var i = 0; i < count; ++i) {
+ var types = dt.mozTypesAt(i);
+ for (var j = 0; j < flavourSet.flavours.length; j++) {
+ var type = flavourSet.flavours[j].contentType;
+ // dataTransfer uses text/plain but older code used text/unicode, so
+ // switch this for compatibility
+ var modtype = (type == "text/unicode") ? "text/plain" : type;
+ if (Array.from(types).includes(modtype)) {
+ var data = dt.mozGetDataAt(modtype, i);
+ if (data) {
+ // Non-strings need some non-zero value used for their data length.
+ const kNonStringDataLength = 4;
+
+ var length = (typeof data == "string") ? data.length : kNonStringDataLength;
+ dataArray[i] = FlavourToXfer(data, length, flavourSet.flavourTable[type]);
+ break;
+ }
+ }
+ }
+ }
+
+ var transferData = new TransferDataSet(dataArray)
+
+ // hand over to the client to respond to dropped data
+ var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
+ var dropData = multiple ? transferData : transferData.first.first;
+ aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
+ aEvent.stopPropagation();
+ },
+
+ /**
+ * void dragExit (DOMEvent aEvent, Object aDragDropObserver) ;
+ *
+ * called when a drag leaves this element
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by leaving the element
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ **/
+ dragExit: function (aEvent, aDragDropObserver)
+ {
+ if (!this.checkCanDrop(aEvent, aDragDropObserver))
+ return;
+ if ("onDragExit" in aDragDropObserver)
+ aDragDropObserver.onDragExit(aEvent, this.mDragSession);
+ },
+
+ /**
+ * void dragEnter (DOMEvent aEvent, Object aDragDropObserver) ;
+ *
+ * called when a drag enters in this element
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by entering in the element
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ **/
+ dragEnter: function (aEvent, aDragDropObserver)
+ {
+ if (!this.checkCanDrop(aEvent, aDragDropObserver))
+ return;
+ if ("onDragEnter" in aDragDropObserver)
+ aDragDropObserver.onDragEnter(aEvent, this.mDragSession);
+ },
+
+ /**
+ * Boolean checkCanDrop (DOMEvent aEvent, Object aDragDropObserver) ;
+ *
+ * Sets the canDrop attribute for the drag session.
+ * returns false if there is no current drag session.
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by the drop
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ **/
+ checkCanDrop: function (aEvent, aDragDropObserver)
+ {
+ if (!this.mDragSession)
+ this.mDragSession = this.mDragService.getCurrentSession();
+ if (!this.mDragSession)
+ return false;
+ this.mDragSession.canDrop = this.mDragSession.sourceNode != aEvent.target;
+ if ("canDrop" in aDragDropObserver)
+ this.mDragSession.canDrop &= aDragDropObserver.canDrop(aEvent, this.mDragSession);
+ return true;
+ },
+
+ /**
+ * Do a security check for drag n' drop. Make sure the source document
+ * can load the dragged link.
+ *
+ * @param DOMEvent aEvent
+ * the DOM event fired by leaving the element
+ * @param Object aDragDropObserver
+ * javascript object of format described above that specifies
+ * the way in which the element responds to drag events.
+ * @param String aDraggedText
+ * the text being dragged
+ **/
+ dragDropSecurityCheck: function (aEvent, aDragSession, aDraggedText)
+ {
+ // Strip leading and trailing whitespace, then try to create a
+ // URI from the dropped string. If that succeeds, we're
+ // dropping a URI and we need to do a security check to make
+ // sure the source document can load the dropped URI. We don't
+ // so much care about creating the real URI here
+ // (i.e. encoding differences etc don't matter), we just want
+ // to know if aDraggedText really is a URI.
+
+ aDraggedText = aDraggedText.replace(/^\s*|\s*$/g, '');
+
+ var uri;
+ try {
+ uri = Services.io.newURI(aDraggedText);
+ } catch (e) {
+ }
+
+ if (!uri)
+ return;
+
+ // aDraggedText is a URI, do the security check.
+ let secMan = Services.scriptSecurityManager;
+
+ if (!aDragSession)
+ aDragSession = this.mDragService.getCurrentSession();
+
+ var sourceDoc = aDragSession.sourceDocument;
+ // Use "file:///" as the default sourceURI so that drops of file:// URIs
+ // are always allowed.
+ var principal = sourceDoc ? sourceDoc.nodePrincipal
+ : secMan.createCodebasePrincipal(Services.io.newURI("file:///"), {});
+
+ try {
+ secMan.checkLoadURIStrWithPrincipal(principal, aDraggedText,
+ Ci.nsIScriptSecurityManager.STANDARD);
+ } catch (e) {
+ // Stop event propagation right here.
+ aEvent.stopPropagation();
+
+ throw "Drop of " + aDraggedText + " denied.";
+ }
+ }
+};
+
diff --git a/comm/suite/mailnews/content/phishingDetector.js b/comm/suite/mailnews/content/phishingDetector.js
new file mode 100644
index 0000000000..04d2910753
--- /dev/null
+++ b/comm/suite/mailnews/content/phishingDetector.js
@@ -0,0 +1,173 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Dependencies:
+// gBrandBundle, gMessengerBundle should already be defined
+// gatherTextUnder from utilityOverlay.js
+
+ChromeUtils.import("resource:///modules/hostnameUtils.jsm");
+
+const kPhishingNotSuspicious = 0;
+const kPhishingWithIPAddress = 1;
+const kPhishingWithMismatchedHosts = 2;
+
+//////////////////////////////////////////////////////////////////////////////
+// isEmailScam --> examines the message currently loaded in the message pane
+// and returns true if we think that message is an e-mail scam.
+// Assumes the message has been completely loaded in the message pane (i.e. OnMsgParsed has fired)
+// aUrl: nsIURI object for the msg we want to examine...
+//////////////////////////////////////////////////////////////////////////////
+function isMsgEmailScam(aUrl)
+{
+ var isEmailScam = false;
+ if (!aUrl || !Services.prefs.getBoolPref("mail.phishing.detection.enabled"))
+ return isEmailScam;
+
+ try {
+ // nsIMsgMailNewsUrl.folder can throw an NS_ERROR_FAILURE, especially if
+ // we are opening an .eml file.
+ var folder = aUrl.folder;
+
+ // Ignore NNTP and RSS messages.
+ if (folder.server.type == 'nntp' || folder.server.type == 'rss')
+ return isEmailScam;
+
+ // Also ignore messages in Sent/Drafts/Templates/Outbox.
+ let outgoingFlags = Ci.nsMsgFolderFlags.SentMail |
+ Ci.nsMsgFolderFlags.Drafts |
+ Ci.nsMsgFolderFlags.Templates |
+ Ci.nsMsgFolderFlags.Queue;
+ if (folder.isSpecialFolder(outgoingFlags, true))
+ return isEmailScam;
+
+ } catch (ex) {
+ if (ex.result != Cr.NS_ERROR_FAILURE)
+ throw ex;
+ }
+
+ // loop through all of the link nodes in the message's DOM, looking for phishing URLs...
+ var msgDocument = document.getElementById('messagepane').contentDocument;
+ var index;
+
+ // examine all links...
+ var linkNodes = msgDocument.links;
+ for (index = 0; index < linkNodes.length && !isEmailScam; index++)
+ isEmailScam = isPhishingURL(linkNodes[index], true);
+
+ // if an e-mail contains a non-addressbook form element, then assume the message is
+ // a phishing attack. Legitimate sites should not be using forms inside of e-mail
+ if (!isEmailScam)
+ {
+ var forms = msgDocument.getElementsByTagName("form");
+ for (index = 0; index < forms.length && !isEmailScam; index++)
+ isEmailScam = forms[index].action != "" && !/^addbook:/.test(forms[index].action);
+ }
+
+ // we'll add more checks here as our detector matures....
+ return isEmailScam;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// isPhishingURL --> examines the passed in linkNode and returns true if we think
+// the URL is an email scam.
+// aLinkNode: the link node to examine
+// aSilentMode: don't prompt the user to confirm
+// aHref: optional href for XLinks
+//////////////////////////////////////////////////////////////////////////////
+
+function isPhishingURL(aLinkNode, aSilentMode, aHref)
+{
+ if (!Services.prefs.getBoolPref("mail.phishing.detection.enabled"))
+ return false;
+
+ var phishingType = kPhishingNotSuspicious;
+ var aLinkText = gatherTextUnder(aLinkNode);
+ var href = aHref || aLinkNode.href;
+ if (!href)
+ return false;
+
+ var linkTextURL = {};
+ var isPhishingURL = false;
+
+ var hrefURL;
+ // Make sure relative link urls don't make us bail out.
+ try {
+ hrefURL = Services.io.newURI(href);
+ } catch(ex) { return false; }
+
+ // only check for phishing urls if the url is an http or https link.
+ // this prevents us from flagging imap and other internally handled urls
+ if (hrefURL.schemeIs('http') || hrefURL.schemeIs('https'))
+ {
+
+ if (aLinkText)
+ aLinkText = aLinkText.replace(/^<(.+)>$|^"(.+)"$/, "$1$2");
+ if (aLinkText != aLinkNode.href &&
+ aLinkText.replace(/\/+$/, "") != aLinkNode.href.replace(/\/+$/, ""))
+ {
+ let ipAddress = isLegalIPAddress(hrefURL.host, true);
+ if (ipAddress && !isLegalLocalIPAddress(ipAddress))
+ phishingType = kPhishingWithIPAddress;
+ else if (misMatchedHostWithLinkText(aLinkNode, hrefURL))
+ phishingType = kPhishingWithMismatchedHosts;
+
+ isPhishingURL = phishingType != kPhishingNotSuspicious;
+
+ if (!aSilentMode && isPhishingURL) // allow the user to override the decision
+ isPhishingURL = confirmSuspiciousURL(phishingType, hrefURL.host);
+ }
+ }
+
+ return isPhishingURL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// helper methods in support of isPhishingURL
+//////////////////////////////////////////////////////////////////////////////
+
+function misMatchedHostWithLinkText(aLinkNode, aHrefURL)
+{
+ var linkNodeText = gatherTextUnder(aLinkNode);
+
+ // gatherTextUnder puts a space between each piece of text it gathers,
+ // so strip the spaces out (see bug 326082 for details).
+ linkNodeText = linkNodeText.replace(/ /g, "");
+
+ // only worry about http and https urls
+ if (linkNodeText)
+ {
+ // does the link text look like a http url?
+ if (linkNodeText.search(/(^http:|^https:)/) != -1)
+ {
+ var linkURI = Services.io.newURI(linkNodeText);
+ // compare hosts, but ignore possible www. prefix
+ return !(aHrefURL.host.replace(/^www\./, "") == linkURI.host.replace(/^www\./, ""));
+ }
+ }
+
+ return false;
+}
+
+// returns true if the user confirms the URL is a scam
+function confirmSuspiciousURL(aPhishingType, aSuspiciousHostName)
+{
+ var brandShortName = gBrandBundle.getString("brandShortName");
+ var titleMsg = gMessengerBundle.getString("confirmPhishingTitle");
+ var dialogMsg;
+
+ switch (aPhishingType)
+ {
+ case kPhishingWithIPAddress:
+ case kPhishingWithMismatchedHosts:
+ dialogMsg = gMessengerBundle.getFormattedString("confirmPhishingUrl" + aPhishingType, [brandShortName, aSuspiciousHostName], 2);
+ break;
+ default:
+ return false;
+ }
+
+ var buttons = Services.prompt.STD_YES_NO_BUTTONS +
+ Services.prompt.BUTTON_POS_1_DEFAULT;
+ return Services.prompt.confirmEx(window, titleMsg, dialogMsg, buttons, "", "", "", "", {}); /* the yes button is in position 0 */
+} \ No newline at end of file
diff --git a/comm/suite/mailnews/content/searchBar.js b/comm/suite/mailnews/content/searchBar.js
new file mode 100644
index 0000000000..2c23b395a3
--- /dev/null
+++ b/comm/suite/mailnews/content/searchBar.js
@@ -0,0 +1,432 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {PluralForm} = ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
+
+var gSearchSession = null;
+var gPreQuickSearchView = null;
+var gSearchTimer = null;
+var gViewSearchListener;
+var gSearchBundle;
+var gProgressMeter = null;
+var gSearchInProgress = false;
+var gClearButton = null;
+var gDefaultSearchViewTerms = null;
+var gQSViewIsDirty = false;
+var gNumTotalMessages;
+var gNumUnreadMessages;
+
+function SetQSStatusText(aNumHits)
+{
+ var statusMsg;
+ // if there are no hits, it means no matches were found in the search.
+ if (aNumHits == 0)
+ {
+ statusMsg = gSearchBundle.getString("noMatchesFound");
+ }
+ else
+ {
+ statusMsg = PluralForm.get(aNumHits,
+ gSearchBundle.getString("matchesFound"));
+ statusMsg = statusMsg.replace("#1", aNumHits);
+ }
+ statusFeedback.showStatusString(statusMsg);
+}
+
+// nsIMsgSearchNotify object
+var gSearchNotificationListener =
+{
+ onSearchHit: function(header, folder)
+ {
+ gNumTotalMessages++;
+ if (!header.isRead)
+ gNumUnreadMessages++;
+ // XXX todo
+ // update status text?
+ },
+
+ onSearchDone: function(status)
+ {
+ SetQSStatusText(gDBView.QueryInterface(Ci.nsITreeView).rowCount)
+ statusFeedback.showProgress(0);
+ gProgressMeter.setAttribute("mode", "normal");
+ gSearchInProgress = false;
+
+ // ### TODO need to find out if there's quick search within a virtual folder.
+ if (gCurrentVirtualFolderUri &&
+ (!gSearchInput || gSearchInput.value == ""))
+ {
+ var vFolder = MailUtils.getFolderForURI(gCurrentVirtualFolderUri, false);
+ var dbFolderInfo = vFolder.msgDatabase.dBFolderInfo;
+ dbFolderInfo.numUnreadMessages = gNumUnreadMessages;
+ dbFolderInfo.numMessages = gNumTotalMessages;
+ vFolder.updateSummaryTotals(true); // force update from db.
+ var msgdb = vFolder.msgDatabase;
+ msgdb.Commit(Ci.nsMsgDBCommitType.kLargeCommit);
+ // now that we have finished loading a virtual folder,
+ // scroll to the correct message if there is at least one.
+ if (vFolder.getTotalMessages(false) > 0)
+ ScrollToMessageAfterFolderLoad(vFolder);
+ }
+ },
+
+ onNewSearch: function()
+ {
+ statusFeedback.showProgress(0);
+ statusFeedback.showStatusString(gSearchBundle.getString("searchingMessage"));
+ gProgressMeter.setAttribute("mode", "undetermined");
+ gSearchInProgress = true;
+ gNumTotalMessages = 0;
+ gNumUnreadMessages = 0;
+ }
+}
+
+function getDocumentElements()
+{
+ gSearchBundle = document.getElementById("bundle_search");
+ gProgressMeter = document.getElementById('statusbar-icon');
+ gClearButton = document.getElementById('clearButton');
+ GetSearchInput();
+}
+
+function addListeners()
+{
+ gViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify);
+ gSearchSession.registerListener(gViewSearchListener);
+}
+
+function removeListeners()
+{
+ gSearchSession.unregisterListener(gViewSearchListener);
+}
+
+function removeGlobalListeners()
+{
+ removeListeners();
+ gSearchSession.unregisterListener(gSearchNotificationListener);
+}
+
+function initializeGlobalListeners()
+{
+ // Setup the javascript object as a listener on the search results
+ gSearchSession.registerListener(gSearchNotificationListener);
+}
+
+function createQuickSearchView()
+{
+ //if not already in quick search view
+ if (gDBView.viewType != nsMsgViewType.eShowQuickSearchResults)
+ {
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView); //clear selection
+ if (treeView && treeView.selection)
+ treeView.selection.clearSelection();
+ gPreQuickSearchView = gDBView;
+ if (gDBView.viewType == nsMsgViewType.eShowVirtualFolderResults)
+ {
+ // remove the view as a listener on the search results
+ var saveViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify);
+ gSearchSession.unregisterListener(saveViewSearchListener);
+ }
+ CreateDBView(gDBView.msgFolder, (gXFVirtualFolderTerms) ? nsMsgViewType.eShowVirtualFolderResults : nsMsgViewType.eShowQuickSearchResults, gDBView.viewFlags, gDBView.sortType, gDBView.sortOrder);
+ }
+}
+
+function initializeSearchBar()
+{
+ createQuickSearchView();
+ if (!gSearchSession)
+ {
+ var searchSessionContractID = "@mozilla.org/messenger/searchSession;1";
+ gSearchSession = Cc[searchSessionContractID].createInstance(Ci.nsIMsgSearchSession);
+ initializeGlobalListeners();
+ }
+ else
+ {
+ if (gSearchInProgress)
+ {
+ onSearchStop();
+ gSearchInProgress = false;
+ }
+ removeListeners();
+ }
+ addListeners();
+}
+
+function onEnterInSearchBar()
+{
+ if (!gSearchBundle)
+ getDocumentElements();
+ if (gSearchInput.value == "")
+ {
+ let viewType = gDBView && gDBView.viewType;
+ if (viewType == nsMsgViewType.eShowQuickSearchResults ||
+ viewType == nsMsgViewType.eShowVirtualFolderResults)
+ {
+ statusFeedback.showStatusString("");
+ disableQuickSearchClearButton();
+
+ viewDebug ("onEnterInSearchBar gDefaultSearchViewTerms = " + gDefaultSearchViewTerms + "gVirtualFolderTerms = "
+ + gVirtualFolderTerms + "gXFVirtualFolderTerms = " + gXFVirtualFolderTerms + "\n");
+ var addTerms = gDefaultSearchViewTerms || gVirtualFolderTerms || gXFVirtualFolderTerms;
+ if (addTerms)
+ {
+ viewDebug ("addTerms = " + addTerms + " count = " + addTerms.length + "\n");
+ initializeSearchBar();
+ onSearch(addTerms);
+ }
+ else
+ restorePreSearchView();
+ }
+ else if (gPreQuickSearchView && !gDefaultSearchViewTerms)// may be a quick search from a cross-folder virtual folder
+ restorePreSearchView();
+
+ gQSViewIsDirty = false;
+ return;
+ }
+
+ initializeSearchBar();
+
+ if (gClearButton)
+ gClearButton.setAttribute("disabled", false); //coming into search enable clear button
+
+ ClearThreadPaneSelection();
+ ClearMessagePane();
+
+ onSearch(null);
+ gQSViewIsDirty = false;
+}
+
+function restorePreSearchView()
+{
+ var selectedHdr = null;
+ //save selection
+ try
+ {
+ selectedHdr = gDBView.hdrForFirstSelectedMessage;
+ }
+ catch (ex)
+ {}
+
+ //we might have to sort the view coming out of quick search
+ var sortType = gDBView.sortType;
+ var sortOrder = gDBView.sortOrder;
+ var viewFlags = gDBView.viewFlags;
+ var folder = gDBView.msgFolder;
+
+ gDBView.close();
+ gDBView = null;
+
+ if (gPreQuickSearchView)
+ {
+ gDBView = gPreQuickSearchView;
+ if (gDBView.viewType == nsMsgViewType.eShowVirtualFolderResults)
+ {
+ // readd the view as a listener on the search results
+ var saveViewSearchListener = gDBView.QueryInterface(Ci.nsIMsgSearchNotify);
+ if (gSearchSession)
+ gSearchSession.registerListener(saveViewSearchListener);
+ }
+// dump ("view type = " + gDBView.viewType + "\n");
+
+ if (sortType != gDBView.sortType || sortOrder != gDBView.sortOrder)
+ {
+ gDBView.sort(sortType, sortOrder);
+ }
+ UpdateSortIndicators(sortType, sortOrder);
+
+ gPreQuickSearchView = null;
+ }
+ else //create default view type
+ CreateDBView(folder, nsMsgViewType.eShowAllThreads, viewFlags, sortType, sortOrder);
+
+ RerootThreadPane();
+
+ var scrolled = false;
+
+ // now restore selection
+ if (selectedHdr)
+ {
+ gDBView.selectMsgByKey(selectedHdr.messageKey);
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ var selectedIndex = treeView.selection.currentIndex;
+ if (selectedIndex >= 0)
+ {
+ // scroll
+ EnsureRowInThreadTreeIsVisible(selectedIndex);
+ scrolled = true;
+ }
+ else
+ ClearMessagePane();
+ }
+ if (!scrolled)
+ ScrollToMessageAfterFolderLoad(null);
+}
+
+function onSearch(aSearchTerms)
+{
+ viewDebug("in OnSearch, searchTerms = " + aSearchTerms + "\n");
+ RerootThreadPane();
+
+ if (aSearchTerms)
+ createSearchTermsWithList(aSearchTerms);
+ else
+ createSearchTerms();
+
+ gDBView.searchSession = gSearchSession;
+ try
+ {
+ gSearchSession.search(msgWindow);
+ }
+ catch(ex)
+ {
+ dump("Search Exception\n");
+ }
+}
+
+function createSearchTermsWithList(aTermsArray)
+{
+ var nsMsgSearchScope = Ci.nsMsgSearchScope;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var nsMsgSearchOp = Ci.nsMsgSearchOp;
+
+ gSearchSession.searchTerms.clear();
+ gSearchSession.clearScopes();
+
+ var i;
+ var selectedFolder = GetThreadPaneFolder();
+ if (gXFVirtualFolderTerms)
+ {
+ var msgDatabase = selectedFolder.msgDatabase;
+ if (msgDatabase)
+ {
+ var dbFolderInfo = msgDatabase.dBFolderInfo;
+ var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri");
+ viewDebug("createSearchTermsWithList xf vf scope = " + srchFolderUri + "\n");
+ var srchFolderUriArray = srchFolderUri.split('|');
+ for (i in srchFolderUriArray)
+ {
+ let realFolder = MailUtils.getFolderForURI(srchFolderUriArray[i]);
+ if (!realFolder.isServer)
+ gSearchSession.addScopeTerm(nsMsgSearchScope.offlineMail, realFolder);
+ }
+ }
+ }
+ else
+ {
+ viewDebug ("in createSearchTermsWithList, adding scope term for selected folder\n");
+ gSearchSession.addScopeTerm(nsMsgSearchScope.offlineMail, selectedFolder);
+ }
+
+ // Add each item in aTermsArray to the search session.
+ for (let term of aTermsArray) {
+ gSearchSession.appendTerm(term);
+ }
+}
+
+function createSearchTerms()
+{
+ var nsMsgSearchScope = Ci.nsMsgSearchScope;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var nsMsgSearchOp = Ci.nsMsgSearchOp;
+
+ // create an nsIMutableArray to store our search terms
+ var searchTermsArray = Cc["@mozilla.org/array;1"]
+ .createInstance(Ci.nsIMutableArray);
+ var selectedFolder = GetThreadPaneFolder();
+
+ // implement | for QS
+ // does this break if the user types "foo|bar" expecting to see subjects with that string?
+ // I claim no, since "foo|bar" will be a hit for "foo" || "bar"
+ // they just might get more false positives
+ var termList = gSearchInput.value.split("|");
+ for (var i = 0; i < termList.length; i ++)
+ {
+ // if the term is empty, skip it
+ if (termList[i] == "")
+ continue;
+
+ // create, fill, and append the subject term
+ var term = gSearchSession.createTerm();
+ var value = term.value;
+ value.str = termList[i];
+ term.value = value;
+ term.attrib = nsMsgSearchAttrib.Subject;
+ term.op = nsMsgSearchOp.Contains;
+ term.booleanAnd = false;
+ searchTermsArray.appendElement(term);
+
+ // create, fill, and append the AllAddresses term
+ term = gSearchSession.createTerm();
+ value = term.value;
+ value.str = termList[i];
+ term.value = value;
+ term.attrib = nsMsgSearchAttrib.AllAddresses;
+ term.op = nsMsgSearchOp.Contains;
+ term.booleanAnd = false;
+ searchTermsArray.appendElement(term);
+ }
+
+ // now append the default view or virtual folder criteria to the quick search
+ // so we don't lose any default view information
+ viewDebug("gDefaultSearchViewTerms = " + gDefaultSearchViewTerms + "gVirtualFolderTerms = " + gVirtualFolderTerms +
+ "gXFVirtualFolderTerms = " + gXFVirtualFolderTerms + "\n");
+ var defaultSearchTerms = (gDefaultSearchViewTerms || gVirtualFolderTerms || gXFVirtualFolderTerms);
+ if (defaultSearchTerms)
+ {
+ for (let searchTerm of defaultSearchTerms)
+ {
+ searchTermsArray.appendElement(searchTerm);
+ }
+ }
+
+ createSearchTermsWithList(searchTermsArray);
+
+ // now that we've added the terms, clear out our input array
+ searchTermsArray.clear();
+}
+
+function onSearchStop()
+{
+ gSearchSession.interruptSearch();
+}
+
+function onClearSearch()
+{
+ // Use the last focused element so that focus can be restored
+ // if it does not exist, try and get the thread tree instead
+ var focusedElement = gLastFocusedElement || GetThreadTree();
+ Search("");
+ focusedElement.focus();
+}
+
+function disableQuickSearchClearButton()
+{
+ if (gClearButton)
+ gClearButton.setAttribute("disabled", true); //going out of search disable clear button
+}
+
+function ClearQSIfNecessary()
+{
+ GetSearchInput();
+
+ if (gSearchInput.value == "")
+ return;
+
+ Search("");
+}
+
+function Search(str)
+{
+ GetSearchInput();
+
+ if (str != gSearchInput.value)
+ {
+ gQSViewIsDirty = true;
+ viewDebug("in Search(), setting gQSViewIsDirty true\n");
+ }
+
+ gSearchInput.value = str; //on input does not get fired for some reason
+ onEnterInSearchBar();
+}
diff --git a/comm/suite/mailnews/content/searchTermOverlay.xul b/comm/suite/mailnews/content/searchTermOverlay.xul
new file mode 100644
index 0000000000..cd3b1df635
--- /dev/null
+++ b/comm/suite/mailnews/content/searchTermOverlay.xul
@@ -0,0 +1,64 @@
+<?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 SYSTEM "chrome://messenger/locale/searchTermOverlay.dtd">
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://messenger/content/searchTerm.js"/>
+ <script src="chrome://messenger/content/dateFormat.js"/>
+
+ <vbox id="searchTermListBox">
+
+ <radiogroup id="booleanAndGroup" orient="horizontal" value="and"
+ oncommand="booleanChanged(event);">
+ <radio value="and" label="&matchAll.label;"
+ accesskey="&matchAll.accesskey;"/>
+ <radio value="or" label="&matchAny.label;"
+ accesskey="&matchAny.accesskey;"/>
+ <radio value="matchAll" id="matchAllItem" label="&matchAllMsgs.label;"
+ accesskey="&matchAllMsgs.accesskey;"/>
+ </radiogroup>
+
+ <hbox flex="1">
+ <hbox id="searchterms"/>
+ <listbox flex="1" id="searchTermList" rows="4" minheight="35%">
+ <listcols>
+ <listcol flex="&searchTermListAttributesFlexValue;"/>
+ <listcol flex="&searchTermListOperatorsFlexValue;"/>
+ <listcol flex="&searchTermListValueFlexValue;"/>
+ <listcol class="filler"/>
+ </listcols>
+
+ <!-- this is what the listitems will look like:
+ <listitem id="searchListItem">
+ <listcell allowevents="true">
+ <searchattribute id="searchAttr1" for="searchOp1,searchValue1" flex="1"/>
+ </listcell>
+ <listcell allowevents="true">
+ <searchoperator id="searchOp1" opfor="searchValue1" flex="1"/>
+ </listcell>
+ <listcell allowevents="true" >
+ <searchvalue id="searchValue1" flex="1"/>
+ </listcell>
+ <listcell>
+ <button label="add"/>
+ <button label="remove"/>
+ </listcell>
+ </listitem>
+ <listitem>
+ <listcell label="the.."/>
+ <listcell label="contains.."/>
+ <listcell label="text here"/>
+ <listcell label="+/-"/>
+ </listitem>
+ -->
+ </listbox>
+
+ </hbox>
+ </vbox>
+
+</overlay>
diff --git a/comm/suite/mailnews/content/start.xhtml b/comm/suite/mailnews/content/start.xhtml
new file mode 100644
index 0000000000..d9aaf6b790
--- /dev/null
+++ b/comm/suite/mailnews/content/start.xhtml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://messenger/skin/start.css" type="text/css"?>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % startDTD SYSTEM "chrome://messenger/locale/start.dtd" >
+%startDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>&startpage.title;</title>
+</head>
+
+<body>
+<h1>&headline.label;</h1>
+
+<div id="main">
+<p>&description.label;</p>
+<h2>&features.title;</h2>
+<ul>
+ <li>&feat_multiacc.label;</li>
+ <li>&feat_junk.label;</li>
+ <li>&feat_feeds.label;</li>
+ <li>&feat_filters.label;</li>
+ <li>&feat_htmlmsg.label;</li>
+ <li>&feat_abook.label;</li>
+ <li>&feat_tags.label;</li>
+ <li>&feat_integration.label;</li>
+</ul>
+<h2>&dict.title;</h2>
+<p>&dict_intro.label;</p>
+<p>&dict_info.label2;</p>
+<h2>&info.title;</h2>
+<p>&info_bugs.label2;</p>
+</div>
+
+<script>
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ // get vendor, dictionaries and release notes URLs from prefs
+ let formatter = Services.urlFormatter;
+ var vendorURL = formatter.formatURLPref("app.vendorURL");
+
+ if (vendorURL != "about:blank") {
+ var vendor = document.getElementById("vendorURL");
+ if (vendor)
+ vendor.setAttribute("href", vendorURL);
+ }
+
+ var dictURL = formatter.formatURLPref("spellchecker.dictionaries.download.url");
+ var dictionaries = document.getElementById("dictURL");
+ if (dictionaries)
+ dictionaries.setAttribute("href", dictURL);
+
+ var releaseNotesURL = formatter.formatURLPref("app.releaseNotesURL");
+ var relnotes = document.getElementById("releaseNotesURL");
+ if (relnotes)
+ relnotes.setAttribute("href", releaseNotesURL);
+</script>
+
+</body>
+</html>
diff --git a/comm/suite/mailnews/content/tabmail.js b/comm/suite/mailnews/content/tabmail.js
new file mode 100644
index 0000000000..694941ce92
--- /dev/null
+++ b/comm/suite/mailnews/content/tabmail.js
@@ -0,0 +1,969 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Traditionally, mailnews tabs come in two flavours: "folder" and
+// "message" tabs. But these modes are just mere default settings on tab
+// creation, defining layout, URI to load, etc.
+// The user can turn a "message" tab into a "folder" tab just by unhiding
+// the folder pane (F9) and hiding the message pane (F8), and vice versa.
+// Tab title and icon will change accordingly.
+// Both flavours are just instances of the basic "3pane" mode, triggered by
+// a bitwise or combination of these possible pane values:
+const kTabShowNoPane = 0;
+const kTabShowFolderPane = 1 << 0;
+const kTabShowMessagePane = 1 << 1;
+const kTabShowThreadPane = 1 << 2;
+const kTabShowAcctCentral = 1 << 3;
+// predefined mode masks
+const kTabMaskDisplayDeck = kTabShowThreadPane | kTabShowAcctCentral;
+// predefined traditional flavours
+const kTabModeFolder = kTabShowFolderPane | kTabShowThreadPane | kTabShowMessagePane;
+const kTabModeMessage = kTabShowMessagePane; // message tab
+
+
+// global mailnews tab definition object
+var gMailNewsTabsType =
+{
+ name: "mailnews",
+ panelId: "mailContent",
+
+ modes:
+ {
+ "3pane":
+ {
+ isDefault: true,
+ type: "3pane",
+
+ // aTabInfo belongs to the newly created tab,
+ // aArgs can contain:
+ // * modeBits is a combination of kTabShow* layout bits (or null),
+ // * folderURI designates the folder to select (or null)
+ // * msgHdr designates the message to select (or null)
+ openTab: function(aTabInfo, {modeBits: aModeBits, folderURI: aFolderURI,
+ msgHdr: aMsgHdr}) {
+ // clone the current 3pane state before overriding parts of it
+ this.saveTabState(aTabInfo);
+
+ // aModeBits must have at least one bit set
+ // if not, we just copy the current state
+ let cloneMode = !aModeBits;
+ if (cloneMode)
+ aModeBits = this.getCurrentModeBits() || kTabModeFolder;
+ aTabInfo.modeBits = aModeBits;
+ // Currently, we only check for kTabModeMessage vs. kTabModeFolder,
+ // but in theory we could distinguish in much more detail!
+ let messageId = null;
+ if (aModeBits == kTabModeMessage || cloneMode)
+ {
+ if (!aMsgHdr && gDBView)
+ {
+ try
+ {
+ // duplicate current message tab if nothing else is specified
+ aMsgHdr = gDBView.hdrForFirstSelectedMessage;
+ // Use the header's folder - this will open a msg in a virtual folder view
+ // in its real folder, which is needed if the msg wouldn't be in a new
+ // view with the same terms - e.g., it's read and the view is unread only.
+ // If we cloned the view, we wouldn't have to do this.
+ if (aTabInfo.switchToNewTab)
+ {
+ // Fix it so we won't try to load the previously loaded message.
+ aMsgHdr.folder.lastMessageLoaded = nsMsgKey_None;
+ }
+ aFolderURI = aMsgHdr.folder.URI;
+ }
+ catch (ex) {}
+ }
+ if (aMsgHdr)
+ messageId = aMsgHdr.messageId;
+ aTabInfo.clearSplitter = true;
+ }
+
+ if (!messageId)
+ {
+ // only sanitize the URL, if possible
+ let clearSplitter = aModeBits == kTabModeFolder;
+ if (!aFolderURI)
+ {
+ // Use GetSelectedMsgFolders() to find out which folder to open
+ // instead of GetLoadedMsgFolder().URI. This is required because on a
+ // right-click, the currentIndex value will be different from the
+ // actual row that is highlighted. GetSelectedMsgFolders() will
+ // return the message that is highlighted.
+ let msgFolder = GetSelectedMsgFolders()[0];
+ aFolderURI = msgFolder.URI;
+ // don't kill the splitter settings for account central
+ clearSplitter &= !msgFolder.isServer;
+ }
+ aMsgHdr = null;
+ aTabInfo.clearSplitter = clearSplitter;
+ }
+ aTabInfo.uriToOpen = aFolderURI;
+ aTabInfo.hdr = aMsgHdr;
+ aTabInfo.selectedMsgId = messageId;
+
+ // call superclass logic
+ this.openTab(aTabInfo);
+ },
+
+ // We can close all mailnews tabs - but one.
+ // Closing the last mailnews tab would destroy our mailnews functionality.
+ canCloseTab: function(aTabInfo)
+ {
+ return aTabInfo.mode.tabs.length > 1;
+ }
+ }
+ },
+
+ // combines the current pane visibility states into a mode bit mask
+ getCurrentModeBits: function()
+ {
+ let modeBits = kTabShowNoPane;
+ if (!IsFolderPaneCollapsed())
+ modeBits |= kTabShowFolderPane;
+ if (!IsDisplayDeckCollapsed())
+ {
+ // currently, the display deck has only two panes
+ if (gAccountCentralLoaded)
+ modeBits |= kTabShowAcctCentral;
+ else
+ modeBits |= kTabShowThreadPane;
+ }
+ if (!IsMessagePaneCollapsed())
+ modeBits |= kTabShowMessagePane;
+ return modeBits;
+ },
+
+ _updatePaneLayout: function(aTabInfo)
+ {
+ // first show all needed panes, then hide all unwanted ones
+ // (we have to keep this order to avoid hiding all panes!)
+ let showFolderPane = aTabInfo.modeBits & kTabShowFolderPane;
+ let showMessagePane = aTabInfo.modeBits & kTabShowMessagePane;
+ let showDisplayDeck = aTabInfo.modeBits & (kTabShowThreadPane | kTabShowAcctCentral);
+ if (showMessagePane && IsMessagePaneCollapsed())
+ MsgToggleMessagePane(true); // show message pane
+ if (showDisplayDeck && IsDisplayDeckCollapsed())
+ MsgToggleThreadPane(); // show thread pane
+ if (showFolderPane && IsFolderPaneCollapsed())
+ MsgToggleFolderPane(true); // show folder pane
+ if (!showMessagePane && !IsMessagePaneCollapsed())
+ MsgToggleMessagePane(true); // hide message pane
+ if (!showDisplayDeck && !IsDisplayDeckCollapsed())
+ MsgToggleThreadPane(); // hide thread pane
+ if (!showFolderPane && !IsFolderPaneCollapsed())
+ MsgToggleFolderPane(true); // hide folder pane
+ UpdateLayoutVisibility();
+ },
+
+ /**
+ * Create the new tab's state, which engenders some side effects.
+ * Part of our contract is that we leave the tab in the selected state.
+ */
+ openTab: function(aTabInfo)
+ {
+ // each tab gets its own messenger instance
+ // for undo/redo, backwards/forwards, etc.
+ messenger = Cc["@mozilla.org/messenger;1"]
+ .createInstance(Ci.nsIMessenger);
+ messenger.setWindow(window, msgWindow);
+ aTabInfo.messenger = messenger;
+
+ // remember the currently selected folder
+ aTabInfo.msgSelectedFolder = gMsgFolderSelected;
+
+ // show tab if permitted
+ if (aTabInfo.switchToNewTab)
+ this.showTab(aTabInfo);
+ },
+
+ showTab: function(aTabInfo)
+ {
+ // don't allow saveTabState while restoring a tab
+ aTabInfo.lock = true;
+ // set the messagepane as the primary browser for content
+ var messageBrowser = getMessageBrowser();
+ messageBrowser.setAttribute("type", "content");
+ messageBrowser.setAttribute("primary", "true");
+
+ if (aTabInfo.uriToOpen)
+ {
+ // HACK: Since we've switched away from the tab, we need to bring
+ // back the real selection before selecting the folder, so do that
+ RestoreSelectionWithoutContentLoad(document.getElementById("folderTree"));
+
+ // Clear selection, because context clicking on a folder and opening in a
+ // new tab needs to have selectFolder think the selection has changed.
+ gFolderTreeView.selection.clearSelection();
+ gFolderTreeView.selection.currentIndex = -1;
+ gMsgFolderSelected = null;
+ msgWindow.openFolder = null;
+
+ // clear gDBView so we won't try to close it
+ gDBView = null;
+
+ // reroot the message sink (we might have switched layout)
+ messenger.setWindow(null, null);
+ messenger.setWindow(window, msgWindow);
+
+ // Clear thread pane selection - otherwise, the tree tries to impose the
+ // the current selection on the new view.
+ let msgHdr = aTabInfo.hdr;
+ let msgId = aTabInfo.selectedMsgId;
+ aTabInfo.hdr = null;
+ aTabInfo.selectedMsgId = null;
+ aTabInfo.dbView = null;
+ let folder = MailUtils.getFolderForURI(aTabInfo.uriToOpen);
+ gFolderTreeView.selectFolder(folder);
+ gCurrentFolderToReroot = null;
+ delete aTabInfo.uriToOpen; // destroy after use!
+ // Store the folder that is being opened.
+ aTabInfo.msgSelectedFolder = folder;
+
+ // restore our message data
+ aTabInfo.hdr = msgHdr;
+ aTabInfo.selectedMsgId = msgId;
+
+ aTabInfo.dbView = gDBView;
+ UpdateMailToolbar("new tab");
+ }
+
+ // Do not bother with Thread and Message panes if at server level.
+ if (!aTabInfo.msgSelectedFolder.isServer) {
+ // Restore the layout if present.
+ ShowThreadPane();
+ // Some modes (e.g. new message tabs) need to initially hide the
+ // splitters, this is marked by aTabInfo.clearSplitter=true.
+ let clearSplitter = "clearSplitter" in aTabInfo && aTabInfo.clearSplitter;
+ if (clearSplitter) {
+ aTabInfo.messageSplitter.collapsible = true;
+ aTabInfo.folderSplitter.collapsible = true;
+ delete aTabInfo.clearSplitter;
+ }
+ SetSplitterState(GetThreadAndMessagePaneSplitter(),
+ aTabInfo.messageSplitter);
+ SetSplitterState(GetFolderPaneSplitter(),
+ aTabInfo.folderSplitter);
+ this._updatePaneLayout(aTabInfo);
+ ClearMessagePane();
+ // Force the header pane twisty state restoration by toggling from the
+ // opposite.
+ if (gCollapsedHeaderViewMode != aTabInfo.headerViewMode)
+ ToggleHeaderView();
+ }
+
+ // restore globals
+ messenger = aTabInfo.messenger;
+ gDBView = aTabInfo.dbView;
+ gSearchSession = aTabInfo.searchSession;
+ let folderToSelect = aTabInfo.msgSelectedFolder || gDBView && gDBView.msgFolder;
+
+ // restore view state if we had one
+ let row = gFolderTreeView.getIndexOfFolder(folderToSelect);
+ let treeBoxObj = document.getElementById("folderTree").treeBoxObject;
+ let folderTreeSelection = gFolderTreeView.selection;
+
+ // make sure that row.value is valid so that it doesn't mess up
+ // the call to ensureRowIsVisible()
+ if ((row >= 0) && !folderTreeSelection.isSelected(row))
+ {
+ gMsgFolderSelected = folderToSelect;
+ msgWindow.openFolder = folderToSelect;
+ folderTreeSelection.select(row);
+ treeBoxObj.ensureRowIsVisible(row);
+ }
+
+ if (gDBView)
+ {
+ // This sets the thread pane tree's view to the gDBView view.
+ UpdateSortIndicators(gDBView.sortType, gDBView.sortOrder);
+ RerootThreadPane();
+
+ // We don't want to reapply the mailview (threadpane changes by switching
+ // tabs alone would be rather surprising), just update the viewpicker
+ // and resave the new view.
+ UpdateViewPickerByValue(aTabInfo.mailView);
+ SetMailViewForFolder(folderToSelect, aTabInfo.mailView);
+
+ // restore quick search
+ GetSearchInput().value = aTabInfo.searchInput;
+
+ // We need to restore the selection to what it was when we switched away
+ // from this tab. We need to remember the selected keys, instead of the
+ // selected indices, since the view might have changed. But maybe the
+ // selectedIndices adjust as items are added/removed from the (hidden)
+ // view.
+ try
+ {
+ if (aTabInfo.selectedMsgId && aTabInfo.msgSelectedFolder)
+ {
+ // We clear the selection in order to generate an event when we
+ // re-select our message. This destroys aTabInfo.selectedMsgId.
+ let selectedMsgId = aTabInfo.selectedMsgId;
+ ClearThreadPaneSelection();
+ aTabInfo.selectedMsgId = selectedMsgId;
+ let msgDB = aTabInfo.msgSelectedFolder.msgDatabase;
+ let msgHdr = msgDB.getMsgHdrForMessageID(aTabInfo.selectedMsgId);
+ setTimeout(gDBView.selectFolderMsgByKey,
+ 0,
+ aTabInfo.msgSelectedFolder,
+ msgHdr.messageKey);
+ }
+ // We do not clear the selection if there was more than one message
+ // displayed. this leaves our selection intact. there was originally
+ // some claim that the selection might lose synchronization with the
+ // view, but this is unsubstantiated. said comment came from the
+ // original code that stored information on the selected rows, but
+ // then failed to do anything with it, probably because there is no
+ // existing API call that accomplishes it.
+ }
+ catch (ex)
+ {
+ dump(ex);
+ }
+ GetThreadTree().treeBoxObject.scrollToRow(aTabInfo.firstVisibleRow);
+ }
+ else if (gMsgFolderSelected.isServer)
+ {
+ // Load AccountCentral page here.
+ ShowAccountCentral(gMsgFolderSelected);
+ }
+ SetUpToolbarButtons(gMsgFolderSelected.URI);
+ UpdateMailToolbar("tab changed");
+ delete aTabInfo.lock;
+ },
+
+ closeTab: function(aTabInfo)
+ {
+ // If the tab has never been opened, we must not clean up the view,
+ // because it still belongs to a different tab.
+ if (aTabInfo.uriToOpen)
+ return;
+
+ if (aTabInfo.dbView)
+ aTabInfo.dbView.close();
+ if (aTabInfo.messenger)
+ aTabInfo.messenger.setWindow(null, null);
+ },
+
+ // called when switching away from aTabInfo
+ saveTabState: function(aTabInfo)
+ {
+ if (aTabInfo.lock)
+ return;
+
+ // save message db data and view filters
+ aTabInfo.messenger = messenger;
+ aTabInfo.dbView = gDBView;
+ aTabInfo.searchSession = gSearchSession;
+ aTabInfo.msgSelectedFolder = gMsgFolderSelected;
+ aTabInfo.selectedMsgId = null;
+ if (gDBView)
+ {
+ // save thread pane scroll position
+ aTabInfo.firstVisibleRow = GetThreadTree().treeBoxObject.getFirstVisibleRow();
+
+ let curMsgViewIndex = gDBView.currentlyDisplayedMessage;
+ if (curMsgViewIndex != nsMsgViewIndex_None)
+ {
+ try // there may not be a selected message.
+ {
+ // the currentlyDisplayedMessage is not always the first selected
+ // message, e.g. on a right click for the context menu
+ let curMsgHdr = gDBView.getMsgHdrAt(curMsgViewIndex);
+ aTabInfo.selectedMsgId = curMsgHdr.messageId;
+ }
+ catch (ex) {}
+ }
+ if (!aTabInfo.selectedMsgId)
+ aTabInfo.msgSelectedFolder = gDBView.msgFolder;
+ }
+ aTabInfo.mailView = GetMailViewForFolder(aTabInfo.msgSelectedFolder);
+
+ // remember layout
+ aTabInfo.modeBits = this.getCurrentModeBits();
+ aTabInfo.messageSplitter = GetSplitterState(GetThreadAndMessagePaneSplitter());
+ aTabInfo.folderSplitter = GetSplitterState(GetFolderPaneSplitter());
+
+ // header pane twisty state
+ aTabInfo.headerViewMode = gCollapsedHeaderViewMode;
+
+ // quick search
+ aTabInfo.searchInput = GetSearchInput().value;
+ },
+
+ onTitleChanged: function(aTabInfo, aTabNode)
+ {
+ // If we have an account, we also always have a "Local Folders" account,
+ let multipleRealAccounts = MailServices.accounts.accounts.length > 2;
+
+ // clear out specific tab data now, because we might need to return early
+ aTabNode.removeAttribute("SpecialFolder");
+ aTabNode.removeAttribute("ServerType");
+ aTabNode.removeAttribute("IsServer");
+ aTabNode.removeAttribute("IsSecure");
+ aTabNode.removeAttribute("NewMessages");
+ aTabNode.removeAttribute("ImapShared");
+ aTabNode.removeAttribute("BiffState");
+ aTabNode.removeAttribute("MessageType");
+ aTabNode.removeAttribute("Offline");
+ aTabNode.removeAttribute("Attachment");
+ aTabNode.removeAttribute("IMAPDeleted");
+
+ // aTabInfo.msgSelectedFolder may contain the base folder of saved search
+ let folder = null;
+ if (aTabInfo.uriToOpen)
+ {
+ // select folder for the backgound tab without changing the current one
+ // (stolen from SelectFolder)
+ folder = MailUtils.getFolderForURI(aTabInfo.uriToOpen);
+ }
+ else
+ {
+ folder = (aTabInfo.dbView && aTabInfo.dbView.viewFolder) ||
+ (aTabInfo.dbView && aTabInfo.dbView.msgFolder) ||
+ aTabInfo.msgSelectedFolder || gMsgFolderSelected;
+ }
+
+ // update the message header only if we're the current tab
+ if (aTabNode.selected)
+ {
+ try
+ {
+ aTabInfo.hdr = aTabInfo.dbView && aTabInfo.dbView.hdrForFirstSelectedMessage;
+ }
+ catch (e)
+ {
+ aTabInfo.hdr = null;
+ }
+ }
+
+ // update tab title and icon state
+ aTabInfo.title = "";
+ if (IsMessagePaneCollapsed() || !aTabInfo.hdr)
+ {
+ // Folder Tab
+ aTabNode.setAttribute("type", "folder"); // override "3pane"
+ if (!folder)
+ {
+ // nothing to do
+ return;
+ }
+ else
+ {
+ aTabInfo.title = folder.prettyName;
+ if (!folder.isServer && multipleRealAccounts)
+ aTabInfo.title += " - " + folder.server.prettyName;
+ }
+
+ // The user may have changed folders, triggering our onTitleChanged callback.
+ // Update the appropriate attributes on the tab.
+ aTabNode.setAttribute("SpecialFolder", FolderUtils.getSpecialFolderString(folder));
+ aTabNode.setAttribute("ServerType", folder.server.type);
+ aTabNode.setAttribute("IsServer", folder.isServer);
+ aTabNode.setAttribute("IsSecure", folder.server.isSecure);
+ aTabNode.setAttribute("NewMessages", folder.hasNewMessages);
+ aTabNode.setAttribute("ImapShared", folder.imapShared);
+
+ let biffState = "UnknownMail";
+ switch (folder.biffState)
+ {
+ case Ci.nsIMsgFolder.nsMsgBiffState_NewMail:
+ biffState = "NewMail";
+ break;
+ case Ci.nsIMsgFolder.nsMsgBiffState_NoMail:
+ biffState = "NoMail";
+ break;
+ }
+ aTabNode.setAttribute("BiffState", biffState);
+ }
+ else
+ {
+ // Message Tab
+ aTabNode.setAttribute("type", "message"); // override "3pane"
+ if (aTabInfo.hdr.flags & Ci.nsMsgMessageFlags.HasRe)
+ aTabInfo.title = "Re: ";
+ if (aTabInfo.hdr.mime2DecodedSubject)
+ aTabInfo.title += aTabInfo.hdr.mime2DecodedSubject;
+ aTabInfo.title += " - " + aTabInfo.hdr.folder.prettyName;
+ if (multipleRealAccounts)
+ aTabInfo.title += " - " + aTabInfo.hdr.folder.server.prettyName;
+
+ // message specific tab data
+ let flags = aTabInfo.hdr.flags;
+ aTabNode.setAttribute("MessageType", folder.server.type);
+ aTabNode.setAttribute("Offline",
+ Boolean(flags & Ci.nsMsgMessageFlags.Offline));
+ aTabNode.setAttribute("Attachment",
+ Boolean(flags & Ci.nsMsgMessageFlags.Attachment));
+ aTabNode.setAttribute("IMAPDeleted",
+ Boolean(flags & Ci.nsMsgMessageFlags.IMAPDeleted));
+ }
+ },
+
+ getBrowser: function(aTabInfo)
+ {
+ // we currently use the messagepane element for all 3pane tab types
+ return getMessageBrowser();
+ },
+
+ //
+ // nsIController implementation
+ //
+ // We ignore the aTabInfo parameter sent by tabmail when calling nsIController
+ // stuff and just delegate the call to the DefaultController by using it as
+ // our proto chain.
+ // XXX remove the MessageWindowController stuff once we kill messageWindow.xul
+ __proto__: "DefaultController" in window && window.DefaultController ||
+ "MessageWindowController" in window && window.MessageWindowController
+};
+
+
+
+//
+// tabmail support methods
+//
+
+function GetTabMail()
+{
+ return document.getElementById("tabmail");
+}
+
+function MsgOpenNewTab(aType, aModeBits, aBackground) {
+ // duplicate the current tab
+ var tabmail = GetTabMail();
+ if (tabmail)
+ tabmail.openTab(aType, {modeBits: aModeBits, background: aBackground});
+}
+
+function MsgOpenNewTabForFolder(aBackground) {
+ // open current folder in full 3pane tab
+ MsgOpenNewTab("3pane", kTabModeFolder, aBackground);
+}
+
+function MsgOpenNewTabForMessage(aBackground) {
+ // open current message in message tab
+ MsgOpenNewTab("3pane", kTabModeMessage, aBackground);
+}
+
+// A Thunderbird compatibility function called from e.g. newsblog.
+// We ignore aHandlerRegExp as it is not needed by SeaMonkey.
+function openContentTab(aUrl, aWhere, aHandlerRegExp)
+{
+ openUILinkIn(aUrl, aWhere);
+}
+
+function AllowOpenTabOnMiddleClick()
+{
+ return Services.prefs.getBoolPref("mail.tabs.opentabfor.middleclick");
+}
+
+function AllowOpenTabOnDoubleClick()
+{
+ return Services.prefs.getBoolPref("mail.tabs.opentabfor.doubleclick");
+}
+
+//
+// pane management
+// (maybe we should cache these items in a global object?)
+//
+
+function GetFolderPane()
+{
+ return document.getElementById("folderPaneBox");
+}
+
+function GetThreadPane()
+{
+ return document.getElementById("threadPaneBox");
+}
+
+function GetDisplayDeck()
+{
+ return document.getElementById("displayDeck");
+}
+
+function GetMessagePane()
+{
+ return document.getElementById("messagepanebox");
+}
+
+function GetHeaderPane()
+{
+ return document.getElementById("msgHeaderView");
+}
+
+function GetFolderPaneSplitter()
+{
+ return document.getElementById("folderpane-splitter");
+}
+
+function GetThreadAndMessagePaneSplitter()
+{
+ return document.getElementById("threadpane-splitter");
+}
+
+
+
+//
+// pane visibility management
+//
+// - collapsing the folderpane by clicking its splitter doesn't need
+// additional processing
+// - collapsing the messagepane by clicking its splitter needs some special
+// treatment of attachments, gDBView, etc.
+// - the threadpane has no splitter assigned to it
+// - collapsing the messagepane, threadpane or folderpane by <key> needs to
+// pay attention to the other panes' (and splitters') visibility
+
+function IsMessagePaneCollapsed()
+{
+ return GetMessagePane().collapsed;
+}
+
+function IsDisplayDeckCollapsed()
+{
+ // regard display deck as collapsed in the standalone message window
+ var displayDeck = GetDisplayDeck();
+ return !displayDeck || displayDeck.collapsed;
+}
+
+function IsFolderPaneCollapsed()
+{
+ // regard folderpane as collapsed in the standalone message window
+ var folderPane = GetFolderPane();
+ return !folderPane || folderPane.collapsed;
+}
+
+// Which state is the splitter in? Is it collapsed?
+// How wide/high is the associated pane?
+function GetSplitterState(aSplitter)
+{
+ var next = aSplitter.getAttribute("collapse") == "after";
+ var pane = next ? aSplitter.nextSibling : aSplitter.previousSibling;
+ var vertical = aSplitter.orient == "vertical";
+ var rv =
+ {
+ state: aSplitter.getAttribute("state"),
+ collapsed: aSplitter.collapsed,
+ // <splitter>s are <hbox>es,
+ // thus the "orient" attribute is usually either unset or "vertical"
+ size: vertical ? pane.height : pane.width,
+ collapsible: "collapsible" in aSplitter && aSplitter.collapsible
+ };
+ return rv;
+}
+
+function SetSplitterState(aSplitter, aState)
+{
+ // all settings in aState are optional
+ if (!aState)
+ return;
+ if ("state" in aState)
+ aSplitter.setAttribute("state", aState.state);
+ if ("collapsed" in aState)
+ aSplitter.collapsed = aState.collapsed;
+ if ("size" in aState)
+ {
+ let next = aSplitter.getAttribute("collapse") == "after";
+ let pane = next ? aSplitter.nextSibling : aSplitter.previousSibling;
+ let vertical = aSplitter.orient == "vertical";
+ if (vertical)
+ {
+ // vertical splitter orientation
+ pane.height = aState.size;
+ }
+ else
+ {
+ // horizontal splitter orientation
+ pane.width = aState.size;
+ }
+ }
+ if ("collapsible" in aState)
+ aSplitter.collapsible = aState.collapsible;
+}
+
+// If we hit one of the pane splitter <key>s or choose the respective menuitem,
+// we show/hide both the pane *and* the splitter, just like we do for the
+// browser sidebar. Clicking a splitter's grippy, though, will hide the pane
+// but not the splitter.
+function MsgToggleSplitter(aSplitter)
+{
+ var state = aSplitter.getAttribute("state");
+ if (state == "collapsed")
+ {
+ // removing the attribute would hurt persistency
+ aSplitter.setAttribute("state", "open");
+ aSplitter.collapsed = false; // always show splitter when open
+ }
+ else
+ {
+ aSplitter.setAttribute("state", "collapsed");
+ aSplitter.collapsed = true; // hide splitter
+ }
+}
+
+function MsgCollapseSplitter(aSplitter, aCollapse)
+{
+ if (!("collapsible" in aSplitter))
+ aSplitter.collapsible = true;
+ aSplitter.collapsed = aCollapse && aSplitter.collapsible;
+}
+
+// helper function for UpdateLayoutVisibility
+function UpdateFolderPaneFlex(aTuneLayout)
+{
+ var folderBox = GetFolderPane();
+ var messagesBox = document.getElementById("messagesBox");
+ if (aTuneLayout)
+ {
+ // tune folderpane layout
+ folderBox.setAttribute("flex", "1");
+ messagesBox.removeAttribute("flex");
+ }
+ else
+ {
+ // restore old layout
+ folderBox.removeAttribute("flex");
+ messagesBox.setAttribute("flex", "1");
+ }
+}
+
+// we need to finetune the pane and splitter layout in certain circumstances
+function UpdateLayoutVisibility()
+{
+ var modeBits = gMailNewsTabsType.getCurrentModeBits();
+ var folderPaneVisible = modeBits & kTabShowFolderPane;
+ var messagePaneVisible = modeBits & kTabShowMessagePane;
+ var threadPaneVisible = modeBits & kTabShowThreadPane;
+ var displayDeckVisible = modeBits & kTabMaskDisplayDeck;
+ var onlyFolderPane = modeBits == kTabShowFolderPane;
+ var onlyMessagePane = modeBits == kTabShowMessagePane;
+ var onlyDisplayDeck = modeBits == kTabShowThreadPane ||
+ modeBits == kTabShowAcctCentral;
+ var onlyOnePane = onlyFolderPane || onlyMessagePane || onlyDisplayDeck;
+ var showFolderSplitter = false;
+ var showMessageSplitter = false;
+ switch (Services.prefs.getIntPref("mail.pane_config.dynamic"))
+ {
+ case kClassicMailLayout:
+ // if only the folderpane is visible it has to flex,
+ // while the messagesbox must not
+ UpdateFolderPaneFlex(onlyFolderPane);
+ if (!onlyOnePane)
+ {
+ showFolderSplitter = folderPaneVisible;
+ showMessageSplitter = threadPaneVisible && messagePaneVisible;
+ }
+ break;
+
+ case kWideMailLayout:
+ // if only the messagepane is visible, collapse the rest
+ let messengerBox = document.getElementById("messengerBox");
+ messengerBox.collapsed = onlyMessagePane;
+ // a hidden displaydeck must not flex, while the folderpane has to
+ if (!onlyMessagePane)
+ UpdateFolderPaneFlex(!displayDeckVisible);
+ if (!onlyOnePane)
+ {
+ showFolderSplitter = folderPaneVisible && displayDeckVisible;
+ showMessageSplitter = messagePaneVisible;
+ }
+ break;
+
+ case kVerticalMailLayout:
+ // if the threadpane is hidden, we need to hide its outer box as well
+ let messagesBox = document.getElementById("messagesBox");
+ messagesBox.collapsed = !displayDeckVisible;
+ // if only the folderpane is visible, it needs to flex
+ UpdateFolderPaneFlex(onlyFolderPane);
+ if (!onlyOnePane)
+ {
+ showFolderSplitter = folderPaneVisible;
+ showMessageSplitter = messagePaneVisible;
+ }
+ break;
+ }
+
+ // set splitter visibility
+ // if the pane was hidden by clicking the splitter grippy,
+ // the splitter must not hide
+ MsgCollapseSplitter(GetFolderPaneSplitter(), !showFolderSplitter);
+ MsgCollapseSplitter(GetThreadAndMessagePaneSplitter(), !showMessageSplitter);
+
+ // disable location bar if only message pane is visible
+ document.getElementById("locationFolders").disabled = onlyMessagePane;
+ // disable mailviews and search if threadpane is invisible
+ if (!threadPaneVisible)
+ gDisableViewsSearch.setAttribute("disabled", true);
+ else
+ gDisableViewsSearch.removeAttribute("disabled");
+}
+
+function ChangeMessagePaneVisibility()
+{
+ var hidden = IsMessagePaneCollapsed();
+ // We also have to disable the Message/Attachments menuitem.
+ // It will be enabled when loading a message with attachments
+ // (see messageHeaderSink.handleAttachment).
+ if (hidden)
+ {
+ let node = document.getElementById("msgAttachmentMenu");
+ if (node)
+ node.setAttribute("disabled", "true");
+ }
+
+ if (gDBView)
+ {
+ // clear the subject, collapsing won't automatically do this
+ setTitleFromFolder(GetThreadPaneFolder(), null);
+ // the collapsed state is the state after we released the mouse
+ // so we take it as it is
+ gDBView.suppressMsgDisplay = hidden;
+ // set the subject, uncollapsing won't automatically do this
+ gDBView.loadMessageByUrl("about:blank");
+ gDBView.selectionChanged();
+ }
+
+ var event = new Event( "messagepane-"+ (hidden ? "hide" : "unhide"),
+ { bubbles: false, cancelable: true });
+ document.getElementById("messengerWindow").dispatchEvent(event);
+}
+
+function MsgToggleMessagePane(aToggleManually)
+{
+ // don't hide all three panes at once
+ if (IsDisplayDeckCollapsed() && IsFolderPaneCollapsed())
+ return;
+ // toggle the splitter manually if it wasn't clicked and remember that
+ var splitter = GetThreadAndMessagePaneSplitter();
+ if (aToggleManually)
+ MsgToggleSplitter(splitter);
+ splitter.collapsible = aToggleManually;
+ ChangeMessagePaneVisibility();
+ UpdateLayoutVisibility();
+}
+
+function MsgToggleFolderPane(aToggleManually)
+{
+ // don't hide all three panes at once
+ if (IsDisplayDeckCollapsed() && IsMessagePaneCollapsed())
+ return;
+ // toggle the splitter manually if it wasn't clicked and remember that
+ var splitter = GetFolderPaneSplitter();
+ if (aToggleManually)
+ MsgToggleSplitter(splitter);
+ splitter.collapsible = aToggleManually;
+ UpdateLayoutVisibility();
+}
+
+function MsgToggleThreadPane()
+{
+ // don't hide all three panes at once
+ if (IsFolderPaneCollapsed() && IsMessagePaneCollapsed())
+ return;
+ var threadPane = GetDisplayDeck();
+ threadPane.collapsed = !threadPane.collapsed;
+ // we only get here by hitting a key, so always hide border splitters
+ UpdateLayoutVisibility();
+}
+
+// When the ThreadPane is hidden via the displayDeck, we should collapse the
+// elements that are only meaningful to the thread pane. When AccountCentral is
+// shown via the displayDeck, we need to switch the displayDeck to show the
+// accountCentralBox and load the iframe in the AccountCentral box with the
+// corresponding page.
+function ShowAccountCentral(displayedFolder)
+{
+ GetDisplayDeck().selectedPanel = accountCentralBox;
+ let acctCentralPage = GetLocalizedStringPref("mailnews.account_central_page.url");
+ if (acctCentralPage) {
+ let loadURL =
+ acctCentralPage +
+ (displayedFolder ? "?folderURI=" + displayedFolder : "");
+ if (window.frames["accountCentralPane"].location.href != loadURL) {
+ window.frames["accountCentralPane"].location.href = loadURL;
+ }
+ } else {
+ dump("Error loading AccountCentral page\n");
+ }
+}
+
+function ShowThreadPane()
+{
+ GetDisplayDeck().selectedPanel = GetThreadPane();
+}
+
+function ShowingThreadPane()
+{
+ gDisableViewsSearch.removeAttribute("disabled");
+ var threadPaneSplitter = GetThreadAndMessagePaneSplitter();
+ threadPaneSplitter.collapsed = false;
+ if (!threadPaneSplitter.hidden && threadPaneSplitter.getAttribute("state") != "collapsed")
+ {
+ GetMessagePane().collapsed = false;
+ // XXX We need to force the tree to refresh its new height
+ // so that it will correctly scroll to the newest message
+ GetThreadTree().boxObject.height;
+ }
+ document.getElementById("key_toggleThreadPane").removeAttribute("disabled");
+ document.getElementById("key_toggleMessagePane").removeAttribute("disabled");
+}
+
+function HidingThreadPane()
+{
+ ClearThreadPane();
+ GetUnreadCountElement().hidden = true;
+ GetTotalCountElement().hidden = true;
+ GetMessagePane().collapsed = true;
+ GetThreadAndMessagePaneSplitter().collapsed = true;
+ gDisableViewsSearch.setAttribute("disabled", true);
+ document.getElementById("key_toggleThreadPane").setAttribute("disabled", "true");
+ document.getElementById("key_toggleMessagePane").setAttribute("disabled", "true");
+}
+
+var gCurrentDisplayDeckId = "";
+function ObserveDisplayDeckChange(aEvent)
+{
+ var selectedPanel = GetDisplayDeck().selectedPanel;
+ var nowSelected = selectedPanel ? selectedPanel.id : "";
+ // onselect fires for every mouse click inside the deck, so ObserveDisplayDeckChange
+ // is getting called every time we click on a message in the thread pane.
+ // Only show/hide elements if the selected deck is actually changing.
+ if (nowSelected != gCurrentDisplayDeckId)
+ {
+ if (nowSelected == "threadPaneBox")
+ ShowingThreadPane();
+ else
+ HidingThreadPane();
+
+ if (nowSelected == "accountCentralBox") {
+ if (!document.getElementById("folderPaneBox").collapsed)
+ document.getElementById("folderTree").focus();
+ gAccountCentralLoaded = true;
+ } else {
+ gAccountCentralLoaded = false;
+ }
+ gCurrentDisplayDeckId = nowSelected;
+ }
+}
+
+function InvalidateTabDBs()
+{
+ // enforce reloading the tab's dbView
+ var tabInfos = GetTabMail().tabInfo;
+ for (let i = 0; i < tabInfos.length; ++i)
+ {
+ let tabInfo = tabInfos[i];
+ // only reroot 3pane tabs
+ if (tabInfo.mode.type == "3pane")
+ {
+ // don't change URI if already set -
+ // we might try to read from an invalid msgSelectedFolder
+ if (!("uriToOpen" in tabInfo))
+ tabInfo.uriToOpen = tabInfo.msgSelectedFolder.URI;
+ }
+ }
+}
diff --git a/comm/suite/mailnews/content/tabmail.xml b/comm/suite/mailnews/content/tabmail.xml
new file mode 100644
index 0000000000..613bb3a418
--- /dev/null
+++ b/comm/suite/mailnews/content/tabmail.xml
@@ -0,0 +1,1583 @@
+<?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 bindings [
+ <!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
+ %messengerDTD;
+ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+]>
+
+<bindings id="tabmailBindings"
+ 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">
+
+ <!-- SeaMonkey's clone of Thunderbird's tab UI mechanism.
+ -
+ - We expect to be instantiated with the following children:
+ - * One "tabpanels" child element whose id must be placed in the
+ - "panelcontainer" attribute on the element we are being bound to. We do
+ - this because it is important to allow overlays to contribute panels.
+ - When we attempted to have the immediate children of the bound element
+ - be propagated through use of the "children" tag, we found that children
+ - contributed by overlays did not propagate.
+ - * Any children you want added to the right side of the tab bar. This is
+ - primarily intended to allow for "open a BLANK tab" buttons, namely
+ - calendar and tasks. For reasons similar to the tabpanels case, we
+ - expect the instantiating element to provide a child hbox for overlays
+ - to contribute buttons to.
+ -
+ - From a javascript perspective, there are three types of code that we
+ - expect to interact with:
+ - 1) Code that wants to open new tabs.
+ - 2) Code that wants to contribute one or more varieties of tabs.
+ - 3) Code that wants to monitor to know when the active tab changes.
+ -
+ - Consumer code should use the following methods:
+ - * openTab(aTabModeName, aArgs): Open a tab of the given "mode",
+ - passing the provided arguments as an object. The tab type author
+ - should tell you the modes they implement and the required/optional
+ - arguments.
+ - One of the arguments you can pass is "background": if this is true,
+ - the tab will be loaded in the background.
+ - * setTabTitle([aOptionalTabInfo]): Tells us that the title of the current
+ - tab (if no argument is provided) or provided tab needs to be updated.
+ - This will result in a call to the tab mode's logic to update the title.
+ - In the event this is not for the current tab, the caller is responsible
+ - for ensuring that the underlying tab mode is capable of providing a tab
+ - title when it is in the background.
+ - * removeCurrentTab(): Close the current tab.
+ - * removeTab(aTabElement): Close the tab whose tabmail-tab bound
+ - element is passed in.
+ - Changing the currently displayed tab is accomplished by changing
+ - tabmail.tabContainer's selectedIndex or selectedItem property.
+ -
+ - Tab contributing code should define a tab type object and register it
+ - with us by calling registerTabType. Each tab type can provide multiple
+ - tab modes. The rationale behind this organization is that Thunderbird
+ - historically/currently uses a single 3-pane view to display both
+ - three-pane folder browsing and single message browsing across multiple
+ - tabs. Each tab type has the ability to use a single tab panel for all
+ - of its display needs. So Thunderbird's "mail" tab type covers both the
+ - "folder" (3-pane folder-based browsing) and "message" (just a single
+ - message) tab modes, while SeaMonkey integrates both flavours into just
+ - one "3pane" mode. Likewise, calendar/lightning currently displays
+ - both its calendar and tasks in the same panel. A tab type can also
+ - create a new tabpanel for each tab as it is created. In that case, the
+ - tab type should probably only have a single mode unless there are a
+ - number of similar modes that can gain from code sharing.
+ - The tab type definition should include the following attributes:
+ - * name: The name of the tab-type, mainly to aid in debugging.
+ - * panelId or perTabPanel: If using a single tab panel, the id of the
+ - panel must be provided in panelId. If using one tab panel per tab,
+ - perTabPanel should be either the XUL element name that should be
+ - created for each tab, or a helper function to create and return the
+ - element.
+ - * modes: An object whose attributes are mode names (which are
+ - automatically propagated to a 'name' attribute for debugging) and
+ - values are objects with the following attributes...
+ - * any of the openTab/closeTab/saveTabState/showTab/onTitleChanged
+ - functions as described on the mode definitions. These will only be
+ - called if the mode does not provide the functions. Note that because
+ - the 'this' variable passed to the functions will always reference the
+ - tab type definition (rather than the mode definition), the mode
+ - functions can defer to the tab type functions by calling
+ - this.functionName(). (This should prove convenient.)
+ - Mode definition attributes:
+ - * type: The "type" attribute to set on the displayed tab for CSS purposes.
+ - Generally, this would be the same as the mode name, but you can do as
+ - you please.
+ - * isDefault: This should only be present and should be true for the tab
+ - mode that is the tab displayed automatically on startup.
+ - * maxTabs: The maximum number of this mode that can be opened at a time.
+ - If this limit is reached, any additional calls to openTab for this
+ - mode will simply result in the first existing tab of this mode being
+ - displayed.
+ - * shouldSwitchTo(aArgs): Optional function. Called when openTab is called
+ - on the top-level tabmail binding. It is used to decide if the openTab
+ - function should switch to an existing tab or actually open a new tab.
+ - If the openTab function should switch to an existing tab, return the
+ - index of that tab; otherwise return -1.
+ - aArgs is a set of named parameters (the ones that are later passed to
+ - openTab).
+ - * openTab(aTabInfo, aArgs): Called when a tab of the given mode is in the
+ - process of being opened. aTabInfo will have its "mode" attribute
+ - set to the mode definition of the tab mode being opened. You should
+ - set the "title" attribute on it, and may set any other attributes
+ - you wish for your own use in subsequent functions. Note that 'this'
+ - points to the tab type definition, not the mode definition as you
+ - might expect. This allows you to place common logic code on the
+ - tab type for use by multiple modes and to defer to it. Any arguments
+ - provided to the caller of tabmail.openTab will be passed to your
+ - function as well, including background.
+ - * canCloseTab(aTabInfo): Optional function.
+ - Return true (false) if the tab is (not) allowed to close.
+ - A tab's default permission is stored in aTabInfo.canClose.
+ - * closeTab(aTabInfo): Called when aTabInfo is being closed. The tab need
+ - not be currently displayed. You are responsible for properly cleaning
+ - up any state you preserved in aTabInfo.
+ - * saveTabState(aTabInfo): Called when aTabInfo is being switched away from
+ - so that you can preserve its state on aTabInfo. This is primarily for
+ - single tab panel implementations; you may not have much state to save
+ - if your tab has its own tab panel.
+ - * showTab(aTabInfo): Called when aTabInfo is being displayed and you
+ - should restore its state (if required).
+ - * onTitleChanged(aTabInfo): Called when someone calls
+ - tabmail.setTabTitle() to hint that the tab's title needs to be
+ - updated. This function should update aTabInfo.title if it can.
+ - * getBrowser(aTabInfo): This function should return the browser element
+ - for your tab if there is one (return null or don't define this
+ - function otherwise). It is used for some toolkit functions that
+ - require a global "getBrowser" function, e.g. ZoomManager.
+ -
+ - Mode definition functions for menu/toolbar commands (see nsIController):
+ - * supportsCommand(aCommand, aTabInfo): Called when a menu or toolbar needs
+ - to be updated. Return true if you support that command in
+ - isCommandEnabled and doCommand, return false otherwise.
+ - * isCommandEnabled(aCommand, aTabInfo): Called when a menu or toolbar
+ - needs to be updated. Return true if the command can be executed at the
+ - current time, false otherwise.
+ - * doCommand(aCommand, aTabInfo): Called when a menu or toolbar command is
+ - to be executed. Perform the action appropriate to the command.
+ - * onEvent(aEvent, aTabInfo): This can be used to handle different events
+ - on the window.
+ -
+ - Tab monitoring code is expected to be used for widgets on the screen
+ - outside of the tab box that need to update themselves as the active tab
+ - changes. This is primarily intended to be used for the ThunderBar; if
+ - you are not the ThunderBar and this sounds appealing to you, please
+ - solicit discussion on your needs on the mozilla.dev.apps.thunderbird
+ - newsgroup.
+ - Tab monitoring code (un)registers itself via (un)registerTabMonitor.
+ - The following functions should be provided on the monitor object:
+ - * onTabTitleChanged(aTabInfo): Called when the tab's title changes.
+ - * onTabSwitched(aTabInfo, aOldTabInfo): Called when a new tab is made
+ - active. If this is the first tab ever, aOldTabInfo will be null,
+ - otherwise aOldTabInfo will be the previously active tab.
+ -->
+ <binding id="tabmail"
+ extends="chrome://navigator/content/tabbrowser.xml#tabbrowser">
+ <resources>
+ <stylesheet src="chrome://navigator/skin/tabbrowser.css"/>
+ </resources>
+ <content>
+ <xul:stringbundle anonid="tmstringbundle" src="chrome://messenger/locale/tabmail.properties"/>
+ <xul:tabbox anonid="tabbox"
+ flex="1"
+ eventnode="document"
+ onselect="if (event.target.localName == 'tabs' &amp;&amp;
+ 'updateCurrentTab' in this.parentNode)
+ this.parentNode.updateCurrentTab();">
+ <xul:hbox class="tab-drop-indicator-bar" collapsed="true">
+ <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
+ </xul:hbox>
+ <xul:hbox class="tabbrowser-strip tabmail-strip"
+ tooltip="_child"
+ context="_child"
+ anonid="strip"
+ ondragstart="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
+ ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
+ ondrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
+ ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
+ <xul:tooltip onpopupshowing="var tabmail = this.parentNode.parentNode.parentNode;
+ return tabmail.FillTabmailTooltip(document, event);"/>
+ <xul:menupopup anonid="tabContextMenu"
+ onpopupshowing="return document.getBindingParent(this)
+ .onTabContextMenuShowing();">
+ <xul:menuitem label="&closeTabCmd.label;"
+ accesskey="&closeTabCmd.accesskey;"
+ oncommand="var tabmail = document.getBindingParent(this);
+ tabmail.removeTab(tabmail.mContextTab);"/>
+ </xul:menupopup>
+ <xul:tabs class="tabbrowser-tabs tabmail-tabs"
+ flex="1"
+ anonid="tabcontainer"
+ setfocus="false"
+ onclick="this.parentNode.parentNode.parentNode.onTabClick(event);">
+ <xul:tab selected="true"
+ validate="never"
+ type="3pane"
+ maxwidth="250"
+ width="0"
+ minwidth="100"
+ flex="100"
+ class="tabbrowser-tab tabmail-tab icon-holder"
+ crop="end"/>
+ </xul:tabs>
+ <children/>
+ </xul:hbox>
+ <!-- Remember, user of this binding, you need to provide tabpanels! -->
+ <children includes="tabpanels"/>
+ </xul:tabbox>
+ </content>
+
+ <implementation implements="nsIController, nsIObserver">
+ <constructor>
+ <![CDATA[
+ window.controllers.insertControllerAt(0, this);
+ const kAutoHide = "mail.tabs.autoHide";
+ this.mAutoHide = Services.prefs.getBoolPref(kAutoHide);
+ Services.prefs.addObserver(kAutoHide, this);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ Services.prefs.removeObserver("mail.tabs.autoHide", this);
+ window.controllers.removeController(this);
+ ]]>
+ </destructor>
+
+ <field name="currentTabInfo">
+ null
+ </field>
+
+ <field name="tabTypes" readonly="true">
+ new Object()
+ </field>
+
+ <field name="tabModes" readonly="true">
+ new Object()
+ </field>
+
+ <field name="defaultTabMode">
+ null
+ </field>
+
+ <field name="tabInfo" readonly="true">
+ new Array()
+ </field>
+
+ <field name="tabStrip" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "strip");
+ </field>
+
+ <field name="tabContainer" readonly="true">
+ document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
+ </field>
+
+ <field name="panelContainer" readonly="true">
+ document.getElementById(this.getAttribute("panelcontainer"));
+ </field>
+ <field name="tabs" readonly="true">
+ this.tabContainer.childNodes
+ </field>
+ <field name="mStringBundle">
+ document.getAnonymousElementByAttribute(this, "anonid", "tmstringbundle");
+ </field>
+ <field name="mContextTab">
+ null
+ </field>
+
+ <!-- _mAutoHide/mAutoHide reflect the current autoHide pref value -->
+ <field name="_mAutoHide">false</field>
+ <property name="mAutoHide" onget="return this._mAutoHide;">
+ <setter>
+ <![CDATA[
+ if (val != this._mAutoHide)
+ {
+ if (this.tabContainer.childNodes.length == 1)
+ this.mStripVisible = !val;
+ this._mAutoHide = val;
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <!-- mStripVisible reflects the actual XUL autoHide state -->
+ <property name="mStripVisible"
+ onget="return !this.tabStrip.collapsed;"
+ onset="return this.tabStrip.collapsed = !val;"/>
+
+ <method name="registerTabType">
+ <parameter name="aTabType"/>
+ <body>
+ <![CDATA[
+ if (aTabType.name in this.tabTypes)
+ return;
+ this.tabTypes[aTabType.name] = aTabType;
+ for (let [modeName, modeDetails] of Object.entries(aTabType.modes))
+ {
+ modeDetails.name = modeName;
+ modeDetails.tabType = aTabType;
+ modeDetails.tabs = [];
+ this.tabModes[modeName] = modeDetails;
+ if (modeDetails.isDefault)
+ this.defaultTabMode = modeDetails;
+ }
+ aTabType.panel = document.getElementById(aTabType.panelId);
+ ]]>
+ </body>
+ </method>
+
+ <field name="tabMonitors" readonly="true">
+ new Array()
+ </field>
+
+ <method name="registerTabMonitor">
+ <parameter name="aTabMonitor"/>
+ <body>
+ <![CDATA[
+ if (!this.tabMonitors.includes(aTabMonitor))
+ this.tabMonitors.push(aTabMonitor);
+ ]]>
+ </body>
+ </method>
+
+ <method name="unregisterTabMonitor">
+ <parameter name="aTabMonitor"/>
+ <body>
+ <![CDATA[
+ let index = this.tabMonitors.indexOf(aTabMonitor);
+ if (index >= 0)
+ this.tabMonitors.splice(index, 1);
+ ]]>
+ </body>
+ </method>
+
+ <method name="openFirstTab">
+ <body>
+ <![CDATA[
+ // From the moment of creation, our XBL binding already has a
+ // visible tab. We need to create a tab information structure for
+ // this tab. In the process we also generate a synthetic "tab title
+ // changed" event to ensure we have an accurate title.
+ // Note: for mail tabs, the title gets only set later when the
+ // folder or message is loaded, as we don't have a gDBView yet!
+ // We assume the tab contents will set themselves up correctly.
+ if (!this.tabInfo.length)
+ {
+ let firstTabInfo = {mode: this.defaultTabMode, canClose: true};
+ let firstTabNode = this.tabContainer.firstChild;
+ firstTabInfo.mode.tabs.push(firstTabInfo);
+ this.tabInfo[0] = this.currentTabInfo = firstTabInfo;
+ this.setTabTitle(firstTabInfo);
+ if (this.tabMonitors.length)
+ {
+ for (let tabMonitor of this.tabMonitors)
+ tabMonitor.onTabSwitched(firstTabInfo, null);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="openTab">
+ <parameter name="aTabModeName"/>
+ <parameter name="aArgs"/>
+ <body>
+ <![CDATA[
+ if (!aTabModeName)
+ aTabModeName = this.currentTabInfo.mode.type;
+
+ let tabMode = this.tabModes[aTabModeName];
+ // if we are already at our limit for this mode, show an existing one
+ if (tabMode.tabs.length == tabMode.maxTabs)
+ {
+ // show the first tab of this mode
+ this.tabContainer.selectedIndex = this.tabInfo.indexOf(tabMode.tabs[0]);
+ return;
+ }
+
+ // Do this so that we don't generate strict warnings.
+ let background = ("background" in aArgs) && aArgs.background;
+
+ // If the mode wants us to, we should switch to an existing tab
+ // rather than open a new one. We shouldn't switch to the tab if
+ // we're opening it in the background, though.
+ let shouldSwitchToFunc = tabMode.shouldSwitchTo ||
+ tabMode.tabType.shouldSwitchTo;
+
+ if (shouldSwitchToFunc)
+ {
+ let tabIndex = shouldSwitchToFunc.apply(tabMode.tabType, [aArgs]);
+ if (tabIndex >= 0)
+ {
+ if (!background)
+ this.selectTabByIndex(tabIndex);
+ return;
+ }
+ }
+
+ if (!background)
+ // we need to save the state before it gets corrupted
+ this.saveCurrentTabState();
+
+ let tabInfo = {mode: tabMode, canClose: true};
+ tabMode.tabs.push(tabInfo);
+
+ let t = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "tab");
+ t.setAttribute("crop", "end");
+ t.maxWidth = this.tabContainer.mTabMaxWidth;
+ t.minWidth = this.tabContainer.mTabMinWidth;
+ t.width = 0;
+ t.setAttribute("flex", "100");
+ t.setAttribute("validate", "never");
+ t.className = "tabbrowser-tab tabmail-tab icon-holder";
+ // for styling purposes, apply the type to the tab
+ // (this attribute may be overwritten by mode functions)
+ t.setAttribute("type", tabInfo.mode.type);
+ this.tabContainer.appendChild(t);
+ if (!this.mStripVisible)
+ {
+ this.mStripVisible = true;
+ this.tabContainer._updateCloseButtons();
+ }
+
+ let oldPanel = this.panelContainer.selectedPanel;
+
+ // Open new tabs in the background?
+ tabInfo.switchToNewTab = !background;
+
+ // the order of the following statements is important
+ let oldTabInfo = this.currentTabInfo;
+ this.tabInfo[this.tabContainer.childNodes.length - 1] = tabInfo;
+
+ if (!background) {
+ this.currentTabInfo = tabInfo;
+ // this has a side effect of calling updateCurrentTab, but our
+ // setting currentTabInfo above will cause it to take no action.
+ this.tabContainer.selectedIndex =
+ this.tabContainer.childNodes.length - 1;
+ }
+ // make sure we are on the right panel
+ let selectedPanel;
+ if (tabInfo.mode.tabType.perTabPanel)
+ {
+ // should we create the element for them, or will they do it?
+ if (typeof(tabInfo.mode.tabType.perTabPanel) == "string")
+ {
+ tabInfo.panel = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ tabInfo.mode.tabType.perTabPanel);
+ }
+ else
+ {
+ tabInfo.panel = tabInfo.mode.tabType.perTabPanel(tabInfo);
+ }
+ this.panelContainer.appendChild(tabInfo.panel);
+ selectedPanel = tabInfo.panel;
+ }
+ else
+ {
+ selectedPanel = tabInfo.mode.tabType.panel;
+ }
+ if (!background)
+ this.panelContainer.selectedPanel = selectedPanel;
+
+ oldPanel.removeAttribute("selected");
+ this.panelContainer.selectedPanel.setAttribute("selected", "true");
+
+ let tabOpenFunc = tabInfo.mode.openTab ||
+ tabInfo.mode.tabType.openTab;
+ if (tabOpenFunc)
+ tabOpenFunc.apply(tabInfo.mode.tabType, [tabInfo, aArgs]);
+
+ if (background) {
+ // if the new tab isn't made current,
+ // its title won't change automatically
+ this.setTabTitle(tabInfo);
+ }
+
+ if (!background && this.tabMonitors.length) {
+ for (let tabMonitor of this.tabMonitors)
+ tabMonitor.onTabSwitched(tabInfo, oldTabInfo);
+ }
+
+ t.setAttribute("label", tabInfo.title);
+
+ if (!background) {
+ let docTitle = tabInfo.title;
+ if (AppConstants.platform != "macosx") {
+ docTitle += " - " + gBrandBundle.getString("brandFullName");
+ }
+ document.title = docTitle;
+
+ // Update the toolbar status - we don't need to do menus as they
+ // do themselves when we open them.
+ UpdateMailToolbar("tabmail");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="selectTabByMode">
+ <parameter name="aTabModeName"/>
+ <body>
+ <![CDATA[
+ let tabMode = this.tabModes[aTabModeName];
+ if (tabMode.tabs.length)
+ {
+ let desiredTab = tabMode.tabs[0];
+ this.tabContainer.selectedIndex = this.tabInfo.indexOf(desiredTab);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="selectTabByIndex">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ // count backwards for aIndex < 0
+ if (aIndex < 0)
+ aIndex += this.tabInfo.length;
+ if (aIndex >= 0 &&
+ aIndex < this.tabInfo.length &&
+ aIndex != this.tabContainer.selectedIndex)
+ {
+ this.tabContainer.selectedIndex = aIndex;
+ }
+
+ if (aEvent)
+ {
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="closeTabs">
+ <body>
+ <![CDATA[
+ for (let i = 0; i < this.tabInfo.length; i++)
+ {
+ let tabInfo = this.tabInfo[i];
+ let tabCloseFunc = tabInfo.mode.closeTab ||
+ tabInfo.mode.tabType.closeTab;
+ if (tabCloseFunc)
+ tabCloseFunc.call(tabInfo.mode.tabType, tabInfo);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeTab">
+ <parameter name="aTabNode"/>
+ <!-- parameter name="aMoreParameters..."/-->
+ <body>
+ <![CDATA[
+ // Find and locate the tab in our list.
+ let iTab, numTabs = this.tabContainer.childNodes.length;
+ for (iTab = 0; iTab < numTabs; iTab++)
+ if (this.tabContainer.childNodes[iTab] == aTabNode)
+ break;
+ let tabInfo = this.tabInfo[iTab];
+
+ // ask the tab type implementation if we're allowed to close the tab
+ let canClose = tabInfo.canClose;
+ let canCloseFunc = tabInfo.mode.canCloseTab ||
+ tabInfo.mode.tabType.canCloseTab;
+ if (canCloseFunc)
+ canClose = canCloseFunc.call(tabInfo.mode.tabType, tabInfo);
+ if (!canClose)
+ return;
+
+ let closeFunc = tabInfo.mode.closeTab ||
+ tabInfo.mode.tabType.closeTab;
+ if (closeFunc)
+ closeFunc.call(tabInfo.mode.tabType, tabInfo);
+
+ this.tabInfo.splice(iTab, 1);
+ tabInfo.mode.tabs.splice(tabInfo.mode.tabs.indexOf(tabInfo), 1);
+ aTabNode.remove();
+ --numTabs;
+ if (this.tabContainer.selectedIndex == -1)
+ this.tabContainer.selectedIndex = (iTab == numTabs) ? iTab - 1 : iTab;
+ if (this.currentTabInfo == tabInfo)
+ this.updateCurrentTab();
+
+ if (tabInfo.panel)
+ {
+ tabInfo.panel.remove();
+ delete tabInfo.panel;
+ }
+ if (numTabs == 1 && this.mAutoHide)
+ this.mStripVisible = false;
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeCurrentTab">
+ <body>
+ <![CDATA[
+ this.removeTab(this.tabContainer.selectedItem);
+ ]]>
+ </body>
+ </method>
+
+ <!-- UpdateCurrentTab - called in response to changing the current tab -->
+ <method name="updateCurrentTab">
+ <body>
+ <![CDATA[
+ if (this.currentTabInfo != this.tabInfo[this.tabContainer.selectedIndex])
+ {
+ if (this.currentTabInfo)
+ this.saveCurrentTabState();
+ let oldTabInfo = this.currentTabInfo;
+ let oldPanel = this.panelContainer.selectedPanel;
+ let tabInfo = this.currentTabInfo = this.tabInfo[this.tabContainer.selectedIndex];
+ this.panelContainer.selectedPanel = tabInfo.panel ||
+ tabInfo.mode.tabType.panel;
+
+ // Update the selected attribute on the current and old tab panel.
+ oldPanel.removeAttribute("selected");
+ this.panelContainer.selectedPanel.setAttribute("selected", "true");
+
+ let showTabFunc = tabInfo.mode.showTab ||
+ tabInfo.mode.tabType.showTab;
+ if (showTabFunc)
+ showTabFunc.call(tabInfo.mode.tabType, tabInfo);
+ if (this.tabMonitors.length)
+ {
+ for (let tabMonitor of this.tabMonitors)
+ tabMonitor.onTabSwitched(tabInfo, oldTabInfo);
+ }
+
+ let docTitle = tabInfo.title;
+ if (AppConstants.platform != "macosx") {
+ docTitle += " - " + gBrandBundle.getString("brandFullName");
+ }
+ document.title = docTitle;
+
+ // Update the toolbar status - we don't need to do menus as they
+ // do themselves when we open them.
+ UpdateMailToolbar("tabmail");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="saveTabState">
+ <parameter name="aTabInfo"/>
+ <body>
+ <![CDATA[
+ if (!aTabInfo)
+ return;
+ let saveTabFunc = aTabInfo.mode.saveTabState ||
+ aTabInfo.mode.tabType.saveTabState;
+ if (saveTabFunc)
+ saveTabFunc.call(aTabInfo.mode.tabType, aTabInfo);
+ ]]>
+ </body>
+ </method>
+
+ <method name="saveCurrentTabState">
+ <body>
+ <![CDATA[
+ if (!this.currentTabInfo)
+ this.currentTabInfo = this.tabInfo[0];
+ // save the old tab state before we change the current tab
+ this.saveTabState(this.currentTabInfo);
+ ]]>
+ </body>
+ </method>
+
+ <method name="setTabTitle">
+ <parameter name="aTabInfo"/>
+ <body>
+ <![CDATA[
+ // First find the tab and its index.
+ let tabInfo;
+ let index;
+ if (aTabInfo)
+ {
+ tabInfo = aTabInfo;
+ for (index = 0; index < this.tabInfo.length; ++index)
+ {
+ if (tabInfo == this.tabInfo[index])
+ break;
+ }
+ }
+ else
+ {
+ index = this.tabContainer.selectedIndex;
+ tabInfo = this.tabInfo[index];
+ }
+
+ if (tabInfo)
+ {
+ let tabNode = this.tabContainer.childNodes[index];
+ let titleChangeFunc = tabInfo.mode.onTitleChanged ||
+ tabInfo.mode.tabType.onTitleChanged;
+ if (titleChangeFunc)
+ titleChangeFunc.call(tabInfo.mode.tabType, tabInfo, tabNode);
+ if (this.tabMonitors.length)
+ {
+ for (let tabMonitor of this.tabMonitors)
+ tabMonitor.onTabTitleChanged(tabInfo);
+ }
+ tabNode.setAttribute("label", tabInfo.title);
+
+ // Update the window title if we're the displayed tab.
+ if (index == this.tabContainer.selectedIndex)
+ {
+ let docTitle = tabInfo.title;
+ if (AppConstants.platform != "macosx") {
+ docTitle += " - " + gBrandBundle.getString("brandFullName");
+ }
+ document.title = docTitle;
+
+ // Update the toolbar status - we don't need to do menus as they
+ // do themselves when we open them.
+ UpdateMailToolbar("tabmail");
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="FillTabmailTooltip">
+ <parameter name="aDocument"/>
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ aEvent.stopPropagation();
+ let tn = aDocument.tooltipNode;
+ if (tn.localName != "tab")
+ return false; // Not a tab, so cancel the tooltip.
+ if (tn.hasAttribute("label"))
+ {
+ aEvent.target.setAttribute("label", tn.getAttribute("label"));
+ return true;
+ }
+ return false;
+ ]]>
+ </body>
+ </method>
+
+ <method name="onTabContextMenuShowing">
+ <body>
+ <![CDATA[
+ // The user might right-click on a non-tab area of the tab strip.
+ this.mContextTab = document.popupNode;
+ return this.mContextTab.localName == "tab";
+ ]]>
+ </body>
+ </method>
+
+ <!-- getBrowserForSelectedTab is required as some toolkit functions
+ require a getBrowser() function. -->
+ <method name="getBrowserForSelectedTab">
+ <body>
+ <![CDATA[
+ if (!this.currentTabInfo)
+ this.currentTabInfo = this.tabInfo[0];
+ let tabInfo = this.currentTabInfo;
+ let browserFunc = tabInfo.mode.getBrowser ||
+ tabInfo.mode.tabType.getBrowser;
+ if (!browserFunc)
+ return null;
+ return browserFunc.call(tabInfo.mode.tabType, tabInfo);
+ ]]>
+ </body>
+ </method>
+
+ <method name="_getTabForContentWindow">
+ <parameter name="aWindow"/>
+ <body>
+ <![CDATA[
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="getBrowserIndexForDocument">
+ <parameter name="aDocument"/>
+ <body>
+ <![CDATA[
+ return -1;
+ ]]>
+ </body>
+ </method>
+
+ <!-- nsIObserver implementation -->
+
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ const kAutoHide = "mail.tabs.autoHide";
+ if (aTopic == "nsPref:changed" && aData == kAutoHide)
+ this.mAutoHide = Services.prefs.getBoolPref(kAutoHide);
+ ]]>
+ </body>
+ </method>
+
+ <!-- nsIController implementation -->
+
+ <method name="supportsCommand">
+ <parameter name="aCommand"/>
+ <body>
+ <![CDATA[
+ // return early on startup when we haven't got a tab loaded yet
+ let tabInfo = this.currentTabInfo;
+ if (!tabInfo)
+ return false;
+
+ let supportsCommandFunc = tabInfo.mode.supportsCommand ||
+ tabInfo.mode.tabType.supportsCommand;
+ if (!supportsCommandFunc)
+ return false;
+ return supportsCommandFunc.call(tabInfo.mode.tabType,
+ aCommand,
+ tabInfo);
+ ]]>
+ </body>
+ </method>
+
+ <method name="isCommandEnabled">
+ <parameter name="aCommand"/>
+ <body>
+ <![CDATA[
+ // return early on startup when we haven't got a tab loaded yet
+ let tabInfo = this.currentTabInfo;
+ if (!tabInfo)
+ return false;
+
+ let isCommandEnabledFunc = tabInfo.mode.isCommandEnabled ||
+ tabInfo.mode.tabType.isCommandEnabled;
+ if (!isCommandEnabledFunc)
+ return false;
+ return isCommandEnabledFunc.call(tabInfo.mode.tabType,
+ aCommand,
+ tabInfo);
+ ]]>
+ </body>
+ </method>
+
+ <method name="doCommand">
+ <parameter name="aCommand"/>
+ <body>
+ <![CDATA[
+ // return early on startup when we haven't got a tab loaded yet
+ let tabInfo = this.currentTabInfo;
+ if (!tabInfo)
+ return;
+
+ let doCommandFunc = tabInfo.mode.doCommand ||
+ tabInfo.mode.tabType.doCommand;
+ if (!doCommandFunc)
+ return;
+ doCommandFunc.call(tabInfo.mode.tabType,
+ aCommand,
+ tabInfo);
+ ]]>
+ </body>
+ </method>
+
+ <method name="onEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ // return early on startup when we haven't got a tab loaded yet
+ let tabInfo = this.currentTabInfo;
+ if (!tabInfo)
+ return;
+
+ let onEventFunc = tabInfo.mode.onEvent ||
+ tabInfo.mode.tabType.onEvent;
+ if (!onEventFunc)
+ return;
+
+ onEventFunc.call(tabInfo.mode.tabType, aCommand, tabInfo);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="tabmail-tab"
+ display="xul:box"
+ extends="chrome://global/content/bindings/tabbox.xml#tab">
+ <content closetabtext="&tabmailClose.label;">
+ <xul:hbox class="tab-middle box-inherit"
+ xbl:inherits="align,dir,pack,orient,selected"
+ flex="1">
+ <xul:image class="tab-icon tab-icon-image" xbl:inherits="validate,src=image"/>
+ <xul:label class="tab-text"
+ xbl:inherits="value=label,accesskey,crop,disabled"
+ flex="1"/>
+ </xul:hbox>
+ <xul:toolbarbutton anonid="close-button"
+ tooltiptext="&tabmailClose.tooltip;"
+ tabindex="-1"
+ class="tabs-closebutton tab-close-button"/>
+ </content>
+
+ <implementation>
+ <field name="mCorrespondingMenuitem">null</field>
+ </implementation>
+ </binding>
+
+ <binding id="tabmail-arrowscrollbox"
+ extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
+ <content>
+ <xul:toolbarbutton class="scrollbutton-up tab-scrollbutton-up"
+ collapsed="true"
+ xbl:inherits="orient"
+ anonid="scrollbutton-up"
+ onmousedown="_startScroll(-1);"
+ onmouseup="_stopScroll();"
+ onmouseout="_stopScroll();"/>
+ <xul:scrollbox xbl:inherits="orient,align,pack,dir"
+ flex="1"
+ anonid="scrollbox">
+ <children/>
+ </xul:scrollbox>
+ <xul:stack align="center" pack="end" class="scrollbutton-down-stack">
+ <xul:hbox flex="1"
+ class="scrollbutton-down-box"
+ collapsed="true"
+ anonid="down-box"/>
+ <xul:hbox flex="1"
+ class="scrollbutton-down-box-animate"
+ collapsed="true"
+ anonid="down-box-animate"/>
+ <xul:toolbarbutton class="scrollbutton-down tab-scrollbutton-down"
+ collapsed="true"
+ xbl:inherits="orient"
+ anonid="scrollbutton-down"
+ onmousedown="_startScroll(1);"
+ onmouseup="_stopScroll();"
+ onmouseout="_stopScroll();"/>
+ </xul:stack>
+ </content>
+
+ <implementation>
+ <field name="_scrollButtonDownBox">
+ document.getAnonymousElementByAttribute(this, "anonid", "down-box");
+ </field>
+ <field name="_scrollButtonDownBoxAnimate">
+ document.getAnonymousElementByAttribute(this, "anonid", "down-box-animate");
+ </field>
+ </implementation>
+
+ <handlers>
+ <handler event="underflow" phase="target">
+ <![CDATA[
+ // Ignore vertical events.
+ if (event.detail == 0)
+ return;
+ this._scrollButtonDownBox.collapsed = true;
+ this._scrollButtonDownBoxAnimate.collapsed = true;
+ ]]>
+ </handler>
+
+ <handler event="overflow" phase="target">
+ <![CDATA[
+ // Ignore vertical events.
+ if (event.detail == 0)
+ return;
+ this._scrollButtonDownBox.collapsed = false;
+ this._scrollButtonDownBoxAnimate.collapsed = false;
+ ]]>
+ </handler>
+
+ <handler event="UpdatedScrollButtonsDisabledState">
+ <![CDATA[
+ // filter underflow events which were dispatched on nested scrollboxes
+ if (event.target != this)
+ return;
+
+ // fix for bug #352353
+ // unlike the scrollup button on the tab strip (which is a
+ // simple toolbarbutton) the scrolldown button is
+ // a more complicated stack of boxes and a toolbarbutton
+ // so that we can animate when a tab is opened offscreen.
+ // in order to style the box with the actual background image
+ // we need to manually set the disable state to match the
+ // disable state of the toolbarbutton.
+ this._scrollButtonDownBox
+ .setAttribute("disabled", this._scrollButtonDown.disabled);
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="tabmail-tabs"
+ extends="chrome://global/content/bindings/tabbox.xml#tabs">
+ <content>
+ <xul:stack flex="1" class="tabs-stack">
+ <xul:vbox>
+ <xul:spacer flex="1"/>
+ <xul:hbox class="tabs-bottom" align="center"/>
+ </xul:vbox>
+ <xul:stack>
+ <xul:spacer class="tabs-left tabs-right"/>
+ <xul:hbox>
+ <xul:hbox class="tabs-newbutton-box"
+ pack="start"
+ anonid="tabstrip-newbutton">
+ <xul:toolbarbutton class="new-button tabs-newbutton"
+ tooltiptext="&tabmailNewButton.tooltip;"/>
+ </xul:hbox>
+ <xul:arrowscrollbox anonid="arrowscrollbox"
+ class="tabbrowser-arrowscrollbox tabmail-arrowscrollbox"
+ flex="1"
+ xbl:inherits="smoothscroll"
+ orient="horizontal"
+ style="min-width: 1px;">
+ <children includes="tab"/>
+ </xul:arrowscrollbox>
+ <children/>
+ <xul:hbox class="tabs-closebutton-box"
+ align="center"
+ pack="end"
+ anonid="tabstrip-closebutton">
+ <xul:toolbarbutton class="close-button tabs-closebutton"
+ tooltiptext="&tabmailCloseButton.tooltip;"/>
+ </xul:hbox>
+ <xul:stack align="center" pack="end" class="tabs-alltabs-stack">
+ <xul:hbox flex="1" class="tabs-alltabs-box" anonid="alltabs-box"/>
+ <xul:hbox flex="1"
+ class="tabs-alltabs-box-animate"
+ anonid="alltabs-box-animate"/>
+ <xul:toolbarbutton class="tabs-alltabs-button"
+ type="menu"
+ anonid="alltabs-button"
+ tooltipstring="&tabmailAllTabs.tooltip;">
+ <xul:menupopup class="tabs-alltabs-popup"
+ anonid="alltabs-popup"
+ position="after_end"/>
+ </xul:toolbarbutton>
+ </xul:stack>
+ </xul:hbox>
+ </xul:stack>
+ </xul:stack>
+ </content>
+
+ <implementation implements="nsITimerCallback, nsIDOMEventListener, nsIObserver">
+ <constructor>
+ <![CDATA[
+ this.mTabMinWidth = Services.prefs.getIntPref ("browser.tabs.tabMinWidth");
+ this.mTabMaxWidth = Services.prefs.getIntPref ("browser.tabs.tabMaxWidth");
+ this.mTabClipWidth = Services.prefs.getIntPref ("browser.tabs.tabClipWidth");
+ this.mCloseButtons = Services.prefs.getIntPref ("browser.tabs.closeButtons");
+ this.firstChild.minWidth = this.mTabMinWidth;
+ this.firstChild.maxWidth = this.mTabMaxWidth;
+ this._updateCloseButtons();
+ Services.prefs.addObserver("browser.tabs.", this);
+ window.addEventListener("resize", this);
+
+ // Listen to overflow/underflow events on the tabstrip,
+ // we cannot put these as xbl handlers on the entire binding because
+ // they would also get called for the all-tabs popup scrollbox.
+ // Also, we can't rely on event.target because these are all
+ // anonymous nodes.
+ this.arrowScrollbox.addEventListener("overflow", this);
+ this.arrowScrollbox.addEventListener("underflow", this);
+ ]]>
+ </constructor>
+
+ <destructor>
+ <![CDATA[
+ Services.prefs.removeObserver("browser.tabs.", this);
+
+ // Release timer to avoid reference cycles.
+ if (this._animateTimer)
+ {
+ this._animateTimer.cancel();
+ this._animateTimer = null;
+ }
+ this.arrowScrollbox.removeEventListener("overflow", this);
+ this.arrowScrollbox.removeEventListener("underflow", this);
+ ]]>
+ </destructor>
+
+ <field name="arrowScrollboxWidth">0</field>
+
+ <field name="arrowScrollbox">
+ document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
+ </field>
+
+ <field name="arrowScrollboxClosebutton">
+ document.getAnonymousElementByAttribute(this, "anonid", "tabstrip-closebutton");
+ </field>
+
+ <field name="mTabMinWidth">100</field>
+ <field name="mTabMaxWidth">250</field>
+ <field name="mTabClipWidth">140</field>
+ <field name="mCloseButtons">3</field>
+ <method name="_updateCloseButtons">
+ <body>
+ <![CDATA[
+ // modes for tabstrip
+ // 0 - activetab = close button on active tab only
+ // 1 - alltabs = close buttons on all tabs
+ // 2 - noclose = no close buttons at all
+ // 3 - closeatend = close button at the end of the tabstrip
+ switch (this.mCloseButtons)
+ {
+ case 0:
+ this.setAttribute("closebuttons", "activetab");
+ break;
+ case 1:
+ let width = this.firstChild.boxObject.width;
+ // 0 width is an invalid value and indicates
+ // an item without display, so ignore.
+ if (width > this.mTabClipWidth || width == 0)
+ this.setAttribute("closebuttons", "alltabs");
+ else
+ this.setAttribute("closebuttons", "activetab");
+ break;
+ case 2:
+ this.setAttribute("closebuttons", "noclose");
+ break;
+ case 3:
+ this.setAttribute("closebuttons", "closeatend");
+ break;
+ }
+ this.arrowScrollboxClosebutton.collapsed = this.mCloseButtons != 3;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_handleTabSelect">
+ <body>
+ <![CDATA[
+ this.arrowScrollbox.ensureElementIsVisible(this.selectedItem);
+ ]]>
+ </body>
+ </method>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ switch (aEvent.type)
+ {
+ case "overflow":
+ this.setAttribute("overflow", "true");
+ this.arrowScrollbox.scrollBoxObject
+ .ensureElementIsVisible(this.selectedItem);
+ break;
+ case "underflow":
+ this.removeAttribute("overflow");
+ break;
+ case "resize":
+ let width = this.arrowScrollbox.boxObject.width;
+ if (width != this.arrowScrollboxWidth)
+ {
+ this._updateCloseButtons();
+ // XXX without this line the tab bar won't budge
+ this.arrowScrollbox.scrollByPixels(1);
+ this._handleTabSelect();
+ this.arrowScrollboxWidth = width;
+ }
+ break;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <field name="mAllTabsPopup">
+ document.getAnonymousElementByAttribute(this, "anonid", "alltabs-popup");
+ </field>
+
+ <field name="mAllTabsBoxAnimate">
+ document.getAnonymousElementByAttribute(this, "anonid", "alltabs-box-animate");
+ </field>
+
+ <field name="mDownBoxAnimate">
+ this.arrowScrollbox._scrollButtonDownBoxAnimate;
+ </field>
+
+ <field name="mAllTabsButton">
+ document.getAnonymousElementByAttribute(this, "anonid", "alltabs-button");
+ </field>
+
+ <field name="_animateTimer">null</field>
+ <field name="_animateStep">-1</field>
+ <field name="_animateDelay">25</field>
+ <field name="_animatePercents">
+ [1.00, 0.85, 0.80, 0.75, 0.71, 0.68, 0.65, 0.62, 0.59, 0.57,
+ 0.54, 0.52, 0.50, 0.47, 0.45, 0.44, 0.42, 0.40, 0.38, 0.37,
+ 0.35, 0.34, 0.32, 0.31, 0.30, 0.29, 0.28, 0.27, 0.26, 0.25,
+ 0.24, 0.23, 0.23, 0.22, 0.22, 0.21, 0.21, 0.21, 0.20, 0.20,
+ 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.19, 0.19, 0.19, 0.18,
+ 0.18, 0.17, 0.17, 0.16, 0.15, 0.14, 0.13, 0.11, 0.09, 0.06]
+ </field>
+
+ <method name="_stopAnimation">
+ <body>
+ <![CDATA[
+ if (this._animateStep != -1)
+ {
+ if (this._animateTimer)
+ this._animateTimer.cancel();
+
+ this._animateStep = -1;
+ this.mAllTabsBoxAnimate.style.opacity = 0.0;
+ this.mDownBoxAnimate.style.opacity = 0.0;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_notifyBackgroundTab">
+ <parameter name="aTabNode"/>
+ <body>
+ <![CDATA[
+ let tsbo = this.arrowScrollbox.scrollBoxObject;
+ let tsboStart = tsbo.screenX;
+ let tsboEnd = tsboStart + tsbo.width;
+ let ctbo = aTabNode.boxObject;
+ let ctboStart = ctbo.screenX;
+ let ctboEnd = ctboStart + ctbo.width;
+
+ // only start the flash timer if the new tab (which was loaded in
+ // the background) is not completely visible
+ if (tsboStart > ctboStart || ctboEnd > tsboEnd)
+ {
+ this._animateStep = 0;
+
+ if (!this._animateTimer)
+
+ this._animateTimer =
+ Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ else
+ this._animateTimer.cancel();
+
+ this._animateTimer.initWithCallback(this,
+ this._animateDelay,
+ Ci.nsITimer.TYPE_REPEATING_SLACK);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="notify">
+ <parameter name="aTimer"/>
+ <body>
+ <![CDATA[
+ if (!document)
+ aTimer.cancel();
+
+ let percent = this._animatePercents[this._animateStep];
+ this.mAllTabsBoxAnimate.style.opacity = percent;
+ this.mDownBoxAnimate.style.opacity = percent;
+
+ if (this._animateStep < (this._animatePercents.length - 1))
+ this._animateStep++;
+ else
+ this._stopAnimation();
+ ]]>
+ </body>
+ </method>
+
+ <!-- nsIObserver implementation -->
+
+ <method name="observe">
+ <parameter name="aSubject"/>
+ <parameter name="aTopic"/>
+ <parameter name="aData"/>
+ <body>
+ <![CDATA[
+ const kCloseButtons = "browser.tabs.closeButtons";
+ if (aTopic == "nsPref:changed" && aData == kCloseButtons)
+ {
+ this.mCloseButtons = Services.prefs.getIntPref(kCloseButtons);
+ this._updateCloseButtons();
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="TabSelect" action="this._handleTabSelect();"/>
+
+ <handler event="mouseover">
+ <![CDATA[
+ if (event.originalTarget == this.mAllTabsButton)
+ {
+ this.mAllTabsButton
+ .setAttribute("tooltiptext",
+ this.mAllTabsButton.getAttribute("tooltipstring"));
+ }
+ else
+ {
+ this.mAllTabsButton.removeAttribute("tooltiptext");
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <!-- alltabs-popup binding
+ This binding relies on the structure of the tabbrowser binding.
+ Therefore it should only be used as a child of the tabs element.
+ This binding is exposed as a pseudo-public-API so themes can customize
+ the tabbar appearance without having to be scriptable
+ (see globalBindings.xml in osx for example).
+ -->
+ <binding id="tabmail-alltabs-popup"
+ extends="chrome://global/content/bindings/popup.xml#popup">
+ <implementation implements="nsIDOMEventListener">
+ <method name="_tabOnTabClose">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ let menuItem = aEvent.target.mCorrespondingMenuitem;
+ if (menuItem)
+ menuItem.remove();
+ ]]>
+ </body>
+ </method>
+
+ <method name="handleEvent">
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ switch (aEvent.type)
+ {
+ case "TabClose":
+ this._tabOnTabClose(aEvent);
+ break;
+ case "TabOpen":
+ this._createTabMenuItem(aEvent.originalTarget);
+ break;
+ case "scroll":
+ this._updateTabsVisibilityStatus();
+ break;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_updateTabsVisibilityStatus">
+ <body>
+ <![CDATA[
+ let tabContainer = document.getBindingParent(this);
+ let tabstripBO = tabContainer.arrowScrollbox.scrollBoxObject;
+
+ for (let i = 0; i < this.childNodes.length; i++)
+ {
+ let curTabBO = this.childNodes[i].tab.boxObject;
+ if (curTabBO.screenX >= tabstripBO.screenX &&
+ curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
+ this.childNodes[i].removeAttribute("tabIsScrolled");
+ else
+ this.childNodes[i].setAttribute("tabIsScrolled", "true");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_createTabMenuItem">
+ <parameter name="aTabNode"/>
+ <body>
+ <![CDATA[
+ let menuItem = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "menuitem");
+ menuItem.setAttribute("class", "menuitem-iconic alltabs-item icon-holder");
+ menuItem.setAttribute("label", aTabNode.label);
+ menuItem.setAttribute("crop", aTabNode.getAttribute("crop"));
+ menuItem.setAttribute("image", aTabNode.getAttribute("image"));
+
+ let attributes = ["busy", "selected", "type", "NewMessages", "ServerType",
+ "SpecialFolder", "ImapShared", "BiffState", "IsServer",
+ "IsSecure", "Attachment", "IMAPDeleted", "Offline",
+ "MessageType"];
+
+ attributes.forEach(
+ function(attribute)
+ {
+ if (aTabNode.hasAttribute(attribute))
+ {
+ menuItem.setAttribute(attribute, aTabNode.getAttribute(attribute));
+ }
+ }
+ );
+
+ // Keep some attributes of the menuitem in sync with its
+ // corresponding tab (e.g. the tab label)
+ aTabNode.mCorrespondingMenuitem = menuItem;
+ document.addBroadcastListenerFor(aTabNode, menuItem, "label");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "crop");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "image");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "busy");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "selected");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "NewMessages");
+ document.addBroadcastListenerFor(aTabNode, menuItem, "BiffState");
+ aTabNode.addEventListener("TabClose", this);
+ menuItem.tab = aTabNode;
+ menuItem.addEventListener("command", this);
+ this.appendChild(menuItem);
+ return menuItem;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="popupshowing">
+ <![CDATA[
+ // set up the menu popup
+ let tabcontainer = document.getBindingParent(this);
+ let tabs = tabcontainer.childNodes;
+
+ // Listen for changes in the tab bar.
+ let tabbrowser = document.getBindingParent(tabcontainer);
+ tabbrowser.addEventListener("TabOpen", this);
+ tabcontainer.arrowScrollbox.addEventListener("scroll", this);
+
+ // if an animation is in progress and the user
+ // clicks on the "all tabs" button, stop the animation
+ tabcontainer._stopAnimation();
+
+ for (let i = 0; i < tabs.length; i++)
+ this._createTabMenuItem(tabs[i]);
+ this._updateTabsVisibilityStatus();
+ ]]>
+ </handler>
+
+ <handler event="popuphiding">
+ <![CDATA[
+ // clear out the menu popup and remove the listeners
+ while (this.hasChildNodes())
+ {
+ let menuItem = this.lastChild;
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "label");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "crop");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "image");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "busy");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "selected");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "NewMessages");
+ document.removeBroadcastListenerFor(menuItem.tab, menuItem, "BiffState");
+ menuItem.removeEventListener("command", this);
+ menuItem.tab.removeEventListener("TabClose", this);
+ menuItem.tab.mCorrespondingMenuitem = null;
+ menuItem.remove();
+ }
+ let tabcontainer = document.getBindingParent(this);
+ tabcontainer.arrowScrollbox.removeEventListener("scroll", this);
+ document.getBindingParent(tabcontainer).removeEventListener("TabOpen", this);
+ ]]>
+ </handler>
+
+ <handler event="command">
+ <![CDATA[
+ let tabcontainer = document.getBindingParent(this);
+ tabcontainer.selectedItem = event.target.tab;
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <!-- new-tab-button/close-tab-button binding
+ These bindings rely on the structure of the tabbrowser binding.
+ Therefore they should only be used as a child of the tab or the tabs
+ element (in both cases, when they are anonymous nodes of <tabbrowser>).
+ These bindings are exposed as pseudo-public-APIs, so themes can customize
+ the tabbar appearance without having to be scriptable
+ (see globalBindings.xml in osx for example).
+ -->
+ <binding id="tabmail-new-tab-button"
+ extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
+ <handlers>
+ <handler event="command">
+ <![CDATA[
+ let bindingParent = document.getBindingParent(this);
+ if (bindingParent)
+ {
+ let tabmail = document.getBindingParent(bindingParent);
+ if (bindingParent.localName == "tabs")
+ {
+ // new-tab-button only appears in the tabstrip
+ // duplicate the current tab
+ tabmail.openTab("", {});
+ }
+ }
+ ]]>
+ </handler>
+ <handler event="dblclick" button="0" phase="capturing">
+ <![CDATA[
+ event.stopPropagation();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="tabmail-close-tab-button"
+ extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
+ <handlers>
+ <handler event="command">
+ <![CDATA[
+ let bindingParent = document.getBindingParent(this);
+ if (bindingParent)
+ {
+ let tabmail = document.getBindingParent(bindingParent);
+ if (bindingParent.localName == "tab")
+ {
+ /* The only sequence in which a second click event (i.e. dblclik)
+ * can be dispatched on an in-tab close button is when it is shown
+ * after the first click (i.e. the first click event was dispatched
+ * on the tab). This happens when we show the close button only on
+ * the active tab. (bug 352021)
+ * The only sequence in which a third click event can be dispatched
+ * on an in-tab close button is when the tab was opened with a
+ * double click on the tabbar. (bug 378344)
+ * In both cases, it is most likely that the close button area has
+ * been accidentally clicked, therefore we do not close the tab.
+ */
+ if (event.detail > 1)
+ return;
+
+ tabmail.removeTab(bindingParent);
+ tabmail._blockDblClick = true;
+
+ /* XXXmano hack (see bug 343628):
+ * Since we're removing the event target, if the user
+ * double-clicks this button, the dblclick event will be dispatched
+ * with the tabbar as its event target (and explicit/originalTarget),
+ * which treats that as a mouse gesture for opening a new tab.
+ * In this context, we're manually blocking the dblclick event
+ * (see onTabBarDblClick).
+ */
+ let clickedOnce = false;
+ function enableDblClick(event)
+ {
+ var target = event.originalTarget;
+ if (target.className == "tab-close-button")
+ target._ignoredClick = true;
+ if (!clickedOnce)
+ {
+ clickedOnce = true;
+ return;
+ }
+ tabContainer._blockDblClick = false;
+ tabContainer.removeEventListener("click", enableDblClick, true);
+ }
+ tabContainer.addEventListener("click", enableDblClick, true);
+ }
+ else
+ {
+ // "tabs"
+ tabmail.removeCurrentTab();
+ }
+ }
+ ]]>
+ </handler>
+ <handler event="dblclick" button="0" phase="capturing">
+ <![CDATA[
+ // for the one-close-button case
+ event.stopPropagation();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/mailnews/content/threadPane.js b/comm/suite/mailnews/content/threadPane.js
new file mode 100644
index 0000000000..ac4943d91f
--- /dev/null
+++ b/comm/suite/mailnews/content/threadPane.js
@@ -0,0 +1,598 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+var gLastMessageUriToLoad = null;
+var gThreadPaneCommandUpdater = null;
+
+function ThreadPaneOnClick(event)
+{
+ // usually, we're only interested in tree content clicks, not scrollbars etc.
+ let t = event.originalTarget;
+
+ // we may want to open the message in a new tab on middle click
+ if (event.button == kMouseButtonMiddle)
+ {
+ if (t.localName == "treechildren" && AllowOpenTabOnMiddleClick())
+ {
+ // we don't allow new tabs in the search dialog
+ if (document.documentElement.id != "searchMailWindow")
+ {
+ OpenMessageInNewTab(event);
+ RestoreSelectionWithoutContentLoad(GetThreadTree());
+ }
+ return;
+ }
+ }
+
+ // otherwise, we only care about left click events
+ if (event.button != kMouseButtonLeft)
+ return;
+
+ // We are already handling marking as read and flagging in nsMsgDBView.cpp,
+ // so all we need to worry about here is double clicks and column header.
+ // We also get in here for clicks on the "treecol" (headers) and the
+ // "scrollbarbutton" (scrollbar buttons), but we don't want those events to
+ // cause a "double click".
+ if (t.localName == "treecol")
+ {
+ HandleColumnClick(t.id);
+ }
+ else if (t.localName == "treechildren")
+ {
+ let tree = GetThreadTree();
+ // figure out what cell the click was in
+ var cell = tree.treeBoxObject.getCellAt(event.clientX, event.clientY);
+ if (cell.row == -1)
+ return;
+
+ // If the cell is in a "cycler" column or if the user double clicked on the
+ // twisty, don't open the message in a new window.
+ if (event.detail == 2 && !cell.col.cycler && (cell.childElt != "twisty"))
+ {
+ ThreadPaneDoubleClick(event);
+ // Double clicking should not toggle the open/close state of the thread.
+ // This will happen if we don't prevent the event from bubbling to the
+ // default handler in tree.xml.
+ event.stopPropagation();
+ }
+ else if (cell.col.id == "junkStatusCol")
+ {
+ MsgJunkMailInfo(true);
+ }
+ else if (cell.col.id == "threadCol" && !event.shiftKey && (event.ctrlKey || event.metaKey))
+ {
+ gDBView.ExpandAndSelectThreadByIndex(cell.row, true);
+ event.stopPropagation();
+ }
+ }
+}
+
+function nsMsgDBViewCommandUpdater()
+{}
+
+nsMsgDBViewCommandUpdater.prototype =
+{
+ updateCommandStatus : function()
+ {
+ // the back end is smart and is only telling us to update command status
+ // when the # of items in the selection has actually changed.
+ UpdateMailToolbar("dbview driven, thread pane");
+ },
+
+ displayMessageChanged : function(aFolder, aSubject, aKeywords)
+ {
+ if (!gDBView.suppressMsgDisplay)
+ setTitleFromFolder(aFolder, aSubject);
+ ClearPendingReadTimer(); // we are loading / selecting a new message so kill the mark as read timer for the currently viewed message
+ gHaveLoadedMessage = true;
+ goUpdateCommand("button_delete");
+ goUpdateCommand("button_junk");
+ },
+
+ updateNextMessageAfterDelete : function()
+ {
+ SetNextMessageAfterDelete();
+ },
+
+ summarizeSelection: function() {return false},
+
+ QueryInterface : function(iid)
+ {
+ if (iid.equals(Ci.nsIMsgDBViewCommandUpdater) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_NOINTERFACE;
+ }
+}
+
+function HandleColumnClick(columnID)
+{
+ const columnMap = {dateCol: 'byDate',
+ receivedCol: 'byReceived',
+ senderCol: 'byAuthor',
+ recipientCol: 'byRecipient',
+ subjectCol: 'bySubject',
+ locationCol: 'byLocation',
+ accountCol: 'byAccount',
+ unreadButtonColHeader: 'byUnread',
+ statusCol: 'byStatus',
+ sizeCol: 'bySize',
+ priorityCol: 'byPriority',
+ flaggedCol: 'byFlagged',
+ threadCol: 'byThread',
+ tagsCol: 'byTags',
+ junkStatusCol: 'byJunkStatus',
+ idCol: 'byId',
+ attachmentCol: 'byAttachments'};
+
+
+ var sortType;
+ if (columnID in columnMap) {
+ sortType = columnMap[columnID];
+ } else {
+ // If the column isn't in the map, check and see if it's a custom column
+ try {
+ // try to grab the columnHandler (an error is thrown if it does not exist)
+ columnHandler = gDBView.getColumnHandler(columnID);
+
+ // it exists - save this column ID in the customSortCol property of
+ // dbFolderInfo for later use (see nsIMsgDBView.cpp)
+ gDBView.db.dBFolderInfo.setProperty('customSortCol', columnID);
+
+ sortType = "byCustom";
+ } catch(err) {
+ dump("unsupported sort column: " + columnID + " - no custom handler installed. (Error was: " + err + ")\n");
+ return; // bail out
+ }
+ }
+
+ var dbview = GetDBView();
+ var simpleColumns = false;
+ try {
+ simpleColumns = !Services.prefs.getBoolPref("mailnews.thread_pane_column_unthreads");
+ }
+ catch (ex) {
+ }
+ if (sortType == "byThread") {
+ if (simpleColumns)
+ MsgToggleThreaded();
+ else if (dbview.viewFlags & nsMsgViewFlagsType.kThreadedDisplay)
+ MsgReverseSortThreadPane();
+ else
+ MsgSortByThread();
+ }
+ else {
+ if (!simpleColumns && (dbview.viewFlags & nsMsgViewFlagsType.kThreadedDisplay)) {
+ dbview.viewFlags &= ~nsMsgViewFlagsType.kThreadedDisplay;
+ MsgSortThreadPane(sortType);
+ }
+ else if (dbview.sortType == nsMsgViewSortType[sortType]) {
+ MsgReverseSortThreadPane();
+ }
+ else {
+ MsgSortThreadPane(sortType);
+ }
+ }
+}
+
+function ThreadPaneDoubleClick(event) {
+ if (IsSpecialFolderSelected(Ci.nsMsgFolderFlags.Drafts, true))
+ {
+ MsgComposeDraftMessage();
+ }
+ else if (IsSpecialFolderSelected(Ci.nsMsgFolderFlags.Templates, true))
+ {
+ ComposeMsgByType(Ci.nsIMsgCompType.Template, null,
+ Ci.nsIMsgCompFormat.Default);
+ }
+ else if (AllowOpenTabOnDoubleClick() &&
+ document.documentElement.id != "searchMailWindow")
+ { // we don't allow new tabs in the search dialog
+ // open the message in a new tab on double click
+ OpenMessageInNewTab(event);
+ RestoreSelectionWithoutContentLoad(GetThreadTree());
+ }
+ else
+ {
+ MsgOpenSelectedMessages();
+ }
+}
+
+function ThreadPaneKeyPress(event)
+{
+ if (event.keyCode == KeyEvent.DOM_VK_RETURN) {
+ if ((AppConstants.platform == "macosx" ? event.metaKey : event.ctrlKey) &&
+ AllowOpenTabOnMiddleClick()) {
+ OpenMessageInNewTab(event);
+ } else {
+ ThreadPaneDoubleClick(event);
+ }
+ }
+}
+
+function MsgSortByThread()
+{
+ var dbview = GetDBView();
+ dbview.viewFlags |= nsMsgViewFlagsType.kThreadedDisplay;
+ dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
+ MsgSortThreadPane('byDate');
+}
+
+function MsgSortThreadPane(sortName)
+{
+ var sortType = nsMsgViewSortType[sortName];
+ var dbview = GetDBView();
+
+ // turn off grouping
+ dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
+
+ dbview.sort(sortType, nsMsgViewSortOrder.ascending);
+ UpdateSortIndicators(sortType, nsMsgViewSortOrder.ascending);
+}
+
+function MsgReverseSortThreadPane()
+{
+ var dbview = GetDBView();
+ if (dbview.sortOrder == nsMsgViewSortOrder.ascending) {
+ MsgSortDescending();
+ }
+ else {
+ MsgSortAscending();
+ }
+}
+
+function MsgToggleThreaded()
+{
+ var dbview = GetDBView();
+ var newViewFlags = dbview.viewFlags ^ nsMsgViewFlagsType.kThreadedDisplay;
+ newViewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
+ dbview.viewFlags = newViewFlags;
+
+ dbview.sort(dbview.sortType, dbview.sortOrder);
+ UpdateSortIndicators(dbview.sortType, dbview.sortOrder);
+}
+
+function MsgSortThreaded()
+{
+ var dbview = GetDBView();
+ var viewFlags = dbview.viewFlags;
+ let wasGrouped = viewFlags & nsMsgViewFlagsType.kGroupBySort;
+ dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
+ // if we were grouped, and not a saved search, just rebuild the view
+ if (wasGrouped && !(gMsgFolderSelected.flags &
+ Ci.nsMsgFolderFlags.Virtual))
+ SwitchView("cmd_viewAllMsgs");
+ // Toggle if not already threaded.
+ else if ((viewFlags & nsMsgViewFlagsType.kThreadedDisplay) == 0)
+ MsgToggleThreaded();
+}
+
+function MsgGroupBySort()
+{
+ var dbview = GetDBView();
+ var viewFlags = dbview.viewFlags;
+ var sortOrder = dbview.sortOrder;
+ var sortType = dbview.sortType;
+ var count = new Object;
+ var msgFolder = dbview.msgFolder;
+
+ var sortTypeSupportsGrouping = (sortType == nsMsgViewSortType.byAuthor
+ || sortType == nsMsgViewSortType.byDate || sortType == nsMsgViewSortType.byReceived || sortType == nsMsgViewSortType.byPriority
+ || sortType == nsMsgViewSortType.bySubject || sortType == nsMsgViewSortType.byTags
+ || sortType == nsMsgViewSortType.byStatus || sortType == nsMsgViewSortType.byRecipient
+ || sortType == nsMsgViewSortType.byAccount || sortType == nsMsgViewSortType.byFlagged
+ || sortType == nsMsgViewSortType.byAttachments);
+
+ if (!sortTypeSupportsGrouping)
+ return; // we shouldn't be trying to group something we don't support grouping for...
+
+ viewFlags |= nsMsgViewFlagsType.kThreadedDisplay | nsMsgViewFlagsType.kGroupBySort;
+ if (gDBView &&
+ gMsgFolderSelected.flags & Ci.nsMsgFolderFlags.Virtual)
+ {
+ gDBView.viewFlags = viewFlags;
+ UpdateSortIndicators(sortType, nsMsgViewSortOrder.ascending);
+ return;
+ }
+ // null this out, so we don't try sort.
+ if (gDBView) {
+ gDBView.close();
+ gDBView = null;
+ }
+ gDBView = Cc["@mozilla.org/messenger/msgdbview;1?type=group"]
+ .createInstance(Ci.nsIMsgDBView);
+
+ if (!gThreadPaneCommandUpdater)
+ gThreadPaneCommandUpdater = new nsMsgDBViewCommandUpdater();
+
+
+ gDBView.init(messenger, msgWindow, gThreadPaneCommandUpdater);
+ gDBView.open(msgFolder, sortType, sortOrder, viewFlags, count);
+ RerootThreadPane();
+ UpdateSortIndicators(sortType, nsMsgViewSortOrder.ascending);
+ Services.obs.notifyObservers(msgFolder, "MsgCreateDBView",
+ Ci.nsMsgViewType.eShowAllThreads + ":" + viewFlags);
+}
+
+function MsgSortUnthreaded()
+{
+ // Toggle if not already unthreaded.
+ if ((GetDBView().viewFlags & nsMsgViewFlagsType.kThreadedDisplay) != 0)
+ MsgToggleThreaded();
+}
+
+function MsgSortAscending()
+{
+ var dbview = GetDBView();
+ dbview.sort(dbview.sortType, nsMsgViewSortOrder.ascending);
+ UpdateSortIndicators(dbview.sortType, nsMsgViewSortOrder.ascending);
+}
+
+function MsgSortDescending()
+{
+ var dbview = GetDBView();
+ dbview.sort(dbview.sortType, nsMsgViewSortOrder.descending);
+ UpdateSortIndicators(dbview.sortType, nsMsgViewSortOrder.descending);
+}
+
+function groupedBySortUsingDummyRow()
+{
+ return (gDBView.viewFlags & nsMsgViewFlagsType.kGroupBySort) &&
+ (gDBView.sortType != nsMsgViewSortType.bySubject);
+}
+
+function UpdateSortIndicators(sortType, sortOrder)
+{
+ // Remove the sort indicator from all the columns
+ var treeColumns = document.getElementById('threadCols').childNodes;
+ for (var i = 0; i < treeColumns.length; i++)
+ treeColumns[i].removeAttribute('sortDirection');
+
+ // show the twisties if the view is threaded
+ var threadCol = document.getElementById("threadCol");
+ var sortedColumn;
+ // set the sort indicator on the column we are sorted by
+ var colID = ConvertSortTypeToColumnID(sortType);
+ if (colID)
+ sortedColumn = document.getElementById(colID);
+
+ var dbview = GetDBView();
+ var currCol = dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort
+ ? sortedColumn : document.getElementById("subjectCol");
+
+ if (dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort)
+ {
+ var threadTree = document.getElementById("threadTree");
+ var subjectCol = document.getElementById("subjectCol");
+
+ if (groupedBySortUsingDummyRow())
+ {
+ currCol.removeAttribute("primary");
+ subjectCol.setAttribute("primary", "true");
+ }
+
+ // hide the threaded column when in grouped view since you can't do
+ // threads inside of a group.
+ document.getElementById("threadCol").collapsed = true;
+ }
+
+ // clear primary attribute from group column if going to a non-grouped view.
+ if (!(dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort))
+ document.getElementById("threadCol").collapsed = false;
+
+ if ((dbview.viewFlags & nsMsgViewFlagsType.kThreadedDisplay) && !groupedBySortUsingDummyRow()) {
+ threadCol.setAttribute("sortDirection", "ascending");
+ currCol.setAttribute("primary", "true");
+ }
+ else {
+ threadCol.removeAttribute("sortDirection");
+ currCol.removeAttribute("primary");
+ }
+
+ if (sortedColumn) {
+ if (sortOrder == nsMsgViewSortOrder.ascending) {
+ sortedColumn.setAttribute("sortDirection","ascending");
+ }
+ else {
+ sortedColumn.setAttribute("sortDirection","descending");
+ }
+ }
+}
+
+function IsSpecialFolderSelected(flags, checkAncestors)
+{
+ var folder = GetThreadPaneFolder();
+ return folder && folder.isSpecialFolder(flags, checkAncestors);
+}
+
+function GetThreadTree()
+{
+ return document.getElementById("threadTree")
+}
+
+function GetThreadPaneFolder()
+{
+ try {
+ return gDBView.msgFolder;
+ }
+ catch (ex) {
+ return null;
+ }
+}
+
+function EnsureRowInThreadTreeIsVisible(index)
+{
+ if (index < 0)
+ return;
+
+ var tree = GetThreadTree();
+ tree.treeBoxObject.ensureRowIsVisible(index);
+}
+
+function RerootThreadPane()
+{
+ SetNewsFolderColumns();
+
+ var treeView = gDBView.QueryInterface(Ci.nsITreeView);
+ if (treeView)
+ {
+ var tree = GetThreadTree();
+ tree.view = treeView;
+ }
+}
+
+function ThreadPaneOnLoad()
+{
+ var tree = GetThreadTree();
+ // We won't have the tree if we're in a message window, so exit silently
+ if (!tree)
+ return;
+
+ tree.addEventListener("click",ThreadPaneOnClick,true);
+
+ // The mousedown event listener below should only be added in the thread
+ // pane of the mailnews 3pane window, not in the advanced search window.
+ if(tree.parentNode.id == "searchResultListBox")
+ return;
+
+ tree.addEventListener("mousedown",TreeOnMouseDown,true);
+ var delay = Services.prefs.getIntPref("mailnews.threadpane_select_delay");
+ document.getElementById("threadTree")._selectDelay = delay;
+}
+
+function ThreadPaneSelectionChanged()
+{
+ UpdateStatusMessageCounts(gMsgFolderSelected);
+ if (!gRightMouseButtonDown)
+ GetThreadTree().view.selectionChanged();
+}
+
+var ThreadPaneDND = {
+ onDragStart(aEvent) {
+ if (aEvent.originalTarget.localName != "treechildren")
+ return;
+
+ let messageUris = gFolderDisplay.selectedMessageUris;
+ if (!messageUris)
+ return;
+
+ // A message can be dragged from one window and dropped on another window.
+ // Therefore we setNextMessageAfterDelete() here since there is no major
+ // disadvantage, even if it is a copy operation.
+ SetNextMessageAfterDelete();
+ let messengerBundle = document.getElementById("bundle_messenger");
+ let noSubject = messengerBundle.getString("defaultSaveMessageAsFileName");
+ if (noSubject.endsWith(".eml")) {
+ noSubject = noSubject.slice(0, -4);
+ }
+ let fileNames = [];
+ let dataTransfer = aEvent.dataTransfer;
+
+ for (let [index, msgUri] of messageUris.entries()) {
+ let msgService = messenger.messageServiceFromURI(msgUri);
+ let msgHdr = msgService.messageURIToMsgHdr(msgUri);
+ let subject = msgHdr.mime2DecodedSubject || noSubject;
+ if (msgHdr.flags & Ci.nsMsgMessageFlags.HasRe) {
+ subject = "Re: " + subject;
+ }
+ let uniqueFileName = suggestUniqueFileName(subject.substr(0, 120), ".eml",
+ fileNames);
+ fileNames[index] = uniqueFileName;
+ let msgUrl = {};
+ msgService.GetUrlForUri(msgUri, msgUrl, null);
+ dataTransfer.mozSetDataAt("text/x-moz-message", msgUri, index);
+ dataTransfer.mozSetDataAt("text/x-moz-url", msgUrl.value.spec, index);
+ dataTransfer.mozSetDataAt("application/x-moz-file-promise-url",
+ msgUrl.value.spec + "?fileName=" +
+ encodeURIComponent(uniqueFileName),
+ index);
+ dataTransfer.mozSetDataAt("application/x-moz-file-promise",
+ new messageFlavorDataProvider(), index);
+ }
+ dataTransfer.effectAllowed = "copyMove";
+ dataTransfer.addElement(aEvent.originalTarget);
+ },
+
+ onDragOver(aEvent) {
+ if (!gMsgFolderSelected.canFileMessages ||
+ gMsgFolderSelected.server.type == "rss")
+ return;
+ let dt = aEvent.dataTransfer;
+ dt.effectAllowed = "copy";
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ if (Array.from(dt.mozTypesAt(i)).includes("application/x-moz-file")) {
+ let extFile = dt.mozGetDataAt("application/x-moz-file", i);
+ if (!extFile) {
+ return;
+ }
+
+ extFile = extFile.QueryInterface(Ci.nsIFile);
+ if (extFile.isFile() && /\.eml$/i.test(extFile.leafName)) {
+ aEvent.preventDefault();
+ return;
+ }
+ }
+ }
+ },
+
+ onDrop(aEvent) {
+ let dt = aEvent.dataTransfer;
+ for (let i = 0; i < dt.mozItemCount; i++) {
+ let extFile = dt.mozGetDataAt("application/x-moz-file", i);
+ if (!extFile) {
+ continue;
+ }
+
+ extFile = extFile.QueryInterface(Ci.nsIFile);
+ if (extFile.isFile() && /\.eml$/i.test(extFile.leafName))
+ MailServices.copy.CopyFileMessage(extFile, gMsgFolderSelected, null,
+ false, 1, "", null, msgWindow);
+ }
+ },
+}
+
+function messageFlavorDataProvider() {}
+
+messageFlavorDataProvider.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIFlavorDataProvider"]),
+
+ getFlavorData(aTransferable, aFlavor, aData, aDataLen) {
+ if (aFlavor !== "application/x-moz-file-promise") {
+ return;
+ }
+ let fileUriPrimitive = {};
+ let dataSize = {};
+ aTransferable.getTransferData("application/x-moz-file-promise-url",
+ fileUriPrimitive, dataSize);
+
+ let fileUriStr = fileUriPrimitive.value
+ .QueryInterface(Ci.nsISupportsString);
+ let fileUri = Services.io.newURI(fileUriStr.data);
+ let fileUrl = fileUri.QueryInterface(Ci.nsIURL);
+ let fileName = fileUrl.fileName;
+
+ let destDirPrimitive = {};
+ aTransferable.getTransferData("application/x-moz-file-promise-dir",
+ destDirPrimitive, dataSize);
+ let destDirectory = destDirPrimitive.value.QueryInterface(Ci.nsIFile);
+ let file = destDirectory.clone();
+ file.append(fileName);
+
+ let messageUriPrimitive = {};
+ aTransferable.getTransferData("text/x-moz-message", messageUriPrimitive,
+ dataSize);
+ let messageUri = messageUriPrimitive.value
+ .QueryInterface(Ci.nsISupportsString);
+
+ messenger.saveAs(messageUri.data, true, null, decodeURIComponent(file.path),
+ true);
+ },
+};
+
+addEventListener("load",ThreadPaneOnLoad,true);
diff --git a/comm/suite/mailnews/content/threadPane.xul b/comm/suite/mailnews/content/threadPane.xul
new file mode 100644
index 0000000000..c012852967
--- /dev/null
+++ b/comm/suite/mailnews/content/threadPane.xul
@@ -0,0 +1,91 @@
+<?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://messenger/skin/threadPane.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/threadPaneExtras.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/threadPaneLabels.css" type="text/css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/threadpane.dtd">
+
+<overlay
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<script src="chrome://messenger/content/threadPane.js"/>
+
+<tree id="threadTree"
+ persist="width lastfoldersent"
+ flex="1"
+ enableColumnDrag="true"
+ _selectDelay="250"
+ class="plain focusring"
+ disableKeyNavigation="true"
+ lastfoldersent="false"
+ noattachcol="true"
+ onkeypress="ThreadPaneKeyPress(event);"
+ onselect="ThreadPaneSelectionChanged();">
+ <treecols id="threadCols" pickertooltiptext="&columnChooser2.tooltip;">
+ <treecol id="threadCol" persist="hidden ordinal" fixed="true" cycler="true" class="treecol-image threadColumnHeader" currentView="unthreaded"
+ label="&threadColumn.label;" tooltiptext="&threadColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="attachmentCol" persist="hidden ordinal" fixed="true" class="treecol-image attachmentColumnHeader" hidden="true"
+ label="&attachmentColumn.label;" tooltiptext="&attachmentColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="subjectCol" persist="hidden ordinal width" flex="7" ignoreincolumnpicker="true"
+ label="&subjectColumn.label;" tooltiptext="&subjectColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="junkStatusCol" persist="hidden ordinal width" fixed="true" cycler="true" class="treecol-image junkStatusHeader"
+ label="&junkStatusColumn.label;" tooltiptext="&junkStatusColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="senderCol" persist="ordinal width hidden swappedhidden" flex="4" hidden="false" swappedhidden="true"
+ label="&fromColumn.label;" tooltiptext="&fromColumn2.tooltip;"/>
+ <treecol id="recipientCol" persist="ordinal width hidden swappedhidden" flex="4" hidden="true" swappedhidden="false"
+ label="&recipientColumn.label;" tooltiptext="&recipientColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="unreadButtonColHeader" persist="hidden ordinal" fixed="true" cycler="true" class="treecol-image readColumnHeader"
+ label="&readColumn.label;" tooltiptext="&readColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="receivedCol" persist="hidden ordinal width temphidden" flex="2" hidden="true" temphidden="false"
+ label="&receivedColumn.label;" tooltiptext="&receivedColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="dateCol" persist="hidden ordinal width" flex="2"
+ label="&dateColumn.label;" tooltiptext="&dateColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="statusCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&statusColumn.label;" tooltiptext="&statusColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="sizeCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&sizeColumn.label;" tooltiptext="&sizeColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="flaggedCol" persist="hidden ordinal" fixed="true" cycler="true" hidden="true" class="treecol-image flagColumnHeader"
+ label="&flagColumn.label;" tooltiptext="&flagColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="tagsCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&tagsColumn.label;" tooltiptext="&tagsColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="accountCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&accountColumn.label;" tooltiptext="&accountColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="priorityCol" persist="hidden ordinal width" flex="1"
+ label="&priorityColumn.label;" tooltiptext="&priorityColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="unreadCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&unreadColumn.label;" tooltiptext="&unreadColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="totalCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&totalColumn.label;" tooltiptext="&totalColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="locationCol" persist="width" flex="1" hidden="true" ignoreincolumnpicker="true"
+ label="&locationColumn.label;" tooltiptext="&locationColumn2.tooltip;"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="idCol" persist="hidden ordinal width" flex="1" hidden="true"
+ label="&idColumn.label;" tooltiptext="&idColumn2.tooltip;"/>
+ </treecols>
+ <treechildren ondragstart="ThreadPaneDND.onDragStart(event);"
+ ondragover="ThreadPaneDND.onDragOver(event);"
+ ondrop="ThreadPaneDND.onDrop(event);"/>
+</tree>
+
+</overlay>
diff --git a/comm/suite/mailnews/jar.mn b/comm/suite/mailnews/jar.mn
new file mode 100644
index 0000000000..5529995b02
--- /dev/null
+++ b/comm/suite/mailnews/jar.mn
@@ -0,0 +1,70 @@
+# 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/.
+
+messenger.jar:
+% content messagebody %content/messagebody/ contentaccessible=yes
+% content messenger %content/messenger/
+% override chrome://global/content/nsDragAndDrop.js chrome://messenger/content/nsDragAndDrop.js
+# provide the nsTransferable in nsDragAndDrop.js to extensions that have to
+# work with Geckos from before 1.9, when there was a separate file
+% override chrome://global/content/nsTransferable.js chrome://messenger/content/nsDragAndDrop.js
+% override chrome://messagebody/skin/messageBody.css chrome://messenger/skin/messageBody.css
+% content messenger-region %content/messenger-region/
+% overlay chrome://communicator/content/pref/preferences.xul chrome://messenger/content/mailPrefsOverlay.xul
+% overlay chrome://communicator/content/pref/pref-appearance.xul chrome://messenger/content/mailPrefsOverlay.xul
+% overlay chrome://communicator/content/pref/pref-cookies.xul chrome://messenger/content/mailPrefsOverlay.xul
+% overlay chrome://editor/content/editorTasksOverlay.xul chrome://messenger/content/mailTasksOverlay.xul
+% overlay chrome://messenger/content/addressbook/abSelectAddressesDialog.xul chrome://messenger/content/mailOverlay.xul
+% overlay chrome://editor/content/composerOverlay.xul chrome://messenger/content/mailEditorOverlay.xul
+ content/messenger/browserRequest.js (content/browserRequest.js)
+ content/messenger/browserRequest.xul (content/browserRequest.xul)
+ content/messenger/commandglue.js (content/commandglue.js)
+ content/messenger/folderDisplay.js (content/folderDisplay.js)
+ content/messenger/folderPane.js (content/folderPane.js)
+ content/messenger/folderPane.xul (content/folderPane.xul)
+ content/messenger/mail-offline.js (content/mail-offline.js)
+ content/messenger/mail3PaneWindowCommands.js (content/mail3PaneWindowCommands.js)
+ content/messenger/mailCommands.js (content/mailCommands.js)
+ content/messenger/mailContextMenus.js (content/mailContextMenus.js)
+ content/messenger/mailEditorOverlay.xul (content/mailEditorOverlay.xul)
+* content/messenger/mailKeysOverlay.xul (content/mailKeysOverlay.xul)
+ content/messenger/mailOverlay.js (content/mailOverlay.js)
+* content/messenger/mailOverlay.xul (content/mailOverlay.xul)
+ content/messenger/mailTasksOverlay.js (content/mailTasksOverlay.js)
+ content/messenger/mailTasksOverlay.xul (content/mailTasksOverlay.xul)
+ content/messenger/mailViewList.js (content/mailViewList.js)
+ content/messenger/mailViewList.xul (content/mailViewList.xul)
+ content/messenger/mailViewSetup.js (content/mailViewSetup.js)
+ content/messenger/mailViewSetup.xul (content/mailViewSetup.xul)
+ content/messenger/mailWidgets.xml (content/mailWidgets.xml)
+ content/messenger/mailWindow.js (content/mailWindow.js)
+ content/messenger/mailWindowOverlay.js (content/mailWindowOverlay.js)
+* content/messenger/mailWindowOverlay.xul (content/mailWindowOverlay.xul)
+ content/messenger/messageWindow.js (content/messageWindow.js)
+ content/messenger/messageWindow.xul (content/messageWindow.xul)
+ content/messenger/messenger.css (content/messenger.css)
+ content/messenger/messenger.xul (content/messenger.xul)
+ content/messenger/msgFolderPickerOverlay.js (content/msgFolderPickerOverlay.js)
+ content/messenger/msgHdrViewOverlay.js (content/msgHdrViewOverlay.js)
+ content/messenger/msgHdrViewOverlay.xul (content/msgHdrViewOverlay.xul)
+ content/messenger/msgMail3PaneWindow.js (content/msgMail3PaneWindow.js)
+ content/messenger/msgViewNavigation.js (content/msgViewNavigation.js)
+ content/messenger/msgViewPickerOverlay.js (content/msgViewPickerOverlay.js)
+ content/messenger/nsDragAndDrop.js (content/nsDragAndDrop.js)
+ content/messenger/phishingDetector.js (content/phishingDetector.js)
+ content/messenger/searchBar.js (content/searchBar.js)
+ content/messenger/start.xhtml (content/start.xhtml)
+ content/messenger/tabmail.js (content/tabmail.js)
+ content/messenger/tabmail.xml (content/tabmail.xml)
+ content/messenger/threadPane.js (content/threadPane.js)
+ content/messenger/threadPane.xul (content/threadPane.xul)
+
+ content/messenger/SearchDialog.xul (content/SearchDialog.xul)
+ content/messenger/SearchDialog.js (content/SearchDialog.js)
+ content/messenger/ABSearchDialog.xul (content/ABSearchDialog.xul)
+ content/messenger/ABSearchDialog.js (content/ABSearchDialog.js)
+ content/messenger/FilterListDialog.xul (content/FilterListDialog.xul)
+ content/messenger/FilterListDialog.js (content/FilterListDialog.js)
+
+ content/messenger/searchTermOverlay.xul (content/searchTermOverlay.xul)
diff --git a/comm/suite/mailnews/modules/MailUtils.js b/comm/suite/mailnews/modules/MailUtils.js
new file mode 100644
index 0000000000..3bb844718f
--- /dev/null
+++ b/comm/suite/mailnews/modules/MailUtils.js
@@ -0,0 +1,100 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["MailUtils"];
+
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+/**
+ * This module has several utility functions for use by both core and
+ * third-party code. Some functions are aimed at code that doesn't have a
+ * window context, while others can be used anywhere.
+ */
+var MailUtils =
+{
+ /**
+ * Discover all folders. This is useful during startup, when you have code
+ * that deals with folders and that executes before the main 3pane window is
+ * open (the folder tree wouldn't have been initialized yet).
+ */
+ discoverFolders: function MailUtils_discoverFolders()
+ {
+ for (let server of MailServices.accounts.allServers) {
+ // Bug 466311 Sometimes this can throw file not found, we're unsure
+ // why, but catch it and log the fact.
+ try {
+ server.rootFolder.subFolders;
+ }
+ catch (ex) {
+ Services.console.logStringMessage("Discovering folders for account failed with " +
+ "exception: " + ex);
+ }
+ }
+ },
+
+ /**
+ * Get the nsIMsgFolder corresponding to this URI. This uses the RDF service
+ * to do the work.
+ *
+ * @param aFolderURI the URI to convert into a folder
+ * @param aCheckFolderAttributes whether to check that the folder either has
+ * a parent or isn't a server
+ * @returns the nsIMsgFolder corresponding to this URI, or null if
+ * aCheckFolderAttributes is true and the folder doesn't have a
+ * parent or is a server
+ */
+ getFolderForURI: function MailUtils_getFolderForURI(aFolderURI,
+ aCheckFolderAttributes)
+ {
+ let folder = null;
+ let rdfService = Cc['@mozilla.org/rdf/rdf-service;1']
+ .getService(Ci.nsIRDFService);
+ folder = rdfService.GetResource(aFolderURI);
+ // This is going to QI the folder to an nsIMsgFolder as well
+ if (folder && folder instanceof Ci.nsIMsgFolder)
+ {
+ if (aCheckFolderAttributes && !(folder.parent || folder.isServer))
+ return null;
+ }
+ else
+ {
+ return null;
+ }
+
+ return folder;
+ },
+
+ /**
+ * Displays this message in a new window.
+ *
+ * @param aMsgHdr the message header to display
+ */
+ displayMessage: function MailUtils_displayMessage(aMsgHdr)
+ {
+ this.openMessageInNewWindow(aMsgHdr);
+ },
+
+ /**
+ * Open a new standalone message window with this header.
+ *
+ * @param aMsgHdr the message header to display
+ */
+ openMessageInNewWindow: function MailUtils_openMessageInNewWindow(aMsgHdr)
+ {
+ // Pass in the message URI as messageWindow.js doesn't handle message headers
+ let messageURI = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+ messageURI.data = aMsgHdr.folder.getUriForMsg(aMsgHdr);
+
+ Services.ww.openWindow(null,
+ "chrome://messenger/content/messageWindow.xul",
+ "_blank",
+ "all,chrome,dialog=no,status,toolbar",
+ messageURI);
+ }
+};
diff --git a/comm/suite/mailnews/modules/moz.build b/comm/suite/mailnews/modules/moz.build
new file mode 100644
index 0000000000..4a6802690b
--- /dev/null
+++ b/comm/suite/mailnews/modules/moz.build
@@ -0,0 +1,8 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXTRA_JS_MODULES += [
+ "MailUtils.js",
+]
diff --git a/comm/suite/mailnews/moz.build b/comm/suite/mailnews/moz.build
new file mode 100644
index 0000000000..00f4213662
--- /dev/null
+++ b/comm/suite/mailnews/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+DIRS += [
+ "components",
+ "modules",
+]
diff --git a/comm/suite/modules/Feeds.jsm b/comm/suite/modules/Feeds.jsm
new file mode 100644
index 0000000000..6c24dcfea8
--- /dev/null
+++ b/comm/suite/modules/Feeds.jsm
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [ "Feeds" ];
+
+ChromeUtils.defineModuleGetter(this, "BrowserUtils",
+ "resource://gre/modules/BrowserUtils.jsm");
+
+var Feeds = {
+
+ /**
+ * isValidFeed: checks whether the given data represents a valid feed.
+ *
+ * @param aLink
+ * An object representing a feed with title, href and type.
+ * @param aPrincipal
+ * The principal of the document, used for security check.
+ * @param aIsFeed
+ * Whether this is already a known feed or not, if true only a security
+ * check will be performed.
+ */
+ isValidFeed(aLink, aPrincipal, aIsFeed) {
+ if (!aLink || !aPrincipal)
+ return false;
+
+ var type = aLink.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
+ if (!aIsFeed) {
+ aIsFeed = (type == "application/rss+xml" ||
+ type == "application/atom+xml");
+ }
+
+ if (aIsFeed) {
+ try {
+ let href = BrowserUtils.makeURI(aLink.href, aLink.ownerDocument.characterSet)
+ BrowserUtils.urlSecurityCheck(href, aPrincipal,
+ Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+ return type || "application/rss+xml";
+ } catch (ex) {
+ }
+ }
+
+ return null;
+ },
+
+};
diff --git a/comm/suite/modules/OfflineAppCacheHelper.jsm b/comm/suite/modules/OfflineAppCacheHelper.jsm
new file mode 100644
index 0000000000..840012b87c
--- /dev/null
+++ b/comm/suite/modules/OfflineAppCacheHelper.jsm
@@ -0,0 +1,16 @@
+/* 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 EXPORTED_SYMBOLS = ["OfflineAppCacheHelper"];
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+var OfflineAppCacheHelper = {
+ clear() {
+ var appCacheStorage = Services.cache2.appCacheStorage(Services.loadContextInfo.default, null);
+ try {
+ appCacheStorage.asyncEvictStorage(null);
+ } catch (er) {}
+ }
+};
diff --git a/comm/suite/modules/OpenInTabsUtils.jsm b/comm/suite/modules/OpenInTabsUtils.jsm
new file mode 100644
index 0000000000..a04aa8d137
--- /dev/null
+++ b/comm/suite/modules/OpenInTabsUtils.jsm
@@ -0,0 +1,83 @@
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["OpenInTabsUtils"];
+
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "bundle", function() {
+ return Services.strings.createBundle("chrome://navigator/locale/tabbrowser.properties");
+});
+
+/**
+ * Utility functions that can be used when opening multiple tabs, that can be
+ * called without any tabbrowser instance.
+ */
+var OpenInTabsUtils = {
+ getString(key) {
+ return bundle.GetStringFromName(key);
+ },
+
+ getFormattedString(key, params) {
+ return bundle.formatStringFromName(key, params, params.length);
+ },
+
+ /**
+ * Gives the user a chance to cancel loading lots of tabs at once.
+ */
+ confirmOpenInTabs(numTabsToOpen, aWindow) {
+ const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
+ const MAX_OPNE_PREF = "browser.tabs.maxOpenBeforeWarn";
+ if (!Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) {
+ return true;
+ }
+ if (numTabsToOpen < Services.prefs.getIntPref(MAX_OPNE_PREF)) {
+ return true;
+ }
+
+ // default to true: if it were false, we wouldn't get this far
+ let warnOnOpen = { value: true };
+
+ const messageKey = "tabs.openWarningMultipleBranded";
+ const openKey = "tabs.openButtonMultiple";
+ const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
+ let brandShortName = Services.strings
+ .createBundle(BRANDING_BUNDLE_URI)
+ .GetStringFromName("brandShortName");
+
+ let buttonPressed = Services.prompt.confirmEx(
+ aWindow,
+ this.getString("tabs.openWarningTitle"),
+ this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
+ (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
+ this.getString(openKey), null, null,
+ this.getFormattedString("tabs.openWarningPromptMeBranded",
+ [brandShortName]),
+ warnOnOpen
+ );
+
+ let reallyOpen = (buttonPressed == 0);
+ // don't set the pref unless they press OK and it's false
+ if (reallyOpen && !warnOnOpen.value) {
+ Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false);
+ }
+
+ return reallyOpen;
+ },
+
+ /*
+ * Async version of confirmOpenInTabs.
+ */
+ promiseConfirmOpenInTabs(numTabsToOpen, aWindow) {
+ return new Promise(resolve => {
+ Services.tm.dispatchToMainThread(() => {
+ resolve(this.confirmOpenInTabs(numTabsToOpen, aWindow));
+ });
+ });
+ }
+};
diff --git a/comm/suite/modules/PermissionUI.jsm b/comm/suite/modules/PermissionUI.jsm
new file mode 100644
index 0000000000..09d24a9a82
--- /dev/null
+++ b/comm/suite/modules/PermissionUI.jsm
@@ -0,0 +1,612 @@
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "PermissionUI",
+];
+
+/**
+ * PermissionUI is responsible for exposing both a prototype
+ * PermissionPrompt that can be used by arbitrary browser
+ * components and add-ons, but also hosts the implementations of
+ * built-in permission prompts.
+ *
+ * If you're developing a feature that requires web content to ask
+ * for special permissions from the user, this module is for you.
+ *
+ * Suppose a system add-on wants to add a new prompt for a new request
+ * for getting more low-level access to the user's sound card, and the
+ * permission request is coming up from content by way of the
+ * nsContentPermissionHelper. The system add-on could then do the following:
+ *
+ * ChromeUtils.import("resource://gre/modules/Integration.jsm");
+ * ChromeUtils.import("resource:///modules/PermissionUI.jsm");
+ *
+ * const SoundCardIntegration = (base) => ({
+ * __proto__: base,
+ * createPermissionPrompt(type, request) {
+ * if (type != "sound-api") {
+ * return super.createPermissionPrompt(...arguments);
+ * }
+ *
+ * return {
+ * __proto__: PermissionUI.PermissionPromptForRequestPrototype,
+ * get permissionKey() {
+ * return "sound-permission";
+ * }
+ * // etc - see the documentation for PermissionPrompt for
+ * // a better idea of what things one can and should override.
+ * }
+ * },
+ * });
+ *
+ * // Add-on startup:
+ * Integration.contentPermission.register(SoundCardIntegration);
+ * // ...
+ * // Add-on shutdown:
+ * Integration.contentPermission.unregister(SoundCardIntegration);
+ *
+ * Note that PermissionPromptForRequestPrototype must be used as the
+ * prototype, since the prompt is wrapping an nsIContentPermissionRequest,
+ * and going through nsIContentPermissionPrompt.
+ *
+ * It is, however, possible to take advantage of PermissionPrompt without
+ * having to go through nsIContentPermissionPrompt or with a
+ * nsIContentPermissionRequest. The PermissionPromptPrototype can be
+ * imported, subclassed, and have prompt() called directly, without
+ * the caller having called into createPermissionPrompt.
+ */
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ChromeUtils.defineModuleGetter(this, "SitePermissions",
+ "resource:///modules/SitePermissions.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+
+XPCOMUtils.defineLazyGetter(this, "gNotificationBundle", function() {
+ return Services.strings
+ .createBundle("chrome://communicator/locale/notification.properties");
+});
+
+var PermissionUI = {};
+
+/**
+ * PermissionPromptPrototype should be subclassed by callers that
+ * want to display prompts to the user. See each method and property
+ * below for guidance on what to override.
+ *
+ * Note that if you're creating a prompt for an
+ * nsIContentPermissionRequest, you'll want to subclass
+ * PermissionPromptForRequestPrototype instead.
+ */
+var PermissionPromptPrototype = {
+ /**
+ * Returns the associated <xul:browser> for the request. This should
+ * work for the e10s and non-e10s case.
+ *
+ * Subclasses must override this.
+ *
+ * @return {<xul:browser>}
+ */
+ get browser() {
+ throw new Error("Not implemented.");
+ },
+
+ /**
+ * Returns the nsIPrincipal associated with the request.
+ *
+ * Subclasses must override this.
+ *
+ * @return {nsIPrincipal}
+ */
+ get principal() {
+ throw new Error("Not implemented.");
+ },
+
+ /**
+ * If the nsIPermissionManager is being queried and written
+ * to for this permission request, set this to the key to be
+ * used. If this is undefined, user permissions will not be
+ * read from or written to.
+ *
+ * Note that if a permission is set, in any follow-up
+ * prompting within the expiry window of that permission,
+ * the prompt will be skipped and the allow or deny choice
+ * will be selected automatically.
+ */
+ get permissionKey() {
+ return undefined;
+ },
+
+ /**
+ * These are the options that will be passed to the
+ * PopupNotification when it is shown. See the documentation
+ * for PopupNotification for more details.
+ *
+ * Note that prompt() will automatically set displayURI to
+ * be the URI of the requesting pricipal, unless the displayURI is exactly
+ * set to false.
+ */
+ get popupOptions() {
+ return {};
+ },
+
+ /**
+ * PopupNotification requires a unique ID to open the notification.
+ * You must return a unique ID string here, for which PopupNotification
+ * will then create a <xul:popupnotification> node with the ID
+ * "<notificationID>-notification".
+ *
+ * If there's a custom <xul:popupnotification> you're hoping to show,
+ * then you need to make sure its ID has the "-notification" suffix,
+ * and then return the prefix here.
+ *
+ * See PopupNotification.jsm for more details.
+ *
+ * @return {string}
+ * The unique ID that will be used to as the
+ * "<unique ID>-notification" ID for the <xul:popupnotification>
+ * to use or create.
+ */
+ get notificationID() {
+ throw new Error("Not implemented.");
+ },
+
+ /**
+ * The ID of the element to anchor the PopupNotification to.
+ *
+ * @return {string}
+ */
+ get anchorID() {
+ return "default-notification-icon";
+ },
+
+ /**
+ * The message to show to the user in the PopupNotification. A string
+ * with "<>" as a placeholder that is usually replaced by an addon name
+ * or a host name, formatted in bold, to describe the permission that is being requested.
+ *
+ * Subclasses must override this.
+ *
+ * @return {string}
+ */
+ get message() {
+ throw new Error("Not implemented.");
+ },
+
+ /**
+ * This will be called if the request is to be cancelled.
+ *
+ * Subclasses only need to override this if they provide a
+ * permissionKey.
+ */
+ cancel() {
+ throw new Error("Not implemented.")
+ },
+
+ /**
+ * This will be called if the request is to be allowed.
+ *
+ * Subclasses only need to override this if they provide a
+ * permissionKey.
+ */
+ allow() {
+ throw new Error("Not implemented.");
+ },
+
+ /**
+ * The actions that will be displayed in the PopupNotification
+ * via a dropdown menu. The first item in this array will be
+ * the default selection. Each action is an Object with the
+ * following properties:
+ *
+ * label (string):
+ * The label that will be displayed for this choice.
+ * accessKey (string):
+ * The access key character that will be used for this choice.
+ * action (SitePermissions state)
+ * The action that will be associated with this choice.
+ * This should be either SitePermissions.ALLOW or SitePermissions.BLOCK.
+ *
+ * callback (function, optional)
+ * A callback function that will fire if the user makes this choice, with
+ * a single parameter, state. State is an Object that contains the property
+ * checkboxChecked, which identifies whether the checkbox to remember this
+ * decision was checked.
+ */
+ get promptActions() {
+ return [];
+ },
+
+ /**
+ * Will determine if a prompt should be shown to the user, and if so,
+ * will show it.
+ *
+ * If a permissionKey is defined prompt() might automatically
+ * allow or cancel itself based on the user's current
+ * permission settings without displaying the prompt.
+ *
+ * If the permission is not already set and the <xul:browser> that the request
+ * is associated with does not belong to a browser window with the
+ * PopupNotifications global set, the prompt request is ignored.
+ */
+ prompt() {
+ // We ignore requests from non-nsIStandardURLs
+ let requestingURI = this.principal.URI;
+ if (!(requestingURI instanceof Ci.nsIStandardURL)) {
+ return;
+ }
+
+ if (this.permissionKey) {
+ // If we're reading and setting permissions, then we need
+ // to check to see if we already have a permission setting
+ // for this particular principal.
+ let {state} = SitePermissions.getForPrincipal(this.principal,
+ this.permissionKey,
+ this.browser);
+
+ if (state == SitePermissions.BLOCK) {
+ this.cancel();
+ return;
+ }
+
+ if (state == SitePermissions.ALLOW) {
+ this.allow();
+ return;
+ }
+
+ // Tell the browser to refresh the identity block display in case there
+ // are expired permission states.
+ this.browser.dispatchEvent(new this.browser.ownerGlobal
+ .CustomEvent("PermissionStateChange"));
+ }
+
+ let chromeWin = this.browser.ownerGlobal;
+ if (!chromeWin.PopupNotifications) {
+ this.cancel();
+ return;
+ }
+
+ // Transform the PermissionPrompt actions into PopupNotification actions.
+ let popupNotificationActions = [];
+ for (let promptAction of this.promptActions) {
+ let action = {
+ label: promptAction.label,
+ accessKey: promptAction.accessKey,
+ callback: state => {
+ if (promptAction.callback) {
+ promptAction.callback();
+ }
+
+ if (this.permissionKey) {
+
+ // Permanently store permission.
+ if ((state && state.checkboxChecked) ||
+ promptAction.scope == SitePermissions.SCOPE_PERSISTENT) {
+ let scope = SitePermissions.SCOPE_PERSISTENT;
+ // Only remember permission for session if in PB mode.
+ if (PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
+ scope = SitePermissions.SCOPE_SESSION;
+ }
+ SitePermissions.setForPrincipal(this.principal,
+ this.permissionKey,
+ promptAction.action,
+ scope);
+ } else if (promptAction.action == SitePermissions.BLOCK) {
+ // Temporarily store BLOCK permissions only.
+ // SitePermissions does not consider subframes when storing temporary
+ // permissions on a tab, thus storing ALLOW could be exploited.
+ SitePermissions.setForPrincipal(this.principal,
+ this.permissionKey,
+ promptAction.action,
+ SitePermissions.SCOPE_TEMPORARY,
+ this.browser);
+ }
+
+ // Grant permission if action is ALLOW.
+ if (promptAction.action == SitePermissions.ALLOW) {
+ this.allow();
+ } else {
+ this.cancel();
+ }
+ }
+ },
+ };
+ if (promptAction.dismiss) {
+ action.dismiss = promptAction.dismiss
+ }
+
+ popupNotificationActions.push(action);
+ }
+
+ let mainAction = popupNotificationActions.length ?
+ popupNotificationActions[0] : null;
+ let secondaryActions = popupNotificationActions.splice(1);
+
+ let options = this.popupOptions;
+
+ if (!options.hasOwnProperty("displayURI") || options.displayURI) {
+ options.displayURI = this.principal.URI;
+ }
+ // Permission prompts are always persistent; the close button is controlled by a pref.
+ options.persistent = true;
+ options.hideClose = !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton");
+ // When the docshell of the browser is aboout to be swapped to another one,
+ // the "swapping" event is called. Returning true causes the notification
+ // to be moved to the new browser.
+ options.eventCallback = topic => topic == "swapping";
+
+ chromeWin.PopupNotifications.show(this.browser,
+ this.notificationID,
+ this.message,
+ this.anchorID,
+ mainAction,
+ secondaryActions,
+ options);
+ },
+};
+
+PermissionUI.PermissionPromptPrototype = PermissionPromptPrototype;
+
+/**
+ * A subclass of PermissionPromptPrototype that assumes
+ * that this.request is an nsIContentPermissionRequest
+ * and fills in some of the required properties on the
+ * PermissionPrompt. For callers that are wrapping an
+ * nsIContentPermissionRequest, this should be subclassed
+ * rather than PermissionPromptPrototype.
+ */
+var PermissionPromptForRequestPrototype = {
+ __proto__: PermissionPromptPrototype,
+
+ get browser() {
+ // In the e10s-case, the <xul:browser> will be at request.element.
+ // In the single-process case, we have to use some XPCOM incantations
+ // to resolve to the <xul:browser>.
+ if (this.request.element) {
+ return this.request.element;
+ }
+ return this.request
+ .window
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ },
+
+ get principal() {
+ return this.request.principal;
+ },
+
+ cancel() {
+ this.request.cancel();
+ },
+
+ allow() {
+ this.request.allow();
+ },
+};
+
+PermissionUI.PermissionPromptForRequestPrototype =
+ PermissionPromptForRequestPrototype;
+
+/**
+ * Creates a PermissionPrompt for a nsIContentPermissionRequest for
+ * the GeoLocation API.
+ *
+ * @param request (nsIContentPermissionRequest)
+ * The request for a permission from content.
+ */
+function GeolocationPermissionPrompt(request) {
+ this.request = request;
+}
+
+GeolocationPermissionPrompt.prototype = {
+ __proto__: PermissionPromptForRequestPrototype,
+
+ get permissionKey() {
+ return "geo";
+ },
+
+ get popupOptions() {
+ let options = {
+ displayURI: false,
+ name: this.principal.URI.hostPort,
+ };
+
+ if (this.principal.URI.schemeIs("file")) {
+ options.checkbox = { show: false };
+ } else {
+ // Don't offer "always remember" action in PB mode
+ options.checkbox = {
+ show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
+ };
+ }
+
+ if (options.checkbox.show) {
+ options.checkbox.label = gNotificationBundle.GetStringFromName("geolocation.remember");
+ }
+
+ return options;
+ },
+
+ get notificationID() {
+ return "geolocation";
+ },
+
+ get anchorID() {
+ return "geo-notification-icon";
+ },
+
+ get message() {
+ if (this.principal.URI.schemeIs("file")) {
+ return gNotificationBundle.GetStringFromName("geolocation.shareWithFile3");
+ }
+
+ return gNotificationBundle.formatStringFromName("geolocation.shareWithSite3",
+ ["<>"], 1);
+ },
+
+ get promptActions() {
+ return [{
+ label: gNotificationBundle.GetStringFromName("geolocation.allowLocation"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("geolocation.allowLocation.accesskey"),
+ action: SitePermissions.ALLOW,
+ callback(state) {
+ },
+ }, {
+ label: gNotificationBundle.GetStringFromName("geolocation.dontAllowLocation"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("geolocation.dontAllowLocation.accesskey"),
+ action: SitePermissions.BLOCK,
+ callback(state) {
+ },
+ }];
+ },
+};
+
+PermissionUI.GeolocationPermissionPrompt = GeolocationPermissionPrompt;
+
+/**
+ * Creates a PermissionPrompt for a nsIContentPermissionRequest for
+ * the Desktop Notification API.
+ *
+ * @param request (nsIContentPermissionRequest)
+ * The request for a permission from content.
+ * @return {PermissionPrompt} (see documentation in header)
+ */
+function DesktopNotificationPermissionPrompt(request) {
+ this.request = request;
+}
+
+DesktopNotificationPermissionPrompt.prototype = {
+ __proto__: PermissionPromptForRequestPrototype,
+
+ get permissionKey() {
+ return "desktop-notification";
+ },
+
+ get popupOptions() {
+ return {
+ displayURI: false,
+ name: this.principal.URI.hostPort,
+ };
+ },
+
+ get notificationID() {
+ return "web-notifications";
+ },
+
+ get anchorID() {
+ return "web-notifications-notification-icon";
+ },
+
+ get message() {
+ return gNotificationBundle.formatStringFromName("webNotifications.receiveFromSite2",
+ ["<>"], 1);
+ },
+
+ get promptActions() {
+ let actions = [
+ {
+ label: gNotificationBundle.GetStringFromName("webNotifications.allow"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("webNotifications.allow.accesskey"),
+ action: SitePermissions.ALLOW,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ },
+ {
+ label: gNotificationBundle.GetStringFromName("webNotifications.notNow"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("webNotifications.notNow.accesskey"),
+ action: SitePermissions.BLOCK,
+ },
+ ];
+ if (!PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
+ actions.push({
+ label: gNotificationBundle.GetStringFromName("webNotifications.never"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("webNotifications.never.accesskey"),
+ action: SitePermissions.BLOCK,
+ scope: SitePermissions.SCOPE_PERSISTENT,
+ });
+ }
+ return actions;
+ },
+};
+
+PermissionUI.DesktopNotificationPermissionPrompt =
+ DesktopNotificationPermissionPrompt;
+
+/**
+ * Creates a PermissionPrompt for a nsIContentPermissionRequest for
+ * the persistent-storage API.
+ *
+ * @param request (nsIContentPermissionRequest)
+ * The request for a permission from content.
+ */
+function PersistentStoragePermissionPrompt(request) {
+ this.request = request;
+}
+
+PersistentStoragePermissionPrompt.prototype = {
+ __proto__: PermissionPromptForRequestPrototype,
+
+ get permissionKey() {
+ return "persistent-storage";
+ },
+
+ get popupOptions() {
+ let checkbox = {
+ // In PB mode, we don't want the "always remember" checkbox
+ show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
+ };
+ if (checkbox.show) {
+ checkbox.checked = true;
+ checkbox.label = gNotificationBundle.GetStringFromName("persistentStorage.remember");
+ }
+ return {
+ checkbox,
+ displayURI: false,
+ name: this.principal.URI.hostPort,
+ };
+ },
+
+ get notificationID() {
+ return "persistent-storage";
+ },
+
+ get anchorID() {
+ return "persistent-storage-notification-icon";
+ },
+
+ get message() {
+ return gNotificationBundle.formatStringFromName("persistentStorage.allowWithSite",
+ ["<>"], 1);
+ },
+
+ get promptActions() {
+ return [
+ {
+ label: gNotificationBundle.GetStringFromName("persistentStorage.allow"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("persistentStorage.allow.accesskey"),
+ action: Ci.nsIPermissionManager.ALLOW_ACTION
+ },
+ {
+ label: gNotificationBundle.GetStringFromName("persistentStorage.dontAllow"),
+ accessKey:
+ gNotificationBundle.GetStringFromName("persistentStorage.dontAllow.accesskey"),
+ action: Ci.nsIPermissionManager.DENY_ACTION
+ }
+ ];
+ }
+};
+
+PermissionUI.PersistentStoragePermissionPrompt = PersistentStoragePermissionPrompt;
diff --git a/comm/suite/modules/RecentWindow.jsm b/comm/suite/modules/RecentWindow.jsm
new file mode 100644
index 0000000000..9f3e6d669a
--- /dev/null
+++ b/comm/suite/modules/RecentWindow.jsm
@@ -0,0 +1,66 @@
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["RecentWindow"];
+
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {PrivateBrowsingUtils} = ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+var RecentWindow = {
+ /*
+ * Get the most recent browser window.
+ *
+ * @param aOptions an object accepting the arguments for the search.
+ * * private: true to restrict the search to private windows
+ * only, false to restrict the search to non-private only.
+ * Omit the property to search in both groups.
+ * * allowPopups: true if popup windows are permissable.
+ */
+ getMostRecentBrowserWindow: function RW_getMostRecentBrowserWindow(aOptions) {
+ let checkPrivacy = typeof aOptions == "object" &&
+ "private" in aOptions;
+
+ let allowPopups = typeof aOptions == "object" && !!aOptions.allowPopups;
+
+ function isSuitableBrowserWindow(win) {
+ return (!win.closed &&
+ (allowPopups || win.toolbar.visible) &&
+ (!checkPrivacy ||
+ PrivateBrowsingUtils.permanentPrivateBrowsing ||
+ PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
+ }
+
+ let broken_wm_z_order =
+ AppConstants.platform != "macosx" && AppConstants.platform != "win";
+
+ if (broken_wm_z_order) {
+ let win = Services.wm.getMostRecentWindow("navigator:browser");
+
+ // if we're lucky, this isn't a popup, and we can just return this
+ if (win && !isSuitableBrowserWindow(win)) {
+ win = null;
+ let windowList = Services.wm.getEnumerator("navigator:browser");
+ // this is oldest to newest, so this gets a bit ugly
+ while (windowList.hasMoreElements()) {
+ let nextWin = windowList.getNext();
+ if (isSuitableBrowserWindow(nextWin))
+ win = nextWin;
+ }
+ }
+ return win;
+ } else {
+ let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
+ while (windowList.hasMoreElements()) {
+ let win = windowList.getNext();
+ if (isSuitableBrowserWindow(win))
+ return win;
+ }
+ return null;
+ }
+ }
+};
+
diff --git a/comm/suite/modules/SitePermissions.jsm b/comm/suite/modules/SitePermissions.jsm
new file mode 100644
index 0000000000..40d9a2d44a
--- /dev/null
+++ b/comm/suite/modules/SitePermissions.jsm
@@ -0,0 +1,796 @@
+/* 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 EXPORTED_SYMBOLS = [ "SitePermissions" ];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var gStringBundle =
+ Services.strings.createBundle("chrome://communicator/locale/sitePermissions.properties");
+
+/**
+ * A helper module to manage temporarily blocked permissions.
+ *
+ * Permissions are keyed by browser, so methods take a Browser
+ * element to identify the corresponding permission set.
+ *
+ * This uses a WeakMap to key browsers, so that entries are
+ * automatically cleared once the browser stops existing
+ * (once there are no other references to the browser object);
+ */
+var TemporaryBlockedPermissions = {
+ // This is a three level deep map with the following structure:
+ //
+ // Browser => {
+ // <prePath>: {
+ // <permissionID>: {Number} <timeStamp>
+ // }
+ // }
+ //
+ // Only the top level browser elements are stored via WeakMap. The WeakMap
+ // value is an object with URI prePaths as keys. The keys of that object
+ // are ids that identify permissions that were set for the specific URI.
+ // The final value is an object containing the timestamp of when the permission
+ // was set (in order to invalidate after a certain amount of time has passed).
+ _stateByBrowser: new WeakMap(),
+
+ // Private helper method that bundles some shared behavior for
+ // get() and getAll(), e.g. deleting permissions when they have expired.
+ _get(entry, prePath, id, timeStamp) {
+ if (timeStamp == null) {
+ delete entry[prePath][id];
+ return null;
+ }
+ if (timeStamp + SitePermissions.temporaryPermissionExpireTime < Date.now()) {
+ delete entry[prePath][id];
+ return null;
+ }
+ return {id, state: SitePermissions.BLOCK, scope: SitePermissions.SCOPE_TEMPORARY};
+ },
+
+ // Sets a new permission for the specified browser.
+ set(browser, id) {
+ if (!browser) {
+ return;
+ }
+ if (!this._stateByBrowser.has(browser)) {
+ this._stateByBrowser.set(browser, {});
+ }
+ let entry = this._stateByBrowser.get(browser);
+ let prePath = browser.currentURI.prePath;
+ if (!entry[prePath]) {
+ entry[prePath] = {};
+ }
+ entry[prePath][id] = Date.now();
+ },
+
+ // Removes a permission with the specified id for the specified browser.
+ remove(browser, id) {
+ if (!browser) {
+ return;
+ }
+ let entry = this._stateByBrowser.get(browser);
+ let prePath = browser.currentURI.prePath;
+ if (entry && entry[prePath]) {
+ delete entry[prePath][id];
+ }
+ },
+
+ // Gets a permission with the specified id for the specified browser.
+ get(browser, id) {
+ if (!browser || !browser.currentURI) {
+ return null;
+ }
+ let entry = this._stateByBrowser.get(browser);
+ let prePath = browser.currentURI.prePath;
+ if (entry && entry[prePath]) {
+ let permission = entry[prePath][id];
+ return this._get(entry, prePath, id, permission);
+ }
+ return null;
+ },
+
+ // Gets all permissions for the specified browser.
+ // Note that only permissions that apply to the current URI
+ // of the passed browser element will be returned.
+ getAll(browser) {
+ let permissions = [];
+ let entry = this._stateByBrowser.get(browser);
+ let prePath = browser.currentURI.prePath;
+ if (entry && entry[prePath]) {
+ let timeStamps = entry[prePath];
+ for (let id of Object.keys(timeStamps)) {
+ let permission = this._get(entry, prePath, id, timeStamps[id]);
+ // _get() returns null when the permission has expired.
+ if (permission) {
+ permissions.push(permission);
+ }
+ }
+ }
+ return permissions;
+ },
+
+ // Clears all permissions for the specified browser.
+ // Unlike other methods, this does NOT clear only for
+ // the currentURI but the whole browser state.
+ clear(browser) {
+ this._stateByBrowser.delete(browser);
+ },
+
+ // Copies the temporary permission state of one browser
+ // into a new entry for the other browser.
+ copy(browser, newBrowser) {
+ let entry = this._stateByBrowser.get(browser);
+ if (entry) {
+ this._stateByBrowser.set(newBrowser, entry);
+ }
+ },
+};
+
+/**
+ * A module to manage permanent and temporary permissions
+ * by URI and browser.
+ *
+ * Some methods have the side effect of dispatching a "PermissionStateChange"
+ * event on changes to temporary permissions, as mentioned in the respective docs.
+ */
+var SitePermissions = {
+ // Permission states.
+ UNKNOWN: Services.perms.UNKNOWN_ACTION,
+ ALLOW: Services.perms.ALLOW_ACTION,
+ BLOCK: Services.perms.DENY_ACTION,
+ PROMPT: Services.perms.PROMPT_ACTION,
+ ALLOW_COOKIES_FOR_SESSION: Ci.nsICookiePermission.ACCESS_SESSION,
+
+ // Permission scopes.
+ SCOPE_REQUEST: "{SitePermissions.SCOPE_REQUEST}",
+ SCOPE_TEMPORARY: "{SitePermissions.SCOPE_TEMPORARY}",
+ SCOPE_SESSION: "{SitePermissions.SCOPE_SESSION}",
+ SCOPE_PERSISTENT: "{SitePermissions.SCOPE_PERSISTENT}",
+
+ _defaultPrefBranch: Services.prefs.getBranch("permissions.default."),
+
+ /**
+ * Deprecated! Please use getAllByPrincipal(principal) instead.
+ * Gets all custom permissions for a given URI.
+ * Install addon permission is excluded, check bug 1303108.
+ *
+ * @return {Array} a list of objects with the keys:
+ * - id: the permissionId of the permission
+ * - scope: the scope of the permission (e.g. SitePermissions.SCOPE_TEMPORARY)
+ * - state: a constant representing the current permission state
+ * (e.g. SitePermissions.ALLOW)
+ */
+ getAllByURI(uri) {
+ let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null;
+ return this.getAllByPrincipal(principal);
+ },
+
+ /**
+ * Gets all custom permissions for a given principal.
+ * Install addon permission is excluded, check bug 1303108.
+ *
+ * @return {Array} a list of objects with the keys:
+ * - id: the permissionId of the permission
+ * - scope: the scope of the permission (e.g. SitePermissions.SCOPE_TEMPORARY)
+ * - state: a constant representing the current permission state
+ * (e.g. SitePermissions.ALLOW)
+ */
+ getAllByPrincipal(principal) {
+ let result = [];
+ if (!this.isSupportedPrincipal(principal)) {
+ return result;
+ }
+
+ let permissions = Services.perms.getAllForPrincipal(principal);
+ while (permissions.hasMoreElements()) {
+ let permission = permissions.getNext();
+
+ // filter out unknown permissions
+ if (gPermissionObject[permission.type]) {
+ // XXX Bug 1303108 - Control Center should only show non-default permissions
+ if (permission.type == "install") {
+ continue;
+ }
+ let scope = this.SCOPE_PERSISTENT;
+ if (permission.expireType == Services.perms.EXPIRE_SESSION) {
+ scope = this.SCOPE_SESSION;
+ }
+ result.push({
+ id: permission.type,
+ scope,
+ state: permission.capability,
+ });
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Returns all custom permissions for a given browser.
+ *
+ * To receive a more detailed, albeit less performant listing see
+ * SitePermissions.getAllPermissionDetailsForBrowser().
+ *
+ * @param {Browser} browser
+ * The browser to fetch permission for.
+ *
+ * @return {Array} a list of objects with the keys:
+ * - id: the permissionId of the permission
+ * - state: a constant representing the current permission state
+ * (e.g. SitePermissions.ALLOW)
+ * - scope: a constant representing how long the permission will
+ * be kept.
+ */
+ getAllForBrowser(browser) {
+ let permissions = {};
+
+ for (let permission of TemporaryBlockedPermissions.getAll(browser)) {
+ permission.scope = this.SCOPE_TEMPORARY;
+ permissions[permission.id] = permission;
+ }
+
+ for (let permission of this.getAllByPrincipal(browser.contentPrincipal)) {
+ permissions[permission.id] = permission;
+ }
+
+ return Object.values(permissions);
+ },
+
+ /**
+ * Returns a list of objects with detailed information on all permissions
+ * that are currently set for the given browser.
+ *
+ * @param {Browser} browser
+ * The browser to fetch permission for.
+ *
+ * @return {Array<Object>} a list of objects with the keys:
+ * - id: the permissionID of the permission
+ * - state: a constant representing the current permission state
+ * (e.g. SitePermissions.ALLOW)
+ * - scope: a constant representing how long the permission will
+ * be kept.
+ * - label: the localized label
+ */
+ getAllPermissionDetailsForBrowser(browser) {
+ return this.getAllForBrowser(browser).map(({id, scope, state}) =>
+ ({id, scope, state, label: this.getPermissionLabel(id)}));
+ },
+
+ /**
+ * Deprecated! Please use isSupportedPrincipal(principal) instead.
+ * Checks whether a UI for managing permissions should be exposed for a given
+ * URI.
+ *
+ * @param {nsIURI} uri
+ * The URI to check.
+ *
+ * @return {boolean} if the URI is supported.
+ */
+ isSupportedURI(uri) {
+ return uri && ["file", "http", "https", "moz-extension"].includes(uri.scheme);
+ },
+
+ /**
+ * Checks whether a UI for managing permissions should be exposed for a given
+ * principal.
+ *
+ * @param {nsIPrincipal} principal
+ * The principal to check.
+ *
+ * @return {boolean} if the principal is supported.
+ */
+ isSupportedPrincipal(principal) {
+ return principal && principal.URI &&
+ ["file", "http", "https", "moz-extension"].includes(principal.URI.scheme);
+ },
+
+ /**
+ * Gets an array of all permission IDs.
+ *
+ * @return {Array<String>} an array of all permission IDs.
+ */
+ listPermissions() {
+ return Object.keys(gPermissionObject);
+ },
+
+ /**
+ * Returns an array of permission states to be exposed to the user for a
+ * permission with the given ID.
+ *
+ * @param {string} permissionID
+ * The ID to get permission states for.
+ *
+ * @return {Array<SitePermissions state>} an array of all permission states.
+ */
+ getAvailableStates(permissionID) {
+ if (permissionID in gPermissionObject &&
+ gPermissionObject[permissionID].states)
+ return gPermissionObject[permissionID].states;
+
+ /* Since the permissions we are dealing with have adopted the convention
+ * of treating UNKNOWN == PROMPT, we only include one of either UNKNOWN
+ * or PROMPT in this list, to avoid duplicating states. */
+ if (this.getDefault(permissionID) == this.UNKNOWN)
+ return [ SitePermissions.UNKNOWN, SitePermissions.ALLOW, SitePermissions.BLOCK ];
+
+ return [ SitePermissions.PROMPT, SitePermissions.ALLOW, SitePermissions.BLOCK ];
+ },
+
+ /**
+ * Returns the default state of a particular permission.
+ *
+ * @param {string} permissionID
+ * The ID to get the default for.
+ *
+ * @return {SitePermissions.state} the default state.
+ */
+ getDefault(permissionID) {
+ // If the permission has custom logic for getting its default value,
+ // try that first.
+ if (permissionID in gPermissionObject &&
+ gPermissionObject[permissionID].getDefault)
+ return gPermissionObject[permissionID].getDefault();
+
+ // Otherwise try to get the default preference for that permission.
+ return this._defaultPrefBranch.getIntPref(permissionID, this.UNKNOWN);
+ },
+
+ /**
+ * Set the default state of a particular permission.
+ *
+ * @param {string} permissionID
+ * The ID to set the default for.
+ *
+ * @param {string} state
+ * The state to set.
+ */
+ setDefault(permissionID, state) {
+ if (permissionID in gPermissionObject &&
+ gPermissionObject[permissionID].setDefault) {
+ return gPermissionObject[permissionID].setDefault(state);
+ }
+ let key = "permissions.default." + permissionID;
+ return Services.prefs.setIntPref(key, state);
+ },
+ /**
+ * Returns the state and scope of a particular permission for a given URI.
+ *
+ * This method will NOT dispatch a "PermissionStateChange" event on the specified
+ * browser if a temporary permission was removed because it has expired.
+ *
+ * @param {nsIURI} uri
+ * The URI to check.
+ * @param {String} permissionID
+ * The id of the permission.
+ * @param {Browser} browser (optional)
+ * The browser object to check for temporary permissions.
+ *
+ * @return {Object} an object with the keys:
+ * - state: The current state of the permission
+ * (e.g. SitePermissions.ALLOW)
+ * - scope: The scope of the permission
+ * (e.g. SitePermissions.SCOPE_PERSISTENT)
+ */
+ get(uri, permissionID, browser) {
+ let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null;
+ return this.getForPrincipal(principal, permissionID, browser);
+ },
+
+ /**
+ * Returns the state and scope of a particular permission for a given
+ * principal.
+ *
+ * This method will NOT dispatch a "PermissionStateChange" event on the
+ * specified browser if a temporary permission was removed because it has
+ * expired.
+ *
+ * @param {nsIPrincipal} principal
+ * The principal to check.
+ * @param {String} permissionID
+ * The id of the permission.
+ * @param {Browser} browser (optional)
+ * The browser object to check for temporary permissions.
+ *
+ * @return {Object} an object with the keys:
+ * - state: The current state of the permission
+ * (e.g. SitePermissions.ALLOW)
+ * - scope: The scope of the permission
+ * (e.g. SitePermissions.SCOPE_PERSISTENT)
+ */
+ getForPrincipal(principal, permissionID, browser) {
+ let defaultState = this.getDefault(permissionID);
+ let result = { state: defaultState, scope: this.SCOPE_PERSISTENT };
+ if (this.isSupportedPrincipal(principal)) {
+ let permission = null;
+ if (permissionID in gPermissionObject &&
+ gPermissionObject[permissionID].exactHostMatch) {
+ permission = Services.perms.getPermissionObject(principal, permissionID, true);
+ } else {
+ permission = Services.perms.getPermissionObject(principal, permissionID, false);
+ }
+
+ if (permission) {
+ result.state = permission.capability;
+ if (permission.expireType == Services.perms.EXPIRE_SESSION) {
+ result.scope = this.SCOPE_SESSION;
+ }
+ }
+ }
+
+ if (result.state == defaultState) {
+ // If there's no persistent permission saved, check if we have something
+ // set temporarily.
+ let value = TemporaryBlockedPermissions.get(browser, permissionID);
+
+ if (value) {
+ result.state = value.state;
+ result.scope = this.SCOPE_TEMPORARY;
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Deprecated! Use setForPrincipal(...) instead.
+ * Sets the state of a particular permission for a given URI or browser.
+ * This method will dispatch a "PermissionStateChange" event on the specified
+ * browser if a temporary permission was set
+ *
+ * @param {nsIURI} uri
+ * The URI to set the permission for.
+ * Note that this will be ignored if the scope is set to SCOPE_TEMPORARY
+ * @param {String} permissionID
+ * The id of the permission.
+ * @param {SitePermissions state} state
+ * The state of the permission.
+ * @param {SitePermissions scope} scope (optional)
+ * The scope of the permission. Defaults to SCOPE_PERSISTENT.
+ * @param {Browser} browser (optional)
+ * The browser object to set temporary permissions on.
+ * This needs to be provided if the scope is SCOPE_TEMPORARY!
+ */
+ set(uri, permissionID, state, scope = this.SCOPE_PERSISTENT, browser = null) {
+ let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null;
+ return this.setForPrincipal(principal, permissionID, state, scope, browser);
+ },
+
+ /**
+ * Sets the state of a particular permission for a given principal or browser.
+ * This method will dispatch a "PermissionStateChange" event on the specified
+ * browser if a temporary permission was set
+ *
+ * @param {nsIPrincipal} principal
+ * The principal to set the permission for.
+ * Note that this will be ignored if the scope is set to SCOPE_TEMPORARY
+ * @param {String} permissionID
+ * The id of the permission.
+ * @param {SitePermissions state} state
+ * The state of the permission.
+ * @param {SitePermissions scope} scope (optional)
+ * The scope of the permission. Defaults to SCOPE_PERSISTENT.
+ * @param {Browser} browser (optional)
+ * The browser object to set temporary permissions on.
+ * This needs to be provided if the scope is SCOPE_TEMPORARY!
+ */
+ setForPrincipal(principal, permissionID, state,
+ scope = this.SCOPE_PERSISTENT, browser = null) {
+ if (state == this.UNKNOWN || state == this.getDefault(permissionID)) {
+ // Because they are controlled by two prefs with many states that do not
+ // correspond to the classical ALLOW/DENY/PROMPT model, we want to always
+ // allow the user to add exceptions to their cookie rules without
+ // removing them.
+ if (permissionID != "cookie") {
+ this.removeFromPrincipal(principal, permissionID, browser);
+ return;
+ }
+ }
+
+ if (state == this.ALLOW_COOKIES_FOR_SESSION && permissionID != "cookie") {
+ throw "ALLOW_COOKIES_FOR_SESSION can only be set on the cookie permission";
+ }
+
+ // Save temporary permissions.
+ if (scope == this.SCOPE_TEMPORARY) {
+ // We do not support setting temp ALLOW for security reasons.
+ // In its current state, this permission could be exploited by subframes
+ // on the same page. This is because for BLOCK we ignore the request
+ // principal and only consider the current browser principal, to avoid
+ // notification spamming.
+ //
+ // If you ever consider removing this line, you likely want to implement
+ // a more fine-grained TemporaryBlockedPermissions that temporarily blocks for the
+ // entire browser, but temporarily allows only for specific frames.
+ if (state != this.BLOCK) {
+ throw "'Block' is the only permission we can save temporarily on a browser";
+ }
+
+ if (!browser) {
+ throw "TEMPORARY scoped permissions require a browser object";
+ }
+
+ TemporaryBlockedPermissions.set(browser, permissionID);
+
+ browser.dispatchEvent(new browser.ownerGlobal
+ .CustomEvent("PermissionStateChange"));
+ } else if (this.isSupportedPrincipal(principal)) {
+ let perms_scope = Services.perms.EXPIRE_NEVER;
+ if (scope == this.SCOPE_SESSION) {
+ perms_scope = Services.perms.EXPIRE_SESSION;
+ }
+
+ Services.perms.addFromPrincipal(principal, permissionID, state, perms_scope);
+ }
+ },
+
+ /**
+ * Deprecated! Please use removeFromPrincipal(principal, permissionID, browser).
+ * Removes the saved state of a particular permission for a given URI and/or browser.
+ * This method will dispatch a "PermissionStateChange" event on the specified
+ * browser if a temporary permission was removed.
+ *
+ * @param {nsIURI} uri
+ * The URI to remove the permission for.
+ * @param {String} permissionID
+ * The id of the permission.
+ * @param {Browser} browser (optional)
+ * The browser object to remove temporary permissions on.
+ */
+ remove(uri, permissionID, browser) {
+ let principal = uri ? Services.scriptSecurityManager.createCodebasePrincipal(uri, {}) : null;
+ return this.removeFromPrincipal(principal, permissionID, browser);
+ },
+
+ /**
+ * Removes the saved state of a particular permission for a given principal
+ * and/or browser.
+ * This method will dispatch a "PermissionStateChange" event on the specified
+ * browser if a temporary permission was removed.
+ *
+ * @param {nsIPrincipal} principal
+ * The principal to remove the permission for.
+ * @param {String} permissionID
+ * The id of the permission.
+ * @param {Browser} browser (optional)
+ * The browser object to remove temporary permissions on.
+ */
+ removeFromPrincipal(principal, permissionID, browser) {
+ if (this.isSupportedPrincipal(principal))
+ Services.perms.removeFromPrincipal(principal, permissionID);
+
+ // TemporaryBlockedPermissions.get() deletes expired permissions automatically,
+ if (TemporaryBlockedPermissions.get(browser, permissionID)) {
+ // If it exists but has not expired, remove it explicitly.
+ TemporaryBlockedPermissions.remove(browser, permissionID);
+ // Send a PermissionStateChange event only if the permission hasn't expired.
+ browser.dispatchEvent(new browser.ownerGlobal
+ .CustomEvent("PermissionStateChange"));
+ }
+ },
+
+ /**
+ * Clears all permissions that were temporarily saved.
+ *
+ * @param {Browser} browser
+ * The browser object to clear.
+ */
+ clearTemporaryPermissions(browser) {
+ TemporaryBlockedPermissions.clear(browser);
+ },
+
+ /**
+ * Copy all permissions that were temporarily saved on one
+ * browser object to a new browser.
+ *
+ * @param {Browser} browser
+ * The browser object to copy from.
+ * @param {Browser} newBrowser
+ * The browser object to copy to.
+ */
+ copyTemporaryPermissions(browser, newBrowser) {
+ TemporaryBlockedPermissions.copy(browser, newBrowser);
+ },
+
+ /**
+ * Returns the localized label for the permission with the given ID, to be
+ * used in a UI for managing permissions.
+ *
+ * @param {string} permissionID
+ * The permission to get the label for.
+ *
+ * @return {String} the localized label.
+ */
+ getPermissionLabel(permissionID) {
+ let labelID = gPermissionObject[permissionID].labelID || permissionID;
+ return gStringBundle.GetStringFromName("permission." + labelID + ".label");
+ },
+
+ /**
+ * Returns the localized label for the given permission state, to be used in
+ * a UI for managing permissions.
+ *
+ * @param {string} permissionID
+ * The permission to get the label for.
+ *
+ * @param {SitePermissions state} state
+ * The state to get the label for.
+ *
+ * @return {String|null} the localized label or null if an
+ * unknown state was passed.
+ */
+ getMultichoiceStateLabel(permissionID, state) {
+ // If the permission has custom logic for getting its default value,
+ // try that first.
+ if (permissionID in gPermissionObject &&
+ gPermissionObject[permissionID].getMultichoiceStateLabel) {
+ return gPermissionObject[permissionID].getMultichoiceStateLabel(state);
+ }
+
+ switch (state) {
+ case this.UNKNOWN:
+ case this.PROMPT:
+ return gStringBundle.GetStringFromName("state.multichoice.alwaysAsk");
+ case this.ALLOW:
+ return gStringBundle.GetStringFromName("state.multichoice.allow");
+ case this.ALLOW_COOKIES_FOR_SESSION:
+ return gStringBundle.GetStringFromName("state.multichoice.allowForSession");
+ case this.BLOCK:
+ return gStringBundle.GetStringFromName("state.multichoice.block");
+ default:
+ return null;
+ }
+ },
+
+ /**
+ * Returns the localized label for a permission's current state.
+ *
+ * @param {SitePermissions state} state
+ * The state to get the label for.
+ * @param {string} id
+ * The permission to get the state label for.
+ * @param {SitePermissions scope} scope (optional)
+ * The scope to get the label for.
+ *
+ * @return {String|null} the localized label or null if an
+ * unknown state was passed.
+ */
+ getCurrentStateLabel(state, id, scope = null) {
+ switch (state) {
+ case this.PROMPT:
+ return gStringBundle.GetStringFromName("state.current.prompt");
+ case this.ALLOW:
+ if (scope && scope != this.SCOPE_PERSISTENT)
+ return gStringBundle.GetStringFromName("state.current.allowedTemporarily");
+ return gStringBundle.GetStringFromName("state.current.allowed");
+ case this.ALLOW_COOKIES_FOR_SESSION:
+ return gStringBundle.GetStringFromName("state.current.allowedForSession");
+ case this.BLOCK:
+ if (scope && scope != this.SCOPE_PERSISTENT)
+ return gStringBundle.GetStringFromName("state.current.blockedTemporarily");
+ return gStringBundle.GetStringFromName("state.current.blocked");
+ default:
+ return null;
+ }
+ },
+};
+
+var gPermissionObject = {
+ /* Holds permission ID => options pairs.
+ *
+ * Supported options:
+ *
+ * - exactHostMatch
+ * Allows sub domains to have their own permissions.
+ * Defaults to false.
+ *
+ * - getDefault
+ * Called to get the permission's default state.
+ * Defaults to UNKNOWN, indicating that the user will be asked each time
+ * a page asks for that permissions.
+ *
+ * - labelID
+ * Use the given ID instead of the permission name for looking up strings.
+ * e.g. "desktop-notification2" to use permission.desktop-notification2.label
+ *
+ * - states
+ * Array of permission states to be exposed to the user.
+ * Defaults to ALLOW, BLOCK and the default state (see getDefault).
+ *
+ * - getMultichoiceStateLabel
+ * Allows for custom logic for getting its default value
+ */
+
+ "image": {
+ states: [
+ SitePermissions.ALLOW,
+ SitePermissions.PROMPT,
+ SitePermissions.BLOCK
+ ],
+ getMultichoiceStateLabel(state) {
+ switch (state) {
+ case SitePermissions.ALLOW:
+ return gStringBundle.GetStringFromName("state.multichoice.allow");
+ // Equates to BEHAVIOR_NOFOREIGN from nsContentBlocker.cpp
+ case SitePermissions.PROMPT:
+ return gStringBundle.GetStringFromName("state.multichoice.allowForSameDomain");
+ case SitePermissions.BLOCK:
+ return gStringBundle.GetStringFromName("state.multichoice.block");
+ }
+ throw new Error(`Unknown state: ${state}`);
+ },
+ },
+
+ "cookie": {
+ states: [ SitePermissions.ALLOW, SitePermissions.ALLOW_COOKIES_FOR_SESSION, SitePermissions.BLOCK ],
+ getDefault() {
+ if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == Ci.nsICookieService.BEHAVIOR_REJECT)
+ return SitePermissions.BLOCK;
+
+ if (Services.prefs.getIntPref("network.cookie.lifetimePolicy") == Ci.nsICookieService.ACCEPT_SESSION)
+ return SitePermissions.ALLOW_COOKIES_FOR_SESSION;
+
+ return SitePermissions.ALLOW;
+ }
+ },
+
+ "desktop-notification": {
+ exactHostMatch: true,
+ labelID: "desktop-notification2",
+ },
+
+ "camera": {
+ exactHostMatch: true,
+ },
+
+ "microphone": {
+ exactHostMatch: true,
+ },
+
+ "screen": {
+ exactHostMatch: true,
+ states: [ SitePermissions.UNKNOWN, SitePermissions.BLOCK ],
+ },
+
+ "popup": {
+ getDefault() {
+ return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
+ SitePermissions.BLOCK : SitePermissions.ALLOW;
+ },
+ states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
+ },
+
+ "install": {
+ getDefault() {
+ return Services.prefs.getBoolPref("xpinstall.whitelist.required") ?
+ SitePermissions.BLOCK : SitePermissions.ALLOW;
+ },
+ states: [ SitePermissions.ALLOW, SitePermissions.BLOCK ],
+ },
+
+ "geo": {
+ exactHostMatch: true
+ },
+
+ "focus-tab-by-prompt": {
+ exactHostMatch: true,
+ states: [ SitePermissions.UNKNOWN, SitePermissions.ALLOW ],
+ },
+
+ "persistent-storage": {
+ exactHostMatch: true
+ },
+
+};
+
+// Delete this entry while being pre-off
+// or the persistent-storage permission would appear in Page info's Permission section
+if (!Services.prefs.getBoolPref("browser.storageManager.enabled")) {
+ delete gPermissionObject["persistent-storage"];
+}
+
+XPCOMUtils.defineLazyPreferenceGetter(SitePermissions, "temporaryPermissionExpireTime",
+ "privacy.temporary_permission_expire_time_ms", 3600 * 1000);
diff --git a/comm/suite/modules/ThemeVariableMap.jsm b/comm/suite/modules/ThemeVariableMap.jsm
new file mode 100644
index 0000000000..8da6c4e476
--- /dev/null
+++ b/comm/suite/modules/ThemeVariableMap.jsm
@@ -0,0 +1,29 @@
+/* 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 EXPORTED_SYMBOLS = ["ThemeVariableMap"];
+
+const ThemeVariableMap = [
+ ["--lwt-accent-color-inactive", "accentcolorInactive"],
+ ["--lwt-background-alignment", "backgroundsAlignment"],
+ ["--lwt-background-tiling", "backgroundsTiling"],
+ ["--tab-loading-fill", "tab_loading"],
+ ["--lwt-tab-text", "tab_text"],
+ ["--toolbar-bgcolor", "toolbarColor"],
+ ["--toolbar-color", "toolbar_text"],
+ ["--url-and-searchbar-background-color", "toolbar_field"],
+ ["--url-and-searchbar-color", "toolbar_field_text"],
+ ["--lwt-toolbar-field-border-color", "toolbar_field_border"],
+ ["--urlbar-separator-color", "toolbar_field_separator"],
+ ["--tabs-border-color", "toolbar_top_separator"],
+ ["--lwt-toolbar-vertical-separator", "toolbar_vertical_separator"],
+ ["--toolbox-border-bottom-color", "toolbar_bottom_separator"],
+ ["--lwt-toolbarbutton-icon-fill", "icon_color"],
+ ["--lwt-toolbarbutton-icon-fill-attention", "icon_attention_color"],
+ ["--lwt-toolbarbutton-hover-background", "button_background_hover"],
+ ["--lwt-toolbarbutton-active-background", "button_background_active"],
+ ["--arrowpanel-background", "popup"],
+ ["--arrowpanel-color", "popup_text"],
+ ["--arrowpanel-border-color", "popup_border"],
+];
diff --git a/comm/suite/modules/WindowsJumpLists.jsm b/comm/suite/modules/WindowsJumpLists.jsm
new file mode 100644
index 0000000000..2a19f71628
--- /dev/null
+++ b/comm/suite/modules/WindowsJumpLists.jsm
@@ -0,0 +1,604 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+/**
+ * Constants
+ */
+
+// Stop updating jumplists after some idle time.
+const IDLE_TIMEOUT_SECONDS = 5 * 60;
+
+// Prefs
+const PREF_TASKBAR_BRANCH = "browser.taskbar.lists.";
+const PREF_TASKBAR_ENABLED = "enabled";
+const PREF_TASKBAR_ITEMCOUNT = "maxListItemCount";
+const PREF_TASKBAR_FREQUENT = "frequent.enabled";
+const PREF_TASKBAR_RECENT = "recent.enabled";
+const PREF_TASKBAR_TASKS = "tasks.enabled";
+const PREF_TASKBAR_REFRESH = "refreshInSeconds";
+
+// Hash keys for pendingStatements.
+const LIST_TYPE = {
+ FREQUENT: 0
+, RECENT: 1
+}
+
+/**
+ * Exports
+ */
+
+var EXPORTED_SYMBOLS = [
+ "WinTaskbarJumpList",
+];
+
+/**
+ * Smart getters
+ */
+
+XPCOMUtils.defineLazyGetter(this, "_prefs", function() {
+ return Services.prefs.getBranch(PREF_TASKBAR_BRANCH);
+});
+
+XPCOMUtils.defineLazyGetter(this, "_stringBundle", function() {
+ return Services.strings
+ .createBundle("chrome://navigator/locale/taskbar.properties");
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "_idle",
+ "@mozilla.org/widget/idleservice;1",
+ "nsIIdleService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService",
+ "@mozilla.org/windows-taskbar;1",
+ "nsIWinTaskbar");
+
+ChromeUtils.defineModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+
+/**
+ * Global functions
+ */
+
+function _getString(name) {
+ return _stringBundle.GetStringFromName(name);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Task list configuration data object.
+
+var tasksCfg = [
+ /**
+ * Task configuration options: title, description, args, iconIndex, open, close.
+ *
+ * title - Task title displayed in the list. (strings in the table are temp fillers.)
+ * description - Tooltip description on the list item.
+ * args - Command line args to invoke the task.
+ * iconIndex - Optional win icon index into the main application for the
+ * list item.
+ * open - Boolean indicates if the command should be visible after the browser opens.
+ * close - Boolean indicates if the command should be visible after the browser closes.
+ */
+ // Open new tab
+ {
+ get title() { return _getString("taskbar.tasks.newTab.label"); },
+ get description() { return _getString("taskbar.tasks.newTab.description"); },
+ args: "-new-tab about:blank",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // The jump list already has an app launch icon, but
+ // we don't always update the list on shutdown.
+ // Thus true for consistency.
+ },
+
+ // Open new window
+ {
+ get title() { return _getString("taskbar.tasks.newWindow.label"); },
+ get description() { return _getString("taskbar.tasks.newWindow.description"); },
+ args: "-browser",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // No point, but we don't always update the list on
+ // shutdown. Thus true for consistency.
+ },
+
+ // Open private window
+ {
+ get title() { return _getString("taskbar.tasks.newPrivate.label"); },
+ get description() { return _getString("taskbar.tasks.newPrivate.description"); },
+ args: "-private",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // No point, but we don't always update the list on
+ // shutdown. Thus true for consistency.
+ },
+
+ // Open mailnews
+ {
+ get title() { return _getString("taskbar.tasks.mailWindow.label"); },
+ get description() { return _getString("taskbar.tasks.mailWindow.description"); },
+ args: "-mail",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // No point, but we don't always update the list on
+ // shutdown. Thus true for consistency.
+ },
+
+ // Compose Message
+ {
+ get title() { return _getString("taskbar.tasks.composeMessage.label"); },
+ get description() { return _getString("taskbar.tasks.composeMessage.description"); },
+ args: "-compose",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // No point, but we don't always update the list on
+ // shutdown. Thus true for consistency.
+ },
+
+ // Address Book
+ {
+ get title() { return _getString("taskbar.tasks.openAddressBook.label"); },
+ get description() { return _getString("taskbar.tasks.openAddressBook.description"); },
+ args: "-addressbook",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // No point, but we don't always update the list on
+ // shutdown. Thus true for consistency.
+ },
+
+ // Composer
+ {
+ get title() { return _getString("taskbar.tasks.openEditor.label"); },
+ get description() { return _getString("taskbar.tasks.openEditor.description"); },
+ args: "-edit",
+ iconIndex: 0, // SeaMonkey app icon
+ open: true,
+ close: true, // No point, but we don't always update the list on
+ // shutdown. Thus true for consistency.
+ },
+];
+
+/////////////////////////////////////////////////////////////////////////////
+// Implementation
+
+var WinTaskbarJumpList =
+{
+ _builder: null,
+ _tasks: null,
+ _shuttingDown: false,
+
+ /**
+ * Startup, shutdown, and update
+ */
+
+ startup: function WTBJL_startup() {
+ // exit if this isn't win7 or higher.
+ if (!this._initTaskbar())
+ return;
+
+ // Store our task list config data
+ this._tasks = tasksCfg;
+
+ // retrieve taskbar related prefs.
+ this._refreshPrefs();
+
+ // observer for our prefs branch
+ this._initObs();
+
+ // jump list refresh timer
+ this._updateTimer();
+ },
+
+ update: function WTBJL_update() {
+ // are we disabled via prefs? don't do anything!
+ if (!this._enabled)
+ return;
+
+ // do what we came here to do, update the taskbar jumplist
+ this._buildList();
+ },
+
+ _shutdown: function WTBJL__shutdown() {
+ this._shuttingDown = true;
+
+ // Correctly handle a clear history on shutdown. If there are no
+ // entries be sure to empty all history lists. Luckily Places caches
+ // this value, so it's a pretty fast call.
+ if (!PlacesUtils.history.hasHistoryEntries) {
+ this.update();
+ }
+
+ this._free();
+ },
+
+ /**
+ * List building
+ *
+ * @note Async builders must add their mozIStoragePendingStatement to
+ * _pendingStatements object, using a different LIST_TYPE entry for
+ * each statement. Once finished they must remove it and call
+ * commitBuild(). When there will be no more _pendingStatements,
+ * commitBuild() will commit for real.
+ */
+
+ _pendingStatements: {},
+ _hasPendingStatements: function WTBJL__hasPendingStatements() {
+ return Object.keys(this._pendingStatements).length > 0;
+ },
+
+ _buildList: function WTBJL__buildList() {
+ if (this._hasPendingStatements()) {
+ // We were requested to update the list while another update was in
+ // progress, this could happen at shutdown or idle.
+ // Abort the current list building.
+ for (let listType in this._pendingStatements) {
+ this._pendingStatements[listType].cancel();
+ delete this._pendingStatements[listType];
+ }
+ this._builder.abortListBuild();
+ }
+
+ // anything to build?
+ if (!this._showFrequent && !this._showRecent && !this._showTasks) {
+ // don't leave the last list hanging on the taskbar.
+ this._deleteActiveJumpList();
+ return;
+ }
+
+ if (!this._startBuild())
+ return;
+
+ if (this._showTasks)
+ this._buildTasks();
+
+ // Space for frequent items takes priority over recent.
+ if (this._showFrequent)
+ this._buildFrequent();
+
+ if (this._showRecent)
+ this._buildRecent();
+
+ this._commitBuild();
+ },
+
+ /**
+ * Taskbar api wrappers
+ */
+
+ _startBuild: function WTBJL__startBuild() {
+ var removedItems = Cc["@mozilla.org/array;1"].
+ createInstance(Ci.nsIMutableArray);
+ this._builder.abortListBuild();
+ if (this._builder.initListBuild(removedItems)) {
+ // Prior to building, delete removed items from history.
+ this._clearHistory(removedItems);
+ return true;
+ }
+ return false;
+ },
+
+ _commitBuild: function WTBJL__commitBuild() {
+
+ if (this._hasPendingStatements()) {
+ return;
+ }
+
+ this._builder.commitListBuild(succeed => {
+ if (!succeed) {
+ this._builder.abortListBuild();
+ }
+ });
+ },
+
+ _buildTasks: function WTBJL__buildTasks() {
+ var items = Cc["@mozilla.org/array;1"].
+ createInstance(Ci.nsIMutableArray);
+ this._tasks.forEach(function (task) {
+ if ((this._shuttingDown && !task.close) || (!this._shuttingDown && !task.open))
+ return;
+ var item = this._getHandlerAppItem(task.title, task.description,
+ task.args, task.iconIndex, null);
+ items.appendElement(item);
+ }, this);
+
+ if (items.length > 0)
+ this._builder.addListToBuild(this._builder.JUMPLIST_CATEGORY_TASKS, items);
+ },
+
+ _buildCustom: function WTBJL__buildCustom(title, items) {
+ if (items.length > 0)
+ this._builder.addListToBuild(this._builder.JUMPLIST_CATEGORY_CUSTOMLIST, items, title);
+ },
+
+ _buildFrequent: function WTBJL__buildFrequent() {
+ // If history is empty, just bail out.
+ if (!PlacesUtils.history.hasHistoryEntries) {
+ return;
+ }
+
+ // Windows supports default frequent and recent lists,
+ // but those depend on internal windows visit tracking
+ // which we don't populate. So we build our own custom
+ // frequent and recent lists using our nav history data.
+
+ var items = Cc["@mozilla.org/array;1"].
+ createInstance(Ci.nsIMutableArray);
+ // track frequent items so that we don't add them to
+ // the recent list.
+ this._frequentHashList = [];
+
+ this._pendingStatements[LIST_TYPE.FREQUENT] = this._getHistoryResults(
+ Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING,
+ this._maxItemCount,
+ function (aResult) {
+ if (!aResult) {
+ delete this._pendingStatements[LIST_TYPE.FREQUENT];
+ // The are no more results, build the list.
+ this._buildCustom(_getString("taskbar.frequent.label"), items);
+ this._commitBuild();
+ return;
+ }
+
+ let title = aResult.title || aResult.uri;
+ let faviconPageUri = Services.io.newURI(aResult.uri);
+ let shortcut = this._getHandlerAppItem(title, title, aResult.uri, 2,
+ faviconPageUri);
+ items.appendElement(shortcut);
+ this._frequentHashList.push(aResult.uri);
+ },
+ this
+ );
+ },
+
+ _buildRecent: function WTBJL__buildRecent() {
+ // If history is empty, just bail out.
+ if (!PlacesUtils.history.hasHistoryEntries) {
+ return;
+ }
+
+ var items = Cc["@mozilla.org/array;1"].
+ createInstance(Ci.nsIMutableArray);
+ // Frequent items will be skipped, so we select a double amount of
+ // entries and stop fetching results at _maxItemCount.
+ var count = 0;
+
+ this._pendingStatements[LIST_TYPE.RECENT] = this._getHistoryResults(
+ Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING,
+ this._maxItemCount * 2,
+ function (aResult) {
+ if (!aResult) {
+ // The are no more results, build the list.
+ this._buildCustom(_getString("taskbar.recent.label"), items);
+ delete this._pendingStatements[LIST_TYPE.RECENT];
+ this._commitBuild();
+ return;
+ }
+
+ if (count >= this._maxItemCount) {
+ return;
+ }
+
+ // Do not add items to recent that have already been added to frequent.
+ if (this._frequentHashList &&
+ this._frequentHashList.includes(aResult.uri)) {
+ return;
+ }
+
+ let title = aResult.title || aResult.uri;
+ let faviconPageUri = Services.io.newURI(aResult.uri);
+ let shortcut = this._getHandlerAppItem(title, title, aResult.uri, 2,
+ faviconPageUri);
+ items.appendElement(shortcut);
+ count++;
+ },
+ this
+ );
+ },
+
+ _deleteActiveJumpList: function WTBJL__deleteAJL() {
+ this._builder.deleteActiveList();
+ },
+
+ /**
+ * Jump list item creation helpers
+ */
+
+ _getHandlerAppItem: function WTBJL__getHandlerAppItem(name, description,
+ args, iconIndex,
+ faviconPageUri) {
+ var file = Services.dirsvc.get("XREExeF", Ci.nsIFile);
+
+ var handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Ci.nsILocalHandlerApp);
+ handlerApp.executable = file;
+ // handlers default to the leaf name if a name is not specified
+ if (name && name.length != 0)
+ handlerApp.name = name;
+ handlerApp.detailedDescription = description;
+ handlerApp.appendParameter(args);
+
+ var item = Cc["@mozilla.org/windows-jumplistshortcut;1"].
+ createInstance(Ci.nsIJumpListShortcut);
+ item.app = handlerApp;
+ item.iconIndex = iconIndex;
+ item.faviconPageUri = faviconPageUri;
+ return item;
+ },
+
+ _getSeparatorItem: function WTBJL__getSeparatorItem() {
+ var item = Cc["@mozilla.org/windows-jumplistseparator;1"].
+ createInstance(Ci.nsIJumpListSeparator);
+ return item;
+ },
+
+ /**
+ * Nav history helpers
+ */
+
+ _getHistoryResults:
+ function WTBLJL__getHistoryResults(aSortingMode, aLimit, aCallback, aScope) {
+ var options = PlacesUtils.history.getNewQueryOptions();
+ options.maxResults = aLimit;
+ options.sortingMode = aSortingMode;
+ var query = PlacesUtils.history.getNewQuery();
+
+ // Return the pending statement to the caller, to allow cancelation.
+ return PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+ .asyncExecuteLegacyQueries([query], 1, options, {
+ handleResult: function (aResultSet) {
+ for (let row; (row = aResultSet.getNextRow());) {
+ try {
+ aCallback.call(aScope,
+ { uri: row.getResultByIndex(1)
+ , title: row.getResultByIndex(2)
+ });
+ } catch (e) {}
+ }
+ },
+ handleError: function (aError) {
+ Cu.reportError(
+ "Async execution error (" + aError.result + "): " + aError.message);
+ },
+ handleCompletion: function (aReason) {
+ aCallback.call(WinTaskbarJumpList, null);
+ },
+ });
+ },
+
+ _clearHistory: function WTBJL__clearHistory(items) {
+ if (!items)
+ return;
+ var URIsToRemove = [];
+ var e = items.enumerate();
+ while (e.hasMoreElements()) {
+ let oldItem = e.getNext().QueryInterface(Ci.nsIJumpListShortcut);
+ if (oldItem) {
+ try { // in case we get a bad uri
+ let uriSpec = oldItem.app.getParameter(0);
+ URIsToRemove.push(Services.io.newURI(uriSpec));
+ } catch (err) { }
+ }
+ }
+ if (URIsToRemove.length > 0) {
+ PlacesUtils.history.remove(URIsToRemove).catch(Cu.reportError);
+ }
+ },
+
+ /**
+ * Prefs utilities
+ */
+
+ _refreshPrefs: function WTBJL__refreshPrefs() {
+ this._enabled = _prefs.getBoolPref(PREF_TASKBAR_ENABLED);
+ this._showFrequent = _prefs.getBoolPref(PREF_TASKBAR_FREQUENT);
+ this._showRecent = _prefs.getBoolPref(PREF_TASKBAR_RECENT);
+ this._showTasks = _prefs.getBoolPref(PREF_TASKBAR_TASKS);
+ this._maxItemCount = _prefs.getIntPref(PREF_TASKBAR_ITEMCOUNT);
+ },
+
+ /**
+ * Init and shutdown utilities
+ */
+
+ _initTaskbar: function WTBJL__initTaskbar() {
+ this._builder = _taskbarService.createJumpListBuilder();
+ if (!this._builder || !this._builder.available)
+ return false;
+
+ return true;
+ },
+
+ _initObs: function WTBJL__initObs() {
+ // If the browser is closed while in private browsing mode, the "exit"
+ // notification is fired on quit-application-granted.
+ // History cleanup can happen at profile-change-teardown.
+ Services.obs.addObserver(this, "profile-before-change");
+ Services.obs.addObserver(this, "browser:purge-session-history");
+ _prefs.addObserver("", this);
+ },
+
+ _freeObs: function WTBJL__freeObs() {
+ Services.obs.removeObserver(this, "profile-before-change");
+ Services.obs.removeObserver(this, "browser:purge-session-history");
+ _prefs.removeObserver("", this);
+ },
+
+ _updateTimer: function WTBJL__updateTimer() {
+ if (this._enabled && !this._shuttingDown && !this._timer) {
+ this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ this._timer.initWithCallback(this,
+ _prefs.getIntPref(PREF_TASKBAR_REFRESH)*1000,
+ this._timer.TYPE_REPEATING_SLACK);
+ }
+ else if ((!this._enabled || this._shuttingDown) && this._timer) {
+ this._timer.cancel();
+ delete this._timer;
+ }
+ },
+
+ _hasIdleObserver: false,
+ _updateIdleObserver: function WTBJL__updateIdleObserver() {
+ if (this._enabled && !this._shuttingDown && !this._hasIdleObserver) {
+ _idle.addIdleObserver(this, IDLE_TIMEOUT_SECONDS);
+ this._hasIdleObserver = true;
+ }
+ else if ((!this._enabled || this._shuttingDown) && this._hasIdleObserver) {
+ _idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
+ this._hasIdleObserver = false;
+ }
+ },
+
+ _free: function WTBJL__free() {
+ this._freeObs();
+ this._updateTimer();
+ this._updateIdleObserver();
+ delete this._builder;
+ },
+
+ /**
+ * Notification handlers
+ */
+
+ notify: function WTBJL_notify(aTimer) {
+ // Add idle observer on the first notification so it doesn't hit startup.
+ this._updateIdleObserver();
+ Services.tm.idleDispatchToMainThread(() => { this.update(); });
+ },
+
+ observe: function WTBJL_observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "nsPref:changed":
+ if (this._enabled == true && !_prefs.getBoolPref(PREF_TASKBAR_ENABLED))
+ this._deleteActiveJumpList();
+ this._refreshPrefs();
+ this._updateTimer();
+ this._updateIdleObserver();
+ Services.tm.idleDispatchToMainThread(() => { this.update(); });
+ break;
+
+ case "profile-before-change":
+ this._shutdown();
+ break;
+
+ case "browser:purge-session-history":
+ this.update();
+ break;
+
+ case "idle":
+ if (this._timer) {
+ this._timer.cancel();
+ delete this._timer;
+ }
+ break;
+
+ case "active":
+ this._updateTimer();
+ break;
+ }
+ },
+};
+
diff --git a/comm/suite/modules/WindowsPreviewPerTab.jsm b/comm/suite/modules/WindowsPreviewPerTab.jsm
new file mode 100644
index 0000000000..d526331a97
--- /dev/null
+++ b/comm/suite/modules/WindowsPreviewPerTab.jsm
@@ -0,0 +1,872 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+/*
+ * This module implements the front end behavior for AeroPeek. Starting in
+ * Windows Vista, the taskbar began showing live thumbnail previews of windows
+ * when the user hovered over the window icon in the taskbar. Starting with
+ * Windows 7, the taskbar allows an application to expose its tabbed interface
+ * in the taskbar by showing thumbnail previews rather than the default window
+ * preview. Additionally, when a user hovers over a thumbnail (tab or window),
+ * they are shown a live preview of the window (or tab + its containing window).
+ *
+ * In Windows 7, a title, icon, close button and optional toolbar are shown for
+ * each preview. This feature does not make use of the toolbar. For window
+ * previews, the title is the window title and the icon the window icon. For
+ * tab previews, the title is the page title and the page's favicon. In both
+ * cases, the close button "does the right thing."
+ *
+ * The primary objects behind this feature are nsITaskbarTabPreview and
+ * nsITaskbarPreviewController. Each preview has a controller. The controller
+ * responds to the user's interactions on the taskbar and provides the required
+ * data to the preview for determining the size of the tab and thumbnail. The
+ * PreviewController class implements this interface. The preview will request
+ * the controller to provide a thumbnail or preview when the user interacts with
+ * the taskbar. To reduce the overhead of drawing the tab area, the controller
+ * implementation caches the tab's contents in a <canvas> element. If no
+ * previews or thumbnails have been requested for some time, the controller will
+ * discard its cached tab contents.
+ *
+ * Screen real estate is limited so when there are too many thumbnails to fit
+ * on the screen, the taskbar stops displaying thumbnails and instead displays
+ * just the title, icon and close button in a similar fashion to previous
+ * versions of the taskbar. If there are still too many previews to fit on the
+ * screen, the taskbar resorts to a scroll up and scroll down button pair to let
+ * the user scroll through the list of tabs. Since this is undoubtedly
+ * inconvenient for users with many tabs, the AeroPeek objects turns off all of
+ * the tab previews. This tells the taskbar to revert to one preview per window.
+ * If the number of tabs falls below this magic threshold, the preview-per-tab
+ * behavior returns. There is no reliable way to determine when the scroll
+ * buttons appear on the taskbar, so a magic pref-controlled number determines
+ * when this threshold has been crossed.
+ */
+var EXPORTED_SYMBOLS = ["AeroPeek"];
+
+
+const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const {PlacesUtils} = ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
+const {PrivateBrowsingUtils} = ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+// Pref to enable/disable preview-per-tab.
+const TOGGLE_PREF_NAME = "browser.taskbar.previews.enable";
+// Pref to determine the magic auto-disable threshold.
+const DISABLE_THRESHOLD_PREF_NAME = "browser.taskbar.previews.max";
+// Pref to control the time in seconds that tab contents live in the cache.
+const CACHE_EXPIRATION_TIME_PREF_NAME = "browser.taskbar.previews.cachetime";
+
+const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Various utility properties.
+XPCOMUtils.defineLazyServiceGetter(this, "imgTools",
+ "@mozilla.org/image/tools;1",
+ "imgITools");
+ChromeUtils.defineModuleGetter(this, "PageThumbs",
+ "resource://gre/modules/PageThumbs.jsm");
+
+// nsIURI -> imgIContainer
+function _imageFromURI(uri, privateMode, callback) {
+ let channel = NetUtil.newChannel({
+ uri: uri,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
+ });
+
+ try {
+ channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);
+ channel.setPrivate(privateMode);
+ } catch (e) {
+ // Ignore channels which do not support nsIPrivateBrowsingChannel.
+ }
+ NetUtil.asyncFetch(channel, function(inputStream, resultCode) {
+ if (!Components.isSuccessCode(resultCode)) {
+ return;
+ }
+
+ const decodeCallback = {
+ onImageReady(image, status) {
+ if (!image) {
+ // We failed, so use the default favicon (only if this wasn't the
+ // default favicon).
+ let defaultURI = PlacesUtils.favicons.defaultFavicon;
+ if (!defaultURI.equals(uri)) {
+ _imageFromURI(defaultURI, privateMode, callback);
+ return;
+ }
+ }
+
+ callback(image);
+ }
+ };
+
+ try {
+ let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
+ imgTools.decodeImageAsync(inputStream, channel.contentType,
+ decodeCallback, threadManager.currentThread);
+ } catch (e) {
+ // We failed, so use the default favicon (only if this wasn't the default
+ // favicon).
+ let defaultURI = PlacesUtils.favicons.defaultFavicon;
+ if (!defaultURI.equals(uri))
+ _imageFromURI(defaultURI, privateMode, callback);
+ }
+ });
+}
+
+// string? -> imgIContainer
+function getFaviconAsImage(iconurl, privateMode, callback) {
+ if (iconurl) {
+ _imageFromURI(NetUtil.newURI(iconurl), privateMode, callback);
+ } else {
+ _imageFromURI(PlacesUtils.favicons.defaultFavicon, privateMode, callback);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// PreviewController
+
+/*
+ * This class manages the behavior of thumbnails and previews. It has the following
+ * responsibilities:
+ * 1) Responding to requests from Windows taskbar for a thumbnail or window
+ * preview.
+ * 2) Listens for dom events that result in a thumbnail or window preview needing
+ * to be refreshed and communicates this to the taskbar.
+ * 3) Handles querying and returning to the taskbar new thumbnail or window
+ * preview images through PageThumbs.
+ *
+ * @param win
+ * The TabWindow (see below) that owns the preview that this controls.
+ * @param tab
+ * The <tab> that this preview is associated with.
+ */
+function PreviewController(win, tab) {
+ this.win = win;
+ this.tab = tab;
+ this.linkedBrowser = tab.linkedBrowser;
+ this.preview = this.win.createTabPreview(this);
+
+ this.tab.addEventListener("TabAttrModified", this);
+
+ XPCOMUtils.defineLazyGetter(this, "canvasPreview", function () {
+ let canvas = PageThumbs.createCanvas();
+ canvas.mozOpaque = true;
+ return canvas;
+ });
+}
+
+PreviewController.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITaskbarPreviewController,
+ Ci.nsIDOMEventListener]),
+
+ _cachedWidth: 0,
+ _cachedHeight: 0,
+
+ destroy: function () {
+ this.tab.removeEventListener("TabAttrModified", this);
+
+ // Break cycles, otherwise we end up leaking the window with everything
+ // attached to it.
+ delete this.win;
+ delete this.preview;
+ },
+
+ get wrappedJSObject() {
+ return this;
+ },
+
+ // Resizes the canvasPreview to 0x0, essentially freeing its memory.
+ resetCanvasPreview: function () {
+ this.canvasPreview.width = 0;
+ this.canvasPreview.height = 0;
+ },
+
+ // Set the canvas dimensions.
+ resizeCanvasPreview: function (aRequestedWidth, aRequestedHeight) {
+ this.canvasPreview.width = aRequestedWidth;
+ this.canvasPreview.height = aRequestedHeight;
+ },
+
+ get zoom() {
+ // Note that winutils.fullZoom accounts for "quantization" of the zoom factor
+ // from nsIContentViewer due to conversion through appUnits.
+ // We do -not- want screenPixelsPerCSSPixel here, because that would -also-
+ // incorporate any scaling that is applied due to hi-dpi resolution options.
+ return this.tab.linkedBrowser.fullZoom;
+ },
+
+ get screenPixelsPerCSSPixel() {
+ let chromeWin = this.tab.ownerGlobal;
+ let windowUtils = chromeWin.getInterface(Ci.nsIDOMWindowUtils);
+ return windowUtils.screenPixelsPerCSSPixel;
+ },
+
+ get browserDims() {
+ return this.tab.linkedBrowser.getBoundingClientRect();
+ },
+
+ cacheBrowserDims: function () {
+ let dims = this.browserDims;
+ this._cachedWidth = dims.width;
+ this._cachedHeight = dims.height;
+ },
+
+ testCacheBrowserDims: function () {
+ let dims = this.browserDims;
+ return this._cachedWidth == dims.width &&
+ this._cachedHeight == dims.height;
+ },
+
+ // Capture a new thumbnail image for this preview. Called by the controller
+ // in response to a request for a new thumbnail image.
+ updateCanvasPreview: function (aFullScale, aCallback) {
+ // Update our cached browser dims so that delayed resize
+ // events don't trigger another invalidation if this tab becomes active.
+ this.cacheBrowserDims();
+ PageThumbs.captureToCanvas(this.linkedBrowser, this.canvasPreview,
+ aCallback, { fullScale: aFullScale });
+ // If we're updating the canvas, then we're in the middle of a peek so
+ // don't discard the cache of previews.
+ AeroPeek.resetCacheTimer();
+ },
+
+ updateTitleAndTooltip: function () {
+ let title = this.win.tabbrowser.getWindowTitleForBrowser(this.linkedBrowser);
+ this.preview.title = title;
+ this.preview.tooltip = title;
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// nsITaskbarPreviewController
+
+ // Window width, not browser.
+ get width() {
+ return this.win.width;
+ },
+
+ // Window height, not browser.
+ get height() {
+ return this.win.height;
+ },
+
+ get thumbnailAspectRatio() {
+ let browserDims = this.browserDims;
+ // Avoid returning 0.
+ let tabWidth = browserDims.width || 1;
+ // Avoid divide by 0.
+ let tabHeight = browserDims.height || 1;
+ return tabWidth / tabHeight;
+ },
+
+ /**
+ * Responds to taskbar requests for window previews. Returns the results asynchronously
+ * through updateCanvasPreview.
+ *
+ * @param aTaskbarCallback nsITaskbarPreviewCallback results callback
+ */
+ requestPreview: function (aTaskbarCallback) {
+ // Grab a high res content preview.
+ this.resetCanvasPreview();
+ this.updateCanvasPreview(true, (aPreviewCanvas) => {
+ let winWidth = this.win.width;
+ let winHeight = this.win.height;
+
+ let composite = PageThumbs.createCanvas();
+
+ // Use transparency, Aero glass is drawn black without it.
+ composite.mozOpaque = false;
+
+ let ctx = composite.getContext('2d');
+ let scale = this.screenPixelsPerCSSPixel / this.zoom;
+
+ composite.width = winWidth * scale;
+ composite.height = winHeight * scale;
+
+ ctx.save();
+ ctx.scale(scale, scale);
+
+ // Draw chrome. Note we currently do not get scrollbars for remote frames
+ // in the image above.
+ ctx.drawWindow(this.win.win, 0, 0, winWidth, winHeight, "rgba(0,0,0,0)");
+
+ // Draw the content are into the composite canvas at the right location.
+ ctx.drawImage(aPreviewCanvas, this.browserDims.x, this.browserDims.y,
+ aPreviewCanvas.width, aPreviewCanvas.height);
+ ctx.restore();
+
+ // Deliver the resulting composite canvas to Windows.
+ this.win.tabbrowser.previewTab(this.tab, function () {
+ aTaskbarCallback.done(composite, false);
+ });
+ });
+ },
+
+ /**
+ * Responds to taskbar requests for tab thumbnails. Returns the results asynchronously
+ * through updateCanvasPreview.
+ *
+ * Note Windows requests a specific width and height here, if the resulting thumbnail
+ * does not match these dimensions thumbnail display will fail.
+ *
+ * @param aTaskbarCallback nsITaskbarPreviewCallback results callback
+ * @param aRequestedWidth width of the requested thumbnail
+ * @param aRequestedHeight height of the requested thumbnail
+ */
+ requestThumbnail: function (aTaskbarCallback, aRequestedWidth, aRequestedHeight) {
+ this.resizeCanvasPreview(aRequestedWidth, aRequestedHeight);
+ this.updateCanvasPreview(false, (aThumbnailCanvas) => {
+ aTaskbarCallback.done(aThumbnailCanvas, false);
+ });
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ //// Event handling
+
+ onClose: function () {
+ this.win.tabbrowser.removeTab(this.tab);
+ },
+
+ onActivate: function () {
+ this.win.tabbrowser.selectedTab = this.tab;
+
+ // Accept activation - this will restore the browser window
+ // if it's minimized.
+ return true;
+ },
+
+ // nsIDOMEventListener
+ handleEvent: function (evt) {
+ switch (evt.type) {
+ case "TabAttrModified":
+ this.updateTitleAndTooltip();
+ break;
+ }
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// TabWindow
+
+/*
+ * This class monitors a browser window for changes to its tabs.
+ *
+ * @param win
+ * The nsIDOMWindow browser window
+ */
+function TabWindow(win) {
+ this.win = win;
+ this.tabbrowser = win.getBrowser();
+
+ this.previews = new Map();
+
+ for (let i = 0; i < this.tabEvents.length; i++)
+ this.tabbrowser.tabContainer.addEventListener(this.tabEvents[i], this);
+
+ for (let i = 0; i < this.winEvents.length; i++)
+ this.win.addEventListener(this.winEvents[i], this);
+
+ this.tabbrowser.addTabsProgressListener(this);
+
+ AeroPeek.windows.push(this);
+ let tabs = this.tabbrowser.tabs;
+ for (let i = 0; i < tabs.length; i++)
+ this.newTab(tabs[i]);
+
+ this.updateTabOrdering();
+ AeroPeek.checkPreviewCount();
+}
+
+TabWindow.prototype = {
+ _enabled: false,
+ _cachedWidth: 0,
+ _cachedHeight: 0,
+ tabEvents: ["TabOpen", "TabClose", "TabSelect", "TabMove"],
+ winEvents: ["resize"],
+
+ destroy: function () {
+ this._destroying = true;
+
+ let tabs = this.tabbrowser.tabs;
+
+ this.tabbrowser.removeTabsProgressListener(this);
+
+ for (let i = 0; i < this.winEvents.length; i++)
+ this.win.removeEventListener(this.winEvents[i], this);
+
+ for (let i = 0; i < this.tabEvents.length; i++)
+ this.tabbrowser.tabContainer.removeEventListener(this.tabEvents[i], this);
+
+ for (let i = 0; i < tabs.length; i++)
+ this.removeTab(tabs[i]);
+
+ let idx = AeroPeek.windows.indexOf(this.win.gTaskbarTabGroup);
+ AeroPeek.windows.splice(idx, 1);
+ AeroPeek.checkPreviewCount();
+ },
+
+ get width () {
+ return this.win.innerWidth;
+ },
+ get height () {
+ return this.win.innerHeight;
+ },
+
+ cacheDims: function () {
+ this._cachedWidth = this.width;
+ this._cachedHeight = this.height;
+ },
+
+ testCacheDims: function () {
+ return this._cachedWidth == this.width && this._cachedHeight == this.height;
+ },
+
+ // Invoked when the given tab is added to this window.
+ newTab: function (tab) {
+ let controller = new PreviewController(this, tab);
+ // It's OK to add the preview now while the favicon still loads.
+ this.previews.set(tab, controller.preview);
+ AeroPeek.addPreview(controller.preview);
+ // updateTitleAndTooltip relies on having controller.preview which is lazily resolved.
+ // Now that we've updated this.previews, it will resolve successfully.
+ controller.updateTitleAndTooltip();
+ },
+
+ createTabPreview: function (controller) {
+ let docShell = this.win
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let preview = AeroPeek.taskbar.createTaskbarTabPreview(docShell, controller);
+ preview.visible = AeroPeek.enabled;
+ preview.active = this.tabbrowser.selectedTab == controller.tab;
+ this.onLinkIconAvailable(controller.tab.linkedBrowser,
+ controller.tab.getAttribute("image"));
+
+ return preview;
+ },
+
+ // Invoked when the given tab is closed.
+ removeTab: function (tab) {
+ let preview = this.previewFromTab(tab);
+ preview.active = false;
+ preview.visible = false;
+ preview.move(null);
+ preview.controller.wrappedJSObject.destroy();
+
+ this.previews.delete(tab);
+ AeroPeek.removePreview(preview);
+ },
+
+ get enabled () {
+ return this._enabled;
+ },
+
+ set enabled (enable) {
+ this._enabled = enable;
+ // Because making a tab visible requires that the tab it is next to be
+ // visible, it is far simpler to unset the 'next' tab and recreate them all
+ // at once.
+ for (let [tab, preview] of this.previews) {
+ preview.move(null);
+ preview.visible = enable;
+ }
+ this.updateTabOrdering();
+ },
+
+ previewFromTab: function (tab) {
+ return this.previews.get(tab);
+ },
+
+ updateTabOrdering: function () {
+ let previews = this.previews;
+ let tabs = this.tabbrowser.tabs;
+
+ // Previews are internally stored using a map, so we need to iterate the
+ // tabbrowser's array of tabs to retrieve previews in the same order.
+ let inorder = [];
+ for (let t of tabs) {
+ if (previews.has(t)) {
+ inorder.push(previews.get(t));
+ }
+ }
+
+ // Since the internal taskbar array has not yet been updated we must force
+ // on it the sorting order of our local array. To do so we must walk
+ // the local array backwards, otherwise we would send move requests in the
+ // wrong order. See bug 522610 for details.
+ for (let i = inorder.length - 1; i >= 0; i--) {
+ inorder[i].move(inorder[i + 1] || null);
+ }
+ },
+
+ //// nsIDOMEventListener
+ handleEvent: function (evt) {
+ let tab = evt.originalTarget;
+ switch (evt.type) {
+ case "TabOpen":
+ this.newTab(tab);
+ this.updateTabOrdering();
+ break;
+ case "TabClose":
+ this.removeTab(tab);
+ this.updateTabOrdering();
+ break;
+ case "TabSelect":
+ this.previewFromTab(tab).active = true;
+ break;
+ case "TabMove":
+ this.updateTabOrdering();
+ break;
+ case "resize":
+ if (!AeroPeek._prefenabled)
+ return;
+ this.onResize();
+ break;
+ }
+ },
+
+ // Set or reset a timer that will invalidate visible thumbnails soon.
+ setInvalidationTimer: function () {
+ if (!this.invalidateTimer) {
+ this.invalidateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ }
+ this.invalidateTimer.cancel();
+
+ // Delay 1 second before invalidating.
+ this.invalidateTimer.initWithCallback(() => {
+ // Invalidate every preview. Note the internal implementation of
+ // invalidate ignores thumbnails that aren't visible.
+ this.previews.forEach(function (aPreview) {
+ let controller = aPreview.controller.wrappedJSObject;
+ if (!controller.testCacheBrowserDims()) {
+ controller.cacheBrowserDims();
+ aPreview.invalidate();
+ }
+ });
+ }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+
+ onResize: function () {
+ // Specific to a window.
+
+ // Call invalidate on each tab thumbnail so that Windows will request an
+ // updated image. However don't do this repeatedly across multiple resize
+ // events triggered during window border drags.
+ if (this.testCacheDims()) {
+ return;
+ }
+
+ // Update the window dims on our TabWindow object.
+ this.cacheDims();
+
+ // Invalidate soon.
+ this.setInvalidationTimer();
+ },
+
+ invalidateTabPreview: function(aBrowser) {
+ for (let [tab, preview] of this.previews) {
+ if (aBrowser == tab.linkedBrowser) {
+ preview.invalidate();
+ break;
+ }
+ }
+ },
+
+ //// Browser progress listener
+
+ onLocationChange: function (aBrowser) {
+ // I'm not sure we need this, onStateChange does a really good job
+ // of picking up page changes.
+ // this.invalidateTabPreview(aBrowser);
+ },
+
+ onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
+ this.invalidateTabPreview(aBrowser);
+ }
+ },
+
+ directRequestProtocols: new Set([
+ "file", "chrome", "resource", "about"
+ ]),
+
+ onLinkIconAvailable: function (aBrowser, aIconURL) {
+
+ let requestURL = null;
+ if (aIconURL) {
+ let shouldRequestFaviconURL = true;
+ try {
+ let urlObject = NetUtil.newURI(aIconURL);
+ shouldRequestFaviconURL =
+ !this.directRequestProtocols.has(urlObject.scheme);
+ } catch (ex) {}
+
+ requestURL = shouldRequestFaviconURL ?
+ "moz-anno:favicon:" + aIconURL :
+ aIconURL;
+ }
+
+ let isDefaultFavicon = !requestURL;
+
+ getFaviconAsImage(
+ requestURL,
+ PrivateBrowsingUtils.isWindowPrivate(this.win),
+ img => {
+ let index = this.tabbrowser.browsers.indexOf(aBrowser);
+ // Only add it if we've found the index and the URI is still the same.
+ // The tab could have closed, and there's no guarantee the icons
+ // will have finished fetching 'in order'.
+ if (index != -1) {
+ let tab = this.tabbrowser.tabs[index];
+
+ let preview = this.previews.get(tab);
+
+ if (tab.getAttribute("image") == aIconURL ||
+ (!preview.icon && isDefaultFavicon)) {
+ preview.icon = img;
+ }
+ }
+ }
+ );
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// AeroPeek
+
+// This object acts as global storage and external interface for this feature.
+// It maintains the values of the prefs.
+var AeroPeek = {
+ available: false,
+ // Does the pref say we're enabled?
+ _prefenabled: false,
+
+ _enabled: true,
+
+ initialized: false,
+
+ // nsITaskbarTabPreview array
+ previews: [],
+
+ // TabWindow array
+ windows: [],
+
+ // nsIWinTaskbar service
+ taskbar: null,
+
+ // Maximum number of previews.
+ maxpreviews: 20,
+
+ // Length of time in seconds that previews are cached.
+ cacheLifespan: 20,
+
+ initialize: function () {
+ if (!(WINTASKBAR_CONTRACTID in Cc))
+ return;
+ this.taskbar = Cc[WINTASKBAR_CONTRACTID]
+ .getService(Ci.nsIWinTaskbar);
+ this.available = this.taskbar.available;
+ if (!this.available)
+ return;
+
+ this.prefs.addObserver(TOGGLE_PREF_NAME, this, true);
+ this.enabled = this._prefenabled = this.prefs.getBoolPref(TOGGLE_PREF_NAME);
+ this.initialized = true;
+ },
+
+ destroy: function destroy() {
+ this._enabled = false;
+
+ this.prefs.removeObserver(TOGGLE_PREF_NAME, this);
+ this.prefs.removeObserver(DISABLE_THRESHOLD_PREF_NAME, this);
+ this.prefs.removeObserver(CACHE_EXPIRATION_TIME_PREF_NAME, this);
+
+ if (this.cacheTimer)
+ this.cacheTimer.cancel();
+ },
+
+ get enabled() {
+ return this._enabled;
+ },
+
+ set enabled(enable) {
+ if (this._enabled == enable)
+ return;
+
+ this._enabled = enable;
+
+ this.windows.forEach(function (win) {
+ win.enabled = enable;
+ });
+ },
+ get _prefenabled() {
+ return this.__prefenabled;
+ },
+
+ set _prefenabled(enable) {
+ if (enable == this.__prefenabled) {
+ return;
+ }
+ this.__prefenabled = enable;
+
+ if (enable) {
+ this.enable();
+ } else {
+ this.disable();
+ }
+ },
+
+ _observersAdded: false,
+
+ enable() {
+ if (!this._observersAdded) {
+ this.prefs.addObserver(DISABLE_THRESHOLD_PREF_NAME, this, true);
+ this.prefs.addObserver(CACHE_EXPIRATION_TIME_PREF_NAME, this, true);
+ PlacesUtils.history.addObserver(this, true);
+ this._observersAdded = true;
+ }
+
+ this.cacheLifespan = this.prefs.getIntPref(CACHE_EXPIRATION_TIME_PREF_NAME);
+
+ this.maxpreviews = this.prefs.getIntPref(DISABLE_THRESHOLD_PREF_NAME);
+
+ // If the user toggled us on/off while the browser was already up
+ // (rather than this code running on startup because the pref was
+ // already set to true), we must initialize previews for open windows.
+ if (this.initialized) {
+ let browserWindows = Services.wm.getEnumerator("navigator:browser");
+ while (browserWindows.hasMoreElements()) {
+ let win = browserWindows.getNext();
+ if (!win.closed) {
+ this.onOpenWindow(win);
+ }
+ }
+ }
+ },
+
+ disable() {
+ while (this.windows.length) {
+ // We can't call onCloseWindow here because it'll bail if we're not
+ // enabled.
+ let tabWinObject = this.windows[0];
+ tabWinObject.destroy(); // This will remove us from the array.
+ delete tabWinObject.win.gTaskbarTabGroup; // Tidy up the window.
+ }
+ },
+
+ addPreview: function (preview) {
+ this.previews.push(preview);
+ this.checkPreviewCount();
+ },
+
+ removePreview: function (preview) {
+ let idx = this.previews.indexOf(preview);
+ this.previews.splice(idx, 1);
+ this.checkPreviewCount();
+ },
+
+ checkPreviewCount: function () {
+ if (!this._prefenabled) {
+ return;
+ }
+ this.enabled = this.previews.length <= this.maxpreviews;
+ },
+
+ onOpenWindow: function (win) {
+ // This occurs when the taskbar service is not available (xp, vista).
+ if (!this.available || !this._prefenabled)
+ return;
+
+ win.gTaskbarTabGroup = new TabWindow(win);
+ },
+
+ onCloseWindow: function (win) {
+ // This occurs when the taskbar service is not available (xp, vista).
+ if (!this.available || !this._prefenabled)
+ return;
+
+ win.gTaskbarTabGroup.destroy();
+ delete win.gTaskbarTabGroup;
+
+ if (this.windows.length == 0)
+ this.destroy();
+ },
+
+ resetCacheTimer: function () {
+ this.cacheTimer.cancel();
+ this.cacheTimer.init(this, 1000 * this.cacheLifespan,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+
+ //// nsIObserver
+ observe: function (aSubject, aTopic, aData) {
+
+ if (aTopic == "nsPref:changed" && aData == TOGGLE_PREF_NAME) {
+ this._prefenabled = this.prefs.getBoolPref(TOGGLE_PREF_NAME);
+ }
+
+ // Bail out. Nothing more to do here if previews are disabled.
+ if (!this._prefenabled) {
+ return;
+ }
+
+ switch (aTopic) {
+ case "nsPref:changed":
+ if (aData == CACHE_EXPIRATION_TIME_PREF_NAME)
+ break;
+
+ if (aData == DISABLE_THRESHOLD_PREF_NAME)
+ this.maxpreviews = this.prefs.getIntPref(DISABLE_THRESHOLD_PREF_NAME);
+ // Might need to enable/disable ourselves.
+ this.checkPreviewCount();
+ break;
+ case "timer-callback":
+ this.previews.forEach(function (preview) {
+ let controller = preview.controller.wrappedJSObject;
+ controller.resetCanvasPreview();
+ });
+ break;
+ }
+ },
+
+ // nsINavHistoryObserver implementation
+ onBeginUpdateBatch() {},
+ onEndUpdateBatch() {},
+ onVisit() {},
+ onTitleChanged() {},
+ onFrecencyChanged() {},
+ onManyFrecenciesChanged() {},
+ onDeleteURI() {},
+ onClearHistory() {},
+ onDeleteVisits() {},
+ onPageChanged(uri, changedConst, newValue) {
+ if (this.enabled && changedConst == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
+ for (let win of this.windows) {
+ for (let [tab, preview] of win.previews) {
+ if (tab.getAttribute("image") == newValue) {
+ win.onLinkIconAvailable(tab.linkedBrowser, newValue);
+ }
+ }
+ }
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
+ Ci.nsINavHistoryObserver,
+ Ci.nsIObserver]),
+};
+
+XPCOMUtils.defineLazyGetter(AeroPeek, "cacheTimer", () =>
+ Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
+);
+
+XPCOMUtils.defineLazyServiceGetter(AeroPeek, "prefs",
+ "@mozilla.org/preferences-service;1",
+ "nsIPrefBranch");
+
+AeroPeek.initialize();
diff --git a/comm/suite/modules/moz.build b/comm/suite/modules/moz.build
new file mode 100644
index 0000000000..830c03e703
--- /dev/null
+++ b/comm/suite/modules/moz.build
@@ -0,0 +1,20 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
+
+EXTRA_JS_MODULES += [
+ "Feeds.jsm",
+ "OfflineAppCacheHelper.jsm",
+ "OpenInTabsUtils.jsm",
+ "PermissionUI.jsm",
+ "RecentWindow.jsm",
+ "SitePermissions.jsm",
+ "ThemeVariableMap.jsm",
+ "WindowsPreviewPerTab.jsm",
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ EXTRA_JS_MODULES += ["WindowsJumpLists.jsm"]
diff --git a/comm/suite/modules/test/unit/head.js b/comm/suite/modules/test/unit/head.js
new file mode 100644
index 0000000000..c947a4533c
--- /dev/null
+++ b/comm/suite/modules/test/unit/head.js
@@ -0,0 +1,135 @@
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ChromeUtils.defineModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "Promise",
+ "resource://gre/modules/commonjs/sdk/core/promise.js");
+
+// Need to explicitly load profile for Places
+do_get_profile();
+
+/**
+ * Waits until a new cache entry has been opened
+ *
+ * @return {Promise}
+ * @resolves When the new cache entry has been opened.
+ * @rejects Never.
+ *
+ */
+function promiseOpenCacheEntry(aKey, aAccessMode, aCacheSession)
+{
+ let deferred = Promise.defer();
+
+ let cacheListener = {
+ onCacheEntryAvailable: function (entry, access, status) {
+ deferred.resolve(entry);
+ },
+
+ onCacheEntryDoomed: function (status) {
+ }
+ };
+
+ aCacheSession.asyncOpenCacheEntry(aKey, aAccessMode, cacheListener);
+
+ return deferred.promise;
+}
+
+/**
+ * Waits for all pending async statements on the default connection.
+ *
+ * @return {Promise}
+ * @resolves When all pending async statements finished.
+ * @rejects Never.
+ *
+ * @note The result is achieved by asynchronously executing a query requiring
+ * a write lock. Since all statements on the same connection are
+ * serialized, the end of this write operation means that all writes are
+ * complete. Note that WAL makes so that writers don't block readers, but
+ * this is a problem only across different connections.
+ */
+function promiseAsyncUpdates()
+{
+ let deferred = Promise.defer();
+
+ let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+ .DBConnection;
+ let begin = db.createAsyncStatement("BEGIN EXCLUSIVE");
+ begin.executeAsync();
+ begin.finalize();
+
+ let commit = db.createAsyncStatement("COMMIT");
+ commit.executeAsync({
+ handleResult: function() {},
+ handleError: function() {},
+ handleCompletion: function(aReason)
+ {
+ deferred.resolve();
+ }
+ });
+ commit.finalize();
+
+ return deferred.promise;
+}
+
+/**
+ * Asynchronously adds visits to a page.
+ *
+ * @param aPlaceInfo
+ * Can be an nsIURI, in such a case a single LINK visit will be added.
+ * Otherwise can be an object describing the visit to add, or an array
+ * of these objects:
+ * { uri: nsIURI of the page,
+ * transition: one of the TRANSITION_* from nsINavHistoryService,
+ * [optional] title: title of the page,
+ * [optional] visitDate: visit date in microseconds from the epoch
+ * [optional] referrer: nsIURI of the referrer for this visit
+ * }
+ *
+ * @return {Promise}
+ * @resolves When all visits have been added successfully.
+ * @rejects JavaScript exception.
+ */
+function promiseAddVisits(aPlaceInfo)
+{
+ let deferred = Promise.defer();
+ let places = [];
+ if (aPlaceInfo instanceof Ci.nsIURI) {
+ places.push({ uri: aPlaceInfo });
+ }
+ else if (Array.isArray(aPlaceInfo)) {
+ places = places.concat(aPlaceInfo);
+ } else {
+ places.push(aPlaceInfo)
+ }
+
+ // Create mozIVisitInfo for each entry.
+ let now = Date.now();
+ for (let i = 0; i < places.length; i++) {
+ if (!places[i].title) {
+ places[i].title = "test visit for " + places[i].uri.spec;
+ }
+ places[i].visits = [{
+ transitionType: places[i].transition === undefined ? PlacesUtils.history.TRANSITION_LINK
+ : places[i].transition,
+ visitDate: places[i].visitDate || (now++) * 1000,
+ referrerURI: places[i].referrer
+ }];
+ }
+
+ PlacesUtils.asyncHistory.updatePlaces(
+ places,
+ {
+ handleError: function AAV_handleError(aResultCode, aPlaceInfo) {
+ let ex = new Components.Exception("Unexpected error in adding visits.",
+ aResultCode);
+ deferred.reject(ex);
+ },
+ handleResult: function () {},
+ handleCompletion: function UP_handleCompletion() {
+ deferred.resolve();
+ }
+ }
+ );
+
+ return deferred.promise;
+}
diff --git a/comm/suite/modules/test/unit/test_browser_sanitizer.js b/comm/suite/modules/test/unit/test_browser_sanitizer.js
new file mode 100644
index 0000000000..9e6ad39d08
--- /dev/null
+++ b/comm/suite/modules/test/unit/test_browser_sanitizer.js
@@ -0,0 +1,339 @@
+ChromeUtils.import("resource:///modules/Sanitizer.jsm", this);
+ChromeUtils.defineModuleGetter(this, "FormHistory",
+ "resource://gre/modules/FormHistory.jsm");
+
+var sanTests = {
+ cache: {
+ desc: "Cache",
+ async setup() {
+ var entry = null;
+ this.cs = Services.cache.createSession("SanitizerTest", Ci.nsICache.STORE_ANYWHERE, true);
+ entry = await promiseOpenCacheEntry("http://santizer.test", Ci.nsICache.ACCESS_READ_WRITE, this.cs);
+ entry.setMetaDataElement("Foo", "Bar");
+ entry.markValid();
+ entry.close();
+ },
+
+ async check(aShouldBeCleared) {
+ let entry = null;
+ entry = await promiseOpenCacheEntry("http://santizer.test", Ci.nsICache.ACCESS_READ, this.cs);
+
+ if (entry) {
+ entry.close();
+ }
+
+ Assert.equal(!entry, aShouldBeCleared);
+ }
+ },
+
+ offlineApps: {
+ desc: "Offline app cache",
+ async setup() {
+ //XXX test offline DOMStorage
+ var entry = null;
+ this.cs = Services.cache.createSession("SanitizerTest", Ci.nsICache.STORE_OFFLINE, true);
+ entry = await promiseOpenCacheEntry("http://santizer.test", Ci.nsICache.ACCESS_READ_WRITE, this.cs);
+ entry.setMetaDataElement("Foo", "Bar");
+ entry.markValid();
+ entry.close();
+ },
+
+ async check(aShouldBeCleared) {
+ var entry = null;
+ entry = await promiseOpenCacheEntry("http://santizer.test", Ci.nsICache.ACCESS_READ, this.cs);
+ if (entry) {
+ entry.close();
+ }
+
+ Assert.equal(!entry, aShouldBeCleared);
+ }
+ },
+
+ cookies: {
+ desc: "Cookie",
+ setup: function() {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ this.uri = Services.io.newURI("http://sanitizer.test/");
+ this.cs = Cc["@mozilla.org/cookieService;1"]
+ .getService(Ci.nsICookieService);
+ this.cs.setCookieString(this.uri, null, "Sanitizer!", null);
+ },
+
+ check: function(aShouldBeCleared) {
+ if (aShouldBeCleared)
+ Assert.notEqual(this.cs.getCookieString(this.uri, null), "Sanitizer!");
+ else
+ Assert.equal(this.cs.getCookieString(this.uri, null), "Sanitizer!");
+ }
+ },
+
+ history: {
+ desc: "History",
+ async setup() {
+ var uri = Services.io.newURI("http://sanitizer.test/");
+ await promiseAddVisits({
+ uri: uri,
+ title: "Sanitizer!"
+ });
+ },
+
+ check: function(aShouldBeCleared) {
+ var rv = false;
+ var history = Cc["@mozilla.org/browser/nav-history-service;1"]
+ .getService(Ci.nsINavHistoryService);
+ var options = history.getNewQueryOptions();
+ var query = history.getNewQuery();
+ query.searchTerms = "Sanitizer!";
+ var results = history.executeQuery(query, options).root;
+ results.containerOpen = true;
+ for (var i = 0; i < results.childCount; i++) {
+ if (results.getChild(i).uri == "http://sanitizer.test/") {
+ rv = true;
+ break;
+ }
+ }
+
+ // Close container after reading from it
+ results.containerOpen = false;
+
+ Assert.equal(rv, !aShouldBeCleared);
+ }
+ },
+
+ urlbar: {
+ desc: "Location bar history",
+ setup: function() {
+ // Create urlbarhistory file first otherwise tests will fail.
+ var file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append("urlbarhistory.sqlite");
+ if (!file.exists()) {
+ var connection = Cc["@mozilla.org/storage/service;1"]
+ .getService(Ci.mozIStorageService)
+ .openDatabase(file);
+ connection.createTable("urlbarhistory", "url TEXT");
+ connection.executeSimpleSQL(
+ "INSERT INTO urlbarhistory (url) VALUES ('Sanitizer')");
+ connection.close();
+ }
+
+ // Open location dialog.
+ Services.prefs.setStringPref("general.open_location.last_url", "Sanitizer!");
+ },
+
+ check: function(aShouldBeCleared) {
+ let locData;
+ try {
+ locData = Services.prefs.getStringPref("general.open_location.last_url");
+ } catch(ex) {}
+
+ Assert.equal(locData == "Sanitizer!", !aShouldBeCleared);
+
+ var file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("ProfD", Ci.nsIFile);
+ file.append("urlbarhistory.sqlite");
+
+ var connection = Cc["@mozilla.org/storage/service;1"]
+ .getService(Ci.mozIStorageService)
+ .openDatabase(file);
+ var urlbar = connection.tableExists("urlbarhistory");
+ if (urlbar) {
+ var handle = connection.createStatement(
+ "SELECT url FROM urlbarhistory");
+ if (handle.executeStep())
+ urlbar = (handle.getString(0) == "Sanitizer");
+ handle.reset();
+ handle.finalize();
+ }
+ connection.close();
+
+ Assert.equal(urlbar, !aShouldBeCleared);
+ }
+ },
+
+ formdata: {
+ desc: "Form history",
+ async setup() {
+ // Adds a form entry to history.
+ function promiseAddFormEntry(aName, aValue) {
+ return new Promise((resolve, reject) =>
+ FormHistory.update({ op: "add", fieldname: aName, value: aValue },
+ { handleError(error) {
+ reject();
+ throw new Error("Error occurred updating form history: " + error);
+ },
+ handleCompletion(reason) {
+ resolve();
+ }
+ })
+ )
+ }
+ await promiseAddFormEntry("Sanitizer", "Foo");
+ },
+ async check(aShouldBeCleared) {
+ // Check if a form name exists.
+ function formNameExists(aName) {
+ return new Promise((resolve, reject) => {
+ let count = 0;
+ FormHistory.count({ fieldname: aName },
+ { handleResult: result => count = result,
+ handleError(error) {
+ reject(error);
+ throw new Error("Error occurred searching form history: " + error);
+ },
+ handleCompletion(reason) {
+ if (!reason) {
+ resolve(count);
+ }
+ }
+ });
+ });
+ }
+
+ // Checking for Sanitizer form history entry creation.
+ let exists = await formNameExists("Sanitizer");
+ Assert.equal(exists, !aShouldBeCleared);
+ }
+ },
+
+ downloads: {
+ desc: "Download",
+ setup: function() {
+ var uri = Services.io.newURI("http://sanitizer.test/");
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("sanitizer.file");
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
+ var dest = Services.io.newFileURI(file);
+
+ this.dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ this.dl = this.dm.addDownload(this.dm.DOWNLOAD_CANCELED, uri, dest,
+ "Sanitizer!", null,
+ Math.round(Date.now() * 1000), null,
+ persist, false);
+
+ // Stupid DM...
+ this.dm.cancelDownload(this.dl.id);
+ },
+
+ check: function(aShouldBeCleared) {
+ var dl = null;
+ try {
+ dl = this.dm.getDownload(this.dl.id);
+ } catch(ex) {}
+
+ if (aShouldBeCleared)
+ Assert.equal(!dl, aShouldBeCleared)
+ else
+ Assert.equal(dl.displayName, "Sanitizer!");
+ }
+ },
+
+ passwords: {
+ desc: "Login manager",
+ setup: function() {
+ this.pm = Cc["@mozilla.org/login-manager;1"]
+ .getService(Ci.nsILoginManager);
+ var info = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
+ Ci.nsILoginInfo, "init");
+ var login = new info("http://sanitizer.test", null, "Rick Astley Fan Club",
+ "dolske", "iliketurtles1", "", "");
+ this.pm.addLogin(login);
+ },
+
+ check: function(aShouldBeCleared) {
+ let rv = false;
+ let logins = this.pm.findLogins({}, "http://sanitizer.test", null, "Rick Astley Fan Club");
+ for (var i = 0; i < logins.length; i++) {
+ if (logins[i].username == "dolske") {
+ rv = true;
+ break;
+ }
+ }
+
+ Assert.equal(rv, !aShouldBeCleared);
+ }
+ },
+
+ sessions: {
+ desc: "HTTP auth session",
+ setup: function() {
+ this.authMgr = Cc["@mozilla.org/network/http-auth-manager;1"]
+ .getService(Ci.nsIHttpAuthManager);
+
+ this.authMgr.setAuthIdentity("http", "sanitizer.test", 80, "basic", "Sanitizer",
+ "", "Foo", "fooo", "foo12");
+ },
+
+ check: function(aShouldBeCleared) {
+ var domain = {};
+ var user = {};
+ var password = {};
+
+ try {
+ this.authMgr.getAuthIdentity("http", "sanitizer.test", 80, "basic", "Sanitizer",
+ "", domain, user, password);
+ } catch(ex) {}
+
+ Assert.equal(domain.value == "Foo", !aShouldBeCleared);
+ }
+ }
+}
+
+async function fullSanitize() {
+ info("Now doing a full sanitize run");
+ var prefs = Services.prefs.getBranch("privacy.clearOnShutdown.");
+
+ Services.prefs.setBoolPref("privacy.sanitize.promptOnSanitize", false);
+
+ for (var testName in sanTests) {
+ var test = sanTests[testName];
+ await test.setup();
+ prefs.setBoolPref(testName, true);
+ }
+
+ Sanitizer.sanitize();
+
+ for (var testName in sanTests) {
+ var test = sanTests[testName];
+ await test.check(true);
+ info(test.desc + " data cleared by full sanitize");
+ try {
+ prefs.clearUserPref(testName);
+ } catch (ex) {}
+ }
+
+ try {
+ Services.prefs.clearUserPref("privacy.sanitize.promptOnSanitize");
+ } catch(ex) {}
+}
+
+function run_test()
+{
+ run_next_test();
+}
+
+add_task(async function test_browser_sanitizer()
+{
+ for (var testName in sanTests) {
+ let test = sanTests[testName];
+ dump("\nExecuting test: " + testName + "\n" + "*** " + test.desc + "\n");
+ await test.setup();
+ await test.check(false);
+
+ Sanitizer.items[testName].clear();
+ info(test.desc + " data cleared");
+
+ await test.check(true);
+ }
+});
+
+add_task(fullSanitize);
diff --git a/comm/suite/modules/test/unit/xpcshell.ini b/comm/suite/modules/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..4f6c1dad63
--- /dev/null
+++ b/comm/suite/modules/test/unit/xpcshell.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+head = head.js
+tail =
+run-sequentially = Avoid bustage.
+
+[test_browser_sanitizer.js]
diff --git a/comm/suite/moz.build b/comm/suite/moz.build
new file mode 100644
index 0000000000..09ba2b26d1
--- /dev/null
+++ b/comm/suite/moz.build
@@ -0,0 +1,33 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CONFIGURE_SUBST_FILES += ["installer/Makefile"]
+
+DIRS += [
+ "base",
+ "browser",
+ "components",
+ "editor",
+ "extensions",
+ "locales",
+ "mailnews",
+ "modules",
+ "themes/classic",
+ "themes/modern",
+]
+
+if CONFIG['MOZ_IRC']:
+ DIRS += ['chatzilla']
+
+if CONFIG["MAKENSISU"]:
+ DIRS += ["installer/windows"]
+
+if CONFIG["MOZ_BUNDLED_FONTS"]:
+ DIRS += ["/browser/fonts"]
+
+# app is always last as it packages up the built files on mac.
+DIRS += [
+ "app",
+]
diff --git a/comm/suite/moz.configure b/comm/suite/moz.configure
new file mode 100644
index 0000000000..4f9e7be414
--- /dev/null
+++ b/comm/suite/moz.configure
@@ -0,0 +1,117 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+set_config("MOZ_SUITE", True)
+set_define("MOZ_SUITE", True)
+
+imply_option("MOZ_APP_BASENAME", "SeaMonkey")
+
+imply_option('--enable-default-browser-agent', False)
+
+@depends(target_is_windows, target_has_linux_kernel)
+def bundled_fonts(is_windows, is_linux):
+ if is_windows or is_linux:
+ return True
+
+
+set_config("MOZ_BUNDLED_FONTS", bundled_fonts)
+add_old_configure_assignment("MOZ_BUNDLED_FONTS", bundled_fonts)
+
+
+@depends(build_environment, "--help")
+@imports(_from="os.path", _import="join")
+def commtopsrcdir(build_env, _):
+ topsrcdir = build_env.topsrcdir
+ return join(topsrcdir, "comm")
+
+
+@template
+def set_defconf(k, v):
+ set_config(k, v)
+ set_define(k, v)
+ add_old_configure_assignment(k, v)
+
+
+add_old_configure_assignment("commtopsrcdir", commtopsrcdir)
+set_config("commtopsrcdir", commtopsrcdir)
+
+
+@depends(build_environment, application)
+@imports(_from="os.path", _import="exists")
+@imports(_from="__builtin__", _import="open")
+def seamonkey_version(build_env, app_path):
+ version_file = os.path.join(
+ build_env.topsrcdir, app_path[0], "config", "version.txt"
+ )
+ version_file_display = os.path.join(
+ build_env.topsrcdir, app_path[0], "config", "version_display.txt"
+ )
+ version_file_package = os.path.join(
+ build_env.topsrcdir, app_path[0], "config", "version_package.txt"
+ )
+ rv = []
+ for f in [version_file, version_file_display, version_file_package]:
+ if exists(f):
+ f_value = open(f).read().strip()
+ else:
+ f_value = "unknown"
+ rv.append(f_value)
+
+ return namespace(version=rv[0],
+ version_display=rv[1],
+ version_package=rv[2])
+
+
+set_defconf("SEAMONKEY_VERSION", seamonkey_version.version)
+set_defconf("SEAMONKEY_VERSION_DISPLAY", seamonkey_version.version_display)
+# Currently not set in suite comm-central
+# set_defconf("MOZ_PKG_VERSION", seamonkey_version.version_package)
+
+
+imply_option("MOZ_PLACES", True)
+imply_option("MOZ_SERVICES_SYNC", False)
+
+# Building extensions is disabled by default.
+
+# =========================================================
+# = ChatZilla extension
+# =========================================================
+option(
+ "--enable-irc", default=False, help="Enable building of the ChatZilla IRC extension"
+)
+
+
+@depends_if("--enable-irc")
+def irc(arg):
+ return True
+
+
+set_config("MOZ_IRC", irc)
+
+# =========================================================
+# = DebugQA extension
+# =========================================================
+option(
+ "--enable-debugqa", default=False, help="Enable building of the DebugQA extension"
+)
+
+
+@depends_if("--enable-debugqa")
+def debugqa(arg):
+ return True
+
+
+set_config("MOZ_DEBUGQA", debugqa)
+
+# Miscellaneous programs
+# ==============================================================
+
+check_prog("ZIP", ("zip",))
+
+include("../build/moz.configure/gecko_source.configure")
+
+include("../mailnews/moz.configure")
+include("../../toolkit/moz.configure")
diff --git a/comm/suite/themes/classic/Makefile.in b/comm/suite/themes/classic/Makefile.in
new file mode 100644
index 0000000000..1cdce732af
--- /dev/null
+++ b/comm/suite/themes/classic/Makefile.in
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CLASSIC_EXTENSION_DIR = {972ce4c6-7e08-4474-a285-3208198ce6fd}
+
+INSTALL_RDF = $(srcdir)/install.rdf
+INSTALL_RDF_PATH = $(FINAL_TARGET)/extensions/$(CLASSIC_EXTENSION_DIR)
+PP_TARGETS += INSTALL_RDF
diff --git a/comm/suite/themes/classic/README b/comm/suite/themes/classic/README
new file mode 100644
index 0000000000..9f5a75a341
--- /dev/null
+++ b/comm/suite/themes/classic/README
@@ -0,0 +1,19 @@
+Welcome to the Classic Skin, a contemporary native look for Mozilla.
+
+Notes:
+
+Unix builds the Windows version, and all Unix subdirectories are ignored. Should get around to
+removing them some day.
+
+Many aspects of this skin have been tuned for optimum performance. Barring style sheet scoping
+work, the rules here are generally good.
+
+Do NOT inject Dumb-Ass style rules into this skin, or I will hunt you down and kill you.
+
+Do NOT inject Tabs into skins in this skin, even the Mac files, or I will hunt you down and kill you. Configure your editor to insert Tabs as spaces.
+
+Please conform to the indentation style standard set by the Windows version of this skin.
+
+This skin uses System Colours in the Windows version, and is slowly picking up System Colour usage in the Macintosh version as support is implemented. Please don't check in hard coded colour values into the Windows version or the Macintosh version where an appropriate System Colour exists.
+
+Send comments and questions to <ben@netscape.com>
diff --git a/comm/suite/themes/classic/communicator/about.css b/comm/suite/themes/classic/communicator/about.css
new file mode 100644
index 0000000000..cfe6b54648
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/about.css
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+html {
+ background: -moz-Dialog;
+ padding: 0 1em;
+ font: message-box;
+}
+
+body {
+ color: -moz-FieldText;
+ position: relative;
+ min-width: 330px;
+ max-width: 50em;
+ margin: 4em auto;
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ background: -moz-Field;
+}
+
+.aboutPageWideContainer {
+ max-width: 80%;
+}
+
+#aboutLogoContainer {
+ border: 1px solid ThreeDLightShadow;
+ width: 300px;
+ margin-bottom: 2em;
+}
+
+img {
+ border: 0;
+}
+
+#version {
+ font-weight: bold;
+ color: #909090;
+ margin: -24px 0 9px 17px;
+ text-align: left; /* Override direction alignment on RTL to make sure that the version will fit well on the background. bug 1325232 */
+}
+
+ul {
+ margin: 0;
+ margin-inline-start: 1.5em;
+ padding: 0;
+ list-style: square;
+}
+
+ul > li {
+ margin-top: .5em;
+}
+
+th, td {
+ padding: 0 5px;
+}
diff --git a/comm/suite/themes/classic/communicator/aboutPrivateBrowsing.css b/comm/suite/themes/classic/communicator/aboutPrivateBrowsing.css
new file mode 100644
index 0000000000..4d34fcd5e9
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/aboutPrivateBrowsing.css
@@ -0,0 +1,86 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#warningScreen {
+ background-color: -moz-Dialog;
+}
+
+#warningBox {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ margin-left: 1em;
+ margin-right: 1em;
+}
+
+#warningInnerBox {
+ max-width: 50em;
+}
+
+#warningTitle {
+ margin: 0 0 .6em 0;
+ font-size: 160%;
+ border-bottom: 1px solid ThreeDLightShadow
+}
+
+#warningText {
+ font-size: 110%;
+ margin-left: 0;
+}
+
+/* Pick the desired icons depending on the window's context */
+
+#warningBox.private > #warningBoxIcon {
+ list-style-image: url("chrome://communicator/skin/icons/information-48.png");
+
+ width: 48px;
+ height: 48px;
+ margin-inline-end: 3em;
+}
+
+#warningBox.normal > #warningBoxIcon {
+ list-style-image: url("chrome://communicator/skin/icons/question-48.png");
+ width: 48px;
+ height: 48px;
+ margin-inline-end: 3em;
+}
+
+#trackWarnBox {
+ margin-top: 0.6em;
+ margin-inline-end: 7em;
+ -moz-box-align: center;
+}
+
+#trackWarnIcon {
+ list-style-image: url("chrome://communicator/skin/icons/warning-24.png");
+ width: 24px;
+ height: 24px;
+}
+
+/* Define additional styles to look similar to the netError/certError pages */
+
+#warningTitle {
+ font-weight: bold;
+}
+
+#warningStatus {
+ margin: 0.4em 0 1.2em 0;
+ padding-bottom: 1.2em;
+ border-bottom: 1px solid ThreeDLightShadow;
+ font-size: 135%;
+}
+
+#warningInnerBox > button {
+ margin: 0.8em 0 1em 0;
+}
+
+#warningOuterBox > vbox > label,
+#warningInnerBox > description {
+ margin-inline-start: 0;
+}
diff --git a/comm/suite/themes/classic/communicator/aboutSessionRestore.css b/comm/suite/themes/classic/communicator/aboutSessionRestore.css
new file mode 100644
index 0000000000..d1b9921d03
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/aboutSessionRestore.css
@@ -0,0 +1,52 @@
+/* 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/. */
+
+#tabList {
+ width: 100%;
+ height: 12em;
+}
+
+treechildren::-moz-tree-image(icon),
+treechildren::-moz-tree-image(noicon) {
+ padding-right: 2px;
+ margin: 0px 2px;
+ width: 16px;
+ height: 16px;
+}
+
+treechildren::-moz-tree-image(noicon) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+treechildren::-moz-tree-image(container, noicon) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+treechildren::-moz-tree-image(container, noicon, open) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open.png");
+}
+
+treechildren::-moz-tree-checkbox(checked) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
+treechildren::-moz-tree-checkbox(partial) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
+}
+
+treechildren::-moz-tree-row(alternate) {
+ background-color: -moz-oddtreerow;
+}
+
+treechildren::-moz-tree-row(alternate, selected) {
+ background-color: Highlight;
+}
+
+#buttons {
+ margin-inline-start: 80px;
+}
+#buttons > button {
+ margin-top: 2em;
+ margin-inline-start: 5px;
+}
diff --git a/comm/suite/themes/classic/communicator/aboutSupport.css b/comm/suite/themes/classic/communicator/aboutSupport.css
new file mode 100644
index 0000000000..248700dffb
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/aboutSupport.css
@@ -0,0 +1,139 @@
+/* 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/. */
+
+@import url("chrome://communicator/skin/common.css");
+
+html {
+ background-color: var(--in-content-page-background);
+ color: -moz-FieldText;
+ font: message-box;
+}
+
+body {
+ width: 90%;
+ margin-left: 5%;
+ margin-right: 5%;
+}
+
+.page-subtitle {
+ margin-bottom: 1.5em;
+}
+
+.major-section {
+ margin-top: 2em;
+ margin-bottom: 1em;
+ font-size: large;
+ text-align: start;
+ font-weight: bold;
+}
+
+button {
+ margin-inline-start: 0;
+ margin-inline-end: 8px;
+ border-radius: 3px;
+}
+
+table {
+ background-color: var(--in-content-table-background);
+ color: var(--in-content-text-color);
+ font: message-box;
+ text-align: start;
+ width: 100%;
+ border: 1px solid var(--in-content-border-color);
+ border-spacing: 0px;
+}
+
+th, td {
+ border: var(--in-content-border-dotted-color);
+ padding: 3px;
+}
+
+thead th {
+ text-align: center;
+}
+
+th {
+ text-align: start;
+ background-color: var(--in-content-table-header-background);
+ color: var(--in-content-selected-text);
+}
+
+th.title-column {
+ white-space: nowrap;
+ width: 0px;
+ font-size: medium;
+}
+
+th.column {
+ white-space: nowrap;
+ width: 0px;
+}
+
+td {
+ text-align: start;
+ border-top: var(--in-content-border-dotted-color);
+}
+
+.prefs-table {
+ table-layout: fixed;
+}
+
+.name {
+ width: 70%;
+}
+
+.value {
+ width: 30%;
+}
+
+.pref-name,
+.pref-value {
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+#action-box {
+ background-color: var(--in-content-table-background);
+ border: 1px solid var(--in-content-border-color);
+ border-radius: 6px;
+ color: var(--in-content-text-color);
+ float: right;
+ margin-top: 2em;
+ margin-bottom: 20px;
+ margin-inline-start: 20px;
+ margin-inline-end: 0;
+ padding: 16px;
+ width: 30%;
+}
+
+#action-box,
+#reset-box,
+#safe-mode-box {
+ display: none;
+}
+
+#action-box:dir(rtl) {
+ float: left;
+}
+
+#reset-box > h3 {
+ margin-top: 0;
+}
+
+#action-box button {
+ display: block;
+}
+
+#verify-place-result {
+ max-height: 200px;
+ overflow: auto;
+}
+
+.block {
+ display: block;
+}
+
+.hidden {
+ display: none;
+}
diff --git a/comm/suite/themes/classic/communicator/aboutSyncTabs.css b/comm/suite/themes/classic/communicator/aboutSyncTabs.css
new file mode 100644
index 0000000000..8308d5ab77
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/aboutSyncTabs.css
@@ -0,0 +1,88 @@
+/* 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/. */
+
+#tabsList {
+ background: #FFFFFF url(chrome://communicator/skin/sync/sync-bg.png) repeat-x center -80px;
+}
+
+#headers {
+ background: url(chrome://communicator/skin/sync/sync-32.png) no-repeat;
+ width: 45em;
+ height: 32px;
+ margin: 4px 2em 0px;
+}
+
+#tabsListHeading {
+ font-size: 140%;
+ font-weight: bold;
+ margin-inline-start: 40px;
+}
+
+richlistitem {
+ margin-inline-end: 2em;
+}
+
+richlistitem[selected="true"],
+richlistitem:focus {
+ outline-style: none;
+}
+
+richlistitem[type="tab"] {
+ min-height: 3em;
+ border: #999999 1px solid !important;
+ padding: 2px 5px;
+ margin-bottom: 4px;
+ margin-inline-start: 4em;
+ border-radius: 6px;
+ background-color: menu;
+ width: 44em;
+ opacity: 0.9;
+ box-shadow:
+ inset rgba(255, 255, 255, 0.5) 0 1px 0px,
+ inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
+ rgba(0, 0, 0, 0.1) 0px 1px 0px;
+}
+
+richlistitem[type="tab"][selected="true"] {
+ background-color: Highlight;
+}
+
+richlistitem[type="client"] {
+ min-height: 2em;
+ color: #000000;
+ margin-inline-start: 2em;
+ margin-top: 2px;
+ margin-bottom: 3px;
+ width: 42em;
+ border-radius: 6px;
+ background-color: transparent;
+ -moz-user-focus: ignore !important;
+}
+
+.mobile[type="client"] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-mobileIcon.png");
+}
+
+.desktop[type="client"] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-desktopIcon.png");
+}
+
+.title,
+.clientName {
+ font-size: 1.1em;
+}
+
+.url {
+ color: -moz-nativehyperlinktext;
+ font-size: 0.95em;
+}
+
+.url[selected="true"] {
+ color: inherit;
+}
+
+.tabIcon {
+ padding-inline-start: 2px;
+ padding-top: 2px;
+}
diff --git a/comm/suite/themes/classic/communicator/blockedSite.css b/comm/suite/themes/classic/communicator/blockedSite.css
new file mode 100644
index 0000000000..c695b3c974
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/blockedSite.css
@@ -0,0 +1,20 @@
+/* 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/. */
+
+#buttons > span {
+ float: left;
+}
+
+#buttons > #ignoreWarningButton {
+ float: right;
+ font-size: smaller;
+}
+
+/* the following ids refer to <span> elements defined in safeBrowsing.dtd */
+
+#malware_sitename,
+#phishing_sitename,
+#unwanted_sitename {
+ word-wrap: break-word;
+}
diff --git a/comm/suite/themes/classic/communicator/brand.css b/comm/suite/themes/classic/communicator/brand.css
new file mode 100644
index 0000000000..d80d5afbb7
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/brand.css
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/* ==== brand.css ===========================================================
+ == Styles related to branding in the Communicator suite.
+ ========================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#wrapper-throbber-box > #throbber-box > #navigator-throbber,
+#navigator-throbber {
+ -moz-user-focus: ignore;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ margin-inline-start: 10px;
+ margin-inline-end: 5px;
+ border: none;
+ padding: 1px;
+ min-width: 0;
+ list-style-image: url("chrome://communicator/skin/brand/throbber-single.png");
+ background-color: transparent;
+ -moz-appearance:none !important;
+}
+
+#navigator-throbber .button-box {
+ padding: 0;
+ margin: 0;
+}
+
+#navigator-throbber .button-icon {
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+}
+
+#navigator-throbber .button-text {
+ display: none;
+}
+
+#navigator-throbber[busy="true"] {
+ list-style-image: url("chrome://communicator/skin/brand/throbber-anim.png");
+}
+
+window[chromehidden~="toolbar"] #navigator-throbber,
+toolbar[mode="text"] #wrapper-throbber-box > #throbber-box > #navigator-throbber,
+toolbar[iconsize="small"] #wrapper-throbber-box > #throbber-box > #navigator-throbber,
+toolbar[mode="text"] #navigator-throbber,
+toolbar[iconsize="small"] #navigator-throbber {
+ margin: 0 5px;
+ list-style-image: url("chrome://communicator/skin/brand/throbber16-single.png");
+}
+
+window[chromehidden~="toolbar"] #navigator-throbber[busy="true"],
+toolbar[mode="text"] #navigator-throbber[busy="true"],
+toolbar[iconsize="small"] #navigator-throbber[busy="true"] {
+ list-style-image: url("chrome://communicator/skin/brand/throbber16-anim.png");
+}
diff --git a/comm/suite/themes/classic/communicator/brand/throbber-anim.png b/comm/suite/themes/classic/communicator/brand/throbber-anim.png
new file mode 100644
index 0000000000..c946b2e5a1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/brand/throbber-anim.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/brand/throbber-single.png b/comm/suite/themes/classic/communicator/brand/throbber-single.png
new file mode 100644
index 0000000000..609352f86a
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/brand/throbber-single.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/brand/throbber16-anim.png b/comm/suite/themes/classic/communicator/brand/throbber16-anim.png
new file mode 100644
index 0000000000..480ef01635
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/brand/throbber16-anim.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/brand/throbber16-single.png b/comm/suite/themes/classic/communicator/brand/throbber16-single.png
new file mode 100644
index 0000000000..c8a8c52aee
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/brand/throbber16-single.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/button.css b/comm/suite/themes/classic/communicator/button.css
new file mode 100644
index 0000000000..50fff7c831
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/button.css
@@ -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/. */
+
+/* ==== button.css ==========================================================
+ == Styles for special buttons in the Communicator suite.
+ ========================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Restore Windows Classic fallbacks removed by bug 1340495 */
+
+@media (-moz-windows-classic) {
+
+ /* ::::: toolbarbutton ::::: */
+
+ toolbarbutton {
+ border: 1px solid transparent;
+ background-color: transparent;
+ }
+
+ toolbarbutton:hover:not([disabled="true"]) {
+ border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+ }
+
+ toolbarbutton:hover:active:not([disabled="true"]),
+ toolbarbutton[open="true"]:hover,
+ toolbarbutton[open="true"] {
+ border-color: ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow;
+ }
+
+ toolbarbutton[checked="true"]:not([disabled="true"]) {
+ border-color: ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow;
+ background-color: rgba(255,255,255,0.5);
+ }
+
+ /* ::::: toolbarbutton menu ::::: */
+
+ .toolbarbutton-menu-dropmarker {
+ border: none !important;
+ background-color: transparent !important;
+ }
+
+ /* ::::: toolbarbutton menu-button ::::: */
+
+ toolbarbutton[type="menu-button"],
+ toolbarbutton[type="menu-button"]:hover,
+ toolbarbutton[type="menu-button"]:hover:active,
+ toolbarbutton[type="menu-button"][open="true"],
+ toolbarbutton[type="menu-button"][disabled="true"],
+ toolbarbutton[type="menu-button"][disabled="true"]:hover,
+ toolbarbutton[type="menu-button"][disabled="true"]:hover:active {
+ border-style: none;
+ }
+
+ /* .......... dropmarker .......... */
+
+ .toolbarbutton-menubutton-dropmarker {
+ background-color: transparent;
+ }
+
+}
+
+/* End restore Windows Classic fallbacks removed by bug 1340495 */
+
+/* ::::: large toolbar buttons ::::: */
+
+.toolbarbutton-1,
+.toolbarbutton-1 > .toolbarbutton-menubutton-button {
+ min-width: 47px;
+ -moz-box-orient: vertical;
+}
+
+.toolbarbutton-1[type="menu-button"] {
+ -moz-box-orient: horizontal;
+}
+
+.toolbarbutton-1,
+.toolbarbutton-1 > .toolbarbutton-menubutton-button,
+.toolbarbutton-1[disabled="true"]:hover:active,
+.toolbarbutton-1[disabled="true"]:hover:active > .toolbarbutton-menubutton-button {
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-inline-start: 2px;
+ padding-inline-end: 3px;
+}
+
+.toolbarbutton-1:hover:active,
+.toolbarbutton-1[open="true"],
+.toolbarbutton-1:hover:active > .toolbarbutton-menubutton-button {
+ padding-top: 3px;
+ padding-bottom: 1px;
+ padding-inline-start: 3px;
+ padding-inline-end: 2px;
+}
+
+.toolbarbutton-1[type="menu-button"],
+.toolbarbutton-1[type="menu-button"]:hover:active,
+.toolbarbutton-1[type="menu-button"][open="true"] {
+ padding: 0;
+}
+
+.toolbarbutton-1[type="menu"] {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#menu-vertical");
+}
+
+toolbar[mode="icons"] .toolbarbutton-1,
+toolbar[mode="icons"] .toolbarbutton-menubutton-button,
+toolbar[iconsize="small"] .toolbarbutton-1,
+toolbar[iconsize="small"] .toolbarbutton-menubutton-button {
+ min-width: 0px;
+}
+
+toolbar[mode="icons"] .toolbarbutton-text,
+toolbar[mode="text"] .toolbarbutton-icon {
+ display: none;
+}
+
+toolbar[mode="text"] .toolbarbutton-1,
+toolbar[mode="text"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
+ -moz-box-orient: horizontal;
+}
+
+toolbar[labelalign="end"] .toolbarbutton-1,
+toolbar[labelalign="end"] .toolbarbutton-1 > .toolbarbutton-menubutton-button,
+toolbar[labelalign="end"] .toolbarbutton-1 > hbox > vbox {
+ -moz-box-orient: horizontal;
+}
+
+/* ::::: standard toolbar buttons ::::: */
+
+.button-toolbar {
+ -moz-user-focus: ignore;
+ border: 2px solid;
+ border-radius: 0;
+ min-width: 0;
+ background-color: transparent;
+}
+
+.button-toolbar > .button-box {
+ margin: 0;
+ padding: 1px 2px;
+}
+
+.button-toolbar:hover:active > .button-box {
+ padding-top: 2px;
+ padding-bottom: 0px;
+ padding-inline-start: 3px;
+ padding-inline-end: 1px;
+}
+
+.button-toolbar > .button-box > .button-icon[src],
+.button-toolbar > .button-box > .button-text {
+ margin-inline-end: 2px;
+}
+
+.button-toolbar,
+.button-toolbar[disabled="true"]:hover,
+.button-toolbar[disabled="true"]:hover:active {
+ padding: 1px;
+ border-top-color: ThreeDHighlight;
+ border-right-color: ThreeDShadow;
+ border-bottom-color: ThreeDShadow;
+ border-left-color: ThreeDHighlight;
+}
+
+.button-toolbar:hover {
+ border-top-color: ThreeDDarkShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+ border-left-color: ThreeDDarkShadow;
+}
+
+.button-toolbar:hover:active {
+ border-top-color: ThreeDDarkShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+ border-left-color: ThreeDDarkShadow;
+}
+
+/* ::::: taskbuttons ::::: */
+
+.taskbutton {
+ padding: 1px;
+}
+
+.taskbutton:hover:active {
+ padding-top: 2px;
+ padding-bottom: 0px;
+ padding-inline-start: 2px;
+ padding-inline-end: 0px;
+}
+
+/* override windows */
+
+.toolbarbutton-icon[label]:not([label=""]),
+.toolbarbutton-icon[type="menu"] {
+ margin-inline-end: 2px;
+}
+
+/**
+ * Fix the missing dropmarker bevel in Classic (bug 216266)
+ * Not used in Firefox but used elsewhere,
+ **/
+
+@media (-moz-windows-classic) {
+
+ .toolbarbutton-menubutton-dropmarker {
+ border: 1px solid;
+ padding: 2px;
+ border-top-color: transparent;
+ border-right-color: transparent;
+ border-bottom-color: transparent;
+ border-left-color: transparent;
+ }
+
+ toolbarbutton[type="menu-button"]:hover > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]) {
+ border-top-color: ThreeDHighlight;
+ border-right-color: ThreeDShadow;
+ border-bottom-color: ThreeDShadow;
+ border-left-color: ThreeDHighlight;
+ }
+
+ toolbarbutton[type="menu-button"]:hover:active > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
+ toolbarbutton[type="menu-button"][open="true"] > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]) {
+ border-top-color: ThreeDShadow;
+ border-right-color: ThreeDHighlight;
+ border-bottom-color: ThreeDHighlight;
+ border-left-color: ThreeDShadow;
+ padding-top: 3px;
+ padding-bottom: 1px;
+ padding-inline-start: 3px;
+ padding-inline-end: 1px;
+ }
+}
diff --git a/comm/suite/themes/classic/communicator/certError.css b/comm/suite/themes/classic/communicator/certError.css
new file mode 100644
index 0000000000..85505bc9ef
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/certError.css
@@ -0,0 +1,81 @@
+/* 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/. */
+
+
+html {
+ background: -moz-Dialog;
+}
+
+body {
+ margin: 0;
+ padding: 0 1em;
+ color: -moz-FieldText;
+ font: message-box;
+}
+
+h1 {
+ margin: 0 0 .6em 0;
+ border-bottom: 1px solid ThreeDLightShadow;
+ font-size: 160%;
+}
+
+h2 {
+ font-size: 130%;
+}
+
+span.hostname {
+ font-weight: bolder;
+}
+
+#errorPageContainer {
+ position: relative;
+ min-width: 13em;
+ max-width: 52em;
+ margin: 4em auto;
+ border: 1px solid #FFBD09;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ background: url("chrome://global/skin/icons/sslWarning.png") left 0 no-repeat -moz-Field;
+ background-origin: content-box;
+}
+
+body[dir="rtl"] #errorPageContainer {
+ background-position: right 0;
+}
+
+#errorTitle {
+ margin-inline-start: 80px;
+}
+
+#errorLongContent {
+ margin-inline-start: 80px;
+}
+
+#errorShortDescText.wrap {
+ white-space: pre-wrap;
+}
+
+#technicalContent > h2, #expertContent > h2 {
+ cursor: pointer;
+ padding-inline-start: 20px;
+ position: relative;
+ left: -20px;
+ background: url("chrome://messenger/skin/icons/twisty-open.png") left center no-repeat;
+}
+
+#technicalContent[collapsed] > h2,
+#expertContent[collapsed] > h2 {
+ background-image: url("chrome://messenger/skin/icons/twisty-clsd.png");
+}
+
+#technicalContent[collapsed] > p,
+#expertContent[collapsed] > div {
+ display: none;
+}
+
+#technicalContentText {
+ overflow: auto;
+ white-space: pre-wrap;
+}
diff --git a/comm/suite/themes/classic/communicator/common.css b/comm/suite/themes/classic/communicator/common.css
new file mode 100644
index 0000000000..05acba2aa4
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/common.css
@@ -0,0 +1,66 @@
+/* - 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/. */
+
+@import url("resource://gre-resources/forms.css");
+@import url("resource://gre-resources/html.css");
+@import url("chrome://communicator/skin/communicator.css");
+
+@namespace html "http://www.w3.org/1999/xhtml";
+@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+*|*:root {
+ --in-content-page-color: -moz-DialogText;
+ --in-content-page-background: -moz-Field;
+ --in-content-text-color: -moz-DialogText;
+ --in-content-selected-text: HighlightText;
+ --in-content-header-border-color: #c8c8c8;
+ --in-content-box-background: #fff;
+ --in-content-box-background-odd: #f3f6fa;
+ --in-content-box-background-hover: #ebebeb;
+ --in-content-box-background-active: #dadada;
+ --in-content-box-border-color: #c1c1c1;
+ --in-content-button-background-hover: -moz-buttonhoverface;
+ --in-content-button-color-hover: -moz-buttonhovertext;
+ --in-content-button-background-hover-active: ButtonFace;
+ --in-content-button-color-hover-active: ButtonText;
+ --in-content-item-hover: rgba(0,149,221,0.25);
+ --in-content-item-selected: #0095dd;
+ --in-content-border-highlight: #ff9500;
+ --in-content-border-focus: #0095dd;
+ --in-content-border-color: ThreeDShadow;
+ --in-content-border-dotted-color: 1px dotted ThreeDShadow;
+ --in-content-category-text: #c1c1c1;
+ --in-content-category-border-focus: 1px dotted #fff;
+ --in-content-category-text-selected: #f2f2f2;
+ --in-content-category-background: #424f5a;
+ --in-content-category-background-hover: #5e6972;
+ --in-content-category-background-active: #343f48;
+ --in-content-tab-color: -moz-DialogText;
+ --in-content-link-color: #0095dd;
+ --in-content-link-color-hover: #178ce5;
+ --in-content-link-color-active: #ff9500;
+ --in-content-link-color-visited: #551a8b;
+ --in-content-primary-button-background: #0095dd;
+ --in-content-primary-button-background-hover: #008acb;
+ --in-content-primary-button-background-active: #006b9d;
+ --in-content-table-background: -moz-Dialog;
+ --in-content-table-border-dark-color: #d1d1d1;
+ --in-content-table-header-background: Highlight;
+}
+
+html|html,
+xul|page,
+xul|window {
+ font: message-box;
+ background-color: var(--in-content-page-background);
+ color: var(--in-content-page-color);
+}
+
+html|body {
+ font-size: inherit;
+}
+
+*|button {
+ min-height: 0px;
+}
diff --git a/comm/suite/themes/classic/communicator/communicator.css b/comm/suite/themes/classic/communicator/communicator.css
new file mode 100644
index 0000000000..0380a50393
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/communicator.css
@@ -0,0 +1,321 @@
+/* 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/. */
+
+/* ==== communicator.css ====================================================
+ == Styles shared everywhere throughout the Communicator suite.
+ ========================================================================== */
+
+@import url("chrome://global/skin/global.css");
+@import url("chrome://communicator/content/communicator.css");
+@import url("chrome://communicator/skin/brand.css");
+@import url("chrome://communicator/skin/button.css");
+@import url("chrome://communicator/skin/toolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.wizard-box {
+ padding: 20px 44px 10px;
+}
+
+/* ::::: toolbar-primary ::::: */
+
+.toolbar-primary {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-primary");
+}
+
+/* .padded is used by autocomplete widgets that don't have an icon. Gross. -dwh */
+textbox:not(.padded) {
+ cursor: default;
+ padding: 0;
+}
+
+/* ::::: autocomplete ::::: */
+
+.autocomplete-history-popup {
+ max-height: 25em;
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue) {
+ width: 16px;
+ height: 16px;
+ margin-left: 1px;
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue, directory) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue, file) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+/* ::::: online/offline icons ::::: */
+
+#offline-status[offline="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/offline.png");
+}
+
+#offline-status {
+ list-style-image: url("chrome://communicator/skin/icons/online.png");
+}
+
+/* ::::: security button icons ::::: */
+
+#security-button {
+ list-style-image: url("chrome://communicator/skin/icons/lock-insecure.png");
+}
+
+#security-button[level="high"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-secure.png");
+}
+
+#security-button[level="broken"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-broken.png");
+}
+
+/* ::::: spell checker ::::: */
+
+.spell-suggestion {
+ font-weight: bold;
+}
+
+/* ::::: error messages ::::: */
+
+description.error {
+ color: #FF0000;
+}
+
+/* ::::: directional button icons ::::: */
+
+.up {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
+}
+
+.down {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+}
+
+.sidebarTree {
+ border: none;
+ margin: 0px !important;
+}
+
+/* ::::: iconic menus and menuitems ::::: */
+
+menu.menu-iconic > .menu-iconic-left,
+menuitem.menuitem-iconic > .menu-iconic-left {
+ display: -moz-box;
+}
+
+/* ::::: toolbar print button ::::: */
+#print-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#print-button:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#print-button:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#print-button[disabled="true"] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #print-button:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #print-button:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #print-button[disabled="true"] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+/* ::::: lightweight themes ::::: */
+
+toolbar button:-moz-lwtheme,
+toolbar menulist:-moz-lwtheme:not([open="true"]),
+toolbar textbox:-moz-lwtheme:not([focused="true"]) {
+ opacity: .8;
+}
+
+treecols:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
+
+/* ::::: notification bars ::::: */
+
+.messageImage[value="refresh-blocked"] {
+ list-style-image: url("chrome://communicator/skin/icons/application.png");
+}
+
+.messageImage[value="plugin-crashed"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.messageImage[value="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geo.png");
+}
+
+.messageImage[value="persistent-storage"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-icons.svg#persistent-storage");
+ width: 16px;
+ height: 16px;
+}
+
+.messageImage[value="webNotifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
+.messageImage[value="indexedDB-permissions-prompt"],
+.messageImage[value="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+}
+
+.messageImage[value="addon-install-blocked"],
+.messageImage[value="addon-install-cancelled"],
+.messageImage[value="addon-install-complete"],
+.messageImage[value="addon-install-disabled"],
+.messageImage[value="addon-install-failed"],
+.messageImage[value="addon-install-started"],
+.messageImage[value="lwtheme-install-request"],
+.messageImage[value="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.messageImage[value="popup-blocked"] {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+.messageImage[value="blocked-badware-page"] {
+ list-style-image: url("chrome://global/skin/icons/blacklist_favicon.png");
+}
+
+.messageImage[value="EnterInsecureMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-insecure-16.png");
+}
+
+.messageImage[value="EnterSecureMessage"],
+.messageImage[value="BlockedActiveContentMessage"],
+.messageImage[value="BlockedDisplayContentMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-secure-16.png");
+}
+
+.messageImage[value="MixedContentMessage"],
+.messageImage[value="MixedActiveContentMessage"],
+.messageImage[value="MixedDisplayContentMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-broken-16.png");
+}
+
+/* ::::: tree rows ::::: */
+
+@media (-moz-windows-default-theme) {
+ treechildren:not(.autocomplete-treebody)::-moz-tree-row {
+ height: 1.3em !important;
+ border-width: 1px !important;
+ }
+}
+
+/* ::::: dialog header ::::: */
+
+dialogheader {
+ margin: 0px 5px 5px 5px;
+ border: 1px solid;
+ border-top-color: ThreeDDarkShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+ border-left-color: ThreeDDarkShadow;
+ padding: 5px 8px;
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.dialogheader-title {
+ margin: 0px !important;
+ font-size: larger;
+ font-weight: bold;
+}
+
+/* ::::: statusbar ::::: */
+
+statusbar {
+ -moz-appearance: statusbar;
+ border-top: 1px solid ThreeDLightShadow;
+ border-left: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDHighlight;
+ border-bottom: 1px solid ThreeDHighlight;
+ background-color: -moz-Dialog;
+ min-height: 22px;
+}
+
+statusbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-style: none;
+}
+
+statusbarpanel {
+ -moz-appearance: statusbarpanel;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border-left: 1px solid ThreeDHighlight;
+ border-top: 1px solid ThreeDHighlight;
+ border-right: 1px solid ThreeDShadow;
+ border-bottom: 1px solid ThreeDShadow;
+ padding: 0 4px;
+}
+
+statusbarpanel:not(.statusbar-resizerpanel):-moz-lwtheme {
+ -moz-appearance: none;
+ border-top-style: none;
+ border-bottom-style: none;
+ border-inline-start-style: none;
+}
+
+.statusbar-resizerpanel {
+ -moz-box-align: end;
+ -moz-box-pack: end;
+ -moz-appearance: resizerpanel;
+ padding: 0;
+ border: none;
+}
+
+.statusbarpanel-iconic,
+.statusbarpanel-iconic-text {
+ padding: 0 1px;
+}
+
+.statusbarpanel-backgroundbox {
+ -moz-box-align: stretch;
+ padding: 0px;
+}
+
+.statusbarpanel-backgroundbox > .statusbarpanel-contentbox {
+ padding: 0px 1px;
+ -moz-box-align: center;
+}
diff --git a/comm/suite/themes/classic/communicator/communicatorBindings.xml b/comm/suite/themes/classic/communicator/communicatorBindings.xml
new file mode 100644
index 0000000000..afd798a3a6
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/communicatorBindings.xml
@@ -0,0 +1,22 @@
+<?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/. -->
+
+
+<bindings id="communicatorBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="row-iconic" extends="xul:row">
+ <content>
+ <xul:hbox align="center">
+ <xul:image class="row-iconic-icon"/>
+ <children/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/themes/classic/communicator/config.css b/comm/suite/themes/classic/communicator/config.css
new file mode 100644
index 0000000000..92c1ee9ca7
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/config.css
@@ -0,0 +1,66 @@
+/* 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/. */
+
+@import url("chrome://global/skin/global.css");
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#warningScreen {
+ background-color: -moz-Dialog;
+}
+
+#warningBox {
+ background-image: url("chrome://global/skin/icons/warning-large.png");
+ background-repeat: no-repeat;
+ background-position: 30px 3em;
+ max-width: 55em;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 64px;
+ margin-inline-start: 1em;
+ margin-inline-end: 1em;
+}
+
+.title, .description {
+ max-width: 50em;
+ padding-inline-start: 3em;
+}
+
+#warningTitle {
+ margin: 0 0 .6em 0;
+ font-size: 160%;
+ border-bottom: 1px solid ThreeDLightShadow;
+}
+
+#warningText {
+ font-size: 110%;
+ margin-inline-start: 0;
+}
+
+#warningButton {
+ margin-top: 0.6em;
+}
+
+#showWarningNextTime {
+ margin-top: 0.6em;
+}
+
+#configTreeBody::-moz-tree-cell-text(user) {
+ font-weight: bold;
+}
+
+#configTreeBody::-moz-tree-cell-text(locked) {
+ font-style: italic;
+}
+
+#configTree {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+#filterRow {
+ margin-top: 5px;
+}
diff --git a/comm/suite/themes/classic/communicator/console/console-error-caret.png b/comm/suite/themes/classic/communicator/console/console-error-caret.png
new file mode 100644
index 0000000000..44b78aa29c
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/console/console-error-caret.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/console/console-error-dash.png b/comm/suite/themes/classic/communicator/console/console-error-dash.png
new file mode 100644
index 0000000000..ab07898eff
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/console/console-error-dash.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/console/console-toolbar.png b/comm/suite/themes/classic/communicator/console/console-toolbar.png
new file mode 100644
index 0000000000..fc681f9268
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/console/console-toolbar.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/console/console.css b/comm/suite/themes/classic/communicator/console/console.css
new file mode 100644
index 0000000000..e4e4afdabc
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/console/console.css
@@ -0,0 +1,208 @@
+/* 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/. */
+
+/* ===== console.css ====================================================
+ == Styles used by the Error Console window.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.console-box {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+/* ::::: console rows ::::: */
+
+.console-row {
+ border-bottom: 1px solid ThreeDLightShadow;
+ padding: 4px 0px;
+}
+
+.console-row-icon {
+ padding: 4px;
+ padding-inline-start: 5px;
+ -moz-box-align: start !important;
+}
+
+.console-row-msg > label:first-child,
+.console-row-file > label:first-child {
+ display: none;
+}
+
+.console-time {
+ font-weight: normal !important;
+}
+
+.console-icon {
+ list-style-image: inherit;
+}
+
+.console-error-msg {
+ margin-bottom: 2px;
+}
+
+/* ..... error rows ..... */
+
+.console-row-code {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-inline-start: 3px;
+ padding-inline-end: 0px;
+ color: #0000BB;
+ font-size: larger;
+}
+
+.console-dots,
+.console-caret {
+ height: 9px;
+}
+
+.console-dots {
+ background: url("chrome://communicator/skin/console/console-error-dash.png") repeat-x top;
+}
+
+.console-caret {
+ width: 7px;
+ background: url("chrome://communicator/skin/console/console-error-caret.png") no-repeat top;
+}
+
+/* ..... message rows ..... */
+
+.console-row[type="message"] {
+ font-family: monospace;
+}
+
+/* ..... selected state ..... */
+
+.console-row[selected="true"] {
+ background-image: url("chrome://communicator/skin/console/itemSelected.png");
+}
+
+.console-row-code[selected="true"],
+.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
+ color: inherit !important;
+}
+
+/* ::::: icons ::::: */
+
+.console-row[type="error"],
+.console-row[type="exception"] {
+ list-style-image: url("chrome://global/skin/icons/error-16.png");
+}
+
+.console-row[type="error"] .console-row-msg,
+.console-row[type="exception"] .console-row-msg {
+ font-weight: bold;
+}
+
+.console-row[type="warning"] {
+ list-style-image: url("chrome://global/skin/icons/warning-16.png");
+}
+
+.console-row[type="message"] {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+}
+
+/* ::::: toolbars ::::: */
+
+#TextboxEval {
+ margin: 2px !important;
+}
+
+#ButtonEval {
+ margin-top: 2px !important;
+ margin-bottom: 2px !important;
+ margin-inline-start: 0px !important;
+ margin-inline-end: 2px !important;
+}
+
+toolbarseparator {
+ min-height: 1em;
+}
+
+/* Toolbar icons */
+
+#ToolbarMode toolbarbutton {
+ min-width: 57px;
+ padding: 4px !important;
+}
+
+toolbar#ToolbarMode toolbarbutton:active,
+toolbar#ToolbarMode toolbarbutton[checked="true"] {
+ padding-inline-start: 5px !important;
+ padding-inline-end: 3px !important;
+}
+
+
+toolbar#ToolbarMode toolbarbutton {
+ list-style-image: url("chrome://communicator/skin/console/console-toolbar.png");
+ -moz-box-orient: horizontal;
+ padding: 4px !important;
+}
+
+#Console\:modeAll {
+ -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+#Console\:modeAll {
+ -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+#Console\:modeAll:hover,
+#Console\:modeAll[checked="true"] {
+ -moz-image-region: rect(24px 24px 48px 0px);
+}
+
+#Console\:modeErrors {
+ -moz-image-region: rect(0px 96px 24px 72px);
+}
+
+#Console\:modeErrors:hover,
+#Console\:modeErrors[checked="true"] {
+ -moz-image-region: rect(24px 96px 48px 72px);
+}
+
+#Console\:modeWarnings {
+ -moz-image-region: rect(0px 72px 24px 48px);
+}
+
+#Console\:modeWarnings:hover,
+#Console\:modeWarnings[checked="true"] {
+ -moz-image-region: rect(24px 72px 48px 48px);
+}
+
+#Console\:modeMessages {
+ -moz-image-region: rect(0px 48px 24px 24px);
+}
+
+#Console\:modeMessages:hover,
+#Console\:modeMessages[checked="true"] {
+ -moz-image-region: rect(24px 48px 48px 24px);
+}
+
+#Console\:clear {
+ -moz-image-region: rect(0px 120px 24px 96px);
+}
+
+#Console\:clear:hover,
+#Console\:clear[checked="true"] {
+ -moz-image-region: rect(24px 120px 48px 96px);
+}
+
+toolbar#ToolbarMode .toolbarbutton-icon {
+ padding: 2px 0 !important;
+}
+
+toolbar#ToolbarMode .toolbarbutton-text {
+ padding-inline-end: 4px;
+}
+
+
+/* ::::: Fix Error Console toolbar button text spacing ::::: */
+
+.toolbarbutton-text {
+ padding-inline-start: 0px;
+ padding-inline-end: 5px;
+}
diff --git a/comm/suite/themes/classic/communicator/console/itemSelected.png b/comm/suite/themes/classic/communicator/console/itemSelected.png
new file mode 100644
index 0000000000..964cd6acc6
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/console/itemSelected.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/customizeToolbar.css b/comm/suite/themes/classic/communicator/customizeToolbar.css
new file mode 100644
index 0000000000..3c11b9cbe0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/customizeToolbar.css
@@ -0,0 +1,23 @@
+/* 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/. */
+
+window,
+dialog {
+ padding: 6px;
+}
+
+#instructions {
+ font-weight: bold;
+ font-size: larger;
+}
+
+#palette-box {
+ -moz-appearance: listbox;
+ margin: 0 0 10px;
+}
+
+#palette-box > toolbarpaletteitem {
+ padding: 8px 2px;
+ margin: 0 8px;
+}
diff --git a/comm/suite/themes/classic/communicator/dataman/dataman.css b/comm/suite/themes/classic/communicator/dataman/dataman.css
new file mode 100644
index 0000000000..d13e307fc6
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/dataman/dataman.css
@@ -0,0 +1,31 @@
+/* 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/. */
+
+#permList {
+ margin-top: .5em;
+ overflow: auto;
+}
+
+#permList[disabled="true"] {
+ opacity: .5;
+}
+
+.permission {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted ThreeDFace;
+}
+
+.hostLabel {
+ font-weight: bold;
+ font-style: italic;
+ margin-inline-end: 1em;
+}
+
+.permissionLabel {
+ font-weight: bold;
+}
diff --git a/comm/suite/themes/classic/communicator/dataman/datamanIcon-16.png b/comm/suite/themes/classic/communicator/dataman/datamanIcon-16.png
new file mode 100644
index 0000000000..9f1ba6b43e
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/dataman/datamanIcon-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/datetimepicker.css b/comm/suite/themes/classic/communicator/datetimepicker.css
new file mode 100644
index 0000000000..006a63a4c0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/datetimepicker.css
@@ -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/. */
+
+/* ===== datetimepicker.css =============================================
+ == Styles used by the XUL datepicker and timepicker elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+datepicker {
+ margin: 2px 4px;
+ padding: 0;
+ border: none;
+ background: none;
+ cursor: default;
+}
+
+.datetimepicker-input-box {
+ -moz-appearance: textfield;
+ cursor: text;
+ margin-inline-end: 2px;
+ padding: 2px 0 3px;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+ color: -moz-FieldText;
+}
+
+.datetimepicker-input-subbox {
+ width: 1.6em;
+}
+
+html|*.datetimepicker-input {
+ text-align: end;
+}
+
+.datetimepicker-separator {
+ margin: 0 !important;
+}
+
+.datetimepicker-year {
+ width: 3.2em;
+}
+
+datepicker[readonly="true"] {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+datepicker[disabled="true"] {
+ cursor: default;
+ background-color: -moz-Dialog;
+ color: GrayText;
+}
+
+.datepicker-mainbox {
+ margin: 2px 4px;
+ border: 1px ThreeDShadow;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.datepicker-popupgrid > .datepicker-mainbox {
+ margin: 0;
+ border: none;
+}
+
+.datepicker-gridlabel, .datepicker-weeklabel {
+ text-align: center;
+}
+
+.datepicker-gridlabel[today="true"] {
+ background-color: darkgrey;
+ color: white;
+}
+
+.datepicker-gridlabel[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.datepicker-button {
+ -moz-appearance: none;
+ min-width: 8px;
+ padding: 0;
+}
+
+.datepicker-previous,
+.datepicker-next:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://messenger/skin/icons/arrow/arrow-left.png");
+}
+
+.datepicker-next,
+.datepicker-previous:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://messenger/skin/icons/arrow/arrow-right.png");
+}
diff --git a/comm/suite/themes/classic/communicator/dialogs.css b/comm/suite/themes/classic/communicator/dialogs.css
new file mode 100644
index 0000000000..58b813f719
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/dialogs.css
@@ -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/. */
+
+/* ==== dialogs.css ==================================================
+ == Styles used by certain dialogs in the Communicator suite.
+ ====================================================================== */
+
+@import url("chrome://communicator/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/classic/communicator/downloads/dl-remove.png b/comm/suite/themes/classic/communicator/downloads/dl-remove.png
new file mode 100644
index 0000000000..167ecbb08c
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/downloads/dl-remove.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/downloads/downloadButtons.png b/comm/suite/themes/classic/communicator/downloads/downloadButtons.png
new file mode 100644
index 0000000000..4fe7963abb
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/downloads/downloadButtons.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/downloads/downloadmanager.css b/comm/suite/themes/classic/communicator/downloads/downloadmanager.css
new file mode 100644
index 0000000000..db3e1e2a82
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/downloads/downloadmanager.css
@@ -0,0 +1,95 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Note to themers:
+ On rows and all cells, those properties for download states are available:
+ active, inactive, resumable, paused, downloading, finished, failed, canceled, blocked
+*/
+
+treechildren::-moz-tree-image(Name) {
+ margin-inline-end: 2px;
+}
+
+#pauseButton,
+treechildren::-moz-tree-image(ActionPlay, downloading, resumable) {
+ /* pause */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+#ActionPlay,
+#resumeButton,
+treechildren::-moz-tree-image(ActionPlay, paused, resumable) {
+ /* resume */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+#retryButton,
+treechildren::-moz-tree-image(ActionPlay, failed),
+treechildren::-moz-tree-image(ActionPlay, canceled) {
+ /* retry */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+#ActionStop,
+#cancelButton,
+treechildren::-moz-tree-image(ActionStop, active) {
+ /* cancel */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+treechildren::-moz-tree-image(ActionStop, inactive) {
+ /* remove */
+ list-style-image: url("chrome://communicator/skin/downloads/dl-remove.png");
+ -moz-image-region: auto;
+}
+
+/* progress dialogs */
+#dlProgressWindow {
+ /* match dialog.css */
+ padding-top: 8px;
+ padding-bottom: 10px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+/* label with dropdown, actually done as a button type=menu */
+#fileName, #fileSource {
+ -moz-appearance: none;
+ background-color: transparent;
+ margin: 0px 5px;
+ border: 0px;
+ min-width: 0px;
+ min-height: 0px;
+}
+
+#fileName > .button-box,
+#fileSource > .button-box {
+ -moz-appearance: none;
+ padding: 0px;
+}
+
+#fileName {
+ font-weight: bold;
+}
+
+.mini-button {
+ -moz-appearance: none;
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ margin: 0;
+ min-width: 0;
+ min-height: 0;
+}
+
+.mini-button > .button-box {
+ -moz-appearance: none;
+ padding: 0 !important;
+}
diff --git a/comm/suite/themes/classic/communicator/feed-subscribe-ui.css b/comm/suite/themes/classic/communicator/feed-subscribe-ui.css
new file mode 100644
index 0000000000..b5b1fe18df
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/feed-subscribe-ui.css
@@ -0,0 +1,20 @@
+/* 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/. */
+
+#feedSubscribeLine {
+ font: message-box;
+}
+
+.menuitem-iconic {
+ padding-inline-start: 2px;
+}
+
+.menu-iconic-left {
+ display: -moz-box;
+ padding-inline-end: 2px;
+}
+
+menupopup:-moz-locale-dir(rtl) {
+ direction: rtl;
+}
diff --git a/comm/suite/themes/classic/communicator/feed-subscribe.css b/comm/suite/themes/classic/communicator/feed-subscribe.css
new file mode 100644
index 0000000000..b709d9c056
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/feed-subscribe.css
@@ -0,0 +1,148 @@
+/* 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/. */
+
+html {
+ background: -moz-Dialog;
+ font: message-box;
+}
+
+#feedBody {
+ border: 1px solid ThreeDShadow;
+ padding: 3em;
+ padding-inline-start: 30px;
+ margin: 2em auto;
+ background: -moz-Field;
+}
+
+#feedHeaderContainer {
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ margin: -4em auto 0 auto;
+ background-color: InfoBackground;
+}
+
+#feedHeader {
+ margin-top: 4.9em;
+ margin-bottom: 1em;
+ margin-inline-start: 1.4em;
+ margin-inline-end: 1em;
+ padding-inline-start: 2.9em;
+ font-size: 110%;
+ color: InfoText;
+}
+
+.feedBackground {
+ background: url("chrome://communicator/skin/icons/feedIcon.png") 0% 10% no-repeat InfoBackground;
+}
+
+.videoPodcastBackground {
+ background: url("chrome://communicator/skin/icons/videoFeedIcon.png") 0% 10% no-repeat InfoBackground;
+}
+
+.audioPodcastBackground {
+ background: url("chrome://communicator/skin/icons/audioFeedIcon.png") 0% 10% no-repeat InfoBackground;
+}
+
+#feedHeader[dir="rtl"] {
+ background-position: 100% 10%;
+}
+
+#feedIntroText {
+ display: none;
+}
+
+#feedHeader[firstrun="true"] #feedIntroText {
+ padding-top: 0.1em;
+ padding-inline-start: 0.6em;
+ display: block;
+}
+
+#feedHeader[firstrun="true"] > #feedSubscribeLine {
+ padding-inline-start: 1.8em;
+}
+
+#feedSubscribeLine {
+ padding-top: 0.2em;
+}
+
+/* Don't print subscription UI */
+@media print {
+ #feedHeaderContainer {
+ display: none;
+ }
+}
+
+body {
+ margin: 0;
+ padding: 0 3em;
+ color: -moz-fieldText;
+ font: message-box;
+}
+
+h1 {
+ font-size: 160%;
+ border-bottom: 2px solid ThreeDLightShadow;
+ margin: 0 0 .2em 0;
+}
+
+h2 {
+ font-size: 110%;
+ font-weight: normal;
+ margin: 0 0 .6em 0;
+}
+
+#feedTitleLink {
+ float: right;
+ margin-inline-start: .6em;
+ margin-inline-end: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+a[href] img {
+ border: none;
+}
+
+#feedTitleContainer {
+ margin-inline-start: 0;
+ margin-inline-end: .6em;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#feedTitleImage {
+ margin-inline-start: .6em;
+ margin-inline-end: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ max-width: 300px;
+ max-height: 150px;
+}
+
+.feedEntryContent {
+ font-size: 110%;
+}
+
+.lastUpdated {
+ font-size: 85%;
+ font-weight: normal;
+}
+
+.type-icon {
+ vertical-align: bottom;
+ height: 16px;
+ width: 16px;
+}
+
+.enclosures {
+ border: 1px solid ThreeDShadow;
+ padding: 1em;
+ margin: 1em auto;
+ background: -moz-Dialog;
+}
+
+.enclosure {
+ vertical-align: middle;
+ margin-left: 2px;
+}
diff --git a/comm/suite/themes/classic/communicator/fullscreen-video.css b/comm/suite/themes/classic/communicator/fullscreen-video.css
new file mode 100644
index 0000000000..88d9c7b734
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/fullscreen-video.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+html,
+body,
+video {
+ height: 100%;
+}
+
+html {
+ background-color: black;
+}
+
+body {
+ margin: 0px;
+ overflow: -moz-hidden-unscrollable;
+ visibility: hidden;
+}
+
+video {
+ width: 100%;
+ max-height: 100%;
+}
+
+div {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ width: 32px;
+ height: 32px;
+ background: url("chrome://communicator/skin/icons/closeFullScreenVideo.png") center center no-repeat;
+}
diff --git a/comm/suite/themes/classic/communicator/helpviewer/Toolbar-rtl.png b/comm/suite/themes/classic/communicator/helpviewer/Toolbar-rtl.png
new file mode 100644
index 0000000000..eaa8088ec2
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/helpviewer/Toolbar-rtl.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/helpviewer/help.css b/comm/suite/themes/classic/communicator/helpviewer/help.css
new file mode 100644
index 0000000000..4b4036aaca
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/helpviewer/help.css
@@ -0,0 +1,136 @@
+/* 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/. */
+
+@import url("chrome://communicator/skin/");
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#HelpToolbar toolbarbutton {
+ -moz-box-orient: horizontal !important;
+ min-width: 0px;
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ margin: 0 !important;
+ padding: 4px 5px;
+}
+
+#HelpToolbar:-moz-locale-dir(rtl) toolbarbutton {
+ list-style-image: url("chrome://communicator/skin/helpviewer/Toolbar-rtl.png");
+}
+
+/* Hide labels for the toolbar because we really don't need them what with the
+ tooltips */
+#HelpToolbar .toolbarbutton-text {
+ display: none;
+}
+
+/* With no labels, we don't need the margin on the icon that separates it
+ from the label */
+#HelpToolbar .toolbarbutton-icon {
+ margin-inline-end: 0;
+}
+
+/* Set the minimum sidebar width so the help contents aren't squeezed together.*/
+#help-sidebar { min-width: 15em; width: 20em; max-width: 25em; }
+
+#help-back-button toolbarbutton,
+#help-forward-button toolbarbutton {
+ list-style-image: inherit;
+ -moz-image-region: inherit;
+}
+
+/* ----- BACK BUTTON ----- */
+
+#help-back-button {
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#help-back-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#help-back-button:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#help-back-button[disabled="true"] {
+ -moz-image-region: rect(60px 119px 89px 90px);
+}
+
+#help-back-button .toolbarbutton-menubutton-button {
+ -moz-box-align: end !important;
+}
+
+/* ----- FORWARD BUTTON ----- */
+
+#help-forward-button {
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#help-forward-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#help-forward-button:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#help-forward-button[disabled="true"] {
+ -moz-image-region: rect(90px 119px 119px 90px);
+}
+
+/* ----- HOME BUTTON ----- */
+
+#help-home-button {
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#help-home-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#help-home-button:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(120px 89px 149px 60px);
+}
+
+#help-home-button[disabled="true"] {
+ -moz-image-region: rect(120px 119px 149px 90px);
+}
+
+/* ----- PRINT BUTTON ----- */
+
+#help-print-button {
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#help-print-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#help-print-button:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#help-print-button[disabled="true"] {
+ -moz-image-region: rect(0 119px 29px 90px);
+}
+
+/* ----- OTHER ----- */
+
+#context-copy[disabled="true"] {
+ display: none;
+}
+
+/* make findbar appear above content */
+#appcontent {
+ -moz-box-direction: reverse;
+}
+
+/* style findbar for being on top */
+#FindToolbar {
+ border-top-color: ThreeDHighlight;
+ border-top-width: 1px;
+ border-bottom: 1px solid;
+ border-bottom-color: ThreeDShadow;
+ padding-top: 1px;
+ padding-bottom: 0px;
+}
diff --git a/comm/suite/themes/classic/communicator/icons/alwaysAsk.png b/comm/suite/themes/classic/communicator/icons/alwaysAsk.png
new file mode 100644
index 0000000000..ddd4cb2130
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/alwaysAsk.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/application.png b/comm/suite/themes/classic/communicator/icons/application.png
new file mode 100644
index 0000000000..e878926105
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/application.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/close-button.png b/comm/suite/themes/classic/communicator/icons/close-button.png
new file mode 100644
index 0000000000..e9e00449c2
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/close-button.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/closeFullScreenVideo.png b/comm/suite/themes/classic/communicator/icons/closeFullScreenVideo.png
new file mode 100644
index 0000000000..6090eab39f
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/closeFullScreenVideo.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/communicatoricons-small.png b/comm/suite/themes/classic/communicator/icons/communicatoricons-small.png
new file mode 100644
index 0000000000..0e48c39fcb
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/communicatoricons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/communicatoricons.png b/comm/suite/themes/classic/communicator/icons/communicatoricons.png
new file mode 100644
index 0000000000..28abfa3ff8
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/communicatoricons.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/feedIcon.png b/comm/suite/themes/classic/communicator/icons/feedIcon.png
new file mode 100644
index 0000000000..848a3bac91
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/feedIcon.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/feedIcon16.png b/comm/suite/themes/classic/communicator/icons/feedIcon16.png
new file mode 100644
index 0000000000..0286dffab9
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/feedIcon16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/geo.png b/comm/suite/themes/classic/communicator/icons/geo.png
new file mode 100644
index 0000000000..2c1b2ecfba
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/geo.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/geolocation-16.png b/comm/suite/themes/classic/communicator/icons/geolocation-16.png
new file mode 100644
index 0000000000..d710e7336d
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/geolocation-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/geolocation-64.png b/comm/suite/themes/classic/communicator/icons/geolocation-64.png
new file mode 100644
index 0000000000..1bd46ba5e1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/geolocation-64.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/identity.png b/comm/suite/themes/classic/communicator/icons/identity.png
new file mode 100644
index 0000000000..dc35c2c6fe
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/identity.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/information-48.png b/comm/suite/themes/classic/communicator/icons/information-48.png
new file mode 100644
index 0000000000..59893b29b4
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/information-48.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/key-16.png b/comm/suite/themes/classic/communicator/icons/key-16.png
new file mode 100644
index 0000000000..ac135b847e
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/key-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/key-64.png b/comm/suite/themes/classic/communicator/icons/key-64.png
new file mode 100644
index 0000000000..148f4ebb81
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/key-64.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/loading.png b/comm/suite/themes/classic/communicator/icons/loading.png
new file mode 100644
index 0000000000..5d7858dffd
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/loading.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/lock-broken-16.png b/comm/suite/themes/classic/communicator/icons/lock-broken-16.png
new file mode 100644
index 0000000000..102c96c7d1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/lock-broken-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/lock-broken.png b/comm/suite/themes/classic/communicator/icons/lock-broken.png
new file mode 100644
index 0000000000..71c3aa8191
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/lock-broken.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/lock-insecure-16.png b/comm/suite/themes/classic/communicator/icons/lock-insecure-16.png
new file mode 100644
index 0000000000..6e6c7ab16b
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/lock-insecure-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/lock-insecure.png b/comm/suite/themes/classic/communicator/icons/lock-insecure.png
new file mode 100644
index 0000000000..a6b0842e57
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/lock-insecure.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/lock-secure-16.png b/comm/suite/themes/classic/communicator/icons/lock-secure-16.png
new file mode 100644
index 0000000000..1d9bd471ea
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/lock-secure-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/lock-secure.png b/comm/suite/themes/classic/communicator/icons/lock-secure.png
new file mode 100644
index 0000000000..178499e002
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/lock-secure.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/notification-16.png b/comm/suite/themes/classic/communicator/icons/notification-16.png
new file mode 100644
index 0000000000..9e61a63973
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/notification-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/notification-64.png b/comm/suite/themes/classic/communicator/icons/notification-64.png
new file mode 100644
index 0000000000..a01d0ab776
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/notification-64.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/notification-icons.svg b/comm/suite/themes/classic/communicator/icons/notification-icons.svg
new file mode 100644
index 0000000000..cc5d4f7ba1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/notification-icons.svg
@@ -0,0 +1,104 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+ width="32" height="32" viewBox="0 0 32 32">
+ <style>
+ :root > use:not(:target),
+ :root > g:not(:target),
+ :root > circle:not(:target),
+ #strikeout {
+ display: none;
+ }
+ .blocked:target ~ #strikeout {
+ display: block;
+ }
+ .blocked {
+ clip-path: url(#blocked-clipPath);
+ }
+
+ #badge {
+ fill: #3088d4;
+ }
+
+ #update-icon {
+ stroke: #fff;
+ stroke-width: 3px;
+ stroke-linecap: round;
+ }
+ </style>
+
+ <defs>
+ <path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z" />
+ <path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z" />
+ <path id="focus-tab-by-prompt-icon" d="M29.43,25,18.57,3.8A2.92,2.92,0,0,0,16,2a2.92,2.92,0,0,0-2.57,1.8L2.57,25a3.47,3.47,0,0,0,0,3.4A3.15,3.15,0,0,0,5.33,30H26.66a3.15,3.15,0,0,0,2.77-1.6A3.47,3.47,0,0,0,29.43,25ZM16,7.2a2.3,2.3,0,0,1,2.37,2.4L18,18a1.88,1.88,0,0,1-2,2,1.88,1.88,0,0,1-2-2l-.4-8.4A2.3,2.3,0,0,1,16,7.2ZM16,28a3,3,0,0,1,0-6,3,3,0,0,1,0,6Z"/>
+ <path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z" />
+ <path id="geo-linux-detailed-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 3,2.1 a 11,11 0 0 0 9,9 l 1,-5 2,0 1,5 a 11,11 0 0 0 9,-9 l -5,-1 0,-2 5,-1 a 11,11 0 0 0 -9,-9 l -1,5 -2,0 -1,-5 a 11,11 0 0 0 -9,9 l 5,1 0,2 z" />
+ <path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z" />
+ <path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z" />
+ <path id="geo-windows-detailed-icon" d="m 2,14.5 0,3 2,0.5 a 12,12 0 0 0 10,10 l 0.5,2 3,0 0.5,-2 a 12,12 0 0 0 10,-10 l 2,-0.5 0,-3 -2,-0.5 a 12,12 0 0 0 -10,-10 l -0.5,-2 -3,0 -0.5,2 a 12,12 0 0 0 -10,10 z m 4,1.4 a 10,10 0 1 1 0,0.2 z m 3,0 a 7,7 0 1 1 0,0.2 z" />
+ <path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z" />
+ <path id="login-icon" d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z" />
+ <path id="login-detailed-icon" d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z" />
+ <path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
+ <path id="microphone-detailed-icon" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
+ <path id="persistent-storage-icon" d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
+ <path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z" />
+ <path id="popup-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
+ <path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
+ <path id="update-icon" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14" />
+
+ <clipPath id="blocked-clipPath">
+ <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
+ </clipPath>
+
+ <mask id="i-mask" style="fill-opacity: 1;">
+ <rect fill="white" width="32" height="32"/>
+ <circle fill="black" cx="16" cy="9" r="2.5"/>
+ <rect fill="black" x="14" y="14" width="4" height="10" rx="2" ry="2"/>
+ </mask>
+ </defs>
+
+ <g id="default-info">
+ <circle cx="16" cy="16" r="14" mask="url(#i-mask)"/>
+ </g>
+
+ <use id="camera" href="#camera-icon" />
+ <use id="camera-sharing" href="#camera-icon"/>
+ <use id="camera-indicator" href="#camera-icon" />
+ <use id="camera-blocked" class="blocked" href="#camera-icon" />
+ <use id="desktop-notification" href="#desktop-notification-icon" />
+ <use id="desktop-notification-blocked" class="blocked" href="#desktop-notification-icon" />
+ <use id="focus-tab-by-prompt" href="#focus-tab-by-prompt-icon" />
+ <use id="geo-osx" href="#geo-osx-icon" />
+ <use id="geo-osx-blocked" class="blocked" href="#geo-osx-icon" />
+ <use id="geo-linux" href="#geo-linux-icon" />
+ <use id="geo-linux-blocked" class="blocked" href="#geo-linux-icon" />
+ <use id="geo-linux-detailed" href="#geo-linux-detailed-icon" />
+ <use id="geo-windows" href="#geo-windows-icon" />
+ <use id="geo-windows-blocked" class="blocked" href="#geo-windows-icon" />
+ <use id="geo-windows-detailed" href="#geo-windows-detailed-icon" />
+ <use id="indexedDB" href="#indexedDB-icon" />
+ <use id="indexedDB-blocked" class="blocked" href="#indexedDB-icon" />
+ <use id="login" href="#login-icon" />
+ <use id="login-detailed" href="#login-detailed-icon" />
+ <use id="microphone" href="#microphone-icon" />
+ <use id="microphone-sharing" href="#microphone-icon"/>
+ <use id="microphone-indicator" href="#microphone-icon"/>
+ <use id="microphone-blocked" class="blocked" href="#microphone-icon" />
+ <use id="microphone-detailed" href="#microphone-detailed-icon" />
+ <use id="persistent-storage" href="#persistent-storage-icon" />
+ <use id="persistent-storage-blocked" class="blocked" href="#persistent-storage-icon" />
+ <use id="plugin" href="#plugin-icon" />
+ <use id="plugin-blocked" class="blocked" href="#plugin-icon" />
+ <use id="plugin-badge" href="#badge" />
+ <use id="popup" href="#popup-icon" />
+ <use id="screen" href="#screen-icon" />
+ <use id="screen-sharing" href="#screen-icon"/>
+ <use id="screen-indicator" href="#screen-icon"/>
+ <use id="screen-blocked" class="blocked" href="#screen-icon" />
+ <use id="update" href="#update-icon" />
+
+ <path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
+ <circle id="badge" cx="27" cy="5" r="5"/>
+</svg>
diff --git a/comm/suite/themes/classic/communicator/icons/offline.png b/comm/suite/themes/classic/communicator/icons/offline.png
new file mode 100644
index 0000000000..c23b98e7de
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/offline.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/online.png b/comm/suite/themes/classic/communicator/icons/online.png
new file mode 100644
index 0000000000..2e9fb8c3b1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/online.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/question-48.png b/comm/suite/themes/classic/communicator/icons/question-48.png
new file mode 100644
index 0000000000..b818636200
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/question-48.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/save.png b/comm/suite/themes/classic/communicator/icons/save.png
new file mode 100644
index 0000000000..46bb519033
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/save.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/search.png b/comm/suite/themes/classic/communicator/icons/search.png
new file mode 100644
index 0000000000..b889c6d98b
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/search.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-cool.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-cool.png
new file mode 100644
index 0000000000..1eee4586e5
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-cool.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-cry.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-cry.png
new file mode 100644
index 0000000000..3c25cb658d
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-cry.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-embarrassed.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-embarrassed.png
new file mode 100644
index 0000000000..5088e28e07
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-embarrassed.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-foot.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-foot.png
new file mode 100644
index 0000000000..d1589f3696
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-foot.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-frown.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-frown.png
new file mode 100644
index 0000000000..159c04b0c6
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-frown.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-innocent.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-innocent.png
new file mode 100644
index 0000000000..85cbcf67ed
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-innocent.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-kiss.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-kiss.png
new file mode 100644
index 0000000000..36fbf51dde
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-kiss.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-laughing.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-laughing.png
new file mode 100644
index 0000000000..0d013d5012
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-laughing.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-money.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-money.png
new file mode 100644
index 0000000000..59f089944f
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-money.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-sealed.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-sealed.png
new file mode 100644
index 0000000000..c4f3042810
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-sealed.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-smile.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-smile.png
new file mode 100644
index 0000000000..de862b15db
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-smile.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-surprise.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-surprise.png
new file mode 100644
index 0000000000..4b4d423a55
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-surprise.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-tongue.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-tongue.png
new file mode 100644
index 0000000000..9f53727055
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-tongue.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-undecided.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-undecided.png
new file mode 100644
index 0000000000..8311076c53
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-undecided.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-wink.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-wink.png
new file mode 100644
index 0000000000..e2db57ec39
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-wink.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/smileys/smiley-yell.png b/comm/suite/themes/classic/communicator/icons/smileys/smiley-yell.png
new file mode 100644
index 0000000000..2ae846d201
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/smileys/smiley-yell.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/icons/warning-24.png b/comm/suite/themes/classic/communicator/icons/warning-24.png
new file mode 100644
index 0000000000..42f20a7a49
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/icons/warning-24.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/numberbox.css b/comm/suite/themes/classic/communicator/numberbox.css
new file mode 100644
index 0000000000..ea9189b30c
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/numberbox.css
@@ -0,0 +1,23 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+textbox[type="number"] {
+ padding: 0 !important;
+ cursor: default;
+}
+
+html|*.numberbox-input {
+ text-align: right;
+}
+
+.numberbox-input-box {
+ -moz-box-align: center;
+}
+
+textbox[hidespinbuttons="true"] > .numberbox-input-box {
+ -moz-appearance: textfield;
+}
diff --git a/comm/suite/themes/classic/communicator/places/allBookmarks.png b/comm/suite/themes/classic/communicator/places/allBookmarks.png
new file mode 100644
index 0000000000..9bf003529f
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/allBookmarks.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmark-folder-closed.png b/comm/suite/themes/classic/communicator/places/bookmark-folder-closed.png
new file mode 100644
index 0000000000..514ab6f137
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark-folder-closed.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmark-folder-dis.png b/comm/suite/themes/classic/communicator/places/bookmark-folder-dis.png
new file mode 100644
index 0000000000..3ecfd95aa0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark-folder-dis.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmark-folder-open.png b/comm/suite/themes/classic/communicator/places/bookmark-folder-open.png
new file mode 100644
index 0000000000..ec21a423a0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark-folder-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmark-item-dis.png b/comm/suite/themes/classic/communicator/places/bookmark-item-dis.png
new file mode 100644
index 0000000000..cd169981d1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark-item-dis.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmark-item-updated.png b/comm/suite/themes/classic/communicator/places/bookmark-item-updated.png
new file mode 100644
index 0000000000..2d12e93098
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark-item-updated.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmark-item.svg b/comm/suite/themes/classic/communicator/places/bookmark-item.svg
new file mode 100644
index 0000000000..9953214605
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark-item.svg
@@ -0,0 +1,13 @@
+<!-- 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/. -->
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<image xlink:href="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAP1BMVEVhAAAJJg4VPhghUyEr
+YyktaS04dzhetGNsu3FtvnJ0v319wYJ/w4OKxpKPyJmYyaGcy6inzLCrzbC3zbECGg8L8SJ4
+AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElN
+RQfXAwQNJhdD+P4AAAAAgHRFWHRDb21tZW50AChDKSAyMDA2IE1vemlsbGEub3JnCkF1dGhv
+cjogTWFybG9uIEJpc2hvcApJY29uIERlc2lnbjogSWNvbmZhY3RvcnkKQ29udHJpYnV0b3Jz
+OgogIE1hbnVlbCBSZWltZXIgKE1hbnVlbC5SZWltZXJAZ214LmRlKVc/DKgAAABISURBVBjT
+jc67AoAgDEPR+ohRoaK0//+tzmHibjlTzGYCoDsioJAugt5bVeHnReTg67fIzlYvkY1POUVW
+OvTPwhz+5giWNtcPheMCpetMnNIAAAAASUVORK5CYII=" width="16" height="16"/></svg>
diff --git a/comm/suite/themes/classic/communicator/places/bookmark.png b/comm/suite/themes/classic/communicator/places/bookmark.png
new file mode 100644
index 0000000000..bee337e107
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmark.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmarks.css b/comm/suite/themes/classic/communicator/places/bookmarks.css
new file mode 100644
index 0000000000..37069ae749
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmarks.css
@@ -0,0 +1,131 @@
+/* -*- Mode: C; 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Trees ::::: */
+
+treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+treechildren::-moz-tree-twisty {
+ padding-bottom: 1px;
+}
+
+/**
+ * Style rules for generic bookmarks items.
+ */
+
+treechildren::-moz-tree-image(title) {
+ margin-inline-end: 2px;
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item,
+treechildren::-moz-tree-image(title) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.bookmark-item[container="true"],
+treechildren::-moz-tree-image(title, container) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+.bookmark-item[open="true"],
+treechildren::-moz-tree-image(title, open) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open.png");
+}
+
+.bookmark-item[container="true"][open="true"][loading="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png") !important;
+}
+
+.bookmark-item > .toolbarbutton-box > .toolbarbutton-icon {
+ list-style-image: inherit;
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item[container][livemark],
+treechildren::-moz-tree-image(title, container, livemark) {
+ list-style-image: url("chrome://communicator/skin/places/livemark-folder.png");
+ -moz-image-region: auto;
+}
+
+.bookmark-item[container][livemark] .bookmark-item,
+treechildren::-moz-tree-image(title, livemarkItem) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-updated.png");
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited],
+treechildren::-moz-tree-image(title, livemarkItem, visited) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+#bookmarksToolbarFolderMenu,
+#BMB_bookmarksToolbarFolderMenu,
+treechildren::-moz-tree-image(container, queryFolder_toolbar_____) {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(container, queryFolder_menu________) {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksMenu.png");
+ -moz-image-region: auto;
+}
+
+#unsortedBookmarksFolderMenu,
+#BMB_unsortedBookmarksFolderMenu,
+treechildren::-moz-tree-image(container, queryFolder_unfiled_____) {
+ list-style-image: url("chrome://communicator/skin/places/unsortedBookmarks.png");
+ -moz-image-region: auto;
+}
+
+/* query-nodes should be styled even if they're not expandable */
+.bookmark-item[container][query],
+treechildren::-moz-tree-image(query) {
+ list-style-image: url("chrome://communicator/skin/places/query.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(query, OrganizerQuery_AllBookmarks) {
+ list-style-image: url("chrome://communicator/skin/places/allBookmarks.png");
+ -moz-image-region: auto;
+}
+
+.bookmark-item[query][tagContainer],
+treechildren::-moz-tree-image(title, query, tagContainer),
+treechildren::-moz-tree-image(query, OrganizerQuery_Tags) {
+ list-style-image: url("chrome://communicator/skin/places/tag.png");
+ -moz-image-region: auto;
+}
+
+/* calendar icon for history grouping items by day */
+treechildren::-moz-tree-image(title, query, dayContainer) {
+ list-style-image: url("chrome://communicator/skin/places/calendar.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(query, OrganizerQuery_History) {
+ list-style-image: url("chrome://communicator/skin/places/history.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(title, separator) {
+ list-style-image: none;
+ width: 0px;
+ height: 0px;
+}
+
+treechildren::-moz-tree-cell-text(title, separator) {
+ color: ThreeDShadow;
+ margin: 0px 5px;
+}
+
+treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
+ color: HighlightText;
+}
diff --git a/comm/suite/themes/classic/communicator/places/bookmarksMenu.png b/comm/suite/themes/classic/communicator/places/bookmarksMenu.png
new file mode 100644
index 0000000000..b76d849b91
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmarksMenu.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/bookmarksToolbar.css b/comm/suite/themes/classic/communicator/places/bookmarksToolbar.css
new file mode 100644
index 0000000000..68c270b31b
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmarksToolbar.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/* ===== bookmarksToolbar.css ===========================================
+ == Styles specific to bookmark items in a toolbar.
+ ======================================================================= */
+
+@import url("chrome://communicator/skin/places/bookmarks.css");
+
+/* ::::: bookmark toolbar buttons ::::: */
+
+/* ..... bookmark items ..... */
+
+toolbarbutton.bookmark-item {
+ min-width: 0px;
+ max-width: 13em;
+}
+
+toolbarbutton.bookmark-item:not([container="true"]) {
+ -moz-appearance: none;
+}
+
+toolbarbutton.bookmark-item:not([container="true"]):not([disabled="true"]) {
+ cursor: pointer;
+}
+
+toolbarbutton.bookmark-item:not([container="true"]):not([disabled="true"]):hover {
+ text-decoration: underline;
+ color: #0000FF;
+}
+
+toolbarbutton.bookmark-item[disabled="true"],
+toolbarbutton.bookmark-item:not([container="true"]):hover,
+toolbarbutton.bookmark-item:not([container="true"]):hover:active {
+ border-top-color: transparent !important;
+ border-right-color: transparent !important;
+ border-bottom-color: transparent !important;
+ border-left-color: transparent !important;
+ background: transparent !important;
+}
+
+toolbarbutton.bookmark-item[disabled="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png") !important;
+}
+
+toolbarbutton.bookmark-item[disabled="true"][container="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-dis.png") !important;
+}
+
+.bookmark-item > .toolbarbutton-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+/* ..... drag and drop styles ..... */
+
+#PlacesToolbarDropIndicator {
+ list-style-image: url("chrome://communicator/skin/places/toolbarDropMarker.png");
+}
+
+toolbarbutton.bookmark-item[dragover="true"][open="true"] {
+ background: Highlight !important;
+ color: HighlightText !important;
+}
+
+/* ::::: bookmark menus ::::: */
+
+menu.bookmark-item,
+menuitem.bookmark-item {
+ border-top: 1px solid transparent !important;
+ border-bottom: 1px solid transparent !important;
+}
+
+.bookmark-item > .menu-iconic-left > .menu-iconic-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.menuitem-iconic.bookmark-item[disabled="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png");
+}
+
+.menu-iconic.bookmark-item[disabled="true"][container="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-dis.png");
+}
+
+/* ..... drag and drop styles ..... */
+
+/* rules for menupopup drop indicators */
+.menupopup-drop-indicator-bar {
+ position: relative;
+ /* these two margins must together compensate the indicator's height */
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+.menupopup-drop-indicator {
+ list-style-image: none;
+ height: 2px;
+ margin-inline-end: -4em;
+ background-color: Highlight;
+}
diff --git a/comm/suite/themes/classic/communicator/places/bookmarksToolbar.png b/comm/suite/themes/classic/communicator/places/bookmarksToolbar.png
new file mode 100644
index 0000000000..1e36e1177e
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/bookmarksToolbar.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/calendar.png b/comm/suite/themes/classic/communicator/places/calendar.png
new file mode 100644
index 0000000000..666f6295f1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/calendar.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/editBookmarkOverlay.css b/comm/suite/themes/classic/communicator/places/editBookmarkOverlay.css
new file mode 100644
index 0000000000..93bb5176c7
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/editBookmarkOverlay.css
@@ -0,0 +1,84 @@
+/* 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/. */
+
+/**** folder menulist ****/
+.folder-icon > .menulist-label-box > .menulist-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.folder-icon > .menu-iconic-left {
+ display: -moz-box;
+}
+
+.folder-icon {
+ list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
+ -moz-image-region: rect(0px, 32px, 16px, 16px) !important;
+}
+
+
+/**** expanders ****/
+
+.expander-up,
+.expander-down {
+ min-width: 0;
+ padding: 2px 0;
+ padding-inline-start: 2px;
+}
+
+.expander-up {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.expander-down {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.expander-down:hover:active {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-hov.gif");
+}
+
+.expander-up:hover:active {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-hov.gif");
+}
+
+#editBookmarkPanelContent {
+ min-width: 23em;
+ width: 32em;
+}
+
+#editBMPanel_folderTree {
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+/* Hide the value column of the tag autocomplete popup
+ * leaving only the comment column visible. This is
+ * so that only the tag being edited is shown in the
+ * popup.
+ */
+#editBMPanel_tagsField #treecolAutoCompleteValue {
+ visibility: collapse;
+}
+
+
+/* ::::: bookmark panel dropdown icons ::::: */
+
+#editBMPanel_folderMenuList[selectedIndex="0"],
+#editBMPanel_toolbarFolderItem {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png") !important;
+ -moz-image-region: auto !important;
+}
+
+#editBMPanel_folderMenuList[selectedIndex="1"],
+#editBMPanel_bmRootItem {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksMenu.png") !important;
+ -moz-image-region: auto !important;
+}
+
+#editBMPanel_folderMenuList[selectedIndex="2"],
+#editBMPanel_unfiledRootItem {
+ list-style-image: url("chrome://communicator/skin/places/unsortedBookmarks.png") !important;
+ -moz-image-region: auto !important;
+}
diff --git a/comm/suite/themes/classic/communicator/places/history.png b/comm/suite/themes/classic/communicator/places/history.png
new file mode 100644
index 0000000000..fcc89bbbf0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/history.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/livemark-folder.png b/comm/suite/themes/classic/communicator/places/livemark-folder.png
new file mode 100644
index 0000000000..3da846aff9
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/livemark-folder.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/organizer.css b/comm/suite/themes/classic/communicator/places/organizer.css
new file mode 100644
index 0000000000..2636ff5bdf
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/organizer.css
@@ -0,0 +1,17 @@
+/* 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/. */
+
+/* Info box */
+#detailsDeck {
+ margin: 5px;
+}
+
+#infoBoxExpanderLabel {
+ padding-inline-start: 2px;
+}
+
+#organizerScopeBar {
+ padding: 2px 0;
+ padding-inline-end: 3px;
+}
diff --git a/comm/suite/themes/classic/communicator/places/query.png b/comm/suite/themes/classic/communicator/places/query.png
new file mode 100644
index 0000000000..75a84a0b33
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/query.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/tag.png b/comm/suite/themes/classic/communicator/places/tag.png
new file mode 100644
index 0000000000..4c876b92b0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/tag.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/toolbarDropMarker.png b/comm/suite/themes/classic/communicator/places/toolbarDropMarker.png
new file mode 100644
index 0000000000..a57b575bb2
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/toolbarDropMarker.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/places/unsortedBookmarks.png b/comm/suite/themes/classic/communicator/places/unsortedBookmarks.png
new file mode 100644
index 0000000000..3a87625eb7
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/places/unsortedBookmarks.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/preferences.css b/comm/suite/themes/classic/communicator/preferences.css
new file mode 100644
index 0000000000..58d940e270
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/preferences.css
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Styles used by all preference windows and panes of SeaMonkey */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Main Window ::::: */
+
+prefwindow {
+ -moz-appearance: window;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+ font: message-box;
+ padding-top: 8px;
+ padding-bottom: 0px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+prefpane {
+ padding-top: 8px;
+ padding-bottom: 10px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+prefwindow[type="child"] {
+ padding-top: 8px;
+ padding-bottom: 10px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+prefwindow[type="child"] > prefpane {
+ padding: 0px;
+}
+
+.prefWindow-dlgbuttons {
+ padding-bottom: 10px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+prefwindow[type="child"] .prefWindow-dlgbuttons {
+ padding: 0px;
+}
+
+radio[pane] {
+ -moz-appearance: none;
+ margin: 0px 1px 0px 1px;
+ padding: 1px 3px 1px 3px;
+ min-width: 4.5em;
+}
+
+.paneSelector {
+ border-bottom: 2px groove ThreeDFace;
+ margin: 0px;
+ padding-inline-start: 10px;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.paneButtonIcon {
+ width: 32px;
+ height: 32px;
+}
+
+radio[pane]:hover {
+ background-color: #E0E8F6;
+ color: black;
+ -moz-appearance: none;
+}
+
+radio[pane][selected="true"] {
+ background-color: #C1D2EE;
+ color: black;
+ -moz-appearance: none;
+}
diff --git a/comm/suite/themes/classic/communicator/prefpanels.css b/comm/suite/themes/classic/communicator/prefpanels.css
new file mode 100644
index 0000000000..06b97ebd56
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/prefpanels.css
@@ -0,0 +1,84 @@
+/* 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/. */
+
+/* ==== prefpanels.css ==================================================
+ == Styles used by all preference panels in the Communicator suite.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Fonts ::::: */
+
+#sizeVar,
+#sizeMono {
+ width: 4em;
+}
+
+.prefpanel-font-list {
+ -moz-box-flex: 1;
+}
+
+/* checkbox which is disabled for changes but non-gray text (i.e., in effect) */
+
+checkbox.nogray-disabled[disabled="true"][nogray="true"] {
+ color: inherit;
+ text-shadow: inherit;
+}
+
+/* ::::: Applications ::::: */
+.handler-action > .menu-iconic-left {
+ /**
+ * 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.
+ */
+ display: -moz-box;
+ min-width: 16px;
+}
+
+/* Set icons on app pane elements */
+.handler-action .listcell-icon,
+.handler-type .listcell-icon {
+ height: 16px;
+ width: 16px;
+}
+
+.handler-action[appHandlerIcon="app"] {
+ list-style-image: url("chrome://communicator/skin/icons/application.png");
+}
+
+.handler-action[appHandlerIcon="ask"] {
+ list-style-image: url("chrome://communicator/skin/icons/alwaysAsk.png");
+}
+
+.handler-action[appHandlerIcon="save"] {
+ list-style-image: url("chrome://communicator/skin/icons/save.png");
+}
+
+.handler-action[appHandlerIcon="feed"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+.handler-type[typeClass="unknown"] {
+ list-style-image: url("moz-icon://goat?size=16");
+}
+
+.handler-type[typeClass="webFeed"],
+.handler-type[typeClass="videoPodcastFeed"],
+.handler-type[typeClass="audioPodcastFeed"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+/* ::::: Search ::::: */
+
+#engineList > .menulist-label-box > .menulist-icon {
+ height: 16px;
+ width: 16px;
+}
+
+/* ::::: Sync ::::: */
+
+#syncDesc {
+ padding: 0 12em;
+}
diff --git a/comm/suite/themes/classic/communicator/profile/migrate.png b/comm/suite/themes/classic/communicator/profile/migrate.png
new file mode 100644
index 0000000000..dc1405dc47
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/profile/migrate.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/profile/profile.css b/comm/suite/themes/classic/communicator/profile/profile.css
new file mode 100644
index 0000000000..df6c1e06bb
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/profile/profile.css
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@import url("chrome://global/skin/global.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+treechildren::-moz-tree-image {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://communicator/skin/profile/profileicon-large.png");
+}
+
+treechildren::-moz-tree-image(rowMigrate-no) {
+ list-style-image: url("chrome://communicator/skin/profile/migrate.png");
+}
+
+/* profile selection dialog */
+
+/* Override global.css */
+hbox.wizard-box {
+ padding: 10px 10px 10px 10px;
+}
+
+#header {
+ -moz-box-orient: vertical;
+ margin-top: -8px;
+ margin-bottom: 0;
+ margin-inline-start: -8px;
+ margin-inline-end: -10px;
+ border-left: none;
+ border-right: none;
+ border-top: none;
+ border-bottom-color: ThreeDShadow;
+ padding-top: 12px;
+ padding-bottom: 12px;
+ padding-inline-start: 25px;
+ padding-inline-end: 5px;
+ background-color: Window;
+ color: WindowText;
+}
+
+#header > .dialogheader-title {
+ font: inherit;
+ font-weight: bold;
+}
+
+#header > .dialogheader-description {
+ margin-inline-start: 12px !important;
+}
+
+#intro,
+#label {
+ width: 17em;
+}
+
+#managebuttons > button {
+ min-width: 8em;
+}
diff --git a/comm/suite/themes/classic/communicator/profile/profileManager.css b/comm/suite/themes/classic/communicator/profile/profileManager.css
new file mode 100644
index 0000000000..4fd88568a3
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/profile/profileManager.css
@@ -0,0 +1,18 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#dialoginfo {
+ width: 36em;
+}
+
+#table-housing {
+ background-color: white;
+ height: 100%;
+}
+
+#buttons-box {
+ margin-inline-start: 1em;
+}
diff --git a/comm/suite/themes/classic/communicator/profile/profileicon-large.png b/comm/suite/themes/classic/communicator/profile/profileicon-large.png
new file mode 100644
index 0000000000..e11eabd3ef
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/profile/profileicon-large.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/search/engineManager.css b/comm/suite/themes/classic/communicator/search/engineManager.css
new file mode 100644
index 0000000000..53cf96f9d1
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/search/engineManager.css
@@ -0,0 +1,14 @@
+/* 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/. */
+
+#engineChildren::-moz-tree-image(engineName) {
+ margin-inline-end: 4px;
+ margin-inline-start: 1px;
+ width: 16px;
+ height: 16px;
+}
+
+#engineChildren::-moz-tree-row {
+ height: 20px;
+}
diff --git a/comm/suite/themes/classic/communicator/search/mainwindow-dropdown-arrow.png b/comm/suite/themes/classic/communicator/search/mainwindow-dropdown-arrow.png
new file mode 100644
index 0000000000..c3c82fda0e
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/search/mainwindow-dropdown-arrow.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/search/search-glass.png b/comm/suite/themes/classic/communicator/search/search-glass.png
new file mode 100644
index 0000000000..16a75cf297
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/search/search-glass.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/search/search.css b/comm/suite/themes/classic/communicator/search/search.css
new file mode 100644
index 0000000000..93e0d6592d
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/search/search.css
@@ -0,0 +1,18 @@
+/* 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/. */
+
+/* ===== search.css =====================================================
+ == Styles specific to the Search sidebar panel.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.autocomplete-treebody::-moz-tree-cell(datalist-first) {
+ border-top: 1px solid GrayText;
+}
+
+#sidebar-search-engines > .menulist-label-box > .menulist-icon {
+ height: 16px;
+ width: 16px;
+}
diff --git a/comm/suite/themes/classic/communicator/search/searchbar.css b/comm/suite/themes/classic/communicator/search/searchbar.css
new file mode 100644
index 0000000000..8f30bd4cd0
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/search/searchbar.css
@@ -0,0 +1,74 @@
+/* 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/. */
+
+.searchbar-textbox {
+ width: 6em;
+ min-width: 6em;
+}
+
+/* ::::: search bar ::::: */
+
+.autocomplete-treebody::-moz-tree-cell-text(datalist-first, treecolAutoCompleteComment) {
+ color: GrayText;
+ font-size: smaller;
+}
+
+.autocomplete-treebody::-moz-tree-cell(datalist-first) {
+ border-top: 1px solid GrayText;
+}
+
+/* ::::: searchbar-engine-button ::::: */
+
+.toolbarbutton-icon {
+ margin: 3px;
+ height: 16px;
+ width: 16px;
+ list-style-image: url("chrome://global/skin/icons/folder-item.png");
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.toolbarbutton-menu-dropmarker {
+ -moz-appearance: none !important;
+ list-style-image: url("chrome://communicator/skin/search/mainwindow-dropdown-arrow.png");
+ -moz-image-region: rect(0, 13px, 11px, 0);
+}
+
+.searchbar-engine-button[open="true"] > .toolbarbutton-menu-dropmarker {
+ -moz-image-region: rect(0, 26px, 11px, 13px);
+}
+
+
+/* ::::: search-go-button ::::: */
+
+.search-go-container {
+ -moz-box-align: center;
+}
+
+.search-go-button {
+ padding: 3px;
+ list-style-image: url("chrome://communicator/skin/search/search-glass.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+@media (-moz-touch-enabled) {
+ .search-go-button {
+ padding: 3px 5px;
+ }
+}
+
+.search-go-button:-moz-locale-dir(rtl) {
+ transform: scaleX(-1);
+}
+
+.search-go-button:hover {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+.search-go-button:hover:active {
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+.searchbar-engine-menuitem[selected="true"] > .menu-iconic-text {
+ font-weight: bold;
+}
diff --git a/comm/suite/themes/classic/communicator/sidebar/customize.css b/comm/suite/themes/classic/communicator/sidebar/customize.css
new file mode 100644
index 0000000000..732909bd56
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/customize.css
@@ -0,0 +1,39 @@
+/* -*- Mode: C; 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.treecell-panel
+{
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.treecell-panel[haslink="true"],
+treeitem[container="true"] > treerow > .treecell-panel
+{
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+treeitem[selected="true"] > treerow > .treecell-panel[haslink="true"],
+treeitem[selected="true"][container="true"] > treerow > .treecell-panel
+{
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed-sel.png");
+}
+
+treeitem[container="true"][open="true"] > treerow > .treecell-panel
+{
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open.png");
+}
+treeitem[selected="true"][container="true"][open="true"] > treerow > .treecell-panel
+{
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open-sel.png");
+}
+
+#header {
+ list-style-image: url("chrome://communicator/skin/sidebar/sidebar-icon.png");
+}
+
+box#reorder {
+ -moz-box-pack: center;
+}
diff --git a/comm/suite/themes/classic/communicator/sidebar/preview.css b/comm/suite/themes/classic/communicator/sidebar/preview.css
new file mode 100644
index 0000000000..48de5ea54e
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/preview.css
@@ -0,0 +1,21 @@
+/* -*- Mode: C; 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/*
+ Style rules specific to the Preview dialog.
+*/
+
+#previewframe {
+ min-width: 10px;
+ min-height: 10px;
+ width: 162px;
+ height: 300px;
+ border: none;
+ margin: 0px;
+}
+
+
diff --git a/comm/suite/themes/classic/communicator/sidebar/sbtab-twisty-open.png b/comm/suite/themes/classic/communicator/sidebar/sbtab-twisty-open.png
new file mode 100644
index 0000000000..649f538405
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/sbtab-twisty-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sidebar/sbtab-twisty.png b/comm/suite/themes/classic/communicator/sidebar/sbtab-twisty.png
new file mode 100644
index 0000000000..53d40ae69c
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/sbtab-twisty.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sidebar/sidebar.css b/comm/suite/themes/classic/communicator/sidebar/sidebar.css
new file mode 100644
index 0000000000..bf35d16013
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/sidebar.css
@@ -0,0 +1,173 @@
+/* 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/. */
+
+/* ===== sidebar.css ====================================================
+ == Styles used by the Sidebar panel and Sidebar tabs.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#sidebar-box {
+ border-left: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDHighlight;
+ background-color: AppWorkspace;
+}
+
+#sidebar-box:-moz-lwtheme {
+ background-color: transparent;
+}
+
+/* ::::: sidebar header ::::: */
+
+.sidebarheader-main {
+ -moz-appearance: toolbar;
+ border: 1px outset InactiveCaption;
+ background-color: InactiveCaption;
+ color: CaptionText;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 6px;
+ padding-inline-end: 2px;
+ height: auto;
+ overflow-x: hidden;
+}
+
+.sidebarheader-main:-moz-lwtheme {
+ -moz-appearance: none;
+ background-color: transparent;
+ color: inherit;
+}
+
+#sidebar-panel-picker:not(:-moz-lwtheme) {
+ /* would override hover/active styles, so let lwtheme do it in its own way */
+ color: inherit;
+}
+
+#sidebar-panel-picker > .toolbarbutton-dropmarker {
+ padding: 0 2px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+.sidebar-header-text {
+ font-weight: bold;
+}
+
+/* ..... close button ..... */
+
+#sidebar-close-button {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 3px;
+ list-style-image: url("chrome://communicator/skin/icons/close-button.png");
+}
+
+/* ::::: sidebar panel ::::: */
+
+.sidebar-iframe-no-panels,
+.loadarea {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.iframe-panel, .browser-sidebar {
+ border-left: 1px solid ThreeDHighlight;
+ border-bottom: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDShadow;
+}
+
+/* ::::: loading info ::::: */
+
+.text-panel-loading {
+ margin: 5px 0px;
+}
+
+.text-panel-loading[loading="false"] {
+ margin-inline-start: 11px;
+}
+
+.image-panel-loading {
+ margin: 5px;
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+/* ::::: sidebar tabs ::::: */
+
+.box-texttab {
+ -moz-binding: url("chrome://communicator/skin/sidebar/sidebarBindings.xml#sbtab");
+ -moz-box-align: center;
+ cursor: pointer;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.box-texttab:-moz-lwtheme {
+ text-shadow: none;
+}
+
+.box-texttab,
+.box-texttab[selected="true"],
+.box-texttab[selected="true"]:hover,
+.box-texttab[selected="true"]:hover:active {
+ border: 1px solid;
+ border-top-color: ThreeDLightShadow;
+ border-right-color: ThreeDShadow;
+ border-bottom-color: ThreeDShadow;
+ border-left-color: ThreeDLightShadow;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-inline-start: 0px;
+ padding-inline-end: 1px;
+}
+
+.box-texttab:hover {
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+}
+
+.box-texttab:hover:active {
+ border-top-color: ThreeDShadow;
+ border-right-color: ThreeDShadow;
+ border-bottom-color: ThreeDShadow;
+ border-left-color: ThreeDShadow;
+}
+
+.sbtab-label {
+ margin: 0px !important;
+}
+
+.sbtab-twisty {
+ margin: 0px 7px;
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-twisty.png");
+}
+
+.sbtab-texture {
+ margin: 2px 0px;
+ background: url("chrome://communicator/skin/toolbar/tbgrip-texture.png") repeat-y;
+ width: 12px;
+ height: 12px;
+}
+
+.box-texttab[selected="true"] {
+ font-weight: bold;
+ cursor: default;
+}
+
+.sbtab-twisty[selected="true"] {
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-twisty-open.png");
+}
+
+/* ::::: sidebar navigation buttons ::::: */
+
+.sidebar-nav-button {
+ padding: 5px 0px;
+}
+
+.tab-fwd {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.tab-back {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
diff --git a/comm/suite/themes/classic/communicator/sidebar/sidebarBindings.xml b/comm/suite/themes/classic/communicator/sidebar/sidebarBindings.xml
new file mode 100644
index 0000000000..ec291c9944
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/sidebarBindings.xml
@@ -0,0 +1,21 @@
+<?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/. -->
+
+
+<bindings id="sidebarBindings"
+ 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="sbtab" extends="xul:button">
+ <content>
+ <xul:image class="sbtab-twisty" xbl:inherits="selected"/>
+ <xul:label class="sbtab-label" xbl:inherits="value=label,accesskey" crop="right"/>
+ <xul:spacer flex="1"/>
+ <xul:image class="sbtab-texture"/>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/themes/classic/communicator/sidebar/sidebarListView.css b/comm/suite/themes/classic/communicator/sidebar/sidebarListView.css
new file mode 100644
index 0000000000..39f974cbe2
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sidebar/sidebarListView.css
@@ -0,0 +1,17 @@
+/* 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/. */
+
+treechildren::-moz-tree-image(leaf),
+treechildren::-moz-tree-cell(leaf) {
+ cursor: pointer;
+}
+
+treechildren::-moz-tree-cell-text(leaf, hover) {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+treechildren::-moz-tree-cell(separator) {
+ cursor: default;
+}
diff --git a/comm/suite/themes/classic/communicator/smileys.css b/comm/suite/themes/classic/communicator/smileys.css
new file mode 100644
index 0000000000..1516b7b9de
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/smileys.css
@@ -0,0 +1,132 @@
+/* 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/. */
+
+/* ==== smileys.css ====================================================
+ == Style rules to bind smiley image types generated by editor and the mozTxtToHTMLConv
+ == into skinnable images.
+ ========================================================================== */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+
+/* ::::: we also represent smilies inside of spans ::::: */
+
+.moz-smiley-s1,
+.moz-smiley-s2,
+.moz-smiley-s3,
+.moz-smiley-s4,
+.moz-smiley-s5,
+.moz-smiley-s6,
+.moz-smiley-s7,
+.moz-smiley-s8,
+.moz-smiley-s9,
+.moz-smiley-s10,
+.moz-smiley-s11,
+.moz-smiley-s12,
+.moz-smiley-s13,
+.moz-smiley-s14,
+.moz-smiley-s15,
+.moz-smiley-s16 {
+ display: inline-block;
+ vertical-align: middle;
+ -moz-user-select: all;
+}
+
+.moz-smiley-s1 > span,
+.moz-smiley-s2 > span,
+.moz-smiley-s3 > span,
+.moz-smiley-s4 > span,
+.moz-smiley-s5 > span,
+.moz-smiley-s6 > span,
+.moz-smiley-s7 > span,
+.moz-smiley-s8 > span,
+.moz-smiley-s9 > span,
+.moz-smiley-s10 > span,
+.moz-smiley-s11 > span,
+.moz-smiley-s12 > span,
+.moz-smiley-s13 > span,
+.moz-smiley-s14 > span,
+.moz-smiley-s15 > span,
+.moz-smiley-s16 > span {
+ display: none;
+}
+
+/* smile */
+.moz-smiley-s1:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-smile.png");
+}
+
+/* frown */
+.moz-smiley-s2:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-frown.png");
+}
+
+/* wink */
+.moz-smiley-s3:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-wink.png");
+}
+
+/* tongue */
+.moz-smiley-s4:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-tongue.png");
+}
+
+/* laughing */
+.moz-smiley-s5:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-laughing.png");
+}
+
+/* embarrassed */
+.moz-smiley-s6:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-embarrassed.png");
+}
+
+/* undecided */
+.moz-smiley-s7:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-undecided.png");
+}
+
+/* surprise */
+.moz-smiley-s8:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-surprise.png");
+}
+
+/* kiss */
+.moz-smiley-s9:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-kiss.png");
+}
+
+/* yell */
+.moz-smiley-s10:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-yell.png");
+}
+
+/* cool */
+.moz-smiley-s11:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-cool.png");
+}
+
+/* money */
+.moz-smiley-s12:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-money.png");
+}
+
+/* foot */
+.moz-smiley-s13:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-foot.png");
+}
+
+/* innocent */
+.moz-smiley-s14:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-innocent.png");
+}
+
+/* cry */
+.moz-smiley-s15:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-cry.png");
+}
+
+/* sealed */
+.moz-smiley-s16:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-sealed.png");
+}
diff --git a/comm/suite/themes/classic/communicator/spinbuttons.css b/comm/suite/themes/classic/communicator/spinbuttons.css
new file mode 100644
index 0000000000..3e333bb474
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/spinbuttons.css
@@ -0,0 +1,24 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+spinbuttons {
+ -moz-appearance: spinner;
+ cursor: default;
+}
+
+.spinbuttons-button {
+ min-width: 13px;
+ min-height: 11px;
+ margin: 0 !important;
+}
+
+.spinbuttons-up {
+ -moz-appearance: spinner-upbutton;
+}
+
+.spinbuttons-down {
+ -moz-appearance: spinner-downbutton;
+}
diff --git a/comm/suite/themes/classic/communicator/sync/sync-16-throbber.png b/comm/suite/themes/classic/communicator/sync/sync-16-throbber.png
new file mode 100644
index 0000000000..d604a32d75
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-16-throbber.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/sync-16.png b/comm/suite/themes/classic/communicator/sync/sync-16.png
new file mode 100644
index 0000000000..c7839c33e9
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/sync-32-throbber.png b/comm/suite/themes/classic/communicator/sync/sync-32-throbber.png
new file mode 100644
index 0000000000..4c8d903544
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-32-throbber.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/sync-32.png b/comm/suite/themes/classic/communicator/sync/sync-32.png
new file mode 100644
index 0000000000..d0d3b19050
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-32.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/sync-bg.png b/comm/suite/themes/classic/communicator/sync/sync-bg.png
new file mode 100644
index 0000000000..893a27d76e
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-bg.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/sync-desktopIcon.png b/comm/suite/themes/classic/communicator/sync/sync-desktopIcon.png
new file mode 100644
index 0000000000..a98f21b30f
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-desktopIcon.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/sync-mobileIcon.png b/comm/suite/themes/classic/communicator/sync/sync-mobileIcon.png
new file mode 100644
index 0000000000..57fbe054c8
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/sync-mobileIcon.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/sync/syncCommon.css b/comm/suite/themes/classic/communicator/sync/syncCommon.css
new file mode 100644
index 0000000000..a5383bc330
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/syncCommon.css
@@ -0,0 +1,45 @@
+/* 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/. */
+
+/* The following are used by both syncSetup.xul and syncGenericChange.xul */
+.status {
+ color: -moz-dialogtext;
+}
+
+.statusIcon {
+ margin-inline-start: 4px;
+ max-height: 16px;
+ max-width: 16px;
+}
+
+.statusIcon[status="active"] {
+ list-style-image: url("chrome://global/skin/icons/loading.png");
+}
+
+.statusIcon[status="error"] {
+ list-style-image: url("chrome://global/skin/icons/error-16.png");
+}
+
+.statusIcon[status="success"] {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+}
+
+/* .data is only used by syncGenericChange.xul, but it seems unnecessary to have
+ a separate stylesheet for it. */
+.data {
+ font-size: 90%;
+ font-weight: bold;
+}
+
+#change-dialog {
+ width: 40em;
+}
+
+#introText {
+ margin-top: 2px;
+}
+
+#feedback {
+ height: 2em;
+}
diff --git a/comm/suite/themes/classic/communicator/sync/syncQuota.css b/comm/suite/themes/classic/communicator/sync/syncQuota.css
new file mode 100644
index 0000000000..1577de8a3b
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/syncQuota.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#quotaDialog {
+ width: 33em;
+ height: 25em;
+}
+
+treechildren::-moz-tree-checkbox {
+ list-style-image: none;
+}
+treechildren::-moz-tree-checkbox(checked) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+treechildren::-moz-tree-checkbox(disabled) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
+}
+
+#treeCaption {
+ height: 4em;
+}
+
+.captionWarning {
+ font-weight: bold;
+}
diff --git a/comm/suite/themes/classic/communicator/sync/syncSetup.css b/comm/suite/themes/classic/communicator/sync/syncSetup.css
new file mode 100644
index 0000000000..0112c94893
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/sync/syncSetup.css
@@ -0,0 +1,129 @@
+/* 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/. */
+
+wizard {
+ -moz-appearance: none;
+ width: 55em;
+ height: 42em;
+ padding: 0;
+ background-color: Window;
+}
+
+.wizard-page-box {
+ -moz-appearance: none;
+ padding-left: 0;
+ padding-right: 0;
+ margin: 0;
+}
+
+wizardpage {
+ -moz-box-pack: center;
+ -moz-box-align: center;
+ margin: 0;
+ padding: 0 8em;
+ background-color: Window;
+}
+
+.wizard-header {
+ -moz-appearance: none;
+ border: none;
+ padding: 2em 0 1em 0;
+ text-align: center;
+}
+.wizard-header-label {
+ font-size: 24pt;
+ font-weight: normal;
+}
+
+.wizard-buttons {
+ border-top: 2px solid #ccd9ea;
+ background-color: #f1f5fb;
+ padding: 1em;
+}
+
+.wizard-buttons-separator {
+ visibility: collapse;
+}
+
+.wizard-header-icon {
+ visibility: collapse;
+}
+
+.accountChoiceButton {
+ font: menu;
+}
+
+.confirm {
+ border: 1px solid black;
+ padding: 1em;
+ border-radius: 5px;
+}
+
+/* Override the text-link style from global.css */
+description > .text-link,
+description > .text-link:focus {
+ margin: 0px;
+ padding: 0px;
+}
+
+.success,
+.error {
+ padding: 2px;
+ border-radius: 2px;
+}
+
+.error {
+ background-color: #FF0000 !important;
+ color: #FFFFFF !important;
+}
+
+.success {
+ background-color: #00FF00 !important;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 100%;
+ color: red;
+}
+
+.mainDesc {
+ font-weight: bold;
+ font-size: 100%;
+}
+
+.normal {
+ font-size: 100%;
+}
+
+.inputColumn {
+ margin-inline-end: 2px;
+}
+
+.recommended {
+ font-weight: bold;
+}
+
+.pin {
+ font-size: 18pt;
+ text-align: center;
+}
+
+#passphraseHelpSpacer {
+ width: 0.5em;
+}
+
+#add-device-throbber,
+#login-throbber {
+ list-style-image: url("chrome://global/skin/icons/loading.png");
+}
+
+#successPageIcon {
+ /* TODO replace this with a 128px version (bug 591122) */
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#pickSetupDesc {
+ padding: 0 7em;
+}
diff --git a/comm/suite/themes/classic/communicator/taskbar/taskbar.png b/comm/suite/themes/classic/communicator/taskbar/taskbar.png
new file mode 100755
index 0000000000..5d54e0c3d4
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/taskbar/taskbar.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/taskbar/taskmenu-abook.png b/comm/suite/themes/classic/communicator/taskbar/taskmenu-abook.png
new file mode 100644
index 0000000000..2e7d096cc6
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/taskbar/taskmenu-abook.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/taskbar/taskmenu-browser.png b/comm/suite/themes/classic/communicator/taskbar/taskmenu-browser.png
new file mode 100644
index 0000000000..14b1f3a70b
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/taskbar/taskmenu-browser.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/taskbar/taskmenu-composer.png b/comm/suite/themes/classic/communicator/taskbar/taskmenu-composer.png
new file mode 100644
index 0000000000..d3e98a3c96
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/taskbar/taskmenu-composer.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/taskbar/taskmenu-mailnews.png b/comm/suite/themes/classic/communicator/taskbar/taskmenu-mailnews.png
new file mode 100644
index 0000000000..9347ca2551
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/taskbar/taskmenu-mailnews.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/tasksOverlay.css b/comm/suite/themes/classic/communicator/tasksOverlay.css
new file mode 100644
index 0000000000..1500b1029f
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/tasksOverlay.css
@@ -0,0 +1,98 @@
+/* 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/. */
+
+/* ==== tasksOverlay.css ================================================
+ == Styles used by the the overlay which contains tasks related
+ == content, such as the taskbar and product launcher icons.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: communicator product icons ::::: */
+
+#mini-nav {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 17px 13px 0);
+}
+
+#mini-nav:hover {
+ -moz-image-region: rect(13px 17px 26px 0);
+}
+
+#mini-nav:hover:active {
+ -moz-image-region: rect(26px 17px 39px 0);
+}
+
+.icon-navigator16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-browser.png");
+}
+
+#mini-mail {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 40px 13px 17px);
+}
+
+#mini-mail:hover {
+ -moz-image-region: rect(13px 40px 26px 17px);
+}
+
+#mini-mail:hover:active {
+ -moz-image-region: rect(26px 40px 39px 17px);
+}
+
+#mini-mail[BiffState="NewMail"] {
+ -moz-image-region: rect(0 63px 13px 40px);
+}
+
+#mini-mail[BiffState="NewMail"]:hover {
+ -moz-image-region: rect(13px 63px 26px 40px);
+}
+
+#mini-mail[BiffState="NewMail"]:hover:active {
+ -moz-image-region: rect(26px 63px 39px 40px);
+}
+
+.icon-mail16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-mailnews.png");
+}
+
+#mini-addr {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 100px 13px 81px);
+}
+
+#mini-addr:hover {
+ -moz-image-region: rect(13px 100px 26px 81px);
+}
+
+#mini-addr:hover:active {
+ -moz-image-region: rect(26px 100px 39px 81px);
+}
+
+.icon-addressbook16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-abook.png");
+}
+
+#mini-comp {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 81px 13px 63px);
+}
+
+#mini-comp:hover {
+ -moz-image-region: rect(13px 81px 26px 63px);
+}
+
+#mini-comp:hover:active {
+ -moz-image-region: rect(26px 81px 39px 63px);
+}
+
+.icon-composer16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-composer.png");
+}
+
+/* ::::: component bar ::::: */
+
+#component-bar {
+ -moz-box-align: stretch;
+}
diff --git a/comm/suite/themes/classic/communicator/toolbar.css b/comm/suite/themes/classic/communicator/toolbar.css
new file mode 100644
index 0000000000..5d13481165
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/toolbar.css
@@ -0,0 +1,118 @@
+/* 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/. */
+
+/* ===== toolbar.css ====================================================
+ == Styles used by XUL grippytoolbar in addition to general toolbar styles.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Restore Windows Classic fallbacks removed by bug 1343196 */
+
+@media (-moz-windows-classic) {
+
+ /* ::::: toolbox ::::: */
+
+ toolbox {
+ background-color: -moz-Dialog;
+ border-top: 1px solid;
+ border-top-color: ThreeDShadow;
+ }
+
+ /* ::::: toolbar & menubar ::::: */
+
+ toolbar {
+ border-top: 1px solid ThreeDHighlight;
+ border-bottom: 1px solid ThreeDShadow;
+ }
+
+ toolbar:first-child, menubar {
+ border-bottom: 1px solid ThreeDShadow;
+ border-top: 0px !important;
+ }
+
+/* ::::: lightweight theme ::::: */
+
+ menubar:-moz-lwtheme,
+ toolbox:-moz-lwtheme,
+ toolbar:-moz-lwtheme {
+ background: none;
+ border-color: transparent;
+ }
+
+ /* ::::: toolbar decorations ::::: */
+
+ toolbarseparator {
+ padding: 2px;
+ border-left: 1px solid;
+ border-right: 1px solid;
+ border-left-color : ThreeDShadow;
+ border-right-color : ThreeDHighlight;
+ }
+}
+
+/* End restore Windows Classic fallbacks removed by bug 1343196 */
+
+@media (-moz-menubar-drag) {
+ toolbar[type="menubar"]:not([autohide="true"]):not([xpfe="false"]):not(:-moz-lwtheme) {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-drag");
+ }
+}
+
+/* ::::: toolbar & menubar ::::: */
+
+.toolbar-holder {
+ border-left: 1px solid ThreeDHighlight;
+}
+
+toolbaritem > menubar,
+toolbar > menubar {
+ border-bottom: 0px none;
+}
+
+/* ::::: toolbargrippy ::::: */
+
+toolbargrippy {
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+ border-right: 1px solid ThreeDShadow;
+ width: 10px;
+ padding: 2px 1px;
+ list-style-image: url("chrome://communicator/skin/toolbar/tbgrip-arrow.png");
+}
+
+toolbargrippy:hover {
+ background-color: ThreeDHighlight;
+}
+
+.toolbargrippy-texture {
+ margin-top: 2px;
+ width: 6px;
+ background: url("chrome://communicator/skin/toolbar/tbgrip-texture.png");
+}
+
+/* ::::: collapsed tray and grippies ::::: */
+
+toolbargrippy[tbgrippy-collapsed="true"] {
+ -moz-box-orient: horizontal;
+ border-left: 1px solid ThreeDHighlight;
+ border-top: 1px solid ThreeDHighlight;
+ border-right: 1px solid ThreeDShadow;
+ border-bottom: 1px solid ThreeDShadow;
+ width: 40px;
+ height: 10px;
+ padding: 1px 2px;
+ list-style-image: url("chrome://communicator/skin/toolbar/tbgrip-arrow-clps.png");
+}
+
+toolbargrippy[tbgrippy-collapsed="true"] > .toolbargrippy-texture {
+ margin-top: 0;
+ margin-inline-start: 2px;
+ width: 0px;
+ height: 6px;
+}
+
+.collapsed-tray-spacer {
+ border-bottom: 1px solid ThreeDShadow;
+}
diff --git a/comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow-clps.png b/comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow-clps.png
new file mode 100644
index 0000000000..6342970127
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow-clps.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow.png b/comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow.png
new file mode 100644
index 0000000000..c97fd08365
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/toolbar/tbgrip-arrow.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/toolbar/tbgrip-texture.png b/comm/suite/themes/classic/communicator/toolbar/tbgrip-texture.png
new file mode 100644
index 0000000000..34d15fba48
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/toolbar/tbgrip-texture.png
Binary files differ
diff --git a/comm/suite/themes/classic/communicator/viewSourceOverlay.css b/comm/suite/themes/classic/communicator/viewSourceOverlay.css
new file mode 100644
index 0000000000..a21699672c
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/viewSourceOverlay.css
@@ -0,0 +1,18 @@
+/* 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/. */
+
+/* make findbar appear above content */
+#appcontent {
+ -moz-box-direction: reverse;
+}
+
+/* style findbar for being on top */
+#FindToolbar {
+ border-top-color: ThreeDHighlight;
+ border-top-width: 1px;
+ border-bottom: 1px solid;
+ border-bottom-color: ThreeDShadow;
+ padding-top: 1px;
+ padding-bottom: 0px;
+}
diff --git a/comm/suite/themes/classic/communicator/xpinstall/xpinstall.css b/comm/suite/themes/classic/communicator/xpinstall/xpinstall.css
new file mode 100644
index 0000000000..863a6a68b7
--- /dev/null
+++ b/comm/suite/themes/classic/communicator/xpinstall/xpinstall.css
@@ -0,0 +1,32 @@
+/* */
+/* 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/. */
+
+@import url("chrome://communicator/skin");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#confirmBox {
+ margin: 1ex;
+}
+
+#urlColumn {
+ margin-inline-start: 1ex;
+}
+
+.confirmSeparator {
+ height: 1em;
+}
+
+.confirmName {
+ font-weight : bold;
+}
+
+.packageName {
+ font-weight : bold;
+}
+
+#alert-image {
+ margin: 1em;
+}
diff --git a/comm/suite/themes/classic/editor/EditorDialog.css b/comm/suite/themes/classic/editor/EditorDialog.css
new file mode 100644
index 0000000000..3b7d7f04af
--- /dev/null
+++ b/comm/suite/themes/classic/editor/EditorDialog.css
@@ -0,0 +1,267 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.MinWidth5em {
+ min-width: 5em;
+}
+
+.MinWidth10em {
+ min-width: 10em;
+}
+
+.MinWidth20em {
+ min-width: 20em;
+}
+
+.bold {
+ font-weight: bold;
+}
+
+.italic {
+ font-style: italic;
+}
+
+.larger {
+ font-size: 120%;
+}
+
+.narrow {
+ width: 3em;
+}
+
+.wrap {
+ width: 1em;
+}
+
+.menuitem-highlight-1 {
+ font-weight : bold;
+}
+
+.color-well {
+ width: 20px;
+ height: 12px;
+ border: 1px inset #CCCCCC;
+}
+
+.color-well[default="true"] {
+ border: 1px solid transparent !important;
+ background-color: inherit !important;
+}
+
+.color-button {
+ /* override large default min-width */
+ min-width : 0px;
+ margin: 2px;
+}
+
+#ColorPicker {
+ -moz-user-focus: normal;
+}
+
+#ColorPickerSwatch {
+ border : 2px outset #CCCCCC;
+ width: 25px;
+ height: 20px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 3px;
+}
+
+#ColorPickerSwatch[default="true"] {
+ border : 2px solid transparent;
+ background-color: inherit;
+}
+
+#LastPickedColor {
+ width: 17px;
+ height: 13px;
+ border: 1px inset #CCCCCC;
+ margin-top: 1px;
+ margin-bottom: 2px;
+ margin-inline-start: 4px;
+ margin-inline-end: 2px;
+}
+
+#ColorPreview {
+ border: 1px inset #CCCCCC;
+ margin-inline-start: 10px;
+ padding: 0 5px;
+ min-width: 100px;
+ min-height: 50px;
+}
+
+/* ::::: table properties dialog ::::: */
+
+#MoreFewerButton[more="0"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+ min-width: 12em;
+}
+
+#MoreFewerButton[more="1"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+ min-width: 12em;
+}
+
+#PreviousButton {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.gif");
+}
+
+#NextButton {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.gif");
+}
+
+#PreviousButton[type="row"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+#NextButton[type="row"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+/* ::::: spelling dialog ::::: */
+
+#ReplaceWordInput {
+ min-width: 16em;
+ width: 16em;
+}
+
+.spell-check {
+ min-width: 8em;
+}
+
+/* ::::: color picker ::::: */
+
+/* use outset shape for a button look */
+.colorpicker {
+ border: 1px outset #CCCCCC;
+ /* This should be the same as for textbox */
+ margin-inline-start: 4px;
+ /* For a little extra space between buttons */
+ margin-bottom: 2px;
+}
+
+
+.colorpicker:active {
+ border: 1px inset #CCCCCC;
+}
+
+.smallspacer {
+ width: 3px;
+ height: 3px;
+ min-width: 3px;
+ min-height: 3px;
+}
+
+.spacer {
+ width: 5px;
+ height: 5px;
+ min-width: 5px;
+ min-height: 5px;
+}
+
+.bigspacer {
+ width: 10px;
+ height: 10px;
+ min-width: 10px;
+ min-height: 10px;
+}
+
+/* These should be the width of the checkbox and radio button images + margin + padding
+ Used to indent below those to the level of the text label next to image
+*/
+.checkbox-spacer {
+ width: 2em;
+ min-width: 2em;
+}
+
+.radio-spacer {
+ width: 2em;
+ min-width: 2em;
+}
+
+.align-menu > .menu-iconic-left > .menu-iconic-icon {
+ height: auto;
+ width: auto;
+}
+
+.align-menu[value="top"] {
+ list-style-image:url("chrome://editor/skin/icons/img-align-top.png") !important;
+}
+
+.align-menu[value="middle"] {
+ list-style-image:url("chrome://editor/skin/icons/img-align-middle.png") !important;
+}
+
+.align-menu[value="bottom"] {
+ list-style-image:url("chrome://editor/skin/icons/img-align-bottom.png") !important;
+}
+
+.align-menu[value="right"] {
+ list-style-image:url("chrome://editor/skin/icons/img-align-right.png") !important;
+}
+
+.align-menu[value="left"] {
+ list-style-image:url("chrome://editor/skin/icons/img-align-left.png") !important;
+}
+
+/* Don't change width/height of these without changing values in
+ GetOriginalWidth(), EdImageProps.js
+*/
+#preview-image-box {
+ border: 1px inset #CCCCCC;
+ width : 82px;
+ max-width : 82px;
+ min-width : 82px;
+ height : 52px;
+ max-height : 52px;
+ min-height : 52px;
+ margin : 6px 5px;
+ overflow : -moz-hidden-unscrollable;
+}
+
+#preview-image-holder {
+ padding : 0px;
+ margin : 0px;
+}
+
+/* Rest are from Ben Goodger for Advanced Edit dialog
+ These need reviewing and eliminate all but essential attributes */
+/* Have made some mods and eliminated some unnecessary portions.
+ hope to merge most of the treecell formatting into global as some of
+ this "property list" style treecell formatting is used by cookie/signon
+ viewers. */
+
+
+#tagLabel {
+ font-weight: bold;
+}
+
+/* styles for an attribute tree-table */
+.AttributesTree {
+ min-width : 200px;
+ min-height: 200px;
+}
+
+/* ::::: select edit dialog ::::: */
+
+#SelectTreeChildren::-moz-tree-cell(SelectSelCol, checked-true) {
+ background: url("chrome://global/skin/checkbox/cbox-check.gif") 50% 50% no-repeat;
+}
+
+/* ::::: Publishing Progress ::::: */
+
+.progressitem[progress="busy"] {
+ list-style-image: url("chrome://editor/skin/icons/progress-busy.png");
+}
+
+.progressitem[progress="done"] {
+ list-style-image: url("chrome://editor/skin/icons/progress-done.png");
+}
+
+.progressitem[progress="failed"] {
+ list-style-image: url("chrome://editor/skin/icons/progress-failed.png");
+}
diff --git a/comm/suite/themes/classic/editor/editor.css b/comm/suite/themes/classic/editor/editor.css
new file mode 100644
index 0000000000..bee714c526
--- /dev/null
+++ b/comm/suite/themes/classic/editor/editor.css
@@ -0,0 +1,85 @@
+/* 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/. */
+
+/* ==== editor.css ==========================================================
+ == Styles shared throughout the Editor application.
+ ========================================================================== */
+
+@import url("chrome://communicator/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#content-frame {
+ min-width: 10px;
+ min-height: 10px;
+ height: 400px;
+}
+
+.source-editor,
+.source-editor:focus {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ margin-inline-start: 0px;
+ margin-inline-end: 5px;
+ border: 0px;
+ /* Scroll bars are in content,
+ so we can't use right and bottom padding! */
+ padding-top: 5px;
+ padding-bottom: 0px;
+ padding-inline-start: 5px;
+ padding-inline-end: 0px;
+}
+
+/* Multiline textarea for HTML source editing */
+#content-source,
+#doctype-text {
+ font-family: -moz-fixed;
+ font-size: initial;
+}
+
+#appcontent {
+ border-left: 1px solid ThreeDShadow;
+ border-top: 1px solid ThreeDDarkShadow;
+ border-right: 1px solid ThreeDHighlight;
+}
+
+#ContentWindowDeck {
+ border-left: 1px solid ThreeDDarkShadow;
+ border-right: 1px solid ThreeDLightShadow;
+}
+
+/* ::::: struct toolbar ::::: */
+
+#structToolbar {
+ min-width: 1px;
+ overflow: -moz-hidden-unscrollable;
+}
+
+#structSpacer {
+ margin: 2px 0px;
+}
+
+.struct-button {
+ padding: 2px;
+}
+
+.struct-button[checked="true"] {
+ font-weight: bold;
+}
+
+.struct-textbox {
+ -moz-appearance: none !important;
+ padding: 0px !important;
+ margin: 0px !important;
+ border: none !important;
+}
+
+/* ::::: lightweight themes ::::: */
+
+#EditModeToolbar:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
+
diff --git a/comm/suite/themes/classic/editor/editorFormatToolbar.css b/comm/suite/themes/classic/editor/editorFormatToolbar.css
new file mode 100644
index 0000000000..4445ab0fab
--- /dev/null
+++ b/comm/suite/themes/classic/editor/editorFormatToolbar.css
@@ -0,0 +1,574 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: formatting buttons ::::: */
+
+#FormatToolbar > toolbarbutton > .toolbarbutton-text {
+ display: none;
+}
+
+toolbarbutton.formatting-button {
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+ min-width: 32px;
+}
+
+#DecreaseFontSizeButton {
+ -moz-image-region: rect(64px 14px 80px 0px);
+}
+
+#DecreaseFontSizeButton:hover {
+ -moz-image-region: rect(64px 28px 80px 14px);
+}
+
+#DecreaseFontSizeButton:hover:active {
+ -moz-image-region: rect(64px 42px 80px 28px);
+}
+
+#DecreaseFontSizeButton[disabled="true"] {
+ -moz-image-region: rect(64px 56px 80px 42px) !important;
+}
+
+#IncreaseFontSizeButton {
+ -moz-image-region: rect(81px 18px 98px 0px);
+}
+
+#IncreaseFontSizeButton:hover {
+ -moz-image-region: rect(81px 36px 98px 18px);
+}
+
+#IncreaseFontSizeButton:hover:active {
+ -moz-image-region: rect(81px 54px 98px 36px);
+}
+
+#IncreaseFontSizeButton[disabled="true"] {
+ -moz-image-region: rect(81px 72px 98px 54px) !important;
+}
+
+#boldButton {
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#boldButton:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#boldButton:hover:active {
+ -moz-image-region: rect(16px 48px 32px 32px);
+}
+
+#boldButton[checked="true"] {
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#boldButton[disabled="true"] {
+ -moz-image-region: rect(16px 64px 32px 48px) !important;
+}
+
+#italicButton {
+ -moz-image-region: rect(98px 16px 114px 0px);
+}
+
+#italicButton:hover {
+ -moz-image-region: rect(98px 32px 114px 16px);
+}
+
+#italicButton:hover:active {
+ -moz-image-region: rect(98px 48px 114px 32px);
+}
+
+#italicButton[checked="true"] {
+ -moz-image-region: rect(98px 16px 114px 0px);
+}
+
+#italicButton[disabled="true"] {
+ -moz-image-region: rect(98px 64px 114px 48px) !important;
+}
+
+#underlineButton {
+ -moz-image-region: rect(114px 16px 130px 0px);
+}
+
+#underlineButton:hover {
+ -moz-image-region: rect(114px 32px 130px 16px);
+}
+
+#underlineButton:hover:active {
+ -moz-image-region: rect(114px 48px 130px 32px);
+}
+
+#underlineButton[checked="true"] {
+ -moz-image-region: rect(114px 16px 130px 0px);
+}
+
+#underlineButton[disabled="true"] {
+ -moz-image-region: rect(114px 64px 130px 48px) !important;
+}
+
+#ulButton {
+ -moz-image-region: rect(32px 16px 48px 0px);
+}
+
+#ulButton:hover {
+ -moz-image-region: rect(32px 32px 48px 16px);
+}
+
+#ulButton:hover:active {
+ -moz-image-region: rect(32px 48px 48px 32px);
+}
+
+#ulButton[checked="true"] {
+ -moz-image-region: rect(32px 16px 48px 0px);
+}
+
+#ulButton[disabled="true"] {
+ -moz-image-region: rect(32px 64px 48px 48px) !important;
+}
+
+#olButton {
+ -moz-image-region: rect(194px 16px 210px 0px);
+}
+
+#olButton:hover {
+ -moz-image-region: rect(194px 32px 210px 16px);
+}
+
+#olButton:hover:active {
+ -moz-image-region: rect(194px 48px 210px 32px);
+}
+
+#olButton[checked="true"] {
+ -moz-image-region: rect(194px 16px 210px 0px);
+}
+
+#olButton[disabled="true"] {
+ -moz-image-region: rect(194px 64px 210px 48px) !important;
+}
+
+#outdentButton {
+ -moz-image-region: rect(210px 16px 226px 0px);
+}
+
+#outdentButton:hover {
+ -moz-image-region: rect(210px 32px 226px 16px);
+}
+
+#outdentButton:hover:active {
+ -moz-image-region: rect(210px 48px 226px 32px);
+}
+
+#outdentButton[disabled="true"] {
+ -moz-image-region: rect(210px 64px 226px 48px) !important;
+}
+
+#indentButton {
+ -moz-image-region: rect(178px 16px 194px 0px);
+}
+
+#indentButton:hover {
+ -moz-image-region: rect(178px 32px 194px 16px);
+}
+
+#indentButton:hover:active {
+ -moz-image-region: rect(178px 48px 194px 32px);
+}
+
+#indentButton[disabled="true"] {
+ -moz-image-region: rect(178px 64px 194px 48px) !important;
+}
+
+#align-left-button {
+ -moz-image-region: rect(146px 16px 162px 0px);
+}
+
+#align-left-button:hover {
+ -moz-image-region: rect(146px 32px 162px 16px);
+}
+
+#align-left-button:hover:active {
+ -moz-image-region: rect(146px 48px 162px 32px);
+}
+
+#align-left-button[checked="true"] {
+ -moz-image-region: rect(146px 16px 162px 0px);
+}
+
+#align-left-button[disabled="true"] {
+ -moz-image-region: rect(146px 64px 162px 48px) !important;
+}
+
+#align-center-button {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#align-center-button:hover {
+ -moz-image-region: rect(48px 32px 64px 16px);
+}
+
+#align-center-button:hover:active {
+ -moz-image-region: rect(48px 48px 64px 32px);
+}
+
+#align-center-button[checked="true"] {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#align-center-button[disabled="true"] {
+ -moz-image-region: rect(48px 64px 64px 48px) !important;
+}
+
+#align-right-button {
+ -moz-image-region: rect(162px 16px 178px 0px);
+}
+
+#align-right-button:hover {
+ -moz-image-region: rect(162px 32px 178px 16px);
+}
+
+#align-right-button:hover:active {
+ -moz-image-region: rect(162px 48px 178px 32px);
+}
+
+#align-right-button[checked="true"] {
+ -moz-image-region: rect(162px 16px 178px 0px);
+}
+
+#align-right-button[disabled="true"] {
+ -moz-image-region: rect(162px 64px 178px 48px) !important;
+}
+
+#align-justify-button {
+ -moz-image-region: rect(130px 16px 146px 0px);
+}
+
+#align-justify-button:hover {
+ -moz-image-region: rect(130px 32px 146px 16px);
+}
+
+#align-justify-button:hover:active {
+ -moz-image-region: rect(130px 48px 146px 32px);
+}
+
+#align-justify-button[checked="true"] {
+ -moz-image-region: rect(130px 16px 146px 0px);
+}
+
+#align-justify-button[disabled="true"] {
+ -moz-image-region: rect(130px 64px 146px 48px) !important;
+}
+
+#AlignPopupButton {
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#AlignPopupButton:hover {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#AlignPopupButton[open="true"] {
+ -moz-image-region: rect(0px 48px 16px 32px) !important;
+}
+
+#AlignPopupButton[disabled="true"] {
+ -moz-image-region: rect(0px 64px 16px 48px) !important;
+}
+
+#InsertPopupButton {
+ -moz-image-region: rect(244px 16px 260px 0px);
+}
+
+#InsertPopupButton:hover {
+ -moz-image-region: rect(244px 32px 260px 16px);
+}
+
+#InsertPopupButton[open="true"] {
+ -moz-image-region: rect(244px 48px 260px 32px) !important;
+}
+
+#InsertPopupButton[disabled="true"] {
+ -moz-image-region: rect(244px 64px 260px 48px) !important;
+}
+
+#smileButtonMenu {
+ -moz-image-region: rect(227px 17px 244px 0px);
+}
+
+#smileButtonMenu:hover {
+ -moz-image-region: rect(227px 34px 244px 17px);
+}
+
+#smileButtonMenu[open="true"] {
+ -moz-image-region: rect(227px 51px 244px 34px) !important;
+}
+
+#smileButtonMenu[disabled="true"] {
+ -moz-image-region: rect(227px 68px 244px 51px) !important;
+}
+
+/* ::::: menuitem icons ::::: */
+
+/* ..... align menu ..... */
+
+#AlignPopup > menuitem {
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+}
+
+#AlignLeftItem {
+ -moz-image-region: rect(146px 16px 162px 0px);
+}
+
+#AlignLeftItem:hover {
+ -moz-image-region: rect(146px 32px 162px 16px);
+}
+
+#AlignCenterItem {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#AlignCenterItem:hover {
+ -moz-image-region: rect(48px 32px 64px 16px);
+}
+
+#AlignRightItem {
+ -moz-image-region: rect(162px 16px 178px 0px);
+}
+
+#AlignRightItem:hover {
+ -moz-image-region: rect(162px 32px 178px 16px);
+}
+
+#AlignJustifyItem {
+ -moz-image-region: rect(130px 16px 146px 0px);
+}
+
+#AlignJustifyItem:hover {
+ -moz-image-region: rect(130px 32px 146px 16px);
+}
+
+/* ..... insert menu ..... */
+
+#InsertPopup > menuitem {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+}
+
+#InsertLinkItem {
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+#InsertLinkItem:hover {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+#InsertAnchorItem {
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+#InsertAnchorItem:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+#InsertImageItem {
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+#InsertImageItem:hover {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+#InsertHRuleItem {
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+#InsertHRuleItem:hover {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+#InsertTableItem {
+ -moz-image-region: rect(180px 19px 199px 0);
+}
+
+#InsertTableItem:hover {
+ -moz-image-region: rect(180px 39px 199px 20px);
+}
+
+/* ..... smiley menu ..... */
+
+.insert-smile {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-smile.png");
+}
+
+.insert-frown {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-frown.png");
+}
+
+.insert-wink {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-wink.png");
+}
+
+.insert-tongue {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-tongue.png");
+}
+
+.insert-laughing {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-laughing.png");
+}
+
+.insert-embarrassed {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-embarrassed.png");
+}
+
+.insert-undecided {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-undecided.png");
+}
+
+.insert-surprise {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-surprise.png");
+}
+
+.insert-kiss {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-kiss.png");
+}
+
+.insert-yell {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-yell.png");
+}
+
+.insert-cool {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-cool.png");
+}
+
+.insert-money {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-money.png");
+}
+
+.insert-foot {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-foot.png");
+}
+
+.insert-innocent {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-innocent.png");
+}
+
+.insert-cry {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-cry.png");
+}
+
+.insert-sealed {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-sealed.png");
+}
+
+/* ::::: fg/bg color picker ::::: */
+
+.ColorPickerLabel {
+ border: 1px inset ThreeDFace;
+ margin: 0px;
+ padding: 2px;
+}
+
+.color-button {
+ border: 1px inset ThreeDFace;
+ padding: 0px;
+ width: 14px;
+ height: 12px;
+ margin: 2px;
+}
+
+.color-button:hover {
+ border: 1px solid ThreeDDarkShadow;
+}
+
+#TextColorButton {
+ margin-top: 2px;
+ margin-bottom: 9px;
+ margin-inline-start: 2px;
+ margin-inline-end: 9px;
+}
+
+#TextColorButton[color="mixed"] {
+ background-image: url("chrome://editor/skin/icons/multicolor.png");
+ background-size: cover;
+}
+
+#BackgroundColorButton {
+ margin-top: 9px;
+ margin-bottom: 2px;
+ margin-inline-start: 9px;
+ margin-inline-end: 2px;
+}
+
+#HighlightColorButton {
+ -moz-image-region: rect(260px 16px 272px 0px);
+ background-color: transparent;
+}
+
+#HighlightColorButton:hover {
+ -moz-image-region: rect(260px 32px 272px 16px);
+}
+
+#HighlightColorButton:hover:active {
+ -moz-image-region: rect(260px 48px 272px 32px);
+}
+
+#HighlightColorButton[disabled="true"],
+#HighlightColorButton[disabled="true"]:hover,
+#HighlightColorButton[disabled="true"]:hover:active {
+ -moz-image-region: rect(260px 64px 272px 48px);
+}
+
+
+#absolutePositionButton {
+ -moz-image-region: rect(273px 16px 289px 0px);
+}
+
+#absolutePositionButton:hover {
+ -moz-image-region: rect(273px 32px 289px 16px);
+}
+
+#absolutePositionButton:hover:active {
+ -moz-image-region: rect(273px 48px 289px 32px);
+}
+
+#absolutePositionButton[disabled="true"] {
+ -moz-image-region: rect(273px 64px 289px 48px) ! important;
+}
+
+#absolutePositionButton[checked="true"]{
+ -moz-image-region: rect(289px 16px 305px 0px);
+}
+
+#absolutePositionButton[checked="true"]:hover {
+ -moz-image-region: rect(289px 32px 305px 16px);
+}
+
+#absolutePositionButton[checked="true"]:hover:active {
+ -moz-image-region: rect(289px 48px 305px 32px);
+}
+
+#increaseZIndexButton {
+ list-style-image: url("chrome://editor/content/images/bringtofront.png");
+}
+
+#increaseZIndexButton[disabled="true"] {
+ list-style-image: url("chrome://editor/content/images/bringtofront-disabled.png");
+}
+
+#decreaseZIndexButton {
+ list-style-image: url("chrome://editor/content/images/sendtoback.png");
+}
+
+#decreaseZIndexButton[disabled="true"] {
+ list-style-image: url("chrome://editor/content/images/sendtoback-disabled.png");
+}
+
+/* Force the folder location and mail view items to fit in the available width
+ in the Customize Toolbar dialog. */
+#palette-box #paragraph-select-container,
+#palette-box #ParagraphSelect,
+#palette-box #font-face-select-container,
+#palette-box #FontFaceSelect,
+#palette-box #font-size-select-container,
+#palette-box #FontSizeSelect {
+ -moz-box-flex: 1;
+}
diff --git a/comm/suite/themes/classic/editor/editorModeToolbar.css b/comm/suite/themes/classic/editor/editorModeToolbar.css
new file mode 100644
index 0000000000..8284e0f254
--- /dev/null
+++ b/comm/suite/themes/classic/editor/editorModeToolbar.css
@@ -0,0 +1,39 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: edit mode toolbar ::::: */
+
+#EditModeToolbar {
+ -moz-box-align: start;
+ border-left: 1px solid ThreeDDarkShadow;
+ border-right: 1px solid ThreeDLightShadow;
+ padding: 0px 2px 2px;
+ min-width: 1px;
+}
+
+.tab-bottom[selected="true"] {
+ color: -moz-FieldText;
+ border-top-color: -moz-Field;
+ border-bottom-color: ThreeDShadow;
+ border-left-color: ThreeDHighlight;
+}
+
+#NormalModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-normal.png");
+}
+
+#TagModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-tags.png");
+}
+
+#SourceModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-html.png");
+}
+
+#PreviewModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-preview.png");
+}
+
diff --git a/comm/suite/themes/classic/editor/editorPrimaryToolbar.css b/comm/suite/themes/classic/editor/editorPrimaryToolbar.css
new file mode 100644
index 0000000000..392661d819
--- /dev/null
+++ b/comm/suite/themes/classic/editor/editorPrimaryToolbar.css
@@ -0,0 +1,569 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+#newButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#newButton:hover {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#newButton:hover:active {
+ -moz-image-region: rect(120px 89px 149px 60px);
+}
+
+#newButton[disabled="true"] {
+ -moz-image-region: rect(120px 119px 149px 90px) !important;
+}
+
+#openButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(150px 29px 179px 0);
+}
+
+#openButton:hover {
+ -moz-image-region: rect(150px 59px 179px 30px);
+}
+
+#openButton:hover:active {
+ -moz-image-region: rect(150px 89px 179px 60px);
+}
+
+#openButton[disabled="true"] {
+ -moz-image-region: rect(150px 119px 179px 90px) !important;
+}
+
+#saveButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(210px 29px 239px 0);
+}
+
+#saveButton:hover {
+ -moz-image-region: rect(210px 59px 239px 30px);
+}
+
+#saveButton:hover:active {
+ -moz-image-region: rect(210px 89px 239px 60px);
+}
+
+#saveButton[disabled="true"] {
+ -moz-image-region: rect(210px 119px 239px 90px) !important;
+}
+
+#publishButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(300px 29px 329px 0);
+}
+
+#publishButton:hover {
+ -moz-image-region: rect(300px 59px 329px 30px);
+}
+
+#publishButton:hover:active {
+ -moz-image-region: rect(300px 89px 329px 60px);
+}
+
+#publishButton[disabled="true"] {
+ -moz-image-region: rect(300px 119px 329px 90px) !important;
+}
+
+#previewButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(180px 29px 209px 0);
+}
+
+#previewButton:hover {
+ -moz-image-region: rect(180px 59px 209px 30px);
+}
+
+#previewButton:hover:active {
+ -moz-image-region: rect(180px 89px 209px 60px);
+}
+
+#previewButton[disabled="true"] {
+ -moz-image-region: rect(180px 119px 209px 90px) !important;
+}
+
+#linkButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#linkButton:hover {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#linkButton:hover:active {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#linkButton[disabled="true"] {
+ -moz-image-region: rect(90px 119px 119px 90px) !important;
+}
+
+#imageButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#imageButton:hover {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#imageButton:hover:active {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#imageButton[disabled="true"] {
+ -moz-image-region: rect(60px 119px 89px 90px) !important;
+}
+
+#formButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(450px 29px 479px 0);
+}
+
+#formButton:hover {
+ -moz-image-region: rect(450px 59px 479px 30px);
+}
+
+#formButton:hover:active {
+ -moz-image-region: rect(450px 89px 479px 60px);
+}
+
+#formButton[disabled="true"] {
+ -moz-image-region: rect(450px 119px 479px 90px) !important;
+}
+
+#namedAnchorButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#namedAnchorButton:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#namedAnchorButton:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#namedAnchorButton[disabled="true"] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+#hlineButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#hlineButton:hover {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#hlineButton:hover:active {
+ -moz-image-region: rect(30px 89px 59px 60px);
+}
+
+#hlineButton[disabled="true"] {
+ -moz-image-region: rect(30px 119px 59px 90px) !important;
+}
+
+#tableButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(270px 29px 299px 0);
+}
+
+#tableButton:hover {
+ -moz-image-region: rect(270px 59px 299px 30px);
+}
+
+#tableButton:hover:active {
+ -moz-image-region: rect(270px 89px 299px 60px);
+}
+
+#tableButton[disabled="true"] {
+ -moz-image-region: rect(270px 119px 299px 90px) !important;
+}
+
+#spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(240px 29px 269px 0);
+}
+
+#spellingButton:hover {
+ -moz-image-region: rect(240px 59px 269px 30px);
+}
+
+#spellingButton:hover:active {
+ -moz-image-region: rect(240px 89px 269px 60px);
+}
+
+#spellingButton[disabled="true"] {
+ -moz-image-region: rect(240px 119px 269px 90px) !important;
+}
+
+#cutButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(330px 29px 359px 0);
+}
+
+#cutButton:hover {
+ -moz-image-region: rect(330px 59px 359px 30px);
+}
+
+#cutButton:hover:active {
+ -moz-image-region: rect(330px 89px 359px 60px);
+}
+
+#cutButton[disabled="true"] {
+ -moz-image-region: rect(330px 119px 359px 90px) !important;
+}
+
+#copyButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(360px 29px 389px 0);
+}
+
+#copyButton:hover {
+ -moz-image-region: rect(360px 59px 389px 30px);
+}
+
+#copyButton:hover:active {
+ -moz-image-region: rect(360px 89px 389px 60px);
+}
+
+#copyButton[disabled="true"] {
+ -moz-image-region: rect(360px 119px 389px 90px) !important;
+}
+
+#pasteButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(390px 29px 419px 0);
+}
+
+#pasteButton:hover {
+ -moz-image-region: rect(390px 59px 419px 30px);
+}
+
+#pasteButton:hover:active {
+ -moz-image-region: rect(390px 89px 419px 60px);
+}
+
+#pasteButton[disabled="true"] {
+ -moz-image-region: rect(390px 119px 419px 90px) !important;
+}
+
+#findButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(420px 29px 449px 0);
+}
+
+#findButton:hover {
+ -moz-image-region: rect(420px 59px 449px 30px);
+}
+
+#findButton:hover:active {
+ -moz-image-region: rect(420px 89px 449px 60px);
+}
+
+#findButton[disabled="true"] {
+ -moz-image-region: rect(420px 119px 449px 90px) !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #newButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #newButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(80px 19px 99px 0);
+}
+
+toolbar[iconsize="small"] > #newButton:hover {
+ -moz-image-region: rect(80px 39px 99px 20px);
+}
+
+toolbar[iconsize="small"] > #newButton:hover:active {
+ -moz-image-region: rect(80px 59px 99px 40px);
+}
+
+toolbar[iconsize="small"] > #newButton[disabled="true"] {
+ -moz-image-region: rect(80px 79px 99px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #openButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #openButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(100px 19px 119px 0);
+}
+
+toolbar[iconsize="small"] > #openButton:hover {
+ -moz-image-region: rect(100px 39px 119px 20px);
+}
+
+toolbar[iconsize="small"] > #openButton:hover:active {
+ -moz-image-region: rect(100px 59px 119px 40px);
+}
+
+toolbar[iconsize="small"] > #openButton[disabled="true"] {
+ -moz-image-region: rect(100px 79px 119px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #saveButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #saveButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(140px 19px 159px 0);
+}
+
+toolbar[iconsize="small"] > #saveButton:hover {
+ -moz-image-region: rect(140px 39px 159px 20px);
+}
+
+toolbar[iconsize="small"] > #saveButton:hover:active {
+ -moz-image-region: rect(140px 59px 159px 40px);
+}
+
+toolbar[iconsize="small"] > #saveButton[disabled="true"] {
+ -moz-image-region: rect(140px 79px 159px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #publishButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #publishButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(200px 19px 219px 0);
+}
+
+toolbar[iconsize="small"] > #publishButton:hover {
+ -moz-image-region: rect(200px 39px 219px 20px);
+}
+
+toolbar[iconsize="small"] > #publishButton:hover:active {
+ -moz-image-region: rect(200px 59px 219px 40px);
+}
+
+toolbar[iconsize="small"] > #publishButton[disabled="true"] {
+ -moz-image-region: rect(200px 79px 219px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #previewButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #previewButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(120px 19px 139px 0);
+}
+
+toolbar[iconsize="small"] > #previewButton:hover {
+ -moz-image-region: rect(120px 39px 139px 20px);
+}
+
+toolbar[iconsize="small"] > #previewButton:hover:active {
+ -moz-image-region: rect(120px 59px 139px 40px);
+}
+
+toolbar[iconsize="small"] > #previewButton[disabled="true"] {
+ -moz-image-region: rect(120px 79px 139px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #linkButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #linkButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #linkButton:hover {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #linkButton:hover:active {
+ -moz-image-region: rect(60px 59px 79px 40px);
+}
+
+toolbar[iconsize="small"] > #linkButton[disabled="true"] {
+ -moz-image-region: rect(60px 79px 79px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #imageButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #imageButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #imageButton:hover {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #imageButton:hover:active {
+ -moz-image-region: rect(40px 59px 59px 40px);
+}
+
+toolbar[iconsize="small"] > #imageButton[disabled="true"] {
+ -moz-image-region: rect(40px 79px 59px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #formButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #formButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(300px 19px 319px 0);
+}
+
+toolbar[iconsize="small"] > #formButton:hover {
+ -moz-image-region: rect(300px 39px 319px 20px);
+}
+
+toolbar[iconsize="small"] > #formButton:hover:active {
+ -moz-image-region: rect(300px 59px 319px 40px);
+}
+
+toolbar[iconsize="small"] > #formButton[disabled="true"] {
+ -moz-image-region: rect(300px 79px 319px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #namedAnchorButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton[disabled="true"] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #hlineButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #hlineButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #hlineButton:hover {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #hlineButton:hover:active {
+ -moz-image-region: rect(20px 59px 39px 40px);
+}
+
+toolbar[iconsize="small"] > #hlineButton[disabled="true"] {
+ -moz-image-region: rect(20px 79px 39px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #tableButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #tableButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(180px 19px 199px 0);
+}
+
+toolbar[iconsize="small"] > #tableButton:hover {
+ -moz-image-region: rect(180px 39px 199px 20px);
+}
+
+toolbar[iconsize="small"] > #tableButton:hover:active {
+ -moz-image-region: rect(180px 59px 199px 40px);
+}
+
+toolbar[iconsize="small"] > #tableButton[disabled="true"] {
+ -moz-image-region: rect(180px 79px 199px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #spellingButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(160px 19px 179px 0);
+}
+
+toolbar[iconsize="small"] > #spellingButton:hover {
+ -moz-image-region: rect(160px 39px 179px 20px);
+}
+
+toolbar[iconsize="small"] > #spellingButton:hover:active {
+ -moz-image-region: rect(160px 59px 179px 40px);
+}
+
+toolbar[iconsize="small"] > #spellingButton[disabled="true"] {
+ -moz-image-region: rect(160px 79px 179px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #cutButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #cutButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(220px 19px 239px 0);
+}
+
+toolbar[iconsize="small"] > #cutButton:hover {
+ -moz-image-region: rect(220px 39px 239px 20px);
+}
+
+toolbar[iconsize="small"] > #cutButton:hover:active {
+ -moz-image-region: rect(220px 59px 239px 40px);
+}
+
+toolbar[iconsize="small"] > #cutButton[disabled="true"] {
+ -moz-image-region: rect(220px 79px 239px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #copyButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #copyButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(240px 19px 259px 0);
+}
+
+toolbar[iconsize="small"] > #copyButton:hover {
+ -moz-image-region: rect(240px 39px 259px 20px);
+}
+
+toolbar[iconsize="small"] > #copyButton:hover:active {
+ -moz-image-region: rect(240px 59px 259px 40px);
+}
+
+toolbar[iconsize="small"] > #copyButton[disabled="true"] {
+ -moz-image-region: rect(240px 79px 259px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #pasteButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #pasteButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(260px 19px 279px 0);
+}
+
+toolbar[iconsize="small"] > #pasteButton:hover {
+ -moz-image-region: rect(260px 39px 279px 20px);
+}
+
+toolbar[iconsize="small"] > #pasteButton:hover:active {
+ -moz-image-region: rect(260px 59px 279px 40px);
+}
+
+toolbar[iconsize="small"] > #pasteButton[disabled="true"] {
+ -moz-image-region: rect(260px 79px 279px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #findButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #findButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(280px 19px 299px 0);
+}
+
+toolbar[iconsize="small"] > #findButton:hover {
+ -moz-image-region: rect(280px 39px 299px 20px);
+}
+
+toolbar[iconsize="small"] > #findButton:hover:active {
+ -moz-image-region: rect(280px 59px 299px 40px);
+}
+
+toolbar[iconsize="small"] > #findButton[disabled="true"] {
+ -moz-image-region: rect(280px 79px 299px 60px) !important;
+}
diff --git a/comm/suite/themes/classic/editor/icons/btn2.png b/comm/suite/themes/classic/editor/icons/btn2.png
new file mode 100644
index 0000000000..5558ecac1c
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/btn2.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/editmode-html.png b/comm/suite/themes/classic/editor/icons/editmode-html.png
new file mode 100644
index 0000000000..4040b13078
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/editmode-html.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/editmode-normal.png b/comm/suite/themes/classic/editor/icons/editmode-normal.png
new file mode 100644
index 0000000000..5195b5da2c
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/editmode-normal.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/editmode-preview.png b/comm/suite/themes/classic/editor/icons/editmode-preview.png
new file mode 100644
index 0000000000..c54a61fa91
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/editmode-preview.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/editmode-tags.png b/comm/suite/themes/classic/editor/icons/editmode-tags.png
new file mode 100644
index 0000000000..a1784dada6
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/editmode-tags.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/editoricons-small.png b/comm/suite/themes/classic/editor/icons/editoricons-small.png
new file mode 100644
index 0000000000..dbbb99e77f
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/editoricons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/editoricons.png b/comm/suite/themes/classic/editor/icons/editoricons.png
new file mode 100644
index 0000000000..b68444f66b
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/editoricons.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/img-align-bottom.png b/comm/suite/themes/classic/editor/icons/img-align-bottom.png
new file mode 100644
index 0000000000..aed8e15b69
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/img-align-bottom.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/img-align-left.png b/comm/suite/themes/classic/editor/icons/img-align-left.png
new file mode 100644
index 0000000000..425cd7a238
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/img-align-left.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/img-align-middle.png b/comm/suite/themes/classic/editor/icons/img-align-middle.png
new file mode 100644
index 0000000000..439573c44b
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/img-align-middle.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/img-align-right.png b/comm/suite/themes/classic/editor/icons/img-align-right.png
new file mode 100644
index 0000000000..3e41c9a1da
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/img-align-right.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/img-align-top.png b/comm/suite/themes/classic/editor/icons/img-align-top.png
new file mode 100644
index 0000000000..1a248a99d1
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/img-align-top.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/multicolor.png b/comm/suite/themes/classic/editor/icons/multicolor.png
new file mode 100644
index 0000000000..ea20f50c5a
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/multicolor.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/progress-busy.png b/comm/suite/themes/classic/editor/icons/progress-busy.png
new file mode 100644
index 0000000000..260775bdda
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/progress-busy.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/progress-done.png b/comm/suite/themes/classic/editor/icons/progress-done.png
new file mode 100644
index 0000000000..ae07a3cb99
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/progress-done.png
Binary files differ
diff --git a/comm/suite/themes/classic/editor/icons/progress-failed.png b/comm/suite/themes/classic/editor/icons/progress-failed.png
new file mode 100644
index 0000000000..e6c54b08d0
--- /dev/null
+++ b/comm/suite/themes/classic/editor/icons/progress-failed.png
Binary files differ
diff --git a/comm/suite/themes/classic/icon.png b/comm/suite/themes/classic/icon.png
new file mode 100644
index 0000000000..ac0a948e62
--- /dev/null
+++ b/comm/suite/themes/classic/icon.png
Binary files differ
diff --git a/comm/suite/themes/classic/install.rdf b/comm/suite/themes/classic/install.rdf
new file mode 100644
index 0000000000..58d0ecca1e
--- /dev/null
+++ b/comm/suite/themes/classic/install.rdf
@@ -0,0 +1,48 @@
+<?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/. -->
+
+
+#filter substitution
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>{972ce4c6-7e08-4474-a285-3208198ce6fd}</em:id>
+ <em:version>@SEAMONKEY_VERSION@</em:version>
+ <em:type>4</em:type>
+
+ <!-- Target Application this theme can install into,
+ with minimum and maximum supported versions. -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id>
+ <em:minVersion>@SEAMONKEY_VERSION@</em:minVersion>
+ <em:maxVersion>@SEAMONKEY_VERSION@</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>SeaMonkey Default Theme</em:name>
+ <em:description>This theme uses styles and colors from the system to fit in with other applications.</em:description>
+
+ <!-- EXTENSION AUTHORS!
+ DO NOT COPY THIS PROPERTY INTO YOUR INSTALL RDF FILES
+ It will cause users not to be informed of incompatibilities
+ with your extension when they are updated with Software Update
+ and your extension will become unavailable to them!
+ -->
+ <em:appManaged>true</em:appManaged>
+
+ <em:locked>true</em:locked>
+
+ <!-- Front End Integration Hooks (used by Theme Manager)-->
+ <em:creator>Mozilla Contributors</em:creator>
+ <em:contributor>Mozilla Contributors</em:contributor>
+
+ <em:internalName>classic/1.0</em:internalName>
+ </Description>
+
+</RDF>
diff --git a/comm/suite/themes/classic/jar.mn b/comm/suite/themes/classic/jar.mn
new file mode 100644
index 0000000000..f9c3902ec3
--- /dev/null
+++ b/comm/suite/themes/classic/jar.mn
@@ -0,0 +1,520 @@
+# 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/.
+
+classic.jar:
+% skin communicator classic/1.0 %skin/classic/communicator/
+% skin editor classic/1.0 %skin/classic/editor/
+% skin messenger classic/1.0 %skin/classic/messenger/
+% skin navigator classic/1.0 %skin/classic/navigator/
+% skin messenger-newsblog classic/1.0 %skin/classic/messenger-newsblog/
+% style chrome://communicator/content/customizeToolbar.xul chrome://navigator/skin/navigator.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/primaryToolbar.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/messengercompose/messengercompose.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/smime/msgCompSMIMEOverlay.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/addressbook/addressbook.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://editor/skin/editorPrimaryToolbar.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://editor/skin/editorFormatToolbar.css
+#ifdef XP_MACOSX
+ skin/classic/communicator/aboutPrivateBrowsing.css (mac/communicator/aboutPrivateBrowsing.css)
+ skin/classic/communicator/aboutSessionRestore.css (mac/communicator/aboutSessionRestore.css)
+ skin/classic/communicator/button.css (mac/communicator/button.css)
+ skin/classic/communicator/communicator.css (mac/communicator/communicator.css)
+ skin/classic/communicator/customizeToolbar.css (mac/communicator/customizeToolbar.css)
+ skin/classic/communicator/config.css (mac/communicator/config.css)
+ skin/classic/communicator/datetimepicker.css (mac/communicator/datetimepicker.css)
+ skin/classic/communicator/numberbox.css (mac/communicator/numberbox.css)
+ skin/classic/communicator/preferences.css (mac/communicator/preferences.css)
+ skin/classic/communicator/spinbuttons.css (mac/communicator/spinbuttons.css)
+ skin/classic/communicator/toolbar.css (mac/communicator/toolbar.css)
+ skin/classic/communicator/viewSourceOverlay.css (mac/communicator/viewSourceOverlay.css)
+ skin/classic/communicator/console/console-error-caret.png (mac/communicator/console/console-error-caret.png)
+ skin/classic/communicator/console/console-error-dash.png (mac/communicator/console/console-error-dash.png)
+ skin/classic/communicator/console/console.css (mac/communicator/console/console.css)
+ skin/classic/communicator/downloads/downloadButtons.png (mac/communicator/downloads/downloadButtons.png)
+ skin/classic/communicator/downloads/progressBg.png (mac/communicator/downloads/progressBg.png)
+ skin/classic/communicator/downloads/downloadmanager.css (mac/communicator/downloads/downloadmanager.css)
+ skin/classic/communicator/helpviewer/dropmark-nav.png (mac/communicator/helpviewer/dropmark-nav.png)
+ skin/classic/communicator/helpviewer/help.css (mac/communicator/helpviewer/help.css)
+ skin/classic/communicator/places/bookmarks.css (mac/communicator/places/bookmarks.css)
+ skin/classic/communicator/places/bookmarksMenu.png (mac/communicator/places/bookmarksMenu.png)
+ skin/classic/communicator/places/bookmarksToolbar.css (mac/communicator/places/bookmarksToolbar.css)
+ skin/classic/communicator/places/bookmarksToolbar.png (mac/communicator/places/bookmarksToolbar.png)
+ skin/classic/communicator/places/editBookmarkOverlay.css (mac/communicator/places/editBookmarkOverlay.css)
+ skin/classic/communicator/places/filters.svg (mac/communicator/places/filters.svg)
+ skin/classic/communicator/places/livemark-folder.png (mac/communicator/places/livemark-folder.png)
+ skin/classic/communicator/places/organizer.css (mac/communicator/places/organizer.css)
+ skin/classic/communicator/places/query.png (mac/communicator/places/query.png)
+ skin/classic/communicator/places/tag.png (mac/communicator/places/tag.png)
+ skin/classic/communicator/places/toolbarDropMarker.png (mac/communicator/places/toolbarDropMarker.png)
+ skin/classic/communicator/profile/profile.css (mac/communicator/profile/profile.css)
+ skin/classic/communicator/sanitizeDialog.css (mac/communicator/sanitizeDialog.css)
+ skin/classic/communicator/search/searchbar.css (mac/communicator/search/searchbar.css)
+ skin/classic/communicator/search/searchbar-dropmarker.png (mac/communicator/search/searchbar-dropmarker.png)
+ skin/classic/communicator/search/searchbar-search.png (mac/communicator/search/searchbar-search.png)
+ skin/classic/communicator/sidebar/sidebar.css (mac/communicator/sidebar/sidebar.css)
+ skin/classic/communicator/icons/autocomplete-dropmarker.png (mac/communicator/icons/autocomplete-dropmarker.png)
+ skin/classic/communicator/icons/communicatoricons.png (mac/communicator/icons/communicatoricons.png)
+ skin/classic/communicator/icons/communicatoricons-small.png (mac/communicator/icons/communicatoricons-small.png)
+ skin/classic/communicator/icons/feedIcon16-disabled.png (mac/communicator/icons/feedIcon16-disabled.png)
+ skin/classic/communicator/icons/geolocation-16.png (mac/communicator/icons/geolocation-16.png)
+ skin/classic/communicator/icons/geolocation-64.png (mac/communicator/icons/geolocation-64.png)
+ skin/classic/communicator/icons/item.png (mac/communicator/icons/item.png)
+ skin/classic/communicator/icons/key-16.png (mac/communicator/icons/key-16.png)
+ skin/classic/communicator/icons/key-64.png (mac/communicator/icons/key-64.png)
+ skin/classic/communicator/icons/loading.png (mac/communicator/icons/loading.png)
+ skin/classic/communicator/icons/panebutton-active.png (mac/communicator/icons/panebutton-active.png)
+ skin/classic/communicator/icons/panebutton-inactive.png (mac/communicator/icons/panebutton-inactive.png)
+ skin/classic/communicator/icons/warning-24.png (mac/communicator/icons/warning-24.png)
+ skin/classic/communicator/toolbar/toolbar-gradient22.png (mac/communicator/toolbar/toolbar-gradient22.png)
+ skin/classic/communicator/toolbar/toolbar-gradient34.png (mac/communicator/toolbar/toolbar-gradient34.png)
+#else
+ skin/classic/communicator/aboutPrivateBrowsing.css (communicator/aboutPrivateBrowsing.css)
+ skin/classic/communicator/aboutSessionRestore.css (communicator/aboutSessionRestore.css)
+ skin/classic/communicator/button.css (communicator/button.css)
+ skin/classic/communicator/config.css (communicator/config.css)
+ skin/classic/communicator/customizeToolbar.css (communicator/customizeToolbar.css)
+ skin/classic/communicator/datetimepicker.css (communicator/datetimepicker.css)
+#ifdef MOZ_WIDGET_GTK
+ skin/classic/communicator/communicator.css (linux/communicator/communicator.css)
+ skin/classic/communicator/numberbox.css (linux/communicator/numberbox.css)
+ skin/classic/communicator/preferences.css (linux/communicator/preferences.css)
+ skin/classic/communicator/spinbuttons.css (linux/communicator/spinbuttons.css)
+#else
+ skin/classic/communicator/communicator.css (communicator/communicator.css)
+ skin/classic/communicator/numberbox.css (communicator/numberbox.css)
+ skin/classic/communicator/preferences.css (communicator/preferences.css)
+ skin/classic/communicator/spinbuttons.css (communicator/spinbuttons.css)
+#endif
+ skin/classic/communicator/toolbar.css (communicator/toolbar.css)
+ skin/classic/communicator/viewSourceOverlay.css (communicator/viewSourceOverlay.css)
+ skin/classic/communicator/console/console.css (communicator/console/console.css)
+ skin/classic/communicator/console/console-error-caret.png (communicator/console/console-error-caret.png)
+ skin/classic/communicator/console/console-error-dash.png (communicator/console/console-error-dash.png)
+ skin/classic/communicator/console/console-toolbar.png (communicator/console/console-toolbar.png)
+ skin/classic/communicator/console/itemSelected.png (communicator/console/itemSelected.png)
+ skin/classic/communicator/downloads/downloadButtons.png (communicator/downloads/downloadButtons.png)
+ skin/classic/communicator/downloads/downloadmanager.css (communicator/downloads/downloadmanager.css)
+ skin/classic/communicator/helpviewer/Toolbar-rtl.png (communicator/helpviewer/Toolbar-rtl.png)
+ skin/classic/communicator/helpviewer/help.css (communicator/helpviewer/help.css)
+ skin/classic/communicator/places/bookmark-folder-closed.png (communicator/places/bookmark-folder-closed.png)
+ skin/classic/communicator/places/bookmark-folder-dis.png (communicator/places/bookmark-folder-dis.png)
+ skin/classic/communicator/places/bookmark-folder-open.png (communicator/places/bookmark-folder-open.png)
+ skin/classic/communicator/places/bookmarks.css (communicator/places/bookmarks.css)
+ skin/classic/communicator/places/bookmarksMenu.png (communicator/places/bookmarksMenu.png)
+ skin/classic/communicator/places/bookmarksToolbar.css (communicator/places/bookmarksToolbar.css)
+ skin/classic/communicator/places/bookmarksToolbar.png (communicator/places/bookmarksToolbar.png)
+ skin/classic/communicator/places/editBookmarkOverlay.css (communicator/places/editBookmarkOverlay.css)
+ skin/classic/communicator/places/livemark-folder.png (communicator/places/livemark-folder.png)
+ skin/classic/communicator/places/organizer.css (communicator/places/organizer.css)
+ skin/classic/communicator/places/query.png (communicator/places/query.png)
+ skin/classic/communicator/places/tag.png (communicator/places/tag.png)
+ skin/classic/communicator/places/toolbarDropMarker.png (communicator/places/toolbarDropMarker.png)
+ skin/classic/communicator/profile/profile.css (communicator/profile/profile.css)
+#ifdef MOZ_WIDGET_GTK
+ skin/classic/communicator/sanitizeDialog.css (linux/communicator/sanitizeDialog.css)
+#else
+ skin/classic/communicator/sanitizeDialog.css (windows/communicator/sanitizeDialog.css)
+#endif
+ skin/classic/communicator/search/mainwindow-dropdown-arrow.png (communicator/search/mainwindow-dropdown-arrow.png)
+ skin/classic/communicator/search/search-glass.png (communicator/search/search-glass.png)
+ skin/classic/communicator/search/searchbar.css (communicator/search/searchbar.css)
+ skin/classic/communicator/sidebar/sidebar.css (communicator/sidebar/sidebar.css)
+ skin/classic/communicator/icons/communicatoricons.png (communicator/icons/communicatoricons.png)
+ skin/classic/communicator/icons/communicatoricons-small.png (communicator/icons/communicatoricons-small.png)
+ skin/classic/communicator/icons/geolocation-16.png (communicator/icons/geolocation-16.png)
+ skin/classic/communicator/icons/geolocation-64.png (communicator/icons/geolocation-64.png)
+ skin/classic/communicator/icons/information-48.png (communicator/icons/information-48.png)
+ skin/classic/communicator/icons/key-16.png (communicator/icons/key-16.png)
+ skin/classic/communicator/icons/key-64.png (communicator/icons/key-64.png)
+ skin/classic/communicator/icons/loading.png (communicator/icons/loading.png)
+ skin/classic/communicator/icons/question-48.png (communicator/icons/question-48.png)
+ skin/classic/communicator/icons/warning-24.png (communicator/icons/warning-24.png)
+#endif
+ skin/classic/communicator/icons/notification-icons.svg (communicator/icons/notification-icons.svg)
+ skin/classic/communicator/about.css (communicator/about.css)
+ skin/classic/communicator/aboutSupport.css (communicator/aboutSupport.css)
+ skin/classic/communicator/brand.css (communicator/brand.css)
+ skin/classic/communicator/blockedSite.css (communicator/blockedSite.css)
+ skin/classic/communicator/certError.css (communicator/certError.css)
+ skin/classic/communicator/common.css (communicator/common.css)
+ skin/classic/communicator/dialogs.css (communicator/dialogs.css)
+ skin/classic/communicator/communicatorBindings.xml (communicator/communicatorBindings.xml)
+ skin/classic/communicator/prefpanels.css (communicator/prefpanels.css)
+ skin/classic/communicator/smileys.css (communicator/smileys.css)
+ skin/classic/communicator/tasksOverlay.css (communicator/tasksOverlay.css)
+ skin/classic/communicator/brand/throbber-anim.png (communicator/brand/throbber-anim.png)
+ skin/classic/communicator/brand/throbber-single.png (communicator/brand/throbber-single.png)
+ skin/classic/communicator/brand/throbber16-anim.png (communicator/brand/throbber16-anim.png)
+ skin/classic/communicator/brand/throbber16-single.png (communicator/brand/throbber16-single.png)
+ skin/classic/communicator/dataman/dataman.css (communicator/dataman/dataman.css)
+ skin/classic/communicator/dataman/datamanIcon-16.png (communicator/dataman/datamanIcon-16.png)
+#ifdef MOZ_WIDGET_GTK
+ skin/classic/communicator/feed-subscribe.css (linux/communicator/feed-subscribe.css)
+#else
+ skin/classic/communicator/feed-subscribe.css (communicator/feed-subscribe.css)
+#endif
+ skin/classic/communicator/feed-subscribe-ui.css (communicator/feed-subscribe-ui.css)
+ skin/classic/communicator/fullscreen-video.css (communicator/fullscreen-video.css)
+ skin/classic/communicator/downloads/dl-remove.png (communicator/downloads/dl-remove.png)
+ skin/classic/communicator/places/allBookmarks.png (communicator/places/allBookmarks.png)
+ skin/classic/communicator/places/bookmark.png (communicator/places/bookmark.png)
+ skin/classic/communicator/places/bookmark-item-dis.png (communicator/places/bookmark-item-dis.png)
+ skin/classic/communicator/places/bookmark-item-updated.png (communicator/places/bookmark-item-updated.png)
+ skin/classic/communicator/places/bookmark-item.svg (communicator/places/bookmark-item.svg)
+ skin/classic/communicator/places/calendar.png (communicator/places/calendar.png)
+ skin/classic/communicator/places/history.png (communicator/places/history.png)
+ skin/classic/communicator/places/unsortedBookmarks.png (communicator/places/unsortedBookmarks.png)
+ skin/classic/communicator/profile/migrate.png (communicator/profile/migrate.png)
+ skin/classic/communicator/profile/profileManager.css (communicator/profile/profileManager.css)
+ skin/classic/communicator/profile/profileicon-large.png (communicator/profile/profileicon-large.png)
+ skin/classic/communicator/search/engineManager.css (communicator/search/engineManager.css)
+ skin/classic/communicator/search/search.css (communicator/search/search.css)
+ skin/classic/communicator/sidebar/sbtab-twisty.png (communicator/sidebar/sbtab-twisty.png)
+ skin/classic/communicator/sidebar/sbtab-twisty-open.png (communicator/sidebar/sbtab-twisty-open.png)
+ skin/classic/communicator/sidebar/customize.css (communicator/sidebar/customize.css)
+ skin/classic/communicator/sidebar/preview.css (communicator/sidebar/preview.css)
+ skin/classic/communicator/sidebar/sidebarBindings.xml (communicator/sidebar/sidebarBindings.xml)
+ skin/classic/communicator/sidebar/sidebarListView.css (communicator/sidebar/sidebarListView.css)
+ skin/classic/communicator/xpinstall/xpinstall.css (communicator/xpinstall/xpinstall.css)
+ skin/classic/communicator/icons/audioFeedIcon.png (communicator/icons/feedIcon.png)
+ skin/classic/communicator/icons/closeFullScreenVideo.png (communicator/icons/closeFullScreenVideo.png)
+ skin/classic/communicator/icons/close-button.png (communicator/icons/close-button.png)
+ skin/classic/communicator/icons/offline.png (communicator/icons/offline.png)
+ skin/classic/communicator/icons/online.png (communicator/icons/online.png)
+ skin/classic/communicator/icons/search.png (communicator/icons/search.png)
+ skin/classic/communicator/icons/identity.png (communicator/icons/identity.png)
+ skin/classic/communicator/icons/lock-broken.png (communicator/icons/lock-broken.png)
+ skin/classic/communicator/icons/lock-broken-16.png (communicator/icons/lock-broken-16.png)
+ skin/classic/communicator/icons/lock-insecure.png (communicator/icons/lock-insecure.png)
+ skin/classic/communicator/icons/lock-insecure-16.png (communicator/icons/lock-insecure-16.png)
+ skin/classic/communicator/icons/lock-secure.png (communicator/icons/lock-secure.png)
+ skin/classic/communicator/icons/lock-secure-16.png (communicator/icons/lock-secure-16.png)
+ skin/classic/communicator/icons/alwaysAsk.png (communicator/icons/alwaysAsk.png)
+ skin/classic/communicator/icons/application.png (communicator/icons/application.png)
+ skin/classic/communicator/icons/feedIcon.png (communicator/icons/feedIcon.png)
+ skin/classic/communicator/icons/feedIcon16.png (communicator/icons/feedIcon16.png)
+ skin/classic/communicator/icons/notification-16.png (communicator/icons/notification-16.png)
+ skin/classic/communicator/icons/notification-64.png (communicator/icons/notification-64.png)
+ skin/classic/communicator/icons/geo.png (communicator/icons/geo.png)
+ skin/classic/communicator/icons/save.png (communicator/icons/save.png)
+ skin/classic/communicator/icons/smileys/smiley-smile.png (communicator/icons/smileys/smiley-smile.png)
+ skin/classic/communicator/icons/smileys/smiley-frown.png (communicator/icons/smileys/smiley-frown.png)
+ skin/classic/communicator/icons/smileys/smiley-wink.png (communicator/icons/smileys/smiley-wink.png)
+ skin/classic/communicator/icons/smileys/smiley-tongue.png (communicator/icons/smileys/smiley-tongue.png)
+ skin/classic/communicator/icons/smileys/smiley-laughing.png (communicator/icons/smileys/smiley-laughing.png)
+ skin/classic/communicator/icons/smileys/smiley-embarrassed.png (communicator/icons/smileys/smiley-embarrassed.png)
+ skin/classic/communicator/icons/smileys/smiley-undecided.png (communicator/icons/smileys/smiley-undecided.png)
+ skin/classic/communicator/icons/smileys/smiley-surprise.png (communicator/icons/smileys/smiley-surprise.png)
+ skin/classic/communicator/icons/smileys/smiley-kiss.png (communicator/icons/smileys/smiley-kiss.png)
+ skin/classic/communicator/icons/smileys/smiley-yell.png (communicator/icons/smileys/smiley-yell.png)
+ skin/classic/communicator/icons/smileys/smiley-cool.png (communicator/icons/smileys/smiley-cool.png)
+ skin/classic/communicator/icons/smileys/smiley-money.png (communicator/icons/smileys/smiley-money.png)
+ skin/classic/communicator/icons/smileys/smiley-foot.png (communicator/icons/smileys/smiley-foot.png)
+ skin/classic/communicator/icons/smileys/smiley-innocent.png (communicator/icons/smileys/smiley-innocent.png)
+ skin/classic/communicator/icons/smileys/smiley-cry.png (communicator/icons/smileys/smiley-cry.png)
+ skin/classic/communicator/icons/smileys/smiley-sealed.png (communicator/icons/smileys/smiley-sealed.png)
+ skin/classic/communicator/icons/videoFeedIcon.png (communicator/icons/feedIcon.png)
+ skin/classic/communicator/taskbar/taskbar.png (communicator/taskbar/taskbar.png)
+ skin/classic/communicator/taskbar/taskmenu-browser.png (communicator/taskbar/taskmenu-browser.png)
+ skin/classic/communicator/taskbar/taskmenu-composer.png (communicator/taskbar/taskmenu-composer.png)
+ skin/classic/communicator/taskbar/taskmenu-mailnews.png (communicator/taskbar/taskmenu-mailnews.png)
+ skin/classic/communicator/taskbar/taskmenu-abook.png (communicator/taskbar/taskmenu-abook.png)
+ skin/classic/communicator/toolbar/tbgrip-arrow.png (communicator/toolbar/tbgrip-arrow.png)
+ skin/classic/communicator/toolbar/tbgrip-arrow-clps.png (communicator/toolbar/tbgrip-arrow-clps.png)
+ skin/classic/communicator/toolbar/tbgrip-texture.png (communicator/toolbar/tbgrip-texture.png)
+#ifdef XP_MACOSX
+ skin/classic/editor/editor.css (mac/editor/editor.css)
+ skin/classic/editor/editorModeToolbar.css (mac/editor/editorModeToolbar.css)
+ skin/classic/editor/editorPrimaryToolbar.css (mac/editor/editorPrimaryToolbar.css)
+ skin/classic/editor/editorFormatToolbar.css (mac/editor/editorFormatToolbar.css)
+ skin/classic/editor/icons/editoricons.png (mac/editor/icons/editoricons.png)
+ skin/classic/editor/icons/editoricons-small.png (mac/editor/icons/editoricons-small.png)
+#else
+ skin/classic/editor/editor.css (editor/editor.css)
+ skin/classic/editor/editorModeToolbar.css (editor/editorModeToolbar.css)
+ skin/classic/editor/editorPrimaryToolbar.css (editor/editorPrimaryToolbar.css)
+ skin/classic/editor/editorFormatToolbar.css (editor/editorFormatToolbar.css)
+ skin/classic/editor/icons/editoricons.png (editor/icons/editoricons.png)
+ skin/classic/editor/icons/editoricons-small.png (editor/icons/editoricons-small.png)
+#endif
+ skin/classic/editor/EditorDialog.css (editor/EditorDialog.css)
+ skin/classic/editor/icons/editmode-html.png (editor/icons/editmode-html.png)
+ skin/classic/editor/icons/editmode-normal.png (editor/icons/editmode-normal.png)
+ skin/classic/editor/icons/editmode-preview.png (editor/icons/editmode-preview.png)
+ skin/classic/editor/icons/editmode-tags.png (editor/icons/editmode-tags.png)
+ skin/classic/editor/icons/img-align-bottom.png (editor/icons/img-align-bottom.png)
+ skin/classic/editor/icons/img-align-left.png (editor/icons/img-align-left.png)
+ skin/classic/editor/icons/img-align-middle.png (editor/icons/img-align-middle.png)
+ skin/classic/editor/icons/img-align-right.png (editor/icons/img-align-right.png)
+ skin/classic/editor/icons/img-align-top.png (editor/icons/img-align-top.png)
+ skin/classic/editor/icons/btn2.png (editor/icons/btn2.png)
+ skin/classic/editor/icons/multicolor.png (editor/icons/multicolor.png)
+ skin/classic/editor/icons/progress-busy.png (editor/icons/progress-busy.png)
+ skin/classic/editor/icons/progress-done.png (editor/icons/progress-done.png)
+ skin/classic/editor/icons/progress-failed.png (editor/icons/progress-failed.png)
+#ifdef XP_MACOSX
+ skin/classic/messenger/accountManage.css (mac/messenger/accountManage.css)
+ skin/classic/messenger/browserRequest.css (mac/messenger/browserRequest.css)
+ skin/classic/messenger/filterDialog.css (mac/messenger/filterDialog.css)
+ skin/classic/messenger/mailWindow1.css (mac/messenger/mailWindow1.css)
+ skin/classic/messenger/messageHeader.css (mac/messenger/messageHeader.css)
+ skin/classic/messenger/messageQuotes.css (mac/messenger/messageQuotes.css)
+ skin/classic/messenger/primaryToolbar.css (mac/messenger/primaryToolbar.css)
+ skin/classic/messenger/searchDialog.css (mac/messenger/searchDialog.css)
+ skin/classic/messenger/icons/messengericons.png (mac/messenger/icons/messengericons.png)
+ skin/classic/messenger/icons/messengericons-small.png (mac/messenger/icons/messengericons-small.png)
+ skin/classic/messenger/icons/spin-buttons.png (mac/messenger/icons/spin-buttons.png)
+ skin/classic/messenger/icons/spin-buttons-active.png (mac/messenger/icons/spin-buttons-active.png)
+ skin/classic/messenger/icons/tab-arrow-left.png (mac/messenger/icons/tab-arrow-left.png)
+ skin/classic/messenger/icons/tab-arrow-right.png (mac/messenger/icons/tab-arrow-right.png)
+ skin/classic/messenger/icons/twisty-clsd.png (mac/messenger/icons/twisty-clsd.png)
+ skin/classic/messenger/icons/twisty-open.png (mac/messenger/icons/twisty-open.png)
+ skin/classic/messenger/addressbook/addressbook.css (mac/messenger/addressbook/addressbook.css)
+ skin/classic/messenger/addressbook/icons/addressbookicons.png (mac/messenger/addressbook/icons/addressbookicons.png)
+ skin/classic/messenger/addressbook/icons/addressbookicons-small.png (mac/messenger/addressbook/icons/addressbookicons-small.png)
+ skin/classic/messenger/messengercompose/messengercompose.css (mac/messenger/messengercompose/messengercompose.css)
+ skin/classic/messenger/smime/msgCompSMIMEOverlay.css (mac/messenger/smime/msgCompSMIMEOverlay.css)
+ skin/classic/messenger/smime/icons/smimeicons.png (mac/messenger/smime/icons/smimeicons.png)
+ skin/classic/messenger/smime/icons/smimeicons-small.png (mac/messenger/smime/icons/smimeicons-small.png)
+#else
+ skin/classic/messenger/accountManage.css (messenger/accountManage.css)
+ skin/classic/messenger/browserRequest.css (messenger/browserRequest.css)
+ skin/classic/messenger/filterDialog.css (messenger/filterDialog.css)
+ skin/classic/messenger/mailWindow1.css (messenger/mailWindow1.css)
+ skin/classic/messenger/messageHeader.css (messenger/messageHeader.css)
+ skin/classic/messenger/messageQuotes.css (messenger/messageQuotes.css)
+ skin/classic/messenger/primaryToolbar.css (messenger/primaryToolbar.css)
+ skin/classic/messenger/searchDialog.css (messenger/searchDialog.css)
+ skin/classic/messenger/icons/messengericons.png (messenger/icons/messengericons.png)
+ skin/classic/messenger/icons/messengericons-small.png (messenger/icons/messengericons-small.png)
+ skin/classic/messenger/icons/twisty-clsd.png (messenger/icons/twisty-clsd.png)
+ skin/classic/messenger/icons/twisty-open.png (messenger/icons/twisty-open.png)
+ skin/classic/messenger/addressbook/addressbook.css (messenger/addressbook/addressbook.css)
+ skin/classic/messenger/addressbook/icons/addressbookicons.png (messenger/addressbook/icons/addressbookicons.png)
+ skin/classic/messenger/addressbook/icons/addressbookicons-small.png (messenger/addressbook/icons/addressbookicons-small.png)
+ skin/classic/messenger/messengercompose/messengercompose.css (messenger/messengercompose/messengercompose.css)
+ skin/classic/messenger/smime/msgCompSMIMEOverlay.css (messenger/smime/msgCompSMIMEOverlay.css)
+ skin/classic/messenger/smime/icons/smimeicons.png (messenger/smime/icons/smimeicons.png)
+ skin/classic/messenger/smime/icons/smimeicons-small.png (messenger/smime/icons/smimeicons-small.png)
+#endif
+ skin/classic/messenger/dialogs.css (messenger/dialogs.css)
+ skin/classic/messenger/accountWizard.css (messenger/accountWizard.css)
+ skin/classic/messenger/folderMenus.css (messenger/folderMenus.css)
+ skin/classic/messenger/folderPane.css (messenger/folderPane.css)
+ skin/classic/messenger/accountCentral.css (messenger/accountCentral.css)
+ skin/classic/messenger/addressingWidget.css (messenger/addressingWidget.css)
+ skin/classic/messenger/messageWindow.css (messenger/messageWindow.css)
+ skin/classic/messenger/messenger.css (messenger/messenger.css)
+ skin/classic/messenger/newmailalert.css (messenger/newmailalert.css)
+ skin/classic/messenger/prefPanels.css (messenger/prefPanels.css)
+ skin/classic/messenger/subscribe.css (messenger/subscribe.css)
+ skin/classic/messenger/threadPane.css (messenger/threadPane.css)
+ skin/classic/messenger/threadPaneLabels.css (messenger/threadPaneLabels.css)
+ skin/classic/messenger/messageBody.css (messenger/messageBody.css)
+ skin/classic/messenger/start.css (messenger/start.css)
+ skin/classic/messenger/msgSelectOffline.css (messenger/msgSelectOffline.css)
+ skin/classic/messenger/icons/acct-compose.png (messenger/icons/acct-compose.png)
+ skin/classic/messenger/icons/acct-filters.png (messenger/icons/acct-filters.png)
+ skin/classic/messenger/icons/acct-newaccount.png (messenger/icons/acct-newaccount.png)
+ skin/classic/messenger/icons/acct-prefs.png (messenger/icons/acct-prefs.png)
+ skin/classic/messenger/icons/acct-read.png (messenger/icons/acct-read.png)
+ skin/classic/messenger/icons/acct-search.png (messenger/icons/acct-search.png)
+ skin/classic/messenger/icons/acct-subscribe.png (messenger/icons/acct-subscribe.png)
+ skin/classic/messenger/icons/attachment-col.png (messenger/icons/attachment-col.png)
+ skin/classic/messenger/icons/attachment.png (messenger/icons/attachment.png)
+ skin/classic/messenger/icons/attachment-selected.png (messenger/icons/attachment-selected.png)
+ skin/classic/messenger/icons/attach.png (messenger/icons/attach.png)
+ skin/classic/messenger/icons/check.png (messenger/icons/check.png)
+ skin/classic/messenger/icons/dot.png (messenger/icons/dot.png)
+ skin/classic/messenger/icons/flagcol.png (messenger/icons/flagcol.png)
+ skin/classic/messenger/icons/folder-closed.png (messenger/icons/folder-closed.png)
+ skin/classic/messenger/icons/folder-draft-open.png (messenger/icons/folder-draft-open.png)
+ skin/classic/messenger/icons/folder-draft.png (messenger/icons/folder-draft.png)
+ skin/classic/messenger/icons/folder-draft-share-open.png (messenger/icons/folder-draft-share-open.png)
+ skin/classic/messenger/icons/folder-draft-share.png (messenger/icons/folder-draft-share.png)
+ skin/classic/messenger/icons/folder-inbox-new.png (messenger/icons/folder-inbox-new.png)
+ skin/classic/messenger/icons/folder-inbox-open.png (messenger/icons/folder-inbox-open.png)
+ skin/classic/messenger/icons/folder-inbox.png (messenger/icons/folder-inbox.png)
+ skin/classic/messenger/icons/folder-inbox-share-open.png (messenger/icons/folder-inbox-share-open.png)
+ skin/classic/messenger/icons/folder-inbox-share.png (messenger/icons/folder-inbox-share.png)
+ skin/classic/messenger/icons/folder-junk-open.png (messenger/icons/folder-junk-open.png)
+ skin/classic/messenger/icons/folder-junk.png (messenger/icons/folder-junk.png)
+ skin/classic/messenger/icons/folder-new-open.png (messenger/icons/folder-new-open.png)
+ skin/classic/messenger/icons/folder-new.png (messenger/icons/folder-new.png)
+ skin/classic/messenger/icons/folder-newsgroup-new.png (messenger/icons/folder-newsgroup-new.png)
+ skin/classic/messenger/icons/folder-newsgroup.png (messenger/icons/folder-newsgroup.png)
+ skin/classic/messenger/icons/folder-open.png (messenger/icons/folder-open.png)
+ skin/classic/messenger/icons/folder-outbox-open.png (messenger/icons/folder-outbox-open.png)
+ skin/classic/messenger/icons/folder-outbox.png (messenger/icons/folder-outbox.png)
+ skin/classic/messenger/icons/folder-search.png (messenger/icons/folder-search.png)
+ skin/classic/messenger/icons/folder-sent-open.png (messenger/icons/folder-sent-open.png)
+ skin/classic/messenger/icons/folder-sent.png (messenger/icons/folder-sent.png)
+ skin/classic/messenger/icons/folder-sent-share-open.png (messenger/icons/folder-sent-share-open.png)
+ skin/classic/messenger/icons/folder-sent-share.png (messenger/icons/folder-sent-share.png)
+ skin/classic/messenger/icons/folder-share-open.png (messenger/icons/folder-share-open.png)
+ skin/classic/messenger/icons/folder-share.png (messenger/icons/folder-share.png)
+ skin/classic/messenger/icons/folder-template-open.png (messenger/icons/folder-template-open.png)
+ skin/classic/messenger/icons/folder-template.png (messenger/icons/folder-template.png)
+ skin/classic/messenger/icons/folder-template-share-open.png (messenger/icons/folder-template-share-open.png)
+ skin/classic/messenger/icons/folder-template-share.png (messenger/icons/folder-template-share.png)
+ skin/classic/messenger/icons/folder-trash-open.png (messenger/icons/folder-trash-open.png)
+ skin/classic/messenger/icons/folder-trash.png (messenger/icons/folder-trash.png)
+ skin/classic/messenger/icons/folder-trash-share-open.png (messenger/icons/folder-trash-share-open.png)
+ skin/classic/messenger/icons/folder-trash-share.png (messenger/icons/folder-trash-share.png)
+ skin/classic/messenger/icons/info.png (messenger/icons/info.png)
+ skin/classic/messenger/icons/insecure.png (messenger/icons/insecure.png)
+ skin/classic/messenger/icons/junk.png (messenger/icons/junk.png)
+ skin/classic/messenger/icons/junkcol.png (messenger/icons/junkcol.png)
+ skin/classic/messenger/icons/loading.png (messenger/icons/loading.png)
+ skin/classic/messenger/icons/local-mailhost.png (messenger/icons/local-mailhost.png)
+ skin/classic/messenger/icons/message-mail-attach-del.png (messenger/icons/message-mail-attach-del.png)
+ skin/classic/messenger/icons/message-mail-attach-fwd-offl-reply.png (messenger/icons/message-mail-attach-fwd-offl-reply.png)
+ skin/classic/messenger/icons/message-mail-attach-fwd-offl.png (messenger/icons/message-mail-attach-fwd-offl.png)
+ skin/classic/messenger/icons/message-mail-attach-fwd-reply.png (messenger/icons/message-mail-attach-fwd-reply.png)
+ skin/classic/messenger/icons/message-mail-attach-fwd.png (messenger/icons/message-mail-attach-fwd.png)
+ skin/classic/messenger/icons/message-mail-attach-offl-reply.png (messenger/icons/message-mail-attach-offl-reply.png)
+ skin/classic/messenger/icons/message-mail-attach-offl.png (messenger/icons/message-mail-attach-offl.png)
+ skin/classic/messenger/icons/message-mail-attach-reply.png (messenger/icons/message-mail-attach-reply.png)
+ skin/classic/messenger/icons/message-mail-attach.png (messenger/icons/message-mail-attach.png)
+ skin/classic/messenger/icons/message-mail-delete-offl.png (messenger/icons/message-mail-delete-offl.png)
+ skin/classic/messenger/icons/message-mail-fwd-offl-reply.png (messenger/icons/message-mail-fwd-offl-reply.png)
+ skin/classic/messenger/icons/message-mail-fwd-offl.png (messenger/icons/message-mail-fwd-offl.png)
+ skin/classic/messenger/icons/message-mail-fwd-reply.png (messenger/icons/message-mail-fwd-reply.png)
+ skin/classic/messenger/icons/message-mail-fwd.png (messenger/icons/message-mail-fwd.png)
+ skin/classic/messenger/icons/message-mail-imapdelete.png (messenger/icons/message-mail-imapdelete.png)
+ skin/classic/messenger/icons/message-mail-new-offl.png (messenger/icons/message-mail-new-offl.png)
+ skin/classic/messenger/icons/message-mail-new.png (messenger/icons/message-mail-new.png)
+ skin/classic/messenger/icons/message-mail-offl-reply.png (messenger/icons/message-mail-offl-reply.png)
+ skin/classic/messenger/icons/message-mail-offl.png (messenger/icons/message-mail-offl.png)
+ skin/classic/messenger/icons/message-mail-reply.png (messenger/icons/message-mail-reply.png)
+ skin/classic/messenger/icons/message-mail.png (messenger/icons/message-mail.png)
+ skin/classic/messenger/icons/message-news-attach-kill-offl.png (messenger/icons/message-news-attach-kill-offl.png)
+ skin/classic/messenger/icons/message-news-attach-kill.png (messenger/icons/message-news-attach-kill.png)
+ skin/classic/messenger/icons/message-news-attach-offl.png (messenger/icons/message-news-attach-offl.png)
+ skin/classic/messenger/icons/message-news-attach.png (messenger/icons/message-news-attach.png)
+ skin/classic/messenger/icons/message-news-kill-offl.png (messenger/icons/message-news-kill-offl.png)
+ skin/classic/messenger/icons/message-news-kill.png (messenger/icons/message-news-kill.png)
+ skin/classic/messenger/icons/message-news-new-attach-off.png (messenger/icons/message-news-new-attach-off.png)
+ skin/classic/messenger/icons/message-news-new-attach.png (messenger/icons/message-news-new-attach.png)
+ skin/classic/messenger/icons/message-news-new-offl.png (messenger/icons/message-news-new-offl.png)
+ skin/classic/messenger/icons/message-news-new.png (messenger/icons/message-news-new.png)
+ skin/classic/messenger/icons/message-news-offl.png (messenger/icons/message-news-offl.png)
+ skin/classic/messenger/icons/message-news.png (messenger/icons/message-news.png)
+ skin/classic/messenger/icons/phishing.png (messenger/icons/phishing.png)
+ skin/classic/messenger/icons/readcol.png (messenger/icons/readcol.png)
+ skin/classic/messenger/icons/remote-blocked.png (messenger/icons/remote-blocked.png)
+ skin/classic/messenger/icons/secure.png (messenger/icons/secure.png)
+ skin/classic/messenger/icons/server-local-new.png (messenger/icons/server-local-new.png)
+ skin/classic/messenger/icons/server-local.png (messenger/icons/server-local.png)
+ skin/classic/messenger/icons/server-mail-new.png (messenger/icons/server-mail-new.png)
+ skin/classic/messenger/icons/server-mail.png (messenger/icons/server-mail.png)
+ skin/classic/messenger/icons/server-news-lock.png (messenger/icons/server-news-lock.png)
+ skin/classic/messenger/icons/server-news-new.png (messenger/icons/server-news-new.png)
+ skin/classic/messenger/icons/server-news.png (messenger/icons/server-news.png)
+ skin/classic/messenger/icons/server-remote-lock-new.png (messenger/icons/server-remote-lock-new.png)
+ skin/classic/messenger/icons/server-remote-lock.png (messenger/icons/server-remote-lock.png)
+ skin/classic/messenger/icons/thread-closed-eye.png (messenger/icons/thread-closed-eye.png)
+ skin/classic/messenger/icons/thread-closed-kill.png (messenger/icons/thread-closed-kill.png)
+ skin/classic/messenger/icons/thread-closed-offl-eye.png (messenger/icons/thread-closed-offl-eye.png)
+ skin/classic/messenger/icons/thread-closed-offl-kill.png (messenger/icons/thread-closed-offl-kill.png)
+ skin/classic/messenger/icons/thread-closed.png (messenger/icons/thread-closed.png)
+ skin/classic/messenger/icons/threadcol-threaded.png (messenger/icons/threadcol-threaded.png)
+ skin/classic/messenger/icons/threadcol-unthreaded.png (messenger/icons/threadcol-unthreaded.png)
+ skin/classic/messenger/icons/thread-new-closed-eye.png (messenger/icons/thread-new-closed-eye.png)
+ skin/classic/messenger/icons/thread-new-closed-kill.png (messenger/icons/thread-new-closed-kill.png)
+ skin/classic/messenger/icons/thread-new-closed-offl-eye.png (messenger/icons/thread-new-closed-offl-eye.png)
+ skin/classic/messenger/icons/thread-new-closed-offl-kill.png (messenger/icons/thread-new-closed-offl-kill.png)
+ skin/classic/messenger/icons/thread-new-closed.png (messenger/icons/thread-new-closed.png)
+ skin/classic/messenger/folderPaneExtras.css (messenger/folderPaneExtras.css)
+ skin/classic/messenger/threadPaneExtras.css (messenger/threadPaneExtras.css)
+ skin/classic/messenger/messageKeywords.css (messenger/messageKeywords.css)
+ skin/classic/messenger/icons/new-mail-alert.png (messenger/icons/new-mail-alert.png)
+ skin/classic/messenger/addressbook/abResultsPane.css (messenger/addressbook/abResultsPane.css)
+ skin/classic/messenger/addressbook/addressPanes.css (messenger/addressbook/addressPanes.css)
+ skin/classic/messenger/addressbook/cardDialog.css (messenger/addressbook/cardDialog.css)
+ skin/classic/messenger/addressbook/selectAddressesDialog.css (messenger/addressbook/selectAddressesDialog.css)
+ skin/classic/messenger/addressbook/sidebarPanel.css (messenger/addressbook/sidebarPanel.css)
+ skin/classic/messenger/addressbook/icons/addrbook.png (messenger/addressbook/icons/addrbook.png)
+ skin/classic/messenger/addressbook/icons/ablist.png (messenger/addressbook/icons/ablist.png)
+ skin/classic/messenger/addressbook/icons/remote-addrbook.png (messenger/addressbook/icons/remote-addrbook.png)
+ skin/classic/messenger/addressbook/icons/secure-remote-addrbook.png (messenger/addressbook/icons/secure-remote-addrbook.png)
+ skin/classic/messenger/addressbook/icons/remote-addrbook-error.png (messenger/addressbook/icons/remote-addrbook-error.png)
+ skin/classic/messenger/addressbook/icons/abcard.png (messenger/addressbook/icons/abcard.png)
+ skin/classic/messenger/addressbook/icons/contact-generic.png (messenger/addressbook/icons/contact-generic.png)
+ skin/classic/messenger/addressbook/icons/contact-generic-tiny.png (messenger/addressbook/icons/contact-generic-tiny.png)
+ skin/classic/messenger/smime/msgReadSecurityInfo.css (messenger/smime/msgReadSecurityInfo.css)
+ skin/classic/messenger/smime/msgCompSecurityInfo.css (messenger/smime/msgCompSecurityInfo.css)
+ skin/classic/messenger/smime/msgReadSMIMEOverlay.css (messenger/smime/msgReadSMIMEOverlay.css)
+ skin/classic/messenger/smime/certFetchingStatus.css (messenger/smime/certFetchingStatus.css)
+ skin/classic/messenger/smime/msgHdrViewSMIMEOverlay.css (messenger/smime/msgHdrViewSMIMEOverlay.css)
+ skin/classic/messenger/smime/icons/sbSignOk.png (messenger/smime/icons/sbSignOk.png)
+ skin/classic/messenger/smime/icons/sbSignUnknown.png (messenger/smime/icons/sbSignUnknown.png)
+ skin/classic/messenger/smime/icons/sbSignNotOk.png (messenger/smime/icons/sbSignNotOk.png)
+ skin/classic/messenger/smime/icons/sbCryptoOk.png (messenger/smime/icons/sbCryptoOk.png)
+ skin/classic/messenger/smime/icons/sbCryptoNotOk.png (messenger/smime/icons/sbCryptoNotOk.png)
+ skin/classic/messenger/smime/icons/hdrSignOk.png (messenger/smime/icons/hdrSignOk.png)
+ skin/classic/messenger/smime/icons/hdrSignUnknown.png (messenger/smime/icons/hdrSignUnknown.png)
+ skin/classic/messenger/smime/icons/hdrSignNotOk.png (messenger/smime/icons/hdrSignNotOk.png)
+ skin/classic/messenger/smime/icons/hdrCryptoOk.png (messenger/smime/icons/hdrCryptoOk.png)
+ skin/classic/messenger/smime/icons/hdrCryptoNotOk.png (messenger/smime/icons/hdrCryptoNotOk.png)
+ skin/classic/messenger-newsblog/feed-subscriptions.css (messenger/newsblog/feed-subscriptions.css)
+ skin/classic/messenger-newsblog/icons/rss-feed.png (messenger/newsblog/rss-feed.png)
+#ifdef XP_MACOSX
+ skin/classic/navigator/navigator.css (mac/navigator/navigator.css)
+ skin/classic/navigator/linkToolbar.css (mac/navigator/linkToolbar.css)
+ skin/classic/navigator/pageInfo.css (mac/navigator/pageInfo.css)
+ skin/classic/navigator/tabbrowser.css (mac/navigator/tabbrowser.css)
+ skin/classic/navigator/webDeveloper.css (mac/navigator/webDeveloper.css)
+ skin/classic/navigator/icons/close.png (mac/navigator/icons/close.png)
+ skin/classic/navigator/icons/identity.png (mac/navigator/icons/identity.png)
+ skin/classic/navigator/icons/navigatoricons.png (mac/navigator/icons/navigatoricons.png)
+ skin/classic/navigator/icons/navigatoricons-small.png (mac/navigator/icons/navigatoricons-small.png)
+ skin/classic/navigator/icons/panel-expander-closed.png (mac/navigator/icons/panel-expander-closed.png)
+ skin/classic/navigator/icons/panel-expander-open.png (mac/navigator/icons/panel-expander-open.png)
+ skin/classic/navigator/icons/panel-plus-sign.png (mac/navigator/icons/panel-plus-sign.png)
+ skin/classic/navigator/icons/restore.png (mac/navigator/icons/restore.png)
+#else
+ skin/classic/navigator/navigator.css (navigator/navigator.css)
+ skin/classic/navigator/linkToolbar.css (navigator/linkToolbar.css)
+ skin/classic/navigator/pageInfo.css (navigator/pageInfo.css)
+ skin/classic/navigator/tabbrowser.css (navigator/tabbrowser.css)
+ skin/classic/navigator/webDeveloper.css (navigator/webDeveloper.css)
+ skin/classic/navigator/btn1/first-disabled.png (navigator/btn1/first-disabled.png)
+ skin/classic/navigator/btn1/first-hover.png (navigator/btn1/first-hover.png)
+ skin/classic/navigator/btn1/last-disabled.png (navigator/btn1/last-disabled.png)
+ skin/classic/navigator/btn1/last-hover.png (navigator/btn1/last-hover.png)
+ skin/classic/navigator/btn1/next-disabled.png (navigator/btn1/next-disabled.png)
+ skin/classic/navigator/btn1/next-hover.png (navigator/btn1/next-hover.png)
+ skin/classic/navigator/btn1/previous-disabled.png (navigator/btn1/previous-disabled.png)
+ skin/classic/navigator/btn1/previous-hover.png (navigator/btn1/previous-hover.png)
+ skin/classic/navigator/btn1/top-disabled.png (navigator/btn1/top-disabled.png)
+ skin/classic/navigator/btn1/top-hover.png (navigator/btn1/top-hover.png)
+ skin/classic/navigator/btn1/up-disabled.png (navigator/btn1/up-disabled.png)
+ skin/classic/navigator/btn1/up-hover.png (navigator/btn1/up-hover.png)
+ skin/classic/navigator/icons/close.png (navigator/icons/close.png)
+ skin/classic/navigator/icons/identity.png (navigator/icons/identity.png)
+ skin/classic/navigator/icons/minimize.png (navigator/icons/minimize.png)
+ skin/classic/navigator/icons/navigatoricons.png (navigator/icons/navigatoricons.png)
+ skin/classic/navigator/icons/navigatoricons-small.png (navigator/icons/navigatoricons-small.png)
+ skin/classic/navigator/icons/restore.png (navigator/icons/restore.png)
+ skin/classic/navigator/icons/tab-arrow-left.png (navigator/icons/tab-arrow-left.png)
+ skin/classic/navigator/icons/tab-arrow-right.png (navigator/icons/tab-arrow-right.png)
+#endif
+ skin/classic/navigator/btn1/feeds.png (navigator/btn1/feeds.png)
+ skin/classic/navigator/btn1/first.png (navigator/btn1/first.png)
+ skin/classic/navigator/btn1/last.png (navigator/btn1/last.png)
+ skin/classic/navigator/btn1/next.png (navigator/btn1/next.png)
+ skin/classic/navigator/btn1/previous.png (navigator/btn1/previous.png)
+ skin/classic/navigator/btn1/top.png (navigator/btn1/top.png)
+ skin/classic/navigator/btn1/up.png (navigator/btn1/up.png)
+ skin/classic/navigator/icons/chevron.png (navigator/icons/chevron.png)
+ skin/classic/navigator/icons/popup-blocked.png (navigator/icons/popup-blocked.png)
+ skin/classic/navigator/icons/tab-drag-indicator.png (navigator/icons/tab-drag-indicator.png)
+ skin/classic/navigator/icons/tab-new.png (navigator/icons/tab-new.png)
+[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
+% override chrome://global/skin/about.css chrome://communicator/skin/about.css
+% override chrome://global/skin/aboutSupport.css chrome://communicator/skin/aboutSupport.css
+% override chrome://global/skin/config.css chrome://communicator/skin/config.css
+% override chrome://global/skin/in-content/info-pages.css chrome://communicator/skin/communicator.css
+% override chrome://mozapps/skin/places/defaultFavicon.svg chrome://communicator/skin/places/bookmark-item.svg
diff --git a/comm/suite/themes/classic/linux/communicator/communicator.css b/comm/suite/themes/classic/linux/communicator/communicator.css
new file mode 100644
index 0000000000..2f68d4fbba
--- /dev/null
+++ b/comm/suite/themes/classic/linux/communicator/communicator.css
@@ -0,0 +1,331 @@
+/* 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/. */
+
+/* ==== communicator.css ====================================================
+ == Styles shared everywhere throughout the Communicator suite.
+ ========================================================================== */
+
+@import url("chrome://global/skin/global.css");
+@import url("chrome://communicator/content/communicator.css");
+@import url("chrome://communicator/skin/brand.css");
+@import url("chrome://communicator/skin/button.css");
+@import url("chrome://communicator/skin/toolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.wizard-box {
+ padding: 20px 44px 10px;
+}
+
+/* ::::: toolbar-primary ::::: */
+
+.toolbar-primary {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-primary");
+}
+
+/* .padded is used by autocomplete widgets that don't have an icon. Gross. -dwh */
+textbox:not(.padded) {
+ cursor: default;
+ padding: 0;
+}
+
+textbox[enablehistory="true"] {
+ -moz-appearance: none;
+ border: 0;
+ background-color: transparent;
+}
+
+textbox[enablehistory="true"] > .autocomplete-textbox-container {
+ -moz-appearance: menulist-textfield;
+}
+/* ::::: autocomplete ::::: */
+
+.autocomplete-history-popup {
+ max-height: 25em;
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue) {
+ width: 16px;
+ height: 16px;
+ margin-left: 1px;
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue, directory) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue, file) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+/* ::::: online/offline icons ::::: */
+
+#offline-status[offline="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/offline.png");
+}
+
+#offline-status {
+ list-style-image: url("chrome://communicator/skin/icons/online.png");
+}
+
+/* ::::: spell checker ::::: */
+
+.spell-suggestion {
+ font-weight: bold;
+}
+
+/* ::::: error messages ::::: */
+
+description.error {
+ color: #FF0000;
+}
+
+/* ::::: directional button icons ::::: */
+
+.up {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
+}
+
+.down {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+}
+
+.sidebarTree {
+ border: none;
+ margin: 0px !important;
+}
+
+/* ::::: iconic menus and menuitems ::::: */
+
+menu.menu-iconic > .menu-iconic-left,
+menuitem.menuitem-iconic > .menu-iconic-left {
+ display: -moz-box;
+}
+
+/* Fix to show the menulist-dropmarker under newer GTK3 versions */
+menulist[editable="true"] > .menulist-dropmarker {
+ min-width: 2em;
+}
+
+/* ::::: toolbar print button ::::: */
+#print-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#print-button:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#print-button:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#print-button[disabled="true"] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #print-button:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #print-button:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #print-button[disabled="true"] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+/* ::::: lightweight themes ::::: */
+
+toolbar button:-moz-lwtheme,
+toolbar menulist:-moz-lwtheme:not([open="true"]),
+toolbar textbox:-moz-lwtheme:not([focused="true"]) {
+ opacity: .8;
+}
+
+treecols:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
+
+/* ::::: notification bars ::::: */
+
+.messageImage[value="refresh-blocked"] {
+ list-style-image: url("chrome://communicator/skin/icons/application.png");
+}
+
+.messageImage[value="blocked-plugins"],
+.messageImage[value="disabled-plugins"],
+.messageImage[value="missing-plugins"],
+.messageImage[value="outdated-plugins"],
+.messageImage[value="click-to-play-plugins"],
+.messageImage[value="plugin-crashed"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.messageImage[value="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geo.png");
+}
+
+.messageImage[value="webNotifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
+.messageImage[value="indexedDB-permissions-prompt"],
+.messageImage[value="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+}
+
+.messageImage[value="addon-install-blocked"],
+.messageImage[value="addon-install-cancelled"],
+.messageImage[value="addon-install-complete"],
+.messageImage[value="addon-install-disabled"],
+.messageImage[value="addon-install-failed"],
+.messageImage[value="addon-install-started"],
+.messageImage[value="lwtheme-install-request"],
+.messageImage[value="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.messageImage[value="popup-blocked"] {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+.messageImage[value="blocked-badware-page"] {
+ list-style-image: url("chrome://global/skin/icons/blacklist_favicon.png");
+}
+
+.messageImage[value="EnterInsecureMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-insecure-16.png");
+}
+
+.messageImage[value="EnterSecureMessage"],
+.messageImage[value="BlockedActiveContentMessage"],
+.messageImage[value="BlockedDisplayContentMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-secure-16.png");
+}
+
+.messageImage[value="MixedContentMessage"],
+.messageImage[value="MixedActiveContentMessage"],
+.messageImage[value="MixedDisplayContentMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-broken-16.png");
+}
+
+/* ::::: tree rows ::::: */
+
+/* ::::: dialog header ::::: */
+
+dialogheader {
+ margin: 0px 5px 5px 5px;
+ border: 1px solid;
+ border-top-color: ThreeDDarkShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+ border-left-color: ThreeDDarkShadow;
+ padding: 5px 8px;
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.dialogheader-title {
+ margin: 0px !important;
+ font-size: larger;
+ font-weight: bold;
+}
+
+/* ::::: statusbar ::::: */
+
+statusbar {
+ -moz-appearance: statusbar;
+ border-top: 1px solid ThreeDLightShadow;
+ border-left: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDHighlight;
+ border-bottom: 1px solid ThreeDHighlight;
+ background-color: -moz-Dialog;
+ min-height: 22px;
+}
+
+statusbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-style: none;
+}
+
+statusbarpanel {
+ -moz-appearance: statusbarpanel;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border-left: 1px solid ThreeDHighlight;
+ border-top: 1px solid ThreeDHighlight;
+ border-right: 1px solid ThreeDShadow;
+ border-bottom: 1px solid ThreeDShadow;
+ padding: 0 4px;
+}
+
+statusbarpanel:not(.statusbar-resizerpanel):-moz-lwtheme {
+ -moz-appearance: none;
+ border-top-style: none;
+ border-bottom-style: none;
+ border-inline-start-style: none;
+}
+
+.statusbar-resizerpanel {
+ -moz-box-align: end;
+ -moz-box-pack: end;
+ -moz-appearance: resizerpanel;
+ padding: 0;
+ border: none;
+}
+
+.statusbarpanel-iconic,
+.statusbarpanel-iconic-text {
+ padding: 0 1px;
+}
+
+.statusbarpanel-backgroundbox {
+ -moz-box-align: stretch;
+ padding: 0px;
+}
+
+.statusbarpanel-backgroundbox > .statusbarpanel-contentbox {
+ padding: 0px 1px;
+ -moz-box-align: center;
+}
+
+/* colorpicker button */
+
+colorpicker[type="button"] {
+ -moz-appearance: button;
+ width: 56px;
+ height: 24px;
+}
+
+/* filefield */
+
+.fileFieldContentBox {
+ padding-inline-start: 3px;
+ margin-inline-start: -4px;
+ margin-inline-end: -4px;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
diff --git a/comm/suite/themes/classic/linux/communicator/feed-subscribe.css b/comm/suite/themes/classic/linux/communicator/feed-subscribe.css
new file mode 100644
index 0000000000..55dfc55e26
--- /dev/null
+++ b/comm/suite/themes/classic/linux/communicator/feed-subscribe.css
@@ -0,0 +1,149 @@
+/* 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/. */
+
+html {
+ background: -moz-Dialog;
+ font: message-box;
+}
+
+#feedBody {
+ border: 1px solid ThreeDShadow;
+ padding: 3em;
+ padding-inline-start: 30px;
+ margin: 2em auto;
+ background: -moz-Field;
+}
+
+#feedHeaderContainer {
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ margin: -4em auto 0 auto;
+ background-color: InfoBackground;
+ -moz-appearance: -moz-gtk-info-bar;
+}
+
+#feedHeader {
+ margin-top: 4.9em;
+ margin-bottom: 1em;
+ margin-inline-start: 1.4em;
+ margin-inline-end: 1em;
+ padding-inline-start: 2.9em;
+ font-size: 110%;
+ color: -moz-gtk-info-bar-text;
+}
+
+.feedBackground {
+ background: url("chrome://communicator/skin/icons/feedIcon.png") 0% 10% no-repeat;
+}
+
+.videoPodcastBackground {
+ background: url("chrome://communicator/skin/icons/videoFeedIcon.png") 0% 10% no-repeat;
+}
+
+.audioPodcastBackground {
+ background: url("chrome://communicator/skin/icons/audioFeedIcon.png") 0% 10% no-repeat;
+}
+
+#feedHeader[dir="rtl"] {
+ background-position: 100% 10%;
+}
+
+#feedIntroText {
+ display: none;
+}
+
+#feedHeader[firstrun="true"] #feedIntroText {
+ padding-top: 0.1em;
+ padding-inline-start: 0.6em;
+ display: block;
+}
+
+#feedHeader[firstrun="true"] > #feedSubscribeLine {
+ padding-inline-start: 1.8em;
+}
+
+#feedSubscribeLine {
+ padding-top: 0.2em;
+}
+
+/* Don't print subscription UI */
+@media print {
+ #feedHeaderContainer {
+ display: none;
+ }
+}
+
+body {
+ margin: 0;
+ padding: 0 3em;
+ color: -moz-fieldText;
+ font: message-box;
+}
+
+h1 {
+ font-size: 160%;
+ border-bottom: 2px solid ThreeDLightShadow;
+ margin: 0 0 .2em 0;
+}
+
+h2 {
+ font-size: 110%;
+ font-weight: normal;
+ margin: 0 0 .6em 0;
+}
+
+#feedTitleLink {
+ float: right;
+ margin-inline-start: .6em;
+ margin-inline-end: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+a[href] img {
+ border: none;
+}
+
+#feedTitleContainer {
+ margin-inline-start: 0;
+ margin-inline-end: .6em;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#feedTitleImage {
+ margin-inline-start: .6em;
+ margin-inline-end: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ max-width: 300px;
+ max-height: 150px;
+}
+
+.feedEntryContent {
+ font-size: 110%;
+}
+
+.lastUpdated {
+ font-size: 85%;
+ font-weight: normal;
+}
+
+.type-icon {
+ vertical-align: bottom;
+ height: 16px;
+ width: 16px;
+}
+
+.enclosures {
+ border: 1px solid ThreeDShadow;
+ padding: 1em;
+ margin: 1em auto;
+ background: -moz-Dialog;
+}
+
+.enclosure {
+ vertical-align: middle;
+ margin-left: 2px;
+}
diff --git a/comm/suite/themes/classic/linux/communicator/numberbox.css b/comm/suite/themes/classic/linux/communicator/numberbox.css
new file mode 100644
index 0000000000..ba38649a2b
--- /dev/null
+++ b/comm/suite/themes/classic/linux/communicator/numberbox.css
@@ -0,0 +1,29 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+textbox[type="number"] {
+ -moz-appearance: none;
+ padding: 0 !important;
+ border: none;
+ cursor: default;
+ background-color: transparent;
+}
+
+html|*.numberbox-input {
+ text-align: right;
+}
+
+.numberbox-input-box {
+ -moz-box-align: center;
+ -moz-appearance: spinner-textfield;
+ margin-right: -1px;
+ padding: 3px;
+}
+
+textbox[hidespinbuttons="true"] > .numberbox-input-box {
+ -moz-appearance: textfield;
+}
diff --git a/comm/suite/themes/classic/linux/communicator/preferences.css b/comm/suite/themes/classic/linux/communicator/preferences.css
new file mode 100644
index 0000000000..8b7cf445d3
--- /dev/null
+++ b/comm/suite/themes/classic/linux/communicator/preferences.css
@@ -0,0 +1,66 @@
+/* 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/. */
+
+/* Styles used by all preference windows and panes of SeaMonkey */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Main Window ::::: */
+
+prefwindow {
+ -moz-appearance: window;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+ font: message-box;
+ padding-top: 8px;
+ padding-bottom: 0px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+prefpane {
+ padding: 8px;
+}
+
+prefwindow[type="child"] {
+ padding: 8px;
+}
+
+prefwindow[type="child"] > prefpane {
+ padding: 0px;
+}
+
+.prefWindow-dlgbuttons {
+ padding-bottom: 8px;
+ padding-inline-start: 8px;
+ padding-inline-end: 8px;
+}
+
+prefwindow[type="child"] .prefWindow-dlgbuttons {
+ padding: 0px;
+}
+
+radio[pane] {
+ -moz-appearance: none;
+ min-width: 4.5em;
+ margin: 0;
+ padding: 3px;
+ color: -moz-FieldText;
+}
+
+.paneSelector {
+ -moz-appearance: listbox;
+ margin: 8px 8px 0 8px;
+ padding: 0;
+}
+
+.paneButtonIcon {
+ width: 32px;
+ height: 32px;
+}
+
+radio[pane][selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
diff --git a/comm/suite/themes/classic/linux/communicator/sanitizeDialog.css b/comm/suite/themes/classic/linux/communicator/sanitizeDialog.css
new file mode 100644
index 0000000000..9d119134fd
--- /dev/null
+++ b/comm/suite/themes/classic/linux/communicator/sanitizeDialog.css
@@ -0,0 +1,49 @@
+/* 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/. */
+
+#sanitizeDurationChoice {
+ margin-inline-end: 0;
+}
+
+/* Align the duration label with the warning box and item list */
+#sanitizeDurationLabel {
+ margin-inline-start: 3px;
+}
+
+
+/* Hide the duration dropdown suffix label if it's empty. Otherwise it
+ takes up a little space, causing the end of the dropdown to not be aligned
+ with the warning box. */
+#sanitizeDurationSuffixLabel[value=""] {
+ display: none;
+}
+
+
+/* Sanitize everything warning box */
+#sanitizeWarningBox {
+ background-color: Window;
+ border: 1px solid ThreeDDarkShadow;
+ border-radius: 5px;
+ padding: 16px;
+}
+
+#sanitizeWarningIcon {
+ list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=dialog");
+ padding: 0;
+ margin: 0;
+}
+
+#sanitizeWarningDescBox {
+ padding: 0 16px;
+ margin: 0;
+}
+
+
+/* Align the last dialog button with the end of the warning box */
+.dialog-button-box {
+ margin-inline-end: 0;
+}
+.dialog-button[dlgtype="accept"] {
+ margin-inline-end: 0;
+}
diff --git a/comm/suite/themes/classic/linux/communicator/spinbuttons.css b/comm/suite/themes/classic/linux/communicator/spinbuttons.css
new file mode 100644
index 0000000000..3e333bb474
--- /dev/null
+++ b/comm/suite/themes/classic/linux/communicator/spinbuttons.css
@@ -0,0 +1,24 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+spinbuttons {
+ -moz-appearance: spinner;
+ cursor: default;
+}
+
+.spinbuttons-button {
+ min-width: 13px;
+ min-height: 11px;
+ margin: 0 !important;
+}
+
+.spinbuttons-up {
+ -moz-appearance: spinner-upbutton;
+}
+
+.spinbuttons-down {
+ -moz-appearance: spinner-downbutton;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/aboutPrivateBrowsing.css b/comm/suite/themes/classic/mac/communicator/aboutPrivateBrowsing.css
new file mode 100644
index 0000000000..94cb3b83c8
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/aboutPrivateBrowsing.css
@@ -0,0 +1,85 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#warningScreen {
+ background-color: -moz-Dialog;
+}
+
+#warningBox {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ margin-left: 1em;
+ margin-right: 1em;
+}
+
+#warningInnerBox {
+ max-width: 50em;
+}
+
+#warningTitle {
+ margin: 0 0 .6em 0;
+ font-size: 160%;
+ border-bottom: 1px solid ThreeDLightShadow
+}
+
+#warningText {
+ font-size: 110%;
+ margin-left: 0;
+}
+
+/* Pick the desired icons depending on the window's context */
+
+#warningBox.private > #warningBoxIcon {
+ list-style-image: url("chrome://global/skin/icons/information-large.png");
+ width: 48px;
+ height: 48px;
+ margin-inline-end: 1.5em;
+}
+
+#warningBox.normal > #warningBoxIcon {
+ list-style-image: url("chrome://global/skin/icons/question-large.png");
+ width: 48px;
+ height: 48px;
+ margin-inline-end: 1.5em;
+}
+
+#trackWarnBox {
+ margin-top: 0.6em;
+ margin-inline-end: 4em;
+ -moz-box-align: start;
+}
+
+#trackWarnIcon {
+ list-style-image: url("chrome://communicator/skin/icons/warning-24.png");
+ width: 24px;
+ height: 24px;
+ margin-inline-end: 1em;
+}
+
+/* Define additional styles to look similar to the netError/certError pages */
+
+#warningTitle {
+ font-weight: bold;
+}
+#warningStatus {
+ margin: 0.4em 0 1.2em 0;
+ padding-bottom: 1.2em;
+ border-bottom: 1px solid ThreeDLightShadow;
+ font-size: 135%;
+}
+
+#warningInnerBox > button {
+ margin: 0.8em 0 1em 0;
+}
+
+#warningOuterBox > vbox > label,
+#warningInnerBox > description {
+ margin-inline-start: 0;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/aboutSessionRestore.css b/comm/suite/themes/classic/mac/communicator/aboutSessionRestore.css
new file mode 100644
index 0000000000..d949105f50
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/aboutSessionRestore.css
@@ -0,0 +1,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/. */
+
+#tabList {
+ width: 100%;
+ height: 12em;
+}
+
+treechildren::-moz-tree-image(icon),
+treechildren::-moz-tree-image(noicon) {
+ padding-right: 2px;
+ margin: 0px 2px;
+ width: 16px;
+ height: 16px;
+}
+
+treechildren::-moz-tree-image(noicon) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+treechildren::-moz-tree-image(container, noicon) {
+ list-style-image: url("chrome://global/skin/tree/folder.png");
+}
+
+treechildren::-moz-tree-checkbox(checked) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
+treechildren::-moz-tree-checkbox(partial) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
+}
+
+#buttons {
+ margin-inline-start: 80px;
+}
+#buttons > button {
+ margin-top: 2em;
+ margin-inline-start: 5px;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/button.css b/comm/suite/themes/classic/mac/communicator/button.css
new file mode 100644
index 0000000000..73b87ae2a9
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/button.css
@@ -0,0 +1,65 @@
+/* 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/. */
+
+/* ==== button.css ==========================================================
+ == Styles for special buttons in the Communicator suite.
+ ========================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: large toolbar buttons ::::: */
+
+.toolbarbutton-1,
+.toolbarbutton-1 > .toolbarbutton-menubutton-button {
+ -moz-box-orient: vertical;
+}
+
+.toolbarbutton-1[open],
+.toolbarbutton-1[open] > .toolbarbutton-menubutton-button {
+ text-shadow: none;
+}
+
+.toolbarbutton-1[type="menu-button"] {
+ -moz-box-orient: horizontal;
+ padding: 0;
+}
+
+.toolbarbutton-1[type="menu"] {
+ -moz-binding: url("chrome://communicator/content/bindings/generalBindings.xml#menu-vertical");
+}
+
+/* Override global/toolbarbutton.css */
+.toolbarbutton-1 > .toolbarbutton-text,
+.toolbarbutton-menubutton-button > .toolbarbutton-text {
+ margin: 2px 6px !important;
+}
+
+toolbar[mode="icons"] .toolbarbutton-1,
+toolbar[mode="icons"] .toolbarbutton-menubutton-button,
+toolbar[iconsize="small"] .toolbarbutton-1,
+toolbar[iconsize="small"] .toolbarbutton-menubutton-button {
+ min-width: 0px;
+}
+
+toolbar[mode="icons"] .toolbarbutton-text,
+toolbar[mode="text"] .toolbarbutton-icon {
+ display: none;
+}
+
+toolbar[mode="text"] .toolbarbutton-1,
+toolbar[mode="text"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
+ -moz-box-orient: horizontal;
+}
+
+toolbar[labelalign="end"] .toolbarbutton-1,
+toolbar[labelalign="end"] .toolbarbutton-1 > .toolbarbutton-menubutton-button,
+toolbar[labelalign="end"] .toolbarbutton-1 > hbox > vbox {
+ -moz-box-orient: horizontal;
+}
+
+/* ::::: taskbuttons ::::: */
+
+.taskbutton {
+ padding: 1px;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/communicator.css b/comm/suite/themes/classic/mac/communicator/communicator.css
new file mode 100644
index 0000000000..ae70b24ee7
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/communicator.css
@@ -0,0 +1,285 @@
+/* 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/. */
+
+/* ==== communicator.css ====================================================
+ == Styles shared everywhere throughout the Communicator suite.
+ ========================================================================== */
+
+@import url("chrome://global/skin/global.css");
+@import url("chrome://communicator/content/communicator.css");
+@import url("chrome://communicator/skin/brand.css");
+@import url("chrome://communicator/skin/button.css");
+@import url("chrome://communicator/skin/toolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.wizard-box {
+ padding: 20px 44px 10px;
+}
+
+#titlebar:not(:-moz-lwtheme) {
+ display: none;
+}
+
+#titlebar {
+ height: 22px;
+ -moz-window-dragging: drag;
+}
+
+#titlebar-spacer {
+ pointer-events: none;
+}
+
+/* On OS X, window controls are always at the left side of the window */
+#titlebar-buttonbox-container:-moz-locale-dir(ltr) {
+ -moz-box-ordinal-group: 0;
+ -moz-box-align: start;
+}
+
+#titlebar-buttonbox-container:-moz-lwtheme {
+ margin-top: 3px;
+}
+
+#titlebar-buttonbox {
+ -moz-appearance: -moz-window-button-box;
+ margin-left: 7px;
+}
+
+/* ::::: toolbar-primary ::::: */
+
+.toolbar-primary {
+ -moz-binding: url("chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-primary");
+}
+
+#throbber-box {
+ -moz-window-dragging: drag;
+}
+
+/* ::::: grippies ::::: */
+
+grippy {
+ display: none;
+}
+
+/* ::::: autocomplete ::::: */
+
+.autocomplete-history-popup {
+ max-height: 285px; /* 15 rows */
+}
+
+.autocomplete-history-dropmarker {
+ -moz-appearance: none;
+ border: none;
+ background-color: transparent;
+ margin: 0px;
+ padding: 0px;
+ list-style-image: url("chrome://communicator/skin/icons/autocomplete-dropmarker.png");
+}
+
+/* ::::: online/offline icons ::::: */
+
+#offline-status[offline="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/offline.png");
+}
+
+#offline-status {
+ list-style-image: url("chrome://communicator/skin/icons/online.png");
+}
+
+/* ::::: security button icons ::::: */
+
+#security-button {
+ list-style-image: url("chrome://communicator/skin/icons/lock-insecure.png");
+}
+
+#security-button[level="high"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-secure.png");
+}
+
+#security-button[level="broken"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-broken.png");
+}
+
+/* ::::: spell checker ::::: */
+
+.spell-suggestion {
+ font-weight: bold;
+}
+
+/* ::::: error messages ::::: */
+
+description.error {
+ color: #FF0000;
+}
+
+/* ::::: directional button icons ::::: */
+
+.up {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
+}
+
+.down {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+}
+
+.sidebarTree {
+ border: none;
+ margin: 0px !important;
+}
+
+/* ::::: toolbar print button ::::: */
+#print-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#print-button:hover:active,
+#print-button[open] {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#print-button[disabled="true"] {
+ -moz-image-region: rect(0 89px 29px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #print-button:hover:active,
+toolbar[iconsize="small"] > #print-button[open] {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #print-button[disabled="true"] {
+ -moz-image-region: rect(0 59px 19px 40px) !important;
+}
+
+/* ::::: lightweight themes ::::: */
+
+toolbar textbox:-moz-lwtheme:not([focused="true"]) {
+ opacity: 0.9;
+}
+
+treecols:-moz-lwtheme {
+ text-shadow: none;
+}
+
+/* ::::: notification bars ::::: */
+
+.messageImage[value="refresh-blocked"] {
+ list-style-image: url("chrome://communicator/skin/icons/application.png");
+}
+
+.messageImage[value="plugin-crashed"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.messageImage[value="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geo.png");
+}
+
+.messageImage[value="persistent-storage"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-icons.svg#persistent-storage");
+ width: 16px;
+ height: 16px;
+}
+
+.messageImage[value="webNotifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
+.messageImage[value="indexedDB-permissions-prompt"],
+.messageImage[value="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+}
+
+.messageImage[value="addon-install-blocked"],
+.messageImage[value="addon-install-cancelled"],
+.messageImage[value="addon-install-complete"],
+.messageImage[value="addon-install-disabled"],
+.messageImage[value="addon-install-failed"],
+.messageImage[value="addon-install-started"],
+.messageImage[value="lwtheme-install-request"],
+.messageImage[value="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.messageImage[value="popup-blocked"] {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+.messageImage[value="blocked-badware-page"] {
+ list-style-image: url("chrome://global/skin/icons/blacklist_favicon.png");
+}
+
+/* ::::: dialog header ::::: */
+
+dialogheader {
+ margin: 0 5px 5px;
+ padding: 5px 8px;
+}
+
+.dialogheader-title {
+ margin: 0 !important;
+ font-size: larger;
+ font-weight: bold;
+ display: none;
+}
+
+.dialogheader-description {
+ font-weight: bold !important;
+}
+
+/* ::::: statusbar ::::: */
+
+statusbar {
+ min-width: 1px; /* DON'T DELETE!
+ Prevents hiding of scrollbars in browser when window is made smaller.*/
+ min-height: 15px !important;
+ margin: 0px !important;
+ /* need to use padding-inline-end when/if bug 631729 gets fixed: */
+ padding: 0px 16px 1px 1px;
+ -moz-appearance: statusbar;
+ text-shadow: rgba(255, 255, 255, 0.4) 0 1px;
+}
+
+statusbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-style: none;
+ text-shadow: inherit;
+}
+
+statusbarpanel {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ padding: 0 4px;
+}
+
+.statusbarpanel-iconic {
+ padding: 0px;
+}
+
+.statusbarpanel-backgroundbox {
+ -moz-box-align: stretch;
+ padding: 0px;
+}
+
+.statusbarpanel-backgroundbox > .statusbarpanel-contentbox {
+ padding: 0px 1px;
+ -moz-box-align: center;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/config.css b/comm/suite/themes/classic/mac/communicator/config.css
new file mode 100644
index 0000000000..b83b219d28
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/config.css
@@ -0,0 +1,79 @@
+/* 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/. */
+
+@import url("chrome://global/skin/global.css");
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#warningScreen {
+ background-color: -moz-Dialog;
+}
+
+#warningBox {
+ background-image: url("chrome://global/skin/icons/warning-large.png");
+ background-repeat: no-repeat;
+ background-position: 30px 3em;
+ max-width: 55em;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 64px;
+ margin-inline-start: 1em;
+ margin-inline-end: 1em;
+}
+
+.title, .description {
+ max-width: 50em;
+ padding-inline-start: 3em;
+}
+
+#warningTitle {
+ margin: 0 0 .6em 0;
+ font-size: 160%;
+ border-bottom: 1px solid ThreeDLightShadow;
+}
+
+#warningText {
+ font-size: 110%;
+ margin-inline-start: 0;
+}
+
+#warningButton {
+ margin-top: 0.6em;
+}
+
+#showWarningNextTime {
+ margin-top: 0.6em;
+}
+
+#configTreeBody::-moz-tree-cell-text(user) {
+ font-weight: bold;
+}
+
+#configTreeBody::-moz-tree-cell-text(locked) {
+ font-style: italic;
+}
+
+#configTree {
+ margin: 0;
+ -moz-appearance: none;
+}
+
+#filterRow {
+ background: linear-gradient(#E8E8E8, #D0D0D0) repeat-x;
+ border-bottom: 1px solid #888;
+ padding: 1px 2px 0;
+}
+
+#filterRow > label {
+ margin: 0 4px;
+ color: #6D6D6D;
+ text-shadow: 0 1px rgba(255, 255, 255, .4);
+ font-weight: bold;
+}
+
+#textbox {
+ max-width: 35em;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/console/console-error-caret.png b/comm/suite/themes/classic/mac/communicator/console/console-error-caret.png
new file mode 100644
index 0000000000..44b78aa29c
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/console/console-error-caret.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/console/console-error-dash.png b/comm/suite/themes/classic/mac/communicator/console/console-error-dash.png
new file mode 100644
index 0000000000..ab07898eff
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/console/console-error-dash.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/console/console.css b/comm/suite/themes/classic/mac/communicator/console/console.css
new file mode 100644
index 0000000000..0bca72cb8d
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/console/console.css
@@ -0,0 +1,164 @@
+/* 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/. */
+
+/* ===== console.css ====================================================
+ == Styles used by the Error Console window.
+ ======================================================================= */
+
+/* View buttons */
+@import "chrome://global/skin/viewbuttons.css";
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.console-box {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ overflow: auto;
+}
+
+/* ::::: console rows ::::: */
+
+.console-row {
+ border-bottom: 1px solid #A3A3A3;
+ padding: 4px;
+}
+
+.console-row-file {
+ color: #505050;
+}
+
+.console-row-msg > label:first-child {
+ font-weight: bold;
+}
+
+.console-row-msg > label, .comsole-row-msg > description, .console-error-msg, .console-row-file, .console-row-code {
+ margin: 2px;
+}
+
+.console-row-file > label {
+ margin: 0;
+}
+
+.console-msg-text {
+ white-space: pre-wrap !important;
+}
+.console-icon {
+ list-style-image: inherit;
+ padding-right: 6px;
+ padding-left: 6px;
+}
+
+/* ..... error rows ..... */
+
+.console-row-code {
+ color: #0000BB;
+ font-size: larger;
+}
+
+.console-dots,
+.console-caret {
+ height: 9px;
+}
+
+.console-dots {
+ background: url("chrome://communicator/skin/console/console-error-dash.png") repeat-x top;
+}
+
+.console-caret {
+ width: 7px;
+ background: url("chrome://communicator/skin/console/console-error-caret.png") no-repeat top;
+}
+
+/* ..... message rows ..... */
+
+.console-row[type="message"] {
+ font-family: monospace;
+}
+
+/* ..... selected state ..... */
+
+.console-row[selected="true"] {
+ background-color: #3D80DF !important;
+ color: #FFF;
+}
+
+.console-row-code[selected="true"],
+.console-row-content[selected="true"] > .console-row-file,
+.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
+ color: #FFF !important;
+}
+
+/* ::::: row colors ::::: */
+
+.console-row[type="error"],
+.console-row[type="exception"] {
+ background-color: #FFD0DC;
+}
+
+.console-row[type="warning"] {
+ background-color: #F8F3CC;
+}
+
+.console-row[type="message"] {
+ background-color: #D3EDFF;
+}
+
+/* ::::: toolbars ::::: */
+
+#ToolbarEval {
+ -moz-appearance: none;
+ background: linear-gradient(#E8E8E8, #D0D0D0) repeat-x; /* @scopeBarBackground@ */
+ border-bottom: 1px solid #888; /* @scopeBarSeparatorBorder@ */
+ padding: 2px;
+}
+
+#ToolbarEval > label {
+ font-weight: bold;
+ color: #6D6D6D; /* @scopeBarTitleColor@ */
+}
+
+#TextfieldEval {
+ margin: 2px !important;
+}
+
+#ButtonEval {
+ margin: 0 4px;
+ padding: 1px 10px;
+ -moz-appearance: none;
+ border-radius: 10000px;
+ border: 1px solid rgba(0, 0, 0, 0.35); /* @roundButtonBorder@ */
+ background: linear-gradient(#F6F6F6, #E9E9E9); /* @roundButtonBackground@ */
+ box-shadow: 0 1px rgba(255, 255, 255, 0.5), inset 0 1px 1px rgba(255, 255, 255, 0.5); /* @roundButtonShadow@ */
+}
+
+#ButtonEval:hover:active {
+ text-shadow: 0 1px rgba(255, 255, 255, 0.4); /* @loweredShadow@ */
+ background: #DADADA; /* @roundButtonPressedBackground@ */
+ box-shadow: 0 1px rgba(255, 255, 255, 0.4), inset 0 1px 3px rgba(0, 0, 0, 0.2); /* @roundButtonPressedShadow@ */
+}
+
+toolbarseparator {
+ min-height: 1em;
+ background-image: none;
+}
+
+/* Toolbar icons */
+
+#ToolbarMode {
+ -moz-box-pack: center;
+}
+
+#ToolbarMode toolbarbutton > .toolbarbutton-icon {
+ display: none;
+}
+
+#Console\:clear {
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+ -moz-appearance: toolbarbutton;
+ font: menu;
+ text-shadow: 0 1px rgba(255, 255, 255, 0.4); /* @loweredShadow@ */
+ margin: 4px 0 9px;
+ padding: 0 1px;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/customizeToolbar.css b/comm/suite/themes/classic/mac/communicator/customizeToolbar.css
new file mode 100644
index 0000000000..bcedb2b99a
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/customizeToolbar.css
@@ -0,0 +1,38 @@
+/* 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/. */
+
+#palette-box {
+ margin-top: 2px;
+ -moz-appearance: listbox;
+ margin: 0 0 10px;
+}
+
+#palette-box > toolbarpaletteitem {
+ padding: 8px 2px;
+ margin: 0 8px;
+}
+
+#main-box {
+ padding: 12px;
+}
+
+#main-box > separator {
+ -moz-appearance: none;
+ border-bottom: none;
+}
+
+#instructions {
+ font: menu;
+ font-weight: bold;
+ line-height: 16pt;
+}
+
+hbox button {
+ font: menu;
+}
+
+#main-box > box > button {
+ min-height: 19px; /* aqua size for small buttons */
+ font: message-box;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/datetimepicker.css b/comm/suite/themes/classic/mac/communicator/datetimepicker.css
new file mode 100644
index 0000000000..24e54aae34
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/datetimepicker.css
@@ -0,0 +1,94 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+datepicker {
+ padding: 0 0 1px;
+ margin: 4px;
+ border: none;
+}
+
+.datetimepicker-input-box {
+ -moz-appearance: textfield;
+ cursor: text;
+ margin-right: 4px;
+ margin-bottom: 2px;
+ padding: 0;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.datetimepicker-input-subbox {
+ width: 1.6em;
+}
+
+html|*.datetimepicker-input {
+ text-align: end;
+}
+
+.datetimepicker-separator {
+ margin: 0 !important;
+}
+
+.datetimepicker-year {
+ width: 3.2em;
+}
+
+.datepicker-dropmarker {
+ margin-bottom: 2px;
+}
+
+datepicker[readonly="true"] {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+datepicker[disabled="true"] {
+ cursor: default;
+ background-color: -moz-Dialog;
+ color: GrayText;
+}
+
+.datepicker-mainbox {
+ margin: 2px 4px;
+ border: 1px solid ThreeDShadow;
+ background-color: #EEEEEE;
+ color: -moz-DialogText;
+}
+
+.datepicker-popupgrid > .datepicker-mainbox {
+ margin: 0;
+ border: none;
+}
+
+.datepicker-gridlabel, .datepicker-weeklabel {
+ text-align: center;
+}
+
+.datepicker-gridlabel[today="true"] {
+ background-color: darkgrey;
+ color: white;
+}
+
+.datepicker-gridlabel[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.datepicker-button {
+ -moz-appearance: none;
+ min-width: 8px;
+ padding: 0;
+}
+
+.datepicker-previous {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.gif");
+ transform: scaleX(-1);
+}
+
+.datepicker-next {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.gif");
+}
diff --git a/comm/suite/themes/classic/mac/communicator/downloads/downloadButtons.png b/comm/suite/themes/classic/mac/communicator/downloads/downloadButtons.png
new file mode 100644
index 0000000000..6f3c7c9855
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/downloads/downloadButtons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/downloads/downloadmanager.css b/comm/suite/themes/classic/mac/communicator/downloads/downloadmanager.css
new file mode 100644
index 0000000000..8f84152c48
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/downloads/downloadmanager.css
@@ -0,0 +1,142 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Note to themers:
+ On rows and all cells, those properties for download states are available:
+ active, inactive, resumable, paused, downloading, finished, failed, canceled, blocked
+*/
+
+#clearListButton {
+ -moz-appearance: toolbarbutton;
+ text-shadow: 0 1px rgba(255, 255, 255, 0.4);
+ margin: 0 6px;
+ min-height: 18px;
+ min-width: 0;
+}
+
+#clearListButton:-moz-lwtheme:not([disabled="true"]) {
+ color: inherit;
+ text-shadow: inherit;
+}
+
+treechildren::-moz-tree-image {
+ margin-inline-start: -1px;
+}
+
+treechildren::-moz-tree-image(Name) {
+ margin-inline-end: 2px;
+}
+
+#ActionPlay {
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+treechildren::-moz-tree-image(ActionPlay, downloading, resumable),
+#pauseButton {
+ /* pause */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+treechildren::-moz-tree-image(ActionPlay, paused, resumable),
+#resumeButton {
+ /* resume */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+treechildren::-moz-tree-image(ActionPlay, failed),
+treechildren::-moz-tree-image(ActionPlay, canceled),
+#retryButton {
+ /* retry */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+#ActionStop {
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+treechildren::-moz-tree-image(ActionStop, active),
+#cancelButton {
+ /* cancel */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+treechildren::-moz-tree-image(ActionStop, inactive) {
+ /* remove */
+ list-style-image: url("chrome://communicator/skin/downloads/dl-remove.png");
+ -moz-image-region: auto;
+}
+
+/* There's no way this will look like a native progressbar, but we can at least
+ try to mimic the aqua, graphite and non-active colors. */
+treechildren::-moz-tree-progressmeter {
+ background: url("chrome://communicator/skin/downloads/progressBg.png") repeat-x;
+ color: rgba(0, 115, 255, 0.5);
+ border: none;
+ padding-bottom: 1px;
+ margin-top: 3px;
+}
+
+@media (-moz-mac-graphite-theme) {
+ treechildren::-moz-tree-progressmeter {
+ color: rgba(43, 71, 106, 0.5);
+ }
+}
+
+treechildren:-moz-window-inactive::-moz-tree-progressmeter {
+ color: rgba(0, 0, 0, 0.1);
+}
+
+/* progress dialogs */
+
+#dlProgressWindow {
+ padding: 14px;
+}
+
+/* focusable label, focus ring like .link-text but not a link */
+#fileName, #fileSource {
+ border: 1px solid transparent;
+ /* 1px is used for border, make margins smaller by that */
+ margin-top: 0px;
+ margin-bottom: 1px;
+ margin-inline-start: 5px;
+ margin-inline-end: 4px;
+}
+
+#fileName:focus,
+#fileSource:focus {
+ border: 1px dotted -moz-DialogText;
+}
+
+#fileName {
+ font-weight: bold;
+ margin-bottom: 6px;
+}
+
+.mini-button {
+ -moz-appearance: none;
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ margin: 0;
+ min-width: 0;
+ min-height: 0;
+}
+
+.mini-button > .button-box {
+ -moz-appearance: none;
+ padding: 0 !important;
+}
+
+#progressBox {
+ margin-top: 6px;
+ margin-bottom: 6px;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/downloads/progressBg.png b/comm/suite/themes/classic/mac/communicator/downloads/progressBg.png
new file mode 100644
index 0000000000..c3261526f7
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/downloads/progressBg.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/helpviewer/dropmark-nav.png b/comm/suite/themes/classic/mac/communicator/helpviewer/dropmark-nav.png
new file mode 100644
index 0000000000..e841574c39
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/helpviewer/dropmark-nav.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/helpviewer/help.css b/comm/suite/themes/classic/mac/communicator/helpviewer/help.css
new file mode 100644
index 0000000000..1b0947d488
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/helpviewer/help.css
@@ -0,0 +1,136 @@
+/* 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/. */
+
+@import url("chrome://communicator/skin/");
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#HelpToolbar {
+ padding-inline-start: 5px;
+}
+
+#HelpToolbar toolbarbutton {
+ min-width: 0px;
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-box-orient: horizontal !important;
+}
+
+#HelpToolbar toolbarbutton[type="menu-button"] {
+ margin: 0 2px 0 2px;
+ padding: 3px 1px 3px 1px;
+
+}
+
+/* this element contains the icon and the label */
+#HelpToolbar .toolbarbutton-menubutton-button {
+ margin: 0px !important;
+ padding: 0px !important;
+ border: 0px !important;
+}
+
+/* Hide labels for the toolbar because we really don't need them what with the
+ tooltips */
+#HelpToolbar .toolbarbutton-text {
+ display: none;
+}
+
+/* Set the minimum sidebar width so the help contents aren't squeezed together.*/
+#help-sidebar { min-width: 30px; width: 20em; max-width: 25em; }
+
+#help-back-button toolbarbutton,
+#help-forward-button toolbarbutton {
+ list-style-image: inherit;
+ -moz-image-region: inherit;
+}
+
+#help-back-button,
+#help-back-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#help-back-button:not([disabled="true"]):hover:active,
+#help-back-button[open]:not([disabled="true"]) {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#help-back-button[disabled="true"] {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#help-forward-button,
+#help-forward-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#help-forward-button:not([disabled="true"]):hover:active,
+#help-forward-button[open]:not([disabled="true"]) {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#help-forward-button[disabled="true"] {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#help-home-button,
+#help-home-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#help-home-button:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#help-home-button[disabled="true"] {
+ -moz-image-region: rect(120px 89px 149px 60px);
+}
+
+#help-print-button,
+#help-print-button:not([disabled="true"]):hover {
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#help-print-button:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#help-print-button[disabled="true"] {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+/* Style the back/forward dropmarks to connect them to the buttons */
+
+#help-back-button > .toolbarbutton-menubutton-dropmarker,
+#help-forward-button > .toolbarbutton-menubutton-dropmarker {
+ margin-top: 3px;
+}
+
+#help-back-button > .toolbarbutton-menubutton-dropmarker,
+#help-back-button:hover > .toolbarbutton-menubutton-dropmarker,
+#help-back-button:hover:active > .toolbarbutton-menubutton-dropmarker,
+#help-forward-button > .toolbarbutton-menubutton-dropmarker,
+#help-forward-button:hover > .toolbarbutton-menubutton-dropmarker,
+#help-forward-button:hover:active > .toolbarbutton-menubutton-dropmarker {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+ -moz-image-region: auto;
+}
+
+#help-back-button[disabled="true"] > .toolbarbutton-menubutton-dropmarker,
+#help-forward-button[disabled="true"] > .toolbarbutton-menubutton-dropmarker {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.png") !important;
+ -moz-image-region: auto !important;
+}
+
+#help-sidebar-splitter {
+ border-inline-end: 1px solid #BEBEBE;
+}
+
+/* make findbar appear above content */
+#appcontent {
+ -moz-box-direction: reverse;
+}
+
+/* style findbar for being on top */
+#FindToolbar {
+ border-top: none;
+ border-bottom: 1px solid #888888;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/icons/autocomplete-dropmarker.png b/comm/suite/themes/classic/mac/communicator/icons/autocomplete-dropmarker.png
new file mode 100644
index 0000000000..e48d044526
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/autocomplete-dropmarker.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/communicatoricons-small.png b/comm/suite/themes/classic/mac/communicator/icons/communicatoricons-small.png
new file mode 100644
index 0000000000..4e94de98ba
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/communicatoricons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/communicatoricons.png b/comm/suite/themes/classic/mac/communicator/icons/communicatoricons.png
new file mode 100644
index 0000000000..85e5511582
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/communicatoricons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/feedIcon16-disabled.png b/comm/suite/themes/classic/mac/communicator/icons/feedIcon16-disabled.png
new file mode 100644
index 0000000000..77efbb49b1
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/feedIcon16-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/geolocation-16.png b/comm/suite/themes/classic/mac/communicator/icons/geolocation-16.png
new file mode 100644
index 0000000000..a40cc1bb9a
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/geolocation-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/geolocation-64.png b/comm/suite/themes/classic/mac/communicator/icons/geolocation-64.png
new file mode 100644
index 0000000000..099b9c76f3
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/geolocation-64.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/item.png b/comm/suite/themes/classic/mac/communicator/icons/item.png
new file mode 100644
index 0000000000..d706dfeeb3
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/item.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/key-16.png b/comm/suite/themes/classic/mac/communicator/icons/key-16.png
new file mode 100644
index 0000000000..420ff450fa
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/key-16.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/key-16@2x.png b/comm/suite/themes/classic/mac/communicator/icons/key-16@2x.png
new file mode 100644
index 0000000000..7ec431b0ad
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/key-16@2x.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/key-64.png b/comm/suite/themes/classic/mac/communicator/icons/key-64.png
new file mode 100644
index 0000000000..49819350d7
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/key-64.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/key.png b/comm/suite/themes/classic/mac/communicator/icons/key.png
new file mode 100644
index 0000000000..e51cf29881
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/key.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/loading.png b/comm/suite/themes/classic/mac/communicator/icons/loading.png
new file mode 100644
index 0000000000..290d0523be
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/loading.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/panebutton-active.png b/comm/suite/themes/classic/mac/communicator/icons/panebutton-active.png
new file mode 100644
index 0000000000..43a95235e0
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/panebutton-active.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/panebutton-inactive.png b/comm/suite/themes/classic/mac/communicator/icons/panebutton-inactive.png
new file mode 100644
index 0000000000..e424dc9ced
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/panebutton-inactive.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/icons/warning-24.png b/comm/suite/themes/classic/mac/communicator/icons/warning-24.png
new file mode 100644
index 0000000000..a599a7e19a
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/icons/warning-24.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/numberbox.css b/comm/suite/themes/classic/mac/communicator/numberbox.css
new file mode 100644
index 0000000000..73db124959
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/numberbox.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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+textbox[type="number"] {
+ -moz-appearance: none;
+ -moz-box-align: center;
+ padding: 0 !important;
+ border: none;
+ background-color: transparent;
+ cursor: default;
+}
+
+html|*.numberbox-input {
+ text-align: right;
+ padding: 0 1px !important;
+}
+
+.numberbox-input-box {
+ -moz-appearance: textfield;
+ margin-right: 4px;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/places/bookmarks.css b/comm/suite/themes/classic/mac/communicator/places/bookmarks.css
new file mode 100644
index 0000000000..684fd8b9e2
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/bookmarks.css
@@ -0,0 +1,108 @@
+/* -*- Mode: C; 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/**
+ * Style rules for generic bookmarks items.
+ */
+
+treechildren::-moz-tree-image(title) {
+ margin-inline-end: 2px;
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item,
+treechildren::-moz-tree-image(title) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.bookmark-item[container="true"],
+treechildren::-moz-tree-image(title, container) {
+ list-style-image: url("chrome://global/skin/tree/folder.png");
+}
+
+.bookmark-item[container="true"][open="true"][loading="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png") !important;
+}
+
+.bookmark-item > .toolbarbutton-box > .toolbarbutton-icon {
+ list-style-image: inherit;
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item[container][livemark],
+treechildren::-moz-tree-image(title, container, livemark) {
+ list-style-image: url("chrome://communicator/skin/places/livemark-folder.png");
+}
+
+.bookmark-item[container][livemark] .bookmark-item,
+treechildren::-moz-tree-image(title, livemarkItem) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-updated.png");
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited],
+treechildren::-moz-tree-image(title, livemarkItem, visited) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+#bookmarksToolbarFolderMenu,
+#BMB_bookmarksToolbarFolderMenu,
+treechildren::-moz-tree-image(container, queryFolder_toolbar_____) {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+}
+
+treechildren::-moz-tree-image(container, queryFolder_menu________) {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksMenu.png");
+}
+
+#unsortedBookmarksFolderMenu,
+#BMB_unsortedBookmarksFolderMenu,
+treechildren::-moz-tree-image(container, queryFolder_unfiled_____) {
+ list-style-image: url("chrome://communicator/skin/places/unsortedBookmarks.png");
+}
+
+/* query-nodes should be styled even if they're not expandable */
+.bookmark-item[query],
+treechildren::-moz-tree-image(query) {
+ list-style-image: url("chrome://communicator/skin/places/query.png");
+}
+
+treechildren::-moz-tree-image(query, OrganizerQuery_AllBookmarks) {
+ list-style-image: url("chrome://communicator/skin/places/allBookmarks.png");
+}
+
+
+.bookmark-item[query][tagContainer],
+treechildren::-moz-tree-image(title, query, tagContainer),
+treechildren::-moz-tree-image(query, OrganizerQuery_Tags) {
+ list-style-image: url("chrome://communicator/skin/places/tag.png");
+}
+
+/* calendar icon for history grouping items by day */
+treechildren::-moz-tree-image(title, query, dayContainer) {
+ list-style-image: url("chrome://communicator/skin/places/calendar.png");
+}
+
+treechildren::-moz-tree-image(query, OrganizerQuery_History) {
+ list-style-image: url("chrome://communicator/skin/places/history.png");
+}
+
+treechildren::-moz-tree-image(title, separator) {
+ list-style-image: none;
+ width: 0px;
+ height: 0px;
+}
+
+treechildren::-moz-tree-cell-text(title, separator) {
+ color: ThreeDShadow;
+ margin: 0px 5px;
+}
+
+treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
+ color: HighlightText;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/places/bookmarksMenu.png b/comm/suite/themes/classic/mac/communicator/places/bookmarksMenu.png
new file mode 100644
index 0000000000..cb2351d963
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/bookmarksMenu.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.css b/comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.css
new file mode 100644
index 0000000000..fb4875387b
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.css
@@ -0,0 +1,77 @@
+/* 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/. */
+
+/* ===== bookmarksToolbar.css ===========================================
+ == Styles specific to bookmark items in a toolbar.
+ ======================================================================= */
+
+@import url("chrome://communicator/skin/places/bookmarks.css");
+
+/* ::::: bookmark toolbar buttons ::::: */
+
+/* ..... bookmark items ..... */
+
+toolbarbutton.bookmark-item {
+ min-width: 0px;
+ max-width: 13em;
+}
+
+.bookmark-item > .toolbarbutton-icon {
+ background-image: url("chrome://communicator/skin/places/filters.svg"); /* Preload filter */
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item:not([disabled="true"]):hover:active > .toolbarbutton-icon,
+.bookmark-item[open] > .toolbarbutton-icon {
+ filter: url("chrome://communicator/skin/places/filters.svg#iconPressed");
+}
+
+toolbarbutton.bookmark-item[disabled="true"] > .toolbarbutton-icon {
+ opacity: 0.5 !important;
+}
+
+.bookmark-item > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+/* ::::: bookmark menus ::::: */
+
+.bookmark-group {
+ list-style-image: url(chrome://communicator/skin/places/bookmark-group.png) !important;
+}
+
+.menuitem-iconic.bookmark-item[disabled="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png");
+}
+
+.menu-iconic.bookmark-item[disabled="true"][container="true"] {
+ opacity: 0.5;
+}
+
+/* ..... drag and drop styles ..... */
+
+#PlacesToolbarDropIndicator {
+ list-style-image: url("chrome://communicator/skin/places/toolbarDropMarker.png");
+}
+
+.bookmark-item[dragover-into="true"] {
+ background: Highlight !important;
+ color: HighlightText !important;
+}
+
+/* rules for menupopup drop indicators */
+.menupopup-drop-indicator-bar {
+ position: relative;
+ /* these two margins must together compensate the indicator's height */
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+.menupopup-drop-indicator {
+ list-style-image: none;
+ height: 2px;
+ margin-inline-end: -4em;
+ background-color: Highlight;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.png b/comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.png
new file mode 100644
index 0000000000..075708d02e
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/bookmarksToolbar.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/places/editBookmarkOverlay.css b/comm/suite/themes/classic/mac/communicator/places/editBookmarkOverlay.css
new file mode 100644
index 0000000000..8b8f90c82a
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/editBookmarkOverlay.css
@@ -0,0 +1,75 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/**** folder menulist ****/
+.folder-icon > .menulist-label-box > .menulist-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.folder-icon > .menu-iconic-left {
+ display: -moz-box;
+}
+
+.folder-icon {
+ list-style-image: url("chrome://global/skin/tree/folder.png");
+}
+
+.menulist-icon {
+ margin: 0 !important;
+}
+
+/**** expanders ****/
+
+.expander-up,
+.expander-down {
+ margin: 0 4px 1px 8px;
+ padding: 0;
+}
+
+.expander-up {
+ -moz-appearance: -moz-mac-disclosure-button-open;
+}
+
+.expander-down {
+ -moz-appearance: -moz-mac-disclosure-button-closed;
+}
+
+#editBookmarkPanelContent {
+ min-width: 23em;
+}
+
+#editBMPanel_folderTree {
+ margin: 6px 4px 0 4px;
+}
+
+/* Hide the value column of the tag autocomplete popup
+ * leaving only the comment column visible. This is
+ * so that only the tag being edited is shown in the
+ * popup.
+ */
+#editBMPanel_tagsField #treecolAutoCompleteValue {
+ visibility: collapse;
+}
+
+
+/* ::::: bookmark panel dropdown icons ::::: */
+
+#editBMPanel_folderMenuList[selectedIndex="0"],
+#editBMPanel_toolbarFolderItem {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+}
+
+#editBMPanel_folderMenuList[selectedIndex="1"],
+#editBMPanel_bmRootItem {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksMenu.png");
+}
+
+#editBMPanel_folderMenuList[selectedIndex="2"],
+#editBMPanel_unfiledRootItem {
+ list-style-image: url("chrome://communicator/skin/places/unsortedBookmarks.png");
+}
diff --git a/comm/suite/themes/classic/mac/communicator/places/filters.svg b/comm/suite/themes/classic/mac/communicator/places/filters.svg
new file mode 100644
index 0000000000..d3ad6a76b8
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/filters.svg
@@ -0,0 +1,14 @@
+<!-- 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/. -->
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <filter id="iconPressed" color-interpolation-filters="sRGB">
+ <!-- Multiply all components with 0.55. -->
+ <feComponentTransfer>
+ <feFuncR type="linear" slope=".55"/>
+ <feFuncG type="linear" slope=".55"/>
+ <feFuncB type="linear" slope=".55"/>
+ </feComponentTransfer>
+ </filter>
+</svg>
diff --git a/comm/suite/themes/classic/mac/communicator/places/livemark-folder.png b/comm/suite/themes/classic/mac/communicator/places/livemark-folder.png
new file mode 100644
index 0000000000..62414dbeda
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/livemark-folder.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/places/organizer.css b/comm/suite/themes/classic/mac/communicator/places/organizer.css
new file mode 100644
index 0000000000..327e4a513d
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/organizer.css
@@ -0,0 +1,185 @@
+/* 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/. */
+
+#placesList {
+ padding: 0;
+ -moz-appearance: -moz-mac-source-list;
+ -moz-font-smoothing-background-color: -moz-mac-source-list;
+}
+
+#placesList > treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+#placesList > treechildren::-moz-tree-row(selected) {
+ -moz-appearance: -moz-mac-source-list-selection;
+ -moz-font-smoothing-background-color: -moz-mac-source-list-selection;
+}
+
+#placesList > treechildren::-moz-tree-row(selected, focus) {
+ -moz-appearance: -moz-mac-active-source-list-selection;
+ -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
+}
+
+#placesList > treechildren::-moz-tree-twisty(selected),
+#placesList > treechildren::-moz-tree-cell-text(selected) {
+ color: #FFFFFF;
+ fill-opacity: 1;
+}
+
+#placesList > treechildren::-moz-tree-twisty {
+ width: 16px;
+ padding-bottom: 1px;
+}
+
+@media (-moz-mac-yosemite-theme) {
+ #placesList > treechildren::-moz-tree-twisty(selected),
+ #placesList > treechildren::-moz-tree-cell-text(selected) {
+ color: -moz-dialogtext;
+ fill-opacity: 0.6;
+ }
+
+ #placesList > treechildren::-moz-tree-twisty(selected, focus),
+ #placesList > treechildren::-moz-tree-cell-text(selected, focus) {
+ color: #FFFFFF;
+ fill-opacity: 1;
+ }
+}
+
+#placeContent {
+ border: 0;
+}
+
+#placeContent > treechildren::-moz-tree-row {
+ min-height: 18px;
+ border-top: none !important;
+}
+
+#placeContent > treechildren::-moz-tree-cell,
+#placeContent > treechildren::-moz-tree-column {
+ border-right: 1px solid #D7DAD7;
+}
+
+#placeContent > treechildren::-moz-tree-cell(separator) {
+ border-color: transparent;
+}
+
+#placesList-splitter {
+ border: solid #B3B3B3;
+ border-width: 0 1px 0 0;
+ background-image: none;
+ min-width: 1px;
+}
+
+/* This splitter is always disabled and contains a grippy... see bug 591779 */
+#detailsDeck-splitter {
+ display: none;
+}
+
+#detailsDeck {
+ border-top: 1px solid #919191;
+ background-color: #F0F0F0;
+ padding: 10px;
+}
+
+#infoBoxExpander {
+ margin: 0;
+}
+
+#infoBoxExpanderLabel {
+ display: none;
+}
+
+#advancedSearch > hbox,
+#advancedSearchRows > row {
+ background-color: #E8E8E8;
+ border-top: 1px solid #F8F8F8;
+ border-bottom: 1px solid #BABABA;
+}
+
+#advancedSearchRows > row,
+#searchActions {
+ padding-inline-start: 2px;
+ padding-inline-end: 8px;
+}
+
+#organizerScopeBar {
+ padding: 3px;
+ -moz-appearance: none;
+ background: linear-gradient(#E8E8E8, #D0D0D0) repeat-x;
+ border-bottom: 1px solid #888888;
+}
+
+#scopeBarTitle {
+ font: icon;
+ color: #6D6D6D;
+ font-weight: bold;
+ text-shadow: 0 1px rgba(255, 255, 255, .4);
+ margin: 0 6px 1px;
+}
+
+button[group="scopeBar"] {
+ -moz-appearance: none;
+ -moz-box-pack: center;
+ border: 0;
+ font: icon;
+ font-weight: bold;
+ min-width: 0;
+ color: #2D2D2D;
+ border-radius: 10000px;
+ padding: 0 10px 1px;
+ margin: 1px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
+}
+
+button[group="scopeBar"]:hover,
+button[group="scopeBar"][checked="true"] {
+ color: #FFFFFF;
+ text-shadow: 0 1px rgba(0, 0, 0, .4);
+ background-color: rgba(0, 0, 0, .32);
+}
+
+button[group="scopeBar"]:active:hover,
+button[group="scopeBar"][checked="true"] {
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.4), 0 1px rgba(255, 255, 255, 0.4);
+}
+
+button[group="scopeBar"]:active:hover {
+ background-color: rgba(0, 0, 0, .5);
+}
+
+.button-text {
+ margin: 0 !important;
+}
+
+#organizerScopeBarExpander {
+ margin: 0;
+ padding: 0;
+ padding-inline-end: 2px;
+}
+
+#saveSearch {
+ margin: 0 4px;
+ padding: 0 10px;
+ min-width: 0;
+ -moz-appearance: none;
+ border-radius: 10000px;
+ border: 1px solid rgba(0, 0, 0, .4);
+ text-shadow: 0 1px rgba(255, 255, 255, .4);
+ background: linear-gradient(#FFF, #CACACA) repeat-x;
+ box-shadow: 0 1px rgba(255, 255, 255, .4);
+}
+
+#saveSearch:active:hover {
+ background: #CCCCCC;
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2), 0 1px rgba(255, 255, 255, .4);
+}
+
+#saveSearch:focus {
+ box-shadow: 0 0 1px -moz-mac-focusring inset, 0 0 3px 1px -moz-mac-focusring, 0 0 1.5px 1px -moz-mac-focusring, 0 1px rgba(255, 255, 255, .4);
+}
+
+#saveSearch:active:hover:focus {
+ box-shadow: 0 0 1px -moz-mac-focusring inset, 0 0 3px 1px -moz-mac-focusring, 0 0 1.5px 1px -moz-mac-focusring, inset 0 1px 3px rgba(0, 0, 0, .2), 0 1px rgba(255, 255, 255, .4);
+}
diff --git a/comm/suite/themes/classic/mac/communicator/places/query.png b/comm/suite/themes/classic/mac/communicator/places/query.png
new file mode 100644
index 0000000000..4b87272ed4
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/query.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/places/tag.png b/comm/suite/themes/classic/mac/communicator/places/tag.png
new file mode 100644
index 0000000000..a4038bb4fa
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/tag.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/places/toolbarDropMarker.png b/comm/suite/themes/classic/mac/communicator/places/toolbarDropMarker.png
new file mode 100644
index 0000000000..026c1b82db
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/places/toolbarDropMarker.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/preferences.css b/comm/suite/themes/classic/mac/communicator/preferences.css
new file mode 100644
index 0000000000..883f09c2b4
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/preferences.css
@@ -0,0 +1,77 @@
+/* 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/. */
+
+/* Styles used by all preference windows and panes of SeaMonkey */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Main Window ::::: */
+
+prefwindow {
+ -moz-appearance: dialog;
+ background-color: #FFFFFF;
+ color: -moz-DialogText;
+ font: message-box;
+ padding-top: 8px;
+ padding-bottom: 0px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+ font: -moz-dialog;
+}
+
+prefwindow[type="child"] {
+ padding-top: 18px;
+ padding-bottom: 15px;
+ padding-inline-start: 18px;
+ padding-inline-end: 20px;
+}
+
+prefpane {
+ padding: 12px 12px 0 12px;
+}
+
+prefwindow[type="child"] > prefpane {
+ padding: 0;
+}
+
+.prefWindow-dlgbuttons {
+ margin: 0 12px 12px;
+ padding-top: 0 !important;
+}
+
+.paneSelector {
+ font: message-box;
+ padding: 1px 4px;
+ -moz-appearance: toolbar;
+ margin: 0;
+}
+
+radio[pane] {
+ border: solid transparent;
+ border-width: 0 2px;
+ padding: 5px 4px 3px;
+ margin: 0;
+ -moz-appearance: none;
+ text-shadow: rgba(255, 255, 255, 0.4) 0 1px;
+}
+
+radio[pane]:active:hover {
+ text-shadow: none;
+}
+
+radio[pane]:active:hover > .paneButtonIcon {
+ filter: brightness(0.55);
+}
+
+radio[pane][selected="true"] {
+ border-image: url("chrome://communicator/skin/icons/panebutton-active.png") 0 2 fill repeat stretch;
+}
+
+radio[pane][selected="true"]:-moz-window-inactive {
+ border-image: url("chrome://communicator/skin/icons/panebutton-inactive.png") 0 2 fill repeat stretch;
+}
+
+.paneButtonLabel {
+ margin: 0 !important;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/profile/profile.css b/comm/suite/themes/classic/mac/communicator/profile/profile.css
new file mode 100644
index 0000000000..0c28c6b019
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/profile/profile.css
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+@import url("chrome://global/skin/global.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+treechildren::-moz-tree-image {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://communicator/skin/profile/profileicon-large.gif");
+}
+
+treechildren::-moz-tree-image(rowMigrate-no) {
+ list-style-image: url("chrome://communicator/skin/profile/migrate.gif");
+}
+
+/* profile selection dialog */
+
+/* Override global.css */
+hbox.wizard-box {
+ padding: 10px 10px 10px 10px;
+}
+
+#header {
+ -moz-box-orient: vertical;
+ margin: -14px -14px 0;
+ padding: 12px;
+ padding-inline-end: 5px;
+ padding-inline-start: 25px;
+}
+
+#header > .dialogheader-title {
+ font: inherit;
+ font-weight: bold;
+}
+
+#header > .dialogheader-description {
+ margin-inline-start: 12px !important;
+}
+
+#intro,
+#label {
+ width: 17em;
+}
+
+#managebuttons > button {
+ min-width: 8em;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/sanitizeDialog.css b/comm/suite/themes/classic/mac/communicator/sanitizeDialog.css
new file mode 100644
index 0000000000..f01eb2c0b1
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/sanitizeDialog.css
@@ -0,0 +1,45 @@
+/* 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/. */
+
+/* Align the duration label with the warning box and item list */
+#sanitizeDurationLabel {
+ margin-inline-start: 1px;
+}
+
+
+/* Hide the duration dropdown suffix label if it's empty. Otherwise it
+ takes up a little space, causing the end of the dropdown to not be aligned
+ with the warning box. */
+#sanitizeDurationSuffixLabel[value=""] {
+ display: none;
+}
+
+
+/* Sanitize everything warning box */
+#sanitizeWarningBox {
+ background-color: Window;
+ border: 1px solid ThreeDDarkShadow;
+ border-radius: 5px;
+ padding: 16px;
+}
+
+#sanitizeWarningIcon {
+ list-style-image: url("chrome://global/skin/icons/warning-large.png");
+ padding: 0;
+ margin: 0;
+}
+
+#sanitizeWarningDescBox {
+ padding: 0 16px;
+ margin: 0;
+}
+
+
+/* Align the last dialog button with the end of the warning box */
+.dialog-button-box {
+ margin-inline-end: 0;
+}
+.dialog-button[dlgtype="accept"] {
+ margin-inline-end: 0;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/search/searchbar-dropmarker.png b/comm/suite/themes/classic/mac/communicator/search/searchbar-dropmarker.png
new file mode 100644
index 0000000000..3177b09a5f
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/search/searchbar-dropmarker.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/search/searchbar-search.png b/comm/suite/themes/classic/mac/communicator/search/searchbar-search.png
new file mode 100644
index 0000000000..ca2dbfcd16
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/search/searchbar-search.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/search/searchbar.css b/comm/suite/themes/classic/mac/communicator/search/searchbar.css
new file mode 100644
index 0000000000..9b1280e749
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/search/searchbar.css
@@ -0,0 +1,69 @@
+/* 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/. */
+
+.searchbar-textbox {
+ width: 6em;
+ min-width: 6em;
+ -moz-appearance: none;
+ border-radius: 10000px;
+ background-clip: padding-box;
+ padding: 0;
+ margin: 0 4px;
+ border: 1px solid;
+ border-color: rgb(132, 132, 132) rgba(132, 132, 132, 0.6) rgba(132, 132, 132, 0.2);
+ box-shadow: 0 1px rgb(212, 212, 212) inset,
+ 0 -1px rgb(253, 253, 253) inset,
+ 1px 0 rgb(247, 247, 247) inset,
+ -1px 0 rgb(247, 247, 247) inset;
+}
+
+.searchbar-textbox[focused="true"] {
+ box-shadow: 0 0 1px -moz-mac-focusring inset,
+ 0 0 4px 1px -moz-mac-focusring,
+ 0 0 1.5px 1px -moz-mac-focusring;
+}
+
+.textbox-input-box {
+ margin: 0;
+ padding: 3px 0 2px;
+ height: 20px;
+}
+
+/* ::::: searchbar-engine-button ::::: */
+
+.toolbarbutton-icon {
+ height: 16px;
+ width: 16px;
+ list-style-image: url("chrome://communicator/skin/icons//item.png");
+}
+
+.searchbar-engine-button {
+ padding-inline-start: 6px !important; /* Need !important because of class="plain". */
+ min-width: 0;
+}
+
+.toolbarbutton-text {
+ display: none;
+}
+
+.toolbarbutton-menu-dropmarker {
+ list-style-image: url("chrome://communicator/skin/search/searchbar-dropmarker.png");
+ margin-inline-end: 4px;
+}
+
+.searchbar-popup {
+ margin-top: 4px;
+ margin-inline-start: 3px;
+}
+
+/* ::::: search-go-button ::::: */
+
+.search-go-container {
+ -moz-box-align: center;
+ padding-inline-end: 6px;
+}
+
+.search-go-button {
+ list-style-image: url("chrome://communicator/skin/search/searchbar-search.png");
+}
diff --git a/comm/suite/themes/classic/mac/communicator/sidebar/sidebar.css b/comm/suite/themes/classic/mac/communicator/sidebar/sidebar.css
new file mode 100644
index 0000000000..7e7d05785c
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/sidebar/sidebar.css
@@ -0,0 +1,164 @@
+/* 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/. */
+
+/* ===== sidebar.css ====================================================
+ == Styles used by the Sidebar panel and Sidebar tabs.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#sidebar-box {
+ border-left: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDHighlight;
+ background-color: AppWorkspace;
+}
+
+#sidebar-box:-moz-lwtheme {
+ background-color: transparent;
+}
+
+/* ::::: sidebar header ::::: */
+
+.sidebarheader-main {
+ -moz-appearance: toolbar;
+ background-color: InactiveCaption;
+ color: CaptionText;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 6px;
+ padding-inline-end: 2px;
+ overflow-x: hidden;
+}
+
+.sidebarheader-main:-moz-lwtheme {
+ -moz-appearance: none;
+ background-color: transparent;
+ color: inherit;
+}
+
+#sidebar-panel-picker:not(:-moz-lwtheme) {
+ /* would override hover/active styles, so let lwtheme do it in its own way */
+ color: inherit;
+}
+
+#sidebar-panel-picker > .toolbarbutton-dropmarker {
+ padding: 0 2px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+.sidebar-header-text {
+ font-weight: bold;
+}
+
+/* ..... close button ..... */
+
+#sidebar-close-button {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 3px;
+ list-style-image: url("chrome://communicator/skin/icons/close-button.png");
+}
+
+/* ::::: sidebar panel ::::: */
+
+.sidebar-iframe-no-panels,
+.loadarea {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.iframe-panel, .browser-sidebar {
+ border-left: 1px solid ThreeDHighlight;
+ border-bottom: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDShadow;
+}
+
+/* ::::: loading info ::::: */
+
+.text-panel-loading {
+ margin: 5px 0px;
+}
+
+.text-panel-loading[loading="false"] {
+ margin-inline-start: 11px;
+}
+
+.image-panel-loading {
+ margin: 5px;
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+/* ::::: sidebar tabs ::::: */
+
+.box-texttab {
+ -moz-binding: url("chrome://communicator/skin/sidebar/sidebarBindings.xml#sbtab");
+ -moz-box-align: center;
+ cursor: pointer;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.box-texttab:-moz-lwtheme {
+ text-shadow: none;
+}
+
+.box-texttab,
+.box-texttab[selected="true"],
+.box-texttab[selected="true"]:hover,
+.box-texttab[selected="true"]:hover:active {
+ border: 1px solid;
+ border-color: ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-inline-start: 0px;
+ padding-inline-end: 1px;
+}
+
+.box-texttab:hover {
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+}
+
+.box-texttab:hover:active {
+ border-color: ThreeDShadow;
+}
+
+.sbtab-label {
+ margin: 0px !important;
+}
+
+.sbtab-twisty {
+ margin: 0px 7px;
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-twisty.png");
+}
+
+.sbtab-texture {
+ margin: 2px 0px;
+ width: 12px;
+ height: 12px;
+}
+
+.box-texttab[selected="true"] {
+ font-weight: bold;
+ cursor: default;
+}
+
+.sbtab-twisty[selected="true"] {
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-twisty-open.png");
+}
+
+/* ::::: sidebar navigation buttons ::::: */
+
+.sidebar-nav-button {
+ padding: 5px 0px;
+}
+
+.tab-fwd {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.tab-back {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
diff --git a/comm/suite/themes/classic/mac/communicator/spinbuttons.css b/comm/suite/themes/classic/mac/communicator/spinbuttons.css
new file mode 100644
index 0000000000..bf89520f68
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/spinbuttons.css
@@ -0,0 +1,31 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+spinbuttons {
+ height: 24px;
+ min-height: 24px;
+ -moz-appearance: spinner;
+ cursor: default;
+}
+
+.spinbuttons-up {
+ -moz-appearance: none;
+ -moz-box-flex: 1;
+ min-width: 1px;
+ min-height: 1px;
+ margin: 0;
+ padding: 0;
+}
+
+.spinbuttons-down {
+ -moz-appearance: none;
+ -moz-box-flex: 1;
+ min-width: 1px;
+ min-height: 1px;
+ margin: 0;
+ padding: 0;
+}
+
diff --git a/comm/suite/themes/classic/mac/communicator/sync/syncSetup.css b/comm/suite/themes/classic/mac/communicator/sync/syncSetup.css
new file mode 100644
index 0000000000..3edbd1fe95
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/sync/syncSetup.css
@@ -0,0 +1,127 @@
+/* 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/. */
+
+wizard {
+ -moz-appearance: none;
+ width: 55em;
+ height: 45em;
+ padding: 0;
+ background-color: Window;
+}
+
+.wizard-page-box {
+ -moz-appearance: none;
+ padding-left: 0;
+ padding-right: 0;
+ margin: 0;
+}
+
+wizardpage {
+ -moz-box-pack: center;
+ -moz-box-align: center;
+ margin: 0;
+ padding: 0 6em;
+ background-color: Window;
+}
+
+.wizard-header {
+ -moz-appearance: none;
+ border: none;
+ padding: 2em 0 1em 0;
+ text-align: center;
+}
+.wizard-header-label {
+ font-size: 24pt;
+ font-weight: normal;
+}
+
+.wizard-buttons {
+ background-color: rgba(0,0,0,0.1);
+ padding: 1em;
+}
+
+.wizard-buttons-separator {
+ visibility: collapse;
+}
+
+.wizard-header-icon {
+ visibility: collapse;
+}
+
+.accountChoiceButton {
+ font: menu;
+}
+
+.confirm {
+ border: 1px solid black;
+ padding: 1em;
+ border-radius: 5px;
+}
+
+/* Override the label margin from global.css */
+description > .text-link {
+ margin: 0px;
+}
+
+.success,
+.error {
+ padding: 2px;
+ border-radius: 2px;
+}
+
+.error {
+ background-color: #FF0000 !important;
+ color: #FFFFFF !important;
+}
+
+.success {
+ background-color: #00FF00 !important;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 100%;
+ color: red;
+}
+
+.mainDesc {
+ font-weight: bold;
+ font-size: 100%;
+}
+
+.normal {
+ font-size: 100%;
+}
+
+.inputColumn {
+ margin-inline-end: 2px;
+}
+
+.recommended {
+ font-weight: bold;
+}
+
+.pin {
+ font-size: 18pt;
+ width: 4em;
+ text-align: center;
+}
+
+#passphraseHelpSpacer {
+ width: 0.5em;
+}
+
+#add-device-throbber,
+#login-throbber {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+#successPageIcon {
+ /* TODO replace this with a 128px version (bug 591122) */
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#pickSetupDesc {
+ padding: 0 7em;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/toolbar.css b/comm/suite/themes/classic/mac/communicator/toolbar.css
new file mode 100644
index 0000000000..34ce372a93
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/toolbar.css
@@ -0,0 +1,57 @@
+/* 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/. */
+
+/* ===== toolbar.css ====================================================
+ == Styles used by XUL grippytoolbar in addition to general toolbar styles.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.toolbar-primary {
+ min-height: 24px;
+}
+
+toolbargrippy {
+ display: none;
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+ width: 10px;
+ padding: 2px 1px;
+ list-style-image: url("chrome://communicator/skin/toolbar/tbgrip-arrow.png");
+}
+
+toolbargrippy:hover {
+ background-color: ThreeDHighlight;
+}
+
+.toolbargrippy-texture {
+ margin-top: 2px;
+ width: 6px;
+ background: url("chrome://communicator/skin/toolbar/tbgrip-texture.png");
+}
+
+/* ::::: collapsed tray and grippies ::::: */
+
+toolbargrippy[tbgrippy-collapsed="true"] {
+ -moz-box-orient: horizontal;
+ border-left: 1px solid ThreeDHighlight;
+ border-top: 1px solid ThreeDHighlight;
+ border-right: 1px solid ThreeDShadow;
+ border-bottom: 1px solid ThreeDShadow;
+ width: 40px;
+ height: 10px;
+ padding: 1px 2px;
+ list-style-image: url("chrome://communicator/skin/toolbar/tbgrip-arrow-clps.png");
+}
+
+toolbargrippy[tbgrippy-collapsed="true"] > .toolbargrippy-texture {
+ margin-top: 0;
+ margin-inline-start: 2px;
+ width: 0px;
+ height: 6px;
+}
+
+.collapsed-tray-spacer {
+ border-bottom: 1px solid ThreeDShadow;
+}
diff --git a/comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient22.png b/comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient22.png
new file mode 100644
index 0000000000..4ab1228bbc
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient22.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient34.png b/comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient34.png
new file mode 100644
index 0000000000..79a8835dad
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/toolbar/toolbar-gradient34.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/communicator/viewSourceOverlay.css b/comm/suite/themes/classic/mac/communicator/viewSourceOverlay.css
new file mode 100644
index 0000000000..ef28c640e2
--- /dev/null
+++ b/comm/suite/themes/classic/mac/communicator/viewSourceOverlay.css
@@ -0,0 +1,14 @@
+/* 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/. */
+
+/* make findbar appear above content */
+#appcontent {
+ -moz-box-direction: reverse;
+}
+
+/* style findbar for being on top */
+#FindToolbar {
+ border-top: none;
+ border-bottom: 1px solid #888888;
+}
diff --git a/comm/suite/themes/classic/mac/editor/editor.css b/comm/suite/themes/classic/mac/editor/editor.css
new file mode 100644
index 0000000000..1c180f2f42
--- /dev/null
+++ b/comm/suite/themes/classic/mac/editor/editor.css
@@ -0,0 +1,84 @@
+/* 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/. */
+
+/* ==== editor.css ==========================================================
+ == Styles shared throughout the Editor application.
+ ========================================================================== */
+
+@import url("chrome://communicator/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#editorWindow {
+ -moz-appearance: none;
+ background-color: #EEEEEE;
+}
+
+#content-frame {
+ min-width: 10px;
+ min-height: 10px;
+ height: 400px;
+}
+
+.source-editor,
+.source-editor:focus {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ margin-inline-start: 0px;
+ margin-inline-end: 5px;
+ border: 0px;
+ /* Scroll bars are in content,
+ so we can't use right and bottom padding! */
+ padding-top: 5px;
+ padding-bottom: 0px;
+ padding-inline-start: 5px;
+ padding-inline-end: 0px;
+}
+
+/* Multiline textarea for HTML source editing */
+#content-source,
+#doctype-text {
+ font-family: -moz-fixed;
+ font-size: initial;
+}
+
+#ContentWindowDeck {
+ border-left: 1px solid ThreeDDarkShadow;
+ border-right: 1px solid ThreeDLightShadow;
+}
+
+/* ::::: struct toolbar ::::: */
+
+#structToolbar {
+ min-width: 1px;
+ overflow: -moz-hidden-unscrollable;
+}
+
+#structSpacer {
+ margin: 2px 0px;
+}
+
+.struct-button {
+ padding: 2px;
+}
+
+.struct-button[checked="true"] {
+ font-weight: bold;
+}
+
+.struct-textbox {
+ -moz-appearance: none !important;
+ padding: 0px !important;
+ margin: 0px !important;
+ border: none !important;
+}
+
+/* ::::: lightweight themes ::::: */
+
+#EditModeToolbar:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
+
diff --git a/comm/suite/themes/classic/mac/editor/editorFormatToolbar.css b/comm/suite/themes/classic/mac/editor/editorFormatToolbar.css
new file mode 100644
index 0000000000..d3f6ff36d8
--- /dev/null
+++ b/comm/suite/themes/classic/mac/editor/editorFormatToolbar.css
@@ -0,0 +1,538 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: formatting toolbar and buttons ::::: */
+
+#FormatToolbar {
+ -moz-appearance: none;
+ border-bottom: 1px solid rgba(0,0,0,0.35);
+}
+
+#FormatToolbar > toolbarbutton > .toolbarbutton-text {
+ display: none;
+}
+
+toolbarbutton.formatting-button {
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+ min-width: 32px;
+}
+
+#DecreaseFontSizeButton {
+ -moz-image-region: rect(64px 14px 80px 0px);
+}
+
+#DecreaseFontSizeButton:hover {
+ -moz-image-region: rect(64px 28px 80px 14px);
+}
+
+#DecreaseFontSizeButton:hover:active {
+ -moz-image-region: rect(64px 42px 80px 28px);
+}
+
+#DecreaseFontSizeButton[disabled="true"] {
+ -moz-image-region: rect(64px 56px 80px 42px) !important;
+}
+
+#IncreaseFontSizeButton {
+ -moz-image-region: rect(81px 18px 98px 0px);
+}
+
+#IncreaseFontSizeButton:hover {
+ -moz-image-region: rect(81px 36px 98px 18px);
+}
+
+#IncreaseFontSizeButton:hover:active {
+ -moz-image-region: rect(81px 54px 98px 36px);
+}
+
+#IncreaseFontSizeButton[disabled="true"] {
+ -moz-image-region: rect(81px 72px 98px 54px) !important;
+}
+
+#boldButton {
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#boldButton:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#boldButton:hover:active {
+ -moz-image-region: rect(16px 48px 32px 32px);
+}
+
+#boldButton[checked="true"] {
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#boldButton[disabled="true"] {
+ -moz-image-region: rect(16px 64px 32px 48px) !important;
+}
+
+#italicButton {
+ -moz-image-region: rect(98px 16px 114px 0px);
+}
+
+#italicButton:hover {
+ -moz-image-region: rect(98px 32px 114px 16px);
+}
+
+#italicButton:hover:active {
+ -moz-image-region: rect(98px 48px 114px 32px);
+}
+
+#italicButton[checked="true"] {
+ -moz-image-region: rect(98px 16px 114px 0px);
+}
+
+#italicButton[disabled="true"] {
+ -moz-image-region: rect(98px 64px 114px 48px) !important;
+}
+
+#underlineButton {
+ -moz-image-region: rect(114px 16px 130px 0px);
+}
+
+#underlineButton:hover {
+ -moz-image-region: rect(114px 32px 130px 16px);
+}
+
+#underlineButton:hover:active {
+ -moz-image-region: rect(114px 48px 130px 32px);
+}
+
+#underlineButton[checked="true"] {
+ -moz-image-region: rect(114px 16px 130px 0px);
+}
+
+#underlineButton[disabled="true"] {
+ -moz-image-region: rect(114px 64px 130px 48px) !important;
+}
+
+#ulButton {
+ -moz-image-region: rect(32px 16px 48px 0px);
+}
+
+#ulButton:hover {
+ -moz-image-region: rect(32px 32px 48px 16px);
+}
+
+#ulButton:hover:active {
+ -moz-image-region: rect(32px 48px 48px 32px);
+}
+
+#ulButton[checked="true"] {
+ -moz-image-region: rect(32px 16px 48px 0px);
+}
+
+#ulButton[disabled="true"] {
+ -moz-image-region: rect(32px 64px 48px 48px) !important;
+}
+
+#olButton {
+ -moz-image-region: rect(194px 16px 210px 0px);
+}
+
+#olButton:hover {
+ -moz-image-region: rect(194px 32px 210px 16px);
+}
+
+#olButton:hover:active {
+ -moz-image-region: rect(194px 48px 210px 32px);
+}
+
+#olButton[checked="true"] {
+ -moz-image-region: rect(194px 16px 210px 0px);
+}
+
+#olButton[disabled="true"] {
+ -moz-image-region: rect(194px 64px 210px 48px) !important;
+}
+
+#outdentButton {
+ -moz-image-region: rect(210px 16px 226px 0px);
+}
+
+#outdentButton:hover {
+ -moz-image-region: rect(210px 32px 226px 16px);
+}
+
+#outdentButton:hover:active {
+ -moz-image-region: rect(210px 48px 226px 32px);
+}
+
+#outdentButton[disabled="true"] {
+ -moz-image-region: rect(210px 64px 226px 48px) !important;
+}
+
+#indentButton {
+ -moz-image-region: rect(178px 16px 194px 0px);
+}
+
+#indentButton:hover {
+ -moz-image-region: rect(178px 32px 194px 16px);
+}
+
+#indentButton:hover:active {
+ -moz-image-region: rect(178px 48px 194px 32px);
+}
+
+#indentButton[disabled="true"] {
+ -moz-image-region: rect(178px 64px 194px 48px) !important;
+}
+
+#align-left-button {
+ -moz-image-region: rect(146px 16px 162px 0px);
+}
+
+#align-left-button:hover {
+ -moz-image-region: rect(146px 32px 162px 16px);
+}
+
+#align-left-button:hover:active {
+ -moz-image-region: rect(146px 48px 162px 32px);
+}
+
+#align-left-button[checked="true"] {
+ -moz-image-region: rect(146px 16px 162px 0px);
+}
+
+#align-left-button[disabled="true"] {
+ -moz-image-region: rect(146px 64px 162px 48px) !important;
+}
+
+#align-center-button {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#align-center-button:hover {
+ -moz-image-region: rect(48px 32px 64px 16px);
+}
+
+#align-center-button:hover:active {
+ -moz-image-region: rect(48px 48px 64px 32px);
+}
+
+#align-center-button[checked="true"] {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#align-center-button[disabled="true"] {
+ -moz-image-region: rect(48px 64px 64px 48px) !important;
+}
+
+#align-right-button {
+ -moz-image-region: rect(162px 16px 178px 0px);
+}
+
+#align-right-button:hover {
+ -moz-image-region: rect(162px 32px 178px 16px);
+}
+
+#align-right-button:hover:active {
+ -moz-image-region: rect(162px 48px 178px 32px);
+}
+
+#align-right-button[checked="true"] {
+ -moz-image-region: rect(162px 16px 178px 0px);
+}
+
+#align-right-button[disabled="true"] {
+ -moz-image-region: rect(162px 64px 178px 48px) !important;
+}
+
+#align-justify-button {
+ -moz-image-region: rect(130px 16px 146px 0px);
+}
+
+#align-justify-button:hover {
+ -moz-image-region: rect(130px 32px 146px 16px);
+}
+
+#align-justify-button:hover:active {
+ -moz-image-region: rect(130px 48px 146px 32px);
+}
+
+#align-justify-button[checked="true"] {
+ -moz-image-region: rect(130px 16px 146px 0px);
+}
+
+#align-justify-button[disabled="true"] {
+ -moz-image-region: rect(130px 64px 146px 48px) !important;
+}
+
+#AlignPopupButton {
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#AlignPopupButton:hover {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#AlignPopupButton[open="true"] {
+ -moz-image-region: rect(0px 48px 16px 32px) !important;
+}
+
+#AlignPopupButton[disabled="true"] {
+ -moz-image-region: rect(0px 64px 16px 48px) !important;
+}
+
+#InsertPopupButton {
+ -moz-image-region: rect(244px 16px 260px 0px);
+}
+
+#InsertPopupButton:hover {
+ -moz-image-region: rect(244px 32px 260px 16px);
+}
+
+#InsertPopupButton[open="true"] {
+ -moz-image-region: rect(244px 48px 260px 32px) !important;
+}
+
+#InsertPopupButton[disabled="true"] {
+ -moz-image-region: rect(244px 64px 260px 48px) !important;
+}
+
+#smileButtonMenu {
+ -moz-image-region: rect(227px 17px 244px 0px);
+}
+
+#smileButtonMenu:hover {
+ -moz-image-region: rect(227px 34px 244px 17px);
+}
+
+#smileButtonMenu[open="true"] {
+ -moz-image-region: rect(227px 51px 244px 34px) !important;
+}
+
+#smileButtonMenu[disabled="true"] {
+ -moz-image-region: rect(227px 68px 244px 51px) !important;
+}
+
+/* ::::: menuitem icons ::::: */
+
+/* ..... align menu ..... */
+
+#AlignPopup > menuitem {
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+}
+
+#AlignLeftItem {
+ -moz-image-region: rect(146px 16px 162px 0px);
+}
+
+#AlignCenterItem {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#AlignRightItem {
+ -moz-image-region: rect(162px 16px 178px 0px);
+}
+
+#AlignJustifyItem {
+ -moz-image-region: rect(130px 16px 146px 0px);
+}
+
+/* ..... insert menu ..... */
+
+#InsertPopup > menuitem {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+}
+
+#InsertLinkItem {
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+#InsertAnchorItem {
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+#InsertImageItem {
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+#InsertHRuleItem {
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+#InsertTableItem {
+ -moz-image-region: rect(180px 19px 199px 0);
+}
+
+/* ..... smiley menu ..... */
+
+.insert-smile {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-smile.png");
+}
+
+.insert-frown {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-frown.png");
+}
+
+.insert-wink {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-wink.png");
+}
+
+.insert-tongue {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-tongue.png");
+}
+
+.insert-laughing {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-laughing.png");
+}
+
+.insert-embarrassed {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-embarrassed.png");
+}
+
+.insert-undecided {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-undecided.png");
+}
+
+.insert-surprise {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-surprise.png");
+}
+
+.insert-kiss {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-kiss.png");
+}
+
+.insert-yell {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-yell.png");
+}
+
+.insert-cool {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-cool.png");
+}
+
+.insert-money {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-money.png");
+}
+
+.insert-foot {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-foot.png");
+}
+
+.insert-innocent {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-innocent.png");
+}
+
+.insert-cry {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-cry.png");
+}
+
+.insert-sealed {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-sealed.png");
+}
+
+/* ::::: fg/bg color picker ::::: */
+
+.ColorPickerLabel {
+ border: 1px inset ThreeDFace;
+ margin: 0px;
+ padding: 2px;
+}
+
+.color-button {
+ border: 1px inset ThreeDFace;
+ padding: 0px;
+ width: 14px;
+ height: 12px;
+ margin: 2px;
+}
+
+.color-button:hover {
+ border: 1px solid ThreeDDarkShadow;
+}
+
+#TextColorButton {
+ margin-top: 2px;
+ margin-bottom: 9px;
+ margin-inline-start: 2px;
+ margin-inline-end: 9px;
+}
+
+#BackgroundColorButton {
+ margin-top: 9px;
+ margin-bottom: 2px;
+ margin-inline-start: 9px;
+ margin-inline-end: 2px;
+}
+
+#HighlightColorButton {
+ -moz-image-region: rect(260px 16px 272px 0px);
+ background-color: transparent;
+}
+
+#HighlightColorButton:hover {
+ -moz-image-region: rect(260px 32px 272px 16px);
+}
+
+#HighlightColorButton:hover:active {
+ -moz-image-region: rect(260px 48px 272px 32px);
+}
+
+#HighlightColorButton[disabled="true"],
+#HighlightColorButton[disabled="true"]:hover,
+#HighlightColorButton[disabled="true"]:hover:active {
+ -moz-image-region: rect(260px 64px 272px 48px);
+}
+
+
+#absolutePositionButton {
+ -moz-image-region: rect(273px 16px 289px 0px);
+}
+
+#absolutePositionButton:hover {
+ -moz-image-region: rect(273px 32px 289px 16px);
+}
+
+#absolutePositionButton:hover:active {
+ -moz-image-region: rect(273px 48px 289px 32px);
+}
+
+#absolutePositionButton[disabled="true"] {
+ -moz-image-region: rect(273px 64px 289px 48px) ! important;
+}
+
+#absolutePositionButton[checked="true"]{
+ -moz-image-region: rect(289px 16px 305px 0px);
+}
+
+#absolutePositionButton[checked="true"]:hover {
+ -moz-image-region: rect(289px 32px 305px 16px);
+}
+
+#absolutePositionButton[checked="true"]:hover:active {
+ -moz-image-region: rect(289px 48px 305px 32px);
+}
+
+#increaseZIndexButton {
+ list-style-image: url("chrome://editor/content/images/bringtofront.png");
+}
+
+#increaseZIndexButton[disabled="true"] {
+ list-style-image: url("chrome://editor/content/images/bringtofront-disabled.png");
+}
+
+#decreaseZIndexButton {
+ list-style-image: url("chrome://editor/content/images/sendtoback.png");
+}
+
+#decreaseZIndexButton[disabled="true"] {
+ list-style-image: url("chrome://editor/content/images/sendtoback-disabled.png");
+}
+
+/* Force the folder location and mail view items to fit in the available width
+ in the Customize Toolbar dialog. */
+#palette-box #paragraph-select-container,
+#palette-box #ParagraphSelect,
+#palette-box #font-face-select-container,
+#palette-box #FontFaceSelect,
+#palette-box #font-size-select-container,
+#palette-box #FontSizeSelect {
+ -moz-box-flex: 1;
+}
diff --git a/comm/suite/themes/classic/mac/editor/editorModeToolbar.css b/comm/suite/themes/classic/mac/editor/editorModeToolbar.css
new file mode 100644
index 0000000000..79762cc446
--- /dev/null
+++ b/comm/suite/themes/classic/mac/editor/editorModeToolbar.css
@@ -0,0 +1,76 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: edit mode toolbar ::::: */
+
+#EditModeToolbar {
+ -moz-box-align: start;
+ min-width: 1px;
+}
+
+#NormalModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-normal.png");
+}
+
+#TagModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-tags.png");
+}
+
+#SourceModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-html.png");
+}
+
+#PreviewModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-preview.png");
+}
+
+#EditModeTabs {
+ background-color: rgba(0, 0, 0, 0.1);
+ padding: 0;
+ margin: 0;
+ border-top: 1px solid #888;
+ -moz-box-align: start;
+ font: message-box;
+}
+#EditModeTabs > .tabs-left {
+ -moz-box-flex: 0;
+}
+
+.tab-bottom {
+ -moz-appearance: none;
+ margin: -1px 0 0;
+ padding: 0 0 2px 0;
+ position: relative;
+ border-inline-end: 1px solid rgba(0, 0, 0, 0.19);
+}
+
+.tab-bottom:first-of-type {
+ border-inline-start: 1px solid rgba(0, 0, 0, 0.19);
+}
+
+.tab-bottom > .tab-middle {
+ padding: 1px 2px 0 2px;
+}
+
+.tab-bottom:not([visuallyselected=true]):hover {
+ background-color: rgba(0, 0, 0, 0.1);
+ border-inline-end-color: rgba(0, 0, 0, 0.1);
+}
+
+.tab-bottom[visuallyselected=true] {
+ color: #000;
+ text-shadow: none;
+ border: solid #888;
+ border-width: 0 1px 1px;
+ border-radius: 2px;
+ margin-inline-end: -1px;
+ margin-bottom: 1px;
+ padding: 0;
+}
+
+.tab-bottom[visuallyselected=true] > .tab-middle {
+ -moz-appearance: dialog;
+}
diff --git a/comm/suite/themes/classic/mac/editor/editorPrimaryToolbar.css b/comm/suite/themes/classic/mac/editor/editorPrimaryToolbar.css
new file mode 100644
index 0000000000..bf2ec77e82
--- /dev/null
+++ b/comm/suite/themes/classic/mac/editor/editorPrimaryToolbar.css
@@ -0,0 +1,441 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+#newButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#newButton:hover:active {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#newButton[disabled="true"] {
+ -moz-image-region: rect(120px 89px 149px 60px) !important;
+}
+
+#openButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(150px 29px 179px 0);
+}
+
+#openButton:hover:active {
+ -moz-image-region: rect(150px 59px 179px 30px);
+}
+
+#openButton[disabled="true"] {
+ -moz-image-region: rect(150px 89px 179px 60px) !important;
+}
+
+#saveButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(210px 29px 239px 0);
+}
+
+#saveButton:hover:active {
+ -moz-image-region: rect(210px 59px 239px 30px);
+}
+
+#saveButton[disabled="true"] {
+ -moz-image-region: rect(210px 89px 239px 60px) !important;
+}
+
+#publishButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(300px 29px 329px 0);
+}
+
+#publishButton:hover:active {
+ -moz-image-region: rect(300px 59px 329px 30px);
+}
+
+#publishButton[disabled="true"] {
+ -moz-image-region: rect(300px 89px 329px 60px) !important;
+}
+
+#previewButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(180px 29px 209px 0);
+}
+
+#previewButton:hover:active {
+ -moz-image-region: rect(180px 59px 209px 30px);
+}
+
+#previewButton[disabled="true"] {
+ -moz-image-region: rect(180px 89px 209px 60px) !important;
+}
+
+#linkButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#linkButton:hover:active {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#linkButton[disabled="true"] {
+ -moz-image-region: rect(90px 89px 119px 60px) !important;
+}
+
+#imageButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#imageButton:hover:active {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#imageButton[disabled="true"] {
+ -moz-image-region: rect(60px 89px 89px 60px) !important;
+}
+
+#formButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(450px 29px 479px 0);
+}
+
+#formButton:hover:active {
+ -moz-image-region: rect(450px 59px 479px 30px);
+}
+
+#formButton[disabled="true"] {
+ -moz-image-region: rect(450px 89px 479px 60px) !important;
+}
+
+#namedAnchorButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#namedAnchorButton:hover:active {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#namedAnchorButton[disabled="true"] {
+ -moz-image-region: rect(0 89px 29px 60px) !important;
+}
+
+#hlineButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#hlineButton:hover:active {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#hlineButton[disabled="true"] {
+ -moz-image-region: rect(30px 89px 59px 60px) !important;
+}
+
+#tableButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(270px 29px 299px 0);
+}
+
+#tableButton:hover:active {
+ -moz-image-region: rect(270px 59px 299px 30px);
+}
+
+#tableButton[disabled="true"] {
+ -moz-image-region: rect(270px 89px 299px 60px) !important;
+}
+
+#spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(240px 29px 269px 0);
+}
+
+#spellingButton:hover:active {
+ -moz-image-region: rect(240px 59px 269px 30px);
+}
+
+#spellingButton[disabled="true"] {
+ -moz-image-region: rect(240px 89px 269px 60px) !important;
+}
+
+#cutButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(330px 29px 359px 0);
+}
+
+#cutButton:hover:active {
+ -moz-image-region: rect(330px 59px 359px 30px);
+}
+
+#cutButton[disabled="true"] {
+ -moz-image-region: rect(330px 89px 359px 60px) !important;
+}
+
+#copyButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(360px 29px 389px 0);
+}
+
+#copyButton:hover:active {
+ -moz-image-region: rect(360px 59px 389px 30px);
+}
+
+#copyButton[disabled="true"] {
+ -moz-image-region: rect(360px 89px 389px 60px) !important;
+}
+
+#pasteButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(390px 29px 419px 0);
+}
+
+#pasteButton:hover:active {
+ -moz-image-region: rect(390px 59px 419px 30px);
+}
+
+#pasteButton[disabled="true"] {
+ -moz-image-region: rect(390px 89px 419px 60px) !important;
+}
+
+#findButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(420px 29px 449px 0);
+}
+
+#findButton:hover:active {
+ -moz-image-region: rect(420px 59px 449px 30px);
+}
+
+#findButton[disabled="true"] {
+ -moz-image-region: rect(420px 89px 449px 60px) !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #newButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #newButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(80px 19px 99px 0);
+}
+
+toolbar[iconsize="small"] > #newButton:hover:active {
+ -moz-image-region: rect(80px 39px 99px 20px);
+}
+
+toolbar[iconsize="small"] > #newButton[disabled="true"] {
+ -moz-image-region: rect(80px 59px 99px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #openButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #openButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(100px 19px 119px 0);
+}
+
+toolbar[iconsize="small"] > #openButton:hover:active {
+ -moz-image-region: rect(100px 39px 119px 20px);
+}
+
+toolbar[iconsize="small"] > #openButton[disabled="true"] {
+ -moz-image-region: rect(100px 59px 119px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #saveButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #saveButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(140px 19px 159px 0);
+}
+
+toolbar[iconsize="small"] > #saveButton:hover:active {
+ -moz-image-region: rect(140px 39px 159px 20px);
+}
+
+toolbar[iconsize="small"] > #saveButton[disabled="true"] {
+ -moz-image-region: rect(140px 59px 159px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #publishButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #publishButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(200px 19px 219px 0);
+}
+
+toolbar[iconsize="small"] > #publishButton:hover:active {
+ -moz-image-region: rect(200px 39px 219px 20px);
+}
+
+toolbar[iconsize="small"] > #publishButton[disabled="true"] {
+ -moz-image-region: rect(200px 59px 219px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #previewButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #previewButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(120px 19px 139px 0);
+}
+
+toolbar[iconsize="small"] > #previewButton:hover:active {
+ -moz-image-region: rect(120px 39px 139px 20px);
+}
+
+toolbar[iconsize="small"] > #previewButton[disabled="true"] {
+ -moz-image-region: rect(120px 59px 139px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #linkButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #linkButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #linkButton:hover:active {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #linkButton[disabled="true"] {
+ -moz-image-region: rect(60px 59px 79px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #imageButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #imageButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #imageButton:hover:active {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #imageButton[disabled="true"] {
+ -moz-image-region: rect(40px 59px 59px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #formButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #formButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(300px 19px 319px 0);
+}
+
+toolbar[iconsize="small"] > #formButton:hover:active {
+ -moz-image-region: rect(300px 39px 319px 20px);
+}
+
+toolbar[iconsize="small"] > #formButton[disabled="true"] {
+ -moz-image-region: rect(300px 59px 319px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #namedAnchorButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton:hover:active {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #namedAnchorButton[disabled="true"] {
+ -moz-image-region: rect(0 59px 19px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #hlineButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #hlineButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #hlineButton:hover:active {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #hlineButton[disabled="true"] {
+ -moz-image-region: rect(20px 59px 39px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #tableButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #tableButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(180px 19px 199px 0);
+}
+
+toolbar[iconsize="small"] > #tableButton:hover:active {
+ -moz-image-region: rect(180px 39px 199px 20px);
+}
+
+toolbar[iconsize="small"] > #tableButton[disabled="true"] {
+ -moz-image-region: rect(180px 59px 199px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #spellingButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(160px 19px 179px 0);
+}
+
+toolbar[iconsize="small"] > #spellingButton:hover:active {
+ -moz-image-region: rect(160px 39px 179px 20px);
+}
+
+toolbar[iconsize="small"] > #spellingButton[disabled="true"] {
+ -moz-image-region: rect(160px 59px 179px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #cutButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #cutButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(220px 19px 239px 0);
+}
+
+toolbar[iconsize="small"] > #cutButton:hover:active {
+ -moz-image-region: rect(220px 39px 239px 20px);
+}
+
+toolbar[iconsize="small"] > #cutButton[disabled="true"] {
+ -moz-image-region: rect(220px 59px 239px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #copyButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #copyButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(240px 19px 259px 0);
+}
+
+toolbar[iconsize="small"] > #copyButton:hover:active {
+ -moz-image-region: rect(240px 39px 259px 20px);
+}
+
+toolbar[iconsize="small"] > #copyButton[disabled="true"] {
+ -moz-image-region: rect(240px 59px 259px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #pasteButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #pasteButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(260px 19px 279px 0);
+}
+
+toolbar[iconsize="small"] > #pasteButton:hover:active {
+ -moz-image-region: rect(260px 39px 279px 20px);
+}
+
+toolbar[iconsize="small"] > #pasteButton[disabled="true"] {
+ -moz-image-region: rect(260px 59px 279px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #findButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #findButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(280px 19px 299px 0);
+}
+
+toolbar[iconsize="small"] > #findButton:hover:active {
+ -moz-image-region: rect(280px 39px 299px 20px);
+}
+
+toolbar[iconsize="small"] > #findButton[disabled="true"] {
+ -moz-image-region: rect(280px 59px 299px 40px) !important;
+}
diff --git a/comm/suite/themes/classic/mac/editor/icons/editoricons-small.png b/comm/suite/themes/classic/mac/editor/icons/editoricons-small.png
new file mode 100644
index 0000000000..c42e638f92
--- /dev/null
+++ b/comm/suite/themes/classic/mac/editor/icons/editoricons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/editor/icons/editoricons.png b/comm/suite/themes/classic/mac/editor/icons/editoricons.png
new file mode 100644
index 0000000000..6be73b1a31
--- /dev/null
+++ b/comm/suite/themes/classic/mac/editor/icons/editoricons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/accountManage.css b/comm/suite/themes/classic/mac/messenger/accountManage.css
new file mode 100644
index 0000000000..45fecb5922
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/accountManage.css
@@ -0,0 +1,57 @@
+/* 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/. */
+
+/* ===== accountManage.css ==============================================
+ == Styles for the Mail Account Manager.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: account manager :::::: */
+
+.specialFolderPickerGrid {
+ margin-inline-start: 20px;
+}
+
+.fccReplyFollowsParent {
+ margin-inline-start: 20px;
+}
+
+.signatureBox {
+ font-family: -moz-fixed;
+}
+
+#identitiesList > listitem:first-child,
+treechildren::-moz-tree-cell-text(isDefaultServer-true) {
+ font-weight: bold;
+}
+
+dialogheader {
+ display: none;
+}
+
+button:not(.spinbuttons-button):not(.dialog-button) {
+ min-height: 19px;
+}
+
+page > description,
+#smimeEditing > description {
+ margin: 1px 5px 2px 6px;
+}
+
+/* ::::: SMTP Server Panel :::::: */
+
+.smtpServerListItem {
+ padding-inline-start: 3px;
+}
+
+#backgroundBox {
+ background-color: ThreeDLightShadow;
+}
+
+#smtpServerInfoBox textbox {
+ background-color: transparent;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/addressbook/addressbook.css b/comm/suite/themes/classic/mac/messenger/addressbook/addressbook.css
new file mode 100644
index 0000000000..6d6a209451
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/addressbook/addressbook.css
@@ -0,0 +1,334 @@
+/* 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/. */
+
+/* ===== addressbook.css ================================================
+ == Styles for the main Address Book window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+#addressbookWindow {
+ -moz-appearance: none;
+ background-color: #EEEEEE;
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-newcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#button-newcard:hover:active {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#button-newcard[disabled]{
+ -moz-image-region: rect(60px 89px 89px 60px) !important;
+}
+
+#button-newlist {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#button-newlist:hover:active {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#button-newlist[disabled] {
+ -moz-image-region: rect(90px 89px 119px 60px) !important;
+}
+
+#button-editcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#button-editcard:hover:active {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#button-editcard[disabled] {
+ -moz-image-region: rect(30px 89px 59px 60px) !important;
+}
+
+#button-newmessage {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(150px 29px 179px 0);
+}
+
+#button-newmessage:hover:active {
+ -moz-image-region: rect(150px 59px 179px 30px);
+}
+
+#button-newmessage[disabled]{
+ -moz-image-region: rect(150px 89px 179px 60px) !important;
+}
+
+#button-abdelete {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#button-abdelete:hover:active {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#button-abdelete[disabled] {
+ -moz-image-region: rect(0 89px 29px 60px) !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #button-newcard,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #button-newcard:hover:active {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newcard[disabled] {
+ -moz-image-region: rect(40px 59px 59px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-newlist,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newlist {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #button-newlist:hover:active {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newlist[disabled] {
+ -moz-image-region: rect(60px 59px 79px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-editcard,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-editcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #button-editcard:hover:active {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #button-editcard[disabled] {
+ -moz-image-region: rect(20px 59px 39px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-newmessage,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newmessage {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(100px 19px 119px 0);
+}
+
+toolbar[iconsize="small"] > #button-newmessage:hover:active {
+ -moz-image-region: rect(100px 39px 119px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newmessage[disabled] {
+ -moz-image-region: rect(100px 59px 119px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-abdelete,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-abdelete {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #button-abdelete:hover:active {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #button-abdelete[disabled] {
+ -moz-image-region: rect(0 59px 19px 40px) !important;
+}
+
+#results-splitter {
+ background-color: -moz-dialog;
+ border-top: 1px solid #A5A5A5;
+ border-bottom: 1px solid #A5A5A5;
+}
+
+#dirTree-splitter {
+ border: solid #B3B3B3;
+ border-width: 0 1px 0 0;
+ background-image: none;
+ min-width: 1px;
+}
+
+/* Make sure users find the splitter when it's collapsed... */
+
+#dirTree-splitter[state="collapsed"] {
+ min-width: 5px;
+ background-color: #D6DDE5;
+}
+
+#dirTree {
+ -moz-appearance: -moz-mac-source-list;
+ -moz-font-smoothing-background-color: -moz-mac-source-list;
+}
+
+.tree-stack > .tree-rows > .tree-bodybox {
+ border: none;
+}
+
+#dirTree > treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+#dirTree > treechildren::-moz-tree-row(selected) {
+ -moz-appearance: -moz-mac-source-list-selection;
+ -moz-font-smoothing-background-color: -moz-mac-source-list-selection;
+}
+
+#dirTree > treechildren::-moz-tree-row(selected, focus) {
+ -moz-appearance: -moz-mac-active-source-list-selection;
+ -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
+}
+
+#dirTree > treechildren::-moz-tree-twisty(selected),
+#dirTree > treechildren::-moz-tree-cell-text(selected) {
+ color: #FFFFFF;
+ fill-opacity: 1;
+}
+
+#dirTree > treechildren::-moz-tree-twisty {
+ width: 16px;
+ padding-bottom: 1px;
+}
+
+@media (-moz-mac-yosemite-theme) {
+ #dirTree > treechildren::-moz-tree-twisty(selected),
+ #dirTree > treechildren::-moz-tree-cell-text(selected) {
+ color: -moz-dialogtext;
+ fill-opacity: 0.6;
+ }
+
+ #dirTree > treechildren::-moz-tree-twisty(selected, focus),
+ #dirTree > treechildren::-moz-tree-cell-text(selected, focus) {
+ color: #FFFFFF;
+ fill-opacity: 1;
+ }
+}
+
+#DirCol {
+ -moz-appearance: none;
+ border: none;
+ background-color: transparent;
+}
+
+#DirCol > .treecol-text {
+ padding: 6px 0 6px 8px;
+ font-weight: bold;
+ color: #738193;
+}
+
+#blankResultsPaneMessage {
+ font-style: italic;
+}
+
+#localResultsOnlyMessage {
+ font-style: italic;
+ text-align: center;
+}
+
+/* CardView styles - used in the Card View Pane */
+
+#CardViewBox {
+ -moz-user-focus: ignore;
+ overflow: auto;
+ min-width: 150px;
+ background-color: #FFFFFF;
+}
+
+#CardViewInnerBox {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ padding: 0 8px;
+}
+
+#CardTitle {
+ font-size: 150%;
+ font-weight: bold;
+ border-bottom: 2px solid black;
+ min-width: 120px;
+ margin: 0px;
+}
+
+.cardViewColumn {
+ margin-inline-end: 10px;
+}
+
+.cardViewGroup {
+ margin-top: 8px;
+ padding-bottom: 5px;
+ min-width: 50px;
+}
+
+.CardViewHeading {
+ padding: 1px;
+ padding-inline-start: 5px;
+ margin: 0px 0px 1px;
+ background-color: -moz-dialog;
+ color: black;
+ font-weight: bold;
+ min-width: 30px;
+}
+
+#cvPhoto {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic.png");
+}
+
+#cvBuddyIcon {
+ padding-inline-start: 20px;
+ padding-top: 2px;
+}
+
+.CardViewText,
+.CardViewLink {
+ padding-inline-start: 20px;
+ padding-inline-end: 2px;
+ min-width: 30px;
+ margin: 0px;
+}
+
+.CardViewLink {
+ color: blue;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+
+#cvHomeMapIt, #cvWorkMapIt {
+ margin-bottom: 0px;
+}
+
+html|a {
+ padding-inline-start: 0px;
+ padding-inline-end: 2px;
+ border: none !important;
+}
+
+html|p {
+ border: none !important;
+}
+
+/* ::::: lightweight themes ::::: */
+
+#CardViewOuterBox:-moz-lwtheme {
+ background-color: -moz-dialog;
+ color: -moz-dialogtext;
+ text-shadow: none
+}
diff --git a/comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons-small.png b/comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons-small.png
new file mode 100644
index 0000000000..8024eb395f
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons.png b/comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons.png
new file mode 100644
index 0000000000..f9ca42c789
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/addressbook/icons/addressbookicons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/browserRequest.css b/comm/suite/themes/classic/mac/messenger/browserRequest.css
new file mode 100644
index 0000000000..7ce21092c5
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/browserRequest.css
@@ -0,0 +1,59 @@
+/* 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/. */
+
+#security-button {
+ width: 20px;
+ margin-top: -1px;
+ margin-right: 5px;
+ background-repeat: no-repeat;
+}
+
+#security-button[level="high"] {
+ background-image: url("chrome://messenger/skin/icons/secure.png");
+}
+
+#security-button[level="broken"] {
+ background-image: url("chrome://messenger/skin/icons/insecure.png");
+}
+
+#security-button[loading="true"] {
+ background-image: url("chrome://messenger/skin/icons/loading.png");
+ background-position: 4px 3px;
+}
+
+@media (min-resolution: 2ddpx) {
+ #security-button[loading="true"] {
+ background-image: url("chrome://messenger/skin/icons/loading@2x.png");
+ }
+}
+
+/*
+#header {
+ overflow: hidden;
+ padding: 5px;
+ border-bottom: 1px solid black;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+*/
+
+
+#header {
+ border-bottom: 1px solid rgb(105, 105, 105);
+ overflow: hidden;
+}
+
+#addressbox {
+ font-weight: bold;
+ font-size: normal;
+ -moz-appearance: textfield;
+ overflow: hidden;
+ margin: 5px 5px;
+ font-weight: normal;
+}
+
+#headerMessage {
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/filterDialog.css b/comm/suite/themes/classic/mac/messenger/filterDialog.css
new file mode 100644
index 0000000000..3709323b36
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/filterDialog.css
@@ -0,0 +1,108 @@
+/* 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/. */
+
+/* ===== filterDialog.css ===============================================
+ == Styles for the Mail Filters dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: columns :::::: */
+
+listcell.listcell-iconic > .listcell-label {
+ display: none;
+}
+
+listcell[enabled="false"] {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+listcell[enabled="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/check.png");
+}
+
+.small-button {
+ -moz-appearance: none;
+ font: icon;
+ text-shadow: 0 1px #F2F2F2;
+ border: 1px solid #A8A8A8;
+ background: linear-gradient(to top, #ECECEC, #ECECEC 50%, #F9F9F9 0%, #F9F9F9);
+ min-width: 22px;
+ height: 20px;
+ padding: 0 1px 1px 0;
+ margin: 0;
+}
+
+.small-button:first-child {
+ margin-left: 1px;
+}
+
+.small-button:not([disabled="true"]):hover:active {
+ background: linear-gradient(to top, #B0B0B0, #B4B4B4 50%, #BFBFBF 0%, #BABABA);
+ border: 1px solid #9C9C9C;
+}
+
+.small-button[disabled="true"] {
+ opacity: 0.6;
+}
+
+.small-button + .small-button,
+.small-button + .small-button:hover:active {
+ border-left-width: 0;
+ min-width: 21px;
+}
+
+
+.small-button:focus {
+ box-shadow: inset 0 0 1px -moz-mac-focusring,
+ 1px 0 2px 1px -moz-mac-focusring,
+ -1px 0 2px 1px -moz-mac-focusring;
+}
+
+.small-button:focus + .small-button {
+ box-shadow: inset 1px 0 -moz-mac-focusring;
+}
+
+listcell > hbox {
+ -moz-box-pack: end;
+}
+
+/* No '.filler' here, so add margin to make more room. */
+hbox > .small-button + .small-button {
+ margin-inline-end: 9px;
+}
+
+.search-menulist, .search-value-menulist {
+ width: 12em;
+}
+
+.search-menulist[unavailable="true"] {
+ color: GrayText;
+}
+
+#searchTermList > listitem[selected="true"] {
+ background-color: inherit;
+}
+
+#filterListDialog {
+ padding: 0px;
+}
+
+.filler {
+ padding-inline-end: 22px;
+}
+
+.ruleaction {
+ border: 1px solid transparent;
+}
+
+.ruleactionitem {
+ min-width: 20em;
+}
+
+.ruleaction-type {
+ min-width: 15em;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/icons/junk.png b/comm/suite/themes/classic/mac/messenger/icons/junk.png
new file mode 100644
index 0000000000..0ae15cd390
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/junk.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/messengericons-small.png b/comm/suite/themes/classic/mac/messenger/icons/messengericons-small.png
new file mode 100644
index 0000000000..b51ef83d27
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/messengericons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/messengericons.png b/comm/suite/themes/classic/mac/messenger/icons/messengericons.png
new file mode 100644
index 0000000000..e450734a1b
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/messengericons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/phishing.png b/comm/suite/themes/classic/mac/messenger/icons/phishing.png
new file mode 100644
index 0000000000..d99a9ea6d4
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/phishing.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/spin-buttons-active.png b/comm/suite/themes/classic/mac/messenger/icons/spin-buttons-active.png
new file mode 100644
index 0000000000..5ff980eb98
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/spin-buttons-active.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/spin-buttons.png b/comm/suite/themes/classic/mac/messenger/icons/spin-buttons.png
new file mode 100644
index 0000000000..010b170e8e
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/spin-buttons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/tab-arrow-left.png b/comm/suite/themes/classic/mac/messenger/icons/tab-arrow-left.png
new file mode 100644
index 0000000000..ca55639992
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/tab-arrow-left.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/tab-arrow-right.png b/comm/suite/themes/classic/mac/messenger/icons/tab-arrow-right.png
new file mode 100644
index 0000000000..3374e2305c
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/tab-arrow-right.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/twisty-clsd.png b/comm/suite/themes/classic/mac/messenger/icons/twisty-clsd.png
new file mode 100644
index 0000000000..ed46b222be
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/twisty-clsd.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/icons/twisty-open.png b/comm/suite/themes/classic/mac/messenger/icons/twisty-open.png
new file mode 100644
index 0000000000..2aa35f80b1
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/icons/twisty-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/mailWindow1.css b/comm/suite/themes/classic/mac/messenger/mailWindow1.css
new file mode 100644
index 0000000000..6e40fff2f6
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/mailWindow1.css
@@ -0,0 +1,266 @@
+/* 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/. */
+
+/* ===== mailWindow1.css ================================================
+ == Styles for the main Mail window in the default layout scheme.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+@import url("chrome://messenger/skin/primaryToolbar.css");
+@import url("chrome://messenger/skin/folderMenus.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#messengerWindow {
+ -moz-appearance: none;
+ background-color: #EEEEEE;
+}
+
+#messengerBox {
+ border-top: 1px solid #A1A1A1;
+}
+
+/* :::::
+ :: Make sure the min height is small so we can
+ :: resize the pane vertically -EDV
+ ::::: */
+
+#messagepanebox {
+ min-height: 10px;
+ height: 0px;
+}
+
+#messagepanebox:-moz-lwtheme {
+ background-color: -moz-dialog;
+}
+
+#searchToolbar {
+ -moz-appearance: none;
+ background-color: #CDCDCD;
+ background-image: url("chrome://communicator/skin/toolbar/toolbar-gradient34.png");
+ background-repeat: repeat-x;
+ border-bottom: 1px solid #9F9F9F;
+}
+
+#searchToolbar:-moz-lwtheme {
+ background-image: none;
+ background-color: transparent;
+}
+
+/* Leopard-styled "zero-width" vertical splitter */
+
+#folderpane-splitter {
+ border: solid #B3B3B3;
+ border-width: 0 1px 0 0;
+ min-width: 1px;
+ background-image: none;
+}
+
+/* Make sure users find the splitter when it's collapsed... */
+
+#folderpane-splitter[state="collapsed"] {
+ min-width: 5px;
+ background-color: #D6DDE5;
+}
+
+/* Thick horisontal splitter */
+
+#threadpane-splitter {
+ background-color: -moz-dialog;
+ border: solid #B3B3B3;
+ border-width: 0 0 0 1px;
+}
+
+/* Thick vertical splitter */
+
+#threadpane-splitter[orient="vertical"] {
+ border: solid #A5A5A5;
+ border-width: 1px 0;
+}
+
+/* ..... tree adjustments ..... */
+
+#folderTree {
+ min-width: 1px;
+ -moz-appearance: -moz-mac-source-list;
+ -moz-font-smoothing-background-color: -moz-mac-source-list;
+}
+
+.tree-stack > .tree-rows > .tree-bodybox {
+ border: none;
+}
+
+#folderTree > treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+#folderTree > treechildren::-moz-tree-row(selected) {
+ -moz-appearance: -moz-mac-source-list-selection;
+ -moz-font-smoothing-background-color: -moz-mac-source-list-selection;
+}
+
+#folderTree > treechildren::-moz-tree-row(selected, focus) {
+ -moz-appearance: -moz-mac-active-source-list-selection;
+ -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
+}
+
+#folderTree > treechildren::-moz-tree-twisty(selected),
+#folderTree > treechildren::-moz-tree-cell-text(selected) {
+ color: #FFFFFF;
+ fill-opacity: 1;
+}
+
+#folderTree > treechildren::-moz-tree-twisty {
+ width: 16px;
+ padding-bottom: 1px;
+}
+
+@media (-moz-mac-yosemite-theme) {
+ #folderTree > treechildren::-moz-tree-twisty(selected),
+ #folderTree > treechildren::-moz-tree-cell-text(selected) {
+ color: -moz-dialogtext;
+ fill-opacity: 0.6;
+ }
+
+ #folderTree > treechildren::-moz-tree-twisty(selected, focus),
+ #folderTree > treechildren::-moz-tree-cell-text(selected, focus) {
+ color: #FFFFFF;
+ fill-opacity: 1;
+ }
+}
+
+#threadTree {
+ min-width: 1px;
+}
+
+#locationIcon {
+ list-style-image: none;
+}
+
+/* ..... message pane adjustments ..... */
+
+#msgHeaderView {
+ border-left: none !important;
+}
+
+#messagepane {
+ border: 1px solid transparent;
+ border-right: none;
+}
+
+#messagepanebox[focusring="true"] > #messagepanewrapper > #messagepane {
+ border-color: -moz-mac-focusring;
+}
+
+/* ..... folder pane adjustments ..... */
+
+.folderview-cycler,
+.folderview-cycler > .toolbarbutton-icon {
+ padding: 0 !important;
+ margin: 0 !important;
+ border: none !important;
+}
+
+.folderview-cycler > .toolbarbutton-text {
+ display: none;
+}
+
+.folderview-cycler[dir="prev"],
+.folderview-cycler[dir="next"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://messenger/skin/icons/spin-buttons.png");
+ -moz-image-region: rect(0 15px 16px 0);
+}
+
+.folderview-cycler[dir="next"],
+.folderview-cycler[dir="prev"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://messenger/skin/icons/spin-buttons.png");
+ -moz-image-region: rect(0 31px 16px 15px);
+}
+
+.folderview-cycler:active {
+ list-style-image: url("chrome://messenger/skin/icons/spin-buttons-active.png") !important;
+}
+
+/* ..... tabmail ..... */
+
+.tabmail-strip {
+ padding-bottom: 3px;
+ background-color: #E8E8E8;
+}
+
+.tabmail-strip:-moz-lwtheme {
+ background-color: transparent;
+}
+
+.tab-close-button {
+ margin: 0;
+}
+
+.tab-scrollbutton-up,
+.tab-scrollbutton-down {
+ border: 0;
+ padding: 0 4px;
+ margin: 0;
+}
+
+.tab-scrollbutton-up:-moz-locale-dir(ltr),
+.tab-scrollbutton-down:-moz-locale-dir(rtl) {
+ border-right: 1px solid rgba(0, 0, 0, 0.19);
+ list-style-image: url("chrome://messenger/skin/icons/tab-arrow-left.png");
+ -moz-image-region: rect(0, 7px, 11px, 0);
+}
+
+.tab-scrollbutton-up[disabled="true"]:-moz-locale-dir(ltr),
+.tab-scrollbutton-down[disabled="true"]:-moz-locale-dir(rtl) {
+ -moz-image-region: rect(0, 14px, 11px, 7px);
+ border-right-color: transparent;
+}
+
+.tab-scrollbutton-down:-moz-locale-dir(ltr),
+.tab-scrollbutton-up:-moz-locale-dir(rtl) {
+ border-left: 1px solid rgba(0, 0, 0, 0.19);
+ list-style-image: url("chrome://messenger/skin/icons/tab-arrow-right.png");
+ -moz-image-region: rect(0, 7px, 11px, 0);
+}
+
+.tab-scrollbutton-down[disabled="true"]:-moz-locale-dir(ltr),
+.tab-scrollbutton-up[disabled="true"]:-moz-locale-dir(rtl) {
+ -moz-image-region: rect(0, 14px, 11px, 7px);
+ border-left-color: transparent;
+}
+
+.tab-scrollbutton-up:hover:not([disabled="true"]),
+.tab-scrollbutton-down:hover:not([disabled="true"]) {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.tab-scrollbutton-up:hover:active:not([disabled="true"]),
+.tab-scrollbutton-down:hover:active:not([disabled="true"]) {
+ background-color: rgba(0, 0, 0 , 0.2);
+}
+
+.tabmail-tab[type="message"] .tab-icon {
+ margin-top: -2px;
+}
+
+.tabmail-tab[type="folder"][NewMessages="true"],
+.tabmail-tab[type="folder"][IsServer="true"] {
+ font-weight: bold;
+}
+
+.alltabs-item[tabIsScrolled] {
+ font-style: italic;
+}
+
+#tabbar-toolbar {
+ -moz-appearance: none;
+ min-height: 0;
+}
+
+#tabbar-toolbar > .toolbarbutton-1 {
+ margin-top: 1px;
+ margin-bottom: 1px;
+ padding-top: 0;
+ padding-bottom: 0;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/messageHeader.css b/comm/suite/themes/classic/mac/messenger/messageHeader.css
new file mode 100644
index 0000000000..7a823a9860
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/messageHeader.css
@@ -0,0 +1,178 @@
+/* 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/. */
+
+/* ===== messageHeader.css ==============================================
+ == Styles for the header toolbars of a mail message.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: msg header buttons ::::: */
+
+#expandedAttachmentBox {
+ width: 20em;
+ height: 0;
+ list-style-image: url("chrome://messenger/skin/icons/attach.png");
+}
+
+#attachmentText {
+ font-weight: bold;
+}
+
+#attachmentList {
+ min-height: 58px;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+/* ::::: msg header captions ::::: */
+
+#msgHeaderView {
+ border-right: 1px solid ThreeDShadow;
+ border-left: 1px solid ThreeDShadow;
+}
+
+#collapsedHeaderView,
+#expandedHeaderView {
+ margin: 2px 4px 4px 4px;
+ border-bottom: 1px solid ThreeDDarkShadow;
+ min-width: 1px;
+}
+
+.headerNameBox {
+ width: 7.7em;
+}
+
+.headerName {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ margin-inline-start: 0px;
+ margin-inline-end: .5em;
+ font-weight: bold;
+ text-align: right;
+}
+
+.headerValue {
+ margin: 0;
+ min-width: 50px;
+ white-space: normal;
+}
+
+.headerValueBox {
+ margin: 0 0 5px;
+}
+
+.headerValueBox[singleline="true"] {
+ overflow: hidden;
+}
+
+.subjectvalue {
+ font-weight: bold;
+}
+
+.tagvalue {
+ margin-top: 0;
+ margin-inline-start: 0;
+}
+
+/* ::::: msg header message ids ::::: */
+
+.messageIdDisplayButton {
+ cursor: pointer;
+ color: #0000FF;
+ text-decoration: underline;
+ margin: 0;
+}
+
+.messageIdDisplayButton:hover {
+ color: #FF0000;
+}
+
+.messageIdDisplayImage {
+ padding-inline-start: 2px;
+ -moz-box-pack: end;
+}
+
+/* ::::: msg header email addresses ::::: */
+
+.emailDisplayButton {
+ cursor: pointer;
+ color: #0000FF;
+ text-decoration: underline;
+ margin: 0;
+}
+
+.emailDisplayButton:hover {
+ color: #FF0000;
+}
+
+.emailDisplayImage {
+ padding-inline-start: 2px;
+ -moz-box-pack: end;
+}
+
+/* ::::: email address twisty ::::: */
+
+.addresstwisty {
+ margin: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/twisty-clsd.png");
+}
+
+.addresstwisty[open] {
+ list-style-image: url("chrome://messenger/skin/icons/twisty-open.png");
+}
+
+/* ::::: view expand and collapse twisties ::::: */
+
+.expandHeaderViewButton {
+ list-style-image: url("chrome://messenger/skin/icons/twisty-open.png");
+}
+
+.collapsedHeaderViewButton {
+ margin: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/twisty-clsd.png");
+}
+
+/* ::::: collapsed view styles ::::: */
+
+.collapsedHeaderDisplayName {
+ margin: 0 0.5em 0 0;
+ min-height: 16px;
+ font-weight: bold;
+}
+
+#expandedsubjectBox {
+ margin-top: 5px;
+}
+
+
+#collapsedAttachmentBox {
+ -moz-box-pack: center;
+}
+
+#collapseddateValue {
+ margin: 0 .5em;
+ text-align: right;
+}
+
+#collapseddateValue > .textbox-input-box menupopup {
+ text-align: left;
+}
+
+#collapsedfromBox {
+ width: 18em;
+}
+
+#collapseddateBox {
+ width: 12em;
+}
+
+.collapsedHeaderValue {
+ margin: 0;
+}
+
+.collapsedAttachmentButton {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach.png");
+ margin-inline-end: .5em;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/messageQuotes.css b/comm/suite/themes/classic/mac/messenger/messageQuotes.css
new file mode 100644
index 0000000000..62cb67d990
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/messageQuotes.css
@@ -0,0 +1,43 @@
+/* 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/. */
+
+/* Because this sheet is loaded synchronously while the user is waiting for the
+ compose window to appear, it must not @import a ton of other things, and
+ especially must not trigger network access. */
+
+/* ===== messageQuotes.css =================================================
+ == Shared styles such as block quote colors and signature style
+ == between the message body during
+ == message display and the mail editor instance for mail compose.
+ ======================================================================= */
+
+/* workaround for MS Outlook mails where the line-height is set to 0 */
+body {
+ line-height: initial !important;
+}
+
+/* ::::: signature ::::: */
+
+@media not print {
+ div.moz-text-flowed > div.moz-txt-sig,
+ div.moz-text-plain > pre > div.moz-txt-sig,
+ pre.moz-signature {
+ opacity: 0.6;
+ }
+}
+
+blockquote[type=cite] {
+ color: blue;
+ border-color: blue;
+}
+
+blockquote[type=cite] blockquote[type=cite] {
+ color: green;
+ border-color: green;
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+ color: maroon;
+ border-color: maroon;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/messengercompose/messengercompose.css b/comm/suite/themes/classic/mac/messenger/messengercompose/messengercompose.css
new file mode 100644
index 0000000000..e0e4578dfc
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/messengercompose/messengercompose.css
@@ -0,0 +1,249 @@
+/* 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/. */
+
+/* ===== messengercompose.css ===========================================
+ == Styles for the main Messenger Compose window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-send {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(330px 29px 359px 0);
+}
+
+#button-send:hover:active {
+ -moz-image-region: rect(330px 59px 359px 30px);
+}
+
+#button-send[disabled="true"] {
+ -moz-image-region: rect(330px 89px 359px 60px) !important;
+}
+
+#button-address {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(270px 29px 299px 0);
+}
+
+#button-address:hover:active {
+ -moz-image-region: rect(270px 59px 299px 30px);
+}
+
+#button-address[disabled="true"] {
+ -moz-image-region: rect(270px 89px 299px 60px) !important;
+}
+
+#button-attach {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(300px 29px 329px 0);
+}
+
+#button-attach:hover:active,
+#button-attach[open] {
+ -moz-image-region: rect(300px 59px 329px 30px);
+}
+
+#button-attach[disabled="true"] {
+ -moz-image-region: rect(300px 89px 329px 60px) !important;
+}
+
+#spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(240px 29px 269px 0);
+}
+
+#spellingButton:hover:active,
+#spellingButton[open] {
+ -moz-image-region: rect(240px 59px 269px 30px);
+}
+
+#spellingButton[disabled="true"] {
+ -moz-image-region: rect(240px 89px 269px 60px) !important;
+}
+
+#button-save {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(210px 29px 239px 0);
+}
+
+#button-save:hover:active,
+#button-save[open] {
+ -moz-image-region: rect(210px 59px 239px 30px);
+}
+
+#button-save[disabled="true"] {
+ -moz-image-region: rect(210px 89px 239px 60px) !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #button-send,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-send {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(220px 19px 239px 0);
+}
+
+toolbar[iconsize="small"] > #button-send:hover:active {
+ -moz-image-region: rect(220px 39px 239px 20px);
+}
+
+toolbar[iconsize="small"] > #button-send[disabled="true"] {
+ -moz-image-region: rect(220px 59px 239px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-address,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-address {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(180px 19px 199px 0);
+}
+
+toolbar[iconsize="small"] > #button-address:hover:active {
+ -moz-image-region: rect(180px 39px 199px 20px);
+}
+
+toolbar[iconsize="small"] > #button-address[disabled="true"] {
+ -moz-image-region: rect(180px 59px 199px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-attach,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-attach {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(200px 19px 219px 0);
+}
+
+toolbar[iconsize="small"] > #button-attach:hover:active,
+toolbar[iconsize="small"] > #button-attach[open] {
+ -moz-image-region: rect(200px 39px 219px 20px);
+}
+
+toolbar[iconsize="small"] > #button-attach[disabled="true"] {
+ -moz-image-region: rect(200px 59px 219px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #spellingButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(160px 19px 179px 0);
+}
+
+toolbar[iconsize="small"] > #spellingButton:hover:active,
+toolbar[iconsize="small"] > #spellingButton[open] {
+ -moz-image-region: rect(160px 39px 179px 20px);
+}
+
+toolbar[iconsize="small"] > #spellingButton[disabled="true"] {
+ -moz-image-region: rect(160px 59px 179px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-save,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-save {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(140px 19px 159px 0);
+}
+
+toolbar[iconsize="small"] > #button-save:hover:active,
+toolbar[iconsize="small"] > #button-save[open] {
+ -moz-image-region: rect(140px 39px 159px 20px);
+}
+
+toolbar[iconsize="small"] > #button-save[disabled="true"] {
+ -moz-image-region: rect(140px 59px 159px 40px) !important;
+}
+
+#attachmentbucket-sizer {
+ border-top: none;
+ border-bottom: none;
+}
+
+#compose-toolbar-sizer {
+ height: 10px;
+ background-color: -moz-dialog;
+ border-top: 1px solid #A5A5A5;
+ border-bottom: 1px solid #A5A5A5;
+}
+
+#msgSubject {
+ margin-top: 0px;
+ margin-bottom: 4px;
+}
+
+#MsgHeadersToolbar {
+ min-height: 0px; /* this undoes the min-height in toolbar.css */
+ -moz-appearance: none;
+}
+
+#FormatToolbar {
+ border-bottom: none !important;
+}
+
+/* ::::: autocomplete icons ::::: */
+
+treechildren::-moz-tree-cell-text(default-match) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 15px;
+ margin-inline-end: -3px;
+ border: none;
+}
+
+treechildren::-moz-tree-image(local-abook) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 4px;
+ margin-inline-end: -1px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addrbook.png");
+}
+
+treechildren::-moz-tree-image(remote-abook) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 2px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/remote-addrbook.png");
+}
+
+treechildren::-moz-tree-image(remote-err) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 2px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/remote-addrbook-error.png");
+}
+
+treechildren::-moz-tree-image(subscribed-news) {
+ margin-inline-start: 2px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+/* ::::: compact menulists ::::: */
+
+.menulist-compact {
+ -moz-binding: url("chrome://messenger/content/messengercompose/menulistCompactBindings.xml#menulist-compact");
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin: 0;
+}
+
+.menulist-compact > .menulist-label-box > .menulist-label {
+ text-align: end;
+}
+
+/* ::::: lightweight themes ::::: */
+
+#MsgHeadersToolbar:-moz-lwtheme,
+#FormatToolbar:-moz-lwtheme,
+#compose-toolbar-sizer:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
+
+#MsgHeadersToolbar textbox:-moz-lwtheme {
+ opacity: 1;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/primaryToolbar.css b/comm/suite/themes/classic/mac/messenger/primaryToolbar.css
new file mode 100644
index 0000000000..372c6472d9
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/primaryToolbar.css
@@ -0,0 +1,490 @@
+/* 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/. */
+
+/* ===== primaryToolbar.css =============================================
+ == Images for the Mail primary toolbar.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.mail-toolbox {
+ border-bottom: 1px solid #A1A1A1;
+ background-color: #E8E8E8;
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-getmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#button-getmsg:hover:active,
+#button-getmsg[open] {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#button-getmsg[disabled="true"] {
+ -moz-image-region: rect(90px 89px 119px 60px) !important;
+}
+
+#button-newmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(150px 29px 179px 0);
+}
+
+#button-newmsg:hover:active,
+#button-newmsg[open] {
+ -moz-image-region: rect(150px 59px 179px 30px);
+}
+
+#button-newmsg[disabled="true"] {
+ -moz-image-region: rect(150px 89px 179px 60px) !important;
+}
+
+#button-reply {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(210px 29px 239px 0);
+}
+
+#button-reply:hover:active,
+#button-reply[open] {
+ -moz-image-region: rect(210px 59px 239px 30px);
+}
+
+#button-reply[disabled="true"] {
+ -moz-image-region: rect(210px 89px 239px 60px) !important;
+}
+
+#button-replyall {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(240px 29px 269px 0);
+}
+
+#button-replyall:hover:active,
+#button-replyall[open] {
+ -moz-image-region: rect(240px 59px 269px 30px);
+}
+
+#button-replyall[disabled="true"] {
+ -moz-image-region: rect(240px 89px 269px 60px) !important;
+}
+
+#button-forward {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#button-forward:hover:active,
+#button-forward[open] {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#button-forward[disabled="true"] {
+ -moz-image-region: rect(60px 89px 89px 60px) !important;
+}
+
+#button-file {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#button-file:hover:active,
+#button-file[open] {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#button-file[disabled="true"] {
+ -moz-image-region: rect(30px 89px 59px 60px) !important;
+}
+
+#button-goback {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#button-goback:hover:active,
+#button-goback[open] {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#button-goback[disabled="true"] {
+ -moz-image-region: rect(60px 89px 89px 60px) !important;
+}
+
+#button-goforward {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#button-goforward:hover:active,
+#button-goforward[open] {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#button-goforward[disabled="true"] {
+ -moz-image-region: rect(90px 89px 119px 60px) !important;
+}
+
+#button-next {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(180px 29px 209px 0);
+}
+
+#button-next:hover:active,
+#button-next[open] {
+ -moz-image-region: rect(180px 59px 209px 30px);
+}
+
+#button-next[disabled="true"] {
+ -moz-image-region: rect(180px 89px 209px 60px) !important;
+}
+
+#button-delete {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#button-delete:hover:active {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#button-delete[disabled] {
+ -moz-image-region: rect(0 89px 29px 60px) !important;
+}
+
+toolbarpaletteitem > #button-delete {
+ display: -moz-box;
+}
+
+#button-mark {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#button-mark:hover:active,
+#button-mark[open] {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#button-mark[disabled="true"] {
+ -moz-image-region: rect(120px 89px 149px 60px) !important;
+}
+
+#button-junk {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(360px 29px 389px 0);
+}
+
+#button-junk:hover:active {
+ -moz-image-region: rect(360px 59px 389px 30px);
+}
+
+#button-junk[disabled="true"] {
+ -moz-image-region: rect(360px 89px 389px 60px) !important;
+}
+
+#button-stop {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#button-stop:hover:active {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#button-stop[disabled="true"] {
+ -moz-image-region: rect(30px 89px 59px 60px) !important;
+}
+
+#sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32-throbber.png");
+}
+
+#locationFolders {
+ width: 22em;
+}
+
+/* Force the folder location and mail view items to fit in the available width
+ in the Customize Toolbar dialog. */
+#palette-box #locationFolders,
+#palette-box #folder-location-container,
+#palette-box #searchInput,
+#palette-box #button-search-container,
+#palette-box #button-search,
+#palette-box #mailviews-container,
+#palette-box #viewPicker {
+ -moz-box-flex: 1;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #button-getmsg,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-getmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #button-getmsg:hover:active,
+toolbar[iconsize="small"] > #button-getmsg[open] {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #button-getmsg[disabled="true"] {
+ -moz-image-region: rect(60px 59px 79px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-newmsg,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(100px 19px 119px 0);
+}
+
+toolbar[iconsize="small"] > #button-newmsg:hover:active,
+toolbar[iconsize="small"] > #button-newmsg[open] {
+ -moz-image-region: rect(100px 39px 119px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newmsg[disabled="true"] {
+ -moz-image-region: rect(100px 59px 119px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-reply,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-reply {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(140px 19px 159px 0);
+}
+
+toolbar[iconsize="small"] > #button-reply:hover:active {
+ -moz-image-region: rect(140px 39px 159px 20px);
+}
+
+toolbar[iconsize="small"] > #button-reply[disabled="true"] {
+ -moz-image-region: rect(140px 59px 159px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-replyall,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-replyall {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(160px 19px 179px 0);
+}
+
+toolbar[iconsize="small"] > #button-replyall:hover:active {
+ -moz-image-region: rect(160px 39px 179px 20px);
+}
+
+toolbar[iconsize="small"] > #button-replyall[disabled="true"] {
+ -moz-image-region: rect(160px 59px 179px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-forward,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-forward {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #button-forward:hover:active,
+toolbar[iconsize="small"] > #button-forward[open] {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #button-forward[disabled="true"] {
+ -moz-image-region: rect(40px 59px 59px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-file,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-file {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #button-file:hover:active,
+toolbar[iconsize="small"] > #button-file[open] {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #button-file[disabled="true"] {
+ -moz-image-region: rect(20px 59px 39px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-goback,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-goback {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #button-goback:hover:active,
+toolbar[iconsize="small"] > #button-goback[open] {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #button-goback[disabled="true"] {
+ -moz-image-region: rect(40px 59px 59px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-goforward,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-goforward {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #button-goforward:hover:active,
+toolbar[iconsize="small"] > #button-goforward[open] {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #button-goforward[disabled="true"] {
+ -moz-image-region: rect(60px 59px 79px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-next,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-next {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(120px 19px 139px 0);
+}
+
+toolbar[iconsize="small"] > #button-next:hover:active,
+toolbar[iconsize="small"] > #button-next[open] {
+ -moz-image-region: rect(120px 39px 139px 20px);
+}
+
+toolbar[iconsize="small"] > #button-next[disabled="true"] {
+ -moz-image-region: rect(120px 59px 139px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-delete,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-delete {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #button-delete:hover:active {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #button-delete[disabled],
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-delete[disabled] {
+ -moz-image-region: rect(0 59px 19px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-mark,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-mark {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(80px 19px 99px 0);
+}
+
+toolbar[iconsize="small"] > #button-mark:hover:active,
+toolbar[iconsize="small"] > #button-mark[open] {
+ -moz-image-region: rect(80px 39px 99px 20px);
+}
+
+toolbar[iconsize="small"] > #button-mark[disabled="true"] {
+ -moz-image-region: rect(80px 59px 99px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-junk,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-junk {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(240px 19px 259px 0);
+}
+
+toolbar[iconsize="small"] > #button-junk:hover:active {
+ -moz-image-region: rect(240px 39px 259px 20px);
+}
+
+toolbar[iconsize="small"] > #button-junk[disabled],
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-junk[disabled] {
+ -moz-image-region: rect(240px 59px 259px 40px) !important;
+}
+
+toolbar[iconsize="small"] > #button-stop,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-stop {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #button-stop:hover:active {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #button-stop[disabled="true"] {
+ -moz-image-region: rect(20px 59px 39px 40px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button,
+toolbar[iconsize="small"] > #sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button[status=active],
+toolbar[iconsize="small"] > #sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16-throbber.png");
+}
+
+/* ::::: Lightning icon sizes ::::: */
+
+#lightning-button-calendar > .toolbarbutton-icon,
+#lightning-button-tasks > .toolbarbutton-icon,
+#extractEventButton > .toolbarbutton-icon,
+#extractTaskButton > .toolbarbutton-icon,
+#extractEventButton > .box-inherit > .toolbarbutton-icon,
+#extractTaskButton > .box-inherit > .toolbarbutton-icon {
+ width: 29px;
+ height: 29px;
+}
+
+toolbar[iconsize="small"] > #lightning-button-calendar > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #lightning-button-tasks > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #extractEventButton > .box-inherit > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #extractTaskButton > .box-inherit > .toolbarbutton-icon {
+ width: 19px;
+ height: 19px;
+}
+
+#lightning-button-calendar[disabled="true"] > .toolbarbutton-icon,
+#lightning-button-tasks[disabled="true"] > .toolbarbutton-icon,
+#extractEventButton[disabled="true"] > .box-inherit > .toolbarbutton-icon,
+#extractTaskButton[disabled="true"] > .box-inherit > .toolbarbutton-icon {
+ opacity: 0.3;
+}
+
+/* ::::: message notification bar style rules ::::: */
+
+.msgNotificationBar {
+ border-bottom: 1px solid #A5A5A5;
+ background-color: #C7BC8F;
+ color: black;
+}
+
+.msgNotificationBar:-moz-lwtheme {
+ text-shadow: none;
+}
+
+.msgNotificationBarText {
+ font-weight: bold;
+}
+
+.messageImage[value="remoteContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/remote-blocked.png");
+}
+
+.messageImage[value="junkContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/junk.png");
+}
+
+.messageImage[value="phishingContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/phishing.png");
+}
+
+.messageImage[value="mdnContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/info.png");
+}
+
+.inline-toolbar {
+ -moz-appearance: none;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/searchDialog.css b/comm/suite/themes/classic/mac/messenger/searchDialog.css
new file mode 100644
index 0000000000..347375beaa
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/searchDialog.css
@@ -0,0 +1,90 @@
+/* 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/. */
+
+/* ===== searchDialog.css ===============================================
+ == Styles for the Mail Search dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: box sizes ::::: */
+
+#searchTermTree {
+ min-height: 50px;
+}
+
+#searchTermListBox {
+ height: 100px;
+}
+
+#searchTermList > listitem[selected="true"] {
+ background-color: inherit;
+}
+
+listcell > hbox {
+ -moz-box-pack: end;
+}
+
+#searchResultListBox {
+ -moz-appearance: listbox;
+ margin-inline-start: 4px;
+ margin-inline-end: 4px;
+ height: 100px;
+}
+
+#searchAddressBookWindow,
+#searchMailWindow {
+ padding: 0px;
+}
+
+menulist:not(#menuSearchLocalSystem) {
+ width: 18em;
+}
+
+.search-menulist {
+ width: 12em;
+}
+
+.search-menulist[unavailable="true"] {
+ color: GrayText;
+}
+
+.small-button {
+ -moz-appearance: none;
+ font: icon;
+ text-shadow: 0 1px #F2F2F2;
+ border: 1px solid #A8A8A8;
+ background: linear-gradient(to top, #ECECEC, #ECECEC 50%, #F9F9F9 0%, #F9F9F9);
+ min-width: 22px;
+ height: 20px;
+ padding: 0 1px 1px 0;
+ margin: 0;
+}
+
+.small-button:not([disabled="true"]):hover:active {
+ background: linear-gradient(to top, #B0B0B0, #B4B4B4 50%, #BFBFBF 0%, #BABABA);
+ border: 1px solid #9C9C9C;
+}
+
+.small-button[disabled="true"] {
+ opacity: 0.6;
+}
+
+.small-button + .small-button,
+.small-button + .small-button:hover:active {
+ border-left-width: 0;
+ min-width: 21px;
+}
+
+.small-button:focus {
+ box-shadow: inset 0 0 1px -moz-mac-focusring,
+ 1px 0 2px 1px -moz-mac-focusring,
+ -1px 0 2px 1px -moz-mac-focusring;
+}
+
+.small-button:focus + .small-button {
+ box-shadow: inset 1px 0 -moz-mac-focusring;
+}
diff --git a/comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons-small.png b/comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons-small.png
new file mode 100644
index 0000000000..4c6d42fff2
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons.png b/comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons.png
new file mode 100644
index 0000000000..1434fa34ca
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/smime/icons/smimeicons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/messenger/smime/msgCompSMIMEOverlay.css b/comm/suite/themes/classic/mac/messenger/smime/msgCompSMIMEOverlay.css
new file mode 100644
index 0000000000..3c68553bbd
--- /dev/null
+++ b/comm/suite/themes/classic/mac/messenger/smime/msgCompSMIMEOverlay.css
@@ -0,0 +1,68 @@
+/* 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/. */
+
+/* ===== msgCompSMIMEOverlay.css ========================================
+ == Styles for the S/Mime in composer window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#button-security {
+ list-style-image: url("chrome://messenger/skin/smime/icons/smimeicons.png");
+ -moz-image-region: rect(0 29px 29px 0px);
+}
+
+#button-security:hover:active,
+#button-security[open] {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#button-security[disabled] {
+ -moz-image-region: rect(0 89px 29px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-security,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-security {
+ list-style-image: url("chrome://messenger/skin/smime/icons/smimeicons-small.png");
+ -moz-image-region: rect(0 19px 19px 0px);
+}
+
+toolbar[iconsize="small"] > #button-security:hover:active,
+toolbar[iconsize="small"] > #button-security[open] {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #button-security[disabled] {
+ -moz-image-region: rect(0 59px 19px 40px) !important;
+}
+
+#msgcomposeWindow #signing-status {
+ list-style-image: none;
+ visibility: collapse;
+}
+
+#msgcomposeWindow[signing="ok"] #signing-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow[signing="notok"] #signing-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignNotOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow #encryption-status {
+ list-style-image: none;
+ visibility: collapse;
+}
+
+#msgcomposeWindow[crypto="ok"] #encryption-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow[crypto="notok"] #encryption-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoNotOk.png");
+ visibility: visible;
+}
diff --git a/comm/suite/themes/classic/mac/navigator/icons/close.png b/comm/suite/themes/classic/mac/navigator/icons/close.png
new file mode 100644
index 0000000000..9bba044ce5
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/close.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/identity.png b/comm/suite/themes/classic/mac/navigator/icons/identity.png
new file mode 100644
index 0000000000..44153b016b
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/identity.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/navigatoricons-small.png b/comm/suite/themes/classic/mac/navigator/icons/navigatoricons-small.png
new file mode 100644
index 0000000000..6f7bda51da
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/navigatoricons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/navigatoricons.png b/comm/suite/themes/classic/mac/navigator/icons/navigatoricons.png
new file mode 100644
index 0000000000..cfca28c59f
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/navigatoricons.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/panel-expander-closed.png b/comm/suite/themes/classic/mac/navigator/icons/panel-expander-closed.png
new file mode 100644
index 0000000000..f0e97b22e1
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/panel-expander-closed.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/panel-expander-open.png b/comm/suite/themes/classic/mac/navigator/icons/panel-expander-open.png
new file mode 100644
index 0000000000..e3febf4ffb
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/panel-expander-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/panel-plus-sign.png b/comm/suite/themes/classic/mac/navigator/icons/panel-plus-sign.png
new file mode 100644
index 0000000000..b12ecd514b
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/panel-plus-sign.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/icons/restore.png b/comm/suite/themes/classic/mac/navigator/icons/restore.png
new file mode 100644
index 0000000000..becb5420df
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/icons/restore.png
Binary files differ
diff --git a/comm/suite/themes/classic/mac/navigator/linkToolbar.css b/comm/suite/themes/classic/mac/navigator/linkToolbar.css
new file mode 100644
index 0000000000..b51da8f788
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/linkToolbar.css
@@ -0,0 +1,59 @@
+/* -*- Mode: C; 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/. */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#linktoolbar {
+ -moz-appearance: none;
+ background-color: #d5d5d5;
+ background-image: url("chrome://communicator/skin/toolbar/toolbar-gradient22.png");
+ background-repeat: repeat-x;
+ border-bottom: 1px solid rgb(159, 159, 159);
+}
+
+#linktoolbar:-moz-lwtheme {
+ background-color: transparent;
+ background-image: none;
+}
+
+#linktoolbar > .bookmark-item {
+ text-shadow: none;
+}
+
+#linktoolbar > .bookmark-item:not([disabled="true"]):hover:active,
+#linktoolbar > .bookmark-item[open] {
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.7);
+}
+
+#link-top {
+ list-style-image: url("chrome://navigator/skin/btn1/top.png");
+}
+
+#link-up {
+ list-style-image: url("chrome://navigator/skin/btn1/up.png");
+}
+
+#link-first {
+ list-style-image: url("chrome://navigator/skin/btn1/first.png");
+}
+
+#link-prev {
+ list-style-image: url("chrome://navigator/skin/btn1/previous.png");
+}
+
+#link-next {
+ list-style-image: url("chrome://navigator/skin/btn1/next.png");
+}
+
+#link-last {
+ list-style-image: url("chrome://navigator/skin/btn1/last.png");
+}
+
+#link-feed {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png") !important;
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
diff --git a/comm/suite/themes/classic/mac/navigator/navigator.css b/comm/suite/themes/classic/mac/navigator/navigator.css
new file mode 100644
index 0000000000..73b305eeb8
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/navigator.css
@@ -0,0 +1,956 @@
+/* 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/. */
+
+@import url("chrome://navigator/content/navigator.css");
+@import url("chrome://communicator/skin/");
+@import url("chrome://communicator/skin/places/bookmarksToolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+#main-window {
+ -moz-appearance: none;
+ background-color: #EEEEEE;
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+#back-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#back-button:hover:active,
+#back-button[open] {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#back-button[disabled="true"] {
+ -moz-image-region: rect(60px 89px 89px 60px) !important;
+}
+
+#forward-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#forward-button:hover:active,
+#forward-button[open] {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#forward-button[disabled="true"] {
+ -moz-image-region: rect(90px 89px 119px 60px) !important;
+}
+
+#reload-button {
+ list-style-image: url("chrome://navigator/skin/icons/navigatoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#reload-button:hover:active {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#reload-button[disabled="true"] {
+ -moz-image-region: rect(0 89px 29px 60px) !important;
+}
+
+#stop-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#stop-button:hover:active {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#stop-button[disabled="true"] {
+ -moz-image-region: rect(30px 89px 59px 60px) !important;
+}
+
+#home-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#home-button:hover:active {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#home-button[disabled="true"] {
+ -moz-image-region: rect(120px 89px 149px 60px) !important;
+}
+
+#sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32-throbber.png");
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #back-button,
+toolbar[iconsize="small"] > #back-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #back-button:hover:active,
+toolbar[iconsize="small"] > #back-button[open] {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #back-button[disabled="true"] {
+ -moz-image-region: rect(40px 59px 59px 40px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #forward-button,
+toolbar[iconsize="small"] > #forward-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #forward-button:hover:active,
+toolbar[iconsize="small"] > #forward-button[open] {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #forward-button[disabled="true"] {
+ -moz-image-region: rect(60px 59px 79px 40px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #reload-button,
+toolbar[iconsize="small"] > #reload-button {
+ list-style-image: url("chrome://navigator/skin/icons/navigatoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #reload-button:hover:active {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #reload-button[disabled="true"] {
+ -moz-image-region: rect(0 59px 19px 40px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #stop-button,
+toolbar[iconsize="small"] > #stop-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #stop-button:hover:active {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #stop-button[disabled="true"] {
+ -moz-image-region: rect(20px 59px 39px 40px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #home-button,
+toolbar[iconsize="small"] > #home-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(80px 19px 99px 0);
+}
+
+toolbar[iconsize="small"] > #home-button:hover:active {
+ -moz-image-region: rect(80px 39px 99px 20px);
+}
+
+toolbar[iconsize="small"] > #home-button[disabled="true"] {
+ -moz-image-region: rect(80px 59px 99px 40px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button,
+toolbar[iconsize="small"] > #sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button[status=active],
+toolbar[iconsize="small"] > #sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16-throbber.png");
+}
+
+#FindToolbar {
+ border-top: none;
+ border-bottom: 1px solid #888888;
+}
+
+/* ::::: fullscreen window controls ::::: */
+
+#window-controls {
+ -moz-box-align: center;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+}
+
+toolbar[mode="text"] > #window-controls > toolbarbutton > .toolbarbutton-icon {
+ display: -moz-box;
+}
+
+#restore-button {
+ list-style-image: url("chrome://navigator/skin/icons/restore.png");
+}
+
+/* ::::: nav-bar-inner ::::: */
+
+.nav-bar-class {
+ -moz-box-align: center;
+ min-width: 0px;
+ -moz-window-dragging: drag;
+}
+
+#urlbar > .autocomplete-textbox-container {
+ font: -moz-info;
+}
+
+/* nsNativeThemeCocoa adds top/bottom padding to textfields... */
+#urlbar > .autocomplete-textbox-container,
+#urlbar > .autocomplete-history-dropmarker {
+ margin-top: -1px;
+ margin-bottom: -2px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+}
+
+#urlbar[level="high"] > .autocomplete-textbox-container {
+ -moz-appearance: none;
+ background-color: #FFFFC7;
+ color: #000000;
+}
+
+#urlbar[level="high"] > .autocomplete-history-dropmarker {
+ background-color: #FFFFC7 !important;
+}
+
+#wrapper-nav-bar-inner[place="palette"] > #nav-bar-inner > .button-toolbar,
+#wrapper-nav-bar-inner[place="palette"] > #nav-bar-inner > #urlbar > .urlbar-icons {
+ display: none;
+}
+
+#urlbar-search-splitter {
+ min-width: 8px;
+ margin: 0 -4px;
+ background-image: none;
+ position: relative;
+ height: 22px;
+}
+
+/* ::::: notification popups ::::: */
+
+.popup-notification-icon {
+ width: 64px;
+ height: 64px;
+ margin-inline-end: 10px;
+}
+
+.popup-notification-icon[popupid="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geolocation-64.png");
+}
+
+.popup-notification-icon[popupid="persistent-storage"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-icons.svg#persistent-storage");
+}
+
+.popup-notification-icon[popupid="web-notifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-64.png");
+}
+
+.popup-notification-icon[popupid="addon-install-disabled"],
+.popup-notification-icon[popupid="addon-install-blocked"],
+.popup-notification-icon[popupid="addon-install-started"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
+.popup-notification-icon[popupid="addon-install-failed"],
+.popup-notification-icon[popupid="addon-install-complete"],
+.popup-notification-icon[popupid="lwtheme-install-request"],
+.popup-notification-icon[popupid="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ width: 32px;
+ height: 32px;
+}
+
+.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
+.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-64.png");
+}
+
+.popup-notification-icon[popupid="password"] {
+ list-style-image: url("chrome://communicator/skin/icons/key-64.png");
+}
+
+.addon-progress-description {
+ width: 350px;
+ max-width: 350px;
+}
+
+/* Notification icon box */
+#notification-popup-box {
+ margin: 0 3px;
+}
+
+.notification-anchor-icon:-moz-focusring {
+ box-shadow: 0 0 2px 1px -moz-mac-focusring inset,
+ 0 0 2px 2px -moz-mac-focusring;
+}
+
+#default-notification-icon {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#geo-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/geolocation-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#web-notifications-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#addons-notification-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#indexedDB-notification-icon {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#password-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/key-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+.center-item-box {
+ border-inline-start: 1px solid rgba(255, 255, 255, 0.2);
+ padding-top: 7px;
+ padding-inline-end: 11px;
+ padding-inline-start: 16px;
+ margin-bottom: -3px;
+}
+
+.center-item-box[padbottom="true"] {
+ padding-bottom: 12px;
+}
+
+.center-item-icon {
+ background-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+ background-repeat: no-repeat;
+ height: 16px;
+ width: 16px;
+ margin-bottom: 4px;
+ margin-inline-end: 6px;
+}
+
+.center-item-box[padbottom="true"][warn="true"] {
+ padding-bottom: 7px;
+}
+
+.center-item-box[showseparator="true"] {
+ border-top: 1px solid rgba(3, 14, 27, 0.1);
+}
+
+.center-item-box[warn="false"] > .center-item-warning {
+ display: none;
+}
+
+.center-item-warning > .text-link {
+ color: #3d8cd7;
+}
+
+.center-item-warning > .text-link[href=""] {
+ display: none;
+}
+
+.center-item-warning-icon {
+ background-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+ background-repeat: no-repeat;
+ width: 16px;
+ height: 15px;
+ margin-bottom: 4px;
+}
+
+.center-item-warning-description {
+ color: #828282;
+}
+
+.center-item-button {
+ min-width: 0;
+}
+
+/* ::::: page proxy icon ::::: */
+
+#page-proxy-deck,
+#page-proxy-favicon,
+#page-proxy-button {
+ width: 16px;
+ height: 16px;
+}
+
+#page-proxy-deck {
+ cursor: grab;
+ margin: 0 3px 1px 3px;
+}
+
+#page-proxy-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+#page-proxy-favicon {
+ list-style-image: none;
+}
+
+#page-proxy-button[pageproxystate="invalid"] {
+ cursor: default;
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png");
+}
+
+/* ::::: autocomplete ::::: */
+
+#PopupAutoComplete > richlistbox > richlistitem {
+ height: 20px;
+ min-height: 20px;
+ border: 0;
+ border-radius: 0;
+ padding: 0px 1px 0px 1px;
+}
+
+#PopupAutoComplete > richlistbox > richlistitem > .ac-title {
+ font: icon;
+ margin-inline-start: 6px;
+}
+
+#PopupAutoComplete > richlistbox {
+ padding: 0;
+}
+
+.autocomplete-textbox-container {
+ -moz-box-align: center;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(value) {
+ padding-inline-start: 15px;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(comment) {
+ color: GrayText;
+}
+
+.autocomplete-history-popup > .popup-internal-box {
+ padding: 0 !important;
+}
+
+.autocomplete-history-popup > menuitem {
+ -moz-appearance: none !important;
+ max-width: none !important;
+ font: inherit;
+ padding: 2px;
+ margin: 0;
+}
+
+.autocomplete-history-popup > menuitem[_moz-menuactive="true"],
+.autocomplete-search-engine[menuactive="true"] {
+ background-color: Highlight !important;
+ color: HighlightText !important;
+}
+
+.autocomplete-search-engine {
+ border-top: 1px solid #D9D9D9; /* Same color as menuseparator is probably sane here */
+ padding: 2px;
+}
+
+.autocomplete-search-engine-img {
+ margin-inline-end: 4px;
+ width: 16px;
+ height: 16px;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
+ color: GrayText;
+}
+
+/* ::::: searchbutton ::::: */
+
+#search-button {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ list-style-image: url("chrome://global/skin/icons/search-textbox.png");
+}
+
+toolbar[mode="text"] #search-button > .button-box > .button-icon,
+toolbar[mode="icons"] #search-button > .button-box > .button-text {
+ display: none;
+}
+
+/* ::::: search bar ::::: */
+
+.autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment),
+.autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment) {
+ color: GrayText;
+ font-size: smaller;
+}
+
+.autocomplete-treebody::-moz-tree-cell(suggesthint) {
+ border-top: 1px solid GrayText;
+}
+
+/* ::::: sidebar splitter ::::: */
+
+#sidebar-splitter {
+ border-left: none;
+ border-right: none;
+ min-width: 5px;
+}
+
+/* ::::: content area ::::: */
+
+#security-button[level="high"] > .statusbarpanel-contentbox {
+ background-color: #B4CD32;
+}
+
+#security-button[level="broken"] > .statusbarpanel-contentbox {
+ background-color: #E83404;
+}
+
+#security-button[label] > .statusbarpanel-contentbox {
+ background-color: #62C441;
+}
+
+#security-button > .statusbarpanel-contentbox > .statusbarpanel-text {
+ margin: 0px;
+ color: #FFFFFF;
+ text-shadow: none;
+}
+
+/* make same background-color translucent on lwthemes */
+#security-button[level="high"] > .statusbarpanel-contentbox:-moz-lwtheme {
+ background-color: rgba(232, 219, 153, .8);
+}
+
+#security-button[level="broken"] > .statusbarpanel-contentbox:-moz-lwtheme {
+ background-color: rgba(232, 52, 4, .8);
+}
+
+#security-button[label] > .statusbarpanel-contentbox:-moz-lwtheme {
+ background-color: rgba(98, 196, 65, .8);
+}
+
+#ev-button {
+ list-style-image: url("chrome://communicator/skin/icons/identity.png");
+}
+
+#popupIcon {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+#invalid-form-popup {
+ -moz-appearance: tooltip;
+ padding: 2px 3px 0px 3px;
+ max-width: 40em;
+ color: InfoText;
+ font-weight: bold;
+}
+
+/* Set default text-shadow that we will override if this is in the PT. */
+#personal-bookmarks {
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
+}
+
+/* ::::: personal toolbar ::::: */
+
+#PersonalToolbar {
+ -moz-appearance: none;
+ background-color: #d5d5d5;
+ background-image: url("chrome://communicator/skin/toolbar/toolbar-gradient22.png");
+ background-repeat: repeat-x;
+ border-bottom: 1px solid rgb(159, 159, 159);
+}
+
+#PersonalToolbar:-moz-lwtheme {
+ background-color: transparent;
+ background-image: none;
+}
+
+/* No text-shadow when we're in the PT. */
+#PersonalToolbar > toolbarbutton,
+#PersonalToolbar > #personal-bookmarks {
+ text-shadow: none;
+}
+
+/* text-shadow styling depends on where the items are, so use inheritance. */
+#PlacesToolbar > .bookmark-item,
+#PlacesToolbarItems > .bookmark-item {
+ text-shadow: inherit;
+}
+
+#PersonalToolbar > toolbarbutton:hover:active,
+#PersonalToolbar > toolbarbutton[open],
+#PlacesToolbarItems > .bookmark-item:hover:active,
+#PlacesToolbarItems > .bookmark-item[open] {
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.7) !important;
+}
+
+#PersonalToolbar > toolbarbutton > .toolbarbutton-text,
+.bookmark-item > .toolbarbutton-text {
+ margin: 0 3px 0 2px !important;
+}
+
+#PersonalToolbar > toolbarbutton > .toolbarbutton-icon,
+.bookmark-item > .toolbarbutton-icon {
+ padding: 0;
+}
+
+#bookmarks-button {
+ list-style-image: url("chrome://global/skin/tree/folder.png");
+ padding: 0;
+}
+
+toolbarbutton.chevron {
+ list-style-image: url("chrome://navigator/skin/icons/chevron.png");
+ height: 16px;
+ width: 16px;
+}
+
+toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+toolbarbutton.chevron > .toolbarbutton-text {
+ display: none; /* hide chevron label which has a width even if blank */
+}
+
+toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
+ display: -moz-box; /* display chevron icon in text mode */
+}
+
+/* Prevent [mode="icons"|"text"] from hiding the label and icon */
+#PlacesToolbarItems .bookmark-item > .toolbarbutton-text,
+#PlacesToolbarItems .bookmark-item > .toolbarbutton-icon {
+ display: -moz-box !important;
+}
+
+#PersonalToolbar[iconsize="small"] > toolbarpaletteitem > #home-button > .toolbarbutton-icon,
+#PersonalToolbar[iconsize="small"] > #home-button > .toolbarbutton-icon {
+ width: 16px;
+ height: 16px;
+}
+
+/*
+ In customize mode we hide the normal bookmark items and show a placeholder
+ for the drag/drop UI.
+*/
+.bookmarks-toolbar-customize {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+ display: none;
+ max-width: 15em !important;
+}
+
+#wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
+ width: 16px;
+ height: 16px;
+ background: url("chrome://communicator/skin/places/bookmarksToolbar.png") no-repeat;
+}
+
+/* ::::: star button ::::: */
+
+#star-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark.png");
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#star-button:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#star-button:hover:active {
+ -moz-image-region: rect(16px 48px 32px 32px);
+}
+
+#star-button[starred="true"] {
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#star-button[starred="true"]:hover {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#star-button[starred="true"]:hover:active {
+ -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+#editBookmarkPanelStarIcon {
+ list-style-image: url("chrome://communicator/skin/places/bookmark.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#editBookmarkPanelTitle {
+ font-size: 130%;
+ font-weight: bold;
+}
+
+/* ::::: Edit bookmark panel ::::: */
+
+#editBMPanel_rows > row {
+ margin-bottom: 8px;
+}
+
+#editBMPanel_rows > row:last-of-type {
+ margin-bottom: 0;
+}
+
+#editBMPanel_rows > row > textbox,
+#editBMPanel_rows > row > hbox > textbox {
+ -moz-appearance: none;
+ background: linear-gradient(#FAFAFA, #FFFFFF);
+ background-clip: padding-box;
+ border-radius: 3px;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ box-shadow: inset 0 1px 1px 1px rgba(0, 0, 0, 0.05),
+ 0 1px rgba(255, 255, 255, 0.3);
+ margin: 0;
+ padding: 3px 6px;
+}
+
+#editBMPanel_rows > row > textbox[focused="true"],
+#editBMPanel_rows > row > hbox > textbox[focused="true"] {
+ border-color: -moz-mac-focusring !important;
+ box-shadow: 0 0 1px -moz-mac-focusring inset,
+ 0 0 4px 1px -moz-mac-focusring,
+ 0 0 1.5px 1px -moz-mac-focusring;
+}
+
+.editBookmarkPanelHeaderButton,
+.editBookmarkPanelBottomButton {
+ -moz-appearance: none;
+ color: #434343;
+ border-radius: 4px;
+ border: 1px solid #B5B5B5;
+ background: linear-gradient(#FFFFFF, #F2F2F2);
+ box-shadow: inset 0 1px rgba(255, 255, 255, 0.8),
+ inset 0 0 1px rgba(255, 255, 255, 0.25),
+ 0 1px rgba(255, 255, 255, 0.3);
+ background-clip: padding-box;
+ background-origin: padding-box;
+ margin: 0;
+ padding: 2px 6px;
+}
+
+.editBookmarkPanelHeaderButton:hover:active,
+.editBookmarkPanelBottomButton:hover:active {
+ box-shadow: inset 0 1px 4px -3px #000, 0 1px rgba(255, 255, 255, 0.3);
+}
+
+.editBookmarkPanelHeaderButton:-moz-focusring,
+.editBookmarkPanelBottomButton:-moz-focusring {
+ box-shadow: inset 0 0 1px -moz-mac-focusring,
+ 0 0 4px 1px -moz-mac-focusring,
+ 0 0 2px 1px -moz-mac-focusring;
+}
+
+.editBookmarkPanelBottomButton {
+ margin-inline-start: 8px;
+}
+
+.editBookmarkPanelBottomButton[default="true"] {
+ background-color: #666666;
+}
+
+#editBookmarkPanelHeader {
+ margin-bottom: 6px;
+}
+
+#editBMPanel_editColumn {
+ color: #FFFFFF;
+}
+
+/* The following elements come from editBookmarkOverlay.xul, but the styling is
+ specific to the HUD-styled editBookmarkPanel. Styling shared by all
+ editBookmarkOverlay.xul consumers should be in editBookmarkOverlay.css. */
+
+#editBMPanel_newFolderBox {
+ background: linear-gradient(#FFFFFF, #F2F2F2);
+ background-origin: padding-box;
+ background-clip: padding-box;
+ border-radius: 0 0 3px 3px;
+ border: 1px solid #A5A5A5;
+ box-shadow: inset 0 1px rgba(255,255, 255, 0.8),
+ inset 0 0 1px rgba(255, 255, 255, 0.25),
+ 0 1px rgba(255, 255, 255, 0.3);
+ padding: 0;
+ margin: 0;
+ height: 20px;
+}
+
+#editBMPanel_newFolderButton {
+ -moz-appearance: none;
+ border: 0 solid #A5A5A5;
+ border-inline-end-width: 1px;
+
+ padding: 0 9px;
+ margin: 0;
+ min-width: 21px;
+ min-height: 20px;
+ height: 20px;
+ color: #FFFFFF;
+ list-style-image: url("chrome://navigator/skin/icons/panel-plus-sign.png");
+ position: relative;
+}
+
+#editBMPanel_newFolderButton .button-text {
+ display: none;
+}
+
+#editBMPanel_folderMenuList,
+#editBMPanel_tagsSelectorExpander,
+#editBMPanel_foldersExpander {
+ color: #434343;
+ border-radius: 4px;
+ border: 1px solid #B5B5B5;
+ background: linear-gradient(#FFFFFF, #F2F2F2);
+ box-shadow: inset 0 1px rgba(255, 255, 255, 0.8),
+ inset 0 0 1px rgba(255, 255, 255, 0.25),
+ 0 1px rgba(255, 255, 255, 0.3);
+ background-clip: padding-box;
+ background-origin: padding-box;
+ margin: 0;
+}
+
+#editBMPanel_folderMenuList {
+ -moz-appearance: none;
+ min-height: 22px;
+ padding-top: 2px;
+ padding-bottom: 1px;
+ padding-inline-start: 8px;
+ padding-inline-end: 4px;
+}
+
+#editBMPanel_tagsSelectorExpander,
+#editBMPanel_foldersExpander {
+ margin-inline-start: 4px;
+ padding-top: 2px;
+ padding-inline-start: 6px;
+ padding-inline-end: 2px;
+ min-width: 27px;
+ min-height: 22px;
+}
+
+#editBMPanel_folderMenuList > .menulist-dropmarker {
+ -moz-appearance: none;
+ display: -moz-box;
+ background-color: transparent;
+ border: 0;
+ margin: 0;
+ padding: 0;
+ padding-inline-end: 4px;
+ width: 7px;
+}
+
+#editBMPanel_folderMenuList > .menulist-dropmarker > .dropmarker-icon {
+ list-style-image: url("chrome://global/skin/icons/panel-dropmarker.png");
+}
+
+/**** folder tree and tag selector ****/
+#editBMPanel_folderTree,
+#editBMPanel_tagsSelector {
+ -moz-appearance: none;
+ background: linear-gradient(#FAFAFA, #FFFFFF);
+ background-clip: padding-box;
+ border-radius: 3px;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ box-shadow: inset 0 1px 1px 1px rgba(0, 0, 0, 0.05),
+ 0 1px rgba(255, 255, 255, 0.3);
+ margin: 0;
+}
+
+#editBMPanel_folderTree {
+ border-bottom: none;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ /* Implements editBookmarkPanel resizing on folderTree un-collapse. */
+ margin: 0 !important;
+ min-width: 27em;
+ position: relative;
+}
+
+
+#editBMPanel_folderTree:-moz-focusring,
+#editBMPanel_tagsSelector:-moz-focusring {
+ border-color: -moz-mac-focusring;
+ box-shadow: 0 0 4px 1px -moz-mac-focusring,
+ 0 0 2px 1px -moz-mac-focusring;
+}
+
+/* hover:active */
+#editBMPanel_newFolderButton:hover:active,
+#editBMPanel_folderMenuList[open="true"],
+#editBMPanel_folderMenuList:hover:active,
+#editBMPanel_tagsSelectorExpander:hover:active,
+#editBMPanel_foldersExpander:hover:active {
+ box-shadow: inset 0 1px 4px -3px #000000, 0 1px rgba(255, 255, 255, 0.3);
+}
+
+/* focus ring */
+#editBMPanel_newFolderButton:-moz-focusring,
+#editBMPanel_folderMenuList:-moz-focusring,
+#editBMPanel_tagsSelectorExpander:-moz-focusring,
+#editBMPanel_foldersExpander:-moz-focusring,
+#editBMPanel_namePicker[droppable="false"][focused="true"] > .menulist-editable-box {
+ box-shadow: inset 0 0 1px -moz-mac-focusring,
+ 0 0 4px 1px -moz-mac-focusring,
+ 0 0 2px 1px -moz-mac-focusring;
+}
+
+/* expander icons */
+#editBMPanel_rows > row > hbox > .expander-up {
+ list-style-image: url("chrome://navigator/skin/icons/panel-expander-open.png");
+}
+
+#editBMPanel_rows > row > hbox > .expander-down {
+ list-style-image: url("chrome://navigator/skin/icons/panel-expander-closed.png");
+}
+
+.editBMPanel_rowLabel {
+ text-align: end;
+}
+
+/* ::::: feeds ::::: */
+
+.feedsMenu {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+#BMB_feedsMenu[disabled="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16-disabled.png");
+}
+
+#feedsButton {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png");
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#feedsButton[open="true"] {
+ -moz-image-region: rect(48px 32px 64px 16px) !important;
+}
+
+/* Need to set fixed width to stop the zoom display from changing size and moving around the zoom buttons */
+#zoomLevel-display {
+ width: 60px;
+ max-width: 60px;
+ margin-left: 0px;
+ margin-right: 0px;
+}
diff --git a/comm/suite/themes/classic/mac/navigator/pageInfo.css b/comm/suite/themes/classic/mac/navigator/pageInfo.css
new file mode 100644
index 0000000000..3e912719da
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/pageInfo.css
@@ -0,0 +1,183 @@
+/* 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/. */
+
+@import "chrome://global/skin/global.css";
+
+#main-window {
+ width: 600px !important;
+ height: 530px !important;
+}
+
+#dragbox {
+ -moz-window-dragging: drag;
+ min-height: 20px;
+ -moz-appearance: toolbar;
+ -moz-box-align: center;
+}
+
+#tabbox {
+ margin: 0;
+ -moz-appearance: none;
+ padding: 0;
+}
+
+#tabs {
+ padding: 1px;
+ margin: 3px 0 8px;
+}
+
+tab {
+ -moz-appearance: toolbarbutton;
+ -moz-box-orient: vertical;
+ text-shadow: 0 1px rgba(255, 255, 255, 0.4);
+ margin: 0;
+}
+
+tab[selected=true] {
+ color: #FFFFFF;
+ text-shadow: 0 1px rgba(0, 0, 0, 0.4);
+}
+
+.tab-middle {
+ padding: 0;
+}
+
+.tab-icon {
+ display: none;
+}
+
+#mainDeck {
+ -moz-appearance: none;
+}
+
+/* Misc */
+tree {
+ margin: .5em;
+}
+
+.gridSeparator {
+ width: .5em;
+}
+
+textbox {
+ background: transparent !important;
+ border: none;
+ padding: 0px;
+ margin-top: 1px;
+ -moz-appearance: none;
+ -moz-user-focus: none;
+}
+
+textbox.header {
+ margin-inline-start: 0;
+}
+
+.iframe {
+ margin: .5em;
+ background: white;
+ overflow: auto;
+}
+
+.fixedsize {
+ height: 8.5em;
+}
+
+textbox[disabled] {
+ font-style: italic;
+}
+
+/* General Tab */
+#metatree {
+ margin: 0;
+}
+
+#metaTags > .groupbox-body {
+ padding: 0;
+}
+
+#general-security-identity {
+ white-space: pre-wrap;
+ line-height: 2em;
+}
+
+/* Media Tab */
+#imagetree {
+ min-height: 10em;
+}
+
+#mediaGrid {
+ min-height: 9em;
+}
+
+#mediaLabelColumn {
+ min-width: 10em;
+}
+
+#thepreviewimage {
+ margin: 1em;
+}
+
+treechildren::-moz-tree-cell-text(broken) {
+ font-style: italic;
+ color: graytext;
+}
+
+/* Feeds Tab */
+#feedListbox > richlistitem {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C0C0C0;
+}
+
+.feedTitle {
+ font-weight: bold;
+}
+
+/* Permissions Tab */
+#permList {
+ margin-top: .5em;
+ overflow: auto;
+}
+
+#permList > richlistitem {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C0C0C0;
+}
+
+#permList > richlistitem > label {
+ font-weight: bold;
+}
+
+/* Security Tab */
+#security-technical-longform1,
+#security-technical-longform2 {
+ margin-inline-start: 6px;
+}
+
+.fieldValue {
+ font-weight: bold;
+}
+
+#identity-icon {
+ width: 64px;
+ height: 64px;
+ max-height: 64px;
+ list-style-image: url("chrome://navigator/skin/icons/identity.png");
+ -moz-image-region: rect(0px, 64px, 64px, 0px);
+}
+
+#identity-icon.verifiedDomain {
+ -moz-image-region: rect(64px, 64px, 128px, 0px);
+}
+
+#identity-icon.verifiedIdentity {
+ -moz-image-region: rect(128px, 64px, 192px, 0px);
+}
diff --git a/comm/suite/themes/classic/mac/navigator/tabbrowser.css b/comm/suite/themes/classic/mac/navigator/tabbrowser.css
new file mode 100644
index 0000000000..1017f4293e
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/tabbrowser.css
@@ -0,0 +1,238 @@
+/* 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/. */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+tabbrowser {
+ border: 0 !important;
+}
+
+tabbox {
+ margin: 0;
+}
+
+tabpanels {
+ -moz-appearance: none;
+}
+
+.tabs-bottom {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+}
+
+.tabbrowser-tabs {
+ font: icon;
+ background-color: rgba(0, 0, 0, 0.1);
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
+ padding: 0;
+ margin: 0;
+ margin-bottom: 2px;
+ -moz-box-align: end;
+}
+
+.tabs-stack {
+ margin-top: -1px;
+}
+
+.tab-icon {
+ margin-inline-end: 3px;
+ width: 16px;
+ height: 16px;
+}
+
+.tabbrowser-tab {
+ margin: 0;
+ padding: 0;
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+ border: solid transparent;
+ border-width: 0 1px;
+ -moz-appearance: none;
+}
+
+.tabbrowser-tab:not([selected="true"]):-moz-locale-dir(ltr),
+.tabbrowser-tab:not([selected="true"]):-moz-locale-dir(rtl):first-child {
+ border-right-color: rgba(0, 0, 0, 0.19);
+}
+
+.tabbrowser-tab:not([selected="true"]):-moz-locale-dir(ltr):first-child,
+.tabbrowser-tab:not([selected="true"]):-moz-locale-dir(rtl) {
+ border-left-color: rgba(0, 0, 0, 0.19);
+}
+
+.tabbrowser-tab:not(:first-child) {
+ margin-inline-start: -1px;
+}
+
+.tab-middle {
+ padding: 4px 7px;
+}
+
+.tab-middle:-moz-lwtheme {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.tab-middle:-moz-lwtheme:not([selected="true"]) > .tab-text {
+ opacity: 0.8;
+}
+
+.tab-middle[selected="true"] {
+ padding: 1px 7px 4px;
+ background-color: #E8E8E8;
+}
+
+.tab-middle[selected="true"]:-moz-lwtheme {
+ background-color: transparent;
+}
+
+.tabbrowser-tab:hover > .tab-middle:not([selected="true"]) {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.tabbrowser-tab:-moz-lwtheme:hover > .tab-middle:not([selected="true"]) {
+ background-color: rgba(0, 0, 0, 0.20);
+}
+
+.tabbrowser-tab[busy] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.tabbrowser-tab[selected="true"] {
+ color: #000000;
+ text-shadow: none;
+ border-radius: 2px;
+ border-top-width: 1px;
+ border-top-color: rgba(0, 0, 0, 0.1);
+ border-left-color: rgba(0, 0, 0, 0.08);
+ border-right-color: rgba(0, 0, 0, 0.08);
+ margin-top: 2px;
+}
+
+.tabbrowser-tab:focus {
+ outline: 2px solid -moz-mac-focusring;
+ outline-offset: -2px;
+ -moz-outline-radius: 5px;
+}
+
+.tabbrowser-tab[beforeselected="true"]:-moz-locale-dir(ltr) {
+ border-right-color: transparent;
+}
+
+.tabbrowser-tab[beforeselected="true"]:-moz-locale-dir(rtl) {
+ border-left-color: transparent;
+}
+
+.tabs-newbutton > .toolbarbutton-text,
+.tabs-alltabs-button > .toolbarbutton-text,
+.tabs-closebutton > .toolbarbutton-text {
+ display: none;
+}
+
+/* ::::: Tab scrollbox arrow ::::: */
+
+.scrollbutton-up,
+.scrollbutton-down {
+ border: 0;
+ padding: 0 4px;
+ margin: 0;
+}
+
+.scrollbutton-up:-moz-locale-dir(ltr),
+.scrollbutton-down:-moz-locale-dir(rtl) {
+ border-right: 1px solid;
+ border-right-color: rgba(0, 0, 0, 0.19);
+ list-style-image: url("chrome://messenger/skin/icons/tab-arrow-left.png");
+ -moz-image-region: rect(0, 7px, 11px, 0);
+}
+
+.scrollbutton-up[disabled="true"]:-moz-locale-dir(ltr),
+.scrollbutton-down[disabled="true"]:-moz-locale-dir(rtl) {
+ -moz-image-region: rect(0, 14px, 11px, 7px);
+ border-right-color: transparent;
+}
+
+.scrollbutton-down:-moz-locale-dir(ltr),
+.scrollbutton-up:-moz-locale-dir(rtl) {
+ border-left: 1px solid;
+ border-left-color: rgba(0, 0, 0, 0.19);
+ list-style-image: url("chrome://messenger/skin/icons/tab-arrow-right.png");
+ -moz-image-region: rect(0, 7px, 11px, 0);
+ box-shadow: 0 0 0 9px transparent inset;
+ transition: box-shadow 1s ease-out;
+}
+
+.scrollbutton-down[notifybgtab="true"] {
+ box-shadow: 0 0 0 9px rgba(0, 0, 0, 0.2) inset;
+ transition: none;
+}
+
+.scrollbutton-down[disabled="true"]:-moz-locale-dir(ltr),
+.scrollbutton-up[disabled="true"]:-moz-locale-dir(rtl) {
+ -moz-image-region: rect(0, 14px, 11px, 7px);
+ border-left-color: transparent;
+}
+
+.scrollbutton-up:hover:not([disabled="true"]),
+.scrollbutton-down:hover:not([disabled="true"]) {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+.scrollbutton-up:hover:active:not([disabled="true"]),
+.scrollbutton-down:hover:active:not([disabled="true"]) {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+/* All Tabs Menupopup */
+.alltabs-item {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.alltabs-item[busy] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png") !important;
+}
+
+.alltabs-item[tabIsScrolled] {
+ font-style: italic;
+}
+
+/* ::::: close button ::::: */
+
+.tabs-closebutton {
+ margin: 3px;
+ padding: 1px;
+ list-style-image: url("chrome://navigator/skin/icons/close.png");
+ -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+.tabs-closebutton:hover {
+ -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+.tabs-closebutton:active:hover {
+ -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
+.tabs-newbutton {
+ margin: 0;
+ padding: 1px;
+ list-style-image: url("chrome://navigator/skin/icons/tab-new.png");
+}
+
+.tab-drop-indicator-bar {
+ height: 11px;
+ margin-top: -11px;
+ margin-inline-start: -6px;
+ position: relative;
+}
+
+.tab-drop-indicator {
+ height: 11px;
+ width: 11px;
+ margin-bottom: -5px;
+ position: relative;
+ list-style-image: url('chrome://navigator/skin/icons/tab-drag-indicator.png');
+}
+
+tooltip[tabpreview="true"] {
+ max-width: none;
+}
diff --git a/comm/suite/themes/classic/mac/navigator/webDeveloper.css b/comm/suite/themes/classic/mac/navigator/webDeveloper.css
new file mode 100644
index 0000000000..682c48c544
--- /dev/null
+++ b/comm/suite/themes/classic/mac/navigator/webDeveloper.css
@@ -0,0 +1,197 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* Mostly copied from mozilla/devtools/client/themes/commandline.inc.css */
+/* Developer Tools */
+
+/* Developer toolbar */
+
+/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
+ We are copy/pasting variables from light-theme and dark-theme,
+ since they aren't loaded in this context (within browser.css). */
+#developer-toolbar[devtoolstheme="light"] {
+ --gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
+ --gcli-input-background: #f0f1f2; /* --theme-toolbar-background */
+ --gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
+ --gcli-input-color: #18191a; /* --theme-body-color */
+ --gcli-border-color: #aaaaaa; /* --theme-splitter-color */
+ --selection-background: #4c9ed9; /* --theme-selection-background */
+ --selection-color: #f5f7fa; /* --theme-selection-color */
+}
+
+#developer-toolbar[devtoolstheme="dark"] {
+ --gcli-background-color: #343c45; /* --theme-toolbar-background */
+ --gcli-input-background: rgba(37, 44, 51, .6); /* --theme-tab-toolbar-background */
+ --gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
+ --gcli-input-color: #b6babf; /* --theme-body-color-alt */
+ --gcli-border-color: #000000; /* --theme-splitter-color */
+ --selection-background: #1d4f73; /* --theme-selection-background */
+ --selection-color: #f5f7fa; /* --theme-selection-color */
+}
+
+#developer-toolbar {
+ padding: 0;
+ background-color: var(--gcli-background-color);
+ border-top: 1px solid var(--gcli-border-color);
+}
+
+#developer-toolbar[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before {
+ filter: invert(1);
+}
+
+#developer-toolbar-toolbox-button {
+ list-style-image: url("chrome://devtools/skin/images/toggle-tools.png");
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+#developer-toolbar-toolbox-button:hover > .toolbarbutton-icon {
+ filter: brightness(120%);
+}
+
+#developer-toolbar-toolbox-button:hover:active > .toolbarbutton-icon {
+ filter: saturate(150%);
+}
+
+#developer-toolbar-toolbox-button[checked=true] > .toolbarbutton-icon {
+ filter: hue-rotate(180deg);
+}
+
+@media (min-resolution: 1.1dppx) {
+ #developer-toolbar-toolbox-button {
+ list-style-image: url("chrome://devtools/skin/images/toggle-tools@2x.png");
+ -moz-image-region: rect(0px, 128px, 32px, 96px);
+ }
+}
+
+/* Error counter */
+
+#developer-toolbar-toolbox-button[error-count]:before {
+ color: white;
+ min-width: 16px;
+ text-shadow: none;
+ background-color: firebrick;
+ border-radius: 2px;
+ margin-inline-end: 2px;
+/*
+ Firefox browser/themes/osx/browser.css
+ color: #FDF3DE;
+ min-width: 16px;
+ text-shadow: none;
+ background-image: linear-gradient(#B4211B, #8A1915);
+ border-radius: 1px;
+*/
+}
+
+/* GCLI */
+
+html|*#gcli-tooltip-frame,
+html|*#gcli-output-frame {
+ padding: 0;
+ border-width: 0;
+ background-color: transparent;
+}
+
+#gcli-output,
+#gcli-tooltip {
+ border-width: 0;
+ background-color: transparent;
+ -moz-appearance: none;
+}
+
+.gclitoolbar-input-node,
+.gclitoolbar-complete-node {
+ -moz-box-align: center;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-right: 8px;
+ text-shadow: none;
+ box-shadow: none;
+ background-color: transparent;
+}
+
+.gclitoolbar-input-node {
+ -moz-appearance: none;
+ color: var(--gcli-input-color);
+ background-color: var(--gcli-input-background);
+ background-repeat: no-repeat;
+ background-position: 4px center;
+ box-shadow: 1px 0 0 var(--gcli-border-color) inset,
+ -1px 0 0 var(--gcli-border-color) inset;
+ outline-style: none;
+ padding: 0;
+}
+
+.gclitoolbar-input-node[focused="true"] {
+ background-color: var(--gcli-input-focused-background);
+}
+
+.gclitoolbar-input-node::before {
+ content: "";
+ display: inline-block;
+ -moz-box-ordinal-group: 0;
+ width: 16px;
+ height: 16px;
+ margin: 0 2px;
+ background-image: url("chrome://devtools/skin/images/commandline-icon.png");
+ background-position: 0 center;
+ background-size: 32px 16px;
+}
+
+.gclitoolbar-input-node[focused="true"]::before {
+ background-position: -16px center;
+}
+
+@media (min-resolution: 1.1dppx) {
+ .gclitoolbar-input-node::before {
+ background-image: url("chrome://devtools/skin/images/commandline-icon@2x.png");
+ }
+}
+
+.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
+ background-color: var(--selection-background);
+ color: var(--selection-color);
+ text-shadow: none;
+}
+
+.gclitoolbar-complete-node {
+ padding-left: 21px;
+ background-color: transparent;
+ color: transparent;
+ z-index: 100;
+ pointer-events: none;
+}
+
+.gcli-in-incomplete,
+.gcli-in-error,
+.gcli-in-ontab,
+.gcli-in-todo,
+.gcli-in-closebrace,
+.gcli-in-param,
+.gcli-in-valid {
+ margin: 0;
+ padding: 0;
+}
+
+.gcli-in-incomplete {
+ border-bottom: 2px dotted #999;
+}
+
+.gcli-in-error {
+ border-bottom: 2px dotted #F00;
+}
+
+.gcli-in-ontab {
+ color: hsl(210,0%,35%);
+}
+
+.gcli-in-todo {
+ color: hsl(210,50%,35%);
+}
+
+.gcli-in-closebrace {
+ color: hsl(0,0%,80%);
+}
diff --git a/comm/suite/themes/classic/messenger/accountCentral.css b/comm/suite/themes/classic/messenger/accountCentral.css
new file mode 100644
index 0000000000..fdde99c557
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/accountCentral.css
@@ -0,0 +1,120 @@
+/* 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/. */
+
+/* ===== accountCentral.css ==========================================
+ == Styles for the Messenger Account Central panel.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#acctCentralGrid {
+ background-color: #FFFFFF;
+}
+
+spacer {
+ max-height: .5em;
+}
+
+spacer.big {
+ max-height: 4em;
+}
+
+/* ::::: rows ::::: */
+
+#acctCentralHeaderRow {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ padding-inline-start: 10px;
+ padding-inline-end: 0px;
+ font-size: 180%;
+ font-weight: bold;
+ color: #000000;
+}
+
+.acctCentralRow {
+ -moz-binding: url("chrome://communicator/skin/communicatorBindings.xml#row-iconic");
+ margin-inline-start: 10px;
+ font-size: 125%;
+ color: #000000;
+}
+
+.row-iconic-icon {
+ list-style-image: inherit;
+ margin-inline-end: 10px;
+}
+
+.acctCentralRowTitleBox {
+ background-color: -moz-dialog;
+ font-size: 150%;
+ font-weight: bold;
+ color: -moz-fieldtext;
+}
+
+/* ::::: links ::::: */
+
+.acctCentralLinkText {
+ cursor: pointer;
+ color: #212731;
+ text-decoration: underline;
+}
+
+.acctCentralLinkText:hover {
+ color: #39598E;
+}
+
+.acctCentralLinkText:hover:active {
+ color: #000000;
+}
+
+/* ::::: row icons ::::: */
+
+#ReadMessages {
+ list-style-image: url("chrome://messenger/skin/icons/acct-read.png");
+}
+
+#ComposeMessage {
+ list-style-image: url("chrome://messenger/skin/icons/acct-compose.png");
+}
+
+#SubscribeNewsgroups {
+ list-style-image: url("chrome://messenger/skin/icons/acct-subscribe.png");
+}
+
+#SubscribeImapFolders {
+ list-style-image: url("chrome://messenger/skin/icons/acct-subscribe.png");
+}
+
+#SubscribeRSS {
+ list-style-image: url("chrome://messenger/skin/icons/acct-subscribe.png");
+}
+
+#SearchMessages {
+ list-style-image: url("chrome://messenger/skin/icons/acct-search.png");
+}
+
+#AccountSettings {
+ list-style-image: url("chrome://messenger/skin/icons/acct-prefs.png");
+}
+
+#CreateAccount {
+ list-style-image: url("chrome://messenger/skin/icons/acct-newaccount.png");
+}
+
+#CreateFilters {
+ list-style-image: url("chrome://messenger/skin/icons/acct-filters.png");
+}
+
+#OfflineSettings {
+ list-style-image: url("chrome://messenger/skin/icons/acct-prefs.png");
+}
+
+#JunkSettingsMail {
+ list-style-image: url("chrome://messenger/skin/icons/acct-filters.png");
+}
+
+#JunkSettingsNews {
+ list-style-image: url("chrome://messenger/skin/icons/acct-filters.png");
+}
diff --git a/comm/suite/themes/classic/messenger/accountManage.css b/comm/suite/themes/classic/messenger/accountManage.css
new file mode 100644
index 0000000000..4fce958ede
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/accountManage.css
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ===== accountManage.css ==============================================
+ == Styles for the Mail Account Manager.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: account manager :::::: */
+
+.specialFolderPickerGrid {
+ margin-inline-start: 20px;
+}
+
+.fccReplyFollowsParent {
+ margin-inline-start: 20px;
+}
+
+.selectForOfflineUseButton {
+ list-style-image: url("chrome://communicator/skin/icons/offline.png");
+}
+
+.selectForOfflineUseButton > .button-box > .button-icon {
+ margin-inline-start: 4px;
+ margin-inline-end: 4px;
+}
+
+.signatureBox {
+ font-family: -moz-fixed;
+}
+
+listitem[default="true"],
+#identitiesList > listitem:first-child {
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(isDefaultServer-true) {
+ text-decoration: underline;
+}
+
+#accounttree treechildren::-moz-tree-indentation {
+ width: 1ch;
+}
+
+/* ::::: SMTP Server Panel :::::: */
+
+.smtpServerListItem {
+ padding-inline-start: 3px;
+}
+
+#backgroundBox {
+ background-color: ThreeDLightShadow;
+}
+
+#smtpServerInfoBox textbox {
+ background-color: transparent;
+}
diff --git a/comm/suite/themes/classic/messenger/accountWizard.css b/comm/suite/themes/classic/messenger/accountWizard.css
new file mode 100644
index 0000000000..265f83b5c0
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/accountWizard.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+/* ===== accountWizard.css ==============================================
+ == Styles for the Mail Account Wizard.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: account wizard :::::: */
+
+.awIdentityLabel {
+ width: 8em;
+ margin-inline-start: 5px;
+}
+
+.serverLabel {
+ width: 8em;
+}
+
+.serverDataBox {
+ margin-inline-start: 15px;
+}
diff --git a/comm/suite/themes/classic/messenger/addressbook/abResultsPane.css b/comm/suite/themes/classic/messenger/addressbook/abResultsPane.css
new file mode 100644
index 0000000000..a5cc6b2e7e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/abResultsPane.css
@@ -0,0 +1,16 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+treechildren::-moz-tree-image(GeneratedName) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/abcard.png");
+}
+
+treechildren::-moz-tree-image(GeneratedName, MailList) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/ablist.png");
+}
+
diff --git a/comm/suite/themes/classic/messenger/addressbook/addressPanes.css b/comm/suite/themes/classic/messenger/addressbook/addressPanes.css
new file mode 100644
index 0000000000..4a854b9cca
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/addressPanes.css
@@ -0,0 +1,36 @@
+/* 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/. */
+
+/* ===== addressPanes.css ================================================
+ == Styles for directory pane.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: directory pane icons ::::: */
+
+treechildren::-moz-tree-image(DirCol) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addrbook.png");
+}
+
+treechildren::-moz-tree-image(DirCol, IsMailList-true) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/ablist.png");
+}
+
+treechildren::-moz-tree-image(DirCol, IsRemote-true) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/remote-addrbook.png");
+}
+
+treechildren::-moz-tree-image(DirCol, IsRemote-true, IsSecure-true) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/secure-remote-addrbook.png");
+}
+
+#dirTree [sortDirection="ascending"] {
+ list-style-image: none;
+}
+
+#dirTree [sortDirection="descending"] {
+ list-style-image: none;
+}
diff --git a/comm/suite/themes/classic/messenger/addressbook/addressbook.css b/comm/suite/themes/classic/messenger/addressbook/addressbook.css
new file mode 100644
index 0000000000..f645a367c3
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/addressbook.css
@@ -0,0 +1,317 @@
+/* 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/. */
+
+/* ===== addressbook.css ================================================
+ == Styles for the main Address Book window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* ::::: Trees ::::: */
+
+treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+treechildren::-moz-tree-twisty {
+ padding-bottom: 1px;
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-newcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#button-newcard:hover {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#button-newcard:hover:active {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#button-newcard[disabled] {
+ -moz-image-region: rect(60px 119px 89px 90px) !important;
+}
+
+#button-newlist {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#button-newlist:hover {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#button-newlist:hover:active {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#button-newlist[disabled] {
+ -moz-image-region: rect(90px 119px 119px 90px) !important;
+}
+
+#button-editcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#button-editcard:hover {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#button-editcard:hover:active {
+ -moz-image-region: rect(30px 89px 59px 60px);
+}
+
+#button-editcard[disabled] {
+ -moz-image-region: rect(30px 119px 59px 90px) !important;
+}
+
+#button-newmessage {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(150px 29px 179px 0);
+}
+
+#button-newmessage:hover {
+ -moz-image-region: rect(150px 59px 179px 30px);
+}
+
+#button-newmessage:hover:active {
+ -moz-image-region: rect(150px 89px 179px 60px);
+}
+
+#button-newmessage[disabled] {
+ -moz-image-region: rect(150px 119px 179px 90px) !important;
+}
+
+#button-abdelete {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#button-abdelete:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#button-abdelete:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#button-abdelete[disabled] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #button-newcard,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #button-newcard:hover {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newcard:hover:active {
+ -moz-image-region: rect(40px 59px 59px 40px);
+}
+
+toolbar[iconsize="small"] > #button-newcard[disabled] {
+ -moz-image-region: rect(40px 79px 59px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-newlist,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newlist {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #button-newlist:hover {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newlist:hover:active {
+ -moz-image-region: rect(60px 59px 79px 40px);
+}
+
+toolbar[iconsize="small"] > #button-newlist[disabled] {
+ -moz-image-region: rect(60px 79px 79px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-editcard,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-editcard {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #button-editcard:hover {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #button-editcard:hover:active {
+ -moz-image-region: rect(20px 59px 39px 40px);
+}
+
+toolbar[iconsize="small"] > #button-editcard[disabled] {
+ -moz-image-region: rect(20px 79px 39px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-newmessage,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newmessage {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(100px 19px 119px 0);
+}
+
+toolbar[iconsize="small"] > #button-newmessage:hover {
+ -moz-image-region: rect(100px 39px 119px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newmessage:hover:active {
+ -moz-image-region: rect(100px 59px 119px 40px);
+}
+
+toolbar[iconsize="small"] > #button-newmessage[disabled] {
+ -moz-image-region: rect(100px 79px 119px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-abdelete,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-abdelete {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addressbookicons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #button-abdelete:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #button-abdelete:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #button-abdelete[disabled] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+#blankResultsPaneMessage {
+ font-style: italic;
+}
+
+#localResultsOnlyMessage {
+ font-style: italic;
+ text-align: center;
+}
+
+/* CardView styles - used in the Card View Pane */
+
+#CardViewOuterBox {
+ border-left: 1px solid ThreeDShadow;
+ border-top: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDHighlight;
+}
+
+#CardViewBox {
+ -moz-user-focus: ignore;
+ overflow: auto;
+ min-width: 150px;
+ border-left: 1px solid ThreeDDarkShadow;
+ border-top: 1px solid ThreeDDarkShadow;
+ border-bottom: 1px solid ThreeDLightShadow;
+ border-right: 1px solid ThreeDLightShadow;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+#CardViewInnerBox {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ padding: 0 8px;
+}
+
+#CardTitle {
+ font-size: 150%;
+ font-weight: bold;
+ border-bottom: 2px solid -moz-FieldText;
+ min-width: 120px;
+ margin: 0px;
+}
+
+.cardViewColumn {
+ margin-inline-end: 10px;
+}
+
+.cardViewGroup {
+ margin-top: 8px;
+ padding-bottom: 5px;
+ min-width: 50px;
+}
+
+.CardViewHeading {
+ padding: 1px;
+ padding-inline-start: 5px;
+ margin: 0px 0px 1px;
+ background-color: #CCCCFF;
+ color: black;
+ font-weight: bold;
+ min-width: 30px;
+}
+
+#cvPhoto {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic.png");
+}
+
+#cvBuddyIcon {
+ padding-inline-start: 20px;
+ padding-top: 2px;
+}
+
+.CardViewText,
+.CardViewLink {
+ padding-inline-start: 20px;
+ padding-inline-end: 2px;
+ min-width: 30px;
+ margin: 0px;
+}
+
+.CardViewLink {
+ text-decoration: underline;
+ color: -moz-nativehyperlinktext;
+ cursor: pointer;
+}
+
+
+#cvHomeMapIt, #cvWorkMapIt {
+ margin-bottom: 0px;
+}
+
+html|a {
+ padding-inline-start: 0px;
+ padding-inline-end: 2px;
+ border: none !important;
+}
+
+html|p {
+ border: none !important;
+}
+
+/* ::::: border adjustments ::::: */
+
+#abResultsTree {
+ border-left: 1px solid ThreeDShadow;
+}
+
+/* ::::: lightweight themes ::::: */
+
+#CardViewOuterBox:-moz-lwtheme,
+treecols:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
diff --git a/comm/suite/themes/classic/messenger/addressbook/cardDialog.css b/comm/suite/themes/classic/messenger/addressbook/cardDialog.css
new file mode 100644
index 0000000000..9d4d091024
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/cardDialog.css
@@ -0,0 +1,72 @@
+/* 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/. */
+
+/* ===== cardViewOverlay.css ============================================
+ == Styles for the Address Book Card view.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Card Edit dialog ::::: */
+
+.CardEditWidth {
+ width: 42ch;
+}
+
+.CardEditLabel {
+ text-align: end;
+}
+
+.PhoneEditWidth {
+ width: 24ch;
+}
+
+.AddressCardEditWidth {
+ width: 72ch;
+}
+
+.YearWidth {
+ width: 8ch;
+}
+
+.stateZipSpacer {
+ width: 6ch;
+}
+
+.ZipWidth {
+ width: 14ch;
+}
+
+#photo {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic.png");
+}
+
+#GenericPhotoList[value="default"] {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic-tiny.png");
+}
+
+#PhotoContainer {
+ margin: 5px;
+}
+
+#PhotoDropTarget {
+ margin-top: 5px;
+}
+
+#PhotoDropTarget:hover {
+ border: 1px dashed #CACAFF;
+}
+
+#ProgressContainer {
+ max-height: 0;
+ transition: all .5s ease-out;
+ overflow: hidden;
+}
+
+#ProgressContainer.expanded {
+ margin-top: 10px;
+ max-height: 40px; /* something higher than the actual height, but not too large */
+}
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/abcard.png b/comm/suite/themes/classic/messenger/addressbook/icons/abcard.png
new file mode 100644
index 0000000000..7937bda57e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/abcard.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/ablist.png b/comm/suite/themes/classic/messenger/addressbook/icons/ablist.png
new file mode 100644
index 0000000000..a892125591
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/ablist.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/addrbook.png b/comm/suite/themes/classic/messenger/addressbook/icons/addrbook.png
new file mode 100644
index 0000000000..3eafea43c4
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/addrbook.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons-small.png b/comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons-small.png
new file mode 100644
index 0000000000..9c6f2519e1
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons.png b/comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons.png
new file mode 100644
index 0000000000..782397570e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/addressbookicons.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/contact-generic-tiny.png b/comm/suite/themes/classic/messenger/addressbook/icons/contact-generic-tiny.png
new file mode 100644
index 0000000000..300c09e914
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/contact-generic-tiny.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/contact-generic.png b/comm/suite/themes/classic/messenger/addressbook/icons/contact-generic.png
new file mode 100644
index 0000000000..452f1cf655
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/contact-generic.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook-error.png b/comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook-error.png
new file mode 100644
index 0000000000..ba79dbc4ca
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook-error.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook.png b/comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook.png
new file mode 100644
index 0000000000..ea661f4a26
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/remote-addrbook.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/icons/secure-remote-addrbook.png b/comm/suite/themes/classic/messenger/addressbook/icons/secure-remote-addrbook.png
new file mode 100644
index 0000000000..a7612cf489
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/icons/secure-remote-addrbook.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/addressbook/selectAddressesDialog.css b/comm/suite/themes/classic/messenger/addressbook/selectAddressesDialog.css
new file mode 100644
index 0000000000..062cf13c44
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/selectAddressesDialog.css
@@ -0,0 +1,48 @@
+/* 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/. */
+
+/* ===== selectAddressesDialog.css ======================================
+ == Styles for the Select Addresses Dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Select Addresses dialog ::::: */
+#topBox {
+ padding: 0.5em 0 1.0em;
+}
+
+#abResultsTree {
+ min-width: 10px;
+}
+
+#resultsBox {
+ border: 1px solid #000000;
+}
+
+#addToBucketButtonBox {
+ min-width: 10px;
+ padding: 0.3em;
+}
+
+#addressBucket {
+ min-width: 10px;
+}
+
+#newEditButtonBox {
+ padding-top: 0.5em;
+ padding-inline-end: 0px;
+ margin-inline-start: 0px;
+}
+
+.middle-button-spacer {
+ width: 10px;
+}
+
+.above-remove-spacer {
+ width: 10px;
+ height: 15px;
+}
diff --git a/comm/suite/themes/classic/messenger/addressbook/sidebarPanel.css b/comm/suite/themes/classic/messenger/addressbook/sidebarPanel.css
new file mode 100644
index 0000000000..842fc33d26
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressbook/sidebarPanel.css
@@ -0,0 +1,12 @@
+/* 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/. */
+
+/* ===== sidebarPanel.css ===============================================
+ == Styles for the Address Book sidebar panel.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/addressbook/addressbook.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
diff --git a/comm/suite/themes/classic/messenger/addressingWidget.css b/comm/suite/themes/classic/messenger/addressingWidget.css
new file mode 100644
index 0000000000..f0e58cceba
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/addressingWidget.css
@@ -0,0 +1,41 @@
+/* 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/. */
+
+/* ::::: addressing widget ::::: */
+
+#addressingWidget {
+ -moz-user-focus: none;
+ width: 0;
+}
+
+#typecol-addressingWidget {
+ min-width: 9em;
+ border-right: 1px solid #CACAFF;
+}
+
+.addressingWidgetItem,
+.dummy-row {
+ border: none !important;
+ background-color: inherit !important;
+ color: inherit !important;
+}
+
+.addressingWidgetCell {
+ border-bottom: 1px solid #CACAFF;
+ padding: 0px;
+}
+
+.addressingWidgetCell:first-child {
+ border-top: none;
+}
+
+.dummy-row-cell:first-child {
+ border-top: none;
+ border-bottom: 1px solid #CACAFF;
+}
+
+.person-icon {
+ margin: 0 3px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/abcard.png");
+}
diff --git a/comm/suite/themes/classic/messenger/browserRequest.css b/comm/suite/themes/classic/messenger/browserRequest.css
new file mode 100644
index 0000000000..0924641265
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/browserRequest.css
@@ -0,0 +1,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/. */
+
+#security-button {
+ width: 20px;
+ padding-right: 5px;
+ background-repeat: no-repeat;
+}
+
+#security-button[level="high"] {
+ background-image: url("chrome://messenger/skin/icons/secure.png");
+}
+
+#security-button[level="broken"] {
+ background-image: url("chrome://messenger/skin/icons/insecure.png");
+}
+
+#security-button[loading="true"] {
+ background-image: url("chrome://messenger/skin/icons/loading.png");
+ background-position: 4px 2px;
+}
+
+#header {
+ overflow: hidden;
+ border-bottom: 1px solid black;
+}
+
+#addressbox {
+ font-weight: bold;
+ font-size: normal;
+ -moz-appearance: textfield;
+ overflow: hidden;
+ margin: 0px 5px;
+ font-weight: normal;
+}
+
+#headerMessage {
+ margin-top: 4px;
+}
diff --git a/comm/suite/themes/classic/messenger/dialogs.css b/comm/suite/themes/classic/messenger/dialogs.css
new file mode 100644
index 0000000000..83710494dd
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/dialogs.css
@@ -0,0 +1,14 @@
+/* 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/. */
+
+/* ===== dialogs.css ====================================================
+ == Styles used by the general dialogs in Messenger.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: styles for messenger dialogs ::::: */
+
diff --git a/comm/suite/themes/classic/messenger/filterDialog.css b/comm/suite/themes/classic/messenger/filterDialog.css
new file mode 100644
index 0000000000..87ae896f14
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/filterDialog.css
@@ -0,0 +1,67 @@
+/* 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/. */
+
+/* ===== filterDialog.css ===============================================
+ == Styles for the Mail Filters dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: columns :::::: */
+
+listcell.listcell-iconic > .listcell-label {
+ display: none;
+}
+
+listcell[enabled="false"] {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+listcell[enabled="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/check.png");
+}
+
+.small-button {
+ min-width: 3em;
+ padding: 0px;
+ margin: 0px 1px;
+}
+
+.search-menulist, .search-value-menulist {
+ width: 12em;
+}
+
+.search-menulist[unavailable="true"] {
+ color: GrayText;
+}
+
+textbox {
+ margin: 1px 4px;
+}
+
+#FilterEditor {
+ padding: 0px;
+}
+
+#filterListDialog {
+ padding: 0px;
+}
+
+.filler {
+ padding-inline-end: 22px;
+}
+
+.ruleaction {
+ border: 1px solid transparent;
+}
+
+.ruleactionitem {
+ min-width: 20em;
+}
+
+.ruleaction-type {
+ min-width: 15em;
+}
diff --git a/comm/suite/themes/classic/messenger/folderMenus.css b/comm/suite/themes/classic/messenger/folderMenus.css
new file mode 100644
index 0000000000..c14ec4d25a
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/folderMenus.css
@@ -0,0 +1,124 @@
+/* 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/. */
+
+/* ===== folderMenus.css ================================================
+ == Icons for menus which represent mail folder.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: folder icons for menus ::::: */
+
+.folderMenuItem {
+ list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+}
+
+.folderMenuItem[open="true"] {
+list-style-image: url("chrome://messenger/skin/icons/folder-open.png");
+}
+
+.folderMenuItem[ServerType="nntp"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+.folderMenuItem[ServerType="rss"] {
+ list-style-image: url("chrome://messenger-newsblog/skin/icons/rss-feed.png");
+ -moz-image-region: rect(0 16px 16px 0);
+}
+
+/* ..... special folders ..... */
+
+.folderMenuItem[SpecialFolder="Inbox"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox.png");
+}
+
+.folderMenuItem[SpecialFolder="Inbox"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Sent"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-sent.png");
+}
+
+.folderMenuItem[SpecialFolder="Sent"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-sent-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Outbox"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-outbox.png");
+}
+
+.folderMenuItem[SpecialFolder="Outbox"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-outbox-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Drafts"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-draft.png");
+}
+
+.folderMenuItem[SpecialFolder="Drafts"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-draft-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Templates"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-template.png");
+}
+
+.folderMenuItem[SpecialFolder="Templates"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-template-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Junk"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk.png");
+}
+
+.folderMenuItem[SpecialFolder="Junk"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Trash"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-trash.png");
+}
+
+.folderMenuItem[SpecialFolder="Trash"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-trash-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Virtual"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-search.png");
+}
+
+/* ..... servers ..... */
+
+.folderMenuItem[IsServer="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.folderMenuItem[IsServer="true"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="imap"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="pop3"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="none"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-local.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="nntp"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="nntp"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news-lock.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="rss"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
diff --git a/comm/suite/themes/classic/messenger/folderPane.css b/comm/suite/themes/classic/messenger/folderPane.css
new file mode 100644
index 0000000000..c4f0b7d736
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/folderPane.css
@@ -0,0 +1,251 @@
+/* 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/. */
+
+/* ===== folderPane.css =================================================
+ == Styles for the Folder pane in the Messenger 3-pane window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Trees ::::: */
+
+treechildren::-moz-tree-row {
+ min-height: 18px;
+}
+
+treechildren::-moz-tree-twisty {
+ padding-bottom: 1px;
+}
+
+/* ::::: mail folder ::::: */
+
+treechildren::-moz-tree-image(folderNameCol) {
+ margin-inline-end: 2px;
+}
+
+.icon-holder[type="folder"],
+treechildren::-moz-tree-image(folderNameCol) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+}
+
+/* ..... IMAP shared ..... */
+
+.icon-holder[type="folder"][ImapShared="true"],
+treechildren::-moz-tree-image(folderNameCol, imapShared-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-share.png");
+}
+
+/* ..... mail folder with new messages ..... */
+
+treechildren::-moz-tree-image(folderNameCol, newMessages-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-new.png");
+}
+
+/* ..... News ..... */
+
+.icon-holder[type="folder"][ServerType="nntp"],
+treechildren::-moz-tree-image(folderNameCol, serverType-nntp) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+.icon-holder[type="folder"][ServerType="nntp"][NewMessages="true"],
+treechildren::-moz-tree-image(folderNameCol, serverType-nntp, newMessages-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup-new.png");
+}
+
+/* ..... Feeds ..... */
+
+.icon-holder[type="folder"][ServerType="rss"],
+treechildren::-moz-tree-image(folderNameCol, serverType-rss) {
+ list-style-image: url("chrome://messenger-newsblog/skin/icons/rss-feed.png");
+ -moz-image-region: rect(0 16px 16px 0);
+}
+
+.icon-holder[type="folder"][ServerType="rss"][NewMessages="true"],
+treechildren::-moz-tree-image(folderNameCol, serverType-rss, newMessages-true) {
+ -moz-image-region: rect(16px 16px 32px 0);
+}
+
+/* ..... Inbox ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Inbox"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Inbox) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox.png");
+}
+
+.icon-holder[type="folder"][SpecialFolder="Inbox"][NewMessages="true"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Inbox, newMessages-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox-new.png");
+}
+
+/* ..... Sent ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Sent"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Sent) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-sent.png");
+}
+
+/* ..... Outbox ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Outbox"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Outbox) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-outbox.png");
+}
+
+/* ..... Drafts ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Drafts"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Drafts) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-draft.png");
+}
+
+/* ..... Templates ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Templates"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Templates) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-template.png");
+}
+
+/* ..... Junk ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Junk"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Junk) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk.png");
+}
+
+/* ..... Trash ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Trash"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Trash) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-trash.png");
+}
+
+/* ..... Saved Searches ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Virtual"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Virtual) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-search.png");
+}
+
+treechildren::-moz-tree-cell-text(folderNameCol, newMessages-true) {
+ font-weight: bold;
+}
+
+/* ..... Server Folders ..... */
+
+.icon-holder[type="folder"][IsServer="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.icon-holder[type="folder"][BiffState="NewMail"][IsServer="true"],
+treechildren::-moz-tree-image(folderNameCol, biffState-NewMail, isServer-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail-new.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="pop3"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-pop3, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="imap"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-imap, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.icon-holder[type="folder"][BiffState="NewMail"][IsServer="true"][ServerType="imap"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, biffState-NewMail, isServer-true, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock-new.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="none"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-none) {
+ list-style-image: url("chrome://messenger/skin/icons/local-mailhost.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="nntp"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-nntp) {
+ list-style-image: url("chrome://messenger/skin/icons/server-news.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="nntp"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-nntp, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-news-lock.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="rss"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-rss) {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+/* ::::: All Servers ::::: */
+
+treechildren::-moz-tree-cell-text(closed, subfoldersHaveUnreadMessages-true),
+treechildren::-moz-tree-cell-text(folderNameCol, isServer-true),
+treechildren::-moz-tree-cell-text(hasUnreadMessages-true) {
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(folderNameCol, noSelect-true) {
+ color: gray;
+ font-style: italic;
+}
+
+.tree-folder-checkbox {
+ list-style-image: none;
+}
+
+treechildren::-moz-tree-image(syncCol) {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+treechildren::-moz-tree-image(syncCol, synchronize-true) {
+ list-style-image: url("chrome://messenger/skin/icons/check.png");
+}
+
+treechildren::-moz-tree-image(syncCol, isServer-true) {
+ list-style-image: none;
+}
+
+#folderUnreadCol,
+#folderTotalCol,
+#folderSizeCol {
+ text-align: right;
+}
+
+#folderNameCol [sortDirection="ascending"] {
+ list-style-image: none;
+}
+
+#folderNameCol [sortDirection="descending"] {
+ list-style-image: none;
+}
+
+/* ::::: Folder Summary Popup ::::: */
+
+.folderSummary-message-row {
+ /* This max width ends up dictating the overall width of the popup
+ because it controls how large the preview, subject and sender text can be
+ before cropping kicks in */
+ max-width: 450px;
+}
+
+.folderSummary-subject {
+ font-weight: bold;
+}
+
+.folderSummary-previewText {
+ color: GrayText;
+}
+
+/* Virtual Folder List Dialog */
+
+treechildren::-moz-tree-image(selectedColumn) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+treechildren::-moz-tree-image(selectedColumn, selected-true) {
+ list-style-image: url("chrome://messenger/skin/icons/check.png");
+}
diff --git a/comm/suite/themes/classic/messenger/folderPaneExtras.css b/comm/suite/themes/classic/messenger/folderPaneExtras.css
new file mode 100644
index 0000000000..f315e6e12c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/folderPaneExtras.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* distributors / ISPs can override folder pane icons
+ * see http://www.mozilla.org/mailnews/arch/folderpaneextras.html
+ */
diff --git a/comm/suite/themes/classic/messenger/icons/acct-compose.png b/comm/suite/themes/classic/messenger/icons/acct-compose.png
new file mode 100644
index 0000000000..e6789dfab8
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-compose.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/acct-filters.png b/comm/suite/themes/classic/messenger/icons/acct-filters.png
new file mode 100644
index 0000000000..24815bbd79
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-filters.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/acct-newaccount.png b/comm/suite/themes/classic/messenger/icons/acct-newaccount.png
new file mode 100644
index 0000000000..963a061440
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-newaccount.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/acct-prefs.png b/comm/suite/themes/classic/messenger/icons/acct-prefs.png
new file mode 100644
index 0000000000..3ebb66c8ec
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-prefs.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/acct-read.png b/comm/suite/themes/classic/messenger/icons/acct-read.png
new file mode 100644
index 0000000000..771f9ea98c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-read.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/acct-search.png b/comm/suite/themes/classic/messenger/icons/acct-search.png
new file mode 100644
index 0000000000..20fcb23fd9
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-search.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/acct-subscribe.png b/comm/suite/themes/classic/messenger/icons/acct-subscribe.png
new file mode 100644
index 0000000000..b36aaf00eb
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/acct-subscribe.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/attach.png b/comm/suite/themes/classic/messenger/icons/attach.png
new file mode 100644
index 0000000000..dbdd623cd9
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/attach.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/attachment-col.png b/comm/suite/themes/classic/messenger/icons/attachment-col.png
new file mode 100644
index 0000000000..3830005149
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/attachment-col.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/attachment-selected.png b/comm/suite/themes/classic/messenger/icons/attachment-selected.png
new file mode 100644
index 0000000000..15c0faf3ce
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/attachment-selected.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/attachment.png b/comm/suite/themes/classic/messenger/icons/attachment.png
new file mode 100644
index 0000000000..be96529197
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/attachment.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/check.png b/comm/suite/themes/classic/messenger/icons/check.png
new file mode 100644
index 0000000000..2f00ab3f9b
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/check.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/dot.png b/comm/suite/themes/classic/messenger/icons/dot.png
new file mode 100644
index 0000000000..519b937ae9
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/dot.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/flagcol.png b/comm/suite/themes/classic/messenger/icons/flagcol.png
new file mode 100644
index 0000000000..837eb51f06
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/flagcol.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-closed.png b/comm/suite/themes/classic/messenger/icons/folder-closed.png
new file mode 100644
index 0000000000..2add76b7ce
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-closed.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-draft-open.png b/comm/suite/themes/classic/messenger/icons/folder-draft-open.png
new file mode 100644
index 0000000000..8db1357802
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-draft-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-draft-share-open.png b/comm/suite/themes/classic/messenger/icons/folder-draft-share-open.png
new file mode 100644
index 0000000000..c0fdb86954
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-draft-share-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-draft-share.png b/comm/suite/themes/classic/messenger/icons/folder-draft-share.png
new file mode 100644
index 0000000000..9862b25a19
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-draft-share.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-draft.png b/comm/suite/themes/classic/messenger/icons/folder-draft.png
new file mode 100644
index 0000000000..fdaf8b60bd
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-draft.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-inbox-new.png b/comm/suite/themes/classic/messenger/icons/folder-inbox-new.png
new file mode 100644
index 0000000000..ee76303adb
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-inbox-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-inbox-open.png b/comm/suite/themes/classic/messenger/icons/folder-inbox-open.png
new file mode 100644
index 0000000000..f22eca5273
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-inbox-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-inbox-share-open.png b/comm/suite/themes/classic/messenger/icons/folder-inbox-share-open.png
new file mode 100644
index 0000000000..0d085d39ee
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-inbox-share-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-inbox-share.png b/comm/suite/themes/classic/messenger/icons/folder-inbox-share.png
new file mode 100644
index 0000000000..225d230552
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-inbox-share.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-inbox.png b/comm/suite/themes/classic/messenger/icons/folder-inbox.png
new file mode 100644
index 0000000000..65f85a88dd
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-inbox.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-junk-open.png b/comm/suite/themes/classic/messenger/icons/folder-junk-open.png
new file mode 100644
index 0000000000..9adb3245b9
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-junk-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-junk.png b/comm/suite/themes/classic/messenger/icons/folder-junk.png
new file mode 100644
index 0000000000..da74962834
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-junk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-new-open.png b/comm/suite/themes/classic/messenger/icons/folder-new-open.png
new file mode 100644
index 0000000000..e2ea13cdcf
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-new-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-new.png b/comm/suite/themes/classic/messenger/icons/folder-new.png
new file mode 100644
index 0000000000..5ae36cae47
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-newsgroup-new.png b/comm/suite/themes/classic/messenger/icons/folder-newsgroup-new.png
new file mode 100644
index 0000000000..367431db38
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-newsgroup-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-newsgroup.png b/comm/suite/themes/classic/messenger/icons/folder-newsgroup.png
new file mode 100644
index 0000000000..6e72ede3ee
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-newsgroup.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-open.png b/comm/suite/themes/classic/messenger/icons/folder-open.png
new file mode 100644
index 0000000000..b1da23a5a5
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-outbox-open.png b/comm/suite/themes/classic/messenger/icons/folder-outbox-open.png
new file mode 100644
index 0000000000..c7f5f10f22
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-outbox-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-outbox.png b/comm/suite/themes/classic/messenger/icons/folder-outbox.png
new file mode 100644
index 0000000000..6e2963f028
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-outbox.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-search.png b/comm/suite/themes/classic/messenger/icons/folder-search.png
new file mode 100644
index 0000000000..ff887dc2a9
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-search.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-sent-open.png b/comm/suite/themes/classic/messenger/icons/folder-sent-open.png
new file mode 100644
index 0000000000..398d836048
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-sent-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-sent-share-open.png b/comm/suite/themes/classic/messenger/icons/folder-sent-share-open.png
new file mode 100644
index 0000000000..50b0f1bd87
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-sent-share-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-sent-share.png b/comm/suite/themes/classic/messenger/icons/folder-sent-share.png
new file mode 100644
index 0000000000..123cb06821
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-sent-share.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-sent.png b/comm/suite/themes/classic/messenger/icons/folder-sent.png
new file mode 100644
index 0000000000..d082a0fd4c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-sent.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-share-open.png b/comm/suite/themes/classic/messenger/icons/folder-share-open.png
new file mode 100644
index 0000000000..173b94c2d2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-share-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-share.png b/comm/suite/themes/classic/messenger/icons/folder-share.png
new file mode 100644
index 0000000000..76a646b8da
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-share.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-template-open.png b/comm/suite/themes/classic/messenger/icons/folder-template-open.png
new file mode 100644
index 0000000000..4c7d854ba4
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-template-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-template-share-open.png b/comm/suite/themes/classic/messenger/icons/folder-template-share-open.png
new file mode 100644
index 0000000000..d5a107e64a
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-template-share-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-template-share.png b/comm/suite/themes/classic/messenger/icons/folder-template-share.png
new file mode 100644
index 0000000000..da18662eef
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-template-share.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-template.png b/comm/suite/themes/classic/messenger/icons/folder-template.png
new file mode 100644
index 0000000000..341773ff90
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-template.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-trash-open.png b/comm/suite/themes/classic/messenger/icons/folder-trash-open.png
new file mode 100644
index 0000000000..ca3a814029
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-trash-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-trash-share-open.png b/comm/suite/themes/classic/messenger/icons/folder-trash-share-open.png
new file mode 100644
index 0000000000..dbf554e13c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-trash-share-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-trash-share.png b/comm/suite/themes/classic/messenger/icons/folder-trash-share.png
new file mode 100644
index 0000000000..cc9f9e1390
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-trash-share.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/folder-trash.png b/comm/suite/themes/classic/messenger/icons/folder-trash.png
new file mode 100644
index 0000000000..167ecbb08c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/folder-trash.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/info.png b/comm/suite/themes/classic/messenger/icons/info.png
new file mode 100644
index 0000000000..3ce14ddc1a
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/info.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/insecure.png b/comm/suite/themes/classic/messenger/icons/insecure.png
new file mode 100644
index 0000000000..efda0de255
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/insecure.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/junk.png b/comm/suite/themes/classic/messenger/icons/junk.png
new file mode 100644
index 0000000000..0ae15cd390
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/junk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/junkBar.png b/comm/suite/themes/classic/messenger/icons/junkBar.png
new file mode 100644
index 0000000000..e57cedf0b1
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/junkBar.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/junkcol.png b/comm/suite/themes/classic/messenger/icons/junkcol.png
new file mode 100644
index 0000000000..cac03787a8
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/junkcol.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/loading.png b/comm/suite/themes/classic/messenger/icons/loading.png
new file mode 100644
index 0000000000..201d014c1f
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/loading.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/local-mailhost.png b/comm/suite/themes/classic/messenger/icons/local-mailhost.png
new file mode 100644
index 0000000000..c00da149cb
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/local-mailhost.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-del.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-del.png
new file mode 100644
index 0000000000..00c82a34c2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-del.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl-reply.png
new file mode 100644
index 0000000000..8e5d289d2e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl.png
new file mode 100644
index 0000000000..a3cd9604c6
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-reply.png
new file mode 100644
index 0000000000..c1d6e8f874
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd.png
new file mode 100644
index 0000000000..16b52dc18a
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-fwd.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-offl-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-offl-reply.png
new file mode 100644
index 0000000000..8b855e20b2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-offl.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-offl.png
new file mode 100644
index 0000000000..74aeadaced
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach-reply.png
new file mode 100644
index 0000000000..daa3d091f7
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-attach.png b/comm/suite/themes/classic/messenger/icons/message-mail-attach.png
new file mode 100644
index 0000000000..282da48f9d
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-attach.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-delete-offl.png b/comm/suite/themes/classic/messenger/icons/message-mail-delete-offl.png
new file mode 100644
index 0000000000..3c9b999148
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-delete-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl-reply.png
new file mode 100644
index 0000000000..2f21f3d151
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl.png b/comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl.png
new file mode 100644
index 0000000000..44b4f81987
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-fwd-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-fwd-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-fwd-reply.png
new file mode 100644
index 0000000000..b1d8963076
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-fwd-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-fwd.png b/comm/suite/themes/classic/messenger/icons/message-mail-fwd.png
new file mode 100644
index 0000000000..9cbeabb42e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-fwd.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-imapdelete.png b/comm/suite/themes/classic/messenger/icons/message-mail-imapdelete.png
new file mode 100644
index 0000000000..78307c838a
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-imapdelete.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-new-offl.png b/comm/suite/themes/classic/messenger/icons/message-mail-new-offl.png
new file mode 100644
index 0000000000..822a8a262d
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-new-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-new.png b/comm/suite/themes/classic/messenger/icons/message-mail-new.png
new file mode 100644
index 0000000000..9407265785
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-offl-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-offl-reply.png
new file mode 100644
index 0000000000..a0ebeb9ea8
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-offl.png b/comm/suite/themes/classic/messenger/icons/message-mail-offl.png
new file mode 100644
index 0000000000..bf62b12ce2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail-reply.png b/comm/suite/themes/classic/messenger/icons/message-mail-reply.png
new file mode 100644
index 0000000000..44c5f29dee
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail-reply.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-mail.png b/comm/suite/themes/classic/messenger/icons/message-mail.png
new file mode 100644
index 0000000000..e74d08a096
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-mail.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-attach-kill-offl.png b/comm/suite/themes/classic/messenger/icons/message-news-attach-kill-offl.png
new file mode 100644
index 0000000000..8e36e243e5
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-attach-kill-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-attach-kill.png b/comm/suite/themes/classic/messenger/icons/message-news-attach-kill.png
new file mode 100644
index 0000000000..110e943507
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-attach-kill.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-attach-offl.png b/comm/suite/themes/classic/messenger/icons/message-news-attach-offl.png
new file mode 100644
index 0000000000..d387f75cec
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-attach-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-attach.png b/comm/suite/themes/classic/messenger/icons/message-news-attach.png
new file mode 100644
index 0000000000..f5d9d34327
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-attach.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-kill-offl.png b/comm/suite/themes/classic/messenger/icons/message-news-kill-offl.png
new file mode 100644
index 0000000000..0caac52c20
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-kill-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-kill.png b/comm/suite/themes/classic/messenger/icons/message-news-kill.png
new file mode 100644
index 0000000000..509b52ce86
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-kill.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-new-attach-off.png b/comm/suite/themes/classic/messenger/icons/message-news-new-attach-off.png
new file mode 100644
index 0000000000..9c8a63c70d
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-new-attach-off.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-new-attach.png b/comm/suite/themes/classic/messenger/icons/message-news-new-attach.png
new file mode 100644
index 0000000000..80d814cbbd
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-new-attach.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-new-offl.png b/comm/suite/themes/classic/messenger/icons/message-news-new-offl.png
new file mode 100644
index 0000000000..0112ed9ce6
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-new-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-new.png b/comm/suite/themes/classic/messenger/icons/message-news-new.png
new file mode 100644
index 0000000000..af2b900a9b
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news-offl.png b/comm/suite/themes/classic/messenger/icons/message-news-offl.png
new file mode 100644
index 0000000000..49021f9d1b
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news-offl.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/message-news.png b/comm/suite/themes/classic/messenger/icons/message-news.png
new file mode 100644
index 0000000000..b612266e75
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/message-news.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/messengericons-small.png b/comm/suite/themes/classic/messenger/icons/messengericons-small.png
new file mode 100644
index 0000000000..21fc19fed6
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/messengericons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/messengericons.png b/comm/suite/themes/classic/messenger/icons/messengericons.png
new file mode 100644
index 0000000000..bbb957baab
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/messengericons.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/new-mail-alert.png b/comm/suite/themes/classic/messenger/icons/new-mail-alert.png
new file mode 100644
index 0000000000..7d3364336c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/new-mail-alert.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/phishing.png b/comm/suite/themes/classic/messenger/icons/phishing.png
new file mode 100644
index 0000000000..d99a9ea6d4
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/phishing.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/readcol.png b/comm/suite/themes/classic/messenger/icons/readcol.png
new file mode 100644
index 0000000000..d8dd852326
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/readcol.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/remote-blocked.png b/comm/suite/themes/classic/messenger/icons/remote-blocked.png
new file mode 100755
index 0000000000..552c93f7a2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/remote-blocked.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/secure.png b/comm/suite/themes/classic/messenger/icons/secure.png
new file mode 100644
index 0000000000..bcca43a917
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/secure.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-local-new.png b/comm/suite/themes/classic/messenger/icons/server-local-new.png
new file mode 100644
index 0000000000..51e5fd3078
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-local-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-local.png b/comm/suite/themes/classic/messenger/icons/server-local.png
new file mode 100644
index 0000000000..fd8d90dc4b
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-local.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-mail-new.png b/comm/suite/themes/classic/messenger/icons/server-mail-new.png
new file mode 100644
index 0000000000..caf9b7896b
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-mail-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-mail.png b/comm/suite/themes/classic/messenger/icons/server-mail.png
new file mode 100644
index 0000000000..13e3558d1f
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-mail.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-news-lock.png b/comm/suite/themes/classic/messenger/icons/server-news-lock.png
new file mode 100644
index 0000000000..3822ad4144
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-news-lock.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-news-new.png b/comm/suite/themes/classic/messenger/icons/server-news-new.png
new file mode 100644
index 0000000000..000d538624
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-news-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-news.png b/comm/suite/themes/classic/messenger/icons/server-news.png
new file mode 100644
index 0000000000..ee0a6ddae9
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-news.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-remote-lock-new.png b/comm/suite/themes/classic/messenger/icons/server-remote-lock-new.png
new file mode 100644
index 0000000000..0e53edfbf6
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-remote-lock-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/server-remote-lock.png b/comm/suite/themes/classic/messenger/icons/server-remote-lock.png
new file mode 100644
index 0000000000..c7f33db676
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/server-remote-lock.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-closed-eye.png b/comm/suite/themes/classic/messenger/icons/thread-closed-eye.png
new file mode 100644
index 0000000000..dbe9dd7efc
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-closed-eye.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-closed-kill.png b/comm/suite/themes/classic/messenger/icons/thread-closed-kill.png
new file mode 100644
index 0000000000..57c530751e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-closed-kill.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-closed-offl-eye.png b/comm/suite/themes/classic/messenger/icons/thread-closed-offl-eye.png
new file mode 100644
index 0000000000..51a3774223
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-closed-offl-eye.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-closed-offl-kill.png b/comm/suite/themes/classic/messenger/icons/thread-closed-offl-kill.png
new file mode 100644
index 0000000000..8ab8acaef1
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-closed-offl-kill.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-closed.png b/comm/suite/themes/classic/messenger/icons/thread-closed.png
new file mode 100644
index 0000000000..bd4cdc117c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-closed.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-new-closed-eye.png b/comm/suite/themes/classic/messenger/icons/thread-new-closed-eye.png
new file mode 100644
index 0000000000..75630b9545
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-new-closed-eye.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-new-closed-kill.png b/comm/suite/themes/classic/messenger/icons/thread-new-closed-kill.png
new file mode 100644
index 0000000000..0e2ae7047a
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-new-closed-kill.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-eye.png b/comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-eye.png
new file mode 100644
index 0000000000..980b55ef67
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-eye.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-kill.png b/comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-kill.png
new file mode 100644
index 0000000000..c74c2d2ea7
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-new-closed-offl-kill.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/thread-new-closed.png b/comm/suite/themes/classic/messenger/icons/thread-new-closed.png
new file mode 100644
index 0000000000..cf992f5aa1
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/thread-new-closed.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/threadcol-threaded.png b/comm/suite/themes/classic/messenger/icons/threadcol-threaded.png
new file mode 100644
index 0000000000..5d3ce7898f
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/threadcol-threaded.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/threadcol-unthreaded.png b/comm/suite/themes/classic/messenger/icons/threadcol-unthreaded.png
new file mode 100644
index 0000000000..3d1cac5edd
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/threadcol-unthreaded.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/twisty-clsd.png b/comm/suite/themes/classic/messenger/icons/twisty-clsd.png
new file mode 100644
index 0000000000..7d178c3df8
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/twisty-clsd.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/icons/twisty-open.png b/comm/suite/themes/classic/messenger/icons/twisty-open.png
new file mode 100644
index 0000000000..3cfe9ae414
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/icons/twisty-open.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/mailWindow1.css b/comm/suite/themes/classic/messenger/mailWindow1.css
new file mode 100644
index 0000000000..99b97a8053
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/mailWindow1.css
@@ -0,0 +1,156 @@
+/* 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/. */
+
+/* ===== mailWindow1.css ================================================
+ == Styles for the main Mail window in the default layout scheme.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+@import url("chrome://messenger/skin/primaryToolbar.css");
+@import url("chrome://messenger/skin/folderMenus.css");
+@import url("chrome://messenger/skin/folderPane.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* :::::
+ :: Make sure the min height is small so we can
+ :: resize the pane vertically -EDV
+ ::::: */
+
+#messagepanebox {
+ min-height: 10px;
+ height: 0px;
+}
+
+/* ..... folder pane adjustments ..... */
+
+.folderview-cycler > .toolbarbutton-text {
+ display: none;
+}
+
+.folderview-cycler > .toolbarbutton-icon {
+ margin: 0px;
+}
+
+.folderview-cycler[dir="prev"],
+.folderview-cycler[dir="next"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.gif");
+}
+
+.folderview-cycler[dir="next"],
+.folderview-cycler[dir="prev"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.gif");
+}
+
+/* ::::: border adjustments for focus ring and joined splitters ::::: */
+
+/* ..... splitter adjustments ..... */
+
+#folderpane-splitter {
+ border-right: none;
+ border-bottom: none;
+ border-left: none;
+ min-width: 5px;
+}
+
+#threadpane-splitter {
+ border: none;
+ min-height: 5px;
+}
+
+#threadpane-splitter[state="collapsed"] {
+ border-bottom: 2px solid;
+}
+
+/* ..... tree adjustments ..... */
+
+#folderTree {
+ border-right: 1px solid;
+ border-right-color: ThreeDShadow;
+ min-width: 1px;
+}
+
+#threadTree {
+ border-bottom: 1px solid;
+ border-bottom-color: ThreeDLightShadow;
+ min-width: 1px;
+}
+
+#threadTree,
+#accountCentralBox {
+ border-left: 1px solid;
+ border-left-color: ThreeDDarkShadow;
+}
+
+#locationIcon {
+ list-style-image: none;
+}
+
+/* ::::: tree focusring ::::: */
+
+.focusring:focus > .tree-stack > .tree-rows > .tree-bodybox {
+ border: 1px dotted ThreeDDarkShadow;
+}
+
+/* ..... message pane adjustments ..... */
+
+#messagepanebox {
+ border-top: 1px solid;
+ border-left: 1px solid;
+ border-top-color: ThreeDDarkShadow;
+ border-left-color: ThreeDDarkShadow;
+}
+
+#msgHeaderView {
+ border-left: none !important;
+}
+
+#messagepane {
+ border: 1px solid -moz-Field;
+ border-right: none;
+}
+
+#messagepanebox[focusring="true"] > #messagepanewrapper > #messagepane {
+ border-color: #000000;
+}
+
+/* ..... tabmail ..... */
+
+tabpanels {
+ /* don't draw tabpanel borders; see also tabbrowser.css */
+ -moz-appearance: none;
+}
+
+.tab-close-button {
+ margin: 0;
+}
+
+.tabmail-tab[type="message"] .tab-icon {
+ margin-top: -2px;
+}
+
+.tabmail-tab[type="folder"][NewMessages="true"],
+.tabmail-tab[type="folder"][IsServer="true"],
+.alltabs-item[selected="true"] {
+ font-weight: bold;
+}
+
+.tabmail-tab:-moz-lwtheme:not([selected="true"]) {
+ opacity: .8;
+}
+
+.tabmail-tab:-moz-lwtheme {
+ text-shadow: none;
+}
+
+.alltabs-item[tabIsScrolled] {
+ font-style: italic;
+}
+
+#tabbar-toolbar {
+ -moz-appearance: none;
+ border-top-style: none;
+ border-bottom-style: none;
+ min-height: 0;
+}
diff --git a/comm/suite/themes/classic/messenger/messageBody.css b/comm/suite/themes/classic/messenger/messageBody.css
new file mode 100644
index 0000000000..1e3f73d836
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messageBody.css
@@ -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/. */
+
+/* ===== messageBody.css =================================================
+ == Styles for the body of a mail message.
+ ======================================================================= */
+
+@import url(chrome://communicator/skin/smileys.css);
+@import url(chrome://messenger/skin/messageQuotes.css);
+
+@namespace url("http://www.w3.org/1999/xhtml");
+
+/* :::: message header ::::: */
+
+.header-part1 {
+ background-color: #EFEFEF;
+}
+
+.header-part2,
+.header-part3 {
+ background-color: #DEDEDE;
+}
+
+.headerdisplayname {
+ display: inline;
+ font-weight: bold;
+ white-space: pre;
+}
+
+/* ::::: message text, incl. quotes ::::: */
+
+.moz-text-flowed blockquote,
+.moz-text-plain blockquote {
+ margin: 0;
+}
+
+.moz-text-plain pre {
+ margin: 0;
+ font-family: inherit;
+}
+
+.moz-text-plain[wrap="true"] {
+ white-space: pre-wrap;
+}
+
+.moz-text-plain[wrap="false"] {
+ white-space: pre;
+}
+
+.moz-text-plain[wrap="flow"] .moz-txt-sig {
+ white-space: pre-wrap;
+}
+
+.moz-text-plain[graphical-quote="false"] blockquote {
+ border-style: none;
+ padding: 0;
+}
+
+.moz-text-plain[graphical-quote="true"] .moz-txt-citetags {
+ display: none;
+}
+
+.moz-txt-underscore {
+ text-decoration: underline;
+}
+
+.moz-txt-formfeed {
+ display: block;
+ height: 100%;
+}
+
+/* ::::: signature ::::: */
+
+@media not print {
+ .moz-txt-sig,
+ .moz-signature {
+ opacity: 0.5;
+ }
+
+ .moz-txt-sig .moz-txt-sig,
+ .moz-signature .moz-signature {
+ opacity: 1.0;
+ }
+}
+
+/* ::::: vcard ::::: */
+
+.moz-vcard-table {
+ border-radius: 8px;
+ border: thin solid gray;
+ margin-top: 10px;
+}
+
+.moz-vcard-property {
+ font-size: 80%;
+ color: gray;
+}
+
+.moz-vcard-title-property {
+}
+
+.moz-vcard-badge {
+ height: 30px;
+ width: 30px;
+ display: block;
+ background-image: url("chrome://messenger/skin/icons/messengericons.png");
+ background-position: 0px -270px;
+}
+
+.moz-vcard-badge:hover {
+ background-position: -30px -270px;
+ outline: 1px dotted;
+}
+
+.moz-vcard-badge:hover:active {
+ background-position: -60px -270px;
+}
+
+/* ::::: attached images ::::: */
+.moz-attached-image-container {
+ text-align: center;
+}
+
+.moz-attached-image {
+ image-orientation: from-image;
+}
+
+.moz-attached-image[overflowing="true"] {
+ cursor: zoom-out;
+}
+
+.moz-attached-image[isshrunk="true"] {
+ cursor: zoom-in;
+ max-width: 100%;
+}
+
+/* Old style feeds. */
+#_mailrssiframe {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+}
+
+/* Attachment display styling (for inline attachments and printing) */
+.mimeAttachmentHeader {
+ border-style: none;
+ border-top: 1px solid gray;
+}
+
+.mimeAttachmentHeaderName {
+ color: gray;
+ font-size: 80%;
+}
+
+.mimeAttachmentWrap {
+ padding: 0 1em;
+}
+
+.mimeAttachmentTable {
+ width: 100%;
+ border-collapse: collapse;
+ table-layout: fixed;
+}
+
+.mimeAttachmentTable tr + tr > td {
+ border-top: 1px solid gray;
+}
+
+.mimeAttachmentFile {
+ word-wrap: break-word;
+}
+
+.mimeAttachmentSize {
+ vertical-align: top;
+ width: 10ch;
+ text-align: right;
+}
+
+.mimeAttachmentFile,
+.mimeAttachmentSize {
+ padding: 0.25em 0;
+}
diff --git a/comm/suite/themes/classic/messenger/messageHeader.css b/comm/suite/themes/classic/messenger/messageHeader.css
new file mode 100644
index 0000000000..1d8bcd854d
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messageHeader.css
@@ -0,0 +1,185 @@
+/* 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/. */
+
+/* ===== messageHeader.css ==============================================
+ == Styles for the header toolbars of a mail message.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: msg header toolbars ::::: */
+
+#collapsedHeaderView,
+#expandedHeaderView {
+ min-width: 1px;
+}
+
+/* ::::: msg header buttons ::::: */
+
+#expandedAttachmentBox {
+ width: 20em;
+ height: 0;
+ list-style-image: url("chrome://messenger/skin/icons/attach.png");
+}
+
+#attachmentText {
+ font-weight: bold;
+}
+
+/* ::::: msg header captions ::::: */
+
+#msgHeaderView {
+ border-right: 1px solid ThreeDShadow;
+ border-left: 1px solid ThreeDShadow;
+}
+
+#collapsedHeaderView,
+#expandedHeaderView {
+ border-bottom: 1px solid ThreeDDarkShadow;
+ min-width: 1px;
+}
+
+.headerNameBox {
+ width: 7.7em;
+}
+
+.headerName {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ margin-inline-start: 0px;
+ margin-inline-end: .5em;
+ font-weight: bold;
+ text-align: right;
+}
+
+.headerValue {
+ margin: 0;
+ min-width: 50px;
+ white-space: normal;
+}
+
+.headerValueBox {
+ margin: 0 0 5px;
+}
+
+.headerValueBox[singleline="true"] {
+ overflow: hidden;
+}
+
+.subjectvalue {
+ font-weight: bold;
+}
+
+.tagvalue {
+ margin-top: 0;
+ margin-inline-start: 0;
+}
+
+/* ::::: msg header message ids ::::: */
+
+.messageIdDisplayButton {
+ cursor: pointer;
+ color: #0000FF;
+ text-decoration: underline;
+ margin: 0;
+}
+
+.messageIdDisplayButton:hover {
+ color: #FF0000;
+}
+
+.messageIdDisplayImage {
+ padding-inline-start: 2px;
+ -moz-box-pack: end;
+}
+
+/* ::::: msg header email addresses ::::: */
+
+.emailDisplayButton {
+ cursor: pointer;
+ color: #0000FF;
+ text-decoration: underline;
+ margin: 0;
+}
+
+.emailDisplayButton:hover {
+ color: #FF0000;
+}
+
+.emailDisplayImage {
+ padding-inline-start: 2px;
+ -moz-box-pack: end;
+}
+
+mail-emailaddress:-moz-focusring {
+ outline: 1px dotted;
+ outline-offset: -1px;
+}
+
+/* ::::: email address twisty ::::: */
+
+.addresstwisty {
+ margin: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/twisty-clsd.png");
+}
+
+.addresstwisty[open] {
+ list-style-image: url("chrome://messenger/skin/icons/twisty-open.png");
+}
+
+/* ::::: view expand and collapse twisties ::::: */
+
+.expandHeaderViewButton,
+.collapsedHeaderViewButton {
+ margin: 2px .5em 0;
+}
+
+.expandHeaderViewButton {
+ list-style-image: url("chrome://messenger/skin/icons/twisty-open.png");
+}
+
+.collapsedHeaderViewButton {
+ list-style-image: url("chrome://messenger/skin/icons/twisty-clsd.png");
+}
+
+/* ::::: collapsed view styles ::::: */
+
+#collapsedAttachmentBox {
+ -moz-box-pack: center;
+}
+
+#collapseddateValue {
+ margin: 0 .5em;
+ text-align: right;
+}
+
+#collapseddateValue > .textbox-input-box menupopup {
+ text-align: left;
+}
+
+#collapsedfromBox {
+ width: 18em;
+}
+
+#collapseddateBox {
+ width: 12em;
+}
+
+.collapsedHeaderDisplayName {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: .55em;
+ margin-inline-end: .5em;
+ min-height: 16px;
+ font-weight: bold;
+}
+
+.collapsedHeaderValue {
+ margin: 0;
+}
+
+.collapsedAttachmentButton {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach.png");
+ margin-inline-end: .5em;
+}
diff --git a/comm/suite/themes/classic/messenger/messageKeywords.css b/comm/suite/themes/classic/messenger/messageKeywords.css
new file mode 100644
index 0000000000..751eb1eb83
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messageKeywords.css
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* distributors / ISPs can use this
+ * to add or override icons and colors to the message pane.
+ * see http://www.mozilla.org/mailnews/arch/messagekeywords.html
+ */
diff --git a/comm/suite/themes/classic/messenger/messageQuotes.css b/comm/suite/themes/classic/messenger/messageQuotes.css
new file mode 100644
index 0000000000..2cbf8f5ba7
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messageQuotes.css
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/* Because this sheet is loaded synchronously while the user is waiting for the
+ compose window to appear, it must not @import a ton of other things, and
+ especially must not trigger network access. */
+
+/* ===== messageQuotes.css =================================================
+ == Shared styles such as block quote colors and signature style
+ == between the message body during
+ == message display and the mail editor instance for mail compose.
+ ======================================================================= */
+
+/* workaround for MS Outlook mails where the line-height is set to 0 */
+body {
+ line-height: initial !important;
+}
+
+/* ::::: signature ::::: */
+
+@media not print {
+ div.moz-text-flowed > div.moz-txt-sig,
+ div.moz-text-plain > pre > div.moz-txt-sig,
+ pre.moz-signature {
+ opacity: 0.6;
+ }
+}
+
+/* ::::: Turn on borders and padding for quotes. ::::: */
+blockquote[type=cite] {
+ padding: 0.4ex 1ex;
+ margin: 1ex;
+ border-width: 0px 0px 0px 2px;
+ border-style: none none none solid;
+ border-radius: 2px;
+}
+
+/* ::::: Colorize block quote borders. We only go 5 levels deep. ::::: */
+blockquote[type=cite] {
+ border-color: rgb(114,159,207); /* Sky Blue 1 */
+}
+
+blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(173,127,168); /* Plum 1 */
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(138,226,52); /* Chameleon 1 */
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(252,175,62); /* Orange 1 */
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(233,185,110); /* Chocolate 1 */
+}
+
diff --git a/comm/suite/themes/classic/messenger/messageWindow.css b/comm/suite/themes/classic/messenger/messageWindow.css
new file mode 100644
index 0000000000..cddd5b4e2e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messageWindow.css
@@ -0,0 +1,19 @@
+/* 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/. */
+
+/* ===== messageWindow.css ==============================================
+ == Styles for the message window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+@import url("chrome://messenger/skin/primaryToolbar.css");
+@import url("chrome://messenger/skin/threadPaneLabels.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: message header borders ::::: */
+
+#msgHeaderView {
+ border: 1px outset ThreeDFace !important;
+}
diff --git a/comm/suite/themes/classic/messenger/messenger.css b/comm/suite/themes/classic/messenger/messenger.css
new file mode 100644
index 0000000000..51b99bb76e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messenger.css
@@ -0,0 +1,20 @@
+/* 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/. */
+
+/* ===== messenger.css ==================================================
+ == Styles shared throughout the Messenger application.
+ ======================================================================= */
+
+@import url("chrome://communicator/skin/");
+@import url("chrome://messenger/content/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: lightweight themes ::::: */
+
+#msgHeaderView:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
diff --git a/comm/suite/themes/classic/messenger/messengercompose/messengercompose.css b/comm/suite/themes/classic/messenger/messengercompose/messengercompose.css
new file mode 100644
index 0000000000..762472e2b2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/messengercompose/messengercompose.css
@@ -0,0 +1,333 @@
+/* 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/. */
+
+/* ===== messengercompose.css ===========================================
+ == Styles for the main Messenger Compose window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-send {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(330px 29px 359px 0);
+}
+
+#button-send:hover {
+ -moz-image-region: rect(330px 59px 359px 30px);
+}
+
+#button-send:hover:active {
+ -moz-image-region: rect(330px 89px 359px 60px);
+}
+
+#button-send[disabled="true"] {
+ -moz-image-region: rect(330px 119px 359px 90px) !important;
+}
+
+#button-address {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(270px 29px 299px 0);
+}
+
+#button-address:hover {
+ -moz-image-region: rect(270px 59px 299px 30px);
+}
+
+#button-address:hover:active {
+ -moz-image-region: rect(270px 89px 299px 60px);
+}
+
+#button-address[disabled="true"] {
+ -moz-image-region: rect(270px 119px 299px 90px) !important;
+}
+
+#button-attach {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(300px 29px 329px 0);
+}
+
+#button-attach:hover {
+ -moz-image-region: rect(300px 59px 329px 30px);
+}
+
+#button-attach:hover:active {
+ -moz-image-region: rect(300px 89px 329px 60px);
+}
+
+#button-attach[disabled="true"] {
+ -moz-image-region: rect(300px 119px 329px 90px) !important;
+}
+
+#spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(240px 29px 269px 0);
+}
+
+#spellingButton:hover {
+ -moz-image-region: rect(240px 59px 269px 30px);
+}
+
+#spellingButton:hover:active {
+ -moz-image-region: rect(240px 89px 269px 60px);
+}
+
+#spellingButton[disabled="true"] {
+ -moz-image-region: rect(240px 119px 269px 90px) !important;
+}
+
+#button-save {
+ list-style-image: url("chrome://editor/skin/icons/editoricons.png");
+ -moz-image-region: rect(210px 29px 239px 0);
+}
+
+#button-save:hover {
+ -moz-image-region: rect(210px 59px 239px 30px);
+}
+
+#button-save:hover:active {
+ -moz-image-region: rect(210px 89px 239px 60px);
+}
+
+#button-save[disabled="true"] {
+ -moz-image-region: rect(210px 119px 239px 90px) !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #button-send,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-send {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(220px 19px 239px 0);
+}
+
+toolbar[iconsize="small"] > #button-send:hover {
+ -moz-image-region: rect(220px 39px 239px 20px);
+}
+
+toolbar[iconsize="small"] > #button-send:hover:active {
+ -moz-image-region: rect(220px 59px 239px 40px);
+}
+
+toolbar[iconsize="small"] > #button-send[disabled="true"] {
+ -moz-image-region: rect(220px 79px 239px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-address,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-address {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(180px 19px 199px 0);
+}
+
+toolbar[iconsize="small"] > #button-address:hover {
+ -moz-image-region: rect(180px 39px 199px 20px);
+}
+
+toolbar[iconsize="small"] > #button-address:hover:active {
+ -moz-image-region: rect(180px 59px 199px 40px);
+}
+
+toolbar[iconsize="small"] > #button-address[disabled="true"] {
+ -moz-image-region: rect(180px 79px 199px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-attach,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-attach {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(200px 19px 219px 0);
+}
+
+toolbar[iconsize="small"] > #button-attach:hover {
+ -moz-image-region: rect(200px 39px 219px 20px);
+}
+
+toolbar[iconsize="small"] > #button-attach:hover:active {
+ -moz-image-region: rect(200px 59px 219px 40px);
+}
+
+toolbar[iconsize="small"] > #button-attach[disabled="true"] {
+ -moz-image-region: rect(200px 79px 219px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #spellingButton,
+toolbar[iconsize="small"] > toolbarpaletteitem > #spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(160px 19px 179px 0);
+}
+
+toolbar[iconsize="small"] > #spellingButton:hover {
+ -moz-image-region: rect(160px 39px 179px 20px);
+}
+
+toolbar[iconsize="small"] > #spellingButton:hover:active {
+ -moz-image-region: rect(160px 59px 179px 40px);
+}
+
+toolbar[iconsize="small"] > #spellingButton[disabled="true"] {
+ -moz-image-region: rect(160px 79px 179px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-save,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-save {
+ list-style-image: url("chrome://editor/skin/icons/editoricons-small.png");
+ -moz-image-region: rect(140px 19px 159px 0);
+}
+
+toolbar[iconsize="small"] > #button-save:hover {
+ -moz-image-region: rect(140px 39px 159px 20px);
+}
+
+toolbar[iconsize="small"] > #button-save:hover:active {
+ -moz-image-region: rect(140px 59px 159px 40px);
+}
+
+toolbar[iconsize="small"] > #button-save[disabled="true"] {
+ -moz-image-region: rect(140px 79px 159px 60px) !important;
+}
+
+/* ::::: special toolbar colors ::::: */
+
+#content-frame {
+ border-left: 1px solid ThreeDDarkShadow;
+ border-right: 1px solid ThreeDLightShadow;
+}
+
+#appcontent {
+ border-left: 1px solid ThreeDShadow;
+ border-right: 1px solid ThreeDHighlight;
+}
+
+#attachmentbucket-sizer {
+ border-top: none;
+ border-bottom: none;
+}
+
+#compose-toolbar-sizer {
+ border-top-width: 1px;
+ border-left: none;
+ border-right: none;
+ border-top-color: ThreeDHighlight;
+}
+
+#msgSubject {
+ margin-top: 0px;
+ margin-bottom: 4px;
+}
+
+#MsgHeadersToolbar {
+ min-height: 0px; /* this undoes the min-height in toolbar.css */
+}
+
+/* ::::: autocomplete icons ::::: */
+
+treechildren::-moz-tree-cell-text(default-match) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 15px;
+ margin-inline-end: -3px;
+ border: none;
+}
+
+treechildren::-moz-tree-image(local-abook) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 4px;
+ margin-inline-end: -1px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/addrbook.png");
+}
+
+treechildren::-moz-tree-image(remote-abook) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 2px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/remote-addrbook.png");
+}
+
+treechildren::-moz-tree-image(remote-err) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 2px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/remote-addrbook-error.png");
+}
+
+treechildren::-moz-tree-image(subscribed-news) {
+ margin-inline-start: 2px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+/* ::::: compact menulists ::::: */
+
+.menulist-compact {
+ -moz-binding: url("chrome://messenger/content/messengercompose/menulistCompactBindings.xml#menulist-compact");
+ -moz-appearance: none;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin: 0;
+ border: 1px solid;
+ border-top-color: ThreeDLightShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+ border-left-color: ThreeDLightShadow;
+ background-color: ThreeDFace;
+ color: ButtonText;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.menulist-compact[open="true"] {
+ border-top-color: ThreeDDarkShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-bottom-color: ThreeDDarkShadow;
+ border-left-color: ThreeDDarkShadow;
+}
+
+.menulist-compact[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+}
+
+.menulist-compact > .menulist-label-box > .menulist-label {
+ margin: 0 3px !important;
+ text-align: end;
+}
+
+.menulist-compact > .menulist-label-box > .menulist-icon {
+ margin-inline-start: 2px;
+}
+
+.menulist-compact > .menulist-label-box,
+.menulist-compact[open="true"]:focus > .menulist-label-box {
+ -moz-appearance: none;
+ border: 1px solid transparent;
+ background: transparent;
+ color: inherit;
+}
+
+.menulist-compact:-moz-focusring > .menulist-label-box {
+ border: 1px dotted;
+}
+
+#FontFaceSelect {
+ max-width: 35ch;
+}
+
+/* ::::: lightweight themes ::::: */
+
+#MsgHeadersToolbar:-moz-lwtheme,
+#FormatToolbar:-moz-lwtheme,
+#compose-toolbar-sizer:-moz-lwtheme {
+ text-shadow: none;
+ color: -moz-dialogtext;
+ background-color: -moz-dialog;
+}
+
+#MsgHeadersToolbar textbox:-moz-lwtheme,
+#MsgHeadersToolbar menulist:-moz-lwtheme,
+.formatting-button > menulist:-moz-lwtheme {
+ opacity: 1;
+}
diff --git a/comm/suite/themes/classic/messenger/msgSelectOffline.css b/comm/suite/themes/classic/messenger/msgSelectOffline.css
new file mode 100644
index 0000000000..bc24e7b606
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/msgSelectOffline.css
@@ -0,0 +1,32 @@
+/* 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/. */
+
+/* ===== msgSelectOffline.css =================================================
+ == The dialog where you select which folders to enable for offline use.
+ ========================================================================== */
+
+@import url("chrome://messenger/skin/");
+@import url("chrome://messenger/skin/folderPane.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+treechildren::-moz-tree-image(syncCol) {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+treechildren::-moz-tree-image(syncCol, synchronize-true) {
+ list-style-image: url("chrome://messenger/skin/icons/check.png");
+}
+
+treechildren::-moz-tree-image(syncCol, isServer-true) {
+ list-style-image: none;
+}
+
+#folderNameCol [sortDirection="ascending"] {
+ list-style-image: none;
+}
+
+#folderNameCol [sortDirection="descending"] {
+ list-style-image: none;
+}
diff --git a/comm/suite/themes/classic/messenger/newmailalert.css b/comm/suite/themes/classic/messenger/newmailalert.css
new file mode 100644
index 0000000000..e1d7a0fa26
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/newmailalert.css
@@ -0,0 +1,67 @@
+/* -*- 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/. */
+
+/* ===== alert.css =====================================================
+ == Styles specific to the alerts dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#newMailAlertNotification {
+ min-height: 60px;
+ border: ridge SteelBlue 4px;
+}
+
+#alertImage {
+ list-style-image: url("chrome://branding/content/icon64.png");
+}
+
+#alertImageBox {
+ padding: 4px;
+}
+
+#alertTitle {
+ font-weight: bold;
+}
+
+#alertTextBox {
+ padding: 4px;
+ padding-inline-end: 16px;
+}
+
+.folderSummary-message-row
+{
+ /* This max width ends up dictating the overall width of the alert window
+ because it controls how large the preview, subject and sender text can be
+ before cropping kicks in */
+ max-width: 450px;
+ padding: 0px 5px;
+}
+
+.folderSummary-subject {
+ font-weight: bold;
+}
+
+.folderSummary-sender, .folderSummary-subject {
+ cursor: inherit;
+}
+
+.folderSummary-previewText {
+ color: grey;
+}
+
+.folderSummaryMessage:hover > .folderSummary-message-row {
+ cursor: pointer;
+ color: blue;
+}
+
+#closeButton {
+ list-style-image: url("chrome://navigator/skin/icons/close.png");
+ -moz-appearance: none;
+ border: none !important;
+ padding: 2px 0px 0px;
+}
diff --git a/comm/suite/themes/classic/messenger/newsblog/feed-subscriptions.css b/comm/suite/themes/classic/messenger/newsblog/feed-subscriptions.css
new file mode 100644
index 0000000000..12d91ab08e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/newsblog/feed-subscriptions.css
@@ -0,0 +1,30 @@
+/* -*- 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/. */
+
+/* ::::: Feed Subscription styling :::::: */
+
+#subscriptionsDialog {
+ width: 40em;
+ height: 30em;
+}
+
+#contentPane {
+ padding: 14px;
+}
+
+#rssFeedInfoBox {
+ border: 1px solid ThreeDShadow;
+ margin: 4px;
+ padding-top: 4px;
+ background-color: ThreeDLightShadow;
+}
+
+#statusContainerBox {
+ height: 24px;
+}
+
+#autotagPrefix {
+ width: 35ch;
+}
diff --git a/comm/suite/themes/classic/messenger/newsblog/rss-feed.png b/comm/suite/themes/classic/messenger/newsblog/rss-feed.png
new file mode 100644
index 0000000000..aef600bf35
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/newsblog/rss-feed.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/prefPanels.css b/comm/suite/themes/classic/messenger/prefPanels.css
new file mode 100644
index 0000000000..f01b406d98
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/prefPanels.css
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ===== prefPanels.css =================================================
+ == Styles for the Messenger preference panels.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Tags ::::: */
+
+#tagList > listhead {
+ text-align: center;
+}
+
+#tagList > listitem > listcell {
+ padding: 0 16px;
+}
diff --git a/comm/suite/themes/classic/messenger/primaryToolbar.css b/comm/suite/themes/classic/messenger/primaryToolbar.css
new file mode 100644
index 0000000000..baae8199db
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/primaryToolbar.css
@@ -0,0 +1,572 @@
+/* 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/. */
+
+/* ===== primaryToolbar.css =============================================
+ == Images for the Mail primary toolbar.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-getmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#button-getmsg:hover {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#button-getmsg:hover:active {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#button-getmsg[disabled] {
+ -moz-image-region: rect(90px 119px 119px 90px) !important;
+}
+
+#button-newmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(150px 29px 179px 0);
+}
+
+#button-newmsg:hover {
+ -moz-image-region: rect(150px 59px 179px 30px);
+}
+
+#button-newmsg:hover:active {
+ -moz-image-region: rect(150px 89px 179px 60px);
+}
+
+#button-newmsg[disabled] {
+ -moz-image-region: rect(150px 119px 179px 90px) !important;
+}
+
+#button-reply {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(210px 29px 239px 0);
+}
+
+#button-reply:hover {
+ -moz-image-region: rect(210px 59px 239px 30px);
+}
+
+#button-reply:hover:active {
+ -moz-image-region: rect(210px 89px 239px 60px);
+}
+
+#button-reply[disabled] {
+ -moz-image-region: rect(210px 119px 239px 90px) !important;
+}
+
+#button-replyall {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(240px 29px 269px 0);
+}
+
+#button-replyall:hover {
+ -moz-image-region: rect(240px 59px 269px 30px);
+}
+
+#button-replyall:hover:active {
+ -moz-image-region: rect(240px 89px 269px 60px);
+}
+
+#button-replyall[disabled] {
+ -moz-image-region: rect(240px 119px 269px 90px) !important;
+}
+
+#button-forward {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#button-forward:hover {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#button-forward:hover:active {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#button-forward[disabled] {
+ -moz-image-region: rect(60px 119px 89px 90px) !important;
+}
+
+#button-file {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#button-file:hover {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#button-file:hover:active,
+#button-file[open] {
+ -moz-image-region: rect(30px 89px 59px 60px);
+}
+
+#button-file[disabled] {
+ -moz-image-region: rect(30px 119px 59px 90px) !important;
+}
+
+#button-goback {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#button-goback:hover {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#button-goback:hover:active {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#button-goback[disabled="true"] {
+ -moz-image-region: rect(60px 119px 89px 90px) !important;
+}
+
+#button-goforward {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#button-goforward:hover {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#button-goforward:hover:active {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#button-goforward[disabled="true"] {
+ -moz-image-region: rect(90px 119px 119px 90px) !important;
+}
+
+#button-next {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(180px 29px 209px 0);
+}
+
+#button-next:hover {
+ -moz-image-region: rect(180px 59px 209px 30px);
+}
+
+#button-next:hover:active {
+ -moz-image-region: rect(180px 89px 209px 60px);
+}
+
+#button-next[disabled] {
+ -moz-image-region: rect(180px 119px 209px 90px) !important;
+}
+
+#button-delete {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#button-delete:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#button-delete:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#button-delete[disabled] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+toolbarpaletteitem > #button-delete {
+ display: -moz-box;
+}
+
+#button-mark {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#button-mark:hover {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#button-mark:hover:active {
+ -moz-image-region: rect(120px 89px 149px 60px);
+}
+
+#button-mark[disabled] {
+ -moz-image-region: rect(120px 119px 149px 90px) !important;
+}
+
+#button-junk {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons.png");
+ -moz-image-region: rect(360px 29px 389px 0);
+}
+
+#button-junk:hover {
+ -moz-image-region: rect(360px 59px 389px 30px);
+}
+
+#button-junk:hover:active {
+ -moz-image-region: rect(360px 89px 389px 60px);
+}
+
+#button-junk[disabled="true"] {
+ -moz-image-region: rect(360px 119px 389px 90px) !important;
+}
+
+#button-stop {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#button-stop:hover {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#button-stop:hover:active {
+ -moz-image-region: rect(30px 89px 59px 60px);
+}
+
+#button-stop[disabled="true"] {
+ -moz-image-region: rect(30px 119px 59px 90px) !important;
+}
+
+#sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32-throbber.png");
+}
+
+#locationFolders {
+ width: 22em;
+}
+
+/* Force the folder location and mail view items to fit in the available width
+ in the Customize Toolbar dialog. */
+#palette-box #locationFolders,
+#palette-box #folder-location-container,
+#palette-box #searchInput,
+#palette-box #button-search-container,
+#palette-box #button-search,
+#palette-box #mailviews-container,
+#palette-box #viewPicker {
+ -moz-box-flex: 1;
+}
+
+#viewPickerPopup > menuitem > .menu-iconic-left,
+#viewPickerPopup > menu > menupopup > menuitem > .menu-iconic-left {
+ display: -moz-box !important;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > #button-getmsg,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-getmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #button-getmsg:hover {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #button-getmsg:hover:active {
+ -moz-image-region: rect(60px 59px 79px 40px);
+}
+
+toolbar[iconsize="small"] > #button-getmsg[disabled] {
+ -moz-image-region: rect(60px 79px 79px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-newmsg,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-newmsg {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(100px 19px 119px 0);
+}
+
+toolbar[iconsize="small"] > #button-newmsg:hover {
+ -moz-image-region: rect(100px 39px 119px 20px);
+}
+
+toolbar[iconsize="small"] > #button-newmsg:hover:active {
+ -moz-image-region: rect(100px 59px 119px 40px);
+}
+
+toolbar[iconsize="small"] > #button-newmsg[disabled] {
+ -moz-image-region: rect(100px 79px 119px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-reply,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-reply {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(140px 19px 159px 0);
+}
+
+toolbar[iconsize="small"] > #button-reply:hover {
+ -moz-image-region: rect(140px 39px 159px 20px);
+}
+
+toolbar[iconsize="small"] > #button-reply:hover:active {
+ -moz-image-region: rect(140px 59px 159px 40px);
+}
+
+toolbar[iconsize="small"] > #button-reply[disabled] {
+ -moz-image-region: rect(140px 79px 159px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-replyall,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-replyall {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(160px 19px 179px 0);
+}
+
+toolbar[iconsize="small"] > #button-replyall:hover {
+ -moz-image-region: rect(160px 39px 179px 20px);
+}
+
+toolbar[iconsize="small"] > #button-replyall:hover:active {
+ -moz-image-region: rect(160px 59px 179px 40px);
+}
+
+toolbar[iconsize="small"] > #button-replyall[disabled] {
+ -moz-image-region: rect(160px 79px 179px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-forward,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-forward {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #button-forward:hover {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #button-forward:hover:active {
+ -moz-image-region: rect(40px 59px 59px 40px);
+}
+
+toolbar[iconsize="small"] > #button-forward[disabled] {
+ -moz-image-region: rect(40px 79px 59px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-file,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-file {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #button-file:hover {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #button-file:hover:active,
+toolbar[iconsize="small"] > #button-file[open] {
+ -moz-image-region: rect(20px 59px 39px 40px);
+}
+
+toolbar[iconsize="small"] > #button-file[disabled] {
+ -moz-image-region: rect(20px 79px 39px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-goback,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-goback {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #button-goback:hover {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #button-goback:hover:active {
+ -moz-image-region: rect(40px 59px 59px 40px);
+}
+
+toolbar[iconsize="small"] > #button-goback[disabled="true"] {
+ -moz-image-region: rect(40px 79px 59px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-goforward,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-goforward {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #button-goforward:hover {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #button-goforward:hover:active {
+ -moz-image-region: rect(60px 59px 79px 40px);
+}
+
+toolbar[iconsize="small"] > #button-goforward[disabled="true"] {
+ -moz-image-region: rect(60px 79px 79px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-next,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-next {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(120px 19px 139px 0);
+}
+
+toolbar[iconsize="small"] > #button-next:hover {
+ -moz-image-region: rect(120px 39px 139px 20px);
+}
+
+toolbar[iconsize="small"] > #button-next:hover:active {
+ -moz-image-region: rect(120px 59px 139px 40px);
+}
+
+toolbar[iconsize="small"] > #button-next[disabled] {
+ -moz-image-region: rect(120px 79px 139px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-delete,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-delete {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #button-delete:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #button-delete:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #button-delete[disabled],
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-delete[disabled] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-mark,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-mark {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(80px 19px 99px 0);
+}
+
+toolbar[iconsize="small"] > #button-mark:hover {
+ -moz-image-region: rect(80px 39px 99px 20px);
+}
+
+toolbar[iconsize="small"] > #button-mark:hover:active {
+ -moz-image-region: rect(80px 59px 99px 40px);
+}
+
+toolbar[iconsize="small"] > #button-mark[disabled] {
+ -moz-image-region: rect(80px 79px 99px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-junk,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-junk {
+ list-style-image: url("chrome://messenger/skin/icons/messengericons-small.png");
+ -moz-image-region: rect(240px 19px 259px 0);
+}
+
+toolbar[iconsize="small"] > #button-junk:hover {
+ -moz-image-region: rect(240px 39px 259px 20px);
+}
+
+toolbar[iconsize="small"] > #button-junk:hover:active {
+ -moz-image-region: rect(240px 59px 259px 40px);
+}
+
+toolbar[iconsize="small"] > #button-junk[disabled],
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-junk[disabled] {
+ -moz-image-region: rect(240px 79px 259px 60px) !important;
+}
+
+toolbar[iconsize="small"] > #button-stop,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-stop {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #button-stop:hover {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #button-stop:hover:active {
+ -moz-image-region: rect(20px 59px 39px 40px);
+}
+
+toolbar[iconsize="small"] > #button-stop[disabled="true"] {
+ -moz-image-region: rect(20px 79px 39px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button,
+toolbar[iconsize="small"] > #sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button[status=active],
+toolbar[iconsize="small"] > #sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16-throbber.png");
+}
+
+/* ::::: Lightning icon sizes ::::: */
+
+#lightning-button-calendar > .toolbarbutton-icon,
+#lightning-button-tasks > .toolbarbutton-icon,
+#extractEventButton > .toolbarbutton-icon,
+#extractTaskButton > .toolbarbutton-icon,
+#extractEventButton > .box-inherit > .toolbarbutton-icon,
+#extractTaskButton > .box-inherit > .toolbarbutton-icon {
+ width: 29px;
+ height: 29px;
+}
+
+toolbar[iconsize="small"] > #lightning-button-calendar > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #lightning-button-tasks > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #extractEventButton > .box-inherit > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #extractTaskButton > .box-inherit > .toolbarbutton-icon {
+ width: 19px;
+ height: 19px;
+}
+
+#lightning-button-calendar[disabled="true"] > .toolbarbutton-icon,
+#lightning-button-tasks[disabled="true"] > .toolbarbutton-icon,
+#extractEventButton[disabled="true"] > .box-inherit > .toolbarbutton-icon,
+#extractTaskButton[disabled="true"] > .box-inherit > .toolbarbutton-icon {
+ opacity: 0.3;
+}
+
+/* ::::: message notification bar style rules ::::: */
+
+.msgNotificationBar {
+ border-bottom: 1px solid;
+ border-bottom-color: #000000;
+ -moz-appearance: toolbox;
+ background-color: #C7BC8F;
+ color: black;
+}
+
+.msgNotificationBar:-moz-lwtheme {
+ text-shadow: none;
+}
+
+.messageImage[value="remoteContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/remote-blocked.png");
+}
+
+.messageImage[value="junkContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/junk.png");
+}
+
+.messageImage[value="phishingContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/phishing.png");
+}
+
+.messageImage[value="mdnContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/info.png");
+}
diff --git a/comm/suite/themes/classic/messenger/searchDialog.css b/comm/suite/themes/classic/messenger/searchDialog.css
new file mode 100644
index 0000000000..49774465b6
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/searchDialog.css
@@ -0,0 +1,57 @@
+/* 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/. */
+
+/* ===== searchDialog.css ===============================================
+ == Styles for the Mail Search dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: box sizes ::::: */
+
+#searchTermTree {
+ min-height: 50px;
+}
+
+#searchTermListBox {
+ height: 100px;
+}
+
+#searchResultListBox {
+ margin: 5px;
+ height: 100px;
+}
+
+#searchAddressBookWindow,
+#searchMailWindow {
+ padding: 0px;
+}
+
+.search-menulist[unavailable="true"] {
+ color: GrayText;
+}
+
+menulist:not(#menuSearchLocalSystem) {
+ width: 12em;
+}
+
+menulist:not(#menuSearchLocalSystem) > menupopup > menuitem {
+ padding-inline-end: 2px;
+}
+
+textbox {
+ margin: 1px 4px;
+}
+
+.filler {
+ padding-inline-end: 22px;
+}
+
+.small-button {
+ min-width: 3em;
+ padding: 0px;
+ margin: 0px 1px;
+}
diff --git a/comm/suite/themes/classic/messenger/smime/certFetchingStatus.css b/comm/suite/themes/classic/messenger/smime/certFetchingStatus.css
new file mode 100644
index 0000000000..9638e0879d
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/certFetchingStatus.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/classic/messenger/smime/icons/hdrCryptoNotOk.png b/comm/suite/themes/classic/messenger/smime/icons/hdrCryptoNotOk.png
new file mode 100644
index 0000000000..32e871e0f8
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/hdrCryptoNotOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/hdrCryptoOk.png b/comm/suite/themes/classic/messenger/smime/icons/hdrCryptoOk.png
new file mode 100644
index 0000000000..5fa57bdb10
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/hdrCryptoOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/hdrSignNotOk.png b/comm/suite/themes/classic/messenger/smime/icons/hdrSignNotOk.png
new file mode 100644
index 0000000000..8db53b65c2
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/hdrSignNotOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/hdrSignOk.png b/comm/suite/themes/classic/messenger/smime/icons/hdrSignOk.png
new file mode 100644
index 0000000000..232b7e6f34
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/hdrSignOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/hdrSignUnknown.png b/comm/suite/themes/classic/messenger/smime/icons/hdrSignUnknown.png
new file mode 100644
index 0000000000..d4befb2256
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/hdrSignUnknown.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/sbCryptoNotOk.png b/comm/suite/themes/classic/messenger/smime/icons/sbCryptoNotOk.png
new file mode 100644
index 0000000000..1a69247ad6
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/sbCryptoNotOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/sbCryptoOk.png b/comm/suite/themes/classic/messenger/smime/icons/sbCryptoOk.png
new file mode 100644
index 0000000000..1c80d511d3
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/sbCryptoOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/sbSignNotOk.png b/comm/suite/themes/classic/messenger/smime/icons/sbSignNotOk.png
new file mode 100644
index 0000000000..d34feafdb3
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/sbSignNotOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/sbSignOk.png b/comm/suite/themes/classic/messenger/smime/icons/sbSignOk.png
new file mode 100644
index 0000000000..addaf488e3
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/sbSignOk.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/sbSignUnknown.png b/comm/suite/themes/classic/messenger/smime/icons/sbSignUnknown.png
new file mode 100644
index 0000000000..0127c147ec
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/sbSignUnknown.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/smimeicons-small.png b/comm/suite/themes/classic/messenger/smime/icons/smimeicons-small.png
new file mode 100644
index 0000000000..a0b4e9841e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/smimeicons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/icons/smimeicons.png b/comm/suite/themes/classic/messenger/smime/icons/smimeicons.png
new file mode 100644
index 0000000000..e850adc565
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/icons/smimeicons.png
Binary files differ
diff --git a/comm/suite/themes/classic/messenger/smime/msgCompSMIMEOverlay.css b/comm/suite/themes/classic/messenger/smime/msgCompSMIMEOverlay.css
new file mode 100644
index 0000000000..76569981b1
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/msgCompSMIMEOverlay.css
@@ -0,0 +1,75 @@
+/* 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/. */
+
+/* ===== msgCompSMIMEOverlay.css ========================================
+ == Styles for the S/Mime in composer window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#button-security {
+ list-style-image: url("chrome://messenger/skin/smime/icons/smimeicons.png");
+ -moz-image-region: rect(0 29px 29px 0px);
+}
+
+#button-security:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#button-security:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#button-security[disabled] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+toolbar[iconsize="small"] > #button-security,
+toolbar[iconsize="small"] > toolbarpaletteitem > #button-security {
+ list-style-image: url("chrome://messenger/skin/smime/icons/smimeicons-small.png");
+ -moz-image-region: rect(0 19px 19px 0px);
+}
+
+toolbar[iconsize="small"] > #button-security:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #button-security:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #button-security[disabled] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+#msgcomposeWindow #signing-status {
+ list-style-image: none;
+ visibility: collapse;
+}
+
+#msgcomposeWindow[signing="ok"] #signing-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow[signing="notok"] #signing-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignNotOk.png");
+ visibility: visible;
+}
+
+
+#msgcomposeWindow #encryption-status {
+ list-style-image: none;
+ visibility: collapse;
+}
+
+#msgcomposeWindow[crypto="ok"] #encryption-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow[crypto="notok"] #encryption-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoNotOk.png");
+ visibility: visible;
+}
diff --git a/comm/suite/themes/classic/messenger/smime/msgCompSecurityInfo.css b/comm/suite/themes/classic/messenger/smime/msgCompSecurityInfo.css
new file mode 100644
index 0000000000..170c162340
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/msgCompSecurityInfo.css
@@ -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/. */
+
+/* ===== msgReadSecurityInfo.css ========================================
+ == Styles for the security info window when displaying received mail.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/classic/messenger/smime/msgHdrViewSMIMEOverlay.css b/comm/suite/themes/classic/messenger/smime/msgHdrViewSMIMEOverlay.css
new file mode 100644
index 0000000000..9e4cdd72af
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/msgHdrViewSMIMEOverlay.css
@@ -0,0 +1,45 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#signedHdrIcon {
+ list-style-image: none;
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="ok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignOk.png");
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="unknown"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignUnknown.png");
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="mismatch"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignUnknown.png");
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="notok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignNotOk.png");
+ visibility: visible;
+}
+
+#encryptedHdrIcon {
+ list-style-image: none;
+ visibility: visible;
+}
+
+#encryptedHdrIcon[encrypted="ok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrCryptoOk.png");
+ visibility: visible;
+}
+
+#encryptedHdrIcon[encrypted="notok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrCryptoNotOk.png");
+ visibility: visible;
+}
diff --git a/comm/suite/themes/classic/messenger/smime/msgReadSMIMEOverlay.css b/comm/suite/themes/classic/messenger/smime/msgReadSMIMEOverlay.css
new file mode 100644
index 0000000000..8633a4c09d
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/msgReadSMIMEOverlay.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#status-bar #signed-status {
+ list-style-image: none;
+}
+
+#status-bar[signed="ok"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignOk.png");
+}
+
+#status-bar[signed="unknown"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignUnknown.png");
+}
+
+#status-bar[signed="mismatch"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignUnknown.png");
+}
+
+#status-bar[signed="notok"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignNotOk.png");
+}
+
+#status-bar #encrypted-status {
+ list-style-image: none;
+}
+
+#status-bar[encrypted="ok"] #encrypted-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoOk.png");
+}
+
+#status-bar[encrypted="notok"] #encrypted-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoNotOk.png");
+}
diff --git a/comm/suite/themes/classic/messenger/smime/msgReadSecurityInfo.css b/comm/suite/themes/classic/messenger/smime/msgReadSecurityInfo.css
new file mode 100644
index 0000000000..7ca95f7b04
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/smime/msgReadSecurityInfo.css
@@ -0,0 +1,27 @@
+/* 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/. */
+
+/* ===== msgReadSecurityInfo.css ========================================
+ == Styles for the security info window when displaying received mail.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#signatureLabel {
+ font-weight: bold;
+}
+
+#signatureCert {
+ margin: 5px;
+}
+
+#encryptionLabel {
+ font-weight: bold;
+}
+
+#encryptionCert {
+ margin: 5px;
+}
diff --git a/comm/suite/themes/classic/messenger/start.css b/comm/suite/themes/classic/messenger/start.css
new file mode 100644
index 0000000000..059a488a2e
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/start.css
@@ -0,0 +1,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/. */
+
+/* ===== start.css =====================================================
+ == Styles used by the mailnews start page.
+ ======================================================================= */
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url("chrome://branding/content/messenger-start-bg.png") no-repeat fixed center 3em;
+ background-size: auto 85%;
+}
+
+h1 {
+ background: url("chrome://branding/content/messenger-start-hdr.png") repeat-x bottom;
+ border-bottom: 2px solid #26269A;
+ font-size: 1.5em;
+ padding: 0.25em 0.67em; /* 0.67*1.5=1, so fit with 1em below */
+ margin: 0;
+}
+
+#main {
+ margin: 0 1em;
+}
+
+p {
+ margin: 0.5em 0;
+}
+
+h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+}
+
+ul {
+ margin: 0.5em 0;
+}
+
diff --git a/comm/suite/themes/classic/messenger/subscribe.css b/comm/suite/themes/classic/messenger/subscribe.css
new file mode 100644
index 0000000000..f7ff21f403
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/subscribe.css
@@ -0,0 +1,70 @@
+/* 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/. */
+
+/* ===== subscribe.css ==================================================
+ == Styles for the Subscribe dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: subscribed icons :::::: */
+
+treechildren::-moz-tree-image(subscribedColumn),
+treechildren::-moz-tree-image(subscribedColumn2) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+treechildren::-moz-tree-image(subscribedColumn, subscribed-true),
+treechildren::-moz-tree-image(subscribedColumn2, subscribed-true) {
+ list-style-image: url("chrome://messenger/skin/icons/check.png");
+}
+
+treechildren::-moz-tree-image(subscribedColumn, subscribable-false) {
+ list-style-image: none;
+}
+
+treechildren::-moz-tree-cell-text(nameColumn, subscribable-false) {
+ color: gray;
+ font-style: italic;
+}
+
+/* ::::: folder icons :::::: */
+
+treechildren::-moz-tree-image(nameColumn) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+}
+
+treechildren::-moz-tree-image(nameColumn, serverType-nntp),
+treechildren::-moz-tree-image(nameColumn2, serverType-nntp) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+/* ::::: server icons :::::: */
+
+.subscribeMenuItem {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.subscribeMenuItem[ServerType="imap"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.subscribeMenuItem[ServerType="nntp"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news.png");
+}
+
+.subscribeMenuItem[ServerType="nntp"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news-lock.png");
+}
+
+/* ::::: statusbar adjustments :::::: */
+
+#statusContainerBox {
+ margin: 0px 4px;
+}
diff --git a/comm/suite/themes/classic/messenger/threadPane.css b/comm/suite/themes/classic/messenger/threadPane.css
new file mode 100644
index 0000000000..6eb11439cd
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/threadPane.css
@@ -0,0 +1,352 @@
+/* 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/. */
+
+/* ===== threadPane.css ==============================================
+ == Styles for the thread pane in the Messenger 3-pane window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: thread decoration ::::: */
+
+treechildren::-moz-tree-cell-text(read) {
+ font-weight: normal;
+}
+
+treechildren::-moz-tree-cell-text(unread) {
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(imapdeleted) {
+ text-decoration: line-through;
+}
+
+/* on a collapsed thread, if the top level message is read, but the thread has
+ * unread children, underline the text. 4.x mac did this, very slick
+ */
+treechildren::-moz-tree-cell-text(container, closed, hasUnread, read) {
+ text-decoration: underline;
+}
+
+/* ::::: priority colors ::::: */
+/****
+ **** Priority colors currently not being used at the moment. It has been
+ **** disabled so as to not conflict with the labels color feature.
+ ****
+treechildren::-moz-tree-cell-text(priorityCol, priority-highest) {
+ color: red;
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, priority-high) {
+ color: rgb(128, 0, 0);
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, priority-lowest) {
+ color: rgb(170, 170, 170);
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, priority-low) {
+ color: rgb(85, 85, 85);
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, selected) {
+ color: -moz-DialogText;
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, selected, focus) {
+ color: HighlightText;
+}
+ ****/
+
+/* ::::: message icons ::::: */
+
+/* ::::: message column icons ::::: */
+
+/* ..... thread column ..... */
+
+.threadColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/threadcol-unthreaded.png");
+}
+
+.threadColumnHeader[sortDirection="ascending"] {
+ list-style-image: url("chrome://messenger/skin/icons/threadcol-threaded.png");
+}
+
+.threadColumnHeader[sortDirection="descending"] {
+ list-style-image: url("chrome://messenger/skin/icons/threadcol-threaded.png");
+}
+
+treechildren::-moz-tree-image(threadCol, container) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed.png");
+}
+
+treechildren::-moz-tree-image(threadCol, container, hasUnread) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed.png");
+}
+
+/* ..... read column ..... */
+
+.readColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/readcol.png");
+}
+
+treechildren::-moz-tree-image(unreadButtonColHeader) {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+treechildren::-moz-tree-image(unreadButtonColHeader, unread) {
+ list-style-image: url("chrome://messenger/skin/icons/readcol.png");
+}
+
+/* ..... attachment column ..... */
+
+.attachmentColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/attachment-col.png");
+}
+
+treechildren::-moz-tree-image(attachmentCol, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/attachment.png");
+}
+
+treechildren::-moz-tree-image(attachmentCol, attach, focus, selected) {
+ list-style-image: url("chrome://messenger/skin/icons/attachment-selected.png");
+}
+
+/* ..... flag column ..... */
+
+.flagColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/flagcol.png");
+}
+
+treechildren::-moz-tree-image(flaggedCol) {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+}
+
+treechildren::-moz-tree-image(flaggedCol, flagged) {
+ list-style-image: url("chrome://messenger/skin/icons/flagcol.png");
+}
+
+/* ..... junkStatus column ..... */
+
+.junkStatusHeader {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk.png");
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+/* "unknown" now looks like "not junk". see bug #182386 */
+treechildren::-moz-tree-image(junkStatusCol) {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+treechildren::-moz-tree-image(junkStatusCol, junk) {
+ list-style-image: url("chrome://messenger/skin/icons/junkcol.png");
+}
+
+treechildren::-moz-tree-image(junkStatusCol, notjunk) {
+ list-style-image: url("chrome://messenger/skin/icons/dot.png");
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+/* ..... subject column, tab and menuitem icons ..... */
+
+.icon-holder[type="message"],
+treechildren::-moz-tree-image(subjectCol) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail.png");
+}
+
+treechildren::-moz-tree-image(subjectCol) {
+ margin-inline-end: 2px;
+}
+
+treechildren::-moz-tree-image(subjectCol, new) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-new.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-reply.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd-reply.png");
+}
+
+.icon-holder[type="message"][Attachment="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-reply.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd-reply.png");
+}
+
+.icon-holder[type="message"][IMAPDeleted="true"],
+treechildren::-moz-tree-image(subjectCol, imapdeleted) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-imapdelete.png");
+}
+
+.icon-holder[type="message"][Offline="true"],
+treechildren::-moz-tree-image(subjectCol, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, new, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-new-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-offl-reply.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd-offl-reply.png");
+}
+
+.icon-holder[type="message"][Attachment="true"][Offline="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-offl.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd-offl.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-offl-reply.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd-offl-reply.png");
+}
+
+.icon-holder[type="message"][IMAPDeleted="true"][Offline="true"],
+treechildren::-moz-tree-image(subjectCol, imapdeleted, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-delete-offl.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"],
+.icon-holder[type="message"][MessageType="nntp"],
+treechildren::-moz-tree-image(subjectCol, rss),
+treechildren::-moz-tree-image(subjectCol, news) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, rss, ignoreSubthread),
+treechildren::-moz-tree-image(subjectCol, news, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-kill.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"][Attachment="true"],
+.icon-holder[type="message"][MessageType="nntp"][Attachment="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach, ignoreSubthread),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach-kill.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"][Attachment="true"][Offline="true"],
+.icon-holder[type="message"][MessageType="nntp"][Attachment="true"][Offline="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach, offline),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach-offl.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach, offline, ignoreSubthread),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach, offline, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach-kill-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, rss, new),
+treechildren::-moz-tree-image(subjectCol, news, new) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, new, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new-attach.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, new, attach, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new-attach-off.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, news, new, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new-offl.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"][Offline="true"],
+.icon-holder[type="message"][MessageType="nntp"][Offline="true"],
+treechildren::-moz-tree-image(subjectCol, rss, offline),
+treechildren::-moz-tree-image(subjectCol, news, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, rss, offline, ignoreSubthread),
+treechildren::-moz-tree-image(subjectCol, news, offline, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-kill-offl.png");
+}
+
+/* ..... new thread icons for watch and ignore ..... */
+
+treechildren::-moz-tree-image(news, threadCol, watch) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, ignore) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-kill.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, watch, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-offl-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, ignore, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-offl-kill.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, watch) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, ignore) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-kill.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, watch, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-offl-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, ignore, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-offl-kill.png");
+}
+
+#sizeCol,
+#unreadCol,
+#totalCol {
+ text-align: right;
+}
diff --git a/comm/suite/themes/classic/messenger/threadPaneExtras.css b/comm/suite/themes/classic/messenger/threadPaneExtras.css
new file mode 100644
index 0000000000..7ac07445a1
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/threadPaneExtras.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* distributors / ISPs can override this to override thread pane icons.
+ * see http://www.mozilla.org/mailnews/arch/threadpaneextras.html
+ */
diff --git a/comm/suite/themes/classic/messenger/threadPaneLabels.css b/comm/suite/themes/classic/messenger/threadPaneLabels.css
new file mode 100644
index 0000000000..21060b4a2c
--- /dev/null
+++ b/comm/suite/themes/classic/messenger/threadPaneLabels.css
@@ -0,0 +1,527 @@
+/* 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/. */
+
+/* ===== threadPaneLabels.css ===========================================
+ == Styles for the thread pane in the Messenger 3-pane window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: thread labels decoration ::::: */
+
+/* There are 10x7 color definitions (size of the color picker used)
+ times 2 (2 style definitions for each color) + 2 general black
+ and white color definitions.
+ The color definitions can be in the following formats:
+ color: red;
+ color: #FF0000;
+ color: rgb(128, 0, 0);
+*/
+treechildren::-moz-tree-cell-text(lc-FFFFFF), .lc-FFFFFF:not([_moz-menuactive]) {
+ color: #FFFFFF
+}
+treechildren::-moz-tree-row(lc-FFFFFF, selected, focus), .lc-FFFFFF[_moz-menuactive] {
+ background-color: #FFFFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-CCCCCC), .lc-CCCCCC:not([_moz-menuactive]) {
+ color: #CCCCCC
+}
+treechildren::-moz-tree-row(lc-CCCCCC, selected, focus), .lc-CCCCCC[_moz-menuactive] {
+ background-color: #CCCCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-C0C0C0), .lc-C0C0C0:not([_moz-menuactive]) {
+ color: #C0C0C0
+}
+treechildren::-moz-tree-row(lc-C0C0C0, selected, focus), .lc-C0C0C0[_moz-menuactive] {
+ background-color: #C0C0C0;
+}
+
+treechildren::-moz-tree-cell-text(lc-999999), .lc-999999:not([_moz-menuactive]) {
+ color: #999999
+}
+treechildren::-moz-tree-row(lc-999999, selected, focus), .lc-999999[_moz-menuactive] {
+ background-color: #999999;
+}
+
+treechildren::-moz-tree-cell-text(lc-666666), .lc-666666:not([_moz-menuactive]) {
+ color: #666666
+}
+treechildren::-moz-tree-row(lc-666666, selected, focus), .lc-666666[_moz-menuactive] {
+ background-color: #666666;
+}
+
+treechildren::-moz-tree-cell-text(lc-333333), .lc-333333:not([_moz-menuactive]) {
+ color: #333333
+}
+treechildren::-moz-tree-row(lc-333333, selected, focus), .lc-333333[_moz-menuactive] {
+ background-color: #333333;
+}
+
+treechildren::-moz-tree-cell-text(lc-000000), .lc-000000:not([_moz-menuactive]) {
+ color: #000000
+}
+treechildren::-moz-tree-row(lc-000000, selected, focus), .lc-000000[_moz-menuactive] {
+ background-color: #000000;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFCCCC), .lc-FFCCCC:not([_moz-menuactive]) {
+ color: #FFCCCC
+}
+treechildren::-moz-tree-row(lc-FFCCCC, selected, focus), .lc-FFCCCC[_moz-menuactive] {
+ background-color: #FFCCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF6666), .lc-FF6666:not([_moz-menuactive]) {
+ color: #FF6666
+}
+treechildren::-moz-tree-row(lc-FF6666, selected, focus), .lc-FF6666[_moz-menuactive] {
+ background-color: #FF6666;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF0000), .lc-FF0000:not([_moz-menuactive]) {
+ color: #FF0000
+}
+treechildren::-moz-tree-row(lc-FF0000, selected, focus), .lc-FF0000[_moz-menuactive] {
+ background-color: #FF0000;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC0000), .lc-CC0000:not([_moz-menuactive]) {
+ color: #CC0000
+}
+treechildren::-moz-tree-row(lc-CC0000, selected, focus), .lc-CC0000[_moz-menuactive] {
+ background-color: #CC0000;
+}
+
+treechildren::-moz-tree-cell-text(lc-990000), .lc-990000:not([_moz-menuactive]) {
+ color: #990000
+}
+treechildren::-moz-tree-row(lc-990000, selected, focus), .lc-990000[_moz-menuactive] {
+ background-color: #990000;
+}
+
+treechildren::-moz-tree-cell-text(lc-660000), .lc-660000:not([_moz-menuactive]) {
+ color: #660000
+}
+treechildren::-moz-tree-row(lc-660000, selected, focus), .lc-660000[_moz-menuactive] {
+ background-color: #660000;
+}
+
+treechildren::-moz-tree-cell-text(lc-330000), .lc-330000:not([_moz-menuactive]) {
+ color: #330000
+}
+treechildren::-moz-tree-row(lc-330000, selected, focus), .lc-330000[_moz-menuactive] {
+ background-color: #330000;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFCC99), .lc-FFCC99:not([_moz-menuactive]) {
+ color: #FFCC99
+}
+treechildren::-moz-tree-row(lc-FFCC99, selected, focus), .lc-FFCC99[_moz-menuactive] {
+ background-color: #FFCC99;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF9966), .lc-FF9966:not([_moz-menuactive]) {
+ color: #FF9966
+}
+treechildren::-moz-tree-row(lc-FF9966, selected, focus), .lc-FF9966[_moz-menuactive] {
+ background-color: #FF9966;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF9900), .lc-FF9900:not([_moz-menuactive]) {
+ color: #FF9900
+}
+treechildren::-moz-tree-row(lc-FF9900, selected, focus), .lc-FF9900[_moz-menuactive] {
+ background-color: #FF9900;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF6600), .lc-FF6600:not([_moz-menuactive]) {
+ color: #FF6600
+}
+treechildren::-moz-tree-row(lc-FF6600, selected, focus), .lc-FF6600[_moz-menuactive] {
+ background-color: #FF6600;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC6600), .lc-CC6600:not([_moz-menuactive]) {
+ color: #CC6600
+}
+treechildren::-moz-tree-row(lc-CC6600, selected, focus), .lc-CC6600[_moz-menuactive] {
+ background-color: #CC6600;
+}
+
+treechildren::-moz-tree-cell-text(lc-993300), .lc-993300:not([_moz-menuactive]) {
+ color: #993300
+}
+treechildren::-moz-tree-row(lc-993300, selected, focus), .lc-993300[_moz-menuactive] {
+ background-color: #993300;
+}
+
+treechildren::-moz-tree-cell-text(lc-663300), .lc-663300:not([_moz-menuactive]) {
+ color: #663300
+}
+treechildren::-moz-tree-row(lc-663300, selected, focus), .lc-663300[_moz-menuactive] {
+ background-color: #663300;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFFF99), .lc-FFFF99:not([_moz-menuactive]) {
+ color: #FFFF99
+}
+treechildren::-moz-tree-row(lc-FFFF99, selected, focus), .lc-FFFF99[_moz-menuactive] {
+ background-color: #FFFF99;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFFF66), .lc-FFFF66:not([_moz-menuactive]) {
+ color: #FFFF66
+}
+treechildren::-moz-tree-row(lc-FFFF66, selected, focus), .lc-FFFF66[_moz-menuactive] {
+ background-color: #FFFF66;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFCC66), .lc-FFCC66:not([_moz-menuactive]) {
+ color: #FFCC66
+}
+treechildren::-moz-tree-row(lc-FFCC66, selected, focus), .lc-FFCC66[_moz-menuactive] {
+ background-color: #FFCC66;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFCC33), .lc-FFCC33:not([_moz-menuactive]) {
+ color: #FFCC33
+}
+treechildren::-moz-tree-row(lc-FFCC33, selected, focus), .lc-FFCC33[_moz-menuactive] {
+ background-color: #FFCC33;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC9933), .lc-CC9933:not([_moz-menuactive]) {
+ color: #CC9933
+}
+treechildren::-moz-tree-row(lc-CC9933, selected, focus), .lc-CC9933[_moz-menuactive] {
+ background-color: #CC9933;
+}
+
+treechildren::-moz-tree-cell-text(lc-996633), .lc-996633:not([_moz-menuactive]) {
+ color: #996633
+}
+treechildren::-moz-tree-row(lc-996633, selected, focus), .lc-996633[_moz-menuactive] {
+ background-color: #996633;
+}
+
+treechildren::-moz-tree-cell-text(lc-663333), .lc-663333:not([_moz-menuactive]) {
+ color: #663333
+}
+treechildren::-moz-tree-row(lc-663333, selected, focus), .lc-663333[_moz-menuactive] {
+ background-color: #663333;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFFFCC), .lc-FFFFCC:not([_moz-menuactive]) {
+ color: #FFFFCC
+}
+treechildren::-moz-tree-row(lc-FFFFCC, selected, focus), .lc-FFFFCC[_moz-menuactive] {
+ background-color: #FFFFCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFFF33), .lc-FFFF33:not([_moz-menuactive]) {
+ color: #FFFF33
+}
+treechildren::-moz-tree-row(lc-FFFF33, selected, focus), .lc-FFFF33[_moz-menuactive] {
+ background-color: #FFFF33;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFFF00), .lc-FFFF00:not([_moz-menuactive]) {
+ color: #FFFF00
+}
+treechildren::-moz-tree-row(lc-FFFF00, selected, focus), .lc-FFFF00[_moz-menuactive] {
+ background-color: #FFFF00;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFCC00), .lc-FFCC00:not([_moz-menuactive]) {
+ color: #FFCC00
+}
+treechildren::-moz-tree-row(lc-FFCC00, selected, focus), .lc-FFCC00[_moz-menuactive] {
+ background-color: #FFCC00;
+}
+
+treechildren::-moz-tree-cell-text(lc-999900), .lc-999900:not([_moz-menuactive]) {
+ color: #999900
+}
+treechildren::-moz-tree-row(lc-999900, selected, focus), .lc-999900[_moz-menuactive] {
+ background-color: #999900;
+}
+
+treechildren::-moz-tree-cell-text(lc-666600), .lc-666600:not([_moz-menuactive]) {
+ color: #666600
+}
+treechildren::-moz-tree-row(lc-666600, selected, focus), .lc-666600[_moz-menuactive] {
+ background-color: #666600;
+}
+
+treechildren::-moz-tree-cell-text(lc-333300), .lc-333300:not([_moz-menuactive]) {
+ color: #333300
+}
+treechildren::-moz-tree-row(lc-333300, selected, focus), .lc-333300[_moz-menuactive] {
+ background-color: #333300;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-99FF99), .lc-99FF99:not([_moz-menuactive]) {
+ color: #99FF99
+}
+treechildren::-moz-tree-row(lc-99FF99, selected, focus), .lc-99FF99[_moz-menuactive] {
+ background-color: #99FF99;
+}
+
+treechildren::-moz-tree-cell-text(lc-66FF99), .lc-66FF99:not([_moz-menuactive]) {
+ color: #66FF99
+}
+treechildren::-moz-tree-row(lc-66FF99, selected, focus), .lc-66FF99[_moz-menuactive] {
+ background-color: #66FF99;
+}
+
+treechildren::-moz-tree-cell-text(lc-33FF33), .lc-33FF33:not([_moz-menuactive]) {
+ color: #33FF33
+}
+treechildren::-moz-tree-row(lc-33FF33, selected, focus), .lc-33FF33[_moz-menuactive] {
+ background-color: #33FF33;
+}
+
+treechildren::-moz-tree-cell-text(lc-33CC00), .lc-33CC00:not([_moz-menuactive]) {
+ color: #33CC00
+}
+treechildren::-moz-tree-row(lc-33CC00, selected, focus), .lc-33CC00[_moz-menuactive] {
+ background-color: #33CC00;
+}
+
+treechildren::-moz-tree-cell-text(lc-009900), .lc-009900:not([_moz-menuactive]) {
+ color: #009900
+}
+treechildren::-moz-tree-row(lc-009900, selected, focus), .lc-009900[_moz-menuactive] {
+ background-color: #009900;
+}
+
+treechildren::-moz-tree-cell-text(lc-006600), .lc-006600:not([_moz-menuactive]) {
+ color: #006600
+}
+treechildren::-moz-tree-row(lc-006600, selected, focus), .lc-006600[_moz-menuactive] {
+ background-color: #006600;
+}
+
+treechildren::-moz-tree-cell-text(lc-003300), .lc-003300:not([_moz-menuactive]) {
+ color: #003300
+}
+treechildren::-moz-tree-row(lc-003300, selected, focus), .lc-003300[_moz-menuactive] {
+ background-color: #003300;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-99FFFF), .lc-99FFFF:not([_moz-menuactive]) {
+ color: #99FFFF
+}
+treechildren::-moz-tree-row(lc-99FFFF, selected, focus), .lc-99FFFF[_moz-menuactive] {
+ background-color: #99FFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-33FFFF), .lc-33FFFF:not([_moz-menuactive]) {
+ color: #33FFFF
+}
+treechildren::-moz-tree-row(lc-33FFFF, selected, focus), .lc-33FFFF[_moz-menuactive] {
+ background-color: #33FFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-66CCCC), .lc-66CCCC:not([_moz-menuactive]) {
+ color: #66CCCC
+}
+treechildren::-moz-tree-row(lc-66CCCC, selected, focus), .lc-66CCCC[_moz-menuactive] {
+ background-color: #66CCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-00CCCC), .lc-00CCCC:not([_moz-menuactive]) {
+ color: #00CCCC
+}
+treechildren::-moz-tree-row(lc-00CCCC, selected, focus), .lc-00CCCC[_moz-menuactive] {
+ background-color: #00CCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-339999), .lc-339999:not([_moz-menuactive]) {
+ color: #339999
+}
+treechildren::-moz-tree-row(lc-339999, selected, focus), .lc-339999[_moz-menuactive] {
+ background-color: #339999;
+}
+
+treechildren::-moz-tree-cell-text(lc-336666), .lc-336666:not([_moz-menuactive]) {
+ color: #336666
+}
+treechildren::-moz-tree-row(lc-336666, selected, focus), .lc-336666[_moz-menuactive] {
+ background-color: #336666;
+}
+
+treechildren::-moz-tree-cell-text(lc-003333), .lc-003333:not([_moz-menuactive]) {
+ color: #003333
+}
+treechildren::-moz-tree-row(lc-003333, selected, focus), .lc-003333[_moz-menuactive] {
+ background-color: #003333;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-CCFFFF), .lc-CCFFFF:not([_moz-menuactive]) {
+ color: #CCFFFF
+}
+treechildren::-moz-tree-row(lc-CCFFFF, selected, focus), .lc-CCFFFF[_moz-menuactive] {
+ background-color: #CCFFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-66FFFF), .lc-66FFFF:not([_moz-menuactive]) {
+ color: #66FFFF
+}
+treechildren::-moz-tree-row(lc-66FFFF, selected, focus), .lc-66FFFF[_moz-menuactive] {
+ background-color: #66FFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-33CCFF), .lc-33CCFF:not([_moz-menuactive]) {
+ color: #33CCFF
+}
+treechildren::-moz-tree-row(lc-33CCFF, selected, focus), .lc-33CCFF[_moz-menuactive] {
+ background-color: #33CCFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-3366FF), .lc-3366FF:not([_moz-menuactive]) {
+ color: #3366FF
+}
+treechildren::-moz-tree-row(lc-3366FF, selected, focus), .lc-3366FF[_moz-menuactive] {
+ background-color: #3366FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-3333FF), .lc-3333FF:not([_moz-menuactive]) {
+ color: #3333FF
+}
+treechildren::-moz-tree-row(lc-3333FF, selected, focus), .lc-3333FF[_moz-menuactive] {
+ background-color: #3333FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-000099), .lc-000099:not([_moz-menuactive]) {
+ color: #000099
+}
+treechildren::-moz-tree-row(lc-000099, selected, focus), .lc-000099[_moz-menuactive] {
+ background-color: #000099;
+}
+
+treechildren::-moz-tree-cell-text(lc-000066), .lc-000066:not([_moz-menuactive]) {
+ color: #000066
+}
+treechildren::-moz-tree-row(lc-000066, selected, focus), .lc-000066[_moz-menuactive] {
+ background-color: #000066;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-CCCCFF), .lc-CCCCFF:not([_moz-menuactive]) {
+ color: #CCCCFF
+}
+treechildren::-moz-tree-row(lc-CCCCFF, selected, focus), .lc-CCCCFF[_moz-menuactive] {
+ background-color: #CCCCFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-9999FF), .lc-9999FF:not([_moz-menuactive]) {
+ color: #9999FF
+}
+treechildren::-moz-tree-row(lc-9999FF, selected, focus), .lc-9999FF[_moz-menuactive] {
+ background-color: #9999FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-6666CC), .lc-6666CC:not([_moz-menuactive]) {
+ color: #6666CC
+}
+treechildren::-moz-tree-row(lc-6666CC, selected, focus), .lc-6666CC[_moz-menuactive] {
+ background-color: #6666CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-6633FF), .lc-6633FF:not([_moz-menuactive]) {
+ color: #6633FF
+}
+treechildren::-moz-tree-row(lc-6633FF, selected, focus), .lc-6633FF[_moz-menuactive] {
+ background-color: #6633FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-6600CC), .lc-6600CC:not([_moz-menuactive]) {
+ color: #6600CC
+}
+treechildren::-moz-tree-row(lc-6600CC, selected, focus), .lc-6600CC[_moz-menuactive] {
+ background-color: #6600CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-333399), .lc-333399:not([_moz-menuactive]) {
+ color: #333399
+}
+treechildren::-moz-tree-row(lc-333399, selected, focus), .lc-333399[_moz-menuactive] {
+ background-color: #333399;
+}
+
+treechildren::-moz-tree-cell-text(lc-330099), .lc-330099:not([_moz-menuactive]) {
+ color: #330099
+}
+treechildren::-moz-tree-row(lc-330099, selected, focus), .lc-330099[_moz-menuactive] {
+ background-color: #330099;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFCCFF), .lc-FFCCFF:not([_moz-menuactive]) {
+ color: #FFCCFF
+}
+treechildren::-moz-tree-row(lc-FFCCFF, selected, focus), .lc-FFCCFF[_moz-menuactive] {
+ background-color: #FFCCFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF99FF), .lc-FF99FF:not([_moz-menuactive]) {
+ color: #FF99FF
+}
+treechildren::-moz-tree-row(lc-FF99FF, selected, focus), .lc-FF99FF[_moz-menuactive] {
+ background-color: #FF99FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC66CC), .lc-CC66CC:not([_moz-menuactive]) {
+ color: #CC66CC
+}
+treechildren::-moz-tree-row(lc-CC66CC, selected, focus), .lc-CC66CC[_moz-menuactive] {
+ background-color: #CC66CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC33CC), .lc-CC33CC:not([_moz-menuactive]) {
+ color: #CC33CC
+}
+treechildren::-moz-tree-row(lc-CC33CC, selected, focus), .lc-CC33CC[_moz-menuactive] {
+ background-color: #CC33CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-993399), .lc-993399:not([_moz-menuactive]) {
+ color: #993399
+}
+treechildren::-moz-tree-row(lc-993399, selected, focus), .lc-993399[_moz-menuactive] {
+ background-color: #993399;
+}
+
+treechildren::-moz-tree-cell-text(lc-663366), .lc-663366:not([_moz-menuactive]) {
+ color: #663366
+}
+treechildren::-moz-tree-row(lc-663366, selected, focus), .lc-663366[_moz-menuactive] {
+ background-color: #663366;
+}
+
+treechildren::-moz-tree-cell-text(lc-330033), .lc-330033:not([_moz-menuactive]) {
+ color: #330033
+}
+treechildren::-moz-tree-row(lc-330033, selected, focus), .lc-330033[_moz-menuactive] {
+ background-color: #330033;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-white, selected, focus) {
+ color: #FFFFFF
+}
+treechildren::-moz-tree-cell-text(lc-black, selected, focus) {
+ color: #000000
+}
+
diff --git a/comm/suite/themes/classic/moz.build b/comm/suite/themes/classic/moz.build
new file mode 100644
index 0000000000..e496b75494
--- /dev/null
+++ b/comm/suite/themes/classic/moz.build
@@ -0,0 +1,15 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ["jar.mn"]
+
+classic_extension_dir = "{972ce4c6-7e08-4474-a285-3208198ce6fd}"
+
+DEFINES["SEAMONKEY_VERSION"] = CONFIG["SEAMONKEY_VERSION"]
+
+FINAL_TARGET_FILES.extensions[classic_extension_dir] += [
+ "icon.png",
+ "preview.png",
+]
diff --git a/comm/suite/themes/classic/navigator/btn1/feeds.png b/comm/suite/themes/classic/navigator/btn1/feeds.png
new file mode 100644
index 0000000000..788b2e6b6b
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/feeds.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/first-disabled.png b/comm/suite/themes/classic/navigator/btn1/first-disabled.png
new file mode 100644
index 0000000000..d716342733
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/first-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/first-hover.png b/comm/suite/themes/classic/navigator/btn1/first-hover.png
new file mode 100644
index 0000000000..67fc2075df
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/first-hover.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/first.png b/comm/suite/themes/classic/navigator/btn1/first.png
new file mode 100644
index 0000000000..1f2f416913
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/first.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/last-disabled.png b/comm/suite/themes/classic/navigator/btn1/last-disabled.png
new file mode 100644
index 0000000000..90fdddf0a8
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/last-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/last-hover.png b/comm/suite/themes/classic/navigator/btn1/last-hover.png
new file mode 100644
index 0000000000..a43aef7f91
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/last-hover.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/last.png b/comm/suite/themes/classic/navigator/btn1/last.png
new file mode 100644
index 0000000000..14db75045b
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/last.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/next-disabled.png b/comm/suite/themes/classic/navigator/btn1/next-disabled.png
new file mode 100644
index 0000000000..cb15799eff
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/next-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/next-hover.png b/comm/suite/themes/classic/navigator/btn1/next-hover.png
new file mode 100644
index 0000000000..700ffdc5e9
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/next-hover.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/next.png b/comm/suite/themes/classic/navigator/btn1/next.png
new file mode 100644
index 0000000000..f54b1245f2
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/next.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/previous-disabled.png b/comm/suite/themes/classic/navigator/btn1/previous-disabled.png
new file mode 100644
index 0000000000..94b1850f72
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/previous-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/previous-hover.png b/comm/suite/themes/classic/navigator/btn1/previous-hover.png
new file mode 100644
index 0000000000..cb62ab48fd
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/previous-hover.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/previous.png b/comm/suite/themes/classic/navigator/btn1/previous.png
new file mode 100644
index 0000000000..3eef01aea5
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/previous.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/top-disabled.png b/comm/suite/themes/classic/navigator/btn1/top-disabled.png
new file mode 100644
index 0000000000..e3811da465
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/top-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/top-hover.png b/comm/suite/themes/classic/navigator/btn1/top-hover.png
new file mode 100644
index 0000000000..7146014aed
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/top-hover.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/top.png b/comm/suite/themes/classic/navigator/btn1/top.png
new file mode 100644
index 0000000000..c95acea0eb
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/top.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/up-disabled.png b/comm/suite/themes/classic/navigator/btn1/up-disabled.png
new file mode 100644
index 0000000000..5f19075989
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/up-disabled.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/up-hover.png b/comm/suite/themes/classic/navigator/btn1/up-hover.png
new file mode 100644
index 0000000000..38c075faf9
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/up-hover.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/btn1/up.png b/comm/suite/themes/classic/navigator/btn1/up.png
new file mode 100644
index 0000000000..9ec230b04e
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/btn1/up.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/chevron.png b/comm/suite/themes/classic/navigator/icons/chevron.png
new file mode 100644
index 0000000000..d556c62a53
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/chevron.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/close.png b/comm/suite/themes/classic/navigator/icons/close.png
new file mode 100644
index 0000000000..e74c7bd42a
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/close.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/identity.png b/comm/suite/themes/classic/navigator/icons/identity.png
new file mode 100644
index 0000000000..35b376f30d
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/identity.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/minimize.png b/comm/suite/themes/classic/navigator/icons/minimize.png
new file mode 100644
index 0000000000..4eeeccf3bf
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/minimize.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/navigatoricons-small.png b/comm/suite/themes/classic/navigator/icons/navigatoricons-small.png
new file mode 100644
index 0000000000..b1716e089c
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/navigatoricons-small.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/navigatoricons.png b/comm/suite/themes/classic/navigator/icons/navigatoricons.png
new file mode 100644
index 0000000000..a05bb5ee9b
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/navigatoricons.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/popup-blocked.png b/comm/suite/themes/classic/navigator/icons/popup-blocked.png
new file mode 100644
index 0000000000..3606ab49c8
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/popup-blocked.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/restore.png b/comm/suite/themes/classic/navigator/icons/restore.png
new file mode 100644
index 0000000000..9064e3934d
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/restore.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/tab-arrow-left.png b/comm/suite/themes/classic/navigator/icons/tab-arrow-left.png
new file mode 100644
index 0000000000..6769c66e7d
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/tab-arrow-left.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/tab-arrow-right.png b/comm/suite/themes/classic/navigator/icons/tab-arrow-right.png
new file mode 100644
index 0000000000..a0c47f1a14
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/tab-arrow-right.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/tab-drag-indicator.png b/comm/suite/themes/classic/navigator/icons/tab-drag-indicator.png
new file mode 100644
index 0000000000..60a3a21b2f
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/tab-drag-indicator.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/icons/tab-new.png b/comm/suite/themes/classic/navigator/icons/tab-new.png
new file mode 100644
index 0000000000..c87613aa3c
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/icons/tab-new.png
Binary files differ
diff --git a/comm/suite/themes/classic/navigator/linkToolbar.css b/comm/suite/themes/classic/navigator/linkToolbar.css
new file mode 100644
index 0000000000..6944743a34
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/linkToolbar.css
@@ -0,0 +1,125 @@
+/* -*- Mode: C; 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/. */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/**
+ * Link toolbar items
+ **/
+
+#link-top
+{
+ list-style-image : url("chrome://navigator/skin/btn1/top.png");
+}
+
+#link-top[disabled="true"]
+{
+ list-style-image : url("chrome://navigator/skin/btn1/top-disabled.png") !important;
+}
+
+#link-top:hover,
+#link-top:hover:active
+{
+ list-style-image : url("chrome://navigator/skin/btn1/top-hover.png");
+}
+
+#link-up
+{
+ list-style-image : url("chrome://navigator/skin/btn1/up.png");
+}
+
+#link-up[disabled="true"]
+{
+ list-style-image : url("chrome://navigator/skin/btn1/up-disabled.png") !important;
+}
+
+#link-up:hover,
+#link-up:hover:active
+{
+ list-style-image : url("chrome://navigator/skin/btn1/up-hover.png");
+}
+
+#link-first
+{
+ list-style-image : url("chrome://navigator/skin/btn1/first.png");
+}
+
+#link-first[disabled="true"]
+{
+ list-style-image : url("chrome://navigator/skin/btn1/first-disabled.png") !important;
+}
+
+#link-first:hover,
+#link-first:hover:active
+{
+ list-style-image : url("chrome://navigator/skin/btn1/first-hover.png");
+}
+
+#link-prev
+{
+ list-style-image : url("chrome://navigator/skin/btn1/previous.png");
+}
+
+#link-prev[disabled="true"]
+{
+ list-style-image : url("chrome://navigator/skin/btn1/previous-disabled.png") !important;
+}
+
+#link-prev:hover,
+#link-prev:hover:active
+{
+ list-style-image : url("chrome://navigator/skin/btn1/previous-hover.png");
+}
+
+#link-next
+{
+ list-style-image : url("chrome://navigator/skin/btn1/next.png");
+}
+
+#link-next[disabled="true"]
+{
+ list-style-image : url("chrome://navigator/skin/btn1/next-disabled.png") !important;
+}
+
+#link-next:hover,
+#link-next:hover:active
+{
+ list-style-image : url("chrome://navigator/skin/btn1/next-hover.png");
+}
+
+#link-last
+{
+ list-style-image : url("chrome://navigator/skin/btn1/last.png");
+}
+
+#link-last[disabled="true"]
+{
+ list-style-image : url("chrome://navigator/skin/btn1/last-disabled.png") !important;
+}
+
+#link-last:hover,
+#link-last:hover:active
+{
+ list-style-image : url("chrome://navigator/skin/btn1/last-hover.png");
+}
+
+#link-feed {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png") !important;
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#link-feed:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#link-feed[disabled="true"] {
+ -moz-image-region: rect(32px 32px 48px 16px);
+}
+
+#link-feed[open="true"] {
+ -moz-image-region: rect(48px 32px 64px 16px);
+}
diff --git a/comm/suite/themes/classic/navigator/navigator.css b/comm/suite/themes/classic/navigator/navigator.css
new file mode 100644
index 0000000000..8828c73bd4
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/navigator.css
@@ -0,0 +1,745 @@
+/* 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/. */
+
+@import url("chrome://navigator/content/navigator.css");
+@import url("chrome://communicator/skin/");
+@import url("chrome://communicator/skin/places/bookmarksToolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+#back-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(60px 29px 89px 0);
+}
+
+#back-button:hover {
+ -moz-image-region: rect(60px 59px 89px 30px);
+}
+
+#back-button:hover:active {
+ -moz-image-region: rect(60px 89px 89px 60px);
+}
+
+#back-button[disabled="true"] {
+ -moz-image-region: rect(60px 119px 89px 90px) !important;
+}
+
+#forward-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(90px 29px 119px 0);
+}
+
+#forward-button:hover {
+ -moz-image-region: rect(90px 59px 119px 30px);
+}
+
+#forward-button:hover:active {
+ -moz-image-region: rect(90px 89px 119px 60px);
+}
+
+#forward-button[disabled="true"] {
+ -moz-image-region: rect(90px 119px 119px 90px) !important;
+}
+
+#reload-button {
+ list-style-image: url("chrome://navigator/skin/icons/navigatoricons.png");
+ -moz-image-region: rect(0 29px 29px 0);
+}
+
+#reload-button:hover {
+ -moz-image-region: rect(0 59px 29px 30px);
+}
+
+#reload-button:hover:active {
+ -moz-image-region: rect(0 89px 29px 60px);
+}
+
+#reload-button[disabled="true"] {
+ -moz-image-region: rect(0 119px 29px 90px) !important;
+}
+
+#stop-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(30px 29px 59px 0);
+}
+
+#stop-button:hover {
+ -moz-image-region: rect(30px 59px 59px 30px);
+}
+
+#stop-button:hover:active {
+ -moz-image-region: rect(30px 89px 59px 60px);
+}
+
+#stop-button[disabled="true"] {
+ -moz-image-region: rect(30px 119px 59px 90px) !important;
+}
+
+#home-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons.png");
+ -moz-image-region: rect(120px 29px 149px 0);
+}
+
+#home-button:hover {
+ -moz-image-region: rect(120px 59px 149px 30px);
+}
+
+#home-button:hover:active {
+ -moz-image-region: rect(120px 89px 149px 60px);
+}
+
+#home-button[disabled="true"] {
+ -moz-image-region: rect(120px 119px 149px 90px) !important;
+}
+
+#sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32-throbber.png");
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #back-button,
+toolbar[iconsize="small"] > #back-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(40px 19px 59px 0);
+}
+
+toolbar[iconsize="small"] > #back-button:hover {
+ -moz-image-region: rect(40px 39px 59px 20px);
+}
+
+toolbar[iconsize="small"] > #back-button:hover:active {
+ -moz-image-region: rect(40px 59px 59px 40px);
+}
+
+toolbar[iconsize="small"] > #back-button[disabled="true"] {
+ -moz-image-region: rect(40px 79px 59px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #forward-button,
+toolbar[iconsize="small"] > #forward-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(60px 19px 79px 0);
+}
+
+toolbar[iconsize="small"] > #forward-button:hover {
+ -moz-image-region: rect(60px 39px 79px 20px);
+}
+
+toolbar[iconsize="small"] > #forward-button:hover:active {
+ -moz-image-region: rect(60px 59px 79px 40px);
+}
+
+toolbar[iconsize="small"] > #forward-button[disabled="true"] {
+ -moz-image-region: rect(60px 79px 79px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #reload-button,
+toolbar[iconsize="small"] > #reload-button {
+ list-style-image: url("chrome://navigator/skin/icons/navigatoricons-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #reload-button:hover {
+ -moz-image-region: rect(0 39px 19px 20px);
+}
+
+toolbar[iconsize="small"] > #reload-button:hover:active {
+ -moz-image-region: rect(0 59px 19px 40px);
+}
+
+toolbar[iconsize="small"] > #reload-button[disabled="true"] {
+ -moz-image-region: rect(0 79px 19px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #stop-button,
+toolbar[iconsize="small"] > #stop-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(20px 19px 39px 0);
+}
+
+toolbar[iconsize="small"] > #stop-button:hover {
+ -moz-image-region: rect(20px 39px 39px 20px);
+}
+
+toolbar[iconsize="small"] > #stop-button:hover:active {
+ -moz-image-region: rect(20px 59px 39px 40px);
+}
+
+toolbar[iconsize="small"] > #stop-button[disabled="true"] {
+ -moz-image-region: rect(20px 79px 39px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #home-button,
+toolbar[iconsize="small"] > #home-button {
+ list-style-image: url("chrome://communicator/skin/icons/communicatoricons-small.png");
+ -moz-image-region: rect(80px 19px 99px 0);
+}
+
+toolbar[iconsize="small"] > #home-button:hover {
+ -moz-image-region: rect(80px 39px 99px 20px);
+}
+
+toolbar[iconsize="small"] > #home-button:hover:active {
+ -moz-image-region: rect(80px 59px 99px 40px);
+}
+
+toolbar[iconsize="small"] > #home-button[disabled="true"] {
+ -moz-image-region: rect(80px 79px 99px 60px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button,
+toolbar[iconsize="small"] > #sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button[status=active],
+toolbar[iconsize="small"] > #sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16-throbber.png");
+}
+
+#FindToolbar {
+ border-top-color: ThreeDHighlight;
+ border-top-width: 1px;
+ border-bottom: 1px solid;
+ border-bottom-color: ThreeDShadow;
+ padding-top: 1px;
+ padding-bottom: 0px;
+}
+
+#FindToolbar:-moz-lwtheme {
+ border-top-color: transparent;
+ border-bottom-color: transparent;
+}
+
+/* ::::: fullscreen window controls ::::: */
+
+#window-controls {
+ -moz-box-align: center;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+ border-left: 1px solid;
+ border-left-color: ThreeDShadow;
+ background-color: -moz-Dialog;
+}
+
+toolbar[mode="text"] > #window-controls > toolbarbutton > .toolbarbutton-icon {
+ display: -moz-box;
+}
+
+#minimize-button {
+ list-style-image: url("chrome://navigator/skin/icons/minimize.png");
+}
+
+#restore-button {
+ list-style-image: url("chrome://navigator/skin/icons/restore.png");
+}
+
+#close-button {
+ list-style-image: url("chrome://navigator/skin/icons/close.png");
+}
+
+/* ::::: nav-bar-inner ::::: */
+
+.nav-bar-class {
+ -moz-box-align: center;
+ min-width: 0px;
+}
+
+.urlbar-security-level[level="high"] {
+ background-color: #FFFFC7;
+ color: #000000;
+}
+
+@media (-moz-windows-theme: aero) {
+ #urlbar {
+ padding: 1px;
+ }
+}
+
+#wrapper-nav-bar-inner[place="palette"] > #nav-bar-inner
+ > .button-toolbar,
+#wrapper-nav-bar-inner[place="palette"] > #nav-bar-inner
+ > #urlbar > .urlbar-icons {
+ display: none;
+}
+
+#urlbar-search-splitter {
+ -moz-appearance: none;
+ min-width: 6px;
+ margin: 0 -3px;
+ border: none;
+ background-color: transparent;
+}
+
+/* ::::: notification popups ::::: */
+
+.popup-notification-icon {
+ width: 64px;
+ height: 64px;
+ margin-inline-end: 10px;
+}
+
+.popup-notification-icon[popupid="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geolocation-64.png");
+}
+
+.popup-notification-icon[popupid="web-notifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-64.png");
+}
+
+.popup-notification-icon[popupid="addon-install-disabled"],
+.popup-notification-icon[popupid="addon-install-blocked"],
+.popup-notification-icon[popupid="addon-install-started"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
+.popup-notification-icon[popupid="addon-install-failed"],
+.popup-notification-icon[popupid="addon-install-complete"],
+.popup-notification-icon[popupid="lwtheme-install-request"],
+.popup-notification-icon[popupid="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ width: 32px;
+ height: 32px;
+}
+
+.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
+.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-64.png");
+}
+
+.popup-notification-icon[popupid="password"] {
+ list-style-image: url("chrome://communicator/skin/icons/key-64.png");
+}
+
+.addon-progress-description {
+ width: 350px;
+ max-width: 350px;
+}
+
+/* Notification icon box */
+#notification-popup-box {
+ margin: 0 3px;
+}
+
+.notification-anchor-icon:-moz-focusring {
+ outline: 1px dotted -moz-DialogText;
+}
+
+#default-notification-icon {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#geo-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/geolocation-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#web-notifications-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#addons-notification-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#indexedDB-notification-icon {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#password-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/key-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+.center-item-box {
+ padding: 12px 16px 0px 16px;
+}
+
+.center-item-box[padbottom="true"] {
+ padding-bottom: 12px;
+}
+
+.center-item-icon {
+ background-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+ background-repeat: no-repeat;
+ height: 16px;
+ width: 16px;
+ margin-bottom: 4px;
+}
+
+.center-item-box[padbottom="true"][warn="true"] {
+ padding-bottom: 4px;
+}
+
+.center-item-box[showseparator="true"] {
+ border-top: 1px solid rgba(3, 14, 27, .1);
+}
+
+.center-item-box[warn="false"] > .center-item-warning {
+ display: none;
+}
+
+.center-item-warning > .text-link[href=""] {
+ display: none;
+}
+
+.center-item-warning-icon {
+ background-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+ background-repeat: no-repeat;
+ width: 16px;
+ height: 15px;
+ margin-bottom: 4px;
+}
+
+.center-item-warning-description {
+ color: #828282;
+}
+
+.center-item-button {
+ min-width: 0px;
+}
+
+/* ::::: page proxy icon ::::: */
+
+#page-proxy-deck,
+#page-proxy-favicon,
+#page-proxy-button {
+ width: 16px;
+ height: 16px;
+}
+
+#page-proxy-deck {
+ cursor: grab;
+ margin: 3px;
+}
+
+#page-proxy-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+#page-proxy-favicon {
+ list-style-image: none;
+}
+
+#page-proxy-button[pageproxystate="invalid"] {
+ cursor: default;
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png");
+}
+
+/* ::::: autocomplete ::::: */
+
+#PopupAutoComplete > richlistbox > richlistitem {
+ height: 20px;
+ min-height: 20px;
+ border: 0;
+ border-radius: 0;
+ padding: 0px 1px 0px 1px;
+}
+
+#PopupAutoComplete > richlistbox > richlistitem > .ac-title {
+ font: icon;
+ margin-inline-start: 6px;
+}
+
+#PopupAutoComplete > richlistbox {
+ padding: 0;
+}
+
+.autocomplete-history-dropmarker {
+ min-width: 2em; /* Fix to show the menulist-dropmarker under newer GTK3 versions */
+ border-right-width: 1px;
+ border-bottom-width: 1px;
+ border-right-color: ThreeDShadow;
+ border-bottom-color: ThreeDShadow;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(value) {
+ padding-inline-start: 15px;
+}
+
+.autocomplete-search-box {
+ border-top: 2px groove -moz-Dialog;
+ background-color: -moz-Dialog;
+ color: ButtonText;
+}
+
+panel[nomatch="true"] > .autocomplete-search-box {
+ border-top: 1px solid ThreeDHighlight;
+}
+
+.autocomplete-search-engine {
+ padding: 2px;
+}
+
+.autocomplete-search-engine[menuactive="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.autocomplete-search-engine-img {
+ margin-inline-end: 4px;
+ width: 16px;
+ height: 16px;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
+ color: GrayText;
+}
+
+/* ::::: go and searchbuttons ::::: */
+
+#search-button,
+#go-button {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 4px;
+ min-height: 25px;
+ font: message-box;
+ font-weight: bold;
+}
+
+#go-button {
+ padding-inline-start: 2px;
+ padding-inline-end: 2px;
+}
+
+#search-button {
+ list-style-image: url("chrome://communicator/skin/icons/search.png");
+ -moz-image-region: rect(0 17px 17px 0);
+}
+
+#search-button:hover {
+ -moz-image-region: rect(0 35px 17px 18px);
+}
+
+#search-button:hover:active {
+ -moz-image-region: rect(0 53px 17px 36px);
+}
+
+#search-button > .button-box > .button-icon {
+ display: -moz-box;
+}
+
+toolbar[mode="text"] #search-button > .button-box > .button-icon,
+toolbar[mode="icons"] #search-button > .button-box > .button-text {
+ display: none;
+}
+
+/* ::::: sidebar splitter ::::: */
+
+#sidebar-splitter {
+ border-left: none;
+ border-right: none;
+ min-width: 5px;
+}
+
+/* ::::: content area ::::: */
+
+#status-bar {
+ border-top: none;
+ min-width: 1px;
+}
+
+#security-button[level="high"] > .statusbarpanel-contentbox {
+ background-color: #B4CD32;
+}
+
+#security-button[level="broken"] > .statusbarpanel-contentbox {
+ background-color: #E83404;
+}
+
+#security-button[label] > .statusbarpanel-contentbox {
+ background-color: #62C441;
+}
+
+#security-button > .statusbarpanel-contentbox > .statusbarpanel-text {
+ margin: 0px;
+ color: #FFFFFF;
+ text-shadow: none;
+}
+
+/* make same background-color translucent on lwthemes */
+#security-button[level="high"] > .statusbarpanel-contentbox:-moz-lwtheme {
+ background-color: rgba(232, 219, 153, .8);
+}
+
+#security-button[level="broken"] > .statusbarpanel-contentbox:-moz-lwtheme {
+ background-color: rgba(232, 52, 4, .8);
+}
+
+#security-button[label] > .statusbarpanel-contentbox:-moz-lwtheme {
+ background-color: rgba(98, 196, 65, .8);
+}
+
+#ev-button {
+ list-style-image: url("chrome://communicator/skin/icons/identity.png");
+}
+
+#popupIcon {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+#invalid-form-popup {
+ -moz-appearance: tooltip;
+ border: 1px solid InfoText;
+ padding: 2px 3px 0px 3px;
+ max-width: 40em;
+ background-color: InfoBackground;
+ color: InfoText;
+ font: message-box;
+ font-weight: bold;
+}
+
+/* ::::: personal toolbar ::::: */
+
+#bookmarks-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+#bookmarks-button[open="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open.png");
+}
+
+toolbarbutton.chevron {
+ list-style-image: url("chrome://navigator/skin/icons/chevron.png") !important;
+}
+
+toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+toolbarbutton.chevron > .toolbarbutton-text {
+ display: none; /* hide chevron label which has a width even if blank */
+}
+
+toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
+ display: -moz-box; /* display chevron icon in text mode */
+}
+
+/* Prevent [mode="icons"|"text"] from hiding the label and icon */
+#PlacesToolbarItems .bookmark-item > .toolbarbutton-text,
+#PlacesToolbarItems .bookmark-item > .toolbarbutton-icon {
+ display: -moz-box !important;
+}
+
+#PersonalToolbar[iconsize="small"] > toolbarpaletteitem > #home-button > .toolbarbutton-icon,
+#PersonalToolbar[iconsize="small"] > #home-button > .toolbarbutton-icon {
+ width: 16px;
+ height: 16px;
+}
+
+#PersonalToolbar > #home-button {
+ cursor: pointer;
+}
+
+#PersonalToolbar > #home-button:hover {
+ text-decoration: underline;
+}
+
+#PersonalToolbar > #home-button[disabled="true"] {
+ cursor: default !important;
+ text-decoration: none !important;
+}
+
+/*
+ In customize mode we hide the normal bookmark items and show a placeholder
+ for the drag/drop UI.
+*/
+.bookmarks-toolbar-customize {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+ display: none;
+ max-width: 15em !important;
+}
+
+#wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
+ width: 16px;
+ height: 16px;
+ background: url("chrome://communicator/skin/places/bookmarksToolbar.png") no-repeat;
+}
+
+/* ::::: star button ::::: */
+
+#star-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark.png");
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#star-button:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#star-button:hover:active {
+ -moz-image-region: rect(16px 48px 32px 32px);
+}
+
+#star-button[starred="true"] {
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#star-button[starred="true"]:hover {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#star-button[starred="true"]:hover:active {
+ -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+#editBookmarkPanelStarIcon {
+ list-style-image: url("chrome://communicator/skin/places/bookmark.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+/* ::::: feeds ::::: */
+
+.feedsMenu {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+.feedsMenu[_moz-menuactive="true"] {
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+.feedsMenu[disabled="true"] {
+ -moz-image-region: rect(32px 16px 48px 0px);
+}
+
+.feedsMenu[open="true"] {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#feedsButton {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png");
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#feedsButton:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+/* Need to set fixed width to stop the zoom display from changing size and moving around the zoom buttons */
+#zoomLevel-display {
+ width: 60px;
+ max-width: 60px;
+ margin-left: 0px;
+ margin-right: 0px;
+}
diff --git a/comm/suite/themes/classic/navigator/pageInfo.css b/comm/suite/themes/classic/navigator/pageInfo.css
new file mode 100644
index 0000000000..42ddab213f
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/pageInfo.css
@@ -0,0 +1,126 @@
+/* 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/. */
+
+@import "chrome://global/skin/global.css";
+
+/* Misc */
+tree {
+ margin: .5em;
+}
+
+.gridSeparator {
+ width: .5em;
+}
+
+textbox {
+ background: transparent !important;
+ border: none;
+ padding: 0px;
+ -moz-appearance: none;
+ -moz-user-focus: none;
+}
+
+textbox.header {
+ margin-inline-start: 0;
+}
+
+textbox.meta-properties {
+ margin-top: 1px;
+}
+
+.iframe {
+ margin: .5em;
+ background: white;
+ overflow: auto;
+}
+
+.fixedsize {
+ height: 8.5em;
+}
+
+textbox[disabled] {
+ font-style: italic;
+}
+
+/* General Tab */
+#general-security-identity {
+ white-space: pre-wrap;
+ line-height: 2em;
+}
+
+/* Media Tab */
+#imagetree {
+ min-height: 10em;
+}
+
+#mediaGrid {
+ min-height: 9em;
+}
+
+#mediaLabelColumn {
+ min-width: 10em;
+}
+
+#thepreviewimage {
+ margin: 1em;
+}
+
+treechildren::-moz-tree-cell-text(broken) {
+ font-style: italic;
+ color: graytext;
+}
+
+/* Feeds Tab */
+#feedListbox > richlistitem {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C0C0C0;
+}
+
+.feedTitle {
+ font-weight: bold;
+}
+
+/* Permissions Tab */
+#permList {
+ margin-top: .5em;
+ overflow: auto;
+}
+
+.permission {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C0C0C0;
+}
+
+.permissionLabel {
+ font-weight: bold;
+}
+
+/* Security Tab */
+.fieldValue {
+ font-weight: bold;
+}
+
+#identity-icon {
+ width: 64px;
+ height: 64px;
+ max-height: 64px;
+ list-style-image: url("chrome://navigator/skin/icons/identity.png");
+ -moz-image-region: rect(0px, 64px, 64px, 0px);
+}
+
+#identity-icon.verifiedDomain {
+ -moz-image-region: rect(64px, 64px, 128px, 0px);
+}
+
+#identity-icon.verifiedIdentity {
+ -moz-image-region: rect(128px, 64px, 192px, 0px);
+}
diff --git a/comm/suite/themes/classic/navigator/tabbrowser.css b/comm/suite/themes/classic/navigator/tabbrowser.css
new file mode 100644
index 0000000000..d3b9fd7731
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/tabbrowser.css
@@ -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/. */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.tabbrowser-strip {
+ min-height: 0px;
+ padding: 0px;
+ /* 1px ThreeDShadow bottom border that doesn't take up space and that
+ selected panel can just draw over */
+ box-shadow: 0px -1px ThreeDShadow inset;
+}
+
+tabpanels {
+ -moz-appearance: none;
+}
+
+.tabbrowser-tabs {
+ padding-top: 1px;
+}
+
+.tabs-left,
+.tabs-right {
+ width: 3px;
+ border-bottom: none;
+}
+
+.tab-icon {
+ margin-top: 1px;
+ margin-inline-start: 1px;
+ margin-inline-end: 4px;
+ width: 16px;
+ height: 16px;
+}
+
+.tabbrowser-tab {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+ padding: 0px 2px 2px;
+ margin-bottom: 1px;
+ border-bottom: none;
+}
+
+.tabbrowser-tab[busy] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.tabbrowser-tab[selected="true"] {
+ margin-bottom: 0px;
+ padding-top: 2px; /* compensates the top margin of background tabs */
+ padding-bottom: 3px; /* compensates the bottom margin of background tabs */
+ background-color: -moz-dialog;
+ border-top-color: unset;
+ border-right-color: unset;
+ color: -moz-dialogtext;
+ border: 1px solid Gray;
+ border-top: 2px solid RoyalBlue;
+ border-bottom: 0px none;
+ -moz-appearance: none;
+}
+
+.tabbrowser-tab:-moz-lwtheme {
+ text-shadow: none;
+}
+
+.scrollbutton-up:-moz-lwtheme,
+.scrollbutton-down:-moz-lwtheme:not([selected="true"]),
+.tabbrowser-tab:-moz-lwtheme:not([selected="true"]) {
+ opacity: .8;
+}
+
+/* ::::: Tab scrollbox arrow, and all-tabs buttons ::::: */
+
+.scrollbutton-up,
+.scrollbutton-down {
+ -moz-appearance: tab;
+ margin: 2px 0px 1px;
+ padding: 0px;
+ border-top: 1px solid;
+ border-right: 1px solid;
+ border-left: 1px solid;
+ border-bottom: 1px solid ThreeDHighlight;
+ border-top-color: ThreeDLightShadow;
+ border-right-color: ThreeDDarkShadow;
+ border-left-color: ThreeDLightShadow;
+ border-radius: 2px 2px 0px 0px;
+}
+
+@media (-moz-windows-theme: aero) {
+ .scrollbutton-up,
+ .scrollbutton-down {
+ padding: 0px 2px;
+ }
+}
+
+.scrollbutton-up:-moz-locale-dir(rtl),
+.scrollbutton-down:-moz-locale-dir(ltr) {
+ border-right-color: ThreeDShadow;
+}
+
+.scrollbutton-up:-moz-locale-dir(ltr),
+.scrollbutton-down:-moz-locale-dir(rtl) {
+ border-left-color: ThreeDShadow;
+}
+
+.scrollbutton-up,
+.scrollbutton-down:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://navigator/skin/icons/tab-arrow-left.png");
+}
+
+.scrollbutton-down,
+.scrollbutton-up:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://navigator/skin/icons/tab-arrow-right.png");
+}
+
+.scrollbutton-up,
+.scrollbutton-down {
+ -moz-image-region: rect(0px, 8px, 12px, 0px);
+}
+
+.scrollbutton-up[disabled="true"],
+.scrollbutton-down[disabled="true"] {
+ -moz-image-region: rect(0px, 16px, 12px, 8px);
+}
+
+.scrollbutton-down {
+ box-shadow: 0px 0px 0px 9px transparent inset;
+ transition: box-shadow 1s ease-out;
+}
+
+.scrollbutton-down:hover,
+.scrollbutton-down[notifybgtab="true"],
+.scrollbutton-down[disabled="true"] {
+ transition: none;
+}
+
+.scrollbutton-up:not([disabled="true"]):hover:active,
+.scrollbutton-down:not([disabled="true"]):hover:active,
+.scrollbutton-down[notifybgtab="true"] {
+ box-shadow: 0px 0px 0px 9px ThreeDShadow inset;
+}
+
+.tabs-alltabs-button {
+ margin: 2px 0px 1px;
+}
+
+.tabs-alltabs-button > .toolbarbutton-icon {
+ margin: 0px !important;
+}
+
+/* All tabs menupopup */
+.alltabs-item {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.alltabs-item[selected="true"] {
+ font-weight: bold;
+}
+
+.alltabs-item[busy] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.alltabs-item[tabIsScrolled] {
+ font-style: italic;
+}
+
+/* ::::: close button ::::: */
+
+.tabs-closebutton {
+ margin: 3px;
+ list-style-image: url("chrome://communicator/skin/icons/close-button.png");
+}
+
+.tabs-newbutton {
+ margin: 0px;
+ list-style-image: url("chrome://navigator/skin/icons/tab-new.png");
+}
+
+.tab-drop-indicator-bar {
+ height: 11px;
+ margin-top: -11px;
+ margin-inline-start: -6px;
+ position: relative;
+}
+
+.tab-drop-indicator {
+ height: 11px;
+ width: 11px;
+ margin-bottom: -5px;
+ position: relative;
+ list-style-image: url('chrome://navigator/skin/icons/tab-drag-indicator.png');
+}
+
+tooltip[tabpreview="true"] {
+ max-width: none;
+}
diff --git a/comm/suite/themes/classic/navigator/webDeveloper.css b/comm/suite/themes/classic/navigator/webDeveloper.css
new file mode 100644
index 0000000000..185f7b38ba
--- /dev/null
+++ b/comm/suite/themes/classic/navigator/webDeveloper.css
@@ -0,0 +1,205 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* Mostly copied from mozilla/devtools/client/themes/commandline.inc.css */
+/* Developer Tools */
+
+/* Developer toolbar */
+
+/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
+ We are copy/pasting variables from light-theme and dark-theme,
+ since they aren't loaded in this context (within browser.css). */
+#developer-toolbar[devtoolstheme="light"] {
+ --gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
+ --gcli-input-background: #f0f1f2; /* --theme-toolbar-background */
+ --gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
+ --gcli-input-color: #18191a; /* --theme-body-color */
+ --gcli-border-color: #aaaaaa; /* --theme-splitter-color */
+ --selection-background: #4c9ed9; /* --theme-selection-background */
+ --selection-color: #f5f7fa; /* --theme-selection-color */
+}
+
+#developer-toolbar[devtoolstheme="dark"] {
+ --gcli-background-color: #343c45; /* --theme-toolbar-background */
+ --gcli-input-background: rgba(37, 44, 51, .6); /* --theme-tab-toolbar-background */
+ --gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
+ --gcli-input-color: #b6babf; /* --theme-body-color-alt */
+ --gcli-border-color: #000000; /* --theme-splitter-color */
+ --selection-background: #1d4f73; /* --theme-selection-background */
+ --selection-color: #f5f7fa; /* --theme-selection-color */
+}
+
+#developer-toolbar {
+ padding: 0;
+ background-color: var(--gcli-background-color);
+ border-top: 1px solid var(--gcli-border-color);
+}
+
+#developer-toolbar[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before {
+ filter: invert(1);
+}
+
+#developer-toolbar-toolbox-button {
+ list-style-image: url("chrome://devtools/skin/images/toggle-tools.png");
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+#developer-toolbar-toolbox-button:hover > .toolbarbutton-icon {
+ filter: brightness(120%);
+}
+
+#developer-toolbar-toolbox-button:hover:active > .toolbarbutton-icon {
+ filter: saturate(150%);
+}
+
+#developer-toolbar-toolbox-button[checked=true] > .toolbarbutton-icon {
+ filter: hue-rotate(180deg);
+}
+
+@media (min-resolution: 1.1dppx) {
+ #developer-toolbar-toolbox-button {
+ list-style-image: url("chrome://devtools/skin/images/toggle-tools@2x.png");
+ -moz-image-region: rect(0px, 128px, 32px, 96px);
+ }
+}
+
+/* Error counter */
+
+#developer-toolbar-toolbox-button[error-count]:before {
+ color: white;
+ min-width: 16px;
+ text-shadow: none;
+ background-color: firebrick;
+ border-radius: 2px;
+ margin-inline-end: 2px;
+/*
+ Firefox browser/themes/windows/browser.css
+ color: #FDF3DE;
+ min-width: 16px;
+ text-shadow: none;
+ background-image: linear-gradient(#B4211B, #8A1915);
+ border-radius: 1px;
+ margin-inline-end: 5px;
+ Firefox browser/themes/linux/browser.css
+ color: #FDF3DE;
+ min-width: 16px;
+ text-shadow: none;
+ background-image: linear-gradient(#B4211B, #8A1915);
+ border-radius: 1px;
+ margin-inline-end: 2px;
+*/
+}
+
+/* GCLI */
+
+html|*#gcli-tooltip-frame,
+html|*#gcli-output-frame {
+ padding: 0;
+ border-width: 0;
+ background-color: transparent;
+}
+
+#gcli-output,
+#gcli-tooltip {
+ border-width: 0;
+ background-color: transparent;
+ -moz-appearance: none;
+}
+
+.gclitoolbar-input-node,
+.gclitoolbar-complete-node {
+ -moz-box-align: center;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-right: 8px;
+ text-shadow: none;
+ box-shadow: none;
+ background-color: transparent;
+}
+
+.gclitoolbar-input-node {
+ -moz-appearance: none;
+ color: var(--gcli-input-color);
+ background-color: var(--gcli-input-background);
+ background-repeat: no-repeat;
+ background-position: 4px center;
+ box-shadow: 1px 0 0 var(--gcli-border-color) inset,
+ -1px 0 0 var(--gcli-border-color) inset;
+ outline-style: none;
+ padding: 0;
+}
+
+.gclitoolbar-input-node[focused="true"] {
+ background-color: var(--gcli-input-focused-background);
+}
+
+.gclitoolbar-input-node::before {
+ content: "";
+ display: inline-block;
+ -moz-box-ordinal-group: 0;
+ width: 16px;
+ height: 16px;
+ margin: 0 2px;
+ background-image: url("chrome://devtools/skin/images/commandline-icon.png");
+ background-position: 0 center;
+ background-size: 32px 16px;
+}
+
+.gclitoolbar-input-node[focused="true"]::before {
+ background-position: -16px center;
+}
+
+@media (min-resolution: 1.1dppx) {
+ .gclitoolbar-input-node::before {
+ background-image: url("chrome://devtools/skin/images/commandline-icon@2x.png");
+ }
+}
+
+.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
+ background-color: var(--selection-background);
+ color: var(--selection-color);
+ text-shadow: none;
+}
+
+.gclitoolbar-complete-node {
+ padding-left: 21px;
+ background-color: transparent;
+ color: transparent;
+ z-index: 100;
+ pointer-events: none;
+}
+
+.gcli-in-incomplete,
+.gcli-in-error,
+.gcli-in-ontab,
+.gcli-in-todo,
+.gcli-in-closebrace,
+.gcli-in-param,
+.gcli-in-valid {
+ margin: 0;
+ padding: 0;
+}
+
+.gcli-in-incomplete {
+ border-bottom: 2px dotted #999;
+}
+
+.gcli-in-error {
+ border-bottom: 2px dotted #F00;
+}
+
+.gcli-in-ontab {
+ color: hsl(210,0%,35%);
+}
+
+.gcli-in-todo {
+ color: hsl(210,50%,35%);
+}
+
+.gcli-in-closebrace {
+ color: hsl(0,0%,80%);
+}
diff --git a/comm/suite/themes/classic/preview.png b/comm/suite/themes/classic/preview.png
new file mode 100644
index 0000000000..d3ed55290d
--- /dev/null
+++ b/comm/suite/themes/classic/preview.png
Binary files differ
diff --git a/comm/suite/themes/classic/windows/communicator/sanitizeDialog.css b/comm/suite/themes/classic/windows/communicator/sanitizeDialog.css
new file mode 100644
index 0000000000..097a483d88
--- /dev/null
+++ b/comm/suite/themes/classic/windows/communicator/sanitizeDialog.css
@@ -0,0 +1,56 @@
+/* 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/. */
+
+#sanitizeDurationChoice {
+ margin-inline-end: 0;
+}
+
+/* Align the duration label with the warning box and item list */
+#sanitizeDurationLabel {
+ margin-inline-start: 3px;
+}
+
+
+/* Hide the duration dropdown suffix label if it's empty. Otherwise it
+ takes up a little space, causing the end of the dropdown to not be aligned
+ with the warning box. */
+#sanitizeDurationSuffixLabel[value=""] {
+ display: none;
+}
+
+
+/* Sanitize everything warning box */
+#sanitizeWarningBox {
+ background-color: Window;
+ border: 1px solid ThreeDDarkShadow;
+ border-radius: 5px;
+ padding: 16px;
+}
+
+#sanitizeWarningIcon {
+ list-style-image: url("chrome://global/skin/icons/warning-large.png");
+ padding: 0;
+ margin: 0;
+}
+
+#sanitizeWarningDescBox {
+ padding: 0 16px;
+ margin: 0;
+}
+
+
+/* Make the item list the same width as the warning box */
+#itemList {
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+}
+
+
+/* Align the last dialog button with the end of the warning box */
+.dialog-button-box {
+ margin-inline-end: 0;
+}
+.dialog-button[dlgtype="cancel"] {
+ margin-inline-end: 0;
+}
diff --git a/comm/suite/themes/modern/README b/comm/suite/themes/modern/README
new file mode 100644
index 0000000000..66f8cb8f97
--- /dev/null
+++ b/comm/suite/themes/modern/README
@@ -0,0 +1,6 @@
+this is the modern skin (the original 6.0 skin)
+
+if you add to this, make sure you add to the other skin, otherwise when you
+switch skins, bad things will happen.
+
+questions to pixeljockeys@netscape.com
diff --git a/comm/suite/themes/modern/communicator/aboutPrivateBrowsing.css b/comm/suite/themes/modern/communicator/aboutPrivateBrowsing.css
new file mode 100644
index 0000000000..8d2488202a
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/aboutPrivateBrowsing.css
@@ -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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#warningScreen {
+ background-color: #FFF;
+}
+
+#warningBox {
+ background-color: #C7D0D9;
+ color: #22262F;
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ margin: 0 1em;
+}
+
+#warningInnerBox {
+ max-width: 50em;
+}
+
+#warningTitle {
+ font-weight: bold;
+ margin: 0 0 .6em;
+ font-size: 160%;
+ border-bottom: 1px solid #7A8490;
+}
+
+#warningText
+{
+ font-size: 110%;
+ margin-inline-start: 0;
+}
+
+/* Pick the desired icons depending on the window's context */
+
+#warningBox.private > #warningBoxIcon {
+ list-style-image: url("chrome://global/skin/icons/information-48.png");
+ width: 48px;
+ height: 48px;
+ margin-inline-end: 3em;
+}
+
+#warningBox.normal > #warningBoxIcon {
+ list-style-image: url("chrome://global/skin/icons/question-48.png");
+ width: 48px;
+ height: 48px;
+ margin-inline-end: 3em;
+}
+
+#trackWarnBox {
+ margin-top: 0.6em;
+ margin-inline-end: 7em;
+ -moz-box-align: center;
+}
+
+#trackWarnIcon {
+ list-style-image: url("chrome://global/skin/icons/warning-24.png");
+ width: 24px;
+ height: 24px;
+}
+
+/* Define additional styles to look similar to the netError/certError pages */
+
+#warningStatus {
+ margin: 0.4em 0 1.2em 0;
+ padding-bottom: 1.2em;
+ border-bottom: 1px solid #7A8490;
+ font-size: 135%;
+}
+
+#warningInnerBox > button {
+ margin: 0.8em 0 1em 0;
+}
+
+#warningOuterBox > vbox > label,
+#warningInnerBox > description {
+ margin-inline-start: 0;
+}
diff --git a/comm/suite/themes/modern/communicator/aboutSessionRestore.css b/comm/suite/themes/modern/communicator/aboutSessionRestore.css
new file mode 100644
index 0000000000..1faf46d241
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/aboutSessionRestore.css
@@ -0,0 +1,45 @@
+/* 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/. */
+
+#tabList {
+ width: 100%;
+ height: 12em;
+}
+
+treechildren::-moz-tree-image(icon),
+treechildren::-moz-tree-image(noicon) {
+ padding-right: 2px;
+ margin: 0px 2px;
+ width: 16px;
+ height: 16px;
+}
+
+treechildren::-moz-tree-image(noicon) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+treechildren::-moz-tree-image(container, noicon) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+treechildren::-moz-tree-image(container, noicon, open) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open.png");
+}
+
+treechildren::-moz-tree-checkbox {
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+treechildren::-moz-tree-checkbox(checked) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+treechildren::-moz-tree-checkbox(partial) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-dis-check.png");
+}
+
+#buttons {
+ margin-top: 2em;
+ margin-inline-start: 80px;
+}
diff --git a/comm/suite/themes/modern/communicator/aboutSyncTabs.css b/comm/suite/themes/modern/communicator/aboutSyncTabs.css
new file mode 100644
index 0000000000..069fad2d57
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/aboutSyncTabs.css
@@ -0,0 +1,83 @@
+/* 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/. */
+
+#tabsList {
+ background: #FFFFFF url(chrome://communicator/skin/sync/sync-bg.png) repeat-x center -80px;
+}
+
+#headers {
+ background: url(chrome://communicator/skin/sync/sync-32.png) no-repeat;
+ width: 45em;
+ height: 32px;
+ margin: 4px 2em 0px;
+}
+
+#tabsListHeading {
+ font-size: 140%;
+ font-weight: bold;
+ margin-inline-start: 40px;
+}
+
+richlistitem {
+ margin-inline-end: 2em;
+}
+
+richlistitem[type="tab"] {
+ min-height: 3em;
+ border: #999999 1px solid !important;
+ padding: 2px 5px;
+ margin-bottom: 4px;
+ margin-inline-start: 4em;
+ border-radius: 6px;
+ background-color: #DDE3EB;
+ width: 44em;
+ opacity: 0.9;
+ box-shadow:
+ inset rgba(255, 255, 255, 0.5) 0 1px 0px,
+ inset rgba(0, 0, 0, 0.1) 0 -2px 0px,
+ rgba(0, 0, 0, 0.1) 0px 1px 0px;
+}
+
+richlistitem[type="tab"][selected="true"] {
+ background-color: #424F63;
+}
+
+richlistitem[type="client"] {
+ min-height: 2em;
+ color: #000000;
+ margin-inline-start: 2em;
+ margin-top: 2px;
+ margin-bottom: 3px;
+ width: 42em;
+ border-radius: 6px;
+ background-color: transparent;
+ -moz-user-focus: ignore !important;
+}
+
+.mobile[type="client"] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-mobileIcon.png");
+}
+
+.desktop[type="client"] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-desktopIcon.png");
+}
+
+.title,
+.clientName {
+ font-size: 1.1em;
+}
+
+.url {
+ color: blue;
+ font-size: 0.95em;
+}
+
+.url[selected="true"] {
+ color: inherit;
+}
+
+.tabIcon {
+ padding-inline-start: 2px;
+ padding-top: 2px;
+}
diff --git a/comm/suite/themes/modern/communicator/blockedSite.css b/comm/suite/themes/modern/communicator/blockedSite.css
new file mode 100644
index 0000000000..eabf1217b3
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/blockedSite.css
@@ -0,0 +1,19 @@
+/* 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/. */
+
+#buttons > span {
+ float: left;
+}
+
+#buttons > #ignoreWarningButton {
+ float: right;
+}
+
+/* the following two ids refer to <span> elements defined in safeBrowsing.dtd */
+
+#malware_sitename,
+#phishing_sitename,
+#unwanted_sitename {
+ word-wrap: break-word;
+}
diff --git a/comm/suite/themes/modern/communicator/brand.css b/comm/suite/themes/modern/communicator/brand.css
new file mode 100644
index 0000000000..cc19f0100b
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/brand.css
@@ -0,0 +1,60 @@
+/* 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/. */
+
+/* ==== brand.css ===========================================================
+ == Styles related to branding in the Communicator suite.
+ ========================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#wrapper-throbber-box > #throbber-box > #navigator-throbber,
+#navigator-throbber {
+ -moz-user-focus: ignore !important;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin: 6px 8px 0px;
+ border: none;
+ min-width: 0px;
+ list-style-image: url("chrome://communicator/skin/brand/throbber-single.png");
+ background: transparent;
+}
+
+#navigator-throbber .button-icon {
+ margin-inline-start: 0;
+}
+
+#navigator-throbber .button-text {
+ display: none;
+}
+
+#navigator-throbber[busy="true"] {
+ list-style-image: url("chrome://communicator/skin/brand/throbber-anim.png");
+}
+
+#throbber-box {
+ -moz-box-align: start;
+}
+
+toolbar[mode="icons"] #wrapper-throbber-box > #throbber-box > #navigator-throbber,
+toolbar[mode="icons"] #navigator-throbber {
+ margin: 4px 8px 2px;
+}
+
+window[chromehidden~="toolbar"] #navigator-throbber,
+toolbar[mode="text"] #wrapper-throbber-box > #throbber-box > #navigator-throbber,
+toolbar[iconsize="small"] #wrapper-throbber-box > #throbber-box > #navigator-throbber,
+toolbar[mode="text"] #navigator-throbber,
+toolbar[iconsize="small"] #navigator-throbber {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 6px;
+ margin-inline-end: 4px;
+ list-style-image: url("chrome://communicator/skin/brand/throbber16-single.png");
+}
+
+window[chromehidden~="toolbar"] #navigator-throbber[busy="true"],
+toolbar[mode="text"] #navigator-throbber[busy="true"],
+toolbar[iconsize="small"] #navigator-throbber[busy="true"] {
+ list-style-image: url("chrome://communicator/skin/brand/throbber16-anim.png");
+}
diff --git a/comm/suite/themes/modern/communicator/brand/throbber-anim.png b/comm/suite/themes/modern/communicator/brand/throbber-anim.png
new file mode 100644
index 0000000000..c946b2e5a1
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/brand/throbber-anim.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/brand/throbber-single.png b/comm/suite/themes/modern/communicator/brand/throbber-single.png
new file mode 100644
index 0000000000..609352f86a
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/brand/throbber-single.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/brand/throbber16-anim.png b/comm/suite/themes/modern/communicator/brand/throbber16-anim.png
new file mode 100644
index 0000000000..480ef01635
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/brand/throbber16-anim.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/brand/throbber16-single.png b/comm/suite/themes/modern/communicator/brand/throbber16-single.png
new file mode 100644
index 0000000000..c8a8c52aee
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/brand/throbber16-single.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/button.css b/comm/suite/themes/modern/communicator/button.css
new file mode 100644
index 0000000000..9b830f64af
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/button.css
@@ -0,0 +1,115 @@
+/* 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/. */
+
+
+/* ==== button.css ==========================================================
+ == Styles for special buttons in the Communicator suite.
+ ========================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: large toolbar buttons ::::: */
+
+.toolbarbutton-1 {
+ color: #323642;
+ font-size: 83.3333%;
+}
+
+toolbar[mode="text"] .toolbarbutton-1,
+toolbar[labelalign="end"] .toolbarbutton-1 {
+ color: inherit;
+ font-size: 100%;
+ padding: 0px 2px !important;
+}
+
+toolbar[iconsize="small"] .toolbarbutton-1 {
+ border: 1px solid transparent !important;
+}
+
+.toolbarbutton-1,
+.toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-button,
+.toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-button
+{
+ -moz-box-orient: vertical;
+ -moz-box-pack: center;
+ border: none !important;
+ padding: 0px !important;
+}
+
+.toolbarbutton-1 .toolbarbutton-text {
+ min-width: 33px;
+}
+
+.toolbarbutton-1:hover:active {
+ color: #FFFFFF;
+}
+
+.toolbarbutton-1[disabled="true"]:hover:active {
+ color: #9399AB;
+}
+
+.toolbarbutton-1 > .toolbarbutton-icon,
+.toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
+.toolbarbutton-1[type="menu-button"]
+ > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+ margin-bottom: 2px;
+}
+
+.toolbarbutton-1[type="menu"] > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+toolbar[mode="text"] .toolbarbutton-1 {
+ min-width: 46px;
+}
+
+toolbar[mode="text"] .toolbarbutton-1,
+toolbar[mode="text"] .toolbarbutton-1[type="menu-button"]
+ > .toolbarbutton-menubutton-button,
+toolbar[labelalign="end"] .toolbarbutton-1,
+toolbar[labelalign="end"] .toolbarbutton-1[type="menu-button"]
+ > .toolbarbutton-menubutton-button {
+ -moz-box-orient: horizontal;
+}
+
+toolbar[mode="icons"] .toolbarbutton-text,
+toolbar[mode="text"] .toolbarbutton-icon {
+ display: none;
+}
+
+toolbar[mode="icons"] .toolbarbutton-menubutton-dropmarker {
+ margin-top: 20px;
+ margin-bottom: 0px;
+ margin-inline-start: 40px;
+ margin-inline-end: 0px;
+}
+
+toolbar[mode="text"] .toolbarbutton-menubutton-dropmarker,
+toolbar[labelalign="end"]:not([mode="icons"])
+ .toolbarbutton-menubutton-dropmarker {
+ margin: 0px 2px !important;
+}
+
+toolbar[iconsize="small"] .toolbarbutton-menubutton-dropmarker {
+ margin-top: 0px;
+ margin-bottom: 2px;
+ margin-inline-start: 14px;
+ margin-inline-end: 0px;
+}
+
+toolbar[iconsize=small][mode="icons"] .toolbarbutton-menubutton-dropmarker {
+ margin-top: 8px;
+ margin-bottom: 0px;
+ margin-inline-start: 14px;
+ margin-inline-end: 0px;
+}
+
+/* ::::: standard toolbar buttons ::::: */
+
+.button-toolbar {
+ -moz-user-focus: ignore !important;
+ margin: 0px;
+}
diff --git a/comm/suite/themes/modern/communicator/certError.css b/comm/suite/themes/modern/communicator/certError.css
new file mode 100644
index 0000000000..bacc9b1c57
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/certError.css
@@ -0,0 +1,81 @@
+/* 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/. */
+
+
+html {
+ background: #FFF;
+}
+
+body {
+ margin: 0;
+ padding: 0 1em;
+ color: #22262F;
+ font: message-box;
+}
+
+h1 {
+ margin: 0 0 .6em;
+ border-bottom: 1px solid #7A8490;
+ font-size: 160%;
+}
+
+h2 {
+ font-size: 130%;
+}
+
+span.hostname {
+ font-weight: bolder;
+}
+
+#errorPageContainer {
+ position: relative;
+ min-width: 13em;
+ max-width: 52em;
+ margin: 4em auto;
+ border: 1px solid #E8DB99;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ background: url("chrome://global/skin/icons/sslWarning.png") left 0 no-repeat #C7D0D9;
+ background-origin: content-box;
+}
+
+body[dir="rtl"] #errorPageContainer {
+ background-position: right 0;
+}
+
+#errorTitle {
+ margin-inline-start: 80px;
+}
+
+#errorLongContent {
+ margin-inline-start: 80px;
+}
+
+#errorShortDescText.wrap {
+ white-space: pre-wrap;
+}
+
+#technicalContent > h2, #expertContent > h2 {
+ cursor: pointer;
+ padding-inline-start: 20px;
+ position: relative;
+ left: -20px;
+ background: url("chrome://global/skin/tree/twisty-open.png") left center no-repeat;
+}
+
+#technicalContent[collapsed] > h2,
+#expertContent[collapsed] > h2 {
+ background-image: url("chrome://global/skin/tree/twisty-clsd.png");
+}
+
+#technicalContent[collapsed] > p,
+#expertContent[collapsed] > div {
+ display: none;
+}
+
+#technicalContentText {
+ overflow: auto;
+ white-space: pre-wrap;
+}
diff --git a/comm/suite/themes/modern/communicator/communicator.css b/comm/suite/themes/modern/communicator/communicator.css
new file mode 100644
index 0000000000..af7ee6008d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/communicator.css
@@ -0,0 +1,264 @@
+/* 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/. */
+
+/* ==== communicator.css ====================================================
+ == Styles shared everywhere throughout the Communicator suite.
+ ========================================================================== */
+
+@import url("chrome://global/skin/global.css");
+@import url("chrome://communicator/content/communicator.css");
+@import url("chrome://communicator/skin/brand.css");
+@import url("chrome://communicator/skin/button.css");
+@import url("chrome://communicator/skin/toolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.wizard-box {
+ padding: 20px 44px 10px;
+}
+
+/* ::::: autocomplete ::::: */
+
+.autocomplete-history-popup {
+ max-height: 25em;
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue) {
+ width: 16px;
+ height: 16px;
+ margin-left: 1px;
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue, directory) {
+ /*list-style-image: url("chrome://global/skin/filepicker/dir-closed.png");*/
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+textbox[autocompletesearch="history file"] .autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue, file) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+/* ::::: online/offline icons ::::: */
+
+#offline-status[offline="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/offline.png");
+}
+
+#offline-status {
+ list-style-image: url("chrome://communicator/skin/icons/online.png");
+}
+
+/* ::::: security button icons ::::: */
+
+#security-button {
+ list-style-image: url("chrome://communicator/skin/icons/lock-insecure.png");
+}
+
+#security-button[level="high"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-secure.png");
+}
+
+#security-button[level="broken"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-broken.png");
+}
+
+/* ::::: spell checker ::::: */
+
+.spell-suggestion {
+ font-weight: bold;
+}
+
+/* ::::: directional button icons ::::: */
+
+.up {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.png");
+}
+
+.down {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+.down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.png");
+}
+
+.sidebarTree {
+ border: none;
+ margin: 0px !important;
+}
+
+/* ::::: toolbar print button ::::: */
+#print-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+ -moz-image-region: rect(0 42px 39px 0);
+}
+
+#print-button:hover {
+ -moz-image-region: rect(0 84px 39px 42px);
+}
+
+#print-button:hover:active {
+ -moz-image-region: rect(0 126px 39px 84px);
+}
+
+#print-button[disabled="true"] {
+ -moz-image-region: rect(0 168px 39px 126px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button {
+ list-style-image: url("chrome://communicator/skin/icons/common-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #print-button:hover {
+ -moz-image-region: rect(0 38px 19px 19px);
+}
+
+toolbar[iconsize="small"] > #print-button:hover:active {
+ -moz-image-region: rect(0 57px 19px 38px);
+}
+
+toolbar[iconsize="small"] > #print-button[disabled="true"] {
+ -moz-image-region: rect(0 76px 19px 57px) !important;
+}
+
+/* ::::: notification bars ::::: */
+
+.messageImage[value="refresh-blocked"] {
+ list-style-image: url("chrome://communicator/skin/icons/application.png");
+}
+
+.messageImage[value="plugin-crashed"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.messageImage[value="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geo.png");
+}
+
+.messageImage[value="persistent-storage"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-icons.svg#persistent-storage");
+ width: 16px;
+ height: 16px;
+}
+
+.messageImage[value="webNotifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
+.messageImage[value="indexedDB-permissions-prompt"],
+.messageImage[value="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+}
+
+.messageImage[value="addon-install-blocked"],
+.messageImage[value="addon-install-cancelled"],
+.messageImage[value="addon-install-complete"],
+.messageImage[value="addon-install-disabled"],
+.messageImage[value="addon-install-failed"],
+.messageImage[value="addon-install-started"],
+.messageImage[value="lwtheme-install-request"],
+.messageImage[value="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.messageImage[value="popup-blocked"] {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+.messageImage[value="blocked-badware-page"] {
+ list-style-image: url("chrome://global/skin/icons/blacklist_favicon.png");
+}
+
+.messageImage[value="EnterInsecureMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-insecure-16.png");
+}
+
+.messageImage[value="EnterSecureMessage"],
+.messageImage[value="BlockedActiveContentMessage"],
+.messageImage[value="BlockedDisplayContentMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-secure-16.png");
+}
+
+.messageImage[value="MixedContentMessage"],
+.messageImage[value="MixedActiveContentMessage"],
+.messageImage[value="MixedDisplayContentMessage"] {
+ list-style-image: url("chrome://communicator/skin/icons/lock-broken-16.png");
+}
+
+/* ::::: dialog header ::::: */
+
+dialogheader {
+ margin: 0px 5px 5px;
+ border: 1px solid #2D3B49;
+ padding: 5px 8px;
+ background-color: #90A1B3;
+ color: #000000;
+}
+
+.dialogheader-title {
+ margin: 0px !important;
+ font-size: 120%;
+ font-weight: bold;
+}
+
+/* ::::: statusbar ::::: */
+
+statusbar {
+ border-top: 1px solid #C7D0D9;
+ min-height: 15px;
+ min-width: 1px; /* DON'T DELETE!
+ Prevents hiding of scrollbars in browser when window is made smaller.*/
+ background-color: #C7D0D9;
+ color: #22262F;
+ font-size: 83.3333%;
+}
+
+statusbarpanel {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border-top: 1px solid #E0ECF6;
+ border-right: 1px solid #8997A1;
+ border-left: 1px solid #E0ECF6;
+ padding: 0px 4px;
+}
+
+.statusbar-resizerpanel {
+ border: 0px;
+ -moz-box-align: end;
+ -moz-box-pack: end;
+ padding: 0px;
+}
+
+.statusbarpanel-progress {
+ -moz-box-align: stretch;
+ border: 0px;
+ margin: 0px;
+ padding: 0px;
+}
+
+.statusbarpanel-iconic {
+ padding: 0px;
+}
+
+.statusbarpanel-text {
+ margin: 0px !important;
+}
+
+.statusbarpanel-backgroundbox {
+ -moz-box-align: stretch;
+ padding: 0px;
+}
+
+.statusbarpanel-backgroundbox > .statusbarpanel-contentbox {
+ padding: 0px 4px;
+ -moz-box-align: center;
+}
diff --git a/comm/suite/themes/modern/communicator/console/console.css b/comm/suite/themes/modern/communicator/console/console.css
new file mode 100644
index 0000000000..3a42abb282
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/console/console.css
@@ -0,0 +1,106 @@
+/* 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/. */
+
+/* ===== console.css ====================================================
+ == Styles used by the Error Console window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.console-box {
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+/* ::::: console rows ::::: */
+
+.console-row {
+ border-bottom: 2px solid #000000;
+ padding: 0px;
+}
+
+.console-row-icon {
+ border-right: 1px outset #C7D0D9;
+ padding: 5px;
+ background-color: #C7D0D9;
+ -moz-box-align: start !important;
+}
+
+.console-icon {
+ list-style-image: inherit;
+}
+
+/* ..... error rows ..... */
+
+.console-row-code {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-inline-start: 3px;
+ padding-inline-end: 0px;
+ color: #0000BB;
+ font-size: 120%;
+}
+
+.console-dots,
+.console-caret {
+ height: 9px;
+}
+
+.console-dots {
+ background: url("chrome://communicator/skin/console/console-error-dash.png") repeat-x top;
+}
+
+.console-caret {
+ width: 7px;
+ background: url("chrome://communicator/skin/console/console-error-caret.png") no-repeat top;
+}
+
+/* ..... message rows ..... */
+
+.console-row[type="message"] {
+ font-family: monospace;
+}
+
+/* ..... selected state ..... */
+
+.console-row[selected="true"] {
+ background-color: #B7DBEB !important;
+}
+
+.console-row-code[selected="true"],
+.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
+ color: inherit !important;
+}
+
+/* ::::: icons ::::: */
+
+.console-row[type="error"],
+.console-row[type="exception"] {
+ list-style-image: url("chrome://global/skin/icons/error-48.png");
+}
+
+.console-row[type="warning"] {
+ list-style-image: url("chrome://global/skin/icons/warning-48.png");
+}
+
+.console-row[type="message"] {
+ list-style-image: url("chrome://global/skin/icons/information-48.png");
+}
+
+/* ::::: toolbars ::::: */
+
+#TextfieldEval {
+ margin: 2px !important;
+}
+
+#ButtonEval {
+ margin-top: 2px !important;
+ margin-bottom: 2px !important;
+ margin-inline-start: 0px !important;
+ margin-inline-end: 2px !important;
+}
+
+toolbarseparator {
+ min-height: 1em;
+}
diff --git a/comm/suite/themes/modern/communicator/console/error-caret.png b/comm/suite/themes/modern/communicator/console/error-caret.png
new file mode 100644
index 0000000000..44b78aa29c
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/console/error-caret.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/console/error-dash.png b/comm/suite/themes/modern/communicator/console/error-dash.png
new file mode 100644
index 0000000000..ab07898eff
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/console/error-dash.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/customizeToolbar.css b/comm/suite/themes/modern/communicator/customizeToolbar.css
new file mode 100644
index 0000000000..940f26e7dc
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/customizeToolbar.css
@@ -0,0 +1,20 @@
+/* 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/. */
+
+window,
+dialog {
+ padding: 7px 5px 5px 5px;
+}
+
+#instructions {
+ font-weight: bold;
+ font-size: 120%;
+}
+
+#CustomizeToolbarWindow > #main-box {
+ border-top: none;
+ border-left: 1px solid #5D616E;
+ border-right: 1px solid #5D616E;
+ border-bottom: 2px solid #5D616E;
+}
diff --git a/comm/suite/themes/modern/communicator/dataman/dataman.css b/comm/suite/themes/modern/communicator/dataman/dataman.css
new file mode 100644
index 0000000000..7293d7247f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/dataman/dataman.css
@@ -0,0 +1,31 @@
+/* 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/. */
+
+#permList {
+ margin-top: .5em;
+ overflow: auto;
+}
+
+#permList[disabled="true"] {
+ opacity: .5;
+}
+
+.permission {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C7D0D9;
+}
+
+.hostLabel {
+ font-weight: bold;
+ font-style: italic;
+ margin-inline-end: 1em;
+}
+
+.permissionLabel {
+ font-weight: bold;
+}
diff --git a/comm/suite/themes/modern/communicator/dataman/datamanIcon-16.png b/comm/suite/themes/modern/communicator/dataman/datamanIcon-16.png
new file mode 100644
index 0000000000..9f1ba6b43e
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/dataman/datamanIcon-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/datetimepicker.css b/comm/suite/themes/modern/communicator/datetimepicker.css
new file mode 100644
index 0000000000..bba0369906
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/datetimepicker.css
@@ -0,0 +1,129 @@
+/* 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/. */
+
+/* ===== datetimepicker.css =============================================
+ == Styles used by the XUL datepicker and timepicker elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+datepicker, timepicker {
+ margin: 2px 4px;
+ padding: 0;
+ border: none;
+ background: none;
+ cursor: default;
+}
+
+.datetimepicker-input-box {
+ -moz-appearance: textfield;
+ cursor: text;
+ margin-inline-end: 2px;
+ border: 1px solid #5D616E;
+ padding: 2px 0 3px 0;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+ padding: 1px;
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+.datetimepicker-input-subbox {
+ text-align: right;
+ width: 1.6em;
+}
+
+html|*.datetimepicker-input {
+ text-align: end;
+}
+
+.datetimepicker-separator {
+ margin: 0 !important;
+}
+
+.datetimepicker-year {
+ width: 3.2em;
+}
+
+datepicker[readonly="true"],
+timepicker[readonly="true"] {
+ background-color: #C7D0D9;
+ color: -moz-DialogText;
+}
+
+datepicker[disabled="true"],
+timepicker[disabled="true"] {
+ cursor: default;
+ background-color: #C7D0D9;
+ color: GrayText;
+}
+
+.datepicker-mainbox {
+ margin: 2px 4px;
+ border: 2px solid;
+ -moz-border-top-colors: #BEC3D3 #5D616E;
+ -moz-border-right-colors: #F8FAFE #5D616E;
+ -moz-border-bottom-colors: #F8FAFE #5D616E;
+ -moz-border-left-colors: #BEC3D3 #5D616E;
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+.datepicker-popupgrid > .datepicker-mainbox {
+ margin: 0;
+ border: none;
+}
+
+.datepicker-gridlabel, .datepicker-weeklabel {
+ text-align: center;
+}
+
+.datepicker-gridlabel[today="true"] {
+ background-color: #C7D0D9;
+ color: white;
+}
+
+.datepicker-gridlabel[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.datepicker-button {
+ -moz-appearance: none;
+ min-width: 8px;
+ padding: 0px;
+}
+
+.datepicker-previous {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.png");
+}
+
+.datepicker-next {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.png");
+}
+
+.datepicker-previous[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-dis.png");
+}
+
+.datepicker-next[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-dis.png");
+}
+
+.datepicker-previous:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.png");
+}
+
+.datepicker-next:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.png");
+}
+
+.datepicker-previous[disabled="true"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-dis.png");
+}
+
+.datepicker-next[disabled="true"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-dis.png");
+}
diff --git a/comm/suite/themes/modern/communicator/dialogs.css b/comm/suite/themes/modern/communicator/dialogs.css
new file mode 100644
index 0000000000..58b813f719
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/dialogs.css
@@ -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/. */
+
+/* ==== dialogs.css ==================================================
+ == Styles used by certain dialogs in the Communicator suite.
+ ====================================================================== */
+
+@import url("chrome://communicator/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/modern/communicator/downloads/dl-remove.png b/comm/suite/themes/modern/communicator/downloads/dl-remove.png
new file mode 100644
index 0000000000..b653b600e5
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/downloads/dl-remove.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/downloads/downloadButtons.png b/comm/suite/themes/modern/communicator/downloads/downloadButtons.png
new file mode 100644
index 0000000000..4fe7963abb
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/downloads/downloadButtons.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/downloads/downloadmanager.css b/comm/suite/themes/modern/communicator/downloads/downloadmanager.css
new file mode 100644
index 0000000000..b6052da3c3
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/downloads/downloadmanager.css
@@ -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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Note to themers:
+ On rows and all cells, those properties for download states are available:
+ active, inactive, resumable, paused, downloading, finished, failed, canceled, blocked
+*/
+
+treechildren::-moz-tree-image(Name) {
+ margin-inline-end: 2px;
+}
+
+#pauseButton,
+treechildren::-moz-tree-image(ActionPlay, downloading, resumable) {
+ /* pause */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+#ActionPlay,
+#resumeButton,
+treechildren::-moz-tree-image(ActionPlay, paused, resumable) {
+ /* resume */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+#retryButton,
+treechildren::-moz-tree-image(ActionPlay, failed),
+treechildren::-moz-tree-image(ActionPlay, canceled) {
+ /* retry */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+#ActionStop,
+#cancelButton,
+treechildren::-moz-tree-image(ActionStop, active) {
+ /* cancel */
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+treechildren::-moz-tree-image(ActionStop, inactive) {
+ /* remove */
+ list-style-image: url("chrome://communicator/skin/downloads/dl-remove.png");
+ -moz-image-region: auto;
+}
+
+/* progress dialogs */
+#dlProgressWindow {
+ /* match dialog.css */
+ padding: 7px 5px 5px;
+}
+
+/* label with dropdown, actually done as a button type=menu */
+#fileName, #fileSource {
+ background-color: transparent;
+ border: none;
+}
+
+#fileName:focus,
+#fileSource:focus {
+ outline: 3px solid #98A5B2;
+ -moz-outline-radius: 4px 5px;
+}
+
+#fileName > .button-box > hbox > .button-text,
+#fileSource > .button-box > hbox > .button-text {
+ margin: 0 !important;
+}
+
+#fileName {
+ font-weight: bold;
+}
+
+.mini-button {
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ margin: 0;
+ min-width: 0;
+ min-height: 0;
+}
+
+.mini-button:focus {
+ outline: 3px solid #98A5B2;
+ outline-offset: -2px;
+ -moz-outline-radius: 50%;
+}
+
+.mini-button > .button-box > .button-text {
+ margin: 0 !important;
+}
diff --git a/comm/suite/themes/modern/communicator/feed-subscribe-ui.css b/comm/suite/themes/modern/communicator/feed-subscribe-ui.css
new file mode 100644
index 0000000000..8bb90b3dab
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/feed-subscribe-ui.css
@@ -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/. */
+
+#feedSubscribeLine {
+ font: message-box;
+}
+
+menupopup:-moz-locale-dir(rtl) {
+ direction: rtl;
+}
diff --git a/comm/suite/themes/modern/communicator/feed-subscribe.css b/comm/suite/themes/modern/communicator/feed-subscribe.css
new file mode 100644
index 0000000000..4b6d693b6d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/feed-subscribe.css
@@ -0,0 +1,150 @@
+/* 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/. */
+
+html {
+ background-color: #FFFFFF;
+ font: message-box;
+}
+
+#feedBody {
+ border: 1px solid #2D3B49;
+ padding: 3em;
+ padding-inline-start: 30px;
+ margin: 2em auto;
+ background-color: #C7D0D9;
+ color: #22262F;
+}
+
+#feedHeaderContainer {
+ border: 1px solid #2D3B49;
+ border-radius: 10px;
+ margin: -4em auto 0 auto;
+ background-color: #C7D0D9;
+}
+
+#feedHeader {
+ margin-top: 4.9em;
+ margin-bottom: 1em;
+ margin-inline-start: 1.4em;
+ margin-inline-end: 1em;
+ padding-inline-start: 2.9em;
+ font-size: 110%;
+ color: #000000;
+}
+
+.feedBackground {
+ background: url("chrome://communicator/skin/icons/feedIcon.png") 0% 10% no-repeat transparent;
+}
+
+.videoPodcastBackground {
+ background: url("chrome://communicator/skin/icons/videoFeedIcon.png") 0% 10% no-repeat transparent;
+}
+
+.audioPodcastBackground {
+ background: url("chrome://communicator/skin/icons/audioFeedIcon.png") 0% 10% no-repeat transparent;
+}
+
+#feedHeader[dir="rtl"] {
+ background-position: 100% 10%;
+}
+
+#feedIntroText {
+ display: none;
+}
+
+#feedHeader[firstrun="true"] #feedIntroText {
+ padding-top: 0.1em;
+ padding-inline-start: 0.6em;
+ display: block;
+}
+
+#feedHeader[firstrun="true"] > #feedSubscribeLine {
+ padding-inline-start: 1.8em;
+}
+
+#feedSubscribeLine {
+ padding-top: 0.2em;
+}
+
+/* Don't print subscription UI */
+@media print {
+ #feedHeaderContainer {
+ display: none;
+ }
+}
+
+body {
+ margin: 0;
+ padding: 0 3em;
+ color: -moz-fieldText;
+ font: message-box;
+}
+
+h1 {
+ font-size: 160%;
+ border-bottom: 2px solid #7A8490;
+ margin: 0 0 .2em 0;
+}
+
+h2 {
+ font-size: 110%;
+ font-weight: normal;
+ margin: 0 0 .6em 0;
+}
+
+#feedTitleLink {
+ float: right;
+ margin-inline-start: .6em;
+ margin-inline-end: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+a[href] img {
+ border: none;
+}
+
+#feedTitleContainer {
+ margin-inline-start: 0;
+ margin-inline-end: .6em;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#feedTitleImage {
+ margin-inline-start: .6em;
+ margin-inline-end: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ max-width: 300px;
+ max-height: 150px;
+}
+
+.feedEntryContent {
+ font-size: 110%;
+}
+
+.lastUpdated {
+ font-size: 85%;
+ font-weight: normal;
+}
+
+.type-icon {
+ vertical-align: bottom;
+ height: 16px;
+ width: 16px;
+}
+
+.enclosures {
+ border: 1px solid #2D3B49;
+ padding: 1em;
+ margin: 1em auto;
+ background-color: #90A1B3;
+ color: #000000;
+}
+
+.enclosure {
+ vertical-align: middle;
+ margin-left: 2px;
+}
diff --git a/comm/suite/themes/modern/communicator/fullscreen-video.css b/comm/suite/themes/modern/communicator/fullscreen-video.css
new file mode 100644
index 0000000000..88d9c7b734
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/fullscreen-video.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+html,
+body,
+video {
+ height: 100%;
+}
+
+html {
+ background-color: black;
+}
+
+body {
+ margin: 0px;
+ overflow: -moz-hidden-unscrollable;
+ visibility: hidden;
+}
+
+video {
+ width: 100%;
+ max-height: 100%;
+}
+
+div {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ width: 32px;
+ height: 32px;
+ background: url("chrome://communicator/skin/icons/closeFullScreenVideo.png") center center no-repeat;
+}
diff --git a/comm/suite/themes/modern/communicator/helpviewer/help.css b/comm/suite/themes/modern/communicator/helpviewer/help.css
new file mode 100644
index 0000000000..00c54ef3b7
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/helpviewer/help.css
@@ -0,0 +1,144 @@
+/* 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/. */
+
+@import url("chrome://communicator/skin/");
+@import url("chrome://communicator/skin/sidebar/sidebar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar masthead ::::: */
+
+/* pick up modern's usual primary toolbar style */
+#HelpToolbar {
+ -moz-binding: url("chrome://communicator/skin/toolbar/toolbarBindings.xml#toolbar-primary");
+ background-color: #B1BDC9;
+}
+
+.toolbar-primary-holder {
+ -moz-box-align: start;
+ background-image: url("chrome://communicator/skin/toolbar/prtb-bg-noline.png");
+}
+
+.toolbar-primary-icon {
+ display: none;
+}
+
+#HelpToolbar > toolbarbutton,
+#HelpToolbar > toolbarbutton[type="menu-button"] > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-button
+{
+ -moz-box-orient: vertical;
+ -moz-box-pack: start;
+ border: none !important;
+ padding: 0px !important;
+}
+
+/* hide labels for the toolbar because not all of them are set in help viewer
+ make sure we always display the icon though */
+#HelpToolbar .toolbarbutton-text {
+ display: none;
+}
+
+#HelpToolbar .toolbarbutton-icon {
+ display: -moz-box;
+}
+
+#help-back-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+#help-forward-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker
+{
+ margin-top: 30px;
+ margin-bottom: 0px;
+ margin-inline-start: 34px;
+ margin-inline-end: 0px;
+}
+
+#help-sidebar {
+ min-width: 30px;
+ width: 20em;
+ max-width: 25em;
+ background-color: #BDC7D6;
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+toolbar toolbarbutton {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+}
+
+#help-back-button {
+ -moz-image-region: rect(78px 42px 117px 0);
+}
+
+#help-back-button:hover {
+ -moz-image-region: rect(78px 84px 117px 42px);
+}
+
+#help-back-button:hover:active {
+ -moz-image-region: rect(78px 126px 117px 84px);
+}
+
+#help-back-button[disabled="true"] {
+ -moz-image-region: rect(78px 168px 117px 126px) !important;
+}
+
+#help-forward-button {
+ -moz-image-region: rect(117px 42px 156px 0);
+}
+
+#help-forward-button:hover {
+ -moz-image-region: rect(117px 84px 156px 42px);
+}
+
+#help-forward-button:hover:active {
+ -moz-image-region: rect(117px 126px 156px 84px);
+}
+
+#help-forward-button[disabled="true"] {
+ -moz-image-region: rect(117px 168px 156px 126px) !important;
+}
+
+#help-home-button {
+ -moz-image-region: rect(156px 42px 195px 0);
+}
+
+#help-home-button:hover {
+ -moz-image-region: rect(156px 84px 195px 42px);
+}
+
+#help-home-button:hover:active {
+ -moz-image-region: rect(156px 126px 195px 84px);
+}
+
+#help-home-button[disabled="true"] {
+ -moz-image-region: rect(156px 168px 195px 126px) !important;
+}
+
+#help-print-button {
+ -moz-image-region: rect(0 42px 39px 0);
+}
+
+#help-print-button:hover {
+ -moz-image-region: rect(0 84px 39px 42px);
+}
+
+#help-print-button:hover:active {
+ -moz-image-region: rect(0 126px 39px 84px);
+}
+
+#help-print-button[disabled="true"] {
+ -moz-image-region: rect(0 168px 39px 126px) !important;
+}
+
+/* ::::: OTHER ::::: */
+
+#context-copy[disabled="true"] {
+ display: none;
+}
+
+/* make findbar appear above content */
+#appcontent {
+ -moz-box-direction: reverse;
+}
diff --git a/comm/suite/themes/modern/communicator/icons/alwaysAsk.png b/comm/suite/themes/modern/communicator/icons/alwaysAsk.png
new file mode 100644
index 0000000000..e367e999f2
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/alwaysAsk.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/application.png b/comm/suite/themes/modern/communicator/icons/application.png
new file mode 100644
index 0000000000..23357e2764
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/application.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/btn1.png b/comm/suite/themes/modern/communicator/icons/btn1.png
new file mode 100644
index 0000000000..3179c8c430
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/btn1.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/closeFullScreenVideo.png b/comm/suite/themes/modern/communicator/icons/closeFullScreenVideo.png
new file mode 100644
index 0000000000..6090eab39f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/closeFullScreenVideo.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/common-small.png b/comm/suite/themes/modern/communicator/icons/common-small.png
new file mode 100644
index 0000000000..0fdc64eecb
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/common-small.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/common.png b/comm/suite/themes/modern/communicator/icons/common.png
new file mode 100644
index 0000000000..908da42153
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/common.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/feedIcon.png b/comm/suite/themes/modern/communicator/icons/feedIcon.png
new file mode 100644
index 0000000000..848a3bac91
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/feedIcon.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/feedIcon16.png b/comm/suite/themes/modern/communicator/icons/feedIcon16.png
new file mode 100644
index 0000000000..0286dffab9
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/feedIcon16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/geo.png b/comm/suite/themes/modern/communicator/icons/geo.png
new file mode 100644
index 0000000000..2c1b2ecfba
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/geo.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/geolocation-16.png b/comm/suite/themes/modern/communicator/icons/geolocation-16.png
new file mode 100644
index 0000000000..082b177811
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/geolocation-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/geolocation-64.png b/comm/suite/themes/modern/communicator/icons/geolocation-64.png
new file mode 100644
index 0000000000..6e09ab9c32
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/geolocation-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/identity.png b/comm/suite/themes/modern/communicator/icons/identity.png
new file mode 100644
index 0000000000..dc35c2c6fe
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/identity.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/loading.png b/comm/suite/themes/modern/communicator/icons/loading.png
new file mode 100644
index 0000000000..907e55fb37
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/loading.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/lock-broken-16.png b/comm/suite/themes/modern/communicator/icons/lock-broken-16.png
new file mode 100644
index 0000000000..8ba5d729a1
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/lock-broken-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/lock-broken.png b/comm/suite/themes/modern/communicator/icons/lock-broken.png
new file mode 100644
index 0000000000..71c3aa8191
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/lock-broken.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/lock-insecure-16.png b/comm/suite/themes/modern/communicator/icons/lock-insecure-16.png
new file mode 100644
index 0000000000..ef95c734e7
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/lock-insecure-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/lock-insecure.png b/comm/suite/themes/modern/communicator/icons/lock-insecure.png
new file mode 100644
index 0000000000..a6b0842e57
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/lock-insecure.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/lock-secure-16.png b/comm/suite/themes/modern/communicator/icons/lock-secure-16.png
new file mode 100644
index 0000000000..f153751872
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/lock-secure-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/lock-secure.png b/comm/suite/themes/modern/communicator/icons/lock-secure.png
new file mode 100644
index 0000000000..178499e002
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/lock-secure.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/notification-16.png b/comm/suite/themes/modern/communicator/icons/notification-16.png
new file mode 100644
index 0000000000..9e61a63973
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/notification-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/notification-64.png b/comm/suite/themes/modern/communicator/icons/notification-64.png
new file mode 100644
index 0000000000..a01d0ab776
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/notification-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/notification-icons.svg b/comm/suite/themes/modern/communicator/icons/notification-icons.svg
new file mode 100644
index 0000000000..cc5d4f7ba1
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/notification-icons.svg
@@ -0,0 +1,104 @@
+<!-- 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/. -->
+<svg fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg"
+ width="32" height="32" viewBox="0 0 32 32">
+ <style>
+ :root > use:not(:target),
+ :root > g:not(:target),
+ :root > circle:not(:target),
+ #strikeout {
+ display: none;
+ }
+ .blocked:target ~ #strikeout {
+ display: block;
+ }
+ .blocked {
+ clip-path: url(#blocked-clipPath);
+ }
+
+ #badge {
+ fill: #3088d4;
+ }
+
+ #update-icon {
+ stroke: #fff;
+ stroke-width: 3px;
+ stroke-linecap: round;
+ }
+ </style>
+
+ <defs>
+ <path id="camera-icon" d="m 2,23 a 3,3 0 0 0 3,3 l 14,0 a 3,3 0 0 0 3,-3 l 0,-4 6,5.5 c 0.5,0.5 1,0.7 2,0.5 l 0,-18 c -1,-0.2 -1.5,0 -2,0.5 l -6,5.5 0,-4 a 3,3 0 0 0 -3,-3 l -14,0 a 3,3 0 0 0 -3,3 z" />
+ <path id="desktop-notification-icon" d="m 2,20 a 4,4 0 0 0 4,4 l 13,0 7,7 0,-7 a 4,4 0 0 0 4,-4 l 0,-12 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 5,-2 a 1,1 0 1 1 0,-2 l 10,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 14,0 a 1,1 0 1 1 0,2 z m 0,-4 a 1,1 0 1 1 0,-2 l 18,0 a 1,1 0 1 1 0,2 z" />
+ <path id="focus-tab-by-prompt-icon" d="M29.43,25,18.57,3.8A2.92,2.92,0,0,0,16,2a2.92,2.92,0,0,0-2.57,1.8L2.57,25a3.47,3.47,0,0,0,0,3.4A3.15,3.15,0,0,0,5.33,30H26.66a3.15,3.15,0,0,0,2.77-1.6A3.47,3.47,0,0,0,29.43,25ZM16,7.2a2.3,2.3,0,0,1,2.37,2.4L18,18a1.88,1.88,0,0,1-2,2,1.88,1.88,0,0,1-2-2l-.4-8.4A2.3,2.3,0,0,1,16,7.2ZM16,28a3,3,0,0,1,0-6,3,3,0,0,1,0,6Z"/>
+ <path id="geo-linux-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 4,2.1 a 10,10 0 0 0 8,8 l 0,-4 4,0 0,4 a 10,10 0 0 0 8,-8 l -4,0 0,-4 4,0 a 10,10 0 0 0 -8,-8 l 0,4 -4,0 0,-4 a 10,10 0 0 0 -8,8 l 4,0 0,4 z" />
+ <path id="geo-linux-detailed-icon" d="m 2,15.9 a 14,14 0 1 1 0,0.2 z m 3,2.1 a 11,11 0 0 0 9,9 l 1,-5 2,0 1,5 a 11,11 0 0 0 9,-9 l -5,-1 0,-2 5,-1 a 11,11 0 0 0 -9,-9 l -1,5 -2,0 -1,-5 a 11,11 0 0 0 -9,9 l 5,1 0,2 z" />
+ <path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z" />
+ <path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z" />
+ <path id="geo-windows-detailed-icon" d="m 2,14.5 0,3 2,0.5 a 12,12 0 0 0 10,10 l 0.5,2 3,0 0.5,-2 a 12,12 0 0 0 10,-10 l 2,-0.5 0,-3 -2,-0.5 a 12,12 0 0 0 -10,-10 l -0.5,-2 -3,0 -0.5,2 a 12,12 0 0 0 -10,10 z m 4,1.4 a 10,10 0 1 1 0,0.2 z m 3,0 a 7,7 0 1 1 0,0.2 z" />
+ <path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z" />
+ <path id="login-icon" d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z" />
+ <path id="login-detailed-icon" d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z" />
+ <path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
+ <path id="microphone-detailed-icon" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
+ <path id="persistent-storage-icon" d="M26 21.1H6c-1.1 0-2 .9-2 2V27c0 1.1.9 2 2 2h20c1.1 0 2-.9 2-2v-3.9c0-1.1-.9-2-2-2zM24.1 27c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM25 3H7C5.3 3 4 4.4 4 6.2v13.3c.6-.3 1.3-.5 2-.5h20c.7 0 1.4.2 2 .5V6.2C28 4.4 26.7 3 25 3z"/>
+ <path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z" />
+ <path id="popup-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
+ <path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
+ <path id="update-icon" d="M 16,9 L 16,24 M 16,9 L 11,14 M 16,9 L 21,14" />
+
+ <clipPath id="blocked-clipPath">
+ <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
+ </clipPath>
+
+ <mask id="i-mask" style="fill-opacity: 1;">
+ <rect fill="white" width="32" height="32"/>
+ <circle fill="black" cx="16" cy="9" r="2.5"/>
+ <rect fill="black" x="14" y="14" width="4" height="10" rx="2" ry="2"/>
+ </mask>
+ </defs>
+
+ <g id="default-info">
+ <circle cx="16" cy="16" r="14" mask="url(#i-mask)"/>
+ </g>
+
+ <use id="camera" href="#camera-icon" />
+ <use id="camera-sharing" href="#camera-icon"/>
+ <use id="camera-indicator" href="#camera-icon" />
+ <use id="camera-blocked" class="blocked" href="#camera-icon" />
+ <use id="desktop-notification" href="#desktop-notification-icon" />
+ <use id="desktop-notification-blocked" class="blocked" href="#desktop-notification-icon" />
+ <use id="focus-tab-by-prompt" href="#focus-tab-by-prompt-icon" />
+ <use id="geo-osx" href="#geo-osx-icon" />
+ <use id="geo-osx-blocked" class="blocked" href="#geo-osx-icon" />
+ <use id="geo-linux" href="#geo-linux-icon" />
+ <use id="geo-linux-blocked" class="blocked" href="#geo-linux-icon" />
+ <use id="geo-linux-detailed" href="#geo-linux-detailed-icon" />
+ <use id="geo-windows" href="#geo-windows-icon" />
+ <use id="geo-windows-blocked" class="blocked" href="#geo-windows-icon" />
+ <use id="geo-windows-detailed" href="#geo-windows-detailed-icon" />
+ <use id="indexedDB" href="#indexedDB-icon" />
+ <use id="indexedDB-blocked" class="blocked" href="#indexedDB-icon" />
+ <use id="login" href="#login-icon" />
+ <use id="login-detailed" href="#login-detailed-icon" />
+ <use id="microphone" href="#microphone-icon" />
+ <use id="microphone-sharing" href="#microphone-icon"/>
+ <use id="microphone-indicator" href="#microphone-icon"/>
+ <use id="microphone-blocked" class="blocked" href="#microphone-icon" />
+ <use id="microphone-detailed" href="#microphone-detailed-icon" />
+ <use id="persistent-storage" href="#persistent-storage-icon" />
+ <use id="persistent-storage-blocked" class="blocked" href="#persistent-storage-icon" />
+ <use id="plugin" href="#plugin-icon" />
+ <use id="plugin-blocked" class="blocked" href="#plugin-icon" />
+ <use id="plugin-badge" href="#badge" />
+ <use id="popup" href="#popup-icon" />
+ <use id="screen" href="#screen-icon" />
+ <use id="screen-sharing" href="#screen-icon"/>
+ <use id="screen-indicator" href="#screen-icon"/>
+ <use id="screen-blocked" class="blocked" href="#screen-icon" />
+ <use id="update" href="#update-icon" />
+
+ <path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
+ <circle id="badge" cx="27" cy="5" r="5"/>
+</svg>
diff --git a/comm/suite/themes/modern/communicator/icons/offline.png b/comm/suite/themes/modern/communicator/icons/offline.png
new file mode 100644
index 0000000000..3f90a8b946
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/offline.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/online.png b/comm/suite/themes/modern/communicator/icons/online.png
new file mode 100644
index 0000000000..f1178c95ba
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/online.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/save.png b/comm/suite/themes/modern/communicator/icons/save.png
new file mode 100644
index 0000000000..46bb519033
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/save.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-cool.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-cool.png
new file mode 100644
index 0000000000..1eee4586e5
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-cool.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-cry.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-cry.png
new file mode 100644
index 0000000000..3c25cb658d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-cry.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-embarrassed.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-embarrassed.png
new file mode 100644
index 0000000000..5088e28e07
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-embarrassed.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-foot.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-foot.png
new file mode 100644
index 0000000000..d1589f3696
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-foot.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-frown.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-frown.png
new file mode 100644
index 0000000000..159c04b0c6
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-frown.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-innocent.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-innocent.png
new file mode 100644
index 0000000000..85cbcf67ed
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-innocent.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-kiss.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-kiss.png
new file mode 100644
index 0000000000..36fbf51dde
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-kiss.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-laughing.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-laughing.png
new file mode 100644
index 0000000000..0d013d5012
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-laughing.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-money.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-money.png
new file mode 100644
index 0000000000..59f089944f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-money.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-sealed.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-sealed.png
new file mode 100644
index 0000000000..c4f3042810
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-sealed.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-smile.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-smile.png
new file mode 100644
index 0000000000..de862b15db
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-smile.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-surprise.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-surprise.png
new file mode 100644
index 0000000000..4b4d423a55
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-surprise.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-tongue.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-tongue.png
new file mode 100644
index 0000000000..9f53727055
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-tongue.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-undecided.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-undecided.png
new file mode 100644
index 0000000000..8311076c53
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-undecided.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-wink.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-wink.png
new file mode 100644
index 0000000000..e2db57ec39
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-wink.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/icons/smileys/smiley-yell.png b/comm/suite/themes/modern/communicator/icons/smileys/smiley-yell.png
new file mode 100644
index 0000000000..2ae846d201
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/icons/smileys/smiley-yell.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/numberbox.css b/comm/suite/themes/modern/communicator/numberbox.css
new file mode 100644
index 0000000000..f171ec15e8
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/numberbox.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/. */
+
+/* ===== numberbox.css ==================================================
+ == Styles used by the XUL textbox type="number" element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+textbox[type="number"] {
+ padding: 0 !important;
+ cursor: default;
+}
+
+html|*.textbox-input {
+ text-align: right;
+}
+
+.numberbox-input-box {
+ -moz-box-align: center;
+ padding: 1px;
+ margin: 0;
+}
diff --git a/comm/suite/themes/modern/communicator/places/allBookmarks.png b/comm/suite/themes/modern/communicator/places/allBookmarks.png
new file mode 100644
index 0000000000..d2dfbf0e07
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/allBookmarks.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmark-folder-closed.png b/comm/suite/themes/modern/communicator/places/bookmark-folder-closed.png
new file mode 100644
index 0000000000..ce200e4945
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark-folder-closed.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmark-folder-dis.png b/comm/suite/themes/modern/communicator/places/bookmark-folder-dis.png
new file mode 100644
index 0000000000..aab45d465d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark-folder-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmark-folder-open.png b/comm/suite/themes/modern/communicator/places/bookmark-folder-open.png
new file mode 100644
index 0000000000..f2fb7fc684
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark-folder-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmark-item-dis.png b/comm/suite/themes/modern/communicator/places/bookmark-item-dis.png
new file mode 100644
index 0000000000..8731d51571
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark-item-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmark-item-updated.png b/comm/suite/themes/modern/communicator/places/bookmark-item-updated.png
new file mode 100644
index 0000000000..80e390f028
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark-item-updated.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmark-item.svg b/comm/suite/themes/modern/communicator/places/bookmark-item.svg
new file mode 100644
index 0000000000..e6bf57ce2a
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark-item.svg
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<image xlink:href="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAP1BMVEWYAgBSXmbr6+udqbMD
+Dh/GzNa8w8u/x83S1+Di6esSHSgpMT09R1BMVmBhbHWrtL6vusCzvMfL1NvV3eTd4+rDkTEb
+AAAAAXRSTlMAQObYZgAAAEZJREFUeNqNzjcCADEMAkEhJIfL6f9vvRpX3o6pMJsJgG53h0Kk
+CHpvVYVfFpGDb94iO1u9RDY+5RRZmdA/C2P4GyNY2Fw/rHYBQhkHlJYAAAAASUVORK5CYII="
+width="16" height="16"/></svg>
diff --git a/comm/suite/themes/modern/communicator/places/bookmark.png b/comm/suite/themes/modern/communicator/places/bookmark.png
new file mode 100644
index 0000000000..6b31e70c18
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmark.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmarks.css b/comm/suite/themes/modern/communicator/places/bookmarks.css
new file mode 100644
index 0000000000..456d563b8c
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmarks.css
@@ -0,0 +1,123 @@
+/* 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/. */
+
+/* ===== bookmarks.css ==================================================
+ == Styles specific to widgets containing bookmarks.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: bookmark items ::::: */
+
+treechildren::-moz-tree-image(Name),
+treechildren::-moz-tree-image(title) {
+ margin-inline-end: 2px;
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item,
+treechildren::-moz-tree-image(title) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.bookmark-item[container="true"],
+treechildren::-moz-tree-image(title, container) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
+.bookmark-item[open="true"],
+treechildren::-moz-tree-image(title, open) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-open.png");
+}
+
+.bookmark-item[container="true"][open="true"][loading="true"] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png") !important;
+}
+
+.bookmark-item > .toolbarbutton-box > .toolbarbutton-icon {
+ list-style-image: inherit;
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item[container][livemark],
+treechildren::-moz-tree-image(title, container, livemark) {
+ list-style-image: url("chrome://communicator/skin/places/livemark-folder.png");
+ -moz-image-region: auto;
+}
+
+.bookmark-item[container][livemark] .bookmark-item,
+treechildren::-moz-tree-image(title, livemarkItem) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-updated.png");
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited],
+treechildren::-moz-tree-image(title, livemarkItem, visited) {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+#bookmarksToolbarFolderMenu,
+#BMB_bookmarksToolbarFolderMenu,
+treechildren::-moz-tree-image(container, queryFolder_toolbar_____) {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(container, queryFolder_menu________) {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksMenu.png");
+ -moz-image-region: auto;
+}
+
+#unsortedBookmarksFolderMenu,
+#BMB_unsortedBookmarksFolderMenu,
+treechildren::-moz-tree-image(container, queryFolder_unfiled_____) {
+ list-style-image: url("chrome://communicator/skin/places/unsortedBookmarks.png");
+ -moz-image-region: auto;
+}
+
+/* query-nodes should be styled even if they're not expandable */
+.bookmark-item[container][query],
+treechildren::-moz-tree-image(query) {
+ list-style-image: url("chrome://communicator/skin/places/query.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(query, OrganizerQuery_AllBookmarks) {
+ list-style-image: url("chrome://communicator/skin/places/allBookmarks.png");
+ -moz-image-region: auto;
+}
+
+.bookmark-item[query][tagContainer],
+treechildren::-moz-tree-image(title, query, tagContainer),
+treechildren::-moz-tree-image(query, OrganizerQuery_Tags) {
+ list-style-image: url("chrome://communicator/skin/places/tag.png");
+ -moz-image-region: auto;
+}
+
+/* calendar icon for folders grouping items by day */
+treechildren::-moz-tree-image(title, query, dayContainer) {
+ list-style-image: url("chrome://communicator/skin/places/calendar.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(query, OrganizerQuery_History) {
+ list-style-image: url("chrome://communicator/skin/places/history.png");
+ -moz-image-region: auto;
+}
+
+treechildren::-moz-tree-image(title, separator) {
+ list-style-image: none;
+ width: 0px;
+ height: 0px;
+}
+
+treechildren::-moz-tree-cell-text(title, separator) {
+ color: #808080;
+ margin: 0px 5px;
+}
+
+treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
+ color: #FFFFFF;
+}
diff --git a/comm/suite/themes/modern/communicator/places/bookmarksMenu.png b/comm/suite/themes/modern/communicator/places/bookmarksMenu.png
new file mode 100644
index 0000000000..a1b5ae590f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmarksMenu.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/bookmarksToolbar.css b/comm/suite/themes/modern/communicator/places/bookmarksToolbar.css
new file mode 100644
index 0000000000..d80e87c117
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmarksToolbar.css
@@ -0,0 +1,103 @@
+/* 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/. */
+
+/* ===== bookmarksToolbar.css ===========================================
+ == Styles specific to bookmark items in a toolbar.
+ ======================================================================= */
+
+@import url("chrome://communicator/skin/places/bookmarks.css");
+
+/* ::::: bookmark toolbar buttons ::::: */
+
+/* ..... bookmark items ..... */
+
+toolbarbutton.bookmark-item {
+ cursor: pointer;
+ min-width: 0;
+ max-width: 10em;
+}
+
+toolbarbutton.bookmark-item:hover {
+ text-decoration: underline;
+}
+
+toolbarbutton.bookmark-item[container="true"],
+toolbarbutton.bookmark-item[container="true"]:hover {
+ cursor: default;
+ text-decoration: none;
+}
+
+/* removes border from non-container bookmark items */
+toolbarbutton.bookmark-item:not([container="true"]):hover,
+toolbarbutton.bookmark-item:not([container="true"]):hover:active {
+ border: 1px solid transparent;
+}
+
+toolbarbutton.bookmark-item[disabled="true"] {
+ cursor: default !important;
+ text-decoration: none !important;
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png") !important;
+}
+
+toolbarbutton.bookmark-item[disabled="true"][container="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-dis.png") !important;
+}
+
+.bookmark-item > .toolbarbutton-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.bookmark-item > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+/* Prevent [mode="icons"] from hiding the label */
+.bookmark-item > .toolbarbutton-text {
+ display: -moz-box !important;
+}
+
+/* ..... drag and drop styles ..... */
+
+#PlacesToolbarDropIndicator {
+ list-style-image: url("chrome://communicator/skin/places/toolbarDropMarker.png");
+}
+
+toolbarbutton.bookmark-item[dragover="true"][open="true"] {
+ border: 1px solid #000000;
+}
+
+/* ::::: bookmark menus ::::: */
+
+menu.bookmark-item,
+menuitem.bookmark-item {
+ border-top: 1px solid transparent !important;
+ border-bottom: 1px solid transparent !important;
+ padding: 1px 2px;
+}
+
+menuitem.bookmark-item[disabled="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png");
+}
+
+menuitem.bookmark-item[disabled="true"][container="true"] {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-dis.png");
+}
+
+/* ..... drag and drop styles ..... */
+
+/* rules for menupopup drop indicators */
+.menupopup-drop-indicator-bar {
+ position: relative;
+ /* these two margins must together compensate the indicator's height */
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+.menupopup-drop-indicator {
+ list-style-image: none;
+ height: 2px;
+ margin-inline-end: -4em;
+ background-color: #424F63;
+}
diff --git a/comm/suite/themes/modern/communicator/places/bookmarksToolbar.png b/comm/suite/themes/modern/communicator/places/bookmarksToolbar.png
new file mode 100644
index 0000000000..c37b51e25a
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/bookmarksToolbar.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/calendar.png b/comm/suite/themes/modern/communicator/places/calendar.png
new file mode 100644
index 0000000000..666f6295f1
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/calendar.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/editBookmarkOverlay.css b/comm/suite/themes/modern/communicator/places/editBookmarkOverlay.css
new file mode 100644
index 0000000000..b685bedd1a
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/editBookmarkOverlay.css
@@ -0,0 +1,75 @@
+/* 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/. */
+
+/**** folder menulist ****/
+.folder-icon > .menulist-label-box > .menulist-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.folder-icon > .menu-iconic-left {
+ display: -moz-box;
+}
+
+.folder-icon {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-folder-closed.png") !important;
+}
+
+
+/**** expanders ****/
+
+.expander-up,
+.expander-down {
+ min-width: 0;
+ padding: 2px 0;
+ padding-inline-start: 2px;
+}
+
+.expander-up {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.expander-down {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+#editBookmarkPanelContent {
+ min-width: 23em;
+ width: 32em;
+}
+
+#editBMPanel_folderTree {
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+/* Hide the value column of the tag autocomplete popup
+ * leaving only the comment column visible. This is
+ * so that only the tag being edited is shown in the
+ * popup.
+ */
+#editBMPanel_tagsField #treecolAutoCompleteValue {
+ visibility: collapse;
+}
+
+
+/* ::::: bookmark panel dropdown icons ::::: */
+
+#editBMPanel_folderMenuList[selectedIndex="0"],
+#editBMPanel_toolbarFolderItem {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png") !important;
+ -moz-image-region: auto !important;
+}
+
+#editBMPanel_folderMenuList[selectedIndex="1"],
+#editBMPanel_bmRootItem {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksMenu.png") !important;
+ -moz-image-region: auto !important;
+}
+
+#editBMPanel_folderMenuList[selectedIndex="2"],
+#editBMPanel_unfiledRootItem {
+ list-style-image: url("chrome://communicator/skin/places/unsortedBookmarks.png") !important;
+ -moz-image-region: auto !important;
+}
diff --git a/comm/suite/themes/modern/communicator/places/history.png b/comm/suite/themes/modern/communicator/places/history.png
new file mode 100644
index 0000000000..fcc89bbbf0
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/history.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/livemark-folder.png b/comm/suite/themes/modern/communicator/places/livemark-folder.png
new file mode 100644
index 0000000000..c76bf3abb8
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/livemark-folder.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/organizer.css b/comm/suite/themes/modern/communicator/places/organizer.css
new file mode 100644
index 0000000000..86641cbb74
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/organizer.css
@@ -0,0 +1,23 @@
+/* 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/. */
+
+
+#placesToolbar {
+ /* match menubar background */
+ background-color: #DDE3EB;
+}
+
+/* Info box */
+#detailsDeck {
+ margin: 5px;
+}
+
+#infoBoxExpanderLabel {
+ padding-inline-start: 2px;
+}
+
+#organizerScopeBar {
+ padding: 2px 0;
+ padding-inline-end: 3px;
+}
diff --git a/comm/suite/themes/modern/communicator/places/query.png b/comm/suite/themes/modern/communicator/places/query.png
new file mode 100644
index 0000000000..bd63cfebf3
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/query.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/tag.png b/comm/suite/themes/modern/communicator/places/tag.png
new file mode 100644
index 0000000000..95b4ee8656
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/tag.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/toolbarDropMarker.png b/comm/suite/themes/modern/communicator/places/toolbarDropMarker.png
new file mode 100644
index 0000000000..d0e143c36f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/toolbarDropMarker.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/places/unsortedBookmarks.png b/comm/suite/themes/modern/communicator/places/unsortedBookmarks.png
new file mode 100644
index 0000000000..2f0926a71c
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/places/unsortedBookmarks.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/preferences.css b/comm/suite/themes/modern/communicator/preferences.css
new file mode 100644
index 0000000000..bfa001fea6
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/preferences.css
@@ -0,0 +1,18 @@
+/* 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/. */
+
+/* Styles used by all preference windows and panes of SeaMonkey */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Main Window ::::: */
+
+prefwindow {
+ padding: 7px 5px 5px;
+}
+
+prefpane,
+.prefWindow-dlgbuttons {
+ padding: 0px;
+}
diff --git a/comm/suite/themes/modern/communicator/prefpanels.css b/comm/suite/themes/modern/communicator/prefpanels.css
new file mode 100644
index 0000000000..0ef6482b84
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/prefpanels.css
@@ -0,0 +1,117 @@
+/* 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/. */
+
+/* ==== prefpanels.css ==================================================
+ == Styles used by all preference panels in the Communicator suite.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Fonts ::::: */
+
+#sizeVar,
+#sizeMono {
+ width: 4em;
+}
+
+.prefpanel-font-list {
+ -moz-box-flex: 1;
+}
+
+/* checkbox which is disabled for changes but non-gray text (i.e., in effect) */
+
+checkbox.nogray-disabled[disabled="true"][nogray="true"]
+ > .checkbox-label-box > .checkbox-label {
+ color: inherit !important;
+}
+
+/* ::::: Applications ::::: */
+/**
+ * Line up the actions menu with action labels above and below it.
+ * Equalize the distance from the left side of the action box to the left side
+ * of the icon for both the menu and the non-menu versions of the action box.
+ * Also make sure the labels are the same distance away from the icons.
+ */
+
+.handler-action,
+.handler-type {
+ padding: 0;
+}
+
+.handler-action .listcell-icon,
+.handler-type .listcell-icon {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ margin-inline-start: 7px;
+ margin-inline-end: 2px;
+ height: 16px;
+ width: 16px;
+}
+
+.handler-action .listcell-label,
+.handler-type .listcell-label {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-inline-start: 0px;
+ padding-inline-end: 5px;
+}
+
+.actionsMenu {
+ margin: 0;
+}
+
+.actionsMenu > .menulist-label-box {
+ padding-inline-start: 0;
+}
+
+.actionsMenu > .menulist-label-box > .menulist-icon {
+ margin: 0 2px;
+ height: 16px;
+ width: 16px;
+}
+
+.handler-action > .menu-iconic-left > .menu-iconic-icon {
+ margin-inline-start: 6px;
+}
+
+/* Set icons on app pane elements */
+
+.handler-action[appHandlerIcon="app"] {
+ list-style-image: url("chrome://communicator/skin/icons/application.png");
+}
+
+.handler-action[appHandlerIcon="ask"] {
+ list-style-image: url("chrome://communicator/skin/icons/alwaysAsk.png");
+}
+
+.handler-action[appHandlerIcon="save"] {
+ list-style-image: url("chrome://communicator/skin/icons/save.png");
+}
+
+.handler-action[appHandlerIcon="feed"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+.handler-type[typeClass="unknown"] {
+ list-style-image: url("moz-icon://goat?size=16");
+}
+
+.handler-type[typeClass="webFeed"],
+.handler-type[typeClass="videoPodcastFeed"],
+.handler-type[typeClass="audioPodcastFeed"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+/* ::::: Search ::::: */
+
+#engineList > .menulist-label-box > .menulist-icon {
+ height: 16px;
+ width: 16px;
+}
+
+/* ::::: Sync ::::: */
+
+#syncDesc {
+ padding: 0 12em;
+}
diff --git a/comm/suite/themes/modern/communicator/profile/migrate.png b/comm/suite/themes/modern/communicator/profile/migrate.png
new file mode 100644
index 0000000000..5ce2f08cb5
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/profile/migrate.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/profile/profile.css b/comm/suite/themes/modern/communicator/profile/profile.css
new file mode 100644
index 0000000000..b86851d0d3
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/profile/profile.css
@@ -0,0 +1,68 @@
+/* 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/. */
+
+/* ===== profile.css ====================================================
+ == Styles for Profile Manager window and dialogs.
+ ======================================================================= */
+
+@import url("chrome://global/skin/global.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Profile Selection dialog ::::: */
+
+treechildren::-moz-tree-image {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://communicator/skin/profile/profile.png");
+}
+
+treechildren::-moz-tree-image(rowMigrate-no) {
+ list-style-image: url("chrome://communicator/skin/profile/migrate.png");
+}
+
+/* Override global.css */
+hbox.wizard-box {
+ padding: 10px 10px 10px 10px;
+}
+
+#header {
+ -moz-box-orient: vertical;
+ margin-top: -7px;
+ margin-bottom: 5px;
+ margin-inline-start: -7px;
+ margin-inline-end: -5px;
+ border-left: none;
+ border-right: none;
+ border-top: none;
+ border-bottom-width: 1px;
+ border-bottom-color: #000000;
+ padding-top: 12px;
+ padding-bottom: 12px;
+ padding-inline-start: 25px;
+ padding-inline-end: 5px;
+ background-color: #90A1B3;
+ color: #000000;
+}
+
+#header > .dialogheader-title {
+ font: inherit;
+ font-weight: bold;
+}
+
+#header > .dialogheader-description {
+ margin-inline-start: 12px !important;
+}
+
+#intro,
+#label {
+ width: 17em;
+}
+
+#managebuttons > button {
+ min-width: 8em;
+}
+
+#finishtext {
+ margin-top: -20px;
+}
diff --git a/comm/suite/themes/modern/communicator/profile/profile.png b/comm/suite/themes/modern/communicator/profile/profile.png
new file mode 100644
index 0000000000..208c9a0b52
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/profile/profile.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sanitizeDialog.css b/comm/suite/themes/modern/communicator/sanitizeDialog.css
new file mode 100644
index 0000000000..0c0adaf26d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sanitizeDialog.css
@@ -0,0 +1,56 @@
+/* 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/. */
+
+#sanitizeDurationChoice {
+ margin-inline-end: 0;
+}
+
+/* Align the duration label with the warning box and item list */
+#sanitizeDurationLabel {
+ margin-inline-start: 3px;
+}
+
+
+/* Hide the duration dropdown suffix label if it's empty. Otherwise it
+ takes up a little space, causing the end of the dropdown to not be aligned
+ with the warning box. */
+#sanitizeDurationSuffixLabel[value=""] {
+ display: none;
+}
+
+
+/* Sanitize everything warning box */
+#sanitizeWarningBox {
+ background-color: Window;
+ border: 1px solid #494F5D;
+ border-radius: 5px;
+ padding: 16px;
+}
+
+#sanitizeWarningIcon {
+ list-style-image: url("chrome://global/skin/icons/warning-48.png");
+ padding: 0;
+ margin: 0;
+}
+
+#sanitizeWarningDescBox {
+ padding: 0 16px;
+ margin: 0;
+}
+
+
+/* Make the item list the same width as the warning box */
+#itemList {
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+}
+
+
+/* Align the last dialog button with the end of the warning box */
+.dialog-button-box {
+ margin-inline-end: 0;
+}
+.dialog-button[dlgtype="cancel"] {
+ margin-inline-end: 0;
+}
diff --git a/comm/suite/themes/modern/communicator/search/engineManager.css b/comm/suite/themes/modern/communicator/search/engineManager.css
new file mode 100644
index 0000000000..53cf96f9d1
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/search/engineManager.css
@@ -0,0 +1,14 @@
+/* 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/. */
+
+#engineChildren::-moz-tree-image(engineName) {
+ margin-inline-end: 4px;
+ margin-inline-start: 1px;
+ width: 16px;
+ height: 16px;
+}
+
+#engineChildren::-moz-tree-row {
+ height: 20px;
+}
diff --git a/comm/suite/themes/modern/communicator/search/mainwindow-dropdown-arrow.png b/comm/suite/themes/modern/communicator/search/mainwindow-dropdown-arrow.png
new file mode 100644
index 0000000000..a556f6e046
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/search/mainwindow-dropdown-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/search/search.css b/comm/suite/themes/modern/communicator/search/search.css
new file mode 100755
index 0000000000..05d79ccb7c
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/search/search.css
@@ -0,0 +1,18 @@
+/* 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/. */
+
+/* ===== search.css =====================================================
+ == Styles specific to the Search sidebar panel.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.autocomplete-treebody::-moz-tree-cell(datalist-first) {
+ border-top: 1px solid #999999;
+}
+
+#sidebar-search-engines > .menulist-label-box > .menulist-icon {
+ height: 16px;
+ width: 16px;
+}
diff --git a/comm/suite/themes/modern/communicator/search/searchbar.css b/comm/suite/themes/modern/communicator/search/searchbar.css
new file mode 100644
index 0000000000..92ba634510
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/search/searchbar.css
@@ -0,0 +1,62 @@
+/* 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/. */
+
+.searchbar-textbox {
+ width: 6em;
+ min-width: 6em;
+}
+
+/* ::::: search bar ::::: */
+
+.autocomplete-treebody::-moz-tree-cell-text(datalist-first, treecolAutoCompleteComment) {
+ color: #999999;
+ font-size: smaller;
+}
+
+.autocomplete-treebody::-moz-tree-cell(datalist-first) {
+ border-top: 1px solid #999999;
+}
+
+/* ::::: searchbar-engine-button ::::: */
+
+.toolbarbutton-icon {
+ height: 16px;
+ width: 16px;
+ list-style-image: url("chrome://communicator/skin/directory/file-icon.png");
+}
+
+.toolbarbutton-menu-dropmarker {
+ list-style-image: url("chrome://communicator/skin/search/mainwindow-dropdown-arrow.png");
+ -moz-image-region: rect(0, 13px, 11px, 0);
+}
+
+.searchbar-engine-button[open="true"] > .toolbarbutton-menu-dropmarker {
+ -moz-image-region: rect(0, 26px, 11px, 13px);
+}
+
+
+/* ::::: search-go-button ::::: */
+
+.search-go-container {
+ -moz-box-align: center;
+}
+
+.search-go-button {
+ padding: 0px 2px;
+ list-style-image: url("chrome://global/skin/icons/search.png");
+}
+
+@media (-moz-touch-enabled) {
+ .search-go-button {
+ padding: 0px 5px;
+ }
+}
+
+.search-go-button:-moz-locale-dir(rtl) {
+ transform: scaleX(-1);
+}
+
+.searchbar-engine-menuitem[selected="true"] > .menu-iconic-text {
+ font-weight: bold;
+}
diff --git a/comm/suite/themes/modern/communicator/sidebar/customize.css b/comm/suite/themes/modern/communicator/sidebar/customize.css
new file mode 100644
index 0000000000..d1626f7156
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/customize.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+#other-panels {
+ min-width: 180px;
+}
diff --git a/comm/suite/themes/modern/communicator/sidebar/preview.css b/comm/suite/themes/modern/communicator/sidebar/preview.css
new file mode 100644
index 0000000000..a1123226db
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/preview.css
@@ -0,0 +1,19 @@
+/* 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/. */
+
+/* ===== preview.css ====================================================
+ == Styles used by the Sidebar Preview dialog.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#previewframe {
+ margin: 0px;
+ border: none;
+ width: 162px;
+ height: 300px;
+ min-width: 10px;
+ min-height: 1px;
+}
+
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbar-top-tabopen.png b/comm/suite/themes/modern/communicator/sidebar/sbar-top-tabopen.png
new file mode 100644
index 0000000000..c3304f310f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbar-top-tabopen.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbar-top.png b/comm/suite/themes/modern/communicator/sidebar/sbar-top.png
new file mode 100644
index 0000000000..5494e79085
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbar-top.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbpicker-arrow.png b/comm/suite/themes/modern/communicator/sidebar/sbpicker-arrow.png
new file mode 100644
index 0000000000..e26dcc597c
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbpicker-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-lft-act.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-lft-act.png
new file mode 100644
index 0000000000..b5f5410b6c
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-lft-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-lft-sel.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-lft-sel.png
new file mode 100644
index 0000000000..d57fa9a73b
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-lft-sel.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-lft.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-lft.png
new file mode 100644
index 0000000000..f4c7d1997e
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-lft.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-mid-act.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-mid-act.png
new file mode 100644
index 0000000000..f1e6dec403
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-mid-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-mid-sel.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-mid-sel.png
new file mode 100644
index 0000000000..599fa1f7fc
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-mid-sel.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-mid.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-mid.png
new file mode 100644
index 0000000000..8ba5bdc69e
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-mid.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-act.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-act.png
new file mode 100644
index 0000000000..f24851d998
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-hov.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-hov.png
new file mode 100644
index 0000000000..f8ebe8c5c9
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-btm-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-act.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-act.png
new file mode 100644
index 0000000000..6bc3513748
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-hov.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-hov.png
new file mode 100644
index 0000000000..d7aa72dfca
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-sel.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-sel.png
new file mode 100644
index 0000000000..afd1757b95
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top-sel.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top.png b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top.png
new file mode 100644
index 0000000000..a584e0d9d6
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sbtab-rit-top.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sidebar/sidebar.css b/comm/suite/themes/modern/communicator/sidebar/sidebar.css
new file mode 100644
index 0000000000..6037702c4b
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sidebar.css
@@ -0,0 +1,247 @@
+/* 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/. */
+
+/* ===== sidebar.css ====================================================
+ == Styles used by the Sidebar panel and Sidebar tabs.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#sidebar-box {
+ border-left: 1px solid #8998AA;
+ background-color: #B9C4D0;
+}
+
+/* ::::: sidebar header ::::: */
+
+.sidebar-header-text {
+ color: #000000;
+ font-weight: bold;
+ -moz-box-align: center;
+}
+
+.sidebarheader-main {
+ -moz-binding: url("chrome://communicator/skin/sidebar/sidebarBindings.xml#sidebarheader") !important;
+ background-color: #B9C4D0;
+ background-image: url("chrome://communicator/skin/sidebar/sbar-top.png");
+ background-repeat: no-repeat;
+ overflow-x: hidden;
+}
+
+/* ..... picker button ..... */
+
+#sidebar-panel-picker {
+ margin-inline-end: 1px;
+ margin-top: 1px;
+ padding: 2px;
+}
+
+#sidebar-panel-picker:hover {
+ border: 1px outset #B1BDC9;
+}
+
+#sidebar-panel-picker[open="true"] {
+ border-style: inset;
+}
+
+#sidebar-panel-picker > .toolbarbutton-menu-dropmarker {
+ list-style-image: url("chrome://communicator/skin/sidebar/sbpicker-arrow.png");
+ padding-inline-end: 2px;
+ padding-inline-start: 2px;
+}
+
+/* ..... close button ..... */
+
+#sidebar-close-button {
+ margin: 0px 1px;
+ padding: 0px;
+ list-style-image: url("chrome://global/skin/icons/closebox.png");
+}
+
+#sidebar-close-button > .toolbarbutton-icon {
+ margin: 0px;
+}
+
+/* ::::: loading info ::::: */
+
+.text-panel-loading {
+ margin: 5px 0px;
+}
+
+.text-panel-loading[loading="false"] {
+ margin-inline-start: 11px;
+}
+
+.image-panel-loading {
+ margin: 5px;
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+/* ::::: sidebar panel ::::: */
+
+.iframe-panel {
+ border-bottom: 1px solid;
+ border-bottom-color: #96A7B8;
+}
+
+.browser-sidebar {
+ -moz-binding: url("chrome://global/skin/globalBindings.xml#browser-miniscroll") !important;
+ border-bottom: 1px solid;
+ border-bottom-color: #96A7B8;
+}
+
+
+/* ::::: sidebar tabs ::::: */
+
+/* ..... normal tabs ..... */
+
+.box-texttab {
+ -moz-binding: url("chrome://communicator/skin/sidebar/sidebarBindings.xml#sidebar-tab");
+ color: #000000;
+ border-left: 1px solid #DAE3ED;
+}
+
+.sidebar-tab-left-box,
+.sidebar-tab-right-box {
+ margin-bottom: 3px;
+}
+
+.sidebar-tab-left-box {
+ border-top: 1px solid;
+ border-bottom: 2px solid #B9C4D0;
+ border-top-color: #8A9DAF;
+ min-width: 90px;
+}
+
+.sidebar-tab-left {
+ width: 10px;
+ background: url("chrome://communicator/skin/sidebar/sbtab-lft.png") no-repeat;
+}
+
+.sidebar-tab-text {
+ margin: 0px !important;
+ background: url("chrome://communicator/skin/sidebar/sbtab-mid.png") repeat-x top;
+}
+
+.sidebar-tab-right-img {
+ width: 32px;
+ height: 13px;
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-rit-top.png");
+}
+
+.sidebar-tab-right-btm {
+ min-height: 5px;
+}
+
+.sidebar-tab-right-line {
+ border-bottom: 1px solid;
+ border-bottom-color: #96A7B8;
+}
+
+/* ..... hover state ..... */
+
+.box-texttab:hover > .sidebar-tab-left-box {
+ border-bottom: 1px solid;
+ border-bottom-color: #9CADBB;
+}
+
+.box-texttab:hover > .sidebar-tab-right-box > .sidebar-tab-right-top-box
+ > .sidebar-tab-right-img
+{
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-rit-top-hov.png");
+}
+
+.box-texttab:hover > .sidebar-tab-right-box > .sidebar-tab-right-btm {
+ background: url("chrome://communicator/skin/sidebar/sbtab-rit-btm-hov.png") no-repeat bottom left;
+}
+
+/* ..... active state ..... */
+
+.box-texttab:hover:active {
+ border-left: none;
+ color: #FFFFFF;
+}
+
+.box-texttab:hover:active > .sidebar-tab-left-box {
+ border-left: 1px solid;
+ border-bottom-color: #A1B1BE ;
+ border-top-color: #748490;
+ border-left-color: #737E8A;
+ background-color: #A1B1BE;
+}
+
+.box-texttab:hover:active > .sidebar-tab-left-box > .sidebar-tab-left {
+ background-image: url("chrome://communicator/skin/sidebar/sbtab-lft-act.png");
+}
+
+.box-texttab:hover:active > .sidebar-tab-left-box > .sidebar-tab-text {
+ background-image: url("chrome://communicator/skin/sidebar/sbtab-mid-act.png");
+}
+
+.box-texttab:hover:active > .sidebar-tab-right-box > .sidebar-tab-right-top-box
+ > .sidebar-tab-right-img
+{
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-rit-top-act.png");
+}
+
+.box-texttab:hover:active > .sidebar-tab-right-box > .sidebar-tab-right-btm {
+ background-image: url("chrome://communicator/skin/sidebar/sbtab-rit-btm-act.png");
+}
+
+/* ..... selected state ..... */
+
+.box-texttab[selected] {
+ border-left: 1px solid #E8EEF5 !important;
+ border-bottom: 1px solid #CBD5E0 !important;
+ color: #000000 !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-left-box {
+ border-bottom: none !important;
+ border-left: none !important;
+ border-top-color: #8A9DAF !important;
+ background-color: #CBD5E0 !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-left-box,
+.box-texttab[selected] > .sidebar-tab-right-box {
+ margin-bottom: 0 !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-left-box > .sidebar-tab-left {
+ background-image: url("chrome://communicator/skin/sidebar/sbtab-lft-sel.png") !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-left-box > .sidebar-tab-text {
+ background-image: url("chrome://communicator/skin/sidebar/sbtab-mid-sel.png") !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-right-box > .sidebar-tab-right-top-box
+ > .sidebar-tab-right-img
+{
+ list-style-image: url("chrome://communicator/skin/sidebar/sbtab-rit-top-sel.png") !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-right-box > .sidebar-tab-right-btm {
+ background: #CBD5E0 !important;
+}
+
+.box-texttab[selected] > .sidebar-tab-right-box > .sidebar-tab-right-top-box > .sidebar-tab-right-line
+{
+ border-bottom-color: #8CA2B3 !important;
+}
+
+/* ::::: sidebar navigation buttons ::::: */
+
+.sidebar-nav-button {
+ padding: 5px 0px 5px 0px;
+}
+
+.tab-fwd {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.tab-back {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
diff --git a/comm/suite/themes/modern/communicator/sidebar/sidebarBindings.xml b/comm/suite/themes/modern/communicator/sidebar/sidebarBindings.xml
new file mode 100644
index 0000000000..001afe71bc
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sidebarBindings.xml
@@ -0,0 +1,69 @@
+<?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/. -->
+
+
+<bindings id="sidebarSplitterBindings.xml"
+ 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="sidebar-tab" extends="xul:button">
+ <content>
+ <xul:hbox class="sidebar-tab-left-box">
+ <xul:spacer class="sidebar-tab-left"/>
+ <xul:label class="sidebar-tab-text" xbl:inherits="value=label,accesskey" crop="right" flex="1"/>
+ </xul:hbox>
+ <xul:vbox class="sidebar-tab-right-box" flex="1">
+ <xul:hbox class="sidebar-tab-right-top-box">
+ <xul:image class="sidebar-tab-right-img"/>
+ <xul:spacer class="sidebar-tab-right-line" flex="1"/>
+ </xul:hbox>
+ <xul:spacer class="sidebar-tab-right-btm" flex="1"/>
+ </xul:vbox>
+ </content>
+ </binding>
+
+ <binding id="DEAD" extends="xul:button">
+ <content>
+ <xul:vbox class="sidebar-tab-left">
+ <xul:spacer class="sidebar-tab-left-top" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"/>
+ <xul:spacer class="sidebar-tab-left-mid" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"
+ flex="1"/>
+ <xul:spacer class="sidebar-tab-left-btm" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"/>
+ </xul:vbox>
+
+ <xul:stack class="sidebar-tab-mid-1">
+ <xul:vbox class="sidebar-tab-mid-2">
+ <xul:spacer class="sidebar-tab-mid-top" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"
+ flex="1"/>
+ <xul:spacer class="sidebar-tab-mid-btm" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"/>
+ </xul:vbox>
+
+ <xul:vbox class="sidebar-tab-text-box">
+ <xul:label class="sidebar-tab-text" xbl:inherits="value=label" crop="right"/>
+ </xul:vbox>
+ </xul:stack>
+
+ <xul:vbox class="sidebar-tab-right">
+ <xul:spacer class="sidebar-tab-right-top" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"/>
+ <xul:spacer class="sidebar-tab-right-mid" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"
+ flex="1"/>
+ <xul:spacer class="sidebar-tab-right-btm" xbl:inherits="selected,first-panel-after-selected,top-panel,last-panel"/>
+ </xul:vbox>
+
+ <xul:spacer class="sidebar-tab-rightcap" flex="1"/>
+ </content>
+ </binding>
+
+ <binding id="sidebarheader">
+ <content>
+ <xul:label class="sidebar-header-text" xbl:inherits="value=label,crop" crop="right" flex="1"/>
+ <xul:hbox>
+ <children/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/themes/modern/communicator/sidebar/sidebarListView.css b/comm/suite/themes/modern/communicator/sidebar/sidebarListView.css
new file mode 100644
index 0000000000..39f974cbe2
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sidebar/sidebarListView.css
@@ -0,0 +1,17 @@
+/* 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/. */
+
+treechildren::-moz-tree-image(leaf),
+treechildren::-moz-tree-cell(leaf) {
+ cursor: pointer;
+}
+
+treechildren::-moz-tree-cell-text(leaf, hover) {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+treechildren::-moz-tree-cell(separator) {
+ cursor: default;
+}
diff --git a/comm/suite/themes/modern/communicator/smileys.css b/comm/suite/themes/modern/communicator/smileys.css
new file mode 100644
index 0000000000..858070dd52
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/smileys.css
@@ -0,0 +1,132 @@
+/* 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/. */
+
+/* ==== smileys.css ====================================================
+ == Style rules to bind smiley image types generated by editor and the mozTxtToHTMLConv
+ == into skinnable images.
+ ========================================================================== */
+
+@namespace url("http://www.w3.org/1999/xhtml");
+
+/* ::::: we also represent smilies inside of spans ::::: */
+
+.moz-smiley-s1,
+.moz-smiley-s2,
+.moz-smiley-s3,
+.moz-smiley-s4,
+.moz-smiley-s5,
+.moz-smiley-s6,
+.moz-smiley-s7,
+.moz-smiley-s8,
+.moz-smiley-s9,
+.moz-smiley-s10,
+.moz-smiley-s11,
+.moz-smiley-s12,
+.moz-smiley-s13,
+.moz-smiley-s14,
+.moz-smiley-s15,
+.moz-smiley-s16 {
+ display: inline-block;
+ vertical-align: middle;
+ -moz-user-select: all;
+}
+
+.moz-smiley-s1 > span,
+.moz-smiley-s2 > span,
+.moz-smiley-s3 > span,
+.moz-smiley-s4 > span,
+.moz-smiley-s5 > span,
+.moz-smiley-s6 > span,
+.moz-smiley-s7 > span,
+.moz-smiley-s8 > span,
+.moz-smiley-s9 > span,
+.moz-smiley-s10 > span,
+.moz-smiley-s11 > span,
+.moz-smiley-s12 > span,
+.moz-smiley-s13 > span,
+.moz-smiley-s14 > span,
+.moz-smiley-s15 > span,
+.moz-smiley-s16 > span {
+ display: none;
+}
+
+/* smile */
+.moz-smiley-s1:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-smile.png");
+}
+
+/* frown */
+.moz-smiley-s2:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-frown.png");
+}
+
+/* wink */
+.moz-smiley-s3:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-wink.png");
+}
+
+/* tongue */
+.moz-smiley-s4:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-tongue.png");
+}
+
+/* laughing */
+.moz-smiley-s5:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-laughing.png");
+}
+
+/* embarrassed */
+.moz-smiley-s6:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-embarrassed.png");
+}
+
+/* undecided */
+.moz-smiley-s7:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-undecided.png");
+}
+
+/* surprise */
+.moz-smiley-s8:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-surprise.png");
+}
+
+/* kiss */
+.moz-smiley-s9:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-kiss.png");
+}
+
+/* yell */
+.moz-smiley-s10:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-yell.png");
+}
+
+/* cool */
+.moz-smiley-s11:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-cool.png");
+}
+
+/* money */
+.moz-smiley-s12:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-money.png");
+}
+
+/* foot */
+.moz-smiley-s13:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-foot.png");
+}
+
+/* innocent */
+.moz-smiley-s14:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-innocent.png");
+}
+
+/* cry */
+.moz-smiley-s15:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-cry.png");
+}
+
+/* sealed */
+.moz-smiley-s16:before {
+ content: url("chrome://communicator/skin/icons/smileys/smiley-sealed.png");
+}
diff --git a/comm/suite/themes/modern/communicator/spinbuttons.css b/comm/suite/themes/modern/communicator/spinbuttons.css
new file mode 100644
index 0000000000..eda3b1d13a
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/spinbuttons.css
@@ -0,0 +1,49 @@
+/* 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/. */
+
+spinbuttons {
+ height: 16px;
+}
+
+.spinbuttons-button {
+ border: 1px solid;
+ padding: 1px;
+ border-top-color: #5D616E;
+ border-right-color: #2D3B49;
+ border-bottom-color: #2D3B49;
+ border-left-color: #5D616E;
+ border-radius: 0;
+ background: #B1BBC5 50% 50% no-repeat;
+ width: 13px;
+ min-width: 13px;
+ margin: 0;
+ -moz-box-flex: 1;
+}
+
+.spinbuttons-button:hover:active {
+ border-color: #8290A5;
+ border-top-color: #8290A5;
+ border-right-color: #8290A5;
+ border-bottom-color: #8290A5;
+ border-left-color: #8290A5;
+ background-color: #90A1B3;
+}
+
+.spinbuttons-button[disabled="true"] {
+ background-color: #B7BFCB;
+ border: 1px solid !important;
+ padding: 1px !important;
+ border-top-color: #8290A5 !important;
+ border-right-color: #8290A5 !important;
+ border-bottom-color: #8290A5 !important;
+ border-left-color: #8290A5 !important;
+}
+
+.spinbuttons-up {
+ list-style-image: url("chrome://global/skin/scrollbar/mini-btn-up.gif");
+}
+
+.spinbuttons-down {
+ list-style-image: url("chrome://global/skin/scrollbar/mini-btn-dn.gif");
+}
diff --git a/comm/suite/themes/modern/communicator/sync/sync-16-throbber.png b/comm/suite/themes/modern/communicator/sync/sync-16-throbber.png
new file mode 100644
index 0000000000..d604a32d75
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-16-throbber.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/sync-16.png b/comm/suite/themes/modern/communicator/sync/sync-16.png
new file mode 100644
index 0000000000..c7839c33e9
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/sync-32-throbber.png b/comm/suite/themes/modern/communicator/sync/sync-32-throbber.png
new file mode 100644
index 0000000000..4c8d903544
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-32-throbber.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/sync-32.png b/comm/suite/themes/modern/communicator/sync/sync-32.png
new file mode 100644
index 0000000000..d0d3b19050
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-32.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/sync-bg.png b/comm/suite/themes/modern/communicator/sync/sync-bg.png
new file mode 100644
index 0000000000..893a27d76e
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-bg.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/sync-desktopIcon.png b/comm/suite/themes/modern/communicator/sync/sync-desktopIcon.png
new file mode 100644
index 0000000000..a98f21b30f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-desktopIcon.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/sync-mobileIcon.png b/comm/suite/themes/modern/communicator/sync/sync-mobileIcon.png
new file mode 100644
index 0000000000..57fbe054c8
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/sync-mobileIcon.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/sync/syncCommon.css b/comm/suite/themes/modern/communicator/sync/syncCommon.css
new file mode 100644
index 0000000000..28e53d89a1
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/syncCommon.css
@@ -0,0 +1,41 @@
+/* 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/. */
+
+/* The following are used by both syncSetup.xul and syncGenericChange.xul */
+.statusIcon {
+ margin-inline-start: 4px;
+ max-height: 16px;
+ max-width: 16px;
+}
+
+.statusIcon[status="active"] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.statusIcon[status="error"] {
+ list-style-image: url("chrome://global/skin/icons/error-16.png");
+}
+
+.statusIcon[status="success"] {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+}
+
+/* .data is only used by syncGenericChange.xul, but it seems unnecessary to have
+ a separate stylesheet for it. */
+.data {
+ font-size: 90%;
+ font-weight: bold;
+}
+
+#change-dialog {
+ width: 40em;
+}
+
+#introText {
+ margin-top: 2px;
+}
+
+#feedback {
+ height: 2em;
+}
diff --git a/comm/suite/themes/modern/communicator/sync/syncQuota.css b/comm/suite/themes/modern/communicator/sync/syncQuota.css
new file mode 100644
index 0000000000..073b0e5d69
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/syncQuota.css
@@ -0,0 +1,16 @@
+/* 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/. */
+
+#quotaDialog {
+ width: 33em;
+ height: 25em;
+}
+
+#treeCaption {
+ height: 4em;
+}
+
+.captionWarning {
+ font-weight: bold;
+}
diff --git a/comm/suite/themes/modern/communicator/sync/syncSetup.css b/comm/suite/themes/modern/communicator/sync/syncSetup.css
new file mode 100644
index 0000000000..c9216df69e
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/sync/syncSetup.css
@@ -0,0 +1,113 @@
+/* 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/. */
+
+wizard {
+ -moz-appearance: none;
+ width: 55em;
+ height: 42em;
+ padding: 0em;
+}
+
+.wizard-page-box {
+ -moz-appearance: none;
+ padding: 1em 0em;
+ margin: 0em;
+}
+
+wizardpage {
+ -moz-box-pack: center;
+ -moz-box-align: center;
+ margin: 0em;
+ padding: 0em 8em;
+}
+
+.wizard-header {
+ -moz-appearance: none;
+ border: none;
+ padding: 1em 0em;
+ text-align: center;
+}
+
+.wizard-header-label {
+ font-size: 24pt;
+ font-weight: normal;
+}
+
+.wizard-header-icon {
+ visibility: collapse;
+}
+
+.confirm {
+ border: 1px solid #000000;
+ padding: 1em;
+ border-radius: 5px;
+}
+
+/* Override the text-link style from global.css */
+description > .text-link,
+description > .text-link:focus {
+ margin: 0px;
+ padding: 0px;
+}
+
+.success,
+.error {
+ padding: 2px;
+ border-radius: 2px;
+}
+
+.error {
+ background-color: #FF0000 !important;
+ color: #FFFFFF !important;
+}
+
+.success {
+ background-color: #00FF00 !important;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 100%;
+ color: #FF0000;
+}
+
+.mainDesc {
+ font-weight: bold;
+ font-size: 100%;
+}
+
+.normal {
+ font-size: 100%;
+}
+
+.inputColumn {
+ margin-inline-end: 2px;
+}
+
+.recommended {
+ font-weight: bold;
+}
+
+.pin {
+ font-size: 18pt;
+ text-align: center;
+}
+
+#passphraseHelpSpacer {
+ width: 0.5em;
+}
+
+#add-device-throbber,
+#login-throbber {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+#successPageIcon {
+ /* TODO replace this with a 128px version (bug 591122) */
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#pickSetupDesc {
+ padding: 0em 7em;
+}
diff --git a/comm/suite/themes/modern/communicator/taskbar/taskbar.png b/comm/suite/themes/modern/communicator/taskbar/taskbar.png
new file mode 100644
index 0000000000..be3aee9b3b
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/taskbar/taskbar.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/taskbar/taskmenu-abook.png b/comm/suite/themes/modern/communicator/taskbar/taskmenu-abook.png
new file mode 100644
index 0000000000..5492cdd23f
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/taskbar/taskmenu-abook.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/taskbar/taskmenu-browser.png b/comm/suite/themes/modern/communicator/taskbar/taskmenu-browser.png
new file mode 100644
index 0000000000..6472c01f2b
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/taskbar/taskmenu-browser.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/taskbar/taskmenu-composer.png b/comm/suite/themes/modern/communicator/taskbar/taskmenu-composer.png
new file mode 100644
index 0000000000..13ec6c26a4
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/taskbar/taskmenu-composer.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/taskbar/taskmenu-mailnews.png b/comm/suite/themes/modern/communicator/taskbar/taskmenu-mailnews.png
new file mode 100644
index 0000000000..dd2bb3e2bb
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/taskbar/taskmenu-mailnews.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/tasksOverlay.css b/comm/suite/themes/modern/communicator/tasksOverlay.css
new file mode 100644
index 0000000000..d22c197e63
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/tasksOverlay.css
@@ -0,0 +1,87 @@
+/* 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/. */
+
+/* ==== tasksOverlay.css ================================================
+ == Styles used by the the overlay which contains tasks related
+ == content, such as the taskbar and product launcher icons.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: communicator product icons ::::: */
+
+#mini-nav {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 13px 13px 0);
+ margin-inline-end: 2px;
+}
+
+#mini-nav:hover:active {
+ -moz-image-region: rect(13px 13px 26px 0);
+}
+
+.icon-navigator16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-browser.png");
+}
+
+#mini-mail {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 34px 13px 13px);
+ margin-inline-start: 1px;
+}
+
+#mini-mail:hover:active {
+ -moz-image-region: rect(13px 34px 26px 13px);
+}
+
+#mini-mail[BiffState="NewMail"] {
+ -moz-image-region: rect(0 55px 13px 34px);
+}
+
+#mini-mail[BiffState="NewMail"]:hover:active {
+ -moz-image-region: rect(13px 55px 26px 34px);
+}
+
+.icon-mail16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-mailnews.png");
+}
+
+#mini-addr {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 89px 13px 74px);
+}
+
+#mini-addr:hover:active {
+ -moz-image-region: rect(13px 89px 26px 74px);
+}
+
+.icon-addressbook16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-abook.png");
+}
+
+#mini-comp {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskbar.png");
+ -moz-image-region: rect(0 74px 13px 55px);
+}
+
+#mini-comp:hover:active {
+ -moz-image-region: rect(13px 74px 26px 55px);
+}
+
+.icon-composer16 {
+ list-style-image: url("chrome://communicator/skin/taskbar/taskmenu-composer.png");
+}
+
+/* ::::: component bar ::::: */
+
+#component-bar {
+ padding: 1px 3px 0px;
+}
+
+.taskbutton {
+ margin: 0px 3px;
+ border: none !important;
+ padding: 0px !important;
+}
+
diff --git a/comm/suite/themes/modern/communicator/toolbar.css b/comm/suite/themes/modern/communicator/toolbar.css
new file mode 100644
index 0000000000..8bf33648d7
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar.css
@@ -0,0 +1,106 @@
+/* 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/. */
+
+/* ==== toolbar.css =====================================================
+ == Styles for toolbars that are used throughout the Communicator suite.
+ ====================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: toolbar-primary ::::: */
+
+.toolbar-primary {
+ -moz-binding: url("chrome://communicator/skin/toolbar/toolbarBindings.xml#toolbar-primary");
+ background-color: #B1BDC9;
+}
+
+.toolbar-primary-holder {
+ background: url("chrome://communicator/skin/toolbar/prtb-bg-line.png") repeat-x top;
+ border-top: none;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+ border-left: none;
+}
+
+.toolbar-primary-holder[mode="icons"],
+.toolbar-primary-holder[mode="text"],
+.toolbar-primary-holder[iconsize="small"],
+.toolbar-primary-holder[labelalign="end"] {
+ background-image: url("chrome://communicator/skin/toolbar/prtb-bg-noline.png");
+}
+
+.toolbar-primary-icon {
+ width: 77px;
+ height: 50px;
+ background-repeat: no-repeat;
+ background-position: top;
+}
+
+.toolbar-primary-icon[mode="icons"],
+.toolbar-primary-icon[mode="text"],
+.toolbar-primary-icon[iconsize="small"],
+.toolbar-primary-icon[labelalign="end"] {
+ display: none;
+}
+
+.toolbar-primary > .toolbaritem-noline,
+.toolbar-primary > toolbarpaletteitem > .toolbaritem-noline {
+ background: url("chrome://communicator/skin/toolbar/prtb-bg-noline.png") #B1BDC9 repeat-x top;
+}
+
+toolbar[mode="text"] toolbarbutton[type="menu-button"],
+toolbar[labelalign="end"]:not([mode="icons"]) toolbarbutton[type="menu-button"] {
+ -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#menu-button");
+}
+
+toolbar[mode="text"] .toolbarbutton-1 .toolbarbutton-text,
+toolbar[labelalign="end"] .toolbarbutton-1 .toolbarbutton-text {
+ min-width: 0px;
+}
+
+/* ::::: toolbargrippy ::::: */
+
+.toolbar-primary-grippy {
+ -moz-binding: url("chrome://communicator/skin/toolbar/toolbarBindings.xml#toolbargrippy-primary");
+ border: none;
+ background: url("chrome://communicator/skin/toolbar/prtb-grip-mid.png") repeat-y;
+ list-style-image: url("chrome://communicator/skin/toolbar/prtb-grip-btm.png");
+}
+
+.toolbar-primary-grippy > .toolbargrippy-texture {
+ width: 13px;
+ height: 0px;
+ list-style-image: url("chrome://communicator/skin/toolbar/prtb-grip-top.png");
+}
+
+.toolbar-primary-grippy > .toolbargrippy-arrow {
+ margin: 0px;
+ width: 13px;
+ height: 10px;
+ list-style-image: inherit;
+}
+
+.toolbar-primary-grippy:hover:active {
+ background-image: url("chrome://communicator/skin/toolbar/prtb-grip-mid-act.png");
+ list-style-image: url("chrome://communicator/skin/toolbar/prtb-grip-btm-act.png");
+}
+
+.toolbar-primary-grippy:hover:active > .toolbargrippy-texture {
+ list-style-image: url("chrome://communicator/skin/toolbar/prtb-grip-top-act.png");
+}
+
+/* ::::: toolbar-primary separator ::::: */
+
+.toolbar-primary > toolbarseparator,
+.toolbarseparator-primary {
+ margin: 0px;
+ padding: 0px;
+ border: none;
+ width: 18px;
+ background: none;
+}
+
+.toolbar-primary > toolbarpaletteitem > toolbarseparator {
+ margin: 0px 8px;
+}
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-bg-line.png b/comm/suite/themes/modern/communicator/toolbar/prtb-bg-line.png
new file mode 100644
index 0000000000..56590ad7fe
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-bg-line.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-bg-noline.png b/comm/suite/themes/modern/communicator/toolbar/prtb-bg-noline.png
new file mode 100644
index 0000000000..01f16335c4
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-bg-noline.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm-act.png b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm-act.png
new file mode 100644
index 0000000000..ad4203e24d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm.png b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm.png
new file mode 100644
index 0000000000..1afb079117
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-btm.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid-act.png b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid-act.png
new file mode 100644
index 0000000000..d113f628de
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid.png b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid.png
new file mode 100644
index 0000000000..e12831e441
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-mid.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-grip-top-act.png b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-top-act.png
new file mode 100644
index 0000000000..b6508e6dc0
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-top-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/prtb-grip-top.png b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-top.png
new file mode 100644
index 0000000000..d86fc582c4
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/prtb-grip-top.png
Binary files differ
diff --git a/comm/suite/themes/modern/communicator/toolbar/toolbarBindings.xml b/comm/suite/themes/modern/communicator/toolbar/toolbarBindings.xml
new file mode 100644
index 0000000000..c5bf0d5abf
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/toolbar/toolbarBindings.xml
@@ -0,0 +1,34 @@
+<?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/. -->
+
+<bindings id="toolbarBindings"
+ 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="toolbar-primary"
+ extends="chrome://communicator/content/bindings/toolbar-xpfe.xml#grippytoolbar-primary">
+ <content>
+ <xul:toolbargrippy xbl:inherits="last-toolbar,hidden=grippyhidden"
+ tbattr="toolbar-grippy" class="toolbar-primary-grippy"/>
+ <xul:hbox class="toolbar-holder toolbar-primary-holder" flex="1" xbl:inherits="mode,iconsize,labelalign">
+ <xul:image class="toolbar-primary-icon" xbl:inherits="mode,iconsize,labelalign"/>
+ <xul:hbox class="toolbar-button-box" flex="1">
+ <children/>
+ </xul:hbox>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="toolbargrippy-primary"
+ extends="chrome://communicator/content/bindings/toolbar-xpfe.xml#toolbargrippy">
+ <content>
+ <xul:image class="toolbargrippy-arrow"/>
+ <xul:image class="toolbargrippy-texture" flex="1"/>
+ </content>
+ </binding>
+
+</bindings>
diff --git a/comm/suite/themes/modern/communicator/viewSourceOverlay.css b/comm/suite/themes/modern/communicator/viewSourceOverlay.css
new file mode 100644
index 0000000000..9381ac4b4d
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/viewSourceOverlay.css
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* make findbar appear above content */
+#appcontent {
+ -moz-box-direction: reverse;
+}
diff --git a/comm/suite/themes/modern/communicator/xpinstall/xpinstall.css b/comm/suite/themes/modern/communicator/xpinstall/xpinstall.css
new file mode 100644
index 0000000000..2d3b47c846
--- /dev/null
+++ b/comm/suite/themes/modern/communicator/xpinstall/xpinstall.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+/* ===== xpinstall.css ==================================================
+ == Styles used by the XPInstall dialogs.
+ ======================================================================= */
+
+@import url("chrome://communicator/skin");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Install Confirm and Progress dialogs ::::: */
+
+.packageName {
+ font-weight: bold;
+}
+
+#confirmBox {
+ margin: 1ex;
+}
+
+.confirmSeparator {
+ height: 1em;
+}
+
+.confirmName {
+ font-weight: bold;
+}
+
+#urlColumn {
+ margin-inline-start: 1ex;
+}
+
+#alert-image {
+ margin: 1em;
+}
diff --git a/comm/suite/themes/modern/editor/EditorDialog.css b/comm/suite/themes/modern/editor/EditorDialog.css
new file mode 100644
index 0000000000..575c3d0653
--- /dev/null
+++ b/comm/suite/themes/modern/editor/EditorDialog.css
@@ -0,0 +1,289 @@
+/* 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/. */
+
+/* ==== EditorDialog.css ====================================================
+ == Styles used by all dialog boxes in the Editor application.
+ ========================================================================== */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: common styles ::::: */
+
+.MinWidth5em {
+ min-width: 5em;
+}
+
+.MinWidth10em {
+ min-width: 10em;
+}
+
+.MinWidth20em {
+ min-width: 20em;
+}
+
+.bold {
+ font-weight: bold;
+}
+
+.italic {
+ font-style: italic;
+}
+
+.larger {
+ font-size: 120%;
+}
+
+.narrow {
+ width: 3em;
+}
+
+.wrap {
+ width: 1em;
+}
+
+.menuitem-highlight-1 {
+ font-weight: bold;
+}
+
+/* ::::: common buttons ::::: */
+
+#MoreFewerButton[more="0"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+ min-width: 12em;
+}
+
+#MoreFewerButton[more="1"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+ min-width: 12em;
+}
+
+/* ::::: table properties dialog ::::: */
+
+#PreviousButton {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.png");
+}
+
+#NextButton {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.png");
+}
+
+#PreviousButton[type="row"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+#NextButton[type="row"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+/* ::::: spelling dialog ::::: */
+
+#MisspelledWord {
+ font-weight: bold;
+}
+
+#ReplaceWordInput {
+ min-width: 16em;
+ width: 16em;
+}
+
+.spell-check {
+ min-width: 8em;
+}
+
+/* ::::: spacers ::::: */
+
+.smallspacer {
+ width: 3px;
+ height: 3px;
+ min-width: 3px;
+ min-height: 3px;
+}
+
+.spacer {
+ width: 5px;
+ height: 5px;
+ min-width: 5px;
+ min-height: 5px;
+}
+
+.bigspacer {
+ width: 10px;
+ height: 10px;
+ min-width: 10px;
+ min-height: 10px;
+}
+
+/* These should be the width of the checkbox and radio button images + margin + padding
+ Used to indent below those to the level of the text label next to image
+*/
+.checkbox-spacer {
+ width: 20px;
+ min-width: 20px;
+}
+
+.radio-spacer {
+ width: 20px;
+ min-width: 20px;
+}
+
+/* ::::: widget adjustments ::::: */
+
+/* The defaults are WAY to big! */
+groupbox {
+ margin: 2px 5px 4px;
+ padding-top: 2px;
+ padding-bottom: 4px;
+}
+
+/* ::::: color picker button ::::: */
+
+.color-button {
+ /* override larger default values */
+ min-width: 0px;
+ margin: 2px;
+}
+
+.color-well {
+ border: 1px inset #B4C3D4;
+ width: 20px;
+ height: 12px;
+}
+
+.color-well[default="true"] {
+ border: 1px solid transparent !important;
+ background-color: inherit !important;
+}
+
+/* ::::: color picker dialog ::::: */
+
+#ColorPicker {
+ -moz-user-focus: normal;
+}
+
+#ColorPickerSwatch {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 3px;
+ border: 2px outset #B4C3D4;
+ width: 25px;
+ height: 20px;
+}
+
+#ColorPickerSwatch[default="true"] {
+ border: 2px solid transparent;
+ background-color: inherit;
+}
+
+#LastPickedColor {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 2px;
+ margin-inline-end: 8px;
+ border: 1px inset #B4C3D4;
+ width: 17px;
+ height: 13px;
+}
+
+
+/* ::::: Image Properties dialog ::::: */
+
+.align-menu > .menu-iconic-left > .menu-iconic-icon {
+ height: auto;
+ width: auto;
+}
+
+.align-menu[value="top"] {
+ list-style-image: url("chrome://editor/skin/icons/img-align-top.png") !important;
+}
+
+.align-menu[value="top"]:hover {
+ list-style-image: url("chrome://editor/skin/icons/img-align-top-sel.png") !important;
+}
+
+.align-menu[value="middle"] {
+ list-style-image: url("chrome://editor/skin/icons/img-align-mid.png") !important;
+}
+
+.align-menu[value="middle"]:hover {
+ list-style-image: url("chrome://editor/skin/icons/img-align-mid-sel.png") !important;
+}
+
+.align-menu[value="bottom"] {
+ list-style-image: url("chrome://editor/skin/icons/img-align-btm.png") !important;
+}
+
+.align-menu[value="bottom"]:hover {
+ list-style-image: url("chrome://editor/skin/icons/img-align-btm-sel.png") !important;
+}
+
+.align-menu[value="right"] {
+ list-style-image: url("chrome://editor/skin/icons/img-align-rit.png") !important;
+}
+
+.align-menu[value="left"] {
+ list-style-image: url("chrome://editor/skin/icons/img-align-lft.png") !important;
+}
+
+/* Don't change width/height of these without changing values in
+ GetOriginalWidth(), EdImageProps.js */
+#preview-image-box {
+ margin: 6px 5px;
+ border: 1px inset #B4C3D4;
+ width: 82px;
+ height: 52px;
+ min-width: 82px;
+ min-height: 52px;
+ max-width: 82px;
+ max-height: 52px;
+ overflow: -moz-hidden-unscrollable;
+}
+
+#preview-image-holder {
+ padding: 0px;
+ margin: 0px;
+}
+
+/* :::::: Advanced Edit dialog ::::: */
+
+#tagLabel {
+ font-weight: bold;
+}
+
+.AttributesTree {
+ min-width: 200px;
+ min-height: 200px;
+}
+
+/* ::::: Editor pref panels ::::: */
+
+#ColorPreview {
+ margin-inline-start: 10px;
+ border: 1px inset #B4C3D4;
+ padding: 0 5px;
+ min-width: 100px;
+ min-height: 50px;
+}
+
+/* ::::: select edit dialog ::::: */
+
+#SelectTreeChildren::-moz-tree-cell(SelectSelCol, checked-false) {
+ background: url("chrome://global/skin/checkbox/cbox.png") 50% 50% no-repeat;
+}
+
+#SelectTreeChildren::-moz-tree-cell(SelectSelCol, checked-true) {
+ background: url("chrome://global/skin/checkbox/cbox-check.png") 50% 50% no-repeat;
+}
+
+/* ::::: Publishing Progress ::::: */
+
+.progressitem[progress="busy"] {
+ list-style-image: url("chrome://editor/skin/icons/progress-busy.png");
+}
+.progressitem[progress="done"] {
+ list-style-image: url("chrome://editor/skin/icons/progress-done.png");
+}
+.progressitem[progress="failed"] {
+ list-style-image: url("chrome://editor/skin/icons/progress-failed.png");
+}
diff --git a/comm/suite/themes/modern/editor/editor.css b/comm/suite/themes/modern/editor/editor.css
new file mode 100644
index 0000000000..b4b65020d1
--- /dev/null
+++ b/comm/suite/themes/modern/editor/editor.css
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ==== editor.css ==========================================================
+ == Styles shared throughout the Editor application.
+ ========================================================================== */
+
+@import url("chrome://communicator/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#content-frame {
+ height: 400px;
+ min-width: 10px;
+ min-height: 10px;
+}
+
+.source-editor,
+.source-editor:focus {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ margin-inline-start: 0px;
+ margin-inline-end: 5px;
+ border: 0px;
+ padding-top: 5px;
+ padding-bottom: 0px;
+ padding-inline-start: 5px;
+ padding-inline-end: 0px;
+}
+
+/* Multiline textarea for HTML source editing */
+#content-source, #doctype-text {
+ font-family : -moz-fixed;
+ font-size: initial;
+}
+
+/* ::::: formatting toolbar ::::: */
+
+#FormatToolbar {
+ background: url("chrome://global/skin/toolbar/tb-mid.png") #C7D0D9 repeat-x top;
+}
+
+#FormatToolbar > .toolbar-box > toolbargrippy,
+#FormatToolbar > .toolbar-box > .toolbar-holder {
+ border-top: 1px solid #CED6DD;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+ border-left: 1px solid #DAE3ED;
+}
+
+#FormatToolbar > .toolbar-box > toolbargrippy:hover:active {
+ border-color: #67737E;
+}
+
+/* ::::: struct toolbar ::::: */
+
+#structToolbar {
+ min-width: 1px;
+ overflow: -moz-hidden-unscrollable;
+}
+
+#structSpacer {
+ margin: 1px 0px;
+}
+
+.struct-button {
+ padding: 0px;
+}
+
+.struct-button[checked="true"] {
+ font-weight: bold;
+}
+
+.struct-textbox {
+ padding: 0px !important;
+ border: 1px solid #000000 !important;
+ margin: 0px !important;
+}
+
diff --git a/comm/suite/themes/modern/editor/editorFormatToolbar.css b/comm/suite/themes/modern/editor/editorFormatToolbar.css
new file mode 100644
index 0000000000..8eb20b0f16
--- /dev/null
+++ b/comm/suite/themes/modern/editor/editorFormatToolbar.css
@@ -0,0 +1,591 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: formatting toolbar ::::: */
+
+#FormatToolbar {
+ background: url("chrome://global/skin/toolbar/tb-mid.png") #C7D0D9 repeat-x top;
+}
+
+#FormatToolbar > toolbargrippy,
+#FormatToolbar > .toolbar-holder {
+ border-top: 1px solid #CED6DD;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+ border-left: 1px solid #DAE3ED;
+}
+
+#FormatToolbar > toolbargrippy:hover:active {
+ border-color: #67737E;
+}
+
+/* ::::: formatting buttons ::::: */
+
+toolbarbutton.formatting-button {
+ border: none !important;
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+ background-color: transparent !important;
+}
+
+#DecreaseFontSizeButton {
+ -moz-image-region: rect(101px 22px 118px 0px);
+}
+
+#DecreaseFontSizeButton:hover {
+ -moz-image-region: rect(101px 44px 118px 22px);
+}
+
+#DecreaseFontSizeButton:hover:active {
+ -moz-image-region: rect(101px 66px 118px 44px);
+}
+
+#DecreaseFontSizeButton[disabled="true"] {
+ -moz-image-region: rect(101px 88px 118px 66px) !important;
+}
+
+#IncreaseFontSizeButton {
+ -moz-image-region: rect(118px 22px 135px 0px);
+}
+
+#IncreaseFontSizeButton:hover {
+ -moz-image-region: rect(118px 44px 135px 22px);
+}
+
+#IncreaseFontSizeButton:hover:active {
+ -moz-image-region: rect(118px 66px 135px 44px);
+}
+
+#IncreaseFontSizeButton[disabled="true"] {
+ -moz-image-region: rect(118px 88px 135px 66px) !important;
+}
+
+#boldButton {
+ -moz-image-region: rect(33px 54px 50px 32px);
+}
+
+#boldButton:hover {
+ -moz-image-region: rect(33px 76px 50px 54px);
+}
+
+#boldButton:hover:active {
+ -moz-image-region: rect(50px 22px 67px 0px );
+}
+
+#boldButton[checked="true"] {
+ -moz-image-region: rect(33px 98px 50px 76px);
+}
+
+#boldButton[disabled="true"] {
+ -moz-image-region: rect(50px 44px 67px 22px) !important;
+}
+
+#italicButton {
+ -moz-image-region: rect(185px 86px 202px 64px);
+}
+
+#italicButton:hover {
+ -moz-image-region: rect(202px 22px 219px 0px);
+}
+
+#italicButton:hover:active {
+ -moz-image-region: rect(202px 66px 219px 44px);
+}
+
+#italicButton[checked="true"] {
+ -moz-image-region: rect(202px 44px 219px 22px);
+}
+
+#italicButton[disabled="true"] {
+ -moz-image-region: rect(202px 88px 219px 66px) !important;
+}
+
+#underlineButton {
+ -moz-image-region: rect(338px 22px 355px 0px);
+}
+
+#underlineButton:hover {
+ -moz-image-region: rect(338px 44px 355px 22px);
+}
+
+#underlineButton:hover:active {
+ -moz-image-region: rect(338px 88px 355px 66px);
+}
+
+#underlineButton[checked="true"] {
+ -moz-image-region: rect(338px 66px 355px 44px);
+}
+
+#underlineButton[disabled="true"] {
+ -moz-image-region: rect(355px 22px 372px 0px) !important;
+}
+
+#ulButton {
+ -moz-image-region: rect(50px 66px 67px 44px);
+}
+
+#ulButton:hover {
+ -moz-image-region: rect(50px 88px 67px 66px);
+}
+
+#ulButton:hover:active {
+ -moz-image-region: rect(67px 44px 84px 22px);
+}
+
+#ulButton[checked="true"] {
+ -moz-image-region: rect(67px 22px 84px 0px);
+}
+
+#ulButton[disabled="true"] {
+ -moz-image-region: rect(67px 66px 84px 44px ) !important;
+}
+
+
+#olButton {
+ -moz-image-region: rect(253px 66px 270px 44px);
+}
+
+#olButton:hover {
+ -moz-image-region: rect(253px 88px 270px 66px);
+}
+
+#olButton:hover:active {
+ -moz-image-region: rect(270px 44px 287px 22px);
+}
+
+#olButton[checked="true"] {
+ -moz-image-region: rect(270px 22px 287px 0px);
+}
+
+#olButton[disabled="true"] {
+ -moz-image-region: rect(270px 66px 287px 44px) !important;
+}
+
+#outdentButton {
+ -moz-image-region: rect(270px 88px 287px 66px);
+}
+
+#outdentButton:hover {
+ -moz-image-region: rect(287px 22px 304px 0px);
+}
+
+#outdentButton:hover:active {
+ -moz-image-region: rect(287px 44px 304px 22px);
+}
+
+#outdentButton[disabled="true"] {
+ -moz-image-region: rect(287px 66px 304px 44px) !important;
+}
+
+#indentButton {
+ -moz-image-region: rect(135px 22px 152px 0px);
+}
+
+#indentButton:hover {
+ -moz-image-region: rect(135px 44px 152px 22px);
+}
+
+#indentButton:hover:active {
+ -moz-image-region: rect(135px 66px 152px 44px);
+}
+
+#indentButton[disabled="true"] {
+ -moz-image-region: rect(135px 88px 152px 66px) !important;
+}
+
+#align-left-button {
+ -moz-image-region: rect(236px 44px 253px 22px);
+}
+
+#align-left-button:hover {
+ -moz-image-region: rect(236px 66px 253px 44px);
+}
+
+#align-left-button:hover:active {
+ -moz-image-region: rect(253px 22px 270px 0px);
+}
+
+#align-left-button[checked="true"] {
+ -moz-image-region: rect(236px 88px 253px 66px);
+}
+
+#align-left-button[disabled="true"] {
+ -moz-image-region: rect(253px 44px 270px 22px) !important;
+}
+
+#align-center-button {
+ -moz-image-region: rect(67px 88px 84px 66px);
+}
+
+#align-center-button:hover {
+ -moz-image-region: rect(84px 22px 101px 0px);
+}
+
+#align-center-button:hover:active {
+ -moz-image-region: rect(84px 66px 101px 44px);
+}
+
+#align-center-button[checked="true"] {
+ -moz-image-region: rect(84px 44px 101px 22px);
+}
+
+#align-center-button[disabled="true"] {
+ -moz-image-region: rect(84px 88px 101px 66px) !important;
+}
+
+#align-right-button {
+ -moz-image-region: rect(287px 88px 304px 66px);
+}
+
+#align-right-button:hover {
+ -moz-image-region: rect(304px 22px 321px 0px);
+}
+
+#align-right-button:hover:active {
+ -moz-image-region: rect(304px 66px 321px 44px);
+}
+
+#align-right-button[checked="true"] {
+ -moz-image-region: rect(304px 44px 321px 22px);
+}
+
+#align-right-button[disabled="true"] {
+ -moz-image-region: rect(304px 88px 321px 66px) !important;
+}
+
+#align-justify-button {
+ -moz-image-region: rect(219px 22px 236px 0px);
+}
+
+#align-justify-button:hover {
+ -moz-image-region: rect(219px 44px 236px 22px);
+}
+
+#align-justify-button:hover:active {
+ -moz-image-region: rect(219px 88px 236px 66px);
+}
+
+#align-justify-button[checked="true"] {
+ -moz-image-region: rect(219px 66px 236px 44px);
+}
+
+#align-justify-button[disabled="true"] {
+ -moz-image-region: rect(236px 22px 253px 0px) !important;
+}
+
+#AlignPopupButton {
+ -moz-image-region: rect(0px 22px 17px 0px);
+}
+
+#AlignPopupButton:hover {
+ -moz-image-region: rect(0px 44px 17px 22px);
+}
+
+#AlignPopupButton[open="true"] {
+ -moz-image-region: rect(0px 66px 17px 44px) !important;
+}
+
+#AlignPopupButton[disabled="true"] {
+ -moz-image-region: rect(0px 88px 17px 66px) !important;
+}
+
+#InsertPopupButton {
+ -moz-image-region: rect(152px 22px 169px 0px);
+}
+
+#InsertPopupButton:hover {
+ -moz-image-region: rect(152px 44px 169px 22px);
+}
+
+#InsertPopupButton[open="true"] {
+ -moz-image-region: rect(152px 66px 169px 44px) !important;
+}
+
+#InsertPopupButton[disabled="true"] {
+ -moz-image-region: rect(152px 88px 169px 66px) !important;
+}
+
+#smileButtonMenu {
+ -moz-image-region: rect(321px 22px 338px 0px);
+}
+
+#smileButtonMenu:hover {
+ -moz-image-region: rect(321px 44px 338px 22px);
+}
+
+#smileButtonMenu[open="true"] {
+ -moz-image-region: rect(321px 66px 338px 44px) !important;
+}
+
+#smileButtonMenu[disabled="true"] {
+ -moz-image-region: rect(321px 88px 338px 66px) !important;
+}
+
+/* ::::: menuitem icons ::::: */
+
+/* ..... align menu ..... */
+
+#AlignPopup > menuitem {
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+}
+
+#AlignLeftItem {
+ -moz-image-region: rect(17px 80px 33px 64px);
+}
+
+#AlignLeftItem:hover {
+ -moz-image-region: rect(17px 96px 33px 80px);
+}
+
+#AlignCenterItem {
+ -moz-image-region: rect(17px 16px 33px 0px);
+}
+
+#AlignCenterItem:hover {
+ -moz-image-region: rect(17px 32px 33px 16px);
+}
+
+#AlignRightItem {
+ -moz-image-region: rect(33px 16px 49px 0px);
+}
+
+#AlignRightItem:hover {
+ -moz-image-region: rect(33px 32px 49px 0px);
+}
+
+#AlignJustifyItem {
+ -moz-image-region: rect(17px 48px 33px 32px);
+}
+
+#AlignJustifyItem:hover {
+ -moz-image-region: rect(17px 64px 33px 48px);
+}
+
+/* ..... insert menu ..... */
+
+#InsertPopup > menuitem {
+ list-style-image: url("chrome://editor/skin/icons/btn2.png");
+}
+
+#InsertLinkItem {
+ -moz-image-region: rect(185px 16px 201px 0px);
+}
+
+#InsertLinkItem:hover {
+ -moz-image-region: rect(185px 32px 201px 16px);
+}
+
+#InsertAnchorItem {
+ -moz-image-region: rect(169px 16px 185px 0px);
+}
+
+#InsertAnchorItem:hover {
+ -moz-image-region: rect(169px 32px 185px 16px);
+}
+
+#InsertImageItem {
+ -moz-image-region: rect(169px 80px 185px 64px);
+}
+
+#InsertImageItem:hover {
+ -moz-image-region: rect(169px 96px 185px 80px);
+}
+
+#InsertHRuleItem {
+ -moz-image-region: rect(169px 48px 185px 32px);
+}
+
+#InsertHRuleItem:hover {
+ -moz-image-region: rect(169px 64px 185px 48px);
+}
+
+#InsertTableItem {
+ -moz-image-region: rect(185px 48px 201px 32px);
+}
+
+#InsertTableItem:hover {
+ -moz-image-region: rect(185px 64px 201px 48px);
+}
+
+/* ..... smiley menu ..... */
+
+.insert-smile {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-smile.png");
+}
+
+.insert-frown {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-frown.png");
+}
+
+.insert-wink {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-wink.png");
+}
+
+.insert-tongue {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-tongue.png");
+}
+
+.insert-laughing {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-laughing.png");
+}
+
+.insert-embarrassed {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-embarrassed.png");
+}
+
+.insert-undecided {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-undecided.png");
+}
+
+.insert-surprise {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-surprise.png");
+}
+
+.insert-kiss {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-kiss.png");
+}
+
+.insert-yell {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-yell.png");
+}
+
+.insert-cool {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-cool.png");
+}
+
+.insert-money {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-money.png");
+}
+
+.insert-foot {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-foot.png");
+}
+
+.insert-innocent {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-innocent.png");
+}
+
+.insert-cry {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-cry.png");
+}
+
+.insert-sealed {
+ list-style-image: url("chrome://communicator/skin/icons/smileys/smiley-sealed.png");
+}
+
+/* ::::: fg/bg color picker ::::: */
+
+.color-button {
+ margin: 2px;
+ border: 1px inset #5B7693;
+ padding: 0px;
+ width: 14px;
+ height: 12px;
+}
+
+.color-button:hover {
+ border: 1px solid #ffffff;
+}
+
+#TextColorButton {
+ margin-top: 2px;
+ margin-bottom: 9px;
+ margin-inline-start: 2px;
+ margin-inline-end: 9px;
+}
+
+#TextColorButton[color="mixed"] {
+ background-image: url("chrome://editor/skin/icons/multicolor.png");
+ background-size: cover;
+}
+
+#BackgroundColorButton {
+ margin-top: 9px;
+ margin-bottom: 2px;
+ margin-inline-start: 9px;
+ margin-inline-end: 2px;
+}
+
+#HighlightColorButton > .toolbarbutton-icon {
+ margin-inline-end: 0px ! important ;
+}
+
+#HighlightColorButton {
+ -moz-image-region: rect(355px 44px 372px 22px);
+ margin: 4px;
+ border: 1px inset #5B7693;
+ padding: 0px;
+ width: 20px; height: 17px;
+}
+
+#HighlightColorButton:hover {
+ -moz-image-region: rect(355px 66px 372px 44px);
+}
+
+#HighlightColorButton:hover:active {
+ -moz-image-region: rect(355px 88px 372px 66px);
+}
+
+
+#HighlightColorButton[disabled="true"],
+#HighlightColorButton[disabled="true"]:hover,
+#HighlightColorButton[disabled="true"]:hover:active {
+ -moz-image-region: rect(372px 22px 389px 0px);
+}
+
+#absolutePositionButton {
+ -moz-image-region: rect(372px 44px 389px 22px);
+}
+
+#absolutePositionButton:hover {
+ -moz-image-region: rect(372px 66px 389px 44px);
+}
+
+#absolutePositionButton:hover:active {
+ -moz-image-region: rect(372px 88px 389px 66px);
+}
+
+#absolutePositionButton[checked="true"]{
+ -moz-image-region: rect(389px 22px 406px 0px);
+}
+
+#absolutePositionButton[checked="true"]:hover {
+ -moz-image-region: rect(389px 44px 406px 22px);
+}
+
+#absolutePositionButton[checked="true"]:hover:active {
+ -moz-image-region: rect(389px 66px 406px 44px);
+}
+
+#absolutePositionButton[disabled="true"] {
+ -moz-image-region: rect(389px 88px 406px 66px) !important;
+}
+
+#increaseZIndexButton {
+ list-style-image: url("chrome://editor/content/images/bringtofront.png");
+}
+
+#increaseZIndexButton[disabled="true"] {
+ list-style-image: url("chrome://editor/content/images/bringtofront-disabled.png");
+}
+
+#decreaseZIndexButton {
+ list-style-image: url("chrome://editor/content/images/sendtoback.png");
+}
+
+#decreaseZIndexButton[disabled="true"] {
+ list-style-image: url("chrome://editor/content/images/sendtoback-disabled.png");
+}
+
+/* Force the folder location and mail view items to fit in the available width
+ in the Customize Toolbar dialog. */
+#palette-box #paragraph-select-container,
+#palette-box #ParagraphSelect,
+#palette-box #font-face-select-container,
+#palette-box #FontFaceSelect,
+#palette-box #font-size-select-container,
+#palette-box #FontSizeSelect {
+ -moz-box-flex: 1;
+}
diff --git a/comm/suite/themes/modern/editor/editorModeToolbar.css b/comm/suite/themes/modern/editor/editorModeToolbar.css
new file mode 100644
index 0000000000..25ce81355f
--- /dev/null
+++ b/comm/suite/themes/modern/editor/editorModeToolbar.css
@@ -0,0 +1,29 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: edit mode toolbar ::::: */
+
+#EditModeToolbar {
+ border-bottom: 1px solid #B4C3D4;
+ padding: 0px 0px 2px;
+ min-width: 1px;
+}
+
+#NormalModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-normal.png");
+}
+
+#TagModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-tags.png");
+}
+
+#SourceModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-html.png");
+}
+
+#PreviewModeButton {
+ list-style-image: url("chrome://editor/skin/icons/editmode-preview.png");
+}
diff --git a/comm/suite/themes/modern/editor/editorPrimaryToolbar.css b/comm/suite/themes/modern/editor/editorPrimaryToolbar.css
new file mode 100644
index 0000000000..5b0dd2ce6e
--- /dev/null
+++ b/comm/suite/themes/modern/editor/editorPrimaryToolbar.css
@@ -0,0 +1,311 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar masthead ::::: */
+
+#EditToolbar > .toolbar-holder > .toolbar-primary-icon {
+ background-image: url("chrome://editor/skin/icons/mast-editor.png");
+ width: 57px;
+}
+
+.toolbar-primary-icon {
+ width: 57px;
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+.toolbarseparator-primary {
+ width: 9px !important;
+}
+
+#newButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(136px 49px 169px 0);
+}
+
+#newButton:hover {
+ -moz-image-region: rect(136px 99px 169px 50px);
+}
+
+#newButton:hover:active {
+ -moz-image-region: rect(136px 149px 169px 100px);
+}
+
+#newButton[disabled="true"] {
+ -moz-image-region: rect(136px 199px 169px 150px) !important;
+}
+
+#openButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(170px 49px 203px 0);
+}
+
+#openButton:hover {
+ -moz-image-region: rect(170px 99px 203px 50px);
+}
+
+#openButton:hover:active {
+ -moz-image-region: rect(170px 149px 203px 100px);
+}
+
+#openButton[disabled="true"] {
+ -moz-image-region: rect(170px 199px 203px 150px) !important;
+}
+
+#saveButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(238px 49px 271px 0);
+}
+
+#saveButton:hover {
+ -moz-image-region: rect(238px 99px 271px 50px);
+}
+
+#saveButton:hover:active {
+ -moz-image-region: rect(238px 149px 271px 100px);
+}
+
+#saveButton[disabled="true"] {
+ -moz-image-region: rect(238px 199px 271px 150px) !important;
+}
+
+#publishButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(340px 49px 373px 0);
+}
+
+#publishButton:hover {
+ -moz-image-region: rect(340px 99px 373px 50px);
+}
+
+#publishButton:hover:active {
+ -moz-image-region: rect(340px 149px 373px 100px);
+}
+
+#publishButton[disabled="true"] {
+ -moz-image-region: rect(340px 199px 373px 150px) !important;
+}
+
+#previewButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(204px 49px 237px 0);
+}
+
+#previewButton:hover {
+ -moz-image-region: rect(204px 99px 237px 50px);
+}
+
+#previewButton:hover:active {
+ -moz-image-region: rect(204px 149px 237px 100px);
+}
+
+#previewButton[disabled="true"] {
+ -moz-image-region: rect(204px 199px 237px 150px) !important;
+}
+
+/* To workaround that the composer icons are all 33px tall */
+#print-button {
+ -moz-image-region: rect(5px 42px 38px 0);
+}
+
+#print-button:hover {
+ -moz-image-region: rect(5px 84px 38px 42px);
+}
+
+#print-button:hover:active {
+ -moz-image-region: rect(5px 126px 38px 84px);
+}
+
+#print-button[disabled="true"] {
+ -moz-image-region: rect(5px 168px 38px 126px) !important;
+}
+
+#linkButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(102px 49px 135px 0);
+}
+
+#linkButton:hover {
+ -moz-image-region: rect(102px 99px 135px 50px);
+}
+
+#linkButton:hover:active {
+ -moz-image-region: rect(102px 149px 135px 100px);
+}
+
+#linkButton[disabled="true"] {
+ -moz-image-region: rect(102px 199px 135px 150px) !important;
+}
+
+#imageButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(68px 49px 101px 0);
+}
+
+#imageButton:hover {
+ -moz-image-region: rect(68px 99px 101px 50px);
+}
+
+#imageButton:hover:active {
+ -moz-image-region: rect(68px 149px 101px 100px);
+}
+
+#imageButton[disabled="true"] {
+ -moz-image-region: rect(68px 199px 101px 150px) !important;
+}
+
+#formButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(510px 49px 543px 0);
+}
+
+#formButton:hover {
+ -moz-image-region: rect(510px 99px 543px 50px);
+}
+
+#formButton:hover:active {
+ -moz-image-region: rect(510px 149px 543px 100px);
+}
+
+#formButton[disabled="true"] {
+ -moz-image-region: rect(510px 199px 543px 150px) !important;
+}
+
+#namedAnchorButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(0 49px 33px 0);
+}
+
+#namedAnchorButton:hover {
+ -moz-image-region: rect(0 99px 33px 50px);
+}
+
+#namedAnchorButton:hover:active {
+ -moz-image-region: rect(0 149px 33px 100px);
+}
+
+#namedAnchorButton[disabled="true"] {
+ -moz-image-region: rect(0 199px 33px 150px) !important;
+}
+
+#hlineButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(34px 49px 67px 0);
+}
+
+#hlineButton:hover {
+ -moz-image-region: rect(34px 99px 67px 50px);
+}
+
+#hlineButton:hover:active {
+ -moz-image-region: rect(34px 149px 67px 100px);
+}
+
+#hlineButton[disabled="true"] {
+ -moz-image-region: rect(34px 199px 67px 150px) !important;
+}
+
+#tableButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(306px 49px 339px 0);
+}
+
+#tableButton:hover {
+ -moz-image-region: rect(306px 99px 339px 50px);
+}
+
+#tableButton:hover:active {
+ -moz-image-region: rect(306px 149px 339px 100px);
+}
+
+#tableButton[disabled="true"] {
+ -moz-image-region: rect(306px 199px 339px 150px) !important;
+}
+
+#spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(272px 49px 305px 0);
+}
+
+#spellingButton:hover {
+ -moz-image-region: rect(272px 99px 305px 50px);
+}
+
+#spellingButton:hover:active {
+ -moz-image-region: rect(272px 149px 305px 100px);
+}
+
+#spellingButton[disabled="true"] {
+ -moz-image-region: rect(272px 199px 305px 150px) !important;
+}
+
+#cutButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(374px 49px 407px 0);
+}
+
+#cutButton:hover {
+ -moz-image-region: rect(374px 99px 407px 50px);
+}
+
+#cutButton:hover:active {
+ -moz-image-region: rect(374px 149px 407px 100px);
+}
+
+#cutButton[disabled="true"] {
+ -moz-image-region: rect(374px 199px 407px 150px) !important;
+}
+
+#copyButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(408px 49px 441px 0);
+}
+
+#copyButton:hover {
+ -moz-image-region: rect(408px 99px 441px 50px);
+}
+
+#copyButton:hover:active {
+ -moz-image-region: rect(408px 149px 441px 100px);
+}
+
+#copyButton[disabled="true"] {
+ -moz-image-region: rect(408px 199px 441px 150px) !important;
+}
+
+#pasteButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(442px 49px 475px 0);
+}
+
+#pasteButton:hover {
+ -moz-image-region: rect(442px 99px 475px 50px);
+}
+
+#pasteButton:hover:active {
+ -moz-image-region: rect(442px 149px 475px 100px);
+}
+
+#pasteButton[disabled="true"] {
+ -moz-image-region: rect(442px 199px 475px 150px) !important;
+}
+
+#findButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(476px 49px 509px 0);
+}
+
+#findButton:hover {
+ -moz-image-region: rect(476px 99px 509px 50px);
+}
+
+#findButton:hover:active {
+ -moz-image-region: rect(476px 149px 509px 100px);
+}
+
+#findButton[disabled="true"] {
+ -moz-image-region: rect(476px 199px 509px 150px) !important;
+}
diff --git a/comm/suite/themes/modern/editor/icons/btn1.png b/comm/suite/themes/modern/editor/icons/btn1.png
new file mode 100644
index 0000000000..ff849ccb72
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/btn1.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/btn2.png b/comm/suite/themes/modern/editor/icons/btn2.png
new file mode 100644
index 0000000000..e314da5b6b
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/btn2.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/editmode-html.png b/comm/suite/themes/modern/editor/icons/editmode-html.png
new file mode 100644
index 0000000000..a21159669f
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/editmode-html.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/editmode-normal.png b/comm/suite/themes/modern/editor/icons/editmode-normal.png
new file mode 100644
index 0000000000..5161e167b1
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/editmode-normal.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/editmode-preview.png b/comm/suite/themes/modern/editor/icons/editmode-preview.png
new file mode 100644
index 0000000000..c54a61fa91
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/editmode-preview.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/editmode-tags.png b/comm/suite/themes/modern/editor/icons/editmode-tags.png
new file mode 100644
index 0000000000..a1784dada6
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/editmode-tags.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-btm-sel.png b/comm/suite/themes/modern/editor/icons/img-align-btm-sel.png
new file mode 100644
index 0000000000..fd920e634e
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-btm-sel.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-btm.png b/comm/suite/themes/modern/editor/icons/img-align-btm.png
new file mode 100644
index 0000000000..f4d3b83870
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-btm.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-lft.png b/comm/suite/themes/modern/editor/icons/img-align-lft.png
new file mode 100644
index 0000000000..425cd7a238
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-lft.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-mid-sel.png b/comm/suite/themes/modern/editor/icons/img-align-mid-sel.png
new file mode 100644
index 0000000000..b7fa551786
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-mid-sel.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-mid.png b/comm/suite/themes/modern/editor/icons/img-align-mid.png
new file mode 100644
index 0000000000..8baa8c1a36
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-mid.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-rit.png b/comm/suite/themes/modern/editor/icons/img-align-rit.png
new file mode 100644
index 0000000000..3e41c9a1da
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-rit.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-top-sel.png b/comm/suite/themes/modern/editor/icons/img-align-top-sel.png
new file mode 100644
index 0000000000..7cf6558964
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-top-sel.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/img-align-top.png b/comm/suite/themes/modern/editor/icons/img-align-top.png
new file mode 100644
index 0000000000..eaabca9205
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/img-align-top.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/mast-editor.png b/comm/suite/themes/modern/editor/icons/mast-editor.png
new file mode 100644
index 0000000000..f841eba9bf
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/mast-editor.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/multicolor.png b/comm/suite/themes/modern/editor/icons/multicolor.png
new file mode 100644
index 0000000000..ea20f50c5a
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/multicolor.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/progress-busy.png b/comm/suite/themes/modern/editor/icons/progress-busy.png
new file mode 100644
index 0000000000..260775bdda
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/progress-busy.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/progress-done.png b/comm/suite/themes/modern/editor/icons/progress-done.png
new file mode 100644
index 0000000000..ae07a3cb99
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/progress-done.png
Binary files differ
diff --git a/comm/suite/themes/modern/editor/icons/progress-failed.png b/comm/suite/themes/modern/editor/icons/progress-failed.png
new file mode 100644
index 0000000000..e6c54b08d0
--- /dev/null
+++ b/comm/suite/themes/modern/editor/icons/progress-failed.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/about.css b/comm/suite/themes/modern/global/about.css
new file mode 100644
index 0000000000..3ba9673475
--- /dev/null
+++ b/comm/suite/themes/modern/global/about.css
@@ -0,0 +1,65 @@
+/* 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/. */
+
+@import url("chrome://global/skin/plugins.css");
+
+html {
+ background: #FFF;
+}
+
+body {
+ color: #22262F;
+ font: message-box;
+ position: relative;
+ min-width: 330px;
+ max-width: 50em;
+ margin: 4em auto;
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ background: #C7D0D9;
+ text-align: left;
+}
+
+.aboutPageWideContainer {
+ max-width: 80%;
+}
+
+#aboutLogoContainer {
+ border: 1px solid #494F5D;
+ width: 300px;
+ margin-bottom: 2em;
+}
+
+img {
+ border: 0;
+}
+
+#version {
+ font-weight: bold;
+ color: #000;
+ margin-top: -24px;
+ margin-bottom: 9px;
+ margin-inline-start: 10px;
+ margin-inline-end: 0px;
+ font-size: 130%;
+}
+
+ul {
+ margin: 0;
+ margin-inline-start: 1.5em;
+ padding: 0;
+ list-style: square;
+ font-size: 110%;
+}
+
+ul > li {
+ margin-top: .5em;
+}
+
+.columns {
+ -moz-column-width: 20em;
+ -moz-column-gap: 3em;
+}
diff --git a/comm/suite/themes/modern/global/aboutCache.css b/comm/suite/themes/modern/global/aboutCache.css
new file mode 100644
index 0000000000..49e9015ee4
--- /dev/null
+++ b/comm/suite/themes/modern/global/aboutCache.css
@@ -0,0 +1,50 @@
+/* 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/. */
+
+h2 {
+ margin-top: 1em;
+}
+
+table {
+ table-layout: fixed;
+ width: 100%;
+ margin-bottom: 1em;
+}
+
+th {
+ width: 14em;
+ white-space: nowrap;
+ text-align: end;
+}
+
+td {
+ font-family: -moz-fixed;
+ word-wrap: break-word;
+}
+
+#col-key {
+ width: 52%;
+}
+
+#col-dataSize,
+#col-fetchCount,
+#col-lastModified,
+#col-expires {
+ width: 12%;
+}
+
+#entries > tbody > tr > td {
+ padding: .5em 0;
+ text-align: center;
+}
+
+#entries > thead > tr > th {
+ text-align: center;
+ white-space: normal;
+}
+
+#entries > thead > tr > th:first-child,
+#entries > tbody > tr > td:first-child {
+ text-align: start;
+}
diff --git a/comm/suite/themes/modern/global/aboutCacheEntry.css b/comm/suite/themes/modern/global/aboutCacheEntry.css
new file mode 100644
index 0000000000..2ea1bf7ba6
--- /dev/null
+++ b/comm/suite/themes/modern/global/aboutCacheEntry.css
@@ -0,0 +1,27 @@
+/* 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/. */
+
+body {
+ display: table;
+}
+
+table {
+ table-layout: fixed;
+ width: 100%;
+}
+
+th {
+ width: 12em;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ vertical-align: top;
+ text-align: end;
+}
+
+td {
+ display: block;
+ font-family: -moz-fixed;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
diff --git a/comm/suite/themes/modern/global/aboutMemory.css b/comm/suite/themes/modern/global/aboutMemory.css
new file mode 100644
index 0000000000..295ef6a847
--- /dev/null
+++ b/comm/suite/themes/modern/global/aboutMemory.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+@import url("chrome://global/content/aboutMemory.css");
+@import url("chrome://global/skin/button.css");
+
+html {
+ background-color: #FFF;
+}
+
+div.section, div.opsRow {
+ background-color: #C7D0D9;
+}
+
+h2 {
+ background-color: #90A1B3;
+}
+
+a:link, a:visited, a:active {
+ color: #313063;
+}
+
+input[type="checkbox"] {
+ -moz-appearance: none;
+}
diff --git a/comm/suite/themes/modern/global/aboutSupport.css b/comm/suite/themes/modern/global/aboutSupport.css
new file mode 100644
index 0000000000..b63f309cd2
--- /dev/null
+++ b/comm/suite/themes/modern/global/aboutSupport.css
@@ -0,0 +1,108 @@
+/* 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/. */
+
+@import url("chrome://global/skin/plugins.css");
+@import url("chrome://global/skin/button.css");
+
+body {
+ width: 90%;
+ margin-left: 5%;
+ margin-right: 5%;
+}
+
+.page-subtitle {
+ margin-bottom: 1.5em;
+}
+
+.major-section {
+ margin-top: 2em;
+ margin-bottom: 1em;
+ text-align: start;
+}
+
+button {
+ margin-inline-start: 0;
+ margin-inline-end: 8px;
+}
+
+th.title-column {
+ white-space: nowrap;
+ width: 0px;
+ font-size: medium;
+}
+
+th.column {
+ text-align: start;
+ border-top: 1px dotted #2D3B49;
+ white-space: nowrap;
+ width: 0px;
+}
+
+tr:first-child > th,
+tr:first-child > td {
+ border-top: 0;
+}
+
+.prefs-table {
+ table-layout: fixed;
+}
+
+.name {
+ width: 70%;
+}
+
+.value {
+ width: 30%;
+}
+
+.pref-name,
+.pref-value {
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+#action-box {
+ background-color: #C7D0D9;
+ border: 1px solid #2D3B49;
+ border-radius: 6px;
+ color: #22262F;
+ float: right;
+ margin-top: 2em;
+ margin-bottom: 20px;
+ margin-inline-start: 20px;
+ margin-inline-end: 0;
+ padding: 16px;
+ width: 30%;
+}
+
+#action-box,
+#reset-box,
+#safe-mode-box {
+ display: none;
+}
+
+#action-box:dir(rtl) {
+ float: left;
+}
+
+#reset-box > h3 {
+ margin-top: 0;
+}
+
+#action-box button {
+ display: block;
+}
+
+#verify-place-result {
+ max-height: 200px;
+ overflow: auto;
+}
+
+.block {
+ display: block;
+}
+
+.hidden {
+ display: none;
+}
diff --git a/comm/suite/themes/modern/global/alerts/alert.css b/comm/suite/themes/modern/global/alerts/alert.css
new file mode 100644
index 0000000000..5f0984db7d
--- /dev/null
+++ b/comm/suite/themes/modern/global/alerts/alert.css
@@ -0,0 +1,113 @@
+/* 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/. */
+
+/* ===== alert.css =====================================================
+ == Styles specific to the alerts dialog.
+ ======================================================================= */
+
+@import url("chrome://global/skin/global.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.alertBox {
+ background-color: #C7D0D9;
+}
+
+#alertBox[hasBodyText] > #alertTitleBox,
+#alertBox[hasOrigin] > #alertTitleBox {
+ border-bottom: 1px solid #7B969C;
+}
+
+#alertBox[animate][clicked],
+#alertBox[animate][closing] {
+ animation-duration: 0.75s;
+}
+
+alertBox[hasImage] > box > .alertImageBox {
+ padding: 4px;
+ width: 64px;
+}
+
+.alertTextBox {
+ padding: 4px;
+ width: 255px;
+ -moz-box-flex: 1;
+}
+
+.alertTitle {
+ font-weight: bold;
+ font-size: 110%;
+ padding: 4px;
+ -moz-box-flex: 1;
+}
+
+.alertSource {
+ color: #8C99AB;
+ -moz-box-flex: 1;
+}
+
+#alertImage {
+ max-width: 48px;
+ max-height: 48px;
+ list-style-image: url("chrome://global/skin/alerts/notification-48.png");
+}
+
+#alertNotification {
+ border: ridge #5486DA 4px;
+}
+
+#alertNotification[clickable="true"] {
+ cursor: pointer;
+}
+
+label {
+ cursor: inherit;
+}
+
+.alertText[clickable="true"] {
+ color: #1455D6;
+ text-decoration: underline;
+}
+
+.alertText[clickable="true"]:hover:active {
+ color: #424F63;
+}
+
+@keyframes alert-animation {
+ from {
+ opacity: 0;
+ }
+ 6.25% {
+ opacity: 1;
+ }
+ 93.75% {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+.alertCloseButton {
+ list-style-image: url("chrome://global/skin/icons/closebox.png");
+ padding: 4px;
+ border: none;
+}
+
+#alertFooter {
+ -moz-box-align: end;
+}
+
+#alertSettings {
+ min-width: 0px;
+ list-style-image: url("chrome://mozapps/skin/extensions/utilities.png");
+ margin: 0px;
+ border: none;
+ background-color: transparent;
+}
+
+.button-menu-dropmarker,
+.button-text {
+ display: none;
+}
diff --git a/comm/suite/themes/modern/global/alerts/notification-48.png b/comm/suite/themes/modern/global/alerts/notification-48.png
new file mode 100644
index 0000000000..23ab5a0eb8
--- /dev/null
+++ b/comm/suite/themes/modern/global/alerts/notification-48.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/appPicker.css b/comm/suite/themes/modern/global/appPicker.css
new file mode 100644
index 0000000000..2af61e1eb6
--- /dev/null
+++ b/comm/suite/themes/modern/global/appPicker.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#app-picker {
+ width: 57ch;
+}
+
+#content-description {
+ font-weight: bold;
+}
+
+.listitem-iconic {
+ border-bottom: 1px dotted #C7D0D9;
+}
+
+#content-icon,
+.listcell-icon {
+ margin: 5px;
+ width: 32px;
+ height: 32px;
+}
+
+.listcell-label {
+ padding: 5px;
+}
diff --git a/comm/suite/themes/modern/global/arrow/arrow-dn-dis.gif b/comm/suite/themes/modern/global/arrow/arrow-dn-dis.gif
new file mode 100644
index 0000000000..d1e378c24e
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-dn-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-dn-dis.png b/comm/suite/themes/modern/global/arrow/arrow-dn-dis.png
new file mode 100644
index 0000000000..11b72f18f1
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-dn-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-dn.gif b/comm/suite/themes/modern/global/arrow/arrow-dn.gif
new file mode 100644
index 0000000000..459dfa4130
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-dn.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-dn.png b/comm/suite/themes/modern/global/arrow/arrow-dn.png
new file mode 100644
index 0000000000..527efee994
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-dn.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft-dis.gif b/comm/suite/themes/modern/global/arrow/arrow-lft-dis.gif
new file mode 100644
index 0000000000..70a62b7620
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft-dis.png b/comm/suite/themes/modern/global/arrow/arrow-lft-dis.png
new file mode 100644
index 0000000000..4ea0e174d9
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.gif b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.gif
new file mode 100644
index 0000000000..c22294ba21
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.png b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.png
new file mode 100644
index 0000000000..e6817290b1
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp-end.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft-sharp.gif b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp.gif
new file mode 100644
index 0000000000..ae9b1dd0fb
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft-sharp.png b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp.png
new file mode 100644
index 0000000000..d306dbb626
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft-sharp.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft.gif b/comm/suite/themes/modern/global/arrow/arrow-lft.gif
new file mode 100644
index 0000000000..18d3c0d4a5
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-lft.png b/comm/suite/themes/modern/global/arrow/arrow-lft.png
new file mode 100644
index 0000000000..1a09350721
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-lft.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit-dis.gif b/comm/suite/themes/modern/global/arrow/arrow-rit-dis.gif
new file mode 100644
index 0000000000..e53bd10e2d
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit-dis.png b/comm/suite/themes/modern/global/arrow/arrow-rit-dis.png
new file mode 100644
index 0000000000..48074d503a
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.gif b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.gif
new file mode 100644
index 0000000000..c1b3750d4c
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.png b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.png
new file mode 100644
index 0000000000..0ced7e34af
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp-end.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit-sharp.gif b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp.gif
new file mode 100644
index 0000000000..ca628ba69b
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit-sharp.png b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp.png
new file mode 100644
index 0000000000..e662e73090
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit-sharp.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit.gif b/comm/suite/themes/modern/global/arrow/arrow-rit.gif
new file mode 100644
index 0000000000..fdbb09773b
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-rit.png b/comm/suite/themes/modern/global/arrow/arrow-rit.png
new file mode 100644
index 0000000000..299d40ed2d
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-rit.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-up-dis.gif b/comm/suite/themes/modern/global/arrow/arrow-up-dis.gif
new file mode 100644
index 0000000000..0484fe7d2a
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-up-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-up-dis.png b/comm/suite/themes/modern/global/arrow/arrow-up-dis.png
new file mode 100644
index 0000000000..dfee46b783
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-up-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-up.gif b/comm/suite/themes/modern/global/arrow/arrow-up.gif
new file mode 100644
index 0000000000..bbba9d43dc
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-up.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/arrow/arrow-up.png b/comm/suite/themes/modern/global/arrow/arrow-up.png
new file mode 100644
index 0000000000..76adae3649
--- /dev/null
+++ b/comm/suite/themes/modern/global/arrow/arrow-up.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/autocomplete.css b/comm/suite/themes/modern/global/autocomplete.css
new file mode 100644
index 0000000000..bbc747db4a
--- /dev/null
+++ b/comm/suite/themes/modern/global/autocomplete.css
@@ -0,0 +1,105 @@
+/* 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/. */
+
+/* ===== autocomplete.css =================================================
+ == Styles used by the autocomplete widget.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* ::::: autocomplete ::::: */
+
+textbox[type="autocomplete"] {
+ cursor: default !important;
+}
+
+textbox[nomatch="true"][highlightnonmatches="true"] {
+ color: #F00;
+}
+
+.textbox-input-box {
+ -moz-box-align: center;
+}
+
+/* ::::: history button ::::: */
+
+.autocomplete-history-dropmarker {
+ margin: 2px;
+ padding: 0;
+ border: none !important;
+ background-color: transparent !important;
+}
+
+.autocomplete-history-dropmarker[open="true"] {
+ margin-top: 3px;
+ margin-bottom: 1px;
+ margin-inline-start: 3px;
+ margin-inline-end: 1px;
+}
+
+/* ::::: autocomplete popups ::::: */
+
+panel[type="autocomplete"],
+panel[type="autocomplete-richlistbox"],
+.autocomplete-history-popup {
+ -moz-appearance: none;
+ border: 1px solid;
+ border-color: #6B747E;
+ background-color: #FFFFFF;
+}
+
+.autocomplete-history-popup {
+ max-height: 180px;
+}
+
+/* ::::: tree ::::: */
+
+.autocomplete-tree {
+ border: none;
+ background-color: transparent !important;
+}
+
+.autocomplete-treecol {
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px !important;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text {
+ padding-inline-start: 8px;
+ color: #000000;
+}
+
+.autocomplete-treebody::-moz-tree-row(selected) {
+ background-color: #9499AC;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(selected) {
+ color: #FFFFFF !important;
+}
+
+.autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue) {
+ max-width: 16px;
+ height: 16px;
+}
+
+/* ::::: richlistbox autocomplete ::::: */
+
+.autocomplete-richlistbox {
+ -moz-appearance: none;
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px !important;
+}
+
+/* ::::: textboxes inside toolbarpaletteitems ::::: */
+
+toolbarpaletteitem > toolbaritem > textbox > hbox > hbox > html|*.textbox-input {
+ visibility: hidden;
+}
+
+toolbarpaletteitem > toolbaritem > * > textbox > hbox > hbox > html|*.textbox-input {
+ visibility: hidden;
+}
diff --git a/comm/suite/themes/modern/global/button.css b/comm/suite/themes/modern/global/button.css
new file mode 100644
index 0000000000..216791cc3e
--- /dev/null
+++ b/comm/suite/themes/modern/global/button.css
@@ -0,0 +1,127 @@
+/* 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/. */
+
+/* ===== button.css =====================================================
+ == Styles used by the XUL (and XHTML in netError.xhtml) button element.
+ ======================================================================= */
+
+/* :::::::::: button :::::::::: */
+
+button {
+ margin: 2px;
+ border: 1px solid;
+ padding: 3px;
+ border-color: #5F5F5F;
+ border-radius: 2px;
+ min-width: 6em;
+ background-color: #AAB6C4;
+ background-clip: padding-box;
+ color: #000000;
+ font: menu;
+}
+
+.button-icon {
+ margin-inline-start: 2px;
+}
+
+.button-text {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 4px !important;
+ margin-inline-end: 6px !important;
+ text-align: center;
+}
+
+/* .......... focused state .......... */
+
+button:focus {
+ border-color: #000000;
+}
+
+/* .......... active/open/checked state .......... */
+
+button:hover:active,
+button[checked="true"],
+button[open="true"] {
+ border-color: #000000;
+ background-color: #90A1B3;
+ color: #FFFFFF;
+}
+
+button:hover:active:focus,
+button[checked="true"]:focus,
+button[open="true"]:focus {
+ border: 2px solid;
+ border-color: #000000;
+}
+
+/* .......... disabled state .......... */
+
+button[disabled="true"] {
+ border-color: #8290A5 !important;
+ background-color: #B7BFCB !important;
+ color: #8C99AB !important;
+}
+
+/* .......... default state .......... */
+
+button[default="true"] {
+ border-color: #000000;
+}
+
+button[default="true"]:hover:active {
+ border-color: #000000;
+ border-radius: 2px;
+}
+
+button[default="true"]:hover:active:focus {
+ border-color: #000000;
+}
+
+button[default="true"]:focus {
+ border-color: #000000;
+}
+
+/* ::::: menu/menu-button buttons ::::: */
+
+button[type="menu-button"] {
+ border: none;
+ background: transparent !important;
+}
+
+.button-menubutton-button {
+ margin: 0;
+}
+
+.button-menu-dropmarker,
+.button-menubutton-dropmarker {
+ margin: 1px;
+ background-color: transparent;
+ border: none;
+}
+
+.button-menubutton-dropmarker[open="true"] {
+ margin-top: 2px;
+ margin-bottom: 0px;
+ margin-inline-start: 2px;
+ margin-inline-end: 0px;
+}
+
+/* ::::: plain buttons ::::: */
+
+button.plain,
+button.plain:hover,
+button.plain:hover:active
+{
+ margin: 0px;
+ border: none !important;
+ padding: 0px;
+}
+
+/* ::::: xhtml buttons ::::: */
+
+button::-moz-focus-inner {
+ padding: 0px;
+ border: 0px none;
+}
diff --git a/comm/suite/themes/modern/global/button/tbmbtn-arrow-act.gif b/comm/suite/themes/modern/global/button/tbmbtn-arrow-act.gif
new file mode 100644
index 0000000000..fadbe25025
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmbtn-arrow-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmbtn-arrow-act.png b/comm/suite/themes/modern/global/button/tbmbtn-arrow-act.png
new file mode 100644
index 0000000000..0cf89767ed
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmbtn-arrow-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.gif b/comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.gif
new file mode 100644
index 0000000000..65d8d50c59
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.png b/comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.png
new file mode 100644
index 0000000000..db21aa0524
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmbtn-arrow-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmbtn-arrow.gif b/comm/suite/themes/modern/global/button/tbmbtn-arrow.gif
new file mode 100644
index 0000000000..2f9e3ac67c
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmbtn-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmbtn-arrow.png b/comm/suite/themes/modern/global/button/tbmbtn-arrow.png
new file mode 100644
index 0000000000..2f7d89c498
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmbtn-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmenu-arrow-act.gif b/comm/suite/themes/modern/global/button/tbmenu-arrow-act.gif
new file mode 100644
index 0000000000..6f1a8a0fb9
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmenu-arrow-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmenu-arrow-act.png b/comm/suite/themes/modern/global/button/tbmenu-arrow-act.png
new file mode 100644
index 0000000000..291f25a8ca
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmenu-arrow-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmenu-arrow-dis.gif b/comm/suite/themes/modern/global/button/tbmenu-arrow-dis.gif
new file mode 100644
index 0000000000..ad7fdb61ca
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmenu-arrow-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmenu-arrow-dis.png b/comm/suite/themes/modern/global/button/tbmenu-arrow-dis.png
new file mode 100644
index 0000000000..f490d05edb
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmenu-arrow-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmenu-arrow.gif b/comm/suite/themes/modern/global/button/tbmenu-arrow.gif
new file mode 100644
index 0000000000..1d8fbda3d7
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmenu-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/button/tbmenu-arrow.png b/comm/suite/themes/modern/global/button/tbmenu-arrow.png
new file mode 100644
index 0000000000..d9f193db27
--- /dev/null
+++ b/comm/suite/themes/modern/global/button/tbmenu-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox.css b/comm/suite/themes/modern/global/checkbox.css
new file mode 100644
index 0000000000..b787096a9c
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox.css
@@ -0,0 +1,69 @@
+/* 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/. */
+
+/* ===== checkbox.css ===================================================
+ == Styles used by the XUL checkbox element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: checkbox ::::: */
+
+checkbox {
+ -moz-box-align: center;
+ margin: 0px 2px;
+ border: 2px solid transparent;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+}
+
+.checkbox-icon {
+ margin-inline-start: 2px;
+}
+
+/* ..... focused state ..... */
+
+checkbox:focus {
+ border-color: #98A5B2;
+ border-radius: 4px 5px;
+}
+
+/* ..... disabled state ..... */
+
+checkbox[disabled="true"] > .checkbox-label-box > .checkbox-label {
+ color: #8C99AB !important;
+}
+
+/* ::::: checkmark image ::::: */
+
+.checkbox-check {
+ width: 13px;
+ height: 13px;
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+checkbox:hover:active > .checkbox-check {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-act.png");
+}
+
+.checkbox-check[disabled="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-dis.png") !important
+}
+
+/* ..... checked state ..... */
+
+.checkbox-check[checked="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+checkbox:hover:active > .checkbox-check[checked="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-act-check.png");
+}
+
+.checkbox-check[checked="true"][disabled="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-dis-check.png") !important;
+}
+
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-act-check.gif b/comm/suite/themes/modern/global/checkbox/cbox-act-check.gif
new file mode 100644
index 0000000000..1775859c91
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-act-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-act-check.png b/comm/suite/themes/modern/global/checkbox/cbox-act-check.png
new file mode 100644
index 0000000000..fc6c1b400e
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-act-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-act.gif b/comm/suite/themes/modern/global/checkbox/cbox-act.gif
new file mode 100644
index 0000000000..f8a9e004ec
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-act.png b/comm/suite/themes/modern/global/checkbox/cbox-act.png
new file mode 100644
index 0000000000..712d7cd2d5
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-check.gif b/comm/suite/themes/modern/global/checkbox/cbox-check.gif
new file mode 100644
index 0000000000..22f133bc5e
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-check.png b/comm/suite/themes/modern/global/checkbox/cbox-check.png
new file mode 100644
index 0000000000..7676c399e4
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-dis-check.gif b/comm/suite/themes/modern/global/checkbox/cbox-dis-check.gif
new file mode 100644
index 0000000000..c34cf95d3c
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-dis-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-dis-check.png b/comm/suite/themes/modern/global/checkbox/cbox-dis-check.png
new file mode 100644
index 0000000000..4d71c80634
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-dis-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-dis.gif b/comm/suite/themes/modern/global/checkbox/cbox-dis.gif
new file mode 100644
index 0000000000..52f3c2353f
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox-dis.png b/comm/suite/themes/modern/global/checkbox/cbox-dis.png
new file mode 100644
index 0000000000..b8ed5d3ddf
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox.gif b/comm/suite/themes/modern/global/checkbox/cbox.gif
new file mode 100644
index 0000000000..5fecf79d03
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/checkbox/cbox.png b/comm/suite/themes/modern/global/checkbox/cbox.png
new file mode 100644
index 0000000000..0c0355c438
--- /dev/null
+++ b/comm/suite/themes/modern/global/checkbox/cbox.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/colorpicker.css b/comm/suite/themes/modern/global/colorpicker.css
new file mode 100644
index 0000000000..28ccc2b30d
--- /dev/null
+++ b/comm/suite/themes/modern/global/colorpicker.css
@@ -0,0 +1,57 @@
+/* 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/. */
+
+/* ===== colorpicker.css ================================================
+ == Styles used by the XUL colorpicker element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: colorpicker button ::::: */
+
+colorpicker[type="button"] {
+ width: 38px;
+ height: 24px;
+ border: 4px solid transparent;
+ background-color: #C7D0D9;
+ padding: 3px;
+}
+
+.colorpicker-button-colorbox {
+ border: 1px solid #000000;
+}
+
+colorpicker[type="button"][open="true"] {
+ border-color: #3B414F;
+ background-color: #A6B3C0;
+}
+
+colorpicker[type="button"]:focus {
+ border-color: #98A5B2;
+}
+
+colorpicker[type="button"][open="true"]:focus {
+ border-color: #98A5B2;
+}
+
+/* ::::: colorpicker tiles ::::: */
+
+.colorpickertile {
+ width: 20px;
+ height: 20px;
+ margin: 1px;
+ border: 1px inset #C7D0D9;
+}
+
+.colorpickertile[selected="true"] {
+ border: 2px outset #C0C0C0;
+}
+
+.colorpickertile[hover="true"] {
+ border: 2px dotted #FFFFFF;
+}
+
+.cp-light[hover="true"] {
+ border : 2px dotted #909090;
+}
diff --git a/comm/suite/themes/modern/global/config.css b/comm/suite/themes/modern/global/config.css
new file mode 100644
index 0000000000..c4864c7adb
--- /dev/null
+++ b/comm/suite/themes/modern/global/config.css
@@ -0,0 +1,72 @@
+/* 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/. */
+
+/* ===== config.css ==============================================
+ == Styles for about:config
+ ======================================================================= */
+
+@import url("chrome://global/skin/global.css");
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: warning screen ::::: */
+
+#warningScreen
+{
+ background-color: #FFF;
+}
+
+#warningBox
+{
+ background-color: #C7D0D9;
+ background-image: url("chrome://global/skin/icons/warning-48.png");
+ background-repeat: no-repeat;
+ background-position: 30px 3em;
+ color: #22262F;
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 78px;
+ margin: 0 1em;
+}
+
+.title, .description
+{
+ max-width: 50em;
+ padding-inline-start: 3em;
+}
+
+#warningTitle
+{
+ margin: 0 0 .6em;
+ font-size: 160%;
+ border-bottom: 1px solid #7A8490;
+}
+
+#warningText
+{
+ font-size: 110%;
+ margin-inline-start: 0;
+}
+
+#warningButton
+{
+ margin-top: 0.6em;
+}
+
+#showWarningNextTime
+{
+ margin-top: 0.6em;
+}
+
+/* ::::: tree rows ::::: */
+
+treechildren::-moz-tree-cell-text(user)
+{
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(locked)
+{
+ font-style: italic;
+}
diff --git a/comm/suite/themes/modern/global/dialog.css b/comm/suite/themes/modern/global/dialog.css
new file mode 100644
index 0000000000..686f9fcf31
--- /dev/null
+++ b/comm/suite/themes/modern/global/dialog.css
@@ -0,0 +1,16 @@
+/* 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/. */
+
+/* ===== dialog.css =====================================================
+ == Styles used by the XUL dialog element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: dialog ::::: */
+
+dialog {
+ padding: 7px 5px 5px;
+}
+
diff --git a/comm/suite/themes/modern/global/dirListing/dirListing.css b/comm/suite/themes/modern/global/dirListing/dirListing.css
new file mode 100644
index 0000000000..2b7412f51d
--- /dev/null
+++ b/comm/suite/themes/modern/global/dirListing/dirListing.css
@@ -0,0 +1,107 @@
+/* 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/. */
+
+:root {
+ background-color: #FFF;
+ font: message-box;
+ padding-left: 2em;
+ padding-right: 2em;
+}
+
+body {
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ padding: 3em;
+ min-width: 30em;
+ max-width: 65em;
+ margin: 4em auto;
+ background-color: #C7D0D9;
+ color: #22262F;
+}
+
+h1 {
+ font-size: 160%;
+ margin: 0 0 .6em;
+ border-bottom: 1px solid #7A8490;
+ font-weight: normal;
+}
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+p {
+ font-size: 110%;
+}
+
+#UI_goUp {
+ margin-top: 0;
+ float: left;
+}
+
+body[dir="rtl"] #UI_goUp {
+float: right;
+}
+
+#UI_showHidden {
+ margin-top: 0;
+ float: right;
+}
+
+#UI_showHidden:not(:hover) {
+ color: #8C99AB;
+}
+
+body[dir="rtl"] #UI_showHidden {
+ float: left;
+}
+
+table {
+ clear: both;
+ width: 90%;
+ margin: 0 auto;
+}
+
+thead {
+ font-size: 130%;
+}
+
+/* last modified */
+th:last-child {
+ text-align: center;
+}
+
+th:hover > a {
+ text-decoration: underline;
+}
+
+body > table > tbody > tr:hover {
+ outline: 1px solid #98A5B2;
+ -moz-outline-radius: .3em;
+}
+
+td:not(:first-child) {
+ width: 0;
+}
+
+.up {
+ padding: 0 .5em;
+ margin-inline-start: 24px;
+}
+
+.up::before {
+ margin-inline-end: 4px;
+ margin-inline-start: -24px;
+ vertical-align: middle;
+ content: url("chrome://global/skin/filepicker/folder-up.png");
+}
+
+.dir::before {
+ content: url("chrome://communicator/skin/places/bookmark-folder-closed.png");
+}
+
diff --git a/comm/suite/themes/modern/global/dropmarker.css b/comm/suite/themes/modern/global/dropmarker.css
new file mode 100644
index 0000000000..6a6c7c1c13
--- /dev/null
+++ b/comm/suite/themes/modern/global/dropmarker.css
@@ -0,0 +1,24 @@
+/* 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/. */
+
+dropmarker {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border: 3px solid;
+ border-top-color: #BBC4D1;
+ border-right-color: #99A7B7;
+ border-bottom-color: #99A7B7;
+ border-left-color: #BBC4D1;
+ border-radius: 2px;
+ padding-top: 3px;
+ padding-bottom: 2px;
+ background-color: #A6B3C0;
+ list-style-image: url("chrome://global/skin/menulist/mlist-arrow.png");
+}
+
+dropmarker[disabled="true"] {
+ border-color: #727D8E !important;
+ background-color: #B7BFCB !important;
+ list-style-image: url("chrome://global/skin/menulist/mlist-dis-arrow.png") !important;
+}
diff --git a/comm/suite/themes/modern/global/filefield.css b/comm/suite/themes/modern/global/filefield.css
new file mode 100644
index 0000000000..4a7b05b63e
--- /dev/null
+++ b/comm/suite/themes/modern/global/filefield.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* File Field Widget */
+filefield {
+ margin: 2px 4px;
+ border: 1px solid #5D616E;
+}
+
+.fileFieldContentBox {
+ background-color: #C7D0D9;
+}
+
+filefield[disabled="true"] {
+ border-color: #98A5B2;
+}
+
+.fileFieldIcon[disabled="true"] {
+ opacity: 0.4;
+}
+
+.fileFieldIcon {
+ width: 16px;
+ height: 16px;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ margin-inline-start: 1px;
+ margin-inline-end: 4px;
+}
+
+.fileFieldLabel {
+ border: none;
+ margin: 0px;
+}
diff --git a/comm/suite/themes/modern/global/filepicker.css b/comm/suite/themes/modern/global/filepicker.css
new file mode 100644
index 0000000000..8faebdc84e
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker.css
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/* ===== filepicker.css =================================================
+ == Styles used by the File Picker dialog.
+ ======================================================================= */
+
+@import url("chrome://global/skin/global.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: column widths ::::: */
+
+#FilenameColumn,
+#ContentLengthColumn,
+#LastModifiedDateColumn {
+ width: 100px;
+}
+
+/* ::::: file/directory items ::::: */
+
+treechildren::-moz-tree-image(treecolAutoCompleteValue),
+treechildren::-moz-tree-image(FilenameColumn) {
+ margin-inline-end: 2px;
+}
+
+treechildren::-moz-tree-image(treecolAutoCompleteValue, directory),
+treechildren::-moz-tree-image(FilenameColumn, directory) {
+ list-style-image: url("chrome://global/skin/filepicker/dir-closed.png");
+}
+
+treechildren::-moz-tree-image(treecolAutoCompleteValue, file),
+treechildren::-moz-tree-image(FilenameColumn, file) {
+ list-style-image: url("chrome://global/skin/filepicker/blank.png");
+}
+
+/* ::::: button items ::::: */
+
+.up-button {
+ list-style-image: url("chrome://global/skin/filepicker/folder-up.png");
+ min-width: 0%; /* don't let XUL layout min-size override our max-width setting */
+ min-height: 0%;
+ max-width: 36px;
+}
+
+.home-button {
+ list-style-image: url("chrome://global/skin/filepicker/folder-home.png");
+ min-width: 0%; /* don't let XUL layout min-size override our max-width setting */
+ min-height: 0%;
+ max-width: 36px;
+}
+
+.new-dir-button {
+ list-style-image: url("chrome://global/skin/filepicker/folder-new.png");
+ min-width: 0%; /* don't let XUL layout min-size override our max-width setting */
+ min-height: 0%;
+ max-width: 36px;
+}
diff --git a/comm/suite/themes/modern/global/filepicker/blank.gif b/comm/suite/themes/modern/global/filepicker/blank.gif
new file mode 100644
index 0000000000..e9233162fc
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/blank.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/blank.png b/comm/suite/themes/modern/global/filepicker/blank.png
new file mode 100644
index 0000000000..39a5ea7a1f
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/blank.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/dir-closed.gif b/comm/suite/themes/modern/global/filepicker/dir-closed.gif
new file mode 100644
index 0000000000..2487b6cfe6
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/dir-closed.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/dir-closed.png b/comm/suite/themes/modern/global/filepicker/dir-closed.png
new file mode 100644
index 0000000000..38cbc836ee
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/dir-closed.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/dir-open.gif b/comm/suite/themes/modern/global/filepicker/dir-open.gif
new file mode 100644
index 0000000000..245a0002df
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/dir-open.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/dir-open.png b/comm/suite/themes/modern/global/filepicker/dir-open.png
new file mode 100644
index 0000000000..c923d5cff6
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/dir-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/folder-home.gif b/comm/suite/themes/modern/global/filepicker/folder-home.gif
new file mode 100644
index 0000000000..bcc09c3254
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/folder-home.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/folder-home.png b/comm/suite/themes/modern/global/filepicker/folder-home.png
new file mode 100644
index 0000000000..cc722e2640
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/folder-home.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/folder-new.gif b/comm/suite/themes/modern/global/filepicker/folder-new.gif
new file mode 100644
index 0000000000..c48f63af85
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/folder-new.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/folder-new.png b/comm/suite/themes/modern/global/filepicker/folder-new.png
new file mode 100644
index 0000000000..a3bcaa42eb
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/folder-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/folder-up.gif b/comm/suite/themes/modern/global/filepicker/folder-up.gif
new file mode 100644
index 0000000000..8f622d1647
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/folder-up.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/filepicker/folder-up.png b/comm/suite/themes/modern/global/filepicker/folder-up.png
new file mode 100644
index 0000000000..1b715959f7
--- /dev/null
+++ b/comm/suite/themes/modern/global/filepicker/folder-up.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/findBar.css b/comm/suite/themes/modern/global/findBar.css
new file mode 100644
index 0000000000..add2f5f2ba
--- /dev/null
+++ b/comm/suite/themes/modern/global/findBar.css
@@ -0,0 +1,98 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.findbar-closebutton {
+ list-style-image: url("chrome://global/skin/icons/close.png");
+ padding: 0 !important;
+ margin-top: 3px !important;
+ margin-bottom: 3px !important;
+ margin-inline-start: 6px !important;
+ margin-inline-end: 4px !important;
+ border: none;
+}
+
+.findbar-closebutton:hover {
+ list-style-image: url("chrome://global/skin/icons/close-hov.png");
+}
+
+.findbar-closebutton:hover:active {
+ list-style-image: url("chrome://global/skin/icons/close-act.png");
+}
+
+findbar {
+ min-width: 1px;
+ background-color: #C7D0D9;
+ border-top: 1px solid #EBF4FF;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+}
+
+/* find-next button */
+
+.findbar-find-next {
+ list-style-image: url("chrome://global/skin/icons/find.png");
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.findbar-find-next[disabled="true"] {
+ -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
+}
+
+/* find-previous button */
+
+.findbar-find-previous {
+ list-style-image: url("chrome://global/skin/icons/find.png");
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.findbar-find-previous[disabled="true"] {
+ -moz-image-region: rect(16px, 32px, 32px, 16px) !important;
+}
+
+/* highlight button */
+
+.findbar-highlight {
+ list-style-image: url("chrome://global/skin/icons/find.png");
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+.findbar-highlight[disabled="true"] {
+ -moz-image-region: rect(16px, 48px, 32px, 32px) !important;
+}
+
+.findbar-highlight:hover:active {
+ -moz-image-region: rect(32px, 48px, 48px, 32px);
+}
+
+.findbar-highlight[checked="true"] {
+ -moz-image-region: rect(32px, 48px, 48px, 32px);
+}
+
+.find-status-icon {
+ list-style-image: none;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 8px;
+ margin-inline-end: 0;
+ width: 16px;
+ height: 16px;
+}
+
+.find-status-icon[status="notfound"] {
+ list-style-image: url("chrome://global/skin/icons/notfound.png");
+}
+
+.findbar-textbox[status="notfound"] {
+ background-color: #FFCCCC;
+}
+
+.findbar-textbox[flash="true"] {
+ background-color: #FFFF00;
+}
+
+.find-status-icon[status="wrapped"] {
+ list-style-image: url("chrome://global/skin/icons/wrap.png");
+}
diff --git a/comm/suite/themes/modern/global/global.css b/comm/suite/themes/modern/global/global.css
new file mode 100644
index 0000000000..2fe9344fc0
--- /dev/null
+++ b/comm/suite/themes/modern/global/global.css
@@ -0,0 +1,309 @@
+/* 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/. */
+
+/* ===== global.css =====================================================
+ == Styles that apply everywhere.
+ ======================================================================= */
+
+/* all localizable skin settings shall live here */
+@import url("chrome://global/locale/intl.css");
+@import url("chrome://global/skin/menu.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: XBL bindings ::::: */
+
+toolbarbutton[type="menu-button"] {
+ -moz-binding: url("chrome://global/skin/globalBindings.xml#toolbar-menu-button");
+}
+
+toolbarbutton.devtools-toolbarbutton[type="menu-button"] {
+ -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#menu-button");
+}
+
+.menulist-compact {
+ -moz-binding: url("chrome://global/skin/globalBindings.xml#menulist-compact");
+}
+
+/* ::::: root elements ::::: */
+
+window,
+page,
+dialog,
+wizard,
+prefwindow {
+ background-color: #C7D0D9;
+ color: #000000;
+ font: message-box;
+}
+
+[wait-cursor] {
+ cursor: wait !important;
+}
+
+/* deprecated */
+window.dialog {
+ padding: 7px 5px 5px;
+}
+
+/* ::::: alert icons :::::*/
+
+.message-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/information-48.png");
+}
+
+.alert-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/warning-48.png");
+}
+
+.error-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/error-48.png");
+}
+
+.question-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/question-48.png");
+}
+
+.authentication-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/authentication-48.png");
+}
+
+/* ::::: iframe ::::: */
+
+iframe {
+ border: none;
+ width: 100px;
+ height: 100px;
+ min-width: 10px;
+ min-height: 10px;
+}
+
+/* Override resizer.css for "normal" resizers */
+resizer {
+ background-color: transparent;
+}
+
+/* ::::: miscellaneous ::::: */
+
+.toolbar-focustarget {
+ -moz-user-focus: ignore !important;
+}
+
+description.error {
+ color: #FF0000;
+}
+/* :::::: autoscroll popup ::::: */
+
+.autoscroller {
+ height: 28px;
+ width: 28px;
+ border: none;
+ margin: -14px;
+ padding: 0;
+ background-image: url("chrome://global/skin/icons/autoscroll.png");
+ background-color: transparent;
+ -moz-appearance: none;
+ -moz-window-shadow: none;
+}
+
+.autoscroller[scrolldir="NS"] {
+ background-position: left center;
+}
+
+.autoscroller[scrolldir="EW"] {
+ background-position: left bottom;
+}
+
+.autoscroller[transparent="true"] {
+ background-position: center top;
+}
+
+.autoscroller[transparent="true"][scrolldir="NS"] {
+ background-position: center center;
+}
+
+.autoscroller[transparent="true"][scrolldir="EW"] {
+ background-position: center bottom;
+}
+
+.autoscroller[translucent="true"] {
+ background-position: right top;
+}
+
+.autoscroller[translucent="true"][scrolldir="NS"] {
+ background-position: right center;
+}
+
+.autoscroller[translucent="true"][scrolldir="EW"] {
+ background-position: right bottom;
+}
+
+/* :::::: Close button icons ::::: */
+
+.close-icon {
+ list-style-image: url("chrome://global/skin/icons/close.png");
+}
+
+.close-icon:hover {
+ list-style-image: url("chrome://global/skin/icons/close-hov.png");
+}
+
+.close-icon:hover:active {
+ list-style-image: url("chrome://global/skin/icons/close-act.png");
+}
+
+/* ===== taken from formatting.css ===== */
+
+/* ::::: inset areas ::::: */
+
+.inset {
+ border: 1px inset #C7D0D9;
+ margin: 0px 5px 5px 5px;
+}
+
+/* ::::: formatting ::::: */
+
+.outset {
+ border: 1px outset #C7D0D9;
+}
+
+/* ::::: separator rules ::::: */
+
+/* ..... standard separators ..... */
+
+separator, separator[orient="horizontal"] {
+ height: 1.5em;
+}
+
+separator[orient="vertical"] {
+ width: 1.5em;
+}
+
+/* ..... thinner separators ..... */
+
+separator.thin, separator.thin[orient="horizontal"] {
+ height: 0.5em;
+}
+
+separator.thin[orient="vertical"] {
+ width: 0.5em;
+}
+
+/* ..... groove separators ..... */
+
+separator.groove,
+separator.groove[orient="horizontal"] {
+ border-top: 1px solid #7A8490;
+ border-bottom: 1px solid #FEFEFE;
+ height: 0px;
+ margin-top: 0.4em;
+ margin-bottom: 0.4em;
+}
+
+separator.groove[orient="vertical"] {
+ border-left: 1px solid #7A8490;
+ border-right: 1px solid #FEFEFE;
+ margin: 0 .4em;
+}
+
+/* ..... thin groove separators ..... */
+
+separator.groove-thin {
+ border-top: 2px groove #D7D9E0;
+ height: 0px;
+}
+
+separator[orient="vertical"].groove-thin {
+ border-left: 2px groove #D7D9E0;
+}
+
+/* ::::: text formatting rules ::::: */
+
+/* ..... label (with margins) ..... */
+
+/* reduced margin for some UI */
+.small-margin {
+ margin: 1px 2px 1px 2px;
+}
+
+.plain {
+ margin: 0px !important;
+ border: none;
+ padding: 0px;
+}
+
+description, label {
+ cursor: default;
+}
+
+description {
+ margin: 1px 5px 4px 5px;
+}
+
+label {
+ margin: 1px 5px 2px 5px;
+}
+
+description[disabled="true"],
+label[disabled="true"] {
+ color: #8C99AB;
+}
+
+.tooltip-label {
+ margin: 0px;
+}
+
+.header {
+ font-weight: bold;
+}
+
+.monospace {
+ font-family: monospace;
+}
+
+.indent {
+ margin-inline-start: 20px;
+}
+
+.box-padded {
+ padding: 5px;
+}
+
+.spaced {
+ margin: 3px 5px 4px 5px;
+}
+
+/* class for text with a 'link' appearance */
+.text-link {
+ color: blue;
+ text-decoration: underline;
+}
+
+.text-link:focus {
+ color: red;
+ /* Don't specify the outline-color, we should always use initial value. */
+ outline: 1px dotted;
+}
+
+.text-link:hover {
+ cursor: pointer;
+}
+
+.text-link:hover:active {
+ color: red;
+}
+
+.text-link[visited="true"] {
+ color: purple;
+}
diff --git a/comm/suite/themes/modern/global/globalBindings.xml b/comm/suite/themes/modern/global/globalBindings.xml
new file mode 100644
index 0000000000..0e31150286
--- /dev/null
+++ b/comm/suite/themes/modern/global/globalBindings.xml
@@ -0,0 +1,70 @@
+<?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/. -->
+
+
+<bindings id="globalBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <!-- :::/ widgets \ ::::::::::::::::::::::::::::::::::::::::::::::::::::: -->
+
+ <binding id="menulist-compact" display="xul:menu"
+ extends="chrome://global/content/bindings/menulist.xml#menulist">
+ <content sizetopopup="false">
+ <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
+ <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
+ <children includes="menupopup"/>
+ </content>
+ </binding>
+
+ <binding id="toolbar-menu-button" display="xul:menu"
+ extends="chrome://global/content/bindings/toolbarbutton.xml#menu-button">
+ <content>
+ <children includes="observes|template|menupopup|tooltip"/>
+ <xul:stack class="box-inherit toolbarbutton-menubutton-stack" flex="1">
+ <xul:toolbarbutton class="box-inherit toolbarbutton-menubutton-button"
+ anonid="button" allowevents="true"
+ xbl:inherits="disabled,image,label,crop,accesskey,command,
+ align,dir,pack,orient"/>
+ <xul:dropmarker type="menu-button" xbl:inherits="open,disabled"
+ class="toolbarbutton-menubutton-dropmarker"/>
+ </xul:stack>
+ </content>
+ </binding>
+
+ <!-- :::/ mini scrollbar \ ::::::::::::::::::::::::::::::::::::::::::::::::: -->
+
+ <binding id="iframe-miniscroll" extends="chrome://global/content/bindings/general.xml#iframe">
+ <content usechromesheets="chrome://global/skin/scrollbars-mini.css"/>
+ </binding>
+
+ <binding id="browser-miniscroll" extends="chrome://global/content/bindings/browser.xml#browser">
+ <content usechromesheets="chrome://global/skin/scrollbars-mini.css"/>
+ </binding>
+
+ <binding id="scrollbar-mini" extends="chrome://global/content/bindings/scrollbar.xml#scrollbar">
+ <resources>
+ <stylesheet src="chrome://global/skin/scrollbars-mini.css"/>
+ </resources>
+ </binding>
+
+ <!-- :::/ misc scrollbar \ ::::::::::::::::::::::::::::::::::::::::::::::::: -->
+
+ <binding id="row-iconic" extends="xul:row">
+ <content>
+ <xul:hbox align="center">
+ <xul:image class="row-iconic-icon"/>
+ <children/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <!-- Hack to prevent Add-ons Manager window transparency on Mac (bug 415934) -->
+
+ <binding id="unifiedWindow"/>
+
+</bindings>
diff --git a/comm/suite/themes/modern/global/groupbox.css b/comm/suite/themes/modern/global/groupbox.css
new file mode 100644
index 0000000000..41ae48c5bd
--- /dev/null
+++ b/comm/suite/themes/modern/global/groupbox.css
@@ -0,0 +1,27 @@
+/* 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/. */
+
+/* ===== groupbox.css ==================================================
+ == Styles used by the XUL groupbox and related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: groupbox ::::: */
+
+groupbox {
+ margin: 5px;
+ border: 1px solid #858B97;
+ padding: 5px;
+}
+
+.groupbox-body {
+ padding: inherit;
+}
+
+caption {
+ margin: 0px 6px 2px;
+ padding: 0px 3px;
+ background-color: #C7D0D9;
+}
diff --git a/comm/suite/themes/modern/global/icons/Error.png b/comm/suite/themes/modern/global/icons/Error.png
new file mode 100644
index 0000000000..512a1b010e
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/Error.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/Question.png b/comm/suite/themes/modern/global/icons/Question.png
new file mode 100644
index 0000000000..19304ac5e6
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/Question.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/authentication-48.png b/comm/suite/themes/modern/global/icons/authentication-48.png
new file mode 100644
index 0000000000..cfd1a7144f
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/authentication-48.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/autoscroll.png b/comm/suite/themes/modern/global/icons/autoscroll.png
new file mode 100644
index 0000000000..95794f822f
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/autoscroll.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/blacklist_favicon.png b/comm/suite/themes/modern/global/icons/blacklist_favicon.png
new file mode 100644
index 0000000000..c347fb36aa
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/blacklist_favicon.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/blacklist_large.png b/comm/suite/themes/modern/global/icons/blacklist_large.png
new file mode 100644
index 0000000000..96ff341c0a
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/blacklist_large.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close-act.gif b/comm/suite/themes/modern/global/icons/close-act.gif
new file mode 100644
index 0000000000..3d2fc64d02
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close-act.png b/comm/suite/themes/modern/global/icons/close-act.png
new file mode 100644
index 0000000000..a860ea07bd
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close-dis.gif b/comm/suite/themes/modern/global/icons/close-dis.gif
new file mode 100644
index 0000000000..42d0a09961
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close-dis.png b/comm/suite/themes/modern/global/icons/close-dis.png
new file mode 100644
index 0000000000..d79c92749c
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close-hov.gif b/comm/suite/themes/modern/global/icons/close-hov.gif
new file mode 100644
index 0000000000..451d92d0c4
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close-hov.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close-hov.png b/comm/suite/themes/modern/global/icons/close-hov.png
new file mode 100644
index 0000000000..b5a7e6ed8f
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close.gif b/comm/suite/themes/modern/global/icons/close.gif
new file mode 100644
index 0000000000..e0a4cff20c
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/close.png b/comm/suite/themes/modern/global/icons/close.png
new file mode 100644
index 0000000000..7055768b7d
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/close.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/closebox.gif b/comm/suite/themes/modern/global/icons/closebox.gif
new file mode 100644
index 0000000000..da69691601
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/closebox.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/closebox.png b/comm/suite/themes/modern/global/icons/closebox.png
new file mode 100644
index 0000000000..609b18cf99
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/closebox.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/error-16.png b/comm/suite/themes/modern/global/icons/error-16.png
new file mode 100644
index 0000000000..f2b01a13eb
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/error-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/error-24.png b/comm/suite/themes/modern/global/icons/error-24.png
new file mode 100644
index 0000000000..6b81eb7933
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/error-24.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/error-48.png b/comm/suite/themes/modern/global/icons/error-48.png
new file mode 100644
index 0000000000..a233945b0f
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/error-48.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/error-64.png b/comm/suite/themes/modern/global/icons/error-64.png
new file mode 100644
index 0000000000..92c11533ee
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/error-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/find.png b/comm/suite/themes/modern/global/icons/find.png
new file mode 100644
index 0000000000..4fb70d2766
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/find.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/information-16.png b/comm/suite/themes/modern/global/icons/information-16.png
new file mode 100644
index 0000000000..dad31249ed
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/information-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/information-24.png b/comm/suite/themes/modern/global/icons/information-24.png
new file mode 100644
index 0000000000..b8120e5be8
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/information-24.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/information-32.png b/comm/suite/themes/modern/global/icons/information-32.png
new file mode 100644
index 0000000000..af7974c132
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/information-32.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/information-48.png b/comm/suite/themes/modern/global/icons/information-48.png
new file mode 100644
index 0000000000..e8b6bd6468
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/information-48.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/information-64.png b/comm/suite/themes/modern/global/icons/information-64.png
new file mode 100644
index 0000000000..bd1ead88c6
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/information-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/notfound.png b/comm/suite/themes/modern/global/icons/notfound.png
new file mode 100644
index 0000000000..dad31249ed
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/notfound.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-landscape-small.gif b/comm/suite/themes/modern/global/icons/pg-landscape-small.gif
new file mode 100644
index 0000000000..96a9381049
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-landscape-small.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-landscape-small.png b/comm/suite/themes/modern/global/icons/pg-landscape-small.png
new file mode 100644
index 0000000000..b518415cae
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-landscape-small.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-landscape.gif b/comm/suite/themes/modern/global/icons/pg-landscape.gif
new file mode 100644
index 0000000000..fe94aba114
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-landscape.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-landscape.png b/comm/suite/themes/modern/global/icons/pg-landscape.png
new file mode 100644
index 0000000000..cfd9c70732
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-landscape.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-portrait-small.gif b/comm/suite/themes/modern/global/icons/pg-portrait-small.gif
new file mode 100644
index 0000000000..4fc6745bc8
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-portrait-small.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-portrait-small.png b/comm/suite/themes/modern/global/icons/pg-portrait-small.png
new file mode 100644
index 0000000000..141ba8f629
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-portrait-small.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-portrait.gif b/comm/suite/themes/modern/global/icons/pg-portrait.gif
new file mode 100644
index 0000000000..01840302e2
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-portrait.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/pg-portrait.png b/comm/suite/themes/modern/global/icons/pg-portrait.png
new file mode 100644
index 0000000000..8c47256c9e
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/pg-portrait.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/question-16.png b/comm/suite/themes/modern/global/icons/question-16.png
new file mode 100644
index 0000000000..80ec9dd12f
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/question-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/question-24.png b/comm/suite/themes/modern/global/icons/question-24.png
new file mode 100644
index 0000000000..151b244991
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/question-24.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/question-48.png b/comm/suite/themes/modern/global/icons/question-48.png
new file mode 100644
index 0000000000..d055385dac
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/question-48.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/question-64.png b/comm/suite/themes/modern/global/icons/question-64.png
new file mode 100644
index 0000000000..85ea0c8b5c
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/question-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/resizer-rtl.png b/comm/suite/themes/modern/global/icons/resizer-rtl.png
new file mode 100644
index 0000000000..d6bb324401
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/resizer-rtl.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/resizer.png b/comm/suite/themes/modern/global/icons/resizer.png
new file mode 100644
index 0000000000..d6841f9b1e
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/resizer.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/search.gif b/comm/suite/themes/modern/global/icons/search.gif
new file mode 100644
index 0000000000..2b7b08dc26
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/search.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/search.png b/comm/suite/themes/modern/global/icons/search.png
new file mode 100644
index 0000000000..85f6bfb61f
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/search.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/sslWarning.png b/comm/suite/themes/modern/global/icons/sslWarning.png
new file mode 100644
index 0000000000..09946986fe
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/sslWarning.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/warning-16.png b/comm/suite/themes/modern/global/icons/warning-16.png
new file mode 100644
index 0000000000..cd6537c2ea
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/warning-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/warning-24.png b/comm/suite/themes/modern/global/icons/warning-24.png
new file mode 100644
index 0000000000..c12a5c3de0
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/warning-24.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/warning-32.png b/comm/suite/themes/modern/global/icons/warning-32.png
new file mode 100644
index 0000000000..d5a70bdbf0
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/warning-32.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/warning-48.png b/comm/suite/themes/modern/global/icons/warning-48.png
new file mode 100644
index 0000000000..8c988cc5d5
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/warning-48.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/warning-64.png b/comm/suite/themes/modern/global/icons/warning-64.png
new file mode 100644
index 0000000000..2208e9b58a
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/warning-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/icons/wrap.png b/comm/suite/themes/modern/global/icons/wrap.png
new file mode 100644
index 0000000000..c2043877ce
--- /dev/null
+++ b/comm/suite/themes/modern/global/icons/wrap.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/listbox.css b/comm/suite/themes/modern/global/listbox.css
new file mode 100644
index 0000000000..87cedcfe59
--- /dev/null
+++ b/comm/suite/themes/modern/global/listbox.css
@@ -0,0 +1,137 @@
+/* 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/. */
+
+/* ===== listbox.css =======================================================
+ == Styles used by XUL listbox-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: listbox ::::: */
+
+listbox {
+ margin: 2px 4px;
+ border: 1px solid #5D616E;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+listbox[disabled="true"] {
+ color: #999999;
+}
+
+/* ::::: listitem ::::: */
+
+listitem {
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+}
+
+listbox:focus > listitem[current="true"] {
+ border-top-color: #000000;
+ border-bottom-color: #000000;
+}
+
+listitem[selected="true"] {
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+listbox:focus > listitem[selected="true"] {
+ background-color: #424F63;
+ color: #FFFFFF;
+}
+
+listcell {
+ padding-top: 0px;
+ padding-bottom: 1px;
+ padding-inline-start: 2px;
+ padding-inline-end: 0px;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+ margin: 0px 2px;
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+.listcell-check[checked="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+.listcell-check[disabled="true"] {
+ border-color: #999999;
+ list-style-image: url("chrome://global/skin/checkbox/cbox-dis.png");
+}
+
+.listcell-check[disabled="true"][checked="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-dis-check.png");
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+ -moz-box-align: center;
+ border: 1px solid;
+ border-top-color: #C7D0D9;
+ border-right-color: #63676B;
+ border-bottom-color: #63676B;
+ border-left-color: #C7D0D9;
+ padding: 0 4px;
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+listheader[sortable="true"]:hover:active {
+ border-top: 1px solid #A5ABB0;
+ border-right: 1px solid #A5ABB0;
+ border-bottom: 1px solid #A5ABB0;
+ border-left: 1px solid #A5ABB0;
+ padding-top: 1px;
+ padding-bottom: 0px;
+ padding-inline-start: 5px;
+ padding-inline-end: 4px;
+}
+
+.listheader-icon {
+ margin-inline-end: 2px;
+}
+
+.listheader-label {
+ margin: 0px !important;
+}
+
+/* ..... sort direction icon ..... */
+
+.listheader-sortdirection {
+ list-style-image: none;
+}
+
+.listheader-sortdirection[sortDirection="ascending"] {
+ list-style-image: url("chrome://global/skin/tree/sort-asc.png");
+}
+
+.listheader-sortdirection[sortDirection="descending"] {
+ list-style-image: url("chrome://global/skin/tree/sort-dsc.png");
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+ margin: 0px !important;
+ padding-top: 0px;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 0px;
+ white-space: nowrap;
+}
+
+.listcell-icon {
+ margin-inline-end: 2px;
+}
+
+.listcell-label[disabled="true"] {
+ color: #999999;
+}
diff --git a/comm/suite/themes/modern/global/media/TopLevelImageDocument.css b/comm/suite/themes/modern/global/media/TopLevelImageDocument.css
new file mode 100644
index 0000000000..5242175161
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/TopLevelImageDocument.css
@@ -0,0 +1,17 @@
+/* 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/. */
+
+@media not print {
+ /* N.B.: Remember to update ImageDocument.css in the tree or reftests may fail! */
+
+ body {
+ color: #eee;
+ background-image: url("chrome://global/skin/media/imagedoc-darknoise.png");
+ }
+
+ img.transparent {
+ background: hsl(0,0%,90%) url("chrome://global/skin/media/imagedoc-lightnoise.png");
+ color: #222;
+ }
+}
diff --git a/comm/suite/themes/modern/global/media/TopLevelVideoDocument.css b/comm/suite/themes/modern/global/media/TopLevelVideoDocument.css
new file mode 100644
index 0000000000..2d857ced2d
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/TopLevelVideoDocument.css
@@ -0,0 +1,12 @@
+/* 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/. */
+
+body {
+ background-image: url("chrome://global/skin/media/imagedoc-darknoise.png");
+ background-color: rgb(33,33,33); /* Average color of that ^ image. */
+}
+
+video {
+ box-shadow: 0 0 5px rgba(0,0,0,0.6);
+}
diff --git a/comm/suite/themes/modern/global/media/closedCaptionButton.svg b/comm/suite/themes/modern/global/media/closedCaptionButton.svg
new file mode 100644
index 0000000000..0c55071956
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/closedCaptionButton.svg
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #48a0f7;
+ }
+ use[id$="-active"] {
+ fill: #2d89e6;
+ }
+ use[id$="-focus"] {
+ fill: #48a0f7;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+ <symbol id="cc-off-shape">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M16.531,16.107H5.267l1.982-2H15c0.6,0,1-0.4,1-1V5.274
+ l1.946-1.964C17.963,3.399,18,3.483,18,3.576v11.031C18,15.407,17.331,16.107,16.531,16.107z M14.016,8.506h-1.218l1.005-1.014
+ C13.913,7.789,13.984,8.128,14.016,8.506z M11.786,12.361c-0.828,0-1.476-0.326-1.913-0.902l1.09-1.101
+ c0.136,0.323,0.374,0.541,0.796,0.541c0.514,0,0.695-0.44,0.756-1.014h1.535C13.908,11.43,13.071,12.361,11.786,12.361z
+ M1.496,16.106C0.697,16.104,0,15.406,0,14.607V3.576c0-0.8,0.7-1.5,1.5-1.5h12.846L16.299,0l1.316,1.283L2.615,17.13L1.496,16.106
+ z M3,4.107c-0.6,0-1,0.4-1,1v8c0,0.6,0.4,1,1,1h0.029l2.031-2.16c-0.757-0.503-1.191-1.457-1.191-2.744
+ c0-1.936,1.069-3.14,2.428-3.14c1.357,0,2.136,0.76,2.361,2.059l3.777-4.016H3z M8.298,8.506H7.355
+ c-0.047-0.623-0.49-1.23-0.99-1.23c-0.561,0-1.337,0.84-1.337,1.995c0,0.674,0.381,1.427,0.95,1.702L8.298,8.506z"/>
+ </symbol>
+
+ <symbol id="cc-shape">
+ <path d="M16.531,1.984H1.5c-0.8,0-1.5,0.7-1.5,1.5v11.031c0,0.8,0.7,1.5,1.5,1.5h15.031
+ c0.8,0,1.469-0.7,1.469-1.5V3.484C18,2.684,17.331,1.984,16.531,1.984z M16,13.016c0,0.6-0.4,1-1,1H3c-0.6,0-1-0.4-1-1v-8
+ c0-0.6,0.4-1,1-1h12c0.6,0,1,0.4,1,1V13.016z M6.426,10.807c-0.811,0-0.96-0.789-0.96-1.628c0-1.155,0.338-1.745,0.899-1.745
+ c0.5,0,0.818,0.357,0.866,0.98h1.484C8.585,6.877,7.785,5.972,6.297,5.972c-1.359,0-2.428,1.205-2.428,3.14
+ c0,1.944,0.974,3.157,2.583,3.157c1.285,0,2.153-0.93,2.295-2.476H7.244C7.183,10.367,6.94,10.807,6.426,10.807z M11.759,10.807
+ c-0.811,0-0.96-0.789-0.96-1.628c0-1.155,0.338-1.745,0.899-1.745c0.5,0,0.756,0.357,0.803,0.98h1.515
+ c-0.129-1.537-0.898-2.443-2.385-2.443c-1.359,0-2.396,1.205-2.396,3.14c0,1.944,0.943,3.157,2.552,3.157
+ c1.285,0,2.122-0.93,2.264-2.476h-1.535C12.454,10.367,12.273,10.807,11.759,10.807z"/>
+ </symbol>
+ <use id="cc" xlink:href="#cc-shape"/>
+ <use id="cc-hover" xlink:href="#cc-shape"/>
+ <use id="cc-active" xlink:href="#cc-shape"/>
+ <use id="cc-focus" xlink:href="#cc-shape"/>
+ <use id="cc-disabled" xlink:href="#cc-shape"/>
+
+ <use id="cc-off" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-hover" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-active" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-focus" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-disabled" xlink:href="#cc-off-shape"/>
+</svg>
diff --git a/comm/suite/themes/modern/global/media/error.png b/comm/suite/themes/modern/global/media/error.png
new file mode 100644
index 0000000000..30ee8eb0b3
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/error.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/media/fullscreenButton.svg b/comm/suite/themes/modern/global/media/fullscreenButton.svg
new file mode 100644
index 0000000000..b39fa3407a
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/fullscreenButton.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #C7D0D9;
+ }
+ use[id$="-active"] {
+ fill: #6B7B8D;
+ }
+ use[id$="-focus"] {
+ fill: #C7D0D9;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+ <symbol id="fullscreen-shape">
+ <path d="M6.728,10.188l-3.235,3.094l0.017-2.267l-1.513-0.016l0,5l4.987-0.008l0.011-1.537l-2.281-0.022
+ l3.097-3.158L6.728,10.188z M14.453,11.004l-0.022,2.281l-3.158-3.097l-1.086,1.083l3.094,3.235l-2.267-0.017l-0.016,1.514l5,0
+ l-0.008-4.988L14.453,11.004z M11.015,2.01l-0.011,1.537l2.281,0.022l-3.097,3.158l1.083,1.086l3.235-3.094L14.49,6.986
+ l1.513,0.016v-5L11.015,2.01z M6.986,3.511l0.016-1.514l-5,0L2.01,6.985l1.537,0.011l0.022-2.281l3.158,3.097l1.086-1.083
+ L4.718,3.494L6.986,3.511z"/>
+ </symbol>
+ <symbol id="unfullscreen-shape">
+ <path d="M2.047,11.135l-0.011,1.537l2.281,0.022L1.22,15.851l1.083,1.086l3.235-3.094l-0.017,2.268l1.513,0.016
+ l0-5L2.047,11.135z M13.781,12.587l2.267,0.017l0.016-1.514l-5,0l0.008,4.988l1.537,0.011l0.022-2.281l3.158,3.097l1.086-1.083
+ L13.781,12.587z M16.058,5.578l-2.281-0.021l3.097-3.158l-1.083-1.086l-3.235,3.094l0.017-2.267L11.06,2.123v5l4.988-0.008
+ L16.058,5.578z M5.516,2.098L5.494,4.379L2.336,1.283L1.25,2.365L4.344,5.6L2.077,5.583L2.06,7.097l5,0L7.053,2.109L5.516,2.098z"/>
+ </symbol>
+ <use id="fullscreen" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-hover" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-active" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-focus" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-disabled" xlink:href="#fullscreen-shape"/>
+
+ <use id="unfullscreen" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-hover" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-active" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-focus" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-disabled" xlink:href="#unfullscreen-shape"/>
+</svg>
diff --git a/comm/suite/themes/modern/global/media/imagedoc-darknoise.png b/comm/suite/themes/modern/global/media/imagedoc-darknoise.png
new file mode 100644
index 0000000000..5c33e24d4c
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/imagedoc-darknoise.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/media/imagedoc-lightnoise.png b/comm/suite/themes/modern/global/media/imagedoc-lightnoise.png
new file mode 100644
index 0000000000..3467cf4d47
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/imagedoc-lightnoise.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/media/muteButton.svg b/comm/suite/themes/modern/global/media/muteButton.svg
new file mode 100644
index 0000000000..0ac2ab9376
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/muteButton.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #C7D0D9;
+ }
+ use[id$="-active"] {
+ fill: #6B7B8D;
+ }
+ use[id$="-focus"] {
+ fill: #C7D0D9;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+ <symbol id="unmute-shape">
+ <path d="M3.52,5.367c-1.332,0-2.422,1.09-2.422,2.422v2.422c0,1.332,1.09,2.422,2.422,2.422h1.516l4.102,3.633
+ V1.735L5.035,5.367H3.52z M12.059,9c0-0.727-0.484-1.211-1.211-1.211v2.422C11.574,10.211,12.059,9.727,12.059,9z M14.48,9
+ c0-1.695-1.211-3.148-2.785-3.512l-0.363,1.09C12.422,6.82,13.27,7.789,13.27,9c0,1.211-0.848,2.18-1.938,2.422l0.484,1.09
+ C13.27,12.148,14.48,10.695,14.48,9z M12.543,3.188l-0.484,1.09C14.238,4.883,15.691,6.82,15.691,9c0,2.18-1.453,4.117-3.512,4.601
+ l0.484,1.09c2.422-0.605,4.238-2.906,4.238-5.691C16.902,6.215,15.086,3.914,12.543,3.188z"/>
+ </symbol>
+ <symbol id="mute-shape">
+ <path d="M3.52,5.367c-1.332,0-2.422,1.09-2.422,2.422v2.422c0,1.332,1.09,2.422,2.422,2.422h1.516l4.102,3.633
+ V1.735L5.035,5.367H3.52z"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.155,12.066l-1.138-1.138l4.872-4.872l1.138,1.138
+ L12.155,12.066z"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.998,7.204l1.138-1.138l4.872,4.872l-1.138,1.138L10.998,7.204
+ z"/>
+ </symbol>
+ <symbol id="noaudio-shape">
+ <path d="M14.901,3.571l-4.412,3.422V1.919L6.286,5.46H4.869c-1.298,0-2.36,1.062-2.36,2.36v2.36
+ c0,1.062,0.708,1.888,1.652,2.242l-2.242,1.77l1.18,1.416L16.081,4.987L14.901,3.571z M10.489,16.081V11.36l-2.669,2.36
+ L10.489,16.081z"/>
+ </symbol>
+ <use id="unmute" xlink:href="#unmute-shape"/>
+ <use id="unmute-hover" xlink:href="#unmute-shape"/>
+ <use id="unmute-active" xlink:href="#unmute-shape"/>
+ <use id="unmute-focus" xlink:href="#unmute-shape"/>
+ <use id="unmute-disabled" xlink:href="#unmute-shape"/>
+
+ <use id="mute" xlink:href="#mute-shape"/>
+ <use id="mute-hover" xlink:href="#mute-shape"/>
+ <use id="mute-active" xlink:href="#mute-shape"/>
+ <use id="mute-focus" xlink:href="#mute-shape"/>
+ <use id="mute-disabled" xlink:href="#mute-shape"/>
+
+ <use id="noaudio" xlink:href="#noaudio-shape"/>
+</svg>
diff --git a/comm/suite/themes/modern/global/media/pauseButton.svg b/comm/suite/themes/modern/global/media/pauseButton.svg
new file mode 100644
index 0000000000..dfc258194a
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/pauseButton.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 18 18">
+ <path fill="context-fill" d="M6.002,1.953C5.172,1.953,4.5,2.626,4.5,3.455v11.08
+ c0,0.83,0.672,1.502,1.502,1.502c0.829,0,1.502-0.672,1.502-1.502V3.455C7.504,2.626,6.831,1.953,6.002,1.953z M12,1.953
+ c-0.828,0-1.5,0.672-1.5,1.5v11.094c0,0.828,0.672,1.5,1.5,1.5s1.5-0.672,1.5-1.5V3.453C13.5,2.625,12.828,1.953,12,1.953z"/>
+</svg>
+
diff --git a/comm/suite/themes/modern/global/media/playButton.svg b/comm/suite/themes/modern/global/media/playButton.svg
new file mode 100644
index 0000000000..ee8e482a2a
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/playButton.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 18 18">
+ <path fill="context-fill" d="M3.243,15.155c0,0.845,0.593,1.157,1.317,0.707l9.659-6.041c0.727-0.453,0.722-1.193,0-1.645L4.556,2.137
+ C3.827,1.682,3.237,2.014,3.237,2.844v12.312H3.243z"/>
+</svg>
+
diff --git a/comm/suite/themes/modern/global/media/stalled.png b/comm/suite/themes/modern/global/media/stalled.png
new file mode 100644
index 0000000000..525375889b
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/stalled.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/media/throbber.png b/comm/suite/themes/modern/global/media/throbber.png
new file mode 100644
index 0000000000..1c8fe0f5cb
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/throbber.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/media/videocontrols.css b/comm/suite/themes/modern/global/media/videocontrols.css
new file mode 100644
index 0000000000..43e8e1d236
--- /dev/null
+++ b/comm/suite/themes/modern/global/media/videocontrols.css
@@ -0,0 +1,482 @@
+/* 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/. */
+
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace url("http://www.w3.org/1999/xhtml");
+
+video > xul|videocontrols,
+audio > xul|videocontrols {
+ writing-mode: horizontal-tb;
+ width: 100%;
+ height: 100%;
+ display: inline-block;
+}
+
+.controlsContainer [hidden="true"],
+.controlBar[hidden] {
+ display: none;
+}
+
+.controlBar[size="hidden"] {
+ display: none;
+}
+
+.controlsSpacer[hideCursor] {
+ cursor: none;
+}
+
+.controlsContainer,
+.progressContainer {
+ position: relative;
+ height: 100%;
+}
+
+.stackItem {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.statusOverlay {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: rgb(80,80,80, .85);
+}
+
+.controlsOverlay {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ position: relative;
+}
+
+.controlsSpacerStack {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ justify-content: center;
+ align-items: center;
+}
+
+.controlsSpacer {
+ background-color: rgba(255,255,255,.4);
+}
+
+.controlBar {
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ height: 40px;
+ padding: 0 9px;
+ background-color: #A5B3C0;
+ border-style: solid;
+ border-width: 2px;
+ border-color: #000000;
+ border-radius: 5px;
+}
+
+.playButton,
+.muteButton,
+.closedCaptionButton,
+.fullscreenButton {
+ -moz-binding: none;
+ height: 100%;
+ min-height: 30px;
+ min-width: 30px;
+ padding: 6px;
+ border: 0;
+ margin: 0;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-origin: content-box;
+ background-clip: content-box;
+}
+
+.playButton {
+ background-image: url(chrome://global/skin/media/pauseButton.svg);
+ -moz-context-properties: fill;
+ fill: #ffffff;
+}
+.playButton:hover {
+ fill: #C7D0D9;
+}
+.playButton:hover:active {
+ fill: #6B7B8D;
+}
+.playButton[paused] {
+ background-image: url(chrome://global/skin/media/playButton.svg);
+ -moz-context-properties: fill;
+ fill: #ffffff;
+}
+.playButton[paused]:hover {
+ fill: #C7D0D9;
+}
+.playButton[paused]:hover:active {
+ fill: #6B7B8D;
+}
+
+.muteButton {
+ background-image: url(chrome://global/skin/media/muteButton.svg#unmute);
+}
+.muteButton:hover {
+ background-image: url(chrome://global/skin/media/muteButton.svg#unmute-hover);
+}
+.muteButton:hover:active {
+ background-image: url(chrome://global/skin/media/muteButton.svg#unmute-active);
+}
+.muteButton[muted] {
+ background-image: url(chrome://global/skin/media/muteButton.svg#mute);
+}
+.muteButton[muted]:hover {
+ background-image: url(chrome://global/skin/media/muteButton.svg#mute-hover);
+}
+.muteButton[muted]:hover:active {
+ background-image: url(chrome://global/skin/media/muteButton.svg#mute-active);
+}
+.muteButton[noAudio],
+.muteButton[noAudio]:hover,
+.muteButton[noAudio]:hover:active {
+ background-image: url(chrome://global/skin/media/muteButton.svg#noaudio);
+}
+.muteButton[noAudio] + .volumeStack {
+ display: none;
+}
+
+.closedCaptionButton {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off);
+}
+.closedCaptionButton:hover {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off-hover);
+}
+.closedCaptionButton:hover:active {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off-active);
+}
+.closedCaptionButton[enabled] {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc);
+}
+.closedCaptionButton[enabled]:hover {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-hover);
+}
+.closedCaptionButton[enabled]:hover:active {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-active);
+}
+
+.fullscreenButton {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen);
+}
+.fullscreenButton:hover {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen-hover);
+}
+.fullscreenButton:hover:active {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen-active);
+}
+.fullscreenButton[fullscreened] {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen);
+}
+.fullscreenButton[fullscreened]:hover {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen-hover);
+}
+.fullscreenButton[fullscreened]:hover:active {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen-active);
+}
+
+.controlBarSpacer {
+ flex-grow: 1;
+}
+
+.volumeControl::-moz-range-thumb,
+.scrubber::-moz-range-thumb {
+ height: 13px;
+ width: 13px;
+ border-style: solid;
+ border-color: #000000;
+ border-radius: 50%;
+ background-color: #ffffff;
+}
+
+.volumeControl::-moz-focus-outer,
+.scrubber::-moz-focus-outer {
+ border: 0;
+}
+
+.progressBackgroundBar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.progressStack {
+ position: relative;
+ width: 100%;
+ height: 5px;
+}
+
+.scrubberStack {
+ min-width: 48px;
+ flex-basis: 48px;
+ flex-grow: 2;
+ flex-shrink: 0;
+ margin: 0 9px;
+}
+
+.volumeStack {
+ max-width: 60px;
+ min-width: 48px;
+ flex-grow: 1;
+ flex-shrink: 0;
+ margin-right: 6px;
+ margin-left: 4px;
+}
+
+.bufferBar,
+.progressBar,
+.scrubber,
+.volumeBackground,
+.volumeControl {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ border: 0;
+ border-radius: 2.5px;
+ margin: 0;
+ background: none;
+ background-color: transparent;
+}
+
+.bufferBar,
+.volumeBackground {
+ background-color: rgba(0,0,0,0.7);
+}
+
+.bufferBar::-moz-progress-bar,
+.progressBar::-moz-progress-bar,
+.volumeBackground::-moz-meter-bar {
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ border-radius: 2.5px;
+ background: none;
+}
+
+.scrubber:hover::-moz-range-thumb,
+.volumeControl:hover::-moz-range-thumb {
+ background-color: #C7D0D9;
+}
+
+.scrubber:active::-moz-range-thumb,
+.volumeControl:active::-moz-range-thumb {
+ background-color: #6B7B8D;
+}
+
+.scrubber::-moz-range-track,
+.scrubber::-moz-range-progress {
+ background-color: transparent;
+}
+
+.volumeControl::-moz-range-progress,
+.volumeControl::-moz-range-track {
+ height: 5px;
+ border-radius: 2.5px;
+}
+
+.volumeControl::-moz-range-progress {
+ background-color: #ffffff;
+}
+
+.volumeControl::-moz-range-track {
+ background-color: rgba(0,0,0,0.7);
+}
+
+
+.bufferBar::-moz-progress-bar {
+ background-color: rgba(255,255,255,0.3);
+ border-radius: 2.5px;
+}
+
+.progressBar::-moz-progress-bar {
+ background-color: #C7D0D9;
+}
+
+.textTrackList {
+ position: absolute;
+ right: 5px;
+ bottom: 45px;
+ max-width: 80%;
+ border: 1px solid #000000;
+ border-radius: 2.5px;
+ padding: 5px 0;
+ vertical-align: middle;
+ font-size: 12px;
+ background-color: #000000;
+ opacity: 0.7;
+}
+
+.textTrackList > .textTrackItem {
+ display: block;
+ width: 100%;
+ height: 30px;
+ padding: 2px 10px;
+ border: none;
+ margin: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-align: left;
+ text-overflow: ellipsis;
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.textTrackList > .textTrackItem:hover {
+ background-color: #444444;
+}
+
+.textTrackList > .textTrackItem[on] {
+ color: #C7D0D9;
+}
+
+.positionLabel,
+.durationLabel {
+ display: none;
+}
+
+.positionDurationBox {
+ text-align: center;
+ padding-inline-start: 1px;
+ padding-inline-end: 9px;
+ white-space: nowrap;
+ font: message-box;
+ font-size: 13px;
+ font-size-adjust: 0.55;
+ color: #ffffff;
+}
+
+.duration {
+ display: inline-block;
+ white-space: pre;
+ color: #404040;
+}
+
+.statusIcon {
+ width: 36px;
+ height: 36px;
+ margin-bottom: 20px;
+}
+
+.statusIcon[type="throbber"] {
+ background: url(chrome://global/skin/media/throbber.png) no-repeat center;
+}
+
+.statusIcon[type="throbber"][stalled] {
+ background: url(chrome://global/skin/media/stalled.png) no-repeat center;
+}
+
+.statusIcon[type="error"] {
+ min-width: 70px;
+ min-height: 60px;
+ background: url(chrome://global/skin/media/error.png) no-repeat center;
+ background-size: contain;
+}
+
+/* Overlay Play button */
+.clickToPlay {
+ min-width: 48px;
+ min-height: 48px;
+ border-radius: 50%;
+ background-image: url(chrome://global/skin/media/playButton.svg);
+ background-repeat: no-repeat;
+ background-position: 54% 50%;
+ background-size: 40% 40%;
+ background-color: #1a1a1a;
+ -moz-context-properties: fill;
+ fill: #ffffff;
+ opacity: 0.8;
+ position: relative;
+ top: 20px;
+}
+
+.controlsSpacerStack:hover > .clickToPlay,
+.clickToPlay:hover {
+ opacity: 0.55;
+}
+
+.controlsSpacerStack:hover > .clickToPlay[fadeout] {
+ opacity: 0;
+}
+
+.controlBar[fullscreen-unavailable] .fullscreenButton {
+ display: none;
+}
+
+/* CSS Transitions */
+.clickToPlay {
+ transition-property: transform, opacity;
+ transition-duration: 400ms, 400ms;
+}
+
+.controlsSpacer[fadeout] {
+ opacity: 0;
+}
+
+.clickToPlay[fadeout] {
+ transform: scale(3);
+ opacity: 0;
+}
+
+.clickToPlay[fadeout][immediate] {
+ transition-property: opacity, background-size;
+ transition-duration: 0s, 0s;
+}
+.controlBar:not([immediate]) {
+ transition-property: opacity;
+ transition-duration: 200ms;
+}
+.controlBar[fadeout] {
+ opacity: 0;
+}
+.volumeStack:not([immediate]) {
+ transition-property: opacity, margin-top;
+ transition-duration: 200ms, 200ms;
+}
+.statusOverlay:not([immediate]) {
+ transition-property: opacity;
+ transition-duration: 300ms;
+ transition-delay: 750ms;
+}
+.statusOverlay[fadeout] {
+ opacity: 0;
+}
+
+/* Error description formatting */
+.errorLabel {
+ padding: 0 10px;
+ text-align: center;
+ font: message-box;
+ font-size: 14px;
+ color: #ffffff;
+}
+
+.errorLabel {
+ display: none;
+}
+
+[error="errorAborted"] > [anonid="errorAborted"],
+[error="errorNetwork"] > [anonid="errorNetwork"],
+[error="errorDecode"] > [anonid="errorDecode"],
+[error="errorSrcNotSupported"] > [anonid="errorSrcNotSupported"],
+[error="errorNoSource"] > [anonid="errorNoSource"],
+[error="errorGeneric"] > [anonid="errorGeneric"] {
+ display: inline;
+}
diff --git a/comm/suite/themes/modern/global/menu.css b/comm/suite/themes/modern/global/menu.css
new file mode 100644
index 0000000000..5d2df75295
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu.css
@@ -0,0 +1,233 @@
+/* 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/. */
+
+/* ===== menu.css =======================================================
+ == Styles used by XUL menu-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: menu/menuitem ::::: */
+
+menu,
+menuitem {
+ -moz-box-align: center;
+ color: #000000;
+ font: menu;
+ list-style-image: none;
+ -moz-image-region: auto;
+}
+
+menuitem[default="true"] {
+ font-weight: bold;
+}
+
+menu[disabled="true"],
+menuitem[disabled="true"],
+menu[_moz-menuactive="true"][disabled="true"],
+menuitem[_moz-menuactive="true"][disabled="true"] {
+ color: #8C99AB;
+}
+
+/* ..... internal content .... */
+
+.menu-text,
+.menu-iconic-left,
+.menu-iconic-text {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 0px !important;
+ margin-inline-end: 2px !important;
+ color: inherit;
+}
+
+.menu-text {
+ margin-inline-start: 18px !important;
+ font-weight: inherit;
+}
+
+.menu-description {
+ font-style: italic;
+ color: #8C99AB;
+ margin-inline-start: 1ex !important;
+}
+
+.menu-accel,
+.menu-iconic-accel {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 7px !important;
+ margin-inline-end: 2px !important;
+ padding-inline-end: 14px;
+ color: inherit;
+}
+
+.menu-iconic-icon {
+ width: 16px;
+ height: 16px;
+ list-style-image: inherit;
+}
+
+/* ..... menu arrow box ..... */
+
+.menu-right {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 6px;
+ margin-inline-end: 4px;
+ width: 4px;
+ list-style-image: url("chrome://global/skin/menu/menu-arrow.png");
+ -moz-image-region: auto;
+}
+
+.menu-right[_moz-menuactive="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-arrow-hov.png");
+ -moz-image-region: auto;
+}
+
+.menu-right[disabled="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-arrow-dis.png") !important;
+ -moz-image-region: auto;
+}
+
+/* ::::: menu/menuitems in menubar ::::: */
+
+menubar > menu {
+ border: 1px solid transparent;
+ padding: 1px 4px 2px;
+ margin: 1px 0px;
+}
+
+menubar > menu[_moz-menuactive="true"] {
+ border: 1px outset #DDE3EB;
+ background-color: transparent;
+ color: #000000;
+}
+
+menubar > menu[_moz-menuactive="true"][open="true"] {
+ border-style: inset;
+}
+
+/* ..... internal content .... */
+
+.menubar-left {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 2px;
+ color: inherit;
+}
+
+.menubar-text {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 0px !important;
+ margin-inline-end: 2px !important;
+ color: inherit;
+}
+
+/* ::::: menu/menuitems in popups ::::: */
+
+menu,
+menuitem {
+ padding: 2px;
+ max-width: 42em;
+}
+
+menu[_moz-menuactive="true"],
+menuitem[_moz-menuactive="true"] {
+ background-color: #424F63;
+ color: #FFFFFF;
+}
+
+/* ::::: menu/menuitems in menulist popups ::::: */
+
+.menulist-menupopup > menuitem,
+menulist > menupopup > menuitem,
+.menulist-menupopup > menu,
+menulist > menupopup > menu {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ max-width: none;
+ font: message-box;
+}
+
+menulist > menupopup > menuitem {
+ padding-inline-end: 30px;
+}
+
+/* ..... selected state ..... */
+
+menulist > menupopup > menuitem[selected="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-check.png");
+ -moz-image-region: auto;
+}
+
+menulist > menupopup > menuitem[_moz-menuactive="true"][selected="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-check-hov.png");
+ -moz-image-region: auto;
+}
+
+/* ::::: checkbox menuitem ::::: */
+
+menuitem[checked="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-check.png");
+ -moz-image-region: auto;
+}
+
+menuitem[checked="true"][disabled="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-check-dis.png");
+ -moz-image-region: auto;
+}
+
+menuitem[checked="true"][_moz-menuactive="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-check-hov.png");
+ -moz-image-region: auto;
+}
+
+/* ::::: radio menuitem ::::: */
+
+menuitem[checked="true"][type="radio"] {
+ list-style-image: url("chrome://global/skin/menu/menu-radio.png");
+ -moz-image-region: auto;
+}
+
+menuitem[checked="true"][type="radio"][disabled="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-radio-dis.png");
+ -moz-image-region: auto;
+}
+
+menuitem[checked="true"][type="radio"][_moz-menuactive="true"] {
+ list-style-image: url("chrome://global/skin/menu/menu-radio-hov.png");
+ -moz-image-region: auto;
+}
+
+/* ::::: menuseparator ::::: */
+
+menuseparator {
+ margin: 2px 3px;
+ border-top: 1px solid #A5ABC0;
+ border-bottom: 1px solid #EEF1F7;
+}
+
+menulist > menupopup > menuseparator,
+.menulist-menupopup > menuseparator {
+ margin: 2px 0;
+ border-bottom: none;
+}
+
+/* ::::: autocomplete ::::: */
+
+.autocomplete-history-popup > menuitem {
+ max-width: none !important;
+ font: message-box;
+}
+
+/* ::::: tree column picker ::::: */
+
+.treecell-popupcell-menu {
+ margin-inline-start: -2px;
+ list-style-image: url("chrome://global/skin/tree/columnpicker.png");
+ -moz-image-region: auto;
+}
diff --git a/comm/suite/themes/modern/global/menu/menu-arrow-dis.gif b/comm/suite/themes/modern/global/menu/menu-arrow-dis.gif
new file mode 100644
index 0000000000..99860477e4
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-arrow-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-arrow-dis.png b/comm/suite/themes/modern/global/menu/menu-arrow-dis.png
new file mode 100644
index 0000000000..ea3aef3c50
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-arrow-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-arrow-hov.gif b/comm/suite/themes/modern/global/menu/menu-arrow-hov.gif
new file mode 100644
index 0000000000..22a82a66d5
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-arrow-hov.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-arrow-hov.png b/comm/suite/themes/modern/global/menu/menu-arrow-hov.png
new file mode 100644
index 0000000000..a572a4bb96
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-arrow-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-arrow.gif b/comm/suite/themes/modern/global/menu/menu-arrow.gif
new file mode 100644
index 0000000000..921ae65b69
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-arrow.png b/comm/suite/themes/modern/global/menu/menu-arrow.png
new file mode 100644
index 0000000000..0be772e464
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-check-dis.gif b/comm/suite/themes/modern/global/menu/menu-check-dis.gif
new file mode 100644
index 0000000000..ddd8ecdc35
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-check-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-check-dis.png b/comm/suite/themes/modern/global/menu/menu-check-dis.png
new file mode 100644
index 0000000000..46a6b8526a
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-check-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-check-hov.gif b/comm/suite/themes/modern/global/menu/menu-check-hov.gif
new file mode 100644
index 0000000000..03e7e12790
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-check-hov.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-check-hov.png b/comm/suite/themes/modern/global/menu/menu-check-hov.png
new file mode 100644
index 0000000000..e7bda1cec3
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-check-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-check.gif b/comm/suite/themes/modern/global/menu/menu-check.gif
new file mode 100644
index 0000000000..ddd8ecdc35
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-check.png b/comm/suite/themes/modern/global/menu/menu-check.png
new file mode 100644
index 0000000000..46a6b8526a
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-radio-dis.gif b/comm/suite/themes/modern/global/menu/menu-radio-dis.gif
new file mode 100644
index 0000000000..83cb3cdf5d
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-radio-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-radio-dis.png b/comm/suite/themes/modern/global/menu/menu-radio-dis.png
new file mode 100644
index 0000000000..d68b52b9bd
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-radio-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-radio-hov.gif b/comm/suite/themes/modern/global/menu/menu-radio-hov.gif
new file mode 100644
index 0000000000..c9eb82afbb
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-radio-hov.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-radio-hov.png b/comm/suite/themes/modern/global/menu/menu-radio-hov.png
new file mode 100644
index 0000000000..ac61ee33ef
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-radio-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-radio.gif b/comm/suite/themes/modern/global/menu/menu-radio.gif
new file mode 100644
index 0000000000..83cb3cdf5d
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-radio.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menu/menu-radio.png b/comm/suite/themes/modern/global/menu/menu-radio.png
new file mode 100644
index 0000000000..d68b52b9bd
--- /dev/null
+++ b/comm/suite/themes/modern/global/menu/menu-radio.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist.css b/comm/suite/themes/modern/global/menulist.css
new file mode 100644
index 0000000000..aae5b01114
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist.css
@@ -0,0 +1,180 @@
+/* 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/. */
+
+/* ===== menulist.css ===================================================
+ == Styles used by the XUL menulist element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
+
+/* :::::::::: menulist :::::::::: */
+
+menulist {
+ margin: 1px 4px;
+ border: 2px solid transparent;
+ min-height: 19px;
+ color: #000000;
+ font: message-box;
+}
+
+.menulist-label {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 0px !important;
+ margin-inline-end: 2px !important;
+}
+
+.menulist-description {
+ font-style: italic;
+ color: #5D616E;
+ margin-inline-start: 1em !important;
+}
+
+.menulist-icon {
+ margin-inline-end: 2px;
+}
+
+.menulist-label-box {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ padding: 2px;
+ border: 1px solid #000000;
+ border-right: 1px solid transparent;
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+ background-color: #A6B3C0;
+ padding-inline-start: 8px;
+}
+
+.menulist-dropmarker {
+ border: 1px solid black;
+ border-left: 1px solid #6D7C8F;
+ border-top-left-radius: 0px;
+ border-bottom-left-radius: 0px;
+}
+
+.menulist-dropmarker > .dropmarker-icon {
+ margin: 1px 2px 0px;
+}
+
+/* ..... focused state ..... */
+
+menulist:focus {
+ border-color: #98A5B2;
+ border-radius: 7px;
+ background-color: #98A5B2;
+}
+
+/* ..... active state ..... */
+
+menulist[open="true"] {
+ color: #FFFFFF;
+}
+
+menulist[open="true"] > .menulist-label-box {
+ border-top-color: #313948;
+ border-bottom-color: #313948;
+ border-left-color: #313948;
+ background-color: #91A2B4;
+}
+
+menulist[open="true"] > .menulist-dropmarker {
+ list-style-image: url("chrome://global/skin/menulist/mlist-act-arrow.png");
+}
+
+/* ..... disabled state ..... */
+
+menulist[disabled="true"] {
+ color: #8C99AB !important;
+}
+
+menulist[disabled="true"] > .menulist-label-box {
+ border-top-color: #727D8E !important;
+ border-bottom-color: #727D8E !important;
+ background-color: #B7BFCB !important;
+}
+
+menulist[disabled="true"] > .menulist-label-box {
+ border-left-color: #727D8E !important;
+}
+
+menulist[disabled="true"] > .menulist-dropmarker {
+ border-left-color: #95A1AD !important;
+}
+
+/* ::::: editable menulists ::::: */
+
+.menulist-editable-box {
+ border: 1px solid #000000;
+ border-right: none;
+ background-color: #FFFFFF;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-inline-start: 2px;
+ padding-inline-end: 0px;
+}
+
+html|*.menulist-editable-input {
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px !important;
+ background: inherit;
+ font: inherit;
+}
+
+menulist[focused="true"] {
+ border-color: #98A5B2;
+ border-top-right-radius: 7px;
+ border-bottom-right-radius: 7px;
+ background-color: #98A5B2;
+}
+
+/* ::::: compact menulists ::::: */
+
+.menulist-compact {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin: 0;
+ border: 1px solid;
+ border-top-color: #A6B3C0;
+ border-right-color: #5D616E;
+ border-bottom-color: #5D616E;
+ border-left-color: #A6B3C0;
+ border-radius: 0;
+ padding: 1px;
+ min-height: 0px;
+ background-color: #A6B3C0;
+ color: #000000;
+}
+
+.menulist-compact > .menulist-label {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 0px !important;
+ margin-inline-end: 3px !important;
+ text-align: end;
+}
+
+.menulist-compact > .menulist-dropmarker {
+ border: none;
+ background: transparent;
+}
+
+.menulist-compact > .menulist-dropmarker,
+.menulist-compact[open="true"] > .menulist-dropmarker {
+ list-style-image: url("chrome://global/skin/menulist/mlist-compact-arrow.png");
+}
+
+.menulist-compact:focus {
+ border-radius: 0;
+}
+
+.menulist-compact[open="true"] {
+ border-top-color: #788797;
+ border-right-color: #5D616E;
+ border-bottom-color: #5D616E;
+ border-left-color: #788797;
+ background-color: #91A2B4;
+}
diff --git a/comm/suite/themes/modern/global/menulist/mlist-act-arrow.gif b/comm/suite/themes/modern/global/menulist/mlist-act-arrow.gif
new file mode 100644
index 0000000000..79c2777abb
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-act-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-act-arrow.png b/comm/suite/themes/modern/global/menulist/mlist-act-arrow.png
new file mode 100644
index 0000000000..20c978ad90
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-act-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-arrow.gif b/comm/suite/themes/modern/global/menulist/mlist-arrow.gif
new file mode 100644
index 0000000000..0067826651
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-arrow.png b/comm/suite/themes/modern/global/menulist/mlist-arrow.png
new file mode 100644
index 0000000000..aa69aca2d2
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-compact-arrow.gif b/comm/suite/themes/modern/global/menulist/mlist-compact-arrow.gif
new file mode 100644
index 0000000000..41751cbf2f
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-compact-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-compact-arrow.png b/comm/suite/themes/modern/global/menulist/mlist-compact-arrow.png
new file mode 100644
index 0000000000..3fbcf0b362
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-compact-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-dis-arrow.gif b/comm/suite/themes/modern/global/menulist/mlist-dis-arrow.gif
new file mode 100644
index 0000000000..9ca54729bc
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-dis-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/menulist/mlist-dis-arrow.png b/comm/suite/themes/modern/global/menulist/mlist-dis-arrow.png
new file mode 100644
index 0000000000..498285b415
--- /dev/null
+++ b/comm/suite/themes/modern/global/menulist/mlist-dis-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/netError.css b/comm/suite/themes/modern/global/netError.css
new file mode 100644
index 0000000000..3c84a12bdd
--- /dev/null
+++ b/comm/suite/themes/modern/global/netError.css
@@ -0,0 +1,137 @@
+/* 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/. */
+
+/*
+ * This defines the look-and-feel styling of the error pages.
+ * (see: netError.xhtml)
+ *
+ * Original styling by William Price <bugzilla@mob.rice.edu>
+ * Updated by: Steven Garrity <steven@silverorange.com>
+ * Henrik Skupin <mozilla@hskupin.info>
+ * Akihiro Misaki <spitfire.kuden@gmail.com>
+ */
+
+@import url("chrome://global/skin/button.css");
+
+html {
+ background: #FFF;
+}
+
+body {
+ margin: 0;
+ padding: 0 1em;
+ color: #22262F;
+ font: message-box;
+}
+
+h1 {
+ margin: 0 0 .6em;
+ border-bottom: 1px solid #7A8490;
+ font-size: 160%;
+}
+
+ul, ol {
+ margin: 0;
+ margin-inline-start: 1.5em;
+ padding: 0;
+}
+
+ul > li, ol > li {
+ margin-bottom: .5em;
+}
+
+ul {
+ list-style: square;
+}
+
+#errorPageContainer {
+ position: relative;
+ min-width: 13em;
+ max-width: 52em;
+ margin: 4em auto;
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ background: url("chrome://global/skin/icons/warning-48.png") left 0 no-repeat #C7D0D9;
+ background-origin: content-box;
+}
+
+#errorPageContainer.certerror {
+ background-image: url("chrome://global/skin/icons/authentication-48.png");
+}
+
+#errorPageContainer:dir(rtl) {
+ background-position: right 0;
+}
+
+#errorTitle {
+ margin-inline-start: 80px;
+}
+
+#errorLongContent {
+ margin-inline-start: 80px;
+}
+
+#errorShortDesc > p {
+ overflow: auto;
+ border-bottom: 1px solid #7A8490;
+ padding-bottom: 1em;
+ font-size: 130%;
+ white-space: pre-wrap;
+}
+
+#errorLongDesc {
+ padding-inline-end: 3em;
+ font-size: 110%;
+}
+
+#errorPageContainer > #errorTryAgain {
+ margin-top: 2em;
+ margin-inline-start: 80px;
+}
+
+#brand {
+ position: absolute;
+ right: 0;
+ bottom: -1.5em;
+ margin-inline-end: 10px;
+ opacity: .6;
+}
+
+#brand:dir(rtl) {
+ right: auto;
+ left: 0;
+}
+
+#brand > p {
+ margin: 0;
+}
+
+#errorContainer {
+ display: none;
+}
+
+#securityOverrideDiv {
+ padding-top: 10px;
+}
+
+#securityOverrideContent {
+ background-color: #FFFFE7;
+ color: #000000;
+ padding: 10px;
+ border-radius: 10px;
+ display: none;
+}
+
+/* Custom styling for 'blacklist' error class */
+:root.blacklist #errorPageContainer {
+ background-image: url("chrome://global/skin/icons/blacklist_large.png");
+ background-color: #772222;
+ color: #FFFFFF;
+}
+
+:root.blacklist {
+ background: #333333;
+}
diff --git a/comm/suite/themes/modern/global/notification.css b/comm/suite/themes/modern/global/notification.css
new file mode 100644
index 0000000000..30487da13b
--- /dev/null
+++ b/comm/suite/themes/modern/global/notification.css
@@ -0,0 +1,129 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+notification {
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+notification[type="warning"] {
+ background-color: #E8DB99;
+}
+
+notification[type="critical"] {
+ background-color: #FF0000;
+ color: #FFFFFF;
+}
+
+.notification-inner[type="warning"] {
+ border-color: #E8DB99 !important;
+}
+
+.notification-inner[type="critical"] {
+ border-color: #FF0000 !important;
+}
+
+.messageImage {
+ width: 16px;
+ height: 16px;
+ margin-top: 0px;
+ margin-bottom: 3px;
+ margin-inline-start: 5px;
+ margin-inline-end: 1px;
+}
+
+/* Default icons for notifications */
+
+.messageImage[type="info"] {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+}
+
+.messageImage[type="warning"] {
+ list-style-image: url("chrome://global/skin/icons/warning-16.png");
+}
+
+.messageImage[type="critical"] {
+ list-style-image: url("chrome://global/skin/icons/error-16.png");
+}
+
+.messageText {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+ margin-inline-start: 5px !important;
+ margin-inline-end: 1px !important;
+}
+
+.messageCloseButton {
+ list-style-image: url("chrome://global/skin/icons/close.png");
+ padding: 4px 2px;
+ border: none;
+}
+
+.messageCloseButton:hover {
+ list-style-image: url("chrome://global/skin/icons/close-hov.png");
+}
+
+.messageCloseButton:hover:active {
+ list-style-image: url("chrome://global/skin/icons/close-act.png");
+}
+
+/* Popup notifications */
+
+popupnotification {
+ background-color: #C7D0D9;
+}
+
+.popup-notification-description {
+ max-width: 248px;
+ margin-top: 4px !important;
+}
+
+.popup-notification-learnmore-link:not([href]) {
+ display: none;
+}
+
+.popup-notification-closeitem {
+ list-style-image: url("chrome://global/skin/icons/closebox.png");
+}
+
+.popup-notification-button-container {
+ background-color: transparent;
+ display: flex;
+}
+
+.popup-notification-button-container > toolbarseparator {
+ display: none;
+}
+
+.popup-notification-button {
+ flex: 1;
+ min-width: 0;
+}
+
+.popup-notification-button[anonid="secondarybutton"][hidden="true"] ~ .popup-notification-button[default] {
+ flex: 1;
+}
+
+.popup-notification-button > .button-box {
+ padding: 0;
+}
+
+.popup-notification-dropmarker {
+ flex: none;
+ padding: 0 15px;
+}
+
+.popup-notification-dropmarker > .button-box > hbox {
+ display: none;
+}
+
+.popup-notification-dropmarker > .button-box > .button-menu-dropmarker {
+ /* This is to override the linux !important */
+ -moz-appearance: none !important;
+ display: -moz-box;
+ padding: 0;
+ margin: 0;
+}
diff --git a/comm/suite/themes/modern/global/plugins.css b/comm/suite/themes/modern/global/plugins.css
new file mode 100644
index 0000000000..ca612c13d2
--- /dev/null
+++ b/comm/suite/themes/modern/global/plugins.css
@@ -0,0 +1,101 @@
+/* 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/. */
+
+/* ===== plugins.css =====================================================
+ == Styles used by the about:plugins dialog.
+ ======================================================================= */
+
+body {
+ background-color: #FFFFFF;
+ color: #22262F;
+ font: message-box;
+ text-align: start;
+}
+
+a:link, a:visited, a:active {
+ color: #313063;
+}
+
+hr {
+ display: none;
+}
+
+div#outside {
+ text-align: justify;
+ width: 90%;
+ margin: 0 5%;
+}
+
+div#plugs {
+ text-align: center;
+ font-size: x-large;
+ font-weight: bold;
+ color: #5D616E;
+}
+
+div#noplugs {
+ font-size: large;
+ font-weight: bold;
+ color: #5D616E;
+}
+
+div#findmore {
+ margin-top: 2em;
+}
+
+div.plugname {
+ margin-top: 2em;
+ margin-bottom: 1em;
+ font-size: large;
+ text-align: left;
+ font-weight: bold;
+ color: #5D616E;
+}
+
+dl {
+ margin: 0px 0px 3px;
+}
+
+table {
+ background-color: #C7D0D9;
+ color: #22262F;
+ font: message-box;
+ text-align: left;
+ width: 100%;
+ border: 1px solid #2D3B49;
+ border-spacing: 0px;
+}
+
+th {
+ text-align: center;
+ font-weight: bold;
+ background-color: #90A1B3;
+ color: #000000;
+}
+
+th + th,
+td + td {
+ border-left: 1px dotted #2D3B49;
+}
+
+td {
+ text-align: left;
+ border-top: 1px dotted #2D3B49;
+}
+
+th, td {
+ padding: 3px;
+}
+
+th.type, th.suff {
+ width: 20%;
+}
+
+th.desc {
+ width: 50%;
+}
+
+th.enabled {
+ width: 10%;
+} \ No newline at end of file
diff --git a/comm/suite/themes/modern/global/popup.css b/comm/suite/themes/modern/global/popup.css
new file mode 100644
index 0000000000..33250e6733
--- /dev/null
+++ b/comm/suite/themes/modern/global/popup.css
@@ -0,0 +1,104 @@
+/* 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/. */
+
+/* ===== popup.css =====================================================
+ == Styles used by the XUL popup & menupopup elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: popup/menupopup ::::: */
+
+.panel-arrowcontent,
+panel:not([type="arrow"]),
+menupopup,
+popup {
+ cursor: default;
+ border: 1px solid #7F8893;
+ min-width: 1px;
+ background-color: #DDE3EB;
+ color: #22262F;
+}
+
+menupopup > menu > menupopup,
+popup > menu > menupopup {
+ margin: -2px;
+}
+
+/* Ideally we would set and forget a -16px margin around the entire panel.
+ Unfortunately this makes the panel code think we're hiding the anchor,
+ and it reacts by turning the arrow off. So instead we have to turn off
+ the 16px border on the appropriate side of the arrow. */
+panel[type="arrow"][side="top"],
+panel[type="arrow"][side="bottom"] {
+ margin: 0px -16px;
+}
+
+panel[type="arrow"][side="left"],
+panel[type="arrow"][side="right"] {
+ margin: -16px 0px;
+}
+
+/* The arrow is empty, but it has equal triangular borders on all four sides.
+ We just have to turn on the border on the side touching the content. */
+.panel-arrow {
+ border: 16px solid transparent;
+}
+
+.panel-arrow[side="top"] {
+ border-top-style: none;
+ border-bottom-color: #7F8893;
+}
+
+.panel-arrow[side="bottom"] {
+ border-bottom-style: none;
+ border-top-color: #7F8893;
+}
+
+.panel-arrow[side="left"] {
+ border-left-style: none;
+ border-right-color: #7F8893;
+}
+
+.panel-arrow[side="right"] {
+ border-right-style: none;
+ border-left-color: #7F8893;
+}
+
+/* ::::: tooltip ::::: */
+
+tooltip {
+ margin-top: 21px;
+ border: 1px solid #000000;
+ padding: 2px 3px;
+ max-width: 40em;
+ background-color: #FFFFE7;
+ color: #000000;
+ font: message-box;
+}
+
+tooltip[titletip="true"] {
+ /* See bug 32157 comment 128
+ * margin-top: -2px;
+ * margin-bottom: 0px;
+ * margin-inline-start: -4px;
+ * margin-inline-end: 0px;
+ */
+ max-width: none;
+}
+
+/* ::::: menulist popup :::::: */
+
+menulist > menupopup,
+.menulist-menupopup {
+ border-width: 1px;
+ border-color: #000000;
+ padding: 0px;
+ min-width: 0px;
+}
+
+menupopup > menu > .menulist-menupopup,
+popup > menu > .menulist-menupopup {
+ margin: -1px;
+}
diff --git a/comm/suite/themes/modern/global/preferences.css b/comm/suite/themes/modern/global/preferences.css
new file mode 100644
index 0000000000..49450a6ef7
--- /dev/null
+++ b/comm/suite/themes/modern/global/preferences.css
@@ -0,0 +1,65 @@
+/* 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/. */
+
+/* ===== preferences.css =====================================================
+ == Styles used by the XUL prefwindow element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: dialog ::::: */
+
+prefwindow {
+ padding: 0px;
+}
+
+prefwindow[type="child"] {
+ padding: 7px 5px 5px;
+}
+
+prefpane {
+ padding: 7px 5px 0px 5px;
+}
+
+prefwindow[type="child"] > prefpane {
+ padding: 0px;
+}
+
+.prefWindow-dlgbuttons {
+ padding: 0px 5px 5px;
+}
+
+prefwindow[type="child"] > .prefWindow-dlgbuttons {
+ padding: 0px;
+}
+
+radio[pane] {
+ margin: 0px 1px;
+ padding: 1px 3px;
+ min-width: 4.5em;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+radio[pane][selected="true"] {
+ background-color: #C7D0D9;
+}
+
+radio[pane]:hover:active {
+ background-color: #90A1B3;
+ color: #FFFFFF;
+}
+
+.paneSelector {
+ border-bottom: 1px solid #2D3B49;
+ margin: 0px;
+ padding-inline-start: 10px;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+.paneButtonIcon {
+ width: 32px;
+ height: 32px;
+}
diff --git a/comm/suite/themes/modern/global/printPageSetup.css b/comm/suite/themes/modern/global/printPageSetup.css
new file mode 100644
index 0000000000..7846c8b58e
--- /dev/null
+++ b/comm/suite/themes/modern/global/printPageSetup.css
@@ -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/. */
+
+.portrait-page {
+ list-style-image: url("chrome://global/skin/icons/pg-portrait.png");
+}
+
+.landscape-page {
+ list-style-image: url("chrome://global/skin/icons/pg-landscape.png");
+}
diff --git a/comm/suite/themes/modern/global/printPreview.css b/comm/suite/themes/modern/global/printPreview.css
new file mode 100644
index 0000000000..2c416e51f1
--- /dev/null
+++ b/comm/suite/themes/modern/global/printPreview.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+/* ::::: print preview toolbar ::::: */
+
+/* ..... page navigation ..... */
+
+.home-arrow,
+.end-arrow:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-sharp-end.png");
+}
+
+.end-arrow,
+.home-arrow:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-sharp-end.png");
+}
+
+.previous-arrow,
+.next-arrow:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-sharp.png");
+}
+
+.next-arrow,
+.previous-arrow:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-sharp.png");
+}
+
+/* ..... orientation ..... */
+
+.toolbar-portrait-page {
+ list-style-image: url("chrome://global/skin/icons/pg-portrait-small.png");
+}
+
+.toolbar-landscape-page {
+ list-style-image: url("chrome://global/skin/icons/pg-landscape-small.png");
+}
diff --git a/comm/suite/themes/modern/global/progressmeter.css b/comm/suite/themes/modern/global/progressmeter.css
new file mode 100644
index 0000000000..ae44afec74
--- /dev/null
+++ b/comm/suite/themes/modern/global/progressmeter.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+/* ===== progressmeter.css ==============================================
+ == Styles used by the XUL progressmeter element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: progressmeter ::::: */
+
+progressmeter {
+ margin: 2px 4px;
+ border: 1px solid;
+ border-top-color: #8997A1;
+ border-right-color: #C7D0D9;
+ border-bottom-color: #C7D0D9;
+ border-left-color: #8997A1;
+ min-width: 128px;
+ height: 14px;
+}
+
+progressmeter[mode="undetermined"] {
+ background: url("chrome://global/skin/progressmeter/progress-busy.png");
+}
+
+.progress-bar {
+ min-width: 0px;
+ background-color: #6B7B8D;
+}
+
+.progress-bar[mode="undetermined"],
+.progress-remainder[mode="undetermined"] {
+ visibility: hidden;
+}
+
+/* ::::: statusbar progressmeter ::::: */
+
+.progressmeter-statusbar {
+ margin: 0;
+ border-bottom-color: transparent;
+ border-top-color: #C7D0D9;
+ border-right-color: #8997A1;
+ border-left-color: #C7D0D9;
+}
+
diff --git a/comm/suite/themes/modern/global/progressmeter/progress-busy.gif b/comm/suite/themes/modern/global/progressmeter/progress-busy.gif
new file mode 100644
index 0000000000..acbf06931d
--- /dev/null
+++ b/comm/suite/themes/modern/global/progressmeter/progress-busy.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/progressmeter/progress-busy.png b/comm/suite/themes/modern/global/progressmeter/progress-busy.png
new file mode 100644
index 0000000000..0c5dc994dc
--- /dev/null
+++ b/comm/suite/themes/modern/global/progressmeter/progress-busy.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio.css b/comm/suite/themes/modern/global/radio.css
new file mode 100644
index 0000000000..a9fb28856f
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio.css
@@ -0,0 +1,72 @@
+/* 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/. */
+
+/* ===== radio.css ======================================================
+ == Styles used by the XUL radio element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: radio ::::: */
+
+radio {
+ -moz-box-align: center;
+ margin: 0px 2px;
+ border: 2px solid transparent;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+ border-radius: 4px 5px;
+}
+
+.radio-icon {
+ margin-inline-start: 2px;
+}
+
+.radio-text {
+ margin: 0px 2px !important;
+}
+
+/* ..... focused state ..... */
+
+radio[focused="true"] {
+ border-color: #98A5B2;
+}
+
+/* ..... disabled state ..... */
+
+radio[disabled="true"] > .radio-label-box > .radio-label {
+ color: #8C99AB !important;
+}
+
+/* ::::: checkmark image ::::: */
+
+.radio-check {
+ width: 13px;
+ height: 13px;
+ list-style-image: url("chrome://global/skin/radio/radio.png");
+}
+
+radio:hover:active > .radio-check {
+ list-style-image: url("chrome://global/skin/radio/radio-act.png");
+}
+
+radio[disabled="true"] > .radio-check {
+ list-style-image: url("chrome://global/skin/radio/radio-dis.png") !important;
+}
+
+/* ..... checked state ..... */
+
+radio[selected] > .radio-check {
+ list-style-image: url("chrome://global/skin/radio/radio-check.png");
+}
+
+radio[selected]:hover:active > .radio-check {
+ list-style-image: url("chrome://global/skin/radio/radio-act-check.png");
+}
+
+radio[selected][disabled="true"] > .radio-check {
+ list-style-image: url("chrome://global/skin/radio/radio-dis-check.png") !important;
+}
diff --git a/comm/suite/themes/modern/global/radio/radio-act-check.gif b/comm/suite/themes/modern/global/radio/radio-act-check.gif
new file mode 100644
index 0000000000..a3ad23afdf
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-act-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-act-check.png b/comm/suite/themes/modern/global/radio/radio-act-check.png
new file mode 100644
index 0000000000..360c4370d4
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-act-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-act.gif b/comm/suite/themes/modern/global/radio/radio-act.gif
new file mode 100644
index 0000000000..6acf3006e2
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-act.png b/comm/suite/themes/modern/global/radio/radio-act.png
new file mode 100644
index 0000000000..f8fe1cb595
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-check.gif b/comm/suite/themes/modern/global/radio/radio-check.gif
new file mode 100644
index 0000000000..e3a534af41
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-check.png b/comm/suite/themes/modern/global/radio/radio-check.png
new file mode 100644
index 0000000000..69bffa6361
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-dis-check.gif b/comm/suite/themes/modern/global/radio/radio-dis-check.gif
new file mode 100644
index 0000000000..fbec743564
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-dis-check.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-dis-check.png b/comm/suite/themes/modern/global/radio/radio-dis-check.png
new file mode 100644
index 0000000000..3fa12a37dd
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-dis-check.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-dis.gif b/comm/suite/themes/modern/global/radio/radio-dis.gif
new file mode 100644
index 0000000000..1ccf4f060b
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-dis.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio-dis.png b/comm/suite/themes/modern/global/radio/radio-dis.png
new file mode 100644
index 0000000000..e415e54871
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio.gif b/comm/suite/themes/modern/global/radio/radio.gif
new file mode 100644
index 0000000000..abc479afa6
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/radio/radio.png b/comm/suite/themes/modern/global/radio/radio.png
new file mode 100644
index 0000000000..7d226f7341
--- /dev/null
+++ b/comm/suite/themes/modern/global/radio/radio.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/resizer.css b/comm/suite/themes/modern/global/resizer.css
new file mode 100644
index 0000000000..2242efc976
--- /dev/null
+++ b/comm/suite/themes/modern/global/resizer.css
@@ -0,0 +1,49 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
+
+/* This affects all resizers, even native anonymous ones. */
+resizer {
+ background-color: #B1BBC5;
+}
+
+resizer[dir="top"],
+resizer[dir="bottom"] {
+ cursor: ns-resize;
+}
+
+resizer[dir="left"],
+resizer[dir="right"] {
+ cursor: ew-resize;
+}
+
+resizer[dir="topleft"] {
+ cursor: nw-resize;
+}
+
+resizer[dir="bottomright"],
+resizer[dir="bottomend"]:-moz-locale-dir(ltr) {
+ background-image: url("chrome://global/skin/icons/resizer.png");
+ background-position: bottom right;
+ background-repeat: no-repeat;
+ cursor: se-resize;
+ width: 15px;
+ height: 15px;
+}
+
+resizer[dir="topright"] {
+ cursor: ne-resize;
+}
+
+resizer[dir="bottomleft"],
+resizer[dir="bottomend"]:-moz-locale-dir(rtl) {
+ background-image: url("chrome://global/skin/icons/resizer-rtl.png");
+ background-position: bottom left;
+ background-repeat: no-repeat;
+ cursor: sw-resize;
+ width: 15px;
+ height: 15px;
+}
diff --git a/comm/suite/themes/modern/global/richlistbox.css b/comm/suite/themes/modern/global/richlistbox.css
new file mode 100644
index 0000000000..b938b0005d
--- /dev/null
+++ b/comm/suite/themes/modern/global/richlistbox.css
@@ -0,0 +1,51 @@
+/* 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/. */
+
+/* ===== richlistbox.css ===================================================
+ == Styles used by XUL richlistbox-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: richlistbox ::::: */
+
+richlistbox {
+ /* -moz-appearance needed to override xul.css */
+ -moz-appearance: none !important;
+ margin: 2px 4px;
+ border: 1px solid #5D616E;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+richlistbox[disabled="true"] {
+ color: #8C99AB;
+}
+
+/* ::::: richlistitem ::::: */
+
+richlistitem {
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+}
+
+richlistitem[selected="true"] {
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+richlistbox:focus > richlistitem[selected="true"] {
+ background-color: #424F63;
+ color: #FFFFFF;
+}
+
+richlistbox:focus > richlistitem[current="true"] {
+ border-top-color: #000000;
+ border-bottom-color: #000000;
+}
+
+richlistbox[seltype="multiple"]:focus > richlistitem[selected="true"][current="true"] {
+ border-top-color: #FFFFFF;
+ border-bottom-color: #FFFFFF;
+}
diff --git a/comm/suite/themes/modern/global/scale.css b/comm/suite/themes/modern/global/scale.css
new file mode 100644
index 0000000000..e7863deaf7
--- /dev/null
+++ b/comm/suite/themes/modern/global/scale.css
@@ -0,0 +1,51 @@
+/* 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/. */
+
+/* ===== scale.css =================================================
+ == Styles used by XUL scale elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: scale ::::: */
+
+.scale-slider {
+ background: url("chrome://global/skin/scrollbar/slider-hrz.png") repeat-x;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+ margin: 2px 4px;
+ width: 100px;
+ height: 15px;
+}
+
+.scale-slider[orient="vertical"]
+{
+ background: url("chrome://global/skin/scrollbar/slider-vrt.png") repeat-y;
+ border-left: none;
+ border-right: none;
+ border-top: 1px solid black;
+ border-bottom: 1px solid black;
+ margin: 4px 2px;
+ width: 15px;
+ height: 100px;
+}
+
+/* ::::: scale thumb ::::: */
+
+.scale-thumb {
+ border: 1px solid #000000;
+ background-image: url("chrome://global/skin/scrollbar/thumb-hrz-grip.png");
+ min-width: 18px;
+}
+
+.scale-thumb[orient="vertical"] {
+ background-image: url("chrome://global/skin/scrollbar/thumb-vrt-grip.png");
+ min-height: 18px;
+ min-width: 0px;
+}
+
+.scale-thumb[disabled="true"] {
+ border-color: #000000;
+ background: #9CA8B4;
+}
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-dn.gif b/comm/suite/themes/modern/global/scrollbar/btn-dn.gif
new file mode 100644
index 0000000000..ccd686cb8b
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-dn.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-dn.png b/comm/suite/themes/modern/global/scrollbar/btn-dn.png
new file mode 100644
index 0000000000..e26dcc597c
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-dn.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-lft.gif b/comm/suite/themes/modern/global/scrollbar/btn-lft.gif
new file mode 100644
index 0000000000..c0493ca918
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-lft.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-lft.png b/comm/suite/themes/modern/global/scrollbar/btn-lft.png
new file mode 100644
index 0000000000..d306dbb626
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-lft.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-rit.gif b/comm/suite/themes/modern/global/scrollbar/btn-rit.gif
new file mode 100644
index 0000000000..b23b771f27
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-rit.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-rit.png b/comm/suite/themes/modern/global/scrollbar/btn-rit.png
new file mode 100644
index 0000000000..e662e73090
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-rit.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-up.gif b/comm/suite/themes/modern/global/scrollbar/btn-up.gif
new file mode 100644
index 0000000000..97db3ed22f
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-up.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/btn-up.png b/comm/suite/themes/modern/global/scrollbar/btn-up.png
new file mode 100644
index 0000000000..6888f3ed79
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/btn-up.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-dn.gif b/comm/suite/themes/modern/global/scrollbar/mini-btn-dn.gif
new file mode 100644
index 0000000000..1f03248f07
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-dn.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-dn.png b/comm/suite/themes/modern/global/scrollbar/mini-btn-dn.png
new file mode 100644
index 0000000000..0855351197
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-dn.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-lft.gif b/comm/suite/themes/modern/global/scrollbar/mini-btn-lft.gif
new file mode 100644
index 0000000000..6f85443e2a
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-lft.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-lft.png b/comm/suite/themes/modern/global/scrollbar/mini-btn-lft.png
new file mode 100644
index 0000000000..14d44d7eea
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-lft.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-rit.gif b/comm/suite/themes/modern/global/scrollbar/mini-btn-rit.gif
new file mode 100644
index 0000000000..e8404bc185
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-rit.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-rit.png b/comm/suite/themes/modern/global/scrollbar/mini-btn-rit.png
new file mode 100644
index 0000000000..21f9e43779
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-rit.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-up.gif b/comm/suite/themes/modern/global/scrollbar/mini-btn-up.gif
new file mode 100644
index 0000000000..837c4369b5
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-up.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-btn-up.png b/comm/suite/themes/modern/global/scrollbar/mini-btn-up.png
new file mode 100644
index 0000000000..0bbd83ba8f
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-btn-up.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.gif b/comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.gif
new file mode 100644
index 0000000000..bc391487c1
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.png b/comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.png
new file mode 100644
index 0000000000..306a6c0dd4
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-slider-hrz.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.gif b/comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.gif
new file mode 100644
index 0000000000..09c5ac8a93
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.png b/comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.png
new file mode 100644
index 0000000000..ff6b021b36
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-slider-vrt.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.gif b/comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.gif
new file mode 100644
index 0000000000..087219863e
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.png b/comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.png
new file mode 100644
index 0000000000..0fcae98003
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-thumb-hrz-grip.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.gif b/comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.gif
new file mode 100644
index 0000000000..95073fcee9
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.png b/comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.png
new file mode 100644
index 0000000000..cafac1df0d
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/mini-thumb-vrt-grip.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/slider-hrz.gif b/comm/suite/themes/modern/global/scrollbar/slider-hrz.gif
new file mode 100644
index 0000000000..93a4d1a090
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/slider-hrz.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/slider-hrz.png b/comm/suite/themes/modern/global/scrollbar/slider-hrz.png
new file mode 100644
index 0000000000..8511a19a83
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/slider-hrz.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/slider-vrt.gif b/comm/suite/themes/modern/global/scrollbar/slider-vrt.gif
new file mode 100644
index 0000000000..9d85a0c774
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/slider-vrt.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/slider-vrt.png b/comm/suite/themes/modern/global/scrollbar/slider-vrt.png
new file mode 100644
index 0000000000..1641d0cbc2
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/slider-vrt.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.gif b/comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.gif
new file mode 100644
index 0000000000..3e40a51b8e
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.png b/comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.png
new file mode 100644
index 0000000000..3e04533f68
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/thumb-hrz-grip.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.gif b/comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.gif
new file mode 100644
index 0000000000..046e343a4e
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.png b/comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.png
new file mode 100644
index 0000000000..9d6521b117
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbar/thumb-vrt-grip.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/scrollbars-mini.css b/comm/suite/themes/modern/global/scrollbars-mini.css
new file mode 100644
index 0000000000..a9591c60bf
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbars-mini.css
@@ -0,0 +1,108 @@
+/* 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/. */
+
+/* ===== scrollbars.css =================================================
+ == Styles used by XUL scrollbar-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+
+/* ::::: scrollbar ::::: */
+
+scrollbar {
+ -moz-binding: url("chrome://global/content/bindings/scrollbar.xml#scrollbar");
+ cursor: default;
+ min-width: 11px;
+ min-height: 11px;
+}
+
+/* ::::: square at the corner of two scrollbars ::::: */
+
+scrollcorner {
+ -moz-binding: url("chrome://global/content/bindings/scrollbar.xml#scrollbar-base");
+ cursor: default;
+ background-color: #B1BBC5;
+}
+
+/* ::::: slider ::::: */
+
+slider {
+ background: url("chrome://global/skin/scrollbar/mini-slider-hrz.png") repeat-x;
+}
+
+slider[orient="vertical"] {
+ background: url("chrome://global/skin/scrollbar/mini-slider-vrt.png") repeat-y;
+}
+
+/* ::::: borders for thumb and buttons ::::: */
+
+thumb,
+scrollbarbutton {
+ border: 1px solid;
+ border-color: #000000;
+ background: #B1BBC5 50% 50% no-repeat;
+}
+
+thumb:active {
+ background-color: #C2CCD6;
+ border-color: #111111;
+}
+
+/* ::::: thumb (horizontal) ::::: */
+
+thumb {
+ background-image: url("chrome://global/skin/scrollbar/thumb-vrt-grip.png");
+}
+
+thumb[orient="horizontal"] {
+ background-image: url("chrome://global/skin/scrollbar/thumb-hrz-grip.png");
+}
+
+/* ::::: scrollbar button ::::: */
+
+scrollbarbutton {
+ width: 11px;
+ height: 14px;
+ max-width: 11px;
+ max-height: 14px;
+ -moz-box-flex: 1;
+}
+
+scrollbar[orient="horizontal"] > scrollbarbutton {
+ width: 14px;
+ height: 11px;
+ max-width: 14px;
+ max-height: 11px;
+}
+
+scrollbarbutton[disabled="true"],
+scrollbarbutton[active="true"],
+scrollbarbutton:hover:active {
+ border-left-width: 1px;
+ border-right-width: 1px;
+ border-color: #000000;
+ background-color: #9CA8B4;
+}
+
+/* ..... increment .... */
+
+scrollbarbutton[type="increment"] {
+ background-image: url("chrome://global/skin/scrollbar/mini-btn-rit.png")
+}
+
+scrollbar[orient="vertical"] > scrollbarbutton[type="increment"] {
+ background-image: url("chrome://global/skin/scrollbar/mini-btn-dn.png")
+}
+
+/* ..... decrement .... */
+
+scrollbarbutton[type="decrement"] {
+ background-image: url("chrome://global/skin/scrollbar/mini-btn-lft.png")
+}
+
+scrollbar[orient="vertical"] > scrollbarbutton[type="decrement"] {
+ background-image: url("chrome://global/skin/scrollbar/mini-btn-up.png")
+}
+
diff --git a/comm/suite/themes/modern/global/scrollbars.css b/comm/suite/themes/modern/global/scrollbars.css
new file mode 100644
index 0000000000..64ce6a4f63
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbars.css
@@ -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/. */
+
+/* ===== scrollbars.css =================================================
+ == Styles used by XUL scrollbar-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
+
+/* ::::: scrollbar ::::: */
+
+scrollbar {
+ -moz-binding: url("chrome://global/content/bindings/scrollbar.xml#scrollbar");
+ cursor: default;
+}
+
+/* ::::: slider ::::: */
+
+slider {
+ min-width: 15px;
+ min-height: 15px;
+ background: url("chrome://global/skin/scrollbar/slider-hrz.png") repeat-x;
+}
+
+slider[orient="vertical"] {
+ background: url("chrome://global/skin/scrollbar/slider-vrt.png") repeat-y;
+}
+
+/* ::::: borders for thumb and buttons ::::: */
+
+thumb,
+scrollbarbutton {
+ border: 1px solid;
+ border-color: #000000;
+ background: #B1BBC5 50% 50% no-repeat;
+}
+
+thumb:active {
+ background-color: #C2CCD6;
+ border-color: #111111;
+}
+
+%ifdef MOZ_WIDGET_GTK
+scrollbarbutton[sbattr="scrollbar-up-bottom"],
+scrollbarbutton[sbattr="scrollbar-down-top"] {
+ display: none;
+}
+
+scrollbarbutton[sbattr="scrollbar-up-top"],
+scrollbarbutton[sbattr="scrollbar-down-bottom"] {
+ display: -moz-box !important;
+}
+%endif
+
+/* ::::: thumb (horizontal) ::::: */
+
+thumb {
+ min-height: 18px;
+ background-image: url("chrome://global/skin/scrollbar/thumb-vrt-grip.png");
+}
+
+thumb[orient="horizontal"] {
+ min-width: 18px;
+ background-image: url("chrome://global/skin/scrollbar/thumb-hrz-grip.png");
+}
+
+/* ::::: scrollbar button ::::: */
+
+scrollbarbutton {
+ width: 15px;
+ height: 15px;
+ max-width: 15px;
+ max-height: 15px;
+ -moz-box-flex: 1;
+}
+
+scrollbarbutton[disabled="true"],
+scrollbarbutton[active="true"],
+scrollbarbutton:hover:active {
+ border-color: #000000;
+ background-color: #9CA8B4;
+}
+
+/* ::::: square at the corner of two scrollbars ::::: */
+
+scrollcorner {
+ -moz-binding: url("chrome://global/content/bindings/scrollbar.xml#scrollbar-base");
+ width: 15px;
+ cursor: default;
+ background-color: #B1BBC5;
+}
+
+/* ..... increment .... */
+
+scrollbarbutton[type="increment"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-rit.png")
+}
+
+scrollbar[orient="vertical"] > scrollbarbutton[type="increment"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-dn.png")
+}
+
+/* ..... decrement .... */
+
+scrollbarbutton[type="decrement"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-lft.png")
+}
+
+scrollbar[orient="vertical"] > scrollbarbutton[type="decrement"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-up.png")
+}
+
+/* :::::::::::::::::::::::::::::::::::::::::::::::::::::::: */
+/* ::::::::::::::::::::: MEDIA PRINT :::::::::::::::::::::: */
+/* :::::::::::::::::::::::::::::::::::::::::::::::::::::::: */
+@media print {
+
+ /* ::::: slider ::::: */
+
+ html|div slider {
+ height: 15px;
+ background: url("chrome://global/skin/scrollbar/slider-hrz.png") repeat-x;
+ }
+
+ html|div slider[orient="vertical"] {
+ width: 15px;
+ background: url("chrome://global/skin/scrollbar/slider-vrt.png") repeat-y;
+ }
+
+ /* ::::: borders for thumb and buttons ::::: */
+
+ html|div thumb,
+ html|div scrollbarbutton {
+ border: 1px solid;
+ border-color: #000000;
+ background: #B1BBC5 50% 50% no-repeat;
+ }
+
+ html|div thumb:active {
+ background-color: #C2CCD6;
+ border-color: #111111;
+ }
+
+ /* ::::: thumb (horizontal) ::::: */
+
+ html|div thumb {
+ min-height: 18px;
+ background-image: url("chrome://global/skin/scrollbar/thumb-vrt-grip.png");
+ }
+
+ html|div thumb[orient="horizontal"] {
+ min-width: 18px;
+ background-image: url("chrome://global/skin/scrollbar/thumb-hrz-grip.png");
+ }
+
+ /* ::::: scrollbar button ::::: */
+
+ html|div scrollbarbutton {
+ width: 15px;
+ height: 15px;
+ }
+
+ html|div scrollbarbutton[disabled="true"],
+ html|div scrollbarbutton[active="true"],
+ html|div scrollbarbutton:hover:active {
+ border-color: #000000;
+ background-color: #9CA8B4;
+ }
+
+ /* ..... increment .... */
+
+ html|div scrollbarbutton[type="increment"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-rit.png")
+ }
+
+ html|div scrollbar[orient="vertical"] > scrollbarbutton[type="increment"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-dn.png")
+ }
+
+ /* ..... decrement .... */
+
+ html|div scrollbarbutton[type="decrement"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-lft.png")
+ }
+
+ html|div scrollbar[orient="vertical"] > scrollbarbutton[type="decrement"] {
+ background-image: url("chrome://global/skin/scrollbar/btn-up.png")
+ }
+
+}
diff --git a/comm/suite/themes/modern/global/scrollbox.css b/comm/suite/themes/modern/global/scrollbox.css
new file mode 100644
index 0000000000..10b5d417fe
--- /dev/null
+++ b/comm/suite/themes/modern/global/scrollbox.css
@@ -0,0 +1,95 @@
+/* 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/. */
+
+/* ===== arrowscrollbox.css =============================================
+ == Styles used by the XUL arrowscrollbox and related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: auto-repeat button ::::: */
+
+autorepeatbutton {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin-top: 1px;
+ margin-bottom: 2px;
+ margin-inline-start: 1px;
+ margin-inline-end: 2px;
+ border: 1px solid transparent;
+ padding: 3px;
+}
+
+autorepeatbutton:not([disabled="true"]):hover {
+ margin: 1px;
+ border: 1px inset #A5B2C2;
+ padding-top: 4px;
+ padding-bottom: 3px;
+ padding-inline-start: 4px;
+ padding-inline-end: 3px;
+ background-color: #A5B2C2;
+}
+
+.scrollbutton-up,
+.scrollbutton-down {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+}
+
+.scrollbutton-up > .toolbarbutton-text,
+.scrollbutton-down > .toolbarbutton-text {
+ display: none;
+}
+
+/* Vertical enabled */
+.autorepeatbutton-up,
+.scrollbutton-up {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png")
+}
+
+.autorepeatbutton-down,
+.scrollbutton-down {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png")
+}
+
+/* Vertical disabled */
+.autorepeatbutton-up[disabled="true"],
+.scrollbutton-up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.png");
+}
+
+.autorepeatbutton-down[disabled="true"],
+.scrollbutton-down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.png");
+}
+
+/* Horizontal enabled */
+.autorepeatbutton-up[orient="horizontal"],
+.autorepeatbutton-down[orient="horizontal"]:-moz-locale-dir(rtl),
+.scrollbutton-up[orient="horizontal"],
+.scrollbutton-down[orient="horizontal"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.png");
+}
+
+.autorepeatbutton-down[orient="horizontal"],
+.autorepeatbutton-up[orient="horizontal"]:-moz-locale-dir(rtl),
+.scrollbutton-down[orient="horizontal"],
+.scrollbutton-up[orient="horizontal"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.png");
+}
+
+ /* Horizontal disabled */
+.autorepeatbutton-up[orient="horizontal"][disabled="true"],
+.autorepeatbutton-down[orient="horizontal"][disabled="true"]:-moz-locale-dir(rtl),
+.scrollbutton-up[orient="horizontal"][disabled="true"],
+.scrollbutton-down[orient="horizontal"][disabled="true"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-dis.png");
+}
+
+.autorepeatbutton-down[orient="horizontal"][disabled="true"],
+.autorepeatbutton-up[orient="horizontal"][disabled="true"]:-moz-locale-dir(rtl),
+.scrollbutton-down[orient="horizontal"][disabled="true"],
+.scrollbutton-up[orient="horizontal"][disabled="true"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-dis.png");
+}
diff --git a/comm/suite/themes/modern/global/splitter.css b/comm/suite/themes/modern/global/splitter.css
new file mode 100644
index 0000000000..4236334444
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter.css
@@ -0,0 +1,182 @@
+/* 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/. */
+
+/* ===== splitter.css ===================================================
+ == Styles used by the XUL splitter element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: splitter (vertical) ::::: */
+
+splitter {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ cursor: ew-resize;
+ border-top: 1px solid #DFE5EF;
+ border-right: 1px solid #3B414F;
+ border-bottom: 1px solid #AAB4BF;
+ border-left: 1px solid #97A4B2;
+ min-width: 5px;
+ background-color: #C1CBD5;
+}
+
+splitter[state="collapsed"][collapse="before"],
+splitter[state="collapsed"][substate="before"],
+splitter[state="collapsed"][collapse="after"]:-moz-locale-dir(rtl),
+splitter[state="collapsed"][substate="after"]:-moz-locale-dir(rtl) {
+ cursor: e-resize;
+}
+
+splitter[state="collapsed"][collapse="after"],
+splitter[state="collapsed"][substate="after"],
+splitter[state="collapsed"][collapse="before"]:-moz-locale-dir(rtl),
+splitter[state="collapsed"][substate="before"]:-moz-locale-dir(rtl) {
+ cursor: w-resize;
+}
+
+/* ::::: splitter (horizontal) ::::: */
+
+splitter[orient="vertical"] {
+ cursor: ns-resize;
+ border-top: 1px solid #97A4B2;
+ border-right: 1px solid #AAB4BF;
+ border-bottom: 1px solid #3B414F;
+ border-left: 1px solid #DFE5EF;
+ min-width: 0;
+ min-height: 5px;
+}
+
+splitter[orient="vertical"][state="collapsed"][collapse="before"],
+splitter[orient="vertical"][state="collapsed"][substate="before"] {
+ cursor: s-resize;
+}
+
+splitter[orient="vertical"][state="collapsed"][collapse="after"],
+splitter[orient="vertical"][state="collapsed"][substate="after"] {
+ cursor: n-resize;
+}
+
+splitter[disabled="true"] {
+ cursor: default !important;
+}
+
+/* ::::: splitter grippy ::::: */
+
+grippy {
+ cursor: pointer;
+ margin: 0px;
+ min-width: 5px;
+ min-height: 50px;
+}
+
+splitter[orient="vertical"] > grippy {
+ margin: 0px;
+ min-width: 50px;
+ min-height: 5px;
+}
+
+/* ..... normal state ..... */
+
+/* vertical grippies */
+splitter[collapse="before"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-before.png");
+}
+
+splitter[collapse="before"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-before-act.png");
+}
+
+splitter[collapse="before"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-before-act.png");
+}
+
+splitter[collapse="after"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-after.png");
+}
+
+splitter[collapse="after"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-after-act.png");
+}
+
+splitter[collapse="after"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-after-act.png");
+}
+
+/* horizontal grippies */
+splitter[collapse="before"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-before.png");
+}
+
+splitter[collapse="before"][orient="vertical"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-before-act.png");
+}
+
+splitter[collapse="before"][orient="vertical"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-before-act.png");
+}
+
+splitter[collapse="after"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-after.png");
+}
+
+splitter[collapse="after"][orient="vertical"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-after-act.png");
+}
+
+splitter[collapse="after"][orient="vertical"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-after-act.png");
+}
+
+/* ..... collapsed state ..... */
+
+/* vertical grippies */
+splitter[collapse="before"][state="collapsed"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-after.png");
+}
+
+splitter[collapse="before"][state="collapsed"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-after-act.png");
+}
+
+splitter[collapse="before"][state="collapsed"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-after-act.png");
+}
+
+splitter[collapse="after"][state="collapsed"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-before.png");
+}
+
+splitter[collapse="after"][state="collapsed"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-before-act.png");
+}
+
+splitter[collapse="after"][state="collapsed"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-vrt-before-act.png");
+}
+
+/* horizontal grippies */
+splitter[collapse="before"][state="collapsed"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-after.png");
+}
+
+splitter[collapse="before"][state="collapsed"][orient="vertical"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-after-act.png");
+}
+
+splitter[collapse="before"][state="collapsed"][orient="vertical"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-after-act.png");
+}
+
+splitter[collapse="after"][state="collapsed"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-before.png");
+}
+
+splitter[collapse="after"][state="collapsed"][orient="vertical"]:hover:active > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-before-act.png");
+}
+
+splitter[collapse="after"][state="collapsed"][orient="vertical"] > grippy:hover:active {
+ background-image: url("chrome://global/skin/splitter/grip-hrz-before-act.png");
+}
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-after-act.gif b/comm/suite/themes/modern/global/splitter/grip-hrz-after-act.gif
new file mode 100644
index 0000000000..700815a7a1
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-after-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-after-act.png b/comm/suite/themes/modern/global/splitter/grip-hrz-after-act.png
new file mode 100644
index 0000000000..3d907f8516
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-after-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-after.gif b/comm/suite/themes/modern/global/splitter/grip-hrz-after.gif
new file mode 100644
index 0000000000..533e55399f
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-after.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-after.png b/comm/suite/themes/modern/global/splitter/grip-hrz-after.png
new file mode 100644
index 0000000000..b1d03f51c5
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-after.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-before-act.gif b/comm/suite/themes/modern/global/splitter/grip-hrz-before-act.gif
new file mode 100644
index 0000000000..df08d29c0d
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-before-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-before-act.png b/comm/suite/themes/modern/global/splitter/grip-hrz-before-act.png
new file mode 100644
index 0000000000..f929149031
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-before-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-before.gif b/comm/suite/themes/modern/global/splitter/grip-hrz-before.gif
new file mode 100644
index 0000000000..703bbafeff
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-before.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-hrz-before.png b/comm/suite/themes/modern/global/splitter/grip-hrz-before.png
new file mode 100644
index 0000000000..4b2e3d41e8
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-hrz-before.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-after-act.gif b/comm/suite/themes/modern/global/splitter/grip-vrt-after-act.gif
new file mode 100644
index 0000000000..1fb624dbb0
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-after-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-after-act.png b/comm/suite/themes/modern/global/splitter/grip-vrt-after-act.png
new file mode 100644
index 0000000000..cbcd094da7
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-after-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-after.gif b/comm/suite/themes/modern/global/splitter/grip-vrt-after.gif
new file mode 100644
index 0000000000..15ea90a20a
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-after.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-after.png b/comm/suite/themes/modern/global/splitter/grip-vrt-after.png
new file mode 100644
index 0000000000..c2f2ff0722
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-after.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-before-act.gif b/comm/suite/themes/modern/global/splitter/grip-vrt-before-act.gif
new file mode 100644
index 0000000000..e06f8cbeef
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-before-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-before-act.png b/comm/suite/themes/modern/global/splitter/grip-vrt-before-act.png
new file mode 100644
index 0000000000..f17373c84d
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-before-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-before.gif b/comm/suite/themes/modern/global/splitter/grip-vrt-before.gif
new file mode 100644
index 0000000000..0f08486e44
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-before.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/splitter/grip-vrt-before.png b/comm/suite/themes/modern/global/splitter/grip-vrt-before.png
new file mode 100644
index 0000000000..99ebfb38e0
--- /dev/null
+++ b/comm/suite/themes/modern/global/splitter/grip-vrt-before.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tabbox.css b/comm/suite/themes/modern/global/tabbox.css
new file mode 100644
index 0000000000..0e93163048
--- /dev/null
+++ b/comm/suite/themes/modern/global/tabbox.css
@@ -0,0 +1,123 @@
+/* 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/. */
+
+/* ===== tabbox.css =================================================
+ == Styles used by XUL tab-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: tabs ::::: */
+
+tabs {
+ -moz-box-align: end;
+}
+
+.tabs-left,
+.tabs-right {
+ border-bottom: 1px solid #000000;
+}
+
+/* ::::: tabpanels ::::: */
+
+tabpanels {
+ border-right: 1px solid #000000;
+ border-left: 1px solid #000000;
+ border-bottom: 1px solid #000000;
+ padding: 5px;
+ background-color: #C7D0D9;
+}
+
+/* ::::: tab ::::: */
+
+tab {
+ margin: 0px;
+ border: 1px solid #000000;
+ border-bottom-width: 1px;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ padding: 1px 3px;
+ /*background-color: #8C9DAF;*/
+ background-color: #9CABBA;
+ color: #000000;
+ font: menu;
+}
+
+.tab-text {
+ margin: 0px !important;
+}
+
+.tab-image {
+ list-style-image: inherit;
+}
+
+/* ..... active state ..... */
+
+tab:hover:active {
+ border-top-color: #8190A5;
+ border-right-color: #8190A5;
+ border-left-color: #8190A5;
+ background-color: #8B9AAD;
+ color: #FFFFFF;
+}
+
+/* ..... selected state ..... */
+
+tab[selected="true"],
+tab[selected="true"]:hover:active {
+ border-top-color: #000000;
+ border-right-color: #000000;
+ border-bottom-color: transparent;
+ border-left-color: #000000;
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+/* ::::: tab-bottom ::::::::::
+ :: Tabs that are attached to the bottom of a panel, but not necessarily
+ :: a tabpanels.
+ ::::: */
+
+.tab-bottom {
+ border-top-color: #000000;
+ border-bottom-color: #000000;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+
+.tab-bottom:hover:active {
+ border-top-color: #000000;
+ border-bottom-color: transparent;
+}
+
+.tab-bottom[selected="true"] {
+ border-top-color: transparent !important;
+ border-bottom-color: #000000 !important;
+}
+
+tab:focus {
+ border-top-color: #000000;
+ border-left-color: #000000;
+ border-right-color: #000000;
+ border-bottom-color: #8190A5;
+}
+
+.tab-bottom > .tab-text {
+ font: message-box;
+ font-weight: bold;
+}
+
+/* ::::: tabs-bottom ::::: */
+
+.tabs-bottom {
+ -moz-box-align: start;
+}
+
+.tabs-bottom > .tabs-left,
+.tabs-bottom > .tabs-right {
+ border-top: 1px solid #000000;
+ border-bottom: none;
+}
diff --git a/comm/suite/themes/modern/global/textbox.css b/comm/suite/themes/modern/global/textbox.css
new file mode 100644
index 0000000000..d0bae80b5a
--- /dev/null
+++ b/comm/suite/themes/modern/global/textbox.css
@@ -0,0 +1,120 @@
+/* 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/. */
+
+/* ===== textbox.css ==================================================
+ == Styles used by the XUL textbox element.
+ ======================================================================= */
+
+@import url("chrome://global/content/autocomplete.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
+
+/* ::::: textbox ::::: */
+
+textbox {
+ margin: 2px 4px;
+ border: 1px solid;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 2px;
+ padding-inline-end: 0px;
+ background-color: #FFFFFF;
+ color: #000000;
+ font: inherit;
+}
+
+html|*.textbox-input::placeholder,
+html|*.textbox-textarea::placeholder {
+ color: #999999;
+ opacity: 1.0;
+}
+
+textbox,
+textbox[readonly="true"][focused="true"] {
+ border-color: #5D616E;
+}
+
+html|*.textbox-input,
+html|*.textbox-textarea {
+ cursor: text;
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px !important;
+ background-color: inherit;
+ color: inherit;
+ font: inherit;
+}
+
+/* ..... focused state ..... */
+
+textbox[focused="true"] {
+ border-color: #000000;
+}
+
+/* ..... disabled state ..... */
+
+textbox[disabled="true"] {
+ border-top-color: #98A5B2;
+ border-right-color: #F8FAFE;
+ border-bottom-color: #F8FAFE;
+ border-left-color: #BEC3D3;
+ background-color: #C7D0D9;
+ color: #999999;
+ cursor: default !important;
+}
+
+/* ..... readonly state ..... */
+
+textbox[readonly="true"] {
+ background-color: #C7D0D9;
+}
+
+/* ::::: plain textbox ::::: */
+
+textbox.plain {
+ background-color: transparent;
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px !important;
+}
+
+/* ::::: search textbox ::::: */
+
+.textbox-search-icon {
+ list-style-image: url("chrome://global/skin/icons/search.png");
+}
+
+.textbox-search-clear {
+ list-style-image: url("chrome://global/skin/icons/closebox.png");
+}
+
+/* ::::: scrollable textbox ::::: */
+
+.scrollfield {
+ border: none !important;
+ margin: 0px;
+ margin-top: 1px;
+ padding: 0px !important;
+ background: inherit;
+}
+
+.scrollfield > .textbox-internal-box {
+ border: none !important;
+ margin: 0px !important;
+ padding: 0px !important;
+}
+
+/* ::::: inline-edit textbox ::::: */
+
+.textbox-inline-edit {
+ margin: 0px !important;
+ border: 1px solid #000000 !important;
+}
+
+/* ::::: textboxes inside toolbarpaletteitems ::::: */
+
+toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box > html|*.textbox-input {
+ visibility: hidden;
+}
diff --git a/comm/suite/themes/modern/global/toolbar.css b/comm/suite/themes/modern/global/toolbar.css
new file mode 100644
index 0000000000..483b986c93
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar.css
@@ -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/. */
+
+/* ===== toolbar.css ====================================================
+ == Styles used by XUL toolbar-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: toolbox ::::: */
+
+toolbox {
+ border-bottom: 1px solid #494F5D;
+}
+
+/* ::::: toolbar ::::: */
+
+toolbar {
+ min-width: 1px; /* DON'T DELETE!
+ Prevents hiding of scrollbars in browser when window is made smaller.*/
+ min-height: 22px;
+ background: #C7D0D9;
+ color: #000000;
+}
+
+toolbar > .toolbar-box > .toolbar-holder {
+ border-top: 1px solid #EEF0F3;
+ border-right: 1px solid #86929E;
+ border-bottom: 1px solid #86929E;
+}
+
+/* ::::: menubar ::::: */
+
+menubar,
+toolbar[type="menubar"] {
+ cursor: default;
+ min-width: 1px; /* DON'T DELETE!
+ Prevents hiding of scrollbars in browser when window is made smaller.*/
+ background-color: #DDE3EB;
+ color: #000000;
+}
+
+menubar > .toolbar-box > .toolbar-holder,
+toolbar[type="menubar"] > .toolbar-box > .toolbar-holder {
+ border-top: 1px solid #EBF4FF;
+ border-right: 1px solid #86929E;
+ border-bottom: 1px solid #B9BFC7;
+ border-left: 1px solid #EEF4FC;
+}
+
+toolbar > toolbaritem > menubar > .toolbar-box > .toolbar-holder {
+ border: 0px none;
+}
+
+/* ::::: toolbargrippy ::::: */
+
+toolbargrippy {
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+ -moz-box-pack: end;
+ -moz-box-direction: reverse;
+ border-top: 1px solid #EEF0F3;
+ border-right: 1px solid #86929E;
+ border-bottom: 1px solid #86929E;
+ border-left: 1px solid #EEF0F3;
+ list-style-image: url("chrome://global/skin/toolbar/tbgrip-arrow.png");
+}
+
+toolbargrippy:hover:active {
+ border-color: #67737E;
+ background-color: #9DA9B6;
+ list-style-image: url("chrome://global/skin/toolbar/tbgrip-arrow-act.png");
+}
+
+.toolbargrippy-arrow {
+ margin: 0px 1px 3px;
+ width: 8px;
+ height: 5px;
+}
+
+/* ::::: menubar grippy ::::: */
+
+toolbar[type="menubar"] > .toolbar-box > toolbargrippy,
+menubar > .toolbar-box > toolbargrippy {
+ border-top: 1px solid #EBF4FF;
+ border-right: 1px solid #B9BFC7;
+ border-bottom: 1px solid #B9BFC7;
+ border-left: 1px solid #F4FAFF;
+ background-color: #DDE3EB;
+ list-style-image: url("chrome://global/skin/toolbar/mbgrip-arrow.png");
+}
+
+/* ::::: collapsed toolbargrippy and tray ::::: */
+
+toolbargrippy[tbgrippy-collapsed="true"] {
+ -moz-box-orient: horizontal;
+ -moz-box-pack: start;
+ -moz-box-direction: normal;
+ width: 40px;
+ list-style-image: url("chrome://global/skin/toolbar/tbgrip-arrow-clps.png");
+}
+
+toolbargrippy[tbgrippy-collapsed="true"] > .toolbargrippy-arrow {
+ margin-top: 1px;
+ margin-bottom: 1px;
+ margin-inline-start: 3px;
+ margin-inline-end: 0px;
+ width: 5px;
+ height: 8px;
+}
+
+toolbargrippy[tbgrippy-collapsed="true"]:hover:active > .toolbargrippy-arrow {
+ list-style-image: url("chrome://global/skin/toolbar/tbgrip-arrow-clps-act.png");
+}
+
+.collapsed-tray-holder {
+ background-color: #C7D0D9;
+}
+
+/* ::::: toolbar decorations ::::: */
+
+toolbarseparator {
+ margin: 0px 0.2em;
+ border-right: 1px solid #E2E7EB;
+ border-left: 1px solid #95A0AD;
+ width: 0px;
+}
+
+toolbarspacer {
+ width: 15px;
+}
+
+/* ::::: toolbarpaletteitem ::::: */
+
+toolbarpaletteitem {
+ cursor: grab;
+}
+
+.toolbarpaletteitem-box[type="spacer"],
+.toolbarpaletteitem-box[type="spring"] {
+ border: 1px solid #808080;
+ background-color: #F0F0F0 !important;
+}
+
+toolbarpaletteitem[place="toolbar"] > toolbarspacer {
+ width: 11px;
+}
+
+.toolbarpaletteitem-box[type="separator"][place="palette"] {
+ width: 2px;
+ height: 50px;
+}
+
+.toolbarpaletteitem-box[type="splitter"][place="palette"] {
+ width: 8px;
+ height: 50px;
+}
+
+.toolbarpaletteitem-box[type="spacer"][place="palette"],
+.toolbarpaletteitem-box[type="spring"][place="palette"] {
+ margin-bottom: 2px;
+ width: 50px;
+ height: 50px;
+}
+
+.toolbarpaletteitem-box[type="spring"][place] {
+ background: url("chrome://global/skin/toolbar/spring.png") no-repeat center;
+}
+
+/* ..... drag and drop feedback ..... */
+
+toolbarpaletteitem[place="toolbar"] {
+ margin-left: -2px;
+ margin-right: -2px;
+ border-left: 2px solid transparent;
+ border-right: 2px solid transparent;
+}
+
+toolbarpaletteitem[dragover="left"] {
+ border-left-color: #000000;
+}
+
+toolbarpaletteitem[dragover="right"] {
+ border-right-color: #000000;
+}
diff --git a/comm/suite/themes/modern/global/toolbar/chevron.gif b/comm/suite/themes/modern/global/toolbar/chevron.gif
new file mode 100644
index 0000000000..186aac9764
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/chevron.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/chevron.png b/comm/suite/themes/modern/global/toolbar/chevron.png
new file mode 100644
index 0000000000..7b7a9a1d9e
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/chevron.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/mbgrip-arrow.gif b/comm/suite/themes/modern/global/toolbar/mbgrip-arrow.gif
new file mode 100644
index 0000000000..0b0715ca1d
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/mbgrip-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/mbgrip-arrow.png b/comm/suite/themes/modern/global/toolbar/mbgrip-arrow.png
new file mode 100644
index 0000000000..6e4828cb66
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/mbgrip-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/spring.png b/comm/suite/themes/modern/global/toolbar/spring.png
new file mode 100644
index 0000000000..16ba2f6b1c
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/spring.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tb-mid.gif b/comm/suite/themes/modern/global/toolbar/tb-mid.gif
new file mode 100644
index 0000000000..b41b42b925
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tb-mid.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tb-mid.png b/comm/suite/themes/modern/global/toolbar/tb-mid.png
new file mode 100644
index 0000000000..ec36d448a4
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tb-mid.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.gif b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.gif
new file mode 100644
index 0000000000..a7af50a254
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.png b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.png
new file mode 100644
index 0000000000..23d527d458
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.gif b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.gif
new file mode 100644
index 0000000000..4e44940114
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.png b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.png
new file mode 100644
index 0000000000..4c277a704c
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.gif b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.gif
new file mode 100644
index 0000000000..b7d879acfa
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.png b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.png
new file mode 100644
index 0000000000..52dca015bc
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow-clps.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow.gif b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow.gif
new file mode 100644
index 0000000000..cc4c647156
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbar/tbgrip-arrow.png b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow.png
new file mode 100644
index 0000000000..e90a1c0a5a
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbar/tbgrip-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/toolbarbutton.css b/comm/suite/themes/modern/global/toolbarbutton.css
new file mode 100644
index 0000000000..1b0ba564fe
--- /dev/null
+++ b/comm/suite/themes/modern/global/toolbarbutton.css
@@ -0,0 +1,146 @@
+/* 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/. */
+
+/* ===== toolbarbutton.css ==============================================
+ == Styles used by XUL toolbarbutton elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: toolbarbutton ::::: */
+
+toolbarbutton {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border: 1px solid transparent;
+ padding: 1px 2px;
+ font: message-box;
+}
+
+.toolbarbutton-icon[label]:not([label=""]),
+.toolbarbutton-icon[type="menu"] {
+ margin-inline-end: 2px;
+}
+
+.toolbarbutton-text {
+ margin: 0px !important;
+ text-align: center;
+}
+
+.tabbable {
+ -moz-user-focus: normal !important;
+}
+
+toolbarbutton:focus,
+toolbarbutton:hover {
+ border-top-color: #EEF0F3;
+ border-right-color: #86929E;
+ border-bottom-color: #86929E;
+ border-left-color: #EEF0F3;
+}
+
+toolbarbutton:hover:active,
+toolbarbutton[open="true"] {
+ border-top-color: #86929E;
+ border-right-color: #EEF0F3;
+ border-bottom-color: #EEF0F3;
+ border-left-color: #86929E;
+ padding-top: 2px;
+ padding-bottom: 0px;
+ padding-inline-start: 3px;
+ padding-inline-end: 1px;
+}
+
+toolbarbutton[disabled="true"] {
+ border-color: transparent !important;
+ padding: 1px 2px !important;
+ color: #9399AB !important;
+}
+
+/* ..... checked state ..... */
+
+toolbarbutton[checked="true"] {
+ border-top-color: #454C55 !important;
+ border-right-color: #7D848D !important;
+ border-bottom-color: #7D848D !important;
+ border-left-color: #454C55 !important;
+}
+
+toolbarbutton:not(.devtools-toolbarbutton)[checked="true"] {
+ background-color: #8C9AA8 !important;
+ color: #EFF1F4;
+}
+
+toolbarbutton[checked="true"]:hover:active {
+ padding: 1px 2px;
+}
+
+/* ::::: toolbarbutton menu ::::: */
+
+.toolbarbutton-menu-dropmarker {
+ list-style-image: url("chrome://global/skin/button/tbmenu-arrow.png");
+ -moz-image-region: auto; /* cut off inheritance */
+ padding: 0px;
+ border: none !important;
+ background-color: transparent !important;
+}
+
+.toolbarbutton-menu-dropmarker[disabled="true"] {
+ list-style-image: url("chrome://global/skin/button/tbmenu-arrow-dis.png");
+}
+
+.toolbarbutton-menu-dropmarker[open="true"] {
+ list-style-image: url("chrome://global/skin/button/tbmenu-arrow-act.png");
+}
+
+/* ::::: toolbarbutton menu-button ::::: */
+
+.toolbarbutton-menubutton-button {
+ font: inherit;
+}
+
+/* ..... dropmarker ..... */
+
+.toolbarbutton-menubutton-dropmarker {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin-top: 20px;
+ margin-bottom: 15px;
+ margin-inline-start: 40px;
+ margin-inline-end: 5px;
+ padding: 0px;
+ border: none !important;
+ background-color: transparent !important;
+ width: 13px;
+ height: 11px;
+ list-style-image: url("chrome://global/skin/button/tbmbtn-arrow.png");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+toolbarbutton:hover > .toolbarbutton-menubutton-dropmarker,
+toolbarbutton:hover > stack > .toolbarbutton-menubutton-dropmarker {
+ list-style-image: url("chrome://global/skin/button/tbmbtn-arrow-hov.png");
+}
+
+toolbarbutton[open="true"] > .toolbarbutton-menubutton-dropmarker,
+toolbarbutton[open="true"] > stack > .toolbarbutton-menubutton-dropmarker {
+ list-style-image: url("chrome://global/skin/button/tbmbtn-arrow-act.png");
+}
+
+toolbarbutton[disabled="true"] > .toolbarbutton-menubutton-dropmarker,
+toolbarbutton[disabled="true"] > stack > .toolbarbutton-menubutton-dropmarker,
+toolbarbutton[disabled="true"]:hover > stack > .toolbarbutton-menubutton-dropmarker
+{
+ list-style-image: none;
+}
+
+/* ::::: Devtools toolbarbuttons ::::: */
+
+.devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
+.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 0px;
+}
diff --git a/comm/suite/themes/modern/global/tree.css b/comm/suite/themes/modern/global/tree.css
new file mode 100644
index 0000000000..0acc157a7f
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree.css
@@ -0,0 +1,278 @@
+/* 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/. */
+
+/* ===== tree.css ===================================================
+ == Styles used by the XUL outline element.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: tree ::::: */
+
+tree {
+ margin: 0px 4px;
+ border: 1px solid #5D616E;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+/* ::::: tree focusring ::::: */
+
+.focusring > .tree-stack > .tree-rows > .tree-bodybox {
+ border: 1px solid transparent;
+}
+
+.focusring:focus > .tree-stack > .tree-rows > .tree-bodybox {
+ border: 1px solid #000000;
+}
+
+/* ::::: tree rows ::::: */
+
+treechildren::-moz-tree-row {
+ border: 1px solid transparent;
+ background-color: transparent;
+ min-height: 18px;
+ height: 1.3em;
+}
+
+/* ::::: alternating background ::::: */
+treechildren[alternatingbackground="true"]::-moz-tree-row(odd) {
+ background-color: #f3f3f3;
+}
+
+treechildren[alternatingbackground="true"]::-moz-tree-row(odd, selected),
+treechildren::-moz-tree-row(selected) {
+ background-color: #C7D0D9;
+}
+
+treechildren[alternatingbackground="true"]::-moz-tree-row(odd, selected, focus),
+treechildren::-moz-tree-row(selected, focus) {
+ background-color: #424F63;
+}
+
+treechildren::-moz-tree-row(current, focus) {
+ border-top-color: #000000;
+ border-bottom-color: #000000;
+}
+
+/* ::::: tree cells ::::: */
+
+treechildren::-moz-tree-cell {
+ padding: 0px 2px;
+}
+
+treechildren::-moz-tree-cell-text {
+ color: inherit;
+}
+
+treechildren::-moz-tree-cell-text(selected) {
+ color: #000000;
+}
+
+treechildren::-moz-tree-cell-text(selected, focus) {
+ color: #FFFFFF;
+}
+
+/* ::::: lines connecting cells ::::: */
+
+treechildren::-moz-tree-line {
+ border: 1px dotted #808080;
+}
+
+treechildren::-moz-tree-line(selected, focus) {
+ border: 1px dotted #FFFFFF;
+}
+
+/* ::::: tree separator ::::: */
+
+treechildren::-moz-tree-separator {
+ border-top: 1px solid #7A8490;
+ border-bottom: 1px solid #FEFEFE;
+}
+
+/* ::::: drop feedback ::::: */
+
+treechildren::-moz-tree-cell-text(primary, dropOn) {
+ background-color: #424F63;
+ color: #FFFFFF;
+}
+
+treechildren::-moz-tree-drop-feedback {
+ background-color: #424F63;
+ width: 50px;
+ height: 2px;
+ margin-inline-start: 5px;
+}
+
+/* ::::: tree checkbox ::::: */
+
+treechildren::-moz-tree-checkbox {
+ list-style-image: url("chrome://global/skin/tree/checkbox.png");
+}
+
+treechildren::-moz-tree-checkbox(checked) {
+ list-style-image: url("chrome://global/skin/tree/checkbox-checked.png");
+}
+
+/* ::::: tree progress meter ::::: */
+
+treechildren::-moz-tree-progressmeter {
+ margin: 2px 4px;
+ border: 1px solid;
+ border-top-color: #8997A1;
+ border-right-color: #C7D0D9;
+ border-bottom-color: #C7D0D9;
+ border-left-color: #8997A1;
+ color: #6B7B8D;
+}
+
+treechildren::-moz-tree-progressmeter(progressUndetermined) {
+ list-style-image: url("chrome://global/skin/progressmeter/progress-busy.png");
+}
+
+treechildren::-moz-tree-cell-text(progressmeter) {
+ margin: 2px 4px;
+ }
+
+/* ::::: tree columns ::::: */
+
+treecol,
+treecolpicker {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border: 1px solid;
+ border-top-color: #C7D0D9;
+ border-right-color: #63676B;
+ border-bottom-color: #63676B;
+ border-left-color: #C7D0D9;
+ background-color: #C7D0D9;
+ color: #000000;
+ padding: 0px 4px;
+ min-width: 19px;
+}
+
+.treecol-image {
+ padding: 0px 1px;
+}
+
+.treecol-text {
+ margin: 0px !important;
+}
+
+treecol[hideheader="true"] {
+ border: none;
+ padding: 0;
+}
+
+/* ..... internal box ..... */
+
+treecol:hover:active,
+treecolpicker:hover:active {
+ border-top: 1px solid;
+ border-right: 1px solid;
+ border-bottom: 1px solid;
+ border-left: 2px solid;
+ border-color: #A5ABB0;
+ padding-top: 1px;
+ padding-bottom: 0px;
+ padding-inline-start: 5px;
+ padding-inline-end: 4px;
+}
+
+.treecol-image:hover:active {
+ padding-top: 1px;
+ padding-bottom: 0px;
+ padding-inline-start: 2px;
+ padding-inline-end: 1px;
+}
+
+/* ::::: column drag and drop styles ::::: */
+
+treecol[dragging="true"] {
+ border-top-color: #000000 !important;
+ border-right-color: #000000 !important;
+ border-bottom-color: #000000 !important;
+ border-left-color: #000000 !important;
+ background-color: #90A1B3 !important;
+ color: #FFFFFF !important;
+}
+
+treecol[insertafter="true"]:-moz-locale-dir(ltr),
+treecol[insertbefore="true"]:-moz-locale-dir(rtl) {
+ border-right-color: #000000;
+}
+
+treecol[insertafter="true"]:-moz-locale-dir(rtl),
+treecol[insertbefore="true"]:-moz-locale-dir(ltr) {
+ border-left-color: #000000;
+}
+
+treechildren::-moz-tree-column(insertbefore) {
+ border-inline-start: 1px solid #AAAAAA;
+}
+
+treechildren::-moz-tree-column(insertafter) {
+ border-inline-end: 1px solid #AAAAAA;
+}
+
+/* ::::: sort direction indicator ::::: */
+
+.treecol-sortdirection {
+ list-style-image: none;
+}
+
+.treecol-sortdirection[sortDirection="ascending"] {
+ list-style-image: url("chrome://global-platform/skin/tree/sort-asc.png");
+}
+
+.treecol-sortdirection[sortDirection="descending"] {
+ list-style-image: url("chrome://global-platform/skin/tree/sort-dsc.png");
+}
+
+/* ::::: column picker ::::: */
+
+.tree-columnpicker-icon {
+ list-style-image: url("chrome://global/skin/tree/columnpicker.png");
+}
+
+/* ::::: twisty ::::: */
+
+treechildren::-moz-tree-twisty {
+ padding-inline-end: 2px;
+ width: 10px; /* The image's width is 10 pixels */
+ list-style-image: url("chrome://global/skin/tree/twisty-clsd.png");
+}
+
+treechildren::-moz-tree-twisty(open) {
+ width: 10px; /* The image's width is 10 pixels */
+ list-style-image: url("chrome://global/skin/tree/twisty-open.png");
+}
+
+treechildren::-moz-tree-indentation {
+ width: 16px;
+}
+
+/* ::::: editable tree ::::: */
+
+treechildren::-moz-tree-row(selected, editing) {
+ background-color: transparent;
+}
+
+treechildren::-moz-tree-cell-text(selected, editing) {
+ color: inherit;
+}
+
+.tree-input {
+ -moz-appearance: none;
+ border-top-color: #C7D0D9;
+ border-bottom-color: #C7D0D9;
+ border-left-color: #C7D0D9;
+ border-right-color: #C7D0D9;
+ border: 1px solid #C7D0D9;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: -4px;
+ margin-inline-end: 0px;
+ padding: 1px;
+}
diff --git a/comm/suite/themes/modern/global/tree/checkbox-checked.gif b/comm/suite/themes/modern/global/tree/checkbox-checked.gif
new file mode 100644
index 0000000000..f9fa6338de
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/checkbox-checked.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/checkbox-checked.png b/comm/suite/themes/modern/global/tree/checkbox-checked.png
new file mode 100644
index 0000000000..bae3ab8cc7
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/checkbox-checked.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/checkbox.gif b/comm/suite/themes/modern/global/tree/checkbox.gif
new file mode 100644
index 0000000000..4b6110c037
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/checkbox.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/checkbox.png b/comm/suite/themes/modern/global/tree/checkbox.png
new file mode 100644
index 0000000000..541c2f6374
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/checkbox.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/columnpicker.gif b/comm/suite/themes/modern/global/tree/columnpicker.gif
new file mode 100644
index 0000000000..a4ef0d6080
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/columnpicker.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/columnpicker.png b/comm/suite/themes/modern/global/tree/columnpicker.png
new file mode 100644
index 0000000000..6ba273a2fc
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/columnpicker.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/sort-asc.gif b/comm/suite/themes/modern/global/tree/sort-asc.gif
new file mode 100644
index 0000000000..5e13c7398f
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/sort-asc.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/sort-asc.png b/comm/suite/themes/modern/global/tree/sort-asc.png
new file mode 100644
index 0000000000..4dc365f1fe
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/sort-asc.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/sort-dsc.gif b/comm/suite/themes/modern/global/tree/sort-dsc.gif
new file mode 100644
index 0000000000..fa957ac1e7
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/sort-dsc.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/sort-dsc.png b/comm/suite/themes/modern/global/tree/sort-dsc.png
new file mode 100644
index 0000000000..a24720dc1b
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/sort-dsc.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/twisty-clsd.gif b/comm/suite/themes/modern/global/tree/twisty-clsd.gif
new file mode 100644
index 0000000000..3efbd5b2d7
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/twisty-clsd.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/twisty-clsd.png b/comm/suite/themes/modern/global/tree/twisty-clsd.png
new file mode 100644
index 0000000000..df1ab82608
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/twisty-clsd.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/twisty-open.gif b/comm/suite/themes/modern/global/tree/twisty-open.gif
new file mode 100644
index 0000000000..2117e5ff70
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/twisty-open.gif
Binary files differ
diff --git a/comm/suite/themes/modern/global/tree/twisty-open.png b/comm/suite/themes/modern/global/tree/twisty-open.png
new file mode 100644
index 0000000000..955ebed9e3
--- /dev/null
+++ b/comm/suite/themes/modern/global/tree/twisty-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/global/wizard.css b/comm/suite/themes/modern/global/wizard.css
new file mode 100644
index 0000000000..62f3b010e8
--- /dev/null
+++ b/comm/suite/themes/modern/global/wizard.css
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ===== wizard.css =====================================================
+ == Styles used by the wizards which contains buttons for stepping
+ == through a wizard.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.wizard-header {
+ border-top: 1px solid #94AACE;
+ border-bottom: 1px solid #000000;
+ padding: 10px 0px;
+ background-color: #5B7693;
+ color: #ffffff;
+}
+
+.wizard-header-description[value=""] {
+ display: none;
+}
+
+.wizard-header-label {
+ margin-inline-start: 23px !important;
+ font-weight: bold;
+}
+
+.wizard-header-description {
+ margin-inline-start: 44px !important;
+}
+
+wizard[branded="true"] .wizard-header-icon {
+ list-style-image: url("chrome://branding/content/icon48.png");
+ margin-inline-end: 5px;
+}
+
+.wizard-page-box {
+ margin: 10px 44px;
+}
+
+.wizard-buttons-separator {
+ margin-bottom: 0px !important;
+}
+
+.wizard-buttons-box-2,
+.wizard-buttons-btm {
+ padding: 5px;
+}
+
+.wizard-button[dlgtype="finish"],
+.wizard-button[dlgtype="next"] {
+ margin-inline-start: 0px;
+}
+
+.wizard-button[dlgtype="back"] {
+ margin-inline-end: 0px;
+}
diff --git a/comm/suite/themes/modern/icon.png b/comm/suite/themes/modern/icon.png
new file mode 100644
index 0000000000..7afda68363
--- /dev/null
+++ b/comm/suite/themes/modern/icon.png
Binary files differ
diff --git a/comm/suite/themes/modern/install.rdf b/comm/suite/themes/modern/install.rdf
new file mode 100644
index 0000000000..582aefea56
--- /dev/null
+++ b/comm/suite/themes/modern/install.rdf
@@ -0,0 +1,47 @@
+<?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/. -->
+
+
+#filter substitution
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>modern@themes.mozilla.org</em:id>
+ <em:version>@SEAMONKEY_VERSION@</em:version>
+
+ <!-- Target Application this theme can install into,
+ with minimum and maximum supported versions. -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id>
+ <em:minVersion>@SEAMONKEY_VERSION@</em:minVersion>
+ <em:maxVersion>@SEAMONKEY_VERSION@</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>SeaMonkey Modern</em:name>
+ <em:description>A contemporary theme for all components.</em:description>
+
+ <!-- EXTENSION AUTHORS!
+ DO NOT COPY THIS PROPERTY INTO YOUR INSTALL RDF FILES
+ It will cause users not to be informed of incompatibilities
+ with your extension when they are updated with Software Update
+ and your extension will become unavailable to them!
+ -->
+ <em:appManaged>true</em:appManaged>
+
+ <em:locked>true</em:locked>
+
+ <!-- Front End Integration Hooks (used by Theme Manager)-->
+ <em:creator>Mozilla Contributors</em:creator>
+ <em:contributor>Mozilla Contributors</em:contributor>
+
+ <em:internalName>modern/1.0</em:internalName>
+ </Description>
+
+</RDF>
diff --git a/comm/suite/themes/modern/jar.mn b/comm/suite/themes/modern/jar.mn
new file mode 100644
index 0000000000..cecaf5359b
--- /dev/null
+++ b/comm/suite/themes/modern/jar.mn
@@ -0,0 +1,766 @@
+# 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/.
+
+modern.jar:
+% override chrome://mozapps/skin/places/defaultFavicon.svg chrome://communicator/skin/places/bookmark-item.svg
+% skin communicator modern/1.0 %skin/modern/communicator/
+% skin editor modern/1.0 %skin/modern/editor/
+% skin global modern/1.0 %skin/modern/global/
+% skin global-platform modern/1.0 %skin/modern/global/gtk/
+% skin global-platform modern/1.0 %skin/modern/global/mac/ os=Darwin
+% skin global-platform modern/1.0 %skin/modern/global/win/ os=WINNT
+% skin messenger modern/1.0 %skin/modern/messenger/
+% skin mozapps modern/1.0 %skin/modern/mozapps/
+% skin navigator modern/1.0 %skin/modern/navigator/
+% skin messenger-newsblog modern/1.0 %skin/modern/messenger-newsblog/
+% style chrome://communicator/content/customizeToolbar.xul chrome://navigator/skin/navigator.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/primaryToolbar.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/messengercompose/messengercompose.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/smime/msgCompSMIMEOverlay.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://messenger/skin/addressbook/addressbook.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://editor/skin/editorPrimaryToolbar.css
+% style chrome://communicator/content/customizeToolbar.xul chrome://editor/skin/editorFormatToolbar.css
+ skin/modern/communicator/brand.css (communicator/brand.css)
+ skin/modern/communicator/blockedSite.css (communicator/blockedSite.css)
+ skin/modern/communicator/certError.css (communicator/certError.css)
+ skin/modern/communicator/customizeToolbar.css (communicator/customizeToolbar.css)
+ skin/modern/communicator/datetimepicker.css (communicator/datetimepicker.css)
+ skin/modern/communicator/numberbox.css (communicator/numberbox.css)
+ skin/modern/communicator/prefpanels.css (communicator/prefpanels.css)
+ skin/modern/communicator/preferences.css (communicator/preferences.css)
+ skin/modern/communicator/spinbuttons.css (communicator/spinbuttons.css)
+ skin/modern/communicator/tasksOverlay.css (communicator/tasksOverlay.css)
+ skin/modern/communicator/button.css (communicator/button.css)
+ skin/modern/communicator/toolbar.css (communicator/toolbar.css)
+ skin/modern/communicator/dialogs.css (communicator/dialogs.css)
+ skin/modern/communicator/smileys.css (communicator/smileys.css)
+ skin/modern/communicator/communicator.css (communicator/communicator.css)
+ skin/modern/communicator/aboutPrivateBrowsing.css (communicator/aboutPrivateBrowsing.css)
+ skin/modern/communicator/aboutSessionRestore.css (communicator/aboutSessionRestore.css)
+ skin/modern/communicator/viewSourceOverlay.css (communicator/viewSourceOverlay.css)
+ skin/modern/communicator/brand/throbber-anim.png (communicator/brand/throbber-anim.png)
+ skin/modern/communicator/brand/throbber-single.png (communicator/brand/throbber-single.png)
+ skin/modern/communicator/brand/throbber16-anim.png (communicator/brand/throbber16-anim.png)
+ skin/modern/communicator/brand/throbber16-single.png (communicator/brand/throbber16-single.png)
+ skin/modern/communicator/console/console.css (communicator/console/console.css)
+ skin/modern/communicator/console/error-caret.png (communicator/console/error-caret.png)
+ skin/modern/communicator/console/error-dash.png (communicator/console/error-dash.png)
+ skin/modern/communicator/dataman/dataman.css (communicator/dataman/dataman.css)
+ skin/modern/communicator/dataman/datamanIcon-16.png (communicator/dataman/datamanIcon-16.png)
+ skin/modern/communicator/downloads/dl-remove.png (communicator/downloads/dl-remove.png)
+ skin/modern/communicator/downloads/downloadButtons.png (communicator/downloads/downloadButtons.png)
+ skin/modern/communicator/downloads/downloadmanager.css (communicator/downloads/downloadmanager.css)
+ skin/modern/communicator/feed-subscribe.css (communicator/feed-subscribe.css)
+ skin/modern/communicator/feed-subscribe-ui.css (communicator/feed-subscribe-ui.css)
+ skin/modern/communicator/fullscreen-video.css (communicator/fullscreen-video.css)
+ skin/modern/communicator/helpviewer/help.css (communicator/helpviewer/help.css)
+ skin/modern/communicator/icons/alwaysAsk.png (communicator/icons/alwaysAsk.png)
+ skin/modern/communicator/icons/application.png (communicator/icons/application.png)
+ skin/modern/communicator/icons/audioFeedIcon.png (communicator/icons/feedIcon.png)
+ skin/modern/communicator/icons/closeFullScreenVideo.png (communicator/icons/closeFullScreenVideo.png)
+ skin/modern/communicator/icons/btn1.png (communicator/icons/btn1.png)
+ skin/modern/communicator/icons/common.png (communicator/icons/common.png)
+ skin/modern/communicator/icons/common-small.png (communicator/icons/common-small.png)
+ skin/modern/communicator/icons/feedIcon.png (communicator/icons/feedIcon.png)
+ skin/modern/communicator/icons/feedIcon16.png (communicator/icons/feedIcon16.png)
+ skin/modern/communicator/icons/geo.png (communicator/icons/geo.png)
+ skin/modern/communicator/icons/geolocation-16.png (communicator/icons/geolocation-16.png)
+ skin/modern/communicator/icons/geolocation-64.png (communicator/icons/geolocation-64.png)
+ skin/modern/communicator/icons/identity.png (communicator/icons/identity.png)
+ skin/modern/communicator/icons/loading.png (communicator/icons/loading.png)
+ skin/modern/communicator/icons/lock-broken.png (communicator/icons/lock-broken.png)
+ skin/modern/communicator/icons/lock-broken-16.png (communicator/icons/lock-broken-16.png)
+ skin/modern/communicator/icons/lock-insecure.png (communicator/icons/lock-insecure.png)
+ skin/modern/communicator/icons/lock-insecure-16.png (communicator/icons/lock-insecure-16.png)
+ skin/modern/communicator/icons/lock-secure.png (communicator/icons/lock-secure.png)
+ skin/modern/communicator/icons/lock-secure-16.png (communicator/icons/lock-secure-16.png)
+ skin/modern/communicator/icons/notification-16.png (communicator/icons/notification-16.png)
+ skin/modern/communicator/icons/notification-64.png (communicator/icons/notification-64.png)
+ skin/modern/communicator/icons/notification-icons.svg (communicator/icons/notification-icons.svg)
+ skin/modern/communicator/icons/offline.png (communicator/icons/offline.png)
+ skin/modern/communicator/icons/online.png (communicator/icons/online.png)
+ skin/modern/communicator/icons/save.png (communicator/icons/save.png)
+ skin/modern/communicator/icons/smileys/smiley-smile.png (communicator/icons/smileys/smiley-smile.png)
+ skin/modern/communicator/icons/smileys/smiley-frown.png (communicator/icons/smileys/smiley-frown.png)
+ skin/modern/communicator/icons/smileys/smiley-wink.png (communicator/icons/smileys/smiley-wink.png)
+ skin/modern/communicator/icons/smileys/smiley-tongue.png (communicator/icons/smileys/smiley-tongue.png)
+ skin/modern/communicator/icons/smileys/smiley-laughing.png (communicator/icons/smileys/smiley-laughing.png)
+ skin/modern/communicator/icons/smileys/smiley-embarrassed.png (communicator/icons/smileys/smiley-embarrassed.png)
+ skin/modern/communicator/icons/smileys/smiley-undecided.png (communicator/icons/smileys/smiley-undecided.png)
+ skin/modern/communicator/icons/smileys/smiley-surprise.png (communicator/icons/smileys/smiley-surprise.png)
+ skin/modern/communicator/icons/smileys/smiley-kiss.png (communicator/icons/smileys/smiley-kiss.png)
+ skin/modern/communicator/icons/smileys/smiley-yell.png (communicator/icons/smileys/smiley-yell.png)
+ skin/modern/communicator/icons/smileys/smiley-cool.png (communicator/icons/smileys/smiley-cool.png)
+ skin/modern/communicator/icons/smileys/smiley-money.png (communicator/icons/smileys/smiley-money.png)
+ skin/modern/communicator/icons/smileys/smiley-foot.png (communicator/icons/smileys/smiley-foot.png)
+ skin/modern/communicator/icons/smileys/smiley-innocent.png (communicator/icons/smileys/smiley-innocent.png)
+ skin/modern/communicator/icons/smileys/smiley-cry.png (communicator/icons/smileys/smiley-cry.png)
+ skin/modern/communicator/icons/smileys/smiley-sealed.png (communicator/icons/smileys/smiley-sealed.png)
+ skin/modern/communicator/icons/videoFeedIcon.png (communicator/icons/feedIcon.png)
+ skin/modern/communicator/places/allBookmarks.png (communicator/places/allBookmarks.png)
+ skin/modern/communicator/places/bookmark.png (communicator/places/bookmark.png)
+ skin/modern/communicator/places/bookmark-folder-closed.png (communicator/places/bookmark-folder-closed.png)
+ skin/modern/communicator/places/bookmark-folder-dis.png (communicator/places/bookmark-folder-dis.png)
+ skin/modern/communicator/places/bookmark-folder-open.png (communicator/places/bookmark-folder-open.png)
+ skin/modern/communicator/places/bookmark-item-dis.png (communicator/places/bookmark-item-dis.png)
+ skin/modern/communicator/places/bookmark-item-updated.png (communicator/places/bookmark-item-updated.png)
+ skin/modern/communicator/places/bookmark-item.svg (communicator/places/bookmark-item.svg)
+ skin/modern/communicator/places/bookmarks.css (communicator/places/bookmarks.css)
+ skin/modern/communicator/places/bookmarksMenu.png (communicator/places/bookmarksMenu.png)
+ skin/modern/communicator/places/bookmarksToolbar.css (communicator/places/bookmarksToolbar.css)
+ skin/modern/communicator/places/bookmarksToolbar.png (communicator/places/bookmarksToolbar.png)
+ skin/modern/communicator/places/calendar.png (communicator/places/calendar.png)
+ skin/modern/communicator/places/editBookmarkOverlay.css (communicator/places/editBookmarkOverlay.css)
+ skin/modern/communicator/places/history.png (communicator/places/history.png)
+ skin/modern/communicator/places/livemark-folder.png (communicator/places/livemark-folder.png)
+ skin/modern/communicator/places/organizer.css (communicator/places/organizer.css)
+ skin/modern/communicator/places/query.png (communicator/places/query.png)
+ skin/modern/communicator/places/tag.png (communicator/places/tag.png)
+ skin/modern/communicator/places/toolbarDropMarker.png (communicator/places/toolbarDropMarker.png)
+ skin/modern/communicator/places/unsortedBookmarks.png (communicator/places/unsortedBookmarks.png)
+ skin/modern/communicator/profile/migrate.png (communicator/profile/migrate.png)
+ skin/modern/communicator/profile/profile.png (communicator/profile/profile.png)
+ skin/modern/communicator/profile/profile.css (communicator/profile/profile.css)
+ skin/modern/communicator/sanitizeDialog.css (communicator/sanitizeDialog.css)
+ skin/modern/communicator/search/engineManager.css (communicator/search/engineManager.css)
+ skin/modern/communicator/search/mainwindow-dropdown-arrow.png (communicator/search/mainwindow-dropdown-arrow.png)
+ skin/modern/communicator/search/search.css (communicator/search/search.css)
+ skin/modern/communicator/search/searchbar.css (communicator/search/searchbar.css)
+ skin/modern/communicator/sidebar/sidebarBindings.xml (communicator/sidebar/sidebarBindings.xml)
+ skin/modern/communicator/sidebar/sbar-top.png (communicator/sidebar/sbar-top.png)
+ skin/modern/communicator/sidebar/sbar-top-tabopen.png (communicator/sidebar/sbar-top-tabopen.png)
+ skin/modern/communicator/sidebar/sbtab-rit-top-act.png (communicator/sidebar/sbtab-rit-top-act.png)
+ skin/modern/communicator/sidebar/sbtab-rit-btm-act.png (communicator/sidebar/sbtab-rit-btm-act.png)
+ skin/modern/communicator/sidebar/sbtab-rit-top.png (communicator/sidebar/sbtab-rit-top.png)
+ skin/modern/communicator/sidebar/sbtab-mid.png (communicator/sidebar/sbtab-mid.png)
+ skin/modern/communicator/sidebar/sbtab-lft-act.png (communicator/sidebar/sbtab-lft-act.png)
+ skin/modern/communicator/sidebar/sbtab-mid-act.png (communicator/sidebar/sbtab-mid-act.png)
+ skin/modern/communicator/sidebar/sbtab-rit-top-hov.png (communicator/sidebar/sbtab-rit-top-hov.png)
+ skin/modern/communicator/sidebar/sbtab-rit-btm-hov.png (communicator/sidebar/sbtab-rit-btm-hov.png)
+ skin/modern/communicator/sidebar/sbtab-rit-top-sel.png (communicator/sidebar/sbtab-rit-top-sel.png)
+ skin/modern/communicator/sidebar/sbtab-mid-sel.png (communicator/sidebar/sbtab-mid-sel.png)
+ skin/modern/communicator/sidebar/sbtab-lft.png (communicator/sidebar/sbtab-lft.png)
+ skin/modern/communicator/sidebar/sbtab-lft-sel.png (communicator/sidebar/sbtab-lft-sel.png)
+ skin/modern/communicator/sidebar/sbpicker-arrow.png (communicator/sidebar/sbpicker-arrow.png)
+ skin/modern/communicator/sidebar/customize.css (communicator/sidebar/customize.css)
+ skin/modern/communicator/sidebar/preview.css (communicator/sidebar/preview.css)
+ skin/modern/communicator/sidebar/sidebar.css (communicator/sidebar/sidebar.css)
+ skin/modern/communicator/sidebar/sidebarListView.css (communicator/sidebar/sidebarListView.css)
+ skin/modern/communicator/taskbar/taskbar.png (communicator/taskbar/taskbar.png)
+ skin/modern/communicator/taskbar/taskmenu-browser.png (communicator/taskbar/taskmenu-browser.png)
+ skin/modern/communicator/taskbar/taskmenu-composer.png (communicator/taskbar/taskmenu-composer.png)
+ skin/modern/communicator/taskbar/taskmenu-mailnews.png (communicator/taskbar/taskmenu-mailnews.png)
+ skin/modern/communicator/taskbar/taskmenu-abook.png (communicator/taskbar/taskmenu-abook.png)
+ skin/modern/communicator/toolbar/prtb-grip-btm-act.png (communicator/toolbar/prtb-grip-btm-act.png)
+ skin/modern/communicator/toolbar/prtb-grip-btm.png (communicator/toolbar/prtb-grip-btm.png)
+ skin/modern/communicator/toolbar/prtb-grip-mid-act.png (communicator/toolbar/prtb-grip-mid-act.png)
+ skin/modern/communicator/toolbar/prtb-grip-mid.png (communicator/toolbar/prtb-grip-mid.png)
+ skin/modern/communicator/toolbar/prtb-grip-top-act.png (communicator/toolbar/prtb-grip-top-act.png)
+ skin/modern/communicator/toolbar/prtb-grip-top.png (communicator/toolbar/prtb-grip-top.png)
+ skin/modern/communicator/toolbar/toolbarBindings.xml (communicator/toolbar/toolbarBindings.xml)
+ skin/modern/communicator/toolbar/prtb-bg-line.png (communicator/toolbar/prtb-bg-line.png)
+ skin/modern/communicator/toolbar/prtb-bg-noline.png (communicator/toolbar/prtb-bg-noline.png)
+ skin/modern/communicator/xpinstall/xpinstall.css (communicator/xpinstall/xpinstall.css)
+ skin/modern/editor/EditorDialog.css (editor/EditorDialog.css)
+ skin/modern/editor/editor.css (editor/editor.css)
+ skin/modern/editor/editorModeToolbar.css (editor/editorModeToolbar.css)
+ skin/modern/editor/editorPrimaryToolbar.css (editor/editorPrimaryToolbar.css)
+ skin/modern/editor/editorFormatToolbar.css (editor/editorFormatToolbar.css)
+ skin/modern/editor/icons/editmode-html.png (editor/icons/editmode-html.png)
+ skin/modern/editor/icons/editmode-normal.png (editor/icons/editmode-normal.png)
+ skin/modern/editor/icons/editmode-preview.png (editor/icons/editmode-preview.png)
+ skin/modern/editor/icons/editmode-tags.png (editor/icons/editmode-tags.png)
+ skin/modern/editor/icons/img-align-btm-sel.png (editor/icons/img-align-btm-sel.png)
+ skin/modern/editor/icons/img-align-btm.png (editor/icons/img-align-btm.png)
+ skin/modern/editor/icons/img-align-lft.png (editor/icons/img-align-lft.png)
+ skin/modern/editor/icons/img-align-mid-sel.png (editor/icons/img-align-mid-sel.png)
+ skin/modern/editor/icons/img-align-mid.png (editor/icons/img-align-mid.png)
+ skin/modern/editor/icons/img-align-rit.png (editor/icons/img-align-rit.png)
+ skin/modern/editor/icons/img-align-top-sel.png (editor/icons/img-align-top-sel.png)
+ skin/modern/editor/icons/img-align-top.png (editor/icons/img-align-top.png)
+ skin/modern/editor/icons/mast-editor.png (editor/icons/mast-editor.png)
+ skin/modern/editor/icons/btn1.png (editor/icons/btn1.png)
+ skin/modern/editor/icons/btn2.png (editor/icons/btn2.png)
+ skin/modern/editor/icons/multicolor.png (editor/icons/multicolor.png)
+ skin/modern/editor/icons/progress-busy.png (editor/icons/progress-busy.png)
+ skin/modern/editor/icons/progress-done.png (editor/icons/progress-done.png)
+ skin/modern/editor/icons/progress-failed.png (editor/icons/progress-failed.png)
+ skin/modern/global/globalBindings.xml (global/globalBindings.xml)
+ skin/modern/global/about.css (global/about.css)
+ skin/modern/global/aboutCache.css (global/aboutCache.css)
+ skin/modern/global/aboutCacheEntry.css (global/aboutCacheEntry.css)
+ skin/modern/global/aboutMemory.css (global/aboutMemory.css)
+ skin/modern/global/aboutSupport.css (global/aboutSupport.css)
+ skin/modern/global/appPicker.css (global/appPicker.css)
+ skin/modern/global/autocomplete.css (global/autocomplete.css)
+ skin/modern/global/button.css (global/button.css)
+ skin/modern/global/checkbox.css (global/checkbox.css)
+ skin/modern/global/colorpicker.css (global/colorpicker.css)
+ skin/modern/global/config.css (global/config.css)
+ skin/modern/global/datetimeinputpickers.css (shared/datetimeinputpickers.css)
+ skin/modern/global/datetimepopup.css (shared/datetimepopup.css)
+ skin/modern/global/dialog.css (global/dialog.css)
+ skin/modern/global/dropmarker.css (global/dropmarker.css)
+ skin/modern/global/filefield.css (global/filefield.css)
+ skin/modern/global/filepicker.css (global/filepicker.css)
+ skin/modern/global/findBar.css (global/findBar.css)
+ skin/modern/global/global.css (global/global.css)
+ skin/modern/global/groupbox.css (global/groupbox.css)
+ skin/modern/global/listbox.css (global/listbox.css)
+ skin/modern/global/menu.css (global/menu.css)
+ skin/modern/global/menulist.css (global/menulist.css)
+ skin/modern/global/netError.css (global/netError.css)
+ skin/modern/global/notification.css (global/notification.css)
+ skin/modern/global/plugins.css (global/plugins.css)
+ skin/modern/global/popup.css (global/popup.css)
+ skin/modern/global/preferences.css (global/preferences.css)
+ skin/modern/global/printPreview.css (global/printPreview.css)
+ skin/modern/global/printPageSetup.css (global/printPageSetup.css)
+ skin/modern/global/progressmeter.css (global/progressmeter.css)
+ skin/modern/global/radio.css (global/radio.css)
+ skin/modern/global/resizer.css (global/resizer.css)
+ skin/modern/global/richlistbox.css (global/richlistbox.css)
+ skin/modern/global/scale.css (global/scale.css)
+ skin/modern/global/scrollbox.css (global/scrollbox.css)
+ skin/modern/global/splitter.css (global/splitter.css)
+ skin/modern/global/tabbox.css (global/tabbox.css)
+ skin/modern/global/textbox.css (global/textbox.css)
+ skin/modern/global/toolbar.css (global/toolbar.css)
+ skin/modern/global/toolbarbutton.css (global/toolbarbutton.css)
+ skin/modern/global/tree.css (global/tree.css)
+ skin/modern/global/wizard.css (global/wizard.css)
+* skin/modern/global/scrollbars.css (global/scrollbars.css)
+ skin/modern/global/scrollbars-mini.css (global/scrollbars-mini.css)
+ skin/modern/global/alerts/alert.css (global/alerts/alert.css)
+ skin/modern/global/alerts/notification-48.png (global/alerts/notification-48.png)
+ skin/modern/global/arrow/arrow-dn-dis.png (global/arrow/arrow-dn-dis.png)
+ skin/modern/global/arrow/arrow-dn.png (global/arrow/arrow-dn.png)
+ skin/modern/global/arrow/arrow-lft-dis.png (global/arrow/arrow-lft-dis.png)
+ skin/modern/global/arrow/arrow-lft.png (global/arrow/arrow-lft.png)
+ skin/modern/global/arrow/arrow-lft-sharp.png (global/arrow/arrow-lft-sharp.png)
+ skin/modern/global/arrow/arrow-lft-sharp-end.png (global/arrow/arrow-lft-sharp-end.png)
+ skin/modern/global/arrow/arrow-rit-dis.png (global/arrow/arrow-rit-dis.png)
+ skin/modern/global/arrow/arrow-rit.png (global/arrow/arrow-rit.png)
+ skin/modern/global/arrow/arrow-rit-sharp.png (global/arrow/arrow-rit-sharp.png)
+ skin/modern/global/arrow/arrow-rit-sharp-end.png (global/arrow/arrow-rit-sharp-end.png)
+ skin/modern/global/arrow/arrow-up-dis.png (global/arrow/arrow-up-dis.png)
+ skin/modern/global/arrow/arrow-up.png (global/arrow/arrow-up.png)
+ skin/modern/global/button/tbmbtn-arrow-act.png (global/button/tbmbtn-arrow-act.png)
+ skin/modern/global/button/tbmbtn-arrow-hov.png (global/button/tbmbtn-arrow-hov.png)
+ skin/modern/global/button/tbmbtn-arrow.png (global/button/tbmbtn-arrow.png)
+ skin/modern/global/button/tbmenu-arrow-act.png (global/button/tbmenu-arrow-act.png)
+ skin/modern/global/button/tbmenu-arrow-dis.png (global/button/tbmenu-arrow-dis.png)
+ skin/modern/global/button/tbmenu-arrow.png (global/button/tbmenu-arrow.png)
+ skin/modern/global/checkbox/cbox-act-check.png (global/checkbox/cbox-act-check.png)
+ skin/modern/global/checkbox/cbox-act.png (global/checkbox/cbox-act.png)
+ skin/modern/global/checkbox/cbox-check.png (global/checkbox/cbox-check.png)
+ skin/modern/global/checkbox/cbox-dis-check.png (global/checkbox/cbox-dis-check.png)
+ skin/modern/global/checkbox/cbox-dis.png (global/checkbox/cbox-dis.png)
+ skin/modern/global/checkbox/cbox.png (global/checkbox/cbox.png)
+ skin/modern/global/dirListing/dirListing.css (global/dirListing/dirListing.css)
+ skin/modern/global/filepicker/blank.png (global/filepicker/blank.png)
+ skin/modern/global/filepicker/dir-closed.png (global/filepicker/dir-closed.png)
+ skin/modern/global/filepicker/dir-open.png (global/filepicker/dir-open.png)
+ skin/modern/global/filepicker/folder-up.png (global/filepicker/folder-up.png)
+ skin/modern/global/filepicker/folder-home.png (global/filepicker/folder-home.png)
+ skin/modern/global/filepicker/folder-new.png (global/filepicker/folder-new.png)
+ skin/modern/global/icons/authentication-48.png (global/icons/authentication-48.png)
+ skin/modern/global/icons/autoscroll.png (global/icons/autoscroll.png)
+ skin/modern/global/icons/blacklist_favicon.png (global/icons/blacklist_favicon.png)
+ skin/modern/global/icons/blacklist_large.png (global/icons/blacklist_large.png)
+ skin/modern/global/icons/close-act.png (global/icons/close-act.png)
+ skin/modern/global/icons/close-dis.png (global/icons/close-dis.png)
+ skin/modern/global/icons/close-hov.png (global/icons/close-hov.png)
+ skin/modern/global/icons/close.png (global/icons/close.png)
+ skin/modern/global/icons/closebox.png (global/icons/closebox.png)
+ skin/modern/global/icons/Error.png (global/icons/Error.png)
+ skin/modern/global/icons/error-16.png (global/icons/error-16.png)
+ skin/modern/global/icons/error-24.png (global/icons/error-24.png)
+ skin/modern/global/icons/error-48.png (global/icons/error-48.png)
+ skin/modern/global/icons/error-64.png (global/icons/error-64.png)
+ skin/modern/global/icons/find.png (global/icons/find.png)
+ skin/modern/global/icons/information-16.png (global/icons/information-16.png)
+ skin/modern/global/icons/information-24.png (global/icons/information-24.png)
+ skin/modern/global/icons/information-32.png (global/icons/information-32.png)
+ skin/modern/global/icons/information-48.png (global/icons/information-48.png)
+ skin/modern/global/icons/information-64.png (global/icons/information-64.png)
+ skin/modern/global/icons/notfound.png (global/icons/notfound.png)
+ skin/modern/global/icons/pg-landscape.png (global/icons/pg-landscape.png)
+ skin/modern/global/icons/pg-landscape-small.png (global/icons/pg-landscape-small.png)
+ skin/modern/global/icons/pg-portrait.png (global/icons/pg-portrait.png)
+ skin/modern/global/icons/pg-portrait-small.png (global/icons/pg-portrait-small.png)
+ skin/modern/global/icons/Question.png (global/icons/Question.png)
+ skin/modern/global/icons/question-16.png (global/icons/question-16.png)
+ skin/modern/global/icons/question-24.png (global/icons/question-24.png)
+ skin/modern/global/icons/question-48.png (global/icons/question-48.png)
+ skin/modern/global/icons/question-64.png (global/icons/question-64.png)
+ skin/modern/global/icons/resizer.png (global/icons/resizer.png)
+ skin/modern/global/icons/resizer-rtl.png (global/icons/resizer-rtl.png)
+ skin/modern/global/icons/search.png (global/icons/search.png)
+ skin/modern/global/icons/sslWarning.png (global/icons/sslWarning.png)
+ skin/modern/global/icons/warning-16.png (global/icons/warning-16.png)
+ skin/modern/global/icons/warning-24.png (global/icons/warning-24.png)
+ skin/modern/global/icons/warning-32.png (global/icons/warning-32.png)
+ skin/modern/global/icons/warning-48.png (global/icons/warning-48.png)
+ skin/modern/global/icons/warning-64.png (global/icons/warning-64.png)
+ skin/modern/global/icons/wrap.png (global/icons/wrap.png)
+ skin/modern/global/icons/calendar-arrow-left.svg (shared/icons/calendar-arrow-left.svg)
+ skin/modern/global/icons/calendar-arrow-right.svg (shared/icons/calendar-arrow-right.svg)
+ skin/modern/global/icons/input-clear.svg (shared/icons/input-clear.svg)
+ skin/modern/global/icons/spinner-arrow-down.svg (shared/icons/spinner-arrow-down.svg)
+ skin/modern/global/icons/spinner-arrow-up.svg (shared/icons/spinner-arrow-up.svg)
+ skin/modern/global/media/closedCaptionButton.svg (global/media/closedCaptionButton.svg)
+ skin/modern/global/media/error.png (global/media/error.png)
+ skin/modern/global/media/fullscreenButton.svg (global/media/fullscreenButton.svg)
+ skin/modern/global/media/imagedoc-darknoise.png (global/media/imagedoc-darknoise.png)
+ skin/modern/global/media/imagedoc-lightnoise.png (global/media/imagedoc-lightnoise.png)
+ skin/modern/global/media/muteButton.svg (global/media/muteButton.svg)
+ skin/modern/global/media/pauseButton.svg (global/media/pauseButton.svg)
+ skin/modern/global/media/playButton.svg (global/media/playButton.svg)
+ skin/modern/global/media/stalled.png (global/media/stalled.png)
+ skin/modern/global/media/throbber.png (global/media/throbber.png)
+ skin/modern/global/media/TopLevelImageDocument.css (global/media/TopLevelImageDocument.css)
+ skin/modern/global/media/TopLevelVideoDocument.css (global/media/TopLevelVideoDocument.css)
+ skin/modern/global/media/videocontrols.css (global/media/videocontrols.css)
+ skin/modern/global/menu/menu-arrow-dis.png (global/menu/menu-arrow-dis.png)
+ skin/modern/global/menu/menu-arrow-hov.png (global/menu/menu-arrow-hov.png)
+ skin/modern/global/menu/menu-arrow.png (global/menu/menu-arrow.png)
+ skin/modern/global/menu/menu-check-dis.png (global/menu/menu-check-dis.png)
+ skin/modern/global/menu/menu-check-hov.png (global/menu/menu-check-hov.png)
+ skin/modern/global/menu/menu-check.png (global/menu/menu-check.png)
+ skin/modern/global/menu/menu-radio-dis.png (global/menu/menu-radio-dis.png)
+ skin/modern/global/menu/menu-radio-hov.png (global/menu/menu-radio-hov.png)
+ skin/modern/global/menu/menu-radio.png (global/menu/menu-radio.png)
+ skin/modern/global/menulist/mlist-act-arrow.png (global/menulist/mlist-act-arrow.png)
+ skin/modern/global/menulist/mlist-arrow.png (global/menulist/mlist-arrow.png)
+ skin/modern/global/menulist/mlist-compact-arrow.png (global/menulist/mlist-compact-arrow.png)
+ skin/modern/global/menulist/mlist-dis-arrow.png (global/menulist/mlist-dis-arrow.png)
+ skin/modern/global/progressmeter/progress-busy.png (global/progressmeter/progress-busy.png)
+ skin/modern/global/radio/radio-act-check.png (global/radio/radio-act-check.png)
+ skin/modern/global/radio/radio-act.png (global/radio/radio-act.png)
+ skin/modern/global/radio/radio-check.png (global/radio/radio-check.png)
+ skin/modern/global/radio/radio-dis-check.png (global/radio/radio-dis-check.png)
+ skin/modern/global/radio/radio-dis.png (global/radio/radio-dis.png)
+ skin/modern/global/radio/radio.png (global/radio/radio.png)
+ skin/modern/global/scrollbar/thumb-vrt-grip.png (global/scrollbar/thumb-vrt-grip.png)
+ skin/modern/global/scrollbar/btn-lft.png (global/scrollbar/btn-lft.png)
+ skin/modern/global/scrollbar/btn-rit.png (global/scrollbar/btn-rit.png)
+ skin/modern/global/scrollbar/btn-up.png (global/scrollbar/btn-up.png)
+ skin/modern/global/scrollbar/mini-btn-dn.png (global/scrollbar/mini-btn-dn.png)
+ skin/modern/global/scrollbar/mini-btn-lft.png (global/scrollbar/mini-btn-lft.png)
+ skin/modern/global/scrollbar/mini-btn-rit.png (global/scrollbar/mini-btn-rit.png)
+ skin/modern/global/scrollbar/mini-btn-up.png (global/scrollbar/mini-btn-up.png)
+ skin/modern/global/scrollbar/mini-slider-hrz.png (global/scrollbar/mini-slider-hrz.png)
+ skin/modern/global/scrollbar/mini-slider-vrt.png (global/scrollbar/mini-slider-vrt.png)
+ skin/modern/global/scrollbar/mini-thumb-hrz-grip.png (global/scrollbar/mini-thumb-hrz-grip.png)
+ skin/modern/global/scrollbar/mini-thumb-vrt-grip.png (global/scrollbar/mini-thumb-vrt-grip.png)
+ skin/modern/global/scrollbar/slider-hrz.png (global/scrollbar/slider-hrz.png)
+ skin/modern/global/scrollbar/slider-vrt.png (global/scrollbar/slider-vrt.png)
+ skin/modern/global/scrollbar/thumb-hrz-grip.png (global/scrollbar/thumb-hrz-grip.png)
+ skin/modern/global/scrollbar/btn-dn.png (global/scrollbar/btn-dn.png)
+ skin/modern/global/splitter/grip-vrt-after.png (global/splitter/grip-vrt-after.png)
+ skin/modern/global/splitter/grip-vrt-before.png (global/splitter/grip-vrt-before.png)
+ skin/modern/global/splitter/grip-vrt-after-act.png (global/splitter/grip-vrt-after-act.png)
+ skin/modern/global/splitter/grip-vrt-before-act.png (global/splitter/grip-vrt-before-act.png)
+ skin/modern/global/splitter/grip-hrz-after.png (global/splitter/grip-hrz-after.png)
+ skin/modern/global/splitter/grip-hrz-before.png (global/splitter/grip-hrz-before.png)
+ skin/modern/global/splitter/grip-hrz-after-act.png (global/splitter/grip-hrz-after-act.png)
+ skin/modern/global/splitter/grip-hrz-before-act.png (global/splitter/grip-hrz-before-act.png)
+ skin/modern/global/toolbar/tbgrip-arrow.png (global/toolbar/tbgrip-arrow.png)
+ skin/modern/global/toolbar/mbgrip-arrow.png (global/toolbar/mbgrip-arrow.png)
+ skin/modern/global/toolbar/tbgrip-arrow-act.png (global/toolbar/tbgrip-arrow-act.png)
+ skin/modern/global/toolbar/tbgrip-arrow-clps.png (global/toolbar/tbgrip-arrow-clps.png)
+ skin/modern/global/toolbar/tbgrip-arrow-clps-act.png (global/toolbar/tbgrip-arrow-clps-act.png)
+ skin/modern/global/toolbar/tb-mid.png (global/toolbar/tb-mid.png)
+ skin/modern/global/toolbar/chevron.png (global/toolbar/chevron.png)
+ skin/modern/global/toolbar/spring.png (global/toolbar/spring.png)
+ skin/modern/global/tree/checkbox.png (global/tree/checkbox.png)
+ skin/modern/global/tree/checkbox-checked.png (global/tree/checkbox-checked.png)
+ skin/modern/global/mac/tree/sort-asc.png (global/tree/sort-dsc.png)
+ skin/modern/global/mac/tree/sort-dsc.png (global/tree/sort-asc.png)
+ skin/modern/global/gtk/tree/sort-asc.png (global/tree/sort-asc.png)
+ skin/modern/global/gtk/tree/sort-dsc.png (global/tree/sort-dsc.png)
+ skin/modern/global/win/tree/sort-asc.png (global/tree/sort-dsc.png)
+ skin/modern/global/win/tree/sort-dsc.png (global/tree/sort-asc.png)
+ skin/modern/global/tree/twisty-clsd.png (global/tree/twisty-clsd.png)
+ skin/modern/global/tree/twisty-open.png (global/tree/twisty-open.png)
+ skin/modern/global/tree/columnpicker.png (global/tree/columnpicker.png)
+ skin/modern/messenger/threadPaneLabels.css (messenger/threadPaneLabels.css)
+ skin/modern/messenger/accountCentral.css (messenger/accountCentral.css)
+ skin/modern/messenger/accountManage.css (messenger/accountManage.css)
+ skin/modern/messenger/accountWizard.css (messenger/accountWizard.css)
+ skin/modern/messenger/addressingWidget.css (messenger/addressingWidget.css)
+ skin/modern/messenger/browserRequest.css (messenger/browserRequest.css)
+ skin/modern/messenger/dialogs.css (messenger/dialogs.css)
+ skin/modern/messenger/filterDialog.css (messenger/filterDialog.css)
+ skin/modern/messenger/folderMenus.css (messenger/folderMenus.css)
+ skin/modern/messenger/folderPane.css (messenger/folderPane.css)
+ skin/modern/messenger/folderPaneExtras.css (messenger/folderPaneExtras.css)
+ skin/modern/messenger/threadPaneExtras.css (messenger/threadPaneExtras.css)
+ skin/modern/messenger/messageBody.css (messenger/messageBody.css)
+ skin/modern/messenger/msgSelectOffline.css (messenger/msgSelectOffline.css)
+ skin/modern/messenger/messageHeader.css (messenger/messageHeader.css)
+ skin/modern/messenger/messageQuotes.css (messenger/messageQuotes.css)
+ skin/modern/messenger/messageKeywords.css (messenger/messageKeywords.css)
+ skin/modern/messenger/messageWindow.css (messenger/messageWindow.css)
+ skin/modern/messenger/messenger.css (messenger/messenger.css)
+ skin/modern/messenger/newmailalert.css (messenger/newmailalert.css)
+ skin/modern/messenger/prefPanels.css (messenger/prefPanels.css)
+ skin/modern/messenger/searchDialog.css (messenger/searchDialog.css)
+ skin/modern/messenger/subscribe.css (messenger/subscribe.css)
+ skin/modern/messenger/mailWindow1.css (messenger/mailWindow1.css)
+ skin/modern/messenger/threadPane.css (messenger/threadPane.css)
+ skin/modern/messenger/primaryToolbar.css (messenger/primaryToolbar.css)
+ skin/modern/messenger/start.css (messenger/start.css)
+ skin/modern/messenger/addressbook/abResultsPane.css (messenger/addressbook/abResultsPane.css)
+ skin/modern/messenger/addressbook/addressPanes.css (messenger/addressbook/addressPanes.css)
+ skin/modern/messenger/addressbook/cardDialog.css (messenger/addressbook/cardDialog.css)
+ skin/modern/messenger/addressbook/selectAddressesDialog.css (messenger/addressbook/selectAddressesDialog.css)
+ skin/modern/messenger/addressbook/addressbook.css (messenger/addressbook/addressbook.css)
+ skin/modern/messenger/addressbook/sidebarPanel.css (messenger/addressbook/sidebarPanel.css)
+ skin/modern/messenger/addressbook/icons/directory-down.png (messenger/addressbook/icons/directory-down.png)
+ skin/modern/messenger/addressbook/icons/directory.png (messenger/addressbook/icons/directory.png)
+ skin/modern/messenger/addressbook/icons/secure-directory.png (messenger/addressbook/icons/secure-directory.png)
+ skin/modern/messenger/addressbook/icons/list.png (messenger/addressbook/icons/list.png)
+ skin/modern/messenger/addressbook/icons/mast-ab.png (messenger/addressbook/icons/mast-ab.png)
+ skin/modern/messenger/addressbook/icons/myaddrbk.png (messenger/addressbook/icons/myaddrbk.png)
+ skin/modern/messenger/addressbook/icons/person.png (messenger/addressbook/icons/person.png)
+ skin/modern/messenger/addressbook/icons/contact-generic.png (messenger/addressbook/icons/contact-generic.png)
+ skin/modern/messenger/addressbook/icons/contact-generic-tiny.png (messenger/addressbook/icons/contact-generic-tiny.png)
+ skin/modern/messenger/icons/acct-compose.png (messenger/icons/acct-compose.png)
+ skin/modern/messenger/icons/acct-filters.png (messenger/icons/acct-filters.png)
+ skin/modern/messenger/icons/acct-newaccount.png (messenger/icons/acct-newaccount.png)
+ skin/modern/messenger/icons/acct-prefs.png (messenger/icons/acct-prefs.png)
+ skin/modern/messenger/icons/acct-read.png (messenger/icons/acct-read.png)
+ skin/modern/messenger/icons/acct-search.png (messenger/icons/acct-search.png)
+ skin/modern/messenger/icons/acct-subscribe.png (messenger/icons/acct-subscribe.png)
+ skin/modern/messenger/icons/attach.png (messenger/icons/attach.png)
+ skin/modern/messenger/icons/attachment.png (messenger/icons/attachment.png)
+ skin/modern/messenger/icons/attachment-col.png (messenger/icons/attachment-col.png)
+ skin/modern/messenger/icons/attachment-selected.png (messenger/icons/attachment-selected.png)
+ skin/modern/messenger/icons/check.png (messenger/icons/check.png)
+ skin/modern/messenger/icons/dot.png (messenger/icons/dot.png)
+ skin/modern/messenger/icons/flagcol-flagged.png (messenger/icons/flagcol-flagged.png)
+ skin/modern/messenger/icons/folder-closed.png (messenger/icons/folder-closed.png)
+ skin/modern/messenger/icons/folder-draft-open.png (messenger/icons/folder-draft-open.png)
+ skin/modern/messenger/icons/folder-draft-share-open.png (messenger/icons/folder-draft-share-open.png)
+ skin/modern/messenger/icons/folder-draft-share.png (messenger/icons/folder-draft-share.png)
+ skin/modern/messenger/icons/folder-draft.png (messenger/icons/folder-draft.png)
+ skin/modern/messenger/icons/folder-inbox-new.png (messenger/icons/folder-inbox-new.png)
+ skin/modern/messenger/icons/folder-inbox-open.png (messenger/icons/folder-inbox-open.png)
+ skin/modern/messenger/icons/folder-inbox-share-open.png (messenger/icons/folder-inbox-share-open.png)
+ skin/modern/messenger/icons/folder-inbox-share.png (messenger/icons/folder-inbox-share.png)
+ skin/modern/messenger/icons/folder-inbox.png (messenger/icons/folder-inbox.png)
+ skin/modern/messenger/icons/folder-new-open.png (messenger/icons/folder-new-open.png)
+ skin/modern/messenger/icons/folder-new.png (messenger/icons/folder-new.png)
+ skin/modern/messenger/icons/folder-newsgroup-new.png (messenger/icons/folder-newsgroup-new.png)
+ skin/modern/messenger/icons/folder-newsgroup.png (messenger/icons/folder-newsgroup.png)
+ skin/modern/messenger/icons/folder-open.png (messenger/icons/folder-open.png)
+ skin/modern/messenger/icons/folder-outbox-open.png (messenger/icons/folder-outbox-open.png)
+ skin/modern/messenger/icons/folder-outbox.png (messenger/icons/folder-outbox.png)
+ skin/modern/messenger/icons/folder-sent-open.png (messenger/icons/folder-sent-open.png)
+ skin/modern/messenger/icons/folder-sent-share-open.png (messenger/icons/folder-sent-share-open.png)
+ skin/modern/messenger/icons/folder-sent-share.png (messenger/icons/folder-sent-share.png)
+ skin/modern/messenger/icons/folder-sent.png (messenger/icons/folder-sent.png)
+ skin/modern/messenger/icons/folder-share-open.png (messenger/icons/folder-share-open.png)
+ skin/modern/messenger/icons/folder-share.png (messenger/icons/folder-share.png)
+ skin/modern/messenger/icons/folder-junk-open.png (messenger/icons/folder-junk-open.png)
+ skin/modern/messenger/icons/folder-junk.png (messenger/icons/folder-junk.png)
+ skin/modern/messenger/icons/folder-template-open.png (messenger/icons/folder-template-open.png)
+ skin/modern/messenger/icons/folder-template-share-open.png (messenger/icons/folder-template-share-open.png)
+ skin/modern/messenger/icons/folder-template-share.png (messenger/icons/folder-template-share.png)
+ skin/modern/messenger/icons/folder-template.png (messenger/icons/folder-template.png)
+ skin/modern/messenger/icons/folder-trash-open.png (messenger/icons/folder-trash-open.png)
+ skin/modern/messenger/icons/folder-trash-share-open.png (messenger/icons/folder-trash-share-open.png)
+ skin/modern/messenger/icons/folder-trash-share.png (messenger/icons/folder-trash-share.png)
+ skin/modern/messenger/icons/folder-trash.png (messenger/icons/folder-trash.png)
+ skin/modern/messenger/icons/folder-search.png (messenger/icons/folder-search.png)
+ skin/modern/messenger/icons/info.png (messenger/icons/info.png)
+ skin/modern/messenger/icons/insecure.png (messenger/icons/insecure.png)
+ skin/modern/messenger/icons/junk.png (messenger/icons/junk.png)
+ skin/modern/messenger/icons/loading.png (messenger/icons/loading.png)
+ skin/modern/messenger/icons/local-mailhost.png (messenger/icons/local-mailhost.png)
+ skin/modern/messenger/icons/mast-mail.png (messenger/icons/mast-mail.png)
+ skin/modern/messenger/icons/message-mail-attach-del.png (messenger/icons/message-mail-attach-del.png)
+ skin/modern/messenger/icons/message-mail-attach-fwd-offl-reply.png (messenger/icons/message-mail-attach-fwd-offl-reply.png)
+ skin/modern/messenger/icons/message-mail-attach-fwd-offl.png (messenger/icons/message-mail-attach-fwd-offl.png)
+ skin/modern/messenger/icons/message-mail-attach-fwd-reply.png (messenger/icons/message-mail-attach-fwd-reply.png)
+ skin/modern/messenger/icons/message-mail-attach-fwd.png (messenger/icons/message-mail-attach-fwd.png)
+ skin/modern/messenger/icons/message-mail-attach-offl-reply.png (messenger/icons/message-mail-attach-offl-reply.png)
+ skin/modern/messenger/icons/message-mail-attach-offl.png (messenger/icons/message-mail-attach-offl.png)
+ skin/modern/messenger/icons/message-mail-attach-reply.png (messenger/icons/message-mail-attach-reply.png)
+ skin/modern/messenger/icons/message-mail-attach.png (messenger/icons/message-mail-attach.png)
+ skin/modern/messenger/icons/message-mail-delete-offl.png (messenger/icons/message-mail-delete-offl.png)
+ skin/modern/messenger/icons/message-mail-fwd-offl-reply.png (messenger/icons/message-mail-fwd-offl-reply.png)
+ skin/modern/messenger/icons/message-mail-fwd-offl.png (messenger/icons/message-mail-fwd-offl.png)
+ skin/modern/messenger/icons/message-mail-fwd-reply.png (messenger/icons/message-mail-fwd-reply.png)
+ skin/modern/messenger/icons/message-mail-fwd.png (messenger/icons/message-mail-fwd.png)
+ skin/modern/messenger/icons/message-mail-imapdelete.png (messenger/icons/message-mail-imapdelete.png)
+ skin/modern/messenger/icons/message-mail-new-offl.png (messenger/icons/message-mail-new-offl.png)
+ skin/modern/messenger/icons/message-mail-new.png (messenger/icons/message-mail-new.png)
+ skin/modern/messenger/icons/message-mail-offl-reply.png (messenger/icons/message-mail-offl-reply.png)
+ skin/modern/messenger/icons/message-mail-offl.png (messenger/icons/message-mail-offl.png)
+ skin/modern/messenger/icons/message-mail-reply.png (messenger/icons/message-mail-reply.png)
+ skin/modern/messenger/icons/message-mail.png (messenger/icons/message-mail.png)
+ skin/modern/messenger/icons/message-news.png (messenger/icons/message-news.png)
+ skin/modern/messenger/icons/message-news-attach-kill-offl.png (messenger/icons/message-news-attach-kill-offl.png)
+ skin/modern/messenger/icons/message-news-attach-kill.png (messenger/icons/message-news-attach-kill.png)
+ skin/modern/messenger/icons/message-news-attach.png (messenger/icons/message-news-attach.png)
+ skin/modern/messenger/icons/message-news-attach-offl.png (messenger/icons/message-news-attach-offl.png)
+ skin/modern/messenger/icons/message-news-kill-offl.png (messenger/icons/message-news-kill-offl.png)
+ skin/modern/messenger/icons/message-news-kill.png (messenger/icons/message-news-kill.png)
+ skin/modern/messenger/icons/message-news-new.png (messenger/icons/message-news-new.png)
+ skin/modern/messenger/icons/message-news-new-attach.png (messenger/icons/message-news-new-attach.png)
+ skin/modern/messenger/icons/message-news-new-attach-off.png (messenger/icons/message-news-new-attach-off.png)
+ skin/modern/messenger/icons/message-news-new-offl.png (messenger/icons/message-news-new-offl.png)
+ skin/modern/messenger/icons/message-news-offl.png (messenger/icons/message-news-offl.png)
+ skin/modern/messenger/icons/phishing.png (messenger/icons/phishing.png)
+ skin/modern/messenger/icons/readcol-read.png (messenger/icons/readcol-read.png)
+ skin/modern/messenger/icons/readcol-unread.png (messenger/icons/readcol-unread.png)
+ skin/modern/messenger/icons/remote-blocked.png (messenger/icons/remote-blocked.png)
+ skin/modern/messenger/icons/secure.png (messenger/icons/secure.png)
+ skin/modern/messenger/icons/server-local-new.png (messenger/icons/server-local-new.png)
+ skin/modern/messenger/icons/server-local.png (messenger/icons/server-local.png)
+ skin/modern/messenger/icons/server-mail-new.png (messenger/icons/server-mail-new.png)
+ skin/modern/messenger/icons/server-mail.png (messenger/icons/server-mail.png)
+ skin/modern/messenger/icons/server-news-lock.png (messenger/icons/server-news-lock.png)
+ skin/modern/messenger/icons/server-news-new.png (messenger/icons/server-news-new.png)
+ skin/modern/messenger/icons/server-news.png (messenger/icons/server-news.png)
+ skin/modern/messenger/icons/server-remote-lock.png (messenger/icons/server-remote-lock.png)
+ skin/modern/messenger/icons/server-remote-lock-new.png (messenger/icons/server-remote-lock-new.png)
+ skin/modern/messenger/icons/thread-closed.png (messenger/icons/thread-closed.png)
+ skin/modern/messenger/icons/thread-new-closed.png (messenger/icons/thread-new-closed.png)
+ skin/modern/messenger/icons/threadcol-threaded.png (messenger/icons/threadcol-threaded.png)
+ skin/modern/messenger/icons/threadcol-unthreaded.png (messenger/icons/threadcol-unthreaded.png)
+ skin/modern/messenger/icons/thread-closed-eye.png (messenger/icons/thread-closed-eye.png)
+ skin/modern/messenger/icons/thread-closed-kill.png (messenger/icons/thread-closed-kill.png)
+ skin/modern/messenger/icons/thread-closed-offl-eye.png (messenger/icons/thread-closed-offl-eye.png)
+ skin/modern/messenger/icons/thread-closed-offl-kill.png (messenger/icons/thread-closed-offl-kill.png)
+ skin/modern/messenger/icons/thread-new-closed-eye.png (messenger/icons/thread-new-closed-eye.png)
+ skin/modern/messenger/icons/thread-new-closed-kill.png (messenger/icons/thread-new-closed-kill.png)
+ skin/modern/messenger/icons/thread-new-closed-offl-eye.png (messenger/icons/thread-new-closed-offl-eye.png)
+ skin/modern/messenger/icons/thread-new-closed-offl-kill.png (messenger/icons/thread-new-closed-offl-kill.png)
+ skin/modern/messenger/icons/new-mail-alert.png (messenger/icons/new-mail-alert.png)
+ skin/modern/messenger/icons/btn1.png (messenger/icons/btn1.png)
+ skin/modern/messenger/icons/junkBar.png (messenger/icons/junkBar.png)
+ skin/modern/messenger/icons/message-junk-other.png (messenger/icons/message-junk-other.png)
+ skin/modern/messenger/messengercompose/messengercompose.css (messenger/messengercompose/messengercompose.css)
+ skin/modern/messenger/messengercompose/icons/mast-msgcomp.png (messenger/messengercompose/icons/mast-msgcomp.png)
+ skin/modern/messenger/smime/msgReadSecurityInfo.css (messenger/smime/msgReadSecurityInfo.css)
+ skin/modern/messenger/smime/msgCompSecurityInfo.css (messenger/smime/msgCompSecurityInfo.css)
+ skin/modern/messenger/smime/msgReadSMIMEOverlay.css (messenger/smime/msgReadSMIMEOverlay.css)
+ skin/modern/messenger/smime/msgCompSMIMEOverlay.css (messenger/smime/msgCompSMIMEOverlay.css)
+ skin/modern/messenger/smime/certFetchingStatus.css (messenger/smime/certFetchingStatus.css)
+ skin/modern/messenger/smime/msgHdrViewSMIMEOverlay.css (messenger/smime/msgHdrViewSMIMEOverlay.css)
+ skin/modern/messenger/smime/icons/smbtn1.png (messenger/smime/icons/smbtn1.png)
+ skin/modern/messenger/smime/icons/sbSignOk.png (messenger/smime/icons/sbSignOk.png)
+ skin/modern/messenger/smime/icons/sbSignUnknown.png (messenger/smime/icons/sbSignUnknown.png)
+ skin/modern/messenger/smime/icons/sbSignNotOk.png (messenger/smime/icons/sbSignNotOk.png)
+ skin/modern/messenger/smime/icons/sbCryptoOk.png (messenger/smime/icons/sbCryptoOk.png)
+ skin/modern/messenger/smime/icons/sbCryptoNotOk.png (messenger/smime/icons/sbCryptoNotOk.png)
+ skin/modern/messenger/smime/icons/hdrSignOk.png (messenger/smime/icons/hdrSignOk.png)
+ skin/modern/messenger/smime/icons/hdrSignUnknown.png (messenger/smime/icons/hdrSignUnknown.png)
+ skin/modern/messenger/smime/icons/hdrSignNotOk.png (messenger/smime/icons/hdrSignNotOk.png)
+ skin/modern/messenger/smime/icons/hdrCryptoOk.png (messenger/smime/icons/hdrCryptoOk.png)
+ skin/modern/messenger/smime/icons/hdrCryptoNotOk.png (messenger/smime/icons/hdrCryptoNotOk.png)
+ skin/modern/messenger-newsblog/feed-subscriptions.css (messenger/newsblog/feed-subscriptions.css)
+ skin/modern/messenger-newsblog/icons/rss-feed.png (messenger/newsblog/rss-feed.png)
+ skin/modern/mozapps/aboutNetworking.css (mozapps/aboutNetworking.css)
+ skin/modern/mozapps/downloads/downloadIcon.png (mozapps/downloads/downloadIcon.png)
+ skin/modern/mozapps/downloads/downloads.css (mozapps/downloads/downloads.css)
+ skin/modern/mozapps/downloads/unknownContentType.css (mozapps/downloads/unknownContentType.css)
+ skin/modern/mozapps/extensions/about.css (mozapps/extensions/about.css)
+ skin/modern/mozapps/extensions/alerticon-error.png (mozapps/extensions/alerticon-error.png)
+ skin/modern/mozapps/extensions/alerticon-info-negative.png (mozapps/extensions/alerticon-info-negative.png)
+ skin/modern/mozapps/extensions/alerticon-info-positive.png (mozapps/extensions/alerticon-info-positive.png)
+ skin/modern/mozapps/extensions/alerticon-warning.png (mozapps/extensions/alerticon-warning.png)
+ skin/modern/mozapps/extensions/blocklist.css (mozapps/extensions/blocklist.css)
+ skin/modern/mozapps/extensions/cancel.png (mozapps/extensions/cancel.png)
+ skin/modern/mozapps/extensions/category-available.png (mozapps/extensions/category-available.png)
+ skin/modern/mozapps/extensions/category-dictionaries.png (mozapps/extensions/category-dictionaries.png)
+ skin/modern/mozapps/extensions/category-discover.png (mozapps/extensions/category-discover.png)
+ skin/modern/mozapps/extensions/category-extensions.png (mozapps/extensions/category-extensions.png)
+ skin/modern/mozapps/extensions/category-languages.png (mozapps/extensions/category-languages.png)
+ skin/modern/mozapps/extensions/category-plugins.png (mozapps/extensions/category-plugins.png)
+ skin/modern/mozapps/extensions/category-recent.png (mozapps/extensions/category-recent.png)
+ skin/modern/mozapps/extensions/category-searchengines.png (mozapps/extensions/category-searchengines.png)
+ skin/modern/mozapps/extensions/category-search.png (mozapps/extensions/category-search.png)
+ skin/modern/mozapps/extensions/category-themes.png (mozapps/extensions/category-themes.png)
+ skin/modern/mozapps/extensions/discover-logo.png (mozapps/extensions/discover-logo.png)
+ skin/modern/mozapps/extensions/dictionaryGeneric.png (mozapps/extensions/dictionaryGeneric.png)
+ skin/modern/mozapps/extensions/dictionaryGeneric-16.png (mozapps/extensions/dictionaryGeneric-16.png)
+ skin/modern/mozapps/extensions/eula.css (mozapps/extensions/eula.css)
+ skin/modern/mozapps/extensions/extensionGeneric-16.png (mozapps/extensions/extensionGeneric-16.png)
+ skin/modern/mozapps/extensions/extensionGeneric.png (mozapps/extensions/extensionGeneric.png)
+ skin/modern/mozapps/extensions/extensions.css (mozapps/extensions/extensions.css)
+ skin/modern/mozapps/extensions/extensions.svg (mozapps/extensions/extensions.svg)
+ skin/modern/mozapps/extensions/heart.png (mozapps/extensions/heart.png)
+ skin/modern/mozapps/extensions/localeGeneric.png (mozapps/extensions/localeGeneric.png)
+ skin/modern/mozapps/extensions/navigation.png (mozapps/extensions/navigation.png)
+ skin/modern/mozapps/extensions/newaddon.css (mozapps/extensions/newaddon.css)
+ skin/modern/mozapps/extensions/rating-not-won.png (mozapps/extensions/rating-not-won.png)
+ skin/modern/mozapps/extensions/rating-won.png (mozapps/extensions/rating-won.png)
+ skin/modern/mozapps/extensions/stripes-error.png (mozapps/extensions/stripes-error.png)
+ skin/modern/mozapps/extensions/stripes-info-negative.png (mozapps/extensions/stripes-info-negative.png)
+ skin/modern/mozapps/extensions/stripes-info-positive.png (mozapps/extensions/stripes-info-positive.png)
+ skin/modern/mozapps/extensions/stripes-warning.png (mozapps/extensions/stripes-warning.png)
+ skin/modern/mozapps/extensions/themeGeneric-16.png (mozapps/extensions/themeGeneric-16.png)
+ skin/modern/mozapps/extensions/themeGeneric.png (mozapps/extensions/themeGeneric.png)
+ skin/modern/mozapps/extensions/update.css (mozapps/extensions/update.css)
+ skin/modern/mozapps/extensions/utilities.png (mozapps/extensions/utilities.png)
+ skin/modern/mozapps/handling/handling.css (mozapps/handling/handling.css)
+ skin/modern/mozapps/icons/itemDisabledFader.png (mozapps/icons/itemDisabledFader.png)
+ skin/modern/mozapps/icons/itemEnabledFader.png (mozapps/icons/itemEnabledFader.png)
+ skin/modern/mozapps/passwordmgr/key-16.png (mozapps/passwordmgr/key-16.png)
+ skin/modern/mozapps/passwordmgr/key-64.png (mozapps/passwordmgr/key-64.png)
+ skin/modern/mozapps/passwordmgr/key.png (mozapps/passwordmgr/key.png)
+ skin/modern/mozapps/plugins/pluginBlocked.png (mozapps/plugins/pluginBlocked.png)
+ skin/modern/mozapps/plugins/pluginBlocked-16.png (mozapps/plugins/pluginBlocked.png)
+ skin/modern/mozapps/plugins/pluginGeneric.png (mozapps/plugins/pluginGeneric.png)
+ skin/modern/mozapps/plugins/pluginGeneric-16.png (mozapps/plugins/pluginGeneric-16.png)
+ skin/modern/mozapps/plugins/pluginInstallerWizard.css (mozapps/plugins/pluginInstallerWizard.css)
+ skin/modern/mozapps/update/update.png (mozapps/update/update.png)
+ skin/modern/mozapps/update/updates.css (mozapps/update/updates.css)
+ skin/modern/mozapps/viewsource/viewsource.css (mozapps/viewsource/viewsource.css)
+ skin/modern/mozapps/xpinstall/xpinstallConfirm.css (mozapps/xpinstall/xpinstallConfirm.css)
+ skin/modern/mozapps/xpinstall/xpinstallItemGeneric.png (mozapps/extensions/extensionGeneric.png)
+ skin/modern/navigator/linkToolbar.css (navigator/linkToolbar.css)
+ skin/modern/navigator/navigator.css (navigator/navigator.css)
+ skin/modern/navigator/pageInfo.css (navigator/pageInfo.css)
+ skin/modern/navigator/tabbrowser.css (navigator/tabbrowser.css)
+ skin/modern/navigator/webDeveloper.css (navigator/webDeveloper.css)
+ skin/modern/navigator/btn1/feeds.png (navigator/btn1/feeds.png)
+ skin/modern/navigator/btn1/first-dis.png (navigator/btn1/first-dis.png)
+ skin/modern/navigator/btn1/first-hov.png (navigator/btn1/first-hov.png)
+ skin/modern/navigator/btn1/first.png (navigator/btn1/first.png)
+ skin/modern/navigator/btn1/last-dis.png (navigator/btn1/last-dis.png)
+ skin/modern/navigator/btn1/last-hov.png (navigator/btn1/last-hov.png)
+ skin/modern/navigator/btn1/last.png (navigator/btn1/last.png)
+ skin/modern/navigator/btn1/next-dis.png (navigator/btn1/next-dis.png)
+ skin/modern/navigator/btn1/next-hov.png (navigator/btn1/next-hov.png)
+ skin/modern/navigator/btn1/next.png (navigator/btn1/next.png)
+ skin/modern/navigator/btn1/previous-dis.png (navigator/btn1/previous-dis.png)
+ skin/modern/navigator/btn1/previous-hov.png (navigator/btn1/previous-hov.png)
+ skin/modern/navigator/btn1/previous.png (navigator/btn1/previous.png)
+ skin/modern/navigator/btn1/top-dis.png (navigator/btn1/top-dis.png)
+ skin/modern/navigator/btn1/top-hov.png (navigator/btn1/top-hov.png)
+ skin/modern/navigator/btn1/top.png (navigator/btn1/top.png)
+ skin/modern/navigator/btn1/up-dis.png (navigator/btn1/up-dis.png)
+ skin/modern/navigator/btn1/up-hov.png (navigator/btn1/up-hov.png)
+ skin/modern/navigator/btn1/up.png (navigator/btn1/up.png)
+ skin/modern/navigator/toolbar/ubhist-arrow-act.png (navigator/toolbar/ubhist-arrow-act.png)
+ skin/modern/navigator/toolbar/ubhist-arrow.png (navigator/toolbar/ubhist-arrow.png)
+ skin/modern/navigator/icons/browser.png (navigator/icons/browser.png)
+ skin/modern/navigator/icons/browser-small.png (navigator/icons/browser-small.png)
+ skin/modern/navigator/icons/identity.png (navigator/icons/identity.png)
+ skin/modern/navigator/icons/popup-blocked.png (navigator/icons/popup-blocked.png)
+ skin/modern/navigator/icons/tab-drag-indicator.png (navigator/icons/tab-drag-indicator.png)
+ skin/modern/navigator/icons/tab-new.png (navigator/icons/tab-new.png)
+ skin/modern/navigator/icons/tab-new-hov.png (navigator/icons/tab-new-hov.png)
+ skin/modern/navigator/icons/tab-new-act.png (navigator/icons/tab-new-act.png)
+ skin/modern/navigator/icons/windowcontrols.png (navigator/icons/windowcontrols.png)
+
+# Keep global gif icons for compatibility with toolkit.
+ skin/modern/global/arrow/arrow-dn-dis.gif (global/arrow/arrow-dn-dis.gif)
+ skin/modern/global/arrow/arrow-dn.gif (global/arrow/arrow-dn.gif)
+ skin/modern/global/arrow/arrow-lft-dis.gif (global/arrow/arrow-lft-dis.gif)
+ skin/modern/global/arrow/arrow-lft.gif (global/arrow/arrow-lft.gif)
+ skin/modern/global/arrow/arrow-lft-sharp.gif (global/arrow/arrow-lft-sharp.gif)
+ skin/modern/global/arrow/arrow-lft-sharp-end.gif (global/arrow/arrow-lft-sharp-end.gif)
+ skin/modern/global/arrow/arrow-rit-dis.gif (global/arrow/arrow-rit-dis.gif)
+ skin/modern/global/arrow/arrow-rit.gif (global/arrow/arrow-rit.gif)
+ skin/modern/global/arrow/arrow-rit-sharp.gif (global/arrow/arrow-rit-sharp.gif)
+ skin/modern/global/arrow/arrow-rit-sharp-end.gif (global/arrow/arrow-rit-sharp-end.gif)
+ skin/modern/global/arrow/arrow-up-dis.gif (global/arrow/arrow-up-dis.gif)
+ skin/modern/global/arrow/arrow-up.gif (global/arrow/arrow-up.gif)
+ skin/modern/global/button/tbmbtn-arrow-act.gif (global/button/tbmbtn-arrow-act.gif)
+ skin/modern/global/button/tbmbtn-arrow-hov.gif (global/button/tbmbtn-arrow-hov.gif)
+ skin/modern/global/button/tbmbtn-arrow.gif (global/button/tbmbtn-arrow.gif)
+ skin/modern/global/button/tbmenu-arrow-act.gif (global/button/tbmenu-arrow-act.gif)
+ skin/modern/global/button/tbmenu-arrow-dis.gif (global/button/tbmenu-arrow-dis.gif)
+ skin/modern/global/button/tbmenu-arrow.gif (global/button/tbmenu-arrow.gif)
+ skin/modern/global/checkbox/cbox-act-check.gif (global/checkbox/cbox-act-check.gif)
+ skin/modern/global/checkbox/cbox-act.gif (global/checkbox/cbox-act.gif)
+ skin/modern/global/checkbox/cbox-check.gif (global/checkbox/cbox-check.gif)
+ skin/modern/global/checkbox/cbox-dis-check.gif (global/checkbox/cbox-dis-check.gif)
+ skin/modern/global/checkbox/cbox-dis.gif (global/checkbox/cbox-dis.gif)
+ skin/modern/global/checkbox/cbox.gif (global/checkbox/cbox.gif)
+ skin/modern/global/filepicker/blank.gif (global/filepicker/blank.gif)
+ skin/modern/global/filepicker/dir-closed.gif (global/filepicker/dir-closed.gif)
+ skin/modern/global/filepicker/dir-open.gif (global/filepicker/dir-open.gif)
+ skin/modern/global/filepicker/folder-up.gif (global/filepicker/folder-up.gif)
+ skin/modern/global/filepicker/folder-home.gif (global/filepicker/folder-home.gif)
+ skin/modern/global/filepicker/folder-new.gif (global/filepicker/folder-new.gif)
+ skin/modern/global/icons/close-act.gif (global/icons/close-act.gif)
+ skin/modern/global/icons/close-dis.gif (global/icons/close-dis.gif)
+ skin/modern/global/icons/close-hov.gif (global/icons/close-hov.gif)
+ skin/modern/global/icons/close.gif (global/icons/close.gif)
+ skin/modern/global/icons/closebox.gif (global/icons/closebox.gif)
+ skin/modern/global/icons/pg-landscape.gif (global/icons/pg-landscape.gif)
+ skin/modern/global/icons/pg-landscape-small.gif (global/icons/pg-landscape-small.gif)
+ skin/modern/global/icons/pg-portrait.gif (global/icons/pg-portrait.gif)
+ skin/modern/global/icons/pg-portrait-small.gif (global/icons/pg-portrait-small.gif)
+ skin/modern/global/icons/search.gif (global/icons/search.gif)
+ skin/modern/global/menu/menu-arrow-dis.gif (global/menu/menu-arrow-dis.gif)
+ skin/modern/global/menu/menu-arrow-hov.gif (global/menu/menu-arrow-hov.gif)
+ skin/modern/global/menu/menu-arrow.gif (global/menu/menu-arrow.gif)
+ skin/modern/global/menu/menu-check-dis.gif (global/menu/menu-check-dis.gif)
+ skin/modern/global/menu/menu-check-hov.gif (global/menu/menu-check-hov.gif)
+ skin/modern/global/menu/menu-check.gif (global/menu/menu-check.gif)
+ skin/modern/global/menu/menu-radio-dis.gif (global/menu/menu-radio-dis.gif)
+ skin/modern/global/menu/menu-radio-hov.gif (global/menu/menu-radio-hov.gif)
+ skin/modern/global/menu/menu-radio.gif (global/menu/menu-radio.gif)
+ skin/modern/global/menulist/mlist-act-arrow.gif (global/menulist/mlist-act-arrow.gif)
+ skin/modern/global/menulist/mlist-arrow.gif (global/menulist/mlist-arrow.gif)
+ skin/modern/global/menulist/mlist-compact-arrow.gif (global/menulist/mlist-compact-arrow.gif)
+ skin/modern/global/menulist/mlist-dis-arrow.gif (global/menulist/mlist-dis-arrow.gif)
+ skin/modern/global/progressmeter/progress-busy.gif (global/progressmeter/progress-busy.gif)
+ skin/modern/global/radio/radio-act-check.gif (global/radio/radio-act-check.gif)
+ skin/modern/global/radio/radio-act.gif (global/radio/radio-act.gif)
+ skin/modern/global/radio/radio-check.gif (global/radio/radio-check.gif)
+ skin/modern/global/radio/radio-dis-check.gif (global/radio/radio-dis-check.gif)
+ skin/modern/global/radio/radio-dis.gif (global/radio/radio-dis.gif)
+ skin/modern/global/radio/radio.gif (global/radio/radio.gif)
+ skin/modern/global/scrollbar/thumb-vrt-grip.gif (global/scrollbar/thumb-vrt-grip.gif)
+ skin/modern/global/scrollbar/btn-lft.gif (global/scrollbar/btn-lft.gif)
+ skin/modern/global/scrollbar/btn-rit.gif (global/scrollbar/btn-rit.gif)
+ skin/modern/global/scrollbar/btn-up.gif (global/scrollbar/btn-up.gif)
+ skin/modern/global/scrollbar/mini-btn-dn.gif (global/scrollbar/mini-btn-dn.gif)
+ skin/modern/global/scrollbar/mini-btn-lft.gif (global/scrollbar/mini-btn-lft.gif)
+ skin/modern/global/scrollbar/mini-btn-rit.gif (global/scrollbar/mini-btn-rit.gif)
+ skin/modern/global/scrollbar/mini-btn-up.gif (global/scrollbar/mini-btn-up.gif)
+ skin/modern/global/scrollbar/mini-slider-hrz.gif (global/scrollbar/mini-slider-hrz.gif)
+ skin/modern/global/scrollbar/mini-slider-vrt.gif (global/scrollbar/mini-slider-vrt.gif)
+ skin/modern/global/scrollbar/mini-thumb-hrz-grip.gif (global/scrollbar/mini-thumb-hrz-grip.gif)
+ skin/modern/global/scrollbar/mini-thumb-vrt-grip.gif (global/scrollbar/mini-thumb-vrt-grip.gif)
+ skin/modern/global/scrollbar/slider-hrz.gif (global/scrollbar/slider-hrz.gif)
+ skin/modern/global/scrollbar/slider-vrt.gif (global/scrollbar/slider-vrt.gif)
+ skin/modern/global/scrollbar/thumb-hrz-grip.gif (global/scrollbar/thumb-hrz-grip.gif)
+ skin/modern/global/scrollbar/btn-dn.gif (global/scrollbar/btn-dn.gif)
+ skin/modern/global/splitter/grip-vrt-after.gif (global/splitter/grip-vrt-after.gif)
+ skin/modern/global/splitter/grip-vrt-before.gif (global/splitter/grip-vrt-before.gif)
+ skin/modern/global/splitter/grip-vrt-after-act.gif (global/splitter/grip-vrt-after-act.gif)
+ skin/modern/global/splitter/grip-vrt-before-act.gif (global/splitter/grip-vrt-before-act.gif)
+ skin/modern/global/splitter/grip-hrz-after.gif (global/splitter/grip-hrz-after.gif)
+ skin/modern/global/splitter/grip-hrz-before.gif (global/splitter/grip-hrz-before.gif)
+ skin/modern/global/splitter/grip-hrz-after-act.gif (global/splitter/grip-hrz-after-act.gif)
+ skin/modern/global/splitter/grip-hrz-before-act.gif (global/splitter/grip-hrz-before-act.gif)
+ skin/modern/global/toolbar/tbgrip-arrow.gif (global/toolbar/tbgrip-arrow.gif)
+ skin/modern/global/toolbar/mbgrip-arrow.gif (global/toolbar/mbgrip-arrow.gif)
+ skin/modern/global/toolbar/tbgrip-arrow-act.gif (global/toolbar/tbgrip-arrow-act.gif)
+ skin/modern/global/toolbar/tbgrip-arrow-clps.gif (global/toolbar/tbgrip-arrow-clps.gif)
+ skin/modern/global/toolbar/tbgrip-arrow-clps-act.gif (global/toolbar/tbgrip-arrow-clps-act.gif)
+ skin/modern/global/toolbar/tb-mid.gif (global/toolbar/tb-mid.gif)
+ skin/modern/global/toolbar/chevron.gif (global/toolbar/chevron.gif)
+ skin/modern/global/tree/checkbox.gif (global/tree/checkbox.gif)
+ skin/modern/global/tree/checkbox-checked.gif (global/tree/checkbox-checked.gif)
+ skin/modern/global/mac/tree/sort-asc.gif (global/tree/sort-dsc.gif)
+ skin/modern/global/mac/tree/sort-dsc.gif (global/tree/sort-asc.gif)
+ skin/modern/global/gtk/tree/sort-asc.gif (global/tree/sort-asc.gif)
+ skin/modern/global/gtk/tree/sort-dsc.gif (global/tree/sort-dsc.gif)
+ skin/modern/global/win/tree/sort-asc.gif (global/tree/sort-dsc.gif)
+ skin/modern/global/win/tree/sort-dsc.gif (global/tree/sort-asc.gif)
+ skin/modern/global/tree/twisty-clsd.gif (global/tree/twisty-clsd.gif)
+ skin/modern/global/tree/twisty-open.gif (global/tree/twisty-open.gif)
+ skin/modern/global/tree/columnpicker.gif (global/tree/columnpicker.gif)
diff --git a/comm/suite/themes/modern/messenger/accountCentral.css b/comm/suite/themes/modern/messenger/accountCentral.css
new file mode 100644
index 0000000000..8c8f2272fe
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/accountCentral.css
@@ -0,0 +1,117 @@
+/* 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/. */
+
+/* ===== accountCentral.css ==========================================
+ == Styles for the Messenger Account Central panel.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#acctCentralGrid {
+ background-color: #FFFFFF;
+}
+
+spacer {
+ max-height: .5em;
+}
+
+spacer.big {
+ max-height: 4em;
+}
+
+/* ::::: rows ::::: */
+
+#acctCentralHeaderRow {
+ padding: 10px 0 10px 10px;
+ font-size: 180%;
+ font-weight: bold;
+ color: #000000;
+}
+
+.acctCentralRow {
+ -moz-binding: url("chrome://global/skin/globalBindings.xml#row-iconic");
+ margin-inline-start: 10px;
+ font-size: 125%;
+ color: #000000;
+}
+
+.row-iconic-icon {
+ list-style-image: inherit;
+ margin-inline-end: 10px;
+}
+
+.acctCentralRowTitleBox {
+ background-color: #C7D0D9;
+ font-size: 150%;
+ font-weight: bold;
+ color: #000000;
+}
+
+/* ::::: links ::::: */
+
+.acctCentralLinkText {
+ cursor: pointer;
+ color: #212731;
+ text-decoration: underline;
+}
+
+.acctCentralLinkText:hover {
+ color: #39598E;
+}
+
+.acctCentralLinkText:hover:active {
+ color: #000000;
+}
+
+/* ::::: row icons ::::: */
+
+#ReadMessages {
+ list-style-image: url("chrome://messenger/skin/icons/acct-read.png");
+}
+
+#ComposeMessage {
+ list-style-image: url("chrome://messenger/skin/icons/acct-compose.png");
+}
+
+#SubscribeNewsgroups {
+ list-style-image: url("chrome://messenger/skin/icons/acct-subscribe.png");
+}
+
+#SubscribeImapFolders {
+ list-style-image: url("chrome://messenger/skin/icons/acct-subscribe.png");
+}
+
+#SubscribeRSS {
+ list-style-image: url("chrome://messenger/skin/icons/acct-subscribe.png");
+}
+
+#SearchMessages {
+ list-style-image: url("chrome://messenger/skin/icons/acct-search.png");
+}
+
+#AccountSettings {
+ list-style-image: url("chrome://messenger/skin/icons/acct-prefs.png");
+}
+
+#CreateAccount {
+ list-style-image: url("chrome://messenger/skin/icons/acct-newaccount.png");
+}
+
+#CreateFilters {
+ list-style-image: url("chrome://messenger/skin/icons/acct-filters.png");
+}
+
+#OfflineSettings {
+ list-style-image: url("chrome://messenger/skin/icons/acct-prefs.png");
+}
+
+#JunkSettingsMail {
+ list-style-image: url("chrome://messenger/skin/icons/acct-filters.png");
+}
+
+#JunkSettingsNews {
+ list-style-image: url("chrome://messenger/skin/icons/acct-filters.png");
+}
diff --git a/comm/suite/themes/modern/messenger/accountManage.css b/comm/suite/themes/modern/messenger/accountManage.css
new file mode 100644
index 0000000000..8171d9f184
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/accountManage.css
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ===== accountManage.css ==============================================
+ == Styles for the Mail Account Manager.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: account manager :::::: */
+
+.specialFolderPickerGrid {
+ margin-inline-start: 20px;
+}
+
+.fccReplyFollowsParent {
+ margin-inline-start: 20px;
+}
+
+.selectForOfflineUseButton {
+ list-style-image: url("chrome://communicator/skin/icons/offline.png");
+}
+
+.selectForOfflineUseButton > .button-box > .button-icon {
+ margin-inline-start: 4px;
+ margin-inline-end: 4px;
+}
+
+.signatureBox {
+ font-family: -moz-fixed;
+}
+
+listitem[default="true"],
+#identitiesList > listitem:first-child {
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(isDefaultServer-true) {
+ text-decoration: underline;
+}
+
+#accounttree treechildren::-moz-tree-indentation {
+ width: 1ch;
+}
+
+/* ::::: SMTP Server Panel :::::: */
+
+.smtpServerListItem {
+ padding-inline-start: 3px;
+}
+
+#backgroundBox {
+ background-color: #BBC6D1;
+}
+
+#smtpServerInfoBox textbox {
+ background-color: transparent;
+}
diff --git a/comm/suite/themes/modern/messenger/accountWizard.css b/comm/suite/themes/modern/messenger/accountWizard.css
new file mode 100644
index 0000000000..5acf7f1b56
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/accountWizard.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+/* ===== accountWizard.css ==============================================
+ == Styles for the Mail Account Wizard.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: account wizard :::::: */
+
+.awIdentityLabel {
+ width: 8em;
+ margin-inline-start: 5px;
+}
+
+.serverLabel {
+ width: 8em;
+}
+
+.serverDataBox {
+ margin-inline-start: 15px;
+}
diff --git a/comm/suite/themes/modern/messenger/addressbook/abResultsPane.css b/comm/suite/themes/modern/messenger/addressbook/abResultsPane.css
new file mode 100644
index 0000000000..740bcfa0a2
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/abResultsPane.css
@@ -0,0 +1,15 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+treechildren::-moz-tree-image(GeneratedName) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/person.png");
+}
+
+treechildren::-moz-tree-image(GeneratedName, MailList) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/list.png");
+}
+
diff --git a/comm/suite/themes/modern/messenger/addressbook/addressPanes.css b/comm/suite/themes/modern/messenger/addressbook/addressPanes.css
new file mode 100644
index 0000000000..a98952048c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/addressPanes.css
@@ -0,0 +1,36 @@
+/* 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/. */
+
+/* ===== addressPanes.css ================================================
+ == Styles for directory and address panes.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: directory pane icons ::::: */
+
+treechildren::-moz-tree-image(DirCol) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/myaddrbk.png");
+}
+
+treechildren::-moz-tree-image(DirCol, IsMailList-true) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/list.png");
+}
+
+treechildren::-moz-tree-image(DirCol, IsRemote-true) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/directory.png");
+}
+
+treechildren::-moz-tree-image(DirCol, IsRemote-true, IsSecure-true) {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/secure-directory.png");
+}
+
+#dirTree [sortDirection="ascending"] {
+ list-style-image: none;
+}
+
+#dirTree [sortDirection="descending"] {
+ list-style-image: none;
+}
diff --git a/comm/suite/themes/modern/messenger/addressbook/addressbook.css b/comm/suite/themes/modern/messenger/addressbook/addressbook.css
new file mode 100644
index 0000000000..918dcbef69
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/addressbook.css
@@ -0,0 +1,200 @@
+/* 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/. */
+
+/* ===== addressbook.css ================================================
+ == Styles for the main Address Book window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* ::::: primary toolbar masthead ::::: */
+
+#ab-bar2 > .toolbar-holder > .toolbar-primary-icon {
+ background-image: url("chrome://messenger/skin/addressbook/icons/mast-ab.png");
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-newcard {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(476px 49px 509px 0);
+}
+
+#button-newcard:hover {
+ -moz-image-region: rect(476px 99px 509px 50px);
+}
+
+#button-newcard:hover:active {
+ -moz-image-region: rect(476px 149px 509px 100px);
+}
+
+#button-newcard[disabled] {
+ -moz-image-region: rect(476px 199px 509px 150px) !important;
+}
+
+#button-newlist {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(510px 49px 543px 0);
+}
+
+#button-newlist:hover {
+ -moz-image-region: rect(510px 99px 543px 50px);
+}
+
+#button-newlist:hover:active {
+ -moz-image-region: rect(510px 149px 543px 100px);
+}
+
+#button-newlist[disabled] {
+ -moz-image-region: rect(510px 199px 543px 150px) !important;
+}
+
+#button-editcard {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(442px 49px 475px 0);
+}
+
+#button-editcard:hover {
+ -moz-image-region: rect(442px 99px 475px 50px);
+}
+
+#button-editcard:hover:active {
+ -moz-image-region: rect(442px 149px 475px 100px);
+}
+
+#button-editcard[disabled] {
+ -moz-image-region: rect(442px 199px 475px 150px) !important;
+}
+
+#button-newmessage {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(170px 49px 203px 0);
+}
+
+
+#button-newmessage:hover {
+ -moz-image-region: rect(170px 99px 203px 50px);
+}
+
+#button-newmessage:hover:active {
+ -moz-image-region: rect(170px 149px 203px 100px);
+}
+
+#button-newmessage[disabled] {
+ -moz-image-region: rect(170px 199px 203px 150px) !important;
+}
+
+#button-abdelete {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(408px 49px 441px 0);
+}
+
+#button-abdelete:hover {
+ -moz-image-region: rect(408px 99px 441px 50px);
+}
+
+#button-abdelete:hover:active {
+ -moz-image-region: rect(408px 149px 441px 100px);
+}
+
+#button-abdelete[disabled] {
+ -moz-image-region: rect(408px 199px 441px 150px) !important;
+}
+
+#blankResultsPaneMessage {
+ font-style: italic;
+}
+
+#localResultsOnlyMessage {
+ font-style: italic;
+ text-align: center;
+}
+
+/* ::::: Card View pane ::::: */
+
+#CardViewBox {
+ -moz-user-focus: ignore;
+ min-width: 150px;
+ background-color: #EFEFEF;
+ overflow: auto;
+}
+
+#CardViewInnerBox {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ padding: 0 8px;
+}
+
+#CardTitle {
+ margin: 0px;
+ border-bottom: 2px solid black;
+ min-width: 120px;
+ font-size: 150%;
+ font-weight: bold;
+ color: #000000;
+}
+
+.cardViewColumn {
+ margin-inline-end: 10px;
+}
+
+.cardViewGroup {
+ margin-top: 8px;
+ padding-bottom: 5px;
+ min-width: 50px;
+}
+
+.CardViewHeading {
+ margin: 0px 0px 1px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ padding-inline-start: 5px;
+ padding-inline-end: 1px;
+ min-width: 30px;
+ background-color: #8E9EAA;
+ color: #ffffff;
+ font-weight: bold;
+}
+
+#cvPhoto {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic.png");
+}
+
+#cvBuddyIcon {
+ padding-inline-start: 20px;
+ padding-top: 2px;
+}
+
+.CardViewText,
+.CardViewLink {
+ margin: 0px;
+ padding-inline-end: 2px;
+ padding-inline-start: 20px;
+ min-width: 30px;
+ color: #000000;
+}
+
+.CardViewLink {
+ color: blue;
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+#cvHomeMapIt, #cvWorkMapIt {
+ margin-inline-start: 3px;
+ margin-bottom: -1px;
+}
+
+html|a {
+ border: none !important;
+ padding-inline-end: 2px;
+ padding-inline-start: 0px;
+}
+
+html|p {
+ border: none !important;
+}
diff --git a/comm/suite/themes/modern/messenger/addressbook/cardDialog.css b/comm/suite/themes/modern/messenger/addressbook/cardDialog.css
new file mode 100644
index 0000000000..9d4d091024
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/cardDialog.css
@@ -0,0 +1,72 @@
+/* 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/. */
+
+/* ===== cardViewOverlay.css ============================================
+ == Styles for the Address Book Card view.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Card Edit dialog ::::: */
+
+.CardEditWidth {
+ width: 42ch;
+}
+
+.CardEditLabel {
+ text-align: end;
+}
+
+.PhoneEditWidth {
+ width: 24ch;
+}
+
+.AddressCardEditWidth {
+ width: 72ch;
+}
+
+.YearWidth {
+ width: 8ch;
+}
+
+.stateZipSpacer {
+ width: 6ch;
+}
+
+.ZipWidth {
+ width: 14ch;
+}
+
+#photo {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic.png");
+}
+
+#GenericPhotoList[value="default"] {
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/contact-generic-tiny.png");
+}
+
+#PhotoContainer {
+ margin: 5px;
+}
+
+#PhotoDropTarget {
+ margin-top: 5px;
+}
+
+#PhotoDropTarget:hover {
+ border: 1px dashed #CACAFF;
+}
+
+#ProgressContainer {
+ max-height: 0;
+ transition: all .5s ease-out;
+ overflow: hidden;
+}
+
+#ProgressContainer.expanded {
+ margin-top: 10px;
+ max-height: 40px; /* something higher than the actual height, but not too large */
+}
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/contact-generic-tiny.png b/comm/suite/themes/modern/messenger/addressbook/icons/contact-generic-tiny.png
new file mode 100644
index 0000000000..300c09e914
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/contact-generic-tiny.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/contact-generic.png b/comm/suite/themes/modern/messenger/addressbook/icons/contact-generic.png
new file mode 100644
index 0000000000..452f1cf655
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/contact-generic.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/directory-down.png b/comm/suite/themes/modern/messenger/addressbook/icons/directory-down.png
new file mode 100644
index 0000000000..13e8f1f793
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/directory-down.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/directory.png b/comm/suite/themes/modern/messenger/addressbook/icons/directory.png
new file mode 100644
index 0000000000..db549f2406
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/directory.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/list.png b/comm/suite/themes/modern/messenger/addressbook/icons/list.png
new file mode 100644
index 0000000000..7abc48d0cc
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/list.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/mast-ab.png b/comm/suite/themes/modern/messenger/addressbook/icons/mast-ab.png
new file mode 100644
index 0000000000..139d884b34
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/mast-ab.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/myaddrbk.png b/comm/suite/themes/modern/messenger/addressbook/icons/myaddrbk.png
new file mode 100644
index 0000000000..9aa2816f28
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/myaddrbk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/person.png b/comm/suite/themes/modern/messenger/addressbook/icons/person.png
new file mode 100644
index 0000000000..bd1456b017
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/person.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/icons/secure-directory.png b/comm/suite/themes/modern/messenger/addressbook/icons/secure-directory.png
new file mode 100644
index 0000000000..f0a9e5e757
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/icons/secure-directory.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/addressbook/selectAddressesDialog.css b/comm/suite/themes/modern/messenger/addressbook/selectAddressesDialog.css
new file mode 100644
index 0000000000..eeca2ef7fe
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/selectAddressesDialog.css
@@ -0,0 +1,49 @@
+/* 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/. */
+
+/* ===== selectAddressesDialog.css ======================================
+ == Styles for the Select Addresses Dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Select Addresses dialog ::::: */
+
+#topBox {
+ padding: 0.5em 0 1.0em;
+}
+
+#abResultsTree {
+ min-width: 10px;
+}
+
+#resultsBox {
+ border: 1px solid #000000;
+}
+
+#addToBucketButtonBox {
+ min-width: 10px;
+ padding: 0.3em;
+}
+
+#addressBucket {
+ min-width: 10px;
+}
+
+#newEditButtonBox {
+ padding-top: 0.5em;
+ padding-inline-end: 0px;
+ margin-inline-start: 0px;
+}
+
+.middle-button-spacer {
+ width: 10px;
+}
+
+.above-remove-spacer {
+ width: 10px;
+ height: 15px;
+}
diff --git a/comm/suite/themes/modern/messenger/addressbook/sidebarPanel.css b/comm/suite/themes/modern/messenger/addressbook/sidebarPanel.css
new file mode 100644
index 0000000000..842fc33d26
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressbook/sidebarPanel.css
@@ -0,0 +1,12 @@
+/* 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/. */
+
+/* ===== sidebarPanel.css ===============================================
+ == Styles for the Address Book sidebar panel.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/addressbook/addressbook.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
diff --git a/comm/suite/themes/modern/messenger/addressingWidget.css b/comm/suite/themes/modern/messenger/addressingWidget.css
new file mode 100644
index 0000000000..89bf3530c1
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/addressingWidget.css
@@ -0,0 +1,43 @@
+/* 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/. */
+
+/* ::::: addressing widget ::::: */
+
+#addressingWidget {
+ -moz-user-focus: none;
+ width: 0;
+}
+
+#typecol-addressingWidget {
+ min-width: 9em;
+ border-right: 1px solid #C4CADC;
+}
+
+.addressingWidgetItem,
+.dummy-row {
+ border: none !important;
+ background-color: inherit !important;
+ color: inherit !important;
+}
+
+.addressingWidgetCell {
+ border-bottom: 1px solid #C4CADC;
+ padding: 0px;
+}
+
+.addressingWidgetCell:first-child {
+ border-top: none;
+}
+
+.dummy-row-cell:first-child {
+ border-top: none;
+ border-bottom: 1px solid #C4CADC;
+}
+
+.person-icon {
+ cursor: default !important;
+ margin: 2px 4px;
+ border: none;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/person.png");
+}
diff --git a/comm/suite/themes/modern/messenger/browserRequest.css b/comm/suite/themes/modern/messenger/browserRequest.css
new file mode 100644
index 0000000000..0924641265
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/browserRequest.css
@@ -0,0 +1,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/. */
+
+#security-button {
+ width: 20px;
+ padding-right: 5px;
+ background-repeat: no-repeat;
+}
+
+#security-button[level="high"] {
+ background-image: url("chrome://messenger/skin/icons/secure.png");
+}
+
+#security-button[level="broken"] {
+ background-image: url("chrome://messenger/skin/icons/insecure.png");
+}
+
+#security-button[loading="true"] {
+ background-image: url("chrome://messenger/skin/icons/loading.png");
+ background-position: 4px 2px;
+}
+
+#header {
+ overflow: hidden;
+ border-bottom: 1px solid black;
+}
+
+#addressbox {
+ font-weight: bold;
+ font-size: normal;
+ -moz-appearance: textfield;
+ overflow: hidden;
+ margin: 0px 5px;
+ font-weight: normal;
+}
+
+#headerMessage {
+ margin-top: 4px;
+}
diff --git a/comm/suite/themes/modern/messenger/dialogs.css b/comm/suite/themes/modern/messenger/dialogs.css
new file mode 100644
index 0000000000..095ef86341
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/dialogs.css
@@ -0,0 +1,32 @@
+/* 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/. */
+
+/* ===== dialogs.css ====================================================
+ == Styles used by the general dialogs in Messenger.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: fieldMapImport.xul ::::: */
+
+.importsampledata {
+ border-left: 3px solid #C2D0D0;
+ padding-inline-start: 10px;
+}
+
+/* ::::: importDialog.xul ::::: */
+
+#progressStatus {
+ margin: 1em 0px 0px;
+}
+
+#progressMeter {
+ margin-top: 1em;
+ margin-bottom: 2em;
+ margin-inline-start: 1em;
+ margin-inline-end: 2em;
+ width: 26em;
+}
diff --git a/comm/suite/themes/modern/messenger/filterDialog.css b/comm/suite/themes/modern/messenger/filterDialog.css
new file mode 100644
index 0000000000..e7ce71dee4
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/filterDialog.css
@@ -0,0 +1,67 @@
+/* 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/. */
+
+/* ===== filterDialog.css ===============================================
+ == Styles for the Mail Filters dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: columns :::::: */
+
+listcell.listcell-iconic > .listcell-label {
+ display: none;
+}
+
+listcell[enabled="false"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+listcell[enabled="true"] {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+.search-menulist, .search-value-menulist {
+ width: 12em;
+}
+
+.search-menulist[unavailable="true"] {
+ color: #8C99AB;
+}
+
+.small-button {
+ min-width: 3em;
+ padding: 0px;
+ margin: 0px 1px;
+}
+
+textbox {
+ margin: 1px 4px;
+}
+
+#FilterEditor {
+ padding: 0px;
+}
+
+#filterListDialog {
+ padding: 0px;
+}
+
+.filler {
+ padding-inline-end: 22px;
+}
+
+.ruleaction {
+ border: 1px solid transparent;
+}
+
+.ruleactionitem {
+ min-width: 20em;
+}
+
+.ruleaction-type {
+ min-width: 15em;
+}
diff --git a/comm/suite/themes/modern/messenger/folderMenus.css b/comm/suite/themes/modern/messenger/folderMenus.css
new file mode 100644
index 0000000000..c5ded47094
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/folderMenus.css
@@ -0,0 +1,120 @@
+/* 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/. */
+
+/* ===== folderMenus.css ================================================
+ == Icons for menus which represent mail folder.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: folder icons for menus ::::: */
+
+.folderMenuItem {
+ list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+}
+
+.folderMenuItem[open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-open.png");
+}
+
+.folderMenuItem[ServerType="nntp"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+.folderMenuItem[ServerType="rss"] {
+ list-style-image: url("chrome://messenger-newsblog/skin/icons/rss-feed.png");
+ -moz-image-region: rect(0 16px 16px 0);
+}
+
+/* ..... special folders ..... */
+
+.folderMenuItem[SpecialFolder="Inbox"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox.png");
+}
+
+.folderMenuItem[SpecialFolder="Inbox"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Sent"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-sent.png");
+}
+
+.folderMenuItem[SpecialFolder="Sent"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-sent-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Drafts"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-draft.png");
+}
+
+.folderMenuItem[SpecialFolder="Drafts"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-draft-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Templates"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-template.png");
+}
+
+.folderMenuItem[SpecialFolder="Templates"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-template-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Outbox"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-outbox.png");
+}
+
+.folderMenuItem[SpecialFolder="Outbox"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-outbox-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Junk"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk.png");
+}
+
+.folderMenuItem[SpecialFolder="Junk"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Trash"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-trash.png");
+}
+
+.folderMenuItem[SpecialFolder="Trash"][open="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-trash-open.png");
+}
+
+.folderMenuItem[SpecialFolder="Virtual"] {
+ list-style-image: url("chrome://messenger/skin/icons/folder-search.png");
+}
+
+/* ..... servers ..... */
+
+.folderMenuItem[IsServer="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="imap"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="pop3"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="none"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-local.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="nntp"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="nntp"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news-lock.png");
+}
+
+.folderMenuItem[IsServer="true"][ServerType="rss"] {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
diff --git a/comm/suite/themes/modern/messenger/folderPane.css b/comm/suite/themes/modern/messenger/folderPane.css
new file mode 100644
index 0000000000..7a351985ed
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/folderPane.css
@@ -0,0 +1,237 @@
+/* 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/. */
+
+/* ===== folderPane.css =================================================
+ == Styles for the Folder pane in the Messenger 3-pane window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: mail folder ::::: */
+
+treechildren::-moz-tree-image(folderNameCol) {
+ margin-inline-end: 2px;
+}
+
+.icon-holder[type="folder"],
+treechildren::-moz-tree-image(folderNameCol) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+}
+
+/* ..... Shared ..... */
+
+.icon-holder[type="folder"][ImapShared="true"],
+treechildren::-moz-tree-image(folderNameCol, imapShared-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-share.png");
+}
+
+/* ..... mail folder with new messages ..... */
+
+treechildren::-moz-tree-image(folderNameCol, newMessages-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-new.png");
+}
+
+/* ..... News ..... */
+
+.icon-holder[type="folder"][ServerType="nntp"],
+treechildren::-moz-tree-image(folderNameCol, serverType-nntp) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+.icon-holder[type="folder"][ServerType="nntp"][NewMessages="true"],
+treechildren::-moz-tree-image(folderNameCol, serverType-nntp, newMessages-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup-new.png");
+}
+
+/* ..... Feeds ..... */
+
+.icon-holder[type="folder"][ServerType="rss"],
+treechildren::-moz-tree-image(folderNameCol, serverType-rss) {
+ list-style-image: url("chrome://messenger-newsblog/skin/icons/rss-feed.png");
+ -moz-image-region: rect(0 16px 16px 0);
+}
+
+.icon-holder[type="folder"][ServerType="rss"][NewMessages="true"],
+treechildren::-moz-tree-image(folderNameCol, serverType-rss, newMessages-true) {
+ -moz-image-region: rect(16px 16px 32px 0);
+}
+
+/* ..... Inbox ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Inbox"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Inbox) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox.png");
+}
+
+.icon-holder[type="folder"][SpecialFolder="Inbox"][NewMessages="true"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Inbox, newMessages-true) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-inbox-new.png");
+}
+
+/* ..... Sent ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Sent"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Sent) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-sent.png");
+}
+
+/* ..... Drafts ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Drafts"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Drafts) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-draft.png");
+}
+
+/* ..... Templates ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Templates"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Templates) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-template.png");
+}
+
+/* ..... Outbox ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Outbox"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Outbox) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-outbox.png");
+}
+
+/* ..... Junk ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Junk"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Junk) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-junk.png");
+}
+
+/* ..... Trash ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Trash"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Trash) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-trash.png");
+}
+
+/* ..... Saved Searches ..... */
+
+.icon-holder[type="folder"][SpecialFolder="Virtual"],
+treechildren::-moz-tree-image(folderNameCol, specialFolder-Virtual) {
+ list-style-image: url("chrome://messenger/skin/icons/folder-search.png");
+}
+
+/* ..... Server Folders ..... */
+
+.icon-holder[type="folder"][IsServer="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.icon-holder[type="folder"][BiffState="NewMail"][IsServer="true"],
+treechildren::-moz-tree-image(folderNameCol, biffState-NewMail, isServer-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail-new.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="pop3"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-pop3, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="imap"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-imap, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.icon-holder[type="folder"][BiffState="NewMail"][IsServer="true"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, biffState-NewMail, isServer-true, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock-new.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="none"]
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-none) {
+ list-style-image: url("chrome://messenger/skin/icons/server-local.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="nntp"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-nntp) {
+ list-style-image: url("chrome://messenger/skin/icons/server-news.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="nntp"][IsSecure="true"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-nntp, isSecure-true) {
+ list-style-image: url("chrome://messenger/skin/icons/server-news-lock.png");
+}
+
+.icon-holder[type="folder"][IsServer="true"][ServerType="rss"],
+treechildren::-moz-tree-image(folderNameCol, isServer-true, serverType-rss) {
+ list-style-image: url("chrome://communicator/skin/icons/feedIcon16.png");
+}
+
+/* ::::: All Servers ::::: */
+
+treechildren::-moz-tree-cell-text(closed, subfoldersHaveUnreadMessages-true),
+treechildren::-moz-tree-cell-text(folderNameCol, isServer-true),
+treechildren::-moz-tree-cell-text(hasUnreadMessages-true) {
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(folderNameCol, noSelect-true) {
+ color: gray;
+ font-style: italic;
+}
+
+.tree-folder-checkbox {
+ list-style-image: none;
+}
+
+treechildren::-moz-tree-image(syncCol) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+treechildren::-moz-tree-image(syncCol, synchronize-true) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+treechildren::-moz-tree-image(syncCol, isServer-true) {
+ list-style-image: none;
+}
+
+#folderUnreadCol,
+#folderTotalCol,
+#folderSizeCol {
+ text-align: right;
+}
+
+#folderNameCol [sortDirection="ascending"] {
+ list-style-image: none;
+}
+
+#folderNameCol [sortDirection="descending"] {
+ list-style-image: none;
+}
+
+/* ::::: Folder Summary Popup ::::: */
+
+.folderSummary-message-row {
+ /* This max width ends up dictating the overall width of the popup
+ because it controls how large the preview, subject and sender text can be
+ before cropping kicks in */
+ max-width: 450px;
+}
+
+.folderSummary-subject {
+ font-weight: bold;
+}
+
+.folderSummary-previewText {
+ color: #8C99AB;
+}
+
+/* Virtual Folder List Dialog */
+
+treechildren::-moz-tree-image(selectedColumn) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://global/skin/checkbox/cbox.gif");
+}
+
+treechildren::-moz-tree-image(selectedColumn, selected-true) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
diff --git a/comm/suite/themes/modern/messenger/folderPaneExtras.css b/comm/suite/themes/modern/messenger/folderPaneExtras.css
new file mode 100644
index 0000000000..f315e6e12c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/folderPaneExtras.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* distributors / ISPs can override folder pane icons
+ * see http://www.mozilla.org/mailnews/arch/folderpaneextras.html
+ */
diff --git a/comm/suite/themes/modern/messenger/icons/acct-compose.png b/comm/suite/themes/modern/messenger/icons/acct-compose.png
new file mode 100644
index 0000000000..5f08885c24
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-compose.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/acct-filters.png b/comm/suite/themes/modern/messenger/icons/acct-filters.png
new file mode 100644
index 0000000000..d87ce17f12
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-filters.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/acct-newaccount.png b/comm/suite/themes/modern/messenger/icons/acct-newaccount.png
new file mode 100644
index 0000000000..80a9c66e82
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-newaccount.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/acct-prefs.png b/comm/suite/themes/modern/messenger/icons/acct-prefs.png
new file mode 100644
index 0000000000..93f19dbf6b
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-prefs.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/acct-read.png b/comm/suite/themes/modern/messenger/icons/acct-read.png
new file mode 100644
index 0000000000..edff1f86ca
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-read.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/acct-search.png b/comm/suite/themes/modern/messenger/icons/acct-search.png
new file mode 100644
index 0000000000..4edcf5d4cf
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-search.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/acct-subscribe.png b/comm/suite/themes/modern/messenger/icons/acct-subscribe.png
new file mode 100644
index 0000000000..8430da5cae
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/acct-subscribe.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/attach.png b/comm/suite/themes/modern/messenger/icons/attach.png
new file mode 100644
index 0000000000..2f8209d675
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/attach.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/attachment-col.png b/comm/suite/themes/modern/messenger/icons/attachment-col.png
new file mode 100644
index 0000000000..6e5342df80
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/attachment-col.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/attachment-selected.png b/comm/suite/themes/modern/messenger/icons/attachment-selected.png
new file mode 100644
index 0000000000..1ed3d5a0d1
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/attachment-selected.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/attachment.png b/comm/suite/themes/modern/messenger/icons/attachment.png
new file mode 100644
index 0000000000..2c1edc83a9
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/attachment.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/btn1.png b/comm/suite/themes/modern/messenger/icons/btn1.png
new file mode 100644
index 0000000000..373eeb4773
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/btn1.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/check.png b/comm/suite/themes/modern/messenger/icons/check.png
new file mode 100644
index 0000000000..bae3ab8cc7
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/check.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/dot.png b/comm/suite/themes/modern/messenger/icons/dot.png
new file mode 100644
index 0000000000..541c2f6374
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/dot.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/flagcol-flagged.png b/comm/suite/themes/modern/messenger/icons/flagcol-flagged.png
new file mode 100644
index 0000000000..6fec71af1d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/flagcol-flagged.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-closed.png b/comm/suite/themes/modern/messenger/icons/folder-closed.png
new file mode 100644
index 0000000000..59134ad340
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-closed.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-draft-open.png b/comm/suite/themes/modern/messenger/icons/folder-draft-open.png
new file mode 100644
index 0000000000..2101f71c4e
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-draft-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-draft-share-open.png b/comm/suite/themes/modern/messenger/icons/folder-draft-share-open.png
new file mode 100644
index 0000000000..d184ce03c9
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-draft-share-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-draft-share.png b/comm/suite/themes/modern/messenger/icons/folder-draft-share.png
new file mode 100644
index 0000000000..70478ec756
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-draft-share.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-draft.png b/comm/suite/themes/modern/messenger/icons/folder-draft.png
new file mode 100644
index 0000000000..51c33505a2
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-draft.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-inbox-new.png b/comm/suite/themes/modern/messenger/icons/folder-inbox-new.png
new file mode 100644
index 0000000000..373135b6fd
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-inbox-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-inbox-open.png b/comm/suite/themes/modern/messenger/icons/folder-inbox-open.png
new file mode 100644
index 0000000000..718d02c166
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-inbox-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-inbox-share-open.png b/comm/suite/themes/modern/messenger/icons/folder-inbox-share-open.png
new file mode 100644
index 0000000000..0ee8a7dbe7
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-inbox-share-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-inbox-share.png b/comm/suite/themes/modern/messenger/icons/folder-inbox-share.png
new file mode 100644
index 0000000000..a359131887
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-inbox-share.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-inbox.png b/comm/suite/themes/modern/messenger/icons/folder-inbox.png
new file mode 100644
index 0000000000..718d02c166
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-inbox.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-junk-open.png b/comm/suite/themes/modern/messenger/icons/folder-junk-open.png
new file mode 100644
index 0000000000..ff34019710
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-junk-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-junk.png b/comm/suite/themes/modern/messenger/icons/folder-junk.png
new file mode 100644
index 0000000000..6b4bef6fbe
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-junk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-new-open.png b/comm/suite/themes/modern/messenger/icons/folder-new-open.png
new file mode 100644
index 0000000000..c862bf16ea
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-new-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-new.png b/comm/suite/themes/modern/messenger/icons/folder-new.png
new file mode 100644
index 0000000000..dade2ce9c0
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-newsgroup-new.png b/comm/suite/themes/modern/messenger/icons/folder-newsgroup-new.png
new file mode 100644
index 0000000000..f6ae9b5687
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-newsgroup-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-newsgroup.png b/comm/suite/themes/modern/messenger/icons/folder-newsgroup.png
new file mode 100644
index 0000000000..9637bb00b5
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-newsgroup.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-open.png b/comm/suite/themes/modern/messenger/icons/folder-open.png
new file mode 100644
index 0000000000..abffc7834e
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-outbox-open.png b/comm/suite/themes/modern/messenger/icons/folder-outbox-open.png
new file mode 100644
index 0000000000..f74785fc9a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-outbox-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-outbox.png b/comm/suite/themes/modern/messenger/icons/folder-outbox.png
new file mode 100644
index 0000000000..01c63f8aa2
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-outbox.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-search.png b/comm/suite/themes/modern/messenger/icons/folder-search.png
new file mode 100644
index 0000000000..85934c2474
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-search.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-sent-open.png b/comm/suite/themes/modern/messenger/icons/folder-sent-open.png
new file mode 100644
index 0000000000..381207cf88
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-sent-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-sent-share-open.png b/comm/suite/themes/modern/messenger/icons/folder-sent-share-open.png
new file mode 100644
index 0000000000..e8e4d791e5
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-sent-share-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-sent-share.png b/comm/suite/themes/modern/messenger/icons/folder-sent-share.png
new file mode 100644
index 0000000000..7234b225fe
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-sent-share.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-sent.png b/comm/suite/themes/modern/messenger/icons/folder-sent.png
new file mode 100644
index 0000000000..5ee8888b84
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-sent.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-share-open.png b/comm/suite/themes/modern/messenger/icons/folder-share-open.png
new file mode 100644
index 0000000000..85feccd59d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-share-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-share.png b/comm/suite/themes/modern/messenger/icons/folder-share.png
new file mode 100644
index 0000000000..d1928bbbc9
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-share.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-template-open.png b/comm/suite/themes/modern/messenger/icons/folder-template-open.png
new file mode 100644
index 0000000000..d82d224944
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-template-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-template-share-open.png b/comm/suite/themes/modern/messenger/icons/folder-template-share-open.png
new file mode 100644
index 0000000000..bf8667747d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-template-share-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-template-share.png b/comm/suite/themes/modern/messenger/icons/folder-template-share.png
new file mode 100644
index 0000000000..2fce83f395
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-template-share.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-template.png b/comm/suite/themes/modern/messenger/icons/folder-template.png
new file mode 100644
index 0000000000..b0fce572ea
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-template.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-trash-open.png b/comm/suite/themes/modern/messenger/icons/folder-trash-open.png
new file mode 100644
index 0000000000..25cf8bb429
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-trash-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-trash-share-open.png b/comm/suite/themes/modern/messenger/icons/folder-trash-share-open.png
new file mode 100644
index 0000000000..267004c321
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-trash-share-open.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-trash-share.png b/comm/suite/themes/modern/messenger/icons/folder-trash-share.png
new file mode 100644
index 0000000000..98bda91af9
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-trash-share.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/folder-trash.png b/comm/suite/themes/modern/messenger/icons/folder-trash.png
new file mode 100644
index 0000000000..83f14e1e22
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/folder-trash.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/info.png b/comm/suite/themes/modern/messenger/icons/info.png
new file mode 100644
index 0000000000..3ce14ddc1a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/info.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/insecure.png b/comm/suite/themes/modern/messenger/icons/insecure.png
new file mode 100644
index 0000000000..efda0de255
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/insecure.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/junk.png b/comm/suite/themes/modern/messenger/icons/junk.png
new file mode 100644
index 0000000000..0ae15cd390
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/junk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/junkBar.png b/comm/suite/themes/modern/messenger/icons/junkBar.png
new file mode 100644
index 0000000000..82e47972e2
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/junkBar.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/loading.png b/comm/suite/themes/modern/messenger/icons/loading.png
new file mode 100644
index 0000000000..87d0badd10
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/loading.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/local-mailhost.png b/comm/suite/themes/modern/messenger/icons/local-mailhost.png
new file mode 100644
index 0000000000..96c4dea511
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/local-mailhost.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/mast-mail.png b/comm/suite/themes/modern/messenger/icons/mast-mail.png
new file mode 100644
index 0000000000..cf75df88aa
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/mast-mail.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-junk-other.png b/comm/suite/themes/modern/messenger/icons/message-junk-other.png
new file mode 100644
index 0000000000..e3976f698a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-junk-other.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-del.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-del.png
new file mode 100644
index 0000000000..5d63878f57
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-del.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl-reply.png
new file mode 100644
index 0000000000..c7fbc15387
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl.png
new file mode 100644
index 0000000000..16897414e8
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-reply.png
new file mode 100644
index 0000000000..defaedf14a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd.png
new file mode 100644
index 0000000000..6eb1896ead
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-fwd.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-offl-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-offl-reply.png
new file mode 100644
index 0000000000..d99d1bb64c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-offl.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-offl.png
new file mode 100644
index 0000000000..f571c591d4
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach-reply.png
new file mode 100644
index 0000000000..f581e4f0a9
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-attach.png b/comm/suite/themes/modern/messenger/icons/message-mail-attach.png
new file mode 100644
index 0000000000..a6a329c999
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-attach.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-delete-offl.png b/comm/suite/themes/modern/messenger/icons/message-mail-delete-offl.png
new file mode 100644
index 0000000000..a7f52efae2
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-delete-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl-reply.png
new file mode 100644
index 0000000000..1ff711861a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl.png b/comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl.png
new file mode 100644
index 0000000000..26b66c828d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-fwd-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-fwd-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-fwd-reply.png
new file mode 100644
index 0000000000..875cbb24c2
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-fwd-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-fwd.png b/comm/suite/themes/modern/messenger/icons/message-mail-fwd.png
new file mode 100644
index 0000000000..2af6da71ca
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-fwd.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-imapdelete.png b/comm/suite/themes/modern/messenger/icons/message-mail-imapdelete.png
new file mode 100644
index 0000000000..eaa573c724
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-imapdelete.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-new-offl.png b/comm/suite/themes/modern/messenger/icons/message-mail-new-offl.png
new file mode 100644
index 0000000000..18d0ba5eac
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-new-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-new.png b/comm/suite/themes/modern/messenger/icons/message-mail-new.png
new file mode 100644
index 0000000000..85c6754428
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-offl-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-offl-reply.png
new file mode 100644
index 0000000000..361ece8d7d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-offl-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-offl.png b/comm/suite/themes/modern/messenger/icons/message-mail-offl.png
new file mode 100644
index 0000000000..7776f9b4f6
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail-reply.png b/comm/suite/themes/modern/messenger/icons/message-mail-reply.png
new file mode 100644
index 0000000000..fc75f516d3
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail-reply.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-mail.png b/comm/suite/themes/modern/messenger/icons/message-mail.png
new file mode 100644
index 0000000000..f727e0c8b4
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-mail.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-attach-kill-offl.png b/comm/suite/themes/modern/messenger/icons/message-news-attach-kill-offl.png
new file mode 100644
index 0000000000..8e36e243e5
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-attach-kill-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-attach-kill.png b/comm/suite/themes/modern/messenger/icons/message-news-attach-kill.png
new file mode 100644
index 0000000000..110e943507
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-attach-kill.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-attach-offl.png b/comm/suite/themes/modern/messenger/icons/message-news-attach-offl.png
new file mode 100644
index 0000000000..b9a60d7923
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-attach-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-attach.png b/comm/suite/themes/modern/messenger/icons/message-news-attach.png
new file mode 100644
index 0000000000..3094a71d25
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-attach.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-kill-offl.png b/comm/suite/themes/modern/messenger/icons/message-news-kill-offl.png
new file mode 100644
index 0000000000..0caac52c20
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-kill-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-kill.png b/comm/suite/themes/modern/messenger/icons/message-news-kill.png
new file mode 100644
index 0000000000..509b52ce86
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-kill.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-new-attach-off.png b/comm/suite/themes/modern/messenger/icons/message-news-new-attach-off.png
new file mode 100644
index 0000000000..9281a74ba8
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-new-attach-off.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-new-attach.png b/comm/suite/themes/modern/messenger/icons/message-news-new-attach.png
new file mode 100644
index 0000000000..5df3a9926d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-new-attach.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-new-offl.png b/comm/suite/themes/modern/messenger/icons/message-news-new-offl.png
new file mode 100644
index 0000000000..5aa7495bcd
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-new-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-new.png b/comm/suite/themes/modern/messenger/icons/message-news-new.png
new file mode 100644
index 0000000000..b8ef186906
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news-offl.png b/comm/suite/themes/modern/messenger/icons/message-news-offl.png
new file mode 100644
index 0000000000..fcd8ec577b
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news-offl.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/message-news.png b/comm/suite/themes/modern/messenger/icons/message-news.png
new file mode 100644
index 0000000000..cd134c3428
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/message-news.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/new-mail-alert.png b/comm/suite/themes/modern/messenger/icons/new-mail-alert.png
new file mode 100644
index 0000000000..7d3364336c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/new-mail-alert.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/phishing.png b/comm/suite/themes/modern/messenger/icons/phishing.png
new file mode 100644
index 0000000000..d99a9ea6d4
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/phishing.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/readcol-read.png b/comm/suite/themes/modern/messenger/icons/readcol-read.png
new file mode 100644
index 0000000000..d459afad89
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/readcol-read.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/readcol-unread.png b/comm/suite/themes/modern/messenger/icons/readcol-unread.png
new file mode 100644
index 0000000000..8c7ef2d57b
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/readcol-unread.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/remote-blocked.png b/comm/suite/themes/modern/messenger/icons/remote-blocked.png
new file mode 100644
index 0000000000..872efec63f
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/remote-blocked.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/secure.png b/comm/suite/themes/modern/messenger/icons/secure.png
new file mode 100644
index 0000000000..bcca43a917
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/secure.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-local-new.png b/comm/suite/themes/modern/messenger/icons/server-local-new.png
new file mode 100644
index 0000000000..82ba657d2c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-local-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-local.png b/comm/suite/themes/modern/messenger/icons/server-local.png
new file mode 100644
index 0000000000..ae05749377
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-local.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-mail-new.png b/comm/suite/themes/modern/messenger/icons/server-mail-new.png
new file mode 100644
index 0000000000..1288143820
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-mail-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-mail.png b/comm/suite/themes/modern/messenger/icons/server-mail.png
new file mode 100644
index 0000000000..eeeb1a124b
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-mail.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-news-lock.png b/comm/suite/themes/modern/messenger/icons/server-news-lock.png
new file mode 100644
index 0000000000..3c11c02beb
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-news-lock.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-news-new.png b/comm/suite/themes/modern/messenger/icons/server-news-new.png
new file mode 100644
index 0000000000..7d9982a7ee
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-news-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-news.png b/comm/suite/themes/modern/messenger/icons/server-news.png
new file mode 100644
index 0000000000..2e7bba3b91
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-news.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-remote-lock-new.png b/comm/suite/themes/modern/messenger/icons/server-remote-lock-new.png
new file mode 100644
index 0000000000..90dd729c97
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-remote-lock-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/server-remote-lock.png b/comm/suite/themes/modern/messenger/icons/server-remote-lock.png
new file mode 100644
index 0000000000..cca53c85b3
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/server-remote-lock.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-closed-eye.png b/comm/suite/themes/modern/messenger/icons/thread-closed-eye.png
new file mode 100644
index 0000000000..9d36b28575
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-closed-eye.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-closed-kill.png b/comm/suite/themes/modern/messenger/icons/thread-closed-kill.png
new file mode 100644
index 0000000000..e0b2f8fcf7
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-closed-kill.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-closed-offl-eye.png b/comm/suite/themes/modern/messenger/icons/thread-closed-offl-eye.png
new file mode 100644
index 0000000000..e2fdaa68f8
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-closed-offl-eye.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-closed-offl-kill.png b/comm/suite/themes/modern/messenger/icons/thread-closed-offl-kill.png
new file mode 100644
index 0000000000..0fc2ff71dc
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-closed-offl-kill.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-closed.png b/comm/suite/themes/modern/messenger/icons/thread-closed.png
new file mode 100644
index 0000000000..a4330b21e9
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-closed.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-new-closed-eye.png b/comm/suite/themes/modern/messenger/icons/thread-new-closed-eye.png
new file mode 100644
index 0000000000..6a6b6f8397
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-new-closed-eye.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-new-closed-kill.png b/comm/suite/themes/modern/messenger/icons/thread-new-closed-kill.png
new file mode 100644
index 0000000000..9641038a64
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-new-closed-kill.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-eye.png b/comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-eye.png
new file mode 100644
index 0000000000..70a3ccca20
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-eye.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-kill.png b/comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-kill.png
new file mode 100644
index 0000000000..846aa7094e
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-new-closed-offl-kill.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/thread-new-closed.png b/comm/suite/themes/modern/messenger/icons/thread-new-closed.png
new file mode 100644
index 0000000000..74263ba533
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/thread-new-closed.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/threadcol-threaded.png b/comm/suite/themes/modern/messenger/icons/threadcol-threaded.png
new file mode 100644
index 0000000000..2faf37198f
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/threadcol-threaded.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/icons/threadcol-unthreaded.png b/comm/suite/themes/modern/messenger/icons/threadcol-unthreaded.png
new file mode 100644
index 0000000000..f9bc07bb0f
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/icons/threadcol-unthreaded.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/mailWindow1.css b/comm/suite/themes/modern/messenger/mailWindow1.css
new file mode 100644
index 0000000000..6f23ee00bf
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/mailWindow1.css
@@ -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/. */
+
+/* ===== mailWindow1.css ================================================
+ == Styles for the main Mail window in the default layout scheme.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+@import url("chrome://messenger/skin/primaryToolbar.css");
+@import url("chrome://messenger/skin/folderMenus.css");
+@import url("chrome://messenger/skin/folderPane.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* :::::
+ :: Make sure the min height is small so we can
+ :: resize the pane vertically -EDV
+ ::::: */
+
+#messagepanebox {
+ min-height: 10px;
+ height: 0px;
+}
+
+/* ..... folder pane adjustments ..... */
+
+.folderview-cycler > .toolbarbutton-text {
+ display: none;
+}
+
+.folderview-cycler > .toolbarbutton-icon {
+ margin: 0px;
+}
+
+.folderview-cycler[dir="prev"],
+.folderview-cycler[dir="next"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.png");
+}
+
+.folderview-cycler[dir="next"],
+.folderview-cycler[dir="prev"]:-moz-locale-dir(rtl) {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.png");
+}
+
+/* ::::: border adjustments for focus ring and joined splitters ::::: */
+
+#folderpane-splitter {
+ border-right: none;
+ border-bottom: none;
+ border-left: none;
+ min-width: 5px;
+}
+
+#threadpane-splitter {
+ border: none;
+ min-height: 5px;
+}
+
+#threadpane-splitter[state="collapsed"] {
+ border-bottom: 2px solid;
+}
+
+/* ..... tree adjustments ..... */
+
+#folderTree {
+ border-right: 1px solid #97A4B2;
+ min-width: 1px;
+}
+
+#threadTree {
+ border-bottom: 1px solid #97A4B2;
+ min-width: 1px;
+}
+
+#threadTree,
+#accountCentralBox,
+#messagepanebox {
+ border-left: 1px solid #3B414F;
+}
+
+/* ::::: search toolbar ::::: */
+
+#searchToolbar {
+ background: url("chrome://global/skin/toolbar/tb-mid.png") #C7D0D9 repeat-x top;
+}
+
+#searchToolbar > .toolbar-box > toolbargrippy,
+#searchToolbar > .toolbar-box > .toolbar-holder {
+ border-top: 1px solid #CED6DD;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+ border-left: 1px solid #DAE3ED;
+}
+
+#searchToolbar > .toolbar-box > toolbargrippy:hover:active {
+ border-color: #67737E;
+}
+
+/* ::::: location widget ::::: */
+
+#locationIcon {
+ list-style-image: none;
+}
+
+/* ..... message pane adjustments ..... */
+
+#messagepanebox {
+ border-top: 1px solid #3B414F;
+}
+
+#msgHeaderView {
+ border-left: none !important;
+}
+
+#messagepane {
+ border: 1px solid #FFFFFF;
+ border-right: none;
+}
+
+#messagepanebox[focusring="true"] > #messagepanewrapper > #messagepane {
+ border-color: #000000;
+}
+
+/* ..... tabmail ..... */
+
+.tab-close-button {
+ list-style-image: url("chrome://global/skin/icons/close.png");
+}
+
+.tabmail-tab[type="folder"][IsServer="true"],
+.alltabs-item[selected="true"] {
+ font-weight: bold;
+}
+
+.alltabs-item[tabIsScrolled] {
+ font-style: italic;
+}
+
+#tabbar-toolbar {
+ background-color: transparent;
+ min-height: 0;
+}
diff --git a/comm/suite/themes/modern/messenger/messageBody.css b/comm/suite/themes/modern/messenger/messageBody.css
new file mode 100644
index 0000000000..dca30c7826
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messageBody.css
@@ -0,0 +1,190 @@
+/* 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/. */
+
+/* ===== messageBody.css =================================================
+ == Styles for the body of a mail message.
+ ======================================================================= */
+
+@import url(chrome://communicator/skin/smileys.css);
+@import url(chrome://messenger/skin/messageQuotes.css);
+
+@namespace url("http://www.w3.org/1999/xhtml");
+
+/* :::: message header ::::: */
+
+.header-part1 {
+ background-color: #EFEFEF;
+}
+
+.header-part2,
+.header-part3 {
+ background-color: #DEDEDE;
+}
+
+.headerdisplayname {
+ display: inline;
+ font-weight: bold;
+ white-space: pre;
+}
+
+/* ::::: message text, incl. quotes ::::: */
+
+.moz-text-flowed blockquote,
+.moz-text-plain blockquote {
+ margin: 0;
+}
+
+.moz-text-plain pre {
+ margin: 0;
+ font-family: inherit;
+}
+
+.moz-text-plain[wrap="true"] {
+ white-space: pre-wrap;
+}
+
+.moz-text-plain[wrap="false"] {
+ white-space: pre;
+}
+
+.moz-text-plain[wrap="flow"] .moz-txt-sig {
+ white-space: pre-wrap;
+}
+
+.moz-text-plain[graphical-quote="false"] blockquote {
+ border-style: none;
+ padding: 0;
+}
+
+.moz-text-plain[graphical-quote="true"] .moz-txt-citetags {
+ display: none;
+}
+
+.moz-txt-underscore {
+ text-decoration: underline;
+}
+
+.moz-txt-formfeed {
+ display: block;
+ height: 100%;
+}
+
+/* ::::: signature ::::: */
+
+@media not print {
+ .moz-txt-sig,
+ .moz-signature {
+ opacity: 0.5;
+ }
+
+ .moz-txt-sig .moz-txt-sig,
+ .moz-signature .moz-signature {
+ opacity: 1.0;
+ }
+}
+
+/* ::::: vcard ::::: */
+
+.moz-vcard-table {
+ border-radius: 8px;
+ border: thin solid gray;
+ margin-top: 10px;
+ background: url("chrome://communicator/skin/toolbar/prtb-bg-noline.png") repeat-x;
+ background-position: 0px -1px;
+}
+
+.moz-vcard-property {
+ font-size: 80%;
+ color: gray;
+}
+
+.moz-vcard-title-property {
+ padding-bottom: 10px;
+}
+
+.moz-vcard-badge {
+ height: 30px;
+ width: 30px;
+ display: block;
+ background-image: url("chrome://messenger/skin/icons/btn1.png");
+ background-position: -5px -308px;
+}
+
+.moz-vcard-badge:hover {
+ background-position: -55px -308px;
+ outline: 1px dotted;
+}
+
+.moz-vcard-badge:hover:active {
+ background-position: -105px -308px;
+}
+
+/* ::::: attached images ::::: */
+
+.moz-attached-image-container {
+ text-align: center;
+}
+
+.moz-attached-image {
+ image-orientation: from-image;
+}
+
+.moz-attached-image[overflowing="true"] {
+ cursor: zoom-out;
+}
+
+.moz-attached-image[isshrunk="true"] {
+ cursor: zoom-in;
+ max-width: 100%;
+}
+
+/* Old style feeds. */
+#_mailrssiframe {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: none;
+}
+
+/* Attachment display styling (for inline attachments and printing) */
+.mimeAttachmentHeader {
+ border-style: none;
+ border-top: 1px solid gray;
+}
+
+.mimeAttachmentHeaderName {
+ color: gray;
+ font-size: 80%;
+}
+
+.mimeAttachmentWrap {
+ padding: 0 1em;
+}
+
+.mimeAttachmentTable {
+ width: 100%;
+ border-collapse: collapse;
+ table-layout: fixed;
+}
+
+.mimeAttachmentTable tr + tr > td {
+ border-top: 1px solid gray;
+}
+
+.mimeAttachmentFile {
+ word-wrap: break-word;
+}
+
+.mimeAttachmentSize {
+ vertical-align: top;
+ width: 10ch;
+ text-align: right;
+}
+
+.mimeAttachmentFile,
+.mimeAttachmentSize {
+ padding: 0.25em 0;
+}
diff --git a/comm/suite/themes/modern/messenger/messageHeader.css b/comm/suite/themes/modern/messenger/messageHeader.css
new file mode 100644
index 0000000000..d86d816486
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messageHeader.css
@@ -0,0 +1,189 @@
+/* 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/. */
+
+/* ===== messageHeader.css ==============================================
+ == Styles for the header toolbars of a mail message.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: msg header toolbars ::::: */
+
+#collapsedHeaderView,
+#expandedHeaderView {
+ min-width: 1px;
+}
+
+.header-part1 {
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+/* ::::: msg header buttons ::::: */
+
+#expandedAttachmentBox {
+ width: 20em;
+ height: 0;
+}
+
+#attachmentText {
+ font-weight: bold;
+}
+
+/* ::::: msg header captions ::::: */
+
+#msgHeaderView {
+ border-right: 1px solid #7B969C;
+ border-bottom: 1px solid #000000;
+ border-left: 1px solid #7B969C;
+}
+
+#collapsedHeaderView,
+#expandedHeaderView {
+ min-width: 1px;
+}
+
+.headerNameBox {
+ width: 7.7em;
+}
+
+.headerName {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ margin-inline-start: 0px;
+ margin-inline-end: .5em;
+ font-weight: bold;
+ text-align: right;
+}
+
+.headerValue {
+ margin: 0;
+ min-width: 50px;
+ white-space: normal;
+}
+
+.headerValueBox {
+ margin: 0 0 5px;
+}
+
+.headerValueBox[singleline="true"] {
+ overflow: hidden;
+}
+
+.subjectvalue {
+ font-weight: bold;
+}
+
+.tagvalue {
+ margin-top: 0;
+ margin-inline-start: 0;
+}
+
+/* ::::: msg header message ids ::::: */
+
+.messageIdDisplayButton {
+ cursor: pointer;
+ color: #424F63;
+ text-decoration: underline;
+ margin: 0;
+}
+
+.messageIdDisplayButton:hover {
+ color: #1455D6;
+}
+
+.messageIdDisplayImage {
+ padding-inline-start: 2px;
+ -moz-box-pack: end;
+}
+
+/* ::::: msg header email addresses ::::: */
+
+.emailDisplayButton {
+ cursor: pointer;
+ color: #424F63;
+ text-decoration: underline;
+ margin: 0;
+}
+
+.emailDisplayButton:hover {
+ color: #1455D6;
+}
+
+.emailDisplayImage {
+ padding-inline-start: 2px;
+ -moz-box-pack: end;
+}
+
+mail-emailaddress:-moz-focusring {
+ outline: 1px dotted;
+ outline-offset: -1px;
+}
+
+/* ::::: email address twisty ::::: */
+
+.addresstwisty {
+ margin: 2px;
+ list-style-image: url("chrome://global/skin/tree/twisty-clsd.png");
+}
+
+.addresstwisty[open] {
+ list-style-image: url("chrome://global/skin/tree/twisty-open.png");
+}
+
+/* ::::: view expand and collapse twisties ::::: */
+
+.expandHeaderViewButton,
+.collapsedHeaderViewButton {
+ margin: 2px .5em 0;
+}
+
+.expandHeaderViewButton {
+ list-style-image: url("chrome://global/skin/tree/twisty-open.png");
+}
+
+.collapsedHeaderViewButton {
+ list-style-image: url("chrome://global/skin/tree/twisty-clsd.png");
+}
+
+/* ::::: collapsed view styles ::::: */
+
+#collapsedAttachmentBox {
+ -moz-box-pack: center;
+}
+
+#collapseddateValue {
+ margin: 0 .5em;
+ text-align: right;
+}
+
+#collapseddateValue > .textbox-input-box menupopup {
+ text-align: left;
+}
+
+#collapsedfromBox {
+ width: 18em;
+}
+
+#collapseddateBox {
+ width: 12em;
+}
+
+.collapsedHeaderDisplayName {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: .7em;
+ margin-inline-end: .5em;
+ min-height: 16px;
+ font-weight: bold;
+}
+
+.collapsedHeaderValue {
+ margin: 0;
+}
+
+.collapsedAttachmentButton {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach.png");
+ margin-inline-end: .5em;
+}
diff --git a/comm/suite/themes/modern/messenger/messageKeywords.css b/comm/suite/themes/modern/messenger/messageKeywords.css
new file mode 100644
index 0000000000..751eb1eb83
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messageKeywords.css
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* distributors / ISPs can use this
+ * to add or override icons and colors to the message pane.
+ * see http://www.mozilla.org/mailnews/arch/messagekeywords.html
+ */
diff --git a/comm/suite/themes/modern/messenger/messageQuotes.css b/comm/suite/themes/modern/messenger/messageQuotes.css
new file mode 100644
index 0000000000..6c4b636f3c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messageQuotes.css
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Because this sheet is loaded synchronously while the user is waiting for the
+ compose window to appear, it must not @import a ton of other things, and
+ especially must not trigger network access. */
+
+/* ===== messageQuotes.css =================================================
+ == Shared styles such as block quote colors and signature style
+ == between the message body during
+ == message display and the mail editor instance for mail compose.
+ ======================================================================= */
+
+/* workaround for MS Outlook mails where the line-height is set to 0 */
+body {
+ line-height: initial !important;
+}
+
+/* ::::: signature ::::: */
+
+@media not print {
+ div.moz-text-flowed > div.moz-txt-sig,
+ div.moz-text-plain > pre > div.moz-txt-sig,
+ pre.moz-signature {
+ opacity: 0.6;
+ }
+}
+
+/* ::::: Turn on borders and padding for quotes. ::::: */
+blockquote[type=cite] {
+ padding: 0.4ex 1ex;
+ margin: 1ex;
+ border-width: 0px 0px 0px 2px;
+ border-style: none none none solid;
+ border-radius: 2px;
+}
+
+/* ::::: Colorize block quote borders. We only go 5 levels deep. ::::: */
+blockquote[type=cite] {
+ border-color: rgb(131, 165, 201); /* 65% */}
+
+blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(149, 178, 208); /* 70% */
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(166, 190, 216); /* 75% */
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+
+ border-color: rgb(184, 203, 224); /* 80% */
+}
+
+blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] blockquote[type=cite] {
+ border-color: rgb(202, 216, 232); /* 85% */
+}
diff --git a/comm/suite/themes/modern/messenger/messageWindow.css b/comm/suite/themes/modern/messenger/messageWindow.css
new file mode 100644
index 0000000000..105ccb0752
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messageWindow.css
@@ -0,0 +1,13 @@
+/* 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/. */
+
+/* ===== messageWindow.css ==============================================
+ == Styles for the message window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+@import url("chrome://messenger/skin/primaryToolbar.css");
+@import url("chrome://messenger/skin/threadPaneLabels.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/modern/messenger/messenger.css b/comm/suite/themes/modern/messenger/messenger.css
new file mode 100644
index 0000000000..f938368535
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messenger.css
@@ -0,0 +1,23 @@
+/* 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/. */
+
+/* ===== messenger.css ==================================================
+ == Styles shared throughout the Messenger application.
+ ======================================================================= */
+
+@import url("chrome://communicator/skin/");
+@import url("chrome://messenger/content/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: miscellany :::::: */
+
+spacer.spacer {
+ width: 5px;
+ height: 5px;
+}
+
+
+
+
diff --git a/comm/suite/themes/modern/messenger/messengercompose/icons/mast-msgcomp.png b/comm/suite/themes/modern/messenger/messengercompose/icons/mast-msgcomp.png
new file mode 100644
index 0000000000..46c5b8d286
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messengercompose/icons/mast-msgcomp.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/messengercompose/messengercompose.css b/comm/suite/themes/modern/messenger/messengercompose/messengercompose.css
new file mode 100644
index 0000000000..58e6719ad8
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/messengercompose/messengercompose.css
@@ -0,0 +1,211 @@
+/* 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/. */
+
+/* ===== messengercompose.css ===========================================
+ == Styles for the main Messenger Compose window.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar masthead ::::: */
+
+#composeToolbar > .toolbar-holder > .toolbar-primary-icon {
+ background-image: url("chrome://messenger/skin/messengercompose/icons/mast-msgcomp.png");
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+#button-send {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(374px 49px 407px 0);
+}
+
+#button-send:hover {
+ -moz-image-region: rect(374px 99px 407px 50px);
+}
+
+#button-send:hover:active {
+ -moz-image-region: rect(374px 149px 407px 100px);
+}
+
+#button-send[disabled="true"] {
+ -moz-image-region: rect(374px 199px 407px 150px) !important;
+}
+
+#button-address {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(306px 49px 339px 0);
+}
+
+#button-address:hover {
+ -moz-image-region: rect(306px 99px 339px 50px);
+}
+
+#button-address:hover:active {
+ -moz-image-region: rect(306px 149px 339px 100px);
+}
+
+#button-address[disabled="true"] {
+ -moz-image-region: rect(306px 199px 339px 150px) !important;
+}
+
+#button-attach {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(340px 49px 373px 0);
+}
+
+#button-attach:hover {
+ -moz-image-region: rect(340px 99px 373px 50px);
+}
+
+#button-attach:hover:active {
+ -moz-image-region: rect(340px 149px 373px 100px);
+}
+
+#button-attach[disabled="true"] {
+ -moz-image-region: rect(340px 199px 373px 150px) !important;
+}
+
+#spellingButton {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(272px 49px 305px 0);
+}
+
+#spellingButton:hover {
+ -moz-image-region: rect(272px 99px 305px 50px);
+}
+
+#spellingButton:hover:active {
+ -moz-image-region: rect(272px 149px 305px 100px);
+}
+
+#spellingButton[disabled="true"] {
+ -moz-image-region: rect(272px 199px 305px 150px) !important;
+}
+
+#button-save {
+ list-style-image: url("chrome://editor/skin/icons/btn1.png");
+ -moz-image-region: rect(238px 49px 271px 0);
+}
+
+#button-save:hover {
+ -moz-image-region: rect(238px 99px 271px 50px);
+}
+
+#button-save:hover:active {
+ -moz-image-region: rect(238px 149px 271px 100px);
+}
+
+#button-save[disabled="true"] {
+ -moz-image-region: rect(238px 199px 271px 150px) !important;
+}
+
+/* To workaround that the mailnews icons are all 33px tall and do not have small versions */
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button,
+#print-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+ -moz-image-region: rect(5px 42px 38px 0);
+}
+
+toolbar[iconsize="small"] > #print-button:hover,
+#print-button:hover {
+ -moz-image-region: rect(5px 84px 38px 42px);
+}
+
+toolbar[iconsize="small"] > #print-button:hover:active,
+#print-button:hover:active {
+ -moz-image-region: rect(5px 126px 38px 84px);
+}
+
+toolbar[iconsize="small"] > #print-button[disabled="true"],
+#print-button[disabled="true"] {
+ -moz-image-region: rect(5px 168px 38px 126px) !important;
+}
+
+/* ::::: special toolbar colors ::::: */
+
+#MsgHeadersToolbar {
+ background: #B1BDC9;
+}
+
+#FormatToolbar {
+ background: url("chrome://global/skin/toolbar/tb-mid.png") #C7D0D9 repeat-x top;
+}
+
+#MsgHeadersToolbar > toolbargrippy,
+#MsgHeadersToolbar > .toolbar-holder,
+#FormatToolbar > toolbargrippy,
+#FormatToolbar > .toolbar-holder {
+ border-top: 1px solid #CED6DD;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+ border-left: 1px solid #DAE3ED;
+}
+
+#MsgHeadersToolbar > toolbargrippy:hover:active,
+#FormatToolbar > toolbargrippy:hover:active {
+ border-color: #67737E;
+}
+
+.toolbox-top {
+ border-bottom: none;
+}
+
+#attachmentbucket-sizer {
+ border-top: none;
+ border-bottom: none;
+ background-color: #B1BDC9;
+}
+
+#compose-toolbar-sizer {
+ border-top-width: 1px;
+ border-top-color: #EEF0F3;
+}
+
+#FontFaceSelect {
+ max-width: 35ch;
+}
+
+/* ::::: autocomplete icons ::::: */
+
+treechildren::-moz-tree-cell-text(default-match) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 15px;
+ margin-inline-end: -3px;
+ border: none;
+}
+
+treechildren::-moz-tree-image(local-abook) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 4px;
+ margin-inline-end: -3px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/myaddrbk.png");
+}
+
+treechildren::-moz-tree-image(remote-abook) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 3px;
+ margin-inline-end: -4px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/directory.png");
+}
+
+treechildren::-moz-tree-image(remote-err) {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 3px;
+ margin-inline-end: -4px;
+ list-style-image: url("chrome://messenger/skin/addressbook/icons/directory-down.png");
+}
+
+treechildren::-moz-tree-image(subscribed-news) {
+ margin-inline-start: 3px;
+ margin-inline-end: -4px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
diff --git a/comm/suite/themes/modern/messenger/msgSelectOffline.css b/comm/suite/themes/modern/messenger/msgSelectOffline.css
new file mode 100644
index 0000000000..4b025ed994
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/msgSelectOffline.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+/* ===== msgSelectOffline.css =================================================
+ == The dialog where you select which folders to enable for offline use.
+ ========================================================================== */
+
+@import url("chrome://messenger/skin/");
+@import url("chrome://messenger/skin/folderPane.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+treechildren::-moz-tree-image(syncCol) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+treechildren::-moz-tree-image(syncCol, synchronize-true) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+treechildren::-moz-tree-image(syncCol, isServer-true) {
+ list-style-image: none;
+}
+
+#folderNameCol [sortDirection="ascending"] {
+ list-style-image: none;
+}
+
+#folderNameCol [sortDirection="descending"] {
+ list-style-image: none;
+}
diff --git a/comm/suite/themes/modern/messenger/newmailalert.css b/comm/suite/themes/modern/messenger/newmailalert.css
new file mode 100644
index 0000000000..24c6c1e616
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/newmailalert.css
@@ -0,0 +1,67 @@
+/* -*- 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/. */
+
+/* ===== alert.css =====================================================
+ == Styles specific to the alerts dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#newMailAlertNotification {
+ min-height: 60px;
+ border: ridge #5486DA 4px;
+}
+
+#alertImage {
+ list-style-image: url("chrome://branding/content/icon64.png");
+}
+
+#alertImageBox {
+ padding: 4px;
+}
+
+#alertTitle {
+ font-weight: bold;
+}
+
+#alertTextBox {
+ padding: 4px;
+ padding-inline-end: 16px;
+}
+
+.folderSummary-message-row
+{
+ /* This max width ends up dictating the overall width of the alert window
+ because it controls how large the preview, subject and sender text can be
+ before cropping kicks in */
+ max-width: 450px;
+ padding: 0px 5px;
+}
+
+.folderSummary-subject {
+ font-weight: bold;
+}
+
+.folderSummary-sender, .folderSummary-subject {
+ cursor: inherit;
+}
+
+.folderSummary-previewText {
+ color: #8C99AB;
+}
+
+.folderSummaryMessage:hover > .folderSummary-message-row {
+ cursor: pointer;
+ color: #0000FF;
+}
+
+#closeButton {
+ list-style-image: url("chrome://global/skin/icons/closebox.png");
+ -moz-appearance: none;
+ border: none !important;
+ padding: 2px 0px 0px;
+}
diff --git a/comm/suite/themes/modern/messenger/newsblog/feed-subscriptions.css b/comm/suite/themes/modern/messenger/newsblog/feed-subscriptions.css
new file mode 100644
index 0000000000..436140430a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/newsblog/feed-subscriptions.css
@@ -0,0 +1,30 @@
+/* -*- 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/. */
+
+/* ::::: Feed Subscription styling :::::: */
+
+#subscriptionsDialog {
+ width: 40em;
+ height: 30em;
+}
+
+#contentPane {
+ padding: 14px;
+}
+
+#rssFeedInfoBox {
+ border: 1px solid #2D3B49;
+ margin: 4px;
+ padding-top: 4px;
+ background-color: #BBC6D1;
+}
+
+#statusContainerBox {
+ height: 24px;
+}
+
+#autotagPrefix {
+ width: 35ch;
+}
diff --git a/comm/suite/themes/modern/messenger/newsblog/rss-feed.png b/comm/suite/themes/modern/messenger/newsblog/rss-feed.png
new file mode 100644
index 0000000000..aef600bf35
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/newsblog/rss-feed.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/prefPanels.css b/comm/suite/themes/modern/messenger/prefPanels.css
new file mode 100644
index 0000000000..f01b406d98
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/prefPanels.css
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ===== prefPanels.css =================================================
+ == Styles for the Messenger preference panels.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Tags ::::: */
+
+#tagList > listhead {
+ text-align: center;
+}
+
+#tagList > listitem > listcell {
+ padding: 0 16px;
+}
diff --git a/comm/suite/themes/modern/messenger/primaryToolbar.css b/comm/suite/themes/modern/messenger/primaryToolbar.css
new file mode 100644
index 0000000000..7145629267
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/primaryToolbar.css
@@ -0,0 +1,385 @@
+/* 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/. */
+
+/* ===== primaryToolbar.css =============================================
+ == Images for the Mail primary toolbar.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar masthead ::::: */
+
+#msgToolbar > .toolbar-holder > .toolbar-primary-icon {
+ list-style-image: url("chrome://messenger/skin/icons/mast-mail.png");
+}
+
+/* ::::: primary toolbar buttons ::::: */
+
+.toolbarbutton-1 {
+ min-width: 0px;
+}
+
+#button-getmsg {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(102px 49px 135px 0);
+}
+
+#button-getmsg:hover {
+ -moz-image-region: rect(102px 99px 135px 50px);
+}
+
+#button-getmsg:hover:active {
+ -moz-image-region: rect(102px 149px 135px 100px);
+}
+
+#button-getmsg[disabled] {
+ -moz-image-region: rect(102px 199px 135px 150px) !important;
+}
+
+#button-newmsg {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(170px 49px 203px 0);
+}
+
+#button-newmsg:hover {
+ -moz-image-region: rect(170px 99px 203px 50px);
+}
+
+#button-newmsg:hover:active {
+ -moz-image-region: rect(170px 149px 203px 100px);
+}
+
+#button-newmsg[disabled] {
+ -moz-image-region: rect(170px 199px 203px 150px) !important;
+}
+
+#button-reply {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(238px 49px 271px 0);
+}
+
+#button-reply:hover {
+ -moz-image-region: rect(238px 99px 271px 50px);
+}
+
+#button-reply:hover:active {
+ -moz-image-region: rect(238px 149px 271px 100px);
+}
+
+#button-reply[disabled] {
+ -moz-image-region: rect(238px 199px 271px 150px) !important;
+}
+
+#button-replyall {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(272px 49px 305px 0);
+}
+
+#button-replyall:hover {
+ -moz-image-region: rect(272px 99px 305px 50px);
+}
+
+#button-replyall:hover:active {
+ -moz-image-region: rect(272px 149px 305px 100px);
+}
+
+#button-replyall[disabled] {
+ -moz-image-region: rect(272px 199px 305px 150px) !important;
+}
+
+#button-forward {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(68px 49px 101px 0);
+}
+
+#button-forward:hover {
+ -moz-image-region: rect(68px 99px 101px 50px);
+}
+
+#button-forward:hover:active {
+ -moz-image-region: rect(68px 149px 101px 100px);
+}
+
+#button-forward[disabled] {
+ -moz-image-region: rect(68px 199px 101px 150px) !important;
+}
+
+#button-file {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(34px 49px 67px 0);
+}
+
+#button-file:hover {
+ -moz-image-region: rect(34px 99px 67px 50px);
+}
+
+#button-file:hover:active,
+#button-file[open] {
+ -moz-image-region: rect(34px 149px 67px 100px);
+}
+
+#button-file[disabled] {
+ -moz-image-region: rect(34px 199px 67px 150px) !important;
+}
+
+#button-goback {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(578px 49px 611px 0);
+}
+
+#button-goback:hover {
+ -moz-image-region: rect(578px 99px 611px 50px);
+}
+
+#button-goback:hover:active {
+ -moz-image-region: rect(578px 149px 611px 100px);
+}
+
+#button-goback[disabled] {
+ -moz-image-region: rect(578px 199px 611px 150px) !important;
+}
+
+#button-goforward {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(612px 49px 645px 0);
+}
+
+#button-goforward:hover {
+ -moz-image-region: rect(612px 99px 645px 50px);
+}
+
+#button-goforward:hover:active {
+ -moz-image-region: rect(612px 149px 645px 100px);
+}
+
+#button-goforward[disabled] {
+ -moz-image-region: rect(612px 199px 645px 150px) !important;
+}
+
+#button-next {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(204px 49px 237px 0);
+}
+
+#button-next:hover {
+ -moz-image-region: rect(204px 99px 237px 50px);
+}
+
+#button-next:hover:active {
+ -moz-image-region: rect(204px 149px 237px 100px);
+}
+
+#button-next[disabled] {
+ -moz-image-region: rect(204px 199px 237px 150px) !important;
+}
+
+#button-delete {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(0 49px 33px 0);
+}
+
+#button-delete:hover {
+ -moz-image-region: rect(0 99px 33px 50px);
+}
+
+#button-delete:hover:active {
+ -moz-image-region: rect(0 149px 33px 100px);
+}
+
+#button-delete[disabled] {
+ -moz-image-region: rect(0 199px 33px 150px) !important;
+}
+
+toolbarpaletteitem > #button-delete {
+ display: -moz-box;
+}
+
+#button-mark {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(136px 49px 169px 0);
+}
+
+#button-mark:hover {
+ -moz-image-region: rect(136px 99px 169px 50px);
+}
+
+#button-mark:hover:active {
+ -moz-image-region: rect(136px 149px 169px 100px);
+}
+
+#button-mark[disabled] {
+ -moz-image-region: rect(136px 199px 169px 150px) !important;
+}
+
+#button-junk {
+ list-style-image: url("chrome://messenger/skin/icons/btn1.png");
+ -moz-image-region: rect(544px 49px 577px 0);
+}
+
+#button-junk:hover {
+ -moz-image-region: rect(544px 99px 577px 50px);
+}
+
+#button-junk:hover:active {
+ -moz-image-region: rect(544px 149px 577px 100px);
+}
+
+#button-junk[disabled="true"] {
+ -moz-image-region: rect(544px 199px 577px 150px) !important;
+}
+
+/* To workaround the mailnews icons are 33px tall and have no small versions */
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #print-button,
+toolbar[iconsize="small"] > #print-button,
+#print-button {
+ -moz-image-region: rect(5px 42px 38px 0);
+}
+
+toolbar[iconsize="small"] > #print-button:hover,
+#print-button:hover {
+ -moz-image-region: rect(5px 84px 38px 42px);
+}
+
+toolbar[iconsize="small"] > #print-button:hover:active,
+#print-button:hover:active {
+ -moz-image-region: rect(5px 126px 38px 84px);
+}
+
+toolbar[iconsize="small"] > #print-button[disabled="true"],
+#print-button[disabled="true"] {
+ -moz-image-region: rect(5px 168px 38px 126px) !important;
+}
+
+#button-stop {
+ list-style-image: url("chrome://communicator/skin/icons/btn1.png");
+ -moz-image-region: rect(68px 49px 101px 0);
+}
+
+#button-stop:hover {
+ -moz-image-region: rect(68px 99px 101px 50px);
+}
+
+#button-stop:hover:active {
+ -moz-image-region: rect(68px 149px 101px 100px);
+}
+
+#button-stop[disabled="true"] {
+ -moz-image-region: rect(68px 199px 101px 150px) !important;
+}
+
+#sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32-throbber.png");
+}
+
+#locationFolders {
+ width: 22em;
+}
+
+
+/* Force the folder location and mail view items to fit in the available width
+ in the Customize Toolbar dialog. */
+#palette-box #locationFolders,
+#palette-box #folder-location-container,
+#palette-box #searchInput,
+#palette-box #button-search-container,
+#palette-box #button-search,
+#palette-box #mailviews-container,
+#palette-box #viewPicker {
+ -moz-box-flex: 1;
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+/* XXXRatty We don't have any small graphics for these buttons yet.
+toolbar[iconsize="small"] #button-getmsg {}
+toolbar[iconsize="small"] #button-newmsg {}
+toolbar[iconsize="small"] #button-reply {}
+toolbar[iconsize="small"] #button-replyall {}
+toolbar[iconsize="small"] #button-forward {}
+toolbar[iconsize="small"] #button-file {}
+toolbar[iconsize="small"] #button-goback {}
+toolbar[iconsize="small"] #button-goforward {}
+toolbar[iconsize="small"] #button-next {}
+toolbar[iconsize="small"] #button-junk {}
+toolbar[iconsize="small"] #button-delete {}
+toolbar[iconsize="small"] #button-mark {}
+*/
+
+toolbar[iconsize="small"] #button-stop {
+ list-style-image: url("chrome://communicator/skin/icons/common-small.png");
+ -moz-image-region: rect(19px 19px 38px 0);
+}
+
+toolbar[iconsize="small"] #button-stop:hover {
+ -moz-image-region: rect(19px 38px 38px 19px);
+}
+
+toolbar[iconsize="small"] #button-stop:hover:active {
+ -moz-image-region: rect(19px 57px 38px 38px);
+}
+
+toolbar[iconsize="small"] #button-stop[disabled="true"] {
+ -moz-image-region: rect(19px 76px 38px 57px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button,
+toolbar[iconsize="small"] > #sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button[status=active],
+toolbar[iconsize="small"] > #sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16-throbber.png");
+}
+
+/* ::::: Lightning icon sizes ::::: */
+
+#lightning-button-calendar > .toolbarbutton-icon,
+#lightning-button-tasks > .toolbarbutton-icon,
+#extractEventButton .toolbarbutton-icon,
+#extractTaskButton .toolbarbutton-icon {
+ width: 33px;
+ height: 33px;
+}
+
+#lightning-button-calendar[disabled="true"] > .toolbarbutton-icon,
+#lightning-button-tasks[disabled="true"] > .toolbarbutton-icon,
+#extractEventButton[disabled="true"] .toolbarbutton-icon,
+#extractTaskButton[disabled="true"] .toolbarbutton-icon {
+ opacity: 0.3;
+}
+
+/* ::::: message notification bar style rules ::::: */
+
+.msgNotificationBar {
+ border-bottom: 1px solid #000000;
+ -moz-appearance: toolbox;
+ background-color: #C7BC8F;
+ color: black;
+}
+
+.messageImage[value="remoteContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/remote-blocked.png");
+}
+
+.messageImage[value="junkContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/junk.png");
+}
+
+.messageImage[value="phishingContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/phishing.png");
+}
+
+.messageImage[value="mdnContent"] {
+ list-style-image: url("chrome://messenger/skin/icons/info.png");
+}
diff --git a/comm/suite/themes/modern/messenger/searchDialog.css b/comm/suite/themes/modern/messenger/searchDialog.css
new file mode 100644
index 0000000000..a47f54c17b
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/searchDialog.css
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/* ===== searchDialog.css ===============================================
+ == Styles for the Mail Search dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: Search Dialog adjustments ::::: */
+
+
+#searchTermTree {
+ min-height: 50px;
+}
+
+#searchTermListBox {
+ height: 100px;
+}
+
+#searchResultListBox {
+ margin: 5px;
+ height: 100px;
+}
+
+#searchAddressBookWindow,
+#searchMailWindow {
+ padding: 0px;
+}
+
+.search-menulist[unavailable="true"] {
+ color: #8C99AB;
+}
+
+menulist:not(#menuSearchLocalSystem) {
+ width: 12em;
+}
+
+menulist:not(#menuSearchLocalSystem) > menupopup > menuitem {
+ padding-inline-end: 2px;
+}
+
+.filler {
+ padding-inline-end: 22px;
+}
+
+.small-button {
+ min-width: 3em;
+ padding: 0px;
+ margin: 0px 1px;
+}
+
+textbox {
+ margin: 1px 4px;
+}
+
diff --git a/comm/suite/themes/modern/messenger/smime/certFetchingStatus.css b/comm/suite/themes/modern/messenger/smime/certFetchingStatus.css
new file mode 100644
index 0000000000..9638e0879d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/certFetchingStatus.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/modern/messenger/smime/icons/hdrCryptoNotOk.png b/comm/suite/themes/modern/messenger/smime/icons/hdrCryptoNotOk.png
new file mode 100644
index 0000000000..3f4e8add83
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/hdrCryptoNotOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/hdrCryptoOk.png b/comm/suite/themes/modern/messenger/smime/icons/hdrCryptoOk.png
new file mode 100644
index 0000000000..c0f4b5e9d4
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/hdrCryptoOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/hdrSignNotOk.png b/comm/suite/themes/modern/messenger/smime/icons/hdrSignNotOk.png
new file mode 100644
index 0000000000..36c72dd351
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/hdrSignNotOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/hdrSignOk.png b/comm/suite/themes/modern/messenger/smime/icons/hdrSignOk.png
new file mode 100644
index 0000000000..3a0e75d97c
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/hdrSignOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/hdrSignUnknown.png b/comm/suite/themes/modern/messenger/smime/icons/hdrSignUnknown.png
new file mode 100644
index 0000000000..537d4daf84
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/hdrSignUnknown.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/sbCryptoNotOk.png b/comm/suite/themes/modern/messenger/smime/icons/sbCryptoNotOk.png
new file mode 100644
index 0000000000..92c6ee0311
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/sbCryptoNotOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/sbCryptoOk.png b/comm/suite/themes/modern/messenger/smime/icons/sbCryptoOk.png
new file mode 100644
index 0000000000..155c5582ce
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/sbCryptoOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/sbSignNotOk.png b/comm/suite/themes/modern/messenger/smime/icons/sbSignNotOk.png
new file mode 100644
index 0000000000..657b504044
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/sbSignNotOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/sbSignOk.png b/comm/suite/themes/modern/messenger/smime/icons/sbSignOk.png
new file mode 100644
index 0000000000..ceb4d347ab
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/sbSignOk.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/sbSignUnknown.png b/comm/suite/themes/modern/messenger/smime/icons/sbSignUnknown.png
new file mode 100644
index 0000000000..103849166a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/sbSignUnknown.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/icons/smbtn1.png b/comm/suite/themes/modern/messenger/smime/icons/smbtn1.png
new file mode 100644
index 0000000000..994e72b98d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/icons/smbtn1.png
Binary files differ
diff --git a/comm/suite/themes/modern/messenger/smime/msgCompSMIMEOverlay.css b/comm/suite/themes/modern/messenger/smime/msgCompSMIMEOverlay.css
new file mode 100644
index 0000000000..fafce19419
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/msgCompSMIMEOverlay.css
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ===== msgCompSMIMEOverlay.css ========================================
+ == Styles for the S/Mime in composer window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#button-security {
+ list-style-image: url("chrome://messenger/skin/smime/icons/smbtn1.png");
+ -moz-image-region: rect(0px 49px 33px 0px);
+}
+
+#button-security:hover {
+ -moz-image-region: rect(0px 99px 33px 50px);
+}
+
+#button-security:hover:active {
+ -moz-image-region: rect(0px 149px 33px 100px);
+}
+
+#button-security[disabled] {
+ -moz-image-region: rect(0px 199px 33px 150px) !important;
+}
+
+
+#msgcomposeWindow #signing-status {
+ list-style-image: none;
+ visibility: collapse;
+}
+
+#msgcomposeWindow[signing="ok"] #signing-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow[signing="notok"] #signing-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignNotOk.png");
+ visibility: visible;
+}
+
+
+#msgcomposeWindow #encryption-status {
+ list-style-image: none;
+ visibility: collapse;
+}
+
+#msgcomposeWindow[crypto="ok"] #encryption-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoOk.png");
+ visibility: visible;
+}
+
+#msgcomposeWindow[crypto="notok"] #encryption-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoNotOk.png");
+ visibility: visible;
+}
diff --git a/comm/suite/themes/modern/messenger/smime/msgCompSecurityInfo.css b/comm/suite/themes/modern/messenger/smime/msgCompSecurityInfo.css
new file mode 100644
index 0000000000..170c162340
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/msgCompSecurityInfo.css
@@ -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/. */
+
+/* ===== msgReadSecurityInfo.css ========================================
+ == Styles for the security info window when displaying received mail.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
diff --git a/comm/suite/themes/modern/messenger/smime/msgHdrViewSMIMEOverlay.css b/comm/suite/themes/modern/messenger/smime/msgHdrViewSMIMEOverlay.css
new file mode 100644
index 0000000000..9e4cdd72af
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/msgHdrViewSMIMEOverlay.css
@@ -0,0 +1,45 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#signedHdrIcon {
+ list-style-image: none;
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="ok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignOk.png");
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="unknown"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignUnknown.png");
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="mismatch"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignUnknown.png");
+ visibility: visible;
+}
+
+#signedHdrIcon[signed="notok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrSignNotOk.png");
+ visibility: visible;
+}
+
+#encryptedHdrIcon {
+ list-style-image: none;
+ visibility: visible;
+}
+
+#encryptedHdrIcon[encrypted="ok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrCryptoOk.png");
+ visibility: visible;
+}
+
+#encryptedHdrIcon[encrypted="notok"] {
+ list-style-image: url("chrome://messenger/skin/smime/icons/hdrCryptoNotOk.png");
+ visibility: visible;
+}
diff --git a/comm/suite/themes/modern/messenger/smime/msgReadSMIMEOverlay.css b/comm/suite/themes/modern/messenger/smime/msgReadSMIMEOverlay.css
new file mode 100644
index 0000000000..8633a4c09d
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/msgReadSMIMEOverlay.css
@@ -0,0 +1,37 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#status-bar #signed-status {
+ list-style-image: none;
+}
+
+#status-bar[signed="ok"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignOk.png");
+}
+
+#status-bar[signed="unknown"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignUnknown.png");
+}
+
+#status-bar[signed="mismatch"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignUnknown.png");
+}
+
+#status-bar[signed="notok"] #signed-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbSignNotOk.png");
+}
+
+#status-bar #encrypted-status {
+ list-style-image: none;
+}
+
+#status-bar[encrypted="ok"] #encrypted-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoOk.png");
+}
+
+#status-bar[encrypted="notok"] #encrypted-status {
+ list-style-image: url("chrome://messenger/skin/smime/icons/sbCryptoNotOk.png");
+}
diff --git a/comm/suite/themes/modern/messenger/smime/msgReadSecurityInfo.css b/comm/suite/themes/modern/messenger/smime/msgReadSecurityInfo.css
new file mode 100644
index 0000000000..7ca95f7b04
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/smime/msgReadSecurityInfo.css
@@ -0,0 +1,27 @@
+/* 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/. */
+
+/* ===== msgReadSecurityInfo.css ========================================
+ == Styles for the security info window when displaying received mail.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#signatureLabel {
+ font-weight: bold;
+}
+
+#signatureCert {
+ margin: 5px;
+}
+
+#encryptionLabel {
+ font-weight: bold;
+}
+
+#encryptionCert {
+ margin: 5px;
+}
diff --git a/comm/suite/themes/modern/messenger/start.css b/comm/suite/themes/modern/messenger/start.css
new file mode 100644
index 0000000000..059a488a2e
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/start.css
@@ -0,0 +1,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/. */
+
+/* ===== start.css =====================================================
+ == Styles used by the mailnews start page.
+ ======================================================================= */
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url("chrome://branding/content/messenger-start-bg.png") no-repeat fixed center 3em;
+ background-size: auto 85%;
+}
+
+h1 {
+ background: url("chrome://branding/content/messenger-start-hdr.png") repeat-x bottom;
+ border-bottom: 2px solid #26269A;
+ font-size: 1.5em;
+ padding: 0.25em 0.67em; /* 0.67*1.5=1, so fit with 1em below */
+ margin: 0;
+}
+
+#main {
+ margin: 0 1em;
+}
+
+p {
+ margin: 0.5em 0;
+}
+
+h2 {
+ font-size: 1.2em;
+ font-weight: bold;
+}
+
+ul {
+ margin: 0.5em 0;
+}
+
diff --git a/comm/suite/themes/modern/messenger/subscribe.css b/comm/suite/themes/modern/messenger/subscribe.css
new file mode 100644
index 0000000000..1b4961321b
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/subscribe.css
@@ -0,0 +1,78 @@
+/* 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/. */
+
+/* ===== subscribe.css ==================================================
+ == Styles for the Subscribe dialog.
+ ======================================================================= */
+
+@import url("chrome://messenger/skin/messenger.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: subscribed icons :::::: */
+
+treechildren::-moz-tree-checkbox {
+ list-style-image: none;
+}
+
+treechildren::-moz-tree-image(subscribedColumn),
+treechildren::-moz-tree-image(subscribedColumn2) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox.png");
+}
+
+treechildren::-moz-tree-image(subscribedColumn, subscribed-true),
+treechildren::-moz-tree-image(subscribedColumn2, subscribed-true) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.png");
+}
+
+treechildren::-moz-tree-image(subscribedColumn, subscribable-false) {
+ list-style-image: none;
+}
+
+treechildren::-moz-tree-cell-text(nameColumn, subscribable-false) {
+ color: gray;
+ font-style: italic;
+}
+
+/* ::::: folder icons :::::: */
+
+treechildren::-moz-tree-image(nameColumn) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+}
+
+treechildren::-moz-tree-image(nameColumn, serverType-nntp),
+treechildren::-moz-tree-image(nameColumn2, serverType-nntp) {
+ margin-inline-end: 2px;
+ list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
+}
+
+/* ::::: server icons :::::: */
+
+.subscribeMenuItem {
+ list-style-image: url("chrome://messenger/skin/icons/server-mail.png");
+}
+
+.subscribeMenuItem[ServerType="imap"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-remote-lock.png");
+}
+
+.subscribeMenuItem[ServerType="nntp"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news.png");
+}
+
+.subscribeMenuItem[ServerType="nntp"][IsSecure="true"] {
+ list-style-image: url("chrome://messenger/skin/icons/server-news-lock.png");
+}
+
+/* ::::: statusbar adjustments :::::: */
+
+statusbarpanel,
+.progressmeter-statusbar {
+ border: none !important;
+}
+
+#statusContainerBox {
+ margin-inline-end: 4px;
+}
diff --git a/comm/suite/themes/modern/messenger/threadPane.css b/comm/suite/themes/modern/messenger/threadPane.css
new file mode 100644
index 0000000000..5d19edabec
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/threadPane.css
@@ -0,0 +1,351 @@
+/* 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/. */
+
+/* ===== threadPane.css ==============================================
+ == Styles for the thread pane in the Messenger 3-pane window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: thread decoration ::::: */
+
+treechildren::-moz-tree-cell-text(read) {
+ font-weight: normal;
+}
+
+treechildren::-moz-tree-cell-text(unread) {
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell-text(imapdeleted) {
+ text-decoration: line-through;
+}
+
+/* on a collapsed thread, if the top level message is read, but the thread has
+ * unread children, underline the text. 4.x mac did this, very slick
+ */
+treechildren::-moz-tree-cell-text(container, closed, hasUnread, read) {
+ text-decoration: underline;
+}
+
+/* ::::: priority colors ::::: */
+/****
+ **** Priority colors currently not being used at the moment. It has been
+ **** disabled so as to not conflict with the labels color feature.
+ ****
+treechildren::-moz-tree-cell-text(priorityCol, priority-highest) {
+ color: red;
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, priority-high) {
+ color: rgb(128, 0, 0);
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, priority-lowest) {
+ color: rgb(170, 170, 170);
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, priority-low) {
+ color: rgb(85, 85, 85);
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, selected, focus) {
+ color: #000000;
+}
+
+treechildren::-moz-tree-cell-text(priorityCol, selected, focus) {
+ color: #FFFFFF;
+}
+ ****/
+
+/* ::::: message icons ::::: */
+
+/* ::::: message column icons ::::: */
+
+/* ..... thread column ..... */
+
+.threadColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/threadcol-unthreaded.png");
+}
+
+.threadColumnHeader[sortDirection="ascending"] {
+ list-style-image: url("chrome://messenger/skin/icons/threadcol-threaded.png");
+}
+
+.threadColumnHeader[sortDirection="descending"] {
+ list-style-image: url("chrome://messenger/skin/icons/threadcol-threaded.png");
+}
+
+treechildren::-moz-tree-image(threadCol, container) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed.png");
+}
+
+treechildren::-moz-tree-image(threadCol, container, hasUnread) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed.png");
+}
+
+/* ::::: new thread icons for watch and ignore ::::: */
+
+treechildren::-moz-tree-image(news, threadCol, watch) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, ignore) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-kill.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, watch, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-offl-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, ignore, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-closed-offl-kill.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, watch) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, ignore) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-kill.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, watch, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-offl-eye.png");
+}
+
+treechildren::-moz-tree-image(news, threadCol, container, hasUnread, ignore, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/thread-new-closed-offl-kill.png");
+}
+
+/* ..... read column ..... */
+
+.readColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/readcol-unread.png");
+}
+
+treechildren::-moz-tree-image(unreadButtonColHeader) {
+ list-style-image: url("chrome://messenger/skin/icons/readcol-read.png");
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+treechildren::-moz-tree-image(unreadButtonColHeader, unread) {
+ list-style-image: url("chrome://messenger/skin/icons/readcol-unread.png");
+}
+
+/* ..... attachment column ..... */
+
+.attachmentColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/attachment-col.png");
+}
+
+treechildren::-moz-tree-image(attachmentCol, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/attachment.png");
+}
+
+treechildren::-moz-tree-image(attachmentCol, attach, focus, selected) {
+ list-style-image: url("chrome://messenger/skin/icons/attachment-selected.png");
+}
+
+/* ..... flag column ..... */
+
+.flagColumnHeader {
+ list-style-image: url("chrome://messenger/skin/icons/flagcol-flagged.png");
+}
+
+treechildren::-moz-tree-image(flaggedCol) {
+ list-style-image: url("chrome://messenger/skin/icons/readcol-read.png");
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+treechildren::-moz-tree-image(flaggedCol, flagged) {
+ list-style-image: url("chrome://messenger/skin/icons/flagcol-flagged.png");
+}
+
+/* ..... junkStatus column ..... */
+
+.junkStatusHeader {
+ list-style-image: url("chrome://messenger/skin/icons/message-junk-other.png");
+}
+
+/* "unknown" now looks like "not junk". see bug #182386 */
+treechildren::-moz-tree-image(junkStatusCol) {
+ list-style-image: url("chrome://messenger/skin/icons/readcol-read.png");
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+treechildren::-moz-tree-image(junkStatusCol, junk) {
+ list-style-image: url("chrome://messenger/skin/icons/message-junk-other.png");
+}
+
+treechildren::-moz-tree-image(junkStatusCol, notjunk) {
+ list-style-image: url("chrome://messenger/skin/icons/readcol-read.png");
+ padding-inline-start: 0px;
+ padding-inline-end: 4px;
+}
+
+/* ..... subject column, tab and menuitem icons ..... */
+
+.icon-holder[type="message"],
+treechildren::-moz-tree-image(subjectCol) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail.png");
+}
+
+treechildren::-moz-tree-image(subjectCol) {
+ margin-inline-end: 2px;
+}
+
+treechildren::-moz-tree-image(subjectCol, new) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-new.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-reply.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd-reply.png");
+}
+
+.icon-holder[type="message"][Attachment="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, replied, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-reply.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd-reply.png");
+}
+
+.icon-holder[type="message"][IMAPDeleted="true"],
+treechildren::-moz-tree-image(subjectCol, imapdeleted) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-imapdelete.png");
+}
+.icon-holder[type="message"][Offline="true"],
+treechildren::-moz-tree-image(subjectCol, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, new, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-new-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-offl-reply.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, forwarded, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-fwd-offl-reply.png");
+}
+
+.icon-holder[type="message"][Attachment="true"][Offline="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-offl.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd-offl.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-offl-reply.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, attach, forwarded, offline, replied) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-attach-fwd-offl-reply.png");
+}
+
+.icon-holder[type="message"][IMAPDeleted="true"][Offline="true"],
+treechildren::-moz-tree-image(subjectCol, imapdeleted, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-mail-delete-offl.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"],
+.icon-holder[type="message"][MessageType="nntp"],
+treechildren::-moz-tree-image(subjectCol, rss),
+treechildren::-moz-tree-image(subjectCol, news) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, rss, ignoreSubthread),
+treechildren::-moz-tree-image(subjectCol, news, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-kill.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"][Attachment="true"],
+.icon-holder[type="message"][MessageType="nntp"][Attachment="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach, ignoreSubthread),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach-kill.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"][Attachment="true"][Offline="true"],
+.icon-holder[type="message"][MessageType="nntp"][Attachment="true"][Offline="true"],
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach, offline),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach-offl.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, rss, attach, offline, ignoreSubthread),
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, attach, offline, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-attach-kill-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, rss, new),
+treechildren::-moz-tree-image(subjectCol, news, new) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, new, attach) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new-attach.png");
+}
+
+tree[noattachcol="true"] > treechildren::-moz-tree-image(subjectCol, news, new, attach, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new-attach-off.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, news, new, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-new-offl.png");
+}
+
+.icon-holder[type="message"][MessageType="rss"][Offline="true"],
+.icon-holder[type="message"][MessageType="nntp"][Offline="true"],
+treechildren::-moz-tree-image(subjectCol, rss, offline),
+treechildren::-moz-tree-image(subjectCol, news, offline) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-offl.png");
+}
+
+treechildren::-moz-tree-image(subjectCol, rss, offline, ignoreSubthread),
+treechildren::-moz-tree-image(subjectCol, news, offline, ignoreSubthread) {
+ list-style-image: url("chrome://messenger/skin/icons/message-news-kill-offl.png");
+}
+
+#sizeCol,
+#unreadCol,
+#totalCol {
+ text-align: right;
+}
diff --git a/comm/suite/themes/modern/messenger/threadPaneExtras.css b/comm/suite/themes/modern/messenger/threadPaneExtras.css
new file mode 100644
index 0000000000..7ac07445a1
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/threadPaneExtras.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* distributors / ISPs can override this to override thread pane icons.
+ * see http://www.mozilla.org/mailnews/arch/threadpaneextras.html
+ */
diff --git a/comm/suite/themes/modern/messenger/threadPaneLabels.css b/comm/suite/themes/modern/messenger/threadPaneLabels.css
new file mode 100644
index 0000000000..a506bc1b1a
--- /dev/null
+++ b/comm/suite/themes/modern/messenger/threadPaneLabels.css
@@ -0,0 +1,527 @@
+/* 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/. */
+
+/* ===== threadPaneLabels.css ===========================================
+ == Styles for the thread pane in the Messenger 3-pane window.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: thread labels decoration ::::: */
+
+/* There are 10x7 color definitions (size of the color picker used)
+ times 2 (2 style definitions for each color) + 2 general black
+ and white color definitions.
+ The color definitions can be in the following formats:
+ color: red;
+ color: #FF0000;
+ color: rgb(128, 0, 0);
+*/
+treechildren::-moz-tree-cell-text(lc-FFFFFF), .lc-FFFFFF:not([_moz-menuactive]) {
+ color: #FFFFFF
+}
+treechildren::-moz-tree-row(lc-FFFFFF, selected, focus), .lc-FFFFFF[_moz-menuactive] {
+ background-color: #FFFFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-CCCCCC), .lc-CCCCCC:not([_moz-menuactive]) {
+ color: #CCCCCC
+}
+treechildren::-moz-tree-row(lc-CCCCCC, selected, focus), .lc-CCCCCC[_moz-menuactive] {
+ background-color: #CCCCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-C0C0C0), .lc-C0C0C0:not([_moz-menuactive]) {
+ color: #C0C0C0
+}
+treechildren::-moz-tree-row(lc-C0C0C0, selected, focus), .lc-C0C0C0[_moz-menuactive] {
+ background-color: #C0C0C0;
+}
+
+treechildren::-moz-tree-cell-text(lc-999999), .lc-999999:not([_moz-menuactive]) {
+ color: #999999
+}
+treechildren::-moz-tree-row(lc-999999, selected, focus), .lc-999999[_moz-menuactive] {
+ background-color: #999999;
+}
+
+treechildren::-moz-tree-cell-text(lc-666666), .lc-666666:not([_moz-menuactive]) {
+ color: #666666
+}
+treechildren::-moz-tree-row(lc-666666, selected, focus), .lc-666666[_moz-menuactive] {
+ background-color: #666666;
+}
+
+treechildren::-moz-tree-cell-text(lc-333333), .lc-333333:not([_moz-menuactive]) {
+ color: #333333
+}
+treechildren::-moz-tree-row(lc-333333, selected, focus), .lc-333333[_moz-menuactive] {
+ background-color: #333333;
+}
+
+treechildren::-moz-tree-cell-text(lc-000000), .lc-000000:not([_moz-menuactive]) {
+ color: #000000
+}
+treechildren::-moz-tree-row(lc-000000, selected, focus), .lc-000000[_moz-menuactive] {
+ background-color: #000000;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFCCCC), .lc-FFCCCC:not([_moz-menuactive]) {
+ color: #FFCCCC
+}
+treechildren::-moz-tree-row(lc-FFCCCC, selected, focus), .lc-FFCCCC[_moz-menuactive] {
+ background-color: #FFCCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF6666), .lc-FF6666:not([_moz-menuactive]) {
+ color: #FF6666
+}
+treechildren::-moz-tree-row(lc-FF6666, selected, focus), .lc-FF6666[_moz-menuactive] {
+ background-color: #FF6666;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF0000), .lc-FF0000:not([_moz-menuactive]) {
+ color: #FF0000
+}
+treechildren::-moz-tree-row(lc-FF0000, selected, focus), .lc-FF0000[_moz-menuactive] {
+ background-color: #FF0000;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC0000), .lc-CC0000:not([_moz-menuactive]) {
+ color: #CC0000
+}
+treechildren::-moz-tree-row(lc-CC0000, selected, focus), .lc-CC0000[_moz-menuactive] {
+ background-color: #CC0000;
+}
+
+treechildren::-moz-tree-cell-text(lc-990000), .lc-990000:not([_moz-menuactive]) {
+ color: #990000
+}
+treechildren::-moz-tree-row(lc-990000, selected, focus), .lc-990000[_moz-menuactive] {
+ background-color: #990000;
+}
+
+treechildren::-moz-tree-cell-text(lc-660000), .lc-660000:not([_moz-menuactive]) {
+ color: #660000
+}
+treechildren::-moz-tree-row(lc-660000, selected, focus), .lc-660000[_moz-menuactive] {
+ background-color: #660000;
+}
+
+treechildren::-moz-tree-cell-text(lc-330000), .lc-330000:not([_moz-menuactive]) {
+ color: #330000
+}
+treechildren::-moz-tree-row(lc-330000, selected, focus), .lc-330000[_moz-menuactive] {
+ background-color: #330000;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFCC99), .lc-FFCC99:not([_moz-menuactive]) {
+ color: #FFCC99
+}
+treechildren::-moz-tree-row(lc-FFCC99, selected, focus), .lc-FFCC99[_moz-menuactive] {
+ background-color: #FFCC99;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF9966), .lc-FF9966:not([_moz-menuactive]) {
+ color: #FF9966
+}
+treechildren::-moz-tree-row(lc-FF9966, selected, focus), .lc-FF9966[_moz-menuactive] {
+ background-color: #FF9966;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF9900), .lc-FF9900:not([_moz-menuactive]) {
+ color: #FF9900
+}
+treechildren::-moz-tree-row(lc-FF9900, selected, focus), .lc-FF9900[_moz-menuactive] {
+ background-color: #FF9900;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF6600), .lc-FF6600:not([_moz-menuactive]) {
+ color: #FF6600
+}
+treechildren::-moz-tree-row(lc-FF6600, selected, focus), .lc-FF6600[_moz-menuactive] {
+ background-color: #FF6600;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC6600), .lc-CC6600:not([_moz-menuactive]) {
+ color: #CC6600
+}
+treechildren::-moz-tree-row(lc-CC6600, selected, focus), .lc-CC6600[_moz-menuactive] {
+ background-color: #CC6600;
+}
+
+treechildren::-moz-tree-cell-text(lc-993300), .lc-993300:not([_moz-menuactive]) {
+ color: #993300
+}
+treechildren::-moz-tree-row(lc-993300, selected, focus), .lc-993300[_moz-menuactive] {
+ background-color: #993300;
+}
+
+treechildren::-moz-tree-cell-text(lc-663300), .lc-663300:not([_moz-menuactive]) {
+ color: #663300
+}
+treechildren::-moz-tree-row(lc-663300, selected, focus), .lc-663300[_moz-menuactive] {
+ background-color: #663300;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFFF99), .lc-FFFF99:not([_moz-menuactive]) {
+ color: #FFFF99
+}
+treechildren::-moz-tree-row(lc-FFFF99, selected, focus), .lc-FFFF99[_moz-menuactive] {
+ background-color: #FFFF99;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFFF66), .lc-FFFF66:not([_moz-menuactive]) {
+ color: #FFFF66
+}
+treechildren::-moz-tree-row(lc-FFFF66, selected, focus), .lc-FFFF66[_moz-menuactive] {
+ background-color: #FFFF66;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFCC66), .lc-FFCC66:not([_moz-menuactive]) {
+ color: #FFCC66
+}
+treechildren::-moz-tree-row(lc-FFCC66, selected, focus), .lc-FFCC66[_moz-menuactive] {
+ background-color: #FFCC66;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFCC33), .lc-FFCC33:not([_moz-menuactive]) {
+ color: #FFCC33
+}
+treechildren::-moz-tree-row(lc-FFCC33, selected, focus), .lc-FFCC33[_moz-menuactive] {
+ background-color: #FFCC33;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC9933), .lc-CC9933:not([_moz-menuactive]) {
+ color: #CC9933
+}
+treechildren::-moz-tree-row(lc-CC9933, selected, focus), .lc-CC9933[_moz-menuactive] {
+ background-color: #CC9933;
+}
+
+treechildren::-moz-tree-cell-text(lc-996633), .lc-996633:not([_moz-menuactive]) {
+ color: #996633
+}
+treechildren::-moz-tree-row(lc-996633, selected, focus), .lc-996633[_moz-menuactive] {
+ background-color: #996633;
+}
+
+treechildren::-moz-tree-cell-text(lc-663333), .lc-663333:not([_moz-menuactive]) {
+ color: #663333
+}
+treechildren::-moz-tree-row(lc-663333, selected, focus), .lc-663333[_moz-menuactive] {
+ background-color: #663333;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFFFCC), .lc-FFFFCC:not([_moz-menuactive]) {
+ color: #FFFFCC
+}
+treechildren::-moz-tree-row(lc-FFFFCC, selected, focus), .lc-FFFFCC[_moz-menuactive] {
+ background-color: #FFFFCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFFF33), .lc-FFFF33:not([_moz-menuactive]) {
+ color: #FFFF33
+}
+treechildren::-moz-tree-row(lc-FFFF33, selected, focus), .lc-FFFF33[_moz-menuactive] {
+ background-color: #FFFF33;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFFF00), .lc-FFFF00:not([_moz-menuactive]) {
+ color: #FFFF00
+}
+treechildren::-moz-tree-row(lc-FFFF00, selected, focus), .lc-FFFF00[_moz-menuactive] {
+ background-color: #FFFF00;
+}
+
+treechildren::-moz-tree-cell-text(lc-FFCC00), .lc-FFCC00:not([_moz-menuactive]) {
+ color: #FFCC00
+}
+treechildren::-moz-tree-row(lc-FFCC00, selected, focus), .lc-FFCC00[_moz-menuactive] {
+ background-color: #FFCC00;
+}
+
+treechildren::-moz-tree-cell-text(lc-999900), .lc-999900:not([_moz-menuactive]) {
+ color: #999900
+}
+treechildren::-moz-tree-row(lc-999900, selected, focus), .lc-999900[_moz-menuactive] {
+ background-color: #999900;
+}
+
+treechildren::-moz-tree-cell-text(lc-666600), .lc-666600:not([_moz-menuactive]) {
+ color: #666600
+}
+treechildren::-moz-tree-row(lc-666600, selected, focus), .lc-666600[_moz-menuactive] {
+ background-color: #666600;
+}
+
+treechildren::-moz-tree-cell-text(lc-333300), .lc-333300:not([_moz-menuactive]) {
+ color: #333300
+}
+treechildren::-moz-tree-row(lc-333300, selected, focus), .lc-333300[_moz-menuactive] {
+ background-color: #333300;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-99FF99), .lc-99FF99:not([_moz-menuactive]) {
+ color: #99FF99
+}
+treechildren::-moz-tree-row(lc-99FF99, selected, focus), .lc-99FF99[_moz-menuactive] {
+ background-color: #99FF99;
+}
+
+treechildren::-moz-tree-cell-text(lc-66FF99), .lc-66FF99:not([_moz-menuactive]) {
+ color: #66FF99
+}
+treechildren::-moz-tree-row(lc-66FF99, selected, focus), .lc-66FF99[_moz-menuactive] {
+ background-color: #66FF99;
+}
+
+treechildren::-moz-tree-cell-text(lc-33FF33), .lc-33FF33:not([_moz-menuactive]) {
+ color: #33FF33
+}
+treechildren::-moz-tree-row(lc-33FF33, selected, focus), .lc-33FF33[_moz-menuactive] {
+ background-color: #33FF33;
+}
+
+treechildren::-moz-tree-cell-text(lc-33CC00), .lc-33CC00:not([_moz-menuactive]) {
+ color: #33CC00
+}
+treechildren::-moz-tree-row(lc-33CC00, selected, focus), .lc-33CC00[_moz-menuactive] {
+ background-color: #33CC00;
+}
+
+treechildren::-moz-tree-cell-text(lc-009900), .lc-009900:not([_moz-menuactive]) {
+ color: #009900
+}
+treechildren::-moz-tree-row(lc-009900, selected, focus), .lc-009900[_moz-menuactive] {
+ background-color: #009900;
+}
+
+treechildren::-moz-tree-cell-text(lc-006600), .lc-006600:not([_moz-menuactive]) {
+ color: #006600
+}
+treechildren::-moz-tree-row(lc-006600, selected, focus), .lc-006600[_moz-menuactive] {
+ background-color: #006600;
+}
+
+treechildren::-moz-tree-cell-text(lc-003300), .lc-003300:not([_moz-menuactive]) {
+ color: #003300
+}
+treechildren::-moz-tree-row(lc-003300, selected, focus), .lc-003300[_moz-menuactive] {
+ background-color: #003300;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-99FFFF), .lc-99FFFF:not([_moz-menuactive]) {
+ color: #99FFFF
+}
+treechildren::-moz-tree-row(lc-99FFFF, selected, focus), .lc-99FFFF[_moz-menuactive] {
+ background-color: #99FFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-33FFFF), .lc-33FFFF:not([_moz-menuactive]) {
+ color: #33FFFF
+}
+treechildren::-moz-tree-row(lc-33FFFF, selected, focus), .lc-33FFFF[_moz-menuactive] {
+ background-color: #33FFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-66CCCC), .lc-66CCCC:not([_moz-menuactive]) {
+ color: #66CCCC
+}
+treechildren::-moz-tree-row(lc-66CCCC, selected, focus), .lc-66CCCC[_moz-menuactive] {
+ background-color: #66CCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-00CCCC), .lc-00CCCC:not([_moz-menuactive]) {
+ color: #00CCCC
+}
+treechildren::-moz-tree-row(lc-00CCCC, selected, focus), .lc-00CCCC[_moz-menuactive] {
+ background-color: #00CCCC;
+}
+
+treechildren::-moz-tree-cell-text(lc-339999), .lc-339999:not([_moz-menuactive]) {
+ color: #339999
+}
+treechildren::-moz-tree-row(lc-339999, selected, focus), .lc-339999[_moz-menuactive] {
+ background-color: #339999;
+}
+
+treechildren::-moz-tree-cell-text(lc-336666), .lc-336666:not([_moz-menuactive]) {
+ color: #336666
+}
+treechildren::-moz-tree-row(lc-336666, selected, focus), .lc-336666[_moz-menuactive] {
+ background-color: #336666;
+}
+
+treechildren::-moz-tree-cell-text(lc-003333), .lc-003333:not([_moz-menuactive]) {
+ color: #003333
+}
+treechildren::-moz-tree-row(lc-003333, selected, focus), .lc-003333[_moz-menuactive] {
+ background-color: #003333;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-CCFFFF), .lc-CCFFFF:not([_moz-menuactive]) {
+ color: #CCFFFF
+}
+treechildren::-moz-tree-row(lc-CCFFFF, selected, focus), .lc-CCFFFF[_moz-menuactive] {
+ background-color: #CCFFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-66FFFF), .lc-66FFFF:not([_moz-menuactive]) {
+ color: #66FFFF
+}
+treechildren::-moz-tree-row(lc-66FFFF, selected, focus), .lc-66FFFF[_moz-menuactive] {
+ background-color: #66FFFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-33CCFF), .lc-33CCFF:not([_moz-menuactive]) {
+ color: #33CCFF
+}
+treechildren::-moz-tree-row(lc-33CCFF, selected, focus), .lc-33CCFF[_moz-menuactive] {
+ background-color: #33CCFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-3366FF), .lc-3366FF:not([_moz-menuactive]) {
+ color: #3366FF
+}
+treechildren::-moz-tree-row(lc-3366FF, selected, focus), .lc-3366FF[_moz-menuactive] {
+ background-color: #3366FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-3333FF), .lc-3333FF:not([_moz-menuactive]) {
+ color: #3333FF
+}
+treechildren::-moz-tree-row(lc-3333FF, selected, focus), .lc-3333FF[_moz-menuactive] {
+ background-color: #3333FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-000099), .lc-000099:not([_moz-menuactive]) {
+ color: #000099
+}
+treechildren::-moz-tree-row(lc-000099, selected, focus), .lc-000099[_moz-menuactive] {
+ background-color: #000099;
+}
+
+treechildren::-moz-tree-cell-text(lc-000066), .lc-000066:not([_moz-menuactive]) {
+ color: #000066
+}
+treechildren::-moz-tree-row(lc-000066, selected, focus), .lc-000066[_moz-menuactive] {
+ background-color: #000066;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-CCCCFF), .lc-CCCCFF:not([_moz-menuactive]) {
+ color: #CCCCFF
+}
+treechildren::-moz-tree-row(lc-CCCCFF, selected, focus), .lc-CCCCFF[_moz-menuactive] {
+ background-color: #CCCCFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-9999FF), .lc-9999FF:not([_moz-menuactive]) {
+ color: #9999FF
+}
+treechildren::-moz-tree-row(lc-9999FF, selected, focus), .lc-9999FF[_moz-menuactive] {
+ background-color: #9999FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-6666CC), .lc-6666CC:not([_moz-menuactive]) {
+ color: #6666CC
+}
+treechildren::-moz-tree-row(lc-6666CC, selected, focus), .lc-6666CC[_moz-menuactive] {
+ background-color: #6666CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-6633FF), .lc-6633FF:not([_moz-menuactive]) {
+ color: #6633FF
+}
+treechildren::-moz-tree-row(lc-6633FF, selected, focus), .lc-6633FF[_moz-menuactive] {
+ background-color: #6633FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-6600CC), .lc-6600CC:not([_moz-menuactive]) {
+ color: #6600CC
+}
+treechildren::-moz-tree-row(lc-6600CC, selected, focus), .lc-6600CC[_moz-menuactive] {
+ background-color: #6600CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-333399), .lc-333399:not([_moz-menuactive]) {
+ color: #333399
+}
+treechildren::-moz-tree-row(lc-333399, selected, focus), .lc-333399[_moz-menuactive] {
+ background-color: #333399;
+}
+
+treechildren::-moz-tree-cell-text(lc-330099), .lc-330099:not([_moz-menuactive]) {
+ color: #330099
+}
+treechildren::-moz-tree-row(lc-330099, selected, focus), .lc-330099[_moz-menuactive] {
+ background-color: #330099;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-FFCCFF), .lc-FFCCFF:not([_moz-menuactive]) {
+ color: #FFCCFF
+}
+treechildren::-moz-tree-row(lc-FFCCFF, selected, focus), .lc-FFCCFF[_moz-menuactive] {
+ background-color: #FFCCFF;
+}
+
+treechildren::-moz-tree-cell-text(lc-FF99FF), .lc-FF99FF:not([_moz-menuactive]) {
+ color: #FF99FF
+}
+treechildren::-moz-tree-row(lc-FF99FF, selected, focus), .lc-FF99FF[_moz-menuactive] {
+ background-color: #FF99FF;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC66CC), .lc-CC66CC:not([_moz-menuactive]) {
+ color: #CC66CC
+}
+treechildren::-moz-tree-row(lc-CC66CC, selected, focus), .lc-CC66CC[_moz-menuactive] {
+ background-color: #CC66CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-CC33CC), .lc-CC33CC:not([_moz-menuactive]) {
+ color: #CC33CC
+}
+treechildren::-moz-tree-row(lc-CC33CC, selected, focus), .lc-CC33CC[_moz-menuactive] {
+ background-color: #CC33CC;
+}
+
+treechildren::-moz-tree-cell-text(lc-993399), .lc-993399:not([_moz-menuactive]) {
+ color: #993399
+}
+treechildren::-moz-tree-row(lc-993399, selected, focus), .lc-993399[_moz-menuactive] {
+ background-color: #993399;
+}
+
+treechildren::-moz-tree-cell-text(lc-663366), .lc-663366:not([_moz-menuactive]) {
+ color: #663366
+}
+treechildren::-moz-tree-row(lc-663366, selected, focus), .lc-663366[_moz-menuactive] {
+ background-color: #663366;
+}
+
+treechildren::-moz-tree-cell-text(lc-330033), .lc-330033:not([_moz-menuactive]) {
+ color: #330033
+}
+treechildren::-moz-tree-row(lc-330033, selected, focus), .lc-330033[_moz-menuactive] {
+ background-color: #330033;
+}
+
+
+treechildren::-moz-tree-cell-text(lc-white, selected, focus) {
+ color: #FFFFFF
+}
+treechildren::-moz-tree-cell-text(lc-black, selected, focus) {
+ color: #000000
+}
+
diff --git a/comm/suite/themes/modern/moz.build b/comm/suite/themes/modern/moz.build
new file mode 100644
index 0000000000..03ef5c46a1
--- /dev/null
+++ b/comm/suite/themes/modern/moz.build
@@ -0,0 +1,19 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_TARGET_PP_FILES += ["install.rdf"]
+
+DIST_SUBDIR = "extensions/modern@themes.mozilla.org"
+
+JAR_MANIFESTS += ["jar.mn"]
+
+USE_EXTENSION_MANIFEST = True
+
+DEFINES["SEAMONKEY_VERSION"] = CONFIG["SEAMONKEY_VERSION"]
+
+FINAL_TARGET_FILES += [
+ "icon.png",
+ "preview.png",
+]
diff --git a/comm/suite/themes/modern/mozapps/aboutNetworking.css b/comm/suite/themes/modern/mozapps/aboutNetworking.css
new file mode 100644
index 0000000000..a44151b8ed
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/aboutNetworking.css
@@ -0,0 +1,149 @@
+/* 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/. */
+
+@import url("chrome://global/skin/plugins.css");
+@import url("chrome://global/skin/button.css");
+
+html {
+ height: 100%;
+}
+
+body {
+ display: block;
+ align-items: stretch;
+ height: 100%;
+}
+
+#sectionTitle {
+ float: left;
+}
+
+#refreshDiv {
+ text-align: end;
+ margin-bottom: 0.5em;
+}
+
+#refreshButton, #autorefcheck {
+ vertical-align: middle;
+}
+
+/** Categories **/
+
+#categories > .category
+{
+ -moz-appearance: button;
+ display: inline-block;
+
+ margin: 2px;
+ border: 1px solid;
+ padding: 3px;
+ border-color: #000000;
+ border-radius: 2px;
+ min-width: 6em;
+ background-color: #AAB6C4;
+ background-clip: padding-box;
+ color: #000000;
+ font: menu;
+}
+
+.category {
+ cursor: pointer;
+ /* Center category names */
+ align-items: center;
+}
+
+.category .category-name {
+ pointer-events: none;
+}
+
+#categories hr {
+ display: none;
+}
+
+/** Warning container **/
+
+/* XXX: a lot of this is duplicated from info-pages.css since that stylesheet
+ is incompatible with this type of layout */
+.warningBackground:not([hidden]) {
+ display: flex;
+}
+
+.warningBackground {
+ flex-direction: column;
+ box-sizing: border-box;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 50%;
+ top: 0;
+ left: 0;
+ position: fixed;
+}
+
+.container {
+ width: 40%;
+ color: #22262F;
+ background-color: #C7D0D9;
+ background-image: url("chrome://global/skin/icons/warning-48.png");
+ background-repeat: no-repeat;
+ background-position: 30px 3em;
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 78px;
+}
+
+.title {
+ display: inline-block;
+ font-size: 1rem;
+ position: relative;
+ border-bottom: 1px solid #000000;
+ margin-bottom: 1em;
+ padding-bottom: 0.5em;
+}
+
+.warningBackground button {
+ margin-top: 1em;
+ margin-left: 0;
+ min-width: 100px;
+}
+
+/** Content area **/
+
+.warningBackground:not([hidden]) ~ div
+{
+ display: none;
+}
+
+.main-content {
+ flex: 1;
+ margin: 5px 0px;
+ padding: 5px;
+ border: 1px solid #2D3B49;
+ border-radius: 6px;
+}
+
+.header-name {
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+.tab {
+ padding: 0.5em 0;
+}
+
+.tab table {
+ border: 1px solid;
+ width: 100%;
+}
+
+th, td, table {
+ text-align: start;
+}
+
+th {
+ font-size: medium;
+ text-align: start;
+ border-top: 1px dotted #2D3B49;
+}
diff --git a/comm/suite/themes/modern/mozapps/downloads/downloadIcon.png b/comm/suite/themes/modern/mozapps/downloads/downloadIcon.png
new file mode 100644
index 0000000000..da5c66fe42
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/downloads/downloadIcon.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/downloads/downloads.css b/comm/suite/themes/modern/mozapps/downloads/downloads.css
new file mode 100644
index 0000000000..54fed087c8
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/downloads/downloads.css
@@ -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/. */
+
+/* Download View */
+#downloadView {
+ margin: 0;
+ border-top: none;
+ border-right: none;
+ border-left: none;
+}
+
+/* Download View Items */
+richlistitem[type="download"] {
+ padding: 4px 8px 4px 4px;
+ min-height: 46px;
+}
+
+richlistitem[type="download"]:not([selected="true"]):nth-child(odd) {
+ background-color: #F3F3F3;
+}
+
+richlistitem[type="download"][selected="true"] {
+ background-image: url("chrome://mozapps/skin/icons/itemEnabledFader.png");
+}
+
+richlistitem[type="download"] .name {
+ font-size: 120%;
+}
+
+richlistitem[type="download"] .dateTime {
+ font-size: 83.3333%;
+}
+
+richlistitem[type="download"] progressmeter {
+ background-color: #C7D0D9;
+}
+
+richlistitem[type="download"]:not([selected="true"]) .dateTime,
+richlistitem[type="download"]:not([selected="true"]) .status {
+ color: #808080;
+}
+
+.mini-button > .button-box > .button-icon {
+ margin-inline-start: 0;
+}
+
+.mini-button > .button-box > .button-text {
+ margin-inline-start: 0 !important;
+ margin-inline-end: 0 !important;
+}
+
+.mini-button {
+ min-width: 0;
+ min-height: 0;
+ color: #000000 !important;
+ background-color: transparent !important;
+ list-style-image: url("chrome://communicator/skin/downloads/downloadButtons.png");
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+ margin: 0;
+ border: 1px dotted transparent !important;
+ border-radius: 0 !important;
+ outline: none !important;
+}
+
+.mini-button:focus {
+ border-color: #000000 !important;
+}
+
+.cancel {
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.retry {
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+.pause {
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+.pause[disabled="true"] {
+ opacity: 0.5;
+}
+
+.resume {
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.blockedIcon {
+ list-style-image: url("chrome://global/skin/icons/Error.png");
+}
+
+/* prevent flickering when changing states */
+.downloadTypeIcon {
+ min-height: 32px;
+ min-width: 32px;
+}
+
+#clearListButton {
+ margin: 2px 4px;
+}
diff --git a/comm/suite/themes/modern/mozapps/downloads/unknownContentType.css b/comm/suite/themes/modern/mozapps/downloads/unknownContentType.css
new file mode 100644
index 0000000000..3bd06833b8
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/downloads/unknownContentType.css
@@ -0,0 +1,27 @@
+/* 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/. */
+
+#from {
+ margin-top: 1px;
+}
+
+#location {
+ font-weight: bold;
+}
+
+#contentTypeImage {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-inline-start: 0;
+ margin-inline-end: 5px;
+}
+
+.small-indent {
+ margin-left: 15px;
+ margin-right: 15px;
+}
+
+.small-indent label {
+ margin-inline-start: 0;
+}
diff --git a/comm/suite/themes/modern/mozapps/extensions/about.css b/comm/suite/themes/modern/mozapps/extensions/about.css
new file mode 100644
index 0000000000..2764176a63
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/about.css
@@ -0,0 +1,79 @@
+/* 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/. */
+
+#genericAbout {
+ padding: 0px;
+ min-height: 200px;
+ max-height: 400px;
+ width: 30em;
+}
+
+#clientBox {
+ background-color: #C7D0D9;
+ color: #000000;
+}
+
+.basic-info {
+ padding: 10px;
+}
+
+#extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 64px;
+ max-height: 64px;
+ margin-inline-end: 6px;
+}
+
+#genericAbout[addontype="theme"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+#genericAbout[addontype="locale"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+#genericAbout[addontype="plugin"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+#genericAbout[addontype="dictionary"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+#extensionName {
+ font-size: 200%;
+ font-weight: bolder;
+}
+
+#extensionVersion {
+ font-weight: bold;
+}
+
+#extensionDescription {
+ margin-top: 4px;
+}
+
+#groove {
+ margin-top: 8px;
+}
+
+#extensionDetailsBox {
+ overflow: auto;
+ min-height: 100px;
+}
+
+.boxIndent {
+ margin-inline-start: 18px;
+}
+
+#extensionCreator, .contributor {
+ margin: 0px;
+}
+
+.sectionTitle {
+ padding: 2px 0px 3px 0px;
+ margin-top: 3px;
+ font-weight: bold;
+}
+
diff --git a/comm/suite/themes/modern/mozapps/extensions/alerticon-error.png b/comm/suite/themes/modern/mozapps/extensions/alerticon-error.png
new file mode 100644
index 0000000000..8740e4911a
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/alerticon-error.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/alerticon-info-negative.png b/comm/suite/themes/modern/mozapps/extensions/alerticon-info-negative.png
new file mode 100644
index 0000000000..2c5f628ab6
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/alerticon-info-negative.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/alerticon-info-positive.png b/comm/suite/themes/modern/mozapps/extensions/alerticon-info-positive.png
new file mode 100644
index 0000000000..a186c6b7ad
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/alerticon-info-positive.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/alerticon-warning.png b/comm/suite/themes/modern/mozapps/extensions/alerticon-warning.png
new file mode 100644
index 0000000000..75ea826f91
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/alerticon-warning.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/blocklist.css b/comm/suite/themes/modern/mozapps/extensions/blocklist.css
new file mode 100644
index 0000000000..2749b586a7
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/blocklist.css
@@ -0,0 +1,20 @@
+/* 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 {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ border-bottom: 1px solid #A5ABC0;
+}
+
+.addonName {
+ font-weight: bold;
+}
+
+.blockedLabel {
+ font-weight: bold;
+ font-style: italic;
+}
diff --git a/comm/suite/themes/modern/mozapps/extensions/cancel.png b/comm/suite/themes/modern/mozapps/extensions/cancel.png
new file mode 100644
index 0000000000..0d98ab2359
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/cancel.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-available.png b/comm/suite/themes/modern/mozapps/extensions/category-available.png
new file mode 100644
index 0000000000..d1b737ab05
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-available.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-dictionaries.png b/comm/suite/themes/modern/mozapps/extensions/category-dictionaries.png
new file mode 100644
index 0000000000..b26bb7100c
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-dictionaries.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-discover.png b/comm/suite/themes/modern/mozapps/extensions/category-discover.png
new file mode 100644
index 0000000000..a6f5b49b37
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-discover.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-extensions.png b/comm/suite/themes/modern/mozapps/extensions/category-extensions.png
new file mode 100644
index 0000000000..6a76774c7b
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-extensions.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-languages.png b/comm/suite/themes/modern/mozapps/extensions/category-languages.png
new file mode 100644
index 0000000000..4d9ac5ad89
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-languages.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-plugins.png b/comm/suite/themes/modern/mozapps/extensions/category-plugins.png
new file mode 100644
index 0000000000..5c4d8bf471
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-plugins.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-recent.png b/comm/suite/themes/modern/mozapps/extensions/category-recent.png
new file mode 100644
index 0000000000..7ecfc7d4c8
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-recent.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-search.png b/comm/suite/themes/modern/mozapps/extensions/category-search.png
new file mode 100644
index 0000000000..496a919cf3
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-search.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-searchengines.png b/comm/suite/themes/modern/mozapps/extensions/category-searchengines.png
new file mode 100644
index 0000000000..b893cb48a2
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-searchengines.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/category-themes.png b/comm/suite/themes/modern/mozapps/extensions/category-themes.png
new file mode 100644
index 0000000000..be645f76df
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/category-themes.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric-16.png b/comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric-16.png
new file mode 100644
index 0000000000..37e2a5e4ce
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric.png b/comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric.png
new file mode 100644
index 0000000000..b26bb7100c
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/dictionaryGeneric.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/discover-logo.png b/comm/suite/themes/modern/mozapps/extensions/discover-logo.png
new file mode 100644
index 0000000000..cd50735a89
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/discover-logo.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/eula.css b/comm/suite/themes/modern/mozapps/extensions/eula.css
new file mode 100644
index 0000000000..68a8a5bd30
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/eula.css
@@ -0,0 +1,43 @@
+/* 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/. */
+
+#icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 48px;
+ max-height: 48px;
+ margin-inline-end: 6px;
+}
+
+#eula-dialog[addontype="theme"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+#eula-dialog[addontype="locale"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+#eula-dialog[addontype="plugin"] #icon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+#eula-dialog[addontype="dictionary"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+#heading-container {
+ -moz-box-align: center;
+}
+
+#heading {
+ font-size: 120%;
+}
+
+#eula {
+ -moz-appearance: none;
+ color: #000000;
+ background-color: #FFFFFF;
+ margin: 1em;
+ border: 1px solid;
+ border-colors: #6E7378 #EEF0F3 #EEF0F3 #6E7378;
+}
diff --git a/comm/suite/themes/modern/mozapps/extensions/extensionGeneric-16.png b/comm/suite/themes/modern/mozapps/extensions/extensionGeneric-16.png
new file mode 100644
index 0000000000..aec2675e31
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/extensionGeneric-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/extensionGeneric.png b/comm/suite/themes/modern/mozapps/extensions/extensionGeneric.png
new file mode 100644
index 0000000000..6a76774c7b
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/extensionGeneric.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/extensions.css b/comm/suite/themes/modern/mozapps/extensions/extensions.css
new file mode 100644
index 0000000000..55e4deb534
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/extensions.css
@@ -0,0 +1,1077 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#addons-page {
+ padding: 18px;
+}
+
+#view-port-container {
+ /* Needed to allow the radius to clip the inner content, see bug 595656 */
+ /* Disabled because of bug 623615
+ overflow: hidden;
+ */
+ background-color: #C7D0D9;
+ border: 1px solid #494F5D;
+ border-radius: 2px;
+}
+
+.nav-button {
+ list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
+}
+
+#forward-btn {
+ border-inline-start: none;
+}
+
+#back-btn:-moz-locale-dir(ltr),
+#forward-btn:-moz-locale-dir(rtl) {
+ -moz-image-region: rect(0, 18px, 18px, 0);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+#back-btn:-moz-locale-dir(rtl),
+#forward-btn:-moz-locale-dir(ltr) {
+ -moz-image-region: rect(0, 36px, 18px, 18px);
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+
+/*** global warnings ***/
+
+.global-warning-container {
+ overflow-x: hidden;
+}
+
+.global-warning {
+ -moz-box-align: center;
+ padding: 0 8px;
+ color: #916D15;
+ font-weight: bold;
+}
+
+#addons-page[warning] .global-warning-container {
+ background-color: rgba(255, 255, 0, 0.1);
+ background-image: url("chrome://mozapps/skin/extensions/stripes-warning.png");
+ background-repeat: repeat-x;
+}
+
+#detail-view .global-warning {
+ padding: 4px 12px;
+ border-bottom: 1px solid #CAD4E0;
+}
+
+@media (max-width: 600px) {
+ .global-warning-text {
+ display: none;
+ }
+
+ .global-warning .warning-icon {
+ background-color: #FFF;
+ box-shadow: 0px 0px 2px 5px #FFF;
+ border-radius: 10px;
+ }
+}
+
+/*** notification icons ***/
+
+.warning-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+.error-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-error.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+.pending-icon,
+.info-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-info-positive.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+.addon-view[pending="disable"] .pending-icon,
+.addon-view[pending="uninstall"] .pending-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+
+/*** view alert boxes ***/
+
+.alert-container {
+ -moz-box-align: center;
+}
+
+.alert-spacer-before {
+ -moz-box-flex: 1;
+}
+
+.alert-spacer-after {
+ -moz-box-flex: 3;
+}
+
+.alert {
+ -moz-box-align: center;
+ padding: 10px;
+ color: #000000;
+ border: 1px solid #494F5D;
+ border-radius: 8px;
+ background-color: #C7D0D9;
+}
+
+.alert .alert-title {
+ font-weight: bold;
+ font-size: 200%;
+ margin-bottom: 15px;
+}
+
+.alert .addon-control {
+ margin: 1em 2em;
+}
+
+.loading {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+
+/*** category selector ***/
+
+#categories {
+ -moz-appearance: none;
+ border: none;
+ margin-inline-end: -1px;
+ background-color: transparent;
+ position: relative;
+ margin-top: 31px;
+}
+
+.category {
+ -moz-appearance: none;
+ background-color: transparent;
+ color: #252F3B;
+ min-height: 0;
+ padding: 10px 4px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: transparent;
+ -moz-box-align: center;
+ overflow: hidden;
+}
+
+.category:-moz-locale-dir(ltr) {
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+}
+
+.category:-moz-locale-dir(rtl) {
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+}
+
+.category[disabled] {
+ border-top: 0;
+ border-bottom: 0;
+ height: 0;
+ opacity: 0;
+ transition-property: height, opacity;
+ transition-duration: 1s, 0.8s;
+}
+
+.category:not([disabled]) {
+ height: 52px;
+ transition-property: height, opacity;
+ transition-duration: 1s, 0.8s;
+}
+
+.category[selected] {
+ background-color: #C7D0D9;
+ color: #000000;
+ border-color: #494F5D;
+ border-inline-end-color: #C7D0D9;
+}
+
+.category-name {
+ font-size: 150%;
+}
+
+/* Maximize the size of the viewport when the window is small */
+@media (max-width: 800px) {
+ .category-name {
+ display: none;
+ }
+}
+
+.category-badge {
+ background-color: #55D4FF;
+ padding: 2px 8px;
+ margin: 6px 0;
+ border-radius: 10000px;
+ color: #FFF;
+ font-weight: bold;
+ text-align: center;
+}
+
+.category-badge[value="0"] {
+ visibility: hidden;
+}
+
+.category-icon {
+ width: 32px;
+ height: 32px;
+ margin-inline-start: 6px;
+}
+
+#category-search {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-search.png");
+}
+#category-discover {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-discover.png");
+}
+#category-locale {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-languages.png");
+}
+#category-searchengine {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-searchengines.png");
+}
+#category-extension {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-extensions.png");
+}
+#category-theme {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-themes.png");
+}
+#category-plugin {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
+}
+#category-dictionary {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-dictionaries.png");
+}
+#category-availableUpdates {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
+}
+#category-recentUpdates {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
+}
+
+
+/*** header ***/
+
+#header {
+ margin-bottom: 18px;
+}
+
+#header-search {
+ margin: 0;
+}
+
+#header-utils-btn {
+ list-style-image: url("chrome://mozapps/skin/extensions/utilities.png");
+ margin-inline-end: 16px;
+}
+
+.view-header {
+ background-color: rgba(251, 252, 253, 0.25);
+ padding: 4px;
+ margin: 0;
+ min-height: 31px;
+ border-bottom: 1px solid #CAD4E0;
+}
+
+
+/*** sorters ***/
+
+.sort-controls {
+ -moz-appearance: none;
+}
+
+.sorter {
+ -moz-appearance: none;
+ border: none;
+ background-color: transparent;
+ color: #536680;
+ border-radius: 10000px;
+ padding: 0 6px;
+ margin: 0 6px;
+ min-width: 12px !important;
+ -moz-box-direction: reverse;
+}
+
+.sorter .button-box {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.sorter[checkState="1"],
+.sorter[checkState="2"] {
+ background-color: rgba(194, 200, 206, 0.4);
+ box-shadow: 1px 1px 2px #B6BBC4 inset;
+}
+
+.sorter[checkState="1"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+.sorter[checkState="2"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.sorter .button-icon {
+ margin-inline-start: 4px;
+}
+
+
+/*** discover view ***/
+
+.discover-spacer-before,
+.discover-spacer-after {
+ -moz-box-flex: 1;
+}
+
+#discover-error .alert {
+ max-width: 45em;
+ -moz-box-flex: 1;
+}
+
+.discover-logo {
+ list-style-image: url("chrome://mozapps/skin/extensions/discover-logo.png");
+ margin-inline-end: 15px;
+}
+
+.discover-title {
+ font-weight: bold;
+ font-size: 24px;
+ font-family: MetaWebPro-Book, "Trebuchet MS", sans-serif;
+ margin: 0 0 15px 0;
+}
+
+.discover-description {
+ text-align: justify;
+ margin: 0 0 15px 0;
+}
+
+.discover-footer {
+ text-align: justify;
+}
+
+
+/*** list ***/
+
+.list {
+ -moz-appearance: none;
+ margin: 0;
+ border: none;
+ background-color: transparent;
+}
+
+.addon {
+ color: black;
+ border-top: 1px solid;
+ border-top-color: rgba(0, 0, 0, 0.1);
+ border-bottom: 1px solid;
+ border-bottom-color: rgba(255, 255, 255, 0.1);
+ padding: 5px;
+ background-origin: border-box;
+}
+
+.view-pane:not(#search-view) .addon:first-of-type,
+#search-view .addon[first] {
+ border-top-width: 1px;
+ border-top-color: rgba(255, 255, 255, 0.1);
+}
+
+.view-pane:not(#search-view) .addon:last-of-type,
+#search-view .addon[last] {
+ border-bottom-width: 1px;
+ border-bottom-color: rgba(0, 0, 0, 0.1);
+}
+
+.details {
+ cursor: pointer;
+ margin: 0;
+ margin-inline-start: 10px;
+}
+
+.icon-container {
+ width: 48px;
+ height: 48px;
+ margin: 3px 7px;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+}
+
+.icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 48px;
+ max-height: 48px;
+}
+
+.addon[active="false"] .icon {
+ filter: url("chrome://mozapps/skin/extensions/extensions.svg#greyscale");
+}
+
+
+.addon-view[type="theme"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+.addon-view[type="locale"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+.addon-view[type="plugin"] .icon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+.addon-view[type="dictionary"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+.name-container {
+ font-size: 150%;
+ font-weight: bold;
+ margin-bottom: 0;
+ -moz-box-align: end;
+ -moz-box-flex: 1;
+}
+
+.creator {
+ font-weight: bold;
+}
+
+.creator .text-link {
+ color: #0066CC;
+}
+
+.description-container {
+ margin-inline-start: 6px;
+ -moz-box-align: center;
+}
+
+.description {
+ margin: 0;
+}
+
+.warning,
+.pending,
+.error {
+ margin-inline-start: 48px;
+ font-weight: bold;
+ -moz-box-align: center;
+}
+
+.content-container,
+.basicinfo-container {
+ -moz-box-align: start;
+}
+
+.addon[status="installing"] > .content-container {
+ -moz-box-align: stretch;
+}
+
+.advancedinfo-container,
+.update-info-container {
+ -moz-box-align: center;
+}
+
+.update-available {
+ -moz-box-align: end;
+}
+
+.install-status-container {
+ -moz-box-pack: end;
+ -moz-box-align: end;
+}
+
+.name-outer-container {
+ -moz-box-pack: center;
+}
+
+.relnotes-toggle-container,
+.icon-outer-container {
+ -moz-box-pack: start;
+}
+
+.status-container,
+.control-container {
+ -moz-box-pack: end;
+}
+
+.addon-view .warning {
+ color: #916D15;
+}
+
+.addon-view .error {
+ color: #864441;
+}
+
+.addon-view .pending {
+ color: #1B7123;
+}
+
+.addon-view[pending="disable"] .pending,
+.addon-view[pending="uninstall"] .pending {
+ color: #62666E;
+}
+
+.addon .relnotes-container {
+ -moz-box-align: start;
+ margin-inline-start: 6px;
+ height: 0;
+ overflow: hidden;
+ opacity: 0;
+ transition-property: height, opacity;
+ transition-duration: 0.5s, 0.5s;
+}
+
+.addon[show-relnotes] .relnotes-container {
+ opacity: 1;
+ transition-property: height, opacity;
+ transition-duration: 0.5s, 0.5s;
+}
+
+.addon .relnotes-header {
+ font-weight: bold;
+ margin: 10px 0;
+}
+
+.addon .relnotes-toggle {
+ -moz-appearance: none;
+ border: none;
+ background: transparent;
+ font-weight: bold;
+ -moz-box-direction: reverse;
+ cursor: pointer;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+}
+
+.addon .relnotes-toggle > .button-box > .button-icon {
+ padding-inline-start: 4px;
+}
+
+.addon[show-relnotes] .relnotes-toggle {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.png");
+}
+
+.addon[active="false"] {
+ background-color: #B7BFCB;
+}
+
+.addon-view[active="false"],
+.addon-view[active="false"] .name-container {
+ color: #8C99AB;
+}
+
+.addon-view[notification="warning"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-warning.png"),
+ linear-gradient(rgba(255, 255, 0, 0.04),
+ rgba(255, 255, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[notification="error"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-error.png"),
+ linear-gradient(rgba(255, 0, 0, 0.04),
+ rgba(255, 0, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[pending="enable"],
+.addon-view[pending="upgrade"],
+.addon-view[pending="install"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-info-positive.png"),
+ linear-gradient(rgba(0, 255, 0, 0.04),
+ rgba(0, 255, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[pending="disable"],
+.addon-view[pending="uninstall"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-info-negative.png"),
+ linear-gradient(rgba(128, 128, 128, 0.04),
+ rgba(128, 128, 128, 0));
+ background-repeat: repeat-x;
+}
+
+.addon[selected] {
+ background-color: rgba(148, 172, 204, 0.39);
+ color: black;
+}
+
+.addon[active="false"][selected] .name-container {
+ color: #8C99AB;
+}
+
+
+/*** item - uninstalled ***/
+
+.addon[status="uninstalled"] {
+ border: none;
+}
+
+.addon[status="uninstalled"] > .container {
+ -moz-box-align: center;
+ padding: 4px 20px;
+ background-color: #FDFFA8;
+ border-radius: 8px;
+ font-size: 120%;
+}
+
+.addon[status="uninstalled"][selected] {
+ background-color: transparent;
+}
+
+
+
+/*** search view ***/
+
+#search-filter {
+ padding: 5px 20px;
+ font-size: 120%;
+ border-bottom: 1px solid #CAD4E0;
+ overflow-x: hidden;
+}
+
+#search-filter-label {
+ font-weight: bold;
+ color: grey;
+}
+
+.search-filter-radio {
+ -moz-appearance: none;
+ padding: 0 6px;
+ margin: 0 3px;
+ border-radius: 10000px;
+}
+
+.search-filter-radio[selected] {
+ background-color: grey;
+ color: white;
+}
+
+.search-filter-radio .radio-check-box1 {
+ display: none;
+}
+
+.search-filter-radio .radio-icon {
+ display: none;
+}
+
+#search-allresults-link {
+ margin-top: 1em;
+ margin-bottom: 2em;
+}
+
+/*** detail view ***/
+
+#detail-view .loading {
+ opacity: 0;
+}
+
+#detail-view[loading-extended] .loading {
+ opacity: 1;
+ transition-property: opacity;
+ transition-duration: 1s;
+}
+
+.detail-view-container {
+ padding: 0 2em 2em 2em;
+ font-size: 110%;
+}
+
+#detail-notifications {
+ margin-top: 1em;
+ margin-bottom: 2em;
+}
+
+#detail-notifications .warning,
+#detail-notifications .pending,
+#detail-notifications .error {
+ margin-inline-start: 0;
+}
+
+#detail-icon {
+ margin-inline-end: 10px;
+ margin-top: 6px;
+ max-width: 64px;
+ max-height: 64px;
+}
+
+#detail-summary {
+ margin-bottom: 2em;
+}
+
+#detail-name-container {
+ font-size: 200%;
+}
+
+#detail-screenshot {
+ margin-inline-end: 2em;
+ max-width: 300px;
+ max-height: 300px;
+}
+
+#detail-desc-container {
+ margin-bottom: 2em;
+}
+
+#detail-desc, #detail-fulldesc {
+ margin-inline-start: 6px;
+ /* This is necessary to fix layout issues with multi-line descriptions, see
+ bug 592712*/
+ outline: solid transparent;
+ white-space: pre-wrap;
+ min-width: 10em;
+}
+
+#detail-fulldesc {
+ margin-top: 1em;
+}
+
+#detail-contributions {
+ border-radius: 5px;
+ border: 1px solid #D2DBE8;
+ margin-bottom: 2em;
+ padding: 1em;
+ background-color: #F3F7FB;
+}
+
+#detail-contrib-description {
+ font-style: italic;
+ margin-bottom: 1em;
+ color: #373D48;
+}
+
+#detail-contrib-suggested {
+ color: grey;
+ font-weight: bold;
+}
+
+#detail-contrib-btn {
+ color: #FFF;
+ border: 1px solid #3A4EEE;
+ border-radius: 3px;
+ list-style-image: url("chrome://mozapps/skin/extensions/heart.png");
+ background-color: #2F73EF;
+ background-image: linear-gradient(rgba(251, 252, 253, 0.70), rgba(246, 247, 248, 0.27) 49%,
+ rgba(231, 232, 233, 0.25) 51%, rgba(225, 226, 229, 0.1));
+}
+
+#detail-contrib-btn .button-box {
+ padding: 0 6px 1px 6px;
+}
+
+#detail-contrib-btn .button-icon {
+ margin-inline-end: 3px;
+}
+
+#detail-contrib-btn:not(:active):hover {
+ border-color: #4271FF;
+ background-color: #0459F7;
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1),
+ 0 0 3.5px hsl(190, 90%, 80%);
+ transition: background-color .4s ease-in,
+ border-color .3s ease-in,
+ box-shadow .3s ease-in
+}
+
+#detail-contrib-btn:active:hover {
+ background-color: #8FA1C1;
+ border-color: rgba(0, 0, 0, 0.65) rgba(0, 0, 0, 0.55) rgba(0, 0, 0, 0.5);
+ box-shadow: 0 0 6.5px rgba(0, 0, 0, 0.4) inset,
+ 0 0 2px rgba(0, 0, 0, 0.4) inset,
+ 0 1px 0 rgba(255, 255, 255, 0.4);
+}
+
+#detail-grid {
+ margin-bottom: 2em;
+}
+
+.detail-row[first-row="true"],
+.detail-row-complex[first-row="true"] {
+ border-top: none;
+}
+
+.detail-row,
+.detail-row-complex {
+ border-top: 1px solid;
+ border-top-color: rgba(28, 31, 37, 0.1);
+ -moz-box-align: center;
+}
+
+.detail-row-value {
+ margin-inline-start: 0;
+}
+
+#detail-controls {
+ margin-bottom: 1em;
+}
+
+#detail-view[active="false"]:not([pending]):not([notification]) {
+ background-color: #B7BFCB;
+}
+
+/*** creator ***/
+
+.creator > label {
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+}
+
+.creator > .text-link {
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+
+/*** rating ***/
+
+.meta-rating {
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+ padding-top: 2px;
+}
+
+.meta-rating > .star {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-not-won.png");
+ padding: 0 1px;
+}
+
+.meta-rating > .star[on="true"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-won.png");
+}
+
+
+/*** download progress ***/
+
+.download-progress {
+ background-color: rgba(151,152,153,.05);
+ background-image: linear-gradient(rgba(251, 252, 253, 0.95), rgba(246, 247, 248, 0.47) 49%,
+ rgba(231, 232, 233, 0.45) 51%, rgba(225, 226, 229, 0.3));
+ background-clip: padding-box;
+ border-radius: 3px;
+ border: 1px solid;
+ border-color: rgba(0, 0, 0, 0.12) rgba(0, 0, 0, 0.19) rgba(0, 0, 0, 0.38);
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.3) inset,
+ 0 0 0 2px rgba(255, 255, 255, 0.1) inset;
+ width: 200px;
+ height: 21px;
+ margin: 0 8px;
+}
+
+.download-progress[mode="undetermined"] {
+ border-color: #358942 #317F3D #2E773A;
+}
+
+.download-progress[mode="undetermined"] .status-container {
+ padding: 0 2px;
+}
+
+.download-progress .start-cap,
+.download-progress[complete] .end-cap,
+.download-progress[mode="undetermined"] .end-cap,
+.download-progress .progress .progress-bar {
+ -moz-appearance: none;
+ background-image: linear-gradient(#71CA83, #54B06C 49%, #43A05D 51%, #60BB76);
+ margin-top: -1px;
+ margin-bottom: -1px;
+ border: 1px solid;
+ border-color: #358942 #317F3D #2E773A;
+}
+
+.download-progress .start-cap {
+ margin-inline-start: -1px;
+ border-inline-end-width: 0;
+}
+
+.download-progress .end-cap {
+ margin-inline-end: -1px;
+ border-inline-start: 0px !important;
+}
+
+.download-progress .progress .progress-bar {
+ border-left-width: 0;
+ border-right-width: 0;
+ min-height: 21px;
+}
+
+.download-progress .progress {
+ -moz-appearance: none;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ border: none;
+}
+
+.download-progress .start-cap,
+.download-progress .end-cap {
+ width: 4px;
+}
+
+.download-progress .start-cap:-moz-locale-dir(ltr),
+.download-progress .end-cap:-moz-locale-dir(rtl) {
+ border-radius: 3px 0 0 3px;
+}
+
+.download-progress .end-cap:-moz-locale-dir(ltr),
+.download-progress .start-cap:-moz-locale-dir(rtl) {
+ border-radius: 0 3px 3px 0;
+}
+
+.download-progress .cancel {
+ -moz-appearance: none;
+ background-color: rgba(255, 255, 255, 0.4);
+ border: 1px solid rgba(0, 0, 0, 0.4);
+ padding: 3px;
+ border-radius: 3px;
+ min-width: 0;
+ margin: 3px;
+}
+
+.download-progress .cancel:hover {
+ background-color: rgba(255, 255, 255, 0.6);
+ border: 1px solid rgba(0, 0, 0, 0.8);
+}
+
+.download-progress .cancel:active:hover {
+ box-shadow: inset rgba(0, 0, 0, 0.5) 1px 1px 2px;
+}
+
+.download-progress .cancel .button-box {
+ padding: 0;
+ border: none;
+}
+
+.download-progress .cancel .button-text {
+ display: none;
+}
+
+.download-progress .cancel .button-icon {
+ margin-inline-start: 0;
+}
+
+.download-progress .cancel {
+ list-style-image: url('chrome://mozapps/skin/extensions/cancel.png');
+}
+
+.download-progress .status-container {
+ -moz-box-align: center;
+}
+
+.download-progress .status {
+ text-shadow: #FFF 0 0 2px;
+}
+
+/*** install status ***/
+
+.install-status {
+ -moz-box-align: center;
+}
+
+
+/*** check for updates ***/
+
+#updates-container {
+ -moz-box-align: center;
+}
+
+#updates-container .button-link {
+ font-weight: bold;
+}
+
+#updates-installed,
+#updates-downloaded {
+ color: #00BB00;
+ font-weight: bold;
+}
+
+#update-selected {
+ margin: 12px;
+}
+
+
+/*** buttons ***/
+
+.addon-control[disabled="true"] {
+ display: none;
+}
+
+.button-link {
+ -moz-appearance: none;
+ background: transparent;
+ border: none;
+ text-decoration: underline;
+ color: blue;
+ cursor: pointer;
+ min-width: 0;
+ margin: 0 6px;
+}
+
+.button-link:focus {
+ color: red;
+ outline: 1px dotted;
+}
+
+.button-link:hover:active {
+ color: red;
+}
+
+.button-link[visited="true"] {
+ color: purple;
+}
+
+.header-button {
+ margin: 2px;
+ border: 1px solid;
+ padding: 3px;
+ border-color: #000000;
+ border-radius: 2px;
+ background-color: #AAB6C4;
+ background-clip: padding-box;
+ color: #000000;
+ font: menu;
+}
+
+.header-button:focus {
+ border-color: #98A5B2;
+}
+
+.header-button[disabled="true"] {
+ border-color: #8290A5 !important;
+ background-color: #B7BFCB !important;
+ color: #8C99AB !important;
+}
+
+.header-button[disabled="true"] > .toolbarbutton-icon {
+ opacity: 0.4;
+}
+
+.header-button:not([disabled="true"]):active:hover,
+.header-button[open="true"] {
+ border-color: #000000;
+ background-color: #90A1B3;
+ color: #FFFFFF;
+}
+
+.header-button:not([disabled="true"]):active:hover:focus,
+.header-button[open="true"]:focus {
+ border-color: #98A5B2;
+}
+
+.header-button > .toolbarbutton-text {
+ display: none;
+}
diff --git a/comm/suite/themes/modern/mozapps/extensions/extensions.svg b/comm/suite/themes/modern/mozapps/extensions/extensions.svg
new file mode 100644
index 0000000000..076c210469
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/extensions.svg
@@ -0,0 +1,10 @@
+<?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/. -->
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <filter id="greyscale">
+ <feColorMatrix type="saturate" values="0"/>
+ </filter>
+</svg>
diff --git a/comm/suite/themes/modern/mozapps/extensions/heart.png b/comm/suite/themes/modern/mozapps/extensions/heart.png
new file mode 100644
index 0000000000..655f4c4be7
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/heart.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/localeGeneric.png b/comm/suite/themes/modern/mozapps/extensions/localeGeneric.png
new file mode 100644
index 0000000000..4d9ac5ad89
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/localeGeneric.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/navigation.png b/comm/suite/themes/modern/mozapps/extensions/navigation.png
new file mode 100644
index 0000000000..c1e2a026d0
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/navigation.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/newaddon.css b/comm/suite/themes/modern/mozapps/extensions/newaddon.css
new file mode 100644
index 0000000000..525b487e5b
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/newaddon.css
@@ -0,0 +1,110 @@
+/* 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/. */
+
+#addon-page {
+ background: #FFF;
+ padding: 0;
+}
+
+#addon-scrollbox {
+ overflow: auto;
+ -moz-box-orient: vertical;
+ -moz-box-flex: 1;
+}
+
+#spacer-start {
+ -moz-box-flex: 1;
+}
+
+#spacer-end {
+ -moz-box-flex: 3;
+}
+
+#addon-container {
+ color: #22262F;
+ background: #C7D0D9;
+ border: 1px solid #494F5D;
+ border-radius: 10px;
+ max-width: 600px;
+ margin: 20px;
+ padding: 30px 90px;
+}
+
+#addon-info {
+ -moz-box-align: start;
+ margin: 25px 10px;
+}
+
+#icon {
+ margin-inline-end: 10px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+}
+
+.addon-info[type="theme"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+.addon-info[type="locale"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+.addon-info[type="plugin"] #icon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+.addon-info[type="dictionary"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+#name {
+ font-size: 130%;
+}
+
+#author {
+ font-style: italic;
+}
+
+#location {
+ font-style: italic;
+}
+
+#warning {
+ margin-bottom: 25px;
+ -moz-box-align: start;
+}
+
+#warning-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.png");
+ width: 16px;
+ height: 15px;
+ margin-inline-end: 5px;
+}
+
+#allow {
+ margin-inline-start: 84px;
+ margin-bottom: 20px;
+}
+
+#continuePanel,
+#restartPanel {
+ margin-top: 25px;
+ -moz-box-pack: end;
+ -moz-box-align: center;
+}
+
+#continuePanel {
+ -moz-box-pack: end;
+}
+
+#restartMessage {
+ text-align: right;
+}
+
+#restartSpacer {
+ -moz-box-flex: 1;
+}
+
+#later {
+ font-style: italic;
+}
diff --git a/comm/suite/themes/modern/mozapps/extensions/rating-not-won.png b/comm/suite/themes/modern/mozapps/extensions/rating-not-won.png
new file mode 100644
index 0000000000..2761f19255
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/rating-not-won.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/rating-won.png b/comm/suite/themes/modern/mozapps/extensions/rating-won.png
new file mode 100644
index 0000000000..336dd8f6eb
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/rating-won.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/stripes-error.png b/comm/suite/themes/modern/mozapps/extensions/stripes-error.png
new file mode 100644
index 0000000000..9dce6d0dcd
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/stripes-error.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/stripes-info-negative.png b/comm/suite/themes/modern/mozapps/extensions/stripes-info-negative.png
new file mode 100644
index 0000000000..8315e0fb21
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/stripes-info-negative.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/stripes-info-positive.png b/comm/suite/themes/modern/mozapps/extensions/stripes-info-positive.png
new file mode 100644
index 0000000000..11fee7d689
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/stripes-info-positive.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/stripes-warning.png b/comm/suite/themes/modern/mozapps/extensions/stripes-warning.png
new file mode 100644
index 0000000000..157faf6d2d
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/stripes-warning.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/themeGeneric-16.png b/comm/suite/themes/modern/mozapps/extensions/themeGeneric-16.png
new file mode 100644
index 0000000000..16d77a4a25
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/themeGeneric-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/themeGeneric.png b/comm/suite/themes/modern/mozapps/extensions/themeGeneric.png
new file mode 100644
index 0000000000..39118b15f3
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/themeGeneric.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/extensions/update.css b/comm/suite/themes/modern/mozapps/extensions/update.css
new file mode 100644
index 0000000000..c6cba309c5
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/update.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#alert {
+ list-style-image: url("chrome://mozapps/skin/update/update.png");
+}
+
+.throbber {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+ width: 16px;
+ height: 16px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ margin-inline-start: 5px;
+ margin-inline-end: 2px;
+}
+
+.alertBox {
+ background-color: #FFFFE7;
+ color: #000000;
+ border: 1px outset #FFFFE7;
+ margin-left: 3px;
+ margin-right: 3px;
+ padding: 5px;
+}
diff --git a/comm/suite/themes/modern/mozapps/extensions/utilities.png b/comm/suite/themes/modern/mozapps/extensions/utilities.png
new file mode 100644
index 0000000000..8efc337118
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/extensions/utilities.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/handling/handling.css b/comm/suite/themes/modern/mozapps/handling/handling.css
new file mode 100644
index 0000000000..a6c7d0b15c
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/handling/handling.css
@@ -0,0 +1,34 @@
+/* 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/. */
+
+#description-image:not([src]) {
+ height: 32px;
+ width: 32px;
+}
+
+richlistitem[type] {
+ min-height: 36px; /* Don't forget to update the richlistbox height! */
+ padding-inline-start: 2px;
+ }
+
+richlistitem {
+ -moz-box-align: center;
+}
+
+richlistbox {
+ /* 3 items high, plus 4px for top and bottom margins, less 2px for border */
+ min-height: 110px;
+}
+
+.name {
+ font-weight: bold;
+}
+
+.description {
+ color: #006400;
+}
+
+richlistitem[selected="true"] .description {
+ color: inherit !important;
+}
diff --git a/comm/suite/themes/modern/mozapps/icons/itemDisabledFader.png b/comm/suite/themes/modern/mozapps/icons/itemDisabledFader.png
new file mode 100644
index 0000000000..9da353ce5c
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/icons/itemDisabledFader.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/icons/itemEnabledFader.png b/comm/suite/themes/modern/mozapps/icons/itemEnabledFader.png
new file mode 100644
index 0000000000..12886160ca
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/icons/itemEnabledFader.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/passwordmgr/key-16.png b/comm/suite/themes/modern/mozapps/passwordmgr/key-16.png
new file mode 100644
index 0000000000..c6e0fda650
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/passwordmgr/key-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/passwordmgr/key-64.png b/comm/suite/themes/modern/mozapps/passwordmgr/key-64.png
new file mode 100644
index 0000000000..31f2fbb9e5
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/passwordmgr/key-64.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/passwordmgr/key.png b/comm/suite/themes/modern/mozapps/passwordmgr/key.png
new file mode 100644
index 0000000000..b5e8afefca
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/passwordmgr/key.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/plugins/pluginBlocked-16.png b/comm/suite/themes/modern/mozapps/plugins/pluginBlocked-16.png
new file mode 100644
index 0000000000..d38cfde90f
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/plugins/pluginBlocked-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/plugins/pluginBlocked.png b/comm/suite/themes/modern/mozapps/plugins/pluginBlocked.png
new file mode 100644
index 0000000000..a912cbe2e8
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/plugins/pluginBlocked.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/plugins/pluginGeneric-16.png b/comm/suite/themes/modern/mozapps/plugins/pluginGeneric-16.png
new file mode 100644
index 0000000000..d38cfde90f
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/plugins/pluginGeneric-16.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/plugins/pluginGeneric.png b/comm/suite/themes/modern/mozapps/plugins/pluginGeneric.png
new file mode 100644
index 0000000000..a912cbe2e8
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/plugins/pluginGeneric.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/plugins/pluginInstallerWizard.css b/comm/suite/themes/modern/mozapps/plugins/pluginInstallerWizard.css
new file mode 100644
index 0000000000..6db976c01d
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/plugins/pluginInstallerWizard.css
@@ -0,0 +1,10 @@
+/* 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/. */
+
+#licenseContainer, #pluginList {
+ margin: 2px 4px;
+ border: 1px solid #5D616E;
+ background-color: #FFFFFF;
+ color: #000000;
+}
diff --git a/comm/suite/themes/modern/mozapps/update/update.png b/comm/suite/themes/modern/mozapps/update/update.png
new file mode 100644
index 0000000000..e2737058f8
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/update/update.png
Binary files differ
diff --git a/comm/suite/themes/modern/mozapps/update/updates.css b/comm/suite/themes/modern/mozapps/update/updates.css
new file mode 100644
index 0000000000..69cccd75c2
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/update/updates.css
@@ -0,0 +1,168 @@
+/* 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/. */
+
+/**
+ * General
+ */
+wizardpage {
+ height: 360px;
+ width: 700px;
+}
+
+#updates, .wizard-page-box {
+ margin: 0;
+ padding: 0;
+}
+
+.update-header {
+ padding: 0px 10px;
+}
+
+.update-content {
+ padding: 10px;
+}
+
+/**
+ * License Page
+ */
+.loadingBox {
+ padding: 3px 5px;
+}
+
+.remoteLoadingThrobber {
+ margin-top: 3px;
+}
+
+.remoteLoadingThrobber[state="loading"] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.remoteLoadingThrobber[state="error"] {
+ list-style-image: url("chrome://global/skin/icons/notfound.png");
+}
+
+/**
+ * Update Found Page
+ */
+.wizard-buttons-separator {
+ margin-top: 0 !important;
+}
+
+/* Update Found Basic Page */
+#updateName, #updateFinishedName {
+ font-weight: bold;
+ font-size: 120%;
+}
+
+/* License Page */
+#licenseContent {
+ margin: 2px 4px;
+ border: 1px solid #5D616E;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+/**
+ * Downloading Page
+ */
+#downloadStatusLine {
+ -moz-box-align: center;
+}
+
+#downloadStatus {
+ height: 3em !important;
+}
+
+#downloadStatusProgress {
+ padding-inline-end: 5px;
+}
+
+#pauseButton > .button-box > .button-icon {
+ margin-inline-start: 0;
+}
+
+#pauseButton > .button-box > .button-text {
+ margin-inline-start: 0 !important;
+ margin-inline-end: 0 !important;
+}
+
+#pauseButton {
+ min-width: 0;
+ min-height: 0;
+ color: #000000 !important;
+ background-color: transparent !important;
+ list-style-image: url("chrome://mozapps/skin/icons/buttons.png");
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+ margin: 0;
+ border: 1px solid !important;
+ border-top-colors: transparent !important;
+ border-right-colors: transparent !important;
+ border-bottom-colors: transparent !important;
+ border-left-colors: transparent !important;
+ border-radius: 0 !important;
+ outline: none !important;
+}
+
+#pauseButton:focus {
+ border-style: dotted !important;
+ border-color: #000000 !important;
+}
+
+#pauseButton[disabled="true"] {
+ -moz-image-region: rect(16px, 48px, 32px, 32px);
+}
+
+#pauseButton[paused="true"] {
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+#pauseButton[paused="true"][disabled="true"] {
+ -moz-image-region: rect(16px, 16px, 32px, 0px);
+}
+
+#verificationFailedIcon {
+ margin-inline-start: 5px;
+ list-style-image: url("chrome://global/skin/icons/notfound.png");
+}
+
+/**
+ * Error Page
+ */
+#errorReason {
+ margin-top: 1px;
+ margin-bottom: 2px;
+ margin-inline-start: 6px !important;
+ margin-inline-end: 5px;
+ font-weight: bold;
+}
+
+/**
+ * Update History Window
+ */
+update {
+ border-bottom: 1px solid #90A1B3;
+}
+
+.update-name {
+ font-weight: bold;
+}
+
+.update-label-column {
+ -moz-box-align: end;
+}
+
+.update-type {
+ font-weight: bold;
+ color: #990000;
+}
+
+#historyItems {
+ height: 200px;
+ margin: 1px 5px 4px;
+ border: 1px solid #5D616E;
+}
+
+#historyItems > scrollbox {
+ margin-bottom: 1px;
+}
diff --git a/comm/suite/themes/modern/mozapps/viewsource/viewsource.css b/comm/suite/themes/modern/mozapps/viewsource/viewsource.css
new file mode 100644
index 0000000000..76c7d00b9d
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/viewsource/viewsource.css
@@ -0,0 +1,5 @@
+/* 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/. */
+
+/* This is for styling the menus of the viewsource window */
diff --git a/comm/suite/themes/modern/mozapps/xpinstall/xpinstallConfirm.css b/comm/suite/themes/modern/mozapps/xpinstall/xpinstallConfirm.css
new file mode 100644
index 0000000000..0188d345a5
--- /dev/null
+++ b/comm/suite/themes/modern/mozapps/xpinstall/xpinstallConfirm.css
@@ -0,0 +1,91 @@
+/* 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/. */
+
+#xpinstallheader {
+ margin-bottom: 2em;
+}
+
+#itemList {
+ margin-top: 2px;
+ margin-bottom: 10px;
+ height: 14em;
+ border: 1px solid #5D616E;
+ background-color: #FFFFFF;
+ color: #000000;
+}
+
+#itemWarningIntro {
+ margin-inline-start: 8px;
+}
+
+#dialogContentBox {
+ padding: 5px;
+}
+
+installitem {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-inline-start: 5px;
+ padding-inline-end: 0;
+ border-bottom: 1px solid #A5ABC0;
+ margin-bottom: 5px;
+}
+
+.alert-icon {
+ margin-inline-end: 20px;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 1.25em;
+ margin-bottom: 1em;
+}
+
+.xpinstallIconContainer {
+ width: 32px;
+ height: 32px;
+ margin-inline-end: 5px;
+}
+
+.xpinstallItemName {
+ font-weight: bold;
+}
+
+.xpinstallItemSigned {
+ font-style: italic;
+ font-size: 0.9em;
+}
+
+.xpinstallItemURL {
+ border: none;
+ padding: 0;
+ background-color: #FFFFFF;
+ color: #000000;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ margin-inline-start: 6px;
+ margin-inline-end: 5px;
+}
+
+.xpinstallItemIcon {
+ max-width: 32px;
+ max-height: 32px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+}
+
+installitem[type="theme"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+installitem[type="locale"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+installitem[type="plugin"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+installitem[type="dictionary"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
diff --git a/comm/suite/themes/modern/navigator/btn1/feeds.png b/comm/suite/themes/modern/navigator/btn1/feeds.png
new file mode 100644
index 0000000000..61853ac262
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/feeds.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/first-dis.png b/comm/suite/themes/modern/navigator/btn1/first-dis.png
new file mode 100644
index 0000000000..c9bbb8e759
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/first-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/first-hov.png b/comm/suite/themes/modern/navigator/btn1/first-hov.png
new file mode 100644
index 0000000000..e5bd045c82
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/first-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/first.png b/comm/suite/themes/modern/navigator/btn1/first.png
new file mode 100644
index 0000000000..bdfca7c38e
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/first.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/last-dis.png b/comm/suite/themes/modern/navigator/btn1/last-dis.png
new file mode 100644
index 0000000000..c67a320d22
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/last-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/last-hov.png b/comm/suite/themes/modern/navigator/btn1/last-hov.png
new file mode 100644
index 0000000000..e97442907c
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/last-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/last.png b/comm/suite/themes/modern/navigator/btn1/last.png
new file mode 100644
index 0000000000..769aaf1cae
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/last.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/next-dis.png b/comm/suite/themes/modern/navigator/btn1/next-dis.png
new file mode 100644
index 0000000000..d2a6bfc98f
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/next-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/next-hov.png b/comm/suite/themes/modern/navigator/btn1/next-hov.png
new file mode 100644
index 0000000000..b9962372d0
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/next-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/next.png b/comm/suite/themes/modern/navigator/btn1/next.png
new file mode 100644
index 0000000000..6563b91dec
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/next.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/previous-dis.png b/comm/suite/themes/modern/navigator/btn1/previous-dis.png
new file mode 100644
index 0000000000..cf7ba06e8a
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/previous-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/previous-hov.png b/comm/suite/themes/modern/navigator/btn1/previous-hov.png
new file mode 100644
index 0000000000..512c3582d8
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/previous-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/previous.png b/comm/suite/themes/modern/navigator/btn1/previous.png
new file mode 100644
index 0000000000..fcdc6a8896
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/previous.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/top-dis.png b/comm/suite/themes/modern/navigator/btn1/top-dis.png
new file mode 100644
index 0000000000..86dbe1b80f
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/top-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/top-hov.png b/comm/suite/themes/modern/navigator/btn1/top-hov.png
new file mode 100644
index 0000000000..dce156271b
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/top-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/top.png b/comm/suite/themes/modern/navigator/btn1/top.png
new file mode 100644
index 0000000000..d750c751ec
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/top.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/up-dis.png b/comm/suite/themes/modern/navigator/btn1/up-dis.png
new file mode 100644
index 0000000000..31bab499da
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/up-dis.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/up-hov.png b/comm/suite/themes/modern/navigator/btn1/up-hov.png
new file mode 100644
index 0000000000..721a044e95
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/up-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/btn1/up.png b/comm/suite/themes/modern/navigator/btn1/up.png
new file mode 100644
index 0000000000..2405a46181
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/btn1/up.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/browser-small.png b/comm/suite/themes/modern/navigator/icons/browser-small.png
new file mode 100644
index 0000000000..0845512968
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/browser-small.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/browser.png b/comm/suite/themes/modern/navigator/icons/browser.png
new file mode 100644
index 0000000000..ae73bf56d2
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/browser.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/identity.png b/comm/suite/themes/modern/navigator/icons/identity.png
new file mode 100644
index 0000000000..35b376f30d
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/identity.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/popup-blocked.png b/comm/suite/themes/modern/navigator/icons/popup-blocked.png
new file mode 100644
index 0000000000..6d32ce1697
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/popup-blocked.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/tab-drag-indicator.png b/comm/suite/themes/modern/navigator/icons/tab-drag-indicator.png
new file mode 100644
index 0000000000..60a3a21b2f
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/tab-drag-indicator.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/tab-new-act.png b/comm/suite/themes/modern/navigator/icons/tab-new-act.png
new file mode 100644
index 0000000000..b88743a3fe
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/tab-new-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/tab-new-hov.png b/comm/suite/themes/modern/navigator/icons/tab-new-hov.png
new file mode 100644
index 0000000000..c48a3bc247
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/tab-new-hov.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/tab-new.png b/comm/suite/themes/modern/navigator/icons/tab-new.png
new file mode 100644
index 0000000000..93bebb8c61
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/tab-new.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/icons/windowcontrols.png b/comm/suite/themes/modern/navigator/icons/windowcontrols.png
new file mode 100644
index 0000000000..63a4317b88
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/icons/windowcontrols.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/linkToolbar.css b/comm/suite/themes/modern/navigator/linkToolbar.css
new file mode 100644
index 0000000000..6fccf75d2f
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/linkToolbar.css
@@ -0,0 +1,112 @@
+/* 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/. */
+
+/* ::::: Link toolbar items ::::: */
+
+#link-top {
+ list-style-image: url("chrome://navigator/skin/btn1/top.png");
+}
+
+#link-top[disabled="true"],
+#link-top[disabled="true"]:hover,
+#link-top[disabled="true"]:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/top-dis.png") !important;
+}
+
+#link-top:hover,
+#link-top:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/top-hov.png");
+}
+
+#link-up {
+ list-style-image: url("chrome://navigator/skin/btn1/up.png");
+}
+
+#link-up[disabled="true"],
+#link-up[disabled="true"]:hover,
+#link-up[disabled="true"]:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/up-dis.png") !important;
+}
+
+#link-up:hover,
+#link-up:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/up-hov.png");
+}
+
+#link-first {
+ list-style-image: url("chrome://navigator/skin/btn1/first.png");
+}
+
+#link-first[disabled="true"],
+#link-first[disabled="true"]:hover,
+#link-first[disabled="true"]:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/first-dis.png") !important;
+}
+
+#link-first:hover,
+#link-first:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/first-hov.png");
+}
+
+#link-prev {
+ list-style-image: url("chrome://navigator/skin/btn1/previous.png");
+}
+
+#link-prev[disabled="true"],
+#link-prev[disabled="true"]:hover,
+#link-prev[disabled="true"]:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/previous-dis.png") !important;
+}
+
+#link-prev:hover,
+#link-prev:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/previous-hov.png");
+}
+
+#link-next {
+ list-style-image: url("chrome://navigator/skin/btn1/next.png");
+}
+
+#link-next[disabled="true"],
+#link-next[disabled="true"]:hover,
+#link-next[disabled="true"]:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/next-dis.png") !important;
+}
+
+#link-next:hover,
+#link-next:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/next-hov.png");
+}
+
+#link-last {
+ list-style-image: url("chrome://navigator/skin/btn1/last.png");
+}
+
+#link-last[disabled="true"],
+#link-last[disabled="true"]:hover,
+#link-last[disabled="true"]:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/last-dis.png") !important;
+}
+
+#link-last:hover,
+#link-last:hover:active {
+ list-style-image: url("chrome://navigator/skin/btn1/last-hov.png");
+}
+
+#link-feed {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png") !important;
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#link-feed:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#link-feed[disabled="true"] {
+ -moz-image-region: rect(32px 32px 48px 16px);
+}
+
+#link-feed[open="true"] {
+ -moz-image-region: rect(48px 32px 64px 16px);
+}
diff --git a/comm/suite/themes/modern/navigator/navigator.css b/comm/suite/themes/modern/navigator/navigator.css
new file mode 100644
index 0000000000..51b7d5b73c
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/navigator.css
@@ -0,0 +1,936 @@
+/* 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/. */
+
+@import url("chrome://navigator/content/navigator.css");
+@import url("chrome://communicator/skin/");
+@import url("chrome://communicator/skin/places/bookmarksToolbar.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: primary toolbar buttons ::::: */
+
+.toolbarbutton-1 {
+ min-width: 0px;
+}
+
+toolbox {
+ border-bottom: none;
+}
+
+#appcontent {
+ border-top: 1px solid #494F5D;
+}
+
+#back-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+ -moz-image-region: rect(78px 42px 117px 0);
+}
+
+#back-button:hover {
+ -moz-image-region: rect(78px 84px 117px 42px);
+}
+
+#back-button:hover:active {
+ -moz-image-region: rect(78px 126px 117px 84px);
+}
+
+#back-button[disabled="true"] {
+ -moz-image-region: rect(78px 168px 117px 126px) !important;
+}
+
+#forward-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+ -moz-image-region: rect(117px 42px 156px 0);
+}
+
+#forward-button:hover {
+ -moz-image-region: rect(117px 84px 156px 42px);
+}
+
+#forward-button:hover:active {
+ -moz-image-region: rect(117px 126px 156px 84px);
+}
+
+#forward-button[disabled="true"] {
+ -moz-image-region: rect(117px 168px 156px 126px) !important;
+}
+
+#reload-button {
+ list-style-image: url("chrome://navigator/skin/icons/browser.png");
+ -moz-image-region: rect(0px 42px 39px 0);
+}
+
+#reload-button:hover {
+ -moz-image-region: rect(0px 84px 39px 42px);
+}
+
+#reload-button:hover:active {
+ -moz-image-region: rect(0px 126px 39px 84px);
+}
+
+#reload-button[disabled="true"] {
+ -moz-image-region: rect(0px 168px 39px 126px) !important;
+}
+
+#stop-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+ -moz-image-region: rect(39px 42px 78px 0);
+}
+
+#stop-button:hover {
+ -moz-image-region: rect(39px 84px 78px 42px);
+}
+
+#stop-button:hover:active {
+ -moz-image-region: rect(39px 126px 78px 84px);
+}
+
+#stop-button[disabled="true"] {
+ -moz-image-region: rect(39px 168px 78px 126px) !important;
+}
+
+#home-button {
+ list-style-image: url("chrome://communicator/skin/icons/common.png");
+ -moz-image-region: rect(156px 42px 195px 0);
+}
+
+#home-button:hover {
+ -moz-image-region: rect(156px 84px 195px 42px);
+}
+
+#home-button:hover:active {
+ -moz-image-region: rect(156px 126px 195px 84px);
+}
+
+#home-button[disabled="true"] {
+ -moz-image-region: rect(156px 168px 195px 126px) !important;
+}
+
+#sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32.png");
+}
+
+#sync-button > .toolbarbutton-icon {
+ margin-top: 5px;
+ margin-bottom: 4px;
+ margin-inline-start: 5px;
+ margin-inline-end: 7px;
+}
+
+#sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-32-throbber.png");
+}
+
+/* ::::: small primary toolbar buttons ::::: */
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #back-button,
+toolbar[iconsize="small"] > #back-button {
+ list-style-image: url("chrome://communicator/skin/icons/common-small.png");
+ -moz-image-region: rect(38px 19px 57px 0);
+}
+
+toolbar[iconsize="small"] > #back-button:hover {
+ -moz-image-region: rect(38px 38px 57px 19px);
+}
+
+toolbar[iconsize="small"] > #back-button:hover:active {
+ -moz-image-region: rect(38px 57px 57px 38px);
+}
+
+toolbar[iconsize="small"] > #back-button[disabled="true"] {
+ -moz-image-region: rect(38px 76px 57px 57px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #forward-button,
+toolbar[iconsize="small"] > #forward-button {
+ list-style-image: url("chrome://communicator/skin/icons/common-small.png");
+ -moz-image-region: rect(57px 19px 76px 0);
+}
+
+toolbar[iconsize="small"] > #forward-button:hover {
+ -moz-image-region: rect(57px 38px 76px 19px);
+}
+
+toolbar[iconsize="small"] > #forward-button:hover:active {
+ -moz-image-region: rect(57px 57px 76px 38px);
+}
+
+toolbar[iconsize="small"] > #forward-button[disabled="true"] {
+ -moz-image-region: rect(57px 76px 76px 57px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #reload-button,
+toolbar[iconsize="small"] > #reload-button {
+ list-style-image: url("chrome://navigator/skin/icons/browser-small.png");
+ -moz-image-region: rect(0 19px 19px 0);
+}
+
+toolbar[iconsize="small"] > #reload-button:hover {
+ -moz-image-region: rect(0 38px 19px 19px);
+}
+
+toolbar[iconsize="small"] > #reload-button:hover:active {
+ -moz-image-region: rect(0 57px 19px 38px);
+}
+
+toolbar[iconsize="small"] > #reload-button[disabled="true"] {
+ -moz-image-region: rect(0 76px 19px 57px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #stop-button,
+toolbar[iconsize="small"] > #stop-button {
+ list-style-image: url("chrome://communicator/skin/icons/common-small.png");
+ -moz-image-region: rect(19px 19px 38px 0);
+}
+
+toolbar[iconsize="small"] > #stop-button:hover {
+ -moz-image-region: rect(19px 38px 38px 19px);
+}
+
+toolbar[iconsize="small"] > #stop-button:hover:active {
+ -moz-image-region: rect(19px 57px 38px 38px);
+}
+
+toolbar[iconsize="small"] > #stop-button[disabled="true"] {
+ -moz-image-region: rect(19px 76px 38px 57px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #home-button,
+toolbar[iconsize="small"] > #home-button {
+ list-style-image: url("chrome://communicator/skin/icons/common-small.png");
+ -moz-image-region: rect(76px 19px 95px 0);
+}
+
+toolbar[iconsize="small"] > #home-button:hover {
+ -moz-image-region: rect(76px 38px 95px 19px);
+}
+
+toolbar[iconsize="small"] > #home-button:hover:active {
+ -moz-image-region: rect(76px 57px 95px 38px);
+}
+
+toolbar[iconsize="small"] > #home-button[disabled="true"] {
+ -moz-image-region: rect(76px 76px 95px 57px) !important;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button,
+toolbar[iconsize="small"] > #sync-button {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16.png");
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button > .toolbarbutton-icon,
+toolbar[iconsize="small"] > #sync-button > .toolbarbutton-icon {
+ margin-top: 2px;
+ margin-bottom: 3px;
+ margin-inline-start: 3px;
+ margin-inline-end: 5px;
+}
+
+toolbar[iconsize="small"] > toolbarpaletteitem > #sync-button[status=active],
+toolbar[iconsize="small"] > #sync-button[status=active] {
+ list-style-image: url("chrome://communicator/skin/sync/sync-16-throbber.png");
+}
+
+#nav-bar[iconsize="small"] {
+ min-height: 0;
+}
+
+/* ::::: fullscreen window controls ::::: */
+
+#window-controls {
+ -moz-box-align: center;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-inline-start: 4px;
+ padding-inline-end: 2px;
+ background-color: #A9B2BD;
+ border-left: 1px solid #B6BEC5;
+}
+
+#window-controls > toolbarbutton {
+ list-style-image: url("chrome://navigator/skin/icons/windowcontrols.png");
+ min-width: 0;
+ border: none;
+}
+
+toolbar[mode="text"] > #window-controls > toolbarbutton > .toolbarbutton-icon {
+ display: -moz-box;
+}
+
+#minimize-button {
+ -moz-image-region: rect(16px 15px 32px 0);
+}
+
+#minimize-button:hover {
+ -moz-image-region: rect(16px 30px 32px 15px);
+}
+
+#minimize-button:hover:active {
+ -moz-image-region: rect(16px 45px 32px 30px);
+}
+
+#minimize-button[disabled="true"] {
+ -moz-image-region: rect(16px 60px 32px 45px) !important;
+}
+
+#restore-button {
+ -moz-image-region: rect(0 15px 16px 0);
+}
+
+#restore-button:hover {
+ -moz-image-region: rect(0 30px 16px 15px);
+}
+
+#restore-button:hover:active {
+ -moz-image-region: rect(0 45px 16px 30px);
+}
+
+#restore-button[disabled="true"] {
+ -moz-image-region: rect(0 60px 16px 45px) !important;
+}
+
+#close-button {
+ -moz-image-region: rect(32px 15px 48px 0);
+}
+
+#close-button:hover {
+ -moz-image-region: rect(32px 30px 48px 15px);
+}
+
+#close-button:hover:active {
+ -moz-image-region: rect(32px 45px 48px 30px);
+}
+
+#close-button[disabled="true"] {
+ -moz-image-region: rect(32px 60px 48px 45px) !important;
+}
+
+/* ::::: special menubutton dropmarkers ::::: */
+
+/* ..... dropmarker box ..... */
+
+#back-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+#forward-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker
+{
+ margin-top: 20px;
+ margin-bottom: 0px;
+ margin-inline-start: 34px;
+ margin-inline-end: 0px;
+}
+
+#print-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker
+{
+ margin-top: 20px;
+ margin-bottom: 0px;
+ margin-inline-start: 40px;
+ margin-inline-end: 0px;
+}
+
+toolbar[mode="icons"] #back-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+toolbar[mode="icons"] #forward-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker
+{
+ margin-top: 30px;
+ margin-bottom: 0px;
+ margin-inline-start: 34px;
+ margin-inline-end: 0px;
+}
+
+toolbar[iconsize=small] #back-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+toolbar[iconsize=small] #forward-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+toolbar[iconsize=small] #print-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker
+{
+ margin-top: 8px;
+ margin-bottom: 8px;
+ margin-inline-start: 14px;
+ margin-inline-end: 0px;
+}
+
+toolbar[iconsize=small][mode="icons"] #back-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+toolbar[iconsize=small][mode="icons"] #forward-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker,
+toolbar[iconsize=small][mode="icons"] #print-button > .toolbarbutton-menubutton-stack
+ > .toolbarbutton-menubutton-dropmarker
+{
+ margin-top: 8px;
+ margin-bottom: 0px;
+ margin-inline-start: 14px;
+ margin-inline-end: 0px;
+}
+
+/* ::::: nav-bar - the navigator primary toolbar ::::: */
+
+.toolbar-primary-icon {
+ display: none;
+}
+
+.toolbar-primary-holder {
+ -moz-box-align: start;
+ background-image: url("chrome://communicator/skin/toolbar/prtb-bg-noline.png");
+}
+
+/* ::::: the grooved area around the urlbar and associated buttons ::::: */
+
+.nav-bar-class {
+ -moz-box-align: center;
+ margin-top: 7px;
+ margin-bottom: 3px;
+ border-top: 1px solid;
+ border-bottom: 1px solid;
+ border-top-color: #A2AFBD;
+ border-right-color: #A2AFBD;
+ border-bottom-color: #939EAA;
+ border-left-color: #9FABB9;
+ padding: 0px;
+ min-width: 0px;
+}
+
+.nav-bar-first,
+toolbarpaletteitem > .nav-bar-class {
+ margin-inline-start: 5px;
+ border-inline-start: 2px solid #D2DAE1;
+ padding-inline-start: 3px;
+}
+
+.nav-bar-last,
+toolbarpaletteitem > .nav-bar-class {
+ margin-inline-end: 0px;
+ border-inline-end: 2px solid #A2AFBD;
+ padding-inline-end: 3px;
+}
+
+.nav-bar-last:-moz-locale-dir(rtl),
+.nav-bar-first:-moz-locale-dir(ltr),
+toolbarpaletteitem > .nav-bar-class:-moz-locale-dir(rtl),
+toolbarpaletteitem > .nav-bar-class:-moz-locale-dir(ltr) {
+ border-bottom-left-radius: 3px;
+ border-top-left-radius: 3px;
+}
+
+.nav-bar-last:-moz-locale-dir(ltr),
+.nav-bar-first:-moz-locale-dir(rtl),
+toolbarpaletteitem > .nav-bar-class:-moz-locale-dir(ltr),
+toolbarpaletteitem > .nav-bar-class:-moz-locale-dir(rtl) {
+ border-bottom-right-radius: 3px;
+ border-top-right-radius: 3px;
+}
+
+toolbar[mode="text"] > .nav-bar-class,
+toolbar[iconsize="small"] > .nav-bar-class,
+toolbar[mode="text"] > toolbarpaletteitem > .nav-bar-class,
+toolbar[iconsize="small"] > toolbarpaletteitem > .nav-bar-class {
+ margin: 0 !important;
+ padding: 0 !important;
+ border: none !important;
+}
+
+toolbar[mode="text"] #search-button {
+ margin-inline-end: 0px;
+}
+
+#search-button > .button-box > .button-icon {
+ display: -moz-box;
+}
+
+toolbar[mode="text"] #search-button > .button-box > .button-icon,
+toolbar[mode="icons"] #search-button > .button-box > .button-text {
+ display: none;
+}
+
+#wrapper-nav-bar-inner[place="palette"] > #nav-bar-inner
+ > .button-toolbar,
+#wrapper-nav-bar-inner[place="palette"] > #nav-bar-inner
+ > #urlbar > .urlbar-icons {
+ display: none;
+}
+
+/* ::::: urlbar - the url textbox ::::: */
+
+.searchbar-textbox,
+#urlbar {
+ margin-top: 2px;
+ margin-bottom: 2px;
+ margin-inline-start: 3px;
+ margin-inline-end: 6px;
+ padding: 3px;
+ border: 1px solid #000000;
+ background-color: #EDF5F7;
+ box-shadow: inset 0px -2px #E9F3F6;
+}
+
+#urlbar[level="high"] {
+ background-color: #E8DB99;
+ box-shadow: none;
+}
+
+#urlbar-search-splitter {
+ min-width: 6px;
+ margin: 0 -3px;
+ border: none;
+ background-color: transparent;
+}
+
+/* ::::: notification popups ::::: */
+
+.popup-notification-icon {
+ width: 64px;
+ height: 64px;
+ margin-inline-end: 10px;
+}
+
+.popup-notification-icon[popupid="geolocation"] {
+ list-style-image: url("chrome://communicator/skin/icons/geolocation-64.png");
+}
+
+.popup-notification-icon[popupid="persistent-storage"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-icons.svg#persistent-storage");
+}
+
+.popup-notification-icon[popupid="web-notifications"] {
+ list-style-image: url("chrome://communicator/skin/icons/notification-64.png");
+}
+
+.popup-notification-icon[popupid="addon-install-disabled"],
+.popup-notification-icon[popupid="addon-install-blocked"],
+.popup-notification-icon[popupid="addon-install-started"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
+.popup-notification-icon[popupid="addon-install-failed"],
+.popup-notification-icon[popupid="addon-install-complete"],
+.popup-notification-icon[popupid="lwtheme-install-request"],
+.popup-notification-icon[popupid="lwtheme-install-notification"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ width: 32px;
+ height: 32px;
+}
+
+.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
+.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
+ list-style-image: url("chrome://global/skin/icons/question-64.png");
+}
+
+.popup-notification-icon[popupid="password"] {
+ list-style-image: url("chrome://mozapps/skin/passwordmgr/key-64.png");
+}
+
+.addon-progress-description {
+ width: 350px;
+ max-width: 350px;
+}
+
+/* Notification icon box */
+#notification-popup-box {
+ margin-inline-end: 3px;
+}
+
+.notification-anchor-icon:-moz-focusring {
+ outline: 1px dotted;
+}
+
+#default-notification-icon {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#geo-notification-icon {
+ list-style-image: url("chrome://communicator/skin/icons/geolocation-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#web-notifications-notification-icon {
+ width: 16px;
+ height: 16px;
+ list-style-image: url("chrome://communicator/skin/icons/notification-16.png");
+}
+
+#addons-notification-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#indexedDB-notification-icon {
+ list-style-image: url("chrome://global/skin/icons/question-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+#password-notification-icon {
+ list-style-image: url("chrome://mozapps/skin/passwordmgr/key-16.png");
+ width: 16px;
+ height: 16px;
+}
+
+.center-item-box {
+ padding: 8px 16px 0px 16px;
+}
+
+.center-item-box[padbottom="true"] {
+ padding-bottom: 8px;
+}
+
+.center-item-icon {
+ background-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+ background-repeat: no-repeat;
+ height: 16px;
+ width: 16px;
+ margin-bottom: 4px;
+}
+
+.center-item-box[warn="true"] {
+ background-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+ background-repeat: repeat-x;
+ padding: 8px 16px 6px 16px;
+}
+
+.center-item-box[padbottom="true"][warn="true"] {
+ padding-bottom: 4px;
+}
+
+.center-item-box[showseparator="true"] {
+ border-top: 1px solid #B1BBC5;
+}
+
+.center-item-box[warn="false"] > .center-item-warning {
+ display: none;
+}
+
+.center-item-warning > .text-link[href=""] {
+ display: none;
+}
+
+.center-item-warning-icon {
+ background-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+ background-repeat: no-repeat;
+ width: 16px;
+ height: 15px;
+ margin-bottom: 4px;
+}
+
+.center-item-warning-description {
+ color: #828282;
+}
+
+.center-item-button {
+ min-width: 0px;
+}
+
+/* ::::: page proxy icon ::::: */
+
+#page-proxy-deck,
+#page-proxy-button,
+#page-proxy-favicon {
+ width: 16px;
+ height: 16px;
+}
+
+#page-proxy-deck {
+ cursor: grab;
+ margin-inline-end: 4px;
+}
+
+#page-proxy-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+#page-proxy-favicon {
+ list-style-image: none;
+}
+
+#page-proxy-button[pageproxystate="invalid"] {
+ cursor: default;
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item-dis.png");
+}
+
+/* ::::: autocomplete ::::: */
+
+#PopupAutoComplete > richlistbox > richlistitem {
+ height: 20px;
+ min-height: 20px;
+ border: 0;
+ border-radius: 0;
+ padding: 0px 1px 0px 1px;
+}
+
+#PopupAutoComplete > richlistbox > richlistitem > .ac-title {
+ font: icon;
+ margin-inline-start: 6px;
+}
+
+#PopupAutoComplete > richlistbox {
+ padding: 0;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(value) {
+ padding-inline-start: 15px;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(comment) {
+ color: #555566;
+}
+
+.autocomplete-search-box {
+ border-top: 2px groove #DDE3EB;
+ background-color: #DDE3EB;
+}
+
+panel[nomatch="true"] > .autocomplete-search-box {
+ border-top: 1px solid #E4EAEF;
+}
+
+.autocomplete-search-engine {
+ padding: 2px;
+}
+
+.autocomplete-search-engine[menuactive="true"] {
+ background-color: #9499AC;
+ color: #FFFFFF;
+}
+
+.autocomplete-search-engine-img {
+ margin-inline-end: 4px;
+ width: 16px;
+ height: 16px;
+}
+
+.autocomplete-history-dropmarker {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 3px;
+ list-style-image: url("chrome://navigator/skin/toolbar/ubhist-arrow.png");
+}
+
+.autocomplete-history-dropmarker[open="true"] {
+ list-style-image: url("chrome://navigator/skin/toolbar/ubhist-arrow-act.png");
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
+ color: #999999;
+}
+
+/* ::::: go and searchbuttons ::::: */
+
+#go-button,
+#search-button {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-inline-start: 0px;
+ margin-inline-end: 4px;
+ min-width: 0px;
+ font: message-box;
+ font-weight: bold;
+}
+
+#search-button {
+ list-style-image: url("chrome://global/skin/icons/search.png");
+}
+
+/* ::::: navigator throbber ::::: */
+
+.toolbar-primary #throbber-box {
+ margin: 0px 0px 2px;
+}
+
+/* ::::: personal toolbar ::::: */
+
+#PersonalToolbar {
+ background: url("chrome://global/skin/toolbar/tb-mid.png") #C7D0D9 repeat-x top;
+}
+
+#PersonalToolbar > .toolbar-box > toolbargrippy,
+#PersonalToolbar > .toolbar-box > .toolbar-holder {
+ border-top: 1px solid #CED6DD;
+ border-right: 1px solid #95A0AD;
+ border-bottom: 1px solid #95A0AD;
+ border-left: 1px solid #DAE3ED;
+}
+
+#PersonalToolbar > .toolbar-box > toolbargrippy:hover:active {
+ border-color: #67737E;
+}
+
+toolbarbutton.chevron {
+ list-style-image: url("chrome://global/skin/toolbar/chevron.png") !important;
+}
+
+toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
+ display: none;
+}
+
+toolbarbutton.chevron > .toolbarbutton-text {
+ display: none; /* hide chevron label which has a width even if blank */
+}
+
+toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
+ display: -moz-box; /* display chevron icon in text mode */
+}
+
+/* Prevent [mode="icons"|"text"] from hiding the label and icon */
+#PlacesToolbarItems .bookmark-item > .toolbarbutton-text,
+#PlacesToolbarItems .bookmark-item > .toolbarbutton-icon {
+ display: -moz-box !important;
+}
+
+#PersonalToolbar[iconsize="small"] > toolbarpaletteitem > #home-button > .toolbarbutton-icon,
+#PersonalToolbar[iconsize="small"] > #home-button > .toolbarbutton-icon {
+ width: 16px;
+ height: 16px;
+}
+
+#PersonalToolbar > #home-button {
+ cursor: pointer;
+}
+
+#PersonalToolbar > #home-button:hover {
+ text-decoration: underline;
+}
+
+#PersonalToolbar > #home-button[disabled="true"] {
+ cursor: default !important;
+ text-decoration: none !important;
+}
+
+/*
+ In customize mode we hide the normal bookmark items and show a placeholder
+ for the drag/drop UI.
+*/
+.bookmarks-toolbar-customize {
+ list-style-image: url("chrome://communicator/skin/places/bookmarksToolbar.png");
+ display: none;
+ max-width: 15em !important;
+}
+
+#wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
+ width: 16px;
+ height: 16px;
+ background: url("chrome://communicator/skin/places/bookmarksToolbar.png") no-repeat;
+}
+
+/* ::::: content area ::::: */
+
+#content {
+ border-bottom: 1px solid #494F5D;
+}
+
+#status-bar {
+ border-top: none;
+ min-width: 1px;
+}
+
+#security-button[level="high"] > .statusbarpanel-contentbox {
+ background-color: #B4CD32;
+}
+
+#security-button[level="broken"] > .statusbarpanel-contentbox {
+ background-color: #E83404;
+}
+
+#security-button[label] > .statusbarpanel-contentbox {
+ background-color: #62C441;
+}
+
+#security-button > .statusbarpanel-contentbox > .statusbarpanel-text {
+ margin: 0px;
+ color: #FFFFFF;
+}
+
+#ev-button {
+ list-style-image: url("chrome://communicator/skin/icons/identity.png");
+}
+
+#popupIcon {
+ list-style-image: url("chrome://navigator/skin/icons/popup-blocked.png");
+}
+
+#invalid-form-popup {
+ border: 1px solid #000000;
+ padding: 2px 3px 0px 3px;
+ max-width: 40em;
+ background-color: #FFFFE7;
+ color: #000000;
+ font: message-box;
+ font-weight: bold;
+}
+
+/* ::::: star button ::::: */
+
+#star-button {
+ list-style-image: url("chrome://communicator/skin/places/bookmark.png");
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+#star-button:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+#star-button:hover:active {
+ -moz-image-region: rect(16px 48px 32px 32px);
+}
+
+#star-button[starred="true"] {
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#star-button[starred="true"]:hover {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#star-button[starred="true"]:hover:active {
+ -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+#editBookmarkPanelStarIcon {
+ list-style-image: url("chrome://communicator/skin/places/bookmark.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+/* ::::: feeds ::::: */
+
+.feedsMenu {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+.feedsMenu[_moz-menuactive="true"] {
+ -moz-image-region: rect(16px 16px 32px 0px);
+}
+
+.feedsMenu[disabled="true"] {
+ -moz-image-region: rect(32px 16px 48px 0px);
+}
+
+.feedsMenu[open="true"] {
+ -moz-image-region: rect(48px 16px 64px 0px);
+}
+
+#feedsButton {
+ list-style-image: url("chrome://navigator/skin/btn1/feeds.png");
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#feedsButton:hover {
+ -moz-image-region: rect(16px 32px 32px 16px);
+}
+
+/* Need to set fixed width to stop the zoom display from changing size and moving around the zoom buttons */
+#zoomLevel-display {
+ width: 60px;
+ max-width: 60px;
+ margin-left: 0px;
+ margin-right: 0px;
+}
+
+.zoom-button-align{
+ padding-bottom: 3px;
+}
diff --git a/comm/suite/themes/modern/navigator/pageInfo.css b/comm/suite/themes/modern/navigator/pageInfo.css
new file mode 100644
index 0000000000..2406f9aab9
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/pageInfo.css
@@ -0,0 +1,122 @@
+/* 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/. */
+
+@import "chrome://global/skin/global.css";
+
+/* Misc */
+tree {
+ margin: .5em;
+}
+
+.gridSeparator {
+ width: .5em;
+}
+
+textbox {
+ background: transparent !important;
+ border: none;
+ padding: 0px;
+ -moz-user-focus: none;
+}
+
+textbox.header {
+ margin-inline-start: 0;
+}
+
+textbox.meta-properties {
+ margin-top: 1px;
+}
+
+.iframe {
+ margin: .5em;
+ background: white;
+ overflow: auto;
+}
+
+.fixedsize {
+ height: 8.5em;
+}
+
+textbox[disabled] {
+ font-style: italic;
+}
+
+/* General Tab */
+#general-security-identity {
+ white-space: pre-wrap;
+ line-height: 2em;
+}
+
+/* Media Tab */
+#imagetree {
+ min-height: 10em;
+}
+
+#mediaGrid {
+ min-height: 9em;
+}
+
+#mediaLabelColumn {
+ min-width: 10em;
+}
+
+#thepreviewimage {
+ margin: 1em;
+}
+
+treechildren::-moz-tree-cell-text(broken) {
+ font-style: italic;
+ color: #999999;
+}
+
+/* Feeds Tab */
+#feedListbox > richlistitem {
+ padding: 6px 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C7D0D9;
+}
+
+.feedTitle {
+ font-weight: bold;
+}
+
+/* Permissions Tab */
+#permList {
+ margin-top: .5em;
+ overflow: auto;
+}
+
+.permission {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ padding-inline-start: 7px;
+ padding-inline-end: 7px;
+ min-height: 25px;
+ border-bottom: 1px dotted #C7D0D9;
+}
+
+.permissionLabel {
+ font-weight: bold;
+}
+
+/* Security Tab */
+.fieldValue {
+ font-weight: bold;
+}
+
+#identity-icon {
+ width: 64px;
+ height: 64px;
+ max-height: 64px;
+ list-style-image: url("chrome://navigator/skin/icons/identity.png");
+ -moz-image-region: rect(0px, 64px, 64px, 0px);
+}
+
+#identity-icon.verifiedDomain {
+ -moz-image-region: rect(64px, 64px, 128px, 0px);
+}
+
+#identity-icon.verifiedIdentity {
+ -moz-image-region: rect(128px, 64px, 192px, 0px);
+}
diff --git a/comm/suite/themes/modern/navigator/tabbrowser.css b/comm/suite/themes/modern/navigator/tabbrowser.css
new file mode 100644
index 0000000000..817e110e7a
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/tabbrowser.css
@@ -0,0 +1,155 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: tabs ::::: */
+
+.tabbrowser-strip {
+ padding-bottom: 3px;
+ border-bottom: 1px solid #000000;
+}
+
+.tabbrowser-tabs {
+ padding-top: 1px;
+}
+
+.tabs-left {
+ width: 3px;
+}
+
+tab {
+ padding: 0px 3px;
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+tab[busy] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.tab-icon {
+ margin-inline-end: 3px;
+ width: 16px;
+ height: 16px;
+}
+
+/* ::::: Tab scrollbox arrow, and all-tabs buttons ::::: */
+
+.scrollbutton-up,
+.scrollbutton-down,
+.tabs-alltabs-button {
+ margin: 0px;
+ border-bottom: 1px solid #000000;
+}
+
+.scrollbutton-up:not([disabled="true"]):-moz-locale-dir(ltr),
+.scrollbutton-down:not([disabled="true"]):-moz-locale-dir(rtl) {
+ border-right: 1px solid #000000;
+}
+
+.scrollbutton-up:not([disabled="true"]):-moz-locale-dir(rtl),
+.scrollbutton-down:not([disabled="true"]):-moz-locale-dir(ltr) {
+ border-left: 1px solid #000000;
+}
+
+.scrollbutton-down {
+ transition: background-color 1s ease-out;
+}
+
+.scrollbutton-down:hover,
+.scrollbutton-down[notifybgtab="true"],
+.scrollbutton-down[disabled="true"] {
+ transition: none;
+}
+
+.scrollbutton-down[notifybgtab="true"] {
+ background-color: #8C9AA8;
+}
+
+.scrollbutton-up:not([disabled="true"]):hover,
+.scrollbutton-down:not([disabled="true"]):hover {
+ border-color: #EEF0F3;
+}
+
+.scrollbutton-up:not([disabled="true"]):hover:active,
+.scrollbutton-down:not([disabled="true"]):hover:active {
+ border-color: #86929E;
+ background-color: #8C9AA8;
+}
+
+.tabs-alltabs-button > .toolbarbutton-icon {
+ margin: 0px;
+}
+
+/* All tabs menupopup */
+.alltabs-item {
+ list-style-image: url("chrome://communicator/skin/places/bookmark-item.svg");
+}
+
+.alltabs-item[selected="true"] {
+ font-weight: bold;
+}
+
+.alltabs-item[busy] {
+ list-style-image: url("chrome://communicator/skin/icons/loading.png");
+}
+
+.alltabs-item[tabIsScrolled] {
+ font-style: italic;
+}
+
+/* ::::: close button ::::: */
+
+.tabs-closebutton {
+ margin: 0px 4px;
+ padding: 3px 2px;
+ border: none;
+ list-style-image: url("chrome://global/skin/icons/close.png");
+}
+
+.tabs-closebutton:hover {
+ list-style-image: url("chrome://global/skin/icons/close-hov.png");
+}
+
+.tabs-closebutton:hover:active {
+ list-style-image: url("chrome://global/skin/icons/close-act.png");
+}
+
+.tabs-closebutton[disabled="true"] {
+ padding: 3px 2px !important;
+ list-style-image: url("chrome://global/skin/icons/close-dis.png") !important;
+}
+
+.tabs-newbutton {
+ margin: 0px;
+ padding-top: 2px;
+ padding-bottom: 3px;
+ padding-inline-start: 2px;
+ padding-inline-end: 0px;
+ border: none;
+ list-style-image: url("chrome://navigator/skin/icons/tab-new.png");
+}
+
+.tabs-newbutton:hover {
+ list-style-image: url("chrome://navigator/skin/icons/tab-new-hov.png");
+}
+
+.tabs-newbutton:hover:active {
+ list-style-image: url("chrome://navigator/skin/icons/tab-new-act.png");
+}
+
+.tab-drop-indicator-bar {
+ height: 11px;
+ margin-top: -11px;
+ margin-inline-start: -6px;
+ position: relative;
+}
+
+.tab-drop-indicator {
+ height: 11px;
+ width: 11px;
+ margin-bottom: -5px;
+ position: relative;
+ list-style-image: url('chrome://navigator/skin/icons/tab-drag-indicator.png');
+}
diff --git a/comm/suite/themes/modern/navigator/toolbar/ubhist-arrow-act.png b/comm/suite/themes/modern/navigator/toolbar/ubhist-arrow-act.png
new file mode 100644
index 0000000000..92cf956d94
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/toolbar/ubhist-arrow-act.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/toolbar/ubhist-arrow.png b/comm/suite/themes/modern/navigator/toolbar/ubhist-arrow.png
new file mode 100644
index 0000000000..f6da257004
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/toolbar/ubhist-arrow.png
Binary files differ
diff --git a/comm/suite/themes/modern/navigator/webDeveloper.css b/comm/suite/themes/modern/navigator/webDeveloper.css
new file mode 100644
index 0000000000..185f7b38ba
--- /dev/null
+++ b/comm/suite/themes/modern/navigator/webDeveloper.css
@@ -0,0 +1,205 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* Mostly copied from mozilla/devtools/client/themes/commandline.inc.css */
+/* Developer Tools */
+
+/* Developer toolbar */
+
+/* NOTE: THESE NEED TO STAY IN SYNC WITH LIGHT-THEME.CSS AND DARK-THEME.CSS.
+ We are copy/pasting variables from light-theme and dark-theme,
+ since they aren't loaded in this context (within browser.css). */
+#developer-toolbar[devtoolstheme="light"] {
+ --gcli-background-color: #ebeced; /* --theme-tab-toolbar-background */
+ --gcli-input-background: #f0f1f2; /* --theme-toolbar-background */
+ --gcli-input-focused-background: #f7f7f7; /* --theme-sidebar-background */
+ --gcli-input-color: #18191a; /* --theme-body-color */
+ --gcli-border-color: #aaaaaa; /* --theme-splitter-color */
+ --selection-background: #4c9ed9; /* --theme-selection-background */
+ --selection-color: #f5f7fa; /* --theme-selection-color */
+}
+
+#developer-toolbar[devtoolstheme="dark"] {
+ --gcli-background-color: #343c45; /* --theme-toolbar-background */
+ --gcli-input-background: rgba(37, 44, 51, .6); /* --theme-tab-toolbar-background */
+ --gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
+ --gcli-input-color: #b6babf; /* --theme-body-color-alt */
+ --gcli-border-color: #000000; /* --theme-splitter-color */
+ --selection-background: #1d4f73; /* --theme-selection-background */
+ --selection-color: #f5f7fa; /* --theme-selection-color */
+}
+
+#developer-toolbar {
+ padding: 0;
+ background-color: var(--gcli-background-color);
+ border-top: 1px solid var(--gcli-border-color);
+}
+
+#developer-toolbar[devtoolstheme="light"] .gclitoolbar-input-node:not([focused=true])::before {
+ filter: invert(1);
+}
+
+#developer-toolbar-toolbox-button {
+ list-style-image: url("chrome://devtools/skin/images/toggle-tools.png");
+ -moz-image-region: rect(0px, 64px, 16px, 48px);
+}
+
+#developer-toolbar-toolbox-button:hover > .toolbarbutton-icon {
+ filter: brightness(120%);
+}
+
+#developer-toolbar-toolbox-button:hover:active > .toolbarbutton-icon {
+ filter: saturate(150%);
+}
+
+#developer-toolbar-toolbox-button[checked=true] > .toolbarbutton-icon {
+ filter: hue-rotate(180deg);
+}
+
+@media (min-resolution: 1.1dppx) {
+ #developer-toolbar-toolbox-button {
+ list-style-image: url("chrome://devtools/skin/images/toggle-tools@2x.png");
+ -moz-image-region: rect(0px, 128px, 32px, 96px);
+ }
+}
+
+/* Error counter */
+
+#developer-toolbar-toolbox-button[error-count]:before {
+ color: white;
+ min-width: 16px;
+ text-shadow: none;
+ background-color: firebrick;
+ border-radius: 2px;
+ margin-inline-end: 2px;
+/*
+ Firefox browser/themes/windows/browser.css
+ color: #FDF3DE;
+ min-width: 16px;
+ text-shadow: none;
+ background-image: linear-gradient(#B4211B, #8A1915);
+ border-radius: 1px;
+ margin-inline-end: 5px;
+ Firefox browser/themes/linux/browser.css
+ color: #FDF3DE;
+ min-width: 16px;
+ text-shadow: none;
+ background-image: linear-gradient(#B4211B, #8A1915);
+ border-radius: 1px;
+ margin-inline-end: 2px;
+*/
+}
+
+/* GCLI */
+
+html|*#gcli-tooltip-frame,
+html|*#gcli-output-frame {
+ padding: 0;
+ border-width: 0;
+ background-color: transparent;
+}
+
+#gcli-output,
+#gcli-tooltip {
+ border-width: 0;
+ background-color: transparent;
+ -moz-appearance: none;
+}
+
+.gclitoolbar-input-node,
+.gclitoolbar-complete-node {
+ -moz-box-align: center;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-right: 8px;
+ text-shadow: none;
+ box-shadow: none;
+ background-color: transparent;
+}
+
+.gclitoolbar-input-node {
+ -moz-appearance: none;
+ color: var(--gcli-input-color);
+ background-color: var(--gcli-input-background);
+ background-repeat: no-repeat;
+ background-position: 4px center;
+ box-shadow: 1px 0 0 var(--gcli-border-color) inset,
+ -1px 0 0 var(--gcli-border-color) inset;
+ outline-style: none;
+ padding: 0;
+}
+
+.gclitoolbar-input-node[focused="true"] {
+ background-color: var(--gcli-input-focused-background);
+}
+
+.gclitoolbar-input-node::before {
+ content: "";
+ display: inline-block;
+ -moz-box-ordinal-group: 0;
+ width: 16px;
+ height: 16px;
+ margin: 0 2px;
+ background-image: url("chrome://devtools/skin/images/commandline-icon.png");
+ background-position: 0 center;
+ background-size: 32px 16px;
+}
+
+.gclitoolbar-input-node[focused="true"]::before {
+ background-position: -16px center;
+}
+
+@media (min-resolution: 1.1dppx) {
+ .gclitoolbar-input-node::before {
+ background-image: url("chrome://devtools/skin/images/commandline-icon@2x.png");
+ }
+}
+
+.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
+ background-color: var(--selection-background);
+ color: var(--selection-color);
+ text-shadow: none;
+}
+
+.gclitoolbar-complete-node {
+ padding-left: 21px;
+ background-color: transparent;
+ color: transparent;
+ z-index: 100;
+ pointer-events: none;
+}
+
+.gcli-in-incomplete,
+.gcli-in-error,
+.gcli-in-ontab,
+.gcli-in-todo,
+.gcli-in-closebrace,
+.gcli-in-param,
+.gcli-in-valid {
+ margin: 0;
+ padding: 0;
+}
+
+.gcli-in-incomplete {
+ border-bottom: 2px dotted #999;
+}
+
+.gcli-in-error {
+ border-bottom: 2px dotted #F00;
+}
+
+.gcli-in-ontab {
+ color: hsl(210,0%,35%);
+}
+
+.gcli-in-todo {
+ color: hsl(210,50%,35%);
+}
+
+.gcli-in-closebrace {
+ color: hsl(0,0%,80%);
+}
diff --git a/comm/suite/themes/modern/preview.png b/comm/suite/themes/modern/preview.png
new file mode 100644
index 0000000000..df9a736b17
--- /dev/null
+++ b/comm/suite/themes/modern/preview.png
Binary files differ
diff --git a/comm/suite/themes/modern/shared/datetimeinputpickers.css b/comm/suite/themes/modern/shared/datetimeinputpickers.css
new file mode 100644
index 0000000000..510a19d98b
--- /dev/null
+++ b/comm/suite/themes/modern/shared/datetimeinputpickers.css
@@ -0,0 +1,438 @@
+/* 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/. */
+
+/*
+ * - Arrows and the month+year toggle at the top are now styled as
+ * buttons - properties for buttons were mostly replaced with
+ * rulesets copied from modern's button.css.
+ *
+ * - Other colors changed to match those used in modern. */
+
+:root {
+ --font-size-default: 1.1rem;
+ --spinner-width: 3rem;
+ --spinner-margin-top-bottom: 0.4rem;
+ --spinner-item-height: 2.4rem;
+ --spinner-item-margin-bottom: 0.1rem;
+ --spinner-button-height: 1.2rem;
+ --colon-width: 2rem;
+ --day-period-spacing-width: 1rem;
+ --calendar-width: 23.1rem;
+ --date-picker-item-height: 2.5rem;
+ --date-picker-item-width: 3.3rem;
+ --nav-arrow-button-width: 2.8rem;
+
+ /* Used for selected and hovered items */
+ --border-radius: 2px;
+
+ --font-color: #000000;
+
+ /* hovered items: colors from menu.css */
+ --fill-color: #424f63;
+ --hover-font-color: #FFFFFF;
+
+ /* --today-fill-color is var(--mmDayTodayBackground) from lightning,
+ see chrome://calendar-common/skin/widgets/minimonth.css */
+ --today-fill-color: #dfeaf4;
+ --today-border: 0.1rem solid #000000;
+
+ /* currently selected/chosen item */
+ --selected-font-color: #FFFFFF;
+ --selected-fill-color: #0996F8;
+
+ /* from button.css */
+ --button-font-color: #000000;
+ --button-fill-color: #AAB6C4;
+ --button-font-color-active: #FFFFFF;
+ --button-fill-color-active: #90A1B3;
+
+ --weekday-header-font-color: #6C6C6C;
+ --weekend-header-font-color: rgb(218, 78, 68);
+
+ --weekend-font-color: rgb(218, 78, 68);
+ --weekday-outside-font-color: rgb(153, 153, 153);
+ --weekend-outside-font-color: rgb(255, 152, 143);
+
+ --weekday-disabled-font-color: rgba(25, 25, 25, 0.2);
+ --weekend-disabled-font-color: rgba(218, 78, 68, 0.2);
+ --disabled-fill-color: rgba(235, 235, 235, 0.8);
+
+ --disabled-opacity: 0.2;
+}
+
+html {
+ font-size: 10px;
+}
+
+body {
+ margin: 0;
+ color: var(--font-color);
+ font: message-box;
+ font-size: var(--font-size-default);
+}
+/* The two following rulesets (buttons and active buttons) were copied
+ from modern's button.css (with modifications). */
+button {
+ margin: 2px;
+ border: 3px solid;
+ -moz-border-top-colors: #000000 #BBC6D1 #B1BBC9;
+ -moz-border-right-colors: #000000 #A2AEBB #A7B4C1;
+ -moz-border-bottom-colors: #000000 #97A6B6 #9DAAB9;
+ -moz-border-left-colors: #000000 #BBC6D1 #B1BBC9;
+ border-radius: 2px;
+ background-color: var(--button-fill-color);
+ background-clip: padding-box;
+ color: var(--button-font-color);
+ font: menu;
+}
+
+button:active,
+button.active {
+ -moz-border-top-colors: #000000 #8290A5 #899AAC;
+ -moz-border-right-colors: #000000 #8290A5 #899AAC;
+ -moz-border-bottom-colors: #000000 #8290A5 #899AAC;
+ -moz-border-left-colors: #000000 #8290A5 #899AAC;
+ background-color: var(--button-fill-color-active);
+ color: var(--button-font-color-active);
+}
+
+
+.nav {
+ display: flex;
+ width: var(--calendar-width);
+ height: 2.4rem;
+ margin-bottom: 0.8rem;
+ justify-content: space-between;
+}
+
+.nav > button {
+ width: var(--nav-arrow-button-width);
+ height: var(--date-picker-item-height);
+ -moz-context-properties: fill;
+ fill: var(--button-font-color);
+}
+
+.nav > button.active {
+ fill: var(--button-font-color-active);
+}
+
+.nav > button.prev,
+.nav > button.next:dir(rtl) {
+ background-image: url("chrome://global/skin/icons/calendar-arrow-left.svg");
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+}
+
+.nav > button.next,
+.nav > button.prev:dir(rtl) {
+ background-image: url("chrome://global/skin/icons/calendar-arrow-right.svg");
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+}
+
+.month-year-container {
+ position: absolute;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ top: 0;
+ /* .nav arrow buttons have a width of --nav-arrow-button-width as well
+ as 2px margins. The following three properties take this into
+ account to avoid overlapping these buttons and to align them
+ vertically. */
+ left: var(--nav-arrow-button-width);
+ right: var(--nav-arrow-button-width);
+ margin: 2px 4px;
+ height: var(--date-picker-item-height);
+ z-index: 10;
+}
+
+button.month-year {
+ font-size: 1.3rem;
+ padding-inline-start: 0.5rem;
+ padding-inline-end: 1.1rem;
+ min-height: var(--date-picker-item-height);
+ width: 100%;
+}
+
+
+button.month-year::after {
+ position: absolute;
+ content: "";
+ width: 2.6rem;
+ height: 1.6rem;
+ background: url("chrome://global/skin/icons/spinner-arrow-down.svg") no-repeat 50% 50%;
+ -moz-context-properties: fill;
+ fill: var(--button-font-color);
+}
+
+button.month-year.active::after {
+ background: url("chrome://global/skin/icons/spinner-arrow-up.svg") no-repeat 50% 50%;
+ fill: var(--button-font-color-active);
+}
+
+.month-year-view {
+ position: absolute;
+ z-index: 5;
+ padding-top: 3.2rem;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: var(--calendar-width);
+ background: #DDE3EB;
+ opacity: 1;
+/* transition: opacity 0.15s; */
+}
+
+.month-year-view.hidden {
+ visibility: hidden;
+ opacity: 0;
+}
+
+.month-year-view > .spinner-container {
+ width: 5.5rem;
+ margin: 0 0.5rem;
+}
+
+/*.month-year-view .spinner {
+ transform: scaleY(1);
+ transform-origin: top;
+ transition: transform 0.15s;
+}
+
+.month-year-view.hidden .spinner {
+ transform: scaleY(0);
+ transition: none;
+}
+
+.month-year-view .spinner > div {
+ transform: scaleY(1);
+ transition: transform 0.15s;
+}
+
+.month-year-view.hidden .spinner > div {
+ transform: scaleY(2.5);
+ transition: none;
+}*/
+
+.order-month-year > #spinner-month,
+.order-year-month > #spinner-year {
+ order: 1;
+}
+
+.order-month-year > #spinner-year,
+.order-year-month > #spinner-month {
+ order: 2;
+}
+
+.calendar-container {
+ cursor: default;
+ display: flex;
+ flex-direction: column;
+ width: var(--calendar-width);
+}
+
+.week-header {
+ display: flex;
+}
+
+.week-header > div {
+ color: var(--weekday-header-font-color);
+}
+
+.week-header > div.weekend {
+ color: var(--weekend-header-font-color);
+}
+
+.days-viewport {
+ height: 15rem;
+ overflow: hidden;
+ position: relative;
+}
+
+.days-view {
+ position: absolute;
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: row;
+}
+
+.week-header > div,
+.days-view > div {
+ align-items: center;
+ display: flex;
+ height: var(--date-picker-item-height);
+ position: relative;
+ justify-content: center;
+ width: var(--date-picker-item-width);
+}
+
+.days-view > .outside {
+ color: var(--weekday-outside-font-color);
+}
+
+.days-view > .weekend {
+ color: var(--weekend-font-color);
+}
+
+.days-view > .weekend.outside {
+ color: var(--weekend-outside-font-color);
+}
+
+.days-view > .out-of-range,
+.days-view > .off-step {
+ color: var(--weekday-disabled-font-color);
+ background: var(--disabled-fill-color);
+}
+
+.days-view > .out-of-range.weekend,
+.days-view > .off-step.weekend {
+ color: var(--weekend-disabled-font-color);
+}
+
+.days-view > .today {
+ font-weight: bold;
+}
+
+.days-view > .out-of-range::before,
+.days-view > .off-step::before {
+ display: none;
+}
+
+.days-view > div:hover::before,
+.days-view > .select::before,
+.days-view > .today::before {
+ top: 5%;
+ bottom: 5%;
+ left: 5%;
+ right: 5%;
+}
+
+#time-picker,
+.month-year-view {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.spinner-container {
+ display: flex;
+ flex-direction: column;
+ width: var(--spinner-width);
+}
+
+.spinner-container > button {
+ height: var(--spinner-button-height);
+ -moz-context-properties: fill;
+ fill: var(--button-font-color);
+}
+
+.spinner-container > button.active {
+ fill: var(--button-font-color-active);
+}
+
+.spinner-container > button.up {
+ background-image: url("chrome://global/skin/icons/spinner-arrow-up.svg");
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+}
+
+.spinner-container > button.down {
+ background-image: url("chrome://global/skin/icons/spinner-arrow-down.svg");
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+}
+
+.spinner-container.hide-buttons > button {
+ visibility: hidden;
+}
+
+.spinner-container > .spinner {
+ position: relative;
+ width: 100%;
+ margin: var(--spinner-margin-top-bottom) 0;
+ cursor: default;
+ overflow-y: scroll;
+ scroll-snap-type: mandatory;
+ scroll-snap-points-y: repeat(100%);
+}
+
+.spinner-container > .spinner > div {
+ box-sizing: border-box;
+ position: relative;
+ text-align: center;
+ padding: calc((var(--spinner-item-height) - var(--font-size-default)) / 2) 0;
+ margin-bottom: var(--spinner-item-margin-bottom);
+ height: var(--spinner-item-height);
+ -moz-user-select: none;
+ scroll-snap-coordinate: 0 0;
+}
+
+.spinner-container > .spinner > div::before,
+.calendar-container .days-view > div::before {
+ position: absolute;
+ top: 5%;
+ bottom: 5%;
+ left: 5%;
+ right: 5%;
+ z-index: -10;
+ border-radius: var(--border-radius);
+}
+
+.calendar-container .days-view > div:hover,
+.spinner-container > .spinner > div:hover {
+ color: var(--hover-font-color);
+}
+.calendar-container .days-view > div.today:hover::before {
+ background: var(--fill-color);
+ color: var(--hover-font-color);
+}
+.spinner-container > .spinner > div:hover::before,
+.calendar-container .days-view > div:hover::before,
+.spinner-container > .spinner > div.selection:hover::before,
+.calendar-container .days-view > div.selection:hover::before{
+ background: var(--fill-color);
+ color: var(--hover-font-color);
+ content: "";
+}
+
+.calendar-container .days-view > div.today::before {
+ background: var(--today-fill-color);
+ border: var(--today-border);
+ content: "";
+}
+
+.spinner-container > .spinner:not(.scrolling) > div.selection,
+.calendar-container .days-view > div.selection {
+ color: var(--selected-font-color);
+}
+
+.spinner-container > .spinner > div.selection::before,
+.calendar-container .days-view > div.selection::before {
+ background: var(--selected-fill-color);
+ border: none;
+ content: "";
+}
+
+.spinner-container > .spinner > div.disabled::before,
+.spinner-container > .spinner.scrolling > div.selection::before,
+.spinner-container > .spinner.scrolling > div:hover::before {
+ display: none;
+}
+
+.spinner-container > .spinner > div.disabled {
+ opacity: var(--disabled-opacity);
+}
+
+.colon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: var(--colon-width);
+ margin-bottom: 0.3rem;
+}
+
+.spacer {
+ width: var(--day-period-spacing-width);
+}
diff --git a/comm/suite/themes/modern/shared/datetimepopup.css b/comm/suite/themes/modern/shared/datetimepopup.css
new file mode 100644
index 0000000000..52f6fc7a2d
--- /dev/null
+++ b/comm/suite/themes/modern/shared/datetimepopup.css
@@ -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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+panel[type="arrow"][side="top"],
+panel[type="arrow"][side="bottom"] {
+ margin-left: 0;
+ margin-right: 0;
+}
diff --git a/comm/suite/themes/modern/shared/icons/calendar-arrow-left.svg b/comm/suite/themes/modern/shared/icons/calendar-arrow-left.svg
new file mode 100644
index 0000000000..3ff50f1e85
--- /dev/null
+++ b/comm/suite/themes/modern/shared/icons/calendar-arrow-left.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="14" height="14" viewBox="0 0 14 14">
+ <path fill="context-fill" d="M9.2 0L11 1.7 5.5 7 11 12.3 9.2 14 2 7"/>
+</svg>
diff --git a/comm/suite/themes/modern/shared/icons/calendar-arrow-right.svg b/comm/suite/themes/modern/shared/icons/calendar-arrow-right.svg
new file mode 100644
index 0000000000..0b4978713f
--- /dev/null
+++ b/comm/suite/themes/modern/shared/icons/calendar-arrow-right.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="14" height="14" viewBox="0 0 14 14">
+ <path fill="context-fill" d="M4.8 14L3 12.3 8.5 7 3 1.7 4.8 0 12 7"/>
+</svg>
diff --git a/comm/suite/themes/modern/shared/icons/input-clear.svg b/comm/suite/themes/modern/shared/icons/input-clear.svg
new file mode 100644
index 0000000000..0abaaf9b1f
--- /dev/null
+++ b/comm/suite/themes/modern/shared/icons/input-clear.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
+ <style>
+ .st0 {
+ fill: #858585;
+ }
+ .st1 {
+ fill: #FFFFFF;
+ }
+ </style>
+ <circle id="Combined-Shape" class="st0" cx="6" cy="6" r="6"/>
+ <polygon id="Close_Button_-_Normal-path" class="st1" points="9,8.1 8.1,9 6,6.9 3.9,9 3,8.1 5.1,6 3,3.9 3.9,3 6,5.1 8.1,3 9,3.9 6.9,6"/>
+</svg>
diff --git a/comm/suite/themes/modern/shared/icons/spinner-arrow-down.svg b/comm/suite/themes/modern/shared/icons/spinner-arrow-down.svg
new file mode 100644
index 0000000000..420f8a3f2d
--- /dev/null
+++ b/comm/suite/themes/modern/shared/icons/spinner-arrow-down.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="10" height="6" viewBox="0 0 10 6">
+ <path fill="context-fill" d="M0 1l1-1 4 4 4-4 1 1-5 5"/>
+</svg>
diff --git a/comm/suite/themes/modern/shared/icons/spinner-arrow-up.svg b/comm/suite/themes/modern/shared/icons/spinner-arrow-up.svg
new file mode 100644
index 0000000000..6ce9923b48
--- /dev/null
+++ b/comm/suite/themes/modern/shared/icons/spinner-arrow-up.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="10" height="6" viewBox="0 0 10 6">
+ <path fill="context-fill" d="M0 5l1 1 4-4 4 4 1-1-5-5"/>
+</svg>